summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html')
-rw-r--r--testing/web-platform/tests/html/META.yml7
-rw-r--r--testing/web-platform/tests/html/README.md21
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js67
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js50
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js60
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js94
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js128
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js124
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js64
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js44
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/hasStorageAccess.tentative.https.window.js30
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js104
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js34
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js74
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js57
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/requestStorageAccess.tentative.https.window.js35
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/requestStorageAccessFor.tentative.https.window.js35
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js49
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers1
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/common.js55
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js72
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js16
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js21
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js85
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js57
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js93
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js75
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js70
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/README.md60
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/broadcast-channel.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/dedicated-worker.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-1.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-2.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-cors.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-redirects.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/shared-worker.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/events.html44
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/focus.html73
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/pushstate.https.html63
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/echo-worker.js16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/event-recorder.js54
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor-pushstate.html13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.js64
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js229
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/inflight-fetch-helper.js47
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js137
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/service-worker.js58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/slow.py13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/worker-helper.js28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-client-postmessage.https.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-claim.https.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-matchall.https.html76
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-controlled-after-restore.https.html54
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-unregister.https.html67
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/storage-events.html101
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/timers.html68
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html47
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html43
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html44
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html133
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html26
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html151
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html49
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html70
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html74
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html146
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html55
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html81
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html48
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html45
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html50
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html74
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html85
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html95
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html12
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js52
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js90
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html76
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html178
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html144
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html66
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html178
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html124
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html134
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html86
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html64
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html44
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js121
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html63
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html61
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html81
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html81
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html78
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js80
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html56
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html66
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html26
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html0
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html0
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html75
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html45
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html45
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html43
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html42
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html76
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html77
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html82
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html89
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js89
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html67
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html67
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html49
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdfbin0 -> 58927 bytes
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html19
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/README.md11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/anchor-fragment-history-back-on-click.html40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-nav.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-traversal.html38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-nav.html44
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-traversal.html37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-stop.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-nav.html77
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-traversal.html160
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-nav.html66
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-traversal.html94
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-stop.html42
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/forward-to-pruned-entry.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-1.html62
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-2.sub.html178
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/helpers.mjs52
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/nav-cancelation-2-helper.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/slow.py7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-nav.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-traversal.html70
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-nav.html61
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-traversal.html55
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-stop.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-nav.html42
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-traversal.html88
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-nav.html57
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-hashchange.html148
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-pushstate.html137
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-stop.html37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image-in-popup.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-video.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/read-media/resources/iframe-document.sub.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/read-text/load-text-plain.html40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addHTML.window.js20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc-startOn.window.js24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc.window.js27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe.window.js40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addScripts.window.js19
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-defaults.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-extra-config.window.js36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-features.window.js23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-invalid-origin.window.js21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-startOn.window.js19
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-target.window.js17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWorker.window.js29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/constructor.window.js38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/createContext-bad-executorCreator.window.js20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/getRequestHeaders.window.js18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigateToNew.window.js37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-bfcache.window.js35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-helpers.window.js28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-same-document.window.js39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js48
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script.js3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script2.js3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-common.js14
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.js67
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.py40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-worker.js12
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js592
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/resources/has-iframe.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/resources/helpers.js84
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/resources/post-top-opener-on-load.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/001.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/002.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/003.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/004.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/005.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/006.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/007.html37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/forward-triggers-hashchange.html44
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-2.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html50
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/navigate-helpers.js23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/replacement-enabled.html69
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-non-utf8-encoded-document.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-percent-encoded.html62
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-lr.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-rl.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-anchor-name.html59
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-id-top.html51
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html56
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/001.html15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/002.html15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/003.html15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/004.html15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/005.html15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/base.html14
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html222
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back-1.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-1.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-2.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-iframe.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-manual.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-popup.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html33
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-1.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-2.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward-1.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward.html19
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-closeable.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable-1.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-1.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-2.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001.html14
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/003.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-1.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-2.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-001.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-002.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-003.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-004.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-005.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-006.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/next.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001-1.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001a.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001b.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002-1.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002a.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002b.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003-1.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003a.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003b.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004-1.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004a.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004b.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005-1.html13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005a.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005b.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-destination.html13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-start.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-1.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-2.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002-1.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003-1.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003.html33
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004-1.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-1.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-2.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-1.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-2.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008-1.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009-1.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual-1.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-cross-origin.window.js34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-same-origin.window.js33
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child1.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child2.html5
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-filler.html1
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild1.html8
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild2.html5
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-iframe-state.html41
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-only-fully-active.html30
-rw-r--r--testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-remove-iframe.html24
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/001.html339
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/002.html318
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/004.html62
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/005.html47
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/006.html53
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/007.html66
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/008.html40
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/008.js11
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/009-1.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/009-3.html28
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/009-5.html23
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/009.html22
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/010-1.html16
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/010-3.html24
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/010-5.html23
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/010.html22
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/011.html32
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/012.html32
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/blank-new.html5
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/blank-old.html5
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/blank.html8
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/blank2.html13
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/blank3.html11
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_001.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_002.html22
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_003.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_004.html29
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_005.html34
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_006.html30
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_007.html32
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history-associated-with-document.window.js6
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history-state-after-bfcache.window.js43
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history.js35
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_back-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_back.html27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_1.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_cross_realm_method.html22
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_entry.html11
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-1.html14
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-2.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward.html32
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_1.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_cross_realm_method.html28
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_cross_realm_method.html23
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_minus.html27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument-1.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument.html27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_plus.html33
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri-1.html22
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri.html30
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined-1.html14
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero-1.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero.html27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero_which_document.window.js27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_properties_only_fully_active.html23
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate.html19
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_err.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_nooptionalparam.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url.html24
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url_rewriting.html176
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_err.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_nooptionalparam.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/history_state.html24
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/iframe_history_go_0.html40
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001-1.html72
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002-1.html35
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/filler.html5
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history.js35
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history_entry.html12
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-1.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html31
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-1.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html31
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-1.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html30
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-base.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-whitespace.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-base.html18
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-whitespace.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/resources/message-opener.sub.html8
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-beforeunload.html12
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-unload.html12
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/resources/url-rewriting-helper.html12
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-beforeunload.html28
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-unload.html28
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1-1.html16
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1.html25
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1.html28
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1-1.html12
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1.html29
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2.html24
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1.html27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2.html27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html197
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load.html23
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load.html23
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/document_location.html39
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-non-configurable-toString-valueOf.html42
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-origin-idna.sub.window.js11
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-pathname-setter-question-mark.html16
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-prevent-extensions.html21
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html35
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html63
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-sameish.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-with-colon.sub.html52
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter.html104
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-no-toString-valueOf.html55
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin-domain.sub.html31
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin.sub.html28
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-goes-cross-origin-domain.sub.html39
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html30
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin.html21
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-stringifier.html24
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-symbol-toprimitive.html14
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-tojson.html13
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location-valueof.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank-1.html2
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank.html24
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_hash.html62
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_host.html28
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_hostname.html33
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_href.html19
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_origin.html14
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_pathname.html22
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_port.html31
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_protocol.html25
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload-iframe.html4
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload.html43
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload_javascript_url.html60
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_replace.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/location_search.html20
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/no-browsing-context.window.js86
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-manual.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-1.html10
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-manual.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_replace_during_load-manual.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-manual.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-manual.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/per-global.window.js3
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write-1.html19
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write-1.html4
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write.html21
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload.html26
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/reload_post_1-manual.html27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-origin.html3
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-protocol.html4
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/resources/reload_post_1-1.py13
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/same-hash.html101
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html12
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-1.html10
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-1.html13
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load.html16
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-1.html12
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load.html17
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-location-interface/security_location_0.htm27
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload.html37
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-2.html22
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-dynamic-iframe.html16
-rw-r--r--testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-iframe.html10
-rw-r--r--testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_checking-manual.html23
-rw-r--r--testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_downloading-manual.html24
-rw-r--r--testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_obsolete-manual.html24
-rw-r--r--testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_updateready-manual.html25
-rw-r--r--testing/web-platform/tests/html/browsers/offline/application-cache-api/api_swapcache-manual.html30
-rw-r--r--testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_event-manual.https.html46
-rw-r--r--testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_online.https.html17
-rw-r--r--testing/web-platform/tests/html/browsers/offline/changestonetworkingmodel/original-id.json1
-rw-r--r--testing/web-platform/tests/html/browsers/offline/introduction-4/event_downloading-manual.html23
-rw-r--r--testing/web-platform/tests/html/browsers/offline/introduction-4/event_error-manual.html23
-rw-r--r--testing/web-platform/tests/html/browsers/offline/introduction-4/event_obsolete-manual.html23
-rw-r--r--testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready-manual.html22
-rw-r--r--testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready_swapcache-manual.html30
-rw-r--r--testing/web-platform/tests/html/browsers/offline/manifest_main_empty-manual.https.html14
-rw-r--r--testing/web-platform/tests/html/browsers/offline/manifest_notchanged_online-manual.https.html19
-rw-r--r--testing/web-platform/tests/html/browsers/offline/manifest_section_empty-manual.https.html19
-rw-r--r--testing/web-platform/tests/html/browsers/offline/manifest_section_many-manual.https.html19
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/css/clock.css1
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/css/offline.css5
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/css/online.css5
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/css/result.css11
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/html/clock.html12
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/js/clock.js3
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/manifest/clock.manifest17
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/manifest/section_empty.manifest10
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/manifest/section_many.manifest19
-rw-r--r--testing/web-platform/tests/html/browsers/offline/resources/manifest/url_check.manifest17
-rw-r--r--testing/web-platform/tests/html/browsers/offline/section_network_offline-manual.https.html17
-rw-r--r--testing/web-platform/tests/html/browsers/offline/section_network_online-manual.https.html16
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-due-to-document-domain-only.html33
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-caching.html49
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-common.js34
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-length.html45
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-name.html45
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html25
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html718
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame-with-then.html19
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html51
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.window.js51
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/resources/cross-origin-due-to-document-domain-only-helper.html9
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/win-documentdomain.sub.html63
-rw-r--r--testing/web-platform/tests/html/browsers/origin/cross-origin-objects/window-location-and-location-href-cross-realm-set.html106
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-iframe.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-window.html25
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/about-srcdoc.html29
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js10
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/javascript-url.html33
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html40
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html7
-rw-r--r--testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-bad-subdomain.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-same.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain-with-redirect.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yeswithparams-subdomain.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html26
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html26
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html36
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html37
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain1-child2-yes-subdomain2.sub.https.html37
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-port.sub.https.html37
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html36
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html39
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html38
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/META.yml5
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/README.md30
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html63
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html52
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html15
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html15
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-no.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-no.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-no.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-to-javascript-test.mjs33
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-url-test.mjs13
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/helpers.mjs28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/javascript-url-test.mjs14
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-iframe-test.sub.mjs20
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-same-origin-iframe-test.sub.mjs20
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-no.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-no.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html62
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-port.sub.https.html41
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-subdomain.sub.https.html41
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html40
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain2.sub.https.html41
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html43
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-yes-subdomain-2-no-subdomain.sub.https.html41
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html41
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html41
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html26
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html9
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-port.sub.https.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-same.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-subdomain.sub.https.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html27
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html28
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/regression-1399759.https.sub.html100
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html46
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/README.md6
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html5
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html.headers2
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html6
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html12
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs390
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs63
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py43
-rw-r--r--testing/web-platform/tests/html/browsers/origin/origin-of-data-document.html30
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html35
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html305
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html76
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html84
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html21
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html52
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.sub.js65
-rw-r--r--testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_setter_iframe.html12
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/inner-iframe.html13
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/noscript-iframe.html3
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/popup-from-initial-empty-sandboxed-document.window.js46
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/resources/check-sandbox-flags.html8
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/resources/document-open.html14
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/resources/execute-postmessage.html5
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/resources/post-done-to-opener.html3
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html15
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-javascript-window-open.html18
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-same-origin.html30
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-scripts.html29
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-popups.html39
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-same-origin.html35
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html33
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts.html29
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open-mutation.window.js37
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open.html50
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-frame.html64
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html46
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html154
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html30
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-javascript-window-open.html19
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing-iframe.tentative.html16
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html29
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context-iframe.html5
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context.html39
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript-ref.html6
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript.html7
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/sandbox-window-open-srcdoc.html52
-rw-r--r--testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html90
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/BarProp.window.js59
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/Document-defaultView.html38
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/Window-document.html25
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-01.html47
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-02.html62
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-03.html30
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/iterator.html11
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test1.html12
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test2.html6
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test3.html8
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/window_length.html51
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/close-method.window.js39
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/closed-attribute.window.js69
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/defaultstatus.html17
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/document-attribute.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/focus.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-1.html20
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-2.html2
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1.html10
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2-1.html22
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2.html10
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-1.html21
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-2.html4
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-3.html4
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3.html9
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-1.html21
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-2.html4
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-3.html4
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4.html9
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-1.html27
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-2.html2
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-manual.html10
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-1.html19
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-2.html1
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-3.html1
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-manual.html10
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/historical.window.js4
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/length-attribute.window.js24
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/name-attribute.window.js18
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html38
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html4
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html76
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/navigated-named-objects.window.js67
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/prototype.html94
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/strict-mode-redefine-readonly-property.html17
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/test.html7
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-named-properties.html83
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-null-names.html20
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/navigate-to-about-blank-while-initial-load-pending.html26
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-BarProp.window.js23
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-sizing.window.js17
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/callback.js1
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload.html16
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide.html16
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer-1.html1
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer.html18
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload.html16
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/creating_browsing_context_test_01.html39
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/no_window_open_when_term_nesting_level_nonzero.window.js113
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-1.html2
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-2.html16
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001.html3
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-1.html8
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-2.html16
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002.html3
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-is-popup-condition.html149
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-innerwidth-innerheight.html75
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-screenx-screeny.html67
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-top-left.html68
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-width-height.html75
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-height.html91
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerheight.html76
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html75
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-left.html76
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screenx.html75
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screeny.html76
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-top.html73
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-width.html91
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-innerheight-innerwidth.html55
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noopener.html11
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noreferrer.html11
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-screenx-screeny.html55
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-top-left.html69
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-width-height.html71
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/close-self.html3
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/is-popup-barprop.html15
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/message-opener.html52
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/tokenization-noopener-noreferrer.js152
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/proxy-getOwnPropertyDescriptor.html126
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/security-window/window-security.https.html200
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/self-et-al.window.js43
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/BarProp-target.html17
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/closed.html12
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/noopener-target.html15
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/noreferrer-target.html13
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/same-origin-iframe.html8
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/sizing-target.html17
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/window-open-popup-target.html24
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/support/windowFeature-values-target.html24
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-aliases.html28
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-indexed-access-vs-named-access.html58
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-delete-no-cache.html31
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-strict.html75
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties.html71
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-open-defaults.window.js12
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-open-invalid-url.html10
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-open-noopener.html137
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-open-noreferrer.html20
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-open-popup-behavior.html51
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-open-windowfeatures-values.html72
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-opener-unconfigurable.window.js17
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-properties.https.html359
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-prototype-chain.html32
-rw-r--r--testing/web-platform/tests/html/browsers/the-window-object/window-reuse-in-nested-browsing-contexts.tentative.html158
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/document-tree-child-browsing-context-name-property-set.sub.html26
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-define-own-property-unforgeable-same-origin.html47
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html21
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin-domain.sub.html29
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin.sub.html28
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-goes-cross-origin-domain.sub.html35
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html30
-rw-r--r--testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin.html21
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html31
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-multiple.html35
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html25
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html29
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.html33
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.window.js38
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener.html55
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/close-opener.html30
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/message-window-opener.html14
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/multiple-opener.html32
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/no-opener.html16
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/open-closer.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/opener-setter.html23
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-001.html35
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-002.html21
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-003.html20
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-001.html15
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-002.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-003.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-004.html40
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-001.html15
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-002.html46
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-001.html34
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-002.html33
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-003.html39
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-001.html23
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-002.html16
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-existing-001.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-001-iframe-1.html5
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-iframe.html4
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-window.html12
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-iframe.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-window.html4
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-1.html14
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-2.html9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-001-iframe.html9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-002-iframe.html11
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-002-window.html16
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-1.html15
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-2.html10
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-default-002-iframe.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-existing-001-iframe.html7
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_parent.html9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_top.html9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-opener.html12
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-top.html10
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-has-opener.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-is-top.html10
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context-window.html37
-rw-r--r--testing/web-platform/tests/html/browsers/windows/browsing-context.html48
-rw-r--r--testing/web-platform/tests/html/browsers/windows/clear-window-name.https.html122
-rw-r--r--testing/web-platform/tests/html/browsers/windows/dangling-markup-window-name.html97
-rw-r--r--testing/web-platform/tests/html/browsers/windows/document-domain-nested-navigate.window.js16
-rw-r--r--testing/web-platform/tests/html/browsers/windows/document-domain-nested-set.window.js10
-rw-r--r--testing/web-platform/tests/html/browsers/windows/document-domain-nested.window.js9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/document-domain-removed-iframe.html72
-rw-r--r--testing/web-platform/tests/html/browsers/windows/embedded-opener-a-form.html30
-rw-r--r--testing/web-platform/tests/html/browsers/windows/embedded-opener-remove-frame.html66
-rw-r--r--testing/web-platform/tests/html/browsers/windows/embedded-opener.html32
-rw-r--r--testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-print.sub.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-scaled-print.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/iframe-nested-print-ref.html9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/iframe-nested-print.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/iframe-nested-scaled-print-ref.html15
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement-siblings.sub.html42
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html66
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/name-attribute.window.js58
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-nested-frame.html7
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessed.html16
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessor.html33
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-window-post.html14
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-opener.html7
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-parent.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent-null.html66
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent.html44
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top-null.html66
-rw-r--r--testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top.html65
-rw-r--r--testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-close-manual.sub.html3
-rw-r--r--testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-manual.html10
-rw-r--r--testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-window-name-manual.sub.html3
-rw-r--r--testing/web-platform/tests/html/browsers/windows/noreferrer-null-opener.html25
-rw-r--r--testing/web-platform/tests/html/browsers/windows/noreferrer-window-name.html86
-rw-r--r--testing/web-platform/tests/html/browsers/windows/opener-cross-origin-manual.sub.html10
-rw-r--r--testing/web-platform/tests/html/browsers/windows/opener-string.window.js14
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-cross-partition.sub.html26
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-same-partition.html26
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-cross-origin.sub.html27
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-same-origin.sub.html27
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-cross-partition-window.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-same-partition-window.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-iframe.https.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-window.sub.https.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-iframe.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-window.sub.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-iframe.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-window.https.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-iframe.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-window.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-a.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-b.https.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-window.sub.https.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-a.sub.https.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-b.https.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-window.sub.https.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-a.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-b.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-window.sub.html6
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-cross-origin.sub.html29
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-same-origin.sub.html29
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html30
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html30
-rw-r--r--testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html30
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/browsing-context-window.html7
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/document-domain-setter.html7
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/echo-window-name.html1
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-cross-origin.html2
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-printing-pass.html9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/message-parent.html5
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/nested-post-to-opener.html12
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/noreferrer-window-name.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-embed.sub.html2
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-end.txt1
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin.html4
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/post-to-opener.html8
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/restore-window-name-back.sub.html7
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/restore-window-name.sub.html43
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/target-cross-origin.sub.html3
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/window-close-button.html1
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/window-name-stash.py13
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/window-name.sub.html102
-rw-r--r--testing/web-platform/tests/html/browsers/windows/resources/window-opener.html4
-rw-r--r--testing/web-platform/tests/html/browsers/windows/restore-window-name-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/browsers/windows/targeting-cross-origin-nested-browsing-contexts.html39
-rw-r--r--testing/web-platform/tests/html/browsers/windows/targeting-multiple-cross-origin-manual.sub.html9
-rw-r--r--testing/web-platform/tests/html/browsers/windows/targeting-with-embedded-null-in-target.html34
-rw-r--r--testing/web-platform/tests/html/canvas/META.yml6
-rw-r--r--testing/web-platform/tests/html/canvas/README.md1
-rw-r--r--testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.basics.html62
-rw-r--r--testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.delete.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.drawings.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.missingargs.html141
-rw-r--r--testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree-ref.html9
-rw-r--r--testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.exists.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.cache.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.create.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.invalid.args.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.prototype.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.shared.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.exists.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.extend.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.prototype.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.replace.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.unique.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.initial.reset.2dstate.html102
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.readonly.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.reference.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled-manual.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled.pngbin0 -> 219 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.hex.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.zero.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.hex.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.zero.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.pngbin0 -> 209 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.clear.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.copy.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-atop.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-in.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-out.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-over.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.lighter.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-atop.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-in.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-out.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-over.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.xor.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvas.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvascopy.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvaspattern.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.default.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.fill.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.image.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.imagepattern.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.invalid.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.range.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.pngbin0 -> 209 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.casesensitive.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.clear.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.darker.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.default.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.get.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.highlight.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.nullsuffix.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.over.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.unrecognised.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.pngbin0 -> 207 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.pngbin0 -> 222 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.pngbin0 -> 208 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.pngbin0 -> 222 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.pngbin0 -> 222 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.basics.html61
-rw-r--r--testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.delete.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.drawings.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.missingargs.html140
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.3arg.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.5arg.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.alpha.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.animated.gif.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.broken.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.canvas.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.clip.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.composite.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.floatsource.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.immediate.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.reload.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedest.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedir.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativesource.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonexistent.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html335
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nowrap.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.null.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.path.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.1.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.2.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.svg.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.transform.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.paragraph.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.path.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.path.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html53
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html59
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.transparency.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.currentcolor.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.default.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.solid.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.transparent.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidstring.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidtype.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.basic.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.changed.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.pngbin0 -> 112 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.pngbin0 -> 207 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.pngbin0 -> 207 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.pngbin0 -> 112 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.pngbin0 -> 207 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.pngbin0 -> 207 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.pngbin0 -> 112 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.pngbin0 -> 207 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.pngbin0 -> 207 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.system.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.empty.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.pngbin0 -> 219 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.pngbin0 -> 219 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.pngbin0 -> 396 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.pngbin0 -> 256 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.outside.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.pngbin0 -> 248 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.solid.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.pngbin0 -> 229 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html48
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.1.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.2.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.3.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.compare.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.pngbin0 -> 112 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.return.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.type.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.update.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.front.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.top.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.equal.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside2.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside3.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.negative.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html100
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside2.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside3.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch2.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch3.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.1.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.2.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.3.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.animated.gif.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.canvas.html48
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.image.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.nocontext.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.type.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.crosscanvas.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.broken.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.emptysrc.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.immediate.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.nosrc.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.reload.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.removedsrc.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent-but-loading.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nosrc.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.null.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.string.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.undefined.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zeroheight.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zerowidth.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas2.html48
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image1.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image2.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.case.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.empty.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.null.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.undefined.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.nonexistent.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zeroheight.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zerowidth.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.identity.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.infinity.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.invalid.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.transparency.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colormix.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.default.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html68
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html66
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html57
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html56
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html66
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html123
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html57
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html107
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.tentative.html48
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html129
-rw-r--r--testing/web-platform/tests/html/canvas/element/filters/2d.filter.value.html55
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.beginLayer-options.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full.html23
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial-expected.html21
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside-expected.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-expected.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside.html23
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside-expected.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow-expected.html17
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.exceptions-are-no-op.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation-expected.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter-expected.html17
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending-expected.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow-expected.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite-expected.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow-expected.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending-expected.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html55
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite-expected.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html55
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow-expected.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow-expected.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow-expected.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow-expected.html53
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow-expected.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-restore.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.endLayer.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-beginLayer-restore.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-endLayer.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html56
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.toBlob.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.createPattern.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.drawImage.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.getImageData.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.putImageData.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.toDataURL.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-expected.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters-expected.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas-expected.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style-expected.html24
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style.html25
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html21
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html21
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-endLayer.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-save.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.restore.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save-beginLayer.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_reset_restore.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_restore.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.butt.html60
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.closed.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.invalid.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.open.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.round.html76
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.square.html60
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.valid.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cross.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.defaults.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.fill.noop.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.invalid.strokestyle.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.bevel.html79
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.closed.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.invalid.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.miter.html70
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.open.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.parallel.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.round.html77
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.valid.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.acute.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.exceeded.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.invalid.html59
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.lineedge.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.obtuse.html51
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.rightangle.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.valid.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.within.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.union.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.basic.html62
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.invalid.html59
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.scaledefault.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.transformed.html68
-rw-r--r--testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.valid.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/README.md1
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001-ref.htm22
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001.htm26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001-ref.htm32
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001.htm35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow-ref.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001-ref.htm11
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001.htm32
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/canvas-with-padding.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false-ref.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false-ref.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false-ref.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false-ref.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false.html18
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/getContextAttributes.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false-ref.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false.html13
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false-ref.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false.html17
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas.html203
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self.html17
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self_ref.html11
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_crossorigin.sub.html61
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_html_image.html268
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_1.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_with_foreign_object_does_not_taint.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none.tentative.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none.tentative.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height.tentative.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap.tentative.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob-ref.html10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob.tentative.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none.tentative.html24
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none.tentative.html24
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-ref.html12
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height.tentative.html24
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element.tentative.html24
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect-ref.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect.tentative.html25
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_complexshapes_ispointInpath_001.htm31
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_focus_drawFocusIfNeeded_AAPI_001-manual.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html66
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_002.html68
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_003.html69
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html88
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html88
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math-ref.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/2d.fillStyle.parse.current.notrendered.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/canvas_colorsandstyles_createlineargradient_001.htm59
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-expected.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation-expected.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-fillStyle-opacity.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle-expected.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur-expected.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-expected.html21
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-globalAlpha.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes-expected.html48
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-expected.html66
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/svg-filter-crash.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur-expected.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur.html24
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer-expected.html65
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer.html62
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix-expected.html77
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix.html60
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence-expected.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion-expected.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion.html58
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion-expected.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-sequence-conversion.html55
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/image-smoothing/imagesmoothing.html119
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html89
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html170
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html62
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js168
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html67
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html87
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html121
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html61
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html73
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html239
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html53
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js17
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html18
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html146
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001-ref.htm11
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001.htm37
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_a.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_ref.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/line-styles/setLineDash.html104
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_001.htm60
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002-ref.htm27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002.htm34
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors-expected.html11
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/shadows/shadowBlur_gaussian_tolerance.1.html191
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected-ref.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected.html24
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch-ref.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.condensed.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.expanded.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-condensed.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-expanded.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.normal.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-condensed.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-expanded.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-condensed.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-expanded.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001-ref.htm22
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001.htm33
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.state.saverestore.imageSmoothingEnabled.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.zero.size.canvas.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001-ref.htm11
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001.htm42
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/transformations/2d.transformation.getTransform.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001-ref.html21
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001-ref.htm11
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001.htm31
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/transformations/transform_a.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/transformations/transform_ref.html16
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1-expected.htm10
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1.htm14
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2-expected.htm14
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2.htm15
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3-expected.htm13
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3.htm16
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4-expected.htm14
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4.htm17
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/ImageData-fidelity.html126
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-Blob.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageBitmap.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageData.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-canvas.html53
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-cloned.html70
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-image.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-video.html57
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-video.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage.https.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-canvas.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-settings.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3.js283
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/imagedata-no-color-settings-crash.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000CC.pngbin0 -> 575 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000FF.pngbin0 -> 562 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000CCCC.pngbin0 -> 560 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000FFFF.pngbin0 -> 554 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000CC.pngbin0 -> 575 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000FF.pngbin0 -> 562 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000CCCC.pngbin0 -> 575 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000FFFF.pngbin0 -> 562 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-0.7333-0-0.svg3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-1-0-0.svg3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000CC.pngbin0 -> 620 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000FF.pngbin0 -> 607 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000CCCC.pngbin0 -> 605 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000FFFF.pngbin0 -> 599 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000CC.pngbin0 -> 620 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000FF.pngbin0 -> 607 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000CCCC.pngbin0 -> 620 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000FFFF.pngbin0 -> 607 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-BE000000.jpgbin0 -> 55590 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-FF000000.jpgbin0 -> 55590 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.mp4bin0 -> 3275 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.webmbin0 -> 604 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.mp4bin0 -> 3275 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.webmbin0 -> 605 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-p3d65.pngbin0 -> 5912 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-rec2020.pngbin0 -> 6136 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-srgb.pngbin0 -> 5319 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.avifbin0 -> 529 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.bmpbin0 -> 1738 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.icobin0 -> 1742 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.pngbin0 -> 5319 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.webpbin0 -> 252 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.avifbin0 -> 330 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.bmpbin0 -> 1738 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.gifbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.icobin0 -> 1742 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.jpgbin0 -> 898 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.pngbin0 -> 163 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.svg6
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.webpbin0 -> 208 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.pngbin0 -> 3164 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.pngbin0 -> 3175 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_opaque.pngbin0 -> 3262 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_transparent.pngbin0 -> 3271 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_opaque.pngbin0 -> 3338 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_transparent.pngbin0 -> 3347 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_opaque.pngbin0 -> 3144 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_transparent.pngbin0 -> 3153 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.pngbin0 -> 3165 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.pngbin0 -> 3174 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.pngbin0 -> 3265 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.pngbin0 -> 3272 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.pngbin0 -> 3339 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.pngbin0 -> 3348 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.pngbin0 -> 3147 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.pngbin0 -> 3153 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.pngbin0 -> 2868 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.pngbin0 -> 2876 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_opaque.pngbin0 -> 2867 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_transparent.pngbin0 -> 2672 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.pngbin0 -> 3173 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.pngbin0 -> 3177 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_opaque.pngbin0 -> 3250 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_transparent.pngbin0 -> 3255 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_opaque.pngbin0 -> 3325 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_transparent.pngbin0 -> 3331 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_opaque.pngbin0 -> 3132 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_transparent.pngbin0 -> 3136 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_opaque.pngbin0 -> 2853 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_transparent.pngbin0 -> 2857 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.mp4bin0 -> 1600 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.svg3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.webmbin0 -> 604 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000CC.pngbin0 -> 243 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000FF.pngbin0 -> 230 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000CCCC.pngbin0 -> 228 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000FFFF.pngbin0 -> 222 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000.svg3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000CC.pngbin0 -> 243 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000FF.pngbin0 -> 230 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.mp4bin0 -> 1600 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.webmbin0 -> 605 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000CCCC.pngbin0 -> 243 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000FFFF.pngbin0 -> 230 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.3.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.4.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.5.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.6.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.default.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.empty.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.end.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.negative.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonempty.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonfinite.html76
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.1.html53
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.2.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.1.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.2.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.1.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.2.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.3.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.4.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.5.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.3.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.4.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.5.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.6.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zeroradius.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.1.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.2.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.1.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.2.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.3.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.negative.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.nonfinite.html74
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.scale.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve1.html59
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve2.html58
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.end.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.start.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.transformation.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.1.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.2.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.beginPath.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.basic.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.nonfinite.html108
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.scaled.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.shape.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.1.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.2.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.empty.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.intersect.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.unaffected.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.2.html48
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.empty.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.newline.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.nextpoint.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.ellipse.basics.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.unaffected.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.add.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.1.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.2.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.3.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.initial.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.arc.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.1.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.2.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.html65
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bezier.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bigarc.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.edge.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.empty.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.nonfinite.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.outside.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.subpath.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.3.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.4.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.unclosed.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.winding.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInStroke.scaleddashes.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.invalid.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.multi.path.html48
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.basic.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nextpoint.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.details.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.basic.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.multiple.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.newsubpath.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.nonfinite.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.basic.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.nonfinite.html56
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.scaled.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.shape.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.basic.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.closed.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.2.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.negative.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.newsubpath.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.nonfinite.html56
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.selfintersect.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.winding.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.3.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.4.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.5.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.6.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.single.argument.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompoint.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompoint.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompoint.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompoint.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompoint.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompoint.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompoint.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompoint.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompoint.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.double.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.badinput.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.closed.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.2.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.3.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.4.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.negative.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.newsubpath.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.nonfinite.html115
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.1.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.2.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.negative.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noargument.html49
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noarugment.html49
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.none.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.toomany.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.selfintersect.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.winding.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.3.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.4.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.5.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.6.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.empty.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.arc.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.closed.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.corner.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.curve.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.line.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.rect.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale1.html53
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale2.html55
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.skew.html69
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.unaffected.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.union.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.basic.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.changing.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.multiple.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create.and.resize.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.basic.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.initial.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.this.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.type.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.zero.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.basic.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.double.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.initial.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.large.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.negative.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.nonfinite.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.round.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.this.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.type.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.zero.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.basic.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.clamp.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.double.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.invalid.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.large.crash.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.length.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonfinite.html74
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonpremul.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.alpha.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.cols.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rgb.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rows.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.range.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.rounding.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.negative.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.outside.html85
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.size.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.type.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.unaffected.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.zero.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.clamp.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.bounds.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html111
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.bounds.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.nan.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.properties.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.readonly.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.round.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.set.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.string.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.undefined.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.basic.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.clip.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.created.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.cross.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.negative.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.outside.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect2.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.zero.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.modified.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.nonfinite.html108
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.null.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.path.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unaffected.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unchanged.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.wrongtype.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.basic.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line-expected.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc.html23
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit-expected.html22
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip.html23
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.direction.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.fill_style.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.filter.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_kerning.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_stretch.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_variant_caps.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_alpha.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_composite_operation.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_enabled.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_quality.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.letter_spacing.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_cap.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash_offset.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_join.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_width.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.miter_limit.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_blur.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_color.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_x.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_y.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.stroke_style.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_align.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_baseline.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_rendering.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.transformation_matrix.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.word_spacing.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.basic.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.path.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalLR.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalRL.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.1.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.initial.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.invalid.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.valid.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.basic.html28
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.changed.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.removed.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.initial.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.invalid.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.valid.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.initial.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.invalid.html66
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.valid.html49
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high-manual.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high.pngbin0 -> 246 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low-manual.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low.pngbin0 -> 1586 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.basic.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.1.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.2.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.1.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.2.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.3.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.3.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.blur.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.x.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.y.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.1.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.2.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.basic.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.scale.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.section.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.2.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeX.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeY.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveX.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveY.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.outside.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.1.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.2.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.basic.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.1.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.2.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.1.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.2.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.3.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.align.default.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.align.invalid.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.align.valid.html39
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.default.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.invalid.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.valid.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.center.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.ltr.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.rtl.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.left.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.right.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.ltr.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.rtl.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.alphabetic.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.bottom.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.hanging.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.ideographic.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.middle.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.top.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic-manual.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic.pngbin0 -> 1137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.NaN.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.bound.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.fontface.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large-manual.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large.pngbin0 -> 1137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.negative.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.small.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.zero.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl-manual.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl.pngbin0 -> 1137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.unaffected.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.html44
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.notinpage.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.repeat.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.kern.consistent-manual.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.basic.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.end.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.nonspace.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.other.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.space.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.start.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic-manual.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic.pngbin0 -> 1634 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.unaffected.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.absolute.spacing.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.font-relative.spacing.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.with.uppercase.html57
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontStretch.settings.html81
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontVariant.settings.html78
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.invalid.spacing.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.change.font.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.measure.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.direction.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.rtl.text.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.textAlign.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.nonfinite.spacing.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.textRendering.settings.html80
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.change.font.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.measure.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.default.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.basic.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex2.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.family.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.invalid.html71
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.default.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.system.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.tiny.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.relative_size.html29
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.font.weight.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1.html18
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps2.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6.html19
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.actualBoundingBox.html61
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.advances.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.baselines.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-low-ascent.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-zero-descent.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights.html45
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-reduced-ascent.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-zero-descent.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.ahem.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.basic.html42
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.empty.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.space.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/2d.text.setFont.mathFont.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/direction-inherit-rtl.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/direction-ltr.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/direction-rtl.html31
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/parent-style-relative-units.html23
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/reference/direction-default-ref.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/text/reference/direction-rtl-ref.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.bitmap.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.clip.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.fillStyle.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalAlpha.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineCap.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineJoin.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineWidth.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.miterLimit.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.path.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowBlur.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowColor.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetX.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetY.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stack.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stackdepth.html36
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.strokeStyle.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html40
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.transformation.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.underflow.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.order.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.direction.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.nonfinite.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.radians.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrap.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrapnegative.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.zero.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.basic.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.large.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.multiple.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.negative.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.nonfinite.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.zero.html43
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.multiple.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.nonfinite.html109
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.skewed.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.identity.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.multiply.html33
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.nonfinite.html109
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.skewed.html54
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.basic.html32
-rw-r--r--testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.nonfinite.html41
-rw-r--r--testing/web-platform/tests/html/canvas/element/video/2d.video.invalid.html30
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.html58
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.shadow.html62
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.strokeText.html59
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.p3.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.srgb.html37
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.p3.canvas.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.with.putImageData.html56
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.jpeg.p3.canvas.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.p3.canvas.html46
-rw-r--r--testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.with.putImageData.html53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.html62
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.worker.js58
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.html141
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.worker.js137
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/META.yml7
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.getContext.options.any.js46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.html89
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.worker.js84
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.html62
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.worker.js57
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.html141
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.worker.js136
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.html24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js20
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.html25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js21
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html328
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js324
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.svg.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.worker.js47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.worker.js48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.worker.js49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html60
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.pngbin0 -> 219 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.pngbin0 -> 219 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.pngbin0 -> 396 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.pngbin0 -> 256 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.pngbin0 -> 248 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.worker.js47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.pngbin0 -> 229 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.worker.js44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.worker.js48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.worker.js48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html101
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.worker.js96
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.colormix.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html69
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.worker.js64
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html67
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.worker.js62
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html58
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.worker.js53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html57
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.worker.js52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html67
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.worker.js62
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html124
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js119
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html57
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html110
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.w.html124
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.html49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.worker.js44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html130
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.worker.js125
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.html56
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.worker.js51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.worker.js46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.w.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.w.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.html26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.w.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial-expected.html21
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.html25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.w.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside-expected.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.w.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-expected.html26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.html26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.w.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside-expected.html26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.html25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.w.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow-expected.html17
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow-expected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter-expected.html17
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.w.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending-expected.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow-expected.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.w.html56
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.w.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite-expected.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow-expected.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.w.html56
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.w.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.w.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.w.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.w.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.w.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.w.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.w.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending-expected.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.w.html59
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.w.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite-expected.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.w.html59
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.w.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow-expected.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.w.html58
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow-expected.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.w.html58
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow-expected.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.w.html58
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states-expected.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow-expected.html53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.w.html57
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states-expected.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.w.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow-expected.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html57
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-expected.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters-expected.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.w.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.w.html53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas-expected.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.w.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.w.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style-expected.html24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.w.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.w.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html19
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html21
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.w.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.w.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html19
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html21
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.w.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.w.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.html61
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.worker.js56
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.worker.js47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.html77
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.worker.js72
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.html61
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.worker.js56
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.html80
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.worker.js75
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.worker.js47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.html71
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.worker.js66
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.html78
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.worker.js73
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.worker.js47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.html60
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.worker.js55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.worker.js47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.html63
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.worker.js58
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.html60
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.worker.js55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.html69
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.worker.js64
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html165
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html337
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html128
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js126
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html19
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html80
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js77
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html240
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html12
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html112
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html201
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html76
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html83
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html142
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.html77
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.worker.js72
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.worker.js49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.html48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.worker.js43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.html75
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.worker.js70
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.html60
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.worker.js55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.html59
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.worker.js54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.html109
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.worker.js104
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.html49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.worker.js44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.html29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.worker.js24
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.html66
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.worker.js61
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.html48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.worker.js43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.worker.js46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.html49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.worker.js44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.html57
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.worker.js52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.html57
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.worker.js52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.html116
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.worker.js111
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.worker.js45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.pngbin0 -> 205 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.worker.js49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.html56
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.worker.js51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.html70
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.worker.js65
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.html47
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.worker.js42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.html75
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.worker.js70
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.html86
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.worker.js81
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.pngbin0 -> 221 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.html109
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.worker.js104
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.html25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.w.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.html23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.w.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line-expected.html19
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.w.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.html26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.w.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit-expected.html22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.w.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.w.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.html26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.w.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/set-proprietary-font-names-001-crash.html13
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.html53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.worker.js48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.html67
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.worker.js62
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.worker.js45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high.pngbin0 -> 246 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low.pngbin0 -> 1586 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.worker.js25
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.pngbin0 -> 206 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.html45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.worker.js40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic.pngbin0 -> 1137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large.pngbin0 -> 1137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl.pngbin0 -> 1137 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic.pngbin0 -> 1634 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.html58
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.worker.js53
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.html82
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.worker.js77
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.html79
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.worker.js74
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.html43
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.worker.js38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.html81
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.worker.js76
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.html46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.worker.js41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.html72
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.worker.js67
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.html28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js23
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.html21
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.w.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2-unexpected.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.html22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.w.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.html22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.w.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.html22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.w.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6-expected.html15
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.html22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.w.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.html50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js46
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.html26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.html32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.worker.js22
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch-ref.html19
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.html36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.worker.js31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.html37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.worker.js32
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.html41
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.worker.js36
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.html31
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.worker.js26
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.html44
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.worker.js39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.html35
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.worker.js30
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.html110
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.worker.js105
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.worker.js29
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.html110
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.worker.js105
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.html55
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.worker.js50
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.worker.js28
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.html42
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.worker.js37
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.worker.js34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.html38
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.worker.js33
-rw-r--r--testing/web-platform/tests/html/canvas/resources/2x2.pngbin0 -> 1575 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-frame.css21
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-frame.css.headers1
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-index.css31
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-index.css.headers1
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-spec.css50
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-spec.css.headers1
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-tests.css134
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-tests.css.headers1
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-tests.js221
-rw-r--r--testing/web-platform/tests/html/canvas/resources/canvas-tests.js.headers1
-rw-r--r--testing/web-platform/tests/html/canvas/tools/gentest.py18
-rw-r--r--testing/web-platform/tests/html/canvas/tools/gentest_union.py3
-rw-r--r--testing/web-platform/tests/html/canvas/tools/gentestutils.py369
-rw-r--r--testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py535
-rw-r--r--testing/web-platform/tests/html/canvas/tools/name2dir-canvas.yaml26
-rw-r--r--testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml22
-rw-r--r--testing/web-platform/tests/html/canvas/tools/name2dir.yaml25
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates.yaml80
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates/reftest.html14
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html34
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html29
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html37
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html68
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html45
-rw-r--r--testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js35
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml319
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml232
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml178
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml640
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml469
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml2104
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml680
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml1022
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/line-styles.yaml954
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/path-objects.yaml3403
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml1042
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml286
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml76
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml1090
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml1680
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/the-canvas-state.yaml89
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/transformations.yaml356
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml10
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml/element/meta.yaml539
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml/element/the-canvas-element.yaml143
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml539
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml/offscreen/the-offscreen-canvas.yaml269
-rw-r--r--testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html47
-rw-r--r--testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html46
-rw-r--r--testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html51
-rw-r--r--testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html49
-rw-r--r--testing/web-platform/tests/html/capability-delegation/delegation-consumes-activation.https.tentative.html55
-rw-r--r--testing/web-platform/tests/html/capability-delegation/delegation-sender-checks.tentative.html60
-rw-r--r--testing/web-platform/tests/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html29
-rw-r--r--testing/web-platform/tests/html/capability-delegation/resources/utils.js55
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/META.yml9
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/README.md1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html59
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/block-local-documents-inheriting-none.https.html112
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html51
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html64
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html49
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/coep-frame-javascript.https.html25
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/coep-on-response-from-service-worker.https.html111
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/META.yml7
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/README.md3
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache-storage.https.window.js150
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache.window.js84
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cross-origin-isolated.window.js49
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/dedicated-worker.https.window.js137
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/fetch.https.window.js127
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-credentialless.https.window.js37
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-none.https.window.js22
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-redirect.https.window.js41
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-require-corp.https.window.js38
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe.window.js47
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/image.https.window.js97
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/link.https.window.js99
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/redirect.window.js55
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-navigation.https.window.js139
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-subresource-corp.https.window.js74
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/common.js134
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/iframeTest.js85
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/redirect_none_to_credentialless.py14
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/script.https.window.js99
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-credentialless-proxy.https.window.js85
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-none-proxy.https.window.js87
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker.https.window.js113
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/shared-worker.https.window.js93
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js53
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/websocket.https.window.js77
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js74
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js170
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html20
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https.html128
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker.https.html214
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/header-parsing.https.html85
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/iframe-history-none-require-corp.https.html54
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html21
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/meta-http-equiv.https.html20
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html3
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/worker.js1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html14
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/worker.js1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/worker.js1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html49
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html19
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html21
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/none-load-from-cache-storage.https.html173
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-none.https.html89
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html93
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html91
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-unsafe-none.tentative.https.any.js2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html86
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-navigation.https.html170
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html206
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-document-reporting-endpoint.https.window.js140
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html209
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html.sub.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-frame-owner.https.html87
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-worker-owner.https.html89
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html48
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html48
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html74
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html179
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-revalidated-images.https.html76
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-none.https.html92
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html93
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw.https.html53
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-worker-script-revalidation.html25
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html251
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html24
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js63
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js19
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py31
-rwxr-xr-xtesting/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py15
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js7
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py7
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html91
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js6
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html3
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html38
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html34
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html29
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html24
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html4
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py41
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis9
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html5
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js25
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js23
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js27
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js30
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py24
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js7
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js31
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js12
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner-frame.html2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner.js36
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-support.js81
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html40
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/service-worker-cache-storage.https.html117
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/shared-workers.https.html228
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html21
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html61
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/META.yml9
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/README.md11
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html40
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html51
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html72
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html68
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html59
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox-navigate.https.html40
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox.https.html24
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigate-same-origin-csp-sandbox.html63
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-history-popup.https.html18
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html37
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html84
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write.html62
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-cuts-opener.https.html66
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-redirects-cuts-opener.https.html68
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html59
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-failures.https.html90
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html17
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html16
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-successes.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html40
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html88
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html86
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html85
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html88
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html87
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html87
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html85
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/javascript-url.https.html199
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/navigate-to-aboutblank.https.html193
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/navigate-top-to-aboutblank.https.html171
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html21
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-coop-by-sw.https.html136
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-meta-http-equiv.https.html24
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html90
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-same-origin-allow-popups.https.html96
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html17
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-cross-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-origin.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-site.https.html58
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html33
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/META.yml6
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro.https.html82
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro_cross-origin.https.html85
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro.https.html63
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro_cross-origin.https.html63
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro.https.html92
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro_cross-origin.https.html93
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro.https.html77
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro_cross-origin.https.html79
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro.https.html67
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro_cross-origin.https.html68
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro.https.html82
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro_cross-origin.https.html83
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-blur.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-close.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-closed.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-focus.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html66
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-length.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-get.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html71
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-get.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-1.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-2.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-top.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html13
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/report-to-both_coop-ro.https.html124
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html275
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html86
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html.sub.headers6
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-to-both_coop-ro.https.html124
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-same-origin-allow-popups.https.html111
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-unsafe-none.https.html130
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html86
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html.sub.headers6
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html71
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html96
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html.sub.headers3
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html32
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html.headers3
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html32
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html.headers3
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html73
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-opener.https.html67
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html85
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html.sub.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html126
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html.sub.headers3
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html173
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html.sub.headers4
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html216
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html.sub.headers3
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html110
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html126
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html.sub.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html111
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/reporting-common.js422
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/test-access-property.js65
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/try-access.js20
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resource-popup.https.html91
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/call-functionCalledByOpenee.html5
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/common.js86
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-coep.py84
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis24
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/csp-sandbox.py29
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/fully-loaded.js10
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/iframe-test.js241
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/popup-test.js99
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html45
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/redirect.py5
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-cleanup.html11
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-popup.html28
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/resources/universal-worker.js2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/README.md9
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-closed.https.html19
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-openee-rp-ro.https.html62
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-opener-rp-ro.https.html71
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-post-message.https.html19
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html65
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js144
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html94
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html91
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html90
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html91
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/named_targeting.https.html57
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html37
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html37
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-u.https.html37
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html37
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html44
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html62
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html.sub.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html55
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html.sub.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html55
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html.sub.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html43
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html43
-rw-r--r--testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/dom/aria-attribute-reflection.html487
-rw-r--r--testing/web-platform/tests/html/dom/aria-element-reflection-disconnected.html82
-rw-r--r--testing/web-platform/tests/html/dom/aria-element-reflection.html846
-rw-r--r--testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir-ref.html13
-rw-r--r--testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir.html17
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.body.html227
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.currentScript.html219
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.getElementsByClassName-null-undef.html30
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Element.getElementsByClassName-null-undef.html30
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/cross-domain.js1
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.embeds-document.plugins-01.html87
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.forms.html83
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByClassName-same.html17
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case-xhtml.xhtml21
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case.html16
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id-xhtml.xhtml20
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id.html15
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-interface.html16
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-liveness.html26
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace-xhtml.xhtml32
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace.html27
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements-xhtml.xhtml126
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements.html47
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef-xhtml.xhtml35
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef.html30
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param-xhtml.xhtml28
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param.html23
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-same.html17
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-01.html22
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-02.html20
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.images.html119
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.links.html27
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.scripts.html21
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-01.html32
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-02.xhtml37
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-03.html31
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-04.xhtml48
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-05.html42
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-06.html19
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-07.html11
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-08.html22
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html97
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-not-in-html-svg.html27
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-01.html19
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-02.html99
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-03.html18
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-04.html104
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-05.html104
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html104
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-07.html109
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-08.html31
-rw-r--r--testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-names.html101
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-01.html13
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-02.html14
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-03.html12
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-04.xhtml18
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-05.xhtml19
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-06.xhtml17
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-cookie.html41
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified-01.html25
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html15
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html.headers1
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-readyState.html33
-rw-r--r--testing/web-platform/tests/html/dom/documents/resource-metadata-management/support/document-lastModified-utils.js78
-rw-r--r--testing/web-platform/tests/html/dom/elements-embedded.js156
-rw-r--r--testing/web-platform/tests/html/dom/elements-forms-weekmonth.js42
-rw-r--r--testing/web-platform/tests/html/dom/elements-forms.js128
-rw-r--r--testing/web-platform/tests/html/dom/elements-grouping.js57
-rw-r--r--testing/web-platform/tests/html/dom/elements-metadata.js50
-rw-r--r--testing/web-platform/tests/html/dom/elements-misc.js60
-rw-r--r--testing/web-platform/tests/html/dom/elements-obsolete.js50
-rw-r--r--testing/web-platform/tests/html/dom/elements-sections.js64
-rw-r--r--testing/web-platform/tests/html/dom/elements-tabular.js109
-rw-r--r--testing/web-platform/tests/html/dom/elements-text.js63
-rw-r--r--testing/web-platform/tests/html/dom/elements/elements-in-the-dom/historical.html24
-rw-r--r--testing/web-platform/tests/html/dom/elements/elements-in-the-dom/unknown-element.html22
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/.htaccess16
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/classlist-nonstring.html44
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/custom-attrs.html29
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/data_unicode_attr.html22
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dataset-binding.window.js45
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dataset-delete.html54
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dataset-enumeration.html31
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dataset-get.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dataset-prototype.html26
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dataset-set.html44
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dataset.html38
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js115
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-div-append-child.html18
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-dynamic-changes.window.js276
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-form-associated.window.js71
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-bdi-script.html24
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12.html31
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15-ref.html24
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20-ref.html22
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21-ref.html22
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23-ref.html22
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24-ref.html22
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25-ref.html25
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26-ref.html25
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27-ref.html25
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28-ref.html25
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29-ref.html25
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30-ref.html25
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39-ref.html24
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40-ref.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41-ref.html24
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-utils.js8
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir-slots-directionality.html97
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-ref.html50
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN.html51
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L-ref.html61
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L.html62
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L-ref.html60
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L.html61
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L-ref.html60
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L.html61
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L-ref.html60
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L.html61
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L-ref.html60
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L.html61
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-ref.html53
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN.html54
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R.html59
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-ref.html53
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN.html69
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R-ref.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R.html70
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate-ref.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN-ref.html49
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN.html66
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs-ref.html61
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs.html76
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed-ref.html61
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed.html77
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN-ref.html49
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN.html66
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs-ref.html64
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs.html79
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed-ref.html64
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed.html80
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN-ref.html49
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN.html77
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs-ref.html60
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs.html84
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed-ref.html64
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed.html100
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/document-dir.html26
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/id-attribute.html130
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/id-name-specialcase.html30
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/id-name.html17
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute-shadow.window.js81
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute.window.js16
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01-ref.html20
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01.html58
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy-ref.html9
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy.html12
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/mapped-attribute-adopt-001.html23
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/style-01-ref.html24
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/style-01.html26
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-001.tentative.html39
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-002.tentative.html50
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003-crash.tentative.html27
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003.tentative.html71
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-001.html41
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-002.html41
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html41
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html.headers1
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-004.html42
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html41
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html.headers1
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html42
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html.headers1
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-007.html42
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-008.html41
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html41
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html.headers1
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-010.html42
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-011.html.headers1
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-007.html29
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-008.html29
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-009.html29
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-010.html29
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-011.html29
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-012.html29
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/title-manual.html8
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/translate-enumerated-ascii-case-insensitive.html26
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/translate-inherit-no-parent-element.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/global-attributes/translate-non-html-translation-mode.html46
-rw-r--r--testing/web-platform/tests/html/dom/elements/images/bypass-cache-revalidation.html37
-rw-r--r--testing/web-platform/tests/html/dom/elements/images/image.py28
-rw-r--r--testing/web-platform/tests/html/dom/elements/name-content-attribute-and-property.html57
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001a.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001b.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001c.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002a.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002b.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002c.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003a.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003b.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003c.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004a.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004b.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004c.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005a.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005b.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005c.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006a.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006b.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006c.html32
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007a.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007b.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007c.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008a.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008b.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008c.html36
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009a.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html33
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-001-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002a-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002b-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002c-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-003-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-004-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-005-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006c-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-007-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-008-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009b-ref.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/dynamic-getter.html88
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-first-letter-marker-multicol.html18
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-tests.js401
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter.html64
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-domnoderemoved-crash.html16
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter-tests.js42
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter.html88
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/multiple-text-nodes.window.js16
-rw-r--r--testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/outertext-setter.html180
-rw-r--r--testing/web-platform/tests/html/dom/elements/wai-aria/README.md1
-rw-r--r--testing/web-platform/tests/html/dom/historical.html55
-rw-r--r--testing/web-platform/tests/html/dom/idlharness-shadowrealm.window.js2
-rw-r--r--testing/web-platform/tests/html/dom/idlharness.https.html241
-rw-r--r--testing/web-platform/tests/html/dom/idlharness.worker.js22
-rw-r--r--testing/web-platform/tests/html/dom/new-harness.js11
-rw-r--r--testing/web-platform/tests/html/dom/original-harness.js339
-rw-r--r--testing/web-platform/tests/html/dom/reflection-embedded.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-forms-weekmonth.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-forms.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-grouping.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-metadata.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-misc.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-obsolete.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-original.html40
-rw-r--r--testing/web-platform/tests/html/dom/reflection-sections.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-tabular.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection-text.html17
-rw-r--r--testing/web-platform/tests/html/dom/reflection.js1065
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html44
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html28
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html37
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html33
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html28
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html36
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html31
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html31
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html35
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html31
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html36
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html38
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html38
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html38
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html48
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html61
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html21
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html21
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html20
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html18
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html25
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html28
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html27
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html86
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html83
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html25
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html22
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html21
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html26
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html27
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js1
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs1
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/target-red.css3
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js118
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/utils.js5
-rw-r--r--testing/web-platform/tests/html/dom/resources/self-origin-subframe.html22
-rw-r--r--testing/web-platform/tests/html/dom/self-origin.any.js5
-rw-r--r--testing/web-platform/tests/html/dom/self-origin.sub.html93
-rw-r--r--testing/web-platform/tests/html/dom/usvstring-reflection.https.html139
-rw-r--r--testing/web-platform/tests/html/editing/activation/click.html18
-rw-r--r--testing/web-platform/tests/html/editing/activation/click_checkbox.html23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/README23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/001.html67
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/002.html56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/003-1.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/003.html59
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/004.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/005.html49
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/006.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/007.html42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/008.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/009.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/010-1.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/010.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/011.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/012.xhtml42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/013.xhtml42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/014.xhtml42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/cross-domain/001-manual.xhtml59
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/helper-drag-me-green-box.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/canvas/helper-drop-here-canvas.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/crashers/dialog-001.html38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/cross-document/001-1.html47
-rw-r--r--testing/web-platform/tests/html/editing/dnd/cross-document/001.html23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/cross-document/002-manual.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/cross-document/003-1.html104
-rw-r--r--testing/web-platform/tests/html/editing/dnd/cross-document/003.html29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/001.xhtml71
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/002.xhtml54
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/003.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/004.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/005.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/006.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/007.xhtml57
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/008.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/009-1.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/009.xhtml30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/010-1.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/010.xhtml47
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/011.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/012.xhtml33
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/013-1.xhtml28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/013.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/014-1.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/014.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/015-manual.html63
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/016.xhtml58
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/017.xhtml58
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/018.xhtml59
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/019.xhtml59
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/020.xhtml64
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/021.xhtml60
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/022.xhtml74
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/023.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/024.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/025.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/026.xhtml57
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/027.xhtml58
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/028.xhtml62
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/029.xhtml58
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/030.xhtml72
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/031.xhtml141
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/032.xhtml125
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/033.xhtml125
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/034.xhtml126
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/035.xhtml126
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/036.xhtml125
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/037.xhtml127
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/038.xhtml146
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/039.xhtml131
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/040.xhtml131
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/041.xhtml132
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/042.xhtml132
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/043.xhtml131
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/044.xhtml132
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/045.xhtml57
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/046.xhtml172
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/047.xhtml157
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/048.xhtml157
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/049.xhtml158
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/050.xhtml158
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/051.xhtml157
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/052.xhtml158
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/053.html83
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/054.html83
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/055.html46
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/056.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/057.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/058.html79
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-constructor-001.html14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-types.html136
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/datatransferitemlist-remove.html23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/datastore/helper-drop-box-here.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dom/draggable.html207
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dom/events.html48
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dom/specials.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/draggable-areas/border-radius.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/draggable-areas/border.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/draggable-areas/box-shadow.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/draggable-areas/outline.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/draggable-areas/transform.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/draggable-areas/z-index.html35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/001.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/002.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/003.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/004.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/005.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/006.xhtml50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/007.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/008.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/009.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/010.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/011.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/012.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/013.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/014.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/015.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/016.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/017.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/018.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/019.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/020.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/021.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/022.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/023.xhtml40
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/024.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/025.html162
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/026.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/027.xhtml34
-rw-r--r--testing/web-platform/tests/html/editing/dnd/drop/028.html42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/001.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/002.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/003.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/004.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/005.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/006.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/007.html54
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/008.html56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/009.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/dropzone/010.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/001-1.xhtml51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/001.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/002-1.xhtml51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/002.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/003-1.xhtml51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/003.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/004.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/005.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/006.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/007.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/008.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/009.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/010.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/011.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/012.xhtml55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/013.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/014.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/015.xhtml56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/016.xhtml29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/017.xhtml29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/018.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/019.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/020.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/021.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/022.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/023.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/024.xhtml34
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/025.html69
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/026.html61
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/027.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/028.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/029.html79
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/030.html61
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/031-1.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/031.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/032.html81
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/033.html79
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/034.html56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/035.html83
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/036.html70
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/037-proposed.xhtml86
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/037-spec.xhtml88
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/038-proposed.html84
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/drag-event-div-manual.html62
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/drag-event-manual.html62
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/dragend-event-manual.html64
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/dragenter-event-manual.html67
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/dragleave-event-manual.html66
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/dragover-event-manual.html64
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/dragstart-event-manual.html62
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/drop-event-manual.html64
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-1.html205
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-2.html77
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-manual.html12
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/events-file-suite-manual.html176
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-001-manual.html60
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-002-manual.html101
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/events-suite-manual.html371
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-input-with-circle.xhtml51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-link-with-circle.xhtml51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-p-with-circle.xhtml51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/helper-drop-here-body-circle.xhtml51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/historical-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/events/relatedTarget-attribute-manual.html65
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/001.html103
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/002.html146
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/003.html90
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/004.html51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/005.html13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/006.html13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/007.html99
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/008.html113
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/009.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/010.html29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/011.html93
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/fail.txt1
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/001.html28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/002.html39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/003.html28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/004.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/005.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/006.html41
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/007.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/008.html41
-rw-r--r--testing/web-platform/tests/html/editing/dnd/file/prompt/009.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/historical.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/001.html51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/002.html51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/003.html31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/004.html53
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/005.html50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/006.html53
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/007.html37
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/008.html39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/009.html53
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/010.html39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/011.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/012-1.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/012.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/013-1.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/013.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/014-1.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/014.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/015.html49
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/016.html48
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/017.html50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/018.html49
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/021.html49
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/022.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/023.html47
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/024.html47
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/025.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/026.xhtml40
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/027.xhtml40
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/028.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/cross-domain/001-manual.xhtml49
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/helper-circle.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/helper-drag-me-data-url-image.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/helper-drop-horizontal-scrollbar.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/helper-drop-image-here.xhtml28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/images/helper-drop-vertical-scrollbar.xhtml13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactive/frames-1.html18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactive/frames.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactive/object-retention.html144
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactive/plugins.html28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/001.html29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/002.html29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/003.html29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/004.html29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/005.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/006.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/007.html36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/008.html36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/009.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/010.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/011.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/012.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/015.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/016.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/017.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/018.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/019.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/020.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/021.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/interactiveelements/022.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/media/001.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/000.html93
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/001.html128
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/002.html106
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/003.html132
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/004.html116
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/005.html115
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/006.html105
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/007.html116
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/008.html94
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/009.html98
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/010.html104
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/011.html107
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/012.html104
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/013.html104
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/014.html97
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/015.html99
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/016.html160
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/017.html137
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/018.html95
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/019.html96
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/020.html99
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/021.html104
-rw-r--r--testing/web-platform/tests/html/editing/dnd/microdata/test2
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/001-1.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/001.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/002.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/003.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/004.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/005.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/006.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/007-1.xhtml13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/007.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/008-1.xhtml13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/008.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/009-1.xhtml13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/009.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/010-1.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/010.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/011-1.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/011.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/012.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/013.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/014.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/015.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/016-1.xhtml30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/016.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/017.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/018.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/019.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/020.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/021-1.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/021.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/022-1.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/022.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/023-1.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/023.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-image-dont-drop.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-selection-dont-drop.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-here-reload.xhtml30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-image-now.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-link-now.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-now.xhtml13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-selection-here.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/001.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/002.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/003.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/004.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/005.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/006.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/007.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/008.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/009.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/010.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/011.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/012.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/013.xhtml28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/014.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/015.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/016.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/017.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/018.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/019.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/020.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/021.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/022.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/023.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/024.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/025.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/026.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/027.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/028.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/029.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/030.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/031.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/032.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/033.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/034.xhtml29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/035.xhtml30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/036.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/038.xhtml23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/039.xhtml23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/040.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/041.xhtml35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/042.html59
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/043.html68
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/044.html53
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/045.html55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/046.html54
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/047.html55
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/048.html54
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/049.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/050.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/051.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/052.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/053.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/054.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/055.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/056.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/057.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/058.html29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/059.html20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/060.html30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/061.html30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/062.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-001.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-002.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-003.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-004.html19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-005.html15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-006.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-007.html20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-008.html31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-009.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-001.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-002.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-003.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-004.html21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-005.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-006.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/overlay/oversized-001.html18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/alttab.html12
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cancel-middle-click.html50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cancel-right-click.html50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/close-drag-001-manual.html24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/close-drag-002-manual.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/close-drag-003-manual.html28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/close-drag-004-manual.html22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/close-drag-005-manual.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/close-drag-006-manual.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/001.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/002.html26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/003.html38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/004.html42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/005.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/006.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/007.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/008.html51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/009.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/010.html28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/cursors/011.html57
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/drag-keypress-manual.html28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/drag-link-manual.html12
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/drag-to-title-manual.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/file-drop-position.html23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/file-os-to-os.html13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/file-to-system.html50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/html-to-os-HELPER-FILE.html14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/html-to-os.html20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/html-unicode-to-os.html19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/inputs-no-js.html15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/001.html47
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/002.html49
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/003.html59
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/004.html31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/005.html35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/006.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/007.html34
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/008.html40
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/009.html42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/010.html39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/011.html40
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/012.html44
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/013.html47
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/014.html39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/015.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/016.html34
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/017.html40
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/018.html42
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/019.html39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/020.html33
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/021.html33
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/fail.txt1
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/file1.txt1
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/file2.txt1
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/interrupt/pass.txt1
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/keyboardshortcuts.html18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/all.html175
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/copy.html175
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/copylink.html175
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/copymove.html175
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/link.html175
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/linkmove.html175
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/move.html175
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzone.html70
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzoneevents.html82
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrag-manual.html125
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrop.html108
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/modifiers/scriptmodified.html99
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/moving-window.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/overlappingwindows.html56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/placeholderposition1.html14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/placeholderposition2.html14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/plugindrop.html24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-between-ui.html20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-from-os.html17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-from-ui.html20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-to-os.html14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui-via.html19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-ui-to-self.html21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/selection-unicode-to-os.html13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/taskbardrop.html23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/taskbarminimise.html12
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/text-os-to-os.html13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/platform/text-to-os.html19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/001.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/002.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/003.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/004.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/005.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/006.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/007.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/008.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/009-1.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/009.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/010-1.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/010.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/011-1.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/011.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/012-1.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/012-2.xhtml28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/reload/012.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/001.xhtml43
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/002.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/003.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/004.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/005.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/006.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/007.xhtml28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/008.xhtml28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/009.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/010.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/011.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/012.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/013.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/014.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/015.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/016.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/017.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/018.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/019.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/020.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/021.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/022-1.html26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/022.html28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-input.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-link.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-p.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/100x100-navy.pngbin0 -> 278 bytes
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/1x1-transparent.gifbin0 -> 43 bytes
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/32mb.py12
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/boxnavy.swfbin0 -> 4254 bytes
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/circle.pngbin0 -> 1252 bytes
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/crossorigin.sub.js13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/dragdrop_support.js9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/fail.pngbin0 -> 759 bytes
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/filler.html109
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/pass.pngbin0 -> 1689 bytes
-rw-r--r--testing/web-platform/tests/html/editing/dnd/resources/test-helper.js50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/001.xhtml38
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/002.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/003.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/004.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/005.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/006.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/007.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/roundtrip/008.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/001.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/002.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/003.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/004.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/005.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/006.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/007.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/008.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/009.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/010.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/011.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/012.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/013.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/014.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/015.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/016.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/017.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/018.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/019.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/020.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/021.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/022.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/023.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/024.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/025.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/026.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/027.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/028.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/029.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/030.xhtml23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/031.xhtml34
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/032.xhtml33
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/033.xhtml23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/034.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/035.xhtml30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/036.xhtml29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/037.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/038.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/039.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/040.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/041.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/042.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/043.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/044.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/045.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/046.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/047.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/048.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/049.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/050.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/051.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/052.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/053.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/054.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/055.xhtml23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/056.xhtml23
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/057.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/058.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/059.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/060.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/061.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/062.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/063.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/064-1.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/064.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/065.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/066.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/067-1.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/067-2.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/067.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/068-1.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/068-2.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/068.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/069.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/070.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/071.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/072.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/073.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/074.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/075.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/076.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/077.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/078.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/079.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/080.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/081.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/082.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/083.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/084.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/085.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/086.xhtml36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/087.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/088.xhtml29
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/089.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/090.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/091-1.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/091-2.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/091.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/092.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/093.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/094.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/095.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/096.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/097.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/098.xhtml26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/099.xhtml26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/100.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/101.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/102.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/103.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/104.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/105.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/106.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/107-1.html8
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/107.html5
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/108-1.html3
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/108.html5
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/109.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/110.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/111.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/112.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/113.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/114.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/115.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/116.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/117.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/118.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/119.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/120.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/121.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/122.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/123.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/124.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/125.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/126.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/127.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/128.xhtml26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/129.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/130.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/131.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/132.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/133.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/134.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/135.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/136.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/137.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/138.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/139.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/140.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/141.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/142.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/143.xhtml26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/144.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/145.xhtml31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/146.xhtml28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/147.html15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/148.html15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/149.html16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/150.html15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/151.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/152.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/153.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/154.xhtml17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/155.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/156.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/157.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/158.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/159.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/160.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/161.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/162.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/163.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/164.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/165.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/166.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/167.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/168.xhtml25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/169.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/170.xhtml33
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/171.xhtml39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/172.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/173.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-input.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-textarea.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input.xhtml10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-blue-box.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-input.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-textarea.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box-contenteditable.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-input.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-textarea.xhtml16
-rw-r--r--testing/web-platform/tests/html/editing/dnd/selection/helper-scroll-then-drop-input.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/001.svg9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/002.svg9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/003.svg13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/004.svg13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/005.svg8
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/006.svg9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/007.svg9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/008.svg13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/009.svg13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/010.svg8
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/011.svg15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/012.svg15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/013-1.xhtml15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/013.svg11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/014-1.xhtml18
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/014.svg10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/015-1.xhtml9
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/015.svg10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/016-1.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/016.svg10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/017.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/018.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/019.xhtml19
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/020.xhtml27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/021.xhtml26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/022.xhtml26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/023.xhtml26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/024.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/025.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/026.xhtml22
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/027.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/028.xhtml20
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/029.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/030-1.svg5
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/030.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/031.xhtml32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/032.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/033.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/034.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/035.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/036-1.svg4
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/036.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/037.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/038.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/039-1.svg4
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/039.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/040.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/041.xhtml11
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/042.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/043.xhtml21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/044.svg10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/045.svg10
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/046.svg14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/047.svg13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/048.svg13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/049.xhtml14
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/050.xhtml13
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/051.xhtml24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/052.svg28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/053.svg30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/054.svg35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/055.svg30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/056.svg69
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/057.svg121
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/058.svg36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/059-1.svg21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/059.svg15
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/060-1.svg12
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/060.svg28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/061.svg27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/062.svg17
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/063.svg24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/064.svg26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/065.svg28
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/066.svg31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/067.svg35
-rw-r--r--testing/web-platform/tests/html/editing/dnd/svg/helper-drop-selection-here-textArea.svg5
-rw-r--r--testing/web-platform/tests/html/editing/dnd/synthetic/001.html115
-rw-r--r--testing/web-platform/tests/html/editing/dnd/synthetic/005-manual.html340
-rw-r--r--testing/web-platform/tests/html/editing/dnd/synthetic/006-manual.html79
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/001-manual.html111
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/002-manual.html87
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/003-manual.html95
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/004-1.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/004-manual.html36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/005-1.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/005-manual.html36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/006-manual.html52
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/007-manual.html52
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/008-manual.html53
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/009-manual.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/010-manual.html25
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/011-manual.html63
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/012-manual.html62
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/013-manual.html24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/101-manual.html124
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/102-manual.html36
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/103-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/103-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/104-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/104-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/105-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/105-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/106-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/106-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/107-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/107-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/108-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/108-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/109-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/109-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/110-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/110-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/111-manual.html52
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/112-manual.html56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/113-manual.html50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/114-manual.html56
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/115-manual.html50
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/116-manual.html31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/117-1.html33
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/117-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/118-1.html32
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/118-manual.html27
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/201-manual.html83
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/202.html64
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustallow.html26
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustblock.html21
-rw-r--r--testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-showorigin.html31
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/DataTransfer-types-manual.html30
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/dndTransferCases-manual.html66
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/effectAllowed-manual.html76
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/files-manual.html81
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedDragDataTransfer-manual.html142
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedPasteDataTransfer-manual.html106
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/setData-manual.html78
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/types-manual.html72
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-datatransferitem-interface/getAsString-manual.html39
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dragevent-interface/dragevent-manual.html95
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable-enumerated-ascii-case-insensitive.html24
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable_attribute.html123
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_file_type-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_file-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_string-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_string_type-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element-manual.html51
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element_dbcs-manual.html46
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_link_element-manual.html48
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_COpy-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_STRING-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_String_-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_linK-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_copy-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_foo-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_link-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_move-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_bar_move-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_link-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_move_copy-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_not_specified-manual.html45
-rw-r--r--testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_unordered_unique_space_separated-manual.html53
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/autocapitalization/autocapitalize.html688
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html37
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-enumerated-ascii-case-insensitive.html45
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height-ref.html9
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height.html14
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block-ref.html7
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block.html11
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/selection-in-contentEditable-at-turning-designMode-on-off.tentative.html31
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/contenteditable/user-interaction-editing-contenteditable.html68
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/original-id.json1
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg34
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml32
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html32
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/references/spelling-markers-001-ref.html7
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spellcheck-enumerated-ascii-case-insensitive.html27
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-001.html17
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-002.html18
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-003.html17
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-004.html18
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-005.html18
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-006.html20
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-007.html27
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-008.html27
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-009.html27
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-010.html27
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/user-interaction-editing-spellcheck.html35
-rw-r--r--testing/web-platform/tests/html/editing/editing-0/writing-suggestions/writingsuggestions.html457
-rw-r--r--testing/web-platform/tests/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event.html38
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-element-fragment-navigation.html200
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-scroll-to-text-fragment.html68
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1-ref.html4
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1a.html8
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1b.html11
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1c.html12
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1d.html12
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1e.html10
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1f.html10
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1g.html11
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2-ref.svg7
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2.svg12
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-idl.html49
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-ua-stylesheet.html65
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-001.html49
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-002.html43
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-004.html28
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005-ref.html17
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005.html22
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-006-ref.html40
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-007.html52
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-text-fragment.html30
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-basic.html48
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-bubble.html30
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-with-anchor.html36
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/resources/container-ref.html17
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/resources/hidden-until-found-text-fragment.html25
-rw-r--r--testing/web-platform/tests/html/editing/the-hidden-attribute/resources/spacer-and-container-ref.html23
-rw-r--r--testing/web-platform/tests/html/embedded-content/the-img-element/attr-img-fetchpriority.html26
-rw-r--r--testing/web-platform/tests/html/iana/application-x-www-form-urlencoded/original-id.json1
-rw-r--r--testing/web-platform/tests/html/iana/application-xhtml-xml/original-id.json1
-rw-r--r--testing/web-platform/tests/html/iana/multipart-x-mixed-replace/original-id.json1
-rw-r--r--testing/web-platform/tests/html/iana/text-cache-manifest/original-id.json1
-rw-r--r--testing/web-platform/tests/html/iana/text-html/original-id.json1
-rw-r--r--testing/web-platform/tests/html/iana/web-scheme-prefix/original-id.json1
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist.html61
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/historical.html33
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html334
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html121
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmloptionscollection.html216
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/radionodelist.html78
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive-ref.html6
-rw-r--r--testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive.html20
-rw-r--r--testing/web-platform/tests/html/infrastructure/conformance-requirements/extensibility/foreign.html45
-rw-r--r--testing/web-platform/tests/html/infrastructure/fetching-resources/crossorigin-enumerated-ascii-case-insensitive.html34
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/cross-origin-transfer-resizable-arraybuffer.html28
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/messagechannel.any.js16
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html11
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js5
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/iframe-resizable-arraybuffer-helper.html11
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/post-parent-type-error.html9
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html132
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html35
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html55
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html68
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html8
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html8
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js33
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html31
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html22
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js7
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js9
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html14
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html3
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html6
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html7
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html7
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html10
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html10
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html10
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js7
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js4
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html5
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html5
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html5
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html10
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js14
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js33
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js18
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js.headers1
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-incrementer.js80
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-sab.js15
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html36
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-idb.any.js44
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-notifications-api.any.js28
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html49
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html15
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html80
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html22
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html56
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html31
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html72
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html.headers2
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-extra.html46
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js106
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html637
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js47
-rw-r--r--testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.js16
-rw-r--r--testing/web-platform/tests/html/infrastructure/terminology/plugins/sample.txt3
-rw-r--r--testing/web-platform/tests/html/infrastructure/terminology/plugins/text-plain.html32
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-detached-document.https.window.js34
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-javascript-nav.https.window.js22
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-uri-synthetic-document.html34
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-about-srcdoc-2.https.html67
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-after-nav-about-srcdoc.https.window.js83
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-initiated-grand-parent.https.window.js62
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.js40
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-open-about-blank.https.window.js24
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/matches-about-blank-base-url.window.js29
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/base-url/resources/send-back-base-url.html8
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/dynamic-urls.sub.html41
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/historical.sub.xhtml59
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/attributes.sub.html67
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/location.sub.html46
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/lone-surrogates.sub.html30
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html162
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/blank.py2
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/css-tmpl.py7
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js713
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resource.py103
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/stash.py15
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.htmlbin0 -> 2640 bytes
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.htmlbin0 -> 2646 bytes
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-8.html26
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1251.html26
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1252.html26
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.js27
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-changes-about-srcdoc.https.window.js47
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url.html93
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/terminology-0/multiple-base.sub.html17
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/terminology-0/nontraditional-about-srcdoc.html106
-rw-r--r--testing/web-platform/tests/html/infrastructure/urls/terminology-0/resources/echo-referrer-text.py4
-rw-r--r--testing/web-platform/tests/html/interaction/focus/chrome-object-tab-focus-bug.html43
-rw-r--r--testing/web-platform/tests/html/interaction/focus/composed.window.js16
-rw-r--r--testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-has-system-focus.html44
-rw-r--r--testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-level-apis.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/popup.html18
-rw-r--r--testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/test.html5
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focus-01.html45
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focus-02.html38
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focus-file-input.html18
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focus-input-type-switch.html26
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focus-keyboard-js.html26
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focus-management/focus-event-targets-simple.html33
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focus-management/focus-events.html33
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focused-element-move-documents-crash.html16
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/descends-into-extending-focusgroup.html37
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-focusgroup-root.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-non-focusgroup-item.html33
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item-and-wraps.html29
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item.html29
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-outside-focusgroup.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-wrap-when-not-supported.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/ascends-to-parent-focusgroup.html36
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-non-extending-focusgroup.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-move-when-axis-not-supported.html27
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-wrap-in-orthogonal-axis.html28
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/moves-when-only-current-axis-supported.html28
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html38
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/wraps-in-axis.html29
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item-and-skips-focusable-item.html32
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-empty-wrapping-focusgroup.html37
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-non-focusgroup-subtree.html35
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup-complex-case.html41
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup.html37
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/ascends-to-parent-focusgroup.html36
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-non-extending-focusgroup.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-move-when-axis-not-supported.html27
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-wrap-in-orthogonal-axis.html28
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/moves-when-only-current-axis-supported.html28
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html38
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/wraps-in-axis.html29
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-in-extending-focusgroup.html43
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully-complex-case.html40
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully.html32
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-focusgroup-root.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-non-item.html33
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item-and-wraps.html29
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item.html29
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-outside-focusgroup.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-in-focusgroup-with-no-items.html35
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-when-not-supported.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/descends-in-horizontal-inner-focusgroup.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-move-when-axis-not-supported.html27
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-wrap-even-when-other-axis-supported.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/moves-when-only-current-axis-supported.html28
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-and-skips-orthogonal-inner-focusgroup.html32
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-appropriate-focusgroup.html35
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-inner-focusgroup.html33
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-inside-extending-focusgroup.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-and-skips-non-focusable.html32
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-outside-extending-focusgroup.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-within-descendants.html37
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/descends-in-vertical-inner-focusgroup.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-move-when-axis-not-supported.html27
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-wrap-even-when-other-axis-supported.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/moves-when-only-current-axis-supported.html28
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html30
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-and-skips-orthogonal-inner-focusgroup.html32
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-appropriate-focusgroup.html35
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-inner-focusgroup.html33
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-and-goes-into-inner-focusgroup.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-successfully.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-to-parent-focusgroup.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-flow-only.html58
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-and-row-flow.html60
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-only.html58
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/colspan.html93
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table-not-focusgroup.html32
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table.html44
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/does-not-wrap-or-flow.html56
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/empty-spaces.html144
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/flows-in-both-axes.html60
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/moves-across-table-sections.html97
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/non-table.html26
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/relayout-before-navigation.html39
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-flow-only.html58
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-and-col-flow.html60
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-only.html58
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/rowspan.html148
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case-with-non-focusable-cell-in-the-center.html65
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case.html60
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/wraps-in-both-axes.html60
-rw-r--r--testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/resources/focusgroup-utils.js15
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html109
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html65
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html17
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/legend.html20
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html62
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html40
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html76
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html6
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html46
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-default-value.html21
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-negative.html45
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-order.html65
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-positive.html44
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-zero.html46
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/resources/frameset-using-page.html6
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter-frame.html55
-rw-r--r--testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter.html95
-rw-r--r--testing/web-platform/tests/html/interaction/focus/setSequentialFocusStartingPoint.tentative.html27
-rw-r--r--testing/web-platform/tests/html/interaction/focus/tabindex-focus-flag.html134
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-area.html22
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html29
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html16
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html21
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-nonexistent.html31
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html34
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-valid.html57
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-reconnected.html22
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html26
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later.html26
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first.html24
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html24
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-autofocus-on-changing-input-type.html27
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html44
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html17
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/not-on-first-task.html22
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html20
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html20
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html18
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/erase-first.css3
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-a.html4
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-anchor.html4
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html5
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html18
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/imagemap.html5
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html10
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/utils.js51
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html48
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html17
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html19
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html18
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html19
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/supported-elements.html83
-rw-r--r--testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html40
-rw-r--r--testing/web-platform/tests/html/links/icon/no-error-event.sub.html16
-rw-r--r--testing/web-platform/tests/html/links/icon/no-load-event.html16
-rw-r--r--testing/web-platform/tests/html/links/manifest/document-not-attached-manual.html17
-rw-r--r--testing/web-platform/tests/html/links/manifest/link-relationship/link-rel-manifest.html16
-rw-r--r--testing/web-platform/tests/html/links/manifest/link-relationship/link-tree-order-manual.html13
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-application-json-manual.html9
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest3
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest.headers1
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json-manual.html10
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest3
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest.headers1
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-invalid-manual.html9
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest3
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest.headers1
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-none-manual.html9
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest3
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest.headers0
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-text-json-manual.html9
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest3
-rw-r--r--testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest.headers1
-rw-r--r--testing/web-platform/tests/html/links/manifest/no-manifest-from-iframe-manual.html21
-rw-r--r--testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain-manual.html9
-rw-r--r--testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest3
-rw-r--r--testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest.headers1
-rw-r--r--testing/web-platform/tests/html/links/stylesheet/quirk-origin-check-recursive-import.html15
-rw-r--r--testing/web-platform/tests/html/links/stylesheet/quirk-origin-check.html13
-rw-r--r--testing/web-platform/tests/html/obsolete/META.yml2
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html52
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html95
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-02.html53
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-03.html53
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-04.html47
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/heading-obsolete-attributes-01.html18
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/nothing.html27
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/original-id.json1
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/script-IDL-event-htmlfor.html57
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-calc.html3
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-table.html1
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-adopt-to-inactive-document-crash.html9
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-down-manual.html8
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-left-manual.html8
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-right-manual.html8
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-up-manual.html8
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-events-historical.html32
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-loop.html27
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size-ref.html9
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size.html15
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount-effect-manual.html9
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount.html27
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrolldelay.html35
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-start-manual.html23
-rw-r--r--testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-stop-manual.html22
-rw-r--r--testing/web-platform/tests/html/rendering/bidi-rendering/slot-direction.window.js72
-rw-r--r--testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001-ref.html8
-rw-r--r--testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001.html22
-rw-r--r--testing/web-platform/tests/html/rendering/bidi-rendering/unicode-bidi-ua-rules.html25
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical.html8
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type.html10
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label-ref.html19
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label.html66
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-default.html5
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-zero.html5
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-default.html5
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-zero.html5
-rw-r--r--testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/textarea-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/dimension-attributes.html233
-rw-r--r--testing/web-platform/tests/html/rendering/interactive-media/links-forms-and-navigation/original-id.json1
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog-display.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog.html115
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align-ref.html76
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align.html71
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure-ref.html11
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html20
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles-iso-8859-8.html54
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles.html49
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-focusable.tentative.html51
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-tabbable.tentative.html58
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/support/dialog-framed.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-computed.html32
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-ref.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/placeholder-opacity-default.tentative.html25
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html118
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-fixedpos-crash.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001-ref.html59
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001.html77
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform-ref.html39
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform.html40
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display-ref.html47
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display.html64
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/hidden-elements.html52
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/TODO-lists.html17
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-text-align.html18
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html45
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-xhtml.xhtml40
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html35
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-alpha.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-roman.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-ref.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-alpha.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-roman.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-presentational-hints-ascii-case-insensitive.html34
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-styles.html228
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-display-contents-ref.html8
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-display-contents.html8
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-reversed-display-contents.html8
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-ref.html25
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-xhtml.xhtml14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-circle.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-disc.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-alpha.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-roman.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-none.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-ref.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-round.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-square.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-alpha.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-roman.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-ref.html21
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported.html8
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-decimal.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-alpha.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-roman.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-ref.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-alpha.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-roman.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/compare-computed-style.js7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-standards-mode.html8
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/br-wbr-content/content-property.tentative.html18
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-a.html33
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-q.html32
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-ref.html20
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-s.html33
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-x.xhtml22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-face.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-size.html103
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/sections-and-headings/headings-styles.html152
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign-ref.xhtmlbin0 -> 588 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_bottom.xhtmlbin0 -> 814 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_top.xhtmlbin0 -> 802 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables-xhtml.xhtml23
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables.html35
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/hidden-attr.html42
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/aqua-yellow-32x32.pngbin0 -> 156 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/blue-16x20-green-16x20.pngbin0 -> 132 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/fuchsia-32x32.pngbin0 -> 110 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/red-32x32.pngbin0 -> 110 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/yellow-32x32.pngbin0 -> 110 bytes
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-attribute.html194
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print-ref.html42
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html46
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1-ref.html46
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1.html37
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-notref.html40
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-ref.html30
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2.html31
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3-ref.html91
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3q.html95
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3s.html95
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive-ref.html41
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive.html45
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001-ref.html26
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001.html29
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width.html15
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-ref.html37
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-s.html55
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width.html54
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction-ref.html37
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction.html38
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-notref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction-ref.html45
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction.html46
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction-ref.html45
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction.html54
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height-ref.html30
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height.html31
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html29
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print.html33
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print-ref.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print.html26
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-ua-stylesheet.html44
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-valign-baseline-ascii-case-insensitive.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace-s.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent-ref.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-ref.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-s.html31
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width.html30
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border-ref.html18
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border.html23
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/META.yml4
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend-ref.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend.html52
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/crashtests/fieldset-middleclick.html37
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline-ref.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-formatting-context.html29
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-size.html42
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-negative-margin.html11
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-hittest.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha-ref.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha.html26
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-calculating-min-max-content.html34
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block-ref.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block.html35
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-before-legend.html41
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-percentage-size.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl-ref.html24
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl.html26
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-crash.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-default-style.html57
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-display.html37
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-div-display-contents.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-oof-container-crash.html46
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo-ref.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo.html27
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html95
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-foo-ref.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-generated-content.html30
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-grid.html85
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html34
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size.html36
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-multicol.html29
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview.html30
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden.html11
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-ref.html64
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow.html82
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order-ref.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html17
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-block-size.html64
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-padding.html31
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize-ref.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize.html15
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-shadow-dom.html28
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez-ref.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical-ref.html25
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html27
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/flex-legend-float-abspos.html97
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation.html20
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/insert-legend-in-multicol-fieldset-crash.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-justify-self.html34
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-text-align.html27
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align.html50
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins-ref.html37
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins.html32
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-formatting-context.html81
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2-ref.html142
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2.html139
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-ref.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-position-centering.html58
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering.html11
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering-ref.html34
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering.html119
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html40
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-dynamic-update.html19
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-abspos.html88
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html31
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-hover.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-inline-position-with-fieldset-padding.html42
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering-ref.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering.html17
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item.html11
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-margin-inline.html50
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order-ref.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order.html13
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2-ref.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2.html21
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-ref.html10
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative.html11
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sans-fieldset-display.html68
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sticky-crash.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall-ref.html20
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall.html20
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend.html62
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/min-inline-size.html39
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/multicol-legend-becomes-floated-crash.html11
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/no-red-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/resources/fieldset-vertical.css18
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/second-legend-becomes-rendered-legend-crash.html20
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-crash.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-ref.html34
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content.html35
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/different-writing-modes.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/exceed-then-not-exceed.html32
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/frameset-visibility-hidden.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-abssize.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-percentage.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-relsize.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-abssize.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-percentage.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-relsize.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/multicol-table-crash.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/different-writing-modes-ref.html6
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/empty-ref.html1
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/green-ref.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/green.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/red.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align-ref.html31
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align.html24
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color-ref.html22
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color.html7
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/hr.html55
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/setting-overflow-visible.html64
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width-ref.html19
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width.html15
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1a.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1b.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1c.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1d.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1e.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1f.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1g.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1h.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1i.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1j.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1k.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1l.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2a.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2b.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2c.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2d.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2e.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2f.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2g.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2h.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2i.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2j.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2k.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2l.html5
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_link.xhtml16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff-ref.html14
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff.xhtml12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/crashtests/body-huge-attr-value-crash.html9
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-body-margin-attributes.html32
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-marginwidth-marginheight.html12
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html16
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html29
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html30
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html24
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/big-page.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-marginwidth-marginheight.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-topmargin-leftmargin.html2
-rw-r--r--testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/test-body.xhtml8
-rw-r--r--testing/web-platform/tests/html/rendering/pixel-length-attributes.html173
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/align.html59
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html52
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html25
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/embedded-and-images-presentational-hints-ascii-case-insensitive.html46
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html15
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.html36
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html120
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim-ref.html11
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim.html12
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-empty-alt-replaced.html23
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-no-alt-replaced.html30
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-replaced-box-while-loading.html29
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-title-only-w-sizing.html25
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border-ref.xhtml9
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border_percent.xhtml10
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-1.html3
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-2.html3
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-content-crash.html15
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt-ref.html18
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt.html23
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1.html10
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned-ref.html8
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned.html16
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border-ref.xhtml8
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_perc.xhtml9
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_pixel.xhtml9
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/picture-aspect-ratio.html242
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/resources/aspect-ratio.js14
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-aspect-ratio.html72
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html41
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-001.html7
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-002.html12
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-without-controls.html11
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback.html26
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit.html31
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale.html23
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale_ref.html14
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_a.html15
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_ref.html14
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/audio-controls-intrinsic-size.html25
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html21
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub.html21
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub.html17
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration.html9
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/resources/tall.html6
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub.html20
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode.html8
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp-ref.html5
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp.html8
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/img-sizes-auto.html53
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content.html14
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print.html24
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/space-ref.html13
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/images/space.html14
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/resources/svg-sizing.js418
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-embedded-sizing.js96
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-auto.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-fixed.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-percentage.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-auto.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-fixed.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-percentage.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-auto.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-fixed.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-percentage.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html29
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.js79
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br-ref.html21
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br.html32
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos-ref.html11
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos.html19
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref-2.html24
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref.html24
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001.html24
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-ref.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size.html38
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height-ref.html23
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height.html36
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty-ref.html35
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty.html33
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size-ref.html8
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size.html9
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform-ref.html5
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform.html8
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html12
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001-ref.html32
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001.tentative.html43
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001-ref.html34
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001.tentative.html46
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001-ref.html32
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001.tentative.html37
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001-ref.html34
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001.tentative.html46
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/tools/gen-svgsizing-tests.py55
-rw-r--r--testing/web-platform/tests/html/rendering/support/test-ua-stylesheet.js74
-rw-r--r--testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change.html10
-rw-r--r--testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/mouse-cursor-imagemap.html19
-rw-r--r--testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links-wrapped-in-svg.historical.html52
-rw-r--r--testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links.historical.html50
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/auto-expand-details-text-fragment.html30
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-after.html12
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-autofocus-crash.html25
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-before.html12
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-blockification.html35
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored-ref.html21
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored.html26
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref.html46
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref2.html37
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001.tentative.html51
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002-ref.html10
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002.tentative.html17
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-1-print.html20
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-2-print.html20
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-1-print.html20
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-2-print.html20
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001-ref.html13
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001.tentative.html19
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002-ref.html14
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002.tentative.html21
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-revert-ref.html19
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-revert.html18
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/details-two-pages-print-ref.html12
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/empty-crash.html7
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/resources/auto-expand-details-text-fragment.html27
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/single-summary.html6
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex-ref.html105
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex.html112
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid-ref.html72
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid.html77
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex-ref.html105
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex.html112
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid-ref.html72
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid.html77
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001-ref.html28
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001.html34
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002-ref.html13
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002.html23
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol-ref.html20
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol.html22
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration-ref.html11
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration.html11
-rw-r--r--testing/web-platform/tests/html/rendering/the-details-element/two-summaries-removal-crash.html17
-rw-r--r--testing/web-platform/tests/html/rendering/unmapped-attributes.html95
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-001.html23
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002-ref.html3
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002.html20
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001-ref.html7
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001.html21
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002-ref.html6
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002.html19
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-003.html19
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/appearance/default-styles.html96
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/baseline-alignment-and-overflow.tentative.html237
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box-ref.html31
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box.html31
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/computed-style.html52
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/display-other.html52
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/flex.html61
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/grid.html30
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level-ref.html10
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level.html25
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip.html15
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2-mismatch.html2
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2.html6
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-mismatch.html2
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline.html6
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration.html13
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/button-layout/shrink-wrap.html42
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/field-sizing-input-number.html149
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/field-sizing-input-text.html154
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/field-sizing-select.html87
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/field-sizing-textarea.html141
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked-notref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked.html5
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-no-centering.html19
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate.tentative.html7
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-notref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-ref.html4
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl-notref.html1
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl.tentative.html3
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.html14
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.window.js34
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height-ref.html6
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height.html10
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-baseline-print.html25
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-baseline-ref.html16
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-baseline.html25
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-content-size-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-content-size.html11
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-date-no-resize-on-hover.html35
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-no-centering-ref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked-notref.html2
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked.html5
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-radio-no-centering.html19
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-text-size.html45
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-time-content-size-ref.html9
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/input-time-content-size.html11
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/select-wrap-no-spill.optional.html23
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/textarea-cols-rows.html64
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label-quirks.html20
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label.html21
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling-ref.html21
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling.html21
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label-to-empty-string.html21
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label.html14
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-and-text.html14
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-ref.html12
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-only-label.html14
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/option-rm-label.html21
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/select-as-listbox-default-styles.tentative.html113
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation-ref.html15
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation.html31
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-001.html11
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-002.html11
-rw-r--r--testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-ref.html7
-rw-r--r--testing/web-platform/tests/html/resources/common.js201
-rw-r--r--testing/web-platform/tests/html/scripting/the-script-element/attr-script-fetchpriority.html26
-rw-r--r--testing/web-platform/tests/html/scripting/the-script-element/resources/script.js0
-rw-r--r--testing/web-platform/tests/html/select/options-length-too-large.html50
-rw-r--r--testing/web-platform/tests/html/semantics/disabled-elements/disabled-checkbox-click.html26
-rw-r--r--testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch-additional.tentative.html74
-rw-r--r--testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch.tentative.html57
-rw-r--r--testing/web-platform/tests/html/semantics/disabled-elements/disabledElement.html44
-rw-r--r--testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled-keyboard.tentative.html135
-rw-r--r--testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled.tentative.html264
-rw-r--r--testing/web-platform/tests/html/semantics/disabled-elements/fieldset-event-propagation.tentative.html55
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/conditionally-block-rendering-on-link-media-attr.html27
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-link-stylesheet-does-not-block-script.html21
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-style-element-does-not-block-script.html21
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/link-stylesheet-with-non-match-media-does-not-block-render.tentative.html21
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-link-stylesheet-does-not-block-script.html20
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-style-element-does-not-block-script.html20
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-match-block-script.html17
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-not-match-does-not-block-script.html17
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/stylesheet.py10
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/link-style.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/utils.js20
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/styling/LinkStyle.html88
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/styling/support/alternate.css7
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/styling/support/emptytitle.css4
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/styling/support/normal.css5
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/styling/support/notitle.css4
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/styling/support/unmatch.css4
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-data.html32
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-javascript.html32
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_about_blank.html19
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_empty.html29
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_invalid.html12
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_specified.html33
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_unspecified.html30
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_multiple.html29
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_srcdoc.html19
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_iframe_src_navigation.html10
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_location_assignment.html10
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example.html7
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example2.html5
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/document-without-browsing-context.html35
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-error-fired-before-scripting-unblocked.html25
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.html6
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.https.html6
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-event.html17
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-fired-before-scripting-unblocked.html24
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-error-events.html21
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-load-events.html21
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive-notref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive.html22
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute.html44
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rellist.html25
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-01.html37
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-limited-quirks.html7
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-quirks.html7
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute-ref.html3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute.html9
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/bad.css4
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/css.py7
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/empty-href.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/good.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-load-error-events.sub.js192
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-rel-attribute.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-style-error.js47
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/neutral.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/stylesheet.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/style.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href-ref.html8
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href.html13
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href.html12
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media.html17
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-not-removed-until-next-stylesheet-loads.html22
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base-ref.html11
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base.html11
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.css3
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.py9
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-attribute-changes.html35
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-empty-content-value.html14
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-first-valid-applies.html16
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-insert.html26
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-no-content-value.html14
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-normal-descendant-change.html20
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove-head.html17
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove.html19
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-body.html12
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-head.html10
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-shadow-tree.html22
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/support/compute-root-color-scheme.js28
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-1.html23
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-2.html13
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html57
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html55
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/dynamic-append.html31
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/not-in-shadow-tree.html38
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html147
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/remove-from-document.html37
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/;url=foo1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/__dir__.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo'bar1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.py4
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.sub.html1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/ufoo1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urfoo1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/url foo1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urlfoo1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/x;url=foo1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-lower.html5
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-message.js1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-mixed.html5
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-other.html5
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive.html32
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/the-lang-attribute-012.html51
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/historical.html14
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment.xhtml18
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/mutations.window.js48
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-error-01.html32
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-load-after-mutate.html16
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_disabled.html39
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_events.html36
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_async.html25
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_event.html28
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media.html40
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media_change.html43
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_non_matching_media.html20
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_change.html39
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_html.html71
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_svg.svg75
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/support/css-red.txt1
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive.html16
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-01.html25
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-02.xhtml30
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-03.html32
-rw-r--r--testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-04.xhtml37
-rw-r--r--testing/web-platform/tests/html/semantics/edits/the-del-element/del_effect.html19
-rw-r--r--testing/web-platform/tests/html/semantics/edits/the-ins-element/ins_effect.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-html.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-img.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-js.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-mp4.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-not-found.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-type-only.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/bfcache/resources/common.js46
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html254
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_controls_present-manual.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_base.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_seek_to_eos.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_overriding_volume-manual.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_present-manual.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_check.html58
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_loudest-manual.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_silent-manual.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-supported-by-feature-policy.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-with-broken-track.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/controlsList.tentative.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/error-codes/error.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay_noautoplay.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough_noautoplay.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata_noautoplay.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata_noautoplay.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart_noautoplay.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_canplaythrough.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_playing.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadedmetadata_loadeddata.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadstart_progress.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause_noautoplay.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play_noautoplay.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing_noautoplay.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress_noautoplay.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate_noautoplay.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_volumechange.html72
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/historical.html56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/addTextTrack.html116
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/crossOrigin.html60
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/textTracks.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/default.html55
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/kind.html146
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/label.html83
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/readyState.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/src.html43
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/srclang.html82
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/track.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/activeCues.html101
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html68
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/constants.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/cues.html100
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/kind.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/label.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/language.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/mode.html55
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/oncuechange.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/removeCue.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/endTime.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/id.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onenter.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onexit.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/pauseOnExit.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/startTime.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/track.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getCueById.html53
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getter.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/length.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getTrackById.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/length.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onaddtrack.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onremovetrack.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/constructor.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/createEvent.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/autoplay-overrides-preload.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html91
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-removes-queued-error-event.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-moved.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-addEventListener.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-no-listener.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-onerror.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-currentSrc.html93
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor-no-src.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-in-sync-event.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-fragment-into-document.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-document.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-iframe.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-parent-into-document.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-div.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-namespace.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-networkState.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-not-in-document.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-load.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause-networkState.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-play.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document-networkState.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-src.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-in-namespace.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-networkState.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-not-in-document.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-control.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-br.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-source.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-text.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source-after.html56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source.html43
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-text.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-source.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-src.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-resumes-onload.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media-env-change.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/delayed-broken-video.py5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/media-min-width.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-beforeunload-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-dialogs-manual.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-print-manual.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/location-of-the-media-resource/currentSrc.html48
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/media_fragment_seek.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html123
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_loadstart.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_progress.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_initial.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/currentTime.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/duration.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_false_during_play.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_true_during_pause.html48
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/pitch-detector.js58
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html57
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-different-load.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-networkState.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document.html37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/play-in-detached-document.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/playbackRate.html53
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/preload_reflects_none_autoplay.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/preserves-pitch.html132
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-with-slow-text-tracks.html46
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay.html73
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplay.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplaythrough.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadeddata.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadedmetadata.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_playing.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_initial.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-currentTime.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-max-value.htm23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-negative-time.htm23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_object_blob.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_reflects_attribute_not_source_elements.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cloneNode.html87
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/003.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/004.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/005.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/006.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/007.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/008.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/009.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/010.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/011.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/012.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/013.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/014.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/015.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/016.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/017.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/018.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/019.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/020.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/021.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/022.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/023.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/024.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/025.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/026.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/027.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/028.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/029.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/030.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/031.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/032.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/033.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/034.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/035.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/036.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/037.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/038.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/039.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/040.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/041.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/042.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/043.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/044.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/045.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/046.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/common.js144
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/cors-tester.py50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/remove-cookie.html6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/set-cookie.html6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/crashtests/track-element-src-aborted-load-onerror-crash.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/no-cuechange-before-play.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning-bad.vtt20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning.vtt20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position-bad.vtt21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position.vtt28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-bad.vtt22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-ltr.vtt22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment.vtt22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/bom.vtt10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-fast.vtt13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-gaps.vtt18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-html.vtt18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions.vtt18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class-bad.vtt17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id-error.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id.vtt18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id-error.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id.vtt11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-cuetext.vtt6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-header.vtt6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-note.vtt9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align-bad.vtt18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align.vtt19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-bad.vtt17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size.vtt19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-chrono-order.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-no-separation.vtt11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-overlapping.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues.vtt17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/default-styles.vtt19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/degenerate-cues.vtt5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/empty-cue.vtt11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities-wrong.vtt15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities.vtt30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/interspersed-non-cue.vtt9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/iso2022jp3.vtt10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/large-timestamp.vtt5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position-bad.vtt30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position.vtt37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup-bad.vtt22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup.vtt22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata-area.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata.vtt38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/missed-cues.vtt31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-newline-at-eof.vtt6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-timings.vtt13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-webvtt.vtt10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-bad.vtt39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-ltr.vtt21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning.vtt21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings-bad-separation.vtt20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings.vtt18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/simple-captions.vtt17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/sorted-dispatch.vtt34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp-bad.vtt17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour-error.vtt22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour.vtt14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour-errors.vtt22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour.vtt18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-whitespace.vtt51
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.de.vtt4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.en.vtt4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.fr.vtt4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.vtt4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/unsupported-markup.vtt23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/utf8.vtt10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-bad.vtt17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-ltr.vtt20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign.vtt20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice-bad.vtt17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice.vtt15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/vp8-vorbis-webvtt.webmbin0 -> 143662 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-file.vtt9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-rubbish.vtt10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-clear-cues.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-empty-string.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-remove-cue.html92
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-addtrack-kind.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-api-texttracks.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-change-event.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-css-cue-pseudo-class.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-empty.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable-fragment.html85
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable.html99
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-duration.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp-events.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-order.html83
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added-ref.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added.html37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed-ref.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit-ref.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video-ref.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange-dynamically-created-track-element.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange.html41
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-exit.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-seeking.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-missed.html59
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-pause-on-exit.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-seeking.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-sorted-before-dispatch.html48
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-data-url.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-default-attribute.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-delete-during-setup.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled-addcue.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-dom-change.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-aborted-load.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html86
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html55
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-helpers.js83
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-id.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-insert-after-load.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-large-timestamp.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-error-readyState.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-element-readyState.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-src-readyState.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-disabled.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html74
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-triggers-loading.html37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html76
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-node-add-remove.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-active-cue.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-by-setting-innerHTML.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-insert-ready-state.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-quickly.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track-inband.html79
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-metadata.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-texttracks.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-positioning.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-text-line-position.html54
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-alignment.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-blank-lines.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-bom.html34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-class-markup.html56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-identifiers.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-no-id.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-recovery.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size-align.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-degenerate-cues.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-empty-cue.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-entities.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-header-comment.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-interspersed-non-cue.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-line-position.html58
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-magic-header.html57
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-markup.html90
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-newlines.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-no-timings.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines-ref.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-positioning.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-settings.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timestamp.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-hour.html61
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-no-hours.html67
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-whitespace.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end-ref.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-unsupported-markup.html37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-utf8.html57
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-valign.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-voice.html54
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/vtt-cue-float-precision.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/user-interface/muted.html169
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_controls_present-manual.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_loop_base.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_overriding_volume-manual.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_present-manual.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_check.html58
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_loudest-manual.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_silent-manual.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/media-elements/volume_nonfinite.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/resources/common.js45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html3
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/resources/should-load.html3
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/resources/should-not-load.html5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-coords.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-download-click.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-processing.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-shape.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-stringifier.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-area-element/resources/area-download-click.html5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-area-element/support/hit-test.js42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-appendChild-to-inactive-document-crash.html6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-play-in-inactive-document-crash.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_001.htm18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_002.htm18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_constructor.html56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_content-ref.htm13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/2d-getcontext-options.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-001.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-002.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-003.tentative.html48
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-004.tentative.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-005.html61
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.arguments.missing.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.casesensitive.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.emptystring.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badname.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badsuffix.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.nullsuffix.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.unicode.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.basic.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.multiple.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.nested.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/historical.html77
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/imagedata.html58
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.clip.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.pngbin0 -> 107 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.gradient.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.pattern.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.transform.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.dataURI.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.cross.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.redirect.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.cross.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.redirect.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.cross.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.redirect.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.cross.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.redirect.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.cross.html41
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.redirect.html41
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.cross.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.redirect.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.cross.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.redirect.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.cross.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.redirect.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.cross.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.redirect.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.cross.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.redirect.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.pngbin0 -> 272 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.get.pngbin0 -> 125 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.set.zero.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.pngbin0 -> 125 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.pngbin0 -> 125 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidlzero.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.pngbin0 -> 168 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.set.pngbin0 -> 125 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.pngbin0 -> 117 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.jpeg.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.null.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.png.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.1.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.2.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.3.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.bogustype.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.default.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.pngbin0 -> 208 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.html56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.pngbin0 -> 213 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.notnumber.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.outsiderange.html43
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpg.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.ascii.html35
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.unicode.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.nocontext.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.pngbin0 -> 242 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.pngbin0 -> 220 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.unrecognised.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zeroheight.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerosize.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerowidth.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.delete.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.exists.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.extend.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.name.html27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.prototype.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.replace.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/document-getters-return-null-for-cross-origin.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-change-src.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-dimension.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-focus.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-gbcr.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute-ref.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-iframe.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-ignored-in-media-element.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-2.html56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-subdocument.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-named-attribute-detached-context-crash.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-network-error.sub.html54
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-01.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-02.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-03.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-04.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-ref.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-svg-navigation-resets-size.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/historical.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-frame-element/document-getters-return-null-for-cross-origin.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_child.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_grandchild.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_parentage.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/content_document_changes_only_after_load_matures.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom-part-2.window.js59
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom.window.js37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_child.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_grandchild.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.sub.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/document-getters-return-null-for-cross-origin.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/historical.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/hittest-detached-iframe-crash.html9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allow.html66
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html95
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-append-to-child-document.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-display-none-with-object.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-document-move-crash.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-first-load-canceled-second-load-blank.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated-ref.html6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-load-event.html43
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-eager.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-2.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-3.html45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url.html45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-script-disabled-iframe.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-far.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal-far.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-2.html57
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-3.html65
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-4.html58
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-5.html61
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested.html57
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-001.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-load-event.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-queued-navigations.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-times.html69
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-referrerpolicy-change.sub.html45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-to-eager.html55
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html110
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes-ref.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-network-error.sub.html54
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-nosrc.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-synchronously-discard.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_harness.js27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_javascript_url_01.htm61
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_navigate_ancestor-1.sub.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_remove_src.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_script.html46
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-1.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-2.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-3.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation-manual.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation_without_user_gesture.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_allow_downloads.tentative.html48
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_block_downloads.tentative.html73
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-1.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-2.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-3.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-4.html13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-1.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-2.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_descendants.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back-2.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_forward.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_itself.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html64
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_allow_downloads.sub.tentative.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_block_downloads.sub.tentative.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-1.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-2.html28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-3.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_allow_downloads.tentative.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_block_downloads.tentative.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_01.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_02.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_04.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/multiple-iframes-with-allow-scripts-crash.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/empty.html1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/hello-world.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/iframe-loading-lazy-in-viewport.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/post-origin-to-opener.html3
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/sandbox-top-navigation-helper.js76
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/subframe.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/unload-reporter.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_child.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_grandchild.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_parentage.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-ascii-case-insensitive.html41
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed-frame.html87
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html100
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-toggle-in-inactive-document-crash.html9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-special-cases.tentative.sub.window.js47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.js56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-escalate-privileges.tentative.sub.window.js63
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.js50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_001.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_002.htm25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_003-manual.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_004.htm27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_005.htm33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_006-manual.htm37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_007-manual.htm37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_008-manual.htm37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_010-manual.htm37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_011.htm65
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_012.htm34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_013.htm36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_014.htm34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_015.htm34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_016.htm33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_017.htm34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_018.htm34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_019.htm34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_020-manual.htm34
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_021-manual.htm44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_022-manual.htm37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_023.htm33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_024.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_025.htm30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_026.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_027.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_028.htm33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_029.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_030.htm31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_031.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_032.htm32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor.html138
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-anchor.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-attribute-reset.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_change_hash.html68
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_process_attributes.html76
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/stash.py5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/blank.htm1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/document-with-embedded-svg.html9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/download_stash.py32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-checks-contentDocument.html3
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-opens-modals.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-on-popup.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-without-user-gesture-failed.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-send-message-to-the-opener.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-tries-to-navigate-parent-and-sends-result-to-grandparent.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-history.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-its-child.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-its-child.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-itself.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-which-content-height-equals-400px.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-with-object.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_001.htm11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_002.htm21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_003.htm12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_004.htm11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_006.htm12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_007.htm12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_008.htm9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_010.htm9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_012.htm13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020.htm28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020a.htm13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021.htm28
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021a.htm13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_022.htm11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_023.htm15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_024.htm13
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_026.htm12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_027.htm21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_028.htm20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_029.htm19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_031.htm19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_032.htm27
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_block_modals.js18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_download_helper.js37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/load-into-the-iframe.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/navigation-changed-iframe.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdfbin0 -> 80990 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdf.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_allow_script.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_helper.js14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-fail.htm9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-iframe-content.htm9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-pass.htm9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/svg.svg1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/3.jpgbin0 -> 91072 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.py4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/Image-constructor.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adopt-from-image-document.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adoption.html91
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/already-loaded-image-sync-width.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-onload.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/below-viewport-image-loading-lazy-load-event.html69
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/brokenimg.jpg4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/basic.html37
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/error.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/currentSrc-blob-cache.html45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/data-url.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-iframe.html54
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-image-document.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes-svg.tentative.html82
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes.html121
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-picture.html133
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-svg.tentative.html128
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach-svg.tentative.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode.html138
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-detached.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-until-move-to-empty-source.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/disconnected-image-loading-lazy.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-destroyed-crash.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/iframed.sub.html78
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html65
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/historical-progress-event.window.js16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-1.jpgbin0 -> 389245 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-base-url.html70
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change-ref.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change.html31
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-eager.html47
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-available.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url-2.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url.html51
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-below-viewport-dynamic.html45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-crossorigin-change.sub.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-different-crossorigin.html63
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-empty-src.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-001.sub.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-002.sub.html46
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-script-disabled-iframe.html23
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-far.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal-far.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-2.html57
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-3.html65
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-4.html58
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-5.html61
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested.html57
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-viewport-dynamic.html40
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-document.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-into-script-disabled-iframe.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multicol.html44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multiple-times.html60
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-negative-margin.html61
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-referrerpolicy-change.sub.html46
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-relevant-mutations.html83
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-srcset.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-subframe-detached-crash.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-to-eager.html55
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-use-list-of-available-images.html62
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-zero-intersection-area.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy.html112
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print-ref.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print.html6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image.pngbin0 -> 268 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-created-in-active-document-crash.html6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-picture-ancestor.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-src-in-synthetic-document.html16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size-ref.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size.html24
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img.complete.html200
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invalid-src.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invisible-image.html78
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-after.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-before.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-inside.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-manual.html78
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-does-not-coalesce-in-flight-requests.sub.tentative.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-matching.https.html68
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/move-element-and-scroll.html36
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/natural-size-orientation.html45
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/non-active-document.html52
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/nonexistent-image.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-below-viewport-image-loading-lazy.html63
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-dimension-getter.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-image-loading-lazy.html49
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/null-image-source.html30
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/picture-loading-lazy.html64
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations-lazy.html78
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations.html628
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/remove-element-and-scroll.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/blue-10.pngbin0 -> 76 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/cat.jpgbin0 -> 21474 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/green.pngbin0 -> 91 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-and-stash.py44
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-below-viewport.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-in-viewport.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image.pngbin0 -> 11493 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/newwindow.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/red.pngbin0 -> 510 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/referrer-checker-img.py14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print.html12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/scrolling-below-viewport-image-lazy-loading-in-iframe.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/implicit-sizes-ignores-width.html19
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-display-none.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-quirks-mode.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-standards-mode.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-width-1000px.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/reference/sizes-auto-rendering-ref.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-2.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-3.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-dynamic.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering.html26
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto.html153
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-002.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/parse-a-sizes-attribute.js29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/sizes-iframed.sub.html186
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/source-media-outside-doc.html50
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/avoid-reload-on-resize.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/common.js25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html245
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.pngbin0 -> 268 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.png.headers3
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/resized.html2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/select-an-image-source.html20
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/external-sheet.svg4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/red-bg.css2
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/relevant-mutations.js15
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet.html6
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-media.html32
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-src-complete.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/current-request-microtask.html38
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/fail-to-resolve.html25
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-source-set.html140
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-img-element/usemap-casing.html93
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/block-object-with-ruby-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/document-getters-return-null-for-cross-origin.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/historical.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-attributes.html60
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-construct-in-document-with-null-browsing-context-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-events.html81
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-fallback-failed-cross-origin-navigation.sub.html68
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-handler.html33
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-ignored-in-media-element.html22
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-display-none-load-event.html14
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-object-fallback-2.html56
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url-ref.html39
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url.html41
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-remove-param-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-setcustomvalidity.html17
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test0.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test1.html9
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html82
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm71
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/resize-during-playback.html42
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-import-to-inactive-document-crash.html7
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto.html10
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-tabindex.html18
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content-ref.htm12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_image.htm16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_text.htm16
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_crash_empty_src.html29
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster-ref.htm5
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_absolute.htm12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_relative.htm12
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused-ref.html8
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused.html21
-rw-r--r--testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_size_preserved_after_ended.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-ltr.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-only-if-applies.html80
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-auto.html31
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-inherited.html32
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-manual.html29
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/disabled-elements-01.html84
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html93
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formaction.html42
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname-iframe.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname.js12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/beforeinput.tentative.html58
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-checkValidity.html145
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-reportValidity.html145
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validate.html127
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-badInput.html46
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-customError.html48
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html53
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow-weekmonth.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html89
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow-weekmonth.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html87
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-stepMismatch.html81
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html50
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooShort.html52
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid-weekmonth.html37
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid.html110
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-weekmonth.html55
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html147
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate-datalist.html67
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html96
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/infinite_backtracking.tentative.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/input-maxlength-emoji.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/input-number-validity-dynamic-value-no-change.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/input-pattern-dynamic-value.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/inputwillvalidate.html26
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/number-input-lang-validationMessage-crash.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/radio-valueMissing.html90
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/reportValidity-crash.html37
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js481
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-email-add-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-password-add-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-search-add-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-tel-add-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-text-add-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-url-add-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/constraints/tooShort-textarea-add-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/association.window.js7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form.html91
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_attribute.html233
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table.html50
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_2.html45
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_3.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/FormDataEvent.window.js21
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/SubmitEvent.window.js41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/constructing-form-data-set.html161
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/enctypes-helper.js187
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-empty-file.window.js99
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv-form.html27
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv.html52
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-2.html49
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-3.html50
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-default-action.html108
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-multiple-targets.html37
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault-click.html67
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault.html59
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-requestsubmit.html142
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html63
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit.html50
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-echo.py7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submission-algorithm.html165
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submit-iframe-then-location-navigate.html23
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/getactionurl.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/historical.window.js19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/implicit-submission.optional.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html32
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-navigation-then-form-submit.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/multipart-formdata.window.js357
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/newline-normalization.html112
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/request-submit-activation.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/file-submission.py10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form-submission.py12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/getactionurl-iframe.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/jsurl-form-submit-iframe.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/location.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/targetted-form.js38
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/submission-checks.window.js62
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-entity-body.html114
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-file.sub.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/text-plain.window.js223
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/url-encoded.html51
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-0/urlencoded2.window.js223
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe-helper.py3
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe.html29
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-request-header.html31
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-base-target.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-button-target.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-form-target.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-input-target.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/endpoint.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/form-target-request-header-helper.py14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/reltester.js82
-rw-r--r--testing/web-platform/tests/html/semantics/forms/historical-search-event.html21
-rw-r--r--testing/web-platform/tests/html/semantics/forms/historical.html97
-rw-r--r--testing/web-platform/tests/html/semantics/forms/input-change-event-properties.html86
-rw-r--r--testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-event.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-2.html61
-rw-r--r--testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-event-realm.html38
-rw-r--r--testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form.html118
-rw-r--r--testing/web-platform/tests/html/semantics/forms/resetting-a-form/support/reset-form-event-realm.html3
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/defaultSelection.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/original-id.json1
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/select-event.html154
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-after-content-change.html144
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application-textarea.html40
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application.html112
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end-extra.html153
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end.html206
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-value-interactions.html127
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/selection.html206
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/setSelectionRange.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/textarea-selection-while-parsing.xhtml20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setRangeText.html155
-rw-r--r--testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html304
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/active-onblur.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-frame.html3
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-keyup-prevented.html38
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-checkvalidity.html44
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-click-submits.html232
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html166
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-labels.html38
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-menu-historical.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-setcustomvalidity.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-children.html58
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children-jssubmit.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children.html32
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-jssubmit.html36
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-type-enumerated-ascii-case-insensitive.html34
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-type.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-untrusted-key-event.html112
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-validation.html29
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-validationmessage.html40
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-validity.html40
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate-readonly-attribute.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate.html40
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-datalist-element/datalistoptions.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-datalist-element/remove-datalist-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html54
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/README.md12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/aria-manual.html7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/baseline-manual.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-appearance-none-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-contents-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-none-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-div-display-contents-manual.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-none-manual.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-presentation-manual.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-collapse-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-hidden-manual.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/flexbox-manual.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/grid-manual.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-abspos-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-display-none-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-visibility-hidden-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-contents-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-none-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-float-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-role-group-manual.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-collapse-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-hidden-manual.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/multiple-legends-manual.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/role-manual.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/shadow-dom-manual.html32
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/title-attribute-and-empty-legend-manual.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-001.html78
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-002.xhtml25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-003.html39
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-checkvalidity.html45
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-intrinsic-size.html74
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-setcustomvalidity.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validationmessage.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validity.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-willvalidate.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html6
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html36
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html35
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html56
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html56
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html130
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html192
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html46
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html26
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html45
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html38
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html418
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html215
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html5
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-active-contenteditable.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-contenteditable-navigate.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-dynamic.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/button.html66
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/change-to-empty-value.html31
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur-with-click.html158
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur.html52
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-being-disabled.html90
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-prevented-default.html55
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-untrusted-event.html48
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox-click-events.html109
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html149
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/checked.xhtml19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/clone.html150
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/cloning-steps.html64
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/color.html45
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/date.html90
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local-trailing-zeros.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local.html42
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-weekmonth.html51
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/datetime.html60
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/defaultValue-clobbering.html36
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/disabled-click-picker-manual.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/email-set-value.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/email.html66
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/event-select-manual.html39
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/files.html83
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change-on-blur.html61
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change.html55
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive-child.html5
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive.html30
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/hidden.html74
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/image-click-form-data.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/image01-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/image01.html7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-checkvalidity.html44
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-disabled-fieldset-dynamic.html38
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-form-detach-style-crash.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-height.html42
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-importNode-to-detached-document-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-labels.html49
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-list.html67
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html52
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-setcustomvalidity.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown-weekmonth.html22
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup-weekmonth.html22
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup.html44
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-submit-remove-jssubmit.html36
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-button.html51
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-empty-crash.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-value.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox-switch.tentative.window.js19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox.html60
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-number-rtl-invalid-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-types.js24
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-untrusted-key-event.html225
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-validationmessage.html40
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-validity.html40
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-value-invalidstateerr.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-invalidstateerr.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-stepping.html83
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate.html108
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-invalidstateerr.html39
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-stepping.html94
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber.html151
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-whitespace.html34
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-width.html42
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/input-willvalidate.html40
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/invalid-datalist-options-crash.html6
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/large-step-crash.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-manual.html37
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-number.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength.html55
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/minlength.html55
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/month.html100
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-cr.html1
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-crlf.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/number-constraint-validation.html42
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/number-disabled.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/number.html57
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/password-delete-space.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/password.html79
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/pattern_attribute.html117
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html168
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio-double-activate-pseudo.html22
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio-groupname-case.html83
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio-input-cancel.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio-keyboard-navigation-order.html70
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio-morphed.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio-multiple-selected.html21
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html351
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-2.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size-ref.html97
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size.html85
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-added-repaint.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-change-repaint.html24
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-duplicate-id-repaint.html26
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-nonexistent.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-add-repaint.html21
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-remove-repaint.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-value-change-repaint.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-restore-oninput-onchange-event.https.html52
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value-ref.html7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value.html27
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01-notref.html3
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02-ref.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03-notref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04-ref.html7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05-ref.html7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/range.html245
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/required_attribute.html34
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/reset.html113
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/resources/image-submit-click.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/resources/loadresolver.html6
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/resources/show-picker-child-iframe.html26
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/search_input.html35
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/selection-pointer.html56
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/selection-weekmonth.html57
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/selection.html140
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-cross-origin-iframe.html79
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-disabled-readonly.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-user-gesture.html31
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/telephone.html84
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/text.html104
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/time-2.html42
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/time-datalist-crash.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/time-focus-dynamic-value-change.html31
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/time.html357
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-file-to-text-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state-weekmonth.html169
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state.html166
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/url.html59
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode-weekmonth.html37
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode.html304
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-input-element/week.html41
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-interactive-content.html111
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-labelable-content.html112
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-unlabelable-content.html130
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/forward-focus-to-associated-element.html99
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/iframe-label-attributes.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.sub.html339
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/label-inside-anchor.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/labelable-elements.html174
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-click-to-associated-element.html58
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-modifier-click-to-associated-element.tentative.html67
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-legend-element/HTMLLegendElement.html37
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-legend-element/legend-form.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html3
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-meter-element/meter.html250
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-disabled-manual.html35
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-removal.window.js7
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering-ref.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering.html34
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-disabled-manual.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-element-constructor.html135
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-form.html32
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-index.html54
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-label-value.js82
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-label.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-selected.html61
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-backslash.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-label.html23
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-recurse.html92
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-setter.html24
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-spaces.html75
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-option-element/option-value.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-output-element/mutations.window.js36
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-output-element/output-setcustomvalidity.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-output-element/output-validity.html23
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-output-element/output.html46
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-progress-element/progress-2.html34
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.html74
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.window.js18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-add.html89
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-namedItem.html54
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection.html117
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/inserted-or-removed.html103
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering-ref.html21
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering.html39
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/resources/show-picker-child-iframe.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/resources/stylable-select-styles.css18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-optgroup.html29
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-option-crash.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-add.html36
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-ask-for-reset.html97
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-ref.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist.tentative.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-in-table-crash.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-multiple.html48
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-named-getter.html46
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-parsing.tentative.html112
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-remove.html64
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-selectedOptions.html143
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-setcustomvalidity.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-validity.html124
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-value.html56
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/select-willvalidate-readonly-attribute.html24
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/selected-index.html143
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-cv-hidden.html23
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-rendered.html21
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-cross-origin-iframe.tentative.html79
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-disabled.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-multiple.tentative.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-size.tentative.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-user-gesture.tentative.html24
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html6
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html119
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html51
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html45
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html244
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html231
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html39
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html57
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html182
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html142
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html29
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html16
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html35
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html140
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html23
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html94
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html44
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html67
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html49
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html16
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html495
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html135
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html115
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html102
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html61
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html61
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html63
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html88
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html224
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js112
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg3
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/change-to-empty-value.html32
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/cloning-steps.html34
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-cr.html1
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-crlf.html21
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-ref.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder.html22
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space-notref.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space.tentative.html15
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/support/placeholder.css6
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-maxlength.html51
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-minlength.html51
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi.html23
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-lineheight.html37
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-manual.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-event-manual.html31
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-manual.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-setcustomvalidity.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-crash.html24
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-simple-crash.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-textLength.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-type.html16
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-clone.html27
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-valueMissing-inside-datalist.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent-xhtml.xhtml26
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html181
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive-child.html5
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1a.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1b.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrapping-transformation.window.js58
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-dd-element/grouping-dd.html27
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-div-element/grouping-div.html28
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-dl-element/grouping-dl.html30
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-dt-element/grouping-dt.html28
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-figcaption-element/grouping-figcaption.html28
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-figure-element/grouping-figure.html29
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-hr-element/grouping-hr.html30
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-novalue-manual.html148
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html46
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html47
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html32
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html34
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003-ref.html14
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003.html18
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004-ref.html23
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004.html26
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005.html25
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-006.html29
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html31
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html40
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html44
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html46
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html58
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html52
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html64
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html52
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html58
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html46
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html52
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html40
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html45
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html42
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html52
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html46
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html58
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html24
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html30
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li.html193
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html50
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html56
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html53
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html60
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html53
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html57
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html52
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html55
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html59
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html62
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html76
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html79
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol.html299
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-1.html25
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-2.html25
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1-ref.html6
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1a.html10
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1b.html15
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1c.html16
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1d.html12
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1e.html11
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2-ref.html6
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2.html7
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-p-element/grouping-p.html28
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001.html23
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre.html28
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi.html23
-rw-r--r--testing/web-platform/tests/html/semantics/grouping-content/the-ul-element/grouping-ul.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js36
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html10
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html13
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html12
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html14
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html99
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html25
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html15
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html52
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html47
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html9
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html31
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/name-attribute.html469
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/support/empty-html-document.html2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html183
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html175
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html55
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html7
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits.html25
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html50
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html65
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html81
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html31
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html69
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html60
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html51
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html10
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html42
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html65
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html53
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html49
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html58
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html37
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html107
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html33
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html47
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html59
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html77
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-previous-outside.html82
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html53
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html274
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html58
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html48
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html21
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission-unusual.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html131
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html45
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html45
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-no-throw-requested-state.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html5
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html27
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html43
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay-re-add-during-transition.html32
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html36
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html54
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html188
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html41
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html27
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.tentative.html27
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.tentative.html27
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html229
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-previous-iframe.tentative.html52
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html73
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html85
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html53
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html36
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html33
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html55
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html75
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html68
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html131
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html101
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html42
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html21
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html101
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html42
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html59
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html14
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html32
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html68
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html20
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html25
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html68
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html52
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html102
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/pass-dialog-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html44
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css14
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html1
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpgbin0 -> 7782 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html63
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html32
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html33
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html60
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html66
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html31
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute-ref.html15
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html46
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html55
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.tentative.html56
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html134
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html77
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html9
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html104
-rw-r--r--testing/web-platform/tests/html/semantics/interfaces.html74
-rw-r--r--testing/web-platform/tests/html/semantics/interfaces.js150
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html16
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html93
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html104
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html166
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html119
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html175
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invoketarget-generic-eventtarget-crash.tentative.html18
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html285
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html218
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html209
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html253
-rw-r--r--testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js12
-rw-r--r--testing/web-platform/tests/html/semantics/links/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer-when-downgrade.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin-when-cross-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-same-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin-when-cross-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-unsafe-url.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.html19
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.js40
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer-when-downgrade.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin-when-cross-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-same-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin-when-cross-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-unsafe-url.html20
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.html19
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.js40
-rw-r--r--testing/web-platform/tests/html/semantics/links/downloading-resources/resources/inspect-header.py18
-rw-r--r--testing/web-platform/tests/html/semantics/links/following-hyperlinks/activation-behavior.window.js50
-rw-r--r--testing/web-platform/tests/html/semantics/links/following-hyperlinks/active-document.window.js23
-rw-r--r--testing/web-platform/tests/html/semantics/links/hyperlink-auditing/headers.optional.html55
-rw-r--r--testing/web-platform/tests/html/semantics/links/hyperlink-auditing/resources/stash-headers.py27
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_attribute-getter-setter.html65
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_getter.html48
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_noopener.html78
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-parsable-url-getter-setter.window.js54
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-opaque-path-url-getter-setter.window.js59
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-url-getter-setter.window.js63
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-popup.html19
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-1.html4
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-2.html8
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/target_blank_implicit_noopener.html6
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html58
-rw-r--r--testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html59
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/alternate-css-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/alternate-css.html7
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/alternate-import.css3
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/alternate.css5
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-lower.css1
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-mixed.css1
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-other.css1
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.css3
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.html14
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/original-id.json1
-rw-r--r--testing/web-platform/tests/html/semantics/links/linktypes/preferred.css3
-rw-r--r--testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.html27
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/hide-other-popover-side-effects.html21
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/invoker-show-crash.html12
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/label-in-invoker.html23
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/light-dismiss-event-ordering.html82
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display-ref.html24
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display.tentative.html51
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-none.tentative.html34
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-ref.html29
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html109
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-idl-property.tentative.html52
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-multicol-display.tentative.html62
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display-ref.html57
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display.tentative.html56
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-nesting.tentative.html56
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display-ref.html32
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html86
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-anchor-transition.tentative.tentative.html34
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-and-svg-ref.html11
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-and-svg.html18
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-appearance-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-appearance.html26
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-attribute-all-elements.html47
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html359
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance-ref.html45
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance.html47
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-beforetoggle-opening-event.html33
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-change-type.html42
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-close-request.html40
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-css-properties.tentative.html54
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance-ref.html33
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance.html27
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-dialog-crash.html26
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-document-open.html30
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-events.html216
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-focus-2.html174
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-focus-harness.html25
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-focus.html292
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-hidden-display-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-hidden-display.html39
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-hint-crash.tentative.html29
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hide.tentative.html21
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hover.tentative.html21
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-show.tentative.html21
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-toggle.tentative.html21
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none.html19
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-invoker-reset.html34
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute-hint.tentative.html19
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute.html74
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree-nested.html55
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree.html46
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-hint.tentative.html107
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-on-scroll.html66
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-with-anchor.tentative.tentative.html90
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html607
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-manual-crash.html32
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-move-documents.html89
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-not-keyboard-focusable.html49
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-open-display-ref.html20
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-open-display.html27
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-open-in-beforetoggle.html66
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-2.html69
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display.tentative.html37
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-overlay.html51
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-removal-2.html29
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-removal.html28
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-shadow-dom.html202
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-shadowhost-focus.html56
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-stacking-anchor-attribute.tentative.html104
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-stacking-context-ref.html29
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-stacking-context.html35
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-stacking.html131
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html180
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-target-element-disabled.html159
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-top-layer-combinations.html155
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-top-layer-interactions.html89
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-anchor.tentative.html45
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-hints.tentative.html46
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.tentative.html45
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-types-with-hints.tentative.html179
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-types.html37
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-undefined-remove-crash.html12
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popovertarget-reflection.html53
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/resources/popover-hover-hide-common.js139
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/resources/popover-invoking-attribute.js122
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/resources/popover-styles.css17
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/resources/popover-top-layer-nesting.js108
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/resources/popover-utils.js176
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/togglePopover.html79
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/toggleevent-interface.html208
-rw-r--r--testing/web-platform/tests/html/semantics/rellist-feature-detection.html84
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-noscript-element/non-html-noscript.html16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/README.md16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_001.htm18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_002.htm31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_003.htm40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_004.htm38
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_005.htm43
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_006.htm46
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_007.htm49
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_008.htm47
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_009.htm25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_010.htm55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_011.htm19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-2.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-bom.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/content-type-checking.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/cors-crossorigin-requests.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/credentials.sub.html55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/css-module-worker-test.html54
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-basic.html83
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-dynamic.html23
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/integrity.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/load-error-events.html67
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/referrer-policies.sub.html84
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/relative-urls.html18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/atImported.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bad-import.css4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic-large.css7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16be.cssbin0 -> 64 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16le.cssbin0 -> 64 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-8.css1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/credentials-iframe.sub.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/cross-origin.py17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-parse-error-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-without-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/css-module-without-assertion-iframe.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-matches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-mismatches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-error-events.py14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-relative-url.css5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/malformed.css7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/parse-error.css2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/record-fetch.py20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/utf-8.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/windows-1250.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker-dynamic-import.sub.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker.sub.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/script-element-css-src.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-2.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-bom.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/content-type-checking.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/cors-crossorigin-requests.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/credentials.sub.html55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/css-module-worker-test.html54
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-basic.html83
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-dynamic.html23
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/load-error-events.html67
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/referrer-policies.sub.html84
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/relative-urls.html18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/atImported.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bad-import.css4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic-large.css7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16be.cssbin0 -> 64 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16le.cssbin0 -> 64 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-8.css1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/credentials-iframe.sub.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/cross-origin.py17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-parse-error-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-without-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/css-module-without-attribute-iframe.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-matches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-mismatches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-error-events.py14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-relative-url.css5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/malformed.css7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/parse-error.css2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/record-fetch.py20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/utf-8.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/windows-1250.css3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker-dynamic-import.sub.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker.sub.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/script-element-css-src.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/data-url.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/README.md7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script-2.html40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml.xhtml40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/document-write.html67
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/async-script-1.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-1.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-2.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write-close.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-close.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-iframe.sub.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/helper.js17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-1.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-2.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/support/async-script.html44
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/emptyish-script-elements.html75
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/001.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/002.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/003.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/004.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/005.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/006.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/007.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/008.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/009.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/010.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/011.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/012.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/013.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/014.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015a.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/016.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/017.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/018.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/019.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/020.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/021.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/022.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/023.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/024.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/025.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/026.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/027.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/028.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/030.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/031.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/032.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/033.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/034.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/035.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/036.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/037.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/038.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/039.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/040.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/041.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/042.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/043.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/044.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/045.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/046.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/047.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/048.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/049.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/050.html50
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/051.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/052.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/053.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/054.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/055.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/056.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/057.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/058.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/059.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/060.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/061.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/062.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/063.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/064.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/065.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/066.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/067.html38
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/068.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/069.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/070.html48
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/071.html55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/072.html50
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/073.html52
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/074.html49
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/075.html42
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/076.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/077.html41
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/078.html44
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/079.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/081.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/082.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/083.html48
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/084.html47
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/085.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/086.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/087.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/088.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/089.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/090.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/091.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/092.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/094.html23
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/095.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/096.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/097.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/099.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/101.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/102.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/103.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/104.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/105.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-import.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-noimport.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-import.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-import.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-noimport.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-noimport.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import-xhtml.xhtml20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-import.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-noimport.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport-xhtml.xhtml20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-import.html21
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-noimport.html21
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/108.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/109.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/110.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/111.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/112.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/113.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/114.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/115.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/116.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/117.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/118.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/119.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/120.html17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/121.html17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/122.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/123.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/124.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/125.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/126.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/127.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/128.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/129.html40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/130.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/131.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/132.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/133.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/134.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/135.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/136.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/137.html21
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/138.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/139.html30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/140.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/141.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/142.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/143.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/144.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/145.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146-href.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146.html23
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/147.html40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/148.html40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/149.html59
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import-xhtml.xhtml21
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport-xhtml.xhtml20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/background.css1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/import.css1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/non-external-no-import.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld-postMessage.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/check-style-sheet.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/count-script-tags.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-body.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-foo.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-10.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-11.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-5.js7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-6.js6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-7.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-8.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-9.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/testlib/testlib.js43
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-utf8.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-windows1250.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/base.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/test.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/beta/test.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty-with-base.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/failure.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/unreachable.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/historical.html53
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/dynamic-import-with-assertion-argument.any.js18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-type-assertion.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/export-hello.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/hello.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-import-errors-order.html36
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion-error.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/javascript-type-assertion.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/js-type-assertion.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/dynamic-import-with-attributes-argument.any.js18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-type-attribute.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/export-hello.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/hello.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-import-errors-order.html36
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute-error.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/javascript-type-attribute.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/js-type-attribute.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-module-goal.mjs1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-script-goal.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/array.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16be.jsonbin0 -> 40 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16le.jsonbin0 -> 40 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-8.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-2.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-bom.any.js17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cors-crossorigin-requests.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials-iframe.sub.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials.sub.html55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cross-origin.py16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-parse-error-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-without-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/data.json3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/false.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-matches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-mismatches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/invalid-content-type.any.js17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/json-module-service-worker-test.https.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.html67
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.py14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.html18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.json3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/non-object.any.js14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/null.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.html21
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-policies.sub.html84
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/repeated-imports.any.js65
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/script-element-json-src.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker-dynamic-import.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/string.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/true.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/utf-8.json4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/valid-content-type.html46
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/windows-1250.json4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/array.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16be.jsonbin0 -> 40 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16le.jsonbin0 -> 40 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-8.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-2.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-bom.any.js17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cors-crossorigin-requests.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials-iframe.sub.html33
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials.sub.html55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cross-origin.py16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-parse-error-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-with-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-without-cors.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/data.json3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/false.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-matches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-mismatches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.any.js17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.html67
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.py14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.html18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.json3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/non-object.any.js14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/null.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.html21
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-policies.sub.html84
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/repeated-imports.any.js65
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/script-element-json-src.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker-dynamic-import.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/string.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/true.json1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/utf-8.json4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html46
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/windows-1250.json4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-1.html68
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-2.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-3.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/log.py13
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror-module.html9
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror.html9
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror.html13
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-importScripts.any.js40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-importScripts.any.js11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-static-import.any.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-importScripts.any.js22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-static-import.any.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.any.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.html8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.any.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.html8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-4.html18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker-module.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event.js89
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow-setup.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw-setup.js10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2-setup.js10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.1.mjs8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.2.mjs7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3-setup.js7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.1.mjs11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.2.mjs5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.1.mjs8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.2.mjs5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-setup.js30
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/resolve-then-throw.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/bad-module-specifier.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-01.html52
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-02.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-03.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1b.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2b.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3.html38
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3b.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-1.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-2.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/credentials.sub.html68
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-common.js8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-different.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-missingheader.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-same.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-wrongheader.sub.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-different.sub.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-missingheader.sub.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-same.sub.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-wrongheader.sub.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin.html43
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentScript-null.html13
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentscript.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/custom-element-exception.html31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access-a.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable-a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-1.html23
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-2.html23
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker.sub.html8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/code-cache.js9
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/import.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/worker-importScripts.sub.js15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/code-cache.js9
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/import.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/redirect.py19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url-workers.window.js60
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js66
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-base-url.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-nonce.html43
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/delay-load-event.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials-setTimeout.sub.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-fetch-error.sub.html61
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-script-error.html53
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js58
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/code-cache.js9
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/inline-event-handler.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/basic.any.js32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/css-import-in-worker.any.js14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.css4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/serviceworker.any.js14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/ticker.js13
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/with-import-assertions.any.js15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet-ref.https.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet.https.html43
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-classic-manual.html55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-module-manual.html55
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-classic.html5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external.js7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-classic.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/blob-url-worker.js11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce-iframe.sub.html4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce.js4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/empty-iframe.html9
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/status-changing-script.py18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache-iframe.sub.html9
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache.js74
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/Function.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/eval.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/inline-event-handlers-UA-code.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/no-active-script.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/reflected-inline-event-handlers.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/setTimeout.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html63
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html64
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html73
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html73
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html54
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html54
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html104
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html103
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html83
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-other-document.html79
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/v8-code-cache.html42
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-and-slow-dependency.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-common.js10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.html16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-root.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype-import.js8
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype.js7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling.html60
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-1.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-2.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-3.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-4.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered2.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered3.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered4.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered1.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered2.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered2.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered4.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered1.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered2.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder.html106
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-default.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something-nested.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-1.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-dependent.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-object.any.js20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-importmap.html57
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-multiple-scripts.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve.any.js77
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-root.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.any.js38
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.html34
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/postmessage-worker.js12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-1.mjs1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-2.mjs1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-import-meta.mjs2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/store-import-meta.html5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-something-namespace.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-subgraph-404.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-a.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-b.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-b.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle.js6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-ab.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-b.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self-inner.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self.js6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports.html64
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inactive-context-import.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-execorder.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-onload.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-defer-onload.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-no-async-onload.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4b.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4c.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4d.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5b.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5c.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5d.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5e.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6.html36
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6b.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6c.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6d.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7.html37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7a.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7b.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7c.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7d.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7e.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7f.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-8.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches-inner.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches-inner.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity.html40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-namespace-request.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-star-export-request.html25
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events-inline.html61
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events.html61
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export-nested.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.html48
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-in-xhtml.xhtml20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-1.html17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-2.html17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-a.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-b.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-c.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-d.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-e.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-f.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-g.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-h.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports.html29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-missing-export.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nomodule-attribute.html18
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-no-referrer.sub.html68
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html82
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin.sub.html71
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-policy-for-descendants.sub.html122
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-same-origin.sub.html77
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-strict-policies.sub.html38
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-unsafe-url.sub.html81
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/404-but-js.asis4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/500-but-js.asis4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/credentials-iframe.sub.html50
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/delayed-modulescript.py7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-helper.sub.js67
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-iframe.sub.html51
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-setTimeout-iframe.sub.html56
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/fast-module.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8-with-charset-header.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-remote-origin-referrer-checker.sub.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js.headers1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8-with-charset-header.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-404-but-js.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-500-but-js.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-b-cross-origin.sub.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/slow-module.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/script-for-event.html93
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/set-currentScript-on-window.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-1.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-2.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-cycle.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-a.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-b.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/specifier-error.html22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror-nested.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this-nested.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-error.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-nested.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw2.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked.any.js11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-get-sync.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-set-async.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__parent.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-get-sync.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-set-async.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__parent.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/type.html24
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents-during-evaluation.html59
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/README.md16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-module.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-inline-classic.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/README.md6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-1.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-2.html15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-iframe.html4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/helper.js31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/in-order.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/parser-blocking.html41
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-helper.js214
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-iframe.py102
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/slow-flag-setter.py29
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/tools/generate.py61
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors.sub.html85
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-reflect.html75
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-async-classic-script.html63
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-external-module-script.html28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-classic-scripts.html56
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-module-script.html32
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-synchronously-loaded-classic-scripts.html41
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/promise-reject-and-remove.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16be.jsbin0 -> 156 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16le.jsbin0 -> 156 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-8.js2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cocoa-module.js5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/exports-cocoa.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/flag-setter.js3
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events-helpers.js47
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events.py15
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/promise-reject-and-remove-iframe.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/script-type-and-language-js.js141
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/set-script-executed.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/syntax-error.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/throw.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-01.html89
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-02.html41
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-03.html20
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.sub.html120
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html39
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer-xhtml.xhtml31
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event-xhtml.xhtml22
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event.html93
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-noembed-noframes-iframe.xhtml36
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown-child.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed-2.py4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.py4
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html13
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-string.html17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-referrerpolicy-idl.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-supports.html52
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications-csp.html52
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications.html40
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-xhtml.xhtml28
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text.html72
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-empty.html48
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-svg.svg37
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-xhtml.xhtml42
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js.html35
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-with-params.html41
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-whitespace.html27
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/scripting-enabled.html16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-json-then-js.py21
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-with-content-type.py17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html2
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js1
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/node-document.html150
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/tag-name.xhtml16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/template-child-nodes.html102
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-serializing-xhtml-documents/outerhtml.html71
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001-ref.html6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-002.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-003.html19
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/template-clone-children.html82
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/templates-copy-document-owner.html126
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html83
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-001.html44
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-002.html67
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents.html172
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/innerhtml-on-templates/innerhtml.html105
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-body.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-head.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/frameset-end-tag.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-div-no-end-tag.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-table-no-end-tag.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/html-start-tag.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-div.xhtml14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-nested.xhtml16
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-attribute.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-body.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-div-no-end-tag.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-empty.html11
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-frameset.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-head.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-html.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-nested.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-table-no-end-tag.html14
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-text.html10
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-body.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-frameset.html12
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-head.html13
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/two-templates.html17
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/serializing-html-templates/outerhtml.html70
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/content-attribute.html114
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/node-document-changes.html192
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-as-a-descendant.html135
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-construction-in-inactive-document-crash.html5
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-hierarcy.html81
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-in-inactive-document-crash.html7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-move-to-inactive-document-crash.html7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-node-document.html59
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content.html63
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-body.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-frameset.html62
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-head.html26
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-element-clone-into-inactive-document-crash.html7
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-set-inner-html-in-inactive-document-crash.html6
-rw-r--r--testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-table-crash.html13
-rw-r--r--testing/web-platform/tests/html/semantics/sections/the-h1-h2-h3-h4-h5-and-h6-elements/original-id.json1
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js91
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html54
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html11
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html18
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js27
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html24
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html44
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html64
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html47
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html21
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html99
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html18
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html80
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html43
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html24
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html5
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html51
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html21
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html28
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html26
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html24
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html37
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js75
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html43
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html84
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html28
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html21
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html27
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html36
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html118
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html36
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html35
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js20
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html57
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html146
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/attributes-common-to-td-and-th-elements/cellIndex.html50
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/historical.html25
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/html-table-section-element.js22
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/col-span-limits.html59
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits.html66
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-caption-element/caption_001.html70
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/caption-methods.html276
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/createTBody.html173
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/delete-caption.html94
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-01.html24
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-02.html34
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-03.html32
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/remove-row.html64
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tBodies.html40
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tFoot.html65
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tHead.html74
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-insertRow.html56
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-rows.html234
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/deleteRow.html61
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/insertRow.html56
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/rows.html15
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tfoot-element/rows.html15
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-thead-element/rows.html15
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/cells.html28
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/deleteCell.html61
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/insertCell.html64
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/rowIndex.html77
-rw-r--r--testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/sectionRowIndex.html130
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/historical.html29
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-click-handler-with-null-browsing-context-crash.html21
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-404.py2
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-404.html25
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-redirect-to-javascript.html29
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click.html33
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-stringifier.html16
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-getter-01.html34
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-setter-01.html41
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-404.html2
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-click.html2
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-redirect-to-javascript.html5
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-onclick-handler-iframe.html1
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage-notref.html6
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage.html8
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default-ref.html36
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default.html46
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf-ref.html44
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf.html56
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested-ref.html44
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested.html52
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number-ref.html44
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number.html53
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate-ref.html36
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate.html47
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1-ref.html47
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1.html58
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2-ref.html47
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2.html59
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1-ref.html45
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1.html54
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2-ref.html45
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2.html54
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1-ref.html45
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1.html54
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2-ref.html45
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2.html54
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1-ref.html45
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1.html54
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2-ref.html45
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2.html53
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run-ref.html44
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run.html56
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped-ref.html52
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped.html73
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container-ref.html36
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container.html46
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-child.html17
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-ltr.html15
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-override.html18
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001-ref.html11
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001.html14
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors-ref.html38
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors.html59
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi.html22
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/rt-without-ruby-crash.html11
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage-notref.html6
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage.html8
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-time-element/001.html68
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element-ref.html10
-rw-r--r--testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element.html12
-rw-r--r--testing/web-platform/tests/html/semantics/the-link-element/attr-link-fetchpriority.html26
-rw-r--r--testing/web-platform/tests/html/semantics/the-link-element/resources/stylesheet.css3
-rw-r--r--testing/web-platform/tests/html/syntax/charset/README.md6
-rw-r--r--testing/web-platform/tests/html/syntax/charset/after-1kb.html955
-rw-r--r--testing/web-platform/tests/html/syntax/charset/after-bogus-after-1kb.html933
-rw-r--r--testing/web-platform/tests/html/syntax/charset/after-bogus.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/after-head-after-1kb-crlf.html927
-rw-r--r--testing/web-platform/tests/html/syntax/charset/after-head-after-1kb.html933
-rw-r--r--testing/web-platform/tests/html/syntax/charset/after-head-in-1kb-crlf.html932
-rw-r--r--testing/web-platform/tests/html/syntax/charset/after-head-in-1kb.html938
-rw-r--r--testing/web-platform/tests/html/syntax/charset/baseline.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/document-write.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-comment.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-noscript-after-template-after-1kb.html894
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-object.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-script.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-style.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-svg-in-cdata.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-svg.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-template-after-1kb.html1046
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-template.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/in-title.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta-utf-8.html35
-rw-r--r--testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta.html46
-rw-r--r--testing/web-platform/tests/html/syntax/charset/ncr.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/non-ascii-in-comment-before.html11
-rw-r--r--testing/web-platform/tests/html/syntax/charset/non-ascii-in-title-before.html11
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/after-1kb-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/after-bogus-after-1kb-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/after-bogus-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-crlf-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-crlf-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/baseline-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/document-write-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-comment-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-noscript-after-template-after-1kb-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-object-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-script-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-style-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-svg-in-cdata-ref.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-svg-ref.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-template-after-1kb-ref.html8
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-template-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/in-title-ref.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/ncr-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-comment-before-ref.html9
-rw-r--r--testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-title-before-ref.html10
-rw-r--r--testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http-valid-meta.py4
-rw-r--r--testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http.py4
-rw-r--r--testing/web-platform/tests/html/syntax/charset/resources/bogus-charset.html7
-rw-r--r--testing/web-platform/tests/html/syntax/charset/with-inheritance.html60
-rw-r--r--testing/web-platform/tests/html/syntax/charset/without-inheritance.htmlbin0 -> 4550 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/charset/xhr.html73
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/innerHTML-setter-default-namespace.xhtml35
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-1.css4
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15-inverse.css4
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15.css4
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-utf8.css4
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html37
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-003.htmlbin0 -> 2632 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-004.htmlbin0 -> 2624 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html37
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html37
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html38
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html38
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html38
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html39
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html37
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html38
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing-html-fragments/tokenizer-modes-001.html85
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/DOMContentLoaded-defer.html17
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-01.html143
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-02.html24
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-01.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-02.html29
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/README8
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/adoption_agency_check_the_end_tag_name.html20
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/ambiguous-ampersand.html36
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/common.js24
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/empty-doctype-ids.html10
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_001.html43
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_003.html47
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_004.html65
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_005.html45
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_006.html38
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_008.html39
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_009.html46
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_010.html44
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_011.html26
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/foreign_content_013.html26
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html-integration-point.html31
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_adoption01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_adoption02.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_blocks.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_comments01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_doctype01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_domjs-unsafe.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_entities01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_entities02.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_html5test-com.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_inbody01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_adoption01.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_foreign-fragment.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_math.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_svg.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_template.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests4.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests6.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests7.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests_innerHTML_1.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_webkit02.html25
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_isindex.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_main-element.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_menuitem-element.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_namespace-sensitivity.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes-plain-text-unsafe.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_plain-text-unsafe.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_quirks01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_ruby.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_scriptdata01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_adoption01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_ark.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_webkit01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_search-element.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tables01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_template.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests1.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests10.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests11.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests12.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests14.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests15.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests16.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests17.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests18.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests19.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests2.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests20.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests21.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests22.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests23.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests24.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests25.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests26.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests3.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests5.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests6.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests7.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests8.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tests9.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_tricky01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_webkit01.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html5lib_webkit02.html28
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/html_content_in_foreign_context.html29
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/inhead-noscript-head.html17
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/math-parse01.html62
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/math-parse03.html132
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/meta-inhead-insertion-mode.html15
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/named-character-references-data.js2233
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/named-character-references.html36
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/no-doctype-name.html22
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/quotes-in-meta.html12
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/support/DOMContentLoaded-defer.js14
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-eof.html1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-line.html2
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-space.html1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/support/svg-script-self-closing.js1
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template.js214
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-a-foster-parent-element.html63
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-not-a-foster-parent-element.html70
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/generating-of-implied-end-tags.html136
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-body-token.html132
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-frameset-token.html125
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-head-token.html129
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-html-token.html158
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-body.html97
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-html.html38
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/template-end-tag-without-start-one.html102
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-frameset-insertion-mode/end-tag-frameset.html26
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/generating-of-implied-end-tags.html137
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/template-end-tag-without-start-one.html101
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-table-insertion-mode/end-tag-table.html42
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/appending-to-a-template/template-child-nodes.html116
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-body-context.html183
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-context.html85
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-row-context.html72
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html222
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/test.js350
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/the-end.html59
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/unclosed-svg-script.html45
-rw-r--r--testing/web-platform/tests/html/syntax/parsing/zero.html93
-rw-r--r--testing/web-platform/tests/html/syntax/serializing-html-fragments/escaping.html89
-rw-r--r--testing/web-platform/tests/html/syntax/serializing-html-fragments/initial-linefeed-pre.html48
-rw-r--r--testing/web-platform/tests/html/syntax/serializing-html-fragments/outerHTML.html32
-rw-r--r--testing/web-platform/tests/html/syntax/serializing-html-fragments/serializing.html336
-rw-r--r--testing/web-platform/tests/html/syntax/serializing-html-fragments/template.html23
-rw-r--r--testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html39
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-charset/speculative-script.tentative.html27
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-charset/support/script.py18
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-charset/support/speculative-script.py11
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/base-href-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/image-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-data-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-crossorigin.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-loading-lazy.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-referrerpolicy-no-referrer.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-srcset.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-no-rel.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-alternate-stylesheet.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-font-crossorigin.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-image.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-script.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-style.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-crossorigin.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-integrity.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-nomatch-media.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-type-text-css.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-unsupported-type.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-face-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-charset-script-src.tentative.sub.html35
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-asterisk.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-none.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-referrer-no-referrer-img-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-1.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-2.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-br-img.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-no-img.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-nomatch-media.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-unsupported-type.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-async.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-crossorigin.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-defer.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-integrity.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-module.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-nomodule.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-referrerpolicy-no-referrer.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-type-application-ecmascript.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-unsupported-type.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-href.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-xlinkhref.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-href.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-xlinkhref.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-img-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-link-stylesheet.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-img-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-link-stylesheet.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/video-poster.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/xmp-script-src.tentative.sub.html25
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/base-href-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/image-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-data-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-crossorigin.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-loading-lazy.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-referrerpolicy-no-referrer.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-srcset.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-no-rel.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-alternate-stylesheet.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-font-crossorigin.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-image.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-script.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-style.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-crossorigin.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-disabled.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-integrity.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-nomatch-media.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-type-text-css.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-unsupported-type.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-face-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-charset-script-src.tentative.html31
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-asterisk.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-none.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-referrer-no-referrer-img-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-viewport-link-stylesheet-media.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-1.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-2.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-br-img.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-no-img.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-nomatch-media.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-unsupported-type.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/base-href-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/image-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-data-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-crossorigin-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-loading-lazy-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-referrerpolicy-no-referrer-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-srcset-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-no-rel-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-alternate-stylesheet-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-font-crossorigin-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-image-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-script-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-style-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-crossorigin-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-disabled-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-integrity-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-nomatch-media-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-referrerpolicy-no-referrer-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-type-text-css-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-unsupported-type-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-face-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-charset-script-src-framed.sub.html22
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-asterisk-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-none-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-referrer-no-referrer-img-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-viewport-link-stylesheet-media-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-1-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-2-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-br-img-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-no-img-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-nomatch-media-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-unsupported-type-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-async-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-crossorigin-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-defer-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-integrity-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-module-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-nomodule-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-referrerpolicy-no-referrer-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-type-application-ecmascript-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-unsupported-type-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-href-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-xlinkhref-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-href-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-xlinkhref-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-img-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-link-stylesheet-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-img-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-link-stylesheet-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/video-poster-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/xmp-script-src-framed.sub.html12
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-async.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-crossorigin.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-defer.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-integrity.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-module.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-nomodule.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-referrerpolicy-no-referrer.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-type-application-ecmascript.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-unsupported-type.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-href.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-xlinkhref.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-href.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-xlinkhref.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-img-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-link-stylesheet.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-img-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-link-stylesheet.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/video-poster.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/xmp-script-src.tentative.html21
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/base-href-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/image-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-data-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-crossorigin-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-referrerpolicy-no-referrer-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-srcset-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-no-rel-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-alternate-stylesheet-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-font-crossorigin-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-image-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-script-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-style-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-crossorigin-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-disabled-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-integrity-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nomatch-media-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-referrerpolicy-no-referrer-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-type-text-css-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-unsupported-type-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-face-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-charset-script-src-nonspeculative.sub.html20
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-asterisk-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-none-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-referrer-no-referrer-img-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-viewport-link-stylesheet-media-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-1-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-2-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-br-img-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-no-img-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-nomatch-media-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-unsupported-type-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-async-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-crossorigin-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-defer-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-integrity-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-module-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nomodule-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-referrerpolicy-no-referrer-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-type-application-ecmascript-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-unsupported-type-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-href-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-xlinkhref-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-href-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-xlinkhref-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-img-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-link-stylesheet-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-img-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-link-stylesheet-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/video-poster-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/xmp-script-src-nonspeculative.sub.html10
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/resources/speculative-parsing-util.js58
-rw-r--r--testing/web-platform/tests/html/syntax/speculative-parsing/resources/stash.py12
-rwxr-xr-xtesting/web-platform/tests/html/syntax/speculative-parsing/tools/generate.py701
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING-trail.htmbin0 -> 8350 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/VERSION-trail.htmbin0 -> 8348 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/VERSION.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS-trail.htmbin0 -> 8349 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/XML-trail.htmbin0 -> 8331 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/XML.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/ascii-decl-for-utf-16.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/baseline-trail.htmbin0 -> 8318 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/baseline.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/cp1251-trail.htmbin0 -> 8335 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/cp1251.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding-trail.htmbin0 -> 8343 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding-trail.htmbin0 -> 8341 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding-trail.htmbin0 -> 8374 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta-trail.htmbin0 -> 8302 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta.htmbin0 -> 110 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta-trail.htmbin0 -> 8304 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta.htmbin0 -> 112 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after-trail.htmbin0 -> 9362 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before-trail.htmbin0 -> 9363 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after-trail.htmbin0 -> 9363 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before-trail.htmbin0 -> 9364 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding-trail.htmbin0 -> 8378 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding-trail.htmbin0 -> 8374 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding-trail.htmbin0 -> 8449 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset-trail.htmbin0 -> 8448 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space-trail.htmbin0 -> 8383 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-trail.htmbin0 -> 8387 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trail.htmbin0 -> 8346 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals-trail.htmbin0 -> 8450 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals.htm6
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals-trail.htmbin0 -> 8447 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-trail.htmbin0 -> 8415 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-trail.htmbin0 -> 8395 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt-trail.htmbin0 -> 8391 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-trail.htmbin0 -> 8321 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version-trail.htmbin0 -> 8313 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/no-version.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals-trail.htmbin0 -> 8345 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label-trail.htmbin0 -> 8344 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/replacement-trail.htmbin0 -> 8375 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/replacement.htm3
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label-trail.htmbin0 -> 8346 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/space-before-trail.htmbin0 -> 8337 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/space-before.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/test_support.js26
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/trickle.py12
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes-trail.htmbin0 -> 8377 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes.htm4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta-trail.htmbin0 -> 8304 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta.htmbin0 -> 112 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.htmlbin0 -> 8278 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.htmlbin0 -> 86 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta-trail.htmbin0 -> 8306 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta.htmbin0 -> 114 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.htmlbin0 -> 8277 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.htmlbin0 -> 85 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta-trail.htmbin0 -> 8397 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta.htm5
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.htmlbin0 -> 8318 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html4
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html.headers1
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals-trail.htmbin0 -> 8345 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals.htmbin0 -> 153 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label-trail.htmbin0 -> 8344 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label.htmbin0 -> 152 bytes
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/xmldecl-1.html59
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/xmldecl-2.html78
-rw-r--r--testing/web-platform/tests/html/syntax/xmldecl/xmldecl-3.html21
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001-ref.html9
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html19
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing.xhtml11
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd-ref.html4
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd.html27
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/entities.json2233
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/xhtml-mathml-dtd-entity.htm49
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-1.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-10.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-2.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-3.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-4.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-5.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-6.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-7.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-8.htm13
-rw-r--r--testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-9.htm13
-rw-r--r--testing/web-platform/tests/html/tools/README1
-rw-r--r--testing/web-platform/tests/html/tools/html5lib_python_revision1
-rw-r--r--testing/web-platform/tests/html/tools/html5lib_test.xml29
-rw-r--r--testing/web-platform/tests/html/tools/html5lib_test_fragment.xml28
-rw-r--r--testing/web-platform/tests/html/tools/html5lib_tests_revision1
-rw-r--r--testing/web-platform/tests/html/tools/update_html5lib_tests.py201
-rw-r--r--testing/web-platform/tests/html/user-activation/META.yml3
-rw-r--r--testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-enter.html47
-rw-r--r--testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-escape.html41
-rw-r--r--testing/web-platform/tests/html/user-activation/activation-trigger-mouse-left.html45
-rw-r--r--testing/web-platform/tests/html/user-activation/activation-trigger-mouse-right.html60
-rw-r--r--testing/web-platform/tests/html/user-activation/activation-trigger-pointerevent.html64
-rw-r--r--testing/web-platform/tests/html/user-activation/chained-setTimeout.html63
-rw-r--r--testing/web-platform/tests/html/user-activation/detached-iframe.html47
-rw-r--r--testing/web-platform/tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html55
-rw-r--r--testing/web-platform/tests/html/user-activation/message-event-init.tentative.html19
-rw-r--r--testing/web-platform/tests/html/user-activation/navigation-state-reset-crossorigin.sub.html53
-rw-r--r--testing/web-platform/tests/html/user-activation/navigation-state-reset-sameorigin.html62
-rw-r--r--testing/web-platform/tests/html/user-activation/no-activation-thru-escape-key.html65
-rw-r--r--testing/web-platform/tests/html/user-activation/propagation-crossorigin.sub.html121
-rw-r--r--testing/web-platform/tests/html/user-activation/propagation-same-and-cross-origin.sub.html151
-rw-r--r--testing/web-platform/tests/html/user-activation/propagation-sameorigin.html120
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/child-message-event-api.html24
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/child-one.html29
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/child-two.html29
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/consumption-crossorigin-child.sub.html29
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/consumption-sameorigin-child.html29
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/propagation-crossorigin-child.sub.html27
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/propagation-sameorigin-child.html27
-rw-r--r--testing/web-platform/tests/html/user-activation/resources/utils.js48
-rw-r--r--testing/web-platform/tests/html/user-activation/user-activation-interface.html32
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/callback-cross-realm-report-exception.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/callback-exception.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/callback-handle.html16
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/callback-invoked.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/callback-multicalls.html26
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/callback-timestamp.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/cancel-handle-manual.html51
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/cancel-invoked.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/cancel-pending.html26
-rw-r--r--testing/web-platform/tests/html/webappapis/animation-frames/same-dispatch-time.html31
-rw-r--r--testing/web-platform/tests/html/webappapis/atob/base64.any.js163
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document-close-with-pending-script.html67
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document.close-01.xhtml19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/load-event-after-location-set-during-write.window.js19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/001.html12
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/002.html13
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/003.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/004.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.js4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008-1.js3
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.js4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/009.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010-1.js4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.js4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011-1.js5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.js5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.js5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/014.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/015.html16
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/016.html16
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/017.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/018.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/019.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/020.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/021.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/022.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/023.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/024.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/025.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/026.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/027.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/028.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/029.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/030.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/031.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/032.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/033.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/034.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/035.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/036.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/037.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/038.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/039.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/040.html10
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/041.html13
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/042.html16
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/043.html16
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/044.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/045.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/046.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047-1.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/049.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/050.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/051.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/contentType.window.js28
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-01.xhtml19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-02.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/during-readystatechange.window.js24
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/empty.html1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_001.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_002.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_003.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_004.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.js3
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_006.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_007.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_008.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_009.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_010.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed-iframe.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import-iframe.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.mjs4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-iframe.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed-iframe.html5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html28
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.mjs5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-iframe.html5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.html26
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.mjs4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed-iframe.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise-iframe.html10
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import-iframe.html5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.mjs4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise-iframe.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html24
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-1.html2
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-2.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-external.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/original-id.json1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_001.html10
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_002.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_003.html10
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_004.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_005.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_006.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_007.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_008.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_009.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_010.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_011.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_012.html22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_013.html24
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/write-active-document.html35
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-01.xhtml19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-02.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-03.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/original-id.json1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.html35
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.html54
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.html10
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.html42
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.html77
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html10
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html6
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js3
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js36
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-CEReactions.html71
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-xml.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.html55
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/002.html12
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/004.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/006.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011-1.html5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012-1.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013-1.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014-1.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015-1.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016-1.html41
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-immediate.window.js119
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-header.window.js69
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-meta.window.js69
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-while-navigating.window.js179
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort.sub.window.js104
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js31
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/active.window.js98
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-origin.sub.window.js117
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-xml.window.js26
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window.js18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-same-origin-domain.sub.window.js14
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-synchronous-script.window.js19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-xml.window.js20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/beforeunload.window.js18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/crbug-583445-regression.window.js127
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/custom-element.window.js39
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document-open-cancels-javascript-url-navigation.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-01.xhtml19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-02.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03-frame.html10
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/encoding.window.js12
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/event-listeners.window.js308
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/form-control-state.html61
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history-state.window.js29
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history.window.js29
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/ignore-opens-during-unload.window.js60
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html31
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-events.window.js22
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-observer.window.js19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/no-new-global.window.js57
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-basic.html26
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-same-origin-domain.sub.html24
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/quirks.window.js74
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/readiness.window.js25
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js71
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/remove-initial-about-blankness.window.js65
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-custom-element-with-domain-frame.sub.html13
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-frame.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-with-domain-frame.sub.html5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-domain-frame.sub.xhtml11
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-synchronous-script-frame.xhtml11
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/document-open-side-effects.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/dummy.html2
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/encoding-frame.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/global-variables-frame.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/history-frame.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/http-refresh.py5
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/meta-refresh.py3
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/page-with-frame.html1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/set-document-domain.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/slow-png.py8
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-incumbent-frame.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-timer-frame.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-frame.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js106
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt1
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js23
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js20
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/unload.window.js19
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document-sync-call.window.js13
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document.window.js18
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-fragment.window.js26
-rw-r--r--testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window.js93
-rw-r--r--testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html28
-rw-r--r--testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js15
-rw-r--r--testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.any.js39
-rw-r--r--testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.window.js45
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js29
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html57
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html55
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html64
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html85
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html48
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html167
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html69
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html71
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm21
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html88
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html71
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html24
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html57
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html60
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html64
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html67
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html78
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js22
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html48
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html57
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html63
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js40
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js49
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js22
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html62
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html60
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js76
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html40
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js55
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html76
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html53
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js16
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js14
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html114
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html56
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html12
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js61
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html32
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html37
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html28
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers2
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js9
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js5
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html112
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html101
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html164
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md5
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js7
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js7
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html96
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html50
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html44
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html31
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html146
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html47
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html8
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html12
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html70
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py18
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js961
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html40
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html43
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html43
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js49
-rw-r--r--testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js45
-rw-r--r--testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js169
-rw-r--r--testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests.js753
-rw-r--r--testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-cross-realm-method.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-detached-window-crash.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/structured-clone/structured-clone.any.js14
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html16
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js16
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html28
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html41
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js106
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html53
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html8
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html13
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js4
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html83
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html218
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js3
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js53
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/README.md1
-rw-r--r--testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/Worker_Self_Origin.html45
-rw-r--r--testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginSharedWorker.js5
-rw-r--r--testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginWorker.js4
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/clearinterval-from-callback.any.js19
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/cleartimeout-clearinterval.any.js29
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/evil-spec-example.any.js12
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/missing-timeout-setinterval.any.js34
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/negative-setinterval.any.js12
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/negative-settimeout.any.js3
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html32
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html28
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/type-long-setinterval.any.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/timers/type-long-settimeout.any.js3
-rw-r--r--testing/web-platform/tests/html/webappapis/update-rendering/child-document-raf-order.html118
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/confirm-different-origin-frame.sub.html24
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/prompt-different-origin-frame.sub.html24
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/confirm.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/prompt.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/newline-normalization-manual.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/print-during-beforeunload.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/print-during-unload.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/print-in-detached-frame.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/print-manual.html93
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/resources/destination.html8
-rw-r--r--testing/web-platform/tests/html/webappapis/user-prompts/resources/print-during-event.sub.html17
11933 files changed, 439441 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/META.yml b/testing/web-platform/tests/html/META.yml
new file mode 100644
index 0000000000..7eecf519f1
--- /dev/null
+++ b/testing/web-platform/tests/html/META.yml
@@ -0,0 +1,7 @@
+spec: https://html.spec.whatwg.org/multipage/
+suggested_reviewers:
+ - annevk
+ - domenic
+ - foolip
+ - jdm
+ - jgraham
diff --git a/testing/web-platform/tests/html/README.md b/testing/web-platform/tests/html/README.md
new file mode 100644
index 0000000000..2a444bd5a7
--- /dev/null
+++ b/testing/web-platform/tests/html/README.md
@@ -0,0 +1,21 @@
+# HTML
+
+This directory contains tests for [HTML](https://html.spec.whatwg.org/multipage/).
+
+Sub-directory names should be based on the URL of the corresponding part of the
+multipage-version specification. For example, the URL of
+"8.3 Base64 utility methods" is [https://html.spec.whatwg.org/multipage/webappapis.html#atob](https://html.spec.whatwg.org/multipage/webappapis.html#atob). So the directory in WPT is [webappapis/atob/](/html/webappapis/atob).
+
+For historical reasons, parts of HTML have their own directories:
+
+* [/custom-elements](/custom-elements)
+* [/eventsource](/eventsource)
+* [/imagebitmap-renderingcontext](/imagebitmap-renderingcontext)
+* [/innerText](/innerText)
+* [/shadow-dom](/shadow-dom)
+* [/webmessaging](/webmessaging)
+* [/websockets](/websockets)
+* [/webstorage](/webstorage)
+* [/workers](/workers)
+* [/worklets](/worklets)
+* [/x-frame-options](/x-frame-options)
diff --git a/testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js
new file mode 100644
index 0000000000..fbdeeab4ed
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js
@@ -0,0 +1,67 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+
+const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
+const control_iframe = document.createElement('iframe');
+const iframe_credentialless = document.createElement('iframe');
+
+promise_setup(async t => {
+ const createControlIframe = new Promise(async resolve => {
+ control_iframe.onload = resolve;
+ control_iframe.src = ORIGIN + `/common/blank.html`;
+ document.body.append(control_iframe);
+ });
+
+ const createIframeCredentialless = new Promise(async resolve => {
+ iframe_credentialless.onload = resolve;
+ iframe_credentialless.src = ORIGIN + `/common/blank.html`;
+ iframe_credentialless.credentialless = true;
+ document.body.append(iframe_credentialless);
+ });
+
+ await Promise.all([createControlIframe, createIframeCredentialless]);
+});
+
+// Create cross-origin popup from iframes. The opener should be blocked for
+// credentialless iframe and work for normal iframe.
+promise_test(async t => {
+ const control_token = token();
+ const control_src = REMOTE_ORIGIN + executor_path + `&uuid=${control_token}`;
+ const control_popup = control_iframe.contentWindow.open(control_src);
+ add_completion_callback(() => send(control_token, "close();"));
+ assert_equals(
+ control_popup.opener, control_iframe.contentWindow,
+ "Opener from normal iframe should be available.");
+
+ const credentialless_token = token();
+ const credentialless_src =
+ REMOTE_ORIGIN + executor_path + `&uuid=${credentialless_token}`;
+ const credentialless_popup =
+ iframe_credentialless.contentWindow.open(credentialless_src);
+ add_completion_callback(() => send(credentialless_token, "close();"));
+ assert_equals(credentialless_popup, null,
+ "Opener from credentialless iframe should be blocked.");
+}, 'Cross-origin popup from normal/credentiallessiframes.');
+
+// Create a same-origin popup from iframes. The opener should be blocked for
+// credentialless iframe and work for normal iframe.
+promise_test(async t => {
+ const control_token = token();
+ const control_src = ORIGIN + executor_path + `&uuid=${control_token}`;
+ const control_popup = control_iframe.contentWindow.open(control_src);
+ add_completion_callback(() => send(control_token, "close();"));
+ assert_equals(
+ control_popup.opener, control_iframe.contentWindow,
+ "Opener from normal iframe should be available.");
+
+ const credentialless_token = token();
+ const credentialless_src =
+ ORIGIN + executor_path + `&uuid=${credentialless_token}`;
+ const credentialless_popup = iframe_credentialless.contentWindow.open(credentialless_src);
+ add_completion_callback(() => send(credentialless_token, "close();"));
+ assert_equals(credentialless_popup, null,
+ "Opener from credentialless iframe should be blocked.");
+}, 'Same-origin popup from normal/credentialless iframes.');
diff --git a/testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js
new file mode 100644
index 0000000000..8c9c103429
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js
@@ -0,0 +1,50 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+
+const {ORIGIN} = get_host_info();
+
+promise_test_parallel(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = ORIGIN + "/common/blank.html?pipe=status(204)";
+ iframe.credentialless = false;
+ document.body.appendChild(iframe);
+ iframe.credentialless = true;
+ iframe.contentWindow.modified = true;
+ iframe.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => iframe.onload = resolve);
+ assert_true(iframe.credentialless);
+ assert_true(iframe.contentWindow.credentialless);
+ assert_equals(undefined, iframe.contentWindow.modified);
+}, "Credentialless (false => true) => window not reused.");
+
+promise_test_parallel(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = ORIGIN + "/common/blank.html?pipe=status(204)";
+ iframe.credentialless = true;
+ document.body.appendChild(iframe);
+ iframe.credentialless = false;
+ iframe.contentWindow.modified = true;
+ iframe.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => iframe.onload = resolve);
+ assert_false(iframe.credentialless);
+ assert_false(iframe.contentWindow.credentialless);
+ assert_equals(undefined, iframe.contentWindow.modified);
+}, "Credentialless (true => false) => window not reused.");
+
+promise_test_parallel(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.credentialless = true;
+ iframe.src = ORIGIN + "/common/blank.html?pipe=status(204)";
+ document.body.appendChild(iframe);
+ iframe.credentialless = true;
+ iframe.contentWindow.modified = true;
+ iframe.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => iframe.onload = resolve);
+ assert_true(iframe.credentialless);
+ assert_true(iframe.contentWindow.credentialless);
+ assert_true(iframe.contentWindow.modified);
+}, "Credentialless (true => true) => window reused.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js
new file mode 100644
index 0000000000..b816de4cd5
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js
@@ -0,0 +1,60 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// A script storing a value into the CacheStorage.
+const store_script = (key, value, done) => `
+ const request = new Request("/${key}.txt");
+ const response = new Response("${value}", {
+ headers: { "content-type": "plain/txt" }
+ });
+ const cache = await caches.open("v1");
+ const value = await cache.put(request, response.clone());
+ send("${done}", "stored");
+`;
+
+// A script loading a value from the CacheStorage.
+const load_script = (key, done) => `
+ const cache = await caches.open("v1");
+ const request = new Request("/${key}.txt");
+ try {
+ const response = await cache.match(request);
+ const value = await response.text();
+ send("${done}", value);
+ } catch (error) {
+ send("${done}", "not found");
+ }
+`;
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 2 actors: A credentialless iframe and a normal one.
+ const iframe_credentialless = newIframeCredentialless(origin);
+ const iframe_normal = newIframe(origin);
+ const response_queue_1 = token();
+ const response_queue_2 = token();
+
+ // 1. Each of them store a value in CacheStorage with different keys.
+ send(iframe_credentialless , store_script(key_1, "value_1", response_queue_1));
+ send(iframe_normal, store_script(key_2, "value_2", response_queue_2));
+ assert_equals(await receive(response_queue_1), "stored");
+ assert_equals(await receive(response_queue_2), "stored");
+
+ // 2. Each of them tries to retrieve the value from opposite side, without
+ // success.
+ send(iframe_credentialless , load_script(key_2, response_queue_1));
+ send(iframe_normal, load_script(key_1, response_queue_2));
+ assert_equals(await receive(response_queue_1), "not found");
+ assert_equals(await receive(response_queue_2), "not found");
+
+ // 3. Each of them tries to retrieve the value from their side, with success:
+ send(iframe_credentialless , load_script(key_1, response_queue_1));
+ send(iframe_normal, load_script(key_2, response_queue_2));
+ assert_equals(await receive(response_queue_1), "value_1");
+ assert_equals(await receive(response_queue_2), "value_2");
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js
new file mode 100644
index 0000000000..ddad924ea0
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js
@@ -0,0 +1,94 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// A set of tests, checking cookies defined from within a credentialless iframe
+// continue to work.
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = token()
+
+const credentialless_iframe = newIframeCredentialless(cross_origin);
+
+// Install some helper functions in the child to observe Cookies:
+promise_setup(async () => {
+ await send(credentialless_iframe, `
+ window.getMyCookie = () => {
+ const value = "; " + document.cookie;
+ const parts = value.split("; ${cookie_key}=");
+ if (parts.length !== 2)
+ return undefined
+ return parts.pop().split(';').shift();
+ };
+
+ window.nextCookieValue = () => {
+ return new Promise(resolve => {
+ const old_cookie = getMyCookie();
+ let timeToLive = 40; // 40 iterations of 100ms = 4s;
+ const interval = setInterval(() => {
+ const next_cookie_value = getMyCookie();
+ timeToLive--;
+ if (old_cookie !== next_cookie_value || timeToLive <= 0) {
+ clearInterval(interval);
+ resolve(next_cookie_value)
+ }
+ }, 100)
+ });
+ };
+ `);
+}, "Setup");
+
+promise_test(async test => {
+ const this_token = token();
+ send(credentialless_iframe, `
+ document.cookie = "${cookie_key}=cookie_value_1";
+ send("${this_token}", getMyCookie());
+ `);
+
+ assert_equals(await receive(this_token), "cookie_value_1");
+}, "Set/Get cookie via JS API");
+
+promise_test(async test => {
+ const resource_token = token();
+ send(credentialless_iframe, `
+ fetch("${showRequestHeaders(cross_origin, resource_token)}");
+ `);
+
+ const request_headers = JSON.parse(await receive(resource_token));
+ const cookie_value = parseCookies(request_headers)[cookie_key];
+ assert_equals(cookie_value, "cookie_value_1");
+}, "Get Cookie via subresource requests");
+
+promise_test(async test => {
+ const resource_token = token();
+ const resource_url = cross_origin + "/common/blank.html?pipe=" +
+ `|header(Set-Cookie,${cookie_key}=cookie_value_2;Path=/common/dispatcher)`;
+ const this_token = token();
+ send(credentialless_iframe, `
+ const next_cookie_value = nextCookieValue();
+ fetch("${resource_url}");
+ send("${this_token}", await next_cookie_value);
+ `);
+
+ assert_equals(await receive(this_token), "cookie_value_2");
+}, "Set Cookie via subresource requests");
+
+promise_test(async test => {
+ const resource_token = token();
+ const resource_url = cross_origin + "/common/blank.html?pipe=" +
+ `|header(Set-Cookie,${cookie_key}=cookie_value_3;Path=/common/dispatcher)`;
+ const this_token = token();
+ send(credentialless_iframe, `
+ const next_cookie_value = nextCookieValue();
+ const iframe = document.createElement("iframe");
+ iframe.src = "${resource_url}";
+ document.body.appendChild(iframe);
+ send("${this_token}", await next_cookie_value);
+ `);
+
+ assert_equals(await receive(this_token), "cookie_value_3");
+}, "Set Cookie via navigation requests");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js
new file mode 100644
index 0000000000..d6889ae52d
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js
@@ -0,0 +1,128 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = "credentialless_iframe_load_cookie";
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+const cookieFromResource = async resource_token => {
+ let headers = JSON.parse(await receive(resource_token));
+ return parseCookies(headers)[cookie_key];
+};
+
+// Load a credentialless iframe, return the HTTP request cookies.
+const cookieFromCredentiallessIframeRequest = async (iframe_origin) => {
+ const resource_token = token();
+ let iframe = document.createElement("iframe");
+ iframe.src = `${showRequestHeaders(iframe_origin, resource_token)}`;
+ iframe.credentialless = true;
+ document.body.appendChild(iframe);
+ return await cookieFromResource(resource_token);
+};
+
+// Load a resource `type` from the iframe with `document_token`,
+// return the HTTP request cookies.
+const cookieFromResourceInIframe =
+ async (document_token, resource_origin, type = "img") => {
+ const resource_token = token();
+ send(document_token, `
+ let el = document.createElement("${type}");
+ el.src = "${showRequestHeaders(resource_origin, resource_token)}";
+ document.body.appendChild(el);
+ `);
+ return await cookieFromResource(resource_token);
+};
+
+promise_test_parallel(async test => {
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin),
+ ]);
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromCredentiallessIframeRequest(same_origin),
+ undefined
+ );
+ }, "Credentialless same-origin iframe is loaded without credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromCredentiallessIframeRequest(cross_origin),
+ undefined
+ );
+ }, "Credentialless cross-origin iframe is loaded without credentials");
+
+ const iframe_same_origin = newIframeCredentialless(same_origin);
+ const iframe_cross_origin = newIframeCredentialless(cross_origin);
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, same_origin),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send same_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, cross_origin),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send cross_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, cross_origin),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send cross_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, same_origin),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send same_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, same_origin,
+ "iframe"),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send same_origin credentials "
+ + "on child iframe");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, cross_origin,
+ "iframe"),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send cross_origin credentials "
+ + "on child iframe");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, cross_origin,
+ "iframe"),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send cross_origin credentials "
+ + "on child iframe");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, same_origin,
+ "iframe"),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send same_origin credentials "
+ + "on child iframe");
+
+}, "Setup")
diff --git a/testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js
new file mode 100644
index 0000000000..8c5e2fa60f
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js
@@ -0,0 +1,124 @@
+// META: variant=?1-1
+// META: variant=?2-2
+// META: variant=?3-3
+// META: variant=?4-4
+// META: variant=?5-5
+// META: variant=?6-6
+// META: variant=?7-7
+// META: variant=?8-8
+// META: variant=?9-9
+// META: variant=?10-10
+// META: variant=?11-11
+// META: variant=?12-12
+// META: variant=?13-last
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/subset-tests.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+// META: script=./resources/embedding-test.js
+
+const {REMOTE_ORIGIN} = get_host_info();
+
+// variant = 1
+subsetTest(embeddingTest,
+ "Parent embeds same-origin credentialless iframe", {
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 2
+subsetTest(embeddingTest,
+ "Parent embeds cross-origin credentialless iframe", {
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 3
+subsetTest(embeddingTest,
+ "COEP:require-corp parent embeds same-origin credentialless iframe", {
+ parent_headers: coep_require_corp,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 4
+subsetTest(embeddingTest,
+ "COEP:require-corp parent embeds cross-origin credentialless iframe", {
+ parent_headers: coep_require_corp,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 5
+subsetTest(embeddingTest,
+ "COEP:credentialless parent embeds same-origin credentialless iframe", {
+ parent_headers: coep_credentialless,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 6
+subsetTest(embeddingTest,
+ "COEP:credentialless parent embeds cross-origin credentialless iframe", {
+ parent_headers: coep_credentialless,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 7
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:require-corp embeds same-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 8
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:require-corp embeds cross-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 9
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:credentialless embeds same-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_credentialless,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 10
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:credentialless embeds cross-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_credentialless,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 11
+subsetTest(embeddingTest,
+ "Parents embeds a CSP:frame-ancestors credentialless iframe", {
+ child_headers: "|header(Content-Security-Policy,frame-ancestors 'none')",
+ expectation: EXPECT_BLOCK,
+});
+
+// variant = 12
+subsetTest(embeddingTest,
+ "Cross-Origin-Isolated parent embeds same-origin COEP credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ child_headers: coop_same_origin + coep_require_corp,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 13
+subsetTest(embeddingTest,
+ "Cross-Origin-Isolated parent embeds cross-origin COEP credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ child_headers: coop_same_origin + coep_require_corp,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
diff --git a/testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js
new file mode 100644
index 0000000000..e5d12397a9
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js
@@ -0,0 +1,64 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/fenced-frame/resources/utils.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+// META: timeout=long
+
+setup(() => {
+ assert_implements(window.HTMLFencedFrameElement,
+ "HTMLFencedFrameElement is not supported.");
+})
+
+// 4 actors:
+// A (this document)
+// ┌─────────────────────┴───────â”
+// ┌─┼────────────────────────┠D (credentialless-iframe)
+// │ B (fenced-frame) │
+// │ │ │
+// │ C (credentialless-iframe)│
+// └──────────────────────────┘
+//
+// This test whether the two credentialless iframe can communicate and bypass the
+// fencedframe boundary. This shouldn't happen.
+promise_test(async test => {
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const msg_queue = token();
+
+ // Create the the 3 actors.
+ const iframe_credentialless_1 = newIframeCredentialless(cross_origin);
+ const fenced_frame = await newFencedFrame(cross_origin);
+ send(fenced_frame, `
+ const importScript = ${importScript};
+ await importScript("/common/utils.js");
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ const support_loading_mode_fenced_frame =
+ "|header(Supports-Loading-Mode,fenced-frame)";
+ const iframe_credentialless_2 = newIframeCredentialless("${cross_origin}",
+ support_loading_mode_fenced_frame);
+ send("${msg_queue}", iframe_credentialless_2);
+ `);
+ const iframe_credentialless_2 = await receive(msg_queue);
+
+ // Try to communicate using BroadCastChannel, in between the credentialless
+ // iframes.
+ const bc_key = token();
+ send(iframe_credentialless_1, `
+ const bc = new BroadcastChannel("${bc_key}");
+ bc.onmessage = event => send("${msg_queue}", event.data);
+ send("${msg_queue}", "BroadcastChannel registered");
+ `);
+ assert_equals(await receive(msg_queue), "BroadcastChannel registered");
+ await send(iframe_credentialless_2, `
+ const bc = new BroadcastChannel("${bc_key}");
+ bc.postMessage("Can communicate");
+ `);
+ test.step_timeout(() => {
+ send(msg_queue, "Cannot communicate");
+ }, 4000);
+
+ assert_equals(await receive(msg_queue), "Cannot communicate");
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js
new file mode 100644
index 0000000000..c72341fc5c
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js
@@ -0,0 +1,44 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+// META: timeout=long
+
+setup(() => {
+ assert_implements(window.HTMLFencedFrameElement,
+ "HTMLFencedFrameElement is not supported.");
+})
+
+// Check whether this credentialless bit propagates toward FencedFrame. It
+// shouldn't.
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_ORIGIN;
+ const msg_queue = token();
+
+ // 1. Create a credentialless iframe.
+ const iframe_credentialless = newIframeCredentialless(origin);
+
+ // 2. Create a FencedFrame within it.
+ send(iframe_credentialless, `
+ const importScript = ${importScript};
+ await importScript("/common/utils.js");
+ await importScript("/fenced-frame/resources/utils.js");
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ const frame_fenced = await newFencedFrame("${origin}");
+ send("${msg_queue}", frame_fenced);
+ `);
+ // TODO: Properly generate a fenced frame to check credentialless.
+ assert_true(false, "Fenced frame cannot be created.");
+ const frame_fenced = await receive(msg_queue);
+
+ // 3. Expect it not to be considered credentialless.
+ send(frame_fenced, `
+ send("${msg_queue}", window.credentialless);
+ `);
+ // TODO: Properly generate a fenced frame which can perform this check.
+ assert_equals(await receive(msg_queue), "false",
+ "Check window.credentialless in FencedFrame");
+}, 'FencedFrame within a credentialless iframe is not credentialless')
diff --git a/testing/web-platform/tests/html/anonymous-iframe/hasStorageAccess.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/hasStorageAccess.tentative.https.window.js
new file mode 100644
index 0000000000..bf33023793
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/hasStorageAccess.tentative.https.window.js
@@ -0,0 +1,30 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+setup(() => {
+ assert_implements(document.hasStorageAccess,
+ "requestStorageAccess is not supported.");
+})
+
+const hasStorageAccess = (iframe) => {
+ const reply = token();
+ send(iframe, `
+ send("${reply}", await document.hasStorageAccess());
+ `);
+ return receive(reply);
+}
+
+promise_test(async test => {
+ const same_origin = window.origin;
+ const iframe = newIframeCredentialless(same_origin);
+ assert_equals(await hasStorageAccess(iframe), "false");
+}, "Same-origin credentialless iframe can't request storage access");
+
+promise_test(async test => {
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const iframe = newIframeCredentialless(cross_origin);
+ assert_equals(await hasStorageAccess(iframe), "false");
+}, "Cross-origin credentialless iframe can't request storage access");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js
new file mode 100644
index 0000000000..d2749c6be2
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js
@@ -0,0 +1,104 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// "token()" is used to get unique value for every execution of the test. This
+// avoids potential side effects of one run toward the second.
+const g_db_store = token();
+const g_db_name = token();
+const g_db_version = 1;
+
+// A script storing "|id|=|value|" in IndexedDB.
+const write_script = (id, value, done) => `
+ // Open the database:
+ const request = indexedDB.open("${g_db_name}", "${g_db_version}");
+ request.onupgradeneeded = () => {
+ request.result.createObjectStore("${g_db_store}", {keyPath: "id"});
+ };
+ await new Promise(r => request.onsuccess = r);
+ const db = request.result;
+
+ // Write the value:
+ const transaction_write = db.transaction("${g_db_store}", "readwrite");
+ transaction_write.objectStore("${g_db_store}").add({
+ id: "${id}",
+ value: "${value}",
+ });
+ await transaction_write.complete;
+
+ db.close();
+ send("${done}", "Done");
+`;
+
+// A script retrieving what was stored inside IndexedDB.
+const read_script = (done) => `
+ // Open the database:
+ const request = indexedDB.open("${g_db_name}", "${g_db_version}");
+ await new Promise(r => request.onsuccess = r);
+ const db = request.result;
+
+ // Read:
+ const transaction_read = db.transaction("${g_db_store}", "readonly");
+ const get_all = transaction_read.objectStore("${g_db_store}").getAll();
+ await new Promise(r => transaction_read.oncomplete = r);
+
+ db.close();
+ send("${done}", JSON.stringify(get_all.result));
+`;
+
+promise_test(async test => {
+ // 4 actors: 2 credentialless iframe and 2 normal iframe.
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const iframes = [
+ newIframeCredentialless(origin),
+ newIframeCredentialless(origin),
+ newIframe(origin),
+ newIframe(origin),
+ ];
+
+ // 1. Write a different key-value pair from the iframes in IndexedDB:
+ const keys = iframes.map(token);
+ const values = iframes.map(token);
+ const response_queues = iframes.map(token);
+ await Promise.all(iframes.map(async (_, i) => {
+ send(iframes[i], write_script(keys[i], values[i], response_queues[i]));
+ assert_equals(await receive(response_queues[i]), "Done");
+ }));
+
+ // 2. Read the state from every iframes:
+ const states = await Promise.all(iframes.map(async (_, i) => {
+ send(iframes[i], read_script(response_queues[i]));
+ const reply = JSON.parse(await receive(response_queues[i]));
+
+ const state = {}
+ for(entry of reply)
+ state[entry.id] = entry.value;
+ return state;
+ }));
+
+
+ // Verify the two credentialless iframe share the same state and the normal
+ // iframe share a second state
+ assert_equals(states[0][keys[0]], values[0]);
+ assert_equals(states[0][keys[1]], values[1]);
+ assert_equals(states[0][keys[2]], undefined);
+ assert_equals(states[0][keys[3]], undefined);
+
+ assert_equals(states[1][keys[0]], values[0]);
+ assert_equals(states[1][keys[1]], values[1]);
+ assert_equals(states[1][keys[2]], undefined);
+ assert_equals(states[1][keys[3]], undefined);
+
+ assert_equals(states[2][keys[0]], undefined);
+ assert_equals(states[2][keys[1]], undefined);
+ assert_equals(states[2][keys[2]], values[2]);
+ assert_equals(states[2][keys[3]], values[3]);
+
+ assert_equals(states[3][keys[0]], undefined);
+ assert_equals(states[3][keys[1]], undefined);
+ assert_equals(states[3][keys[2]], values[2]);
+ assert_equals(states[3][keys[3]], values[3]);
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js
new file mode 100644
index 0000000000..8d8b095588
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js
@@ -0,0 +1,34 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+
+const {ORIGIN} = get_host_info();
+
+promise_test_parallel(async t => {
+ const parent = document.createElement("iframe");
+ parent.credentialless = true;
+ document.body.appendChild(parent);
+ parent.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => parent.onload = resolve);
+ assert_true(parent.credentialless);
+
+ const child = document.createElement("iframe");
+ parent.contentDocument.body.appendChild(child);
+ assert_false(child.credentialless);
+ assert_true(child.contentWindow.credentialless);
+}, "Initial empty document inherits from parent's document.");
+
+promise_test_parallel(async t => {
+ const parent = document.createElement("iframe");
+ document.body.appendChild(parent);
+ parent.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => parent.onload = resolve);
+ assert_false(parent.credentialless);
+
+ const child = document.createElement("iframe");
+ child.credentialless = true;
+ parent.contentDocument.body.appendChild(child);
+ assert_true(child.credentialless);
+ assert_true(child.contentWindow.credentialless);
+}, "Initial empty document inherits from its's iframe's credentialless attribute.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js
new file mode 100644
index 0000000000..37678ff12b
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js
@@ -0,0 +1,74 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// This test verifies the behavior of the initial empty document nested inside
+// credentialless iframes.
+//
+// The following tree of frames and documents is used:
+// A
+// ├──B (credentialless)
+// │ └──D (initial empty document)
+// └──C (control)
+// └──E (initial empty document)
+//
+// Storage used for D and E must be different.
+promise_test(async test => {
+ const iframe_B = newIframeCredentialless(origin);
+ const iframe_C = newIframe(origin);
+
+ // Create iframe_D and store a value in localStorage.
+ const key_D = token();
+ const value_D = "value_D";
+ const queue_B = token();
+ send(iframe_B, `
+ const iframe_D = document.createElement("iframe");
+ document.body.appendChild(iframe_D);
+ iframe_D.contentWindow.localStorage.setItem("${key_D}","${value_D}");
+ send("${queue_B}", "Done");
+ `);
+
+ // Create iframe_E and store a value in localStorage.
+ const key_E = token();
+ const value_E = "value_E";
+ const queue_C = token();
+ send(iframe_C, `
+ const iframe_E = document.createElement("iframe");
+ document.body.appendChild(iframe_E);
+ iframe_E.contentWindow.localStorage.setItem("${key_E}","${value_E}");
+ send("${queue_C}", "Done");
+ `);
+
+ assert_equals(await receive(queue_B), "Done");
+ assert_equals(await receive(queue_C), "Done");
+
+ // Try to load both values from both contexts:
+ send(iframe_B, `
+ const iframe_D = document.querySelector("iframe");
+ const value_D = iframe_D.contentWindow.localStorage.getItem("${key_D}");
+ const value_E = iframe_D.contentWindow.localStorage.getItem("${key_E}");
+ send("${queue_B}", value_D);
+ send("${queue_B}", value_E);
+ `);
+ send(iframe_C, `
+ const iframe_E = document.querySelector("iframe");
+ const value_D = iframe_E.contentWindow.localStorage.getItem("${key_D}");
+ const value_E = iframe_E.contentWindow.localStorage.getItem("${key_E}");
+ send("${queue_C}", value_D);
+ send("${queue_C}", value_E);
+ `);
+
+ // Verify the credentialless iframe and the normal one do not have access to
+ // each other.
+ assert_equals(await receive(queue_B), value_D, // key_D
+ "Credentialless iframe can access credentialless context");
+ assert_equals(await receive(queue_B), "", // key_E
+ "Credentialless iframe can't access credentialled context");
+ assert_equals(await receive(queue_C), "", // key_D
+ "Credentialled iframe can't access credentialless context");
+ assert_equals(await receive(queue_C), value_E, // key_E
+ "Credentialled iframe can access credentialled context");
+}, "Local storage is correctly partitioned with regards to credentialless " +
+ "iframe in initial empty documents.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js
new file mode 100644
index 0000000000..bd80ff4729
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js
@@ -0,0 +1,57 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// Make |iframe| to store |key|=|value| into LocalStorage.
+const store = async (iframe, key, value) => {
+ const response_queue = token();
+ send(iframe, `
+ localStorage.setItem("${key}", "${value}");
+ send("${response_queue}", "stored");
+ `);
+ assert_equals(await receive(response_queue), "stored");
+};
+
+// Make |iframe| to load |key| in LocalStorage. Check it matches the
+// |expected_value|.
+const load = async (iframe, key, expected_value) => {
+ const response_queue = token();
+ send(iframe, `
+ const value = localStorage.getItem("${key}");
+ send("${response_queue}", value || "not found");
+ `);
+ assert_equals(await receive(response_queue), expected_value);
+};
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 4 actors: 2 credentialless iframe and 2 normal iframe.
+ const iframe_credentialless_1 = newIframeCredentialless(origin);
+ const iframe_credentialless_2 = newIframeCredentialless(origin);
+ const iframe_normal_1 = newIframe(origin);
+ const iframe_normal_2 = newIframe(origin);
+
+ // 1. Store a value in one credentialless iframe and one normal iframe.
+ await Promise.all([
+ store(iframe_credentialless_1, key_1, "value_1"),
+ store(iframe_normal_1, key_2, "value_2"),
+ ]);
+
+ // 2. Check what each of them can retrieve.
+ await Promise.all([
+ load(iframe_credentialless_1, key_1, "value_1"),
+ load(iframe_credentialless_2, key_1, "value_1"),
+ load(iframe_credentialless_1, key_2, "not found"),
+ load(iframe_credentialless_2, key_2, "not found"),
+
+ load(iframe_normal_1, key_1, "not found"),
+ load(iframe_normal_2, key_1, "not found"),
+ load(iframe_normal_1, key_2, "value_2"),
+ load(iframe_normal_2, key_2, "value_2"),
+ ]);
+}, "Local storage is correctly partitioned with regards to credentialless iframe");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/requestStorageAccess.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/requestStorageAccess.tentative.https.window.js
new file mode 100644
index 0000000000..b7fbcd84eb
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/requestStorageAccess.tentative.https.window.js
@@ -0,0 +1,35 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+setup(() => {
+ assert_implements(document.requestStorageAccess,
+ "requestStorageAccess is not supported.");
+})
+
+const requestStorageAccess = (iframe) => {
+ const reply = token();
+ send(iframe, `
+ try {
+ await document.requestStorageAccess();
+ send("${reply}", "success");
+ } catch {
+ send("${reply}", "failed");
+ }
+ `);
+ return receive(reply);
+}
+
+promise_test(async test => {
+ const same_origin = window.origin;
+ const iframe = newIframeCredentialless(same_origin);
+ assert_equals(await requestStorageAccess(iframe), "failed");
+}, "Same-origin credentialless iframe can't request storage access");
+
+promise_test(async test => {
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const iframe = newIframeCredentialless(cross_origin);
+ assert_equals(await requestStorageAccess(iframe), "failed");
+}, "Cross-origin credentialless iframe can't request storage access");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/requestStorageAccessFor.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/requestStorageAccessFor.tentative.https.window.js
new file mode 100644
index 0000000000..fa2fc6576f
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/requestStorageAccessFor.tentative.https.window.js
@@ -0,0 +1,35 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+setup(() => {
+ assert_implements(document.requestStorageAccessFor,
+ "requestStorageAccessFor is not supported.");
+})
+
+const requestStorageAccessFor = (iframe, origin) => {
+ const reply = token();
+ send(iframe, `
+ try {
+ await document.requestStorageAccessFor("${origin}");
+ send("${reply}", "success");
+ } catch {
+ send("${reply}", "failed");
+ }
+ `);
+ return receive(reply);
+}
+
+promise_test(async test => {
+ const same_origin = window.origin;
+ const iframe = newIframeCredentialless(same_origin);
+ assert_equals(await requestStorageAccessFor(iframe, same_origin), "failed");
+}, "Same-origin credentialless iframe can't request storage access");
+
+promise_test(async test => {
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const iframe = newIframeCredentialless(cross_origin);
+ assert_equals(await requestStorageAccessFor(iframe, cross_origin), "failed");
+}, "Cross-origin credentialless iframe can't request storage access");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js
new file mode 100644
index 0000000000..ccaa41f9ef
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js
@@ -0,0 +1,49 @@
+// META: script=/common/utils.js
+
+promise_test(async t => {
+ let iframe_allowed = (iframe) => new Promise(async resolve => {
+ window.addEventListener("message", t.step_func(msg => {
+ if (msg.source !== iframe.contentWindow) return;
+ assert_equals(msg.data, "loaded",
+ "Unexpected message from broadcast channel.");
+ resolve(true);
+ }));
+
+ // To see whether the iframe was blocked, we check whether it
+ // becomes cross-origin (since error pages are loaded cross-origin).
+ await t.step_wait(() => {
+ try {
+ // Accessing contentWindow.location.href cross-origin throws.
+ iframe.contentWindow.location.href === null;
+ return false;
+ } catch {
+ return true;
+ }
+ });
+ resolve(false);
+ });
+
+ // Create a credentialless child iframe.
+ const child = document.createElement("iframe");
+ child.credentialless = true;
+ t.add_cleanup(() => child.remove());
+
+ child.src = "/html/cross-origin-embedder-policy/resources/" +
+ "navigate-none.sub.html?postMessageTo=top";
+ document.body.append(child);
+
+ assert_true(await iframe_allowed(child),
+ "The credentialless iframe should be allowed.");
+
+ // Create a child of the credentialless iframe. Even if the grandchild
+ // does not have the 'credentialless' attribute set, it inherits the
+ // credentialless property from the parent.
+ const grandchild = child.contentDocument.createElement("iframe");
+
+ grandchild.src = "/html/cross-origin-embedder-policy/resources/" +
+ "navigate-none.sub.html?postMessageTo=top";
+ child.contentDocument.body.append(grandchild);
+
+ assert_true(await iframe_allowed(grandchild),
+ "The child of the credentialless iframe should be allowed.");
+}, 'Loading a credentialless iframe with COEP: require-corp is allowed.');
diff --git a/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/common.js b/testing/web-platform/tests/html/anonymous-iframe/resources/common.js
new file mode 100644
index 0000000000..2fa51aa9c6
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/common.js
@@ -0,0 +1,55 @@
+// Create a credentialless iframe. The new document will execute any scripts
+// sent toward the token it returns.
+const newIframeCredentialless = (child_origin, opt_headers) => {
+ opt_headers ||= "";
+ const sub_document_token = token();
+ let iframe = document.createElement('iframe');
+ iframe.src = child_origin + executor_path + opt_headers +
+ `&uuid=${sub_document_token}`;
+ iframe.credentialless = true;
+ document.body.appendChild(iframe);
+ return sub_document_token;
+};
+
+// Create a normal iframe. The new document will execute any scripts sent
+// toward the token it returns.
+const newIframe = (child_origin) => {
+ const sub_document_token = token();
+ let iframe = document.createElement('iframe');
+ iframe.src = child_origin + executor_path + `&uuid=${sub_document_token}`;
+ iframe.credentialless = false
+ document.body.appendChild(iframe);
+ return sub_document_token;
+};
+
+// Create a popup. The new document will execute any scripts sent toward the
+// token it returns.
+const newPopup = (test, origin) => {
+ const popup_token = token();
+ const popup = window.open(origin + executor_path + `&uuid=${popup_token}`);
+ test.add_cleanup(() => popup.close());
+ return popup_token;
+}
+
+// Create a fenced frame. The new document will execute any scripts sent
+// toward the token it returns.
+const newFencedFrame = async (child_origin) => {
+ const support_loading_mode_fenced_frame =
+ "|header(Supports-Loading-Mode,fenced-frame)";
+ const sub_document_token = token();
+ const url = child_origin + executor_path +
+ support_loading_mode_fenced_frame +
+ `&uuid=${sub_document_token}`;
+ const urn = await generateURNFromFledge(url, []);
+ attachFencedFrame(urn);
+ return sub_document_token;
+};
+
+const importScript = (url) => {
+ const script = document.createElement("script");
+ script.type = "text/javascript";
+ script.src = url;
+ const loaded = new Promise(resolve => script.onload = resolve);
+ document.body.appendChild(script);
+ return loaded;
+}
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js b/testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js
new file mode 100644
index 0000000000..fb5d11efa0
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js
@@ -0,0 +1,72 @@
+// One document embeds another in an iframe. Both are loaded from the network.
+// Check whether or not the child can load.
+
+// There are no interoperable ways to check an iframe failed to load. So a
+// timeout is being used. See https://github.com/whatwg/html/issues/125
+// Moreover, we want to track progress, managing timeout explicitly allows to
+// get a per-test results, even in case of failure of one.
+setup({ explicit_timeout: true });
+
+const EXPECT_LOAD = "load";
+const EXPECT_BLOCK = "block";
+
+// Load a credentialless iframe. Control both the parent and the child headers.
+// Check whether it loaded or not.
+const embeddingTest = (description, {
+ parent_headers,
+ child_headers,
+ child_origin,
+ expectation,
+}) => {
+ // Default values:
+ child_origin ||= globalThis.origin;
+ parent_headers ||= "";
+ child_headers||= "";
+
+ const parent_origin = window.origin;
+
+ promise_test_parallel(async test => {
+ const parent_token = token();
+ const parent_url = parent_origin + executor_path + parent_headers +
+ `&uuid=${parent_token}`;
+
+ const child_token = token();
+ const child_url = child_origin + executor_path + child_headers +
+ `&uuid=${child_token}`;
+
+ // Create the parent:
+ window.open(parent_url);
+ add_completion_callback(() => send(parent_token, "close()"));
+
+ // The parent creates its child:
+ await send(parent_token, `
+ const iframe = document.createElement("iframe");
+ iframe.credentialless = true;
+ iframe.src = "${child_url}";
+ document.body.appendChild(iframe);
+ `);
+
+ // Ping the child to know whether it was allowed to load or not:
+ const reply_token = token();
+ await send(child_token, `
+ send("${reply_token}", "load");
+ `);
+
+ // There are no interoperable ways to check an iframe failed to load. So a
+ // timeout is being used.
+ // See https://github.com/whatwg/html/issues/125
+ // Use a shorter timeout when it is expected to be reached.
+ // - The long delay reduces the false-positive rate. False-positive causes
+ // stability problems on bot, so a big delay is used to vanish them.
+ // https://crbug.com/1215956.
+ // - The short delay avoids delaying too much the test(s) for nothing and
+ // timing out. False-negative are not a problem, they just need not to
+ // overwhelm the true-negative, which is trivial to get.
+ step_timeout(() => send(reply_token, "block"), expectation == EXPECT_BLOCK
+ ? 1500
+ : 3500
+ );
+
+ assert_equals(await receive(reply_token), expectation);
+ }, description);
+};
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js b/testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js
new file mode 100644
index 0000000000..288ad5954e
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js
@@ -0,0 +1,16 @@
+let messages = {};
+let ports = {};
+
+self.addEventListener("message", e => {
+ const from = e.data.from;
+ const check = e.data.check;
+
+ if (from) {
+ messages[from] = true;
+ ports[from] = e.ports[0];
+ }
+
+ if (check) {
+ ports[check].postMessage(messages);
+ }
+});
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js b/testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js
new file mode 100644
index 0000000000..8b416c33d7
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js
@@ -0,0 +1,21 @@
+let messages = {};
+
+onconnect = function(e) {
+ let port = e.ports[0];
+
+ port.addEventListener('message', function(e) {
+ const action = e.data.action;
+ const from = e.data.from;
+
+ if (action === 'record') {
+ messages[from] = true;
+ port.postMessage({ack: from});
+ }
+
+ if (action === 'retrieve') {
+ port.postMessage({ack: from, messages: messages});
+ }
+ });
+
+ port.start();
+};
diff --git a/testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js
new file mode 100644
index 0000000000..14997a4754
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js
@@ -0,0 +1,85 @@
+// META: script=/common/utils.js
+
+const sw_url = location.pathname.replace(/[^/]*$/, '') +
+ "./resources/serviceworker-partitioning-helper.js";
+
+promise_test(async t => {
+ // Create 4 iframes (two normal and two credentialless ones) and register
+ // a serviceworker with the same scope and url in all of them.
+ //
+ // Registering the same service worker again with the same url and
+ // scope is a no-op. However, credentialless iframes get partitioned
+ // service workers, so we should have a total of 2 service workers
+ // at the end (one for the normal iframes and one for the credentialless
+ // ones).
+ let iframes = await Promise.all([
+ { name: "normal", credentialless: false},
+ { name: "normal_control", credentialless: false},
+ { name: "credentialless", credentialless: true},
+ { name: "credentialless_control", credentialless: true},
+ ].map(async ({name, credentialless}) => {
+
+ let iframe = await new Promise(resolve => {
+ let iframe = document.createElement('iframe');
+ iframe.onload = () => resolve(iframe);
+ iframe.src = '/common/blank.html';
+ if (credentialless) iframe.credentialless = true;
+ document.body.append(iframe);
+ });
+
+ let sw = await new Promise(resolve => {
+ iframe.contentWindow.navigator.serviceWorker.register(sw_url)
+ .then(r => {
+ add_completion_callback(_ => r.unregister());
+ resolve(r.active || r.installing || r.waiting);
+ });
+ });
+ return { iframe: iframe, name: name, sw: sw };
+ }));
+
+ // Setup a MessageChannel for each pair (iframe, serviceworker).
+ // Ping each serviceworker telling him which iframe it belongs to.
+ iframes.forEach((iframe, i) => {
+ iframe.channel = new MessageChannel();
+ iframe.sw.postMessage({ from: iframe.name }, [iframe.channel.port2]);
+ });
+
+ let msg_promises = iframes.map(iframe => new Promise(resolve => {
+ iframe.channel.port1.onmessage = event => resolve(event.data);
+ }));
+
+ // Ping each (iframe, serviceworker) asking for which messages it got.
+ iframes.map(iframe => iframe.sw.postMessage({ check: iframe.name }));
+
+ // Collect all replies.
+ let msgs = await Promise.all(msg_promises);
+
+ // The "normal" iframe serviceworker belongs to the "normal" and the
+ // "normal_control" iframes.
+ assert_true(!!msgs[0]["normal"]);
+ assert_true(!!msgs[0]["normal_control"]);
+ assert_false(!!msgs[0]["credentialless"]);
+ assert_false(!!msgs[0]["credentialless_control"]);
+
+ // The "normal_control" iframe shares the same serviceworker as the "normal"
+ // iframe.
+ assert_true(!!msgs[1]["normal"]);
+ assert_true(!!msgs[1]["normal_control"]);
+ assert_false(!!msgs[1]["credentialless"]);
+ assert_false(!!msgs[1]["credentialless_control"]);
+
+ // The "credentialless" iframe serviceworker belongs to the "credentialless"
+ // and the "credentialless_control" iframes.
+ assert_false(!!msgs[2]["normal"]);
+ assert_false(!!msgs[2]["normal_control"]);
+ assert_true(!!msgs[2]["credentialless"]);
+ assert_true(!!msgs[2]["credentialless_control"]);
+
+ // The "credentialless_control" iframe shares the same serviceworker as the
+ // "credentialless" iframe.
+ assert_false(!!msgs[3]["normal"]);
+ assert_false(!!msgs[3]["normal_control"]);
+ assert_true(!!msgs[3]["credentialless"]);
+ assert_true(!!msgs[3]["credentialless_control"]);
+
+}, "credentialless iframes get partitioned service workers.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js
new file mode 100644
index 0000000000..425886ce38
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js
@@ -0,0 +1,57 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// Make |iframe| to store |key|=|value| into sessionStorage.
+const store = async (iframe, key, value) => {
+ const response_queue = token();
+ send(iframe, `
+ sessionStorage.setItem("${key}", "${value}");
+ send("${response_queue}", "stored");
+ `);
+ assert_equals(await receive(response_queue), "stored");
+};
+
+// Make |iframe| to load |key| in sessionStorage. Check it matches the
+// |expected_value|.
+const load = async (iframe, key, expected_value) => {
+ const response_queue = token();
+ send(iframe, `
+ const value = sessionStorage.getItem("${key}");
+ send("${response_queue}", value || "not found");
+ `);
+ assert_equals(await receive(response_queue), expected_value);
+};
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 4 actors: 2 credentialless iframe and 2 normal iframe.
+ const iframe_credentialless_1 = newIframeCredentialless(origin);
+ const iframe_credentialless_2 = newIframeCredentialless(origin);
+ const iframe_normal_1 = newIframe(origin);
+ const iframe_normal_2 = newIframe(origin);
+
+ // 1. Store a value in one credentialless iframe and one normal iframe.
+ await Promise.all([
+ store(iframe_credentialless_1, key_1, "value_1"),
+ store(iframe_normal_1, key_2, "value_2"),
+ ]);
+
+ // 2. Check what each of them can retrieve.
+ await Promise.all([
+ load(iframe_credentialless_1, key_1, "value_1"),
+ load(iframe_credentialless_2, key_1, "value_1"),
+ load(iframe_credentialless_1, key_2, "not found"),
+ load(iframe_credentialless_2, key_2, "not found"),
+
+ load(iframe_normal_1, key_1, "not found"),
+ load(iframe_normal_2, key_1, "not found"),
+ load(iframe_normal_1, key_2, "value_2"),
+ load(iframe_normal_2, key_2, "value_2"),
+ ]);
+}, "Session storage is correctly partitioned with regards to credentialless iframe");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js
new file mode 100644
index 0000000000..6c373ee490
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js
@@ -0,0 +1,93 @@
+// META: script=/common/utils.js
+
+const sw_url = location.pathname.replace(/[^/]*$/, '') +
+ "./resources/sharedworker-partitioning-helper.js";
+
+promise_test(async t => {
+ // Create 4 iframes (two normal and two credentialless ones) and create
+ // a shared worker with the same url in all of them.
+ //
+ // Creating the same shared worker again with the same url is a
+ // no-op. However, credentialless iframes get partitioned shared workers,
+ // so we should have a total of 2 shared workers at the end (one for
+ // the normal iframes and one for the credentialless ones).
+ let iframes = await Promise.all([
+ { name: "normal", credentialless: false},
+ { name: "normal_control", credentialless: false},
+ { name: "credentialless", credentialless: true},
+ { name: "credentialless_control", credentialless: true},
+ ].map(async ({name, credentialless}) => {
+
+ let iframe = await new Promise(resolve => {
+ let iframe = document.createElement('iframe');
+ iframe.onload = () => resolve(iframe);
+ iframe.src = '/common/blank.html';
+ if (credentialless) iframe.credentialless = true;
+ document.body.append(iframe);
+ });
+
+ let sw = new iframe.contentWindow.SharedWorker(sw_url);
+ return { iframe: iframe, name: name, sw: sw };
+ }));
+
+ // Ping each worker telling him which iframe it belongs to.
+ await Promise.all(iframes.map(iframe => {
+ iframe.sw.port.postMessage({ action: 'record', from: iframe.name});
+ return new Promise(resolve => {
+ iframe.sw.port.onmessage = event => {
+ if (event.data.ack === iframe.name) resolve();
+ }
+ });
+ }));
+
+ // Ping each (iframe, sharedworker) asking for which messages it got.
+ let msgs = await Promise.all(iframes.map(iframe => {
+ iframe.sw.port.postMessage({ action: 'retrieve', from: iframe.name });
+ return new Promise(resolve => {
+ iframe.sw.port.onmessage = event => {
+ if (event.data.ack === iframe.name) resolve(event.data.messages);
+ }
+ });
+ }));
+
+ // The "normal" iframe sharedworker belongs to the "normal" and the
+ // "normal_control" iframes.
+ assert_true(!!msgs[0]["normal"] &&
+ !!msgs[0]["normal_control"] &&
+ !msgs[0]["credentialless"] &&
+ !msgs[0]["credentialless_control"],
+ 'The "normal" iframe\'s sharedworker should return ' +
+ '{"normal": true, "normal_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[0]));
+
+ // The "normal_control" iframe shares the same sharedworker as the "normal"
+ // iframe.
+ assert_true(!!msgs[1]["normal"] &&
+ !!msgs[1]["normal_control"] &&
+ !msgs[1]["credentialless"] &&
+ !msgs[1]["credentialless_control"],
+ 'The "normal_control" iframe\'s sharedworker should return ' +
+ '{"normal": true, "normal_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[1]));
+
+ // The "credentialless" iframe sharedworker belongs to the "credentialless" and the
+ // "credentialless_control" iframes.
+ assert_true(!msgs[2]["normal"] &&
+ !msgs[2]["normal_control"] &&
+ !!msgs[2]["credentialless"] &&
+ !!msgs[2]["credentialless_control"],
+ 'The "credentialless" iframe\'s sharedworker should return ' +
+ '{"credentialless": true, "credentialless_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[2]));
+
+ // The "credentialless_control" iframe shares the same sharedworker as
+ // the "credentialless" iframe.
+ assert_true(!msgs[3]["normal"] &&
+ !msgs[3]["normal_control"] &&
+ !!msgs[3]["credentialless"] &&
+ !!msgs[3]["credentialless_control"],
+ 'The "credentialless_control" iframe\'s sharedworker should return ' +
+ '{"credentialless": true, "credentialless_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[3]));
+
+}, "credentialless iframes get partitioned shared workers.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js
new file mode 100644
index 0000000000..fbee4ad28e
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js
@@ -0,0 +1,75 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// A script acquiring a lock. It can be released using window.releaseLocks
+const acquire_script = (key, response) => `
+ window.releaseLocks ||= [];
+ navigator.locks.request("${key}", async lock => {
+ send("${response}", "locked")
+ await new Promise(r => releaseLocks.push(r));
+ send("${response}", "unlocked");
+ });
+`;
+
+const release_script = (response) => `
+ for (release of releaseLocks)
+ release();
+`;
+
+// Assert that |context| holds |expected_keys|.
+const assertHeldKeys = async (context, expected_keys) => {
+ const queue = token();
+ send(context, `
+ const list = await navigator.locks.query();
+ send("${queue}", JSON.stringify(list));
+ `);
+ const state = JSON.parse(await receive(queue));
+ const held = state.held.map(x => x.name);
+ assert_equals(held.length, expected_keys.length);
+ assert_array_equals(held.sort(), expected_keys.sort());
+}
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 2 actors: A credentialless iframe and a normal one.
+ const iframe_credentialless = newIframeCredentialless(origin);
+ const iframe_normal = newIframe(origin);
+ const response_queue_1 = token();
+ const response_queue_2 = token();
+
+ // 1. Hold two different locks on both sides.
+ send(iframe_credentialless, acquire_script(key_1, response_queue_1));
+ send(iframe_normal, acquire_script(key_2, response_queue_2));
+ assert_equals(await receive(response_queue_1), "locked");
+ assert_equals(await receive(response_queue_2), "locked");
+ await assertHeldKeys(iframe_credentialless, [key_1]);
+ await assertHeldKeys(iframe_normal, [key_2]);
+
+ // 2. Try to acquire the lock with the same key on the opposite side. It
+ // shouldn't block, because they are partitioned.
+ send(iframe_credentialless , acquire_script(key_2, response_queue_1));
+ send(iframe_normal, acquire_script(key_1, response_queue_2));
+ assert_equals(await receive(response_queue_1), "locked");
+ assert_equals(await receive(response_queue_2), "locked");
+ await assertHeldKeys(iframe_credentialless, [key_1, key_2]);
+ await assertHeldKeys(iframe_normal, [key_1, key_2]);
+
+ // 3. Cleanup: release the 4 locks (2 on each sides).
+ send(iframe_credentialless, release_script(response_queue_1));
+ assert_equals(await receive(response_queue_1), "unlocked");
+ assert_equals(await receive(response_queue_1), "unlocked");
+ await assertHeldKeys(iframe_credentialless, []);
+ await assertHeldKeys(iframe_normal, [key_1, key_2]);
+
+ send(iframe_normal, release_script(response_queue_2));
+ assert_equals(await receive(response_queue_2), "unlocked");
+ assert_equals(await receive(response_queue_2), "unlocked");
+ await assertHeldKeys(iframe_credentialless, []);
+ await assertHeldKeys(iframe_normal, []);
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js
new file mode 100644
index 0000000000..8c25306baf
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js
@@ -0,0 +1,70 @@
+// META: timeout=long
+// META: variant=?worker=dedicated_worker
+// META: variant=?worker=shared_worker
+// META: variant=?worker=service_worker
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// Execute the same set of tests for every type of worker.
+// - DedicatedWorkers
+// - SharedWorkers
+// - ServiceWorkers.
+const params = new URLSearchParams(document.location.search);
+const worker_param = params.get("worker") || "dedicated_worker";
+
+const cookie_key = token();
+const cookie_value = "cookie_value";
+const cookie_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+// Create worker spawned from `context` and return its uuid.
+const workerFrom = context => {
+ const reply = token();
+ send(context, `
+ for(deps of [
+ "/common/utils.js",
+ "/resources/testharness.js",
+ "/html/cross-origin-embedder-policy/credentialless/resources/common.js",
+ ]) {
+ await new Promise(resolve => {
+ const script = document.createElement("script");
+ script.src = deps;
+ script.onload = resolve;
+ document.body.appendChild(script);
+ });
+ }
+
+ const worker_constructor = environments["${worker_param}"];
+ const headers = "";
+ const [worker, error] = worker_constructor(headers);
+ send("${reply}", worker);
+ `);
+ return receive(reply);
+};
+
+// Set a cookie from a top-level document.
+promise_test(async test => {
+ await setCookie(cookie_origin, cookie_key, cookie_value);
+}, "set cookies");
+
+// Control: iframe is not credentialless. The worker can access cookies.
+promise_test(async test => {
+ const headers = token();
+ send(await workerFrom(newIframe(cookie_origin)), `
+ fetch("${showRequestHeaders(cookie_origin, headers)}");
+ `);
+ const cookie = parseCookies(JSON.parse(await receive(headers)));
+ assert_equals(cookie[cookie_key], cookie_value)
+}, "Worker spawned from normal iframe can access global cookies");
+
+// Experiment: iframe is credentialless.
+promise_test(async test => {
+ const headers = token();
+ send(await workerFrom(newIframeCredentialless(cookie_origin)), `
+ fetch("${showRequestHeaders(cookie_origin, headers)}");
+ `);
+ const cookie = parseCookies(JSON.parse(await receive(headers)));
+ assert_equals(cookie[cookie_key], undefined)
+}, "Worker spawned from credentialless iframe can't access global cookies");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/README.md b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/README.md
new file mode 100644
index 0000000000..afda8f6c8a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/README.md
@@ -0,0 +1,60 @@
+# How to write back-forward cache tests
+
+In the back-forward cache tests, the main test HTML usually:
+
+1. Opens new executor Windows using `window.open()` + `noopener` option,
+ because less isolated Windows (e.g. iframes and `window.open()` without
+ `noopener` option) are often not eligible for back-forward cache (e.g.
+ in Chromium).
+2. Injects scripts to the executor Windows and receives the results via
+ `RemoteContext.execute_script()` by
+ [/common/dispatcher](../../../../common/dispatcher/README.md).
+ Follow the semantics and guideline described there.
+
+Back-forward cache specific helpers are in:
+
+- [resources/executor.html](resources/executor.html):
+ The BFCache-specific executor and contains helpers for executors.
+- [resources/helper.sub.js](resources/helper.sub.js):
+ Helpers for main test HTMLs.
+
+We must ensure that injected scripts are evaluated only after page load
+(more precisely, the first `pageshow` event) and not during navigation,
+to prevent unexpected interference between injected scripts, in-flight fetch
+requests behind `RemoteContext.execute_script()`, navigation and back-forward
+cache. To ensure this,
+
+- Call `await remoteContext.execute_script(waitForPageShow)` before any
+ other scripts are injected to the remote context, and
+- Call `prepareNavigation(callback)` synchronously from the script injected
+ by `RemoteContext.execute_script()`, and trigger navigation on or after the
+ callback is called.
+
+In typical A-B-A scenarios (where we navigate from Page A to Page B and then
+navigate back to Page A, assuming Page A is (or isn't) in BFCache),
+
+- Call `prepareNavigation()` on the executor, and then navigate to B, and then
+ navigate back to Page A.
+- Call `assert_bfcached()` or `assert_not_bfcached()` on the main test HTML, to
+ check the BFCache status. This is important to do to ensure the test would
+ not fail normally and instead result in `PRECONDITION_FAILED` if the page is
+ unexpectedly bfcached/not bfcached.
+- Check other test expectations on the main test HTML,
+
+as in [events.html](./events.html) and `runEventTest()` in
+[resources/helper.sub.js](resources/helper.sub.js).
+
+# Asserting PRECONDITION_FAILED for unexpected BFCache eligibility
+
+Browsers are not actually obliged to put pages in BFCache after navigations, so
+BFCache WPTs shouldn't result in `FAILED` if it expects a certain case to be
+supported by BFCache. But, it is still useful to test those cases in the
+browsers that do support BFCache for that case.
+
+To distinguish genuine failures from just not using BFCache, we use
+`assert_bfcached()` and `assert_not_bfcached()` which result in
+`PRECONDITION_FAILED` rather than `FAILED`. that should be put in the
+expectations for the failing tests (instead of marking it as `FAILED` or
+skipping the test). This means if the test starts passing (e.g. if we start
+BFCaching in the case being tested), we will notice that the output changed from
+`PRECONDITION_FAILED` to `PASS`.
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/broadcast-channel.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/broadcast-channel.html
new file mode 100644
index 0000000000..bc04a5ed7f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/broadcast-channel.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../resources/helper.sub.js"></script>
+<script>
+// Check whether the page is BFCached when there are open BroadcastChannels.
+// See https://github.com/whatwg/html/issues/7219 for other related scenarios.
+runEventTest(
+ {funcBeforeNavigation: () => {
+ window.bc = new BroadcastChannel('foo');
+ }},
+ 'Eligibility (BroadcastChannel)');
+
+// Same as above, but the BroadcastChannels are closed in the pagehide event.
+runEventTest(
+ {funcBeforeNavigation: () => {
+ window.bc = new BroadcastChannel('foo');
+ window.addEventListener('pagehide', () => window.bc.close());
+ }},
+ 'Eligibility (BroadcastChannel closed in the pagehide event)');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/dedicated-worker.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/dedicated-worker.html
new file mode 100644
index 0000000000..b08588a8bd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/dedicated-worker.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../resources/helper.sub.js"></script>
+<script>
+// Check whether the page is BFCached when there are dedicated workers that are
+// already loaded.
+runBfcacheTest({
+ funcBeforeNavigation: async () => {
+ globalThis.worker = new Worker('../resources/echo-worker.js');
+ // Make sure the worker starts before navigation.
+ await WorkerHelper.pingWorker(globalThis.worker);
+ },
+ funcAfterAssertion: async (pageA) => {
+ // Confirm that the worker is still there.
+ assert_equals(
+ await pageA.execute_script(() => WorkerHelper.pingWorker(globalThis.worker)),
+ 'PASS',
+ 'Worker should still work after restored from BFCache');
+ }
+}, 'Eligibility: dedicated workers');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-1.html
new file mode 100644
index 0000000000..6a48d0657b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-1.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../resources/helper.sub.js"></script>
+<script src="../resources/inflight-fetch-helper.js"></script>
+<script>
+// Check whether the page is BFCached when there are in-flight network requests
+// at the time of navigation.
+
+// Successful fetch completion with header received before BFCached.
+runTest(sameOriginUrl + '?delayBeforeBody=2000', false, true,
+ 'Header received before BFCache and body received when in BFCache');
+runTest(sameOriginUrl + '?delayBeforeBody=3500', false, true,
+ 'Header received before BFCache and body received after BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-2.html
new file mode 100644
index 0000000000..a767c4fa83
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-2.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../resources/helper.sub.js"></script>
+<script src="../resources/inflight-fetch-helper.js"></script>
+<script>
+// Check whether the page is BFCached when there are in-flight network requests
+// at the time of navigation.
+
+// Successful fetch completion with header received when in BFCache or after
+// BFCache.
+runTest(sameOriginUrl + '?delayBeforeHeader=2000', false, true,
+ 'Header and body received when in BFCache');
+runTest(sameOriginUrl + '?delayBeforeHeader=2000&delayBeforeBody=1500',
+ false, true,
+ 'Header received when in BFCache and body received after BFCache');
+runTest(sameOriginUrl + '?delayBeforeHeader=3500', false, true,
+ 'Header and body received after BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-cors.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-cors.html
new file mode 100644
index 0000000000..c04089a5e2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-cors.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../resources/helper.sub.js"></script>
+<script src="../resources/inflight-fetch-helper.js"></script>
+<script>
+// Check whether the page is BFCached when there are in-flight network requests
+// at the time of navigation.
+
+// CORS and failing fetch.
+runTest(crossSiteUrl + '?delayBeforeHeader=2000&cors=yes', false, true,
+ 'CORS succeeded when in BFCache');
+runTest(crossSiteUrl + '?delayBeforeHeader=2000', false, false,
+ 'CORS failed when in BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-redirects.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-redirects.html
new file mode 100644
index 0000000000..b0b49d5f12
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/inflight-fetch-redirects.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../resources/helper.sub.js"></script>
+<script src="../resources/inflight-fetch-helper.js"></script>
+<script>
+// Check whether the page is BFCached when there are in-flight network requests
+// at the time of navigation.
+
+// Redirects and CSP.
+runTest(
+ '/common/slow-redirect.py?delay=2&location=' +
+ encodeURIComponent(sameOriginUrl),
+ false, true,
+ 'Redirect header received when in BFCache');
+runTest(
+ '/common/slow-redirect.py?delay=2&location=' +
+ encodeURIComponent(sameOriginUrl),
+ true, true,
+ 'Redirect header received when in BFCache w/ CSP passing');
+runTest(
+ '/common/slow-redirect.py?delay=2&location=' +
+ encodeURIComponent(crossSiteUrl + '?cors=yes'),
+ false, true,
+ 'Cross-origin redirect header received when in BFCache');
+runTest(
+ '/common/slow-redirect.py?delay=2&location=' +
+ encodeURIComponent(crossSiteUrl + '?cors=yes'),
+ true, false,
+ 'Cross-origin redirect header received when in BFCache w/ CSP failing');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/shared-worker.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/shared-worker.html
new file mode 100644
index 0000000000..77139fd08a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/eligibility/shared-worker.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../resources/helper.sub.js"></script>
+<script>
+// Check whether the page is BFCached when there are shared workers that are
+// already loaded.
+runBfcacheTest({
+ funcBeforeNavigation: async () => {
+ globalThis.worker = new SharedWorker('../resources/echo-worker.js');
+ // Make sure the worker starts before navigation.
+ await WorkerHelper.pingWorker(globalThis.worker);
+ },
+ funcAfterAssertion: async (pageA) => {
+ // Confirm that the worker is still there.
+ assert_equals(
+ await pageA.execute_script(() => WorkerHelper.pingWorker(globalThis.worker)),
+ 'PASS',
+ 'SharedWorker should still work after restored from BFCache');
+ }
+}, 'Eligibility: shared workers');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/events.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/events.html
new file mode 100644
index 0000000000..4b1d3e408e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/events.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script>
+// Basic event tests.
+runEventTest(
+ {targetOrigin: originSameOrigin},
+ 'SameOrigin');
+
+runEventTest(
+ {targetOrigin: originSameSite},
+ 'SameSite');
+
+runEventTest(
+ {},
+ 'CrossSite');
+
+// beforeunload.
+runEventTest({
+ events: ['pagehide', 'pageshow', 'load', 'beforeunload'],
+ expectedEvents: [
+ 'window.load',
+ 'window.pageshow',
+ 'window.beforeunload',
+ 'window.pagehide.persisted',
+ 'window.pageshow.persisted'
+ ]},
+ 'beforeunload');
+
+// unload.
+runEventTest({
+ events: ['pagehide', 'pageshow', 'load', 'unload'],
+ expectedEvents: [
+ 'window.load',
+ 'window.pageshow',
+ 'window.pagehide.persisted',
+ 'window.pageshow.persisted'
+ ]},
+ 'unload');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/focus.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/focus.html
new file mode 100644
index 0000000000..3901a5417d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/focus.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<meta name="timeout" content="long">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focused-area-of-the-document">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script>
+// Focus should remain the same and thus blur/focus events shouldn't be fired
+// when page gets into and out of BFCache, as explicitly noted in the spec:
+// https://html.spec.whatwg.org/multipage/interaction.html#focused-area-of-the-document
+// "Even if a document is not fully active and not shown to the user, it can still
+// have a focused area of the document. If a document's fully active state changes,
+// its focused area of the document will stay the same."
+runBfcacheTest({
+ openFunc: (url) => window.open(url + '&events=pagehide,pageshow,load',
+ '_blank', 'noopener'),
+ funcBeforeNavigation: () => {
+ // Create and focus on an <input> before navigation.
+ // Focus/blur events on the <input> are recorded.
+ const textInput = document.createElement('input');
+ textInput.setAttribute('type', 'text');
+ textInput.setAttribute('id', 'toBeFocused');
+ textInput.onfocus = () => {
+ recordEvent('input.focus');
+ };
+ textInput.onblur = () => {
+ recordEvent('input.blur');
+ };
+ document.body.appendChild(textInput);
+ textInput.focus();
+ window.activeElementBeforePageHide = document.activeElement;
+ window.addEventListener('pagehide', () => {
+ window.activeElementOnPageHide = document.activeElement;
+ });
+ },
+ funcAfterAssertion: async (pageA) => {
+ assert_true(
+ await pageA.execute_script(() => {
+ return window.activeElementBeforePageHide ===
+ document.querySelector('#toBeFocused');
+ }),
+ 'activeElement before pagehide');
+
+ assert_true(
+ await pageA.execute_script(() => {
+ return window.activeElementOnPageHide ===
+ document.querySelector('#toBeFocused');
+ }),
+ 'activeElement on pagehide');
+
+ assert_true(
+ await pageA.execute_script(() => {
+ return document.activeElement ===
+ document.querySelector('#toBeFocused');
+ }),
+ 'activeElement after navigation');
+
+ assert_array_equals(
+ await pageA.execute_script(() => getRecordedEvents()),
+ [
+ 'window.load',
+ 'window.pageshow',
+ 'input.focus',
+ 'window.pagehide.persisted',
+ 'window.pageshow.persisted'
+ ],
+ 'blur/focus events should not be fired ' +
+ 'when page gets into and out of BFCache');
+ }
+}, 'Focus should be kept when page gets into and out of BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/pushstate.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/pushstate.https.html
new file mode 100644
index 0000000000..218562254a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/pushstate.https.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script>
+// Tests what happens when doing history navigation to an entry that's created
+// via pushState, with and without BFCache:
+// 1. Navigate to `urlA`.
+// 2. `pushState(urlPushState)`.
+// 3. Navigate to `urlB`.
+// 4. Do a back navigation.
+// With BFCache, the page loaded at Step 1 is restored from BFCache,
+// and is not reloaded from `urlPushState` nor `urlA`.
+// Without BFCache, a page is loaded from `urlPushState`, not from `urlA`.
+// In both cases, `location` and `history.state` are set to those set by
+// `pushState()` in Step 2.
+// See https://github.com/whatwg/html/issues/6207 for more discussion on the
+// specified, implemented and desired behaviors. While this test contradicts
+// the current spec but matches the desired behavior, and the spec will be
+// fixed as part of https://github.com/whatwg/html/pull/6315.
+for (const bfcacheDisabled of [false, true]) {
+ const pushStateExecutorPath =
+ '/html/browsers/browsing-the-web/back-forward-cache/resources/executor-pushstate.html';
+
+ runBfcacheTest({
+ funcBeforeNavigation: async (bfcacheDisabled, pushStateExecutorPath) => {
+ const urlPushState = new URL(location.href);
+ urlPushState.pathname = pushStateExecutorPath;
+ if (bfcacheDisabled) {
+ await disableBFCache();
+ }
+
+ // `pushState(..., urlPushState)` on `urlA`,
+ history.pushState('blue', '', urlPushState.href);
+ },
+ argsBeforeNavigation: [bfcacheDisabled, pushStateExecutorPath],
+ shouldBeCached: !bfcacheDisabled,
+ funcAfterAssertion: async (pageA) => {
+ // We've navigated to `urlB` and back again
+ // (already done within `runBfcacheTest()`).
+ // After the back navigation, `location` etc. should point to
+ // `urlPushState` and the state that's pushed.
+ const urlPushState = location.origin + pushStateExecutorPath +
+ '?uuid=' + pageA.context_id;
+ assert_equals(await pageA.execute_script(() => location.href),
+ urlPushState, 'url');
+ assert_equals(await pageA.execute_script(() => history.state),
+ 'blue', 'history.state');
+
+ if (bfcacheDisabled) {
+ // When the page is not restored from BFCache, the HTML page is loaded
+ // from `urlPushState` (not from `urlA`).
+ assert_true(await pageA.execute_script(() => isLoadedFromPushState),
+ 'document should be loaded from urlPushState');
+ }
+ }
+ }, 'back navigation to pushState()d page (' +
+ (bfcacheDisabled ? 'not ' : '') + 'in BFCache)');
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/echo-worker.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/echo-worker.js
new file mode 100644
index 0000000000..3e3ecb52e9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/echo-worker.js
@@ -0,0 +1,16 @@
+// On receiving a message from the parent Document, send back a message to the
+// parent Document. This is used to wait for worker initialization and test
+// that this worker is alive and working.
+
+// For dedicated workers.
+self.addEventListener('message', event => {
+ postMessage(event.data);
+});
+
+// For shared workers.
+onconnect = e => {
+ const port = e.ports[0];
+ port.onmessage = event => {
+ port.postMessage(event.data);
+ }
+};
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/event-recorder.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/event-recorder.js
new file mode 100644
index 0000000000..469286a399
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/event-recorder.js
@@ -0,0 +1,54 @@
+// Recording events
+
+const params = new URLSearchParams(window.location.search);
+const uuid = params.get('uuid');
+
+// The recorded events are stored in localStorage rather than global variables
+// to catch events fired just before navigating out.
+function getPushedItems(key) {
+ return JSON.parse(localStorage.getItem(key) || '[]');
+}
+
+function pushItem(key, value) {
+ const array = getPushedItems(key);
+ array.push(value);
+ localStorage.setItem(key, JSON.stringify(array));
+}
+
+window.recordEvent = function(eventName) {
+ pushItem(uuid + '.observedEvents', eventName);
+}
+
+window.getRecordedEvents = function() {
+ return getPushedItems(uuid + '.observedEvents');
+}
+
+// Records events fired on `window` and `document`, with names listed in
+// `eventNames`.
+function startRecordingEvents(eventNames) {
+ for (const eventName of eventNames) {
+ window.addEventListener(eventName, event => {
+ let result = eventName;
+ if (event.persisted) {
+ result += '.persisted';
+ }
+ if (eventName === 'visibilitychange') {
+ result += '.' + document.visibilityState;
+ }
+ recordEvent('window.' + result);
+ });
+ document.addEventListener(eventName, () => {
+ let result = eventName;
+ if (eventName === 'visibilitychange') {
+ result += '.' + document.visibilityState;
+ }
+ recordEvent('document.' + result);
+ });
+ }
+}
+
+// When a comma-separated list of event names are given as the `events`
+// parameter in the URL, start record the events of the given names.
+if (params.get('events')) {
+ startRecordingEvents(params.get('events').split(','));
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor-pushstate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor-pushstate.html
new file mode 100644
index 0000000000..dcf4a798d0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor-pushstate.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="event-recorder.js" type="module"></script>
+<script src="worker-helper.js" type="module"></script>
+<script type="module">
+// This is mostly the same as `executor.html`, except for
+// `isLoadedFromPushState` is set here, in order to detect whether the page
+// was loaded from `executor.html` or `executor-pushstate.html`.
+// Full executor functionality is still needed to handle remote script
+// execution requests etc.
+window.isLoadedFromPushState = true;
+</script>
+<script src="executor.js" type="module"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.html
new file mode 100644
index 0000000000..2d118bbe2b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="event-recorder.js" type="module"></script>
+<script src="worker-helper.js" type="module"></script>
+<script src="executor.js" type="module"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.js
new file mode 100644
index 0000000000..67ce068130
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/executor.js
@@ -0,0 +1,64 @@
+const params = new URLSearchParams(window.location.search);
+const uuid = params.get('uuid');
+
+// Executor and BFCache detection
+
+// When navigating out from this page, always call
+// `prepareNavigation(callback)` synchronously from the script injected by
+// `RemoteContext.execute_script()`, and trigger navigation on or after the
+// callback is called.
+// prepareNavigation() suspends task polling and avoid in-flight fetch
+// requests during navigation that might evict the page from BFCache.
+//
+// When we navigate to the page again, task polling is resumed, either
+// - (BFCache cases) when the pageshow event listener added by
+// prepareNavigation() is executed, or
+// - (Non-BFCache cases) when `Executor.execute()` is called again during
+// non-BFCache page loading.
+//
+// In such scenarios, `assert_bfcached()` etc. in `helper.sub.js` can determine
+// whether the page is restored from BFCache or not, by observing
+// - `isPageshowFired`: whether the pageshow event listener added by the
+// prepareNavigation() before navigating out, and
+// - `loadCount`: whether this inline script is evaluated again.
+// - `isPageshowPersisted` is used to assert that `event.persisted` is true
+// when restored from BFCache.
+
+window.isPageshowFired = false;
+window.isPageshowPersisted = null;
+window.loadCount = parseInt(localStorage.getItem(uuid + '.loadCount') || '0') + 1;
+localStorage.setItem(uuid + '.loadCount', loadCount);
+
+window.pageShowPromise = new Promise(resolve =>
+ window.addEventListener('pageshow', resolve, {once: true}));
+
+const executor = new Executor(uuid);
+
+window.prepareNavigation = function(callback) {
+ window.addEventListener(
+ 'pageshow',
+ (event) => {
+ window.isPageshowFired = true;
+ window.isPageshowPersisted = event.persisted;
+ executor.resume();
+ },
+ {once: true});
+ executor.suspend(callback);
+}
+
+// Try to disable BFCache by acquiring and never releasing a Web Lock.
+// This requires HTTPS.
+// Note: This is a workaround depending on non-specified WebLock+BFCache
+// behavior, and doesn't work on Safari. We might want to introduce a
+// test-only BFCache-disabling API instead in the future.
+// https://github.com/web-platform-tests/wpt/issues/16359#issuecomment-795004780
+// https://crbug.com/1298336
+window.disableBFCache = () => {
+ return new Promise(resolve => {
+ // Use page's UUID as a unique lock name.
+ navigator.locks.request(uuid, () => {
+ resolve();
+ return new Promise(() => {});
+ });
+ });
+};
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js
new file mode 100644
index 0000000000..a1d18d108e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js
@@ -0,0 +1,229 @@
+// Helpers called on the main test HTMLs.
+// Functions in `RemoteContext.execute_script()`'s 1st argument are evaluated
+// on the executors (`executor.html`), and helpers available on the executors
+// are defined in `executor.html`.
+
+const originSameOrigin =
+ location.protocol === 'http:' ?
+ 'http://{{host}}:{{ports[http][0]}}' :
+ 'https://{{host}}:{{ports[https][0]}}';
+const originSameSite =
+ location.protocol === 'http:' ?
+ 'http://{{host}}:{{ports[http][1]}}' :
+ 'https://{{host}}:{{ports[https][1]}}';
+const originCrossSite =
+ location.protocol === 'http:' ?
+ 'http://{{hosts[alt][www]}}:{{ports[http][0]}}' :
+ 'https://{{hosts[alt][www]}}:{{ports[https][0]}}';
+
+const executorPath =
+ '/html/browsers/browsing-the-web/back-forward-cache/resources/executor.html?uuid=';
+
+// Asserts that the executor `target` is (or isn't, respectively)
+// restored from BFCache. These should be used in the following fashion:
+// 1. Call prepareNavigation() on the executor `target`.
+// 2. Navigate the executor to another page.
+// 3. Navigate back to the executor `target`.
+// 4. Call assert_bfcached() or assert_not_bfcached() on the main test HTML.
+async function assert_bfcached(target) {
+ const status = await getBFCachedStatus(target);
+ assert_implements_optional(status === 'BFCached',
+ "Could have been BFCached but actually wasn't");
+}
+
+async function assert_not_bfcached(target) {
+ const status = await getBFCachedStatus(target);
+ assert_implements(status !== 'BFCached',
+ 'Should not be BFCached but actually was');
+}
+
+async function getBFCachedStatus(target) {
+ const [loadCount, isPageshowFired, isPageshowPersisted] =
+ await target.execute_script(() => [
+ window.loadCount, window.isPageshowFired, window.isPageshowPersisted]);
+
+ if (loadCount === 1 && isPageshowFired === true &&
+ isPageshowPersisted === true) {
+ return 'BFCached';
+ } else if (loadCount === 2 && isPageshowFired === false) {
+ return 'Not BFCached';
+ } else {
+ // This can occur for example when this is called before first navigating
+ // away (loadCount = 1, isPageshowFired = false), e.g. when
+ // 1. sending a script for navigation and then
+ // 2. calling getBFCachedStatus() without waiting for the completion of
+ // the script on the `target` page.
+ assert_unreached(
+ `Got unexpected BFCache status: loadCount = ${loadCount}, ` +
+ `isPageshowFired = ${isPageshowFired}, ` +
+ `isPageshowPersisted = ${isPageshowPersisted}`);
+ }
+}
+
+// Always call `await remoteContext.execute_script(waitForPageShow);` after
+// triggering to navigation to the page, to wait for pageshow event on the
+// remote context.
+const waitForPageShow = () => window.pageShowPromise;
+
+// Run a test that navigates A->B->A:
+// 1. Page A is opened by `params.openFunc(url)`.
+// 2. `params.funcBeforeNavigation(params.argsBeforeNavigation)` is executed
+// on page A.
+// 3. The window is navigated to page B on `params.targetOrigin`.
+// 4. The window is back navigated to page A (expecting BFCached).
+//
+// Events `params.events` (an array of strings) are observed on page A and
+// `params.expectedEvents` (an array of strings) is expected to be recorded.
+// See `event-recorder.js` for event recording.
+//
+// Parameters can be omitted. See `defaultParams` below for default.
+function runEventTest(params, description) {
+ const defaultParams = {
+ openFunc(url) {
+ window.open(
+ `${url}&events=${this.events.join(',')}`,
+ '_blank',
+ 'noopener'
+ )
+ },
+ events: ['pagehide', 'pageshow', 'load'],
+ expectedEvents: [
+ 'window.load',
+ 'window.pageshow',
+ 'window.pagehide.persisted',
+ 'window.pageshow.persisted'
+ ],
+ async funcAfterAssertion(pageA) {
+ assert_array_equals(
+ await pageA.execute_script(() => getRecordedEvents()),
+ this.expectedEvents);
+ }
+ }
+ // Apply defaults.
+ params = { ...defaultParams, ...params };
+
+ runBfcacheTest(params, description);
+}
+
+async function navigateAndThenBack(pageA, pageB, urlB,
+ funcBeforeBackNavigation,
+ argsBeforeBackNavigation) {
+ await pageA.execute_script(
+ (url) => {
+ prepareNavigation(() => {
+ location.href = url;
+ });
+ },
+ [urlB]
+ );
+
+ await pageB.execute_script(waitForPageShow);
+ if (funcBeforeBackNavigation) {
+ await pageB.execute_script(funcBeforeBackNavigation,
+ argsBeforeBackNavigation);
+ }
+ await pageB.execute_script(
+ () => {
+ prepareNavigation(() => { history.back(); });
+ }
+ );
+
+ await pageA.execute_script(waitForPageShow);
+}
+
+function runBfcacheTest(params, description) {
+ const defaultParams = {
+ openFunc: url => window.open(url, '_blank', 'noopener'),
+ scripts: [],
+ funcBeforeNavigation: () => {},
+ argsBeforeNavigation: [],
+ targetOrigin: originCrossSite,
+ funcBeforeBackNavigation: () => {},
+ argsBeforeBackNavigation: [],
+ shouldBeCached: true,
+ funcAfterAssertion: () => {},
+ }
+ // Apply defaults.
+ params = {...defaultParams, ...params };
+
+ promise_test(async t => {
+ const pageA = new RemoteContext(token());
+ const pageB = new RemoteContext(token());
+
+ const urlA = executorPath + pageA.context_id;
+ const urlB = params.targetOrigin + executorPath + pageB.context_id;
+
+ // So that tests can refer to these URLs for assertions if necessary.
+ pageA.url = originSameOrigin + urlA;
+ pageB.url = urlB;
+
+ params.openFunc(urlA);
+
+ await pageA.execute_script(waitForPageShow);
+
+ for (const src of params.scripts) {
+ await pageA.execute_script((src) => {
+ const script = document.createElement("script");
+ script.src = src;
+ document.head.append(script);
+ return new Promise(resolve => script.onload = resolve);
+ }, [src]);
+ }
+
+ await pageA.execute_script(params.funcBeforeNavigation,
+ params.argsBeforeNavigation);
+ await navigateAndThenBack(pageA, pageB, urlB,
+ params.funcBeforeBackNavigation,
+ params.argsBeforeBackNavigation);
+
+ if (params.shouldBeCached) {
+ await assert_bfcached(pageA);
+ } else {
+ await assert_not_bfcached(pageA);
+ }
+
+ if (params.funcAfterAssertion) {
+ await params.funcAfterAssertion(pageA, pageB, t);
+ }
+ }, description);
+}
+
+// Call clients.claim() on the service worker
+async function claim(t, worker) {
+ const channel = new MessageChannel();
+ const saw_message = new Promise(function(resolve) {
+ channel.port1.onmessage = t.step_func(function(e) {
+ assert_equals(e.data, 'PASS', 'Worker call to claim() should fulfill.');
+ resolve();
+ });
+ });
+ worker.postMessage({type: "claim", port: channel.port2}, [channel.port2]);
+ await saw_message;
+}
+
+// Assigns the current client to a local variable on the service worker.
+async function storeClients(t, worker) {
+ const channel = new MessageChannel();
+ const saw_message = new Promise(function(resolve) {
+ channel.port1.onmessage = t.step_func(function(e) {
+ assert_equals(e.data, 'PASS', 'storeClients');
+ resolve();
+ });
+ });
+ worker.postMessage({type: "storeClients", port: channel.port2}, [channel.port2]);
+ await saw_message;
+}
+
+// Call storedClients.postMessage("") on the service worker
+async function postMessageToStoredClients(t, worker) {
+ const channel = new MessageChannel();
+ const saw_message = new Promise(function(resolve) {
+ channel.port1.onmessage = t.step_func(function(e) {
+ assert_equals(e.data, 'PASS', 'postMessageToStoredClients');
+ resolve();
+ });
+ });
+ worker.postMessage({type: "postMessageToStoredClients",
+ port: channel.port2}, [channel.port2]);
+ await saw_message;
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/inflight-fetch-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/inflight-fetch-helper.js
new file mode 100644
index 0000000000..7832003b76
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/inflight-fetch-helper.js
@@ -0,0 +1,47 @@
+// Delay after fetch start:
+// - 0.0 seconds: before BFCache
+// - 2.0 seconds: when in BFCache
+// - 3.5 seconds: after restored from BFCache
+function runTest(urlToFetch, hasCSP, shouldSucceed, description) {
+ runBfcacheTest({
+ funcBeforeNavigation: async (urlToFetch, hasCSP) => {
+ if (hasCSP) {
+ // Set CSP.
+ const meta = document.createElement('meta');
+ meta.setAttribute('http-equiv', 'Content-Security-Policy');
+ meta.setAttribute('content', "connect-src 'self'");
+ document.head.appendChild(meta);
+ }
+
+ // Initiate a `fetch()`.
+ window.fetchPromise = fetch(urlToFetch);
+
+ // Wait for 0.5 seconds to receive response headers for the fetch()
+ // before BFCache, if any.
+ await new Promise(resolve => setTimeout(resolve, 500));
+ },
+ argsBeforeNavigation: [urlToFetch, hasCSP],
+ funcBeforeBackNavigation: () => {
+ // Wait for 2 seconds before back navigating to pageA.
+ return new Promise(resolve => setTimeout(resolve, 2000));
+ },
+ funcAfterAssertion: async (pageA, pageB, t) => {
+ // Wait for fetch() completion and check the result.
+ const result = pageA.execute_script(
+ () => window.fetchPromise.then(r => r.text()));
+ if (shouldSucceed) {
+ assert_equals(
+ await result,
+ 'Body',
+ 'Fetch should complete successfully after restored from BFCache');
+ } else {
+ await promise_rejects_js(t, TypeError, result,
+ 'Fetch should fail after restored from BFCache');
+ }
+ }
+ }, 'Eligibility (in-flight fetch): ' + description);
+}
+
+const url = new URL('../resources/slow.py', location);
+const sameOriginUrl = url.href;
+const crossSiteUrl = originCrossSite + url.pathname;
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js
new file mode 100644
index 0000000000..80c164f560
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js
@@ -0,0 +1,137 @@
+// A collection of helper functions that make use of the `remoteContextHelper`
+// to test BFCache support and behavior.
+
+// Call `prepareForBFCache()` before navigating away from the page. This simply
+// sets a variable in window.
+async function prepareForBFCache(remoteContextHelper) {
+ await remoteContextHelper.executeScript(() => {
+ window.beforeBFCache = true;
+ });
+}
+
+// Call `getBeforeCache()` after navigating back to the page. This returns the
+// value in window.
+async function getBeforeBFCache(remoteContextHelper) {
+ return await remoteContextHelper.executeScript(() => {
+ return window.beforeBFCache;
+ });
+}
+
+// If the value in window is set to true, this means that the page was reloaded,
+// i.e., the page was restored from BFCache.
+// Call `prepareForBFCache()` before navigating away to call this function.
+async function assertImplementsBFCacheOptional(remoteContextHelper) {
+ var beforeBFCache = await getBeforeBFCache(remoteContextHelper);
+ assert_implements_optional(beforeBFCache == true, 'BFCache not supported.');
+}
+
+// Subtracts set `b` from set `a` and returns the result.
+function setMinus(a, b) {
+ const minus = new Set();
+ a.forEach(e => {
+ if (!b.has(e)) {
+ minus.add(e);
+ }
+ });
+ return minus;
+}
+
+// Return a sorted Array from the iterable `s`.
+function sorted(s) {
+ return Array.from(s).sort();
+}
+
+// Assert expected reasons and the reported reasons match.
+function matchReasons(expectedNotRestoredReasonsSet, notRestoredReasonsSet) {
+ const missing = setMinus(
+ expectedNotRestoredReasonsSet, notRestoredReasonsSet, 'Missing reasons');
+ const extra = setMinus(
+ notRestoredReasonsSet, expectedNotRestoredReasonsSet, 'Extra reasons');
+ assert_true(missing.size + extra.size == 0, `Expected: ${sorted(expectedNotRestoredReasonsSet)}\n` +
+ `Got: ${sorted(notRestoredReasonsSet)}\n` +
+ `Missing: ${sorted(missing)}\n` +
+ `Extra: ${sorted(extra)}\n`);
+}
+
+// This function takes a set of reasons and extracts reasons out of it and returns a set of strings.
+// For example, if the input is [{"reason": "error-document"}, {"reason": "masked"}],
+// the output is ["error-document", "masked"].
+function extractReason(reasonSet) {
+ let reasonsExtracted = new Set();
+ for (let reason of reasonSet) {
+ reasonsExtracted.add(reason.reason);
+ }
+ return reasonsExtracted;
+}
+
+// A helper function to assert that the page is not restored from BFCache by
+// checking whether the `beforeBFCache` value from `window` is undefined
+// due to page reload.
+// This function also takes an optional `notRestoredReasons` list which
+// indicates the set of expected reasons that make the page not restored.
+// If the reasons list is undefined, the check will be skipped. Otherwise
+// this check will use the `notRestoredReasons` API, to obtain the reasons
+// in a tree structure, and flatten the reasons before making the order-
+// insensitive comparison.
+// If the API is not available, the function will terminate instead of marking
+// the assertion failed.
+// Call `prepareForBFCache()` before navigating away to call this function.
+async function assertNotRestoredFromBFCache(
+ remoteContextHelper, notRestoredReasons) {
+ var beforeBFCache = await getBeforeBFCache(remoteContextHelper);
+ assert_equals(beforeBFCache, undefined, 'document unexpectedly BFCached');
+
+ // The reason is optional, so skip the remaining test if the
+ // `notRestoredReasons` is not set.
+ if (notRestoredReasons === undefined) {
+ return;
+ }
+
+ let isFeatureEnabled = await remoteContextHelper.executeScript(() => {
+ return 'notRestoredReasons' in
+ performance.getEntriesByType('navigation')[0];
+ });
+
+ // Return if the `notRestoredReasons` API is not available.
+ if (!isFeatureEnabled) {
+ return;
+ }
+
+ let result = await remoteContextHelper.executeScript(() => {
+ return performance.getEntriesByType('navigation')[0].notRestoredReasons;
+ });
+
+ let expectedNotRestoredReasonsSet = new Set(notRestoredReasons);
+ let notRestoredReasonsSet = new Set();
+
+ // Flatten the reasons from the main frame and all the child frames.
+ const collectReason = (node) => {
+ for (let reason of node.reasons) {
+ notRestoredReasonsSet.add(reason.reason);
+ }
+ for (let child of node.children) {
+ collectReason(child);
+ }
+ };
+ collectReason(result);
+ matchReasons(expectedNotRestoredReasonsSet, notRestoredReasonsSet);
+}
+
+// A helper function that combines the steps of setting window property,
+// navigating away and back, and making assertion on whether BFCache is
+// supported.
+// This function can be used to check if the current page is eligible for
+// BFCache.
+async function assertBFCacheEligibility(
+ remoteContextHelper, shouldRestoreFromBFCache) {
+ await prepareForBFCache(remoteContextHelper);
+ // Navigate away and back.
+ const newRemoteContextHelper = await remoteContextHelper.navigateToNew();
+ await newRemoteContextHelper.historyBack();
+
+ if (shouldRestoreFromBFCache) {
+ await assertImplementsBFCacheOptional(remoteContextHelper);
+ } else {
+ await assertNotRestoredFromBFCache(remoteContextHelper);
+ }
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/service-worker.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/service-worker.js
new file mode 100644
index 0000000000..ab9a3239ea
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/service-worker.js
@@ -0,0 +1,58 @@
+self.addEventListener('message', function(event) {
+ if (event.data.type == "claim") {
+ self.clients.claim()
+ .then(function(result) {
+ if (result !== undefined) {
+ event.data.port.postMessage(
+ 'FAIL: claim() should be resolved with undefined');
+ return;
+ }
+ event.data.port.postMessage('PASS');
+ })
+ .catch(function(error) {
+ event.data.port.postMessage('FAIL: exception: ' + error.name);
+ });
+ } else if (event.data.type == "storeClients") {
+ self.clients.matchAll()
+ .then(function(result) {
+ self.storedClients = result;
+ event.data.port.postMessage("PASS");
+ });
+ } else if (event.data.type == "postMessageToStoredClients") {
+ for (let client of self.storedClients) {
+ client.postMessage("dummyValue");
+ }
+ event.data.port.postMessage("PASS");
+ } else if (event.data.type == 'storeMessagePort') {
+ let isCloseEventFired = false;
+ const port = event.ports[0];
+ port.start();
+ port.onmessage = (event) => {
+ if (event.data == 'Confirm the ports can communicate') {
+ port.postMessage('Receive message');
+ } else if (event.data == 'Ask if the close event was fired') {
+ port.postMessage(isCloseEventFired);
+ }
+ };
+ port.onclose = () => {
+ isCloseEventFired = true;
+ };
+ }
+ });
+
+self.addEventListener('fetch', e => {
+ if (e.request.url.match(/\/is-controlled/)) {
+ e.respondWith(new Response('controlled'));
+ }
+ else if (e.request.url.match(/\/get-clients-matchall/)) {
+ const options = { includeUncontrolled: true, type: 'all' };
+ e.respondWith(
+ self.clients.matchAll(options)
+ .then(clients => {
+ const client_urls = [];
+ clients.forEach(client => client_urls.push(client.url));
+ return new Response(JSON.stringify(client_urls));
+ })
+ );
+ }
+ });
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/slow.py b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/slow.py
new file mode 100644
index 0000000000..01bb3309b1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/slow.py
@@ -0,0 +1,13 @@
+import time
+
+def main(request, response):
+ delay_before_header = float(request.GET.first(b"delayBeforeHeader", 0)) / 1000
+ delay_before_body = float(request.GET.first(b"delayBeforeBody", 0)) / 1000
+
+ time.sleep(delay_before_header)
+ if b"cors" in request.GET:
+ response.headers.set(b"Access-Control-Allow-Origin", b"*")
+ response.write_status_headers()
+
+ time.sleep(delay_before_body)
+ response.writer.write_content(b"Body")
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/worker-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/worker-helper.js
new file mode 100644
index 0000000000..d5f3a0c814
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/worker-helper.js
@@ -0,0 +1,28 @@
+// Worker-related helper file to be used from executor.html.
+
+// The class `WorkerHelper` is exposed to `globalThis` because this should be
+// used via `eval()`.
+globalThis.WorkerHelper = class {
+ static pingWorker(worker) {
+ return new Promise((resolve, reject) => {
+ const message = 'message ' + Math.random();
+ const onmessage = e => {
+ if (e.data === message) {
+ resolve('PASS');
+ } else {
+ reject('pingWorker: expected ' + message + ' but got ' + e.data);
+ }
+ };
+ worker.onerror = reject;
+ if (worker instanceof Worker) {
+ worker.addEventListener('message', onmessage, {once: true});
+ worker.postMessage(message);
+ } else if (worker instanceof SharedWorker) {
+ worker.port.onmessage = onmessage;
+ worker.port.postMessage(message);
+ } else {
+ reject('Unexpected worker type');
+ }
+ });
+ }
+};
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-client-postmessage.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-client-postmessage.https.html
new file mode 100644
index 0000000000..acc682a073
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-client-postmessage.https.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+// When a service worker is unregistered when a controlled page is in BFCache,
+// the page can be still restored from BFCache and remain controlled by the
+// service worker.
+promise_test(async t => {
+ // Register a service worker and make this page controlled.
+ const workerUrl =
+ 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
+ const registration =
+ await service_worker_unregister_and_register(t, workerUrl, './');
+ t.add_cleanup(_ => registration.unregister());
+ await wait_for_state(t, registration.installing, 'activated');
+ const controllerChanged = new Promise(
+ resolve => navigator.serviceWorker.oncontrollerchange = resolve);
+ await claim(t, registration.active);
+ await controllerChanged;
+
+ const pageA = new RemoteContext(token());
+ const pageB = new RemoteContext(token());
+
+ const urlA = location.origin + executorPath + pageA.context_id;
+ const urlB = originCrossSite + executorPath + pageB.context_id;
+
+ // Open `urlA`.
+ window.open(urlA, '_blank', 'noopener');
+ await pageA.execute_script(waitForPageShow);
+
+ assert_true(
+ await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null)),
+ 'pageA should be controlled before navigation');
+
+ await storeClients(t, registration.active);
+
+ // Navigate to `urlB`.
+ await pageA.execute_script(
+ (url) => prepareNavigation(() => {
+ location.href = url;
+ }),
+ [urlB]);
+ await pageB.execute_script(waitForPageShow);
+
+ // Posting a message to a client should evict it from the bfcache.
+ await postMessageToStoredClients(t, registration.active);
+
+ // Back navigate and check whether the page is restored from BFCache.
+ await pageB.execute_script(
+ () => {
+ prepareNavigation(() => { history.back(); });
+ }
+ );
+ await pageA.execute_script(waitForPageShow);
+ await assert_not_bfcached(pageA);
+
+ await pageA.execute_script(() => navigator.serviceWorker.ready);
+
+ assert_true(
+ await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null)),
+ 'pageA should be controlled after history navigation');
+
+}, 'Client.postMessage while a controlled page is in BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-claim.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-claim.https.html
new file mode 100644
index 0000000000..d9540c221b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-claim.https.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+// Calling Clients.claim() on the service worker when a controlled page is in
+// BFCache should evict the page from BFCache, as per
+// https://github.com/w3c/ServiceWorker/issues/1038#issuecomment-291028845.
+promise_test(async t => {
+ const pageA = new RemoteContext(token());
+ const pageB = new RemoteContext(token());
+
+ const urlA = location.origin + executorPath + pageA.context_id;
+ const urlB = originCrossSite + executorPath + pageB.context_id;
+
+ window.open(urlA, '_blank', 'noopener');
+ await pageA.execute_script(waitForPageShow);
+
+ // Register a service worker after `pageA` is loaded to make `pageA`
+ // uncontrolled at this time.
+ const workerUrl =
+ 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
+ const registration =
+ await service_worker_unregister_and_register(t, workerUrl, './');
+ t.add_cleanup(_ => registration.unregister());
+ await wait_for_state(t, registration.installing, 'activated');
+
+ // Navigate to `urlB`.
+ await pageA.execute_script(
+ (url) => {
+ prepareNavigation(() => { location.href = url; });
+ },
+ [urlB]);
+ await pageB.execute_script(waitForPageShow);
+
+ // Call Clients.claim() on the service worker when `pageA` is in BFCache.
+ const controllerChanged = new Promise(
+ resolve => navigator.serviceWorker.oncontrollerchange = resolve);
+ await claim(t, registration.active);
+ await controllerChanged;
+
+ // `pageA` doesn't appear in matchAll().
+ const clients1 = await (await fetch('/get-clients-matchall')).json();
+ assert_true(clients1.indexOf(urlA) < 0,
+ '1: matchAll() before back navigation');
+
+ // Back navigate and check that the page was evicted from BFCache.
+ await pageB.execute_script(
+ () => {
+ prepareNavigation(() => { history.back(); });
+ }
+ );
+ await pageA.execute_script(waitForPageShow);
+ await assert_not_bfcached(pageA);
+
+ // After back navigation, `pageA` appear in matchAll(), because it was newly
+ // loaded and controlled by the service worker.
+ const clients2 = await (await fetch('/get-clients-matchall')).json();
+ const controlled2 = await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null));
+ assert_true(clients2.indexOf(urlA) >= 0,
+ '2: matchAll() just after back navigation');
+ assert_true(controlled2,
+ '2: pageA should be controlled just after back navigation');
+
+}, 'Clients.claim() evicts pages that would be affected from BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-matchall.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-matchall.https.html
new file mode 100644
index 0000000000..069529dbe4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-clients-matchall.https.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+promise_test(async t => {
+ // Register a service worker and make this page controlled.
+ const workerUrl =
+ 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
+ const registration =
+ await service_worker_unregister_and_register(t, workerUrl, './');
+ t.add_cleanup(_ => registration.unregister());
+ await wait_for_state(t, registration.installing, 'activated');
+ const controllerChanged = new Promise(
+ resolve => navigator.serviceWorker.oncontrollerchange = resolve);
+ await claim(t, registration.active);
+ await controllerChanged;
+
+ const pageA = new RemoteContext(token());
+ const pageB = new RemoteContext(token());
+
+ const urlA = location.origin + executorPath + pageA.context_id;
+ const urlB = originCrossSite + executorPath + pageB.context_id;
+
+ // Open `urlA`.
+ window.open(urlA, '_blank', 'noopener');
+ await pageA.execute_script(waitForPageShow);
+
+ // Get Clients.matchAll() and check whether `pageA` is controlled.
+ // Actual `assert_*()` is called after `assert_bfcached()` below.
+ const clients1 = await (await fetch('/get-clients-matchall')).json();
+ const controlled1 = await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null));
+
+ // Navigate to `urlB` and get Clients.matchAll() when `urlA` is in BFCache.
+ await pageA.execute_script(
+ (url) => prepareNavigation(() => {
+ location.href = url;
+ }),
+ [urlB]);
+ await pageB.execute_script(waitForPageShow);
+ const clients2 = await (await fetch('/get-clients-matchall')).json();
+
+ // Back navigate and check whether the page is restored from BFCache.
+ await pageB.execute_script(
+ () => {
+ prepareNavigation(() => { history.back(); });
+ }
+ );
+ await pageA.execute_script(waitForPageShow);
+ await assert_bfcached(pageA);
+
+ // Get Clients.matchAll() and check whether `pageA` is controlled.
+ const clients3 = await (await fetch('/get-clients-matchall')).json();
+ const controlled3 = await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null));
+
+ // Clients.matchAll() should not list `urlA` when it is in BFCache.
+ assert_true(clients1.indexOf(urlA) >= 0,
+ '1: matchAll() before navigation');
+ assert_true(clients2.indexOf(urlA) < 0,
+ '2: matchAll() before back navigation');
+ assert_true(clients3.indexOf(urlA) >= 0,
+ '3: matchAll() after back navigation');
+
+ // `pageA` should be controlled before/after BFCached.
+ assert_true(controlled1,
+ 'pageA should be controlled before BFCached');
+ assert_true(controlled3,
+ 'pageA should be controlled after restored');
+}, 'Clients.matchAll() should not list pages in BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-controlled-after-restore.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-controlled-after-restore.https.html
new file mode 100644
index 0000000000..a937eb85ac
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-controlled-after-restore.https.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+promise_test(async t => {
+ const pageA = new RemoteContext(token());
+ const pageB = new RemoteContext(token());
+
+ const urlA = location.origin + executorPath + pageA.context_id;
+ const urlB = originCrossSite + executorPath + pageB.context_id;
+
+ // Register a service worker.
+ const workerUrl =
+ 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
+ const registration =
+ await service_worker_unregister_and_register(t, workerUrl, './');
+ t.add_cleanup(_ => registration.unregister());
+ await wait_for_state(t, registration.installing, 'activated');
+
+ window.open(urlA, '_blank', 'noopener');
+ await pageA.execute_script(waitForPageShow);
+
+ assert_true(
+ await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null)),
+ 'pageA should be controlled before navigation');
+
+ await navigateAndThenBack(pageA, pageB, urlB);
+ await assert_bfcached(pageA);
+
+ assert_true(
+ await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null)),
+ 'navigator.serviceWorker.controller should be non-null ' +
+ 'after restored from BFCache');
+
+ const isControlled = await pageA.execute_script(
+ () => fetch('/is-controlled').then(r => r.text()));
+
+ assert_true(
+ await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null)),
+ 'navigator.serviceWorker.controller should be non-null ' +
+ 'after restored from BFCache and after fetch');
+
+ assert_equals(isControlled, 'controlled',
+ 'fetch should be intercepted after restored from BFCache');
+}, 'Pages should remain controlled after restored from BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-unregister.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-unregister.https.html
new file mode 100644
index 0000000000..1c3f81153c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/service-worker-unregister.https.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+// When a service worker is unregistered when a controlled page is in BFCache,
+// the page can be still restored from BFCache and remain controlled by the
+// service worker.
+promise_test(async t => {
+ // Register a service worker and make this page controlled.
+ const workerUrl =
+ 'resources/service-worker.js?pipe=header(Service-Worker-Allowed,../)';
+ const registration =
+ await service_worker_unregister_and_register(t, workerUrl, './');
+ t.add_cleanup(_ => registration.unregister());
+ await wait_for_state(t, registration.installing, 'activated');
+ const controllerChanged = new Promise(
+ resolve => navigator.serviceWorker.oncontrollerchange = resolve);
+ await claim(t, registration.active);
+ await controllerChanged;
+
+ const pageA = new RemoteContext(token());
+ const pageB = new RemoteContext(token());
+
+ const urlA = location.origin + executorPath + pageA.context_id;
+ const urlB = originCrossSite + executorPath + pageB.context_id;
+
+ // Open `urlA`.
+ window.open(urlA, '_blank', 'noopener');
+ await pageA.execute_script(waitForPageShow);
+
+ assert_true(
+ await pageA.execute_script(
+ () => (navigator.serviceWorker.controller !== null)),
+ 'pageA should be controlled before navigation');
+
+ // Navigate to `urlB`.
+ await pageA.execute_script(
+ (url) => prepareNavigation(() => {
+ location.href = url;
+ }),
+ [urlB]);
+ await pageB.execute_script(waitForPageShow);
+
+ // Unregister the service worker when the controlled `pageA` is in BFCache.
+ await registration.unregister();
+
+ // Back navigate and check whether the page is restored from BFCache.
+ await pageB.execute_script(
+ () => {
+ prepareNavigation(() => { history.back(); });
+ }
+ );
+ await pageA.execute_script(waitForPageShow);
+ await assert_not_bfcached(pageA);
+
+ assert_true(
+ await pageA.execute_script(
+ () => (navigator.serviceWorker.controller === null)),
+ 'pageA should not be controlled');
+
+}, 'Unregister service worker while a controlled page is in BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/storage-events.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/storage-events.html
new file mode 100644
index 0000000000..6957496c30
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/storage-events.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script>
+// When localStorage (`key1`) is modified when a page (`pageA`) is in BFCache,
+// storage events should not be fired for the page after becoming active.
+// https://github.com/whatwg/storage/issues/119#issuecomment-1115844532
+promise_test(async t => {
+ const pageA = new RemoteContext(token());
+ const pageB = new RemoteContext(token());
+ const pageC = new RemoteContext(token());
+
+ const urlA = executorPath + pageA.context_id + '&events=pagehide,pageshow,load';
+ const urlB = originCrossSite + executorPath + pageB.context_id;
+ const urlC = executorPath + pageC.context_id + '&events=pagehide,pageshow,load';
+
+ // localStorage key to set while pageA is in BFCache.
+ const key1 = token();
+ // localStorage key to set after pageA is restored from BFCache.
+ const key2 = token();
+
+ const startRecordingStorageEvent = (key1, key2) => {
+ window.key1EventFired = new Promise(resolve => {
+ window.addEventListener('storage', e => {
+ if (e.key === key1) {
+ recordEvent('storage1');
+ resolve();
+ }
+ });
+ });
+ window.key2EventFired = new Promise(resolve => {
+ window.addEventListener('storage', e => {
+ if (e.key === key2) {
+ recordEvent('storage2');
+ resolve();
+ }
+ });
+ });
+ };
+
+ window.open(urlA, '_blank', 'noopener');
+ await pageA.execute_script(waitForPageShow);
+ await pageA.execute_script(startRecordingStorageEvent, [key1, key2]);
+
+ // Window C is an unrelated window kept open without navigation, to confirm
+ // that storage events are fired as expected in non-BFCache-related scenario
+ // and not blocked due to non-BFCache-related reasons.
+ window.open(urlC, '_blank');
+ await pageC.execute_script(waitForPageShow);
+ await pageC.execute_script(startRecordingStorageEvent, [key1, key2]);
+
+ // Navigate A to B.
+ await pageA.execute_script((url) => {
+ prepareNavigation(() => {
+ location.href = url;
+ });
+ }, [urlB]);
+ await pageB.execute_script(waitForPageShow);
+
+ // Update `key1` while pageA is in BFCache.
+ localStorage.setItem(key1, 'value');
+
+ // Wait for a storage event is fired on PageC and a while,
+ // to prevent race conditions between event processing
+ // triggered by `setItem()` and the following operations.
+ await pageC.execute_script(() => window.key1EventFired);
+ await new Promise(resolve => t.step_timeout(resolve, 1000));
+
+ // Back navigate to pageA, to be restored from BFCache.
+ await pageB.execute_script(
+ () => {
+ prepareNavigation(() => { history.back(); });
+ }
+ );
+ await pageA.execute_script(waitForPageShow);
+ await assert_bfcached(pageA);
+
+ // Update `key2` after pageA is restored from BFCache.
+ localStorage.setItem(key2, 'value');
+
+ // Wait for a storage event for `key2` is fired on PageA.
+ await pageA.execute_script(() => window.key2EventFired);
+
+ // Confirm that a storage event for `key1` is not fired on PageA.
+ assert_array_equals(
+ await pageA.execute_script(() => getRecordedEvents()),
+ [
+ 'window.load',
+ 'window.pageshow',
+ 'window.pagehide.persisted',
+ 'window.pageshow.persisted',
+ 'storage2',
+ ],
+ 'pageA should not receive storage events for updates while in BFCache');
+
+}, 'Storage events should not be fired for BFCached pages after becoming active');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/timers.html b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/timers.html
new file mode 100644
index 0000000000..aab650f36e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/timers.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers:fully-active">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/helper.sub.js"></script>
+<script>
+// Timers should be paused when the Document is not fully active.
+// This test is checking this by measuring the actual elapsed time for a timer
+// started before a page is stored into BFCache, staying for a while in BFCache,
+// and fired after the page is restored from BFCache.
+
+const delayMain = 18000;
+const delayBeforeForwardNavigation = 6000;
+const delayBeforeBackNavigation = 5000;
+// `delayBeforeForwardNavigation` and `delayBeforeBackNavigation` are set
+// sufficiently large in order to distinguish the expected case from other
+// scenarios listed in `funcAfterAssertion()`, and to allow some delays outside
+// timers (e.g. due to communication between Windows). The additional delays
+// can be large (e.g. ~4 seconds), so the delays above should be sufficiently
+// large.
+
+const startTime = performance.now();
+
+runBfcacheTest({
+ funcBeforeNavigation: async (delayMain, delayBeforeForwardNavigation) => {
+ // Set `promiseMainTimer` that is resolved after a timeout of `delayMain`
+ // ms.
+ window.promiseMainTimer = new Promise(resolve => {
+ setTimeout(resolve, delayMain);
+ });
+ // Then navigate to another page after `delayBeforeForwardNavigation` ms.
+ await new Promise(resolve =>
+ setTimeout(resolve, delayBeforeForwardNavigation));
+ },
+ argsBeforeNavigation: [delayMain, delayBeforeForwardNavigation],
+ funcBeforeBackNavigation: async (delayBeforeBackNavigation) => {
+ // Back navigate after `delayBeforeBackNavigation` ms.
+ await new Promise(resolve =>
+ setTimeout(resolve, delayBeforeBackNavigation));
+ },
+ argsBeforeBackNavigation: [delayBeforeBackNavigation],
+ funcAfterAssertion: async (pageA) => {
+ // Wait for `promiseMainTimer` resolution and check its timing.
+ await pageA.execute_script(() => window.promiseMainTimer);
+ const actualDelay = performance.now() - startTime;
+
+ if (actualDelay >= delayMain + delayBeforeBackNavigation +
+ delayBeforeForwardNavigation) {
+ assert_unreached(
+ "The timer is fired too late. " +
+ "Maybe the timer is reset when restored from BFCache and " +
+ "waits from the beginning again");
+ } else if (actualDelay >= delayMain + delayBeforeBackNavigation) {
+ // Expected: The timer is paused when the page is in BFCache.
+ } else if (actualDelay >= delayMain) {
+ assert_unreached(
+ "The timer is fired too early. " +
+ "Maybe the time isn't paused when the page is in BFCache");
+ } else {
+ assert_unreached(
+ "The timer is fired too early, even earlier than delayMain.");
+ }
+ }
+}, 'Timers should be paused when the page is in BFCache');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html
new file mode 100644
index 0000000000..cadcf126f5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html
@@ -0,0 +1,11 @@
+<!doctype html>
+001-1
+<script>
+addEventListener("pageshow",
+ function(e) {
+ parent.events.push(e);
+ if (parent.events.length == 2) {
+ parent.do_test();
+ }
+ }, false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html
new file mode 100644
index 0000000000..6387bc89c8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html
@@ -0,0 +1,5 @@
+<!doctype html>
+001-2
+<script>
+onload = function() {setTimeout(function() {history.go(-1)}, 500)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html
new file mode 100644
index 0000000000..336ede4cb6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>pageshow event from traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="001-1.html"></iframe>
+<script>
+var t = async_test();
+var events = [];
+var iframe = document.getElementsByTagName("iframe")[0];
+
+onload = t.step_func(function() {
+ setTimeout(t.step_func(
+ function() {
+ assert_equals(iframe.contentDocument.readyState, "complete")
+ iframe.src = "001-2.html";
+ }), 500);
+ onload = null;
+})
+
+do_test = t.step_func(function() {
+ assert_equals(events.length, 2);
+ events.forEach(function(e, i) {
+ phase = i ? "after" : "before";
+ assert_equals(e.type, "pageshow", "type " + phase + " navigation");
+
+ // https://github.com/whatwg/html/issues/6794
+ assert_equals(e.bubbles, true, "bubbles " + phase + " navigation");
+ assert_equals(e.cancelable, true, "cancelable " + phase + " navigation");
+
+ assert_equals(e.persisted, i == 0 ? false : true, "persisted " + phase + " navigation");
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html
new file mode 100644
index 0000000000..9ee7576249
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Synthetic popstate events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_throws_js(
+ TypeError,
+ () => PopStateEvent(''),
+ "Calling PopStateEvent constructor without 'new' must throw"
+ );
+}, "PopStateEvent constructor called as normal function");
+
+test(function () {
+ assert_false('initPopStateEvent' in PopStateEvent.prototype,
+ 'There should be no PopStateEvent#initPopStateEvent');
+}, 'initPopStateEvent');
+
+test(function () {
+ var popStateEvent = new PopStateEvent("popstate");
+ assert_equals(popStateEvent.state, null, "the PopStateEvent.state");
+}, "Initial value of PopStateEvent.state must be null");
+
+test(function () {
+ var popStateEvent = new PopStateEvent("popstate");
+ assert_false(popStateEvent.hasUAVisualTransition, "the PopStateEvent.hasUAVisualTransition");
+}, "Initial value of PopStateEvent.hasUAVisualTransition must be false");
+
+test(function () {
+ var state = history.state;
+ var data;
+ var hasUAVisualTransition = false;
+ window.addEventListener('popstate', function (e) {
+ data = e.state;
+ hasUAVisualTransition = e.hasUAVisualTransition;
+ });
+ window.dispatchEvent(new PopStateEvent('popstate', {
+ 'state': {testdata:true},
+ 'hasUAVisualTransition': true
+ }));
+ assert_true(data.testdata,'state data was corrupted');
+ assert_equals(history.state, state, "history.state was NOT set by dispatching the event");
+ assert_true(hasUAVisualTransition, 'hasUAVisualTransition not set correctly');
+}, 'Dispatching a synthetic PopStateEvent');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html
new file mode 100644
index 0000000000..2f7d3fafdf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>API availability following history traversal</title>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<p>Test requires popup blocker disabled</p>
+<div id=log></div>
+<script>
+var t = async_test();
+var hasNavigated = false;
+var child;
+t.step(function() {
+ child = window.open("resources/api-availability-1.html");
+ t.add_cleanup(function() {
+ child.close();
+ });
+});
+navigate = t.step_func(function() {
+ hasNavigated = true;
+ child.location = child.location.href.replace("api-availability-1.html", "api-availability-2.html");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html
new file mode 100644
index 0000000000..5cbab71a5e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html
@@ -0,0 +1,35 @@
+<iframe id="test"></iframe>
+<script>
+var opener = window.opener;
+var t = opener.t;
+var f = document.getElementById("test");
+var l = opener.document.getElementById("step_log");
+
+log = function(t) {l.textContent += ("\n" + t)}
+var navigated = false;
+var steps = [
+ () => f.src = "browsing_context_name-1.html",
+ () => {
+ navigated = true;
+ opener.assert_equals(f.contentWindow.name, "test", "Initial load");
+ f.src = "browsing_context_name-2.html"
+ },
+ () => {
+ opener.assert_equals(f.contentWindow.name, "test1");
+ opener.assert_equals(history.length, 2);
+ history.back()
+ },
+ () => {
+ opener.assert_equals(f.contentWindow.name, "test1", "After navigation");
+ t.done();
+ }
+].map((x, i) => t.step_func(() => {log("Step " + (i+1)); x()}));
+
+next = () => steps.shift()();
+
+onload = () => {
+ log("page load");
+ f.onload = () => {log("iframe onload"); next()};
+ setTimeout(next, 0);
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html
new file mode 100644
index 0000000000..85748a2ebc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html
@@ -0,0 +1,6 @@
+document 1
+<script>
+if (!parent.navigated) {
+ window.name = "test";
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html
new file mode 100644
index 0000000000..b0c869046b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html
@@ -0,0 +1,4 @@
+document 2
+<script>
+window.name = "test1";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html
new file mode 100644
index 0000000000..e0c239744f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html
@@ -0,0 +1,6 @@
+document 3
+<script>
+if (!parent.navigated) {
+ window.name = "test3";
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html
new file mode 100644
index 0000000000..5d2dfa6bb8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html
@@ -0,0 +1,6 @@
+document 4
+<script>
+if (!parent.navigated) {
+ window.name = "test4";
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html
new file mode 100644
index 0000000000..60a8acb098
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>Retaining window.name on history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<pre id="step_log"></pre>
+
+<script>
+var t = async_test();
+t.step(() => {
+ win = window.open("browsing_context_name-0.html");
+ t.add_cleanup(() => win.close());
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html
new file mode 100644
index 0000000000..9e91722714
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html
@@ -0,0 +1,34 @@
+<iframe id="test"></iframe>
+<script>
+var t = opener.t;
+var f = document.getElementById("test");
+var l = opener.document.getElementById("step_log");
+
+var log = function(t) {l.textContent += ("\n" + t)}
+var navigated = false;
+var steps = [
+ () => f.src = "browsing_context_name-1.html",
+ () => {
+ navigated = true;
+ opener.assert_equals(f.contentWindow.name, "test", "Initial load");
+ f.src = f.src.replace("http://", "http://www.").replace("browsing_context_name-1", "browsing_context_name-2");
+ },
+ () => {
+ // Can't test .name easily here because it's cross-origin
+ opener.assert_equals(history.length, 2);
+ history.back()
+ },
+ () => {
+ opener.assert_equals(f.contentWindow.name, "test", "After navigation");
+ t.done();
+ }
+].map((x, i) => t.step_func(() => {log("Step " + (i+1)); x()}));
+
+next = () => steps.shift()();
+
+onload = () => {
+ log("page load");
+ f.onload = () => {log("iframe onload"); next()};
+ setTimeout(next, 0);
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html
new file mode 100644
index 0000000000..caa0bce3eb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>Restoring window.name on cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<pre id="step_log"></pre>
+
+<script>
+var t = async_test();
+t.step(() => {
+ var win = window.open("browsing_context_name_cross_origin-0.html");
+ t.add_cleanup(() => win.close());
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html
new file mode 100644
index 0000000000..8202a892a3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<title>Restoring window.name on cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<pre id="step_log"></pre>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var f = document.getElementById("test");
+var l = document.getElementById("step_log");
+
+log = function(t) {l.textContent += ("\n" + t)}
+
+var steps = [
+ function() {f.src = "browsing_context_name-1.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test", "Initial load");
+ setTimeout(next, 0);
+ },
+ function() {f.src = "browsing_context_name-3.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 1");
+ setTimeout(next, 0);
+ },
+ function() {f.src = f.src.replace("http://", "http://www.").replace("browsing_context_name-3", "browsing_context_name-2");},
+ function() {
+ setTimeout(next, 0);
+ },
+ function() {history.go(-2); setTimeout(next, 500)},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 2");
+ t.done();
+ }
+].map(function(x) {return t.step_func(function() {log("Step " + step + " " + f.contentWindow.location); x()})});
+
+var step = 0;
+next = t.step_func(function() {steps[step++]()});
+
+f.onload=next;
+
+onload = function() { setTimeout(next, 0); };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html
new file mode 100644
index 0000000000..b6a35680dd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Restoring window.name on cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<pre id="step_log"></pre>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var f = document.getElementById("test");
+var l = document.getElementById("step_log");
+
+log = function(t) {l.textContent += ("\n" + t)}
+
+var steps = [
+ function() {f.src = "browsing_context_name-1.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test", "Initial load");
+ setTimeout(next, 0);
+ },
+ function() {f.src = "browsing_context_name-3.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 1");
+ setTimeout(next, 0);
+ },
+ function() {f.src = f.src.replace("http://", "http://www.").replace("browsing_context_name-1", "browsing_context_name-2");},
+ function() {f.src = f.src.replace("http://www.", "http://").replace("browsing_context_name-2", "browsing_context_name-4");},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 2");
+ history.go(-3); setTimeout(next, 500)
+ },
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 3");
+ t.done();
+ }
+].map(function(x) {return t.step_func(function() {log("Step " + step + " " + f.contentWindow.location); x()})});
+
+var step = 0;
+next = t.step_func(function() {steps[step++]()});
+
+f.onload=next;
+
+onload = function() { setTimeout(next, 0); };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html
new file mode 100644
index 0000000000..f03efc3b65
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test the properties of a session history entry's document state</title>
+<link rel="help" href="https://html.spec.whatwg.org/#document-state">
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+// In this test, we create an auxiliary window with a session history A -> B,
+// where the document on site B is the current active document. Bf-cache is
+// disabled via `Cache-Control: no-store` headers. We then `history.back()` to
+// site A, and perform `location.replace(B)`. This makes the first document in
+// the session history now same-origin/site with the URL in the subsequent
+// session history entry's document state.
+//
+// We then perform `history.forward()` in the first document, which loads the
+// second document (from network). We confirm that the resulting navigation
+// request was made with the expected state, stored on the history entry's
+// document state. The consequences of this are:
+// - The navigation is made with the `Sec-Fetch-Site: cross-site` header,
+// indicating that the *original* document state's initiator origin was
+// preserved
+// - The navigation is made with a cross-origin `Referer` header, indicating
+// that the *original* document state's referrer was preserved
+// - The resulting document has a cross-origin `document.referrer`, indicating
+// the same as above
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const A = await rcHelper.addWindow();
+
+ // Create B on a new origin (with bf-cache disabled).
+ const B = await A.navigateToNew({
+ origin: 'HTTPS_NOTSAMESITE_ORIGIN',
+ headers: [['Cache-Control', 'no-store']],
+ });
+
+ // This is the origin we're going to navigate A to, so that it becomes
+ // same-origin with B.
+ const originB = new URL(await B.executeScript(() => location.href)).origin;
+ await B.historyBack();
+
+ // Make A navigate to the same document but in origin B:
+ const urlA = await A.executeScript(() => location.href);
+ const originA = new URL(urlA).origin;
+ assert_not_equals(originA, originB, 'Contexts A and B are cross-origin');
+
+ // Load A's current document but on origin B.
+ const newUrlOnOriginB = urlA.replace(originA, originB);
+ await A.navigate((url) => {
+ location.replace(url);
+ }, [newUrlOnOriginB]);
+
+ // Assert that A and B are now same-origin:
+ const newUrlA = await A.executeScript(() => {
+ return location.href;
+ });
+
+ // Now the session history looks like:
+ // B -> B (initiator origin: A)
+ assert_equals(new URL(newUrlA).origin, originB);
+
+ // This means that when we navigate forward, we should request the second
+ // document with the history entry's document state, which mostly preserves
+ // parameters from the original initiator (a cross-site document), despite a
+ // now-same-origin document initiating this navigation via history.
+ await A.historyForward();
+
+ const requestHeaders = await B.getRequestHeaders();
+ const documentReferrer = await B.executeScript(() => document.referrer);
+
+ assert_equals(requestHeaders.get('sec-fetch-site'), 'cross-site',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site, ends up with Sec-Fetch-Dest: cross-site ' +
+ 'header');
+ assert_equals(requestHeaders.get('referer'), originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with the Referer header that is the ' +
+ 'original cross-site initiator');
+ assert_equals(documentReferrer, originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with document.referrer that is the ' +
+ 'original cross-site initiator');
+}, "A navigation's initiator origin and referrer are stored in the document " +
+ "state and used in the document repopulation case");
+
+// This test is similar to the above, but instead of testing for the true
+// history entry -> document state -> document repopulation case, we stay on [B]
+// (the document who was navigated to from [A]) and run `location.reload()` to
+// confirm that the initiator information from the [A] -> [B] navigation is used
+// when reloading [B], not [B]'s own same-origin information.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const A = await rcHelper.addWindow();
+
+ const originA = new URL(await A.executeScript(() => location.href)).origin;
+
+ // Create B on a new origin.
+ const B = await A.navigateToNew({
+ origin: 'HTTPS_NOTSAMESITE_ORIGIN',
+ });
+
+ const originB = new URL(await B.executeScript(() => location.href)).origin;
+ assert_not_equals(originA, originB, 'Contexts A and B are cross-origin');
+
+ // Reload B.
+ await B.navigate(() => {
+ location.reload();
+ }, []);
+
+ const requestHeaders = await B.getRequestHeaders();
+ const documentReferrer = await B.executeScript(() => document.referrer);
+
+ assert_equals(requestHeaders.get('sec-fetch-site'), 'cross-site',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site, ends up with Sec-Fetch-Dest: cross-site ' +
+ 'header');
+ assert_equals(requestHeaders.get('referer'), originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with the Referer header that is the ' +
+ 'original cross-site initiator');
+ assert_equals(documentReferrer, originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with document.referrer that is the ' +
+ 'original cross-site initiator');
+}, "A navigation's initiator origin and referrer are stored in the document " +
+ "state and used on location.reload()");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html
new file mode 100644
index 0000000000..75889ef517
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "popstate", "hashchange"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "popstate", "hashchange", "hashchange"]);
+ }));
+ }), { once: true });
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ location.hash = "#2";
+ assert_array_equals(window.eventOrder, ["load", "popstate", "popstate"]);
+ }, 0));
+}, "when changing hash, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html
new file mode 100644
index 0000000000..f74d716d91
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+ }));
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ }, 0));
+}, "when changing hash, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html
new file mode 100644
index 0000000000..4f9f3dad47
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do pushState before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+
+ history.pushState({ state: "new state" }, "");
+ }, 0));
+}, "when pushing state, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html
new file mode 100644
index 0000000000..28148ff7b2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+
+ history.replaceState({ state: "new state" }, "");
+ }));
+}, "when replacing state, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html
new file mode 100644
index 0000000000..7c8df11843
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["popstate", "popstate", "hashchange", "hashchange", "load"]);
+ }));
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["popstate"]);
+ location.hash = "#2";
+ assert_array_equals(window.eventOrder, ["popstate", "popstate"]);
+}, "when changing hash twice, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html
new file mode 100644
index 0000000000..97c4636fad
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["popstate", "hashchange", "load"]);
+ }));
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["popstate"]);
+}, "when changing hash, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html
new file mode 100644
index 0000000000..a08afa474f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func(() => {
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+ }));
+
+ history.pushState({ state: "new state" }, "");
+}, "when pushing state, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html
new file mode 100644
index 0000000000..10d30038fb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+
+ history.replaceState({ state: "new state" }, "");
+}, "when replacing state, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html
new file mode 100644
index 0000000000..35ada116ed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(t => {
+ let popstate_called = false;
+ window.onpopstate = t.step_func(e => {
+ popstate_called = true;
+ history.pushState(2, null, "#2");
+ assert_not_equals(history.state, e.state);
+ });
+ location.hash = "#1";
+ assert_true(popstate_called);
+}, "pushState inside popstate")
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html
new file mode 100644
index 0000000000..51ea20b289
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange", "popstate", "hashchange"]);
+ }));
+ }), { once: true });
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ history.back();
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ }, 0));
+}, "when traversing back, before hashchange");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html
new file mode 100644
index 0000000000..39bc760ff7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange", "popstate", "hashchange"]);
+ }));
+
+ history.back();
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+ }), { once: true });
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ }, 0));
+}, "when traversing back, after hashchange");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html
new file mode 100644
index 0000000000..d5ff83fac0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html
@@ -0,0 +1,151 @@
+<!doctype html>
+<title> PageTransitionEffect Event </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:false, cancelable:false, bubbles:false});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ assert_false(e.persisted, "persisted");
+}, "Constructing pageshow event");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:false, cancelable:false, bubbles:false});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_true(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, persisted true");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_true(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, persisted true");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, empty options");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, empty options");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow");
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, missing options");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide");
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, missing options");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:null});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, persisted:null");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:null});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, persisted:null");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:undefined});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, persisted:undefined");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:undefined});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, persisted:undefined");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {bubbles:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_true(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, bubbles:true");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {bubbles:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_true(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, bubbles:true");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {cancelable:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_true(e.cancelable, "cancelable");
+}, "Constructing pageshow event, cancelable:true");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {cancelable:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_true(e.cancelable, "cancelable");
+}, "Constructing pagehide event, cancelable:true");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html
new file mode 100644
index 0000000000..b7111255f8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>Queue a task to fire hashchange event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+t = async_test();
+window.onload = t.step_func(function () {
+ if (location.href.toString().indexOf("#") > -1) {
+ location.href = location.href.replace(/#.*$/,'');
+ return;
+ }
+ var root = location.href;
+ var oldURLs = [];
+ var newURLs = [];
+
+ var timer = null;
+
+ location.hash = 'foo';
+ window.onhashchange = t.step_func(function (e) {
+ assert_true(e.isTrusted, "isTrusted");
+ assert_equals(e.target, window, "target");
+ assert_equals(e.type, "hashchange", "type");
+ assert_true(e instanceof HashChangeEvent, "is HashChangeEvent");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ oldURLs.push(e.oldURL);
+ newURLs.push(e.newURL);
+ if (newURLs.length === 2) {
+ check_result();
+ } else if (timer === null) {
+ timer = setTimeout(function() {check_result()}, 500);
+ }
+ })
+
+ check_result = t.step_func(function() {
+ clearTimeout(timer);
+ try {
+ assert_array_equals([root, root+"#foo"], oldURLs, "e.newURL");
+ assert_array_equals([root+"#foo", root+"#bar"], newURLs, "e.newURL");
+ t.done();
+ } finally {
+ location.hash = "";
+ }
+ });
+
+ location.hash = 'bar';
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html
new file mode 100644
index 0000000000..2b70375a14
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i"></iframe>
+<body>
+<script>
+async_test(t => {
+ let starting_history_length = history.length;
+ let iframe_url = (new URL("/common/blank.html", location.href)).href;
+ i.src = iframe_url;
+
+ history.pushState("a", "", "#a");
+ assert_equals(history.length, starting_history_length + 1, "First history length");
+
+ i.onload = t.step_func(() => {
+ assert_equals(history.length, starting_history_length + 1, "Second history length");
+ assert_equals(i.contentWindow.location.href, iframe_url);
+ assert_equals(location.hash, "#a");
+ history.back();
+ // Wait a while for a back navigation. Since all of the possible outcomes
+ // are either same-document or navigating to about:blank, this doesn't need
+ // to wait terribly long.
+ t.step_timeout(t.step_func_done(() => {
+ assert_equals(location.hash, "", "top frame should have navigated back");
+ assert_equals(i.contentWindow.location.href, iframe_url, "iframe should not have navigated");
+ }), 100);
+ });
+}, "pushState() in parent while child is doing initial navigation, then go back");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html
new file mode 100644
index 0000000000..4f2429fbfd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = () => t.step_timeout(t.step_func(() => {
+ let starting_history_length = history.length;
+ location.hash = "#a";
+ assert_equals(starting_history_length + 1, history.length);
+ i.contentWindow.location.hash = "#b";
+ assert_equals(starting_history_length + 2, history.length);
+
+ let popstateCount = 0;
+ const popstateCalled = t.step_func(() => {
+ popstateCount++;
+ if (popstateCount < 2)
+ return;
+ assert_equals(location.hash, "");
+ assert_equals(i.contentWindow.location.hash, "");
+ t.done();
+ });
+
+ window.onpopstate = popstateCalled;
+ i.contentWindow.onpopstate = popstateCalled;
+ history.go(-2);
+ }), 0);
+}, "A history traversal should be able to navigate a parent and child simultaneously");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html
new file mode 100644
index 0000000000..f453c80a2a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on restoration from BFCache</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#updating-the-document">
+<link rel="author" href="mailto:bokan@chromium.org">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
+<script>
+// runBfcacheTest opens a popup to pageA which navigates to pageB and then
+// back, ensuring pageA is stored in the BFCache.
+runBfcacheTest({
+ funcBeforeNavigation: async () => {
+ // This function executes in pageA
+
+ // Wait for an animation frame to ensure the the initial-load
+ // `pagereveal` has already been fired so it doesn't get recorded
+ // below.
+ const raf = new Promise(resolve => requestAnimationFrame(resolve));
+ await raf;
+
+ window.event_log = [];
+ let restored = false;
+
+ function recordRafs() {
+ requestAnimationFrame( () => {
+ // Avoid recording animation frames until the page is restored from
+ // BFCache since it's currently uncached. This test is interested only
+ // in the behavior during restoration.
+ if (restored)
+ window.event_log.push('rAF');
+
+ recordRafs();
+ });
+ }
+
+ recordRafs();
+
+ addEventListener('pageshow', (e) => {
+ window.event_log.push('pageshow' + (e.persisted ? '.persisted' : ''));
+ if (e.persisted)
+ restored = true;
+ });
+
+ addEventListener('pagereveal', () => {
+ window.event_log.push('pagereveal');
+ });
+ },
+ funcAfterAssertion: async (pageA, pageB, t) => {
+ let event_log = await pageA.execute_script(async () => {
+ // Ensure at least one animation frame is produced to ensure
+ // pagereveal must have fired.
+ await new Promise(requestAnimationFrame);
+ return window.event_log;
+ });
+
+ // Expect that the events seen are:
+ // pageshow.persisted, pagereveal, rAF, rAF, rAF, ...
+ assert_equals(event_log.slice(0, 3).toString(),
+ 'pageshow.persisted,pagereveal,rAF');
+ for (let i = 3; i < event_log.length; ++i) {
+ assert_equals(event_log[i], 'rAF',
+ 'All events following pagereveal should be animation frames');
+ }
+ },
+ targetOrigin: originSameOrigin,
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html
new file mode 100644
index 0000000000..d2c44511d3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on new-document navigation</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering">
+<link rel="author" href="mailto:bokan@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const event_log = [];
+
+addEventListener('pageshow', () => event_log.push('pageshow'));
+addEventListener('pagereveal', () => event_log.push('pagereveal'));
+requestAnimationFrame(() => event_log.push('rAF'));
+
+promise_test(async () => {
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ assert_equals(event_log.toString(),'pageshow,pagereveal,rAF');
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html
new file mode 100644
index 0000000000..b281b2b088
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on prerender activation</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering">
+<link rel="author" href="mailto:bokan@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/speculation-rules/resources/utils.js"></script>
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+const uid = token();
+const initiator_url = `resources/order-in-prerender-activation-popup.html?uid=${uid}`;
+
+// This test opens a popup to an initiator page. That page then prerenders a
+// "prerendering" version of itself (by adding a `prerendering` query param)
+// and navigates itself to activate the prerender. The results are recorded and
+// sent back to this test harness.
+promise_test(async () => {
+ const channel = new PrerenderChannel('result', uid);
+ const test_done = new Promise(resolve => {
+ channel.addEventListener('message', e => resolve(e.data), { once: true });
+ });
+
+ window.open(initiator_url, '_blank', 'noopener');
+
+ const result = await test_done;
+
+ if (result.hasOwnProperty('fail')) {
+ assert_unreached(result.fail);
+ }
+
+ // The test records relevant event occurrences up to the second animation
+ // frame. Ensure their order and apparance is as expected.
+ const events_in_order = result.events.join(',');
+ assert_equals(events_in_order,
+ 'pageshow,prerenderingchange,pagereveal,raf');
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html
new file mode 100644
index 0000000000..78989adc17
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on prerender activation (popup)</title>
+<link rel="author" href="mailto:bokan@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<script>
+const params = new URLSearchParams(location.search);
+const uid = params.get('uid');
+const is_prerender_step = params.has('prerendering');
+
+const ready_channel = new PrerenderChannel('ready-to-activate', uid);
+
+function finish(result) {
+ const result_channel = new PrerenderChannel('result', uid);
+ result_channel.postMessage(result);
+ result_channel.close();
+ window.close();
+}
+
+// testharness.js assertions don't work inside this popup so this small helper
+// sends a failure signal back to the test page which will cause test failure.
+function assert(cond, desc) {
+ if (!cond) {
+ finish({fail: desc});
+ }
+}
+
+// The first load of this page should be without 'prerendering' and is used
+// to setup the prerender and then activate it when it's ready.
+if (!is_prerender_step) {
+ assert(!document.prerendering, 'initiator page must not be prerendered');
+
+ const ready_to_activate = new Promise(resolve => {
+ ready_channel.addEventListener('message', resolve, {once: true});
+ });
+
+ const prerendering_url = location.href + '&prerendering';
+ startPrerendering(prerendering_url);
+
+ ready_to_activate.then(() => {
+ location.replace(prerendering_url);
+ });
+} else {
+ assert(document.prerendering, 'prerendering step must be initially prerendered');
+
+ const result = {
+ events: []
+ };
+
+ document.addEventListener('prerenderingchange', () => {
+ result.events.push('prerenderingchange');
+ });
+
+ addEventListener('pageshow', () => {
+ result.events.push('pageshow');
+ });
+
+ // A second rAF will end the test.
+ requestAnimationFrame(() => {
+ result.events.push('raf');
+ requestAnimationFrame(() => finish(result));
+ });
+
+ addEventListener('pagereveal', () => {
+ result.events.push('pagereveal');
+ });
+
+ addEventListener('load', () => {
+ ready_channel.postMessage('unused-readyToActivateMessage');
+ ready_channel.close();
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html
new file mode 100644
index 0000000000..6b4df1ef2f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+body {
+ height: 2000px;
+ width: 2000px;
+}
+</style>
+<body> Blank 1 </body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html
new file mode 100644
index 0000000000..def2139667
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+body {
+ height: 2000px;
+ width: 2000px;
+}
+</style>
+<body> Blank 2 </body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html
new file mode 100644
index 0000000000..11737661d0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+body {
+ height: 2000px;
+ width: 2000px;
+}
+#fragment {
+ position: absolute;
+ top: 800px;
+ background-color: #faa;
+ display: block;
+ height: 100px;
+ width: 100px;
+}
+
+</style>
+<body>
+Page with fragment
+ <a id="fragment" name="fragment" class='box'></a>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html
new file mode 100644
index 0000000000..1e9b10d1ee
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+addEventListener('load', _ => {
+ let params = new URLSearchParams(window.location.search);
+ window.opener.postMessage(params.get('name'), '*');
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html
new file mode 100644
index 0000000000..77602b2d42
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<title>Verify history.back() on a persisted page resumes timers</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+
+function make_post_back_url(name) {
+ return new URL('resources/post_name_on_load.html?name=' + name,
+ window.location).href;
+}
+
+function wait_for_message(name) {
+ return new Promise(resolve => {
+ addEventListener('message', function onMsg(evt) {
+ if (evt.data !== name) {
+ return;
+ }
+ removeEventListener('message', onMsg);
+ resolve();
+ });
+ });
+}
+
+function with_window_by_name(name) {
+ let win = window.open(make_post_back_url(name));
+ return wait_for_message(name).then(_ => {
+ return win;
+ });
+}
+
+function with_nested_frame(win, url) {
+ return new Promise(resolve => {
+ let frame = win.document.createElement('iframe');
+ frame.addEventListener('load', function onLoad(evt) {
+ removeEventListener('load', onLoad);
+ resolve(frame);
+ });
+ frame.src = url;
+ win.document.body.appendChild(frame);
+ });
+}
+
+function delay(win, delay) {
+ return new Promise(resolve => {
+ win.setTimeout(_ => {
+ resolve(win);
+ }, delay);
+ });
+}
+
+function navigate_by_name(win, name) {
+ win.location = make_post_back_url(name);
+ return wait_for_message(name).then(_ => {
+ return win;
+ });
+}
+
+function go_back(win) {
+ return new Promise(resolve => {
+ win.onpagehide = e => resolve(win);
+ win.history.back();
+ });
+}
+
+let DELAY = 500;
+
+promise_test(t => {
+ // Create a new window so we can navigate it later.
+ return with_window_by_name('foo').then(win => {
+ // Schedule a timer within the new window. Our intent is
+ // to navigate the window before the timer fires.
+ let delayFired = false;
+ let innerDelay = delay(win, DELAY);
+ innerDelay.then(_ => {
+ delayFired = true;
+ });
+
+ return navigate_by_name(win, 'bar').then(_ => {
+ // Since the window has navigated the timer should not
+ // fire. We set a timer on our current test window
+ // to verify the other timer is not received.
+ assert_false(delayFired);
+ return delay(window, DELAY * 2);
+ }).then(_ => {
+ // The navigated window's timer should not have fired.
+ assert_false(delayFired);
+ // Now go back to the document that set the timer.
+ return go_back(win);
+ }).then(_ => {
+ // We wait for one of two conditions here. For browsers
+ // with a bfcache the original suspended timer will fire.
+ // Alternatively, if the browser reloads the page the original
+ // message will be sent again. Wait for either of these
+ // two events.
+ return Promise.race([wait_for_message('foo'), innerDelay]);
+ }).then(_ => {
+ win.close();
+ });
+ });
+}, 'history.back() handles top level page timer correctly');
+
+promise_test(t => {
+ let win;
+ // Create a new window so we can navigate it later.
+ return with_window_by_name('foo').then(w => {
+ win = w;
+
+ // Create a nested frame so we check if navigation and history.back()
+ // properly handle child window state.
+ return with_nested_frame(win, 'about:blank');
+
+ }).then(frame => {
+ // Schedule a timer within the nested frame contained by the new window.
+ // Our intent is to navigate the window before the timer fires.
+ let delayFired = false;
+ let innerDelay = delay(frame.contentWindow, DELAY);
+ innerDelay.then(_ => {
+ delayFired = true;
+ });
+
+ return navigate_by_name(win, 'bar').then(_ => {
+ // Since the window has navigated the timer should not
+ // fire. We set a timer on our current test window
+ // to verify the other timer is not received.
+ assert_false(delayFired);
+ return delay(window, DELAY * 2);
+ }).then(_ => {
+ // The navigated window's timer should not have fired.
+ assert_false(delayFired);
+ // Now go back to the document containing the frame that set the timer.
+ return go_back(win);
+ }).then(_ => {
+ // We wait for one of two conditions here. For browsers
+ // with a bfcache the original suspended timer will fire.
+ // Alternatively, if the browser reloads the page the original
+ // message will be sent again. Wait for either of these
+ // two events.
+ return Promise.race([wait_for_message('foo'), innerDelay]);
+ }).then(_ => {
+ win.close();
+ });
+ });
+}, 'history.back() handles nested iframe timer correctly');
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html
new file mode 100644
index 0000000000..e47cd9c383
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Verify existence and basic read/write function of history.scrollRestoration</title>
+
+<style>
+ body {
+ height: 2000px;
+ width: 2000px;
+ }
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ test(function() {
+ assert_equals(history.scrollRestoration, 'auto');
+ }, 'Default value is "auto"');
+
+ test(function() {
+ history.scrollRestoration = 'manual';
+ assert_equals(history.scrollRestoration, 'manual', 'should be able to set "manual"');
+ history.scrollRestoration = 'auto';
+ assert_equals(history.scrollRestoration, 'auto', 'should be able to set "auto"');
+ }, 'It is writable');
+
+ test(function() {
+ history.scrollRestoration = 'auto';
+ for (var v of [3.1415, {}, 'bogus']) {
+ history.scrollRestoration = v;
+ assert_equals(history.scrollRestoration, 'auto', `setting to invalid value (${v}) should be ignored`);
+ }
+ }, 'Invalid values are ignored');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html
new file mode 100644
index 0000000000..fec801e94b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>Precedence of scroll restoration mode over fragment scrolling in cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<style>
+ iframe {
+ height: 300px;
+ width: 300px;
+ }
+</style>
+<div id="log"></div>
+<script>
+ 'use strict';
+
+ var next;
+ function frameOnload() {
+ if (next) {
+ next();
+ } else {
+ // The test does the following navigation steps for iframe
+ // 1. load page-with-fragment.html#fragment
+ // 2. load blank1
+ // 3. go back to page-with-fragment.html
+ async_test(function(t) {
+ var iframe = document.querySelector('iframe');
+ var hostInfo = get_host_info();
+ var basePath = location.pathname.substring(0, location.pathname.lastIndexOf('/'));
+ var localURL = hostInfo.HTTP_ORIGIN + basePath + '/resources/page-with-fragment.html#fragment';
+ var remoteURL = hostInfo.HTTP_REMOTE_ORIGIN + basePath + "/resources/blank1.html"
+
+ var steps = [
+ function() {
+ assert_equals(iframe.contentWindow.location.href, localURL, 'should be on page-with-fragment page');
+ // wait one animation frame to ensure layout is run and fragment scrolling is complete
+ iframe.contentWindow.requestAnimationFrame(function() {
+ assert_approx_equals(iframe.contentWindow.scrollY, 800, 5, 'should scroll to fragment');
+
+ iframe.contentWindow.history.scrollRestoration = 'manual';
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual');
+ setTimeout(next, 0);
+ });
+ }, function() {
+ // navigate to a new page from a different origin
+ iframe.src = remoteURL;
+ }, function() {
+ // going back causes the iframe to traverse back
+ history.back();
+ }, function() {
+ // coming back from history, scrollRestoration should be set to manual and respected
+ assert_equals(iframe.contentWindow.location.href, localURL, 'should be back on page-with-fragment page');
+ iframe.contentWindow.requestAnimationFrame(t.step_func_done(function() {
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual', 'navigating back should retain scrollRestoration value');
+ assert_equals(iframe.contentWindow.scrollX, 0, 'should not scroll to fragment');
+ assert_equals(iframe.contentWindow.scrollY, 0, 'should not scroll to fragment');
+ }));
+ }
+ ];
+
+ var stepCount = 0;
+ next = t.step_func(function() {
+ steps[stepCount++]();
+ });
+ next();
+ }, 'Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation');
+ }
+ }
+</script>
+<iframe src="resources/page-with-fragment.html#fragment" onload="frameOnload()"></iframe>
+
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html
new file mode 100644
index 0000000000..073e0f6e06
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<style>
+ body {
+ height: 2000px;
+ width: 2000px;
+ }
+
+ #fragment {
+ position: absolute;
+ top: 800px;
+ background-color: #faa;
+ display: block;
+ height: 100px;
+ width: 100px;
+ }
+</style>
+
+<body>
+ <a id="fragment" name="fragment" class='box'></a>
+</body>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ async_test(function(t) {
+ history.scrollRestoration = 'manual';
+ assert_equals(history.scrollRestoration, 'manual');
+
+ location.hash = '#fragment';
+ assert_equals(window.scrollY, 800, 'new navigations should scroll to fragment');
+
+ // create a new entry and reset the scroll before verification
+ history.pushState(null, null, '#done');
+ window.scrollTo(0, 0);
+ assert_equals(window.scrollY, 0, 'should reset scroll before verification');
+
+ setTimeout(function() {
+ // setup verification
+ window.addEventListener('hashchange', t.step_func(function() {
+ assert_equals(location.hash, '#fragment');
+ assert_equals(history.scrollRestoration, 'manual');
+ // navigating back should give precedent to history restoration which is 'manual'
+ assert_equals(window.scrollX, 0, 'should not scroll to fragment');
+ assert_equals(window.scrollY, 0, 'should not scroll to fragment');
+ t.done();
+ }));
+ // kick off verification
+ window.history.back();
+ }, 0);
+
+ }, 'Manual scroll restoration should take precedent over scrolling to fragment in cross doc navigation');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html
new file mode 100644
index 0000000000..87a337b2da
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>Correct behaviour of scroll restoration mode is cross origin history traversal</title>
+
+<style>
+ iframe {
+ height: 300px;
+ width: 300px;
+ }
+</style>
+
+<body>
+ <iframe></iframe>
+</body>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ // The test does the following navigation steps for iframe
+ // 1. load blank1
+ // 2. load blank2
+ // 3. go back to blank1
+ async_test(function(t) {
+ var iframe = document.querySelector('iframe');
+ var baseURL = location.href.substring(0, location.href.lastIndexOf('/'));
+
+ var steps = [
+ function() {
+ iframe.src = 'resources/blank1.html';
+ },
+ function() {
+ assert_equals(iframe.contentWindow.location.href, baseURL + '/resources/blank1.html', 'should be on first blank page');
+ iframe.contentWindow.history.scrollRestoration = 'manual';
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual');
+ iframe.contentWindow.scrollTo(500, 500);
+ assert_equals(iframe.contentWindow.scrollX, 500, 'scripted scrolling should take effect');
+ assert_equals(iframe.contentWindow.scrollY, 500, 'scripted scrolling should take effect');
+ setTimeout(next, 0);
+ },
+ function() {
+ // navigate to new page
+ iframe.src = 'resources/blank2.html';
+ },
+ function() {
+ assert_equals(iframe.contentWindow.location.href, baseURL + '/resources/blank2.html', 'should be on second blank page');
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'auto', 'new page loads should set scrollRestoration to "auto"');
+ setTimeout(next, 0);
+ }, function() {
+ iframe.contentWindow.history.back();
+ }, function() {
+ // coming back scrollRestoration should be restored to 'manual' and respected
+ assert_equals(iframe.contentWindow.location.href, baseURL + '/resources/blank1.html', 'should be back on first blank page');
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual', 'navigating back should retain scrollRestoration value');
+ assert_equals(iframe.contentWindow.scrollX, 0, 'horizontal scroll offset should not be restored');
+ assert_equals(iframe.contentWindow.scrollY, 0, 'vertical scroll offset should not be restored');
+ t.done();
+ }
+ ];
+
+ var stepCount = 0;
+ var next = t.step_func(function() {
+ steps[stepCount++]();
+ });
+
+ iframe.onload = next;
+ next();
+ }, 'Navigating to new page should reset to "auto" and navigating back should restore and respect scroll restoration mode');
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html
new file mode 100644
index 0000000000..46d40eedc6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<title>Correct behaviour of scroll restoration mode in same document history traversals</title>
+
+<style>
+ body {
+ height: 10000px;
+ width: 10000px;
+ }
+</style>
+
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ async_test(function(t) {
+ history.scrollRestoration = 'auto';
+ window.scrollTo(0, 0);
+
+ // create history entries and then verify the impact of scrollRestoration
+ // when they are popped
+ var entries = {
+ /* For scroll restoration mode 'auto', the spec does not require scroll
+ position to be restored at any particular value. */
+ '#1': {type: 'push', expectedScroll: null, scrollRestoration: 'auto'},
+ '#2': {type: 'replace', expectedScroll: null, scrollRestoration: 'auto'},
+ /* For scroll restoration mode 'manual', the spec requires scroll position
+ not to be restored. So we expect [555,555] which is the latest position
+ before navigation. */
+ '#3': {type: 'push', expectedScroll: [555, 555], scrollRestoration: 'manual'},
+ '#4': {type: 'replace', expectedScroll: [555, 555], scrollRestoration: 'manual'}
+ };
+
+ // setup entries
+ for (var key in entries) {
+ var entry = entries[key],
+ beforeValue = history.scrollRestoration,
+ newValue = entry.scrollRestoration;
+
+ var args = [{key: key}, '', key];
+ if (entry.type == 'push') {
+ history.pushState.apply(history, args);
+ } else {
+ history.pushState(null, '', key);
+ history.replaceState.apply(history, args);
+ }
+ assert_equals(history.scrollRestoration, beforeValue, `history.scrollRestoration value is retained after pushing new state`);
+ history.scrollRestoration = newValue;
+ assert_equals(history.scrollRestoration, newValue, `Setting scrollRestoration to ${newValue} works as expected`);
+ window.scrollBy(50, 100);
+ }
+
+ // setup verification
+ window.addEventListener('hashchange', t.step_func(function() {
+ var key = location.hash,
+ entry = entries[key];
+
+ if (key === '') {
+ t.done();
+ return;
+ }
+ assert_equals(history.state.key, key, `state should have key: ${key}`);
+ assert_equals(history.scrollRestoration, entry.scrollRestoration, 'scrollRestoration is updated correctly');
+ if (entry.expectedScroll) {
+ assert_equals(window.scrollX, entry.expectedScroll[0], `scrollX is correct for ${key}`);
+ assert_equals(window.scrollY, entry.expectedScroll[1], `scrollY is correct for ${key}`);
+ }
+
+ window.history.back();
+ }));
+
+ // reset the scroll and kick off the verification
+ setTimeout(function() {
+ history.pushState(null, null, '#done');
+ window.scrollTo(555, 555);
+ window.history.back();
+ }, 0);
+
+ }, 'history.{push,replace}State retain scroll restoration mode and navigation in the same document respects it');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html
new file mode 100644
index 0000000000..05b1dbf4b7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<title>Queue a task to fire popstate event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+t = async_test();
+window.onload = t.step_func(function () {
+ var states = [];
+
+ var timer = null;
+
+ history.pushState("a", "State a", "/a");
+ history.pushState("b", "State b", "/b");
+
+ history.back();
+ window.onpopstate = t.step_func(function (e) {
+ assert_true(e.isTrusted, "isTrusted");
+ assert_equals(e.target, window, "target");
+ assert_equals(e.type, "popstate", "type");
+ assert_true(e instanceof PopStateEvent, "is PopStateEvent");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ assert_not_equals(e.hasUAVisualTransition, undefined);
+
+ states.push(e.state);
+
+ if (states.length === 2) {
+ check_result();
+ } else if (timer === null) {
+ timer = setTimeout(function() {check_result()}, 500);
+ }
+ })
+
+ check_result = t.step_func(function() {
+ clearTimeout(timer);
+ try {
+ assert_array_equals(states, ["a", null]);
+ t.done();
+ } finally {
+ location.hash = "";
+ }
+ });
+
+ setTimeout(function() {history.back()}, 0);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html
new file mode 100644
index 0000000000..55b73e1153
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html
@@ -0,0 +1 @@
+Welcome to A. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html
new file mode 100644
index 0000000000..2c31168750
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>API availability following history traversal - 1</title>
+<script>
+var controller = opener;
+var t = controller.t;
+var assert_not_equals = controller.assert_not_equals;
+
+t.step(function() {
+ // If this document is discarded as a result of navigation, then this script
+ // will be executed a second time. The semantics this test intends to verify
+ // cannot be observed under these conditions, the discarding is not itself a
+ // violation. Silently pass the test in that case.
+ if (controller.hasNavigated) {
+ t.done();
+ return;
+ }
+
+ t.step_timeout(function() {
+ assert_not_equals(window.history, null, 'history');
+ assert_not_equals(window.localStorage, null, 'localStorage');
+ assert_not_equals(window.location, null, 'location');
+ assert_not_equals(window.navigator, null, 'navigator');
+ assert_not_equals(window.opener, null, 'opener');
+ assert_not_equals(window.sessionStorage, null, 'sessionStorage');
+
+ t.done();
+ }, 1000);
+
+ controller.navigate();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html
new file mode 100644
index 0000000000..420e5092bc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>API availability following history traversal - 2</title>
+<body onload="history.back()"></body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html
new file mode 100644
index 0000000000..8f2fc900dd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html
@@ -0,0 +1 @@
+Welcome to B. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html
new file mode 100644
index 0000000000..db494e878e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html
@@ -0,0 +1 @@
+Welcome to C. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html
new file mode 100644
index 0000000000..97918a1f99
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<!-- test must be run in a top level browsing context -->
+<title>window.name test helper</title>
+<script>
+const search = window.location.search.replace("?", "");
+const steps = search.split("|");
+
+async function proceedTest() {
+ while (steps.length) {
+ const step = steps.shift();
+
+ if (step.startsWith("report=")) {
+ const id = step.split("=")[1];
+ const stashURL = new URL("unset_context_name_stash.py", location);
+ stashURL.searchParams.set('id', id);
+ stashURL.searchParams.set('value', window.name);
+
+ await fetch(stashURL, { method: "POST" });
+ continue;
+ }
+
+ if (step === "close") {
+ window.close();
+ break;
+ }
+
+ if (step === "navigate") {
+ const url = new URL(window.location);
+ url.host = "{{hosts[][www]}}:{{ports[http][0]}}";
+ url.search = "?" + steps.join("|");
+ window.location = url.href;
+ break;
+ }
+
+ if (step.startsWith("set=")) {
+ window.name = step.split("=")[1];
+ continue;
+ }
+
+ throw new Error("Unsupported step!");
+ }
+}
+
+proceedTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py
new file mode 100644
index 0000000000..411a4587bc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py
@@ -0,0 +1,13 @@
+def main(request, response):
+ key = request.GET.first(b"id")
+ if request.method == "POST":
+ value = request.GET.first(b"value")
+ request.server.stash.take(key)
+ request.server.stash.put(key, value)
+ return b"OK"
+ else:
+ value = request.server.stash.take(key)
+ if value is not None:
+ return value
+ else:
+ return b"NONE"
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html
new file mode 100644
index 0000000000..bcca5ed90c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html
@@ -0,0 +1,50 @@
+<title>Test same-URL navigation and its effects on history</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe src=resources/a.html></iframe>
+<script>
+async_test((t) => {
+ let state = "begin"
+ self[0].frameElement.onload = t.step_func(() => {
+ if(state === "b first") {
+ assert_equals(history.length, 2)
+
+ state = "c first"
+ navigateFrameAfterDelay(t, "resources/c.html")
+ } else if (state === "c first") {
+ assert_equals(history.length, 3)
+
+ state = "a second"
+ history.back(2)
+ } else if (state === "a second") {
+ assert_equals(history.length, 3)
+
+ state = "a third"
+ navigateFrameAfterDelay(t, "resources/a.html")
+ } else if (state === "a third") {
+ assert_equals(history.length, 3)
+ t.done()
+ }
+ })
+ onload = t.step_func(() => {
+ assert_equals(state, "begin")
+ assert_equals(history.length, 1)
+
+ state = "b first"
+
+ navigateFrameAfterDelay(t, "resources/b.html")
+ })
+})
+
+function navigateFrameAfterDelay(t, url) {
+ // Delay to avoid triggering the "replace" behavior which occurs if
+ // the page isn't yet completely loaded, which only occurs after the
+ // load event handlers have finished:
+ // https://html.spec.whatwg.org/#location-object-navigate
+ // https://html.spec.whatwg.org/#the-end:completely-finish-loading
+ t.step_timeout(() => {
+ self[0].location = url
+ }, 0)
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html
new file mode 100644
index 0000000000..8fe7d9f977
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>History restoration order test</title>
+<meta name="assert" content="https://html.spec.whatwg.org/multipage/browsing-the-web.html#history-traversal">
+<meta name="assert" content="Traversing history should restore scroll position after dispatching popstate and before dispatching hashchange">
+
+<style>
+ body {
+ height: 200vh;
+ width: 200vw;
+ }
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ 'use strict';
+ async_test(function(t) {
+ window.addEventListener('load', t.step_func(function() {
+ // Allow 1px epsilon for fractional scrolling.
+ assert_array_approx_equals(scrollPosition(), [0, 0], 1);
+
+ history.pushState('#1', '', '#1');
+ window.scrollTo(50, 100);
+ assert_array_approx_equals(scrollPosition(), [50, 100], 1);
+
+ history.pushState('#2', '', '#2');
+ window.scrollTo(100, 200);
+ assert_array_approx_equals(scrollPosition(), [100, 200], 1);
+
+ setTimeout(t.step_func(function(){
+ history.pushState(null, null, '#done');
+ window.scrollTo(555, 555);
+ assert_array_approx_equals(scrollPosition(), [555, 555], 1);
+ // Kick off the verification.
+ window.history.back();
+ }), 0);
+ }));
+
+ window.addEventListener('popstate', t.step_func(function() {
+ // Verify that scroll position is *not* restored before popstate.
+ const key = location.hash;
+ const expected_scroll_position = expectedScrollPositionForKey(key);
+ assert_not_equals(scrollPosition()[0], expected_scroll_position[0], `scroll is restored before popstate for ${key}`);
+ assert_not_equals(scrollPosition()[1], expected_scroll_position[1], `scroll is restored before popstate for ${key}`);
+
+ if (key == '')
+ t.done();
+ else
+ setTimeout(t.step_func(function(){ window.history.back(); }), 0);
+ }));
+
+ window.addEventListener('hashchange', t.step_func(function() {
+ // Verify that scroll position is restored before hashchange.
+ var key = location.hash;
+ const expected_scroll_position = expectedScrollPositionForKey(key);
+ assert_array_approx_equals(scrollPosition(), expected_scroll_position, 1, `scroll is restored before hashchange for ${key}`);
+ }));
+
+ function scrollPosition() {
+ return [window.pageXOffset, window.pageYOffset];
+ }
+
+ function expectedScrollPositionForKey(key) {
+ switch (key) {
+ case '#2': return [100, 200];
+ case '#1': return [50, 100];
+ case '' : return [0, 0];
+ default: assert_unreached();
+ }
+ }
+
+ }, 'Traversing history should restore scroll position after dispatching popstate and before dispatching hashchange');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html
new file mode 100644
index 0000000000..9aab36b986
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>changing srcdoc to a different srcdoc</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/6809#issuecomment-905677979">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/helpers.js"></script>
+
+<script>
+"use strict";
+
+// Note: bfcache won't mess with any windows with openers, so it doesn't
+// interfere with these tests.
+
+promise_test(async t => {
+ // Set up a window whose iframe contains [normal page, srcdoc] entries.
+ const w = await openWindow("../../resources/has-iframe.html", t);
+ const iframe = w.document.querySelector("iframe");
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ await waitForMessage(iframe.contentWindow);
+
+ assert_equals(w.history.length, 2);
+
+ // Now navigate to a different srcdoc
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc2");
+
+ // Test that it's a replace.
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 2,
+ "history.length must not change since it was a replace");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc2",
+ "Sanity check: the srcdoc document did indeed update"
+ );
+}, "changing srcdoc does a replace navigation since the URL is still " +
+ "about:srcdoc");
+
+promise_test(async t => {
+ // Set up a window whose iframe contains [normal page, srcdoc] entries.
+ const w = await openWindow("../../resources/has-iframe.html", t);
+ const iframe = w.document.querySelector("iframe");
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ await waitForMessage(iframe.contentWindow);
+
+ assert_equals(w.history.length, 2);
+
+ // Now navigate to about:srcdoc#yo
+ iframe.contentWindow.location.href = "about:srcdoc#yo";
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#yo");
+ assert_equals(w.history.length, 3);
+
+ // Now navigate to a different srcdoc
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc2");
+
+ // Test that it's a push back to about:srcdoc.
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(
+ w.history.length,
+ 4,
+ "history.length must increase since it was a push"
+ );
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc2",
+ "Sanity check: the srcdoc document did indeed update"
+ );
+
+ // Test that we can go back to about:srcdoc#yo.
+ w.history.back();
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#yo");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc1",
+ "srcdoc content must be restored from history"
+ );
+}, "changing srcdoc to about:srcdoc#yo then another srcdoc does two push " +
+ "navigations and we can navigate back");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html
new file mode 100644
index 0000000000..09f4094c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>srcdoc history entries</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/6809">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/helpers.js"></script>
+
+<script>
+"use strict";
+
+// Note: bfcache won't mess with any windows with openers, so it doesn't
+// interfere with these tests.
+
+promise_test(async t => {
+ // Set up a window whose iframe contains
+ // [normal page, srcdoc, normal page, srcdoc] entries.
+ const w = await openWindow("/common/blank.html", t);
+ const iframe = await addIframe("/common/blank.html?iframe", w.document);
+
+ assert_equals(w.history.length, 1);
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ assert_equals(w.history.length, 1, "srcdoc navigation must not be sync");
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 2);
+
+ await waitToAvoidReplace(t);
+ const middleURL = (new URL(
+ "../../resources/post-top-opener-on-load.html", location.href)).href;
+ iframe.contentWindow.location.href = middleURL;
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 3);
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc2");
+ assert_equals(w.history.length, 3, "srcdoc navigation must not be sync");
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 4);
+
+ // Now test traversal.
+ w.history.back();
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.href, middleURL);
+
+ await waitToAvoidReplace(t);
+
+ w.history.back();
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc1",
+ "srcdoc contents must be restored from history, not from the current " +
+ "value ('srcdoc2') of the content attribute"
+ );
+}, "srcdoc history entries: the iframe itself navigates");
+
+promise_test(async t => {
+ // Set up a window whose iframe contains [normal page, srcdoc] entries.
+ const w = await openWindow("../../resources/has-iframe.html", t);
+ const iframe = w.document.querySelector("iframe");
+
+ assert_equals(w.history.length, 1);
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ assert_equals(w.history.length, 1, "srcdoc navigation must not be sync");
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 2);
+
+ // Now navigate the window itself.
+ w.location.href = "../../resources/post-top-opener-on-load.html";
+ await waitForMessage(w);
+ assert_equals(w.history.length, 3);
+
+ // Now test traversal.
+ w.history.back();
+ await waitForMessage(w);
+ const iframeAgain = w.document.querySelector("iframe");
+
+ assert_equals(iframeAgain.contentWindow.location.href, "about:srcdoc");
+ assert_equals(
+ iframeAgain.contentDocument?.querySelector("p")?.textContent,
+ "srcdoc1",
+ "srcdoc contents must be restored from history, not from the current " +
+ "value of the (not-existing) content attribute"
+ );
+}, "srcdoc history entries: the container window navigates");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html
new file mode 100644
index 0000000000..e13d191658
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+ <script>
+ document.location = "window-name-navigation.sub.html?hostname={{domains[www1]}}&shouldhavename=false&sendmessage=true";
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html
new file mode 100644
index 0000000000..4b7824b488
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.location = "window-name-navigation.sub.html?hostname={{host}}&shouldhavename=true&sendmessage=true";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html
new file mode 100644
index 0000000000..285469a148
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ var url = new URL(window.location.href);
+ url.hostname = "{{GET[hostname]}}";
+ url.pathname = "/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html";
+ url.search = "shouldhavename={{GET[shouldhavename]}}&sendmessage={{GET[sendmessage]}}";
+ window.name = "test";
+ window.location = url.href;
+ </script>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html
new file mode 100644
index 0000000000..460db2e979
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html
@@ -0,0 +1,23 @@
+<script>
+ function process_test_result(passed, test_name) {
+ if ({{GET[sendmessage]}}) {
+ if (window.opener) {
+ window.opener.postMessage(passed, "*");
+ } else {
+ parent.postMessage(passed, "*");
+ }
+ } else {
+ let host = window.opener || parent;
+ host.test(function(t) {
+ host.assert_equals(passed, true);
+ }, test_name);
+ host.done();
+ }
+ }
+
+ if ({{GET[shouldhavename]}}) {
+ process_test_result(window.name == "test", "Test that window name is present");
+ } else {
+ process_test_result(window.name == "", "Test that window name is not present");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html
new file mode 100644
index 0000000000..285f6d7428
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>window.name is reset after navigating to a different origin</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script>
+
+async function pollResultAndCheck(t, id, expected) {
+ const stashURL = new URL("resources/unset_context_name_stash.py", location);
+ stashURL.searchParams.set('id', id);
+
+ let res = "NONE";
+ while (res == "NONE") {
+ await new Promise(resolve => { t.step_timeout(resolve, 100); });
+
+ const response = await fetch(stashURL);
+ res = await response.text();
+ }
+ if (res !== expected) {
+ assert_unreached('Stash result does not equal expected result.')
+ }
+}
+
+promise_test(async t => {
+ const id = token();
+
+ window.open(`resources/unset_context_name-1.sub.html?set=${id}|navigate|report=${id}|close`, "_blank", "noopener");
+ await pollResultAndCheck(t, id, "");
+}, "Window.name is reset after navigating to a different origin");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html
new file mode 100644
index 0000000000..6bc64c6373
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a cross-origin auxiliary frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ window.open("support/window-name-navigation.sub.html?hostname={{domains[www1]}}&shouldhavename=true&sendmessage=true");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html
new file mode 100644
index 0000000000..fb0bb1883f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <button id="button" onclick="popup();">open popup</button>
+ <script>
+ function popup() {
+ window.popupWin = window.open('support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html', '_blank');
+ }
+ async_test(t => {
+ t.add_cleanup(() => {
+ popupWin.close();
+ })
+ document.getElementById('button').click();
+ onmessage = t.step_func(e => {
+ assert_true(e.data);
+ });
+ }, 'window.name should equal "" after a cross-origin main frame navigation');
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html
new file mode 100644
index 0000000000..a309be6d80
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a cross-origin sub frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ </script>
+ <iframe src="support/window-name-navigation.sub.html?hostname={{domains[www1]}}&shouldhavename=true&sendmessage=true";
+ </iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html
new file mode 100644
index 0000000000..8e0a95d8c0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a same-origin auxiliary frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ window.open("support/window-name-navigation.sub.html?hostname={{host}}&shouldhavename=true&sendmessage=true");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html
new file mode 100644
index 0000000000..ef11d9971a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<title>window.name after a same-origin main frame navigation</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<body>
+ <script>
+ var win;
+ async_test(function(t) {
+ win = window.open("support/window-name-after-same-origin-main-frame-navigation-1.sub.html")
+ addEventListener("message", t.step_func_done(e => assert_true(e.data)));
+ }).add_cleanup(() => {if (win) {win.close()}});
+ </script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html
new file mode 100644
index 0000000000..48a6e247b0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a same-origin sub frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ </script>
+ <iframe src="support/window-name-navigation.sub.html?hostname={{host}}&shouldhavename=true&sendmessage=true";
+ </iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html
new file mode 100644
index 0000000000..4d2229eb51
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+onload = function() {
+ parent.postMessage("003-1", "*");
+ setTimeout(function() {location = "003-2.html";}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html
new file mode 100644
index 0000000000..827a069479
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html
@@ -0,0 +1,9 @@
+<!doctype html>
+003-2
+<script>
+onload = function() {
+ parent.postMessage("003-2", "*")
+ setTimeout(function() {history.go(-1)})
+}
+onunload = function() {location = "003-3.html"}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html
new file mode 100644
index 0000000000..8b26c896fd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("003-3", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html
new file mode 100644
index 0000000000..f437150960
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Navigation from unload whilst traversing history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="003-1.html"></iframe>
+<script>
+var t = async_test();
+
+var pages = [];
+var iframe = document.getElementsByTagName("iframe")[0];
+
+
+onmessage = t.step_func(function(e) {
+ pages.push(e.data);
+ if(pages.length == 3) {
+ assert_array_equals(pages, ["003-1", "003-2", "003-1"]);
+ t.done();
+ iframe.parentNode.removeChild(iframe);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html
new file mode 100644
index 0000000000..02f916fd9c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+onload = function() {
+ parent.postMessage("004-1", "*");
+ setTimeout(function() {location = location.href.replace("http://", "http://www.").replace("004-1.html", "004-2.html");}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html
new file mode 100644
index 0000000000..f2ef83ee1a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html
@@ -0,0 +1,9 @@
+<!doctype html>
+003-2
+<script>
+onload = function() {
+ parent.postMessage("004-2", "*")
+ setTimeout(function() {history.go(-1)})
+}
+onunload = function() {location = "004-3.html"}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html
new file mode 100644
index 0000000000..c98711ae98
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("004-3", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html
new file mode 100644
index 0000000000..dddde4918b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Navigation from unload whilst traversing cross-origin history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="004-1.html"></iframe>
+<script>
+var t = async_test();
+
+var pages = [];
+var iframe = document.getElementsByTagName("iframe")[0];
+
+
+onmessage = t.step_func(function(e) {
+ pages.push(e.data);
+ if(pages.length == 3) {
+ assert_array_equals(pages, ["004-1", "004-2", "004-1"]);
+ t.done();
+ iframe.parentNode.removeChild(iframe);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html
new file mode 100644
index 0000000000..a87e6e9b3d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Link with onclick navigation and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="document.getElementById('test').contentWindow.location='resources/click.html'" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.links[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html
new file mode 100644
index 0000000000..17b3123faa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Link with onclick form submit and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe name="test"></iframe>
+<form target="test" action="resources/click.html"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.links[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html
new file mode 100644
index 0000000000..dbb23ee644
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Link with onclick javascript url and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="document.getElementById('test').contentWindow.location = 'javascript:\'abc<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>\'';" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html
new file mode 100644
index 0000000000..3201d0679d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Link with onclick form submit to javascript url and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:'<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html
new file mode 100644
index 0000000000..d963bed2bf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Link with onclick form submit to javascript url with document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:(function() {document.write('<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ events.push(e.data);
+ if (events.length === 2) {
+ assert_array_equals(events, ["write", "href"]);
+ t.done();
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html
new file mode 100644
index 0000000000..606ad82f48
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Link with onclick form submit to javascript url with delayed document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:(function() {var x = new XMLHttpRequest(); x.open('GET', 'resources/blank.html?pipe=trickle(d2)', false); x.send(); document.write('<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+onload = t.step_func(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html
new file mode 100644
index 0000000000..4d54267968
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Link with onclick navigation to javascript url with document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="javascript:(function() {document.write('<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ events.push(e.data);
+ if (events.length === 2) {
+ assert_array_equals(events, ["write", "href"]);
+ t.done();
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html
new file mode 100644
index 0000000000..3795975d70
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Link with onclick navigation to javascript url with delayed document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<!-- XXX: What is this test trying to do? It's navigating the subframe, but
+ doing a write() to _this_ document, and the "javascript:" in there is
+ completely a red herring: it's a label, not a protocol. There is no
+ javascript url involved here, unlike what the title claims! -->
+<a target="test" onclick="javascript:(function() {var x = new XMLHttpRequest(); x.open('GET', 'blank.html?pipe=trickle(d2)', false); x.send(); document.write('write<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()" href="href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html
new file mode 100644
index 0000000000..36a4521843
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Link with onclick navigation to javascript url with delayed document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" href="javascript:parent.events.push('javascript');">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click();
+ events.push('after script');
+});
+onload = t.step_func(function() {
+ // javascript: executions are async.
+ assert_array_equals(events, ['after script', 'javascript']);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html
new file mode 100644
index 0000000000..b27ca992bd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title> Link with javascript onclick form submission script order </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:parent.events.push('submit');"></form>
+<a target="test" onclick="document.forms[0].submit()">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click();
+ events.push('after script');
+});
+onload = t.step_func(function() {
+ // javascript: executions are async.
+ assert_array_equals(events, ['after script', 'submit']);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html
new file mode 100644
index 0000000000..696aaec2c8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title> Link with javascript onclick and href script order </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="parent.events.push('click');" href="javascript:parent.events.push('href')">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click();
+ events.push('after script');
+});
+onload = t.step_func(function() {
+ // javascript: executions are async.
+ assert_array_equals(events, ['click', 'after script', 'href']);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html
new file mode 100644
index 0000000000..50a9a50fa0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script>
+parent.postMessage(document.readyState, "*");
+let f = document.createElement("iframe");
+f.onload = function() {
+ parent.postMessage("stop", "*");
+ window.stop();
+};
+document.documentElement.appendChild(f);
+
+window.addEventListener("load", (event) => {
+ parent.postMessage("load", "*");
+});
+window.addEventListener("error", (event) => {
+ parent.postMessage("error", "*");
+});
+window.addEventListener("abort", (event) => {
+ parent.postMessage("abort", "*");
+});
+window.addEventListener("pageshow", (event) => {
+ parent.postMessage("pageshow", "*");
+});
+window.addEventListener("DOMContentLoaded", (event) => {
+ parent.postMessage("DOMContentLoaded", "*");
+});
+document.addEventListener("readystatechange", (event) => {
+ if (document.readyState === "complete") {
+ parent.postMessage("complete", "*");
+ }
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html
new file mode 100644
index 0000000000..966b93c51a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script>
+parent.postMessage(document.readyState, "*");
+
+window.addEventListener("load", (event) => {
+ parent.postMessage("load", "*");
+});
+window.addEventListener("error", (event) => {
+ parent.postMessage("error", "*");
+});
+window.addEventListener("abort", (event) => {
+ parent.postMessage("abort", "*");
+});
+window.addEventListener("pageshow", (event) => {
+ parent.postMessage("pageshow", "*");
+});
+window.addEventListener("DOMContentLoaded", (event) => {
+ parent.postMessage("DOMContentLoaded", "*");
+});
+document.addEventListener("readystatechange", (event) => {
+ if (document.readyState === "complete") {
+ parent.postMessage("complete", "*");
+ }
+});
+
+window.setTimeout(function() {
+ parent.postMessage("stop", "*");
+ window.stop();
+}, 100);
+
+</script>
+<link rel="stylesheet" href="/common/slow.py"></link>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html
new file mode 100644
index 0000000000..4a4c3df4e7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>Aborting a Document load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#aborting-a-document-load">
+<div id="log"></div>
+<script>
+var events = [];
+onmessage = function(e) {
+ events.push(e.data);
+};
+async_test(test => {
+ test.step_timeout(() => {
+ const frame = document.querySelector('iframe');
+ const child = frame.contentWindow;
+ assert_equals(child.document.readyState, 'complete', 'readyState is complete');
+ assert_array_equals(events, ["loading", "DOMContentLoaded", "stop", "complete"], 'no load event was fired');
+ events = [];
+ frame.src = "abort-document-load-2.html";
+
+ test.step_timeout(() => {
+ const child = frame.contentWindow;
+ assert_equals(child.document.readyState, 'complete', 'readyState is complete');
+ assert_array_equals(events, ["loading", "DOMContentLoaded", "stop", "complete"], 'no load event was fired');
+ test.done();
+ }, 1000);
+ }, 1000);
+});
+</script>
+<iframe src="abort-document-load-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js
new file mode 100644
index 0000000000..58b73494e1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js
@@ -0,0 +1,52 @@
+// META: title=Navigation to about:srcdoc, not via srcdoc="", must be blocked
+// META: script=../resources/helpers.js
+
+promise_test(async t => {
+ const iframe = await addSrcdocIframe();
+
+ iframe.contentWindow.location = "/common/blank.html";
+ await waitForIframeLoad(iframe);
+
+ iframe.contentWindow.location = "about:srcdoc";
+
+ // Fetching "about:srcdoc" should result in a network error, and navigating
+ // to a network error should produce an opaque-origin page. In particular,
+ // since the error page should end up being cross-origin to the parent
+ // frame, `contentDocument` should return `null`.
+ //
+ // If instead this results in a message because we re-loaded a srcdoc document
+ // from the contents of the srcdoc="" attribute, immediately fail.
+ await Promise.race([
+ t.step_wait(() => iframe.contentDocument === null),
+ failOnMessage(iframe.contentWindow)
+ ]);
+}, "Navigations to about:srcdoc via window.location must be blocked");
+
+promise_test(async t => {
+ const iframe = await addSrcdocIframe();
+
+ iframe.contentWindow.location = "about:srcdoc?query";
+
+ // See the documentation in the above test.
+ await Promise.race([
+ t.step_wait(() => iframe.contentDocument === null),
+ failOnMessage(iframe.contentWindow)
+ ]);
+}, "Navigations to about:srcdoc?query via window.location within an " +
+ "about:srcdoc document must be blocked");
+
+promise_test(async t => {
+ const iframe = await addSrcdocIframe();
+ iframe.contentWindow.name = "test_frame";
+
+ iframe.contentWindow.location = "/common/blank.html";
+ await waitForIframeLoad(iframe);
+
+ window.open("about:srcdoc", "test_frame");
+
+ // See the documentation in the above test.
+ await Promise.race([
+ t.step_wait(() => iframe.contentDocument === null),
+ failOnMessage(iframe.contentWindow)
+ ]);
+}, "Navigations to about:srcdoc via window.open() must be blocked");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html
new file mode 100644
index 0000000000..8b9802f589
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to fragment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which is just a fragment like "#",
+ then the form should be submitted. Many sites rely on this behavior. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()" href="#fragment">Test</a>
+
+<script>
+async_test(t => {
+ const anchor = document.getElementById('anchor');
+ t.step(() => anchor.click());
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html
new file mode 100644
index 0000000000..823174181e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to fragment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which is just a fragment like "#",
+ then the form should be submitted. Many sites rely on this behavior. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()">Test</a>
+
+<script>
+async_test(t => {
+ const iframe = document.querySelector('iframe');
+ iframe.onload = t.step_func(() => {
+ const anchor = document.getElementById('anchor');
+ anchor.href = '/#';
+ anchor.click();
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+ });
+
+ iframe.src = '/';
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html
new file mode 100644
index 0000000000..e0c0e6f82d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to fragment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which is just a fragment like "#",
+ then the form should be submitted. Many sites rely on this behavior. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()" href="#">Test</a>
+
+<script>
+async_test(t => {
+ const anchor = document.getElementById('anchor');
+ t.step(() => anchor.click());
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html
new file mode 100644
index 0000000000..1bf5d3595a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to javascript: url</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which returns undefined, then the form
+ should be submitted. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()" href="javascript:void(0)">Test</a>
+
+<script>
+async_test(t => {
+ const anchor = document.getElementById('anchor');
+ t.step(() => anchor.click());
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js
new file mode 100644
index 0000000000..c5bed0fd4c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js
@@ -0,0 +1,90 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=resources/wait-for-messages.js
+
+function testNavigationFails(params) {
+ return async (t) => {
+ // Start waiting for messages before inserting the child frame, to avoid any
+ // race conditions. Note that this would be racy if we executed tests
+ // concurrently, thankfully `promise_test` executes sequentially. See also:
+ // https://github.com/web-platform-tests/rfcs/pull/75
+ const messagesPromise = waitForMessages(1);
+
+ // Execute the test in an iframe, so that the document executing the test
+ // is not navigated away mid-test in case of failure.
+ const child = document.createElement("iframe");
+ document.body.appendChild(child);
+ t.add_cleanup(() => { document.body.removeChild(child); });
+
+ const url = new URL(
+ "resources/child-navigates-parent-cross-origin-inner.html",
+ window.location);
+
+ // Load the grandchild iframe from a different origin.
+ url.host = get_host_info().REMOTE_HOST;
+
+ for (const key in params || {}) {
+ url.searchParams.set(key, params[key]);
+ }
+
+ const grandchild = child.contentDocument.createElement("iframe");
+ grandchild.src = url;
+ child.contentDocument.body.appendChild(grandchild);
+
+ const messages = await messagesPromise;
+ assert_array_equals(messages, ["error: SecurityError"]);
+ }
+}
+
+promise_test(
+ testNavigationFails(),
+ "Child document attempts to navigate cross-origin parent via location");
+
+promise_test(
+ testNavigationFails({ "property": "hash" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.hash");
+
+promise_test(
+ testNavigationFails({ "property": "host" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.host");
+
+promise_test(
+ testNavigationFails({ "property": "hostname" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.hostname");
+
+promise_test(
+ testNavigationFails({ "property": "href" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.href");
+
+promise_test(
+ testNavigationFails({ "property": "pathname" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.pathname");
+
+promise_test(
+ testNavigationFails({ "property": "protocol" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.protocol");
+
+promise_test(
+ testNavigationFails({ "property": "reload" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.reload()");
+
+promise_test(
+ testNavigationFails({ "property": "replace" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.replace()");
+
+promise_test(
+ testNavigationFails({ "property": "search" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.search");
+
+promise_test(
+ testNavigationFails({ "property": "xxxNonExistent" }),
+ "Child document attempts to navigate cross-origin parent via non-standard "+
+ "location property");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js
new file mode 100644
index 0000000000..a40c412029
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js
@@ -0,0 +1,24 @@
+// META: script=resources/wait-for-messages.js
+
+function testNavigation(url) {
+ return async (t) => {
+ // Start waiting for messages before inserting the child frame, to avoid any
+ // race conditions.
+ const messagesPromise = waitForMessages(3);
+
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+ document.body.appendChild(iframe);
+
+ const messages = await messagesPromise;
+ assert_array_equals(messages, ["initial", "inner", "destination"]);
+ }
+}
+
+promise_test(
+ testNavigation("resources/child-navigates-parent-location-initial.html"),
+ "Child document navigates same-origin parent via document.location");
+
+promise_test(
+ testNavigation("resources/child-navigates-parent-submit-initial.html"),
+ "Child document navigates same-origin parent via form submission");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js
new file mode 100644
index 0000000000..42b4b208ee
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js
@@ -0,0 +1,8 @@
+async_test(t => {
+ addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data, 'Denied');
+ }));
+ const w = open("resources/page-with-top-navigating-iframe.html?parent_user_gesture=true");
+ t.add_cleanup(() => {w.close()});
+
+}, "Cross-origin top navigation is blocked without user activation, even if the parent has user activation");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js
new file mode 100644
index 0000000000..57f0bce247
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js
@@ -0,0 +1,8 @@
+async_test(t => {
+ addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data, 'Denied');
+ }));
+ const w = open("resources/page-with-top-navigating-iframe.html");
+ t.add_cleanup(() => {w.close()});
+
+}, "Cross-origin top navigation is blocked without user activation");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html
new file mode 100644
index 0000000000..b9108f9937
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>load event for empty iframe in relation to the event loop</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+setup({explicit_done:true});
+let ran = false;
+
+onload = function() {
+ let iframe = document.createElement("iframe");
+ iframe.onload = function() {
+ test(function() {
+ assert_equals(ran, false, 'Expected onload to run first');
+ }, "Check execution order on load handler");
+ if (ran) {
+ done();
+ } else {
+ ran = true;
+ }
+ };
+ document.body.appendChild(iframe);
+
+ // Nested timeout to accommodate Gecko, because the it seems
+ // the outer setTimeout takes its slot in the event queue right away
+ // but the load event task takes its slot only at the end of this script.
+ setTimeout(function() {
+ setTimeout(function() {
+ test(function() {
+ assert_equals(ran, true, 'Expected nested setTimeout to run second');
+ }, "Check execution order from nested timeout");
+ if (ran) {
+ done();
+ } else {
+ ran = true;
+ }
+ });
+ });
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html
new file mode 100644
index 0000000000..18a6f84c9f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Navigating to the same URL with an empty fragment aborts the navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe src="empty_fragment_iframe.html"></iframe>
+<script>
+// If the navigation were not aborted, we would expect multiple load events
+// as the page continually reloads itself.
+async_test(function(t) {
+ var count = 0;
+ var iframe = document.querySelector('iframe');
+ iframe.onload = t.step_func(function() {
+ count++;
+ });
+ window.child_succeeded = t.step_func_done(function() {
+ assert_equals(count, 1);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html
new file mode 100644
index 0000000000..26b28a0d7d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html
@@ -0,0 +1,11 @@
+<script>
+var timeout;
+onload = function() {
+ location.hash = "";
+ timeout = setTimeout(function() { parent.child_succeeded() }, 2000);
+};
+
+onbeforeunload = function() {
+ clearTimeout(timeout);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html
new file mode 100644
index 0000000000..5d7aa2b9cb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Sequence of the checks performed against a navigation response</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+'use strict';
+const collect = (win) => {
+ const report = new Promise((resolve) => {
+ if (!win.ReportingObserver) {
+ return;
+ }
+
+ const observer = new win.ReportingObserver(resolve);
+ observer.observe();
+ }).then((reports) => reports[0].type);
+ // Although CSP also makes use of ReportingObserver, monitoring this event
+ // allows the test to provide value to implementations that have not yet
+ // integrated CSP and Reporting (as of the time of this writing, Firefox and
+ // Safari).
+ const cspViolation = new Promise((resolve) => {
+ win.document.addEventListener('securitypolicyviolation', () => resolve('csp-violation'));
+ });
+ const halfASecond = new Promise((resolve) => setTimeout(() => resolve(null), 500));
+
+ return Promise.race([report, cspViolation, halfASecond]);
+};
+
+const createWindow = (t, url) => {
+ const win = open(url);
+ t.add_cleanup(() => win.close());
+ return new Promise((resolve) => win.onload = () => resolve(win));
+};
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(content-security-policy, frame-src none)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html?pipe=header(x-frame-options, deny)';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'csp-violation');
+}, 'CSP check precedes X-Frame-Options check');
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(content-security-policy, frame-src none)|header(cross-origin-embedder-policy, require-corp)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'csp-violation');
+}, 'CSP check precedes COEP check - CSP header first');
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(cross-origin-embedder-policy, require-corp)|header(content-security-policy, frame-src none)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'csp-violation');
+}, 'CSP check precedes COEP check - COEP header first');
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(cross-origin-embedder-policy, require-corp)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html?pipe=header(x-frame-options, deny)';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'coep');
+}, 'COEP check precedes X-Frame-Options check');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html
new file mode 100644
index 0000000000..b89d3bb54b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on iframe without src attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src and srcdoc attribute is not set, it will stay on the initial empty
+ document.
+ These tests verifies the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "/common/blank.html?1";
+const url2 = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through iframe.src. This
+ // should do a replacement.
+ iframe.src = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.src = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.href = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url1);
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.assign(url2);
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.open(url2, "_self");
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. This should do a replacement.
+ const a1 = iframe.contentDocument.createElement("a");
+ a1.href = url1;
+ iframe.contentDocument.body.appendChild(a1);
+ a1.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const a2 = iframe.contentDocument.createElement("a");
+ a2.href = url2;
+ iframe.contentDocument.body.appendChild(a2);
+ a2.click();
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // This should do a replacement.
+ const form1 = iframe.contentDocument.createElement("form");
+ form1.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form1);
+ const input1 = iframe.contentDocument.createElement("input");
+ input1.type = "hidden";
+ input1.name = "1";
+ form1.append(input1);
+ form1.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const form2 = iframe.contentDocument.createElement("form");
+ form2.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form2);
+ const input2 = iframe.contentDocument.createElement("input");
+ input2.type = "hidden";
+ input2.name = "2";
+ form2.append(input2);
+ form2.submit();
+ await waitForLoad(t, iframe, url2 + "=");
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html
new file mode 100644
index 0000000000..2a472f6a6d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Fragment navigations on iframe with src set to URL that doesn't load a document (HTTP 204)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src is set to a URL that doesn't load a new document (e.g. it results in a
+ HTTP 204 response), it will stay on the initial empty document. After fragment
+ navigations happen on it, it should still stay on the initial empty document.
+ These tests verifies the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "about:blank#1";
+const url2 = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through iframe.src.
+ iframe.src = url1;
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through iframe.src.
+ iframe.src = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through setting location.href.
+ // This should do a replacement.
+ iframe.contentWindow.location.href = url1;
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through setting location.href.
+ // This should do a replacement.
+ iframe.contentWindow.location.href = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url1);
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url2);
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url1, "_self");
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url2, "_self");
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through clicking an <a> element.
+ // This should do a replacement.
+ const a1 = iframe.contentDocument.createElement("a");
+ a1.href = url1;
+ iframe.contentDocument.body.appendChild(a1);
+ a1.click();
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through clicking an <a> element.
+ // This should do a replacement.
+ const a2 = iframe.contentDocument.createElement("a");
+ a2.href = url2;
+ iframe.contentDocument.body.appendChild(a2);
+ a2.click();
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "link click");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html
new file mode 100644
index 0000000000..8a97fd36a4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>pushState/replaceState on iframe with src set to URL that doesn't load a document (HTTP 204)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src is set to a URL that doesn't load a new document (e.g. it results in a
+ HTTP 204 response), it will stay on the initial empty document. If
+ history.pushState() or history.replaceState() is called on it, it should
+ still stay on the initial empty document.
+ These tests verifies the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const crossDocumentURL = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a history.pushState() to about:blank#foo.
+ let pushURL = "about:blank#foo";
+ iframe.contentWindow.history.pushState({}, "title", pushURL);
+ assert_equals(iframe.contentWindow.location.href, pushURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after history.pushState() on the initial empty document");
+
+ // Navigate away from the initial empty document. This should do replacement.
+ iframe.src = crossDocumentURL;
+ await waitForLoad(t, iframe, crossDocumentURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "history.pushState");
+
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a history.replaceState() to about:blank#foo.
+ let replaceURL = "about:blank#foo";
+ iframe.contentWindow.history.replaceState({}, "title", replaceURL);
+ assert_equals(iframe.contentWindow.location.href, replaceURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after history.replaceState() on the initial empty document");
+
+ // Navigate away from the initial empty document. This should do replacement.
+ iframe.src = crossDocumentURL;
+ await waitForLoad(t, iframe, crossDocumentURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "history.replaceState");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html
new file mode 100644
index 0000000000..f7bade68fb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on iframe with src set to URL that doesn't load a new document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src is set to a URL that doesn't load a new document (e.g. it results in a
+ HTTP 204 response), it will stay on the initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "/common/blank.html?1";
+const url2 = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through iframe.src. This
+ // should do a replacement.
+ iframe.src = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.src = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.href = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url1);
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.assign(url2);
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.open(url2, "_self");
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. This should do a replacement.
+ const a1 = iframe.contentDocument.createElement("a");
+ a1.href = url1;
+ iframe.contentDocument.body.appendChild(a1);
+ a1.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const a2 = iframe.contentDocument.createElement("a");
+ a2.href = url2;
+ iframe.contentDocument.body.appendChild(a2);
+ a2.click();
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // This should do a replacement.
+ const form1 = iframe.contentDocument.createElement("form");
+ form1.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form1);
+ const input1 = iframe.contentDocument.createElement("input");
+ input1.type = "hidden";
+ input1.name = "1";
+ form1.append(input1);
+ form1.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const form2 = iframe.contentDocument.createElement("form");
+ form2.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form2);
+ const input2 = iframe.contentDocument.createElement("input");
+ input2.type = "hidden";
+ input2.name = "2";
+ form2.append(input2);
+ form2.submit();
+ await waitForLoad(t, iframe, url2 + "=");
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html
new file mode 100644
index 0000000000..a75257d91c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations immediately after appending iframe with src='about:blank'</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created with src="about:blank", it will stay on the initial
+ empty document. These tests verify the behavior of navigations that happen
+ immediately after the iframe is created, which should result in replacement.
+*/
+"use strict";
+const url1 = "/common/blank.html?1";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank. This would trigger a
+ // navigation to a non-initial about:blank document.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Trigger a navigation to url1 through the iframe's src attribute.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.src = url1;
+ // Wait for the latest navigation to finish.
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. The iframe should still be on the initial empty document,
+ // and the navigation should do replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through location.assign().
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.contentWindow.location.assign(url1);
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const a = iframe.contentDocument.createElement("a");
+ a.href = url1;
+ iframe.contentDocument.body.appendChild(a);
+ a.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const form = iframe.contentDocument.createElement("form");
+ form.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form);
+ const input = iframe.contentDocument.createElement("input");
+ input.type = "hidden";
+ input.name = "1";
+ form.append(input);
+ form.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html
new file mode 100644
index 0000000000..b7066ce521
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations after iframe with src='about:blank' finished loading</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+ /*
+ When an iframe is created with src="about:blank", it will stay on the initial
+ empty document. These tests verify the behavior of navigations that happen
+ immediately after the load event is fired on the iframe element, which
+ should result in replacement.
+ */
+"use strict";
+const url1 = "/common/blank.html?1";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Trigger a navigation to url1 through the iframe's src attribute.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.src = url1;
+ // Wait for the latest navigation to finish.
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. The iframe should still be on the initial empty document,
+ // and the navigation should do replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+ }, "Navigating to a different document with location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. The iframe should still be on the initial empty document,
+ // and the navigation should do replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const a = iframe.contentDocument.createElement("a");
+ a.href = url1;
+ iframe.contentDocument.body.appendChild(a);
+ a.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank which will commit an about:blank document that is not the initial empty document, and wait for it to load.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const form = iframe.contentDocument.createElement("form");
+ form.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form);
+ const input = iframe.contentDocument.createElement("input");
+ input.type = "hidden";
+ input.name = "1";
+ form.append(input);
+ form.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html
new file mode 100644
index 0000000000..44f890df6c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>
+ Content synchronously added to iframe/opened window's document after creation
+ won't get replaced asynchronously when staying on the initial empty document
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+// Asserts the document on |w| stays the same after waiting 100ms.
+function assertDocumentStaysTheSame(t, w) {
+ const initialDocument = w.document;
+ initialDocument.body.innerHTML = "foo";
+ return new Promise((resolve) => {
+ t.step_timeout(() => {
+ assert_equals(w.document.body.innerHTML, "foo");
+ assert_equals(w.document, initialDocument);
+ resolve();
+ }, 100);
+ });
+}
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with no src won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='' won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='about:blank' won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank#foo";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='about:blank#foo' won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank?foo";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='about:blank?foo' won't get replaced");
+
+promise_test(async t => {
+ const w = window.open();
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open()-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('')-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("about:blank");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('about:blank')-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("about:blank#foo");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('about:blank#foo')-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("about:blank?foo");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('about:blank?foo')-ed document won't get replaced");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html
new file mode 100644
index 0000000000..0d19770cc1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>"load" event fires on the iframe element when loading the initial empty document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with no src");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src=''");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src='about:blank'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank#foo";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src='about:blank#foo'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank?foo";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src='about:blank?foo'");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html
new file mode 100644
index 0000000000..4aea4aac81
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>"load" & "pageshow" events do not fire on contentWindow of iframe that stays on the initial empty document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow event do not fire on contentWindow of <iframe> element created with no src");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src=''");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank#foo";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank#foo'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank?foo";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank?foo'");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html
new file mode 100644
index 0000000000..9703502f7f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>"load" and "pageshow" events don't fire on window.open() that stays on the initial empty document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const w = window.open();
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open()");
+
+promise_test(async t => {
+ const w = window.open("");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('')");
+
+promise_test(async t => {
+ const w = window.open("about:blank");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('about:blank')");
+
+promise_test(async t => {
+ const w = window.open("about:blank#foo");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('about:blank#foo')");
+
+promise_test(async t => {
+ const w = window.open("about:blank?foo");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('about:blank?foo')");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html
new file mode 100644
index 0000000000..631b95f9ed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe</title>
+
+<script>
+"use strict";
+{{GET[code]}}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js
new file mode 100644
index 0000000000..8d9473a949
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js
@@ -0,0 +1,121 @@
+// Returns a promise that asserts the "load" and "pageshow" events are not
+// fired on |target|.
+function assertNoLoadAndPageshowEvent(t, target) {
+ target.addEventListener("load", t.unreached_func("load should not be fired"));
+ target.addEventListener("pageshow", t.unreached_func("pageshow should not be fired"));
+ return new Promise(resolve => {
+ // Wait 50ms to ensure events fired after asynchronous navigations are
+ // also captured.
+ setTimeout(resolve, 50);
+ });
+}
+
+const url204 = "/common/blank.html?pipe=status(204)";
+const postMessageToOpenerOnLoad = `
+ window.onload = () => {
+ window.opener.postMessage("loaded", "*")
+ }
+ `;
+
+// -- Start of helpers for iframe initial empty document tests.
+
+// Creates an iframe with an unset src and appends it to the document.
+window.insertIframe = (t) => {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+ document.body.append(iframe);
+ return iframe;
+};
+
+// Creates an iframe with src set to a URL that doesn't commit a new document
+// (results in a HTTP 204 response) and appends it to the document.
+window.insertIframeWith204Src = (t) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = url204;
+ t.add_cleanup(() => iframe.remove());
+ document.body.append(iframe);
+ return iframe;
+};
+
+// Creates an iframe with src="about:blank" and appends it to the document.
+window.insertIframeWithAboutBlankSrc = (t) => {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "about:blank";
+ document.body.append(iframe);
+ return iframe;
+};
+
+// Creates an iframe with src="about:blank", appends it to the document, and
+// waits for the non-initial about:blank document finished loading.
+window.insertIframeWithAboutBlankSrcWaitForLoad = async (t) => {
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ const aboutBlankLoad = new Promise(resolve => {
+ // In some browsers, the non-initial about:blank navigation commits
+ // asynchronously, while in other browsers, it would commit synchronously.
+ // This means we can't wait for the "load" event as it might have already
+ // ran. Instead, just wait for 100ms before resolving, as the non-initial
+ // about:blank navigation will most likely take less than 100 ms to commit.
+ t.step_timeout(resolve, 100);
+ });
+ await aboutBlankLoad;
+ return iframe;
+};
+
+// Waits for the "load" event for |urlRelativeToThisDocument| to run on
+// |iframe|.
+window.waitForLoad = (t, iframe, urlRelativeToThisDocument) => {
+ return new Promise(resolve => {
+ iframe.addEventListener("load", t.step_func(() => {
+ assert_equals(iframe.contentWindow.location.href, (new URL(urlRelativeToThisDocument, location.href)).href);
+
+ // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded"
+ // (which happens from a queued task).
+ setTimeout(resolve, 0);
+ }), { once: true });
+ });
+};
+
+// -- End of helpers for iframe initial empty document tests.
+
+// -- Start of helpers for opened windows' initial empty document tests.
+
+// window.open() to a URL that doesn't load a new document (results in a HTTP
+// 204 response). This should create a new window that stays on the initial
+// empty document.
+window.windowOpen204 = (t) => {
+ const openedWindow = window.open(url204);
+ t.add_cleanup(() => openedWindow.close());
+ return openedWindow;
+};
+
+// window.open() (no URL set). This should create a new window that stays on
+// the initial empty document as it won't trigger a non-initial about:blank
+// navigation.
+window.windowOpenNoURL = (t) => {
+ const openedWindow = window.open();
+ t.add_cleanup(() => openedWindow.close());
+ return openedWindow;
+};
+
+// window.open("about:blank"). This should create a new window that stays on
+// the initial empty document as it won't trigger a non-initial about:blank
+// navigation.
+window.windowOpenAboutBlank = (t) => {
+ const openedWindow = window.open("about:blank");
+ t.add_cleanup(() => openedWindow.close());
+ return openedWindow;
+};
+
+// Waits for a postMessage with data set to |message| is received on the current
+// window.
+window.waitForMessage = (t, message) => {
+ return new Promise(resolve => {
+ window.addEventListener("message", t.step_func((event) => {
+ if (event.data == message)
+ resolve();
+ }));
+ });
+};
+
+// -- End of helpers for opened windows' initial empty document tests.
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html
new file mode 100644
index 0000000000..bb47cd3820
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Fragment navigation on initial empty document created through window.open(url-with-204-response)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to the URL that doesn't load a
+ new document (e.g. it results in a HTTP 204 response), it will stay on the
+ initial empty document. If fragment navigations happen, it will still stay on
+ the initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "about:blank#foo";
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a fragment navigation within the initial empty document through setting location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url1;
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(openedWindow.location.hash, "#foo");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through setting location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a fragment navigation within the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(openedWindow.location.hash, "#foo");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.assign");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html
new file mode 100644
index 0000000000..b5382b189f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Fragment navigation on initial empty document created through window.open(url-with-204-response)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to the URL that doesn't load a
+ new document (e.g. it results in a HTTP 204 response), it will stay on the
+ initial empty document. If history.pushState() or history.replaceState() is
+ called on it, it should still stay on the initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "about:blank#foo";
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a history.pushState() to about:blank#foo.
+ let pushURL = "about:blank#foo";
+ openedWindow.history.pushState({}, "title", pushURL);
+ assert_equals(openedWindow.location.href, pushURL);
+ assert_equals(history.length, 1,
+ "history.length must not change after history.pushState() on the initial empty document");
+
+ // Navigate away from the initial empty document through setting location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "history.pushState");
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a history.pushState() to about:blank#foo.
+ let replaceURL = "about:blank#foo";
+ openedWindow.history.replaceState({}, "title", replaceURL);
+ assert_equals(openedWindow.location.href, replaceURL);
+ assert_equals(history.length, 1,
+ "history.length must not change after history.replaceState() on the initial empty document");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "history.replaceState");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html
new file mode 100644
index 0000000000..6461b3c8fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on window.open() with URL that doesn't load a new document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to the URL that doesn't load a
+ new document (e.g. it results in a HTTP 204 response), it will stay on the
+ initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ openedWindow.location.href = url1;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.assign");
+/*
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Navigate away from the initial empty document through setting
+ // window.open(). This should do a replacement.
+ openedWindow.open(url1, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should increase after normal navigation away from non-initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.open(url2, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "window.open");
+*/
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html
new file mode 100644
index 0000000000..f3033d6a21
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on window.open(about:blank) after waiting for it to load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to about:blank, it will stay on
+ the initial empty document (unlike iframes with src="about:blank", which will
+ start a navigation to a non-initial about:blank document).
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a window with URL about:blank, which will commit the
+ // initial empty document and stay on it.
+ const openedWindow = windowOpenAboutBlank(t);
+
+ // Unlike iframe with src="about:blank", window.open("about:blank") won't
+ // trigger a navigation to a non-initial about:blank document, so it should
+ // stay on the initial empty document. To verify, wait for 100ms before
+ // continuing.
+ await new Promise((resolve) => t.step_timeout(resolve, 100));
+
+ // Navigate away from the initial empty document through location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url1;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a window with URL about:blank, which will commit the
+ // initial empty document and stay on it.
+ const openedWindow = windowOpenAboutBlank(t);
+
+ // Unlike iframe with src="about:blank", window.open("about:blank") won't
+ // trigger a navigation to a non-initial about:blank document, so it should
+ // stay on the initial empty document. To verify, wait for 100ms before
+ // continuing.
+ await new Promise((resolve) => t.step_timeout(resolve, 100));
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.assign");
+/*
+promise_test(async t => {
+ // Open a window with URL about:blank, which will commit the
+ // initial empty document and stay on it.
+ const openedWindow = windowOpenAboutBlank(t);
+
+ // Unlike iframe with src="about:blank", window.open("about:blank") won't
+ // trigger a navigation to a non-initial about:blank document, so it should
+ // stay on the initial empty document. To verify, wait for 100ms before
+ // continuing.
+ await new Promise((resolve) => t.step_timeout(resolve, 100));
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ openedWindow.open(url1, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "window.open");
+*/
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html
new file mode 100644
index 0000000000..ab89bc4098
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>history.length value on window.open()-ed window</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document, and the history.length value should be 1.
+*/
+
+promise_test(async t => {
+ const openedWindow = windowOpenNoURL(t);
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should start at 1 for newly opened window");
+}, "Starting history.length for window.open()");
+
+promise_test(async t => {
+ const openedWindow = windowOpenAboutBlank(t);
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should start at 1 for newly opened window");
+}, "Starting history.length for window.open(about:blank)");
+
+promise_test(async t => {
+ const openedWindow = windowOpen204(t);
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should start at 1 for newly opened window");
+}, "Starting history.length for window.open(url-with-204-response)");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html
new file mode 100644
index 0000000000..801f77ad48
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on window.open() to URL that doesn't load a new document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is not set, it will stay on the initial
+ empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" + encodeURIComponent(postMessageToOpenerOnLoad);
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" + encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with no URL, which will stay in the initial empty document until the navigation below.
+ const openedWindow = windowOpenNoURL(t);
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ openedWindow.location.href = url1;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a new window with no URL, which will stay in the initial empty document until the navigation below.
+ const openedWindow = windowOpenNoURL(t);
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.assign");
+/*
+promise_test(async t => {
+ // Open a new window with no URL, which will stay in the initial empty document until the navigation below.
+ const openedWindow = windowOpenNoURL(t);
+
+ // Navigate away from the initial empty document through setting
+ // window.open(). This should do a replacement.
+ openedWindow.open(url1, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.open(url2, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "window.open");
+*/
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html
new file mode 100644
index 0000000000..f626a79ae6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Aborting fetch for javascript:string navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigate">
+<link rel="help" href="https://github.com/whatwg/html/issues/2590">
+<div id="log"></div>
+<iframe src="support/iframe-and-links.html"></iframe>
+<script>
+async_test(test => {
+ onload = () => {
+ const child = document.querySelector('iframe').contentWindow;
+ child.document.querySelector("#slowLink").click();
+ // The step below is in a timeout. The framed page can't communicate back mid-parse because that
+ // would involve running script, which makes that navigation "mature", and we need to do this
+ // before it matures.
+ test.step_timeout(() => {
+ child.document.querySelector("#javascriptStringLink").click();
+ child.document.querySelector("iframe").onload = test.step_func_done(() => {
+ assert_false(child.childLoaded, 'child.childLoaded');
+ });
+ }, 100);
+ };
+ window.javascriptStringDocLoaded = test.step_func(() => {
+ assert_unreached("javascript: URL doc replaced the document, should be targeted to child iframe.");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html
new file mode 100644
index 0000000000..80a0d27a7d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Not aborting fetch for javascript:undefined navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigate">
+<link rel="help" href="https://github.com/whatwg/html/issues/2590">
+<div id="log"></div>
+<iframe src="support/iframe-and-links.html"></iframe>
+<script>
+async_test(test => {
+ onload = () => {
+ const child = document.querySelector('iframe').contentWindow;
+ child.document.querySelector("#slowLink").click();
+ // The step below is in a timeout. The framed page can't communicate back mid-parse because that
+ // would involve running script, which makes that navigation "mature", and we need to do this
+ // before it matures.
+ test.step_timeout(() => {
+ child.document.querySelector("#javascriptUndefinedLink").click();
+ child.document.querySelector("iframe").onload = test.step_func_done(() => {
+ assert_true(child.childLoaded, 'child.childLoaded');
+ });
+ }, 100);
+ };
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html
new file mode 100644
index 0000000000..545b0988ba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html
@@ -0,0 +1,18 @@
+<!doctype html>
+
+<iframe name="iframe"></iframe>
+
+<!-- slow link's response is delayed by 1 second -->
+<!-- https://wptserve.readthedocs.io/en/latest/pipes.html#trickle -->
+<a target="iframe" href="set-child-loaded.html?pipe=trickle(d1)" id="slowLink">slow link</a>
+<a target="iframe" href="javascript:'javascript:string <script> parent.javascriptStringDocLoaded(); </script>'" id="javascriptStringLink">javascript:string link</a>
+<a target="iframe" href="javascript:undefined" id="javascriptUndefinedLink">javascript:undefined link</a>
+
+<script>
+// set-child-loaded.html (the slow link) sets this to true.
+window.childLoaded = false;
+
+// Do nothing when the javascript:string doc has loaded, if it's correctly targeted to the above iframe.
+// However, if it replaces this document, it needs to fail the test (handled in the parent).
+function javascriptStringDocLoaded() {}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html
new file mode 100644
index 0000000000..a4b34ad6e9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html
@@ -0,0 +1,5 @@
+<!doctype html>
+set-child-loaded.html
+<script>
+parent.childLoaded = true;
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html
new file mode 100644
index 0000000000..d3dd38ebed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<a id="javascript-link" href="javascript:changeStatus()">link</a>
+
+<script>
+function changeStatus() {
+ t.done();
+}
+
+var t = async_test(function(t) {
+ document.querySelector("#javascript-link").click();
+}, "javascript: scheme urls should be executed in current global scope");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml
new file mode 100644
index 0000000000..b23bef5917
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/helpers.js"></script>
+ <meta charset="windows-1250"/>
+ <title>javascript: URL navigation to a string must create a HTML document using the correct properties</title>
+</head>
+<body>
+ <!--
+ This document is XHTML and windows-1250 so that we can test the resulting javascript: URL document is not.
+ The same for the window we open.
+ -->
+ <script><![CDATA[
+ promise_test(async (t) => {
+ const w = await openWindow("resources/xhtml-and-non-utf-8.xhtml", t);
+
+ w.location.href = `javascript:'a string<script>
+ opener.postMessage({
+ compatMode: document.compatMode,
+ contentType: document.contentType,
+ characterSet: document.characterSet,
+ doctypeIsNull: document.doctype === null
+ }, "*");
+ <` + `/script>'`;
+
+ const results = await waitForMessage(w);
+
+ assert_equals(results.compatMode, "BackCompat");
+ assert_equals(results.contentType, "text/html");
+ assert_equals(results.characterSet, "UTF-8");
+ assert_equals(results.doctypeIsNull, true);
+ });
+ ]]></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js
new file mode 100644
index 0000000000..47e8f11797
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js
@@ -0,0 +1,80 @@
+// META: script=../resources/helpers.js
+
+for (const stringCompletion of [false, true]) {
+ const testNameSuffix = stringCompletion ? ": string completion" : ": undefined completion";
+
+ testNoBeforeunload(
+ { testRunnerWindow: "top", stringCompletion },
+ async (t, urlToSet) => {
+ const iframe = await addIframe();
+ iframe.contentWindow.location.href = urlToSet;
+
+ return iframe.contentWindow;
+ },
+ `Navigating an iframe via location.href to a javascript: URL must not fire beforeunload${testNameSuffix}`
+ );
+
+ testNoBeforeunload(
+ { testRunnerWindow: "top", stringCompletion },
+ async (t, urlToSet) => {
+ const iframe = await addIframe();
+ iframe.src = urlToSet;
+
+ return iframe.contentWindow;
+ },
+ `Navigating an iframe via src="" to a javascript: URL after insertion must not fire beforeunload${testNameSuffix}`
+ );
+
+ testNoBeforeunload(
+ { testRunnerWindow: "opener", stringCompletion },
+ async (t, urlToSet) => {
+ const w = await openWindow("/common/blank.html", t);
+ w.location.href = urlToSet;
+
+ return w;
+ },
+ `Navigating an opened window via location.href to a javascript: URL must not fire beforeunload${testNameSuffix}`
+ );
+
+
+ testNoBeforeunload(
+ { testRunnerWindow: "opener", stringCompletion },
+ async (t, urlToSet) => {
+ const w = await openWindow("../resources/has-iframe.html", t);
+ w.frames[0].onbeforeunload = t.unreached_func("beforeunload must not fire on the iframe");
+ w.location.href = urlToSet;
+
+ return w;
+ },
+ `Navigating an opened window with an iframe via location.href to a javascript: URL must not fire beforeunload on the iframe${testNameSuffix}`
+ );
+}
+
+function testNoBeforeunload({ testRunnerWindow, stringCompletion }, setupAndNavigateFunc, description) {
+ promise_test(async t => {
+ t.add_cleanup(() => {
+ delete window.resolveTestPromise;
+ });
+
+ const ranPromise = new Promise(resolve => {
+ window.resolveTestPromise = resolve;
+ });
+
+ const urlToSet = makeURL({ testRunnerWindow, stringCompletion });
+ const w = await setupAndNavigateFunc(t, urlToSet);
+ w.onbeforeunload = t.unreached_func("beforeunload must not fire");
+
+ await ranPromise;
+ if (stringCompletion) {
+ await waitForMessage(w);
+ }
+ }, description);
+}
+
+function makeURL({ testRunnerWindow, stringCompletion }) {
+ const completion = stringCompletion ?
+ `"a string<script>window.${testRunnerWindow}.postMessage('ready', '*');</script>";` :
+ `undefined;`;
+
+ return `javascript:window.${testRunnerWindow}.resolveTestPromise();${completion};`;
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html
new file mode 100644
index 0000000000..eced9646e5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title> javascript url with query and fragment components </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var a = null;
+var b = null;
+var c = null;
+</script>
+
+<iframe id="a" src='javascript:"nope" ? "yep" : "what";'></iframe>
+<iframe id="b" src='javascript:"wrong"; // # %0a "ok";'></iframe>
+<iframe id="c" src='javascript:"%252525 ? %252525 # %252525"'></iframe>
+
+<script>
+var t = async_test("iframes with javascript src");
+function check(id, expected) {
+ assert_equals(
+ document.getElementById(id).contentDocument.body.textContent,
+ expected);
+}
+onload = t.step_func(function() {
+ check("a", "yep");
+ check("b", "ok");
+ check("c", "%2525 ? %2525 # %2525");
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js
new file mode 100644
index 0000000000..1f11429c9e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js
@@ -0,0 +1,38 @@
+// META: script=../resources/helpers.js
+// META: title=javascript: URL navigation to a string must create a document whose referrer is the navigation initiator
+
+const originalURL = location.href;
+
+const testCases = [
+ ["unsafe-url", location.href],
+ ["origin", self.origin + "/"],
+ ["no-referrer", ""]
+];
+
+for (const [referrerPolicyForStartingWindowCreation, expectedReferrer] of testCases) {
+ promise_test(async (t) => {
+ const meta = document.createElement("meta");
+ meta.name = "referrer";
+ meta.content = referrerPolicyForStartingWindowCreation;
+ t.add_cleanup(() => meta.remove());
+ document.head.append(meta);
+
+ const w = await openWindow("/common/blank.html", t);
+ const originalReferrer = w.document.referrer;
+ assert_equals(originalReferrer, expectedReferrer,
+ "Sanity check: opened window's referrer is set correctly");
+
+ // Mess with the current document's URL so that the initiator URL is different. Then, if that
+ // shows up as the javascript: URL document's referrer, we know the navigation initiator's URL is
+ // being used as the referrer, which is incorrect.
+ history.replaceState(undefined, "", "/incorrect-referrer.html");
+ t.add_cleanup(() => history.replaceState(undefined, "", originalURL));
+
+ w.location.href = `javascript:'a string<script>opener.postMessage(document.referrer, "*");</script>'`;
+
+ const referrer = await waitForMessage(w);
+
+ assert_equals(referrer, originalReferrer,
+ "javascript: URL-created document's referrer equals the previous document's referrer");
+ }, `${referrerPolicyForStartingWindowCreation} referrer policy used to create the starting page`);
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html
new file mode 100644
index 0000000000..3c08d29674
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=windows-1252> <!-- intentionally not UTF-8 to test that the javascript: frames are forced to UTF-8 -->
+<title>Test javascript URL string return values in direct and indirect (target) frame contexts.</title>
+<!-- Waiting on https://github.com/whatwg/html/pull/6781 to be non-tentative. -->
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+const testCases = [
+ [[0x41]],
+ [[0x80,0xFF]],
+ [[0x80,0xFF,0x100]],
+ [[0xD83D,0xDE0D]],
+ [[0xDE0D,0x41], [0xFFFD,0x41]]
+];
+
+function formatCharCodes(charCodes) {
+ return charCodes.map(code => code.toString(16).toUpperCase().padStart(4, '0')).join(" ");
+}
+
+for (const [input, expected = input] of testCases) {
+ const javascriptURL = `javascript:String.fromCharCode(${input})`;
+ const output = String.fromCharCode(...expected);
+
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = javascriptURL;
+
+ t.step_timeout(() => {
+ assert_equals(frame.contentDocument.body.textContent, output);
+ assert_equals(frame.contentDocument.charset, "UTF-8");
+ t.done();
+ }, 200);
+
+ document.body.appendChild(frame);
+ }, `${formatCharCodes(input)} set in src=""`);
+
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ const href = document.createElement("a");
+ t.add_cleanup(() => { frame.remove(); href.remove(); });
+ frame.name = "hi" + input;
+ href.target = "hi" + input;
+ href.href = javascriptURL;
+
+ t.step_timeout(() => {
+ assert_equals(frame.contentDocument.body.textContent, output);
+ assert_equals(frame.contentDocument.charset, "UTF-8");
+ t.done();
+ }, 200)
+
+ document.body.appendChild(frame);
+ document.body.appendChild(href);
+ href.click();
+ }, `${formatCharCodes(input)} set in href="" targeting a frame and clicked`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html
new file mode 100644
index 0000000000..621a8cbaec
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that javascript: evaluation only performs a navigation to the
+ result when the result is a string value.</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe src="javascript:'1'"></iframe>
+<iframe src="javascript:1"></iframe>
+<iframe src="javascript:({ toString: function() { return '1'; } })"></iframe>
+<iframe src="javascript:undefined"></iframe>
+<iframe src="javascript:null"></iframe>
+<iframe src="javascript:true"></iframe>
+<iframe src="javascript:new String('1')"></iframe>
+<script>
+ var t = async_test();
+ onload = t.step_func_done(function() {
+ assert_equals(frames[0].document.documentElement.textContent,
+ "1", "string return should cause navigation");
+ // The rest of the test is disabled for now, until
+ // https://github.com/whatwg/html/issues/1895 gets sorted out
+/*
+ assert_equals(frames[1].document.documentElement.textContent,
+ "", "number return should not cause navigation");
+ assert_equals(frames[2].document.documentElement.textContent,
+ "", "object return should not cause navigation");
+ assert_equals(frames[3].document.documentElement.textContent,
+ "", "undefined return should not cause navigation");
+ assert_equals(frames[4].document.documentElement.textContent,
+ "", "null return should not cause navigation");
+ assert_equals(frames[5].document.documentElement.textContent,
+ "", "null return should not cause navigation");
+ assert_equals(frames[6].document.documentElement.textContent,
+ "", "String object return should not cause navigation");
+*/
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html
new file mode 100644
index 0000000000..a153ad3e48
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>javascript: URL security check</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+const cases = [
+ ["cross-origin", "http://{{hosts[][www]}}:{{ports[http][0]}}/common/blank.html"],
+ ["cross-origin-domain but same-origin", "/html/browsers/windows/resources/document-domain-setter.html"]
+];
+
+for (const [description, url] of cases) {
+ promise_test(async t => {
+ const iframe = await insertIframe(t, url);
+
+ const unreached = t.unreached_func("message event fired");
+ t.add_cleanup(() => window.removeEventListener("message", unreached));
+ window.addEventListener("message", unreached);
+
+ iframe.src = `javascript:parent.postMessage("boo", "*")`;
+
+ // If no message was received after this time, the test passes.
+ await new Promise(r => t.step_timeout(r, 50));
+ }, `${description}, setting src`);
+
+ promise_test(async t => {
+ const iframe = await insertIframe(t, url);
+
+ const unreached = t.unreached_func("message event fired");
+ t.add_cleanup(() => window.removeEventListener("message", unreached));
+ window.addEventListener("message", unreached);
+
+ iframe.contentWindow.location.href = `javascript:parent.postMessage("boo", "*")`;
+
+ // If no message was received after this time, the test passes.
+ await new Promise(r => t.step_timeout(r, 50));
+ }, `${description}, setting location.href`);
+}
+
+function insertIframe(t, url) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(new Error("Failed to load the outer iframe"));
+
+ t.add_cleanup(() => iframe.remove());
+
+ document.body.append(iframe);
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html
new file mode 100644
index 0000000000..4b9d3b7afa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Multi-globals: which one is the initiator for the javascript: URL security check?</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+document.domain = "{{hosts[][]}}";
+
+// These tests would fail if a different pair of origins were compared (see, e.g., the discussion in
+// https://github.com/whatwg/html/issues/6514).
+
+promise_test(async t => {
+ const iframe = await insertIframe(t);
+ const innerIframe = iframe.contentDocument.querySelector("iframe");
+
+ // - incumbentNavigationOrigin = this page's origin, http://{{hosts[][]}}:{{ports[http][0]}}
+ // - iframe's current origin is this origin, http://{{hosts[][]}}:{{ports[http][0]}}.
+ // javascript:'s security check uses incumbentNavigationOrigin vs. the iframe's current origin
+ // so the check will pass and the result will get written.
+ innerIframe.src = "javascript:'test'";
+
+ await waitForLoad(innerIframe, "Failed to load the javascript: URL");
+
+ assert_equals(innerIframe.contentDocument.body.textContent, "test");
+}, "Using iframeEl.src");
+
+promise_test(async t => {
+ const iframe = await insertIframe(t);
+ const innerIframe = iframe.contentDocument.querySelector("iframe");
+
+ // Here, https://html.spec.whatwg.org/#location-object-navigate sets the source browsing context to the
+ // incumbent settings object's browsing context. So incumbentNavigationOrigin = this page's origin,
+ // http://{{hosts[][]}}:{{ports[http][0]}}.
+ //
+ // So again, the check will pass.
+
+ iframe.contentWindow.frames[0].location.href = "javascript:'test'";
+
+ await waitForLoad(innerIframe, "Failed to load the javascript: URL");
+
+ assert_equals(innerIframe.contentDocument.body.textContent, "test");
+}, "Using location.href");
+
+function insertIframe(t) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "http://{{hosts[][www]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html";
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(new Error("Failed to load the outer iframe"));
+
+ t.add_cleanup(() => iframe.remove());
+
+ document.body.append(iframe);
+ });
+}
+
+function waitForLoad(iframe, errorMessage = "Failed to load iframe") {
+ return new Promise((resolve, reject) => {
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(new Error(errorMessage));
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html
new file mode 100644
index 0000000000..a14a13cfd6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>javascript: URL security check for same-origin-domain but not same-origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="http://{{hosts[][www]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html"></iframe>
+<script>
+"use strict";
+document.domain = "{{host}}";
+
+setup({ explicit_done: true });
+
+window.onload = () => {
+ async_test(t => {
+ assert_equals(frames[0].document.body.textContent, "", "before");
+
+ window.onmessage = t.step_func_done(() => {
+ assert_equals(frames[0].document.body.textContent, "new", "after");
+ });
+
+ frames[0].location.href = "javascript:parent.postMessage('done', '*'); 'new';";
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html
new file mode 100644
index 0000000000..1bb05bfb19
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>javascript: URL task queuing</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/3730">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+testIsAsync(() => {
+ const iframe = document.createElement("iframe");
+ document.body.append(iframe);
+ iframe.contentWindow.location.href = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();";
+}, `Navigating an iframe via location.href to a javascript: URL must queue a task`);
+
+testIsAsync(() => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();";
+ document.body.append(iframe);
+}, `Navigating an iframe via src="" to a javascript: URL before insertion must queue a task`);
+
+testIsAsync(() => {
+ const iframe = document.createElement("iframe");
+ document.body.append(iframe);
+ iframe.src = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();";
+}, `Navigating an iframe via src="" to a javascript: URL after insertion must queue a task`);
+
+testIsAsync(() => {
+ const w = window.open();
+ w.location.href = "javascript:window.opener.javascriptURLRan = true; window.opener.resolveTestPromise();";
+}, `Navigating an opened window via location.href to a javascript: URL must queue a task`);
+
+testIsAsync(() => {
+ window.open("javascript:window.opener.javascriptURLRan = true; window.opener.resolveTestPromise();");
+}, `Navigating an opened window as part of creation to a javascript: URL must queue a task`);
+
+function testIsAsync(setupFunc, description) {
+ promise_test(async t => {
+ t.add_cleanup(() => {
+ delete window.resolveTestPromise;
+ delete window.javascriptURLRan;
+ });
+
+ const ranPromise = new Promise(resolve => {
+ window.resolveTestPromise = resolve;
+ });
+
+ setupFunc();
+
+ assert_equals(window.javascriptURLRan, undefined, "Must not run sync");
+
+ // Ensure that we do actually run the code, though.
+ await ranPromise;
+ }, description);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html
new file mode 100644
index 0000000000..cb2984d409
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun = 'relevantWindow.location.assign("target.html");';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#dom-location-assign
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the incumbent in
+ // https://html.spec.whatwg.org/C/#location-object-navigate
+ assert_equals(
+ e.data.referrer, incumbentUrl,
+ 'Referrer should use the incumbent settings object');
+ }));
+}, 'Fetch client and URL resolution for location.assign()');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html
new file mode 100644
index 0000000000..02ff214ed5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun = 'relevantWindow.location.href = "target.html";';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#dom-location-href
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the incumbent in
+ // https://html.spec.whatwg.org/C/#location-object-navigate
+ assert_equals(
+ e.data.referrer, incumbentUrl,
+ 'Referrer should use the incumbent settings object');
+ }));
+}, 'Fetch client and URL resolution for location.href setter');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html
new file mode 100644
index 0000000000..fae17dd2ac
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun = 'relevantWindow.location = "target.html";';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#dom-location-assign
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the incumbent in
+ // https://html.spec.whatwg.org/C/#location-object-navigate
+ assert_equals(
+ e.data.referrer, incumbentUrl,
+ 'Referrer should use the incumbent settings object');
+ }));
+}, 'Fetch client and URL resolution for location setter');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html
new file mode 100644
index 0000000000..0a391ef28e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun =
+ 'relevantWindow.open("target.html", "target");';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#window-open-steps
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the entry in
+ // https://html.spec.whatwg.org/C/#window-open-steps
+ assert_equals(
+ e.data.referrer, entryUrl,
+ 'Referrer should use the entry settings object');
+ }));
+}, 'Fetch client and URL resolution for window.open()');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html
new file mode 100644
index 0000000000..82fecfdd38
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html
@@ -0,0 +1,4 @@
+<body onload="top.go()">
+<iframe id="incumbent" src="../incumbent/empty.html"></iframe>
+<iframe id="relevant" src="../relevant/empty.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html
new file mode 100644
index 0000000000..5ceaeaf07e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="../resources/target.js"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html
new file mode 100644
index 0000000000..5ceaeaf07e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="../resources/target.js"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html
new file mode 100644
index 0000000000..5ceaeaf07e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="../resources/target.js"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js
new file mode 100644
index 0000000000..dda338b4cc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js
@@ -0,0 +1,34 @@
+// Usage: in the top-level Window, include:
+//
+// <script src="resources/context-helper.js"></script>
+// <script>
+// window.scriptToRun = '...';
+// </script>
+// <iframe id="entry" src="entry/entry.html"></iframe>
+//
+// Then `scriptToRun` is evaluated, with:
+// - The entry Realm is that of entry/entry.html
+// - The incumbent Realm is that of incumbent/empty.html
+// - The relevant Realm of `relevantWindow`, `relevantWindow.location` etc. is
+// that of relevant/empty.html
+
+window.scriptToRun = '';
+
+const entryUrl = new URL('entry/entry.html', location).href;
+const incumbentUrl = new URL('incumbent/empty.html', location).href;
+const relevantUrl = new URL('relevant/empty.html', location).href;
+
+function go() {
+ const entry = document.querySelector('#entry');
+ const incumbent = entry.contentDocument.querySelector('#incumbent');
+ const incumbentScript = incumbent.contentDocument.createElement('script');
+ incumbentScript.textContent = `
+ function go() {
+ const relevantWindow =
+ parent.document.querySelector('#relevant').contentWindow;
+ ${window.scriptToRun}
+ }
+ `;
+ incumbent.contentDocument.head.appendChild(incumbentScript);
+ incumbent.contentWindow.go();
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js
new file mode 100644
index 0000000000..e3a507b0d8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js
@@ -0,0 +1,11 @@
+window.onload = function() {
+ let testWindow;
+ if (opener) {
+ testWindow = opener.top;
+ } else {
+ testWindow = top;
+ }
+ testWindow.postMessage(
+ {location: location.href, referrer: document.referrer},
+ "*");
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html
new file mode 100644
index 0000000000..f74bbfd7d3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+ const crossOriginUrl = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ crossOriginUrl.pathname = "/common/blank.html";
+ const i = document.createElement("iframe");
+ i.src = crossOriginUrl;
+ document.body.appendChild(i);
+
+ let wasLoadEventFired = false;
+ i.onload = t.step_func(() => {
+ // Though iframe is cross-origin and changing hash leads soft reload, the
+ // load event should be fired to protect sensitive information.
+ // See: https://crbug.com/1248444
+ crossOriginUrl.hash = "#foo";
+ i.onload = () => {
+ assert_false(wasLoadEventFired)
+ wasLoadEventFired = true;
+ // Wait for a while to ensure other onload events are never fired.
+ t.step_timeout(() => t.done(), 100);
+ };
+ i.src = crossOriginUrl;
+ });
+
+}, "Changing the URL hash of a cross-origin iframe should fire a load event");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html
new file mode 100644
index 0000000000..ed228ad59b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+ let starting_history_length = history.length;
+ let cross_origin_url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ cross_origin_url.pathname = "/common/blank.html";
+ cross_origin_url.hash = "#foo";
+ let i = document.createElement("iframe");
+ i.src = cross_origin_url;
+ document.body.appendChild(i);
+
+ window.onload = () => t.step_timeout(() => {
+ assert_equals(starting_history_length, history.length);
+ i.src = cross_origin_url;
+ // Give the navigation time to happen - no events will fire.
+ t.step_timeout(() => {
+ assert_equals(starting_history_length + 1, history.length);
+ t.done();
+ }, 100);
+ }, 0);
+}, "Navigating a cross-origin iframe to its current url should not replace");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html
new file mode 100644
index 0000000000..9996d58914
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+ let starting_history_length = history.length;
+ let cross_origin_url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ cross_origin_url.pathname = "/common/blank.html";
+ let i = document.createElement("iframe");
+ i.src = cross_origin_url;
+ document.body.appendChild(i);
+
+ window.onload = () => t.step_timeout(() => {
+ assert_equals(starting_history_length, history.length);
+ i.onload = t.step_func_done(() => assert_equals(starting_history_length + 1, history.length));
+ i.src = cross_origin_url;
+ }, 0);
+}, "Navigating a cross-origin iframe to its current url should not replace");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html
new file mode 100644
index 0000000000..d20a440d03
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>location.href unparseable URL throws a SyntaxError DOMException</title>
+<link rel="help" href="https://html.spec.whatwg.org/#the-location-interface:dom-location-href-2">
+<link rel="help" href="https://html.spec.whatwg.org/#following-hyperlinks-2">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+const kUnparseableURL = self.origin + ":notaport/common/blank.html";
+
+promise_test(async t => {
+ const win = window.open("/common/blank.html");
+ t.add_cleanup(() => {
+ win.close();
+ });
+
+ await new Promise(resolve => {
+ win.onload = resolve;
+ });
+
+ assert_throws_dom("SyntaxError", win.DOMException, () => {
+ win.location.href = kUnparseableURL;
+ }, "location.href setter throws a SyntaxError DOMException");
+}, "location.href setter throws a SyntaxError DOMException for unparseable " +
+ "URLs");
+
+promise_test(async t => {
+ const win = window.open("/common/blank.html");
+ t.add_cleanup(() => {
+ win.close();
+ });
+
+ await new Promise(resolve => {
+ win.onload = resolve;
+ });
+
+ // If the newly-opened window tries to navigate, fail the test.
+ const failPromise = new Promise((resolve, reject) => {
+ win.onpagehide = () =>
+ reject(new Error("Navigation was attempted to unparseable URL"));
+ });
+
+ // A promise to wait on to confirm the newly-opened window did not navigate.
+ const successPromise = new Promise(resolve => {
+ t.step_timeout(resolve, 2000);
+ });
+
+ const a = win.document.createElement('a');
+ a.href = kUnparseableURL;
+ win.document.body.append(a);
+ a.click();
+
+ return Promise.race([successPromise, failPromise]);
+}, "<a> tag navigate fails for unparseable URLs");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js
new file mode 100644
index 0000000000..f23e2c440b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js
@@ -0,0 +1,15 @@
+// META: title=Cross-origin navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href = "//{{hosts[][www]}}/common/blank.html?fail";
+ });
+
+ iframe.src = "/common/blank.html?pass";
+
+ await waitForIframeLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js
new file mode 100644
index 0000000000..cf39b01107
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js
@@ -0,0 +1,15 @@
+// META: title=data: URL navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href =
+ `data:text/html,unload<script>parent.postMessage('fail', '*');</script>`;
+ });
+
+ iframe.src =
+ `data:text/html,load<script>parent.postMessage('pass', '*')</script>`;
+ assert_equals(await waitForMessage(iframe.contentWindow), "pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html
new file mode 100644
index 0000000000..e06def9b20
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-form-submit-1.html</h1>
+
+<script>
+let isSubmit = false;
+
+window.unload = function () {
+ window.location = 'is-Submit' + isSubmit;
+}
+
+function setIsSubmit() {
+ isSubmit = true;
+}
+</script>
+
+<form onsubmit="setIsSubmit" action="navigation-unload-form-submit-2.html">
+ <input type="submit">
+</form>
+
+<script>
+parent.finishedLoading();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html
new file mode 100644
index 0000000000..43cd3c1b33
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-form-submit-2.html</h1>
+
+<script>
+parent.finishedLoading();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html
new file mode 100644
index 0000000000..029170d46a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<iframe id="i" src="navigation-unload-form-submit-1.html"></iframe>
+
+<!-- derived from https://bugzilla.mozilla.org/show_bug.cgi?id=247660#c0 -->
+
+<script>
+var test = async_test('Tests that navigation during an unload caused by a form submit does nothing');
+window.onload = test.step_func(function() {
+ var i = document.querySelector('#i');
+
+ window.finishedLoading = test.step_func_done(function () {
+ assert_equals(i.contentWindow.location.pathname.split('/').pop(), 'navigation-unload-form-submit-2.html');
+ assert_equals(i.contentWindow.location.hash, '');
+ });
+
+ i.contentWindow.document.querySelector('input[type="submit"]').click();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js
new file mode 100644
index 0000000000..abbcb888a0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js
@@ -0,0 +1,15 @@
+// META: title=javascript: URL navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href =
+ `javascript:"unload<script>parent.postMessage('fail', '*');</script>"`;
+ });
+
+ iframe.src =
+ `javascript:"load<script>parent.postMessage('pass', '*')</script>"`;
+ assert_equals(await waitForMessage(iframe.contentWindow), "pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html
new file mode 100644
index 0000000000..3b5d725fee
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-same-origin-fragment-1.html</h1>
+
+<script>
+if (parent.finishedLoading) {
+ parent.finishedLoading();
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html
new file mode 100644
index 0000000000..2fb7f50b43
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-same-origin-fragment-2.html</h1>
+
+<script>
+if (parent.finishedLoading) {
+ parent.finishedLoading();
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html
new file mode 100644
index 0000000000..c4dceb9e04
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<iframe id="i" src="navigation-unload-same-origin-fragment-1.html"></iframe>
+
+<!-- a timeout indicates that setting i.contentWindow.location.hash (a second navigation) aborted the first navigation,
+ and so it stayed on a.html and finishedLoading was never called -->
+
+<script>
+var test = async_test('Tests that a fragment navigation in the unload handler will not block the initial navigation');
+window.onload = test.step_func(function() {
+ var i = document.querySelector('#i');
+
+ i.contentWindow.onunload = test.step_func(function() {
+ i.contentWindow.location.hash = '#fragment';
+ });
+
+ window.finishedLoading = test.step_func_done(function () {
+ assert_equals(i.contentWindow.location.pathname.split('/').pop(), 'navigation-unload-same-origin-fragment-2.html');
+ assert_equals(i.contentWindow.location.hash, '');
+ });
+
+ i.contentWindow.location.href = 'navigation-unload-same-origin-fragment-2.html';
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js
new file mode 100644
index 0000000000..826af4b75b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js
@@ -0,0 +1,15 @@
+// META: title=Same-origin navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href = "/common/blank.html?fail";
+ });
+
+ iframe.src = "/common/blank.html?pass";
+
+ await waitForIframeLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html
new file mode 100644
index 0000000000..547917b795
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-origin PDFs must not create accessible Document objects</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- https://github.com/whatwg/html/pull/6947 -->
+
+<iframe src="resources/portable-document-format-sample-valid.pdf"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+window.onload = () => {
+ test(() => {
+ assert_throws_dom("SecurityError", () => {
+ document.querySelector("iframe").contentWindow.document;
+ });
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js
new file mode 100644
index 0000000000..5480911895
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js
@@ -0,0 +1,34 @@
+"use strict";
+
+["about:blank", "about:srcdoc", "about:nonstandard"].forEach(aboutURL => {
+ promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = `resources/redirect.py?location=${aboutURL}`;
+ document.body.append(iframe);
+
+ // Unfortunately Firefox does not fire a load event for network errors yet, but there is no
+ // other way I can see to test this. (Also applicable below.)
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+ }, `An iframe with src set to a redirect to ${aboutURL}`);
+
+ promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ document.body.append(iframe);
+
+ await new Promise(r => iframe.onload = r);
+
+ iframe.contentWindow.location.href = `resources/redirect.py?location=${aboutURL}`;
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+ }, `An iframe that is navigated to a redirect to ${aboutURL}`);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html
new file mode 100644
index 0000000000..f9e8021ddf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Redirecting to data: URLs is disallowed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async (t) => {
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`;
+ document.body.append(iframe);
+
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Loading an iframe with src=redirecting URL");
+
+promise_test(async (t) => {
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ document.body.append(iframe);
+
+ await new Promise(r => iframe.onload = r);
+
+ iframe.contentWindow.location.href = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`;
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Navigating an iframe to a redirecting URL");
+
+promise_test(async (t) => {
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ const w = window.open(`resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`);
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Loading a popup directly to the redirecting URL");
+
+promise_test(async (t) => {
+ const w = window.open(`resources/message-opener.html`);
+ await new Promise(r => window.onmessage = r);
+
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ w.location.href = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`;
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Loading a popup that eventually goes to the redirecting URL");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html
new file mode 100644
index 0000000000..b025f34478
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Session history interaction with redirects to unparseable URLs</title>
+<link rel="help" href="https://html.spec.whatwg.org/#create-navigation-params-by-fetching">
+<link rel="help" href="https://html.spec.whatwg.org/#read-ua-inline">
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+const kUnparseableURL = self.origin + ":notaport/common/blank.html";
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => {
+ iframe.remove();
+ });
+
+ function getIframeLoadPromise() {
+ return new Promise(resolve => {
+ iframe.addEventListener('load', () => {
+ // Wait for the iframe to load + one task so that its navigations are
+ // not done in "replace" mode.
+ t.step_timeout(resolve, 0);
+ }, {once: true});
+ });
+ }
+
+ document.body.append(iframe);
+
+ assert_equals(history.length, 1, "Precondition: history.length is 1");
+
+ const first_load_promise = getIframeLoadPromise();
+ iframe.src = '/common/blank.html';
+ await first_load_promise;
+
+ // This navigation will fail, because it redirects to an unparseable URL.
+ const error_load_promise = getIframeLoadPromise();
+ const error_url = new URL('resources/no-cache-single-redirect.py', location.href);
+ error_url.searchParams.append('uuid', token());
+ error_url.searchParams.append('location', kUnparseableURL);
+ iframe.src = error_url;
+ await error_load_promise;
+
+ assert_equals(history.length, 2,
+ "history.length is 2 after two iframe navigations beyond the initial " +
+ "about:blank Document, the first of which 'replaced' the initial " +
+ "about:blank Document");
+
+ // Per https://html.spec.whatwg.org/#read-ua-inline, error Documents have
+ // opaque origins, so the `contentDocument` shouldn't be accessible.
+ assert_equals(iframe.contentDocument, null,
+ "Cannot reach iframe.contentDocument for error Documents");
+
+ const back_load_promise = getIframeLoadPromise();
+ history.back();
+ await back_load_promise;
+
+ const forward_load_promise = getIframeLoadPromise();
+ history.forward();
+ await forward_load_promise;
+
+ assert_not_equals(iframe.contentDocument, null, "iframe.contentDocument is accessible");
+ assert_equals(iframe.contentDocument.body.innerText, "No redirect",
+ "Traversal to history entry whose URL was once associated with an " +
+ "error Document correctly requests the same URL again");
+}, "Navigating to a url (A) that redirects to an unparseable URL (B), saves " +
+ "the URL (A) in the history entry, for later traversal");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md
new file mode 100644
index 0000000000..52548db656
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md
@@ -0,0 +1 @@
+See `/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html` for more detailed parsing tests (shared with `<meta http-equiv=refresh>`).
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js
new file mode 100644
index 0000000000..7d5a0fe21d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js
@@ -0,0 +1,23 @@
+async_test(t => {
+ const frame = document.createElement("iframe");
+ frame.src = "resources/refresh.py"
+ frame.onload = t.step_func(() => {
+ // Could be better by verifying that resources/refresh.py loads too
+ if(frame.contentWindow.location.href === (new URL("resources/refreshed.txt?\u0080\u00FF", self.location)).href) { // Make sure bytes got mapped to code points of the same value
+ t.done();
+ }
+ });
+ document.body.appendChild(frame)
+}, "When navigating the Refresh header needs to be followed");
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ frame.src = "resources/multiple.asis"
+ frame.onload = t.step_func(() => {
+ // Could be better by verifying that resources/refresh.py loads too
+ if(frame.contentWindow.location.href === (new URL("resources/refreshed.txt", self.location)).href) {
+ t.done();
+ }
+ });
+ document.body.appendChild(frame)
+}, "When there's both a Refresh header and <meta> the Refresh header wins")
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis
new file mode 100644
index 0000000000..3026d8297b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis
@@ -0,0 +1,6 @@
+HTTP/1.1 200 OK
+Refresh: 0,./refreshed.txt
+Content-Type:text/html
+
+I don't understand.
+<meta http-equiv=refresh content=1;./>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py
new file mode 100644
index 0000000000..ecdd24f268
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ response.headers.set(b"Content-Type", b"text/plain")
+ response.headers.set(b"Refresh", b"0;./refreshed.txt?\x80\xFF") # Test byte to Unicode conversion
+ response.content = u"Not refreshed.\n"
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt
new file mode 100644
index 0000000000..5df065b456
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt
@@ -0,0 +1 @@
+Have another.
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js
new file mode 100644
index 0000000000..930dd34ad5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js
@@ -0,0 +1,6 @@
+promise_test(() => {
+ return fetch("resources/refresh.py").then(response => {
+ assert_equals(response.headers.get("refresh"), "0;./refreshed.txt?\u0080\u00FF"); // Make sure bytes got mapped to code points of the same value
+ assert_equals(response.url, (new URL("resources/refresh.py", self.location)).href);
+ });
+}, "Refresh does not affect subresources.");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html
new file mode 100644
index 0000000000..e035b1f517
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ document.body.append(a);
+ a.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "aElement.click() during the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html
new file mode 100644
index 0000000000..006ce531e0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ document.body.append(a);
+ a.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "aElement.click() during the pageshow event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html
new file mode 100644
index 0000000000..be7d1a9849
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ document.currentScript.before(a);
+ a.click();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "aElement.click() before the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html
new file mode 100644
index 0000000000..811c828331
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ a.id = "the-anchor";
+ a.textContent = "needs to have content to be clickable";
+ document.body.append(a);
+ parent.test_driver.click(a);
+ };
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "User click on <a> during the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html
new file mode 100644
index 0000000000..6621b081e1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ a.id = "the-anchor";
+ a.textContent = "needs to have content to be clickable";
+ document.body.append(a);
+ parent.test_driver.click(a);
+ };
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "User click on <a> during the pageshow event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html
new file mode 100644
index 0000000000..c9034f3573
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ a.id = "the-anchor";
+ a.textContent = "needs to have content to be clickable";
+ document.currentScript.before(a);
+ parent.test_driver.click(a);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "User click on <a> before the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html
new file mode 100644
index 0000000000..0dee49edb3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.requestSubmit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html
new file mode 100644
index 0000000000..0cf0838496
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.requestSubmit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html
new file mode 100644
index 0000000000..80beb3718b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.currentScript.before(form);
+ form.requestSubmit();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by formElement.requestSubmit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html
new file mode 100644
index 0000000000..1b3a163176
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ const button = document.createElement("button");
+ button.type = "submit";
+ form.append(button);
+
+ document.body.append(form);
+ button.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during load, triggered by submitButton.click()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html
new file mode 100644
index 0000000000..c0022fd0f2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ const button = document.createElement("button");
+ button.type = "submit";
+ form.append(button);
+
+ document.body.append(form);
+ button.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during pageshow, triggered by submitButton.click()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html
new file mode 100644
index 0000000000..873c49b5be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ const button = document.createElement("button");
+ button.type = "submit";
+ form.append(button);
+
+ document.currentScript.before(form);
+ button.click();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by submitButton.click()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html
new file mode 100644
index 0000000000..a2ea20bf7f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-frame">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+
+ const form = document.querySelector("form");
+ const frameEndingURL = changeURLHost(
+ absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="),
+ "{{hosts[][www]}}"
+ );
+ form.action = frameEndingURL;
+
+ const frameStartingCode = `
+ parent.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ `;
+
+ const frameStartingURL = codeInjectorURL(frameStartingCode);
+ const frame = insertIframe(t, frameStartingURL, "the-frame");
+ t.add_cleanup(() => frame.remove()); // helps avoid waiting for the slow load to finish the tests
+ assert_equals(history.length, startingHistoryLength, "Inserting frame must not change history.length");
+
+ const frameBeforeLoadedMessage = await waitForMessage();
+ assert_equals(frameBeforeLoadedMessage.historyLength, startingHistoryLength, "frame's starting history.length");
+ assert_equals(frameBeforeLoadedMessage.locationHref, frame.src, "frame's starting location.href");
+
+ form.submit();
+
+ const frameAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(frameAfterFormSubmitMessage.historyLength, startingHistoryLength + 1, "frame's after-submit history.length");
+ assert_equals(frameAfterFormSubmitMessage.locationHref, frameEndingURL, "frame's after-submit location.href");
+}, "No replace before load, triggered by cross-iframe formElement.submit() [iframe is cross-origin]");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html
new file mode 100644
index 0000000000..d647e6ad06
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-frame">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+
+ const form = document.querySelector("form");
+ const frameEndingURL = absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed=");
+ form.action = frameEndingURL;
+
+ const frameStartingCode = `
+ window.onload = () => { window.onloadFired = true; };
+ parent.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ parent.document.querySelector("form").submit();
+ `;
+
+ const frameStartingURL = codeInjectorURL(frameStartingCode);
+ const frame = insertIframe(t, frameStartingURL, "the-frame");
+ t.add_cleanup(() => frame.remove()); // helps avoid waiting for the slow load to finish the tests
+ assert_equals(history.length, startingHistoryLength, "Inserting frame must not change history.length");
+
+ const frameBeforeLoadedMessage = await waitForMessage();
+ assert_equals(frameBeforeLoadedMessage.historyLength, startingHistoryLength, "frame's starting history.length");
+ assert_equals(frameBeforeLoadedMessage.locationHref, frame.src, "frame's starting location.href");
+ assert_equals(frame.contentWindow.onloadFired, undefined, "frame's onload not fired yet");
+
+ const frameAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(frameAfterFormSubmitMessage.historyLength, startingHistoryLength + 1, "frame's after-submit history.length");
+ assert_equals(frameAfterFormSubmitMessage.locationHref, frameEndingURL, "frame's after-submit location.href");
+}, "No replace before load, triggered by cross-iframe formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html
new file mode 100644
index 0000000000..2a60f44fc6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.submit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html
new file mode 100644
index 0000000000..886bf469a2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.submit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html
new file mode 100644
index 0000000000..d54cc81477
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-window">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const form = document.querySelector("form");
+ const wEndingURL = changeURLHost(
+ absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="),
+ "{{hosts[][www]}}"
+ );
+ form.action = wEndingURL;
+
+ const wStartingCode = `
+ opener.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ `;
+
+ const wStartingURL = codeInjectorURL(wStartingCode);
+ const w = window.open(wStartingURL, "the-window");
+ t.add_cleanup(() => w.close());
+
+ const wBeforeLoadedMessage = await waitForMessage();
+ assert_equals(wBeforeLoadedMessage.historyLength, 1, "window's starting history.length");
+ assert_equals(wBeforeLoadedMessage.locationHref, wStartingURL, "window's starting location.href");
+
+ form.submit();
+
+ const wAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(wAfterFormSubmitMessage.historyLength, 2, "window's after-submit history.length");
+ assert_equals(wAfterFormSubmitMessage.locationHref, wEndingURL, "window's after-submit location.href");
+}, "No replace before load, triggered by formElement.submit() in the opener window, after the opener has loaded [window is cross-origin]");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html
new file mode 100644
index 0000000000..b27d54675d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-window">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const form = document.querySelector("form");
+ const wEndingURL = absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed=");
+ form.action = wEndingURL;
+
+ const wStartingCode = `
+ window.onload = () => { window.onloadFired = true; };
+ opener.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ opener.document.querySelector("form").submit();
+ `;
+
+ const wStartingURL = codeInjectorURL(wStartingCode);
+ const w = window.open(wStartingURL, "the-window");
+ t.add_cleanup(() => w.close());
+
+ const wBeforeLoadedMessage = await waitForMessage();
+ assert_equals(wBeforeLoadedMessage.historyLength, 1, "window's starting history.length");
+ assert_equals(wBeforeLoadedMessage.locationHref, wStartingURL, "window's starting location.href");
+ assert_equals(w.onloadFired, undefined, "window's onload not fired yet");
+
+ const wAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(wAfterFormSubmitMessage.historyLength, 2, "window's after-submit history.length");
+ assert_equals(wAfterFormSubmitMessage.locationHref, wEndingURL, "window's after-submit location.href");
+}, "No replace before load, triggered by formElement.submit() in the opener window, after the opener has loaded");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html
new file mode 100644
index 0000000000..eace0b0a69
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.currentScript.before(form);
+ form.submit();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by same-document formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html
new file mode 100644
index 0000000000..233d80d8d7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ history.pushState(null, null, "/common/blank.html?thereplacement");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement");
+}, "history.pushState() during the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html
new file mode 100644
index 0000000000..a4e83baf1d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ history.pushState(null, null, "/common/blank.html?thereplacement");
+ parent.postMessage("done", "*");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ assert_equals(await waitForMessage(), "done");
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement");
+}, "history.pushState() during the pageshow event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html
new file mode 100644
index 0000000000..2faef7c319
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("history.pushState(null, null, `/common/blank.html?thereplacement`);");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement");
+}, "history.pushState() before the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html
new file mode 100644
index 0000000000..83f26b2aa9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ parent.document.querySelectorAll("iframe")[1].src = "/common/blank.html?thereplacement";
+ };
+ `;
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by setting iframeElement.src");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html
new file mode 100644
index 0000000000..61345cf7f9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ parent.document.querySelectorAll("iframe")[1].src = "/common/blank.html?thereplacement";
+ };
+ `;
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by setting iframeElement.src");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html
new file mode 100644
index 0000000000..47407c106e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => { window.onloadFired = true; };
+ `;
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+ while (true) {
+ if (iframe.contentWindow.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => setTimeout(r, 0));
+ }
+
+ assert_equals(iframe.contentWindow.location.href, (new URL(startURL, location.href)).href, "Iframe must be navigated away from the initial about:blank document");
+ assert_equals(iframe.contentWindow.onloadFired, undefined, "onload must not yet have fired");
+
+ iframe.src = afterReplacementURL;
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by setting iframeElement.src");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html
new file mode 100644
index 0000000000..2b4ac49533
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.assign("/common/blank.html?thereplacement");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html
new file mode 100644
index 0000000000..0f84cbe246
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.assign("/common/blank.html?thereplacement");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html
new file mode 100644
index 0000000000..785ec024e0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.assign("/common/blank.html?thereplacement"); };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "NO replace before load, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html
new file mode 100644
index 0000000000..53ea96ffea
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.assign(`/common/blank.html?thereplacement`);");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html
new file mode 100644
index 0000000000..e4c8ca80a1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Replace during the load event, triggered by location setters</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.href = "/common/blank.html?thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.search = "thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.hash = "thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html
new file mode 100644
index 0000000000..9997783ff5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Replace during the pageshow event, triggered by location setters</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.href = "/common/blank.html?thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.search = "thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.hash = "thereplacement";
+ parent.postMessage("done", "*");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ assert_equals(await waitForMessage(), "done");
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html
new file mode 100644
index 0000000000..9276327f1a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>No replace before load, triggered by location setters called as part of user-initiated clicks</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.href = "/common/blank.html?thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.search = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/slow-code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.hash = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html
new file mode 100644
index 0000000000..9068e5a116
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>No replace before load, triggered by location setters called as part of user-initiated mouseups</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!--
+ We test this separate from click because the spec as of
+ https://html.spec.whatwg.org/commit-snapshots/4ba46b025ec806ded7b4911bf8f9dd7bf9ff365e/#location-object-setter-navigate
+ referenced click handlers specifically, instead of using the general user activation concept which
+ includes other events like mouseup.
+-->
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onmouseup = () => { location.href = "/common/blank.html?thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onmouseup = () => { location.search = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/slow-code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onmouseup = () => { location.hash = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html
new file mode 100644
index 0000000000..b8049f084b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Replace before load, triggered by location setters</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.href = `/common/blank.html?thereplacement`;");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.search = `thereplacement`;");
+ const afterReplacementURL = "resources/code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.hash = `thereplacement`;");
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html
new file mode 100644
index 0000000000..73dacf0d76
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe</title>
+
+<body>
+<script>
+"use strict";
+{{GET[code]}}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js
new file mode 100644
index 0000000000..58102aa925
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js
@@ -0,0 +1,89 @@
+window.waitForLoad = (t, iframe, urlRelativeToThisDocument) => {
+ return new Promise(resolve => {
+ iframe.addEventListener("load", t.step_func(() => {
+ assert_equals(iframe.contentWindow.location.href, (new URL(urlRelativeToThisDocument, location.href)).href);
+
+ // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded"
+ // (which happens from a queued task).
+ setTimeout(resolve, 0);
+ }), { once: true });
+ });
+};
+
+window.waitForLoadAllowingIntermediateLoads = (t, iframe, urlRelativeToThisDocument) => {
+ return new Promise(resolve => {
+ const handler = t.step_func(() => {
+ if (iframe.contentWindow.location.href === (new URL(urlRelativeToThisDocument, location.href)).href) {
+ // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded"
+ // (which happens from a queued task).
+ setTimeout(resolve, 0);
+ iframe.removeEventListener("load", handler);
+ }
+ });
+
+ iframe.addEventListener("load", handler);
+ });
+};
+
+window.waitForMessage = () => {
+ return new Promise(resolve => {
+ window.addEventListener("message", e => {
+ resolve(e.data);
+ }, { once: true });
+ });
+};
+
+window.setupSentinelIframe = async (t) => {
+ // If this iframe gets navigated by history.back(), then the iframe under test did not, so we did a replace.
+ const sentinelIframe = document.createElement("iframe");
+ sentinelIframe.src = "/common/blank.html?sentinelstart";
+ document.body.append(sentinelIframe);
+ t.add_cleanup(() => sentinelIframe.remove());
+
+ await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelstart");
+
+ sentinelIframe.src = "/common/blank.html?sentinelend";
+ await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelend");
+
+ return sentinelIframe;
+};
+
+window.checkSentinelIframe = async (t, sentinelIframe) => {
+ // Go back. Since iframe should have done a replace, this should move sentinelIframe back, not iframe.
+ history.back();
+ await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelstart");
+};
+
+window.insertIframe = (t, url, name) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+
+ // In at least Chromium, window name targeting for form submission doesn't work if the name is set
+ // after the iframe is inserted into the DOM. So we can't just have callers do this themselves.
+ if (name) {
+ iframe.name = name;
+ }
+
+ document.body.append(iframe);
+
+ // Intentionally not including the following:
+ // t.add_cleanup(() => iframe.remove());
+ // Doing so breaks some of the testdriver.js tests with "cannot find window" errors.
+ return iframe;
+};
+
+// TODO(domenic): clean up other tests in the parent directory to use this.
+window.absoluteURL = relativeURL => {
+ return (new URL(relativeURL, location.href)).href;
+};
+
+// TODO(domenic): clean up other tests in the parent directory to use this.
+window.codeInjectorURL = code => {
+ return absoluteURL("resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code));
+};
+
+window.changeURLHost = (url, newHost) => {
+ const urlObj = new URL(url);
+ urlObj.host = newHost;
+ return urlObj.href;
+};
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html
new file mode 100644
index 0000000000..b6f9d31358
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popup</title>
+
+<script>
+"use strict";
+opener.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html
new file mode 100644
index 0000000000..b7e99dcbfa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe</title>
+
+<body>
+<script>
+"use strict";
+{{GET[code]}}
+</script>
+
+<!--
+ This is necessary in cases involving user interaction because those happen async through the
+ webdriver infrastructure. Without this, the load event might happen before the click ever goes
+ through.
+-->
+<img src="/common/slow.py">
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html
new file mode 100644
index 0000000000..da279ebf82
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe or popup</title>
+
+<script>
+"use strict";
+(window.opener || window.parent).postMessage(
+ { historyLength: history.length, locationHref: location.href },
+ "*"
+);
+</script>
+
+<!--
+ This delays the load event, hopefully long enough that we can do whatever before-load action we're aiming for.
+-->
+<img src="/common/slow.py">
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html
new file mode 100644
index 0000000000..5308c01c39
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ // Generating a new one instead of hard-coding makes running tests manually a bit easier.
+ const windowName = token();
+
+ const code = `
+ window.onload = () => opener.navigateMe();
+ opener.postMessage("arrived at start URL", "*");
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+
+ const afterReplacementURL = "resources/message-opener.html";
+ const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href;
+
+ window.navigateMe = () => {
+ window.open(absoluteAfterReplacementURL, windowName);
+ };
+
+ // First message sent is ignored; we only check it after navigating back.
+ const w = window.open(startURL, windowName);
+ t.add_cleanup(() => w.close());
+
+ // Wait to get past any initial about:blank
+ while (true) {
+ if (w.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => t.step_timeout(r, 0));
+ }
+
+ assert_equals(w.onloadFired, undefined, "onload must not yet have fired");
+ assert_equals(w.history.length, 1, "history.length for the opened window must start at 1");
+
+ await new Promise(r => {
+ window.addEventListener("message", t.step_func(e => {
+ if (e.data === "ready") {
+ resolve();
+ }
+ }));
+ });
+
+ assert_equals(w.history.length, 2, "history.length must increase");
+ assert_equals(w.location.href, absoluteAfterReplacementURL);
+
+ const promise = new Promise(resolve => {
+ window.addEventListener("message", t.step_func(e => {
+ assert_equals(e.data, "arrived at start URL");
+ resolve();
+ }));
+ });
+
+ w.history.back();
+
+ await promise;
+ assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back");
+}, "No replace before load, triggered by window.open() on a non-_self window");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html
new file mode 100644
index 0000000000..065d933225
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ // Generating a new one instead of hard-coding makes running tests manually a bit easier.
+ const windowName = token();
+
+ const code = `
+ window.onpageshow = () => opener.navigateMe();
+ opener.postMessage("arrived at start URL", "*");
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+
+ const afterReplacementURL = "resources/message-opener.html";
+ const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href;
+
+ window.navigateMe = () => {
+ window.open(absoluteAfterReplacementURL, windowName);
+ };
+
+ // First message sent is ignored; we only check it after navigating back.
+ const w = window.open(startURL, windowName);
+ t.add_cleanup(() => w.close());
+
+ // Wait to get past any initial about:blank
+ while (true) {
+ if (w.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => t.step_timeout(r, 0));
+ }
+
+ assert_equals(w.onloadFired, undefined, "onload must not yet have fired");
+ assert_equals(w.history.length, 1, "history.length for the opened window must start at 1");
+
+ await new Promise(r => {
+ window.addEventListener("message", t.step_func(e => {
+ if (e.data === "ready") {
+ resolve();
+ }
+ }));
+ });
+
+ assert_equals(w.history.length, 2, "history.length must increase");
+ assert_equals(w.location.href, absoluteAfterReplacementURL);
+
+ const promise = new Promise(resolve => {
+ window.addEventListener("message", t.step_func(e => {
+ assert_equals(e.data, "arrived at start URL");
+ resolve();
+ }));
+ });
+
+ w.history.back();
+
+ await promise;
+ assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back");
+}, "No replace before load, triggered by window.open() on a non-_self window");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html
new file mode 100644
index 0000000000..7b3e05f8f6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ // Generating a new one instead of hard-coding makes running manual tests a bit easier.
+ const windowName = token();
+
+ const code = `
+ window.onload = () => { window.onloadFired = true; };
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+
+ const afterReplacementURL = "resources/message-opener.html";
+ const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href;
+
+ const w = window.open(startURL, windowName);
+ t.add_cleanup(() => w.close());
+
+ // Wait to get past any initial about:blank
+ while (true) {
+ if (w.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => setTimeout(r, 0));
+ }
+
+ assert_equals(w.onloadFired, undefined, "onload must not yet have fired");
+ assert_equals(w.history.length, 1, "history.length for the opened window must start at 1");
+
+ window.open(afterReplacementURL, windowName);
+ await new Promise(r => { window.onmessage = r; });
+
+ assert_equals(w.history.length, 2, "history.length must increase");
+ assert_equals(w.location.href, absoluteAfterReplacementURL);
+
+ w.history.back();
+
+ await new Promise(r => t.step_timeout(r, 1000));
+ assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back");
+}, "No replace before load, triggered by window.open() on a non-_self window");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html
new file mode 100644
index 0000000000..255601ba8d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ window.open("/common/blank.html?thereplacement", "_self");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must increase");
+}, "No replace during load, triggered by window.open(_self) on an iframe");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html
new file mode 100644
index 0000000000..a8327cfac3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ window.open("/common/blank.html?thereplacement", "_self");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must increase");
+}, "No replace during pageshow, triggered by window.open(_self) on an iframe");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html
new file mode 100644
index 0000000000..10e8f38001
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("window.open(`/common/blank.html?thereplacement`, `_self`);");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must increase");
+}, "No replace before load, triggered by window.open(_self) on an iframe");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html
new file mode 100644
index 0000000000..c50eddd41f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html
@@ -0,0 +1 @@
+<!doctype html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html
new file mode 100644
index 0000000000..72b92c8061
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<script>
+const params = new URL(window.location).searchParams;
+const property = params.get("property");
+
+try {
+ if (property === null) {
+ parent.location = "foo";
+ } else if (property === "reload") {
+ parent.location.reload();
+ } else if (property === "replace") {
+ parent.location.replace("foo");
+ } else {
+ parent.location[property] = "foo";
+ }
+ parent.parent.postMessage("success", "*");
+} catch (e) {
+ parent.parent.postMessage(`error: ${e.name}`, "*");
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html
new file mode 100644
index 0000000000..bb8ba4e698
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("destination", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html
new file mode 100644
index 0000000000..a4a1713a27
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<script>parent.postMessage("initial", "*")</script>
+<iframe src="child-navigates-parent-location-inner.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html
new file mode 100644
index 0000000000..4d7d24730f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+parent.parent.postMessage("inner", "*");
+parent.location = "child-navigates-parent-destination.html"
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html
new file mode 100644
index 0000000000..943708e0cd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<script>parent.postMessage("initial", "*")</script>
+<iframe src="child-navigates-parent-submit-inner.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html
new file mode 100644
index 0000000000..0b49e13d65
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<form action="child-navigates-parent-destination.html" target="_parent"></form>
+<script>
+parent.parent.postMessage("inner", "*");
+document.forms[0].submit();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html
new file mode 100644
index 0000000000..8cb03b74d5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("click", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html
new file mode 100644
index 0000000000..3c4355c452
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script>
+"use strict";
+document.domain = "{{host}}";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html
new file mode 100644
index 0000000000..6523a82b39
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+parent.postMessage("form navigation", "*");
+</script>
+form navigation
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html
new file mode 100644
index 0000000000..eccadadf41
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+parent.postMessage("href", "*");
+</script>
+href
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html
new file mode 100644
index 0000000000..b6f9d31358
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popup</title>
+
+<script>
+"use strict";
+opener.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html
new file mode 100644
index 0000000000..3656358f2d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html
@@ -0,0 +1,3 @@
+<script>
+ window.parent.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html
new file mode 100644
index 0000000000..a87b2fd2be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Multi-globals test outer subframe</title>
+
+<script>
+"use strict";
+document.domain = "{{hosts[][]}}";
+</script>
+
+<iframe src="http://{{hosts[][]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html
new file mode 100644
index 0000000000..593c428a67
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Multi-globals test inner subframe</title>
+
+<script>
+"use strict";
+document.domain = "{{hosts[][]}}";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py
new file mode 100644
index 0000000000..9d3aff817d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py
@@ -0,0 +1,21 @@
+# This handler receives requests identified by UUIDs that have mandatory
+# `location` query parameters. Every other request for the same URL will result
+# in a redirect to the URL described by `location`. When we don't redirect, we
+# simply return the HTML document "No redirect".
+def main(request, response):
+ response.headers.set(b"Cache-Control", b"no-store")
+
+ uuid = request.GET.first(b"uuid")
+ value = request.server.stash.take(uuid)
+
+ if value is None:
+ response.status = 302
+ location = request.GET.first(b"location")
+ response.headers.set(b"Location", location)
+ # Ensure that the next time this uuid is request, we don't redirect.
+ request.server.stash.put(uuid, "sentinel value")
+ else:
+ # If we're in this branch, then `value` is not none, but the stash now
+ # has `None` associated with `uuid`, which means on the next request for
+ # this `uuid` we'll end up in the above branch instead.
+ return ([(b"Content-Type", b"text/html")], b"No redirect")
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html
new file mode 100644
index 0000000000..65a0c82149
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page that postMessage to its opener</title>
+<script>
+ opener.postMessage('Allowed', '*');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html
new file mode 100644
index 0000000000..568e44296f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src=/common/get-host-info.sub.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<title>Page that embeds an iframe that navigates its top</title>
+<script>
+function addIframe() {
+ const iframe = document.createElement('iframe');
+ const path = new URL("top-navigating-page.html", window.location).pathname;
+ iframe.src = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path;
+ document.body.appendChild(iframe);
+}
+
+addEventListener('load', () => {
+ const urlParams = new URLSearchParams(location.search);
+ const parentUserGesture = urlParams.get('parent_user_gesture') === 'true';
+ if (parentUserGesture)
+ test_driver.bless("Giving parent frame user activation").then(addIframe);
+ else
+ addIframe();
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf
new file mode 100644
index 0000000000..d008b1f23a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf
Binary files differ
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers
new file mode 100644
index 0000000000..5a8e57e482
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers
@@ -0,0 +1 @@
+Content-Type: application/pdf
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py
new file mode 100644
index 0000000000..3c78c256b0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ location = request.GET.first(b"location")
+ response.status = 302
+ response.headers.set(b"Location", location)
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html
new file mode 100644
index 0000000000..557f408fba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page that navigates its top</title>
+<script src=/common/get-host-info.sub.js></script>
+<script>
+
+let path = new URL("page-that-post-message-to-opener.html", window.location).pathname;
+let fullUrl = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path;
+try {
+ top.location = fullUrl;
+} catch {
+ top.opener.postMessage('Denied', '*');
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js
new file mode 100644
index 0000000000..62ddec49f1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js
@@ -0,0 +1,15 @@
+// Asynchronous function that waits for the given number of messages to be
+// received by `window`, then returns those messages.
+function waitForMessages(numMessages) {
+ return new Promise((resolve) => {
+ const messages = [];
+
+ window.addEventListener("message", function handler(evt) {
+ messages.push(evt.data);
+ if (messages.length == numMessages) {
+ window.removeEventListener("message", handler);
+ resolve(messages);
+ }
+ });
+ });
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml
new file mode 100644
index 0000000000..3aacf33f1c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta charset="windows-1250"/>
+ <title>A test document used when you need something very non-default</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html
new file mode 100644
index 0000000000..d01ef4c77a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ Set location from a parent, then do a fragment navigation from within the
+ frame.
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe></iframe>
+<script>
+ promise_test(async test => {
+ // Wait for the DOM to be ready before inserting an <iframe> into it.
+ await new Promise(resolve => { onload = resolve });
+ // Insert an <iframe> and wait for a dummy document to be loaded into it.
+ let iframe = document.createElement("iframe");
+ iframe.src = "support/dummy.html";
+ let iframe_loaded = new Promise(resolve => { iframe.onload = resolve });
+ document.body.appendChild(iframe);
+ await iframe_loaded;
+ // The referrer is the main frame's URL since it initiated the iframe
+ // creation.
+ assert_equals(iframe.contentDocument.referrer, document.URL);
+ // Do a fragment navigation from the frame, which will fire the
+ // 'hashchange' function.
+ let hash_changed = new Promise(resolve => {
+ iframe.contentWindow.onhashchange = resolve
+ });
+ let navigateScript = iframe.contentDocument.createElement("script");
+ navigateScript.innerHTML = "location.href = '#foo'";
+ iframe.contentDocument.body.appendChild(navigateScript);
+ await hash_changed;
+ // The referrer stays the same, even when the last navigation was
+ // initiated by the iframe (instead of the main frame document).
+ assert_equals(iframe.contentDocument.referrer, document.URL);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html
new file mode 100644
index 0000000000..2efc3a6b4a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set location from a parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe></iframe>
+<script>
+ setup({ single_test: true });
+ onload = function() {
+ var fr = document.querySelector("iframe")
+ fr.contentWindow.location = "support/dummy.html"
+ fr.onload = function() {
+ assert_equals(fr.contentDocument.referrer, document.URL)
+ done()
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html
new file mode 100644
index 0000000000..e21260cd35
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set src from a function called from a parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe src="support/set-parent-src.html"></iframe>
+<script>
+async_test(function() {
+ onload = this.step_func(function() {
+ var fr = document.querySelector("iframe")
+ fr.contentWindow.go()
+ fr.onload = this.step_func_done(function() {
+ assert_equals(fr.contentDocument.referrer, document.URL)
+ })
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html
new file mode 100644
index 0000000000..c6fa765b89
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set location from a function called from a parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe src="support/location-set.html"></iframe>
+<script>
+async_test(function() {
+ onload = this.step_func(function() {
+ var fr = document.querySelector("iframe")
+ var url = fr.contentDocument.URL
+ fr.contentWindow.go()
+ fr.onload = this.step_func_done(function() {
+ assert_equals(fr.contentDocument.referrer, url)
+ })
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html
new file mode 100644
index 0000000000..479019d847
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set the src attribute to about:blank and check referrer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe></iframe>
+<script>
+ setup({
+ single_test: true
+ });
+ onload = function() {
+ var fr = document.querySelector("iframe")
+ fr.src = "about:blank"
+ fr.onload = function() {
+ assert_equals(fr.contentDocument.referrer, document.location.origin + '/')
+ done()
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html
new file mode 100644
index 0000000000..0638657093
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<meta charset=utf-8>
+<p>Hello. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html
new file mode 100644
index 0000000000..ad733afac3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+ function go() {
+ location.href = "support/dummy.html"
+ }
+</script>
+<p>Hello. Go. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html
new file mode 100644
index 0000000000..9d45be8c8d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+ function go() {
+ frameElement.src = "support/dummy.html"
+ }
+</script>
+<p>Hello. Go. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js
new file mode 100644
index 0000000000..ca321f106a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js
@@ -0,0 +1,20 @@
+// META: timeout=long
+
+const dataURL = `data:text/html,...`;
+const encodedDataURL = encodeURIComponent(dataURL);
+
+[dataURL, `resources/redirect.py?location=${encodedDataURL}`].forEach(url => {
+ [undefined, "opener", "noopener", "noreferrer"].forEach(opener => {
+ async_test(t => {
+ const popup = window.open(url, "", opener);
+ t.step_timeout(() => {
+ if (opener === "noopener" || opener == "noreferrer") {
+ assert_equals(popup, null);
+ } else {
+ assert_true(popup.closed);
+ }
+ t.done();
+ }, 1500);
+ }, `Navigating a popup using window.open("${url}", "", "${opener}")`);
+ });
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/README.md b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/README.md
new file mode 100644
index 0000000000..cc313a155a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/README.md
@@ -0,0 +1,11 @@
+# Overlapping navigation and traversal tests
+
+These tests follow the behavior outlined in the
+[session history rewrite](https://github.com/whatwg/html/pull/6315).
+
+<https://github.com/whatwg/html/issues/6927> discusses these results.
+
+We are not yet 100% sure on this behavior, especially for overlapping
+traversal cases where the spec is complex and some of the tests don't
+seem to match any browser. Please feel free to discuss on the spec
+issue.
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/anchor-fragment-history-back-on-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/anchor-fragment-history-back-on-click.html
new file mode 100644
index 0000000000..a081bec514
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/anchor-fragment-history-back-on-click.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+ await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+
+ location.hash = "#1";
+ assert_equals(location.hash, "#1");
+ location.hash = "#2";
+ assert_equals(location.hash, "#2");
+
+ let anchor = document.createElement("a");
+ anchor.href = "#3";
+ anchor.onclick = () => {
+ history.back();
+ };
+
+ let navigations = [];
+ let navigationsPromise = new Promise(resolve => {
+ onpopstate = () => {
+ navigations.push(location.hash);
+ if (navigations.length === 2) {
+ resolve();
+ }
+ }
+ });
+
+ anchor.click();
+ await navigationsPromise;
+
+ // We were on #2 when history.back() was called so we should be on #1 now.
+ assert_equals(location.hash, "#1");
+
+ // While the history navigation back to "#1" was pending, we should have navigated to "#3".
+ assert_array_equals(navigations, ["#3", "#1"]);
+}, "Anchor with a fragment href and a click handler that navigates back");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-nav.html
new file mode 100644
index 0000000000..99d9a8fbb1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-nav.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document navigation after a cross-document navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ According to the spec, the navigate algorithm synchronously cancels ongoing
+ non-mature navigations.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, waitForPotentialNetworkLoads } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.search = "?1";
+ iframe.contentWindow.location.search = "?2";
+ assert_equals(iframe.contentWindow.location.search, "");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?2");
+
+ iframe.onload = t.unreached_func("second load event");
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "?2");
+}, "cross-document navigation then cross-document navigation");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-traversal.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-traversal.html
new file mode 100644
index 0000000000..341f66a996
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-traversal.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document traversal during cross-document navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ According to the spec, "apply the history step" will set the ongoing
+ navigation to "traversal", canceling any non-mature navigations.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.location.search = "?3";
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "must go back one step eventually");
+}, "cross-document navigations are stopped by cross-document back()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-nav.html
new file mode 100644
index 0000000000..99525cb3ed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-nav.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document navigation after a same-document navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ According to the spec, the "URL and history update steps" (used by
+ pushState()) and the fragment navigation steps, do *not* modify the ongoing
+ navigation, i.e. do not cancel any navigations.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.search = "?1";
+ iframe.contentWindow.location.hash = "#2";
+
+ assert_equals(iframe.contentWindow.location.search, "");
+ assert_equals(iframe.contentWindow.location.hash, "#2");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1");
+ assert_equals(iframe.contentWindow.location.hash, "");
+}, "cross-document navigation then fragment navigation");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.search = "?1";
+ iframe.contentWindow.history.pushState(null, "", "/2");
+
+ assert_equals(iframe.contentWindow.location.search, "");
+ assert_equals(iframe.contentWindow.location.pathname, "/2");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1");
+ assert_equals(iframe.contentWindow.location.pathname, "/common/blank.html");
+}, "cross-document navigation then pushState()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-traversal.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-traversal.html
new file mode 100644
index 0000000000..2ff91be7e1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-same-document-traversal.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document traversal during cross-document navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ According to the spec, "apply the history step" will set the ongoing
+ navigation to "traversal", canceling any navigation that is still processing
+ in parallel and hasn't yet reached "apply the history step".
+-->
+
+<body>
+<script type="module">
+import { createIframe, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#2";
+ await delay(t, 0);
+
+ iframe.contentWindow.location.search = "?1";
+ iframe.contentWindow.onload = t.unreached_func("load event fired");
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "", "must not go back synchronously (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously (hash)");
+
+ // Does go back eventually, and only one step
+ await t.step_wait(() => iframe.contentWindow.location.hash === "#1" && iframe.contentWindow.location.search === "");
+}, "cross-document navigations are stopped by same-document back()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-stop.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-stop.html
new file mode 100644
index 0000000000..0803d6c8d1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-stop.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Stop during cross-document navigations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script type="module">
+import { createIframe, waitForPotentialNetworkLoads } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.search = "?1";
+ iframe.contentWindow.onload = t.unreached_func("load event fired");
+ iframe.contentWindow.stop();
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "");
+}, "cross-document navigations are stopped by stop()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-nav.html
new file mode 100644
index 0000000000..5141259d08
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-nav.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document navigations during cross-document traversals</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ According to the spec, if ongoing navigation is "traversal", the navigation
+ fails and nothing happens.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, delay, waitForPotentialNetworkLoads } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+ const slowURL = (new URL("resources/slow.py", location.href)).href;
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.href = slowURL;
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.href = "/common/blank.html?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ iframe.contentWindow.location.href = "/common/blank.html?3";
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not navigate synchronously");
+
+ // We end up at slow.py and never at /common/blank.html?3
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.href, slowURL, "first load after the nav");
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.href, slowURL, "must stay on slow.py");
+}, "slow cross-document traversal and then fast cross-document navigation: traversal wins and nav is ignored");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+ const slowURL = (new URL("resources/slow.py", location.href)).href;
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ iframe.contentWindow.location.href = slowURL;
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not navigate synchronously");
+
+ // We end up at ?1 and never at slowURL
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "first load after the nav");
+
+ // The long timeout is because slow.py would take 2 seconds, if it did load.
+ await delay(t, 3000);
+ assert_equals(iframe.contentWindow.location.search, "?1", "must stay on ?1");
+}, "fast cross-document traversal and then slow cross-document navigation: traversal wins and nav is ignored");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-traversal.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-traversal.html
new file mode 100644
index 0000000000..97907df23d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-cross-document-traversal.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document traversals during cross-document traversals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ In the spec, all traversals are queued, and that includes computing what
+ "back" and "forward" mean, based on the "current session history step". The
+ "current session history step" is updated at the end of "apply the history
+ step", at which point the queued steps in "traverse history by a delta" get to
+ run and compute what is back/forward. So the basic structure is:
+
+ - back(), back(): go back once, then again.
+ - back(), forward(): go back once, then go forward.
+
+ However, note that these observable effects (e.g., actually loading an
+ intermediate document) are done via queued tasks. Those tasks will end up not
+ running, once we switch the active document due to the second traversal. So
+ the end observable result looks like:
+
+ - back(), back(): go back -2.
+ - back(), forward(): go nowhere.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, delay, waitForPotentialNetworkLoads } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?3";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.history.back();
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ assert_equals(iframe.contentWindow.location.search, "?2", "we made our way to ?2 for setup");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go forward synchronously");
+
+ iframe.onload = t.unreached_func("second load event");
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "?2", "must stay on ?2");
+}, "cross-document traversals in opposite directions: the result is going nowhere");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go forward synchronously");
+
+ iframe.onload = t.unreached_func("second load event");
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "?2", "must stay on ?2");
+}, "cross-document traversals in opposite directions, second traversal invalid at queuing time but valid at the time it is run: the result is going nowhere");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?3";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?3", "must not go back synchronously (1)");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?3", "must not go back synchronously (2)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "first load event must be going back");
+
+ iframe.onload = t.unreached_func("second load event");
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "?1", "must stay on ?1");
+}, "cross-document traversals in the same (back) direction: the result is going -2 with only one load event");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?3";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.history.back();
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ assert_equals(iframe.contentWindow.location.search, "?2", "we made our way to ?2 for setup");
+ iframe.contentWindow.history.back();
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ assert_equals(iframe.contentWindow.location.search, "?1", "we made our way to ?1 for setup");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?1", "must not go forward synchronously (1)");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?1", "must not go forward synchronously (2)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?3", "first load event must be going forward");
+
+ iframe.onload = t.unreached_func("second load event");
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "?3", "must stay on ?3");
+}, "cross-document traversals in the same (forward) direction: the result is going +2 with only one load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-nav.html
new file mode 100644
index 0000000000..df6258f9b3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-nav.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document navigations during cross-document traversals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ The spec explicitly covers this case, with a Jake diagram:
+ https://whatpr.org/html/6315/browsing-the-web.html#example-sync-navigation-steps-queue-jumping-basic
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ iframe.contentWindow.location.hash = "#3";
+ assert_equals(iframe.contentWindow.location.search, "?2");
+ assert_equals(iframe.contentWindow.location.hash, "#3");
+
+ // Eventually ends up on ?1
+ await t.step_wait(() => iframe.contentWindow.location.search === "?1" && iframe.contentWindow.location.hash === "");
+}, "same-document traversals + fragment navigations");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ iframe.contentWindow.history.pushState(null, "", "?3");
+ assert_equals(iframe.contentWindow.location.search, "?3");
+
+ // Eventually ends up on ?1
+ await t.step_wait(() => iframe.contentWindow.location.search === "?1");
+}, "same-document traversals + pushState()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-traversal.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-traversal.html
new file mode 100644
index 0000000000..3c37c46b64
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-same-document-traversal.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document traversals during cross-document traversals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ This case is not significantly different from
+ cross-document-traversal-cross-document-traversal.html.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, waitForHashchange, delay, waitForPotentialNetworkLoads } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#2";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.search = "?3";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?3", "must not go back synchronously 1 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously 1 (hash)");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?3", "must not go back synchronously 2 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously 2 (hash)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "first load event must be going back (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "first load event must be going back (hash)");
+
+ iframe.contentWindow.onhashchange = t.unreached_func("hashchange event");
+ iframe.onload = t.unreached_func("second load event");
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "?1", "must stay on ?1 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "must stay on ?1 (hash)");
+}, "traversals in the same (back) direction: coalesced");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#3";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.history.back();
+ await waitForHashchange(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.hash, "", "we made our way to ?2 for setup");
+ iframe.contentWindow.history.back();
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ assert_equals(iframe.contentWindow.location.search, "?1", "we made our way to ?1 for setup");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?1", "must not go forward synchronously 1 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "must not go forward synchronously 1 (hash)");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?1", "must not go forward synchronously 2 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "must not go forward synchronously 2 (hash)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?2", "first load event must be going forward (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#3", "first load event must be going forward (hash)");
+
+ iframe.contentWindow.onhashchange = t.unreached_func("hashchange event");
+ iframe.onload = t.unreached_func("second load event");
+
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "?2", "must stay on ?2");
+ assert_equals(iframe.contentWindow.location.hash, "#3", "must stay on ?2");
+}, "traversals in the same (forward) direction: coalesced");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-stop.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-stop.html
new file mode 100644
index 0000000000..6202eb9226
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-traversal-stop.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Stop during cross-document traversals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ The spec says that stop() must not stop traverals.
+
+ (Note: the spec also says the UI "stop" button must not stop traversals, but
+ that does not match browsers. See https://github.com/whatwg/html/issues/6905.
+ But that is not what's under test here.)
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously");
+
+ window.stop();
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "must go back eventually");
+}, "cross-document traversals are not stopped by stop()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/forward-to-pruned-entry.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/forward-to-pruned-entry.html
new file mode 100644
index 0000000000..8e1c349e21
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/forward-to-pruned-entry.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+ await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+ location.hash = "#1";
+ location.hash = "#2";
+ history.go(-2);
+ await new Promise(r => window.onpopstate = r);
+
+ // Traverse forward then immediately do a same-document push. This will
+ // truncate the back forward list.
+ history.forward();
+ location.hash = "#clobber";
+
+ // history.forward() should be aborted.
+ window.onpopstate = t.unreached_func("history.forward() should have been cancelled");
+ await new Promise(r => t.step_timeout(r, 20));
+ assert_equals(location.hash, "#clobber");
+}, "If forward pruning clobbers the target of a traverse, abort");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-1.html
new file mode 100644
index 0000000000..b52fa04977
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-1.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent main frame cancels a same-origin child whose navigation is pending</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ This test asserts that a parent canceling a same-origin child's cross-origin
+ navigation does not result in load events firing synchronously in the parent
+-->
+
+<body>
+
+<iframe src=resources/slow.py></iframe>
+
+<script>
+promise_test(async t => {
+ let window_load_fired = false;
+ let iframe_load_fired = false;
+ const iframe = document.querySelector('iframe');
+
+ const window_load_promise = new Promise(resolve => {
+ window.onload = () => {
+ window_load_fired = true;
+ resolve();
+ }
+ });
+
+ const iframe_onload_promise = new Promise(resolve => {
+ iframe.onload = () => {
+ iframe_load_fired = true;
+ resolve();
+ }
+ });
+
+ // While the child navigation is in-flight, cancel it and record when the
+ // parent `load` event fires.
+ window.frames[0].location.href = "resources/slow.py?different";
+
+ // Synchronously after cancelation, no load events should have been fired.
+ assert_false(window_load_fired,
+ "Parent's load event does not synchronously fire after cancelation");
+ assert_false(iframe_load_fired,
+ "<iframe> load event does not synchronously fire after cancelation");
+
+ // Load events did not fire in a microtask after cancelation.
+ await Promise.resolve();
+ assert_false(window_load_fired,
+ "Parent's load event does not fire in the microtask after cancelation");
+ assert_false(iframe_load_fired,
+ "<iframe> load event does not fire in the microtask after cancelation");
+
+ // Canceling the navigation should unblock the parent's load event, but the
+ // new iframe navigation should still be pending, and the iframe load event
+ // shouldn't fire until *that one* is complete.
+ await window_load_promise;
+ assert_true(window_load_fired,
+ "Parent's load event fires asynchronously after child navigation cancelation");
+ assert_false(iframe_load_fired,
+ "<iframe> load event does not fire until subsequent navigation is complete");
+}, "parent cancels a pending navigation in a same-origin child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-2.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-2.sub.html
new file mode 100644
index 0000000000..c081513b7c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-2.sub.html
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Grandparent main frame cancels a navigation in a cross-origin grandchild</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ This test asserts that an ancestor canceling a cross-origin descendant's
+ ongoing navigation does not result in load events firing in the ancestor
+ synchronously.
+
+ The reason this test uses a grandparent/grandchild pair to represent the
+ ancestor/descendant, instead of a parent/child pair, is because if a child
+ frame is blocking its parent window's load event, that means the child frame
+ navigation is being made from the initial about:blank Document to some
+ resource, and the initial about:blank child is synchronously scriptable from
+ the parent since they share the same window agent. This test is trying to
+ capture the scenario where the descendant document (that owns the ongoing
+ navigation) is hosted/scheduled on a different agent than the ancestor
+ document that cancels the descendant's ongoing navigation. The only way to do
+ this is to have a grandparent frame load a cross-origin child, whose document
+ itself loads a child frame that has a very slow ongoing navigation. That way
+ the grandparent can reach the grandchild via `window.frames[0].frames[0]`,
+ which is a proxy to the document living in a different agent.
+-->
+
+<body>
+
+<iframe src="http://{{domains[www1]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/nav-cancelation-2-helper.html"></iframe>
+
+<script>
+promise_test(async t => {
+ let window_load_fired = false;
+ let iframe_load_fired = false;
+ let grandchild_iframe_load_fired = false;
+ const iframe = document.querySelector('iframe');
+
+ const window_load_promise = new Promise(resolve => {
+ window.onload = () => {
+ window_load_fired = true;
+ resolve();
+ }
+ });
+
+ const iframe_onload_promise = new Promise(resolve => {
+ iframe.onload = () => {
+ iframe_load_fired = true;
+ resolve();
+ }
+ });
+
+ // Let the grandchild frame get registered in window.frames.
+ await new Promise((resolve, reject) => {
+ window.addEventListener('message', e => {
+ if (e.data != "grandchild frame created") {
+ reject(Error(`Expected 'grandchild frame created', but got ${e.data}`));
+ }
+
+ resolve();
+ }, {once: true});
+ });
+
+ // Set up a message handler to listen to the grandchild's iframe element's
+ // load event being fired. We should never get this message, and we assert
+ // this below. If we ever get this message, that means one of two things:
+ // 1.) The grandparent (this document)'s load event was blocked on the
+ // completion of its grandchild's subsequent navigation (after
+ // cancelation)
+ // 2.) After the grandchild's navigation was canceled, its <iframe>'s load
+ // event was fired before its subsequent navigation completed
+ // Both of these are wrong.
+ addEventListener('message', e => {
+ assert_equals(e.data, "grandchild frame loaded",
+ `Expected 'grandchild frame loaded', but got ${e.data}`);
+ grandchild_iframe_load_fired = true;
+ });
+
+ // While the grandchild navigation is in-flight, cancel it and record when the
+ // our `load` event fires. The second navigation is a slow resource so that
+ // the speed of the network doesn't cause the grandchild load event to fire
+ // early and confuse the grandparent when running the assertions below. We're
+ // trying to clearly separate out when the grandparent load event fires vs
+ // when the grandchild load event fires.
+ window.frames[0].frames[0].location.href = "resources/slow.py?different";
+
+ // Synchronously after cancelation, no load events should have been fired.
+ assert_false(window_load_fired,
+ "Grandparent's load event does not synchronously fire after grandchild " +
+ "navigation cancelation");
+ assert_false(iframe_load_fired,
+ "<iframe> load event does not synchronously fire after grandchild " +
+ "navigation cancelation");
+ assert_false(grandchild_iframe_load_fired,
+ "Grandchild <iframe>'s load event does not synchronously fire upon " +
+ "navigation cancelation");
+
+ // Load events did not fire in a microtask after cancelation.
+ await Promise.resolve();
+ assert_false(window_load_fired,
+ "Grandparent's load event does not fire in the microtask after " +
+ "navigation canceled");
+ assert_false(iframe_load_fired,
+ "<iframe> load event does not fire in the microtask after navigation " +
+ "canceled");
+ assert_false(grandchild_iframe_load_fired,
+ "Grandchild <iframe> load event does not fire in the microtask after " +
+ "navigation canceled");
+
+ // Canceling the navigation should however, asynchronously unblock, in this
+ // order:
+ // 1.) Our child window's load event, captured by our `iframe`'s load event
+ // 2.) Our window load event
+ // On the other hand, the grandchild navigation should still be ongoing, so
+ // inside our child's document, the nested <iframe> representing our
+ // grandchild should not have had its load event fired yet.
+ await iframe_onload_promise;
+ assert_true(iframe_load_fired);
+ assert_false(window_load_fired,
+ "Grandparent's load event does not fire before its child iframe's load " +
+ "event");
+ assert_false(grandchild_iframe_load_fired,
+ "Grandchild <iframe>'s load event does not fire before its parent's load " +
+ "event and grandparent's load event");
+
+ // We want to assert that the grandparent is not (incorrectly) blocked on its
+ // grandchild's second navigation from completing. One sign that it was
+ // incorrectly blocked on its grandchild's second navigation is if the
+ // grandparent receives a message (saying that the grandchild <iframe>
+ // element's load event fired) before the grandparent's load event fires.
+ //
+ // This indicates a weird state where the grandparent's immediate child fired
+ // its load event in response to navigation cancelation (see the assertions
+ // above), but the grandparent itself is still blocked on the grandchild
+ // loading. If this is the case, the the postMessage() (that sets
+ // `grandchild_iframe_load_fired = true`) is received by the grandparent just
+ // before the grandparent's load event is unblocked and fired. Therefore we
+ // can detect this situation by checking `grandchild_iframe_load_fired`.
+ await window_load_promise;
+ assert_true(iframe_load_fired);
+ assert_true(window_load_fired,
+ "Grandparent's load event fires asynchronously after grandchild " +
+ "navigation cancelation");
+ assert_false(grandchild_iframe_load_fired,
+ "Grandchild <iframe> load event doesn't fire before grandparent's " +
+ "load event");
+
+ // Verify that the grandchild <iframe>'s load event does not fire within one
+ // task of the grandchild's load event from being fired. This is to further
+ // verify that the grandparent's load event is not tied to its grandchild's
+ // second navigation.
+ //
+ // If for example, the grandparent's load event *is* blocked on the
+ // grandchild's second navigation from finishing, it is still possible for the
+ // grandparent's load event to fire. For example, Chromium has a bug where if
+ // both are true:
+ // 1.) The grandparent frame is in the same process as the grandchild frame
+ // 2.) The grandparent frame's load event is blocked on its grandchild's
+ // second navigation
+ //
+ // ...then the following will happen:
+ // 1.) The grandchild's load event will fire, triggering a postMessage() to
+ // the grandparent frame. This queues a task to run the grandparent's
+ // message handler.
+ // 2.) The grandparent's load event will *immediately* fire, and the
+ // postMessage() will fire a single task later since it is queued.
+ //
+ // Therefore, we assert that `grandchild_iframe_load_fired` is not true up to
+ // a single task after the grandparent's load event fires.
+ await new Promise(resolve => {
+ t.step_timeout(resolve, 0);
+ });
+
+ assert_false(grandchild_iframe_load_fired,
+ "Grandchild <iframe>'s load event does not fire at least one task " +
+ "after the grandparent's window load event fires. It should only fire " +
+ "when its subsequent navigation is complete");
+}, "grandparent cancels a pending navigation in a cross-origin grandchild");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/helpers.mjs b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/helpers.mjs
new file mode 100644
index 0000000000..7938497920
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/helpers.mjs
@@ -0,0 +1,52 @@
+export function createIframe(t) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(new Error("Could not load iframe"));
+ iframe.src = "/common/blank.html";
+
+ t.add_cleanup(() => iframe.remove());
+ document.body.append(iframe);
+ });
+}
+
+export function delay(t, ms) {
+ return new Promise(resolve => t.step_timeout(resolve, ms));
+}
+
+export function waitForLoad(obj) {
+ return new Promise(resolve => {
+ obj.addEventListener("load", resolve, { once: true });
+ });
+}
+
+export function waitForHashchange(obj) {
+ return new Promise(resolve => {
+ obj.addEventListener("hashchange", resolve, { once: true });
+ });
+}
+
+export function waitForPopstate(obj) {
+ return new Promise(resolve => {
+ obj.addEventListener("popstate", resolve, { once: true });
+ });
+}
+
+// This is used when we want to end the test by asserting some load doesn't
+// happen, but we're not sure how long to wait. We could just wait a long-ish
+// time (e.g. a second), but that makes the tests slow. Instead, assume that
+// network loads take roughly the same time. Then, you can use this function to
+// wait a small multiple of the duration of a separate iframe load; this should
+// be long enough to catch any problems.
+export async function waitForPotentialNetworkLoads(t) {
+ const before = performance.now();
+
+ // Sometimes we're doing something, like a traversal, which cancels our first
+ // attempt at iframe loading. In that case we bail out after 100 ms and try
+ // again. (Better ideas welcome...)
+ await Promise.race([createIframe(t), delay(t, 100)]);
+ await createIframe(t);
+
+ const after = performance.now();
+ await delay(t, after - before);
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/nav-cancelation-2-helper.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/nav-cancelation-2-helper.html
new file mode 100644
index 0000000000..a0b4acda2e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/nav-cancelation-2-helper.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page with child frame that navigates slowly</title>
+
+<!--
+ This file is used by `../nav-cancelation-2.sub.html`. The iframe below is its
+ grandchild iframe, and whenever its load event fires we report this up to our
+ parent Document.
+-->
+<iframe src="slow.py"></iframe>
+
+<script>
+ window.parent.postMessage("grandchild frame created", "*");
+ const iframe = document.querySelector('iframe');
+ iframe.onload = e => {
+ window.parent.postMessage("grandchild frame loaded", "*");
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/slow.py b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/slow.py
new file mode 100644
index 0000000000..5ee32a60ba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/resources/slow.py
@@ -0,0 +1,7 @@
+# Like /common/slow.py except with text/html content-type so that it won't
+# trigger strange parts of the <iframe> navigate algorithm.
+import time
+
+def main(request, response):
+ time.sleep(2)
+ return 200, [["Content-Type", "text/html"]], b''
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-nav.html
new file mode 100644
index 0000000000..8082e9bbe0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-nav.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document navigation after a same-document navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ These tests are kind of silly since it's hard to imagine any other result:
+ same-document navigations are always synchronous so of course the
+ same-document navigation will succeed, followed by the cross-document one.
+
+ Nevertheless they're nice as a basis from which to write corresponding app
+ history tests, where the consequences aren't as obvious.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.hash = "#1";
+ assert_equals(iframe.contentWindow.location.hash, "#1");
+
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?2");
+}, "fragment navigation then cross-document navigation");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.history.pushState(null, "", "?1");
+ assert_equals(iframe.contentWindow.location.search, "?1");
+
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?2");
+}, "pushState() then cross-document navigation");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-traversal.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-traversal.html
new file mode 100644
index 0000000000..fc6f92e819
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-cross-document-traversal.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Traversal after a same-document navigations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ These tests are kind of silly since it's hard to imagine any other result:
+ same-document navigations are always synchronous so of course back() won't
+ cancel them.
+
+ Nevertheless they're nice as a basis from which to write corresponding app
+ history tests, where the consequences aren't as obvious.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.location.hash = "#3";
+ iframe.contentWindow.history.go(-2);
+
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#3", "must not go back synchronously (hash)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "must go back eventually (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "must go back eventually (hash)");
+}, "fragment navigation then go(-2)");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+
+ iframe.contentWindow.history.pushState(null, "", "/3");
+ iframe.contentWindow.history.go(-2);
+
+ assert_equals(iframe.contentWindow.location.search, "", "must not go back synchronously (search)");
+ assert_equals(iframe.contentWindow.location.pathname, "/3", "must not go back synchronously (pathname)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "must go back eventually (search)");
+ assert_equals(iframe.contentWindow.location.pathname, "/common/blank.html", "must go back eventually (pathname)");
+
+}, "pushState then go(-2)");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-nav.html
new file mode 100644
index 0000000000..2d8961d6e4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-nav.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document navigation after a same-document navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ These tests are kind of silly since it's hard to imagine any other result:
+ same-document navigations are always synchronous so of course two in a row
+ will succeed.
+
+ Nevertheless they're nice as a basis from which to write corresponding app
+ history tests, where the consequences aren't as obvious.
+-->
+
+<body>
+<script type="module">
+import { createIframe } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.hash = "#1";
+ assert_equals(iframe.contentWindow.location.hash, "#1");
+
+ iframe.contentWindow.location.hash = "#2";
+ assert_equals(iframe.contentWindow.location.hash, "#2");
+}, "fragment navigation then fragment navigation");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.history.pushState(null, "", "?1");
+ assert_equals(iframe.contentWindow.location.search, "?1");
+
+ iframe.contentWindow.history.pushState(null, "", "?2");
+ assert_equals(iframe.contentWindow.location.search, "?2");
+}, "pushState() then pushState()");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.history.pushState(null, "", "?1");
+ assert_equals(iframe.contentWindow.location.search, "?1");
+
+ iframe.contentWindow.location.hash = "#2";
+ assert_equals(iframe.contentWindow.location.search, "?1");
+ assert_equals(iframe.contentWindow.location.hash, "#2");
+}, "pushState() then fragment navigation");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.hash = "#1";
+ assert_equals(iframe.contentWindow.location.hash, "#1");
+
+ iframe.contentWindow.history.pushState(null, "", "?2");
+ assert_equals(iframe.contentWindow.location.search, "?2");
+ assert_equals(iframe.contentWindow.location.hash, "");
+}, "fragment navigation then pushState()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-traversal.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-traversal.html
new file mode 100644
index 0000000000..a112143837
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-same-document-traversal.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document traversal after a same-document navigations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ These tests are kind of silly since it's hard to imagine any other result:
+ same-document navigations are always synchronous so of course back() won't
+ cancel them.
+
+ Nevertheless they're nice as a basis from which to write corresponding app
+ history tests, where the consequences aren't as obvious.
+-->
+
+<body>
+<script type="module">
+import { createIframe, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#2";
+ await delay(t, 0);
+
+ iframe.contentWindow.location.hash = "#3";
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.hash, "#3", "must not go back synchronously");
+
+ // Does go back eventually, and only one step
+ await t.step_wait(() => iframe.contentWindow.location.hash === "#2");
+}, "fragment navigation then back()");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.history.pushState(null, "", "?1");
+ await delay(t, 0);
+ iframe.contentWindow.history.pushState(null, "", "?2");
+ await delay(t, 0);
+
+ iframe.contentWindow.history.pushState(null, "", "?3");
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.search, "?3", "must not go back synchronously");
+
+ // Does go back eventually, and only one step
+ await t.step_wait(() => iframe.contentWindow.location.search === "?2");
+}, "pushState then back()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-stop.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-stop.html
new file mode 100644
index 0000000000..a9036209a5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-nav-stop.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Stop after a same-document navigations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script type="module">
+import { createIframe } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.location.hash = "#1";
+ iframe.contentWindow.stop();
+
+ assert_equals(iframe.contentWindow.location.hash, "#1");
+}, "fragment navigations are not stopped by stop()");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ iframe.contentWindow.history.pushState(null, "", "?1");
+ iframe.contentWindow.stop();
+
+ assert_equals(iframe.contentWindow.location.search, "?1");
+}, "pushState() navigations are not stopped by stop()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-nav.html
new file mode 100644
index 0000000000..37960c3c54
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-nav.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document navigations during same-document traversals</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ The spec says that navigations are ignored if there is an ongoing traversal.
+-->
+
+<body>
+<script type="module">
+import { createIframe, delay, waitForPotentialNetworkLoads } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#2";
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously");
+
+ iframe.contentWindow.location.search = "?1";
+ assert_equals(iframe.contentWindow.location.search, "", "must not navigate synchronously (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not navigate synchronously (hash)");
+
+ // Eventually ends up on #1.
+ await t.step_wait(() => iframe.contentWindow.location.hash === "#1", "traversal");
+
+ // Never loads a different document.
+ iframe.onload = t.unreached_func("load event");
+ await waitForPotentialNetworkLoads(t);
+ assert_equals(iframe.contentWindow.location.search, "", "must stay on #2 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must stay on #2 (hash)");
+}, "same-document traversals are not canceled by cross-document navigations");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-traversal.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-traversal.html
new file mode 100644
index 0000000000..a48f4d484f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-cross-document-traversal.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-document traversals during same-document traversals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ Compare this to cross-document-traversal-cross-document-traversal.html. Since
+ there are no network loads for the first traversal here, it does observably go
+ through. So we end up with both traversals before observable in sequence.
+-->
+
+<body>
+<script type="module">
+import { createIframe, waitForLoad, waitForHashchange, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.search = "?2";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#3";
+ await waitForHashchange(iframe.contentWindow);
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously 1 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#3", "must not go back synchronously 1 (hash)");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.search, "?2", "must not go back synchronously 1 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#3", "must not go back synchronously 1 (hash)");
+
+ await waitForHashchange(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.search, "?2", "first hashchange event must be going back (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "first hashchange event must be going back (hash)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?1", "first load event must be going back (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "first load event must be going back (hash)");
+}, "traversals in the same (back) direction: queued up");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ // Extra delay()s are necessary because if we navigate "inside" the load
+ // handler (i.e. in a promise reaction for the load handler) then it will
+ // be a replace navigation.
+ iframe.contentWindow.location.search = "?1";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#2";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.search = "?3";
+ await waitForLoad(iframe);
+ await delay(t, 0);
+ iframe.contentWindow.history.back();
+ await waitForLoad(iframe);
+ iframe.contentWindow.history.back();
+ await waitForHashchange(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.search, "?1", "we made our way to ?1 for setup (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "we made our way to ?1 for setup (search)");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?1", "must not go forward synchronously 1 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "must not go forward synchronously 1 (hash)");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.search, "?1", "must not go forward synchronously 2 (search)");
+ assert_equals(iframe.contentWindow.location.hash, "", "must not go forward synchronously 2 (hash)");
+
+ await waitForHashchange(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.search, "?1", "first hashchange event must be going forward (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#2", "first hashchange event must be going forward (hash)");
+
+ await waitForLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?3", "first load event must be going forward (search)");
+ assert_equals(iframe.contentWindow.location.hash, "#2", "first load event must be going forward (hash)");
+}, "traversals in the same (forward) direction: queued up");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-nav.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-nav.html
new file mode 100644
index 0000000000..5094651ab5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-nav.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document navigations during same-document traversals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ Per spec, same-document navigations ignore the "ongoing navigation" flag and
+ just happen synchronously, then queue onto the session history traversal queue
+ to update the source of truth. However, the traversal was queued first, so it
+ will ignore that update when calculating its endpoint.
+-->
+
+<body>
+<script type="module">
+import { createIframe, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#2";
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously");
+
+ iframe.contentWindow.location.hash = "#3";
+ assert_equals(iframe.contentWindow.location.hash, "#3");
+
+ // Eventually ends up on #1
+ await t.step_wait(() => iframe.contentWindow.location.hash === "#1");
+}, "same-document traversals are not canceled by fragment navigations and calculate their endpoint based on the original placement");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.history.pushState(null, "", "/1");
+ await delay(t, 0);
+ iframe.contentWindow.history.pushState(null, "", "/2");
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "must not go back synchronously");
+
+ iframe.contentWindow.history.pushState(null, "", "/3");
+ assert_equals(iframe.contentWindow.location.pathname, "/3");
+
+ // Eventually ends up on /1
+ await t.step_wait(() => iframe.contentWindow.location.pathname === "/1");
+}, "same-document traversals are not canceled by pushState() and calculate their endpoint based on the original placement");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-hashchange.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-hashchange.html
new file mode 100644
index 0000000000..df5ea5caab
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-hashchange.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document traversals during same-document traversals (using fragment navigations)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ Compare this to cross-document-traversal-cross-document-traversal.html. Since
+ there are no network loads or document unloads to cancel tasks, both
+ traversals should observably go through. Target step calculation for the
+ second traversal should take place after the first traversal is finished. So
+ we end up with both traversals observable in sequence.
+-->
+
+<body>
+<script type="module">
+import { createIframe, delay, waitForHashchange } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+ const baseURL = iframe.contentWindow.location.href;
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.hash = "#2";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.hash = "#3";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.history.back();
+ await waitForHashchange(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.hash, "#2", "we made our way to #2 for setup");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go forward synchronously");
+
+ const event1 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event1.oldURL, baseURL + "#2", "oldURL 1");
+ assert_equals(event1.newURL, baseURL + "#1", "newURL 1");
+ // Cannot test iframe.contentWindow.location.hash since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event2.oldURL, baseURL + "#1", "oldURL 2");
+ assert_equals(event2.newURL, baseURL + "#2", "newURL 2");
+ assert_equals(iframe.contentWindow.location.hash, "#2");
+}, "same-document traversals in opposite directions: queued up");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+ const baseURL = iframe.contentWindow.location.href;
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.hash = "#2";
+ await waitForHashchange(iframe.contentWindow);
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go forward synchronously");
+
+ const event1 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event1.oldURL, baseURL + "#2", "oldURL 1");
+ assert_equals(event1.newURL, baseURL + "#1", "newURL 1");
+ // Cannot test iframe.contentWindow.location.hash since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event2.oldURL, baseURL + "#1", "oldURL 2");
+ assert_equals(event2.newURL, baseURL + "#2", "newURL 2");
+ assert_equals(iframe.contentWindow.location.hash, "#2");
+}, "same-document traversals in opposite directions, second traversal invalid at queuing time: queued up");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+ const baseURL = iframe.contentWindow.location.href;
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.hash = "#2";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.hash = "#3";
+ await waitForHashchange(iframe.contentWindow);
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.hash, "#3", "must not go back synchronously (1)");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.hash, "#3", "must not go back synchronously (2)");
+
+ const event1 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event1.oldURL, baseURL + "#3", "oldURL 1");
+ assert_equals(event1.newURL, baseURL + "#2", "newURL 1");
+ // Cannot test iframe.contentWindow.location.hash since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event2.oldURL, baseURL + "#2", "oldURL 2");
+ assert_equals(event2.newURL, baseURL + "#1", "newURL 2");
+ assert_equals(iframe.contentWindow.location.hash, "#1");
+}, "same-document traversals in the same (back) direction: queue up");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+ const baseURL = iframe.contentWindow.location.href;
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.hash = "#2";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.location.hash = "#3";
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.history.back();
+ await waitForHashchange(iframe.contentWindow);
+ iframe.contentWindow.history.back();
+ await waitForHashchange(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.hash, "#1", "we made our way to #1 for setup");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.hash, "#1", "must not go forward synchronously (1)");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.hash, "#1", "must not go forward synchronously (2)");
+
+ const event1 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event1.oldURL, baseURL + "#1", "oldURL 1");
+ assert_equals(event1.newURL, baseURL + "#2", "newURL 1");
+ // Cannot test iframe.contentWindow.location.hash since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForHashchange(iframe.contentWindow);
+ assert_equals(event2.oldURL, baseURL + "#2", "oldURL 2");
+ assert_equals(event2.newURL, baseURL + "#3", "newURL 2");
+ assert_equals(iframe.contentWindow.location.hash, "#3");
+}, "same-document traversals in the same (forward) direction: queue up");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-pushstate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-pushstate.html
new file mode 100644
index 0000000000..47c7d6e3dc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-same-document-traversal-pushstate.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-document traversals during same-document traversals (using pushState())</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ Compare this to cross-document-traversal-cross-document-traversal.html. Since
+ there are no network loads or document unloads to cancel tasks, both
+ traversals should observably go through. Target step calculation for the
+ second traversal should take place after the first traversal is finished. So
+ we end up with both traversals observable in sequence.
+-->
+
+<body>
+<script type="module">
+import { createIframe, delay, waitForPopstate } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.history.pushState(1, "", "/1");
+ assert_equals(iframe.contentWindow.location.pathname, "/1", "setup /1");
+ iframe.contentWindow.history.pushState(2, "", "/2");
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "setup /2");
+ iframe.contentWindow.history.pushState(3, "", "/3");
+ assert_equals(iframe.contentWindow.location.pathname, "/3", "setup /3");
+ iframe.contentWindow.history.back();
+ await waitForPopstate(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "we made our way to /2 for setup");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "must not go back synchronously");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "must not go forward synchronously");
+
+ const event1 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event1.state, 1, "state 1");
+ // Cannot test iframe.contentWindow.location.pathname since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event2.state, 2, "state 2");
+ assert_equals(iframe.contentWindow.location.pathname, "/2");
+}, "same-document traversals in opposite directions: queued up");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.history.pushState(1, "", "/1");
+ assert_equals(iframe.contentWindow.location.pathname, "/1", "setup /1");
+ iframe.contentWindow.history.pushState(2, "", "/2");
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "we made our way to /2 for setup");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "must not go back synchronously");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "must not go forward synchronously");
+
+ const event1 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event1.state, 1, "state 1");
+ // Cannot test iframe.contentWindow.location.pathname since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event2.state, 2, "state 2");
+ assert_equals(iframe.contentWindow.location.pathname, "/2");
+}, "same-document traversals in opposite directions, second traversal invalid at queuing time: queued up");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.history.pushState(1, "", "/1");
+ assert_equals(iframe.contentWindow.location.pathname, "/1", "setup /1");
+ iframe.contentWindow.history.pushState(2, "", "/2");
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "setup /2");
+ iframe.contentWindow.history.pushState(3, "", "/3");
+ assert_equals(iframe.contentWindow.location.pathname, "/3", "we made our way to /3 for setup");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.pathname, "/3", "must not go back synchronously (1)");
+
+ iframe.contentWindow.history.back();
+ assert_equals(iframe.contentWindow.location.pathname, "/3", "must not go back synchronously (2)");
+
+ const event1 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event1.state, 2, "state 1");
+ // Cannot test iframe.contentWindow.location.pathname since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event2.state, 1, "state 2");
+ assert_equals(iframe.contentWindow.location.pathname, "/1");
+}, "same-document traversals in the same (back) direction: queue up");
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.history.pushState(1, "", "/1");
+ assert_equals(iframe.contentWindow.location.pathname, "/1", "setup /1");
+ iframe.contentWindow.history.pushState(2, "", "/2");
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "setup /2");
+ iframe.contentWindow.history.pushState(3, "", "/3");
+ assert_equals(iframe.contentWindow.location.pathname, "/3", "setup /3");
+ iframe.contentWindow.history.back();
+ await waitForPopstate(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.pathname, "/2", "setup /2 again");
+ iframe.contentWindow.history.back();
+ await waitForPopstate(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.pathname, "/1", "we made our way to /1 for setup");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.pathname, "/1", "must not go forward synchronously (1)");
+
+ iframe.contentWindow.history.forward();
+ assert_equals(iframe.contentWindow.location.pathname, "/1", "must not go forward synchronously (2)");
+
+ const event1 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event1.state, 2, "state 1");
+ // Cannot test iframe.contentWindow.location.pathname since the second history
+ // traversal task is racing with the fire an event task, so we don't know
+ // which will happen first.
+
+ const event2 = await waitForPopstate(iframe.contentWindow);
+ assert_equals(event2.state, 3, "state 2");
+ assert_equals(iframe.contentWindow.location.pathname, "/3");
+}, "same-document traversals in the same (forward) direction: queue up");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-stop.html b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-stop.html
new file mode 100644
index 0000000000..2f0570380a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/same-document-traversal-stop.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Stop during same-document traversals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ The spec says that stop() must not stop traverals.
+
+ (Note: the spec also says the UI "stop" button must not stop traversals, but
+ that does not match browsers. See https://github.com/whatwg/html/issues/6905.
+ But that is not what's under test here.)
+-->
+
+<body>
+<script type="module">
+import { createIframe, delay } from "./resources/helpers.mjs";
+
+promise_test(async t => {
+ const iframe = await createIframe(t);
+
+ // Setup
+ iframe.contentWindow.location.hash = "#1";
+ await delay(t, 0);
+ iframe.contentWindow.location.hash = "#2";
+ await delay(t, 0);
+
+ iframe.contentWindow.history.back();
+
+ assert_equals(iframe.contentWindow.location.hash, "#2", "must not go back synchronously");
+
+ window.stop();
+
+ // Does go back eventually
+ await t.step_wait(() => iframe.contentWindow.location.hash === "#1");
+}, "same-document traversals are not stopped by stop()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html
new file mode 100644
index 0000000000..5f3e95cce4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>Test cross origin load of media document in parts</title>
+<link rel="motivation" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1781759">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body></body>
+<script>
+promise_test(async () => {
+ const frame = document.createElement('iframe');
+ const dir = location.pathname.replace(/\/[^\/]*$/, '/');
+ frame.src =
+ // remote origin intermediate document
+ get_host_info().HTTP_NOTSAMESITE_ORIGIN + dir
+ // iframe-document.sub.html has an iframe with src=childsrc.
+ + 'resources/iframe-document.sub.html?childsrc='
+ // same origin video document, so that we can play().
+ + get_host_info().ORIGIN
+ // 'PartialContent' ensures that the entire video resource does not load
+ // in one fetch.
+ + '/service-workers/service-worker/resources/fetch-access-control.py?'
+ + 'VIDEO%26PartialContent';
+
+ let v = document.createElement("video");
+ if (v.canPlayType("video/ogv") == "")
+ frame.src += "%26mp4";
+
+ document.body.appendChild(frame);
+ await new Promise(resolve => frame.onload = resolve);
+
+ const inner = frame.contentWindow.frames[0];
+ const video = inner.document.body.childNodes[0];
+ video.muted = true; // to allow playback
+ return video.play();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image-in-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image-in-popup.html
new file mode 100644
index 0000000000..e9284824f4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image-in-popup.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Media documents: image</title>
+ <link rel="author" title="Takayoshi Kochi" href="mailto:kochi@chromium.org">
+ <link rel="author" title="Michael Ventnor" href="mailto:mventnor@mozilla.com">
+ <link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#read-media">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+<script>
+ var t = async_test("The document for a standalone media file should have one child in the body.");
+
+ var imgwin = window.open('/images/blue.png');
+ imgwin.onload = t.step_func_done(function() {
+ assert_equals(imgwin.opener, window);
+ assert_equals(imgwin.document.contentType, "image/png");
+ var imgwinChildren = imgwin.document.body.childNodes;
+ assert_equals(imgwinChildren.length, 1, "Body of image document has 1 child");
+ assert_equals(imgwinChildren[0].nodeName, "IMG", "Only child of body must be an <img> element");
+ assert_equals(imgwinChildren[0].namespaceURI, "http://www.w3.org/1999/xhtml",
+ "Only child of body must be an HTML element");
+ imgwin.close();
+ });
+</script>
+</head>
+<body>
+ <div id="log"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image.html b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image.html
new file mode 100644
index 0000000000..67c97bafdd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-image.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Media documents: image</title>
+ <link rel="author" title="Michael Ventnor" href="mailto:mventnor@mozilla.com">
+ <link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#read-media">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+<script>
+ var t = async_test("The document for a standalone media file should have one child in the body.");
+
+ function frameLoaded() {
+ var testframe = document.getElementById('testframe');
+ assert_equals(testframe.contentDocument.contentType, "image/png");
+ assert_equals(testframe.contentDocument.compatMode, "CSS1Compat", "Media documents should be in standards mode");
+ var testframeChildren = testframe.contentDocument.body.childNodes;
+ assert_equals(testframeChildren.length, 1, "Body of image document has 1 child");
+ assert_equals(testframeChildren[0].nodeName, "IMG", "Only child of body must be an <img> element");
+ assert_equals(testframeChildren[0].namespaceURI, "http://www.w3.org/1999/xhtml",
+ "Only child of body must be an HTML element");
+ t.done();
+ }
+</script>
+</head>
+<body>
+ <div id="log"></div>
+ <iframe id="testframe" onload="t.step(frameLoaded)" src="/images/blue.png"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-video.html b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-video.html
new file mode 100644
index 0000000000..9bce1f0c36
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/pageload-video.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Media documents: video</title>
+<link rel="author" title="Michael Ventnor" href="mailto:mventnor@mozilla.com">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#read-media">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<script>
+async_test(function() {
+ var testframe = document.createElement('iframe');
+ var url = getVideoURI("/media/A4");
+ var contentType = getMediaContentType(url);
+ testframe.onload = this.step_func_done(function() {
+ assert_equals(testframe.contentDocument.contentType, contentType);
+ assert_equals(testframe.contentDocument.compatMode, "CSS1Compat", "Media documents should be in standards mode");
+ var testframeChildren = testframe.contentDocument.body.childNodes;
+ assert_equals(testframeChildren.length, 1, "Body of image document has 1 child");
+ assert_equals(testframeChildren[0].nodeName, "VIDEO", "Only child of body must be an <video> element");
+ assert_equals(testframeChildren[0].namespaceURI, "http://www.w3.org/1999/xhtml",
+ "Only child of body must be an HTML element");
+ });
+ testframe.src = url;
+ document.body.appendChild(testframe);
+}, "The document for a standalone media file should have one child in the body.");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/resources/iframe-document.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/resources/iframe-document.sub.html
new file mode 100644
index 0000000000..69523efa7a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/resources/iframe-document.sub.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<html>
+<iframe src="{{GET[childsrc]}}">
+</iframe>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/read-text/load-text-plain.html b/testing/web-platform/tests/html/browsers/browsing-the-web/read-text/load-text-plain.html
new file mode 100644
index 0000000000..ad75631533
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/read-text/load-text-plain.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>Page load processing model for text files</title>
+<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#read-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("Checking document metadata for text file");
+var tD = async_test("Checking DOM for text file");
+var tC = async_test("Checking contents for text file");
+var iframe = document.body.appendChild(document.createElement("iframe"));
+iframe.onload = function(e) {
+ var doc = iframe.contentDocument;
+ t.step(function() {
+ assert_equals(doc.compatMode, "CSS1Compat");
+ assert_equals(doc.contentType, "text/plain");
+ assert_equals(doc.doctype, null);
+ t.done();
+ })
+ tD.step(function() {
+ assert_equals(doc.childNodes.length, 1, "Document should have 1 child")
+ assert_equals(doc.documentElement.tagName, "HTML");
+ assert_equals(doc.documentElement.childNodes.length, 2,
+ "Root element should have 2 children")
+ assert_equals(doc.documentElement.firstChild.tagName, "HEAD");
+ assert_equals(doc.documentElement.lastChild.tagName, "BODY");
+ assert_equals(doc.documentElement.lastChild.childNodes.length, 1,
+ "Body element should have 1 child")
+ assert_equals(doc.documentElement.lastChild.firstChild.tagName, "PRE");
+ tD.done();
+ })
+ tC.step(function() {
+ assert_equals(doc.documentElement.lastChild.firstChild.firstChild.data,
+ "This is a sample text/plain document.\n\nThis is not an HTML document.\n\n");
+ tC.done();
+ })
+};
+iframe.src = "../../../../common/text-plain.txt";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addHTML.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addHTML.window.js
new file mode 100644
index 0000000000..0c093cc462
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addHTML.window.js
@@ -0,0 +1,20 @@
+// META: title=RemoteContextWrapper addHtml
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const main = await rcHelper.addWindow();
+ await assertSimplestScriptRuns(main);
+
+ await main.addHTML('<div id=div-id>div-content</div>');
+ await assertFunctionRuns(
+ main, () => document.getElementById('div-id').textContent, 'div-content');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc-startOn.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc-startOn.window.js
new file mode 100644
index 0000000000..2ef047c212
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc-startOn.window.js
@@ -0,0 +1,24 @@
+// META: title=RemoteContextWrapper addIframe with srcdoc and startOn
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const main = await rcHelper.addWindow();
+
+ const iframe = await main.addIframeSrcdoc(
+ /*extraConfig=*/ {startOn: 'pageshow'});
+
+ await assertSimplestScriptRuns(iframe);
+ await assert_equals(
+ await iframe.executeScript(() => {
+ return executorStartEvent.type;
+ }),
+ 'pageshow');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc.window.js
new file mode 100644
index 0000000000..1a959cbaf0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe-srcdoc.window.js
@@ -0,0 +1,27 @@
+// META: title=RemoteContextWrapper addIframe with srcdoc
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const main = await rcHelper.addWindow();
+
+ const iframe = await main.addIframeSrcdoc(
+ /*extraConfig=*/ {scripts: ['./resources/test-script.js']},
+ /*attributes=*/ {id: 'test-id'},
+ );
+
+ await assertSimplestScriptRuns(iframe);
+ await assertFunctionRuns(iframe, () => testFunction(), 'testFunction exists');
+
+ assert_equals(
+ await main.executeScript(() => document.getElementById('test-id').id),
+ 'test-id', 'verify id');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe.window.js
new file mode 100644
index 0000000000..c1630e4680
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addIframe.window.js
@@ -0,0 +1,40 @@
+// META: title=RemoteContextWrapper addIframe
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ // Precondition: Test was loaded from the HTTP_ORIGIN.
+ assert_equals(
+ location.origin, get_host_info()['HTTP_ORIGIN'],
+ 'test window was loaded on HTTP_ORIGIN');
+
+ const rcHelper = new RemoteContextHelper();
+
+ const main = await rcHelper.addWindow();
+
+ const headerName = 'x-wpt-test-header';
+ const headerValue = 'test-escaping()';
+ const iframe = await main.addIframe(
+ /*extraConfig=*/ {
+ origin: 'HTTP_REMOTE_ORIGIN',
+ scripts: ['/common/get-host-info.sub.js', './resources/test-script.js'],
+ headers: [[headerName, headerValue]],
+ },
+ /*attributes=*/ {id: 'test-id'},
+ );
+
+ await assertSimplestScriptRuns(iframe);
+ await assertFunctionRuns(iframe, () => testFunction(), 'testFunction exists');
+ await assertOriginIsAsExpected(iframe, get_host_info()['HTTP_REMOTE_ORIGIN']);
+ await assertHeaderIsAsExpected(iframe, headerName, headerValue);
+
+ assert_equals(
+ await main.executeScript(() => document.getElementById('test-id').id),
+ 'test-id', 'verify id');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addScripts.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addScripts.window.js
new file mode 100644
index 0000000000..01cf06c65d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addScripts.window.js
@@ -0,0 +1,19 @@
+// META: title=RemoteContextWrapper addScripts
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const main = await rcHelper.addWindow();
+ await assertSimplestScriptRuns(main);
+
+ await main.addScripts(['./resources/test-script.js']);
+ await assertFunctionRuns(main, () => testFunction(), 'testFunction exists');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-defaults.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-defaults.window.js
new file mode 100644
index 0000000000..34fa9cd39a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-defaults.window.js
@@ -0,0 +1,15 @@
+// META: title=RemoteContextHelper with defaults
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const main = await rcHelper.addWindow();
+ await assertSimplestScriptRuns(main);
+ await assertOriginIsAsExpected(main, location.origin);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-extra-config.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-extra-config.window.js
new file mode 100644
index 0000000000..112d2d726e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-extra-config.window.js
@@ -0,0 +1,36 @@
+// META: title=RemoteContextHelper addWindow with extra config
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ const header1Name = 'x-wpt-test-header1';
+ const header1Value = 'test-escaping1()';
+ const rcHelper = new RemoteContextHelper({
+ origin: 'HTTP_REMOTE_ORIGIN',
+ scripts: ['/common/get-host-info.sub.js', './resources/test-script.js'],
+ headers: [[header1Name, header1Value]],
+ });
+
+ const header2Name = 'x-wpt-test-header2';
+ const header2Value = 'test-escaping2()';
+ const main = await rcHelper.addWindow(
+ {
+ origin: location.origin,
+ scripts: [new URL('./resources/test-script2.js', location).toString()],
+ headers: [[header2Name, header2Value]],
+ },
+ );
+
+ await assertSimplestScriptRuns(main);
+ await assertFunctionRuns(main, () => testFunction(), 'testFunction exists');
+ await assertFunctionRuns(main, () => testFunction2(), 'testFunction2 exists');
+ await assertOriginIsAsExpected(main, location.origin);
+ await assertHeaderIsAsExpected(main, header1Name, header1Value);
+ await assertHeaderIsAsExpected(main, header2Name, header2Value);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-features.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-features.window.js
new file mode 100644
index 0000000000..329c55e626
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-features.window.js
@@ -0,0 +1,23 @@
+// META: title=RemoteContextHelper addWindow features
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ {
+ const main = await rcHelper.addWindow();
+ await assertSimplestScriptRuns(main);
+ await assertWindowHasOpenerEquals(main, true);
+ }
+ {
+ const main = await rcHelper.addWindow(
+ /*extraConfig=*/ null, /*options=*/ {features: 'noopener'});
+ await assertSimplestScriptRuns(main);
+ await assertWindowHasOpenerEquals(main, false);
+ }
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-invalid-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-invalid-origin.window.js
new file mode 100644
index 0000000000..58aee312cc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-invalid-origin.window.js
@@ -0,0 +1,21 @@
+// META: title=RemoteContextHelper addWindow with extra config
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ const header1Name = 'x-wpt-test-header1';
+ const header1Value = 'test-escaping1()';
+ const rcHelper = new RemoteContextHelper({
+ origin: 'INVALID',
+ });
+
+ promise_rejects_js(
+ t, RangeError, rcHelper.addWindow(),
+ 'Exception should be thrown for invalid origin.');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-startOn.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-startOn.window.js
new file mode 100644
index 0000000000..0f57a87638
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-startOn.window.js
@@ -0,0 +1,19 @@
+// META: title=RemoteContextHelper addWindow target
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const main = await rcHelper.addWindow({startOn: 'pageshow'});
+ await assertSimplestScriptRuns(main);
+ await assert_equals(
+ await main.executeScript(() => {
+ return executorStartEvent.type;
+ }),
+ 'pageshow');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-target.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-target.window.js
new file mode 100644
index 0000000000..3f742048b4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWindow-target.window.js
@@ -0,0 +1,17 @@
+// META: title=RemoteContextHelper addWindow target
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const name = 'a name';
+ const main = await rcHelper.addWindow(
+ /*extraConfig=*/ null, /*options=*/ {target: name});
+ await assertSimplestScriptRuns(main);
+ await assertWindowNameEquals(main, name);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWorker.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWorker.window.js
new file mode 100644
index 0000000000..d4ba8b0312
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/addWorker.window.js
@@ -0,0 +1,29 @@
+// META: title=RemoteContextWrapper addWorker
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const main = await rcHelper.addWindow();
+
+ const headerName = 'x-wpt-test-header';
+ const headerValue = 'test-escaping()';
+ const worker = await main.addWorker(
+ {
+ scripts: ['/common/get-host-info.sub.js', './resources/test-script.js'],
+ headers: [[headerName, headerValue]],
+ },
+ );
+
+ await assertSimplestScriptRuns(worker);
+ await assertFunctionRuns(worker, () => testFunction(), 'testFunction exists');
+ await assertOriginIsAsExpected(worker, location.origin);
+ await assertHeaderIsAsExpected(worker, headerName, headerValue);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/constructor.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/constructor.window.js
new file mode 100644
index 0000000000..dcbb3dabbc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/constructor.window.js
@@ -0,0 +1,38 @@
+// META: title=RemoteContextHelper constructor
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+// This tests that arguments passed to the constructor are respected.
+promise_test(async t => {
+ // Precondition: Test was loaded from the HTTP_ORIGIN.
+ assert_equals(
+ location.origin, get_host_info()['HTTP_ORIGIN'],
+ 'test window was loaded on HTTP_ORIGIN');
+
+ const headerName = 'x-wpt-test-header';
+ const headerValue = 'test-escaping()';
+ const rcHelper = new RemoteContextHelper({
+ origin: 'HTTP_REMOTE_ORIGIN',
+ scripts: [
+ '/common/get-host-info.sub.js',
+ './resources/test-script.js',
+ ],
+ headers: [[headerName, headerValue]],
+ });
+
+
+ const main = await rcHelper.addWindow();
+
+ await assertSimplestScriptRuns(main);
+ await assertFunctionRuns(main, () => testFunction(), 'testFunction exists');
+
+ // Verify that the origin is different.
+ await assertOriginIsAsExpected(main, get_host_info()['HTTP_REMOTE_ORIGIN']);
+
+ await assertHeaderIsAsExpected(main, headerName, headerValue);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/createContext-bad-executorCreator.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/createContext-bad-executorCreator.window.js
new file mode 100644
index 0000000000..bf24581173
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/createContext-bad-executorCreator.window.js
@@ -0,0 +1,20 @@
+// META: title=RemoteContextHelper createContext with throwing/rejecting executorCreators.
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const err = new Error('something bad!');
+ promise_rejects_exactly(
+ t, err, rcHelper.createContext({ executorCreator() { throw err; } }),
+ 'Sync exception must be rethrown');
+
+ promise_rejects_exactly(
+ t, err, rcHelper.createContext({ executorCreator() { return Promise.reject(err); } }),
+ 'Async rejection must be rethrown');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/getRequestHeaders.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/getRequestHeaders.window.js
new file mode 100644
index 0000000000..90610e27fb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/getRequestHeaders.window.js
@@ -0,0 +1,18 @@
+// META: title=RemoteContextHelper with defaults
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const main = await rcHelper.addWindow();
+
+ const headers = await main.getRequestHeaders();
+
+ assert_equals(headers.constructor, Headers);
+ assert_true(headers.has("user-agent"), "user-agent is present");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigateToNew.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigateToNew.window.js
new file mode 100644
index 0000000000..f7dd3f8325
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigateToNew.window.js
@@ -0,0 +1,37 @@
+// META: title=RemoteContextWrapper navigateToNew
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ // Precondition: Test was loaded from the HTTP_ORIGIN.
+ assert_equals(
+ location.origin, get_host_info()['HTTP_ORIGIN'],
+ 'test window was loaded on HTTP_ORIGIN');
+
+ const rcHelper = new RemoteContextHelper();
+
+ const main = await rcHelper.addWindow();
+
+ const headerName = 'x-wpt-test-header';
+ const headerValue = 'test-escaping()';
+ const newMain = await main.navigateToNew(
+ {
+ origin: 'HTTP_REMOTE_ORIGIN',
+ scripts: ['/common/get-host-info.sub.js', './resources/test-script.js'],
+ headers: [[headerName, headerValue]],
+ },
+ );
+
+ await assertSimplestScriptRuns(newMain);
+ await assertFunctionRuns(
+ newMain, () => testFunction(), 'testFunction exists');
+
+ const remoteOrigin = get_host_info()['HTTP_REMOTE_ORIGIN'];
+ await assertOriginIsAsExpected(newMain, remoteOrigin);
+ await assertHeaderIsAsExpected(newMain, headerName, headerValue);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-bfcache.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-bfcache.window.js
new file mode 100644
index 0000000000..1fa90a9064
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-bfcache.window.js
@@ -0,0 +1,35 @@
+// META: title=RemoteContextHelper navigation using BFCache
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ // Open a window with noopener so that BFCache will work.
+ const rc1 = await rcHelper.addWindow(
+ /*config=*/ null, /*options=*/ {features: 'noopener'});
+
+ // Add a pageshow listener to stash the event.
+ await rc1.executeScript(() => {
+ window.addEventListener('pageshow', (event) => {
+ window.pageshowEvent = event;
+ });
+ });
+
+ // Navigate away.
+ const rc2 = await rc1.navigateToNew();
+ await assertSimplestScriptRuns(rc2);
+
+ // Navigate back.
+ await rc2.historyBack();
+
+ // Verify that the document was BFCached.
+ assert_true(await rc1.executeScript(() => {
+ return window.pageshowEvent.persisted;
+ }));
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-helpers.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-helpers.window.js
new file mode 100644
index 0000000000..079e20661e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-helpers.window.js
@@ -0,0 +1,28 @@
+// META: title=RemoteContextHelper navigation helpers
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+// META: timeout=long
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const rc1 = await rcHelper.addWindow();
+ await assertSimplestScriptRuns(rc1);
+
+ const rc2 = await rc1.navigateToNew();
+ await assertSimplestScriptRuns(rc2);
+
+ await rc2.historyBack();
+ await assertSimplestScriptRuns(rc1);
+
+ await rc1.historyForward();
+ await assertSimplestScriptRuns(rc2);
+
+ const rc3 = await rc2.navigateToNew();
+ await rc3.historyGo(-2);
+ await assertSimplestScriptRuns(rc1);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-same-document.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-same-document.window.js
new file mode 100644
index 0000000000..6f637e2b90
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-same-document.window.js
@@ -0,0 +1,39 @@
+// META: title=RemoteContextHelper navigation using BFCache
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/test-helper.js
+
+'use strict';
+
+async function assertLocationIs(remoteContextWrapper, expectedLocation) {
+ assert_equals(
+ await remoteContextWrapper.executeScript(() => {
+ return location.toString();
+ }),
+ expectedLocation, 'verify location');
+}
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const rc = await rcHelper.addWindow();
+
+ const oldLocation = await rc.executeScript(() => {
+ return location.toString();
+ });
+ const newLocation = oldLocation + '#fragment';
+
+ // Navigate to same document.
+ await rc.navigateTo(newLocation);
+
+ // Verify that the window navigated.
+ await assertLocationIs(rc, newLocation);
+
+ // Navigate back.
+ await rc.historyBack(oldLocation);
+
+ // Verify that the window navigated back and the executor is running.
+ await assertLocationIs(rc, oldLocation);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
new file mode 100644
index 0000000000..71cd87e553
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
@@ -0,0 +1,48 @@
+async function assertSimplestScriptRuns(remoteContextWrapper) {
+ assert_equals(
+ await remoteContextWrapper.executeScript(() => {
+ return 1;
+ }),
+ 1, 'simplest script runs');
+}
+
+async function assertFunctionRuns(
+ remoteContextWrapper, functionToRun, expectedReturn) {
+ assert_equals(
+ await remoteContextWrapper.executeScript(functionToRun), expectedReturn,
+ 'function runs');
+}
+
+async function assertOriginIsAsExpected(remoteContextWrapper, expectedOrigin) {
+ assert_equals(
+ await remoteContextWrapper.executeScript(() => {
+ return location.origin;
+ }),
+ expectedOrigin, 'verify origin');
+}
+
+async function assertWindowNameEquals(remoteContextWrapper, expectedName) {
+ assert_equals(
+ await remoteContextWrapper.executeScript(() => {
+ return window.name;
+ }),
+ expectedName, 'verify name');
+}
+
+async function assertWindowHasOpenerEquals(remoteContextWrapper, hasParent) {
+ assert_equals(
+ await remoteContextWrapper.executeScript(() => {
+ return !!window.opener;
+ }),
+ hasParent, 'verify opener');
+}
+
+async function assertHeaderIsAsExpected(
+ remoteContextWrapper, headerName, headerValue) {
+ assert_equals(
+ headerValue,
+ await remoteContextWrapper.executeScript(async (headerName) => {
+ const res = await fetch(location);
+ return res.headers.get(headerName);
+ }, [headerName]), 'header is set');
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script.js
new file mode 100644
index 0000000000..d1c02cab29
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script.js
@@ -0,0 +1,3 @@
+function testFunction() {
+ return 'testFunction exists';
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script2.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script2.js
new file mode 100644
index 0000000000..f9e72c442b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-script2.js
@@ -0,0 +1,3 @@
+function testFunction2() {
+ return 'testFunction2 exists';
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-common.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-common.js
new file mode 100644
index 0000000000..ce9b5f9116
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-common.js
@@ -0,0 +1,14 @@
+// Functions available by default in the executor.
+
+'use strict';
+
+let executor;
+
+// Expects addScript to be present (window or worker version).
+function addScripts(urls) {
+ return Promise.all(urls.map(addScript));
+}
+
+function startExecutor(uuid) {
+ executor = new Executor(uuid);
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.js
new file mode 100644
index 0000000000..54df6735c9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.js
@@ -0,0 +1,67 @@
+// Functions available by default in the executor.
+
+'use strict';
+
+let executorStartEvent = null;
+
+function requestExecutor(uuid, startOn) {
+ if (startOn) {
+ addEventListener(startOn, (e) => {
+ executorStartEvent = e;
+ startExecutor(uuid);
+ });
+ } else {
+ startExecutor(uuid);
+ }
+}
+
+function addScript(url) {
+ const script = document.createElement('script');
+ script.src = url;
+ const promise = new Promise((resolve, reject) => {
+ script.onload = () => resolve(url);
+ script.onerror = (e) => reject(e);
+ });
+ document.body.appendChild(script);
+ return promise;
+}
+
+/**
+ * Suspends the executor and executes the function in `fnString` when it has
+ * suspended. Installs a pageshow handler to resume the executor if the
+ * document is BFCached. Installs a hashchange handler to detect when the
+ * navigation did not change documents.
+ *
+ * This returns nothing because fn is invoke after waiting for the document to
+ * be suspended. If we were to return a promise, the executor could not suspend
+ * until that promise resolved but the promise cannot resolve until the executor
+ * is suspended. This could be avoided by adding support
+ * directly in the dispatcher for tasks suspend immediately after execution.
+ *
+ * @param {string} fnString A stringified function to be executed.
+ * @param {any[]} args The arguments to pass to the function.
+ */
+function executeScriptToNavigate(fnString, args) {
+ // Only one of these listeners should run.
+ const controller = new AbortController();
+ window.addEventListener('pageshow', (event) => {
+ controller.abort();
+ executor.resume();
+ }, {signal: controller.signal, once: true});
+ window.addEventListener('hashchange', (event) => {
+ controller.abort();
+ const oldURLObject = new URL(event.oldURL);
+ const newURLObject = new URL(event.newURL);
+ oldURLObject.hash = '';
+ newURLObject.hash = '';
+ // If only the hash-fragment changed then the navigation was
+ // same-document and we should resume the executor.
+ if (oldURLObject.toString() == newURLObject.toString()) {
+ executor.resume();
+ }
+ }, {signal: controller.signal, once: true});
+
+ executor.suspend(() => {
+ eval(fnString).apply(null, args);
+ });
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.py b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.py
new file mode 100644
index 0000000000..1a660100b9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.py
@@ -0,0 +1,40 @@
+import html
+import json
+from urllib import parse
+
+def main(request, response):
+ initRequestHeaders = ""
+ for header_name in request.headers.keys():
+ for header_value in request.headers.get_list(header_name):
+ js_name = json.dumps(header_name.lower().decode("utf-8"))
+ js_value = json.dumps(header_value.decode("utf-8"))
+ initRequestHeaders += f"window.__requestHeaders.append({js_name}, {js_value});\n"
+ if (b"status" in request.GET):
+ status = int(request.GET.first(b"status"))
+ else:
+ status = 200
+ query = parse.parse_qs(request.url_parts.query)
+ scripts = []
+ for script in query.get("script", []):
+ scripts.append(f"<script src='{html.escape(script)}'></script>")
+ scripts_s = "\n".join(scripts)
+
+ uuid = query.get("uuid")[0]
+
+ start_on = query.get("startOn")
+ start_on_s = f"'{start_on[0]}'" if start_on else "null"
+
+ return (status, [("Content-Type", "text/html")], f"""
+<!DOCTYPE HTML>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="./executor-common.js"></script>
+<script src="./executor-window.js"></script>
+
+{scripts_s}
+<body>
+<script>
+window.__requestHeaders = new Headers();
+{initRequestHeaders}
+requestExecutor("{uuid}", {start_on_s});
+</script>
+""")
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-worker.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-worker.js
new file mode 100644
index 0000000000..0ca136191e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/executor-worker.js
@@ -0,0 +1,12 @@
+'use strict';
+
+importScripts('/common/dispatcher/dispatcher.js', './executor-common.js');
+
+function addScript(url) {
+ importScripts(url);
+}
+
+const params = new URLSearchParams(location.search);
+addScripts(params.getAll('script'));
+
+startExecutor(params.get('uuid'));
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
new file mode 100644
index 0000000000..aa24b36e8b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
@@ -0,0 +1,592 @@
+'use strict';
+
+// Requires:
+// - /common/dispatcher/dispatcher.js
+// - /common/utils.js
+// - /common/get-host-info.sub.js if automagic conversion of origin names to
+// URLs is used.
+
+/**
+ * This provides a more friendly interface to remote contexts in dispatches.js.
+ * The goal is to make it easy to write multi-window/-frame/-worker tests where
+ * the logic is entirely in 1 test file and there is no need to check in any
+ * other file (although it is often helpful to check in files of JS helper
+ * functions that are shared across remote context).
+ *
+ * So for example, to test that history traversal works, we create a new window,
+ * navigate it to a new document, go back and then go forward.
+ *
+ * @example
+ * promise_test(async t => {
+ * const rcHelper = new RemoteContextHelper();
+ * const rc1 = await rcHelper.addWindow();
+ * const rc2 = await rc1.navigateToNew();
+ * assert_equals(await rc2.executeScript(() => 'here'), 'here', 'rc2 is live');
+ * rc2.historyBack();
+ * assert_equals(await rc1.executeScript(() => 'here'), 'here', 'rc1 is live');
+ * rc1.historyForward();
+ * assert_equals(await rc2.executeScript(() => 'here'), 'here', 'rc2 is live');
+ * });
+ *
+ * Note on the correspondence between remote contexts and
+ * `RemoteContextWrapper`s. A remote context is entirely determined by its URL.
+ * So navigating away from one and then back again will result in a remote
+ * context that can be controlled by the same `RemoteContextWrapper` instance
+ * before and after navigation. Messages sent to a remote context while it is
+ * destroyed or in BFCache will be queued and processed if that that URL is
+ * navigated back to.
+ *
+ * Navigation:
+ * This framework does not keep track of the history of the frame tree and so it
+ * is up to the test script to keep track of what remote contexts are currently
+ * active and to keep references to the corresponding `RemoteContextWrapper`s.
+ *
+ * Any action that leads to navigation in the remote context must be executed
+ * using
+ * @see RemoteContextWrapper.navigate.
+ */
+
+{
+ const RESOURCES_PATH =
+ '/html/browsers/browsing-the-web/remote-context-helper/resources';
+ const WINDOW_EXECUTOR_PATH = `${RESOURCES_PATH}/executor-window.py`;
+ const WORKER_EXECUTOR_PATH = `${RESOURCES_PATH}/executor-worker.js`;
+
+ /**
+ * Turns a string into an origin. If `origin` is null this will return the
+ * current document's origin. If `origin` contains not '/', this will attempt
+ * to use it as an index in `get_host_info()`. Otherwise returns the input
+ * origin.
+ * @private
+ * @param {string|null} origin The input origin.
+ * @return {string|null} The output origin.
+ * @throws {RangeError} is `origin` cannot be found in
+ * `get_host_info()`.
+ */
+ function finalizeOrigin(origin) {
+ if (!origin) {
+ return location.origin;
+ }
+ if (!origin.includes('/')) {
+ const origins = get_host_info();
+ if (origin in origins) {
+ return origins[origin];
+ } else {
+ throw new RangeError(
+ `${origin} is not a key in the get_host_info() object`);
+ }
+ }
+ return origin;
+ }
+
+ /**
+ * @private
+ * @param {string} url
+ * @returns {string} Absolute url using `location` as the base.
+ */
+ function makeAbsolute(url) {
+ return new URL(url, location).toString();
+ }
+
+ /**
+ * Represents a configuration for a remote context executor.
+ */
+ class RemoteContextConfig {
+ /**
+ * @param {Object} [options]
+ * @param {string} [options.origin] A URL or a key in `get_host_info()`.
+ * @see finalizeOrigin for how origins are handled.
+ * @param {string[]} [options.scripts] A list of script URLs. The current
+ * document will be used as the base for relative URLs.
+ * @param {[string, string][]} [options.headers] A list of pairs of name
+ * and value. The executor will be served with these headers set.
+ * @param {string} [options.startOn] If supplied, the executor will start
+ * when this event occurs, e.g. "pageshow",
+ * (@see window.addEventListener). This only makes sense for
+ * window-based executors, not worker-based.
+ * @param {string} [options.status] If supplied, the executor will pass
+ * this value in the "status" parameter to the executor. The default
+ * executor will default to a status code of 200, if the parameter is
+ * not supplied.
+ */
+ constructor(
+ {origin, scripts = [], headers = [], startOn, status} = {}) {
+ this.origin = origin;
+ this.scripts = scripts;
+ this.headers = headers;
+ this.startOn = startOn;
+ this.status = status;
+ }
+
+ /**
+ * If `config` is not already a `RemoteContextConfig`, one is constructed
+ * using `config`.
+ * @private
+ * @param {object} [config]
+ * @returns
+ */
+ static ensure(config) {
+ if (!config) {
+ return DEFAULT_CONTEXT_CONFIG;
+ }
+ return new RemoteContextConfig(config);
+ }
+
+ /**
+ * Merges `this` with another `RemoteContextConfig` and to give a new
+ * `RemoteContextConfig`. `origin` is replaced by the other if present,
+ * `headers` and `scripts` are concatenated with `this`'s coming first.
+ * @param {RemoteContextConfig} extraConfig
+ * @returns {RemoteContextConfig}
+ */
+ merged(extraConfig) {
+ let origin = this.origin;
+ if (extraConfig.origin) {
+ origin = extraConfig.origin;
+ }
+ let startOn = this.startOn;
+ if (extraConfig.startOn) {
+ startOn = extraConfig.startOn;
+ }
+ let status = this.status;
+ if (extraConfig.status) {
+ status = extraConfig.status;
+ }
+ const headers = this.headers.concat(extraConfig.headers);
+ const scripts = this.scripts.concat(extraConfig.scripts);
+ return new RemoteContextConfig({
+ origin,
+ headers,
+ scripts,
+ startOn,
+ status
+ });
+ }
+ }
+
+ /**
+ * The default `RemoteContextConfig` to use if none is supplied. It has no
+ * origin, headers or scripts.
+ * @constant {RemoteContextConfig}
+ */
+ const DEFAULT_CONTEXT_CONFIG = new RemoteContextConfig();
+
+ /**
+ * This class represents a configuration for creating remote contexts. This is
+ * the entry-point
+ * for creating remote contexts, providing @see addWindow .
+ */
+ class RemoteContextHelper {
+ /**
+ * @param {RemoteContextConfig|object} config The configuration
+ * for this remote context.
+ */
+ constructor(config) {
+ this.config = RemoteContextConfig.ensure(config);
+ }
+
+ /**
+ * Creates a new remote context and returns a `RemoteContextWrapper` giving
+ * access to it.
+ * @private
+ * @param {Object} options
+ * @param {(url: string) => Promise<undefined>} [options.executorCreator] A
+ * function that takes a URL and causes the browser to navigate some
+ * window to that URL, e.g. via an iframe or a new window. If this is
+ * not supplied, then the returned RemoteContextWrapper won't actually
+ * be communicating with something yet, and something will need to
+ * navigate to it using its `url` property, before communication can be
+ * established.
+ * @param {RemoteContextConfig|object} [options.extraConfig] If supplied,
+ * extra configuration for this remote context to be merged with
+ * `this`'s existing config. If it's not a `RemoteContextConfig`, it
+ * will be used to construct a new one.
+ * @returns {Promise<RemoteContextWrapper>}
+ */
+ async createContext({
+ executorCreator,
+ extraConfig,
+ isWorker = false,
+ }) {
+ const config =
+ this.config.merged(RemoteContextConfig.ensure(extraConfig));
+
+ const origin = finalizeOrigin(config.origin);
+ const url = new URL(
+ isWorker ? WORKER_EXECUTOR_PATH : WINDOW_EXECUTOR_PATH, origin);
+
+ // UUID is needed for executor.
+ const uuid = token();
+ url.searchParams.append('uuid', uuid);
+
+ if (config.headers) {
+ addHeaders(url, config.headers);
+ }
+ for (const script of config.scripts) {
+ url.searchParams.append('script', makeAbsolute(script));
+ }
+
+ if (config.startOn) {
+ url.searchParams.append('startOn', config.startOn);
+ }
+
+ if (config.status) {
+ url.searchParams.append('status', config.status);
+ }
+
+ if (executorCreator) {
+ await executorCreator(url.href);
+ }
+
+ return new RemoteContextWrapper(new RemoteContext(uuid), this, url.href);
+ }
+
+ /**
+ * Creates a window with a remote context. @see createContext for
+ * @param {RemoteContextConfig|object} [extraConfig] Will be
+ * merged with `this`'s config.
+ * @param {Object} [options]
+ * @param {string} [options.target] Passed to `window.open` as the
+ * 2nd argument
+ * @param {string} [options.features] Passed to `window.open` as the
+ * 3rd argument
+ * @returns {Promise<RemoteContextWrapper>}
+ */
+ addWindow(extraConfig, options) {
+ return this.createContext({
+ executorCreator: windowExecutorCreator(options),
+ extraConfig,
+ });
+ }
+ }
+ // Export this class.
+ self.RemoteContextHelper = RemoteContextHelper;
+
+ /**
+ * Attaches header to the URL. See
+ * https://web-platform-tests.org/writing-tests/server-pipes.html#headers
+ * @param {string} url the URL to which headers should be attached.
+ * @param {[[string, string]]} headers a list of pairs of head-name,
+ * header-value.
+ */
+ function addHeaders(url, headers) {
+ function escape(s) {
+ return s.replace('(', '\\(').replace(')', '\\)');
+ }
+ const formattedHeaders = headers.map((header) => {
+ return `header(${escape(header[0])}, ${escape(header[1])})`;
+ });
+ url.searchParams.append('pipe', formattedHeaders.join('|'));
+ }
+
+ function windowExecutorCreator({target = '_blank', features} = {}) {
+ return url => {
+ window.open(url, target, features);
+ };
+ }
+
+ function elementExecutorCreator(
+ remoteContextWrapper, elementName, attributes) {
+ return url => {
+ return remoteContextWrapper.executeScript((url, elementName, attributes) => {
+ const el = document.createElement(elementName);
+ for (const attribute in attributes) {
+ el.setAttribute(attribute, attributes[attribute]);
+ }
+ el.src = url;
+ document.body.appendChild(el);
+ }, [url, elementName, attributes]);
+ };
+ }
+
+ function iframeSrcdocExecutorCreator(remoteContextWrapper, attributes) {
+ return async (url) => {
+ // `url` points to the content needed to run an `Executor` in the frame.
+ // So we download the content and pass it via the `srcdoc` attribute,
+ // setting the iframe's `src` to `undefined`.
+ attributes['srcdoc'] = await fetch(url).then(r => r.text());
+ elementExecutorCreator(
+ remoteContextWrapper, 'iframe', attributes)(undefined);
+ };
+ }
+
+ function workerExecutorCreator() {
+ return url => {
+ new Worker(url);
+ };
+ }
+
+ function navigateExecutorCreator(remoteContextWrapper) {
+ return url => {
+ return remoteContextWrapper.navigate((url) => {
+ window.location = url;
+ }, [url]);
+ };
+ }
+
+ /**
+ * This class represents a remote context running an executor (a
+ * window/frame/worker that can receive commands). It is the interface for
+ * scripts to control remote contexts.
+ *
+ * Instances are returned when new remote contexts are created (e.g.
+ * `addFrame` or `navigateToNew`).
+ */
+ class RemoteContextWrapper {
+ /**
+ * This should only be constructed by `RemoteContextHelper`.
+ * @private
+ */
+ constructor(context, helper, url) {
+ this.context = context;
+ this.helper = helper;
+ this.url = url;
+ }
+
+ /**
+ * Executes a script in the remote context.
+ * @param {function} fn The script to execute.
+ * @param {any[]} args An array of arguments to pass to the script.
+ * @returns {Promise<any>} The return value of the script (after
+ * being serialized and deserialized).
+ */
+ async executeScript(fn, args) {
+ return this.context.execute_script(fn, args);
+ }
+
+ /**
+ * Adds a string of HTML to the executor's document.
+ * @param {string} html
+ * @returns {Promise<undefined>}
+ */
+ async addHTML(html) {
+ return this.executeScript((htmlSource) => {
+ document.body.insertAdjacentHTML('beforebegin', htmlSource);
+ }, [html]);
+ }
+
+ /**
+ * Adds scripts to the executor's document.
+ * @param {string[]} urls A list of URLs. URLs are relative to the current
+ * document.
+ * @returns {Promise<undefined>}
+ */
+ async addScripts(urls) {
+ if (!urls) {
+ return [];
+ }
+ return this.executeScript(urls => {
+ return addScripts(urls);
+ }, [urls.map(makeAbsolute)]);
+ }
+
+ /**
+ * Adds an iframe with `src` attribute to the current document.
+ * @param {RemoteContextConfig} [extraConfig]
+ * @param {[string, string][]} [attributes] A list of pairs of strings
+ * of attribute name and value these will be set on the iframe element
+ * when added to the document.
+ * @returns {Promise<RemoteContextWrapper>} The remote context.
+ */
+ addIframe(extraConfig, attributes = {}) {
+ return this.helper.createContext({
+ executorCreator: elementExecutorCreator(this, 'iframe', attributes),
+ extraConfig,
+ });
+ }
+
+ /**
+ * Adds an iframe with `srcdoc` attribute to the current document
+ * @param {RemoteContextConfig} [extraConfig]
+ * @param {[string, string][]} [attributes] A list of pairs of strings
+ * of attribute name and value these will be set on the iframe element
+ * when added to the document.
+ * @returns {Promise<RemoteContextWrapper>} The remote context.
+ */
+ addIframeSrcdoc(extraConfig, attributes = {}) {
+ return this.helper.createContext({
+ executorCreator: iframeSrcdocExecutorCreator(this, attributes),
+ extraConfig,
+ });
+ }
+
+ /**
+ * Adds a dedicated worker to the current document.
+ * @param {RemoteContextConfig} [extraConfig]
+ * @returns {Promise<RemoteContextWrapper>} The remote context.
+ */
+ addWorker(extraConfig) {
+ return this.helper.createContext({
+ executorCreator: workerExecutorCreator(),
+ extraConfig,
+ isWorker: true,
+ });
+ }
+
+ /**
+ * Gets a `Headers` object containing the request headers that were used
+ * when the browser requested this document.
+ *
+ * Currently, this only works for `RemoteContextHelper`s representing
+ * windows, not workers.
+ * @returns {Promise<Headers>}
+ */
+ async getRequestHeaders() {
+ // This only works in window environments for now. We could make it work
+ // for workers too; if you have a need, just share or duplicate the code
+ // that's in executor-window.py. Anyway, we explicitly use `window` in
+ // the script so that we get a clear error if you try using it on a
+ // worker.
+
+ // We need to serialize and deserialize the `Headers` object manually.
+ const asNestedArrays = await this.executeScript(() => [...window.__requestHeaders]);
+ return new Headers(asNestedArrays);
+ }
+
+ /**
+ * Executes a script in the remote context that will perform a navigation.
+ * To do this safely, we must suspend the executor and wait for that to
+ * complete before executing. This ensures that all outstanding requests are
+ * completed and no more can start. It also ensures that the executor will
+ * restart if the page goes into BFCache or it was a same-document
+ * navigation. It does not return a value.
+ *
+ * NOTE: We cannot monitor whether and what navigations are happening. The
+ * logic has been made as robust as possible but is not fool-proof.
+ *
+ * Foolproof rule:
+ * - The script must perform exactly one navigation.
+ * - If that navigation is a same-document history traversal, you must
+ * `await` the result of `waitUntilLocationIs`. (Same-document non-traversal
+ * navigations do not need this extra step.)
+ *
+ * More complex rules:
+ * - The script must perform a navigation. If it performs no navigation,
+ * the remote context will be left in the suspended state.
+ * - If the script performs a direct same-document navigation, it is not
+ * necessary to use this function but it will work as long as it is the only
+ * navigation performed.
+ * - If the script performs a same-document history navigation, you must
+ * `await` the result of `waitUntilLocationIs`.
+ *
+ * @param {function} fn The script to execute.
+ * @param {any[]} args An array of arguments to pass to the script.
+ * @returns {Promise<undefined>}
+ */
+ navigate(fn, args) {
+ return this.executeScript((fnText, args) => {
+ executeScriptToNavigate(fnText, args);
+ }, [fn.toString(), args]);
+ }
+
+ /**
+ * Navigates to the given URL, by executing a script in the remote
+ * context that will perform navigation with the `location.href`
+ * setter.
+ *
+ * Be aware that performing a cross-document navigation using this
+ * method will cause this `RemoteContextWrapper` to become dormant,
+ * since the remote context it points to is no longer active and
+ * able to receive messages. You also won't be able to reliably
+ * tell when the navigation finishes; the returned promise will
+ * fulfill when the script finishes running, not when the navigation
+ * is done. As such, this is most useful for testing things like
+ * unload behavior (where it doesn't matter) or prerendering (where
+ * there is already a `RemoteContextWrapper` for the destination).
+ * For other cases, using `navigateToNew()` will likely be better.
+ *
+ * @param {string|URL} url The URL to navigate to.
+ * @returns {Promise<undefined>}
+ */
+ navigateTo(url) {
+ return this.navigate(url => {
+ location.href = url;
+ }, [url.toString()]);
+ }
+
+ /**
+ * Navigates the context to a new document running an executor.
+ * @param {RemoteContextConfig} [extraConfig]
+ * @returns {Promise<RemoteContextWrapper>} The remote context.
+ */
+ async navigateToNew(extraConfig) {
+ return this.helper.createContext({
+ executorCreator: navigateExecutorCreator(this),
+ extraConfig,
+ });
+ }
+
+ //////////////////////////////////////
+ // Navigation Helpers.
+ //
+ // It is up to the test script to know which remote context will be
+ // navigated to and which `RemoteContextWrapper` should be used after
+ // navigation.
+ //
+ // NOTE: For a same-document history navigation, the caller use `await` a
+ // call to `waitUntilLocationIs` in order to know that the navigation has
+ // completed. For convenience the method below can return the promise to
+ // wait on, if passed the expected location.
+
+ async waitUntilLocationIs(expectedLocation) {
+ return this.executeScript(async (expectedLocation) => {
+ if (location.href === expectedLocation) {
+ return;
+ }
+
+ // Wait until the location updates to the expected one.
+ await new Promise(resolve => {
+ const listener = addEventListener('hashchange', (event) => {
+ if (event.newURL === expectedLocation) {
+ removeEventListener(listener);
+ resolve();
+ }
+ });
+ });
+ }, [expectedLocation]);
+ }
+
+ /**
+ * Performs a history traversal.
+ * @param {integer} n How many steps to traverse. @see history.go
+ * @param {string} [expectedLocation] If supplied will be passed to @see waitUntilLocationIs.
+ * @returns {Promise<undefined>}
+ */
+ async historyGo(n, expectedLocation) {
+ await this.navigate((n) => {
+ history.go(n);
+ }, [n]);
+ if (expectedLocation) {
+ await this.waitUntilLocationIs(expectedLocation);
+ }
+ }
+
+ /**
+ * Performs a history traversal back.
+ * @param {string} [expectedLocation] If supplied will be passed to @see waitUntilLocationIs.
+ * @returns {Promise<undefined>}
+ */
+ async historyBack(expectedLocation) {
+ await this.navigate(() => {
+ history.back();
+ });
+ if (expectedLocation) {
+ await this.waitUntilLocationIs(expectedLocation);
+ }
+ }
+
+ /**
+ * Performs a history traversal back.
+ * @param {string} [expectedLocation] If supplied will be passed to @see waitUntilLocationIs.
+ * @returns {Promise<undefined>}
+ */
+ async historyForward(expectedLocation) {
+ await this.navigate(() => {
+ history.forward();
+ });
+ if (expectedLocation) {
+ await this.waitUntilLocationIs(expectedLocation);
+ }
+ }
+ }
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/resources/has-iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/resources/has-iframe.html
new file mode 100644
index 0000000000..e6dfba1011
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/resources/has-iframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<iframe src="/common/blank.html"></iframe>
+<script>
+window.onload = () => {
+ window.opener.postMessage("top ready", "*");
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/resources/helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/resources/helpers.js
new file mode 100644
index 0000000000..b19addfadf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/resources/helpers.js
@@ -0,0 +1,84 @@
+// This file contains general helpers for navigation/history tests. The goal is
+// to make tests more imperative and ordered, instead of requiring lots of
+// nested callbacks and jumping back and forth. However,
+// html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// might be even better at that, so prefer that when you can.
+//
+// TODO(domenic): consider unifying with
+// overlapping-navigations-and-traversals/resources/helpers.mjs.
+
+window.openWindow = (url, t) => {
+ const w = window.open(url);
+ t?.add_cleanup(() => w.close());
+
+ return new Promise(resolve => {
+ w.addEventListener("load", () => resolve(w), { once: true });
+ });
+};
+
+window.addIframe = (url = "/common/blank.html", doc = document) => {
+ const iframe = doc.createElement("iframe");
+ iframe.src = url;
+ doc.body.append(iframe);
+
+ return new Promise(resolve => {
+ iframe.addEventListener("load", () => resolve(iframe), { once: true });
+ });
+};
+
+window.addSrcdocIframe = async () => {
+ const iframe = document.createElement("iframe");
+ iframe.srcdoc = `<script>window.parent.postMessage("srcdoc ready", "*")</scr` + `ipt>`;
+ document.body.append(iframe);
+
+ assert_equals(await waitForMessage(iframe.contentWindow), "srcdoc ready");
+
+ return iframe;
+};
+
+window.waitToAvoidReplace = t => {
+ return new Promise(resolve => t.step_timeout(resolve, 0));
+};
+
+window.waitForIframeLoad = iframe => {
+ return new Promise(resolve => {
+ iframe.addEventListener("load", () => resolve(), { once: true });
+ });
+};
+
+window.waitForMessage = expectedSource => {
+ return new Promise(resolve => {
+ window.addEventListener("message", ({ source, data }) => {
+ if (source === expectedSource) {
+ resolve(data);
+ }
+ });
+ });
+};
+
+window.waitForHashchange = w => {
+ return new Promise(resolve => {
+ w.addEventListener("hashchange", () => resolve(), { once: true });
+ });
+};
+
+window.srcdocThatPostsParentOpener = text => {
+ return `
+ <p>${text}</p>
+ <script>
+ window.onload = () => {
+ window.top.opener.postMessage('ready', '*');
+ };
+ <\/script>
+ `;
+};
+
+window.failOnMessage = expectedSource => {
+ return new Promise((_, reject) => {
+ window.addEventListener("message", ({ source, data }) => {
+ if (source === expectedSource) {
+ reject(new Error(`Received message "${data}" but expected to receive no message`));
+ }
+ });
+ });
+};
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/resources/post-top-opener-on-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/resources/post-top-opener-on-load.html
new file mode 100644
index 0000000000..47cb6c6150
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/resources/post-top-opener-on-load.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+window.onload = () => {
+ window.top.opener.postMessage("ready", "*");
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/001.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/001.html
new file mode 100644
index 0000000000..32599bbc50
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/001.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<!-- this tests the spec as it hopefully will be once bug https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 is fixed -->
+<title>Fragment Navigation: Updating document address</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(location.hash, "", "Page must be loaded with no hash")
+ var original_location = location.href;
+ location.hash = "test";
+ assert_equals(location.hash, "#test");
+ assert_equals(location.href, original_location + "#test");
+ location.hash = ""
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/002.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/002.html
new file mode 100644
index 0000000000..92bfd63415
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/002.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<!-- this tests the spec as it hopefully will be once bug https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 is fixed -->
+<title>Fragment Navigation: Updating document address twice</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(location.hash, "", "Page must be loaded with no hash")
+ var original_location = location.href;
+ location.hash = "test";
+ assert_equals(location.hash, "#test");
+ assert_equals(location.href, original_location + "#test");
+
+ location.hash = "test1";
+ assert_equals(location.hash, "#test1");
+ assert_equals(location.href, original_location + "#test1");
+
+ location.hash = "";
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/003.html
new file mode 100644
index 0000000000..86d85b9ca6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/003.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- this tests the spec as it hopefully will be once bug https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 is fixed -->
+<title>Fragment Navigation: Updating scroll position</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="log"></div>
+<div id="test">scroll 1</div>
+<div style="height:10000px">Filler</div>
+<div id="test1">scroll 2</div>
+<script>
+test(function() {
+ assert_equals(document.scrollingElement.scrollTop, 0);
+ location.hash = "test";
+
+ var scroll1 = document.scrollingElement.scrollTop;
+ assert_true(scroll1 > 0);
+
+ location.hash = "test1";
+ var scroll2 = document.scrollingElement.scrollTop;
+ assert_true(scroll2 > scroll1);
+
+ location.hash = ""
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/004.html
new file mode 100644
index 0000000000..c365c6fd29
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/004.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<!-- this tests the spec as it hopefully will be once bug https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 is fixed -->
+<title>Fragment Navigation: hashchange event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="log"></div>
+<script>
+var t = async_test();
+t.step(function() {
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ location.hash = "test";
+
+ addEventListener("hashchange",
+ t.step_func(function(e) {
+ assert_equals(e.target, window);
+ assert_equals(e.type, "hashchange");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ t.done();
+ }), true)
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/005.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/005.html
new file mode 100644
index 0000000000..f0761a64f6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/005.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<!-- this tests the spec as it hopefully will be once bug https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 is fixed -->
+<title>Fragment Navigation: hashchange event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="log"></div>
+<script>
+var t = async_test();
+t.step(function() {
+ var original_url = location.href;
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ location.hash = "test";
+
+ addEventListener("hashchange",
+ t.step_func(function(e) {
+ assert_equals(e.oldURL, original_url, "oldURL property");
+ assert_equals(e.newURL, location.href, "newURL property");
+ location.hash = "";
+ t.done();
+ }), true);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/006.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/006.html
new file mode 100644
index 0000000000..a65b9eb4a2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/006.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<!-- this tests the spec as it hopefully will be once bug https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 is fixed -->
+<title>Fragment Navigation: hashchange event multiple changes old/newURL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="log"></div>
+<script>
+var t = async_test();
+t.step(function() {
+ var original_url = location.href;
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ location.hash = "test";
+
+ var count = 0;
+ var mid_url = location.href;
+
+ addEventListener("hashchange",
+ t.step_func(function(e) {
+ if (count === 0) {
+ assert_equals(e.oldURL, original_url, "oldURL property first update");
+ assert_equals(e.newURL, mid_url, "newURL property first update");
+ count = 1;
+ } else if (count === 1) {
+ assert_equals(e.oldURL, mid_url, "oldURL property second update");
+ assert_equals(e.newURL, location.href, "newURL property second update");
+ location.hash = "";
+ t.done();
+ }
+ }), true);
+
+ location.hash = "test1";
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/007.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/007.html
new file mode 100644
index 0000000000..0b6fe813ba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/007.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<!-- this tests the spec as it hopefully will be once bug https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 is fixed -->
+<title>Fragment Navigation: hashchange event multiple changes old/newURL</title>
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="log"></div>
+<script>
+var t = async_test();
+t.step(function() {
+ var original_url = location.href;
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+
+ var count = 0;
+
+ location.hash = "test";
+
+ hashes = [];
+
+ addEventListener("hashchange",
+ t.step_func(function(e) {
+ if (count < 100) {
+ location.hash = "test" + count++;
+ hashes.push(location.hash);
+ } else if (count === 100) {
+ expected = [];
+ for (var i=0; i<100; i++) {
+ expected.push("#test" + i);
+ }
+ assert_array_equals(hashes, expected);
+ location.hash = "";
+ t.done();
+ }
+ }), true);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/forward-triggers-hashchange.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/forward-triggers-hashchange.html
new file mode 100644
index 0000000000..45f7e38ba3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/forward-triggers-hashchange.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigating forward after replace() should still trigger hashchange</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#history-traversal">
+<link rel="author" href="mailto:d@domenic.me" title="Domenic Denicola">
+
+<!-- While writing ./replacement-enabled.html, a bug was discovered in Firefox where it does not
+fire hashchange when using history.forward(), at least under certain conditions. So, this test
+exercises that specifically. -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="navigate-helpers.js"></script>
+
+<body>
+
+<script>
+"use strict";
+let resolve, iframe;
+promise_test(() => {
+ iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ iframe.addEventListener("load", runTest);
+ document.body.appendChild(iframe);
+
+ return new Promise(r => resolve = r);
+});
+
+function runTest() {
+ iframe.removeEventListener("load", runTest);
+ const frameWindow = iframe.contentWindow;
+
+ resolve((async () => {
+ await navigateAndWaitForChange(frameWindow, w => w.location.href = "/common/blank.html#apple");
+ await navigateAndWaitForChange(frameWindow, w => w.location.href = "/common/blank.html#banana");
+ await navigateAndWaitForChange(frameWindow, w => w.location.href = "/common/blank.html#cat");
+
+ await navigateAndWaitForChange(frameWindow, w => w.history.back());
+ await navigateAndWaitForChange(frameWindow,
+ w => w.location.replace("/common/blank.html#zebra"));
+ await navigateAndWaitForChange(frameWindow, w => w.history.forward());
+ })());
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-2.html
new file mode 100644
index 0000000000..cd2502ab1c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-2.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<meta charset=windows-1252>
+<title>Fragment navigation: encoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div style=height:10000px></div>
+<div id=&#xFFFD;></div>
+<div id=&#xFEFF;&#xFFFD;></div>
+<script>
+function goToTop() {
+ location.hash = "top";
+ assert_equals(self.scrollY, 0, "#top");
+}
+
+test(() => {
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+
+ location.hash = "%C2";
+ assert_equals(location.hash, "#%C2");
+ assert_greater_than(self.scrollY, 1000, "#%C2");
+}, "Invalid percent-encoded UTF-8 byte should decode as U+FFFD");
+
+test(() => {
+ goToTop();
+
+ location.hash = "%EF%BB%BF%C2";
+ assert_equals(location.hash, "#%EF%BB%BF%C2");
+ assert_greater_than(self.scrollY, 1000, "#%EF%BB%BF%C2");
+}, "Percent-encoded UTF-8 BOM followed by invalid UTF-8 byte should decode as U+FEFF U+FFFD");
+
+test(() => {
+ goToTop();
+
+ location.hash = "%EF%BF%BD";
+ assert_equals(location.hash, "#%EF%BF%BD");
+ assert_greater_than(self.scrollY, 1000, "#%EF%BF%BD");
+
+ goToTop();
+}, "Percent-encoded UTF-8 byte sequence for U+FFFD should decode as U+FFFD");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html
new file mode 100644
index 0000000000..21fbd4618d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<meta charset=windows-1252>
+<title>Fragment navigation: encoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div style=height:10000px></div>
+<div id=&#xFF;></div>
+<div id=&#xFEFF;></div>
+<div id=&#x2661;&#x00FF;><div>
+<script>
+function goToTop() {
+ location.hash = "top";
+ assert_equals(self.scrollY, 0, "#top");
+}
+
+test(() => {
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+
+ location.hash = "\u00FF";
+ assert_equals(location.hash, "#%C3%BF");
+ assert_greater_than(self.scrollY, 1000, "#%C3%BF");
+}, "U+00FF should find U+00FF");
+
+test(() => {
+ goToTop();
+
+ location.hash = "%EF%BB%BF";
+ assert_greater_than(self.scrollY, 1000, "#%EF%BB%BF");
+}, "Percent-encoded UTF-8 BOM should find U+FEFF as BOM is not stripped when decoding");
+
+test(() => {
+ goToTop();
+
+ location.hash = "%FF";
+ assert_equals(self.scrollY, 0, "#%FF");
+}, "%FF should not find U+00FF as decoding it gives U+FFFD");
+
+test(() => {
+ goToTop();
+
+ // U+2661 in UTF-8 + %FF.
+ // Chrome had an issue that the following fragment was decoded as U+2661 U+00FF.
+ // https://github.com/whatwg/html/pull/3111
+ location.hash = "%E2%99%A1%FF";
+ assert_equals(self.scrollY, 0, "%E2%99%A1%FF");
+
+ goToTop();
+}, "Valid UTF-8 + invalid UTF-8 should not be matched to the utf8-decoded former + the isomorphic-decoded latter");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/navigate-helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/navigate-helpers.js
new file mode 100644
index 0000000000..7a9adeb3d2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/navigate-helpers.js
@@ -0,0 +1,23 @@
+"use strict";
+
+// Usage examples:
+// navigateAndWaitForChange(frameWindow, w => w.location.href = "...");
+// navigateAndWaitForChange(frameWindow, w => w.history.back());
+// navigateAndWaitForChange(frameWindow, w => w.history.back(), { assumeSuccessAfter: 100 });
+
+window.navigateAndWaitForChange = (w, navigationAction, { assumeSuccessAfter } = {}) => {
+ return new Promise(resolve => {
+ w.addEventListener("hashchange", listener);
+
+ function listener() {
+ w.removeEventListener("hashchange", listener);
+ resolve();
+ }
+
+ if (assumeSuccessAfter !== undefined) {
+ step_timeout(resolve, assumeSuccessAfter);
+ }
+
+ navigationAction(w);
+ });
+};
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/replacement-enabled.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/replacement-enabled.html
new file mode 100644
index 0000000000..b22fbed80f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/replacement-enabled.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigating to a fragment should not clear forward history</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#scroll-to-fragid">
+<link rel="help" href="https://github.com/whatwg/html/issues/2796">
+<link rel="help" href="https://github.com/whatwg/html/pull/2869">
+<link rel="author" href="mailto:d@domenic.me" title="Domenic Denicola">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="navigate-helpers.js"></script>
+
+<body>
+
+<script>
+"use strict";
+let resolve, iframe;
+promise_test(() => {
+ iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ iframe.addEventListener("load", runTest);
+ document.body.appendChild(iframe);
+
+ return new Promise(r => resolve = r);
+});
+
+function runTest() {
+ iframe.removeEventListener("load", runTest);
+ const frameWindow = iframe.contentWindow;
+
+ resolve((async () => {
+ await navigateAndWaitForChange(frameWindow, w => w.location.href = "/common/blank.html#apple");
+ await navigateAndWaitForChange(frameWindow, w => w.location.href = "/common/blank.html#banana");
+ await navigateAndWaitForChange(frameWindow, w => w.location.href = "/common/blank.html#cat");
+
+ assert_equals(frameWindow.location.hash, "#cat");
+
+ // Might not be 4 (= 3 for iframe + 1 initial) due to cross-browser differences or if people are
+ // running this test in a window that has previously been places. The important thing for this
+ // test is the delta from this value.
+ const afterThreeNavigations = frameWindow.history.length;
+
+ await navigateAndWaitForChange(frameWindow, w => w.history.back());
+
+ assert_equals(frameWindow.location.hash, "#banana");
+ assert_equals(frameWindow.history.length, afterThreeNavigations,
+ "back() must not change the history length");
+
+ await navigateAndWaitForChange(frameWindow,
+ w => w.location.replace("/common/blank.html#zebra"));
+
+ assert_equals(frameWindow.location.hash, "#zebra");
+ assert_equals(frameWindow.history.length, afterThreeNavigations,
+ "replace() must not change the history length");
+
+ // As of the time of this writing (2017-08-14), Firefox is not firing hashchange on forward, so
+ // we automatically assume navigation succeeded after 100 ms. A sibling test will test this
+ // particular Firefox bug.
+ await navigateAndWaitForChange(frameWindow, w => w.history.forward(),
+ { assumeSuccessAfter: 500 });
+
+ assert_equals(frameWindow.location.hash, "#cat");
+ assert_equals(frameWindow.history.length, afterThreeNavigations,
+ "forward() must not change the history length");
+
+ })());
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-non-utf8-encoded-document.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-non-utf8-encoded-document.html
new file mode 100644
index 0000000000..7d4e994f0a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-non-utf8-encoded-document.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Fragment Navigation: fragment id should not be found in non UTF8 document</title>
+<meta name=timeout content=long>
+<meta http-equiv="Content-Type" content="text/html; charset=gbk"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div></div>
+<div id="&#x586f" style="position:absolute; top:100px;"></div>
+<div style="height:200vh;"></div>
+<script>
+async_test(test => {
+ assert_equals(document.characterSet, "GBK", "Document should be GBK encoded");
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ location.hash = '%89g';
+ test.step_timeout(() => {
+ assert_equals( document.scrollingElement.scrollTop, 0 );
+ test.done();
+ }, 1);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-percent-encoded.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-percent-encoded.html
new file mode 100644
index 0000000000..aa179425c5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-percent-encoded.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<title>Fragment Navigation: fragment id should be percent-decoded</title>
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div></div>
+<div id="has two spaces" style="position:absolute; top:100px;"></div>
+<div id="escape%20collision" style="position:absolute; top:200px;"></div>
+<div id="%20has%20two%20spaces" style="position:absolute; top:300px;"></div>
+<div id="escape collision" style="position:absolute; top:400px;"></div>
+<div id="do%20not%20go%20here" style="position:absolute; top:400px;"></div>
+<div style="height:200em;"></div>
+<script>
+var steps = [{
+ fragid:'has%20two%20spaces',
+ handler: function(){
+ assert_equals( document.scrollingElement.scrollTop, 100 );
+ }
+ },{
+ fragid:'escape%20collision',
+ handler: function(){
+ assert_equals( document.scrollingElement.scrollTop, 200 );
+ document.getElementById("%20has%20two%20spaces").setAttribute("id", "has%20two%20spaces");
+ }
+ },{
+ fragid:'has%20two%20spaces',
+ handler: function(){
+ assert_equals( document.scrollingElement.scrollTop, 300 );
+ }
+ },{
+ fragid:'do%20not%20go%20here',
+ handler: function(){
+ // don't move
+ assert_equals( document.scrollingElement.scrollTop, 400 );
+ }
+ }];
+
+function runNextStep(){
+ if( steps.length > 0 ) {
+ var step = steps.shift();
+ var listener = t.step_func( function(){
+ step.handler();
+ runNextStep();
+ });
+ scrollToFragmentThenDo( step.fragid, listener );
+ } else {
+ t.done();
+ }
+}
+
+function scrollToFragmentThenDo( fragid, then ){
+ location.hash = fragid;
+ setTimeout( then, 1 );
+}
+
+var t = async_test();
+t.step( function(){
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ runNextStep();
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-lr.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-lr.html
new file mode 100644
index 0000000000..57d99440e1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-lr.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html style="writing-mode: vertical-lr;">
+<head>
+<meta charset="UTF-8">
+<title>Fragment Navigation: Scroll to block start position in vertical-lr writing mode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="test" style="position: absolute; top: 5px; left: 7px; margin: 5px 7px; border-style: solid; border-width: 5px 7px; padding: 5px 7px; height: 5px; width: 7px;"></div>
+<div style="width: 200vw;"></div>
+<script>
+async_test(function (t) {
+ on_event(window, 'load', function () {
+ t.step(function () {
+ window.scrollTo(0, 0);
+ location.hash = 'test';
+ assert_equals(window.scrollX, 14, 'Scroll to the left border edge of #test');
+ });
+ t.done();
+ });
+}, '');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-rl.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-rl.html
new file mode 100644
index 0000000000..60a902199a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position-vertical-rl.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html style="writing-mode: vertical-rl;">
+<head>
+<meta charset="UTF-8">
+<title>Fragment Navigation: Scroll to block start position in vertical-rl writing mode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="test" style="position: absolute; top: 5px; right: 7px; margin: 5px 7px; border-style: solid; border-width: 5px 7px; padding: 5px 7px; height: 5px; width: 7px;"></div>
+<div style="width: 200vw;"></div>
+<script>
+async_test(function (t) {
+ on_event(window, 'load', function () {
+ t.step(function () {
+ window.scrollTo(0, 0);
+ location.hash = 'test';
+ assert_equals(window.scrollX, -14, 'Scroll to the right border edge of #test');
+ });
+ t.done();
+ });
+}, '');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position.html
new file mode 100644
index 0000000000..c38d2de83b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-position.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fragment Navigation: Scroll to block start position</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="test" style="position: absolute; top: 5px; left: 7px; margin: 5px 7px; border-style: solid; border-width: 5px 7px; padding: 5px 7px; height: 5px; width: 7px;"></div>
+<div style="height: 200vh;"></div>
+<script>
+async_test(function (t) {
+ on_event(window, 'load', function () {
+ t.step(function () {
+ window.scrollTo(0, 0);
+ location.hash = 'test';
+ assert_equals(window.scrollY, 10, 'Scroll to the top border edge of #test');
+ });
+ t.done();
+ });
+}, '');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-anchor-name.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-anchor-name.html
new file mode 100644
index 0000000000..060aed11e2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-anchor-name.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>Fragment Navigation: scroll to anchor name is lower priority than equal id</title>
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div></div>
+<a name="anchor1" style="position:absolute; top:200px;"></a>
+<div id="id-equals-anchor" style="position:absolute; top:300px;"></div>
+<a name="id-equals-anchor" style="position:absolute; top:400px;"></a>
+<a name="§1" style="position:absolute; top:400px;"></a>
+<div style="height:200em;"></div>
+<script>
+var steps = [{
+ fragid:'anchor1',
+ handler: function(){
+ assert_equals( scrollPosition(), 200 );
+ }
+ },{
+ fragid:'id-equals-anchor',
+ handler: function(){
+ // id still takes precedence over anchor name
+ assert_equals( scrollPosition(), 300 );
+ }
+ },{
+ fragid:'§1',
+ handler: function(){
+ assert_equals( scrollPosition(), 400 );
+ }
+ }];
+
+function scrollPosition(){
+ return document.documentElement.scrollTop || document.body.scrollTop;
+}
+
+function runNextStep(){
+ if( steps.length > 0 ) {
+ var step = steps.shift();
+ var listener = t.step_func( function(){
+ step.handler();
+ runNextStep();
+ });
+ scrollToFragmentThenDo( step.fragid, listener );
+ } else {
+ t.done();
+ }
+}
+
+function scrollToFragmentThenDo( fragid, then ){
+ location.hash = fragid;
+ setTimeout( then, 1 );
+}
+
+var t = async_test();
+t.step( function(){
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ runNextStep();
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-id-top.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-id-top.html
new file mode 100644
index 0000000000..601d40a2a1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-id-top.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<title>Fragment Navigation: TOP is a valid element id, which overrides navigating to top of the document</title>
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div></div>
+<div id="Top" style="position:absolute; top:200px;"></div>
+<div style="height:200em; position:relative;"></div>
+<script>
+var steps = [{
+ fragid:'Top',
+ handler: function(){
+ assert_equals( scrollPosition(), 200 );
+ }
+ },{
+ // scroling to top should work when fragid differs from id by case.
+ fragid:'top',
+ handler: function(){
+ assert_equals( scrollPosition(), 0 );
+ }
+ }];
+
+function scrollPosition(){
+ return document.documentElement.scrollTop || document.body.scrollTop;
+}
+
+function runNextStep(){
+ if( steps.length > 0 ) {
+ var step = steps.shift();
+ var listener = t.step_func( function(){
+ step.handler();
+ runNextStep();
+ });
+ scrollToFragmentThenDo( step.fragid, listener );
+ } else {
+ t.done();
+ }
+}
+
+function scrollToFragmentThenDo( fragid, then ){
+ location.hash = fragid;
+ setTimeout( then, 1 );
+}
+
+var t = async_test();
+t.step( function(){
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ runNextStep();
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html
new file mode 100644
index 0000000000..bf62e4cd55
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<title>Fragment Navigation: When fragid is TOP scroll to the top of the document</title>
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div></div>
+<div id="not-the-top"></div>
+<div style="height:200em"></div>
+<script>
+var steps = [{
+ fragid:'not-the-top',
+ handler: function(){
+ assert_not_equals( document.scrollingElement.scrollTop, 0 );
+ }
+ },{
+ fragid:'top',
+ handler: function(){
+ assert_equals( document.scrollingElement.scrollTop, 0 );
+ }
+ },{
+ fragid:'not-the-top',
+ handler: function(){
+ assert_not_equals( document.scrollingElement.scrollTop, 0 );
+ }
+ },{
+ fragid:'TOP',
+ handler: function(){
+ assert_equals( document.scrollingElement.scrollTop, 0 );
+ }
+ }];
+
+function runNextStep(){
+ if( steps.length > 0 ) {
+ var step = steps.shift();
+ var listener = t.step_func( function(){
+ step.handler();
+ runNextStep();
+ });
+ scrollToFragmentThenDo( step.fragid, listener );
+ } else {
+ t.done();
+ }
+}
+
+function scrollToFragmentThenDo( fragid, then ){
+ location.hash = fragid;
+ setTimeout( then, 1 );
+}
+
+var t = async_test();
+t.step( function(){
+ assert_equals(location.hash, "", "Page must be loaded with no hash");
+ runNextStep();
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/001.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/001.html
new file mode 100644
index 0000000000..1ef88d3cc1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/001.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.open in unload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var win;
+
+t.step(function() {
+ win = window.open("support/001-1.html");
+});
+
+add_completion_callback(function() {win.close()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/002.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/002.html
new file mode 100644
index 0000000000..a4e0b243e2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/002.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.open in unload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var win;
+
+t.step(function() {
+ win = window.open("support/002-1.html");
+});
+
+add_completion_callback(function() {win.close()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/003.html
new file mode 100644
index 0000000000..d0a19e0ddc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/003.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.open in beforeunload with link</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var win;
+
+t.step(function() {
+ win = window.open("support/003-1.html");
+});
+
+add_completion_callback(function() {win.close()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/004.html
new file mode 100644
index 0000000000..fca926f652
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/004.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.open in beforeunload with button</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var win;
+
+t.step(function() {
+ win = window.open("support/004-1.html");
+});
+
+add_completion_callback(function() {win.close()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/005.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/005.html
new file mode 100644
index 0000000000..c215fb88e7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/005.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.open in pagehide in iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var win;
+
+t.step(function() {
+ win = window.open("support/005-1.html");
+});
+
+add_completion_callback(function() {win.close()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/base.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/base.html
new file mode 100644
index 0000000000..70c07cba4c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/base.html
@@ -0,0 +1,14 @@
+<!doctype html>
+Base
+<script>
+onpagehide = function() {
+ if(top.base_hide) {
+ top.base_hide();
+ }
+}
+onpageshow = function() {
+if (top.base_show) {
+ top.base_show();
+}
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html
new file mode 100644
index 0000000000..6ba1e65740
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Support page for beforeunload-canceling.html</title>
+
+<h1>If this goes away, it navigated</h1>
+
+<script>
+"use strict";
+
+window.runTest = (t, { valueToReturn, expectCancelation, setReturnValue, expectedReturnValue, cancel }) => {
+ window.onbeforeunload = t.step_func(e => {
+ if (cancel) {
+ e.preventDefault();
+ }
+
+ if (setReturnValue !== undefined) {
+ e.returnValue = setReturnValue;
+ }
+
+ return valueToReturn;
+ });
+
+ const listener = t.step_func(e => {
+ top.assert_equals(e.defaultPrevented, expectCancelation, "canceled");
+ top.assert_equals(e.returnValue, expectedReturnValue, "returnValue");
+ window.onbeforeunload = null;
+
+ t.done();
+ });
+
+ window.addEventListener("beforeunload", listener);
+
+ window.location.href = "about:blank";
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
new file mode 100644
index 0000000000..29a685fa59
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
@@ -0,0 +1,222 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>beforeunload return value cancelation behavior</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ let onbeforeunloadHappened = false;
+ window.onbeforeunload = t.step_func(() => {
+ onbeforeunloadHappened = true;
+ return "cancel me";
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "beforeunload");
+ const promise = eventWatcher.wait_for("beforeunload").then(e => {
+ assert_true(onbeforeunloadHappened, "CustomEvent must be able to trigger the event handler");
+ assert_false(e.defaultPrevented, "The event must not have been canceled");
+ window.onbeforeunload = null;
+ });
+
+ window.dispatchEvent(new CustomEvent("beforeunload"));
+
+ return promise;
+}, "Returning a string must not cancel the event: CustomEvent, non-cancelable");
+
+promise_test(t => {
+ let onbeforeunloadHappened = false;
+ window.onbeforeunload = t.step_func(() => {
+ onbeforeunloadHappened = true;
+ return "cancel me";
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "beforeunload");
+ const promise = eventWatcher.wait_for("beforeunload").then(e => {
+ assert_true(onbeforeunloadHappened, "CustomEvent must be able to trigger the event handler");
+ assert_false(e.defaultPrevented, "The event must not have been canceled");
+ window.onbeforeunload = null;
+ t.done();
+ });
+
+ window.dispatchEvent(new CustomEvent("beforeunload", { cancelable: true }));
+
+ return promise;
+}, "Returning a string must not cancel the event: CustomEvent, cancelable");
+
+promise_test(t => {
+ let onbeforeunloadHappened = false;
+ window.onbeforeunload = t.step_func(() => {
+ onbeforeunloadHappened = true;
+ return false;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "beforeunload");
+ const promise = eventWatcher.wait_for("beforeunload").then(e => {
+ assert_true(onbeforeunloadHappened, "CustomEvent must be able to trigger the event handler");
+ assert_false(e.defaultPrevented, "The event must not have been canceled");
+ window.onbeforeunload = null;
+ t.done();
+ });
+
+ window.dispatchEvent(new CustomEvent("beforeunload", { cancelable: true }));
+
+ return promise;
+}, "Returning false must not cancel the event, because it's coerced to the DOMString \"false\" which does not cancel " +
+ "CustomEvents: CustomEvent, cancelable");
+
+// This test can be removed if we update the DOM Standard to disallow createEvent("BeforeUnloadEvent"). Browser support
+// is inconsistent. https://github.com/whatwg/dom/issues/362
+promise_test(t => {
+ const eventWatcher = new EventWatcher(t, window, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_false(e.defaultPrevented, "The event must not have been canceled");
+ window.onbeforeunload = null;
+ t.done();
+ });
+
+ const ev = document.createEvent("BeforeUnloadEvent");
+ ev.initEvent("click", false, true);
+ window.dispatchEvent(ev);
+
+ return promise;
+}, "Returning a string must not cancel the event: BeforeUnloadEvent with type \"click\", cancelable");
+
+const testCases = [
+ {
+ valueToReturn: null,
+ expectCancelation: false,
+ expectedReturnValue: ""
+ },
+ {
+ valueToReturn: undefined,
+ expectCancelation: false,
+ expectedReturnValue: ""
+ },
+ {
+ valueToReturn: "",
+ expectCancelation: true,
+ expectedReturnValue: ""
+ },
+ {
+ valueToReturn: false,
+ expectCancelation: true,
+ expectedReturnValue: "false"
+ },
+ {
+ valueToReturn: true,
+ expectCancelation: true,
+ expectedReturnValue: "true"
+ },
+ {
+ valueToReturn: 0,
+ expectCancelation: true,
+ expectedReturnValue: "0"
+ },
+ {
+ valueToReturn: null,
+ expectCancelation: false,
+ setReturnValue: "foo",
+ expectedReturnValue: "foo"
+ },
+ {
+ valueToReturn: undefined,
+ expectCancelation: false,
+ setReturnValue: "foo",
+ expectedReturnValue: "foo"
+ },
+ {
+ valueToReturn: "",
+ expectCancelation: true,
+ setReturnValue: "foo",
+ expectedReturnValue: "foo"
+ },
+ {
+ valueToReturn: false,
+ expectCancelation: true,
+ setReturnValue: "foo",
+ expectedReturnValue: "foo"
+ },
+ {
+ valueToReturn: true,
+ expectCancelation: true,
+ setReturnValue: "foo",
+ expectedReturnValue: "foo"
+ },
+ {
+ valueToReturn: 0,
+ expectCancelation: true,
+ setReturnValue: "foo",
+ expectedReturnValue: "foo"
+ },
+ {
+ setReturnValue: "",
+ expectedReturnValue: "",
+ expectCancelation: false,
+ },
+ {
+ expectCancelation: true,
+ expectedReturnValue: "",
+ cancel: true
+ },
+ {
+ setReturnValue: "foo",
+ expectCancelation: true,
+ expectedReturnValue: "foo",
+ cancel: true
+ },
+ {
+ valueToReturn: "foo",
+ expectedReturnValue: "foo",
+ expectCancelation: true,
+ cancel: true
+ },
+ {
+ valueToReturn: "foo",
+ setReturnValue: "foo",
+ expectedReturnValue: "foo",
+ expectCancelation: true,
+ cancel: true
+ },
+ {
+ valueToReturn: true,
+ setReturnValue: "",
+ expectedReturnValue: "true",
+ expectCancelation: true,
+ cancel: true
+ }
+];
+
+var testCaseIndex = 0;
+function runNextTest() {
+ const testCase = testCases[testCaseIndex];
+
+ const labelAboutReturnValue = testCase.setReturnValue === undefined ? "" :
+ `; setting returnValue to ${testCase.setReturnValue}`;
+
+ const labelAboutCancel = testCase.cancel === undefined ? "" :
+ "; calling preventDefault()";
+
+ const suffixLabels = labelAboutReturnValue + labelAboutCancel;
+
+ async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ iframe.contentWindow.runTest(t, testCase);
+ if (++testCaseIndex < testCases.length)
+ runNextTest();
+ });
+
+ iframe.src = "beforeunload-canceling-1.html";
+ document.body.appendChild(iframe);
+ }, `Returning ${testCase.valueToReturn} with a real iframe unloading${suffixLabels}`);
+}
+
+runNextTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back-1.html
new file mode 100644
index 0000000000..4403cfa8e9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back-1.html
@@ -0,0 +1,5 @@
+<!doctype html>
+001-1
+<script>
+addEventListener("beforeunload", function() {top.t.step(function() {top.beforeunload_fired = true})}, false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back.html
new file mode 100644
index 0000000000..5b0415c422
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>beforeunload event fires on history navigation back</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+beforeunload_fired = false;
+var t = async_test();
+
+var base_count = 0;
+
+onload = function() {setTimeout(t.step_func(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+ iframe.onload = t.step_func(function() {
+ iframe.onload = null;
+ history.go(-1);
+ });
+
+ iframe.src = "beforeunload-on-history-back-1.html";
+}), 100)};
+
+base_show = t.step_func(function() {
+ base_count++;
+ if (base_count > 1) {
+ assert_true(beforeunload_fired);
+ t.done();
+ }
+});
+
+</script>
+<iframe src="base.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-1.html
new file mode 100644
index 0000000000..4f239dad1e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-1.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<iframe src="beforeunload-on-navigation-of-parent-2.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-2.html
new file mode 100644
index 0000000000..a34b182e70
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-2.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+addEventListener("beforeunload", function() {parent.parent.beforeunload_fired=true}, false)
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent.html
new file mode 100644
index 0000000000..96d49567f3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>beforeunload in iframe on navigation of parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+beforeunload_fired = false;
+var t = async_test();
+
+var base_count = 0;
+
+onload = function() {setTimeout(t.step_func(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+ iframe.onload = t.step_func(function() {
+ iframe.onload = null;
+ history.go(-1);
+ });
+
+ iframe.src = "beforeunload-on-navigation-of-parent-1.html";
+}), 100)};
+
+base_show = t.step_func(function() {
+ base_count++;
+ if (base_count > 1) {
+ assert_true(beforeunload_fired);
+ t.done();
+ }
+});
+
+</script>
+<iframe src="base.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-iframe.html
new file mode 100644
index 0000000000..212a10c005
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-iframe.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Beforeunload must be gated behind sticky activation: nested browsing context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p>If you happen to be running this test as a human, then be sure not to interact with any part of the page; that would invalidate the results!
+
+<script>
+setup({ single_test: true });
+
+const iframe = document.createElement('iframe');
+iframe.src = 'support/beforeunload-sticky-start.html';
+
+window.onmessage = e => {
+ assert_equals(e.data, 'navigated successfully');
+
+ const desiredURL = (new URL('support/beforeunload-sticky-destination.html', location.href)).href;
+ assert_equals(iframe.contentWindow.location.href, desiredURL);
+
+ done();
+};
+
+document.body.append(iframe);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-manual.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-manual.html
new file mode 100644
index 0000000000..55612bbfc4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-manual.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Beforeunload must be gated behind sticky activation: normal top-level browsing context</title>
+
+<p>This test is manual because we want to test non-popup, non-iframe situations. Sibling files contain automated tests for those situations.
+
+<p>In three seconds, this document will redirect itself to a new page. The test passes if the redirect succeeds. The test fails if a beforeunload dialog pops up asking for confirmation.
+
+<p>Be sure not to interact with any part of the page in the meantime. That would invalidate the results.
+
+<script>
+window.onbeforeunload = e => e.preventDefault();
+
+setTimeout(() => {
+ location.href = 'support/beforeunload-sticky-destination.html';
+}, 3000);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-popup.html
new file mode 100644
index 0000000000..23bf8a440d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-sticky-activation-popup.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Beforeunload must be gated behind sticky activation: auxiliary browsing context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p>If you happen to be running this test as a human, then be sure not to interact with any part of the page; that would invalidate the results!
+
+<script>
+setup({ single_test: true });
+
+const w = window.open('support/beforeunload-sticky-start.html');
+
+window.onmessage = e => {
+ assert_equals(e.data, 'navigated successfully');
+
+ const desiredURL = (new URL('support/beforeunload-sticky-destination.html', location.href)).href;
+ assert_equals(w.location.href, desiredURL);
+
+ w.close();
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html
new file mode 100644
index 0000000000..6806eaf7a3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>beforeunload event is emitted synchronously</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#the-event-handler-processing-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+'use strict';
+// "navigate a browsing context" synchronously calls "prompt to unload", which
+// synchronously calls "dispatch an event".
+
+async_test(function(t) {
+ var iframe = document.createElement('iframe');
+
+ iframe.onload = t.step_func(function() {
+ var callCount = 0;
+
+ iframe.contentWindow.onbeforeunload = function() {
+ callCount += 1;
+ };
+
+ iframe.contentWindow.location.href = '/common/blank.html';
+
+ assert_equals(callCount, 1, 'invoked synchronously exactly once');
+
+ t.done();
+ });
+
+ document.body.appendChild(iframe);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-1.html
new file mode 100644
index 0000000000..b96234fba2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-1.html
@@ -0,0 +1,10 @@
+<!doctype html>
+004-1
+<script>
+addEventListener("beforeunload",
+function() {
+if (top.counter++ < 999) {
+ location = "navigation-within-beforeunload-2.html?" + top.counter;
+}
+}, false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-2.html
new file mode 100644
index 0000000000..2dceaa6d6a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload-2.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+document.write(location)
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload.html
new file mode 100644
index 0000000000..d6ecf5d52f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>Triggering navigation from within beforeunload event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+beforeunload_fired = false;
+var t = async_test();
+
+var base_count = 0;
+var counter = 0;
+
+onload = function() {setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+
+ iframe.onload = function() {
+ setTimeout(function() {iframe.contentWindow.location="navigation-within-beforeunload-2.html";}, 100);
+ // Step 4 of https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigating-across-documents
+ // doesn't seem to allow navigation within a beforeunload handler,
+ // so the counter should not go beyond 1.
+ iframe.onload = t.step_func(function() {assert_equals(counter, 1); t.done()});
+ };
+
+ iframe.src = "navigation-within-beforeunload-1.html?" + Math.random();
+
+}, 100)};
+
+</script>
+<iframe src="base.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward-1.html
new file mode 100644
index 0000000000..a60c20ed80
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward-1.html
@@ -0,0 +1,2 @@
+<!doctype html>
+filler text
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward.html
new file mode 100644
index 0000000000..5e64b5ec66
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/pagehide-on-history-forward.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>pagehide event fires on history navigation forward</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+onload = function() {setTimeout(t.step_func(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+
+ iframe.src = "pagehide-on-history-forward-1.html";
+}), 100)};
+
+base_hide = t.step_func(function() {
+ t.done()
+});
+</script>
+<iframe src="base.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-closeable.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-closeable.html
new file mode 100644
index 0000000000..b94789c40f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-closeable.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>beforeunload and unload events fire after window.close() in script-closeable browsing context</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+beforeunload_fired = false;
+var t = async_test();
+
+onload = t.step_func(function() {
+ window.close();
+});
+
+onbeforeunload = t.step_func(function() {
+ beforeunload_fired = true;
+});
+
+onunload = t.step_func(function() {
+ assert_true(beforeunload_fired);
+ t.done()
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable-1.html
new file mode 100644
index 0000000000..3a557ce34e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable-1.html
@@ -0,0 +1,10 @@
+<!doctype html>
+script-uncloseable-1
+<script>
+onbeforeunload = function() {
+ parent.beforeunload_fired = true;
+};
+onunload = function() {
+ parent.unload_fired = true;
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable.html
new file mode 100644
index 0000000000..f6a17d740b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-uncloseable.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>beforeunload and unload events do not fire after window.close() in script-uncloseable browsing context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var beforeunload_fired = false;
+var unload_fired = false;
+var t = async_test();
+
+onload = t.step_func(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+ iframe.onload = t.step_func(function() {
+ iframe.contentWindow.close()
+ t.step_timeout(function() {
+ assert_false(beforeunload_fired);
+ assert_false(unload_fired);
+ t.done();
+ }, 1000);
+ });
+ iframe.src = "prompt-and-unload-script-uncloseable-1.html";
+});
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-1.html
new file mode 100644
index 0000000000..b68afc49ec
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-1.html
@@ -0,0 +1,10 @@
+<script>
+addEventListener("beforeunload",
+function() {
+ parent.events.push("beforeunload");
+}, false);
+parent.events.push("before src change");
+
+location.href = "001-2.html&pipe=trickle(d2)";
+parent.events.push("after src change");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-2.html
new file mode 100644
index 0000000000..9da0f9395c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001-2.html
@@ -0,0 +1 @@
+001-2
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001.html
new file mode 100644
index 0000000000..109dcc1393
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/001.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>beforeunload event order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+events = [];
+onload = t.step_func(function() {
+ assert_array_equals(events, ["before src change", "beforeunload", "after src change"]);
+ t.done();
+})
+</script>
+<iframe src="001-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002-1.html
new file mode 100644
index 0000000000..c5f57375da
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002-1.html
@@ -0,0 +1,7 @@
+<script>
+addEventListsner("beforeunload", parent.t.step_func(
+function(e) {
+ parent.do_test(e);
+}, false);
+location.href = "001-2.html&pipe=trickle(d2)";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002.html
new file mode 100644
index 0000000000..d8f4fc60a9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/002.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>beforeunload event properties</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+function do_test(e) {
+ assert_equals(e.type, "beforeunload");
+ assert_false(e.bubbles, "bubbles");
+ assert_true(e.cancelable, "bubbles");
+ assert_equals(e.returnValue, "");
+}
+
+onload = t.step_func(function() {
+ t.done();
+})
+</script>
+<iframe src="001-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/003.html
new file mode 100644
index 0000000000..5683f1b120
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/003.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>beforeunload event in child frame for parent navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+function do_test(e) {
+ assert_equals(e.type, "beforeunload");
+ assert_false(e.bubbles, "bubbles");
+ assert_true(e.cancelable, "bubbles");
+ assert_equals(e.returnValue, "");
+}
+
+onload = t.step_func(function() {
+ t.done();
+})
+</script>
+<iframe src="001-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-1.html
new file mode 100644
index 0000000000..a3ca82f520
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-1.html
@@ -0,0 +1,28 @@
+<!doctype html>
+004-1
+<script>
+var handleBeforeUnload = function() {
+ parent.beforeunload_fired = true;
+ removeListener();
+ setTimeout(function() {
+ parent.timeout_fired = true;
+ }, 1000);
+}
+
+var removeListener = function() {
+ assert_true(window.removeEventListener('beforeunload', handleBeforeUnload, false));
+}
+
+window.addEventListener('beforeunload', handleBeforeUnload, false);
+
+onload = function() {
+ if (!parent.loaded) {
+ parent.loaded = true;
+ location="004-2.html?" + Math.random();
+ }
+}
+</script>
+// child frame with no onbeforeunload listener. Should leave the parent as unsalvageable.
+// Adding the iframe prevents potential implementation bugs where the the recursive steps of #prompt-to-unload-a-document
+// would overwrite the salvageable state of the parent.
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-2.html
new file mode 100644
index 0000000000..1a605b1b3d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004-2.html
@@ -0,0 +1,5 @@
+<!doctype html>
+004-2
+<script>
+onload = function() {setTimeout(parent.t.step_func(function() {parent.start_test(); history.go(-1)}), 100)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004.html
new file mode 100644
index 0000000000..7076a4dd18
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/004.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>salvagable state of document after setting beforeunload listener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+var loaded = false;
+var beforeunload_fired = false;
+var timeout_fired = false;
+
+function start_test() {
+ step_timeout(
+ t.step_func(function() {
+ assert_true(beforeunload_fired);
+ assert_false(timeout_fired);
+ t.done()
+ }), 1000);
+}
+
+onload = function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+ onload = null;
+ iframe.src="004-1.html?" + Math.random();
+};
+
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-001.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-001.html
new file mode 100644
index 0000000000..3b7ef74b71
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-001.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Prompt when beforeunload is canceled</title>
+<script>
+addEventListener("beforeunload",
+function(e) {e.preventDefault()},
+false);
+</script>
+<p>When clicking the link below, you should get a prompt asking if you want to unload the document</p>
+<a href="next.html">Click here</a>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-002.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-002.html
new file mode 100644
index 0000000000..7be8a3301f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-002.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Prompt when beforeunload has returnValue set</title>
+<script>
+addEventListener("beforeunload",
+function(e) {e.returnValue = "PASS if you see this"},
+false);
+</script>
+<p>When clicking the link below, you should get a prompt asking if you want to unload the document</p>
+<a href="next.html">Click here</a>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-003.html
new file mode 100644
index 0000000000..ff72b67055
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-003.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Prompt when beforeunload is canceled</title>
+<script>
+addEventListener("beforeunload",
+function(e) {e.preventDefault()},
+false);
+</script>
+<p>When clicking the button below, you should get a prompt asking if you want to unload the document</p>
+<form method="get" action="next.html">
+<input type="submit" value="Click here">
+</form>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-004.html
new file mode 100644
index 0000000000..a4d2968922
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-004.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Prompt on form submit</title>
+<script>
+addEventListener("beforeunload",
+function(e) {e.preventDefault()},
+false);
+</script>
+<p>When clicking the button below, you should get a prompt asking if you want to unload the document</p>
+<form method="get" action="next.html">
+<input type="submit" value="Click here">
+</form>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-005.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-005.html
new file mode 100644
index 0000000000..71ff0a241d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-005.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Event loop pause for beforeunload</title>
+<script>
+var counter = 0;
+
+onload = function count() {
+ document.getElementById("log").textContent = counter++
+ setTimeout(count, 200);
+}
+
+addEventListener("beforeunload",
+function(e) {
+ e.preventDefault()
+},
+false);
+</script>
+<ul>
+<li>Click on the link below. When the prompt appears the counter at the bottom must stop incrementing.
+<li>Opt not to leave the page. The counter must start incrementing again
+</ul>
+<p><a href="">Click here</a>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-006.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-006.html
new file mode 100644
index 0000000000..dae0340ad9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/manual-006.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Prompt when beforeunload returns string value</title>
+<script>
+addEventListener("beforeunload",
+function(e) {return "PASS if you see this"},
+false);
+</script>
+<p>When clicking the link below, you should get a prompt asking if you want to unload the document</p>
+<a href="next.html">Click here</a>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/next.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/next.html
new file mode 100644
index 0000000000..38e7cdd5e0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/next.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<p>You should have seen a prompt asking you to unload the previous document
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001-1.html
new file mode 100644
index 0000000000..2a9cab4aa4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<script>
+ t = opener.t;
+ do_test = t.step_func(function () {
+ localStorage.test6564729 += '4';
+ var d = document;
+ var e = document.open(); // has no effect (ignore-opens-during-unload > 0)
+ localStorage.test6564729 += (e == d) ? '5' : 'A [' + e + '] ';
+ document.write('FAIL - document.write executed and blocked navigation!'); // has no effect (ignore-opens-during-unload > 0)
+ localStorage.test6564729 += document.body.textContent.match('FAIL') ? 'B' : '6';
+ document.close(); // has no effect (no script-created parser)
+ localStorage.test6564729 += '7';
+ })
+onload = t.step_func(function() {
+ localStorage.test6564729 = '0';
+ setTimeout(t.step_func(function() {document.links[0].click()}));
+});
+</script>
+<body onbeforeunload="localStorage.test6564729 += '1'"
+ onpagehide="localStorage.test6564729 += '3'"
+ onunload="do_test()">
+<p><a href="001a.html">Follow this link to run the test.</a>
+<p><iframe src="001b.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001a.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001a.html
new file mode 100644
index 0000000000..36d4188b9e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001a.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<script>
+opener.t.step(function() {
+ opener.assert_equals(localStorage.test6564729, '0123456789');
+ opener.t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001b.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001b.html
new file mode 100644
index 0000000000..eaafc371a1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/001b.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<body onbeforeunload="localStorage.test6564729 += '2'"
+ onpagehide="localStorage.test6564729 += '8'"
+ onunload="localStorage.test6564729 += '9'">
+<p>Inner frame \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002-1.html
new file mode 100644
index 0000000000..017fbb964c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002-1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<script>
+ var t = opener.t;
+
+ var do_test = t.step_func(function() {
+ localStorage.test6564729 += '1';
+ var d = document;
+ // This document's unload not triggered here because `document.open` erases
+ // all of the document's handlers. However the iframe's event handlers (002b)
+ // will fire. The `beforeunload` event handler will not fire because this is
+ // not a navigation resulting from a user interaction.
+ var e = document.open();
+ localStorage.test6564729 += (e == d) ? '8' : 'X';
+ var s = 'FAIL if you see this | ' + localStorage.test6564729;
+ document.write(s);
+ localStorage.test6564729 += document.body.textContent == s ? '9' : 'x';
+ document.close();
+ localStorage.test6564729 += 'Z';
+ document.body.textContent += ' // ' + localStorage.test6564729;
+ location = '002a.html'; // unload triggers again here, but they're not registered event listeners any more
+ });
+
+onload = t.step_func(function() {
+ localStorage.test6564729 = '0';
+ setTimeout(function() {document.getElementsByTagName("input")[0].click()}, 100);
+});
+</script>
+<body onbeforeunload="localStorage.test6564729 += '2'"
+ onpagehide="localStorage.test6564729 += '4'"
+ onunload="localStorage.test6564729 += '5'">
+<input type=button value="Activate this button to run the test" onclick="do_test()">
+<p><iframe src="002b.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002a.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002a.html
new file mode 100644
index 0000000000..51ff3e37b6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002a.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<script>
+ opener.t.step(function() {
+ opener.assert_equals(localStorage.test6564729, '016789Z');
+ opener.t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002b.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002b.html
new file mode 100644
index 0000000000..d08a7a8add
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/002b.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<body onbeforeunload="localStorage.test6564729 += '3'"
+ onpagehide="localStorage.test6564729 += '6'"
+ onunload="localStorage.test6564729 += '7'">
+<p>Inner frame \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003-1.html
new file mode 100644
index 0000000000..b3a4754b85
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<script>
+ var t = opener.t;
+ var do_test = t.step_func(function() {
+ localStorage.test6564729 += '1';
+ var d = document;
+ var e = document.open(); // has no effect (ignore-opens-during-unload > 0 because we're in beforeunload)
+ localStorage.test6564729 += (e == d) ? '2' : 'A [' + e + '] ';
+ document.write('FAIL - document.write executed and blocked navigation!'); // has no effect (ignore-opens-during-unload > 0)
+ localStorage.test6564729 += document.body.textContent.match('FAIL') ? 'B' : '3';
+ document.close(); // has no effect (no script-created parser)
+ localStorage.test6564729 += '4';
+ })
+
+ onload=t.step_func(function() {localStorage.test6564729 = '0'; setTimeout(t.step_func(function() {document.links[0].click()}), 100)})
+
+</script>
+<body
+ onbeforeunload="do_test()"
+ onpagehide="localStorage.test6564729 += '6'"
+ onunload="localStorage.test6564729 += '7'">
+<p><a href="003a.html">Follow this link to run the test.</a>
+<p><iframe src="003b.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003a.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003a.html
new file mode 100644
index 0000000000..5393fa221e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003a.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<p>FAIL</p>
+<script>
+opener.t.step(function() {
+ opener.assert_equals(localStorage.test6564729, '0123456789')
+ opener.t.done();
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003b.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003b.html
new file mode 100644
index 0000000000..c8f1917b85
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/003b.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<body onbeforeunload="localStorage.test6564729 += '5'"
+ onpagehide="localStorage.test6564729 += '8'"
+ onunload="localStorage.test6564729 += '9'">
+<p>Inner frame \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004-1.html
new file mode 100644
index 0000000000..06aba08af6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<script>
+ var t = opener.t;
+ var do_test = t.step_func(function() {
+ localStorage.test6564729 += 'B';
+ var d = document;
+ var e = document.open(); // unload triggered here - beforeunload C, D in 004b; pagehide E, unload F, pagehide G in 004b, unload HIJK in 004b
+ localStorage.test6564729 += (e == d) ? 'L' : 'Y';
+ var s = 'FAIL if you see this | ' + localStorage.test6564729;
+ document.write(s);
+ localStorage.test6564729 += document.body.textContent == s ? 'M' : 'y';
+ document.close();
+ localStorage.test6564729 += 'N';
+ location = '004a.html'; // unload triggers again here, but they're not registered event listeners any more
+ })
+onload = t.step_func(function() {
+ localStorage.test6564729 = 'A';
+ setTimeout(t.step_func(function() {document.getElementsByTagName("input")[0].click()}), 100);
+})
+</script>
+<body onbeforeunload="localStorage.test6564729 += 'C'"
+ onpagehide="localStorage.test6564729 += 'E'"
+ onunload="localStorage.test6564729 += 'F'">
+<input type=button value="Activate this button to run the test" onclick="do_test()">
+<p><iframe src="004b.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004a.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004a.html
new file mode 100644
index 0000000000..117e2b94ae
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004a.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<p>FAIL</p>
+<script>
+opener.t.step(function() {
+ opener.assert_equals(localStorage.test6564729, 'ABCDEFGHIJKLMN');
+ opener.t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004b.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004b.html
new file mode 100644
index 0000000000..788937a0b0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/004b.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<script>
+ function testRun() {
+ localStorage.test6564729 += 'H';
+ var d = parent.document;
+ var e = parent.document.open(); // no effect, since that document is already in unload
+ localStorage.test6564729 += (e == d) ? 'I' : 'X';
+ var s = 'FAIL';
+ document.write(s);
+ localStorage.test6564729 += document.body.textContent == s ? 'x' : 'J';
+ document.close();
+ localStorage.test6564729 += 'K';
+ }
+</script>
+<body onbeforeunload="localStorage.test6564729 += 'D'"
+ onpagehide="localStorage.test6564729 += 'G'"
+ onunload="testRun()">
+<p>Inner frame \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005-1.html
new file mode 100644
index 0000000000..7b81a9f115
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<script>
+onload = opener.t.step_func(function() {
+ localStorage.test6564729 = '0'
+ setTimeout(opener.t.step_func(function() {document.links[0].click()}), 100);
+});
+</script>
+<body
+ onbeforeunload="localStorage.test6564729 += '1'"
+ onpagehide="localStorage.test6564729 += '3'"
+ onunload="localStorage.test6564729 += '4'">
+<p><a href="005a.html">Follow this link to run the test.</a>
+<p><iframe src="005b.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005a.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005a.html
new file mode 100644
index 0000000000..5185d3b921
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005a.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<p>FAIL</p>
+<script>
+opener.t.step(function() {
+ opener.assert_equals(localStorage.test6564729, '012345678')
+ opener.t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005b.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005b.html
new file mode 100644
index 0000000000..476e8e38c4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/005b.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<script>
+ var t = parent.opener.t;
+ var do_test = t.step_func(function () {
+ localStorage.test6564729 += '5';
+ var s = 'FAIL: document.open() has canceled the navigation (' + localStorage.test6564729 + ')';
+ parent.document.open();
+ parent.document.write(s);
+ parent.document.close();
+ localStorage.test6564729 += parent.document.body.textContent.match('FAIL') == s ? 'X' : '6';
+ localStorage.test6564729 += '7';
+ });
+</script>
+<body onbeforeunload="localStorage.test6564729 += '2'"
+ onpagehide="do_test()"
+ onunload="localStorage.test6564729 += '8'">
+<p>Inner frame
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-destination.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-destination.html
new file mode 100644
index 0000000000..2edcf1a43b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-destination.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Beforeunload must be gated behind sticky activation: destination page</title>
+
+<p>If you reached this page without clicking through a confirmation dialog, then the test has passed!
+
+<script>
+if (window.opener) {
+ window.opener.postMessage('navigated successfully');
+} else if (window.parent) {
+ window.parent.postMessage('navigated successfully');
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-start.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-start.html
new file mode 100644
index 0000000000..37109feafe
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/support/beforeunload-sticky-start.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Beforeunload must be gated behind sticky activation: start page</title>
+
+<p>This page will immediately navigate. If a beforeunload dialog pops up, the test fails.</p>
+
+<script>
+window.onbeforeunload = e => e.preventDefault();
+location.href = 'beforeunload-sticky-destination.html';
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-1.html
new file mode 100644
index 0000000000..74ba43954b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-1.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+addEventListener("pagehide", parent.t.step_func(function() {parent.pagehide_fired = true}), false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-2.html
new file mode 100644
index 0000000000..90e28ab7fb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001-2.html
@@ -0,0 +1,2 @@
+<!doctype html>
+Filler
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001.html
new file mode 100644
index 0000000000..444a2770c7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/001.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>pagehide event on unload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+pagehide_fired = false;
+var t = async_test();
+
+onload = function() {setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+
+ iframe.onload = function() {
+ setTimeout(function() {
+ iframe.contentWindow.location="001-2.html";
+ }, 100);
+ iframe.onload = t.step_func(function() {assert_true(pagehide_fired); t.done()});
+ };
+
+ iframe.src = "001-1.html?" + Math.random();
+
+}, 100)};
+
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002-1.html
new file mode 100644
index 0000000000..fd8e2b7262
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002-1.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+addEventListener("pagehide", parent.t.step_func(parent.do_test()), false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002.html
new file mode 100644
index 0000000000..d36011286c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/002.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>pagehide event properties</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+onload = function() {setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+
+ iframe.onload = function() {
+ setTimeout(function() {
+ iframe.contentWindow.location="001-2.html";
+ }, 100);
+ iframe.onload = t.step_func(function() {t.done()});
+ };
+
+ function do_test(e) {
+ assert_equals(e.type, "pagehide");
+ assert_equals(e.target, iframe.contentDocument);
+ assert_equals(e.currentTarget, iframe.contentWindow);
+
+ // https://github.com/whatwg/html/issues/6794
+ assert_true(e.bubbles, "bubbles");
+ assert_true(e.cancelable, "cancelable");
+
+ assert_true(e.persisted, "persisted");
+ }
+
+ iframe.src = "002-1.html?" + Math.random();
+
+}, 100)};
+
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003-1.html
new file mode 100644
index 0000000000..9838c79456
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003-1.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+addEventListener("unload", parent.t.step_func(function(e) {parent.do_test(e)}), false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003.html
new file mode 100644
index 0000000000..97821be484
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/003.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>unload event properties</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var do_test;
+
+onload = function() {setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+
+ iframe.onload = function() {
+ setTimeout(function() {
+ iframe.contentWindow.location="002-2.html";
+ }, 100);
+ iframe.onload = t.step_func(function() {t.done()});
+ };
+
+ do_test = function(e) {
+ assert_equals(e.type, "unload");
+ assert_equals(e.target, iframe.contentDocument);
+ assert_equals(e.currentTarget, iframe.contentWindow);
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ }
+
+ iframe.src = "003-1.html?" + Math.random();
+
+}, 100)};
+
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004-1.html
new file mode 100644
index 0000000000..5d0497556b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004-1.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+addEventListener("pagehide", function() {parent.events.push("pagehide"); setTimeout(function() {parent.events.push("timeout")}, 0)}, false);
+addEventListener("unload", function() {parent.events.push("unload")}, false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004.html
new file mode 100644
index 0000000000..301baa3b8e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/004.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>pagehide / unload event order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+var events = [];
+
+onload = function() {setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+
+ iframe.onload = function() {
+ setTimeout(function() {
+ iframe.contentWindow.location="001-2.html";
+ }, 100);
+ iframe.onload = t.step_func(function() {
+ assert_array_equals(events, ["pagehide", "unload"])
+ t.done()});
+ };
+
+ iframe.src = "004-1.html?" + Math.random();
+
+}, 100)};
+
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-1.html
new file mode 100644
index 0000000000..bc2e10bdc3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-1.html
@@ -0,0 +1,23 @@
+<!doctype html>
+006-1
+<script>
+onpagehide = function() {
+ onpagehide = null;
+ setTimeout(function() {
+ parent.t.unreached_func('setTimeout survived navigatoin');
+ }, 1000);
+}
+if (parent.loaded) {
+ setTimeout(function() { parent.t.done(); }, 2000);
+}
+onload = function() {
+ if (!parent.loaded) {
+ parent.loaded = true;
+ setTimeout(parent.t.step_func(
+ function() {
+ location="006-2.html?" + Math.random();
+ }
+ ), 100);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-2.html
new file mode 100644
index 0000000000..52365e55d8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006-2.html
@@ -0,0 +1,5 @@
+<!doctype html>
+006-2
+<script>
+onload = function() {setTimeout(parent.t.step_func(function() {history.go(-1)}), 100)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006.html
new file mode 100644
index 0000000000..c9e4d68a10
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/006.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>salvagable state of document after setting pagehide listener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+var loaded = false;
+
+onload = function() {setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+ onload = null;
+ iframe.src="006-1.html?" + Math.random();
+}, 100)};
+
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-1.html
new file mode 100644
index 0000000000..ed19f4498a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-1.html
@@ -0,0 +1,21 @@
+<!doctype html>
+007-1
+<script>
+onunload = function() {
+ onunload = null;
+ parent.unload_fired = true;
+ setTimeout(function() {
+ parent.timeout_fired = true;
+ }, 100);
+}
+onload = function() {
+ if (!parent.loaded) {
+ parent.loaded = true;
+ setTimeout(parent.t.step_func(
+ function() {
+ location="007-2.html?" + Math.random();
+ }
+ ), 100);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-2.html
new file mode 100644
index 0000000000..f74cd1e67e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007-2.html
@@ -0,0 +1,5 @@
+<!doctype html>
+007-2
+<script>
+onload = function() {setTimeout(parent.t.step_func(function() {parent.start_test(); history.go(-1)}), 100)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007.html
new file mode 100644
index 0000000000..4a2fed5fac
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/007.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>salvagable state of document after setting unload listener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+
+var loaded = false;
+var unload_fired = false;
+var timeout_fired = false;
+
+function start_test() {
+ step_timeout(t.step_func(function() {
+ assert_true(unload_fired);
+ assert_false(timeout_fired);
+ t.done()
+ }), 1000);
+}
+
+onload = function() {setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0]
+ onload = null;
+ iframe.src="007-1.html?" + Math.random();
+}, 100)};
+
+</script>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008-1.html
new file mode 100644
index 0000000000..29de29c911
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008-1.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+onpagehide = parent.t.step_func(function() {parent.t.done()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008.html
new file mode 100644
index 0000000000..015507d817
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/008.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>pagehide IDL attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var iframe;
+var t = async_test();
+onload = function() {
+ setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0];
+ iframe.src="about:blank";
+ }, 100)
+};
+</script>
+<iframe src="008-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009-1.html
new file mode 100644
index 0000000000..d69a05914a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009-1.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+onunload = parent.t.step_func(function() {parent.t.done()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009.html
new file mode 100644
index 0000000000..0e93e04701
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/009.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>unload IDL attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var iframe;
+var t = async_test();
+onload = function() {
+ setTimeout(function() {
+ var iframe = document.getElementsByTagName("iframe")[0];
+ iframe.src="about:blank";
+ }, 100)
+}
+</script>
+<iframe src="009-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual-1.html
new file mode 100644
index 0000000000..3da0a0de3e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual-1.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<p>Now go back. PASS should be displayed after a short pause
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual.html b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual.html
new file mode 100644
index 0000000000..ba34c3087f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/pagehide-manual.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>Document salvagable state after setting pagehide handler</title>
+<script>onpagehide = function() {setTimeout(function(){document.body.innerHTML = "PASS"}, 100)}</script>
+<p>Click the link below then navigate back to this page. Shortly after returning you should see the text "PASS"</p>
+<p><a href="pagehide-manual-1.html">Click here</a>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-cross-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-cross-origin.window.js
new file mode 100644
index 0000000000..65b4e533f6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-cross-origin.window.js
@@ -0,0 +1,34 @@
+// META: title=Unload runs in main frame when navigating cross-origin.
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const rc1 = await rcHelper.addWindow();
+
+ t.add_cleanup(() => localStorage.removeItem('unload'));
+
+ // Initialize storage and add "unload" event handler.
+ await rc1.executeScript(() => {
+ localStorage.setItem('unload', 'not yet');
+ addEventListener('unload', () => {
+ localStorage.setItem('unload', 'ran');
+ });
+ });
+
+ // Navigate away.
+ const rc2 = await rc1.navigateToNew(
+ {extraRemoteContextConfig: {origin: 'HTTP_REMOTE_ORIGIN'}});
+
+ // Navigate back.
+ await rc2.historyBack();
+
+ // Test that the unload handler wrote to storage.
+ // Running it in the remote context after going back should ensure that the
+ // navigation (and therefore the unload handler) has completed.
+ assert_equals(
+ await rc1.executeScript(() => localStorage.getItem('unload')), 'ran');
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-same-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-same-origin.window.js
new file mode 100644
index 0000000000..5a95455c4c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/unload-main-frame-same-origin.window.js
@@ -0,0 +1,33 @@
+// META: title=Unload runs in main frame when navigating same-origin.
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const rc1 = await rcHelper.addWindow();
+
+ t.add_cleanup(() => localStorage.removeItem('unload'));
+
+ // Initialize storage and add "unload" event handler.
+ await rc1.executeScript(() => {
+ localStorage.setItem('unload', 'not yet');
+ addEventListener('unload', () => {
+ localStorage.setItem('unload', 'ran');
+ });
+ });
+
+ // Navigate away.
+ const rc2 = await rc1.navigateToNew();
+
+ // Navigate back.
+ await rc2.historyBack();
+
+ // Test that the unload handler wrote to storage.
+ // Running it in the remote context after going back should ensure that the
+ // navigation (and therefore the unload handler) has completed.
+ assert_equals(
+ await rc1.executeScript(() => localStorage.getItem('unload')), 'ran');
+});
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child1.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child1.html
new file mode 100644
index 0000000000..22bb0b2980
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child1.html
@@ -0,0 +1,18 @@
+<body>
+ <a id="link" href="joint-session-history-child2.html">Child1</a>.
+ <iframe id="grandchild"></iframe>
+</body>
+<script>
+ window.onload = function() {
+ var link = document.getElementById("link");
+ var grandchild = document.getElementById("grandchild");
+ var timer = window.setInterval(poll, 100);
+ function poll() {
+ if (grandchild.getAttribute("data-grandchild-loaded")) {
+ window.clearInterval(timer);
+ link.click();
+ }
+ }
+ grandchild.src="joint-session-history-grandchild1.html";
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child2.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child2.html
new file mode 100644
index 0000000000..24b4695166
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-child2.html
@@ -0,0 +1,5 @@
+<body>Child 2.</body>
+<script>
+ // Servo doesn't support postMessage yet, so we poll on attributes.
+ window.frameElement.setAttribute("data-child-loaded", true);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-filler.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-filler.html
new file mode 100644
index 0000000000..b6d47c310b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-filler.html
@@ -0,0 +1 @@
+<body>Filler</body>
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild1.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild1.html
new file mode 100644
index 0000000000..d05e152425
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild1.html
@@ -0,0 +1,8 @@
+<body>
+ <a id="link" href="joint-session-history-grandchild2.html">Grandchild1</a>.
+</body>
+<script>
+ window.onload = function() {
+ document.getElementById("link").click();
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild2.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild2.html
new file mode 100644
index 0000000000..b5c81e1fca
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-grandchild2.html
@@ -0,0 +1,5 @@
+<body>Grandchild2.</body>
+<script>
+ // Servo doesn't support postMessage yet, so we poll on attributes.
+ window.frameElement.setAttribute("data-grandchild-loaded", true);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-iframe-state.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-iframe-state.html
new file mode 100644
index 0000000000..ffa64c0b35
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-iframe-state.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Joint session history should not override parent's state.</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<iframe id="frame" src="about:blank"></iframe>
+<script>
+async_test(function(t) {
+ // Setup.
+ var initialState = {foo: 'bar'};
+ var newStateFromChild = {foo: 'baz'};
+ var child = document.getElementById("frame");
+
+ // Child's initial state should be a default empty state.
+ assert_equals(child.contentWindow.history.state, null);
+
+ // Perform navigation in the top-level container to set the state.
+ window.history.pushState(initialState, 'title');
+
+ // Validate initial state was properly set.
+ assert_object_equals(window.history.state, initialState);
+
+ child.onload = t.step_func_done(() => {
+ // Child's initial state should be `null`.
+ assert_equals(child.contentWindow.history.state, null);
+
+ // Navigate in the child.
+ child.contentWindow.history.pushState(newStateFromChild, 'title');
+
+ // Child's state should now equal the new state.
+ assert_object_equals(child.contentWindow.history.state, newStateFromChild);
+ // Parent's state should still be preserved, having exactly the same state as it
+ // had before parent navigated.
+ assert_object_equals(window.history.state, initialState);
+ })
+ child.src = "joint-session-history-filler.html";
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-only-fully-active.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-only-fully-active.html
new file mode 100644
index 0000000000..c42d160a21
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-only-fully-active.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Do only fully active documents count for session history?</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <iframe id="child"></iframe>
+</body>
+<script>
+ async_test(function(t) {
+ var old_history_len = window.history.length;
+ var child = document.getElementById("child");
+ var timer = window.setInterval(t.step_func(poll), 100);
+ function poll() {
+ if (child.getAttribute("data-child-loaded")) {
+ // Check to see how many entries have been added to the session history.
+ // The spec https://html.spec.whatwg.org/multipage/#joint-session-history
+ // says that only fully active documents are included in the joint session history.
+ // If only fully active documents count, then the only fully active document
+ // is the child, with session length 1, so the joint session length change will be 1.
+ // If all documents count, then the grandchild is reachable via the session history,
+ // and it has session length 1, so the joint session length change will be 2.
+ assert_equals(2, window.history.length - old_history_len);
+ window.clearInterval(timer);
+ t.done();
+ }
+ }
+ child.src = "joint-session-history-child1.html";
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-remove-iframe.html b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-remove-iframe.html
new file mode 100644
index 0000000000..ee7aa368af
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/joint-session-history/joint-session-history-remove-iframe.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Joint session history length does not include entries from a removed iframe.</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<iframe id="frame" src="about:blank"></iframe>
+<script>
+async_test(function(t) {
+ t.step_timeout(() => {
+ var child = document.getElementById("frame");
+ var old_history_len = history.length;
+ child.onload = () => {
+ assert_equals(old_history_len + 1, history.length);
+ document.body.removeChild(document.getElementById("frame"));
+ assert_equals(old_history_len, history.length);
+ t.done();
+ }
+ child.src = "joint-session-history-filler.html";
+ }, 1000);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/001.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/001.html
new file mode 100644
index 0000000000..16cb834c78
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/001.html
@@ -0,0 +1,339 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState tests</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+//does not test for firing of popstate onload, because this was dropped from the specification on 25 March 2011
+//covers history.state after load, in accordance with the specification draft from 25 March 2011
+//history.state before load is tested in 006 and 007
+//does not test for structured cloning of FileList, File or Blob interfaces, as these require manual file selection
+
+//**This test assumes that assignments to location.hash will be synchronous - this is how all browsers implement it.
+//The spec (as of 25 March 2011) disagrees.**//
+
+var histlength, atstep = 0, lasttimer;
+setup({explicit_done:true}); //tests should take under 6 seconds + execution time
+
+window.onload = function () {
+ if( location.protocol == 'file:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'ERROR: This test cannot be run from file: (URL resolving will not work). It must be loaded over HTTP.';
+ return;
+ } else if( location.protocol == 'https:' ) {
+ document.getElementsByTagName('p')[0].innerHTML += '<br>WARNING: Browsers may intentionally fail to update history.length when pages are loaded over HTTPS, as a privacy restriction. If possible, load this page over HTTP.';
+ }
+ //use a timeout, because some browsers intentionally do not add history entries for URL changes in the onload thread
+ setTimeout(testinit,100);
+};
+function testinit() {
+ atstep = 1;
+ histlength = history.length;
+ iframe = document.getElementsByTagName('iframe')[0].src = 'blank2.html';
+ //reportload will now be called by the onload handler for the iframe
+}
+function reportload() {
+ var iframe = document.getElementsByTagName('iframe')[0], hashchng = false;
+ var canvassup = false, cloneobj;
+
+ function tests1() {
+ //Firefox may fail when reloading, because it recovers iframe state, and therefore does not see the need to alter history length
+ test(function () { assert_equals( history.length, histlength + 1, 'make sure that you loaded the test in a new tab/window' ); }, 'history.length should update when loading pages in an iframe');
+ histlength = history.length;
+ iframe.contentWindow.location.hash = 'test'; //should be synchronous **SEE COMMENT AT TOP OF FILE
+ test(function () {
+ assert_equals( history.length, histlength + 1, 'make sure that you loaded the test in a new tab/window' );
+ }, 'history.length should update when setting location.hash');
+ test(function () { assert_true( !!history.pushState, 'critical test; ignore any failures after this' ); }, 'history.pushState must exist'); //assert_own_property does not allow prototype inheritance
+ test(function () { assert_true( !!iframe.contentWindow.history.pushState, 'critical test; ignore any failures after this' ); }, 'history.pushState must exist within iframes');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state, null );
+ }, 'initial history.state should be null');
+ test(function () {
+ histlength = history.length;
+ iframe.contentWindow.history.pushState('','');
+ assert_equals( history.length, histlength + 1 );
+ }, 'history.length should update when pushing a state');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state, '' );
+ }, 'history.state should update after a state is pushed');
+ histlength = history.length;
+ iframe.contentWindow.addEventListener("popstate", tests2, {once: true});
+ history.back();
+ }
+ function tests2() {
+ test(function () {
+ assert_equals( history.length, histlength );
+ }, 'history.length should not decrease after going back');
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test' );
+ }, 'traversing history must traverse pushed states');
+ iframe.contentWindow.addEventListener("hashchange", tests3, {once: true});
+ history.go(-1);
+ }
+ function tests3() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), '', '(this could cause other failures later on)' );
+ }, 'traversing history must also traverse hash changes');
+
+ iframe.contentWindow.addEventListener("hashchange", tests4, {once: true});
+ //Safari 5.0.3 fails here - it navigates *this* document to the *iframe's* location, instead of just navigating the iframe
+ history.go(2);
+ }
+ async function tests4() {
+ test(function () {
+ //Firefox 4 beta 11 has a messed up error object, which does not have the right error type or .SECURITY_ERR property
+ assert_throws_dom('SECURITY_ERR',function () { history.pushState('','','//exa mple'); });
+ }, 'pushState must not be allowed to create invalid URLs');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',function () { history.pushState('','','http://www.example.com/'); });
+ }, 'pushState must not be allowed to create cross-origin URLs');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',function () { history.pushState('','','about:blank'); });
+ }, 'pushState must not be allowed to create cross-origin URLs (about:blank)');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',function () { history.pushState('','','data:text/html,'); });
+ }, 'pushState must not be allowed to create cross-origin URLs (data:URI)');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR', iframe.contentWindow.DOMException, function () { iframe.contentWindow.history.pushState('','','http://www.example.com/'); });
+ }, 'security errors are expected to be thrown in the context of the document that owns the history object');
+ let hashchange =
+ new Promise(function (resolve) {
+ iframe.contentWindow.addEventListener("hashchange", resolve, {once: true});
+ });
+
+ test(function () {
+ iframe.contentWindow.location.hash = 'test2';
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test2', 'location.hash did not change when told to' );
+ }, 'location.hash must be allowed to change (part 1)');
+ await hashchange;
+ history.go(-1);
+ iframe.contentWindow.addEventListener("hashchange", tests5, {once: true});
+ }
+ function tests5() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test', 'location.hash did not change when going back' );
+ }, 'location.hash must be allowed to change (part 2)');
+ test(function () {
+ iframe.contentWindow.history.pushState('','');
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test', 'location.hash changed when an unrelated state was pushed' );
+ }, 'pushState must not alter location.hash when no URL is provided');
+ history.go(1); //should do nothing, since the pushState should have removed the forward history
+ setTimeout(tests6,50); //.go is queued to end of thread
+ }
+ function tests6() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test' );
+ }, 'pushState must remove all history after the current state');
+ test(function () {
+ iframe.contentWindow.history.pushState('','','#test3');
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test3' );
+ }, 'pushState must be able to set location.hash');
+ //begin setup for "remove any tasks queued by the history traversal task source"
+ iframe.contentWindow.location.hash = '#test4';
+ iframe.contentWindow.history.go(-1); //must be queued
+ try {
+ //must remove the queued navigation in the same browsing context
+ iframe.contentWindow.history.pushState('','');
+ } catch(unsuperr) {}
+ //allow the browser to mistakenly run the .go if it is going to
+ //do not put two .go commands in the same thread, in case the browser mistakenly calculates the history position when
+ //calling .go instead of when executing the traversal task - that could give a false PASS in the next test otherwise
+ setTimeout(tests7,50);
+ }
+ function tests7() {
+ iframe.contentWindow.history.go(-1); //must be queued, but should not be removed this time
+ iframe.contentWindow.addEventListener("popstate", tests8, {once: true});
+ }
+ function tests8() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test4' );
+ }, 'pushState must remove any tasks queued by the history traversal task source');
+ //end "remove any tasks queued by the history traversal task source"
+ window.addEventListener('hashchange',function () { hashchng = true; },false);
+ try {
+ //push a state that changes the hash
+ iframe.contentWindow.history.pushState('','',iframe.contentWindow.location.pathname+'#test5');
+ } catch(unsuperr) {}
+ setTimeout(tests9,50); //allow the hashchange event to process, if the browser has mistakenly fired it
+ }
+ function tests9() {
+ test(function () {
+ assert_false( hashchng );
+ }, 'pushState must not fire hashchange events');
+ test(function () {
+ iframe.contentWindow.history.pushState('','','/testing_ignore_me_404');
+ assert_equals( iframe.contentWindow.location.pathname, '/testing_ignore_me_404' );
+ }, 'pushState must be able to set location.pathname');
+ test(function () {
+ var newURL = location.href.replace(/\/[^\/]*$/)+'/testing_ignore_me_404/';
+ iframe.contentWindow.history.pushState('','',newURL);
+ assert_equals( iframe.contentWindow.location.href, newURL );
+ }, 'pushState must be able to set absolute URLs to the same host');
+ test(function () {
+ assert_throws_dom( 'DATA_CLONE_ERR', function () {
+ history.pushState({dummy:function () {}},'');
+ } );
+ }, 'pushState must not be able to use a function as data');
+ test(function () {
+ assert_throws_dom( 'DATA_CLONE_ERR', function () {
+ history.pushState({dummy:window},'');
+ } );
+ }, 'pushState must not be able to use a DOM node as data');
+ test(function () {
+ try { a.b = c; } catch(errdata) {
+ history.pushState({dummy:errdata},'');
+ assert_equals(ReferenceError.prototype, Object.getPrototypeOf(history.state.dummy));
+ }
+ }, 'pushState must be able to use an error object as data');
+ test(function () {
+ assert_throws_dom('DATA_CLONE_ERR', iframe.contentWindow.DOMException, function () {
+ iframe.contentWindow.history.pushState(document,'');
+ });
+ }, 'security errors are expected to be thrown in the context of the document that owns the history object (2)');
+ cloneobj = {
+ nulldata: null,
+ udefdata: window.undefined,
+ booldata: true,
+ numdata: 1,
+ strdata: 'string data',
+ boolobj: new Boolean(true),
+ numobj: new Number(1),
+ strobj: new String('string data'),
+ datedata: new Date(),
+ regdata: /a/g,
+ arrdata: [1]
+ };
+ cloneobj.regdata.lastIndex = 1;
+ cloneobj.looped = cloneobj;
+ //test the ImageData type, if the browser supports it
+ var canvas = document.createElement('canvas');
+ if( canvas.getContext && ( canvas = canvas.getContext('2d') ) && canvas.createImageData ) {
+ canvassup = true;
+ cloneobj.imgdata = canvas.createImageData(1,1);
+ }
+ test(function () {
+ try {
+ iframe.contentWindow.history.pushState(cloneobj,'new title');
+ } catch(e) {
+ cloneobj.looped = null;
+ //try again because this object is needed for future tests
+ iframe.contentWindow.history.pushState(cloneobj,'new title');
+ //rethrow so the browser gets a FAIL for not coping with the circular reference; "internal structured cloning algorithm" step 1
+ throw(e);
+ }
+ }, 'pushState must be able to make structured clones of complex objects');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state && iframe.contentWindow.history.state.strdata, 'string data' );
+ }, 'history.state should also reference a clone of the original object');
+ test(function () {
+ assert_not_equals( cloneobj, iframe.contentWindow.history.state );
+ }, 'history.state should be a clone of the original object, not a reference to it');
+ /*
+ behaviour is not defined per spec, and no known implementations do this
+ test(function () {
+ assert_equals( iframe.contentDocument.title, 'new title', 'not required for specification conformance' );
+ }, 'pushState MIGHT set the document title');
+ */
+ history.go(-1);
+ iframe.contentWindow.addEventListener("popstate", tests10, {once: true});
+ }
+ function tests10() {
+ var eventtime = setTimeout(function () { tests11(false); },500); //should be cleared by the event handler long before it has a chance to fire
+ iframe.contentWindow.addEventListener('popstate',function (e) { clearTimeout(eventtime); tests11(true,e); },false);
+ history.forward();
+ }
+ function tests11(hasFired,ev) {
+ test(function () {
+ assert_true( hasFired );
+ }, 'popstate event should fire when navigation occurs');
+ test(function () {
+ assert_true( !!ev && typeof(ev.state) != 'undefined', 'state information was not passed' );
+ assert_true( !!ev.state, 'state information does not contain the expected value - browser is probably stuck in the wrong history position' );
+ assert_equals( ev.state.nulldata, null, 'state null data was not correct' );
+ assert_equals( ev.state.udefdata, window.undefined, 'state undefined data was not correct' );
+ assert_true( ev.state.booldata, 'state boolean data was not correct' );
+ assert_equals( ev.state.numdata, 1, 'state numeric data was not correct' );
+ assert_equals( ev.state.strdata, 'string data', 'state string data was not correct' );
+ assert_true( !!ev.state.datedata.getTime, 'state date data was not correct' );
+ assert_own_property( ev.state, 'regdata', 'state regex data was not correct' );
+ assert_equals( ev.state.regdata.source, 'a', 'state regex pattern data was not correct' );
+ assert_true( ev.state.regdata.global, 'state regex flag data was not correct' );
+ assert_equals( ev.state.regdata.lastIndex, 0, 'state regex lastIndex data was not correct' );
+ assert_equals( ev.state.arrdata.length, 1, 'state array data was not correct' );
+ assert_true( ev.state.boolobj.valueOf(), 'state boolean data was not correct' );
+ assert_equals( ev.state.numobj.valueOf(), 1, 'state numeric data was not correct' );
+ assert_equals( ev.state.strobj.valueOf(), 'string data', 'state string data was not correct' );
+ if( canvassup ) {
+ assert_equals( ev.state.imgdata.width, 1, 'state ImageData was not correct' );
+ }
+ }, 'popstate event should pass the state data');
+ test(function () {
+ assert_equals( ev.state.looped, ev.state );
+ }, 'state data should cope with circular object references');
+ test(function () {
+ assert_not_equals( cloneobj, ev.state );
+ }, 'state data should be a clone of the original object, not a reference to it');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state && iframe.contentWindow.history.state.strdata, 'string data' );
+ }, 'history.state should also reference a clone of the original object (2)');
+ test(function () {
+ assert_not_equals( cloneobj, iframe.contentWindow.history.state );
+ }, 'history.state should be a clone of the original object, not a reference to it (2)');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state, ev.state );
+ }, 'history.state should be identical to the object passed to the event handler unless history.state is updated');
+ try {
+ iframe.contentWindow.persistval = true;
+ iframe.contentWindow.history.pushState('','', location.href.replace(/\/[^\/]*$/,'/blank3.html') );
+ } catch(unsuperr) {}
+ //it's already cached, so this should be very fast if the browser mistakenly loads it
+ //it should not need to load at all, since it's just a pushed state
+ setTimeout(tests12,1000);
+ }
+ function tests12() {
+ test(function () {
+ assert_true( iframe.contentWindow.persistval && !iframe.contentWindow.forreal );
+ }, 'pushState should not actually load the new URL');
+ atstep = 3;
+ iframe.contentWindow.location.reload(); //load the real URL
+ lasttimer = setTimeout(function () { tests13(false); },3000); //should be cleared by the onload handler long before it has a chance to fire
+ }
+ function tests13(passed) {
+ test(function () {
+ assert_true( passed, 'expected a load event to fire when reloading the URL from cache, gave up waiting after 3 seconds' );
+ }, 'reloading a pushed state should actually load the new URL');
+ //try to make browsers behave when reloading so that the correct URL is recovered - does not always work
+ iframe.contentWindow.location.href = location.href.replace(/\/[^\/]*$/,'/blank.html');
+ done();
+ }
+
+ if( atstep == 1 ) {
+ //blank2 has loaded
+ atstep = 2;
+ //use a timeout, because some browsers intentionally do not add history entries for URL changes in an onload thread
+ setTimeout(tests1,100);
+ } else if( atstep == 3 ) {
+ //blank3 should now have loaded after the .reload() command
+ atstep = 4;
+ clearTimeout(lasttimer);
+ tests13(true);
+ }
+}
+
+
+
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>WARNING: This test should always be loaded in a new tab/window, to avoid browsers attempting to recover the state of frames, and history length. Do not reload the test.</p>
+ <div id="log">Running test...</div>
+ <p><iframe onload="reportload();" src="blank.html"></iframe></p>
+ <p><iframe src="blank.html"></iframe></p>
+ <p><iframe src="blank2.html"></iframe></p>
+ <p><iframe src="blank3.html"></iframe></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/002.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/002.html
new file mode 100644
index 0000000000..8bce7e72ff
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/002.html
@@ -0,0 +1,318 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.replaceState tests</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+//does not test for firing of popstate onload, because this was dropped from the specification on 25 March 2011
+//covers history.state after load, in accordance with the specification draft from 25 March 2011
+//history.state before load is tested in 006 and 007
+//does not test for structured cloning of FileList, File or Blob interfaces, as these require manual file selection
+
+//**This test assumes that assignments to location.hash will be synchronous - this is how all browsers implement it.
+//The spec (as of 25 March 2011) disagrees.
+
+var histlength, atstep = 0, lasttimer;
+setup({explicit_done:true}); //tests should take under 6 seconds + execution time
+
+window.onload = function () {
+ if( location.protocol == 'file:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'ERROR: This test cannot be run from file: (URL resolving will not work). It must be loaded over HTTP.';
+ return;
+ } else if( location.protocol == 'https:' ) {
+ document.getElementsByTagName('p')[0].innerHTML += '<br>WARNING: Browsers may intentionally fail to update history.length when pages are loaded over HTTPS, as a privacy restriction. If possible, load this page over HTTP.';
+ }
+ //use a timeout, because some browsers intentionally do not add history entries for URL changes in the onload thread
+ setTimeout(testinit,100);
+};
+function testinit() {
+ atstep = 1;
+ histlength = history.length;
+ iframe = document.getElementsByTagName('iframe')[0].src = 'blank2.html';
+ //reportload will now be called by the onload handler for the iframe
+}
+function reportload() {
+ var iframe = document.getElementsByTagName('iframe')[0], hashchng = false;
+ var canvassup = false, cloneobj;
+
+ async function tests1() {
+ test(function () { assert_equals( history.length, histlength + 1, 'make sure that you loaded the test in a new tab/window' ); }, 'history.length should update when loading pages in an iframe');
+ histlength = history.length;
+ let hashchange = new Promise(function(resolve) {
+ iframe.contentWindow.addEventListener("hashchange", resolve, {once: true});
+ });
+ iframe.contentWindow.location.hash = 'test'; //should be synchronous **SEE COMMENT AT TOP OF FILE
+ test(function () {
+ assert_equals( history.length, histlength + 1, 'make sure that you loaded the test in a new tab/window' );
+ }, 'history.length should update when setting location.hash');
+ test(function () { assert_true( !!history.replaceState, 'critical test; ignore any failures after this' ); }, 'history.replaceState must exist'); //assert_own_property does not allow prototype inheritance
+ test(function () { assert_true( !!iframe.contentWindow.history.replaceState, 'critical test; ignore any failures after this' ); }, 'history.replaceState must exist within iframes');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state, null );
+ }, 'initial history.state should be null');
+
+ await hashchange;
+ hashchange = new Promise(function(resolve) {
+ iframe.contentWindow.addEventListener("hashchange", resolve, {once: true});
+ });
+ iframe.contentWindow.location.hash = 'test2';
+ await hashchange;
+
+ iframe.contentWindow.addEventListener("hashchange", tests2, {once: true});
+ history.back();
+ }
+ function tests2() {
+ test(function () {
+ histlength = history.length;
+ iframe.contentWindow.history.replaceState('','');
+ assert_equals( history.length, histlength );
+ }, 'history.length should not update when replacing a state with no URL');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state, '' );
+ }, 'history.state should update after a state is pushed');
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test' );
+ }, 'hash should not change when replaceState is called without a URL');
+ test(function () {
+ histlength = history.length;
+ iframe.contentWindow.history.replaceState('','','#test3');
+ assert_equals( history.length, histlength );
+ }, 'history.length should not update when replacing a state with a URL');
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test3' );
+ }, 'hash should change when replaceState is called with a URL');
+ iframe.contentWindow.addEventListener("hashchange", tests3, {once: true});
+ history.go(-1);
+ }
+ function tests3() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), '' );
+ }, 'replaceState must replace the existing state and not add an extra one');
+ iframe.contentWindow.addEventListener("hashchange", tests4, {once: true});
+ history.go(2);
+ }
+ function tests4() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test2' );
+ }, 'replaceState must replace the existing state without altering the forward history');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',function () { history.replaceState('','','//exa mple'); });
+ }, 'replaceState must not be allowed to create invalid URLs');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',function () { history.replaceState('','','http://www.example.com/'); });
+ }, 'replaceState must not be allowed to create cross-origin URLs');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',function () { history.replaceState('','','about:blank'); });
+ }, 'replaceState must not be allowed to create cross-origin URLs (about:blank)');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',function () { history.replaceState('','','data:text/html,'); });
+ }, 'replaceState must not be allowed to create cross-origin URLs (data:URI)');
+ test(function () {
+ assert_throws_dom('SECURITY_ERR',iframe.contentWindow.DOMException,function () { iframe.contentWindow.history.replaceState('','','http://www.example.com/'); });
+ }, 'security errors are expected to be thrown in the context of the document that owns the history object');
+ test(function () {
+ //avoids browsers running .go synchronously when only a hash change is involved
+ iframe.contentWindow.history.replaceState('','','/testing_ignore_me_404#test4');
+ assert_equals( iframe.contentWindow.location.pathname, '/testing_ignore_me_404' );
+ }, 'replaceState must be able to set location.pathname');
+ test(function () {
+ var newURL = location.href.replace(/\/[^\/]*$/)+'/testing_ignore_me_404/';
+ iframe.contentWindow.history.replaceState('','',newURL);
+ assert_equals( iframe.contentWindow.location.href, newURL );
+ }, 'replaceState must be able to set absolute URLs to the same host');
+
+ //allow the browser to run the .go
+ iframe.contentWindow.addEventListener("popstate", tests5, {once: true});
+ //begin setup for "[must not] remove any tasks queued by the history traversal task source"
+ iframe.contentWindow.history.go(-1); //must be queued so the next command takes place *beforehand*
+ try {
+ //must not remove the queued navigation in the same browsing context
+ iframe.contentWindow.history.replaceState('','',iframe.contentWindow.location.pathname+'#test5');
+ } catch(unsuperr2) {}
+ }
+ function tests5() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test3' );
+ }, 'replaceState must not remove any tasks queued by the history traversal task source');
+ iframe.contentWindow.addEventListener("popstate", tests6, {once: true});
+ //Safari 5.0.3 fails here - it navigates *this* document to the *iframe's* location, instead of just navigating the iframe
+ history.go(1);
+ }
+ function tests6() {
+ test(function () {
+ assert_equals( iframe.contentWindow.location.hash.replace(/^#/,''), 'test5' );
+ }, '.go must queue a task with the history traversal task source (run asynchronously)');
+ //end "[must not] remove any tasks queued by the history traversal task source"
+ window.addEventListener('hashchange',function () { hashchng = true; },false);
+ try {
+ //push a state that changes the hash
+ iframe.contentWindow.history.replaceState('','',iframe.contentWindow.location.pathname+'#test6');
+ } catch(unsuperr) {}
+ setTimeout(tests7,50); //allow the hashchange event to process, if the browser has mistakenly fired it
+ }
+ function tests7() {
+ test(function () {
+ assert_false( hashchng );
+ }, 'replaceState must not fire hashchange events');
+ test(function () {
+ assert_throws_dom( 'DATA_CLONE_ERR', function () {
+ history.replaceState({dummy:function () {}},'');
+ } );
+ }, 'replaceState must not be able to use a function as data');
+ test(function () {
+ assert_throws_dom( 'DATA_CLONE_ERR', function () {
+ history.replaceState({dummy:window},'');
+ } );
+ }, 'replaceState must not be able to use a DOM node as data');
+ test(function () {
+ try { a.b = c; } catch(errdata) {
+ history.replaceState({dummy:errdata},'');
+ assert_equals(ReferenceError.prototype, Object.getPrototypeOf(history.state.dummy));
+ }
+ }, 'replaceState must be able to use an error object as data');
+ test(function () {
+ assert_throws_dom('DATA_CLONE_ERR', iframe.contentWindow.DOMException, function () {
+ iframe.contentWindow.history.replaceState(document,'');
+ });
+ }, 'security errors are expected to be thrown in the context of the document that owns the history object (2)');
+ cloneobj = {
+ nulldata: null,
+ udefdata: window.undefined,
+ booldata: true,
+ numdata: 1,
+ strdata: 'string data',
+ boolobj: new Boolean(true),
+ numobj: new Number(1),
+ strobj: new String('string data'),
+ datedata: new Date(),
+ regdata: /a/g,
+ arrdata: [1]
+ };
+ cloneobj.regdata.lastIndex = 1;
+ cloneobj.looped = cloneobj;
+ //test the ImageData type, if the browser supports it
+ var canvas = document.createElement('canvas');
+ if( canvas.getContext && ( canvas = canvas.getContext('2d') ) && canvas.createImageData ) {
+ canvassup = true;
+ cloneobj.imgdata = canvas.createImageData(1,1);
+ }
+ test(function () {
+ try {
+ iframe.contentWindow.history.replaceState(cloneobj,'new title');
+ } catch(e) {
+ cloneobj.looped = null;
+ //try again because this object is needed for future tests
+ iframe.contentWindow.history.replaceState(cloneobj,'new title');
+ //rethrow so the browser gets a FAIL for not coping with the circular reference; "internal structured cloning algorithm" step 1
+ throw(e);
+ }
+ }, 'replaceState must be able to make structured clones of complex objects');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state && iframe.contentWindow.history.state.strdata, 'string data' );
+ }, 'history.state should also reference a clone of the original object');
+ test(function () {
+ assert_not_equals( cloneobj, iframe.contentWindow.history.state );
+ }, 'history.state should be a clone of the original object, not a reference to it');
+ iframe.contentWindow.addEventListener("popstate", tests8, {once: true});
+ history.go(-1);
+ }
+ function tests8() {
+ var eventtime = setTimeout(function () { tests9(false); },500); //should be cleared by the event handler long before it has a chance to fire
+ iframe.contentWindow.addEventListener('popstate',function (e) { clearTimeout(eventtime); tests9(true,e); },false);
+ history.forward();
+ }
+ function tests9(hasFired,ev) {
+ test(function () {
+ assert_true( hasFired );
+ }, 'popstate event should fire when navigation occurs');
+ test(function () {
+ assert_true( !!ev && typeof(ev.state) != 'undefined', 'state information was not passed' );
+ assert_true( !!ev.state, 'state information does not contain the expected value - browser is probably stuck in the wrong history position' );
+ assert_equals( ev.state.nulldata, null, 'state null data was not correct' );
+ assert_equals( ev.state.udefdata, window.undefined, 'state undefined data was not correct' );
+ assert_true( ev.state.booldata, 'state boolean data was not correct' );
+ assert_equals( ev.state.numdata, 1, 'state numeric data was not correct' );
+ assert_equals( ev.state.strdata, 'string data', 'state string data was not correct' );
+ assert_true( !!ev.state.datedata.getTime, 'state date data was not correct' );
+ assert_own_property( ev.state, 'regdata', 'state regex data was not correct' );
+ assert_equals( ev.state.regdata.source, 'a', 'state regex pattern data was not correct' );
+ assert_true( ev.state.regdata.global, 'state regex flag data was not correct' );
+ assert_equals( ev.state.regdata.lastIndex, 0, 'state regex lastIndex data was not correct' );
+ assert_equals( ev.state.arrdata.length, 1, 'state array data was not correct' );
+ assert_true( ev.state.boolobj.valueOf(), 'state boolean data was not correct' );
+ assert_equals( ev.state.numobj.valueOf(), 1, 'state numeric data was not correct' );
+ assert_equals( ev.state.strobj.valueOf(), 'string data', 'state string data was not correct' );
+ if( canvassup ) {
+ assert_equals( ev.state.imgdata.width, 1, 'state ImageData was not correct' );
+ }
+ }, 'popstate event should pass the state data');
+ test(function () {
+ assert_equals( ev.state.looped, ev.state );
+ }, 'state data should cope with circular object references');
+ test(function () {
+ assert_not_equals( cloneobj, ev.state );
+ }, 'state data should be a clone of the original object, not a reference to it');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state && iframe.contentWindow.history.state.strdata, 'string data' );
+ }, 'history.state should also reference a clone of the original object (2)');
+ test(function () {
+ assert_not_equals( cloneobj, iframe.contentWindow.history.state );
+ }, 'history.state should be a clone of the original object, not a reference to it (2)');
+ test(function () {
+ assert_equals( iframe.contentWindow.history.state, ev.state );
+ }, 'history.state should be identical to the object passed to the event handler unless history.state is updated');
+ try {
+ iframe.contentWindow.persistval = true;
+ iframe.contentWindow.history.replaceState('','', location.href.replace(/\/[^\/]*$/,'/blank3.html') );
+ } catch(unsuperr) {}
+ //it's already cached, so this should be very fast if the browser mistakenly loads it
+ //it should not need to load at all, since it's just a pushed state
+ setTimeout(tests10,1000);
+ }
+ function tests10() {
+ test(function () {
+ assert_true( iframe.contentWindow.persistval && !iframe.contentWindow.forreal );
+ }, 'replaceState should not actually load the new URL');
+ atstep = 3;
+ iframe.contentWindow.location.reload(); //load the real URL
+ lasttimer = setTimeout(function () { tests11(false); },3000); //should be cleared by the onload handler long before it has a chance to fire
+ }
+ function tests11(passed) {
+ test(function () {
+ assert_true( passed, 'expected a load event to fire when reloading the URL from cache, gave up waiting after 3 seconds' );
+ }, 'reloading a replaced state should actually load the new URL');
+ //try to make browsers behave when reloading so that the correct URL is recovered - does not always work
+ iframe.contentWindow.location.href = location.href.replace(/\/[^\/]*$/,'/blank.html');
+ done();
+ }
+
+ if( atstep == 1 ) {
+ //blank2 has loaded
+ atstep = 2;
+ //use a timeout, because some browsers intentionally do not add history entries for URL changes in an onload thread
+ setTimeout(tests1,100);
+ } else if( atstep == 3 ) {
+ //blank3 should now have loaded after the .reload() command
+ atstep = 4;
+ clearTimeout(lasttimer);
+ tests11(true);
+ }
+}
+
+
+
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>WARNING: This test should always be loaded in a new tab/window, to avoid browsers attempting to recover the state of frames, and history length. Do not reload the test.</p>
+ <div id="log">Running test...</div>
+ <p><iframe onload="reportload();" src="blank.html"></iframe></p>
+ <p><iframe src="blank.html"></iframe></p>
+ <p><iframe src="blank2.html"></iframe></p>
+ <p><iframe src="blank3.html"></iframe></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/004.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/004.html
new file mode 100644
index 0000000000..e69889724f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/004.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Final history position for history.go should be calculated when executing the task</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+setup({explicit_done:true});
+window.onload = function () {
+ var hashcount = 0;
+ if( location.hash && location.hash != '#' ) {
+ location.href = location.href.replace(/#.*$/,'');
+ return;
+ }
+ setTimeout(add1,100);
+ function add1() {
+ location.hash = '#foo';
+ setTimeout(add2,100);
+ }
+ function add2() {
+ location.hash = '#bar';
+ setTimeout(add3,100);
+ }
+ function add3() {
+ location.hash = '#baz';
+ setTimeout(dojumps,100);
+ }
+ function dojumps() {
+ window.onhashchange = function () {
+ hashcount++;
+ };
+ history.go(-2);
+ test(function () {
+ //many browsers special-case jumps that only imply hash changes and will do them synchronously - the spec does allow this
+ assert_equals( hashcount, 0, 'hashchange fired even though the location should not have changed' );
+ assert_equals( location.hash.replace(/^#/,''), 'baz', 'the browser navigated synchronously' );
+ }, '.go commands should be queued until the thread has ended');
+ history.go(-1);
+ setTimeout(checkjumps,100);
+ }
+ function checkjumps() {
+ test(function () {
+ assert_true( !!hashcount, 'this testcase requires haschange support; the test cannot be used in this browser' );
+ }, 'browser needs to support hashchange events for this testcase');
+ test(function () {
+ assert_equals( hashcount, 2, 'the wrong number of queued commands were executed' );
+ }, 'queued .go commands should all be executed when the queue is processed');
+ test(function () {
+ assert_equals( location.hash.replace(/^#/,''), '' );
+ }, 'history position should be calculated when executing, not when calling the .go command');
+ done();
+ }
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/005.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/005.html
new file mode 100644
index 0000000000..2152e85a3e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/005.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Popstate event listener registration</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+
+//this test checks that onpopstate works on the body element
+
+var readyForPop = false, bodypop = false, inlinepop = false;
+setup({explicit_done:true});
+
+//use a timeout to avoid "popstate fires onload" from setting the variables too early
+setTimeout(step1,1000);
+function step1() {
+ readyForPop = true;
+ test(function () {
+ history.pushState('','');
+ history.pushState('','');
+ }, 'history.pushState support is needed for this testcase');
+ history.go(-1);
+ setTimeout(step2,50); //.go is queued to end of thread
+}
+function step2() {
+ test(function () {
+ assert_true( bodypop );
+ }, '<body onpopstate="..."> should register a listener for the popstate event');
+ window.onpopstate = function () { inlinepop = true; };
+ history.go(-1);
+ setTimeout(step3,50); //.go is queued to end of thread
+}
+function step3() {
+ test(function () {
+ assert_true( inlinepop );
+ }, 'window.onpopstate should register a listener for the popstate event');
+ done();
+}
+ </script>
+ </head>
+ <body onpopstate="if( readyForPop ) { bodypop = true; }">
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/006.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/006.html
new file mode 100644
index 0000000000..442b6f8f1e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/006.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Firing popstate after onload, even if there is no pushed/replaced state</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+
+//spec (25 March 2011 draft) states that popstate must not fire after onload unless there is a pushed/replaced state that is navigated
+var popfired = false;
+setup({explicit_done:true});
+window.addEventListener('popstate',function (e) { popfired = true; },false);
+test(function () {
+ assert_equals( history.state, null );
+}, 'history.state should initially be null');
+window.onload = function () {
+ test(function () {
+ assert_false( popfired );
+ }, 'popstate event should not fire before onload fires');
+ test(function () {
+ assert_equals( history.state, null );
+ }, 'history.state should still be null onload');
+ popfired = false;
+ setTimeout(function () {
+ test(function () {
+ assert_false( popfired );
+ }, 'popstate event should not fire after onload fires');
+ test(function () {
+ assert_equals( history.state, null );
+ }, 'history.state should still be null after onload');
+ test(function () {
+ var failed = false, realstate = history.state;
+ try {
+ history.state = '';
+ } catch(e) {
+ failed = e;
+ }
+ assert_equals(history.state,realstate,'property was read/write');
+ assert_false(failed);
+ }, 'writing to history.state should be silently ignored and not throw an error');
+ done();
+ },100);
+};
+
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/007.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/007.html
new file mode 100644
index 0000000000..decb197624
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/007.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Firing popstate after onload with pushed state</title>
+ <meta name=timeout content=long>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log">It looks like the browser stopped loading the page when encountering a .go(-1) command pointing to a pushed state. This will break the tests.</div>
+ <script type="text/javascript">
+
+//spec (25 March 2011 draft) states that popstate must fire before onload if there is a pushed/replaced state that is navigated
+var popfired = false;
+setup({explicit_done:true});
+test(function () {
+ assert_equals( history.state, null );
+}, 'history.state should initially be null');
+window.addEventListener('popstate',function (e) { popfired = e.state; },false);
+test(function () {
+ history.pushState('state1','');
+ history.pushState('state2','');
+}, 'history.pushState support is needed for this testcase');
+test(function () {
+ assert_equals( history.state, 'state2' );
+}, 'history.state should reflect pushed state');
+if( history.pushState ) { history.go(-1); }
+window.onload = function () {
+ test(function () {
+ assert_true( !!popfired );
+ }, 'popstate event should fire before onload fires');
+ test(function () {
+ assert_equals( popfired, 'state1' );
+ }, 'the correct state should be restored when navigating during initial load');
+ test(function () {
+ assert_equals( history.state, 'state1' );
+ }, 'history.state should reflect the navigated state onload');
+ popfired = false;
+ setTimeout(function () {
+ test(function () {
+ assert_false( !!popfired );
+ }, 'popstate event should not fire after onload fires');
+ test(function () {
+ assert_equals( history.state, 'state1' );
+ }, 'history.state should reflect the navigated state after onload');
+ done();
+ if( history.pushState ) { history.go(-1); } //go back to the start to avoid state recovery when reloading
+ },100);
+};
+
+ </script>
+
+ <!--
+ Reuse an existing server side script to slow down page load so that
+ history.go(-1); above gets run before load event fires.
+ -->
+ <script>
+ // define TEST_DELAY so that executing delay.py doesn't warn about use
+ // of undefined variable.
+ var TEST_DELAY;
+ </script>
+ <script src="/xhr/resources/delay.py?ms=2500"></script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/008.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/008.html
new file mode 100644
index 0000000000..c8071e3156
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/008.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+
+<!-- configure this test below to point to the script -->
+
+ <head>
+ <title>history.pushState/replaceState resolving</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <p></p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+
+/*
+Location of the script (which must be hosted on a separate domain from this test) containing the test code:
+var beforehref = location.href;
+test(function () {
+ history.pushState('','','/testing_ignore_me_404');
+ assert_equals(location.href,beforehref.replace(/^(\w*:\/\/[^\/]*\/)[\w\W]*$/,'$1testing_ignore_me_404'));
+}, 'history.pushState URL resolving should be done relative to the document, not the script');
+test(function () {
+ history.replaceState('','','/testing_ignore_me_404_2');
+ assert_equals(location.href,beforehref.replace(/^(\w*:\/\/[^\/]*\/)[\w\W]*$/,'$1testing_ignore_me_404_2'));
+}, 'history.replaceState URL resolving should be done relative to the document, not the script');
+*/
+var scriptlocation = 'http://www.' + location.host + location.pathname.split("/").slice(0,-1).join("/") + "/008.js";
+
+if( location.protocol == 'file:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'ERROR: This test cannot be run from file: (URL resolving will not work). It must be loaded over HTTP.';
+} else {
+ document.write('<script type="text\/javascript" src="'+scriptlocation+'"><\/script>');
+}
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/008.js b/testing/web-platform/tests/html/browsers/history/the-history-interface/008.js
new file mode 100644
index 0000000000..96a1fe5d4a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/008.js
@@ -0,0 +1,11 @@
+var beforehref = location.href;
+
+test(function () {
+ history.pushState('','','/testing_ignore_me_404');
+ assert_equals(location.href,beforehref.replace(/^(\w*:\/\/[^\/]*\/)[\w\W]*$/,'$1testing_ignore_me_404'));
+}, 'history.pushState URL resolving should be done relative to the document, not the script');
+
+test(function () {
+ history.replaceState('','','/testing_ignore_me_404_2');
+ assert_equals(location.href,beforehref.replace(/^(\w*:\/\/[^\/]*\/)[\w\W]*$/,'$1testing_ignore_me_404_2'));
+}, 'history.replaceState URL resolving should be done relative to the document, not the script');
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/009-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/009-1.html
new file mode 100644
index 0000000000..00b72e8ec6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/009-1.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers</title>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+window.onload = function () {
+ setTimeout(function () {
+ try { history.pushState('','','009-2.html?1234'); } catch(e) {}
+ location.href = '009-3.html?pipe=sub';
+ },10);
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/009-3.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/009-3.html
new file mode 100644
index 0000000000..e58b8fa5e7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/009-3.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers</title>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+var httpReferer = "{{headers[referer]}}";
+var lastUrl = location.href.replace(/\/[^\/]*$/,'\/009-2.html?1234');
+parent.test(function () {
+ parent.assert_equals( httpReferer, lastUrl );
+}, 'HTTP Referer should use the pushed state');
+parent.test(function () {
+ parent.assert_equals( document.referrer, lastUrl );
+}, 'document.referrer should use the pushed state');
+window.onload = function () {
+ setTimeout(function () {
+ try { history.pushState('','','009-4.html?2345'); } catch(e) {}
+ location.href = '009-5.html?pipe=sub';
+ },10);
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/009-5.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/009-5.html
new file mode 100644
index 0000000000..068a089af4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/009-5.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers</title>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+var httpReferer = unescape("{{headers[referer]}}");
+var lastUrl = location.href.replace(/\/[^\/]*$/,'\/009-4.html?2345');
+parent.test(function () {
+ parent.assert_equals( httpReferer, lastUrl );
+}, 'HTTP Referer should use the replaced state');
+parent.test(function () {
+ parent.assert_equals( document.referrer, lastUrl );
+}, 'document.referrer should use the replaced state');
+parent.done();
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/009.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/009.html
new file mode 100644
index 0000000000..c1ae0bbe03
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/009.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+setup({explicit_done:true});
+var iframe = document.createElement('iframe');
+window.onload = function () {
+ iframe.setAttribute('src','009-1.html');
+ document.body.appendChild(iframe)
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/010-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/010-1.html
new file mode 100644
index 0000000000..683397745c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/010-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers (before onload)</title>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+try { history.pushState('','','010-2.html?1234'); } catch(e) {}
+location.href = '010-3.html?pipe=sub';
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/010-3.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/010-3.html
new file mode 100644
index 0000000000..b80f56c3dd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/010-3.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers (before onload)</title>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+ var httpReferer = "{{headers[referer]}}";
+var lastUrl = location.href.replace(/\/[^\/]*$/,'\/010-2.html?1234');
+parent.test(function () {
+ parent.assert_equals( httpReferer, lastUrl );
+}, 'HTTP Referer should use the pushed state (before onload)');
+parent.test(function () {
+ parent.assert_equals( document.referrer, lastUrl );
+}, 'document.referrer should use the pushed state (before onload)');
+try { history.pushState('','','010-4.html?2345'); } catch(e) {}
+location.href = '010-5.html?pipe=sub';
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/010-5.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/010-5.html
new file mode 100644
index 0000000000..d150449eb2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/010-5.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers (before onload)</title>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+var httpReferer = "{{headers[referer]}}";
+var lastUrl = location.href.replace(/\/[^\/]*$/,'\/010-4.html?2345');
+parent.test(function () {
+ parent.assert_equals( httpReferer, lastUrl );
+}, 'HTTP Referer should use the replaced state (before onload)');
+parent.test(function () {
+ parent.assert_equals( document.referrer, lastUrl );
+}, 'document.referrer should use the replaced state (before onload)');
+parent.done();
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/010.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/010.html
new file mode 100644
index 0000000000..ca109a744b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/010.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState/replaceState and referer headers (before onload)</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+setup({explicit_done:true});
+var iframe = document.createElement('iframe');
+window.onload = function () {
+ iframe.setAttribute('src','010-1.html');
+ document.body.appendChild(iframe)
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/011.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/011.html
new file mode 100644
index 0000000000..4043aff7fd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/011.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.pushState before onload</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+var newUrl = location.href.replace(/\/[^\/]*$/,'\/011-1.html');
+setup({explicit_done:true});
+test(function () {
+ history.pushState('','','011-1.html');
+}, 'pushState should be able to set the location state');
+test(function () {
+ assert_equals( location.href, newUrl );
+}, 'pushed location should be reflected immediately');
+window.onload = function () {
+ setTimeout(function () {
+ test(function () {
+ assert_equals( location.href, newUrl );
+ }, 'pushed location should be retained after the page has loaded');
+ done();
+ },10);
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/012.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/012.html
new file mode 100644
index 0000000000..f5e6251671
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/012.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>history.replaceState before onload</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+var newUrl = location.href.replace(/\/[^\/]*$/,'\/011-1.html');
+setup({explicit_done:true});
+test(function () {
+ history.replaceState('','','011-1.html');
+}, 'replaceState should be able to set the location state');
+test(function () {
+ assert_equals( location.href, newUrl );
+}, 'replaced location should be reflected immediately');
+window.onload = function () {
+ setTimeout(function () {
+ test(function () {
+ assert_equals( location.href, newUrl );
+ }, 'replaced location should be retained after the page has loaded');
+ done();
+ },10);
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/blank-new.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank-new.html
new file mode 100644
index 0000000000..2a545af0ed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank-new.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<head>
+ <title>New page</title>
+</head>
+<body>This is a new page.</body>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/blank-old.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank-old.html
new file mode 100644
index 0000000000..a77c00fcc6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank-old.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<head>
+ <title>Old page</title>
+</head>
+<body>This is an old page.</body>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/blank.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank.html
new file mode 100644
index 0000000000..89c8724c09
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dummy page 1</title>
+ </head>
+ <body>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/blank2.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank2.html
new file mode 100644
index 0000000000..f79982e328
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank2.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dummy page 2</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+if( self == top || !parent.reportload ) {
+ document.write("<p>FAIL. Browser got confused when navigating forwards, and navigated the whole window to the iframe's location, instead of just navigating the iframe. It is not possible to run the testsuite.<\/p>");
+}
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/blank3.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank3.html
new file mode 100644
index 0000000000..2a8989f272
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/blank3.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dummy page 3</title>
+ <script type="text/javascript">
+var forreal = true;
+ </script>
+ </head>
+ <body>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_001.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_001.html
new file mode 100644
index 0000000000..21ba22f6fe
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_001.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>combination_history_001(Combine pushState and replaceSate methods.)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ assert_equals(history.state, 1, "first");
+
+ window.history.replaceState(2, document.title, '?x=1');
+ assert_equals(history.state, 2, "second")
+ }, "Combine pushState and replaceSate methods");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_002.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_002.html
new file mode 100644
index 0000000000..29e82f51be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_002.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>combination_history_002(After calling of pushState method, check length.)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var first;
+ var second;
+ first = window.history.length;
+ window.history.pushState(1, document.title, '?x=1');
+ second = window.history.length;
+
+ assert_equals(second - first, 1, "second - first");
+ }, "After calling of pushState method, check length");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_003.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_003.html
new file mode 100644
index 0000000000..7467d9b294
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_003.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>combination_history_003(After calling of pushState and replaceState methods, check length.)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var first;
+ var second;
+ var third;
+ first = window.history.length;
+ window.history.pushState(1, document.title, '?x=1');
+ second = window.history.length;
+ window.history.replaceState(2, document.title, '?x=2');
+ third = window.history.length;
+
+ assert_equals(second - first, 1, "second - first");
+ assert_equals(third, second, "third");
+ }, "After calling of pushState and replaceState methods, check length");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_004.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_004.html
new file mode 100644
index 0000000000..4e38b56205
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_004.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>combination_history_004(After calling of back method, check length.)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("After calling of back method, check length");
+
+ var last;
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+ last = window.history.length;
+
+ window.history.back();
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ assert_equals(e.state, 1, "state");
+ assert_equals(window.history.length, last, "last");
+ t.done();
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_005.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_005.html
new file mode 100644
index 0000000000..4487678015
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_005.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>combination_history_005(After calling of forward method, check length.)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("After calling of forward method, check length");
+
+ var last;
+ var fired = false;
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+ last = window.history.length;
+
+ window.history.back();
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ if(fired) {
+ assert_equals(e.state, 2, "state");
+ assert_equals(window.history.length, last, "last");
+ t.done();
+ }
+ fired = true;
+ window.history.forward();
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_006.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_006.html
new file mode 100644
index 0000000000..305f593c09
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_006.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>combination_history_006(After calling of go method, check length.)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("After calling of go method, check length");
+
+ var last;
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+
+ last = window.history.length;
+
+ window.history.go(-1);
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ assert_equals(e.state, 1, "state");
+ assert_equals(window.history.length, last, "last");
+ t.done();
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_007.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_007.html
new file mode 100644
index 0000000000..cec9ea0981
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/combination_history_007.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>combination_history_007(After calling of back and pushState method, check length.)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("After calling of back and pushState method, check length");
+
+ var last;
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+
+ last = window.history.length;
+
+ window.history.back();
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ assert_equals(e.state, 1, "state");
+ assert_equals(window.history.length, last, "last");
+ window.history.pushState(3, document.title, '?x=3');
+ assert_equals(window.history.length, last, "last");
+ t.done();
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history-associated-with-document.window.js b/testing/web-platform/tests/html/browsers/history/the-history-interface/history-associated-with-document.window.js
new file mode 100644
index 0000000000..94c1b2cf6b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history-associated-with-document.window.js
@@ -0,0 +1,6 @@
+// META: title=the History object must be associated with the Document object, not the Window object
+// META: script=/common/object-association.js
+
+// See https://github.com/whatwg/html/issues/2566.
+
+testIsPerDocument("history");
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history-state-after-bfcache.window.js b/testing/web-platform/tests/html/browsers/history/the-history-interface/history-state-after-bfcache.window.js
new file mode 100644
index 0000000000..5f04890a72
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history-state-after-bfcache.window.js
@@ -0,0 +1,43 @@
+// META: title=Navigating back to a bfcached page does not reset history.state
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+
+// See https://github.com/whatwg/html/issues/6652.
+
+'use strict';
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ // Open a window with noopener so that BFCache will work.
+ const rc = await rcHelper.addWindow(null, { features: "noopener" });
+
+ // Add a pageshow listener to stash the event, and set history.state using replaceState().
+ await rc.executeScript(() => {
+ window.addEventListener('pageshow', (event) => {
+ window.pageshowEvent = event;
+ });
+
+ history.replaceState({ foo: 'bar' }, '', '');
+ window.stashedHistoryState = history.state;
+ });
+
+ const rc2 = await rc.navigateToNew();
+ await rc2.historyBack();
+
+ assert_implements_optional(
+ await rc.executeScript(() => window.pageshowEvent.persisted),
+ 'precondition: document was bfcached'
+ );
+
+ assert_equals(
+ await rc.executeScript(() => history.state.foo),
+ 'bar',
+ 'history.state was restored correctly'
+ );
+
+ assert_true(
+ await rc.executeScript(() => window.stashedHistoryState === history.state),
+ 'history.state did not get serialized and deserialized');
+});
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history.js b/testing/web-platform/tests/html/browsers/history/the-history-interface/history.js
new file mode 100644
index 0000000000..bb5ee6dde0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history.js
@@ -0,0 +1,35 @@
+function parse_query() {
+ var query = location.search.slice(1);
+ var vars = query.split("&");
+ var fields = {};
+ vars.forEach(
+ function (x) {
+ var split = x.split("=");
+ return fields[split[0]] = split.slice(1).join("=");
+ });
+ return fields;
+}
+
+var query_parts = parse_query();
+var id = "id" in query_parts ? parseInt(query_parts.id) : 1;
+var urls_to_load = query_parts.urls.split(",");
+
+document.write(id);
+
+onunload = function() {};
+
+function queue_next() {
+ t = opener.t;
+ setTimeout(t.step_func(
+ function() {
+// opener.assert_equals(history.length, id);
+ if (urls_to_load[0]) {
+ var next_page = urls_to_load[0];
+ (next_page.indexOf("?") > -1) ? (next_page += "&") : (next_page += "?");
+ next_page += "urls=" + urls_to_load.slice(1).join(",");
+ next_page += "&id=" + ++id;
+ location = next_page;
+ }
+ }
+ ), 100);
+}
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back-1.html
new file mode 100644
index 0000000000..5487786fc0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {}
+
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.back();
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back.html
new file mode 100644
index 0000000000..042da4e61b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_back</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("history back");
+
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+
+ window.history.back();
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ assert_equals(e.state, 1, "history state");
+
+ t.done();
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_1.html
new file mode 100644
index 0000000000..0af3206643
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_1.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>history.back() with session history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 2)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [2, 1], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_back-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_cross_realm_method.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_cross_realm_method.html
new file mode 100644
index 0000000000..47937ef583
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_back_cross_realm_method.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>history.back() uses this's associated document's browsing context to determine if navigation is allowed</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/history.html#dom-history-back">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="sandboxedIframe" srcdoc="hello" sandbox="allow-scripts allow-same-origin"></iframe>
+<script>
+const t = async_test();
+
+t.step(() => {
+ history.pushState({}, null, "?prev");
+ history.pushState({}, null, "?current");
+
+ sandboxedIframe.contentWindow.history.back.call(history);
+});
+
+window.onpopstate = t.step_func_done(() => {
+ assert_equals(location.search, "?prev");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_entry.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_entry.html
new file mode 100644
index 0000000000..308371c57d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_entry.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onload = function() {
+ if (!opener.started) {
+ queue_next();
+ } else {
+ opener.pages.push(id);
+ }
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-1.html
new file mode 100644
index 0000000000..1bf7d6ee3b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-1.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {}
+
+ onload = function() {
+ if (!opener.started) {
+ queue_next();
+ } else {
+ opener.pages.push(id);
+ history.forward();
+ }
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-2.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-2.html
new file mode 100644
index 0000000000..cf8a8d20cd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward-2.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {}
+
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.go(-1);
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward.html
new file mode 100644
index 0000000000..6c37f25215
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_forward</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("history forward");
+
+ var fired = false;
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+
+ window.history.back();
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ if(fired) {
+ assert_equals(e.state, 2, "history state");
+
+ t.done();
+ }
+ fired = true;
+ window.history.forward();
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_1.html
new file mode 100644
index 0000000000..7817649f3a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_1.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>history.forward() with session history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 3)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [3, 2, 3], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_forward-1.html,history_forward-2.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_cross_realm_method.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_cross_realm_method.html
new file mode 100644
index 0000000000..7456099b8f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_forward_cross_realm_method.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>history.forward() uses this's associated document's browsing context to determine if navigation is allowed</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/history.html#dom-history-forward">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="sandboxedIframe" srcdoc="hello" sandbox="allow-scripts allow-same-origin"></iframe>
+<script>
+const t = async_test();
+
+t.step(() => {
+ history.pushState({}, null, "?prev");
+ history.pushState({}, null, "?current");
+ history.back();
+});
+
+let isCrossRealmForward = false;
+window.onpopstate = t.step_func(() => {
+ if (isCrossRealmForward) {
+ assert_equals(location.search, "?current");
+ t.done();
+ } else {
+ sandboxedIframe.contentWindow.history.forward.call(history);
+ isCrossRealmForward = true;
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_cross_realm_method.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_cross_realm_method.html
new file mode 100644
index 0000000000..d8852b9f6f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_cross_realm_method.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>history.go() uses this's associated document's browsing context to determine if navigation is allowed</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/history.html#dom-history-go">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="sandboxedIframe" srcdoc="hello" sandbox="allow-scripts allow-same-origin"></iframe>
+<script>
+const t = async_test();
+
+t.step(() => {
+ history.pushState({}, null, "?prev=2");
+ history.pushState({}, null, "?prev=1");
+ history.pushState({}, null, "?current");
+
+ sandboxedIframe.contentWindow.history.go.call(history, -2);
+});
+
+window.onpopstate = t.step_func_done(() => {
+ assert_equals(location.search, "?prev=2");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_minus.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_minus.html
new file mode 100644
index 0000000000..b8fe75573d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_minus.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_go_minus</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("history go minus");
+
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+
+ window.history.go(-1);
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ assert_equals(e.state, 1, "history state");
+
+ t.done();
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument-1.html
new file mode 100644
index 0000000000..6a2212f34b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument-1.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {}
+
+ onload = function() {
+ if (!opener.started) {
+ queue_next();
+ } else {
+ opener.pages.push(id);
+ if (!opener.gone) {
+ history.go();
+ opener.gone = true;
+ }
+ }
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument.html
new file mode 100644
index 0000000000..11f41614bd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_no_argument.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>history.go()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ gone = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 3)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [3, 2, 2], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_go_no_argument-1.html,history_forward-2.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_plus.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_plus.html
new file mode 100644
index 0000000000..74d4c588ca
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_plus.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_go_plus</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var t = async_test("history go plus");
+
+ var fired = false;
+ t.step(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ window.history.pushState(2, document.title, '?x=2');
+
+ window.history.back();
+ });
+
+ window.addEventListener('popstate', t.step_func(function(e) {
+ if(fired) {
+ assert_equals(e.state, 2, "history state");
+
+ t.done();
+ }
+ fired = true;
+ window.history.go(1);
+
+ }), false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri-1.html
new file mode 100644
index 0000000000..49249ff000
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri-1.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {}
+
+ onload = function() {
+ if (!opener.started) {
+ queue_next();
+ } else {
+ opener.pages.push(id);
+ if (!opener.gone) {
+ // This is meant to test that passing a string is not supported.
+ // According to the spec, the value passed to 'go' must be an int.
+ // Internet Explorer supports passing a string and will navigate
+ // to that Url. This test will protect against regressing in
+ // this area and reverting back to IE's incorrect behavior.
+ history.go("history_entry.html");
+ opener.gone = true;
+ }
+ }
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri.html
new file mode 100644
index 0000000000..e0a891a5a2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_to_uri.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>history.go() negative tests</title>
+<link rel="author" title="John Jansen" href="mailto:johnjan@microsoft.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#dom-history-go">
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ gone = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 3)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [3, 2, 2], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_go_to_uri-1.html,history_forward-2.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined-1.html
new file mode 100644
index 0000000000..1bf7d6ee3b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined-1.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {}
+
+ onload = function() {
+ if (!opener.started) {
+ queue_next();
+ } else {
+ opener.pages.push(id);
+ history.forward();
+ }
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined.html
new file mode 100644
index 0000000000..ddc0f588b5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_undefined.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>history.forward() with session history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 3)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [3, 2, 2], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_go_undefined-1.html,history_forward-2.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero-1.html
new file mode 100644
index 0000000000..17e520a810
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero-1.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {}
+
+ onload = function() {
+ if (!opener.started) {
+ queue_next();
+ } else {
+ opener.pages.push(id);
+ if (!opener.gone) {
+ history.go(0);
+ opener.gone = true;
+ }
+ }
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero.html
new file mode 100644
index 0000000000..b071b580c3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>history.go(0)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ gone = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 3)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [3, 2, 2], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_go_zero-1.html,history_forward-2.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero_which_document.window.js b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero_which_document.window.js
new file mode 100644
index 0000000000..f697783f3e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_go_zero_which_document.window.js
@@ -0,0 +1,27 @@
+// META: title=history.go(0) on an iframe must reload the iframe's document, not the parent document
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+
+promise_test(async () => {
+ const rcHelper = new RemoteContextHelper();
+ const main = await rcHelper.addWindow();
+ await main.addIframe();
+
+ await main.executeScript(() => {
+ window.didNotGetReloaded = true;
+
+ const iframe = document.querySelector("iframe");
+
+ // This goes beyond the original test case in https://github.com/whatwg/html/issues/2436, and
+ // tests where current realm != relevant realm. The spec says to use relevant realm so the
+ // result is still, iframe must reload, not parent.
+ History.prototype.go.call(iframe.contentWindow.history, 0);
+
+ return new Promise(resolve => {
+ iframe.addEventListener("load", resolve);
+ });
+ });
+
+ assert_true(await main.executeScript(() => window.didNotGetReloaded));
+});
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_properties_only_fully_active.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_properties_only_fully_active.html
new file mode 100644
index 0000000000..2b2c308526
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_properties_only_fully_active.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>history properties should throw SecurityError when not in a fully active Document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <iframe id="child"></iframe>
+</body>
+<script>
+ test(function(t) {
+ var ifr = document.getElementById("child");
+ var cached_history = ifr.contentWindow.history;
+ var cached_DOMException = ifr.contentWindow.DOMException;
+ ifr.remove();
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.length; });
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.scrollRestoration; });
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.state; });
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.go(0); });
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.back(); });
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.forward(); });
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.pushState(1, document.title, "?x=1"); });
+ assert_throws_dom("SecurityError", cached_DOMException, function() { cached_history.replaceState(2, document.title, "?x=2"); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate.html
new file mode 100644
index 0000000000..5180a3f6e5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_pushState</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ var state;
+ state = window.history.state;
+ assert_equals(state, 1, "history state");
+ }, "history pushState");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_err.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_err.html
new file mode 100644
index 0000000000..6fa0a8589d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_err.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_pushState SECURITY_ERR</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ assert_throws_dom("SecurityError", function () {
+ window.history.pushState(1, document.title, 'http://www.microsoft.com/test.html');
+ });
+ }, "history pushState SECURITY_ERR");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_nooptionalparam.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_nooptionalparam.html
new file mode 100644
index 0000000000..8e4b049a19
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_nooptionalparam.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_pushState_NoOptionalParam</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ window.history.pushState(1, document.title);
+
+ var state;
+ state = window.history.state;
+ assert_equals(state, 1, "history state");
+ }, "history pushState NoOptionalParam");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url.html
new file mode 100644
index 0000000000..cfbca35bfd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>History pushState sets the url</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+async_test(function(t) {
+ var oldLocation = window.location.toString();
+ window.history.pushState(null, "", "#hash");
+ assert_equals(oldLocation + "#hash", window.location.toString(), "pushState updates url");
+ history.back();
+ window.onhashchange = () => {
+ assert_equals(oldLocation, window.location.toString(), 'history traversal restores old url');
+ t.done();
+ };
+}, "history pushState sets url");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url_rewriting.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url_rewriting.html
new file mode 100644
index 0000000000..03c2afe8ea
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_pushstate_url_rewriting.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>URL rewriting allowed/disallowed for history.pushState()</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/6836">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+const baseWithUsernamePassword = new URL(location.href);
+baseWithUsernamePassword.username = "username";
+baseWithUsernamePassword.password = "password";
+
+const blobURL = URL.createObjectURL(new Blob(["foo"], { type: "text/html" }));
+const blobURL2 = URL.createObjectURL(new Blob(["bar"], { type: "text/html" }));
+
+const basicCases = [
+ [new URL("/common/blank.html", location.href), new URL("/common/blank.html#newhash", location.href), true],
+ [new URL("/common/blank.html", location.href), new URL("/common/blank.html?newsearch", location.href), true],
+ [new URL("/common/blank.html", location.href), new URL("/newpath", location.href), true],
+ [new URL("/common/blank.html", location.href), new URL("/common/blank.html", baseWithUsernamePassword), false],
+ [new URL("/common/blank.html", location.href), blobURL, false],
+ [new URL("/common/blank.html", location.href), "about:blank", false],
+ [new URL("/common/blank.html", location.href), "about:srcdoc", false],
+ [blobURL, blobURL, true],
+ [blobURL, blobURL + "#newhash", true],
+ [blobURL, blobURL + "?newsearch", false],
+ [blobURL, "blob:newpath", false],
+ [blobURL, "blob:" + self.origin + "/syntheticblob", false],
+ [blobURL, blobURL2, false],
+
+ // Note: these are cases where we create the iframe pointing at the initial URL,
+ // so its origin will actually be self.origin.
+ ["about:blank", "about:blank", true],
+ ["about:blank", "about:srcdoc", false],
+ ["about:blank", "about:blank?newsearch", false],
+ ["about:blank", "about:blank#newhash", true],
+ ["about:blank", self.origin + "/blank", false],
+
+ // javascript: URL navigation changes the URL to the creator document's URL, so these should all
+ // not work because you can't rewrite a HTTP(S) URL to a javascript: URL.
+ [new URL("/common/blank.html", location.href), "javascript:'foo'", false],
+ ["javascript:'foo'", "javascript:'foo'", false],
+ ["javascript:'foo'", "javascript:'foo'?newsearch", false],
+ ["javascript:'foo'", "javascript:'foo'#newhash", false],
+].map(([from, to, expectedToWork]) => [from.toString(), to.toString(), expectedToWork]);
+
+for (const [from, to, expectedToWork] of basicCases) {
+ // Otherwise the messages are not consistent between runs which breaks some systems.
+ const fromForTitle = from.replaceAll(blobURL, "blob:(a blob URL for this origin)")
+ .replaceAll(blobURL2, "blob:(another blob URL for this origin)");
+ const toForTitle = to.replaceAll(blobURL, "blob:(a blob URL for this origin)")
+ .replaceAll(blobURL2, "blob:(another blob URL for this origin)");
+
+ promise_test(async () => {
+ const iframe = document.createElement("iframe");
+ iframe.src = from;
+ const loadPromise = new Promise(r => iframe.onload = r);
+
+ document.body.append(iframe);
+ await loadPromise;
+
+ if (expectedToWork) {
+ iframe.contentWindow.history.pushState(null, "", to);
+ assert_equals(iframe.contentWindow.location.href, to);
+ } else {
+ assert_throws_dom("SecurityError", iframe.contentWindow.DOMException, () => {
+ iframe.contentWindow.history.pushState(null, "", to);
+ });
+ }
+ }, `${fromForTitle} to ${toForTitle} should ${expectedToWork ? "" : "not"} work`);
+}
+
+const srcdocCases = [
+ ["about:srcdoc", true],
+ ["about:srcdoc?newsearch", false],
+ ["about:srcdoc#newhash", true],
+ [self.origin + "/srcdoc", false]
+];
+
+for (const [to, expectedToWork] of srcdocCases) {
+ promise_test(async () => {
+ const iframe = document.createElement("iframe");
+ iframe.srcdoc = "foo";
+ const loadPromise = new Promise(r => iframe.onload = r);
+
+ document.body.append(iframe);
+ await loadPromise;
+
+ if (expectedToWork) {
+ iframe.contentWindow.history.pushState(null, "", to);
+ assert_equals(iframe.contentWindow.location.href, to);
+ } else {
+ assert_throws_dom("SecurityError", iframe.contentWindow.DOMException, () => {
+ iframe.contentWindow.history.pushState(null, "", to);
+ });
+ }
+ }, `about:srcdoc to ${to} should ${expectedToWork ? "" : "not"} work`);
+}
+
+// We need to test these separately since they're cross-origin.
+
+const sandboxedCases = [
+ [new URL("resources/url-rewriting-helper.html", location.href), new URL("resources/url-rewriting-helper.html", location.href), true],
+ [new URL("resources/url-rewriting-helper.html", location.href), new URL("resources/url-rewriting-helper.html#newhash", location.href), true],
+ [new URL("resources/url-rewriting-helper.html", location.href), new URL("resources/url-rewriting-helper.html?newsearch", location.href), true],
+ [new URL("resources/url-rewriting-helper.html", location.href), new URL("/newpath", location.href), true],
+ [new URL("resources/url-rewriting-helper.html", location.href), new URL("resources/url-rewriting-helper.html", baseWithUsernamePassword), false],
+].map(([from, to, expectedToWork]) => [from.toString(), to.toString(), expectedToWork]);
+
+for (const [from, to, expectedToWork] of sandboxedCases) {
+ promise_test(async () => {
+ const iframe = document.createElement("iframe");
+ iframe.src = from;
+ iframe.sandbox = "allow-scripts";
+ const loadPromise = new Promise(r => iframe.onload = r);
+
+ document.body.append(iframe);
+ await loadPromise;
+
+ const messagePromise = new Promise(r => window.addEventListener("message", r, { once: true }));
+ iframe.contentWindow.postMessage(to, "*");
+ const { data } = await messagePromise;
+
+ if (expectedToWork) {
+ assert_equals(data.result, "no exception");
+ assert_equals(data.locationHref, to);
+ } else {
+ assert_equals(data.result, "exception");
+ assert_equals(data.exceptionName, "SecurityError");
+ }
+ }, `sandboxed ${from} to ${to} should ${expectedToWork ? "" : "not"} work`);
+}
+
+fetch("resources/url-rewriting-helper.html").then(r => r.text()).then(htmlInside => {
+ const dataURLStart = "data:text/html;base64," + btoa(htmlInside);
+
+ const dataURLCases = [
+ [dataURLStart, dataURLStart, true],
+ [dataURLStart, dataURLStart + "#newhash", true],
+ [dataURLStart, dataURLStart + "?newsearch", false],
+ [dataURLStart, "data:newpath", false]
+ ];
+
+ for (const [from, to, expectedToWork] of dataURLCases) {
+ // Otherwise the messages are unreadably long.
+ const fromForTitle = from.replaceAll(dataURLStart, "data:(script to run this test)");
+ const toForTitle = to.replaceAll(dataURLStart, "data:(script to run this test)");
+
+ promise_test(async () => {
+ const iframe = document.createElement("iframe");
+ iframe.src = from;
+ const loadPromise = new Promise(r => iframe.onload = r);
+
+ document.body.append(iframe);
+ await loadPromise;
+
+ const messagePromise = new Promise(r => window.addEventListener("message", r, { once: true }));
+ iframe.contentWindow.postMessage(to, "*");
+ const { data } = await messagePromise;
+ if (expectedToWork) {
+ assert_equals(data.result, "no exception");
+ assert_equals(data.locationHref, to);
+ } else {
+ assert_equals(data.result, "exception");
+ assert_equals(data.exceptionName, "SecurityError");
+ }
+ }, `${fromForTitle} to ${toForTitle} should ${expectedToWork ? "" : "not"} work`);
+ }
+
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate.html
new file mode 100644
index 0000000000..794c2f3713
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_replaceState</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ window.history.replaceState(1, document.title, '?x=1');
+
+ var second;
+ second = window.history.state;
+ assert_equals(second, 1, "history state");
+ }, "history replaceState");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_err.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_err.html
new file mode 100644
index 0000000000..15d2181820
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_err.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_replaceState SECURITY_ERR</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ assert_throws_dom("SecurityError", function () {
+ window.history.replaceState(1, document.title, 'http://www.microsoft.com/test.html');
+ });
+ }, "history replaceState SECURITY_ERR");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_nooptionalparam.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_nooptionalparam.html
new file mode 100644
index 0000000000..838467d782
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_replacestate_nooptionalparam.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_replaceStateNoOptionalParam</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ window.history.replaceState(1, document.title);
+
+ var second;
+ second = window.history.state;
+ assert_equals(second, 1, "history state");
+ }, "history replaceStateNoOptionalParam");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_state.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_state.html
new file mode 100644
index 0000000000..2ee2356b1a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_state.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>history_state</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var first;
+ var second;
+
+ first = window.history.state;
+ window.history.pushState(1, document.title, '?x=1');
+
+ second = window.history.state;
+ assert_equals(first, null, "first");
+ assert_equals(second, 1, "second");
+ }, "history state");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/iframe_history_go_0.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/iframe_history_go_0.html
new file mode 100644
index 0000000000..f93f4c864e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/iframe_history_go_0.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<head>
+ <title>iframe_history_go_0</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<iframe></iframe>
+<script>
+promise_test(async (t) => {
+ let iframe = null;
+ const OLD_URL = 'blank-old.html';
+ const NEW_URL = 'blank-new.html';
+
+ await new Promise(resolve => {
+ iframe = document.createElement('iframe');
+ iframe.onload = () => resolve();
+ iframe.src = OLD_URL;
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+ });
+
+ assert_equals(iframe.contentDocument.body.textContent, 'This is an old page.\n');
+
+ await new Promise(resolve => {
+ iframe.onload = () => resolve();
+ iframe.src = NEW_URL;
+ });
+
+ assert_equals(iframe.contentDocument.body.textContent, 'This is a new page.\n');
+
+ await new Promise(resolve => {
+ iframe.onload = () => resolve();
+ iframe.contentWindow.history.go(0);
+ });
+
+ assert_equals(iframe.contentDocument.body.textContent, 'This is a new page.\n');
+}, 'iframe\'s history.go(0) performs a location.reload()');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001-1.html
new file mode 100644
index 0000000000..9aa5d30d16
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001-1.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<script>
+var o = opener;
+
+var frameloaded = null;
+
+o.t.step(function() {o.assert_equals(history.length, 1)});
+
+onload = function () {
+ o.t.step(function() {
+ o.assert_equals(history.length, 1);
+ o.t.done();
+ });
+
+ o.t1.step(function() {
+ var iframe = document.createElement("iframe");
+ iframe.src = "filler.html?id=2";
+ document.body.appendChild(iframe);
+ frameloaded = o.t1.step_func(function () {
+ o.assert_equals(history.length, 1);
+ setTimeout(o.t1.step_func(function () {
+ o.assert_equals(history.length, 1);
+ iframe.src = "filler.html?id=3";
+ frameloaded = o.t2.step_func(function() {
+ o.assert_equals(history.length, 2);
+ history.go(-1);
+ frameloaded = o.t3.step_func(function() {
+ o.assert_equals(history.length, 2);
+ var parts = iframe.contentWindow.location.href.split("/")
+ o.assert_equals(parts[parts.length - 1], "filler.html?id=2");
+ o.t3.done();
+ o.t4.step(function() {
+ var iframe0 = document.getElementsByTagName("iframe")[0];
+ iframe0.src = "filler.html?id=4"
+ frameloaded = o.t4.step_func(function() {
+ o.assert_equals(history.length, 2);
+ var parts = iframe0.contentWindow.location.href.split("/")
+ o.assert_equals(parts[parts.length - 1], "filler.html?id=4");
+ //This is the point at which gecko and webkit stop running tests
+ history.go(-1);
+ frameloaded = o.t5.step_func(function() {
+ o.assert_equals(history.length, 2);
+ var parts = iframe0.contentWindow.location.href.split("/")
+ o.assert_equals(parts[parts.length - 1], "filler.html?id=1");
+ var parts = iframe.contentWindow.location.href.split("/")
+ o.assert_equals(parts[parts.length - 1], "filler.html?id=2");
+ history.go(1);
+ frameloaded = o.t6.step_func(function() {
+ o.assert_equals(history.length, 2);
+ var parts = iframe0.contentWindow.location.href.split("/")
+ o.assert_equals(parts[parts.length - 1], "filler.html?id=4");
+ var parts = iframe.contentWindow.location.href.split("/")
+ o.assert_equals(parts[parts.length - 1], "filler.html?id=2");
+ o.t6.done();
+ });
+ o.t5.done();
+ });
+ o.t4.done();
+ });
+ });
+ });
+ o.t2.done();
+ });
+ o.t1.done();
+ }, 500))
+ });
+ });
+
+}
+</script>
+
+<iframe src="filler.html?id=1"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001.html
new file mode 100644
index 0000000000..c9d1c6416d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/001.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Joint session history with single iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+setup({timeout:10000});
+var t = async_test("Session history length on initial load");
+var t1 = async_test("Session history length on adding new iframe");
+var t2 = async_test("Navigating second iframe");
+var t3 = async_test("Traversing history back (1)");
+var t4 = async_test("Navigating first iframe");
+var t5 = async_test("Traversing history back (2)");
+var t6 = async_test("Traversing history forward");
+var w = window.open("001-1.html");
+//add_completion_callback(function() {w.close()});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002-1.html
new file mode 100644
index 0000000000..ed69d679da
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002-1.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<script>
+var o = opener;
+
+var frameloaded = null;
+
+o.t.step(function() {o.assert_equals(history.length, 1)});
+
+onload = function () {
+ o.t.step(function() {
+ o.assert_equals(history.length, 1);
+ o.t.done();
+ });
+
+ o.t1.step(function() {
+ var iframe = document.createElement("iframe");
+ iframe.src = "filler.html?id=2";
+ document.body.appendChild(iframe);
+ o.assert_equals(history.length, 1);
+ frameloaded = o.t2.step_func(function() {
+ iframe.contentDocument.open();
+ iframe.contentDocument.write("3<script>onpageshow = function() {alert('pageshow'); parent.frameloaded()}<\/script>");
+ iframe.contentDocument.close();
+ frameloaded = o.t2.step_func(function () {
+ o.assert_equals(history.length, 2);
+ o.t2.done();
+ });
+ });
+ o.t1.done();
+ });
+
+}
+</script>
+
+<iframe src="filler.html?id=1"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002.html
new file mode 100644
index 0000000000..b08c19e52d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/002.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Joint session history with single iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+setup({timeout:10000});
+var t = async_test("Session history length on initial load");
+var t1 = async_test("Session history length on adding new iframe");
+var t2 = async_test("Navigating second iframe");
+<!-- var t3 = async_test("Traversing history back (1)"); -->
+<!-- var t4 = async_test("Navigating first iframe"); -->
+<!-- var t5 = async_test("Traversing history back (2)"); -->
+<!-- var t6 = async_test("Traversing history forward"); -->
+var w = window.open("002-1.html");
+//add_completion_callback(function() {w.close()});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/filler.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/filler.html
new file mode 100644
index 0000000000..93e3c7ccfc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/joint_session_history/filler.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+document.write(location.search)
+onpageshow = function() {if (parent.frameloaded) {parent.frameloaded()}}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history.js b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history.js
new file mode 100644
index 0000000000..bb5ee6dde0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history.js
@@ -0,0 +1,35 @@
+function parse_query() {
+ var query = location.search.slice(1);
+ var vars = query.split("&");
+ var fields = {};
+ vars.forEach(
+ function (x) {
+ var split = x.split("=");
+ return fields[split[0]] = split.slice(1).join("=");
+ });
+ return fields;
+}
+
+var query_parts = parse_query();
+var id = "id" in query_parts ? parseInt(query_parts.id) : 1;
+var urls_to_load = query_parts.urls.split(",");
+
+document.write(id);
+
+onunload = function() {};
+
+function queue_next() {
+ t = opener.t;
+ setTimeout(t.step_func(
+ function() {
+// opener.assert_equals(history.length, id);
+ if (urls_to_load[0]) {
+ var next_page = urls_to_load[0];
+ (next_page.indexOf("?") > -1) ? (next_page += "&") : (next_page += "?");
+ next_page += "urls=" + urls_to_load.slice(1).join(",");
+ next_page += "&id=" + ++id;
+ location = next_page;
+ }
+ }
+ ), 100);
+}
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history_entry.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history_entry.html
new file mode 100644
index 0000000000..e5929ddbe8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/history_entry.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onload = function() {
+ if (!opener.started) {
+ queue_next();
+ } else {
+ opener.pages.push(id);
+ opener.start_test_wait();
+ }
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-1.html
new file mode 100644
index 0000000000..8c4401836a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-1.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onbeforeunload = function() {opener.beforeunload_ran = true; return "Opt to stay on the page"};
+
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.back();
+ }, 100);
+ }
+ }
+</script>
+<p>You should see/have seen a prompt asking if you want to leave the page.</p>
+<p>Opt to stay on the page</p>
+<button onclick="onbeforeunload = null; opener.start_test_wait(); document.getElementsByTagName('button')[0].disabled = true;">Click here</button>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html
new file mode 100644
index 0000000000..587cdfb124
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Traversing the history, prompt in before unload, navigation denied</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ setup({timeout:3600000});
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = null;
+ beforeunload_ran = false;
+ start_test_wait = t.step_func(
+ function() {
+ clearTimeout(timer);
+ timer = setTimeout(t.step_func(
+ function() {
+ try {
+ assert_true(beforeunload_ran, "beforeunload event handler ran");
+ assert_array_equals(pages, [2], "Pages opened during history navigation");
+ t.done();
+ } finally {
+ win.close();
+ }
+ }
+ ), 500);
+ }
+ );
+ t.step(function() {win = window.open("history_entry.html?urls=traverse_the_history_unload_prompt_1-1.html");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-1.html
new file mode 100644
index 0000000000..608a579e69
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-1.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onbeforeunload = function() {opener.beforeunload_ran = true; return "Opt to leave the page"};
+
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.back();
+ }, 100);
+ }
+ }
+</script>
+<p>You should see/have seen a prompt asking if you want to leave the page.</p>
+<p>Opt to leave the page</p>
+<p>If you weren't navigated away after opting to leave the page, that's a FAIL</p>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html
new file mode 100644
index 0000000000..94b66f8b55
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Traversing the history, prompt in before unload, navigation allowed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ setup({timeout:3600000});
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = null;
+ beforeunload_ran = false;
+ start_test_wait = t.step_func(
+ function() {
+ clearTimeout(timer);
+ timer = setTimeout(t.step_func(
+ function() {
+ try {
+ assert_true(beforeunload_ran, "beforeunload event handler ran");
+ assert_array_equals(pages, [2,1], "Pages opened during history navigation");
+ t.done();
+ } finally {
+ win.close();
+ }
+ }
+ ), 500);
+ }
+ );
+ t.step(function() {win = window.open("history_entry.html?urls=traverse_the_history_unload_prompt_2-1.html");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-1.html
new file mode 100644
index 0000000000..c0079b6bec
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-1.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function(e) {opener.unload_ran = true; return "Now refuse to leave the current page"}
+
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.back();
+ }, 100);
+ }
+ } else {
+ opener.start_test_wait();
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html
new file mode 100644
index 0000000000..7f96a39240
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>Traversing the history, unload event is fired on doucment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = null;
+ unload_ran = false;
+ start_test_wait = t.step_func(
+ function() {
+ clearTimeout(timer);
+ timer = setTimeout(t.step_func(
+ function() {
+ try {
+ assert_array_equals(pages, [2], "Pages opened during history navigation");
+ assert_true(unload_ran, "Unload event handler ran");
+ t.done();
+ } finally {
+ // win.close();
+ }
+ }
+ ), 500);
+ }
+ );
+ t.step(function() {win = window.open("history_entry.html?urls=traverse_the_history_unload_prompt_1-1.html");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-base.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-base.html
new file mode 100644
index 0000000000..ea95d812d7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-base.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>history.pushState() with an empty string URL and base URL different from document's URL</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9343">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<base href="/the-base">
+
+<script>
+"use strict";
+
+test(() => {
+ const before = location.pathname;
+
+ history.pushState(null, null, "");
+ assert_equals(location.pathname, before);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-whitespace.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-whitespace.html
new file mode 100644
index 0000000000..72d9be6a9c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate-whitespace.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>history.pushState() with a whitespace URL</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9343">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ location.hash = "test";
+
+ const before = location.pathname;
+
+ history.pushState(null, null, " ");
+ assert_equals(location.pathname, before, "pathname");
+ assert_equals(location.hash, "", "hash");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate.html
new file mode 100644
index 0000000000..1953709269
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/pushstate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>history.pushState() with an empty string URL</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9343">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ location.hash = "test";
+
+ history.pushState(null, null, "");
+ assert_equals(location.hash, "#test");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-base.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-base.html
new file mode 100644
index 0000000000..224e928de6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-base.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>history.replaceState() with an empty string URL and base URL different from document's URL</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9343">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<base href="/the-base">
+
+<script>
+"use strict";
+
+test(() => {
+ const before = location.pathname;
+
+ history.replaceState(null, null, "");
+ assert_equals(location.pathname, before);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-whitespace.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-whitespace.html
new file mode 100644
index 0000000000..7261cdf3fa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate-whitespace.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>history.replaceState() with a whitespace URL</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9343">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ location.hash = "test";
+
+ const before = location.pathname;
+
+ history.replaceState(null, null, " ");
+ assert_equals(location.pathname, before, "pathname");
+ assert_equals(location.hash, "", "hash");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate.html
new file mode 100644
index 0000000000..66b4cc6f82
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/pushstate-replacestate-empty-string/replacestate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>history.replaceState() with an empty string URL</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9343">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ location.hash = "test";
+
+ history.replaceState(null, null, "");
+ assert_equals(location.hash, "#test");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/message-opener.sub.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/message-opener.sub.html
new file mode 100644
index 0000000000..441c08c0ea
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/message-opener.sub.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script>
+"use strict";
+
+opener.postMessage("{{GET[id]}}", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-beforeunload.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-beforeunload.html
new file mode 100644
index 0000000000..53a4a1886e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-beforeunload.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script>
+"use strict";
+
+window.addEventListener("beforeunload", () => {
+ history.back();
+});
+
+location.href = "message-opener.sub.html?id=destination";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-unload.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-unload.html
new file mode 100644
index 0000000000..d5ffb7abae
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/traverse-during-unload.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script>
+"use strict";
+
+window.addEventListener("unload", () => {
+ history.back();
+});
+
+location.href = "message-opener.sub.html?id=destination";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/url-rewriting-helper.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/url-rewriting-helper.html
new file mode 100644
index 0000000000..847d16cd1a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/resources/url-rewriting-helper.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ window.onmessage = ({ data }) => {
+ try {
+ history.pushState(null, "", data);
+ } catch (e) {
+ parent.postMessage({ result: "exception", exceptionName: e.name }, "*");
+ return;
+ }
+ parent.postMessage({ result: "no exception", locationHref: location.href }, "*");
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-beforeunload.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-beforeunload.html
new file mode 100644
index 0000000000..cb8cdca7ff
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-beforeunload.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Traversing the history during beforeunload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const w = window.open("resources/message-opener.sub.html?id=start");
+ t.add_cleanup(() => w.close());
+
+ const messages = [];
+ window.addEventListener("message", t.step_func(({ data }) => {
+ messages.push(data);
+
+ if (messages.length === 1) {
+ assert_array_equals(messages, ["start"]);
+ w.location.href = "resources/traverse-during-beforeunload.html";
+ } else if (messages.length === 2) {
+ assert_array_equals(messages, ["start", "destination"]);
+ t.done();
+ }
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-unload.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-unload.html
new file mode 100644
index 0000000000..6f6e984402
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse-during-unload.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Traversing the history during unload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const w = window.open("resources/message-opener.sub.html?id=start");
+ t.add_cleanup(() => w.close());
+
+ const messages = [];
+ window.addEventListener("message", t.step_func(({ data }) => {
+ messages.push(data);
+
+ if (messages.length === 1) {
+ assert_array_equals(messages, ["start"]);
+ w.location.href = "resources/traverse-during-unload.html";
+ } else if (messages.length === 2) {
+ assert_array_equals(messages, ["start", "destination"]);
+ t.done();
+ }
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1-1.html
new file mode 100644
index 0000000000..13e89e7bc0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+4
+<script>
+ onunload = function() {}
+
+ opener.pages.push(4);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.go(-2);
+ history.go(-1);
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1.html
new file mode 100644
index 0000000000..9b59bb0587
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_1.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Multiple history traversals from the same task</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = null;
+ start_test_wait = t.step_func(
+ function() {
+ clearTimeout(timer);
+ timer = setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [4, 2], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }
+ );
+ t.step(function() {win = window.open("history_entry.html?urls=history_entry.html,history_entry.html,traverse_the_history_1-1.html");
+ t.add_cleanup(() => { win.close() });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2-1.html
new file mode 100644
index 0000000000..fecf060eb7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.go(-3);
+ history.go(-2);
+ history.go(1);
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2.html
new file mode 100644
index 0000000000..429a97d835
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_2.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Multiple history traversals, last would be aborted</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 2)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [6, 3], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_entry.html,history_entry.html,history_entry.html,history_entry.html,traverse_the_history_2-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3-1.html
new file mode 100644
index 0000000000..e7836fedbe
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.go(-2);
+ history.go(-1);
+ history.go(3);
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3.html
new file mode 100644
index 0000000000..429a97d835
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_3.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Multiple history traversals, last would be aborted</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 2)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [6, 3], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_entry.html,history_entry.html,history_entry.html,history_entry.html,traverse_the_history_2-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4-1.html
new file mode 100644
index 0000000000..9211da1172
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.go(-10); //Outside the range
+ history.go(-1);
+ history.go(-2);
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4.html
new file mode 100644
index 0000000000..136093a2c1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_4.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Multiple history traversals, last would be aborted</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 2)
+ return;
+ clearTimeout(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [6, 5], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_entry.html,history_entry.html,history_entry.html,history_entry.html,traverse_the_history_4-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5-1.html
new file mode 100644
index 0000000000..6a28a85683
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.go(10); //Outside the range
+ history.go(-1);
+ history.go(-2);
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5.html
new file mode 100644
index 0000000000..4d3d643425
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_5.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Multiple history traversals, last would be aborted</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 2)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [6, 5], "Pages opened during history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_entry.html,history_entry.html,history_entry.html,history_entry.html,traverse_the_history_5-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1-1.html
new file mode 100644
index 0000000000..b9d8a0c694
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+ onunload = function() {opener.unload_ran = true;}
+
+ opener.pages.push(id);
+ if (!opener.started) {
+ onload = function() {
+ setTimeout(function() {
+ opener.started = true;
+ history.back();
+ }, 100);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1.html
new file mode 100644
index 0000000000..4ef0ea583d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_unload_1.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Traversing the history, unload event is fired on doucment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ unload_ran = false;
+ timer = setInterval(
+ function() {
+ if (pages.length < 2 || !unload_ran)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ assert_array_equals(pages, [2, 1], "Pages opened during history navigation");
+ assert_true(unload_ran, "Unload event handler ran");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=traverse_the_history_unload_1-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1-1.html
new file mode 100644
index 0000000000..945c8d81f8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1-1.html
@@ -0,0 +1,12 @@
+<!doctype html>
+2
+<script>
+ onunload = function() {}
+ opener.pages.push(2);
+ onload = function() {
+ setTimeout(function() {
+ document.write("<!doctype html>3<script>opener.pages.push(3); if(!opener.started) {opener.started = true; history.go(-1);}<\/script>");
+ document.close();
+ }, 100);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1.html
new file mode 100644
index 0000000000..404d61d0cf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_1.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>Traverse the history after document.write after the load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ start_test_wait = t.step_func(
+ function() {
+ check_result = t.step_func(
+ function() {
+ if (pages.length < 3) {
+ setTimeout(check_result, 500);
+ return
+ }
+ assert_array_equals(pages, [2, 3, 1], "Pages opened during history navigation");
+ t.done();
+ }
+ )
+ setTimeout(check_result, 500);
+ }
+ );
+ t.step(function() {
+ win = window.open("history_entry.html?urls=traverse_the_history_write_after_load_1-1.html");
+ t.add_cleanup(function() {win.close()});
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2-1.html
new file mode 100644
index 0000000000..922e1832a6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2-1.html
@@ -0,0 +1,9 @@
+<!doctype html>
+3
+<script>
+ onunload = function() {}
+ opener.pages.push(3);
+ onload = function() {
+ document.write("<!doctype html>4<script>opener.pages.push(4); if(!opener.started) {opener.started = true; history.go(-2);}<\/script>");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2.html
new file mode 100644
index 0000000000..7372e9f4ae
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_after_load_2.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Traverse the history back and forward when a history entry is written after the load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 5)
+ return;
+ setTimeout(t.step_func(function() {
+ //The pass condition here is based on the idea that the spec is wrong and browsers are right
+ assert_array_equals(pages, [3, 4, 2, 3, 4], "Pages opened during history navigation");
+ t.done();
+ }), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_forward-1.html,traverse_the_history_write_onload_2-1.html");
+ t.add_cleanup(function() {win.close()});
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1-1.html
new file mode 100644
index 0000000000..70d946c818
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1-1.html
@@ -0,0 +1,9 @@
+<!doctype html>
+2
+<script>
+ onunload = function() {}
+ opener.pages.push(2);
+ onload = function() {
+ document.write("<!doctype html>3<script>opener.pages.push(3); if(!opener.started) {opener.started = true; history.go(-1);}<\/script>");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1.html
new file mode 100644
index 0000000000..febb4334da
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_1.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>Traverse the history when a history entry is written in the load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 3)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ //The pass condition here is based on the idea that the spec is wrong and browsers are right
+ assert_array_equals(pages, [2, 3, 1], "Pages opened durning history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=traverse_the_history_write_onload_1-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2-1.html
new file mode 100644
index 0000000000..2716514109
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2-1.html
@@ -0,0 +1,9 @@
+<!doctype html>
+3
+<script>
+ onunload = function() {}
+ opener.pages.push(3);
+ onload = function() {
+ document.write("<!doctype html>4<script>opener.pages.push(4); if(!opener.started) {opener.started = true; history.go(-1);}<\/script>");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2.html
new file mode 100644
index 0000000000..aa97d22c02
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-history-interface/traverse_the_history_write_onload_2.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>Traverse the history back and forward when a history entry is written in the load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ started = false;
+ pages = []
+ timer = setInterval(
+ function() {
+ if (pages.length < 5)
+ return;
+ clearInterval(timer);
+ setTimeout(t.step_func(
+ function() {
+ //The pass condition here is based on the idea that the spec is wrong and browsers are right
+ assert_array_equals(pages, [3, 4, 2, 3, 4], "Pages opened durning history navigation");
+ t.done();
+ }
+ ), 500);
+ }, 50);
+ t.step(function() {
+ win = window.open("history_entry.html?urls=history_forward-1.html,traverse_the_history_write_onload_2-1.html");
+ t.add_cleanup(function() { win.close(); });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html
new file mode 100644
index 0000000000..f72ed1eaf2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html
@@ -0,0 +1,197 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+
+ <title>Location objects' custom [[GetPrototypeOf]] trap permit [[Prototype]] chain cycles to be created through them</title>
+
+ <link rel="author" title="Jeff Walden" href="http://whereswalden.com/" />
+ <link rel="help" href="https://tc39.github.io/ecma262/#sec-ordinarysetprototypeof" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#location-getprototypeof" />
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<hr />
+
+<iframe id="same-origin-different-window"></iframe>
+<iframe id="cross-origin-joined-via-document-domain"></iframe>
+
+<script>
+"use strict";
+
+// Handle same-origin, same-window testing first, before any async-requiring
+// testing.
+test(function() {
+ var LocationPrototype = Location.prototype;
+ var ObjectPrototype = Object.prototype;
+
+ var loc = window.location;
+
+ var locProto = Object.getPrototypeOf(loc);
+ assert_equals(locProto, LocationPrototype,
+ "loc's initial [[Prototype]]");
+
+ var originalLocProtoProto = Object.getPrototypeOf(locProto);
+ assert_equals(originalLocProtoProto, ObjectPrototype,
+ "Location.prototype's initial [[Prototype]]");
+
+ Object.setPrototypeOf(locProto, loc);
+
+ assert_equals(Object.getPrototypeOf(locProto), loc,
+ "LocationPrototype's new [[Prototype]]");
+ assert_equals(Object.getPrototypeOf(loc), locProto,
+ "loc's new [[Prototype]]");
+
+ // Reset so as not to muck with testharness.js expectations.
+ Object.setPrototypeOf(locProto, originalLocProtoProto);
+}, "same-origin, same-window location cycle");
+
+var pathdir =
+ location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1);
+
+var triggerCrossOriginTest = (function() {
+ var crossOrigin =
+ document.getElementById("cross-origin-joined-via-document-domain");
+
+ var t = async_test("cross-origin location has null prototype");
+
+ return new Promise(function(resolve, reject) {
+ crossOrigin.onload = t.step_func_done(function(e) {
+ try {
+ var win = crossOrigin.contentWindow;
+
+ var loc = win.location;
+
+ // Between un-opted-in windows, location objects appear to have null
+ // [[Prototype]].
+ assert_equals(Object.getPrototypeOf(loc), null,
+ "cross-origin unjoined location's [[Prototype]");
+
+ resolve();
+ } catch (e) {
+ reject(e);
+ throw e;
+ }
+ });
+
+ crossOrigin.src =
+ "//{{domains[www]}}:" + location.port + pathdir + "cross_origin_joined_frame.sub.html";
+ })
+ .catch(t.unreached_func("crossOrigin onload/src setting"));
+})();
+
+var triggerSameOriginTest = (function() {
+ var sameOriginDifferentWindow =
+ document.getElementById("same-origin-different-window");
+
+ var t = async_test("same-origin, different-window location cycle");
+
+ return new Promise(function(resolve, reject) {
+ sameOriginDifferentWindow.onload = t.step_func_done(function() {
+ try {
+ var win = sameOriginDifferentWindow.contentWindow;
+
+ var loc = win.location;
+ var LocationPrototype = win.Location.prototype;
+ var ObjectPrototype = win.Object.prototype;
+
+ var locProto = Object.getPrototypeOf(loc);
+ assert_equals(locProto, LocationPrototype,
+ "loc's initial [[Prototype]]");
+
+ var originalLocProtoProto = Object.getPrototypeOf(locProto);
+ assert_equals(originalLocProtoProto, ObjectPrototype,
+ "Location.prototype's initial [[Prototype]]");
+
+ Object.setPrototypeOf(locProto, loc);
+
+ assert_equals(Object.getPrototypeOf(locProto), loc,
+ "LocationPrototype's new [[Prototype]]");
+ assert_equals(Object.getPrototypeOf(loc), locProto,
+ "loc's new [[Prototype]]");
+
+ // Reset so as not to muck with testharness.js expectations.
+ Object.setPrototypeOf(locProto, originalLocProtoProto);
+
+ resolve();
+ } catch (e) {
+ reject(e);
+ throw e;
+ }
+ });
+
+ sameOriginDifferentWindow.src = "same_origin_frame.html";
+ })
+ .catch(t.unreached_func("sameOriginDifferentWindow onload/src setting"));
+})();
+
+function crossOriginJoinTest() {
+ var win =
+ document.getElementById("cross-origin-joined-via-document-domain")
+ .contentWindow;
+
+ assert_equals(document.domain, "{{host}}");
+
+ var loc = win.location;
+
+ var threw = false;
+ try {
+ // Still cross-origin until the document.domain set below.
+ win.Location;
+ } catch (e) {
+ threw = true;
+ }
+
+ assert_equals(threw, true,
+ "accessing win.Location before joining win's origin");
+
+ // Join with other frames that have set |document.domain| to this same
+ // value -- namely, this cross-origin frame. Now access between the two
+ // windows should be permitted.
+ assert_equals(document.domain, "{{host}}",
+ "initial document.domain sanity check");
+ document.domain = "{{host}}";
+
+ var LocationPrototype = win.Location.prototype;
+ var ObjectPrototype = win.Object.prototype;
+
+ var locProto = Object.getPrototypeOf(loc);
+ assert_equals(locProto, LocationPrototype,
+ "loc's initial [[Prototype]]");
+
+ var originalLocProtoProto = Object.getPrototypeOf(locProto);
+ assert_equals(originalLocProtoProto, ObjectPrototype,
+ "Location.prototype's initial [[Prototype]]");
+
+ Object.setPrototypeOf(locProto, loc);
+
+ assert_equals(Object.getPrototypeOf(locProto), loc,
+ "LocationPrototype's new [[Prototype]]");
+ assert_equals(Object.getPrototypeOf(loc), locProto,
+ "loc's new [[Prototype]]");
+
+ // Reset so as not to muck with testharness.js expectations.
+ Object.setPrototypeOf(locProto, originalLocProtoProto);
+}
+
+function run() {
+ var t =
+ async_test("cross-origin, but joined via document.domain, location cycle");
+
+ // The cross-origin/joined case must be tested after both unjoined same-origin
+ // and unjoined cross-origin tests: by mucking with document.domain, the
+ // cross-origin/joined case makes it impossible to perform those tests.
+ t.step(function() {
+ Promise.all([triggerCrossOriginTest, triggerSameOriginTest])
+ .then(t.step_func_done(crossOriginJoinTest),
+ t.unreached_func("cross-origin joined error case"));
+ });
+}
+run();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-1.html
new file mode 100644
index 0000000000..3d2b897221
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-1.html
@@ -0,0 +1,9 @@
+<!doctype html>
+1
+<script>
+onload = parent.t.step_func(function() {
+ setTimeout(function() {
+ location = location.toString().replace("assign_after_load-1.html", "assign_after_load-2.html");
+ }, 100);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-2.html
new file mode 100644
index 0000000000..94679571be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+2
+<script>
+onload = parent.t.step_func(function() {
+ setTimeout(function() {parent.do_test()}, 100);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load.html
new file mode 100644
index 0000000000..00dc931d4e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_after_load.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>Assignment to location after document is completely loaded</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe></iframe>
+<script>
+var t = async_test();
+var history_length;
+
+onload = t.step_func(function() {
+ setTimeout(function() {
+ history_length = history.length;
+ document.getElementsByTagName("iframe")[0].src = "assign_after_load-1.html";
+ }, 100);
+});
+
+do_test = t.step_func(function() {
+ assert_equals(history.length, history_length + 2);
+ t.done();
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-1.html
new file mode 100644
index 0000000000..2549867c8f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+1
+<script>
+onload = parent.t.step_func(function() {
+ location = location.toString().replace("assign_before_load-1.html", "assign_before_load-2.html");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-2.html
new file mode 100644
index 0000000000..94679571be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+2
+<script>
+onload = parent.t.step_func(function() {
+ setTimeout(function() {parent.do_test()}, 100);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load.html
new file mode 100644
index 0000000000..62a2aa7c60
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/assign_before_load.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>Assignment to location before document is completely loaded</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe></iframe>
+<script>
+var t = async_test();
+var history_length;
+
+onload = t.step_func(function() {
+ setTimeout(function() {
+ history_length = history.length;
+ document.getElementsByTagName("iframe")[0].src = "assign_before_load-1.html";
+ }, 100);
+});
+
+do_test = t.step_func(function() {
+ assert_equals(history.length, history_length + 1);
+ t.done();
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html
new file mode 100644
index 0000000000..a3ffdd005a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Cross-origin subframe for Location cyclic [[Prototype]] test</title>
+ <link rel="author" title="Jeff Walden" href="http://whereswalden.com/" />
+</head>
+<body>
+<script>
+document.domain = "{{host}}";
+</script>
+<!-- this should be accessible to the parent once it sets document.domain -->
+<p>Cross-origin iframe with joined <code>document.domain</code></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/document_location.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/document_location.html
new file mode 100644
index 0000000000..3bdb028212
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/document_location.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>document.location</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = document.implementation.createHTMLDocument("");
+ assert_equals(doc.location, null);
+}, "document not in a browsing context");
+
+test(function() {
+ assert_equals(document.location, location);
+}, "document.location equals window.location");
+
+test(function() {
+ var desc1 = Object.getOwnPropertyDescriptor(new Document(), "location");
+ assert_not_equals(desc1, undefined);
+ assert_equals(typeof desc1.get, "function");
+
+ var desc2 = Object.getOwnPropertyDescriptor(new Document(), "location");
+ assert_not_equals(desc2, undefined);
+ assert_equals(typeof desc2.get, "function");
+
+ assert_equals(desc1.get, desc2.get);
+}, "Attribute getter deduplication");
+
+test(function() {
+ var desc1 = Object.getOwnPropertyDescriptor(new Document(), "location");
+ assert_not_equals(desc1, undefined);
+ assert_equals(typeof desc1.set, "function");
+
+ var desc2 = Object.getOwnPropertyDescriptor(new Document(), "location");
+ assert_not_equals(desc2, undefined);
+ assert_equals(typeof desc2.set, "function");
+
+ assert_equals(desc1.set, desc2.set);
+}, "Attribute setter deduplication");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-non-configurable-toString-valueOf.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-non-configurable-toString-valueOf.html
new file mode 100644
index 0000000000..80760ac9e4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-non-configurable-toString-valueOf.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-origin Location objects have non-configurable "toString" and "valueOf" properties</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/history.html#location-defineownproperty">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ assert_own_property(location, "toString");
+ const origToString = location.toString;
+
+ assert_throws_js(TypeError, () => {
+ Object.defineProperty(location, "toString", {
+ get() {},
+ set(_v) {},
+ enumerable: true,
+ configurable: true,
+ });
+ });
+
+ assert_equals(location.toString, origToString);
+}, "'toString' redefinition with accessor fails");
+
+test(() => {
+ assert_own_property(location, "valueOf");
+ const origValueOf = location.valueOf;
+
+ assert_throws_js(TypeError, () => {
+ Object.defineProperty(location, "valueOf", {
+ get() {},
+ enumerable: false,
+ configurable: true,
+ });
+ });
+
+ assert_equals(location.valueOf, origValueOf);
+}, "'valueOf' redefinition with accessor fails");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-origin-idna.sub.window.js b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-origin-idna.sub.window.js
new file mode 100644
index 0000000000..83b030f886
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-origin-idna.sub.window.js
@@ -0,0 +1,11 @@
+async_test(t => {
+ const frame = document.createElement("iframe"),
+ asciiOrigin = location.protocol + "//{{domains[天気ã®è‰¯ã„æ—¥]}}:" + location.port,
+ path = new URL("resources/post-your-origin.html", location).pathname;
+ frame.src = asciiOrigin + path;
+ self.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.origin, asciiOrigin);
+ });
+ document.body.appendChild(frame);
+ t.add_cleanup(() => frame.remove());
+}, "Test that location.origin returns ASCII");
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-pathname-setter-question-mark.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-pathname-setter-question-mark.html
new file mode 100644
index 0000000000..09546020f7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-pathname-setter-question-mark.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Set location.pathname to ?</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe src=/common/blank.html></iframe>
+<script>
+async_test((t) => {
+ onload = t.step_func(() => {
+ self[0].frameElement.onload = t.step_func_done(() => {
+ assert_equals(self[0].location.pathname, "/%3F")
+ })
+ self[0].location.pathname = "?"
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prevent-extensions.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prevent-extensions.html
new file mode 100644
index 0000000000..a8c777aded
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prevent-extensions.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[PreventExtensions]] on a Location object should return false</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/history.html#location-preventextensions">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ assert_throws_js(TypeError, () => {
+ Object.preventExtensions(location);
+ });
+}, "Object.preventExtensions throws a TypeError");
+
+test(() => {
+ assert_false(Reflect.preventExtensions(location));
+}, "Reflect.preventExtensions returns false");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html
new file mode 100644
index 0000000000..544eb4ad9a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>Set location.protocol from an HTTP URL</title>
+<!-- In particular, valid non-broken schemes that are nevertheless not going to work -->
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<script>
+self.onload = () => {
+ [
+ 'x',
+ 'data',
+ 'file',
+ 'ftp',
+ 'http+x'
+ ].forEach((val, index) => {
+ async_test((t) => {
+ self[index].location.protocol = val
+ t.step_timeout(() => {
+ assert_equals(self[index].location.protocol, location.protocol)
+ assert_equals(self[index].location.host, location.host)
+ assert_equals(self[index].location.port, location.port)
+ t.done()
+ // Experimentally, 4 seconds is long enough for the navigation to
+ // complete, if it happens.
+ }, 4000)
+ }, "Set location.protocol to " + val)
+ })
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html
new file mode 100644
index 0000000000..585016eae9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<title>Set location.protocol to a non-broken-non-functioning scheme</title>
+<!-- In particular, valid non-broken schemes that are nevertheless not going to work -->
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+self.onload = () => {
+ [
+ 'x',
+ 'data',
+ // 'mailto' opens an email client in Chrome and Firefox and then ends up passing anyway...
+ 'file',
+ 'ftp',
+ 'http+x'
+ ].forEach((val) => {
+ async_test((t) => {
+ // HTTP URL <iframe>
+ const frame = document.createElement("iframe")
+ t.add_cleanup(() => frame.remove())
+ frame.src = "/common/blank.html"
+ frame.onload = t.step_func(() => {
+ frame.contentWindow.location.protocol = val
+ t.step_timeout(() => {
+ assert_equals(frame.contentWindow.location.protocol, location.protocol)
+ assert_equals(frame.contentWindow.location.host, location.host)
+ assert_equals(frame.contentWindow.location.port, location.port)
+ t.done()
+ // Matches the timeout from location-protocol-setter-non-broken-weird.html which suggests
+ // that 4 seconds is enough for a navigation to complete.
+ }, 4000)
+ })
+ document.body.appendChild(frame)
+ }, "Set HTTP URL frame location.protocol to " + val)
+
+ async_test((t) => {
+ // data URL <iframe>
+ const dataFrame = document.createElement("iframe")
+ t.add_cleanup(() => dataFrame.remove())
+ const channel = new MessageChannel()
+ dataFrame.src = `data:text/html,<script>
+onmessage = (e) => {
+ let result = false;
+ try {
+ location.protocol = e.data
+ } catch(e) {
+ result = true
+ }
+ setTimeout(() => e.ports[0].postMessage([result, location.protocol]), 4000)
+}
+<\/script>`
+ dataFrame.onload = t.step_func(() => {
+ dataFrame.contentWindow.postMessage(val, "*", [channel.port2])
+ })
+ channel.port1.onmessage = t.step_func_done((e) => {
+ assert_false(e.data[0])
+ assert_equals(e.data[1], "data:")
+ })
+ document.body.appendChild(dataFrame)
+ }, "Set data URL frame location.protocol to " + val)
+ })
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-sameish.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-sameish.html
new file mode 100644
index 0000000000..c581ae35aa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-sameish.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Set location.protocol to the scheme it already was</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<iframe src=/common/blank.html></iframe>
+<script>
+self.onload = () => {
+ [
+ "http",
+ "ht\x0Atp",
+ "http\x0A",
+ "\x09ht\x09\x0AtP"
+ ].forEach((val, index) => {
+ async_test(t => {
+ self[index].frameElement.onload = t.step_func_done(() => {
+ assert_equals(self[index].location.protocol, "http:");
+ });
+ self[index].location.protocol = val;
+ }, `Set location.protocol to ${encodeURI(val)} (percent-encoded here for clarity)`);
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-with-colon.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-with-colon.sub.html
new file mode 100644
index 0000000000..0612b5c709
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter-with-colon.sub.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe id="existing" src="resources/post-your-protocol.html?existing"></iframe>
+<iframe id="http-and-gunk" src="resources/post-your-protocol.html?http-and-gunk"></iframe>
+<!-- iframe id="https-and-gunk" src="resources/post-your-protocol.html?https-and-gunk"></iframe -->
+<script>
+// NOTE: we do not listen to message events until our load event fires, so we
+// only get them for the things we actually care about.
+var wrapper_test = async_test("General setup");
+var tests = {
+ "existing": { test: async_test("Set location.protocol = location.protocol"),
+ result: location.protocol },
+ "http-and-gunk": { test: async_test("Set location.protocol to http:gunk"),
+ result: "http:" },
+ // We should really test the "https:gunk" case too, and assert that it ends up
+ // with a protocol of "https:", but can't. See comments below for why.
+};
+
+function messageListener(e) {
+ var data = e.data;
+ var id = data.id;
+ var t = tests[id].test;
+ t.step(function() {
+ assert_equals(data.protocol, tests[id].result, "Protocol should match");
+ })
+ t.done();
+}
+
+addEventListener("load", wrapper_test.step_func_done(function() {
+ addEventListener("message", messageListener);
+
+ tests["existing"].test.step(function() {
+ var loc = document.getElementById("existing").contentWindow.location;
+ loc.protocol = loc.protocol;
+ });
+ tests["http-and-gunk"].test.step(function() {
+ var loc = document.getElementById("http-and-gunk").contentWindow.location;
+ loc.protocol = "http:gunk";
+ });
+ // I wish we could test the https bit, but can't figure out a non-racy way to
+ // do it, because we need to change both protocol (to https) _and_ port to
+ // {{ports[https][0]}} to get a successful load unless we're running on the
+ // default http port, but the setter uses the current value, which doesn't get
+ // updated sync, as the url to start with for the set. Oh, and there's no
+ // good way to detect when the port set is "done" either.
+}));
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter.html
new file mode 100644
index 0000000000..3d051e8cfb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-protocol-setter.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<title>Set location.protocol to schemes that throw</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe src="data:text/html,<script>
+onmessage = (e) => {
+ let results = [];
+ e.data.forEach((val) => {
+ try {
+ location.protocol = val;
+ results.push('failure')
+ } catch(e) {
+ results.push(e.name)
+ }
+ });
+ parent.postMessage(results, '*')
+}
+</script>"></iframe>
+<iframe srcdoc="<script>
+onmessage = (e) => {
+ let results = [];
+ e.data.forEach((val) => {
+ try {
+ location.protocol = val;
+ results.push('failure')
+ } catch(e) {
+ results.push(e.name)
+ }
+ });
+ parent.postMessage(results, '*')
+}
+</script>"></iframe>
+<script>
+ const broken = [
+ '\x00',
+ '\x01',
+ '\x09', // becomes the empty string
+ '\x0A', // becomes the empty string
+ '\x0C',
+ '\x0D',
+ '\x20',
+ '\x21',
+ '\x7F',
+ '\x80',
+ '\xFF',
+ ':',
+ '†',
+ '\x00x',
+ '\x01x',
+ '\x20x',
+ '\x21x',
+ '\x7Fx',
+ '\x80x',
+ '\xFFx',
+ ':x',
+ '†x',
+ '\x00X',
+ '\x01X',
+ '\x20X',
+ '\x21X',
+ '\x7FX',
+ '\x80X',
+ '\xFFX',
+ ':X',
+ '†X',
+ 'x\x00',
+ 'x\x01',
+ 'x\x20',
+ 'x\x21',
+ 'x\x7F',
+ 'x\x80',
+ 'x\xFF',
+ 'x†',
+ 'X\x00',
+ 'X\x01',
+ 'X\x20',
+ 'X\x21',
+ 'X\x7F',
+ 'X\x80',
+ 'X\xFF',
+ 'X†',
+ ];
+
+ broken.forEach(val => {
+ test(() => {
+ assert_throws_dom("SyntaxError", () => { location.protocol = val })
+ }, `${encodeURI(val)} (percent-encoded here for clarity) is not a scheme`)
+ })
+ let c = 0
+ async_test((t) => {
+ self.onload = t.step_func(() => {
+ self.onmessage = t.step_func((e) => {
+ assert_array_equals(e.data, broken.map(() => "SyntaxError"))
+ c++
+ if(c === 2) {
+ t.done()
+ }
+ })
+ self[0].postMessage(broken, "*")
+ self[1].postMessage(broken, "*")
+ })
+ }, "Equivalent tests for data URL and srcdoc <iframe>s")
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-no-toString-valueOf.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-no-toString-valueOf.html
new file mode 100644
index 0000000000..56316320af
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-no-toString-valueOf.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Location.prototype objects don't have own "toString" and "valueOf" properties</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(t => {
+ assert_not_own_property(Location.prototype, "toString");
+ t.add_cleanup(() => { delete Location.prototype.toString; });
+
+ let val;
+ Object.defineProperty(Location.prototype, "toString", {
+ get: () => val,
+ set: newVal => { val = newVal; },
+ enumerable: false,
+ configurable: true,
+ });
+
+ Location.prototype.toString = 2;
+ assert_equals(Location.prototype.toString, 2);
+}, "'toString' accessor property is defined");
+
+test(t => {
+ assert_not_own_property(Location.prototype, "toString");
+ t.add_cleanup(() => { delete Location.prototype.toString; });
+
+ Location.prototype.toString = 4;
+ assert_equals(Location.prototype.toString, 4);
+}, "'toString' data property is created via [[Set]]");
+
+test(t => {
+ assert_not_own_property(Location.prototype, "valueOf");
+ t.add_cleanup(() => { delete Location.prototype.valueOf; });
+
+ Object.defineProperty(Location.prototype, "valueOf", {
+ get: () => 6,
+ enumerable: true,
+ configurable: true,
+ });
+
+ assert_equals(Location.prototype.valueOf, 6);
+}, "'valueOf' accessor property is defined");
+
+test(t => {
+ assert_not_own_property(Location.prototype, "valueOf");
+ t.add_cleanup(() => { delete Location.prototype.valueOf; });
+
+ Location.prototype.valueOf = 8;
+ assert_equals(Object.getOwnPropertyDescriptor(Location.prototype, "valueOf").value, 8);
+}, "'valueOf' data property is created via [[Set]]");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin-domain.sub.html
new file mode 100644
index 0000000000..1e677b0365
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin-domain.sub.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a Location object should not allow changing its value: cross-origin via document.domain</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#location-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="/common/domain-setter.sub.html"></iframe>
+
+<script>
+"use strict";
+// This page does *not* set document.domain, so it's cross-origin with the iframe
+setup({ explicit_done: true });
+
+window.onload = () => {
+ const targetLocation = frames[0].location;
+ const origProto = Object.getPrototypeOf(targetLocation);
+
+ test(() => {
+ assert_equals(Object.getPrototypeOf(targetLocation), null);
+ }, "Cross-origin via document.domain: the prototype is null");
+
+ testSettingImmutablePrototype("Cross-origin via document.domain", targetLocation, origProto,
+ { isSameOriginDomain: false });
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin.sub.html
new file mode 100644
index 0000000000..7bb6a27e21
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin.sub.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a Location object should not allow changing its value: cross-origin</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#location-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="//{{domains[www]}}:{{ports[http][1]}}/common/blank.html"></iframe>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ const targetLocation = frames[0].location;
+
+ test(() => {
+ assert_equals(Object.getPrototypeOf(targetLocation), null);
+ }, "Cross-origin: the prototype is null");
+
+ testSettingImmutablePrototype("Cross-origin", targetLocation, null, { isSameOriginDomain: false });
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-goes-cross-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-goes-cross-origin-domain.sub.html
new file mode 100644
index 0000000000..8966a814c5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-goes-cross-origin-domain.sub.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a Location object should not allow changing its value: cross-origin via document.domain after initially getting the object</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#location-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="/common/blank.html"></iframe>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ const targetLocation = frames[0].location;
+ const origProto = Object.getPrototypeOf(targetLocation);
+
+ test(() => {
+ assert_not_equals(origProto, null);
+ }, "Same-origin (for now): the prototype is accessible");
+
+ document.domain = "{{host}}";
+
+ test(() => {
+ assert_equals(Object.getPrototypeOf(targetLocation), null);
+ }, "Became cross-origin via document.domain: the prototype is now null");
+
+ testSettingImmutablePrototype("Became cross-origin via document.domain", targetLocation, null, { isSameOriginDomain: false });
+
+ testSettingImmutablePrototypeToNewValueOnly(
+ "Became cross-origin via document.domain", targetLocation, origProto,
+ "the original value from before going cross-origin", { isSameOriginDomain: false });
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html
new file mode 100644
index 0000000000..8ec7585daa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a Location object should not allow changing its value: cross-origin, but same-origin-domain</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#location-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="//{{domains[www]}}:{{ports[http][1]}}/common/domain-setter.sub.html"></iframe>
+
+<script>
+"use strict";
+document.domain = "{{host}}";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ const targetLocation = frames[0].location;
+ const origProto = Object.getPrototypeOf(targetLocation);
+
+ test(() => {
+ assert_not_equals(origProto, null);
+ }, "Same-origin-domain prerequisite check: the original prototype is accessible");
+
+ testSettingImmutablePrototype("Same-origin-domain", targetLocation, origProto, { isSameOriginDomain: true }, frames[0]);
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin.html
new file mode 100644
index 0000000000..f912004f73
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a location object should not allow changing its value: same-origin</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#location-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<script>
+"use strict";
+
+const origProto = Object.getPrototypeOf(location);
+
+test(() => {
+ assert_not_equals(origProto, null);
+}, "Same-origin prerequisite check: the original prototype is accessible");
+
+testSettingImmutablePrototype("Same-origin", location, origProto, { isSameOriginDomain: true });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-stringifier.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-stringifier.html
new file mode 100644
index 0000000000..48bad7af0e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-stringifier.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Location stringifier</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-stringifier">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/stringifiers.js></script>
+<div id=log></div>
+<script>
+test_stringifier_attribute(location, "href", true);
+
+test(function() {
+ const prop1 = Object.getOwnPropertyDescriptor(location, "toString"),
+ prop2 = Object.getOwnPropertyDescriptor(location, "href")
+
+ assert_true(prop1.enumerable)
+ assert_false(prop1.writable)
+ assert_false(prop1.configurable)
+
+ assert_true(prop2.enumerable)
+ assert_false(prop2.configurable)
+ assert_equals(typeof prop2.get, "function")
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-symbol-toprimitive.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-symbol-toprimitive.html
new file mode 100644
index 0000000000..e666a3e703
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-symbol-toprimitive.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Location Symbol.toPrimitive</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(() => {
+ assert_equals(location[Symbol.toPrimitive], undefined)
+ const prop = Object.getOwnPropertyDescriptor(location, Symbol.toPrimitive)
+ assert_false(prop.enumerable)
+ assert_false(prop.writable)
+ assert_false(prop.configurable)
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-tojson.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-tojson.html
new file mode 100644
index 0000000000..5f20a6e15c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-tojson.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>Location has no toJSON</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(() => {
+ assert_equals(location.toJSON, undefined)
+ assert_equals(Object.getOwnPropertyDescriptor(location, "toJSON"), undefined)
+ assert_false(location.hasOwnProperty("toJSON"))
+})
+</script>
+<!-- See https://github.com/whatwg/html/pull/2294 for context. (And the HTML Standard of course.) -->
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-valueof.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-valueof.html
new file mode 100644
index 0000000000..978bbb63a0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-valueof.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Location valueOf</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(() => {
+ assert_equals(location.valueOf, Object.prototype.valueOf)
+ assert_equals(typeof location.valueOf.call(5), "object")
+ const prop = Object.getOwnPropertyDescriptor(location, "valueOf")
+ assert_false(prop.enumerable)
+ assert_false(prop.writable)
+ assert_false(prop.configurable)
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign.html
new file mode 100644
index 0000000000..55f26d5660
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_assign</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var href = location.href;
+ location.assign('#x');
+
+ assert_equals((href + "#x"), location.href, "location href");
+
+ }, "location assign");
+
+ test(function () {
+ var href = location.href;
+ assert_throws_dom('SYNTAX_ERR', function() { location.assign("http://:"); });
+ assert_equals(location.href, href);
+ }, "URL that fails to parse");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank-1.html
new file mode 100644
index 0000000000..b43598f2cd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank-1.html
@@ -0,0 +1,2 @@
+<!doctype html>
+Filler text
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank.html
new file mode 100644
index 0000000000..f3f7cf26b8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_assign_about_blank.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>location.assign with initial about:blank browsing context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe></iframe>
+<script>
+var t = async_test();
+var history_length;
+onload = t.step_func(function() {
+ setTimeout(t.step_func(function() {
+ var iframe = document.getElementsByTagName("iframe")[0];
+ iframe.onload = t.step_func(function() {
+ setTimeout(t.step_func(function() {
+ assert_equals(history.length, history_length);
+ t.done();
+ }), 100);
+ });
+ history_length = history.length;
+ iframe.src = "location_assign_about_blank-1.html"
+ }), 100);
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_hash.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_hash.html
new file mode 100644
index 0000000000..c063e285ea
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_hash.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_hash</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <iframe id="srcdoc-iframe"
+ srcdoc="<div style='height: 200vh'></div><div id='test'></div>"></iframe>
+ <script>
+ function resetHash() {
+ location.hash = "";
+ }
+
+ test(function (t) {
+ t.add_cleanup(resetHash);
+ window.history.pushState(1, document.title, '#x=1');
+ var hash = location.hash;
+
+ assert_equals(hash, "#x=1", "hash");
+
+ }, "location hash");
+
+ var t = async_test("Setting location.hash on srcdoc iframe");
+ addEventListener("load", t.step_func_done(function() {
+ var frameWin = document.getElementById("srcdoc-iframe").contentWindow;
+ assert_equals(frameWin.location.href, "about:srcdoc");
+ assert_equals(frameWin.scrollY, 0, "Should not have scrolled yet");
+ frameWin.location.hash = "test";
+ assert_equals(frameWin.location.href, "about:srcdoc#test");
+ assert_true(frameWin.scrollY > frameWin.innerHeight,
+ "Should have scrolled by more than one viewport height");
+ }));
+
+ test(function(t) {
+ t.add_cleanup(resetHash);
+ location.hash = "test";
+ assert_equals(location.hash, "#test");
+ }, "Setting hash should automatically include hash character");
+
+ test(function(t) {
+ t.add_cleanup(resetHash);
+ location.hash = "#not encoded";
+ assert_equals(location.hash, "#not%20encoded");
+ }, "Setting hash should encode incompatible characters");
+
+ test(function(t) {
+ t.add_cleanup(resetHash);
+ location.hash = "#already%20encoded";
+ assert_equals(location.hash, "#already%20encoded");
+ }, "Setting hash to an already encoded value should not double encode it");
+
+ test(function(t) {
+ t.add_cleanup(resetHash);
+ location.hash = "#mixed encoding%20here";
+ assert_equals(location.hash, "#mixed%20encoding%20here");
+ }, "Setting hash which is partially encoded should only encode incompatible characters");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_host.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_host.html
new file mode 100644
index 0000000000..d93bf47e50
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_host.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_host</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var host = location.host;
+ var url = location.href;
+
+ var pos = url.indexOf("//");
+ if (pos != -1) {
+ url = url.substr(pos+2, url.length-pos-2);
+ pos = url.indexOf("/");
+ if (pos != -1)
+ url = url.substr(0, pos);
+ }
+
+ assert_equals(host, url, "host");
+
+ }, "location host");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_hostname.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_hostname.html
new file mode 100644
index 0000000000..2ffa0e5fc8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_hostname.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_hostname</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var hostname = location.hostname;
+ var url = location.href;
+
+ var pos = url.indexOf("//");
+ if (pos != -1) {
+ url = url.substr(pos+2, url.length-pos-2);
+ pos = url.indexOf(":");
+ if (pos != -1) {
+ url = url.substr(0, pos);
+ } else {
+ pos = url.indexOf("/");
+ if (pos != -1)
+ url = url.substr(0, pos);
+ }
+ }
+
+ assert_equals(hostname, url, "hostname");
+
+ }, "location hostname");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_href.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_href.html
new file mode 100644
index 0000000000..1aa85dcdc8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_href.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_href</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var href = location.href;
+
+ assert_equals(href, document.URL, "href");
+
+ }, "location href");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_origin.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_origin.html
new file mode 100644
index 0000000000..2325f4018a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_origin.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(function () {
+ assert_equals(
+ location.origin,
+ location.protocol + '//' + location.host,
+ "origin"
+ );
+ }, "location origin");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_pathname.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_pathname.html
new file mode 100644
index 0000000000..dea05d2f37
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_pathname.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_pathname</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var pathname = location.pathname;
+ var url = location.href
+
+ url = url.replace(location.protocol + "//" + location.host, "");
+
+ assert_equals(pathname, url, "pathname");
+
+ }, "location pathname");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_port.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_port.html
new file mode 100644
index 0000000000..fa1308ca5d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_port.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_port</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var port = location.port;
+ var url = location.href;
+
+ var pos = url.indexOf("//");
+ if (pos != -1) {
+ url = url.substr(pos+2, url.length-pos-2);
+ pos = url.indexOf("/");
+ if (pos != -1)
+ url = url.substr(0, pos);
+ pos = url.indexOf(":");
+ if (pos != -1)
+ url = url.substr(pos+1, url.length-pos-1);
+ }
+
+ assert_equals(port, url, "port");
+
+ }, "location port");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_protocol.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_protocol.html
new file mode 100644
index 0000000000..d28bd56393
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_protocol.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_protocol</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var protocol = location.protocol;
+ var url = location.href;
+
+ var pos = url.indexOf("//");
+ if (pos != -1) {
+ url = url.substr(0, pos);
+ }
+
+ assert_equals(protocol, url, "protocol");
+
+ }, "location protocol");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload-iframe.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload-iframe.html
new file mode 100644
index 0000000000..f08cf5de3e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload-iframe.html
@@ -0,0 +1,4 @@
+<script>
+ parent._ping(window.location.href)
+ if (parent._pingCount < 5) { location.reload(); }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload.html
new file mode 100644
index 0000000000..0a2d21d8d2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_reload</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body onload="startTest();">
+ <div id="log"></div>
+
+ <iframe></iframe>
+
+ <script>
+ var history_length;
+ async_test(function(t) {
+
+ var url = new URL("./location_reload-iframe.html", window.location).href;
+
+ window._pingCount = 0;
+ window._ping = t.step_func(function(innerURL) {
+ // Some browsers keep 'about:blank' in the session history
+ if (_pingCount == 0) {
+ history_length = history.length;
+ }
+ assert_equals(url, innerURL, "iframe url (" + _pingCount + ")");
+ assert_equals(history_length, history.length, "history length (" + _pingCount + ")");
+ _pingCount++;
+ if (_pingCount == 5) {
+ t.done();
+ }
+ });
+ });
+
+ function startTest() {
+ var url = new URL("./location_reload-iframe.html", window.location).href;
+ var iframe = document.querySelector("iframe");
+ iframe.src = url;
+ history_length = history.length;
+ }
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload_javascript_url.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload_javascript_url.html
new file mode 100644
index 0000000000..737cafbcd3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload_javascript_url.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_reload_javascript_url</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+
+ <iframe></iframe>
+
+ <script>
+ async_test(function(t) {
+ const URL = "/common/blank.html";
+ const URL2 = "/common/blank.html#foo";
+ const JS_URL_TEXT = "javascript generated page";
+ const JS_URL = "javascript:'<html>" + JS_URL_TEXT + "</html>'";
+
+ var iframe = document.querySelector("iframe");
+ var count = 0;
+ iframe.onload = t.step_func(function() {
+ // The URL should initially be "blank.html", and then "blank.html#foo";
+ // The textContent of the iframe's document should initially be blank,
+ // then become js generated text, and then be blank again after reload.
+ switch (count) {
+ case 0:
+ assert_equals(iframe.contentWindow.document.URL,
+ location.href.replace(location.pathname, URL),
+ "iframe url (" + count + ")");
+ assert_equals(iframe.contentDocument.body.textContent, "",
+ "text of blank page");
+ iframe.contentWindow.location = JS_URL;
+ iframe.contentWindow.location = URL2;
+ break;
+ case 1:
+ assert_equals(iframe.contentWindow.document.URL,
+ location.href.replace(location.pathname, URL2),
+ "iframe url (" + count + ")");
+ assert_equals(iframe.contentDocument.body.textContent,
+ JS_URL_TEXT, "text of js generated page");
+ iframe.contentWindow.location.reload();
+ break;
+ case 2:
+ assert_equals(iframe.contentWindow.document.URL,
+ location.href.replace(location.pathname, URL2),
+ "iframe url (" + count + ")");
+ assert_equals(iframe.contentDocument.body.textContent, "",
+ "text of blank page");
+ t.done();
+ break;
+ }
+ count++;
+ });
+ iframe.src = URL;
+ });
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_replace.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_replace.html
new file mode 100644
index 0000000000..0593420d40
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_replace.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_replace</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ var href = location.href;
+ location.replace('#x');
+
+ assert_equals((href + "#x"), location.href, "location href");
+
+ }, "location replace");
+
+ test(function () {
+ var href = location.href;
+ assert_throws_dom('SYNTAX_ERR', function() { location.replace("//"); });
+ assert_equals(location.href, href);
+ }, "URL that fails to parse");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_search.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_search.html
new file mode 100644
index 0000000000..f9db757841
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_search.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>location_search</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function () {
+ window.history.pushState(1, document.title, '?x=1');
+ var search = location.search;
+
+ assert_equals(search, "?x=1", "search");
+
+ }, "location search");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/no-browsing-context.window.js b/testing/web-platform/tests/html/browsers/history/the-location-interface/no-browsing-context.window.js
new file mode 100644
index 0000000000..4077d90971
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/no-browsing-context.window.js
@@ -0,0 +1,86 @@
+test(() => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ win = frame.contentWindow,
+ loc = win.location;
+ frame.remove();
+ assert_equals(win.location, loc);
+}, "Window and Location are 1:1 after browsing context removal");
+
+function bcLessLocation() {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ win = frame.contentWindow,
+ loc = win.location;
+ frame.remove();
+ return loc;
+}
+
+[
+ {
+ "property": "href",
+ "expected": "about:blank",
+ "values": ["https://example.com/", "/", "http://test:test/", "test test", "test:test", "chrome:fail"]
+ },
+ {
+ "property": "protocol",
+ "expected": "about:",
+ "values": ["http", "about", "test"]
+ },
+ {
+ "property": "host",
+ "expected": "",
+ "values": ["example.com", "test test", "()"]
+ },
+ {
+ "property": "hostname",
+ "expected": "",
+ "values": ["example.com"]
+ },
+ {
+ "property": "port",
+ "expected": "",
+ "values": ["80", "", "443", "notaport"]
+ },
+ {
+ "property": "pathname",
+ "expected": "blank",
+ "values": ["/", "x"]
+ },
+ {
+ "property": "search",
+ "expected": "",
+ "values": ["test"]
+ },
+ {
+ "property": "hash",
+ "expected": "",
+ "values": ["test", "#"]
+ }
+].forEach(testSetup => {
+ testSetup.values.forEach(value => {
+ test(() => {
+ const loc = bcLessLocation();
+ loc[testSetup.property] = value;
+ assert_equals(loc[testSetup.property], testSetup.expected);
+ }, "Setting `" + testSetup.property + "` to `" + value + "` of a `Location` object sans browsing context is a no-op");
+ });
+});
+
+test(() => {
+ const loc = bcLessLocation();
+ assert_equals(loc.origin, "null");
+}, "Getting `origin` of a `Location` object sans browsing context should be \"null\"");
+
+["assign", "replace", "reload"].forEach(method => {
+ ["about:blank", "https://example.com/", "/", "http://test:test/", "test test", "test:test", "chrome:fail"].forEach(value => {
+ test(() => {
+ const loc = bcLessLocation();
+ loc[method](value);
+ assert_equals(loc.href, "about:blank");
+ }, "Invoking `" + method + "` with `" + value + "` on a `Location` object sans browsing context is a no-op");
+ });
+});
+
+test(() => {
+ const loc = bcLessLocation();
+ assert_array_equals(loc.ancestorOrigins, []);
+}, "Getting `ancestorOrigins` of a `Location` object sans browsing context should be []");
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-1.html
new file mode 100644
index 0000000000..c762ece3bc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+opener.history_length = history.length;
+</script>
+<a onclick="location = 'manual_click_assign_during_load-2.html'; return false;" href>Click Here</a>
+<p>Filler image to keep the page loading:</p>
+<img src="/images/smiley.png?pipe=trickle(20:d1:r2)">
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-2.html
new file mode 100644
index 0000000000..1bf7f41e00
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<p>This window should close itself and the test result appear in the original window
+<script>
+onload = function() {
+ setTimeout(function() {opener.do_test(history.length); window.close();}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-manual.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-manual.html
new file mode 100644
index 0000000000..45a0e3e2fd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Assignment to location with click during load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The popup blocker must be disabled for this test</p>
+<div id="log"></div>
+<script>
+setup({timeout:3600000});
+var t = async_test();
+var win = window.open("manual_click_assign_during_load-1.html");
+
+var history_length;
+do_test = t.step_func(function(new_length) {
+ assert_equals(new_length, history_length + 1);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-1.html
new file mode 100644
index 0000000000..e9d03e9364
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-1.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<script>
+opener.history_length = history.length;
+</script>
+<a onclick="location.replace('manual_click_location_replace_during_load-2.html'); return false;" href>Click Here</a>
+<p>Filler image to keep the page loading:</p>
+<img>
+<script>
+document.images[0].src = "/images/smiley.png?pipe=trickle(20:d1:r2)&random=" + Math.random();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-2.html
new file mode 100644
index 0000000000..1bf7f41e00
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<p>This window should close itself and the test result appear in the original window
+<script>
+onload = function() {
+ setTimeout(function() {opener.do_test(history.length); window.close();}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-manual.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-manual.html
new file mode 100644
index 0000000000..a453de34bf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>location.replace with click during load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The popup blocker must be disabled for this test</p>
+<div id="log"></div>
+<script>
+setup({timeout:3600000});
+var t = async_test();
+var win = window.open("manual_click_location_replace_during_load-1.html");
+
+var history_length;
+do_test = t.step_func(function(new_length) {
+ assert_equals(new_length, history_length);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_replace_during_load-manual.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_replace_during_load-manual.html
new file mode 100644
index 0000000000..482858bcd7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_click_replace_during_load-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Assignment to location with click during load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The popup blocker must be disabled for this test</p>
+<div id="log"></div>
+<script>
+setup({timeout:3600000});
+var t = async_test();
+var win = window.open("manual_click_replace_during_load-1.html");
+
+var history_length;
+do_test = t.step_func(function(new_length) {
+ assert_equals(new_length, history_length);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-1.html
new file mode 100644
index 0000000000..08f7e2dd68
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-1.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<script>
+opener.history_length = history.length;
+</script>
+<form onsubmit="location = 'manual_form_submit_assign_during_load-2.html'; return false;">
+<input type=submit value="Click Me">
+</form>
+<p>Filler image to keep the page loading:</p>
+<img src="/images/smiley.png?pipe=trickle(20:d1:r2)">
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-2.html
new file mode 100644
index 0000000000..1bf7f41e00
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<p>This window should close itself and the test result appear in the original window
+<script>
+onload = function() {
+ setTimeout(function() {opener.do_test(history.length); window.close();}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-manual.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-manual.html
new file mode 100644
index 0000000000..d71b206ac5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Assignment to location with form submit during load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The popup blocker must be disabled for this test</p>
+<div id="log"></div>
+<script>
+setup({timeout:3600000});
+var t = async_test();
+var win = window.open("manual_form_submit_assign_during_load-1.html");
+
+var history_length;
+do_test = t.step_func(function(new_length) {
+ assert_equals(new_length, history_length + 1);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-1.html
new file mode 100644
index 0000000000..05b44f4c40
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<p>Resize this window. FAIL if the window doesn't close shortly afterwards.</p>
+<script>
+onload = opener.t.step_func(function() {
+ opener.load_count++;
+ if (opener.load_count > 1) {
+ opener.do_test();
+ }
+})
+
+onresize = opener.t.step_func(function() {
+ opener.flag_resized();
+ location.reload();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-manual.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-manual.html
new file mode 100644
index 0000000000..5cb3fac964
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/non-automated/reload_in_resize-manual.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Reload called from resize event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>Resize the popup window. That window should then close and the result be presented here. If that window doesn't close after resize that's a FAIL.</p>
+<div id="log"></div>
+<script>
+setup({timeout:3600000})
+var t = async_test();
+var load_count = 0;
+var resized = false;
+var win = window.open("reload_in_resize-1.html")
+
+flag_resized = t.step_func(function() {
+ resized = true;
+ setTimeout(do_test, 1000);
+});
+
+do_test = t.step_func(function() {
+ win.close();
+ assert_true(resized, "Resize event happened");
+ assert_equals(load_count, 1, "Number of load events");
+ t.done();
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/per-global.window.js b/testing/web-platform/tests/html/browsers/history/the-location-interface/per-global.window.js
new file mode 100644
index 0000000000..b2956fd21f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/per-global.window.js
@@ -0,0 +1,3 @@
+// META: script=/common/object-association.js
+
+testIsPerWindow("location");
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write-1.html
new file mode 100644
index 0000000000..e1a2e811c9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write-1.html
@@ -0,0 +1,19 @@
+<!doctype html>
+1
+<script>
+function f() {
+ opener.postMessage("original", "*");
+ if (opener.data.length >= 2) {
+ // If we proceed here, then our document.write will be racing with the
+ // setTimeout in our opener. Just stop.
+ return;
+ }
+ setTimeout(function () {
+ document.open();
+ document.write("<!doctype html>2<script>opener.postMessage('written', '*');<\/script>");
+ document.close();
+ });
+}
+
+window.onload = f
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write.html
new file mode 100644
index 0000000000..905ef88743
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_open_write.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Reload document with document.open and document.written content</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var win = window.open("reload_document_open_write-1.html");
+var t = async_test();
+
+var data = [];
+
+window.onmessage = t.step_func(function(e) {
+ data.push(e.data);
+ if (data.length == 2) {
+ win.location.reload();
+ } else if (data.length >= 3) {
+ setTimeout(t.step_func(function() {
+ assert_array_equals(data, ["original", "written", "original"]);
+ t.done();
+ }), 500);
+ }
+});
+
+add_completion_callback(function() {win.close()});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write-1.html
new file mode 100644
index 0000000000..9a08433920
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write-1.html
@@ -0,0 +1,4 @@
+<script>
+document.write(Math.random());
+opener.postMessage(document.body.innerHTML, "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write.html
new file mode 100644
index 0000000000..fb5fddc7da
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Reload document with document.written content</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var win = window.open("reload_document_write-1.html");
+var t = async_test();
+
+window.onmessage = t.step_func(function(e) {
+ var initial_value = e.data;
+ win.location.reload();
+ window.onmessage = t.step_func(function(e) {
+ assert_not_equals(e.data, initial_value);
+ t.done();
+ });
+});
+
+add_completion_callback(function() {win.close()});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload-1.html
new file mode 100644
index 0000000000..36445af3c8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload-1.html
@@ -0,0 +1,9 @@
+<script>
+function f() {
+ opener.postMessage("original", "*");
+ document.write("<!doctype html>2<script>opener.postMessage('written', '*');<\/script>");
+ document.close();
+}
+
+window.onload = f
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload.html
new file mode 100644
index 0000000000..b2cf31147a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_document_write_onload.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Reload document with document.written content written in load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var win = window.open("reload_document_write_onload-1.html");
+var t = async_test();
+
+var data = [];
+
+window.onmessage = t.step_func(function(e) {
+ data.push(e.data);
+ if (data.length < 3) {
+ win.location.reload();
+ } else {
+ setTimeout(t.step_func(function() {
+ assert_array_equals(data, ["original", "written", "written"]);
+ t.done();
+ }), 500);
+ }
+});
+
+add_completion_callback(function() {win.close()});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_post_1-manual.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_post_1-manual.html
new file mode 100644
index 0000000000..95ef660559
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/reload_post_1-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>Reload document with POST</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var win = window.open("resources/reload_post_1-1.py");
+var t = async_test();
+var posted = false;
+var reloaded = false;
+
+next = t.step_func(function() {
+
+if (posted && !reloaded) {
+ reloaded = true;
+ win.location.reload();
+} else if (posted && reloaded) {
+ t.done();
+} else {
+ posted = true;
+ win.document.forms[0].submit();
+}
+
+});
+
+add_completion_callback(function() {win.close()});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-origin.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-origin.html
new file mode 100644
index 0000000000..a8a614c182
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-origin.html
@@ -0,0 +1,3 @@
+<script>
+parent.postMessage({origin: location.origin}, "*");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-protocol.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-protocol.html
new file mode 100644
index 0000000000..c50d623893
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/post-your-protocol.html
@@ -0,0 +1,4 @@
+<script>
+ var id = location.search.substring(1);
+ parent.postMessage({ id: id, protocol: location.protocol }, "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/reload_post_1-1.py b/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/reload_post_1-1.py
new file mode 100644
index 0000000000..56397f07b4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/resources/reload_post_1-1.py
@@ -0,0 +1,13 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/html")]
+ return headers, u'''
+ <script>
+ onload = function() {opener.next()}
+ document.write(Math.random());
+ </script>
+ <form method="POST" action="">
+ <input type=hidden name=test value=test>
+ <input type=submit>
+ </form>
+ <button onclick="location.reload()">Reload</button>
+ '''
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/same-hash.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/same-hash.html
new file mode 100644
index 0000000000..430a57662a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/same-hash.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Using the location interface to navigate to the same hash as the current one</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/7386">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="i" srcdoc="<div style='height: 200vh'></div><div id='te&lt;st'></div>"></iframe>
+
+<script type="module">
+setup({ explicit_done: true });
+await new Promise(r => window.onload = r);
+
+for (const value of ["#te<st", "te<st", "#te%3Cst", "te%3Cst"]) {
+ promise_test(async t => {
+ t.add_cleanup(() => { i.contentWindow.location.hash = ""; });
+ assert_equals(i.contentWindow.scrollY, 0, "Setup: iframe starts at top");
+
+ i.contentWindow.location.hash = "te<st";
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_greater_than(i.contentWindow.scrollY, i.contentWindow.innerHeight, "First hash assignment scrolls the iframe");
+
+ i.contentWindow.scroll({ top: 0, behavior: "instant" });
+ assert_equals(i.contentWindow.scrollY, 0, "Resetting the scroll position must work");
+
+ i.contentWindow.location.hash = value;
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_equals(i.contentWindow.scrollY, 0, "Reassigning the same hash must not change the scroll position");
+ }, `Using location.hash = "${value}" must not reset scroll position`);
+}
+
+// These don't canonicalize to the current value of location.hash; the post-parsing version of
+// "te<st" is "te%3Cst", uppercase.
+for (const value of ["#te%3cst", "te%3cst"]) {
+ promise_test(async t => {
+ t.add_cleanup(() => { i.contentWindow.location.hash = ""; });
+ assert_equals(i.contentWindow.scrollY, 0, "Setup: iframe starts at top");
+
+ i.contentWindow.location.hash = "te<st";
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_greater_than(i.contentWindow.scrollY, i.contentWindow.innerHeight, "First hash assignment scrolls the iframe");
+
+ i.contentWindow.scroll({ top: 0, behavior: "instant" });
+ assert_equals(i.contentWindow.scrollY, 0, "Resetting the scroll position must work");
+
+ i.contentWindow.location.hash = value;
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_greater_than(i.contentWindow.scrollY, i.contentWindow.innerHeight, "Reassigning the same-ish hash scrolls the iframe");
+ }, `Using location.hash = "${value}" must reset scroll position`);
+}
+
+for (const value of ["about:srcdoc#te<st", "about:srcdoc#te%3cst", "about:srcdoc#te%3Cst"]) {
+ promise_test(async t => {
+ t.add_cleanup(() => { i.contentWindow.location.hash = ""; });
+ assert_equals(i.contentWindow.scrollY, 0, "Setup: iframe starts at top");
+
+ i.contentWindow.location.hash = "te<st";
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_greater_than(i.contentWindow.scrollY, i.contentWindow.innerHeight, "First hash assignment scrolls the iframe");
+
+ i.contentWindow.scroll({ top: 0, behavior: "instant" });
+ assert_equals(i.contentWindow.scrollY, 0, "Resetting the scroll position must work");
+
+ i.contentWindow.location.href = value;
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_greater_than(i.contentWindow.scrollY, i.contentWindow.innerHeight, "Setting href must scroll the iframe");
+ }, `Using location.href = "${value}" must reset scroll position`);
+
+ promise_test(async t => {
+ t.add_cleanup(() => { i.contentWindow.location.hash = ""; });
+ assert_equals(i.contentWindow.scrollY, 0, "Setup: iframe starts at top");
+
+ i.contentWindow.location.hash = "te<st";
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_greater_than(i.contentWindow.scrollY, i.contentWindow.innerHeight, "First hash assignment scrolls the iframe");
+
+ i.contentWindow.scroll({ top: 0, behavior: "instant" });
+ assert_equals(i.contentWindow.scrollY, 0, "Resetting the scroll position must work");
+
+ i.contentWindow.location.assign(value);
+ await delayForFragmentNavigationScrolling(t);
+
+ assert_greater_than(i.contentWindow.scrollY, i.contentWindow.innerHeight, "Setting href must scroll the iframe");
+ }, `Using location.assign("${value}") must reset scroll position`);
+}
+
+function delayForFragmentNavigationScrolling(t) {
+ // Scroll behavior for fragment navigation is set to "auto" in the spec, so we can't guarantee it's instant.
+ // In practice 10 milliseconds seems to be enough.
+ return new Promise(r => t.step_timeout(r, 10));
+}
+
+done();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html
new file mode 100644
index 0000000000..953e696b2a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Same-origin subframe for Location cyclic [[Prototype]] test</title>
+ <link rel="author" title="Jeff Walden" href="http://whereswalden.com/" />
+</head>
+<body>
+<!-- nothing to do, this window should be accessible to the parent frame -->
+<p>Same-origin iframe</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-1.html
new file mode 100644
index 0000000000..9561cabdd1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-1.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<script>
+opener.history_length = history.length;
+</script>
+<a onclick="location = 'scripted_click_assign_during_load-2.html'; return false;" href>Click Here</a>
+<script>
+document.links[0].click()
+</script>
+<p>Filler image to keep the page loading:</p>
+<img src="/images/smiley.png?pipe=trickle(20:d1:r2)">
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-2.html
new file mode 100644
index 0000000000..1bf7f41e00
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<p>This window should close itself and the test result appear in the original window
+<script>
+onload = function() {
+ setTimeout(function() {opener.do_test(history.length); window.close();}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load.html
new file mode 100644
index 0000000000..7ccc6cdc09
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_assign_during_load.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Assignment to location with click during load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The popup blocker must be disabled for this test</p>
+<div id="log"></div>
+<script>
+setup({timeout:3600000});
+var t = async_test();
+var win = window.open("scripted_click_assign_during_load-1.html");
+
+var history_length;
+do_test = t.step_func(function(new_length) {
+ assert_equals(new_length, history_length);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-1.html
new file mode 100644
index 0000000000..05bb42f967
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-1.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<script>
+opener.history_length = history.length;
+</script>
+<a onclick="location.assign('scripted_click_location_assign_during_load-2.html'); return false;" href>Click Here</a>
+<script>
+document.links[0].click()
+</script>
+<p>Filler image to keep the page loading:</p>
+<img>
+<script>
+document.images[0].src = "/images/smiley.png?pipe=trickle(20:d1:r2)&random=" + Math.random()
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-2.html
new file mode 100644
index 0000000000..1bf7f41e00
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<p>This window should close itself and the test result appear in the original window
+<script>
+onload = function() {
+ setTimeout(function() {opener.do_test(history.length); window.close();}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load.html
new file mode 100644
index 0000000000..64f3ff942f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_click_location_assign_during_load.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>location.assign with click during load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The popup blocker must be disabled for this test</p>
+<div id="log"></div>
+<script>
+var t = async_test();
+var win = window.open("scripted_click_location_assign_during_load-1.html");
+
+var history_length;
+do_test = t.step_func(function(new_length) {
+ assert_equals(new_length, history_length + 1);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-1.html
new file mode 100644
index 0000000000..ae07ac5cfc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-1.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<script>
+opener.history_length = history.length;
+</script>
+<form onsubmit="location = 'scripted_form_submit_assign_during_load-2.html'; return false;">
+<input type=submit value="Click Me">
+</form>
+<script>
+document.forms[0].elements[0].click()
+</script>
+<p>Filler image to keep the page loading:</p>
+<img src="/images/smiley.png?pipe=trickle(20:d1:r2)">
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-2.html
new file mode 100644
index 0000000000..1bf7f41e00
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-2.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<p>This window should close itself and the test result appear in the original window
+<script>
+onload = function() {
+ setTimeout(function() {opener.do_test(history.length); window.close();}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load.html
new file mode 100644
index 0000000000..2a6eba63ab
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Assignment to location with form submit during load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>The popup blocker must be disabled for this test</p>
+<div id="log"></div>
+<script>
+setup({timeout:3600000});
+var t = async_test();
+var win = window.open("scripted_form_submit_assign_during_load-1.html");
+
+var history_length;
+do_test = t.step_func(function(new_length) {
+ assert_equals(new_length, history_length);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/security_location_0.htm b/testing/web-platform/tests/html/browsers/history/the-location-interface/security_location_0.htm
new file mode 100644
index 0000000000..f509c23b18
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/security_location_0.htm
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Location interface Security</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#security-location" />
+ <meta name="assert" content="access location object from different origins doesn't raise SECURITY_ERR exception" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Access location object from different origins doesn't raise SECURITY_ERR exception</p>
+ <div id=log></div>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script>
+ var runTest = async_test("Accessing location object from different origins doesn't raise SECURITY_ERR exception").step_func_done(function() {
+ var frame = document.getElementById('testframe');
+ frame.setAttribute('onload', '');
+ frame.contentWindow.location = get_host_info().HTTP_REMOTE_ORIGIN + "/";
+ });
+ </script>
+ <iframe id='testframe' onload="runTest()">Test Frame</iframe>
+ <script>
+ document.getElementById('testframe').setAttribute('src', get_host_info().HTTP_REMOTE_ORIGIN + '/');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload.html b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload.html
new file mode 100644
index 0000000000..367d7eea3e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload.html
@@ -0,0 +1,37 @@
+
+<!doctype html>
+<meta charset=utf-8>
+<title>Navigation in onload handler</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var testFiles = [
+ "navigation-in-onload_form-submission-1.html",
+ "navigation-in-onload_form-submission-iframe.html",
+ "navigation-in-onload_form-submission-dynamic-iframe.html"
+ ]
+
+ var t = async_test();
+
+ function scheduleNextTest() {
+ setTimeout(runNextTest, 0);
+ }
+
+ function runNextTest() {
+ var file = testFiles.shift();
+ if (!file) {
+ t.done();
+ return;
+ }
+
+ window.open(file);
+ }
+
+ function verify(actual, expected, desc) {
+ setTimeout(t.step_func(function() {
+ assert_equals(actual, expected, desc);
+ }), 0);
+ }
+
+</script>
+<body onload="scheduleNextTest();"></body>
diff --git a/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-1.html b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-1.html
new file mode 100644
index 0000000000..2079224467
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Navigation in onload handler through form submission</title>
+ <script>
+ function redirect() {
+ document.querySelector("#redirectionForm").submit();
+ }
+ </script>
+ </head>
+ <body onload="redirect();">
+ <form id="redirectionForm" action="navigation-in-onload_form-submission-2.html" method="get"></form>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-2.html b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-2.html
new file mode 100644
index 0000000000..11fbe2a2ba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-2.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Navigation in onload handler through form submission</title>
+ <script>
+
+ // Verify is called after onload event to ensure history has been stable.
+ function verify() {
+ // Navigation in onload handler through form submission should not
+ // increse history length.
+ var runner = window.top.opener;
+ runner.verify(history.length, 1,
+ "history.length of subtest '" + top.document.title + "'.");
+ runner.scheduleNextTest();
+ setTimeout(window.close.bind(top), 0);
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(verify, 0);">
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-dynamic-iframe.html b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-dynamic-iframe.html
new file mode 100644
index 0000000000..aabae3d770
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-dynamic-iframe.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Navigation in onload handler through form submission in a dynamically created iframe</title>
+ <script>
+ function test() {
+ let testFrame = document.createElement("iframe");
+ testFrame.src = "navigation-in-onload_form-submission-1.html";
+ document.body.appendChild(testFrame);
+ }
+ </script>
+ </head>
+ <body onload="test();">
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-iframe.html b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-iframe.html
new file mode 100644
index 0000000000..5fa786d1f5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/history/the-session-history-of-browsing-contexts/navigation-in-onload_form-submission-iframe.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Navigation in onload handler through form submission in an iframe</title>
+ </head>
+ <body>
+ <iframe id="testFrame" src="navigation-in-onload_form-submission-1.html"></iframe>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_checking-manual.html b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_checking-manual.html
new file mode 100644
index 0000000000..a4a3b41a7d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_checking-manual.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - API_status_CHECKING</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Refresh the page.</li>
+ </ol>
+
+ <div id="log"></div>
+ <script>
+ var t = async_test("checking status test"),
+ cache = window.applicationCache;
+
+ cache.onchecking = t.step_func_done(function() {
+ assert_equals(cache.status, cache.CHECKING, "cache.status should equals cache.CHECKING");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_downloading-manual.html b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_downloading-manual.html
new file mode 100644
index 0000000000..c09d11d787
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_downloading-manual.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - API_status_DOWNLOADING</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Modify the commented part in the manifest file (manifest/clock.manifest) on the server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("downloading status test"),
+ cache = window.applicationCache;
+
+ cache.ondownloading = t.step_func_done(function() {
+ assert_equals(cache.status, cache.DOWNLOADING, "cache.status should equals cache.DOWNLOADING");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_obsolete-manual.html b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_obsolete-manual.html
new file mode 100644
index 0000000000..77005644a2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_obsolete-manual.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - API_status_OBSOLETE</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Remove the manifest file (manifest/clock.manifest) from the server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("obsolete status test"),
+ cache = window.applicationCache;
+
+ cache.onobsolete = t.step_func_done(function() {
+ assert_equals(cache.status, cache.OBSOLETE, "cache.status should equals cache.OBSOLETE");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_updateready-manual.html b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_updateready-manual.html
new file mode 100644
index 0000000000..7e1533374b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_status_updateready-manual.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - API_status_UPDATEREADY</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Modify the commented part in the manifest file (manifest/clock.manifest) on the server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("updateready status test"),
+ cache = window.applicationCache;
+
+ cache.onupdateready = t.step_func_done(function() {
+ assert_equals(cache.status, cache.UPDATEREADY, "cache.status should equals cache.UPDATEREADY");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_swapcache-manual.html b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_swapcache-manual.html
new file mode 100644
index 0000000000..6649d980f6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/application-cache-api/api_swapcache-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - API_swapCache</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Modify the part of comment in manifest file of server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("swapCache method test");
+ var cache = window.applicationCache;
+
+ cache.onupdateready = t.step_func(function() {
+ try {
+ cache.swapCache();
+ t.done();
+ } catch (e) {
+ assert_unreached("swapCache method failed.");
+ }
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_event-manual.https.html b/testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_event-manual.https.html
new file mode 100644
index 0000000000..81cad4f4fe
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_event-manual.https.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Offline Application Cache</title>
+ <link rel="stylesheet" href="../resources/css/result.css">
+ </head>
+ <body>
+ <h1>navigator_online_event</h1>
+
+ <ol>
+ <li>Change the 'work offline' mode.</li>
+ <li>If actual result and expected result are same, then test is <span class="manualpass">Pass</span>, otherwise <span class="manualfail">Fail</span>.</li>
+ </ol>
+
+ <hr>
+
+ <h2>Actual Result</h2>
+ <div id="actualResult">
+ <span id="actualMsg"></span>
+ </div>
+
+ <h2>Expected Result</h2>
+ <div id="expectedResult">
+ <span id="expectedMsg">apply 'work offline': offline event is raised.<p>release 'work offline': online event is raised.</span>
+ </div>
+ <script>
+
+ function showOnline(e) {
+ let msg = 'online event is raised';
+ if (e.target != window)
+ msg += ' (on the WRONG target)';
+ document.getElementById('actualMsg').innerHTML = msg + '.';
+ }
+
+ function showOffline(e) {
+ let msg = 'offline event is raised';
+ if (e.target != window)
+ msg += ' (on the WRONG target)';
+ document.getElementById('actualMsg').innerHTML = msg + '.';
+ }
+
+ window.addEventListener("online", showOnline, false);
+ window.addEventListener("offline", showOffline, false);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_online.https.html b/testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_online.https.html
new file mode 100644
index 0000000000..81547c3fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/browser-state/navigator_online_online.https.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Offline Application Cache - navigator_online_online</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+
+ <script>
+ test(function() {
+ assert_true(navigator.onLine, "onLine test");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/changestonetworkingmodel/original-id.json b/testing/web-platform/tests/html/browsers/offline/changestonetworkingmodel/original-id.json
new file mode 100644
index 0000000000..2f77367c8f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/changestonetworkingmodel/original-id.json
@@ -0,0 +1 @@
+{"original_id":"changesToNetworkingModel"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/introduction-4/event_downloading-manual.html b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_downloading-manual.html
new file mode 100644
index 0000000000..26b003f06e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_downloading-manual.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - Event_downloading</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Modify the commented part of the manifest file (manifest/clock.manifest) on the server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("downloading event test");
+ var cache = window.applicationCache;
+
+ cache.ondownloading = t.done();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/introduction-4/event_error-manual.html b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_error-manual.html
new file mode 100644
index 0000000000..19abb3d6bf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_error-manual.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - Event_error</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Add a dummy file in the manifest file (manifest/clock.manifest).</li>
+ <li>Refresh the page.</li>
+ </ol>
+
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("error event test");
+ var cache = window.applicationCache;
+
+ cache.onerror = t.done();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/introduction-4/event_obsolete-manual.html b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_obsolete-manual.html
new file mode 100644
index 0000000000..cab5e01cc7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_obsolete-manual.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - Event_obsolete</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Remove the manifest file (manifest/clock.manifest) from the server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("obsolete event test");
+ var cache = window.applicationCache;
+
+ cache.onobsolete = t.done();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready-manual.html b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready-manual.html
new file mode 100644
index 0000000000..4de435144d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready-manual.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - Event_updateready</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Modify the commented part in the manifest file (manifest/clock.manifest) on the server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("updateready event test");
+ var cache = window.applicationCache;
+
+ cache.onupdateready = t.done();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready_swapcache-manual.html b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready_swapcache-manual.html
new file mode 100644
index 0000000000..da6cead026
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/introduction-4/event_updateready_swapcache-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html manifest="../resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - Event_updateready_swapCache</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol>
+ <li>Modify the commented part in manifest file (manifest/clock.manifest) on the server.</li>
+ <li>Refresh the page.</li>
+ </ol>
+
+ <div id="log"></div>
+
+ <script>
+ var t = async_test("swapCache method test after updateready event is raised");
+ var cache = window.applicationCache;
+
+ cache.onupdateready = t.step_func(function() {
+ try {
+ cache.swapCache();
+ t.done();
+ } catch (e) {
+ assert_unreached("swapCache method failed.");
+ }
+ })
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/manifest_main_empty-manual.https.html b/testing/web-platform/tests/html/browsers/offline/manifest_main_empty-manual.https.html
new file mode 100644
index 0000000000..317aaa1137
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/manifest_main_empty-manual.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html manifest="resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - manifest_main_empty</title>
+ <link rel="stylesheet" href="resources/css/result.css">
+ </head>
+ <body>
+ <ol>
+ <li>Disable the network connection.</li>
+ <li>Refresh the page.</li>
+ <li>If the page is normally displayed, then test is <span class="manualpass"><b>PASS</b></span>, otherwise <span class="manualfail"><b>FAIL</b></span>.</li>
+ </ol>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/manifest_notchanged_online-manual.https.html b/testing/web-platform/tests/html/browsers/offline/manifest_notchanged_online-manual.https.html
new file mode 100644
index 0000000000..a464b426a7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/manifest_notchanged_online-manual.https.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html manifest="resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - manifest_notchanged_online</title>
+ <script src="resources/js/clock.js"></script>
+ <link rel="stylesheet" href="resources/css/result.css">
+ <link rel="stylesheet" href="resources/css/clock.css">
+ <link rel="stylesheet" href="resources/css/online.css" type="text/css" media="screen">
+ </head>
+ <body>
+ <ol>
+ <li>Remove time element of this html document and not change manifest file.</li>
+ <li>Refresh the page.</li>
+ <li>If the page is normally displayed, then test is <span class="manualpass"><b>PASS</b></span>, otherwise <span class="manualfail"><b>FAIL</b></span>.</li>
+ </ol>
+
+ <p class="connectivity" width="600">The time is: <output id="clock"></output></p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/manifest_section_empty-manual.https.html b/testing/web-platform/tests/html/browsers/offline/manifest_section_empty-manual.https.html
new file mode 100644
index 0000000000..eea2dbba35
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/manifest_section_empty-manual.https.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html manifest="resources/manifest/section_empty.manifest">
+ <head>
+ <title>Offline Application Cache - manifest_section_empty</title>
+ <script src="resources/js/clock.js"></script>
+ <link rel="stylesheet" href="resources/css/result.css">
+ <link rel="stylesheet" href="resources/css/clock.css">
+ <link rel="stylesheet" href="resources/css/online.css" type="text/css" media="screen">
+ </head>
+ <body>
+ <ol>
+ <li>Disable the network connection.</li>
+ <li>Refresh the page.</li>
+ <li>If the time element and colors of result elements are normally displayed, then test is <span class="manualpass"><b>PASS</b></span>, otherwise <span class="manualfail"><b>FAIL</b></span>.</li>
+ </ol>
+
+ <p class="connectivity" width="600">The time is: <output id="clock"></output></p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/manifest_section_many-manual.https.html b/testing/web-platform/tests/html/browsers/offline/manifest_section_many-manual.https.html
new file mode 100644
index 0000000000..9378df1b40
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/manifest_section_many-manual.https.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html manifest="resources/manifest/section_many.manifest">
+ <head>
+ <title>Offline Application Cache - manifest_section_many</title>
+ <script src="resources/js/clock.js"></script>
+ <link rel="stylesheet" href="resources/css/result.css">
+ <link rel="stylesheet" href="resources/css/clock.css">
+ <link rel="stylesheet" href="resources/css/online.css" type="text/css" media="screen">
+ </head>
+ <body>
+ <ol type="1">
+ <li>Disable the network connection.</li>
+ <li>Refresh the page.</li>
+ <li>If the time element and colors of result elements are normally displayed, then test is <span class="manualpass"><b>PASS</b></span>, otherwise <span class="manualfail"><b>FAIL</b></span>.</li>
+ </ol>
+
+ <p class="connectivity" width="600">The time is: <output id="clock"></output></p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/css/clock.css b/testing/web-platform/tests/html/browsers/offline/resources/css/clock.css
new file mode 100644
index 0000000000..fa406d0fbe
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/css/clock.css
@@ -0,0 +1 @@
+output { font: 1em sans-serif; } \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/css/offline.css b/testing/web-platform/tests/html/browsers/offline/resources/css/offline.css
new file mode 100644
index 0000000000..76b7f39853
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/css/offline.css
@@ -0,0 +1,5 @@
+.connectivity {
+ color: #fff;
+ background: red;
+ padding: 20px;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/css/online.css b/testing/web-platform/tests/html/browsers/offline/resources/css/online.css
new file mode 100644
index 0000000000..39efcb2ab4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/css/online.css
@@ -0,0 +1,5 @@
+.connectivity {
+ color: #fff;
+ background: blue;
+ padding: 20px;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/css/result.css b/testing/web-platform/tests/html/browsers/offline/resources/css/result.css
new file mode 100644
index 0000000000..7d784b8ab5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/css/result.css
@@ -0,0 +1,11 @@
+.manualpass {
+ color: green;
+}
+.manualfail {
+ color: red;
+}.pass {
+ color: green;
+}
+.fail {
+ color: red;
+}
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/html/clock.html b/testing/web-platform/tests/html/browsers/offline/resources/html/clock.html
new file mode 100644
index 0000000000..6b8949a6b1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/html/clock.html
@@ -0,0 +1,12 @@
+<!-- clock.html -->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Clock</title>
+ <script src="../js/clock.js"></script>
+ <link rel="stylesheet" href="../css/clock.css">
+ </head>
+ <body>
+ <p>The time is: <output id="clock"></output></p>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/js/clock.js b/testing/web-platform/tests/html/browsers/offline/resources/js/clock.js
new file mode 100644
index 0000000000..1ac0dca539
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/js/clock.js
@@ -0,0 +1,3 @@
+setTimeout(function () {
+ document.getElementById('clock').value = new Date();
+}, 1000); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/manifest/clock.manifest b/testing/web-platform/tests/html/browsers/offline/resources/manifest/clock.manifest
new file mode 100644
index 0000000000..a61aae6c61
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/manifest/clock.manifest
@@ -0,0 +1,17 @@
+CACHE MANIFEST
+
+# Version 1
+
+CACHE:
+../css/clock.css
+../js/clock.js
+../css/result.css
+../css/offline.css
+/resources/testharness.js
+/resources/testharnessreport.js
+
+NETWORK:
+../html/clock.html
+
+FALLBACK:
+../css/online.css ../css/offline.css \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/manifest/section_empty.manifest b/testing/web-platform/tests/html/browsers/offline/resources/manifest/section_empty.manifest
new file mode 100644
index 0000000000..a23b9013be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/manifest/section_empty.manifest
@@ -0,0 +1,10 @@
+CACHE MANIFEST
+
+# Version 1
+
+../css/clock.css
+../js/clock.js
+../css/result.css
+../css/online.css
+/resources/testharness.js
+/resources/testharnessreport.js \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/manifest/section_many.manifest b/testing/web-platform/tests/html/browsers/offline/resources/manifest/section_many.manifest
new file mode 100644
index 0000000000..7e5e5e9995
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/manifest/section_many.manifest
@@ -0,0 +1,19 @@
+CACHE MANIFEST
+
+# Version 1
+
+CACHE:
+../css/clock.css
+../js/clock.js
+
+CACHE:
+../css/result.css
+../css/offline.css
+/resources/testharness.js
+/resources/testharnessreport.js
+
+NETWORK:
+../html/clock.html
+
+FALLBACK:
+../css/online.css ../css/offline.css \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/resources/manifest/url_check.manifest b/testing/web-platform/tests/html/browsers/offline/resources/manifest/url_check.manifest
new file mode 100644
index 0000000000..041df5e55f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/resources/manifest/url_check.manifest
@@ -0,0 +1,17 @@
+CACHE MANIFEST
+
+# Version 1
+
+CACHE:
+../css/cl#ock.css
+../js/clock.js
+../css/result.css
+../css/offline.css
+/resources/testharness.js
+/resources/testharnessreport.js
+
+NETWORK:
+../html/clock.html
+
+FALLBACK:
+../css/online.css ../css/offline.css \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/offline/section_network_offline-manual.https.html b/testing/web-platform/tests/html/browsers/offline/section_network_offline-manual.https.html
new file mode 100644
index 0000000000..c4121f5bc5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/section_network_offline-manual.https.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html manifest="resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - Section_network_offline</title>
+ <link rel="stylesheet" href="resources/css/result.css">
+ </head>
+ <body>
+ <ol>
+ <li>Disable the network connection.</li>
+ <li>Refresh the page.</li>
+ <li>If only the frame element can't be loaded, then test is <span class="manualpass"><b>PASS</b></span>, otherwise <span class="manualfail"><b>FAIL</b></span>.</li>
+ </ol>
+
+ <IFRAME id="TestFrame" name="TestWindow" src="html/clock.html" width="600" height="50" scrolling="auto" frameborder="1">
+ </IFRAME>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/offline/section_network_online-manual.https.html b/testing/web-platform/tests/html/browsers/offline/section_network_online-manual.https.html
new file mode 100644
index 0000000000..a5d8e59406
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/offline/section_network_online-manual.https.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html manifest="resources/manifest/clock.manifest">
+ <head>
+ <title>Offline Application Cache - Section_network_online</title>
+ <link rel="stylesheet" href="resources/css/result.css">
+ </head>
+ <body>
+ <ol>
+ <li>Refresh the page.</li>
+ <li>If the frame element is loaded, then test is <span class="manualpass"><b>PASS</b></span>, otherwise <span class="manualfail"><b>FAIL</b></span>.</li>
+ </ol>
+
+ <IFRAME id="TestFrame" name="TestWindow" src="html/clock.html" width="600" height="50" scrolling="auto" frameborder="1">
+ </IFRAME>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-due-to-document-domain-only.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-due-to-document-domain-only.html
new file mode 100644
index 0000000000..425374faec
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-due-to-document-domain-only.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>Cross-origin due to document.domain</title>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe src=resources/cross-origin-due-to-document-domain-only-helper.html></iframe>
+<script>
+async_test(t => {
+ onload = t.step_func_done(() => {
+ const frame = document.querySelector("iframe");
+ const innerSelf = self[0];
+ const innerLocation = innerSelf.location;
+ const innerDocument = innerSelf.document;
+ assert_equals(innerLocation.host, location.host);
+ assert_true(innerSelf.expandosForever);
+ assert_true(innerLocation.expandosForever);
+ assert_equals(frame.contentWindow, innerSelf);
+ assert_equals(frame.contentDocument, innerDocument);
+ innerSelf.setDocumentDomain();
+ assert_throws_dom("SecurityError", () => innerSelf.expandosForever);
+ assert_throws_dom("SecurityError", () => innerLocation.expandosForever);
+ assert_throws_dom("SecurityError", () => innerLocation.host);
+ assert_equals(innerSelf.parent, self);
+ assert_throws_dom("SecurityError", () => innerSelf.frameElement);
+ assert_throws_dom("SecurityError", () => innerLocation.reload());
+ assert_equals(frame.contentWindow, innerSelf);
+ assert_equals(frame.contentDocument, null);
+ // Cross-origin Document object obtained before it became cross-origin has no protections
+ assert_equals(innerDocument.URL, frame.src);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-caching.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-caching.html
new file mode 100644
index 0000000000..a8af18d106
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-caching.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Cross-origin methods and accessors are cached per Realm via[[CrossOriginPropertyDescriptorMap]]</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="cross-origin-objects-function-common.js"></script>
+<div id=log></div>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ for (const {key} of crossOriginWindowMethods) {
+ assert_equals(w[key], w[key], `w.${key} via [[Get]]`);
+ const desc1 = Object.getOwnPropertyDescriptor(w, key);
+ const desc2 = Object.getOwnPropertyDescriptor(w, key);
+ assert_equals(desc1.value, desc2.value, `w.${key} via [[GetOwnProperty]]`);
+ }
+}, "Cross-origin Window methods are cached");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ for (const {key} of crossOriginWindowAccessors) {
+ const desc1 = Object.getOwnPropertyDescriptor(w, key);
+ const desc2 = Object.getOwnPropertyDescriptor(w, key);
+ assert_equals(desc1.get, desc2.get, `w.${key} getter`);
+ if (key === "location") {
+ assert_equals(desc1.set, desc2.set, `w.${key} setter`);
+ }
+ }
+}, "Cross-origin Window accessors are cached");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ assert_equals(w.location.replace, w.location.replace, "via [[Get]]");
+ const desc1 = Object.getOwnPropertyDescriptor(w.location, "replace");
+ const desc2 = Object.getOwnPropertyDescriptor(w.location, "replace");
+ assert_equals(desc1.value, desc2.value, "via [[GetOwnProperty]]");
+}, "Cross-origin Location `replace` method is cached");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ const desc1 = Object.getOwnPropertyDescriptor(w.location, "href");
+ const desc2 = Object.getOwnPropertyDescriptor(w.location, "href");
+ assert_equals(desc1.set, desc2.set);
+}, "Cross-origin Location `href` setter is cached");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-common.js b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-common.js
new file mode 100644
index 0000000000..3b93b498a2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-common.js
@@ -0,0 +1,34 @@
+"use strict";
+
+const crossOriginWindowMethods = [
+ {key: "close", length: 0},
+ {key: "focus", length: 0},
+ {key: "blur", length: 0},
+ {key: "postMessage", length: 1},
+];
+
+const crossOriginWindowAccessors = [
+ "window",
+ "self",
+ "location",
+ "closed",
+ "frames",
+ "length",
+ "top",
+ "opener",
+ "parent",
+].map(key => ({key}));
+
+const makeCrossOriginWindow = t => {
+ const iframe = document.createElement("iframe");
+ const path = location.pathname.slice(0, location.pathname.lastIndexOf("/")) + "/frame.html";
+ iframe.src = get_host_info().HTTP_REMOTE_ORIGIN + path;
+
+ return new Promise((resolve, reject) => {
+ iframe.onload = () => { resolve(iframe.contentWindow); };
+ iframe.onerror = reject;
+
+ document.body.append(iframe);
+ t.add_cleanup(() => { iframe.remove(); });
+ });
+};
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-length.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-length.html
new file mode 100644
index 0000000000..466915a461
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-length.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Cross-origin methods and accessors are created with correct 'length' property</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="cross-origin-objects-function-common.js"></script>
+<div id=log></div>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ for (const {key, length} of crossOriginWindowMethods) {
+ assert_equals(w[key].length, length, `w.${key} via [[Get]]`);
+ const desc = Object.getOwnPropertyDescriptor(w, key);
+ assert_equals(desc.value.length, length, `w.${key} via [[GetOwnProperty]]`);
+ }
+}, "Cross-origin Window methods have correct 'length'");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ for (const {key} of crossOriginWindowAccessors) {
+ const desc = Object.getOwnPropertyDescriptor(w, key);
+ assert_equals(desc.get.length, 0, `w.${key}`);
+ if (key === "location") {
+ assert_equals(desc.set.length, 1, `w.${key}`);
+ }
+ }
+}, "Cross-origin Window accessors have correct 'length'");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ assert_equals(w.location.replace.length, 1);
+ const desc = Object.getOwnPropertyDescriptor(w.location, "replace");
+ assert_equals(desc.value.length, 1);
+}, "Cross-origin Location `replace` method has correct 'length'");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ const desc = Object.getOwnPropertyDescriptor(w.location, "href");
+ assert_equals(desc.set.length, 1);
+}, "Cross-origin Location `href` setter has correct 'length'");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-name.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-name.html
new file mode 100644
index 0000000000..167c30e8f3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-name.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Cross-origin methods and accessors are created with correct 'name' property</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="cross-origin-objects-function-common.js"></script>
+<div id=log></div>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ for (const {key} of crossOriginWindowMethods) {
+ assert_equals(w[key].name, key, `w.${key} via [[Get]]`);
+ const desc = Object.getOwnPropertyDescriptor(w, key);
+ assert_equals(desc.value.name, key, `w.${key} via [[GetOwnProperty]]`);
+ }
+}, "Cross-origin Window methods have correct 'name'");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ for (const {key} of crossOriginWindowAccessors) {
+ const desc = Object.getOwnPropertyDescriptor(w, key);
+ assert_equals(desc.get.name, `get ${key}`);
+ if (key === "location") {
+ assert_equals(desc.set.name, `set ${key}`);
+ }
+ }
+}, "Cross-origin Window accessors have correct 'name'");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ assert_equals(w.location.replace.name, "replace");
+ const desc = Object.getOwnPropertyDescriptor(w.location, "replace");
+ assert_equals(desc.value.name, "replace");
+}, "Cross-origin Location `replace` method has correct 'name'");
+
+promise_test(async t => {
+ const w = await makeCrossOriginWindow(t);
+ const desc = Object.getOwnPropertyDescriptor(w.location, "href");
+ assert_equals(desc.set.name, "set href");
+}, "Cross-origin Location `href` setter has correct 'name'");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html
new file mode 100644
index 0000000000..3ad0de6a3a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>Cross-origin behavior of Window and Location on new Window</title>
+<link rel="author" title="Bobby Holley (:bholley)" href="bobbyholley@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-location">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+setup({explicit_done: true});
+
+window.addEventListener('message', function onmessage(evt) {
+ window.removeEventListener('message', onmessage);
+ test(function() {
+ var results = evt.data;
+ assert_true(results.length > 0, 'Need results');
+ results.forEach(function(r) { assert_true(r.pass, r.message); });
+ }, "Cross-origin object identity preserved across document.domain");
+ win.close();
+ done();
+});
+var win = window.open('win-documentdomain.sub.html');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
new file mode 100644
index 0000000000..d1b6cabc0f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -0,0 +1,718 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>Cross-origin behavior of Window and Location</title>
+<link rel="author" title="Bobby Holley (:bholley)" href="bobbyholley@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-location">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<iframe id="B"></iframe>
+<iframe id="C"></iframe>
+<iframe id="D"></iframe>
+<iframe id="E"></iframe>
+<iframe id="F"></iframe>
+<iframe id="G"></iframe>
+<iframe id="H"></iframe>
+<script>
+
+/*
+ * Setup boilerplate. This gives us a same-origin window "B", cross-origin
+ * windows "C" and "D", initially same-origin but then changing document.domain
+ * windows "E" and "F", and not-same-site (also cross-origin, of course) windows
+ * "G" and "H".
+ */
+var host_info = get_host_info();
+
+setup({explicit_done: true});
+path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame.html';
+pathWithThen = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame-with-then.html';
+var B = document.getElementById('B').contentWindow;
+var C = document.getElementById('C').contentWindow;
+var D = document.getElementById('D').contentWindow;
+var E = document.getElementById('E').contentWindow;
+var F = document.getElementById('F').contentWindow;
+var G = document.getElementById('G').contentWindow;
+var H = document.getElementById('H').contentWindow;
+B.frameElement.uriToLoad = path;
+C.frameElement.uriToLoad = get_host_info().HTTP_REMOTE_ORIGIN + path;
+D.frameElement.uriToLoad = get_host_info().HTTP_REMOTE_ORIGIN + pathWithThen;
+E.frameElement.uriToLoad = path + "?setdomain";
+F.frameElement.uriToLoad = pathWithThen + "?setdomain";
+G.frameElement.uriToLoad = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path;
+H.frameElement.uriToLoad = get_host_info().HTTP_NOTSAMESITE_ORIGIN + pathWithThen;
+
+function winName(win) {
+ var iframes = document.getElementsByTagName('iframe');
+ iframes.find = Array.prototype.find;
+ var found = iframes.find(function (ifr) {
+ return ifr.contentWindow == win;
+ });
+ if (found) {
+ return found.id;
+ }
+ return "UNKNOWN";
+}
+
+function reloadSubframes(cb) {
+ var iframes = document.getElementsByTagName('iframe');
+ iframes.forEach = Array.prototype.forEach;
+ var count = 0;
+ function frameLoaded() {
+ this.onload = null;
+ if (++count == iframes.length)
+ cb();
+ }
+ iframes.forEach(function(ifr) { ifr.onload = frameLoaded; ifr.setAttribute('src', ifr.uriToLoad); });
+}
+function isObject(x) { return Object(x) === x; }
+
+/*
+ * Note: we eschew assert_equals in a lot of these tests, since the harness ends
+ * up throwing when it tries to format a message involving a cross-origin object.
+ */
+
+/*
+ * List of tests. Each test is actually a pair: an array of tests to run and a
+ * boolean for whether these are promise tests. We reload all the subframes in
+ * between running each toplevel test. This is done to avoid having to reload
+ * all the subframes for every single test, which is overkill: some of these
+ * tests are known to touch only one subframe. And doing it makes the test
+ * really slow.
+ */
+var testList = [];
+function addTest(func, desc) {
+ testList.push(
+ {tests: [
+ {func: func.bind(null, C),
+ desc: desc + " (cross-origin)"},
+ {func: func.bind(null, E),
+ desc: desc + " (same-origin + document.domain)"},
+ {func: func.bind(null, G),
+ desc: desc + " (cross-site)"}],
+ promiseTest: false});
+}
+
+function addPromiseTest(func, desc) {
+ testList.push(
+ {tests: [
+ {func: func.bind(null, C),
+ desc: desc + " (cross-origin)"},
+ {func: func.bind(null, E),
+ desc: desc + " (same-origin + document.domain)"},
+ {func: func.bind(null, G),
+ desc: desc + " (cross-site)"}],
+ promiseTest: true});
+}
+
+/**
+ * Similar helpers, but for the subframes that load frame-with-then.html
+ */
+function addThenTest(func, desc) {
+ testList.push(
+ {tests: [
+ {func: func.bind(null, D),
+ desc: desc + " (cross-origin)"},
+ {func: func.bind(null, F),
+ desc: desc + " (same-origin + document.domain)"},
+ {func: func.bind(null, H),
+ desc: desc + " (cross-site)"}],
+ promiseTest: false});
+}
+
+function addPromiseThenTest(func, desc) {
+ testList.push(
+ {tests: [
+ {func: func.bind(null, D),
+ desc: desc + " (cross-origin)"},
+ {func: func.bind(null, F),
+ desc: desc + " (same-origin + document.domain)"},
+ {func: func.bind(null, H),
+ desc: desc + " (cross-site)"}],
+ promiseTest: true});
+}
+
+/*
+ * Basic smoke tests for same-origin and cross-origin behaviors.
+ */
+
+addTest(function(win) {
+ // Note: we do not check location.host as its default port semantics are hard to reflect statically
+ assert_equals(location.hostname, host_info.ORIGINAL_HOST, 'Need to run the top-level test from domain ' + host_info.ORIGINAL_HOST);
+ assert_equals(get_port(location), host_info.HTTP_PORT, 'Need to run the top-level test from port ' + host_info.HTTP_PORT);
+ assert_equals(B.parent, window, "window.parent works same-origin");
+ assert_equals(win.parent, window, "window.parent works cross-origin");
+ assert_equals(B.location.pathname, path, "location.href works same-origin");
+ assert_throws_dom("SecurityError", function() { win.location.pathname; }, "location.pathname throws cross-origin");
+ assert_equals(B.frames, 'override', "Overrides visible in the same-origin case");
+ assert_equals(win.frames, win, "Overrides invisible in the cross-origin case");
+ assert_equals(B.focus, 'override', "Overrides visible in the same-origin case");
+ checkFunction(win.focus, Function.prototype);
+ assert_not_equals(win.focus, focus, "Overrides invisible in the cross-origin case");
+}, "Basic sanity-checking");
+
+/*
+ * Tests regarding which properties are allowed cross-origin.
+ *
+ * Also tests for [[GetOwnProperty]] and [[HasOwnProperty]] behavior.
+ */
+
+var allowedSymbols = [Symbol.toStringTag, Symbol.hasInstance,
+ Symbol.isConcatSpreadable];
+var windowAllowlists = {
+ namedFrames: ['donotleakme'],
+ indices: ['0', '1'],
+ getters: ['location', 'window', 'frames', 'self', 'top', 'parent',
+ 'opener', 'closed', 'length'],
+ setters: ['location'],
+ methods: ['postMessage', 'close', 'blur', 'focus'],
+ // These are methods which return promises and, therefore, when called with a
+ // cross-origin `this` object, do not throw immediately, but instead return a
+ // Promise which rejects with the same SecurityError that they would
+ // otherwise throw. They are not, however, cross-origin accessible.
+ promiseMethods: ['createImageBitmap', 'fetch'],
+}
+windowAllowlists.propNames = Array.from(new Set([...windowAllowlists.indices,
+ ...windowAllowlists.getters,
+ ...windowAllowlists.setters,
+ ...windowAllowlists.methods,
+ 'then'])).sort();
+windowAllowlists.props = windowAllowlists.propNames.concat(allowedSymbols);
+
+var locationAllowlists = {
+ getters: [],
+ setters: ['href'],
+ methods: ['replace'],
+ promiseMethods: [],
+}
+locationAllowlists.propNames = Array.from(new Set([...locationAllowlists.getters,
+ ...locationAllowlists.setters,
+ ...locationAllowlists.methods,
+ 'then'])).sort();
+
+// Define various sets of arguments to call cross-origin methods with. Arguments
+// for any cross-origin-callable method must be valid, and should aim to have no
+// side-effects. Any method without an entry in this list will be called with
+// an empty arguments list.
+var methodArgs = new Map(Object.entries({
+ // As a basic smoke test, we call one cross-origin-inaccessible method with
+ // both valid and invalid arguments to make sure that it rejects with the
+ // same SecurityError regardless.
+ assign: [
+ [],
+ ["javascript:undefined"],
+ ],
+ // Note: If we post a message to frame.html with a matching origin, its
+ // "onmessage" handler will change its `document.domain`, and potentially
+ // invalidate subsequent tests, so be sure to only pass non-matching origins.
+ postMessage: [
+ ["foo", "http://does-not.exist/"],
+ ["foo", {}],
+ ],
+ replace: [["javascript:undefined"]],
+}));
+
+addTest(function(win) {
+ for (var prop in window) {
+ if (windowAllowlists.props.indexOf(prop) != -1) {
+ win[prop]; // Shouldn't throw.
+ Object.getOwnPropertyDescriptor(win, prop); // Shouldn't throw.
+ assert_true(Object.prototype.hasOwnProperty.call(win, prop), "hasOwnProperty for " + String(prop));
+ } else {
+ assert_throws_dom("SecurityError", function() { win[prop]; }, "Should throw when accessing " + String(prop) + " on Window");
+ assert_throws_dom("SecurityError", function() { Object.getOwnPropertyDescriptor(win, prop); },
+ "Should throw when accessing property descriptor for " + prop + " on Window");
+ assert_throws_dom("SecurityError", function() { Object.prototype.hasOwnProperty.call(win, prop); },
+ "Should throw when invoking hasOwnProperty for " + prop + " on Window");
+ }
+ if (prop != 'location')
+ assert_throws_dom("SecurityError", function() { win[prop] = undefined; }, "Should throw when writing to " + prop + " on Window");
+ }
+ for (var prop of windowAllowlists.namedFrames) {
+ win[prop]; // Shouldn't throw.
+ var desc = Object.getOwnPropertyDescriptor(win, prop);
+ assert_false(desc.writable, "[[Writable]] for named frame " + String(prop));
+ assert_false(desc.enumerable, "[[Enumerable]] for named frame " + String(prop));
+ assert_true(desc.configurable, "[[Configurable]] for named frame " + String(prop));
+ assert_true(Object.prototype.hasOwnProperty.call(win, prop), "hasOwnProperty for " + String(prop));
+ }
+ for (var prop in location) {
+ if (prop == 'replace') {
+ win.location[prop]; // Shouldn't throw.
+ Object.getOwnPropertyDescriptor(win.location, prop); // Shouldn't throw.
+ assert_true(Object.prototype.hasOwnProperty.call(win.location, prop), "hasOwnProperty for " + prop);
+ assert_throws_dom("SecurityError", function() { win.location[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
+ }
+ else if (prop == 'href') {
+ Object.getOwnPropertyDescriptor(win.location, prop); // Shouldn't throw.
+ assert_true(Object.prototype.hasOwnProperty.call(win.location, prop), "hasOwnProperty for " + prop);
+ assert_throws_dom("SecurityError", function() { win.location[prop] },
+ "Should throw reading href on Location");
+ }
+ else {
+ assert_throws_dom("SecurityError", function() { win.location[prop]; }, "Should throw when accessing " + prop + " on Location");
+ assert_throws_dom("SecurityError", function() { Object.getOwnPropertyDescriptor(win.location, prop); },
+ "Should throw when accessing property descriptor for " + prop + " on Location");
+ assert_throws_dom("SecurityError", function() { Object.prototype.hasOwnProperty.call(win.location, prop); },
+ "Should throw when invoking hasOwnProperty for " + prop + " on Location");
+ assert_throws_dom("SecurityError", function() { win.location[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
+ }
+ }
+}, "Only certain properties are accessible cross-origin");
+
+addPromiseTest(async function(win, test_obj) {
+ async function checkProperties(objName, allowedlists) {
+ var localObj = window[objName];
+ var otherObj = win[objName];
+
+ for (var prop in localObj) {
+ let desc;
+ for (let obj = localObj; !desc; obj = Object.getPrototypeOf(obj)) {
+ desc = Object.getOwnPropertyDescriptor(obj, prop);
+
+ }
+
+ if ("value" in desc) {
+ if (typeof desc.value === "function" && String(desc.value).includes("[native code]")) {
+ if (allowedlists.promiseMethods.includes(prop)) {
+ await promise_rejects_dom(test_obj, "SecurityError", desc.value.call(otherObj),
+ `Should throw when calling ${objName}.${prop} with cross-origin this object`);
+ } else if (!allowedlists.methods.includes(prop)) {
+ for (let args of methodArgs.get(prop) || [[]]) {
+ assert_throws_dom("SecurityError", desc.value.bind(otherObj, ...args),
+ `Should throw when calling ${objName}.${prop} with cross-origin this object`);
+ }
+
+ } else {
+ for (let args of methodArgs.get(prop) || [[]]) {
+ desc.value.apply(otherObj, args); // Shouldn't throw.
+ }
+ }
+ }
+ } else {
+ if (desc.get) {
+ if (allowedlists.getters.includes(prop)) {
+ desc.get.call(otherObj); // Shouldn't throw.
+ } else {
+ assert_throws_dom("SecurityError", desc.get.bind(otherObj),
+ `Should throw when calling ${objName}.${prop} getter with cross-origin this object`);
+ }
+ }
+ if (desc.set) {
+ if (allowedlists.setters.includes(prop)) {
+ desc.set.call(otherObj, "javascript:undefined"); // Shouldn't throw.
+ } else {
+ assert_throws_dom("SecurityError", desc.set.bind(otherObj, "foo"),
+ `Should throw when calling ${objName}.${prop} setter with cross-origin this object`);
+ }
+ }
+ }
+ }
+ }
+
+ await checkProperties("location", locationAllowlists);
+ await checkProperties("window", windowAllowlists);
+}, "Only certain properties are usable as cross-origin this objects");
+
+/*
+ * ES Internal Methods.
+ */
+
+/*
+ * [[GetPrototypeOf]]
+ */
+addTest(function(win) {
+ assert_equals(Object.getPrototypeOf(win), null, "cross-origin Window proto is null");
+ assert_equals(Object.getPrototypeOf(win.location), null, "cross-origin Location proto is null (__proto__)");
+ var protoGetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').get;
+ assert_equals(protoGetter.call(win), null, "cross-origin Window proto is null");
+ assert_equals(protoGetter.call(win.location), null, "cross-origin Location proto is null (__proto__)");
+ assert_throws_dom("SecurityError", function() { win.__proto__; }, "__proto__ property not available cross-origin");
+ assert_throws_dom("SecurityError", function() { win.location.__proto__; }, "__proto__ property not available cross-origin");
+
+}, "[[GetPrototypeOf]] should return null");
+
+/*
+ * [[SetPrototypeOf]]
+ */
+addTest(function(win) {
+ assert_throws_dom("SecurityError", function() { win.__proto__ = new Object(); }, "proto set on cross-origin Window");
+ assert_throws_dom("SecurityError", function() { win.location.__proto__ = new Object(); }, "proto set on cross-origin Location");
+ var setters = [Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set];
+ if (Object.setPrototypeOf)
+ setters.push(function(p) { Object.setPrototypeOf(this, p); });
+ setters.forEach(function(protoSetter) {
+ assert_throws_js(TypeError, function() { protoSetter.call(win, new Object()); }, "proto setter |call| on cross-origin Window");
+ assert_throws_js(TypeError, function() { protoSetter.call(win.location, new Object()); }, "proto setter |call| on cross-origin Location");
+ });
+ // Hack to avoid "duplicate test name" harness issues.
+ setters.forEach(function(protoSetter) {
+ test(function() { protoSetter.call(win, null); },
+ "proto setter |call| on cross-origin Window with null (" + protoSetter + ", " + winName(win) + ")");
+ test(function() { protoSetter.call(win.location, null); },
+ "proto setter |call| on cross-origin Location with null (" + protoSetter + ", " + winName(win) + ")");
+ });
+ if (Reflect.setPrototypeOf) {
+ assert_false(Reflect.setPrototypeOf(win, new Object()),
+ "Reflect.setPrototypeOf on cross-origin Window");
+ assert_true(Reflect.setPrototypeOf(win, null),
+ "Reflect.setPrototypeOf on cross-origin Window with null");
+ assert_false(Reflect.setPrototypeOf(win.location, new Object()),
+ "Reflect.setPrototypeOf on cross-origin Location");
+ assert_true(Reflect.setPrototypeOf(win.location, null),
+ "Reflect.setPrototypeOf on cross-origin Location with null");
+ }
+}, "[[SetPrototypeOf]] should return false");
+
+/*
+ * [[IsExtensible]]
+ */
+addTest(function(win) {
+ assert_true(Object.isExtensible(win), "cross-origin Window should be extensible");
+ assert_true(Object.isExtensible(win.location), "cross-origin Location should be extensible");
+}, "[[IsExtensible]] should return true for cross-origin objects");
+
+/*
+ * [[PreventExtensions]]
+ */
+addTest(function(win) {
+ assert_throws_js(TypeError, function() { Object.preventExtensions(win) },
+ "preventExtensions on cross-origin Window should throw");
+ assert_throws_js(TypeError, function() { Object.preventExtensions(win.location) },
+ "preventExtensions on cross-origin Location should throw");
+ assert_false(Reflect.preventExtensions(win),
+ "Reflect.preventExtensions on cross-origin Window");
+ assert_false(Reflect.preventExtensions(win.location),
+ "Reflect.preventExtensions on cross-origin Location");
+}, "[[PreventExtensions]] should return false cross-origin objects");
+
+/*
+ * [[GetOwnProperty]]
+ */
+
+addTest(function(win) {
+ assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'close')), "win.close is |own|");
+ assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'top')), "win.top is |own|");
+ assert_true(isObject(Object.getOwnPropertyDescriptor(win.location, 'href')), "win.location.href is |own|");
+ assert_true(isObject(Object.getOwnPropertyDescriptor(win.location, 'replace')), "win.location.replace is |own|");
+}, "[[GetOwnProperty]] - Properties on cross-origin objects should be reported |own|");
+
+function checkPropertyDescriptor(desc, propName, expectWritable) {
+ const isSymbol = typeof(propName) === "symbol";
+ const isArrayIndexPropertyName = !isSymbol && !isNaN(parseInt(propName, 10));
+ propName = String(propName);
+ assert_true(isObject(desc), "property descriptor for " + propName + " should exist");
+ assert_equals(desc.configurable, true, "property descriptor for " + propName + " should be configurable");
+ if (!isArrayIndexPropertyName) {
+ assert_equals(desc.enumerable, false, "property descriptor for " + propName + " should not be enumerable");
+ if (isSymbol || propName == "then") {
+ assert_true("value" in desc,
+ "property descriptor for " + propName + " should be a value descriptor");
+ assert_equals(desc.value, undefined,
+ "symbol-named cross-origin visible prop " + propName +
+ " should come back as undefined");
+ }
+ } else {
+ assert_equals(desc.enumerable, true, "property descriptor for " + propName + " should be enumerable");
+ }
+ if ('value' in desc)
+ assert_equals(desc.writable, expectWritable, "property descriptor for " + propName + " should have writable: " + expectWritable);
+ else
+ assert_equals(typeof desc.set != 'undefined', expectWritable,
+ "property descriptor for " + propName + " should " + (expectWritable ? "" : "not ") + "have setter");
+}
+
+addTest(function(win) {
+ windowAllowlists.props.forEach(function(prop) {
+ var desc = Object.getOwnPropertyDescriptor(win, prop);
+ checkPropertyDescriptor(desc, prop, prop == 'location');
+ });
+ checkPropertyDescriptor(Object.getOwnPropertyDescriptor(win.location, 'replace'), 'replace', false);
+ checkPropertyDescriptor(Object.getOwnPropertyDescriptor(win.location, 'href'), 'href', true);
+ assert_equals(typeof Object.getOwnPropertyDescriptor(win.location, 'href').get, 'undefined', "Cross-origin location should have no href getter");
+ allowedSymbols.forEach(function(prop) {
+ var desc = Object.getOwnPropertyDescriptor(win.location, prop);
+ checkPropertyDescriptor(desc, prop, false);
+ });
+}, "[[GetOwnProperty]] - Property descriptors for cross-origin properties should be set up correctly");
+
+addThenTest(function(win) {
+ assert_equals(typeof win.then, "object");
+}, "[[GetOwnProperty]] - Subframe named 'then' should shadow the default 'then' value");
+
+addThenTest(function(win) {
+ assert_equals(typeof win.close, "function");
+ assert_equals(typeof win.open, "object");
+}, "[[GetOwnProperty]] - Subframes should be visible cross-origin only if their names don't match the names of cross-origin-exposed IDL properties");
+
+addTest(function(win) {
+ assert_equals(typeof Object.getOwnPropertyDescriptor(win, '0').value, "object");
+ assert_equals(typeof Object.getOwnPropertyDescriptor(win, '1').value, "object");
+ assert_throws_dom("SecurityError", function() {
+ Object.getOwnPropertyDescriptor(win, '2');
+ });
+}, "[[GetOwnProperty]] - Should be able to get a property descriptor for an indexed property only if it corresponds to a child window.");
+
+/*
+ * [[Delete]]
+ */
+addTest(function(win) {
+ assert_throws_dom("SecurityError", function() { delete win[0]; }, "Can't delete cross-origin indexed property");
+ assert_throws_dom("SecurityError", function() { delete win[100]; }, "Can't delete cross-origin indexed property");
+ assert_throws_dom("SecurityError", function() { delete win.location; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.parent; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.length; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.document; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.foopy; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.location.href; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.location.replace; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.location.port; }, "Can't delete cross-origin property");
+ assert_throws_dom("SecurityError", function() { delete win.location.foopy; }, "Can't delete cross-origin property");
+}, "[[Delete]] Should throw on cross-origin objects");
+
+/*
+ * [[DefineOwnProperty]]
+ */
+function checkDefine(obj, prop) {
+ var valueDesc = { configurable: true, enumerable: false, writable: false, value: 2 };
+ var accessorDesc = { configurable: true, enumerable: false, get: function() {} };
+ assert_throws_dom("SecurityError", function() { Object.defineProperty(obj, prop, valueDesc); }, "Can't define cross-origin value property " + prop);
+ assert_throws_dom("SecurityError", function() { Object.defineProperty(obj, prop, accessorDesc); }, "Can't define cross-origin accessor property " + prop);
+}
+addTest(function(win) {
+ checkDefine(win, 'length');
+ checkDefine(win, 'parent');
+ checkDefine(win, 'location');
+ checkDefine(win, 'document');
+ checkDefine(win, 'foopy');
+ checkDefine(win.location, 'href');
+ checkDefine(win.location, 'replace');
+ checkDefine(win.location, 'port');
+ checkDefine(win.location, 'foopy');
+}, "[[DefineOwnProperty]] Should throw for cross-origin objects");
+
+/*
+ * EnumerateObjectProperties (backed by [[OwnPropertyKeys]])
+ */
+
+addTest(function(win) {
+ let i = 0;
+ for (var prop in win) {
+ i++;
+ assert_true(windowAllowlists.indices.includes(prop), prop + " is not safelisted for a cross-origin Window");
+ }
+ assert_equals(i, windowAllowlists.indices.length, "Enumerate all enumerable safelisted cross-origin Window properties");
+ i = 0;
+ for (var prop in win.location) {
+ i++;
+ }
+ assert_equals(i, 0, "There's nothing to enumerate for cross-origin Location properties");
+}, "Can only enumerate safelisted enumerable properties");
+
+/*
+ * [[OwnPropertyKeys]]
+ */
+
+addTest(function(win) {
+ assert_array_equals(Object.getOwnPropertyNames(win).sort(),
+ windowAllowlists.propNames,
+ "Object.getOwnPropertyNames() gives the right answer for cross-origin Window");
+ assert_array_equals(Object.keys(win).sort(),
+ windowAllowlists.indices,
+ "Object.keys() gives the right answer for cross-origin Window");
+ assert_array_equals(Object.getOwnPropertyNames(win.location).sort(),
+ locationAllowlists.propNames,
+ "Object.getOwnPropertyNames() gives the right answer for cross-origin Location");
+ assert_equals(Object.keys(win.location).length, 0,
+ "Object.keys() gives the right answer for cross-origin Location");
+}, "[[OwnPropertyKeys]] should return all properties from cross-origin objects");
+
+addTest(function(win) {
+ assert_array_equals(Object.getOwnPropertySymbols(win), allowedSymbols,
+ "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Window");
+ assert_array_equals(Object.getOwnPropertySymbols(win.location),
+ allowedSymbols,
+ "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Location");
+}, "[[OwnPropertyKeys]] should return the right symbol-named properties for cross-origin objects");
+
+addTest(function(win) {
+ var allWindowProps = Reflect.ownKeys(win);
+ indexedWindowProps = allWindowProps.slice(0, windowAllowlists.indices.length);
+ stringWindowProps = allWindowProps.slice(0, -1 * allowedSymbols.length);
+ symbolWindowProps = allWindowProps.slice(-1 * allowedSymbols.length);
+ // stringWindowProps should have "then" last in this case. Do this
+ // check before we call stringWindowProps.sort() below.
+ assert_equals(stringWindowProps[stringWindowProps.length - 1], "then",
+ "'then' property should be added to the end of the string list if not there");
+ assert_array_equals(indexedWindowProps, windowAllowlists.indices,
+ "Reflect.ownKeys should start with the indices exposed on the cross-origin window.");
+ assert_array_equals(stringWindowProps.sort(), windowAllowlists.propNames,
+ "Reflect.ownKeys should continue with the cross-origin window properties for a cross-origin Window.");
+ assert_array_equals(symbolWindowProps, allowedSymbols,
+ "Reflect.ownKeys should end with the cross-origin symbols for a cross-origin Window.");
+
+ var allLocationProps = Reflect.ownKeys(win.location);
+ stringLocationProps = allLocationProps.slice(0, -1 * allowedSymbols.length);
+ symbolLocationProps = allLocationProps.slice(-1 * allowedSymbols.length);
+ assert_array_equals(stringLocationProps.sort(), locationAllowlists.propNames,
+ "Reflect.ownKeys should start with the cross-origin window properties for a cross-origin Location.")
+ assert_array_equals(symbolLocationProps, allowedSymbols,
+ "Reflect.ownKeys should end with the cross-origin symbols for a cross-origin Location.")
+}, "[[OwnPropertyKeys]] should place the symbols after the property names after the subframe indices");
+
+addThenTest(function(win) {
+ var stringProps = Object.getOwnPropertyNames(win);
+ // Named frames are not exposed via [[OwnPropertyKeys]].
+ assert_equals(stringProps.indexOf("a"), -1);
+ assert_equals(stringProps.indexOf("b"), -1);
+ assert_equals(typeof win.a, "object");
+ assert_equals(typeof win.b, "object");
+ assert_equals(stringProps[stringProps.length - 1], "then");
+ assert_equals(stringProps.indexOf("then"), stringProps.lastIndexOf("then"));
+}, "[[OwnPropertyKeys]] should not reorder where 'then' appears if it's a named subframe, nor add another copy of 'then'");
+
+addTest(function(win) {
+ assert_equals(B.eval('parent.' + winName(win)), win, "A and B observe the same identity for C's Window");
+ assert_equals(B.eval('parent.' + winName(win) + '.location'), win.location, "A and B observe the same identity for C's Location");
+}, "A and B jointly observe the same identity for cross-origin Window and Location");
+
+function checkFunction(f, proto) {
+ var name = f.name || '<missing name>';
+ assert_equals(typeof f, 'function', name + " is a function");
+ assert_equals(Object.getPrototypeOf(f), proto, f.name + " has the right prototype");
+}
+
+addTest(function(win) {
+ checkFunction(win.close, Function.prototype);
+ checkFunction(win.location.replace, Function.prototype);
+}, "Cross-origin functions get local Function.prototype");
+
+addTest(function(win) {
+ assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'parent')),
+ "Need to be able to use Object.getOwnPropertyDescriptor do this test");
+ checkFunction(Object.getOwnPropertyDescriptor(win, 'parent').get, Function.prototype);
+ checkFunction(Object.getOwnPropertyDescriptor(win.location, 'href').set, Function.prototype);
+}, "Cross-origin Window accessors get local Function.prototype");
+
+addTest(function(win) {
+ checkFunction(close, Function.prototype);
+ assert_not_equals(close, B.close, 'same-origin Window functions get their own object');
+ assert_not_equals(close, win.close, 'cross-origin Window functions get their own object');
+ var close_B = B.eval('parent.' + winName(win) + '.close');
+ assert_not_equals(close, close_B, 'close_B is unique when viewed by the parent');
+ assert_not_equals(close_B, win.close, 'different Window functions per-incumbent script settings object');
+ checkFunction(close_B, B.Function.prototype);
+
+ checkFunction(location.replace, Function.prototype);
+ assert_not_equals(location.replace, win.location.replace, "cross-origin Location functions get their own object");
+ var replace_B = B.eval('parent.' + winName(win) + '.location.replace');
+ assert_not_equals(replace_B, win.location.replace, 'different Location functions per-incumbent script settings object');
+ checkFunction(replace_B, B.Function.prototype);
+}, "Same-origin observers get different functions for cross-origin objects");
+
+addTest(function(win) {
+ assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'parent')),
+ "Need to be able to use Object.getOwnPropertyDescriptor do this test");
+ var get_self_parent = Object.getOwnPropertyDescriptor(window, 'parent').get;
+ var get_parent_A = Object.getOwnPropertyDescriptor(win, 'parent').get;
+ var get_parent_B = B.eval('Object.getOwnPropertyDescriptor(parent.' + winName(win) + ', "parent").get');
+ assert_not_equals(get_self_parent, get_parent_A, 'different Window accessors per-incumbent script settings object');
+ assert_not_equals(get_parent_A, get_parent_B, 'different Window accessors per-incumbent script settings object');
+ checkFunction(get_self_parent, Function.prototype);
+ checkFunction(get_parent_A, Function.prototype);
+ checkFunction(get_parent_B, B.Function.prototype);
+}, "Same-origin observers get different accessors for cross-origin Window");
+
+addTest(function(win) {
+ var set_self_href = Object.getOwnPropertyDescriptor(window.location, 'href').set;
+ var set_href_A = Object.getOwnPropertyDescriptor(win.location, 'href').set;
+ var set_href_B = B.eval('Object.getOwnPropertyDescriptor(parent.' + winName(win) + '.location, "href").set');
+ assert_not_equals(set_self_href, set_href_A, 'different Location accessors per-incumbent script settings object');
+ assert_not_equals(set_href_A, set_href_B, 'different Location accessors per-incumbent script settings object');
+ checkFunction(set_self_href, Function.prototype);
+ checkFunction(set_href_A, Function.prototype);
+ checkFunction(set_href_B, B.Function.prototype);
+}, "Same-origin observers get different accessors for cross-origin Location");
+
+addTest(function(win) {
+ assert_equals({}.toString.call(win), "[object Object]");
+ assert_equals({}.toString.call(win.location), "[object Object]");
+}, "{}.toString.call() does the right thing on cross-origin objects");
+
+addPromiseTest(function(win) {
+ return Promise.resolve(win).then((arg) => {
+ assert_equals(arg, win);
+ });
+}, "Resolving a promise with a cross-origin window without a 'then' subframe should work");
+
+addPromiseThenTest(function(win) {
+ return Promise.resolve(win).then((arg) => {
+ assert_equals(arg, win);
+ });
+}, "Resolving a promise with a cross-origin window with a 'then' subframe should work");
+
+addPromiseThenTest(function(win) {
+ return Promise.resolve(win.location).then((arg) => {
+ assert_equals(arg, win.location);
+ });
+}, "Resolving a promise with a cross-origin location should work");
+
+addTest(function(win) {
+ var desc = Object.getOwnPropertyDescriptor(window, "onmouseenter");
+ var f = () => {};
+
+ // Check that it has [LegacyLenientThis] behavior
+ assert_equals(desc.get.call({}), undefined, "getter should return undefined");
+ desc.set.call({}, f); // Should not throw.
+
+ // Check that we can apply it to a same-origin window.
+ assert_equals(desc.get.call(B), null, "Should be able to read the value");
+ desc.set.call(B, f);
+ assert_equals(desc.get.call(B), f, "Value should have updated");
+ // And reset it for our next test
+ desc.set.call(B, null);
+ assert_equals(desc.get.call(B), null, "Should have been reset");
+
+ // Check that applying it to a cross-origin window throws instead of doing
+ // the [LegacyLenientThis] behavior.
+ assert_throws_dom("SecurityError", () => {
+ desc.get.call(win);
+ }, "Should throw when getting cross-origin");
+ assert_throws_dom("SecurityError", () => {
+ desc.set.call(win, f);
+ }, "Should throw when setting cross-origin");
+}, "LegacyLenientThis behavior");
+
+// We do a fresh load of the subframes for each test to minimize side-effects.
+// It would be nice to reload ourselves as well, but we can't do that without
+// disrupting the test harness.
+function testDone() {
+ if (testList.length != 0) {
+ reloadSubframes(runNextTest);
+ } else {
+ done();
+ }
+}
+
+async function runNextTest() {
+ var entry = testList.shift();
+ if (entry.promiseTest) {
+ for (let t of entry.tests) {
+ await new Promise(resolve => {
+ promise_test(test_obj => {
+ return new Promise(res => res(t.func(test_obj))).finally(resolve);
+ }, t.desc);
+ });
+ }
+ } else {
+ for (let t of entry.tests) {
+ test(t.func, t.desc);
+ }
+ }
+ testDone();
+}
+reloadSubframes(runNextTest);
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame-with-then.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame-with-then.html
new file mode 100644
index 0000000000..3eedeca38a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame-with-then.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <script>
+ if (location.search == "?setdomain") {
+ document.domain = document.domain;
+ }
+ </script>
+ <body>
+ <!--- Some frames to test ordering -->
+ <iframe name="a"></iframe>
+ <!-- A subframe to test "then" behavior -->
+ <iframe name="then"></iframe>
+ <iframe name="b"></iframe>
+ <!-- Two subframes with names corresponding to IDL-defined properties; one
+ a cross-origin-exposed property and one not exposed cross-origin -->
+ <iframe name="close"></iframe>
+ <iframe name="open"></iframe>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html
new file mode 100644
index 0000000000..ca2dd8ebf8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<html>
+<head>
+<script>
+ if (location.search == "?setdomain") {
+ document.domain = document.domain;
+ }
+
+ // Override the |frames| and |focus| property to test that such overrides are
+ // properly ignored cross-origin.
+ window.frames = "override";
+ window.focus = "override";
+
+ // Also add a |then| property to test that it doesn't get exposed.
+ window.then = "something";
+ window.location.then = "something-else";
+
+ // If we get a postMessage, we grab references to everything and set
+ // document.domain to trim off our topmost subdomain.
+ window.onmessage = function(evt) {
+ window.windowReferences = [];
+ window.locationReferences = [];
+ for (var i = 0; i < parent.length; ++i) {
+ windowReferences.push(parent[i]);
+ locationReferences.push(parent[i].location);
+ }
+ try {
+ document.domain = document.domain.substring(document.domain.indexOf('.') + 1);
+ evt.source.postMessage('PASS', '*');
+ } catch (e) {
+ evt.source.postMessage('FAIL: cannot trim off document.domain: ' + e, '*');
+ }
+ }
+
+ function checkWindowReferences() {
+ for (var i = 0; i < parent.length; ++i) {
+ if (windowReferences[i] != parent[i])
+ throw new Error("Window references don't match for " + i + " after document.domain");
+ if (locationReferences[i] != parent[i].location)
+ throw new Error("Location references don't match for " + i + " after document.domain");
+ }
+ return true;
+ }
+</script>
+</head>
+<body>
+ <!-- Two subframes to give us some indexed properties -->
+ <iframe></iframe>
+ <iframe name=donotleakme></iframe><!-- "donotleakme" is excluded as cross-origin named property due to [[HideFromKeys]] -->
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.window.js b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.window.js
new file mode 100644
index 0000000000..45b61d07f5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.window.js
@@ -0,0 +1,51 @@
+// META: variant=?assign
+// META: variant=?customproperty
+// META: variant=?hash
+// META: variant=?host
+// META: variant=?hostname
+// META: variant=?pathname
+// META: variant=?port
+// META: variant=?protocol
+// META: variant=?reload
+// META: variant=?search
+// META: variant=?toString
+// META: variant=?valueOf
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+
+const property = window.location.search.substr(1);
+
+promise_test(async t => {
+ const iframeContext = new RemoteContext(token());
+ const iframe = document.createElement("iframe");
+ iframe.src = get_host_info().REMOTE_ORIGIN +
+ "/common/dispatcher/remote-executor.html?uuid=" + iframeContext.context_id;
+ document.body.appendChild(iframe);
+
+ // Wait for the cross-origin document to be loaded inside the iframe.
+ assert_equals(
+ await iframeContext.execute_script(() => "Document loaded") ,
+ "Document loaded"
+ );
+
+ assert_throws_dom("SecurityError", () => {
+ const unused = iframe.contentWindow.location[property];
+ }, "Cross origin get of a location property should throw a security error");
+
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.location[property] = "Random string";
+ }, "Cross origin set of a location property should throw a security error");
+
+ // Verify that the property was indeed not modified.
+ assert_not_equals(
+ await iframeContext.execute_script(property => location[property],
+ [property]),
+ "Random string",
+ );
+
+ assert_throws_dom("SecurityError", () => {
+ const unused = Object.getOwnPropertyDescriptor(
+ iframe.contentWindow.location, property);
+ }, "Cross origin get of descriptors should throw a security error");
+}, `Verifying that cross-origin access of '${property}' is restricted`);
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/resources/cross-origin-due-to-document-domain-only-helper.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/resources/cross-origin-due-to-document-domain-only-helper.html
new file mode 100644
index 0000000000..10ac8ece0e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/resources/cross-origin-due-to-document-domain-only-helper.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+self.expandosForever = true
+self.location.expandosForever = true
+function setDocumentDomain() {
+ document.domain = document.domain
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/win-documentdomain.sub.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/win-documentdomain.sub.html
new file mode 100644
index 0000000000..a315e21208
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/win-documentdomain.sub.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script>
+ function loadFrames() {
+ window.A = document.getElementById('A').contentWindow;
+ window.B = document.getElementById('B').contentWindow;
+ window.C = document.getElementById('C').contentWindow;
+ window.D = document.getElementById('D').contentWindow;
+
+ var path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame.html';
+ A.location = 'frame.html';
+ B.location = '//{{domains[www2]}}:' + get_port(location) + path;
+ C.location = '//{{domains[www2]}}:' + get_port(location) + path;
+ D.location = '//{{domains[www1]}}:' + get_port(location) + path;
+
+ var loadCount = 0;
+ function frameLoaded() {
+ if (++loadCount == 4)
+ go();
+ }
+ var iframes = document.getElementsByTagName('iframe');
+ for (var i = 0; i < iframes.length; i++) {
+ iframes[i].onload = frameLoaded;
+ }
+ }
+
+ var results = [];
+ function assert(cond, msg) {
+ results.push({pass: !!cond, message: msg});
+ }
+
+ function go() {
+ window.onmessage = function(evt) {
+ try {
+ assert(evt.data == "PASS", "frame.html processing should be PASS but got " + evt.data);
+ assert(B.checkWindowReferences(), "B's Window references are still self-consistent after document.domain");
+ for (var i = 0; i < window.length; ++i) {
+ assert(window[i] === B.windowReferences[i],
+ "Window reference " + i + " consistent between globals after document.domain");
+ assert(window[i].location === B.locationReferences[i],
+ "Location reference " + i + " consistent between globals after document.domain");
+ }
+ } catch(e) {
+ assert(false, "Should not receive exception: " + e);
+ }
+ opener.postMessage(results, '*');
+ };
+ A.document.domain = A.document.domain;
+ document.domain = document.domain;
+ B.postMessage('', '*');
+ }
+
+ </script>
+</head>
+<body onload="loadFrames()">
+ <iframe id="A"></iframe>
+ <iframe id="B"></iframe>
+ <iframe id="C"></iframe>
+ <iframe id="D"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/window-location-and-location-href-cross-realm-set.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/window-location-and-location-href-cross-realm-set.html
new file mode 100644
index 0000000000..f03550a141
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/window-location-and-location-href-cross-realm-set.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cross-realm [[Set]] to window.location and location.href throws an error of correct realm</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+<link rel="help" href="https://webidl.spec.whatwg.org/#Unforgeable">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+const URL_SAME_ORIGIN = get_host_info().ORIGINAL_HOST;
+const URL_CROSS_ORIGIN = get_host_info().HTTP_REMOTE_ORIGIN;
+const URL_VALID = "#foo";
+const URL_INVALID = "http://#";
+
+const { get: locationGet, set: locationSet } = Object.getOwnPropertyDescriptor(window, "location");
+const { get: hrefGet, set: hrefSet } = Object.getOwnPropertyDescriptor(location, "href");
+
+
+promise_test(async t => {
+ const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN);
+ assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow).location; });
+ assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.get(sameOriginWindow, "location", {}); });
+ assert_throws_js(TypeError, () => { locationGet.call({}); });
+}, "Same-origin window.location getter throws TypeError in holder's realm on invalid |this| value");
+
+promise_test(async t => {
+ const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN);
+ assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow.location).href; });
+ assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.get(sameOriginWindow.location, "href", {}); });
+ assert_throws_js(TypeError, () => { hrefGet(); });
+}, "Same-origin location.href getter throws TypeError in holder's realm on invalid |this| value");
+
+promise_test(async t => {
+ const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN);
+ assert_throws_dom("SECURITY_ERR", () => { crossOriginWindow.location.href; });
+ assert_throws_dom("SECURITY_ERR", () => { hrefGet.call(crossOriginWindow.location); });
+ assert_equals(Object.getOwnPropertyDescriptor(crossOriginWindow.location, "href").get, undefined);
+}, "Cross-origin location.href getter throws SecurityError in lexical realm");
+
+
+promise_test(async t => {
+ const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN);
+ assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow).location = URL_VALID; });
+ assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.set(sameOriginWindow, "location", URL_VALID, {}); });
+ assert_throws_js(TypeError, () => { locationSet.call(() => {}, URL_VALID); });
+}, "Same-origin window.location setter throws TypeError in holder's realm on invalid |this| value");
+
+promise_test(async t => {
+ const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN);
+ assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow.location).href = URL_VALID; });
+ assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.set(sameOriginWindow.location, "href", URL_VALID, {}); });
+ assert_throws_js(TypeError, () => { hrefSet.call(undefined, URL_VALID); });
+}, "Same-origin location.href setter throws TypeError in holder's realm on invalid |this| value");
+
+promise_test(async t => {
+ const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN);
+ assert_throws_js(TypeError, () => { Object.create(crossOriginWindow).location = URL_VALID; });
+ assert_throws_js(TypeError, () => { Reflect.set(crossOriginWindow, "location", URL_VALID, {}); });
+ assert_throws_js(TypeError, () => { locationSet.call([], URL_VALID); });
+}, "Cross-origin window.location setter throws TypeError in lexical realm on invalid |this| value");
+
+promise_test(async t => {
+ const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN);
+ assert_throws_js(TypeError, () => { Object.create(crossOriginWindow.location).href = URL_VALID; });
+ assert_throws_js(TypeError, () => { Reflect.set(crossOriginWindow.location, "href", URL_VALID, {}); });
+ assert_throws_js(TypeError, () => { hrefSet.call(null, URL_VALID); });
+}, "Cross-origin location.href setter throws TypeError in lexical realm on invalid |this| value");
+
+
+promise_test(async t => {
+ const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN);
+ assert_throws_js(sameOriginWindow.TypeError, () => { sameOriginWindow.location = Symbol(); });
+
+ // The error originates in sameOriginWindow.location.href setter, hence it's not in realm of locationSet.
+ assert_throws_js(sameOriginWindow.TypeError, () => { locationSet.call(sameOriginWindow, Symbol()); });
+}, "Same-origin window.location` setter throws TypeError in holder's realm on non-coercible URL argument");
+
+promise_test(async t => {
+ const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN);
+ assert_throws_js(sameOriginWindow.TypeError, () => { sameOriginWindow.location.href = Symbol(); });
+ assert_throws_js(TypeError, () => { hrefSet.call(sameOriginWindow.location, Symbol()); });
+}, "Same-origin location.href setter throws TypeError in holder's realm on non-coercible URL argument");
+
+promise_test(async t => {
+ const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN);
+ assert_throws_js(TypeError, () => { crossOriginWindow.location = Symbol(); });
+ assert_throws_js(TypeError, () => { locationSet.call(crossOriginWindow, Symbol()); });
+}, "Cross-origin window.location setter throws TypeError in lexical realm on non-coercible URL argument");
+
+promise_test(async t => {
+ const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN);
+ assert_throws_js(TypeError, () => { crossOriginWindow.location.href = Symbol(); });
+ assert_throws_js(TypeError, () => { hrefSet.call(crossOriginWindow.location, Symbol()); });
+}, "Cross-origin location.href setter throws TypeError in lexical realm on non-coercible URL argument");
+
+function makeWindow(t, src) {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => { iframe.remove(); });
+ iframe.onload = () => { resolve(iframe.contentWindow); };
+ iframe.src = src;
+ document.body.append(iframe);
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-iframe.html b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-iframe.html
new file mode 100644
index 0000000000..fabde327a1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-iframe.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <head>
+ <title>about:blank in child browsing context aliases security origin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ test(() => {
+ let iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ // Should not throw: srcdoc should always be same-origin.
+ iframe.contentWindow.document.body.innerHTML = '<p>Hello world!</p>';
+
+ // Explicitly set `domain` component of origin: any other same-origin
+ // browsing contexts are now cross-origin unless they also explicitly
+ // set document.domain to the same value.
+ document.domain = document.domain;
+ // Should not throw: the origin should be aliased, so setting
+ // document.domain in one Document should affect both Documents.
+ assert_equals(
+ iframe.contentWindow.document.body.textContent,
+ 'Hello world!');
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-window.html b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-window.html
new file mode 100644
index 0000000000..cc3177f943
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-window.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>about:blank in auxiliary browsing context aliases security origin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ test(() => {
+ let newWindow = window.open();
+ // Should not throw: the newly-opened window should be same-origin.
+ newWindow.document.body.innerHTML = '<p>Hello world!</p>';
+
+ // Explicitly set `domain` component of origin: any other same-origin
+ // browsing contexts are now cross-origin unless they also explicitly
+ // set document.domain to the same value.
+ document.domain = document.domain;
+ // Should not throw: the origin should be aliased, so setting
+ // document.domain in one Document should affect both Documents.
+ assert_equals(newWindow.document.body.textContent, 'Hello world!');
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/about-srcdoc.html b/testing/web-platform/tests/html/browsers/origin/inheritance/about-srcdoc.html
new file mode 100644
index 0000000000..971811ee66
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/about-srcdoc.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>about:srcdoc aliases security origin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ test(() => {
+ let iframe = document.createElement('iframe');
+ iframe.srcdoc = '<body></body>';
+ document.body.appendChild(iframe);
+ // Should not throw: srcdoc should always be same-origin.
+ iframe.contentWindow.document.body.innerHTML = '<p>Hello world!</p>';
+
+ // Explicitly set `domain` component of origin: any other same-origin
+ // browsing contexts are now cross-origin unless they also explicitly
+ // set document.domain to the same value.
+ document.domain = document.domain;
+ // Should not throw: the origin should be aliased, so setting
+ // document.domain in one Document should affect both Documents.
+ assert_equals(
+ iframe.contentWindow.document.body.textContent,
+ 'Hello world!');
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js b/testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js
new file mode 100644
index 0000000000..39dc3b1cc2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js
@@ -0,0 +1,10 @@
+// META: script=/common/get-host-info.sub.js
+
+// To use document.domain, we need to start from a subdomain.
+//
+// For document.domain setter to work, some web browser require the
+// |Origin-Agent-Cluster: ?0| header to be set uniformly on the origin.
+const origin = get_host_info().OTHER_ORIGIN;
+const openee = window.open(
+ origin + '/html/browsers/origin/inheritance/resources/document-write.html')
+fetch_tests_from_window(openee);
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js.headers b/testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js.headers
new file mode 100644
index 0000000000..e007de4d7b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/document-write.https.window.js.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?0
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/javascript-url.html b/testing/web-platform/tests/html/browsers/origin/inheritance/javascript-url.html
new file mode 100644
index 0000000000..7dfb1130ce
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/javascript-url.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+ <head>
+ <title>javascript: aliases security origin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ promise_test(t => {
+ let iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ // Should not throw: srcdoc should always be same-origin.
+ iframe.contentDocument;
+
+ iframe.contentWindow.location = 'javascript:"Hello world!"';
+ return new Promise(resolve => {
+ iframe.addEventListener('load', resolve);
+ }).then(() => {
+ // Explicitly set `domain` component of origin: any other same-origin
+ // browsing contexts are now cross-origin unless they also explicitly
+ // set document.domain to the same value.
+ document.domain = document.domain;
+ // Should not throw: the origin should be aliased, so setting
+ // document.domain in one Document should affect both Documents.
+ assert_equals(
+ iframe.contentWindow.document.body.textContent,
+ 'Hello world!');
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html
new file mode 100644
index 0000000000..7a6ff3118e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ </head>
+ <body></body>
+ <script>
+ const domain_start = document.domain;
+ const domain_new = domain_start.replace(/^[^.]+\./,'');
+
+ async_test(test => {
+ const iframe = document.createElement('iframe');
+ iframe.src = './iframe-with-about-blank-iframe.html';
+ iframe.onload = test.step_func_done(() => {
+ const doc0 = frames[0].frames[0].document;
+ const doc1 = frames[0].frames[1].document;
+
+ assert_equals(doc0.domain, domain_start);
+ assert_equals(doc1.domain, domain_start);
+
+ doc0.open();
+ doc1.open();
+ assert_equals(doc0.domain, domain_start);
+ assert_equals(doc1.domain, domain_start);
+
+ document.domain = domain_new;
+ assert_equals(doc0.domain, domain_start);
+ assert_equals(doc1.domain, domain_start);
+
+ doc0.close();
+ doc1.close();
+ assert_equals(doc0.domain, domain_start);
+ assert_equals(doc1.domain, domain_start);
+ });
+ document.body.appendChild(iframe);
+ }, "document.open() do not make the callee's origin to alias the caller's"
+ + " one");
+ </script>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html.headers b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html.headers
new file mode 100644
index 0000000000..e007de4d7b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/document-write.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?0
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html
new file mode 100644
index 0000000000..b3f5125233
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <iframe src="about:blank"></iframe>
+ <iframe src=""></iframe>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html.headers b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html.headers
new file mode 100644
index 0000000000..e007de4d7b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/inheritance/resources/iframe-with-about-blank-iframe.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?0
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-bad-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-bad-subdomain.sub.https.html
new file mode 100644
index 0000000000..3a45ee6d6a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-bad-subdomain.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, child attempts to origin-key but uses a bad header value, child is a subdomain of the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frameIndex = 0;
+for (const badValue of ["", "?0", "true", "\"?1\"", "1", "?2", "(?1)"]) {
+ promise_test(async () => {
+ await insertIframe("{{hosts[][www]}}", badValue);
+ }, `"${badValue}": frame insertion`);
+
+ // Since the header values are bad they should be site-keyed.
+ testSameAgentCluster([self, frameIndex], `"${badValue}"`);
+ testGetter(frameIndex, false, `"${badValue}"`);
+ ++frameIndex;
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html
new file mode 100644
index 0000000000..85fb1f64e0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, child is origin-keyed, child is different-origin to the parent because of a port mismatch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][]}}:{{ports[https][1]}}", "?1");
+});
+
+// Since they're different-origin, the child's request is respected, so the
+// parent ends up in the site-keyed agent cluster and the child in the
+// origin-keyed one.
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, false, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-same.sub.https.html
new file mode 100644
index 0000000000..7ece02c81a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-same.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, child is origin-keyed, child is same-origin to the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][]}}", "?1");
+});
+
+// Since they're same-origin, and the parent loaded in the site-keyed agent
+// cluster, the child's request for origin-keying gets ignored, and both end up
+// site-keyed.
+testSameAgentCluster([self, 0]);
+
+testGetter(self, false, "parent");
+testGetter(0, false, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain-with-redirect.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain-with-redirect.sub.https.html
new file mode 100644
index 0000000000..994f80876d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain-with-redirect.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, child is reached via a redirect response with no header, child final response does have the header</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][www]}}", "?1", { redirectFirst: true });
+});
+
+// Since they're different-origin, the child's request is respected, so the
+// parent ends up in the site-keyed agent cluster and the child in the
+// origin-keyed one.
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, false, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..5fc2fa29f3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, child is origin-keyed, child is a subdomain of the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+// Since they're different-origin, the child's request is respected, so the
+// parent ends up in the site-keyed agent cluster and the child in the
+// origin-keyed one.
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, false, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yeswithparams-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yeswithparams-subdomain.sub.https.html
new file mode 100644
index 0000000000..3e7c8419b3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yeswithparams-subdomain.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, child is origin-keyed using parameters on its structured header, child is a subdomain of the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][www]}}", "?1;param1;param2=value2");
+});
+
+// Since they're different-origin, the child's request is respected, so the
+// parent ends up in the site-keyed agent cluster and the child in the
+// origin-keyed one.
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, false, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html
new file mode 100644
index 0000000000..f00814cfbf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, child is site-keyed, child is is different-origin to the parent because of a port mismatch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][]}}:{{ports[https][1]}}");
+});
+
+// Since they're different-origin, the parent's request is respected, as is the
+// child's non-request. So the parent ends up in the origin-keyed agent cluster
+// and the child ends up in the site-keyed one.
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, true, "parent");
+testGetter(0, false, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html
new file mode 100644
index 0000000000..307f8c48d7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, child is site-keyed, child is same-origin to the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][]}}");
+});
+
+// Since they're same-origin, and the parent loaded with origin-keying, the
+// child's non-request gets ignored, and both end up origin-keyed.
+testSameAgentCluster([self, 0]);
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html
new file mode 100644
index 0000000000..8c823fa36f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, child is site-keyed, child is a subdomain of the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][www]}}");
+});
+
+// Since they're different-origin, the parent's request is respected, as is the
+// child's non-request. So the parent ends up in the origin-keyed agent cluster
+// and the child ends up in the site-keyed one.
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, true, "parent");
+testGetter(0, false, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html
new file mode 100644
index 0000000000..5e431e6e41
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, child is site-keyed, child is different-origin to the parent because of a port mismatch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][]}}:{{ports[https][1]}}", "?1");
+});
+
+// Both request origin-keying, so the parent ends up in one origin-keyed agent
+// cluster (the default port's origin), and the child ends up in a different
+// origin-keyed agent cluster (the other port's origin).
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html
new file mode 100644
index 0000000000..3b8c214a61
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, child is site-keyed, child is same-origin to the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][]}}", "?1");
+});
+
+// Both request origin-keying, and they're same-origin, so they both end up in
+// the same origin-keyed agent cluster.
+testSameAgentCluster([self, 0]);
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..136a3a0bba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, child is site-keyed, child is a subdomain of the parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+// Both request origin-keying, so the parent ends up in one origin-keyed agent
+// cluster (the base domain's origin), and the child ends up in a different
+// origin-keyed agent cluster (the www subdomain's origin).
+testDifferentAgentClusters([self, 0]);
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..1bb252f0ab
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, subdomain child 1 is site-keyed, same-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Must be sequential, not parallel: the site-keyed frame must load first.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+
+// Since they're different-origin, the parent's non-request is respected, as is
+// child 1's non-request. child 2 requests origin-keying but is ignored, since
+// child 1 is in the same browsing context group.
+//
+// So, everyone ends up in the site-keyed agent cluster.
+testSameAgentCluster([self, 0], "Parent to child1");
+testSameAgentCluster([self, 1], "Parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+
+testGetter(self, false, "parent");
+testGetter(0, false, "child1");
+testGetter(1, false, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html
new file mode 100644
index 0000000000..5b80c528f0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, subdomain child 1 is site-keyed, different-port-same-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www]}}:{{ports[https][1]}}", "?1");
+});
+
+// Since everybody is different-origin, everyone's requests/non-requests get
+// respected.
+//
+// So, the parent and child 1 end up in the site-keyed agent cluster, and child
+// 2 ends up in its own origin-keyed agent cluster.
+testSameAgentCluster([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, false, "parent");
+testGetter(0, false, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain1-child2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain1-child2-yes-subdomain2.sub.https.html
new file mode 100644
index 0000000000..bba13b82a4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain1-child2-yes-subdomain2.sub.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, subdomain child 1 is site-keyed, different-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www1]}}", "?1");
+});
+
+// Since everybody is different-origin, everyone's requests/non-requests get
+// respected.
+//
+// So, the parent and child 1 end up in the site-keyed agent cluster, and child
+// 2 ends up in its own origin-keyed agent cluster.
+testSameAgentCluster([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, false, "parent");
+testGetter(0, false, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-port.sub.https.html
new file mode 100644
index 0000000000..d01d180213
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-port.sub.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, subdomain child 1 is origin-keyed, non-subdomain but different-port child 2 is site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][]}}:{{ports[https][1]}}");
+});
+
+// Everyone is different-origin, so everyone's request/non-request is
+// respected.
+//
+// So, the parent and child2 end up in the site-keyed agent cluster, and child1
+// ends up in an origin-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testSameAgentCluster([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, false, "parent");
+testGetter(0, true, "child1");
+testGetter(1, false, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html
new file mode 100644
index 0000000000..9a245b3ace
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, subdomain child 1 is origin-keyed, same-subdomain child 2 is site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Must be sequential, not parallel: the origin-keyed frame must load first.
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][www]}}");
+});
+
+
+// Since they're different-origin, the parent's non-request is respected, as is
+// child 1's request. child 2's non-request is ignored, since child 1 is in the
+// same browsing context group.
+//
+// So, the parent ends up in the site-keyed agent cluster, and both children end
+// up in an origin-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+
+testGetter(self, false, "parent");
+testGetter(0, true, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html
new file mode 100644
index 0000000000..c308b9a17a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is site-keyed, same-subdomain child 2 is site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www]}}");
+});
+
+
+// Since everybody is different-origin, everyone's requests/non-requests get
+// respected.
+//
+// So, the parent ends up in its origin-keyed agent cluster, and child 1 and
+// child 2 both end up in the site-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, false, "child1");
+testGetter(1, false, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html
new file mode 100644
index 0000000000..767d908c21
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is site-keyed, different-subdomain child 2 is site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www1]}}");
+});
+
+
+// Since everybody is different-origin, everyone's requests/non-requests get
+// respected.
+//
+// So, the parent ends up in its origin-keyed agent cluster, and child 1 and
+// child 2 both end up in the site-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, false, "child1");
+testGetter(1, false, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..45047f3ae1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is site-keyed, same-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Must be sequential, not parallel: the non-isolaed frame must load first.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+
+// Since they're different-origin, the parent's request is respected, as is
+// child 1's non-request. child 2 requests origin-keying but is ignored, since
+// child 1 is in the same browsing context group.
+//
+// So, the parent ends up in the origin-keyed agent cluster, and both children
+// ends up in the site-keyed one.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, false, "child1");
+testGetter(1, false, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html
new file mode 100644
index 0000000000..202b916767
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is site-keyed, different-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www1]}}", "?1");
+});
+
+
+// Since everybody is different-origin, everyone's requests/non-requests get
+// respected.
+//
+// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in
+// the site-keyed agent cluster, and child 2 ends up in a different origin-keyed
+// agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, false, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html
new file mode 100644
index 0000000000..a1316731ac
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is site-keyed, different-port-same-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}");
+ await insertIframe("{{hosts[][www]}}:{{ports[https][1]}}", "?1");
+});
+
+
+// Since everybody is different-origin, everyone's requests/non-requests get
+// respected.
+//
+// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in
+// the site-keyed agent cluster, and child 2 ends up in a different origin-keyed
+// agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, false, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html
new file mode 100644
index 0000000000..46bef4b9a9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, non-subdomain but different-port child 2 is site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][]}}:{{ports[https][1]}}");
+});
+
+// Everyone is different-origin, so everyone's request/non-request is
+// respected.
+//
+// So, child2 ends up in the site-keyed agent cluster, and the parent and child1
+// end up in two separate origin-keyed agent clusters.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child1");
+testGetter(1, false, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html
new file mode 100644
index 0000000000..39dcfc04b0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, same-subdomain child 2 is site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Must be sequential, not parallel: the origin-keyed frame must load first.
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][www]}}");
+});
+
+
+// Since they're different-origin, the parent's request is respected, as is
+// child 1's request. child 2's non-request is ignored, since child 1 is in the
+// same browsing context group.
+//
+// So, the parent ends up in the origin-keyed agent cluster, and both children
+// ends up in a different origin-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..b6daf91b54
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, same-subdomain child 2 is site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+
+// Since they're different-origin, the parent's request is respected, as is
+// child 1's request. child 2's request is redundant, since child 1 is in the
+// same browsing context group.
+//
+// So, the parent ends up in the origin-keyed agent cluster, and both children
+// ends up in a different origin-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html
new file mode 100644
index 0000000000..b94f9392d4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, different-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][www1]}}", "?1");
+});
+
+
+// Since everybody is different-origin, everyone's requests get
+// respected.
+//
+// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in
+// a second origin-keyed agent cluster, and child 2 ends up in a third
+// origin-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html
new file mode 100644
index 0000000000..fb3fda1bf2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, different-port-same-subdomain child 2 is origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+promise_setup(async () => {
+ // Order of loading should not matter, but we make it sequential to ensure the
+ // tests are deterministic.
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][www]}}:{{ports[https][1]}}", "?1");
+});
+
+
+// Since everybody is different-origin, everyone's requests get
+// respected.
+//
+// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in
+// a second origin-keyed agent cluster, and child 2 ends up in a third
+// origin-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child1");
+testDifferentAgentClusters([self, 1], "Parent to child2");
+testDifferentAgentClusters([0, 1], "child1 to child2");
+testDifferentAgentClusters([1, 0], "child2 to child1");
+
+testGetter(self, true, "parent");
+testGetter(0, true, "child1");
+testGetter(1, true, "child2");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/META.yml b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/META.yml
new file mode 100644
index 0000000000..f21ce69f6a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/META.yml
@@ -0,0 +1,5 @@
+spec: https://html.spec.whatwg.org/multipage/#origin-keyed-agent-clusters
+suggested_reviewers:
+ - domenic
+ - annevk
+ - wjmaclean
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/README.md b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/README.md
new file mode 100644
index 0000000000..85ba3bce7f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/README.md
@@ -0,0 +1,30 @@
+# Origin-keyed agent clusters tests
+
+These are tests for the [origin-keyed agent clusters](https://html.spec.whatwg.org/multipage/origin.html#origin-keyed-agent-clusters)
+feature.
+
+## Test filenames
+
+The tests in `2-iframes` follow the file naming pattern
+
+```
+parent-[yes|no]-child1-[yes|no]-[designator]-child2-[yes|no]-[designator]
+```
+
+Here:
+
+* `yes` or `no` refers to whether the `Origin-Agent-Cluster` header is set or
+ unset.
+* `designator` explains how the child differs from the parent: e.g. by being a
+ subdomain, or having a different port, or both. There's also `same` if it's
+ same-origin.
+
+Other directories have variations on this, e.g. `1-iframe/` does the same thing
+but for a single `child` instead of `child1` and `child2`, and `navigation/`
+uses `1` and `2` to represent the two different locations the single iframe will
+be navigated to.
+
+## Coverage
+
+Header parsing is covered by a few tests in the `1-iframe/` subdirectory, and
+not duplicated to all other scenarios.
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html
new file mode 100644
index 0000000000..556d528aa0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The initial about:blank respects origin isolation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "./resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertAboutBlankIframe();
+ await insertIframe("{{hosts[][www]}}");
+});
+
+// Since the initial about:blank inherits its origin from its parent, it is
+// same-origin with the parent, and thus cross-origin with child2.
+testSameAgentCluster([self, 0], "parent to about:blank");
+testDifferentAgentClusters([0, 1], "about:blank to child2");
+testDifferentAgentClusters([1, 0], "child2 to about:blank");
+
+testGetter(self, true, "parent");
+testGetter(0, true, "about:blank");
+testGetter(1, false, "child2");
+
+async function insertAboutBlankIframe() {
+ const iframe = await createBlankIframe();
+
+ // Now create and add the script, but don't navigate anywhere (since we want
+ // to stay on the initial about:blank).
+ // We need to absolutize the URL to since about:blank doesn't have a base URL.
+ const scriptURL = (new URL("./resources/send-header-page-script.mjs", import.meta.url)).href;
+ const script = iframe.contentDocument.createElement("script");
+ script.type = "module";
+ script.src = scriptURL;
+
+ await new Promise((resolve, reject) => {
+ script.onload = resolve;
+ script.onerror = () => reject(
+ new Error("Could not load the child frame script into the about:blank page")
+ );
+ iframe.contentDocument.body.append(script);
+ });
+
+ await setBothDocumentDomains(iframe.contentWindow);
+}
+
+function createBlankIframe() {
+ const iframe = document.createElement("iframe");
+ const promise = new Promise(resolve => {
+ iframe.addEventListener("load", resolve);
+ });
+ document.body.append(iframe);
+ return promise;
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html
new file mode 100644
index 0000000000..b4535d9e54
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Setting document.domain does not change same-originness when origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ Other tests check that using document.domain doesn't allow cross-origin
+ access. This test ensures a different, more subtle property: that
+ origin-keying makes document.domain into a no-op in other ways.
+-->
+
+<iframe src="resources/frame.html"></iframe>
+<iframe src="//{{domains[www1]}}:{{location[port]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html"></iframe>
+
+<script type="module">
+setup({ explicit_done: true });
+
+window.onload = () => {
+ test(() => {
+ // Normally, setting document.domain to itself would change the domain
+ // component of the origin. Since the iframe does *not* set document.domain,
+ // the two would then be considered cross-origin.
+ document.domain = document.domain;
+
+ // However, because we're origin-keyed, this shouldn't have any impact. The
+ // test fails if this throws, and passes if it succeeds.
+ frames[0].document;
+ }, "Setting document.domain must not change same-originness");
+
+ test(() => {
+ assert_throws_dom("SecurityError", () => {
+ document.domain = "{{hosts[][nonexistent]}}";
+ });
+ }, "The registrable domain suffix check must happen before the bail-out");
+
+ async_test(t => {
+ frames[1].postMessage({
+ type: "set document.domain",
+ newValue: "{{host}}"
+ }, "*");
+
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.type, "new document.domain");
+ assert_equals(e.data.result, "{{domains[www1]}}");
+ });
+ }, "Having an origin-keyed subdomain child try to set document.domain " +
+ "must not change the document.domain value it sees");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html
new file mode 100644
index 0000000000..a521934cc9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster must be implied by cross-origin isolation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="//{{domains[www1]}}:{{location[port]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html"></iframe>
+
+<div id="log"></div>
+
+<script type="module">
+import { testGetter } from "../resources/helpers.mjs";
+
+setup({ explicit_done: true });
+
+window.onload = () => {
+ // Cross-origin isolated pages are always origin-keyed.
+ testGetter(self, true, "self");
+
+ // Child frames of cross-origin isolated pages must also be cross-origin
+ // isolated, and thus also origin-keyed. Make sure the implementation doesn't
+ // treat them specially in some weird way, for the purposes of this
+ // implication.
+ testGetter(0, true, "child");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html.headers
new file mode 100644
index 0000000000..5f8621ef83
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html
new file mode 100644
index 0000000000..e0b5f92376
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a top-level frame sandboxed by CSP with no Origin-Agent-Cluster header</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import { testGetter } from "../resources/helpers.mjs";
+
+// Even without the header, sandboxing makes this page have an opaque origin,
+// so it is origin-keyed.
+testGetter(self, true);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html.headers
new file mode 100644
index 0000000000..4705ce9ded
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts;
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html
new file mode 100644
index 0000000000..a2220c5acc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a top-level frame sandboxed by CSP with an Origin-Agent-Cluster header</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import { testGetter } from "../resources/helpers.mjs";
+
+// We're definitely origin-keyed: both the CSP sandboxing and the
+// Origin-Agent-Cluster header should ensure this.
+testGetter(self, true);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html.headers
new file mode 100644
index 0000000000..a52bf50900
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html.headers
@@ -0,0 +1,2 @@
+Content-Security-Policy: sandbox allow-scripts;
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-no.https.html
new file mode 100644
index 0000000000..06149cda8a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-no.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a javascript: URL navigated to from a data: URL on a site-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/data-to-javascript-test.mjs";
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html
new file mode 100644
index 0000000000..af6fea0ad9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a javascript: URL navigated to from a data: URL on an origin-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/data-to-javascript-test.mjs";
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-no.https.html
new file mode 100644
index 0000000000..8ae564a072
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-no.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a data: URL on a site-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/data-url-test.mjs";
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html
new file mode 100644
index 0000000000..bcbf098a66
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a data: URL on an origin-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/data-url-test.mjs";
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-no.https.html
new file mode 100644
index 0000000000..1b54ad42a4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-no.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a javascript: URL on a site-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/data-url-test.mjs";
+runTest({ expected: false });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html
new file mode 100644
index 0000000000..e2b7730dd2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a javascript: URL on an origin-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/data-url-test.mjs";
+runTest({ expected: true });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html
new file mode 100644
index 0000000000..fcf5068908
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a removed frame</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import { navigateIframe } from "../resources/helpers.mjs";
+
+promise_test(async () => {
+ // We cannot use insertIframe because it sets both `document.domain`s. That
+ // shouldn't matter, but Chrome has a bug (https://crbug.com/1095145), so
+ // let's avoid making the test needlessly fail because of that bug.
+ const iframe = document.createElement("iframe");
+ const navigatePromise = navigateIframe(iframe, "{{hosts[][]}}", "?1");
+ document.body.append(iframe);
+ await navigatePromise;
+
+ const frameWindow = iframe.contentWindow;
+
+ assert_equals(frameWindow.originAgentCluster, true, "before");
+ iframe.remove();
+ assert_equals(frameWindow.originAgentCluster, true, "after");
+}, "Removing the iframe does not change originAgentCluster");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-to-javascript-test.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-to-javascript-test.mjs
new file mode 100644
index 0000000000..3a88253ee3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-to-javascript-test.mjs
@@ -0,0 +1,33 @@
+import { insertCustomIframe, testSupportScript } from "./helpers.mjs";
+import { waitForIframe, testGetter } from "../../resources/helpers.mjs";
+
+const testSupportScriptSuitableForNesting =
+ testSupportScript.replace('</script>', '</scri` + `pt>');
+
+export default () => {
+ promise_setup(async () => {
+ const jsURL = `javascript:'${testSupportScript}'`;
+ const iframe = await insertCustomIframe(`data:text/html,
+ Start page
+ <script>
+ window.onmessage = () => {
+ location.href = \`javascript:'End page${testSupportScriptSuitableForNesting}'\`;
+ };
+ </script>
+ `);
+
+ const waitPromise = waitForIframe(iframe, "javascript: URL");
+
+ // Kick off the navigation. We can't do it directly because only same-origin
+ // pages can navigate to a javascript: URL, and we're not same-origin with
+ // a data: URL.
+ iframe.contentWindow.postMessage(undefined, "*");
+
+ await waitPromise;
+ });
+
+ // The javascript: URL iframe inherits its origin from the previous occupant
+ // of the iframe, which is a data: URL, so it should always be true.
+
+ testGetter(0, true);
+};
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-url-test.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-url-test.mjs
new file mode 100644
index 0000000000..1a9b3be47f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-url-test.mjs
@@ -0,0 +1,13 @@
+import { insertCustomIframe, testSupportScript } from "./helpers.mjs";
+import { testGetter } from "../../resources/helpers.mjs";
+
+export default () => {
+ promise_setup(() => {
+ return insertCustomIframe(`data:text/html,${testSupportScript}`);
+ });
+
+ // The data: URL iframe has an opaque origin, so it should return true, since
+ // for them site === origin so they are always origin-keyed.
+
+ testGetter(0, true, "data: URL child");
+};
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/helpers.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/helpers.mjs
new file mode 100644
index 0000000000..4610ffcad0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/helpers.mjs
@@ -0,0 +1,28 @@
+import { waitForIframe } from "../../resources/helpers.mjs";
+
+/**
+ * Inserts an iframe, not specialized for origin-keyed agent cluster testing,
+ * pointing to a custom URL. This is just a wrapper to remove some boilerplate.
+ * @param {string} src - The src="" value for the iframe
+ */
+export async function insertCustomIframe(src) {
+ const iframe = document.createElement("iframe");
+ iframe.src = src;
+
+ const waitPromise = waitForIframe(iframe);
+ document.body.append(iframe);
+ await waitPromise;
+
+ return iframe;
+}
+
+/**
+ * This is the part of send-oac-header.py that allows us to reuse testGetter.
+ */
+export const testSupportScript = `
+ <script>
+ window.onmessage = () => {
+ parent.postMessage(self.originAgentCluster, "*");
+ };
+ </script>
+`;
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/javascript-url-test.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/javascript-url-test.mjs
new file mode 100644
index 0000000000..de474d8caf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/javascript-url-test.mjs
@@ -0,0 +1,14 @@
+import { insertCustomIframe, testSupportScript } from "./helpers.mjs";
+import { testGetter } from "../../resources/helpers.mjs";
+
+export default ({ expected }) => {
+ promise_setup(() => {
+ return insertCustomIframe(`javascript:'${testSupportScript}'`);
+ });
+
+ // The javascript: URL iframe inherits its origin from the previous occupant
+ // of the iframe, which is about:blank, which in turn inherits from the
+ // parent. So, the caller needs to tell us what to expect.
+
+ testGetter(0, expected);
+};
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-iframe-test.sub.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-iframe-test.sub.mjs
new file mode 100644
index 0000000000..9357df00c5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-iframe-test.sub.mjs
@@ -0,0 +1,20 @@
+import {
+ navigateIframe,
+ testGetter
+} from "../../resources/helpers.mjs";
+
+export default () => {
+ // We do this manually instead of using insertIframe because we want to add a
+ // sandbox="" attribute and we don't want to set both document.domains.
+ promise_setup(() => {
+ const iframe = document.createElement("iframe");
+ iframe.sandbox = "allow-scripts";
+ const navigatePromise = navigateIframe(iframe, "{{hosts[][]}}", "?1");
+ document.body.append(iframe);
+ return navigatePromise;
+ });
+
+ // Sandboxed iframes have an opaque origin, so it should return true, since
+ // for them site === origin so they are always origin-keyed.
+ testGetter(0, true);
+};
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-same-origin-iframe-test.sub.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-same-origin-iframe-test.sub.mjs
new file mode 100644
index 0000000000..272f805870
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-same-origin-iframe-test.sub.mjs
@@ -0,0 +1,20 @@
+import {
+ navigateIframe,
+ testGetter
+} from "../../resources/helpers.mjs";
+
+export default ({ expected }) => {
+ // We do this manually instead of using insertIframe because we want to add a
+ // sandbox="" attribute and we don't want to set both document.domains.
+ promise_setup(() => {
+ const iframe = document.createElement("iframe");
+ iframe.sandbox = "allow-scripts allow-same-origin";
+ const navigatePromise = navigateIframe(iframe, "{{hosts[][]}}", "?1");
+ document.body.append(iframe);
+ return navigatePromise;
+ });
+
+ // Since the allow-same-origin token is set, this should behave like a normal
+ // iframe, and follow the embedder.
+ testGetter(0, expected);
+};
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-no.https.html
new file mode 100644
index 0000000000..29758a17b8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-no.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a sandboxed iframe on a site-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/sandboxed-iframe-test.sub.mjs";
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html
new file mode 100644
index 0000000000..5eb5d08d10
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a sandboxed iframe on an origin-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/sandboxed-iframe-test.sub.mjs";
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-no.https.html
new file mode 100644
index 0000000000..3ed4096f39
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-no.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a sandboxed, but same-origin, iframe on a site-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/sandboxed-same-origin-iframe-test.sub.mjs";
+runTest({ expected: false });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html
new file mode 100644
index 0000000000..c7ea5f0693
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.originAgentCluster for a sandboxed, but same-origin, iframe on an origin-keyed page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import runTest from "./resources/sandboxed-same-origin-iframe-test.sub.mjs";
+runTest({ expected: true });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html
new file mode 100644
index 0000000000..a593619ea6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, child1 is site-keyed, child1 navigates to a different site, child2 gets inserted and is origin-keyed, child1 navigates back</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ waitForIframe,
+ setBothDocumentDomains,
+ testDifferentAgentClusters,
+ testSameAgentCluster,
+} from "./resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][www]}}");
+});
+
+// Since they're different-origin, the parent's origin-keying request is
+// respected, as is the child's non-request. So the parent ends up in the
+// origin-keyed agent cluster and the child ends up in the site-keyed one.
+testDifferentAgentClusters([self, 0], "Before navigation: parent to child1");
+
+// Navigate the iframe to a different site. These, of course, must not be in the
+// same agent cluster.
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[alt][]}}");
+}, "Navigation");
+
+// Now insert a second iframe, pointing to the same place as the first one
+// originally did, but this time with origin-keying requested. Because of the
+// historical map of agent cluster keys for the browsing context group, the new
+// iframe should still end up in the site-keyed agent cluster.
+
+promise_test(async () => {
+ await insertIframe("{{hosts[][www]}}", "?1");
+}, "Inserting a second iframe");
+
+testDifferentAgentClusters([self, 1], "After navigation: parent to child2");
+
+// Now navigate the first iframe back. The resulting Document should be put in
+// the site-keyed agent cluster, together with the second iframe's Document.
+
+promise_test(async () => {
+ const waitPromise = waitForIframe(frame1);
+ history.back();
+ await waitPromise;
+
+ await setBothDocumentDomains(frames[0]);
+}, "Going back in history (navigating back the first iframe)");
+
+testDifferentAgentClusters([self, 0], "After back: parent to child1");
+testDifferentAgentClusters([self, 1], "After back: parent to child2");
+testSameAgentCluster([0, 1], "child1 to child2");
+testSameAgentCluster([1, 0], "child2 to child1");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-port.sub.https.html
new file mode 100644
index 0000000000..8237f2f23f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-port.sub.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, navigate a frame from same-origin site-keyed to different-origin (different-port) origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][]}}");
+});
+
+// Nobody requested origin-keying yet.
+
+testSameAgentCluster([self, 0], "Before: parent to child");
+testGetter(self, false, "before parent");
+testGetter(0, false, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][]}}:{{ports[https][1]}}", "?1");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Since the new page is different-origin, its origin-keying request should be
+// respected.
+
+testDifferentAgentClusters([self, 0], "After: parent to child");
+testGetter(self, false, "after parent");
+testGetter(0, true, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..00d8c3164a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-subdomain.sub.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, navigate a frame from same-origin site-keyed to different-origin (subdomain) origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][]}}");
+});
+
+// Nobody requested origin-keying yet.
+
+testSameAgentCluster([self, 0], "Before: parent to child");
+testGetter(self, false, "before parent");
+testGetter(0, false, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][www]}}", "?1");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Since the new page is different-origin, its origin-keying request should be
+// respected.
+
+testDifferentAgentClusters([self, 0], "After: parent to child");
+testGetter(self, false, "after parent");
+testGetter(0, true, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..803e684e1c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, navigate a frame from a subdomain site-keyed to the same subdomain origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][www]}}");
+});
+
+// Nobody requested origin-keying yet.
+
+testSameAgentCluster([self, 0], "Before: parent to child");
+testGetter(self, false, "before parent");
+testGetter(0, false, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][www]}}", "?1");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Because this subdomain was previously site-keyed, the second load's
+// origin-keying request is ignored; instead we continue with site-keying.
+
+testSameAgentCluster([self, 0], "After: parent to child");
+testGetter(self, false, "after parent");
+testGetter(0, false, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain2.sub.https.html
new file mode 100644
index 0000000000..b96d10afd1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain2.sub.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, navigate a frame from a subdomain site-keyed to a second-subdomain origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][www]}}");
+});
+
+// Nobody requested origin-keying yet.
+
+testSameAgentCluster([self, 0], "Before: parent to child");
+testGetter(self, false, "before parent");
+testGetter(0, false, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][www1]}}", "?1");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Because we're going to a different subdomain (and thus different origin), the
+// origin-keying request is respected.
+
+testDifferentAgentClusters([self, 0], "After: parent to child");
+testGetter(self, false, "after parent");
+testGetter(0, true, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html
new file mode 100644
index 0000000000..a70ed56670
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, navigate a frame from a subdomain origin-keyed to a second-subdomain site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+// Since they are different-origin, the child's origin-keying request is
+// respected.
+
+testDifferentAgentClusters([self, 0], "Before: parent to child");
+testGetter(self, false, "before parent");
+testGetter(0, true, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][www1]}}");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Make sure that the different-subdomain page (which doesn't request
+// origin-keying) doesn't somehow get origin-keyed just because its predecessor
+// was.
+
+testSameAgentCluster([self, 0], "After: parent to child");
+testGetter(self, false, "after parent");
+testGetter(0, false, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-yes-subdomain-2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-yes-subdomain-2-no-subdomain.sub.https.html
new file mode 100644
index 0000000000..38e2630128
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-yes-subdomain-2-no-subdomain.sub.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is site-keyed, navigate a frame from a subdomain origin-keyed to the same subdomain site-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+// Since they are different-origin, the child's origin-keying request is
+// respected.
+
+testDifferentAgentClusters([self, 0], "Before: parent to child");
+testGetter(self, false, "before parent");
+testGetter(0, true, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][www]}}");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Because this subdomain was previously origin-keyed, the second load's
+// non-request is ignored; instead we continue origin-keying.
+
+testDifferentAgentClusters([self, 0], "After: parent to child");
+testGetter(self, false, "after parent");
+testGetter(0, true, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html
new file mode 100644
index 0000000000..6211845be1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, navigate a frame from same-origin site-keyed to different-origin (different-port) origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][]}}");
+});
+
+// Since the parent is origin-keyed, the same-origin child's non-request is
+// ignored, so it gets origin-keyed too.
+
+testSameAgentCluster([self, 0], "Before: parent to child");
+testGetter(self, true, "before parent");
+testGetter(0, true, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][]}}:{{ports[https][1]}}");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Since the new page is different-origin, its non-request should be respected.
+
+testDifferentAgentClusters([self, 0], "After: parent to child");
+testGetter(self, true, "after parent");
+testGetter(0, false, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html
new file mode 100644
index 0000000000..ead56754a7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent is origin-keyed, navigate a frame from same-origin site-keyed to different-origin (subdomain) origin-keyed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ navigateIframe,
+ setBothDocumentDomains,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][]}}");
+});
+
+// Since the parent is origin-keyed, the same-origin child's non-request is
+// ignored, so it gets origin-keyed too.
+
+testSameAgentCluster([self, 0], "Before: parent to child");
+testGetter(self, true, "before parent");
+testGetter(0, true, "before child");
+
+promise_test(async () => {
+ await navigateIframe(frame1, "{{hosts[][www]}}");
+ await setBothDocumentDomains(frames[0]);
+}, "Navigation");
+
+// Since the new page is different-origin, its non-request should be respected.
+
+testDifferentAgentClusters([self, 0], "After: parent to child");
+testGetter(self, true, "after parent");
+testGetter(0, false, "after child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html
new file mode 100644
index 0000000000..6f9e5d8b73
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Parent requests origin-keying, child requests origin-keying, child is a subdomain of the parent, but all over insecure HTTP</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testGetter
+} from "./resources/helpers.mjs";
+
+promise_setup(async () => {
+ await insertIframe("{{hosts[][www]}}", "?1");
+});
+
+// All origin-keying requests are ignored, since this is over insecure HTTP.
+// So both end up in the site-keyed agent cluster.
+testSameAgentCluster([self, 0]);
+
+testGetter(self, false, "parent");
+testGetter(0, false, "child");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html
new file mode 100644
index 0000000000..dcfb5eb277
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Crash test for https://crbug.com/1099718</title>
+
+<div id="log"></div>
+
+<script>
+window.open("resources/crashy-popup.sub.html", "windowName1", "noopener");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-port.sub.https.html
new file mode 100644
index 0000000000..a0bf569b12
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-port.sub.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is site-keyed, openee is origin-keyed, openee is different-origin to the opener because of a port mismatch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInADifferentAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][]}}:{{ports[https][1]}}", "?1");
+});
+
+// Since they're different-origin, the openee's origin-keying request is
+// respected, so the opener ends up in the site-keyed agent cluster and the
+// openee in the origin-keyed one.
+testOpenedWindowIsInADifferentAgentCluster(() => openee);
+
+testGetter(self, false, "opener");
+testGetter(() => openee, true, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-same.sub.https.html
new file mode 100644
index 0000000000..196dff1449
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-same.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is site-keyed, openee is origin-keyed, openee is same-origin to the opener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][]}}", "?1");
+});
+
+// Since they're same-origin, and the opener loaded with site-keying, the
+// child's request for origin-keying gets ignored, and both end up site-keyed.
+testOpenedWindowIsInSameAgentCluster(() => openee);
+
+testGetter(self, false, "opener");
+testGetter(() => openee, false, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..f96d2273d5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-subdomain.sub.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is site-keyed, openee is origin-keyed, openee is a subdomain of the opener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInADifferentAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][www]}}", "?1");
+});
+
+// Since they're different-origin, the openee's origin-keying request is
+// respected, so the opener ends up in the site-keyed agent cluster and the
+// openee in the origin-keyed one.
+testOpenedWindowIsInADifferentAgentCluster(() => openee);
+
+testGetter(self, false, "opener");
+testGetter(() => openee, true, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html
new file mode 100644
index 0000000000..51c5a208c5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is origin-keyed, openee is site-keyed, openee is different-origin to the opener because of a port mismatch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInADifferentAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][]}}:{{ports[https][1]}}");
+});
+
+// Since they're different-origin, the openee's non-request is respected, so the
+// opener ends up in the origin-keyed agent cluster and the openee in the
+// site-keyed one.
+testOpenedWindowIsInADifferentAgentCluster(() => openee);
+
+testGetter(self, true, "opener");
+testGetter(() => openee, false, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html
new file mode 100644
index 0000000000..562ab40c68
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is origin-keyed, openee is site-keyed, openee is same-origin to the opener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][]}}");
+});
+
+// Since they're same-origin, the openee's non-request is ignored, so both end
+// up in the origin-keyed agent cluster.
+testOpenedWindowIsInSameAgentCluster(() => openee);
+
+testGetter(self, true, "opener");
+testGetter(() => openee, true, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html
new file mode 100644
index 0000000000..d7d4791459
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is origin-keyed, openee is site-keyed, openee is a subdomain of the opener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInADifferentAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][www]}}");
+});
+
+// Since they're different-origin, the openee's non-request is respected, so the
+// opener ends up in the origin-keyed agent cluster and the openee in the
+// site-keyed one.
+testOpenedWindowIsInADifferentAgentCluster(() => openee);
+
+testGetter(self, true, "opener");
+testGetter(() => openee, false, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html
new file mode 100644
index 0000000000..32a5066d2e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is origin-keyed, openee is origin-keyed, openee is different-origin to the opener because of a port mismatch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInADifferentAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][]}}:{{ports[https][1]}}", "?1");
+});
+
+// Both request origin-keying, so the opener ends up in one origin-keyed agent
+// cluster (the default port's origin), and the openee ends up in a different
+// origin-keyed agent cluster (the other port's origin).
+testOpenedWindowIsInADifferentAgentCluster(() => openee);
+
+testGetter(self, true, "opener");
+testGetter(() => openee, true, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html
new file mode 100644
index 0000000000..a85decac3c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is origin-keyed, openee is origin-keyed, openee is same-origin to the opener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInSameAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][]}}", "?1");
+});
+
+// Both request origin-keying, and they're same-origin, so they both end up in
+// the same origin-keyed agent cluster.
+testOpenedWindowIsInSameAgentCluster(() => openee);
+
+testGetter(self, true, "opener");
+testGetter(() => openee, true, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html
new file mode 100644
index 0000000000..148b39af23
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Opener is origin-keyed, openee is origin-keyed, openee is a subdomain of the opener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ openWindow,
+ testOpenedWindowIsInADifferentAgentCluster,
+ testGetter
+} from "../resources/helpers.mjs";
+
+let openee;
+promise_setup(async () => {
+ openee = await openWindow("{{hosts[][www]}}", "?1");
+});
+
+// Both request origin-keyed, so the opener ends up in one origin-keyed agent
+// cluster (the base domain's origin), and the openee ends up in a different
+// origin-keyed agent cluster (the www subdomain's origin).
+testOpenedWindowIsInADifferentAgentCluster(() => openee);
+
+testGetter(self, true, "opener");
+testGetter(() => openee, true, "openee");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/regression-1399759.https.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/regression-1399759.https.sub.html
new file mode 100644
index 0000000000..d0b09f335d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/regression-1399759.https.sub.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="variant" content="?pipe=header(Origin-Agent-Cluster,%3F0)">
+<meta name="variant" content="?pipe=header(Origin-Agent-Cluster,%3F1)">
+<title>Origin-Isolation after navigating about:blank.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+</head>
+<body>
+</body>
+<script>
+// Regression test for crbug.com/1399759. This is mainly based on
+// html/infrastructure/urls/terminology-0/document-base-url-initiated-grand-parent.https.window.html,
+// but restricts itself to the exact error condition.
+//
+// This test is run in two variants which differ in the Origin-Agent-Cluster
+// http header values, ?0 and ?1. The test should pass in either case, but the
+// regression we're testing for involves inconsistent clustering decisions,
+// which requires clustering to be enabled in the first place.
+promise_test(async test => {
+ // Create a cross-origin iframe. Use the executor.html, so we can ask it
+ // to execute scripts for us.
+ const child_token = token();
+ const iframe = document.createElement("iframe");
+ iframe.src = get_host_info().HTTPS_REMOTE_ORIGIN +
+ `/common/dispatcher/executor.html?uuid=${child_token}`;
+ document.body.appendChild(iframe);
+
+ // The child creates a grand child in an iframe.
+ const reply_token = token();
+ send(child_token, `
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ iframe.onload = () => {
+ send("${reply_token}", "grand child loaded");
+ };
+ document.body.appendChild(iframe);
+ `);
+ assert_equals(await receive(reply_token), "grand child loaded");
+ const grandchild = iframe.contentWindow[0];
+
+ // Navigate the grand-child toward about:blank.
+ grandchild.location = "about:blank";
+ assert_equals(await receive(reply_token), "grand child loaded");
+
+ // This document and grandchild are same-origin, because about:blank
+ // inherits its origin from the initiator of the navigation, which is us.
+ // This access should not throw.
+ grandchild.document;
+}, "Check the baseURL of an about:blank document cross-origin with its parent");
+
+promise_test(async test => {
+ // This tests the same setup as above, but with about:srcdoc. Since one
+ // cannot just navigate to about:srcdoc, we'll have to include an extra
+ // step: Create an iframe with srcdoc attribute; navigate away; then
+ // navigate to about:srcdoc.
+ // srcdoc does not inherit the origin from the initiator - unlike
+ // about:blank - and so in this case the grandchild.document access should
+ // throw.
+
+ // Create a cross-origin iframe. Use the executor.html, so we can ask it
+ // to execute scripts for us.
+ const child_token = token();
+ const iframe = document.createElement("iframe");
+ iframe.src = get_host_info().HTTPS_REMOTE_ORIGIN +
+ `/common/dispatcher/executor.html?uuid=${child_token}`;
+ document.body.appendChild(iframe);
+
+ // The child creates a grand child in an iframe, using the srcdoc attribute.
+ const reply_token = token();
+ send(child_token, `
+ const iframe = document.createElement("iframe");
+ iframe.onload = () => {
+ send("${reply_token}", "grand child loaded");
+ };
+ iframe.srcdoc = "nothing interesting";
+ document.body.appendChild(iframe);
+ `);
+ assert_equals(await receive(reply_token), "grand child loaded");
+ const grandchild = iframe.contentWindow[0];
+
+ // Navigate the grand child toward a regular URL.
+ grandchild.location = get_host_info().HTTPS_REMOTE_ORIGIN + "/common/blank.html";
+ assert_equals(await receive(reply_token), "grand child loaded");
+
+ // Navigate the grand-child back, to about:srcdoc.
+ grandchild.location = "about:srcdoc";
+ assert_equals(await receive(reply_token), "grand child loaded");
+
+ // This document and grandchild are cross-origin. about:srcdoc does not
+ // inherits its origin from the initiator of the navigation. This access
+ // should throw:
+ assert_throws_dom("SecurityError", () => { grandchild.document; });
+}, "Check that about:srcdoc navigation does not follow about:blank rules.");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html
new file mode 100644
index 0000000000..b83aa9f5be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A site-keyed child at a given origin causes future children to also be site-keyed even after the iframe is removed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script type="module">
+import {
+ insertIframe,
+ testSameAgentCluster,
+ testDifferentAgentClusters,
+ testGetter
+} from "./resources/helpers.mjs";
+
+let frame1;
+promise_setup(async () => {
+ frame1 = await insertIframe("{{hosts[][www]}}");
+});
+
+// Since they're different-origin, the parent's origin-keying request is
+// respected, as is the child's non-request. So the parent ends up in the
+// origin-keyed agent cluster and the child ends up in the site-keyed one.
+testDifferentAgentClusters([self, 0], "Before");
+testGetter(self, true, "parent");
+testGetter(0, false, "child1");
+
+promise_test(async () => {
+ frame1.remove();
+
+ await insertIframe("{{hosts[][www]}}", "?1");
+ await insertIframe("{{hosts[][www1]}}");
+}, "Remove the iframe and insert new ones");
+
+// Because of the historical presence of a site-keyed {{hosts[][www]}} iframe,
+// the origin-keying request for child 2 will be ignored. So,
+// child 2 and child 3 both end up in the site-keyed agent cluster.
+testDifferentAgentClusters([self, 0], "Parent to child2");
+testDifferentAgentClusters([self, 1], "Parent to child3");
+testSameAgentCluster([0, 1], "child2 to child3");
+testSameAgentCluster([1, 0], "child3 to child2");
+
+testGetter(0, false, "child2");
+testGetter(1, false, "child3");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/README.md b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/README.md
new file mode 100644
index 0000000000..9292fe3894
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/README.md
@@ -0,0 +1,6 @@
+Why are there `.headers` files here for the `.mjs` scripts?
+
+Because `../getter-special-cases/sandboxed-iframe.sub.https.html` is testing an
+opaque origin, which is cross-origin with these scripts. Since
+`<script type="module">` respects the same-origin policy, we need CORS headers
+to allow them to be accessed.
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html
new file mode 100644
index 0000000000..7cbd89b943
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A page with COEP set that will respond when asked</title>
+
+<script type="module" src="send-header-page-script.mjs"></script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html
new file mode 100644
index 0000000000..45c8d5074d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This page helps exhibit a crash bug when window.open()ed (see ../popups-crash.https.html)</title>
+
+<iframe src="https://{{hosts[][www]}}:{{ports[https][0]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py"></iframe>
+<iframe src="https://{{hosts[][www]}}:{{ports[https][0]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py?header=?1"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html
new file mode 100644
index 0000000000..537de07b14
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A frame included by a test page</title>
+
+<script>
+window.onmessage = e => {
+ if (e.data.type === "set document.domain") {
+ document.domain = e.data.newValue;
+ e.source.postMessage({ type: "new document.domain", result: document.domain }, "*");
+ }
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html.headers
new file mode 100644
index 0000000000..79a20f30fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html.headers
@@ -0,0 +1 @@
+Origin-Agent-Cluster: ?1
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs
new file mode 100644
index 0000000000..6bad76e3d9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs
@@ -0,0 +1,390 @@
+/**
+ * Inserts an iframe usable for origin-keyed agent cluster testing, and returns
+ * a promise fulfilled when the iframe is loaded and its document.domain is set.
+ * The iframe will point to the send-oac-header.py file, on the designated
+ * host.
+ * @param {string} host - The host used to calculate the iframe's src=""
+ * @param {string=} header - The value of the Origin-Agent-Cluster header that
+ * the iframe will set. Omit this to set no header.
+ * @param {object=} options - Rarely-used options.
+ * @param {boolean=} options.redirectFirst - Whether to do a 302 redirect first
+ * before arriving at the page that sets the header. The redirecting page will
+ * not set the Origin-Agent-Cluster header.
+ * @returns {HTMLIFrameElement} The created iframe element
+ */
+export async function insertIframe(host, header, { redirectFirst = false } = {}) {
+ const iframe = document.createElement("iframe");
+ const navigatePromise = navigateIframe(iframe, host, header, { redirectFirst });
+ document.body.append(iframe);
+ await navigatePromise;
+ await setBothDocumentDomains(iframe.contentWindow);
+ return iframe;
+}
+
+/**
+ * Navigates an iframe to a page for origin-keyed agent cluster testing, similar
+ * to insertIframe but operating on an existing iframe.
+ * @param {HTMLIFrameElement} iframeEl - The <iframe> element to navigate
+ * @param {string} host - The host to calculate the iframe's new src=""
+ * @param {string=} header - The value of the Origin-Agent-Cluster header that
+ * the newly-navigated-to page will set. Omit this to set no header.
+ * @param {object=} options - Rarely-used options.
+ * @param {boolean=} options.redirectFirst - Whether to do a 302 redirect first
+ * before arriving at the page that sets the header. The redirecting page will
+ * not set the Origin-Agent-Cluster header.
+ * @returns {Promise} a promise fulfilled when the load event fires, or rejected
+ * if the error event fires
+ */
+export function navigateIframe(iframeEl, host, header, { redirectFirst = false } = {}) {
+ const url = getSendHeaderURL(host, header, { redirectFirst });
+
+ const waitPromise = waitForIframe(iframeEl, url);
+ iframeEl.src = url;
+ return waitPromise;
+}
+
+/**
+ * Returns a promise that is fulfilled when an iframe's load event fires, or
+ * rejected when its error event fires.
+ * @param {HTMLIFrameElement} iframeEl - The <iframe> element to wait on
+ * @param {string} destinationForErrorMessage - A string used in the promise
+ * rejection error message, if the error event fires
+ * @returns {Promise} a promise fulfilled when the load event fires, or rejected
+ * if the error event fires
+ */
+export function waitForIframe(iframeEl, destinationForErrorMessage) {
+ return new Promise((resolve, reject) => {
+ iframeEl.addEventListener("load", () => resolve());
+ iframeEl.addEventListener(
+ "error",
+ () => reject(new Error(`Could not navigate to ${destinationForErrorMessage}`))
+ );
+ });
+}
+
+/**
+ * Opens a new window usable for origin-keyed agent cluster testing, and returns
+ * a promise fulfilled when the window is loaded and its document.domain is set.
+ * The window will point to the send-oac-header.py file, on the designated host.
+ *
+ * The opened window will be automatically closed when all the tests complete.
+ * @param {string} host - The host used to calculate the window's URL
+ * @param {string=} header - The value of the Origin-Agent-Cluster header that
+ * the opened window's page will set. Omit this to set no header.
+ * @returns {WindowProxy} The created window
+ */
+export async function openWindow(host, header) {
+ const url = getSendHeaderURL(host, header, { sendLoadedMessage: true });
+ const openedWindow = window.open(url);
+
+ add_completion_callback(() => openedWindow.close());
+
+ const whatHappened = await waitForMessage(openedWindow);
+ assert_equals(whatHappened, "loaded");
+
+ await setBothDocumentDomains(openedWindow);
+
+ return openedWindow;
+}
+
+/**
+ * Expands into a pair of promise_test() calls to ensure that two Windows are in
+ * the same agent cluster, by checking both that we can send a
+ * WebAssembly.Module, and that we can synchronously access the DOM.
+ * @param {Array} testFrames - An array of either the form [self, frameIndex] or
+ * [frameIndex1, frameIndex2], indicating the two Windows under test. E.g.
+ * [self, 0] or [0, 1].
+ * @param {string=} testLabelPrefix - A prefix used in the test names. This can
+ * be omitted if testSameAgentCluster is only used once in a test file.
+ */
+export function testSameAgentCluster(testFrames, testLabelPrefix) {
+ const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `;
+
+ if (testFrames[0] === self) {
+ // Between parent and a child at the index given by testFrames[1]
+
+ promise_test(async () => {
+ const frameWindow = frames[testFrames[1]];
+ const frameElement = document.querySelectorAll("iframe")[testFrames[1]];
+
+ // Must not throw
+ frameWindow.document;
+
+ // Must not throw
+ frameWindow.location.href;
+
+ assert_not_equals(frameElement.contentDocument, null, "contentDocument");
+
+ const whatHappened = await accessFrameElement(frameWindow);
+ assert_equals(whatHappened, "frameElement accessed successfully");
+ }, `${prefix}setting document.domain must give sync access`);
+ } else {
+ // Between the two children at the index given by testFrames[0] and
+ // testFrames[1]
+
+ promise_test(async () => {
+ const whatHappened1 = await accessDocumentBetween(testFrames);
+ assert_equals(whatHappened1, "accessed document successfully");
+
+ const whatHappened2 = await accessLocationHrefBetween(testFrames);
+ assert_equals(whatHappened2, "accessed location.href successfully");
+
+ // We don't test contentDocument/frameElement for these because accessing
+ // those via siblings has to go through the parent anyway.
+ }, `${prefix}setting document.domain must give sync access`);
+ }
+}
+
+/**
+ * Expands into a pair of promise_test() calls to ensure that two Windows are in
+ * different agent clusters, by checking both that we cannot send a
+ * WebAssembly.Module, and that we cannot synchronously access the DOM.
+ * @param {Array} testFrames - An array of either the form [self, frameIndex] or
+ * [frameIndex1, frameIndex2], indicating the two Windows under test. E.g.
+ * [self, 0] or [0, 1].
+ * @param {string=} testLabelPrefix - A prefix used in the test names. This can
+ * be omitted if testDifferentAgentClusters is only used once in a test file.
+ */
+export function testDifferentAgentClusters(testFrames, testLabelPrefix) {
+ const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `;
+
+ if (testFrames[0] === self) {
+ // Between parent and a child at the index given by testFrames[1]
+
+ promise_test(async () => {
+ // In general, cross-origin sharing of WebAssembly.Module is prohibited,
+ // so if we're in different agent clusters, it's definitely prohibited.
+ // Basic tests for this cross-origin prohibition are elsewhere; we include
+ // these here as an extra check to make sure there's no weird interactions
+ // with Origin-Agent-Cluster.
+ const frameWindow = frames[testFrames[1]];
+ const whatHappened = await sendWasmModule(frameWindow);
+
+ assert_equals(whatHappened, "messageerror");
+ }, `${prefix}messageerror event must occur`);
+
+ promise_test(async () => {
+ const frameWindow = frames[testFrames[1]];
+ const frameElement = document.querySelectorAll("iframe")[testFrames[1]];
+
+ assert_throws_dom("SecurityError", DOMException, () => {
+ frameWindow.document;
+ });
+
+ assert_throws_dom("SecurityError", DOMException, () => {
+ frameWindow.location.href;
+ });
+
+ assert_equals(frameElement.contentDocument, null, "contentDocument");
+
+ const whatHappened = await accessFrameElement(frameWindow);
+ assert_equals(whatHappened, "null");
+ }, `${prefix}setting document.domain must not give sync access`);
+ } else {
+ // Between the two children at the index given by testFrames[0] and
+ // testFrames[1]
+
+ promise_test(async () => {
+ const whatHappened = await sendWasmModuleBetween(testFrames);
+ assert_equals(whatHappened, "messageerror");
+ }, `${prefix}messageerror event must occur`);
+
+ promise_test(async () => {
+ const whatHappened1 = await accessDocumentBetween(testFrames);
+ assert_equals(whatHappened1, "SecurityError");
+
+ const whatHappened2 = await accessLocationHrefBetween(testFrames);
+ assert_equals(whatHappened2, "SecurityError");
+
+ // We don't test contentDocument/frameElement for these because accessing
+ // those via siblings has to go through the parent anyway.
+ }, `${prefix}setting document.domain must not give sync access`);
+ }
+}
+
+/**
+ * Expands into a pair of promise_test() calls to ensure that the given window,
+ * opened by window.open(), is in a different agent cluster from the current
+ * (opener) window.
+ * @param {function} openedWindowGetter - A function that returns the opened
+ * window
+ */
+export function testOpenedWindowIsInADifferentAgentCluster(openedWindowGetter) {
+ promise_test(async () => {
+ const whatHappened = await sendWasmModule(openedWindowGetter());
+
+ assert_equals(whatHappened, "messageerror");
+ }, `messageerror event must occur`);
+
+ promise_test(async () => {
+ assert_throws_dom("SecurityError", DOMException, () => {
+ openedWindowGetter().document;
+ });
+
+ assert_throws_dom("SecurityError", DOMException, () => {
+ openedWindowGetter().location.href;
+ });
+ }, `setting document.domain must not give sync access`);
+}
+
+/**
+ * Expands into a pair of promise_test() calls to ensure that the given window,
+ * opened by window.open(), is in the same agent cluster as the current
+ * (opener) window.
+ * @param {function} openedWindowGetter - A function that returns the opened
+ * window
+ */
+export function testOpenedWindowIsInSameAgentCluster(openedWindowGetter) {
+ promise_test(async () => {
+ const whatHappened = await sendWasmModule(openedWindowGetter());
+
+ assert_equals(whatHappened, "WebAssembly.Module message received");
+ }, `message event must occur`);
+
+ promise_test(async () => {
+ // Must not throw
+ openedWindowGetter().document;
+
+ // Must not throw
+ openedWindowGetter().location.href;
+ }, `setting document.domain must give sync access`);
+}
+
+/**
+ * Creates a promise_test() to check the value of the originAgentCluster getter
+ * in the given testFrame.
+ * @param {Window|number|function} testFrame - Either self, or a frame index to
+ test, or a function that returns a Window to test.
+ * @param {boolean} expected - The expected value for originAgentCluster.
+ * @param {string=} testLabelPrefix - A prefix used in the test names. This can
+ * be omitted if the function is only used once in a test file.
+ */
+export function testGetter(testFrame, expected, testLabelPrefix) {
+ const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `;
+
+ promise_test(async () => {
+ if (testFrame === self) {
+ assert_equals(self.originAgentCluster, expected);
+ } else if (typeof testFrame === "number") {
+ const frameWindow = frames[testFrame];
+ const result = await accessOriginAgentCluster(frameWindow);
+ assert_equals(result, expected);
+ } else {
+ assert_equals(typeof testFrame, "function",
+ "testFrame argument must be self, a number, or a function");
+ const result = await accessOriginAgentCluster(testFrame());
+ assert_equals(result, expected);
+ }
+ }, `${prefix}originAgentCluster must equal ${expected}`);
+}
+
+/**
+ * Sends a WebAssembly.Module instance to the given Window, and waits for it to
+ * send back a message indicating whether it got the module or got a
+ * messageerror event. (This relies on the given Window being derived from
+ * insertIframe or navigateIframe.)
+ * @param {Window} frameWindow - The destination Window
+ * @returns {Promise} A promise which will be fulfilled with either
+ * "WebAssembly.Module message received" or "messageerror"
+ */
+export async function sendWasmModule(frameWindow) {
+ // This function is coupled to ./send-oac-header.py, which ensures that
+ // sending such a message will result in a message back.
+ frameWindow.postMessage(await createWasmModule(), "*");
+ return waitForMessage(frameWindow);
+}
+
+/**
+ * Sets document.domain (to itself) for both the current Window and the given
+ * Window. The latter relies on the given Window being derived from insertIframe
+ * or navigateIframe.
+ * @param frameWindow - The other Window whose document.domain is to be set
+ * @returns {Promise} A promise which will be fulfilled after both
+ * document.domains are set
+ */
+export async function setBothDocumentDomains(frameWindow) {
+ // By setting both this page's document.domain and the iframe's
+ // document.domain to the same value, we ensure that they can synchronously
+ // access each other, unless they are origin-keyed.
+ // NOTE: document.domain being unset is different than it being set to its
+ // current value. It is a terrible API.
+ document.domain = document.domain;
+
+ // This function is coupled to ./send-oac-header.py, which ensures that
+ // sending such a message will result in a message back.
+ frameWindow.postMessage({ command: "set document.domain", newDocumentDomain: document.domain }, "*");
+ const whatHappened = await waitForMessage(frameWindow);
+ assert_equals(whatHappened, "document.domain is set");
+}
+
+async function accessOriginAgentCluster(frameWindow) {
+ // This function is coupled to ./send-oac-header.py, which ensures that
+ // sending such a message will result in a message back.
+ frameWindow.postMessage({ command: "get originAgentCluster" }, "*");
+ return waitForMessage(frameWindow);
+}
+
+function getSendHeaderURL(host, header, { sendLoadedMessage = false, redirectFirst = false } = {}) {
+ const url = new URL("send-oac-header.py", import.meta.url);
+ url.host = host;
+ if (header !== undefined) {
+ url.searchParams.set("header", header);
+ }
+ if (sendLoadedMessage) {
+ url.searchParams.set("send-loaded-message", "");
+ }
+ if (redirectFirst) {
+ url.searchParams.set("redirect-first", "");
+ }
+
+ return url.href;
+}
+
+async function sendWasmModuleBetween(testFrames) {
+ const sourceFrame = frames[testFrames[0]];
+ const indexIntoParentFrameOfDestination = testFrames[1];
+
+ sourceFrame.postMessage({ command: "send WASM module", indexIntoParentFrameOfDestination }, "*");
+ return waitForMessage(sourceFrame);
+}
+
+async function accessDocumentBetween(testFrames) {
+ const sourceFrame = frames[testFrames[0]];
+ const indexIntoParentFrameOfDestination = testFrames[1];
+
+ sourceFrame.postMessage({ command: "access document", indexIntoParentFrameOfDestination }, "*");
+ return waitForMessage(sourceFrame);
+}
+
+async function accessLocationHrefBetween(testFrames) {
+ const sourceFrame = frames[testFrames[0]];
+ const indexIntoParentFrameOfDestination = testFrames[1];
+
+ sourceFrame.postMessage({ command: "access location.href", indexIntoParentFrameOfDestination }, "*");
+ return waitForMessage(sourceFrame);
+}
+
+async function accessFrameElement(frameWindow) {
+ frameWindow.postMessage({ command: "access frameElement" }, "*");
+ return waitForMessage(frameWindow);
+}
+
+function waitForMessage(expectedSource) {
+ return new Promise(resolve => {
+ const handler = e => {
+ if (e.source === expectedSource) {
+ resolve(e.data);
+ window.removeEventListener("message", handler);
+ }
+ };
+ window.addEventListener("message", handler);
+ });
+}
+
+// Any WebAssembly.Module will work fine for our tests; we just want to find out
+// if it gives message or messageerror. So, we reuse one from the /wasm/ tests.
+async function createWasmModule() {
+ const response = await fetch("/wasm/serialization/module/resources/incrementer.wasm");
+ const ab = await response.arrayBuffer();
+ return WebAssembly.compile(ab);
+}
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs.headers
new file mode 100644
index 0000000000..cb762eff80
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs
new file mode 100644
index 0000000000..17b00684d6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs
@@ -0,0 +1,63 @@
+import { sendWasmModule } from "./helpers.mjs";
+
+// This is done for the window.open() case. For <iframe>s we use the
+// <iframe> element's load event instead.
+const usp = new URLSearchParams(location.search);
+if (usp.has("send-loaded-message")) {
+ opener.postMessage("loaded", "*");
+}
+
+window.onmessage = async (e) => {
+ // These could come from the parent, opener, or siblings.
+ if (e.data.constructor === WebAssembly.Module) {
+ e.source.postMessage("WebAssembly.Module message received", "*");
+ }
+
+ // These could come from the parent or opener.
+ if (e.data.command === "set document.domain") {
+ document.domain = e.data.newDocumentDomain;
+ e.source.postMessage("document.domain is set", "*");
+ } else if (e.data.command === "get originAgentCluster") {
+ e.source.postMessage(self.originAgentCluster, "*");
+ }
+
+ // These only come from the parent.
+ if (e.data.command === "send WASM module") {
+ const destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination];
+ const whatHappened = await sendWasmModule(destinationFrameWindow);
+ parent.postMessage(whatHappened, "*");
+ } else if (e.data.command === "access document") {
+ const destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination];
+ try {
+ destinationFrameWindow.document;
+ parent.postMessage("accessed document successfully", "*");
+ } catch (e) {
+ parent.postMessage(e.name, "*");
+ }
+ } else if (e.data.command === "access location.href") {
+ const destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination];
+ try {
+ destinationFrameWindow.location.href;
+ parent.postMessage("accessed location.href successfully", "*");
+ } catch (e) {
+ parent.postMessage(e.name, "*");
+ }
+ } else if (e.data.command === "access frameElement") {
+ if (frameElement === null) {
+ parent.postMessage("null", "*");
+ } else if (frameElement?.constructor?.name === "HTMLIFrameElement") {
+ parent.postMessage("frameElement accessed successfully", "*");
+ } else {
+ parent.postMessage("something wierd happened", "*");
+ }
+ }
+
+ // We could also receive e.data === "WebAssembly.Module message received",
+ // but that's handled by await sendWasmModule() above.
+};
+
+window.onmessageerror = e => {
+ e.source.postMessage("messageerror", "*");
+};
+
+document.body.textContent = location.href;
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs.headers
new file mode 100644
index 0000000000..cb762eff80
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py
new file mode 100644
index 0000000000..cc8860fe75
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py
@@ -0,0 +1,43 @@
+def main(request, response):
+ """Send a response with the Origin-Agent-Cluster header given in the
+ "header" query parameter, or no header if that is not provided. Other query
+ parameters (only their presence/absence matters) are "send-loaded-message"
+ and "redirect-first", which modify the behavior a bit.
+
+ In either case, the response will listen for various messages posted and
+ coordinate with the sender. See ./helpers.mjs for how these handlers are
+ used.
+ """
+
+ if b"redirect-first" in request.GET:
+ # Create a new query string, which is the same as the one we're given but
+ # with the redirect-first component stripped out. This allows tests to use
+ # any value (or no value) for the other query params, in combination with
+ # redirect-first.
+ query_string_pieces = []
+ if b"header" in request.GET:
+ query_string_pieces.append(b"header=" + request.GET.first(b"header"))
+ if b"send-loaded-message" in request.GET:
+ query_string_pieces.append(b"send-loaded-message")
+ query_string = b"?" + b"&".join(query_string_pieces)
+
+ return (
+ 302,
+ [(b"Location", b"/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py" + query_string)],
+ u""
+ )
+
+ if b"header" in request.GET:
+ header = request.GET.first(b"header")
+ response.headers.set(b"Origin-Agent-Cluster", header)
+
+ response.headers.set(b"Content-Type", b"text/html")
+
+ return u"""
+ <!DOCTYPE html>
+ <meta charset="utf-8">
+ <title>Helper page for origin-keyed agent cluster tests</title>
+
+ <body>
+ <script type="module" src="send-header-page-script.mjs"></script>
+ """
diff --git a/testing/web-platform/tests/html/browsers/origin/origin-of-data-document.html b/testing/web-platform/tests/html/browsers/origin/origin-of-data-document.html
new file mode 100644
index 0000000000..448f47fa24
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/origin-of-data-document.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title>Origin of document produced from a 'data:' URL</title>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#origin">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ async_test(function (t) {
+ var i = document.createElement('iframe');
+ i.src = "data:text/html,<script>" +
+ " window.parent.postMessage('Hello!', '*');" +
+ "</scr" + "ipt>";
+
+ window.addEventListener("message", t.step_func_done(function (e) {
+ assert_equals(e.origin, "null", "Messages sent from a 'data:' URL should have an opaque origin (which serializes to 'null').");
+ assert_throws_dom("SecurityError", function () {
+ var couldAccessCrossOriginProperty = e.source.location.href;
+ }, "The 'data:' frame should be cross-origin: 'window.location.href'");
+ assert_equals(i.contentDocument, null, "The 'data:' iframe should be unable to access its contentDocument.");
+ }));
+
+ document.body.appendChild(i);
+ }, "The origin of a 'data:' document in a frame is opaque.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html
new file mode 100644
index 0000000000..d3af35c6d7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html>
+ <head>
+ <title>document.domain's getter</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ test(function() {
+ assert_equals(typeof document.domain, "string", "document.domain is a string");
+ assert_not_equals(document.domain, "", "document.domain is not empty");
+ }, "basics");
+
+ test(function() {
+ assert_equals(document.domain, window.location.hostname, "equals location.hostname");
+ }, "current document");
+
+ test(function() {
+ var doc = new Document();
+ assert_equals(doc.domain, window.location.hostname, "equals location.hostname");
+ }, "new Document()");
+
+ async_test(t => {
+ const client = new XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.responseType = "document"
+ client.send();
+ client.onload = t.step_func_done(() => {
+ assert_equals(client.response.domain, window.location.hostname);
+ });
+ }, "XMLHttpRequest's response document");
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html
new file mode 100644
index 0000000000..eb02c96f1d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html
@@ -0,0 +1,305 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/document_domain_frame.sub.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "control1-1", "{{domains[www1]}}");
+ let frame2 = await createFrame(t, "control1-2", "{{domains[www1]}}");
+ let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "control1-2" });
+ assert_equals(result.data, "omg!");
+}, "Access allowed if same-origin with no 'document.domain' modification. (Sanity check)");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "control2-1", "{{domains[www1]}}");
+ let frame2 = await createFrame(t, "control2-2", "{{domains[www2]}}");
+ let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "control2-2" });
+ assert_equals(result.data, "SecurityError");
+}, "Access not allowed if different-origin with no 'document.domain' modification. (Sanity check)");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "one-set-one-not-1", "{{domains[www1]}}");
+ let frame2 = await createFrame(t, "one-set-one-not-2", "{{domains[www1]}}");
+ await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+
+ let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "one-set-one-not-2" });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "one-set-one-not-1" });
+ assert_equals(result.data, "SecurityError");
+}, "Access disallowed if same-origin but only one sets document.domain.");
+
+promise_test(async (t) => {
+ var frame1 = await createFrame(t, "both-set-to-existing-1", "{{domains[www1]}}");
+ var frame2 = await createFrame(t, "both-set-to-existing-2", "{{domains[www1]}}");
+ let result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, { 'poke-at-sibling': "both-set-to-existing-2" });
+ assert_equals(result.data, "omg!");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "both-set-to-existing-1" });
+ assert_equals(result.data, "omg!");
+}, "Access allowed if same-origin and both set document.domain to existing value.");
+
+promise_test(async (t) => {
+ var frame1 = await createFrame(t, "both-set-to-parent-1", "{{domains[www1]}}");
+ var frame2 = await createFrame(t, "both-set-to-parent-2", "{{domains[www2]}}");
+ let result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame2, { domain: "{{domains[]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, { 'poke-at-sibling': "both-set-to-parent-2" });
+ assert_equals(result.data, "omg!");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "both-set-to-parent-1" });
+ assert_equals(result.data, "omg!");
+}, "Access allowed if different-origin but both set document.domain to parent domain.");
+
+promise_test(async (t) => {
+ var frame1 = await createFrame(t, "allow-then-revoke-1", "{{domains[www1]}}");
+ var frame2 = await createFrame(t, "allow-then-revoke-2", "{{domains[www1]}}");
+ let result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, { 'poke-at-sibling': "allow-then-revoke-2" });
+ assert_equals(result.data, "omg!");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "allow-then-revoke-1" });
+ assert_equals(result.data, "omg!");
+
+ result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, { 'poke-at-sibling': "allow-then-revoke-2" });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "allow-then-revoke-1" });
+ assert_equals(result.data, "SecurityError");
+}, "Access disallowed again if same-origin, both set document-domain to existing value, then one sets to parent.");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "revoke-Window-1", "{{domains[www1]}}");
+ let frame2 = await createFrame(t, "revoke-Window-2", "{{domains[www1]}}");
+
+ let result = await postMessageToFrame(frame1, { cache: ["parent", "revoke-Window-2"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 1");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "revoke-Window-1"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 1");
+
+ result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "SecurityError");
+}, "Access is revoked to Window object when we stop being same effective script origin due to document.domain.");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "revoke-Location-1", "{{domains[www1]}}");
+ let frame2 = await createFrame(t, "revoke-Location-2", "{{domains[www1]}}");
+
+ let result = await postMessageToFrame(frame1, { cache: ["parent", "revoke-Location-2", "location"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 3");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "revoke-Location-1", "location"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 3");
+
+ result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "SecurityError");
+}, "Access is revoked to Location object when we stop being same effective script origin due to document.domain.");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "no-revoke-Document-1", "{{domains[www1]}}");
+ let frame2 = await createFrame(t, "no-revoke-Document-2", "{{domains[www1]}}");
+
+ let result = await postMessageToFrame(frame1, { cache: ["parent", "no-revoke-Document-2", "document"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 4");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "no-revoke-Document-1", "document"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "Reachable 4");
+
+ result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 4");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "Reachable 4");
+}, "Access is not revoked to Document object when we stop being same effective script origin due to document.domain.");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "no-revoke-object-1", "{{domains[www1]}}");
+ let frame2 = await createFrame(t, "no-revoke-object-2", "{{domains[www1]}}");
+
+ let result = await postMessageToFrame(frame1, { cache: ["parent", "no-revoke-object-2", "bar"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 2");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "no-revoke-object-1", "bar"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 2");
+
+ result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 2");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "Reachable 2");
+}, "Access is not revoked to random object when we stop being same effective script origin due to document.domain.");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "join-and-diverge-1", "{{domains[www2.www1]}}");
+ let frame2 = await createFrame(t, "join-and-diverge-2", "{{domains[www1.www1]}}");
+
+ // Make sure we can't touch each other.
+ let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-2" });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-1" });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-2", "bar"] });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-1", "document"] });
+ assert_equals(result.data, "SecurityError");
+
+ // Let's join up now.
+ result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ // Now we should be able to touch each other.
+ result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-2" });
+ assert_equals(result.data, "omg!");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-1" });
+ assert_equals(result.data, "omg!");
+
+ // Cache a random object and a document.
+ result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-2", "bar"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 2");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-1", "document"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "Reachable 4");
+
+ // OK, now let's diverge
+ result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" });
+ assert_equals(result.data, "Done");
+
+ // We should still be able to touch our cached things.
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 2");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "Reachable 4");
+}, "Access evolves correctly for non-cross-origin objects when we join up via document.domain and then diverge again.");
+
+promise_test(async (t) => {
+ let frame1 = await createFrame(t, "join-and-diverge-cross-origin-1", "{{domains[www2.www1]}}");
+ let frame2 = await createFrame(t, "join-and-diverge-cross-origin-2", "{{domains[www1.www1]}}");
+
+ // Make sure we can't touch each other.
+ let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-cross-origin-2" });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-cross-origin-1" });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-2", "bar"] });
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-1", "document"] });
+ assert_equals(result.data, "SecurityError");
+
+ // Let's join up now.
+ result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" });
+ assert_equals(result.data, "Done");
+
+ // Now we should be able to touch each other.
+ result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-cross-origin-2" });
+ assert_equals(result.data, "omg!");
+
+ result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-cross-origin-1" });
+ assert_equals(result.data, "omg!");
+
+ // Cache a window and a location
+ result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-cross-origin-2"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "Reachable 1");
+
+ result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-cross-origin-1", "location"] });
+ assert_equals(result.data, "cached");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "Reachable 3");
+
+ // OK, now let's diverge
+ result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" });
+ assert_equals(result.data, "Done");
+
+ // Now our cross-origin objects should start denying access.
+ result = await postMessageToFrame(frame1, 'touch-cached');
+ assert_equals(result.data, "SecurityError");
+
+ result = await postMessageToFrame(frame2, 'touch-cached');
+ assert_equals(result.data, "SecurityError");
+}, "Access evolves correctly for cross-origin objects when we join up via document.domain and then diverge again.");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html
new file mode 100644
index 0000000000..2539528341
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<html>
+ <head>
+ <title>document.domain's setter</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ </head>
+ <body>
+ <iframe id="iframe"></iframe>
+ <script>
+ var host_info = get_host_info();
+ var HTTP_PORT = host_info.HTTP_PORT;
+ var ORIGINAL_HOST = host_info.ORIGINAL_HOST;
+ var SUFFIX_HOST = ORIGINAL_HOST.substring(ORIGINAL_HOST.lastIndexOf('.') + 1); // e.g. "test"
+ var REMOTE_HOST = host_info.REMOTE_HOST;
+ var iframe = document.getElementById("iframe");
+ var iframe_url = new URL("support/document_domain_setter_iframe.html", document.location);
+ iframe_url.hostname = REMOTE_HOST;
+ iframe.src = iframe_url;
+
+ test(function() {
+ assert_throws_dom("SecurityError", function() { document.domain = SUFFIX_HOST; });
+ assert_throws_dom("SecurityError", function() { document.domain = "." + SUFFIX_HOST; });
+ assert_throws_dom("SecurityError", function() { document.domain = REMOTE_HOST; });
+ assert_throws_dom("SecurityError", function() { document.domain = "example.com"; });
+ }, "failed setting of document.domain");
+
+ async_test(function(t) {
+ iframe.addEventListener("load", t.step_func_done(function() {
+ // Before setting document.domain, the iframe is not
+ // same-origin-domain, so security checks fail.
+ assert_equals(iframe.contentDocument, null);
+ assert_throws_dom("SecurityError", () => iframe.contentWindow.frameElement);
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.origin; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.href; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.protocol; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.host; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.port; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.hostname; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.pathname; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.hash; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.search; });
+ assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.toString(); });
+ // Set document.domain
+ document.domain = ORIGINAL_HOST;
+ // After setting document.domain, the iframe is
+ // same-origin-domain, so security checks pass.
+ assert_equals(iframe.contentDocument.domain, document.domain);
+ assert_equals(iframe.contentWindow.frameElement, iframe);
+ assert_equals(iframe.contentWindow.origin, iframe_url.origin);
+ assert_equals(iframe.contentWindow.location.href, iframe_url.href);
+ assert_equals(iframe.contentWindow.location.protocol, iframe_url.protocol);
+ assert_equals(iframe.contentWindow.location.host, iframe_url.host);
+ assert_equals(iframe.contentWindow.location.port, iframe_url.port);
+ assert_equals(iframe.contentWindow.location.hostname, iframe_url.hostname);
+ assert_equals(iframe.contentWindow.location.pathname, iframe_url.pathname);
+ assert_equals(iframe.contentWindow.location.hash, iframe_url.hash);
+ assert_equals(iframe.contentWindow.location.search, iframe_url.search);
+ assert_equals(iframe.contentWindow.location.search, iframe_url.search);
+ assert_equals(iframe.contentWindow.location.toString(), iframe_url.toString());
+ // document.open checks for same-origin, not same-origin-domain,
+ // https://github.com/whatwg/html/issues/2282
+ assert_throws_dom("SecurityError", iframe.contentWindow.DOMException,
+ function() { iframe.contentDocument.open(); });
+ }));
+ }, "same-origin-domain iframe");
+
+ test(() => {
+ assert_throws_dom("SecurityError", () => { (new Document).domain = document.domain });
+ assert_throws_dom("SecurityError", () => { document.implementation.createHTMLDocument().domain = document.domain });
+ assert_throws_dom("SecurityError", () => { document.implementation.createDocument(null, "").domain = document.domain });
+ }, "failed setting of document.domain for documents without browsing context");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html
new file mode 100644
index 0000000000..65a7f5c898
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<!-- SEKRITS! -->
+<input id="sekrit" value="omg!">
+
+<script>
+ function postMessageToFrame(frame, message) {
+ return new Promise(resolve => {
+ var c = new MessageChannel();
+ c.port1.onmessage = e => {
+ resolve({ data: e.data, frame: frame })
+ };
+ frame.contentWindow.postMessage(message, '*', [c.port2]);
+ });
+ }
+
+ function createFrame() {
+ return new Promise(resolve => {
+ var i = document.createElement('iframe');
+ i.srcdoc = `
+ <script>
+ window.addEventListener('message', e => {
+ if (e.data.domain !== undefined) {
+ try {
+ document.domain = e.data.domain;
+ e.ports[0].postMessage('Done');
+ } catch(error) {
+ e.ports[0].postMessage(error.name);
+ }
+ } else if (e.data == 'poke-at-parent') {
+ try {
+ var sekrit = window.parent.document.body.querySelector('#sekrit').value;
+ e.ports[0].postMessage(sekrit);
+ } catch(error) {
+ e.ports[0].postMessage(error.name);
+ }
+ }
+ });
+ window.parent.postMessage('Hi!', '*');
+ </scr` + `ipt>`;
+ window.addEventListener('message', m => {
+ if (m.source == i.contentWindow)
+ resolve(i);
+ });
+ document.body.appendChild(i);
+ });
+ }
+
+ promise_test(t => {
+ return createFrame()
+ .then(f => postMessageToFrame(f, 'poke-at-parent'))
+ .then(result => {
+ assert_equals(result.data, document.querySelector('#sekrit').value);
+ result.frame.remove();
+ });
+ }, "srcdoc can access with no 'document.domain' modification.");
+
+ promise_test(t => {
+ return createFrame()
+ .then(f => postMessageToFrame(f, { domain: window.location.hostname }))
+ .then(result => {
+ assert_equals(result.data, 'Done');
+ return postMessageToFrame(result.frame, 'poke-at-parent')
+ .then(result => {
+ assert_equals(result.data, document.querySelector('#sekrit').value);
+ result.frame.remove();
+ });
+ });
+ }, "srcdoc can access with valid 'document.domain'.");
+
+ promise_test(t => {
+ return createFrame()
+ .then(f => {
+ document.domain = window.location.hostname;
+ return postMessageToFrame(f, 'poke-at-parent');
+ })
+ .then(result => {
+ assert_equals(result.data, document.querySelector('#sekrit').value);
+ result.frame.remove();
+ });
+ }, "srcdoc can access when parent modifies 'document.domain'.");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html
new file mode 100644
index 0000000000..ae1a0ccd56
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Sandboxed document.domain</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(() => {
+ assert_throws_dom("SecurityError", () => { document.domain = document.domain });
+ });
+ test(() => {
+ assert_throws_dom("SecurityError", () => { (new Document).domain = document.domain });
+ });
+ test(() => {
+ assert_throws_dom("SecurityError", () => { document.implementation.createHTMLDocument().domain = document.domain });
+ });
+ test(() => {
+ assert_throws_dom("SecurityError", () => { document.implementation.createDocument(null, "").domain = document.domain });
+ });
+ test(() => {
+ assert_throws_dom("SecurityError", () => { document.createElement("template").content.ownerDocument.domain = document.domain });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html.headers b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html.headers
new file mode 100644
index 0000000000..82e8023d0b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts allow-same-origin
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html
new file mode 100644
index 0000000000..61f54af359
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<script>
+ let cache = window;
+ // "foo" needs to be a var so it's a property on the global.
+ var foo = 'Reachable 1';
+ // "bar" needs to be a var so it's a property on the global.
+ var bar = { foo: 'Reachable 2' };
+ location.foo = 'Reachable 3';
+ document.foo = 'Reachable 4';
+ window.addEventListener('message', e => {
+ if (e.data.domain !== undefined) {
+ try {
+ document.domain = e.data.domain;
+ e.ports[0].postMessage('Done');
+ } catch(error) {
+ e.ports[0].postMessage(error.name);
+ }
+ } else if (e.data['poke-at-sibling'] !== undefined) {
+ try {
+ var sekrit = parent[e.data['poke-at-sibling']].document.body.querySelector('#sekrit').value;
+ e.ports[0].postMessage(sekrit);
+ } catch(error) {
+ e.ports[0].postMessage(error.name);
+ }
+ } else if (e.data.cache != undefined) {
+ let path = e.data.cache;
+ try {
+ while (path.length != 0) {
+ cache = cache[path.shift()];
+ }
+ e.ports[0].postMessage('cached');
+ } catch (error) {
+ e.ports[0].postMessage(error.name);
+ }
+ } else if (e.data == 'touch-cached') {
+ try {
+ e.ports[0].postMessage(cache.foo);
+ } catch (error) {
+ e.ports[0].postMessage(error.name);
+ }
+ } else if (e.data == 'poke-at-parent') {
+ try {
+ var sekrit = window.parent.document.body.querySelector('#sekrit').value;
+ e.ports[0].postMessage(sekrit);
+ } catch(error) {
+ e.ports[0].postMessage(error.name);
+ }
+ }
+ });
+ window.parent.postMessage('Hi!', '*');
+</script>
+<input id="sekrit" value="omg!">
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.sub.js b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.sub.js
new file mode 100644
index 0000000000..b6631ea4f1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.sub.js
@@ -0,0 +1,65 @@
+/**
+ * Utilities to be used with document_domain_frame.html.
+ */
+
+/**
+ * Send a message to the frame and resolve a promise when a response is received.
+ *
+ * Supported messages:
+ *
+ * 1) { domain: something }. Has the subframe try to set document.domain to the
+ * given value, and message back 'Done' if that succeeds or an error name if it
+ * fails.
+ *
+ * 2) 'poke-at-parent'. Has the subframe try to synchronously attempt to access
+ * the parent's DOM, read out a string value, and message it back to the parent.
+ * Again, sends back the error name if that fails.
+ *
+ * 3) { 'poke-at-sibling': name }. Has the subframe try to synchronously
+ * attempt to access the DOM of the sibling with the given name, read out a
+ * string value, and message it back to the parent.
+ */
+function postMessageToFrame(frame, message) {
+ return new Promise(resolve => {
+ var c = new MessageChannel();
+ c.port1.onmessage = e => {
+ resolve({ data: e.data, frame: frame })
+ };
+ frame.contentWindow.postMessage(message, '*', [c.port2]);
+ });
+}
+
+/**
+ * Create a frame that loads document_domain_frame.html and resolves a promise
+ * when the frame is loaded enough to be sending and receiving messages.
+ *
+ * If a "name" argument is provided, that name is used for the iframe, so
+ *
+ * If a "hostname" argument is provided, that hostname is used for the load, to
+ * allow testing details of the behavior when different sorts of hostnames are
+ * used.
+ */
+function createFrame(t, name, hostname) {
+ return new Promise(resolve => {
+ var i = document.createElement('iframe');
+ if (hostname) {
+ i.src = `//${hostname}:{{location[port]}}/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html`;
+ } else {
+ i.src = "support/document_domain_frame.html";
+ }
+ if (name) {
+ i.name = name;
+ }
+ var listener = m => {
+ if (m.source == i.contentWindow)
+ resolve(i);
+ }
+ window.addEventListener('message', listener);
+ t.add_cleanup(() => {
+ i.remove();
+ window.removeEventListener('message', listener);
+ });
+ document.body.appendChild(i);
+ });
+}
+
diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_setter_iframe.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_setter_iframe.html
new file mode 100644
index 0000000000..d3d5260af3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_setter_iframe.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+ <head>
+ <title></title>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script>
+ document.domain = get_host_info().ORIGINAL_HOST;
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/inner-iframe.html b/testing/web-platform/tests/html/browsers/sandboxing/inner-iframe.html
new file mode 100644
index 0000000000..229f6b3d85
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/inner-iframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+ window.onload = function() {
+ top.calledFromIframe();
+ }
+ </script>
+ </head>
+ <body>
+ <div id="inner">foo</div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/noscript-iframe.html b/testing/web-platform/tests/html/browsers/sandboxing/noscript-iframe.html
new file mode 100644
index 0000000000..677b5fc83a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/noscript-iframe.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<noscript>PASS</noscript>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/popup-from-initial-empty-sandboxed-document.window.js b/testing/web-platform/tests/html/browsers/sandboxing/popup-from-initial-empty-sandboxed-document.window.js
new file mode 100644
index 0000000000..1ae4fad0cb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/popup-from-initial-empty-sandboxed-document.window.js
@@ -0,0 +1,46 @@
+// META: timeout=long
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+
+// Regression test for: https://crbug.com/1256822.
+//
+// From a sandboxed iframe allowing popups, scripts, and same-origin. Open a
+// popup using the WindowProxy of a new iframe that is still on the initial
+// empty document. Check that the sandbox flags are properly inherited.
+
+// Return true if the execution context is sandboxed.
+const isSandboxed = () => {
+ try {
+ // Setting document.domain in sandboxed document throw errors.
+ document.domain = document.domain;
+ return false;
+ } catch (error) {
+ return true;
+ }
+}
+
+promise_test(async test => {
+ // 1. Create a sandboxed iframe, allowing popups, same-origin and scripts.
+ const iframe_token = token();
+ const iframe_document = new RemoteContext(iframe_token);
+ const iframe_url = remoteExecutorUrl(iframe_token);
+ const iframe = document.createElement("iframe");
+ iframe.sandbox = "allow-same-origin allow-scripts allow-popups";
+ iframe.src = iframe_url;
+ document.body.appendChild(iframe);
+ assert_true(await iframe_document.execute_script(isSandboxed),
+ "iframe is sandboxed");
+
+ // 2. From the sandboxed iframe, create an empty iframe, and open a popup
+ // using it's WindowProxy. The popup must inherit sandbox flags.
+ const popup_token = token();
+ const popup_document = new RemoteContext(popup_token);
+ const popup_url = remoteExecutorUrl(popup_token);
+ iframe_document.execute_script((popup_url) => {
+ let iframe = document.createElement("iframe");
+ iframe.name = "iframe_name";
+ document.body.appendChild(iframe);
+ iframe_name.open(popup_url);
+ }, [popup_url.href]);
+ assert_true(await popup_document.execute_script(isSandboxed), "popup is sandboxed");
+});
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/check-sandbox-flags.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/check-sandbox-flags.html
new file mode 100644
index 0000000000..0dc95315f1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/check-sandbox-flags.html
@@ -0,0 +1,8 @@
+<script>
+ try {
+ document.domain = document.domain;
+ parent.postMessage('document-domain-is-allowed', '*');
+ } catch (error) {
+ parent.postMessage('document-domain-is-disallowed', '*');
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/document-open.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/document-open.html
new file mode 100644
index 0000000000..136c494d5a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/document-open.html
@@ -0,0 +1,14 @@
+<script>
+ onload = () => {
+ document.write(`
+ <script>
+ try {
+ document.domain = document.domain;
+ parent.postMessage('document-domain-is-allowed', '*');
+ } catch (error) {
+ parent.postMessage('document-domain-is-disallowed', '*');
+ }
+ </sc`+`ript>
+ `);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/execute-postmessage.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/execute-postmessage.html
new file mode 100644
index 0000000000..89bd268f9c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/execute-postmessage.html
@@ -0,0 +1,5 @@
+<script>
+ // Execute arbitrary code from somewhere else, via postMessage.
+ window.addEventListener("message", event => eval(event.data));
+ window.opener.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/post-done-to-opener.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/post-done-to-opener.html
new file mode 100644
index 0000000000..b47f0f274e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/post-done-to-opener.html
@@ -0,0 +1,3 @@
+<script>
+ window.opener.top.postMessage("DONE", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html
new file mode 100644
index 0000000000..29c7f12441
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html
@@ -0,0 +1,15 @@
+<script>
+ const iframe_1_script = encodeURI(`
+ <script>
+ try {
+ document.domain = document.domain;
+ parent.postMessage("not sandboxed", "*");
+ } catch (exception) {
+ parent.postMessage("sandboxed", "*");
+ }
+ </scr`+`ipt>
+ `);
+
+ const iframe_1 = parent.document.querySelector("#iframe_1");
+ iframe_1.src = `data:text/html,${iframe_1_script}`;
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html.headers b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html.headers
new file mode 100644
index 0000000000..82e8023d0b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts allow-same-origin
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-javascript-window-open.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-javascript-window-open.html
new file mode 100644
index 0000000000..909956a54f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-javascript-window-open.html
@@ -0,0 +1,18 @@
+<script>
+ // Forward message from the openee toward the parent.
+ window.addEventListener("message", event => top.postMessage(event.data, "*"));
+
+ let check_sandboxed = `"
+ <script>
+ try {
+ document.domain = document.domain;
+ opener.postMessage('allow-document-domain', '*');
+ } catch (error) {
+ opener.postMessage('disallow-document-domain', '*');
+ }
+ </scr`+`ipt>
+ "`;
+
+ window.open('about:blank', "window_name");
+ window.open("javascript:" + check_sandboxed, "window_name");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-same-origin.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-same-origin.html
new file mode 100644
index 0000000000..d6b3b099f2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-same-origin.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>DOM access in sandbox="allow-same-origin" iframe</title>
+ <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
+ <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>DOM access in sandbox="allow-same-origin" iframe</h1>
+ <script type="text/javascript">
+ var t = async_test("DOM access in sandbox='allow-same-origin' iframe is allowed")
+ var called = 0;
+ function calledFromIframe() {
+ called++;
+ }
+ function loaded() {
+ assert_equals(document.getElementById('sandboxedframe').contentWindow.document.getElementById('inner').innerHTML, 'foo');
+ assert_equals(called, 0);
+ t.done();
+ }
+ </script>
+
+ <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox="allow-same-origin" id="sandboxedframe" onload="loaded();"></iframe>
+
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-scripts.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-scripts.html
new file mode 100644
index 0000000000..6cf3f5a4a8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-scripts.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Script execution in sandbox="allow-scripts" iframe</title>
+ <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
+ <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Script execution in sandbox="allow-scripts" iframe</h1>
+ <script type="text/javascript">
+ var t = async_test("Running script from sandbox='allow-scripts' iframe is allowed")
+ var called = 0;
+ function calledFromIframe() {
+ called++;
+ }
+ function loaded() {
+ assert_equals(called, 1);
+ t.done();
+ }
+ </script>
+
+ <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox="allow-scripts allow-same-origin" id="sandboxedframe" onload="loaded();"></iframe>
+
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-popups.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-popups.html
new file mode 100644
index 0000000000..8e4b34eb8b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-popups.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>window.open in sandbox iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<body>
+<script>
+setup({single_test: true});
+// check that the popup's URL is not loaded
+const uuid = token();
+async function assert_popup_not_loaded() {
+ const response = await fetch(`/fetch/api/resources/stash-take.py?key=${uuid}`);
+ assert_equals(await response.json(), null); // is "loaded" if it loads
+}
+
+// check for message from the iframe
+window.onmessage = e => {
+ assert_equals(e.data, 'null', 'return value of window.open (stringified)');
+ step_timeout(async () => {
+ await assert_popup_not_loaded();
+ done();
+ }, 1000);
+};
+const iframe = document.createElement('iframe');
+iframe.sandbox = 'allow-scripts';
+iframe.srcdoc = `
+ <script>
+ let result;
+ try {
+ result = window.open('/fetch/api/resources/stash-put.py?key=${uuid}&value=loaded', '_blank');
+ } catch(ex) {
+ result = ex;
+ }
+ parent.postMessage(String(result), '*');
+ <\/script>
+`;
+document.body.appendChild(iframe);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-same-origin.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-same-origin.html
new file mode 100644
index 0000000000..0dae0137ac
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-same-origin.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Access to sandbox iframe</title>
+ <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#sandboxed-origin-browsing-context-flag">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#integration-with-idl">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Access to sandbox iframe</h1>
+ <script type="text/javascript">
+ var t = async_test("Access to sandbox iframe is disallowed")
+ var called = 0;
+ function calledFromIframe() {
+ called++;
+ }
+ function loaded() {
+ t.step(() => {
+ assert_throws_dom("SecurityError", () => {
+ document.getElementById('sandboxedframe').contentWindow.document;
+ });
+ assert_equals(called, 0);
+ t.done();
+ });
+ }
+ </script>
+
+ <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox id="sandboxedframe" onload="loaded();"></iframe>
+ </body>
+
+ <div id="log"></div>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html
new file mode 100644
index 0000000000..3c8c0b346a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <script>
+ async_test(t => {
+ let i = document.createElement('iframe');
+ i.sandbox = "allow-same-origin allow-popups allow-popups-to-escape-sandbox";
+ i.srcdoc = `<a target='_blank' rel='opener'
+ href="javascript:window.opener.top.postMessage('FAIL', '*');">Click me!</a>
+ <a target='_blank' rel='opener'
+ href="./resources/post-done-to-opener.html">Click me next!</a>`;
+
+ i.onload = _ => {
+ // Since the frame is sandboxed, but allow-same-origin, we can reach into it to grab the
+ // anchor element to click. We'll click the `javascript:` URL first, then pop up a new
+ // window that posts `DONE`.
+ //
+ // TODO(mkwst): This feels like a race, but it's one that we consistently win when I'm
+ // running the test locally 10,000 times. Good enough!â„¢
+ i.contentDocument.body.querySelectorAll('a')[0].click();
+ i.contentDocument.body.querySelectorAll('a')[1].click();
+ };
+ document.body.appendChild(i);
+
+ window.addEventListener('message', t.step_func(e => {
+ assert_not_equals(e.data, "FAIL");
+ if (e.data == "DONE")
+ t.done();
+ }));
+ }, "Sandboxed => unsandboxed popup");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts.html
new file mode 100644
index 0000000000..1bc116ada4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Script execution in sandbox iframe</title>
+ <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
+ <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Script execution in sandbox iframe</h1>
+ <script type="text/javascript">
+ var t = async_test("Running script from sandbox iframe is disallowed")
+ var called = 0;
+ function calledFromIframe() {
+ called++;
+ }
+ function loaded() {
+ assert_equals(called, 0);
+ t.done();
+ }
+ </script>
+
+ <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox id="sandboxedframe" onload="loaded();"></iframe>
+
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open-mutation.window.js b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open-mutation.window.js
new file mode 100644
index 0000000000..713ca612c5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open-mutation.window.js
@@ -0,0 +1,37 @@
+// Return whether the current context is sandboxed or not. The implementation do
+// not matter much, but might have to change over time depending on what side
+// effect sandbox flag have. Feel free to update as needed.
+const is_sandboxed = () => {
+ try {
+ document.domain = document.domain;
+ return "not sandboxed";
+ } catch (error) {
+ return "sandboxed";
+ }
+};
+
+promise_test(async test => {
+ const message = new Promise(r => window.addEventListener("message", r));
+
+ const iframe_unsandboxed = document.createElement("iframe");
+ document.body.appendChild(iframe_unsandboxed);
+
+ const iframe_sandboxed = document.createElement("iframe");
+ iframe_sandboxed.sandbox = "allow-same-origin allow-scripts";
+ document.body.appendChild(iframe_sandboxed);
+
+ iframe_sandboxed.srcdoc = `
+ <script>
+ parent.frames[0].document.write(\`
+ <script>
+ const is_sandboxed = ${is_sandboxed};
+ window.parent.postMessage(is_sandboxed(), '*');
+ </scr\`+\`ipt>
+ \`);
+ parent.frames[0].document.close();
+ </scr`+`ipt>
+ `;
+ assert_equals((await message).data, "not sandboxed");
+
+}, "Using document.open() against a document from a different window must not" +
+ " mutate the other window's sandbox flags");
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open.html
new file mode 100644
index 0000000000..3f754374ba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>
+ Check sandbox-flags aren't lost after using document.open().
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async test => {
+ let message = new Promise(resolve =>
+ window.addEventListener("message", event => resolve(event.data))
+ );
+
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
+ iframe.setAttribute("src", "./resources/document-open.html")
+ document.body.appendChild(iframe);
+
+ assert_equals(await message, "document-domain-is-disallowed");
+}, "document.open()");
+
+promise_test(async test => {
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
+ iframe.setAttribute("src", "/common/blank.html");
+ let loaded = new Promise(resolve => iframe.onload = resolve);
+ document.body.appendChild(iframe);
+ await loaded;
+
+ let message = new Promise(resolve =>
+ window.addEventListener("message", event => resolve(event.data))
+ );
+
+ iframe.contentDocument.write(`
+ <script>
+ try {
+ document.domain = document.domain;
+ parent.postMessage('document-domain-is-allowed', '*');
+ } catch (error) {
+ parent.postMessage('document-domain-is-disallowed', '*');
+ }
+ </sc`+`ript>
+ `);
+
+ assert_equals(await message, "document-domain-is-disallowed");
+}, "other_document.open()");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-frame.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-frame.html
new file mode 100644
index 0000000000..ab87fce5e0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-frame.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Inherit sandbox flags from the initiator's frame</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Check sandbox flags are properly inherited when a document initiate a
+// navigation inside another frame that it doesn't own directly.
+
+// This check the sandbox flags defined by the frame. See also the other test
+// about sandbox flags defined by the response (e.g. CSP sandbox):
+// => sandbox-inherited-from-initiators-response.html
+
+// Return a promise, resolving when |element| triggers |event_name| event.
+let future = (element, event_name) => {
+ return new Promise(resolve => {
+ element.addEventListener(event_name, event => resolve(event))
+ });
+};
+
+promise_test(async test => {
+ const iframe_1 = document.createElement("iframe");
+ const iframe_2 = document.createElement("iframe");
+
+ iframe_1.id = "iframe_1";
+ iframe_2.id = "iframe_2";
+
+ const iframe_1_script = encodeURI(`
+ <script>
+ try {
+ document.domain = document.domain;
+ parent.postMessage("not sandboxed", "*");
+ } catch (exception) {
+ parent.postMessage("sandboxed", "*");
+ }
+ </scr`+`ipt>
+ `);
+
+ const iframe_2_script = `
+ <script>
+ const iframe_1 = parent.document.querySelector("#iframe_1");
+ iframe_1.src = "data:text/html,${iframe_1_script}";
+ </scr`+`ipt>
+ `;
+
+ iframe_2.sandbox = "allow-scripts allow-same-origin";
+ iframe_2.srcdoc = iframe_2_script;
+
+ // Insert |iframe_1|. It will load the initial empty document, with no sandbox
+ // flags.
+ const iframe_1_load_1 = future(iframe_1, "load");
+ document.body.appendChild(iframe_1);
+ await iframe_1_load_1;
+
+ // Insert |iframe_2|. It will load with sandbox flags. It will make |iframe_1|
+ // to navigate toward a data-url, which should inherit the sandbox flags.
+ const iframe_1_reply = future(window, "message");
+ document.body.appendChild(iframe_2);
+ const result = await iframe_1_reply;
+
+ assert_equals("sandboxed", result.data);
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html
new file mode 100644
index 0000000000..638f1ba783
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Inherit sandbox flags from the initiator's response</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Check sandbox flags are properly inherited when a document initiate a
+// navigation inside another frame that it doesn't own directly.
+
+// This check the sandbox flags defined by the response (e.g. CSP sandbox). See
+// also the other test about sandbox flags inherited from the frame.
+// => sandbox-inherited-from-initiators-frame.html
+
+// Return a promise, resolving when |element| triggers |event_name| event.
+let future = (element, event_name) => {
+ return new Promise(resolve => {
+ element.addEventListener(event_name, event => resolve(event))
+ });
+};
+
+promise_test(async test => {
+ const iframe_1 = document.createElement("iframe");
+ const iframe_2 = document.createElement("iframe");
+
+ iframe_1.id = "iframe_1";
+ iframe_2.id = "iframe_2";
+
+ iframe_2.src =
+ "./resources/sandbox-inherited-from-initiator-response-helper.html";
+
+ // Insert |iframe_1|. It will load the initial empty document, with no sandbox
+ // flags.
+ const iframe_1_load_1 = future(iframe_1, "load");
+ document.body.appendChild(iframe_1);
+ await iframe_1_load_1;
+
+ // Insert |iframe_2|. It will load with sandbox flags. It will make |iframe_1|
+ // to navigate toward a data-url, which should inherit the sandbox flags.
+ const iframe_1_reply = future(window, "message");
+ document.body.appendChild(iframe_2);
+ const result = await iframe_1_reply;
+
+ assert_equals("sandboxed", result.data);
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html
new file mode 100644
index 0000000000..04f485cc66
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html
@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Inherit sandbox from CSP embedded enforcement</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+// Check sandbox flags are properly defined when its parent requires them and
+// the child allows it.
+
+const same_origin = get_host_info().HTTP_ORIGIN;
+const cross_origin = get_host_info().HTTP_REMOTE_ORIGIN;
+const check_sandbox_url =
+ "/html/browsers/sandboxing/resources/check-sandbox-flags.html?pipe=";
+const allow_csp_from_star = "|header(Allow-CSP-From,*)";
+
+// Return a promise, resolving when |element| triggers |event_name| event.
+const future = (element, event_name, source) => {
+ return new Promise(resolve => {
+ element.addEventListener(event_name, event => {
+ if (!source || source.contentWindow == event.source)
+ resolve(event)
+ })
+ });
+};
+
+const check_sandbox_script = `
+<script>
+ try {
+ document.domain = document.domain;
+ parent.postMessage("document-domain-is-allowed", "*");
+ } catch (exception) {
+ parent.postMessage("document-domain-is-disallowed", "*");
+ }
+</scr`+`ipt>
+`;
+
+const sandbox_policy = "sandbox allow-scripts allow-same-origin";
+
+// Test using the modern async/await primitives are easier to read/write.
+// However they run sequentially, contrary to async_test. This is the parallel
+// version, to avoid timing out.
+let promise_test_parallel = (promise, description) => {
+ async_test(test => {
+ promise(test)
+ .then(() => {test.done();})
+ .catch(test.step_func(error => { throw error; }));
+ }, description);
+};
+
+promise_test_parallel(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.csp = sandbox_policy;
+
+ // The <iframe> immediately hosts the initial empty document after being
+ // appended into the DOM. It will, as long as its 'src' isn't loaded. That's
+ // why a page do not load is being used.
+ iframe.src = "/fetch/api/resources/infinite-slow-response.py";
+ document.body.appendChild(iframe);
+
+ const iframe_reply = future(window, "message", iframe);
+ iframe.contentDocument.write(check_sandbox_script);
+ const result = await iframe_reply;
+ iframe.remove();
+
+ assert_equals(result.data, "document-domain-is-disallowed");
+}, "initial empty document");
+
+promise_test_parallel(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "data:text/html,dummy";
+
+ const iframe_load_1 = future(iframe, "load");
+ document.body.appendChild(iframe);
+ await iframe_load_1;
+
+ const iframe_load_2 = future(iframe, "load");
+ iframe.csp = sandbox_policy;
+ iframe.src = "about:blank";
+ await iframe_load_2;
+
+ const iframe_reply = future(window, "message", iframe);
+ iframe.contentDocument.write(check_sandbox_script);
+ const result = await iframe_reply;
+
+ assert_equals(result.data, "document-domain-is-disallowed");
+}, "about:blank");
+
+promise_test_parallel(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.csp = sandbox_policy;
+ iframe.src =
+ `data:text/html,${encodeURI(check_sandbox_script)}`;
+
+ const iframe_reply = future(window, "message", iframe);
+ document.body.appendChild(iframe);
+ const result = await iframe_reply;
+
+ assert_equals(result.data, "document-domain-is-disallowed");
+}, "data-url");
+
+promise_test_parallel(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.csp = sandbox_policy;
+ iframe.srcdoc = check_sandbox_script;
+
+ const iframe_reply = future(window, "message", iframe);
+ document.body.appendChild(iframe);
+ const result = await iframe_reply;
+
+ assert_equals(result.data, "document-domain-is-disallowed");
+}, "srcdoc");
+
+promise_test_parallel(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.csp = sandbox_policy;
+
+ const blob = new Blob([check_sandbox_script], { type: "text/html" });
+
+ iframe.src = URL.createObjectURL(blob);
+
+ const iframe_reply = future(window, "message", iframe);
+ document.body.appendChild(iframe);
+ const result = await iframe_reply;
+
+ assert_equals(result.data, "document-domain-is-disallowed");
+}, "blob URL");
+
+promise_test_parallel(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.csp = sandbox_policy;
+ iframe.src = same_origin + check_sandbox_url + allow_csp_from_star;
+
+ const iframe_reply = future(window, "message", iframe);
+ document.body.appendChild(iframe);
+ const result = await iframe_reply;
+
+ assert_equals(result.data, "document-domain-is-disallowed");
+}, "same-origin");
+
+promise_test_parallel(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.csp = sandbox_policy;
+ iframe.src = cross_origin + check_sandbox_url + allow_csp_from_star;
+
+ const iframe_reply = future(window, "message", iframe);
+ document.body.appendChild(iframe);
+ const result = await iframe_reply;
+
+ assert_equals(result.data, "document-domain-is-disallowed");
+}, "cross-origin");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html
new file mode 100644
index 0000000000..d1306c9703
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>
+ Check sandbox-flags inheritance in case of javascript window reuse.
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async test => {
+ let message = new Promise(resolve =>
+ window.addEventListener("message", event => resolve(event.data))
+ );
+
+ // Create an initial empty document in the iframe, sandboxed. It will attempt
+ // to load a slow page, but won't have time.
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute("sandbox", "allow-scripts allow-same-origin");
+ iframe.src = "/fetch/api/resources/infinite-slow-response.py";
+ document.body.appendChild(iframe);
+
+ // Remove sandbox flags. This should apply to documents committed from
+ // navigations started after this instruction.
+ iframe.removeAttribute("sandbox");
+ iframe.src = "./resources/check-sandbox-flags.html";
+
+ // The window is reused, but the new sandbox flags should be used.
+ assert_equals(await message, "document-domain-is-allowed");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-javascript-window-open.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-javascript-window-open.html
new file mode 100644
index 0000000000..fd21e9bb02
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-javascript-window-open.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>window.open in sandbox iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<body>
+<script>
+promise_test(async test => {
+ let message = new Promise(resolve => {
+ window.addEventListener("message", event => resolve(event.data));
+ });
+ let iframe = document.createElement("iframe");
+ iframe.sandbox = "allow-scripts allow-popups allow-same-origin";
+ iframe.src = "./resources/sandbox-javascript-window-open.html";
+ document.body.appendChild(iframe);
+ assert_equals(await message, "disallow-document-domain");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing-iframe.tentative.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing-iframe.tentative.html
new file mode 100644
index 0000000000..43726e7720
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing-iframe.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script>
+ let result;
+ try {
+ parent.document.getElementsByClassName('script');
+ result = 'iframe not sandboxed'
+ } catch (e) {
+ result = 'iframe sandboxed(' + e.message + ')';
+ }
+ window.onmessage = m => {
+ window.parent.postMessage({
+ result: result
+ }, '*');
+ };
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html
new file mode 100644
index 0000000000..686f1c0c9f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Sandbox Navigation Timing</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<html></html>
+<script>
+ const sandboxUrl = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1) + 'sandbox-navigation-timing-iframe.tentative.html';
+ async_test(t => {
+ const iframe = document.createElement('iframe');
+ iframe.src = sandboxUrl;
+ document.body.appendChild(iframe); // Navigation starts; value of sandbox flags locked on.
+ // This should not affect the sandbox value used for both about:blank document
+ // and the final document in iframe.
+ iframe.sandbox = 'allow-scripts';
+ const iframeAboutBlankDocument = iframe.contentDocument;
+
+ iframe.onload = t.step_func(() => {
+ const iframeAboutBlankContents = iframeAboutBlankDocument.querySelectorAll('body');
+ assert_equals(iframeAboutBlankContents[0].tagName, "BODY",
+ "about:blank document's contents should still be accessible");
+
+ iframe.contentWindow.postMessage("is iframe sandboxed?", "*");
+ });
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.result, 'iframe not sandboxed');
+ });
+ }, 'setting sandbox attribute should not affect current document in iframe');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context-iframe.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context-iframe.html
new file mode 100644
index 0000000000..801e78f9c0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context-iframe.html
@@ -0,0 +1,5 @@
+<body>
+ <script>
+ Object.getPrototypeOf(document).changeFromSandboxedIframe = "change from sandboxed iframe";
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context.html
new file mode 100644
index 0000000000..dc1953aee6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Reuse of iframe about:blank document execution context</title>
+ <link rel="author" title="Dan Clark" href="mailto:daniec@microsoft.com">
+ <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Reuse of iframe about:blank document execution context in sandbox="allow-scripts" iframe</h1>
+ <script type="text/javascript">
+ async_test(t => {
+ let iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+
+ let iframeAboutBlankDocument = iframe.contentDocument;
+ assert_equals(iframeAboutBlankDocument.URL, "about:blank");
+
+ iframe.sandbox = "allow-scripts";
+ iframe.src = './sandbox-new-execution-context-iframe.html';
+
+ iframe.onload = t.step_func_done(() => {
+ assert_equals(iframe.contentDocument, null,
+ "New document in sandboxed iframe should have opaque origin");
+
+ assert_equals(Object.getPrototypeOf(iframeAboutBlankDocument).changeFromSandboxedIframe, undefined,
+ "Sandboxed iframe contents should not have been able to mess with type system of about:blank document");
+
+ let iframeAboutBlankContents = iframeAboutBlankDocument.querySelectorAll('body');
+ assert_equals(iframeAboutBlankContents[0].tagName, "BODY",
+ "about:blank document's contents should still be accessible");
+ });
+ },"iframe with sandbox should load with new execution context");
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript-ref.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript-ref.html
new file mode 100644
index 0000000000..9cf92768f7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript-ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>noscript parsing when sandbox disables scripting</title>
+<iframe srcdoc="PASS" sandbox></iframe>
+<iframe srcdoc="PASS" sandbox></iframe>
+<iframe srcdoc="P<b>AS</b>S" sandbox></iframe>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript.html
new file mode 100644
index 0000000000..bb7ced0a14
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>noscript parsing when sandbox disables scripting</title>
+<link rel=match href=/html/browsers/sandboxing/sandbox-parse-noscript-ref.html>
+<iframe srcdoc="<noscript>PASS</noscript>" sandbox></iframe>
+<iframe src="noscript-iframe.html" sandbox></iframe>
+<iframe srcdoc="<noscript>P<b>AS</b>S</noscript>" sandbox></iframe>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-window-open-srcdoc.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-window-open-srcdoc.html
new file mode 100644
index 0000000000..6fbff6df82
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-window-open-srcdoc.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>window.open("about:srcdoc") from a sandboxed iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Check what happens when executing window.open("about:srcdoc") from a
+// sandboxed iframe. Srcdoc can't be loaded in the main frame. It should
+// result in an error page. The error page should be cross-origin with the
+// opener.
+//
+// This test covers an interesting edge case. A main frame should inherit
+// sandbox flags. However the document loaded is an internal error page. This
+// might trigger some assertions, especially if the implementation wrongly
+// applies the sandbox flags of the opener to the internal error page document.
+//
+// This test is mainly a coverage test. It passes if it doesn't crash.
+async_test(test => {
+ let iframe = document.createElement("iframe");
+ iframe.sandbox = "allow-scripts allow-popups allow-same-origin";
+ iframe.srcdoc = `
+ <script>
+ let w = window.open();
+ onunload = () => w.close();
+
+ let notify = () => {
+ try {
+ w.origin; // Will fail after navigating to about:srcdoc.
+ parent.postMessage("pending", "*");
+ } catch (e) {
+ parent.postMessage("done", "*");
+ };
+ };
+
+ addEventListener("message", notify);
+ notify();
+
+ w.location = "about:srcdoc"; // Error page.
+ </scr`+`ipt>
+ `;
+
+ let closed = false;
+ addEventListener("message", event => {
+ closed = (event.data === "done");
+ iframe.contentWindow.postMessage("ping","*");
+ });
+
+ document.body.appendChild(iframe);
+ test.step_wait_func_done(()=>closed);
+}, "window.open('about:srcdoc') from sandboxed srcdoc doesn't crash.");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html b/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html
new file mode 100644
index 0000000000..91817c3db4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html
@@ -0,0 +1,90 @@
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script>
+
+// This is a regression test for https://crbug.com/1170038.
+//
+// A document creates a popup and makes it navigate elsewhere. The navigation
+// will never commit. The popup has not completed any real load outside of the
+// initial empty document. Then from a different window with a different CSP
+// policy, make it navigate to about:blank.
+//
+// Web browser behavior might change depending on whether a pending navigation
+// exists for in the popup or not. Both are tested here.
+
+const same_origin = get_host_info().HTTP_ORIGIN;
+
+// Return a promise, resolving when |element| triggers |event_name| event.
+const future = (element, event_name) => {
+ return new Promise(resolve => element.addEventListener(event_name, resolve));
+};
+
+// `createNewPopup` is a function returning a new window that has not committed
+// any real load in its frame, outside of the initial empty document. The two
+// tests below vary depending on whether there is a pending navigation in the
+// frame or not.
+const runTest = (description, createNewPopup) => {
+ promise_test(async test => {
+ // Open a same-origin window with a different CSP.
+ const executor_path =
+ "/html/browsers/sandboxing/resources/execute-postmessage.html?pipe=";
+ const csp = "|header(Content-Security-Policy, " +
+ "sandbox" +
+ " allow-scripts" +
+ " allow-popups" +
+ " allow-same-origin" +
+ " allow-popups-to-escape-sandbox";
+ const executor = window.open(same_origin + executor_path + csp);
+ const executor_reply = await future(window, "message");
+ assert_equals(executor_reply.data, "ready");
+
+ const popup_name = token();
+ const popup = await createNewPopup(test, popup_name);
+
+ // Request the first real load from a DIFFERENT window with different CSPs.
+ const first_real_load = future(popup, "load");
+ executor.postMessage(`window.open("about:blank", "${popup_name}");`);
+ await first_real_load;
+
+ // The new blank document in the popup must inherit CSPs from |executor|:
+ let is_sandboxed = future(window, "message");
+ popup.document.write(`
+ <script>
+ try {
+ document.domain = document.domain;
+ opener.opener.postMessage("not sandboxed", "*");
+ } catch (error) {
+ opener.opener.postMessage("sandboxed", "*");
+ }
+ </scr`+`ipt>
+ `);
+ assert_equals((await is_sandboxed).data, "sandboxed");
+ }, description);
+}
+
+// Open a new window and start loading from an unresponsive server. The frame
+// will be left with the initial empty document and a pending navigation.
+runTest("One pending navigation", async (test, popup_name) => {
+ const unresponsive_path =
+ "/fetch/api/resources/infinite-slow-response.py";
+ return window.open(same_origin + unresponsive_path, popup_name);
+});
+
+// Open a new window and start loading. The response is a 204 and the navigation
+// is canceled. As a result, the frame will be left with the initial empty
+// document and NO pending navigation.
+runTest("No pending navigation", async (test, popup_name) => {
+ const no_content_path = "/common/blank.html?pipe=status(204)"
+ const popup = window.open(same_origin + no_content_path, popup_name);
+
+ // Unfortunately, there are no web API to detect a navigation has been
+ // canceled. Waiting using setTimeout is the only possible way to wait for it.
+ await new Promise(r => test.step_timeout(r, 1000));
+
+ return popup;
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/BarProp.window.js b/testing/web-platform/tests/html/browsers/the-window-object/BarProp.window.js
new file mode 100644
index 0000000000..266779ee0b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/BarProp.window.js
@@ -0,0 +1,59 @@
+function assert_barProps(barPropObjects, visible) {
+ let lastBarProp = undefined;
+ for (const currentBarProp of barPropObjects) {
+ assert_not_equals(currentBarProp, lastBarProp, "BarBrop objects of different properties are identical");
+ assert_equals(currentBarProp.visible, visible, "a BarProp's visible is wrong");
+ lastBarProp = currentBarProp;
+ }
+}
+
+function assert_identical_barProps(barProps, w, oldBarPropObjects, visible) {
+ barProps.map(val => w[val]).map((val, index) => {
+ assert_equals(val, oldBarPropObjects[index], "BarProp identity not preserved");
+ });
+ assert_barProps(oldBarPropObjects, visible);
+}
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ frameW = frame.contentWindow,
+ barProps = ["locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar"],
+ barPropObjects = barProps.map(val => frameW[val]);
+
+ assert_barProps(barPropObjects, true);
+ frame.remove();
+ assert_identical_barProps(barProps, frameW, barPropObjects, false);
+ t.step_timeout(() => {
+ assert_identical_barProps(barProps, frameW, barPropObjects, false);
+ t.done();
+ }, 0);
+}, "BarBrop objects of a nested Window");
+
+async_test(t => {
+ const openee = window.open("/common/blank.html"),
+ barProps = ["locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar"],
+ barPropObjects = barProps.map(val => openee[val]);
+
+ // This is used to demonstrate that the Document is replaced while the global object (not the
+ // global this object) stays the same
+ openee.tiedToGlobalObject = openee.document;
+
+ assert_barProps(barPropObjects, true);
+ openee.onload = t.step_func(() => {
+ assert_own_property(openee, "tiedToGlobalObject");
+ assert_not_equals(openee.tiedToGlobalObject, openee.document);
+
+ assert_identical_barProps(barProps, openee, barPropObjects, true);
+
+ openee.onpagehide = t.step_func(() => {
+ assert_identical_barProps(barProps, openee, barPropObjects, true);
+ t.step_timeout(() => {
+ assert_identical_barProps(barProps, openee, barPropObjects, false);
+ t.done();
+ }, 0);
+ });
+
+ openee.close();
+ assert_identical_barProps(barProps, openee, barPropObjects, true);
+ });
+}, "BarProp objects of an auxiliary Window");
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/Document-defaultView.html b/testing/web-platform/tests/html/browsers/the-window-object/Document-defaultView.html
new file mode 100644
index 0000000000..dbc75d30b2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/Document-defaultView.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Document#defaultView</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ assert_equals(document.defaultView, window);
+}, "Document in a browsing context");
+
+test(function() {
+ var d = new Document();
+ assert_equals(d.defaultView, null);
+}, "Document created with the Document constructor");
+
+test(function() {
+ var d = document.implementation.createDocument(null, null);
+ assert_equals(d.defaultView, null);
+}, "Document created with createDocument");
+
+test(function() {
+ var d = document.implementation.createHTMLDocument();
+ assert_equals(d.defaultView, null);
+}, "Document created with createHTMLDocument");
+
+test(function() {
+ var parser = new DOMParser();
+ var d = parser.parseFromString("<foo\/\>", "application/xml");
+ assert_equals(d.defaultView, null);
+}, "Document created with XML DOMParser");
+
+test(function() {
+ var parser = new DOMParser();
+ var d = parser.parseFromString("bar", "text/html");
+ assert_equals(d.defaultView, null);
+}, "Document created with HTML DOMParser");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/Window-document.html b/testing/web-platform/tests/html/browsers/the-window-object/Window-document.html
new file mode 100644
index 0000000000..9b27f5f7c7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/Window-document.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Window#document</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+async_test(function() {
+ var URL = "/common/blank.html";
+
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ var initialWindow = iframe.contentWindow;
+ var initialDocument = initialWindow.document;
+ assert_equals(initialDocument.URL, "about:blank");
+ iframe.src = URL;
+ iframe.onload = this.step_func_done(function() {
+ assert_equals(iframe.contentWindow, initialWindow);
+ assert_equals(initialDocument.URL, "about:blank");
+ var loadedDocument = initialWindow.document;
+ assert_equals(loadedDocument.URL, location.href.replace(location.pathname, URL));
+ assert_not_equals(initialDocument, loadedDocument);
+ });
+}, "Document in a browsing context");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-01.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-01.html
new file mode 100644
index 0000000000..9710d15fb2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-01.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: the browsing contexts must be sorted in the order that their containers were inserted into the Document</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/browsers.html#accessing-other-browsing-contexts" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+var t1 = async_test("The window's length must return the number of child browsing contexts(in iframe)");
+function on_load1(fr) {
+ t1.step(function () {
+ var doc = fr.contentDocument;
+ var fr3 = doc.createElement("iframe");
+ fr3.setAttribute("id", "fr3");
+ doc.body.insertBefore(fr3, doc.getElementById("tbl"));
+
+ assert_equals(fr.contentWindow.length, 3, "The window.length should be 3.");
+ assert_array_equals([fr.contentWindow[0].frameElement, fr.contentWindow[1].frameElement, fr.contentWindow[2].frameElement],
+ [fr.contentDocument.getElementById("fr4"), fr.contentDocument.getElementById("fr5"), fr.contentDocument.getElementById("fr3")],
+ "The child browsing contexts must be sorted in the order that their containers were inserted into the Document.");
+ });
+ t1.done();
+}
+
+var t2 = async_test("The window's length must return zero if it has no child browsing context");
+function on_load2(fr) {
+ t2.step(function () {
+ assert_equals(fr.contentWindow.length, 0, "The window.length should be 0.");
+ });
+ t2.done();
+}
+
+</script>
+<iframe id="fr1" src="test1.html" style="display:none" onload="on_load1(this)"></iframe>
+<iframe id="fr2" src="test2.html" style="display:none" onload="on_load2(this)"></iframe>
+<script>
+
+test(function () {
+ assert_equals(window.length, 2, "The window.length should be 2.");
+ assert_array_equals([window[0].frameElement, window[1].frameElement],
+ [document.getElementById("fr1"), document.getElementById("fr2")],
+ "The child browsing contexts must be sorted in the tree order.");
+}, "The window's length must return the number of child browsing contexts");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-02.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-02.html
new file mode 100644
index 0000000000..d09c944fd8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-02.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>HTML Test: the browsing contexts created by various container elements</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+
+ var t1 = async_test("Accessing child browsing contexts 1");
+ var t2 = async_test("Accessing child browsing contexts 2");
+ var t3 = async_test("Accessing child browsing contexts 3");
+ function on_load() {
+ //Child browsing contexts created by iframe, object and embed elements.
+ t1.step(function () {
+ assert_equals(window.length, 3, "The top browsing context should have 3 child browsing contexts.");
+ });
+ t1.step(function () {
+ assert_equals(window[0].name, "win1", "The browsing context name should be 'win1'.");
+ assert_equals(window[1].name, "win2", "The browsing context name should be 'win2'.");
+ assert_equals(window[2].name, "win3", "The browsing context name should be 'win3'.");
+ });
+ t1.done();
+
+ //Child browsing contexts created by frame elements.
+ t2.step(function () {
+ assert_equals(document.getElementById("fr").contentWindow.length, 2,
+ "The child browsing context created by the iframe element should have 2 child browsing contexts.");
+ });
+ t2.step(function () {
+ assert_equals(document.getElementById("fr").contentWindow[0].name, "win4",
+ "The browsing context name should be 'win4'.");
+ assert_equals(document.getElementById("fr").contentWindow[1].name, "win5",
+ "The browsing context name should be 'win5'.");
+ });
+ t2.done();
+
+ //The child browsing context will be removed if the data attribute of the associated object element is removed.
+ t3.step(function () {
+ document.getElementById("obj").removeAttribute("type");
+ assert_equals(window.length, 3, "The top browsing context should have 3 child browsing contexts.");
+ document.getElementById("obj").removeAttribute("data");
+ assert_equals(window.length, 3, "The top browsing context should have 3 child browsing contexts.");
+
+ setTimeout(function () {
+ assert_equals(window.length, 2, "The top browsing context should have 2 child browsing contexts.");
+ }, 1);
+ });
+ t3.done();
+ }
+
+ </script>
+</head>
+<body onload="on_load()">
+ <div id="log"></div>
+ <div style="display:none">
+ <iframe id="fr" name="win1" src="test3.html"></iframe>
+ <object id="obj" name="win2" type="text/html" data="about:blank"></object>
+ <object type="image/png" src="/images/green.png"></object>
+ <embed id="emb" name="win3" type="image/svg+xml" src="/images/green.svg"></embed>
+ </div>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-03.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-03.html
new file mode 100644
index 0000000000..1548891175
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/indexed-browsing-contexts-03.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>HTML Test: indexed property of a Window object</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+
+ var t1 = async_test("Indexed child browsing contexts");
+ function on_load() {
+ t1.step(function () {
+ assert_equals(window[0], document.getElementsByTagName("object")[0].contentWindow,
+ "The first child browsing context's container should be the object element.");
+ assert_equals(window[1], document.getElementsByTagName("iframe")[0].contentWindow,
+ "The second child browsing context's container should be the iframe element.");
+ });
+ t1.done();
+ }
+
+ </script>
+</head>
+<body onload="on_load()">
+ <div id="log"></div>
+ <div style="display:none">
+ <div id="0"></div>
+ <object name="0" type="text/html" data="test2.html"></object>
+ <iframe name="0" src="about:blank"></iframe>
+ </div>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/iterator.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/iterator.html
new file mode 100644
index 0000000000..76dc7dbae6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/iterator.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>window[@@iterator]</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_false(Symbol.iterator in window);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test1.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test1.html
new file mode 100644
index 0000000000..f85f90f7c6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: child browsing contexts created by iframe elements</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<table id="tbl">
+ <tr>
+ <td>
+ <iframe id="fr4" src=""></iframe>
+ </td>
+ </tr>
+ <iframe id="fr5" src="about:blank"></iframe>
+</table>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test2.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test2.html
new file mode 100644
index 0000000000..d6a16647fe
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: child browsing contexts created by object and embed elements</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<object type="image/png" src="/images/green.png"></object>
+<embed type="image/png" src="/images/green.png"></embed>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test3.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test3.html
new file mode 100644
index 0000000000..a62fdbaae7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/test3.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: child browsing contexts created by frame elements</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<frameset>
+ <frame name="win4"></frame>
+ <frame name="win5"></frame>
+</frameset>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/window_length.html b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/window_length.html
new file mode 100644
index 0000000000..c9559b531a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/accessing-other-browsing-contexts/window_length.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<title>window.length</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var iframe;
+var subframe;
+var other_window;
+test(function() {assert_equals(window.length, 0)}, "No child browsing contexts");
+test(function() {
+ iframe = document.createElement("iframe");
+ assert_equals(window.length, 0)
+}, "iframe not inserted into the document");
+
+test(function() {
+ document.body.appendChild(iframe);
+ assert_equals(window.length, 1)
+}, "One iframe inserted into the document");
+
+test(function() {
+ subframe = document.createElement("iframe");
+ iframe.contentDocument.body.appendChild(subframe);
+ assert_equals(window.length, 1);
+}, "Child browsing context has a child browsing context");
+
+test(function() {
+ try {
+ assert_equals(iframe.contentWindow.length, 1);
+ } finally {
+ subframe.parentNode.removeChild(subframe);
+ }
+}, "window.length in child frame");
+
+test(function() {
+ iframe.parentNode.removeChild(iframe);
+ other_window = window.open();
+ assert_equals(window.length, 0);
+ assert_equals(other_window.length, 0);
+}, "Opened window")
+
+test(function() {
+ other_window.document.body.appendChild(iframe);
+ try {
+ assert_equals(other_window.length, 1);
+ } finally {
+ other_window.close();
+ }
+}, "Iframe in opened window")
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/close-method.window.js b/testing/web-platform/tests/html/browsers/the-window-object/close-method.window.js
new file mode 100644
index 0000000000..048c5f7236
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/close-method.window.js
@@ -0,0 +1,39 @@
+function assert_closed_opener(w, closed, opener) {
+ assert_equals(w.closed, closed);
+ assert_equals(w.opener, opener);
+}
+
+async_test(t => {
+ const openee = window.open();
+ assert_closed_opener(openee, false, self);
+ openee.onpagehide = t.step_func(() => {
+ assert_closed_opener(openee, true, self);
+ t.step_timeout(() => {
+ assert_closed_opener(openee, true, null);
+ t.done();
+ }, 0);
+ });
+ openee.close();
+ assert_closed_opener(openee, true, self);
+}, "window.close() queues a task to discard, but window.closed knows immediately");
+
+async_test(t => {
+ const openee = window.open("", "greatname");
+ assert_closed_opener(openee, false, self);
+ openee.close();
+ assert_closed_opener(openee, true, self);
+ const openee2 = window.open("", "greatname");
+ assert_not_equals(openee, openee2);
+ assert_closed_opener(openee, true, self); // Ensure second window.open() call was synchronous
+ openee2.onpagehide = t.step_func(() => {
+ assert_closed_opener(openee2, true, self);
+ t.step_timeout(() => {
+ assert_closed_opener(openee, true, null);
+ assert_closed_opener(openee2, true, null);
+ t.done();
+ }, 0);
+ });
+ openee2.close();
+ assert_closed_opener(openee, true, self); // Ensure second close() call was synchronous
+ assert_closed_opener(openee2, true, self);
+}, "window.close() affects name targeting immediately");
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/closed-attribute.window.js b/testing/web-platform/tests/html/browsers/the-window-object/closed-attribute.window.js
new file mode 100644
index 0000000000..88a3beba6f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/closed-attribute.window.js
@@ -0,0 +1,69 @@
+// META: script=/common/get-host-info.sub.js
+
+function closedTest(newWindow, closeNewWindowsBrowsingContext) {
+ assert_equals(newWindow.closed, false);
+ closeNewWindowsBrowsingContext();
+ assert_equals(newWindow.closed, true);
+}
+
+test(() => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ closedTest(frame.contentWindow, () => frame.remove());
+}, "closed and same-origin nested browsing context");
+
+test(() => {
+ const openee = window.open();
+ closedTest(openee, () => openee.close());
+
+ // close() is a no-op once "is closing" is set
+ openee.close();
+ assert_equals(openee.closed, true);
+}, "closed/close() and same-origin auxiliary browsing context");
+
+const support = new URL("support/closed.html", location.href).pathname;
+[
+ {
+ type: "cross-origin",
+ url: `${get_host_info().HTTP_REMOTE_ORIGIN}${support}`
+ },
+ {
+ type: "cross-site",
+ url: `${get_host_info().HTTP_NOTSAMESITE_ORIGIN}${support}`
+ }
+].forEach(val => {
+ async_test(t => {
+ const frame = document.createElement("iframe"),
+ ident = `${val.type}-nested-bc`;
+ frame.src = `${val.url}?window=parent&ident=${ident}`;
+ const listener = t.step_func(e => {
+ if (e.data === ident) {
+ closedTest(frame.contentWindow, () => frame.remove());
+ self.removeEventListener("message", listener);
+ t.done();
+ }
+ });
+ // Use a message event rather than onload for consistency with auxiliary browsing contexts.
+ self.addEventListener("message", listener);
+ document.body.append(frame);
+ }, `closed and ${val.type} nested browsing context`);
+
+ async_test(t => {
+ const ident = `${val.type}-auxiliary-bc`,
+ support = new URL("support/closed.html", location.href).pathname,
+ openee = window.open(`${val.url}?window=opener&ident=${ident}`),
+ listener = t.step_func(e => {
+ if (e.data === ident) {
+ closedTest(openee, () => openee.close());
+
+ // close() is a no-op once "is closing" is set
+ openee.close();
+ assert_equals(openee.closed, true);
+
+ self.removeEventListener("message", listener);
+ t.done();
+ }
+ });
+ // As there's no cross-origin onload, use a message event.
+ self.addEventListener("message", listener);
+ }, `closed/close() and ${val.type} auxiliary browsing context`);
+});
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/defaultstatus.html b/testing/web-platform/tests/html/browsers/the-window-object/defaultstatus.html
new file mode 100644
index 0000000000..f683f130cd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/defaultstatus.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.defaultStatus and window.defaultstatus are not supported</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/defaultStatus">
+<link rel="help" href="https://crbug.com/692835">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+test(() => {
+ assert_false(window.hasOwnProperty('defaultStatus'));
+ assert_false(window.hasOwnProperty('defaultstatus'));
+ assert_equals(window.defaultStatus,undefined);
+ assert_equals(window.defaultstatus,undefined);
+}, "The window.defaultStatus and window.defaultstatus attributes are not supported");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/document-attribute.window.js b/testing/web-platform/tests/html/browsers/the-window-object/document-attribute.window.js
new file mode 100644
index 0000000000..f13acdb8a3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/document-attribute.window.js
@@ -0,0 +1,15 @@
+async_test(t => {
+ const frame = document.createElement("iframe");
+ frame.onload = t.step_func(() => {
+ const frameW = frame.contentWindow,
+ frameD = frame.contentDocument;
+ assert_equals(frameW.document, frameD);
+ frame.remove();
+ assert_equals(frameW.document, frameD);
+ t.step_timeout(() => {
+ assert_equals(frameW.document, frameD);
+ t.done();
+ }, 100);
+ });
+ document.body.append(frame);
+}, "Window object's document IDL attribute and discarding the browsing context");
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/focus.window.js b/testing/web-platform/tests/html/browsers/the-window-object/focus.window.js
new file mode 100644
index 0000000000..6ec7feee28
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/focus.window.js
@@ -0,0 +1,15 @@
+async_test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ input.onfocus = t.step_func(() => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ frameW = frame.contentWindow;
+ frameW.onfocus = t.unreached_func();
+ frame.remove();
+ frameW.focus();
+ t.step_timeout(() => {
+ assert_equals(document.activeElement, input);
+ t.done();
+ }, 100);
+ });
+ input.focus();
+});
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-1.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-1.html
new file mode 100644
index 0000000000..217608e46e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-1.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<iframe></iframe>
+<script>
+var t = opener.t;
+
+onload = t.step_func(function() {
+ setTimeout(t.step_func(function() {
+ var history_length = history.length;
+ var iframe = document.getElementsByTagName("iframe")[0];
+ iframe.onload = t.step_func(function() {
+ opener.assert_equals(history.length, history_length + 1);
+ iframe.parentNode.removeChild(iframe);
+ opener.assert_equals(history.length, history_length);
+ t.done();
+ window.close();
+ });
+ iframe.src = "discard_iframe_history_1-2.html;";
+ }), 100);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-2.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-2.html
new file mode 100644
index 0000000000..b43598f2cd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1-2.html
@@ -0,0 +1,2 @@
+<!doctype html>
+Filler text
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1.html
new file mode 100644
index 0000000000..4d1e473fc0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_1.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Removing iframe from document removes it from history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var w = window.open("discard_iframe_history_1-1.html");
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2-1.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2-1.html
new file mode 100644
index 0000000000..61e5891ebd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2-1.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<iframe></iframe>
+<script>
+var t = opener.t;
+
+onload = t.step_func(function() {
+ setTimeout(t.step_func(function() {
+ var history_length = history.length;
+ var iframe = document.getElementsByTagName("iframe")[0];
+ iframe.onload = t.step_func(function() {
+ setTimeout(t.step_func(function() {
+ opener.assert_equals(history.length, history_length + 1, "History length before iframe removal");
+ document.body.innerHTML = "";
+ opener.assert_equals(history.length, history_length, "History length after iframe removal");
+ t.done();
+ window.close();
+ }), 100);
+ });
+ iframe.src = "discard_iframe_history_1-2.html";
+ }), 100);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2.html
new file mode 100644
index 0000000000..89d0fb4c64
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_2.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Removing iframe from document via innerHTML removes it from history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var w = window.open("discard_iframe_history_2-1.html");
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-1.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-1.html
new file mode 100644
index 0000000000..de3f075d64
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-1.html
@@ -0,0 +1,21 @@
+<script>
+history_lengths = [];
+
+var t = opener.t;
+
+push_length = t.step_func(function () {
+ history_lengths.push(history.length)
+});
+
+do_test = t.step_func(function () {
+ try {
+ var start_length = history_lengths[0];
+ expected = [start_length, start_length + 1, start_length];
+ opener.assert_array_equals(history_lengths, expected);
+ t.done();
+ } finally {
+ window.close();
+ }
+});
+</script>
+<iframe src="discard_iframe_history_3-2.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-2.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-2.html
new file mode 100644
index 0000000000..95f9fce5d8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-2.html
@@ -0,0 +1,4 @@
+<a href="discard_iframe_history_3-3.html" onclick="parent.push_length()">Click me</a>
+<script>
+onload = function() {setTimeout(parent.t.step_func(function() {document.links[0].click()}), 100)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-3.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-3.html
new file mode 100644
index 0000000000..4672b0ec30
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3-3.html
@@ -0,0 +1,4 @@
+<button onclick="var p = parent; p.push_length(); frameElement.parentNode.removeChild(frameElement); p.push_length(); p.do_test();">Click me</button>
+<script>
+onload = function() {setTimeout(parent.t.step_func(function() {document.getElementsByTagName("button")[0].click()}), 100)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3.html
new file mode 100644
index 0000000000..38dbdbd0fa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Removing iframe from document removes it from history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var w = window.open("discard_iframe_history_3-1.html");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-1.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-1.html
new file mode 100644
index 0000000000..1b5726cdcd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-1.html
@@ -0,0 +1,21 @@
+<script>
+history_lengths = [];
+
+var t = opener.t;
+
+push_length = t.step_func(function () {
+ history_lengths.push(history.length)
+});
+
+do_test = t.step_func(function () {
+ try {
+ var start_length = history_lengths[0];
+ expected = [start_length, start_length + 1, start_length];
+ opener.assert_array_equals(history_lengths, expected);
+ t.done();
+ } finally {
+ window.close();
+ }
+});
+</script>
+<iframe src="discard_iframe_history_4-2.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-2.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-2.html
new file mode 100644
index 0000000000..979b2b28e4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-2.html
@@ -0,0 +1,4 @@
+<a href="discard_iframe_history_4-3.html" onclick="parent.push_length()">Click me</a>
+<script>
+onload = function() {setTimeout(parent.t.step_func(function() {document.links[0].click()}), 100)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-3.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-3.html
new file mode 100644
index 0000000000..b4308f439a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-3.html
@@ -0,0 +1,4 @@
+<button onclick="var p = parent; p.push_length(); frameElement.parentNode.innerHTML = ''; p.push_length(); p.do_test();">Click me</button>
+<script>
+onload = function() {setTimeout(parent.t.step_func(function() {document.getElementsByTagName("button")[0].click()}), 100)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4.html
new file mode 100644
index 0000000000..ffd444e3bf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Removing iframe from document removes it from history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var w = window.open("discard_iframe_history_4-1.html");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-1.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-1.html
new file mode 100644
index 0000000000..9969427989
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-1.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<iframe></iframe>
+<script>
+var t = opener.t;
+var iframe = document.getElementsByTagName("iframe")[0];
+var history_length;
+
+function load_frame(src) {
+ history_length = history.length;
+ iframe.src = src;
+ var button = document.getElementsByTagName("button")[0];
+ button.parentNode.removeChild(button);
+}
+
+remove_frame = t.step_func(function() {
+ try {
+ opener.assert_equals(history.length, history_length + 1, "History length after loading page in iframe");
+ iframe.parentNode.removeChild(iframe);
+ opener.assert_equals(history.length, history_length, "History length after removing iframe");
+ t.done();
+ } finally {
+ window.close();
+ }
+});
+
+</script>
+<button onclick="load_frame('discard_iframe_history_1-2.html')">Click here</button>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-2.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-2.html
new file mode 100644
index 0000000000..8c3d1a9dad
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-2.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<button onclick="parent.remove_frame()">Click here</button>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-manual.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-manual.html
new file mode 100644
index 0000000000..ca9dd91f2e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_1-manual.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Removing iframe from document removes it from history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+setup({timeout:3600000})
+var t = async_test();
+var w = window.open("discard_iframe_history_1-1.html");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-1.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-1.html
new file mode 100644
index 0000000000..bc01cae88c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-1.html
@@ -0,0 +1,19 @@
+<script>
+history_lengths = [];
+
+function push_length() {
+ history_lengths.push(history.length)
+}
+
+do_test = opener.t.step_func(function () {
+ try {
+ var start_length = history_lengths[0];
+ expected = [start_length, start_length + 1, start_length];
+ opener.assert_array_equals(history_lengths, expected);
+ opener.t.done();
+ } finally {
+ window.close();
+ }
+});
+</script>
+<iframe src="discard_iframe_history_2-2.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-2.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-2.html
new file mode 100644
index 0000000000..b25bf5f001
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-2.html
@@ -0,0 +1 @@
+<a href="discard_iframe_history_2-3.html" onclick="parent.push_length()">Click me</a>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-3.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-3.html
new file mode 100644
index 0000000000..68847e9a7c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-3.html
@@ -0,0 +1 @@
+<button onclick="var p = parent; p.push_length(); frameElement.parentNode.removeChild(frameElement); p.push_length(); p.do_test();">Click me</button>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-manual.html b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-manual.html
new file mode 100644
index 0000000000..57eaabcd0c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/non-automated/discard_iframe_history_2-manual.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Removing iframe from document removes it from history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+setup({timeout:3600000})
+var t = async_test();
+var w = window.open("discard_iframe_history_2-1.html");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/historical.window.js b/testing/web-platform/tests/html/browsers/the-window-object/historical.window.js
new file mode 100644
index 0000000000..653f12b464
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/historical.window.js
@@ -0,0 +1,4 @@
+test(() => {
+ assert_false("showModalDialog" in window)
+ assert_false("showModalDialog" in Window.prototype)
+}, "showModalDialog() has been removed from the platform")
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/length-attribute.window.js b/testing/web-platform/tests/html/browsers/the-window-object/length-attribute.window.js
new file mode 100644
index 0000000000..d56e1e4692
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/length-attribute.window.js
@@ -0,0 +1,24 @@
+async_test(t => {
+ const frame = document.createElement("iframe");
+ frame.srcdoc = "<iframe name=x srcdoc='<iframe name=z></iframe>'></iframe><iframe name=y></iframe>";
+ frame.onload = t.step_func_done(() => {
+ const frameW = frame.contentWindow;
+ assert_equals(frameW.length, 2);
+ assert_not_equals(frameW.x, undefined);
+ assert_not_equals(frameW.y, undefined);
+ assert_equals(frameW.z, undefined);
+ assert_equals(frameW.x, frameW[0]);
+ assert_equals(frameW.y, frameW[1]);
+ const xFrameW = frameW.x;
+ assert_equals(xFrameW.length, 1);
+ assert_not_equals(xFrameW.z, undefined);
+ assert_equals(xFrameW.z, xFrameW[0]);
+ frame.remove();
+ assert_equals(frameW.length, 0);
+ assert_equals(frameW.x, undefined);
+ assert_equals(frameW[0], undefined);
+ assert_equals(xFrameW.length, 0);
+ assert_equals(xFrameW.z, undefined);
+ });
+ document.body.append(frame);
+}, "Window object's length IDL attribute (and named access)");
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/name-attribute.window.js b/testing/web-platform/tests/html/browsers/the-window-object/name-attribute.window.js
new file mode 100644
index 0000000000..f266dd7acb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/name-attribute.window.js
@@ -0,0 +1,18 @@
+test(() => {
+ const frame = document.createElement("iframe"),
+ name = "A",
+ name2 = "B";
+ frame.setAttribute("name", name);
+ document.body.append(frame);
+ const frameW = frame.contentWindow;
+ assert_equals(frameW.name, name);
+ frameW.name = name2;
+ assert_equals(frame.getAttribute("name"), name);
+ assert_equals(frameW.name, name2);
+ frame.remove();
+ assert_equals(frame.getAttribute("name"), name);
+ assert_equals(frameW.name, "");
+ frameW.name = name2;
+ assert_equals(frame.getAttribute("name"), name);
+ assert_equals(frameW.name, "");
+}, "Window object's name IDL attribute");
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html
new file mode 100644
index 0000000000..2acad0734c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Named access across globals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function() {
+ var iframe = document.createElement("iframe");
+ iframe.src = "cross-global-support.html";
+ document.body.appendChild(iframe);
+ iframe.onload = this.step_func_done(function() {
+ var name = "named";
+ var win = iframe.contentWindow;
+ var element = win.document.getElementById(name);
+
+ var expectedValues = [
+ // [value, is own property]
+ [element, false, "window"],
+ [element, false, "Window.prototype"],
+ [element, true, "named prototype object"],
+ [undefined, false, "EventTarget.prototype"],
+ [undefined, false, "Object.prototype"],
+ ];
+ for (var object = win; object; object = Object.getPrototypeOf(object)) {
+ var expected = expectedValues.shift();
+ assert_equals(object[name], expected[0], "[[Get]] on " + expected[2]);
+ var desc = Object.getOwnPropertyDescriptor(object, name);
+ if (expected[1]) {
+ assert_not_equals(desc, undefined, "[[GetOwnProperty]] on " + expected[2] + " should return something");
+ assert_equals(desc.value, element, "[[GetOwnProperty]] on " + expected[2]);
+ } else {
+ assert_equals(desc, undefined, "[[GetOwnProperty]] on " + expected[2]);
+ }
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html
new file mode 100644
index 0000000000..9d7b9f8a25
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Named access across globals: support file</title>
+<span id="named"></span>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html
new file mode 100644
index 0000000000..d5b1789d59
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Named access on the Window object</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/browsers.html#named-access-on-the-window-object">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div style="display:none">
+ <p name="a" id="p1"></p>
+ <a name="a" id="a1" href="#"></a>
+ <area name="a" id="area1"></area>
+ <embed name="a" id="embed1"></embed>
+ <form name="a" id="form1"></form>
+ <img name="a" id="img1">
+ <object name="a" id="obj1"></object>
+ <span name="a" id="span1"></span>
+
+ <b id="b" name="c"></b>
+ <a name="c"></a>
+ <iframe name="c" id="fm1"></iframe>
+ <iframe name="c" id="fm2" src="test.html" onload="on_load()"></iframe>
+ <input id="b"></input>
+ <span id="d"></span>
+ <a name=""></a>
+ <b id=""></b>
+</div>
+<script>
+
+test(function() {
+ assert_equals(window['c'], document.getElementById("fm1").contentWindow, "The first iframe's window should be returned.");
+}, "Check if the first nested browsing context is returned by window['c']");
+
+test(function() {
+ assert_true(window['a'] instanceof HTMLCollection);
+ assert_array_equals(window['a'],
+ [ document.getElementById('embed1'),
+ document.getElementById('form1'), document.getElementById('img1'),
+ document.getElementById('obj1') ],
+ "The elements are not in tree order.");
+
+ document.getElementById('form1').setAttribute("name", "");
+ document.getElementById('embed1').setAttribute("name", "");
+ assert_array_equals(window['a'],
+ [ document.getElementById('img1'),
+ document.getElementById('obj1') ],
+ "Window['a'] should not contain the elements with empty name attribute.");
+}, "Check if window['a'] contains all embed, form, img, and object elements, and their order");
+
+var t = async_test("Check that window['fs'] does not return the frameset element with name='fs' (historical)");
+function on_load () {
+ t.step(function () {
+ assert_equals(document.getElementById('fm2').contentWindow['fs'],
+ undefined,
+ "The frameset element should not be returned.");
+ });
+ t.done();
+}
+
+test(function() {
+ assert_true(window['b'] instanceof HTMLCollection);
+ assert_array_equals(window['b'], [document.getElementsByTagName('b')[0], document.getElementsByTagName('input')[0]]);
+
+ document.getElementsByTagName('b')[0].setAttribute("id", "");
+ assert_equals(window['b'], document.getElementsByTagName('input')[0],
+ "The window['b'] should not contain the elements with empty id attribute.");
+}, "Check if window['b'] returns the elements with the id='b'");
+
+test(function() {
+ assert_equals(window['d'], document.getElementById('d'));
+}, "Check if window['d'] returns the element with id='d'");
+
+test(function() {
+ assert_equals(window[''], undefined, "The window[''] should be undefined");
+}, "Check widow[''] when there are some elements with empty id or name attribute");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/navigated-named-objects.window.js b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/navigated-named-objects.window.js
new file mode 100644
index 0000000000..59d94efc99
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/navigated-named-objects.window.js
@@ -0,0 +1,67 @@
+// META: script=/common/get-host-info.sub.js
+
+function echoURL(content) {
+ return `/common/echo.py?content=${encodeURIComponent(content)}`;
+}
+
+function setSrc(frame, type, content) {
+ if (type === "same-origin") {
+ frame.src = echoURL(content);
+ } else if (type === "cross-site") {
+ frame.src = `${get_host_info().HTTP_NOTSAMESITE_ORIGIN}${echoURL(content)}`;
+ } else {
+ frame.srcdoc = content;
+ }
+}
+
+["srcdoc", "same-origin", "cross-site"].forEach(type => {
+ const initialType = type === "srcdoc" ? type : "same-origin";
+
+ [
+ {
+ "namedObject": "<div id=abc></div>",
+ "namedObjectLocalName": "div"
+ },
+ {
+ "namedObject": "<object name=abc></object>",
+ "namedObjectLocalName": "object"
+ },
+ {
+ "namedObject": "<iframe id=abc></iframe>",
+ "namedObjectLocalName": "iframe"
+ }
+ ].forEach(testData => {
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ setSrc(frame, initialType, `<script>function f() { return abc }</script>${testData.namedObject}`);
+ frame.onload = t.step_func(() => {
+ const f = frame.contentWindow.f,
+ associatedAbc = f();
+ frame.onload = t.step_func_done(() => {
+ assert_equals(f(), associatedAbc);
+ assert_equals(associatedAbc.localName, testData.namedObjectLocalName);
+ });
+ setSrc(frame, type, "<span id=abc></span>");
+ });
+ document.body.append(frame);
+ }, `Window's associated Document object is used for finding named objects (<${testData.namedObjectLocalName}> via ${type} <iframe>)`);
+ });
+
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ setSrc(frame, initialType, "<script>function f() { return abc }</script><object name=abc data='about:blank'></object>");
+ frame.onload = t.step_func(() => {
+ const f = frame.contentWindow.f,
+ associatedAbc = f(),
+ associatedAbcContainer = associatedAbc.frameElement;
+ frame.onload = t.step_func_done(() => {
+ assert_equals(f(), associatedAbcContainer);
+ assert_equals(associatedAbcContainer.contentWindow, null);
+ });
+ setSrc(frame, type, "<span id=abc></span>");
+ });
+ document.body.append(frame);
+ }, `Window's associated Document object is used for finding named objects (<object> with browsing ccontext via ${type} <iframe)>`);
+});
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/prototype.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/prototype.html
new file mode 100644
index 0000000000..910374381b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/prototype.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Named access with shadowing properties</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var name = "named1";
+ window[name] = "shadowing";
+ var element = document.createElement("span");
+ element.id = name;
+ document.body.appendChild(element);
+
+ assert_equals(window[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(window, name).value, "shadowing");
+
+ assert_equals(Window.prototype[name], element);
+ assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name), undefined);
+
+ var npo = Object.getPrototypeOf(Window.prototype);
+ assert_equals(npo[name], element);
+ assert_equals(Object.getOwnPropertyDescriptor(npo, name).value, element);
+
+ assert_equals(EventTarget.prototype[name], undefined);
+ assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name), undefined);
+}, "Property on window.");
+
+test(function() {
+ var name = "named2";
+ Window.prototype[name] = "shadowing";
+ var element = document.createElement("span");
+ element.id = name;
+ document.body.appendChild(element);
+
+ assert_equals(window[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(window, name), undefined);
+
+ assert_equals(Window.prototype[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name).value, "shadowing");
+
+ var npo = Object.getPrototypeOf(Window.prototype);
+ assert_equals(npo[name], element);
+ assert_equals(Object.getOwnPropertyDescriptor(npo, name).value, element);
+
+ assert_equals(EventTarget.prototype[name], undefined);
+ assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name), undefined);
+}, "Property on Window.prototype.");
+
+test(function() {
+ var name = "named3";
+ EventTarget.prototype[name] = "shadowing";
+ var element = document.createElement("span");
+ element.id = name;
+ document.body.appendChild(element);
+
+ assert_equals(window[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(window, name), undefined);
+
+ assert_equals(Window.prototype[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name), undefined);
+
+ var npo = Object.getPrototypeOf(Window.prototype);
+ assert_equals(npo[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(npo, name), undefined);
+
+ assert_equals(EventTarget.prototype[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name).value, "shadowing");
+}, "Property on EventTarget.prototype.");
+
+test(function() {
+ var name = "named4";
+ Object.prototype[name] = "shadowing";
+ var element = document.createElement("span");
+ element.id = name;
+ document.body.appendChild(element);
+
+ assert_equals(window[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(window, name), undefined);
+
+ assert_equals(Window.prototype[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name), undefined);
+
+ var npo = Object.getPrototypeOf(Window.prototype);
+ assert_equals(npo[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(npo, name), undefined);
+
+ assert_equals(EventTarget.prototype[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name), undefined);
+
+ assert_equals(Object.prototype[name], "shadowing");
+ assert_equals(Object.getOwnPropertyDescriptor(Object.prototype, name).value, "shadowing");
+}, "Property on Object.prototype.");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/strict-mode-redefine-readonly-property.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/strict-mode-redefine-readonly-property.html
new file mode 100644
index 0000000000..ce1da3747a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/strict-mode-redefine-readonly-property.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<link rel="help" href="https://crbug.com/1448846">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+test(() => {
+ "use strict";
+ const pathname = document.location.pathname;
+ const form = document.createElement('form');
+ form.name = 'location';
+ document.body.appendChild(form);
+ assert_equals(document.location?.pathname, pathname);
+ form.remove();
+}, "Adding/removing form with a name referring to a non-configurable property");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/test.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/test.html
new file mode 100644
index 0000000000..c3b3cc1852
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/test.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Named Object</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<frameset name="fs" id="fs1">
+ <frame></frame>
+</frameset>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-named-properties.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-named-properties.html
new file mode 100644
index 0000000000..bd3929dd89
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-named-properties.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Changes to named properties of the window object</title>
+<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
+<link rel="author" title="Boris Zbarsky" href="bzbarsky@mit.edu">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-window-nameditem">
+<link rel="help" href="https://webidl.spec.whatwg.org/#named-properties-object">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe name="bar"></iframe>
+<iframe name="baz"></iframe>
+<iframe name="baz"></iframe>
+<iframe name="constructor"></iframe>
+<iframe id="quux"></iframe>
+<script>
+function assert_data_propdesc(pd, Writable, Enumerable, Configurable) {
+ assert_equals(typeof pd, "object");
+ assert_equals(pd.writable, Writable);
+ assert_equals(pd.enumerable, Enumerable);
+ assert_equals(pd.configurable, Configurable);
+}
+test(function() {
+ assert_true("bar" in window, "bar not in window");
+ assert_equals(window["bar"],
+ document.getElementsByTagName("iframe")[0].contentWindow);
+}, "Static name");
+
+test(function() {
+ assert_true("quux" in window, "quux not in window");
+ assert_equals(window["quux"],
+ document.getElementsByTagName("iframe")[4]);
+}, "Static id");
+
+test(function() {
+ assert_true("bar" in Window.prototype, "bar in Window.prototype");
+ assert_false(Window.prototype.hasOwnProperty("bar"), "Window.prototype.hasOwnProperty(\"bar\")");
+
+ var gsp = Object.getPrototypeOf(Object.getPrototypeOf(window));
+ assert_true("bar" in gsp, "bar in gsp");
+ assert_true(gsp.hasOwnProperty("bar"), "gsp.hasOwnProperty(\"bar\")");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(gsp, "bar"),
+ true, false, true);
+}, "Static name on the prototype");
+test(function() {
+ assert_equals(window.constructor, Window);
+ assert_false(window.hasOwnProperty("constructor"), "window.constructor should not be an own property.");
+
+ var proto = Object.getPrototypeOf(window);
+ assert_equals(proto.constructor, Window);
+ assert_true("constructor" in proto, "constructor in proto");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(proto, "constructor"),
+ true, false, true);
+
+ var gsp = Object.getPrototypeOf(proto);
+ assert_true("constructor" in gsp, "constructor in gsp");
+ assert_false(gsp.hasOwnProperty("constructor"), "gsp.hasOwnProperty(\"constructor\")");
+ assert_equals(Object.getOwnPropertyDescriptor(gsp, "constructor"), undefined);
+}, "constructor");
+test(function() {
+ var gsp = Object.getPrototypeOf(Object.getPrototypeOf(window));
+ assert_equals(gsp.baz, document.getElementsByTagName("iframe")[1].contentWindow);
+}, "duplicate property names")
+var t = async_test("Dynamic name")
+var t2 = async_test("Ghost name")
+t.step(function() {
+ var iframe = document.getElementsByTagName("iframe")[0];
+ iframe.setAttribute("srcdoc", "<script>window.name='foo'<\/script>");
+ iframe.onload = function() {
+ t.step(function() {
+ assert_true("foo" in window, "foo not in window");
+ assert_equals(window["foo"], iframe.contentWindow);
+ });
+ t.done();
+ t2.step(function() {
+ assert_false("bar" in window, "bar still in window");
+ assert_equals(window["bar"], undefined);
+ });
+ t2.done();
+ };
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-null-names.html b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-null-names.html
new file mode 100644
index 0000000000..6801ef9d8a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/window-null-names.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Named access with null characters</title>
+<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-window-nameditem">
+<link rel="help" href="https://webidl.spec.whatwg.org/#named-properties-object">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var iframe = document.createElement("iframe")
+ iframe.name = "a\0b"
+ document.body.appendChild(iframe)
+ assert_equals(window["a\0b"], iframe.contentWindow)
+ assert_equals(window["ab"], undefined)
+ assert_equals(window["a"], undefined)
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/navigate-to-about-blank-while-initial-load-pending.html b/testing/web-platform/tests/html/browsers/the-window-object/navigate-to-about-blank-while-initial-load-pending.html
new file mode 100644
index 0000000000..3a0def8ae6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/navigate-to-about-blank-while-initial-load-pending.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test navigating to about:blank while window.open initial load pending.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ // Open a new window and initiate a navigation. The test does not actually
+ // expect this navigation to complete so it does not matter what URL is
+ // used other than it must not be about:blank. The intent is to start a
+ // navigation to some URL and then assign about:blank to the location
+ // attribute. This assignment should stop the inital navigation and start a
+ // new navigation to about:blank. When the about:blank page finishes loading
+ // the load event is expected to fire and the document URL should to be set to
+ // about:blank.
+ var window1 = window.open('resources/post-to-opener.html', '_blank');
+ t.add_cleanup(() => {
+ window1.close();
+ });
+ window1.location = 'about:blank';
+ window1.onload = t.step_func_done(e => {
+ assert_equals(window1.document.URL, "about:blank");
+ });
+}, 'Navigating to about:blank while window.open initial load pending.');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-BarProp.window.js b/testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-BarProp.window.js
new file mode 100644
index 0000000000..a75a034650
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-BarProp.window.js
@@ -0,0 +1,23 @@
+const barProps = ["locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar"];
+
+test(() => {
+ for(const prop of barProps) {
+ assert_true(window[prop].visible);
+ }
+}, "All bars visible");
+
+["noopener", "noreferrer"].forEach(openerStyle => {
+ async_test(t => {
+ const channelName = "5454" + openerStyle + "34324";
+ const channel = new BroadcastChannel(channelName);
+ window.open("support/BarProp-target.html?" + channelName, "", openerStyle);
+ channel.onmessage = t.step_func_done(e => {
+ // Send message first so if asserts throw the popup is still closed
+ channel.postMessage(null);
+
+ for(const prop of barProps) {
+ assert_true(e.data[prop]);
+ }
+ });
+ }, `window.open() with ${openerStyle} should have all bars visible`);
+});
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-sizing.window.js b/testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-sizing.window.js
new file mode 100644
index 0000000000..cc53ba5f2f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/noopener-noreferrer-sizing.window.js
@@ -0,0 +1,17 @@
+const windowProps = ["innerWidth", "innerHeight"];
+
+["noopener", "noreferrer"].forEach(openerStyle => {
+ async_test(t => {
+ const channelName = "34342" + openerStyle + "8907";
+ const channel = new BroadcastChannel(channelName);
+ window.open("support/sizing-target.html?" + channelName, "", openerStyle);
+ channel.onmessage = t.step_func_done(e => {
+ // Send message first so if asserts throw the popup is still closed
+ channel.postMessage(null);
+
+ for(const prop of windowProps) {
+ assert_equals(window[prop], e.data[prop]);
+ }
+ });
+ }, `window.open() with ${openerStyle} should have equal viewport width and height`);
+});
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/callback.js b/testing/web-platform/tests/html/browsers/the-window-object/open-close/callback.js
new file mode 100644
index 0000000000..ae51265a21
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/callback.js
@@ -0,0 +1 @@
+opener.callback() \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload-1.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload-1.html
new file mode 100644
index 0000000000..6f44d8a83e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+onload = function() {opener.postMessage("loaded", "*")};
+onbeforeunload = function() {
+ opener.callback();
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload.html
new file mode 100644
index 0000000000..dcb8830ab6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_beforeunload.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Running beforeunload handler in window.close()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var w = window.open("close_beforeunload-1.html");
+onmessage = t.step_func(function(event) {
+ if (event.data != "loaded") {
+ return;
+ }
+ w.close();
+});
+callback = function() {t.done()}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide-1.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide-1.html
new file mode 100644
index 0000000000..5631b539a6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+onload = function() {opener.postMessage("loaded", "*")};
+onpagehide = function() {
+ opener.callback();
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide.html
new file mode 100644
index 0000000000..4d726437c2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_pagehide.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Running pagehide handler in window.close()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var w = window.open("close_pagehide-1.html");
+onmessage = t.step_func(function(event) {
+ if (event.data != "loaded") {
+ return;
+ }
+ w.close();
+});
+callback = function() {t.done()}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer-1.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer-1.html
new file mode 100644
index 0000000000..c50eddd41f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer-1.html
@@ -0,0 +1 @@
+<!doctype html>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer.html
new file mode 100644
index 0000000000..1217882b16
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_script_defer.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Running defer script in window.close()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+t.step(function() {
+ var w = window.open("close_script_defer-1.html");
+ w.document.open()
+ w.document.write("<script defer src='callback.js'><\/script>")
+ setTimeout(function() {
+ w.close();
+ }, 1000);
+})
+setTimeout(function() {t.done();}, 1000)
+callback = t.step(function() {assert_unreached()})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload-1.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload-1.html
new file mode 100644
index 0000000000..9a9e304e84
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+onload = function() {opener.postMessage("loaded", "*")};
+onunload = function() {
+ opener.callback();
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload.html
new file mode 100644
index 0000000000..e4d231b285
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_unload.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Running unload handler in window.close()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+var w = window.open("close_unload-1.html");
+onmessage = t.step_func(function(event) {
+ if (event.data != "loaded") {
+ return;
+ }
+ w.close();
+});
+callback = function() {t.done()}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/creating_browsing_context_test_01.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/creating_browsing_context_test_01.html
new file mode 100644
index 0000000000..062f61949d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/creating_browsing_context_test_01.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[Browsing Context] : [APIs for creating browsing_contexts by name]</title>
+<link rel="author" title="Duhyeong Kim" href="mailto:dduskim@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function() {
+ var currentUrl = 'http://' + window.location.host + '/common/blank.html';
+ var win = window.open(currentUrl, '', 'height=1,width=1');
+ this.add_cleanup(function() { win.close(); });
+ win.onload = this.step_func_done(function () {
+ assert_equals(win.location.href, currentUrl, 'should be equal to result url');
+ });
+}, 'first argument: absolute url');
+
+test(function() {
+ var win = window.open('', '', 'height=1,width=1');
+ this.add_cleanup(function() { win.close(); });
+ assert_equals(win.location.href, 'about:blank', 'win.location.href');
+ assert_equals(win.document.charset, 'UTF-8', 'win.document.charset');
+}, 'first argument: empty url');
+
+test(function () {
+ var win = window.open('', 'testWindow', 'height=1,width=1');
+ win.close();
+ assert_equals(win.name, 'testWindow', 'should have a browsing context name');
+}, 'second argument: passing a non-empty name');
+
+test(function () {
+ var win = window.open('', '', 'height=1,width=1');
+ this.add_cleanup(function() { win.close(); });
+ assert_equals(win.name, '', 'window should not have a name');
+ win.name = 'testWindow';
+ assert_equals(win.name, 'testWindow', 'window should have a name');
+}, 'second argument: setting name after opening');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/no_window_open_when_term_nesting_level_nonzero.window.js b/testing/web-platform/tests/html/browsers/the-window-object/open-close/no_window_open_when_term_nesting_level_nonzero.window.js
new file mode 100644
index 0000000000..3dff403b9c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/no_window_open_when_term_nesting_level_nonzero.window.js
@@ -0,0 +1,113 @@
+test(function() {
+ var test_window = window.open('', '', 'height=1,width=1');
+ var test_document = test_window.document;
+
+ var frame = test_document.createElement('iframe');
+ test_document.body.appendChild(frame);
+
+ frame.contentWindow.onpagehide = function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during pagehide");
+ };
+ frame.contentDocument.onvisibilitychange = function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during visibilitychange");
+ };
+ frame.contentWindow.onbeforeunload = function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during beforeunload");
+ };
+ frame.contentWindow.onunload = function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during unload");
+ };
+
+ frame.remove();
+}, 'no popups with frame removal');
+
+async_test(function(t) {
+ var test_window = window.open('', '', 'height=1,width=1');
+ var test_document = test_window.document;
+
+ var frame = test_document.createElement('iframe');
+ test_document.body.appendChild(frame);
+
+ frame.contentWindow.onpagehide = t.step_func(function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during pagehide");
+ });
+ frame.contentDocument.onvisibilitychange = t.step_func(function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during visibilitychange");
+ });
+ frame.contentWindow.onbeforeunload = t.step_func(function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during beforeunload");
+ });
+ frame.contentWindow.onunload = t.step_func(function(evt) {
+ assert_equals(frame.contentWindow.open('', '', 'height=1,width=1'), null,
+ "expected no popup during unload");
+ });
+
+ frame.onload = t.step_func_done();
+
+ frame.contentWindow.location.href = "about:blank";
+}, 'no popups with frame navigation');
+
+async_test(function(t) {
+ var test_window = window.open('', '', 'height=1,width=1');
+ var test_document = test_window.document;
+
+ var frame = test_document.createElement('iframe');
+ test_document.body.appendChild(frame);
+
+ frame.contentWindow.onpagehide = t.step_func(function(evt) {
+ assert_equals(test_window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during pagehide");
+ });
+ frame.contentDocument.onvisibilitychange = t.step_func(function(evt) {
+ assert_equals(test_window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during visibilitychange");
+ });
+ frame.contentWindow.onbeforeunload = t.step_func(function(evt) {
+ assert_equals(test_window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during beforeunload");
+ });
+ frame.contentWindow.onunload = t.step_func(function(evt) {
+ assert_equals(test_window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during unload");
+ });
+
+ frame.onload = t.step_func_done();
+
+ frame.contentWindow.location.href = "about:blank";
+}, 'no popups from synchronously reachable window');
+
+async_test(function(t) {
+ var test_window = window.open('', '', 'height=1,width=1');
+ var test_document = test_window.document;
+
+ var frame = test_document.createElement('iframe');
+ test_document.body.appendChild(frame);
+
+ frame.contentWindow.onpagehide = t.step_func(function(evt) {
+ assert_equals(window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during pagehide");
+ });
+ frame.contentDocument.onvisibilitychange = t.step_func(function(evt) {
+ assert_equals(window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during visibilitychange");
+ });
+ frame.contentWindow.onbeforeunload = t.step_func(function(evt) {
+ assert_equals(window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during beforeunload");
+ });
+ frame.contentWindow.onunload = t.step_func(function(evt) {
+ assert_equals(window.open('', '', 'height=1,width=1'), null,
+ "expected no popup during unload");
+ });
+
+ frame.onload = t.step_func_done();
+
+ frame.contentWindow.location.href = "about:blank";
+}, 'no popups from another synchronously reachable window');
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-1.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-1.html
new file mode 100644
index 0000000000..7dd48b41c2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-1.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<p>Now open a new tab and navigate to <a href="001-2.html">001-2</a></p>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-2.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-2.html
new file mode 100644
index 0000000000..b1413861a3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001-2.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<script>
+var result = "FAIL";
+if (opener != null) {
+ result = "FAIL (did you open this page in a new tab?)";
+} else {
+ var w = window.open("", "test_name");
+ if (w.location.href !== "about:blank") {
+ result = "FAIL (didn't open an about:blank browsing context)";
+ } else {
+ w.close();
+ result = "PASS";
+ }
+ document.write(result);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001.html
new file mode 100644
index 0000000000..7b0f21ec04
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/001.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Accessing named windows from outside the unit of related browsing contexts</title>
+<a href="001-1.html" target="test_name">Click here</a>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-1.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-1.html
new file mode 100644
index 0000000000..0e210f351b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-1.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<p>Now open a new tab and navigate to <a></a></p>
+<script>
+href = window.location.href.replace("http://", "http://www.").replace("002-1.html", "002-2.html");
+var a = document.getElementsByTagName("a")[0];
+a.href = href;
+a.textContent = href;
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-2.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-2.html
new file mode 100644
index 0000000000..b1413861a3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002-2.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<script>
+var result = "FAIL";
+if (opener != null) {
+ result = "FAIL (did you open this page in a new tab?)";
+} else {
+ var w = window.open("", "test_name");
+ if (w.location.href !== "about:blank") {
+ result = "FAIL (didn't open an about:blank browsing context)";
+ } else {
+ w.close();
+ result = "PASS";
+ }
+ document.write(result);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002.html
new file mode 100644
index 0000000000..b568ae8d48
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/non_automated/002.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Accessing different-origin named windows from outside the unit of related browsing contexts</title>
+<a href="002-1.html" target="test_name">Click here</a>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-is-popup-condition.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-is-popup-condition.html
new file mode 100644
index 0000000000..4f52e2e8de
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-is-popup-condition.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: condition for is popup</title>
+<meta name="variant" content="?single-1">
+<meta name="variant" content="?single-2">
+<meta name="variant" content="?position">
+<meta name="variant" content="?combination">
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var windowURL = 'resources/is-popup-barprop.html';
+
+var target = document.location.search.substring(1);
+
+// features, visible
+// NOTE: visible == !isPopup
+var tests = {
+ "single-1": [
+ // Empty feature results in non-popup.
+ [undefined, true],
+
+ // The explicit popup feature.
+ ["popup", false],
+ ["popup=1", false],
+ ["popup=true", false],
+ ["popup=0", true],
+
+ // Other feature alone results in popup.
+ ["location", false],
+ ["location=yes", false],
+ ["location=true", false],
+ ["location=no", false],
+
+ ["toolbar", false],
+ ["toolbar=yes", false],
+ ["toolbar=true", false],
+ ["toolbar=no", false],
+
+ ["menubar", false],
+ ["menubar=yes", false],
+ ["menubar=true", false],
+ ["menubar=no", false],
+
+ ["resizable", false],
+ ["resizable=yes", false],
+ ["resizable=true", false],
+ ["resizable=no", false],
+ ],
+ "single-2": [
+ ["scrollbars", false],
+ ["scrollbars=yes", false],
+ ["scrollbars=true", false],
+ ["scrollbars=no", false],
+
+ ["status", false],
+ ["status=yes", false],
+ ["status=true", false],
+ ["status=no", false],
+
+ ["titlebar", false],
+ ["titlebar=yes", false],
+ ["titlebar=true", false],
+ ["titlebar=no", false],
+
+ ["close", false],
+ ["close=yes", false],
+ ["close=true", false],
+ ["close=no", false],
+
+ ["minimizable", false],
+ ["minimizable=yes", false],
+ ["minimizable=true", false],
+ ["minimizable=no", false],
+
+ ["personalbar", false],
+ ["personalbar=yes", false],
+ ["personalbar=true", false],
+ ["personalbar=no", false],
+ ],
+ "position": [
+ ["left=500", false],
+ ["screenX=500", false],
+
+ ["top=500", false],
+ ["screenY=500", false],
+
+ ["width=500", false],
+ ["innerWidth=500", false],
+
+ ["outerWidth=500", false],
+
+ ["height=500", false],
+ ["innerHeight=500", false],
+
+ ["outerHeight=500", false],
+ ],
+ "combination": [
+ // The following combination results in non-popup.
+ ["location,toolbar,menubar,resizable,scrollbars,status", true],
+
+ // Either location or toolbar is required for non-popup.
+ ["location,menubar,resizable,scrollbars,status", true],
+ ["toolbar,menubar,resizable,scrollbars,status", true],
+
+ ["resizable,scrollbars,status", false],
+ ["location=no,menubar=no,resizable,scrollbars,status", false],
+
+ // menubar is required for non-popup.
+ ["location,toolbar,resizable,scrollbars,status", false],
+
+ // resizable is required for non-popup, but defaults to true
+ ["location,toolbar,menubar,scrollbars,status", true],
+ ["location,toolbar,menubar,resizable=no,scrollbars,status", false],
+
+ // scrollbars is required for non-popup.
+ ["location,toolbar,menubar,resizable,status", false],
+
+ // status is required for non-popup.
+ ["location,toolbar,menubar,resizable,scrollbars", false],
+
+ // The explicit popup feature has priority than others.
+ ["popup=1,location,toolbar,menubar,resizable,scrollbars,status", false],
+ ["popup=yes,location,toolbar,menubar,resizable,scrollbars,status", false],
+ ["popup=true,location,toolbar,menubar,resizable,scrollbars,status", false],
+ ["popup=0,location,toolbar,menubar,resizable,scrollbars", true],
+ ],
+};
+
+tests[target].forEach(([features, visible]) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.locationbar, visible, `window.locationbar.visible`);
+ assert_equals(data.menubar, visible, `window.menubar.visible`);
+ assert_equals(data.personalbar, visible, `window.personalbar.visible`);
+ assert_equals(data.scrollbars, visible, `window.scrollbars.visible`);
+ assert_equals(data.statusbar, visible, `window.statusbar.visible`);
+ assert_equals(data.toolbar, visible, `window.toolbar.visible`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL), '', features);
+ }, `${format_value(features)} should set BarProp visibility to ${visible}`);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-innerwidth-innerheight.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-innerwidth-innerheight.html
new file mode 100644
index 0000000000..019ee9d730
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-innerwidth-innerheight.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: negative values for legacy `innerwidth`, `innerheight`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var windowURL = 'resources/message-opener.html';
+var featuresPrefix = `top=0,left=0,`;
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the features tested here were set to invalid
+ // values in later tests.
+ // In cases where the value for `innerwidth` or `innerheight` is
+ // is less than the browser's minimum allowed value for that dimension,
+ // but NOT 0, the value affected will become the browser's minimum allowed value.
+
+ // This should result in a minimally-sized window for later comparison
+ var featureString = `${featuresPrefix}innerwidth=1,innerheight=1`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+
+ // Negative values for `innerwidth` should result in a window with minimum
+ // valid allowed width
+ [ 'innerwidth=-404',
+ 'innerwidth=-404.5',
+ 'innerwidth=-404e1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}height=405,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, baselineDimensions.width, `"${feature} is negative and should result in a minimally-wide window"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + "&expected_innerWidth=" + baselineDimensions.width, '', featureString);
+ }, `features "${feature}" should NOT set "width=404"`);
+ });
+
+ // Negative values for `innerheight` should result in a window with minimum
+ // valid allowed height
+ [ 'innerheight=-404',
+ 'innerheight=-404.5',
+ 'innerheight=-404e1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}width=404,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, baselineDimensions.height, `"${feature} is negative and should result in a minimal-height window"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=' + baselineDimensions.height, '', featureString);
+ }, `features "${feature}" should NOT set "height=404"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-screenx-screeny.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-screenx-screeny.html
new file mode 100644
index 0000000000..6e316db482
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-screenx-screeny.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: negative values for legacy `screenx`, `screeny`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `width=401,height=404,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}top=0,left=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // Negative values should be interpreted as 0
+ [ 'screeny=-204',
+ 'screeny=-204.5',
+ 'screeny=-0'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}left=0,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, baselineDimensions.top, `"${feature} is negative and should be set to 0"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=' + baselineDimensions.top, '', featureString);
+ }, `features "${feature}" should NOT set "top=204"`);
+ });
+
+ // Negative values should be interpreted as 0
+ [ 'screenx=-204',
+ 'screenx=-204.5',
+ 'screenx=-0'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}top=0,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, baselineDimensions.left, `"${feature} is negative and should be set to 0"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=' + baselineDimensions.left, '', featureString);
+ }, `features "${feature}" should NOT set "left=204"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-top-left.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-top-left.html
new file mode 100644
index 0000000000..316b01a401
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-top-left.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: negative values for `top`, `left`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `width=401,height=404,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}top=0,left=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+
+ // Negative values for `top`, `left` should be interpreted as 0
+ [ 'top=-204',
+ 'top=-204.5',
+ 'top=-0'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}left=0,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, baselineDimensions.top, `"${feature} is negative and should be set to 0"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=' + baselineDimensions.top, '', featureString);
+ }, `features "${feature}" should NOT set "top=204"`);
+ });
+
+// Negative values for `top`, `left` should be interpreted as 0
+ [ 'left=-204',
+ 'left=-204.5',
+ 'left=-0'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}top=0,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, baselineDimensions.left, `"${feature} is negative and should be set to 0"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=' + baselineDimensions.left, '', featureString);
+ }, `features "${feature}" should NOT set "left=204"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-width-height.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-width-height.html
new file mode 100644
index 0000000000..3cb155620d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-negative-width-height.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: negative values for `width`, `height`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `top=0,left=0,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the features tested here were set to invalid
+ // values in later tests.
+ // In cases where the value for `width` or `height` is
+ // is less than the browser's minimum allowed value for that dimension,
+ // but NOT 0, the value affected will become the browser's minimum allowed value.
+
+ // This should result in a minimally-sized window for later comparison
+ var featureString = `${featuresPrefix}width=1,height=1`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+
+ // Negative values for `width` should result in a window with minimum
+ // valid allowed width
+ [ 'width=-404',
+ 'width=-404.5',
+ 'width=-404e1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}height=405,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, baselineDimensions.width, `"${feature} is negative and should result in a minimally-wide window"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerWidth=' + baselineDimensions.width, '', featureString);
+ }, `features "${feature}" should NOT set "width=404"`);
+ });
+
+ // Negative values for `height` should result in a window with minimum
+ // valid allowed height
+ [ 'height=-404',
+ 'height=-404.5',
+ 'height=-404e1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}width=404,${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, baselineDimensions.height, `"${feature} is negative and should result in a minimal-height window"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=' + baselineDimensions.height, '', featureString);
+ }, `features "${feature}" should NOT set "height=404"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-height.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-height.html
new file mode 100644
index 0000000000..d8ee866c50
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-height.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for feature `height`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `top=0,left=0,width=401,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}height=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // The absence of the sizing feature should have the same behavior
+ // as that feature set to 0
+ [ featuresPrefix,
+ 'top=0,left=0',
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, baselineDimensions.height);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=' + baselineDimensions.height, '', feature);
+ }, `${feature}: absence of feature "height" should be treated same as "height=0"`);
+ });
+
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'height=/404',
+ 'height=_404',
+ 'height=L404'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, baselineDimensions.height, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=' + baselineDimensions.height, '', featureString);
+ }, `features "${feature}" should NOT set "height=404"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'height=405.5',
+ 'height=405.32',
+ 'height=405LLl',
+ 'height=405^4',
+ 'height=405*3',
+ 'height=405/5',
+ 'height=405 ',
+ 'height=405e1',
+ 'height=405e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, 405, `"${featureString} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + "&expected_innerHeight=405", '', featureString);
+ }, `features "${feature}" should set "height=405"`);
+ });
+
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerheight.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerheight.html
new file mode 100644
index 0000000000..f191d875d8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerheight.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for legacy feature `innerheight`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `top=0,left=0,width=401,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}height=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'innerheight=/404',
+ 'innerheight=_404',
+ 'innerheight=L404'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, baselineDimensions.height, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=' + baselineDimensions.height, '', featureString);
+ }, `features "${feature}" should NOT set "height=404"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'innerheight=405.5',
+ 'innerheight=405.32',
+ 'innerheight=405LLl',
+ 'innerheight=405^4',
+ 'innerheight=405*3',
+ 'innerheight=405/5',
+ 'innerheight=405 ',
+ 'innerheight=405e1',
+ 'innerheight=405e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, 405, `"${featureString} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + "&expected_innerHeight=405", '', featureString);
+ }, `features "${feature}" should set "height=405"`);
+ });
+
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html
new file mode 100644
index 0000000000..d1ddc5e43a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for legacy feature `innerwidth`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `top=0,left=0,height=401,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}width=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'innerwidth=/404',
+ 'innerwidth=_404',
+ 'innerwidth=L404'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, baselineDimensions.width, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerWidth=' + baselineDimensions.width, '', featureString);
+ }, `features "${feature}" should NOT set "width=404"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'innerwidth=405.5',
+ 'innerwidth=405.32',
+ 'innerwidth=405LLl',
+ 'innerwidth=405^4',
+ 'innerwidth=405*3',
+ 'innerwidth=405/5',
+ 'innerwidth=405 ',
+ 'innerwidth=405e1',
+ 'innerwidth=405e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, 405, `"${featureString} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerWidth=405', '', featureString);
+ }, `features "${feature}" should set "width=405"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-left.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-left.html
new file mode 100644
index 0000000000..c771204dc4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-left.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for feature `left`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+
+var featuresPrefix = `width=401,height=204,top=0,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}left=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'left=/104',
+ 'left=_104',
+ 'left=L104'
+ ].forEach(feature => {
+ async_test(t => {
+ var featureString = `${featuresPrefix}${feature}`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, baselineDimensions.left, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=' + baselineDimensions.left, '', featureString);
+ }, `features "${feature}" should NOT set "left=104"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'left=105.5',
+ 'left=105.32',
+ 'left=105LLl',
+ 'left=105^4',
+ 'left=105*3',
+ 'left=105/5',
+ 'left=105 ',
+ 'left=105e1',
+ 'left=105e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var featureString = `${featuresPrefix}${feature}`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, 105, `"${featureString} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=105', '', featureString);
+ }, `features "${feature}" should set "left=105"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screenx.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screenx.html
new file mode 100644
index 0000000000..49a3783257
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screenx.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for legacy feature `screenx`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `width=401,height=204,top=0,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}left=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'screenx=/104',
+ 'screenx=_104',
+ 'screenx=L104'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, baselineDimensions.left, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=' + baselineDimensions.left, '', featureString);
+ }, `features "${feature}" should NOT set "screenx=104"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'screenx=105.5',
+ 'screenx=105.32',
+ 'screenx=105LLl',
+ 'screenx=105^4',
+ 'screenx=105*3',
+ 'screenx=105/5',
+ 'screenx=105 ',
+ 'screenx=105e1',
+ 'screenx=105e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, 105, `"${featureString} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=105', '', featureString);
+ }, `features "${feature}" should set "screenx=105"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screeny.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screeny.html
new file mode 100644
index 0000000000..5183405445
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-screeny.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for legacy feature `screeny`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `height=401,width=402,left=0,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}top=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'screeny=/404',
+ 'screeny=_404',
+ 'screeny=L404'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, baselineDimensions.top, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=' + baselineDimensions.top, '', featureString);
+ }, `features "${feature}" should NOT set "screeny=404"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'screeny=405.5',
+ 'screeny=405.32',
+ 'screeny=405LLl',
+ 'screeny=405^4',
+ 'screeny=405*3',
+ 'screeny=405/5',
+ 'screeny=405 ',
+ 'screeny=405e1',
+ 'screeny=405e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, 405, `"${featureString} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=405', '', featureString);
+ }, `features "${feature}" should set "screeny=405"`);
+ });
+
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-top.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-top.html
new file mode 100644
index 0000000000..a7926e748b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-top.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for feature `top`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var windowURL = 'resources/message-opener.html';
+var featuresPrefix = `width=401,height=204,left=0,`;
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}top=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'top=/104',
+ 'top=_104',
+ 'top=L104'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, baselineDimensions.top, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=' + baselineDimensions.top, '', featureString);
+ }, `features "${feature}" should NOT set "top=104"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'top=105.5',
+ 'top=105.32',
+ 'top=105LLl',
+ 'top=105^4',
+ 'top=105*3',
+ 'top=105/5',
+ 'top=105 ',
+ 'top=105e1',
+ 'top=105e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var featureString = `${featuresPrefix}${feature}`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, 105, `"${feature} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=105', '', featureString);
+ }, `features "${feature}" should set "top=105"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-width.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-width.html
new file mode 100644
index 0000000000..6878063ed7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-non-integer-width.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: non-integer values for feature `width`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var featuresPrefix = `top=0,left=0,height=401,`;
+var windowURL = 'resources/message-opener.html';
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-integers
+
+setup (() => {
+ // Before running tests, open a window using features that mimic
+ // what would happen if the feature tested here were set to 0
+ // for comparison later.
+ var featureString = `${featuresPrefix}width=0`;
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage((data, e) => {
+ e.source.close();
+ runWindowTests(data);
+ });
+ var win = window.open(prefixedMessage.url(windowURL), '', featureString);
+});
+
+function runWindowTests (baselineDimensions) {
+
+ // The absence of the sizing feature should have the same behavior
+ // as that feature set to 0
+ [ featuresPrefix,
+ 'top=0,left=0',
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, baselineDimensions.width);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerWidth=' + baselineDimensions.width, '', feature);
+ }, `${feature}: absence of feature "width" should be treated same as "width=0"`);
+ });
+
+ // When code point in first position is not an ASCII digit, "+" or "-",
+ // that's an error and the value becomes 0
+ [ 'width=/404',
+ 'width=_404',
+ 'width=L404'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, baselineDimensions.width, `"${feature} begins with an invalid character and should be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerWidth=' + baselineDimensions.width, '', featureString);
+ }, `features "${feature}" should NOT set "width=404"`);
+ });
+
+ // Codepoints that are valid ASCII digits should be collected
+ // Non-ASCII digits and subsequent code points are ignored
+ [ 'width=405.5',
+ 'width=405.32',
+ 'width=405LLl',
+ 'width=405^4',
+ 'width=405*3',
+ 'width=405/5',
+ 'width=405 ',
+ 'width=405e1',
+ 'width=405e-1'
+ ].forEach(feature => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ var featureString = `${featuresPrefix}${feature}`;
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, 405, `"${featureString} value after first non-digit will be ignored"`);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + "&expected_innerWidth=405", '', featureString);
+ }, `features "${feature}" should set "width=405"`);
+ });
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-innerheight-innerwidth.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-innerheight-innerwidth.html
new file mode 100644
index 0000000000..cf3ad25514
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-innerheight-innerwidth.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: tokenization -- legacy size features `innerheight`, `innerwidth`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var windowURL = 'resources/message-opener.html';
+var width = 'width=401,';
+var height = 'height=402,';
+
+[ 'innerwidth=401',
+ ' innerwidth = 401',
+ 'innerwidth==401',
+ '\ninnerwidth= 401',
+ ',innerwidth=401,,',
+ 'INNERWIDTH=401',
+ 'innerWidth=401'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, 401);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerWidth=401', '', height + features);
+ }, `${format_value(features)} should set width of opened window`);
+});
+
+[ 'innerheight=402',
+ ' innerheight = 402',
+ 'innerheight==402',
+ '\ninnerheight= 402',
+ ',innerheight=402,,',
+ 'INNERHEIGHT=402',
+ 'innerHeight=402'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, 402);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=402', '', width + features);
+ }, `${format_value(features)} should set height of opened window`);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noopener.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noopener.html
new file mode 100644
index 0000000000..c955e67789
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noopener.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: tokenization -- `noopener`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/tokenization-noopener-noreferrer.js"></script>
+<script>
+ booleanTests("noopener");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noreferrer.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noreferrer.html
new file mode 100644
index 0000000000..4807f634fd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-noreferrer.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: tokenization -- `noreferrer`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/tokenization-noopener-noreferrer.js"></script>
+<script>
+ booleanTests("noreferrer");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-screenx-screeny.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-screenx-screeny.html
new file mode 100644
index 0000000000..5a53fefc40
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-screenx-screeny.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: tokenization -- legacy position features `screenx`, `screeny`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var windowURL = 'resources/message-opener.html';
+var width = 'width=401,';
+var height = 'height=402,';
+
+[ 'screenx=141',
+ ' screenx = 141',
+ 'screenx==141',
+ '\nscreenx= 141',
+ ',screenx=141,,',
+ 'SCREENX=141',
+ 'screenX=141'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, 141);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=141', '', width + height + features);
+ }, `${format_value(features)} should set left position of opened window`);
+});
+
+[ 'screeny=142',
+ ' screeny = 142',
+ 'screeny==142',
+ '\nscreeny= 142',
+ ',screeny=142,,',
+ 'SCREENY=142',
+ 'screenY=142'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, 142);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=142', '', width + height + features);
+ }, `${format_value(features)} should set top position of opened window`);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-top-left.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-top-left.html
new file mode 100644
index 0000000000..842cbcf820
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-top-left.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: tokenization -- position features `top` and `left`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var windowURL = 'resources/message-opener.html';
+var width = 'width=401,';
+var height = 'height=402,';
+
+[ 'left=141',
+ ' left = 141',
+ 'left==141',
+ '\nleft= 141',
+ ',left=141,,',
+ 'LEFT=141'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.left, 141);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=141', '', width + height + features);
+ }, `"${features}" should set left position of opened window`);
+});
+
+[ 'top=142',
+ ' top = 142',
+ 'top==142',
+ '\ttop= 142',
+ ',top=142,,',
+ 'TOP=142'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, 142);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenY=142', '', width + height + features);
+ }, `${format_value(features)} should set top position of opened window`);
+});
+
+[ 'top=152,left=152',
+ 'top=152,,left=152,',
+ 'top=152==left=152',
+ ',,top= 152, left=152'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.top, 152);
+ assert_equals(data.left, 152);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_screenX=152&expected_screenY=152', '', width + height + features);
+ }, `${format_value(features)} should set top and left position of opened window`);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-width-height.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-width-height.html
new file mode 100644
index 0000000000..ff61199179
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/open-features-tokenization-width-height.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML: window.open `features`: tokenization -- size features `width` and `height`</title>
+<meta name=timeout content=long>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#apis-for-creating-and-navigating-browsing-contexts-by-name">
+
+<!-- user agents are not required to support open features other than `noopener`
+ and on some platforms position and size features don't make sense -->
+<meta name="flags" content="may" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var windowURL = 'resources/message-opener.html';
+var width = 'width=401,';
+var height = 'height=402,';
+
+[ 'width=401',
+ ' width = 401',
+ 'width==401',
+ '\nwidth= 401',
+ ',width=401,,',
+ 'WIDTH=401'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.width, 401);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerWidth=401', '', height + features);
+ }, `${format_value(features)} should set width of opened window`);
+});
+
+[ 'height=402',
+ ' height = 402',
+ 'height==402',
+ '\nheight= 402',
+ ',height=402,,',
+ 'HEIGHT=402'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, 402);
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=402', '', width + features);
+ }, `${format_value(features)} should set height of opened window`);
+});
+
+[ 'height=402,width=401',
+ ' height = 402 , width = 401 ,',
+ 'height==402 width = 401',
+ '\nheight= 402,,width=\n401',
+ ',height=402,,width==401',
+ 'HEIGHT=402, WIDTH=401'
+].forEach((features, idx, arr) => {
+ async_test(t => {
+ var prefixedMessage = new PrefixedMessageTest();
+ prefixedMessage.onMessage(t.step_func_done((data, e) => {
+ e.source.close();
+ assert_equals(data.height, 402);
+ assert_equals(data.width, 401)
+ }));
+ var win = window.open(prefixedMessage.url(windowURL) + '&expected_innerHeight=402&expected_innerWidth=401', '', features);
+ }, `${format_value(features)} should set height and width of opened window`);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/close-self.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/close-self.html
new file mode 100644
index 0000000000..0c0cf9fc49
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/close-self.html
@@ -0,0 +1,3 @@
+<script>
+ window.close();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/is-popup-barprop.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/is-popup-barprop.html
new file mode 100644
index 0000000000..5636e29878
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/is-popup-barprop.html
@@ -0,0 +1,15 @@
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var prefixedMessage = new PrefixedMessageResource();
+function sendBarProps() {
+ prefixedMessage.postToOpener({
+ locationbar: window.locationbar.visible,
+ menubar: window.menubar.visible,
+ personalbar: window.personalbar.visible,
+ scrollbars: window.scrollbars.visible,
+ statusbar: window.statusbar.visible,
+ toolbar: window.toolbar.visible,
+ });
+}
+window.addEventListener('load', sendBarProps);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/message-opener.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/message-opener.html
new file mode 100644
index 0000000000..39ad139769
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/message-opener.html
@@ -0,0 +1,52 @@
+<script src="/common/PrefixedPostMessage.js"></script>
+<script>
+var prefixedMessage = new PrefixedMessageResource();
+var max = 150, attempts = 0;
+
+const urlParams = new URLSearchParams(location.search);
+const expected_innerWidth = urlParams.get('expected_innerWidth');
+const expected_innerHeight = urlParams.get('expected_innerHeight');
+const expected_screenX = urlParams.get('expected_screenX');
+const expected_screenY = urlParams.get('expected_screenY');
+let should_wait_until_settled = expected_innerWidth === null && expected_innerHeight === null && expected_screenX === null && expected_screenY === null;
+
+function sendCoordinates() {
+ // Certain windowing systems position windows asynchronously.
+ // As a result, the window may not be positioned yet when the
+ // load event fires. To accommodate this, allow waiting up to
+ // 15 seconds for positioning to take place.
+ if ((!window.screenX && expected_screenX) &&
+ (!window.screenY && expected_screenY) && ++attempts < max) {
+ setTimeout(sendCoordinates, 100);
+ return;
+ }
+ if (expected_innerWidth && window.innerWidth != expected_innerWidth && ++attempts < max) {
+ setTimeout(sendCoordinates, 10);
+ return;
+ }
+ if (expected_innerHeight && window.innerHeight != expected_innerHeight && ++attempts < max) {
+ setTimeout(sendCoordinates, 10);
+ return;
+ }
+ if (expected_screenX && window.screenX != expected_screenX && ++attempts < max) {
+ setTimeout(sendCoordinates, 10);
+ return;
+ }
+ if (expected_screenY && window.screenY != expected_screenY && ++attempts < max) {
+ setTimeout(sendCoordinates, 10);
+ return;
+ }
+ if (should_wait_until_settled) {
+ should_wait_until_settled = false;
+ setTimeout(sendCoordinates, 300);
+ return;
+ }
+ prefixedMessage.postToOpener({
+ left: window.screenX,
+ top: window.screenY,
+ width: window.innerWidth,
+ height: window.innerHeight
+ });
+}
+window.addEventListener('load', sendCoordinates);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/tokenization-noopener-noreferrer.js b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/tokenization-noopener-noreferrer.js
new file mode 100644
index 0000000000..a9d42e26de
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/resources/tokenization-noopener-noreferrer.js
@@ -0,0 +1,152 @@
+function booleanTests(feature) {
+ const windowURL = 'resources/close-self.html';
+ // Tests for how windows features are tokenized into 'name', 'value'
+ // window features separators are ASCII whitespace, '=' and ','
+
+ const featureUpper = feature.toUpperCase(),
+ featureSplitBegin = feature.slice(0, 2),
+ featureSplitEnd = feature.slice(2),
+ featureMixedCase = featureSplitBegin.toUpperCase() + featureSplitEnd;
+ featureMixedCase2 = featureSplitBegin + featureSplitEnd.toUpperCase();
+
+ test (t => {
+ // Tokenizing `name`: initial window features separators are ignored
+ // Each of these variants should tokenize to (`${feature}`, '')
+ [
+ ` ${feature}`,
+ `=${feature}`,
+ `,,${feature}`,
+ `,=, ${feature}`,
+ `\n=${feature}=`,
+ `\t${feature}`,
+ `\r,,,=${feature}`,
+ `\u000C${feature}`
+ ].forEach(variant => {
+ const win = window.open(windowURL, "", variant);
+ assert_equals(win, null, `"${variant}" should activate feature "${feature}"`);
+ });
+ }, `Tokenization of "${feature}" should skip window features separators before feature`);
+
+ test (t => {
+ // Tokenizing `name`: lowercase conversion
+ // Each of these variants should tokenize as feature (`${feature}`, '')
+ // except where indicated
+ // Note also that `value` is lowercased during tokenization
+ [
+ `${featureUpper}`,
+ `${featureMixedCase}`,
+ ` ${featureMixedCase2}`,
+ `=${featureUpper}`,
+ `${featureUpper}=1`,
+ `${featureUpper}=1`,
+ `${featureUpper}=yes`,
+ `${feature}=YES`,
+ ].forEach(variant => {
+ const win = window.open(windowURL, '', variant);
+ assert_equals(win, null, `"${variant}" should activate feature "${feature}"`);
+ });
+ }, `Feature "${feature}" should be converted to ASCII lowercase`);
+
+ test (t => {
+ // After `name` has been collected, ignore any window features separators until '='
+ // except ',' OR a non-window-features-separator — break in those cases
+ // i.e. ignore whitespace until '=' unless a ',' is encountered first
+ // Each of these variants should tokenize as feature ('noopener', '')
+ [
+ `${feature}`,
+ ` ${feature}\r`,
+ `${feature}\n =`,
+ `${feature},`,
+ `${feature} =,`,
+ `, ${feature} =`,
+ `${feature},=`,
+ `${feature} foo`,
+ `foo ${feature}=1`,
+ `foo=\u000Cbar\u000C${feature}`
+ ].forEach(variant => {
+ const win = window.open(windowURL, '', variant);
+ assert_equals(win, null, `"${variant}" should activate feature "${feature}"`);
+ });
+ }, `After "${feature}", tokenization should skip window features separators that are not "=" or ","`);
+
+ test (t => {
+ // After initial '=', tokenizing should ignore all separators except ','
+ // before collecting `value`
+ // Each of these variants should tokenize as feature ('noopener', '')
+ // Except where indicated
+ [
+ `${feature}= yes`,
+ `${feature}==,`,
+ `${feature}=\n ,`,
+ `${feature} = \t ,`,
+ `${feature}\n=\r 1,`,
+ `${feature}=,yes`,
+ `${feature}= yes=,`,
+ `${feature} = \u000Cyes`
+ ].forEach(variant => {
+ const win = window.open(windowURL, '', variant);
+ assert_equals(win, null, `"${variant}" should activate feature "${feature}"`);
+ });
+ }, `Tokenizing "${feature}" should ignore window feature separators except "," after initial "=" and before value`);
+
+ test (t => {
+ // Tokenizing `value` should collect any non-separator code points until first separator
+ [
+ `${feature}=1`,
+ `${feature}=yes`,
+ `${feature} = yes ,`,
+ `${feature}=\nyes ,`,
+ `${feature}=yes yes`,
+ `${feature}=yes\ts`,
+ `${feature}==`,
+ `${feature}=1\n,`,
+ `==${feature}===`,
+ `${feature}==\u000C`
+ ].forEach(variant => {
+ const win = window.open(windowURL, '', variant);
+ assert_equals(win, null, `"${variant}" should set "${feature}"`);
+ });
+ }, `Tokenizing "${feature}" should read characters until first window feature separator as \`value\``);
+
+ test (t => {
+ [
+ `${feature}=1`,
+ `${feature}=2`,
+ `${feature}=12345`,
+ `${feature}=1.5`,
+ `${feature}=-1`,
+ ].forEach(variant => {
+ const win = window.open(windowURL, '', variant);
+ assert_equals(win, null, `"${variant}" should activate feature "${feature}"`);
+ });
+ }, 'Integer values other than 0 should activate the feature');
+
+ test (t => {
+ [
+ `${feature}=0`,
+ `${feature}=0.5`,
+ `${feature}=error`,
+ ].forEach(variant => {
+ const win = window.open(windowURL, '', variant);
+ assert_not_equals(win, null, `"${variant}" should NOT activate feature "${feature}"`);
+ });
+ }, `Integer value of 0 should not activate "${feature}"`);
+
+ test (t => {
+ [
+ `-${feature}`,
+ `${featureUpper}RRR`,
+ `${featureMixedCase}R`,
+ `${featureSplitBegin}_${featureSplitEnd}`,
+ ` ${featureSplitBegin} ${featureSplitEnd}`,
+ `${featureSplitBegin}\n${featureSplitEnd}`,
+ `${featureSplitBegin},${featureSplitEnd}`,
+ `\0${feature}`,
+ `${feature}\u0000=yes`,
+ `foo=\u000C${feature}`
+ ].forEach(variant => {
+ const win = window.open(windowURL, '', variant);
+ assert_not_equals(win, null, `"${variant}" should NOT activate feature "${feature}"`);
+ });
+ }, `Invalid feature names should not tokenize as "${feature}"`);
+}
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/proxy-getOwnPropertyDescriptor.html b/testing/web-platform/tests/html/browsers/the-window-object/proxy-getOwnPropertyDescriptor.html
new file mode 100644
index 0000000000..4ea9504870
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/proxy-getOwnPropertyDescriptor.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>getOwnPropertyDescriptor() is correct for Proxy with host object target</title>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+ <link rel="help" href="https://webidl.spec.whatwg.org/#Unforgeable">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+'use strict';
+
+const assert_accessor_descriptor_equals = (actual, expected) => {
+ assert_equals(actual.get, expected.get, 'get');
+ assert_equals(actual.set, expected.set, 'set');
+ assert_equals(actual.enumerable, expected.enumerable, 'enumerable');
+ assert_equals(actual.configurable, expected.configurable, 'configurable');
+};
+
+const assert_data_descriptor_equals = (actual, expected) => {
+ assert_equals(actual.value, expected.value, 'value');
+ assert_equals(actual.writable, expected.writable, 'writable');
+ assert_equals(actual.enumerable, expected.enumerable, 'enumerable');
+ assert_equals(actual.configurable, expected.configurable, 'configurable');
+};
+
+test(() => {
+ const windowProxy = new Proxy(window, {});
+ name = 'old_name';
+ const descriptor = Object.getOwnPropertyDescriptor(windowProxy, 'name');
+
+ assert_equals(descriptor.get.call(window), 'old_name');
+ descriptor.set.call(window, 'new_name');
+ assert_equals(name, 'new_name');
+ assert_true(descriptor.enumerable);
+ assert_true(descriptor.configurable);
+}, 'Window target, no trap, "name" attribute');
+
+test(() => {
+ let trapCalls = 0;
+ const windowProxy = new Proxy(window, {
+ getOwnPropertyDescriptor(...args) {
+ trapCalls++;
+ return Reflect.getOwnPropertyDescriptor(...args);
+ },
+ });
+
+ assert_accessor_descriptor_equals(
+ Object.getOwnPropertyDescriptor(windowProxy, 'document'),
+ Object.getOwnPropertyDescriptor(window, 'document')
+ );
+ assert_equals(trapCalls, 1);
+}, 'Window target, forwarding trap, [LegacyUnforgeable] "document" attribute');
+
+test(() => {
+ const trapResult = {get() {}, set(_val) {}, enumerable: false, configurable: true};
+ const windowProxy = new Proxy(new Proxy(window, {}), {
+ getOwnPropertyDescriptor: () => trapResult,
+ });
+
+ assert_accessor_descriptor_equals(
+ Object.getOwnPropertyDescriptor(windowProxy, 'onclick'),
+ trapResult
+ );
+}, 'Window proxy target, custom trap, "onclick" event handler attribute');
+
+test(() => {
+ let trapCalls = 0;
+ const documentProxy = new Proxy(document, {
+ getOwnPropertyDescriptor(...args) {
+ trapCalls++;
+ return Reflect.getOwnPropertyDescriptor(...args);
+ },
+ });
+
+ assert_accessor_descriptor_equals(
+ Object.getOwnPropertyDescriptor(documentProxy, 'location'),
+ Object.getOwnPropertyDescriptor(document, 'location')
+ );
+ assert_equals(trapCalls, 1);
+}, 'Document target, forwarding trap, [LegacyUnforgeable] "location" attribute');
+
+test(() => {
+ const trapResult = {value: 4, writable: false, enumerable: true, configurable: true};
+ const documentProxy = new Proxy(new Proxy(document, {}), {
+ getOwnPropertyDescriptor: () => trapResult,
+ });
+
+ assert_data_descriptor_equals(
+ Object.getOwnPropertyDescriptor(documentProxy, 'foo'),
+ trapResult
+ );
+}, 'Document proxy target, custom trap, non-existent value attribute');
+
+test(() => {
+ const locationProxy = new Proxy(location, {});
+ location.hash = '#old';
+ const descriptor = Object.getOwnPropertyDescriptor(locationProxy, 'hash');
+
+ assert_equals(descriptor.get.call(location), '#old');
+ descriptor.set.call(location, '#new');
+ assert_equals(location.hash, '#new');
+ assert_true(descriptor.enumerable);
+ assert_false(descriptor.configurable);
+}, 'Location target, no trap, [LegacyUnforgeable] "hash" attribute');
+
+test(() => {
+ let trapCalls = 0;
+ const locationProxy = new Proxy(new Proxy(location, {}), {
+ getOwnPropertyDescriptor(...args) {
+ trapCalls++;
+ return Reflect.getOwnPropertyDescriptor(...args);
+ },
+ });
+
+ assert_data_descriptor_equals(
+ Object.getOwnPropertyDescriptor(locationProxy, 'reload'),
+ Object.getOwnPropertyDescriptor(location, 'reload')
+ );
+ assert_equals(trapCalls, 1);
+}, 'Location proxy target, forwarding trap, [LegacyUnforgeable] "reload" method');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/security-window/window-security.https.html b/testing/web-platform/tests/html/browsers/the-window-object/security-window/window-security.https.html
new file mode 100644
index 0000000000..68af5bd90b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/security-window/window-security.https.html
@@ -0,0 +1,200 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Window Security</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/browsers.html#the-window-object" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/timers.html#timers" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/webappapis.html#atob" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#windowsessionstorage" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#windowlocalstorage" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/browsers.html#window" />
+<link rel="help" href="http://dev.w3.org/csswg/cssom/#extensions-to-the-window-interface" />
+<link rel="help" href="http://dev.w3.org/csswg/cssom-view/#extensions-to-the-window-interface" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("Window Security testing");
+
+function fr_load() {
+ fr = document.getElementById("fr");
+
+ t.step(function () {
+ //SecurityError should be thrown
+ [
+ //attributes
+ {name: "devicePixelRatio"},
+ {name: "document"},
+ {name: "external"},
+ {name: "frameElement"},
+ {name: "history"},
+ {name: "innerWidth"},
+ {name: "innerHeight"},
+ {name: "locationbar"},
+ {name: "localStorage"},
+ {name: "menubar"},
+ {name: "name"},
+ {name: "navigator"},
+ {name: "onabort"},
+ {name: "onafterprint"},
+ {name: "onbeforeprint"},
+ {name: "onbeforeunload"},
+ {name: "onblur"},
+ {name: "oncancel"},
+ {name: "oncanplay"},
+ {name: "oncanplaythrough"},
+ {name: "onchange"},
+ {name: "onclick"},
+ {name: "onclose"},
+ {name: "oncontextmenu"},
+ {name: "oncuechange"},
+ {name: "ondblclick"},
+ {name: "ondrag"},
+ {name: "ondragend"},
+ {name: "ondragenter"},
+ {name: "ondragleave"},
+ {name: "ondragover"},
+ {name: "ondragstart"},
+ {name: "ondrop"},
+ {name: "ondurationchange"},
+ {name: "onemptied"},
+ {name: "onended"},
+ {name: "onerror"},
+ {name: "onfocus"},
+ {name: "onhashchange"},
+ {name: "oninput"},
+ {name: "oninvalid"},
+ {name: "onkeydown"},
+ {name: "onkeypress"},
+ {name: "onkeyup"},
+ {name: "onload"},
+ {name: "onloadeddata"},
+ {name: "onloadedmetadata"},
+ {name: "onloadstart"},
+ {name: "onmessage"},
+ {name: "onmousedown"},
+ {name: "onmousemove"},
+ {name: "onmouseout"},
+ {name: "onmouseover"},
+ {name: "onmouseup"},
+ {name: "onmousewheel"},
+ {name: "onoffline"},
+ {name: "ononline"},
+ {name: "onpause"},
+ {name: "onplay"},
+ {name: "onplaying"},
+ {name: "onpagehide"},
+ {name: "onpageshow"},
+ {name: "onpopstate"},
+ {name: "onprogress"},
+ {name: "onratechange"},
+ {name: "onreset"},
+ {name: "onresize"},
+ {name: "onscroll"},
+ {name: "onseeked"},
+ {name: "onseeking"},
+ {name: "onselect"},
+ {name: "onstalled"},
+ {name: "onstorage"},
+ {name: "onsubmit"},
+ {name: "onsuspend"},
+ {name: "ontimeupdate"},
+ {name: "onunload"},
+ {name: "onvolumechange"},
+ {name: "onwaiting"},
+ {name: "pageXOffset"},
+ {name: "pageYOffset"},
+ {name: "personalbar"},
+ {name: "screen"},
+ {name: "scrollbars"},
+ {name: "statusbar"},
+ {name: "status"},
+ {name: "screenX"},
+ {name: "screenY"},
+ {name: "sessionStorage"},
+ {name: "toolbar"},
+ //methods
+ {name: "alert", isMethod: true},
+ {name: "clearInterval", isMethod: true, args:[1]},
+ {name: "clearTimeout", isMethod: true, args:[function () {}, 1]},
+ {name: "confirm", isMethod: true},
+ {name: "getComputedStyle", isMethod: true, args:[document.body, null]},
+ {name: "getSelection", isMethod: true},
+ {name: "matchMedia", isMethod: true, args:["(min-width:50px)"]},
+ {name: "moveBy", isMethod: true, args:[10, 10]},
+ {name: "moveTo", isMethod: true, args:[10, 10]},
+ {name: "open", isMethod: true},
+ {name: "print", isMethod: true},
+ {name: "prompt", isMethod: true},
+ {name: "resizeTo", isMethod: true, args:[10, 10]},
+ {name: "resizeBy", isMethod: true, args:[10, 10]},
+ {name: "scroll", isMethod: true, args:[10, 10]},
+ {name: "scrollTo", isMethod: true, args:[10, 10]},
+ {name: "scrollBy", isMethod: true, args:[10, 10]},
+ {name: "setInterval", isMethod: true, args:[function () {}, 1]},
+ {name: "setTimeout", isMethod: true, args:[function () {}, 1]},
+ {name: "stop", isMethod: true},
+ ].forEach(function (item) {
+ test(function () {
+ assert_true(item.name in window, "window." + item.name + " should exist.");
+ assert_throws_dom("SecurityError", function () {
+ if (item.isMethod)
+ if (item.args)
+ fr.contentWindow[item.name](item.args[0], item.args[1]);
+ else
+ fr.contentWindow[item.name]();
+ else
+ fr.contentWindow[item.name];
+ }, "A SecurityError exception should be thrown.");
+ }, "A SecurityError exception must be thrown when window." + item.name + " is accessed from a different origin.");
+ });
+
+ //SecurityError should not be thrown
+ [
+ //attributes
+ {name: "closed"},
+ {name: "frames"},
+ {name: "length"},
+ {name: "location"},
+ {name: "opener"},
+ {name: "parent"},
+ {name: "self"},
+ {name: "top"},
+ {name: "window"},
+ //methods
+ {name: "blur", isMethod: true},
+ {name: "close", isMethod: true},
+ {name: "focus", isMethod: true},
+ {name: "postMessage", isMethod: true, args: [{msg: 'foo'}, "*"]}
+ ].forEach(function (item) {
+ test(function () {
+ assert_true(item.name in window, "window." + item.name + " should exist.");
+ try {
+ if (item.isMethod)
+ if (item.args)
+ fr.contentWindow[item.name](item.args[0], item.args[1]);
+ else
+ fr.contentWindow[item.name]();
+ else
+ fr.contentWindow[item.name];
+ } catch (e) {
+ assert_unreached("An unexpected exception was thrown.");
+ }
+ }, "A SecurityError exception should not be thrown when window." + item.name + " is accessed from a different origin.");
+ });
+ });
+ t.done();
+}
+
+</script>
+<script>
+onload = function() {
+ var frame = document.createElement('iframe');
+ frame.id = "fr";
+ frame.setAttribute("style", "display:none");
+ frame.setAttribute('src', get_host_info().HTTPS_REMOTE_ORIGIN + "/");
+ frame.setAttribute("onload", "fr_load()");
+ document.body.appendChild(frame);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/self-et-al.window.js b/testing/web-platform/tests/html/browsers/the-window-object/self-et-al.window.js
new file mode 100644
index 0000000000..c42522803a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/self-et-al.window.js
@@ -0,0 +1,43 @@
+function delayed_assert_done(t, w, windowProxySelfReference) {
+ // Let's make sure nobody is being sneaky
+ t.step_timeout(() => {
+ t.step_timeout(() => {
+ assert_equals(w[windowProxySelfReference], w, `${windowProxySelfReference} got cleared after some time`);
+ t.done();
+ }, 0);
+ }, 0);
+}
+
+[
+ "frames",
+ "globalThis",
+ "self",
+ "window"
+].forEach(windowProxySelfReference => {
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ otherW = frame.contentWindow;
+ assert_equals(otherW[windowProxySelfReference], otherW, `${windowProxySelfReference} is broken`);
+ frame.remove();
+ assert_equals(otherW[windowProxySelfReference], otherW, `${windowProxySelfReference} got cleared after browsing context removal`);
+ assert_true(otherW.closed);
+
+ delayed_assert_done(t, otherW, windowProxySelfReference);
+ }, `iframeWindow.${windowProxySelfReference} before and after removal`);
+
+ async_test(t => {
+ const otherW = window.open();
+ assert_equals(otherW[windowProxySelfReference], otherW, `${windowProxySelfReference} is broken`);
+ otherW.onpagehide = t.step_func(() => {
+ assert_equals(otherW[windowProxySelfReference], otherW, `${windowProxySelfReference} got cleared after browsing context pagehide`);
+ t.step_timeout(() => {
+ assert_equals(otherW.opener, null); // Ensure browsing context is discarded
+ assert_equals(otherW[windowProxySelfReference], otherW, `${windowProxySelfReference} got cleared after browsing context removal`);
+ delayed_assert_done(t, otherW, windowProxySelfReference);
+ }, 0);
+ });
+ otherW.close();
+ assert_equals(otherW[windowProxySelfReference], otherW, `${windowProxySelfReference} got cleared after browsing context closure`);
+ assert_true(otherW.closed);
+ }, `popupWindow.${windowProxySelfReference} before, after closing, and after discarding`)
+});
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/BarProp-target.html b/testing/web-platform/tests/html/browsers/the-window-object/support/BarProp-target.html
new file mode 100644
index 0000000000..9921e7a577
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/BarProp-target.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script>
+ const barProps = ["locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar"];
+ const barPropsObj = {};
+ const channelName = location.search.substr(1);
+ const channel = new BroadcastChannel(channelName);
+ for (const prop of barProps) {
+ barPropsObj[prop] = window[prop].visible;
+ }
+ channel.postMessage(barPropsObj);
+
+ // Because messages are not delivered synchronously and because closing a
+ // browsing context prompts the eventual clearing of all task sources, this
+ // document should not be closed until the opener document has confirmed
+ // receipt.
+ channel.onmessage = () => { window.close() };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/closed.html b/testing/web-platform/tests/html/browsers/the-window-object/support/closed.html
new file mode 100644
index 0000000000..3b70598e34
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/closed.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!--
+ There's two URL parameters supported here:
+
+ window: The property name of a getter on the global object that returns the relevant WindowProxy
+ object to message.
+ ident: A reasonably unique identifier that will be echoed as the message.
+-->
+<script>
+ const params = new URLSearchParams(location.search);
+ self[params.get("window")].postMessage(params.get("ident"), "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/noopener-target.html b/testing/web-platform/tests/html/browsers/the-window-object/support/noopener-target.html
new file mode 100644
index 0000000000..41e197a746
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/noopener-target.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<script>
+ var channelName = location.search.substr(1);
+ var channel = new BroadcastChannel(channelName);
+ channel.postMessage({ name: window.name,
+ haveOpener: window.opener !== null });
+
+ // Because messages are not delivered synchronously and because closing a
+ // browsing context prompts the eventual clearing of all task sources, this
+ // document should not be closed until the opener document has confirmed
+ // receipt.
+ channel.onmessage = function() {
+ window.close();
+ };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/noreferrer-target.html b/testing/web-platform/tests/html/browsers/the-window-object/support/noreferrer-target.html
new file mode 100644
index 0000000000..c2446c6fe9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/noreferrer-target.html
@@ -0,0 +1,13 @@
+<script>
+ const channelName = location.search.substr(1),
+ channel = new BroadcastChannel(channelName);
+ channel.postMessage({ name: window.name,
+ haveOpener: window.opener !== null,
+ referrer: document.referrer });
+
+ // Because messages are not delivered synchronously and because closing a
+ // browsing context prompts the eventual clearing of all task sources, this
+ // document should not be closed until the opener document has confirmed
+ // receipt.
+ channel.onmessage = () => window.close();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/same-origin-iframe.html b/testing/web-platform/tests/html/browsers/the-window-object/support/same-origin-iframe.html
new file mode 100644
index 0000000000..763d9e466b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/same-origin-iframe.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+ window.addEventListener('load', () => { window.didLoadFrame = true; });
+ </script>
+ </head>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/sizing-target.html b/testing/web-platform/tests/html/browsers/the-window-object/support/sizing-target.html
new file mode 100644
index 0000000000..7cd5348a85
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/sizing-target.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script>
+ const windowProps = ["innerWidth", "innerHeight"];
+ const windowPropsObj = {};
+ const channelName = location.search.substr(1);
+ const channel = new BroadcastChannel(channelName);
+ for (const prop of windowProps) {
+ windowPropsObj[prop] = window[prop];
+ }
+ channel.postMessage(windowPropsObj);
+
+ // Because messages are not delivered synchronously and because closing a
+ // browsing context prompts the eventual clearing of all task sources, this
+ // document should not be closed until the opener document has confirmed
+ // receipt.
+ channel.onmessage = () => { window.close() };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/window-open-popup-target.html b/testing/web-platform/tests/html/browsers/the-window-object/support/window-open-popup-target.html
new file mode 100644
index 0000000000..a0588de829
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/window-open-popup-target.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script>
+ var channelName = window.name;
+ var channel = new BroadcastChannel(channelName);
+ const allBarProps = [
+ window.locationbar.visible,
+ window.menubar.visible,
+ window.personalbar.visible,
+ window.scrollbars.visible,
+ window.statusbar.visible,
+ window.toolbar.visible
+ ];
+ const allTrue = allBarProps.every(x=>x);
+ const allFalse = allBarProps.every(x=>!x);
+ channel.postMessage({isPopup: allFalse, mixedState: !allTrue && !allFalse});
+
+ // Because messages are not delivered synchronously and because closing a
+ // browsing context prompts the eventual clearing of all task sources, this
+ // document should not be closed until the opener document has confirmed
+ // receipt.
+ channel.onmessage = function() {
+ window.close();
+ };
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/support/windowFeature-values-target.html b/testing/web-platform/tests/html/browsers/the-window-object/support/windowFeature-values-target.html
new file mode 100644
index 0000000000..3a78ddf660
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/support/windowFeature-values-target.html
@@ -0,0 +1,24 @@
+<script>
+ const channelName = location.search.substr(1),
+ channel = new BroadcastChannel(channelName);
+
+ const haveOpener = window.opener !== null;
+ const haveReferrer = document.referrer !== null && document.referrer !== "";
+ const allBarProps = [
+ window.locationbar.visible,
+ window.menubar.visible,
+ window.personalbar.visible,
+ window.scrollbars.visible,
+ window.statusbar.visible,
+ window.toolbar.visible
+ ];
+ const isPopup = allBarProps.every(x=>!x);
+
+ channel.postMessage({haveOpener, haveReferrer, isPopup});
+
+ // Because messages are not delivered synchronously and because closing a
+ // browsing context prompts the eventual clearing of all task sources, this
+ // document should not be closed until the opener document has confirmed
+ // receipt.
+ channel.onmessage = () => window.close();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-aliases.html b/testing/web-platform/tests/html/browsers/the-window-object/window-aliases.html
new file mode 100644
index 0000000000..135be02a30
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-aliases.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Aliases of the window object</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-frames">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-self">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var global = this;
+
+test(function() {
+ assert_equals(window, global);
+ assert_equals(window.window, global);
+}, "window should be the global object");
+
+test(function() {
+ assert_equals(frames, global);
+ assert_equals(window.frames, global);
+}, "frames should be the global object");
+
+test(function() {
+ assert_equals(self, global);
+ assert_equals(window.self, global);
+}, "self should be the global object");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-access-vs-named-access.html b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-access-vs-named-access.html
new file mode 100644
index 0000000000..23b9124ef7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-access-vs-named-access.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Interactions between indexed and named access on the Window object</title>
+<link rel="author" title="Delan Azabani" href="dazabani@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#accessing-other-browsing-contexts">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#named-access-on-the-window-object">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=0></div>
+<div id=4></div>
+<iframe name=3></iframe>
+<iframe name=2></iframe>
+<iframe name=1></iframe>
+<script>
+const divs = document.querySelectorAll("div");
+const iframes = document.querySelectorAll("iframe");
+const wp = Object.getPrototypeOf(window);
+test(function() {
+ assert_equals(window[0], iframes[0].contentWindow);
+ assert_equals(window["0"], iframes[0].contentWindow);
+}, "WindowProxy: document-tree child navigable with index 0 (indexed access)");
+test(function() {
+ assert_equals(window[1], iframes[1].contentWindow);
+ assert_equals(window["1"], iframes[1].contentWindow);
+}, "WindowProxy: document-tree child navigable with index 1 (indexed access)");
+test(function() {
+ assert_equals(window[2], iframes[2].contentWindow);
+ assert_equals(window["2"], iframes[2].contentWindow);
+}, "WindowProxy: document-tree child navigable with index 2 (indexed access)");
+test(function() {
+ assert_equals(window[3], iframes[0].contentWindow);
+ assert_equals(window["3"], iframes[0].contentWindow);
+}, "WindowProxy: document-tree child navigable with target name 3 (named access)");
+test(function() {
+ assert_equals(window[4], divs[1]);
+ assert_equals(window["4"], divs[1]);
+}, "WindowProxy: element with id 4 (named access)");
+test(function() {
+ assert_equals(wp[0], divs[0]);
+ assert_equals(wp["0"], divs[0]);
+}, "Window prototype: element with id 0 (named access)");
+test(function() {
+ assert_equals(wp[1], iframes[2].contentWindow);
+ assert_equals(wp["1"], iframes[2].contentWindow);
+}, "Window prototype: document-tree child navigable with target name 1 (named access)");
+test(function() {
+ assert_equals(wp[2], iframes[1].contentWindow);
+ assert_equals(wp["2"], iframes[1].contentWindow);
+}, "Window prototype: document-tree child navigable with target name 2 (named access)");
+test(function() {
+ assert_equals(wp[3], iframes[0].contentWindow);
+ assert_equals(wp["3"], iframes[0].contentWindow);
+}, "Window prototype: document-tree child navigable with target name 3 (named access)");
+test(function() {
+ assert_equals(wp[4], divs[1]);
+ assert_equals(wp["4"], divs[1]);
+}, "Window prototype: element with id 4 (named access)");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-delete-no-cache.html b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-delete-no-cache.html
new file mode 100644
index 0000000000..22262943aa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-delete-no-cache.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Deletion of WindowProxy's indexed properties is not cached</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+const iframe = document.createElement("iframe");
+iframe.srcdoc = "";
+
+test(() => {
+ assert_equals(window.length, 0);
+ for (let i = 0; i < 1e5; i++) {
+ assert_true(delete window[0]);
+ }
+
+ document.body.append(iframe);
+ assert_false(delete window[0]);
+}, "Absence of index '0' is not cached");
+
+test(() => {
+ assert_equals(window.length, 1);
+ for (let i = 0; i < 1e5; i++) {
+ assert_false(delete window[0]);
+ }
+
+ iframe.remove();
+ assert_true(delete window[0]);
+}, "Presence of index '0' is not cached");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-strict.html b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-strict.html
new file mode 100644
index 0000000000..2ee10a7a64
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties-strict.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Indexed properties of the window object (strict mode)</title>
+<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-window-item">
+<link rel="help" href="https://webidl.spec.whatwg.org/#getownproperty">
+<link rel="help" href="https://webidl.spec.whatwg.org/#defineownproperty">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe></iframe>
+<script>
+test(function() {
+ "use strict";
+ assert_false("-1" in window, "-1 not in window");
+ assert_equals(window[-1], undefined);
+ window[-1] = "foo";
+ assert_equals(window[-1], "foo");
+});
+test(function() {
+ "use strict";
+ assert_throws_js(TypeError, function() {
+ window[0] = "foo";
+ });
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 0, { value: "bar" }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 0, { get() { return "baz" } }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 0, { set(v) { return "qux" } }))
+ assert_equals(window[0],
+ document.getElementsByTagName("iframe")[0].contentWindow);
+ assert_throws_js(TypeError, () => delete window[0]);
+});
+test(function() {
+ "use strict";
+ assert_throws_js(TypeError, function() {
+ window[1] = "foo";
+ });
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 1, { value: "bar" }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 1, { get() { return "baz" } }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 1, { set(v) { return "qux" } }))
+ assert_equals(window[1], undefined);
+ assert_equals(Object.getOwnPropertyDescriptor(window, 1), undefined);
+ assert_equals(delete window[1], true);
+});
+test(function() {
+ "use strict";
+ assert_throws_js(TypeError, () => { window[4294967294] = 1; });
+ assert_false(Reflect.set(window, 4294967294, 2));
+ assert_false(Reflect.defineProperty(window, 4294967294, { value: 3 }));
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 4294967294, { get: () => 4 }));
+ assert_equals(window[4294967294], undefined);
+ assert_false(4294967294 in window);
+ assert_true(delete window[4294967294]);
+}, "Borderline numeric key: 2 ** 32 - 2 is an index (strict mode)");
+test(function() {
+ "use strict";
+ window[4294967295] = 1;
+ assert_equals(window[4294967295], 1);
+ assert_true(Reflect.set(window, 4294967295, 2));
+ assert_equals(window[4294967295], 2);
+ assert_true(Reflect.defineProperty(window, 4294967295, { value: 3 }));
+ assert_equals(window[4294967295], 3);
+ Object.defineProperty(window, 4294967295, { get: () => 4 });
+ assert_equals(window[4294967295], 4);
+ assert_true(delete window[4294967295]);
+ assert_false(4294967295 in window);
+}, "Borderline numeric key: 2 ** 32 - 1 is not an index (strict mode)");
+test(function() {
+ "use strict";
+ var proto = Window.prototype;
+ [-1, 0, 1].forEach(function(idx) {
+ assert_false(idx in proto, idx + " in proto");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties.html b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties.html
new file mode 100644
index 0000000000..57f18c5944
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-indexed-properties.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Indexed properties of the window object (non-strict mode)</title>
+<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-window-item">
+<link rel="help" href="https://webidl.spec.whatwg.org/#getownproperty">
+<link rel="help" href="https://webidl.spec.whatwg.org/#defineownproperty">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe></iframe>
+<script>
+test(function() {
+ assert_false("-1" in window, "-1 not in window");
+ assert_equals(window[-1], undefined);
+ window[-1] = "foo";
+ assert_equals(window[-1], "foo");
+});
+test(() => {
+ const desc = Object.getOwnPropertyDescriptor(window, "0");
+ assert_true(desc.configurable);
+ assert_true(desc.enumerable);
+ assert_false(desc.writable);
+}, "Ensure indexed properties have the correct configuration");
+test(function() {
+ window[0] = "foo";
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 0, { value: "bar" }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 0, { get() { return "baz" } }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 0, { set() { return "quz" } }))
+ assert_equals(window[0],
+ document.getElementsByTagName("iframe")[0].contentWindow);
+ assert_equals(delete window[0], false);
+});
+test(function() {
+ window[1] = "foo";
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 1, { value: "bar" }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 1, { get() { return "baz" } }))
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 1, { set(v) { return "quz" } }))
+ assert_equals(window[1], undefined);
+ assert_equals(Object.getOwnPropertyDescriptor(window, 1), undefined);
+ assert_equals(delete window[1], true);
+});
+test(function() {
+ window[4294967294] = 1;
+ assert_false(Reflect.set(window, 4294967294, 2));
+ assert_false(Reflect.defineProperty(window, 4294967294, { value: 3 }));
+ assert_throws_js(TypeError, () => Object.defineProperty(window, 4294967294, { get: () => 4 }));
+ assert_equals(window[4294967294], undefined);
+ assert_false(4294967294 in window);
+ assert_true(delete window[4294967294]);
+}, "Borderline numeric key: 2 ** 32 - 2 is an index");
+test(function() {
+ window[4294967295] = 1;
+ assert_equals(window[4294967295], 1);
+ assert_true(Reflect.set(window, 4294967295, 2));
+ assert_equals(window[4294967295], 2);
+ assert_true(Reflect.defineProperty(window, 4294967295, { value: 3 }));
+ assert_equals(window[4294967295], 3);
+ Object.defineProperty(window, 4294967295, { get: () => 4 });
+ assert_equals(window[4294967295], 4);
+ assert_true(delete window[4294967295]);
+ assert_false(4294967295 in window);
+}, "Borderline numeric key: 2 ** 32 - 1 is not an index");
+test(function() {
+ var proto = Window.prototype;
+ [-1, 0, 1].forEach(function(idx) {
+ assert_false(idx in proto, idx + " in proto");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-open-defaults.window.js b/testing/web-platform/tests/html/browsers/the-window-object/window-open-defaults.window.js
new file mode 100644
index 0000000000..1b2d68a462
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-open-defaults.window.js
@@ -0,0 +1,12 @@
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.name = "foo";
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func(() => {
+ frame.onload = t.unreached_func();
+ t.step_timeout(() => t.done(), 500);
+ assert_equals(window[0], window.open(undefined, "foo"));
+ });
+ document.body.append(frame);
+}, "window.open()'s url parameter default");
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-open-invalid-url.html b/testing/web-platform/tests/html/browsers/the-window-object/window-open-invalid-url.html
new file mode 100644
index 0000000000..cabc143b9d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-open-invalid-url.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>window.open() with an invalid URL</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(() => {
+ assert_throws_dom("SyntaxError", () => window.open("https://example.com\u0000mozilla.org"));
+}, "Window.open should throw SyntaxError when an invalid url is passed as the first parameter");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-open-noopener.html b/testing/web-platform/tests/html/browsers/the-window-object/window-open-noopener.html
new file mode 100644
index 0000000000..8d3a95df63
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-open-noopener.html
@@ -0,0 +1,137 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>window.open() with "noopener" tests</title>
+
+<meta name="variant" content="?indexed">
+<meta name="variant" content="?_self">
+<meta name="variant" content="?_parent">
+<meta name="variant" content="?_top">
+<meta name=timeout content=long>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+var testData = [
+ { testDescription: "window.open() with 'noopener' should reuse existing target",
+ secondWindowFeatureString: "noopener",
+ shouldReturnWindow: false },
+ { testDescription: "noopener=1 means the same as noopener",
+ secondWindowFeatureString: "noopener=1",
+ shouldReturnWindow: false },
+ { testDescription: "noopener=true means the same as noopener",
+ secondWindowFeatureString: "noopener=true",
+ shouldReturnWindow: false },
+ { testDescription: "noopener=0 means lack of noopener",
+ secondWindowFeatureString: "noopener=0",
+ shouldReturnWindow: true },
+ { testDescription: "noopener separated only by spaces should work",
+ secondWindowFeatureString: "make me noopener",
+ shouldReturnWindow: false },
+ { testDescription: "Trailing noopener should work",
+ secondWindowFeatureString: "abc def, \n\r noopener",
+ shouldReturnWindow: false },
+ { testDescription: "Leading noopener should work",
+ secondWindowFeatureString: "noopener \f\t , hey, there",
+ shouldReturnWindow: false },
+ { testDescription: "Interior noopener should work",
+ secondWindowFeatureString: "and now, noopener , hey, there",
+ shouldReturnWindow: false },
+ { testDescription: "noreferrer should also suppress opener when reusing existing target",
+ secondWindowFeatureString: "noreferrer",
+ shouldReturnWindow: false },
+];
+
+/**
+ * Loop over our testData array and kick off an async test for each entry. Each
+ * async test opens a window using window.open() with some per-test unique name,
+ * then tries to do a second window.open() call with the same name and the
+ * test-specific feature string. It then checks whether that second
+ * window.open() call reuses the existing window, whether the return value of
+ * the second window.open() call is correct (it should be null in the noopener
+ * cases and non-null in the cases when the existing window gets reused) and so
+ * forth.
+ */
+function indexedTests() {
+ var tests = [];
+ for(var i = 0; i < testData.length; ++i) {
+ var test = testData[i];
+ var t = async_test(test.testDescription);
+ tests.push(t);
+ t.secondWindowFeatureString = test.secondWindowFeatureString;
+ t.windowName = "someuniquename" + i;
+
+ if (test.shouldReturnWindow) {
+ t.step(function() {
+ var windowName = this.windowName;
+
+ var w1 = window.open("", windowName);
+ this.add_cleanup(function() { w1.close(); });
+
+ assert_equals(w1.opener, window);
+
+ var w2 = window.open("", windowName, this.secondWindowFeatureString);
+ assert_equals(w2, w1);
+ assert_equals(w2.opener, w1.opener);
+ assert_equals(w2.opener, window);
+ this.done();
+ });
+ } else {
+ t.step(function() {
+ var w1;
+ this.add_cleanup(function() {
+ w1.close();
+ channel.postMessage(null);
+ });
+
+ var windowName = this.windowName;
+ var channel = new BroadcastChannel(windowName);
+
+ channel.onmessage = this.step_func_done(function(e) {
+ var data = e.data;
+ assert_equals(data.name, windowName, "Should have the right name");
+ assert_equals(data.haveOpener, true, "Should still have opener");
+ assert_equals(w1.opener, window);
+ assert_not_equals(w1.location.href, "about:blank", "Should have navigated");
+ });
+
+ w1 = window.open("", windowName);
+ assert_equals(w1.opener, window);
+
+ var w2 = window.open("support/noopener-target.html?" + windowName,
+ windowName, this.secondWindowFeatureString);
+ assert_equals(w2, null);
+
+ assert_equals(w1.opener, window);
+ });
+ }
+ }
+}
+
+/**
+ * Loop over the special targets that ignore noopener and check that doing a
+ * window.open() with those targets correctly reuses the existing window.
+ */
+function specialTargetTest(target) {
+ if (["_self", "_parent", "_top"].includes(target)) {
+ var t = async_test("noopener window.open targeting " + target);
+ t.openedWindow = window.open(`javascript:var w2 = window.open("", "${target}", "noopener"); this.checkValues(w2); this.close(); void(0);`);
+ assert_equals(t.openedWindow.opener, window);
+ t.openedWindow.checkValues = t.step_func_done(function(win) {
+ assert_equals(win, this.openedWindow);
+ });
+ } else {
+ throw 'testError: special target must be one of: _self, _parent, _top'
+ }
+}
+
+/**
+ * Parse the Query string, check if it matches keyword 'indexed' to run the indexed tests,
+ * otherwise test it as a special target
+ */
+var variant = window.location.href.split("?")[1]
+if(variant == "indexed") {
+ indexedTests();
+} else {
+ specialTargetTest(variant);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-open-noreferrer.html b/testing/web-platform/tests/html/browsers/the-window-object/window-open-noreferrer.html
new file mode 100644
index 0000000000..92b72cdb5f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-open-noreferrer.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>window.open() with "noreferrer" tests</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+async_test(t => {
+ const channelName = "343243423432",
+ channel = new BroadcastChannel(channelName);
+ window.open("support/noreferrer-target.html?" + channelName, "", "noreferrer");
+ channel.onmessage = t.step_func_done(e => {
+ // Send message first so if asserts throw the popup is still closed
+ channel.postMessage(null);
+
+ assert_equals(e.data.name, "");
+ assert_equals(e.data.referrer, "");
+ assert_equals(e.data.haveOpener, false);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-open-popup-behavior.html b/testing/web-platform/tests/html/browsers/the-window-object/window-open-popup-behavior.html
new file mode 100644
index 0000000000..258698d94d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-open-popup-behavior.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>Window.open popup behavior</title>
+<link rel="author" href="masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+function testOne(windowFeatures, expectPopup) {
+ const windowName = Math.round(Math.random()*1e12);
+ const channel = new BroadcastChannel(windowName);
+ var w;
+ promise_test(() => {
+ return new Promise(resolve => {
+ w = window.open("support/window-open-popup-target.html", windowName, windowFeatures);
+ channel.addEventListener('message', resolve);
+ }).then(e => {
+ // Send message first so if asserts throw the popup is still closed
+ channel.postMessage(null);
+ assert_false(e.data.mixedState, "No mixed state");
+ assert_equals(e.data.isPopup, expectPopup, "Popup state");
+ });
+ },`${windowFeatures} (expect ${expectPopup ? "popup" : "tab"})`);
+}
+
+// No windowpreferences at all - tab.
+testOne(undefined, /*expectPopup=*/false);
+
+// Test all permutations of these properties:
+const features = ["location","toolbar","menubar","resizable","scrollbars","status"];
+const nProps = features.length;
+const skip = 7; // To speed up the test, don't test all values. Skip 7 to pseudo-randomize.
+for(let i=0;i<2**nProps;i+=skip) {
+ const enableVec = Number(i).toString(2).padStart(nProps,'0').split('').map(s => (s==="1"));
+ let windowFeatures = [];
+ for(let i=0;i<nProps;++i) {
+ if (enableVec[i])
+ windowFeatures.push(features[i] + "=yes");
+ }
+ windowFeatures = windowFeatures.join(',');
+ // We get a popup we got windowFeatures, and any of them are false:
+ const expectPopup = !!windowFeatures.length && (!(enableVec[0] || enableVec[1]) || !enableVec[2] || !enableVec[3] || !enableVec[4] || !enableVec[5]);
+ testOne(windowFeatures, expectPopup);
+ testOne(windowFeatures + ",noopener", /*expectPopup=*/false);
+ testOne(windowFeatures + ",noreferrer", /*expectPopup=*/false);
+ testOne(windowFeatures + ",popup", /*expectPopup=*/true); // "popup" feature = popup
+ testOne(windowFeatures + ",noopener,noreferrer,popup", /*expectPopup=*/false);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-open-windowfeatures-values.html b/testing/web-platform/tests/html/browsers/the-window-object/window-open-windowfeatures-values.html
new file mode 100644
index 0000000000..32551dd8d7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-open-windowfeatures-values.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>window.open() windowFeature value parsing</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function testValueGeneric(val, expectTrue, property, testFn) {
+ const windowFeatureStr = val === "" ? property : `${property}=${val}`;
+ async_test(t => {
+ const windowName = '' + Math.round(Math.random()*1e12);
+ const channel = new BroadcastChannel(windowName);
+ channel.onmessage = t.step_func_done(e => {
+ // Send message first so if asserts throw the popup is still closed
+ channel.postMessage(null);
+ testFn(e.data);
+ });
+ window.open("support/windowFeature-values-target.html?" + windowName, windowName, windowFeatureStr);
+ },`Test ${windowFeatureStr}, expected interpretation is ${expectTrue ? 'true' : 'false'}`);
+}
+
+function testValueForNoReferrer(val, expectTrue) {
+ testValueGeneric(val, expectTrue, "noreferrer", (data) => {
+ if (expectTrue) {
+ assert_false(data.haveReferrer);
+ assert_false(data.haveOpener);
+ } else {
+ assert_true(data.haveReferrer);
+ assert_true(data.haveOpener);
+ }
+ });
+}
+
+function testValueForNoOpener(val, expectTrue) {
+ testValueGeneric(val, expectTrue, "noopener", (data) => {
+ assert_equals(data.haveOpener, !expectTrue);
+ });
+}
+
+function testValueForPopup(val, expectTrue) {
+ testValueGeneric(val, expectTrue, "popup", (data) => {
+ assert_equals(data.isPopup, expectTrue);
+ });
+}
+
+function testValue(val, expectTrue) {
+ const quotes = val === "" ? [''] : ['','"',"'"];
+ let noQuotes = true;
+ for (const quote of quotes) {
+ const thisExpectTrue = expectTrue && noQuotes;
+ const thisVal = quote + val + quote;
+ testValueForNoReferrer(thisVal, thisExpectTrue);
+ testValueForNoOpener(thisVal, thisExpectTrue);
+ testValueForPopup(thisVal, thisExpectTrue);
+ noQuotes = false;
+ }
+}
+
+testValue('',true); // Just the parameter means true
+testValue('yes',true); // Yes means true
+testValue('true',true); // True means true
+testValue('foo',false); // If parsing as an integer is an error, false
+testValue('0',false); // 0 is false
+testValue('00',false); // 0 is false
+testValue('1',true); // Non-zero is true
+testValue('99999',true); // Non-zero is true
+testValue('-1',true); // Non-zero is true
+testValue('1foo',true); // This parses to 1
+testValue('0foo',false); // This parses to 0
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-opener-unconfigurable.window.js b/testing/web-platform/tests/html/browsers/the-window-object/window-opener-unconfigurable.window.js
new file mode 100644
index 0000000000..2b9bda6792
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-opener-unconfigurable.window.js
@@ -0,0 +1,17 @@
+test(t => {
+ let desc = Object.getOwnPropertyDescriptor(self, "opener");
+ assert_true(!!desc.get, "Initially {get: function}");
+ assert_true(!!desc.set, "Initially {set: function}");
+ assert_true(desc.configurable, "Initially {configurable: true}");
+ assert_true(desc.enumerable, "Initially {enumerable: true}");
+
+ Object.defineProperty(self, "opener", {configurable: false});
+
+ desc = Object.getOwnPropertyDescriptor(self, "opener");
+ assert_true(!!desc.get, "Still has {get: function}");
+ assert_true(!!desc.set, "Still has {set: function}");
+ assert_false(desc.configurable, "Changed to {configurable: false}");
+ assert_true(desc.enumerable, "Still has {enumerable: true}");
+
+ assert_throws_js(TypeError, () => self.opener = "something", "Throws a TypeError due to {configurable: false}");
+}, "Corner case: self.opener is set while it's not configurable");
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-properties.https.html b/testing/web-platform/tests/html/browsers/the-window-object/window-properties.https.html
new file mode 100644
index 0000000000..2fb408cdc9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-properties.https.html
@@ -0,0 +1,359 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Properties of the window object</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="http://ecma-international.org/ecma-262/5.1/#sec-15.1">
+<link rel="help" href="https://webidl.spec.whatwg.org/#interface-prototype-object">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-attributes">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-operations">
+<link rel="help" href="https://dom.spec.whatwg.org/#eventtarget">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#windowtimers">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#windowbase64">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#windowsessionstorage">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#windowlocalstorage">
+<link rel="help" href="https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#dom-window-getselection">
+<link rel="help" href="http://dev.w3.org/csswg/cssom/#widl-def-Window">
+<link rel="help" href="http://dev.w3.org/csswg/cssom-view/#widl-def-Window">
+<iframe id="iframe" style="visibility: hidden" src="/resources/blank.html"></iframe>
+<iframe id="detachedIframe" style="visibility: hidden" src="/resources/blank.html"></iframe>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function assert_data_propdesc(pd, Writable, Enumerable, Configurable) {
+ assert_equals(typeof pd, "object");
+ assert_equals(pd.writable, Writable);
+ assert_equals(pd.enumerable, Enumerable);
+ assert_equals(pd.configurable, Configurable);
+}
+function assert_accessor_propdesc(pd, hasSetter, Enumerable, Configurable) {
+ assert_equals(typeof pd, "object");
+ assert_equals(typeof pd.get, "function");
+ assert_true("set" in pd,
+ "Should always have a setter property on the property descriptor");
+ assert_equals(typeof pd.set, hasSetter ? "function" : "undefined");
+ assert_equals(pd.enumerable, Enumerable);
+ assert_equals(pd.configurable, Configurable);
+}
+
+var unforgeableAttributes = [
+ "window",
+ "document",
+ "location",
+ "top"
+];
+
+var replaceableAttributes = [
+ "self",
+ "locationbar",
+ "menubar",
+ "personalbar",
+ "scrollbars",
+ "statusbar",
+ "toolbar",
+ "frames",
+ "parent",
+ "external",
+ "length",
+ "origin",
+
+ // CSSOM-View
+ "screen",
+ "scrollX",
+ "scrollY",
+ "pageXOffset",
+ "pageYOffset",
+ "innerWidth",
+ "innerHeight",
+ "screenLeft",
+ "screenTop",
+ "screenX",
+ "screenY",
+ "outerWidth",
+ "outerHeight",
+ "devicePixelRatio",
+];
+
+var methods = [
+ "close",
+ "stop",
+ "focus",
+ "blur",
+ "open",
+ "alert",
+ "confirm",
+ "prompt",
+ "print",
+ "postMessage",
+
+ // WindowBase64
+ "btoa",
+ "atob",
+
+ // WindowTimers
+ "setTimeout",
+ "clearTimeout",
+ "setInterval",
+ "clearInterval",
+
+ // Microtask queuing
+ "queueMicrotask",
+
+ // ImageBitmap
+ "createImageBitmap",
+
+ // HTML Editing APIs
+ "getSelection",
+
+ // CSSOM
+ "getComputedStyle",
+
+ // CSSOM-View
+ "matchMedia",
+ "moveBy",
+ "moveTo",
+ "resizeBy",
+ "resizeTo",
+ "scroll",
+ "scrollTo",
+ "scrollBy"
+];
+
+var readonlyAttributes = [
+ "history",
+ "frameElement",
+ "navigator",
+
+ // WindowSessionStorage
+ "sessionStorage",
+
+ // WindowLocalStorage
+ "localStorage",
+];
+
+var writableAttributes = [
+ "name",
+ "status",
+ "opener",
+ "onabort",
+ "onafterprint",
+ "onbeforeprint",
+ "onbeforeunload",
+ "onblur",
+ "oncancel",
+ "oncanplay",
+ "oncanplaythrough",
+ "onchange",
+ "onclick",
+ "onclose",
+ "oncontextmenu",
+ "oncuechange",
+ "ondblclick",
+ "ondrag",
+ "ondragend",
+ "ondragenter",
+ "ondragleave",
+ "ondragover",
+ "ondragstart",
+ "ondrop",
+ "ondurationchange",
+ "onemptied",
+ "onended",
+ "onerror",
+ "onfocus",
+ "onhashchange",
+ "oninput",
+ "oninvalid",
+ "onkeydown",
+ "onkeypress",
+ "onkeyup",
+ "onload",
+ "onloadeddata",
+ "onloadedmetadata",
+ "onloadstart",
+ "onmessage",
+ "onmousedown",
+ "onmousemove",
+ "onmouseout",
+ "onmouseover",
+ "onmouseup",
+ "onmousewheel",
+ "onmove",
+ "onoffline",
+ "ononline",
+ "onpause",
+ "onplay",
+ "onplaying",
+ "onpagehide",
+ "onpageshow",
+ "onpopstate",
+ "onprogress",
+ "onratechange",
+ "onreset",
+ "onresize",
+ "onscroll",
+ "onseeked",
+ "onseeking",
+ "onselect",
+ "onstalled",
+ "onstorage",
+ "onsubmit",
+ "onsuspend",
+ "ontimeupdate",
+ "onunload",
+ "onvolumechange",
+ "onwaiting"
+];
+
+test(function() {
+ // 15.1.1 Value Properties of the Global Object
+ ["NaN", "Infinity", "undefined"].forEach(function(id) {
+ test(function() {
+ assert_true(id in window, id + " in window");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ false, false, false);
+ }, "Value Property: " + id);
+ });
+}, "Value Properties of the Global Object");
+test(function() {
+ // 15.1.2 Function Properties of the Global Object
+ ["eval", "parseInt", "parseFloat", "isNaN", "isFinite"].forEach(function(id) {
+ test(function() {
+ assert_true(id in window, id + " in window");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ true, false, true);
+ }, "Function Property: " + id);
+ });
+}, "Function Properties of the Global Object");
+test(function() {
+ // 15.1.3 URI Handling Function Properties
+ ["decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent"].forEach(function(id) {
+ test(function() {
+ assert_true(id in window, id + " in window");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ true, false, true);
+ }, "URI Handling Function Property: " + id);
+ });
+}, "URI Handling Function Properties");
+test(function() {
+ // 15.1.4 Constructor Properties of the Global Object
+ ["Object", "Function", "Array", "String", "Boolean", "Number", "Date",
+ "RegExp", "Error", "EvalError", "RangeError", "ReferenceError",
+ "SyntaxError", "TypeError", "URIError"].forEach(function(id) {
+ test(function() {
+ assert_true(id in window, id + " in window");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ true, false, true);
+ }, "Constructor Property: " + id);
+ });
+}, "Constructor Properties of the Global Object");
+test(function() {
+ // 15.1.5 Other Properties of the Global Object
+ ["Math", "JSON"].forEach(function(id) {
+ test(function() {
+ assert_true(id in window, id + " in window");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ true, false, true);
+ }, "Other Property: " + id);
+ });
+}, "Other Properties of the Global Object");
+test(function() {
+ // EventTarget interface
+ ["addEventListener", "removeEventListener", "dispatchEvent"].forEach(function(id) {
+ test(function() {
+ var EventTargetProto = EventTarget.prototype;
+ assert_true(id in window, id + " in window");
+ assert_equals(window[id], EventTargetProto[id]);
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(EventTargetProto, id),
+ true, true, true);
+ assert_equals(Object.getOwnPropertyDescriptor(window, id), undefined);
+ }, "EventTarget method: " + id);
+ });
+}, "EventTarget interface");
+test(function() {
+ // Window interface
+ methods.forEach(function(id) {
+ test(function() {
+ var WindowProto = Window.prototype;
+ assert_true(id in window, id + " in window");
+ assert_false(id in WindowProto, id + " in Window.prototype");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ true, true, true);
+ }, "Window method: " + id);
+ });
+ readonlyAttributes.forEach(function(id) {
+ test(function() {
+ var WindowProto = Window.prototype;
+ assert_true(id in window, id + " in window");
+ assert_false(id in WindowProto, id + " in Window.prototype");
+ assert_accessor_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ false, true, true);
+ }, "Window readonly attribute: " + id);
+ });
+ writableAttributes.forEach(function(id) {
+ test(function() {
+ var WindowProto = Window.prototype;
+ assert_true(id in window, id + " in window");
+ assert_false(id in WindowProto, id + " in Window.prototype");
+ assert_accessor_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ true, true, true);
+ }, "Window attribute: " + id);
+ });
+ unforgeableAttributes.forEach(function(id) {
+ test(function() {
+ var WindowProto = Window.prototype;
+ assert_true(id in window, id + " in window");
+ assert_false(id in WindowProto, id + " in Window.prototype");
+ // location has a [PutForwards] extended attribute.
+ assert_accessor_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ id === "location", true, false);
+ }, "Window unforgeable attribute: " + id);
+ });
+ replaceableAttributes.forEach(function(id) {
+ test(function() {
+ var WindowProto = Window.prototype;
+ assert_true(id in window, id + " in window");
+ assert_false(id in WindowProto, id + " in Window.prototype");
+ assert_accessor_propdesc(Object.getOwnPropertyDescriptor(window, id),
+ true, true, true);
+ }, "Window replaceable attribute: " + id);
+ });
+}, "Window interface");
+test(function() {
+ assert_equals(window.constructor, Window);
+ assert_false(window.hasOwnProperty("constructor"), "window.constructor should not be an own property.");
+ assert_data_propdesc(Object.getOwnPropertyDescriptor(Window.prototype, "constructor"),
+ true, false, true);
+}, "constructor");
+var selfReferentialAccessors = ['window', 'self', 'frames'];
+// Test a variety of access methods
+var detached = window.detachedIframe;
+var detachedWindow = detached.contentWindow;
+detachedIframe.remove();
+selfReferentialAccessors.forEach(function(id) {
+ test(function() {
+ assert_equals(eval(`window.${id}`), window);
+ assert_equals(window[id], window);
+ assert_equals(Object.getOwnPropertyDescriptor(window, id).get.call(window), window);
+ assert_equals(Reflect.get(window, id), window);
+ }, "Window readonly getter: " + id);
+
+ test(function() {
+ var globalObject = iframe.contentWindow;
+ var _eval = globalObject.eval;
+ assert_equals(globalObject.eval(`window.${id}`), globalObject);
+ assert_equals(globalObject[id], globalObject);
+ assert_equals(_eval(`Object.getOwnPropertyDescriptor(window, "${id}").get.call(window)`), globalObject);
+ assert_equals(_eval(`Reflect.get(window, "${id}")`), globalObject);
+ }, "Window readonly getter in iframe: " + id);
+ test(function() {
+ var globalObject = detachedWindow;
+ var _eval = globalObject.eval;
+ assert_equals(_eval(`window.${id}`), globalObject);
+ assert_equals(globalObject[id], globalObject);
+ assert_equals(_eval(`Object.getOwnPropertyDescriptor(window, "${id}").get.call(window)`), globalObject);
+ assert_equals(_eval(`Reflect.get(window, "${id}")`), globalObject);
+ }, "Window readonly getter in detached iframe: " + id);
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-prototype-chain.html b/testing/web-platform/tests/html/browsers/the-window-object/window-prototype-chain.html
new file mode 100644
index 0000000000..14dbca35e8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-prototype-chain.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Prototype chain of the window object</title>
+<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#window">
+<link rel="help" href="https://dom.spec.whatwg.org/#eventtarget">
+<link rel="help" href="https://webidl.spec.whatwg.org/#interface-prototype-object">
+<link rel="help" href="https://webidl.spec.whatwg.org/#named-properties-object">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ assert_class_string(window, "Window");
+}, "window object");
+test(function() {
+ var proto = Object.getPrototypeOf(window);
+ assert_equals(proto, Window.prototype);
+}, "Window.prototype");
+test(function() {
+ var gsp = Object.getPrototypeOf(Object.getPrototypeOf(window));
+ assert_class_string(gsp, "WindowProperties");
+}, "Global scope polluter");
+test(function() {
+ var protoproto = Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(window)));
+ assert_equals(protoproto, EventTarget.prototype);
+}, "EventTarget.prototype");
+test(function() {
+ var protoprotoproto = Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(window))));
+ assert_equals(protoprotoproto, Object.prototype);
+}, "Object.prototype");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-window-object/window-reuse-in-nested-browsing-contexts.tentative.html b/testing/web-platform/tests/html/browsers/the-window-object/window-reuse-in-nested-browsing-contexts.tentative.html
new file mode 100644
index 0000000000..03c7a68dfd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-reuse-in-nested-browsing-contexts.tentative.html
@@ -0,0 +1,158 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+const setupIframe = (t, attrs) => {
+ const iframe = document.createElement('iframe');
+ for (const [key, value] of Object.entries(attrs))
+ iframe[key] = value;
+ const watcher = new EventWatcher(t, iframe, ['load']);
+ document.body.appendChild(iframe);
+ return {iframe, watcher};
+};
+
+promise_test(async t => {
+ const {iframe, watcher} = setupIframe(t, {});
+
+ // Per https://whatwg.org/c/iframe-embed-object.html#process-the-iframe-attributes,
+ // the task to perform the "iframe load event steps" should still be queued.
+ // If a browser already fired a load event, the test will fail here since
+ // EventWatcher will have received an unexpected load event.
+
+ iframe.contentWindow.persistedString = 'Hello world!';
+ iframe.src = 'support/same-origin-iframe.html';
+ await watcher.wait_for(['load']);
+
+ assert_true(iframe.contentWindow.didLoadFrame);
+ // The <iframe>'s session history has only one Document, and that Document is
+ // the initial about:blank Document. The Window object should be reused per
+ // https://whatwg.org/c/iframe-embed-object.html#process-the-iframe-attributes.
+ assert_equals(iframe.contentWindow.persistedString, 'Hello world!');
+}, 'synchronously navigate iframe with no initial src.');
+
+promise_test(async t => {
+ const {iframe, watcher} = setupIframe(t, {});
+
+ // Per https://whatwg.org/c/iframe-embed-object.html#process-the-iframe-attributes,
+ // the task to perform the "iframe load event steps" should still be queued.
+ await watcher.wait_for(['load']);
+
+ iframe.contentWindow.persistedString = 'Hello world!';
+ iframe.src = 'support/same-origin-iframe.html';
+ await watcher.wait_for(['load']);
+
+ assert_true(iframe.contentWindow.didLoadFrame);
+ // The <iframe>'s session history has only one Document, and that Document is
+ // the initial about:blank Document. The Window object should be reused per
+ // https://whatwg.org/c/iframe-embed-object.html#process-the-iframe-attributes.
+ assert_equals(iframe.contentWindow.persistedString, 'Hello world!');
+}, 'after the first iframe load event, navigate iframe with no initial src.');
+
+// Per https://whatwg.org/c/iframe-embed-object.html#otherwise-steps-for-iframe-or-frame-elements,
+// setting the <iframe> src to an empty string before inserting the <iframe>
+// into the document should begin an attempt to navigate to a resource with
+// url == "about:blank".
+promise_test(async t => {
+ const {iframe, watcher} = setupIframe(t, {src: ''});
+
+ // Per https://whatwg.org/c/browsing-the-web.html#navigate, the "about:blank"
+ // resource should be obtained "in parallel". If a browser performs this step
+ // synchronously, the test will fail here since EventWatcher will have
+ // received an unexpected load event.
+
+ iframe.contentWindow.persistedString = 'Hello world!';
+ // An attempt to navigate to "about:blank" already exists but should not have
+ // matured yet since the resource should be obtained "in parallel". The new
+ // navigation attempt will cancel the preexisting attempt.
+ iframe.src = 'support/same-origin-iframe.html';
+ await watcher.wait_for(['load']);
+
+ assert_true(iframe.contentWindow.didLoadFrame);
+ // The navigation attempt to "about:blank" was cancelled, so the <iframe>'s
+ // session history has only one Document, and that Document is the
+ // initial about:blank Document. The Window object should be reused per
+ // https://whatwg.org/c/iframe-embed-object.html#process-the-iframe-attributes.
+ assert_equals(iframe.contentWindow.persistedString, 'Hello world!');
+}, 'synchronously navigate iframe with initial src == "".');
+
+promise_test(async t => {
+ const {iframe, watcher} = setupIframe(t, {src: ''});
+
+ // Per https://whatwg.org/c/browsing-the-web.html#navigate, the "about:blank"
+ // resource should be obtained "in parallel".
+ await watcher.wait_for(['load']);
+
+ iframe.contentWindow.persistedString = 'Hello world!';
+ iframe.src = 'support/same-origin-iframe.html';
+ await watcher.wait_for(['load']);
+
+ assert_true(iframe.contentWindow.didLoadFrame);
+ // A non-initial navigation to about:blank was committed, so the <iframe>
+ // element is no longer displaying the initial about:blank Document. Per
+ // https://whatwg.org/c/browsing-the-web.html#initialise-the-document-object,
+ // the Window object must not be reused.
+ assert_equals(iframe.contentWindow.persistedString, undefined);
+}, 'after the first iframe load event, navigate iframe with initial src == "".');
+
+promise_test(async t => {
+ const {iframe, watcher} = setupIframe(t, {src: 'about:blank'});
+
+ // Per https://whatwg.org/c/browsing-the-web.html#navigate, the "about:blank"
+ // resource should be obtained "in parallel". If a browser performs this step
+ // synchronously, the test will fail here since EventWatcher will have
+ // received an unexpected load event.
+
+ iframe.contentWindow.persistedString = 'Hello world!';
+ // An attempt to navigate to "about:blank" already exists but should not have
+ // matured yet since the resource should be obtained "in parallel". The new
+ // navigation attempt will cancel the preexisting attempt.
+ iframe.src = 'support/same-origin-iframe.html';
+ await watcher.wait_for(['load']);
+
+ assert_true(iframe.contentWindow.didLoadFrame);
+ // The navigation attempt to "about:blank" was cancelled, so the <iframe>'s
+ // session history has only one Document, and that Document is the
+ // initial about:blank Document. The Window object should be reused per
+ // https://whatwg.org/c/iframe-embed-object.html#process-the-iframe-attributes.
+ assert_equals(iframe.contentWindow.persistedString, 'Hello world!');
+}, 'synchronously navigate iframe with initial src == "about:blank".');
+
+promise_test(async t => {
+ const {iframe, watcher} = setupIframe(t, {src: 'about:blank'});
+
+ // Per https://whatwg.org/c/browsing-the-web.html#navigate, the "about:blank"
+ // resource should be obtained "in parallel".
+ await watcher.wait_for(['load']);
+
+ iframe.contentWindow.persistedString = 'Hello world!';
+ iframe.src = 'support/same-origin-iframe.html';
+ await watcher.wait_for(['load']);
+
+ assert_true(iframe.contentWindow.didLoadFrame);
+ // A non-initial navigation to about:blank was committed, so the <iframe>
+ // element is no longer displaying the initial about:blank Document. Per
+ // https://whatwg.org/c/browsing-the-web.html#initialise-the-document-object,
+ // the Window object must not be reused.
+ assert_equals(iframe.contentWindow.persistedString, undefined);
+}, 'after the first iframe load event, navigate iframe with initial src == "about:blank".');
+
+// Per https://whatwg.org/c/iframe-embed-object.html#otherwise-steps-for-iframe-or-frame-elements,
+// setting <iframe> src before inserting the <iframe> into the document should
+// begin an attempt to navigate to the value of the src attribute.
+promise_test(async t => {
+ const {iframe, watcher} = setupIframe(t, {src: 'support/same-origin-iframe.html'});
+
+ iframe.contentWindow.persistedString = 'Hello world!';
+ // Completion of the attempt to navigate happens "in parallel".
+ await watcher.wait_for(['load']);
+
+ assert_true(iframe.contentWindow.didLoadFrame);
+ // The <iframe>'s session history has only one Document, and that Document is
+ // the initial about:blank Document. The Window object should be reused per
+ // https://whatwg.org/c/iframe-embed-object.html#process-the-iframe-attributes.
+ assert_equals(iframe.contentWindow.persistedString, 'Hello world!');
+}, 'iframe with initial src == same-origin resource.');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/document-tree-child-browsing-context-name-property-set.sub.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/document-tree-child-browsing-context-name-property-set.sub.html
new file mode 100644
index 0000000000..171aa01999
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/document-tree-child-browsing-context-name-property-set.sub.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document-tree child browsing context name property set</title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#document-tree-child-browsing-context-name-property-set">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="//{{domains[www]}}:{{ports[http][1]}}/common/window-name-setter.html#spices"></iframe>
+<iframe name="spices"></iframe>
+<iframe name="fruits"></iframe>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ test(() => {
+ assert_equals(window.spices, undefined);
+ assert_not_equals(window.fruits, undefined);
+ assert_equals(window.fruits, window[2]);
+ }, "Cross origin child window's name shadows the second candidate of a same origin iframe");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-define-own-property-unforgeable-same-origin.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-define-own-property-unforgeable-same-origin.html
new file mode 100644
index 0000000000..ac4eb458c3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-define-own-property-unforgeable-same-origin.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[DefineOwnProperty]] on a WindowProxy forwards to OrdinaryDefineOwnProperty for same-origin objects</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-defineownproperty">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+for (const key of ["window", "document", "location", "top"]) {
+ const { get, set } = Object.getOwnPropertyDescriptor(window, key);
+
+ test(() => {
+ Object.defineProperty(window, key, {});
+ assert_true(Reflect.defineProperty(window, key, { configurable: false }), "[[Configurable]]: false");
+ Object.defineProperty(window, key, { enumerable: true });
+
+ assert_true(Reflect.defineProperty(window, key, { get }), "[[Get]]: unchanged");
+ Object.defineProperty(window, key, { set });
+ assert_true(Reflect.defineProperty(window, key, { get, set }), "[[Get]]: unchanged, [[Set]]: unchanged");
+
+ Object.defineProperty(window, key, { get, set, enumerable: true, configurable: false });
+ }, `[[DefineOwnProperty]] success: "${key}"`);
+
+ test(() => {
+ assert_throws_js(TypeError, () => {
+ Object.defineProperty(window, key, { configurable: true });
+ }, "[[Configurable]]: true");
+
+ assert_false(Reflect.defineProperty(window, key, { enumerable: false }), "[[Enumerable]]: false");
+
+ assert_throws_js(TypeError, () => {
+ Object.defineProperty(window, key, { get() {}, set });
+ }, "[[Get]]: changed, [[Set]]: unchanged");
+
+ assert_false(Reflect.defineProperty(window, key, { get, set() {} }), "[[Get]]: unchanged, [[Set]]: changed");
+
+ assert_throws_js(TypeError, () => {
+ Object.defineProperty(window, key, { writable: false, configurable: true });
+ }, "[[Writable]]: false, [[Configurable]]: true");
+
+ assert_false(Reflect.defineProperty(window, key, { value: window[key], enumerable: true }), "[[Value]], [[Enumerable]]: true");
+ }, `[[DefineOwnProperty]] failure: "${key}"`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html
new file mode 100644
index 0000000000..97a156290a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[PreventExtensions]] on a WindowProxy object should return false</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-preventextensions">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ assert_throws_js(TypeError, () => {
+ Object.preventExtensions(window);
+ });
+}, "Object.preventExtensions throws a TypeError");
+
+test(() => {
+ assert_false(Reflect.preventExtensions(window));
+}, "Reflect.preventExtensions returns false");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin-domain.sub.html
new file mode 100644
index 0000000000..a5ae78cc87
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin-domain.sub.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a WindowProxy object should not allow changing its value: cross-origin via document.domain</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="/common/domain-setter.sub.html"></iframe>
+
+<script>
+"use strict";
+// This page does *not* set document.domain, so it's cross-origin with the iframe
+setup({ explicit_done: true });
+
+window.onload = () => {
+ const target = frames[0];
+
+ test(() => {
+ assert_equals(Object.getPrototypeOf(target), null);
+ }, "Cross-origin via document.domain: the prototype is null");
+
+ testSettingImmutablePrototype("Cross-origin via document.domain", target, null, { isSameOriginDomain: false });
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin.sub.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin.sub.html
new file mode 100644
index 0000000000..a5c1a2f102
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin.sub.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a WindowProxy object should not allow changing its value: cross-origin</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="//{{domains[www]}}:{{ports[http][1]}}/common/blank.html"></iframe>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ const target = frames[0];
+
+ test(() => {
+ assert_equals(Object.getPrototypeOf(target), null);
+ }, "Cross-origin: the prototype is null");
+
+ testSettingImmutablePrototype("Cross-origin", target, null, { isSameOriginDomain: false });
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-goes-cross-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-goes-cross-origin-domain.sub.html
new file mode 100644
index 0000000000..c2a6def6c2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-goes-cross-origin-domain.sub.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a WindowProxy object should not allow changing its value: cross-origin via document.domain after initially getting the object</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="/common/blank.html"></iframe>
+
+<script>
+"use strict";
+window.onload = () => {
+ const target = frames[0];
+ const origProto = Object.getPrototypeOf(target);
+
+ test(() => {
+ assert_not_equals(origProto, null);
+ }, "Same-origin (for now): the prototype is accessible");
+
+ document.domain = "{{host}}";
+
+ test(() => {
+ assert_equals(Object.getPrototypeOf(target), null);
+ }, "Became cross-origin via document.domain: the prototype is now null");
+
+ testSettingImmutablePrototype("Became cross-origin via document.domain", target, null, { isSameOriginDomain: false });
+
+ testSettingImmutablePrototypeToNewValueOnly(
+ "Became cross-origin via document.domain", target, origProto,
+ "the original value from before going cross-origin", { isSameOriginDomain: false });
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html
new file mode 100644
index 0000000000..fb18822ac5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a WindowProxy object should not allow changing its value: cross-origin, but same-origin-domain</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<iframe src="//{{domains[www]}}:{{ports[http][1]}}/common/domain-setter.sub.html"></iframe>
+
+<script>
+"use strict";
+document.domain = "{{host}}";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ const target = frames[0];
+ const origProto = Object.getPrototypeOf(target);
+
+ test(() => {
+ assert_not_equals(origProto, null);
+ }, "Same-origin-domain prerequisite check: the original prototype is accessible");
+
+ testSettingImmutablePrototype("Same-origin-domain", target, origProto, { isSameOriginDomain: true }, frames[0]);
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin.html b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin.html
new file mode 100644
index 0000000000..5779199ca3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[[SetPrototypeOf]] on a WindowProxy object should not allow changing its value: same-origin</title>
+<link rel="help" href="http://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/test-setting-immutable-prototype.js"></script>
+
+<script>
+"use strict";
+
+const origProto = Object.getPrototypeOf(window);
+
+test(() => {
+ assert_not_equals(origProto, null);
+}, "Same-origin prerequisite check: the original prototype is accessible");
+
+testSettingImmutablePrototype("Same-origin", window, origProto, { isSameOriginDomain: true });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html
new file mode 100644
index 0000000000..106c3f97cb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+ <head>
+ <title>Auxiliary Browsing Contexts: window.opener when Opener Removed/Closed</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/PrefixedLocalStorage.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var prefixedLocalStorage;
+ setup (() => prefixedLocalStorage = new PrefixedLocalStorageTest());
+ async_test(t => {
+ t.add_cleanup (() => prefixedLocalStorage.cleanup());
+ var a = document.createElement('a');
+ a.href = prefixedLocalStorage.url('resources/open-closer.html');
+ a.target = '_blank';
+ prefixedLocalStorage.onSet('openerIsNull', t.step_func_done(e => {
+ // The window for this auxiliary browsing context's opener
+ // has been closed and discarded, so the aux browsing context
+ // should now report `null` for `window.opener`
+ assert_equals(e.newValue, 'true');
+ }));
+ document.body.append(a);
+ a.click();
+ }, 'An auxiliary browsing context should report `null` for `window.opener` when that browsing context is discarded');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-multiple.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-multiple.html
new file mode 100644
index 0000000000..e71d4dc868
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-multiple.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Auxiliary Browsing Contexts: window.opener, multiple</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/PrefixedLocalStorage.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var prefixedLocalStorage;
+ setup (() => prefixedLocalStorage = new PrefixedLocalStorageTest());
+ async_test(t => {
+ t.add_cleanup (() => prefixedLocalStorage.cleanup());
+ var a = document.createElement('a');
+ a.href = prefixedLocalStorage.url('resources/multiple-opener.html');
+ a.target = 'multipleOpener';
+ window.name = 'topOpener';
+ document.body.appendChild(a);
+ window.addEventListener('message', t.step_func_done(e => {
+ var aux1 = e.data.aux1; // First opened context
+ var aux2 = e.data.aux2; // Context opened by first-opened context
+ assert_equals(aux1.name, 'multipleOpener');
+ assert_equals(aux1.openerName, window.name);
+ assert_equals(aux1.isTop, true);
+ assert_equals(aux2.name, 'multipleOpenee');
+ assert_equals(aux2.openerName, aux1.name);
+ assert_equals(aux2.isTop, true);
+ }));
+ a.click();
+ }, 'An auxiliary browsing context should be able to open another auxiliary browsing context');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html
new file mode 100644
index 0000000000..086a96442d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Auxiliary Browsing Contexts: window.opener noopener</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/PrefixedLocalStorage.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var prefixedLocalStorage;
+ setup(() => prefixedLocalStorage = new PrefixedLocalStorageTest());
+ async_test(t => {
+ t.add_cleanup(() => prefixedLocalStorage.cleanup());
+ prefixedLocalStorage.onSet('openerIsNull', t.step_func_done(e => {
+ assert_equals(e.newValue, 'true');
+ }));
+ window.open(prefixedLocalStorage.url('resources/no-opener.html'),
+ 'iShouldNotHaveAnOpener',
+ 'noopener');
+ }, 'Auxiliary browsing context created via `window.open` setting `noopener` should report `window.opener` `null`');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html
new file mode 100644
index 0000000000..b8226bd2b9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Auxiliary Browsing Contexts: window.opener noreferrer</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/PrefixedLocalStorage.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var prefixedLocalStorage;
+ setup(() => prefixedLocalStorage = new PrefixedLocalStorageTest());
+ async_test(t => {
+ t.add_cleanup(() => prefixedLocalStorage.cleanup());
+ var a = document.createElement('a');
+ a.href = prefixedLocalStorage.url('resources/no-opener.html');
+ a.target = '_blank';
+ a.rel = 'noreferrer';
+ window.name = 'topWindow';
+ document.body.appendChild(a);
+ prefixedLocalStorage.onSet('openerIsNull', t.step_func_done(e => {
+ assert_equals(e.newValue, 'true');
+ }));
+ a.click();
+ }, 'Auxiliary browsing context created with `rel="noreferrer"` should report `window.opener` `null`');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.html
new file mode 100644
index 0000000000..ac6e47b846
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Auxiliary Browsing Contexts: window.opener setter</title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/PrefixedLocalStorage.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var prefixedLocalStorage;
+ setup(() => prefixedLocalStorage = new PrefixedLocalStorageTest());
+ async_test(t => {
+ t.add_cleanup(() => prefixedLocalStorage.cleanup());
+ prefixedLocalStorage.onSet('openerIsNull', t.step_func_done(e => {
+ assert_equals(e.newValue, 'true');
+ }));
+ window.open(prefixedLocalStorage.url('resources/opener-setter.html'),
+ 'iShouldSetOpenerToNull');
+ }, 'Auxiliary browsing context created via `window.open` and setting `window.opener` to `null` should report `window.opener` `null`');
+ async_test(t => {
+ t.add_cleanup(() => prefixedLocalStorage.cleanup());
+ prefixedLocalStorage.onSet('openerIsTest', t.step_func_done(e => {
+ assert_equals(e.newValue, 'true');
+ }));
+ window.open(prefixedLocalStorage.url('resources/opener-setter.html'),
+ 'iShouldSetOpenerToTest');
+ }, 'Auxiliary browsing context created via `window.open` and setting `window.opener` to `test` should report `test`');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.window.js b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.window.js
new file mode 100644
index 0000000000..6d540ce97c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.window.js
@@ -0,0 +1,38 @@
+[
+ undefined,
+ 42,
+ function() { return "hi" },
+ "hi",
+ {},
+ [],
+ Symbol()
+].forEach(val => {
+ test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ win = frame.contentWindow;
+ t.add_cleanup(() => frame.remove());
+
+ assert_own_property(win, "opener");
+ assert_equals(win.opener, null);
+ const beforeDesc = Object.getOwnPropertyDescriptor(win, "opener"),
+ openerGet = beforeDesc.get,
+ openerSet = beforeDesc.set;
+ assert_own_property(beforeDesc, "get");
+ assert_own_property(beforeDesc, "set");
+ assert_true(beforeDesc.enumerable);
+ assert_true(beforeDesc.configurable);
+
+ win.opener = val;
+ assert_equals(win.opener, val);
+ assert_equals(openerGet(), null);
+
+ const desc = Object.getOwnPropertyDescriptor(win, "opener");
+ assert_equals(desc.value, val);
+ assert_true(desc.writable);
+ assert_true(desc.enumerable);
+ assert_true(desc.configurable);
+
+ openerSet("x");
+ assert_equals(win.opener, "x");
+ }, "Setting window.opener to " + String(val)); // String() needed for symbols
+});
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener.html
new file mode 100644
index 0000000000..c43d3bd3bf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/opener.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Auxiliary Browsing Contexts: window.opener</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/PrefixedLocalStorage.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var prefixedLocalStorage;
+ setup (() => {
+ window.name = 'topWindow';
+ prefixedLocalStorage = new PrefixedLocalStorageTest();
+ });
+
+ function cleanup () {
+ prefixedLocalStorage.setItem('closeAll', 'true');
+ prefixedLocalStorage.clear();
+ }
+
+ function testOpener (t, target) {
+ t.add_cleanup(cleanup);
+ window.addEventListener('message', t.step_func(e => {
+ if (e.data.name === target) {
+ // The opener IDL attribute...must return the WindowProxy object of the
+ // browsing context from which the current browsing context was created
+ assert_equals(e.data.openerName, 'topWindow');
+ // Auxiliary browsing contexts are always top-level browsing contexts
+ assert_equals(e.data.isTop, true);
+ t.done();
+ }
+ }));
+ }
+
+ async_test(t => {
+ var target = 'windowOpenerA';
+ var a = document.createElement('a');
+ a.href = prefixedLocalStorage.url('resources/message-window-opener.html');
+ a.target = target;
+ document.body.appendChild(a);
+ testOpener(t, target);
+ a.click();
+ }, 'Newly-created auxiliary browsing context should report `window.opener`');
+
+ async_test(t => {
+ var target = 'windowOpenerB';
+ testOpener(t, target);
+ window.open(prefixedLocalStorage.url('resources/message-window-opener.html'),
+ target);
+ }, 'Browsing context created with `window.open` should report `window.opener`');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/close-opener.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/close-opener.html
new file mode 100644
index 0000000000..f41773ed2c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/close-opener.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<body onload="closeOpener()">
+<p>This window should close its opener.</p>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedLocalStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+var prefixedLocalStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+function closeOpener () {
+ if (window.opener) {
+ window.opener.close();
+
+ // Give the browsing context a chance to dispose of itself
+ function waitForContextDiscard () {
+ if (window.opener === null) {
+ return prefixedLocalStorage.setItem('openerIsNull', 'true');
+ }
+ return setTimeout(waitForContextDiscard, 0);
+ }
+ waitForContextDiscard();
+ }
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/message-window-opener.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/message-window-opener.html
new file mode 100644
index 0000000000..8105650b61
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/message-window-opener.html
@@ -0,0 +1,14 @@
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedLocalStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+
+if (window.opener) {
+ window.opener.postMessage ({
+ name : window.name,
+ openerName: window.opener.name,
+ isTop : window.top === window
+ }, '*');
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/multiple-opener.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/multiple-opener.html
new file mode 100644
index 0000000000..2e63b9f4c6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/multiple-opener.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<body onload="openNested()">
+<script>
+var prefixedLocalStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+function openNested () {
+ // Listen for message from opened context and pass through to this
+ // context's opener
+ window.addEventListener('message', (e) => {
+ if (window.opener) {
+ window.opener.postMessage({
+ aux2: e.data, // From multipleOpenee
+ aux1: { // This context
+ name : window.name,
+ openerName : window.opener.name,
+ isTop : window.top === window
+ }
+ }, '*');
+ }
+ });
+ var a = document.createElement('a');
+ a.target = 'multipleOpenee';
+ a.href = prefixedLocalStorage.url('message-window-opener.html');
+ document.body.appendChild(a);
+ a.click();
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/no-opener.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/no-opener.html
new file mode 100644
index 0000000000..afd72f948d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/no-opener.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<p>This window should have no opener.</p>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedLocalStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+function checkOpener () {
+ return prefixedLocalStorage.setItem('openerIsNull', window.opener === null);
+}
+</script>
+<body onload="checkOpener()">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/open-closer.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/open-closer.html
new file mode 100644
index 0000000000..7575c7c95f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/open-closer.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<body onload="openAuxiliary()">
+<a rel="opener" target="_blank">Open auxiliary context that will close this window (its opener)</a>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+function openAuxiliary () {
+ var prefixedLocalStorage = new PrefixedLocalStorageResource();
+ var a = document.body.querySelector('a');
+ a.href = prefixedLocalStorage.url('close-opener.html');
+ document.body.append(a);
+ a.click();
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/opener-setter.html b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/opener-setter.html
new file mode 100644
index 0000000000..4112dae0ce
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/auxiliary-browsing-contexts/resources/opener-setter.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<p>This window should set the window.opener attribute</p>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedLocalStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+function checkOpener () {
+ if (window.name == 'iShouldSetOpenerToNull') {
+ window.opener = null;
+ return prefixedLocalStorage.setItem('openerIsNull', window.opener === null);
+ }
+ if (window.name == 'iShouldSetOpenerToTest') {
+ window.opener = 'test';
+ return prefixedLocalStorage.setItem('openerIsTest', window.opener === "test");
+ }
+}
+</script>
+<body onload="checkOpener()">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-001.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-001.html
new file mode 100644
index 0000000000..a1416f2eb8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-001.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Browsing context - `_blank` name keyword</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(t => {
+ var window1 = window.open('about:blank', '_blank');
+ var window2 = window.open('about:blank', '_blank');
+ var window3 = window.open('about:blank', '_blank');
+ t.add_cleanup(() => {
+ window1.close();
+ window2.close();
+ window3.close();
+ });
+ assert_not_equals(window1, window2);
+ assert_not_equals(window2, window3);
+ assert_not_equals(window1, window3);
+}, 'window.open into `_blank` should create a new browsing context each time');
+
+test(t => {
+ var window1 = window.open('about:blank', '_bLAnk');
+ var window2 = window.open('about:blank', '_bLAnk');
+ var window3 = window.open('about:blank', '_bLAnk');
+ t.add_cleanup(() => {
+ window1.close();
+ window2.close();
+ window3.close();
+ });
+ assert_not_equals(window1, window2);
+ assert_not_equals(window2, window3);
+ assert_not_equals(window1, window3);
+}, '`_blank` should be ASCII case-insensitive');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-002.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-002.html
new file mode 100644
index 0000000000..aba9d52ba0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-002.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Link with target=_blank, rel=noreferrer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<div id="log"></div>
+<a href="resources/report-has-opener.html" rel="noreferrer" target="_blank">Link</a>
+<script>
+var prefixedStorage;
+setup (() => prefixedStorage = new PrefixedLocalStorageTest());
+
+async_test(t => {
+ t.add_cleanup(() => prefixedStorage.cleanup());
+ var a = document.getElementsByTagName('a')[0];
+ a.href = prefixedStorage.url(a.href);
+ prefixedStorage.onSet('hasOpener', t.step_func_done(e => {
+ assert_equals(e.newValue, 'false');
+ }));
+ a.click();
+}, 'Context for opened noreferrer link targeted to "_blank" should not have opener reference');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-003.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-003.html
new file mode 100644
index 0000000000..5571344c85
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_blank-003.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Link with target=_blank, no rel</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<div id="log"></div>
+<a href="resources/report-has-opener.html" target="_blank">Link</a>
+<script>
+var prefixedStorage;
+setup(() => prefixedStorage = new PrefixedLocalStorageTest());
+async_test(t => {
+ t.add_cleanup(() => prefixedStorage.cleanup());
+ prefixedStorage.onSet('hasOpener', t.step_func_done(e => {
+ assert_equals(e.newValue, 'false');
+ }));
+ var a = document.getElementsByTagName('a')[0];
+ a.href = prefixedStorage.url(a.href);
+ a.click();
+}, 'Context created by link targeting "_blank" should not have opener reference');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-001.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-001.html
new file mode 100644
index 0000000000..35cbc101c7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-001.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - '_parent'</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ window.addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data.name, 'parentWin');
+ }));
+}, 'The parent browsing context must be chosen if the given name is `_parent`');
+</script>
+<iframe id="embedded" src="resources/choose-_parent-001-iframe-1.html" name="parentWin" style="display:none"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-002.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-002.html
new file mode 100644
index 0000000000..7b7d561030
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-002.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - '_parent' (nested contexts)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ var topWindow;
+ t.add_cleanup(() => topWindow.close());
+ window.addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data.name, 'iframeParent');
+ assert_false(e.data.isTop, 'window.parent is not top');
+ }));
+ topWindow = window.open('resources/choose-_parent-002-window.html', '_blank');
+}, 'choosing _parent context: multiple nested contexts');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-003.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-003.html
new file mode 100644
index 0000000000..20dc9b0d2a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-003.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - '_parent' (via window.open)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ var topWindow;
+ t.add_cleanup(() => topWindow.close());
+ window.addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data.name, 'parentTopReplace');
+ assert_equals(e.data.isTop, true);
+ }));
+ topWindow = window.open('resources/choose-_parent-003-window.html', 'parentTopReplace');
+}, '_parent should reuse window.parent context');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-004.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-004.html
new file mode 100644
index 0000000000..c79378018a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_parent-004.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - '_parent' (case-sensitivity)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<body>
+<div id="log"></div>
+
+<script>
+var prefixedStorage;
+var iframe;
+setup(() => prefixedStorage = new PrefixedLocalStorageTest());
+
+async_test(t => {
+ t.add_cleanup(() => prefixedStorage.cleanup());
+ var testFunc = (function (t) {
+ var completed = 0;
+ var testCount = 2;
+ return function (actual, expected) {
+ assert_equals(actual, expected);
+ if (++completed >= testCount) {
+ t.done();
+ }
+ }
+ }(t));
+
+ prefixedStorage.onSet('isTop', t.step_func(e => {
+ testFunc(e.newValue, 'false');
+ }));
+ prefixedStorage.onSet('name', t.step_func(e => {
+ testFunc(e.newValue, 'parentWin');
+ }));
+ iframe = document.createElement('iframe');
+ iframe.src = prefixedStorage.url('resources/choose-_parent-004-iframe-1.html');
+ iframe.name = 'parentWin';
+ document.body.appendChild(iframe);
+}, 'choosing _parent context should be case-insensitive');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-001.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-001.html
new file mode 100644
index 0000000000..ed7666846d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-001.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - the given name is '_self'</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="resources/choose-_self-001-iframe.html" style="display:none"></iframe>
+<script>
+async_test(t => {
+ window.addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data.name, 'myownself');
+ }), false);
+}, 'The current browsing context must be chosen if the given name is "_self"');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-002.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-002.html
new file mode 100644
index 0000000000..2e798f5493
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_self-002.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - '_self' (case-sensitivity)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<body>
+<div id="log"></div>
+
+<script>
+var prefixedStorage;
+setup(() => prefixedStorage = new PrefixedLocalStorageTest());
+
+async_test(t => {
+ var iframe;
+
+ var testFunc = (function (t) {
+ var completed = 0;
+ var testCount = 2;
+ return function (actual, expected) {
+ assert_equals(actual, expected);
+ if (++completed >= testCount) {
+ t.done();
+ }
+ }
+ }(t));
+
+ t.add_cleanup(() => prefixedStorage.cleanup());
+
+ prefixedStorage.onSet('isTop', t.step_func(e => {
+ testFunc(e.newValue, 'false');
+ }));
+ prefixedStorage.onSet('name', t.step_func(e => {
+ testFunc(e.newValue, 'testWin');
+ }));
+
+ iframe = document.createElement('iframe');
+ iframe.name = 'testWin';
+ iframe.src = prefixedStorage.url('resources/choose-_self-002-iframe.html');
+ document.body.appendChild(iframe);
+
+}, 'choosing _self context should be case-insensitive');
+
+</script>
+
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-001.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-001.html
new file mode 100644
index 0000000000..de4c6ad115
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-001.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>HTML Test: Browsing context name - _top (current is top)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<div id="log"></div>
+<script>
+var prefixedStorage;
+setup (() => prefixedStorage = new PrefixedLocalStorageTest());
+
+async_test(t => {
+ t.add_cleanup(() => prefixedStorage.cleanup());
+
+ var testFunc = (function (t) {
+ var completed = 0;
+ var testCount = 2;
+ return function (actual, expected) {
+ assert_equals(actual, expected);
+ if (++completed >= testCount) {
+ t.done();
+ }
+ }
+ }(t));
+
+ prefixedStorage.onSet('isTop', t.step_func(e => {
+ testFunc(e.newValue, 'true');
+ }));
+ prefixedStorage.onSet('name', t.step_func(e => {
+ testFunc(e.newValue, 'topWin1');
+ }));
+
+ window.open(prefixedStorage.url('resources/open-in-_top.html'), '_blank');
+}, 'Should choose current browsing context for "_top" if current is top');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-002.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-002.html
new file mode 100644
index 0000000000..f29da80c6e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-002.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>HTML Test: Browsing context name - _top</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<div id="log"></div>
+<script>
+var prefixedStorage;
+setup (() => prefixedStorage = new PrefixedLocalStorageTest());
+
+async_test(t => {
+ t.add_cleanup(() => prefixedStorage.cleanup());
+
+ var testFunc = (function (t) {
+ var completed = 0;
+ var testCount = 2;
+ return function (actual, expected) {
+ assert_equals(actual, expected);
+ if (++completed >= testCount) {
+ t.done();
+ }
+ }
+ }(t));
+
+ prefixedStorage.onSet('isTop', t.step_func(e => {
+ testFunc(e.newValue, 'true');
+ }));
+ prefixedStorage.onSet('name', t.step_func(e => {
+ testFunc(e.newValue, 'topWin2');
+ }));
+ window.open(prefixedStorage.url('resources/choose-_top-002-window.html'), '_blank');
+}, 'Should choose top browsing context for "_top" if current is not top');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-003.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-003.html
new file mode 100644
index 0000000000..e068f8cc1f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-_top-003.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - '_top' (case-sensitivity)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<body>
+<div id="log"></div>
+
+<script>
+var prefixedStorage;
+setup(() => prefixedStorage = new PrefixedLocalStorageTest());
+
+async_test(t => {
+ var testFunc = (function (t) {
+ var completed = 0;
+ var testCount = 2;
+ return function (actual, expected) {
+ assert_equals(actual, expected);
+ if (++completed >= testCount) {
+ t.done();
+ }
+ }
+ }(t));
+
+ t.add_cleanup(() => prefixedStorage.cleanup());
+
+ prefixedStorage.onSet('isTop', t.step_func(e => {
+ testFunc(e.newValue, 'true');
+ }));
+ prefixedStorage.onSet('name', t.step_func(e => {
+ testFunc(e.newValue, 'topWin');
+ }));
+
+ window.open(prefixedStorage.url('resources/choose-_top-003-iframe-1.html'), '_blank');
+}, 'choosing _top context should be case-insensitive');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-001.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-001.html
new file mode 100644
index 0000000000..c21159e0a6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-001.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Browsing context - Default name</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="/common/blank.html" style="display:none"></iframe>
+<object id="obj" type="text/html" data="about:blank"></object>
+<embed id="embedded" type="image/svg+xml" src="/images/green.svg" width="0" height="0" />
+<script>
+test(t => {
+ assert_equals(window.frames[0].name, "");
+ assert_equals(document.getElementById("embedded").name, "");
+ assert_equals(window["obj"].name, "");
+}, "A embedded browsing context has empty-string default name");
+
+test(t => {
+ var win = window.open("about:blank", "_blank");
+ assert_equals(win.name, "");
+ win.close();
+}, "A browsing context which is opened by window.open() method with '_blank' parameter has empty-string default name");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-002.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-002.html
new file mode 100644
index 0000000000..748ee68973
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-default-002.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Browsing context names - empty string</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ window.addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data.isTop, false);
+ assert_equals(e.data.name, 'hellothere', 'Empty-string browsing context should choose current context');
+ }), false);
+}, 'The current browsing context must be chosen if the given name is empty string');
+</script>
+<iframe name="hellothere" src="resources/choose-default-002-iframe.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-existing-001.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-existing-001.html
new file mode 100644
index 0000000000..fdf74b8a79
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/choose-existing-001.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Choose browsing context - the given name is same as an existing browsing context's name</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="resources/choose-existing-001-iframe.html" style="display:none"></iframe>
+<iframe name="iExist" style="display:none"></iframe>
+<script>
+async_test(t => {
+ window.addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data.name, 'iExist');
+ }), false);
+
+}, 'An existing browsing context must be chosen if the given name is the same as its name');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-001-iframe-1.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-001-iframe-1.html
new file mode 100644
index 0000000000..04dd74f1a5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-001-iframe-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<iframe src="open-in-_parent.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-iframe.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-iframe.html
new file mode 100644
index 0000000000..52da8986b0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent: nested context</title>
+<iframe name="iframeChild" src="open-in-_parent.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-window.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-window.html
new file mode 100644
index 0000000000..558193742f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-002-window.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent: top-level context</title>
+<iframe name="iframeParent" src="choose-_parent-002-iframe.html"></iframe>
+<script>
+// Relay a message from child context to opener context
+window.addEventListener('message', e => {
+ if (window.opener) {
+ window.opener.postMessage(e.data, '*');
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-iframe.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-iframe.html
new file mode 100644
index 0000000000..1412dc2e79
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-iframe.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent</title>
+<script>
+window.open("post-to-opener.html", "_parent");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-window.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-window.html
new file mode 100644
index 0000000000..bd1802aed6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-003-window.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent: top-level context (gets replaced)</title>
+<iframe name="iframeOpener" src="choose-_parent-003-iframe.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-1.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-1.html
new file mode 100644
index 0000000000..7a8cbecb27
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent</title>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<body>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+var iframe = document.createElement('iframe');
+iframe.src = prefixedStorage.url('choose-_parent-004-iframe-2.html');
+document.body.appendChild(iframe);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-2.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-2.html
new file mode 100644
index 0000000000..33ead5a538
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_parent-004-iframe-2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent (case-insensitive)</title>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource();
+window.open(prefixedStorage.url('report-is-top.html'), '_pARent');
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-001-iframe.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-001-iframe.html
new file mode 100644
index 0000000000..0ad79d6e16
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-001-iframe.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - self</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script>
+window.name = 'myownself';
+var win = window.open('post-to-top.html', '_self');
+win.close();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-002-iframe.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-002-iframe.html
new file mode 100644
index 0000000000..9d88305e09
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_self-002-iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - self (case-insensitive)</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+var win = window.open(prefixedStorage.url('report-is-top.html'), '_sElF');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-002-window.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-002-window.html
new file mode 100644
index 0000000000..d71384b72f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-002-window.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/common/PrefixedLocalStorage.js"></script>
+<title>HTML Test: browsing context name - _top</title>
+<body>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup:true
+});
+window.name = 'topWin2';
+var iframe = document.createElement('iframe');
+iframe.src = prefixedStorage.url('open-in-_top.html');
+// Append iframe that will open another document into `_top` (this context)
+document.body.appendChild(iframe);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-1.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-1.html
new file mode 100644
index 0000000000..aecc2fd88a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - top (case-insensitive)</title>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<body>
+</body>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+window.name = 'topWin';
+var iframe = document.createElement('iframe');
+iframe.src = prefixedStorage.url('choose-_top-003-iframe-2.html');
+document.body.appendChild(iframe);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-2.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-2.html
new file mode 100644
index 0000000000..a1a7e1dda7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-_top-003-iframe-2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - top (case-insensitive)</title>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+window.open(prefixedStorage.url("report-is-top.html"), "_ToP");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-default-002-iframe.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-default-002-iframe.html
new file mode 100644
index 0000000000..567e4ea310
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-default-002-iframe.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - Empty string</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<body onload="followLink()">
+</body>
+<script>
+function followLink() {
+ var a = document.createElement('a');
+ a.href = 'post-to-top.html';
+ a.target = ''; // Target is empty string
+ document.body.appendChild(a);
+ a.click();
+}
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-existing-001-iframe.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-existing-001-iframe.html
new file mode 100644
index 0000000000..cb0b554854
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/choose-existing-001-iframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This is a test page</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script>
+window.open("post-to-top.html", "iExist");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_parent.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_parent.html
new file mode 100644
index 0000000000..81d0735c60
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_parent.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: browsing context name - parent</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script>
+
+window.open("post-to-top.html", "_parent");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_top.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_top.html
new file mode 100644
index 0000000000..929d52d15e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/open-in-_top.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/common/PrefixedLocalStorage.js"></script>
+<title>HTML Test: browsing context name - _top</title>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource();
+window.name = 'topWin1';
+window.open(prefixedStorage.url('report-is-top.html'), '_top');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-opener.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-opener.html
new file mode 100644
index 0000000000..3e9b7aaccb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-opener.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: post window's name to top browsing context</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script>
+if (window.opener) {
+ window.opener.postMessage({
+ name: window.name,
+ isTop: (window.top === window)
+ }, "*");
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-top.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-top.html
new file mode 100644
index 0000000000..aebb57b019
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/post-to-top.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: post window's name to top browsing context</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script>
+top.postMessage({
+ name: window.name,
+ isTop: (window.top === window)
+}, "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-has-opener.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-has-opener.html
new file mode 100644
index 0000000000..37d8cedc6d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-has-opener.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+prefixedStorage.setItem('hasOpener', window.opener !== null);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-is-top.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-is-top.html
new file mode 100644
index 0000000000..8711235982
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/resources/report-is-top.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedStorage = new PrefixedLocalStorageResource({
+ close_on_cleanup: true
+});
+prefixedStorage.setItem('isTop', window === window.top);
+prefixedStorage.setItem('name', window.name);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-window.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-window.html
new file mode 100644
index 0000000000..f4f3c715ac
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-window.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML Test: Newly-Created browsing context Window and `this`</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+
+ /**
+ * Test for creating a new browsing context, [Browsing Contexts](#windows).
+ * This test is separate from the rest of the browsing-content-creation tests
+ * because the `iframe` has a src and thus its document won't be `about:blank`.
+ */
+ var doc, iframe;
+
+ setup(function () {
+ iframe = document.createElement("iframe");
+ iframe.src = get_host_info().HTTP_REMOTE_ORIGIN + "/html/browsers/windows/resources/browsing-context-window.html";
+ document.body.appendChild(iframe);
+ doc = iframe.contentDocument;
+ });
+
+ async_test(function (t) {
+ window.onmessage = t.step_func(function (e) {
+ assert_equals(e.data.thisWindowEquivalency, true, "The global `this` for the created browsing context should be a reference to Window through WindowProxy");
+ t.done();
+ });
+ });
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context.html b/testing/web-platform/tests/html/browsers/windows/browsing-context.html
new file mode 100644
index 0000000000..59367b0428
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/browsing-context.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML Test: Browsing context is first created</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var doc, iframe;
+
+ setup(function () {
+ // Create new browsing context via iframe
+ iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ doc = iframe.contentDocument;
+ });
+
+ test(function () {
+ assert_equals(doc.compatMode, "BackCompat", "The compatMode of a document without a document type declaration should be 'BackCompat'."); // Quirksmode
+ assert_equals(doc.contentType, "text/html", "The document should be an HTML document.");
+ assert_equals(doc.readyState, "complete", "The readyState attribute should be 'complete'.");
+ // Document metadata...
+ assert_equals(doc.documentURI, "about:blank", "The document's address should be 'about:blank'.");
+ assert_equals(doc.URL, "about:blank", "The document's address should be 'about:blank'.");
+ assert_equals(doc.doctype, null, "The docType of a document without a document type declaration should be null.");
+ assert_equals(doc.characterSet, "UTF-8", "The document's encoding should be 'UTF-8'.");
+ }, "Check that browsing context has new, ready HTML document");
+
+ test(function () {
+ assert_equals(doc.childNodes.length, 1, "The document must have only one child.");
+ assert_equals(doc.documentElement.tagName, "HTML");
+ assert_equals(doc.documentElement.childNodes.length, 2, "The HTML element should have 2 children.");
+ assert_equals(doc.documentElement.childNodes[0].tagName, "HEAD", "The first child of HTML element should be a HEAD element.");
+ assert_false(doc.documentElement.childNodes[0].hasChildNodes(), "The HEAD element should not have children.");
+ assert_equals(doc.documentElement.childNodes[1].tagName, "BODY", "The second child of HTML element should be a BODY element.");
+ assert_false(doc.documentElement.childNodes[1].hasChildNodes(), "The BODY element should not have children.");
+ }, "Check that new document nodes extant, empty");
+
+ test(function () {
+ assert_equals(doc.referrer, document.URL, "The document's referrer should be its creator document's URL.");
+ assert_equals(iframe.contentWindow.parent.document, document);
+ }, "Check the document properties corresponding to the creator browsing context");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/clear-window-name.https.html b/testing/web-platform/tests/html/browsers/windows/clear-window-name.https.html
new file mode 100644
index 0000000000..698de8a1ca
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/clear-window-name.https.html
@@ -0,0 +1,122 @@
+<!doctype html>
+<html>
+<head>
+ <title>Clear window.name when cross-origin</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/utils.js"></script>
+</head>
+<body>
+ <script>
+
+function anchorClick(n) {
+ const hyperlink = document.body.appendChild(document.createElement("a"))
+ hyperlink.rel = "noopener";
+ hyperlink.target = "_blank";
+ hyperlink.href = n;
+ hyperlink.click();
+}
+
+async function pollResultAndCheck(t, id, expected) {
+ const stashURL = new URL("resources/window-name-stash.py", location);
+ stashURL.searchParams.set('id', id);
+
+ let res = "NONE";
+ while (res == "NONE") {
+ await new Promise(resolve => { t.step_timeout(resolve, 100); });
+
+ const response = await fetch(stashURL);
+ res = await response.text();
+ }
+ if (res !== expected) {
+ assert_unreached('Stash result does not equal expected result.')
+ }
+}
+
+promise_test(async t => {
+ const id = token();
+
+ window.open(`resources/window-name.sub.html?report=${id}|close`, id);
+ await pollResultAndCheck(t, id, id);
+}, "Window.name is not reset when there is an opener around");
+
+promise_test(async t => {
+ const id = token();
+
+ window.open(`resources/window-name.sub.html?cross|same|report=${id}|close`, id);
+ await pollResultAndCheck(t, id, id);
+}, "Window.name is not reset when there is an opener around (cross-origin)");
+
+promise_test(async t => {
+ const id = token();
+
+ window.open(`resources/window-name.sub.html?report=${id}|close`, id, "noopener");
+ await pollResultAndCheck(t, id, id);
+}, "Window.name is not reset at the first navigation away from initial about:blank with noopener");
+
+promise_test(async t => {
+ const id = token();
+
+ window.open(`resources/window-name.sub.html?cross|same|report=${id}|close`, id, "noopener");
+ await pollResultAndCheck(t, id, "");
+}, "Window.name is reset at the first cross-origin navigation with noopener");
+
+promise_test(async t => {
+ const id = token();
+
+ let win = window.open(`resources/window-name.sub.html?report=${id}|close`, id);
+ win.opener = null;
+ await pollResultAndCheck(t, id, id);
+}, "Window.name is not reset at the first navigation away from initial about:blank with window.opener set to null");
+
+promise_test(async t => {
+ const id = token();
+
+ let win = window.open(`resources/window-name.sub.html?same|report=${id}|close`, id);
+ win.opener = null;
+ await pollResultAndCheck(t, id, id);
+}, "Window.name is not reset at the same-origin navigation with window.opener set to null");
+
+promise_test(async t => {
+ const id = token();
+
+ let win = window.open(`resources/window-name.sub.html?cross|same|report=${id}|close`, id);
+ win.opener = null;
+ await pollResultAndCheck(t, id, "");
+}, "Window.name is reset at the first first cross-origin navigation with window.opener set to null");
+
+promise_test(async t => {
+ const id = token();
+
+ anchorClick(`resources/window-name.sub.html?set=${id}|report=${id}|close`);
+ await pollResultAndCheck(t, id, id);
+}, "Window.name is set by the window");
+
+promise_test(async t => {
+ const id = token();
+
+ anchorClick(`resources/window-name.sub.html?set=${id}|cross|same|report=${id}|close`);
+ await pollResultAndCheck(t, id, "");
+}, "Window.name is reset at the first cross-origin navigation");
+
+promise_test(async t => {
+ const id = token();
+
+ window.open(`resources/window-name.sub.html?open|navOpener=about:blank|reportOpener=${id}|closeOpener|close`, id, "noopener");
+ await pollResultAndCheck(t, id, id);
+}, "window.name is not reset after navigating to an about:blank page from a non-about:blank page");
+
+
+promise_test(async t => {
+ const id = token();
+ const domain = window.location.host;
+
+ anchorClick(`resources/window-name.sub.html?sub|set=${id}|setDomain=${domain}|sub|report=${id}|close`);
+ await pollResultAndCheck(t, id, id);
+}, "Window.name is not reset if the document.domain is set to the parent domain");
+
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/dangling-markup-window-name.html b/testing/web-platform/tests/html/browsers/windows/dangling-markup-window-name.html
new file mode 100644
index 0000000000..1bc8bb9f7f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/dangling-markup-window-name.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+<head>
+ <title>Dangling Markup in target</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/utils.js"></script>
+</head>
+<body>
+ <script>
+ function anchorClick(target, id) {
+ const hyperlink = document.body.appendChild(document.createElement('a'));
+ if (target) {
+ hyperlink.target = target;
+ }
+ hyperlink.href = `resources/window-name.sub.html?report=${id}|close`;
+ hyperlink.click();
+ }
+
+ async function pollResultAndCheck(t, id, expected) {
+ const stashURL = new URL('resources/window-name-stash.py', location);
+ stashURL.searchParams.set('id', id);
+
+ let res = 'NONE';
+ while (res == 'NONE') {
+ await new Promise(resolve => { t.step_timeout(resolve, 100); });
+
+ const response = await fetch(stashURL);
+ res = await response.text();
+ }
+ if (res !== expected) {
+ assert_unreached('Stash result does not equal expected result.')
+ }
+ }
+
+ promise_test(async t => {
+ const id = token();
+ const value = '\n<' + id;
+
+ window.open(`resources/window-name.sub.html?report=${id}|close`, value);
+ await pollResultAndCheck(t, id, value);
+ }, 'Dangling Markup in target is not reset when set by window.open');
+
+ promise_test(async t => {
+ const id = token();
+ const value = '\n<' + id;
+
+ anchorClick(value, id)
+ await pollResultAndCheck(t, id, '');
+ }, 'Dangling Markup with "\\n" in target is reset when set by <a> tag');
+
+ promise_test(async t => {
+ const id = token();
+ const value = '\r<' + id;
+
+ anchorClick(value, id)
+ await pollResultAndCheck(t, id, '');
+ }, 'Dangling Markup with "\\r" in target is reset when set by <a> tag');
+
+ promise_test(async t => {
+ const id = token();
+ const value = '\t<' + id;
+
+ anchorClick(value, id)
+ await pollResultAndCheck(t, id, '');
+ }, 'Dangling Markup with "\\t" in target is reset when set by <a> tag');
+
+ promise_test(async t => {
+ const id = token();
+ const value = '\n<' + id;
+
+ const form = document.body.appendChild(document.createElement('form'));
+ form.target = value;
+ form.method = 'GET';
+ form.action = 'resources/window-name.sub.html';
+ const input = form.appendChild(document.createElement('input'));
+ input.type = 'hidden';
+ input.name = 'report';
+ input.value = `${id}|close`;
+ form.submit();
+
+ await pollResultAndCheck(t, id, '');
+ }, 'Dangling Markup in target is reset when set by <form> tag');
+
+ promise_test(async t => {
+ const id = token();
+ const value = '\n<' + id;
+ const base = document.head.appendChild(document.createElement('base'));
+ base.target = value;
+
+ anchorClick(null, id)
+ await pollResultAndCheck(t, id, '');
+ }, 'Dangling Markup in target is reset when set by <base> tag');
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/document-domain-nested-navigate.window.js b/testing/web-platform/tests/html/browsers/windows/document-domain-nested-navigate.window.js
new file mode 100644
index 0000000000..f51eed5ca9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/document-domain-nested-navigate.window.js
@@ -0,0 +1,16 @@
+async_test(t => {
+ // Setting document.domain makes this document cross-origin with that of the frame. However,
+ // about:blank will end up reusing the origin of this document, at which point the frame's
+ // document is no longer cross-origin.
+ const frame = document.body.appendChild(document.createElement('iframe'));
+ document.domain = document.domain;
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func(() => {
+ assert_throws_dom("SecurityError", () => window[0].document);
+ frame.src = "about:blank";
+ frame.onload = t.step_func_done(() => {
+ // Ensure we can access the child browsing context after navigation to non-initial about:blank
+ assert_equals(window[0].document, frame.contentDocument);
+ });
+ });
+}, "Navigated frame to about:blank and document.domain");
diff --git a/testing/web-platform/tests/html/browsers/windows/document-domain-nested-set.window.js b/testing/web-platform/tests/html/browsers/windows/document-domain-nested-set.window.js
new file mode 100644
index 0000000000..13c0d50ba0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/document-domain-nested-set.window.js
@@ -0,0 +1,10 @@
+test(() => {
+ // As the initial:about frame document reuses the origin of this document, setting document.domain
+ // from the frame, resulting in a origin mutation, has no effect on these documents being able to
+ // reach each other, as they share the same "physical" origin.
+ document.body.appendChild(document.createElement('iframe'));
+ const script = document.createElement("script");
+ script.text = "document.domain = document.domain";
+ window[0].document.body.appendChild(script);
+ assert_equals(window[0].document.body.localName, "body");
+}, "Initial about:blank frame and document.domain in the frame");
diff --git a/testing/web-platform/tests/html/browsers/windows/document-domain-nested.window.js b/testing/web-platform/tests/html/browsers/windows/document-domain-nested.window.js
new file mode 100644
index 0000000000..2e39d05f03
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/document-domain-nested.window.js
@@ -0,0 +1,9 @@
+test(() => {
+ // As the initial:about frame document reuses the origin of this document, setting document.domain
+ // from this document, resulting in a origin mutation, has no effect on these documents being able
+ // to reach each other, as they share the same "physical" origin.
+ document.body.appendChild(document.createElement('iframe'));
+ document.domain = document.domain;
+ // Ensure we can still access the child browsing context
+ assert_equals(window[0].document.body.localName, "body");
+}, "Initial about:blank frame and document.domain");
diff --git a/testing/web-platform/tests/html/browsers/windows/document-domain-removed-iframe.html b/testing/web-platform/tests/html/browsers/windows/document-domain-removed-iframe.html
new file mode 100644
index 0000000000..7403ebfaad
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/document-domain-removed-iframe.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document.domain and removed iframe interaction</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This is a test for https://crbug.com/1095145 where window
+ properties become undefined for document.domain-using removed
+ iframes -->
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ const { contentWindow } = iframe;
+ assert_equals(contentWindow.status, "");
+ resolve();
+ });
+ iframe.src = "/common/blank.html";
+ document.body.append(iframe);
+ });
+}, "No removal, no document.domain");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ const { contentWindow } = iframe;
+ iframe.remove();
+ assert_equals(contentWindow.status, "");
+ resolve();
+ });
+ iframe.src = "/common/blank.html";
+ document.body.append(iframe);
+ });
+}, "Removal, no document.domain");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ document.domain = document.domain;
+ const { contentWindow } = iframe;
+ assert_equals(contentWindow.status, "");
+ resolve();
+ });
+ iframe.src = "resources/document-domain-setter.html";
+ document.body.append(iframe);
+ });
+}, "No removal, document.domain");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ document.domain = document.domain; // technically we already did this above
+ const { contentWindow } = iframe;
+ iframe.remove();
+ assert_equals(contentWindow.status, "");
+ resolve();
+ });
+ iframe.src = "resources/document-domain-setter.html";
+ document.body.append(iframe);
+ });
+}, "Removal, document.domain");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/embedded-opener-a-form.html b/testing/web-platform/tests/html/browsers/windows/embedded-opener-a-form.html
new file mode 100644
index 0000000000..e1ec760b92
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/embedded-opener-a-form.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>opener and embedded documents; using a and form</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe name=matchesastring></iframe>
+<a href=/common/blank.html target=matchesastring>&lt;a></a>
+<form action=/common/blank.html target=matchesastring><input type=submit value="<form>"></form>
+<script>
+async_test(t => {
+ const frame = document.querySelector("iframe");
+ let counter = 0;
+ frame.onload = t.step_func(() => {
+ // Firefox and Chrome/Safari load differently
+ if (frame.contentWindow.location.href === "about:blank") {
+ return;
+ }
+
+ // Test bits
+ assert_equals(frame.contentWindow.opener, null);
+ if (counter === 0) {
+ document.querySelector("input").click();
+ } else {
+ t.done();
+ }
+ counter++;
+ });
+ document.querySelector("a").click();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/embedded-opener-remove-frame.html b/testing/web-platform/tests/html/browsers/windows/embedded-opener-remove-frame.html
new file mode 100644
index 0000000000..a66f52e5f6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/embedded-opener-remove-frame.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<title>opener and discarded browsing contexts</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe name=matchesastring></iframe>
+<script>
+function testOpener(t, otherW, thisW, discardOtherBC, isDiscardedFromATask) {
+ assert_equals(otherW.opener, thisW, "opener before removal");
+
+ const openerDesc = Object.getOwnPropertyDescriptor(otherW, "opener"),
+ openerGet = openerDesc.get;
+
+ assert_equals(openerGet(), thisW, "opener before removal via directly invoking the getter");
+ discardOtherBC();
+ if (isDiscardedFromATask) {
+ t.step_timeout(() => {
+ testOpenerRemainder(t, otherW, openerDesc, openerGet);
+ }, 250);
+ } else {
+ testOpenerRemainder(t, otherW, openerDesc, openerGet);
+ }
+}
+
+function testOpenerRemainder(t, otherW, openerDesc, openerGet) {
+ assert_equals(otherW.opener, null, "opener after removal");
+ assert_equals(openerGet(), null, "opener after removal via directly invoking the getter");
+
+ otherW.opener = null;
+ assert_equals(openerGet(), null, "opener after setting it null via directly invoking the getter");
+ const openerDescNull = Object.getOwnPropertyDescriptor(otherW, "opener");
+ assert_not_equals(openerDescNull, openerDesc);
+ assert_object_equals(openerDescNull, openerDesc);
+
+ otherW.opener = "immaterial";
+ assert_equals(openerGet(), null, "opener after setting it \"immaterial\" via directly invoking the getter");
+ const openerDescImmaterial = Object.getOwnPropertyDescriptor(otherW, "opener");
+ assert_equals(openerDescImmaterial.value, "immaterial");
+ assert_true(openerDescImmaterial.writable);
+ assert_true(openerDescImmaterial.enumerable);
+ assert_true(openerDescImmaterial.configurable);
+
+ t.done();
+}
+
+async_test(t => {
+ const frame = document.querySelector("iframe"),
+ frameW = frame.contentWindow;
+ frame.onload = t.step_func(() => {
+ // Firefox and Chrome/Safari load differently
+ if (frame.contentWindow.location.href === "about:blank") {
+ return;
+ }
+
+ testOpener(t, frameW, window, () => frame.remove(), false);
+ });
+ window.open("/common/blank.html", "matchesastring");
+}, "opener of discarded nested browsing context");
+
+async_test(t => {
+ const popupW = window.open("/common/blank.html");
+ popupW.onload = t.step_func(() => {
+ testOpener(t, popupW, window, () => popupW.close(), true);
+ });
+}, "opener of discarded auxiliary browsing context");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/embedded-opener.html b/testing/web-platform/tests/html/browsers/windows/embedded-opener.html
new file mode 100644
index 0000000000..8e02664342
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/embedded-opener.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>opener and embedded documents; using window.open()</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe name=matchesastring></iframe>
+<script>
+async_test(t => {
+ const frame = document.querySelector("iframe");
+ frame.onload = t.step_func(() => {
+ // Firefox and Chrome/Safari load differently
+ if (frame.contentWindow.location.href === "about:blank") {
+ return;
+ }
+
+ // Test bits
+ assert_equals(frame.contentWindow.opener, window, "opener before setting it to null");
+
+ const openerDesc = Object.getOwnPropertyDescriptor(frame.contentWindow, "opener"),
+ openerGet = openerDesc.get;
+
+ assert_equals(openerGet(), window, "opener before setting it to null via directly invoking the getter");
+ frame.contentWindow.opener = null;
+ frame.contentWindow.opener = "immaterial";
+ assert_equals(openerGet(), null, "opener after setting it to null via directly invoking the getter");
+ assert_equals(frame.contentWindow.opener, "immaterial");
+
+ t.done();
+ });
+ window.open("/common/blank.html", "matchesastring");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-print.sub.html b/testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-print.sub.html
new file mode 100644
index 0000000000..d07db13776
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-print.sub.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<link rel=match href="iframe-nested-print-ref.html">
+<style>
+ body { margin: 0 }
+</style>
+<iframe frameborder=0 scrolling=no src="//{{hosts[alt][www]}}:{{ports[http][0]}}{{location[path]}}/../resources/iframe-nested-cross-origin.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-scaled-print.sub.html b/testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-scaled-print.sub.html
new file mode 100644
index 0000000000..2442563082
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/iframe-cross-origin-scaled-print.sub.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<link rel=match href="iframe-nested-scaled-print-ref.html">
+<style>
+ body { margin: 0 }
+ div {
+ transform-origin: top left;
+ transform: scale(2);
+ overflow: hidden;
+ }
+ iframe {
+ width: 100px;
+ height: 50px;
+ }
+</style>
+<div>
+<iframe frameborder=0 scrolling=no src="//{{hosts[alt][www]}}:{{ports[http][0]}}{{location[path]}}/../resources/iframe-nested-printing-pass.html"></iframe>
+</div>
diff --git a/testing/web-platform/tests/html/browsers/windows/iframe-nested-print-ref.html b/testing/web-platform/tests/html/browsers/windows/iframe-nested-print-ref.html
new file mode 100644
index 0000000000..c36c459881
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/iframe-nested-print-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<style>
+ body { margin: 0 }
+ p {
+ /* 2 times the default margin of <body>, to account for the doubly-nested document */
+ margin: 16px;
+ }
+</style>
+<p>PASS
diff --git a/testing/web-platform/tests/html/browsers/windows/iframe-nested-print.html b/testing/web-platform/tests/html/browsers/windows/iframe-nested-print.html
new file mode 100644
index 0000000000..844f6ba373
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/iframe-nested-print.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<link rel=match href="iframe-nested-print-ref.html">
+<style>
+ body { margin: 0 }
+</style>
+<iframe frameborder=0 scrolling=no srcdoc="<iframe scrolling=no frameborder=0 srcdoc='PASS'></iframe>"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/iframe-nested-scaled-print-ref.html b/testing/web-platform/tests/html/browsers/windows/iframe-nested-scaled-print-ref.html
new file mode 100644
index 0000000000..fceaf1e52c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/iframe-nested-scaled-print-ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<style>
+ body { margin: 0 }
+ div {
+ transform-origin: top left;
+ transform: scale(2);
+ }
+ iframe {
+ width: 100px;
+ height: 50px;
+ }
+</style>
+<div>
+<iframe frameborder=0 scrolling=no src="resources/iframe-nested-printing-pass.html"></iframe>
+</div>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement-siblings.sub.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement-siblings.sub.html
new file mode 100644
index 0000000000..0c814d26d9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement-siblings.sub.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>window.frameElement access to a same-origin-domain sibling</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="//{{hosts[][]}}:{{ports[http][0]}}/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessed.html"></iframe>
+<iframe src="//{{hosts[][www]}}:{{ports[http][0]}}/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessor.html"></iframe>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ promise_test(async () => {
+ frames[1].postMessage({}, "*");
+ const result = await waitForMessage();
+
+ assert_equals(result, "SecurityError");
+ }, "it must give a \"SecurityError\" DOMException if the pages are different-origin domain");
+
+ promise_test(async () => {
+ document.domain = document.domain;
+
+ frames[0].postMessage({ newDocumentDomain: document.domain }, "*");
+ assert_equals(await waitForMessage(), "done");
+
+ frames[1].postMessage({ newDocumentDomain: document.domain }, "*");
+ const result = await waitForMessage();
+
+ assert_equals(result, "HTMLIFrameElement");
+ }, "it must return the iframe element if the pages are same-origin domain");
+
+ done();
+};
+
+function waitForMessage() {
+ return new Promise(resolve => {
+ window.addEventListener("message", e => resolve(e.data), { once: true });
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html
new file mode 100644
index 0000000000..7ea1182081
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: window.frameElement</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <!-- t1 (same-origin)-->
+ <iframe id="iframe_0"></iframe>
+ <iframe id="iframe_1" src="./resources/frameElement-nested-frame.html"></iframe>
+ <object id="object_id" name="object_name" type="text/html" data="about:blank"></object>
+ <embed id="embed_id" name="embed_name" type="image/svg+xml" src="/images/green.svg" />
+
+ <!-- t2 (cross-origin) -->
+ <iframe name="iframe_2" src="http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/nested-browsing-contexts/resources/frameElement-nested-frame.html"></iframe>
+
+ <!-- t3 (cross-origin) -->
+ <iframe id="iframe_3" src="http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/nested-browsing-contexts/resources/frameElement-window-post.html"></iframe>
+
+ <script>
+ test(function() {
+ assert_equals(window.frameElement, null,
+ "The frameElement attribute should be null.");
+ }, "The window's frameElement attribute must return null if it is not a nested browsing context");
+
+ var t1 = async_test("The window's frameElement attribute must return its container element if it is a nested browsing context");
+ window.addEventListener("load", t1.step_func_done(function() {
+ assert_equals(frames[0].frameElement, document.getElementById("iframe_0"),
+ "The frameElement attribute should be the first iframe element.");
+ assert_equals(window["object_name"].frameElement, document.getElementById("object_id"),
+ "The frameElement attribute should be the object element.");
+ assert_equals(window["embed_name"].frameElement, document.getElementById("embed_id"),
+ "The frameElement attribute should be the embed element.");
+ assert_equals(document.getElementById("iframe_1").contentWindow[0].frameElement,
+ document.getElementById("iframe_1").contentDocument.getElementById("f1"),
+ "The frameElement attribute should be the frame element in 'resources/frameElement-nested-frame.html'.");
+ }));
+
+ var t2 = async_test("The SecurityError must be thrown if the window accesses to frameElement attribute of a Window which does not have the same effective script origin");
+ window.addEventListener("load", t2.step_func_done(function() {
+ assert_throws_dom("SecurityError", function() {
+ frames["iframe_2"].frameElement;
+ },
+ "The SecurityError exception should be thrown.");
+ }));
+
+ var t3 = async_test("The window's frameElement attribute must return null if the container's document does not have the same effective script origin");
+ window.addEventListener("load", function() {
+ window.addEventListener("message", function(event) {
+ var data = JSON.parse(event.data);
+ if (data.name == "testcase3") {
+ t3.step(function() {
+ assert_equals(data.result, "window.frameElement = null",
+ "The frameElement attribute should be null.");
+ t3.done();
+ });
+ }
+ }, false);
+ document.getElementById("iframe_3").contentWindow.postMessage(null, "*");
+ })
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/name-attribute.window.js b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/name-attribute.window.js
new file mode 100644
index 0000000000..69908af71b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/name-attribute.window.js
@@ -0,0 +1,58 @@
+// META: script=/common/get-host-info.sub.js
+
+[
+ "frame", // This works without <frameset>, so great
+ "iframe",
+ "object",
+ "embed",
+].forEach(element => {
+ [
+ null,
+ "",
+ "initialvalue"
+ ].forEach(initialNameValue => {
+ [
+ "same-origin",
+ "cross-origin"
+ ].forEach(originType => {
+ async_test(t => {
+ const ident = element + initialNameValue + originType,
+ file = `${new URL("resources/post-to-parent.html", location.href).pathname}?ident=${ident}`,
+ child = originType === "same-origin" ? file : `${get_host_info().HTTP_REMOTE_ORIGIN}${file}`,
+ frame = document.createElement(element),
+ expectedNameValue = initialNameValue || "";
+ let state = "set";
+ const listener = t.step_func(e => {
+ if (e.data.ident === ident) {
+ assert_equals(e.data.name, expectedNameValue); // This check is always the same
+ if (state === "set") {
+ frame.setAttribute("name", "meh");
+ state = "remove"
+ e.source.postMessage(null, "*");
+ return;
+ }
+ if (state === "remove") {
+ frame.removeAttribute("name");
+ state = "done";
+ e.source.postMessage(null, "*");
+ return;
+ }
+ if (state === "done") {
+ t.done();
+ }
+ }
+ });
+ frame.setAttribute(element === "object" ? "data" : "src", child);
+ if (initialNameValue !== null) {
+ frame.setAttribute("name", initialNameValue);
+ }
+ t.add_cleanup(() => {
+ self.removeEventListener("message", listener);
+ frame.remove();
+ });
+ self.addEventListener("message", listener);
+ document.body.append(frame);
+ }, `${originType} <${element}${initialNameValue !== null ? ' name=' + initialNameValue : ''}>`);
+ });
+ });
+});
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-nested-frame.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-nested-frame.html
new file mode 100644
index 0000000000..d066b8d4cb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-nested-frame.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<title>HTML Test: child browsing context created by the frame element</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<frameset>
+ <frame id="f1" name="frame">
+</frameset>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessed.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessed.html
new file mode 100644
index 0000000000..15245981ce
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessed.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This page will set its document.domain on request so that its sibling can access it</title>
+
+<h1>I did get loaded</h1>
+
+<script>
+"use strict";
+
+window.onmessage = e => {
+ const { newDocumentDomain } = e.data;
+ document.domain = newDocumentDomain;
+
+ parent.postMessage("done", "*");
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessor.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessor.html
new file mode 100644
index 0000000000..4b4c7a87bb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-sibling-accessor.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This page will attempt to access the frameElement of its sibling and report the results on request</title>
+
+<h1>I did get loaded</h1>
+
+<script>
+"use strict";
+
+window.onmessage = e => {
+ const { newDocumentDomain } = e.data;
+ if (newDocumentDomain) {
+ document.domain = newDocumentDomain;
+ }
+
+ const siblingWindow = parent.frames[0];
+
+ try {
+ const { frameElement } = siblingWindow;
+
+ let result = "something wierd happened";
+ if (frameElement === null) {
+ result = "null";
+ } else if (frameElement.constructor) {
+ result = frameElement.constructor.name;
+ }
+
+ parent.postMessage(result, "*");
+ } catch (e) {
+ parent.postMessage(e.name, "*");
+ }
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-window-post.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-window-post.html
new file mode 100644
index 0000000000..d67bde26f4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/frameElement-window-post.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<title>Testcase 3: frameElement attribute must return null if the container\'s document does not have the same effective script origin</title>
+<script>
+window.addEventListener("message", function (event) {
+ try {
+ var result = "window.frameElement = " + window.frameElement;
+ } catch (e) {
+ result = e.message;
+ }
+ event.source.postMessage(JSON.stringify({name: "testcase3", result: result}),
+ "*");
+}, false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-opener.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-opener.html
new file mode 100644
index 0000000000..65a825f573
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-opener.html
@@ -0,0 +1,7 @@
+<script>
+ if (window.opener) {
+ window.opener.postMessage({
+ "parent_isTop": (window.parent === window)
+ }, "*");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-parent.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-parent.html
new file mode 100644
index 0000000000..302e9d9cc1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/resources/post-to-parent.html
@@ -0,0 +1,6 @@
+<script>
+const ident = new URL(location).searchParams.get("ident"),
+ post = () => parent.postMessage({ name: window.name, ident }, "*");
+onmessage = () => post();
+post();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent-null.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent-null.html
new file mode 100644
index 0000000000..428312086e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent-null.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>window.parent: `null`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ var iframe = document.createElement('iframe');
+ iframe.onload = t.step_func_done(() => {
+ var iWindow = iframe.contentWindow;
+ assert_equals(iWindow.parent, window);
+ document.body.removeChild(iframe);
+ assert_equals(iWindow.parent, null);
+ });
+
+ document.body.appendChild(iframe);
+}, '`window.parent` is null when browsing context container element removed');
+
+async_test(t => {
+ var iframe = document.createElement('iframe');
+ var iframe2, removedEl;
+
+ var testFunc = t.step_func(() => {
+ var frameWindow = iframe.contentWindow;
+ var frame2Window = iframe2.contentWindow;
+
+ assert_equals(frameWindow.parent, window);
+ assert_equals(frame2Window.parent, frameWindow);
+
+ iframe.removeEventListener('load', nestFrame);
+ iframe2.removeEventListener('load', testFunc);
+ removedEl = document.body.removeChild(iframe);
+
+ assert_equals(frameWindow.parent, null);
+ assert_equals(frame2Window.parent, null);
+
+ removedEl.addEventListener('load', t.step_func_done(() => {
+ // reattached iframe's browsing context will report window.parent again
+ assert_equals(removedEl.contentWindow.parent, window);
+ // The following window s are no longer referenced by active browsing contexts
+ assert_equals(frameWindow.parent, null);
+ assert_equals(frame2Window.parent, null);
+ // Per #the-iframe-element, reattaching a removed iframe will result in the
+ // browser creating a new browsing context once again, with a fresh
+ // document in our case, so the second iframe or any other elements
+ // previously added to iframe.contentDocument will be gone
+ assert_equals(removedEl.contentDocument.querySelector('iframe'), null);
+ assert_equals(removedEl.contentDocument.querySelector('hr'), null);
+ }));
+ document.body.appendChild(removedEl);
+ });
+
+ var nestFrame = function () {
+ iframe.contentDocument.body.appendChild(document.createElement('hr'));
+ iframe2 = iframe.contentDocument.createElement('iframe');
+ // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1229707
+ iframe2.src = '/common/blank.html';
+ iframe2.addEventListener('load', testFunc);
+ iframe.contentDocument.body.appendChild(iframe2);
+ };
+
+ iframe.addEventListener('load', nestFrame);
+ document.body.appendChild(iframe);
+}, '`window.parent` null when parent browsing context container removed');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent.html
new file mode 100644
index 0000000000..ef662dca5e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-parent.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>window.parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ test(function() {
+ assert_equals(window, parent)
+ }, '`window.parent` for top-level browsing context');
+
+async_test(t => {
+ var iframe = document.createElement('iframe');
+ iframe.onload = t.step_func_done(function () {
+ var iWindow = iframe.contentWindow;
+ assert_equals(iWindow.parent, window);
+ });
+ document.body.appendChild(iframe);
+}, '`window.parent` on single nested browsing context');
+
+async_test(t => {
+ var iframe = document.createElement('iframe');
+ var iframe2;
+
+ var testFunc = t.step_func_done(function () {
+ var frameWindow = iframe.contentWindow;
+ var frame2Window = iframe2.contentWindow;
+ assert_equals(frameWindow.parent, window);
+ assert_equals(frame2Window.parent, frameWindow);
+ assert_not_equals(frame2Window.parent, window);
+ });
+
+ var nestFrame = function () {
+ iframe2 = iframe.contentDocument.createElement('iframe');
+ // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1229707
+ iframe2.src = '/common/blank.html';
+ iframe2.addEventListener('load', testFunc);
+ iframe.contentDocument.body.appendChild(iframe2);
+ };
+
+ iframe.addEventListener('load', nestFrame);
+ document.body.appendChild(iframe);
+}, '`window.parent` for multiple nested browsing contexts');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top-null.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top-null.html
new file mode 100644
index 0000000000..cf7fcf2ae1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top-null.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>window.top: `null`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function (t) {
+ var iframe = document.createElement('iframe');
+ iframe.onload = t.step_func_done(() => {
+ var iWindow = iframe.contentWindow;
+ /**
+ * `top` should return the top-level browsing context but will return
+ * `null` if none exists, such as when any of the BC's ancestor browsing
+ * context container's document elements are disconnected/removed.
+ */
+ assert_equals(iWindow.top, window);
+ document.body.removeChild(iframe);
+ assert_equals(iWindow.top, null);
+ });
+
+ document.body.appendChild(iframe);
+}, '`window.top` is null when browsing context container element removed');
+
+async_test(t => {
+ var iframe = document.createElement('iframe');
+ var iframe2, removedEl;
+
+ var testFunc = t.step_func(() => {
+ var frameWindow = iframe.contentWindow;
+ var frame2Window = iframe2.contentWindow;
+
+ assert_equals(frameWindow.top, window);
+ assert_equals(frame2Window.top, window);
+
+ iframe.removeEventListener('load', nestFrame);
+ iframe2.removeEventListener('load', testFunc);
+
+ removedEl = document.body.removeChild(iframe);
+
+ assert_equals(frameWindow.top, null);
+ assert_equals(frame2Window.top, null);
+
+ removedEl.addEventListener('load', t.step_func_done(() => {
+ // reattached iframe's browsing context will report window.top
+ assert_equals(removedEl.contentWindow.top, window);
+ // removed/re-added iframes will have new browsing context / window
+ assert_equals(frameWindow.top, null);
+ assert_equals(frame2Window.top, null);
+ }));
+
+ document.body.appendChild(removedEl);
+ });
+
+ var nestFrame = function () {
+ iframe2 = iframe.contentDocument.createElement('iframe');
+ // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1229707
+ iframe2.src = '/common/blank.html';
+ iframe2.addEventListener('load', testFunc);
+ iframe.contentDocument.body.appendChild(iframe2);
+ };
+
+ iframe.addEventListener('load', nestFrame);
+ document.body.appendChild(iframe);
+}, '`window.top`null when any ancestor browsing context container removed');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top.html b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top.html
new file mode 100644
index 0000000000..e5590adeb8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/window-top.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>window.top</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(window, top)
+}, "Top level browsing context");
+
+function step_func(test) {
+ return function (top_pointer) {
+ test.step(function() {assert_equals(top_pointer, window);})
+ test.done();
+ }
+}
+
+var t1 = async_test("One nested iframe");
+t1.step(function() {
+ var iframe = document.createElement("iframe");
+ //iframe.src = "data:text/html,"
+
+ iframe.onload = t1.step_func(
+ function() {
+ var doc = iframe.contentDocument;
+ iframe.contentWindow.test_func = step_func(t1);
+
+ var script = doc.createElement("script")
+ script.textContent = "test_func(top);"
+ doc.body.appendChild(script);
+ });
+ document.body.appendChild(iframe);
+});
+
+var t2 = async_test("Two nested iframes");
+t2.step(function() {
+ var iframe = document.createElement("iframe");
+ //iframe.src = "data:text/html,"
+
+ iframe.onload = t2.step_func(
+ function() {
+ var doc = iframe.contentDocument;
+ iframe2 = document.createElement("iframe");
+ //iframe2.src = "data:text/html,"
+ // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1229707
+ iframe2.src = '/common/blank.html';
+
+ iframe2.onload = t2.step_func(
+ function() {
+ var doc2 = iframe2.contentDocument;
+
+ iframe2.contentWindow.test_func = step_func(t2);
+
+ var script = doc2.createElement("script")
+ script.textContent = "test_func(top);"
+ doc2.body.appendChild(script);
+ });
+ doc.body.appendChild(iframe2);
+ });
+
+ document.body.appendChild(iframe);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-close-manual.sub.html b/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-close-manual.sub.html
new file mode 100644
index 0000000000..8e2740c268
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-close-manual.sub.html
@@ -0,0 +1,3 @@
+<meta charset=utf-8>
+<p>Follow this link to open a new browsing context and then confirm it can be closed:
+<a rel=noreferrer target=reallydoesnotmatter href="//天気ã®è‰¯ã„æ—¥.{{location[host]}}/html/browsers/windows/resources/window-close-button.html">link</a>.
diff --git a/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-manual.html b/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-manual.html
new file mode 100644
index 0000000000..f5879ee6d7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-manual.html
@@ -0,0 +1,10 @@
+<ol>
+ <li><p>After clicking these two links in order a single browsing context should be open showing
+ <code>example.org</code>:
+ <a target=doesnotmatter href="http://example.com/">one</a>,
+ <a target=doesnotmatter href="http://example.org/">two</a>.
+
+ <li><p>After clicking these two links two browsing contexts should have been opened:
+ <a rel=noreferrer target=reallydoesnotmatter href="http://example.com/">one</a>,
+ <a rel=noreferrer target=reallydoesnotmatter href="http://example.com/">two</a>.
+</ol>
diff --git a/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-window-name-manual.sub.html b/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-window-name-manual.sub.html
new file mode 100644
index 0000000000..c598ffbfaa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/noreferrer-cross-origin-window-name-manual.sub.html
@@ -0,0 +1,3 @@
+<meta charset=utf-8>
+<p>Follow this link to open a new browsing context and then confirm it says "idonteven":
+<a rel=noreferrer target=idonteven href="//天気ã®è‰¯ã„æ—¥.{{location[host]}}/html/browsers/windows/resources/echo-window-name.html">link</a>.
diff --git a/testing/web-platform/tests/html/browsers/windows/noreferrer-null-opener.html b/testing/web-platform/tests/html/browsers/windows/noreferrer-null-opener.html
new file mode 100644
index 0000000000..f308abc05e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/noreferrer-null-opener.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>rel=noreferrer nullifies window.opener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ async_test(function(t) {
+ localStorage.clear()
+
+ var hyperlink = document.body.appendChild(document.createElement("a"))
+ hyperlink.rel = "noreferrer"
+ hyperlink.target = "_blank"
+ hyperlink.href = "resources/window-opener.html"
+ hyperlink.click()
+ document.body.removeChild(hyperlink)
+
+ addEventListener("storage", function(e) {
+ t.step(function() {
+ assert_equals(e.newValue, "null")
+ localStorage.clear()
+ t.done()
+ })
+ })
+ })
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/noreferrer-window-name.html b/testing/web-platform/tests/html/browsers/windows/noreferrer-window-name.html
new file mode 100644
index 0000000000..5fe649d172
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/noreferrer-window-name.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<title>rel=noreferrer and reuse of names</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ async_test(function(t) {
+ localStorage.clear()
+
+ function makeHyperlink(n) {
+ var hyperlink = document.body.appendChild(document.createElement("a"))
+ hyperlink.rel = "noreferrer"
+ hyperlink.target = "sufficientlyrandomwindownameamiright"
+ hyperlink.href = "resources/noreferrer-window-name.html#" + n
+ return hyperlink
+ }
+
+ var hyperlink1 = makeHyperlink(1),
+ hyperlink2 = makeHyperlink(2)
+
+ t.add_cleanup(function() {
+ localStorage.setItem("x", "close")
+ localStorage.clear()
+ document.body.removeChild(hyperlink1)
+ document.body.removeChild(hyperlink2)
+ })
+
+ addEventListener("storage", function(e) {
+ t.step(function() {
+ if(localStorage.getItem("window1") && localStorage.getItem("window2")) {
+ localStorage.setItem("x", "close")
+ t.done()
+ }
+ })
+ })
+
+ hyperlink1.click()
+ hyperlink2.click()
+ }, "Following a noreferrer link with a named target should not cause creation of a window that can be targeted by another noreferrer link with the same named target");
+
+ async_test(function(t) {
+ var ifr = document.createElement("iframe");
+ ifr.name = "sufficientlyrandomwindownameamiright2";
+ ifr.onload = t.step_func(function() {
+ var hyperlink = document.body.appendChild(document.createElement("a"));
+ t.add_cleanup(function() {
+ hyperlink.remove();
+ });
+ hyperlink.rel = "noreferrer";
+ hyperlink.href = URL.createObjectURL(new Blob(["hello subframe"],
+ { type: "text/html"}));
+ hyperlink.target = "sufficientlyrandomwindownameamiright2";
+ ifr.onload = t.step_func_done(function() {
+ assert_equals(ifr.contentDocument.documentElement.textContent,
+ "hello subframe");
+ });
+ hyperlink.click();
+ });
+ document.body.appendChild(ifr);
+ t.add_cleanup(function() {
+ ifr.remove();
+ });
+ }, "Targeting a rel=noreferrer link at an existing named subframe should work");
+
+ async_test(function(t) {
+ var win = window.open("", "sufficientlyrandomwindownameamiright3");
+ t.add_cleanup(function() {
+ win.close();
+ });
+
+ var hyperlink = document.body.appendChild(document.createElement("a"));
+ t.add_cleanup(function() {
+ hyperlink.remove();
+ });
+ hyperlink.rel = "noreferrer";
+ hyperlink.href = URL.createObjectURL(new Blob(["hello window"],
+ { type: "text/html"}));
+ hyperlink.target = "sufficientlyrandomwindownameamiright3";
+ win.onload = t.step_func_done(function() {
+ assert_equals(win.document.documentElement.textContent,
+ "hello window");
+ });
+ hyperlink.click();
+ }, "Targeting a rel=noreferrer link at an existing named window should work");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/opener-cross-origin-manual.sub.html b/testing/web-platform/tests/html/browsers/windows/opener-cross-origin-manual.sub.html
new file mode 100644
index 0000000000..0c018663a2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/opener-cross-origin-manual.sub.html
@@ -0,0 +1,10 @@
+<ol>
+ <li><p>Clicking this link must navigate this page to a resource that contains "THE END":
+ <a href=//{{domains[www1]}}:{{location[port]}}/html/browsers/windows/resources/opener-cross-origin.html target=_blank>test</a>
+
+ <li><p>Clicking this link must open a new browsing context that is empty:
+ <a rel=noreferrer href=//{{domains[www1]}}:{{location[port]}}/html/browsers/windows/resources/opener-cross-origin.html target=_blank>test</a>
+
+ <li><p>Clicking this link must navigate this page to a resource that contains "THE END":
+ <a href=//{{domains[www1]}}:{{location[port]}}/html/browsers/windows/resources/opener-cross-origin-embed.sub.html target=_blank>test</a>
+</ol>
diff --git a/testing/web-platform/tests/html/browsers/windows/opener-string.window.js b/testing/web-platform/tests/html/browsers/windows/opener-string.window.js
new file mode 100644
index 0000000000..a39c584d20
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/opener-string.window.js
@@ -0,0 +1,14 @@
+test(t => {
+ const popup = window.open();
+ t.add_cleanup(() => popup.close());
+ assert_equals(popup.opener, self, "The opener of the popup is me");
+ assert_equals(Object.getOwnPropertyDescriptor(popup, "opener").writable, undefined);
+
+ popup.opener = "blah";
+ assert_equals(popup.opener, "blah", "The popup's opener is now a string");
+ assert_equals(Object.getOwnPropertyDescriptor(popup, "opener").writable, true);
+
+ const openerGetter = Object.getOwnPropertyDescriptor(self, "opener").get;
+ const popupOpener = openerGetter.call(popup);
+ assert_equals(popupOpener, self, "The underlying opener of the popup is still me");
+}, "Setting popup.opener to a string");
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-cross-partition.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-cross-partition.sub.html
new file mode 100644
index 0000000000..f91d9403ea
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-cross-partition.sub.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: First-Party to First-Party, Cross-Partition</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window) set up listener and open Site 2 Window.
+// Step 2. (Site 2 Window) send "Site 2 Window" message to Site 1 Window.
+// Step 3. (Site 1 Window) receive "Site 2 Window" message and exit.
+
+async_test(t => {
+ // Step 3
+ const listener = t.step_func(e => {
+ if (e.data === "Site 2 Window") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2Window = window.open("http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/first-party-to-first-party-cross-partition-window.html", "", "noopener=false");
+ t.add_cleanup(() => site2Window.close());
+}, "postMessage: First-Party to First-Party, Cross-Partition");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-same-partition.html b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-same-partition.html
new file mode 100644
index 0000000000..6fc915298b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-first-party-same-partition.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: First-Party to First-Party, Same-Partition</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window A) set up listener and open Site 1 Window B.
+// Step 2. (Site 1 Window B) send "Site 1 Window B" message to Site 1 Window A.
+// Step 3. (Site 1 Window A) receive "Site 1 Window B" message and exit.
+
+async_test(t => {
+ // Step 3
+ const listener = t.step_func(e => {
+ if (e.data === "Site 1 Window B") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site1WindowB = window.open("/html/browsers/windows/post-message/resources/first-party-to-first-party-same-partition-window.html", "", "noopener=false");
+ t.add_cleanup(() => site1WindowB.close());
+}, "postMessage: First-Party to First-Party, Same-Partition");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-cross-origin.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-cross-origin.sub.html
new file mode 100644
index 0000000000..de776f8381
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-cross-origin.sub.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: First-Party to Third-Party, Cross-Partition, Cross-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window) set up listener and open Site 2 Window.
+// Step 2. (Site 2 Window) open Site 3 Frame.
+// Step 3. (Site 3 Frame) send "Site 3 Frame" message to Site 1 Window.
+// Step 4. (Site 1 Window) receive "Site 1 Frame" message and exit.
+
+async_test(t => {
+ // Step 4
+ const listener = t.step_func(e => {
+ if (e.data === "Site 3 Frame") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2Window = window.open("https://{{hosts[alt][]}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-window.sub.https.html", "", "noopener=false");
+ t.add_cleanup(() => site2Window.close());
+}, "postMessage: First-Party to Third-Party, Cross-Partition, Cross-Origin");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-same-origin.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-same-origin.sub.html
new file mode 100644
index 0000000000..490b647fa4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/first-party-to-third-party-cross-partition-same-origin.sub.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: First-Party to Third-Party, Cross-Partition, Same-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window) set up listener and open Site 2 Window.
+// Step 2. (Site 2 Window) open Site 1 Frame.
+// Step 3. (Site 1 Frame) send "Site 1 Frame" message to Site 1 Window.
+// Step 4. (Site 1 Window) receive "Site 1 Frame" message and exit.
+
+async_test(t => {
+ // Step 4
+ const listener = t.step_func(e => {
+ if (e.data === "Site 1 Frame") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2Window = window.open("http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-window.sub.html", "", "noopener=false");
+ t.add_cleanup(() => site2Window.close());
+}, "postMessage: First-Party to Third-Party, Cross-Partition, Same-Origin");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-cross-partition-window.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-cross-partition-window.html
new file mode 100644
index 0000000000..cdfa9d95ca
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-cross-partition-window.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 2 (html/browsers/windows/post-message/first-party-to-first-party-cross-partition.sub.html)
+window.opener.postMessage("Site 2 Window", "*");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-same-partition-window.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-same-partition-window.html
new file mode 100644
index 0000000000..4baf45d3c8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-first-party-same-partition-window.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 2 (html/browsers/windows/post-message/first-party-to-first-party-same-partition.html)
+window.opener.postMessage("Site 1 Window B", window.opener.origin);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-iframe.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-iframe.https.html
new file mode 100644
index 0000000000..eef594831a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-iframe.https.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 3 (html/browsers/windows/post-message/first-party-to-third-party-cross-partition-cross-origin.sub.html)
+window.top.opener.postMessage("Site 3 Frame", "*");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-window.sub.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-window.sub.https.html
new file mode 100644
index 0000000000..7edfbd9328
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-window.sub.https.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<!--Step 2 (html/browsers/windows/post-message/first-party-to-third-party-cross-partition-cross-origin.sub.html)-->
+<iframe src="https://{{host}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-cross-origin-iframe.https.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-iframe.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-iframe.html
new file mode 100644
index 0000000000..eb17036c8c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-iframe.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 3 (html/browsers/windows/post-message/first-party-to-third-party-cross-partition-same-origin.sub.html)
+window.top.opener.postMessage("Site 1 Frame", window.top.opener.origin);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-window.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-window.sub.html
new file mode 100644
index 0000000000..f99b96c466
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-window.sub.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<!--Step 2 (html/browsers/windows/post-message/first-party-to-third-party-cross-partition-same-origin.sub.html)-->
+<iframe src="http://{{host}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/first-party-to-third-party-cross-partition-same-origin-iframe.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-iframe.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-iframe.sub.html
new file mode 100644
index 0000000000..348efb3f9c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-iframe.sub.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 4 (html/browsers/windows/post-message/third-party-to-first-party-cross-partition-cross-origin.sub.html)
+let site2Window;
+const listener = e => {
+ if (e.data === "Site 3 Window") {
+ site2Window.close();
+ window.top.postMessage("Site 2 Frame", "*");
+ }
+};
+// Step 2 (html/browsers/windows/post-message/third-party-to-first-party-cross-partition-cross-origin.sub.html)
+window.addEventListener("message", listener);
+site2Window = window.open("https://{{hosts[alt][]}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-window.https.html", "", "noopener=false");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-window.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-window.https.html
new file mode 100644
index 0000000000..0045909494
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-window.https.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 3 (html/browsers/windows/post-message/third-party-to-first-party-cross-partition-cross-origin.sub.html)
+window.opener.postMessage("Site 3 Window", "*");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-iframe.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-iframe.sub.html
new file mode 100644
index 0000000000..405b1053d3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-iframe.sub.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 4 (html/browsers/windows/post-message/third-party-to-first-party-cross-partition-same-origin.sub.html)
+let site2Window;
+const listener = e => {
+ if (e.data === "Site 2 Window") {
+ site2Window.close();
+ window.top.postMessage("Site 2 Frame", "*");
+ }
+};
+// Step 2 (html/browsers/windows/post-message/third-party-to-first-party-cross-partition-same-origin.sub.html)
+window.addEventListener("message", listener);
+site2Window = window.open("http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-window.html", "", "noopener=false");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-window.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-window.html
new file mode 100644
index 0000000000..0ae51e3fc1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-window.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 3 (html/browsers/windows/post-message/third-party-to-first-party-cross-partition-same-origin.sub.html)
+window.opener.postMessage("Site 2 Window", window.opener.origin);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-a.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-a.sub.html
new file mode 100644
index 0000000000..a11f3640e8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-a.sub.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 5 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html)
+let site3Window;
+const listener = e => {
+ if (e.data === "Site 4 Frame") {
+ site3Window.close();
+ window.top.postMessage("Site 2 Frame", "*");
+ }
+};
+// Step 2 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html)
+window.addEventListener("message", listener);
+site3Window = window.open("https://{{host}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-window.sub.https.html", "", "noopener=false");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-b.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-b.https.html
new file mode 100644
index 0000000000..f56e2b35d8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-b.https.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 4 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html)
+window.top.opener.postMessage("Site 4 Frame", "*");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-window.sub.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-window.sub.https.html
new file mode 100644
index 0000000000..1307287587
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-window.sub.https.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<!--Step 3 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html)-->
+<iframe src="https://{{hosts[alt][]}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-b.https.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-a.sub.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-a.sub.https.html
new file mode 100644
index 0000000000..ec551bfdec
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-a.sub.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 5 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html)
+let site3Window;
+const listener = e => {
+ if (e.data === "Site 2 Frame B") {
+ site3Window.close();
+ window.top.postMessage("Site 2 Frame A", "*");
+ }
+};
+// Step 2 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html)
+window.addEventListener("message", listener);
+site3Window = window.open("https://{{host}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-window.sub.https.html", "", "noopener=false");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-b.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-b.https.html
new file mode 100644
index 0000000000..fc15b1f125
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-b.https.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 4 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html)
+window.top.opener.postMessage("Site 2 Frame B", window.top.opener.origin);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-window.sub.https.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-window.sub.https.html
new file mode 100644
index 0000000000..f26f709db3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-window.sub.https.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<!--Step 3 (html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html)-->
+<iframe src="https://{{hosts[alt][]}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-b.https.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-a.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-a.sub.html
new file mode 100644
index 0000000000..339dc9f06e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-a.sub.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 5 (html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html)
+let site2WindowB;
+const listener = e => {
+ if (e.data === "Site 2 Frame B") {
+ site2WindowB.close();
+ window.top.postMessage("Site 2 Frame A", "*");
+ }
+};
+// Step 2 (html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html)
+window.addEventListener("message", listener);
+site2WindowB = window.open("http://{{host}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-window.sub.html", "", "noopener=false");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-b.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-b.html
new file mode 100644
index 0000000000..daaf236bfc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-b.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<script>
+// Step 4 (html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html)
+window.top.opener.postMessage("Site 2 Frame B", window.top.opener.origin);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-window.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-window.sub.html
new file mode 100644
index 0000000000..7cea58f235
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-window.sub.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<body>
+<!--Step 3 (html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html)-->
+<iframe src="http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-b.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-cross-origin.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-cross-origin.sub.html
new file mode 100644
index 0000000000..2618e966de
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-cross-origin.sub.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: Third-Party to First-Party, Cross-Partition, Cross-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window) set up listener and open Site 2 Frame.
+// Step 2. (Site 2 Frame) set up listener and open Site 3 Window.
+// Step 3. (Site 3 Window) send "Site 3 Window" message to Site 2 Frame.
+// Step 4. (Site 2 Frame) receive "Site 3 Window" message and send "Site 2 Frame" message to Site 1 Window.
+// Step 5. (Site 1 Window) receive "Site 2 Frame" message and exit.
+
+async_test(t => {
+ // Step 5
+ const listener = t.step_func(e => {
+ if (e.data === "Site 2 Frame") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2Frame = document.createElement("iframe");
+ site2Frame.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-cross-origin-iframe.sub.html";
+ document.body.appendChild(site2Frame);
+}, "postMessage: Third-Party to First-Party, Cross-Partition, Cross-Origin");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-same-origin.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-same-origin.sub.html
new file mode 100644
index 0000000000..735b458841
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-first-party-cross-partition-same-origin.sub.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: Third-Party to First-Party, Cross-Partition, Same-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window) set up listener and open Site 2 Frame.
+// Step 2. (Site 2 Frame) set up listener and open Site 2 Window.
+// Step 3. (Site 2 Window) send "Site 2 Window" message to Site 2 Frame.
+// Step 4. (Site 2 Frame) receive "Site 2 Window" message and send "Site 2 Frame" message to Site 1 Window.
+// Step 5. (Site 1 Window) receive "Site 2 Frame" message and exit.
+
+async_test(t => {
+ // Step 5
+ const listener = t.step_func(e => {
+ if (e.data === "Site 2 Frame") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2Frame = document.createElement("iframe");
+ site2Frame.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/third-party-to-first-party-cross-partition-same-origin-iframe.sub.html";
+ document.body.appendChild(site2Frame);
+}, "postMessage: Third-Party to First-Party, Cross-Partition, Same-Origin");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html
new file mode 100644
index 0000000000..1083071f7d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-cross-origin.sub.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: Third-Party to Third-Party, Cross-Partition, Cross-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window) set up listener and open Site 2 Frame.
+// Step 2. (Site 2 Frame) set up listener and open Site 3 Window.
+// Step 3. (Site 3 Window) open Site 4 Frame.
+// Step 4. (Site 4 Frame) send "Site 4 Frame" message to Site 2 Frame.
+// Step 5. (Site 2 Frame) receive "Site 4 Frame" message and send "Site 2 Frame" message to Site 1 Window.
+// Step 6. (Site 1 Window) receive "Site 2 Frame" message and exit.
+
+async_test(t => {
+ // Step 6
+ const listener = t.step_func(e => {
+ if (e.data === "Site 2 Frame") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2FrameA = document.createElement("iframe");
+ site2FrameA.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-cross-origin-iframe-a.sub.html";
+ document.body.appendChild(site2FrameA);
+}, "postMessage: Third-Party to Third-Party, Cross-Partition, Cross-Origin");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html
new file mode 100644
index 0000000000..9caf6c11e4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-cross-partition-same-origin.sub.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: Third-Party to Third-Party, Cross-Partition, Same-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window) set up listener and open Site 2 Frame A.
+// Step 2. (Site 2 Frame A) set up listener and open Site 3 Window.
+// Step 3. (Site 3 Window) open Site 2 Frame B.
+// Step 4. (Site 2 Frame B) send "Site 2 Frame B" message to Site 2 Frame A.
+// Step 5. (Site 2 Frame A) receive "Site 2 Frame B" message and send "Site 2 Frame A" message to Site 1 Window.
+// Step 6. (Site 1 Window) receive "Site 2 Frame A" message and exit.
+
+async_test(t => {
+ // Step 6
+ const listener = t.step_func(e => {
+ if (e.data === "Site 2 Frame A") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2FrameA = document.createElement("iframe");
+ site2FrameA.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-cross-partition-same-origin-iframe-a.sub.https.html";
+ document.body.appendChild(site2FrameA);
+}, "postMessage: Third-Party to Third-Party, Cross-Partition, Same-Origin");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html
new file mode 100644
index 0000000000..c90a055268
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/post-message/third-party-to-third-party-same-partition.sub.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>postMessage: Third-Party to Third-Party, Same-Partition</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (Site 1 Window A) set up listener and open Site 2 Frame A.
+// Step 2. (Site 2 Frame A) set up listener and open Site 1 Window B.
+// Step 3. (Site 1 Window B) open Site 2 Frame B.
+// Step 4. (Site 2 Frame B) send "Site 2 Frame B" message to Site 2 Frame A.
+// Step 5. (Site 2 Frame A) receive "Site 2 Frame B" message and send "Site 2 Frame A" message to Site 1 Window A.
+// Step 6. (Site 1 Window A) receive "Site 2 Frame A" message and exit.
+
+async_test(t => {
+ // Step 6
+ const listener = t.step_func(e => {
+ if (e.data === "Site 2 Frame A") {
+ t.done();
+ }
+ });
+ // Step 1
+ window.addEventListener("message", listener);
+ const site2FrameA = document.createElement("iframe");
+ site2FrameA.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/browsers/windows/post-message/resources/third-party-to-third-party-same-partition-iframe-a.sub.html";
+ document.body.appendChild(site2FrameA);
+}, "postMessage: Third-Party to Third-Party, Same-Partition");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/browsing-context-window.html b/testing/web-platform/tests/html/browsers/windows/resources/browsing-context-window.html
new file mode 100644
index 0000000000..c1594f637e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/browsing-context-window.html
@@ -0,0 +1,7 @@
+<script>
+if (window.parent) {
+ window.parent.postMessage({
+ "thisWindowEquivalency": (window === this)
+ }, "*");
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/document-domain-setter.html b/testing/web-platform/tests/html/browsers/windows/resources/document-domain-setter.html
new file mode 100644
index 0000000000..3b14255571
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/document-domain-setter.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Helper page that sets document.domain</title>
+<script>
+"use strict";
+document.domain = document.domain;
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/echo-window-name.html b/testing/web-platform/tests/html/browsers/windows/resources/echo-window-name.html
new file mode 100644
index 0000000000..a437fecb2c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/echo-window-name.html
@@ -0,0 +1 @@
+<script>document.write(name)</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-cross-origin.html b/testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-cross-origin.html
new file mode 100644
index 0000000000..a1a66ed842
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-cross-origin.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<iframe scrolling=no frameborder=0 srcdoc='PASS'></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-printing-pass.html b/testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-printing-pass.html
new file mode 100644
index 0000000000..c2dca9185f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/iframe-nested-printing-pass.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<style>
+body {
+ margin: calc(0.5px * 2 / 3);
+}
+</style>
+<body>
+PASS
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/message-parent.html b/testing/web-platform/tests/html/browsers/windows/resources/message-parent.html
new file mode 100644
index 0000000000..ba60618ad4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/message-parent.html
@@ -0,0 +1,5 @@
+<script>
+ window.parent.postMessage({
+ "name": window.name,
+ }, "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/nested-post-to-opener.html b/testing/web-platform/tests/html/browsers/windows/resources/nested-post-to-opener.html
new file mode 100644
index 0000000000..e92b69d7e7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/nested-post-to-opener.html
@@ -0,0 +1,12 @@
+<body>
+<script>
+ var i = document.createElement("iframe");
+ i.name = "nested1";
+ document.body.appendChild(i);
+
+ window.opener.postMessage({
+ "name": window.name,
+ "isTop": window.top === window
+ }, "*");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/noreferrer-window-name.html b/testing/web-platform/tests/html/browsers/windows/resources/noreferrer-window-name.html
new file mode 100644
index 0000000000..8c106ca886
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/noreferrer-window-name.html
@@ -0,0 +1,8 @@
+<script>
+ addEventListener("storage", function(e) {
+ if(e.newValue === "close") {
+ close()
+ }
+ })
+ localStorage.setItem("window" + location.hash.slice(1), "tralala")
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-embed.sub.html b/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-embed.sub.html
new file mode 100644
index 0000000000..db0c003141
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-embed.sub.html
@@ -0,0 +1,2 @@
+<meta charset=utf-8>
+<iframe src=//{{domains[élève]}}:{{location[port]}}/html/browsers/windows/resources/opener-cross-origin.html></iframe>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-end.txt b/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-end.txt
new file mode 100644
index 0000000000..8b74fbab8a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin-end.txt
@@ -0,0 +1 @@
+THE END
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin.html b/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin.html
new file mode 100644
index 0000000000..7c536b9213
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/opener-cross-origin.html
@@ -0,0 +1,4 @@
+<script>
+ parent.opener.location.href = "./opener-cross-origin-end.txt"
+ parent.window.close()
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/post-to-opener.html b/testing/web-platform/tests/html/browsers/windows/resources/post-to-opener.html
new file mode 100644
index 0000000000..453fec97a7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/post-to-opener.html
@@ -0,0 +1,8 @@
+<script>
+ if (window.opener) {
+ window.opener.postMessage({
+ "name": window.name,
+ "isTop": window.top === window
+ }, "*");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/restore-window-name-back.sub.html b/testing/web-platform/tests/html/browsers/windows/resources/restore-window-name-back.sub.html
new file mode 100644
index 0000000000..ea2c3a92b4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/restore-window-name-back.sub.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+ onload = () => {
+ window.name = "clear";
+ history.back();
+ };
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/restore-window-name.sub.html b/testing/web-platform/tests/html/browsers/windows/resources/restore-window-name.sub.html
new file mode 100644
index 0000000000..a1e573a7c0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/restore-window-name.sub.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+<head>
+<title>popup helper for restore window.name test</title>
+<script>
+
+const search = window.location.search.replace("?", "");
+const name = search.split("=")[1];
+
+if (!search.startsWith("name=")) {
+ throw new Error("Unsupported test!");
+}
+
+function startTest() {
+ if (window.name === "start") {
+ window.name = name;
+ const url = new URL(window.location);
+ url.host = "{{hosts[alt][]}}:{{ports[https][0]}}";
+ url.pathname = url.pathname.slice(0, url.pathname.lastIndexOf("/") + 1);
+ url.pathname += "restore-window-name-back.sub.html";
+ window.location = url.href;
+ }
+}
+
+function verifyResult() {
+ const result = document.getElementById("result");
+ if (window.name === "start") {
+ result.textContent = "Please first click the above 'start test' link.";
+ return;
+ }
+
+ result.textContent = window.name === name ? "PASS" : "FAIL";
+}
+
+</script>
+</head>
+<body>
+ <p>Please first click the 'start test' link. And then, click the 'Verify Result' button to verify the result. You should get a PASS after clicking the button</p>
+ <a id="navigate" href="javascript:void(0)" onclick="startTest()">start test</a><br>
+ <button onclick="verifyResult()">Verify Result</button>
+ Result: <a id="result"></a>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/target-cross-origin.sub.html b/testing/web-platform/tests/html/browsers/windows/resources/target-cross-origin.sub.html
new file mode 100644
index 0000000000..8472f96b90
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/target-cross-origin.sub.html
@@ -0,0 +1,3 @@
+<meta charset=utf-8>
+<p>Follow this link to open a new browsing context in a separate origin.
+<a target=second href="{{location[scheme]}}://{{domains[www2]}}:{{location[port]}}/html/browsers/windows/resources/target-cross-origin.sub.html">link</a>.
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/window-close-button.html b/testing/web-platform/tests/html/browsers/windows/resources/window-close-button.html
new file mode 100644
index 0000000000..38ec2aef5c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/window-close-button.html
@@ -0,0 +1 @@
+<p>Clicking this button should close this browsing context: <button onclick=window.close()>button</button>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/window-name-stash.py b/testing/web-platform/tests/html/browsers/windows/resources/window-name-stash.py
new file mode 100644
index 0000000000..411a4587bc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/window-name-stash.py
@@ -0,0 +1,13 @@
+def main(request, response):
+ key = request.GET.first(b"id")
+ if request.method == "POST":
+ value = request.GET.first(b"value")
+ request.server.stash.take(key)
+ request.server.stash.put(key, value)
+ return b"OK"
+ else:
+ value = request.server.stash.take(key)
+ if value is not None:
+ return value
+ else:
+ return b"NONE"
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/window-name.sub.html b/testing/web-platform/tests/html/browsers/windows/resources/window-name.sub.html
new file mode 100644
index 0000000000..9255ba3401
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/window-name.sub.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<title>popup helper</title>
+<script>
+
+const search = decodeURIComponent(window.location.search.replace("?", ""));
+const steps = search.split("|");
+
+async function proceedTest() {
+ while (steps.length) {
+ const step = steps.shift();
+
+ if (step.startsWith("report=")) {
+ const id = step.split("=")[1];
+ const stashURL = new URL("window-name-stash.py", location);
+ stashURL.searchParams.set('id', id);
+ stashURL.searchParams.set('value', window.name);
+
+ await fetch(stashURL, { method: "POST" });
+ continue;
+ }
+
+ if (step === "close") {
+ window.close();
+ break;
+ }
+
+ if (step === "cross") {
+ const url = new URL(window.location);
+ url.host = "{{hosts[alt][]}}:{{ports[https][0]}}";
+ url.search = "?" + steps.join("|");
+ window.location = url.href;
+ break;
+ }
+
+ if (step === "same") {
+ const url = new URL(window.location);
+ url.host = "{{host}}:{{ports[https][0]}}";
+ url.search = "?" + steps.join("|");
+ window.location = url.href;
+ break;
+ }
+
+ if (step === "sub") {
+ const url = new URL(window.location);
+ url.host = "{{hosts[][www]}}:{{ports[https][0]}}";
+ url.search = "?" + steps.join("|");
+ window.location = url.href;
+ break;
+ }
+
+ if (step === "closeOpener") {
+ if (window.opener) {
+ window.opener.close();
+ }
+ continue;
+ }
+
+ if (step.startsWith("navOpener=")) {
+ if (!window.opener) {
+ continue;
+ }
+
+ let url = step.split("=")[1];
+ window.opener.location.href = url;
+
+ continue;
+ }
+
+ if (step === "open") {
+ const url = new URL(window.location);
+ url.host = "{{host}}:{{ports[https][0]}}";
+ url.search = "?" + steps.join("|");
+ window.open(url);
+ break;
+ }
+
+ if (step.startsWith("reportOpener=")) {
+ const id = step.split("=")[1];
+ const stashURL = new URL("window-name-stash.py", location);
+ stashURL.searchParams.set('id', id);
+ stashURL.searchParams.set('value', window.opener.name);
+
+ await fetch(stashURL, { method: "POST" });
+ continue;
+ }
+
+ if (step.startsWith("set=")) {
+ window.name = step.split("=")[1];
+ continue;
+ }
+
+ if (step.startsWith("setDomain=")) {
+ document.domain = step.split("=")[1];
+ continue;
+ }
+
+ throw new Error("Unsupported step!");
+ }
+}
+
+proceedTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/resources/window-opener.html b/testing/web-platform/tests/html/browsers/windows/resources/window-opener.html
new file mode 100644
index 0000000000..c734eb3056
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/resources/window-opener.html
@@ -0,0 +1,4 @@
+<script>
+ localStorage.setItem("opener", window.opener)
+ window.close()
+</script>
diff --git a/testing/web-platform/tests/html/browsers/windows/restore-window-name-manual.https.html b/testing/web-platform/tests/html/browsers/windows/restore-window-name-manual.https.html
new file mode 100644
index 0000000000..13e8381e31
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/restore-window-name-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+<head>
+ <title>Restore window.name when navigating back from a cross-origin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/utils.js"></script>
+</head>
+<body>
+ <script>
+ function runTest() {
+ const name = token();
+ window.open(`resources/restore-window-name.sub.html?name=${name}`, "start");
+ }
+
+ </script>
+ <p>Please click the following link and follow the instructions in the page for testing</p>
+ <a target="start" href="javascript:void(0)" onclick="runTest()">run test</a>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/targeting-cross-origin-nested-browsing-contexts.html b/testing/web-platform/tests/html/browsers/windows/targeting-cross-origin-nested-browsing-contexts.html
new file mode 100644
index 0000000000..44d4fbad6b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/targeting-cross-origin-nested-browsing-contexts.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Targeting nested browsing contexts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script>
+ async_test(function (t) {
+ var windowsToClose = [];
+ window.onmessage = t.step_func(function (e) {
+ if (e.data.name == "openee") {
+ var a = document.body.appendChild(document.createElement('a'));
+ a.target = "nested1";
+ a.href = "resources/post-to-opener.html";
+ a.click();
+ windowsToClose.push(e.source);
+ } else {
+ assert_equals(e.data.name, "nested1");
+ assert_equals(e.data.isTop, true);
+ windowsToClose.push(e.source);
+ windowsToClose.forEach(function (w) {
+ w.close();
+ });
+ t.done();
+ }
+ });
+
+ var a = document.body.appendChild(document.createElement('a'));
+ a.target = "openee";
+ a.href = get_host_info().HTTP_REMOTE_ORIGIN + "/html/browsers/windows/resources/nested-post-to-opener.html";
+ a.click();
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/windows/targeting-multiple-cross-origin-manual.sub.html b/testing/web-platform/tests/html/browsers/windows/targeting-multiple-cross-origin-manual.sub.html
new file mode 100644
index 0000000000..c8f8cfbe29
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/targeting-multiple-cross-origin-manual.sub.html
@@ -0,0 +1,9 @@
+<meta charset=utf-8>
+<p>Follow this link to open a new browsing context in a separate origin. Follow the instructions
+in that new window, and then come back to this window.
+<a target=first href="{{location[scheme]}}://{{domains[天気ã®è‰¯ã„æ—¥]}}:{{location[port]}}/html/browsers/windows/resources/target-cross-origin.sub.html">link</a>.
+
+<p>Once you come back to this page, follow this link.
+<a target=second href="resources/echo-window-name.html">link</a>.
+
+<p>After clicking that link, you should have three additional windows open.
diff --git a/testing/web-platform/tests/html/browsers/windows/targeting-with-embedded-null-in-target.html b/testing/web-platform/tests/html/browsers/windows/targeting-with-embedded-null-in-target.html
new file mode 100644
index 0000000000..7407248ffe
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/windows/targeting-with-embedded-null-in-target.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Targeting with embedded null in target</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <iframe name="abc">
+ </iframe>
+ <a href="resources/message-parent.html" target="abc">Click me</a>
+ <script>
+ var t = async_test();
+ var target_name = "abc\u0000def";
+
+ onmessage = t.step_func_done(function (e) {
+ assert_equals(e.data.name, target_name,
+ "Should come from a window with the right name");
+ assert_equals(e.source, frames[1],
+ "Should come frome the right window");
+ });
+
+ t.step(function() {
+ var iframe = document.createElement("iframe");
+ iframe.setAttribute("name", target_name);
+ document.body.appendChild(iframe);
+ var a = document.querySelector("a");
+ a.target = target_name;
+ a.click();
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/META.yml b/testing/web-platform/tests/html/canvas/META.yml
new file mode 100644
index 0000000000..0f79f25912
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/META.yml
@@ -0,0 +1,6 @@
+spec: https://html.spec.whatwg.org/multipage/canvas.html#2dcontext
+suggested_reviewers:
+ - AmeliaBR
+ - annevk
+ - kenrussell
+ - fserb
diff --git a/testing/web-platform/tests/html/canvas/README.md b/testing/web-platform/tests/html/canvas/README.md
new file mode 100644
index 0000000000..63f2246f2d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/README.md
@@ -0,0 +1 @@
+To update the generated tests, run `wpt update-built --include canvas`.
diff --git a/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.basics.html b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.basics.html
new file mode 100644
index 0000000000..fed4aa61ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.basics.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.basics</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.basics</h1>
+<p class="desc">void methods return undefined</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("void methods return undefined");
+_addTest(function(canvas, ctx) {
+
+_assertSame(ctx.save(), undefined, "ctx.save()", "undefined");
+_assertSame(ctx.restore(), undefined, "ctx.restore()", "undefined");
+_assertSame(ctx.scale(1, 1), undefined, "ctx.scale(1, 1)", "undefined");
+_assertSame(ctx.rotate(0), undefined, "ctx.rotate(0)", "undefined");
+_assertSame(ctx.translate(0, 0), undefined, "ctx.translate(0, 0)", "undefined");
+if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ _assertSame(ctx.transform(1, 0, 0, 1, 0, 0), undefined, "ctx.transform(1, 0, 0, 1, 0, 0)", "undefined");
+}
+if (ctx.setTransform) {
+ _assertSame(ctx.setTransform(1, 0, 0, 1, 0, 0), undefined, "ctx.setTransform(1, 0, 0, 1, 0, 0)", "undefined");
+ _assertSame(ctx.setTransform(), undefined, "ctx.setTransform()", "undefined");
+}
+_assertSame(ctx.clearRect(0, 0, 0, 0), undefined, "ctx.clearRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.fillRect(0, 0, 0, 0), undefined, "ctx.fillRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.strokeRect(0, 0, 0, 0), undefined, "ctx.strokeRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.beginPath(), undefined, "ctx.beginPath()", "undefined");
+_assertSame(ctx.closePath(), undefined, "ctx.closePath()", "undefined");
+_assertSame(ctx.moveTo(0, 0), undefined, "ctx.moveTo(0, 0)", "undefined");
+_assertSame(ctx.lineTo(0, 0), undefined, "ctx.lineTo(0, 0)", "undefined");
+_assertSame(ctx.quadraticCurveTo(0, 0, 0, 0), undefined, "ctx.quadraticCurveTo(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.bezierCurveTo(0, 0, 0, 0, 0, 0), undefined, "ctx.bezierCurveTo(0, 0, 0, 0, 0, 0)", "undefined");
+_assertSame(ctx.arcTo(0, 0, 0, 0, 1), undefined, "ctx.arcTo(0, 0, 0, 0, 1)", "undefined");
+_assertSame(ctx.rect(0, 0, 0, 0), undefined, "ctx.rect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.arc(0, 0, 1, 0, 0, true), undefined, "ctx.arc(0, 0, 1, 0, 0, true)", "undefined");
+_assertSame(ctx.fill(), undefined, "ctx.fill()", "undefined");
+_assertSame(ctx.stroke(), undefined, "ctx.stroke()", "undefined");
+_assertSame(ctx.clip(), undefined, "ctx.clip()", "undefined");
+if (ctx.fillText) {
+ _assertSame(ctx.fillText('test', 0, 0), undefined, "ctx.fillText('test', 0, 0)", "undefined");
+ _assertSame(ctx.strokeText('test', 0, 0), undefined, "ctx.strokeText('test', 0, 0)", "undefined");
+}
+if (ctx.putImageData) {
+ _assertSame(ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0), undefined, "ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0)", "undefined");
+}
+_assertSame(ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+_assertSame(ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white'), undefined, "ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white')", "undefined");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.delete.html b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.delete.html
new file mode 100644
index 0000000000..bfdf94319b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.delete.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.delete</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.delete</h1>
+<p class="desc">window.CanvasRenderingContext2D is Configurable</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("window.CanvasRenderingContext2D is Configurable");
+_addTest(function(canvas, ctx) {
+
+_assertDifferent(window.CanvasRenderingContext2D, undefined, "window.CanvasRenderingContext2D", "undefined");
+_assertSame(delete window.CanvasRenderingContext2D, true, "delete window.CanvasRenderingContext2D", "true");
+_assertSame(window.CanvasRenderingContext2D, undefined, "window.CanvasRenderingContext2D", "undefined");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.drawings.html b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.drawings.html
new file mode 100644
index 0000000000..40038b2191
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.drawings.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.drawings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.drawings</h1>
+<p class="desc">void methods return undefined</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("void methods return undefined");
+_addTest(function(canvas, ctx) {
+
+_assertSame(ctx.drawImage(document.getElementById('yellow.png'), 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(document.getElementById('yellow.png'), 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.missingargs.html b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.missingargs.html
new file mode 100644
index 0000000000..2ac3d58f4e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/2d.conformance.requirements.missingargs.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.missingargs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.missingargs</h1>
+<p class="desc">Missing arguments cause TypeError</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Missing arguments cause TypeError");
+_addTest(function(canvas, ctx) {
+
+assert_throws_js(TypeError, function() { ctx.scale(); });
+assert_throws_js(TypeError, function() { ctx.scale(1); });
+assert_throws_js(TypeError, function() { ctx.rotate(); });
+assert_throws_js(TypeError, function() { ctx.translate(); });
+assert_throws_js(TypeError, function() { ctx.translate(0); });
+if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ assert_throws_js(TypeError, function() { ctx.transform(); });
+ assert_throws_js(TypeError, function() { ctx.transform(1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1, 0); });
+}
+if (ctx.setTransform) {
+ assert_throws_js(TypeError, function() { ctx.setTransform(1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1, 0); });
+}
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.createPattern(canvas); });
+assert_throws_js(TypeError, function() { ctx.clearRect(); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.moveTo(); });
+assert_throws_js(TypeError, function() { ctx.moveTo(0); });
+assert_throws_js(TypeError, function() { ctx.lineTo(); });
+assert_throws_js(TypeError, function() { ctx.lineTo(0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.rect(); });
+assert_throws_js(TypeError, function() { ctx.rect(0); });
+assert_throws_js(TypeError, function() { ctx.rect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.rect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arc(); });
+assert_throws_js(TypeError, function() { ctx.arc(0); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1, 0); });
+// (6th argument to arc is optional)
+if (ctx.isPointInPath) {
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(0); });
+}
+if (ctx.drawFocusRing) {
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas, 0); });
+}
+if (ctx.fillText) {
+ assert_throws_js(TypeError, function() { ctx.fillText(); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test'); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeText(); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test'); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.measureText(); });
+}
+assert_throws_js(TypeError, function() { ctx.drawImage(); });
+assert_throws_js(TypeError, function() { ctx.drawImage(canvas); });
+assert_throws_js(TypeError, function() { ctx.drawImage(canvas, 0); });
+// TODO: n >= 3 args on drawImage could be either a valid overload,
+// or too few for another overload, or too many for another
+// overload - what should happen?
+if (ctx.createImageData) {
+ assert_throws_js(TypeError, function() { ctx.createImageData(); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(1); });
+}
+if (ctx.getImageData) {
+ assert_throws_js(TypeError, function() { ctx.getImageData(); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0, 1); });
+}
+if (ctx.putImageData) {
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ assert_throws_js(TypeError, function() { ctx.putImageData(); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0); });
+}
+var g = ctx.createLinearGradient(0, 0, 0, 0);
+assert_throws_js(TypeError, function() { g.addColorStop(); });
+assert_throws_js(TypeError, function() { g.addColorStop(0); });
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree-ref.html b/testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree-ref.html
new file mode 100644
index 0000000000..707bafd320
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Test reference</title>
+<canvas id="canvas"></canvas>
+<script>
+ let canvas = document.getElementById("canvas");
+ let ctx = canvas.getContext("2d");
+ ctx.font = "10px sans-serif";
+ ctx.fillText('Hello, world', 10, 10);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree.html b/testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree.html
new file mode 100644
index 0000000000..cf76967a1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/2d.text-outside-of-the-flat-tree.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Canvas outside the flat tree</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1792860">
+<link rel="match" href="2d.text-outside-of-the-flat-tree-ref.html">
+<div id="host"><canvas id="canvas"></canvas></div>
+<script>
+ let host = document.getElementById("host");
+ // Leaves the canvas un-slotted.
+ let root = host.attachShadow({ mode: "open"});
+ let canvas = document.getElementById("canvas");
+ let ctx = canvas.getContext("2d");
+ ctx.font = "10px sans-serif";
+ ctx.fillText('Hello, world', 10, 10);
+ // Slots the content.
+ root.innerHTML = `<slot></slot>`;
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.exists.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.exists.html
new file mode 100644
index 0000000000..1c44acbd54
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.exists.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.exists</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.exists</h1>
+<p class="desc">The 2D context is implemented</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("The 2D context is implemented");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(canvas.getContext('2d'), null, "canvas.getContext('2d')", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.cache.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.cache.html
new file mode 100644
index 0000000000..f4db40815d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.cache.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.extraargs.cache</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.extraargs.cache</h1>
+<p class="desc">The 2D context doesn't throw with extra getContext arguments (cached)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("The 2D context doesn't throw with extra getContext arguments (cached)");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(canvas.getContext('2d', false, {}, [], 1, "2"), null, "canvas.getContext('2d', false, {}, [], 1, \"2\")", "null");
+ _assertDifferent(canvas.getContext('2d', 123), null, "canvas.getContext('2d', 123)", "null");
+ _assertDifferent(canvas.getContext('2d', "test"), null, "canvas.getContext('2d', \"test\")", "null");
+ _assertDifferent(canvas.getContext('2d', undefined), null, "canvas.getContext('2d', undefined)", "null");
+ _assertDifferent(canvas.getContext('2d', null), null, "canvas.getContext('2d', null)", "null");
+ _assertDifferent(canvas.getContext('2d', Symbol.hasInstance), null, "canvas.getContext('2d', Symbol.hasInstance)", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.create.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.create.html
new file mode 100644
index 0000000000..6ae7f787c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.extraargs.create.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.extraargs.create</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.extraargs.create</h1>
+<p class="desc">The 2D context doesn't throw with extra getContext arguments (new context)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("The 2D context doesn't throw with extra getContext arguments (new context)");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(document.createElement("canvas").getContext('2d', false, {}, [], 1, "2"), null, "document.createElement(\"canvas\").getContext('2d', false, {}, [], 1, \"2\")", "null");
+ _assertDifferent(document.createElement("canvas").getContext('2d', 123), null, "document.createElement(\"canvas\").getContext('2d', 123)", "null");
+ _assertDifferent(document.createElement("canvas").getContext('2d', "test"), null, "document.createElement(\"canvas\").getContext('2d', \"test\")", "null");
+ _assertDifferent(document.createElement("canvas").getContext('2d', undefined), null, "document.createElement(\"canvas\").getContext('2d', undefined)", "null");
+ _assertDifferent(document.createElement("canvas").getContext('2d', null), null, "document.createElement(\"canvas\").getContext('2d', null)", "null");
+ _assertDifferent(document.createElement("canvas").getContext('2d', Symbol.hasInstance), null, "document.createElement(\"canvas\").getContext('2d', Symbol.hasInstance)", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.invalid.args.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.invalid.args.html
new file mode 100644
index 0000000000..cfa58266ea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.invalid.args.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.invalid.args</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.invalid.args</h1>
+<p class="desc">Calling getContext with invalid arguments.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Calling getContext with invalid arguments.");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.getContext(''), null, "canvas.getContext('')", "null");
+ _assertSame(canvas.getContext('2d#'), null, "canvas.getContext('2d#')", "null");
+ _assertSame(canvas.getContext('This is clearly not a valid context name.'), null, "canvas.getContext('This is clearly not a valid context name.')", "null");
+ _assertSame(canvas.getContext('2d\0'), null, "canvas.getContext('2d\\0')", "null");
+ _assertSame(canvas.getContext('2\uFF44'), null, "canvas.getContext('2\\uFF44')", "null");
+ _assertSame(canvas.getContext('2D'), null, "canvas.getContext('2D')", "null");
+ assert_throws_js(TypeError, function() { canvas.getContext(); });
+ _assertSame(canvas.getContext('null'), null, "canvas.getContext('null')", "null");
+ _assertSame(canvas.getContext('undefined'), null, "canvas.getContext('undefined')", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.prototype.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.prototype.html
new file mode 100644
index 0000000000..5d5edc6864
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.prototype.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.prototype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.prototype</h1>
+<p class="desc">checks CanvasRenderingContext2D prototype</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("checks CanvasRenderingContext2D prototype");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(Object.getPrototypeOf(CanvasRenderingContext2D.prototype), Object.prototype, "Object.getPrototypeOf(CanvasRenderingContext2D.prototype)", "Object.prototype");
+ _assertSame(Object.getPrototypeOf(ctx), CanvasRenderingContext2D.prototype, "Object.getPrototypeOf(ctx)", "CanvasRenderingContext2D.prototype");
+ t.done();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.shared.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.shared.html
new file mode 100644
index 0000000000..fd8deddec0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.shared.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.shared</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.shared</h1>
+<p class="desc">getContext('2d') returns objects which share canvas state</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("getContext('2d') returns objects which share canvas state");
+_addTest(function(canvas, ctx) {
+
+ var ctx2 = canvas.getContext('2d');
+ ctx.fillStyle = '#f00';
+ ctx2.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.exists.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.exists.html
new file mode 100644
index 0000000000..d25f009abb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.exists.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.type.exists</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.type.exists</h1>
+<p class="desc">The 2D context interface is a property of 'window'</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("The 2D context interface is a property of 'window'");
+_addTest(function(canvas, ctx) {
+
+ _assert(window.CanvasRenderingContext2D, "window.CanvasRenderingContext2D");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.extend.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.extend.html
new file mode 100644
index 0000000000..8d34d53101
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.extend.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.type.extend</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.type.extend</h1>
+<p class="desc">Interface methods can be added</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Interface methods can be added");
+_addTest(function(canvas, ctx) {
+
+ window.CanvasRenderingContext2D.prototype.fillRectGreen = function (x, y, w, h)
+ {
+ this.fillStyle = '#0f0';
+ this.fillRect(x, y, w, h);
+ };
+ ctx.fillStyle = '#f00';
+ ctx.fillRectGreen(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.prototype.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.prototype.html
new file mode 100644
index 0000000000..a537fe5106
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.prototype.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.type.prototype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.type.prototype</h1>
+<p class="desc">window.CanvasRenderingContext2D.prototype are not [[Writable]] and not [[Configurable]], and its methods are [[Configurable]].</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("window.CanvasRenderingContext2D.prototype are not [[Writable]] and not [[Configurable]], and its methods are [[Configurable]].");
+_addTest(function(canvas, ctx) {
+
+ _assert(window.CanvasRenderingContext2D.prototype, "window.CanvasRenderingContext2D.prototype");
+ _assert(window.CanvasRenderingContext2D.prototype.fill, "window.CanvasRenderingContext2D.prototype.fill");
+ window.CanvasRenderingContext2D.prototype = null;
+ _assert(window.CanvasRenderingContext2D.prototype, "window.CanvasRenderingContext2D.prototype");
+ delete window.CanvasRenderingContext2D.prototype;
+ _assert(window.CanvasRenderingContext2D.prototype, "window.CanvasRenderingContext2D.prototype");
+ window.CanvasRenderingContext2D.prototype.fill = 1;
+ _assertSame(window.CanvasRenderingContext2D.prototype.fill, 1, "window.CanvasRenderingContext2D.prototype.fill", "1");
+ delete window.CanvasRenderingContext2D.prototype.fill;
+ _assertSame(window.CanvasRenderingContext2D.prototype.fill, undefined, "window.CanvasRenderingContext2D.prototype.fill", "undefined");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.replace.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.replace.html
new file mode 100644
index 0000000000..ac3c40587d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.type.replace.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.type.replace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.type.replace</h1>
+<p class="desc">Interface methods can be overridden</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Interface methods can be overridden");
+_addTest(function(canvas, ctx) {
+
+ var fillRect = window.CanvasRenderingContext2D.prototype.fillRect;
+ window.CanvasRenderingContext2D.prototype.fillRect = function (x, y, w, h)
+ {
+ this.fillStyle = '#0f0';
+ fillRect.call(this, x, y, w, h);
+ };
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.unique.html b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.unique.html
new file mode 100644
index 0000000000..c7b1803856
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-context/2d.canvas.context.unique.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.context.unique</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.context.unique</h1>
+<p class="desc">getContext('2d') returns the same object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getContext('2d') returns the same object");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.getContext('2d'), canvas.getContext('2d'), "canvas.getContext('2d')", "canvas.getContext('2d')");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.initial.reset.2dstate.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.initial.reset.2dstate.html
new file mode 100644
index 0000000000..e194001198
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.initial.reset.2dstate.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.initial.reset.2dstate</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.initial.reset.2dstate</h1>
+<p class="desc">Resetting the canvas state resets 2D state variables</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state resets 2D state variables");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 100;
+ var default_val;
+
+ default_val = ctx.strokeStyle;
+ ctx.strokeStyle = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.strokeStyle, default_val, "ctx.strokeStyle", "default_val");
+
+ default_val = ctx.fillStyle;
+ ctx.fillStyle = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.fillStyle, default_val, "ctx.fillStyle", "default_val");
+
+ default_val = ctx.globalAlpha;
+ ctx.globalAlpha = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.globalAlpha, default_val, "ctx.globalAlpha", "default_val");
+
+ default_val = ctx.lineWidth;
+ ctx.lineWidth = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.lineWidth, default_val, "ctx.lineWidth", "default_val");
+
+ default_val = ctx.lineCap;
+ ctx.lineCap = "round";
+ canvas.width = 100;
+ _assertSame(ctx.lineCap, default_val, "ctx.lineCap", "default_val");
+
+ default_val = ctx.lineJoin;
+ ctx.lineJoin = "round";
+ canvas.width = 100;
+ _assertSame(ctx.lineJoin, default_val, "ctx.lineJoin", "default_val");
+
+ default_val = ctx.miterLimit;
+ ctx.miterLimit = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.miterLimit, default_val, "ctx.miterLimit", "default_val");
+
+ default_val = ctx.shadowOffsetX;
+ ctx.shadowOffsetX = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowOffsetX, default_val, "ctx.shadowOffsetX", "default_val");
+
+ default_val = ctx.shadowOffsetY;
+ ctx.shadowOffsetY = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowOffsetY, default_val, "ctx.shadowOffsetY", "default_val");
+
+ default_val = ctx.shadowBlur;
+ ctx.shadowBlur = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowBlur, default_val, "ctx.shadowBlur", "default_val");
+
+ default_val = ctx.shadowColor;
+ ctx.shadowColor = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.shadowColor, default_val, "ctx.shadowColor", "default_val");
+
+ default_val = ctx.globalCompositeOperation;
+ ctx.globalCompositeOperation = "copy";
+ canvas.width = 100;
+ _assertSame(ctx.globalCompositeOperation, default_val, "ctx.globalCompositeOperation", "default_val");
+
+ default_val = ctx.font;
+ ctx.font = "25px serif";
+ canvas.width = 100;
+ _assertSame(ctx.font, default_val, "ctx.font", "default_val");
+
+ default_val = ctx.textAlign;
+ ctx.textAlign = "center";
+ canvas.width = 100;
+ _assertSame(ctx.textAlign, default_val, "ctx.textAlign", "default_val");
+
+ default_val = ctx.textBaseline;
+ ctx.textBaseline = "bottom";
+ canvas.width = 100;
+ _assertSame(ctx.textBaseline, default_val, "ctx.textBaseline", "default_val");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.readonly.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.readonly.html
new file mode 100644
index 0000000000..cbbf32f2e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.readonly.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.readonly</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.readonly</h1>
+<p class="desc">CanvasRenderingContext2D.canvas is readonly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("CanvasRenderingContext2D.canvas is readonly");
+_addTest(function(canvas, ctx) {
+
+ var c = document.createElement('canvas');
+ var d = ctx.canvas;
+ _assertDifferent(c, d, "c", "d");
+ ctx.canvas = c;
+ _assertSame(ctx.canvas, d, "ctx.canvas", "d");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.reference.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.reference.html
new file mode 100644
index 0000000000..6a4bdb65b0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.reference.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.reference</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.reference</h1>
+<p class="desc">CanvasRenderingContext2D.canvas refers back to its canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("CanvasRenderingContext2D.canvas refers back to its canvas");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.canvas, canvas, "ctx.canvas", "canvas");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled-manual.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled-manual.html
new file mode 100644
index 0000000000..acf3f9c7b3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled-manual.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.scaled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.scaled</h1>
+<p class="desc">CSS-scaled canvases get drawn correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="25" style="width: 100px; height: 50px"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.scaled.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("CSS-scaled canvases get drawn correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillStyle = '#0ff';
+ ctx.fillRect(0, 0, 25, 10);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled.png
new file mode 100644
index 0000000000..875407769f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.scaled.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html
new file mode 100644
index 0000000000..0de11e235d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.decimal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.decimal</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100.999" height="100.999"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.decimal.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100.999', "canvas.getAttribute('width')", "'100.999'");
+ _assertSame(canvas.getAttribute('height'), '100.999', "canvas.getAttribute('height')", "'100.999'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.decimal.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.html
new file mode 100644
index 0000000000..315c8323fa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.em</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.em</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100em" height="100em"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.em.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100em', "canvas.getAttribute('width')", "'100em'");
+ _assertSame(canvas.getAttribute('height'), '100em', "canvas.getAttribute('height')", "'100em'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.em.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.html
new file mode 100644
index 0000000000..b9568712e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.empty</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="" height=""><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.empty.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), '', "canvas.getAttribute('width')", "''");
+ _assertSame(canvas.getAttribute('height'), '', "canvas.getAttribute('height')", "''");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.empty.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.html
new file mode 100644
index 0000000000..208ddeceeb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.exp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.exp</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100e1" height="100e1"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.exp.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100e1', "canvas.getAttribute('width')", "'100e1'");
+ _assertSame(canvas.getAttribute('height'), '100e1', "canvas.getAttribute('height')", "'100e1'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.exp.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.hex.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.hex.html
new file mode 100644
index 0000000000..05f7030d47
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.hex.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.hex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.hex</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="0x100" height="0x100"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "0px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"0px\"");
+ _assertSame(canvas.getAttribute('width'), '0x100', "canvas.getAttribute('width')", "'0x100'");
+ _assertSame(canvas.getAttribute('height'), '0x100', "canvas.getAttribute('height')", "'0x100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.html
new file mode 100644
index 0000000000..3a55fd13dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.junk</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.junk</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="#!?" height="#!?"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.junk.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), '#!?', "canvas.getAttribute('width')", "'#!?'");
+ _assertSame(canvas.getAttribute('height'), '#!?', "canvas.getAttribute('height')", "'#!?'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.junk.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.html
new file mode 100644
index 0000000000..da5f21ad04
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.minus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.minus</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="-100" height="-100"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.minus.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), '-100', "canvas.getAttribute('width')", "'-100'");
+ _assertSame(canvas.getAttribute('height'), '-100', "canvas.getAttribute('height')", "'-100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.minus.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.html
new file mode 100644
index 0000000000..ccc7efaafa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.octal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.octal</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="0100" height="0100"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.octal.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '0100', "canvas.getAttribute('width')", "'0100'");
+ _assertSame(canvas.getAttribute('height'), '0100', "canvas.getAttribute('height')", "'0100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.octal.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html
new file mode 100644
index 0000000000..da14fa25c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.onlyspace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.onlyspace</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width=" " height=" "><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.onlyspace.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), ' ', "canvas.getAttribute('width')", "' '");
+ _assertSame(canvas.getAttribute('height'), ' ', "canvas.getAttribute('height')", "' '");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.html
new file mode 100644
index 0000000000..d699288f73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.percent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.percent</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100%" height="100%"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.percent.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100%', "canvas.getAttribute('width')", "'100%'");
+ _assertSame(canvas.getAttribute('height'), '100%', "canvas.getAttribute('height')", "'100%'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.percent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.html
new file mode 100644
index 0000000000..58fb07f179
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.plus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.plus</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="+100" height="+100"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.plus.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '+100', "canvas.getAttribute('width')", "'+100'");
+ _assertSame(canvas.getAttribute('height'), '+100', "canvas.getAttribute('height')", "'+100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.plus.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.html
new file mode 100644
index 0000000000..52fadaad43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.space</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.space</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width=" 100" height=" 100"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.space.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), ' 100', "canvas.getAttribute('width')", "' 100'");
+ _assertSame(canvas.getAttribute('height'), ' 100', "canvas.getAttribute('height')", "' 100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.space.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html
new file mode 100644
index 0000000000..b18167c599
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.trailingjunk</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.trailingjunk</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100#!?" height="100#!?"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.trailingjunk.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100#!?', "canvas.getAttribute('width')", "'100#!?'");
+ _assertSame(canvas.getAttribute('height'), '100#!?', "canvas.getAttribute('height')", "'100#!?'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html
new file mode 100644
index 0000000000..79e81c1020
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.whitespace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.whitespace</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="&#xD;
+ 100" height="&#xD;
+ 100"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.parse.whitespace.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '\r\n\t\x0c100', "canvas.getAttribute('width')", "'\\r\\n\\t\\x0c100'");
+ _assertSame(canvas.getAttribute('height'), '\r\n\t\x0c100', "canvas.getAttribute('height')", "'\\r\\n\\t\\x0c100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.zero.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.zero.html
new file mode 100644
index 0000000000..b7fd499d1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.parse.zero.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.parse.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.parse.zero</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="0" height="0"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "0px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"0px\"");
+ _assertSame(canvas.getAttribute('width'), '0', "canvas.getAttribute('width')", "'0'");
+ _assertSame(canvas.getAttribute('height'), '0', "canvas.getAttribute('height')", "'0'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.html
new file mode 100644
index 0000000000..b6c2130bf0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.decimal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.decimal</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.decimal.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '100.999');
+ canvas.setAttribute('height', '100.999');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100.999', "canvas.getAttribute('width')", "'100.999'");
+ _assertSame(canvas.getAttribute('height'), '100.999', "canvas.getAttribute('height')", "'100.999'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.decimal.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.html
new file mode 100644
index 0000000000..f70713fd8f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.em</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.em</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.em.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '100em');
+ canvas.setAttribute('height', '100em');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100em', "canvas.getAttribute('width')", "'100em'");
+ _assertSame(canvas.getAttribute('height'), '100em', "canvas.getAttribute('height')", "'100em'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.em.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.html
new file mode 100644
index 0000000000..fab2394fec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.empty</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.empty.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '');
+ canvas.setAttribute('height', '');
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), '', "canvas.getAttribute('width')", "''");
+ _assertSame(canvas.getAttribute('height'), '', "canvas.getAttribute('height')", "''");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.empty.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.html
new file mode 100644
index 0000000000..cfa11eb302
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.exp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.exp</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.exp.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '100e1');
+ canvas.setAttribute('height', '100e1');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100e1', "canvas.getAttribute('width')", "'100e1'");
+ _assertSame(canvas.getAttribute('height'), '100e1', "canvas.getAttribute('height')", "'100e1'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.exp.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.hex.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.hex.html
new file mode 100644
index 0000000000..80a1fbda60
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.hex.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.hex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.hex</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '0x100');
+ canvas.setAttribute('height', '0x100');
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "0px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"0px\"");
+ _assertSame(canvas.getAttribute('width'), '0x100', "canvas.getAttribute('width')", "'0x100'");
+ _assertSame(canvas.getAttribute('height'), '0x100', "canvas.getAttribute('height')", "'0x100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.html
new file mode 100644
index 0000000000..b583871f38
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.junk</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.junk</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.junk.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '#!?');
+ canvas.setAttribute('height', '#!?');
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), '#!?', "canvas.getAttribute('width')", "'#!?'");
+ _assertSame(canvas.getAttribute('height'), '#!?', "canvas.getAttribute('height')", "'#!?'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.junk.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.html
new file mode 100644
index 0000000000..90a31403e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.minus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.minus</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.minus.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '-100');
+ canvas.setAttribute('height', '-100');
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), '-100', "canvas.getAttribute('width')", "'-100'");
+ _assertSame(canvas.getAttribute('height'), '-100', "canvas.getAttribute('height')", "'-100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.minus.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.html
new file mode 100644
index 0000000000..c5b263bd5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.octal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.octal</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.octal.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '0100');
+ canvas.setAttribute('height', '0100');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '0100', "canvas.getAttribute('width')", "'0100'");
+ _assertSame(canvas.getAttribute('height'), '0100', "canvas.getAttribute('height')", "'0100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.octal.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.html
new file mode 100644
index 0000000000..ac4cdb001b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.onlyspace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.onlyspace</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.onlyspace.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', ' ');
+ canvas.setAttribute('height', ' ');
+ _assertSame(canvas.width, 300, "canvas.width", "300");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ _assertSame(canvas.getAttribute('width'), ' ', "canvas.getAttribute('width')", "' '");
+ _assertSame(canvas.getAttribute('height'), ' ', "canvas.getAttribute('height')", "' '");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.onlyspace.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.html
new file mode 100644
index 0000000000..5aebf5a0b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.percent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.percent</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.percent.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '100%');
+ canvas.setAttribute('height', '100%');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100%', "canvas.getAttribute('width')", "'100%'");
+ _assertSame(canvas.getAttribute('height'), '100%', "canvas.getAttribute('height')", "'100%'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.percent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.html
new file mode 100644
index 0000000000..60da44daf3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.plus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.plus</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.plus.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '+100');
+ canvas.setAttribute('height', '+100');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '+100', "canvas.getAttribute('width')", "'+100'");
+ _assertSame(canvas.getAttribute('height'), '+100', "canvas.getAttribute('height')", "'+100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.plus.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.html
new file mode 100644
index 0000000000..fbfd4c33c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.space</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.space</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.space.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', ' 100');
+ canvas.setAttribute('height', ' 100');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), ' 100', "canvas.getAttribute('width')", "' 100'");
+ _assertSame(canvas.getAttribute('height'), ' 100', "canvas.getAttribute('height')", "' 100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.space.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.html
new file mode 100644
index 0000000000..1d230bd654
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.trailingjunk</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.trailingjunk</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.trailingjunk.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '100#!?');
+ canvas.setAttribute('height', '100#!?');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '100#!?', "canvas.getAttribute('width')", "'100#!?'");
+ _assertSame(canvas.getAttribute('height'), '100#!?', "canvas.getAttribute('height')", "'100#!?'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.trailingjunk.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.html
new file mode 100644
index 0000000000..1aa86d6a9e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.whitespace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.whitespace</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.canvas.host.size.attributes.setAttribute.whitespace.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '\r\n\t\x0c100');
+ canvas.setAttribute('height', '\r\n\t\x0c100');
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "100px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"100px\"");
+ _assertSame(canvas.getAttribute('width'), '\r\n\t\x0c100', "canvas.getAttribute('width')", "'\\r\\n\\t\\x0c100'");
+ _assertSame(canvas.getAttribute('height'), '\r\n\t\x0c100', "canvas.getAttribute('height')", "'\\r\\n\\t\\x0c100'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.png b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.png
new file mode 100644
index 0000000000..f842673330
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.zero.html b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.zero.html
new file mode 100644
index 0000000000..64b4811d79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.canvas.host.size.attributes.setAttribute.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.canvas.host.size.attributes.setAttribute.zero</h1>
+<p class="desc">Parsing of non-negative integers in setAttribute</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Parsing of non-negative integers in setAttribute");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('width', '0');
+ canvas.setAttribute('height', '0');
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ _assertSame(window.getComputedStyle(canvas, null).getPropertyValue("width"), "0px", "window.getComputedStyle(canvas, null).getPropertyValue(\"width\")", "\"0px\"");
+ _assertSame(canvas.getAttribute('width'), '0', "canvas.getAttribute('width')", "'0'");
+ _assertSame(canvas.getAttribute('height'), '0', "canvas.getAttribute('height')", "'0'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.html
new file mode 100644
index 0000000000..cb805b692e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.clear</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.clear.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.clear.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.html
new file mode 100644
index 0000000000..d4e75b7bac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.copy</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,191, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.png
new file mode 100644
index 0000000000..f5e9c21964
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.html
new file mode 100644
index 0000000000..6fded39e44
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.destination-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 128,255,128,191, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.png
new file mode 100644
index 0000000000..504b42756e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.html
new file mode 100644
index 0000000000..7277286d8b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.destination-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,96, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.png
new file mode 100644
index 0000000000..790e418a6b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.html
new file mode 100644
index 0000000000..5860c3d5cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.destination-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.destination-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,32, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.png
new file mode 100644
index 0000000000..7f5ed1a836
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.html
new file mode 100644
index 0000000000..76bad84392
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.destination-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.destination-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 109,255,146,223, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.png
new file mode 100644
index 0000000000..226390b2f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.destination-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.html
new file mode 100644
index 0000000000..8d706df67e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.lighter</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.lighter.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,128,255, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.png
new file mode 100644
index 0000000000..fc33f3301e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.lighter.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.html
new file mode 100644
index 0000000000..cf3e449123
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.source-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.source-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.png
new file mode 100644
index 0000000000..1ef9630195
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.html
new file mode 100644
index 0000000000..dabcced9dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.source-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.png
new file mode 100644
index 0000000000..c26cdccf02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.html
new file mode 100644
index 0000000000..2fcf708796
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.source-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.png
new file mode 100644
index 0000000000..c26cdccf02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.html
new file mode 100644
index 0000000000..b9dca65744
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.source-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.source-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 219,255,36,223, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.png
new file mode 100644
index 0000000000..e63ab7e308
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.source-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.html
new file mode 100644
index 0000000000..c252e17a7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.canvas.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.canvas.xor</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.canvas.xor.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.png
new file mode 100644
index 0000000000..1ef9630195
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.canvas.xor.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.clear.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.clear.html
new file mode 100644
index 0000000000..65cfd951e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.clear.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.clear</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.copy.html
new file mode 100644
index 0000000000..b38397a2c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.copy.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.copy</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-atop.html
new file mode 100644
index 0000000000..79fb3af54c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-atop.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.destination-atop</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-in.html
new file mode 100644
index 0000000000..63e8d3acfd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-in.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.destination-in</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-out.html
new file mode 100644
index 0000000000..839246427f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-out.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.destination-out</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-over.html
new file mode 100644
index 0000000000..fc7f1ab728
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.destination-over.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.destination-over</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.lighter.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.lighter.html
new file mode 100644
index 0000000000..b3acab0231
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.lighter.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.lighter</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-atop.html
new file mode 100644
index 0000000000..49c3eb0bb5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-atop.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.source-atop</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-in.html
new file mode 100644
index 0000000000..afe92ba4bf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-in.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.source-in</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-out.html
new file mode 100644
index 0000000000..43b352fb46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-out.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.source-out</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-over.html
new file mode 100644
index 0000000000..4973f8e6e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.source-over.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.source-over</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.xor.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.xor.html
new file mode 100644
index 0000000000..5239d6fd10
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.clip.xor.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.clip.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.clip.xor</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvas.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvas.html
new file mode 100644
index 0000000000..525a56ebc9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvas.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.canvas</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvascopy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvascopy.html
new file mode 100644
index 0000000000..17a46cfca2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvascopy.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.canvascopy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.canvascopy</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.globalCompositeOperation = 'copy'
+ ctx.globalAlpha = 0.51;
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,130, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvaspattern.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvaspattern.html
new file mode 100644
index 0000000000..eea076c974
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.canvaspattern.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.canvaspattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.canvaspattern</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = ctx.createPattern(canvas2, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.default.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.default.html
new file mode 100644
index 0000000000..97cdf267c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.default.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.globalAlpha, 1.0, "ctx.globalAlpha", "1.0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.fill.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.fill.html
new file mode 100644
index 0000000000..5766fdbe77
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.fill.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.fill</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.fill</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.image.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.image.html
new file mode 100644
index 0000000000..5de1d5ff3e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.image.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.image</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.imagepattern.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.imagepattern.html
new file mode 100644
index 0000000000..87e884524e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.imagepattern.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.imagepattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.imagepattern</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.invalid.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.invalid.html
new file mode 100644
index 0000000000..db67f882f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.invalid.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ ctx.globalAlpha = Infinity;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = -Infinity;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = NaN;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.range.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.range.html
new file mode 100644
index 0000000000..883b0cac69
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.globalAlpha.range.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.globalAlpha.range</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.globalAlpha.range</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = 1.1;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = -0.1;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = 0;
+ _assertSame(ctx.globalAlpha, 0, "ctx.globalAlpha", "0");
+ ctx.globalAlpha = 1;
+ _assertSame(ctx.globalAlpha, 1, "ctx.globalAlpha", "1");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.html
new file mode 100644
index 0000000000..a44f8e35b0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.clear</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.clear.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.clear.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.html
new file mode 100644
index 0000000000..d3c972b6d2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.copy</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,191, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.png
new file mode 100644
index 0000000000..f5e9c21964
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.html
new file mode 100644
index 0000000000..7c6f93448b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.destination-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 128,255,128,191, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.png
new file mode 100644
index 0000000000..504b42756e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.html
new file mode 100644
index 0000000000..a2bf972bb9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.destination-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,96, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.png
new file mode 100644
index 0000000000..790e418a6b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.html
new file mode 100644
index 0000000000..d0d08fa09e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.destination-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.destination-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,32, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.png
new file mode 100644
index 0000000000..7f5ed1a836
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.html
new file mode 100644
index 0000000000..7ac98904f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.destination-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.destination-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 109,255,146,223, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.png
new file mode 100644
index 0000000000..226390b2f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.destination-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.html
new file mode 100644
index 0000000000..1df4c683df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.lighter</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.lighter.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,128,255, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.png
new file mode 100644
index 0000000000..fc33f3301e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.lighter.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.html
new file mode 100644
index 0000000000..86833df6b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.source-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.source-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.png
new file mode 100644
index 0000000000..1ef9630195
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.html
new file mode 100644
index 0000000000..0b6ca4b52e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.source-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.png
new file mode 100644
index 0000000000..c26cdccf02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.html
new file mode 100644
index 0000000000..31dfe471e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.source-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.png
new file mode 100644
index 0000000000..c26cdccf02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.html
new file mode 100644
index 0000000000..e396faa5d7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.source-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.source-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 219,255,36,223, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.png
new file mode 100644
index 0000000000..e63ab7e308
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.source-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.html
new file mode 100644
index 0000000000..d43742c272
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.image.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.image.xor</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.image.xor.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+
+});
+</script>
+<img src="/images/yellow75.png" id="yellow75.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.png
new file mode 100644
index 0000000000..1ef9630195
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.image.xor.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.casesensitive.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.casesensitive.html
new file mode 100644
index 0000000000..13ae99b35f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.casesensitive.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.casesensitive</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.casesensitive</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'Source-over';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.clear.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.clear.html
new file mode 100644
index 0000000000..c1e88b4894
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.clear.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.clear</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'clear';
+ _assertSame(ctx.globalCompositeOperation, 'clear', "ctx.globalCompositeOperation", "'clear'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.darker.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.darker.html
new file mode 100644
index 0000000000..4fb6998f71
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.darker.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.darker</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.darker</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'darker';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.default.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.default.html
new file mode 100644
index 0000000000..5ccd488b3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.default.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.globalCompositeOperation, 'source-over', "ctx.globalCompositeOperation", "'source-over'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.get.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.get.html
new file mode 100644
index 0000000000..27b89c9fed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.get.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.get</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.get</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var modes = ['source-atop', 'source-in', 'source-out', 'source-over',
+ 'destination-atop', 'destination-in', 'destination-out', 'destination-over',
+ 'lighter', 'copy', 'xor'];
+ for (var i = 0; i < modes.length; ++i)
+ {
+ ctx.globalCompositeOperation = modes[i];
+ _assertSame(ctx.globalCompositeOperation, modes[i], "ctx.globalCompositeOperation", "modes[\""+(i)+"\"]");
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.highlight.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.highlight.html
new file mode 100644
index 0000000000..45c5dd15eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.highlight.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.highlight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.highlight</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'highlight';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.nullsuffix.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.nullsuffix.html
new file mode 100644
index 0000000000..8a2443ff41
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.nullsuffix.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.nullsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.nullsuffix</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'source-over\0';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.over.html
new file mode 100644
index 0000000000..1d742ef3cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.over.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'over';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.unrecognised.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.unrecognised.html
new file mode 100644
index 0000000000..e4aa877d96
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.operation.unrecognised.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.operation.unrecognised</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.operation.unrecognised</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'nonexistent';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.html
new file mode 100644
index 0000000000..a946989544
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.clear</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.clear.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.clear.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.html
new file mode 100644
index 0000000000..14bee74452
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.copy</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.png
new file mode 100644
index 0000000000..fc0883e74f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.html
new file mode 100644
index 0000000000..d82fb44cce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.destination-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.png
new file mode 100644
index 0000000000..dd04072baf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.html
new file mode 100644
index 0000000000..6a491f57c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.destination-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.png
new file mode 100644
index 0000000000..dd04072baf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.html
new file mode 100644
index 0000000000..bcc7f24e7a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.destination-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.destination-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.html
new file mode 100644
index 0000000000..6e634e6ae5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.destination-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.destination-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.png
new file mode 100644
index 0000000000..dd04072baf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.destination-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.html
new file mode 100644
index 0000000000..fddbe92420
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.lighter</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.lighter.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,255,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.png
new file mode 100644
index 0000000000..bf48767a88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.lighter.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.html
new file mode 100644
index 0000000000..4bb0e3d8b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.source-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.source-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.png
new file mode 100644
index 0000000000..fc0883e74f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.html
new file mode 100644
index 0000000000..f8fee40a93
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.source-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.png
new file mode 100644
index 0000000000..fc0883e74f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.html
new file mode 100644
index 0000000000..2c9d086182
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.source-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.html
new file mode 100644
index 0000000000..335987b12b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.source-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.source-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.png
new file mode 100644
index 0000000000..fc0883e74f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.source-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.html
new file mode 100644
index 0000000000..d9da43070e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.solid.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.solid.xor</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.solid.xor.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.solid.xor.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.html
new file mode 100644
index 0000000000..de417eea34
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.clear</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.clear.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.clear.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.html
new file mode 100644
index 0000000000..2048975ec3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.copy</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,191, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.png
new file mode 100644
index 0000000000..b0cbe3a552
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.html
new file mode 100644
index 0000000000..1da1f1a4ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.destination-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,128,128,191, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.png
new file mode 100644
index 0000000000..5db0a0484a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.html
new file mode 100644
index 0000000000..b0f8abe0ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.destination-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,96, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.png
new file mode 100644
index 0000000000..c6895de985
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.html
new file mode 100644
index 0000000000..badbfc4ad7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.destination-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.destination-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,32, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.png
new file mode 100644
index 0000000000..873a9c45d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.html
new file mode 100644
index 0000000000..e8e0051b2c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.destination-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.destination-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,146,109,223, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.png
new file mode 100644
index 0000000000..5a8726bbca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.destination-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.html
new file mode 100644
index 0000000000..6e4c42ab9a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.lighter</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.lighter.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,128,191,255, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.png
new file mode 100644
index 0000000000..0e1c28c0cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.lighter.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.html
new file mode 100644
index 0000000000..d3d3fbda12
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.source-atop</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.source-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,64,191,128, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.png
new file mode 100644
index 0000000000..e0afff6b00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.html
new file mode 100644
index 0000000000..27c66cb673
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.source-in</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,96, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.png
new file mode 100644
index 0000000000..1459b5e54e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.html
new file mode 100644
index 0000000000..31bb6a2403
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.source-out</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,96, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.png
new file mode 100644
index 0000000000..1459b5e54e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.html
new file mode 100644
index 0000000000..ee20f52b46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.source-over</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.source-over.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,36,219,223, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.png
new file mode 100644
index 0000000000..02acb7a861
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.source-over.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.html
new file mode 100644
index 0000000000..d1818750df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.transparent.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.transparent.xor</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.transparent.xor.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,64,191,128, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.png
new file mode 100644
index 0000000000..e0afff6b00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.transparent.xor.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.html
new file mode 100644
index 0000000000..9dfd650b98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.fill.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.fill.copy</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.fill.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.html
new file mode 100644
index 0000000000..685a139504
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.fill.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.fill.destination-atop</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.fill.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.html
new file mode 100644
index 0000000000..9ca8021151
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.fill.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.fill.destination-in</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.fill.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.html
new file mode 100644
index 0000000000..9f1a5f9d51
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.fill.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.fill.source-in</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.fill.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.html
new file mode 100644
index 0000000000..7653677fd5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.fill.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.fill.source-out</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.fill.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.fill.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.html
new file mode 100644
index 0000000000..61dd0381ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.image.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.image.copy</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.image.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.html
new file mode 100644
index 0000000000..440c832c71
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.image.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.image.destination-atop</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.image.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.html
new file mode 100644
index 0000000000..03e2e807ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.image.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.image.destination-in</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.image.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.html
new file mode 100644
index 0000000000..de2e5fad63
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.image.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.image.source-in</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.image.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.html
new file mode 100644
index 0000000000..5cb9b072b9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.image.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.image.source-out</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.image.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.image.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.html
new file mode 100644
index 0000000000..cac66ade5a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.nocontext.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.nocontext.copy</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.nocontext.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var canvas2 = document.createElement('canvas');
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.html
new file mode 100644
index 0000000000..c7cecc2e3d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.nocontext.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.nocontext.destination-atop</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.nocontext.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var canvas2 = document.createElement('canvas');
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.html
new file mode 100644
index 0000000000..e090d7d4b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.nocontext.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.nocontext.destination-in</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.nocontext.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var canvas2 = document.createElement('canvas');
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.html
new file mode 100644
index 0000000000..0436b40817
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.nocontext.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.nocontext.source-in</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.nocontext.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var canvas2 = document.createElement('canvas');
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.html
new file mode 100644
index 0000000000..efbc8ab2f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.nocontext.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.nocontext.source-out</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.nocontext.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var canvas2 = document.createElement('canvas');
+ ctx.drawImage(canvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.nocontext.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.html
new file mode 100644
index 0000000000..aaee92022a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.pattern.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.pattern.copy</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.pattern.copy.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.copy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.html
new file mode 100644
index 0000000000..aded018f8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.pattern.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.pattern.destination-atop</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.pattern.destination-atop.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-atop.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.html
new file mode 100644
index 0000000000..e520e1ffa3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.pattern.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.pattern.destination-in</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.pattern.destination-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.destination-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.html
new file mode 100644
index 0000000000..bee654877b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.pattern.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.pattern.source-in</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.pattern.source-in.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-in.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.html b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.html
new file mode 100644
index 0000000000..947893091e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.composite.uncovered.pattern.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.composite.uncovered.pattern.source-out</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.composite.uncovered.pattern.source-out.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.png b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/compositing/2d.composite.uncovered.pattern.source-out.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.basics.html b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.basics.html
new file mode 100644
index 0000000000..cea531f9db
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.basics.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.basics</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.basics</h1>
+<p class="desc">void methods return undefined</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("void methods return undefined");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.save(), undefined, "ctx.save()", "undefined");
+ _assertSame(ctx.restore(), undefined, "ctx.restore()", "undefined");
+ _assertSame(ctx.scale(1, 1), undefined, "ctx.scale(1, 1)", "undefined");
+ _assertSame(ctx.rotate(0), undefined, "ctx.rotate(0)", "undefined");
+ _assertSame(ctx.translate(0, 0), undefined, "ctx.translate(0, 0)", "undefined");
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ _assertSame(ctx.transform(1, 0, 0, 1, 0, 0), undefined, "ctx.transform(1, 0, 0, 1, 0, 0)", "undefined");
+ }
+ if (ctx.setTransform) {
+ _assertSame(ctx.setTransform(1, 0, 0, 1, 0, 0), undefined, "ctx.setTransform(1, 0, 0, 1, 0, 0)", "undefined");
+ _assertSame(ctx.setTransform(), undefined, "ctx.setTransform()", "undefined");
+ }
+ _assertSame(ctx.clearRect(0, 0, 0, 0), undefined, "ctx.clearRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.fillRect(0, 0, 0, 0), undefined, "ctx.fillRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.strokeRect(0, 0, 0, 0), undefined, "ctx.strokeRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.beginPath(), undefined, "ctx.beginPath()", "undefined");
+ _assertSame(ctx.closePath(), undefined, "ctx.closePath()", "undefined");
+ _assertSame(ctx.moveTo(0, 0), undefined, "ctx.moveTo(0, 0)", "undefined");
+ _assertSame(ctx.lineTo(0, 0), undefined, "ctx.lineTo(0, 0)", "undefined");
+ _assertSame(ctx.quadraticCurveTo(0, 0, 0, 0), undefined, "ctx.quadraticCurveTo(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.bezierCurveTo(0, 0, 0, 0, 0, 0), undefined, "ctx.bezierCurveTo(0, 0, 0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.arcTo(0, 0, 0, 0, 1), undefined, "ctx.arcTo(0, 0, 0, 0, 1)", "undefined");
+ _assertSame(ctx.rect(0, 0, 0, 0), undefined, "ctx.rect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.arc(0, 0, 1, 0, 0, true), undefined, "ctx.arc(0, 0, 1, 0, 0, true)", "undefined");
+ _assertSame(ctx.fill(), undefined, "ctx.fill()", "undefined");
+ _assertSame(ctx.stroke(), undefined, "ctx.stroke()", "undefined");
+ _assertSame(ctx.clip(), undefined, "ctx.clip()", "undefined");
+ if (ctx.fillText) {
+ _assertSame(ctx.fillText('test', 0, 0), undefined, "ctx.fillText('test', 0, 0)", "undefined");
+ _assertSame(ctx.strokeText('test', 0, 0), undefined, "ctx.strokeText('test', 0, 0)", "undefined");
+ }
+ if (ctx.putImageData) {
+ _assertSame(ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0), undefined, "ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0)", "undefined");
+ }
+ _assertSame(ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white'), undefined, "ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white')", "undefined");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.delete.html b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.delete.html
new file mode 100644
index 0000000000..a33cc869d6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.delete.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.delete</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.delete</h1>
+<p class="desc">window.CanvasRenderingContext2D is Configurable</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("window.CanvasRenderingContext2D is Configurable");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.CanvasRenderingContext2D, undefined, "window.CanvasRenderingContext2D", "undefined");
+ _assertSame(delete window.CanvasRenderingContext2D, true, "delete window.CanvasRenderingContext2D", "true");
+ _assertSame(window.CanvasRenderingContext2D, undefined, "window.CanvasRenderingContext2D", "undefined");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.drawings.html b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.drawings.html
new file mode 100644
index 0000000000..efdad2eab9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.drawings.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.drawings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.drawings</h1>
+<p class="desc">void methods return undefined</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("void methods return undefined");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.drawImage(document.getElementById('yellow.png'), 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(document.getElementById('yellow.png'), 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.missingargs.html b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.missingargs.html
new file mode 100644
index 0000000000..6744c6c381
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/conformance-requirements/2d.conformance.requirements.missingargs.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.conformance.requirements.missingargs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.conformance.requirements.missingargs</h1>
+<p class="desc">Missing arguments cause TypeError</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Missing arguments cause TypeError");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.scale(); });
+ assert_throws_js(TypeError, function() { ctx.scale(1); });
+ assert_throws_js(TypeError, function() { ctx.rotate(); });
+ assert_throws_js(TypeError, function() { ctx.translate(); });
+ assert_throws_js(TypeError, function() { ctx.translate(0); });
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ assert_throws_js(TypeError, function() { ctx.transform(); });
+ assert_throws_js(TypeError, function() { ctx.transform(1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1, 0); });
+ }
+ if (ctx.setTransform) {
+ assert_throws_js(TypeError, function() { ctx.setTransform(1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1, 0); });
+ }
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createPattern(canvas); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.moveTo(); });
+ assert_throws_js(TypeError, function() { ctx.moveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.lineTo(); });
+ assert_throws_js(TypeError, function() { ctx.lineTo(0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.rect(); });
+ assert_throws_js(TypeError, function() { ctx.rect(0); });
+ assert_throws_js(TypeError, function() { ctx.rect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.rect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arc(); });
+ assert_throws_js(TypeError, function() { ctx.arc(0); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1, 0); });
+ // (6th argument to arc is optional)
+ if (ctx.isPointInPath) {
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(0); });
+ }
+ if (ctx.drawFocusRing) {
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas, 0); });
+ }
+ if (ctx.fillText) {
+ assert_throws_js(TypeError, function() { ctx.fillText(); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test'); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeText(); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test'); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.measureText(); });
+ }
+ assert_throws_js(TypeError, function() { ctx.drawImage(); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(canvas, 0); });
+ // TODO: n >= 3 args on drawImage could be either a valid overload,
+ // or too few for another overload, or too many for another
+ // overload - what should happen?
+ if (ctx.createImageData) {
+ assert_throws_js(TypeError, function() { ctx.createImageData(); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(1); });
+ }
+ if (ctx.getImageData) {
+ assert_throws_js(TypeError, function() { ctx.getImageData(); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0, 1); });
+ }
+ if (ctx.putImageData) {
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ assert_throws_js(TypeError, function() { ctx.putImageData(); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0); });
+ }
+ var g = ctx.createLinearGradient(0, 0, 0, 0);
+ assert_throws_js(TypeError, function() { g.addColorStop(); });
+ assert_throws_js(TypeError, function() { g.addColorStop(0); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.3arg.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.3arg.html
new file mode 100644
index 0000000000..d9e2de563b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.3arg.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.3arg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.3arg</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0);
+ ctx.drawImage(bitmap_red, -100, 0);
+ ctx.drawImage(bitmap_red, 100, 0);
+ ctx.drawImage(bitmap_red, 0, -50);
+ ctx.drawImage(bitmap_red, 0, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.5arg.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.5arg.html
new file mode 100644
index 0000000000..befa7108f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.5arg.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.5arg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.5arg</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html
new file mode 100644
index 0000000000..f71eebc2e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.9arg.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.9arg.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html
new file mode 100644
index 0000000000..98da8eccbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.9arg.destpos</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.9arg.destpos</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, -100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -50, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html
new file mode 100644
index 0000000000..f21096cd96
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.9arg.destsize</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.9arg.destsize</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 1, 1, 1, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, -50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -25, 100, 25);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 25);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html
new file mode 100644
index 0000000000..ca4cc18948
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.9arg.sourcepos</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.9arg.sourcepos</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 140, 20, 100, 50, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html
new file mode 100644
index 0000000000..fb38943cfa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.9arg.sourcesize</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.9arg.sourcesize</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 0, 256, 256, 0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 51, 26);
+ ctx.fillRect(49, 24, 51, 26);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 20,20, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,20, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 20,30, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,30, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.alpha.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.alpha.html
new file mode 100644
index 0000000000..52acd11803
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.alpha.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.alpha</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.animated.gif.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.animated.gif.html
new file mode 100644
index 0000000000..6330d06ade
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.animated.gif.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.animated.gif</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.animated.gif</h1>
+<p class="desc">drawImage() of an animated GIF draws the first frame</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage() of an animated GIF draws the first frame");
+_addTest(function(canvas, ctx) {
+
+ deferTest();
+ step_timeout(t.step_func_done(function () {
+ ctx.drawImage(document.getElementById('anim-gr.gif'), 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ }), 500);
+
+});
+</script>
+<img src="/images/anim-gr.gif" id="anim-gr.gif" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.broken.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.broken.html
new file mode 100644
index 0000000000..f6516a8ecc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.broken.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.broken</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.broken</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ const response = await fetch('/images/broken.png');
+ const blob = await response.blob();
+
+ await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(blob), 'The source image could not be decoded.');
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.canvas.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.canvas.html
new file mode 100644
index 0000000000..d1f9aeb326
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.canvas.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.canvas</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.drawImage(canvas2, 0, 0);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+ ctx.drawImage(document.createElement('canvas'), 0, 0);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.clip.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.clip.html
new file mode 100644
index 0000000000..70ec029d8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.clip.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.clip</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.composite.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.composite.html
new file mode 100644
index 0000000000..4ff2026aad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.composite.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.composite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.composite</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.floatsource.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
new file mode 100644
index 0000000000..3e100c257f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.floatsource</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.floatsource</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html
new file mode 100644
index 0000000000..849f41aaf6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.incomplete.emptysrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.incomplete.emptysrc</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('red.png');
+ img.src = "";
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.immediate.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.immediate.html
new file mode 100644
index 0000000000..d2e8108d90
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.immediate.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.incomplete.immediate</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.incomplete.immediate</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = new Image();
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html
new file mode 100644
index 0000000000..dccf75c6d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.incomplete.nosrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.incomplete.nosrc</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = new Image();
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.reload.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.reload.html
new file mode 100644
index 0000000000..b4a8440e46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.reload.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.incomplete.reload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.incomplete.reload</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('yellow.png');
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm,
+ // and resets the image to the "unavailable" state.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html
new file mode 100644
index 0000000000..91e11eb15c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.incomplete.removedsrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.incomplete.removedsrc</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('red.png');
+ img.removeAttribute('src');
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedest.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedest.html
new file mode 100644
index 0000000000..94d4f71a7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedest.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.negativedest</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.negativedest</h1>
+<p class="desc">Negative destination width/height represents the correct rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, 50, 50, 0, 50, 50, -50);
+ ctx.drawImage(bitmap, 100, 128, 50, -50, 100, 50, -50, -50);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Negative destination width/height represents the correct rectangle");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedir.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedir.html
new file mode 100644
index 0000000000..281da9a573
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativedir.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.negativedir</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.negativedir</h1>
+<p class="desc">Negative dimensions do not affect the direction of the image</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 178, 50, -100, 0, 0, 50, 100);
+ ctx.drawImage(bitmap, 0, 78, 50, 100, 50, 100, 50, -100);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Negative dimensions do not affect the direction of the image");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativesource.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativesource.html
new file mode 100644
index 0000000000..5a504973d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.negativesource.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.negativesource</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.negativesource</h1>
+<p class="desc">Negative source width/height represents the correct rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, -100, 50, 0, 0, 50, 50);
+ ctx.drawImage(bitmap, 100, 128, -100, -50, 50, 0, 50, 50);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Negative source width/height represents the correct rectangle");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonexistent.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonexistent.html
new file mode 100644
index 0000000000..0cebf6cf68
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonexistent.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.nonexistent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.nonexistent</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('not-found-at-all.png');
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(img, 0, 0); });
+
+});
+</script>
+<img src="/images/not-found-at-all.png" id="not-found-at-all.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html
new file mode 100644
index 0000000000..8225d15fdd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html
@@ -0,0 +1,335 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.nonfinite</h1>
+<p class="desc">drawImage() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, Infinity, 0);
+ ctx.drawImage(bitmap, -Infinity, 0);
+ ctx.drawImage(bitmap, NaN, 0);
+ ctx.drawImage(bitmap, 0, Infinity);
+ ctx.drawImage(bitmap, 0, -Infinity);
+ ctx.drawImage(bitmap, 0, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, -Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, NaN, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, -Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, NaN, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, -Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, NaN, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, -Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, -Infinity, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, NaN, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, -Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, NaN, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, -Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, NaN, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, -Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, NaN, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, -Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, NaN, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, -Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, NaN, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, -Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, NaN, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, -Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+}, "drawImage() with Infinity/NaN is ignored");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nowrap.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nowrap.html
new file mode 100644
index 0000000000..8c32461b3a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nowrap.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.nowrap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.nowrap</h1>
+<p class="desc">Stretched images do not get pixels wrapping around the edges</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, -1950, 0, 2000, 50);
+ _assertPixelApprox(canvas, 45,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 55,25, 0,255,0,255, 2);
+
+}, "Stretched images do not get pixels wrapping around the edges");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.null.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.null.html
new file mode 100644
index 0000000000..0f46c97dce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.null.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.null</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.drawImage(null, 0, 0); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.path.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.path.html
new file mode 100644
index 0000000000..52852ec32e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.path.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.path</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ ctx.fill();
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.1.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.1.html
new file mode 100644
index 0000000000..90eaaea5d9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.self.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.self.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.drawImage(canvas, 50, 0);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.2.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.2.html
new file mode 100644
index 0000000000..39e619ddc5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.self.2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.self.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.self.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 1, 100, 49);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 1);
+ ctx.drawImage(canvas, 0, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 2);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.svg.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.svg.html
new file mode 100644
index 0000000000..bcfcdadb28
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.svg.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.svg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.svg</h1>
+<p class="desc">drawImage() of an SVG image</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ const img = new Image();
+ const imageLoadPromise = new Promise((resolve, reject) => {
+ img.onload = () => resolve();
+ img.onerror = (err) => reject(err);
+ });
+ img.src = '/images/green.svg';
+ await imageLoadPromise;
+
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50, 25, 0, 255, 0, 255, 2);
+
+}, "drawImage() of an SVG image");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.transform.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.transform.html
new file mode 100644
index 0000000000..42a277e200
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.transform.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.transform</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(100, 0);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html
new file mode 100644
index 0000000000..73e8f45090
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.wrongtype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.wrongtype</h1>
+<p class="desc">Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.drawImage(undefined, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.drawImage("", 0, 0); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.paragraph.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.paragraph.html
new file mode 100644
index 0000000000..ef0be39ca7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.wrongtype.paragraph.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.wrongtype.paragraph</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.wrongtype.paragraph</h1>
+<p class="desc">Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.drawImage(document.createElement('p'), 0, 0); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
new file mode 100644
index 0000000000..d3c644cf88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.zerocanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.zerocanvas</h1>
+<p class="desc">drawImage with zero-sized canvas as the source shoud throw exception</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage with zero-sized canvas as the source shoud throw exception");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 0;
+ canvas2.height = 50;
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(canvas2, 0, 0); });
+
+ canvas2.width = 50;
+ canvas2.height = 0;
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(canvas2, 0, 0); });
+
+ canvas2.width = 0;
+ canvas2.height = 0;
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(canvas2, 0, 0); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.html
new file mode 100644
index 0000000000..2b7b4ebea6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.zerosource</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.zerosource</h1>
+<p class="desc">drawImage with zero-sized source rectangle draws nothing without exception</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10, 10, 0, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "drawImage with zero-sized source rectangle draws nothing without exception");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html
new file mode 100644
index 0000000000..ea00f1f9a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.drawImage.zerosource.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.drawImage.zerosource.image</h1>
+<p class="desc">drawImage with zero-sized source rectangle from image draws nothing without exception</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ function loadImage(src) {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = () => resolve(img);
+ img.onerror = (err) => reject(err);
+ img.src = src;
+ });
+ }
+ const img1 = await loadImage('/images/red-zerowidth.svg');
+ const img2 = await loadImage('/images/red-zeroheight.svg');
+ const img3 = await loadImage('/images/red-zerosize.svg');
+
+ ctx.drawImage(img1, 0, 0, 100, 50);
+ ctx.drawImage(img2, 0, 0, 100, 50);
+ ctx.drawImage(img3, 0, 0, 100, 50);
+ _assertPixel(canvas, 50, 25, 0, 255, 0, 255);
+
+}, "drawImage with zero-sized source rectangle from image draws nothing without exception");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html
new file mode 100644
index 0000000000..78a8c3ac06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.basic</h1>
+<p class="desc">clearRect clears to transparent black</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect clears to transparent black");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html
new file mode 100644
index 0000000000..eb43a401e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.clip</h1>
+<p class="desc">clearRect is affected by clipping regions</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect is affected by clipping regions");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html
new file mode 100644
index 0000000000..73b87ecc81
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.globalalpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.globalalpha</h1>
+<p class="desc">clearRect is not affected by globalAlpha</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect is not affected by globalAlpha");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0.1;
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html
new file mode 100644
index 0000000000..9eb3d33834
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.globalcomposite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.globalcomposite</h1>
+<p class="desc">clearRect is not affected by globalCompositeOperation</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect is not affected by globalCompositeOperation");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html
new file mode 100644
index 0000000000..baa48e2ba3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.negative</h1>
+<p class="desc">clearRect of negative sizes works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect of negative sizes works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 50, 25);
+ ctx.clearRect(100, 0, -50, 25);
+ ctx.clearRect(0, 50, 50, -25);
+ ctx.clearRect(100, 50, -50, -25);
+ _assertPixel(canvas, 25,12, 0,0,0,0);
+ _assertPixel(canvas, 75,12, 0,0,0,0);
+ _assertPixel(canvas, 25,37, 0,0,0,0);
+ _assertPixel(canvas, 75,37, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html
new file mode 100644
index 0000000000..726ed05772
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.nonfinite</h1>
+<p class="desc">clearRect() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(Infinity, 0, 100, 50);
+ ctx.clearRect(-Infinity, 0, 100, 50);
+ ctx.clearRect(NaN, 0, 100, 50);
+ ctx.clearRect(0, Infinity, 100, 50);
+ ctx.clearRect(0, -Infinity, 100, 50);
+ ctx.clearRect(0, NaN, 100, 50);
+ ctx.clearRect(0, 0, Infinity, 50);
+ ctx.clearRect(0, 0, -Infinity, 50);
+ ctx.clearRect(0, 0, NaN, 50);
+ ctx.clearRect(0, 0, 100, Infinity);
+ ctx.clearRect(0, 0, 100, -Infinity);
+ ctx.clearRect(0, 0, 100, NaN);
+ ctx.clearRect(Infinity, Infinity, 100, 50);
+ ctx.clearRect(Infinity, Infinity, Infinity, 50);
+ ctx.clearRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.clearRect(Infinity, Infinity, 100, Infinity);
+ ctx.clearRect(Infinity, 0, Infinity, 50);
+ ctx.clearRect(Infinity, 0, Infinity, Infinity);
+ ctx.clearRect(Infinity, 0, 100, Infinity);
+ ctx.clearRect(0, Infinity, Infinity, 50);
+ ctx.clearRect(0, Infinity, Infinity, Infinity);
+ ctx.clearRect(0, Infinity, 100, Infinity);
+ ctx.clearRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.path.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.path.html
new file mode 100644
index 0000000000..77c9c037b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.path.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.path</h1>
+<p class="desc">clearRect does not affect the current path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect does not affect the current path");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 16, 16);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html
new file mode 100644
index 0000000000..8240073a28
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.shadow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.shadow</h1>
+<p class="desc">clearRect does not draw shadows</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect does not draw shadows");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.clearRect(0, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html
new file mode 100644
index 0000000000..347bedb835
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.transform</h1>
+<p class="desc">clearRect is affected by transforms</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect is affected by transforms");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.clearRect(0, -5, 10, 5);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html
new file mode 100644
index 0000000000..2c192a7102
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.clearRect.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.clearRect.zero</h1>
+<p class="desc">clearRect of zero pixels has no effect</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("clearRect of zero pixels has no effect");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 0);
+ ctx.clearRect(0, 0, 0, 50);
+ ctx.clearRect(0, 0, 0, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html
new file mode 100644
index 0000000000..f29daf426a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.basic</h1>
+<p class="desc">fillRect works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html
new file mode 100644
index 0000000000..cc46b765d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.clip</h1>
+<p class="desc">fillRect is affected by clipping regions</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect is affected by clipping regions");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html
new file mode 100644
index 0000000000..7c64952e8b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.negative</h1>
+<p class="desc">fillRect of negative sizes works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect of negative sizes works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillRect(100, 0, -50, 25);
+ ctx.fillRect(0, 50, 50, -25);
+ ctx.fillRect(100, 50, -50, -25);
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html
new file mode 100644
index 0000000000..090607766d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.nonfinite</h1>
+<p class="desc">fillRect() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(Infinity, 0, 100, 50);
+ ctx.fillRect(-Infinity, 0, 100, 50);
+ ctx.fillRect(NaN, 0, 100, 50);
+ ctx.fillRect(0, Infinity, 100, 50);
+ ctx.fillRect(0, -Infinity, 100, 50);
+ ctx.fillRect(0, NaN, 100, 50);
+ ctx.fillRect(0, 0, Infinity, 50);
+ ctx.fillRect(0, 0, -Infinity, 50);
+ ctx.fillRect(0, 0, NaN, 50);
+ ctx.fillRect(0, 0, 100, Infinity);
+ ctx.fillRect(0, 0, 100, -Infinity);
+ ctx.fillRect(0, 0, 100, NaN);
+ ctx.fillRect(Infinity, Infinity, 100, 50);
+ ctx.fillRect(Infinity, Infinity, Infinity, 50);
+ ctx.fillRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.fillRect(Infinity, Infinity, 100, Infinity);
+ ctx.fillRect(Infinity, 0, Infinity, 50);
+ ctx.fillRect(Infinity, 0, Infinity, Infinity);
+ ctx.fillRect(Infinity, 0, 100, Infinity);
+ ctx.fillRect(0, Infinity, Infinity, 50);
+ ctx.fillRect(0, Infinity, Infinity, Infinity);
+ ctx.fillRect(0, Infinity, 100, Infinity);
+ ctx.fillRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.path.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.path.html
new file mode 100644
index 0000000000..39a6e18103
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.path.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.path</h1>
+<p class="desc">fillRect does not affect the current path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect does not affect the current path");
+_addTest(function(canvas, ctx) {
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html
new file mode 100644
index 0000000000..48aa8538f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.shadow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.shadow</h1>
+<p class="desc">fillRect draws shadows</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect draws shadows");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html
new file mode 100644
index 0000000000..de6f790bc7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.transform</h1>
+<p class="desc">fillRect is affected by transforms</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect is affected by transforms");
+_addTest(function(canvas, ctx) {
+
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -5, 10, 5);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html
new file mode 100644
index 0000000000..095eb19a66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillRect.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillRect.zero</h1>
+<p class="desc">fillRect of zero pixels has no effect</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect of zero pixels has no effect");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 0);
+ ctx.fillRect(0, 0, 0, 50);
+ ctx.fillRect(0, 0, 0, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html
new file mode 100644
index 0000000000..d2ea813178
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.basic</h1>
+<p class="desc">strokeRect works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect works");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html
new file mode 100644
index 0000000000..31db69b6cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.clip</h1>
+<p class="desc">strokeRect is affected by clipping regions</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect is affected by clipping regions");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html
new file mode 100644
index 0000000000..323b663145
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.globalalpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.globalalpha</h1>
+<p class="desc">strokeRect is affected by globalAlpha</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect is affected by globalAlpha");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalAlpha = 0;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html
new file mode 100644
index 0000000000..671db7f07a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.globalcomposite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.globalcomposite</h1>
+<p class="desc">strokeRect is not affected by globalCompositeOperation</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect is not affected by globalCompositeOperation");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html
new file mode 100644
index 0000000000..20bbc5bafe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.negative</h1>
+<p class="desc">strokeRect of negative sizes works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect of negative sizes works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 25;
+ ctx.strokeRect(12, 12, 26, 1);
+ ctx.strokeRect(88, 12, -26, 1);
+ ctx.strokeRect(12, 38, 26, -1);
+ ctx.strokeRect(88, 38, -26, -1);
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html
new file mode 100644
index 0000000000..af7a393c8f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.nonfinite</h1>
+<p class="desc">strokeRect() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 150;
+ ctx.strokeRect(Infinity, 0, 100, 50);
+ ctx.strokeRect(-Infinity, 0, 100, 50);
+ ctx.strokeRect(NaN, 0, 100, 50);
+ ctx.strokeRect(0, Infinity, 100, 50);
+ ctx.strokeRect(0, -Infinity, 100, 50);
+ ctx.strokeRect(0, NaN, 100, 50);
+ ctx.strokeRect(0, 0, Infinity, 50);
+ ctx.strokeRect(0, 0, -Infinity, 50);
+ ctx.strokeRect(0, 0, NaN, 50);
+ ctx.strokeRect(0, 0, 100, Infinity);
+ ctx.strokeRect(0, 0, 100, -Infinity);
+ ctx.strokeRect(0, 0, 100, NaN);
+ ctx.strokeRect(Infinity, Infinity, 100, 50);
+ ctx.strokeRect(Infinity, Infinity, Infinity, 50);
+ ctx.strokeRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.strokeRect(Infinity, Infinity, 100, Infinity);
+ ctx.strokeRect(Infinity, 0, Infinity, 50);
+ ctx.strokeRect(Infinity, 0, Infinity, Infinity);
+ ctx.strokeRect(Infinity, 0, 100, Infinity);
+ ctx.strokeRect(0, Infinity, Infinity, 50);
+ ctx.strokeRect(0, Infinity, Infinity, Infinity);
+ ctx.strokeRect(0, Infinity, 100, Infinity);
+ ctx.strokeRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html
new file mode 100644
index 0000000000..121789d864
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.path</h1>
+<p class="desc">strokeRect does not affect the current path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect does not affect the current path");
+_addTest(function(canvas, ctx) {
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html
new file mode 100644
index 0000000000..8e7a50a745
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.shadow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.shadow</h1>
+<p class="desc">strokeRect draws shadows</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect draws shadows");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, -75, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html
new file mode 100644
index 0000000000..312b9d2003
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.transform</h1>
+<p class="desc">fillRect is affected by transforms</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillRect is affected by transforms");
+_addTest(function(canvas, ctx) {
+
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(2.5, -2.6, 5, 0.2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html
new file mode 100644
index 0000000000..c140a2668b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.zero.1</h1>
+<p class="desc">strokeRect of 0x0 pixels draws nothing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect of 0x0 pixels draws nothing");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.strokeRect(50, 25, 0, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html
new file mode 100644
index 0000000000..f17620a302
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.zero.2</h1>
+<p class="desc">strokeRect of 0x0 pixels draws nothing, including caps and joins</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect of 0x0 pixels draws nothing, including caps and joins");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(50, 25, 0, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html
new file mode 100644
index 0000000000..e671ae4116
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.zero.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.zero.3</h1>
+<p class="desc">strokeRect of Nx0 pixels draws a straight line</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect of Nx0 pixels draws a straight line");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html
new file mode 100644
index 0000000000..ff3c4040b3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.zero.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.zero.4</h1>
+<p class="desc">strokeRect of Nx0 pixels draws a closed line with no caps</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect of Nx0 pixels draws a closed line with no caps");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html
new file mode 100644
index 0000000000..809781ab43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeRect.zero.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeRect.zero.5</h1>
+<p class="desc">strokeRect of Nx0 pixels draws a closed line with joins</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeRect of Nx0 pixels draws a closed line with joins");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 250;
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
new file mode 100644
index 0000000000..481d6fe9e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.CSSHSL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.CSSHSL</h1>
+<p class="desc">CSSHSL works as color input</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("CSSHSL works as color input");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = new CSSHSL(CSS.deg(180), 0.5, 0.5);
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 64,191,191,255, 3);
+
+ const color = new CSSHSL(CSS.deg(180), 1, 1);
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#ffffff', "ctx.fillStyle", "'#ffffff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+ color.l = 0.5;
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#00ffff', "ctx.fillStyle", "'#00ffff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,255,255);
+
+ ctx.fillStyle = new CSSRGB(1, 0, 1).toHSL();
+ _assertSame(ctx.fillStyle, '#ff00ff', "ctx.fillStyle", "'#ff00ff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,0,255,255);
+
+ color.h = CSS.deg(120);
+ color.s = 1;
+ color.l = 0.5;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html
new file mode 100644
index 0000000000..cfdb603d5d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.CSSRGB</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.CSSRGB</h1>
+<p class="desc">CSSRGB works as color input</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("CSSRGB works as color input");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = new CSSRGB(1, 0, 1);
+ _assertSame(ctx.fillStyle, '#ff00ff', "ctx.fillStyle", "'#ff00ff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,0,255,255);
+
+ const color = new CSSRGB(0, CSS.percent(50), 0);
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#008000', "ctx.fillStyle", "'#008000'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,128,0,255);
+ color.g = 0;
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#000000', "ctx.fillStyle", "'#000000'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+
+ color.alpha = 0;
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, 'rgba(0, 0, 0, 0)', "ctx.fillStyle", "'rgba(0, 0, 0, 0)'");
+ ctx.reset();
+ color.alpha = 0.5;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,128);
+
+ ctx.fillStyle = new CSSHSL(CSS.deg(0), 1, 1).toRGB();
+ _assertSame(ctx.fillStyle, '#ffffff', "ctx.fillStyle", "'#ffffff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+
+ color.alpha = 1;
+ color.g = 1;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.html
new file mode 100644
index 0000000000..ae6507350c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.colorObject</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.colorObject</h1>
+<p class="desc">ctx.fillStyle works with color objects</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("ctx.fillStyle works with color objects");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = {r: 1, g: 0, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,0,0,255);
+ ctx.fillStyle = {r: 0, g: 0, b: 1};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,255,255);
+ ctx.fillStyle = {r: 0.2, g: 0.4, b: 0.6};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 51,102,153,255);
+ ctx.fillStyle = {r: 0, g: 1, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ ctx.fillStyle = {r: -1, g: 0, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+ ctx.fillStyle = {r: 0, g: 2, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.transparency.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.transparency.html
new file mode 100644
index 0000000000..b48715f093
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colorObject.transparency.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.colorObject.transparency</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.colorObject.transparency</h1>
+<p class="desc">ctx.fillStyle with color objects has transparency</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("ctx.fillStyle with color objects has transparency");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: -1};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: 0.5};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,128);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: 1};
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.currentcolor.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.currentcolor.html
new file mode 100644
index 0000000000..191395a20b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.currentcolor.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.colormix.currentcolor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.colormix.currentcolor</h1>
+<p class="desc">color-mix works as color input with currentcolor</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("color-mix works as color input with currentcolor");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('style', 'color: magenta');
+ ctx.fillStyle = "color-mix(in srgb, black, currentcolor)";
+ _assertSame(ctx.fillStyle, 'color(srgb 0.5 0 0.5)', "ctx.fillStyle", "'color(srgb 0.5 0 0.5)'");
+ ctx.strokeStyle = "color-mix(in srgb, black, currentcolor)";
+ _assertSame(ctx.strokeStyle, 'color(srgb 0.5 0 0.5)', "ctx.strokeStyle", "'color(srgb 0.5 0 0.5)'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.html
new file mode 100644
index 0000000000..1faa6c0a9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.colormix.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.colormix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.colormix</h1>
+<p class="desc">color-mix works as color input</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("color-mix works as color input");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = "color-mix(in srgb, red, blue)";
+ _assertSame(ctx.fillStyle, 'color(srgb 0.5 0 0.5)', "ctx.fillStyle", "'color(srgb 0.5 0 0.5)'");
+ ctx.fillStyle = "color-mix(in srgb, red, color(srgb 1 0 0))";
+ _assertSame(ctx.fillStyle, 'color(srgb 1 0 0)', "ctx.fillStyle", "'color(srgb 1 0 0)'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.default.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.default.html
new file mode 100644
index 0000000000..1bd3e2d4ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.default.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.fillStyle, '#000000', "ctx.fillStyle", "'#000000'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html
new file mode 100644
index 0000000000..423293e1b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.get.halftransparent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.get.halftransparent</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(255,255,255,0.5)';
+ _assertSame(ctx.fillStyle, 'rgba(255, 255, 255, 0.5)', "ctx.fillStyle", "'rgba(255, 255, 255, 0.5)'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html
new file mode 100644
index 0000000000..ec63d7662d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.get.semitransparent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.get.semitransparent</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(255,255,255,0.45)';
+ assert_regexp_match(ctx.fillStyle, /^rgba\(255, 255, 255, 0\.4\d+\)$/);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.solid.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.solid.html
new file mode 100644
index 0000000000..bcdd57a7ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.solid.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.get.solid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.get.solid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#fa0';
+ _assertSame(ctx.fillStyle, '#ffaa00', "ctx.fillStyle", "'#ffaa00'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.transparent.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.transparent.html
new file mode 100644
index 0000000000..21931aaf23
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.get.transparent.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.get.transparent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.get.transparent</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0,0,0,0)';
+ _assertSame(ctx.fillStyle, 'rgba(0, 0, 0, 0)', "ctx.fillStyle", "'rgba(0, 0, 0, 0)'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidstring.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidstring.html
new file mode 100644
index 0000000000..9fb83063f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidstring.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.invalidstring</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.invalidstring</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = 'invalid';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidtype.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidtype.html
new file mode 100644
index 0000000000..84d8fbd3ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.invalidtype.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.invalidtype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.invalidtype</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = null;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html
new file mode 100644
index 0000000000..87b5bc9371
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html
new file mode 100644
index 0000000000..746c358dbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0% / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html
new file mode 100644
index 0000000000..b8a9cd5a09
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html
new file mode 100644
index 0000000000..d5e58b9321
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html
new file mode 100644
index 0000000000..85bbb802b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html
new file mode 100644
index 0000000000..154bdcd49a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-6.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html
new file mode 100644
index 0000000000..5cf81e41a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-7</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-7</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-7.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(133.33333333grad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html
new file mode 100644
index 0000000000..17d0782df1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-8</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-8.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(2.0943951024rad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html
new file mode 100644
index 0000000000..2f4b1e8e1a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsl-9</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-9</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsl-9.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(0.3333333333turn, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html
new file mode 100644
index 0000000000..bc70e21d62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html
new file mode 100644
index 0000000000..7f77498531
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0% / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html
new file mode 100644
index 0000000000..c33a50a946
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html
new file mode 100644
index 0000000000..3d0cafc66b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html
new file mode 100644
index 0000000000..a70613b49f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html
new file mode 100644
index 0000000000..8c46d887ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-6.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html
new file mode 100644
index 0000000000..b6b4760086
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-7</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-7</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-7.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(133.33333333grad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html
new file mode 100644
index 0000000000..7933d47fd2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-8</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-8.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(2.0943951024rad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html
new file mode 100644
index 0000000000..8a4e29d40e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-hsla-9</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-9</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-hsla-9.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(0.3333333333turn, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html
new file mode 100644
index 0000000000..632b7f57fb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgb-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgb-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255.0, 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html
new file mode 100644
index 0000000000..1d3e683b35
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgb-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgb-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html
new file mode 100644
index 0000000000..8a6f352350
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgb-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgb-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html
new file mode 100644
index 0000000000..1fdf9ecf92
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgb-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgb-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html
new file mode 100644
index 0000000000..a1d99d5e24
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgb-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgb-5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0 / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html
new file mode 100644
index 0000000000..ad2c5f5484
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgb-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgb-6.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0 / 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html
new file mode 100644
index 0000000000..f394dbfb2f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgba-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgba-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255.0, 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html
new file mode 100644
index 0000000000..5529bc1462
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgba-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgba-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html
new file mode 100644
index 0000000000..82047a5698
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgba-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgba-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html
new file mode 100644
index 0000000000..5fea546f10
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgba-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgba-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html
new file mode 100644
index 0000000000..d048566522
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgba-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgba-5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0 / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html
new file mode 100644
index 0000000000..28feb2eb17
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.css-color-4-rgba-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.css-color-4-rgba-6.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0 / 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.png
new file mode 100644
index 0000000000..c5661de82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.basic.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.basic.html
new file mode 100644
index 0000000000..a1481e36e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.basic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.current.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.current.basic</h1>
+<p class="desc">currentColor is computed from the canvas element</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("currentColor is computed from the canvas element");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('style', 'color: #0f0');
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'currentColor';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.changed.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.changed.html
new file mode 100644
index 0000000000..2f4d0fe4f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.changed.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.current.changed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.current.changed</h1>
+<p class="desc">currentColor is computed when the attribute is set, not when it is painted</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("currentColor is computed when the attribute is set, not when it is painted");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('style', 'color: #0f0');
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'currentColor';
+ canvas.setAttribute('style', 'color: #f00');
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.html
new file mode 100644
index 0000000000..96b070c8c1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.current.removed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.current.removed</h1>
+<p class="desc">currentColor is solid black when the canvas element is not in a document</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.current.removed.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("currentColor is solid black when the canvas element is not in a document");
+_addTest(function(canvas, ctx) {
+
+ // Try not to let it undetectably incorrectly pick up opaque-black
+ // from other parts of the document:
+ document.body.parentNode.setAttribute('style', 'color: #f00');
+ document.body.setAttribute('style', 'color: #f00');
+ canvas.setAttribute('style', 'color: #f00');
+
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillStyle = 'currentColor';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.drawImage(canvas2, 0, 0);
+
+ document.body.parentNode.removeAttribute('style');
+ document.body.removeAttribute('style');
+
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.png
new file mode 100644
index 0000000000..d638d03386
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.current.removed.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html
new file mode 100644
index 0000000000..017bfc88c2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hex3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hex3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hex3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html
new file mode 100644
index 0000000000..c8704edbb2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hex4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hex4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hex4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#0f0f';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html
new file mode 100644
index 0000000000..9644a46a0c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hex6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hex6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hex6.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#00fF00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex6.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html
new file mode 100644
index 0000000000..b2dd06982d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hex8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hex8</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hex8.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#00ff00ff';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hex8.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html
new file mode 100644
index 0000000000..ceb3da52e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html
new file mode 100644
index 0000000000..22e67e33d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl( -240 , 100% , 50% )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html
new file mode 100644
index 0000000000..e182952fbf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(360120, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html
new file mode 100644
index 0000000000..b320c1c98b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(-360240, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html
new file mode 100644
index 0000000000..1264f626de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html
new file mode 100644
index 0000000000..e0fc787559
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-6.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(+120, +100%, +50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html
new file mode 100644
index 0000000000..59d66c383c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-clamp-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 200%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html
new file mode 100644
index 0000000000..1a1939e47a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-clamp-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, -200%, 49.9%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 127,127,127,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.png
new file mode 100644
index 0000000000..88fd827985
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html
new file mode 100644
index 0000000000..56f3a0a8b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-clamp-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-clamp-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-clamp-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, 200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.png
new file mode 100644
index 0000000000..bf48767a88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html
new file mode 100644
index 0000000000..af9d11e678
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsl-clamp-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsl-clamp-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsl-clamp-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, -200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.png
new file mode 100644
index 0000000000..d638d03386
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html
new file mode 100644
index 0000000000..dbc738ffa7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 50%, 0.499)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.png
new file mode 100644
index 0000000000..2aa6265f06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html
new file mode 100644
index 0000000000..aa21789b02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla( 120.0 , 100.0% , 50.0% , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html
new file mode 100644
index 0000000000..2acac26e1a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-clamp-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 200%, 50%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html
new file mode 100644
index 0000000000..0f32fb5474
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-clamp-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, -200%, 49.9%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 127,127,127,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.png
new file mode 100644
index 0000000000..88fd827985
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html
new file mode 100644
index 0000000000..4bc134aec5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-clamp-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-clamp-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-clamp-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 200%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.png
new file mode 100644
index 0000000000..bf48767a88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html
new file mode 100644
index 0000000000..f8b2382755
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-clamp-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-clamp-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-clamp-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, -200%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.png
new file mode 100644
index 0000000000..d638d03386
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html
new file mode 100644
index 0000000000..9c5e2258b9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-clamp-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-clamp-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-clamp-5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 50%, 2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html
new file mode 100644
index 0000000000..153515eedd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.hsla-clamp-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.hsla-clamp-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.hsla-clamp-6.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 0%, -2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.html
new file mode 100644
index 0000000000..65d0d2020c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.html4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.html4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.html4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'limE';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.html4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html
new file mode 100644
index 0000000000..4046f825a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50% / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html
new file mode 100644
index 0000000000..2075f8e459
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0 100% 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html
new file mode 100644
index 0000000000..53bbf6c7ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100% 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html
new file mode 100644
index 0000000000..e8be93b59d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0 100% 50% /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html
new file mode 100644
index 0000000000..b8938adeb2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50% /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html
new file mode 100644
index 0000000000..7104ca6a84
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsla-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 100%, 50% / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html
new file mode 100644
index 0000000000..c340e7445d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsla-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0 100% 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html
new file mode 100644
index 0000000000..3f94ec16ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-hsla-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsla-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 100% 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html
new file mode 100644
index 0000000000..69503593fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, 0, 0 / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html
new file mode 100644
index 0000000000..96af643ee8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255 0 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html
new file mode 100644
index 0000000000..a2691acaf1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html
new file mode 100644
index 0000000000..eb5f56692a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(0 0 0 /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html
new file mode 100644
index 0000000000..7de9dd4911
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(0, 0, 0 /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html
new file mode 100644
index 0000000000..20b889c5cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgba-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgba-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0 / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html
new file mode 100644
index 0000000000..908545cebe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgba-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgba-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255 0 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html
new file mode 100644
index 0000000000..851314f643
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.css-color-4-rgba-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgba-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html
new file mode 100644
index 0000000000..9aba392276
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#f'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html
new file mode 100644
index 0000000000..361db1de43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#f0'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html
new file mode 100644
index 0000000000..7df089d840
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#g00'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html
new file mode 100644
index 0000000000..6089675b81
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg00'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html
new file mode 100644
index 0000000000..488daf4690
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#ff000'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html
new file mode 100644
index 0000000000..ec0def2e10
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex6</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg0000'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html
new file mode 100644
index 0000000000..2b07b8dc9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex7</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex7</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#ff0000f'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html
new file mode 100644
index 0000000000..f886d4a67c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hex8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hex8</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg0000ff'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html
new file mode 100644
index 0000000000..153f9cb513
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsl-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0%, 100%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html
new file mode 100644
index 0000000000..7a2bd4f930
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsl-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(z, 100%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html
new file mode 100644
index 0000000000..8ad0f54264
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsl-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 0, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html
new file mode 100644
index 0000000000..780b83b383
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsl-4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html
new file mode 100644
index 0000000000..ad5944f1c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsl-5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100.%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html
new file mode 100644
index 0000000000..948a9d8a36
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsl-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsl-6</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50%,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html
new file mode 100644
index 0000000000..7a4bdb318f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsla-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0%, 100%, 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html
new file mode 100644
index 0000000000..4164de3bbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsla-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 0, 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html
new file mode 100644
index 0000000000..109a22cdf9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.hsla-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.hsla-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 0, 50%, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html
new file mode 100644
index 0000000000..acdd4d4c8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.name-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.name-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'darkbrown'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html
new file mode 100644
index 0000000000..292f909bcd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.name-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.name-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'firebrick1'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html
new file mode 100644
index 0000000000..f47ec43f55
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.name-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.name-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'red blue'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html
new file mode 100644
index 0000000000..d15d787f63
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.name-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.name-4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '"red"'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html
new file mode 100644
index 0000000000..ce391d0069
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.name-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.name-5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '"red'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html
new file mode 100644
index 0000000000..934f725dd3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgb-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgb-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255.0, 0, 0,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html
new file mode 100644
index 0000000000..f92a06b450
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgb-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgb-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(100%, 0, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html
new file mode 100644
index 0000000000..8ebde1782a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgb-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgb-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, - 1, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html
new file mode 100644
index 0000000000..6e3d135f41
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgba-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgba-1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(100%, 0, 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html
new file mode 100644
index 0000000000..a6a61002da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgba-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgba-2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1. 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html
new file mode 100644
index 0000000000..464d562b2f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgba-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgba-3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1.)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html
new file mode 100644
index 0000000000..815ce2f8b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgba-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgba-4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, '; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html
new file mode 100644
index 0000000000..33c3e33dfc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.invalid.rgba-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.invalid.rgba-5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html
new file mode 100644
index 0000000000..0432a3bbd4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-clamp-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-1000, 1000, -1000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html
new file mode 100644
index 0000000000..cf81e3c73f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-clamp-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-200%, 200%, -200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html
new file mode 100644
index 0000000000..b3c609fbee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-clamp-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-clamp-3</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-clamp-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-2147483649, 4294967298, -18446744073709551619)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html
new file mode 100644
index 0000000000..60aa8563bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-clamp-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-clamp-4</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-clamp-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-1000000000000000000000000000000000000000, 1000000000000000000000000000000000000000, -1000000000000000000000000000000000000000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html
new file mode 100644
index 0000000000..242ca3933d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-clamp-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-clamp-5</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-clamp-5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html
new file mode 100644
index 0000000000..201874c27d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-eof</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-eof</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-eof.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html
new file mode 100644
index 0000000000..0ce1db836f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-num</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-num</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-num.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0,255,0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html
new file mode 100644
index 0000000000..9b3de61672
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgb-percent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgb-percent</h1>
+<p class="desc"></p>
+
+<p class="notes">CSS3 Color says "The integer value 255 corresponds to 100%". (In particular, it is not 254...)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgb-percent.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0% ,100% ,0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html
new file mode 100644
index 0000000000..587253ad69
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-clamp-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, -2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html
new file mode 100644
index 0000000000..4e8156e60a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-clamp-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html
new file mode 100644
index 0000000000..5f07d38b68
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-eof</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-eof</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-eof.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 1';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html
new file mode 100644
index 0000000000..0c329c96ea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-num-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-num-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-num-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , .499 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.png
new file mode 100644
index 0000000000..2aa6265f06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html
new file mode 100644
index 0000000000..384a7f412a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-num-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-num-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-num-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 0.499 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.png
new file mode 100644
index 0000000000..2aa6265f06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html
new file mode 100644
index 0000000000..60ee007577
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-percent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-percent</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-percent.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0%,100%,0%,0.499)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.png
new file mode 100644
index 0000000000..2aa6265f06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html
new file mode 100644
index 0000000000..eaec0debcb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-solid-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-solid-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-solid-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html
new file mode 100644
index 0000000000..947bbbce8d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-solid-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-solid-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-solid-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 1.0 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html
new file mode 100644
index 0000000000..052a12beae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-solid-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-solid-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-solid-3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , +1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html
new file mode 100644
index 0000000000..ee9cd4e34e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.rgba-solid-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.rgba-solid-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.rgba-solid-4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( -0 , 255 , +0 , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.png
new file mode 100644
index 0000000000..2733836c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html
new file mode 100644
index 0000000000..0ff839b8cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.svg-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.svg-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.svg-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'gray';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 128,128,128,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.png
new file mode 100644
index 0000000000..5bc39cc699
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html
new file mode 100644
index 0000000000..141f4bfe46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.svg-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.svg-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.svg-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 128,128,128,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.png
new file mode 100644
index 0000000000..5bc39cc699
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.system.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.system.html
new file mode 100644
index 0000000000..3a56389973
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.system.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.system</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.system</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'ThreeDDarkShadow';
+ assert_regexp_match(ctx.fillStyle, /^#(?!(FF0000|ff0000|f00)$)/); // test that it's not red
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html
new file mode 100644
index 0000000000..5b9a834c0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.transparent-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.transparent-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.transparent-1.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'transparent';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html
new file mode 100644
index 0000000000..a077534c23
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.parse.transparent-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.transparent-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.fillStyle.parse.transparent-2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'TrAnSpArEnT';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html
new file mode 100644
index 0000000000..2a12b05839
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.toStringFunctionCallback</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.toStringFunctionCallback</h1>
+<p class="desc">Passing a function in to ctx.fillStyle or ctx.strokeStyle with a toString callback works as specified</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Passing a function in to ctx.fillStyle or ctx.strokeStyle with a toString callback works as specified");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = { toString: function() { return "#008000"; } };
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ ctx.fillStyle = {};
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ ctx.fillStyle = 800000;
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ assert_throws_js(TypeError, function() { ctx.fillStyle = { toString: function() { throw new TypeError; } }; });
+ ctx.strokeStyle = { toString: function() { return "#008000"; } };
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ ctx.strokeStyle = {};
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ ctx.strokeStyle = 800000;
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ assert_throws_js(TypeError, function() { ctx.strokeStyle = { toString: function() { throw new TypeError; } }; });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html
new file mode 100644
index 0000000000..1a4cde4146
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.conic.invalid.inputs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.conic.invalid.inputs</h1>
+<p class="desc">Conic gradient function with invalid inputs</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Conic gradient function with invalid inputs");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(-Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(NaN, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, -Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, NaN, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, Infinity, Infinity); });
+
+ const g = ctx.createConicGradient(0, 0, 25);
+ assert_throws_js(TypeError, function() { g.addColorStop(-Infinity, '#f00'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(NaN, '#f00'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, -Infinity); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, NaN); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html
new file mode 100644
index 0000000000..ba85463b41
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.conic.negative.rotation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.conic.negative.rotation</h1>
+<p class="desc">Conic gradient with negative rotation</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Conic gradient with negative rotation");
+_addTest(function(canvas, ctx) {
+
+ const g = ctx.createConicGradient(-Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,15, 255,0,0,255, 3);
+ _assertPixelApprox(canvas, 75,40, 0,255,0,255, 3);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html
new file mode 100644
index 0000000000..f8cebbb0d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.conic.positive.rotation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.conic.positive.rotation</h1>
+<p class="desc">Conic gradient with positive rotation</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Conic gradient with positive rotation");
+_addTest(function(canvas, ctx) {
+
+ const g = ctx.createConicGradient(3*Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,15, 255,0,0,255, 3);
+ _assertPixelApprox(canvas, 75,40, 0,255,0,255, 3);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.empty.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.empty.html
new file mode 100644
index 0000000000..dcc7575254
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.empty.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.empty</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html
new file mode 100644
index 0000000000..13f48f37bf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.alpha</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.gradient.interpolate.alpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(0,0,255, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 191,191,63,255, 3);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 3);
+ _assertPixelApprox(canvas, 75,25, 63,63,191,255, 3);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.png
new file mode 100644
index 0000000000..af5ac0f07d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.html
new file mode 100644
index 0000000000..3f7e27abe0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.color</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.color</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.gradient.interpolate.color.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 191,191,63,255, 3);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 3);
+ _assertPixelApprox(canvas, 75,25, 63,63,191,255, 3);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.png
new file mode 100644
index 0000000000..af5ac0f07d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.color.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html
new file mode 100644
index 0000000000..0d635ed671
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.coloralpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.coloralpha</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.gradient.interpolate.coloralpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(255,255,0, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 190,190,65,65, 3);
+ _assertPixelApprox(canvas, 50,25, 126,126,128,128, 3);
+ _assertPixelApprox(canvas, 75,25, 62,62,192,192, 3);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.png
new file mode 100644
index 0000000000..552e6ee44b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html
new file mode 100644
index 0000000000..9e62f3436c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.multiple</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.gradient.interpolate.multiple.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.5, '#0ff');
+ g.addColorStop(1, '#f0f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ _assertPixelApprox(canvas, 50,25, 127,255,127,255, 3);
+ _assertPixelApprox(canvas, 100,25, 0,255,255,255, 3);
+ _assertPixelApprox(canvas, 150,25, 127,127,255,255, 3);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.png
new file mode 100644
index 0000000000..86122450d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.multiple.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.outside.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.outside.html
new file mode 100644
index 0000000000..b46a7bed36
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.outside.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.outside</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(25, 0, 75, 0);
+ g.addColorStop(0.4, '#0f0');
+ g.addColorStop(0.6, '#0f0');
+
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 20,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html
new file mode 100644
index 0000000000..099a3ea81a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.overlap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.overlap</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.gradient.interpolate.overlap.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.25, '#00f');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#ff0');
+ g.addColorStop(0.5, '#00f');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.75, '#00f');
+ g.addColorStop(0.75, '#f00');
+ g.addColorStop(0.75, '#ff0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ _assertPixelApprox(canvas, 49,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 51,25, 255,255,0,255, 16);
+ _assertPixelApprox(canvas, 99,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 101,25, 255,255,0,255, 16);
+ _assertPixelApprox(canvas, 149,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 151,25, 255,255,0,255, 16);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.png
new file mode 100644
index 0000000000..5c2bb964e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html
new file mode 100644
index 0000000000..a4a2955d3b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.overlap2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.overlap2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ var ps = [ 0, 1/10, 1/4, 1/3, 1/2, 3/4, 1 ];
+ for (var p = 0; p < ps.length; ++p)
+ {
+ g.addColorStop(ps[p], '#0f0');
+ for (var i = 0; i < 15; ++i)
+ g.addColorStop(ps[p], '#f00');
+ g.addColorStop(ps[p], '#0f0');
+ }
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 30,25, 0,255,0,255);
+ _assertPixel(canvas, 40,25, 0,255,0,255);
+ _assertPixel(canvas, 60,25, 0,255,0,255);
+ _assertPixel(canvas, 80,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.solid.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.solid.html
new file mode 100644
index 0000000000..9ac31626df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.solid.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.solid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.solid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html
new file mode 100644
index 0000000000..80302761d6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.vertical</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.vertical</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.gradient.interpolate.vertical.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,12, 191,191,63,255, 10);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 5);
+ _assertPixelApprox(canvas, 50,37, 63,63,191,255, 10);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.png
new file mode 100644
index 0000000000..37d6a00c62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.vertical.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html
new file mode 100644
index 0000000000..7a8892db11
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.zerosize.fill</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.zerosize.fill</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ _assertPixel(canvas, 40,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html
new file mode 100644
index 0000000000..7696cefdd0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.zerosize.fillRect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.zerosize.fillRect</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 40,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html
new file mode 100644
index 0000000000..98ef6c3425
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.zerosize.fillText</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.zerosize.fillText</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.fillText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html
new file mode 100644
index 0000000000..283e3874ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.zerosize.stroke</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.zerosize.stroke</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.rect(20, 20, 60, 10);
+ ctx.stroke();
+ _assertPixel(canvas, 19,19, 0,255,0,255);
+ _assertPixel(canvas, 20,19, 0,255,0,255);
+ _assertPixel(canvas, 21,19, 0,255,0,255);
+ _assertPixel(canvas, 19,20, 0,255,0,255);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ _assertPixel(canvas, 21,20, 0,255,0,255);
+ _assertPixel(canvas, 19,21, 0,255,0,255);
+ _assertPixel(canvas, 20,21, 0,255,0,255);
+ _assertPixel(canvas, 21,21, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html
new file mode 100644
index 0000000000..7d243ecbc4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.zerosize.strokeRect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.zerosize.strokeRect</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.strokeRect(20, 20, 60, 10);
+ _assertPixel(canvas, 19,19, 0,255,0,255);
+ _assertPixel(canvas, 20,19, 0,255,0,255);
+ _assertPixel(canvas, 21,19, 0,255,0,255);
+ _assertPixel(canvas, 19,20, 0,255,0,255);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ _assertPixel(canvas, 21,20, 0,255,0,255);
+ _assertPixel(canvas, 19,21, 0,255,0,255);
+ _assertPixel(canvas, 20,21, 0,255,0,255);
+ _assertPixel(canvas, 21,21, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html
new file mode 100644
index 0000000000..bab7c6b6d9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.interpolate.zerosize.strokeText</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.interpolate.zerosize.strokeText</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.strokeText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html
new file mode 100644
index 0000000000..89d0092715
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.linear.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.linear.nonfinite</h1>
+<p class="desc">createLinearGradient() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createLinearGradient() throws TypeError if arguments are not finite");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(-Infinity, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(NaN, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, -Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, NaN, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, -Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, NaN, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, Infinity, Infinity); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.1.html
new file mode 100644
index 0000000000..0ae3220f06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.linear.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.linear.transform.1</h1>
+<p class="desc">Linear gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Linear gradient coordinates are relative to the coordinate space at the time of filling");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.2.html
new file mode 100644
index 0000000000..8f761c666f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.linear.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.linear.transform.2</h1>
+<p class="desc">Linear gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Linear gradient coordinates are relative to the coordinate space at the time of filling");
+_addTest(function(canvas, ctx) {
+
+ ctx.translate(100, 0);
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-150, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.3.html
new file mode 100644
index 0000000000..fdd1e0e9a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.linear.transform.3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.linear.transform.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.linear.transform.3</h1>
+<p class="desc">Linear gradient transforms do not experience broken caching effects</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Linear gradient transforms do not experience broken caching effects");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.compare.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.compare.html
new file mode 100644
index 0000000000..9ff3e607c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.compare.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.compare</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.compare</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ var g2 = ctx.createLinearGradient(0, 0, 100, 0);
+ _assertDifferent(g1, g2, "g1", "g2");
+ ctx.fillStyle = g1;
+ _assertSame(ctx.fillStyle, g1, "ctx.fillStyle", "g1");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html
new file mode 100644
index 0000000000..9c30d9cbac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.crosscanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.crosscanvas</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = document.createElement('canvas').getContext('2d').createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.html
new file mode 100644
index 0000000000..95cc893671
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.current</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.current</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.gradient.object.current.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ canvas.setAttribute('style', 'color: #f00');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'currentColor');
+ g.addColorStop(1, 'currentColor');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.png b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.png
new file mode 100644
index 0000000000..d638d03386
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.current.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html
new file mode 100644
index 0000000000..2be7b3d6fd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.invalidcolor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.invalidcolor</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, null); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, undefined); });
+
+ var g = ctx.createRadialGradient(0, 0, 0, 100, 0, 0);
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, null); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, undefined); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html
new file mode 100644
index 0000000000..76aaed7115
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.invalidoffset</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.invalidoffset</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ assert_throws_dom("INDEX_SIZE_ERR", function() { g.addColorStop(-1, '#000'); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { g.addColorStop(2, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(Infinity, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(-Infinity, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(NaN, '#000'); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.return.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.return.html
new file mode 100644
index 0000000000..c32312142d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.return.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.return</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.return</h1>
+<p class="desc">createLinearGradient() and createRadialGradient() returns objects implementing CanvasGradient</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createLinearGradient() and createRadialGradient() returns objects implementing CanvasGradient");
+_addTest(function(canvas, ctx) {
+
+ window.CanvasGradient.prototype.thisImplementsCanvasGradient = true;
+
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ _assertDifferent(g1.addColorStop, undefined, "g1.addColorStop", "undefined");
+ _assertSame(g1.thisImplementsCanvasGradient, true, "g1.thisImplementsCanvasGradient", "true");
+
+ var g2 = ctx.createRadialGradient(0, 0, 10, 0, 0, 20);
+ _assertDifferent(g2.addColorStop, undefined, "g2.addColorStop", "undefined");
+ _assertSame(g2.thisImplementsCanvasGradient, true, "g2.thisImplementsCanvasGradient", "true");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.type.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.type.html
new file mode 100644
index 0000000000..37c77d2f2c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.type.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.type</h1>
+<p class="desc">window.CanvasGradient exists and has the right properties</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("window.CanvasGradient exists and has the right properties");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.CanvasGradient, undefined, "window.CanvasGradient", "undefined");
+ _assertDifferent(window.CanvasGradient.prototype.addColorStop, undefined, "window.CanvasGradient.prototype.addColorStop", "undefined");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.update.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.update.html
new file mode 100644
index 0000000000..ae78d0d78f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.object.update.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.object.update</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.object.update</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createLinearGradient(-100, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ g.addColorStop(0.1, '#0f0');
+ g.addColorStop(0.9, '#0f0');
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html
new file mode 100644
index 0000000000..d32e972cc5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.behind</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.behind</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, 25, 10, 211, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html
new file mode 100644
index 0000000000..b9774aeba6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.beside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.beside</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(0, 100, 40, 100, 100, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html
new file mode 100644
index 0000000000..15808ee50e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.bottom</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.bottom</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 101);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html
new file mode 100644
index 0000000000..067f129983
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.cylinder</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.cylinder</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 100);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.front.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.front.html
new file mode 100644
index 0000000000..88ecdb9810
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.front.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.front</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.front</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(311, 25, 10, 210, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html
new file mode 100644
index 0000000000..046e4d97b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.shape1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.shape1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(30+tol, 40);
+ ctx.lineTo(110, -20+tol);
+ ctx.lineTo(110, 100-tol);
+ ctx.fill();
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html
new file mode 100644
index 0000000000..1c7660ac99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.shape2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.shape2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(30-tol, 40);
+ ctx.lineTo(110, -20-tol);
+ ctx.lineTo(110, 100+tol);
+ ctx.fill();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.top.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.top.html
new file mode 100644
index 0000000000..6760b18db7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.cone.top.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.cone.top</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.cone.top</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(230, 25, 100, 100, 25, 101);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.equal.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.equal.html
new file mode 100644
index 0000000000..1ff7c44193
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.equal.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.equal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.equal</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 20, 50, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside1.html
new file mode 100644
index 0000000000..00ece5750e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.inside1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.inside1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 100, 50, 25, 200);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside2.html
new file mode 100644
index 0000000000..be9c1a810a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.inside2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.inside2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside3.html
new file mode 100644
index 0000000000..5e9bd4c721
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.inside3.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.inside3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.inside3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.993, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.negative.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.negative.html
new file mode 100644
index 0000000000..d86812b68c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.negative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.negative</h1>
+<p class="desc">createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, -0.1, 0, 0, 1); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, 1, 0, 0, -0.1); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, -0.1, 0, 0, -0.1); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html
new file mode 100644
index 0000000000..0c16b41540
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.nonfinite</h1>
+<p class="desc">createRadialGradient() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createRadialGradient() throws TypeError if arguments are not finite");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(-Infinity, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(NaN, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, -Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, NaN, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, -Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, NaN, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, -Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, NaN, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, -Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, NaN, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, Infinity, Infinity); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside1.html
new file mode 100644
index 0000000000..ea9dfdb3bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.outside1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.outside1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 10, 200, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside2.html
new file mode 100644
index 0000000000..b07fd02518
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.outside2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.outside2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside3.html
new file mode 100644
index 0000000000..7c19baf5c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.outside3.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.outside3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.outside3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.001, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch1.html
new file mode 100644
index 0000000000..6487fc440b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.touch1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.touch1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(150, 25, 50, 200, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch2.html
new file mode 100644
index 0000000000..908a0dbc19
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch2.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.touch2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.touch2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.01, '#0f0');
+ g.addColorStop(0.99, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch3.html
new file mode 100644
index 0000000000..07f52d7363
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.touch3.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.touch3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.touch3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, -15, 25, 140, -30, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.1.html
new file mode 100644
index 0000000000..d0eb13c457
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.transform.1</h1>
+<p class="desc">Radial gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Radial gradient coordinates are relative to the coordinate space at the time of filling");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.2.html
new file mode 100644
index 0000000000..6e56c4c63b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.transform.2</h1>
+<p class="desc">Radial gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Radial gradient coordinates are relative to the coordinate space at the time of filling");
+_addTest(function(canvas, ctx) {
+
+ ctx.translate(100, 0);
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.3.html
new file mode 100644
index 0000000000..426021d097
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.radial.transform.3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.gradient.radial.transform.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.gradient.radial.transform.3</h1>
+<p class="desc">Radial gradient transforms do not experience broken caching effects</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Radial gradient transforms do not experience broken caching effects");
+_addTest(function(canvas, ctx) {
+
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.animated.gif.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.animated.gif.html
new file mode 100644
index 0000000000..be69cca997
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.animated.gif.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.animated.gif</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.animated.gif</h1>
+<p class="desc">createPattern() of an animated GIF draws the first frame</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("createPattern() of an animated GIF draws the first frame");
+_addTest(function(canvas, ctx) {
+
+ deferTest();
+ step_timeout(function () {
+ var pattern = ctx.createPattern(document.getElementById('anim-gr.gif'), 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 50, 50);
+ step_timeout(t.step_func_done(function () {
+ ctx.fillRect(50, 0, 50, 50);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ }), 250);
+ }, 250);
+
+});
+</script>
+<img src="/images/anim-gr.gif" id="anim-gr.gif" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.canvas.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.canvas.html
new file mode 100644
index 0000000000..069263adff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.canvas.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.basic.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.basic.canvas</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.image.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.image.html
new file mode 100644
index 0000000000..5ebffe50f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.image.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.basic.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.basic.image</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.nocontext.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.nocontext.html
new file mode 100644
index 0000000000..f24899c302
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.nocontext.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.basic.nocontext</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.basic.nocontext</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.type.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.type.html
new file mode 100644
index 0000000000..c510063d80
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.type.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.basic.type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.basic.type</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.CanvasPattern, undefined, "window.CanvasPattern", "undefined");
+
+ window.CanvasPattern.prototype.thisImplementsCanvasPattern = true;
+
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ _assert(pattern.thisImplementsCanvasPattern, "pattern.thisImplementsCanvasPattern");
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html
new file mode 100644
index 0000000000..fbeb7bd797
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.basic.zerocanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.basic.zerocanvas</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 0;
+ canvas.height = 10;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 10, "canvas.height", "10");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+
+ canvas.width = 10;
+ canvas.height = 0;
+ _assertSame(canvas.width, 10, "canvas.width", "10");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+
+ canvas.width = 0;
+ canvas.height = 0;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.crosscanvas.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.crosscanvas.html
new file mode 100644
index 0000000000..f93b36ddf2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.crosscanvas.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.crosscanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.crosscanvas</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('green.png');
+
+ var pattern = document.createElement('canvas').getContext('2d').createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.broken.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.broken.html
new file mode 100644
index 0000000000..2cf21746ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.broken.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.broken</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.broken</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('broken.png');
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(img, 'repeat'); });
+
+});
+</script>
+<img src="/images/broken.png" id="broken.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.emptysrc.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.emptysrc.html
new file mode 100644
index 0000000000..fb05cbb455
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.emptysrc.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.incomplete.emptysrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.incomplete.emptysrc</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('red.png');
+ img.src = "";
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.immediate.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.immediate.html
new file mode 100644
index 0000000000..375c1b49c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.immediate.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.incomplete.immediate</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.incomplete.immediate</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = new Image();
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.nosrc.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.nosrc.html
new file mode 100644
index 0000000000..d43a0ca918
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.nosrc.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.incomplete.nosrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.incomplete.nosrc</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = new Image();
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.reload.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.reload.html
new file mode 100644
index 0000000000..984bd36a74
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.reload.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.incomplete.reload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.incomplete.reload</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('yellow.png');
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm,
+ // and resets the image to the "unavailable" state.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<img src="/images/yellow.png" id="yellow.png" class="resource">
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.removedsrc.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.removedsrc.html
new file mode 100644
index 0000000000..20d5543684
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.incomplete.removedsrc.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.incomplete.removedsrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.incomplete.removedsrc</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('red.png');
+ img.removeAttribute('src');
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent-but-loading.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent-but-loading.html
new file mode 100644
index 0000000000..2af0a3ed09
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent-but-loading.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.nonexistent-but-loading</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.nonexistent-but-loading</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.createElement("img");
+ img.src = "/images/no-such-image-really.png";
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+ var img = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ img.src = "/images/no-such-image-really.png";
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent.html
new file mode 100644
index 0000000000..b365281179
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nonexistent.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.nonexistent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.nonexistent</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('no-such-image-really.png');
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(img, 'repeat'); });
+
+});
+</script>
+<img src="/images/no-such-image-really.png" id="no-such-image-really.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nosrc.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nosrc.html
new file mode 100644
index 0000000000..710749dd54
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.nosrc.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.nosrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.nosrc</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.createElement("img");
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+ var img = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.null.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.null.html
new file mode 100644
index 0000000000..8ae002d682
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.null.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.null</h1>
+<p class="desc"></p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createPattern(null, 'repeat'); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.string.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.string.html
new file mode 100644
index 0000000000..af32e63bba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.string.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.string</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.string</h1>
+<p class="desc"></p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createPattern('../images/red.png', 'repeat'); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.undefined.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.undefined.html
new file mode 100644
index 0000000000..75f50c7a4a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.undefined.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.undefined</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.undefined</h1>
+<p class="desc"></p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createPattern(undefined, 'repeat'); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zeroheight.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zeroheight.html
new file mode 100644
index 0000000000..cd12c2e883
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zeroheight.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.zeroheight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.zeroheight</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('red-zeroheight.svg');
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<img src="/images/red-zeroheight.svg" id="red-zeroheight.svg" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zerowidth.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zerowidth.html
new file mode 100644
index 0000000000..053b8ac648
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.image.zerowidth.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.image.zerowidth</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.image.zerowidth</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('red-zerowidth.svg');
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<img src="/images/red-zerowidth.svg" id="red-zerowidth.svg" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas1.html
new file mode 100644
index 0000000000..0524351fdb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.modify.canvas1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.modify.canvas1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas2.html
new file mode 100644
index 0000000000..6e50a5761c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.canvas2.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.modify.canvas2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.modify.canvas2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image1.html
new file mode 100644
index 0000000000..94f55c84f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image1.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.modify.image1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.modify.image1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ deferTest();
+ img.onload = t.step_func_done(function ()
+ {
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ });
+ img.src = '/images/red.png';
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image2.html
new file mode 100644
index 0000000000..1025d195f2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.modify.image2.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.modify.image2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.modify.image2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 100, 50);
+ deferTest();
+ img.onload = t.step_func_done(function ()
+ {
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ });
+ img.src = '/images/red.png';
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html
new file mode 100644
index 0000000000..036be9e068
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.norepeat.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.norepeat.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html
new file mode 100644
index 0000000000..dfe2b8589e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.norepeat.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.norepeat.coord1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html
new file mode 100644
index 0000000000..dfd637211d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.norepeat.coord2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.norepeat.coord2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green.png" id="green.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html
new file mode 100644
index 0000000000..5456a543da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.norepeat.coord3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.norepeat.coord3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('red.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html
new file mode 100644
index 0000000000..4df0cdfe67
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.norepeat.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.norepeat.outside</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('red.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+ ctx.fillRect(-100, 0, 100, 50);
+ ctx.fillRect(0, 50, 100, 50);
+ ctx.fillRect(100, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html
new file mode 100644
index 0000000000..f0ad70fc1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.orientation.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.orientation.canvas</h1>
+<p class="desc">Canvas patterns do not get flipped when painted</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Canvas patterns do not get flipped when painted");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 25);
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 25, 100, 25);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html
new file mode 100644
index 0000000000..b45f4ab773
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.orientation.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.orientation.image</h1>
+<p class="desc">Image patterns do not get flipped when painted</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Image patterns do not get flipped when painted");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('rrgg-256x256.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.save();
+ ctx.translate(0, -103);
+ ctx.fillRect(0, 103, 100, 50);
+ ctx.restore();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/rrgg-256x256.png" id="rrgg-256x256.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html
new file mode 100644
index 0000000000..8999ac4e44
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeat.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeat.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('green-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green-16x16.png" id="green-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html
new file mode 100644
index 0000000000..8dc6402d55
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeat.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeat.coord1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('rgrg-256x256.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/rgrg-256x256.png" id="rgrg-256x256.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html
new file mode 100644
index 0000000000..9c03d02898
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeat.coord2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeat.coord2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('ggrr-256x256.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/ggrr-256x256.png" id="ggrr-256x256.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html
new file mode 100644
index 0000000000..0dc0a865b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeat.coord3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeat.coord3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('rgrg-256x256.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/rgrg-256x256.png" id="rgrg-256x256.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html
new file mode 100644
index 0000000000..b939195877
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeat.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeat.outside</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('green-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green-16x16.png" id="green-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html
new file mode 100644
index 0000000000..8c77908cd1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeatx.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeatx.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 16);
+
+ var img = document.getElementById('green-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green-16x16.png" id="green-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html
new file mode 100644
index 0000000000..4c7fa9d498
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeatx.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeatx.coord1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('red-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.translate(0, 16);
+ ctx.fillRect(0, -16, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red-16x16.png" id="red-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html
new file mode 100644
index 0000000000..26ee85e4d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeatx.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeatx.outside</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('red-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red-16x16.png" id="red-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html
new file mode 100644
index 0000000000..c28ff155eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeaty.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeaty.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 50);
+
+ var img = document.getElementById('green-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green-16x16.png" id="green-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html
new file mode 100644
index 0000000000..7f2bb163f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeaty.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeaty.coord1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('red-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.translate(48, 0);
+ ctx.fillRect(-48, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red-16x16.png" id="red-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html
new file mode 100644
index 0000000000..7be743ec2b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.paint.repeaty.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.paint.repeaty.outside</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var img = document.getElementById('red-16x16.png');
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red-16x16.png" id="red-16x16.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.case.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.case.html
new file mode 100644
index 0000000000..d998bbeb2f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.case.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.repeat.case</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.repeat.case</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "Repeat"); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.empty.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.empty.html
new file mode 100644
index 0000000000..2810efe700
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.empty.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.repeat.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.repeat.empty</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('green-1x1.png');
+ var pattern = ctx.createPattern(img, "");
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 200, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+<img src="/images/green-1x1.png" id="green-1x1.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.null.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.null.html
new file mode 100644
index 0000000000..0de121a4f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.null.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.repeat.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.repeat.null</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assert(ctx.createPattern(canvas, null) != null, "ctx.createPattern(canvas, null) != null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html
new file mode 100644
index 0000000000..33d601d974
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.repeat.nullsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.repeat.nullsuffix</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "repeat\0"); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.undefined.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.undefined.html
new file mode 100644
index 0000000000..f0a7ae8488
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.undefined.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.repeat.undefined</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.repeat.undefined</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, undefined); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html
new file mode 100644
index 0000000000..cdcc7d6523
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.repeat.unrecognised</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.repeat.unrecognised</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "invalid"); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html
new file mode 100644
index 0000000000..11c9ec0ebb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.repeat.unrecognisednull</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.repeat.unrecognisednull</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "null"); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.nonexistent.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.nonexistent.html
new file mode 100644
index 0000000000..8cd3983dc3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.nonexistent.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.svgimage.nonexistent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.svgimage.nonexistent</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('no-such-image-really.png');
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(img, 'repeat'); });
+
+});
+</script>
+<svg><image xlink:href="/images/no-such-image-really.png" id="no-such-image-really.png" class="resource"></svg>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zeroheight.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zeroheight.html
new file mode 100644
index 0000000000..11c07e36de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zeroheight.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.svgimage.zeroheight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.svgimage.zeroheight</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('red-zeroheight.svg');
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<svg><image xlink:href="/images/red-zeroheight.svg" id="red-zeroheight.svg" class="resource"></svg>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zerowidth.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zerowidth.html
new file mode 100644
index 0000000000..a8b8cb1351
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.svgimage.zerowidth.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.svgimage.zerowidth</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.svgimage.zerowidth</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('red-zerowidth.svg');
+ _assertSame(ctx.createPattern(img, 'repeat'), null, "ctx.createPattern(img, 'repeat')", "null");
+
+});
+</script>
+<svg><image xlink:href="/images/red-zerowidth.svg" id="red-zerowidth.svg" class="resource"></svg>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.identity.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.identity.html
new file mode 100644
index 0000000000..21da30581d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.identity.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.transform.identity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.transform.identity</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform(new DOMMatrix());
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.infinity.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.infinity.html
new file mode 100644
index 0000000000..bfb22aeb14
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.infinity.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.transform.infinity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.transform.infinity</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform({a: Infinity});
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.invalid.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.invalid.html
new file mode 100644
index 0000000000..13f0459e9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.pattern.transform.invalid.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.pattern.transform.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.pattern.transform.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ assert_throws_js(TypeError, function() { pattern.setTransform({a: 1, m11: 2}); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.html
new file mode 100644
index 0000000000..308e6b54b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeStyle.colorObject</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeStyle.colorObject</h1>
+<p class="desc">ctx.strokeStyle works with color objects</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("ctx.strokeStyle works with color objects");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = {r: 1, g: 0, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 255,0,0,255);
+ ctx.strokeStyle = {r: 0, g: 0, b: 1};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,255,255);
+ ctx.strokeStyle = {r: 0.2, g: 0.4, b: 0.6};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 51,102,153,255);
+ ctx.strokeStyle = {r: 0, g: 1, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ ctx.strokeStyle = {r: -1, g: 0, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+ ctx.strokeStyle = {r: 0, g: 2, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.transparency.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.transparency.html
new file mode 100644
index 0000000000..0232140eaf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colorObject.transparency.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeStyle.colorObject.transparency</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeStyle.colorObject.transparency</h1>
+<p class="desc">ctx.strokeStyle with color objects has transparency</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("ctx.strokeStyle with color objects has transparency");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: -1};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: 0.5};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,255,0,128);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: 1};
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colormix.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colormix.html
new file mode 100644
index 0000000000..c1971666e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.colormix.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeStyle.colormix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeStyle.colormix</h1>
+<p class="desc">color-mix works as color input</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("color-mix works as color input");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = "color-mix(in srgb, red, blue)";
+ _assertSame(ctx.strokeStyle, 'color(srgb 0.5 0 0.5)', "ctx.strokeStyle", "'color(srgb 0.5 0 0.5)'");
+ ctx.strokeStyle = "color-mix(in srgb, red, color(srgb 1 0 0))";
+ _assertSame(ctx.strokeStyle, 'color(srgb 1 0 0)', "ctx.strokeStyle", "'color(srgb 1 0 0)'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.default.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.default.html
new file mode 100644
index 0000000000..a432d74a42
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.strokeStyle.default.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.strokeStyle.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.strokeStyle.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.strokeStyle, '#000000', "ctx.strokeStyle", "'#000000'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html
new file mode 100644
index 0000000000..42fb1ee8f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.blur.exceptions.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.blur.exceptions.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() blur.object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test exceptions on CanvasFilter() blur.object");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur'}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: undefined}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 'foo'}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1,2,3]}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: NaN}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: {}}); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html
new file mode 100644
index 0000000000..b2f6a6ac97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.colorMatrix.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.colorMatrix.tentative</h1>
+<p class="desc">Test the functionality of ColorMatrix filters in CanvasFilter objects</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test the functionality of ColorMatrix filters in CanvasFilter objects");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: 'foo'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 'a']}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, Infinity]}); });
+ ctx.fillStyle = '#f00';
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 0});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 255,0,0,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 90});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,91,0,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 180});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,109,109,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 270});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 109,18,255,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'saturate', values: 0.5});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 155,27,27,255, 2);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'luminanceToAlpha'});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,0,0,54, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', values: [
+ 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0
+ ]});
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 25);
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 25, 50, 25);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(50, 25, 50, 25);
+ _assertPixelApprox(canvas, 10,10, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 60,10, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 10,30, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 60,30, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html
new file mode 100644
index 0000000000..b392b189f2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.componentTransfer.discrete.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.discrete.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with discrete type</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with discrete type");
+_addTest(function(canvas, ctx) {
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k];
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'discrete', tableValues: tableValuesR},
+ funcG: {type: 'discrete', tableValues: tableValuesG},
+ funcB: {type: 'discrete', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html
new file mode 100644
index 0000000000..e5bff7e44d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.componentTransfer.gamma.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.gamma.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with gamma type</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with gamma type");
+_addTest(function(canvas, ctx) {
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, amplitude, exponent, offset) {
+ return [
+ Math.max(0, Math.min(1, Math.pow(inputColor[0]/255, exponent[0]) * amplitude[0] + offset[0])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[1]/255, exponent[1]) * amplitude[1] + offset[1])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[2]/255, exponent[2]) * amplitude[2] + offset[2])) * 255,
+ ];
+ }
+
+ const amplitudes = [2, 1.1, 0.5];
+ const exponents = [5, 3, 1];
+ const offsets = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'gamma', amplitude: amplitudes[0], exponent: exponents[0], offset: offsets[0]},
+ funcG: {type: 'gamma', amplitude: amplitudes[1], exponent: exponents[1], offset: offsets[1]},
+ funcB: {type: 'gamma', amplitude: amplitudes[2], exponent: exponents[2], offset: offsets[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, amplitudes, exponents, offsets);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html
new file mode 100644
index 0000000000..ecd3830be3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.componentTransfer.identity.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.identity.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with identity type</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with identity type");
+_addTest(function(canvas, ctx) {
+
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'identity'},
+ funcG: {type: 'identity'},
+ funcB: {type: 'identity'},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`,
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixel(canvas, 5, 5, color[0],color[1],color[2],255);
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html
new file mode 100644
index 0000000000..8708887f6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.componentTransfer.linear.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.linear.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with linear type</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with linear type");
+_addTest(function(canvas, ctx) {
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, slopes, intercepts) {
+ return [
+ Math.max(0, Math.min(1, inputColor[0]/255 * slopes[0] + intercepts[0])) * 255,
+ Math.max(0, Math.min(1, inputColor[1]/255 * slopes[1] + intercepts[1])) * 255,
+ Math.max(0, Math.min(1, inputColor[2]/255 * slopes[2] + intercepts[2])) * 255,
+ ];
+ }
+
+ const slopes = [0.5, 1.2, -0.2];
+ const intercepts = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'linear', slope: slopes[0], intercept: intercepts[0]},
+ funcG: {type: 'linear', slope: slopes[1], intercept: intercepts[1]},
+ funcB: {type: 'linear', slope: slopes[2], intercept: intercepts[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, slopes, intercepts);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html
new file mode 100644
index 0000000000..4b296d9fd7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.componentTransfer.table.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.table.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with table type</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with table type");
+_addTest(function(canvas, ctx) {
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length - 1;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k] + (C - k/n) * n * (V[k + 1] - V[k]);
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'table', tableValues: tableValuesR},
+ funcG: {type: 'table', tableValues: tableValuesG},
+ funcB: {type: 'table', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html
new file mode 100644
index 0000000000..b80600c141
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() convolveMatrix</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test exceptions on CanvasFilter() convolveMatrix");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', divisor: 2}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: 1}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 'a'], [0]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], 0]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0, Infinity]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: []}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1, 2, 3, 4]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], []]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 2], []]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], [1, 2]]}); });
+ // This should not throw an error
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[]]});
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1]]});
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
new file mode 100644
index 0000000000..8c07a72b2b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.dropShadow.exceptions.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.dropShadow.exceptions.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() dropShadow object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test exceptions on CanvasFilter() dropShadow object");
+_addTest(function(canvas, ctx) {
+
+ // dx
+ _assert(new CanvasFilter({name: 'dropShadow', dx: 10}), "new CanvasFilter({name: 'dropShadow', dx: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: -1}), "new CanvasFilter({name: 'dropShadow', dx: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: 0.5}), "new CanvasFilter({name: 'dropShadow', dx: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: null}), "new CanvasFilter({name: 'dropShadow', dx: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: true}), "new CanvasFilter({name: 'dropShadow', dx: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: false}), "new CanvasFilter({name: 'dropShadow', dx: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: []}), "new CanvasFilter({name: 'dropShadow', dx: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: [20]}), "new CanvasFilter({name: 'dropShadow', dx: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: '30'}), "new CanvasFilter({name: 'dropShadow', dx: '30'})");
+ // dy
+ _assert(new CanvasFilter({name: 'dropShadow', dy: 10}), "new CanvasFilter({name: 'dropShadow', dy: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: -1}), "new CanvasFilter({name: 'dropShadow', dy: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: 0.5}), "new CanvasFilter({name: 'dropShadow', dy: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: null}), "new CanvasFilter({name: 'dropShadow', dy: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: true}), "new CanvasFilter({name: 'dropShadow', dy: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: false}), "new CanvasFilter({name: 'dropShadow', dy: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: []}), "new CanvasFilter({name: 'dropShadow', dy: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: [20]}), "new CanvasFilter({name: 'dropShadow', dy: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: '30'}), "new CanvasFilter({name: 'dropShadow', dy: '30'})");
+ // floodOpacity
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: 10}), "new CanvasFilter({name: 'dropShadow', floodOpacity: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: -1}), "new CanvasFilter({name: 'dropShadow', floodOpacity: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: 0.5}), "new CanvasFilter({name: 'dropShadow', floodOpacity: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: null}), "new CanvasFilter({name: 'dropShadow', floodOpacity: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: true}), "new CanvasFilter({name: 'dropShadow', floodOpacity: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: false}), "new CanvasFilter({name: 'dropShadow', floodOpacity: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: []}), "new CanvasFilter({name: 'dropShadow', floodOpacity: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: [20]}), "new CanvasFilter({name: 'dropShadow', floodOpacity: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: '30'}), "new CanvasFilter({name: 'dropShadow', floodOpacity: '30'})");
+ // stdDeviation
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: 10}), "new CanvasFilter({name: 'dropShadow', stdDeviation: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: -1}), "new CanvasFilter({name: 'dropShadow', stdDeviation: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: 0.5}), "new CanvasFilter({name: 'dropShadow', stdDeviation: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: null}), "new CanvasFilter({name: 'dropShadow', stdDeviation: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: true}), "new CanvasFilter({name: 'dropShadow', stdDeviation: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: false}), "new CanvasFilter({name: 'dropShadow', stdDeviation: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: []}), "new CanvasFilter({name: 'dropShadow', stdDeviation: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [20]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: '30'}), "new CanvasFilter({name: 'dropShadow', stdDeviation: '30'})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [10, -1]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [10, -1]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [0.5, null]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [0.5, null]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [true, false]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [true, false]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [[], [20]]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [[], [\""+(20)+"\"]]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: ['30', ['40']]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: ['30', ['40']]})");
+ // floodColor
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'red'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'red'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'canvas'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'canvas'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(4, -3, 0.5, 1)'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(4, -3, 0.5, 1)'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: '#aabbccdd'}), "new CanvasFilter({name: 'dropShadow', floodColor: '#aabbccdd'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: '#abcd'}), "new CanvasFilter({name: 'dropShadow', floodColor: '#abcd'})");
+
+ // dx
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: [1, 2]}); });
+ // dy
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: [1, 2]}); });
+ // floodOpacity
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: [1, 2]}); });
+ // stdDeviation
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, 2, 3]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, NaN]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, Infinity]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, -Infinity]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, undefined]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, 'test']}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, {}]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, [2, 3]]}); });
+ // floodColor
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(NaN, 3, 2, 1)'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 10}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: NaN}); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html
new file mode 100644
index 0000000000..86c5710132
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.dropShadow.tentative</title>
+<h1>2d.filter.canvasFilterObject.dropShadow.tentative</h1>
+<p class="desc">Test CanvasFilter() dropShadow object.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width=520 height=420
+ color-interpolation-filters="sRGB">
+ <rect x=0 y=0 width=100% height=50 fill="teal" />
+ <rect x=0 y=100 width=100% height=50 fill="teal" />
+ <rect x=0 y=200 width=100% height=50 fill="teal" />
+ <rect x=0 y=300 width=100% height=50 fill="teal" />
+
+ <rect x=10 y=10 width=80 height=80 fill="crimson"
+ filter="drop-shadow(2px 2px 2px black)"/>
+ <rect x=110 y=10 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 5px rgba(128, 0, 128, 0.7))"/>
+
+ <rect x=10 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px purple)"/>
+ <rect x=110 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px LinkText)"/>
+ <rect x=210 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 1))"/>
+ <rect x=310 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 0.7))"/>
+ <rect x=410 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 0.49))"/>
+
+ <rect x=10 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+ <rect x=110 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 5px purple)"/>
+ <rect x=210 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+ <filter id="separable-filter"
+ x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx=9 dy=12 stdDeviation="3 5" flood-color="purple"/>
+ </filter>
+ <rect x=310 y=210 width=80 height=80 fill="crimson"
+ filter="url(#separable-filter)"/>
+ <rect x=410 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+
+ <rect x=10 y=310 width=80 height=80 fill="crimson"
+ filter="drop-shadow(-5px 0px 0px purple)"/>
+ <filter id="separable-filter-degenerate"
+ x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx=0 dy=5 stdDeviation="0 3"
+ flood-color="rgba(128, 0, 128, 0.8)"/>
+ </filter>
+ <rect x=110 y=310 width=80 height=80 fill="crimson"
+ filter="url(#separable-filter-degenerate)"/>
+ <rect x=210 y=310 width=80 height=80 fill="crimson"
+ filter="drop-shadow(1px 10px 0px rgba(128, 0, 128, 0.4))"/>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html
new file mode 100644
index 0000000000..47bb891b3f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.dropShadow.tentative-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.dropShadow.tentative</title>
+<h1>2d.filter.canvasFilterObject.dropShadow.tentative</h1>
+<p class="desc">Test CanvasFilter() dropShadow object.</p>
+<canvas id="canvas" width="520" height="420">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(0, 0, 520, 50);
+ ctx.fillRect(0, 100, 520, 50);
+ ctx.fillRect(0, 200, 520, 50);
+ ctx.fillRect(0, 300, 520, 50);
+
+ ctx.fillStyle = 'crimson';
+
+ // Parameter defaults.
+ ctx.filter = new CanvasFilter({name: 'dropShadow'});
+ ctx.fillRect(10, 10, 80, 80);
+
+ // All parameters specified.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple', floodOpacity: 0.7});
+ ctx.fillRect(110, 10, 80, 80);
+
+ // Named color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 110, 80, 80);
+
+ // System color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'LinkText'});
+ ctx.fillRect(110, 110, 80, 80);
+
+ // Numerical color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 1)'});
+ ctx.fillRect(210, 110, 80, 80);
+
+ // Transparent floodColor.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)'});
+ ctx.fillRect(310, 110, 80, 80);
+
+ // Transparent floodColor and floodOpacity.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)', floodOpacity: 0.7});
+ ctx.fillRect(410, 110, 80, 80);
+
+ // No blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 0,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 210, 80, 80);
+
+ // Single float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple'});
+ ctx.fillRect(110, 210, 80, 80);
+
+ // Single negative float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: -5,
+ floodColor: 'purple'});
+ ctx.fillRect(210, 210, 80, 80);
+
+ // Two floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [3, 5],
+ floodColor: 'purple'});
+ ctx.fillRect(310, 210, 80, 80);
+
+ // Two negative floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [-3, -5],
+ floodColor: 'purple'});
+ ctx.fillRect(410, 210, 80, 80);
+
+ // Degenerate parameter values.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: [-5], dy: [], stdDeviation: null,
+ floodColor: 'purple', floodOpacity: [2]});
+ ctx.fillRect(10, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: null, dy: '5', stdDeviation: [[-5], ['3']],
+ floodColor: 'purple', floodOpacity: '0.8'});
+ ctx.fillRect(110, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: true, dy: ['10'], stdDeviation: false,
+ floodColor: 'purple', floodOpacity: ['0.4']});
+ ctx.fillRect(210, 310, 80, 80);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html
new file mode 100644
index 0000000000..f9571f208e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="4 4" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html
new file mode 100644
index 0000000000..6376d07b0e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html
new file mode 100644
index 0000000000..e76613271f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="4 1" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html
new file mode 100644
index 0000000000..145cb32757
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 1],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html
new file mode 100644
index 0000000000..0f214fca9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="1 4" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html
new file mode 100644
index 0000000000..e921e0eaa6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [1, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html
new file mode 100644
index 0000000000..285a641726
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="4 0" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html
new file mode 100644
index 0000000000..fd6ac687f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 0],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html
new file mode 100644
index 0000000000..d59945b5da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="0 4" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html
new file mode 100644
index 0000000000..6442433e7c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [0, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.tentative.html
new file mode 100644
index 0000000000..c40e718d72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.tentative</h1>
+<p class="desc">Test CanvasFilter() object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test CanvasFilter() object");
+_addTest(function(canvas, ctx) {
+
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1, 2]});
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = new CanvasFilter([
+ {name: 'gaussianBlur', stdDeviation: 5},
+ {name: 'gaussianBlur', stdDeviation: 10}
+ ]);
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.filter = ctx.filter;
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'none';
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ ctx.filter = 'this string is not a filter and should do nothing';
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html
new file mode 100644
index 0000000000..c24cfd2398
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.turbulence.inputTypes.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.canvasFilterObject.turbulence.inputTypes.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() turbulence object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test exceptions on CanvasFilter() turbulence object");
+_addTest(function(canvas, ctx) {
+
+ const errorTestCases = [
+ {baseFrequency: {}},
+ {baseFrequency: -1},
+ {baseFrequency: [0, -1]},
+ {baseFrequency: NaN},
+ {baseFrequency: Infinity},
+ {baseFrequency: undefined},
+ {baseFrequency: -Infinity},
+ {baseFrequency: 'test'},
+
+ {numOctaves: {}},
+ {numOctaves: -1},
+ {numOctaves: NaN},
+ {numOctaves: Infinity},
+ {numOctaves: undefined},
+ {numOctaves: -Infinity},
+ {numOctaves: [1, 1]},
+ {numOctaves: 'test'},
+
+ {seed: {}},
+ {seed: NaN},
+ {seed: Infinity},
+ {seed: undefined},
+ {seed: -Infinity},
+ {seed: [1, 1]},
+ {seed: 'test'},
+
+ {stitchTiles: {}},
+ {stitchTiles: NaN},
+ {stitchTiles: Infinity},
+ {stitchTiles: undefined},
+ {stitchTiles: -Infinity},
+ {stitchTiles: [1, 1]},
+ {stitchTiles: 'test'},
+ {stitchTiles: null},
+ {stitchTiles: []},
+ {stitchTiles: [10]},
+ {stitchTiles: 30},
+ {stitchTiles: false},
+ {stitchTiles: true},
+ {stitchTiles: '10'},
+ {stitchTiles: -1},
+
+ {type: {}},
+ {type: NaN},
+ {type: Infinity},
+ {type: undefined},
+ {type: -Infinity},
+ {type: [1, 1]},
+ {type: 'test'},
+ {type: null},
+ {type: []},
+ {type: [10]},
+ {type: 30},
+ {type: false},
+ {type: true},
+ {type: '10'},
+ {type: -1},
+ ]
+
+ // null and [] = 0 when parsed as number
+ const workingTestCases = [
+ {baseFrequency: null},
+ {baseFrequency: []},
+ {baseFrequency: [10]},
+ {baseFrequency: [10, 3]},
+ {baseFrequency: 30},
+ {baseFrequency: false},
+ {baseFrequency: true},
+ {baseFrequency: '10'},
+
+ {numOctaves: null},
+ {numOctaves: []},
+ {numOctaves: [10]},
+ {numOctaves: 30},
+ {numOctaves: false},
+ {numOctaves: true},
+ {numOctaves: '10'},
+
+ {seed: null},
+ {seed: []},
+ {seed: [10]},
+ {seed: 30},
+ {seed: false},
+ {seed: true},
+ {seed: '10'},
+ {seed: -1},
+
+ {stitchTiles: 'stitch'},
+ {stitchTiles: 'noStitch'},
+
+ {type: 'fractalNoise'},
+ {type: 'turbulence'},
+ ]
+
+ for (testCase of errorTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ assert_throws_js(TypeError, function() { new CanvasFilter(filterOptions); });
+ }
+
+ for (testCase of workingTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ _assert(new CanvasFilter(filterOptions) != null, "new CanvasFilter(filterOptions) != null");
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.value.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.value.html
new file mode 100644
index 0000000000..5716b9bd16
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.value.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.value</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.filter.value</h1>
+<p class="desc">test if ctx.filter works correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("test if ctx.filter works correctly");
+_addTest(function(canvas, ctx) {
+
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.save();
+ ctx.filter = 'none';
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.restore();
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'blur(10)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'blur 10px';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'inherit';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'initial';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'unset';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = '';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = null;
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = undefined;
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'blur( 5px)';
+ assert_equals(ctx.filter, 'blur( 5px)');
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic-expected.html
new file mode 100644
index 0000000000..6a6f0f6892
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.isotropic</title>
+<h1>2d.layer.anisotropic-blur.isotropic</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="4 4" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic.html
new file mode 100644
index 0000000000..4e496b7e48
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.isotropic-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.isotropic</title>
+<h1>2d.layer.anisotropic-blur.isotropic</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x-expected.html
new file mode 100644
index 0000000000..b0473f2ff0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-x</title>
+<h1>2d.layer.anisotropic-blur.mostly-x</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="4 1" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x.html
new file mode 100644
index 0000000000..4217d9fad3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.mostly-x-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-x</title>
+<h1>2d.layer.anisotropic-blur.mostly-x</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 1] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y-expected.html
new file mode 100644
index 0000000000..43da016eea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-y</title>
+<h1>2d.layer.anisotropic-blur.mostly-y</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="1 4" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y.html
new file mode 100644
index 0000000000..97a44e9045
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.mostly-y-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-y</title>
+<h1>2d.layer.anisotropic-blur.mostly-y</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [1, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only-expected.html
new file mode 100644
index 0000000000..0b4d269189
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.x-only</title>
+<h1>2d.layer.anisotropic-blur.x-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="4 0" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only.html
new file mode 100644
index 0000000000..fdf604616b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.x-only-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.x-only</title>
+<h1>2d.layer.anisotropic-blur.x-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 0] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only-expected.html
new file mode 100644
index 0000000000..2572386412
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.y-only</title>
+<h1>2d.layer.anisotropic-blur.y-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="0 4" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only.html
new file mode 100644
index 0000000000..c7ceb63a87
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.y-only-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.y-only</title>
+<h1>2d.layer.anisotropic-blur.y-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [0, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.beginLayer-options.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.beginLayer-options.html
new file mode 100644
index 0000000000..658d7e0991
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.beginLayer-options.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.beginLayer-options</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.beginLayer-options</h1>
+<p class="desc">Checks beginLayer works for different option parameter values</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Checks beginLayer works for different option parameter values");
+_addTest(function(canvas, ctx) {
+
+ ctx.beginLayer(); ctx.endLayer();
+ ctx.beginLayer(null); ctx.endLayer();
+ ctx.beginLayer(undefined); ctx.endLayer();
+ ctx.beginLayer([]); ctx.endLayer();
+ ctx.beginLayer({}); ctx.endLayer();
+
+ assert_throws_js(TypeError, function() { ctx.beginLayer(''); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(0); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(1); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(true); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(false); });
+
+ ctx.beginLayer({filter: null}); ctx.endLayer();
+ ctx.beginLayer({filter: undefined}); ctx.endLayer();
+ ctx.beginLayer({filter: []}); ctx.endLayer();
+ ctx.beginLayer({filter: {}}); ctx.endLayer();
+ ctx.beginLayer({filter: {name: "unknown"}}); ctx.endLayer();
+ ctx.beginLayer({filter: ''}); ctx.endLayer();
+
+ // These cases don't throw TypeError since they can be casted to a
+ // DOMString.
+ ctx.beginLayer({filter: 0}); ctx.endLayer();
+ ctx.beginLayer({filter: 1}); ctx.endLayer();
+ ctx.beginLayer({filter: true}); ctx.endLayer();
+ ctx.beginLayer({filter: false}); ctx.endLayer();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html
new file mode 100644
index 0000000000..6cad180f14
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.no-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.no-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="30" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="201" y="50" width="100" height="100" fill="turquoise"/>
+ <rect x="50" y="201" width="100" height="100" fill="indigo"/>
+ <rect x="-101" y="50" width="100" height="100" fill="orange"/>
+ <rect x="50" y="-101" width="100" height="100" fill="brown"/>
+ </g>
+ </svg>`;
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ // No clipping.
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping.html
new file mode 100644
index 0000000000..b689ad39fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.no-clipping.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.blur-from-outside-canvas.no-clipping-expected.html">
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.no-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.no-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [ {name: 'gaussianBlur', stdDeviation: 30} ]});
+
+ ctx.fillStyle = 'turquoise';
+ ctx.fillRect(201, 50, 100, 100);
+ ctx.fillStyle = 'indigo';
+ ctx.fillRect(50, 201, 100, 100);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(-1, 50, -100, 100);
+ ctx.fillStyle = 'brown';
+ ctx.fillRect(50, -1, 100, -100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html
new file mode 100644
index 0000000000..1823f78983
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.with-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.with-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="30" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="201" y="50" width="100" height="100" fill="turquoise"/>
+ <rect x="50" y="201" width="100" height="100" fill="indigo"/>
+ <rect x="-101" y="50" width="100" height="100" fill="orange"/>
+ <rect x="50" y="-101" width="100" height="100" fill="brown"/>
+ </g>
+ </svg>`;
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping.html
new file mode 100644
index 0000000000..9e8161019a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.blur-from-outside-canvas.with-clipping.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.blur-from-outside-canvas.with-clipping-expected.html">
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.with-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.with-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [ {name: 'gaussianBlur', stdDeviation: 30} ]});
+
+ ctx.fillStyle = 'turquoise';
+ ctx.fillRect(201, 50, 100, 100);
+ ctx.fillStyle = 'indigo';
+ ctx.fillRect(50, 201, 100, 100);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(-1, 50, -100, 100);
+ ctx.fillStyle = 'brown';
+ ctx.fillRect(50, -1, 100, -100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full-expected.html
new file mode 100644
index 0000000000..5a47f8e118
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clearRect.full</title>
+<h1>2d.layer.clearRect.full</h1>
+<p class="desc">clearRect inside a layer can clear all of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full.html
new file mode 100644
index 0000000000..579c6e1053
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.full.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clearRect.full-expected.html">
+<title>Canvas test: 2d.layer.clearRect.full</title>
+<h1>2d.layer.clearRect.full</h1>
+<p class="desc">clearRect inside a layer can clear all of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.fillStyle = 'green';
+ ctx.clearRect(0, 0, 100, 100);
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial-expected.html
new file mode 100644
index 0000000000..ac75105cec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial-expected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clearRect.partial</title>
+<h1>2d.layer.clearRect.partial</h1>
+<p class="desc">clearRect inside a layer can clear a portion of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 10);
+ ctx.fillRect(20, 60, 80, 10);
+ ctx.fillRect(20, 20, 10, 50);
+ ctx.fillRect(90, 20, 10, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial.html
new file mode 100644
index 0000000000..8323ffb931
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clearRect.partial.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clearRect.partial-expected.html">
+<title>Canvas test: 2d.layer.clearRect.partial</title>
+<h1>2d.layer.clearRect.partial</h1>
+<p class="desc">clearRect inside a layer can clear a portion of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.clearRect(30, 30, 60, 30);
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside-expected.html
new file mode 100644
index 0000000000..14e6d56245
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clip-inside-and-outside</title>
+<h1>2d.layer.clip-inside-and-outside</h1>
+<p class="desc">Check clipping set inside and outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginPath();
+ ctx2.rect(15, 15, 70, 70);
+ ctx2.clip();
+
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+
+ const canvas3 = new OffscreenCanvas(200, 200);
+ const ctx3 = canvas3.getContext('2d');
+
+ ctx3.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx3.drawImage(canvas2, 0, 0);
+ ctx3.endLayer();
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+ ctx.drawImage(canvas3, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside.html
new file mode 100644
index 0000000000..1a727bbd3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-and-outside.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clip-inside-and-outside-expected.html">
+<title>Canvas test: 2d.layer.clip-inside-and-outside</title>
+<h1>2d.layer.clip-inside-and-outside</h1>
+<p class="desc">Check clipping set inside and outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-expected.html
new file mode 100644
index 0000000000..281fc1d913
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside-expected.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clip-inside</title>
+<h1>2d.layer.clip-inside</h1>
+<p class="desc">Check clipping set inside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginPath();
+ ctx2.rect(15, 15, 70, 70);
+ ctx2.clip();
+
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx.drawImage(canvas2, 0, 0);
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside.html
new file mode 100644
index 0000000000..f3555c8321
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-inside.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clip-inside-expected.html">
+<title>Canvas test: 2d.layer.clip-inside</title>
+<h1>2d.layer.clip-inside</h1>
+<p class="desc">Check clipping set inside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside-expected.html
new file mode 100644
index 0000000000..1b18c2e8a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside-expected.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clip-outside</title>
+<h1>2d.layer.clip-outside</h1>
+<p class="desc">Check clipping set outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+ ctx2.endLayer();
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside.html
new file mode 100644
index 0000000000..bf8e84e3d2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.clip-outside.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clip-outside-expected.html">
+<title>Canvas test: 2d.layer.clip-outside</title>
+<h1>2d.layer.clip-outside</h1>
+<p class="desc">Check clipping set outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths-expected.html
new file mode 100644
index 0000000000..c394ecdfe3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.cross-layer-paths</title>
+<h1>2d.layer.cross-layer-paths</h1>
+<p class="desc">Checks that path defined in a layer is usable outside.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.moveTo(50, 0); ctx.lineTo(50, 100); ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths.html
new file mode 100644
index 0000000000..7feebfdce6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.cross-layer-paths.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.cross-layer-paths-expected.html">
+<title>Canvas test: 2d.layer.cross-layer-paths</title>
+<h1>2d.layer.cross-layer-paths</h1>
+<p class="desc">Checks that path defined in a layer is usable outside.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ ctx.translate(50, 0);
+ ctx.moveTo(0, 0);
+ ctx.endLayer();
+ ctx.lineTo(50, 100);
+ ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow-expected.html
new file mode 100644
index 0000000000..85503ae33a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow-expected.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.css-filters.blur-and-shadow</title>
+<h1>2d.layer.css-filters.blur-and-shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="5" />
+ <feDropShadow dx="10" dy="10" stdDeviation="5" flood-color="orange" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow.html
new file mode 100644
index 0000000000..08adfa3d74
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.css-filters.blur-and-shadow-expected.html">
+<title>Canvas test: 2d.layer.css-filters.blur-and-shadow</title>
+<h1>2d.layer.css-filters.blur-and-shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'blur(5px) drop-shadow(10px 10px 5px orange)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-expected.html
new file mode 100644
index 0000000000..c6131f6c1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.css-filters.blur</title>
+<h1>2d.layer.css-filters.blur</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="10" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur.html
new file mode 100644
index 0000000000..3eb3ab8f9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.blur.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.css-filters.blur-expected.html">
+<title>Canvas test: 2d.layer.css-filters.blur</title>
+<h1>2d.layer.css-filters.blur</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'blur(10px)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow-expected.html
new file mode 100644
index 0000000000..60f78d8096
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.css-filters.shadow</title>
+<h1>2d.layer.css-filters.shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx="-10" dy="-10" stdDeviation="5" flood-color="purple" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow.html
new file mode 100644
index 0000000000..e6df8a3ac2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.css-filters.shadow.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.css-filters.shadow-expected.html">
+<title>Canvas test: 2d.layer.css-filters.shadow</title>
+<h1>2d.layer.css-filters.shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'drop-shadow(-10px -10px 5px purple)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.exceptions-are-no-op.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.exceptions-are-no-op.html
new file mode 100644
index 0000000000..7ab2080fca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.exceptions-are-no-op.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.exceptions-are-no-op</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.exceptions-are-no-op</h1>
+<p class="desc">Checks that the context state is left unchanged if beginLayer throws.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Checks that the context state is left unchanged if beginLayer throws.");
+_addTest(function(canvas, ctx) {
+
+ // Get `beginLayer` to throw while parsing the filter.
+ assert_throws_js(TypeError,
+ () => ctx.beginLayer({filter: {name: 'colorMatrix',
+ values: 'foo'}}));
+ // `beginLayer` shouldn't have opened the layer, so `endLayer` should throw.
+ assert_throws_dom("InvalidStateError", () => ctx.endLayer());
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation-expected.html
new file mode 100644
index 0000000000..532c29576c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation-expected.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<title>Canvas test: 2d.layer.flush-on-frame-presentation</title>
+<h1>2d.layer.flush-on-frame-presentation</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation.html
new file mode 100644
index 0000000000..c8a118f8a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.flush-on-frame-presentation.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.flush-on-frame-presentation-expected.html">
+<title>Canvas test: 2d.layer.flush-on-frame-presentation</title>
+<h1>2d.layer.flush-on-frame-presentation</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter-expected.html
new file mode 100644
index 0000000000..88057fc354
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter-expected.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-filter</title>
+<h1>2d.layer.global-filter</h1>
+<p class="desc">Tests that layers ignore the global context filter.</p>
+<canvas id="canvas" width="150" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(60, 10, 30, 30);
+ ctx.filter = 'blur(5px)'
+ ctx.fillRect(10, 10, 30, 30);
+ ctx.fillRect(110, 10, 30, 30);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter.html
new file mode 100644
index 0000000000..cf46f41b97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-filter.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-filter-expected.html">
+<title>Canvas test: 2d.layer.global-filter</title>
+<h1>2d.layer.global-filter</h1>
+<p class="desc">Tests that layers ignore the global context filter.</p>
+<canvas id="canvas" width="150" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.filter = 'blur(5px)'
+
+ ctx.beginLayer();
+ ctx.fillRect(10, 10, 30, 30); // `ctx.filter` applied to draw call.
+ ctx.endLayer();
+
+ ctx.beginLayer();
+ ctx.filter = 'none';
+ ctx.fillRect(60, 10, 30, 30); // Should not be filted by the layer.
+ ctx.endLayer();
+
+ ctx.fillRect(110, 10, 30, 30); // `ctx.filter` is still set.
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha-expected.html
new file mode 100644
index 0000000000..0666e3098a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending-expected.html
new file mode 100644
index 0000000000..8a45027588
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.blending</title>
+<h1>2d.layer.global-states.alpha.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.html
new file mode 100644
index 0000000000..8e15a2b936
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.blending</title>
+<h1>2d.layer.global-states.alpha.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow-expected.html
new file mode 100644
index 0000000000..f7b633b35f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow-expected.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow.html
new file mode 100644
index 0000000000..c8c6d433bc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.blending.shadow.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite-expected.html
new file mode 100644
index 0000000000..951049e638
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.composite</title>
+<h1>2d.layer.global-states.alpha.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.html
new file mode 100644
index 0000000000..1ac6a2cbfe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.composite</title>
+<h1>2d.layer.global-states.alpha.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow-expected.html
new file mode 100644
index 0000000000..0ae93871f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow-expected.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow.html
new file mode 100644
index 0000000000..92b8a0d7a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.composite.shadow.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.html
new file mode 100644
index 0000000000..829796acbf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow-expected.html
new file mode 100644
index 0000000000..6f764c5001
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow.html
new file mode 100644
index 0000000000..a325302b3b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.alpha.shadow.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending-expected.html
new file mode 100644
index 0000000000..33fdf46a28
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.blending</title>
+<h1>2d.layer.global-states.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.html
new file mode 100644
index 0000000000..7d4d9ae4b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.blending</title>
+<h1>2d.layer.global-states.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow-expected.html
new file mode 100644
index 0000000000..6f969074f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.blending.shadow</title>
+<h1>2d.layer.global-states.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow.html
new file mode 100644
index 0000000000..51926d76d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.blending.shadow.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.blending.shadow</title>
+<h1>2d.layer.global-states.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite-expected.html
new file mode 100644
index 0000000000..ed7669c4cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.composite</title>
+<h1>2d.layer.global-states.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.html
new file mode 100644
index 0000000000..898d149924
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.composite</title>
+<h1>2d.layer.global-states.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow-expected.html
new file mode 100644
index 0000000000..b687c27f47
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.composite.shadow</title>
+<h1>2d.layer.global-states.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow.html
new file mode 100644
index 0000000000..c563a57b76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.composite.shadow.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.composite.shadow</title>
+<h1>2d.layer.global-states.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha-expected.html
new file mode 100644
index 0000000000..f304700feb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha</title>
+<h1>2d.layer.global-states.filter.alpha</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending-expected.html
new file mode 100644
index 0000000000..7c91ce4229
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending-expected.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending</title>
+<h1>2d.layer.global-states.filter.alpha.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.html
new file mode 100644
index 0000000000..17e0f0c8c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending</title>
+<h1>2d.layer.global-states.filter.alpha.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html
new file mode 100644
index 0000000000..62942ffeae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow.html
new file mode 100644
index 0000000000..ccadfb624b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.blending.shadow.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite-expected.html
new file mode 100644
index 0000000000..8e0d98648e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite-expected.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite</title>
+<h1>2d.layer.global-states.filter.alpha.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.html
new file mode 100644
index 0000000000..71a27cf710
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite</title>
+<h1>2d.layer.global-states.filter.alpha.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html
new file mode 100644
index 0000000000..a649972546
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow.html
new file mode 100644
index 0000000000..b2907f02aa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.composite.shadow.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.html
new file mode 100644
index 0000000000..5133aa170e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha</title>
+<h1>2d.layer.global-states.filter.alpha</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow-expected.html
new file mode 100644
index 0000000000..169baee29b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow-expected.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow.html
new file mode 100644
index 0000000000..6aa8e75a95
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.alpha.shadow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending-expected.html
new file mode 100644
index 0000000000..f81dcf72dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.blending</title>
+<h1>2d.layer.global-states.filter.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.html
new file mode 100644
index 0000000000..31628812c2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.blending</title>
+<h1>2d.layer.global-states.filter.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow-expected.html
new file mode 100644
index 0000000000..91f3725f8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow-expected.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.blending.shadow</title>
+<h1>2d.layer.global-states.filter.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow.html
new file mode 100644
index 0000000000..e54cf06d0d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.blending.shadow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.blending.shadow</title>
+<h1>2d.layer.global-states.filter.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite-expected.html
new file mode 100644
index 0000000000..97e85a1593
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.composite</title>
+<h1>2d.layer.global-states.filter.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.html
new file mode 100644
index 0000000000..d7e365422f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.composite</title>
+<h1>2d.layer.global-states.filter.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow-expected.html
new file mode 100644
index 0000000000..4716bb2760
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow-expected.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.composite.shadow</title>
+<h1>2d.layer.global-states.filter.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow.html
new file mode 100644
index 0000000000..e5c7698634
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.composite.shadow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.composite.shadow</title>
+<h1>2d.layer.global-states.filter.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states-expected.html
new file mode 100644
index 0000000000..e56fe0b360
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.no-global-states</title>
+<h1>2d.layer.global-states.filter.no-global-states</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states.html
new file mode 100644
index 0000000000..68f4d5004a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.no-global-states.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.no-global-states-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.no-global-states</title>
+<h1>2d.layer.global-states.filter.no-global-states</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow-expected.html
new file mode 100644
index 0000000000..13ba2dd4cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow-expected.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow.html
new file mode 100644
index 0000000000..9efcd9d4f7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.filter.shadow.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states-expected.html
new file mode 100644
index 0000000000..b91a2ae8b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.no-global-states</title>
+<h1>2d.layer.global-states.no-global-states</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states.html
new file mode 100644
index 0000000000..d561be2341
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.no-global-states.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.no-global-states-expected.html">
+<title>Canvas test: 2d.layer.global-states.no-global-states</title>
+<h1>2d.layer.global-states.no-global-states</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow-expected.html
new file mode 100644
index 0000000000..835e9d420a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow.html
new file mode 100644
index 0000000000..209316164c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.global-states.shadow.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html
new file mode 100644
index 0000000000..74e05e1e48
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.invalid-calls.beginLayer-reset-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.invalid-calls.beginLayer-reset-endLayer</h1>
+<p class="desc">Raises exception on beginLayer() + reset() + endLayer().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Raises exception on beginLayer() + reset() + endLayer().");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.reset();
+ ctx.endLayer();
+ });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-restore.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-restore.html
new file mode 100644
index 0000000000..1979cb6c73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-restore.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.invalid-calls.beginLayer-restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.invalid-calls.beginLayer-restore</h1>
+<p class="desc">Raises exception on beginLayer() + restore().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Raises exception on beginLayer() + restore().");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.restore();
+ });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html
new file mode 100644
index 0000000000..c635ac75b9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.invalid-calls.beginLayer-save-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.invalid-calls.beginLayer-save-endLayer</h1>
+<p class="desc">Raises exception on beginLayer() + save() + endLayer().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Raises exception on beginLayer() + save() + endLayer().");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.save();
+ ctx.endLayer();
+ });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.endLayer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.endLayer.html
new file mode 100644
index 0000000000..c39a352d65
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.endLayer.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.invalid-calls.endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.invalid-calls.endLayer</h1>
+<p class="desc">Raises exception on lone endLayer calls.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Raises exception on lone endLayer calls.");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.endLayer();
+ });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-beginLayer-restore.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-beginLayer-restore.html
new file mode 100644
index 0000000000..e2d4d56589
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-beginLayer-restore.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.invalid-calls.save-beginLayer-restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.invalid-calls.save-beginLayer-restore</h1>
+<p class="desc">Raises exception on save() + beginLayer() + restore().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Raises exception on save() + beginLayer() + restore().");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.save();
+ ctx.beginLayer();
+ ctx.restore();
+ });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-endLayer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-endLayer.html
new file mode 100644
index 0000000000..f4308e1191
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.invalid-calls.save-endLayer.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.invalid-calls.save-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.invalid-calls.save-endLayer</h1>
+<p class="desc">Raises exception on save() + endLayer().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Raises exception on save() + endLayer().");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.save();
+ ctx.endLayer();
+ });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html
new file mode 100644
index 0000000000..9283fd39b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.layer-rendering-state-reset-in-layer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.layer-rendering-state-reset-in-layer</h1>
+<p class="desc">Tests that layers ignore the global context filter.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Tests that layers ignore the global context filter.");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalAlpha = 0.5;
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#0000ff';
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 20;
+ ctx.shadowBlur = 30;
+
+ _assertSame(ctx.globalAlpha, 0.5, "ctx.globalAlpha", "0.5");
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ _assertSame(ctx.shadowColor, '#0000ff', "ctx.shadowColor", "'#0000ff'");
+ _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
+ _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
+ _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
+
+ ctx.beginLayer();
+
+ _assertSame(ctx.globalAlpha, 1.0, "ctx.globalAlpha", "1.0");
+ _assertSame(ctx.globalCompositeOperation, 'source-over', "ctx.globalCompositeOperation", "'source-over'");
+ _assertSame(ctx.shadowColor, 'rgba(0, 0, 0, 0)', "ctx.shadowColor", "'rgba(0, 0, 0, 0)'");
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+
+ ctx.endLayer();
+
+ _assertSame(ctx.globalAlpha, 0.5, "ctx.globalAlpha", "0.5");
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ _assertSame(ctx.shadowColor, '#0000ff', "ctx.shadowColor", "'#0000ff'");
+ _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
+ _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
+ _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html
new file mode 100644
index 0000000000..f1204aa61b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.malformed-operations-with-promises.createImageBitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.malformed-operations-with-promises.createImageBitmap</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="200" height="200"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ await createImageBitmap(canvas);
+ // Make sure the exception isn't caused by calling the function twice.
+ await createImageBitmap(canvas);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(canvas));
+
+}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.toBlob.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.toBlob.html
new file mode 100644
index 0000000000..6c69bb3784
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations-with-promises.toBlob.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.malformed-operations-with-promises.toBlob</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.malformed-operations-with-promises.toBlob</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="200" height="200"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ await new Promise(resolve => canvas.toBlob(resolve));
+ // Make sure the exception isn't caused by calling the function twice.
+ await new Promise(resolve => canvas.toBlob(resolve));
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ await promise_rejects_dom(t, 'InvalidStateError', new Promise(resolve => canvas.toBlob(resolve)));
+
+}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.createPattern.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.createPattern.html
new file mode 100644
index 0000000000..f927b96524
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.createPattern.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.malformed-operations.createPattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.malformed-operations.createPattern</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="200" height="200"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+_addTest(function(canvas, ctx) {
+
+ // Shouldn't throw on its own.
+ ctx.createPattern(canvas, 'repeat');
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.createPattern(canvas, 'repeat');
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.createPattern(canvas, 'repeat'));
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.drawImage.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.drawImage.html
new file mode 100644
index 0000000000..8bcc89d38e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.drawImage.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.malformed-operations.drawImage</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.malformed-operations.drawImage</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="200" height="200"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+_addTest(function(canvas, ctx) {
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ // Shouldn't throw on its own.
+ ctx2.drawImage(canvas, 0, 0);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx2.drawImage(canvas, 0, 0);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx2.drawImage(canvas, 0, 0));
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.getImageData.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.getImageData.html
new file mode 100644
index 0000000000..5dc3fcc017
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.getImageData.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.malformed-operations.getImageData</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.malformed-operations.getImageData</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="200" height="200"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+_addTest(function(canvas, ctx) {
+
+ // Shouldn't throw on its own.
+ ctx.getImageData(0, 0, 200, 200);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.getImageData(0, 0, 200, 200);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.getImageData(0, 0, 200, 200));
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.putImageData.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.putImageData.html
new file mode 100644
index 0000000000..fd4fc262c2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.putImageData.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.malformed-operations.putImageData</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.malformed-operations.putImageData</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="200" height="200"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+_addTest(function(canvas, ctx) {
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d')
+ const data = ctx2.getImageData(0, 0, 1, 1);
+ // Shouldn't throw on its own.
+ ctx.putImageData(data, 0, 0);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.putImageData(data, 0, 0);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.putImageData(data, 0, 0));
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.toDataURL.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.toDataURL.html
new file mode 100644
index 0000000000..c9bb4f3875
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.malformed-operations.toDataURL.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.malformed-operations.toDataURL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.malformed-operations.toDataURL</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="200" height="200"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+_addTest(function(canvas, ctx) {
+
+ // Shouldn't throw on its own.
+ canvas.toDataURL();
+ // Make sure the exception isn't caused by calling the function twice.
+ canvas.toDataURL();
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => canvas.toDataURL());
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-expected.html
new file mode 100644
index 0000000000..65525d4d6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-expected.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.nested</title>
+<h1>2d.layer.nested</h1>
+<p class="desc">Tests nested canvas layers.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 40, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx2.fillRect(60, 60, 75, 50);
+
+ ctx2.globalAlpha = 0.5;
+
+ canvas3 = document.createElement("canvas");
+ ctx3 = canvas3.getContext("2d");
+
+ ctx3.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx3.fillRect(50, 50, 75, 50);
+ ctx3.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx3.fillRect(70, 70, 75, 50);
+
+ ctx2.drawImage(canvas3, 0, 0);
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters-expected.html
new file mode 100644
index 0000000000..8b53e2dc76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters-expected.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.nested-filters</title>
+<h1>2d.layer.nested-filters</h1>
+<p class="desc">Checks that nested layers work properly when both apply filters.</p>
+<canvas id="canvas" width="400" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(20, 20, 100, 100);
+ ctx.fillRect(30, 30, 100, 100);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(40, 40, 100, 100);
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(280, 80, 100, 100);
+ ctx.fillRect(270, 70, 100, 100);
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(260, 60, 100, 100);
+ ctx.fillStyle = 'red';
+ ctx.fillRect(250, 50, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters.html
new file mode 100644
index 0000000000..333de67ac9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested-filters.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.nested-filters-expected.html">
+<title>Canvas test: 2d.layer.nested-filters</title>
+<h1>2d.layer.nested-filters</h1>
+<p class="desc">Checks that nested layers work properly when both apply filters.</p>
+<canvas id="canvas" width="400" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -20, dy: -20,
+ stdDeviation: 0, floodColor: 'yellow'}});
+ ctx.beginLayer({filter: 'drop-shadow(-10px -10px 0 blue)'});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'drop-shadow(20px 20px 0 blue)'});
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: 10, dy: 10,
+ stdDeviation: 0, floodColor: 'yellow'}});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(250, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested.html
new file mode 100644
index 0000000000..a508d8e5c8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.nested.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.nested-expected.html">
+<title>Canvas test: 2d.layer.nested</title>
+<h1>2d.layer.nested</h1>
+<p class="desc">Tests nested canvas layers.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 40, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas-expected.html
new file mode 100644
index 0000000000..89c85de1e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.opaque-canvas</title>
+<h1>2d.layer.opaque-canvas</h1>
+<p class="desc">Checks that layer blending works inside opaque canvas</p>
+<canvas id="canvas" width="300" height="300">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 300, 300);
+
+ ctx.fillStyle = 'black';
+ ctx.fillRect(0, 0, 200, 200);
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(10, 10, 100, 100);
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'green';
+ ctx2.fillRect(50, 50, 100, 100);
+ ctx2.globalAlpha = 0.8;
+ ctx2.fillStyle = 'yellow';
+ ctx2.fillRect(75, 25, 100, 100);
+
+ ctx.shadowColor = 'rgba(200, 100, 50, 0.5)';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas.html
new file mode 100644
index 0000000000..be8b088fbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.opaque-canvas.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.opaque-canvas-expected.html">
+<title>Canvas test: 2d.layer.opaque-canvas</title>
+<h1>2d.layer.opaque-canvas</h1>
+<p class="desc">Checks that layer blending works inside opaque canvas</p>
+<canvas id="canvas" width="300" height="300">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = document.createElement('canvas');
+ canvas2.width = 200;
+ canvas2.height = 200;
+ const ctx2 = canvas2.getContext('2d', {alpha: false});
+
+ ctx2.fillStyle = 'purple';
+ ctx2.fillRect(10, 10, 100, 100);
+
+ ctx2.beginLayer({filter: {name: 'dropShadow', dx: -10, dy: 10,
+ stdDeviation: 0,
+ floodColor: 'rgba(200, 100, 50, 0.5)'}});
+ ctx2.fillStyle = 'green';
+ ctx2.fillRect(50, 50, 100, 100);
+ ctx2.globalAlpha = 0.8;
+ ctx2.fillStyle = 'yellow';
+ ctx2.fillRect(75, 25, 100, 100);
+ ctx2.endLayer();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 300, 300);
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset-expected.html
new file mode 100644
index 0000000000..93131dca9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.reset</title>
+<h1>2d.layer.reset</h1>
+<p class="desc">Checks that reset discards any pending layers.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(10, 10, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset.html
new file mode 100644
index 0000000000..9ad779abfd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.reset.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.reset-expected.html">
+<title>Canvas test: 2d.layer.reset</title>
+<h1>2d.layer.reset</h1>
+<p class="desc">Checks that reset discards any pending layers.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ // Global states:
+ ctx.globalAlpha = 0.3;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -3;
+ ctx.shadowOffsetY = 3;
+ ctx.shadowColor = 'rgba(0, 30, 0, 0.3)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -3, dy: 3}});
+
+ // Layer states:
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -6;
+ ctx.shadowOffsetY = 6;
+ ctx.shadowColor = 'rgba(0, 60, 0, 0.6)';
+ ctx.shadowBlur = 3;
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style-expected.html
new file mode 100644
index 0000000000..1d0ac3558d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style-expected.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.restore-style</title>
+<h1>2d.layer.restore-style</h1>
+<p class="desc">Test that ensure layers restores style values upon endLayer.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(60, 60, 75, 50);
+ ctx.drawImage(canvas2, 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style.html
new file mode 100644
index 0000000000..2537f409d6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.restore-style.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.restore-style-expected.html">
+<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-950">
+<title>Canvas test: 2d.layer.restore-style</title>
+<h1>2d.layer.restore-style</h1>
+<p class="desc">Test that ensure layers restores style values upon endLayer.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,255,1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(70, 70, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex-expected.html
new file mode 100644
index 0000000000..ef46f69e2a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.several-complex</title>
+<h1>2d.layer.several-complex</h1>
+<p class="desc">Test to ensure beginlayer works for filter, alpha and shadow, even with consecutive layers.</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 95, 70);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'orange';
+ ctx.shadowBlur = 3;
+
+ var canvas2 = [5];
+ var ctx2 = [5];
+
+ for (let i = 0; i < 5; i++) {
+ canvas2[i] = document.createElement("canvas");
+ ctx2[i] = canvas2[i].getContext("2d");
+ ctx2[i].fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2[i].fillRect(60, 40, 75, 50);
+ ctx2[i].fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2[i].fillRect(80, 60, 75, 50);
+
+ ctx.drawImage(canvas2[i], i, i);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex.html
new file mode 100644
index 0000000000..cff0c123ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.several-complex.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.several-complex-expected.html">
+<meta name=fuzzy content="maxDifference=0-3; totalPixels=0-6318">
+<title>Canvas test: 2d.layer.several-complex</title>
+<h1>2d.layer.several-complex</h1>
+<p class="desc">Test to ensure beginlayer works for filter, alpha and shadow, even with consecutive layers.</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 95, 70);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'orange';
+ ctx.shadowBlur = 3
+
+ for (let i = 0; i < 5; i++) {
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60 + i, 40 + i, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(80 + i, 60 + i, 75, 50);
+
+ ctx.endLayer();
+ }
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html
new file mode 100644
index 0000000000..cc10684a9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ // No clipping.
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html
new file mode 100644
index 0000000000..377dd55ef6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html
new file mode 100644
index 0000000000..88a3a67c7b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance.html
new file mode 100644
index 0000000000..8ade08bec5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.long-distance.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.long-distance-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html
new file mode 100644
index 0000000000..8980708dd1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ // No clipping.
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html
new file mode 100644
index 0000000000..024fc8c9a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html
new file mode 100644
index 0000000000..7ced448144
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance.html
new file mode 100644
index 0000000000..37398400d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.shadow-from-outside-canvas.short-distance.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.short-distance-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-endLayer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-endLayer.html
new file mode 100644
index 0000000000..609cb19a7d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-endLayer.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.beginLayer-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.beginLayer-endLayer</h1>
+<p class="desc">No exception raised on beginLayer() + endLayer().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on beginLayer() + endLayer().");
+_addTest(function(canvas, ctx) {
+
+ ctx.beginLayer();
+ ctx.save();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-save.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-save.html
new file mode 100644
index 0000000000..9658040296
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer-save.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.beginLayer-save</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.beginLayer-save</h1>
+<p class="desc">No exception raised on beginLayer() + save().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on beginLayer() + save().");
+_addTest(function(canvas, ctx) {
+
+ ctx.beginLayer();
+ ctx.save();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer.html
new file mode 100644
index 0000000000..0556683331
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.beginLayer.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.beginLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.beginLayer</h1>
+<p class="desc">No exception raised on lone beginLayer() calls.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on lone beginLayer() calls.");
+_addTest(function(canvas, ctx) {
+
+ ctx.beginLayer();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.restore.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.restore.html
new file mode 100644
index 0000000000..b7a370306f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.restore.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.restore</h1>
+<p class="desc">No exception raised on lone restore() calls.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on lone restore() calls.");
+_addTest(function(canvas, ctx) {
+
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save-beginLayer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save-beginLayer.html
new file mode 100644
index 0000000000..816398c7fc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save-beginLayer.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.save-beginLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.save-beginLayer</h1>
+<p class="desc">No exception raised on save() + beginLayer().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on save() + beginLayer().");
+_addTest(function(canvas, ctx) {
+
+ ctx.save();
+ ctx.beginLayer();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save.html
new file mode 100644
index 0000000000..d8df8cedce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.save</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.save</h1>
+<p class="desc">No exception raised on lone save() calls.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on lone save() calls.");
+_addTest(function(canvas, ctx) {
+
+ ctx.save();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_reset_restore.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_reset_restore.html
new file mode 100644
index 0000000000..4c733263a6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_reset_restore.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.save_reset_restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.save_reset_restore</h1>
+<p class="desc">No exception raised on save() + reset() + restore().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on save() + reset() + restore().");
+_addTest(function(canvas, ctx) {
+
+ ctx.save();
+ ctx.reset();
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_restore.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_restore.html
new file mode 100644
index 0000000000..27ae70278e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.valid-calls.save_restore.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.valid-calls.save_restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.layer.valid-calls.save_restore</h1>
+<p class="desc">No exception raised on save() + restore().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("No exception raised on save() + restore().");
+_addTest(function(canvas, ctx) {
+
+ ctx.save();
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.butt.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.butt.html
new file mode 100644
index 0000000000..f8c7cab1f2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.butt.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cap.butt</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cap.butt</h1>
+<p class="desc">lineCap 'butt' is rendered correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineCap 'butt' is rendered correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'butt';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 25,14, 0,255,0,255);
+ _assertPixel(canvas, 25,15, 0,255,0,255);
+ _assertPixel(canvas, 25,16, 0,255,0,255);
+ _assertPixel(canvas, 25,34, 0,255,0,255);
+ _assertPixel(canvas, 25,35, 0,255,0,255);
+ _assertPixel(canvas, 25,36, 0,255,0,255);
+
+ _assertPixel(canvas, 75,14, 0,255,0,255);
+ _assertPixel(canvas, 75,15, 0,255,0,255);
+ _assertPixel(canvas, 75,16, 0,255,0,255);
+ _assertPixel(canvas, 75,34, 0,255,0,255);
+ _assertPixel(canvas, 75,35, 0,255,0,255);
+ _assertPixel(canvas, 75,36, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.closed.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.closed.html
new file mode 100644
index 0000000000..263c6c6c76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.closed.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cap.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cap.closed</h1>
+<p class="desc">Line caps are not drawn at the corners of an unclosed rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Line caps are not drawn at the corners of an unclosed rectangle");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.invalid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.invalid.html
new file mode 100644
index 0000000000..22e98ee326
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.invalid.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cap.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cap.invalid</h1>
+<p class="desc">Setting lineCap to invalid values is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting lineCap to invalid values is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineCap = 'butt'
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'invalid';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'ROUND';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round\0';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round ';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = "";
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'bevel';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.open.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.open.html
new file mode 100644
index 0000000000..6f7918a1c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.open.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cap.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cap.open</h1>
+<p class="desc">Line caps are drawn at the corners of an unclosed rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Line caps are drawn at the corners of an unclosed rectangle");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.lineTo(200, 200);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.round.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.round.html
new file mode 100644
index 0000000000..3990ad384e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.round.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cap.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cap.round</h1>
+<p class="desc">lineCap 'round' is rendered correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineCap 'round' is rendered correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineCap = 'round';
+ ctx.lineWidth = 20;
+
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(35-tol, 15);
+ ctx.arc(25, 15, 10-tol, 0, Math.PI, true);
+ ctx.arc(25, 35, 10-tol, Math.PI, 0, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(85+tol, 15);
+ ctx.arc(75, 15, 10+tol, 0, Math.PI, true);
+ ctx.arc(75, 35, 10+tol, Math.PI, 0, true);
+ ctx.fill();
+
+ _assertPixel(canvas, 17,6, 0,255,0,255);
+ _assertPixel(canvas, 25,6, 0,255,0,255);
+ _assertPixel(canvas, 32,6, 0,255,0,255);
+ _assertPixel(canvas, 17,43, 0,255,0,255);
+ _assertPixel(canvas, 25,43, 0,255,0,255);
+ _assertPixel(canvas, 32,43, 0,255,0,255);
+
+ _assertPixel(canvas, 67,6, 0,255,0,255);
+ _assertPixel(canvas, 75,6, 0,255,0,255);
+ _assertPixel(canvas, 82,6, 0,255,0,255);
+ _assertPixel(canvas, 67,43, 0,255,0,255);
+ _assertPixel(canvas, 75,43, 0,255,0,255);
+ _assertPixel(canvas, 82,43, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.square.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.square.html
new file mode 100644
index 0000000000..8d6af320fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.square.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cap.square</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cap.square</h1>
+<p class="desc">lineCap 'square' is rendered correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineCap 'square' is rendered correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 5, 20, 40);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 5, 20, 40);
+
+ _assertPixel(canvas, 25,4, 0,255,0,255);
+ _assertPixel(canvas, 25,5, 0,255,0,255);
+ _assertPixel(canvas, 25,6, 0,255,0,255);
+ _assertPixel(canvas, 25,44, 0,255,0,255);
+ _assertPixel(canvas, 25,45, 0,255,0,255);
+ _assertPixel(canvas, 25,46, 0,255,0,255);
+
+ _assertPixel(canvas, 75,4, 0,255,0,255);
+ _assertPixel(canvas, 75,5, 0,255,0,255);
+ _assertPixel(canvas, 75,6, 0,255,0,255);
+ _assertPixel(canvas, 75,44, 0,255,0,255);
+ _assertPixel(canvas, 75,45, 0,255,0,255);
+ _assertPixel(canvas, 75,46, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.valid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.valid.html
new file mode 100644
index 0000000000..a11d32030e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cap.valid.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cap.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cap.valid</h1>
+<p class="desc">Setting lineCap to valid values works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting lineCap to valid values works");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineCap = 'butt'
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'round';
+ _assertSame(ctx.lineCap, 'round', "ctx.lineCap", "'round'");
+
+ ctx.lineCap = 'square';
+ _assertSame(ctx.lineCap, 'square', "ctx.lineCap", "'square'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cross.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cross.html
new file mode 100644
index 0000000000..e6866e3e40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.cross.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.cross</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(110, 50);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(100, 60);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.defaults.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.defaults.html
new file mode 100644
index 0000000000..daa4634387
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.defaults.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.defaults</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.defaults</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+ _assertSame(ctx.lineJoin, 'miter', "ctx.lineJoin", "'miter'");
+ _assertSame(ctx.miterLimit, 10, "ctx.miterLimit", "10");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.fill.noop.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.fill.noop.html
new file mode 100644
index 0000000000..6dd8804ab0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.fill.noop.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.fill.noop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.fill.noop</h1>
+<p class="desc">Filling a line draws nothing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Filling a line draws nothing");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 20;
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(90, 30);
+ ctx.fill();
+ _assertPixel(canvas, 50,24, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,26, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.invalid.strokestyle.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.invalid.strokestyle.html
new file mode 100644
index 0000000000..89a234ecc3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.invalid.strokestyle.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.invalid.strokestyle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.invalid.strokestyle</h1>
+<p class="desc">Verify correct behavior of canvas on an invalid strokeStyle()</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify correct behavior of canvas on an invalid strokeStyle()");
+_addTest(function(canvas, ctx) {
+
+ ctx.strokeStyle = 'rgb(0, 255, 0)';
+ ctx.strokeStyle = 'nonsense';
+ ctx.lineWidth = 200;
+ ctx.moveTo(0,100);
+ ctx.lineTo(200,100);
+ ctx.stroke();
+ var imageData = ctx.getImageData(0, 0, 200, 200);
+ var imgdata = imageData.data;
+ _assert(imgdata[4] == 0, "imgdata[\""+(4)+"\"] == 0");
+ _assert(imgdata[5] == 255, "imgdata[\""+(5)+"\"] == 255");
+ _assert(imgdata[6] == 0, "imgdata[\""+(6)+"\"] == 0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.bevel.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.bevel.html
new file mode 100644
index 0000000000..041e9a1605
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.bevel.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.bevel</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.bevel</h1>
+<p class="desc">lineJoin 'bevel' is rendered correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineJoin 'bevel' is rendered correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.lineTo(40-tol, 20);
+ ctx.lineTo(30, 10+tol);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.lineTo(90+tol, 20);
+ ctx.lineTo(80, 10-tol);
+ ctx.fill();
+
+ _assertPixel(canvas, 34,16, 0,255,0,255);
+ _assertPixel(canvas, 34,15, 0,255,0,255);
+ _assertPixel(canvas, 35,15, 0,255,0,255);
+ _assertPixel(canvas, 36,15, 0,255,0,255);
+ _assertPixel(canvas, 36,14, 0,255,0,255);
+
+ _assertPixel(canvas, 84,16, 0,255,0,255);
+ _assertPixel(canvas, 84,15, 0,255,0,255);
+ _assertPixel(canvas, 85,15, 0,255,0,255);
+ _assertPixel(canvas, 86,15, 0,255,0,255);
+ _assertPixel(canvas, 86,14, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.closed.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.closed.html
new file mode 100644
index 0000000000..a52219ccce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.closed.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.closed</h1>
+<p class="desc">Line joins are drawn at the corner of a closed rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Line joins are drawn at the corner of a closed rectangle");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.invalid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.invalid.html
new file mode 100644
index 0000000000..3c34ef3475
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.invalid.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.invalid</h1>
+<p class="desc">Setting lineJoin to invalid values is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting lineJoin to invalid values is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineJoin = 'bevel'
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'invalid';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'ROUND';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round\0';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round ';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = "";
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'butt';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.miter.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.miter.html
new file mode 100644
index 0000000000..5a3472e92d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.miter.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.miter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.miter</h1>
+<p class="desc">lineJoin 'miter' is rendered correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineJoin 'miter' is rendered correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 30, 20);
+ ctx.fillRect(20, 10, 20, 30);
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 30, 20);
+ ctx.fillRect(70, 10, 20, 30);
+
+ _assertPixel(canvas, 38,12, 0,255,0,255);
+ _assertPixel(canvas, 39,11, 0,255,0,255);
+ _assertPixel(canvas, 40,10, 0,255,0,255);
+ _assertPixel(canvas, 41,9, 0,255,0,255);
+ _assertPixel(canvas, 42,8, 0,255,0,255);
+
+ _assertPixel(canvas, 88,12, 0,255,0,255);
+ _assertPixel(canvas, 89,11, 0,255,0,255);
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 91,9, 0,255,0,255);
+ _assertPixel(canvas, 92,8, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.open.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.open.html
new file mode 100644
index 0000000000..55b7a8b49e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.open.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.open</h1>
+<p class="desc">Line joins are not drawn at the corner of an unclosed rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Line joins are not drawn at the corner of an unclosed rectangle");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.parallel.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.parallel.html
new file mode 100644
index 0000000000..cd736b503d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.parallel.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.parallel</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.parallel</h1>
+<p class="desc">Line joins are drawn at 180-degree joins</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Line joins are drawn at 180-degree joins");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 300;
+ ctx.lineJoin = 'round';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(-100, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.round.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.round.html
new file mode 100644
index 0000000000..fb968b9966
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.round.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.round</h1>
+<p class="desc">lineJoin 'round' is rendered correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineJoin 'round' is rendered correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'round';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.arc(30, 20, 10-tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.arc(80, 20, 10+tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ _assertPixel(canvas, 36,14, 0,255,0,255);
+ _assertPixel(canvas, 36,13, 0,255,0,255);
+ _assertPixel(canvas, 37,13, 0,255,0,255);
+ _assertPixel(canvas, 38,13, 0,255,0,255);
+ _assertPixel(canvas, 38,12, 0,255,0,255);
+
+ _assertPixel(canvas, 86,14, 0,255,0,255);
+ _assertPixel(canvas, 86,13, 0,255,0,255);
+ _assertPixel(canvas, 87,13, 0,255,0,255);
+ _assertPixel(canvas, 88,13, 0,255,0,255);
+ _assertPixel(canvas, 88,12, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.valid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.valid.html
new file mode 100644
index 0000000000..3e5e200cb5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.join.valid.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.join.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.join.valid</h1>
+<p class="desc">Setting lineJoin to valid values works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting lineJoin to valid values works");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineJoin = 'bevel'
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'round';
+ _assertSame(ctx.lineJoin, 'round', "ctx.lineJoin", "'round'");
+
+ ctx.lineJoin = 'miter';
+ _assertSame(ctx.lineJoin, 'miter', "ctx.lineJoin", "'miter'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.acute.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.acute.html
new file mode 100644
index 0000000000..6acba4f3d2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.acute.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.acute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.acute</h1>
+<p class="desc">Miter joins are drawn correctly with acute angles</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Miter joins are drawn correctly with acute angles");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 2.614;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 2.613;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.exceeded.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.exceeded.html
new file mode 100644
index 0000000000..542851d580
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.exceeded.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.exceeded</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.exceeded</h1>
+<p class="desc">Miter joins are not drawn when the miter limit is exceeded</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201); // slightly non-right-angle to avoid being a special case
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.invalid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.invalid.html
new file mode 100644
index 0000000000..826a6e24a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.invalid.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.invalid</h1>
+<p class="desc">Setting miterLimit to invalid values is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting miterLimit to invalid values is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.miterLimit = 1.5;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 0;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -1;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = Infinity;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -Infinity;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = NaN;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 'string';
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = true;
+ _assertSame(ctx.miterLimit, 1, "ctx.miterLimit", "1");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = false;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.lineedge.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.lineedge.html
new file mode 100644
index 0000000000..68eeb06162
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.lineedge.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.lineedge</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.lineedge</h1>
+<p class="desc">Miter joins are not drawn when the miter limit is exceeded at the corners of a zero-height rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded at the corners of a zero-height rectangle");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.strokeRect(100, 25, 200, 0);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.obtuse.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.obtuse.html
new file mode 100644
index 0000000000..2dd08f6afc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.obtuse.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.obtuse</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.obtuse</h1>
+<p class="desc">Miter joins are drawn correctly with obtuse angles</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Miter joins are drawn correctly with obtuse angles");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 1600;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.083;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.082;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.rightangle.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.rightangle.html
new file mode 100644
index 0000000000..7ae9f5e232
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.rightangle.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.rightangle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.rightangle</h1>
+<p class="desc">Miter joins are not drawn when the miter limit is exceeded, on exact right angles</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded, on exact right angles");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 200);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.valid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.valid.html
new file mode 100644
index 0000000000..51b00c292d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.valid.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.valid</h1>
+<p class="desc">Setting miterLimit to valid values works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting miterLimit to valid values works");
+_addTest(function(canvas, ctx) {
+
+ ctx.miterLimit = 1.5;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = "1e1";
+ _assertSame(ctx.miterLimit, 10, "ctx.miterLimit", "10");
+
+ ctx.miterLimit = 1/1024;
+ _assertSame(ctx.miterLimit, 1/1024, "ctx.miterLimit", "1/1024");
+
+ ctx.miterLimit = 1000;
+ _assertSame(ctx.miterLimit, 1000, "ctx.miterLimit", "1000");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.within.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.within.html
new file mode 100644
index 0000000000..f438866009
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.miter.within.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.miter.within</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.miter.within</h1>
+<p class="desc">Miter joins are drawn when the miter limit is not quite exceeded</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Miter joins are drawn when the miter limit is not quite exceeded");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.416;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.union.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.union.html
new file mode 100644
index 0000000000..9d43cebee7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.union.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.union</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.union</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 24);
+ ctx.lineTo(100, 25);
+ ctx.lineTo(0, 26);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 25,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 25,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.basic.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.basic.html
new file mode 100644
index 0000000000..f2e9a9f86d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.basic.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.width.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.width.basic</h1>
+<p class="desc">lineWidth determines the width of line strokes</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineWidth determines the width of line strokes");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 20;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 14,25, 0,255,0,255);
+ _assertPixel(canvas, 15,25, 0,255,0,255);
+ _assertPixel(canvas, 16,25, 0,255,0,255);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 34,25, 0,255,0,255);
+ _assertPixel(canvas, 35,25, 0,255,0,255);
+ _assertPixel(canvas, 36,25, 0,255,0,255);
+
+ _assertPixel(canvas, 64,25, 0,255,0,255);
+ _assertPixel(canvas, 65,25, 0,255,0,255);
+ _assertPixel(canvas, 66,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 84,25, 0,255,0,255);
+ _assertPixel(canvas, 85,25, 0,255,0,255);
+ _assertPixel(canvas, 86,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.invalid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.invalid.html
new file mode 100644
index 0000000000..363060cf6d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.invalid.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.width.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.width.invalid</h1>
+<p class="desc">Setting lineWidth to invalid values is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting lineWidth to invalid values is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineWidth = 1.5;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 0;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -1;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = Infinity;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -Infinity;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = NaN;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 'string';
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = true;
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = false;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.scaledefault.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.scaledefault.html
new file mode 100644
index 0000000000..6c4ec52b5d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.scaledefault.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.width.scaledefault</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.width.scaledefault</h1>
+<p class="desc">Default lineWidth strokes are affected by scale transformations</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Default lineWidth strokes are affected by scale transformations");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(50, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.moveTo(0, 0.5);
+ ctx.lineTo(2, 0.5);
+ ctx.stroke();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 50,5, 0,255,0,255);
+ _assertPixel(canvas, 50,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.transformed.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.transformed.html
new file mode 100644
index 0000000000..600ce93fb7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.transformed.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.width.transformed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.width.transformed</h1>
+<p class="desc">Line stroke widths are affected by scale transformations</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Line stroke widths are affected by scale transformations");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 4;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.save();
+ ctx.scale(5, 1);
+ ctx.beginPath();
+ ctx.moveTo(5, 15);
+ ctx.lineTo(5, 35);
+ ctx.stroke();
+ ctx.restore();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.save();
+ ctx.scale(-5, 1);
+ ctx.beginPath();
+ ctx.moveTo(-15, 15);
+ ctx.lineTo(-15, 35);
+ ctx.stroke();
+ ctx.restore();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 14,25, 0,255,0,255);
+ _assertPixel(canvas, 15,25, 0,255,0,255);
+ _assertPixel(canvas, 16,25, 0,255,0,255);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 34,25, 0,255,0,255);
+ _assertPixel(canvas, 35,25, 0,255,0,255);
+ _assertPixel(canvas, 36,25, 0,255,0,255);
+
+ _assertPixel(canvas, 64,25, 0,255,0,255);
+ _assertPixel(canvas, 65,25, 0,255,0,255);
+ _assertPixel(canvas, 66,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 84,25, 0,255,0,255);
+ _assertPixel(canvas, 85,25, 0,255,0,255);
+ _assertPixel(canvas, 86,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.valid.html b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.valid.html
new file mode 100644
index 0000000000..1a76b0ed4c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/line-styles/2d.line.width.valid.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.line.width.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.line.width.valid</h1>
+<p class="desc">Setting lineWidth to valid values works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting lineWidth to valid values works");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineWidth = 1.5;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = "1e1";
+ _assertSame(ctx.lineWidth, 10, "ctx.lineWidth", "10");
+
+ ctx.lineWidth = 1/1024;
+ _assertSame(ctx.lineWidth, 1/1024, "ctx.lineWidth", "1/1024");
+
+ ctx.lineWidth = 1000;
+ _assertSame(ctx.lineWidth, 1000, "ctx.lineWidth", "1000");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/manual/README.md b/testing/web-platform/tests/html/canvas/element/manual/README.md
new file mode 100644
index 0000000000..68d877200b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/README.md
@@ -0,0 +1 @@
+This folder contains tests that were manually added to the repository. All other tests have been autogenerated by html/canvas/tools/build.sh \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001-ref.htm
new file mode 100644
index 0000000000..31ddfcafd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001-ref.htm
@@ -0,0 +1,22 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: arcTo() adds to subpath if same point</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+ ctx.moveTo(0, 50);
+
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: If x1,y1 and x2,y2 are the same point, then arcTo must add x1,y1 to the subpath, and connect that point to x0,y0 with a straight line.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas (test ref).</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001.htm b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001.htm
new file mode 100644
index 0000000000..c3f2fb6f43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_arcto_001.htm
@@ -0,0 +1,26 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: arcTo() adds to subpath if same point</title>
+ <link rel="match" href="canvas_complexshapes_arcto_001-ref.htm">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto" />
+ <meta name="assert" content="If x1,y1 and x2,y2 are the same point, then arcTo must add x1,y1 to the subpath, and connect that point to x0,y0 with a straight line." />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+ ctx.moveTo(0, 50);
+
+ // Since (x1, y1) and (x2, y2) are the same point, (x1, y1) must be added to the subpath, thus creating a line.
+ ctx.arcTo(100, 50, 100, 50, 10);
+ ctx.stroke();
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: If x1,y1 and x2,y2 are the same point, then arcTo must add x1,y1 to the subpath, and connect that point to x0,y0 with a straight line.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001-ref.htm
new file mode 100644
index 0000000000..6be08c0b3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001-ref.htm
@@ -0,0 +1,32 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: bezierCurveTo() must ensure subpaths</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ ctx.moveTo(65,25)
+ ctx.bezierCurveTo(65, 25, 65, 25, 65, 65);
+ ctx.stroke();
+ ctx.beginPath();
+
+ ctx.moveTo(35,25)
+ ctx.bezierCurveTo(35, 25, 35, 25, 35, 65);
+ ctx.stroke();
+ ctx.beginPath();
+
+ ctx.moveTo(0,75)
+ ctx.bezierCurveTo(0, 75, 50, 150, 100, 75);
+ ctx.stroke();
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) must ensure there is a subpath for the point (cp1x,cp1y) if the context has no subpaths, then it must connect the last point in the subpath to the point (x,y).</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas (ref test).</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001.htm b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001.htm
new file mode 100644
index 0000000000..d04926ebac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/building-paths/canvas_complexshapes_beziercurveto_001.htm
@@ -0,0 +1,35 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: bezierCurveTo() must ensure subpaths</title>
+ <link rel="match" href="canvas_complexshapes_beziercurveto_001-ref.htm">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto" />
+ <meta name="assert" content="bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) must ensure there is a subpath for the point (cp1x,cp1y) if the context has no subpaths, then it must connect the last point in the subpath to the point (x,y)." />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Since the canvas has no subpaths, a virtual moveTo must be performed to (65,25) before creating the bezier.
+ ctx.bezierCurveTo(65, 25, 65, 25, 65, 65);
+ ctx.stroke();
+ ctx.beginPath();
+
+ // Since the canvas has no subpaths, a virtual moveTo must be performed to (35,25) before creating the bezier.
+ ctx.bezierCurveTo(35, 25, 35, 25, 35, 65);
+ ctx.stroke();
+ ctx.beginPath();
+
+ // Since the canvas has no subpaths, a virtual moveTo must be performed to (0,75) before creating the bezier.
+ ctx.bezierCurveTo(0, 75, 50, 150, 100, 75);
+ ctx.stroke();
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) must ensure there is a subpath for the point (cp1x,cp1y) if the context has no subpaths, then it must connect the last point in the subpath to the point (x,y).</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow-ref.html b/testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow-ref.html
new file mode 100644
index 0000000000..3ff0ed291d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow-ref.html
@@ -0,0 +1,15 @@
+<body>
+<script>
+var canvas, ctx;
+canvas = document.createElement("canvas");
+canvas.width = 100;
+canvas.height = 100;
+document.body.appendChild(canvas);
+
+ctx = canvas.getContext("2d");
+ctx.globalAlpha = 0.5;
+ctx.shadowOffsetX = -canvas.width;
+ctx.shadowColor = 'red';
+ctx.fillRect(canvas.width,0,canvas.width,canvas.height);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow.html b/testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow.html
new file mode 100644
index 0000000000..1df227ce0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/compositing/alpha_filter_shadow.html
@@ -0,0 +1,16 @@
+<body>
+<script>
+var canvas, ctx;
+canvas = document.createElement("canvas");
+canvas.width = 100;
+canvas.height = 100;
+document.body.appendChild(canvas);
+
+ctx = canvas.getContext("2d");
+ctx.globalAlpha = 0.5;
+ctx.shadowOffsetX = -canvas.width;
+ctx.shadowColor = 'red';
+ctx.filter = 'sepia(0)';
+ctx.fillRect(canvas.width,0,canvas.width,canvas.height);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001-ref.htm
new file mode 100644
index 0000000000..70196fb36a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001-ref.htm
@@ -0,0 +1,11 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: globalCompositeOperation "destination-over"</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ </head>
+ <body>
+ <p>Description: If the globalCompositeOperation is set to "destination-over", display the destination image wherever the destination image is opaque.</p>
+ <div><img alt='black rectangle' src="/images/black-rectangle.png"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001.htm b/testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001.htm
new file mode 100644
index 0000000000..8d6208eb32
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/compositing/canvas_compositing_globalcompositeoperation_001.htm
@@ -0,0 +1,32 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: globalCompositeOperation "destination-over"</title>
+ <link rel="match" href="canvas_compositing_globalcompositeoperation_001-ref.htm">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-globalcompositeoperation" />
+ <meta name="assert" content="If the globalCompositeOperation is set to 'destination-over', display the destination image wherever the destination image is opaque." />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Source image.
+ ctx.fillStyle = "rgba(0, 0, 0, 1.0)";
+ ctx.fillRect(0, 0, 100, 50);
+
+ // Assign the globalCompositeOperation.
+ ctx.globalCompositeOperation = "destination-over";
+
+ // Destination image.
+ ctx.fillStyle = "rgba(255, 0, 0, 1.0)";
+ ctx.fillRect(0, 0, 100, 50);
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: If the globalCompositeOperation is set to "destination-over", display the destination image wherever the destination image is opaque.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/canvas-with-padding.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/canvas-with-padding.html
new file mode 100644
index 0000000000..5a93ef680a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/canvas-with-padding.html
@@ -0,0 +1,10 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<canvas id='c' style="padding-right: 4294967292; border-right: 124px solid black;"></canvas>
+<script>
+ test(function(t) {
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+ ctx.getImageData(0, 0, 1, 1);
+ }, '// The test case passes by not crashing.')
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false-ref.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false-ref.html
new file mode 100644
index 0000000000..eccfc4e2cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, fillRect with semi-transparent color.</title>
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d");
+ctx.fillStyle = 'black';
+ctx.fillRect(-1, -1, 102, 102);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false.html
new file mode 100644
index 0000000000..a328fbf97a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/clearRect_alpha_false.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, clearRect</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/#concept-canvas-alpha">
+<link rel="match" href="clearRect_alpha_false-ref.html">
+<meta name="assert" content="Canvas pixels remain opaque black when drawing semi-transparent rectangle.">
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d", {alpha: false});
+ctx.fillColor = 'red';
+ctx.fillRect(25, 25, 50, 25);
+ctx.clearRect(24, 24, 52, 52);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false-ref.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false-ref.html
new file mode 100644
index 0000000000..2c9e5620b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, drawing a transparent image.</title>
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d");
+ctx.fillStyle = 'black';
+ctx.fillRect(-1, -1, 102, 102);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false.html
new file mode 100644
index 0000000000..64f38bceb1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/drawImage_alpha_false.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, drawing a transparent image</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/#concept-canvas-alpha">
+<link rel="match" href="drawImage_alpha_false-ref.html">
+<meta name="assert" content="Canvas pixels remain opaque black when drawing a transparent image.">
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const source = document.createElement('canvas');
+source.width = source.height = 20;
+const source_ctx = source.getContext("2d"); // leave default transparent content
+
+const ctx = document.getElementById("c").getContext("2d", {alpha: false});
+ctx.globalCompositOperation = 'copy';
+ctx.drawImage(source, 0, 0);
+ctx.globalCompositOperation = 'source-over';
+ctx.drawImage(source, 20, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false-ref.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false-ref.html
new file mode 100644
index 0000000000..eccfc4e2cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, fillRect with semi-transparent color.</title>
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d");
+ctx.fillStyle = 'black';
+ctx.fillRect(-1, -1, 102, 102);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false.html
new file mode 100644
index 0000000000..143756eb0c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fillRect_alpha_false.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, fillRect with semi-transparent color.</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/#concept-canvas-alpha">
+<link rel="match" href="fillRect_alpha_false-ref.html">
+<meta name="assert" content="Canvas pixels remain opaque black when drawing semi-transparent rectangle.">
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d", {alpha: false});
+ctx.fillColor = "rgba(0, 0, 0, 0.5)";
+ctx.fillRect(25, 25, 50, 50);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false-ref.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false-ref.html
new file mode 100644
index 0000000000..03265a656e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, path fill with transparent color and 'copy' composite operation.</title>
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d");
+ctx.fillStyle = 'black';
+ctx.fillRect(-1, -1, 102, 102);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false.html
new file mode 100644
index 0000000000..7872a79380
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/fill_alpha_false.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase, path fill with transparent color and 'copy' composite operation.</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/#concept-canvas-alpha">
+<link rel="match" href="fill_alpha_false-ref.html">
+<meta name="assert" content="Canvas pixels remain opaque black when filling path with tranparent pixels and 'copy' compisite op.">
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d", {alpha: false});
+ctx.fillColor = 'red';
+ctx.fillRect(25, 25, 50, 25); // will be overwritten.
+ctx.fillColor = "rgba(0, 0, 0, 0.0)";
+ctx.globalCompositeOperation = 'copy';
+ctx.rect(25, 25, 50, 50);
+ctx.fill();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/getContextAttributes.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/getContextAttributes.html
new file mode 100644
index 0000000000..47b3d96233
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/getContextAttributes.html
@@ -0,0 +1,46 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+
+var testScenarios = [
+ {testDescription: "Test default context creation attributes",
+ canvasContextAttributes: {},
+ expectedContextAttributes: {alpha: true, desynchronized: false, willReadFrequently: false}},
+ {testDescription: "Test context creation attributes alpha: true",
+ canvasContextAttributes: {alpha: true},
+ expectedContextAttributes: {alpha: true}},
+ {testDescription: "Test context creation attributes alpha: false",
+ canvasContextAttributes: {alpha: false},
+ expectedContextAttributes: {alpha: false}},
+ {testDescription: "Test context creation attributes desynchronized: false",
+ canvasContextAttributes: {desynchronized: false},
+ expectedContextAttributes: {desynchronized: false}},
+ {testDescription: "Test context creation attributes willReadFrequently: true",
+ canvasContextAttributes: {willReadFrequently: true},
+ expectedContextAttributes: {willReadFrequently: true}},
+];
+
+function runTestScenario(testScenario) {
+ var t = test(function() {
+ var canvas = document. createElement('canvas');
+ var ctx = canvas.getContext('2d', testScenario.canvasContextAttributes);
+ var contextAttributes = ctx.getContextAttributes();
+ if (testScenario.expectedContextAttributes.alpha !== undefined) {
+ assert_equals(contextAttributes.alpha,
+ testScenario.expectedContextAttributes.alpha);
+ }
+ if (testScenario.expectedContextAttributes.desynchronized !== undefined) {
+ assert_equals(contextAttributes.desynchronized,
+ testScenario.expectedContextAttributes.desynchronized);
+ }
+ }, testScenario.testDescription);
+}
+
+function runAllTests() {
+ for (var i = 0; i < testScenarios.length; i++)
+ runTestScenario(testScenarios[i]);
+}
+
+runAllTests();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false-ref.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false-ref.html
new file mode 100644
index 0000000000..b67513c464
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase context creation parameter is initialized with solid black.</title>
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d");
+ctx.fillStyle = 'black';
+ctx.fillRect(-1, -1, 102, 102);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false.html
new file mode 100644
index 0000000000..9e86e5ce2c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/initial_color_alpha_false.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase context creation parameter is initialized with solid black.</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/#concept-canvas-alpha">
+<link rel="match" href="initial_color_alpha_false-ref.html">
+<meta name="assert" content="Canvas pixels are initialized to opaque black when alpha attribute is false.">
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+document.getElementById("c").getContext("2d", {alpha: false});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false-ref.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false-ref.html
new file mode 100644
index 0000000000..d663131148
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase context creation parameter is re-initialized with solid black.</title>
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const ctx = document.getElementById("c").getContext("2d");
+ctx.fillStyle = 'black';
+ctx.fillRect(-1, -1, 102, 102);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false.html b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false.html
new file mode 100644
index 0000000000..191975e0f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/context-attributes/reset_color_alpha_false.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext 2D with alpha=flase context creation parameter is re-initialized with solid black.</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/#concept-canvas-alpha">
+<link rel="match" href="reset_color_alpha_false-ref.html">
+<meta name="assert" content="Canvas pixels are re-initialized to opaque black upon context reset.">
+<p>Test passes if a 100x100 black square is displayed below.</p>
+<canvas id="c" width=100 height=100></canvas>
+<script>
+const canvas = document.getElementById("c");
+const ctx = canvas.getContext("2d", {alpha: false});
+ctx.fillColor = 'red';
+ctx.fillRect(25, 25, 50, 50);
+canvas.width = canvas.width;
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas.html
new file mode 100644
index 0000000000..36bd085136
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas.html
@@ -0,0 +1,203 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<canvas id="dest" height="100" width="100"></canvas>
+
+<script>
+var sourceCanvasWidth = sourceCanvasHeight = 50;
+var destCanvasWidth = destCanvasHeight = 100;
+var blueRect = {x: 0, y: 0, w: 50, h: 50};
+var blackRect = {x: 5, y: 5, w: 40, h: 40};
+var redPixel = [255, 0, 0, 255];
+var bluePixel = [0, 0, 255, 255];
+var blackPixel = [0, 0, 0, 255];
+var transparentBlackPixel = [0, 0, 0, 0];
+
+var destCanvas = document.getElementById('dest');
+var destCtx = destCanvas.getContext('2d');
+destCtx.imageSmoothingEnabled = false;
+
+function checkPixel(location, expected) {
+ var actual = destCtx.getImageData(location[0], location[1], 1, 1).data;
+ assert_array_equals(actual, expected);
+}
+
+function PreparePixelTests(blueCheck, blackCheck, redCheck, testDescription) {
+ var pixelTests = [];
+ for (var i = 0; i < blueCheck.length; i++) {
+ var message = testDescription + 'Pixel ' + blueCheck[i][0] + ',' + blueCheck[i][1] + ' should be blue.';
+ pixelTests.push([message, blueCheck[i], bluePixel]);
+ }
+ for (var i = 0; i < blackCheck.length; i++) {
+ var message = testDescription + 'Pixel ' + blackCheck[i][0] + ',' + blackCheck[i][1] + ' should be black.';
+ pixelTests.push([message, blackCheck[i], blackPixel]);
+ }
+ for (var i = 0; i < redCheck.length; i++) {
+ var message = testDescription + 'Pixel ' + redCheck[i][0] + ',' + redCheck[i][1] + ' should be red.';
+ pixelTests.push([message, redCheck[i], redPixel]);
+ }
+ pixelTests.push([testDescription + 'Pixel outside canvas should be transparent black.\n', [100, 100], transparentBlackPixel]);
+ return pixelTests;
+}
+
+function drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription) {
+ destCtx.fillStyle = 'red';
+ destCtx.fillRect(0, 0, destCanvasWidth, destCanvasHeight);
+
+ var sourceCanvas = document.createElement('canvas');
+ sourceCanvas.width = sourceCanvasWidth;
+ sourceCanvas.height = sourceCanvasHeight;
+ var sourceCtx = sourceCanvas.getContext('2d');
+ sourceCtx.fillStyle = 'blue';
+ sourceCtx.fillRect(blueRect.x, blueRect.y, blueRect.w, blueRect.h);
+ sourceCtx.fillStyle = 'black';
+ sourceCtx.fillRect(blackRect.x, blackRect.y, blackRect.w, blackRect.h);
+ if (typeof sourceRect.x !== 'undefined')
+ destCtx.drawImage(sourceCanvas, sourceRect.x, sourceRect.y, sourceRect.w, sourceRect.h,
+ destRect.x, destRect.y, destRect.w, destRect.h);
+ else if (typeof destRect.w !== 'undefined')
+ destCtx.drawImage(sourceCanvas, destRect.x, destRect.y, destRect.w, destRect.h);
+ else
+ destCtx.drawImage(sourceCanvas, destRect.x, destRect.y);
+ var pixelTests = PreparePixelTests(blueCheck, blackCheck, redCheck, testDescription);
+ generate_tests(checkPixel, pixelTests);
+}
+
+var testDescription;
+var sourceRect = {}, destRect = {};
+var blueCheck, blackCheck, redCheck;
+
+// 2 arguments, the dest origin is 0,0
+// The source canvas will be copied to the 0,0 position of the destination canvas
+testDescription = 'Test scenario 1: dx = 0, dy = 0 --- ';
+destRect = {x: 0, y: 0};
+blueCheck = [[0,0], [0,49], [49,0], [49,49]];
+blackCheck = [[5,5], [5,44], [44,5], [44,44]];
+redCheck = [[50,0], [0,50], [50,50], [99,99]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// 2 arguments, the dest origin is not 0,0
+// The source canvas will copied to the 25, 25 position of the destination canvas
+testDescription = 'Test scenario 2: dx = 25, dy = 25 --- ';
+destRect = {x: 25, y: 25};
+blueCheck = [[25,25], [25,74], [74,25], [74,74]];
+blackCheck = [[30,30], [30,69], [69,30], [69,69]];
+redCheck = [[24,24], [24,75], [75,24], [75,75]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// 4 arguments, the source origin is not 0,0, the dest size is provided
+// The source canvas will copied to the 50, 50 position of the destination canvas and
+// on an area of 50x50 pixels
+testDescription = 'Test scenario 3: dx = 50, dy = 50, dw = 50, dh = 50 --- ';
+destRect = {x: 50, y: 50, w: 50, h: 50};
+blueCheck = [[50,50], [50,99], [99,50], [99,99]];
+blackCheck = [[55,55], [55,94], [94,55], [94,94]];
+redCheck = [[0,0], [49,49], [49,99], [99,49]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// 4 arguments, the dest origin is not 0,0 and the dest size is provided but
+// does not match the size of the source. The image will be distorted
+// The source canvas will copied to the 50,50 position of the destination canvas
+// and it will be shrunk to a and area of 20x20
+testDescription = 'Test scenario 4: dx = 50, dy = 50, dw = 20, dh = 20 --- ';
+destRect = {x: 50, y: 50, w: 20, h: 20};
+blueCheck = [[50,50], [50,69], [69,50], [69,69]];
+blackCheck = [[52,52], [52,67], [67,52], [67,67]];
+redCheck = [[49,49], [49,70], [70,49], [70,70]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// The source canvas will copied to the 50,50 position of the destination canvas
+// over an area of 50x25 pixels
+// The copied image will be distorted along the x axis
+testDescription = 'Test scenario 5: dx = 50, dy = 50, dw = 50, dh = 20 --- ';
+destRect = {x: 50, y: 50, w: 50, h: 20};
+blueCheck = [[50,50], [50,69], [99,50], [99,69]];
+blackCheck = [[55,52], [55,67], [94,52], [94,67]];
+redCheck = [[49,49], [49, 69], [99,49], [99,70]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// 8 arguments, both destination and source origins are 0, 0
+// An area of 25x25 pixels of the source image will be copied to
+// an area of 25x25 pixels of the destination canvas
+// destCtx.drawImage(sourceCanvas, 0, 0, 25, 25, 0, 0, 25, 25);
+testDescription = 'Test scenario 6: sx = 0, sy = 0, sw = 25, sh = 25, dx = 0, dy = 0, dw = 25, dh = 25 --- ';
+sourceRect = {x: 0, y: 0, w: 25, h: 25};
+destRect = {x: 0, y: 0, w: 25, h: 25};
+blueCheck = [[0,0], [4,4], [0,24], [24,0]];
+blackCheck = [[5,5], [5,24], [24,5], [24,24]];
+redCheck = [[25,25], [25, 99], [99,25], [99,99]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// 8 arguments the destination origin is not 0,0
+// An area of 25x25 pixels of the source image will be copied to
+// an area of 25x25 pixels of the destination canvas in the position 25,25
+testDescription = 'Test scenario 7: sx = 0, sy = 0, sw = 25, sh = 25, dx = 25, dy = 25, dw = 25, dh = 25 --- ';
+sourceRect = {x: 0, y: 0, w: 25, h: 25};
+destRect = {x: 25, y: 25, w: 25, h: 25};
+blueCheck = [[25,25], [25,49], [49,25], [29,29]];
+blackCheck = [[30,30], [30,49], [49,30], [49,49]];
+redCheck = [[24,24], [24, 50], [50,24], [50,50]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// The source rectangle overflows the source image
+// The source area is clipped to fit the source image
+// and the destination are is clipped in the same proportion
+testDescription = 'Test scenario 8: sx = 25, sy = 25, sw = 50, sh = 50, dx = 0, dy = 0, dw = 50, dh = 50 --- ';
+sourceRect = {x: 25, y: 25, w: 50, h: 50};
+destRect = {x: 0, y: 0, w: 50, h: 50};
+blueCheck = [[0,20], [20,0], [20,20], [24,24]];
+blackCheck = [[0,0], [0,19], [19,0], [19,19]];
+redCheck = [[0,25], [25, 0], [25,25], [99,99]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// The destination rectangle has negative width and height. When the source
+// rectangle is outside the source image, the source rectangle must be clipped
+// to the source image and the destination rectangle must be clipped in the same
+// proportion.
+testDescription = 'Test scenario 9: sx = 0, sy = 0, sw = 50, sh = 50, dx = 100, dy = 100, dw = -50, dh = -50 --- ';
+sourceRect = {x: 0, y: 0, w: 50, h: 50};
+destRect = {x: 100, y: 100, w: -50, h: -50};
+blueCheck = [[50,50], [50,99], [99,50], [99,99]];
+blackCheck = [[55,55], [55,94], [94,55], [94,94]];
+redCheck = [[0,0], [49,49], [0,99], [99,0]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// The destination rectangle is larger than the destination canvas
+// When the destination rectangle is outside the destination image (the scratch bitmap),
+// the pixels that land outside the scratch bitmap are discarded,
+// as if the destination was an infinite canvas whose rendering was
+// clipped to the dimensions of the scratch bitmap.
+testDescription = 'Test scenario 10: sx = 0, sy = 0, sw = 50, sh = 50, dx = 0, dy = 0, dw = 200, dh = 200 --- ';
+sourceRect = {x: 0, y: 0, w: 50, h: 50};
+destRect = {x: 0, y: 0, w: 200, h: 200};
+blueCheck = [[0,0], [0,99], [99,0], [19,19]];
+blackCheck = [[20,20], [20,99], [99,20], [99,99]];
+redCheck = [];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// The source rectangle is larger than the source canvas
+// The source area is clipped to fit the source image
+// and the destination are is clipped in the same proportion
+testDescription = 'Test scenario 11: sx = 0, sy = 0, sw = 100, sh = 100, dx = 0, dy = 0, dw = 50, dh = 50 --- ';
+sourceRect = {x: 0, y: 0, w: 100, h: 100};
+destRect = {x: 0, y: 0, w: 50, h: 50};
+blueCheck = [[0,0], [1,1], [23,23], [24,24]];
+blackCheck = [[3,3], [3,21], [21,3], [21,21]];
+redCheck = [[0,25], [25, 0], [25,25], [99,99]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+// Negative coordinates of the source rectangle.
+// The source area is clipped to fit the source image and the destination area
+// is clipped in the same proportion. In this specific test:
+// - source is clipped by 20 from top and left.
+// - destination will get proportionally clipped by 50 from top and left as we
+// are scaling the source image 2.5 times.
+// - the rect will be drawn from 70,70 to 100,100.
+testDescription = 'Test scenario 12: sx = -20, sy = -20, sw = 50, sh = 50, dx = 20, dy = 20, dw = 125, dh = 125 --- ';
+sourceRect = {x: -20, y: -20, w: 50, h: 50};
+destRect = {x: 20, y: 20, w: 125, h: 125};
+blueCheck = [[70,70], [70,99], [99,70], [82,82]];
+blackCheck = [[84,84], [84,99], [99,84], [99,99]];
+redCheck = [[0,69], [69, 0], [69,69]];
+drawCanvasOnCanvasUsingDrawImage(sourceRect, destRect, blueCheck, blackCheck, redCheck, testDescription);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self.html
new file mode 100644
index 0000000000..83cf53583c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self.html
@@ -0,0 +1,17 @@
+<link rel="match" href="drawimage_canvas_self_ref.html">
+<canvas id="dest" height="100" width="100"></canvas>
+<script>
+var canvasWidth = canvasHeight = 100;
+var destWidth = canvasWidth / 4;
+var destHeight = canvasHeight / 4;
+var destCanvas = document.getElementById('dest');
+var destCtx = destCanvas.getContext('2d');
+
+destCtx.fillStyle = 'red';
+destCtx.fillRect(0, 0, canvasWidth, canvasHeight);
+destCtx.fillStyle = 'green';
+destCtx.fillRect(0, 0, canvasWidth / 2, canvasHeight / 2);
+destCtx.drawImage(destCanvas,
+ 0, 0, destWidth, destHeight,
+ canvasWidth / 2, canvasHeight / 2, destWidth, destHeight);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self_ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self_ref.html
new file mode 100644
index 0000000000..9f297cacdc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_canvas_self_ref.html
@@ -0,0 +1,11 @@
+<canvas id="dest" height="100" width="100"></canvas>
+<script>
+var canvasWidth = canvasHeight = 100;
+var destCanvas = document.getElementById('dest');
+var destCtx = destCanvas.getContext('2d');
+destCtx.fillStyle = 'red';
+destCtx.fillRect(0, 0, canvasWidth, canvasHeight);
+destCtx.fillStyle = 'green';
+destCtx.fillRect(0, 0, canvasWidth / 2, canvasHeight / 2);
+destCtx.fillRect(canvasWidth / 2, canvasHeight / 2, canvasWidth / 4, canvasHeight / 4);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_crossorigin.sub.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_crossorigin.sub.html
new file mode 100644
index 0000000000..3d57d9f064
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_crossorigin.sub.html
@@ -0,0 +1,61 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ function draw_and_read_image(img, should_throw) {
+ let c = document.createElement('canvas');
+ document.body.appendChild(c);
+ let ctx = c.getContext('2d');
+ ctx.drawImage(img, 0, 0);
+
+ function get_image_data() {
+ ctx.getImageData(0, 0, 4, 4);
+ }
+
+ if (should_throw) {
+ assert_throws_dom('SecurityError', get_image_data);
+ } else {
+ get_image_data();
+ }
+
+ document.body.removeChild(c);
+ }
+
+ async_test(t => {
+ let img = new Image();
+ img.src = "/images/green.png";
+ img.crossOrigin = "anonymous";
+ img.onload = t.step_func_done(() => {
+ draw_and_read_image(img, false);
+ });
+ img.onerror = t.unreached_func();
+ }, "Can get pixels of canvas with same origin image drawn");
+
+ async_test(t => {
+ let img = new Image();
+ img.src = "http://{{hosts[][www]}}:{{ports[http][0]}}/images/green.png?pipe=header(Access-Control-Allow-Origin,*)";
+ img.crossOrigin = "anonymous";
+ img.onload = t.step_func_done(() => {
+ draw_and_read_image(img, false);
+ });
+ img.onerror = t.unreached_func();
+ }, "Can get pixels of canvas with CORS enabled cross origin image drawn");
+
+ async_test(t => {
+ let img = new Image();
+ img.src = "http://{{hosts[][www]}}:{{ports[http][0]}}/images/green.png?pipe=header(Access-Control-Allow-Origin,*)";
+ img.onload = t.step_func_done(() => {
+ draw_and_read_image(img, true);
+ });
+ img.onerror = t.unreached_func();
+ }, "Can't get pixels of canvas with CORS enabled cross origin image drawn from non-CORS element");
+
+ async_test(t => {
+ let img = new Image();
+ img.src = "http://{{hosts[][www]}}:{{ports[http][0]}}/images/green.png";
+
+ img.onload = t.step_func_done(() => {
+ draw_and_read_image(img, true);
+ });
+ img.onerror = t.unreached_func();
+ }, "Can't get pixels of canvas with non-CORS enabled cross origin image drawn");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_html_image.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_html_image.html
new file mode 100644
index 0000000000..59a7d64465
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_html_image.html
@@ -0,0 +1,268 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<canvas id="dest" height="100" width="100"></canvas>
+
+<script>
+var sourceWidth = sourceHeight = 100;
+var destCanvasWidth = destCanvasHeight = 100;
+var redPixel = [255, 0, 0, 255];
+var lightPixel = [253, 140, 245, 255];
+var grayPixel = [41, 122, 115, 255];
+var transparentBlackPixel = [0, 0, 0, 0];
+
+var destCanvas = document.getElementById('dest');
+var sourceImg = document.createElement('img');
+sourceImg.src = '/html/canvas/resources/2x2.png';
+sourceImg.width = sourceWidth;
+sourceImg.height = sourceHeight;
+var destCtx = destCanvas.getContext('2d');
+destCtx.imageSmoothingEnabled = false;
+
+function checkPixel(location, expected) {
+ var actual = destCtx.getImageData(location[0], location[1], 1, 1).data;
+ assert_array_equals(actual, expected);
+}
+
+function PreparePixelTests(lightPixelCheck, grayPixelCheck, redCheck, testDescription) {
+ var pixelTests = [];
+ for (var i = 0; i < lightPixelCheck.length; i++) {
+ var message = testDescription + 'Pixel ' + lightPixelCheck[i][0] + ',' + lightPixelCheck[i][1] + ' should be light purple.';
+ pixelTests.push([message, lightPixelCheck[i], lightPixel]);
+ }
+ for (var i = 0; i < grayPixelCheck.length; i++) {
+ var message = testDescription + 'Pixel ' + grayPixelCheck[i][0] + ',' + grayPixelCheck[i][1] + ' should be gray.';
+ pixelTests.push([message, grayPixelCheck[i], grayPixel]);
+ }
+ for (var i = 0; i < redCheck.length; i++) {
+ var message = testDescription + 'Pixel ' + redCheck[i][0] + ',' + redCheck[i][1] + ' should be red.';
+ pixelTests.push([message, redCheck[i], redPixel]);
+ }
+ pixelTests.push([testDescription + 'Pixel outside canvas should be transparent black.\n', [100, 100], transparentBlackPixel]);
+ return pixelTests;
+}
+
+function drawCustomImageOnCanvas(sourceImage, sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription) {
+ destCtx.fillStyle = 'red';
+ destCtx.fillRect(0, 0, destCanvasWidth, destCanvasHeight);
+ if (typeof sourceRect.x !== 'undefined')
+ destCtx.drawImage(sourceImage, sourceRect.x, sourceRect.y, sourceRect.w, sourceRect.h,
+ destRect.x, destRect.y, destRect.w, destRect.h);
+ else if (typeof destRect.w !== 'undefined')
+ destCtx.drawImage(sourceImage, destRect.x, destRect.y, destRect.w, destRect.h);
+ else
+ destCtx.drawImage(sourceImage, destRect.x, destRect.y);
+ var pixelTests = PreparePixelTests(lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+ generate_tests(checkPixel, pixelTests);
+}
+
+function drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription) {
+ drawCustomImageOnCanvas(sourceImg, sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+var testDescription;
+var sourceRect = {}, destRect = {};
+var lightPixelCheck, grayPixelCheck, redCheck;
+
+// 2 arguments, the dest origin is 0,0
+// The source image will copied to the 0,0 position of the destination canvas
+function runDrawImageTest_dX0_dY0() {
+ testDescription = 'Test scenario 1: dx = 0, dy = 0 --- ';
+ destRect = {x: 0, y: 0};
+ lightPixelCheck = [[0,0], [0,99], [99,0], [99,99]];
+ grayPixelCheck = [];
+ redCheck = [];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// 2 arguments, the dest origin is not 0,0
+// The source canvas will copied to the 25,25 position of the destination canvas
+function runDrawImageTest_dX25_dY25() {
+ testDescription = 'Test scenario 2: dx = 25, dy = 25 --- ';
+ destRect = {x: 25, y: 25};
+ lightPixelCheck = [[25,25], [25,99], [99,25], [99,99]];
+ grayPixelCheck = [];
+ redCheck = [[0,0], [24,24], [0,25], [25,0], [0,99], [99,0]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// 4 arguments, the source origin is not 0,0, the dest size is provided
+// The source canvas will copied to the 50, 50 position of the destination canvas and
+// on an area of 50x50 pixels
+function runDrawImageTest_dX50_dY50_dW50_dH50() {
+ testDescription = 'Test scenario 3: dx = 50, dy = 50, dw = 50, dh = 50 --- ';
+ destRect = {x: 50, y: 50, w: 50, h: 50};
+ lightPixelCheck = [[50,50], [99,99]];
+ grayPixelCheck = [[50,99], [99,50]];
+ redCheck = [[0,0], [49,49], [0,50], [50,0], [0,99], [99,0]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// 4 arguments, the dest origin is not 0,0 and the dest size is provided but
+// does not match the size of the source. The image will be distorted
+// The source canvas will copied to the 50,50 position of the destination canvas
+// and it will be shrunk to a and area of 16x16
+function runDrawImageTest_dX50_dY50_dW16_dH16() {
+ testDescription = 'Test scenario 4: dx = 50, dy = 50, dw = 16, dh = 16 --- ';
+ destRect = {x: 50, y: 50, w: 16, h: 16};
+ lightPixelCheck = [[50,50], [65,65]];
+ grayPixelCheck = [[50,65], [65,50]];
+ redCheck = [[0,0], [49,49], [49,66], [66,49], [66,66], [99,99]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// The source canvas will copied to the 50,50 position of the destination canvas
+// over an area of 64x32 pixels
+// The copied image will be distorted along the x axis
+function runDrawImageTest_dX50_dY50_dW64_dH32() {
+ testDescription = 'Test scenario 5: dx = 50, dy = 50, dw = 64, dh = 32 --- ';
+ destRect = {x: 50, y: 50, w: 64, h: 32};
+ lightPixelCheck = [[50,50], [99,81]];
+ grayPixelCheck = [[50,81], [99,50]];
+ redCheck = [[0,0], [49,49], [49,82], [99,49], [99,82], [99,99]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// 8 arguments, both destination and source origins are 0, 0
+// An area of 32x32 pixels of the source image will be copied to
+// an area of 32x32 pixels of the destination canvas
+function runDrawImageTest_sX0_sY0_sW32_sH32_dX0_dY0_dW32_dH32() {
+ testDescription = 'Test scenario 6: sx = 0, sy = 0, sw = 32, sh = 32, dx = 0, dy = 0, dw = 32, dh = 32 --- ';
+ sourceRect = {x: 0, y: 0, w: 32, h: 32};
+ destRect = {x: 0, y: 0, w: 32, h: 32};
+ lightPixelCheck = [[0,0], [0,31], [31,0], [31,31]];
+ grayPixelCheck = [];
+ redCheck = [[0,32], [32,0], [32,32], [99,99]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// 8 arguments the destination origin is not 0,0
+// An area of 32x32 pixels of the source image will be copied to
+// an area of 32x32 pixels of the destination canvas in the position 32,32
+function runDrawImageTest_sX0_sY0_sW32_sH32_dX32_dY32_dW32_dH32() {
+ testDescription = 'Test scenario 7: sx = 0, sy = 0, sw = 32, sh = 32, dx = 32, dy = 32, dw = 32, dh = 32 --- ';
+ sourceRect = {x: 0, y: 0, w: 32, h: 32};
+ destRect = {x: 32, y: 32, w: 32, h: 32};
+ lightPixelCheck = [[32,32], [32,63], [63,32], [63,63]];
+ grayPixelCheck = [];
+ redCheck = [[0,0], [31,31], [31,64], [64,31], [64,64], [99,99]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// The source rectangle overflows the source image
+// The source area is clipped to fit the source image
+// and the destination are is clipped in the same proportion
+function runDrawImageTest_sX32_sY32_sW32_sH32_dX0_dY0_dW32_dH32() {
+ testDescription = 'Test scenario 8: sx = 32, sy = 32, sw = 32, sh = 32, dx = 0, dy = 0, dw = 32, dh = 32 --- ';
+ sourceRect = {x: 32, y: 32, w: 32, h: 32};
+ destRect = {x: 0, y: 0, w: 32, h: 32};
+ lightPixelCheck = [[0,0], [0,31], [31,0], [31,31]];
+ grayPixelCheck = [];
+ redCheck = [[0,32], [32,0], [32,32], [99,99]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// The destination rectangle has negative width and height. When the source
+// rectangle is outside the source image, the source rectangle must be clipped
+// to the source image and the destination rectangle must be clipped in the same
+// proportion.
+function runDrawImageTest_sX0_sY0_sW32_sH32_dX32_dY32_dWm32_dHm32() {
+ testDescription = 'Test scenario 9: sx = 32, sy = 32, sw = 32, sh = 32, dx = 32, dy = 32, dw = -32, dh = -32 --- ';
+ sourceRect = {x: 32, y: 32, w: 32, h: 32};
+ destRect = {x: 0, y: 0, w: 32, h: 32};
+ lightPixelCheck = [[0,0], [0,31], [31,0], [31,31]];
+ grayPixelCheck = [];
+ redCheck = [[0,32], [32,0], [32,32], [99,99]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// The destination rectangle is larger than the destination canvas.
+// When the destination rectangle is outside the destination image (the scratch bitmap),
+// the pixels that land outside the scratch bitmap are discarded,
+// as if the destination was an infinite canvas
+// whose rendering was clipped to the dimensions of the scratch bitmap.
+function runDrawImageTest_sX0_sY0_sW512_sH512_dX0_dY0_dW256_dH256() {
+ testDescription = 'Test scenario 10: sx = 0, sy = 0, sw = 512, sh = 512, dx = 0, dy = 0, dw = 256, dh = 256 --- ';
+ sourceRect = {x: 0, y: 0, w: 512, h: 512};
+ destRect = {x: 0, y: 0, w: 256, h: 256};
+ lightPixelCheck = [[0,0], [0,99], [99,0], [99,99]];
+ grayPixelCheck = [];
+ redCheck = [];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// The source rectangle is larger than the source canvas
+// The source area is clipped to fit the source image
+// and the destination area is clipped in the same proportion
+function runDrawImageTest_sX0_sY0_sW2048_sH2048_dX0_dY0_dW800_dH800() {
+ testDescription = 'Test scenario 11: sx = 0, sy = 0, sw = 2048, sh = 2048, dx = 0, dy = 0, dw = 800, dh = 800 --- ';
+ sourceRect = {x: 0, y: 0, w: 2048, h: 2048};
+ destRect = {x: 0, y: 0, w: 800, h: 800};
+ lightPixelCheck = [[0,0], [0,99], [99,0], [99,99]];
+ grayPixelCheck = [];
+ redCheck = [];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// Negative coordinates of the source rectangle
+// The source area is clipped to fit the source image
+// and the destination area is clipped in the same proportion
+function runDrawImageTest_sXm20_sYm20_sW50_sH50_dX20_dY20_dW125_dH125() {
+ testDescription = 'Test scenario 12: sx = -20, sy = -20, sw = 50, sh = 50, dx = 20, dy = 20, dw = 125, dh = 125 --- ';
+ sourceRect = {x: -20, y: -20, w: 50, h: 50};
+ destRect = {x: 20, y: 20, w: 125, h: 125};
+ lightPixelCheck = [[70,70], [70,99], [99,70], [99,99]];
+ grayPixelCheck = [];
+ redCheck = [[0,0], [0,99], [99,0], [69,69], [69, 99], [99,69]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// The source Image doesn't have a src url defined.
+// No exception is thrown and nothing is drawn.
+function runDrawImageTestImageWithuotSource() {
+ testDescription = 'Test scenario 13: draw an image element that does not have a source --- ';
+ var sourceImage = document.createElement('img');
+ sourceRect = {x: 0, y: 0, w: 50, h: 50};
+ destRect = {x: 0, y: 0, w: 100, h: 100};
+ lightPixelCheck = [];
+ grayPixelCheck = [];
+ redCheck = [[0,0], [0,99], [99,0], [99,69]];
+ drawCustomImageOnCanvas(sourceImage, sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+// Clipping the source and down scaling to the destination
+function runDrawImageTest_sX64_sY64_sW384_sH384_dX0_dY0_dW32_dH64() {
+ testDescription = 'Test scenario 14: sx = 64, sy = 64, sw = 384, sh = 384, dx = 0, dy = 0, dw = 32, dh = 64 --- ';
+ sourceRect = {x: 64, y: 64, w: 384, h: 384};
+ destRect = {x: 0, y: 0, w: 32, h: 64};
+ lightPixelCheck = [[0,0], [15,31], [17,33], [31,63]];
+ grayPixelCheck = [[16,0], [31,31], [0, 33], [15,63]];
+ redCheck = [[0,64], [32,0], [32,64], [99,99]];
+ drawImageOnCanvas(sourceRect, destRect, lightPixelCheck, grayPixelCheck, redCheck, testDescription);
+}
+
+
+function runDrawImageTests() {
+ runDrawImageTest_dX0_dY0();
+ runDrawImageTest_dX25_dY25();
+ runDrawImageTest_dX50_dY50_dW50_dH50();
+ runDrawImageTest_dX50_dY50_dW16_dH16();
+ runDrawImageTest_dX50_dY50_dW64_dH32();
+ runDrawImageTest_sX0_sY0_sW32_sH32_dX0_dY0_dW32_dH32();
+ runDrawImageTest_sX0_sY0_sW32_sH32_dX32_dY32_dW32_dH32();
+ runDrawImageTest_sX32_sY32_sW32_sH32_dX0_dY0_dW32_dH32();
+ runDrawImageTest_sX0_sY0_sW32_sH32_dX32_dY32_dWm32_dHm32();
+ runDrawImageTest_sX0_sY0_sW512_sH512_dX0_dY0_dW256_dH256();
+ runDrawImageTest_sX0_sY0_sW2048_sH2048_dX0_dY0_dW800_dH800();
+ runDrawImageTest_sXm20_sYm20_sW50_sH50_dX20_dY20_dW125_dH125();
+ runDrawImageTestImageWithuotSource();
+ runDrawImageTest_sX64_sY64_sW384_sH384_dX0_dY0_dW32_dH64();
+}
+
+async_test(t => {
+ window.onload = function() {
+ t.step(runDrawImageTests);
+ t.done();
+ }
+}, 'Draw 100x100 image to 100x100 canvas at 0,0.');
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_1.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_1.html
new file mode 100644
index 0000000000..ea3300bef2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_1.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Load a 100x100 image to a SVG image and draw it to a 100x100 canvas.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<div id="log"></div>
+<canvas id="dest" height="100" width="100"></canvas>
+<script>
+async_test(t => {
+ var sourceImg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ sourceImg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '/html/canvas/resources/2x2.png');
+ sourceImg.width = 100;
+ sourceImg.height = 100;
+
+ window.onload = t.step_func_done(() => {
+ var destCanvas = document.getElementById('dest');
+ var destCtx = destCanvas.getContext('2d');
+ destCtx.fillStyle = "#FF0000";
+ destCtx.fillRect(0, 0, destCanvas.width, destCanvas.height);
+ destCtx.imageSmoothingEnabled = false;
+ // 2 arguments, the dest origin is 0,0
+ // The source canvas will copied to the 0,0 position of the destination canvas
+ destCtx.drawImage(sourceImg, 0, 0);
+ _assertPixel(destCanvas, 0, 0, 253, 140, 245, 255);
+ _assertPixel(destCanvas, 0, 99, 253, 140, 245, 255);
+ _assertPixel(destCanvas, 99, 0, 253, 140, 245, 255);
+ _assertPixel(destCanvas, 99, 99, 253, 140, 245, 255);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_with_foreign_object_does_not_taint.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_with_foreign_object_does_not_taint.html
new file mode 100644
index 0000000000..f29b2bf5a8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/drawimage_svg_image_with_foreign_object_does_not_taint.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Draw an SVG image with a foreignObject to a canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function loadImage(url) {
+ return new Promise(resolve => {
+ const image = new window.Image();
+ image.onload = () => {
+ resolve(image);
+ };
+ image.src = url;
+ });
+}
+
+promise_test(async (t) => {
+ // Load a data URL for an SVG image with a foreign object.
+ const url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject></foreignObject></svg>';
+ const image = await loadImage(url);
+
+ // Draw the image to a canvas.
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ canvas.width = image.width;
+ canvas.height = image.height;
+ context.drawImage(image, 0, 0);
+
+ // The canvas should not be tainted, so the following shouldn't throw.
+ assert_true(canvas.toDataURL().length > 0);
+}, 'Canvas should not be tainted after drawing SVG including <foreignObject>');
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none-ref.html
new file mode 100644
index 0000000000..320a9b8108
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference for drawImage from a createImageBitmap source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <img id="img-element" style="image-orientation: none;" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none.tentative.html
new file mode 100644
index 0000000000..39ed4a25dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-orientation-none.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-bitmap-orientation-none-ref.html">
+ <script>
+ image = new Image();
+ image.src = "/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg";
+
+ let imageLoadPromise = new Promise(resolve => {
+ image.onload = resolve;
+ });
+ let contentLoadedPromise = new Promise(resolve => {
+ window.addEventListener('DOMContentLoaded', resolve);
+ });
+ Promise.all([imageLoadPromise, contentLoadedPromise]).then( function() {
+ const can = document.getElementById('bitmap-canvas');
+ can.height = 50;
+ can.width = 100;
+ can.getContext('2d').drawImage(image, 0, 0);
+ })
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <canvas id="bitmap-canvas" style="image-orientation: none;"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-ref.html
new file mode 100644
index 0000000000..261d6a0b7c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference for drawImage from a createImageBitmap source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none-ref.html
new file mode 100644
index 0000000000..3a78aad068
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference for drawImage from a createImageBitmap source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <img id="img-element" style="image-orientation: none;" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none.tentative.html
new file mode 100644
index 0000000000..3b16241c97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-orientation-none.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-bitmap-swap-width-height-orientation-none-ref.html">
+ <script>
+ image = new Image();
+ image.src = "/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg";
+
+ let imageLoadPromise = new Promise(resolve => {
+ image.onload = resolve;
+ });
+ let contentLoadedPromise = new Promise(resolve => {
+ window.addEventListener('DOMContentLoaded', resolve);
+ });
+ Promise.all([imageLoadPromise, contentLoadedPromise]).then( async function() {
+ const bitmap = await createImageBitmap(image);
+ const can = document.getElementById('bitmap-canvas');
+ can.height = 50;
+ can.width = 100;
+ can.getContext('2d').drawImage(bitmap, 0, 0);
+ })
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <canvas id="bitmap-canvas" style="image-orientation: none;"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-ref.html
new file mode 100644
index 0000000000..247d7f4049
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference for drawImage from a createImageBitmap source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height.tentative.html
new file mode 100644
index 0000000000..744ca54f47
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap-swap-width-height.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-bitmap-swap-width-height-ref.html">
+ <script>
+ image = new Image();
+ image.src = "/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg";
+
+ let imageLoadPromise = new Promise(resolve => {
+ image.onload = resolve;
+ });
+ let contentLoadedPromise = new Promise(resolve => {
+ window.addEventListener('DOMContentLoaded', resolve);
+ });
+ Promise.all([imageLoadPromise, contentLoadedPromise]).then( async function() {
+ const bitmap = await createImageBitmap(image);
+ const can = document.getElementById('bitmap-canvas');
+ can.height = 100;
+ can.width = 50;
+ can.getContext('2d').drawImage(bitmap, 0, 0);
+ })
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <canvas id="bitmap-canvas"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap.tentative.html
new file mode 100644
index 0000000000..632a170b88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-bitmap.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-bitmap-ref.html">
+ <script>
+ image = new Image();
+ image.src = "/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg";
+
+ let imageLoadPromise = new Promise(resolve => {
+ image.onload = resolve;
+ });
+ let contentLoadedPromise = new Promise(resolve => {
+ window.addEventListener('DOMContentLoaded', resolve);
+ });
+ Promise.all([imageLoadPromise, contentLoadedPromise]).then( function() {
+ const can = document.getElementById('bitmap-canvas');
+ can.height = 50;
+ can.width = 100;
+ can.getContext('2d').drawImage(image, 0, 0);
+ })
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <canvas id="bitmap-canvas"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob-ref.html
new file mode 100644
index 0000000000..2bd6037835
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from a blob with image orientation: from-image, reference</title>
+</head>
+<body>
+ <img id="img-element" style="width: 150px; height: 300px;" src="/css/css-images/image-orientation/support/exif-orientation-8-llo.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob.tentative.html
new file mode 100644
index 0000000000..330b3cbfe5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-blob.tentative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from a blob with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-blob-ref.html">
+ <script>
+ function makeBlob() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/css/css-images/image-orientation/support/exif-orientation-8-llo.jpg');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ }
+
+ window.onload = function() {
+ var cfb = document.getElementById("canvasWithFileBitmap");
+ makeBlob().then(function(blob){createImageBitmap(blob).then(bitmap => {
+ cfb.getContext("2d").drawImage(bitmap, 0, 0, 150, 150 * bitmap.height / bitmap.width);
+ window.requestAnimationFrame(() => {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+ });
+ }
+</script>
+</head>
+<body>
+ <canvas id="canvasWithFileBitmap" width="150" height="300"></canvas>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none-ref.html
new file mode 100644
index 0000000000..b847b9eb73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference for drawImage from an element source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <img id="img-element" style="image-orientation: none;" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none.tentative.html
new file mode 100644
index 0000000000..61563da738
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-orientation-none.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-element-orientation-none-ref.html">
+ <script>
+ window.onload = () => {
+ const img = document.getElementById('img-element');
+
+ const can = document.getElementById('bitmap-canvas');
+ can.height = img.height;
+ can.width = img.width;
+ can.getContext('2d').drawImage(img, 0, 0);
+ };
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <canvas id="bitmap-canvas" style="image-orientation: none;"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-ref.html
new file mode 100644
index 0000000000..3f4d1e5ff4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference for drawImage from an element source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none-ref.html
new file mode 100644
index 0000000000..b26154b40a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference drawImage from an element source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <img id="img-element" style="image-orientation: none;" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none.tentative.html
new file mode 100644
index 0000000000..290d7acf3a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-orientation-none.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: none</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-element-swap-width-height-orientation-none-ref.html">
+ <script>
+ window.onload = () => {
+ const img = document.getElementById('img-element');
+
+ const can = document.getElementById('bitmap-canvas');
+ can.height = 50;
+ can.width = 100;
+ can.getContext('2d').drawImage(img, 0, 0);
+ };
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <canvas id="bitmap-canvas" style="image-orientation: none;"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-ref.html
new file mode 100644
index 0000000000..21f0f88b88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference drawImage from an element source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height.tentative.html
new file mode 100644
index 0000000000..20d59358b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element-swap-width-height.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-element-swap-width-height-ref.html">
+ <script>
+ window.onload = () => {
+ const img = document.getElementById('img-element');
+
+ const can = document.getElementById('bitmap-canvas');
+ can.height = 100;
+ can.width = 50;
+ can.getContext('2d').drawImage(img, 0, 0);
+ };
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-7-rl.jpg">
+ <canvas id="bitmap-canvas"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element.tentative.html
new file mode 100644
index 0000000000..62cdf20c79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-from-element.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>createImageBitmap and drawImage from an element source with image orientation: from-image</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-from-element-ref.html">
+ <script>
+ window.onload = () => {
+ const img = document.getElementById('img-element');
+
+ const can = document.getElementById('bitmap-canvas');
+ can.height = img.height;
+ can.width = img.width;
+ can.getContext('2d').drawImage(img, 0, 0);
+ };
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <canvas id="bitmap-canvas"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect-ref.html
new file mode 100644
index 0000000000..19ffcc39c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>reference for drawImage with image orientation: from-image and a sub-image source rect</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+ <script>
+ window.onload = () => {
+ const img = document.getElementById('img-element');
+
+ const can = document.getElementById('bitmap-canvas');
+ can.height = img.height;
+ can.width = img.width;
+ can.getContext('2d').drawImage(img, 40, 20, 50, 25, 0, 0, can.width, can.height);
+ };
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr-pre-rotated.jpg">
+ <canvas id="bitmap-canvas"></canvas>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect.tentative.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect.tentative.html
new file mode 100644
index 0000000000..d889e39302
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-images-to-the-canvas/image-orientation/drawImage-with-src-rect.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>drawImage with image orientation: from-image and a sub-image source rect</title>
+<link rel="author" title="Stephen Chenney" href="mailto:schenney@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-3/#propdef-image-orientation">
+<link rel="match" href="drawImage-with-src-rect-ref.html">
+<meta name=fuzzy content="0-3;0-200">
+ <script>
+ window.onload = () => {
+ const img = document.getElementById('img-element');
+
+ const can = document.getElementById('bitmap-canvas');
+ can.height = img.height;
+ can.width = img.width;
+ can.getContext('2d').drawImage(img, 40, 20, 50, 25, 0, 0, can.width, can.height);
+ };
+ </script>
+</head>
+<body>
+ <img id="img-element" src="/css/css-images/image-orientation/support/exif-orientation-3-lr.jpg">
+ <canvas id="bitmap-canvas"></canvas>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_complexshapes_ispointInpath_001.htm b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_complexshapes_ispointInpath_001.htm
new file mode 100644
index 0000000000..18c3c9afb9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_complexshapes_ispointInpath_001.htm
@@ -0,0 +1,31 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: isPointInPath() unaffected by the current transformation matrix</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath" />
+ <meta name="assert" content="isPointInPath must check the point (x, y) as coordinates unaffected by the current transformation matrix." />
+ <script type="text/javascript">
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done(function runTest() {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Create a path that is transformed by a translation transformation matrix.
+ ctx.translate(100, 50);
+ ctx.rect(0, 0, 100, 50);
+
+ // Ensure that the coordinates passed to isPointInPath are unaffected by the current transformation matrix.
+ assert_true(ctx.isPointInPath(125, 75), "isPointInPath(125, 75)");
+ assert_false(ctx.isPointInPath(25, 25), "!isPointInPath(25, 25)");
+ }));
+ }, "isPointInPath unaffected by transformation matrix");
+ </script>
+ </head>
+ <body>
+ <p>Description: isPointInPath must check the point (x, y) as coordinates unaffected by the current transformation matrix.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_focus_drawFocusIfNeeded_AAPI_001-manual.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_focus_drawFocusIfNeeded_AAPI_001-manual.html
new file mode 100644
index 0000000000..bf38fa68b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/canvas_focus_drawFocusIfNeeded_AAPI_001-manual.html
@@ -0,0 +1,50 @@
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>drawFocusIfNeeded() - AAPI test</title>
+ <link rel="author" title="Mark Sadecki" href="mark@w3.org">
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-drawFocusIfNeeded">
+
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This manual test can be used to verify that drawFocusIfNeeded actually updates the accessible location information (i.e. UIAutomation's CurrentBoundingRectangle) in the Accessibility API. To perform this test, you will need an <a href="http://www.w3.org/WAI/PF/wiki/ARIA_Test_Plan#Test_tools">accessibility API inspector</a>. To perform this test, use the <code>tab</code> key to move from the first focusable element to through to the fourth. This test passes if the first parameter of the bounding rectangle increases by 100 when focus is moved from the gray square to the orange square.</p>
+ <p><a href="http://www.w3.org">First focusable element</a></p>
+ <canvas height=100 width=200 id=canvas>
+ <a href='http://www.w3.org' id='button1'>Second focusable element</a>
+ <a href='http://www.w3.org' id='button2'>Third focusable element</a>
+ </canvas>
+ <p><a href="http://www.w3.org">Fourth focusable element</a></p>
+
+ <script>
+ var button1 = document.getElementById('button1');
+ var button2 = document.getElementById('button2');
+ var canvas = document.getElementById('canvas');
+ var buttons = canvas.getElementsByTagName('a');
+
+ for (var i = 0; i < buttons.length; i++) {
+ buttons[i].addEventListener('focus', drawCanvas, false);
+ buttons[i].addEventListener('blur', drawCanvas, false);
+ }
+
+ function drawRect(context, color, element) {
+ context.beginPath();
+ context.rect(10, 10, 80, 80);
+ context.fillStyle = color;
+ context.fill();
+ context.drawFocusIfNeeded(element);
+ }
+
+ function drawCanvas() {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ context.clearRect (0, 0, 200, 100);
+
+ drawRect(context, "gray", button1);
+ context.translate(100,0);
+ drawRect(context, "orange", button2);
+ }
+ drawCanvas();
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html
new file mode 100644
index 0000000000..6daf32a2af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>drawFocusIfNeeded()</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="W3C">
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-drawFocusIfNeeded">
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test uses drawFocusIfNeeded to draw a focus ring in the canvas, then compare the ImageData before and after the invocation of the method to check that the focus ring was actually drawn.</p>
+ <div>
+ <p>Before:</p>
+ <canvas height=100 width=100 id='original'>
+ </canvas>
+ <p>After:</p>
+ <canvas height=100 width=100 id=canvas>
+ <label><a href='http://www.w3.org' id='element'>Focus</a></label>
+ </canvas>
+ </div>
+ <script>
+ test(function() {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ var element = document.getElementById('element');
+ element.focus();
+ context.fillStyle = 'white';
+ context.fillRect(0, 0, 100, 100);
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.rect(10, 10, 80, 80);
+ context.stroke();
+ context.save();
+ var refImage = context.getImageData(0, 0, 100, 100);
+
+ assert_equals(40000, refImage.data.length, "ImageData.data.length is 40000");
+
+ var original = document.getElementById('original');
+ var o_context = original.getContext('2d');
+ o_context.fillStyle = 'white';
+ o_context.fillRect(0, 0, 100, 100);
+ o_context.putImageData(refImage, 0, 0, 0, 0, 100, 100);
+
+ context.rect(5, 5, 90, 90);
+ context.drawFocusIfNeeded(element);
+
+ var ringImage = context.getImageData(0, 0, 100, 100);
+ assert_equals(40000, ringImage.data.length, "ImageData.data.length is 40000");
+
+ // make sure refImage and ringImage are different
+ var length = 40000;
+ var index = length;
+ var identical = true;
+ while (--index > 0) {
+ if (refImage.data[index] != ringImage.data[index]) identical = false;
+ }
+
+ assert_false(identical, "The focus ring must appear in the canvas");
+
+
+ }, 'drawFocusIfNeeded draws a focus ring.');
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_002.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_002.html
new file mode 100644
index 0000000000..ec0a4ef427
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_002.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>drawFocusIfNeeded()</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="W3C">
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-drawFocusIfNeeded">
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test uses drawFocusIfNeeded does nothing if the element is not in focus but comparing ImageData from before and after.</p>
+ <div>
+ <p>Before:</p>
+ <canvas height=100 width=100 id='original'>
+ </canvas>
+ <p>After:</p>
+ <canvas height=100 width=100 id=canvas>
+ <label><a href='http://www.w3.org' id='element'>Focus</a></label>
+ </canvas>
+ </div>
+ <script>
+ test(function() {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ var element = document.getElementById('element');
+ context.fillStyle = 'white';
+ context.fillRect(0, 0, 100, 100);
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.rect(10, 10, 80, 80);
+ context.stroke();
+ context.save();
+ var refImage = context.getImageData(0, 0, 100, 100);
+
+ assert_equals(40000, refImage.data.length, "ImageData.data.length is 40000");
+
+ var original = document.getElementById('original');
+ var o_context = original.getContext('2d');
+ o_context.fillStyle = 'white';
+ o_context.fillRect(0, 0, 100, 100);
+ o_context.putImageData(refImage, 0, 0, 0, 0, 100, 100);
+
+
+
+ context.strokeStyle = 'blue';
+ context.rect(5, 5, 90, 90);
+ context.drawFocusIfNeeded(element);
+
+ var ringImage = context.getImageData(0, 0, 100, 100);
+ assert_equals(40000, ringImage.data.length, "ImageData.data.length is 40000");
+
+ // make sure refImage and ringImage are different
+ var length = 40000;
+ var index = length;
+ var identical = true;
+ while (--index > 0) {
+ if (refImage.data[index] != ringImage.data[index]) identical = false;
+ }
+
+ assert_true(identical, "No focus ring appears in the canvas");
+
+
+ }, 'drawFocusIfNeeded does not draw a focus ring if the element is not in focus.');
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_003.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_003.html
new file mode 100644
index 0000000000..b62c0641f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_003.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>drawFocusIfNeeded()</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="W3C">
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-drawFocusIfNeeded">
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test uses drawFocusIfNeeded does nothing if the element is not a descendant but comparing ImageData from before and after.</p>
+ <div>
+ <p>Before:</p>
+ <canvas height=100 width=100 id='original'>
+ <label><a href='http://www.w3.org' id='element'>Focus</a></label>
+ </canvas>
+ <p>After:</p>
+ <canvas height=100 width=100 id=canvas>
+ </canvas>
+ </div>
+ <script>
+ test(function() {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ var element = document.getElementById('element');
+ element.focus();
+ context.fillStyle = 'white';
+ context.fillRect(0, 0, 100, 100);
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.rect(10, 10, 80, 80);
+ context.stroke();
+ context.save();
+ var refImage = context.getImageData(0, 0, 100, 100);
+
+ assert_equals(40000, refImage.data.length, "ImageData.data.length is 40000");
+
+ var original = document.getElementById('original');
+ var o_context = original.getContext('2d');
+ o_context.fillStyle = 'white';
+ o_context.fillRect(0, 0, 100, 100);
+ o_context.putImageData(refImage, 0, 0, 0, 0, 100, 100);
+
+
+
+ context.strokeStyle = 'blue';
+ context.rect(5, 5, 90, 90);
+ context.drawFocusIfNeeded(element);
+
+ var ringImage = context.getImageData(0, 0, 100, 100);
+ assert_equals(40000, ringImage.data.length, "ImageData.data.length is 40000");
+
+ // make sure refImage and ringImage are different
+ var length = 40000;
+ var index = length;
+ var identical = true;
+ while (--index > 0) {
+ if (refImage.data[index] != ringImage.data[index]) identical = false;
+ }
+
+ assert_true(identical, "No focus ring appears in the canvas");
+
+
+ }, 'drawFocusIfNeeded does not draw a focus ring if the element is not a descendant of the context.');
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html
new file mode 100644
index 0000000000..326db0daa8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>drawFocusIfNeeded()</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="W3C">
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-drawFocusIfNeeded">
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test uses drawFocusIfNeeded to draw a complex path focus then compare ImageData from before and after.</p>
+ <div>
+ <p>Before:</p>
+ <canvas height=100 width=100 id='original'>
+ </canvas>
+ <p>After:</p>
+ <canvas height=100 width=100 id=canvas>
+ <label><a href='http://www.w3.org' id='element'>Focus</a></label>
+ </canvas>
+ </div>
+ <script>
+ test(function() {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ var element = document.getElementById('element');
+ element.focus();
+ context.fillStyle = 'white';
+ context.fillRect(0, 0, 100, 100);
+ context.beginPath();
+ context.strokeStyle = 'black';
+
+ context.moveTo(10, 40);
+ context.lineTo(50, 10);
+ context.lineTo(90, 40);
+ context.lineTo(70, 40);
+ context.lineTo(70, 90);
+ context.lineTo(30, 90);
+ context.lineTo(30, 40);
+ context.closePath();
+
+ context.stroke();
+ context.save();
+ var refImage = context.getImageData(0, 0, 100, 100);
+
+ assert_equals(40000, refImage.data.length, "ImageData.data.length is 40000");
+
+ var original = document.getElementById('original');
+ var o_context = original.getContext('2d');
+ o_context.fillStyle = 'white';
+ o_context.fillRect(0, 0, 100, 100);
+ o_context.putImageData(refImage, 0, 0, 0, 0, 100, 100);
+
+
+
+ context.beginPath();
+ context.moveTo(5, 40);
+ context.lineTo(50, 5);
+ context.lineTo(95, 40);
+ context.lineTo(95, 45);
+ context.lineTo(75, 45);
+ context.lineTo(75, 95);
+ context.lineTo(25, 95);
+ context.lineTo(25, 45);
+ context.lineTo(5, 45);
+ context.closePath();
+
+ context.drawFocusIfNeeded(element);
+
+ var ringImage = context.getImageData(0, 0, 100, 100);
+ assert_equals(40000, ringImage.data.length, "ImageData.data.length is 40000");
+
+ // make sure refImage and ringImage are different
+ var length = 40000;
+ var index = length;
+ var identical = true;
+ while (--index > 0) {
+ if (refImage.data[index] != ringImage.data[index]) identical = false;
+ }
+
+ assert_false(identical, "A focus ring appears in the canvas");
+
+
+ }, 'drawFocusIfNeeded does draw a focus ring if the element is in focus.');
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html
new file mode 100644
index 0000000000..96a4e3fd5d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>drawFocusIfNeeded()</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="W3C">
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-drawFocusIfNeeded">
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test uses drawFocusIfNeeded to draw a complex path focus then compare ImageData from before and after.</p>
+ <div>
+ <p>Before:</p>
+ <canvas height=100 width=100 id='original'>
+ </canvas>
+ <p>After:</p>
+ <canvas height=100 width=100 id=canvas>
+ <p id='element' tabindex='1'>This is text.</p>
+ </canvas>
+ </div>
+ <script>
+ test(function() {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ var element = document.getElementById('element');
+ element.focus();
+ context.fillStyle = 'white';
+ context.fillRect(0, 0, 100, 100);
+ context.beginPath();
+ context.strokeStyle = 'black';
+
+ context.moveTo(10, 40);
+ context.lineTo(50, 10);
+ context.lineTo(90, 40);
+ context.lineTo(70, 40);
+ context.lineTo(70, 90);
+ context.lineTo(30, 90);
+ context.lineTo(30, 40);
+ context.closePath();
+
+ context.stroke();
+ context.save();
+ var refImage = context.getImageData(0, 0, 100, 100);
+
+ assert_equals(40000, refImage.data.length, "ImageData.data.length is 40000");
+
+ var original = document.getElementById('original');
+ var o_context = original.getContext('2d');
+ o_context.fillStyle = 'white';
+ o_context.fillRect(0, 0, 100, 100);
+ o_context.putImageData(refImage, 0, 0, 0, 0, 100, 100);
+
+
+
+ context.beginPath();
+ context.moveTo(5, 40);
+ context.lineTo(50, 5);
+ context.lineTo(95, 40);
+ context.lineTo(95, 45);
+ context.lineTo(75, 45);
+ context.lineTo(75, 95);
+ context.lineTo(25, 95);
+ context.lineTo(25, 45);
+ context.lineTo(5, 45);
+ context.closePath();
+
+ context.drawFocusIfNeeded(element);
+
+ var ringImage = context.getImageData(0, 0, 100, 100);
+ assert_equals(40000, ringImage.data.length, "ImageData.data.length is 40000");
+
+ // make sure refImage and ringImage are different
+ var length = 40000;
+ var index = length;
+ var identical = true;
+ while (--index > 0) {
+ if (refImage.data[index] != ringImage.data[index]) identical = false;
+ }
+
+ assert_false(identical, "A focus ring appears in the canvas");
+
+
+ }, 'drawFocusIfNeeded does draw a focus ring if the element is in focus and the user activated a particular focus ring.');
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math-ref.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math-ref.html
new file mode 100644
index 0000000000..6c5dab9f41
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>font-size: math treated as medium in disconnected canvas (reference)</title>
+<body>
+</body>
+<script>
+var d = new Document();
+var c = d.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+var ctx = c.getContext("2d");
+ctx.font = `medium serif`;
+ctx.fillText("Hello World!", 5, c.height / 2);
+c.toBlob((blob) => {
+ var img = document.createElement("img");
+ const url = URL.createObjectURL(blob);
+ img.src = url;
+ img.style.border = "3px solid cyan";
+ document.body.appendChild(img);
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math.html b/testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math.html
new file mode 100644
index 0000000000..2cecff68f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>font-size: math treated as medium in disconnected canvas</title>
+<link rel="match" href="canvas.2d.disconnected-font-size-math-ref.html">
+<body>
+</body>
+<script>
+var d = new Document();
+var c = d.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+var ctx = c.getContext("2d");
+ctx.font = `math serif`;
+ctx.fillText("Hello World!", 5, c.height / 2);
+c.toBlob((blob) => {
+ var img = document.createElement("img");
+ const url = URL.createObjectURL(blob);
+ img.src = url;
+ img.style.border = "3px solid cyan";
+ document.body.appendChild(img);
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/2d.fillStyle.parse.current.notrendered.html b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/2d.fillStyle.parse.current.notrendered.html
new file mode 100644
index 0000000000..37701bacee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/2d.fillStyle.parse.current.notrendered.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.fillStyle.parse.current.notrendered</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.parse.current.basic</h1>
+<p class="desc">currentColor is computed from the canvas element</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("currentColor is computed from the canvas element even when element is not rendered");
+_addTest(function(canvas, ctx) {
+
+canvas.setAttribute('style', 'color: #0f0;');
+canvas.style.display = 'none';
+canvas.offsetTop;
+ctx.fillStyle = 'currentColor';
+canvas.style.display = 'inline';
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 0,255,0,255);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/canvas_colorsandstyles_createlineargradient_001.htm b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/canvas_colorsandstyles_createlineargradient_001.htm
new file mode 100644
index 0000000000..5b77c98de7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/canvas_colorsandstyles_createlineargradient_001.htm
@@ -0,0 +1,59 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: createlinearGradient() with two points same</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-createlineargradient" />
+ <meta name="assert" content="If the two points in a linear gradient have identical x,y coordinates, the canvas must paint nothing." />
+ <script type="text/javascript">
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done(function runTest() {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+ var width = canvas.width;
+ var height = canvas.height;
+
+ // Start by drawing a left to right, green-to-blue linear gradient.
+ var lingrad = ctx.createLinearGradient(0, 50, 100, 50);
+ lingrad.addColorStop(0, "rgba(0, 255, 0, 1.0)");
+ lingrad.addColorStop(1, "rgba(0, 0, 255, 1.0)");
+ ctx.fillStyle = lingrad;
+ ctx.fillRect(0, 0, 100, 50);
+
+ // Get the current state of the canvas
+ var initial = ctx.getImageData(0, 0, width, height);
+
+ // Nothing must be drawn if the two points in the linear gradient are the same.
+ lingrad = ctx.createLinearGradient(100, 100, 100, 100);
+ lingrad.addColorStop(0, "rgba(255, 0, 0, 1.0)");
+ lingrad.addColorStop(1, "rgba(255, 0, 0, 1.0)");
+ ctx.fillStyle = lingrad;
+ ctx.fillRect(0, 0, 300, 150);
+
+ // Check that nothing is drawn.
+ var after = ctx.getImageData(0, 0, width, height);
+
+ // Asserts
+ assert_equals(initial.width, after.width, "widths are equal");
+ assert_equals(initial.height, after.height, "heights are equal");
+ assert_array_equals(initial.data, after.data, "data are equal");
+
+ for (var i = 0; i < after.data.length; i += 4) {
+ var r = after.data[i];
+ var g = after.data[i+1];
+ var b = after.data[i+2];
+ var a = after.data[i+3];
+ assert_false(r == 0xFF && g == 0 && b == 0 && a == 0xFF, "no red");
+ }
+ }));
+ }, "linear gradient from point to self draws nothing");
+ </script>
+ </head>
+ <body>
+ <p>Description: If the two points in a linear gradient have identical x,y coordinates, the canvas must paint nothing.</p>
+ <p>Test passes if there is one left-to-right, green-to-blue linear gradient seen on the page and no red is seen on the page.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-expected.html b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-expected.html
new file mode 100644
index 0000000000..f347abc9d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-expected.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <style type="text/css">
+ div {
+ width: 300px;
+ height: 150px;
+ background: conic-gradient(
+ from 90deg at 100px 50px,
+ red 0.2turn,
+ orange 0.2turn 0.4turn,
+ yellow 0.4turn 0.6turn,
+ green 0.6turn 0.8turn,
+ blue 0.8turn 1.0turn
+ );
+ </style>
+</head>
+<body>
+ <div id="output"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation-expected.html b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation-expected.html
new file mode 100644
index 0000000000..68d750f462
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation-expected.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <style type="text/css">
+ div {
+ width: 300px;
+ height: 150px;
+ background: conic-gradient(
+ from 180deg at 100px 50px,
+ red 0.2turn,
+ orange 0.2turn 0.4turn,
+ yellow 0.4turn 0.6turn,
+ green 0.6turn 0.8turn,
+ blue 0.8turn 1.0turn
+ );
+ </style>
+</head>
+<body>
+ <div id="output"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation.html b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation.html
new file mode 100644
index 0000000000..e8b213b3d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient-rotation.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Conic gradient</title>
+ <link rel="match" href="conic-gradient-rotation-expected.html"/>
+</head>
+<body>
+ <canvas id="c"></canvas>
+ <script type="text/javascript">
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+
+ const grad = ctx.createConicGradient(2.5*Math.PI, 100, 50);
+
+ grad.addColorStop(0, "red");
+ grad.addColorStop(0.2, "red");
+ grad.addColorStop(0.2, "orange");
+ grad.addColorStop(0.4, "orange");
+ grad.addColorStop(0.4, "yellow");
+ grad.addColorStop(0.6, "yellow");
+ grad.addColorStop(0.6, "green");
+ grad.addColorStop(0.8, "green");
+ grad.addColorStop(0.8, "blue");
+
+ ctx.fillStyle = grad;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ </script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient.html b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient.html
new file mode 100644
index 0000000000..73fcf6c23e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/fill-and-stroke-styles/conic-gradient.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Conic gradient</title>
+ <link rel="match" href="conic-gradient-expected.html"/>
+</head>
+<body>
+ <canvas id="c"></canvas>
+ <script type="text/javascript">
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+
+ const grad = ctx.createConicGradient(0, 100, 50);
+
+ grad.addColorStop(0, "red");
+ grad.addColorStop(0.2, "red");
+ grad.addColorStop(0.2, "orange");
+ grad.addColorStop(0.4, "orange");
+ grad.addColorStop(0.4, "yellow");
+ grad.addColorStop(0.6, "yellow");
+ grad.addColorStop(0.6, "green");
+ grad.addColorStop(0.8, "green");
+ grad.addColorStop(0.8, "blue");
+
+ ctx.fillStyle = grad;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ </script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-fillStyle-opacity.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-fillStyle-opacity.html
new file mode 100644
index 0000000000..b5f9d98b89
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-fillStyle-opacity.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-opacity-expected.html"/>
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-14000">
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.fillStyle = 'rgba(0, 128, 128, 0.75)';
+ ctx.fillRect(10, 10, 50, 50);
+ ctx.fillStyle = 'rgba(255, 0, 255, 0.5)';
+ ctx.fillRect(70, 10, 50, 50);
+ ctx.fillStyle = 'rgba(255, 165, 0, 0.25)';
+ ctx.fillRect(10, 70, 50, 50);
+ ctx.fillStyle = 'rgba(210, 105, 30, 0)';
+ ctx.fillRect(70, 70, 50, 50);
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.8)';
+ ctx.fillRect(10, 150, 50, 50);
+ ctx.fillStyle = 'rgba(255, 0, 0, 0.6)';
+ ctx.fillRect(20, 150, 50, 50);
+ ctx.fillStyle = 'rgba(255, 255, 0, 0.4)';
+ ctx.fillRect(30, 150, 50, 50);
+ ctx.fillStyle = 'rgba(0, 128, 0, 0.2)';
+ ctx.fillRect(40, 150, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle-expected.html
new file mode 100644
index 0000000000..f87794f0b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle-expected.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<body>
+ <div id="sq-1"></div>
+ <div id="sq-2"></div>
+ <div id="sq-3"></div>
+ <div id="sq-4"></div>
+</body>
+<style>
+ /*The expected behavior when setting the opacity through different methods
+ is that the opacity of the resulting drawn element is the product of the opacity
+ value set by each of the methods.*/
+ div{
+ width: 50px;
+ height: 50px;
+ position: absolute;
+ }
+ #sq-1{
+ background-color: black;
+ left: 18px;
+ top: 18px;
+ opacity: 0.125;
+ }
+ #sq-2{
+ background-color: black;
+ left: 78px;
+ top: 18px;
+ opacity: 0.03125;
+ }
+ #sq-3{
+ background-color: black;
+ left: 18px;
+ top: 78px;
+ opacity: 0.1875;
+ }
+ #sq-4{
+ background-color: black;
+ left: 78px;
+ top: 78px;
+ opacity: 0.016;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle.html
new file mode 100644
index 0000000000..24c97d62ec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity-alpha-and-fillStyle.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-filter-opacity-alpha-and-fillStyle-expected.html"/>
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-10000">
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ /*
+ The expected behavior when setting the opacity through different methods
+ is that the opacity of the resulting drawn element is the product of the opacity
+ value set by each of the methods.
+ */
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0.5;
+ ctx.fillStyle = 'rgba(0,0,0,0.5)';
+ ctx.filter = 'opacity(50%)';
+ ctx.fillRect(10, 10, 50, 50);
+
+ ctx.globalAlpha = 0.5;
+ ctx.fillStyle = 'rgba(0,0,0,0.25)';
+ ctx.filter = 'opacity(25%)';
+ ctx.fillRect(70, 10, 50, 50);
+
+ ctx.globalAlpha = 0.75;
+ ctx.fillStyle = 'rgba(0,0,0,0.5)';
+ ctx.filter = 'opacity(50%)';
+ ctx.fillRect(10, 70, 50, 50);
+
+ ctx.globalAlpha = 0.8;
+ ctx.fillStyle = 'rgba(0,0,0,0.2)';
+ ctx.filter = 'opacity(10%)';
+ ctx.fillRect(70, 70, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity.html
new file mode 100644
index 0000000000..cb2fc018af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-opacity.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-opacity-expected.html"/>
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-14000">
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.filter = 'opacity(75%)';
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(10, 10, 50, 50);
+ ctx.filter = 'opacity(50%)';
+ ctx.fillStyle = 'magenta';
+ ctx.fillRect(70, 10, 50, 50);
+ ctx.filter = 'opacity(25%)';
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(10, 70, 50, 50);
+ ctx.filter = 'opacity(0%)';
+ ctx.fillStyle = 'chocolate';
+ ctx.fillRect(70, 70, 50, 50);
+
+ ctx.filter = 'opacity(80%)';
+ ctx.fillStyle = 'cyan';
+ ctx.fillRect(10, 150, 50, 50);
+ ctx.filter = 'opacity(60%)';
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 150, 50, 50);
+ ctx.filter = 'opacity(40%)';
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(30, 150, 50, 50);
+ ctx.filter = 'opacity(20%)';
+ ctx.fillStyle = 'green';
+ ctx.fillRect(40, 150, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur-expected.html
new file mode 100644
index 0000000000..1f218b4bd2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur-expected.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ /*
+ The shadow and shadow blur effects should be the same regardless if they were
+ defined with filters or properties. The blur parameter is set as double when
+ using the shadowBlur property since its uses havlf of the value set as the
+ standard deviation for the gaussian blur (https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur-dev)
+ while the filter parameter is used directly as the standard deviation
+ (https://drafts.fxtf.org/filter-effects/#funcdef-filter-drop-shadow). The
+ fuzziness is defined with a maxDifference of 13 as to be the 5% of 256, since
+ the CSS spec defines the expected behavior in relation to an ideal Gaussian blur
+ with a tolerance of 5%. See: https://drafts.csswg.org/css-backgrounds-3/#shadow-blur.
+ */
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowBlur = 4;
+ ctx.shadowColor = 'red';
+ ctx.fillRect(20, 20, 50, 50);
+ ctx.shadowBlur = 8;
+ ctx.shadowColor = 'blue';
+ ctx.fillRect(100, 20, 50, 50);
+ ctx.shadowBlur = 20;
+ ctx.shadowColor = 'yellow';
+ ctx.fillRect(20, 100, 50, 50);
+ ctx.shadowBlur = 30;
+ ctx.shadowColor = 'cyan';
+ ctx.fillRect(100, 100, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur.html
new file mode 100644
index 0000000000..ab3699906d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-blur.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-filter-shadow-and-properties-blur-expected.html"/>
+<meta name="fuzzy" content="maxDifference=0-13; totalPixels=0-25600">
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ /*
+ The shadow and shadow blur effects should be the same regardless if they were
+ defined with filters or properties. The blur parameter is set as double when
+ using the shadowBlur property since its uses havlf of the value set as the
+ standard deviation for the gaussian blur (https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur-dev)
+ while the filter parameter is used directly as the standard deviation
+ (https://drafts.fxtf.org/filter-effects/#funcdef-filter-drop-shadow). The
+ fuzziness is defined with a maxDifference of 13 as to be the 5% of 256, since
+ the CSS spec defines the expected behavior in relation to an ideal Gaussian blur
+ with a tolerance of 5%. See: https://drafts.csswg.org/css-backgrounds-3/#shadow-blur.
+ */
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.filter = 'drop-shadow(10px 10px 2px red)';
+ ctx.fillRect(20, 20, 50, 50);
+ ctx.filter = 'drop-shadow(10px 10px 4px blue)';
+ ctx.fillRect(100, 20, 50, 50);
+ ctx.filter = 'drop-shadow(10px 10px 10px yellow)';
+ ctx.fillRect(20, 100, 50, 50);
+ ctx.filter = 'drop-shadow(10px 10px 15px cyan)';
+ ctx.fillRect(100, 100, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-expected.html
new file mode 100644
index 0000000000..294a219b70
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties-expected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ /*
+ The expected behavior when both shadow properties and filters are used at the
+ same time is that the filter is applied to the elements drawn and the shadow
+ properties create another shadow that includes even shadows of the
+ filter-generated shadows.
+ */
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.fillStyle = 'cyan';
+ ctx.fillRect(40, 40, 50, 50);
+ ctx.fillRect(30, 30, 50, 50);
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 50, 50);
+ ctx.fillStyle = 'black';
+ ctx.fillRect(10, 10, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties.html
new file mode 100644
index 0000000000..2057b95c3e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-and-properties.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-filter-shadow-and-properties-expected.html"/>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ /*
+ The expected behavior when both shadow properties and filters are used at the
+ same time is that the filter is applied to the elements drawn and the shadow
+ properties create another shadow that includes even shadows of the
+ filter-generated shadows.
+ */
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.shadowColor = 'cyan';
+ ctx.shadowOffsetX = 20;
+ ctx.shadowOffsetY = 20;
+ ctx.filter = 'drop-shadow(10px 10px 0 red)';
+ ctx.fillRect(10, 10, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-expected.html
new file mode 100644
index 0000000000..04bc6392cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ /*
+ The expected behavior of filter-generated shadows is tested against
+ a drawing using only rectangles that draws the shadows manually.
+ */
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 50, 50);
+ ctx.fillStyle = 'black';
+ ctx.fillRect(10, 10, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow.html
new file mode 100644
index 0000000000..ddc6c89ee5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-filter-shadow.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-filter-shadow-expected.html"/>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ /*
+ The expected behavior of filter-generated shadows is tested against
+ a drawing using only rectangles that draws the shadows manually.
+ */
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.filter = 'drop-shadow(10px 10px 0 red)';
+ ctx.fillRect(10, 10, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-globalAlpha.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-globalAlpha.html
new file mode 100644
index 0000000000..6d7bbcd03d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-globalAlpha.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-opacity-expected.html"/>
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-14000">
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.globalAlpha = 0.75;
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(10, 10, 50, 50);
+ ctx.globalAlpha = 0.5;
+ ctx.fillStyle = 'magenta';
+ ctx.fillRect(70, 10, 50, 50);
+ ctx.globalAlpha = 0.25;
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(10, 70, 50, 50);
+ ctx.globalAlpha = 0;
+ ctx.fillStyle = 'chocolate';
+ ctx.fillRect(70, 70, 50, 50);
+
+ ctx.globalAlpha = 0.8;
+ ctx.fillStyle = 'cyan';
+ ctx.fillRect(10, 150, 50, 50);
+ ctx.globalAlpha = 0.6;
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 150, 50, 50);
+ ctx.globalAlpha = 0.4;
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(30, 150, 50, 50);
+ ctx.globalAlpha = 0.2;
+ ctx.fillStyle = 'green';
+ ctx.fillRect(40, 150, 50, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes-expected.html
new file mode 100644
index 0000000000..3eb7581981
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes-expected.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<body>
+</body>
+<script>
+ /*
+ Compare how the opacity is handled in different blend modes when setting its
+ value with filters or with properties.
+ */
+
+ function drawSquares(canvasId, x, y, compositeOperation) {
+ var canvas = document.getElementById(canvasId);
+ var ctx = canvas.getContext('2d');
+ canvas.style.position = 'absolute';
+ canvas.style.left = `${x}px`;
+ canvas.style.top = `${y}px`;
+
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.globalAlpha = 1.0;
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 200, 60);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.globalAlpha = 0.7;
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.globalCompositeOperation = compositeOperation;
+ ctx.globalAlpha = 0.5;
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(25, 25, 50, 50);
+ }
+
+ // Fomatted in the same matrix as the drawn elements.
+ var compositeOperations =
+ ['source-over', 'source-in', 'source-out', 'source-atop','destination-over',
+ 'destination-in', 'destination-out', 'destination-atop', 'lighter', 'copy',
+ 'xor', 'multiply', 'screen', 'overlay', 'darken',
+ 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
+ 'difference', 'exclusion', 'hue', 'saturation', 'color',
+ 'luminosity'];
+
+ for (var i = 0; i < compositeOperations.length; i++){
+ var canvas = document.createElement('canvas');
+ canvas.id = `canvas-${i}`;
+ document.body.appendChild(canvas);
+ drawSquares(canvas.id, (i%5)*300, Math.floor(i/5)*300,
+ compositeOperations[i]);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes.html
new file mode 100644
index 0000000000..a2d513eb62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-blend-modes.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<link rel="match" href="canvas-opacity-blend-modes-expected.html"/>
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-390000">
+<body>
+</body>
+<script>
+ /*
+ Compare how the opacity is handled in different blend modes when setting its
+ value with filters or with properties.
+ */
+
+ function drawSquares(canvasId, x, y, compositeOperation) {
+ var canvas = document.getElementById(canvasId);
+ var ctx = canvas.getContext('2d');
+ canvas.style.position = 'absolute';
+ canvas.style.left = `${x}px`;
+ canvas.style.top = `${y}px`;
+
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.filter = 'opacity(100%)';
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 200, 60);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.filter = 'opacity(70%)';
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.globalCompositeOperation = compositeOperation;
+ ctx.filter = 'opacity(50%)';
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(25, 25, 50, 50);
+ }
+
+ // Fomatted in the same matrix as the drawn elements.
+ var compositeOperations =
+ ['source-over', 'source-in', 'source-out', 'source-atop','destination-over',
+ 'destination-in', 'destination-out', 'destination-atop', 'lighter', 'copy',
+ 'xor', 'multiply', 'screen', 'overlay', 'darken',
+ 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
+ 'difference', 'exclusion', 'hue', 'saturation', 'color',
+ 'luminosity'];
+
+ for (var i = 0; i < compositeOperations.length; i++){
+ var canvas = document.createElement('canvas');
+ canvas.id = `canvas-${i}`;
+ document.body.appendChild(canvas);
+ drawSquares(canvas.id, (i%5)*300, Math.floor(i/5)*300,
+ compositeOperations[i]);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-expected.html
new file mode 100644
index 0000000000..caf6b53ce3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/canvas-opacity-expected.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<body>
+ <div id="sq-1"></div>
+ <div id="sq-2"></div>
+ <div id="sq-3"></div>
+ <div id="sq-4"></div>
+ <div id="sq-5"></div>
+ <div id="sq-6"></div>
+ <div id="sq-7"></div>
+ <div id="sq-8"></div>
+</body>
+<style>
+ div{
+ width: 50px;
+ height: 50px;
+ position: absolute;
+ }
+ #sq-1{
+ background-color: teal;
+ left: 18px;
+ top: 18px;
+ opacity: 0.75;
+ }
+ #sq-2{
+ background-color: magenta;
+ left: 78px;
+ top: 18px;
+ opacity: 0.5;
+ }
+ #sq-3{
+ background-color: orange;
+ left: 18px;
+ top: 78px;
+ opacity: 0.25;
+ }
+ #sq-4{
+ background-color: chocolate;
+ left: 78px;
+ top: 78px;
+ opacity: 0;
+ }
+ #sq-5{
+ background-color: cyan;
+ left: 18px;
+ top: 158px;
+ opacity: 0.8;
+ }
+ #sq-6{
+ background-color: red;
+ left: 28px;
+ top: 158px;
+ opacity: 0.6;
+ }
+ #sq-7{
+ background-color: yellow;
+ left: 38px;
+ top: 158px;
+ opacity: 0.4;
+ }
+ #sq-8{
+ background-color: green;
+ left: 48px;
+ top: 158px;
+ opacity: 0.2;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/svg-filter-crash.html b/testing/web-platform/tests/html/canvas/element/manual/filters/svg-filter-crash.html
new file mode 100644
index 0000000000..f64379c792
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/svg-filter-crash.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<script>
+ document.addEventListener('DOMContentLoaded', async () => {
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
+ const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter')
+ filter.setAttribute('id', 'id_0')
+ svg.appendChild(filter)
+ document.documentElement.appendChild(svg)
+ const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas')
+ const context = canvas.getContext('2d')
+ context.filter = 'url(#id_0) url(#id_0) sepia() url(#id_0)'
+ svg.setAttribute('style', 'display: inline-block;')
+ })
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur-expected.html
new file mode 100644
index 0000000000..ae8911b2de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur-expected.html
@@ -0,0 +1,19 @@
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.filter = 'blur(2px)';
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(10,10,100,100);
+ ctx.filter = 'blur(5px)';
+ ctx.fillStyle = 'magenta';
+ ctx.fillRect(120, 10, 100, 100);
+ ctx.filter = 'blur(5px) blur(10px)';
+ ctx.fillStyle = 'cyan';
+ ctx.fillRect(10, 120, 100, 100);
+ ctx.filter = 'none';
+ ctx.fillStyle = 'black';
+ ctx.fillRect(120, 120, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur.html
new file mode 100644
index 0000000000..9fe7ef120c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-blur.html
@@ -0,0 +1,24 @@
+<head>
+ <link rel="match" href="canvas-filter-object-blur-expected.html">
+</head>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.filter = new CanvasFilter({name: "gaussianBlur", stdDeviation: 2});
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(10,10,100,100);
+ ctx.filter = new CanvasFilter({name: "gaussianBlur", stdDeviation: 5});
+ ctx.fillStyle = 'magenta';
+ ctx.fillRect(120, 10, 100, 100);
+ ctx.filter = new CanvasFilter([
+ {name: "gaussianBlur", stdDeviation: 5},
+ {name: "gaussianBlur", stdDeviation: 10}]);
+ ctx.fillStyle = 'cyan';
+ ctx.fillRect(10, 120, 100, 100);
+ ctx.filter = 'none';
+ ctx.fillStyle = 'black';
+ ctx.fillRect(120, 120, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer-expected.html
new file mode 100644
index 0000000000..a2351cbcf2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer-expected.html
@@ -0,0 +1,65 @@
+<body>
+ <canvas id="canvas" width="500" height="100"></canvas>
+ <svg width="0", height="0">
+ <defs>
+ <filter color-interpolation-filters='sRGB' id="Identity" filterUnits="objectBoundingBox"
+ x="0%" y="0%" width="100%" height="100%">
+ <feComponentTransfer>
+ <feFuncR type="identity"/>
+ <feFuncG type="identity"/>
+ <feFuncB type="identity"/>
+ <feFuncA type="identity"/>
+ </feComponentTransfer>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="Table">
+ <feComponentTransfer>
+ <feFuncR type="table" tableValues="0 2 0.5 1"/>
+ <feFuncG type="table" tableValues="1 -1 5 0"/>
+ <feFuncB type="table" tableValues="0 1 1 0"/>
+ </feComponentTransfer>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="Discrete">
+ <feComponentTransfer>
+ <feFuncR type="discrete" tableValues="0 2 0.5 1"/>
+ <feFuncG type="discrete" tableValues="1 -1 5 0"/>
+ <feFuncB type="discrete" tableValues="0 1 1 0"/>
+ </feComponentTransfer>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="Linear">
+ <feComponentTransfer>
+ <feFuncR type="linear" slope=".5" intercept=".25"/>
+ <feFuncG type="linear" slope="1.5" intercept="0"/>
+ <feFuncB type="linear" slope="-0.5" intercept=".5"/>
+ </feComponentTransfer>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="Gamma">
+ <feComponentTransfer>
+ <feFuncR type="gamma" amplitude="2" exponent="5" offset="-0.5"/>
+ <feFuncG type="gamma" amplitude="0.9" exponent="3" offset="0.3"/>
+ <feFuncB type="gamma" amplitude="1.1" exponent="1" offset="0.1"/>
+ </feComponentTransfer>
+ </filter>
+ </defs>
+ </svg>
+</body>
+<script type="text/javascript">
+ const ctx = document.getElementById("canvas").getContext("2d");
+
+ const grad = ctx.createLinearGradient(10, 0, 490, 0);
+ grad.addColorStop(0, "#f00");
+ grad.addColorStop(0.33, "#0f0");
+ grad.addColorStop(0.67, "#00f");
+ grad.addColorStop(1, "#000");
+ ctx.fillStyle = grad;
+
+ ctx.filter = "url('#Identity')";
+ ctx.fillRect(10, 10, 480, 10);
+ ctx.filter = "url('#Table')";
+ ctx.fillRect(10, 30, 480, 10);
+ ctx.filter = "url('#Discrete')";
+ ctx.fillRect(10, 50, 480, 10);
+ ctx.filter = "url('#Linear')";
+ ctx.fillRect(10, 70, 480, 10);
+ ctx.filter = "url('#Gamma')";
+ ctx.fillRect(10, 90, 480, 10);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer.html
new file mode 100644
index 0000000000..47889c0db2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-component-transfer.html
@@ -0,0 +1,62 @@
+<head>
+ <link rel="match" href="canvas-filter-object-component-transfer-expected.html">
+</head>
+<body>
+ <canvas id="canvas" width="500" height="100"></canvas>
+</body>
+<script>
+ const ctx = document.getElementById("canvas").getContext("2d");
+
+ const grad = ctx.createLinearGradient(10, 0, 490, 0);
+ grad.addColorStop(0, "#f00");
+ grad.addColorStop(0.33, "#0f0");
+ grad.addColorStop(0.67, "#00f");
+ grad.addColorStop(1, "#000");
+ ctx.fillStyle = grad;
+
+ const identityFilter = new CanvasFilter({
+ name: "componentTransfer",
+ funcR: {type: "identity"},
+ funcG: {type: "identity"},
+ funcB: {type: "identity"},
+ funcA: {type: "identity"},
+ });
+ ctx.filter = identityFilter;
+ ctx.fillRect(10, 10, 480, 10);
+
+ const tableFilter = new CanvasFilter({
+ name: "componentTransfer",
+ funcR: {type: "table", tableValues: [0, 2, 0.5, 1]},
+ funcG: {type: "table", tableValues: [1, -1, 5, 0]},
+ funcB: {type: "table", tableValues: [0, 1, 1, 0]},
+ });
+ ctx.filter = tableFilter;
+ ctx.fillRect(10, 30, 480, 10);
+
+ const discreteFilter = new CanvasFilter({
+ name: "componentTransfer",
+ funcR: {type: "discrete", tableValues: [0, 2, 0.5, 1]},
+ funcG: {type: "discrete", tableValues: [1, -1, 5, 0]},
+ funcB: {type: "discrete", tableValues: [0, 1, 1, 0]},
+ });
+ ctx.filter = discreteFilter;
+ ctx.fillRect(10, 50, 480, 10);
+
+ const linearFilter = new CanvasFilter({
+ name: "componentTransfer",
+ funcR: {type: "linear", slope: 0.5, intercept: 0.25},
+ funcG: {type: "linear", slope: 1.5, intercept: 0},
+ funcB: {type: "linear", slope: -0.5, intercept: 0.5},
+ });
+ ctx.filter = linearFilter;
+ ctx.fillRect(10, 70, 480, 10);
+
+ const gammaFilter = new CanvasFilter({
+ name: "componentTransfer",
+ funcR: {type: "gamma", amplitude: 2, exponent: 5, offset: -0.5},
+ funcG: {type: "gamma", amplitude: 0.9, exponent: 3, offset: 0.3},
+ funcB: {type: "gamma", amplitude: 1.1, exponent: 1, offset: 0.1},
+ });
+ ctx.filter = gammaFilter;
+ ctx.fillRect(10, 90, 480, 10);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix-expected.html
new file mode 100644
index 0000000000..896a93542e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix-expected.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<style type="text/css">
+ canvas {
+ margin: 5px;
+ }
+</style>
+<body>
+ <svg width="0" height="0">
+ <filter color-interpolation-filters='sRGB' id="justKernel">
+ <feConvolveMatrix
+ kernelMatrix="3 0 0 0 0 0 0 0 -3"/>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="preserveAlpha">
+ <feConvolveMatrix
+ kernelMatrix="3 0 0 0 0 0 0 0 -3"
+ preserveAlpha="true"/>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="target">
+ <feConvolveMatrix
+ kernelMatrix="3 0 0 0 0 0 0 0 -3"
+ targetX="2" targetY="2"/>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="divisor">
+ <feConvolveMatrix
+ kernelMatrix="3 0 0 0 0 0 0 0 -3"
+ divisor="3"/>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="bias">
+ <feConvolveMatrix
+ kernelMatrix="3 0 0 0 0 0 0 0 -3"
+ bias="0.5"/>
+ </filter>
+ <filter color-interpolation-filters='sRGB' id="edgeMode">
+ <feConvolveMatrix
+ kernelMatrix="3 0 0 0 0 0 0 0 -3"
+ edgeMode="wrap"/>
+ </filter>
+ </svg>
+</body>
+<script type="text/javascript">
+
+const filters = [
+ "url('#justKernel')",
+ "url('#preserveAlpha')",
+ "url('#target')",
+ "url('#divisor')",
+ "url('#bias')",
+ "url('#edgeMode')",
+];
+
+function draw(ctx) {
+ ctx.fillRect(0, 20, 120, 100);
+
+ ctx.beginPath();
+ ctx.arc(150, 70, 50, 0, 2*Math.PI);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(220, 20);
+ ctx.lineTo(170, 120);
+ ctx.lineTo(270, 120);
+ ctx.lineTo(220, 20);
+ ctx.fill();
+}
+
+for (f of filters) {
+ const canvas = document.createElement("canvas");
+ document.body.prepend(canvas);
+ const ctx = canvas.getContext("2d");
+ ctx.filter = "blur(0px)";
+ ctx.fillStyle = "rgba(0,255,0,0.5)";
+ draw(ctx);
+ ctx.fillStyle = "rgba(255,0,255,0.5)";
+ ctx.filter = f;
+ draw(ctx);
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix.html
new file mode 100644
index 0000000000..1dfd602d13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-convolve-matrix.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<head>
+ <link rel="match" href="canvas-filter-object-convolve-matrix-expected.html">
+ <style type="text/css">
+ canvas {
+ margin: 5px;
+ }
+ </style>
+</head>
+<body>
+</body>
+<script>
+function makeConvolveFilter(options) {
+ const KERNEL_MATRIX = [
+ [3, 0, 0],
+ [0, 0, 0],
+ [0, 0, -3],
+ ];
+
+ options = Object.assign(options, {
+ kernelMatrix: KERNEL_MATRIX, name: "convolveMatrix"});
+ return new CanvasFilter(options);
+}
+
+const test_cases = [
+ {},
+ {preserveAlpha: true},
+ {targetX: 2, targetY: 2},
+ {divisor: 3},
+ {bias: 0.5},
+ {edgeMode: "wrap"}
+];
+
+function draw(ctx) {
+ ctx.fillRect(0, 20, 120, 100);
+
+ ctx.beginPath();
+ ctx.arc(150, 70, 50, 0, 2*Math.PI);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(220, 20);
+ ctx.lineTo(170, 120);
+ ctx.lineTo(270, 120);
+ ctx.lineTo(220, 20);
+ ctx.fill();
+}
+
+for (tc of test_cases) {
+ const canvas = document.createElement("canvas");
+ document.body.prepend(canvas);
+ const ctx = canvas.getContext("2d");
+ ctx.filter = "blur(0px)";
+ ctx.fillStyle = "rgba(0,255,0,0.5)";
+ draw(ctx);
+ ctx.fillStyle = "rgba(255,0,255,0.5)";
+ ctx.filter = makeConvolveFilter(tc);
+ draw(ctx);
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence-expected.html
new file mode 100644
index 0000000000..ff0eebe2e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence-expected.html
@@ -0,0 +1,37 @@
+<body>
+ <svg style="display:none">>
+ <filter id="base">
+ <feTurbulence baseFrequency="0.025"/>
+ </filter>
+ <filter id="base2d">
+ <feTurbulence baseFrequency="0.025, 0.1"/>
+ </filter>
+ <filter id="highFrequency">
+ <feTurbulence baseFrequency="0.05"/>
+ </filter>
+ <filter id="seed">
+ <feTurbulence baseFrequency="0.025" seed="100"/>
+ </filter>
+ <filter id="numOctaves">
+ <feTurbulence baseFrequency="0.025" numOctaves="2"/>
+ </filter>
+ <filter id="empty">
+ <feTurbulence/>
+ </filter>
+ <filter id="fractalNoise">
+ <feTurbulence baseFrequency="0.025" type="fractalNoise"/>
+ </filter>
+ <filter id="stitchTiles">
+ <feTurbulence baseFrequency="0.025" stitchTiles="noStitch"/>
+ </filter>
+</body>
+<script>
+ testCases = document.getElementsByTagName("filter");
+ for (tc of testCases) {
+ const canvas = document.createElement("canvas");
+ document.body.appendChild(canvas);
+ const ctx = canvas.getContext("2d");
+ ctx.filter = `url(#${tc.id})`;
+ ctx.fillRect(0, 0, 1, 1);
+ }
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence.html
new file mode 100644
index 0000000000..b5b494825e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence.html
@@ -0,0 +1,26 @@
+<head>
+ <link rel="match" href="canvas-filter-object-blur-expected.html">
+</head>
+<body>
+</body>
+<script>
+ const testCases = [
+ {baseFrequency: 0.025},
+ {baseFrequency: [0.025, 0.1]},
+ {baseFrequency: 0.05},
+ {baseFrequency: 0.025, seed: 100},
+ {baseFrequency: 0.025, numOctaves: 2},
+ {},
+ {baseFrequency: 0.025, type: "fractalNoise"},
+ {baseFrequency: 0.025, stitchTiles: "stitch"},
+ ]
+
+ for (tc of testCases) {
+ const canvas = document.createElement("canvas");
+ document.body.appendChild(canvas);
+ const ctx = canvas.getContext("2d");
+ const filterOptions = {...{name: "turbulence"}, ...tc};
+ ctx.filter = new CanvasFilter(filterOptions);
+ ctx.fillRect(0, 0, 1, 1);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion-expected.html
new file mode 100644
index 0000000000..f043b0e762
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion-expected.html
@@ -0,0 +1,30 @@
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ var ctx = document.getElementById('canvas').getContext('2d');
+
+ // preserveAlpha for convolveMatrix is the only boolean so far implemented
+ function drawWithConvolveFilter(x, y, preserveAlphaValue) {
+ ctx.filter = new CanvasFilter({
+ name: "convolveMatrix",
+ kernelMatrix: [[1, 0], [0, 1]],
+ preserveAlpha: preserveAlphaValue,
+ });
+ ctx.fillRect(x, y, 30, 30);
+ }
+
+ ctx.fillStyle = "rgba(255,0,255,0.5)";
+ let x = 10;
+ let y = 10;
+ for (var i = 0; i < 6; i++) {
+ drawWithConvolveFilter(x, y, true);
+ x += 40;
+ }
+ y = 50;
+ x = 10;
+ for (var i = 0; i < 5; i++) {
+ drawWithConvolveFilter(x, y, false);
+ x += 40;
+ }
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion.html
new file mode 100644
index 0000000000..97ade79f37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-boolean-conversion.html
@@ -0,0 +1,58 @@
+<head>
+ <link rel="match" href="canvas-filter-boolean-conversion-expected.html">
+</head>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ // Test the built-in ECMAScript types Undefined, Null, Boolean, String, Number, and Object
+ // as input to the CanvasFilter resolver when a bool is the intended result.
+ var ctx = document.getElementById('canvas').getContext('2d');
+
+ // preserveAlpha for convolveMatrix is the only boolean so far implemented
+ function drawWithConvolveFilter(x, y, preserveAlphaValue) {
+ ctx.filter = new CanvasFilter({
+ name: "convolveMatrix",
+ kernelMatrix: [[1, 0], [0, 1]],
+ preserveAlpha: preserveAlphaValue,
+ });
+ ctx.fillRect(x, y, 30, 30);
+ }
+
+ const trueTestCases = [
+ true,
+ { valueOf() { return false; }},
+ "foo",
+ 1,
+ {},
+ []
+ ];
+
+ const falseTestCases = [
+ false,
+ "",
+ 0,
+ null,
+ undefined,
+ ];
+
+ ctx.fillStyle = "rgba(255,0,255,0.5)";
+ let x = 10;
+ let y = 10;
+ for (tc of trueTestCases) {
+ drawWithConvolveFilter(x, y, tc);
+ x += 40;
+ }
+ y = 50;
+ x = 10;
+ for (tc of falseTestCases) {
+ drawWithConvolveFilter(x, y, tc);
+ x += 40;
+ }
+
+ ctx.filter = new CanvasFilter({
+ name: "componentTransfer",
+ funcR: {type: "discrete", tableValues: 0.5},
+ });
+ ctx.fillRect(10, 10, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion-expected.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion-expected.html
new file mode 100644
index 0000000000..8b4262ed04
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion-expected.html
@@ -0,0 +1,26 @@
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ var ctx = document.getElementById('canvas').getContext('2d');
+ // Null and False both evaluate to zero
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 0});
+ ctx.fillRect(10, 10, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 0});
+ ctx.fillRect(50, 10, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 0});
+ ctx.fillRect(90, 10, 30, 30);
+ // True evaluates to one
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 1});
+ ctx.fillRect(130, 10, 30, 30);
+ // String, Number and Object should all work
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 5});
+ ctx.fillRect(10, 50, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 5});
+ ctx.fillRect(50, 50, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 5});
+ ctx.fillRect(90, 50, 30, 30);
+ // Valid sequence
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 5});
+ ctx.fillRect(130, 50, 30, 30);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion.html
new file mode 100644
index 0000000000..c742633224
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-long-conversion.html
@@ -0,0 +1,35 @@
+<head>
+ <link rel="match" href="canvas-filter-long-conversion-expected.html">
+</head>
+<body>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+ // Test the built-in ECMAScript types Undefined, Null, Boolean, String, Number, and Object
+ // as input to the CanvasFilter resolver when a long is the intended result.
+ var ctx = document.getElementById('canvas').getContext('2d');
+
+ // Null, False and [] evaluate to zero
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: null});
+ ctx.fillRect(10, 10, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: false});
+ ctx.fillRect(50, 10, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: []});
+ ctx.fillRect(90, 10, 30, 30);
+ // True evaluates to one
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: true});
+ ctx.fillRect(130, 10, 30, 30);
+ // String, Number and Object should all work
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: "5"});
+ ctx.fillRect(10, 50, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: 5});
+ ctx.fillRect(50, 50, 30, 30);
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: { valueOf() { return 5; }}});
+ ctx.fillRect(90, 50, 30, 30);
+ // Valid sequence
+ ctx.filter = new CanvasFilter({filter: "gaussianBlur", stdDeviation: [5]});
+ ctx.fillRect(130, 50, 30, 30);
+
+ // Undefined and other inputs that throw exceptions are tested in:
+ // html/canvas/element/filters/2d.filter.canvasFilterObject.blur.exceptions.html
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-sequence-conversion.html b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-sequence-conversion.html
new file mode 100644
index 0000000000..d48627867e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/filters/tentative/idl-conversions/canvas-filter-sequence-conversion.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>Canvas test: canvas-filter-sequence-conversion</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>canvas-filter-sequence-conversion</h1>
+<p class="desc">Test converting types into sequences</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test pixels on CanvasFilter() various inputs to tableValues (which is a sequence)");
+_addTest(function(canvas, ctx) {
+
+ // Inputs to parameters that are expecting sequence<long>. Results are either the value of the
+ // red pixel drawing using the resultant filter or that we expect this input to throw an error.
+ const testCases = [
+ {input: [], result: 0},
+ {input: [0.5], result: 127},
+ {input: ["0.5"], result: 127},
+ {input: 1, result: "throws"},
+ {input: {}, result: "throws"},
+ {input: false, result: "throws"},
+ {input: true, result: "throws"},
+ {input: NaN, result: "throws"},
+ {input: { valueOf() { return [1]; }}, result: "throws"},
+ ];
+
+ // A simple filter that just overrides the red channel if successful.
+ function makeFilter(value) {
+ return new CanvasFilter({
+ name: "componentTransfer",
+ funcR: {type: "table", tableValues: value}
+ });
+ }
+
+ for (const tc of testCases) {
+ if (tc.result === "throws") {
+ assert_throws_js(TypeError, function(){ makeFilter(tc.input) });
+ } else {
+ ctx.reset();
+ ctx.filter = makeFilter(tc.input);
+ ctx.fillRect(0, 0, 100, 100);
+ _assertPixelApprox(canvas, 5, 5, tc.result,0,0,255, 2);
+ }
+ }
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/image-smoothing/imagesmoothing.html b/testing/web-platform/tests/html/canvas/element/manual/image-smoothing/imagesmoothing.html
new file mode 100644
index 0000000000..1a86a8f201
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/image-smoothing/imagesmoothing.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext2D imageSmoothingEnabled test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#image-smoothing">
+<script>
+function createTestImage() {
+ var image = document.createElement('canvas');
+ var imgctx = image.getContext('2d');
+ imgctx.fillStyle = "#F00";
+ imgctx.fillRect(0, 0, 2, 2);
+ imgctx.fillStyle = "#0F0";
+ imgctx.fillRect(0, 0, 1, 1);
+ return image;
+}
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ assert_true(ctx.imageSmoothingEnabled);
+}, "When the canvas context is created, imageSmoothingEnabled must be set to true.");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ ctx.imageSmoothingEnabled = false;
+ assert_false(ctx.imageSmoothingEnabled);
+}, "On getting imageSmoothingEnabled, the user agent must return the last value it was set to.");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing is actually on by default and just the attribute value.");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ ctx.imageSmoothingEnabled = true;
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing works when imageSmoothingEnabled is set to true");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with drawImage().");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.fillRect(0, 0, 10, 10);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fillRect and createPattern().");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.scale(10, 10);
+ ctx.rect(0, 0, 10, 10);
+ ctx.fill();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fill() and createPattern().");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ var image = createTestImage();
+ ctx.strokeStyle = ctx.createPattern(image, 'repeat');
+ ctx.lineWidth = 5;
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.stroke();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with stroke() and createPattern().");
+
+test(function() {
+ var repaints = 5;
+ var ctx = document.createElement('canvas').getContext('2d');
+
+ function draw() {
+ ctx.clearRect(0, 0, 10, 10);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+ }
+
+ while (repaints > 0) {
+ draw();
+ repaints = repaints - 1;
+ }
+
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) still works after repaints.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html
new file mode 100644
index 0000000000..fca8274528
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<body>
+ <p>Tests that the close method of ImageBitmap does dispose the image data.</p>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+promise_test(function(t) {
+ var worker = new Worker('worker-onmessage-noop.js');
+
+ var imgHeight = 10;
+ var imgWidth = 10;
+ var imageData = new ImageData(10, 10);
+ var bitmap;
+ var ctx;
+ return createImageBitmap(imageData).then(imageBitmap => {
+ bitmap = imageBitmap;
+ assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
+ assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
+
+ // Apply structured clone to the bitmap, nothing should be changed
+ worker.postMessage({data: bitmap});
+ assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
+ assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
+
+ // After calling close, the image data associated with the bitmap should no longer exist
+ bitmap.close();
+ assert_equals(bitmap.width, 0, "bitmap.width = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ var canvas = document.createElement("canvas");
+ canvas.width = imgWidth;
+ canvas.height = imgHeight;
+ ctx = canvas.getContext("2d");
+ assert_throws_dom("InvalidStateError", function() { ctx.drawImage(bitmap, 0, 0); });
+
+ // Try to apply structured clone to an already closed bitmap
+ try {
+ worker.postMessage({data: bitmap});
+ throw new Error("Apply clone to an closed bitmap should be rejected");
+ }
+ catch(ex) {
+ // Apply structured clone to an already closed bitmap is rejected as expected.
+ }
+
+ // Try to apply transferring to an already closed bitmap
+ try {
+ worker.postMessage({data: bitmap}, [bitmap]);
+ throw new Error("Apply transferring to an closed bitmap should be rejected");
+ } catch(ex) {
+ // Apply structured clone to an already closed bitmap is rejected as expected.
+ }
+
+ // Calling createImageBitmap from a closed bitmap should be rejected
+ return createImageBitmap(bitmap).then(function() {
+ throw new Error("createImageBitmap from a closed bitmap should be rejected");
+ }, ex => {
+ // Calling createImageBitmap from a closed ImageBitmap is rejected as expected.
+ });
+ }).then(() => {
+ // Call close to a already closed bitmap should be noop.
+ bitmap.close();
+ assert_equals(bitmap.width, 0, "bitmap.height = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ return createImageBitmap(imageData).then(imageBitmap => {
+ bitmap = imageBitmap;
+ assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
+ assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
+
+ // Transfer the bitmap to a worker
+ worker.postMessage({data: bitmap}, [bitmap]);
+
+ // After transferring, the bitmap is neutered.
+ assert_equals(bitmap.width, 0, "bitmap.height = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ // Calling close to a neutered bitmap should be noop.
+ bitmap.close();
+ assert_equals(bitmap.width, 0, "bitmap.height = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ });
+ }).catch(function(ex) {
+ throw new Error("No exception should be thrown.");
+ })
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html
new file mode 100644
index 0000000000..782c7e130c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html
@@ -0,0 +1,170 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function checkNoCrop(imageBitmap)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = 50;
+ canvas.height = 50;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(imageBitmap, 0, 0);
+ var d = ctx.getImageData(0, 0, 1, 1).data;
+ assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red.");
+ d = ctx.getImageData(39, 0, 1, 1).data;
+ assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green.");
+ d = ctx.getImageData(0, 39, 1, 1).data;
+ assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue.");
+ d = ctx.getImageData(39, 39, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black.");
+ d = ctx.getImageData(41, 41, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black.");
+}
+
+function checkCrop(imageBitmap)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = 50;
+ canvas.height = 50;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(imageBitmap, 0, 0);
+ var d = ctx.getImageData(0, 0, 1, 1).data;
+ assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red.");
+ d = ctx.getImageData(19, 0, 1, 1).data;
+ assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green.");
+ d = ctx.getImageData(0, 19, 1, 1).data;
+ assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue.");
+ d = ctx.getImageData(19, 19, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black.");
+ d = ctx.getImageData(21, 21, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black.");
+}
+
+function compareBitmaps(bitmap1, bitmap2)
+{
+ var canvas1 = document.createElement("canvas");
+ var canvas2 = document.createElement("canvas");
+ canvas1.width = 50;
+ canvas1.height = 50;
+ canvas2.width = 50;
+ canvas2.height = 50;
+ var ctx1 = canvas1.getContext("2d");
+ var ctx2 = canvas2.getContext("2d");
+ ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
+ ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
+ ctx1.drawImage(bitmap1, 0, 0);
+ ctx2.drawImage(bitmap2, 0, 0);
+ var data1 = ctx1.getImageData(0, 0, 50, 50).data;
+ var data2 = ctx2.getImageData(0, 0, 50, 50).data;
+ var dataMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ if (data1[i] != data2[i]) {
+ dataMatched = false;
+ break;
+ }
+ }
+ assert_false(dataMatched);
+}
+
+function testImageBitmap(source)
+{
+ return Promise.all([
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "high"}),
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "medium"}),
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "low"}),
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "pixelated"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "high"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "medium"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "low"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "pixelated"}),
+ ]).then(([noCropHigh, noCropMedium, noCropLow, noCropPixelated, cropHigh, cropMedium, cropLow, cropPixelated]) => {
+ checkNoCrop(noCropHigh);
+ checkNoCrop(noCropMedium);
+ checkNoCrop(noCropLow);
+ checkNoCrop(noCropPixelated);
+ checkCrop(cropHigh);
+ checkCrop(cropMedium);
+ checkCrop(cropLow);
+ checkCrop(cropPixelated);
+ // Brute-force comparison among all bitmaps is too expensive
+ compareBitmaps(noCropHigh, noCropMedium);
+ compareBitmaps(noCropLow, noCropPixelated);
+ compareBitmaps(cropHigh, cropMedium);
+ compareBitmaps(cropLow, cropPixelated);
+ });
+}
+
+function initializeTestCanvas()
+{
+ var testCanvas = document.createElement("canvas");
+ testCanvas.width = 20;
+ testCanvas.height = 20;
+ var testCtx = testCanvas.getContext("2d");
+ testCtx.fillStyle = "rgb(255, 0, 0)";
+ testCtx.fillRect(0, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 255, 0)";
+ testCtx.fillRect(10, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 255)";
+ testCtx.fillRect(0, 10, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 0)";
+ testCtx.fillRect(10, 10, 10, 10);
+ return testCanvas;
+}
+
+// Blob
+promise_test(function() {
+ return new Promise((resolve, reject) => {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/pattern.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ }).then(testImageBitmap);
+}, 'createImageBitmap from a Blob with resize option.');
+
+// HTMLCanvasElement
+promise_test(function() {
+ var testCanvas = initializeTestCanvas();
+ return testImageBitmap(testCanvas);
+}, 'createImageBitmap from a HTMLCanvasElement with resize option.');
+
+// HTMLImageElement
+promise_test(function() {
+ return new Promise((resolve, reject) => {
+ var image = new Image();
+ image.onload = function() {
+ resolve(image);
+ }
+ image.src = '/images/pattern.png'
+ }).then(testImageBitmap);
+}, 'createImageBitmap from a HTMLImageElement with resize option.');
+
+// HTMLImageElement of svg with no specified size
+promise_test(function() {
+ return new Promise((resolve, reject) => {
+ var image = new Image();
+ image.onload = function() {
+ resolve(image);
+ }
+ image.src = '/images/pattern-nosize.svg'
+ }).then(testImageBitmap);
+}, 'createImageBitmap from a HTMLImageElement of svg with no specified size with resize option.');
+
+// ImageBitmap
+promise_test(function() {
+ var testCanvas = initializeTestCanvas();
+ return createImageBitmap(testCanvas).then(testImageBitmap);
+}, 'createImageBitmap from an ImageBitmap with resize option.');
+
+// ImageData
+promise_test(function() {
+ var canvas = initializeTestCanvas();
+ var ctx = canvas.getContext("2d");
+ var data = ctx.getImageData(0, 0, 20, 20);
+ return testImageBitmap(data);
+}, 'createImageBitmap from an ImageData with resize option.');
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html
new file mode 100644
index 0000000000..d48d9e80ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script>
+function createNewCanvas(width, height)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, width, height);
+ return ctx;
+}
+
+function checkLowResult(imageBitmap, bw, bh, video, sx, sy, sw, sh)
+{
+ var ctx1 = createNewCanvas(bw, bh);
+ var ctx2 = createNewCanvas(bw, bh);
+ ctx1.drawImage(imageBitmap, 0, 0);
+ ctx2.drawImage(video, sx, sy, sw, sh, 0, 0, bw, bh);
+ var data1 = ctx1.getImageData(0, 0, bw, bh).data;
+ var data2 = ctx2.getImageData(0, 0, bw, bh).data;
+ var dataMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ // data1[i] is strictly the same as data2[i] on software rendering.
+ // But on GPU, the difference could be quite significant.
+ if (Math.abs(data1[i] - data2[i]) > 33) {
+ dataMatched = false;
+ break;
+ }
+ }
+ assert_true(dataMatched);
+}
+
+function generateTest()
+{
+ bitmapWidth = video.videoWidth/2;
+ bitmapHeight = video.videoHeight/2;
+ return Promise.all([
+ createImageBitmap(video, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}),
+ createImageBitmap(video, 10, 10, bitmapWidth, bitmapHeight, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}),
+ ]).then(t.step_func_done(([noCropLow, cropLow]) => {
+ checkLowResult(noCropLow, bitmapWidth, bitmapHeight, video, 0, 0, video.videoWidth, video.videoHeight);
+ checkLowResult(cropLow, bitmapWidth, bitmapHeight, video, 10, 10, bitmapWidth, bitmapHeight);
+ }), t.step_func_done(function() {
+ assert_true(false, 'Promise rejected');
+ }));
+}
+
+var t = async_test('createImageBitmap(HTMLVideoElement) with resize option');
+
+// HTMLVideoElement
+var video = document.createElement("video");
+video.preload = "auto";
+if (video.requestVideoFrameCallback) {
+ video.requestVideoFrameCallback(t.step_func(() => generateTest()));
+} else {
+ video.oncanplaythrough = t.step_func(() => generateTest());
+}
+video.src = getVideoURI("/media/counting");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js
new file mode 100644
index 0000000000..1889035202
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js
@@ -0,0 +1,168 @@
+function makeCanvas() {
+ return new Promise(resolve => {
+ var testCanvas = document.createElement("canvas");
+ testCanvas.width = 20;
+ testCanvas.height = 20;
+ var testCtx = testCanvas.getContext("2d");
+ testCtx.fillStyle = "rgb(255, 0, 0)";
+ testCtx.fillRect(0, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 255, 0)";
+ testCtx.fillRect(10, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 255)";
+ testCtx.fillRect(0, 10, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 0)";
+ testCtx.fillRect(10, 10, 10, 10);
+ resolve(testCanvas);
+ });
+}
+
+function makeOffscreenCanvas() {
+ return new Promise(resolve => {
+ let canvas = new OffscreenCanvas(20, 20);
+ var testCtx = canvas.getContext("2d");
+ testCtx.fillStyle = "rgb(255, 0, 0)";
+ testCtx.fillRect(0, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 255, 0)";
+ testCtx.fillRect(10, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 255)";
+ testCtx.fillRect(0, 10, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 0)";
+ testCtx.fillRect(10, 10, 10, 10);
+ resolve(canvas);
+ });
+}
+
+var imageBitmapVideoPromise = new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ video.oncanplaythrough = function() {
+ resolve(video);
+ };
+ video.onerror = reject;
+
+ // preload=auto is required to ensure a frame is available once
+ // canplaythrough is fired. The default of preload=metadata does not
+ // gaurantee this.
+ video.preload = "auto";
+ video.src = getVideoURI("/images/pattern");
+
+ // Prevent WebKit from garbage collecting event handlers.
+ window._video = video;
+});
+
+function makeVideo() {
+ return imageBitmapVideoPromise;
+}
+
+var imageBitmapDataUrlVideoPromise = fetch(getVideoURI("/images/pattern"))
+ .then(response => Promise.all([response.headers.get("Content-Type"), response.arrayBuffer()]))
+ .then(([type, data]) => {
+ return new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ video.oncanplaythrough = function() {
+ resolve(video);
+ };
+ video.onerror = reject;
+
+ var encoded = btoa(String.fromCodePoint(...new Uint8Array(data)));
+ var dataUrl = `data:${type};base64,${encoded}`;
+
+ // preload=auto is required to ensure a frame is available once
+ // canplaythrough is fired. The default of preload=metadata does not
+ // gaurantee this.
+ video.preload = "auto";
+ video.src = dataUrl;
+
+ // Prevent WebKit from garbage collecting event handlers.
+ window._dataVideo = video;
+ });
+ });
+
+function makeDataUrlVideo() {
+ return imageBitmapDataUrlVideoPromise;
+}
+
+function makeMakeHTMLImage(src) {
+ return function() {
+ return new Promise((resolve, reject) => {
+ var img = new Image();
+ img.onload = function() {
+ resolve(img);
+ };
+ img.onerror = reject;
+ img.src = src;
+ });
+ }
+}
+
+function makeMakeSVGImage(src) {
+ return function() {
+ return new Promise((resolve, reject) => {
+ var image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ image.onload = () => resolve(image);
+ image.onerror = reject;
+ image.setAttribute("externalResourcesRequired", "true");
+ image.setAttributeNS("http://www.w3.org/1999/xlink", 'xlink:href', src);
+ document.body.appendChild(image);
+ });
+ }
+}
+
+function makeImageData() {
+ return new Promise(function(resolve, reject) {
+ var width = 20, height = 20;
+ var imgData = new ImageData(width, height);
+ for (var i = 0; i < width * height * 4; i += 4) {
+ imgData.data[i] = 0;
+ imgData.data[i + 1] = 0;
+ imgData.data[i + 2] = 0;
+ imgData.data[i + 3] = 255; //alpha channel: 255
+ }
+ var halfWidth = width / 2;
+ var halfHeight = height / 2;
+ // initialize to R, G, B, Black, with each one 10*10 pixels
+ for (var i = 0; i < halfHeight; i++)
+ for (var j = 0; j < halfWidth; j++)
+ imgData.data[i * width * 4 + j * 4] = 255;
+ for (var i = 0; i < halfHeight; i++)
+ for (var j = halfWidth; j < width; j++)
+ imgData.data[i * width * 4 + j * 4 + 1] = 255;
+ for (var i = halfHeight; i < height; i++)
+ for (var j = 0; j < halfWidth; j++)
+ imgData.data[i * width * 4 + j * 4 + 2] = 255;
+ resolve(imgData);
+ });
+}
+
+function makeImageBitmap() {
+ return makeCanvas().then(canvas => {
+ return createImageBitmap(canvas);
+ });
+}
+
+function makeBlob(src) {
+ return function () {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", src);
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ }
+}
+
+var imageSourceTypes = [
+ { name: 'an HTMLCanvasElement', factory: makeCanvas },
+ { name: 'an HTMLVideoElement', factory: makeVideo },
+ { name: 'an HTMLVideoElement from a data URL', factory: makeDataUrlVideo },
+ { name: 'a bitmap HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.png") },
+ { name: 'a vector HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.svg") },
+ { name: 'a bitmap SVGImageElement', factory: makeMakeSVGImage("/images/pattern.png") },
+ { name: 'a vector SVGImageElement', factory: makeMakeSVGImage("/images/pattern.svg") },
+ { name: 'an OffscreenCanvas', factory: makeOffscreenCanvas },
+ { name: 'an ImageData', factory: makeImageData },
+ { name: 'an ImageBitmap', factory: makeImageBitmap },
+ { name: 'a Blob', factory: makeBlob("/images/pattern.png") },
+];
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html
new file mode 100644
index 0000000000..23af96408a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap: blob with wrong mime type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script>
+promise_test(t => {
+ // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+ const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+ "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+
+ let bytes = new Array(IMAGE.length);
+ for (let i = 0; i < IMAGE.length; i++) {
+ bytes[i] = IMAGE.charCodeAt(i);
+ }
+
+ let blob = new Blob([new Uint8Array(bytes)], { type: "text/html"});
+
+ return window.createImageBitmap(blob)
+ .then(imageBitmap => {
+ assert_true(true, "Image created!");
+ assert_equals(imageBitmap.width, 1, "Image is 1x1");
+ assert_equals(imageBitmap.height, 1, "Image is 1x1");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html
new file mode 100644
index 0000000000..a2dcf0cc0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap: clipping to the bitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<div id="results"></div>
+<script>
+const color = 204;
+function testClip( name, sx, sy, sw, sh, expectedColors, expectedWidth, expectedHeight ) {
+ promise_test(function(t) {
+ return new Promise(function(resolve, reject) {
+ const image = new Image();
+ image.onload = function() { resolve(image); };
+ image.onerror = function() { reject(); };
+ image.src = "/images/green-16x16.png";
+ }).then(function(image) {
+ return createImageBitmap(image, sx, sy, sw, sh);
+ }).then(function(imageBitmap) {
+
+ assert_equals(imageBitmap.width, expectedWidth);
+ assert_equals(imageBitmap.height, expectedHeight);
+
+ const canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+
+ // debug
+ document.getElementById("results").append(canvas);
+ canvas.setAttribute("style", "width: 100px; height: 100px;");
+
+ const ctx = canvas.getContext("2d");
+ ctx.fillStyle = `rgb(${color}, ${color}, ${color})`;
+ ctx.fillRect(0, 0, 20, 20);
+ ctx.drawImage(imageBitmap, 0, 0);
+
+ for (let [x, y, r, g, b, a] of expectedColors) {
+ _assertPixel(canvas, x,y, r,g,b,a);
+ }
+ });
+ }, name);
+}
+testClip( "simple clip inside",
+ 8, 8, 8, 8, [
+ [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, color,color,color,255]
+ ], 8, 8
+);
+testClip( "clip outside negative",
+ -8, -8, 16, 16, [
+ [ 4, 4, color,color,color,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, 0,255,0,255]
+ ], 16, 16
+);
+testClip( "clip outside positive",
+ 8, 8, 16, 16, [
+ [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, color,color,color,255]
+ ], 16, 16
+);
+testClip( "clip inside using negative width and height",
+ 24, 24, -16, -16, [
+ [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, color,color,color,255]
+ ], 16, 16
+);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html
new file mode 100644
index 0000000000..2ddf3648f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<title>Test colorSpaceConversion option for createImageBitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body>
+<script>
+function testCanvasDisplayingPattern(canvas, width, height)
+{
+ var tolerance = 10; // high tolerance for differing color management results
+ const check = (x, y, r, g, b, a) =>
+ _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance);
+
+ check(1 * width / 4, 1 * height / 4, 124,0,27,255);
+ check(3 * width / 4, 1 * height / 4, 0,124,46,255);
+ check(1 * width / 4, 3 * height / 4, 60,0,123,255);
+ check(3 * width / 4, 3 * height / 4, 0,0,0,255);
+}
+
+function testDrawImageBitmap(source, options)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = 20;
+ canvas.height = 20;
+ var ctx = canvas.getContext("2d");
+ return createImageBitmap(source, options).then(imageBitmap => {
+ ctx.drawImage(imageBitmap, 0, 0);
+ testCanvasDisplayingPattern(canvas, 20, 20);
+ });
+}
+
+var wideGamutImageSourceTypes = [
+ {name: 'a bitmap HTMLImageElement', factory: makeMakeHTMLImage("/images/wide-gamut-pattern.png")},
+ {name: 'a Blob', factory: makeBlob("/images/wide-gamut-pattern.png")},
+];
+
+for (let { name, factory } of wideGamutImageSourceTypes) {
+ promise_test(function() {
+ return factory().then(function(img) {
+ return testDrawImageBitmap(img, {colorSpaceConversion: "none"});
+ });
+ }, `createImageBitmap from ${name}, and drawImage on the created ImageBitmap with colorSpaceConversion: none`);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html
new file mode 100644
index 0000000000..3b8644cff5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<title>attempt to draw a closed ImageBitmap to a 2d canvas throws INVALID_STATE_ERR</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script>
+promise_test(function(t) {
+ return new Promise(function(resolve, reject) {
+ const image = new Image();
+ image.onload = function() { resolve(image); };
+ image.onerror = function() { reject(); };
+ image.src = "/images/green-16x16.png";
+ }).then(function(image) {
+ return createImageBitmap(image, 0, 0, 16, 16);
+ }).then(function(imageBitmap) {
+ imageBitmap.close();
+
+ const canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+
+ const ctx = canvas.getContext("2d");
+ assert_throws_dom("InvalidStateError", function() { ctx.drawImage(imageBitmap, 0, 0); });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html
new file mode 100644
index 0000000000..5b5698813a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap + drawImage test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body>
+<script>
+function testCanvasDisplayingPattern(canvas, width, height, sourceIsVideo)
+{
+ var tolerance = 3;
+ let topLeft = [255, 0, 0, 255];
+ let topRight = [0, 255, 0, 255];
+ let bottomLeft = [0, 0, 255, 255];
+ let bottomRight = [0, 0, 0, 255];
+ if (sourceIsVideo) {
+ // The source video uses colors in the Rec.601 color space whose
+ // values are close to full red, full green, full blue, and black,
+ // but when converted to sRGB, are somewhat different.
+ topLeft = [247, 37, 0, 255];
+ topRight = [63, 251, 0, 255];
+ bottomLeft = [28, 35, 255, 255];
+ bottomRight = [5, 0, 2, 255];
+ }
+ const check = (x, y, [r, g, b, a]) =>
+ _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance);
+ check(1 * width / 4, 1 * height / 4, topLeft);
+ check(3 * width / 4, 1 * height / 4, topRight);
+ check(1 * width / 4, 3 * height / 4, bottomLeft);
+ check(3 * width / 4, 3 * height / 4, bottomRight);
+}
+
+function testDrawImageBitmap(source, args = [], { resizeWidth = 20, resizeHeight = 20 } = {})
+{
+ let sourceIsVideo = source instanceof HTMLVideoElement;
+ var canvas = document.createElement("canvas");
+ canvas.width = resizeWidth;
+ canvas.height = resizeHeight;
+ var ctx = canvas.getContext("2d");
+ return createImageBitmap(source, ...args).then(imageBitmap => {
+ assert_equals(imageBitmap.width, resizeWidth);
+ assert_equals(imageBitmap.height, resizeHeight);
+ ctx.drawImage(imageBitmap, 0, 0);
+ testCanvasDisplayingPattern(canvas, resizeWidth, resizeHeight, sourceIsVideo);
+ });
+}
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function() {
+ return factory().then(function(img) {
+ return testDrawImageBitmap(img);
+ });
+ }, `createImageBitmap from ${name}, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { resizeWidth: 10, resizeHeight: 10 };
+ return testDrawImageBitmap(img, [options], options);
+ });
+ }, `createImageBitmap from ${name} scaled down, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { resizeWidth: 40, resizeHeight: 40 };
+ return testDrawImageBitmap(img, [options], options);
+ });
+ }, `createImageBitmap from ${name} scaled up, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { resizeWidth: 10, resizeHeight: 40 };
+ return testDrawImageBitmap(img, [options], options);
+ });
+ }, `createImageBitmap from ${name} resized, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ return testDrawImageBitmap(img, [20, 20, -20, -20]);
+ });
+ }, `createImageBitmap from ${name} with negative sw/sh, and drawImage on the created ImageBitmap`);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html
new file mode 100644
index 0000000000..8b2a33e85b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>Test that createImageBitmap honors EXIF orientation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>canvas { outline: 1px solid black; margin-right: 1em; }</style>
+<body>
+<script>
+function loadImage(src) {
+ return new Promise(function(resolve) {
+ const image = new Image();
+ image.addEventListener("load", () => resolve(image), { once: true });
+ image.src = src;
+ });
+}
+
+function checkColors(ctx, w, h, expectedColors) {
+ let data = ctx.getImageData(0, 0, w, h).data;
+ for (let [row, col, r, g, b, a] of expectedColors) {
+ let x = col * 80 + 40;
+ let y = row * 80 + 40;
+ let i = (x + y * w) * 4;
+
+ let expected = [r, g, b, a];
+ let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]];
+
+ assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`);
+ }
+}
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 320;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then((image) => createImageBitmap(image))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 255, 0, 0, 255],
+ [0, 1, 0, 255, 0, 255],
+ [0, 2, 0, 0, 255, 255],
+ [0, 3, 0, 0, 0, 255],
+ [1, 0, 255, 128, 128, 255],
+ [1, 1, 128, 255, 128, 255],
+ [1, 2, 128, 128, 255, 255],
+ [1, 3, 128, 128, 128, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation from-image, and no cropping");
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 320;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then((image) => createImageBitmap(image, { imageOrientation: "flipY" }))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 255, 128, 128, 255],
+ [0, 1, 128, 255, 128, 255],
+ [0, 2, 128, 128, 255, 255],
+ [0, 3, 128, 128, 128, 255],
+ [1, 0, 255, 0, 0, 255],
+ [1, 1, 0, 255, 0, 255],
+ [1, 2, 0, 0, 255, 255],
+ [1, 3, 0, 0, 0, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation flipY, and no cropping");
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 160;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then(image => createImageBitmap(image, 80, 0, 160, 160))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 0, 255, 0, 255],
+ [0, 1, 0, 0, 255, 255],
+ [1, 0, 128, 255, 128, 255],
+ [1, 1, 128, 128, 255, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation from-image, and cropping");
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 160;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then(image => createImageBitmap(image, 80, 0, 160, 160, { imageOrientation: "flipY" }))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 128, 255, 128, 255],
+ [0, 1, 128, 128, 255, 255],
+ [1, 0, 0, 255, 0, 255],
+ [1, 1, 0, 0, 255, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation flipY, and cropping");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html
new file mode 100644
index 0000000000..807925b88a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Test that createImageBitmap honors EXIF orientation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>canvas { outline: 1px solid black; margin-right: 1em; }</style>
+<body>
+<script>
+function loadImage(src) {
+ return new Promise(function(resolve) {
+ const image = new Image();
+ image.addEventListener("load", () => resolve(image), { once: true });
+ image.src = src;
+ });
+}
+
+function checkColors(ctx, w, h, is_verticle, expectedColors) {
+ let data = ctx.getImageData(0, 0, w, h).data;
+ row_width = 80;
+ col_width = 80;
+
+ for (let [row, col, r, g, b, a] of expectedColors) {
+ let x = col * row_width + 10;
+ let y = row * col_width + 10;
+ let i = (x + y * w) * 4;
+
+ let expected = [r, g, b, a];
+ let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]];
+
+ assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`);
+ }
+}
+
+for (let orientation of [1, 2, 3, 4, 5, 6, 7, 8]) {
+ async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 160;
+ canvas.height = 320;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage(`resources/squares_${orientation}.jpg`)
+ .then((image) => createImageBitmap(image, { imageOrientation: "none" }))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0, 160, 320);
+
+ checkColors(ctx, canvas.width, canvas.height, false, [
+ // row, col, r, g, b, a
+ [0, 0, 0, 0, 0, 255],
+ [0, 1, 128, 128, 128, 255],
+ [1, 0, 0, 0, 255, 255],
+ [1, 1, 128, 128, 255, 255],
+ [2, 0, 0, 255, 0, 255],
+ [2, 1, 128, 255, 128, 255],
+ [3, 0, 255, 0, 0, 255],
+ [3, 1, 255, 128, 128, 255],
+ ]);
+ }));
+ }, `createImageBitmap with Orientation ${orientation}`);
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html
new file mode 100644
index 0000000000..9b0d2dfb79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap + drawImage test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body>
+<script>
+function testCanvasDisplayingPattern(canvas, width, height, sourceIsVideo, flipped)
+{
+ var tolerance = 3;
+ let topLeft = [255, 0, 0, 255];
+ let topRight = [0, 255, 0, 255];
+ let bottomLeft = [0, 0, 255, 255];
+ let bottomRight = [0, 0, 0, 255];
+ if (sourceIsVideo) {
+ // The source video uses colors in the Rec.601 color space whose
+ // values are close to full red, full green, full blue, and black,
+ // but when converted to sRGB, are somewhat different.
+ topLeft = [247, 37, 0, 255];
+ topRight = [63, 251, 0, 255];
+ bottomLeft = [28, 35, 255, 255];
+ bottomRight = [5, 0, 2, 255];
+ }
+ if (flipped) {
+ [topLeft, bottomLeft] = [bottomLeft, topLeft];
+ [topRight, bottomRight] = [bottomRight, topRight];
+ }
+ const check = (x, y, [r, g, b, a]) =>
+ _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance);
+ check(1 * width / 4, 1 * height / 4, topLeft);
+ check(3 * width / 4, 1 * height / 4, topRight);
+ check(1 * width / 4, 3 * height / 4, bottomLeft);
+ check(3 * width / 4, 3 * height / 4, bottomRight);
+}
+
+function testDrawImageBitmap(source, args = [], flipped, { resizeWidth = 20, resizeHeight = 20 } = {})
+{
+ let sourceIsVideo = source instanceof HTMLVideoElement;
+ var canvas = document.createElement("canvas");
+ canvas.width = resizeWidth;
+ canvas.height = resizeHeight;
+ var ctx = canvas.getContext("2d");
+ return createImageBitmap(source, ...args).then(imageBitmap => {
+ assert_equals(imageBitmap.width, resizeWidth);
+ assert_equals(imageBitmap.height, resizeHeight);
+ ctx.drawImage(imageBitmap, 0, 0);
+ testCanvasDisplayingPattern(canvas, resizeWidth, resizeHeight, sourceIsVideo, flipped);
+ });
+}
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { imageOrientation: 'from-image' };
+ return testDrawImageBitmap(img, [options], false);
+ });
+ }, `createImageBitmap from ${name} imageOrientation: "from-image", and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { imageOrientation: 'flipY' };
+ return testDrawImageBitmap(img, [options], true);
+ });
+ }, `createImageBitmap from ${name} imageOrientation: "flipY", and drawImage on the created ImageBitmap`);
+
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html
new file mode 100644
index 0000000000..727a8a4978
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap in worker and transfer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+promise_test(function(t) {
+ return new Promise(function(resolve, reject) {
+ var worker = new Worker("createImageBitmap-worker.js");
+ worker.addEventListener("message", function(evt) {
+ var bitmap = evt.data;
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+ resolve();
+ });
+ worker.postMessage('test');
+ });
+}, 'Transfer ImageBitmap created in worker');
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html
new file mode 100644
index 0000000000..3cae3523ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html
@@ -0,0 +1,239 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<script>
+
+function makeOversizedCanvas() {
+
+ return new Promise(resolve => {
+ let canvas = document.createElement('canvas');
+ canvas.setAttribute('width', '100000000');
+ canvas.setAttribute('height', '100000000');
+ resolve(canvas);
+ });
+}
+
+function makeOversizedOffscreenCanvas() {
+ return new Promise(resolve =>{
+ let canvas = new OffscreenCanvas(100000000, 100000000);
+ resolve(canvas);
+ });
+}
+
+function makeInvalidBlob() {
+ return new Promise(resolve => {
+ resolve(new Blob()); // Blob with no data cannot be decoded.
+ });
+}
+
+function makeBrokenImage() {
+ return new Promise((resolve, reject) => {
+ const image = new Image();
+ image.src = "data:,x";
+ image.onload = reject;
+ image.onerror = () => resolve(image);
+ });
+}
+
+function makeAvailableButBrokenImage(path) {
+ return new Promise((resolve, reject) => {
+ const image = new Image();
+ image.src = path;
+ image.onload = () => resolve(image);
+ image.onerror = reject;
+ });
+}
+
+testCases = [
+ {
+ description: 'createImageBitmap with <sourceType> source and sw set to 0',
+ promiseTestFunction:
+ (source, t) => {
+ return promise_rejects_js(t, RangeError,
+ createImageBitmap(source, 0, 0, 0, 10));
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and sh set to 0',
+ promiseTestFunction:
+ (source, t) => {
+ return promise_rejects_js(t, RangeError,
+ createImageBitmap(source, 0, 0, 10, 0));
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and oversized ' +
+ '(unallocatable) crop region',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, 0, 0, 100000000, 100000000)
+ .then(i => {
+ assert_equals(i.width, 100000000);
+ assert_equals(i.height, 100000000);
+ })
+ .catch(e => {
+ // This case is not explicitly documented in the specification for
+ // createImageBitmap, but it is expected that internal failures
+ // cause InvalidStateError.
+ //
+ // Note: https://bugs.chromium.org/p/chromium/issues/detail?id=799025
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value of 0 int resizeWidth',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:0, resizeHeight:10})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value of 0 in resizeHeight',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:10, resizeHeight:0})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value between 0 and 1 in resizeWidth',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:0.5, resizeHeight:10})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value between 0 and 1 in resizeHeight',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:10, resizeHeight:0.5})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+];
+
+// Generate the test matrix for each sourceType + testCase combo.
+imageSourceTypes.forEach(imageSourceType => {
+ testCases.forEach(testCase => {
+ let description = testCase.description.replace('<sourceType>',
+ imageSourceType.name);
+ promise_test( t => {
+ return imageSourceType.factory().then(source => {
+ return testCase.promiseTestFunction(source, t);
+ });
+ }, description);
+ });
+});
+
+promise_test( t => {
+ return promise_rejects_js(t, TypeError, createImageBitmap(undefined));
+}, "createImageBitmap with undefined image source.");
+
+promise_test( t => {
+ return promise_rejects_js(t, TypeError, createImageBitmap(null));
+}, "createImageBitmap with null image source.");
+
+promise_test( t => {
+ var context = document.createElement("canvas").getContext("2d");
+ return promise_rejects_js(t, TypeError, createImageBitmap(context));
+}, "createImageBitmap with CanvasRenderingContext2D image source.");
+
+promise_test( t => {
+ var context = document.createElement("canvas").getContext("webgl");
+ return promise_rejects_js(t, TypeError, createImageBitmap(context));
+}, "createImageBitmap with WebGLRenderingContext image source.");
+
+promise_test( t => {
+ var buffer = new Uint8Array();
+ return promise_rejects_js(t, TypeError, createImageBitmap(buffer));
+}, "createImageBitmap with Uint8Array image source.");
+
+promise_test( t => {
+ var buffer = new ArrayBuffer(8);
+ return promise_rejects_js(t, TypeError, createImageBitmap(buffer));
+}, "createImageBitmap with ArrayBuffer image source.");
+
+promise_test( t => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(new Image()));
+}, "createImageBitmap with empty image source.");
+
+promise_test( t => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(document.createElement('video')));
+}, "createImageBitmap with empty video source.");
+
+promise_test( t => {
+ return makeOversizedCanvas().then(canvas => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(canvas));
+ });
+}, "createImageBitmap with an oversized canvas source.");
+
+promise_test( t => {
+ return makeOversizedOffscreenCanvas().then(offscreenCanvas => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(offscreenCanvas));
+ });
+}, "createImageBitmap with an invalid OffscreenCanvas source.");
+
+promise_test( t => {
+ return makeInvalidBlob().then(blob => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(blob));
+ });
+}, "createImageBitmap with an undecodable blob source.");
+
+promise_test( t => {
+ return makeBrokenImage().then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with a broken image source.");
+
+promise_test( t => {
+ return makeAvailableButBrokenImage("/images/undecodable.png").then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with an available but undecodable image source.");
+
+promise_test( t => {
+ return makeAvailableButBrokenImage("/images/red-zeroheight.svg").then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with an available but zero height image source.");
+
+promise_test( t => {
+ return makeAvailableButBrokenImage("/images/red-zerowidth.svg").then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with an available but zero width image source.");
+
+promise_test( t => {
+ return makeImageBitmap().then(bitmap => {
+ bitmap.close()
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(bitmap));
+ });
+}, "createImageBitmap with a closed ImageBitmap.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html
new file mode 100644
index 0000000000..ae3d23cfbc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap: origin-clean flag</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/media.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<div id=log></div>
+<script>
+
+function assert_origin_unclean_getImageData(bitmap) {
+ const context = document.createElement("canvas").getContext("2d");
+ context.drawImage(bitmap, 0, 0);
+ assert_throws_dom("SecurityError", () => {
+ context.getImageData(0, 0, 1, 1);
+ });
+}
+
+function assert_origin_unclean_drawImage(bitmap) {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(bitmap, 0, 0);
+ assert_throws_dom('SecurityError', () => canvas.toDataURL());
+}
+
+function assert_origin_unclean_transferFromImageBitmap(bitmap) {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(bitmap);
+ assert_throws_dom('SecurityError', () => canvas.toDataURL());
+}
+
+forEachCanvasSource(get_host_info().HTTP_REMOTE_ORIGIN,
+ get_host_info().HTTP_ORIGIN,
+ (name, factory) => {
+ promise_test(function() {
+ return factory().then(createImageBitmap).then(assert_origin_unclean_getImageData);
+ }, `${name}: origin unclear getImageData`);
+ promise_test(function() {
+ return factory().then(createImageBitmap).then(assert_origin_unclean_drawImage);
+ }, `${name}: origin unclear 2dContext.drawImage`);
+ promise_test(function() {
+ return factory().then(createImageBitmap).then(assert_origin_unclean_transferFromImageBitmap);
+ }, `${name}: origin unclear bitmaprenderer.transferFromImageBitmap`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html
new file mode 100644
index 0000000000..c185cd9cbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap serialize test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/namespaces.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+let worker, continuations = {};
+setup(function() {
+ worker = new Worker("transfer-worker.js");
+ worker.addEventListener("message", function(event) {
+ let { name, bitmap } = event.data;
+ if (continuations.hasOwnProperty(name)) {
+ continuations[name](bitmap);
+ }
+ });
+});
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function(t) {
+ return factory().then(createImageBitmap).then(function(bitmap) {
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+
+ worker.postMessage({ name: t.name, bitmap: bitmap });
+
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+
+ return new Promise(function(resolve) {
+ continuations[t.name] = resolve;
+ });
+ }).then(function(bitmap) {
+ assert_class_string(bitmap, "ImageBitmap");
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+ });
+ }, `Serialize ImageBitmap created from ${name}`);
+}
+
+promise_test(async (t) => {
+ const url = get_host_info().REMOTE_ORIGIN + '/images/pattern.png';
+ const image = await makeMakeHTMLImage(url)();
+ const bitmap = await createImageBitmap(image);
+
+ assert_throws_dom('DataCloneError', () => worker.postMessage(bitmap));
+}, 'Serializing a non-origin-clean ImageBitmap throws.');
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html
new file mode 100644
index 0000000000..25c1fb6885
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap with size overflow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 4294967400, 10, 10, 10);
+}, "createImageBitmap does not crash or reject the promise when passing very large sx");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 10, 4294967400, 10, 10);
+}, "createImageBitmap does not crash or reject the promise when passing very large sy");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 10, 10, 4294967400, 10);
+}, "createImageBitmap does not crash or reject the promise when passing very large sw");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 10, 10, 10, 4294967400);
+}, "createImageBitmap does not crash or reject the promise when passing very large sh");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 4294967400, 4294967400, 4294967400, 4294967400);
+}, "createImageBitmap does not crash or reject the promise when passing very large sx, sy, sw and sh");
+
+promise_test(function(t) {
+ var imgData = new ImageData(20, 20);
+ var imageBitmapOptions = {imageOrientation:'from-image', premultiplyAlpha:'default',
+ colorSpaceConversion:'none', resizeHeight:2122252543, resizeQuality:'high'};
+ return createImageBitmap(imgData, 0, 0, 4294967295, 64)
+ .then(imageBitmap => promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(imageBitmap, imageBitmapOptions)));
+}, "createImageBitmap throws an InvalidStateError error with big imageBitmap scaled up in big height");
+
+promise_test(function(t) {
+ var imgData = new ImageData(20, 20);
+ var imageBitmapOptions = {imageOrientation:'from-image', premultiplyAlpha:'default',
+ colorSpaceConversion:'none', resizeWidth:2122252543, resizeQuality:'high'};
+ return createImageBitmap(imgData, 0, 0, 4294967295, 64)
+ .then(imageBitmap => promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(imageBitmap, imageBitmapOptions)));
+}, "createImageBitmap throws an InvalidStateError error with big imageBitmap scaled up in big width");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html
new file mode 100644
index 0000000000..3ec02fcbf4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap transferring test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+let worker, continuations = {};
+setup(function() {
+ worker = new Worker("transfer-worker.js");
+ worker.addEventListener("message", function(event) {
+ let { name, bitmap } = event.data;
+ if (continuations.hasOwnProperty(name)) {
+ continuations[name](bitmap);
+ }
+ });
+});
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function(t) {
+ return factory().then(createImageBitmap).then(function(bitmap) {
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+
+ worker.postMessage({ name: t.name, bitmap: bitmap }, [bitmap]);
+
+ assert_equals(bitmap.width, 0);
+ assert_equals(bitmap.height, 0);
+
+ return new Promise(function(resolve) {
+ continuations[t.name] = resolve;
+ });
+ }).then(function(bitmap) {
+ assert_class_string(bitmap, "ImageBitmap");
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+ });
+ }, `Transfer ImageBitmap created from ${name}`);
+}
+
+promise_test(async (t) => {
+ const url = get_host_info().REMOTE_ORIGIN + '/images/pattern.png';
+ const image = await makeMakeHTMLImage(url)();
+ const bitmap = await createImageBitmap(image);
+
+ assert_throws_dom('DataCloneError',
+ () => worker.postMessage(bitmap, [bitmap]));
+}, 'Transferring a non-origin-clean ImageBitmap throws.');
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js
new file mode 100644
index 0000000000..67a0904e47
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js
@@ -0,0 +1,17 @@
+function makeBlob() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/pattern.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+}
+
+addEventListener("message", () => {
+ makeBlob().then(createImageBitmap).then(bitmap => {
+ postMessage(bitmap, [bitmap]);
+ });
+});
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html
new file mode 100644
index 0000000000..64b1791afe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html
@@ -0,0 +1,18 @@
+<html>
+<body>
+ <canvas id="myCanvas" width="400" height="400"></canvas>
+</body>>
+<script>
+var canvas = document.getElementById('myCanvas');
+ctx = canvas.getContext('2d');
+image = document.createElement("img");
+image.src = "../../../resources/black_white.png"
+image.onload = function() {
+ Promise.all([
+ createImageBitmap(image, { imageOrientation: 'flipY' }),
+ ]).then(function(sprites) {
+ // Draw image onto the canvas
+ ctx.drawImage(sprites[0], 0, 0);
+});
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html
new file mode 100644
index 0000000000..e93c17c8e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html
@@ -0,0 +1,26 @@
+<html>
+ <link rel="match" href="imageBitmap-from-imageData-no-image-rotation-expected.html" />
+ <style type="text/css">
+ canvas {
+ image-orientation: none;
+ }
+ </style>
+<body>
+ <canvas id="myCanvas" width="400" height="400"></canvas>
+</body>>
+<script>
+var canvas = document.getElementById('myCanvas');
+ctx = canvas.getContext('2d');
+image = document.createElement("img");
+image.src = "../../../resources/black_white.png"
+image.onload = function() {
+ Promise.all([
+ // The image should be flipped and ignoring "image-orientation" setting
+ // in css style.
+ createImageBitmap(image, { imageOrientation: 'flipY' }),
+ ]).then(function(sprites) {
+ // Draw image onto the canvas
+ ctx.drawImage(sprites[0], 0, 0);
+});
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html
new file mode 100644
index 0000000000..bababda44c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+ <p>Test whether the imageOrientation "from-image" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,0,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,0,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,150,150,300);
+}
+
+async function runTest() {
+ const canvas = document.getElementById('canvas');
+ canvas.width = 300;
+ canvas.height = 300;
+ const ctx = canvas.getContext('2d');
+ drawSquares(ctx);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html
new file mode 100644
index 0000000000..5e21671973
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+ <p>Test whether the imageOrientation "flipY" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,150,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,150,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,0,150,150);
+}
+
+async function runTest() {
+ const canvas = document.getElementById('canvas');
+ canvas.width = 300;
+ canvas.height = 300;
+ const ctx = canvas.getContext('2d');
+ drawSquares(ctx);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html
new file mode 100644
index 0000000000..02e0690876
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-flipped-expected.html" />
+<body>
+ <p>Test whether the imageOrientation "flipY" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,0,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,0,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,150,150,300);
+}
+
+async function runTest() {
+ const canvas_temp = document.createElement('canvas');
+ canvas_temp.width = 300;
+ canvas_temp.height = 300;
+ const ctx_temp = canvas_temp.getContext('2d');
+ drawSquares(ctx_temp);
+ const imageSource = ctx_temp.getImageData(0, 0, 300, 300);
+ const imageOrientation = 'flipY';
+ imageIDFlipped = await createImageBitmap(imageSource, 0, 0, 300, 300, { imageOrientation });
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(imageIDFlipped);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html
new file mode 100644
index 0000000000..4f155316f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+<body>
+ <p>
+ Test creating an ImageBitmap from the transferToImageBitmap of a webgl OffscreenCanvas, and then
+ transferred to an ImageBitmapRenderingContext.
+ </p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+ function drawSquares(ctx) {
+ ctx.enable(ctx.SCISSOR_TEST);
+ ctx.scissor(0, 150, 150, 150);
+ ctx.clearColor(1, 0, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(150, 150, 300, 150);
+ ctx.clearColor(0, 1, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(0, 0, 150, 150);
+ ctx.clearColor(0, 0, 1, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ }
+
+ async function runTest() {
+ const canvas = document.getElementById('canvas');
+ canvas.width = 300;
+ canvas.height = 300;
+ const ctx = canvas.getContext('webgl');
+ drawSquares(ctx);
+ }
+
+ runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html
new file mode 100644
index 0000000000..049a3822cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-webgl-expected.html" />
+
+<body>
+ <p>
+ Test creating an ImageBitmap from the transferToImageBitmap of a webgl OffscreenCanvas, and then
+ transferred to an ImageBitmapRenderingContext.
+ </p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+ function drawSquares(ctx) {
+ ctx.enable(ctx.SCISSOR_TEST);
+ ctx.scissor(0, 150, 150, 150);
+ ctx.clearColor(1, 0, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(150, 150, 300, 150);
+ ctx.clearColor(0, 1, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(0, 0, 150, 150);
+ ctx.clearColor(0, 0, 1, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ }
+
+ async function runTest() {
+ const offscreen = new OffscreenCanvas(300, 300);
+ const ctxOffscreen = offscreen.getContext('webgl');
+ drawSquares(ctxOffscreen);
+ const image = offscreen.transferToImageBitmap();
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(image);
+ }
+
+ runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html
new file mode 100644
index 0000000000..6d3d886759
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-expected.html" />
+<body>
+ <p>Test whether the imageOrientation "from-image" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,0,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,0,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,150,150,300);
+}
+
+async function runTest() {
+ const canvas_temp = document.createElement('canvas');
+ canvas_temp.width = 300;
+ canvas_temp.height = 300;
+ const ctx_temp = canvas_temp.getContext('2d');
+ drawSquares(ctx_temp);
+ const imageSource = ctx_temp.getImageData(0, 0, 300, 300);
+ const imageOrientation = 'from-image';
+ imageIDFlipped = await createImageBitmap(imageSource, 0, 0, 300, 300, { imageOrientation });
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(imageIDFlipped);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html
new file mode 100644
index 0000000000..ab4331adef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that image orientation is propagated when ImageBitmap objects are replicated.</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+<script>
+// This test is most relevant for browser implementations that apply EXIF image
+// orientation lazily. That is to say that the transform is applied at rasterization
+// time rather than at image decode time. This implies that image orientation metadata
+// is stored internally in the decoded image's data structure. This test ensures
+// that the orientation metadata is correctly carried over when ImageBitmap objects
+// are replicated (serialized/deserialized, copied or transferred).
+
+function checkImageBitmapRotated(bitmap) {
+ assert_equals(bitmap.width, 320, 'Bitmap width');
+ assert_equals(bitmap.height, 160, 'Bitmap height');
+
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+
+ const expected_colors = [
+ // row, col, r, g, b, a
+ [0, 0, 255, 0, 0, 255],
+ [0, 1, 0, 255, 0, 255],
+ [0, 2, 0, 0, 255, 255],
+ [0, 3, 0, 0, 0, 255],
+ [1, 0, 255, 128, 128, 255],
+ [1, 1, 128, 255, 128, 255],
+ [1, 2, 128, 128, 255, 255],
+ [1, 3, 128, 128, 128, 255],
+ ];
+
+ canvas.width = bitmap.width;
+ canvas.height = bitmap.height;
+ ctx.drawImage(bitmap, 0, 0);
+
+ let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
+ for (let [row, col, r, g, b, a] of expected_colors) {
+ let x = col * 80 + 40;
+ let y = row * 80 + 40;
+ let i = (x + y * canvas.width) * 4;
+ let expected = [r, g, b, a];
+ let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]];
+ assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`);
+ }
+}
+
+promise_test(async t => {
+ const response = await fetch("resources/squares_6.jpg");
+ const blob = await response.blob();
+ const image = await createImageBitmap(blob)
+ const image_copy = structuredClone(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, duplicated via structuredClone");
+
+promise_test(async t => {
+ const image = new Image();
+ image.src = "resources/squares_6.jpg"
+ await new Promise(resolve => image.onload = resolve);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img>");
+
+promise_test(async t => {
+ const image = new Image();
+ image.src = "resources/squares_6.jpg"
+ // The following has no effect because the image's style is not
+ // processed unless the element is connected to the DOM.
+ image.style.imageOrientation = "none";
+ await new Promise(resolve => image.onload = resolve);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img> not in DOM, imageOrientation = none");
+
+promise_test(async t => {
+ const image = new Image();
+ document.body.appendChild(image);
+ image.src = "resources/squares_6.jpg"
+ // The style is being processed in this case, but the imageOrientation
+ // CSS property must still have no effect because createImageBitmap
+ // accesses the element's underlying media directly, without being
+ // affected by the image's style (unlike drawImage).
+ image.style.imageOrientation = "none";
+ await new Promise(resolve => image.onload = resolve);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img> in DOM, imageOrientation = none");
+
+
+promise_test(async t => {
+ const response = await fetch("resources/squares_6.jpg");
+ const blob = await response.blob();
+ const image = await createImageBitmap(blob);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, duplicated via createImageBitmap");
+
+promise_test(async t => {
+ const worker = new Worker("serialize-worker.js");
+ const response = await fetch("resources/squares_6.jpg");
+ const blob = await response.blob()
+ const image = await createImageBitmap(blob);
+ worker.postMessage({bitmap: image});
+ const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+ checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker serialization round-trip");
+
+promise_test(async t => {
+ const worker = new Worker("transfer-worker.js");
+ let response = await fetch("resources/squares_6.jpg");
+ let blob = await response.blob();
+ let image = await createImageBitmap(blob);
+ worker.postMessage({bitmap: image}, [image]);
+ const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+ checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip");
+
+promise_test(async t => {
+ // This test variant ensures additional code coverage.
+ // By creating a canvas pattern, a reference to the ImageBitmap's
+ // underlying pixel data is held in the source realm. This forces
+ // implementations that do lazy copying to duplicate the pixel
+ // data at transfer time. This test verifies that the lazy
+ // duplication code path (if applicable) carries over the image
+ // orientation metadata.
+ const worker = new Worker("transfer-worker.js");
+ let response = await fetch("resources/squares_6.jpg");
+ let blob = await response.blob();
+ let image = await createImageBitmap(blob);
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ const pattern = ctx.createPattern(image, 'repeat');
+ worker.postMessage({bitmap: image}, [image]);
+ const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+ checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip, while referenced by a CanvasPattern");
+
+
+</script>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg
new file mode 100644
index 0000000000..0f0e8866b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg
new file mode 100644
index 0000000000..526f7a6c82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg
new file mode 100644
index 0000000000..a21e521c2d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg
new file mode 100644
index 0000000000..c4380b1e67
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg
new file mode 100644
index 0000000000..0bdd89aa1b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg
new file mode 100644
index 0000000000..f197760a11
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg
new file mode 100644
index 0000000000..9b1a346888
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg
new file mode 100644
index 0000000000..41d2fbe7f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js
new file mode 100644
index 0000000000..a76537cc9e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js
@@ -0,0 +1,3 @@
+addEventListener('message', evt => {
+ postMessage(evt.data);
+});
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js
new file mode 100644
index 0000000000..55465a899c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js
@@ -0,0 +1,3 @@
+addEventListener('message', evt => {
+ postMessage(evt.data, [evt.data.bitmap]);
+});
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js
new file mode 100644
index 0000000000..c0a352b4d9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js
@@ -0,0 +1,3 @@
+self.onmessage = function(e) {
+};
+
diff --git a/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers-expected.html b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers-expected.html
new file mode 100644
index 0000000000..873869ea72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers.html b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers.html
new file mode 100644
index 0000000000..afb53cfbeb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-layers.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="match" href="unclosed-layers-expected.html">
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers-expected.html b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers-expected.html
new file mode 100644
index 0000000000..8557441f7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,255,1)';
+ ctx.fillRect(60,60,75,50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers.html b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers.html
new file mode 100644
index 0000000000..ada874a51a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/layers/unclosed-nested-layers.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<link rel="match" href="unclosed-nested-layers-expected.html">
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ // Missing ctx.endLayer() here.
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001-ref.htm
new file mode 100644
index 0000000000..f85af9aab2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001-ref.htm
@@ -0,0 +1,11 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: "square" lineCap</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ </head>
+ <body>
+ <p>Description: The square value of lineCap means that a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line, must be added at the end of each line.</p>
+ <div><img src='/images/black-rectangle.png' alt='black rect' /></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001.htm b/testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001.htm
new file mode 100644
index 0000000000..583dbc9d13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/line-styles/canvas_linestyles_linecap_001.htm
@@ -0,0 +1,37 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: "square" lineCap</title>
+ <link rel="match" href="canvas_linestyles_linecap_001-ref.htm">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-linecap" />
+ <meta name="assert" content="The square value of lineCap means that a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line, must be added at the end of each line." />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Draw the first red rectangle.
+ ctx.fillStyle ="rgba(255, 0, 0, 1.0)";
+ ctx.fillRect(75, 0, 25, 50);
+
+ // Draw second red rectangle.
+ ctx.fillRect(0, 0, 25, 50);
+
+ // Draw a line with square lineCap.
+ ctx.strokeStyle = "rgba(0, 0, 0, 1.0)";
+ ctx.lineWidth = 50;
+ ctx.lineCap = "square";
+ ctx.beginPath();
+ ctx.moveTo(25, 25);
+ ctx.lineTo(75, 25);
+ ctx.stroke();
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: The square value of lineCap means that a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line, must be added at the end of each line.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_a.html b/testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_a.html
new file mode 100644
index 0000000000..7e692f937d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_a.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<link rel=match href=lineto_ref.html>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ }
+</style>
+<canvas id="c" width="150" height="150" >
+Your browser does not support the HTML5 canvas tag.</canvas>
+
+<script>
+var c = document.getElementById("c");
+var ctx = c.getContext("2d");
+
+ctx.beginPath();
+ctx.moveTo(20, 20);
+ctx.lineTo(20, 130);
+ctx.lineTo(130, 130);
+ctx.lineTo(130, 20);
+ctx.closePath();
+
+ctx.fillStyle = '#90EE90';
+ctx.fill();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_ref.html b/testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_ref.html
new file mode 100644
index 0000000000..3dc78ff804
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/line-styles/lineto_ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ }
+ div {
+ background: #90EE90;
+ width: 110px;
+ height: 110px;
+ margin: 20px;
+ }
+</style>
+<div></div>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/line-styles/setLineDash.html b/testing/web-platform/tests/html/canvas/element/manual/line-styles/setLineDash.html
new file mode 100644
index 0000000000..6b8d131da4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/line-styles/setLineDash.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>setLineDash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<canvas id="canvas"></canvas>
+<script>
+test(function() {
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+
+ var initial = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+ ctx.setLineDash(initial);
+ assert_array_equals(ctx.getLineDash(), initial, "line dash sanity");
+
+ ctx.setLineDash([Infinity]);
+ assert_array_equals(ctx.getLineDash(), initial, "Inf doesn't reset line dash");
+
+ ctx.setLineDash([NaN]);
+ assert_array_equals(ctx.getLineDash(), initial, "NaN doesn't reset line dash");
+
+ ctx.setLineDash([-1]);
+ assert_array_equals(ctx.getLineDash(), initial, "Negative doesn't reset line dash");
+}, "Invalid arguments to setLineDash()");
+
+test(function() {
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ assert_equals(ctx.lineDashOffset, 0);
+
+ ctx.setLineDash([15, 10]);
+ ctx.lineDashOffset = 5;
+ ctx.strokeRect(10,10,100,100);
+
+ var lineDash = ctx.getLineDash();
+ assert_array_equals(lineDash, [15, 10]);
+ assert_equals(ctx.lineDashOffset, 5);
+
+ ctx.setLineDash([5, 10, 15]);
+ ctx.strokeRect(20, 20, 120, 120);
+ lineDash = ctx.getLineDash();
+ assert_array_equals(lineDash, [5, 10, 15, 5, 10, 15]);
+
+ ctx.setLineDash(["1", 2]);
+ lineDash = ctx.getLineDash();
+ assert_array_equals(lineDash, [1, 2]);
+
+ ctx.clearRect(0, 0, 700, 700);
+ assert_equals(ctx.lineDashOffset, 5);
+
+ ctx.setLineDash([20, 10]);
+ ctx.lineDashOffset = 0;
+ // Make the test immune to plaform anti-aliasing discrepancies.
+ ctx.lineWidth = 4;
+ ctx.strokeStyle = '#00FF00';
+ ctx.strokeRect(10.5, 10.5, 30, 30);
+
+ _assertPixel(canvas, 25, 10, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 35, 10, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 40, 25, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 40, 35, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 25, 40, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 15, 40, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 10, 25, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 10, 15, 0, 0, 0, 0, 0);
+
+ // Verify that lineDashOffset works as expected.
+ ctx.lineDashOffset = 20;
+ ctx.strokeRect(50.5, 10.5, 30, 30);
+ _assertPixel(canvas, 55, 10, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 65, 10, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 80, 15, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 80, 25, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 75, 40, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 65, 40, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 50, 35, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 50, 25, 0, 255, 0, 255, 0);
+
+ // Verify negative lineDashOffset.
+ ctx.lineDashOffset = -10;
+ ctx.strokeRect(90.5, 10.5, 30, 30);
+ _assertPixel(canvas, 95, 10, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 105, 10, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 120, 15, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 120, 25, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 115, 40, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 105, 40, 0, 255, 0, 255, 0);
+ _assertPixel(canvas, 90, 35, 0, 0, 0, 0, 0);
+ _assertPixel(canvas, 90, 25, 0, 255, 0, 255, 0);
+
+ // Ensure that all zeros or negative pattern does not cause error state in
+ // context.
+ ctx.setLineDash([0, 0]);
+ ctx.strokeRect(130.5, 10.5, 10, 10);
+ ctx.setLineDash([-1]);
+ ctx.strokeRect(130.5, 10.5, 10, 10);
+ _assertPixel(canvas, 135, 15, 0, 0, 0, 0, 0);
+ ctx.fillStyle = '#00FF00';
+ ctx.fillRect(130, 10, 10, 10);
+ _assertPixel(canvas, 135, 15, 0, 255, 0, 255, 0);
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_001.htm b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_001.htm
new file mode 100644
index 0000000000..1763950d61
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_001.htm
@@ -0,0 +1,60 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: Shadows for linear gradients</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#shadows" />
+ <meta name="assert" content="Shadows must be drawn for linear gradients." />
+ <script type="text/javascript">
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done(function runTest() {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Draw a red rectangle.
+ ctx.fillStyle = "rgba(255, 0, 0, 1.0)";
+ ctx.fillRect(150, 0, 100, 50);
+
+ // Set shadow styles to draw a black shadow to overlap the red rectangle.
+ ctx.shadowOffsetX = 150;
+ ctx.shadowColor = "rgba(0, 0, 0, 1.0)";
+
+ // Draw a left to right, green-to-blue linear gradient.
+ var lingrad = ctx.createLinearGradient(0, 50, 100, 50);
+ lingrad.addColorStop(0, "rgba(0, 255, 0, 1.0)");
+ lingrad.addColorStop(1, "rgba(0, 0, 255, 1.0)");
+ ctx.fillStyle = lingrad;
+ ctx.fillRect(0, 0, 100, 50);
+
+ // Check the red is gone
+ var data = ctx.getImageData(150, 0, 100, 50);
+ for (var i = 0; i < data.data.length; i += 4) {
+ var r = data.data[i];
+ var g = data.data[i+1];
+ var b = data.data[i+2];
+ var a = data.data[i+3];
+ assert_equals(r, 0, "r channel");
+ assert_equals(g, 0, "g channel");
+ assert_equals(b, 0, "b channel");
+ assert_equals(a, 0xFF, "a channel");
+ }
+
+ for (var j = 0; j < data.data.length; j += 4) {
+ var r2 = data.data[j];
+ var g2 = data.data[j+1];
+ var b2 = data.data[j+2];
+ var a2 = data.data[j+3];
+ assert_false(r2 == 0xFF && g2 == 0 && b2 == 0 && a2 == 0xFF, "no red");
+ }
+ }));
+ }, "linear gradient fillRect draws shadow (black rectange)");
+ </script>
+ </head>
+ <body>
+ <p>Description: Shadows must be drawn for linear gradients.</p>
+ <p>Test passes if there is one gradient filled rectangle and one black rectangle, and no red seen on the page.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002-ref.htm
new file mode 100644
index 0000000000..0658be808e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002-ref.htm
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: Shadows for images</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#shadows" />
+ <meta name="assert" content="Shadows must be drawn for images." />
+ <script type="text/javascript">
+ function runTest() {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Draw a black rectangle image on the canvas.
+ var img = document.getElementById("imgBlackRect");
+ ctx.drawImage(img, 0, 0);
+ ctx.drawImage(img, 150, 0);
+ }
+ </script>
+
+ </head>
+ <body onload="runTest()">
+ <p>Description: Shadows must be drawn for images.</p>
+ <p>Test passes if two black rectangles are shown and there is no red visible on the page.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ <img id="imgBlackRect" style="display:none;" width="100" height="50" src="/images/black-rectangle.png">
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002.htm b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002.htm
new file mode 100644
index 0000000000..908fffea13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_002.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: Shadows for images</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#shadows" />
+ <link rel="match" href="canvas_shadows_002-ref.htm" />
+ <meta name="assert" content="Shadows must be drawn for images." />
+ <script type="text/javascript">
+ function runTest() {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Draw a red rectangle.
+ ctx.fillStyle = "rgba(255, 0, 0, 1.0)";
+ ctx.fillRect(150, 0, 100, 50);
+
+ // Set shadow styles to draw a black shadow to overlap the red rectangle.
+ ctx.shadowOffsetX = 150;
+ ctx.shadowColor = "rgba(0, 0, 0, 1.0)";
+
+ // Draw a black rectangle image on the canvas.
+ var img = document.getElementById("imgBlackRect");
+ ctx.drawImage(img, 0, 0);
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: Shadows must be drawn for images.</p>
+ <p>Test passes if two black rectangles are shown and there is no red visible on the page.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ <img id="imgBlackRect" style="display:none" width="100" height="50" src="/images/black-rectangle.png">
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors-expected.html b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors-expected.html
new file mode 100644
index 0000000000..e568aa9a5c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>System Colors work for Canvas Drop-Shadow Filters</title>
+</head>
+<body>
+<div style="width: 100px; height: 100px; background-color: black"></div>
+<div style="width: 100px; height: 100px; background-color: GrayText"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors.html b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors.html
new file mode 100644
index 0000000000..42978fb18f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/shadows/canvas_shadows_system_colors.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>System Colors work for Canvas Drop-Shadow Filters</title>
+ <link rel="match" href="canvas_shadows_system_colors-expected.html">
+</head>
+<body>
+<canvas id='c' width="100" height="200">
+<script>
+// See crbug.com/1226282 and crbug.com/1081945
+// A reference test is necessary because system colors do not have defined
+// numeric values. Here we're comparing 'GrayText' to css 'GrayText'.
+var ctx = document.getElementById('c').getContext('2d');
+ctx.filter = 'drop-shadow(0px 100px 0 GrayText)';
+ctx.fillRect(0,0,100,100);
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/shadows/shadowBlur_gaussian_tolerance.1.html b/testing/web-platform/tests/html/canvas/element/manual/shadows/shadowBlur_gaussian_tolerance.1.html
new file mode 100644
index 0000000000..eec27bf108
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/shadows/shadowBlur_gaussian_tolerance.1.html
@@ -0,0 +1,191 @@
+<!DOCTYPE HTML>
+<title>Test of canvas shadowBlur Gaussian blur pixel values</title>
+<meta charset=UTF-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<h1>Test of canvas shadowBlur Gaussian blur pixel values</h1>
+<script>
+
+/**
+ * See https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions
+ */
+function erf(x) {
+ if (x < 0) {
+ return -erf(-x);
+ }
+ var p = 0.3275911, a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741, a4 = -1.453152027, a5 = 1.061405429;
+ var t = 1 / (1 + p * x);
+ return 1 - Math.exp(-x * x) * t * (a1 + t * (a2 + t * (a3 + t * (a4 + t * a5))));
+}
+
+/**
+ * See https://en.wikipedia.org/wiki/Normal_distribution#Cumulative_distribution_function
+ * and https://en.wikipedia.org/wiki/Normal_distribution#Numerical_approximations_for_the_normal_CDF
+ */
+function standard_normal_distribution_cumulative(x) {
+ return 0.5 * (1 + erf(x / Math.SQRT2));
+}
+
+/**
+ * Verify a single pixel; helper for run_blur_test.
+ * params - same as run_blur_test
+ * row & col - relative to the corner of the rectangle being blurred
+ * actual - actual color found there on the canvas
+ */
+function test_pixel(params, row, col, shadowOffset, actual) {
+ var expected_gaussian;
+ if (params.expected_sigma > 0) {
+ // Compute positions within a standard normal distribution (i.e.,
+ // where mean (μ) is and standard deviation (σ) is 1) in both
+ // dimensions.
+ // Add 0.5 because we want the middle of the pixel rather than the edge.
+ var pos_x = (col - shadowOffset + 0.5) / params.expected_sigma;
+ var pos_y = (row - shadowOffset + 0.5) / params.expected_sigma;
+
+ // Find the expected color value based on a Gaussian blur function.
+ // Since we're blurring the corner of a "very large" rectangle, we
+ // can, instead of sampling all of the pixels, use the cumulative
+ // form of the normal (Gaussian) distribution and pass it the
+ // position of the color transition (the edges of the rectangle),
+ // since we know everything above and to the left of that position
+ // is one color, and everything that is either below or to the right
+ // of that position is another color.
+ //
+ // NOTE: This assumes color-interpolation happens in sRGB rather
+ // than linearRGB. The canvas spec doesn't appear to be clear on
+ // this point. If it were linearRGB, we'd need to apply the
+ // correction after doing this calculation. (No correction to the
+ // input is needed since the input is all 0 or 1.)
+ expected_gaussian = standard_normal_distribution_cumulative(-pos_x) *
+ standard_normal_distribution_cumulative(-pos_y);
+ } else {
+ if (col >= shadowOffset || row >= shadowOffset) {
+ expected_gaussian = 0;
+ } else {
+ expected_gaussian = 1;
+ }
+ }
+ // TODO: maybe also compute expected value by triple box blur?
+
+ /*
+ * https://html.spec.whatwg.org/multipage/canvas.html#when-shadows-are-drawn
+ * describes how to draw shadows in canvas. It says, among other things:
+ *
+ * Perform a 2D Gaussian Blur on B, using σ as the standard deviation.
+ *
+ * without giving *any* allowance for error.
+ *
+ * However, other specifications that require Gaussian blurs allow some
+ * error; https://www.w3.org/TR/css-backgrounds-3/#shadow-blur allows up to
+ * 5%, and https://drafts.fxtf.org/filter-effects/#feGaussianBlurElement
+ * allows use of a triple box blur which is within 3%.
+ *
+ * Since expecting zero error is unreasonable, this test tests for the least
+ * restrictive of these bounds, the 5% error.
+ *
+ * Note that this allows 5% error in the color component, but there's no
+ * tolerance for error in the position; see comment below about sizes.
+ */
+
+ // Allow any rounding direction.
+ var min_b = Math.max( 0, Math.floor((expected_gaussian - 0.05) * 255));
+ var max_b = Math.min(255, Math.ceil ((expected_gaussian + 0.05) * 255));
+ var min_r = 255 - max_b;
+ var max_r = 255 - min_b;
+
+ var pos = "at row " + row + " col " + col + " ";
+
+ assert_true(min_r <= actual.r && actual.r <= max_r,
+ pos + "red component " + actual.r + " should be between " +
+ min_r + " and " + max_r + " (inclusive).");
+ assert_true(min_b <= actual.b && actual.b <= max_b,
+ pos + "blue component " + actual.b + " should be between " +
+ min_b + " and " + max_b + " (inclusive).");
+ assert_equals(actual.g, 0, pos + "green component should be 0");
+ assert_equals(actual.a, 255, pos + "alpha component should be 255");
+}
+
+/**
+ * Run a test of a single shadowBlur drawing operation. Expects a
+ * parameters object containing:
+ * name - name of test
+ * canvas_width - width of canvas to create
+ * canvas_height - height of canvas to create
+ * shadowBlur - shadowBlur to use for the test drawing operation
+ * expected_sigma - the standard deviation of the gaussian function
+ * that shadowBlur is expected to produce
+ * pixel_skip - how many pixels to skip when sampling results. Should
+ * be relatively prime with canvas_width.
+ */
+function run_blur_test(params) {
+ test(function() {
+ var canvas = document.createElement("canvas");
+ canvas.setAttribute("width", params.canvas_width);
+ canvas.setAttribute("height", params.canvas_height);
+ document.body.appendChild(canvas);
+ var cx = canvas.getContext("2d");
+
+ cx.fillStyle = "red";
+ cx.fillRect(0, 0, params.canvas_width, params.canvas_height);
+
+ // Fill a huge rect just to the top and left of the canvas, with its shadow
+ // blur centered at the middle of the canvas.
+ let edge = Math.floor(params.canvas_width / 2); // position of vertical
+ let big = Math.max(Math.ceil(params.expected_sigma * 1000),
+ params.canvas_width,
+ params.canvas_height);
+ cx.shadowBlur = params.shadowBlur;
+ cx.fillStyle = "green";
+ cx.shadowColor = "blue";
+ cx.shadowOffsetX = edge;
+ cx.shadowOffsetY = edge;
+ cx.fillRect(-big, -big, big, big);
+
+ var imageData =
+ cx.getImageData(0, 0, params.canvas_width, params.canvas_height);
+ for (var i = 0, i_max = params.canvas_width * params.canvas_height;
+ i < i_max;
+ i += params.pixel_skip) {
+ var row = Math.floor(i / params.canvas_width);
+ var col = i - row * params.canvas_width;
+
+ var actual = { r: imageData.data[i * 4],
+ g: imageData.data[i * 4 + 1],
+ b: imageData.data[i * 4 + 2],
+ a: imageData.data[i * 4 + 3] };
+
+ test_pixel(params, row, col, edge, actual);
+ }
+ }, "shadowBlur Gaussian pixel values for " + params.name);
+}
+
+run_blur_test({
+ name: "no blur",
+ canvas_width: 4,
+ canvas_height: 4,
+ shadowBlur: 0,
+ expected_sigma: 0,
+ pixel_skip: 1
+});
+run_blur_test({
+ name: "small blur",
+ canvas_width: 20,
+ canvas_height: 20,
+ // Try to test something smaller than 8 due to historic change in
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=10647 , but not too
+ // small, to avoid the error from rounding to individual pixels worth
+ // of box blur.
+ shadowBlur: 6,
+ expected_sigma: 3,
+ pixel_skip: 3
+});
+run_blur_test({
+ name: "large blur",
+ canvas_width: 100,
+ canvas_height: 100,
+ shadowBlur: 30,
+ expected_sigma: 15,
+ pixel_skip: 13
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected-ref.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected-ref.html
new file mode 100644
index 0000000000..b36d29b97f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTML Canvas reference</title>
+<body>
+<canvas id=c>
+</body>
+<script>
+var c = document.getElementById("c");
+var ctx = c.getContext("2d");
+ctx.font = "50px monospace";
+ctx.fillText("Hello", 50, 75);
+ctx.font = "25px serif";
+ctx.fillText("World", 100, 100);
+c.style.border = "3px solid cyan";
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected.html
new file mode 100644
index 0000000000..a1715f6663
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.disconnected.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTML Canvas testcase: canvas element not in document</title>
+<link rel=match href="canvas.2d.disconnected-ref.html">
+<meta name=fuzzy content="maxDifference=0-23;totalPixels=0-829">
+<body>
+</body>
+<script>
+var d = new Document();
+var c = d.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+var ctx = c.getContext("2d");
+ctx.font = "50px monospace";
+ctx.fillText("Hello", 50, 75);
+ctx.font = "25px serif";
+ctx.fillText("World", 100, 100);
+c.toBlob((blob) => {
+ var img = document.createElement("img");
+ const url = URL.createObjectURL(blob);
+ img.src = url;
+ img.style.border = "3px solid cyan";
+ document.body.appendChild(img);
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch-ref.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch-ref.html
new file mode 100644
index 0000000000..00ecdccad3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+function draw() {
+ ctx.font = '25px test';
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+document.fonts.add(f);
+
+f.load().then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.condensed.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.condensed.html
new file mode 100644
index 0000000000..72db41f007
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.condensed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "condensed";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.expanded.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.expanded.html
new file mode 100644
index 0000000000..8a13ba13fc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.expanded.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "expanded";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-condensed.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-condensed.html
new file mode 100644
index 0000000000..afa910f62c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-condensed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = extra-condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "extra-condensed";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "extra-condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-expanded.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-expanded.html
new file mode 100644
index 0000000000..d10d4d9312
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.extra-expanded.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = extra-expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "extra-expanded";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "extra-expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.normal.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.normal.html
new file mode 100644
index 0000000000..e8fd66acad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.normal.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = normal and shows as fail for
+// fontStretch = expanded or condensed.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/fail.woff)');
+f.stretch = "expanded";
+document.fonts.add(f);
+
+var f1 = new FontFace('test', 'url(/fonts/pass.woff)');
+document.fonts.add(f1);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+f2.stretch = "condensed";
+document.fonts.add(f2);
+
+Promise.all([f.load(), f1.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-condensed.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-condensed.html
new file mode 100644
index 0000000000..2ac9719595
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-condensed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = semi-condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "semi-condensed";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "semi-condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-expanded.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-expanded.html
new file mode 100644
index 0000000000..3c9fa27894
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.semi-expanded.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = semi-expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "semi-expanded";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "semi-expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-condensed.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-condensed.html
new file mode 100644
index 0000000000..2b0426e976
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-condensed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = ultra-condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "ultra-condensed";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "ultra-condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-expanded.html b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-expanded.html
new file mode 100644
index 0000000000..5b4979de86
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas.2d.fontStretch.ultra-expanded.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = ultra-expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "ultra-expanded";
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "ultra-expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001-ref.htm
new file mode 100644
index 0000000000..1a19757e00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001-ref.htm
@@ -0,0 +1,22 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: Ignore property-independent style sheet syntax "inherit" in Text (reference)</title>
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ ctx.font = "40px Ahem";
+ ctx.fillText("Filler", 5, 50);
+ ctx.fillText("Filler", 5, 100);
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: Ignore "inherit" property-independent style sheet syntax without assigning a new font value.</p>
+ <p>Test passes if there are two identical black boxes below.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001.htm b/testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001.htm
new file mode 100644
index 0000000000..923ce71c07
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/text/canvas_text_font_001.htm
@@ -0,0 +1,33 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: Ignore property-independent style sheet syntax "inherit" in Text</title>
+ <link rel="match" href="canvas_text_font_001-ref.htm" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-font" />
+ <meta name="assert" content=": Ignore 'inherit' property-independent style sheet syntax without assigning a new font value." />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Assign a valid font.
+ ctx.font = "40px Ahem";
+
+ // Assign property-independent style sheet syntax 'inherit' as font (this must be ignored).
+ ctx.font = "20px inherit";
+ ctx.fillText("Filler", 5, 50);
+
+ // Assign a valid font which was used earlier.
+ ctx.font = "40px Ahem";
+ ctx.fillText("Filler", 5, 100);
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: Ignore "inherit" property-independent style sheet syntax without assigning a new font value.</p>
+ <p>Test passes if there are two identical black boxes below.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.state.saverestore.imageSmoothingEnabled.html b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.state.saverestore.imageSmoothingEnabled.html
new file mode 100644
index 0000000000..e99be83d5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.state.saverestore.imageSmoothingEnabled.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CanvasRenderingContext2D imageSmoothingEnabled save/restore test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#image-smoothing">
+<script>
+function createTestImage() {
+ var image = document.createElement('canvas');
+ var imgctx = image.getContext('2d');
+ imgctx.fillStyle = "#F00";
+ imgctx.fillRect(0, 0, 2, 2);
+ imgctx.fillStyle = "#0F0";
+ imgctx.fillRect(0, 0, 1, 1);
+ return image;
+}
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ ctx.save();
+ ctx.imageSmoothingEnabled = false;
+ ctx.restore();
+ assert_equals(ctx.imageSmoothingEnabled, true);
+}, "Test that restore() undoes any modifications to imageSmoothingEnabled.");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ ctx.imageSmoothingEnabled = false;
+ var old = ctx.imageSmoothingEnabled;
+ ctx.save();
+ assert_equals(ctx.imageSmoothingEnabled, old);
+ ctx.restore();
+}, "Test that save() doesn't modify the values of imageSmoothingEnabled.");
+
+test(function() {
+ var ctx = document.createElement('canvas').getContext('2d');
+ ctx.imageSmoothingEnabled = false;
+ ctx.save();
+ ctx.imageSmoothingEnabled = true;
+ ctx.restore();
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(0, 0, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that restoring actually changes smoothing and not just the attribute value.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.zero.size.canvas.html b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.zero.size.canvas.html
new file mode 100644
index 0000000000..273e5c7484
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/2d.zero.size.canvas.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+</head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<canvas id="canvas" width="0" height="0"></canvas>
+<body>
+<script>
+test(function() {
+ var context = document.getElementById("canvas").getContext("2d");
+ context.fillStyle = "green";
+}, 'This test ensures that accessing the context of a zero sized canvas does not crash.');
+</script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001-ref.htm
new file mode 100644
index 0000000000..aee610d2ec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001-ref.htm
@@ -0,0 +1,11 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: restore() pops top entry in drawing state stack</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ </head>
+ <body>
+ <p>Description: restore() pops the top entry in the drawing state stack.</p>
+ <div><img src='/images/threecolors.png' alt='3 colors'></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001.htm b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001.htm
new file mode 100644
index 0000000000..6d5a3cb20f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/the-canvas-state/canvas_state_restore_001.htm
@@ -0,0 +1,42 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: restore() pops top entry in drawing state stack</title>
+ <link rel="match" href="canvas_state_restore_001-ref.htm">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#the-canvas-state" />
+ <meta name="assert" content="restore() pops the top entry in the drawing state stack." />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+ ctx.fillStyle = "rgba(255, 0, 0, 1.0)";
+ ctx.fillRect(0, 0, 99, 50);
+
+ // Save colors to the stack as separate drawing states.
+ ctx.fillStyle = "rgba(255, 255, 0, 1.0)";
+ ctx.save();
+ ctx.fillStyle = "rgba(0, 0, 255, 1.0)";
+ ctx.save();
+ ctx.fillStyle = "rgba(0, 255, 0, 1.0)";
+ ctx.save();
+
+ // Modify the current fillStyle.
+ ctx.fillStyle = "rgba(255, 0, 0, 1.0)";
+
+ // Restore the drawing states previously saved and draw with them.
+ ctx.restore();
+ ctx.fillRect(66, 0, 33, 50);
+ ctx.restore();
+ ctx.fillRect(33, 0, 33, 50);
+ ctx.restore();
+ ctx.fillRect(0, 0, 33, 50);
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: restore() pops the top entry in the drawing state stack.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/transformations/2d.transformation.getTransform.html b/testing/web-platform/tests/html/canvas/element/manual/transformations/2d.transformation.getTransform.html
new file mode 100644
index 0000000000..664efd50e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/transformations/2d.transformation.getTransform.html
@@ -0,0 +1,39 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Ensure that context2d.getTransform works
+const epsilon = 1e-5;
+const canvas = document.createElement('canvas');
+const ctx = canvas.getContext('2d');
+
+test(function(t) {
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that an untransformed matrix is identity");
+
+ ctx.scale(2, 3);
+ transform = ctx.getTransform();
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that context2d scaling works");
+
+ ctx.rotate(Math.PI/2);
+ transform = ctx.getTransform();
+ assert_array_approx_equals(ctx.getTransform().toFloat32Array(),
+ [0, 3, 0, 0, -2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], epsilon,
+ "Assert that context2d rotate works");
+
+ ctx.translate(1, -1);
+ transform = ctx.getTransform();
+ assert_array_approx_equals(ctx.getTransform().toFloat32Array(),
+ [0, 3, 0, 0, -2, 0, 0, 0, 0, 0, 1, 0, 2, 3, 0, 1], epsilon,
+ "Assert context2d translate works.");
+
+ ctx.resetTransform();
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that a reset matrix is identity");
+}, 'This test ensures that getTransform works correctly.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001-ref.html b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001-ref.html
new file mode 100644
index 0000000000..caeea04cef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<body>
+<style>
+ html, body, div {
+ margin: 0;
+ padding: 0;
+ }
+ div {
+ width: 75px;
+ height: 75px;
+ float: left;
+ }
+</style>
+
+<div style="background-color:red"></div>
+<div style="clear:left"></div>
+<div style="background-color:blue"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001.html b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001.html
new file mode 100644
index 0000000000..c12acbf6f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_reset_001.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="match" href="canvas_transformations_reset_001-ref.html">
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ }
+</style>
+<canvas id="c" width="150" height="150"></canvas>
+<script>
+var c = document.getElementById("c");
+var ctx = c.getContext("2d");
+
+ctx.translate(75, 75);
+ctx.fillStyle = 'blue';
+ctx.fillRect(0, 0, 75, 75);
+
+ctx.resetTransform();
+ctx.fillStyle = 'red';
+ctx.fillRect(0, 0, 75, 75);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001-ref.htm b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001-ref.htm
new file mode 100644
index 0000000000..1201bcca9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001-ref.htm
@@ -0,0 +1,11 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>HTML5 Canvas Test: scale() transformation</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ </head>
+ <body>
+ <p>Description: The scale(x, y) method must add the scaling transformation described by the arguments to the transformation matrix.</p>
+ <div><img alt='black rectangle' src="/images/black-rectangle.png"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001.htm b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001.htm
new file mode 100644
index 0000000000..73f71351ea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/transformations/canvas_transformations_scale_001.htm
@@ -0,0 +1,31 @@
+<!doctype HTML>
+
+<html>
+ <head>
+ <title>HTML5 Canvas Test: scale() transformation</title>
+ <link rel="match" href="canvas_transformations_scale_001-ref.htm">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="http://www.w3.org/TR/2dcontext/#dom-context-2d-scale" />
+ <meta name="assert" content="The scale(x, y) method must add the scaling transformation described by the arguments to the transformation matrix." />
+ <script type="text/javascript">
+ function runTest()
+ {
+ var canvas = document.getElementById("canvas1");
+ var ctx = canvas.getContext("2d");
+
+ // Draw a red rectangle.
+ ctx.fillStyle = "rgba(255, 0, 0, 1.0)";
+ ctx.fillRect(0, 0, 100, 50);
+
+ // Draw a black rectangle with scaling.
+ ctx.fillStyle = "rgba(0, 0, 0, 1.0)";
+ ctx.scale(2, 2);
+ ctx.fillRect(0, 0, 50, 25);
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <p>Description: The scale(x, y) method must add the scaling transformation described by the arguments to the transformation matrix.</p>
+ <canvas id="canvas1" width="300" height="150">Browser does not support HTML5 Canvas.</canvas>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/transformations/transform_a.html b/testing/web-platform/tests/html/canvas/element/manual/transformations/transform_a.html
new file mode 100644
index 0000000000..8c1f59efda
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/transformations/transform_a.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel=match href=transform_ref.html>
+<style>
+html, body {
+ margin: 0;
+}
+</style>
+<canvas id=c width=400 height=300></canvas>
+<script>
+var canvas = document.getElementById('c');
+var ctx = canvas.getContext('2d');
+ctx.scale(3, 3);
+ctx.fillStyle = 'rgb(255, 0, 0)';
+ctx.beginPath();
+ctx.moveTo(10, 10);
+ctx.bezierCurveTo(10, 10, 20, 10, 20, 10);
+ctx.bezierCurveTo(20, 10, 20, 20, 20, 20);
+ctx.bezierCurveTo(20, 20, 10, 20, 10, 20);
+ctx.closePath();
+ctx.fill();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/transformations/transform_ref.html b/testing/web-platform/tests/html/canvas/element/manual/transformations/transform_ref.html
new file mode 100644
index 0000000000..2a166c36ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/transformations/transform_ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<style>
+html, body {
+ margin: 0;
+}
+section {
+ position: absolute;
+ background: rgb(255, 0, 0);
+ width: 30px;
+ height: 30px;
+ top: 30px;
+ left: 30px;
+}
+</style>
+<section></section>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1-expected.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1-expected.htm
new file mode 100644
index 0000000000..3272f32fb8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1-expected.htm
@@ -0,0 +1,10 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ </head>
+ <body>
+ <p>There should be no text below this, because the text is inside a canvas element.
+ The canvas is never closed, and the rest of the body ends up inside it.</p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1.htm
new file mode 100644
index 0000000000..5e0c914755
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-1.htm
@@ -0,0 +1,14 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ <link rel="match" href="unclosed-canvas-1-expected.htm">
+ <meta name="assert" content="Test what if canvas tag is unclosed in tag p" />
+ <script type="text/javascript"></script>
+ </head>
+ <body>
+ <p>There should be no text below this, because the text is inside a canvas element.
+ The canvas is never closed, and the rest of the body ends up inside it. </p>
+ <canvas>This text should NOT be visible if JavaScript is enabled.
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2-expected.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2-expected.htm
new file mode 100644
index 0000000000..fd48cd0db1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2-expected.htm
@@ -0,0 +1,14 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ </head>
+ <body>
+ <div><canvas></canvas></div>
+ <p>This text should be visible, even though it's preceded by an unclosed canvas tag,
+ because of the &lt;/div&gt; that closes an element opened before the canvas.
+ There's nothing special about div; we get the same results with other types of
+ elements.
+ </p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2.htm
new file mode 100644
index 0000000000..fb329e73d6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-2.htm
@@ -0,0 +1,15 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ <link rel="match" href="unclosed-canvas-2-expected.htm">
+ <meta name="assert" content="Test what if canvas tag is unclosed in tag div" />
+ <script type="text/javascript"></script>
+ </head>
+ <body>
+ <div><canvas></div>
+ <p>This text should be visible, even though it's preceded by an unclosed canvas tag,
+ because of the &lt;/div&gt; that closes an element opened before the canvas.
+ There's nothing special about div; we get the same results with other types of elements.</p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3-expected.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3-expected.htm
new file mode 100644
index 0000000000..efd239b4fb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3-expected.htm
@@ -0,0 +1,13 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ </head>
+ <body>
+ <p>There should be no text below this, because the text is inside a canvas element
+ and the &lt;/div&gt; that's also inside the canvas element does not close an open
+ element. The canvas is never closed, and the rest of the body ends up inside it.
+ There's nothing special about div; we get the same results with other types of
+ elements.</p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3.htm
new file mode 100644
index 0000000000..8ffaa0f9eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-3.htm
@@ -0,0 +1,16 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ <link rel="match" href="unclosed-canvas-3-expected.htm">
+ <meta name="assert" content="Test what if canvas tag is unclosed in unclosed div" />
+ <script type="text/javascript"></script>
+ </head>
+ <body>
+ <p>There should be no text below this, because the text is inside a canvas element
+ and the &lt;/div&gt; that's also inside the canvas element does not close an open element.
+ The canvas is never closed, and the rest of the body ends up inside it.
+ There's nothing special about div; we get the same results with other types of elements.</p>
+ <canvas></div>This text should NOT be visible if JavaScript is enabled.
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4-expected.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4-expected.htm
new file mode 100644
index 0000000000..492449261e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4-expected.htm
@@ -0,0 +1,14 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ </head>
+ <body>
+ <p>There should be no text below this, because the text is inside a canvas element
+ and the &lt;/div&gt; that's also inside the canvas element does not close an open
+ element. The canvas is never closed, and the rest of the body ends up inside it.
+ There's nothing special about div; we get the same results with other types of
+ elements. The fact that the canvas tag uses XML self-closing syntax has no effect.
+ </p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4.htm b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4.htm
new file mode 100644
index 0000000000..aa5fd14438
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/unclosed-canvas-4.htm
@@ -0,0 +1,17 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>unclosed canvas tag in body</title>
+ <link rel="match" href="unclosed-canvas-4-expected.htm">
+ <meta name="assert" content="Test what if canvas tag is unclosed in body" />
+ <script type="text/javascript"></script>
+ </head>
+ <body>
+ <p>There should be no text below this, because the text is inside a canvas element
+ and the &lt;/div&gt; that's also inside the canvas element does not close an open element.
+ The canvas is never closed, and the rest of the body ends up inside it.
+ There's nothing special about div; we get the same results with other types of elements.
+ The fact that the canvas tag uses XML self-closing syntax has no effect.</p>
+ <canvas/></div>This text should NOT be visible if JavaScript is enabled.
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/ImageData-fidelity.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/ImageData-fidelity.html
new file mode 100644
index 0000000000..c2d158f893
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/ImageData-fidelity.html
@@ -0,0 +1,126 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var image = new Image();
+ // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+ // The image has no embedded color profile.
+ image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAx" +
+ "WXB3AAAAG0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAAElFTkSuQmCC";
+
+ image.onload = function() {
+ var canvas = document.createElement('canvas');
+ canvas.width = 256;
+ canvas.height = 1;
+ var ctx = canvas.getContext('2d');
+ ctx.drawImage(image, 0, 0);
+ var img = ctx.getImageData(0, 0, 256, 1);
+ t.step(function() {
+ for (var i = 0; i < 256; i++) {
+ assert_equals(img.data[4 * i], i, "red component");
+ assert_equals(img.data[4 * i + 1], i, "green component");
+ assert_equals(img.data[4 * i + 2], i, "blue component");
+ assert_equals(img.data[4 * i + 3], 255, "alpha component");
+ }
+ });
+ t.done();
+ }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+ "when image metadata has no color space and canvas uses the default " +
+ "color space.");
+
+async_test(function(t) {
+ var image = new Image();
+ // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+ // The image has an embedded sRGB color profile.
+ image.src =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAxWXB3AAAA" +
+ "G0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAo3pUWHRSYXcgcHJvZm" +
+ "lsZSB0eXBlIEFQUDEAAHicZU5bCsMwDPv3KXoEv/I6TrampTC20ft/LE7WETJBkK1YQrCX" +
+ "ZzmP+/I+X9vxKLAYyCGoC9En77FCV10ROWNHrM8hUW7cQZ00V/026tDZMRKbUQYDt4lJJr" +
+ "2FxeCTJc5BV4svNE4Nxl1Tn8N1LCgMIoKJ2sHvo25sHfK/odYT02luCWMP+AA5M0KbNr61" +
+ "PwAAAABJRU5ErkJggg==";
+
+ image.onload = function() {
+ var canvas = document.createElement('canvas');
+ canvas.width = 256;
+ canvas.height = 1;
+ var ctx = canvas.getContext('2d', {colorSpace: 'srgb'});
+ ctx.drawImage(image, 0, 0);
+ var img = ctx.getImageData(0, 0, 256, 1);
+ t.step(function() {
+ for (var i = 0; i < 256; i++) {
+ assert_equals(img.data[4 * i], i, "red component");
+ assert_equals(img.data[4 * i + 1], i, "green component");
+ assert_equals(img.data[4 * i + 2], i, "blue component");
+ assert_equals(img.data[4 * i + 3], 255, "alpha component");
+ }
+ });
+ t.done();
+ }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+ "when image metadata has srgb color space and canvas uses the srgb " +
+ "color space.");
+
+async_test(function(t) {
+ var image = new Image();
+ // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+ // The image has no embedded color profile.
+ image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAx" +
+ "WXB3AAAAG0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAAElFTkSuQmCC";
+
+ image.onload = function() {
+ var canvas = document.createElement('canvas');
+ canvas.width = 256;
+ canvas.height = 1;
+ var ctx = canvas.getContext('2d', {colorSpace: 'srgb'});
+ ctx.drawImage(image, 0, 0);
+ var img = ctx.getImageData(0, 0, 256, 1);
+ t.step(function() {
+ for (var i = 0; i < 256; i++) {
+ assert_equals(img.data[4 * i], i, "red component");
+ assert_equals(img.data[4 * i + 1], i, "green component");
+ assert_equals(img.data[4 * i + 2], i, "blue component");
+ assert_equals(img.data[4 * i + 3], 255, "alpha component");
+ }
+ });
+ t.done();
+ }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+ "when image metadata has no color space and canvas uses the srgb " +
+ "color space.");
+
+
+async_test(function(t) {
+ var image = new Image();
+ // This image is 256 by 1 and contains an opaque grayscale ramp from 0 to 255.
+ // The image has an embedded sRGB color profile.
+ image.src =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAxWXB3AAAA" +
+ "G0lEQVQ4T2NkYGD4z8jIyDCKR8NgNA2MvDQAAPiPA/5tZ8G+AAAAo3pUWHRSYXcgcHJvZm" +
+ "lsZSB0eXBlIEFQUDEAAHicZU5bCsMwDPv3KXoEv/I6TrampTC20ft/LE7WETJBkK1YQrCX" +
+ "ZzmP+/I+X9vxKLAYyCGoC9En77FCV10ROWNHrM8hUW7cQZ00V/026tDZMRKbUQYDt4lJJr" +
+ "2FxeCTJc5BV4svNE4Nxl1Tn8N1LCgMIoKJ2sHvo25sHfK/odYT02luCWMP+AA5M0KbNr61" +
+ "PwAAAABJRU5ErkJggg==";
+
+ image.onload = function() {
+ var canvas = document.createElement('canvas');
+ canvas.width = 256;
+ canvas.height = 1;
+ var ctx = canvas.getContext('2d');
+ ctx.drawImage(image, 0, 0);
+ var img = ctx.getImageData(0, 0, 256, 1);
+ t.step(function() {
+ for (var i = 0; i < 256; i++) {
+ assert_equals(img.data[4 * i], i, "red component");
+ assert_equals(img.data[4 * i + 1], i, "green component");
+ assert_equals(img.data[4 * i + 2], i, "blue component");
+ assert_equals(img.data[4 * i + 3], 255, "alpha component");
+ }
+ });
+ t.done();
+ }
+}, "Verify that drawImage->getImageData round trip preserves color values " +
+ "when image metadata has srgb color space and canvas uses the default " +
+ "color space.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-Blob.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-Blob.html
new file mode 100644
index 0000000000..9b96b1a6d2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-Blob.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that drawing ImageBitmaps with different image Blob source bit depths
+// and color profiles into sRGB and Display P3 canvases works, by reading pixels
+// with getImageData() as sRGB and Display P3 values.
+for (let [filename, expectedPixels] of Object.entries(imageTests)) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ for (let cropSource of [false, true]) {
+ async_test(function(t) {
+ fetch(`resources/${filename}`)
+ .then(t.step_func(response => response.blob()))
+ .then(t.step_func(blob => (cropSource ? createImageBitmap(blob, 1, 1, 1, 1) : createImageBitmap(blob))))
+ .then(t.step_func_done(function(imageBitmap) {
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+
+ let ctx = canvas.getContext("2d", { colorSpace: contextColorSpace });
+ ctx.drawImage(imageBitmap, 0, 0);
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, { colorSpace: imageDataColorSpace });
+
+ let expected = expectedPixels[`${contextColorSpace} ${imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+ }));
+ }, `${filename}, Context ${contextColorSpace}, ImageData ${imageDataColorSpace}, cropSource=${cropSource}`);
+ }
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageBitmap.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageBitmap.html
new file mode 100644
index 0000000000..0bd18e9beb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageBitmap.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that drawing ImageBitmaps created from other ImageBitamps with different
+// image source bit depths and color profiles into sRGB and Display P3
+// canvases works, by reading pixels with getImageData() as sRGB and Display P3
+// values.
+for (let [filename, expectedPixels] of Object.entries(imageTests)) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ for (let cropSource of [false, true]) {
+ async_test(function(t) {
+ let image = new Image();
+ image.onload = t.step_func_done(function() {
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+
+ let ctx = canvas.getContext("2d", { colorSpace: contextColorSpace });
+
+ createImageBitmap(image).then(t.step_func(function(sourceImageBitmap) {
+ let imageBitmapPromise;
+ if (cropSource)
+ imageBitmapPromise = createImageBitmap(sourceImageBitmap, 1, 1, 1, 1);
+ else
+ imageBitmapPromise = createImageBitmap(sourceImageBitmap);
+
+ imageBitmapPromise.then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, { colorSpace: imageDataColorSpace });
+
+ let expected = expectedPixels[`${contextColorSpace} ${imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+ }));
+ }));
+ });
+ image.src = `resources/${filename}`;
+ }, `${filename}, Context ${contextColorSpace}, ImageData ${imageDataColorSpace}, cropSource=${cropSource}`);
+ }
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageData.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageData.html
new file mode 100644
index 0000000000..2216b46dd8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-ImageData.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that ImageBitmaps created from ImageData sources with different color
+// spaces can be drawn into sRGB and Display P3 canvases, by reading pixels with
+// getImageData() as sRGB and Display P3 values.
+function runTest(sourceColorSpace, destinationColorSpace, colors) {
+ for (let [sourceColorString, expectedColor] of Object.entries(colors)) {
+ for (let cropSource of [false, true]) {
+ async_test(function(t) {
+ let sourceColor = sourceColorString.split(",").map(x => +x);
+
+ let sourceImageData = new ImageData(4, 4, { colorSpace: sourceColorSpace });
+ for (let i = 0; i < 4 * 4 * 4; i += 4) {
+ for (let c = 0; c < 4; ++c)
+ sourceImageData.data[i + c] = sourceColor[c];
+ }
+
+ let imageBitmapPromise;
+ if (cropSource)
+ imageBitmapPromise = createImageBitmap(sourceImageData, 2, 2, 2, 2);
+ else
+ imageBitmapPromise = createImageBitmap(sourceImageData);
+
+ imageBitmapPromise.then(t.step_func_done(function(imageBitmap) {
+ let destination = document.createElement("canvas");
+ destination.width = 2;
+ destination.height = 2;
+
+ let destinationContext = destination.getContext("2d", { colorSpace: destinationColorSpace });
+ destinationContext.drawImage(imageBitmap, 0, 0);
+
+ let destinationImageData = destinationContext.getImageData(1, 1, 1, 1);
+
+ assert_true(pixelsApproximatelyEqual(destinationImageData.data, expectedColor), `Actual pixel value ${[...destinationImageData.data]} is approximately equal to ${expectedColor}.`);
+ }));
+ }, `Source ${sourceColorSpace}, destination ${destinationColorSpace}, color ${sourceColorString}, cropSource=${cropSource}`);
+ }
+ }
+}
+
+runTest("srgb", "display-p3", fromSRGBToDisplayP3);
+runTest("display-p3", "srgb", fromDisplayP3ToSRGB);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-canvas.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-canvas.html
new file mode 100644
index 0000000000..f3156349ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-canvas.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that ImageBitmaps created from canvas sources with different color
+// spaces can be drawn into sRGB and Display P3 canvases, by reading pixels with
+// getImageData() as sRGB and Display P3 values.
+function runTest(sourceColorSpace, destinationColorSpace, colors) {
+ for (let [sourceColorString, expectedColor] of Object.entries(colors)) {
+ for (let cropSource of [false, true]) {
+ async_test(function(t) {
+ let source = document.createElement("canvas");
+ source.width = 4;
+ source.height = 4;
+
+ let sourceContext = source.getContext("2d", { colorSpace: sourceColorSpace });
+
+ let sourceColor = sourceColorString.split(",").map(x => +x);
+
+ let sourceImageData = new ImageData(4, 4, { colorSpace: sourceColorSpace });
+ for (let i = 0; i < 4 * 4 * 4; i += 4) {
+ for (let c = 0; c < 4; ++c)
+ sourceImageData.data[i + c] = sourceColor[c];
+ }
+ sourceContext.putImageData(sourceImageData, 0, 0);
+
+ let imageBitmapPromise;
+ if (cropSource)
+ imageBitmapPromise = createImageBitmap(source, 2, 2, 2, 2);
+ else
+ imageBitmapPromise = createImageBitmap(source);
+
+ imageBitmapPromise.then(t.step_func_done(function(imageBitmap) {
+ let destination = document.createElement("canvas");
+ destination.width = 2;
+ destination.height = 2;
+
+ let destinationContext = destination.getContext("2d", { colorSpace: destinationColorSpace });
+ destinationContext.drawImage(imageBitmap, 0, 0);
+
+ let destinationImageData = destinationContext.getImageData(1, 1, 1, 1);
+
+ assert_true(pixelsApproximatelyEqual(destinationImageData.data, expectedColor), `Actual pixel value ${[...destinationImageData.data]} is approximately equal to ${expectedColor}.`);
+ }));
+ }, `Source ${sourceColorSpace}, destination ${destinationColorSpace}, color ${sourceColorString}, cropSource=${cropSource}`);
+ }
+ }
+}
+
+runTest("srgb", "display-p3", fromSRGBToDisplayP3);
+runTest("display-p3", "srgb", fromDisplayP3ToSRGB);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-cloned.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-cloned.html
new file mode 100644
index 0000000000..cf4f38f8db
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-cloned.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that drawing structured cloned ImageBitmaps with different image source
+// bit depths and color profiles into sRGB and Display P3 canvases works, by
+// reading pixels with getImageData() as sRGB and Display P3 values.
+
+let nextTestID = 0;
+
+class Test {
+ constructor(testConfiguration) {
+ Object.assign(this, testConfiguration);
+ this.testID = nextTestID++;
+ }
+
+ run() {
+ let self = this;
+ async_test(function(t) {
+ self.t = t;
+ self.image = new Image();
+ self.image.onload = t.step_func(self.onImageLoaded.bind(self));
+ self.image.src = `resources/${self.filename}`;
+ }, `${this.filename}, Context ${this.contextColorSpace}, ImageData ${this.imageDataColorSpace}, cropSource=${this.cropSource}`);
+ }
+
+ onImageLoaded() {
+ let imageBitmapPromise;
+ if (this.cropSource)
+ imageBitmapPromise = createImageBitmap(this.image, 1, 1, 1, 1);
+ else
+ imageBitmapPromise = createImageBitmap(this.image);
+ imageBitmapPromise.then(this.t.step_func(this.onImageBitmapCreated.bind(this)));
+ }
+
+ onImageBitmapCreated(imageBitmap) {
+ window.addEventListener("message", this.t.step_func(this.onMessage.bind(this)));
+ window.postMessage({ imageBitmap, testID: this.testID });
+ }
+
+ onMessage(message) {
+ if (message.data.testID != this.testID)
+ return;
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+
+ let ctx = canvas.getContext("2d", { colorSpace: this.contextColorSpace });
+ ctx.drawImage(message.data.imageBitmap, 0, 0);
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, { colorSpace: this.imageDataColorSpace });
+
+ let expected = this.expectedPixels[`${this.contextColorSpace} ${this.imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+
+ this.t.done();
+ }
+}
+
+for (let [filename, expectedPixels] of Object.entries(imageTests)) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ for (let cropSource of [false, true])
+ new Test({ filename, expectedPixels, contextColorSpace, imageDataColorSpace, cropSource }).run();
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-image.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-image.html
new file mode 100644
index 0000000000..e7e85c915d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-image.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that drawing ImageBitmaps with different image source bit depths and
+// color profiles into sRGB and Display P3 canvases works, by reading pixels
+// with getImageData() as sRGB and Display P3 values.
+for (let [filename, expectedPixels] of Object.entries(imageTests)) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ for (let cropSource of [false, true]) {
+ async_test(function(t) {
+ let image = new Image();
+ image.onload = t.step_func(function() {
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+
+ let ctx = canvas.getContext("2d", { colorSpace: contextColorSpace });
+
+ let imageBitmapPromise;
+ if (cropSource)
+ imageBitmapPromise = createImageBitmap(image, 1, 1, 1, 1);
+ else
+ imageBitmapPromise = createImageBitmap(image);
+
+ imageBitmapPromise.then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, { colorSpace: imageDataColorSpace });
+
+ let expected = expectedPixels[`${contextColorSpace} ${imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+ }));
+ });
+ image.src = `resources/${filename}`;
+ }, `${filename}, Context ${contextColorSpace}, ImageData ${imageDataColorSpace}, cropSource=${cropSource}`);
+ }
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-video.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-video.html
new file mode 100644
index 0000000000..3e86bb5596
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-ImageBitmap-video.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<body>
+<script>
+// Test that drawing ImageBitmaps with different video source color profiles
+// into sRGB and Display P3 canvases works, by reading pixels with
+// getImageData() as sRGB and Display P3 values.
+for (let [filenameBase, expectedPixels] of Object.entries(videoTests)) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ for (let cropSource of [false, true]) {
+ promise_test(async function(t) {
+
+ let video = document.createElement("video");
+ for (let format of ["mp4", "webm"]) {
+ let source = document.createElement("source");
+ source.src = `resources/${filenameBase}.${format}`;
+ source.type = `video/${format}`;
+ video.append(source);
+ }
+
+ let loadedData = new Promise(resolver => video.onloadeddata = resolver);
+
+ document.body.append(video);
+ await video.play();
+ await loadedData;
+ await new Promise(requestAnimationFrame);
+
+ let imageBitmap;
+ if (cropSource)
+ imageBitmap = await createImageBitmap(video, 1, 1, 1, 1);
+ else
+ imageBitmap = await createImageBitmap(video);
+
+ video.remove();
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+
+ let ctx = canvas.getContext("2d", { colorSpace: contextColorSpace });
+
+ ctx.drawImage(imageBitmap, 0, 0);
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, { colorSpace: imageDataColorSpace });
+
+ let expected = expectedPixels[`${contextColorSpace} ${imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+
+ }, `${filenameBase}, Context ${contextColorSpace}, ImageData ${imageDataColorSpace}, cropSource=${cropSource}`);
+ }
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-video.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-video.html
new file mode 100644
index 0000000000..1b04cde779
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage-video.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<body>
+<script>
+// Test that drawing videos with different color spaces into sRGB and Display P3
+// canvases works, by reading pixels with getImageData() as sRGB and Display P3
+// values.
+for (let [filenameBase, expectedPixels] of Object.entries(videoTests)) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ for (let scaleImage of [false, true]) {
+ promise_test(async function(t) {
+
+ let video = document.createElement("video");
+ for (let format of ["mp4", "webm"]) {
+ let source = document.createElement("source");
+ source.src = `resources/${filenameBase}.${format}`;
+ source.type = `video/${format}`;
+ video.append(source);
+ }
+
+ let loadedData = new Promise(resolver => video.onloadeddata = resolver);
+
+ document.body.append(video);
+ await video.play();
+ await loadedData;
+ await new Promise(requestAnimationFrame);
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+
+ let ctx = canvas.getContext("2d", { colorSpace: contextColorSpace });
+ if (scaleImage)
+ ctx.drawImage(video, 0, 0, 10, 10);
+ else
+ ctx.drawImage(video, 0, 0);
+ video.remove();
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, { colorSpace: imageDataColorSpace });
+
+ let expected = expectedPixels[`${contextColorSpace} ${imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+
+ }, `${filenameBase}, Context ${contextColorSpace}, ImageData ${imageDataColorSpace}, scaleImage=${scaleImage}`);
+ }
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage.https.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage.https.html
new file mode 100644
index 0000000000..35ee91343d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-drawImage.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that drawing images with different bit depths and color profiles into
+// sRGB and Display P3 canvases works, by reading pixels with getImageData()
+// as sRGB and Display P3 values.
+for (let [filename, expectedPixels] of Object.entries({...imageTests, ...svgImageTests})) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ for (let scaleImage of [false, true]) {
+ async_test(function(t) {
+ let image = new Image();
+ image.onload = t.step_func_done(function() {
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+
+ let ctx = canvas.getContext("2d", { colorSpace: contextColorSpace });
+ if (scaleImage)
+ ctx.drawImage(image, 0, 0, 10, 10);
+ else
+ ctx.drawImage(image, 0, 0);
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, { colorSpace: imageDataColorSpace });
+
+ let expected = expectedPixels[`${contextColorSpace} ${imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+
+ t.done();
+
+ });
+ image.src = `resources/${filename}`;
+ }, `${filename}, Context ${contextColorSpace}, ImageData ${imageDataColorSpace}, scaleImage=${scaleImage}`);
+ }
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-canvas.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-canvas.html
new file mode 100644
index 0000000000..2b9fb9b8f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-canvas.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that patterns created from canvas sources with different color spaces
+// can be drawn into sRGB and Display P3 canvases, by reading pixels with
+// getImageData() as sRGB and Display P3 values.
+function runTest(sourceColorSpace, destinationColorSpace, colors) {
+ for (let [sourceColorString, expectedColor] of Object.entries(colors)) {
+ test(function() {
+ let source = document.createElement("canvas");
+ source.width = 2;
+ source.height = 2;
+
+ let sourceContext = source.getContext("2d", { colorSpace: sourceColorSpace });
+
+ let sourceColor = sourceColorString.split(",").map(x => +x);
+
+ let sourceImageData = new ImageData(2, 2, { colorSpace: sourceColorSpace });
+ for (let i = 0; i < 2 * 2 * 4; i += 4) {
+ for (let c = 0; c < 4; ++c)
+ sourceImageData.data[i + c] = sourceColor[c];
+ }
+ sourceContext.putImageData(sourceImageData, 0, 0);
+
+ let destination = document.createElement("canvas");
+ destination.width = 4;
+ destination.height = 4;
+
+ let destinationContext = destination.getContext("2d", { colorSpace: destinationColorSpace });
+ destinationContext.fillStyle = destinationContext.createPattern(source, "repeat");
+ destinationContext.fillRect(0, 0, 4, 4);
+
+ let destinationImageData = destinationContext.getImageData(2, 2, 1, 1);
+
+ assert_true(pixelsApproximatelyEqual(destinationImageData.data, expectedColor), `Actual pixel value ${[...destinationImageData.data]} is approximately equal to ${expectedColor}.`);
+ }, `Source ${sourceColorSpace}, destination ${destinationColorSpace}, color ${sourceColorString}`);
+ }
+}
+
+runTest("srgb", "display-p3", fromSRGBToDisplayP3);
+runTest("display-p3", "srgb", fromDisplayP3ToSRGB);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html
new file mode 100644
index 0000000000..75a8392c02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="canvas-display-p3.js"></script>
+<script>
+// Test that patterns created from images with different bit depths and color
+// profiles into can be drawn into sRGB and Display P3 canvases, by reading
+// pixels with getImageData() as sRGB and Display P3 values.
+for (let [filename, expectedPixels] of Object.entries(imageTests)) {
+ for (let contextColorSpace of ["srgb", "display-p3"]) {
+ for (let imageDataColorSpace of ["srgb", "display-p3"]) {
+ async_test(function(t) {
+ let image = new Image();
+ image.onload = t.step_func_done(function() {
+
+ let canvas = document.createElement("canvas");
+ canvas.width = 4;
+ canvas.height = 4;
+
+ let ctx = canvas.getContext("2d", { colorSpace: contextColorSpace });
+ ctx.fillStyle = ctx.createPattern(image, "repeat");
+ ctx.fillRect(0, 0, 4, 4);
+
+ let imageData = ctx.getImageData(2, 2, 1, 1, { colorSpace: imageDataColorSpace });
+
+ let expected = expectedPixels[`${contextColorSpace} ${imageDataColorSpace}`];
+ assert_true(pixelsApproximatelyEqual(imageData.data, expected), `Actual pixel value ${[...imageData.data]} is approximately equal to ${expected}.`);
+
+ t.done();
+
+ });
+ image.src = `resources/${filename}`;
+ }, `${filename}, Context ${contextColorSpace}, ImageData ${imageDataColorSpace}`);
+ }
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-settings.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-settings.html
new file mode 100644
index 0000000000..2b1447437d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-settings.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// Test that creating canvas contexts and ImageData objects respect the
+// requested color space.
+
+for (let contextColorSpace of [undefined, "srgb", "display-p3"]) {
+ for (let imageDataColorSpace of [undefined, "srgb", "display-p3"]) {
+ test(function() {
+ let contextSettings = { };
+ if (contextColorSpace)
+ contextSettings.colorSpace = contextColorSpace;
+ let resolvedContextColorSpace = contextColorSpace || "srgb";
+
+ let canvas = document.createElement("canvas");
+ let ctx = canvas.getContext("2d", contextSettings);
+ assert_equals(ctx.getContextAttributes().colorSpace, resolvedContextColorSpace, `CanvasRenderingContext2DSettings.colorSpace when set to ${contextColorSpace}`);
+
+ let imageDataSettings = { };
+ if (imageDataColorSpace)
+ imageDataSettings.colorSpace = imageDataColorSpace;
+ let resolvedImageDataColorSpace = imageDataColorSpace || resolvedContextColorSpace;
+
+ let imageData = ctx.getImageData(0, 0, 1, 1, imageDataSettings);
+ assert_equals(imageData.colorSpace, resolvedImageDataColorSpace, `getImageData() colorSpace when set to ${imageDataColorSpace}`);
+
+ imageData = ctx.createImageData(1, 1, imageDataSettings);
+ assert_equals(imageData.colorSpace, resolvedImageDataColorSpace, `createImageData() colorSpace when set to ${imageDataColorSpace}`);
+
+ imageData = ctx.createImageData(imageData);
+ assert_equals(imageData.colorSpace, resolvedImageDataColorSpace, `Cloned ImageData colorSpace when set to ${imageDataColorSpace}`);
+ }, `Context ${contextColorSpace}, ImageData ${imageDataColorSpace}`);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3.js b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3.js
new file mode 100644
index 0000000000..c6ee97b788
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3.js
@@ -0,0 +1,283 @@
+// Each PNG:
+// * is 2x2 and has a single color
+// * has a filename that indicates its contents:
+// <embedded-profile>-<8-or-16-bit-color-value>.png
+// * was generated using ImageMagick commands like:
+// convert -size 2x2 xc:'#BB0000FF' -profile Display-P3.icc Display-P3-BB0000FF.png
+// convert -size 2x2 xc:'#BBBC00000000FFFF' -profile Adobe-RGB.icc Adobe-RGB-BBBC00000000FFFF.png
+
+// Top level key is the image filename. Second level key is the pair of
+// CanvasRenderingContext2DSettings.colorSpace and ImageDataSettings.colorSpace.
+const imageTests = {
+ // 8 bit source images
+
+ "sRGB-FF0000FF.png": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [234, 51, 35, 255],
+ },
+ "sRGB-FF0000CC.png": {
+ "srgb srgb": [255, 0, 0, 204],
+ "srgb display-p3": [234, 51, 35, 204],
+ "display-p3 srgb": [255, 0, 0, 204],
+ "display-p3 display-p3": [234, 51, 35, 204],
+ },
+ "sRGB-BB0000FF.png": {
+ "srgb srgb": [187, 0, 0, 255],
+ "srgb display-p3": [171, 35, 23, 255],
+ "display-p3 srgb": [187, 1, 0, 255],
+ "display-p3 display-p3": [171, 35, 23, 255],
+ },
+ "sRGB-BB0000CC.png": {
+ "srgb srgb": [187, 0, 0, 204],
+ "srgb display-p3": [171, 35, 23, 204],
+ "display-p3 srgb": [187, 1, 0, 204],
+ "display-p3 display-p3": [171, 35, 23, 204],
+ },
+
+ "Display-P3-FF0000FF.png": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [255, 0, 0, 255],
+ },
+ "Display-P3-FF0000CC.png": {
+ "srgb srgb": [255, 0, 0, 204],
+ "srgb display-p3": [234, 51, 35, 204],
+ "display-p3 srgb": [255, 0, 0, 204],
+ "display-p3 display-p3": [255, 0, 0, 204],
+ },
+ "Display-P3-BB0000FF.png": {
+ "srgb srgb": [205, 0, 0, 255],
+ "srgb display-p3": [188, 39, 26, 255],
+ "display-p3 srgb": [205, 0, 0, 255],
+ "display-p3 display-p3": [187, 0, 0, 255],
+ },
+ "Display-P3-BB0000CC.png": {
+ "srgb srgb": [205, 0, 0, 204],
+ "srgb display-p3": [188, 39, 26, 204],
+ "display-p3 srgb": [205, 0, 0, 204],
+ "display-p3 display-p3": [187, 0, 0, 204],
+ },
+
+ "Adobe-RGB-FF0000FF.png": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 19, 11, 255],
+ "display-p3 display-p3": [255, 61, 43, 255],
+ },
+ "Adobe-RGB-FF0000CC.png": {
+ "srgb srgb": [255, 0, 0, 204],
+ "srgb display-p3": [234, 51, 35, 204],
+ "display-p3 srgb": [255, 19, 11, 204],
+ "display-p3 display-p3": [255, 61, 43, 204],
+ },
+ "Adobe-RGB-BB0000FF.png": {
+ "srgb srgb": [219, 0, 0, 255],
+ "srgb display-p3": [201, 42, 29, 255],
+ "display-p3 srgb": [219, 0, 1, 255],
+ "display-p3 display-p3": [201, 42, 29, 255],
+ },
+ "Adobe-RGB-BB0000CC.png": {
+ "srgb srgb": [219, 0, 0, 204],
+ "srgb display-p3": [201, 42, 29, 204],
+ "display-p3 srgb": [219, 0, 1, 204],
+ "display-p3 display-p3": [201, 42, 29, 204],
+ },
+
+ "Generic-CMYK-FF000000.jpg": {
+ "srgb srgb": [0, 163, 218, 255],
+ "srgb display-p3": [72, 161, 213, 255],
+ "display-p3 srgb": [0, 163, 218, 255],
+ "display-p3 display-p3": [0, 160, 213, 255],
+ },
+ "Generic-CMYK-BE000000.jpg": {
+ "srgb srgb": [0, 180, 223, 255],
+ "srgb display-p3": [80, 177, 219, 255],
+ "display-p3 srgb": [0, 180, 223, 255],
+ "display-p3 display-p3": [65, 177, 219, 255],
+ },
+
+ // 16 bit source images
+
+ "sRGB-FFFF00000000FFFF.png": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [234, 51, 35, 255],
+ },
+ "sRGB-FFFF00000000CCCC.png": {
+ "srgb srgb": [255, 0, 0, 204],
+ "srgb display-p3": [234, 51, 35, 204],
+ "display-p3 srgb": [255, 0, 0, 204],
+ "display-p3 display-p3": [234, 51, 35, 204],
+ },
+ "sRGB-BBBC00000000FFFF.png": {
+ "srgb srgb": [187, 0, 0, 255],
+ "srgb display-p3": [171, 35, 23, 255],
+ "display-p3 srgb": [187, 1, 0, 255],
+ "display-p3 display-p3": [171, 35, 23, 255],
+ },
+ "sRGB-BBBC00000000CCCC.png": {
+ "srgb srgb": [187, 0, 0, 204],
+ "srgb display-p3": [171, 35, 23, 204],
+ "display-p3 srgb": [187, 1, 0, 204],
+ "display-p3 display-p3": [171, 35, 23, 204],
+ },
+
+ "Display-P3-FFFF00000000FFFF.png": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [255, 0, 0, 255],
+ },
+ "Display-P3-FFFF00000000CCCC.png": {
+ "srgb srgb": [255, 0, 0, 204],
+ "srgb display-p3": [234, 51, 35, 204],
+ "display-p3 srgb": [255, 0, 0, 204],
+ "display-p3 display-p3": [255, 0, 0, 204],
+ },
+ "Display-P3-BBBC00000000FFFF.png": {
+ "srgb srgb": [205, 0, 0, 255],
+ "srgb display-p3": [188, 39, 26, 255],
+ "display-p3 srgb": [205, 0, 0, 255],
+ "display-p3 display-p3": [187, 0, 0, 255],
+ },
+ "Display-P3-BBBC00000000CCCC.png": {
+ "srgb srgb": [205, 0, 0, 204],
+ "srgb display-p3": [188, 39, 26, 204],
+ "display-p3 srgb": [205, 0, 0, 204],
+ "display-p3 display-p3": [187, 0, 0, 204],
+ },
+
+ "Adobe-RGB-FFFF00000000FFFF.png": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 19, 11, 255],
+ "display-p3 display-p3": [255, 61, 43, 255],
+ },
+ "Adobe-RGB-FFFF00000000CCCC.png": {
+ "srgb srgb": [255, 0, 0, 204],
+ "srgb display-p3": [234, 51, 35, 204],
+ "display-p3 srgb": [255, 19, 11, 204],
+ "display-p3 display-p3": [255, 61, 43, 204],
+ },
+ "Adobe-RGB-BBBC00000000FFFF.png": {
+ "srgb srgb": [219, 0, 0, 255],
+ "srgb display-p3": [201, 42, 29, 255],
+ "display-p3 srgb": [219, 0, 1, 255],
+ "display-p3 display-p3": [201, 42, 29, 255],
+ },
+ "Adobe-RGB-BBBC00000000CCCC.png": {
+ "srgb srgb": [219, 0, 0, 204],
+ "srgb display-p3": [201, 42, 29, 204],
+ "display-p3 srgb": [219, 0, 1, 204],
+ "display-p3 display-p3": [201, 42, 29, 204],
+ },
+};
+
+const svgImageTests = {
+ // SVG source images
+
+ "sRGB-FF0000.svg": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [234, 51, 35, 255],
+ },
+ "sRGB-BB0000.svg": {
+ "srgb srgb": [187, 0, 0, 255],
+ "srgb display-p3": [171, 35, 23, 255],
+ "display-p3 srgb": [187, 1, 0, 255],
+ "display-p3 display-p3": [171, 35, 23, 255],
+ },
+
+ "Display-P3-1-0-0.svg": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [255, 0, 0, 255],
+ },
+ "Display-P3-0.7333-0-0.svg": {
+ "srgb srgb": [205, 0, 0, 255],
+ "srgb display-p3": [188, 39, 26, 255],
+ "display-p3 srgb": [205, 0, 0, 255],
+ "display-p3 display-p3": [187, 0, 0, 255],
+ },
+};
+
+// Each video:
+// * is 300x200 and has a single color
+// * has a filename base that indicates its contents:
+//
+// <color-space>-<8-or-10-bit-color-value>
+//
+// * was generated using commands like:
+//
+// W=300 H=200 Y=3F Cb=66 Cr=F0 ; \
+// perl -e "print pack('c', 0x$Y) x ($W * $H), pack('c', 0x$Cb) x ($W * $H / 4), pack('c', 0x$Cr) x ($W * $H / 4)" | \
+// ffmpeg -f rawvideo -pix_fmt yuv420p -s:v ${W}x$H -r 25 -i - -pix_fmt yuv420p -colorspace bt709 -color_primaries bt709 -color_trc iec61966_2_1 sRGB-FF0100.webm
+//
+// W=300 H=200 Y=0BB Cb=1BD Cr=2EF ; \
+// perl -e "print pack('s', 0x$Y) x ($W * $H), pack('s', 0x$Cb) x ($W * $H / 4), pack('s', 0x$Cr) x ($W * $H / 4)" | \
+// ffmpeg -f rawvideo -pix_fmt yuv420p10le -s:v ${W}x$H -r 25 -i - -c:v libx265 -vtag hvc1 -pix_fmt yuv420p10le -colorspace bt2020nc -color_primaries bt2020 -color_trc bt2020-10 Rec2020-222000000.mp4
+//
+// W=300 H=200 Y=0BB Cb=1BD Cr=2EF ; \
+// perl -e "print pack('s', 0x$Y) x ($W * $H), pack('s', 0x$Cb) x ($W * $H / 4), pack('s', 0x$Cr) x ($W * $H / 4)" | \
+// ffmpeg -f rawvideo -pix_fmt yuv420p10le -s:v ${W}x$H -r 25 -i - -vcodec libvpx-vp9 -profile:v 2 -pix_fmt yuv420p10le -colorspace bt2020nc -color_primaries bt2020 -color_trc bt2020-10 Rec2020-222000000.webm
+//
+// where the Y'CbCr values were computed using https://jdashg.github.io/misc/colors/from-coeffs.html.
+const videoTests = {
+ // Rec.709 Y'CbCr (0x3F, 0x66, 0xF0) = sRGB (0xFF, 0x01, 0x00)
+ "sRGB-FF0100": {
+ "srgb srgb": [255, 1, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [234, 51, 35, 255],
+ },
+ // Rec.709 Y'CbCr (0x32, 0x6D, 0xD2) = sRGB (0xBB, 0x00, 0x00)
+ "sRGB-BB0000": {
+ "srgb srgb": [187, 0, 0, 255],
+ "srgb display-p3": [171, 35, 23, 255],
+ "display-p3 srgb": [187, 1, 0, 255],
+ "display-p3 display-p3": [171, 35, 23, 255],
+ },
+
+ // 10 bit Rec.2020 Y'CbCr (0x126, 0x183, 0x3C0) = Rec.2020 (0x3FF, 0x000, 0x000)
+ "Rec2020-3FF000000": {
+ "srgb srgb": [255, 0, 0, 255],
+ "srgb display-p3": [234, 51, 35, 255],
+ "display-p3 srgb": [255, 0, 0, 255],
+ "display-p3 display-p3": [255, 0, 9, 255],
+ },
+ // 10 bit Rec.2020 Y'CbCr (0x0BB, 0x1BD, 0x2EF) = Rec.2020 (0x222, 0x000, 0x000)
+ "Rec2020-222000000": {
+ "srgb srgb": [186, 0, 0, 255],
+ "srgb display-p3": [170, 34, 23, 255],
+ "display-p3 srgb": [186, 0, 0, 255],
+ "display-p3 display-p3": [169, 0, 3, 255],
+ },
+};
+
+const fromSRGBToDisplayP3 = {
+ "255,0,0,255": [234, 51, 35, 255],
+ "255,0,0,204": [234, 51, 35, 204],
+ "187,0,0,255": [171, 35, 23, 255],
+ "187,0,0,204": [171, 35, 23, 204],
+};
+
+const fromDisplayP3ToSRGB = {
+ "255,0,0,255": [255, 0, 0, 255],
+ "255,0,0,204": [255, 0, 0, 204],
+ "187,0,0,255": [205, 0, 0, 255],
+ "187,0,0,204": [205, 0, 0, 204],
+};
+
+function pixelsApproximatelyEqual(p1, p2) {
+ for (let i = 0; i < 4; ++i) {
+ if (Math.abs(p1[i] - p2[i]) > 3)
+ return false;
+ }
+ return true;
+}
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/imagedata-no-color-settings-crash.html b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/imagedata-no-color-settings-crash.html
new file mode 100644
index 0000000000..b21eaf72ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/imagedata-no-color-settings-crash.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d',
+ {})
+ var dataFloat32 = new Float32Array(4);
+ var imageData = ctx.createImageData(dataFloat32, 1, 1,
+ {});
+ ctx.putImageData(imageData, 5, 5);
+ var data = ctx.getImageData(5,5,1,1).data;
+}, "Putting a float-32 ImageData with no color settings on a context 2D should not crash.");
+
+test(function() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d',
+ {})
+ var dataUint16 = new Uint16Array(4);
+ var imageData = ctx.createImageData(dataUint16, 1, 1,
+ {});
+ ctx.putImageData(imageData, 5, 5);
+ var data = ctx.getImageData(5,5,1,1).data;
+}, "Putting a uint-16 ImageData with no color settings on a context 2D should not crash.");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000CC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000CC.png
new file mode 100644
index 0000000000..9b3db7e22e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000CC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000FF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000FF.png
new file mode 100644
index 0000000000..78237d5a9d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BB0000FF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000CCCC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000CCCC.png
new file mode 100644
index 0000000000..3fbe7cadc0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000CCCC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000FFFF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000FFFF.png
new file mode 100644
index 0000000000..ab9b1bf5b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-BBBC00000000FFFF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000CC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000CC.png
new file mode 100644
index 0000000000..a83344933d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000CC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000FF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000FF.png
new file mode 100644
index 0000000000..3222c5e26f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FF0000FF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000CCCC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000CCCC.png
new file mode 100644
index 0000000000..60f1239943
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000CCCC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000FFFF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000FFFF.png
new file mode 100644
index 0000000000..c558eb10b3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Adobe-RGB-FFFF00000000FFFF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-0.7333-0-0.svg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-0.7333-0-0.svg
new file mode 100644
index 0000000000..2737814877
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-0.7333-0-0.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="2" height="2">
+ <rect width="2" height="2" style="fill: color(display-p3 0.7333 0 0);"/>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-1-0-0.svg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-1-0-0.svg
new file mode 100644
index 0000000000..5c1b07362a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-1-0-0.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="2" height="2">
+ <rect width="2" height="2" style="fill: color(display-p3 1 0 0);"/>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000CC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000CC.png
new file mode 100644
index 0000000000..2036aeae25
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000CC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000FF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000FF.png
new file mode 100644
index 0000000000..35347a3055
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BB0000FF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000CCCC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000CCCC.png
new file mode 100644
index 0000000000..87f11b5d97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000CCCC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000FFFF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000FFFF.png
new file mode 100644
index 0000000000..ea2f84caa1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-BBBC00000000FFFF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000CC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000CC.png
new file mode 100644
index 0000000000..8a0abc74dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000CC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000FF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000FF.png
new file mode 100644
index 0000000000..7b6d4a6150
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FF0000FF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000CCCC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000CCCC.png
new file mode 100644
index 0000000000..8a0abc74dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000CCCC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000FFFF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000FFFF.png
new file mode 100644
index 0000000000..7b6d4a6150
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Display-P3-FFFF00000000FFFF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-BE000000.jpg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-BE000000.jpg
new file mode 100644
index 0000000000..21eecaa25e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-BE000000.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-FF000000.jpg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-FF000000.jpg
new file mode 100644
index 0000000000..32f3b691e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Generic-CMYK-FF000000.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.mp4 b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.mp4
new file mode 100644
index 0000000000..71ad32c5f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.mp4
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.webm b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.webm
new file mode 100644
index 0000000000..5e4eae49dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-222000000.webm
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.mp4 b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.mp4
new file mode 100644
index 0000000000..0e2880a93a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.mp4
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.webm b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.webm
new file mode 100644
index 0000000000..4f28de8cea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/Rec2020-3FF000000.webm
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-p3d65.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-p3d65.png
new file mode 100644
index 0000000000..5eb3f6a15f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-p3d65.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-rec2020.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-rec2020.png
new file mode 100644
index 0000000000..b64db07379
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-rec2020.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-srgb.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-srgb.png
new file mode 100644
index 0000000000..bfbba8b8e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-semitransparent-srgb.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.avif b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.avif
new file mode 100644
index 0000000000..c3890c5cf6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.avif
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.bmp b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.bmp
new file mode 100644
index 0000000000..9c9561c704
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.bmp
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.ico b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.ico
new file mode 100644
index 0000000000..87e8ff45de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.ico
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.png
new file mode 100644
index 0000000000..bfbba8b8e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.webp b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.webp
new file mode 100644
index 0000000000..925646067a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb-transparent.webp
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.avif b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.avif
new file mode 100644
index 0000000000..c13b320ea8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.avif
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.bmp b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.bmp
new file mode 100644
index 0000000000..465d203d98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.bmp
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.gif b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.gif
new file mode 100644
index 0000000000..25bcefb2bf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.gif
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.ico b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.ico
new file mode 100644
index 0000000000..e5375826ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.ico
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.jpg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.jpg
new file mode 100644
index 0000000000..c4579e8f06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.png
new file mode 100644
index 0000000000..1b5876b5f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.svg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.svg
new file mode 100644
index 0000000000..0517130849
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.svg
@@ -0,0 +1,6 @@
+<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <rect x="0" y="0" width="10px" height="10px" fill="#9b1b1b"/>
+ <rect x="10" y="0" width="10px" height="10px" fill="#1b9b1b"/>
+ <rect x="0" y="10" width="10px" height="10px" fill="#1b1b9b"/>
+ <rect x="10" y="10" width="10px" height="10px" fill="#1b1b1b"/>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.webp b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.webp
new file mode 100644
index 0000000000..b7c0a421dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/pattern-srgb.webp
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png
new file mode 100644
index 0000000000..c4496db19b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png
new file mode 100644
index 0000000000..3b4cfda52c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png
new file mode 100644
index 0000000000..e7a142cd2e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png
new file mode 100644
index 0000000000..0b035317c8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png
new file mode 100644
index 0000000000..a1dc7ddb0d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png
new file mode 100644
index 0000000000..be2eb1208e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_opaque.png
new file mode 100644
index 0000000000..e2a2d14451
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_transparent.png
new file mode 100644
index 0000000000..960d7d8e75
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_Rec2020_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png
new file mode 100644
index 0000000000..80cf9785eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png
new file mode 100644
index 0000000000..3ec565f8c2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png
new file mode 100644
index 0000000000..5f3134b79c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png
new file mode 100644
index 0000000000..500a70eff8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png
new file mode 100644
index 0000000000..b5d0e07a7a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png
new file mode 100644
index 0000000000..e4ec3e4454
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png
new file mode 100644
index 0000000000..c487d5846f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png
new file mode 100644
index 0000000000..78fe202c0e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png
new file mode 100644
index 0000000000..babf232a36
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png
new file mode 100644
index 0000000000..3016404009
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_opaque.png
new file mode 100644
index 0000000000..8a665345e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_transparent.png
new file mode 100644
index 0000000000..e51cda77c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_16bit_sRGB_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png
new file mode 100644
index 0000000000..8b787b5c87
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png
new file mode 100644
index 0000000000..727028e765
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png
new file mode 100644
index 0000000000..fe8bdd4963
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png
new file mode 100644
index 0000000000..b836afebed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png
new file mode 100644
index 0000000000..5ecd868606
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png
new file mode 100644
index 0000000000..85a349dc1d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_opaque.png
new file mode 100644
index 0000000000..599cd34040
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_transparent.png
new file mode 100644
index 0000000000..ecf65c3d46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_Rec2020_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_opaque.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_opaque.png
new file mode 100644
index 0000000000..9cab6d12e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_opaque.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_transparent.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_transparent.png
new file mode 100644
index 0000000000..5fa01e62c2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/png-16bit/2x2_8bit_sRGB_transparent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.mp4 b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.mp4
new file mode 100644
index 0000000000..59572f5138
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.mp4
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.svg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.svg
new file mode 100644
index 0000000000..2b785f8cf4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="2" height="2">
+ <rect width="2" height="2" style="fill: #BB0000;"/>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.webm b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.webm
new file mode 100644
index 0000000000..23a6364bdb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000.webm
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000CC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000CC.png
new file mode 100644
index 0000000000..fe463486ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000CC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000FF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000FF.png
new file mode 100644
index 0000000000..be7009f613
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BB0000FF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000CCCC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000CCCC.png
new file mode 100644
index 0000000000..5220ccb7a9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000CCCC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000FFFF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000FFFF.png
new file mode 100644
index 0000000000..416ef12986
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-BBBC00000000FFFF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000.svg b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000.svg
new file mode 100644
index 0000000000..8798fdfc3a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="2" height="2">
+ <rect width="2" height="2" style="fill: #FF0000;"/>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000CC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000CC.png
new file mode 100644
index 0000000000..e86f64b71b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000CC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000FF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000FF.png
new file mode 100644
index 0000000000..22f33b14da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0000FF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.mp4 b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.mp4
new file mode 100644
index 0000000000..37a29f9f83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.mp4
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.webm b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.webm
new file mode 100644
index 0000000000..31b69af924
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FF0100.webm
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000CCCC.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000CCCC.png
new file mode 100644
index 0000000000..e86f64b71b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000CCCC.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000FFFF.png b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000FFFF.png
new file mode 100644
index 0000000000..22f33b14da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/wide-gamut-canvas/resources/sRGB-FFFF00000000FFFF.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.1.html
new file mode 100644
index 0000000000..85ebff4c0a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.angle.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.angle.1</h1>
+<p class="desc">arc() draws pi/2 .. -pi anticlockwise correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws pi/2 .. -pi anticlockwise correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.2.html
new file mode 100644
index 0000000000..7eefabd993
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.angle.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.angle.2</h1>
+<p class="desc">arc() draws -3pi/2 .. -pi anticlockwise correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws -3pi/2 .. -pi anticlockwise correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -3*Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.3.html
new file mode 100644
index 0000000000..899c50dc90
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.3.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.angle.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.angle.3</h1>
+<p class="desc">arc() wraps angles mod 2pi when anticlockwise and end > start+2pi</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() wraps angles mod 2pi when anticlockwise and end > start+2pi");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (512+1/2)*Math.PI, (1024-1)*Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.4.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.4.html
new file mode 100644
index 0000000000..3f1bd84d69
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.4.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.angle.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.angle.4</h1>
+<p class="desc">arc() draws a full circle when clockwise and end > start+2pi</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws a full circle when clockwise and end > start+2pi");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (512+1/2)*Math.PI, (1024-1)*Math.PI, false);
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.5.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.5.html
new file mode 100644
index 0000000000..2373bd4f0a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.5.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.angle.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.angle.5</h1>
+<p class="desc">arc() wraps angles mod 2pi when clockwise and start > end+2pi</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() wraps angles mod 2pi when clockwise and start > end+2pi");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (1024-1)*Math.PI, (512+1/2)*Math.PI, false);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.6.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.6.html
new file mode 100644
index 0000000000..b0b03cff79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.angle.6.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.angle.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.angle.6</h1>
+<p class="desc">arc() draws a full circle when anticlockwise and start > end+2pi</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws a full circle when anticlockwise and start > end+2pi");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (1024-1)*Math.PI, (512+1/2)*Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.default.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.default.html
new file mode 100644
index 0000000000..b52941fe3f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.default.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.default</h1>
+<p class="desc">arc() with missing last argument defaults to clockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with missing last argument defaults to clockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -Math.PI, Math.PI/2);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.empty.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.empty.html
new file mode 100644
index 0000000000..295ee4bbb2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.empty.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.empty</h1>
+<p class="desc">arc() with an empty path does not draw a straight line to the start point</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with an empty path does not draw a straight line to the start point");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.end.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.end.html
new file mode 100644
index 0000000000..a581a40e8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.end.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.end</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.end</h1>
+<p class="desc">arc() adds the end point of the arc to the subpath</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() adds the end point of the arc to the subpath");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(-100, 0);
+ ctx.arc(-100, 0, 25, -Math.PI/2, Math.PI/2, true);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.negative.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.negative.html
new file mode 100644
index 0000000000..fd81ebadcd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.negative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.negative</h1>
+<p class="desc">arc() with negative radius throws INDEX_SIZE_ERR</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with negative radius throws INDEX_SIZE_ERR");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.arc(0, 0, -1, 0, 0, true); });
+ var path = new Path2D();
+ assert_throws_dom("INDEX_SIZE_ERR", function() { path.arc(10, 10, -5, 0, 1, false); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonempty.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonempty.html
new file mode 100644
index 0000000000..982829013d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonempty.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.nonempty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.nonempty</h1>
+<p class="desc">arc() with a non-empty path does draw a straight line to the start point</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with a non-empty path does draw a straight line to the start point");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonfinite.html
new file mode 100644
index 0000000000..a1b15e7a88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.nonfinite.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.nonfinite</h1>
+<p class="desc">arc() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.arc(Infinity, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(-Infinity, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(NaN, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, -Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, NaN, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, -Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, NaN, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, -Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, NaN, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, 0, Infinity, true);
+ ctx.arc(0, 0, 50, 0, -Infinity, true);
+ ctx.arc(0, 0, 50, 0, NaN, true);
+ ctx.arc(Infinity, Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, Infinity, Infinity, true);
+ ctx.arc(Infinity, Infinity, Infinity, 0, Infinity, true);
+ ctx.arc(Infinity, Infinity, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, 50, Infinity, Infinity, true);
+ ctx.arc(Infinity, Infinity, 50, 0, Infinity, true);
+ ctx.arc(Infinity, 0, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, Infinity, Infinity, Infinity, true);
+ ctx.arc(Infinity, 0, Infinity, 0, Infinity, true);
+ ctx.arc(Infinity, 0, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, 50, Infinity, Infinity, true);
+ ctx.arc(Infinity, 0, 50, 0, Infinity, true);
+ ctx.arc(0, Infinity, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, Infinity, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(0, Infinity, Infinity, Infinity, Infinity, true);
+ ctx.arc(0, Infinity, Infinity, 0, Infinity, true);
+ ctx.arc(0, Infinity, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(0, Infinity, 50, Infinity, Infinity, true);
+ ctx.arc(0, Infinity, 50, 0, Infinity, true);
+ ctx.arc(0, 0, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, Infinity, Infinity, Infinity, true);
+ ctx.arc(0, 0, Infinity, 0, Infinity, true);
+ ctx.arc(0, 0, 50, Infinity, Infinity, true);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.1.html
new file mode 100644
index 0000000000..951c9ae26c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.1.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.scale.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.scale.1</h1>
+<p class="desc">Non-uniformly scaled arcs are the right shape</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Non-uniformly scaled arcs are the right shape");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(2, 0.5);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(25, 50, 56, 0, 2*Math.PI, false);
+ ctx.fill();
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-25, 50);
+ ctx.arc(-25, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(75, 50);
+ ctx.arc(75, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, -25);
+ ctx.arc(25, -25, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, 125);
+ ctx.arc(25, 125, 24, 0, 2*Math.PI, false);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.2.html
new file mode 100644
index 0000000000..6b39e16222
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.scale.2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.scale.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.scale.2</h1>
+<p class="desc">Highly scaled arcs are the right shape</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Highly scaled arcs are the right shape");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(100, 100);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 1.2;
+ ctx.beginPath();
+ ctx.arc(0, 0, 0.6, 0, Math.PI/2, false);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.1.html
new file mode 100644
index 0000000000..3bc81bd77a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.selfintersect.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.selfintersect.1</h1>
+<p class="desc">arc() with lineWidth > 2*radius is drawn sensibly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with lineWidth > 2*radius is drawn sensibly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(100, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(0, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.2.html
new file mode 100644
index 0000000000..e3b14a782f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.selfintersect.2.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.selfintersect.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.selfintersect.2</h1>
+<p class="desc">arc() with lineWidth > 2*radius is drawn sensibly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with lineWidth > 2*radius is drawn sensibly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 180;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(100, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 97,1, 0,255,0,255);
+ _assertPixel(canvas, 97,2, 0,255,0,255);
+ _assertPixel(canvas, 97,3, 0,255,0,255);
+ _assertPixel(canvas, 2,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.1.html
new file mode 100644
index 0000000000..8a456841ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.1.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.shape.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.shape.1</h1>
+<p class="desc">arc() from 0 to pi does not draw anything in the wrong half</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() from 0 to pi does not draw anything in the wrong half");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 20,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.2.html
new file mode 100644
index 0000000000..f820eed761
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.shape.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.shape.2</h1>
+<p class="desc">arc() from 0 to pi draws stuff in the right half</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() from 0 to pi draws stuff in the right half");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 20,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.3.html
new file mode 100644
index 0000000000..aa08d44194
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.shape.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.shape.3</h1>
+<p class="desc">arc() from 0 to -pi/2 does not draw anything in the wrong quadrant</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() from 0 to -pi/2 does not draw anything in the wrong quadrant");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(0, 50, 50, 0, -Math.PI/2, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.4.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.4.html
new file mode 100644
index 0000000000..b00d4468f2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.4.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.shape.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.shape.4</h1>
+<p class="desc">arc() from 0 to -pi/2 draws stuff in the right quadrant</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() from 0 to -pi/2 draws stuff in the right quadrant");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 150;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 100, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.5.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.5.html
new file mode 100644
index 0000000000..f5574d2773
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.shape.5.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.shape.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.shape.5</h1>
+<p class="desc">arc() from 0 to 5pi does not draw crazy things</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() from 0 to 5pi does not draw crazy things");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(300, 0, 100, 0, 5*Math.PI, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.1.html
new file mode 100644
index 0000000000..6312efb3e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.twopie.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.twopie.1</h1>
+<p class="desc">arc() draws nothing when end = start + 2pi-e and anticlockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws nothing when end = start + 2pi-e and anticlockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.2.html
new file mode 100644
index 0000000000..132c2d38dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.twopie.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.twopie.2</h1>
+<p class="desc">arc() draws a full circle when end = start + 2pi-e and clockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws a full circle when end = start + 2pi-e and clockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.3.html
new file mode 100644
index 0000000000..204ed54a1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.3.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.twopie.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.twopie.3</h1>
+<p class="desc">arc() draws a full circle when end = start + 2pi+e and anticlockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws a full circle when end = start + 2pi+e and anticlockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.4.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.4.html
new file mode 100644
index 0000000000..d5535a7949
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.4.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.twopie.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.twopie.4</h1>
+<p class="desc">arc() draws nothing when end = start + 2pi+e and clockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws nothing when end = start + 2pi+e and clockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.5.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.5.html
new file mode 100644
index 0000000000..955a6c8743
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.5.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.twopie.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.twopie.5</h1>
+<p class="desc">arc() draws correctly when start = 2 and end = start + 2pi+e and clockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws correctly when start = 2 and end = start + 2pi+e and clockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 2, 2 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ _assertPixel(canvas, 95,25, 0,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.6.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.6.html
new file mode 100644
index 0000000000..9420ecdc93
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.twopie.6.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.twopie.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.twopie.6</h1>
+<p class="desc">arc() draws correctly when start = 5 and end = start + 2pi+e and clockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws correctly when start = 5 and end = start + 2pi+e and clockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 5, 5 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ _assertPixel(canvas, 5,25, 0,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.1.html
new file mode 100644
index 0000000000..2c93425b1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.zero.1</h1>
+<p class="desc">arc() draws nothing when startAngle = endAngle and anticlockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws nothing when startAngle = endAngle and anticlockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.2.html
new file mode 100644
index 0000000000..c290c2f32f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zero.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.zero.2</h1>
+<p class="desc">arc() draws nothing when startAngle = endAngle and clockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() draws nothing when startAngle = endAngle and clockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zeroradius.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zeroradius.html
new file mode 100644
index 0000000000..f75403b2f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arc.zeroradius.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arc.zeroradius</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arc.zeroradius</h1>
+<p class="desc">arc() with zero radius draws a line to the start point</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arc() with zero radius draws a line to the start point");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00'
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 0, 0, Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.1.html
new file mode 100644
index 0000000000..01c8091c46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.1.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.coincide.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.coincide.1</h1>
+<p class="desc">arcTo() has no effect if P0 = P1</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() has no effect if P0 = P1");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(0, 25, 50, 1000, 1);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.2.html
new file mode 100644
index 0000000000..d216949aae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.coincide.2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.coincide.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.coincide.2</h1>
+<p class="desc">arcTo() draws a straight line to P1 if P1 = P2</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() draws a straight line to P1 if P1 = P2");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.1.html
new file mode 100644
index 0000000000..e91e2e4b1b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.collinear.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.collinear.1</h1>
+<p class="desc">arcTo() with all points on a line, and P1 between P0/P2, draws a straight line to P1</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() with all points on a line, and P1 between P0/P2, draws a straight line to P1");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 200, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.2.html
new file mode 100644
index 0000000000..97b1339295
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.collinear.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.collinear.2</h1>
+<p class="desc">arcTo() with all points on a line, and P2 between P0/P1, draws a straight line to P1</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() with all points on a line, and P2 between P0/P1, draws a straight line to P1");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 10, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 110, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.3.html
new file mode 100644
index 0000000000..2f4394f038
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.collinear.3.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.collinear.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.collinear.3</h1>
+<p class="desc">arcTo() with all points on a line, and P0 between P1/P2, draws a straight line to P1</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() with all points on a line, and P0 between P1/P2, draws a straight line to P1");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 0, 25, 1);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, -200, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..2781bcbba3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the first control point is added (and nothing is drawn up to it)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arcTo(100, 50, 200, 50, 0.1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..f5da07c1c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.ensuresubpath.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the first control point is added</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the first control point is added");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arcTo(0, 25, 50, 250, 0.1); // adds (x1,y1), draws nothing
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.negative.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.negative.html
new file mode 100644
index 0000000000..0091ca2a67
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.negative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.negative</h1>
+<p class="desc">arcTo() with negative radius throws an exception</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() with negative radius throws an exception");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.arcTo(0, 0, 0, 0, -1); });
+ var path = new Path2D();
+ assert_throws_dom("INDEX_SIZE_ERR", function() { path.arcTo(10, 10, 20, 20, -5); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.nonfinite.html
new file mode 100644
index 0000000000..0d7be1d360
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.nonfinite.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.nonfinite</h1>
+<p class="desc">arcTo() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.arcTo(Infinity, 50, 0, 50, 0);
+ ctx.arcTo(-Infinity, 50, 0, 50, 0);
+ ctx.arcTo(NaN, 50, 0, 50, 0);
+ ctx.arcTo(0, Infinity, 0, 50, 0);
+ ctx.arcTo(0, -Infinity, 0, 50, 0);
+ ctx.arcTo(0, NaN, 0, 50, 0);
+ ctx.arcTo(0, 50, Infinity, 50, 0);
+ ctx.arcTo(0, 50, -Infinity, 50, 0);
+ ctx.arcTo(0, 50, NaN, 50, 0);
+ ctx.arcTo(0, 50, 0, Infinity, 0);
+ ctx.arcTo(0, 50, 0, -Infinity, 0);
+ ctx.arcTo(0, 50, 0, NaN, 0);
+ ctx.arcTo(0, 50, 0, 50, Infinity);
+ ctx.arcTo(0, 50, 0, 50, -Infinity);
+ ctx.arcTo(0, 50, 0, 50, NaN);
+ ctx.arcTo(Infinity, Infinity, 0, 50, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, 50, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.arcTo(Infinity, Infinity, Infinity, 50, Infinity);
+ ctx.arcTo(Infinity, Infinity, 0, Infinity, 0);
+ ctx.arcTo(Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.arcTo(Infinity, Infinity, 0, 50, Infinity);
+ ctx.arcTo(Infinity, 50, Infinity, 50, 0);
+ ctx.arcTo(Infinity, 50, Infinity, Infinity, 0);
+ ctx.arcTo(Infinity, 50, Infinity, Infinity, Infinity);
+ ctx.arcTo(Infinity, 50, Infinity, 50, Infinity);
+ ctx.arcTo(Infinity, 50, 0, Infinity, 0);
+ ctx.arcTo(Infinity, 50, 0, Infinity, Infinity);
+ ctx.arcTo(Infinity, 50, 0, 50, Infinity);
+ ctx.arcTo(0, Infinity, Infinity, 50, 0);
+ ctx.arcTo(0, Infinity, Infinity, Infinity, 0);
+ ctx.arcTo(0, Infinity, Infinity, Infinity, Infinity);
+ ctx.arcTo(0, Infinity, Infinity, 50, Infinity);
+ ctx.arcTo(0, Infinity, 0, Infinity, 0);
+ ctx.arcTo(0, Infinity, 0, Infinity, Infinity);
+ ctx.arcTo(0, Infinity, 0, 50, Infinity);
+ ctx.arcTo(0, 50, Infinity, Infinity, 0);
+ ctx.arcTo(0, 50, Infinity, Infinity, Infinity);
+ ctx.arcTo(0, 50, Infinity, 50, Infinity);
+ ctx.arcTo(0, 50, 0, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.scale.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.scale.html
new file mode 100644
index 0000000000..6645f05260
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.scale.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.scale</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.scale</h1>
+<p class="desc">arcTo scales the curve, not just the control points</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo scales the curve, not just the control points");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.scale(0.1, 1);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-1000, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve1.html
new file mode 100644
index 0000000000..4617b2cbbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve1.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.shape.curve1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.shape.curve1</h1>
+<p class="desc">arcTo() curves in the right kind of shape</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() curves in the right kind of shape");
+_addTest(function(canvas, ctx) {
+
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25+tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15-tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 55,19, 0,255,0,255);
+ _assertPixel(canvas, 55,20, 0,255,0,255);
+ _assertPixel(canvas, 55,21, 0,255,0,255);
+ _assertPixel(canvas, 64,22, 0,255,0,255);
+ _assertPixel(canvas, 65,21, 0,255,0,255);
+ _assertPixel(canvas, 72,28, 0,255,0,255);
+ _assertPixel(canvas, 73,27, 0,255,0,255);
+ _assertPixel(canvas, 78,36, 0,255,0,255);
+ _assertPixel(canvas, 79,35, 0,255,0,255);
+ _assertPixel(canvas, 80,44, 0,255,0,255);
+ _assertPixel(canvas, 80,45, 0,255,0,255);
+ _assertPixel(canvas, 80,46, 0,255,0,255);
+ _assertPixel(canvas, 65,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve2.html
new file mode 100644
index 0000000000..bcc4326eea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.curve2.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.shape.curve2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.shape.curve2</h1>
+<p class="desc">arcTo() curves in the right kind of shape</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() curves in the right kind of shape");
+_addTest(function(canvas, ctx) {
+
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25-tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15+tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 55,19, 0,255,0,255);
+ _assertPixel(canvas, 55,20, 0,255,0,255);
+ _assertPixel(canvas, 55,21, 0,255,0,255);
+ _assertPixel(canvas, 64,22, 0,255,0,255);
+ _assertPixel(canvas, 65,21, 0,255,0,255);
+ _assertPixel(canvas, 72,28, 0,255,0,255);
+ _assertPixel(canvas, 73,27, 0,255,0,255);
+ _assertPixel(canvas, 78,36, 0,255,0,255);
+ _assertPixel(canvas, 79,35, 0,255,0,255);
+ _assertPixel(canvas, 80,44, 0,255,0,255);
+ _assertPixel(canvas, 80,45, 0,255,0,255);
+ _assertPixel(canvas, 80,46, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.end.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.end.html
new file mode 100644
index 0000000000..829c70f5f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.end.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.shape.end</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.shape.end</h1>
+<p class="desc">arcTo() does not draw anything from P1 to P2</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() does not draw anything from P1 to P2");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.arcTo(-100, 25, 200, 25, 10);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.start.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.start.html
new file mode 100644
index 0000000000..0abeedf433
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.shape.start.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.shape.start</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.shape.start</h1>
+<p class="desc">arcTo() draws a straight line from P0 to P1</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() draws a straight line from P0 to P1");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(200, 25, 200, 50, 10);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.transformation.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.transformation.html
new file mode 100644
index 0000000000..5aa8562b1d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.transformation.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.transformation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.transformation</h1>
+<p class="desc">arcTo joins up to the last subpath point correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo joins up to the last subpath point correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.1.html
new file mode 100644
index 0000000000..bb2cc1d3e3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.zero.1</h1>
+<p class="desc">arcTo() with zero radius draws a straight line from P0 to P1</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() with zero radius draws a straight line from P0 to P1");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 100, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(0, -25);
+ ctx.arcTo(50, -25, 50, 50, 0);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.2.html
new file mode 100644
index 0000000000..d2eeee535d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.arcTo.zero.2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.arcTo.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.arcTo.zero.2</h1>
+<p class="desc">arcTo() with zero radius draws a straight line from P0 to P1, even when all points are collinear</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("arcTo() with zero radius draws a straight line from P0 to P1, even when all points are collinear");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 50, 25, 0);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.beginPath.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.beginPath.html
new file mode 100644
index 0000000000..316f6af942
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.beginPath.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.beginPath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.beginPath</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.basic.html
new file mode 100644
index 0000000000..0c3d7f948b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.basic.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.bezierCurveTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.bezierCurveTo.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.bezierCurveTo(100, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..ebcce3f48c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.bezierCurveTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.bezierCurveTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the first control point is added (and nothing is drawn up to it)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(100, 50, 200, 50, 200, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 95,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..c269b72d33
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.bezierCurveTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.bezierCurveTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the first control point is added</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the first control point is added");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(0, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.nonfinite.html
new file mode 100644
index 0000000000..6278a67283
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.nonfinite.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.bezierCurveTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.bezierCurveTo.nonfinite</h1>
+<p class="desc">bezierCurveTo() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("bezierCurveTo() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(-Infinity, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(NaN, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, -Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, NaN, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, -Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, NaN, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, -Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, NaN, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, -Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, NaN, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, -Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, NaN);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.scaled.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.scaled.html
new file mode 100644
index 0000000000..e7a5d8b524
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.scaled.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.bezierCurveTo.scaled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.bezierCurveTo.scaled</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-2, 3.1);
+ ctx.bezierCurveTo(-2, -1, 2.1, -1, 2.1, 3.1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.shape.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.shape.html
new file mode 100644
index 0000000000..cbc2824425
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.bezierCurveTo.shape.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.bezierCurveTo.shape</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.bezierCurveTo.shape</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-2000, 3100);
+ ctx.bezierCurveTo(-2000, -1000, 2100, -1000, 2100, 3100);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.1.html
new file mode 100644
index 0000000000..a5515e4f7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.clip.basic.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.clip.basic.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.2.html
new file mode 100644
index 0000000000..27e1ad0b0c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.basic.2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.clip.basic.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.clip.basic.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(-100, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.empty.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.empty.html
new file mode 100644
index 0000000000..17208ad797
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.empty.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.clip.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.clip.empty</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.intersect.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.intersect.html
new file mode 100644
index 0000000000..c1057f2645
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.intersect.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.clip.intersect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.clip.intersect</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50)
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.unaffected.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.unaffected.html
new file mode 100644
index 0000000000..66f0379602
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.unaffected.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.clip.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.clip.unaffected</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.lineTo(0, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.1.html
new file mode 100644
index 0000000000..46e4db9b6c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.clip.winding.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.clip.winding.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.2.html
new file mode 100644
index 0000000000..9341353a8d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.clip.winding.2.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.clip.winding.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.clip.winding.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.clip();
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.empty.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.empty.html
new file mode 100644
index 0000000000..898cdc5fa6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.empty.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.closePath.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.closePath.empty</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.newline.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.newline.html
new file mode 100644
index 0000000000..07ec4b2a82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.newline.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.closePath.newline</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.closePath.newline</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.closePath();
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.nextpoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.nextpoint.html
new file mode 100644
index 0000000000..648d7ad37a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.closePath.nextpoint.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.closePath.nextpoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.closePath.nextpoint</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -1000);
+ ctx.closePath();
+ ctx.lineTo(1000, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.ellipse.basics.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.ellipse.basics.html
new file mode 100644
index 0000000000..d46dc31d26
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.ellipse.basics.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.ellipse.basics</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.ellipse.basics</h1>
+<p class="desc">Verify canvas throws error when drawing ellipse with negative radii.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify canvas throws error when drawing ellipse with negative radii.");
+_addTest(function(canvas, ctx) {
+
+ ctx.ellipse(10, 10, 10, 5, 0, 0, 1, false);
+ ctx.ellipse(10, 10, 10, 0, 0, 0, 1, false);
+ ctx.ellipse(10, 10, -0, 5, 0, 0, 1, false);
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, -2, 5, 0, 0, 1, false); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, 0, -1.5, 0, 0, 1, false); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, -2, -5, 0, 0, 1, false); });
+ ctx.ellipse(80, 0, 10, 4294967277, Math.PI / -84, -Math.PI / 2147483436, false);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.basic.html
new file mode 100644
index 0000000000..010dbf43e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.fill.closed.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.fill.closed.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.unaffected.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.unaffected.html
new file mode 100644
index 0000000000..e00a6c8f55
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.closed.unaffected.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.fill.closed.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.fill.closed.unaffected</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 10,40, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.html
new file mode 100644
index 0000000000..08782ed11e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.fill.overlap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.fill.overlap</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.path.fill.overlap.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.rect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.rect(10, 10, 80, 30);
+ ctx.fill();
+
+ _assertPixelApprox(canvas, 50,25, 0,127,0,255, 1);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.png b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.png
new file mode 100644
index 0000000000..e2a35d48d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.overlap.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.add.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.add.html
new file mode 100644
index 0000000000..ef9a2a0f93
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.add.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.fill.winding.add</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.fill.winding.add</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.1.html
new file mode 100644
index 0000000000..eecf0198e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.fill.winding.subtract.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.fill.winding.subtract.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.2.html
new file mode 100644
index 0000000000..cf8b6a6ce9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.fill.winding.subtract.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.fill.winding.subtract.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.3.html
new file mode 100644
index 0000000000..e056e33003
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.fill.winding.subtract.3.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.fill.winding.subtract.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.fill.winding.subtract.3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(120, -20);
+ ctx.lineTo(120, 70);
+ ctx.lineTo(-20, 70);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.initial.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.initial.html
new file mode 100644
index 0000000000..a3de93a799
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.initial.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.initial</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.arc.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.arc.html
new file mode 100644
index 0000000000..bc4bc560e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.arc.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.arc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.arc</h1>
+<p class="desc">isPointInPath() works on arcs</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() works on arcs");
+_addTest(function(canvas, ctx) {
+
+ ctx.arc(50, 25, 10, 0, Math.PI, false);
+ _assertSame(ctx.isPointInPath(50, 10), false, "ctx.isPointInPath(50, 10)", "false");
+ _assertSame(ctx.isPointInPath(50, 20), false, "ctx.isPointInPath(50, 20)", "false");
+ _assertSame(ctx.isPointInPath(50, 30), true, "ctx.isPointInPath(50, 30)", "true");
+ _assertSame(ctx.isPointInPath(50, 40), false, "ctx.isPointInPath(50, 40)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), false, "ctx.isPointInPath(70, 30)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.1.html
new file mode 100644
index 0000000000..22562c60a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.basic.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.basic.1</h1>
+<p class="desc">isPointInPath() detects whether the point is inside the path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() detects whether the point is inside the path");
+_addTest(function(canvas, ctx) {
+
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), true, "ctx.isPointInPath(10, 10)", "true");
+ _assertSame(ctx.isPointInPath(30, 10), false, "ctx.isPointInPath(30, 10)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.2.html
new file mode 100644
index 0000000000..9fc1060df8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.basic.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.basic.2</h1>
+<p class="desc">isPointInPath() detects whether the point is inside the path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() detects whether the point is inside the path");
+_addTest(function(canvas, ctx) {
+
+ ctx.rect(20, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(30, 10), true, "ctx.isPointInPath(30, 10)", "true");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.html
new file mode 100644
index 0000000000..5e3edfcc80
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.basic.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.basic</h1>
+<p class="desc">Verify the winding rule in isPointInPath works for for rect path.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify the winding rule in isPointInPath works for for rect path.");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50), true, "ctx.isPointInPath(50, 50)", "true");
+ _assertSame(ctx.isPointInPath(NaN, 50), false, "ctx.isPointInPath(NaN, 50)", "false");
+ _assertSame(ctx.isPointInPath(50, NaN), false, "ctx.isPointInPath(50, NaN)", "false");
+
+ // Testing nonzero isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50, 'nonzero'), true, "ctx.isPointInPath(50, 50, 'nonzero')", "true");
+
+ // Testing evenodd isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50, 'evenodd'), false, "ctx.isPointInPath(50, 50, 'evenodd')", "false");
+
+ // Testing extremely large scale
+ ctx.save();
+ ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0, 'nonzero'), true, "ctx.isPointInPath(0, 0, 'nonzero')", "true");
+ _assertSame(ctx.isPointInPath(0, 0, 'evenodd'), true, "ctx.isPointInPath(0, 0, 'evenodd')", "true");
+ ctx.restore();
+
+ // Check with non-invertible ctm.
+ ctx.save();
+ ctx.scale(0, 0);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0, 'nonzero'), false, "ctx.isPointInPath(0, 0, 'nonzero')", "false");
+ _assertSame(ctx.isPointInPath(0, 0, 'evenodd'), false, "ctx.isPointInPath(0, 0, 'evenodd')", "false");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bezier.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bezier.html
new file mode 100644
index 0000000000..b2f2d23724
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bezier.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.bezier</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.bezier</h1>
+<p class="desc">isPointInPath() works on Bezier curves</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() works on Bezier curves");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(25, 25);
+ ctx.bezierCurveTo(50, -50, 50, 100, 75, 25);
+ _assertSame(ctx.isPointInPath(25, 20), false, "ctx.isPointInPath(25, 20)", "false");
+ _assertSame(ctx.isPointInPath(25, 30), false, "ctx.isPointInPath(25, 30)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), true, "ctx.isPointInPath(30, 20)", "true");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(40, 2), false, "ctx.isPointInPath(40, 2)", "false");
+ _assertSame(ctx.isPointInPath(40, 20), true, "ctx.isPointInPath(40, 20)", "true");
+ _assertSame(ctx.isPointInPath(40, 30), false, "ctx.isPointInPath(40, 30)", "false");
+ _assertSame(ctx.isPointInPath(40, 47), false, "ctx.isPointInPath(40, 47)", "false");
+ _assertSame(ctx.isPointInPath(45, 20), true, "ctx.isPointInPath(45, 20)", "true");
+ _assertSame(ctx.isPointInPath(45, 30), false, "ctx.isPointInPath(45, 30)", "false");
+ _assertSame(ctx.isPointInPath(55, 20), false, "ctx.isPointInPath(55, 20)", "false");
+ _assertSame(ctx.isPointInPath(55, 30), true, "ctx.isPointInPath(55, 30)", "true");
+ _assertSame(ctx.isPointInPath(60, 2), false, "ctx.isPointInPath(60, 2)", "false");
+ _assertSame(ctx.isPointInPath(60, 20), false, "ctx.isPointInPath(60, 20)", "false");
+ _assertSame(ctx.isPointInPath(60, 30), true, "ctx.isPointInPath(60, 30)", "true");
+ _assertSame(ctx.isPointInPath(60, 47), false, "ctx.isPointInPath(60, 47)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), true, "ctx.isPointInPath(70, 30)", "true");
+ _assertSame(ctx.isPointInPath(75, 20), false, "ctx.isPointInPath(75, 20)", "false");
+ _assertSame(ctx.isPointInPath(75, 30), false, "ctx.isPointInPath(75, 30)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bigarc.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bigarc.html
new file mode 100644
index 0000000000..77cb495137
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.bigarc.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.bigarc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.bigarc</h1>
+<p class="desc">isPointInPath() works on unclosed arcs larger than 2pi</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() works on unclosed arcs larger than 2pi");
+_addTest(function(canvas, ctx) {
+
+ ctx.arc(50, 25, 10, 0, 7, false);
+ _assertSame(ctx.isPointInPath(50, 10), false, "ctx.isPointInPath(50, 10)", "false");
+ _assertSame(ctx.isPointInPath(50, 20), true, "ctx.isPointInPath(50, 20)", "true");
+ _assertSame(ctx.isPointInPath(50, 30), true, "ctx.isPointInPath(50, 30)", "true");
+ _assertSame(ctx.isPointInPath(50, 40), false, "ctx.isPointInPath(50, 40)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), false, "ctx.isPointInPath(70, 30)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.edge.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.edge.html
new file mode 100644
index 0000000000..b00da5a2a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.edge.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.edge</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.edge</h1>
+<p class="desc">isPointInPath() counts points on the path as being inside</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() counts points on the path as being inside");
+_addTest(function(canvas, ctx) {
+
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0), true, "ctx.isPointInPath(0, 0)", "true");
+ _assertSame(ctx.isPointInPath(10, 0), true, "ctx.isPointInPath(10, 0)", "true");
+ _assertSame(ctx.isPointInPath(20, 0), true, "ctx.isPointInPath(20, 0)", "true");
+ _assertSame(ctx.isPointInPath(20, 10), true, "ctx.isPointInPath(20, 10)", "true");
+ _assertSame(ctx.isPointInPath(20, 20), true, "ctx.isPointInPath(20, 20)", "true");
+ _assertSame(ctx.isPointInPath(10, 20), true, "ctx.isPointInPath(10, 20)", "true");
+ _assertSame(ctx.isPointInPath(0, 20), true, "ctx.isPointInPath(0, 20)", "true");
+ _assertSame(ctx.isPointInPath(0, 10), true, "ctx.isPointInPath(0, 10)", "true");
+ _assertSame(ctx.isPointInPath(10, -0.01), false, "ctx.isPointInPath(10, -0.01)", "false");
+ _assertSame(ctx.isPointInPath(10, 20.01), false, "ctx.isPointInPath(10, 20.01)", "false");
+ _assertSame(ctx.isPointInPath(-0.01, 10), false, "ctx.isPointInPath(-0.01, 10)", "false");
+ _assertSame(ctx.isPointInPath(20.01, 10), false, "ctx.isPointInPath(20.01, 10)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.empty.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.empty.html
new file mode 100644
index 0000000000..3edfe01a2a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.empty.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.empty</h1>
+<p class="desc">isPointInPath() works when there is no path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() works when there is no path");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.isPointInPath(0, 0), false, "ctx.isPointInPath(0, 0)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.nonfinite.html
new file mode 100644
index 0000000000..4ef541a431
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.nonfinite.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.nonfinite</h1>
+<p class="desc">isPointInPath() returns false for non-finite arguments</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() returns false for non-finite arguments");
+_addTest(function(canvas, ctx) {
+
+ ctx.rect(-100, -50, 200, 100);
+ _assertSame(ctx.isPointInPath(Infinity, 0), false, "ctx.isPointInPath(Infinity, 0)", "false");
+ _assertSame(ctx.isPointInPath(-Infinity, 0), false, "ctx.isPointInPath(-Infinity, 0)", "false");
+ _assertSame(ctx.isPointInPath(NaN, 0), false, "ctx.isPointInPath(NaN, 0)", "false");
+ _assertSame(ctx.isPointInPath(0, Infinity), false, "ctx.isPointInPath(0, Infinity)", "false");
+ _assertSame(ctx.isPointInPath(0, -Infinity), false, "ctx.isPointInPath(0, -Infinity)", "false");
+ _assertSame(ctx.isPointInPath(0, NaN), false, "ctx.isPointInPath(0, NaN)", "false");
+ _assertSame(ctx.isPointInPath(NaN, NaN), false, "ctx.isPointInPath(NaN, NaN)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.outside.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.outside.html
new file mode 100644
index 0000000000..6431879eb6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.outside.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.outside</h1>
+<p class="desc">isPointInPath() works on paths outside the canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() works on paths outside the canvas");
+_addTest(function(canvas, ctx) {
+
+ ctx.rect(0, -100, 20, 20);
+ ctx.rect(20, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(10, -110), false, "ctx.isPointInPath(10, -110)", "false");
+ _assertSame(ctx.isPointInPath(10, -90), true, "ctx.isPointInPath(10, -90)", "true");
+ _assertSame(ctx.isPointInPath(10, -70), false, "ctx.isPointInPath(10, -70)", "false");
+ _assertSame(ctx.isPointInPath(30, -20), false, "ctx.isPointInPath(30, -20)", "false");
+ _assertSame(ctx.isPointInPath(30, 0), true, "ctx.isPointInPath(30, 0)", "true");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.subpath.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.subpath.html
new file mode 100644
index 0000000000..5d664557d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.subpath.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.subpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.subpath</h1>
+<p class="desc">isPointInPath() uses the current path, not just the subpath</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() uses the current path, not just the subpath");
+_addTest(function(canvas, ctx) {
+
+ ctx.rect(0, 0, 20, 20);
+ ctx.beginPath();
+ ctx.rect(20, 0, 20, 20);
+ ctx.closePath();
+ ctx.rect(40, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(30, 10), true, "ctx.isPointInPath(30, 10)", "true");
+ _assertSame(ctx.isPointInPath(50, 10), true, "ctx.isPointInPath(50, 10)", "true");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.1.html
new file mode 100644
index 0000000000..7268c3255f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.transform.1</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.translate(50, 0);
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.2.html
new file mode 100644
index 0000000000..3adac7a419
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.transform.2</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(50, 0);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.3.html
new file mode 100644
index 0000000000..8769c8a593
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.3.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.transform.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.transform.3</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.scale(-1, 1);
+ ctx.rect(-70, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.4.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.4.html
new file mode 100644
index 0000000000..1bcc0bc3a3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.transform.4.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.transform.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.transform.4</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.translate(50, 0);
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(0, 50);
+ _assertSame(ctx.isPointInPath(60, 10), false, "ctx.isPointInPath(60, 10)", "false");
+ _assertSame(ctx.isPointInPath(110, 10), true, "ctx.isPointInPath(110, 10)", "true");
+ _assertSame(ctx.isPointInPath(110, 60), false, "ctx.isPointInPath(110, 60)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.unclosed.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.unclosed.html
new file mode 100644
index 0000000000..2e3b711129
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.unclosed.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.unclosed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.unclosed</h1>
+<p class="desc">isPointInPath() works on unclosed subpaths</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() works on unclosed subpaths");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(20, 0);
+ ctx.lineTo(20, 20);
+ ctx.lineTo(0, 20);
+ _assertSame(ctx.isPointInPath(10, 10), true, "ctx.isPointInPath(10, 10)", "true");
+ _assertSame(ctx.isPointInPath(30, 10), false, "ctx.isPointInPath(30, 10)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.winding.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.winding.html
new file mode 100644
index 0000000000..ec22570797
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInPath.winding.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInPath.winding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInPath.winding</h1>
+<p class="desc">isPointInPath() uses the non-zero winding number rule</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInPath() uses the non-zero winding number rule");
+_addTest(function(canvas, ctx) {
+
+ // Create a square ring, using opposite windings to make a hole in the centre
+ ctx.moveTo(0, 0);
+ ctx.lineTo(50, 0);
+ ctx.lineTo(50, 50);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.lineTo(10, 40);
+ ctx.lineTo(40, 40);
+ ctx.lineTo(40, 10);
+ ctx.lineTo(10, 10);
+
+ _assertSame(ctx.isPointInPath(5, 5), true, "ctx.isPointInPath(5, 5)", "true");
+ _assertSame(ctx.isPointInPath(25, 5), true, "ctx.isPointInPath(25, 5)", "true");
+ _assertSame(ctx.isPointInPath(45, 5), true, "ctx.isPointInPath(45, 5)", "true");
+ _assertSame(ctx.isPointInPath(5, 25), true, "ctx.isPointInPath(5, 25)", "true");
+ _assertSame(ctx.isPointInPath(25, 25), false, "ctx.isPointInPath(25, 25)", "false");
+ _assertSame(ctx.isPointInPath(45, 25), true, "ctx.isPointInPath(45, 25)", "true");
+ _assertSame(ctx.isPointInPath(5, 45), true, "ctx.isPointInPath(5, 45)", "true");
+ _assertSame(ctx.isPointInPath(25, 45), true, "ctx.isPointInPath(25, 45)", "true");
+ _assertSame(ctx.isPointInPath(45, 45), true, "ctx.isPointInPath(45, 45)", "true");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInStroke.scaleddashes.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInStroke.scaleddashes.html
new file mode 100644
index 0000000000..e27baa1549
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInStroke.scaleddashes.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInStroke.scaleddashes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInStroke.scaleddashes</h1>
+<p class="desc">isPointInStroke() should return correct results on dashed paths at high scale factors</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("isPointInStroke() should return correct results on dashed paths at high scale factors");
+_addTest(function(canvas, ctx) {
+
+ var scale = 20;
+ ctx.setLineDash([10, 21.4159]); // dash from t=0 to t=10 along the circle
+ ctx.scale(scale, scale);
+ ctx.ellipse(6, 10, 5, 5, 0, 2*Math.PI, false);
+ ctx.stroke();
+
+ // hit-test the beginning of the dash (t=0)
+ _assertSame(ctx.isPointInStroke(11*scale, 10*scale), true, "ctx.isPointInStroke(11*scale, 10*scale)", "true");
+ // hit-test the middle of the dash (t=5)
+ _assertSame(ctx.isPointInStroke(8.70*scale, 14.21*scale), true, "ctx.isPointInStroke(8.70*scale, 14.21*scale)", "true");
+ // hit-test the end of the dash (t=9.8)
+ _assertSame(ctx.isPointInStroke(4.10*scale, 14.63*scale), true, "ctx.isPointInStroke(4.10*scale, 14.63*scale)", "true");
+ // hit-test past the end of the dash (t=10.2)
+ _assertSame(ctx.isPointInStroke(3.74*scale, 14.46*scale), false, "ctx.isPointInStroke(3.74*scale, 14.46*scale)", "false");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.invalid.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.invalid.html
new file mode 100644
index 0000000000..301c7c447d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.invalid.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInpath.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInpath.invalid</h1>
+<p class="desc">Verify isPointInPath throws exceptions with invalid inputs.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify isPointInPath throws exceptions with invalid inputs.");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 200;
+ canvas.height = 200;
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ // Testing invalid enumeration isPointInPath (w/ and w/o Path object');
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(path, 50, 50, 'gazonk'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(50, 50, 'gazonk'); });
+
+ // Testing invalid type isPointInPath with Path object');
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, null); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(path, 50, 50, null); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, undefined); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50, 'evenodd'); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.multi.path.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.multi.path.html
new file mode 100644
index 0000000000..d71bd8a4c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.isPointInpath.multi.path.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.isPointInpath.multi.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.isPointInpath.multi.path</h1>
+<p class="desc">Verify the winding rule in isPointInPath works for path object.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify the winding rule in isPointInPath works for path object.");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(path, 50, 50), true, "ctx.isPointInPath(path, 50, 50)", "true");
+ _assertSame(ctx.isPointInPath(path, 50, 50, undefined), true, "ctx.isPointInPath(path, 50, 50, undefined)", "true");
+ _assertSame(ctx.isPointInPath(path, NaN, 50), false, "ctx.isPointInPath(path, NaN, 50)", "false");
+ _assertSame(ctx.isPointInPath(path, 50, NaN), false, "ctx.isPointInPath(path, 50, NaN)", "false");
+
+ // Testing nonzero isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(path, 50, 50, 'nonzero'), true, "ctx.isPointInPath(path, 50, 50, 'nonzero')", "true");
+
+ // Testing evenodd isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ assert_false(ctx.isPointInPath(path, 50, 50, 'evenodd'));
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.basic.html
new file mode 100644
index 0000000000..c6f2caa196
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.basic.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.lineTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.lineTo.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..ada8f0ca29
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.lineTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.lineTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the point is added and nothing is drawn</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the point is added and nothing is drawn");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..ac1f9238ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.ensuresubpath.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.lineTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.lineTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the point is added and used for subsequent drawing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the point is added and used for subsequent drawing");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nextpoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nextpoint.html
new file mode 100644
index 0000000000..f403c6b508
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nextpoint.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.lineTo.nextpoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.lineTo.nextpoint</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.details.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.details.html
new file mode 100644
index 0000000000..d7595db7d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.details.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.lineTo.nonfinite.details</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.lineTo.nonfinite.details</h1>
+<p class="desc">lineTo() with Infinity/NaN for first arg still converts the second arg</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/clear-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineTo() with Infinity/NaN for first arg still converts the second arg");
+_addTest(function(canvas, ctx) {
+
+ for (var arg1 of [Infinity, -Infinity, NaN]) {
+ var converted = false;
+ ctx.lineTo(arg1, { valueOf: function() { converted = true; return 0; } });
+ _assert(converted, "converted");
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.html
new file mode 100644
index 0000000000..8246064ea3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.lineTo.nonfinite.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.lineTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.lineTo.nonfinite</h1>
+<p class="desc">lineTo() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("lineTo() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(Infinity, 50);
+ ctx.lineTo(-Infinity, 50);
+ ctx.lineTo(NaN, 50);
+ ctx.lineTo(0, Infinity);
+ ctx.lineTo(0, -Infinity);
+ ctx.lineTo(0, NaN);
+ ctx.lineTo(Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.basic.html
new file mode 100644
index 0000000000..d08d67621e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.basic.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.moveTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.moveTo.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 10, 50);
+ ctx.moveTo(100, 0);
+ ctx.lineTo(10, 0);
+ ctx.lineTo(10, 50);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 90,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.multiple.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.multiple.html
new file mode 100644
index 0000000000..384d9fb437
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.multiple.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.moveTo.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.moveTo.multiple</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 25);
+ ctx.moveTo(100, 25);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.newsubpath.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.newsubpath.html
new file mode 100644
index 0000000000..2a3d69b637
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.newsubpath.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.moveTo.newsubpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.moveTo.newsubpath</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.moveTo(100, 0);
+ ctx.moveTo(100, 50);
+ ctx.moveTo(0, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.nonfinite.html
new file mode 100644
index 0000000000..c6cd6f8178
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.moveTo.nonfinite.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.moveTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.moveTo.nonfinite</h1>
+<p class="desc">moveTo() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("moveTo() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.moveTo(Infinity, 50);
+ ctx.moveTo(-Infinity, 50);
+ ctx.moveTo(NaN, 50);
+ ctx.moveTo(0, Infinity);
+ ctx.moveTo(0, -Infinity);
+ ctx.moveTo(0, NaN);
+ ctx.moveTo(Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.basic.html
new file mode 100644
index 0000000000..ffbf039440
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.basic.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.quadraticCurveTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.quadraticCurveTo.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.quadraticCurveTo(100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..f8b8b515ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.quadraticCurveTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.quadraticCurveTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the first control point is added (and nothing is drawn up to it)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(100, 50, 200, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 95,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..189c744c65
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.quadraticCurveTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.quadraticCurveTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the first control point is added</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("If there is no subpath, the first control point is added");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(0, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.nonfinite.html
new file mode 100644
index 0000000000..1488e78a00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.nonfinite.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.quadraticCurveTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.quadraticCurveTo.nonfinite</h1>
+<p class="desc">quadraticCurveTo() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("quadraticCurveTo() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.quadraticCurveTo(Infinity, 50, 0, 50);
+ ctx.quadraticCurveTo(-Infinity, 50, 0, 50);
+ ctx.quadraticCurveTo(NaN, 50, 0, 50);
+ ctx.quadraticCurveTo(0, Infinity, 0, 50);
+ ctx.quadraticCurveTo(0, -Infinity, 0, 50);
+ ctx.quadraticCurveTo(0, NaN, 0, 50);
+ ctx.quadraticCurveTo(0, 50, Infinity, 50);
+ ctx.quadraticCurveTo(0, 50, -Infinity, 50);
+ ctx.quadraticCurveTo(0, 50, NaN, 50);
+ ctx.quadraticCurveTo(0, 50, 0, Infinity);
+ ctx.quadraticCurveTo(0, 50, 0, -Infinity);
+ ctx.quadraticCurveTo(0, 50, 0, NaN);
+ ctx.quadraticCurveTo(Infinity, Infinity, 0, 50);
+ ctx.quadraticCurveTo(Infinity, Infinity, Infinity, 50);
+ ctx.quadraticCurveTo(Infinity, Infinity, Infinity, Infinity);
+ ctx.quadraticCurveTo(Infinity, Infinity, 0, Infinity);
+ ctx.quadraticCurveTo(Infinity, 50, Infinity, 50);
+ ctx.quadraticCurveTo(Infinity, 50, Infinity, Infinity);
+ ctx.quadraticCurveTo(Infinity, 50, 0, Infinity);
+ ctx.quadraticCurveTo(0, Infinity, Infinity, 50);
+ ctx.quadraticCurveTo(0, Infinity, Infinity, Infinity);
+ ctx.quadraticCurveTo(0, Infinity, 0, Infinity);
+ ctx.quadraticCurveTo(0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.scaled.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.scaled.html
new file mode 100644
index 0000000000..90973a8f97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.scaled.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.quadraticCurveTo.scaled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.quadraticCurveTo.scaled</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-1, 1.05);
+ ctx.quadraticCurveTo(0, -1, 1.2, 1.05);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.shape.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.shape.html
new file mode 100644
index 0000000000..23725c2421
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.quadraticCurveTo.shape.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.quadraticCurveTo.shape</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.quadraticCurveTo.shape</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-1000, 1050);
+ ctx.quadraticCurveTo(0, -1000, 1200, 1050);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.basic.html
new file mode 100644
index 0000000000..716cd29af7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.basic.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.closed.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.closed.html
new file mode 100644
index 0000000000..385a88ab7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.closed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.closed</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.rect(100, 50, 100, 100);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.1.html
new file mode 100644
index 0000000000..e1fd8052d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.end.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.end.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.rect(200, 100, 400, 1000);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.2.html
new file mode 100644
index 0000000000..d30bb111f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.end.2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.end.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.end.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.rect(150, 150, 2000, 2000);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.negative.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.negative.html
new file mode 100644
index 0000000000..b917137bae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.negative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.negative</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 50, 25);
+ ctx.rect(100, 0, -50, 25);
+ ctx.rect(0, 50, 50, -25);
+ ctx.rect(100, 50, -50, -25);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.newsubpath.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.newsubpath.html
new file mode 100644
index 0000000000..32b70ff2d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.newsubpath.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.newsubpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.newsubpath</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.rect(200, 25, 1, 1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.nonfinite.html
new file mode 100644
index 0000000000..c0d245a49c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.nonfinite.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.nonfinite</h1>
+<p class="desc">rect() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("rect() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.rect(Infinity, 50, 1, 1);
+ ctx.rect(-Infinity, 50, 1, 1);
+ ctx.rect(NaN, 50, 1, 1);
+ ctx.rect(0, Infinity, 1, 1);
+ ctx.rect(0, -Infinity, 1, 1);
+ ctx.rect(0, NaN, 1, 1);
+ ctx.rect(0, 50, Infinity, 1);
+ ctx.rect(0, 50, -Infinity, 1);
+ ctx.rect(0, 50, NaN, 1);
+ ctx.rect(0, 50, 1, Infinity);
+ ctx.rect(0, 50, 1, -Infinity);
+ ctx.rect(0, 50, 1, NaN);
+ ctx.rect(Infinity, Infinity, 1, 1);
+ ctx.rect(Infinity, Infinity, Infinity, 1);
+ ctx.rect(Infinity, Infinity, Infinity, Infinity);
+ ctx.rect(Infinity, Infinity, 1, Infinity);
+ ctx.rect(Infinity, 50, Infinity, 1);
+ ctx.rect(Infinity, 50, Infinity, Infinity);
+ ctx.rect(Infinity, 50, 1, Infinity);
+ ctx.rect(0, Infinity, Infinity, 1);
+ ctx.rect(0, Infinity, Infinity, Infinity);
+ ctx.rect(0, Infinity, 1, Infinity);
+ ctx.rect(0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.selfintersect.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.selfintersect.html
new file mode 100644
index 0000000000..8ebbf0c1f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.selfintersect.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.selfintersect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.selfintersect</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.rect(45, 20, 10, 10);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.winding.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.winding.html
new file mode 100644
index 0000000000..d7350ef38c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.winding.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.winding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.winding</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.rect(0, 0, 50, 50);
+ ctx.rect(100, 50, -50, -50);
+ ctx.rect(0, 25, 100, -25);
+ ctx.rect(100, 25, -100, 25);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.1.html
new file mode 100644
index 0000000000..c773ef61d0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.zero.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(0, 50, 100, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.2.html
new file mode 100644
index 0000000000..5888231fc6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.zero.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, -100, 0, 250);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.3.html
new file mode 100644
index 0000000000..c000b008fb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.3.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.zero.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.zero.3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.4.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.4.html
new file mode 100644
index 0000000000..bb3ecb3aa5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.4.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.zero.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.zero.4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.rect(100, 25, 0, 0);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.5.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.5.html
new file mode 100644
index 0000000000..081c753d91
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.5.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.zero.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.zero.5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.rect(100, 25, 0, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.6.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.6.html
new file mode 100644
index 0000000000..dafb273d56
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.rect.zero.6.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.rect.zero.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.rect.zero.6</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.rect(100, 25, 1000, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.html
new file mode 100644
index 0000000000..3bb890ae96
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.1.radius.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.1.radius.dompoint</h1>
+<p class="desc">Verify that when one radius is given to roundRect(), specified as a DOMPoint, it applies to all corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a DOMPoint, it applies to all corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html
new file mode 100644
index 0000000000..96e933870b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.1.radius.dompoint.single argument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.1.radius.dompoint.single argument</h1>
+<p class="desc">Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPoint, it applies to all corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPoint, it applies to all corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, new DOMPoint(40, 20));
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.html
new file mode 100644
index 0000000000..1315233d79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.1.radius.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.1.radius.dompointinit</h1>
+<p class="desc">Verify that when one radius is given to roundRect(), specified as a DOMPointInit, applies to all corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a DOMPointInit, applies to all corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html
new file mode 100644
index 0000000000..f5c6d00959
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.1.radius.dompointinit.single.argument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.1.radius.dompointinit.single.argument</h1>
+<p class="desc">Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPointInit, applies to all corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPointInit, applies to all corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, {x: 40, y: 20});
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.html
new file mode 100644
index 0000000000..402631a7a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.1.radius.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.1.radius.double</h1>
+<p class="desc">Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.single.argument.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.single.argument.html
new file mode 100644
index 0000000000..ed4887c2de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.1.radius.double.single.argument.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.1.radius.double.single.argument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.1.radius.double.single.argument</h1>
+<p class="desc">Verify that when one radius is given to roundRect() as a non-array argument, specified as a double, it applies to all corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a double, it applies to all corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, 20);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompoint.html
new file mode 100644
index 0000000000..00a9c14582
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompoint.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.2.radii.1.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.2.radii.1.dompoint</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html
new file mode 100644
index 0000000000..e9b3a7bf79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.2.radii.1.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.2.radii.1.dompointinit</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left and bottom-right corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left and bottom-right corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.double.html
new file mode 100644
index 0000000000..a2f02104e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.1.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.2.radii.1.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.2.radii.1.double</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompoint.html
new file mode 100644
index 0000000000..57df30d5ae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompoint.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.2.radii.2.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.2.radii.2.dompoint</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html
new file mode 100644
index 0000000000..5942ab750f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.2.radii.2.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.2.radii.2.dompointinit</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right and bottom-left corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right and bottom-left corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.double.html
new file mode 100644
index 0000000000..2390a61dfd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.2.radii.2.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.2.radii.2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.2.radii.2.double</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompoint.html
new file mode 100644
index 0000000000..f4fa76afac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompoint.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.1.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.1.dompoint</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html
new file mode 100644
index 0000000000..4e7dc947f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.1.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.1.dompointinit</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.double.html
new file mode 100644
index 0000000000..d7888da6d9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.1.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.1.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.1.double</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompoint.html
new file mode 100644
index 0000000000..38a8baac53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompoint.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.2.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.2.dompoint</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html
new file mode 100644
index 0000000000..5e867caf42
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.2.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.2.dompointinit</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.double.html
new file mode 100644
index 0000000000..41edff205c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.2.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.2.double</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompoint.html
new file mode 100644
index 0000000000..6d6a1a6ae0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompoint.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.3.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.3.dompoint</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html
new file mode 100644
index 0000000000..132420e343
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.3.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.3.dompointinit</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.double.html
new file mode 100644
index 0000000000..75a8e924ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.3.radii.3.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.3.radii.3.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.3.radii.3.double</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompoint.html
new file mode 100644
index 0000000000..b681b59f4e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompoint.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.1.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.1.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html
new file mode 100644
index 0000000000..e5542894b3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.1.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.1.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.double.html
new file mode 100644
index 0000000000..028effa82b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.1.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.1.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.1.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompoint.html
new file mode 100644
index 0000000000..d3c16eb493
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompoint.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.2.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.2.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html
new file mode 100644
index 0000000000..ab5ad3f4da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.2.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.2.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.double.html
new file mode 100644
index 0000000000..ec229e396a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.2.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.2.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompoint.html
new file mode 100644
index 0000000000..5367543fa3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompoint.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.3.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.3.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html
new file mode 100644
index 0000000000..89df524d53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.3.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.3.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.double.html
new file mode 100644
index 0000000000..22ba747b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.3.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.3.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.3.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompoint.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompoint.html
new file mode 100644
index 0000000000..b15c291884
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompoint.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.4.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.4.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html
new file mode 100644
index 0000000000..d9b03a1225
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.4.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.4.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPointInit, applies to the bottom-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPointInit, applies to the bottom-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.double.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.double.html
new file mode 100644
index 0000000000..62ccf85958
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.4.radii.4.double.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.4.radii.4.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.4.radii.4.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.badinput.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.badinput.html
new file mode 100644
index 0000000000..39e1d0c655
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.badinput.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.badinput</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.badinput</h1>
+<p class="desc">roundRect() throws or does not throw errors given the strange inputs.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("roundRect() throws or does not throw errors given the strange inputs.");
+_addTest(function(canvas, ctx) {
+
+ ctx.roundRect(0, 0, 100, 100, { foo: "bar" }); //=> DOMPointInit
+ ctx.roundRect(0, 0, 100, 100, undefined); //=> "missing" -> 0
+ ctx.roundRect(0, 0, 100, 100, [[]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [[25]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [undefined]); //=> « DOMPointInit »
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, 0n); });
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, { x: 0n }); });
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, [{ x: 0n }]); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.closed.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.closed.html
new file mode 100644
index 0000000000..89aeb3b865
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.closed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.closed</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.roundRect(100, 50, 100, 100, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.1.html
new file mode 100644
index 0000000000..be98ce5841
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.end.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.end.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(200, 100, 400, 1000, [0]);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.2.html
new file mode 100644
index 0000000000..16131ad345
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.end.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.end.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.roundRect(150, 150, 2000, 2000, [0]);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.3.html
new file mode 100644
index 0000000000..f9454080eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.3.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.end.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.end.3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(101, 51, 2000, 2000, [500, 500, 500, 500]);
+ ctx.lineTo(-1, -1);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.4.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.4.html
new file mode 100644
index 0000000000..52d49c32e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.end.4.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.end.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.end.4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.roundRect(-1, -1, 2000, 2000, [1000, 1000, 1000, 1000]);
+ ctx.lineTo(-150, -150);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.negative.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.negative.html
new file mode 100644
index 0000000000..66e1172432
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.negative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.negative</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.roundRect(0, 0, 50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 0, -50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(0, 50, 50, -25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 50, -50, -25, [10, 0, 0, 0]);
+ ctx.fill();
+ // All rects drawn
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ // Correct corners are rounded.
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.newsubpath.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.newsubpath.html
new file mode 100644
index 0000000000..df3990a376
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.newsubpath.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.newsubpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.newsubpath</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.roundRect(200, 25, 1, 1, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.nonfinite.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.nonfinite.html
new file mode 100644
index 0000000000..5f0f0b6eaf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.nonfinite.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.nonfinite</h1>
+<p class="desc">roundRect() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("roundRect() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.roundRect(Infinity, 50, 1, 1, [0]);
+ ctx.roundRect(-Infinity, 50, 1, 1, [0]);
+ ctx.roundRect(NaN, 50, 1, 1, [0]);
+ ctx.roundRect(0, Infinity, 1, 1, [0]);
+ ctx.roundRect(0, -Infinity, 1, 1, [0]);
+ ctx.roundRect(0, NaN, 1, 1, [0]);
+ ctx.roundRect(0, 50, Infinity, 1, [0]);
+ ctx.roundRect(0, 50, -Infinity, 1, [0]);
+ ctx.roundRect(0, 50, NaN, 1, [0]);
+ ctx.roundRect(0, 50, 1, Infinity, [0]);
+ ctx.roundRect(0, 50, 1, -Infinity, [0]);
+ ctx.roundRect(0, 50, 1, NaN, [0]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,NaN]);
+ ctx.roundRect(Infinity, Infinity, 1, 1, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, 1, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, Infinity, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, Infinity, 1, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, 1, Infinity, [0]);
+ ctx.roundRect(Infinity, Infinity, 1, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, 1, 1, [Infinity]);
+ ctx.roundRect(Infinity, 50, Infinity, 1, [0]);
+ ctx.roundRect(Infinity, 50, Infinity, Infinity, [0]);
+ ctx.roundRect(Infinity, 50, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, 50, Infinity, 1, [Infinity]);
+ ctx.roundRect(Infinity, 50, 1, Infinity, [0]);
+ ctx.roundRect(Infinity, 50, 1, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, 50, 1, 1, [Infinity]);
+ ctx.roundRect(0, Infinity, Infinity, 1, [0]);
+ ctx.roundRect(0, Infinity, Infinity, Infinity, [0]);
+ ctx.roundRect(0, Infinity, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(0, Infinity, Infinity, 1, [Infinity]);
+ ctx.roundRect(0, Infinity, 1, Infinity, [0]);
+ ctx.roundRect(0, Infinity, 1, Infinity, [Infinity]);
+ ctx.roundRect(0, Infinity, 1, 1, [Infinity]);
+ ctx.roundRect(0, 50, Infinity, Infinity, [0]);
+ ctx.roundRect(0, 50, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(0, 50, Infinity, 1, [Infinity]);
+ ctx.roundRect(0, 50, 1, Infinity, [Infinity]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, -Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, NaN)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(-Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(NaN, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: -Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: NaN}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: -Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: NaN, y: 10}]);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.1.html
new file mode 100644
index 0000000000..913b17e45e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.1.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.radius.intersecting.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.radius.intersecting.1</h1>
+<p class="desc">Check that roundRects with intersecting corner arcs are rendered correctly.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that roundRects with intersecting corner arcs are rendered correctly.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [40, 40, 40, 40]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 2,25, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 97,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.2.html
new file mode 100644
index 0000000000..abd7f86088
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.intersecting.2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.radius.intersecting.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.radius.intersecting.2</h1>
+<p class="desc">Check that roundRects with intersecting corner arcs are rendered correctly.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that roundRects with intersecting corner arcs are rendered correctly.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [1000, 1000, 1000, 1000]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 2,25, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 97,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.negative.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.negative.html
new file mode 100644
index 0000000000..ac2532dcfe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.negative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.radius.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.radius.negative</h1>
+<p class="desc">roundRect() with negative radius throws an exception</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("roundRect() with negative radius throws an exception");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [-1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [1, -1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(-1, 1), 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(1, -1)])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: -1, y: 1}, 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: 1, y: -1}])});
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noargument.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noargument.html
new file mode 100644
index 0000000000..cfaf87ecf2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noargument.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.radius.noargument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.radius.noargument</h1>
+<p class="desc">Check that roundRect draws a rectangle when no radii are provided.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that roundRect draws a rectangle when no radii are provided.");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(10, 10, 80, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ // upper left corner (10, 10)
+ _assertPixel(canvas, 10,9, 255,0,0,255);
+ _assertPixel(canvas, 9,10, 255,0,0,255);
+ _assertPixel(canvas, 10,10, 0,255,0,255);
+
+ // upper right corner (89, 10)
+ _assertPixel(canvas, 90,10, 255,0,0,255);
+ _assertPixel(canvas, 89,9, 255,0,0,255);
+ _assertPixel(canvas, 89,10, 0,255,0,255);
+
+ // lower right corner (89, 39)
+ _assertPixel(canvas, 89,40, 255,0,0,255);
+ _assertPixel(canvas, 90,39, 255,0,0,255);
+ _assertPixel(canvas, 89,39, 0,255,0,255);
+
+ // lower left corner (10, 30)
+ _assertPixel(canvas, 9,39, 255,0,0,255);
+ _assertPixel(canvas, 10,40, 255,0,0,255);
+ _assertPixel(canvas, 10,39, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noarugment.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noarugment.html
new file mode 100644
index 0000000000..08f576b379
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.noarugment.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.radius.noarugment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.radius.noarugment</h1>
+<p class="desc">Check that roundRect draws a rectangle when no radii are provided.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that roundRect draws a rectangle when no radii are provided.");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+ctx.roundRect(10, 10, 80, 30);
+ctx.fillStyle = '#0f0';
+ctx.fill();
+// upper left corner (10, 10)
+_assertPixel(canvas, 10,9, 255,0,0,255);
+_assertPixel(canvas, 9,10, 255,0,0,255);
+_assertPixel(canvas, 10,10, 0,255,0,255);
+
+// upper right corner (89, 10)
+_assertPixel(canvas, 90,10, 255,0,0,255);
+_assertPixel(canvas, 89,9, 255,0,0,255);
+_assertPixel(canvas, 89,10, 0,255,0,255);
+
+// lower right corner (89, 39)
+_assertPixel(canvas, 89,40, 255,0,0,255);
+_assertPixel(canvas, 90,39, 255,0,0,255);
+_assertPixel(canvas, 89,39, 0,255,0,255);
+
+// lower left corner (10, 30)
+_assertPixel(canvas, 9,39, 255,0,0,255);
+_assertPixel(canvas, 10,40, 255,0,0,255);
+_assertPixel(canvas, 10,39, 0,255,0,255);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.none.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.none.html
new file mode 100644
index 0000000000..8363285ffc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.none.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.radius.none</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.radius.none</h1>
+<p class="desc">Check that roundRect throws an RangeError if radii is an empty array.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that roundRect throws an RangeError if radii is an empty array.");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [])});
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.toomany.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.toomany.html
new file mode 100644
index 0000000000..d8fe4bb45c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.radius.toomany.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.radius.toomany</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.radius.toomany</h1>
+<p class="desc">Check that roundRect throws an IndeSizeError if radii has more than four items.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Check that roundRect throws an IndeSizeError if radii has more than four items.");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 0, 0])});
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.selfintersect.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.selfintersect.html
new file mode 100644
index 0000000000..7a69aaf4c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.selfintersect.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.selfintersect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.selfintersect</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 100, 50, [0]);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.roundRect(45, 20, 10, 10, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.winding.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.winding.html
new file mode 100644
index 0000000000..9c1f985eb9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.winding.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.winding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.winding</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 50, 50, [0]);
+ ctx.roundRect(100, 50, -50, -50, [0]);
+ ctx.roundRect(0, 25, 100, -25, [0]);
+ ctx.roundRect(100, 25, -100, 25, [0]);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.1.html
new file mode 100644
index 0000000000..4e9a804dc2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.zero.1</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(0, 50, 100, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.2.html
new file mode 100644
index 0000000000..ac64fa7818
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.zero.2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, -100, 0, 250, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.3.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.3.html
new file mode 100644
index 0000000000..a807715b35
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.3.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.zero.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.zero.3</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, 25, 0, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.4.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.4.html
new file mode 100644
index 0000000000..fc51918c27
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.4.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.zero.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.zero.4</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.5.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.5.html
new file mode 100644
index 0000000000..de7d41b68f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.5.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.zero.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.zero.5</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.6.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.6.html
new file mode 100644
index 0000000000..1e8e338a73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.roundrect.zero.6.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.roundrect.zero.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.roundrect.zero.6</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.roundRect(100, 25, 1000, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.empty.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.empty.html
new file mode 100644
index 0000000000..aa459ec986
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.empty.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.empty</h1>
+<p class="desc">Empty subpaths are not stroked</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Empty subpaths are not stroked");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(40, 25);
+ ctx.moveTo(60, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.html
new file mode 100644
index 0000000000..a577f22769
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.overlap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.overlap</h1>
+<p class="desc">Stroked subpaths are combined before being drawn</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.path.stroke.overlap.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Stroked subpaths are combined before being drawn");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 20);
+ ctx.lineTo(100, 20);
+ ctx.moveTo(0, 30);
+ ctx.lineTo(100, 30);
+ ctx.stroke();
+
+ _assertPixelApprox(canvas, 50,25, 0,127,0,255, 1);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.png b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.png
new file mode 100644
index 0000000000..e2a35d48d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.overlap.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.arc.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.arc.html
new file mode 100644
index 0000000000..6474a15424
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.arc.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.prune.arc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.prune.arc</h1>
+<p class="desc">Zero-length line segments from arcTo and arc are removed before stroking</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Zero-length line segments from arcTo and arc are removed before stroking");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 150, 25, 10);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(60, 25);
+ ctx.arc(50, 25, 10, 0, 0, false);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.closed.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.closed.html
new file mode 100644
index 0000000000..16b46852a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.closed.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.prune.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.prune.closed</h1>
+<p class="desc">Zero-length line segments from closed paths are removed before stroking</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Zero-length line segments from closed paths are removed before stroking");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.corner.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.corner.html
new file mode 100644
index 0000000000..226fccc6d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.corner.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.prune.corner</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.prune.corner</h1>
+<p class="desc">Zero-length line segments are removed before stroking with miters</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Zero-length line segments are removed before stroking with miters");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.4;
+
+ ctx.beginPath();
+ ctx.moveTo(-1000, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 1000);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.curve.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.curve.html
new file mode 100644
index 0000000000..8023b7b3f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.curve.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.prune.curve</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.prune.curve</h1>
+<p class="desc">Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.quadraticCurveTo(50, 25, 50, 25);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.line.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.line.html
new file mode 100644
index 0000000000..97f88847c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.line.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.prune.line</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.prune.line</h1>
+<p class="desc">Zero-length line segments from lineTo are removed before stroking</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Zero-length line segments from lineTo are removed before stroking");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.rect.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.rect.html
new file mode 100644
index 0000000000..bf1fc9aa20
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.prune.rect.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.prune.rect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.prune.rect</h1>
+<p class="desc">Zero-length line segments from rect and strokeRect are removed before stroking</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Zero-length line segments from rect and strokeRect are removed before stroking");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+
+ ctx.strokeRect(50, 25, 0, 0);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale1.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale1.html
new file mode 100644
index 0000000000..3e4651ec4a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale1.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.scale1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.scale1</h1>
+<p class="desc">Stroke line widths are scaled by the current transformation matrix</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Stroke line widths are scaled by the current transformation matrix");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale2.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale2.html
new file mode 100644
index 0000000000..099efec76c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.scale2.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.scale2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.scale2</h1>
+<p class="desc">Stroke line widths are scaled by the current transformation matrix</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Stroke line widths are scaled by the current transformation matrix");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.skew.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.skew.html
new file mode 100644
index 0000000000..86f3eebf1d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.skew.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.skew</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.skew</h1>
+<p class="desc">Strokes lines are skewed by the current transformation matrix</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Strokes lines are skewed by the current transformation matrix");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(49, -50);
+ ctx.lineTo(201, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 283);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.unaffected.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.unaffected.html
new file mode 100644
index 0000000000..8e6b41946f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.unaffected.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.unaffected</h1>
+<p class="desc">Stroking does not start a new path or subpath</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Stroking does not start a new path or subpath");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+
+ ctx.closePath();
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.union.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.union.html
new file mode 100644
index 0000000000..73198ab24d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.stroke.union.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.stroke.union</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.stroke.union</h1>
+<p class="desc">Strokes in opposite directions are unioned, not subtracted</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Strokes in opposite directions are unioned, not subtracted");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 40;
+ ctx.moveTo(0, 10);
+ ctx.lineTo(100, 10);
+ ctx.moveTo(100, 40);
+ ctx.lineTo(0, 40);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.basic.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.basic.html
new file mode 100644
index 0000000000..1cdc81e4f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.basic.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.transformation.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.transformation.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-100, 0);
+ ctx.rect(100, 0, 100, 50);
+ ctx.translate(0, -100);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.changing.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.changing.html
new file mode 100644
index 0000000000..8f711b6b9a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.changing.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.transformation.changing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.transformation.changing</h1>
+<p class="desc">Transformations are applied while building paths, not when drawing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Transformations are applied while building paths, not when drawing");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.translate(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.translate(-100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(1000, 1000);
+ ctx.rotate(Math.PI/2);
+ ctx.scale(0.1, 0.1);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.multiple.html b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.multiple.html
new file mode 100644
index 0000000000..af06c06371
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/path-objects/2d.path.transformation.multiple.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.path.transformation.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.path.transformation.multiple</h1>
+<p class="desc">Transformations are applied while building paths, not when drawing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Transformations are applied while building paths, not when drawing");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.translate(-100, 0);
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ ctx.translate(100, 0);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.translate(0, -50);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ ctx.translate(0, 50);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create.and.resize.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create.and.resize.html
new file mode 100644
index 0000000000..1ecd3fbd65
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create.and.resize.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create.and.resize</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create.and.resize</h1>
+<p class="desc">Verify no crash when resizing an image bitmap to zero.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify no crash when resizing an image bitmap to zero.");
+_addTest(function(canvas, ctx) {
+
+ var image = new Image();
+ image.onload = t.step_func(function() {
+ var options = { resizeHeight: 0 };
+ var p1 = createImageBitmap(image, options);
+ p1.catch(function(error){});
+ t.done();
+ });
+ image.src = 'red.png';
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.basic.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.basic.html
new file mode 100644
index 0000000000..1ea73690e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.basic.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create1.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create1.basic</h1>
+<p class="desc">createImageData(imgdata) exists and returns something</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(imgdata) exists and returns something");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(ctx.createImageData(ctx.createImageData(1, 1)), null, "ctx.createImageData(ctx.createImageData(1, 1))", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.initial.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.initial.html
new file mode 100644
index 0000000000..8709623a0c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.initial.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create1.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create1.initial</h1>
+<p class="desc">createImageData(imgdata) returns transparent black data of the right size</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(imgdata) returns transparent black data of the right size");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata1 = ctx.getImageData(0, 0, 10, 20);
+ var imgdata2 = ctx.createImageData(imgdata1);
+ _assertSame(imgdata2.data.length, imgdata1.data.length, "imgdata2.data.length", "imgdata1.data.length");
+ _assertSame(imgdata2.width, imgdata1.width, "imgdata2.width", "imgdata1.width");
+ _assertSame(imgdata2.height, imgdata1.height, "imgdata2.height", "imgdata1.height");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata2.data.length; ++i)
+ if (imgdata2.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.this.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.this.html
new file mode 100644
index 0000000000..dd4b80ec08
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.this.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create1.this</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create1.this</h1>
+<p class="desc">createImageData(imgdata) should throw when called with the wrong |this|</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(imgdata) should throw when called with the wrong |this|");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.createImageData(1, 1);
+ assert_throws_js(TypeError, function() { CanvasRenderingContext2D.prototype.createImageData.call(null, imgdata); });
+ assert_throws_js(TypeError, function() { CanvasRenderingContext2D.prototype.createImageData.call(undefined, imgdata); });
+ assert_throws_js(TypeError, function() { CanvasRenderingContext2D.prototype.createImageData.call({}, imgdata); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.type.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.type.html
new file mode 100644
index 0000000000..36f3db99be
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.type.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create1.type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create1.type</h1>
+<p class="desc">createImageData(imgdata) returns an ImageData object containing a Uint8ClampedArray object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(imgdata) returns an ImageData object containing a Uint8ClampedArray object");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.ImageData, undefined, "window.ImageData", "undefined");
+ _assertDifferent(window.Uint8ClampedArray, undefined, "window.Uint8ClampedArray", "undefined");
+ window.ImageData.prototype.thisImplementsImageData = true;
+ window.Uint8ClampedArray.prototype.thisImplementsUint8ClampedArray = true;
+ var imgdata = ctx.createImageData(ctx.createImageData(1, 1));
+ _assert(imgdata.thisImplementsImageData, "imgdata.thisImplementsImageData");
+ _assert(imgdata.data.thisImplementsUint8ClampedArray, "imgdata.data.thisImplementsUint8ClampedArray");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.zero.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.zero.html
new file mode 100644
index 0000000000..02619376a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create1.zero.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create1.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create1.zero</h1>
+<p class="desc">createImageData(null) throws TypeError</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(null) throws TypeError");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createImageData(null); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.basic.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.basic.html
new file mode 100644
index 0000000000..807b6dbfa2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.basic.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.basic</h1>
+<p class="desc">createImageData(sw, sh) exists and returns something</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(sw, sh) exists and returns something");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(ctx.createImageData(1, 1), null, "ctx.createImageData(1, 1)", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.double.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.double.html
new file mode 100644
index 0000000000..31aa42d5a8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.double.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.double</h1>
+<p class="desc">createImageData(w, h) double is converted to long</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(w, h) double is converted to long");
+_addTest(function(canvas, ctx) {
+
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.createImageData(-10.01, -10.99);
+ _assertSame(imgdata1.width, 10, "imgdata1.width", "10");
+ _assertSame(imgdata1.height, 10, "imgdata1.height", "10");
+ _assertSame(imgdata2.width, 10, "imgdata2.width", "10");
+ _assertSame(imgdata2.height, 10, "imgdata2.height", "10");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.initial.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.initial.html
new file mode 100644
index 0000000000..c2a69b4c06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.initial.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.initial</h1>
+<p class="desc">createImageData(sw, sh) returns transparent black data of the right size</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(sw, sh) returns transparent black data of the right size");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.createImageData(10, 20);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ _assert(imgdata.width < imgdata.height, "imgdata.width < imgdata.height");
+ _assert(imgdata.width > 0, "imgdata.width > 0");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; ++i)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.large.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.large.html
new file mode 100644
index 0000000000..73b2a1c19f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.large.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.large</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.large</h1>
+<p class="desc">createImageData(sw, sh) works for sizes much larger than the canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(sw, sh) works for sizes much larger than the canvas");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.createImageData(1000, 2000);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ _assert(imgdata.width < imgdata.height, "imgdata.width < imgdata.height");
+ _assert(imgdata.width > 0, "imgdata.width > 0");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; i += 7813) // check ~1024 points (assuming normal scaling)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.negative.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.negative.html
new file mode 100644
index 0000000000..7c4a3944eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.negative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.negative</h1>
+<p class="desc">createImageData(sw, sh) takes the absolute magnitude of the size arguments</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(sw, sh) takes the absolute magnitude of the size arguments");
+_addTest(function(canvas, ctx) {
+
+ var imgdata1 = ctx.createImageData(10, 20);
+ var imgdata2 = ctx.createImageData(-10, 20);
+ var imgdata3 = ctx.createImageData(10, -20);
+ var imgdata4 = ctx.createImageData(-10, -20);
+ _assertSame(imgdata1.data.length, imgdata2.data.length, "imgdata1.data.length", "imgdata2.data.length");
+ _assertSame(imgdata2.data.length, imgdata3.data.length, "imgdata2.data.length", "imgdata3.data.length");
+ _assertSame(imgdata3.data.length, imgdata4.data.length, "imgdata3.data.length", "imgdata4.data.length");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.nonfinite.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.nonfinite.html
new file mode 100644
index 0000000000..af30dc9124
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.nonfinite.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.nonfinite</h1>
+<p class="desc">createImageData() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData() throws TypeError if arguments are not finite");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.createImageData(Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(-Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(Infinity, Infinity); });
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ assert_throws_js(TypeError, function() { ctx.createImageData(posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(neginfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(nanobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, neginfobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, nanobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(posinfobj, posinfobj); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.round.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.round.html
new file mode 100644
index 0000000000..c62243f418
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.round.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.round</h1>
+<p class="desc">createImageData(w, h) is rounded the same as getImageData(0, 0, w, h)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(w, h) is rounded the same as getImageData(0, 0, w, h)");
+_addTest(function(canvas, ctx) {
+
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, 10.01, 10.99);
+ _assertSame(imgdata1.width, imgdata2.width, "imgdata1.width", "imgdata2.width");
+ _assertSame(imgdata1.height, imgdata2.height, "imgdata1.height", "imgdata2.height");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.this.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.this.html
new file mode 100644
index 0000000000..f5373d0edd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.this.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.this</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.this</h1>
+<p class="desc">createImageData(sw, sh) should throw when called with the wrong |this|</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(sw, sh) should throw when called with the wrong |this|");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { CanvasRenderingContext2D.prototype.createImageData.call(null, 1, 1); });
+ assert_throws_js(TypeError, function() { CanvasRenderingContext2D.prototype.createImageData.call(undefined, 1, 1); });
+ assert_throws_js(TypeError, function() { CanvasRenderingContext2D.prototype.createImageData.call({}, 1, 1); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.type.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.type.html
new file mode 100644
index 0000000000..d595d33845
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.type.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.type</h1>
+<p class="desc">createImageData(sw, sh) returns an ImageData object containing a Uint8ClampedArray object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(sw, sh) returns an ImageData object containing a Uint8ClampedArray object");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.ImageData, undefined, "window.ImageData", "undefined");
+ _assertDifferent(window.Uint8ClampedArray, undefined, "window.Uint8ClampedArray", "undefined");
+ window.ImageData.prototype.thisImplementsImageData = true;
+ window.Uint8ClampedArray.prototype.thisImplementsUint8ClampedArray = true;
+ var imgdata = ctx.createImageData(1, 1);
+ _assert(imgdata.thisImplementsImageData, "imgdata.thisImplementsImageData");
+ _assert(imgdata.data.thisImplementsUint8ClampedArray, "imgdata.data.thisImplementsUint8ClampedArray");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.zero.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.zero.html
new file mode 100644
index 0000000000..cc949162d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.create2.zero.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.create2.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.create2.zero</h1>
+<p class="desc">createImageData(sw, sh) throws INDEX_SIZE_ERR if size is zero</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(sw, sh) throws INDEX_SIZE_ERR if size is zero");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(10, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0.99, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(10, 0.1); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.basic.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.basic.html
new file mode 100644
index 0000000000..5f8f199540
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.basic.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.basic</h1>
+<p class="desc">getImageData() exists and returns something</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() exists and returns something");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(ctx.getImageData(0, 0, 100, 50), null, "ctx.getImageData(0, 0, 100, 50)", "null");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.clamp.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.clamp.html
new file mode 100644
index 0000000000..0047e910b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.clamp.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.clamp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.clamp</h1>
+<p class="desc">getImageData() clamps colors to the range [0, 255]</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() clamps colors to the range [0, 255]");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgb(-100, -200, -300)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgb(256, 300, 400)';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata1.data[1], 0, "imgdata1.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata1.data[2], 0, "imgdata1.data[\""+(2)+"\"]", "0");
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ _assertSame(imgdata2.data[0], 255, "imgdata2.data[\""+(0)+"\"]", "255");
+ _assertSame(imgdata2.data[1], 255, "imgdata2.data[\""+(1)+"\"]", "255");
+ _assertSame(imgdata2.data[2], 255, "imgdata2.data[\""+(2)+"\"]", "255");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.double.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.double.html
new file mode 100644
index 0000000000..5cea5fcbc2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.double.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.double</h1>
+<p class="desc">createImageData(w, h) double is converted to long</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("createImageData(w, h) double is converted to long");
+_addTest(function(canvas, ctx) {
+
+ var imgdata1 = ctx.getImageData(0, 0, 10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, -10.01, -10.99);
+ _assertSame(imgdata1.width, 10, "imgdata1.width", "10");
+ _assertSame(imgdata1.height, 10, "imgdata1.height", "10");
+ _assertSame(imgdata2.width, 10, "imgdata2.width", "10");
+ _assertSame(imgdata2.height, 10, "imgdata2.height", "10");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.invalid.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.invalid.html
new file mode 100644
index 0000000000..689e72f69b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.invalid.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.invalid</h1>
+<p class="desc">Verify getImageData() behavior in invalid cases.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify getImageData() behavior in invalid cases.");
+_addTest(function(canvas, ctx) {
+
+ imageData = ctx.getImageData(0,0,2,2);
+ var testValues = [NaN, true, false, "\"garbage\"", "-1",
+ "0", "1", "2", Infinity, -Infinity,
+ -5, -0.5, 0, 0.5, 5,
+ 5.4, 255, 256, null, undefined];
+ var testResults = [0, 1, 0, 0, 0,
+ 0, 1, 2, 255, 0,
+ 0, 0, 0, 0, 5,
+ 5, 255, 255, 0, 0];
+ for (var i = 0; i < testValues.length; i++) {
+ imageData.data[0] = testValues[i];
+ _assert(imageData.data[0] == testResults[i], "imageData.data[\""+(0)+"\"] == testResults[\""+(i)+"\"]");
+ }
+ imageData.data['foo']='garbage';
+ _assert(imageData.data['foo'] == 'garbage', "imageData.data['foo'] == 'garbage'");
+ imageData.data[-1]='garbage';
+ _assert(imageData.data[-1] == undefined, "imageData.data[-1] == undefined");
+ imageData.data[17]='garbage';
+ _assert(imageData.data[17] == undefined, "imageData.data[\""+(17)+"\"] == undefined");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.large.crash.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.large.crash.html
new file mode 100644
index 0000000000..f41eec7b9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.large.crash.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.large.crash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.large.crash</h1>
+<p class="desc">Test that canvas crash when image data cannot be allocated.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test that canvas crash when image data cannot be allocated.");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 0xffffffff, 2147483647, 10); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.length.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.length.html
new file mode 100644
index 0000000000..41b9832025
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.length.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.length</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.length</h1>
+<p class="desc">getImageData() returns a correctly-sized Uint8ClampedArray</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns a correctly-sized Uint8ClampedArray");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonfinite.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonfinite.html
new file mode 100644
index 0000000000..a80e905cfd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonfinite.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.nonfinite</h1>
+<p class="desc">getImageData() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() throws TypeError if arguments are not finite");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(-Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(NaN, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, -Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, NaN, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, Infinity, Infinity); });
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(neginfobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(nanobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, neginfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, nanobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, neginfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, nanobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, neginfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, nanobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, posinfobj, posinfobj); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonpremul.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonpremul.html
new file mode 100644
index 0000000000..bc833c2198
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.nonpremul.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.nonpremul</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.nonpremul</h1>
+<p class="desc">getImageData() returns non-premultiplied colors</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns non-premultiplied colors");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(10, 10, 10, 10);
+ _assert(imgdata.data[0] > 200, "imgdata.data[\""+(0)+"\"] > 200");
+ _assert(imgdata.data[1] > 200, "imgdata.data[\""+(1)+"\"] > 200");
+ _assert(imgdata.data[2] > 200, "imgdata.data[\""+(2)+"\"] > 200");
+ _assert(imgdata.data[3] > 100, "imgdata.data[\""+(3)+"\"] > 100");
+ _assert(imgdata.data[3] < 200, "imgdata.data[\""+(3)+"\"] < 200");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.alpha.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.alpha.html
new file mode 100644
index 0000000000..e5b57f650e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.alpha.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.order.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.order.alpha</h1>
+<p class="desc">getImageData() returns A in the fourth component</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns A in the fourth component");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assert(imgdata.data[3] < 200, "imgdata.data[\""+(3)+"\"] < 200");
+ _assert(imgdata.data[3] > 100, "imgdata.data[\""+(3)+"\"] > 100");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.cols.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.cols.html
new file mode 100644
index 0000000000..66fd984ebd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.cols.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.order.cols</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.order.cols</h1>
+<p class="desc">getImageData() returns leftmost columns first</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns leftmost columns first");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 2, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[Math.round(imgdata.width/2*4)], 255, "imgdata.data[Math.round(imgdata.width/2*4)]", "255");
+ _assertSame(imgdata.data[Math.round((imgdata.height/2)*imgdata.width*4)], 0, "imgdata.data[Math.round((imgdata.height/2)*imgdata.width*4)]", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rgb.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rgb.html
new file mode 100644
index 0000000000..4d15ce7927
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rgb.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.order.rgb</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.order.rgb</h1>
+<p class="desc">getImageData() returns R then G then B</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns R then G then B");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#48c';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0x44, "imgdata.data[\""+(0)+"\"]", "0x44");
+ _assertSame(imgdata.data[1], 0x88, "imgdata.data[\""+(1)+"\"]", "0x88");
+ _assertSame(imgdata.data[2], 0xCC, "imgdata.data[\""+(2)+"\"]", "0xCC");
+ _assertSame(imgdata.data[3], 255, "imgdata.data[\""+(3)+"\"]", "255");
+ _assertSame(imgdata.data[4], 0x44, "imgdata.data[\""+(4)+"\"]", "0x44");
+ _assertSame(imgdata.data[5], 0x88, "imgdata.data[\""+(5)+"\"]", "0x88");
+ _assertSame(imgdata.data[6], 0xCC, "imgdata.data[\""+(6)+"\"]", "0xCC");
+ _assertSame(imgdata.data[7], 255, "imgdata.data[\""+(7)+"\"]", "255");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rows.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rows.html
new file mode 100644
index 0000000000..086ce322e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.order.rows.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.order.rows</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.order.rows</h1>
+<p class="desc">getImageData() returns topmost rows first</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns topmost rows first");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 2);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[Math.floor(imgdata.width/2*4)], 0, "imgdata.data[Math.floor(imgdata.width/2*4)]", "0");
+ _assertSame(imgdata.data[(imgdata.height/2)*imgdata.width*4], 255, "imgdata.data[(imgdata.height/2)*imgdata.width*4]", "255");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.range.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.range.html
new file mode 100644
index 0000000000..d921191772
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.range.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.range</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.range</h1>
+<p class="desc">getImageData() returns values in the range [0, 255]</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns values in the range [0, 255]");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ _assertSame(imgdata2.data[0], 255, "imgdata2.data[\""+(0)+"\"]", "255");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.rounding.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.rounding.html
new file mode 100644
index 0000000000..9255be087b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.rounding.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.rounding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.rounding</h1>
+<p class="desc">Test the handling of non-integer source coordinates in getImageData().</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test the handling of non-integer source coordinates in getImageData().");
+_addTest(function(canvas, ctx) {
+
+ function testDimensions(sx, sy, sw, sh, width, height)
+ {
+ imageData = ctx.getImageData(sx, sy, sw, sh);
+ _assert(imageData.width == width, "imageData.width == width");
+ _assert(imageData.height == height, "imageData.height == height");
+ }
+
+ testDimensions(0, 0, 20, 10, 20, 10);
+
+ testDimensions(.1, .2, 20, 10, 20, 10);
+ testDimensions(.9, .8, 20, 10, 20, 10);
+
+ testDimensions(0, 0, 20.9, 10.9, 20, 10);
+ testDimensions(0, 0, 20.1, 10.1, 20, 10);
+
+ testDimensions(-1, -1, 20, 10, 20, 10);
+
+ testDimensions(-1.1, 0, 20, 10, 20, 10);
+ testDimensions(-1.9, 0, 20, 10, 20, 10);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.negative.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.negative.html
new file mode 100644
index 0000000000..0b84b1caca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.negative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.source.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.source.negative</h1>
+<p class="desc">getImageData() works with negative width and height, and returns top-to-bottom left-to-right</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() works with negative width and height, and returns top-to-bottom left-to-right");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+
+ var imgdata1 = ctx.getImageData(85, 25, -10, -10);
+ _assertSame(imgdata1.data[0], 255, "imgdata1.data[\""+(0)+"\"]", "255");
+ _assertSame(imgdata1.data[1], 255, "imgdata1.data[\""+(1)+"\"]", "255");
+ _assertSame(imgdata1.data[2], 255, "imgdata1.data[\""+(2)+"\"]", "255");
+ _assertSame(imgdata1.data[3], 255, "imgdata1.data[\""+(3)+"\"]", "255");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+0], 0, "imgdata1.data[imgdata1.data.length-4+0]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+1], 0, "imgdata1.data[imgdata1.data.length-4+1]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+2], 0, "imgdata1.data[imgdata1.data.length-4+2]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+3], 255, "imgdata1.data[imgdata1.data.length-4+3]", "255");
+
+ var imgdata2 = ctx.getImageData(0, 0, -1, -1);
+ _assertSame(imgdata2.data[0], 0, "imgdata2.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata2.data[1], 0, "imgdata2.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata2.data[2], 0, "imgdata2.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata2.data[3], 0, "imgdata2.data[\""+(3)+"\"]", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.outside.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.outside.html
new file mode 100644
index 0000000000..fca97f2e6f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.outside.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.source.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.source.outside</h1>
+<p class="desc">getImageData() returns transparent black outside the canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns transparent black outside the canvas");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#08f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var imgdata1 = ctx.getImageData(-10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata1.data[1], 0, "imgdata1.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata1.data[2], 0, "imgdata1.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata1.data[3], 0, "imgdata1.data[\""+(3)+"\"]", "0");
+
+ var imgdata2 = ctx.getImageData(10, -5, 1, 1);
+ _assertSame(imgdata2.data[0], 0, "imgdata2.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata2.data[1], 0, "imgdata2.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata2.data[2], 0, "imgdata2.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata2.data[3], 0, "imgdata2.data[\""+(3)+"\"]", "0");
+
+ var imgdata3 = ctx.getImageData(200, 5, 1, 1);
+ _assertSame(imgdata3.data[0], 0, "imgdata3.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata3.data[1], 0, "imgdata3.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata3.data[2], 0, "imgdata3.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata3.data[3], 0, "imgdata3.data[\""+(3)+"\"]", "0");
+
+ var imgdata4 = ctx.getImageData(10, 60, 1, 1);
+ _assertSame(imgdata4.data[0], 0, "imgdata4.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata4.data[1], 0, "imgdata4.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata4.data[2], 0, "imgdata4.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata4.data[3], 0, "imgdata4.data[\""+(3)+"\"]", "0");
+
+ var imgdata5 = ctx.getImageData(100, 10, 1, 1);
+ _assertSame(imgdata5.data[0], 0, "imgdata5.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata5.data[1], 0, "imgdata5.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata5.data[2], 0, "imgdata5.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata5.data[3], 0, "imgdata5.data[\""+(3)+"\"]", "0");
+
+ var imgdata6 = ctx.getImageData(0, 10, 1, 1);
+ _assertSame(imgdata6.data[0], 0, "imgdata6.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata6.data[1], 136, "imgdata6.data[\""+(1)+"\"]", "136");
+ _assertSame(imgdata6.data[2], 255, "imgdata6.data[\""+(2)+"\"]", "255");
+ _assertSame(imgdata6.data[3], 255, "imgdata6.data[\""+(3)+"\"]", "255");
+
+ var imgdata7 = ctx.getImageData(-10, 10, 20, 20);
+ _assertSame(imgdata7.data[ 0*4+0], 0, "imgdata7.data[ 0*4+0]", "0");
+ _assertSame(imgdata7.data[ 0*4+1], 0, "imgdata7.data[ 0*4+1]", "0");
+ _assertSame(imgdata7.data[ 0*4+2], 0, "imgdata7.data[ 0*4+2]", "0");
+ _assertSame(imgdata7.data[ 0*4+3], 0, "imgdata7.data[ 0*4+3]", "0");
+ _assertSame(imgdata7.data[ 9*4+0], 0, "imgdata7.data[ 9*4+0]", "0");
+ _assertSame(imgdata7.data[ 9*4+1], 0, "imgdata7.data[ 9*4+1]", "0");
+ _assertSame(imgdata7.data[ 9*4+2], 0, "imgdata7.data[ 9*4+2]", "0");
+ _assertSame(imgdata7.data[ 9*4+3], 0, "imgdata7.data[ 9*4+3]", "0");
+ _assertSame(imgdata7.data[10*4+0], 0, "imgdata7.data[10*4+0]", "0");
+ _assertSame(imgdata7.data[10*4+1], 136, "imgdata7.data[10*4+1]", "136");
+ _assertSame(imgdata7.data[10*4+2], 255, "imgdata7.data[10*4+2]", "255");
+ _assertSame(imgdata7.data[10*4+3], 255, "imgdata7.data[10*4+3]", "255");
+ _assertSame(imgdata7.data[19*4+0], 0, "imgdata7.data[19*4+0]", "0");
+ _assertSame(imgdata7.data[19*4+1], 136, "imgdata7.data[19*4+1]", "136");
+ _assertSame(imgdata7.data[19*4+2], 255, "imgdata7.data[19*4+2]", "255");
+ _assertSame(imgdata7.data[19*4+3], 255, "imgdata7.data[19*4+3]", "255");
+ _assertSame(imgdata7.data[20*4+0], 0, "imgdata7.data[20*4+0]", "0");
+ _assertSame(imgdata7.data[20*4+1], 0, "imgdata7.data[20*4+1]", "0");
+ _assertSame(imgdata7.data[20*4+2], 0, "imgdata7.data[20*4+2]", "0");
+ _assertSame(imgdata7.data[20*4+3], 0, "imgdata7.data[20*4+3]", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.size.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.size.html
new file mode 100644
index 0000000000..800edb8baf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.source.size.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.source.size</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.source.size</h1>
+<p class="desc">getImageData() returns bigger ImageData for bigger source rectangle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns bigger ImageData for bigger source rectangle");
+_addTest(function(canvas, ctx) {
+
+ var imgdata1 = ctx.getImageData(0, 0, 10, 10);
+ var imgdata2 = ctx.getImageData(0, 0, 20, 20);
+ _assert(imgdata2.width > imgdata1.width, "imgdata2.width > imgdata1.width");
+ _assert(imgdata2.height > imgdata1.height, "imgdata2.height > imgdata1.height");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.type.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.type.html
new file mode 100644
index 0000000000..748d46b166
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.type.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.type</h1>
+<p class="desc">getImageData() returns an ImageData object containing a Uint8ClampedArray object</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() returns an ImageData object containing a Uint8ClampedArray object");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.ImageData, undefined, "window.ImageData", "undefined");
+ _assertDifferent(window.Uint8ClampedArray, undefined, "window.Uint8ClampedArray", "undefined");
+ window.ImageData.prototype.thisImplementsImageData = true;
+ window.Uint8ClampedArray.prototype.thisImplementsUint8ClampedArray = true;
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ _assert(imgdata.thisImplementsImageData, "imgdata.thisImplementsImageData");
+ _assert(imgdata.data.thisImplementsUint8ClampedArray, "imgdata.data.thisImplementsUint8ClampedArray");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.unaffected.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.unaffected.html
new file mode 100644
index 0000000000..f2f4a2e4fa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.unaffected.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.unaffected</h1>
+<p class="desc">getImageData() is not affected by context state</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() is not affected by context state");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50)
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.rect(0, 0, 5, 5);
+ ctx.clip();
+ var imgdata = ctx.getImageData(0, 0, 50, 50);
+ ctx.restore();
+ ctx.putImageData(imgdata, 50, 0);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.zero.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.zero.html
new file mode 100644
index 0000000000..438bc2fb1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.get.zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.get.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.get.zero</h1>
+<p class="desc">getImageData() throws INDEX_SIZE_ERR if size is zero</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getImageData() throws INDEX_SIZE_ERR if size is zero");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0.1, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, 0.99); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, -0.1, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, -0.99); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.clamp.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.clamp.html
new file mode 100644
index 0000000000..df8ef1ba20
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.clamp.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.clamp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.clamp</h1>
+<p class="desc">ImageData.data clamps numbers to [0, 255]</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData.data clamps numbers to [0, 255]");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 300;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -100;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 200+Math.pow(2, 32);
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -200-Math.pow(2, 32);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Math.pow(10, 39);
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Math.pow(10, 39);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Infinity;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Infinity;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.bounds.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.bounds.html
new file mode 100644
index 0000000000..01affeb947
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.bounds.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.ctor.array.bounds</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.ctor.array.bounds</h1>
+<p class="desc">ImageData has a usable constructor</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData has a usable constructor");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.ImageData, undefined, "window.ImageData", "undefined");
+
+ assert_throws_dom("INVALID_STATE_ERR", function() { new ImageData(new Uint8ClampedArray(0), 1); });
+ assert_throws_dom("INVALID_STATE_ERR", function() { new ImageData(new Uint8ClampedArray(3), 1); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(new Uint8ClampedArray(4), 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(new Uint8ClampedArray(4), 1, 2); });
+ assert_throws_js(TypeError, function() { new ImageData(new Uint8Array(8), 1, 2); });
+ assert_throws_js(TypeError, function() { new ImageData(new Int8Array(8), 1, 2); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.html
new file mode 100644
index 0000000000..0cdc025764
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.array.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.ctor.array</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.ctor.array</h1>
+<p class="desc">ImageData has a usable constructor</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData has a usable constructor");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.ImageData, undefined, "window.ImageData", "undefined");
+
+ var array = new Uint8ClampedArray(8);
+ var imgdata = new window.ImageData(array, 1, 2);
+ _assertSame(imgdata.width, 1, "imgdata.width", "1");
+ _assertSame(imgdata.height, 2, "imgdata.height", "2");
+ _assertSame(imgdata.data, array, "imgdata.data", "array");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html
new file mode 100644
index 0000000000..f7811e7441
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.ctor.basics</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.ctor.basics</h1>
+<p class="desc">Testing different type of ImageData constructor</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing different type of ImageData constructor");
+_addTest(function(canvas, ctx) {
+
+ function setRGBA(imageData, i, rgba)
+ {
+ var s = i * 4;
+ imageData[s] = rgba[0];
+ imageData[s + 1] = rgba[1];
+ imageData[s + 2] = rgba[2];
+ imageData[s + 3] = rgba[3];
+ }
+
+ function getRGBA(imageData, i)
+ {
+ var result = [];
+ var s = i * 4;
+ for (var j = 0; j < 4; j++) {
+ result[j] = imageData[s + j];
+ }
+ return result;
+ }
+
+ function assertArrayEquals(actual, expected)
+ {
+ _assertSame(typeof actual, "object", "typeof actual", "\"object\"");
+ _assertDifferent(actual, null, "actual", "null");
+ _assertSame("length" in actual, true, "\"length\" in actual", "true");
+ _assertSame(actual.length, expected.length, "actual.length", "expected.length");
+ for (var i = 0; i < actual.length; i++) {
+ _assertSame(actual.hasOwnProperty(i), expected.hasOwnProperty(i), "actual.hasOwnProperty(i)", "expected.hasOwnProperty(i)");
+ _assertSame(actual[i], expected[i], "actual[\""+(i)+"\"]", "expected[\""+(i)+"\"]");
+ }
+ }
+
+ _assertDifferent(ImageData, undefined, "ImageData", "undefined");
+ imageData = new ImageData(100, 50);
+
+ _assertDifferent(imageData, null, "imageData", "null");
+ _assertDifferent(imageData.data, null, "imageData.data", "null");
+ _assertSame(imageData.width, 100, "imageData.width", "100");
+ _assertSame(imageData.height, 50, "imageData.height", "50");
+ assertArrayEquals(getRGBA(imageData.data, 4), [0, 0, 0, 0]);
+
+ var testColor = [0, 255, 255, 128];
+ setRGBA(imageData.data, 4, testColor);
+ assertArrayEquals(getRGBA(imageData.data, 4), testColor);
+
+ assert_throws_js(TypeError, function() { ImageData(1, 1); });
+ assert_throws_js(TypeError, function() { new ImageData(10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(0, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(10, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData('width', 'height'); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(1 << 31, 1 << 31); });
+ assert_throws_js(TypeError, function() { new ImageData(new Uint8ClampedArray(0)); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(new Uint8Array(100), 25); });
+ assert_throws_dom("INVALID_STATE_ERR", function() { new ImageData(new Uint8ClampedArray(27), 2); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(new Uint8ClampedArray(28), 7, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(new Uint8ClampedArray(104), 14); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(new Uint8ClampedArray([12, 34, 168, 65328]), 1, 151); });
+ assert_throws_js(TypeError, function() { new ImageData(self, 4, 4); });
+ assert_throws_js(TypeError, function() { new ImageData(null, 4, 4); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(imageData.data, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(imageData.data, 13); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(imageData.data, 1 << 31); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(imageData.data, 'biggish'); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { new ImageData(imageData.data, 1 << 24, 1 << 31); });
+ _assertSame(new ImageData(new Uint8ClampedArray(28), 7).height, 1, "new ImageData(new Uint8ClampedArray(28), 7).height", "1");
+
+ imageDataFromData = new ImageData(imageData.data, 100);
+ _assertSame(imageDataFromData.width, 100, "imageDataFromData.width", "100");
+ _assertSame(imageDataFromData.height, 50, "imageDataFromData.height", "50");
+ _assertSame(imageDataFromData.data, imageData.data, "imageDataFromData.data", "imageData.data");
+ assertArrayEquals(getRGBA(imageDataFromData.data, 10), getRGBA(imageData.data, 10));
+ setRGBA(imageData.data, 10, testColor);
+ assertArrayEquals(getRGBA(imageDataFromData.data, 10), getRGBA(imageData.data, 10));
+
+ var data = new Uint8ClampedArray(400);
+ data[22] = 129;
+ imageDataFromData = new ImageData(data, 20, 5);
+ _assertSame(imageDataFromData.width, 20, "imageDataFromData.width", "20");
+ _assertSame(imageDataFromData.height, 5, "imageDataFromData.height", "5");
+ _assertSame(imageDataFromData.data, data, "imageDataFromData.data", "data");
+ assertArrayEquals(getRGBA(imageDataFromData.data, 2), getRGBA(data, 2));
+ setRGBA(imageDataFromData.data, 2, testColor);
+ assertArrayEquals(getRGBA(imageDataFromData.data, 2), getRGBA(data, 2));
+
+ if (window.SharedArrayBuffer) {
+ assert_throws_js(TypeError, function() { new ImageData(new Uint16Array(new SharedArrayBuffer(32)), 4, 2); });
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.bounds.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.bounds.html
new file mode 100644
index 0000000000..5ea899ac86
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.bounds.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.ctor.size.bounds</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.ctor.size.bounds</h1>
+<p class="desc">ImageData has a usable constructor</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData has a usable constructor");
+_addTest(function(canvas, ctx) {
+
+_assertDifferent(window.ImageData, undefined, "window.ImageData", "undefined");
+assert_throws_dom("INDEX_SIZE_ERR", function() { new window.ImageData(0, 0); });
+assert_throws_dom("INDEX_SIZE_ERR", function() { new window.ImageData(0, 1); });
+assert_throws_dom("INDEX_SIZE_ERR", function() { new window.ImageData(1, 0); });
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.html
new file mode 100644
index 0000000000..a11d8e2340
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.size.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.ctor.size</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.ctor.size</h1>
+<p class="desc">ImageData has a usable constructor</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData has a usable constructor");
+_addTest(function(canvas, ctx) {
+
+ _assertDifferent(window.ImageData, undefined, "window.ImageData", "undefined");
+
+ var imgdata = new window.ImageData(2, 3);
+ _assertSame(imgdata.width, 2, "imgdata.width", "2");
+ _assertSame(imgdata.height, 3, "imgdata.height", "3");
+ _assertSame(imgdata.data.length, 2 * 3 * 4, "imgdata.data.length", "2 * 3 * 4");
+ for (var i = 0; i < imgdata.data.length; ++i) {
+ _assertSame(imgdata.data[i], 0, "imgdata.data[\""+(i)+"\"]", "0");
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.nan.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.nan.html
new file mode 100644
index 0000000000..974b5898a3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.nan.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.nan</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.nan</h1>
+<p class="desc">ImageData.data converts NaN to 0</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData.data converts NaN to 0");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = NaN;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "cheese";
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.properties.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.properties.html
new file mode 100644
index 0000000000..7eefdb2f55
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.properties.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.properties</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.properties</h1>
+<p class="desc">ImageData objects have the right properties</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData objects have the right properties");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(typeof(imgdata.width), 'number', "typeof(imgdata.width)", "'number'");
+ _assertSame(typeof(imgdata.height), 'number', "typeof(imgdata.height)", "'number'");
+ _assertSame(typeof(imgdata.data), 'object', "typeof(imgdata.data)", "'object'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.readonly.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.readonly.html
new file mode 100644
index 0000000000..f002466bab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.readonly.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.readonly</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.readonly</h1>
+<p class="desc">ImageData objects properties are read-only</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData objects properties are read-only");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ var w = imgdata.width;
+ var h = imgdata.height;
+ var d = imgdata.data;
+ imgdata.width = 123;
+ imgdata.height = 123;
+ imgdata.data = [100,100,100,100];
+ _assertSame(imgdata.width, w, "imgdata.width", "w");
+ _assertSame(imgdata.height, h, "imgdata.height", "h");
+ _assertSame(imgdata.data, d, "imgdata.data", "d");
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[1], 0, "imgdata.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata.data[2], 0, "imgdata.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata.data[3], 0, "imgdata.data[\""+(3)+"\"]", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.round.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.round.html
new file mode 100644
index 0000000000..0f7da4fff5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.round.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.round</h1>
+<p class="desc">ImageData.data rounds numbers with round-to-zero</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData.data rounds numbers with round-to-zero");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 0.499;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 0.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 0.501;
+ _assertSame(imgdata.data[0], 1, "imgdata.data[\""+(0)+"\"]", "1");
+ imgdata.data[0] = 1.499;
+ _assertSame(imgdata.data[0], 1, "imgdata.data[\""+(0)+"\"]", "1");
+ imgdata.data[0] = 1.5;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 1.501;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 2.5;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 3.5;
+ _assertSame(imgdata.data[0], 4, "imgdata.data[\""+(0)+"\"]", "4");
+ imgdata.data[0] = 252.5;
+ _assertSame(imgdata.data[0], 252, "imgdata.data[\""+(0)+"\"]", "252");
+ imgdata.data[0] = 253.5;
+ _assertSame(imgdata.data[0], 254, "imgdata.data[\""+(0)+"\"]", "254");
+ imgdata.data[0] = 254.5;
+ _assertSame(imgdata.data[0], 254, "imgdata.data[\""+(0)+"\"]", "254");
+ imgdata.data[0] = 256.5;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = -0.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = -1.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.set.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.set.html
new file mode 100644
index 0000000000..3d03bd6769
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.set.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.set</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.set</h1>
+<p class="desc">ImageData.data can be modified</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData.data can be modified");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ _assertSame(imgdata.data[0], 100, "imgdata.data[\""+(0)+"\"]", "100");
+ imgdata.data[0] = 200;
+ _assertSame(imgdata.data[0], 200, "imgdata.data[\""+(0)+"\"]", "200");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.string.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.string.html
new file mode 100644
index 0000000000..eda6f2838d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.string.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.string</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.string</h1>
+<p class="desc">ImageData.data converts strings to numbers with ToNumber</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData.data converts strings to numbers with ToNumber");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "110";
+ _assertSame(imgdata.data[0], 110, "imgdata.data[\""+(0)+"\"]", "110");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "0x78";
+ _assertSame(imgdata.data[0], 120, "imgdata.data[\""+(0)+"\"]", "120");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = " +130e0 ";
+ _assertSame(imgdata.data[0], 130, "imgdata.data[\""+(0)+"\"]", "130");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.undefined.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.undefined.html
new file mode 100644
index 0000000000..96867bec4a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.object.undefined.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.object.undefined</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.object.undefined</h1>
+<p class="desc">ImageData.data converts undefined to 0</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("ImageData.data converts undefined to 0");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = undefined;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.html
new file mode 100644
index 0000000000..6ee38802f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.alpha</h1>
+<p class="desc">putImageData() puts non-solid image data correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.imageData.put.alpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() puts non-solid image data correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.25)';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,64, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.png b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.png
new file mode 100644
index 0000000000..5428c65524
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.basic.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.basic.html
new file mode 100644
index 0000000000..17f7d3e8e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.basic.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.basic</h1>
+<p class="desc">putImageData() puts image data from getImageData() onto the canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() puts image data from getImageData() onto the canvas");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.clip.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.clip.html
new file mode 100644
index 0000000000..948f617095
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.clip.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.clip</h1>
+<p class="desc">putImageData() is not affected by clipping regions</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() is not affected by clipping regions");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.created.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.created.html
new file mode 100644
index 0000000000..4db45873a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.created.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.created</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.created</h1>
+<p class="desc">putImageData() puts image data from createImageData() onto the canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() puts image data from createImageData() onto the canvas");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.createImageData(100, 50);
+ for (var i = 0; i < imgdata.data.length; i += 4) {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ imgdata.data[i+2] = 0;
+ imgdata.data[i+3] = 255;
+ }
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.cross.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.cross.html
new file mode 100644
index 0000000000..10226834a3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.cross.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.cross</h1>
+<p class="desc">putImageData() accepts image data got from a different canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() accepts image data got from a different canvas");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50)
+ var imgdata = ctx2.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.negative.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.negative.html
new file mode 100644
index 0000000000..efaa6f2550
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.negative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.dirty.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.dirty.negative</h1>
+<p class="desc">putImageData() handles negative-sized dirty rectangles correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() handles negative-sized dirty rectangles correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 20, 20, -20, -20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.outside.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.outside.html
new file mode 100644
index 0000000000..86a77369f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.outside.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.dirty.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.dirty.outside</h1>
+<p class="desc">putImageData() handles dirty rectangles outside the canvas correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() handles dirty rectangles outside the canvas correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+
+ ctx.putImageData(imgdata, 100, 20, 20, 20, -20, -20);
+ ctx.putImageData(imgdata, 200, 200, 0, 0, 100, 50);
+ ctx.putImageData(imgdata, 40, 20, -30, -20, 30, 20);
+ ctx.putImageData(imgdata, -30, 20, 0, 0, 30, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,45, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect1.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect1.html
new file mode 100644
index 0000000000..786db5ac4f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.dirty.rect1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.dirty.rect1</h1>
+<p class="desc">putImageData() only modifies areas inside the dirty rectangle, using width and height</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using width and height");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 0, 0, 20, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect2.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect2.html
new file mode 100644
index 0000000000..f5f8a3a0d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.dirty.rect2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.dirty.rect2</h1>
+<p class="desc">putImageData() only modifies areas inside the dirty rectangle, using x and y</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using x and y");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(60, 30, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, -20, -10, 60, 30, 20, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.zero.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.zero.html
new file mode 100644
index 0000000000..d3799104dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.dirty.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.dirty.zero</h1>
+<p class="desc">putImageData() with zero-sized dirty rectangle puts nothing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() with zero-sized dirty rectangle puts nothing");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0, 0, 0, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.modified.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.modified.html
new file mode 100644
index 0000000000..70b70a866f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.modified.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.modified</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.modified</h1>
+<p class="desc">putImageData() puts modified image data correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() puts modified image data correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(45, 20, 10, 10)
+ var imgdata = ctx.getImageData(45, 20, 10, 10);
+ for (var i = 0, len = imgdata.width*imgdata.height*4; i < len; i += 4)
+ {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ }
+ ctx.putImageData(imgdata, 45, 20);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.nonfinite.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.nonfinite.html
new file mode 100644
index 0000000000..ab03f9f20f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.nonfinite.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.nonfinite</h1>
+<p class="desc">putImageData() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() throws TypeError if arguments are not finite");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, -Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, NaN, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, -Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, NaN, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, Infinity); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.null.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.null.html
new file mode 100644
index 0000000000..4ed429fe7c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.null.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.null</h1>
+<p class="desc">putImageData() with null imagedata throws TypeError</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() with null imagedata throws TypeError");
+_addTest(function(canvas, ctx) {
+
+ assert_throws_js(TypeError, function() { ctx.putImageData(null, 0, 0); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.path.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.path.html
new file mode 100644
index 0000000000..e32d7cc257
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.path.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.path</h1>
+<p class="desc">putImageData() does not affect the current path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() does not affect the current path");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.rect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.putImageData(imgdata, 0, 0);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unaffected.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unaffected.html
new file mode 100644
index 0000000000..bad06b745f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unaffected.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.unaffected</h1>
+<p class="desc">putImageData() is not affected by context state</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() is not affected by context state");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.translate(100, 50);
+ ctx.scale(0.1, 0.1);
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unchanged.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unchanged.html
new file mode 100644
index 0000000000..026e4347ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.unchanged.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.unchanged</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.unchanged</h1>
+<p class="desc">putImageData(getImageData(...), ...) has no effect</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData(getImageData(...), ...) has no effect");
+_addTest(function(canvas, ctx) {
+
+ var i = 0;
+ for (var y = 0; y < 16; ++y) {
+ for (var x = 0; x < 16; ++x, ++i) {
+ ctx.fillStyle = 'rgba(' + i + ',' + (Math.floor(i*1.5) % 256) + ',' + (Math.floor(i*23.3) % 256) + ',' + (i/256) + ')';
+ ctx.fillRect(x, y, 1, 1);
+ }
+ }
+ var imgdata1 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ var olddata = [];
+ for (var i = 0; i < imgdata1.data.length; ++i)
+ olddata[i] = imgdata1.data[i];
+
+ ctx.putImageData(imgdata1, 0.1, 0.2);
+
+ var imgdata2 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ for (var i = 0; i < imgdata2.data.length; ++i) {
+ _assertSame(olddata[i], imgdata2.data[i], "olddata[\""+(i)+"\"]", "imgdata2.data[\""+(i)+"\"]");
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.wrongtype.html b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.wrongtype.html
new file mode 100644
index 0000000000..9e43b1fbe9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/pixel-manipulation/2d.imageData.put.wrongtype.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.imageData.put.wrongtype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.imageData.put.wrongtype</h1>
+<p class="desc">putImageData() does not accept non-ImageData objects</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("putImageData() does not accept non-ImageData objects");
+_addTest(function(canvas, ctx) {
+
+ var imgdata = { width: 1, height: 1, data: [255, 0, 0, 255] };
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.putImageData("cheese", 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(42, 0, 0); });
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.basic.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.basic.html
new file mode 100644
index 0000000000..0753ec6da8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.basic.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.basic</h1>
+<p class="desc">reset clears to transparent black</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("reset clears to transparent black");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ ctx.reset();
+ _assertPixel(canvas, 0,0, 0,0,0,0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ _assertPixel(canvas, 25,50, 0,0,0,0);
+ _assertPixel(canvas, 100,50, 0,0,0,0);
+ _assertPixel(canvas, 0,50, 0,0,0,0);
+ _assertPixel(canvas, 100,0, 0,0,0,0);
+ t.done();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow-expected.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow-expected.html
new file mode 100644
index 0000000000..182f7e40cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.drop_shadow</title>
+<h1>2d.reset.render.drop_shadow</h1>
+<p class="desc">check that drop shadows are correctly rendered after reset</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(100, 100, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow.html
new file mode 100644
index 0000000000..264355c8b3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.drop_shadow.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.drop_shadow-expected.html">
+<title>Canvas test: 2d.reset.render.drop_shadow</title>
+<h1>2d.reset.render.drop_shadow</h1>
+<p class="desc">check that drop shadows are correctly rendered after reset</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = "red";
+ ctx.shadowBlur = 10;
+
+ ctx.reset();
+
+ ctx.fillRect(100, 100, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation-expected.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation-expected.html
new file mode 100644
index 0000000000..1f9d247634
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.global_composite_operation</title>
+<h1>2d.reset.render.global_composite_operation</h1>
+<p class="desc">check that canvas correctly renders rectangles with the default global composite operation after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(10, 10, 100, 100);
+ ctx.fillRect(50, 50, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation.html
new file mode 100644
index 0000000000..fc851b84b8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.global_composite_operation.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.global_composite_operation-expected.html">
+<title>Canvas test: 2d.reset.render.global_composite_operation</title>
+<h1>2d.reset.render.global_composite_operation</h1>
+<p class="desc">check that canvas correctly renders rectangles with the default global composite operation after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = "xor";
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 100, 100);
+ ctx.fillRect(50, 50, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line-expected.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line-expected.html
new file mode 100644
index 0000000000..dcd648d8c3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.line</title>
+<h1>2d.reset.render.line</h1>
+<p class="desc">check that lines are correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.moveTo(100, 100);
+ ctx.lineTo(100, 300);
+ ctx.lineTo(300, 300);
+ ctx.lineTo(300, 100);
+ ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line.html
new file mode 100644
index 0000000000..0dda7c7a7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.line.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.line-expected.html">
+<title>Canvas test: 2d.reset.render.line</title>
+<h1>2d.reset.render.line</h1>
+<p class="desc">check that lines are correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 10;
+ ctx.lineCap = "round";
+ ctx.lineJoin = "bevel";
+ ctx.lineDashOffset = 10;
+ ctx.setLineDash([20]);
+
+ ctx.reset();
+
+ ctx.beginPath();
+ ctx.moveTo(100, 100);
+ ctx.lineTo(100, 300);
+ ctx.lineTo(300, 300);
+ ctx.lineTo(300, 100);
+ ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc-expected.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc-expected.html
new file mode 100644
index 0000000000..c359e6b42e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.misc</title>
+<h1>2d.reset.render.misc</h1>
+<p class="desc">check that canvas correctly renders rectangles after reset (states not covered by other tests)</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(0, 0, 100, 100);
+ ctx.strokeRect(150, 150, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc.html
new file mode 100644
index 0000000000..61d2dbe2f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.misc.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.misc-expected.html">
+<title>Canvas test: 2d.reset.render.misc</title>
+<h1>2d.reset.render.misc</h1>
+<p class="desc">check that canvas correctly renders rectangles after reset (states not covered by other tests)</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = "red";
+ ctx.strokeStyle = "red";
+ ctx.globalAlpha = 0.5;
+ ctx.filter = "blur(2px)";
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 100, 100);
+ ctx.strokeRect(150, 150, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit-expected.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit-expected.html
new file mode 100644
index 0000000000..c91f485c53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit-expected.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.miter_limit</title>
+<h1>2d.reset.render.miter_limit</h1>
+<p class="desc">check that the lines are correctly rendered with the default miter limit after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 100);
+ for (let i = 0; i < 24; i++) {
+ const dy = i % 2 === 0 ? 25 : -25;
+ ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+ }
+ ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit.html
new file mode 100644
index 0000000000..f6eaed00f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.miter_limit.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.miter_limit-expected.html">
+<title>Canvas test: 2d.reset.render.miter_limit</title>
+<h1>2d.reset.render.miter_limit</h1>
+<p class="desc">check that the lines are correctly rendered with the default miter limit after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.miterLimit = 6;
+
+ ctx.reset();
+
+ ctx.lineWidth = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 100);
+ for (let i = 0; i < 24; i++) {
+ const dy = i % 2 === 0 ? 25 : -25;
+ ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+ }
+ ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text-expected.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text-expected.html
new file mode 100644
index 0000000000..7221483b02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.text</title>
+<h1>2d.reset.render.text</h1>
+<p class="desc">check that text is correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillText("Lorem ipsum dolor sit amet, consectetur adipiscing elit", 0, 10);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text.html
new file mode 100644
index 0000000000..3d76ddf292
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.render.text.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.text-expected.html">
+<title>Canvas test: 2d.reset.render.text</title>
+<h1>2d.reset.render.text</h1>
+<p class="desc">check that text is correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "24px serif";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "hanging";
+ ctx.direction = "rtl";
+ ctx.letterSpacing = "10px";
+ ctx.fontKerning = "none";
+ ctx.fontStretch = "semi-condensed";
+ ctx.fontVariantCaps = "tilting-caps";
+ ctx.textRendering = "optimizeLegibility";
+ ctx.wordSpacing = "20px";
+
+ ctx.reset();
+
+ ctx.fillText("Lorem ipsum dolor sit amet, consectetur adipiscing elit", 0, 10);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip-expected.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip-expected.html
new file mode 100644
index 0000000000..974b37fab4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.clip</title>
+<h1>2d.reset.state.clip</h1>
+<p class="desc">check that the clip is reset</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(0, 0, 200, 200);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip.html
new file mode 100644
index 0000000000..ab3fa1081c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.clip.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.state.clip-expected.html">
+<title>Canvas test: 2d.reset.state.clip</title>
+<h1>2d.reset.state.clip</h1>
+<p class="desc">check that the clip is reset</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.clip();
+
+ ctx.fillRect(0, 0, 200, 200);
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 200, 200);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.direction.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.direction.html
new file mode 100644
index 0000000000..8b27e3cfe4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.direction.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.direction</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.direction</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.direction;
+
+ ctx.direction = 'rtl';
+ _assert(ctx.direction == 'rtl', "ctx.direction == 'rtl'");
+
+ ctx.reset();
+ _assert(ctx.direction == default_value, "ctx.direction == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.fill_style.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.fill_style.html
new file mode 100644
index 0000000000..1239e2ec7f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.fill_style.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.fill_style</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.fill_style</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.fillStyle;
+
+ ctx.fillStyle = '#ffffff';
+ _assert(ctx.fillStyle == '#ffffff', "ctx.fillStyle == '#ffffff'");
+
+ ctx.reset();
+ _assert(ctx.fillStyle == default_value, "ctx.fillStyle == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.filter.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.filter.html
new file mode 100644
index 0000000000..80ccd22bb2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.filter.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.filter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.filter</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.filter;
+
+ ctx.filter = 'blur(10px)';
+ _assert(ctx.filter == 'blur(10px)', "ctx.filter == 'blur(10px)'");
+
+ ctx.reset();
+ _assert(ctx.filter == default_value, "ctx.filter == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font.html
new file mode 100644
index 0000000000..c93d038a62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.font</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.font</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.font;
+
+ ctx.font = '16px sans-serif';
+ _assert(ctx.font == '16px sans-serif', "ctx.font == '16px sans-serif'");
+
+ ctx.reset();
+ _assert(ctx.font == default_value, "ctx.font == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_kerning.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_kerning.html
new file mode 100644
index 0000000000..d3ad9d386c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_kerning.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.font_kerning</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.font_kerning</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.fontKerning;
+
+ ctx.fontKerning = 'normal';
+ _assert(ctx.fontKerning == 'normal', "ctx.fontKerning == 'normal'");
+
+ ctx.reset();
+ _assert(ctx.fontKerning == default_value, "ctx.fontKerning == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_stretch.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_stretch.html
new file mode 100644
index 0000000000..0a91c640b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_stretch.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.font_stretch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.font_stretch</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.fontStretch;
+
+ ctx.fontStretch = 'ultra-condensed';
+ _assert(ctx.fontStretch == 'ultra-condensed', "ctx.fontStretch == 'ultra-condensed'");
+
+ ctx.reset();
+ _assert(ctx.fontStretch == default_value, "ctx.fontStretch == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_variant_caps.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_variant_caps.html
new file mode 100644
index 0000000000..262c4fc4d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.font_variant_caps.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.font_variant_caps</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.font_variant_caps</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.fontVariantCaps;
+
+ ctx.fontVariantCaps = 'unicase';
+ _assert(ctx.fontVariantCaps == 'unicase', "ctx.fontVariantCaps == 'unicase'");
+
+ ctx.reset();
+ _assert(ctx.fontVariantCaps == default_value, "ctx.fontVariantCaps == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_alpha.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_alpha.html
new file mode 100644
index 0000000000..18b825e60f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_alpha.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.global_alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.global_alpha</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.globalAlpha;
+
+ ctx.globalAlpha = 0.5;
+ _assert(ctx.globalAlpha == 0.5, "ctx.globalAlpha == 0.5");
+
+ ctx.reset();
+ _assert(ctx.globalAlpha == default_value, "ctx.globalAlpha == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_composite_operation.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_composite_operation.html
new file mode 100644
index 0000000000..aee6841d95
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.global_composite_operation.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.global_composite_operation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.global_composite_operation</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.globalCompositeOperation;
+
+ ctx.globalCompositeOperation = 'destination-over';
+ _assert(ctx.globalCompositeOperation == 'destination-over', "ctx.globalCompositeOperation == 'destination-over'");
+
+ ctx.reset();
+ _assert(ctx.globalCompositeOperation == default_value, "ctx.globalCompositeOperation == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_enabled.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_enabled.html
new file mode 100644
index 0000000000..e865967a38
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_enabled.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.image_smoothing_enabled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.image_smoothing_enabled</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.imageSmoothingEnabled;
+
+ ctx.imageSmoothingEnabled = false;
+ _assert(ctx.imageSmoothingEnabled == false, "ctx.imageSmoothingEnabled == false");
+
+ ctx.reset();
+ _assert(ctx.imageSmoothingEnabled == default_value, "ctx.imageSmoothingEnabled == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_quality.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_quality.html
new file mode 100644
index 0000000000..e78abfce77
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.image_smoothing_quality.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.image_smoothing_quality</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.image_smoothing_quality</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.imageSmoothingQuality;
+
+ ctx.imageSmoothingQuality = 'high';
+ _assert(ctx.imageSmoothingQuality == 'high', "ctx.imageSmoothingQuality == 'high'");
+
+ ctx.reset();
+ _assert(ctx.imageSmoothingQuality == default_value, "ctx.imageSmoothingQuality == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.letter_spacing.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.letter_spacing.html
new file mode 100644
index 0000000000..3e732b1fa0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.letter_spacing.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.letter_spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.letter_spacing</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.letterSpacing;
+
+ ctx.letterSpacing = '12px';
+ _assert(ctx.letterSpacing == '12px', "ctx.letterSpacing == '12px'");
+
+ ctx.reset();
+ _assert(ctx.letterSpacing == default_value, "ctx.letterSpacing == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_cap.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_cap.html
new file mode 100644
index 0000000000..8020e284a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_cap.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.line_cap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.line_cap</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.lineCap;
+
+ ctx.lineCap = 'square';
+ _assert(ctx.lineCap == 'square', "ctx.lineCap == 'square'");
+
+ ctx.reset();
+ _assert(ctx.lineCap == default_value, "ctx.lineCap == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash.html
new file mode 100644
index 0000000000..ca68ae5158
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.line_dash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.line_dash</h1>
+<p class="desc">check that the line dash is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the line dash is reset");
+_addTest(function(canvas, ctx) {
+
+ ctx.setLineDash([1, 2]);
+
+ ctx.reset();
+ _assert(ctx.getLineDash().length == 0, "ctx.getLineDash().length == 0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash_offset.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash_offset.html
new file mode 100644
index 0000000000..10015199b9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_dash_offset.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.line_dash_offset</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.line_dash_offset</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.lineDashOffset;
+
+ ctx.lineDashOffset = 1.0;
+ _assert(ctx.lineDashOffset == 1.0, "ctx.lineDashOffset == 1.0");
+
+ ctx.reset();
+ _assert(ctx.lineDashOffset == default_value, "ctx.lineDashOffset == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_join.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_join.html
new file mode 100644
index 0000000000..1c20cb1b2a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_join.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.line_join</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.line_join</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.lineJoin;
+
+ ctx.lineJoin = 'bevel';
+ _assert(ctx.lineJoin == 'bevel', "ctx.lineJoin == 'bevel'");
+
+ ctx.reset();
+ _assert(ctx.lineJoin == default_value, "ctx.lineJoin == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_width.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_width.html
new file mode 100644
index 0000000000..db2516b535
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.line_width.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.line_width</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.line_width</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.lineWidth;
+
+ ctx.lineWidth = 1;
+ _assert(ctx.lineWidth == 1, "ctx.lineWidth == 1");
+
+ ctx.reset();
+ _assert(ctx.lineWidth == default_value, "ctx.lineWidth == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.miter_limit.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.miter_limit.html
new file mode 100644
index 0000000000..7920c38d52
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.miter_limit.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.miter_limit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.miter_limit</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.miterLimit;
+
+ ctx.miterLimit = 1.0;
+ _assert(ctx.miterLimit == 1.0, "ctx.miterLimit == 1.0");
+
+ ctx.reset();
+ _assert(ctx.miterLimit == default_value, "ctx.miterLimit == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_blur.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_blur.html
new file mode 100644
index 0000000000..07784de7d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_blur.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.shadow_blur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.shadow_blur</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.shadowBlur;
+
+ ctx.shadowBlur = 10.0;
+ _assert(ctx.shadowBlur == 10.0, "ctx.shadowBlur == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowBlur == default_value, "ctx.shadowBlur == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_color.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_color.html
new file mode 100644
index 0000000000..9e8ee6c7f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_color.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.shadow_color</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.shadow_color</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.shadowColor;
+
+ ctx.shadowColor = '#ff0000';
+ _assert(ctx.shadowColor == '#ff0000', "ctx.shadowColor == '#ff0000'");
+
+ ctx.reset();
+ _assert(ctx.shadowColor == default_value, "ctx.shadowColor == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_x.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_x.html
new file mode 100644
index 0000000000..7992ce4c1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_x.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.shadow_offset_x</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.shadow_offset_x</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.shadowOffsetX;
+
+ ctx.shadowOffsetX = 10.0;
+ _assert(ctx.shadowOffsetX == 10.0, "ctx.shadowOffsetX == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowOffsetX == default_value, "ctx.shadowOffsetX == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_y.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_y.html
new file mode 100644
index 0000000000..84a38e0bdb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.shadow_offset_y.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.shadow_offset_y</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.shadow_offset_y</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.shadowOffsetY;
+
+ ctx.shadowOffsetY = 10.0;
+ _assert(ctx.shadowOffsetY == 10.0, "ctx.shadowOffsetY == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowOffsetY == default_value, "ctx.shadowOffsetY == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.stroke_style.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.stroke_style.html
new file mode 100644
index 0000000000..4acb19c41e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.stroke_style.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.stroke_style</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.stroke_style</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.strokeStyle;
+
+ ctx.strokeStyle = '#ffffff';
+ _assert(ctx.strokeStyle == '#ffffff', "ctx.strokeStyle == '#ffffff'");
+
+ ctx.reset();
+ _assert(ctx.strokeStyle == default_value, "ctx.strokeStyle == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_align.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_align.html
new file mode 100644
index 0000000000..a625737d72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_align.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.text_align</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.text_align</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.textAlign;
+
+ ctx.textAlign = 'end';
+ _assert(ctx.textAlign == 'end', "ctx.textAlign == 'end'");
+
+ ctx.reset();
+ _assert(ctx.textAlign == default_value, "ctx.textAlign == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_baseline.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_baseline.html
new file mode 100644
index 0000000000..2e4169bdb1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_baseline.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.text_baseline</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.text_baseline</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.textBaseline;
+
+ ctx.textBaseline = 'middle';
+ _assert(ctx.textBaseline == 'middle', "ctx.textBaseline == 'middle'");
+
+ ctx.reset();
+ _assert(ctx.textBaseline == default_value, "ctx.textBaseline == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_rendering.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_rendering.html
new file mode 100644
index 0000000000..e182babee4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.text_rendering.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.text_rendering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.text_rendering</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.textRendering;
+
+ ctx.textRendering = 'geometricPrecision';
+ _assert(ctx.textRendering == 'geometricPrecision', "ctx.textRendering == 'geometricPrecision'");
+
+ ctx.reset();
+ _assert(ctx.textRendering == default_value, "ctx.textRendering == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.transformation_matrix.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.transformation_matrix.html
new file mode 100644
index 0000000000..7872681bfc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.transformation_matrix.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.transformation_matrix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.transformation_matrix</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ ctx.scale(2, 2);
+
+ ctx.reset();
+ _assert(ctx.getTransform().isIdentity, "ctx.getTransform().isIdentity");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.word_spacing.html b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.word_spacing.html
new file mode 100644
index 0000000000..c4a448f21c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/reset/2d.reset.state.word_spacing.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.word_spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.reset.state.word_spacing</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("check that the state is reset");
+_addTest(function(canvas, ctx) {
+
+ const default_value = ctx.wordSpacing;
+
+ ctx.wordSpacing = '12px';
+ _assert(ctx.wordSpacing == '12px', "ctx.wordSpacing == '12px'");
+
+ ctx.reset();
+ _assert(ctx.wordSpacing == default_value, "ctx.wordSpacing == default_value");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.basic.html b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.basic.html
new file mode 100644
index 0000000000..e4cf8ea9ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.basic.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.scrollPathIntoView.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.scrollPathIntoView.basic</h1>
+<p class="desc">scrollPathIntoView() works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("scrollPathIntoView() works");
+_addTest(function(canvas, ctx) {
+
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; left: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ ctx.beginPath();
+ ctx.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView();
+ var rect = canvas.getBoundingClientRect();
+ _assertSame(Math.round(rect.top), -8, "Math.round(rect.top)", "-8");
+ _assertSame(Math.round(rect.left), 200, "Math.round(rect.left)", "200");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.path.html b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.path.html
new file mode 100644
index 0000000000..ee1f833aaf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.path.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.scrollPathIntoView.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.scrollPathIntoView.path</h1>
+<p class="desc">scrollPathIntoView() with path argument works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("scrollPathIntoView() with path argument works");
+_addTest(function(canvas, ctx) {
+
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; left: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ var path = new Path2D();
+ path.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView(path);
+ var rect = canvas.getBoundingClientRect();
+ _assertSame(Math.round(rect.top), -8, "Math.round(rect.top)", "-8");
+ _assertSame(Math.round(rect.left), 200, "Math.round(rect.left)", "200");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalLR.html b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalLR.html
new file mode 100644
index 0000000000..9d09980b6f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalLR.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.scrollPathIntoView.verticalLR</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.scrollPathIntoView.verticalLR</h1>
+<p class="desc">scrollPathIntoView() works in vertical-lr writing mode</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("scrollPathIntoView() works in vertical-lr writing mode");
+_addTest(function(canvas, ctx) {
+
+ document.documentElement.style.cssText = 'writing-mode: vertical-lr';
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; left: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ ctx.beginPath();
+ ctx.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView();
+ var rect = canvas.getBoundingClientRect();
+ _assertSame(Math.round(rect.top), 100, "Math.round(rect.top)", "100");
+ _assertSame(Math.round(rect.left), -4, "Math.round(rect.left)", "-4");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalRL.html b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalRL.html
new file mode 100644
index 0000000000..8193106f8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/scroll/2d.scrollPathIntoView.verticalRL.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.scrollPathIntoView.verticalRL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.scrollPathIntoView.verticalRL</h1>
+<p class="desc">scrollPathIntoView() works in vertical-rl writing mode</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("scrollPathIntoView() works in vertical-rl writing mode");
+_addTest(function(canvas, ctx) {
+
+ document.documentElement.style.cssText = 'writing-mode: vertical-rl';
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; right: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ ctx.beginPath();
+ ctx.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView();
+ var rect = canvas.getBoundingClientRect();
+ var viewportWidth = document.scrollingElement.clientWidth;
+ var canvasWidth = canvas.width;
+ _assertSame(Math.round(rect.top), 100, "Math.round(rect.top)", "100");
+ _assertSame(Math.round(rect.right), viewportWidth + (canvasWidth - 4 - 16), "Math.round(rect.right)", "viewportWidth + (canvasWidth - 4 - 16)");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.1.html
new file mode 100644
index 0000000000..8888ae6885
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.alpha.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.alpha.1</h1>
+<p class="desc">Shadow color alpha components are used</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadow color alpha components are used");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(255, 0, 0, 0.01)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 4);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.html
new file mode 100644
index 0000000000..534d3dd16d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.alpha.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.alpha.2</h1>
+<p class="desc">Shadow color alpha components are used</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.alpha.2.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadow color alpha components are used");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.5)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.html
new file mode 100644
index 0000000000..468228208c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.alpha.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.alpha.3</h1>
+<p class="desc">Shadows are affected by globalAlpha</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.alpha.3.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are affected by globalAlpha");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.5;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.html
new file mode 100644
index 0000000000..2932c9dfe1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.alpha.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.alpha.4</h1>
+<p class="desc">Shadows with alpha components are correctly affected by globalAlpha</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.alpha.4.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows with alpha components are correctly affected by globalAlpha");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.707)';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.707;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.html
new file mode 100644
index 0000000000..ffa9ed8fde
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.alpha.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.alpha.5</h1>
+<p class="desc">Shadows of shapes with alpha components are drawn correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.alpha.5.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows of shapes with alpha components are drawn correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgba(64, 0, 0, 0.5)';
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.alpha.5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.initial.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.initial.html
new file mode 100644
index 0000000000..b688156546
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.initial.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowBlur.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowBlur.initial</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.invalid.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.invalid.html
new file mode 100644
index 0000000000..68f5dd41bc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.invalid.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowBlur.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowBlur.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -2;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = Infinity;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -Infinity;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = NaN;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = 'string';
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = true;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = false;
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.valid.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.valid.html
new file mode 100644
index 0000000000..51063a6ade
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowBlur.valid.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowBlur.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowBlur.valid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.shadowBlur = 1;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 0.5;
+ _assertSame(ctx.shadowBlur, 0.5, "ctx.shadowBlur", "0.5");
+
+ ctx.shadowBlur = 1e6;
+ _assertSame(ctx.shadowBlur, 1e6, "ctx.shadowBlur", "1e6");
+
+ ctx.shadowBlur = 0;
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.basic.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.basic.html
new file mode 100644
index 0000000000..bfdc54d31b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.basic.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowColor.current.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowColor.current.basic</h1>
+<p class="desc">currentColor is computed from the canvas element</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("currentColor is computed from the canvas element");
+_addTest(function(canvas, ctx) {
+
+ canvas.style.color = '#0f0';
+ ctx.shadowColor = 'currentColor';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.changed.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.changed.html
new file mode 100644
index 0000000000..9eee2122d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.changed.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowColor.current.changed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowColor.current.changed</h1>
+<p class="desc">currentColor is computed when the attribute is set, not when it is painted</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("currentColor is computed when the attribute is set, not when it is painted");
+_addTest(function(canvas, ctx) {
+
+ canvas.style.color = '#0f0';
+ ctx.shadowColor = 'currentColor';
+ canvas.style.color = '#f00';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.removed.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.removed.html
new file mode 100644
index 0000000000..e1989cbdc8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.current.removed.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowColor.current.removed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowColor.current.removed</h1>
+<p class="desc">currentColor is solid black when the canvas element is not in a document</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("currentColor is solid black when the canvas element is not in a document");
+_addTest(function(canvas, ctx) {
+
+ // Try not to let it undetectably incorrectly pick up opaque-black
+ // from other parts of the document:
+ document.documentElement.style.color = '#f00';
+ document.body.style.color = '#f00';
+ canvas.style.color = '#f00';
+
+ canvas.remove();
+ ctx.shadowColor = 'currentColor';
+ _assertSame(ctx.shadowColor, '#000000', "ctx.shadowColor", "'#000000'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.initial.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.initial.html
new file mode 100644
index 0000000000..f4d0d33d6d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.initial.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowColor.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowColor.initial</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.shadowColor, 'rgba(0, 0, 0, 0)', "ctx.shadowColor", "'rgba(0, 0, 0, 0)'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.invalid.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.invalid.html
new file mode 100644
index 0000000000..73f835dd96
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.invalid.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowColor.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowColor.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'bogus';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'red bogus';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = ctx;
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = undefined;
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.valid.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.valid.html
new file mode 100644
index 0000000000..bc86c3d6c1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowColor.valid.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowColor.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowColor.valid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.shadowColor = 'lime';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = 'RGBA(0,255, 0,0)';
+ _assertSame(ctx.shadowColor, 'rgba(0, 255, 0, 0)', "ctx.shadowColor", "'rgba(0, 255, 0, 0)'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.initial.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.initial.html
new file mode 100644
index 0000000000..056dd1607b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.initial.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowOffset.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowOffset.initial</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.invalid.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.invalid.html
new file mode 100644
index 0000000000..1567edb16a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.invalid.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowOffset.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowOffset.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = Infinity;
+ ctx.shadowOffsetY = Infinity;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = -Infinity;
+ ctx.shadowOffsetY = -Infinity;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = NaN;
+ ctx.shadowOffsetY = NaN;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = 'string';
+ ctx.shadowOffsetY = 'string';
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = true;
+ ctx.shadowOffsetY = true;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 1, "ctx.shadowOffsetY", "1");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = false;
+ ctx.shadowOffsetY = false;
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.valid.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.valid.html
new file mode 100644
index 0000000000..04fe18c391
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.attributes.shadowOffset.valid.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.attributes.shadowOffset.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.attributes.shadowOffset.valid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 0.5;
+ ctx.shadowOffsetY = 0.25;
+ _assertSame(ctx.shadowOffsetX, 0.5, "ctx.shadowOffsetX", "0.5");
+ _assertSame(ctx.shadowOffsetY, 0.25, "ctx.shadowOffsetY", "0.25");
+
+ ctx.shadowOffsetX = -0.5;
+ ctx.shadowOffsetY = -0.25;
+ _assertSame(ctx.shadowOffsetX, -0.5, "ctx.shadowOffsetX", "-0.5");
+ _assertSame(ctx.shadowOffsetY, -0.25, "ctx.shadowOffsetY", "-0.25");
+
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+
+ ctx.shadowOffsetX = 1e6;
+ ctx.shadowOffsetY = 1e6;
+ _assertSame(ctx.shadowOffsetX, 1e6, "ctx.shadowOffsetX", "1e6");
+ _assertSame(ctx.shadowOffsetY, 1e6, "ctx.shadowOffsetY", "1e6");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high-manual.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high-manual.html
new file mode 100644
index 0000000000..aae0a163bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high-manual.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.blur.high</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.blur.high</h1>
+<p class="desc">Shadows look correct for large blurs</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.blur.high.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows look correct for large blurs");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 100;
+ ctx.fillRect(-200, -200, 200, 400);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high.png
new file mode 100644
index 0000000000..743640b79f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.high.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low-manual.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low-manual.html
new file mode 100644
index 0000000000..9674d6f0db
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low-manual.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.blur.low</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.blur.low</h1>
+<p class="desc">Shadows look correct for small blurs</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.blur.low.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows look correct for small blurs");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 25;
+ for (var x = 0; x < 100; ++x) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(x, 0, 1, 50);
+ ctx.clip();
+ ctx.shadowBlur = x;
+ ctx.fillRect(-200, -200, 500, 200);
+ ctx.restore();
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low.png
new file mode 100644
index 0000000000..99fb651c21
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.blur.low.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.html
new file mode 100644
index 0000000000..18818fa726
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.canvas.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.canvas.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent canvases</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.canvas.alpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn correctly for partially-transparent canvases");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'rgba(255, 0, 0, 0.5)';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.basic.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.basic.html
new file mode 100644
index 0000000000..e752c8f180
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.basic.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.canvas.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.canvas.basic</h1>
+<p class="desc">Shadows are drawn for canvases</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for canvases");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.1.html
new file mode 100644
index 0000000000..7d159328a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.canvas.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.canvas.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent canvases</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent canvases");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.2.html
new file mode 100644
index 0000000000..4326d42bbe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.canvas.transparent.2.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.canvas.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.canvas.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of canvases</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent parts of canvases");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.drawImage(canvas2, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(canvas2, -50, -50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.1.html
new file mode 100644
index 0000000000..6caff8b92f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.clip.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.clip.1</h1>
+<p class="desc">Shadows of clipped shapes are still drawn within the clipping region</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows of clipped shapes are still drawn within the clipping region");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.2.html
new file mode 100644
index 0000000000..a5b05237e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.2.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.clip.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.clip.2</h1>
+<p class="desc">Shadows are not drawn outside the clipping region</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn outside the clipping region");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.3.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.3.html
new file mode 100644
index 0000000000..b7652634fb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.clip.3.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.clip.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.clip.3</h1>
+<p class="desc">Shadows of clipped shapes are still drawn within the clipping region</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows of clipped shapes are still drawn within the clipping region");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.1.html
new file mode 100644
index 0000000000..d64ecb4972
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.composite.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.composite.1</h1>
+<p class="desc">Shadows are drawn using globalCompositeOperation</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn using globalCompositeOperation");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, 0, 200, 50);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.2.html
new file mode 100644
index 0000000000..abc6b98d72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.composite.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.composite.2</h1>
+<p class="desc">Shadows are drawn using globalCompositeOperation</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn using globalCompositeOperation");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-10, -10, 120, 70);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.3.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.3.html
new file mode 100644
index 0000000000..45eebd9eb0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.composite.3.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.composite.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.composite.3</h1>
+<p class="desc">Areas outside shadows are drawn correctly with destination-out</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Areas outside shadows are drawn correctly with destination-out");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 10;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(200, 0, 100, 50);
+
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.blur.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.blur.html
new file mode 100644
index 0000000000..7efa459c29
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.blur.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.enable.blur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.enable.blur</h1>
+<p class="desc">Shadows are drawn if shadowBlur is set</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn if shadowBlur is set");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.1.html
new file mode 100644
index 0000000000..953ea04277
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.enable.off.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.enable.off.1</h1>
+<p class="desc">Shadows are not drawn when only shadowColor is set</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn when only shadowColor is set");
+_addTest(function(canvas, ctx) {
+
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.2.html
new file mode 100644
index 0000000000..2a8cf7301d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.off.2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.enable.off.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.enable.off.2</h1>
+<p class="desc">Shadows are not drawn when only shadowColor is set</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn when only shadowColor is set");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.x.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.x.html
new file mode 100644
index 0000000000..6eae985fd6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.x.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.enable.x</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.enable.x</h1>
+<p class="desc">Shadows are drawn if shadowOffsetX is set</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn if shadowOffsetX is set");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.y.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.y.html
new file mode 100644
index 0000000000..2025babca6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.enable.y.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.enable.y</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.enable.y</h1>
+<p class="desc">Shadows are drawn if shadowOffsetY is set</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn if shadowOffsetY is set");
+_addTest(function(canvas, ctx) {
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.html
new file mode 100644
index 0000000000..65f4fdad27
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.gradient.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.gradient.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent gradient fills</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.gradient.alpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn correctly for partially-transparent gradient fills");
+_addTest(function(canvas, ctx) {
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(255,0,0,0.5)');
+ gradient.addColorStop(1, 'rgba(255,0,0,0.5)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.basic.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.basic.html
new file mode 100644
index 0000000000..a7c5efe738
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.gradient.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.gradient.basic</h1>
+<p class="desc">Shadows are drawn for gradient fills</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for gradient fills");
+_addTest(function(canvas, ctx) {
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(1, '#f00');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.1.html
new file mode 100644
index 0000000000..1c11f75e80
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.gradient.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.gradient.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent gradient fills</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent gradient fills");
+_addTest(function(canvas, ctx) {
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.2.html
new file mode 100644
index 0000000000..244aecab21
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.gradient.transparent.2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.gradient.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.gradient.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of gradient fills</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent parts of gradient fills");
+_addTest(function(canvas, ctx) {
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(0.499, '#f00');
+ gradient.addColorStop(0.5, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.html
new file mode 100644
index 0000000000..f00e1752df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.image.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.image.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent images</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.image.alpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn correctly for partially-transparent images");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ var img = document.getElementById('transparent50.png');
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+<img src="/images/transparent50.png" id="transparent50.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.basic.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.basic.html
new file mode 100644
index 0000000000..d106e223db
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.basic.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.image.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.image.basic</h1>
+<p class="desc">Shadows are drawn for images</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for images");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ var img = document.getElementById('red.png');
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.scale.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.scale.html
new file mode 100644
index 0000000000..7e07fff378
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.scale.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.image.scale</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.image.scale</h1>
+<p class="desc">Shadows are drawn correctly for scaled images</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn correctly for scaled images");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ var img = document.getElementById('redtransparent.png');
+ ctx.drawImage(img, 0, 0, 100, 50, -10, -50, 240, 50);
+
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+});
+</script>
+<img src="/images/redtransparent.png" id="redtransparent.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.section.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.section.html
new file mode 100644
index 0000000000..a1b3e7032f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.section.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.image.section</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.image.section</h1>
+<p class="desc">Shadows are not drawn for areas outside image source rectangles</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for areas outside image source rectangles");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#f00';
+ var img = document.getElementById('redtransparent.png');
+ ctx.drawImage(img, 50, 0, 50, 50, 0, -50, 50, 50);
+
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+});
+</script>
+<img src="/images/redtransparent.png" id="redtransparent.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.1.html
new file mode 100644
index 0000000000..74d8b22951
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.image.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.image.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent images</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent images");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ var img = document.getElementById('transparent.png');
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+<img src="/images/transparent.png" id="transparent.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.2.html
new file mode 100644
index 0000000000..1a52a52c3f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.image.transparent.2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.image.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.image.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of images</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent parts of images");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ var img = document.getElementById('redtransparent.png');
+ ctx.drawImage(img, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(img, -50, -50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+<img src="/images/redtransparent.png" id="redtransparent.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeX.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeX.html
new file mode 100644
index 0000000000..0a73b07b6c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeX.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.offset.negativeX</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.offset.negativeX</h1>
+<p class="desc">Shadows can be offset with negative x</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows can be offset with negative x");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = -50;
+ ctx.fillRect(50, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeY.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeY.html
new file mode 100644
index 0000000000..04c9620fd2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.negativeY.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.offset.negativeY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.offset.negativeY</h1>
+<p class="desc">Shadows can be offset with negative y</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows can be offset with negative y");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = -25;
+ ctx.fillRect(0, 25, 100, 25);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveX.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveX.html
new file mode 100644
index 0000000000..1254474385
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveX.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.offset.positiveX</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.offset.positiveX</h1>
+<p class="desc">Shadows can be offset with positive x</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows can be offset with positive x");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveY.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveY.html
new file mode 100644
index 0000000000..fdcf25a435
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.offset.positiveY.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.offset.positiveY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.offset.positiveY</h1>
+<p class="desc">Shadows can be offset with positive y</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows can be offset with positive y");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 25;
+ ctx.fillRect(0, 0, 100, 25);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.outside.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.outside.html
new file mode 100644
index 0000000000..fcf2129488
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.outside.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.outside</h1>
+<p class="desc">Shadows of shapes outside the visible area can be offset onto the visible area</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows of shapes outside the visible area can be offset onto the visible area");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.fillRect(-100, 0, 25, 50);
+ ctx.shadowOffsetX = -100;
+ ctx.fillRect(175, 0, 25, 50);
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 100;
+ ctx.fillRect(25, -100, 50, 25);
+ ctx.shadowOffsetY = -100;
+ ctx.fillRect(25, 125, 50, 25);
+ _assertPixel(canvas, 12,25, 0,255,0,255);
+ _assertPixel(canvas, 87,25, 0,255,0,255);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.html
new file mode 100644
index 0000000000..10b5414b12
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.pattern.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.pattern.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent fill patterns</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.shadow.pattern.alpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn correctly for partially-transparent fill patterns");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('transparent50.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+});
+</script>
+<img src="/images/transparent50.png" id="transparent50.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.png b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.basic.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.basic.html
new file mode 100644
index 0000000000..47d2149a57
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.pattern.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.pattern.basic</h1>
+<p class="desc">Shadows are drawn for fill patterns</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for fill patterns");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('red.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+<img src="/images/red.png" id="red.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.1.html
new file mode 100644
index 0000000000..c41dd83778
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.pattern.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.pattern.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent fill patterns</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent fill patterns");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('transparent.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+<img src="/images/transparent.png" id="transparent.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.2.html
new file mode 100644
index 0000000000..ddaf21fe90
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.pattern.transparent.2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.pattern.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.pattern.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of fill patterns</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for transparent parts of fill patterns");
+_addTest(function(canvas, ctx) {
+
+ var img = document.getElementById('redtransparent.png');
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+<img src="/images/redtransparent.png" id="redtransparent.png" class="resource">
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.basic.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.basic.html
new file mode 100644
index 0000000000..6146ba7d19
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.basic.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.stroke.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.stroke.basic</h1>
+<p class="desc">Shadows are drawn for strokes</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for strokes");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, -25);
+ ctx.lineTo(100, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.1.html
new file mode 100644
index 0000000000..5d6e53a685
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.stroke.cap.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.stroke.cap.1</h1>
+<p class="desc">Shadows are not drawn for areas outside stroke caps</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for areas outside stroke caps");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'butt';
+ ctx.moveTo(-50, -25);
+ ctx.lineTo(0, -25);
+ ctx.moveTo(100, -25);
+ ctx.lineTo(150, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.2.html
new file mode 100644
index 0000000000..f18a9b6cc5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.cap.2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.stroke.cap.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.stroke.cap.2</h1>
+<p class="desc">Shadows are drawn for stroke caps</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for stroke caps");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'square';
+ ctx.moveTo(25, -25);
+ ctx.lineTo(75, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.1.html
new file mode 100644
index 0000000000..46735a918f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.stroke.join.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.stroke.join.1</h1>
+<p class="desc">Shadows are not drawn for areas outside stroke joins</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are not drawn for areas outside stroke joins");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.2.html
new file mode 100644
index 0000000000..85abc24e81
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.2.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.stroke.join.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.stroke.join.2</h1>
+<p class="desc">Shadows are drawn for stroke joins</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for stroke joins");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.3.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.3.html
new file mode 100644
index 0000000000..795c6137d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.stroke.join.3.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.stroke.join.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.stroke.join.3</h1>
+<p class="desc">Shadows are drawn for stroke joins respecting miter limit</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows are drawn for stroke joins respecting miter limit");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 0.1;
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100); // (not an exact right angle, to avoid some other bug in Firefox 3)
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.1.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.1.html
new file mode 100644
index 0000000000..abfb54e5db
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.transform.1</h1>
+<p class="desc">Shadows take account of transformations</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadows take account of transformations");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.translate(100, 100);
+ ctx.fillRect(-100, -150, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.2.html b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.2.html
new file mode 100644
index 0000000000..736dd4efa4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/shadows/2d.shadow.transform.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.shadow.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.shadow.transform.2</h1>
+<p class="desc">Shadow offsets are not affected by transformations</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Shadow offsets are not affected by transformations");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.rotate(Math.PI)
+ ctx.fillRect(-100, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.align.default.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.align.default.html
new file mode 100644
index 0000000000..d32f7831d0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.align.default.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.align.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.align.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.align.invalid.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.align.invalid.html
new file mode 100644
index 0000000000..7b7f33766a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.align.invalid.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.align.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.align.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'bogus';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'END';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end ';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end\0';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.align.valid.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.align.valid.html
new file mode 100644
index 0000000000..a568530f3b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.align.valid.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.align.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.align.valid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.textAlign = 'start';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'end';
+ _assertSame(ctx.textAlign, 'end', "ctx.textAlign", "'end'");
+
+ ctx.textAlign = 'left';
+ _assertSame(ctx.textAlign, 'left', "ctx.textAlign", "'left'");
+
+ ctx.textAlign = 'right';
+ _assertSame(ctx.textAlign, 'right', "ctx.textAlign", "'right'");
+
+ ctx.textAlign = 'center';
+ _assertSame(ctx.textAlign, 'center', "ctx.textAlign", "'center'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.default.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.default.html
new file mode 100644
index 0000000000..08e8512c66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.default.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.baseline.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.baseline.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.textBaseline, 'alphabetic', "ctx.textBaseline", "'alphabetic'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.invalid.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.invalid.html
new file mode 100644
index 0000000000..e527a77288
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.invalid.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.baseline.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.baseline.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'bogus';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'MIDDLE';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle ';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle\0';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.valid.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.valid.html
new file mode 100644
index 0000000000..238f6ff7dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.baseline.valid.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.baseline.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.baseline.valid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.textBaseline = 'top';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'hanging';
+ _assertSame(ctx.textBaseline, 'hanging', "ctx.textBaseline", "'hanging'");
+
+ ctx.textBaseline = 'middle';
+ _assertSame(ctx.textBaseline, 'middle', "ctx.textBaseline", "'middle'");
+
+ ctx.textBaseline = 'alphabetic';
+ _assertSame(ctx.textBaseline, 'alphabetic', "ctx.textBaseline", "'alphabetic'");
+
+ ctx.textBaseline = 'ideographic';
+ _assertSame(ctx.textBaseline, 'ideographic', "ctx.textBaseline", "'ideographic'");
+
+ ctx.textBaseline = 'bottom';
+ _assertSame(ctx.textBaseline, 'bottom', "ctx.textBaseline", "'bottom'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.center.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.center.html
new file mode 100644
index 0000000000..723ebe8764
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.center.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.align.center</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.align.center</h1>
+<p class="desc">textAlign center is the center of the em squares (not the bounding box)</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'center';
+ ctx.fillText('DD', 50, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign center is the center of the em squares (not the bounding box)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.ltr.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.ltr.html
new file mode 100644
index 0000000000..d29da833cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.ltr.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.align.end.ltr</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.align.end.ltr</h1>
+<p class="desc">textAlign end with ltr is the right edge</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"dir="ltr"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign end with ltr is the right edge");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.rtl.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.rtl.html
new file mode 100644
index 0000000000..09a9658ac0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.end.rtl.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.align.end.rtl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.align.end.rtl</h1>
+<p class="desc">textAlign end with rtl is the left edge</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"dir="rtl"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign end with rtl is the left edge");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.left.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.left.html
new file mode 100644
index 0000000000..dda5318fbf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.left.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.align.left</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.align.left</h1>
+<p class="desc">textAlign left is the left of the first em square (not the bounding box)</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'left';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign left is the left of the first em square (not the bounding box)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.right.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.right.html
new file mode 100644
index 0000000000..2b3217278a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.right.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.align.right</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.align.right</h1>
+<p class="desc">textAlign right is the right of the last em square (not the bounding box)</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign right is the right of the last em square (not the bounding box)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.ltr.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.ltr.html
new file mode 100644
index 0000000000..8e14642fc2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.ltr.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.align.start.ltr</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.align.start.ltr</h1>
+<p class="desc">textAlign start with ltr is the left edge</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"dir="ltr"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign start with ltr is the left edge");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.rtl.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.rtl.html
new file mode 100644
index 0000000000..fe1ccd1137
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.align.start.rtl.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.align.start.rtl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.align.start.rtl</h1>
+<p class="desc">textAlign start with rtl is the right edge</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"dir="rtl"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign start with rtl is the right edge");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.alphabetic.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.alphabetic.html
new file mode 100644
index 0000000000..a45db596d0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.alphabetic.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.baseline.alphabetic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.baseline.alphabetic</h1>
+<p class="desc"></p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'alphabetic';
+ ctx.fillText('CC', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.bottom.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.bottom.html
new file mode 100644
index 0000000000..ed1bf002a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.bottom.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.baseline.bottom</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.baseline.bottom</h1>
+<p class="desc">textBaseline bottom is the bottom of the em square (not the bounding box)</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'bottom';
+ ctx.fillText('CC', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textBaseline bottom is the bottom of the em square (not the bounding box)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.hanging.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.hanging.html
new file mode 100644
index 0000000000..2f274b4199
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.hanging.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.baseline.hanging</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.baseline.hanging</h1>
+<p class="desc"></p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'hanging';
+ ctx.fillText('CC', 0, 12.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.ideographic.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.ideographic.html
new file mode 100644
index 0000000000..8d6a12e262
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.ideographic.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.baseline.ideographic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.baseline.ideographic</h1>
+<p class="desc"></p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'ideographic';
+ ctx.fillText('CC', 0, 31.25);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.middle.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.middle.html
new file mode 100644
index 0000000000..5b021a5877
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.middle.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.baseline.middle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.baseline.middle</h1>
+<p class="desc">textBaseline middle is the middle of the em square (not the bounding box)</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'middle';
+ ctx.fillText('CC', 0, 25);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textBaseline middle is the middle of the em square (not the bounding box)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.top.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.top.html
new file mode 100644
index 0000000000..2011514d33
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.baseline.top.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.baseline.top</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.baseline.top</h1>
+<p class="desc">textBaseline top is the top of the em square (not the bounding box)</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'top';
+ ctx.fillText('CC', 0, 0);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textBaseline top is the top of the em square (not the bounding box)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic-manual.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic-manual.html
new file mode 100644
index 0000000000..d3f5df9d01
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic-manual.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.basic</h1>
+<p class="desc">fillText draws filled text</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.text.draw.fill.basic.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText draws filled text");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic.png b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic.png
new file mode 100644
index 0000000000..70d7b046cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.basic.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.NaN.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.NaN.html
new file mode 100644
index 0000000000..9705d28830
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.NaN.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.maxWidth.NaN</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.maxWidth.NaN</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, NaN);
+ _assertGreen(ctx, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.bound.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.bound.html
new file mode 100644
index 0000000000..c5f7dcf119
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.bound.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.maxWidth.bound</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.fill.maxWidth.bound</h1>
+<p class="desc">fillText handles maxWidth based on line size, not bounding box size</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('DD', 0, 37.5, 100);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "fillText handles maxWidth based on line size, not bounding box size");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.fontface.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.fontface.html
new file mode 100644
index 0000000000..7df5553815
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.fontface.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.maxWidth.fontface</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.fill.maxWidth.fontface</h1>
+<p class="desc">fillText works on @font-face fonts</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillText('EEEE', -50, 37.5, 40);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "fillText works on @font-face fonts");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large-manual.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large-manual.html
new file mode 100644
index 0000000000..96bb2e7de1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.maxWidth.large</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.maxWidth.large</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.text.draw.fill.maxWidth.large.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35, 200);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large.png b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large.png
new file mode 100644
index 0000000000..70d7b046cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.large.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.negative.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.negative.html
new file mode 100644
index 0000000000..ad50d57608
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.negative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.maxWidth.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.maxWidth.negative</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, -1);
+ _assertGreen(ctx, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.small.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.small.html
new file mode 100644
index 0000000000..75866af406
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.small.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.maxWidth.small</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.maxWidth.small</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', -100, 35, 90);
+ _assertGreen(ctx, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.zero.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.zero.html
new file mode 100644
index 0000000000..a175a57879
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.maxWidth.zero.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.maxWidth.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.maxWidth.zero</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, 0);
+ _assertGreen(ctx, 100, 50);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl-manual.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl-manual.html
new file mode 100644
index 0000000000..6917d7ed6c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl-manual.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.rtl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.rtl</h1>
+<p class="desc">fillText respects Right-To-Left Override characters</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.text.draw.fill.rtl.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText respects Right-To-Left Override characters");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('\u202eFAIL \xa0 \xa0 SSAP', 5, 35);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl.png b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl.png
new file mode 100644
index 0000000000..70d7b046cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.rtl.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.unaffected.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.unaffected.html
new file mode 100644
index 0000000000..94ed31d199
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fill.unaffected.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fill.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.fill.unaffected</h1>
+<p class="desc">fillText does not start a new path or subpath</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("fillText does not start a new path or subpath");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.html
new file mode 100644
index 0000000000..c46ac2084e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fontface</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.fontface</h1>
+<p class="desc"></p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.notinpage.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.notinpage.html
new file mode 100644
index 0000000000..92891fb04f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.notinpage.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fontface.notinpage</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.fontface.notinpage</h1>
+<p class="desc">@font-face fonts should work even if they are not used in the page</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "@font-face fonts should work even if they are not used in the page");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.repeat.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.repeat.html
new file mode 100644
index 0000000000..d597540b97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.fontface.repeat.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.fontface.repeat</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.fontface.repeat</h1>
+<p class="desc">Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+
+ await new Promise(resolve => t.step_timeout(resolve, 500));
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.kern.consistent-manual.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.kern.consistent-manual.html
new file mode 100644
index 0000000000..1840ef01b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.kern.consistent-manual.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.kern.consistent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.kern.consistent</h1>
+<p class="desc">Stroked and filled text should have exactly the same kerning so it overlaps</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Stroked and filled text should have exactly the same kerning so it overlaps");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 3;
+ ctx.font = '20px Arial, sans-serif';
+ ctx.fillText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.fillText('ToToToToToToTo', -50, 45);
+ ctx.strokeText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.strokeText('ToToToToToToTo', -50, 45);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.basic.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.basic.html
new file mode 100644
index 0000000000..d4447402aa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.basic.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.space.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.space.basic</h1>
+<p class="desc">U+0020 is rendered the correct size (1em wide)</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', -100, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "U+0020 is rendered the correct size (1em wide)");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.end.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.end.html
new file mode 100644
index 0000000000..5a14dbd514
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.end.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.space.collapse.end</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.space.collapse.end</h1>
+<p class="desc">Space characters at the end of a line are NOT collapsed</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('EE ', 100, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+
+}, "Space characters at the end of a line are NOT collapsed");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.nonspace.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.nonspace.html
new file mode 100644
index 0000000000..1fc4203b90
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.nonspace.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.space.collapse.nonspace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.space.collapse.nonspace</h1>
+<p class="desc">Non-space characters are not converted to U+0020 and collapsed</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E\x0b EE', -150, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Non-space characters are not converted to U+0020 and collapsed");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.other.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.other.html
new file mode 100644
index 0000000000..ffc82929b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.other.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.space.collapse.other</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.space.collapse.other</h1>
+<p class="desc">Space characters are converted to U+0020, and are NOT collapsed</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dEE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+
+}, "Space characters are converted to U+0020, and are NOT collapsed");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.space.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.space.html
new file mode 100644
index 0000000000..64c14d1a54
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.space.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.space.collapse.space</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.space.collapse.space</h1>
+<p class="desc">Space characters are converted to U+0020, and are NOT collapsed</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+
+}, "Space characters are converted to U+0020, and are NOT collapsed");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.start.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.start.html
new file mode 100644
index 0000000000..272432c3be
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.space.collapse.start.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.space.collapse.start</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.draw.space.collapse.start</h1>
+<p class="desc">Space characters at the start of a line are NOT collapsed</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText(' EE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 255,0,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Space characters at the start of a line are NOT collapsed");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic-manual.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic-manual.html
new file mode 100644
index 0000000000..1db0f0694e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.stroke.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.stroke.basic</h1>
+<p class="desc">strokeText draws stroked text</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="2d.text.draw.stroke.basic.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeText draws stroked text");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 1;
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeText('PASS', 5, 35);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic.png b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic.png
new file mode 100644
index 0000000000..fb3b5b830d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.basic.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.unaffected.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.unaffected.html
new file mode 100644
index 0000000000..76a36476ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.draw.stroke.unaffected.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.draw.stroke.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.draw.stroke.unaffected</h1>
+<p class="desc">strokeText does not start a new path or subpath</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("strokeText does not start a new path or subpath");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeStyle = '#f00';
+ ctx.strokeText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.absolute.spacing.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.absolute.spacing.html
new file mode 100644
index 0000000000..1207f84e2b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.absolute.spacing.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.absolute.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.absolute.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with absolute length</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing letter spacing and word spacing with absolute length");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.letterSpacing = '3px';
+ _assertSame(ctx.letterSpacing, '3px', "ctx.letterSpacing", "'3px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.wordSpacing = '5px';
+ _assertSame(ctx.letterSpacing, '3px', "ctx.letterSpacing", "'3px'");
+ _assertSame(ctx.wordSpacing, '5px', "ctx.wordSpacing", "'5px'");
+
+ ctx.letterSpacing = '-1px';
+ ctx.wordSpacing = '-1px';
+ _assertSame(ctx.letterSpacing, '-1px', "ctx.letterSpacing", "'-1px'");
+ _assertSame(ctx.wordSpacing, '-1px', "ctx.wordSpacing", "'-1px'");
+
+ ctx.letterSpacing = '1PX';
+ ctx.wordSpacing = '10PX';
+ _assertSame(ctx.letterSpacing, '1px', "ctx.letterSpacing", "'1px'");
+ _assertSame(ctx.wordSpacing, '10px', "ctx.wordSpacing", "'10px'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.font-relative.spacing.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.font-relative.spacing.html
new file mode 100644
index 0000000000..a232ec1602
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.font-relative.spacing.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.font-relative.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.font-relative.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with font-relative length</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing letter spacing and word spacing with font-relative length");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.letterSpacing = '1EX';
+ ctx.wordSpacing = '1EM';
+ _assertSame(ctx.letterSpacing, '1ex', "ctx.letterSpacing", "'1ex'");
+ _assertSame(ctx.wordSpacing, '1em', "ctx.wordSpacing", "'1em'");
+
+ ctx.letterSpacing = '1ch';
+ ctx.wordSpacing = '1ic';
+ _assertSame(ctx.letterSpacing, '1ch', "ctx.letterSpacing", "'1ch'");
+ _assertSame(ctx.wordSpacing, '1ic', "ctx.wordSpacing", "'1ic'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.html
new file mode 100644
index 0000000000..6de9c6eb50
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.fontKerning</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.fontKerning</h1>
+<p class="desc">Testing basic functionalities of fontKerning for canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing basic functionalities of fontKerning for canvas");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "normal";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ width_normal = ctx.measureText("TAWATAVA").width;
+ ctx.fontKerning = "none";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ width_none = ctx.measureText("TAWATAVA").width;
+ _assert(width_normal < width_none, "width_normal < width_none");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.with.uppercase.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.with.uppercase.html
new file mode 100644
index 0000000000..991f35af0d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontKerning.with.uppercase.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.fontKerning.with.uppercase</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.fontKerning.with.uppercase</h1>
+<p class="desc">Testing basic functionalities of fontKerning for canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing basic functionalities of fontKerning for canvas");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "Normal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "normal";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ ctx.fontKerning = "Auto";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "noRmal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NoRMal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NORMAL";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+
+ ctx.fontKerning = "None";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "none";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ ctx.fontKerning = "Auto";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nOne";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nonE";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NONE";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontStretch.settings.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontStretch.settings.html
new file mode 100644
index 0000000000..b19eced891
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontStretch.settings.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.fontStretch.settings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.fontStretch.settings</h1>
+<p class="desc">Testing value setting of fontStretch in Canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing value setting of fontStretch in Canvas");
+_addTest(function(canvas, ctx) {
+
+ // Setting textRendering with lower cases
+ ctx.fontStretch = "ultra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-condensed", "ctx.fontStretch", "\"ultra-condensed\"");
+
+ ctx.fontStretch = "extra-condensed";
+ _assertSame(ctx.fontStretch, "extra-condensed", "ctx.fontStretch", "\"extra-condensed\"");
+
+ ctx.fontStretch = "condensed";
+ _assertSame(ctx.fontStretch, "condensed", "ctx.fontStretch", "\"condensed\"");
+
+ ctx.fontStretch = "semi-condensed";
+ _assertSame(ctx.fontStretch, "semi-condensed", "ctx.fontStretch", "\"semi-condensed\"");
+
+ ctx.fontStretch = "normal";
+ _assertSame(ctx.fontStretch, "normal", "ctx.fontStretch", "\"normal\"");
+
+ ctx.fontStretch = "semi-expanded";
+ _assertSame(ctx.fontStretch, "semi-expanded", "ctx.fontStretch", "\"semi-expanded\"");
+
+ ctx.fontStretch = "expanded";
+ _assertSame(ctx.fontStretch, "expanded", "ctx.fontStretch", "\"expanded\"");
+
+ ctx.fontStretch = "extra-expanded";
+ _assertSame(ctx.fontStretch, "extra-expanded", "ctx.fontStretch", "\"extra-expanded\"");
+
+ ctx.fontStretch = "ultra-expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ // Setting fontStretch with lower cases and upper cases word,
+ // these values should be ignored.
+ ctx.fontStretch = "ulTra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Extra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "cOndensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Semi-Condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "normaL";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "semi-Expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "eXtra-expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "abcd";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontVariant.settings.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontVariant.settings.html
new file mode 100644
index 0000000000..cff5ad183a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.fontVariant.settings.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.fontVariant.settings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.fontVariant.settings</h1>
+<p class="desc">Testing basic functionalities of fontVariant for canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing basic functionalities of fontVariant for canvas");
+_addTest(function(canvas, ctx) {
+
+ // Setting fontVariantCaps with lower cases
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "normal";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "small-caps";
+ _assertSame(ctx.fontVariantCaps, "small-caps", "ctx.fontVariantCaps", "\"small-caps\"");
+
+ ctx.fontVariantCaps = "all-small-caps";
+ _assertSame(ctx.fontVariantCaps, "all-small-caps", "ctx.fontVariantCaps", "\"all-small-caps\"");
+
+ ctx.fontVariantCaps = "petite-caps";
+ _assertSame(ctx.fontVariantCaps, "petite-caps", "ctx.fontVariantCaps", "\"petite-caps\"");
+
+ ctx.fontVariantCaps = "all-petite-caps";
+ _assertSame(ctx.fontVariantCaps, "all-petite-caps", "ctx.fontVariantCaps", "\"all-petite-caps\"");
+
+ ctx.fontVariantCaps = "unicase";
+ _assertSame(ctx.fontVariantCaps, "unicase", "ctx.fontVariantCaps", "\"unicase\"");
+
+ ctx.fontVariantCaps = "titling-caps";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+
+ // Setting fontVariantCaps with mixed-case values is not valid
+ ctx.fontVariantCaps = "nORmal";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+
+ ctx.fontVariantCaps = "normal";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "smaLL-caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "all-small-CAPS";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "pEtitE-caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "All-Petite-Caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "uNIcase";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "titling-CAPS";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ // Setting fontVariantCaps with non-existing font variant.
+ ctx.fontVariantCaps = "titling-caps";
+ ctx.fontVariantCaps = "abcd";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.invalid.spacing.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.invalid.spacing.html
new file mode 100644
index 0000000000..a0b8340b2c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.invalid.spacing.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.invalid.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.invalid.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with invalid units</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing letter spacing and word spacing with invalid units");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ }
+ test_word_spacing('0s');
+ test_word_spacing('1min');
+ test_word_spacing('1deg');
+ test_word_spacing('1pp');
+ test_word_spacing('initial');
+ test_word_spacing('inherit');
+ test_word_spacing('normal');
+ test_word_spacing('none');
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.change.font.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.change.font.html
new file mode 100644
index 0000000000..daff0cf9d6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.change.font.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.letterSpacing.change.font</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.letterSpacing.change.font</h1>
+<p class="desc">Set letter spacing and word spacing to font dependent value and verify it works after font change.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Set letter spacing and word spacing to font dependent value and verify it works after font change.");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ // Get the width for 'Hello World' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World').width;
+
+ ctx.letterSpacing = '1em';
+ _assertSame(ctx.letterSpacing, '1em', "ctx.letterSpacing", "'1em'");
+ // 1em = 10px. Add 10px after each letter in "Hello World",
+ // makes it 110px longer.
+ var width_with_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 110, 0.1, "letter-spacing error");
+
+ // Changing font to 20px. Without resetting the spacing, 1em letterSpacing
+ // is now 20px, so it's suppose to be 220px longer without any letterSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World').width;
+ // Now calculate the reference spacing for "Hello World" with no spacing.
+ ctx.letterSpacing = '0em';
+ width_normal = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 220, 0.1, "letter-spacing error after font change");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.measure.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.measure.html
new file mode 100644
index 0000000000..0e4848b6fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.letterSpacing.measure.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.letterSpacing.measure</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.letterSpacing.measure</h1>
+<p class="desc">Testing letter spacing with different length units</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing letter spacing with different length units");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ var width_normal = ctx.measureText('Hello World').width;
+
+ function test_letter_spacing(value, difference_spacing, epsilon) {
+ ctx.letterSpacing = value;
+ _assertSame(ctx.letterSpacing, value, "ctx.letterSpacing", "value");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ width_with_letter_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_letter_spacing, width_normal + difference_spacing, epsilon, "letter spacing doesn't work.");
+ }
+
+ // The first value is the letter Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 11 letters
+ // in 'hello world', so the length difference is always letterSpacing * 11.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 33, 0.1],
+ ['5px', 55, 0.1],
+ ['-2px', -22, 0.1],
+ ['1em', 110, 0.1],
+ ['-0.1em', -11, 0.1],
+ ['1in', 1056, 0.1],
+ ['-0.1cm', -41.65, 0.2],
+ ['-0.6mm', -24,95, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_letter_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.direction.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.direction.html
new file mode 100644
index 0000000000..abe696b196
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.direction.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.measure.direction</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.measure.direction</h1>
+<p class="desc">Measurement should follow text direction</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Measurement should follow text direction");
+_addTest(function(canvas, ctx) {
+
+ ctx.direction = "ltr";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+ ctx.direction = "rtl";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.rtl.text.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.rtl.text.html
new file mode 100644
index 0000000000..e521b09236
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.rtl.text.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.measure.rtl.text</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.measure.rtl.text</h1>
+<p class="desc">Measurement should follow canvas direction instead text direction</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Measurement should follow canvas direction instead text direction");
+_addTest(function(canvas, ctx) {
+
+ metrics = ctx.measureText('اَلْعَرَبÙيَّةÙ');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.textAlign.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.textAlign.html
new file mode 100644
index 0000000000..2d5a4718c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.measure.textAlign.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.measure.textAlign</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.measure.textAlign</h1>
+<p class="desc">Measurement should be related to textAlignment</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Measurement should be related to textAlignment");
+_addTest(function(canvas, ctx) {
+
+ ctx.textAlign = "right";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");
+
+ ctx.textAlign = "left"
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.nonfinite.spacing.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.nonfinite.spacing.html
new file mode 100644
index 0000000000..5ba20208e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.nonfinite.spacing.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.nonfinite.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.nonfinite.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with nonfinite inputs</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing letter spacing and word spacing with nonfinite inputs");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ }
+ test_word_spacing(NaN);
+ test_word_spacing(Infinity);
+ test_word_spacing(-Infinity);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.textRendering.settings.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.textRendering.settings.html
new file mode 100644
index 0000000000..dee856842a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.textRendering.settings.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.textRendering.settings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.textRendering.settings</h1>
+<p class="desc">Testing basic functionalities of textRendering in Canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing basic functionalities of textRendering in Canvas");
+_addTest(function(canvas, ctx) {
+
+ // Setting textRendering with correct case.
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizeSpeed";
+ _assertSame(ctx.textRendering, "optimizeSpeed", "ctx.textRendering", "\"optimizeSpeed\"");
+
+ ctx.textRendering = "optimizeLegibility";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "geometricPrecision";
+ _assertSame(ctx.textRendering, "geometricPrecision", "ctx.textRendering", "\"geometricPrecision\"");
+
+ ctx.textRendering = "auto";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ // Setting textRendering with incorrect case is ignored.
+ ctx.textRendering = "OPtimizeSpeed";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "OPtimizELEgibility";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "GeometricPrecision";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizespeed";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizelegibility";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "geometricprecision";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizeLegibility";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "AUTO";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "Auto";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ // Setting textRendering with non-existing font variant.
+ ctx.textRendering = "abcd";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "normal";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "auto";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.change.font.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.change.font.html
new file mode 100644
index 0000000000..8bad1a0447
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.change.font.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.wordSpacing.change.font</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.wordSpacing.change.font</h1>
+<p class="desc">Set word spacing and word spacing to font dependent value and verify it works after font change.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Set word spacing and word spacing to font dependent value and verify it works after font change.");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ // Get the width for 'Hello World, again' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ ctx.wordSpacing = '1em';
+ _assertSame(ctx.wordSpacing, '1em', "ctx.wordSpacing", "'1em'");
+ // 1em = 10px. Add 10px after each word in "Hello World, again",
+ // makes it 20px longer.
+ var width_with_spacing = ctx.measureText('Hello World, again').width;
+ _assertSame(width_with_spacing, width_normal + 20, "width_with_spacing", "width_normal + 20");
+
+ // Changing font to 20px. Without resetting the spacing, 1em wordSpacing
+ // is now 20px, so it's suppose to be 40px longer without any wordSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World, again').width;
+ // Now calculate the reference spacing for "Hello World, again" with no spacing.
+ ctx.wordSpacing = '0em';
+ width_normal = ctx.measureText('Hello World, again').width;
+ _assertSame(width_with_spacing, width_normal + 40, "width_with_spacing", "width_normal + 40");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.measure.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.measure.html
new file mode 100644
index 0000000000..2562477d94
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.drawing.style.wordSpacing.measure.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.drawing.style.wordSpacing.measure</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.drawing.style.wordSpacing.measure</h1>
+<p class="desc">Testing word spacing with different length units</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing word spacing with different length units");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ function test_word_spacing(value, difference_spacing, epsilon) {
+ ctx.wordSpacing = value;
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, value, "ctx.wordSpacing", "value");
+ width_with_word_spacing = ctx.measureText('Hello World, again').width;
+ assert_approx_equals(width_with_word_spacing, width_normal + difference_spacing, epsilon, "word spacing doesn't work.");
+ }
+
+ // The first value is the word Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 2 words
+ // in 'Hello World, again', so the length difference is always wordSpacing * 2.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 6, 0.1],
+ ['5px', 10, 0.1],
+ ['-2px', -4, 0.1],
+ ['1em', 20, 0.1],
+ ['-0.5em', -10, 0.1],
+ ['1in', 192, 0.1],
+ ['-0.1cm', -7.57, 0.2],
+ ['-0.6mm', -4.54, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_word_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.default.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.default.html
new file mode 100644
index 0000000000..c6ac084843
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.default.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ _assertSame(ctx.font, '10px sans-serif', "ctx.font", "'10px sans-serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.basic.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.basic.html
new file mode 100644
index 0000000000..70efec7743
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.basic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.basic</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = '20px serif';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20PX SERIF';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex.html
new file mode 100644
index 0000000000..12e1d60e56
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.complex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.complex</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = 'small-caps italic 400 12px/2 Unknown Font, sans-serif';
+ _assert(['italic small-caps 12px "Unknown Font", sans-serif', 'italic small-caps 12px Unknown Font, sans-serif'].includes(ctx.font), "['italic small-caps 12px \"Unknown Font\", sans-serif', 'italic small-caps 12px Unknown Font, sans-serif'].includes(ctx.font)");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex2.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex2.html
new file mode 100644
index 0000000000..d85765fcd3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.complex2.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.complex2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.complex2</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = 'small-caps italic 400 12px/2 "Unknown Font #2", sans-serif';
+ _assertSame(ctx.font, 'italic small-caps 12px "Unknown Font #2", sans-serif', "ctx.font", "'italic small-caps 12px \"Unknown Font #2\", sans-serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.family.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.family.html
new file mode 100644
index 0000000000..c53bedbffc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.family.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.family</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.family</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = '20px cursive,fantasy,monospace,sans-serif,serif,UnquotedFont,"QuotedFont\\\\\\","';
+ _assertSame(ctx.font, '20px cursive, fantasy, monospace, sans-serif, serif, UnquotedFont, "QuotedFont\\\\\\","', "ctx.font", "'20px cursive, fantasy, monospace, sans-serif, serif, UnquotedFont, \"QuotedFont\\\\\\\\\\\\\",\"'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.invalid.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.invalid.html
new file mode 100644
index 0000000000..4fef0b4846
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.invalid.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.invalid</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = '20px serif';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'bogus';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'inherit';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px {bogus}';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px initial';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px default';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px inherit';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px revert';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x)';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x, 10px serif)';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '1em serif; background: green; margin: 10px';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.default.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.default.html
new file mode 100644
index 0000000000..cb245f18a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.default.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.size.percentage.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.size.percentage.default</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.font = '1000% serif';
+ _assertSame(ctx2.font, '100px serif', "ctx2.font", "'100px serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.html
new file mode 100644
index 0000000000..520231a804
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.size.percentage.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.size.percentage</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.size.percentage</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"style="font-size: 144px"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = '50% serif';
+ _assertSame(ctx.font, '72px serif', "ctx.font", "'72px serif'");
+ canvas.setAttribute('style', 'font-size: 100px');
+ _assertSame(ctx.font, '72px serif', "ctx.font", "'72px serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.system.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.system.html
new file mode 100644
index 0000000000..10fa0cb36d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.system.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.system</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.system</h1>
+<p class="desc">System fonts must be computed to explicit values</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("System fonts must be computed to explicit values");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = 'message-box';
+ _assertDifferent(ctx.font, 'message-box', "ctx.font", "'message-box'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.tiny.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.tiny.html
new file mode 100644
index 0000000000..10c9d2a0e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.parse.tiny.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.parse.tiny</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.parse.tiny</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = '1px sans-serif';
+ _assertSame(ctx.font, '1px sans-serif', "ctx.font", "'1px sans-serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.relative_size.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.relative_size.html
new file mode 100644
index 0000000000..97b9718b14
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.relative_size.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.relative_size</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.relative_size</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.font = '1em sans-serif';
+ _assertSame(ctx2.font, '10px sans-serif', "ctx2.font", "'10px sans-serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.font.weight.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.weight.html
new file mode 100644
index 0000000000..05816b7254
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.font.weight.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.font.weight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.font.weight</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = 'italic 400 12px serif';
+ _assertSame(ctx.font, 'italic 12px serif', "ctx.font", "'italic 12px serif'");
+
+ ctx.font = 'italic 300 12px serif';
+ _assertSame(ctx.font, 'italic 300 12px serif', "ctx.font", "'italic 300 12px serif'");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1-expected.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1-expected.html
new file mode 100644
index 0000000000..e2cef0d77a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps1</title>
+<h1>2d.text.fontVariantCaps1</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1.html
new file mode 100644
index 0000000000..56acfc61f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps1-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps1</title>
+<h1>2d.text.fontVariantCaps1</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "small-caps";
+ // This should render the same as font = "small-caps 32px serif".
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps2.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps2.html
new file mode 100644
index 0000000000..56efbb6fd3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.fontVariantCaps2</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Testing small caps setting in fontVariant");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = "small-caps 32px serif";
+ // "mismatch" test, to verify that small-caps does change the rendering.
+ smallCaps_len = ctx.measureText("Hello World").width;
+
+ ctx.font = "32px serif";
+ normalCaps_len = ctx.measureText("Hello World").width;
+ _assert(smallCaps_len != normalCaps_len, "smallCaps_len != normalCaps_len");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3-expected.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3-expected.html
new file mode 100644
index 0000000000..cf2d5ae119
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps3</title>
+<h1>2d.text.fontVariantCaps3</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("hello world", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3.html
new file mode 100644
index 0000000000..c3d80d3e56
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps3.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps3-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps3</title>
+<h1>2d.text.fontVariantCaps3</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "all-small-caps";
+ // This should render the same as using font = "small-caps 32px serif"
+ // with all the underlying text in lowercase.
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4-expected.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4-expected.html
new file mode 100644
index 0000000000..3813fd3684
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps4</title>
+<h1>2d.text.fontVariantCaps4</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("hello world", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4.html
new file mode 100644
index 0000000000..1ee9053b4d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps4.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps4-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps4</title>
+<h1>2d.text.fontVariantCaps4</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps overrides the small-caps setting from the font attribute
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5-expected.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5-expected.html
new file mode 100644
index 0000000000..4bda4ec4b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps5</title>
+<h1>2d.text.fontVariantCaps5</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5.html
new file mode 100644
index 0000000000..d80de4ea31
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps5.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps5-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps5</title>
+<h1>2d.text.fontVariantCaps5</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps 'normal' does not override the setting from the font attribute.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "normal";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6-expected.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6-expected.html
new file mode 100644
index 0000000000..af9c736aea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps6</title>
+<h1>2d.text.fontVariantCaps6</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6.html
new file mode 100644
index 0000000000..c17fac18b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.fontVariantCaps6.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps6-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps6</title>
+<h1>2d.text.fontVariantCaps6</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ // fontVariantCaps is reset when the font attribute is set.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.actualBoundingBox.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.actualBoundingBox.html
new file mode 100644
index 0000000000..d0672b23dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.actualBoundingBox.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.actualBoundingBox</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.actualBoundingBox</h1>
+<p class="desc">Testing actualBoundingBox</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ ctx.baseline = 'alphabetic'
+ // Different platforms may render text slightly different.
+ // Values that are nominally expected to be zero might actually vary by a
+ // pixel or so if the UA accounts for antialiasing at glyph edges, so we
+ // allow a slight deviation.
+ _assert(Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1");
+ _assert(ctx.measureText('A').actualBoundingBoxRight >= 50, "ctx.measureText('A').actualBoundingBoxRight >= 50");
+ _assert(ctx.measureText('A').actualBoundingBoxAscent >= 35, "ctx.measureText('A').actualBoundingBoxAscent >= 35");
+ _assert(Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1");
+
+ _assert(ctx.measureText('D').actualBoundingBoxLeft >= 48, "ctx.measureText('D').actualBoundingBoxLeft >= 48");
+ _assert(ctx.measureText('D').actualBoundingBoxLeft <= 52, "ctx.measureText('D').actualBoundingBoxLeft <= 52");
+ _assert(ctx.measureText('D').actualBoundingBoxRight >= 75, "ctx.measureText('D').actualBoundingBoxRight >= 75");
+ _assert(ctx.measureText('D').actualBoundingBoxRight <= 80, "ctx.measureText('D').actualBoundingBoxRight <= 80");
+ _assert(ctx.measureText('D').actualBoundingBoxAscent >= 35, "ctx.measureText('D').actualBoundingBoxAscent >= 35");
+ _assert(ctx.measureText('D').actualBoundingBoxAscent <= 40, "ctx.measureText('D').actualBoundingBoxAscent <= 40");
+ _assert(ctx.measureText('D').actualBoundingBoxDescent >= 12, "ctx.measureText('D').actualBoundingBoxDescent >= 12");
+ _assert(ctx.measureText('D').actualBoundingBoxDescent <= 15, "ctx.measureText('D').actualBoundingBoxDescent <= 15");
+
+ _assert(Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37");
+
+}, "Testing actualBoundingBox");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.advances.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.advances.html
new file mode 100644
index 0000000000..84f04dd677
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.advances.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.advances</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.advances</h1>
+<p class="desc">Testing width advances</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ // Some platforms may return '-0'.
+ _assertSame(Math.abs(ctx.measureText('Hello').advances[0]), 0, "Math.abs(ctx.measureText('Hello').advances[\""+(0)+"\"])", "0");
+ // Different platforms may render text slightly different.
+ _assert(ctx.measureText('Hello').advances[1] >= 36, "ctx.measureText('Hello').advances[\""+(1)+"\"] >= 36");
+ _assert(ctx.measureText('Hello').advances[2] >= 58, "ctx.measureText('Hello').advances[\""+(2)+"\"] >= 58");
+ _assert(ctx.measureText('Hello').advances[3] >= 70, "ctx.measureText('Hello').advances[\""+(3)+"\"] >= 70");
+ _assert(ctx.measureText('Hello').advances[4] >= 80, "ctx.measureText('Hello').advances[\""+(4)+"\"] >= 80");
+
+ var tm = ctx.measureText('Hello');
+ _assertSame(ctx.measureText('Hello').advances[0], tm.advances[0], "ctx.measureText('Hello').advances[\""+(0)+"\"]", "tm.advances[\""+(0)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[1], tm.advances[1], "ctx.measureText('Hello').advances[\""+(1)+"\"]", "tm.advances[\""+(1)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[2], tm.advances[2], "ctx.measureText('Hello').advances[\""+(2)+"\"]", "tm.advances[\""+(2)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[3], tm.advances[3], "ctx.measureText('Hello').advances[\""+(3)+"\"]", "tm.advances[\""+(3)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[4], tm.advances[4], "ctx.measureText('Hello').advances[\""+(4)+"\"]", "tm.advances[\""+(4)+"\"]");
+
+}, "Testing width advances");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.baselines.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.baselines.html
new file mode 100644
index 0000000000..9d947eb361
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.baselines.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.baselines</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.baselines</h1>
+<p class="desc">Testing baselines</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(Math.abs(ctx.measureText('A').alphabeticBaseline), 0, "Math.abs(ctx.measureText('A').alphabeticBaseline)", "0");
+ _assertSame(ctx.measureText('A').ideographicBaseline, 6.25, "ctx.measureText('A').ideographicBaseline", "6.25");
+ _assertSame(ctx.measureText('A').hangingBaseline, 25, "ctx.measureText('A').hangingBaseline", "25");
+
+ _assertSame(Math.abs(ctx.measureText('ABCD').alphabeticBaseline), 0, "Math.abs(ctx.measureText('ABCD').alphabeticBaseline)", "0");
+ _assertSame(ctx.measureText('ABCD').ideographicBaseline, 6.25, "ctx.measureText('ABCD').ideographicBaseline", "6.25");
+ _assertSame(ctx.measureText('ABCD').hangingBaseline, 25, "ctx.measureText('ABCD').hangingBaseline", "25");
+
+}, "Testing baselines");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-low-ascent.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-low-ascent.html
new file mode 100644
index 0000000000..7b6874d10b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-low-ascent.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.emHeights-low-ascent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest-ascent256;
+ src: url("/fonts/CanvasTest-ascent256.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.emHeights-low-ascent</h1>
+<p class="desc">Testing emHeights with reduced ascent metric</p>
+
+
+<span style="font-family: CanvasTest-ascent256; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 20, "ctx.measureText('A').emHeightAscent", "20");
+ _assertSame(ctx.measureText('A').emHeightDescent, 20, "ctx.measureText('A').emHeightDescent", "20");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 20, "ctx.measureText('ABCD').emHeightAscent", "20");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 20, "ctx.measureText('ABCD').emHeightDescent", "20");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+
+}, "Testing emHeights with reduced ascent metric");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-zero-descent.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-zero-descent.html
new file mode 100644
index 0000000000..c3ce994fa1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights-zero-descent.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.emHeights-zero-descent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest-descent0;
+ src: url("/fonts/CanvasTest-descent0.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.emHeights-zero-descent</h1>
+<p class="desc">Testing emHeights with zero descent metric</p>
+
+
+<span style="font-family: CanvasTest-descent0; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightAscent", "40");
+ _assertSame(ctx.measureText('A').emHeightDescent, 0, "ctx.measureText('A').emHeightDescent", "0");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightAscent", "40");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 0, "ctx.measureText('ABCD').emHeightDescent", "0");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+
+}, "Testing emHeights with zero descent metric");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights.html
new file mode 100644
index 0000000000..0db4c30801
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.emHeights.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.emHeights</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.emHeights</h1>
+<p class="desc">Testing emHeights</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 30, "ctx.measureText('A').emHeightAscent", "30");
+ _assertSame(ctx.measureText('A').emHeightDescent, 10, "ctx.measureText('A').emHeightDescent", "10");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 30, "ctx.measureText('ABCD').emHeightAscent", "30");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 10, "ctx.measureText('ABCD').emHeightDescent", "10");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+
+}, "Testing emHeights");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-reduced-ascent.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-reduced-ascent.html
new file mode 100644
index 0000000000..653d26d820
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-reduced-ascent.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.fontBoundingBox-reduced-ascent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest-ascent256;
+ src: url("/fonts/CanvasTest-ascent256.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.fontBoundingBox-reduced-ascent</h1>
+<p class="desc">Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric</p>
+
+
+<span style="font-family: CanvasTest-ascent256; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 10, "ctx.measureText('A').fontBoundingBoxAscent", "10");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 10, "ctx.measureText('ABCD').fontBoundingBoxAscent", "10");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+
+}, "Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-zero-descent.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-zero-descent.html
new file mode 100644
index 0000000000..785c0bc186
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox-zero-descent.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.fontBoundingBox-zero-descent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest-descent0;
+ src: url("/fonts/CanvasTest-descent0.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.fontBoundingBox-zero-descent</h1>
+<p class="desc">Testing fontBoundingBox for OffscreenCanvas with zero descent metric</p>
+
+
+<span style="font-family: CanvasTest-descent0; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 30, "ctx.measureText('A').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 0, "ctx.measureText('A').fontBoundingBoxDescent", "0");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 0, "ctx.measureText('ABCD').fontBoundingBoxDescent", "0");
+
+}, "Testing fontBoundingBox for OffscreenCanvas with zero descent metric");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.ahem.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.ahem.html
new file mode 100644
index 0000000000..0044f4d1e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.ahem.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.fontBoundingBox.ahem</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: Ahem;
+ src: url("/fonts/Ahem.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.fontBoundingBox.ahem</h1>
+<p class="desc">Testing fontBoundingBox for font ahem</p>
+
+
+<span style="font-family: Ahem; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px Ahem';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 40, "ctx.measureText('A').fontBoundingBoxAscent", "40");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 40, "ctx.measureText('ABCD').fontBoundingBoxAscent", "40");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+
+}, "Testing fontBoundingBox for font ahem");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.html
new file mode 100644
index 0000000000..9e7e190754
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.fontBoundingBox.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.fontBoundingBox</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.fontBoundingBox</h1>
+<p class="desc">Testing fontBoundingBox measurements</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 30, "ctx.measureText('A').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+
+}, "Testing fontBoundingBox measurements");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.basic.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.basic.html
new file mode 100644
index 0000000000..36dc468b4e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.basic.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.width.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.width.basic</h1>
+<p class="desc">The width of character is same as font used</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText('A').width, 50, "ctx.measureText('A').width", "50");
+ _assertSame(ctx.measureText('AA').width, 100, "ctx.measureText('AA').width", "100");
+ _assertSame(ctx.measureText('ABCD').width, 200, "ctx.measureText('ABCD').width", "200");
+
+ ctx.font = '100px CanvasTest';
+ _assertSame(ctx.measureText('A').width, 100, "ctx.measureText('A').width", "100");
+
+}, "The width of character is same as font used");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.empty.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.empty.html
new file mode 100644
index 0000000000..39ddad9789
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.empty.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.width.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.width.empty</h1>
+<p class="desc">The empty string has zero width</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText("").width, 0, "ctx.measureText(\"\").width", "0");
+
+}, "The empty string has zero width");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.space.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.space.html
new file mode 100644
index 0000000000..7fd8095e28
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.measure.width.space.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.measure.width.space</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<style>
+@font-face {
+ font-family: CanvasTest;
+ src: url("/fonts/CanvasTest.ttf");
+}
+</style>
+<body class="show_output">
+
+<h1>2d.text.measure.width.space</h1>
+<p class="desc">Space characters are converted to U+0020 and NOT collapsed</p>
+
+
+<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span>
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText('A B').width, 150, "ctx.measureText('A B').width", "150");
+ _assertSame(ctx.measureText('A B').width, 200, "ctx.measureText('A B').width", "200");
+ _assertSame(ctx.measureText('A \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dB').width, 650, "ctx.measureText('A \\x09\\x0a\\x0c\\x0d \\x09\\x0a\\x0c\\x0dB').width", "650");
+ _assert(ctx.measureText('A \x0b B').width >= 200, "ctx.measureText('A \\x0b B').width >= 200");
+
+ _assertSame(ctx.measureText(' AB').width, 150, "ctx.measureText(' AB').width", "150");
+ _assertSame(ctx.measureText('AB ').width, 150, "ctx.measureText('AB ').width", "150");
+
+}, "Space characters are converted to U+0020 and NOT collapsed");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/2d.text.setFont.mathFont.html b/testing/web-platform/tests/html/canvas/element/text/2d.text.setFont.mathFont.html
new file mode 100644
index 0000000000..370104f1b8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/2d.text.setFont.mathFont.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.setFont.mathFont</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.text.setFont.mathFont</h1>
+<p class="desc">crbug.com/1212190, make sure offscreencanvas doesn't crash with Math Font</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("crbug.com/1212190, make sure offscreencanvas doesn't crash with Math Font");
+_addTest(function(canvas, ctx) {
+
+ ctx.font = "math serif";
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/direction-inherit-rtl.html b/testing/web-platform/tests/html/canvas/element/text/direction-inherit-rtl.html
new file mode 100644
index 0000000000..0ad92181a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/direction-inherit-rtl.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Canvas Test: the 'direction' property</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#text-styles">
+<link rel="match" href="reference/direction-rtl-ref.html">
+<meta name="assert" content="text rendering respects the direction property">
+<style>
+canvas {
+ margin: 10px;
+ border: 1px solid gray;
+}
+</style>
+
+<canvas id="c" width="300" height="300" dir="rtl"></canvas>
+
+<script>
+let ctx = c.getContext("2d");
+ctx.direction = "inherit";
+ctx.font = "16px sans-serif";
+ctx.textAlign = "left";
+ctx.fillText("Hello World!", 150, 50);
+ctx.textAlign = "right";
+ctx.fillText("Hello World!", 150, 100);
+ctx.textAlign = "start";
+ctx.fillText("Hello World!", 150, 150);
+ctx.textAlign = "end";
+ctx.fillText("Hello World!", 150, 200);
+ctx.textAlign = "center";
+ctx.fillText("Hello World!", 150, 250);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/direction-ltr.html b/testing/web-platform/tests/html/canvas/element/text/direction-ltr.html
new file mode 100644
index 0000000000..42a39ac589
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/direction-ltr.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Canvas Test: the 'direction' property</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#text-styles">
+<link rel="match" href="reference/direction-default-ref.html">
+<meta name="assert" content="text rendering respects the direction property">
+<style>
+canvas {
+ margin: 10px;
+ border: 1px solid gray;
+}
+</style>
+
+<canvas id="c" width="300" height="300" dir="rtl"></canvas>
+
+<script>
+let ctx = c.getContext("2d");
+ctx.direction = "ltr";
+ctx.font = "16px sans-serif";
+ctx.textAlign = "left";
+ctx.fillText("Hello World!", 150, 50);
+ctx.textAlign = "right";
+ctx.fillText("Hello World!", 150, 100);
+ctx.textAlign = "start";
+ctx.fillText("Hello World!", 150, 150);
+ctx.textAlign = "end";
+ctx.fillText("Hello World!", 150, 200);
+ctx.textAlign = "center";
+ctx.fillText("Hello World!", 150, 250);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/direction-rtl.html b/testing/web-platform/tests/html/canvas/element/text/direction-rtl.html
new file mode 100644
index 0000000000..3cc67c69f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/direction-rtl.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Canvas Test: the 'direction' property</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#text-styles">
+<link rel="match" href="reference/direction-rtl-ref.html">
+<meta name="assert" content="text rendering respects the direction property">
+<style>
+canvas {
+ margin: 10px;
+ border: 1px solid gray;
+}
+</style>
+
+<canvas id="c" width="300" height="300"></canvas>
+
+<script>
+let ctx = c.getContext("2d");
+ctx.direction = "rtl";
+ctx.font = "16px sans-serif";
+ctx.textAlign = "left";
+ctx.fillText("Hello World!", 150, 50);
+ctx.textAlign = "right";
+ctx.fillText("Hello World!", 150, 100);
+ctx.textAlign = "start";
+ctx.fillText("Hello World!", 150, 150);
+ctx.textAlign = "end";
+ctx.fillText("Hello World!", 150, 200);
+ctx.textAlign = "center";
+ctx.fillText("Hello World!", 150, 250);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/parent-style-relative-units.html b/testing/web-platform/tests/html/canvas/element/text/parent-style-relative-units.html
new file mode 100644
index 0000000000..b9a6b314c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/parent-style-relative-units.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Canvas test: CanvasTextDrawingStyles.font with canvas relative units</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-font">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<canvas id="canvas" style="font-size: 30px; line-height: 40px"></canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext("2d");
+
+ test(() => {
+ ctx.font = "2em serif";
+ assert_equals(ctx.font, "60px serif");
+ }, "Font-size based on canvas element font-size");
+
+ test(() => {
+ // Line-height should be forced to normal, but also irrelevant for resolving
+ // lh-units for font-size.
+ ctx.font = "2lh/100 serif";
+ assert_equals(ctx.font, "80px serif");
+ }, "Font-size based on canvas element line-height");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/text/reference/direction-default-ref.html b/testing/web-platform/tests/html/canvas/element/text/reference/direction-default-ref.html
new file mode 100644
index 0000000000..cef6df259c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/reference/direction-default-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Canvas Reference: the 'direction' property</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<style>
+canvas {
+ margin: 10px;
+ border: 1px solid gray;
+}
+</style>
+
+<canvas id="c" width="300" height="300"></canvas>
+
+<script>
+let ctx = c.getContext("2d");
+ctx.font = "16px sans-serif";
+ctx.textAlign = "left";
+ctx.fillText("Hello World!", 150, 50);
+ctx.textAlign = "right";
+ctx.fillText("Hello World!", 150, 100);
+ctx.textAlign = "start";
+ctx.fillText("Hello World!", 150, 150);
+ctx.textAlign = "end";
+ctx.fillText("Hello World!", 150, 200);
+ctx.textAlign = "center";
+ctx.fillText("Hello World!", 150, 250);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/text/reference/direction-rtl-ref.html b/testing/web-platform/tests/html/canvas/element/text/reference/direction-rtl-ref.html
new file mode 100644
index 0000000000..010526d667
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/text/reference/direction-rtl-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Canvas Reference: the 'direction' property</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<style>
+canvas {
+ margin: 10px;
+ border: 1px solid gray;
+}
+</style>
+
+<canvas id="c" width="300" height="300"></canvas>
+
+<script>
+let ctx = c.getContext("2d");
+ctx.font = "16px sans-serif";
+ctx.textAlign = "left";
+ctx.fillText("!Hello World", 150, 50);
+ctx.textAlign = "right";
+ctx.fillText("!Hello World", 150, 100);
+ctx.textAlign = "right";
+ctx.fillText("!Hello World", 150, 150);
+ctx.textAlign = "left";
+ctx.fillText("!Hello World", 150, 200);
+ctx.textAlign = "center";
+ctx.fillText("!Hello World", 150, 250);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.bitmap.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.bitmap.html
new file mode 100644
index 0000000000..6814679aba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.bitmap.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.bitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.bitmap</h1>
+<p class="desc">save()/restore() does not affect the current bitmap</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() does not affect the current bitmap");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.clip.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.clip.html
new file mode 100644
index 0000000000..1ce44ab147
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.clip.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.clip</h1>
+<p class="desc">save()/restore() affects the clipping path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() affects the clipping path");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 1, 1);
+ ctx.clip();
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.fillStyle.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.fillStyle.html
new file mode 100644
index 0000000000..1eb8360e5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.fillStyle.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.fillStyle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.fillStyle</h1>
+<p class="desc">save()/restore() works for fillStyle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for fillStyle");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.fillStyle;
+ ctx.save();
+ ctx.fillStyle = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.fillStyle, old, "ctx.fillStyle", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.fillStyle = "#ff0000";
+ old = ctx.fillStyle;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.fillStyle, old, "ctx.fillStyle", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html
new file mode 100644
index 0000000000..8cb4d171bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.font</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.font</h1>
+<p class="desc">save()/restore() works for font</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for font");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.font;
+ ctx.save();
+ ctx.font = "25px serif";
+ ctx.restore();
+ _assertSame(ctx.font, old, "ctx.font", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.font = "25px serif";
+ old = ctx.font;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "25px serif"
+ ctx.save();
+ _assertSame(ctx.font, old, "ctx.font", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalAlpha.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalAlpha.html
new file mode 100644
index 0000000000..f3dae5ff51
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalAlpha.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.globalAlpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.globalAlpha</h1>
+<p class="desc">save()/restore() works for globalAlpha</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for globalAlpha");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.globalAlpha;
+ ctx.save();
+ ctx.globalAlpha = 0.5;
+ ctx.restore();
+ _assertSame(ctx.globalAlpha, old, "ctx.globalAlpha", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.globalAlpha = 0.5;
+ old = ctx.globalAlpha;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.globalAlpha, old, "ctx.globalAlpha", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html
new file mode 100644
index 0000000000..fed0ceec3b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.globalCompositeOperation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.globalCompositeOperation</h1>
+<p class="desc">save()/restore() works for globalCompositeOperation</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for globalCompositeOperation");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.globalCompositeOperation;
+ ctx.save();
+ ctx.globalCompositeOperation = "copy";
+ ctx.restore();
+ _assertSame(ctx.globalCompositeOperation, old, "ctx.globalCompositeOperation", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.globalCompositeOperation = "copy";
+ old = ctx.globalCompositeOperation;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "copy"
+ ctx.save();
+ _assertSame(ctx.globalCompositeOperation, old, "ctx.globalCompositeOperation", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineCap.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineCap.html
new file mode 100644
index 0000000000..90c52ce564
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineCap.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.lineCap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.lineCap</h1>
+<p class="desc">save()/restore() works for lineCap</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for lineCap");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineCap;
+ ctx.save();
+ ctx.lineCap = "round";
+ ctx.restore();
+ _assertSame(ctx.lineCap, old, "ctx.lineCap", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineCap = "round";
+ old = ctx.lineCap;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "round"
+ ctx.save();
+ _assertSame(ctx.lineCap, old, "ctx.lineCap", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineJoin.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineJoin.html
new file mode 100644
index 0000000000..00495d9fb6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineJoin.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.lineJoin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.lineJoin</h1>
+<p class="desc">save()/restore() works for lineJoin</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for lineJoin");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineJoin;
+ ctx.save();
+ ctx.lineJoin = "round";
+ ctx.restore();
+ _assertSame(ctx.lineJoin, old, "ctx.lineJoin", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineJoin = "round";
+ old = ctx.lineJoin;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "round"
+ ctx.save();
+ _assertSame(ctx.lineJoin, old, "ctx.lineJoin", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineWidth.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineWidth.html
new file mode 100644
index 0000000000..0705878d0a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.lineWidth.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.lineWidth</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.lineWidth</h1>
+<p class="desc">save()/restore() works for lineWidth</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for lineWidth");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineWidth;
+ ctx.save();
+ ctx.lineWidth = 0.5;
+ ctx.restore();
+ _assertSame(ctx.lineWidth, old, "ctx.lineWidth", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineWidth = 0.5;
+ old = ctx.lineWidth;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.lineWidth, old, "ctx.lineWidth", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.miterLimit.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.miterLimit.html
new file mode 100644
index 0000000000..efd2207515
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.miterLimit.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.miterLimit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.miterLimit</h1>
+<p class="desc">save()/restore() works for miterLimit</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for miterLimit");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.miterLimit;
+ ctx.save();
+ ctx.miterLimit = 0.5;
+ ctx.restore();
+ _assertSame(ctx.miterLimit, old, "ctx.miterLimit", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.miterLimit = 0.5;
+ old = ctx.miterLimit;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.miterLimit, old, "ctx.miterLimit", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.path.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.path.html
new file mode 100644
index 0000000000..5848ae560c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.path.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.path</h1>
+<p class="desc">save()/restore() does not affect the current path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() does not affect the current path");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 100, 50);
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowBlur.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowBlur.html
new file mode 100644
index 0000000000..c5699b78fd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowBlur.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.shadowBlur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.shadowBlur</h1>
+<p class="desc">save()/restore() works for shadowBlur</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for shadowBlur");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowBlur;
+ ctx.save();
+ ctx.shadowBlur = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowBlur, old, "ctx.shadowBlur", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowBlur = 5;
+ old = ctx.shadowBlur;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowBlur, old, "ctx.shadowBlur", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowColor.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowColor.html
new file mode 100644
index 0000000000..9b6a435202
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowColor.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.shadowColor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.shadowColor</h1>
+<p class="desc">save()/restore() works for shadowColor</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for shadowColor");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowColor;
+ ctx.save();
+ ctx.shadowColor = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.shadowColor, old, "ctx.shadowColor", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowColor = "#ff0000";
+ old = ctx.shadowColor;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.shadowColor, old, "ctx.shadowColor", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetX.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetX.html
new file mode 100644
index 0000000000..427a44a373
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetX.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.shadowOffsetX</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.shadowOffsetX</h1>
+<p class="desc">save()/restore() works for shadowOffsetX</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for shadowOffsetX");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowOffsetX;
+ ctx.save();
+ ctx.shadowOffsetX = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowOffsetX, old, "ctx.shadowOffsetX", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowOffsetX = 5;
+ old = ctx.shadowOffsetX;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowOffsetX, old, "ctx.shadowOffsetX", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetY.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetY.html
new file mode 100644
index 0000000000..b711445c20
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.shadowOffsetY.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.shadowOffsetY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.shadowOffsetY</h1>
+<p class="desc">save()/restore() works for shadowOffsetY</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for shadowOffsetY");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowOffsetY;
+ ctx.save();
+ ctx.shadowOffsetY = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowOffsetY, old, "ctx.shadowOffsetY", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowOffsetY = 5;
+ old = ctx.shadowOffsetY;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowOffsetY, old, "ctx.shadowOffsetY", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stack.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stack.html
new file mode 100644
index 0000000000..122c543473
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stack.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.stack</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.stack</h1>
+<p class="desc">save()/restore() can be nested as a stack</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() can be nested as a stack");
+_addTest(function(canvas, ctx) {
+
+ ctx.lineWidth = 1;
+ ctx.save();
+ ctx.lineWidth = 2;
+ ctx.save();
+ ctx.lineWidth = 3;
+ _assertSame(ctx.lineWidth, 3, "ctx.lineWidth", "3");
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 2, "ctx.lineWidth", "2");
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stackdepth.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stackdepth.html
new file mode 100644
index 0000000000..6fa591d06a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.stackdepth.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.stackdepth</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.stackdepth</h1>
+<p class="desc">save()/restore() stack depth is not unreasonably limited</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() stack depth is not unreasonably limited");
+_addTest(function(canvas, ctx) {
+
+ var limit = 512;
+ for (var i = 1; i < limit; ++i)
+ {
+ ctx.save();
+ ctx.lineWidth = i;
+ }
+ for (var i = limit-1; i > 0; --i)
+ {
+ _assertSame(ctx.lineWidth, i, "ctx.lineWidth", "i");
+ ctx.restore();
+ }
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.strokeStyle.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.strokeStyle.html
new file mode 100644
index 0000000000..9ccaac7e7a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.strokeStyle.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.strokeStyle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.strokeStyle</h1>
+<p class="desc">save()/restore() works for strokeStyle</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for strokeStyle");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.strokeStyle;
+ ctx.save();
+ ctx.strokeStyle = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.strokeStyle, old, "ctx.strokeStyle", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.strokeStyle = "#ff0000";
+ old = ctx.strokeStyle;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.strokeStyle, old, "ctx.strokeStyle", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html
new file mode 100644
index 0000000000..a86d62a8b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.textAlign</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.textAlign</h1>
+<p class="desc">save()/restore() works for textAlign</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for textAlign");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.textAlign;
+ ctx.save();
+ ctx.textAlign = "center";
+ ctx.restore();
+ _assertSame(ctx.textAlign, old, "ctx.textAlign", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.textAlign = "center";
+ old = ctx.textAlign;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "center"
+ ctx.save();
+ _assertSame(ctx.textAlign, old, "ctx.textAlign", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html
new file mode 100644
index 0000000000..5cbf712fb1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.textBaseline</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.textBaseline</h1>
+<p class="desc">save()/restore() works for textBaseline</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() works for textBaseline");
+_addTest(function(canvas, ctx) {
+
+ // Test that restore() undoes any modifications
+ var old = ctx.textBaseline;
+ ctx.save();
+ ctx.textBaseline = "bottom";
+ ctx.restore();
+ _assertSame(ctx.textBaseline, old, "ctx.textBaseline", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.textBaseline = "bottom";
+ old = ctx.textBaseline;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "bottom"
+ ctx.save();
+ _assertSame(ctx.textBaseline, old, "ctx.textBaseline", "old");
+ ctx.restore();
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.transformation.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.transformation.html
new file mode 100644
index 0000000000..6edee8ae84
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.transformation.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.transformation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.transformation</h1>
+<p class="desc">save()/restore() affects the current transformation matrix</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("save()/restore() affects the current transformation matrix");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.translate(200, 0);
+ ctx.restore();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(-200, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.underflow.html b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.underflow.html
new file mode 100644
index 0000000000..763f269556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/the-canvas-state/2d.state.saverestore.underflow.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.state.saverestore.underflow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.state.saverestore.underflow</h1>
+<p class="desc">restore() with an empty stack has no effect</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("restore() with an empty stack has no effect");
+_addTest(function(canvas, ctx) {
+
+ for (var i = 0; i < 16; ++i)
+ ctx.restore();
+ ctx.lineWidth = 0.5;
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 0.5, "ctx.lineWidth", "0.5");
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.order.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.order.html
new file mode 100644
index 0000000000..6c97bf7426
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.order.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.order</h1>
+<p class="desc">Transformations are applied in the right order</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Transformations are applied in the right order");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 1);
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -50, 50, 50);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.direction.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.direction.html
new file mode 100644
index 0000000000..0b583cd678
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.direction.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.rotate.direction</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.rotate.direction</h1>
+<p class="desc">rotate() is clockwise</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("rotate() is clockwise");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -100, 50, 100);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.nonfinite.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.nonfinite.html
new file mode 100644
index 0000000000..3e685fa7a8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.nonfinite.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.rotate.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.rotate.nonfinite</h1>
+<p class="desc">rotate() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("rotate() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.rotate(Infinity);
+ ctx.rotate(-Infinity);
+ ctx.rotate(NaN);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.radians.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.radians.html
new file mode 100644
index 0000000000..63f895a0de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.radians.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.rotate.radians</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.rotate.radians</h1>
+<p class="desc">rotate() uses radians</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("rotate() uses radians");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI); // should fail obviously if this is 3.1 degrees
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrap.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrap.html
new file mode 100644
index 0000000000..9b923e0008
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrap.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.rotate.wrap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.rotate.wrap</h1>
+<p class="desc">rotate() wraps large positive values correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("rotate() wraps large positive values correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI * (1 + 4096)); // == pi (mod 2*pi)
+ // We need about pi +/- 0.001 in order to get correct-looking results
+ // 32-bit floats can store pi*4097 with precision 2^-10, so that should
+ // be safe enough on reasonable implementations
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,2, 0,255,0,255);
+ _assertPixel(canvas, 98,47, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrapnegative.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrapnegative.html
new file mode 100644
index 0000000000..96894ef20e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.wrapnegative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.rotate.wrapnegative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.rotate.wrapnegative</h1>
+<p class="desc">rotate() wraps large negative values correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("rotate() wraps large negative values correctly");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(-Math.PI * (1 + 4096));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,2, 0,255,0,255);
+ _assertPixel(canvas, 98,47, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.zero.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.zero.html
new file mode 100644
index 0000000000..7026ea9d1b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.rotate.zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.rotate.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.rotate.zero</h1>
+<p class="desc">rotate() by 0 does nothing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("rotate() by 0 does nothing");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.basic.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.basic.html
new file mode 100644
index 0000000000..7e4b3593b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.basic.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.scale.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.scale.basic</h1>
+<p class="desc">scale() works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("scale() works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 4);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 12.5);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.large.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.large.html
new file mode 100644
index 0000000000..ce7f6d9fae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.large.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.scale.large</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.scale.large</h1>
+<p class="desc">scale() with large scale factors works</p>
+
+<p class="notes">Not really that large at all, but it hits the limits in Firefox.
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("scale() with large scale factors works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(1e5, 1e5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 1, 1);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.multiple.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.multiple.html
new file mode 100644
index 0000000000..73bfbc87c1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.multiple.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.scale.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.scale.multiple</h1>
+<p class="desc">Multiple scale()s combine</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Multiple scale()s combine");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.negative.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.negative.html
new file mode 100644
index 0000000000..a5aef69b63
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.negative.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.scale.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.scale.negative</h1>
+<p class="desc">scale() with negative scale factors works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("scale() with negative scale factors works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.scale(-1, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.scale(1, -1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, -50, 50, 50);
+ ctx.restore();
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.nonfinite.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.nonfinite.html
new file mode 100644
index 0000000000..c06f96eb22
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.nonfinite.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.scale.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.scale.nonfinite</h1>
+<p class="desc">scale() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("scale() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.scale(Infinity, 0.1);
+ ctx.scale(-Infinity, 0.1);
+ ctx.scale(NaN, 0.1);
+ ctx.scale(0.1, Infinity);
+ ctx.scale(0.1, -Infinity);
+ ctx.scale(0.1, NaN);
+ ctx.scale(Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.zero.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.zero.html
new file mode 100644
index 0000000000..ae15034a19
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.scale.zero.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.scale.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.scale.zero</h1>
+<p class="desc">scale() with a scale factor of zero works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("scale() with a scale factor of zero works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.scale(0, 1);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.translate(0, 25);
+ ctx.scale(1, 0);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.multiple.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.multiple.html
new file mode 100644
index 0000000000..099831e638
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.multiple.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.setTransform.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.setTransform.multiple</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.setTransform(1/2,0, 0,1/2, 0,0);
+ ctx.setTransform();
+ ctx.setTransform(2,0, 0,2, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ _assertPixel(canvas, 75,35, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.nonfinite.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.nonfinite.html
new file mode 100644
index 0000000000..019493ad16
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.nonfinite.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.setTransform.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.setTransform.nonfinite</h1>
+<p class="desc">setTransform() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("setTransform() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.setTransform(Infinity, 0, 0, 0, 0, 0);
+ ctx.setTransform(-Infinity, 0, 0, 0, 0, 0);
+ ctx.setTransform(NaN, 0, 0, 0, 0, 0);
+ ctx.setTransform(0, Infinity, 0, 0, 0, 0);
+ ctx.setTransform(0, -Infinity, 0, 0, 0, 0);
+ ctx.setTransform(0, NaN, 0, 0, 0, 0);
+ ctx.setTransform(0, 0, Infinity, 0, 0, 0);
+ ctx.setTransform(0, 0, -Infinity, 0, 0, 0);
+ ctx.setTransform(0, 0, NaN, 0, 0, 0);
+ ctx.setTransform(0, 0, 0, Infinity, 0, 0);
+ ctx.setTransform(0, 0, 0, -Infinity, 0, 0);
+ ctx.setTransform(0, 0, 0, NaN, 0, 0);
+ ctx.setTransform(0, 0, 0, 0, Infinity, 0);
+ ctx.setTransform(0, 0, 0, 0, -Infinity, 0);
+ ctx.setTransform(0, 0, 0, 0, NaN, 0);
+ ctx.setTransform(0, 0, 0, 0, 0, Infinity);
+ ctx.setTransform(0, 0, 0, 0, 0, -Infinity);
+ ctx.setTransform(0, 0, 0, 0, 0, NaN);
+ ctx.setTransform(Infinity, Infinity, 0, 0, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, 0, 0);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, 0, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, 0, 0, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, 0, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, 0, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, Infinity, 0);
+ ctx.setTransform(Infinity, 0, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, 0, Infinity);
+ ctx.setTransform(Infinity, 0, 0, Infinity, 0, 0);
+ ctx.setTransform(Infinity, 0, 0, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, 0, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, 0, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, 0, 0, 0, Infinity, 0);
+ ctx.setTransform(Infinity, 0, 0, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, 0, 0, 0, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, 0, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, 0, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, Infinity, 0);
+ ctx.setTransform(0, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, 0, Infinity);
+ ctx.setTransform(0, Infinity, 0, Infinity, 0, 0);
+ ctx.setTransform(0, Infinity, 0, Infinity, Infinity, 0);
+ ctx.setTransform(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.setTransform(0, Infinity, 0, 0, Infinity, 0);
+ ctx.setTransform(0, Infinity, 0, 0, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, 0, 0, 0, Infinity);
+ ctx.setTransform(0, 0, Infinity, Infinity, 0, 0);
+ ctx.setTransform(0, 0, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(0, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, 0, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(0, 0, Infinity, 0, Infinity, 0);
+ ctx.setTransform(0, 0, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(0, 0, Infinity, 0, 0, Infinity);
+ ctx.setTransform(0, 0, 0, Infinity, Infinity, 0);
+ ctx.setTransform(0, 0, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, 0, 0, Infinity, 0, Infinity);
+ ctx.setTransform(0, 0, 0, 0, Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.skewed.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.skewed.html
new file mode 100644
index 0000000000..f6f8655b1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.setTransform.skewed.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.setTransform.skewed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.setTransform.skewed</h1>
+<p class="desc"></p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.setTransform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ _assertPixel(canvas, 21,11, 0,255,0,255);
+ _assertPixel(canvas, 79,11, 0,255,0,255);
+ _assertPixel(canvas, 21,39, 0,255,0,255);
+ _assertPixel(canvas, 79,39, 0,255,0,255);
+ _assertPixel(canvas, 39,19, 0,255,0,255);
+ _assertPixel(canvas, 61,19, 0,255,0,255);
+ _assertPixel(canvas, 39,31, 0,255,0,255);
+ _assertPixel(canvas, 61,31, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.identity.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.identity.html
new file mode 100644
index 0000000000..be5de19a45
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.identity.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.transform.identity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.transform.identity</h1>
+<p class="desc">transform() with the identity matrix does nothing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("transform() with the identity matrix does nothing");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,0, 0,1, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.multiply.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.multiply.html
new file mode 100644
index 0000000000..1e8e4cccdf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.multiply.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.transform.multiply</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.transform.multiply</h1>
+<p class="desc">transform() multiplies the CTM</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("transform() multiplies the CTM");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,2, 3,4, 5,6);
+ ctx.transform(-2,1, 3/2,-1/2, 1,-2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.nonfinite.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.nonfinite.html
new file mode 100644
index 0000000000..d3fae8520c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.nonfinite.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.transform.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.transform.nonfinite</h1>
+<p class="desc">transform() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("transform() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.transform(Infinity, 0, 0, 0, 0, 0);
+ ctx.transform(-Infinity, 0, 0, 0, 0, 0);
+ ctx.transform(NaN, 0, 0, 0, 0, 0);
+ ctx.transform(0, Infinity, 0, 0, 0, 0);
+ ctx.transform(0, -Infinity, 0, 0, 0, 0);
+ ctx.transform(0, NaN, 0, 0, 0, 0);
+ ctx.transform(0, 0, Infinity, 0, 0, 0);
+ ctx.transform(0, 0, -Infinity, 0, 0, 0);
+ ctx.transform(0, 0, NaN, 0, 0, 0);
+ ctx.transform(0, 0, 0, Infinity, 0, 0);
+ ctx.transform(0, 0, 0, -Infinity, 0, 0);
+ ctx.transform(0, 0, 0, NaN, 0, 0);
+ ctx.transform(0, 0, 0, 0, Infinity, 0);
+ ctx.transform(0, 0, 0, 0, -Infinity, 0);
+ ctx.transform(0, 0, 0, 0, NaN, 0);
+ ctx.transform(0, 0, 0, 0, 0, Infinity);
+ ctx.transform(0, 0, 0, 0, 0, -Infinity);
+ ctx.transform(0, 0, 0, 0, 0, NaN);
+ ctx.transform(Infinity, Infinity, 0, 0, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, 0, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, 0, Infinity, 0);
+ ctx.transform(Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, 0, 0, Infinity);
+ ctx.transform(Infinity, Infinity, 0, Infinity, 0, 0);
+ ctx.transform(Infinity, Infinity, 0, Infinity, Infinity, 0);
+ ctx.transform(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.transform(Infinity, Infinity, 0, 0, Infinity, 0);
+ ctx.transform(Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, 0, 0, 0, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, 0, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, 0, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, Infinity, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, 0, Infinity, Infinity, 0, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, Infinity, 0);
+ ctx.transform(Infinity, 0, Infinity, 0, Infinity, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, 0, Infinity);
+ ctx.transform(Infinity, 0, 0, Infinity, 0, 0);
+ ctx.transform(Infinity, 0, 0, Infinity, Infinity, 0);
+ ctx.transform(Infinity, 0, 0, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, 0, 0, Infinity, 0, Infinity);
+ ctx.transform(Infinity, 0, 0, 0, Infinity, 0);
+ ctx.transform(Infinity, 0, 0, 0, Infinity, Infinity);
+ ctx.transform(Infinity, 0, 0, 0, 0, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, 0, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, 0, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, Infinity, 0);
+ ctx.transform(0, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, 0, Infinity);
+ ctx.transform(0, Infinity, 0, Infinity, 0, 0);
+ ctx.transform(0, Infinity, 0, Infinity, Infinity, 0);
+ ctx.transform(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.transform(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.transform(0, Infinity, 0, 0, Infinity, 0);
+ ctx.transform(0, Infinity, 0, 0, Infinity, Infinity);
+ ctx.transform(0, Infinity, 0, 0, 0, Infinity);
+ ctx.transform(0, 0, Infinity, Infinity, 0, 0);
+ ctx.transform(0, 0, Infinity, Infinity, Infinity, 0);
+ ctx.transform(0, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(0, 0, Infinity, Infinity, 0, Infinity);
+ ctx.transform(0, 0, Infinity, 0, Infinity, 0);
+ ctx.transform(0, 0, Infinity, 0, Infinity, Infinity);
+ ctx.transform(0, 0, Infinity, 0, 0, Infinity);
+ ctx.transform(0, 0, 0, Infinity, Infinity, 0);
+ ctx.transform(0, 0, 0, Infinity, Infinity, Infinity);
+ ctx.transform(0, 0, 0, Infinity, 0, Infinity);
+ ctx.transform(0, 0, 0, 0, Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.skewed.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.skewed.html
new file mode 100644
index 0000000000..c69756bc1d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.transform.skewed.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.transform.skewed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.transform.skewed</h1>
+<p class="desc">transform() with skewy matrix transforms correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("transform() with skewy matrix transforms correctly");
+_addTest(function(canvas, ctx) {
+
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.transform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ _assertPixel(canvas, 21,11, 0,255,0,255);
+ _assertPixel(canvas, 79,11, 0,255,0,255);
+ _assertPixel(canvas, 21,39, 0,255,0,255);
+ _assertPixel(canvas, 79,39, 0,255,0,255);
+ _assertPixel(canvas, 39,19, 0,255,0,255);
+ _assertPixel(canvas, 61,19, 0,255,0,255);
+ _assertPixel(canvas, 39,31, 0,255,0,255);
+ _assertPixel(canvas, 61,31, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.basic.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.basic.html
new file mode 100644
index 0000000000..189a1544b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.basic.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.translate.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.translate.basic</h1>
+<p class="desc">translate() works</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("translate() works");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.nonfinite.html b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.nonfinite.html
new file mode 100644
index 0000000000..0857693aff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/transformations/2d.transformation.translate.nonfinite.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.transformation.translate.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.transformation.translate.nonfinite</h1>
+<p class="desc">translate() with Infinity/NaN is ignored</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("translate() with Infinity/NaN is ignored");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.translate(Infinity, 0.1);
+ ctx.translate(-Infinity, 0.1);
+ ctx.translate(NaN, 0.1);
+ ctx.translate(0.1, Infinity);
+ ctx.translate(0.1, -Infinity);
+ ctx.translate(0.1, NaN);
+ ctx.translate(Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/video/2d.video.invalid.html b/testing/web-platform/tests/html/canvas/element/video/2d.video.invalid.html
new file mode 100644
index 0000000000..22b4e054f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/video/2d.video.invalid.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.video.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.video.invalid</h1>
+<p class="desc">Verify test doesn't crash with invalid video.</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Verify test doesn't crash with invalid video.");
+_addTest(function(canvas, ctx) {
+
+ var v = document.createElement('video');
+ v.play();
+ // Test is deliberately not waiting for the 'playing' event to fire.
+ ctx.createPattern(v, 'repeat-x');
+ ctx.drawImage(v, 0, 0);
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.html
new file mode 100644
index 0000000000..9c3d1b3374
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.fillText</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.fillText</h1>
+<p class="desc">Test if fillText can be used with a solid display-p3 color</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test if fillText can be used with a solid display-p3 color");
+_addTest(function(canvas, ctx) {
+
+ deferTest();
+
+ const fullRedInP3 = [255, 0, 0, 255];
+ const sRGBRedInP3 = [234, 51, 35, 255];
+
+ canvas.width = 100;
+ canvas.height = 100;
+
+ let f = new FontFace("Ahem", "url(/fonts/Ahem.ttf)");
+ document.fonts.add(f);
+ f.load().then(function() {
+ t.step(function() {
+ ctx.font = "40px Ahem";
+
+ ctx.fillStyle = "#f00";
+ ctx.fillText("A", 0, 50);
+
+ ctx.fillStyle = "black";
+ ctx.fillStyle = "color(display-p3 100% 0 0)";
+ ctx.fillText("A", 50, 50);
+
+ let pixels = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: "display-p3" }).data;
+ let pixelAt = function(x, y) {
+ let offset = (y * canvas.width + x) * 4;
+ return [pixels[offset], pixels[offset + 1], pixels[offset + 2], pixels[offset + 3]];
+ };
+
+ assert_array_equals(pixelAt(25, 25), sRGBRedInP3);
+ assert_array_equals(pixelAt(75, 25), fullRedInP3);
+
+ t.done();
+ });
+ });
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.shadow.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.shadow.html
new file mode 100644
index 0000000000..cc459fb2cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.fillText.shadow.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.fillText.shadow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.fillText.shadow</h1>
+<p class="desc">Test if fillText can be used with a display-p3 shadow color</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test if fillText can be used with a display-p3 shadow color");
+_addTest(function(canvas, ctx) {
+
+ deferTest();
+
+ const fullRedInP3 = [255, 0, 0, 255];
+ const sRGBRedInP3 = [234, 51, 35, 255];
+
+ canvas.width = 100;
+ canvas.height = 100;
+
+ let f = new FontFace("Ahem", "url(/fonts/Ahem.ttf)");
+ document.fonts.add(f);
+ f.load().then(function() {
+ t.step(function() {
+ ctx.font = "40px Ahem";
+
+ ctx.fillStyle = "black";
+ ctx.shadowBlur = 4;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = "#f00";
+ ctx.fillText("A", 0, 0);
+
+ ctx.shadowColor = "black";
+ ctx.shadowColor = "color(display-p3 100% 0 0)";
+ ctx.fillText("A", 50, 0);
+
+ let pixels = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: "display-p3" }).data;
+ let pixelAt = function(x, y) {
+ let offset = (y * canvas.width + x) * 4;
+ return [pixels[offset], pixels[offset + 1], pixels[offset + 2], pixels[offset + 3]];
+ };
+
+ assert_array_equals(pixelAt(25, 25), sRGBRedInP3);
+ assert_array_equals(pixelAt(75, 25), fullRedInP3);
+
+ t.done();
+ });
+ });
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.strokeText.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.strokeText.html
new file mode 100644
index 0000000000..626a41b49b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.strokeText.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.strokeText</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.strokeText</h1>
+<p class="desc">Test if strokeText can be used with a solid display-p3 color</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Test if strokeText can be used with a solid display-p3 color");
+_addTest(function(canvas, ctx) {
+
+ deferTest();
+
+ const fullRedInP3 = [255, 0, 0, 255];
+ const sRGBRedInP3 = [234, 51, 35, 255];
+
+ canvas.width = 100;
+ canvas.height = 100;
+
+ let f = new FontFace("Ahem", "url(/fonts/Ahem.ttf)");
+ document.fonts.add(f);
+ f.load().then(function() {
+ t.step(function() {
+ ctx.font = "40px Ahem";
+
+ ctx.strokeStyle = "#f00";
+ ctx.lineWidth = 20;
+ ctx.strokeText("A", 0, 50);
+
+ ctx.strokeStyle = "black";
+ ctx.strokeStyle = "color(display-p3 100% 0 0)";
+ ctx.strokeText("A", 50, 50);
+
+ let pixels = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: "display-p3" }).data;
+ let pixelAt = function(x, y) {
+ let offset = (y * canvas.width + x) * 4;
+ return [pixels[offset], pixels[offset + 1], pixels[offset + 2], pixels[offset + 3]];
+ };
+
+ assert_array_equals(pixelAt(25, 25), sRGBRedInP3);
+ assert_array_equals(pixelAt(75, 25), fullRedInP3);
+
+ t.done();
+ });
+ });
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.p3.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.p3.html
new file mode 100644
index 0000000000..a03f49740f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.p3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.to.p3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.to.p3</h1>
+<p class="desc">test getImageData with display-p3 and uint8 from display p3 uint8 canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("test getImageData with display-p3 and uint8 from display p3 uint8 canvas");
+_addTest(function(canvas, ctx) {
+
+ var color_style = 'rgb(50, 100, 150)';
+ // [0.24304, 0.38818, 0.57227, 1.0] * 255 = [62, 99, 146, 255]
+ var pixel_expected = [62, 99, 146, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "display-p3", storageFormat: "uint8"}).data;
+ _assertSame(pixel.length, pixel_expected.length, "pixel.length", "pixel_expected.length");
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.srgb.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.srgb.html
new file mode 100644
index 0000000000..0433ae38c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.to.srgb.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.to.srgb</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.to.srgb</h1>
+<p class="desc">test getImageData with srsb and uint8 from display p3 uint8 canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("test getImageData with srsb and uint8 from display p3 uint8 canvas");
+_addTest(function(canvas, ctx) {
+
+ var color_style = 'rgb(50, 100, 150)';
+ var pixel_expected = [50, 100, 150, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "srgb", storageFormat: "uint8"}).data;
+ _assertSame(pixel.length, pixel_expected.length, "pixel.length", "pixel_expected.length");
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.p3.canvas.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.p3.canvas.html
new file mode 100644
index 0000000000..1a44af20a6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.p3.canvas.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.toBlob.p3.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.toBlob.p3.canvas</h1>
+<p class="desc">test if toblob returns p3 data from p3 color space canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("test if toblob returns p3 data from p3 color space canvas");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ ctx.fillRect(0, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ ctx.fillRect(1, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ ctx.fillRect(0, 1, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ ctx.fillRect(1, 1, 1, 1);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+
+ canvas.toBlob(function(blob) {
+ var urlCreator = window.URL || window.webkitURL;
+ image.src = urlCreator.createObjectURL(blob);
+ }, 'image/png', 1);
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.with.putImageData.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.with.putImageData.html
new file mode 100644
index 0000000000..a53de72259
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toBlob.with.putImageData.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.toBlob.with.putImageData</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.toBlob.with.putImageData</h1>
+<p class="desc">Use putImageData to put some p3 data in canvas and test if toBlob returns the same data</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Use putImageData to put some p3 data in canvas and test if toBlob returns the same data");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 2;
+ canvas.height = 2;
+
+ // Create an ImageData using createImageData and populate its data array.
+ var image_data = ctx.createImageData(canvas.width, canvas.height, {colorSpace: "display-p3"});
+ var color_data = [[255, 100, 150, 1.0], [255, 100, 150, 0.5],
+ [255, 100, 150, 0.5], [255, 100, 150, 0]];
+ var data = image_data.data;
+ for (var i = 0; i < data.length / 4; ++i) {
+ data[4*i + 0] = color_data[i][0];
+ data[4*i + 1] = color_data[i][1];
+ data[4*i + 2] = color_data[i][2];
+ data[4*i + 3] = color_data[i][3];
+ }
+ ctx.putImageData(image_data, 0, 0);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ canvas.toBlob(function(blob) {
+ var urlCreator = window.URL || window.webkitURL;
+ image.src = urlCreator.createObjectURL(blob);
+ }, 'image/png', 1);
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.jpeg.p3.canvas.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.jpeg.p3.canvas.html
new file mode 100644
index 0000000000..e0986cb8e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.jpeg.p3.canvas.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.toDataURL.jpeg.p3.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.toDataURL.jpeg.p3.canvas</h1>
+<p class="desc">test if toDataURL('image/jpeg') returns p3 data from canvas with p3 color space</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("test if toDataURL('image/jpeg') returns p3 data from canvas with p3 color space");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ ctx.fillRect(0, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ ctx.fillRect(1, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ ctx.fillRect(0, 1, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ ctx.fillRect(1, 1, 1, 1);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ image.src = canvas.toDataURL("image/jpeg");
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.p3.canvas.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.p3.canvas.html
new file mode 100644
index 0000000000..94ba703980
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.p3.canvas.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.toDataURL.p3.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.toDataURL.p3.canvas</h1>
+<p class="desc">test if toDataURL returns p3 data from canvas with p3 color space</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("test if toDataURL returns p3 data from canvas with p3 color space");
+_addTest(function(canvas, ctx) {
+
+ ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ ctx.fillRect(0, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ ctx.fillRect(1, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ ctx.fillRect(0, 1, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ ctx.fillRect(1, 1, 1, 1);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ image.src = canvas.toDataURL();
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.with.putImageData.html b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.with.putImageData.html
new file mode 100644
index 0000000000..58d0718eba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/wide-gamut-canvas/2d.color.space.p3.toDataURL.with.putImageData.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.color.space.p3.toDataURL.with.putImageData</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.color.space.p3.toDataURL.with.putImageData</h1>
+<p class="desc">Use putImageData to put some p3 data in canvas and test if toDataURL returns the same data</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Use putImageData to put some p3 data in canvas and test if toDataURL returns the same data");
+_addTest(function(canvas, ctx) {
+
+ canvas.width = 2;
+ canvas.height = 2;
+
+ // Create an ImageData using createImageData and populate its data array.
+ var image_data = ctx.createImageData(canvas.width, canvas.height, {colorSpace: "display-p3"});
+ var color_data = [[255, 100, 150, 1.0], [255, 100, 150, 0.5],
+ [255, 100, 150, 0.5], [255, 100, 150, 0]];
+ var data = image_data.data;
+ for (var i = 0; i < data.length / 4; ++i) {
+ data[4*i + 0] = color_data[i][0];
+ data[4*i + 1] = color_data[i][1];
+ data[4*i + 2] = color_data[i][2];
+ data[4*i + 3] = color_data[i][3];
+ }
+ ctx.putImageData(image_data, 0, 0);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ image.src = canvas.toDataURL();
+
+}, {colorSpace: "display-p3"});
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.html b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.html
new file mode 100644
index 0000000000..a89ec10f9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.conformance.requirements.basics</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.conformance.requirements.basics</h1>
+<p class="desc">void methods return undefined</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("void methods return undefined");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var canvas = new OffscreenCanvas(100, 50);
+var ctx = canvas.getContext('2d');
+
+_assertSame(ctx.save(), undefined, "ctx.save()", "undefined");
+_assertSame(ctx.restore(), undefined, "ctx.restore()", "undefined");
+_assertSame(ctx.scale(1, 1), undefined, "ctx.scale(1, 1)", "undefined");
+_assertSame(ctx.rotate(0), undefined, "ctx.rotate(0)", "undefined");
+_assertSame(ctx.translate(0, 0), undefined, "ctx.translate(0, 0)", "undefined");
+if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ _assertSame(ctx.transform(1, 0, 0, 1, 0, 0), undefined, "ctx.transform(1, 0, 0, 1, 0, 0)", "undefined");
+}
+if (ctx.setTransform) {
+ _assertSame(ctx.setTransform(1, 0, 0, 1, 0, 0), undefined, "ctx.setTransform(1, 0, 0, 1, 0, 0)", "undefined");
+ _assertSame(ctx.setTransform(), undefined, "ctx.setTransform()", "undefined");
+}
+_assertSame(ctx.clearRect(0, 0, 0, 0), undefined, "ctx.clearRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.fillRect(0, 0, 0, 0), undefined, "ctx.fillRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.strokeRect(0, 0, 0, 0), undefined, "ctx.strokeRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.beginPath(), undefined, "ctx.beginPath()", "undefined");
+_assertSame(ctx.closePath(), undefined, "ctx.closePath()", "undefined");
+_assertSame(ctx.moveTo(0, 0), undefined, "ctx.moveTo(0, 0)", "undefined");
+_assertSame(ctx.lineTo(0, 0), undefined, "ctx.lineTo(0, 0)", "undefined");
+_assertSame(ctx.quadraticCurveTo(0, 0, 0, 0), undefined, "ctx.quadraticCurveTo(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.bezierCurveTo(0, 0, 0, 0, 0, 0), undefined, "ctx.bezierCurveTo(0, 0, 0, 0, 0, 0)", "undefined");
+_assertSame(ctx.arcTo(0, 0, 0, 0, 1), undefined, "ctx.arcTo(0, 0, 0, 0, 1)", "undefined");
+_assertSame(ctx.rect(0, 0, 0, 0), undefined, "ctx.rect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.arc(0, 0, 1, 0, 0, true), undefined, "ctx.arc(0, 0, 1, 0, 0, true)", "undefined");
+_assertSame(ctx.fill(), undefined, "ctx.fill()", "undefined");
+_assertSame(ctx.stroke(), undefined, "ctx.stroke()", "undefined");
+_assertSame(ctx.clip(), undefined, "ctx.clip()", "undefined");
+if (ctx.fillText) {
+ _assertSame(ctx.fillText('test', 0, 0), undefined, "ctx.fillText('test', 0, 0)", "undefined");
+ _assertSame(ctx.strokeText('test', 0, 0), undefined, "ctx.strokeText('test', 0, 0)", "undefined");
+}
+if (ctx.putImageData) {
+ _assertSame(ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0), undefined, "ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0)", "undefined");
+}
+_assertSame(ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+_assertSame(ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white'), undefined, "ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white')", "undefined");
+t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.worker.js b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.worker.js
new file mode 100644
index 0000000000..8d6ddc894f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.basics.worker.js
@@ -0,0 +1,58 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.conformance.requirements.basics
+// Description:void methods return undefined
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("void methods return undefined");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var canvas = new OffscreenCanvas(100, 50);
+var ctx = canvas.getContext('2d');
+
+_assertSame(ctx.save(), undefined, "ctx.save()", "undefined");
+_assertSame(ctx.restore(), undefined, "ctx.restore()", "undefined");
+_assertSame(ctx.scale(1, 1), undefined, "ctx.scale(1, 1)", "undefined");
+_assertSame(ctx.rotate(0), undefined, "ctx.rotate(0)", "undefined");
+_assertSame(ctx.translate(0, 0), undefined, "ctx.translate(0, 0)", "undefined");
+if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ _assertSame(ctx.transform(1, 0, 0, 1, 0, 0), undefined, "ctx.transform(1, 0, 0, 1, 0, 0)", "undefined");
+}
+if (ctx.setTransform) {
+ _assertSame(ctx.setTransform(1, 0, 0, 1, 0, 0), undefined, "ctx.setTransform(1, 0, 0, 1, 0, 0)", "undefined");
+ _assertSame(ctx.setTransform(), undefined, "ctx.setTransform()", "undefined");
+}
+_assertSame(ctx.clearRect(0, 0, 0, 0), undefined, "ctx.clearRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.fillRect(0, 0, 0, 0), undefined, "ctx.fillRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.strokeRect(0, 0, 0, 0), undefined, "ctx.strokeRect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.beginPath(), undefined, "ctx.beginPath()", "undefined");
+_assertSame(ctx.closePath(), undefined, "ctx.closePath()", "undefined");
+_assertSame(ctx.moveTo(0, 0), undefined, "ctx.moveTo(0, 0)", "undefined");
+_assertSame(ctx.lineTo(0, 0), undefined, "ctx.lineTo(0, 0)", "undefined");
+_assertSame(ctx.quadraticCurveTo(0, 0, 0, 0), undefined, "ctx.quadraticCurveTo(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.bezierCurveTo(0, 0, 0, 0, 0, 0), undefined, "ctx.bezierCurveTo(0, 0, 0, 0, 0, 0)", "undefined");
+_assertSame(ctx.arcTo(0, 0, 0, 0, 1), undefined, "ctx.arcTo(0, 0, 0, 0, 1)", "undefined");
+_assertSame(ctx.rect(0, 0, 0, 0), undefined, "ctx.rect(0, 0, 0, 0)", "undefined");
+_assertSame(ctx.arc(0, 0, 1, 0, 0, true), undefined, "ctx.arc(0, 0, 1, 0, 0, true)", "undefined");
+_assertSame(ctx.fill(), undefined, "ctx.fill()", "undefined");
+_assertSame(ctx.stroke(), undefined, "ctx.stroke()", "undefined");
+_assertSame(ctx.clip(), undefined, "ctx.clip()", "undefined");
+if (ctx.fillText) {
+ _assertSame(ctx.fillText('test', 0, 0), undefined, "ctx.fillText('test', 0, 0)", "undefined");
+ _assertSame(ctx.strokeText('test', 0, 0), undefined, "ctx.strokeText('test', 0, 0)", "undefined");
+}
+if (ctx.putImageData) {
+ _assertSame(ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0), undefined, "ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0)", "undefined");
+}
+_assertSame(ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+_assertSame(ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white'), undefined, "ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white')", "undefined");
+t.done();
+
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.html b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.html
new file mode 100644
index 0000000000..cfbbca9e5e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.conformance.requirements.missingargs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.conformance.requirements.missingargs</h1>
+<p class="desc">Missing arguments cause TypeError</p>
+
+
+<script>
+var t = async_test("Missing arguments cause TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var canvas = new OffscreenCanvas(100, 50);
+var ctx = canvas.getContext('2d');
+
+assert_throws_js(TypeError, function() { ctx.scale(); });
+assert_throws_js(TypeError, function() { ctx.scale(1); });
+assert_throws_js(TypeError, function() { ctx.rotate(); });
+assert_throws_js(TypeError, function() { ctx.translate(); });
+assert_throws_js(TypeError, function() { ctx.translate(0); });
+if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ assert_throws_js(TypeError, function() { ctx.transform(); });
+ assert_throws_js(TypeError, function() { ctx.transform(1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1, 0); });
+}
+if (ctx.setTransform) {
+ assert_throws_js(TypeError, function() { ctx.setTransform(1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1, 0); });
+}
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.createPattern(canvas); });
+assert_throws_js(TypeError, function() { ctx.clearRect(); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.moveTo(); });
+assert_throws_js(TypeError, function() { ctx.moveTo(0); });
+assert_throws_js(TypeError, function() { ctx.lineTo(); });
+assert_throws_js(TypeError, function() { ctx.lineTo(0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.rect(); });
+assert_throws_js(TypeError, function() { ctx.rect(0); });
+assert_throws_js(TypeError, function() { ctx.rect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.rect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arc(); });
+assert_throws_js(TypeError, function() { ctx.arc(0); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1, 0); });
+// (6th argument to arc is optional)
+if (ctx.isPointInPath) {
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(0); });
+}
+if (ctx.drawFocusRing) {
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas, 0); });
+}
+if (ctx.fillText) {
+ assert_throws_js(TypeError, function() { ctx.fillText(); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test'); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeText(); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test'); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.measureText(); });
+}
+assert_throws_js(TypeError, function() { ctx.drawImage(); });
+assert_throws_js(TypeError, function() { ctx.drawImage(canvas); });
+assert_throws_js(TypeError, function() { ctx.drawImage(canvas, 0); });
+// TODO: n >= 3 args on drawImage could be either a valid overload,
+// or too few for another overload, or too many for another
+// overload - what should happen?
+if (ctx.createImageData) {
+ assert_throws_js(TypeError, function() { ctx.createImageData(); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(1); });
+}
+if (ctx.getImageData) {
+ assert_throws_js(TypeError, function() { ctx.getImageData(); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0, 1); });
+}
+if (ctx.putImageData) {
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ assert_throws_js(TypeError, function() { ctx.putImageData(); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0); });
+}
+var g = ctx.createLinearGradient(0, 0, 0, 0);
+assert_throws_js(TypeError, function() { g.addColorStop(); });
+assert_throws_js(TypeError, function() { g.addColorStop(0); });
+t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.worker.js b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.worker.js
new file mode 100644
index 0000000000..fe1b111f05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/2d.conformance.requirements.missingargs.worker.js
@@ -0,0 +1,137 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.conformance.requirements.missingargs
+// Description:Missing arguments cause TypeError
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Missing arguments cause TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var canvas = new OffscreenCanvas(100, 50);
+var ctx = canvas.getContext('2d');
+
+assert_throws_js(TypeError, function() { ctx.scale(); });
+assert_throws_js(TypeError, function() { ctx.scale(1); });
+assert_throws_js(TypeError, function() { ctx.rotate(); });
+assert_throws_js(TypeError, function() { ctx.translate(); });
+assert_throws_js(TypeError, function() { ctx.translate(0); });
+if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ assert_throws_js(TypeError, function() { ctx.transform(); });
+ assert_throws_js(TypeError, function() { ctx.transform(1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1, 0); });
+}
+if (ctx.setTransform) {
+ assert_throws_js(TypeError, function() { ctx.setTransform(1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1, 0); });
+}
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0); });
+assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0); });
+assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.createPattern(canvas); });
+assert_throws_js(TypeError, function() { ctx.clearRect(); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.clearRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.fillRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.moveTo(); });
+assert_throws_js(TypeError, function() { ctx.moveTo(0); });
+assert_throws_js(TypeError, function() { ctx.lineTo(); });
+assert_throws_js(TypeError, function() { ctx.lineTo(0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.rect(); });
+assert_throws_js(TypeError, function() { ctx.rect(0); });
+assert_throws_js(TypeError, function() { ctx.rect(0, 0); });
+assert_throws_js(TypeError, function() { ctx.rect(0, 0, 0); });
+assert_throws_js(TypeError, function() { ctx.arc(); });
+assert_throws_js(TypeError, function() { ctx.arc(0); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1); });
+assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1, 0); });
+// (6th argument to arc is optional)
+if (ctx.isPointInPath) {
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(0); });
+}
+if (ctx.drawFocusRing) {
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas, 0); });
+}
+if (ctx.fillText) {
+ assert_throws_js(TypeError, function() { ctx.fillText(); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test'); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeText(); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test'); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.measureText(); });
+}
+assert_throws_js(TypeError, function() { ctx.drawImage(); });
+assert_throws_js(TypeError, function() { ctx.drawImage(canvas); });
+assert_throws_js(TypeError, function() { ctx.drawImage(canvas, 0); });
+// TODO: n >= 3 args on drawImage could be either a valid overload,
+// or too few for another overload, or too many for another
+// overload - what should happen?
+if (ctx.createImageData) {
+ assert_throws_js(TypeError, function() { ctx.createImageData(); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(1); });
+}
+if (ctx.getImageData) {
+ assert_throws_js(TypeError, function() { ctx.getImageData(); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0, 1); });
+}
+if (ctx.putImageData) {
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ assert_throws_js(TypeError, function() { ctx.putImageData(); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0); });
+}
+var g = ctx.createLinearGradient(0, 0, 0, 0);
+assert_throws_js(TypeError, function() { g.addColorStop(); });
+assert_throws_js(TypeError, function() { g.addColorStop(0); });
+t.done();
+
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/META.yml b/testing/web-platform/tests/html/canvas/offscreen/META.yml
new file mode 100644
index 0000000000..d0524194c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/META.yml
@@ -0,0 +1,7 @@
+spec: https://html.spec.whatwg.org/multipage/canvas.html#the-offscreencanvas-interface
+suggested_reviewers:
+ - AmeliaBR
+ - annevk
+ - kenrussell
+ - fserb
+ - Cwiiis
diff --git a/testing/web-platform/tests/html/canvas/offscreen/WEB_FEATURES.yml b/testing/web-platform/tests/html/canvas/offscreen/WEB_FEATURES.yml
new file mode 100644
index 0000000000..b1f003a745
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: offscreen-canvas
+ files: "**"
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.html
new file mode 100644
index 0000000000..0c9fdc9639
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.arguments.missing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.arguments.missing</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext(); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.worker.js
new file mode 100644
index 0000000000..1b5c857a79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.arguments.missing.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.arguments.missing
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext(); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.html
new file mode 100644
index 0000000000..aed71a37e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.casesensitive</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.casesensitive</h1>
+<p class="desc">Context name "2D" is unrecognised; matching is case sensitive</p>
+
+
+<script>
+var t = async_test("Context name \"2D\" is unrecognised; matching is case sensitive");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext('2D'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.worker.js
new file mode 100644
index 0000000000..c1df3a9f80
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.casesensitive.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.casesensitive
+// Description:Context name "2D" is unrecognised; matching is case sensitive
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Context name \"2D\" is unrecognised; matching is case sensitive");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext('2D'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.html
new file mode 100644
index 0000000000..10548a6b7c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.emptystring</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.emptystring</h1>
+<p class="desc">getContext with empty string returns null</p>
+
+
+<script>
+var t = async_test("getContext with empty string returns null");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext(""); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.worker.js
new file mode 100644
index 0000000000..53a58c1f5d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.emptystring.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.emptystring
+// Description:getContext with empty string returns null
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getContext with empty string returns null");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext(""); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.html
new file mode 100644
index 0000000000..41b5cfee31
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.exists</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.exists</h1>
+<p class="desc">The 2D context is implemented</p>
+
+
+<script>
+var t = async_test("The 2D context is implemented");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ _assertDifferent(offscreenCanvas2.getContext('2d'), null, "offscreenCanvas2.getContext('2d')", "null");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.worker.js
new file mode 100644
index 0000000000..77a43530a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.exists.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.exists
+// Description:The 2D context is implemented
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("The 2D context is implemented");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ _assertDifferent(offscreenCanvas2.getContext('2d'), null, "offscreenCanvas2.getContext('2d')", "null");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.html
new file mode 100644
index 0000000000..2bcf34490e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.extraargs.cache</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.extraargs.cache</h1>
+<p class="desc">The 2D context doesn't throw with extra getContext arguments (cached)</p>
+
+
+<script>
+var t = async_test("The 2D context doesn't throw with extra getContext arguments (cached)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(canvas.getContext('2d', false, {}, [], 1, "2"), null, "canvas.getContext('2d', false, {}, [], 1, \"2\")", "null");
+ _assertDifferent(canvas.getContext('2d', 123), null, "canvas.getContext('2d', 123)", "null");
+ _assertDifferent(canvas.getContext('2d', "test"), null, "canvas.getContext('2d', \"test\")", "null");
+ _assertDifferent(canvas.getContext('2d', undefined), null, "canvas.getContext('2d', undefined)", "null");
+ _assertDifferent(canvas.getContext('2d', null), null, "canvas.getContext('2d', null)", "null");
+ _assertDifferent(canvas.getContext('2d', Symbol.hasInstance), null, "canvas.getContext('2d', Symbol.hasInstance)", "null");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.worker.js
new file mode 100644
index 0000000000..14284a0a7d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.cache.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.extraargs.cache
+// Description:The 2D context doesn't throw with extra getContext arguments (cached)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("The 2D context doesn't throw with extra getContext arguments (cached)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(canvas.getContext('2d', false, {}, [], 1, "2"), null, "canvas.getContext('2d', false, {}, [], 1, \"2\")", "null");
+ _assertDifferent(canvas.getContext('2d', 123), null, "canvas.getContext('2d', 123)", "null");
+ _assertDifferent(canvas.getContext('2d', "test"), null, "canvas.getContext('2d', \"test\")", "null");
+ _assertDifferent(canvas.getContext('2d', undefined), null, "canvas.getContext('2d', undefined)", "null");
+ _assertDifferent(canvas.getContext('2d', null), null, "canvas.getContext('2d', null)", "null");
+ _assertDifferent(canvas.getContext('2d', Symbol.hasInstance), null, "canvas.getContext('2d', Symbol.hasInstance)", "null");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.html
new file mode 100644
index 0000000000..029122355c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.extraargs.create</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.extraargs.create</h1>
+<p class="desc">The 2D context doesn't throw with extra getContext arguments (new context)</p>
+
+
+<script>
+var t = async_test("The 2D context doesn't throw with extra getContext arguments (new context)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', false, {}, [], 1, "2"), null, "(new OffscreenCanvas(100, 50)).getContext('2d', false, {}, [], 1, \"2\")", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', 123), null, "(new OffscreenCanvas(100, 50)).getContext('2d', 123)", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', "test"), null, "(new OffscreenCanvas(100, 50)).getContext('2d', \"test\")", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', undefined), null, "(new OffscreenCanvas(100, 50)).getContext('2d', undefined)", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', null), null, "(new OffscreenCanvas(100, 50)).getContext('2d', null)", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', Symbol.hasInstance), null, "(new OffscreenCanvas(100, 50)).getContext('2d', Symbol.hasInstance)", "null");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.worker.js
new file mode 100644
index 0000000000..b4208edba8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.extraargs.create.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.extraargs.create
+// Description:The 2D context doesn't throw with extra getContext arguments (new context)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("The 2D context doesn't throw with extra getContext arguments (new context)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', false, {}, [], 1, "2"), null, "(new OffscreenCanvas(100, 50)).getContext('2d', false, {}, [], 1, \"2\")", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', 123), null, "(new OffscreenCanvas(100, 50)).getContext('2d', 123)", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', "test"), null, "(new OffscreenCanvas(100, 50)).getContext('2d', \"test\")", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', undefined), null, "(new OffscreenCanvas(100, 50)).getContext('2d', undefined)", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', null), null, "(new OffscreenCanvas(100, 50)).getContext('2d', null)", "null");
+ _assertDifferent((new OffscreenCanvas(100, 50)).getContext('2d', Symbol.hasInstance), null, "(new OffscreenCanvas(100, 50)).getContext('2d', Symbol.hasInstance)", "null");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.html
new file mode 100644
index 0000000000..cb9501747e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.shared</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.shared</h1>
+<p class="desc">getContext('2d') returns objects which share canvas state</p>
+
+
+<script>
+var t = async_test("getContext('2d') returns objects which share canvas state");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var ctx2 = canvas.getContext('2d');
+ ctx.fillStyle = '#f00';
+ ctx2.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.worker.js
new file mode 100644
index 0000000000..8e53475756
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.shared.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.shared
+// Description:getContext('2d') returns objects which share canvas state
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getContext('2d') returns objects which share canvas state");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var ctx2 = canvas.getContext('2d');
+ ctx.fillStyle = '#f00';
+ ctx2.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.html
new file mode 100644
index 0000000000..66e4a049ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.unique</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.unique</h1>
+<p class="desc">getContext('2d') returns the same object</p>
+
+
+<script>
+var t = async_test("getContext('2d') returns the same object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ _assertSame(offscreenCanvas2.getContext('2d'), offscreenCanvas2.getContext('2d'), "offscreenCanvas2.getContext('2d')", "offscreenCanvas2.getContext('2d')");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.worker.js
new file mode 100644
index 0000000000..275e45fde6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unique.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.unique
+// Description:getContext('2d') returns the same object
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getContext('2d') returns the same object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ _assertSame(offscreenCanvas2.getContext('2d'), offscreenCanvas2.getContext('2d'), "offscreenCanvas2.getContext('2d')", "offscreenCanvas2.getContext('2d')");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.html
new file mode 100644
index 0000000000..4313f40843
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.unrecognised.badname</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.unrecognised.badname</h1>
+<p class="desc">getContext with unrecognised context name returns null</p>
+
+
+<script>
+var t = async_test("getContext with unrecognised context name returns null");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext('This is not an implemented context in any real browser'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.worker.js
new file mode 100644
index 0000000000..dd16f2fbe1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badname.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.unrecognised.badname
+// Description:getContext with unrecognised context name returns null
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getContext with unrecognised context name returns null");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext('This is not an implemented context in any real browser'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.html
new file mode 100644
index 0000000000..c00afa686e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.unrecognised.badsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.unrecognised.badsuffix</h1>
+<p class="desc">Context name "2d" plus a suffix is unrecognised</p>
+
+
+<script>
+var t = async_test("Context name \"2d\" plus a suffix is unrecognised");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext("2d#"); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.worker.js
new file mode 100644
index 0000000000..5e684c92c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.badsuffix.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.unrecognised.badsuffix
+// Description:Context name "2d" plus a suffix is unrecognised
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Context name \"2d\" plus a suffix is unrecognised");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext("2d#"); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.html
new file mode 100644
index 0000000000..0ce3d4195e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.unrecognised.nullsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.unrecognised.nullsuffix</h1>
+<p class="desc">Context name "2d" plus a "\0" suffix is unrecognised</p>
+
+
+<script>
+var t = async_test("Context name \"2d\" plus a \"\\0\" suffix is unrecognised");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext("2d\0"); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.worker.js
new file mode 100644
index 0000000000..2886010fb6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.nullsuffix.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.unrecognised.nullsuffix
+// Description:Context name "2d" plus a "\0" suffix is unrecognised
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Context name \"2d\" plus a \"\\0\" suffix is unrecognised");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext("2d\0"); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.html
new file mode 100644
index 0000000000..316123675a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.context.unrecognised.unicode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.context.unrecognised.unicode</h1>
+<p class="desc">Context name which kind of looks like "2d" is unrecognised</p>
+
+
+<script>
+var t = async_test("Context name which kind of looks like \"2d\" is unrecognised");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext("2\uFF44"); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.worker.js
new file mode 100644
index 0000000000..46e562dd48
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.canvas.context.unrecognised.unicode.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.context.unrecognised.unicode
+// Description:Context name which kind of looks like "2d" is unrecognised
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Context name which kind of looks like \"2d\" is unrecognised");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ assert_throws_js(TypeError, function() { offscreenCanvas2.getContext("2\uFF44"); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.getContext.options.any.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.getContext.options.any.js
new file mode 100644
index 0000000000..930f7722ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-context/2d.getContext.options.any.js
@@ -0,0 +1,46 @@
+test(() => {
+ const expected = [
+ "alpha",
+ "colorSpace",
+ "colorSpace toString",
+ "desynchronized",
+ "willReadFrequently",
+ ];
+ var actual = [];
+ const options = {
+ get alpha() {
+ actual.push("alpha");
+ return true;
+ },
+ get willReadFrequently() {
+ actual.push("willReadFrequently");
+ return false;
+ },
+ get desynchronized() {
+ actual.push("desynchronized");
+ return false;
+ },
+ get colorSpace() {
+ actual.push("colorSpace");
+ return {
+ toString() {
+ actual.push("colorSpace toString");
+ return "srgb";
+ }
+ };
+ },
+ };
+
+ const canvas = new OffscreenCanvas(100, 50);
+ const context = canvas.getContext('2d', options);
+ assert_not_equals(context, null, "context");
+ assert_array_equals(actual, expected, "order of operations (creation)");
+ actual = [];
+ assert_equals(canvas.getContext('2d', options), context, "cached context");
+ // Getting the cached context does not involve the options argument at all;
+ // the context retains its existing settings, and the new options (if any)
+ // are ignored.
+ // Therefore, there is no expectation that the 'options' dictionary will be
+ // accessed again here, and we cannot predict the contents, if any, of the
+ // 'actual' array.
+});
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.html
new file mode 100644
index 0000000000..0793c74f39
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.color</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.color</h1>
+<p class="desc">Initial state is transparent black</p>
+
+
+<script>
+var t = async_test("Initial state is transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.worker.js
new file mode 100644
index 0000000000..11d6b9f098
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.color.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.color
+// Description:Initial state is transparent black
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Initial state is transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.html
new file mode 100644
index 0000000000..36230dab77
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.2dstate</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.2dstate</h1>
+<p class="desc">Resetting the canvas state resets 2D state variables</p>
+
+
+<script>
+var t = async_test("Resetting the canvas state resets 2D state variables");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ var default_val;
+
+ default_val = ctx.strokeStyle;
+ ctx.strokeStyle = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.strokeStyle, default_val, "ctx.strokeStyle", "default_val");
+
+ default_val = ctx.fillStyle;
+ ctx.fillStyle = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.fillStyle, default_val, "ctx.fillStyle", "default_val");
+
+ default_val = ctx.globalAlpha;
+ ctx.globalAlpha = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.globalAlpha, default_val, "ctx.globalAlpha", "default_val");
+
+ default_val = ctx.lineWidth;
+ ctx.lineWidth = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.lineWidth, default_val, "ctx.lineWidth", "default_val");
+
+ default_val = ctx.lineCap;
+ ctx.lineCap = "round";
+ canvas.width = 100;
+ _assertSame(ctx.lineCap, default_val, "ctx.lineCap", "default_val");
+
+ default_val = ctx.lineJoin;
+ ctx.lineJoin = "round";
+ canvas.width = 100;
+ _assertSame(ctx.lineJoin, default_val, "ctx.lineJoin", "default_val");
+
+ default_val = ctx.miterLimit;
+ ctx.miterLimit = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.miterLimit, default_val, "ctx.miterLimit", "default_val");
+
+ default_val = ctx.shadowOffsetX;
+ ctx.shadowOffsetX = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowOffsetX, default_val, "ctx.shadowOffsetX", "default_val");
+
+ default_val = ctx.shadowOffsetY;
+ ctx.shadowOffsetY = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowOffsetY, default_val, "ctx.shadowOffsetY", "default_val");
+
+ default_val = ctx.shadowBlur;
+ ctx.shadowBlur = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowBlur, default_val, "ctx.shadowBlur", "default_val");
+
+ default_val = ctx.shadowColor;
+ ctx.shadowColor = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.shadowColor, default_val, "ctx.shadowColor", "default_val");
+
+ default_val = ctx.globalCompositeOperation;
+ ctx.globalCompositeOperation = "copy";
+ canvas.width = 100;
+ _assertSame(ctx.globalCompositeOperation, default_val, "ctx.globalCompositeOperation", "default_val");
+
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.worker.js
new file mode 100644
index 0000000000..df0bd9f63c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.2dstate.worker.js
@@ -0,0 +1,84 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.2dstate
+// Description:Resetting the canvas state resets 2D state variables
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Resetting the canvas state resets 2D state variables");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ var default_val;
+
+ default_val = ctx.strokeStyle;
+ ctx.strokeStyle = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.strokeStyle, default_val, "ctx.strokeStyle", "default_val");
+
+ default_val = ctx.fillStyle;
+ ctx.fillStyle = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.fillStyle, default_val, "ctx.fillStyle", "default_val");
+
+ default_val = ctx.globalAlpha;
+ ctx.globalAlpha = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.globalAlpha, default_val, "ctx.globalAlpha", "default_val");
+
+ default_val = ctx.lineWidth;
+ ctx.lineWidth = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.lineWidth, default_val, "ctx.lineWidth", "default_val");
+
+ default_val = ctx.lineCap;
+ ctx.lineCap = "round";
+ canvas.width = 100;
+ _assertSame(ctx.lineCap, default_val, "ctx.lineCap", "default_val");
+
+ default_val = ctx.lineJoin;
+ ctx.lineJoin = "round";
+ canvas.width = 100;
+ _assertSame(ctx.lineJoin, default_val, "ctx.lineJoin", "default_val");
+
+ default_val = ctx.miterLimit;
+ ctx.miterLimit = 0.5;
+ canvas.width = 100;
+ _assertSame(ctx.miterLimit, default_val, "ctx.miterLimit", "default_val");
+
+ default_val = ctx.shadowOffsetX;
+ ctx.shadowOffsetX = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowOffsetX, default_val, "ctx.shadowOffsetX", "default_val");
+
+ default_val = ctx.shadowOffsetY;
+ ctx.shadowOffsetY = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowOffsetY, default_val, "ctx.shadowOffsetY", "default_val");
+
+ default_val = ctx.shadowBlur;
+ ctx.shadowBlur = 5;
+ canvas.width = 100;
+ _assertSame(ctx.shadowBlur, default_val, "ctx.shadowBlur", "default_val");
+
+ default_val = ctx.shadowColor;
+ ctx.shadowColor = "#ff0000";
+ canvas.width = 100;
+ _assertSame(ctx.shadowColor, default_val, "ctx.shadowColor", "default_val");
+
+ default_val = ctx.globalCompositeOperation;
+ ctx.globalCompositeOperation = "copy";
+ canvas.width = 100;
+ _assertSame(ctx.globalCompositeOperation, default_val, "ctx.globalCompositeOperation", "default_val");
+
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.html
new file mode 100644
index 0000000000..fc53240795
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.clip</h1>
+<p class="desc">Resetting the canvas state resets the current clip region</p>
+
+
+<script>
+var t = async_test("Resetting the canvas state resets the current clip region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.rect(0, 0, 1, 1);
+ ctx.clip();
+ canvas.width = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.worker.js
new file mode 100644
index 0000000000..80554b0a3f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.clip.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.clip
+// Description:Resetting the canvas state resets the current clip region
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Resetting the canvas state resets the current clip region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.rect(0, 0, 1, 1);
+ ctx.clip();
+ canvas.width = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.html
new file mode 100644
index 0000000000..3771a5e37a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.different</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.different</h1>
+<p class="desc">Changing size resets canvas to transparent black</p>
+
+
+<script>
+var t = async_test("Changing size resets canvas to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 20,20, 255,0,0,255);
+ canvas.width = 50;
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.worker.js
new file mode 100644
index 0000000000..6074d4f2a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.different.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.different
+// Description:Changing size resets canvas to transparent black
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Changing size resets canvas to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 20,20, 255,0,0,255);
+ canvas.width = 50;
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.html
new file mode 100644
index 0000000000..387b4d1f1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.gradient</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.gradient</h1>
+<p class="desc">Resetting the canvas state does not invalidate any existing gradients</p>
+
+
+<script>
+var t = async_test("Resetting the canvas state does not invalidate any existing gradients");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 50;
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.worker.js
new file mode 100644
index 0000000000..9029b48056
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.gradient.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.gradient
+// Description:Resetting the canvas state does not invalidate any existing gradients
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Resetting the canvas state does not invalidate any existing gradients");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 50;
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.html
new file mode 100644
index 0000000000..3a887a8f1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.path</h1>
+<p class="desc">Resetting the canvas state resets the current path</p>
+
+
+<script>
+var t = async_test("Resetting the canvas state resets the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.rect(0, 0, 100, 50);
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.worker.js
new file mode 100644
index 0000000000..0ab12872ae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.path.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.path
+// Description:Resetting the canvas state resets the current path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Resetting the canvas state resets the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.rect(0, 0, 100, 50);
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.html
new file mode 100644
index 0000000000..d822f90917
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.pattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.pattern</h1>
+<p class="desc">Resetting the canvas state does not invalidate any existing patterns</p>
+
+
+<script>
+var t = async_test("Resetting the canvas state does not invalidate any existing patterns");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 30;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 30, 50);
+ var p = ctx.createPattern(canvas, 'repeat-x');
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = p;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.worker.js
new file mode 100644
index 0000000000..c4870a6f99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.pattern.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.pattern
+// Description:Resetting the canvas state does not invalidate any existing patterns
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Resetting the canvas state does not invalidate any existing patterns");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 30;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 30, 50);
+ var p = ctx.createPattern(canvas, 'repeat-x');
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = p;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.html
new file mode 100644
index 0000000000..19394def37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.same</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.same</h1>
+<p class="desc">Setting size (not changing the value) resets canvas to transparent black</p>
+
+
+<script>
+var t = async_test("Setting size (not changing the value) resets canvas to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 20,20, 255,0,0,255);
+ canvas.width = 100;
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.worker.js
new file mode 100644
index 0000000000..48d24750ac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.same.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.same
+// Description:Setting size (not changing the value) resets canvas to transparent black
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting size (not changing the value) resets canvas to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 20,20, 255,0,0,255);
+ canvas.width = 100;
+ _assertPixel(canvas, 20,20, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.html
new file mode 100644
index 0000000000..ee3b5abf10
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.initial.reset.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.initial.reset.transform</h1>
+<p class="desc">Resetting the canvas state resets the current transformation matrix</p>
+
+
+<script>
+var t = async_test("Resetting the canvas state resets the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.scale(0.1, 0.1);
+ canvas.width = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.worker.js
new file mode 100644
index 0000000000..218e64fa5d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.initial.reset.transform.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.initial.reset.transform
+// Description:Resetting the canvas state resets the current transformation matrix
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Resetting the canvas state resets the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 100;
+ ctx.scale(0.1, 0.1);
+ canvas.width = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.html
new file mode 100644
index 0000000000..0e7e10cd24
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.readonly</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.readonly</h1>
+<p class="desc">canvas is readonly</p>
+
+
+<script>
+var t = async_test("canvas is readonly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var d = ctx.canvas;
+ _assertDifferent(offscreenCanvas2, d, "offscreenCanvas2", "d");
+ ctx.canvas = offscreenCanvas2;
+ _assertSame(ctx.canvas, d, "ctx.canvas", "d");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.worker.js
new file mode 100644
index 0000000000..bbe50dcf61
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.readonly.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.readonly
+// Description:canvas is readonly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("canvas is readonly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var d = ctx.canvas;
+ _assertDifferent(offscreenCanvas2, d, "offscreenCanvas2", "d");
+ ctx.canvas = offscreenCanvas2;
+ _assertSame(ctx.canvas, d, "ctx.canvas", "d");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.html
new file mode 100644
index 0000000000..b5de73f403
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.reference</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.reference</h1>
+<p class="desc">canvas refers back to its canvas</p>
+
+
+<script>
+var t = async_test("canvas refers back to its canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.canvas, canvas, "ctx.canvas", "canvas");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.worker.js
new file mode 100644
index 0000000000..7a1f0c5ab4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.reference.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.reference
+// Description:canvas refers back to its canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("canvas refers back to its canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.canvas, canvas, "ctx.canvas", "canvas");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.html
new file mode 100644
index 0000000000..dc7b894b20
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.default</h1>
+<p class="desc">Default width/height when attributes are missing</p>
+
+
+<script>
+var t = async_test("Default width/height when attributes are missing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 50, "canvas.height", "50");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.worker.js
new file mode 100644
index 0000000000..78fce5aef8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.default.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.default
+// Description:Default width/height when attributes are missing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Default width/height when attributes are missing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 50, "canvas.height", "50");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.html
new file mode 100644
index 0000000000..7030103524
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.idl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.idl</h1>
+<p class="desc">Getting/setting width/height IDL attributes</p>
+
+
+<script>
+var t = async_test("Getting/setting width/height IDL attributes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = "100";
+ canvas.height = "100";
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ canvas.width = "+1.5e2";
+ canvas.height = "0x96";
+ _assertSame(canvas.width, 150, "canvas.width", "150");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ canvas.width = 301.999;
+ canvas.height = 301.001;
+ _assertSame(canvas.width, 301, "canvas.width", "301");
+ _assertSame(canvas.height, 301, "canvas.height", "301");
+ assert_throws_js(TypeError, function() { canvas.width = "400x"; });
+ assert_throws_js(TypeError, function() { canvas.height = "foo"; });
+ _assertSame(canvas.width, 301, "canvas.width", "301");
+ _assertSame(canvas.height, 301, "canvas.height", "301");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.html
new file mode 100644
index 0000000000..347c460cd7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.idl.set.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.idl.set.zero</h1>
+<p class="desc">Setting width/height IDL attributes to 0</p>
+
+
+<script>
+var t = async_test("Setting width/height IDL attributes to 0");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 0;
+ canvas.height = 0;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.worker.js
new file mode 100644
index 0000000000..21535da0eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.set.zero.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.idl.set.zero
+// Description:Setting width/height IDL attributes to 0
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting width/height IDL attributes to 0");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 0;
+ canvas.height = 0;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.worker.js
new file mode 100644
index 0000000000..a00201f18c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.idl.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.idl
+// Description:Getting/setting width/height IDL attributes
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Getting/setting width/height IDL attributes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = "100";
+ canvas.height = "100";
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ canvas.width = "+1.5e2";
+ canvas.height = "0x96";
+ _assertSame(canvas.width, 150, "canvas.width", "150");
+ _assertSame(canvas.height, 150, "canvas.height", "150");
+ canvas.width = 301.999;
+ canvas.height = 301.001;
+ _assertSame(canvas.width, 301, "canvas.width", "301");
+ _assertSame(canvas.height, 301, "canvas.height", "301");
+ assert_throws_js(TypeError, function() { canvas.width = "400x"; });
+ assert_throws_js(TypeError, function() { canvas.height = "foo"; });
+ _assertSame(canvas.width, 301, "canvas.width", "301");
+ _assertSame(canvas.height, 301, "canvas.height", "301");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html
new file mode 100644
index 0000000000..c3fa3b4ba6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.decimal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.decimal</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '100.999';
+ canvas.height = '100.999';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.worker.js
new file mode 100644
index 0000000000..1267bb205a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.decimal.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.decimal
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '100.999';
+ canvas.height = '100.999';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.html
new file mode 100644
index 0000000000..ea6f811b8f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.em</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.em</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '100em'; });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.worker.js
new file mode 100644
index 0000000000..2f0642d997
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.em.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.em
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '100em'; });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.html
new file mode 100644
index 0000000000..d38c159a6e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.empty</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '';
+ canvas.height = '';
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.worker.js
new file mode 100644
index 0000000000..b1eee3a5b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.empty.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.empty
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '';
+ canvas.height = '';
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.html
new file mode 100644
index 0000000000..2aa8245a49
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.exp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.exp</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '100e1';
+ canvas.height = '100e1';
+ _assertSame(canvas.width, 1000.0, "canvas.width", "1000.0");
+ _assertSame(canvas.height, 1000.0, "canvas.height", "1000.0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.worker.js
new file mode 100644
index 0000000000..8c8b4cc715
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.exp.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.exp
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '100e1';
+ canvas.height = '100e1';
+ _assertSame(canvas.width, 1000.0, "canvas.width", "1000.0");
+ _assertSame(canvas.height, 1000.0, "canvas.height", "1000.0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.html
new file mode 100644
index 0000000000..4b0e47b53f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.hex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.hex</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '0x100';
+ canvas.height = '0x100';
+ _assertSame(canvas.width, 256, "canvas.width", "256");
+ _assertSame(canvas.height, 256, "canvas.height", "256");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.worker.js
new file mode 100644
index 0000000000..b685aaffa2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.hex.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.hex
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '0x100';
+ canvas.height = '0x100';
+ _assertSame(canvas.width, 256, "canvas.width", "256");
+ _assertSame(canvas.height, 256, "canvas.height", "256");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.html
new file mode 100644
index 0000000000..a619651f78
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.junk</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.junk</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '#!?'; });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.worker.js
new file mode 100644
index 0000000000..f4822f4bd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.junk.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.junk
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '#!?'; });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.html
new file mode 100644
index 0000000000..c7f12fdb02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.minus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.minus</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '-100'; });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.worker.js
new file mode 100644
index 0000000000..322b992eda
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.minus.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.minus
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '-100'; });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.html
new file mode 100644
index 0000000000..7bfb7658d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.octal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.octal</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '0100';
+ canvas.height = '0100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.worker.js
new file mode 100644
index 0000000000..ce518e35c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.octal.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.octal
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '0100';
+ canvas.height = '0100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html
new file mode 100644
index 0000000000..16dace16ea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.onlyspace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.onlyspace</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = ' ';
+ canvas.height = ' ';
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.worker.js
new file mode 100644
index 0000000000..c186a4c08b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.onlyspace.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.onlyspace
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = ' ';
+ canvas.height = ' ';
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.html
new file mode 100644
index 0000000000..040a775ec7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.percent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.percent</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '100%'; });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.worker.js
new file mode 100644
index 0000000000..f38a1862d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.percent.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.percent
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '100%'; });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.html
new file mode 100644
index 0000000000..50c8467e2c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.plus</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.plus</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '+100';
+ canvas.height = '+100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.worker.js
new file mode 100644
index 0000000000..bb45a1e4fa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.plus.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.plus
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '+100';
+ canvas.height = '+100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.html
new file mode 100644
index 0000000000..cb1f3ae840
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.space</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.space</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = ' 100';
+ canvas.height = ' 100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.worker.js
new file mode 100644
index 0000000000..967a0939d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.space.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.space
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = ' 100';
+ canvas.height = ' 100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html
new file mode 100644
index 0000000000..8d8d481aef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.trailingjunk</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.trailingjunk</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '100#!?'; });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.worker.js
new file mode 100644
index 0000000000..ffebcd97f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.trailingjunk.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.trailingjunk
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { canvas.width = '100#!?'; });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html
new file mode 100644
index 0000000000..f6e29d6cb6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.whitespace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.whitespace</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = ' 100';
+ canvas.height = ' 100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.worker.js
new file mode 100644
index 0000000000..fb7ba47975
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.whitespace.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.whitespace
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = ' 100';
+ canvas.height = ' 100';
+ _assertSame(canvas.width, 100, "canvas.width", "100");
+ _assertSame(canvas.height, 100, "canvas.height", "100");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.html
new file mode 100644
index 0000000000..07e924a603
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.parse.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.parse.zero</h1>
+<p class="desc">Parsing of non-negative integers</p>
+
+
+<script>
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '0';
+ canvas.height = '0';
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.worker.js
new file mode 100644
index 0000000000..1363bff772
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.parse.zero.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.parse.zero
+// Description:Parsing of non-negative integers
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Parsing of non-negative integers");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = '0';
+ canvas.height = '0';
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.html
new file mode 100644
index 0000000000..d24b2cc49f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.reflect.setidl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.reflect.setidl</h1>
+<p class="desc">Setting IDL attributes updates IDL and content attributes</p>
+
+
+<script>
+var t = async_test("Setting IDL attributes updates IDL and content attributes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 120;
+ canvas.height = 60;
+ _assertSame(canvas.width, 120, "canvas.width", "120");
+ _assertSame(canvas.height, 60, "canvas.height", "60");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.worker.js
new file mode 100644
index 0000000000..3b3ced124a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidl.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.reflect.setidl
+// Description:Setting IDL attributes updates IDL and content attributes
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting IDL attributes updates IDL and content attributes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 120;
+ canvas.height = 60;
+ _assertSame(canvas.width, 120, "canvas.width", "120");
+ _assertSame(canvas.height, 60, "canvas.height", "60");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.html
new file mode 100644
index 0000000000..c4839afb0e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.attributes.reflect.setidlzero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.attributes.reflect.setidlzero</h1>
+<p class="desc">Setting IDL attributes to 0 updates IDL and content attributes</p>
+
+
+<script>
+var t = async_test("Setting IDL attributes to 0 updates IDL and content attributes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 0;
+ canvas.height = 0;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.worker.js
new file mode 100644
index 0000000000..d440a9d807
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.attributes.reflect.setidlzero.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.attributes.reflect.setidlzero
+// Description:Setting IDL attributes to 0 updates IDL and content attributes
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting IDL attributes to 0 updates IDL and content attributes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 0;
+ canvas.height = 0;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.html b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.html
new file mode 100644
index 0000000000..b220f8f2ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.canvas.host.size.large</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.canvas.host.size.large</h1>
+<p class="desc"></p>
+
+<p class="notes">Not sure how reasonable this is, but the spec doesn't say there's an upper limit on the size.
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var n = 2147483647; // 2^31 - 1, which should be supported by any sensible definition of "long"
+ canvas.width = n;
+ canvas.height = n;
+ _assertSame(canvas.width, n, "canvas.width", "n");
+ _assertSame(canvas.height, n, "canvas.height", "n");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.worker.js b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.worker.js
new file mode 100644
index 0000000000..a763704131
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.canvas.host.size.large
+// Description:
+// Note:<p class="notes">Not sure how reasonable this is, but the spec doesn't say there's an upper limit on the size.
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var n = 2147483647; // 2^31 - 1, which should be supported by any sensible definition of "long"
+ canvas.width = n;
+ canvas.height = n;
+ _assertSame(canvas.width, n, "canvas.width", "n");
+ _assertSame(canvas.height, n, "canvas.height", "n");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.html
new file mode 100644
index 0000000000..eac9dbdef9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.clear</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.worker.js
new file mode 100644
index 0000000000..2f9fb92da1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.clear.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.clear
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.html
new file mode 100644
index 0000000000..7c9771c10f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.copy</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,191, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.worker.js
new file mode 100644
index 0000000000..5b071665ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.copy.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.copy
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,191, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.html
new file mode 100644
index 0000000000..d9c34ded0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.destination-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 128,255,128,191, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.worker.js
new file mode 100644
index 0000000000..58828619a3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-atop.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.destination-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 128,255,128,191, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.html
new file mode 100644
index 0000000000..9c1a8dbf47
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.destination-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,96, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.worker.js
new file mode 100644
index 0000000000..4e2ec058ec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-in.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.destination-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,96, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.html
new file mode 100644
index 0000000000..10c5baf485
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.destination-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,32, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.worker.js
new file mode 100644
index 0000000000..bc7be056a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-out.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.destination-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,32, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.html
new file mode 100644
index 0000000000..0e2ece9eab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.destination-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 109,255,146,223, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.worker.js
new file mode 100644
index 0000000000..c1d15a0959
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.destination-over.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.destination-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 109,255,146,223, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.html
new file mode 100644
index 0000000000..392bb855f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.lighter</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,128,255, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.worker.js
new file mode 100644
index 0000000000..df494f22d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.lighter.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.lighter
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,128,255, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.html
new file mode 100644
index 0000000000..f7d4da4d84
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.source-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.worker.js
new file mode 100644
index 0000000000..781a89c1b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-atop.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.source-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.html
new file mode 100644
index 0000000000..69e57e2578
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.source-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.worker.js
new file mode 100644
index 0000000000..484399acfc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-in.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.source-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.html
new file mode 100644
index 0000000000..eb9dbd0810
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.source-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.worker.js
new file mode 100644
index 0000000000..d0d6cc12e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-out.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.source-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.html
new file mode 100644
index 0000000000..98629de8a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.source-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 219,255,36,223, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.worker.js
new file mode 100644
index 0000000000..848fbf3bec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.source-over.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.source-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 219,255,36,223, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.html
new file mode 100644
index 0000000000..bc69c8a08b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.canvas.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.canvas.xor</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.worker.js
new file mode 100644
index 0000000000..250bba4bac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.canvas.xor.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.canvas.xor
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.html
new file mode 100644
index 0000000000..f0e9989cce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.clear</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.worker.js
new file mode 100644
index 0000000000..abae05ac24
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.clear.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.clear
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.html
new file mode 100644
index 0000000000..2471cd7edd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.copy</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.worker.js
new file mode 100644
index 0000000000..dfebd98a6b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.copy.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.copy
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.html
new file mode 100644
index 0000000000..efb5e7ab6d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.destination-atop</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.worker.js
new file mode 100644
index 0000000000..9c8fa79c5a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-atop.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.destination-atop
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.html
new file mode 100644
index 0000000000..bc467a1d99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.destination-in</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.worker.js
new file mode 100644
index 0000000000..a98624f824
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-in.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.destination-in
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.html
new file mode 100644
index 0000000000..674ec3aeb8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.destination-out</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.worker.js
new file mode 100644
index 0000000000..0cf915d1c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-out.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.destination-out
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.html
new file mode 100644
index 0000000000..6abd0af788
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.destination-over</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.worker.js
new file mode 100644
index 0000000000..b1b5eee6f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.destination-over.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.destination-over
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.html
new file mode 100644
index 0000000000..00782a683e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.lighter</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.worker.js
new file mode 100644
index 0000000000..8576d9db7c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.lighter.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.lighter
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.html
new file mode 100644
index 0000000000..a7dc6a3bc8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.source-atop</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.worker.js
new file mode 100644
index 0000000000..fd220f52b3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-atop.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.source-atop
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.html
new file mode 100644
index 0000000000..3f24076161
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.source-in</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.worker.js
new file mode 100644
index 0000000000..0c61fb7794
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-in.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.source-in
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.html
new file mode 100644
index 0000000000..4033893b2b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.source-out</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.worker.js
new file mode 100644
index 0000000000..ec57663af1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-out.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.source-out
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.html
new file mode 100644
index 0000000000..5b48161c65
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.source-over</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.worker.js
new file mode 100644
index 0000000000..15c322ee59
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.source-over.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.source-over
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.html
new file mode 100644
index 0000000000..0156e4a2ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.clip.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.clip.xor</h1>
+<p class="desc">fill() does not affect pixels outside the clip region.</p>
+
+
+<script>
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.worker.js
new file mode 100644
index 0000000000..30bae44aa9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.clip.xor.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.clip.xor
+// Description:fill() does not affect pixels outside the clip region.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() does not affect pixels outside the clip region.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.html
new file mode 100644
index 0000000000..c096253e27
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.canvas</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.worker.js
new file mode 100644
index 0000000000..ede4733a83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvas.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.canvas
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.html
new file mode 100644
index 0000000000..2eb06556ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.canvascopy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.canvascopy</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy'
+ ctx.globalAlpha = 0.51;
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,130, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.worker.js
new file mode 100644
index 0000000000..f61859d231
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvascopy.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.canvascopy
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy'
+ ctx.globalAlpha = 0.51;
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,130, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.html
new file mode 100644
index 0000000000..cbfe111092
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.canvaspattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.canvaspattern</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = ctx.createPattern(offscreenCanvas2, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.worker.js
new file mode 100644
index 0000000000..7ecd568fae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.canvaspattern.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.canvaspattern
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = ctx.createPattern(offscreenCanvas2, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.html
new file mode 100644
index 0000000000..57c3745d4e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.default</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.globalAlpha, 1.0, "ctx.globalAlpha", "1.0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.worker.js
new file mode 100644
index 0000000000..8acbd6a37f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.default.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.default
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.globalAlpha, 1.0, "ctx.globalAlpha", "1.0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.html
new file mode 100644
index 0000000000..7b31db198e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.fill</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.fill</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.worker.js
new file mode 100644
index 0000000000..3ea8aa7ebe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.fill.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.fill
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.html
new file mode 100644
index 0000000000..4590a2ea1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.image</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js
new file mode 100644
index 0000000000..20953417cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.image
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.html
new file mode 100644
index 0000000000..2fcbca84d2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.imagepattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.imagepattern</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js
new file mode 100644
index 0000000000..c6ab163b65
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.imagepattern
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.html
new file mode 100644
index 0000000000..9bcf85f1c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ ctx.globalAlpha = Infinity;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = -Infinity;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = NaN;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.worker.js
new file mode 100644
index 0000000000..4ca577ad44
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.invalid.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ ctx.globalAlpha = Infinity;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = -Infinity;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = NaN;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.html
new file mode 100644
index 0000000000..c064a41cbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.globalAlpha.range</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.globalAlpha.range</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = 1.1;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = -0.1;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = 0;
+ _assertSame(ctx.globalAlpha, 0, "ctx.globalAlpha", "0");
+ ctx.globalAlpha = 1;
+ _assertSame(ctx.globalAlpha, 1, "ctx.globalAlpha", "1");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.worker.js
new file mode 100644
index 0000000000..76ea819909
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.range.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.globalAlpha.range
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = 1.1;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = -0.1;
+ _assertSame(ctx.globalAlpha, a, "ctx.globalAlpha", "a");
+ ctx.globalAlpha = 0;
+ _assertSame(ctx.globalAlpha, 0, "ctx.globalAlpha", "0");
+ ctx.globalAlpha = 1;
+ _assertSame(ctx.globalAlpha, 1, "ctx.globalAlpha", "1");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.html
new file mode 100644
index 0000000000..f001c4409c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.clear</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.worker.js
new file mode 100644
index 0000000000..f152cee739
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.clear.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.clear
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.html
new file mode 100644
index 0000000000..d472a0682d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.copy</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,191, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.worker.js
new file mode 100644
index 0000000000..05ccab1711
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.copy.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.copy
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,191, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.html
new file mode 100644
index 0000000000..753c460ec6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.destination-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 128,255,128,191, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.worker.js
new file mode 100644
index 0000000000..43648142d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-atop.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.destination-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 128,255,128,191, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.html
new file mode 100644
index 0000000000..df9f1ae786
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.destination-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,96, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.worker.js
new file mode 100644
index 0000000000..2787237c56
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-in.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.destination-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,96, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.html
new file mode 100644
index 0000000000..6981ca1d72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.destination-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,32, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.worker.js
new file mode 100644
index 0000000000..e030f533a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-out.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.destination-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,32, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.html
new file mode 100644
index 0000000000..3360115133
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.destination-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 109,255,146,223, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.worker.js
new file mode 100644
index 0000000000..964df4ae8b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.destination-over.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.destination-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 109,255,146,223, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.html
new file mode 100644
index 0000000000..25f2a7684a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.lighter</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,128,255, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.worker.js
new file mode 100644
index 0000000000..f3828b6411
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.lighter.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.lighter
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,128,255, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.html
new file mode 100644
index 0000000000..cfa386cfd1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.source-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.worker.js
new file mode 100644
index 0000000000..7b1618e3f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-atop.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.source-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.html
new file mode 100644
index 0000000000..cc44706b1c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.source-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.worker.js
new file mode 100644
index 0000000000..9accfb35c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-in.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.source-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.html
new file mode 100644
index 0000000000..bc2f7f2633
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.source-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.worker.js
new file mode 100644
index 0000000000..8e8d9116bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-out.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.source-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,96, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.html
new file mode 100644
index 0000000000..88a8176f7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.source-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 219,255,36,223, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.worker.js
new file mode 100644
index 0000000000..679aa19ef9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.source-over.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.source-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 219,255,36,223, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.html
new file mode 100644
index 0000000000..ef149b2f99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.image.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.image.xor</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.worker.js
new file mode 100644
index 0000000000..83a04928b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.image.xor.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.image.xor
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 191,255,64,128, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.html
new file mode 100644
index 0000000000..f21b92f83e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.casesensitive</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.casesensitive</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'Source-over';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.worker.js
new file mode 100644
index 0000000000..db1b54f5ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.casesensitive.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.casesensitive
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'Source-over';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.html
new file mode 100644
index 0000000000..02496b6d89
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.clear</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'clear';
+ _assertSame(ctx.globalCompositeOperation, 'clear', "ctx.globalCompositeOperation", "'clear'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.worker.js
new file mode 100644
index 0000000000..947dd16c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.clear.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.clear
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'clear';
+ _assertSame(ctx.globalCompositeOperation, 'clear', "ctx.globalCompositeOperation", "'clear'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.html
new file mode 100644
index 0000000000..29dc039157
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.darker</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.darker</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'darker';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.worker.js
new file mode 100644
index 0000000000..528f6b93d9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.darker.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.darker
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'darker';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.html
new file mode 100644
index 0000000000..106c0e73f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.default</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.globalCompositeOperation, 'source-over', "ctx.globalCompositeOperation", "'source-over'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.worker.js
new file mode 100644
index 0000000000..ffdb75ad75
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.default.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.default
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.globalCompositeOperation, 'source-over', "ctx.globalCompositeOperation", "'source-over'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.html
new file mode 100644
index 0000000000..1ade1a4e54
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.get</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.get</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var modes = ['source-atop', 'source-in', 'source-out', 'source-over',
+ 'destination-atop', 'destination-in', 'destination-out', 'destination-over',
+ 'lighter', 'copy', 'xor'];
+ for (var i = 0; i < modes.length; ++i)
+ {
+ ctx.globalCompositeOperation = modes[i];
+ _assertSame(ctx.globalCompositeOperation, modes[i], "ctx.globalCompositeOperation", "modes[\""+(i)+"\"]");
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.worker.js
new file mode 100644
index 0000000000..a266f1d537
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.get.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.get
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var modes = ['source-atop', 'source-in', 'source-out', 'source-over',
+ 'destination-atop', 'destination-in', 'destination-out', 'destination-over',
+ 'lighter', 'copy', 'xor'];
+ for (var i = 0; i < modes.length; ++i)
+ {
+ ctx.globalCompositeOperation = modes[i];
+ _assertSame(ctx.globalCompositeOperation, modes[i], "ctx.globalCompositeOperation", "modes[\""+(i)+"\"]");
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.html
new file mode 100644
index 0000000000..08def9a516
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.highlight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.highlight</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'highlight';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.worker.js
new file mode 100644
index 0000000000..800c4605e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.highlight.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.highlight
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'highlight';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.html
new file mode 100644
index 0000000000..f021d40b53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.nullsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.nullsuffix</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'source-over\0';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.worker.js
new file mode 100644
index 0000000000..1c1d5124a3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.nullsuffix.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.nullsuffix
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'source-over\0';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.html
new file mode 100644
index 0000000000..df934336c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'over';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.worker.js
new file mode 100644
index 0000000000..f75a462e93
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.over.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'over';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.html
new file mode 100644
index 0000000000..c649102cc7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.operation.unrecognised</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.operation.unrecognised</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'nonexistent';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.worker.js
new file mode 100644
index 0000000000..23a6630e82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.operation.unrecognised.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.operation.unrecognised
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'nonexistent';
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.html
new file mode 100644
index 0000000000..9bb03097fc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.clear</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.worker.js
new file mode 100644
index 0000000000..c49ec7a322
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.clear.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.clear
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.html
new file mode 100644
index 0000000000..a5bcda9337
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.copy</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.worker.js
new file mode 100644
index 0000000000..ee4baecbd2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.copy.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.copy
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.html
new file mode 100644
index 0000000000..c5554ff240
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.destination-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.worker.js
new file mode 100644
index 0000000000..8829971c6b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-atop.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.destination-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.html
new file mode 100644
index 0000000000..8e620e2714
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.destination-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.worker.js
new file mode 100644
index 0000000000..fe9e461717
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-in.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.destination-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.html
new file mode 100644
index 0000000000..06da4d3650
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.destination-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.worker.js
new file mode 100644
index 0000000000..05ec2fae16
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-out.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.destination-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.html
new file mode 100644
index 0000000000..e62c3fea70
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.destination-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.worker.js
new file mode 100644
index 0000000000..5c171fc282
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.destination-over.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.destination-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,255,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.html
new file mode 100644
index 0000000000..2e09805cc1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.lighter</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,255,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.worker.js
new file mode 100644
index 0000000000..353389221d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.lighter.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.lighter
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,255,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.html
new file mode 100644
index 0000000000..dd36e0e622
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.source-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.worker.js
new file mode 100644
index 0000000000..27e1c253ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-atop.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.source-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.html
new file mode 100644
index 0000000000..83dcc8e523
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.source-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.worker.js
new file mode 100644
index 0000000000..7dfdb07075
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-in.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.source-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.html
new file mode 100644
index 0000000000..3067c44fcd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.source-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.worker.js
new file mode 100644
index 0000000000..8252549267
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-out.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.source-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.html
new file mode 100644
index 0000000000..059655334f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.source-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.worker.js
new file mode 100644
index 0000000000..1c82a95c36
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.source-over.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.source-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 255,255,0,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.html
new file mode 100644
index 0000000000..822c9c70d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.solid.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.solid.xor</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.worker.js
new file mode 100644
index 0000000000..cb098ebab1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.solid.xor.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.solid.xor
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.html
new file mode 100644
index 0000000000..81d781acc7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.clear</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.clear</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.worker.js
new file mode 100644
index 0000000000..8f30463c41
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.clear.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.clear
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'clear';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.html
new file mode 100644
index 0000000000..ab3a97226f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.copy</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,191, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.worker.js
new file mode 100644
index 0000000000..6b9ea9dbcf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.copy.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.copy
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,191, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.html
new file mode 100644
index 0000000000..5e580f5a67
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.destination-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,128,128,191, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.worker.js
new file mode 100644
index 0000000000..3247632e53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-atop.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.destination-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,128,128,191, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.html
new file mode 100644
index 0000000000..9f0ec56357
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.destination-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,96, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.worker.js
new file mode 100644
index 0000000000..c9205230c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-in.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.destination-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,96, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.html
new file mode 100644
index 0000000000..a1abe3eed9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.destination-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.destination-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,32, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.worker.js
new file mode 100644
index 0000000000..1a5409589b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-out.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.destination-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,32, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.html
new file mode 100644
index 0000000000..4f0305f27f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.destination-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.destination-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,146,109,223, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.worker.js
new file mode 100644
index 0000000000..37eb785d67
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.destination-over.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.destination-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,146,109,223, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.html
new file mode 100644
index 0000000000..6a2f5df71f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.lighter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.lighter</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,128,191,255, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.worker.js
new file mode 100644
index 0000000000..3d890affd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.lighter.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.lighter
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,128,191,255, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.html
new file mode 100644
index 0000000000..bac035cea3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.source-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.source-atop</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,64,191,128, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.worker.js
new file mode 100644
index 0000000000..d56ef6e2e4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-atop.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.source-atop
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,64,191,128, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.html
new file mode 100644
index 0000000000..5dd67046ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.source-in</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,96, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.worker.js
new file mode 100644
index 0000000000..58ad13bc5e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-in.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.source-in
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,96, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.html
new file mode 100644
index 0000000000..56b1d4ad46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.source-out</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,96, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.worker.js
new file mode 100644
index 0000000000..62ec895955
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-out.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.source-out
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,255,96, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.html
new file mode 100644
index 0000000000..f038d44a54
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.source-over</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.source-over</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,36,219,223, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.worker.js
new file mode 100644
index 0000000000..494a45f848
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.source-over.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.source-over
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,36,219,223, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.html
new file mode 100644
index 0000000000..bbab8a086c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.transparent.xor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.transparent.xor</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,64,191,128, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.worker.js
new file mode 100644
index 0000000000..4b65667ed8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.transparent.xor.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.transparent.xor
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,64,191,128, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.html
new file mode 100644
index 0000000000..0ff9d851dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.fill.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.fill.copy</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.worker.js
new file mode 100644
index 0000000000..50f3ed0f22
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.copy.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.fill.copy
+// Description:fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.html
new file mode 100644
index 0000000000..2d2f92edb5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.fill.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.fill.destination-atop</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.worker.js
new file mode 100644
index 0000000000..9a84b0894a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-atop.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.fill.destination-atop
+// Description:fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.html
new file mode 100644
index 0000000000..5670b321a6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.fill.destination-in</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.fill.destination-in</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.worker.js
new file mode 100644
index 0000000000..aa24ba97a6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.destination-in.worker.js
@@ -0,0 +1,29 @@
+// META: timeout=long
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.fill.destination-in
+// Description:fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.html
new file mode 100644
index 0000000000..58347442bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.fill.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.fill.source-in</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.worker.js
new file mode 100644
index 0000000000..33e326d1f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-in.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.fill.source-in
+// Description:fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.html
new file mode 100644
index 0000000000..ea44ca4f04
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.fill.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.fill.source-out</h1>
+<p class="desc">fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.worker.js
new file mode 100644
index 0000000000..1d39b364b0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.fill.source-out.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.fill.source-out
+// Description:fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ ctx.fillStyle = 'rgba(0, 0, 255, 0.75)';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.html
new file mode 100644
index 0000000000..0ecb5ed01d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.image.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.image.copy</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.worker.js
new file mode 100644
index 0000000000..0e56b4f71d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.copy.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.image.copy
+// Description:drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.html
new file mode 100644
index 0000000000..00a4465d0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.image.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.image.destination-atop</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.worker.js
new file mode 100644
index 0000000000..4c4a0a4c95
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-atop.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.image.destination-atop
+// Description:drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.html
new file mode 100644
index 0000000000..f5f8c68c51
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.image.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.image.destination-in</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.worker.js
new file mode 100644
index 0000000000..af1dc50c9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.destination-in.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.image.destination-in
+// Description:drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.html
new file mode 100644
index 0000000000..2e1fe84b60
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.image.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.image.source-in</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.worker.js
new file mode 100644
index 0000000000..490ce4d001
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-in.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.image.source-in
+// Description:drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.html
new file mode 100644
index 0000000000..1f5be066ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.image.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.image.source-out</h1>
+<p class="desc">drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.worker.js
new file mode 100644
index 0000000000..3647e34574
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.image.source-out.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.image.source-out
+// Description:drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ _assertPixelApprox(canvas, 15,15, 0,0,0,0, 5);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.html
new file mode 100644
index 0000000000..10a9960559
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.nocontext.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.nocontext.copy</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.worker.js
new file mode 100644
index 0000000000..1f285eae4c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.copy.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.nocontext.copy
+// Description:drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.html
new file mode 100644
index 0000000000..ead1299cf0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.nocontext.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.nocontext.destination-atop</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.worker.js
new file mode 100644
index 0000000000..a9beb0f27e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-atop.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.nocontext.destination-atop
+// Description:drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.html
new file mode 100644
index 0000000000..0322bfe258
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.nocontext.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.nocontext.destination-in</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.worker.js
new file mode 100644
index 0000000000..e146e2e91a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.destination-in.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.nocontext.destination-in
+// Description:drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.html
new file mode 100644
index 0000000000..0d93a0362e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.nocontext.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.nocontext.source-in</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.worker.js
new file mode 100644
index 0000000000..cce4b54bd5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-in.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.nocontext.source-in
+// Description:drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.html
new file mode 100644
index 0000000000..b6ab8cbda4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.nocontext.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.nocontext.source-out</h1>
+<p class="desc">drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.worker.js
new file mode 100644
index 0000000000..501a84f7e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.nocontext.source-out.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.nocontext.source-out
+// Description:drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.html
new file mode 100644
index 0000000000..c698c2517c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.pattern.copy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.pattern.copy</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.worker.js
new file mode 100644
index 0000000000..a5944be695
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.pattern.copy
+// Description:Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.html
new file mode 100644
index 0000000000..887570fad7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.pattern.destination-atop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.pattern.destination-atop</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js
new file mode 100644
index 0000000000..c34c748059
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.pattern.destination-atop
+// Description:Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.html
new file mode 100644
index 0000000000..7aa9e662f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.pattern.destination-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.pattern.destination-in</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.worker.js
new file mode 100644
index 0000000000..10042455a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.pattern.destination-in
+// Description:Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.html
new file mode 100644
index 0000000000..3830e92f69
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.pattern.source-in</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.pattern.source-in</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.worker.js
new file mode 100644
index 0000000000..c658da8c99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.pattern.source-in
+// Description:Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-in';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.html b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.html
new file mode 100644
index 0000000000..3c202dd556
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.composite.uncovered.pattern.source-out</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.composite.uncovered.pattern.source-out</h1>
+<p class="desc">Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.</p>
+
+
+<script>
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.worker.js
new file mode 100644
index 0000000000..86baf476fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.composite.uncovered.pattern.source-out
+// Description:Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'source-out';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,0,0,0, 5);
+ });
+ }).then(t_pass, t_fail);
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.html b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.html
new file mode 100644
index 0000000000..e754e6c6ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.conformance.requirements.basics</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.conformance.requirements.basics</h1>
+<p class="desc">void methods return undefined</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("void methods return undefined");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.save(), undefined, "ctx.save()", "undefined");
+ _assertSame(ctx.restore(), undefined, "ctx.restore()", "undefined");
+ _assertSame(ctx.scale(1, 1), undefined, "ctx.scale(1, 1)", "undefined");
+ _assertSame(ctx.rotate(0), undefined, "ctx.rotate(0)", "undefined");
+ _assertSame(ctx.translate(0, 0), undefined, "ctx.translate(0, 0)", "undefined");
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ _assertSame(ctx.transform(1, 0, 0, 1, 0, 0), undefined, "ctx.transform(1, 0, 0, 1, 0, 0)", "undefined");
+ }
+ if (ctx.setTransform) {
+ _assertSame(ctx.setTransform(1, 0, 0, 1, 0, 0), undefined, "ctx.setTransform(1, 0, 0, 1, 0, 0)", "undefined");
+ _assertSame(ctx.setTransform(), undefined, "ctx.setTransform()", "undefined");
+ }
+ _assertSame(ctx.clearRect(0, 0, 0, 0), undefined, "ctx.clearRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.fillRect(0, 0, 0, 0), undefined, "ctx.fillRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.strokeRect(0, 0, 0, 0), undefined, "ctx.strokeRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.beginPath(), undefined, "ctx.beginPath()", "undefined");
+ _assertSame(ctx.closePath(), undefined, "ctx.closePath()", "undefined");
+ _assertSame(ctx.moveTo(0, 0), undefined, "ctx.moveTo(0, 0)", "undefined");
+ _assertSame(ctx.lineTo(0, 0), undefined, "ctx.lineTo(0, 0)", "undefined");
+ _assertSame(ctx.quadraticCurveTo(0, 0, 0, 0), undefined, "ctx.quadraticCurveTo(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.bezierCurveTo(0, 0, 0, 0, 0, 0), undefined, "ctx.bezierCurveTo(0, 0, 0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.arcTo(0, 0, 0, 0, 1), undefined, "ctx.arcTo(0, 0, 0, 0, 1)", "undefined");
+ _assertSame(ctx.rect(0, 0, 0, 0), undefined, "ctx.rect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.arc(0, 0, 1, 0, 0, true), undefined, "ctx.arc(0, 0, 1, 0, 0, true)", "undefined");
+ _assertSame(ctx.fill(), undefined, "ctx.fill()", "undefined");
+ _assertSame(ctx.stroke(), undefined, "ctx.stroke()", "undefined");
+ _assertSame(ctx.clip(), undefined, "ctx.clip()", "undefined");
+ if (ctx.fillText) {
+ _assertSame(ctx.fillText('test', 0, 0), undefined, "ctx.fillText('test', 0, 0)", "undefined");
+ _assertSame(ctx.strokeText('test', 0, 0), undefined, "ctx.strokeText('test', 0, 0)", "undefined");
+ }
+ if (ctx.putImageData) {
+ _assertSame(ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0), undefined, "ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0)", "undefined");
+ }
+ _assertSame(ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white'), undefined, "ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white')", "undefined");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.worker.js b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.worker.js
new file mode 100644
index 0000000000..b16a39fd3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.basics.worker.js
@@ -0,0 +1,57 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.conformance.requirements.basics
+// Description:void methods return undefined
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("void methods return undefined");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.save(), undefined, "ctx.save()", "undefined");
+ _assertSame(ctx.restore(), undefined, "ctx.restore()", "undefined");
+ _assertSame(ctx.scale(1, 1), undefined, "ctx.scale(1, 1)", "undefined");
+ _assertSame(ctx.rotate(0), undefined, "ctx.rotate(0)", "undefined");
+ _assertSame(ctx.translate(0, 0), undefined, "ctx.translate(0, 0)", "undefined");
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ _assertSame(ctx.transform(1, 0, 0, 1, 0, 0), undefined, "ctx.transform(1, 0, 0, 1, 0, 0)", "undefined");
+ }
+ if (ctx.setTransform) {
+ _assertSame(ctx.setTransform(1, 0, 0, 1, 0, 0), undefined, "ctx.setTransform(1, 0, 0, 1, 0, 0)", "undefined");
+ _assertSame(ctx.setTransform(), undefined, "ctx.setTransform()", "undefined");
+ }
+ _assertSame(ctx.clearRect(0, 0, 0, 0), undefined, "ctx.clearRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.fillRect(0, 0, 0, 0), undefined, "ctx.fillRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.strokeRect(0, 0, 0, 0), undefined, "ctx.strokeRect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.beginPath(), undefined, "ctx.beginPath()", "undefined");
+ _assertSame(ctx.closePath(), undefined, "ctx.closePath()", "undefined");
+ _assertSame(ctx.moveTo(0, 0), undefined, "ctx.moveTo(0, 0)", "undefined");
+ _assertSame(ctx.lineTo(0, 0), undefined, "ctx.lineTo(0, 0)", "undefined");
+ _assertSame(ctx.quadraticCurveTo(0, 0, 0, 0), undefined, "ctx.quadraticCurveTo(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.bezierCurveTo(0, 0, 0, 0, 0, 0), undefined, "ctx.bezierCurveTo(0, 0, 0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.arcTo(0, 0, 0, 0, 1), undefined, "ctx.arcTo(0, 0, 0, 0, 1)", "undefined");
+ _assertSame(ctx.rect(0, 0, 0, 0), undefined, "ctx.rect(0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.arc(0, 0, 1, 0, 0, true), undefined, "ctx.arc(0, 0, 1, 0, 0, true)", "undefined");
+ _assertSame(ctx.fill(), undefined, "ctx.fill()", "undefined");
+ _assertSame(ctx.stroke(), undefined, "ctx.stroke()", "undefined");
+ _assertSame(ctx.clip(), undefined, "ctx.clip()", "undefined");
+ if (ctx.fillText) {
+ _assertSame(ctx.fillText('test', 0, 0), undefined, "ctx.fillText('test', 0, 0)", "undefined");
+ _assertSame(ctx.strokeText('test', 0, 0), undefined, "ctx.strokeText('test', 0, 0)", "undefined");
+ }
+ if (ctx.putImageData) {
+ _assertSame(ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0), undefined, "ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0)", "undefined");
+ }
+ _assertSame(ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0), undefined, "ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0)", "undefined");
+ _assertSame(ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white'), undefined, "ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white')", "undefined");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.html b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.html
new file mode 100644
index 0000000000..fe28a68e90
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.conformance.requirements.missingargs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.conformance.requirements.missingargs</h1>
+<p class="desc">Missing arguments cause TypeError</p>
+
+
+<script>
+var t = async_test("Missing arguments cause TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.scale(); });
+ assert_throws_js(TypeError, function() { ctx.scale(1); });
+ assert_throws_js(TypeError, function() { ctx.rotate(); });
+ assert_throws_js(TypeError, function() { ctx.translate(); });
+ assert_throws_js(TypeError, function() { ctx.translate(0); });
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ assert_throws_js(TypeError, function() { ctx.transform(); });
+ assert_throws_js(TypeError, function() { ctx.transform(1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1, 0); });
+ }
+ if (ctx.setTransform) {
+ assert_throws_js(TypeError, function() { ctx.setTransform(1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1, 0); });
+ }
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createPattern(canvas); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.moveTo(); });
+ assert_throws_js(TypeError, function() { ctx.moveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.lineTo(); });
+ assert_throws_js(TypeError, function() { ctx.lineTo(0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.rect(); });
+ assert_throws_js(TypeError, function() { ctx.rect(0); });
+ assert_throws_js(TypeError, function() { ctx.rect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.rect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arc(); });
+ assert_throws_js(TypeError, function() { ctx.arc(0); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1, 0); });
+ // (6th argument to arc is optional)
+ if (ctx.isPointInPath) {
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(0); });
+ }
+ if (ctx.drawFocusRing) {
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas, 0); });
+ }
+ if (ctx.fillText) {
+ assert_throws_js(TypeError, function() { ctx.fillText(); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test'); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeText(); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test'); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.measureText(); });
+ }
+ assert_throws_js(TypeError, function() { ctx.drawImage(); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(canvas, 0); });
+ // TODO: n >= 3 args on drawImage could be either a valid overload,
+ // or too few for another overload, or too many for another
+ // overload - what should happen?
+ if (ctx.createImageData) {
+ assert_throws_js(TypeError, function() { ctx.createImageData(); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(1); });
+ }
+ if (ctx.getImageData) {
+ assert_throws_js(TypeError, function() { ctx.getImageData(); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0, 1); });
+ }
+ if (ctx.putImageData) {
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ assert_throws_js(TypeError, function() { ctx.putImageData(); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0); });
+ }
+ var g = ctx.createLinearGradient(0, 0, 0, 0);
+ assert_throws_js(TypeError, function() { g.addColorStop(); });
+ assert_throws_js(TypeError, function() { g.addColorStop(0); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.worker.js b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.worker.js
new file mode 100644
index 0000000000..f249fcb881
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/conformance-requirements/2d.conformance.requirements.missingargs.worker.js
@@ -0,0 +1,136 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.conformance.requirements.missingargs
+// Description:Missing arguments cause TypeError
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Missing arguments cause TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.scale(); });
+ assert_throws_js(TypeError, function() { ctx.scale(1); });
+ assert_throws_js(TypeError, function() { ctx.rotate(); });
+ assert_throws_js(TypeError, function() { ctx.translate(); });
+ assert_throws_js(TypeError, function() { ctx.translate(0); });
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ assert_throws_js(TypeError, function() { ctx.transform(); });
+ assert_throws_js(TypeError, function() { ctx.transform(1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.transform(1, 0, 0, 1, 0); });
+ }
+ if (ctx.setTransform) {
+ assert_throws_js(TypeError, function() { ctx.setTransform(1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.setTransform(1, 0, 0, 1, 0); });
+ }
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.createPattern(canvas); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.clearRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.fillRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeRect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.moveTo(); });
+ assert_throws_js(TypeError, function() { ctx.moveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.lineTo(); });
+ assert_throws_js(TypeError, function() { ctx.lineTo(0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.quadraticCurveTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.bezierCurveTo(0, 0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arcTo(0, 0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.rect(); });
+ assert_throws_js(TypeError, function() { ctx.rect(0); });
+ assert_throws_js(TypeError, function() { ctx.rect(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.rect(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arc(); });
+ assert_throws_js(TypeError, function() { ctx.arc(0); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.arc(0, 0, 1, 0); });
+ // (6th argument to arc is optional)
+ if (ctx.isPointInPath) {
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(0); });
+ }
+ if (ctx.drawFocusRing) {
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawFocusRing(canvas, 0); });
+ }
+ if (ctx.fillText) {
+ assert_throws_js(TypeError, function() { ctx.fillText(); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test'); });
+ assert_throws_js(TypeError, function() { ctx.fillText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.strokeText(); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test'); });
+ assert_throws_js(TypeError, function() { ctx.strokeText('test', 0); });
+ assert_throws_js(TypeError, function() { ctx.measureText(); });
+ }
+ assert_throws_js(TypeError, function() { ctx.drawImage(); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(canvas); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(canvas, 0); });
+ // TODO: n >= 3 args on drawImage could be either a valid overload,
+ // or too few for another overload, or too many for another
+ // overload - what should happen?
+ if (ctx.createImageData) {
+ assert_throws_js(TypeError, function() { ctx.createImageData(); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(1); });
+ }
+ if (ctx.getImageData) {
+ assert_throws_js(TypeError, function() { ctx.getImageData(); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(0, 0, 1); });
+ }
+ if (ctx.putImageData) {
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ assert_throws_js(TypeError, function() { ctx.putImageData(); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0); });
+ }
+ var g = ctx.createLinearGradient(0, 0, 0, 0);
+ assert_throws_js(TypeError, function() { g.addColorStop(); });
+ assert_throws_js(TypeError, function() { g.addColorStop(0); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.html
new file mode 100644
index 0000000000..7f1f66654d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.3arg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.3arg</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0);
+ ctx.drawImage(bitmap_red, -100, 0);
+ ctx.drawImage(bitmap_red, 100, 0);
+ ctx.drawImage(bitmap_red, 0, -50);
+ ctx.drawImage(bitmap_red, 0, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
new file mode 100644
index 0000000000..b2f860bc60
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.3arg
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0);
+ ctx.drawImage(bitmap_red, -100, 0);
+ ctx.drawImage(bitmap_red, 100, 0);
+ ctx.drawImage(bitmap_red, 0, -50);
+ ctx.drawImage(bitmap_red, 0, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.html
new file mode 100644
index 0000000000..f7bf0568c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.5arg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.5arg</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
new file mode 100644
index 0000000000..f725acf011
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.5arg
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html
new file mode 100644
index 0000000000..47e442e23e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.9arg.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.9arg.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
new file mode 100644
index 0000000000..b2d6f7d860
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.9arg.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html
new file mode 100644
index 0000000000..5e0f3f1088
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.9arg.destpos</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.9arg.destpos</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, -100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -50, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
new file mode 100644
index 0000000000..380d526f9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.9arg.destpos
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, -100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -50, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html
new file mode 100644
index 0000000000..93b587a44f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.9arg.destsize</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.9arg.destsize</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 1, 1, 1, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, -50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -25, 100, 25);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 25);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
new file mode 100644
index 0000000000..76e3e8baaf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.9arg.destsize
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 1, 1, 1, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, -50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -25, 100, 25);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 25);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html
new file mode 100644
index 0000000000..e822ab43fa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.9arg.sourcepos</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.9arg.sourcepos</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 140, 20, 100, 50, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
new file mode 100644
index 0000000000..d3525435fa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.9arg.sourcepos
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 140, 20, 100, 50, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html
new file mode 100644
index 0000000000..b99ca58768
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.9arg.sourcesize</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.9arg.sourcesize</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 0, 256, 256, 0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 51, 26);
+ ctx.fillRect(49, 24, 51, 26);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 20,20, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,20, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 20,30, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,30, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
new file mode 100644
index 0000000000..aa15efb705
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.9arg.sourcesize
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 0, 256, 256, 0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 51, 26);
+ ctx.fillRect(49, 24, 51, 26);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 20,20, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,20, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 20,30, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,30, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.html
new file mode 100644
index 0000000000..a95f3ece7a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.alpha</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
new file mode 100644
index 0000000000..958a2431b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.alpha
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.html
new file mode 100644
index 0000000000..a2afa05f01
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.broken</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.broken</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response = await fetch('/images/broken.png');
+ const blob = await response.blob();
+
+ await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(blob), 'The source image could not be decoded.');
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
new file mode 100644
index 0000000000..dc695b5459
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
@@ -0,0 +1,20 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.broken
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response = await fetch('/images/broken.png');
+ const blob = await response.blob();
+
+ await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(blob), 'The source image could not be decoded.');
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.html
new file mode 100644
index 0000000000..e5eeedea6e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.canvas</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.worker.js
new file mode 100644
index 0000000000..1f18cd7723
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.canvas.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.canvas
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.html
new file mode 100644
index 0000000000..a828039b7b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.clip</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
new file mode 100644
index 0000000000..fea2a7bac7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.clip
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.html
new file mode 100644
index 0000000000..b32b5a12ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.composite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.composite</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
new file mode 100644
index 0000000000..0b17673b68
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.composite
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
new file mode 100644
index 0000000000..e201082e35
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.floatsource</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.floatsource</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
new file mode 100644
index 0000000000..1efc17d04d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
@@ -0,0 +1,21 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.floatsource
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.html
new file mode 100644
index 0000000000..3e43a772c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.negativedest</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.negativedest</h1>
+<p class="desc">Negative destination width/height represents the correct rectangle</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, 50, 50, 0, 50, 50, -50);
+ ctx.drawImage(bitmap, 100, 128, 50, -50, 100, 50, -50, -50);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Negative destination width/height represents the correct rectangle");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
new file mode 100644
index 0000000000..ce5cf5efad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.negativedest
+// Description:Negative destination width/height represents the correct rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, 50, 50, 0, 50, 50, -50);
+ ctx.drawImage(bitmap, 100, 128, 50, -50, 100, 50, -50, -50);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Negative destination width/height represents the correct rectangle");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.html
new file mode 100644
index 0000000000..347b634cfd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.negativedir</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.negativedir</h1>
+<p class="desc">Negative dimensions do not affect the direction of the image</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 178, 50, -100, 0, 0, 50, 100);
+ ctx.drawImage(bitmap, 0, 78, 50, 100, 50, 100, 50, -100);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Negative dimensions do not affect the direction of the image");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
new file mode 100644
index 0000000000..c82b9ba1be
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.negativedir
+// Description:Negative dimensions do not affect the direction of the image
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 178, 50, -100, 0, 0, 50, 100);
+ ctx.drawImage(bitmap, 0, 78, 50, 100, 50, 100, 50, -100);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Negative dimensions do not affect the direction of the image");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.html
new file mode 100644
index 0000000000..3e18725720
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.negativesource</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.negativesource</h1>
+<p class="desc">Negative source width/height represents the correct rectangle</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, -100, 50, 0, 0, 50, 50);
+ ctx.drawImage(bitmap, 100, 128, -100, -50, 50, 0, 50, 50);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Negative source width/height represents the correct rectangle");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
new file mode 100644
index 0000000000..f92d15774d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.negativesource
+// Description:Negative source width/height represents the correct rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, -100, 50, 0, 0, 50, 50);
+ ctx.drawImage(bitmap, 100, 128, -100, -50, 50, 0, 50, 50);
+ _assertPixelApprox(canvas, 1,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 48,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,1, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Negative source width/height represents the correct rectangle");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html
new file mode 100644
index 0000000000..93dccd3d4e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html
@@ -0,0 +1,328 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.nonfinite</h1>
+<p class="desc">drawImage() with Infinity/NaN is ignored</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, Infinity, 0);
+ ctx.drawImage(bitmap, -Infinity, 0);
+ ctx.drawImage(bitmap, NaN, 0);
+ ctx.drawImage(bitmap, 0, Infinity);
+ ctx.drawImage(bitmap, 0, -Infinity);
+ ctx.drawImage(bitmap, 0, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, -Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, NaN, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, -Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, NaN, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, -Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, NaN, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, -Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, -Infinity, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, NaN, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, -Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, NaN, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, -Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, NaN, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, -Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, NaN, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, -Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, NaN, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, -Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, NaN, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, -Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, NaN, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, -Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+}, "drawImage() with Infinity/NaN is ignored");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
new file mode 100644
index 0000000000..a56f09e0b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
@@ -0,0 +1,324 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.nonfinite
+// Description:drawImage() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, Infinity, 0);
+ ctx.drawImage(bitmap, -Infinity, 0);
+ ctx.drawImage(bitmap, NaN, 0);
+ ctx.drawImage(bitmap, 0, Infinity);
+ ctx.drawImage(bitmap, 0, -Infinity);
+ ctx.drawImage(bitmap, 0, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, -Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, NaN, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, -Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, NaN, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, -Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, NaN, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, -Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, -Infinity, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, NaN, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, -Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, NaN, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, -Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, NaN, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, -Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, NaN, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, -Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, NaN, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, -Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, NaN, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, -Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, NaN, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, -Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, NaN);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, 50);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+}, "drawImage() with Infinity/NaN is ignored");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.html
new file mode 100644
index 0000000000..1182baec3b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.nowrap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.nowrap</h1>
+<p class="desc">Stretched images do not get pixels wrapping around the edges</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, -1950, 0, 2000, 50);
+ _assertPixelApprox(canvas, 45,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 55,25, 0,255,0,255, 2);
+
+}, "Stretched images do not get pixels wrapping around the edges");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
new file mode 100644
index 0000000000..47a77834c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.nowrap
+// Description:Stretched images do not get pixels wrapping around the edges
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, -1950, 0, 2000, 50);
+ _assertPixelApprox(canvas, 45,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 55,25, 0,255,0,255, 2);
+ t.done();
+}, "Stretched images do not get pixels wrapping around the edges");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.html
new file mode 100644
index 0000000000..7a8338b353
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.null</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.drawImage(null, 0, 0); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.worker.js
new file mode 100644
index 0000000000..329abd2221
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.null.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.null
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.drawImage(null, 0, 0); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.html
new file mode 100644
index 0000000000..063353c11f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.path</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ ctx.fill();
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
new file mode 100644
index 0000000000..3259ff0258
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.path
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ ctx.fill();
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.html
new file mode 100644
index 0000000000..c5e51c2772
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.self.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.self.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.drawImage(canvas, 50, 0);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.worker.js
new file mode 100644
index 0000000000..a9af88d13a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.1.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.self.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.drawImage(canvas, 50, 0);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.html
new file mode 100644
index 0000000000..ac67476966
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.self.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.self.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 1, 100, 49);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 1);
+ ctx.drawImage(canvas, 0, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 2);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.worker.js
new file mode 100644
index 0000000000..802d2d8c52
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.self.2.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.self.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 1, 100, 49);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 1);
+ ctx.drawImage(canvas, 0, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 2);
+
+ _assertPixelApprox(canvas, 0,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.svg.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.svg.html
new file mode 100644
index 0000000000..c2ebd265cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.svg.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.svg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.svg</h1>
+<p class="desc">drawImage() of an SVG image</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const img = new Image();
+ const imageLoadPromise = new Promise((resolve, reject) => {
+ img.onload = () => resolve();
+ img.onerror = (err) => reject(err);
+ });
+ img.src = '/images/green.svg';
+ await imageLoadPromise;
+
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50, 25, 0, 255, 0, 255, 2);
+
+}, "drawImage() of an SVG image");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.html
new file mode 100644
index 0000000000..ca148c1a68
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.transform</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(100, 0);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
new file mode 100644
index 0000000000..4a4cd07be9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.transform
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(100, 0);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html
new file mode 100644
index 0000000000..96906e8fc3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.wrongtype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.wrongtype</h1>
+<p class="desc">Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError</p>
+
+
+<script>
+var t = async_test("Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.drawImage(undefined, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.drawImage("", 0, 0); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.worker.js
new file mode 100644
index 0000000000..176e418d64
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.wrongtype.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.wrongtype
+// Description:Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.drawImage(undefined, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.drawImage(0, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.drawImage("", 0, 0); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
new file mode 100644
index 0000000000..a30c300730
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.zerocanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.zerocanvas</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(0, 10);
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
+ offscreenCanvas2.width = 10;
+ offscreenCanvas2.height = 0;
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
+ offscreenCanvas2.width = 0;
+ offscreenCanvas2.height = 0;
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js
new file mode 100644
index 0000000000..41346ad328
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.zerocanvas
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(0, 10);
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
+ offscreenCanvas2.width = 10;
+ offscreenCanvas2.height = 0;
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
+ offscreenCanvas2.width = 0;
+ offscreenCanvas2.height = 0;
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.html
new file mode 100644
index 0000000000..0239c5356d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.zerosource</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.zerosource</h1>
+<p class="desc">drawImage with zero-sized source rectangle draws nothing without exception</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10, 10, 0, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+
+}, "drawImage with zero-sized source rectangle draws nothing without exception");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html
new file mode 100644
index 0000000000..77182242d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.drawImage.zerosource.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.drawImage.zerosource.image</h1>
+<p class="desc">drawImage with zero-sized source rectangle from image draws nothing without exception</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ function loadImage(src) {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = () => resolve(img);
+ img.onerror = (err) => reject(err);
+ img.src = src;
+ });
+ }
+ const img1 = await loadImage('/images/red-zerowidth.svg');
+ const img2 = await loadImage('/images/red-zeroheight.svg');
+ const img3 = await loadImage('/images/red-zerosize.svg');
+
+ ctx.drawImage(img1, 0, 0, 100, 50);
+ ctx.drawImage(img2, 0, 0, 100, 50);
+ ctx.drawImage(img3, 0, 0, 100, 50);
+ _assertPixel(canvas, 50, 25, 0, 255, 0, 255);
+
+}, "drawImage with zero-sized source rectangle from image draws nothing without exception");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
new file mode 100644
index 0000000000..a80bfff562
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.drawImage.zerosource
+// Description:drawImage with zero-sized source rectangle draws nothing without exception
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10, 10, 0, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+}, "drawImage with zero-sized source rectangle draws nothing without exception");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html
new file mode 100644
index 0000000000..02a9cb37ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.basic</h1>
+<p class="desc">clearRect clears to transparent black</p>
+
+
+<script>
+var t = async_test("clearRect clears to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.worker.js
new file mode 100644
index 0000000000..8f3a7e46de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.basic.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.basic
+// Description:clearRect clears to transparent black
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect clears to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html
new file mode 100644
index 0000000000..2f0f171601
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.clip</h1>
+<p class="desc">clearRect is affected by clipping regions</p>
+
+
+<script>
+var t = async_test("clearRect is affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.worker.js
new file mode 100644
index 0000000000..3d3caedbb1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.clip.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.clip
+// Description:clearRect is affected by clipping regions
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect is affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html
new file mode 100644
index 0000000000..45875c5270
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.globalalpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.globalalpha</h1>
+<p class="desc">clearRect is not affected by globalAlpha</p>
+
+
+<script>
+var t = async_test("clearRect is not affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0.1;
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.worker.js
new file mode 100644
index 0000000000..1b07b66cb3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalalpha.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.globalalpha
+// Description:clearRect is not affected by globalAlpha
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect is not affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0.1;
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html
new file mode 100644
index 0000000000..b57be641ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.globalcomposite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.globalcomposite</h1>
+<p class="desc">clearRect is not affected by globalCompositeOperation</p>
+
+
+<script>
+var t = async_test("clearRect is not affected by globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.worker.js
new file mode 100644
index 0000000000..e8c1c9b27c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.globalcomposite.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.globalcomposite
+// Description:clearRect is not affected by globalCompositeOperation
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect is not affected by globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.clearRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html
new file mode 100644
index 0000000000..12126dfe87
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.negative</h1>
+<p class="desc">clearRect of negative sizes works</p>
+
+
+<script>
+var t = async_test("clearRect of negative sizes works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 50, 25);
+ ctx.clearRect(100, 0, -50, 25);
+ ctx.clearRect(0, 50, 50, -25);
+ ctx.clearRect(100, 50, -50, -25);
+ _assertPixel(canvas, 25,12, 0,0,0,0);
+ _assertPixel(canvas, 75,12, 0,0,0,0);
+ _assertPixel(canvas, 25,37, 0,0,0,0);
+ _assertPixel(canvas, 75,37, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.worker.js
new file mode 100644
index 0000000000..62846aec8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.negative.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.negative
+// Description:clearRect of negative sizes works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect of negative sizes works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 50, 25);
+ ctx.clearRect(100, 0, -50, 25);
+ ctx.clearRect(0, 50, 50, -25);
+ ctx.clearRect(100, 50, -50, -25);
+ _assertPixel(canvas, 25,12, 0,0,0,0);
+ _assertPixel(canvas, 75,12, 0,0,0,0);
+ _assertPixel(canvas, 25,37, 0,0,0,0);
+ _assertPixel(canvas, 75,37, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html
new file mode 100644
index 0000000000..3327834146
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.nonfinite</h1>
+<p class="desc">clearRect() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("clearRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(Infinity, 0, 100, 50);
+ ctx.clearRect(-Infinity, 0, 100, 50);
+ ctx.clearRect(NaN, 0, 100, 50);
+ ctx.clearRect(0, Infinity, 100, 50);
+ ctx.clearRect(0, -Infinity, 100, 50);
+ ctx.clearRect(0, NaN, 100, 50);
+ ctx.clearRect(0, 0, Infinity, 50);
+ ctx.clearRect(0, 0, -Infinity, 50);
+ ctx.clearRect(0, 0, NaN, 50);
+ ctx.clearRect(0, 0, 100, Infinity);
+ ctx.clearRect(0, 0, 100, -Infinity);
+ ctx.clearRect(0, 0, 100, NaN);
+ ctx.clearRect(Infinity, Infinity, 100, 50);
+ ctx.clearRect(Infinity, Infinity, Infinity, 50);
+ ctx.clearRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.clearRect(Infinity, Infinity, 100, Infinity);
+ ctx.clearRect(Infinity, 0, Infinity, 50);
+ ctx.clearRect(Infinity, 0, Infinity, Infinity);
+ ctx.clearRect(Infinity, 0, 100, Infinity);
+ ctx.clearRect(0, Infinity, Infinity, 50);
+ ctx.clearRect(0, Infinity, Infinity, Infinity);
+ ctx.clearRect(0, Infinity, 100, Infinity);
+ ctx.clearRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.worker.js
new file mode 100644
index 0000000000..e2525df3be
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.worker.js
@@ -0,0 +1,47 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.nonfinite
+// Description:clearRect() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(Infinity, 0, 100, 50);
+ ctx.clearRect(-Infinity, 0, 100, 50);
+ ctx.clearRect(NaN, 0, 100, 50);
+ ctx.clearRect(0, Infinity, 100, 50);
+ ctx.clearRect(0, -Infinity, 100, 50);
+ ctx.clearRect(0, NaN, 100, 50);
+ ctx.clearRect(0, 0, Infinity, 50);
+ ctx.clearRect(0, 0, -Infinity, 50);
+ ctx.clearRect(0, 0, NaN, 50);
+ ctx.clearRect(0, 0, 100, Infinity);
+ ctx.clearRect(0, 0, 100, -Infinity);
+ ctx.clearRect(0, 0, 100, NaN);
+ ctx.clearRect(Infinity, Infinity, 100, 50);
+ ctx.clearRect(Infinity, Infinity, Infinity, 50);
+ ctx.clearRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.clearRect(Infinity, Infinity, 100, Infinity);
+ ctx.clearRect(Infinity, 0, Infinity, 50);
+ ctx.clearRect(Infinity, 0, Infinity, Infinity);
+ ctx.clearRect(Infinity, 0, 100, Infinity);
+ ctx.clearRect(0, Infinity, Infinity, 50);
+ ctx.clearRect(0, Infinity, Infinity, Infinity);
+ ctx.clearRect(0, Infinity, 100, Infinity);
+ ctx.clearRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.html
new file mode 100644
index 0000000000..25ad954d06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.path</h1>
+<p class="desc">clearRect does not affect the current path</p>
+
+
+<script>
+var t = async_test("clearRect does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 16, 16);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.worker.js
new file mode 100644
index 0000000000..40f51f9f0e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.path.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.path
+// Description:clearRect does not affect the current path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 16, 16);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html
new file mode 100644
index 0000000000..3b3e41c19c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.shadow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.shadow</h1>
+<p class="desc">clearRect does not draw shadows</p>
+
+
+<script>
+var t = async_test("clearRect does not draw shadows");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.clearRect(0, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.worker.js
new file mode 100644
index 0000000000..a1ec8ac7ae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.shadow.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.shadow
+// Description:clearRect does not draw shadows
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect does not draw shadows");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.clearRect(0, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html
new file mode 100644
index 0000000000..8208b6f657
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.transform</h1>
+<p class="desc">clearRect is affected by transforms</p>
+
+
+<script>
+var t = async_test("clearRect is affected by transforms");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.clearRect(0, -5, 10, 5);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.worker.js
new file mode 100644
index 0000000000..f176f3da8a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.transform.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.transform
+// Description:clearRect is affected by transforms
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect is affected by transforms");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.clearRect(0, -5, 10, 5);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html
new file mode 100644
index 0000000000..461c318d24
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.clearRect.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.clearRect.zero</h1>
+<p class="desc">clearRect of zero pixels has no effect</p>
+
+
+<script>
+var t = async_test("clearRect of zero pixels has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 0);
+ ctx.clearRect(0, 0, 0, 50);
+ ctx.clearRect(0, 0, 0, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.worker.js
new file mode 100644
index 0000000000..b6f42551a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.clearRect.zero.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.clearRect.zero
+// Description:clearRect of zero pixels has no effect
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("clearRect of zero pixels has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 0);
+ ctx.clearRect(0, 0, 0, 50);
+ ctx.clearRect(0, 0, 0, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html
new file mode 100644
index 0000000000..3e65472b83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.basic</h1>
+<p class="desc">fillRect works</p>
+
+
+<script>
+var t = async_test("fillRect works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.worker.js
new file mode 100644
index 0000000000..69918278e4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.basic.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.basic
+// Description:fillRect works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html
new file mode 100644
index 0000000000..3b26960e24
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.clip</h1>
+<p class="desc">fillRect is affected by clipping regions</p>
+
+
+<script>
+var t = async_test("fillRect is affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.worker.js
new file mode 100644
index 0000000000..890b31e342
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.clip.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.clip
+// Description:fillRect is affected by clipping regions
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect is affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html
new file mode 100644
index 0000000000..2a1942811f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.negative</h1>
+<p class="desc">fillRect of negative sizes works</p>
+
+
+<script>
+var t = async_test("fillRect of negative sizes works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillRect(100, 0, -50, 25);
+ ctx.fillRect(0, 50, 50, -25);
+ ctx.fillRect(100, 50, -50, -25);
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.worker.js
new file mode 100644
index 0000000000..5df1652331
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.negative.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.negative
+// Description:fillRect of negative sizes works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect of negative sizes works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillRect(100, 0, -50, 25);
+ ctx.fillRect(0, 50, 50, -25);
+ ctx.fillRect(100, 50, -50, -25);
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html
new file mode 100644
index 0000000000..e44a9d4b78
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.nonfinite</h1>
+<p class="desc">fillRect() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("fillRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(Infinity, 0, 100, 50);
+ ctx.fillRect(-Infinity, 0, 100, 50);
+ ctx.fillRect(NaN, 0, 100, 50);
+ ctx.fillRect(0, Infinity, 100, 50);
+ ctx.fillRect(0, -Infinity, 100, 50);
+ ctx.fillRect(0, NaN, 100, 50);
+ ctx.fillRect(0, 0, Infinity, 50);
+ ctx.fillRect(0, 0, -Infinity, 50);
+ ctx.fillRect(0, 0, NaN, 50);
+ ctx.fillRect(0, 0, 100, Infinity);
+ ctx.fillRect(0, 0, 100, -Infinity);
+ ctx.fillRect(0, 0, 100, NaN);
+ ctx.fillRect(Infinity, Infinity, 100, 50);
+ ctx.fillRect(Infinity, Infinity, Infinity, 50);
+ ctx.fillRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.fillRect(Infinity, Infinity, 100, Infinity);
+ ctx.fillRect(Infinity, 0, Infinity, 50);
+ ctx.fillRect(Infinity, 0, Infinity, Infinity);
+ ctx.fillRect(Infinity, 0, 100, Infinity);
+ ctx.fillRect(0, Infinity, Infinity, 50);
+ ctx.fillRect(0, Infinity, Infinity, Infinity);
+ ctx.fillRect(0, Infinity, 100, Infinity);
+ ctx.fillRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.worker.js
new file mode 100644
index 0000000000..bc0170918c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.nonfinite.worker.js
@@ -0,0 +1,48 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.nonfinite
+// Description:fillRect() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(Infinity, 0, 100, 50);
+ ctx.fillRect(-Infinity, 0, 100, 50);
+ ctx.fillRect(NaN, 0, 100, 50);
+ ctx.fillRect(0, Infinity, 100, 50);
+ ctx.fillRect(0, -Infinity, 100, 50);
+ ctx.fillRect(0, NaN, 100, 50);
+ ctx.fillRect(0, 0, Infinity, 50);
+ ctx.fillRect(0, 0, -Infinity, 50);
+ ctx.fillRect(0, 0, NaN, 50);
+ ctx.fillRect(0, 0, 100, Infinity);
+ ctx.fillRect(0, 0, 100, -Infinity);
+ ctx.fillRect(0, 0, 100, NaN);
+ ctx.fillRect(Infinity, Infinity, 100, 50);
+ ctx.fillRect(Infinity, Infinity, Infinity, 50);
+ ctx.fillRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.fillRect(Infinity, Infinity, 100, Infinity);
+ ctx.fillRect(Infinity, 0, Infinity, 50);
+ ctx.fillRect(Infinity, 0, Infinity, Infinity);
+ ctx.fillRect(Infinity, 0, 100, Infinity);
+ ctx.fillRect(0, Infinity, Infinity, 50);
+ ctx.fillRect(0, Infinity, Infinity, Infinity);
+ ctx.fillRect(0, Infinity, 100, Infinity);
+ ctx.fillRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.html
new file mode 100644
index 0000000000..bcf7ee5a97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.path</h1>
+<p class="desc">fillRect does not affect the current path</p>
+
+
+<script>
+var t = async_test("fillRect does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.worker.js
new file mode 100644
index 0000000000..ddbfc4a96d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.path.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.path
+// Description:fillRect does not affect the current path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html
new file mode 100644
index 0000000000..8a8e38070c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.shadow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.shadow</h1>
+<p class="desc">fillRect draws shadows</p>
+
+
+<script>
+var t = async_test("fillRect draws shadows");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.worker.js
new file mode 100644
index 0000000000..ff4ef0ca2d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.shadow.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.shadow
+// Description:fillRect draws shadows
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect draws shadows");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html
new file mode 100644
index 0000000000..19cb6ac77a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.transform</h1>
+<p class="desc">fillRect is affected by transforms</p>
+
+
+<script>
+var t = async_test("fillRect is affected by transforms");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -5, 10, 5);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.worker.js
new file mode 100644
index 0000000000..a7f328955b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.transform.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.transform
+// Description:fillRect is affected by transforms
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect is affected by transforms");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -5, 10, 5);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html
new file mode 100644
index 0000000000..3ec78ba18a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillRect.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillRect.zero</h1>
+<p class="desc">fillRect of zero pixels has no effect</p>
+
+
+<script>
+var t = async_test("fillRect of zero pixels has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 0);
+ ctx.fillRect(0, 0, 0, 50);
+ ctx.fillRect(0, 0, 0, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.worker.js
new file mode 100644
index 0000000000..a310d3ff28
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.fillRect.zero.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillRect.zero
+// Description:fillRect of zero pixels has no effect
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect of zero pixels has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 0);
+ ctx.fillRect(0, 0, 0, 50);
+ ctx.fillRect(0, 0, 0, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html
new file mode 100644
index 0000000000..79cc3934b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.basic</h1>
+<p class="desc">strokeRect works</p>
+
+
+<script>
+var t = async_test("strokeRect works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.worker.js
new file mode 100644
index 0000000000..de7b13c8f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.basic.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.basic
+// Description:strokeRect works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html
new file mode 100644
index 0000000000..9990f2761e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.clip</h1>
+<p class="desc">strokeRect is affected by clipping regions</p>
+
+
+<script>
+var t = async_test("strokeRect is affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.worker.js
new file mode 100644
index 0000000000..f70a421f1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.clip.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.clip
+// Description:strokeRect is affected by clipping regions
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect is affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html
new file mode 100644
index 0000000000..efc532f33f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.globalalpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.globalalpha</h1>
+<p class="desc">strokeRect is affected by globalAlpha</p>
+
+
+<script>
+var t = async_test("strokeRect is affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.worker.js
new file mode 100644
index 0000000000..2f938b3848
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalalpha.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.globalalpha
+// Description:strokeRect is affected by globalAlpha
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect is affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html
new file mode 100644
index 0000000000..4532604c9a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.globalcomposite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.globalcomposite</h1>
+<p class="desc">strokeRect is not affected by globalCompositeOperation</p>
+
+
+<script>
+var t = async_test("strokeRect is not affected by globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.worker.js
new file mode 100644
index 0000000000..9de70c15a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.globalcomposite.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.globalcomposite
+// Description:strokeRect is not affected by globalCompositeOperation
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect is not affected by globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html
new file mode 100644
index 0000000000..13af38e565
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.negative</h1>
+<p class="desc">strokeRect of negative sizes works</p>
+
+
+<script>
+var t = async_test("strokeRect of negative sizes works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 25;
+ ctx.strokeRect(12, 12, 26, 1);
+ ctx.strokeRect(88, 12, -26, 1);
+ ctx.strokeRect(12, 38, 26, -1);
+ ctx.strokeRect(88, 38, -26, -1);
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.worker.js
new file mode 100644
index 0000000000..bc2aa0434b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.negative.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.negative
+// Description:strokeRect of negative sizes works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect of negative sizes works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 25;
+ ctx.strokeRect(12, 12, 26, 1);
+ ctx.strokeRect(88, 12, -26, 1);
+ ctx.strokeRect(12, 38, 26, -1);
+ ctx.strokeRect(88, 38, -26, -1);
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html
new file mode 100644
index 0000000000..6393797464
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.nonfinite</h1>
+<p class="desc">strokeRect() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("strokeRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 150;
+ ctx.strokeRect(Infinity, 0, 100, 50);
+ ctx.strokeRect(-Infinity, 0, 100, 50);
+ ctx.strokeRect(NaN, 0, 100, 50);
+ ctx.strokeRect(0, Infinity, 100, 50);
+ ctx.strokeRect(0, -Infinity, 100, 50);
+ ctx.strokeRect(0, NaN, 100, 50);
+ ctx.strokeRect(0, 0, Infinity, 50);
+ ctx.strokeRect(0, 0, -Infinity, 50);
+ ctx.strokeRect(0, 0, NaN, 50);
+ ctx.strokeRect(0, 0, 100, Infinity);
+ ctx.strokeRect(0, 0, 100, -Infinity);
+ ctx.strokeRect(0, 0, 100, NaN);
+ ctx.strokeRect(Infinity, Infinity, 100, 50);
+ ctx.strokeRect(Infinity, Infinity, Infinity, 50);
+ ctx.strokeRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.strokeRect(Infinity, Infinity, 100, Infinity);
+ ctx.strokeRect(Infinity, 0, Infinity, 50);
+ ctx.strokeRect(Infinity, 0, Infinity, Infinity);
+ ctx.strokeRect(Infinity, 0, 100, Infinity);
+ ctx.strokeRect(0, Infinity, Infinity, 50);
+ ctx.strokeRect(0, Infinity, Infinity, Infinity);
+ ctx.strokeRect(0, Infinity, 100, Infinity);
+ ctx.strokeRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.worker.js
new file mode 100644
index 0000000000..1878e54c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.nonfinite.worker.js
@@ -0,0 +1,49 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.nonfinite
+// Description:strokeRect() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 150;
+ ctx.strokeRect(Infinity, 0, 100, 50);
+ ctx.strokeRect(-Infinity, 0, 100, 50);
+ ctx.strokeRect(NaN, 0, 100, 50);
+ ctx.strokeRect(0, Infinity, 100, 50);
+ ctx.strokeRect(0, -Infinity, 100, 50);
+ ctx.strokeRect(0, NaN, 100, 50);
+ ctx.strokeRect(0, 0, Infinity, 50);
+ ctx.strokeRect(0, 0, -Infinity, 50);
+ ctx.strokeRect(0, 0, NaN, 50);
+ ctx.strokeRect(0, 0, 100, Infinity);
+ ctx.strokeRect(0, 0, 100, -Infinity);
+ ctx.strokeRect(0, 0, 100, NaN);
+ ctx.strokeRect(Infinity, Infinity, 100, 50);
+ ctx.strokeRect(Infinity, Infinity, Infinity, 50);
+ ctx.strokeRect(Infinity, Infinity, Infinity, Infinity);
+ ctx.strokeRect(Infinity, Infinity, 100, Infinity);
+ ctx.strokeRect(Infinity, 0, Infinity, 50);
+ ctx.strokeRect(Infinity, 0, Infinity, Infinity);
+ ctx.strokeRect(Infinity, 0, 100, Infinity);
+ ctx.strokeRect(0, Infinity, Infinity, 50);
+ ctx.strokeRect(0, Infinity, Infinity, Infinity);
+ ctx.strokeRect(0, Infinity, 100, Infinity);
+ ctx.strokeRect(0, 0, Infinity, Infinity);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html
new file mode 100644
index 0000000000..da8e903f9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.path</h1>
+<p class="desc">strokeRect does not affect the current path</p>
+
+
+<script>
+var t = async_test("strokeRect does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.worker.js
new file mode 100644
index 0000000000..839b973b17
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.path.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.path
+// Description:strokeRect does not affect the current path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html
new file mode 100644
index 0000000000..2adf24e2b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.shadow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.shadow</h1>
+<p class="desc">strokeRect draws shadows</p>
+
+
+<script>
+var t = async_test("strokeRect draws shadows");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, -75, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.worker.js
new file mode 100644
index 0000000000..36b3455016
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.shadow.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.shadow
+// Description:strokeRect draws shadows
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect draws shadows");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, -75, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html
new file mode 100644
index 0000000000..214ea14023
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.transform</h1>
+<p class="desc">fillRect is affected by transforms</p>
+
+
+<script>
+var t = async_test("fillRect is affected by transforms");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(2.5, -2.6, 5, 0.2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.worker.js
new file mode 100644
index 0000000000..43aab18533
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.transform.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.transform
+// Description:fillRect is affected by transforms
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillRect is affected by transforms");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(2.5, -2.6, 5, 0.2);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html
new file mode 100644
index 0000000000..2c63913fca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.zero.1</h1>
+<p class="desc">strokeRect of 0x0 pixels draws nothing</p>
+
+
+<script>
+var t = async_test("strokeRect of 0x0 pixels draws nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.strokeRect(50, 25, 0, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.worker.js
new file mode 100644
index 0000000000..115017b3bf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.zero.1
+// Description:strokeRect of 0x0 pixels draws nothing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect of 0x0 pixels draws nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.strokeRect(50, 25, 0, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html
new file mode 100644
index 0000000000..16bddea7ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.zero.2</h1>
+<p class="desc">strokeRect of 0x0 pixels draws nothing, including caps and joins</p>
+
+
+<script>
+var t = async_test("strokeRect of 0x0 pixels draws nothing, including caps and joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(50, 25, 0, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.worker.js
new file mode 100644
index 0000000000..973ebec44a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.2.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.zero.2
+// Description:strokeRect of 0x0 pixels draws nothing, including caps and joins
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect of 0x0 pixels draws nothing, including caps and joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(50, 25, 0, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html
new file mode 100644
index 0000000000..8bf3e043db
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.zero.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.zero.3</h1>
+<p class="desc">strokeRect of Nx0 pixels draws a straight line</p>
+
+
+<script>
+var t = async_test("strokeRect of Nx0 pixels draws a straight line");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.worker.js
new file mode 100644
index 0000000000..2612e54fdb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.zero.3
+// Description:strokeRect of Nx0 pixels draws a straight line
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect of Nx0 pixels draws a straight line");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html
new file mode 100644
index 0000000000..87aeaf7c13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.zero.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.zero.4</h1>
+<p class="desc">strokeRect of Nx0 pixels draws a closed line with no caps</p>
+
+
+<script>
+var t = async_test("strokeRect of Nx0 pixels draws a closed line with no caps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.worker.js
new file mode 100644
index 0000000000..3cec206313
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.4.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.zero.4
+// Description:strokeRect of Nx0 pixels draws a closed line with no caps
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect of Nx0 pixels draws a closed line with no caps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html
new file mode 100644
index 0000000000..e03e7aa118
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeRect.zero.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeRect.zero.5</h1>
+<p class="desc">strokeRect of Nx0 pixels draws a closed line with joins</p>
+
+
+<script>
+var t = async_test("strokeRect of Nx0 pixels draws a closed line with joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 250;
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.worker.js
new file mode 100644
index 0000000000..8591409705
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-rectangles-to-the-canvas/2d.strokeRect.zero.5.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeRect.zero.5
+// Description:strokeRect of Nx0 pixels draws a closed line with joins
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeRect of Nx0 pixels draws a closed line with joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 250;
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
new file mode 100644
index 0000000000..8e488a5a58
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.CSSHSL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.CSSHSL</h1>
+<p class="desc">CSSHSL works as color input</p>
+
+
+<script>
+var t = async_test("CSSHSL works as color input");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = new CSSHSL(CSS.deg(180), 0.5, 0.5);
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 64,191,191,255, 3);
+
+ const color = new CSSHSL(CSS.deg(180), 1, 1);
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#ffffff', "ctx.fillStyle", "'#ffffff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+ color.l = 0.5;
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#00ffff', "ctx.fillStyle", "'#00ffff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,255,255);
+
+ ctx.fillStyle = new CSSRGB(1, 0, 1).toHSL();
+ _assertSame(ctx.fillStyle, '#ff00ff', "ctx.fillStyle", "'#ff00ff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,0,255,255);
+
+ color.h = CSS.deg(120);
+ color.s = 1;
+ color.l = 0.5;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html
new file mode 100644
index 0000000000..1670d64295
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.CSSRGB</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.CSSRGB</h1>
+<p class="desc">CSSRGB works as color input</p>
+
+
+<script>
+var t = async_test("CSSRGB works as color input");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = new CSSRGB(1, 0, 1);
+ _assertSame(ctx.fillStyle, '#ff00ff', "ctx.fillStyle", "'#ff00ff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,0,255,255);
+
+ const color = new CSSRGB(0, CSS.percent(50), 0);
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#008000', "ctx.fillStyle", "'#008000'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,128,0,255);
+ color.g = 0;
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, '#000000', "ctx.fillStyle", "'#000000'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+
+ color.alpha = 0;
+ ctx.fillStyle = color;
+ _assertSame(ctx.fillStyle, 'rgba(0, 0, 0, 0)', "ctx.fillStyle", "'rgba(0, 0, 0, 0)'");
+ ctx.reset();
+ color.alpha = 0.5;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,128);
+
+ ctx.fillStyle = new CSSHSL(CSS.deg(0), 1, 1).toRGB();
+ _assertSame(ctx.fillStyle, '#ffffff', "ctx.fillStyle", "'#ffffff'");
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+
+ color.alpha = 1;
+ color.g = 1;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.html
new file mode 100644
index 0000000000..059c551e46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.colormix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.colormix</h1>
+<p class="desc">color-mix works as color input</p>
+
+
+<script>
+var t = async_test("color-mix works as color input");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = "color-mix(in srgb, red, blue)";
+ _assertSame(ctx.fillStyle, 'color(srgb 0.5 0 0.5)', "ctx.fillStyle", "'color(srgb 0.5 0 0.5)'");
+ ctx.fillStyle = "color-mix(in srgb, red, color(srgb 1 0 0))";
+ _assertSame(ctx.fillStyle, 'color(srgb 1 0 0)', "ctx.fillStyle", "'color(srgb 1 0 0)'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.worker.js
new file mode 100644
index 0000000000..d015790ead
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.colormix.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.colormix
+// Description:color-mix works as color input
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("color-mix works as color input");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = "color-mix(in srgb, red, blue)";
+ _assertSame(ctx.fillStyle, 'color(srgb 0.5 0 0.5)', "ctx.fillStyle", "'color(srgb 0.5 0 0.5)'");
+ ctx.fillStyle = "color-mix(in srgb, red, color(srgb 1 0 0))";
+ _assertSame(ctx.fillStyle, 'color(srgb 1 0 0)', "ctx.fillStyle", "'color(srgb 1 0 0)'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.html
new file mode 100644
index 0000000000..58075168e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.default</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.fillStyle, '#000000', "ctx.fillStyle", "'#000000'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.worker.js
new file mode 100644
index 0000000000..34a6f1276a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.default.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.default
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.fillStyle, '#000000', "ctx.fillStyle", "'#000000'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html
new file mode 100644
index 0000000000..cd0b0c2437
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.get.halftransparent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.get.halftransparent</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(255,255,255,0.5)';
+ _assertSame(ctx.fillStyle, 'rgba(255, 255, 255, 0.5)', "ctx.fillStyle", "'rgba(255, 255, 255, 0.5)'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.worker.js
new file mode 100644
index 0000000000..7590816b32
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.halftransparent.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.get.halftransparent
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(255,255,255,0.5)';
+ _assertSame(ctx.fillStyle, 'rgba(255, 255, 255, 0.5)', "ctx.fillStyle", "'rgba(255, 255, 255, 0.5)'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html
new file mode 100644
index 0000000000..6be295ae0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.get.semitransparent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.get.semitransparent</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(255,255,255,0.45)';
+ assert_regexp_match(ctx.fillStyle, /^rgba\(255, 255, 255, 0\.4\d+\)$/);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.worker.js
new file mode 100644
index 0000000000..1c25bc2ed5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.semitransparent.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.get.semitransparent
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(255,255,255,0.45)';
+ assert_regexp_match(ctx.fillStyle, /^rgba\(255, 255, 255, 0\.4\d+\)$/);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.html
new file mode 100644
index 0000000000..4d8ccc0ff4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.get.solid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.get.solid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fa0';
+ _assertSame(ctx.fillStyle, '#ffaa00', "ctx.fillStyle", "'#ffaa00'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.worker.js
new file mode 100644
index 0000000000..efec71ca9e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.solid.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.get.solid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fa0';
+ _assertSame(ctx.fillStyle, '#ffaa00', "ctx.fillStyle", "'#ffaa00'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.html
new file mode 100644
index 0000000000..8fe102f2b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.get.transparent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.get.transparent</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,0,0)';
+ _assertSame(ctx.fillStyle, 'rgba(0, 0, 0, 0)', "ctx.fillStyle", "'rgba(0, 0, 0, 0)'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.worker.js
new file mode 100644
index 0000000000..51542bad82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.get.transparent.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.get.transparent
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,0,0)';
+ _assertSame(ctx.fillStyle, 'rgba(0, 0, 0, 0)', "ctx.fillStyle", "'rgba(0, 0, 0, 0)'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.html
new file mode 100644
index 0000000000..a30bc4ac7c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.invalidstring</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.invalidstring</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = 'invalid';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.worker.js
new file mode 100644
index 0000000000..f2f5ccacce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidstring.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.invalidstring
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = 'invalid';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.html
new file mode 100644
index 0000000000..23814ae87c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.invalidtype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.invalidtype</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = null;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.worker.js
new file mode 100644
index 0000000000..13e10e1254
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.invalidtype.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.invalidtype
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = null;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html
new file mode 100644
index 0000000000..222d7f6077
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.worker.js
new file mode 100644
index 0000000000..74137880b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html
new file mode 100644
index 0000000000..b2c93c1e26
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0% / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.worker.js
new file mode 100644
index 0000000000..3ccd75a400
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0% / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html
new file mode 100644
index 0000000000..fc8726904c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.worker.js
new file mode 100644
index 0000000000..bfaba36446
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html
new file mode 100644
index 0000000000..71cb327c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.worker.js
new file mode 100644
index 0000000000..afe12657bc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html
new file mode 100644
index 0000000000..787d5e7644
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.worker.js
new file mode 100644
index 0000000000..05c77b0a1d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-5
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html
new file mode 100644
index 0000000000..8acc67f968
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.worker.js
new file mode 100644
index 0000000000..8ff08394b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-6
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html
new file mode 100644
index 0000000000..dc295d070a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-7</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-7</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(133.33333333grad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.worker.js
new file mode 100644
index 0000000000..9d0513ffca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-7.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-7
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(133.33333333grad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html
new file mode 100644
index 0000000000..50970585ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-8</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(2.0943951024rad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.worker.js
new file mode 100644
index 0000000000..ce56342cea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-8.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-8
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(2.0943951024rad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html
new file mode 100644
index 0000000000..47cd157c44
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsl-9</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsl-9</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(0.3333333333turn, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.worker.js
new file mode 100644
index 0000000000..e45f55930d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsl-9.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsl-9
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(0.3333333333turn, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html
new file mode 100644
index 0000000000..2d1e46f1a9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.worker.js
new file mode 100644
index 0000000000..2a1157ed1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html
new file mode 100644
index 0000000000..f5531f68e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0% / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.worker.js
new file mode 100644
index 0000000000..23cd58d1a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120 100.0% 50.0% / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html
new file mode 100644
index 0000000000..fde2328c43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.worker.js
new file mode 100644
index 0000000000..64577bc348
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html
new file mode 100644
index 0000000000..3b6e1f1347
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.worker.js
new file mode 100644
index 0000000000..05b08f0a0c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html
new file mode 100644
index 0000000000..b0677c16dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.worker.js
new file mode 100644
index 0000000000..51484f3aef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-5
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html
new file mode 100644
index 0000000000..809d11c4cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.worker.js
new file mode 100644
index 0000000000..7b224e5c93
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-6
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120deg, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html
new file mode 100644
index 0000000000..48c3c6927e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-7</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-7</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(133.33333333grad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.worker.js
new file mode 100644
index 0000000000..ed51a5df40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-7.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-7
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(133.33333333grad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html
new file mode 100644
index 0000000000..f49b313a1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-8</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(2.0943951024rad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.worker.js
new file mode 100644
index 0000000000..c61fb1a1fc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-8.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-8
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(2.0943951024rad, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html
new file mode 100644
index 0000000000..68c4e224a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-hsla-9</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-hsla-9</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(0.3333333333turn, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.worker.js
new file mode 100644
index 0000000000..51541b7148
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-hsla-9.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-hsla-9
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(0.3333333333turn, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html
new file mode 100644
index 0000000000..c9d75ca5c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgb-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255.0, 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.worker.js
new file mode 100644
index 0000000000..f0a67250f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgb-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255.0, 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html
new file mode 100644
index 0000000000..0acc23ef95
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgb-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.worker.js
new file mode 100644
index 0000000000..eb6b952b4c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgb-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html
new file mode 100644
index 0000000000..85c2606c98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgb-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.worker.js
new file mode 100644
index 0000000000..9db5b7c33e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgb-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html
new file mode 100644
index 0000000000..e028260ee5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgb-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.worker.js
new file mode 100644
index 0000000000..fecb3ecb6e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgb-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html
new file mode 100644
index 0000000000..51ace916b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgb-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0 / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.worker.js
new file mode 100644
index 0000000000..03948b0c69
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgb-5
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0 / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html
new file mode 100644
index 0000000000..6df2f914e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgb-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgb-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0 / 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.worker.js
new file mode 100644
index 0000000000..5f31d9a9f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgb-6
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0 255 0 / 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html
new file mode 100644
index 0000000000..19d726ee3b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgba-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255.0, 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.worker.js
new file mode 100644
index 0000000000..24f278221e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgba-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255.0, 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html
new file mode 100644
index 0000000000..1603ae93ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgba-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.worker.js
new file mode 100644
index 0000000000..fcda86bae2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgba-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html
new file mode 100644
index 0000000000..53ad71781a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgba-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.worker.js
new file mode 100644
index 0000000000..164f17f3d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgba-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html
new file mode 100644
index 0000000000..3dc9f9c7a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgba-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.worker.js
new file mode 100644
index 0000000000..da512a261e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgba-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html
new file mode 100644
index 0000000000..db0e891f22
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgba-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0 / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.worker.js
new file mode 100644
index 0000000000..7f10cba4d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgba-5
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0 / 0.2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html
new file mode 100644
index 0000000000..c4ec4622a5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgba-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.css-color-4-rgba-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0 / 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.worker.js
new file mode 100644
index 0000000000..2d81992eba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.css-color-4-rgba-6
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0 255 0 / 20%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,51);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html
new file mode 100644
index 0000000000..90c4d4f5d7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hex3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hex3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.worker.js
new file mode 100644
index 0000000000..7ee281b443
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hex3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html
new file mode 100644
index 0000000000..8a6260fa89
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hex4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hex4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#0f0f';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.worker.js
new file mode 100644
index 0000000000..53001f9580
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hex4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#0f0f';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html
new file mode 100644
index 0000000000..02f698ea30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hex6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hex6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#00fF00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.worker.js
new file mode 100644
index 0000000000..e9420b62e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hex6
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#00fF00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html
new file mode 100644
index 0000000000..1563bab113
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hex8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hex8</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#00ff00ff';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.worker.js
new file mode 100644
index 0000000000..35cdc5c292
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hex8.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hex8
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '#00ff00ff';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html
new file mode 100644
index 0000000000..96e6515a61
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.worker.js
new file mode 100644
index 0000000000..204df1a35f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html
new file mode 100644
index 0000000000..b6f8f2bb76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl( -240 , 100% , 50% )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.worker.js
new file mode 100644
index 0000000000..f282d3ca1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl( -240 , 100% , 50% )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html
new file mode 100644
index 0000000000..5ed3b96b9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(360120, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.worker.js
new file mode 100644
index 0000000000..a4abd304a8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(360120, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html
new file mode 100644
index 0000000000..f91edd75bf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(-360240, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.worker.js
new file mode 100644
index 0000000000..19cbfd08cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(-360240, 100%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html
new file mode 100644
index 0000000000..c4624da0c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.worker.js
new file mode 100644
index 0000000000..49689b8274
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-5
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120.0, 100.0%, 50.0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html
new file mode 100644
index 0000000000..5baa5b3b00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(+120, +100%, +50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.worker.js
new file mode 100644
index 0000000000..9f99d96403
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-6
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(+120, +100%, +50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html
new file mode 100644
index 0000000000..db43c45dc8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 200%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.worker.js
new file mode 100644
index 0000000000..868beda764
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-clamp-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 200%, 50%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html
new file mode 100644
index 0000000000..a5b399a748
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, -200%, 49.9%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 127,127,127,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.worker.js
new file mode 100644
index 0000000000..2f1d2d72e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-clamp-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, -200%, 49.9%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 127,127,127,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html
new file mode 100644
index 0000000000..cfa6d82382
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-clamp-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-clamp-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, 200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.worker.js
new file mode 100644
index 0000000000..4c71d6d140
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-clamp-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, 200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html
new file mode 100644
index 0000000000..a3bfd8c22f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsl-clamp-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsl-clamp-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, -200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.worker.js
new file mode 100644
index 0000000000..e4477f5cb8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsl-clamp-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsl-clamp-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsl(120, 100%, -200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html
new file mode 100644
index 0000000000..67a65da730
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 50%, 0.499)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.worker.js
new file mode 100644
index 0000000000..c002368227
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 50%, 0.499)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html
new file mode 100644
index 0000000000..c63fac5dd6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla( 120.0 , 100.0% , 50.0% , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.worker.js
new file mode 100644
index 0000000000..aca5d8d916
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla( 120.0 , 100.0% , 50.0% , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html
new file mode 100644
index 0000000000..ebd766165d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 200%, 50%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.worker.js
new file mode 100644
index 0000000000..6a29123d95
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-clamp-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 200%, 50%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html
new file mode 100644
index 0000000000..91ec54841c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, -200%, 49.9%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 127,127,127,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.worker.js
new file mode 100644
index 0000000000..c27c9f8619
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-clamp-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, -200%, 49.9%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 127,127,127,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html
new file mode 100644
index 0000000000..343bf8b2e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-clamp-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-clamp-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 200%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.worker.js
new file mode 100644
index 0000000000..64f459c23f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-clamp-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 200%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 255,255,255,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html
new file mode 100644
index 0000000000..c54d409934
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-clamp-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-clamp-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, -200%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.worker.js
new file mode 100644
index 0000000000..b24163a786
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-clamp-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, -200%, 1)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html
new file mode 100644
index 0000000000..d068ffa921
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-clamp-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-clamp-5</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 50%, 2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.worker.js
new file mode 100644
index 0000000000..a10af90a0f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-clamp-5
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 50%, 2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html
new file mode 100644
index 0000000000..f8f0a78f21
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.hsla-clamp-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.hsla-clamp-6</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 0%, -2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.worker.js
new file mode 100644
index 0000000000..da6cd93e2a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.hsla-clamp-6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.hsla-clamp-6
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'hsla(120, 100%, 0%, -2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.html
new file mode 100644
index 0000000000..fc1c1af18b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.html4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.html4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'limE';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.worker.js
new file mode 100644
index 0000000000..7430608f63
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.html4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.html4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'limE';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html
new file mode 100644
index 0000000000..3edffc6d93
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50% / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.worker.js
new file mode 100644
index 0000000000..054568e1de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsl-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50% / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html
new file mode 100644
index 0000000000..477b318f41
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0 100% 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.worker.js
new file mode 100644
index 0000000000..74d6904f84
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsl-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0 100% 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html
new file mode 100644
index 0000000000..e2092666c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100% 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.worker.js
new file mode 100644
index 0000000000..7e930df400
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsl-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100% 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html
new file mode 100644
index 0000000000..5bcc1755b0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0 100% 50% /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.worker.js
new file mode 100644
index 0000000000..cca9d52ab1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsl-4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0 100% 50% /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html
new file mode 100644
index 0000000000..ba46540802
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsl-5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50% /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.worker.js
new file mode 100644
index 0000000000..5c17b9eeb3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsl-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsl-5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50% /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html
new file mode 100644
index 0000000000..a5f8f3fe9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsla-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 100%, 50% / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.worker.js
new file mode 100644
index 0000000000..1ab7e713d0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsla-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 100%, 50% / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html
new file mode 100644
index 0000000000..bd543220d0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsla-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0 100% 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.worker.js
new file mode 100644
index 0000000000..9f1aeed563
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsla-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0 100% 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html
new file mode 100644
index 0000000000..e33b9415b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-hsla-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-hsla-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 100% 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.worker.js
new file mode 100644
index 0000000000..c06a31941b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-hsla-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-hsla-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 100% 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html
new file mode 100644
index 0000000000..8fbbc7ae51
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, 0, 0 / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.worker.js
new file mode 100644
index 0000000000..a728e8d358
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgb-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, 0, 0 / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html
new file mode 100644
index 0000000000..adc8297488
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255 0 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.worker.js
new file mode 100644
index 0000000000..da9b95c191
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgb-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255 0 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html
new file mode 100644
index 0000000000..4d385d56cc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.worker.js
new file mode 100644
index 0000000000..eee7fb92fd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgb-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html
new file mode 100644
index 0000000000..6b855b4357
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(0 0 0 /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.worker.js
new file mode 100644
index 0000000000..2b25416de7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgb-4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(0 0 0 /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html
new file mode 100644
index 0000000000..13fadb9a24
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgb-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgb-5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(0, 0, 0 /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.worker.js
new file mode 100644
index 0000000000..7932932d93
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgb-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgb-5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(0, 0, 0 /)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html
new file mode 100644
index 0000000000..5353f77b8f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgba-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgba-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0 / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.worker.js
new file mode 100644
index 0000000000..ef559c45f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgba-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0 / 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html
new file mode 100644
index 0000000000..aaa555aa05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgba-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgba-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255 0 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.worker.js
new file mode 100644
index 0000000000..7a06f832e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgba-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255 0 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html
new file mode 100644
index 0000000000..e8c0588851
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.css-color-4-rgba-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.css-color-4-rgba-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.worker.js
new file mode 100644
index 0000000000..65ed246a39
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.css-color-4-rgba-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.css-color-4-rgba-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html
new file mode 100644
index 0000000000..28a7ff4739
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#f'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.worker.js
new file mode 100644
index 0000000000..0cb79048c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#f'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html
new file mode 100644
index 0000000000..0dcf0f8282
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#f0'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.worker.js
new file mode 100644
index 0000000000..d2dd193748
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#f0'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html
new file mode 100644
index 0000000000..8e1bd9fa0f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#g00'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.worker.js
new file mode 100644
index 0000000000..805becb3b8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#g00'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html
new file mode 100644
index 0000000000..f59991d317
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg00'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.worker.js
new file mode 100644
index 0000000000..1499fb7e13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg00'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html
new file mode 100644
index 0000000000..9567114a13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#ff000'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.worker.js
new file mode 100644
index 0000000000..d50daeb4c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#ff000'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html
new file mode 100644
index 0000000000..b36d0c8654
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex6</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg0000'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.worker.js
new file mode 100644
index 0000000000..606aa4f458
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex6
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg0000'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html
new file mode 100644
index 0000000000..5b38e7873f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex7</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex7</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#ff0000f'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.worker.js
new file mode 100644
index 0000000000..92ff04a530
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex7.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex7
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#ff0000f'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html
new file mode 100644
index 0000000000..1eda0c74a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hex8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hex8</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg0000ff'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.worker.js
new file mode 100644
index 0000000000..4508cc567c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hex8.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hex8
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '#fg0000ff'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html
new file mode 100644
index 0000000000..9ff3c68fe2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsl-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsl-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0%, 100%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.worker.js
new file mode 100644
index 0000000000..e3cbe474b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsl-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0%, 100%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html
new file mode 100644
index 0000000000..46fac5fd40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsl-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsl-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(z, 100%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.worker.js
new file mode 100644
index 0000000000..56c36f3688
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsl-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(z, 100%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html
new file mode 100644
index 0000000000..1f05e8d944
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsl-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsl-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 0, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.worker.js
new file mode 100644
index 0000000000..491b5bdf49
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsl-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 0, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html
new file mode 100644
index 0000000000..dd2cec7977
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsl-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsl-4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.worker.js
new file mode 100644
index 0000000000..fef0f0aee2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsl-4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html
new file mode 100644
index 0000000000..909e230b16
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsl-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsl-5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100.%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.worker.js
new file mode 100644
index 0000000000..635e0bf72e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsl-5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100.%, 50%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html
new file mode 100644
index 0000000000..9bb652c076
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsl-6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsl-6</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50%,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.worker.js
new file mode 100644
index 0000000000..efe1aa7ded
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsl-6.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsl-6
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsl(0, 100%, 50%,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html
new file mode 100644
index 0000000000..9b553c669d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsla-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsla-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0%, 100%, 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.worker.js
new file mode 100644
index 0000000000..757ca911fb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsla-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0%, 100%, 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html
new file mode 100644
index 0000000000..d894bc42fd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsla-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsla-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 0, 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.worker.js
new file mode 100644
index 0000000000..be2b2ddf00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsla-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 0, 50%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html
new file mode 100644
index 0000000000..8901a112b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.hsla-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.hsla-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 0, 50%, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.worker.js
new file mode 100644
index 0000000000..52d3707f70
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.hsla-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.hsla-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'hsla(0, 0, 50%, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html
new file mode 100644
index 0000000000..ceb6c53a83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.name-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.name-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'darkbrown'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.worker.js
new file mode 100644
index 0000000000..6331a2dfb2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.name-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'darkbrown'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html
new file mode 100644
index 0000000000..684a665574
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.name-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.name-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'firebrick1'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.worker.js
new file mode 100644
index 0000000000..101646c995
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.name-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'firebrick1'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html
new file mode 100644
index 0000000000..ebf508f464
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.name-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.name-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'red blue'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.worker.js
new file mode 100644
index 0000000000..61e43e03d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.name-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'red blue'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html
new file mode 100644
index 0000000000..ba1f71b123
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.name-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.name-4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '"red"'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.worker.js
new file mode 100644
index 0000000000..b1efecdc57
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.name-4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '"red"'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html
new file mode 100644
index 0000000000..a889a387af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.name-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.name-5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '"red'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.worker.js
new file mode 100644
index 0000000000..e519fff426
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.name-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.name-5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '"red'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html
new file mode 100644
index 0000000000..e1fb32a179
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgb-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgb-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255.0, 0, 0,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.worker.js
new file mode 100644
index 0000000000..8255f35478
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgb-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255.0, 0, 0,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html
new file mode 100644
index 0000000000..54d7daf999
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgb-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgb-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(100%, 0, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.worker.js
new file mode 100644
index 0000000000..c0787e32cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgb-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(100%, 0, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html
new file mode 100644
index 0000000000..9874fc6f74
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgb-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgb-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, - 1, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.worker.js
new file mode 100644
index 0000000000..62365ac17a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgb-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgb(255, - 1, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html
new file mode 100644
index 0000000000..fbfa4baef2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgba-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgba-1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(100%, 0, 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.worker.js
new file mode 100644
index 0000000000..53063fd9b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgba-1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(100%, 0, 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html
new file mode 100644
index 0000000000..43cd1763c1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgba-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgba-2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1. 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.worker.js
new file mode 100644
index 0000000000..df7079284c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgba-2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1. 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html
new file mode 100644
index 0000000000..96955226a5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgba-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgba-3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1.)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.worker.js
new file mode 100644
index 0000000000..8f45ef582f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgba-3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1.)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html
new file mode 100644
index 0000000000..9334d693ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgba-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgba-4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, '; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.worker.js
new file mode 100644
index 0000000000..47549bea5e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgba-4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, '; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html
new file mode 100644
index 0000000000..7b53652a94
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.invalid.rgba-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.invalid.rgba-5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.worker.js
new file mode 100644
index 0000000000..562b9661ec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.invalid.rgba-5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = 'rgba(255, 0, 0, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html
new file mode 100644
index 0000000000..c9e9e4c413
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-1000, 1000, -1000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.worker.js
new file mode 100644
index 0000000000..51b81c5bba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-clamp-1
+// Description:
+// Note:<p class="notes">Assumes colors are clamped to [0,255].
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-1000, 1000, -1000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html
new file mode 100644
index 0000000000..98df8d67e4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-200%, 200%, -200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.worker.js
new file mode 100644
index 0000000000..bfe9102f99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-clamp-2
+// Description:
+// Note:<p class="notes">Assumes colors are clamped to [0,255].
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-200%, 200%, -200%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html
new file mode 100644
index 0000000000..70f5ed05f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-clamp-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-clamp-3</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-2147483649, 4294967298, -18446744073709551619)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.worker.js
new file mode 100644
index 0000000000..809d209348
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-clamp-3
+// Description:
+// Note:<p class="notes">Assumes colors are clamped to [0,255].
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-2147483649, 4294967298, -18446744073709551619)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html
new file mode 100644
index 0000000000..1f387998e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-clamp-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-clamp-4</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-1000000000000000000000000000000000000000, 1000000000000000000000000000000000000000, -1000000000000000000000000000000000000000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.worker.js
new file mode 100644
index 0000000000..c0ec5ec921
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-clamp-4
+// Description:
+// Note:<p class="notes">Assumes colors are clamped to [0,255].
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-1000000000000000000000000000000000000000, 1000000000000000000000000000000000000000, -1000000000000000000000000000000000000000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html
new file mode 100644
index 0000000000..c2ac0b94d2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-clamp-5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-clamp-5</h1>
+<p class="desc"></p>
+
+<p class="notes">Assumes colors are clamped to [0,255].
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.worker.js
new file mode 100644
index 0000000000..ac2692ec4b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-clamp-5.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-clamp-5
+// Description:
+// Note:<p class="notes">Assumes colors are clamped to [0,255].
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html
new file mode 100644
index 0000000000..2979d7da98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-eof</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-eof</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.worker.js
new file mode 100644
index 0000000000..997df7b03f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-eof.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-eof
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0, 255, 0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html
new file mode 100644
index 0000000000..67ba77f3e3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-num</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-num</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0,255,0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.worker.js
new file mode 100644
index 0000000000..1d54b1f189
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-num.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-num
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0,255,0)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html
new file mode 100644
index 0000000000..8fe3be55ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgb-percent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgb-percent</h1>
+<p class="desc"></p>
+
+<p class="notes">CSS3 Color says "The integer value 255 corresponds to 100%". (In particular, it is not 254...)
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0% ,100% ,0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.worker.js
new file mode 100644
index 0000000000..824376083e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgb-percent.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgb-percent
+// Description:
+// Note:<p class="notes">CSS3 Color says "The integer value 255 corresponds to 100%". (In particular, it is not 254...)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgb(0% ,100% ,0%)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html
new file mode 100644
index 0000000000..af3acb95e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-clamp-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-clamp-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, -2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.worker.js
new file mode 100644
index 0000000000..e3a5bf56ea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-clamp-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, -2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html
new file mode 100644
index 0000000000..32ee979894
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-clamp-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-clamp-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.worker.js
new file mode 100644
index 0000000000..612c40b675
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-clamp-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-clamp-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 2)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html
new file mode 100644
index 0000000000..baa42cde18
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-eof</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-eof</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 1';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.worker.js
new file mode 100644
index 0000000000..73e0b4fd43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-eof.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-eof
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0, 255, 0, 1';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html
new file mode 100644
index 0000000000..eaa83e1801
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-num-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-num-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , .499 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.worker.js
new file mode 100644
index 0000000000..bb1a97fbe7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-num-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , .499 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html
new file mode 100644
index 0000000000..47bf3ddced
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-num-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-num-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 0.499 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.worker.js
new file mode 100644
index 0000000000..5629ca5cb2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-num-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-num-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 0.499 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html
new file mode 100644
index 0000000000..068befd9b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-percent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-percent</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0%,100%,0%,0.499)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.worker.js
new file mode 100644
index 0000000000..6d90415325
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-percent.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-percent
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba(0%,100%,0%,0.499)';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,127);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html
new file mode 100644
index 0000000000..0e58836e5d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-solid-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-solid-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.worker.js
new file mode 100644
index 0000000000..cddedb7d59
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-solid-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html
new file mode 100644
index 0000000000..8da29eeffb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-solid-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-solid-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 1.0 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.worker.js
new file mode 100644
index 0000000000..126cbb9269
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-solid-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , 1.0 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html
new file mode 100644
index 0000000000..e67374c541
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-solid-3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-solid-3</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , +1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.worker.js
new file mode 100644
index 0000000000..d8d1513c90
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-3.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-solid-3
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( 0 , 255 , 0 , +1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html
new file mode 100644
index 0000000000..777aacea70
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.rgba-solid-4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.rgba-solid-4</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( -0 , 255 , +0 , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.worker.js
new file mode 100644
index 0000000000..aa516a2d7f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.rgba-solid-4.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.rgba-solid-4
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'rgba( -0 , 255 , +0 , 1 )';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html
new file mode 100644
index 0000000000..8dc6844ae9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.svg-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.svg-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'gray';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 128,128,128,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.worker.js
new file mode 100644
index 0000000000..e8660caf6d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.svg-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'gray';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 128,128,128,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html
new file mode 100644
index 0000000000..2833565006
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.svg-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.svg-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 128,128,128,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.worker.js
new file mode 100644
index 0000000000..3488200d86
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.svg-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.svg-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 128,128,128,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.html
new file mode 100644
index 0000000000..6e124062b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.system</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.system</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'ThreeDDarkShadow';
+ assert_regexp_match(ctx.fillStyle, /^#(?!(FF0000|ff0000|f00)$)/); // test that it's not red
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.worker.js
new file mode 100644
index 0000000000..cf62855af9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.system.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.system
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'ThreeDDarkShadow';
+ assert_regexp_match(ctx.fillStyle, /^#(?!(FF0000|ff0000|f00)$)/); // test that it's not red
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html
new file mode 100644
index 0000000000..a595d70bc0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.transparent-1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.transparent-1</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'transparent';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.worker.js
new file mode 100644
index 0000000000..7f9a975bc1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.transparent-1
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'transparent';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html
new file mode 100644
index 0000000000..0f2a7e0609
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.parse.transparent-2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.parse.transparent-2</h1>
+<p class="desc"></p>
+
+<p class="notes">
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'TrAnSpArEnT';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.worker.js
new file mode 100644
index 0000000000..794ca926dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.parse.transparent-2.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.parse.transparent-2
+// Description:
+// Note:<p class="notes">
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'TrAnSpArEnT';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html
new file mode 100644
index 0000000000..3e56bd2b62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.toStringFunctionCallback</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.toStringFunctionCallback</h1>
+<p class="desc">Passing a function in to ctx.fillStyle or ctx.strokeStyle with a toString callback works as specified</p>
+
+
+<script>
+var t = async_test("Passing a function in to ctx.fillStyle or ctx.strokeStyle with a toString callback works as specified");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = { toString: function() { return "#008000"; } };
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ ctx.fillStyle = {};
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ ctx.fillStyle = 800000;
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ assert_throws_js(TypeError, function() { ctx.fillStyle = { toString: function() { throw new TypeError; } }; });
+ ctx.strokeStyle = { toString: function() { return "#008000"; } };
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ ctx.strokeStyle = {};
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ ctx.strokeStyle = 800000;
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ assert_throws_js(TypeError, function() { ctx.strokeStyle = { toString: function() { throw new TypeError; } }; });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.worker.js
new file mode 100644
index 0000000000..3ae93092b8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.toStringFunctionCallback.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.fillStyle.toStringFunctionCallback
+// Description:Passing a function in to ctx.fillStyle or ctx.strokeStyle with a toString callback works as specified
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Passing a function in to ctx.fillStyle or ctx.strokeStyle with a toString callback works as specified");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = { toString: function() { return "#008000"; } };
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ ctx.fillStyle = {};
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ ctx.fillStyle = 800000;
+ _assertSame(ctx.fillStyle, "#008000", "ctx.fillStyle", "\"#008000\"");
+ assert_throws_js(TypeError, function() { ctx.fillStyle = { toString: function() { throw new TypeError; } }; });
+ ctx.strokeStyle = { toString: function() { return "#008000"; } };
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ ctx.strokeStyle = {};
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ ctx.strokeStyle = 800000;
+ _assertSame(ctx.strokeStyle, "#008000", "ctx.strokeStyle", "\"#008000\"");
+ assert_throws_js(TypeError, function() { ctx.strokeStyle = { toString: function() { throw new TypeError; } }; });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html
new file mode 100644
index 0000000000..5ccb19e09c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.conic.invalid.inputs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.conic.invalid.inputs</h1>
+<p class="desc">Conic gradient function with invalid inputs</p>
+
+
+<script>
+var t = async_test("Conic gradient function with invalid inputs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(-Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(NaN, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, -Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, NaN, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, Infinity, Infinity); });
+
+ const g = ctx.createConicGradient(0, 0, 25);
+ assert_throws_js(TypeError, function() { g.addColorStop(-Infinity, '#f00'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(NaN, '#f00'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, -Infinity); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, NaN); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.worker.js
new file mode 100644
index 0000000000..19b603ea75
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.invalid.inputs.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.conic.invalid.inputs
+// Description:Conic gradient function with invalid inputs
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Conic gradient function with invalid inputs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(-Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(NaN, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, -Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, NaN, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, 0, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createConicGradient(0, Infinity, Infinity); });
+
+ const g = ctx.createConicGradient(0, 0, 25);
+ assert_throws_js(TypeError, function() { g.addColorStop(-Infinity, '#f00'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(NaN, '#f00'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, -Infinity); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, NaN); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html
new file mode 100644
index 0000000000..848f1496ea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.conic.negative.rotation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.conic.negative.rotation</h1>
+<p class="desc">Conic gradient with negative rotation</p>
+
+
+<script>
+var t = async_test("Conic gradient with negative rotation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const g = ctx.createConicGradient(-Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,15, 255,0,0,255, 3);
+ _assertPixelApprox(canvas, 75,40, 0,255,0,255, 3);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.worker.js
new file mode 100644
index 0000000000..1f9735ee08
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.negative.rotation.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.conic.negative.rotation
+// Description:Conic gradient with negative rotation
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Conic gradient with negative rotation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const g = ctx.createConicGradient(-Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,15, 255,0,0,255, 3);
+ _assertPixelApprox(canvas, 75,40, 0,255,0,255, 3);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html
new file mode 100644
index 0000000000..06b593904f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.conic.positive.rotation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.conic.positive.rotation</h1>
+<p class="desc">Conic gradient with positive rotation</p>
+
+
+<script>
+var t = async_test("Conic gradient with positive rotation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const g = ctx.createConicGradient(3*Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,15, 255,0,0,255, 3);
+ _assertPixelApprox(canvas, 75,40, 0,255,0,255, 3);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.worker.js
new file mode 100644
index 0000000000..45bbf40313
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.conic.positive.rotation.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.conic.positive.rotation
+// Description:Conic gradient with positive rotation
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Conic gradient with positive rotation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const g = ctx.createConicGradient(3*Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,15, 255,0,0,255, 3);
+ _assertPixelApprox(canvas, 75,40, 0,255,0,255, 3);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.html
new file mode 100644
index 0000000000..bad1ff4767
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.empty</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.worker.js
new file mode 100644
index 0000000000..225de25823
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.empty.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.empty
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html
new file mode 100644
index 0000000000..525cb79e03
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.alpha</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(0,0,255, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 191,191,63,255, 3);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 3);
+ _assertPixelApprox(canvas, 75,25, 63,63,191,255, 3);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.png b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.png
new file mode 100644
index 0000000000..af5ac0f07d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.worker.js
new file mode 100644
index 0000000000..5c2f7e07b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.alpha.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.alpha
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(0,0,255, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 191,191,63,255, 3);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 3);
+ _assertPixelApprox(canvas, 75,25, 63,63,191,255, 3);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.html
new file mode 100644
index 0000000000..c742eff2f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.color</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.color</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 191,191,63,255, 3);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 3);
+ _assertPixelApprox(canvas, 75,25, 63,63,191,255, 3);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.png b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.png
new file mode 100644
index 0000000000..af5ac0f07d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.worker.js
new file mode 100644
index 0000000000..6d273b7b43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.color.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.color
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 191,191,63,255, 3);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 3);
+ _assertPixelApprox(canvas, 75,25, 63,63,191,255, 3);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html
new file mode 100644
index 0000000000..00575c399a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.coloralpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.coloralpha</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(255,255,0, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 190,190,65,65, 3);
+ _assertPixelApprox(canvas, 50,25, 126,126,128,128, 3);
+ _assertPixelApprox(canvas, 75,25, 62,62,192,192, 3);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.png b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.png
new file mode 100644
index 0000000000..552e6ee44b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.worker.js
new file mode 100644
index 0000000000..1462cb59e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.coloralpha
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(255,255,0, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 25,25, 190,190,65,65, 3);
+ _assertPixelApprox(canvas, 50,25, 126,126,128,128, 3);
+ _assertPixelApprox(canvas, 75,25, 62,62,192,192, 3);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html
new file mode 100644
index 0000000000..3f2594f2cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.multiple</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.5, '#0ff');
+ g.addColorStop(1, '#f0f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ _assertPixelApprox(canvas, 50,25, 127,255,127,255, 3);
+ _assertPixelApprox(canvas, 100,25, 0,255,255,255, 3);
+ _assertPixelApprox(canvas, 150,25, 127,127,255,255, 3);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.png b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.png
new file mode 100644
index 0000000000..86122450d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.worker.js
new file mode 100644
index 0000000000..ebfcddca9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.multiple.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.multiple
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.5, '#0ff');
+ g.addColorStop(1, '#f0f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ _assertPixelApprox(canvas, 50,25, 127,255,127,255, 3);
+ _assertPixelApprox(canvas, 100,25, 0,255,255,255, 3);
+ _assertPixelApprox(canvas, 150,25, 127,127,255,255, 3);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.html
new file mode 100644
index 0000000000..02ca008518
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.outside</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(25, 0, 75, 0);
+ g.addColorStop(0.4, '#0f0');
+ g.addColorStop(0.6, '#0f0');
+
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 20,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.worker.js
new file mode 100644
index 0000000000..d6c8961661
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.outside.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.outside
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(25, 0, 75, 0);
+ g.addColorStop(0.4, '#0f0');
+ g.addColorStop(0.6, '#0f0');
+
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 20,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 80,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html
new file mode 100644
index 0000000000..0139da177a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.overlap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.overlap</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.25, '#00f');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#ff0');
+ g.addColorStop(0.5, '#00f');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.75, '#00f');
+ g.addColorStop(0.75, '#f00');
+ g.addColorStop(0.75, '#ff0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ _assertPixelApprox(canvas, 49,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 51,25, 255,255,0,255, 16);
+ _assertPixelApprox(canvas, 99,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 101,25, 255,255,0,255, 16);
+ _assertPixelApprox(canvas, 149,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 151,25, 255,255,0,255, 16);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.png b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.png
new file mode 100644
index 0000000000..5c2bb964e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.worker.js
new file mode 100644
index 0000000000..d404deab26
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap.worker.js
@@ -0,0 +1,47 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.overlap
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.25, '#00f');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#ff0');
+ g.addColorStop(0.5, '#00f');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.75, '#00f');
+ g.addColorStop(0.75, '#f00');
+ g.addColorStop(0.75, '#ff0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ _assertPixelApprox(canvas, 49,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 51,25, 255,255,0,255, 16);
+ _assertPixelApprox(canvas, 99,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 101,25, 255,255,0,255, 16);
+ _assertPixelApprox(canvas, 149,25, 0,0,255,255, 16);
+ _assertPixelApprox(canvas, 151,25, 255,255,0,255, 16);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html
new file mode 100644
index 0000000000..c26c8ecb78
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.overlap2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.overlap2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ var ps = [ 0, 1/10, 1/4, 1/3, 1/2, 3/4, 1 ];
+ for (var p = 0; p < ps.length; ++p)
+ {
+ g.addColorStop(ps[p], '#0f0');
+ for (var i = 0; i < 15; ++i)
+ g.addColorStop(ps[p], '#f00');
+ g.addColorStop(ps[p], '#0f0');
+ }
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 30,25, 0,255,0,255);
+ _assertPixel(canvas, 40,25, 0,255,0,255);
+ _assertPixel(canvas, 60,25, 0,255,0,255);
+ _assertPixel(canvas, 80,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.worker.js
new file mode 100644
index 0000000000..3938081cef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.overlap2.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.overlap2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ var ps = [ 0, 1/10, 1/4, 1/3, 1/2, 3/4, 1 ];
+ for (var p = 0; p < ps.length; ++p)
+ {
+ g.addColorStop(ps[p], '#0f0');
+ for (var i = 0; i < 15; ++i)
+ g.addColorStop(ps[p], '#f00');
+ g.addColorStop(ps[p], '#0f0');
+ }
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 30,25, 0,255,0,255);
+ _assertPixel(canvas, 40,25, 0,255,0,255);
+ _assertPixel(canvas, 60,25, 0,255,0,255);
+ _assertPixel(canvas, 80,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.html
new file mode 100644
index 0000000000..dd369ae5e3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.solid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.solid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.worker.js
new file mode 100644
index 0000000000..387aab0ab7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.solid.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.solid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html
new file mode 100644
index 0000000000..dff5708f05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.vertical</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.vertical</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,12, 191,191,63,255, 10);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 5);
+ _assertPixelApprox(canvas, 50,37, 63,63,191,255, 10);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.png b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.png
new file mode 100644
index 0000000000..37d6a00c62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.worker.js
new file mode 100644
index 0000000000..61cf9b6d8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.vertical.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.vertical
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,12, 191,191,63,255, 10);
+ _assertPixelApprox(canvas, 50,25, 127,127,127,255, 5);
+ _assertPixelApprox(canvas, 50,37, 63,63,191,255, 10);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html
new file mode 100644
index 0000000000..27579fc829
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.zerosize.fill</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.zerosize.fill</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ _assertPixel(canvas, 40,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.worker.js
new file mode 100644
index 0000000000..916b1cb977
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fill.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.zerosize.fill
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ _assertPixel(canvas, 40,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html
new file mode 100644
index 0000000000..9cf2c7350f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.zerosize.fillRect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.zerosize.fillRect</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 40,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.worker.js
new file mode 100644
index 0000000000..607ad85ae7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillRect.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.zerosize.fillRect
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 40,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html
new file mode 100644
index 0000000000..59f0964461
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.zerosize.fillText</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.zerosize.fillText</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.fillText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.worker.js
new file mode 100644
index 0000000000..417b564e6d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.fillText.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.zerosize.fillText
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.fillText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html
new file mode 100644
index 0000000000..562f46796c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.zerosize.stroke</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.zerosize.stroke</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.rect(20, 20, 60, 10);
+ ctx.stroke();
+ _assertPixel(canvas, 19,19, 0,255,0,255);
+ _assertPixel(canvas, 20,19, 0,255,0,255);
+ _assertPixel(canvas, 21,19, 0,255,0,255);
+ _assertPixel(canvas, 19,20, 0,255,0,255);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ _assertPixel(canvas, 21,20, 0,255,0,255);
+ _assertPixel(canvas, 19,21, 0,255,0,255);
+ _assertPixel(canvas, 20,21, 0,255,0,255);
+ _assertPixel(canvas, 21,21, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.worker.js
new file mode 100644
index 0000000000..b969be6f85
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.stroke.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.zerosize.stroke
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.rect(20, 20, 60, 10);
+ ctx.stroke();
+ _assertPixel(canvas, 19,19, 0,255,0,255);
+ _assertPixel(canvas, 20,19, 0,255,0,255);
+ _assertPixel(canvas, 21,19, 0,255,0,255);
+ _assertPixel(canvas, 19,20, 0,255,0,255);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ _assertPixel(canvas, 21,20, 0,255,0,255);
+ _assertPixel(canvas, 19,21, 0,255,0,255);
+ _assertPixel(canvas, 20,21, 0,255,0,255);
+ _assertPixel(canvas, 21,21, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html
new file mode 100644
index 0000000000..de1e57bbcc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.zerosize.strokeRect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.zerosize.strokeRect</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.strokeRect(20, 20, 60, 10);
+ _assertPixel(canvas, 19,19, 0,255,0,255);
+ _assertPixel(canvas, 20,19, 0,255,0,255);
+ _assertPixel(canvas, 21,19, 0,255,0,255);
+ _assertPixel(canvas, 19,20, 0,255,0,255);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ _assertPixel(canvas, 21,20, 0,255,0,255);
+ _assertPixel(canvas, 19,21, 0,255,0,255);
+ _assertPixel(canvas, 20,21, 0,255,0,255);
+ _assertPixel(canvas, 21,21, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.worker.js
new file mode 100644
index 0000000000..b9884d4adf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeRect.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.zerosize.strokeRect
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.strokeRect(20, 20, 60, 10);
+ _assertPixel(canvas, 19,19, 0,255,0,255);
+ _assertPixel(canvas, 20,19, 0,255,0,255);
+ _assertPixel(canvas, 21,19, 0,255,0,255);
+ _assertPixel(canvas, 19,20, 0,255,0,255);
+ _assertPixel(canvas, 20,20, 0,255,0,255);
+ _assertPixel(canvas, 21,20, 0,255,0,255);
+ _assertPixel(canvas, 19,21, 0,255,0,255);
+ _assertPixel(canvas, 20,21, 0,255,0,255);
+ _assertPixel(canvas, 21,21, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html
new file mode 100644
index 0000000000..153ec102ac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.interpolate.zerosize.strokeText</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.interpolate.zerosize.strokeText</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.strokeText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.worker.js
new file mode 100644
index 0000000000..f0d7192627
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.interpolate.zerosize.strokeText.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.interpolate.zerosize.strokeText
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.strokeText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html
new file mode 100644
index 0000000000..6c8f8ec38a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.linear.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.linear.nonfinite</h1>
+<p class="desc">createLinearGradient() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("createLinearGradient() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(-Infinity, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(NaN, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, -Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, NaN, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, -Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, NaN, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, Infinity, Infinity); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.worker.js
new file mode 100644
index 0000000000..9fd4949d7d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.nonfinite.worker.js
@@ -0,0 +1,44 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.linear.nonfinite
+// Description:createLinearGradient() throws TypeError if arguments are not finite
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createLinearGradient() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(-Infinity, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(NaN, 0, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, -Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, NaN, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, -Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, NaN, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, 1, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, 1, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, Infinity, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(Infinity, 0, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, Infinity, 0); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, Infinity, 1, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createLinearGradient(0, 0, Infinity, Infinity); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.html
new file mode 100644
index 0000000000..69fc52cedd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.linear.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.linear.transform.1</h1>
+<p class="desc">Linear gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<script>
+var t = async_test("Linear gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.worker.js
new file mode 100644
index 0000000000..79cdc96da8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.1.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.linear.transform.1
+// Description:Linear gradient coordinates are relative to the coordinate space at the time of filling
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Linear gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.html
new file mode 100644
index 0000000000..5cf27f1d8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.linear.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.linear.transform.2</h1>
+<p class="desc">Linear gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<script>
+var t = async_test("Linear gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(100, 0);
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-150, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.worker.js
new file mode 100644
index 0000000000..a2b532d1f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.2.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.linear.transform.2
+// Description:Linear gradient coordinates are relative to the coordinate space at the time of filling
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Linear gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(100, 0);
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-150, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.html
new file mode 100644
index 0000000000..074722a49b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.linear.transform.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.linear.transform.3</h1>
+<p class="desc">Linear gradient transforms do not experience broken caching effects</p>
+
+
+<script>
+var t = async_test("Linear gradient transforms do not experience broken caching effects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.worker.js
new file mode 100644
index 0000000000..77df9495c3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.linear.transform.3.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.linear.transform.3
+// Description:Linear gradient transforms do not experience broken caching effects
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Linear gradient transforms do not experience broken caching effects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.html
new file mode 100644
index 0000000000..d923a2f552
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.object.compare</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.object.compare</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ var g2 = ctx.createLinearGradient(0, 0, 100, 0);
+ _assertDifferent(g1, g2, "g1", "g2");
+ ctx.fillStyle = g1;
+ _assertSame(ctx.fillStyle, g1, "ctx.fillStyle", "g1");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.worker.js
new file mode 100644
index 0000000000..f6d45d259f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.compare.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.object.compare
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ var g2 = ctx.createLinearGradient(0, 0, 100, 0);
+ _assertDifferent(g1, g2, "g1", "g2");
+ ctx.fillStyle = g1;
+ _assertSame(ctx.fillStyle, g1, "ctx.fillStyle", "g1");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html
new file mode 100644
index 0000000000..6d229ea3d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.object.crosscanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.object.crosscanvas</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = new OffscreenCanvas(100, 50).getContext('2d').createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.worker.js
new file mode 100644
index 0000000000..1afebdea1b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.crosscanvas.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.object.crosscanvas
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = new OffscreenCanvas(100, 50).getContext('2d').createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html
new file mode 100644
index 0000000000..3db5afeeb5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.object.invalidcolor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.object.invalidcolor</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, null); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, undefined); });
+
+ var g = ctx.createRadialGradient(0, 0, 0, 100, 0, 0);
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, null); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, undefined); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.worker.js
new file mode 100644
index 0000000000..33b524fbed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidcolor.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.object.invalidcolor
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, null); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, undefined); });
+
+ var g = ctx.createRadialGradient(0, 0, 0, 100, 0, 0);
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, null); });
+ assert_throws_dom("SYNTAX_ERR", function() { g.addColorStop(0, undefined); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html
new file mode 100644
index 0000000000..b9f1104254
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.object.invalidoffset</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.object.invalidoffset</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ assert_throws_dom("INDEX_SIZE_ERR", function() { g.addColorStop(-1, '#000'); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { g.addColorStop(2, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(Infinity, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(-Infinity, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(NaN, '#000'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.worker.js
new file mode 100644
index 0000000000..80be64235b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.invalidoffset.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.object.invalidoffset
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ assert_throws_dom("INDEX_SIZE_ERR", function() { g.addColorStop(-1, '#000'); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { g.addColorStop(2, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(Infinity, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(-Infinity, '#000'); });
+ assert_throws_js(TypeError, function() { g.addColorStop(NaN, '#000'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.html
new file mode 100644
index 0000000000..779b941d20
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.object.return</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.object.return</h1>
+<p class="desc">createLinearGradient() and createRadialGradient() returns objects implementing CanvasGradient</p>
+
+
+<script>
+var t = async_test("createLinearGradient() and createRadialGradient() returns objects implementing CanvasGradient");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ window.CanvasGradient.prototype.thisImplementsCanvasGradient = true;
+
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ _assertDifferent(g1.addColorStop, undefined, "g1.addColorStop", "undefined");
+ _assertSame(g1.thisImplementsCanvasGradient, true, "g1.thisImplementsCanvasGradient", "true");
+
+ var g2 = ctx.createRadialGradient(0, 0, 10, 0, 0, 20);
+ _assertDifferent(g2.addColorStop, undefined, "g2.addColorStop", "undefined");
+ _assertSame(g2.thisImplementsCanvasGradient, true, "g2.thisImplementsCanvasGradient", "true");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.worker.js
new file mode 100644
index 0000000000..a279eed4f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.return.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.object.return
+// Description:createLinearGradient() and createRadialGradient() returns objects implementing CanvasGradient
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createLinearGradient() and createRadialGradient() returns objects implementing CanvasGradient");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ self.CanvasGradient.prototype.thisImplementsCanvasGradient = true;
+
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ _assertDifferent(g1.addColorStop, undefined, "g1.addColorStop", "undefined");
+ _assertSame(g1.thisImplementsCanvasGradient, true, "g1.thisImplementsCanvasGradient", "true");
+
+ var g2 = ctx.createRadialGradient(0, 0, 10, 0, 0, 20);
+ _assertDifferent(g2.addColorStop, undefined, "g2.addColorStop", "undefined");
+ _assertSame(g2.thisImplementsCanvasGradient, true, "g2.thisImplementsCanvasGradient", "true");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.html
new file mode 100644
index 0000000000..aa72183ca5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.object.type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.object.type</h1>
+<p class="desc">window.CanvasGradient exists and has the right properties</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("window.CanvasGradient exists and has the right properties");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(window.CanvasGradient, undefined, "window.CanvasGradient", "undefined");
+ _assertDifferent(window.CanvasGradient.prototype.addColorStop, undefined, "window.CanvasGradient.prototype.addColorStop", "undefined");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.worker.js
new file mode 100644
index 0000000000..e0101dfca4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.type.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.object.type
+// Description:window.CanvasGradient exists and has the right properties
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("window.CanvasGradient exists and has the right properties");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(self.CanvasGradient, undefined, "self.CanvasGradient", "undefined");
+ _assertDifferent(self.CanvasGradient.prototype.addColorStop, undefined, "self.CanvasGradient.prototype.addColorStop", "undefined");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.html
new file mode 100644
index 0000000000..00c60041e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.object.update</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.object.update</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(-100, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ g.addColorStop(0.1, '#0f0');
+ g.addColorStop(0.9, '#0f0');
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.worker.js
new file mode 100644
index 0000000000..db79ebfa2f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.object.update.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.object.update
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createLinearGradient(-100, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ g.addColorStop(0.1, '#0f0');
+ g.addColorStop(0.9, '#0f0');
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html
new file mode 100644
index 0000000000..c270a75175
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.behind</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.behind</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, 25, 10, 211, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.worker.js
new file mode 100644
index 0000000000..07ea409ad8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.behind.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.behind
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, 25, 10, 211, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html
new file mode 100644
index 0000000000..89d8757bde
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.beside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.beside</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(0, 100, 40, 100, 100, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.worker.js
new file mode 100644
index 0000000000..686721cbc6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.beside.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.beside
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(0, 100, 40, 100, 100, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html
new file mode 100644
index 0000000000..cda6743f66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.bottom</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.bottom</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 101);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.worker.js
new file mode 100644
index 0000000000..c96b9c59f2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.bottom.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.bottom
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 101);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html
new file mode 100644
index 0000000000..a6f1d67619
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.cylinder</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.cylinder</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 100);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.worker.js
new file mode 100644
index 0000000000..22e6cb49ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.cylinder.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.cylinder
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 100);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.html
new file mode 100644
index 0000000000..5cda039e94
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.front</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.front</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(311, 25, 10, 210, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.worker.js
new file mode 100644
index 0000000000..c4e70f53c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.front.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.front
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(311, 25, 10, 210, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html
new file mode 100644
index 0000000000..290923b5e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.shape1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.shape1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(30+tol, 40);
+ ctx.lineTo(110, -20+tol);
+ ctx.lineTo(110, 100-tol);
+ ctx.fill();
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.worker.js
new file mode 100644
index 0000000000..d03c0db23c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape1.worker.js
@@ -0,0 +1,48 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.shape1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(30+tol, 40);
+ ctx.lineTo(110, -20+tol);
+ ctx.lineTo(110, 100-tol);
+ ctx.fill();
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html
new file mode 100644
index 0000000000..edf1909ac1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.shape2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.shape2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(30-tol, 40);
+ ctx.lineTo(110, -20-tol);
+ ctx.lineTo(110, 100+tol);
+ ctx.fill();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.worker.js
new file mode 100644
index 0000000000..8788954a3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.worker.js
@@ -0,0 +1,48 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.shape2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(30-tol, 40);
+ ctx.lineTo(110, -20-tol);
+ ctx.lineTo(110, 100+tol);
+ ctx.fill();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.html
new file mode 100644
index 0000000000..9487c8c931
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.cone.top</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.cone.top</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(230, 25, 100, 100, 25, 101);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.worker.js
new file mode 100644
index 0000000000..6fc834373a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.cone.top.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.cone.top
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(230, 25, 100, 100, 25, 101);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.html
new file mode 100644
index 0000000000..b14b44fe30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.equal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.equal</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 20, 50, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.worker.js
new file mode 100644
index 0000000000..c8e6095805
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.equal.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.equal
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 20, 50, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.html
new file mode 100644
index 0000000000..a55f0a1104
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.inside1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.inside1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 100, 50, 25, 200);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.worker.js
new file mode 100644
index 0000000000..5da290a7ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside1.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.inside1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 100, 50, 25, 200);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.html
new file mode 100644
index 0000000000..3a7af0658b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.inside2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.inside2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.worker.js
new file mode 100644
index 0000000000..dd49fa3771
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside2.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.inside2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.html
new file mode 100644
index 0000000000..3a539e6b63
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.inside3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.inside3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.993, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js
new file mode 100644
index 0000000000..0d05bf4d5b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.inside3.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.inside3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.993, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.html
new file mode 100644
index 0000000000..b6dcaf175e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.negative</h1>
+<p class="desc">createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative</p>
+
+
+<script>
+var t = async_test("createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, -0.1, 0, 0, 1); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, 1, 0, 0, -0.1); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, -0.1, 0, 0, -0.1); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.worker.js
new file mode 100644
index 0000000000..72ad3f803e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.negative.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.negative
+// Description:createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, -0.1, 0, 0, 1); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, 1, 0, 0, -0.1); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createRadialGradient(0, 0, -0.1, 0, 0, -0.1); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html
new file mode 100644
index 0000000000..4f068bbda5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.nonfinite</h1>
+<p class="desc">createRadialGradient() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("createRadialGradient() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(-Infinity, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(NaN, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, -Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, NaN, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, -Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, NaN, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, -Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, NaN, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, -Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, NaN, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, Infinity, Infinity); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.worker.js
new file mode 100644
index 0000000000..641a341ecc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.nonfinite.worker.js
@@ -0,0 +1,96 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.nonfinite
+// Description:createRadialGradient() throws TypeError if arguments are not finite
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createRadialGradient() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(-Infinity, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(NaN, 0, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, -Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, NaN, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, -Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, NaN, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, -Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, NaN, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, -Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, NaN, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, 0, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, Infinity, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(Infinity, 0, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, Infinity, 1, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, 0, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, Infinity, 0, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, Infinity, 1); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, Infinity, 0, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createRadialGradient(0, 0, 1, 0, Infinity, Infinity); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.html
new file mode 100644
index 0000000000..2643c67170
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.outside1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.outside1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 10, 200, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.worker.js
new file mode 100644
index 0000000000..52d45093ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside1.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.outside1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 10, 200, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.html
new file mode 100644
index 0000000000..423de4491d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.outside2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.outside2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.worker.js
new file mode 100644
index 0000000000..861dcccbe3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside2.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.outside2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.html
new file mode 100644
index 0000000000..61df3dccfe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.outside3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.outside3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.001, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.worker.js
new file mode 100644
index 0000000000..db65ea1b8b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.outside3.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.outside3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.001, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.html
new file mode 100644
index 0000000000..86e2af1428
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.touch1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.touch1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(150, 25, 50, 200, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.worker.js
new file mode 100644
index 0000000000..dd8d727209
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch1.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.touch1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(150, 25, 50, 200, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.html
new file mode 100644
index 0000000000..b0ff86ad05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.touch2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.touch2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.01, '#0f0');
+ g.addColorStop(0.99, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.worker.js
new file mode 100644
index 0000000000..ad6bc9d897
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch2.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.touch2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.01, '#0f0');
+ g.addColorStop(0.99, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.html
new file mode 100644
index 0000000000..5e085fd998
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.touch3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.touch3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, -15, 25, 140, -30, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.worker.js
new file mode 100644
index 0000000000..540e9a7ed4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.touch3.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.touch3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, -15, 25, 140, -30, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 1);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixelApprox(canvas, 50,48, 0,255,0,255, 1);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.html
new file mode 100644
index 0000000000..517c9e3288
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.transform.1</h1>
+<p class="desc">Radial gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<script>
+var t = async_test("Radial gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.worker.js
new file mode 100644
index 0000000000..198dab1fce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.1.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.transform.1
+// Description:Radial gradient coordinates are relative to the coordinate space at the time of filling
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Radial gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.html
new file mode 100644
index 0000000000..f7f4120a92
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.transform.2</h1>
+<p class="desc">Radial gradient coordinates are relative to the coordinate space at the time of filling</p>
+
+
+<script>
+var t = async_test("Radial gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(100, 0);
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.worker.js
new file mode 100644
index 0000000000..27793b90ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.2.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.transform.2
+// Description:Radial gradient coordinates are relative to the coordinate space at the time of filling
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Radial gradient coordinates are relative to the coordinate space at the time of filling");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(100, 0);
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.html
new file mode 100644
index 0000000000..e0ac17fa82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.gradient.radial.transform.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.gradient.radial.transform.3</h1>
+<p class="desc">Radial gradient transforms do not experience broken caching effects</p>
+
+
+<script>
+var t = async_test("Radial gradient transforms do not experience broken caching effects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.worker.js
new file mode 100644
index 0000000000..1b74d8b4d7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.gradient.radial.transform.3.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.gradient.radial.transform.3
+// Description:Radial gradient transforms do not experience broken caching effects
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Radial gradient transforms do not experience broken caching effects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.html
new file mode 100644
index 0000000000..f82a259e43
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.basic.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.basic.canvas</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.worker.js
new file mode 100644
index 0000000000..510723f009
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.canvas.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.basic.canvas
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.html
new file mode 100644
index 0000000000..ab7b7bbeb5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.basic.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.basic.image</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
new file mode 100644
index 0000000000..14cfcbc993
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.basic.image
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.html
new file mode 100644
index 0000000000..66e301a002
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.basic.nocontext</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.basic.nocontext</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.worker.js
new file mode 100644
index 0000000000..f6c24c82ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.nocontext.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.basic.nocontext
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.html
new file mode 100644
index 0000000000..1edb4fd227
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.basic.type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.basic.type</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(window.CanvasPattern, undefined, "window.CanvasPattern", "undefined");
+
+ window.CanvasPattern.prototype.thisImplementsCanvasPattern = true;
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ _assert(pattern.thisImplementsCanvasPattern, "pattern.thisImplementsCanvasPattern");
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js
new file mode 100644
index 0000000000..56a8a7d23c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.basic.type
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(self.CanvasPattern, undefined, "self.CanvasPattern", "undefined");
+
+ self.CanvasPattern.prototype.thisImplementsCanvasPattern = true;
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ _assert(pattern.thisImplementsCanvasPattern, "pattern.thisImplementsCanvasPattern");
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html
new file mode 100644
index 0000000000..7100c8313f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.basic.zerocanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.basic.zerocanvas</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 0;
+ canvas.height = 10;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 10, "canvas.height", "10");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+
+ canvas.width = 10;
+ canvas.height = 0;
+ _assertSame(canvas.width, 10, "canvas.width", "10");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+
+ canvas.width = 0;
+ canvas.height = 0;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.worker.js
new file mode 100644
index 0000000000..9bf487cf74
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.zerocanvas.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.basic.zerocanvas
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 0;
+ canvas.height = 10;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 10, "canvas.height", "10");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+
+ canvas.width = 10;
+ canvas.height = 0;
+ _assertSame(canvas.width, 10, "canvas.width", "10");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+
+ canvas.width = 0;
+ canvas.height = 0;
+ _assertSame(canvas.width, 0, "canvas.width", "0");
+ _assertSame(canvas.height, 0, "canvas.height", "0");
+ assert_throws_dom("INVALID_STATE_ERR", function() { ctx.createPattern(canvas, 'repeat'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.html
new file mode 100644
index 0000000000..bdf89021bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.crosscanvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.crosscanvas</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+
+ var pattern = new OffscreenCanvas(100, 50).getContext('2d').createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
new file mode 100644
index 0000000000..d9004fc028
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.crosscanvas
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+
+ var pattern = new OffscreenCanvas(100, 50).getContext('2d').createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.html
new file mode 100644
index 0000000000..fbec25862d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.image.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.image.null</h1>
+<p class="desc"></p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createPattern(null, 'repeat'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.worker.js
new file mode 100644
index 0000000000..a68ddfdbd7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.null.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.image.null
+// Description:
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createPattern(null, 'repeat'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.html
new file mode 100644
index 0000000000..ff7355d2a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.image.string</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.image.string</h1>
+<p class="desc"></p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createPattern('../images/red.png', 'repeat'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.worker.js
new file mode 100644
index 0000000000..dc53d82292
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.string.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.image.string
+// Description:
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createPattern('../images/red.png', 'repeat'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.html
new file mode 100644
index 0000000000..845f823e7f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.image.undefined</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.image.undefined</h1>
+<p class="desc"></p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createPattern(undefined, 'repeat'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.worker.js
new file mode 100644
index 0000000000..32f5749493
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.image.undefined.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.image.undefined
+// Description:
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createPattern(undefined, 'repeat'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.html
new file mode 100644
index 0000000000..ebeb92c7f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.modify.canvas1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.modify.canvas1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.worker.js
new file mode 100644
index 0000000000..8e4f9ed447
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas1.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.modify.canvas1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.html
new file mode 100644
index 0000000000..042ebba3b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.modify.canvas2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.modify.canvas2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.worker.js
new file mode 100644
index 0000000000..ed17db8127
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.modify.canvas2.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.modify.canvas2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html
new file mode 100644
index 0000000000..8ab1c8eb40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.norepeat.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.norepeat.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
new file mode 100644
index 0000000000..9ac4e1605c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.norepeat.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html
new file mode 100644
index 0000000000..4bdd356df7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.norepeat.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.norepeat.coord1</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
new file mode 100644
index 0000000000..6c4a1409b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.norepeat.coord1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html
new file mode 100644
index 0000000000..b9164ec6a9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.norepeat.coord2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.norepeat.coord2</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
new file mode 100644
index 0000000000..d2fdd86022
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.norepeat.coord2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/green.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html
new file mode 100644
index 0000000000..e2983948c2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.norepeat.coord3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.norepeat.coord3</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
new file mode 100644
index 0000000000..584a5d6cfd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.norepeat.coord3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html
new file mode 100644
index 0000000000..43a718d44a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.norepeat.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.norepeat.outside</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+ ctx.fillRect(-100, 0, 100, 50);
+ ctx.fillRect(0, 50, 100, 50);
+ ctx.fillRect(100, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
new file mode 100644
index 0000000000..0b5fef95ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.norepeat.outside
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+ ctx.fillRect(-100, 0, 100, 50);
+ ctx.fillRect(0, 50, 100, 50);
+ ctx.fillRect(100, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html
new file mode 100644
index 0000000000..b63535c706
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.orientation.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.orientation.canvas</h1>
+<p class="desc">Canvas patterns do not get flipped when painted</p>
+
+
+<script>
+var t = async_test("Canvas patterns do not get flipped when painted");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 25);
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 25, 100, 25);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.worker.js
new file mode 100644
index 0000000000..2fbe52af73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.orientation.canvas
+// Description:Canvas patterns do not get flipped when painted
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Canvas patterns do not get flipped when painted");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 25);
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 25, 100, 25);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html
new file mode 100644
index 0000000000..0b3645981e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.orientation.image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.orientation.image</h1>
+<p class="desc">Image patterns do not get flipped when painted</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/rrgg-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.save();
+ ctx.translate(0, -103);
+ ctx.fillRect(0, 103, 100, 50);
+ ctx.restore();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "Image patterns do not get flipped when painted");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
new file mode 100644
index 0000000000..b303b2d813
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.orientation.image
+// Description:Image patterns do not get flipped when painted
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/rrgg-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.save();
+ ctx.translate(0, -103);
+ ctx.fillRect(0, 103, 100, 50);
+ ctx.restore();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "Image patterns do not get flipped when painted");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html
new file mode 100644
index 0000000000..b6647f776e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeat.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeat.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
new file mode 100644
index 0000000000..37f4c04228
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeat.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html
new file mode 100644
index 0000000000..54b24ac705
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeat.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeat.coord1</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/rgrg-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
new file mode 100644
index 0000000000..efbd1c5846
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeat.coord1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/rgrg-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html
new file mode 100644
index 0000000000..a77ce2c696
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeat.coord2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeat.coord2</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/ggrr-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
new file mode 100644
index 0000000000..db707ecb0a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeat.coord2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/ggrr-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html
new file mode 100644
index 0000000000..10f380fed3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeat.coord3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeat.coord3</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/rgrg-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
new file mode 100644
index 0000000000..e58cddccfe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeat.coord3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/rgrg-256x256.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html
new file mode 100644
index 0000000000..6558e07c5b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeat.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeat.outside</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
new file mode 100644
index 0000000000..4a173af0b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeat.outside
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html
new file mode 100644
index 0000000000..494b72cfb9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeatx.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeatx.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 16);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
new file mode 100644
index 0000000000..e776f7a008
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeatx.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 16);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html
new file mode 100644
index 0000000000..6caef0cb1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeatx.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeatx.coord1</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.translate(0, 16);
+ ctx.fillRect(0, -16, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
new file mode 100644
index 0000000000..57c2102cc8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeatx.coord1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.translate(0, 16);
+ ctx.fillRect(0, -16, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html
new file mode 100644
index 0000000000..b3229c8d87
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeatx.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeatx.outside</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
new file mode 100644
index 0000000000..e40f6aa207
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeatx.outside
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html
new file mode 100644
index 0000000000..d88f3a0ab6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeaty.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeaty.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 50);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
new file mode 100644
index 0000000000..6a53b5dc4d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeaty.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 50);
+
+ var response = await fetch('/images/green-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html
new file mode 100644
index 0000000000..59bfeb2abf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeaty.coord1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeaty.coord1</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.translate(48, 0);
+ ctx.fillRect(-48, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
new file mode 100644
index 0000000000..d59abb6e07
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeaty.coord1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.translate(48, 0);
+ ctx.fillRect(-48, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html
new file mode 100644
index 0000000000..ad2bd3383f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.paint.repeaty.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.paint.repeaty.outside</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
new file mode 100644
index 0000000000..c4feba10ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.paint.repeaty.outside
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var response = await fetch('/images/red-16x16.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.html
new file mode 100644
index 0000000000..377ae3edc4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.repeat.case</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.repeat.case</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "Repeat"); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.worker.js
new file mode 100644
index 0000000000..8073f47ed0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.case.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.repeat.case
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "Repeat"); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.html
new file mode 100644
index 0000000000..c2197893d7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.repeat.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.repeat.empty</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var response = await fetch('/images/green-1x1.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, "");
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 200, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
new file mode 100644
index 0000000000..5aefc0dab3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.repeat.empty
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var response = await fetch('/images/green-1x1.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, "");
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 200, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.html
new file mode 100644
index 0000000000..d79551a302
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.repeat.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.repeat.null</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assert(ctx.createPattern(canvas, null) != null, "ctx.createPattern(canvas, null) != null");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.worker.js
new file mode 100644
index 0000000000..6ab4da9f8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.null.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.repeat.null
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assert(ctx.createPattern(canvas, null) != null, "ctx.createPattern(canvas, null) != null");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html
new file mode 100644
index 0000000000..2f4a35dedf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.repeat.nullsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.repeat.nullsuffix</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "repeat\0"); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.worker.js
new file mode 100644
index 0000000000..83e7c62fe5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.nullsuffix.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.repeat.nullsuffix
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "repeat\0"); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.html
new file mode 100644
index 0000000000..8ddd231985
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.repeat.undefined</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.repeat.undefined</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, undefined); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.worker.js
new file mode 100644
index 0000000000..f4f25ae5a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.undefined.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.repeat.undefined
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, undefined); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html
new file mode 100644
index 0000000000..3a37b46ab7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.repeat.unrecognised</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.repeat.unrecognised</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "invalid"); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.worker.js
new file mode 100644
index 0000000000..3114dde21a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognised.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.repeat.unrecognised
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "invalid"); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html
new file mode 100644
index 0000000000..58fb005d70
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.repeat.unrecognisednull</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.repeat.unrecognisednull</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "null"); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.worker.js
new file mode 100644
index 0000000000..c8d18511f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.unrecognisednull.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.repeat.unrecognisednull
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("SYNTAX_ERR", function() { ctx.createPattern(canvas, "null"); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.html
new file mode 100644
index 0000000000..2d73296395
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.transform.identity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.transform.identity</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform(new DOMMatrix());
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.worker.js
new file mode 100644
index 0000000000..a33c771346
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.identity.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.transform.identity
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform(new DOMMatrix());
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.html
new file mode 100644
index 0000000000..aa58ad119f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.transform.infinity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.transform.infinity</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform({a: Infinity});
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.worker.js
new file mode 100644
index 0000000000..999739b848
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.infinity.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.transform.infinity
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform({a: Infinity});
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.html
new file mode 100644
index 0000000000..f2420d4a2b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.pattern.transform.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.pattern.transform.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ assert_throws_js(TypeError, function() { pattern.setTransform({a: 1, m11: 2}); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.worker.js
new file mode 100644
index 0000000000..163951807f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform.invalid.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.pattern.transform.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ assert_throws_js(TypeError, function() { pattern.setTransform({a: 1, m11: 2}); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.colormix.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.colormix.html
new file mode 100644
index 0000000000..eba026619a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.colormix.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeStyle.colormix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeStyle.colormix</h1>
+<p class="desc">color-mix works as color input</p>
+
+
+<script>
+var t = async_test("color-mix works as color input");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = "color-mix(in srgb, red, blue)";
+ _assertSame(ctx.strokeStyle, 'color(srgb 0.5 0 0.5)', "ctx.strokeStyle", "'color(srgb 0.5 0 0.5)'");
+ ctx.strokeStyle = "color-mix(in srgb, red, color(srgb 1 0 0))";
+ _assertSame(ctx.strokeStyle, 'color(srgb 1 0 0)', "ctx.strokeStyle", "'color(srgb 1 0 0)'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.html b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.html
new file mode 100644
index 0000000000..cd7e424374
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.strokeStyle.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.strokeStyle.default</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.strokeStyle, '#000000', "ctx.strokeStyle", "'#000000'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.worker.js
new file mode 100644
index 0000000000..9e0ac8be3e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.strokeStyle.default.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.strokeStyle.default
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.strokeStyle, '#000000', "ctx.strokeStyle", "'#000000'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html
new file mode 100644
index 0000000000..d8f14529c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.blur.exceptions.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.blur.exceptions.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() blur.object</p>
+
+
+<script>
+var t = async_test("Test exceptions on CanvasFilter() blur.object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur'}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: undefined}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 'foo'}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1,2,3]}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: NaN}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: {}}); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.worker.js
new file mode 100644
index 0000000000..9b3fb3c04c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.blur.exceptions.tentative.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.blur.exceptions.tentative
+// Description:Test exceptions on CanvasFilter() blur.object
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test exceptions on CanvasFilter() blur.object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur'}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: undefined}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 'foo'}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1,2,3]}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: NaN}); });
+ assert_throws_js(TypeError, function() { ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: {}}); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html
new file mode 100644
index 0000000000..242094d9f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.colorMatrix.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.colorMatrix.tentative</h1>
+<p class="desc">Test the functionality of ColorMatrix filters in CanvasFilter objects</p>
+
+
+<script>
+var t = async_test("Test the functionality of ColorMatrix filters in CanvasFilter objects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: 'foo'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 'a']}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, Infinity]}); });
+ ctx.fillStyle = '#f00';
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 0});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 255,0,0,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 90});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,91,0,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 180});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,109,109,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 270});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 109,18,255,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'saturate', values: 0.5});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 155,27,27,255, 2);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'luminanceToAlpha'});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,0,0,54, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', values: [
+ 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0
+ ]});
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 25);
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 25, 50, 25);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(50, 25, 50, 25);
+ _assertPixelApprox(canvas, 10,10, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 60,10, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 10,30, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 60,30, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.worker.js
new file mode 100644
index 0000000000..d214e1d836
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.colorMatrix.tentative.worker.js
@@ -0,0 +1,64 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.colorMatrix.tentative
+// Description:Test the functionality of ColorMatrix filters in CanvasFilter objects
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test the functionality of ColorMatrix filters in CanvasFilter objects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: 'foo'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 'a']}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, Infinity]}); });
+ ctx.fillStyle = '#f00';
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 0});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 255,0,0,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 90});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,91,0,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 180});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,109,109,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 270});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 109,18,255,255, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'saturate', values: 0.5});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 155,27,27,255, 2);
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'luminanceToAlpha'});
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixelApprox(canvas, 10,10, 0,0,0,54, 2);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', values: [
+ 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0
+ ]});
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 25);
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 25, 50, 25);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(50, 25, 50, 25);
+ _assertPixelApprox(canvas, 10,10, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 60,10, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 10,30, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 60,30, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html
new file mode 100644
index 0000000000..ff1de6bc9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.componentTransfer.discrete.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.discrete.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with discrete type</p>
+
+
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with discrete type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k];
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'discrete', tableValues: tableValuesR},
+ funcG: {type: 'discrete', tableValues: tableValuesG},
+ funcB: {type: 'discrete', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.worker.js
new file mode 100644
index 0000000000..0e68f4899f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.discrete.tentative.worker.js
@@ -0,0 +1,62 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.componentTransfer.discrete.tentative
+// Description:Test pixels on CanvasFilter() componentTransfer with discrete type
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with discrete type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k];
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'discrete', tableValues: tableValuesR},
+ funcG: {type: 'discrete', tableValues: tableValuesG},
+ funcB: {type: 'discrete', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html
new file mode 100644
index 0000000000..64c30fc417
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.componentTransfer.gamma.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.gamma.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with gamma type</p>
+
+
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with gamma type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, amplitude, exponent, offset) {
+ return [
+ Math.max(0, Math.min(1, Math.pow(inputColor[0]/255, exponent[0]) * amplitude[0] + offset[0])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[1]/255, exponent[1]) * amplitude[1] + offset[1])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[2]/255, exponent[2]) * amplitude[2] + offset[2])) * 255,
+ ];
+ }
+
+ const amplitudes = [2, 1.1, 0.5];
+ const exponents = [5, 3, 1];
+ const offsets = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'gamma', amplitude: amplitudes[0], exponent: exponents[0], offset: offsets[0]},
+ funcG: {type: 'gamma', amplitude: amplitudes[1], exponent: exponents[1], offset: offsets[1]},
+ funcB: {type: 'gamma', amplitude: amplitudes[2], exponent: exponents[2], offset: offsets[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, amplitudes, exponents, offsets);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.worker.js
new file mode 100644
index 0000000000..d59bc699df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.gamma.tentative.worker.js
@@ -0,0 +1,53 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.componentTransfer.gamma.tentative
+// Description:Test pixels on CanvasFilter() componentTransfer with gamma type
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with gamma type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, amplitude, exponent, offset) {
+ return [
+ Math.max(0, Math.min(1, Math.pow(inputColor[0]/255, exponent[0]) * amplitude[0] + offset[0])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[1]/255, exponent[1]) * amplitude[1] + offset[1])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[2]/255, exponent[2]) * amplitude[2] + offset[2])) * 255,
+ ];
+ }
+
+ const amplitudes = [2, 1.1, 0.5];
+ const exponents = [5, 3, 1];
+ const offsets = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'gamma', amplitude: amplitudes[0], exponent: exponents[0], offset: offsets[0]},
+ funcG: {type: 'gamma', amplitude: amplitudes[1], exponent: exponents[1], offset: offsets[1]},
+ funcB: {type: 'gamma', amplitude: amplitudes[2], exponent: exponents[2], offset: offsets[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, amplitudes, exponents, offsets);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html
new file mode 100644
index 0000000000..e0d628952e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.componentTransfer.identity.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.identity.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with identity type</p>
+
+
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with identity type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'identity'},
+ funcG: {type: 'identity'},
+ funcB: {type: 'identity'},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`,
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixel(canvas, 5, 5, color[0],color[1],color[2],255);
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.worker.js
new file mode 100644
index 0000000000..1b714b58ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.identity.tentative.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.componentTransfer.identity.tentative
+// Description:Test pixels on CanvasFilter() componentTransfer with identity type
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with identity type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'identity'},
+ funcG: {type: 'identity'},
+ funcB: {type: 'identity'},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`,
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixel(canvas, 5, 5, color[0],color[1],color[2],255);
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html
new file mode 100644
index 0000000000..adbe557fd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.componentTransfer.linear.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.linear.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with linear type</p>
+
+
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with linear type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, slopes, intercepts) {
+ return [
+ Math.max(0, Math.min(1, inputColor[0]/255 * slopes[0] + intercepts[0])) * 255,
+ Math.max(0, Math.min(1, inputColor[1]/255 * slopes[1] + intercepts[1])) * 255,
+ Math.max(0, Math.min(1, inputColor[2]/255 * slopes[2] + intercepts[2])) * 255,
+ ];
+ }
+
+ const slopes = [0.5, 1.2, -0.2];
+ const intercepts = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'linear', slope: slopes[0], intercept: intercepts[0]},
+ funcG: {type: 'linear', slope: slopes[1], intercept: intercepts[1]},
+ funcB: {type: 'linear', slope: slopes[2], intercept: intercepts[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, slopes, intercepts);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.worker.js
new file mode 100644
index 0000000000..fb2e01876f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.linear.tentative.worker.js
@@ -0,0 +1,52 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.componentTransfer.linear.tentative
+// Description:Test pixels on CanvasFilter() componentTransfer with linear type
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with linear type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, slopes, intercepts) {
+ return [
+ Math.max(0, Math.min(1, inputColor[0]/255 * slopes[0] + intercepts[0])) * 255,
+ Math.max(0, Math.min(1, inputColor[1]/255 * slopes[1] + intercepts[1])) * 255,
+ Math.max(0, Math.min(1, inputColor[2]/255 * slopes[2] + intercepts[2])) * 255,
+ ];
+ }
+
+ const slopes = [0.5, 1.2, -0.2];
+ const intercepts = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'linear', slope: slopes[0], intercept: intercepts[0]},
+ funcG: {type: 'linear', slope: slopes[1], intercept: intercepts[1]},
+ funcB: {type: 'linear', slope: slopes[2], intercept: intercepts[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, slopes, intercepts);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html
new file mode 100644
index 0000000000..47048b68a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.componentTransfer.table.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.componentTransfer.table.tentative</h1>
+<p class="desc">Test pixels on CanvasFilter() componentTransfer with table type</p>
+
+
+<script>
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with table type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length - 1;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k] + (C - k/n) * n * (V[k + 1] - V[k]);
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'table', tableValues: tableValuesR},
+ funcG: {type: 'table', tableValues: tableValuesG},
+ funcB: {type: 'table', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.worker.js
new file mode 100644
index 0000000000..0799e73a58
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.componentTransfer.table.tentative.worker.js
@@ -0,0 +1,62 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.componentTransfer.table.tentative
+// Description:Test pixels on CanvasFilter() componentTransfer with table type
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test pixels on CanvasFilter() componentTransfer with table type");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length - 1;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k] + (C - k/n) * n * (V[k + 1] - V[k]);
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'table', tableValues: tableValuesR},
+ funcG: {type: 'table', tableValues: tableValuesG},
+ funcB: {type: 'table', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html
new file mode 100644
index 0000000000..301d11f888
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() convolveMatrix</p>
+
+
+<script>
+var t = async_test("Test exceptions on CanvasFilter() convolveMatrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', divisor: 2}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: 1}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 'a'], [0]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], 0]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0, Infinity]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: []}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1, 2, 3, 4]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], []]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 2], []]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], [1, 2]]}); });
+ // This should not throw an error
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[]]});
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1]]});
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.worker.js
new file mode 100644
index 0000000000..b4ce4d76b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative
+// Description:Test exceptions on CanvasFilter() convolveMatrix
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test exceptions on CanvasFilter() convolveMatrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', divisor: 2}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: 1}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 'a'], [0]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], 0]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0, Infinity]]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: []}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1, 2, 3, 4]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], []]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 2], []]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], [1, 2]]}); });
+ // This should not throw an error
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[]]});
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1]]});
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
new file mode 100644
index 0000000000..3e55e97e54
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.dropShadow.exceptions.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.dropShadow.exceptions.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() dropShadow object</p>
+
+
+<script>
+var t = async_test("Test exceptions on CanvasFilter() dropShadow object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // dx
+ _assert(new CanvasFilter({name: 'dropShadow', dx: 10}), "new CanvasFilter({name: 'dropShadow', dx: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: -1}), "new CanvasFilter({name: 'dropShadow', dx: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: 0.5}), "new CanvasFilter({name: 'dropShadow', dx: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: null}), "new CanvasFilter({name: 'dropShadow', dx: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: true}), "new CanvasFilter({name: 'dropShadow', dx: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: false}), "new CanvasFilter({name: 'dropShadow', dx: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: []}), "new CanvasFilter({name: 'dropShadow', dx: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: [20]}), "new CanvasFilter({name: 'dropShadow', dx: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: '30'}), "new CanvasFilter({name: 'dropShadow', dx: '30'})");
+ // dy
+ _assert(new CanvasFilter({name: 'dropShadow', dy: 10}), "new CanvasFilter({name: 'dropShadow', dy: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: -1}), "new CanvasFilter({name: 'dropShadow', dy: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: 0.5}), "new CanvasFilter({name: 'dropShadow', dy: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: null}), "new CanvasFilter({name: 'dropShadow', dy: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: true}), "new CanvasFilter({name: 'dropShadow', dy: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: false}), "new CanvasFilter({name: 'dropShadow', dy: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: []}), "new CanvasFilter({name: 'dropShadow', dy: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: [20]}), "new CanvasFilter({name: 'dropShadow', dy: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: '30'}), "new CanvasFilter({name: 'dropShadow', dy: '30'})");
+ // floodOpacity
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: 10}), "new CanvasFilter({name: 'dropShadow', floodOpacity: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: -1}), "new CanvasFilter({name: 'dropShadow', floodOpacity: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: 0.5}), "new CanvasFilter({name: 'dropShadow', floodOpacity: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: null}), "new CanvasFilter({name: 'dropShadow', floodOpacity: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: true}), "new CanvasFilter({name: 'dropShadow', floodOpacity: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: false}), "new CanvasFilter({name: 'dropShadow', floodOpacity: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: []}), "new CanvasFilter({name: 'dropShadow', floodOpacity: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: [20]}), "new CanvasFilter({name: 'dropShadow', floodOpacity: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: '30'}), "new CanvasFilter({name: 'dropShadow', floodOpacity: '30'})");
+ // stdDeviation
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: 10}), "new CanvasFilter({name: 'dropShadow', stdDeviation: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: -1}), "new CanvasFilter({name: 'dropShadow', stdDeviation: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: 0.5}), "new CanvasFilter({name: 'dropShadow', stdDeviation: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: null}), "new CanvasFilter({name: 'dropShadow', stdDeviation: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: true}), "new CanvasFilter({name: 'dropShadow', stdDeviation: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: false}), "new CanvasFilter({name: 'dropShadow', stdDeviation: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: []}), "new CanvasFilter({name: 'dropShadow', stdDeviation: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [20]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: '30'}), "new CanvasFilter({name: 'dropShadow', stdDeviation: '30'})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [10, -1]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [10, -1]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [0.5, null]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [0.5, null]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [true, false]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [true, false]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [[], [20]]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [[], [\""+(20)+"\"]]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: ['30', ['40']]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: ['30', ['40']]})");
+ // floodColor
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'red'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'red'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'canvas'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'canvas'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(4, -3, 0.5, 1)'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(4, -3, 0.5, 1)'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: '#aabbccdd'}), "new CanvasFilter({name: 'dropShadow', floodColor: '#aabbccdd'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: '#abcd'}), "new CanvasFilter({name: 'dropShadow', floodColor: '#abcd'})");
+
+ // dx
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: [1, 2]}); });
+ // dy
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: [1, 2]}); });
+ // floodOpacity
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: [1, 2]}); });
+ // stdDeviation
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, 2, 3]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, NaN]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, Infinity]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, -Infinity]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, undefined]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, 'test']}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, {}]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, [2, 3]]}); });
+ // floodColor
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(NaN, 3, 2, 1)'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 10}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: NaN}); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js
new file mode 100644
index 0000000000..bd18524f28
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js
@@ -0,0 +1,119 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.dropShadow.exceptions.tentative
+// Description:Test exceptions on CanvasFilter() dropShadow object
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test exceptions on CanvasFilter() dropShadow object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // dx
+ _assert(new CanvasFilter({name: 'dropShadow', dx: 10}), "new CanvasFilter({name: 'dropShadow', dx: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: -1}), "new CanvasFilter({name: 'dropShadow', dx: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: 0.5}), "new CanvasFilter({name: 'dropShadow', dx: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: null}), "new CanvasFilter({name: 'dropShadow', dx: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: true}), "new CanvasFilter({name: 'dropShadow', dx: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: false}), "new CanvasFilter({name: 'dropShadow', dx: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: []}), "new CanvasFilter({name: 'dropShadow', dx: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: [20]}), "new CanvasFilter({name: 'dropShadow', dx: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', dx: '30'}), "new CanvasFilter({name: 'dropShadow', dx: '30'})");
+ // dy
+ _assert(new CanvasFilter({name: 'dropShadow', dy: 10}), "new CanvasFilter({name: 'dropShadow', dy: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: -1}), "new CanvasFilter({name: 'dropShadow', dy: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: 0.5}), "new CanvasFilter({name: 'dropShadow', dy: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: null}), "new CanvasFilter({name: 'dropShadow', dy: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: true}), "new CanvasFilter({name: 'dropShadow', dy: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: false}), "new CanvasFilter({name: 'dropShadow', dy: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: []}), "new CanvasFilter({name: 'dropShadow', dy: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: [20]}), "new CanvasFilter({name: 'dropShadow', dy: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', dy: '30'}), "new CanvasFilter({name: 'dropShadow', dy: '30'})");
+ // floodOpacity
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: 10}), "new CanvasFilter({name: 'dropShadow', floodOpacity: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: -1}), "new CanvasFilter({name: 'dropShadow', floodOpacity: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: 0.5}), "new CanvasFilter({name: 'dropShadow', floodOpacity: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: null}), "new CanvasFilter({name: 'dropShadow', floodOpacity: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: true}), "new CanvasFilter({name: 'dropShadow', floodOpacity: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: false}), "new CanvasFilter({name: 'dropShadow', floodOpacity: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: []}), "new CanvasFilter({name: 'dropShadow', floodOpacity: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: [20]}), "new CanvasFilter({name: 'dropShadow', floodOpacity: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodOpacity: '30'}), "new CanvasFilter({name: 'dropShadow', floodOpacity: '30'})");
+ // stdDeviation
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: 10}), "new CanvasFilter({name: 'dropShadow', stdDeviation: 10})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: -1}), "new CanvasFilter({name: 'dropShadow', stdDeviation: -1})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: 0.5}), "new CanvasFilter({name: 'dropShadow', stdDeviation: 0.5})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: null}), "new CanvasFilter({name: 'dropShadow', stdDeviation: null})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: true}), "new CanvasFilter({name: 'dropShadow', stdDeviation: true})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: false}), "new CanvasFilter({name: 'dropShadow', stdDeviation: false})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: []}), "new CanvasFilter({name: 'dropShadow', stdDeviation: []})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [20]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [\""+(20)+"\"]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: '30'}), "new CanvasFilter({name: 'dropShadow', stdDeviation: '30'})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [10, -1]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [10, -1]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [0.5, null]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [0.5, null]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [true, false]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [true, false]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: [[], [20]]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: [[], [\""+(20)+"\"]]})");
+ _assert(new CanvasFilter({name: 'dropShadow', stdDeviation: ['30', ['40']]}), "new CanvasFilter({name: 'dropShadow', stdDeviation: ['30', ['40']]})");
+ // floodColor
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'red'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'red'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'canvas'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'canvas'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(4, -3, 0.5, 1)'}), "new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(4, -3, 0.5, 1)'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: '#aabbccdd'}), "new CanvasFilter({name: 'dropShadow', floodColor: '#aabbccdd'})");
+ _assert(new CanvasFilter({name: 'dropShadow', floodColor: '#abcd'}), "new CanvasFilter({name: 'dropShadow', floodColor: '#abcd'})");
+
+ // dx
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dx: [1, 2]}); });
+ // dy
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', dy: [1, 2]}); });
+ // floodOpacity
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodOpacity: [1, 2]}); });
+ // stdDeviation
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: NaN}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: -Infinity}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: {}}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, 2, 3]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, NaN]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, Infinity]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, -Infinity]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, undefined]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, 'test']}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, {}]}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', stdDeviation: [1, [2, 3]]}); });
+ // floodColor
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 'test'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 'rgba(NaN, 3, 2, 1)'}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: 10}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: undefined}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: null}); });
+ assert_throws_js(TypeError, function() { new CanvasFilter({name: 'dropShadow', floodColor: NaN}); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html
new file mode 100644
index 0000000000..86c5710132
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative-expected.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.dropShadow.tentative</title>
+<h1>2d.filter.canvasFilterObject.dropShadow.tentative</h1>
+<p class="desc">Test CanvasFilter() dropShadow object.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width=520 height=420
+ color-interpolation-filters="sRGB">
+ <rect x=0 y=0 width=100% height=50 fill="teal" />
+ <rect x=0 y=100 width=100% height=50 fill="teal" />
+ <rect x=0 y=200 width=100% height=50 fill="teal" />
+ <rect x=0 y=300 width=100% height=50 fill="teal" />
+
+ <rect x=10 y=10 width=80 height=80 fill="crimson"
+ filter="drop-shadow(2px 2px 2px black)"/>
+ <rect x=110 y=10 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 5px rgba(128, 0, 128, 0.7))"/>
+
+ <rect x=10 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px purple)"/>
+ <rect x=110 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px LinkText)"/>
+ <rect x=210 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 1))"/>
+ <rect x=310 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 0.7))"/>
+ <rect x=410 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 0.49))"/>
+
+ <rect x=10 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+ <rect x=110 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 5px purple)"/>
+ <rect x=210 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+ <filter id="separable-filter"
+ x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx=9 dy=12 stdDeviation="3 5" flood-color="purple"/>
+ </filter>
+ <rect x=310 y=210 width=80 height=80 fill="crimson"
+ filter="url(#separable-filter)"/>
+ <rect x=410 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+
+ <rect x=10 y=310 width=80 height=80 fill="crimson"
+ filter="drop-shadow(-5px 0px 0px purple)"/>
+ <filter id="separable-filter-degenerate"
+ x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx=0 dy=5 stdDeviation="0 3"
+ flood-color="rgba(128, 0, 128, 0.8)"/>
+ </filter>
+ <rect x=110 y=310 width=80 height=80 fill="crimson"
+ filter="url(#separable-filter-degenerate)"/>
+ <rect x=210 y=310 width=80 height=80 fill="crimson"
+ filter="drop-shadow(1px 10px 0px rgba(128, 0, 128, 0.4))"/>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html
new file mode 100644
index 0000000000..81eb1eae45
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.dropShadow.tentative-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.dropShadow.tentative</title>
+<h1>2d.filter.canvasFilterObject.dropShadow.tentative</h1>
+<p class="desc">Test CanvasFilter() dropShadow object.</p>
+<canvas id="canvas" width="520" height="420">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(520, 420);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(0, 0, 520, 50);
+ ctx.fillRect(0, 100, 520, 50);
+ ctx.fillRect(0, 200, 520, 50);
+ ctx.fillRect(0, 300, 520, 50);
+
+ ctx.fillStyle = 'crimson';
+
+ // Parameter defaults.
+ ctx.filter = new CanvasFilter({name: 'dropShadow'});
+ ctx.fillRect(10, 10, 80, 80);
+
+ // All parameters specified.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple', floodOpacity: 0.7});
+ ctx.fillRect(110, 10, 80, 80);
+
+ // Named color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 110, 80, 80);
+
+ // System color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'LinkText'});
+ ctx.fillRect(110, 110, 80, 80);
+
+ // Numerical color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 1)'});
+ ctx.fillRect(210, 110, 80, 80);
+
+ // Transparent floodColor.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)'});
+ ctx.fillRect(310, 110, 80, 80);
+
+ // Transparent floodColor and floodOpacity.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)', floodOpacity: 0.7});
+ ctx.fillRect(410, 110, 80, 80);
+
+ // No blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 0,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 210, 80, 80);
+
+ // Single float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple'});
+ ctx.fillRect(110, 210, 80, 80);
+
+ // Single negative float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: -5,
+ floodColor: 'purple'});
+ ctx.fillRect(210, 210, 80, 80);
+
+ // Two floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [3, 5],
+ floodColor: 'purple'});
+ ctx.fillRect(310, 210, 80, 80);
+
+ // Two negative floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [-3, -5],
+ floodColor: 'purple'});
+ ctx.fillRect(410, 210, 80, 80);
+
+ // Degenerate parameter values.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: [-5], dy: [], stdDeviation: null,
+ floodColor: 'purple', floodOpacity: [2]});
+ ctx.fillRect(10, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: null, dy: '5', stdDeviation: [[-5], ['3']],
+ floodColor: 'purple', floodOpacity: '0.8'});
+ ctx.fillRect(110, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: true, dy: ['10'], stdDeviation: false,
+ floodColor: 'purple', floodOpacity: ['0.4']});
+ ctx.fillRect(210, 310, 80, 80);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.w.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.w.html
new file mode 100644
index 0000000000..fe9087244a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.w.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.filter.canvasFilterObject.dropShadow.tentative-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.dropShadow.tentative</title>
+<h1>2d.filter.canvasFilterObject.dropShadow.tentative</h1>
+<p class="desc">Test CanvasFilter() dropShadow object.</p>
+<canvas id="canvas" width="520" height="420">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(520, 420);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(0, 0, 520, 50);
+ ctx.fillRect(0, 100, 520, 50);
+ ctx.fillRect(0, 200, 520, 50);
+ ctx.fillRect(0, 300, 520, 50);
+
+ ctx.fillStyle = 'crimson';
+
+ // Parameter defaults.
+ ctx.filter = new CanvasFilter({name: 'dropShadow'});
+ ctx.fillRect(10, 10, 80, 80);
+
+ // All parameters specified.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple', floodOpacity: 0.7});
+ ctx.fillRect(110, 10, 80, 80);
+
+ // Named color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 110, 80, 80);
+
+ // System color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'LinkText'});
+ ctx.fillRect(110, 110, 80, 80);
+
+ // Numerical color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 1)'});
+ ctx.fillRect(210, 110, 80, 80);
+
+ // Transparent floodColor.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)'});
+ ctx.fillRect(310, 110, 80, 80);
+
+ // Transparent floodColor and floodOpacity.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)', floodOpacity: 0.7});
+ ctx.fillRect(410, 110, 80, 80);
+
+ // No blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 0,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 210, 80, 80);
+
+ // Single float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple'});
+ ctx.fillRect(110, 210, 80, 80);
+
+ // Single negative float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: -5,
+ floodColor: 'purple'});
+ ctx.fillRect(210, 210, 80, 80);
+
+ // Two floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [3, 5],
+ floodColor: 'purple'});
+ ctx.fillRect(310, 210, 80, 80);
+
+ // Two negative floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [-3, -5],
+ floodColor: 'purple'});
+ ctx.fillRect(410, 210, 80, 80);
+
+ // Degenerate parameter values.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: [-5], dy: [], stdDeviation: null,
+ floodColor: 'purple', floodOpacity: [2]});
+ ctx.fillRect(10, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: null, dy: '5', stdDeviation: [[-5], ['3']],
+ floodColor: 'purple', floodOpacity: '0.8'});
+ ctx.fillRect(110, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: true, dy: ['10'], stdDeviation: false,
+ floodColor: 'purple', floodOpacity: ['0.4']});
+ ctx.fillRect(210, 310, 80, 80);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html
new file mode 100644
index 0000000000..f9571f208e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="4 4" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html
new file mode 100644
index 0000000000..4417a1917c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.w.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.w.html
new file mode 100644
index 0000000000..d2024ad205
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.isotropic</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html
new file mode 100644
index 0000000000..e76613271f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="4 1" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html
new file mode 100644
index 0000000000..2ea26359fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 1],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.w.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.w.html
new file mode 100644
index 0000000000..fa49ea4682
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-x</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 1],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html
new file mode 100644
index 0000000000..0f214fca9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="1 4" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html
new file mode 100644
index 0000000000..a9783ccb6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [1, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.w.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.w.html
new file mode 100644
index 0000000000..ab83f50ea0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.mostly-y</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [1, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html
new file mode 100644
index 0000000000..285a641726
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="4 0" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html
new file mode 100644
index 0000000000..3028c2a700
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 0],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.w.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.w.html
new file mode 100644
index 0000000000..e8482cf3ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.x-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [4, 0],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html
new file mode 100644
index 0000000000..d59945b5da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100" height="100"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="0 4" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html
new file mode 100644
index 0000000000..fca324716b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [0, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.w.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.w.html
new file mode 100644
index 0000000000..50cfb3083a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only-expected.html">
+<title>Canvas test: 2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</title>
+<h1>2d.filter.canvasFilterObject.gaussianBlur.tentative.y-only</h1>
+<p class="desc">Test CanvasFilter() with gaussianBlur.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [0, 4],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.html
new file mode 100644
index 0000000000..fb99ef7d1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.tentative</h1>
+<p class="desc">Test CanvasFilter() object</p>
+
+
+<script>
+var t = async_test("Test CanvasFilter() object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1, 2]});
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = new CanvasFilter([
+ {name: 'gaussianBlur', stdDeviation: 5},
+ {name: 'gaussianBlur', stdDeviation: 10}
+ ]);
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.filter = ctx.filter;
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'none';
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ ctx.filter = 'this string is not a filter and should do nothing';
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.worker.js
new file mode 100644
index 0000000000..db95e0b006
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.tentative.worker.js
@@ -0,0 +1,44 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.tentative
+// Description:Test CanvasFilter() object
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test CanvasFilter() object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1, 2]});
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = new CanvasFilter([
+ {name: 'gaussianBlur', stdDeviation: 5},
+ {name: 'gaussianBlur', stdDeviation: 10}
+ ]);
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.filter = ctx.filter;
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'none';
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ ctx.filter = 'this string is not a filter and should do nothing';
+ _assert(ctx.filter.toString() == '[object CanvasFilter]', "ctx.filter.toString() == '[object CanvasFilter]'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html
new file mode 100644
index 0000000000..040c62a96b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.canvasFilterObject.turbulence.inputTypes.tentative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.canvasFilterObject.turbulence.inputTypes.tentative</h1>
+<p class="desc">Test exceptions on CanvasFilter() turbulence object</p>
+
+
+<script>
+var t = async_test("Test exceptions on CanvasFilter() turbulence object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const errorTestCases = [
+ {baseFrequency: {}},
+ {baseFrequency: -1},
+ {baseFrequency: [0, -1]},
+ {baseFrequency: NaN},
+ {baseFrequency: Infinity},
+ {baseFrequency: undefined},
+ {baseFrequency: -Infinity},
+ {baseFrequency: 'test'},
+
+ {numOctaves: {}},
+ {numOctaves: -1},
+ {numOctaves: NaN},
+ {numOctaves: Infinity},
+ {numOctaves: undefined},
+ {numOctaves: -Infinity},
+ {numOctaves: [1, 1]},
+ {numOctaves: 'test'},
+
+ {seed: {}},
+ {seed: NaN},
+ {seed: Infinity},
+ {seed: undefined},
+ {seed: -Infinity},
+ {seed: [1, 1]},
+ {seed: 'test'},
+
+ {stitchTiles: {}},
+ {stitchTiles: NaN},
+ {stitchTiles: Infinity},
+ {stitchTiles: undefined},
+ {stitchTiles: -Infinity},
+ {stitchTiles: [1, 1]},
+ {stitchTiles: 'test'},
+ {stitchTiles: null},
+ {stitchTiles: []},
+ {stitchTiles: [10]},
+ {stitchTiles: 30},
+ {stitchTiles: false},
+ {stitchTiles: true},
+ {stitchTiles: '10'},
+ {stitchTiles: -1},
+
+ {type: {}},
+ {type: NaN},
+ {type: Infinity},
+ {type: undefined},
+ {type: -Infinity},
+ {type: [1, 1]},
+ {type: 'test'},
+ {type: null},
+ {type: []},
+ {type: [10]},
+ {type: 30},
+ {type: false},
+ {type: true},
+ {type: '10'},
+ {type: -1},
+ ]
+
+ // null and [] = 0 when parsed as number
+ const workingTestCases = [
+ {baseFrequency: null},
+ {baseFrequency: []},
+ {baseFrequency: [10]},
+ {baseFrequency: [10, 3]},
+ {baseFrequency: 30},
+ {baseFrequency: false},
+ {baseFrequency: true},
+ {baseFrequency: '10'},
+
+ {numOctaves: null},
+ {numOctaves: []},
+ {numOctaves: [10]},
+ {numOctaves: 30},
+ {numOctaves: false},
+ {numOctaves: true},
+ {numOctaves: '10'},
+
+ {seed: null},
+ {seed: []},
+ {seed: [10]},
+ {seed: 30},
+ {seed: false},
+ {seed: true},
+ {seed: '10'},
+ {seed: -1},
+
+ {stitchTiles: 'stitch'},
+ {stitchTiles: 'noStitch'},
+
+ {type: 'fractalNoise'},
+ {type: 'turbulence'},
+ ]
+
+ for (testCase of errorTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ assert_throws_js(TypeError, function() { new CanvasFilter(filterOptions); });
+ }
+
+ for (testCase of workingTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ _assert(new CanvasFilter(filterOptions) != null, "new CanvasFilter(filterOptions) != null");
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.worker.js
new file mode 100644
index 0000000000..1a950a9207
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.turbulence.inputTypes.tentative.worker.js
@@ -0,0 +1,125 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.canvasFilterObject.turbulence.inputTypes.tentative
+// Description:Test exceptions on CanvasFilter() turbulence object
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test exceptions on CanvasFilter() turbulence object");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const errorTestCases = [
+ {baseFrequency: {}},
+ {baseFrequency: -1},
+ {baseFrequency: [0, -1]},
+ {baseFrequency: NaN},
+ {baseFrequency: Infinity},
+ {baseFrequency: undefined},
+ {baseFrequency: -Infinity},
+ {baseFrequency: 'test'},
+
+ {numOctaves: {}},
+ {numOctaves: -1},
+ {numOctaves: NaN},
+ {numOctaves: Infinity},
+ {numOctaves: undefined},
+ {numOctaves: -Infinity},
+ {numOctaves: [1, 1]},
+ {numOctaves: 'test'},
+
+ {seed: {}},
+ {seed: NaN},
+ {seed: Infinity},
+ {seed: undefined},
+ {seed: -Infinity},
+ {seed: [1, 1]},
+ {seed: 'test'},
+
+ {stitchTiles: {}},
+ {stitchTiles: NaN},
+ {stitchTiles: Infinity},
+ {stitchTiles: undefined},
+ {stitchTiles: -Infinity},
+ {stitchTiles: [1, 1]},
+ {stitchTiles: 'test'},
+ {stitchTiles: null},
+ {stitchTiles: []},
+ {stitchTiles: [10]},
+ {stitchTiles: 30},
+ {stitchTiles: false},
+ {stitchTiles: true},
+ {stitchTiles: '10'},
+ {stitchTiles: -1},
+
+ {type: {}},
+ {type: NaN},
+ {type: Infinity},
+ {type: undefined},
+ {type: -Infinity},
+ {type: [1, 1]},
+ {type: 'test'},
+ {type: null},
+ {type: []},
+ {type: [10]},
+ {type: 30},
+ {type: false},
+ {type: true},
+ {type: '10'},
+ {type: -1},
+ ]
+
+ // null and [] = 0 when parsed as number
+ const workingTestCases = [
+ {baseFrequency: null},
+ {baseFrequency: []},
+ {baseFrequency: [10]},
+ {baseFrequency: [10, 3]},
+ {baseFrequency: 30},
+ {baseFrequency: false},
+ {baseFrequency: true},
+ {baseFrequency: '10'},
+
+ {numOctaves: null},
+ {numOctaves: []},
+ {numOctaves: [10]},
+ {numOctaves: 30},
+ {numOctaves: false},
+ {numOctaves: true},
+ {numOctaves: '10'},
+
+ {seed: null},
+ {seed: []},
+ {seed: [10]},
+ {seed: 30},
+ {seed: false},
+ {seed: true},
+ {seed: '10'},
+ {seed: -1},
+
+ {stitchTiles: 'stitch'},
+ {stitchTiles: 'noStitch'},
+
+ {type: 'fractalNoise'},
+ {type: 'turbulence'},
+ ]
+
+ for (testCase of errorTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ assert_throws_js(TypeError, function() { new CanvasFilter(filterOptions); });
+ }
+
+ for (testCase of workingTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ _assert(new CanvasFilter(filterOptions) != null, "new CanvasFilter(filterOptions) != null");
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.html
new file mode 100644
index 0000000000..5594174cf6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.filter.value</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.filter.value</h1>
+<p class="desc">test if ctx.filter works correctly</p>
+
+
+<script>
+var t = async_test("test if ctx.filter works correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.save();
+ ctx.filter = 'none';
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.restore();
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'blur(10)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'blur 10px';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'inherit';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'initial';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'unset';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = '';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = null;
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = undefined;
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'blur( 5px)';
+ assert_equals(ctx.filter, 'blur( 5px)');
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.worker.js
new file mode 100644
index 0000000000..2308315b63
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.value.worker.js
@@ -0,0 +1,51 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.filter.value
+// Description:test if ctx.filter works correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("test if ctx.filter works correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.filter = 'blur(5px)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.save();
+ ctx.filter = 'none';
+ _assert(ctx.filter == 'none', "ctx.filter == 'none'");
+ ctx.restore();
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'blur(10)';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'blur 10px';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'inherit';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'initial';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = 'unset';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = '';
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = null;
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+ ctx.filter = undefined;
+ _assert(ctx.filter == 'blur(5px)', "ctx.filter == 'blur(5px)'");
+
+ ctx.filter = 'blur( 5px)';
+ assert_equals(ctx.filter, 'blur( 5px)');
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic-expected.html
new file mode 100644
index 0000000000..6a6f0f6892
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.isotropic</title>
+<h1>2d.layer.anisotropic-blur.isotropic</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="4 4" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.html
new file mode 100644
index 0000000000..d59a4ccf62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.isotropic-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.isotropic</title>
+<h1>2d.layer.anisotropic-blur.isotropic</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.w.html
new file mode 100644
index 0000000000..15961d5c7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.anisotropic-blur.isotropic-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.isotropic</title>
+<h1>2d.layer.anisotropic-blur.isotropic</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x-expected.html
new file mode 100644
index 0000000000..b0473f2ff0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-x</title>
+<h1>2d.layer.anisotropic-blur.mostly-x</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="4 1" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.html
new file mode 100644
index 0000000000..c5ad2ee6c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.mostly-x-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-x</title>
+<h1>2d.layer.anisotropic-blur.mostly-x</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 1] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.w.html
new file mode 100644
index 0000000000..60f5ac3ebd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.anisotropic-blur.mostly-x-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-x</title>
+<h1>2d.layer.anisotropic-blur.mostly-x</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 1] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y-expected.html
new file mode 100644
index 0000000000..43da016eea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-y</title>
+<h1>2d.layer.anisotropic-blur.mostly-y</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="1 4" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.html
new file mode 100644
index 0000000000..9545257cfa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.mostly-y-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-y</title>
+<h1>2d.layer.anisotropic-blur.mostly-y</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [1, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.w.html
new file mode 100644
index 0000000000..d533ad3187
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.anisotropic-blur.mostly-y-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.mostly-y</title>
+<h1>2d.layer.anisotropic-blur.mostly-y</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [1, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only-expected.html
new file mode 100644
index 0000000000..0b4d269189
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.x-only</title>
+<h1>2d.layer.anisotropic-blur.x-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="4 0" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.html
new file mode 100644
index 0000000000..4552ad4433
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.x-only-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.x-only</title>
+<h1>2d.layer.anisotropic-blur.x-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 0] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.w.html
new file mode 100644
index 0000000000..38abb21cd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.anisotropic-blur.x-only-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.x-only</title>
+<h1>2d.layer.anisotropic-blur.x-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [4, 0] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only-expected.html
new file mode 100644
index 0000000000..2572386412
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.anisotropic-blur.y-only</title>
+<h1>2d.layer.anisotropic-blur.y-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="0 4" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.html
new file mode 100644
index 0000000000..87dbcd3708
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.anisotropic-blur.y-only-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.y-only</title>
+<h1>2d.layer.anisotropic-blur.y-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [0, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.w.html
new file mode 100644
index 0000000000..062328db75
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.anisotropic-blur.y-only-expected.html">
+<title>Canvas test: 2d.layer.anisotropic-blur.y-only</title>
+<h1>2d.layer.anisotropic-blur.y-only</h1>
+<p class="desc">Checks that layers allow gaussian blur with separate X and Y components.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: { name: 'gaussianBlur', stdDeviation: [0, 4] }});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.html
new file mode 100644
index 0000000000..4fb042a1d8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.beginLayer-options</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.beginLayer-options</h1>
+<p class="desc">Checks beginLayer works for different option parameter values</p>
+
+
+<script>
+var t = async_test("Checks beginLayer works for different option parameter values");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer(); ctx.endLayer();
+ ctx.beginLayer(null); ctx.endLayer();
+ ctx.beginLayer(undefined); ctx.endLayer();
+ ctx.beginLayer([]); ctx.endLayer();
+ ctx.beginLayer({}); ctx.endLayer();
+
+ assert_throws_js(TypeError, function() { ctx.beginLayer(''); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(0); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(1); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(true); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(false); });
+
+ ctx.beginLayer({filter: null}); ctx.endLayer();
+ ctx.beginLayer({filter: undefined}); ctx.endLayer();
+ ctx.beginLayer({filter: []}); ctx.endLayer();
+ ctx.beginLayer({filter: {}}); ctx.endLayer();
+ ctx.beginLayer({filter: {name: "unknown"}}); ctx.endLayer();
+ ctx.beginLayer({filter: ''}); ctx.endLayer();
+
+ // These cases don't throw TypeError since they can be casted to a
+ // DOMString.
+ ctx.beginLayer({filter: 0}); ctx.endLayer();
+ ctx.beginLayer({filter: 1}); ctx.endLayer();
+ ctx.beginLayer({filter: true}); ctx.endLayer();
+ ctx.beginLayer({filter: false}); ctx.endLayer();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.worker.js
new file mode 100644
index 0000000000..cafbc83f3e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.beginLayer-options.worker.js
@@ -0,0 +1,46 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.beginLayer-options
+// Description:Checks beginLayer works for different option parameter values
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Checks beginLayer works for different option parameter values");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer(); ctx.endLayer();
+ ctx.beginLayer(null); ctx.endLayer();
+ ctx.beginLayer(undefined); ctx.endLayer();
+ ctx.beginLayer([]); ctx.endLayer();
+ ctx.beginLayer({}); ctx.endLayer();
+
+ assert_throws_js(TypeError, function() { ctx.beginLayer(''); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(0); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(1); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(true); });
+ assert_throws_js(TypeError, function() { ctx.beginLayer(false); });
+
+ ctx.beginLayer({filter: null}); ctx.endLayer();
+ ctx.beginLayer({filter: undefined}); ctx.endLayer();
+ ctx.beginLayer({filter: []}); ctx.endLayer();
+ ctx.beginLayer({filter: {}}); ctx.endLayer();
+ ctx.beginLayer({filter: {name: "unknown"}}); ctx.endLayer();
+ ctx.beginLayer({filter: ''}); ctx.endLayer();
+
+ // These cases don't throw TypeError since they can be casted to a
+ // DOMString.
+ ctx.beginLayer({filter: 0}); ctx.endLayer();
+ ctx.beginLayer({filter: 1}); ctx.endLayer();
+ ctx.beginLayer({filter: true}); ctx.endLayer();
+ ctx.beginLayer({filter: false}); ctx.endLayer();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html
new file mode 100644
index 0000000000..6cad180f14
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping-expected.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.no-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.no-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="30" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="201" y="50" width="100" height="100" fill="turquoise"/>
+ <rect x="50" y="201" width="100" height="100" fill="indigo"/>
+ <rect x="-101" y="50" width="100" height="100" fill="orange"/>
+ <rect x="50" y="-101" width="100" height="100" fill="brown"/>
+ </g>
+ </svg>`;
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ // No clipping.
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.html
new file mode 100644
index 0000000000..3cd1c674a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.blur-from-outside-canvas.no-clipping-expected.html">
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.no-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.no-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [ {name: 'gaussianBlur', stdDeviation: 30} ]});
+
+ ctx.fillStyle = 'turquoise';
+ ctx.fillRect(201, 50, 100, 100);
+ ctx.fillStyle = 'indigo';
+ ctx.fillRect(50, 201, 100, 100);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(-1, 50, -100, 100);
+ ctx.fillStyle = 'brown';
+ ctx.fillRect(50, -1, 100, -100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.w.html
new file mode 100644
index 0000000000..90b013c077
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.no-clipping.w.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.blur-from-outside-canvas.no-clipping-expected.html">
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.no-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.no-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [ {name: 'gaussianBlur', stdDeviation: 30} ]});
+
+ ctx.fillStyle = 'turquoise';
+ ctx.fillRect(201, 50, 100, 100);
+ ctx.fillStyle = 'indigo';
+ ctx.fillRect(50, 201, 100, 100);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(-1, 50, -100, 100);
+ ctx.fillStyle = 'brown';
+ ctx.fillRect(50, -1, 100, -100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html
new file mode 100644
index 0000000000..1823f78983
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping-expected.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.with-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.with-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="30" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="201" y="50" width="100" height="100" fill="turquoise"/>
+ <rect x="50" y="201" width="100" height="100" fill="indigo"/>
+ <rect x="-101" y="50" width="100" height="100" fill="orange"/>
+ <rect x="50" y="-101" width="100" height="100" fill="brown"/>
+ </g>
+ </svg>`;
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.html
new file mode 100644
index 0000000000..76477445e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.blur-from-outside-canvas.with-clipping-expected.html">
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.with-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.with-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [ {name: 'gaussianBlur', stdDeviation: 30} ]});
+
+ ctx.fillStyle = 'turquoise';
+ ctx.fillRect(201, 50, 100, 100);
+ ctx.fillStyle = 'indigo';
+ ctx.fillRect(50, 201, 100, 100);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(-1, 50, -100, 100);
+ ctx.fillStyle = 'brown';
+ ctx.fillRect(50, -1, 100, -100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.w.html
new file mode 100644
index 0000000000..cf5570c932
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.blur-from-outside-canvas.with-clipping.w.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.blur-from-outside-canvas.with-clipping-expected.html">
+<title>Canvas test: 2d.layer.blur-from-outside-canvas.with-clipping</title>
+<h1>2d.layer.blur-from-outside-canvas.with-clipping</h1>
+<p class="desc">Checks blur leaking inside from drawing outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [ {name: 'gaussianBlur', stdDeviation: 30} ]});
+
+ ctx.fillStyle = 'turquoise';
+ ctx.fillRect(201, 50, 100, 100);
+ ctx.fillStyle = 'indigo';
+ ctx.fillRect(50, 201, 100, 100);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(-1, 50, -100, 100);
+ ctx.fillStyle = 'brown';
+ ctx.fillRect(50, -1, 100, -100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full-expected.html
new file mode 100644
index 0000000000..5a47f8e118
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clearRect.full</title>
+<h1>2d.layer.clearRect.full</h1>
+<p class="desc">clearRect inside a layer can clear all of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.html
new file mode 100644
index 0000000000..ec9f453b69
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clearRect.full-expected.html">
+<title>Canvas test: 2d.layer.clearRect.full</title>
+<h1>2d.layer.clearRect.full</h1>
+<p class="desc">clearRect inside a layer can clear all of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.fillStyle = 'green';
+ ctx.clearRect(0, 0, 100, 100);
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.w.html
new file mode 100644
index 0000000000..a6f4021dd5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.full.w.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.clearRect.full-expected.html">
+<title>Canvas test: 2d.layer.clearRect.full</title>
+<h1>2d.layer.clearRect.full</h1>
+<p class="desc">clearRect inside a layer can clear all of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.fillStyle = 'green';
+ ctx.clearRect(0, 0, 100, 100);
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial-expected.html
new file mode 100644
index 0000000000..ac75105cec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial-expected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clearRect.partial</title>
+<h1>2d.layer.clearRect.partial</h1>
+<p class="desc">clearRect inside a layer can clear a portion of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 10);
+ ctx.fillRect(20, 60, 80, 10);
+ ctx.fillRect(20, 20, 10, 50);
+ ctx.fillRect(90, 20, 10, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.html
new file mode 100644
index 0000000000..88509a8a40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clearRect.partial-expected.html">
+<title>Canvas test: 2d.layer.clearRect.partial</title>
+<h1>2d.layer.clearRect.partial</h1>
+<p class="desc">clearRect inside a layer can clear a portion of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.clearRect(30, 30, 60, 30);
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.w.html
new file mode 100644
index 0000000000..c6ba41afd7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clearRect.partial.w.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.clearRect.partial-expected.html">
+<title>Canvas test: 2d.layer.clearRect.partial</title>
+<h1>2d.layer.clearRect.partial</h1>
+<p class="desc">clearRect inside a layer can clear a portion of the layer content.</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.clearRect(30, 30, 60, 30);
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside-expected.html
new file mode 100644
index 0000000000..14e6d56245
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clip-inside-and-outside</title>
+<h1>2d.layer.clip-inside-and-outside</h1>
+<p class="desc">Check clipping set inside and outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginPath();
+ ctx2.rect(15, 15, 70, 70);
+ ctx2.clip();
+
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+
+ const canvas3 = new OffscreenCanvas(200, 200);
+ const ctx3 = canvas3.getContext('2d');
+
+ ctx3.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx3.drawImage(canvas2, 0, 0);
+ ctx3.endLayer();
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+ ctx.drawImage(canvas3, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.html
new file mode 100644
index 0000000000..03a3aee605
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clip-inside-and-outside-expected.html">
+<title>Canvas test: 2d.layer.clip-inside-and-outside</title>
+<h1>2d.layer.clip-inside-and-outside</h1>
+<p class="desc">Check clipping set inside and outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.w.html
new file mode 100644
index 0000000000..71f9fe7db4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-and-outside.w.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.clip-inside-and-outside-expected.html">
+<title>Canvas test: 2d.layer.clip-inside-and-outside</title>
+<h1>2d.layer.clip-inside-and-outside</h1>
+<p class="desc">Check clipping set inside and outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-expected.html
new file mode 100644
index 0000000000..281fc1d913
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside-expected.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clip-inside</title>
+<h1>2d.layer.clip-inside</h1>
+<p class="desc">Check clipping set inside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginPath();
+ ctx2.rect(15, 15, 70, 70);
+ ctx2.clip();
+
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx.drawImage(canvas2, 0, 0);
+ ctx.endLayer();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.html
new file mode 100644
index 0000000000..e0c41f21e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clip-inside-expected.html">
+<title>Canvas test: 2d.layer.clip-inside</title>
+<h1>2d.layer.clip-inside</h1>
+<p class="desc">Check clipping set inside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.w.html
new file mode 100644
index 0000000000..9c4260a8ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-inside.w.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.clip-inside-expected.html">
+<title>Canvas test: 2d.layer.clip-inside</title>
+<h1>2d.layer.clip-inside</h1>
+<p class="desc">Check clipping set inside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside-expected.html
new file mode 100644
index 0000000000..1b18c2e8a7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside-expected.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.clip-outside</title>
+<h1>2d.layer.clip-outside</h1>
+<p class="desc">Check clipping set outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+ ctx2.endLayer();
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.html
new file mode 100644
index 0000000000..d2d091e96b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.clip-outside-expected.html">
+<title>Canvas test: 2d.layer.clip-outside</title>
+<h1>2d.layer.clip-outside</h1>
+<p class="desc">Check clipping set outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.w.html
new file mode 100644
index 0000000000..aed72db5ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.clip-outside.w.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.clip-outside-expected.html">
+<title>Canvas test: 2d.layer.clip-outside</title>
+<h1>2d.layer.clip-outside</h1>
+<p class="desc">Check clipping set outside the layer</p>
+<canvas id="canvas" width="100" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths-expected.html
new file mode 100644
index 0000000000..c394ecdfe3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.cross-layer-paths</title>
+<h1>2d.layer.cross-layer-paths</h1>
+<p class="desc">Checks that path defined in a layer is usable outside.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.moveTo(50, 0); ctx.lineTo(50, 100); ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.html
new file mode 100644
index 0000000000..f167ae00bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.cross-layer-paths-expected.html">
+<title>Canvas test: 2d.layer.cross-layer-paths</title>
+<h1>2d.layer.cross-layer-paths</h1>
+<p class="desc">Checks that path defined in a layer is usable outside.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ ctx.translate(50, 0);
+ ctx.moveTo(0, 0);
+ ctx.endLayer();
+ ctx.lineTo(50, 100);
+ ctx.stroke();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.w.html
new file mode 100644
index 0000000000..5b1f7f949c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.cross-layer-paths.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.cross-layer-paths-expected.html">
+<title>Canvas test: 2d.layer.cross-layer-paths</title>
+<h1>2d.layer.cross-layer-paths</h1>
+<p class="desc">Checks that path defined in a layer is usable outside.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ ctx.translate(50, 0);
+ ctx.moveTo(0, 0);
+ ctx.endLayer();
+ ctx.lineTo(50, 100);
+ ctx.stroke();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow-expected.html
new file mode 100644
index 0000000000..85503ae33a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow-expected.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.css-filters.blur-and-shadow</title>
+<h1>2d.layer.css-filters.blur-and-shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="5" />
+ <feDropShadow dx="10" dy="10" stdDeviation="5" flood-color="orange" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.html
new file mode 100644
index 0000000000..a44ce4707f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.css-filters.blur-and-shadow-expected.html">
+<title>Canvas test: 2d.layer.css-filters.blur-and-shadow</title>
+<h1>2d.layer.css-filters.blur-and-shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'blur(5px) drop-shadow(10px 10px 5px orange)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.w.html
new file mode 100644
index 0000000000..9413ca6674
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.css-filters.blur-and-shadow-expected.html">
+<title>Canvas test: 2d.layer.css-filters.blur-and-shadow</title>
+<h1>2d.layer.css-filters.blur-and-shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'blur(5px) drop-shadow(10px 10px 5px orange)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-expected.html
new file mode 100644
index 0000000000..c6131f6c1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.css-filters.blur</title>
+<h1>2d.layer.css-filters.blur</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur stdDeviation="10" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.html
new file mode 100644
index 0000000000..5407ec37ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.css-filters.blur-expected.html">
+<title>Canvas test: 2d.layer.css-filters.blur</title>
+<h1>2d.layer.css-filters.blur</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'blur(10px)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.w.html
new file mode 100644
index 0000000000..35af0e1bfb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.blur.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.css-filters.blur-expected.html">
+<title>Canvas test: 2d.layer.css-filters.blur</title>
+<h1>2d.layer.css-filters.blur</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'blur(10px)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow-expected.html
new file mode 100644
index 0000000000..60f78d8096
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.css-filters.shadow</title>
+<h1>2d.layer.css-filters.shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx="-10" dy="-10" stdDeviation="5" flood-color="purple" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+</svg>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.html
new file mode 100644
index 0000000000..28d6e6e403
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.css-filters.shadow-expected.html">
+<title>Canvas test: 2d.layer.css-filters.shadow</title>
+<h1>2d.layer.css-filters.shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'drop-shadow(-10px -10px 5px purple)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.w.html
new file mode 100644
index 0000000000..98e9d2fbd4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.css-filters.shadow-expected.html">
+<title>Canvas test: 2d.layer.css-filters.shadow</title>
+<h1>2d.layer.css-filters.shadow</h1>
+<p class="desc">Checks that beginLayer works with a CSS filter string as input.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: 'drop-shadow(-10px -10px 5px purple)'});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.html
new file mode 100644
index 0000000000..a047c539cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.exceptions-are-no-op</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.exceptions-are-no-op</h1>
+<p class="desc">Checks that the context state is left unchanged if beginLayer throws.</p>
+
+
+<script>
+var t = async_test("Checks that the context state is left unchanged if beginLayer throws.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Get `beginLayer` to throw while parsing the filter.
+ assert_throws_js(TypeError,
+ () => ctx.beginLayer({filter: {name: 'colorMatrix',
+ values: 'foo'}}));
+ // `beginLayer` shouldn't have opened the layer, so `endLayer` should throw.
+ assert_throws_dom("InvalidStateError", () => ctx.endLayer());
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.worker.js
new file mode 100644
index 0000000000..bd1e376084
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.exceptions-are-no-op.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.exceptions-are-no-op
+// Description:Checks that the context state is left unchanged if beginLayer throws.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Checks that the context state is left unchanged if beginLayer throws.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Get `beginLayer` to throw while parsing the filter.
+ assert_throws_js(TypeError,
+ () => ctx.beginLayer({filter: {name: 'colorMatrix',
+ values: 'foo'}}));
+ // `beginLayer` shouldn't have opened the layer, so `endLayer` should throw.
+ assert_throws_dom("InvalidStateError", () => ctx.endLayer());
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter-expected.html
new file mode 100644
index 0000000000..88057fc354
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter-expected.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-filter</title>
+<h1>2d.layer.global-filter</h1>
+<p class="desc">Tests that layers ignore the global context filter.</p>
+<canvas id="canvas" width="150" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(60, 10, 30, 30);
+ ctx.filter = 'blur(5px)'
+ ctx.fillRect(10, 10, 30, 30);
+ ctx.fillRect(110, 10, 30, 30);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.html
new file mode 100644
index 0000000000..2388a06a96
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-filter-expected.html">
+<title>Canvas test: 2d.layer.global-filter</title>
+<h1>2d.layer.global-filter</h1>
+<p class="desc">Tests that layers ignore the global context filter.</p>
+<canvas id="canvas" width="150" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(150, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.filter = 'blur(5px)'
+
+ ctx.beginLayer();
+ ctx.fillRect(10, 10, 30, 30); // `ctx.filter` applied to draw call.
+ ctx.endLayer();
+
+ ctx.beginLayer();
+ ctx.filter = 'none';
+ ctx.fillRect(60, 10, 30, 30); // Should not be filted by the layer.
+ ctx.endLayer();
+
+ ctx.fillRect(110, 10, 30, 30); // `ctx.filter` is still set.
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.w.html
new file mode 100644
index 0000000000..4c8f92d18a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-filter.w.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-filter-expected.html">
+<title>Canvas test: 2d.layer.global-filter</title>
+<h1>2d.layer.global-filter</h1>
+<p class="desc">Tests that layers ignore the global context filter.</p>
+<canvas id="canvas" width="150" height="100">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(150, 100);
+ const ctx = canvas.getContext('2d');
+
+ ctx.filter = 'blur(5px)'
+
+ ctx.beginLayer();
+ ctx.fillRect(10, 10, 30, 30); // `ctx.filter` applied to draw call.
+ ctx.endLayer();
+
+ ctx.beginLayer();
+ ctx.filter = 'none';
+ ctx.fillRect(60, 10, 30, 30); // Should not be filted by the layer.
+ ctx.endLayer();
+
+ ctx.fillRect(110, 10, 30, 30); // `ctx.filter` is still set.
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha-expected.html
new file mode 100644
index 0000000000..0666e3098a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending-expected.html
new file mode 100644
index 0000000000..8a45027588
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.blending</title>
+<h1>2d.layer.global-states.alpha.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.html
new file mode 100644
index 0000000000..71414b4b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.blending</title>
+<h1>2d.layer.global-states.alpha.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow-expected.html
new file mode 100644
index 0000000000..f7b633b35f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow-expected.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.html
new file mode 100644
index 0000000000..ed2d2d70af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.w.html
new file mode 100644
index 0000000000..1ff3ad8385
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.shadow.w.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.alpha.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.w.html
new file mode 100644
index 0000000000..618480c813
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.blending.w.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.alpha.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.blending</title>
+<h1>2d.layer.global-states.alpha.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite-expected.html
new file mode 100644
index 0000000000..951049e638
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.composite</title>
+<h1>2d.layer.global-states.alpha.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.html
new file mode 100644
index 0000000000..94fed5752d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.composite</title>
+<h1>2d.layer.global-states.alpha.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow-expected.html
new file mode 100644
index 0000000000..0ae93871f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow-expected.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.html
new file mode 100644
index 0000000000..eb579cdcce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.w.html
new file mode 100644
index 0000000000..60e36f4b97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.shadow.w.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.alpha.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.w.html
new file mode 100644
index 0000000000..d7d2b7a21e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.composite.w.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.alpha.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.composite</title>
+<h1>2d.layer.global-states.alpha.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.html
new file mode 100644
index 0000000000..63a264e681
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow-expected.html
new file mode 100644
index 0000000000..6f764c5001
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.html
new file mode 100644
index 0000000000..65a66c977d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.w.html
new file mode 100644
index 0000000000..f404601e3d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.w.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.w.html
new file mode 100644
index 0000000000..694f31e208
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.alpha.w.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending-expected.html
new file mode 100644
index 0000000000..33fdf46a28
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.blending</title>
+<h1>2d.layer.global-states.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.html
new file mode 100644
index 0000000000..6a36bb4ba1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.blending</title>
+<h1>2d.layer.global-states.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow-expected.html
new file mode 100644
index 0000000000..6f969074f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.blending.shadow</title>
+<h1>2d.layer.global-states.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.html
new file mode 100644
index 0000000000..2e91f3d2d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.blending.shadow</title>
+<h1>2d.layer.global-states.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.w.html
new file mode 100644
index 0000000000..d8e20d0479
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.shadow.w.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.blending.shadow</title>
+<h1>2d.layer.global-states.blending.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.w.html
new file mode 100644
index 0000000000..8964e97713
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.blending.w.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.blending</title>
+<h1>2d.layer.global-states.blending</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite-expected.html
new file mode 100644
index 0000000000..ed7669c4cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.composite</title>
+<h1>2d.layer.global-states.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.html
new file mode 100644
index 0000000000..84fb4b3d95
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.composite</title>
+<h1>2d.layer.global-states.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow-expected.html
new file mode 100644
index 0000000000..b687c27f47
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.composite.shadow</title>
+<h1>2d.layer.global-states.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.html
new file mode 100644
index 0000000000..1e3ab4d6a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.composite.shadow</title>
+<h1>2d.layer.global-states.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.w.html
new file mode 100644
index 0000000000..7dfb70148b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.shadow.w.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.composite.shadow</title>
+<h1>2d.layer.global-states.composite.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.w.html
new file mode 100644
index 0000000000..b695871fcd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.composite.w.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.composite</title>
+<h1>2d.layer.global-states.composite</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha-expected.html
new file mode 100644
index 0000000000..f304700feb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha</title>
+<h1>2d.layer.global-states.filter.alpha</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending-expected.html
new file mode 100644
index 0000000000..7c91ce4229
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending-expected.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending</title>
+<h1>2d.layer.global-states.filter.alpha.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.html
new file mode 100644
index 0000000000..be6f962b33
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending</title>
+<h1>2d.layer.global-states.filter.alpha.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html
new file mode 100644
index 0000000000..62942ffeae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow-expected.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.html
new file mode 100644
index 0000000000..62d98d967c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.w.html
new file mode 100644
index 0000000000..e81efd6b8d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.shadow.w.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.alpha.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.w.html
new file mode 100644
index 0000000000..2b53ad7c1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.blending.w.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.alpha.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.blending</title>
+<h1>2d.layer.global-states.filter.alpha.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite-expected.html
new file mode 100644
index 0000000000..8e0d98648e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite-expected.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite</title>
+<h1>2d.layer.global-states.filter.alpha.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.html
new file mode 100644
index 0000000000..087fea88af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite</title>
+<h1>2d.layer.global-states.filter.alpha.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html
new file mode 100644
index 0000000000..a649972546
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow-expected.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.html
new file mode 100644
index 0000000000..d067ff2f5e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.w.html
new file mode 100644
index 0000000000..39abc78b17
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.shadow.w.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.alpha.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.w.html
new file mode 100644
index 0000000000..6aa83a3278
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.composite.w.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.alpha.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.composite</title>
+<h1>2d.layer.global-states.filter.alpha.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.html
new file mode 100644
index 0000000000..f1d631c25c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha</title>
+<h1>2d.layer.global-states.filter.alpha</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow-expected.html
new file mode 100644
index 0000000000..169baee29b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow-expected.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.alpha.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.html
new file mode 100644
index 0000000000..2548c21181
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.w.html
new file mode 100644
index 0000000000..5e1494422e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.shadow.w.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha.shadow</title>
+<h1>2d.layer.global-states.filter.alpha.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.w.html
new file mode 100644
index 0000000000..e75d668ad5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.alpha.w.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.alpha</title>
+<h1>2d.layer.global-states.filter.alpha</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalAlpha = 0.6;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending-expected.html
new file mode 100644
index 0000000000..f81dcf72dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.blending</title>
+<h1>2d.layer.global-states.filter.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.html
new file mode 100644
index 0000000000..ce2b046798
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.blending</title>
+<h1>2d.layer.global-states.filter.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow-expected.html
new file mode 100644
index 0000000000..91f3725f8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow-expected.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.blending.shadow</title>
+<h1>2d.layer.global-states.filter.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.html
new file mode 100644
index 0000000000..d0d429bee3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.blending.shadow</title>
+<h1>2d.layer.global-states.filter.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.w.html
new file mode 100644
index 0000000000..ce432ea74d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.shadow.w.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.blending.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.blending.shadow</title>
+<h1>2d.layer.global-states.filter.blending.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.w.html
new file mode 100644
index 0000000000..bb101cdc0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.blending.w.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.blending-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.blending</title>
+<h1>2d.layer.global-states.filter.blending</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'multiply';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite-expected.html
new file mode 100644
index 0000000000..97e85a1593
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.composite</title>
+<h1>2d.layer.global-states.filter.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.html
new file mode 100644
index 0000000000..32052a1150
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.composite</title>
+<h1>2d.layer.global-states.filter.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow-expected.html
new file mode 100644
index 0000000000..4716bb2760
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow-expected.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.composite.shadow</title>
+<h1>2d.layer.global-states.filter.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.html
new file mode 100644
index 0000000000..b5e8b9f843
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.composite.shadow</title>
+<h1>2d.layer.global-states.filter.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.w.html
new file mode 100644
index 0000000000..894089d88e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.shadow.w.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.composite.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.composite.shadow</title>
+<h1>2d.layer.global-states.filter.composite.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.w.html
new file mode 100644
index 0000000000..41ccdaf5c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.composite.w.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.composite-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.composite</title>
+<h1>2d.layer.global-states.filter.composite</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states-expected.html
new file mode 100644
index 0000000000..e56fe0b360
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states-expected.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.no-global-states</title>
+<h1>2d.layer.global-states.filter.no-global-states</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.html
new file mode 100644
index 0000000000..3effa3ee9d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.no-global-states-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.no-global-states</title>
+<h1>2d.layer.global-states.filter.no-global-states</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.w.html
new file mode 100644
index 0000000000..ec744d7ffe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.no-global-states.w.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.no-global-states-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.no-global-states</title>
+<h1>2d.layer.global-states.filter.no-global-states</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow-expected.html
new file mode 100644
index 0000000000..13ba2dd4cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow-expected.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="200" height="200"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = 200;
+ img.height = 200;
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.html
new file mode 100644
index 0000000000..7bb0ef5e13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.w.html
new file mode 100644
index 0000000000..bc9bd48aad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.w.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.filter.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers with filters correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states-expected.html
new file mode 100644
index 0000000000..b91a2ae8b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.no-global-states</title>
+<h1>2d.layer.global-states.no-global-states</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.html
new file mode 100644
index 0000000000..c8a9815381
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.no-global-states-expected.html">
+<title>Canvas test: 2d.layer.global-states.no-global-states</title>
+<h1>2d.layer.global-states.no-global-states</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.w.html
new file mode 100644
index 0000000000..db03a3fd0c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.no-global-states.w.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.no-global-states-expected.html">
+<title>Canvas test: 2d.layer.global-states.no-global-states</title>
+<h1>2d.layer.global-states.no-global-states</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ // No global states.
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow-expected.html
new file mode 100644
index 0000000000..835e9d420a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.html
new file mode 100644
index 0000000000..ad60e87fb1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.w.html
new file mode 100644
index 0000000000..1fc35fd33a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.global-states.shadow.w.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.global-states.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html
new file mode 100644
index 0000000000..c0b11aa611
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.invalid-calls.beginLayer-reset-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.invalid-calls.beginLayer-reset-endLayer</h1>
+<p class="desc">Raises exception on beginLayer() + reset() + endLayer().</p>
+
+
+<script>
+var t = async_test("Raises exception on beginLayer() + reset() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.reset();
+ ctx.endLayer();
+ });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.worker.js
new file mode 100644
index 0000000000..1c147d6f34
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-reset-endLayer.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.invalid-calls.beginLayer-reset-endLayer
+// Description:Raises exception on beginLayer() + reset() + endLayer().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Raises exception on beginLayer() + reset() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.reset();
+ ctx.endLayer();
+ });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.html
new file mode 100644
index 0000000000..022532b329
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.invalid-calls.beginLayer-restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.invalid-calls.beginLayer-restore</h1>
+<p class="desc">Raises exception on beginLayer() + restore().</p>
+
+
+<script>
+var t = async_test("Raises exception on beginLayer() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.restore();
+ });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.worker.js
new file mode 100644
index 0000000000..1aa86635e6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-restore.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.invalid-calls.beginLayer-restore
+// Description:Raises exception on beginLayer() + restore().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Raises exception on beginLayer() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.restore();
+ });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html
new file mode 100644
index 0000000000..26dd0eee4b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.invalid-calls.beginLayer-save-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.invalid-calls.beginLayer-save-endLayer</h1>
+<p class="desc">Raises exception on beginLayer() + save() + endLayer().</p>
+
+
+<script>
+var t = async_test("Raises exception on beginLayer() + save() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.save();
+ ctx.endLayer();
+ });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.worker.js
new file mode 100644
index 0000000000..613921c67c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.beginLayer-save-endLayer.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.invalid-calls.beginLayer-save-endLayer
+// Description:Raises exception on beginLayer() + save() + endLayer().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Raises exception on beginLayer() + save() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.beginLayer();
+ ctx.save();
+ ctx.endLayer();
+ });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.html
new file mode 100644
index 0000000000..440249980a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.invalid-calls.endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.invalid-calls.endLayer</h1>
+<p class="desc">Raises exception on lone endLayer calls.</p>
+
+
+<script>
+var t = async_test("Raises exception on lone endLayer calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.endLayer();
+ });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.worker.js
new file mode 100644
index 0000000000..b2ba231b9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.endLayer.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.invalid-calls.endLayer
+// Description:Raises exception on lone endLayer calls.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Raises exception on lone endLayer calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.endLayer();
+ });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.html
new file mode 100644
index 0000000000..c2b09961ac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.invalid-calls.save-beginLayer-restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.invalid-calls.save-beginLayer-restore</h1>
+<p class="desc">Raises exception on save() + beginLayer() + restore().</p>
+
+
+<script>
+var t = async_test("Raises exception on save() + beginLayer() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.save();
+ ctx.beginLayer();
+ ctx.restore();
+ });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.worker.js
new file mode 100644
index 0000000000..d155379fcb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-beginLayer-restore.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.invalid-calls.save-beginLayer-restore
+// Description:Raises exception on save() + beginLayer() + restore().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Raises exception on save() + beginLayer() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.save();
+ ctx.beginLayer();
+ ctx.restore();
+ });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.html
new file mode 100644
index 0000000000..01b62d1e85
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.invalid-calls.save-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.invalid-calls.save-endLayer</h1>
+<p class="desc">Raises exception on save() + endLayer().</p>
+
+
+<script>
+var t = async_test("Raises exception on save() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.save();
+ ctx.endLayer();
+ });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.worker.js
new file mode 100644
index 0000000000..353c1b00cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.invalid-calls.save-endLayer.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.invalid-calls.save-endLayer
+// Description:Raises exception on save() + endLayer().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Raises exception on save() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ ctx.save();
+ ctx.endLayer();
+ });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html
new file mode 100644
index 0000000000..867553b45e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.layer-rendering-state-reset-in-layer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.layer-rendering-state-reset-in-layer</h1>
+<p class="desc">Tests that layers ignore the global context filter.</p>
+
+
+<script>
+var t = async_test("Tests that layers ignore the global context filter.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0.5;
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#0000ff';
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 20;
+ ctx.shadowBlur = 30;
+
+ _assertSame(ctx.globalAlpha, 0.5, "ctx.globalAlpha", "0.5");
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ _assertSame(ctx.shadowColor, '#0000ff', "ctx.shadowColor", "'#0000ff'");
+ _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
+ _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
+ _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
+
+ ctx.beginLayer();
+
+ _assertSame(ctx.globalAlpha, 1.0, "ctx.globalAlpha", "1.0");
+ _assertSame(ctx.globalCompositeOperation, 'source-over', "ctx.globalCompositeOperation", "'source-over'");
+ _assertSame(ctx.shadowColor, 'rgba(0, 0, 0, 0)', "ctx.shadowColor", "'rgba(0, 0, 0, 0)'");
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+
+ ctx.endLayer();
+
+ _assertSame(ctx.globalAlpha, 0.5, "ctx.globalAlpha", "0.5");
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ _assertSame(ctx.shadowColor, '#0000ff', "ctx.shadowColor", "'#0000ff'");
+ _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
+ _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
+ _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js
new file mode 100644
index 0000000000..5c2bb73e8f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js
@@ -0,0 +1,52 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.layer-rendering-state-reset-in-layer
+// Description:Tests that layers ignore the global context filter.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Tests that layers ignore the global context filter.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalAlpha = 0.5;
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#0000ff';
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 20;
+ ctx.shadowBlur = 30;
+
+ _assertSame(ctx.globalAlpha, 0.5, "ctx.globalAlpha", "0.5");
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ _assertSame(ctx.shadowColor, '#0000ff', "ctx.shadowColor", "'#0000ff'");
+ _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
+ _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
+ _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
+
+ ctx.beginLayer();
+
+ _assertSame(ctx.globalAlpha, 1.0, "ctx.globalAlpha", "1.0");
+ _assertSame(ctx.globalCompositeOperation, 'source-over', "ctx.globalCompositeOperation", "'source-over'");
+ _assertSame(ctx.shadowColor, 'rgba(0, 0, 0, 0)', "ctx.shadowColor", "'rgba(0, 0, 0, 0)'");
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+
+ ctx.endLayer();
+
+ _assertSame(ctx.globalAlpha, 0.5, "ctx.globalAlpha", "0.5");
+ _assertSame(ctx.globalCompositeOperation, 'xor', "ctx.globalCompositeOperation", "'xor'");
+ _assertSame(ctx.shadowColor, '#0000ff', "ctx.shadowColor", "'#0000ff'");
+ _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
+ _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
+ _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.html
new file mode 100644
index 0000000000..0b3854c31d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.malformed-operations-with-promises.convertToBlob</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.malformed-operations-with-promises.convertToBlob</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ await canvas.convertToBlob();
+ // Make sure the exception isn't caused by calling the function twice.
+ await canvas.convertToBlob();
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ await promise_rejects_dom(t, 'InvalidStateError', canvas.convertToBlob());
+
+}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js
new file mode 100644
index 0000000000..be0f4f2cde
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.malformed-operations-with-promises.convertToBlob
+// Description:Check that exceptions are thrown for operations that are malformed while layers are open.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ await canvas.convertToBlob();
+ // Make sure the exception isn't caused by calling the function twice.
+ await canvas.convertToBlob();
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ await promise_rejects_dom(t, 'InvalidStateError', canvas.convertToBlob());
+ t.done();
+}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html
new file mode 100644
index 0000000000..085554d9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.malformed-operations-with-promises.createImageBitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.malformed-operations-with-promises.createImageBitmap</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ await createImageBitmap(canvas);
+ // Make sure the exception isn't caused by calling the function twice.
+ await createImageBitmap(canvas);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(canvas));
+
+}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js
new file mode 100644
index 0000000000..b286b7a9e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.malformed-operations-with-promises.createImageBitmap
+// Description:Check that exceptions are thrown for operations that are malformed while layers are open.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ await createImageBitmap(canvas);
+ // Make sure the exception isn't caused by calling the function twice.
+ await createImageBitmap(canvas);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(canvas));
+ t.done();
+}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.html
new file mode 100644
index 0000000000..a206e64ceb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.malformed-operations.createPattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.malformed-operations.createPattern</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ ctx.createPattern(canvas, 'repeat');
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.createPattern(canvas, 'repeat');
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.createPattern(canvas, 'repeat'));
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.worker.js
new file mode 100644
index 0000000000..bcb42cba87
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.createPattern.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.malformed-operations.createPattern
+// Description:Check that exceptions are thrown for operations that are malformed while layers are open.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ ctx.createPattern(canvas, 'repeat');
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.createPattern(canvas, 'repeat');
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.createPattern(canvas, 'repeat'));
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.html
new file mode 100644
index 0000000000..e6a9872100
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.malformed-operations.drawImage</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.malformed-operations.drawImage</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ // Shouldn't throw on its own.
+ ctx2.drawImage(canvas, 0, 0);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx2.drawImage(canvas, 0, 0);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx2.drawImage(canvas, 0, 0));
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.worker.js
new file mode 100644
index 0000000000..b66cdee62e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.drawImage.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.malformed-operations.drawImage
+// Description:Check that exceptions are thrown for operations that are malformed while layers are open.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ // Shouldn't throw on its own.
+ ctx2.drawImage(canvas, 0, 0);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx2.drawImage(canvas, 0, 0);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx2.drawImage(canvas, 0, 0));
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.html
new file mode 100644
index 0000000000..87bc8c6ede
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.malformed-operations.getImageData</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.malformed-operations.getImageData</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ ctx.getImageData(0, 0, 200, 200);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.getImageData(0, 0, 200, 200);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.getImageData(0, 0, 200, 200));
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.worker.js
new file mode 100644
index 0000000000..6a1a16fccb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.getImageData.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.malformed-operations.getImageData
+// Description:Check that exceptions are thrown for operations that are malformed while layers are open.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ ctx.getImageData(0, 0, 200, 200);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.getImageData(0, 0, 200, 200);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.getImageData(0, 0, 200, 200));
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.html
new file mode 100644
index 0000000000..e8059076bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.malformed-operations.putImageData</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.malformed-operations.putImageData</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d')
+ const data = ctx2.getImageData(0, 0, 1, 1);
+ // Shouldn't throw on its own.
+ ctx.putImageData(data, 0, 0);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.putImageData(data, 0, 0);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.putImageData(data, 0, 0));
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.worker.js
new file mode 100644
index 0000000000..8810c3a73c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.putImageData.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.malformed-operations.putImageData
+// Description:Check that exceptions are thrown for operations that are malformed while layers are open.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d')
+ const data = ctx2.getImageData(0, 0, 1, 1);
+ // Shouldn't throw on its own.
+ ctx.putImageData(data, 0, 0);
+ // Make sure the exception isn't caused by calling the function twice.
+ ctx.putImageData(data, 0, 0);
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => ctx.putImageData(data, 0, 0));
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.html
new file mode 100644
index 0000000000..79c216421f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.malformed-operations.transferToImageBitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.malformed-operations.transferToImageBitmap</h1>
+<p class="desc">Check that exceptions are thrown for operations that are malformed while layers are open.</p>
+
+
+<script>
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ canvas.transferToImageBitmap();
+ // Make sure the exception isn't caused by calling the function twice.
+ canvas.transferToImageBitmap();
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => canvas.transferToImageBitmap());
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.worker.js
new file mode 100644
index 0000000000..be0b43665a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations.transferToImageBitmap.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.malformed-operations.transferToImageBitmap
+// Description:Check that exceptions are thrown for operations that are malformed while layers are open.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that exceptions are thrown for operations that are malformed while layers are open.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(200, 200);
+ var ctx = canvas.getContext('2d');
+
+ // Shouldn't throw on its own.
+ canvas.transferToImageBitmap();
+ // Make sure the exception isn't caused by calling the function twice.
+ canvas.transferToImageBitmap();
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => canvas.transferToImageBitmap());
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-expected.html
new file mode 100644
index 0000000000..65525d4d6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-expected.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.nested</title>
+<h1>2d.layer.nested</h1>
+<p class="desc">Tests nested canvas layers.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 40, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx2.fillRect(60, 60, 75, 50);
+
+ ctx2.globalAlpha = 0.5;
+
+ canvas3 = document.createElement("canvas");
+ ctx3 = canvas3.getContext("2d");
+
+ ctx3.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx3.fillRect(50, 50, 75, 50);
+ ctx3.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx3.fillRect(70, 70, 75, 50);
+
+ ctx2.drawImage(canvas3, 0, 0);
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters-expected.html
new file mode 100644
index 0000000000..8b53e2dc76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters-expected.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.nested-filters</title>
+<h1>2d.layer.nested-filters</h1>
+<p class="desc">Checks that nested layers work properly when both apply filters.</p>
+<canvas id="canvas" width="400" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(20, 20, 100, 100);
+ ctx.fillRect(30, 30, 100, 100);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(40, 40, 100, 100);
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(280, 80, 100, 100);
+ ctx.fillRect(270, 70, 100, 100);
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(260, 60, 100, 100);
+ ctx.fillStyle = 'red';
+ ctx.fillRect(250, 50, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.html
new file mode 100644
index 0000000000..adf99bacbe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.nested-filters-expected.html">
+<title>Canvas test: 2d.layer.nested-filters</title>
+<h1>2d.layer.nested-filters</h1>
+<p class="desc">Checks that nested layers work properly when both apply filters.</p>
+<canvas id="canvas" width="400" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(400, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -20, dy: -20,
+ stdDeviation: 0, floodColor: 'yellow'}});
+ ctx.beginLayer({filter: 'drop-shadow(-10px -10px 0 blue)'});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'drop-shadow(20px 20px 0 blue)'});
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: 10, dy: 10,
+ stdDeviation: 0, floodColor: 'yellow'}});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(250, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.w.html
new file mode 100644
index 0000000000..0616fecfd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested-filters.w.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.nested-filters-expected.html">
+<title>Canvas test: 2d.layer.nested-filters</title>
+<h1>2d.layer.nested-filters</h1>
+<p class="desc">Checks that nested layers work properly when both apply filters.</p>
+<canvas id="canvas" width="400" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(400, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -20, dy: -20,
+ stdDeviation: 0, floodColor: 'yellow'}});
+ ctx.beginLayer({filter: 'drop-shadow(-10px -10px 0 blue)'});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'drop-shadow(20px 20px 0 blue)'});
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: 10, dy: 10,
+ stdDeviation: 0, floodColor: 'yellow'}});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(250, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.html
new file mode 100644
index 0000000000..9e5b7d5826
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.nested-expected.html">
+<title>Canvas test: 2d.layer.nested</title>
+<h1>2d.layer.nested</h1>
+<p class="desc">Tests nested canvas layers.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 40, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.w.html
new file mode 100644
index 0000000000..99e7390609
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.nested.w.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.nested-expected.html">
+<title>Canvas test: 2d.layer.nested</title>
+<h1>2d.layer.nested</h1>
+<p class="desc">Tests nested canvas layers.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 40, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas-expected.html
new file mode 100644
index 0000000000..89c85de1e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.opaque-canvas</title>
+<h1>2d.layer.opaque-canvas</h1>
+<p class="desc">Checks that layer blending works inside opaque canvas</p>
+<canvas id="canvas" width="300" height="300">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 300, 300);
+
+ ctx.fillStyle = 'black';
+ ctx.fillRect(0, 0, 200, 200);
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(10, 10, 100, 100);
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'green';
+ ctx2.fillRect(50, 50, 100, 100);
+ ctx2.globalAlpha = 0.8;
+ ctx2.fillStyle = 'yellow';
+ ctx2.fillRect(75, 25, 100, 100);
+
+ ctx.shadowColor = 'rgba(200, 100, 50, 0.5)';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.html
new file mode 100644
index 0000000000..f9759abebe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.opaque-canvas-expected.html">
+<title>Canvas test: 2d.layer.opaque-canvas</title>
+<h1>2d.layer.opaque-canvas</h1>
+<p class="desc">Checks that layer blending works inside opaque canvas</p>
+<canvas id="canvas" width="300" height="300">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(300, 300);
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d', {alpha: false});
+
+ ctx2.fillStyle = 'purple';
+ ctx2.fillRect(10, 10, 100, 100);
+
+ ctx2.beginLayer({filter: {name: 'dropShadow', dx: -10, dy: 10,
+ stdDeviation: 0,
+ floodColor: 'rgba(200, 100, 50, 0.5)'}});
+ ctx2.fillStyle = 'green';
+ ctx2.fillRect(50, 50, 100, 100);
+ ctx2.globalAlpha = 0.8;
+ ctx2.fillStyle = 'yellow';
+ ctx2.fillRect(75, 25, 100, 100);
+ ctx2.endLayer();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 300, 300);
+ ctx.drawImage(canvas2, 0, 0);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.w.html
new file mode 100644
index 0000000000..ff5ec19418
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.opaque-canvas.w.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.opaque-canvas-expected.html">
+<title>Canvas test: 2d.layer.opaque-canvas</title>
+<h1>2d.layer.opaque-canvas</h1>
+<p class="desc">Checks that layer blending works inside opaque canvas</p>
+<canvas id="canvas" width="300" height="300">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(300, 300);
+ const ctx = canvas.getContext('2d');
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d', {alpha: false});
+
+ ctx2.fillStyle = 'purple';
+ ctx2.fillRect(10, 10, 100, 100);
+
+ ctx2.beginLayer({filter: {name: 'dropShadow', dx: -10, dy: 10,
+ stdDeviation: 0,
+ floodColor: 'rgba(200, 100, 50, 0.5)'}});
+ ctx2.fillStyle = 'green';
+ ctx2.fillRect(50, 50, 100, 100);
+ ctx2.globalAlpha = 0.8;
+ ctx2.fillStyle = 'yellow';
+ ctx2.fillRect(75, 25, 100, 100);
+ ctx2.endLayer();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 300, 300);
+ ctx.drawImage(canvas2, 0, 0);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset-expected.html
new file mode 100644
index 0000000000..93131dca9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.reset</title>
+<h1>2d.layer.reset</h1>
+<p class="desc">Checks that reset discards any pending layers.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(10, 10, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.html
new file mode 100644
index 0000000000..3254c0a048
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.reset-expected.html">
+<title>Canvas test: 2d.layer.reset</title>
+<h1>2d.layer.reset</h1>
+<p class="desc">Checks that reset discards any pending layers.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ // Global states:
+ ctx.globalAlpha = 0.3;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -3;
+ ctx.shadowOffsetY = 3;
+ ctx.shadowColor = 'rgba(0, 30, 0, 0.3)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -3, dy: 3}});
+
+ // Layer states:
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -6;
+ ctx.shadowOffsetY = 6;
+ ctx.shadowColor = 'rgba(0, 60, 0, 0.6)';
+ ctx.shadowBlur = 3;
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.w.html
new file mode 100644
index 0000000000..09bd6451bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.reset.w.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.reset-expected.html">
+<title>Canvas test: 2d.layer.reset</title>
+<h1>2d.layer.reset</h1>
+<p class="desc">Checks that reset discards any pending layers.</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ // Global states:
+ ctx.globalAlpha = 0.3;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -3;
+ ctx.shadowOffsetY = 3;
+ ctx.shadowColor = 'rgba(0, 30, 0, 0.3)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -3, dy: 3}});
+
+ // Layer states:
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -6;
+ ctx.shadowOffsetY = 6;
+ ctx.shadowColor = 'rgba(0, 60, 0, 0.6)';
+ ctx.shadowBlur = 3;
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style-expected.html
new file mode 100644
index 0000000000..1d0ac3558d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style-expected.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.restore-style</title>
+<h1>2d.layer.restore-style</h1>
+<p class="desc">Test that ensure layers restores style values upon endLayer.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(60, 60, 75, 50);
+ ctx.drawImage(canvas2, 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.html
new file mode 100644
index 0000000000..a3593cd2a8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.restore-style-expected.html">
+<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-950">
+<title>Canvas test: 2d.layer.restore-style</title>
+<h1>2d.layer.restore-style</h1>
+<p class="desc">Test that ensure layers restores style values upon endLayer.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,255,1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(70, 70, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.w.html
new file mode 100644
index 0000000000..a66fc2bc59
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.restore-style.w.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.restore-style-expected.html">
+<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-950">
+<title>Canvas test: 2d.layer.restore-style</title>
+<h1>2d.layer.restore-style</h1>
+<p class="desc">Test that ensure layers restores style values upon endLayer.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,255,1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(70, 70, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex-expected.html
new file mode 100644
index 0000000000..ef46f69e2a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.several-complex</title>
+<h1>2d.layer.several-complex</h1>
+<p class="desc">Test to ensure beginlayer works for filter, alpha and shadow, even with consecutive layers.</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 95, 70);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'orange';
+ ctx.shadowBlur = 3;
+
+ var canvas2 = [5];
+ var ctx2 = [5];
+
+ for (let i = 0; i < 5; i++) {
+ canvas2[i] = document.createElement("canvas");
+ ctx2[i] = canvas2[i].getContext("2d");
+ ctx2[i].fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2[i].fillRect(60, 40, 75, 50);
+ ctx2[i].fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2[i].fillRect(80, 60, 75, 50);
+
+ ctx.drawImage(canvas2[i], i, i);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.html
new file mode 100644
index 0000000000..e30f1d2fa0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.several-complex-expected.html">
+<meta name=fuzzy content="maxDifference=0-3; totalPixels=0-6318">
+<title>Canvas test: 2d.layer.several-complex</title>
+<h1>2d.layer.several-complex</h1>
+<p class="desc">Test to ensure beginlayer works for filter, alpha and shadow, even with consecutive layers.</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(500, 500);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 95, 70);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'orange';
+ ctx.shadowBlur = 3
+
+ for (let i = 0; i < 5; i++) {
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60 + i, 40 + i, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(80 + i, 60 + i, 75, 50);
+
+ ctx.endLayer();
+ }
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.w.html
new file mode 100644
index 0000000000..e00de63507
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.several-complex.w.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.several-complex-expected.html">
+<meta name=fuzzy content="maxDifference=0-3; totalPixels=0-6318">
+<title>Canvas test: 2d.layer.several-complex</title>
+<h1>2d.layer.several-complex</h1>
+<p class="desc">Test to ensure beginlayer works for filter, alpha and shadow, even with consecutive layers.</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(500, 500);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 95, 70);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'orange';
+ ctx.shadowBlur = 3
+
+ for (let i = 0; i < 5; i++) {
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60 + i, 40 + i, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(80 + i, 60 + i, 75, 50);
+
+ ctx.endLayer();
+ }
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html
new file mode 100644
index 0000000000..cc10684a9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ // No clipping.
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html
new file mode 100644
index 0000000000..377dd55ef6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html
new file mode 100644
index 0000000000..98262f53b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.w.html
new file mode 100644
index 0000000000..90b4332f94
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance-with-clipping.w.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.long-distance-with-clipping-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.html
new file mode 100644
index 0000000000..eea9b5dc25
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.long-distance-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.w.html
new file mode 100644
index 0000000000..e4422558cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.long-distance.w.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.long-distance-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.long-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.long-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 10000;
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html
new file mode 100644
index 0000000000..8980708dd1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ // No clipping.
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html
new file mode 100644
index 0000000000..024fc8c9a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html
new file mode 100644
index 0000000000..d5f5b0c8ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.w.html
new file mode 100644
index 0000000000..acbf09edec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance-with-clipping.w.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.short-distance-with-clipping-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance-with-clipping</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.html
new file mode 100644
index 0000000000..a1c4689f7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.short-distance-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.w.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.w.html
new file mode 100644
index 0000000000..2e39091f3d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.shadow-from-outside-canvas.short-distance.w.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.shadow-from-outside-canvas.short-distance-expected.html">
+<title>Canvas test: 2d.layer.shadow-from-outside-canvas.short-distance</title>
+<h1>2d.layer.shadow-from-outside-canvas.short-distance</h1>
+<p class="desc">Checks shadow produced by object drawn outside the canvas</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ const delta = 1;
+
+ // No clipping.
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -(200 + delta),
+ dy: -(200 + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(200 + delta, 200 + delta, 100, 100);
+
+ ctx.endLayer();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.html
new file mode 100644
index 0000000000..0a6e195ca8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.beginLayer-endLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.beginLayer-endLayer</h1>
+<p class="desc">No exception raised on beginLayer() + endLayer().</p>
+
+
+<script>
+var t = async_test("No exception raised on beginLayer() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ ctx.save();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.worker.js
new file mode 100644
index 0000000000..a10bcd98b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-endLayer.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.beginLayer-endLayer
+// Description:No exception raised on beginLayer() + endLayer().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on beginLayer() + endLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ ctx.save();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.html
new file mode 100644
index 0000000000..12abcc5270
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.beginLayer-save</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.beginLayer-save</h1>
+<p class="desc">No exception raised on beginLayer() + save().</p>
+
+
+<script>
+var t = async_test("No exception raised on beginLayer() + save().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ ctx.save();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.worker.js
new file mode 100644
index 0000000000..03fbeeb4f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer-save.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.beginLayer-save
+// Description:No exception raised on beginLayer() + save().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on beginLayer() + save().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ ctx.save();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.html
new file mode 100644
index 0000000000..2178138e8d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.beginLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.beginLayer</h1>
+<p class="desc">No exception raised on lone beginLayer() calls.</p>
+
+
+<script>
+var t = async_test("No exception raised on lone beginLayer() calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.worker.js
new file mode 100644
index 0000000000..e64be136d7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.beginLayer.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.beginLayer
+// Description:No exception raised on lone beginLayer() calls.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on lone beginLayer() calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.beginLayer();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.html
new file mode 100644
index 0000000000..156dd5abbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.restore</h1>
+<p class="desc">No exception raised on lone restore() calls.</p>
+
+
+<script>
+var t = async_test("No exception raised on lone restore() calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.worker.js
new file mode 100644
index 0000000000..7417d12abe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.restore.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.restore
+// Description:No exception raised on lone restore() calls.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on lone restore() calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.html
new file mode 100644
index 0000000000..afe735764e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.save-beginLayer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.save-beginLayer</h1>
+<p class="desc">No exception raised on save() + beginLayer().</p>
+
+
+<script>
+var t = async_test("No exception raised on save() + beginLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ ctx.beginLayer();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.worker.js
new file mode 100644
index 0000000000..773ba77a15
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save-beginLayer.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.save-beginLayer
+// Description:No exception raised on save() + beginLayer().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on save() + beginLayer().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ ctx.beginLayer();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.html
new file mode 100644
index 0000000000..2b2de23bb8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.save</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.save</h1>
+<p class="desc">No exception raised on lone save() calls.</p>
+
+
+<script>
+var t = async_test("No exception raised on lone save() calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.worker.js
new file mode 100644
index 0000000000..5bbf0386fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.save
+// Description:No exception raised on lone save() calls.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on lone save() calls.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.html
new file mode 100644
index 0000000000..ec5aa6455e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.save_reset_restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.save_reset_restore</h1>
+<p class="desc">No exception raised on save() + reset() + restore().</p>
+
+
+<script>
+var t = async_test("No exception raised on save() + reset() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ ctx.reset();
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.worker.js
new file mode 100644
index 0000000000..3636c94357
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_reset_restore.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.save_reset_restore
+// Description:No exception raised on save() + reset() + restore().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on save() + reset() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ ctx.reset();
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.html
new file mode 100644
index 0000000000..1682bca91c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.layer.valid-calls.save_restore</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.layer.valid-calls.save_restore</h1>
+<p class="desc">No exception raised on save() + restore().</p>
+
+
+<script>
+var t = async_test("No exception raised on save() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.worker.js
new file mode 100644
index 0000000000..8607722df7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.valid-calls.save_restore.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.layer.valid-calls.save_restore
+// Description:No exception raised on save() + restore().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("No exception raised on save() + restore().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.save();
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.html
new file mode 100644
index 0000000000..145e145cb4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cap.butt</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cap.butt</h1>
+<p class="desc">lineCap 'butt' is rendered correctly</p>
+
+
+<script>
+var t = async_test("lineCap 'butt' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'butt';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 25,14, 0,255,0,255);
+ _assertPixel(canvas, 25,15, 0,255,0,255);
+ _assertPixel(canvas, 25,16, 0,255,0,255);
+ _assertPixel(canvas, 25,34, 0,255,0,255);
+ _assertPixel(canvas, 25,35, 0,255,0,255);
+ _assertPixel(canvas, 25,36, 0,255,0,255);
+
+ _assertPixel(canvas, 75,14, 0,255,0,255);
+ _assertPixel(canvas, 75,15, 0,255,0,255);
+ _assertPixel(canvas, 75,16, 0,255,0,255);
+ _assertPixel(canvas, 75,34, 0,255,0,255);
+ _assertPixel(canvas, 75,35, 0,255,0,255);
+ _assertPixel(canvas, 75,36, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.worker.js
new file mode 100644
index 0000000000..127393790c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.butt.worker.js
@@ -0,0 +1,56 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cap.butt
+// Description:lineCap 'butt' is rendered correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineCap 'butt' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'butt';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 25,14, 0,255,0,255);
+ _assertPixel(canvas, 25,15, 0,255,0,255);
+ _assertPixel(canvas, 25,16, 0,255,0,255);
+ _assertPixel(canvas, 25,34, 0,255,0,255);
+ _assertPixel(canvas, 25,35, 0,255,0,255);
+ _assertPixel(canvas, 25,36, 0,255,0,255);
+
+ _assertPixel(canvas, 75,14, 0,255,0,255);
+ _assertPixel(canvas, 75,15, 0,255,0,255);
+ _assertPixel(canvas, 75,16, 0,255,0,255);
+ _assertPixel(canvas, 75,34, 0,255,0,255);
+ _assertPixel(canvas, 75,35, 0,255,0,255);
+ _assertPixel(canvas, 75,36, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.html
new file mode 100644
index 0000000000..edddeb08e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cap.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cap.closed</h1>
+<p class="desc">Line caps are not drawn at the corners of an unclosed rectangle</p>
+
+
+<script>
+var t = async_test("Line caps are not drawn at the corners of an unclosed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.worker.js
new file mode 100644
index 0000000000..b8357316e4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.closed.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cap.closed
+// Description:Line caps are not drawn at the corners of an unclosed rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Line caps are not drawn at the corners of an unclosed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.html
new file mode 100644
index 0000000000..2054bbc515
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cap.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cap.invalid</h1>
+<p class="desc">Setting lineCap to invalid values is ignored</p>
+
+
+<script>
+var t = async_test("Setting lineCap to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineCap = 'butt'
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'invalid';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'ROUND';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round\0';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round ';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = "";
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'bevel';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.worker.js
new file mode 100644
index 0000000000..813ceb7f66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.invalid.worker.js
@@ -0,0 +1,47 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cap.invalid
+// Description:Setting lineCap to invalid values is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting lineCap to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineCap = 'butt'
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'invalid';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'ROUND';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round\0';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round ';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = "";
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'bevel';
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.html
new file mode 100644
index 0000000000..f26fbc686c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cap.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cap.open</h1>
+<p class="desc">Line caps are drawn at the corners of an unclosed rectangle</p>
+
+
+<script>
+var t = async_test("Line caps are drawn at the corners of an unclosed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.lineTo(200, 200);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.worker.js
new file mode 100644
index 0000000000..169010ac2a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.open.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cap.open
+// Description:Line caps are drawn at the corners of an unclosed rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Line caps are drawn at the corners of an unclosed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.lineTo(200, 200);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.html
new file mode 100644
index 0000000000..31f5ba5423
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cap.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cap.round</h1>
+<p class="desc">lineCap 'round' is rendered correctly</p>
+
+
+<script>
+var t = async_test("lineCap 'round' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineCap = 'round';
+ ctx.lineWidth = 20;
+
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(35-tol, 15);
+ ctx.arc(25, 15, 10-tol, 0, Math.PI, true);
+ ctx.arc(25, 35, 10-tol, Math.PI, 0, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(85+tol, 15);
+ ctx.arc(75, 15, 10+tol, 0, Math.PI, true);
+ ctx.arc(75, 35, 10+tol, Math.PI, 0, true);
+ ctx.fill();
+
+ _assertPixel(canvas, 17,6, 0,255,0,255);
+ _assertPixel(canvas, 25,6, 0,255,0,255);
+ _assertPixel(canvas, 32,6, 0,255,0,255);
+ _assertPixel(canvas, 17,43, 0,255,0,255);
+ _assertPixel(canvas, 25,43, 0,255,0,255);
+ _assertPixel(canvas, 32,43, 0,255,0,255);
+
+ _assertPixel(canvas, 67,6, 0,255,0,255);
+ _assertPixel(canvas, 75,6, 0,255,0,255);
+ _assertPixel(canvas, 82,6, 0,255,0,255);
+ _assertPixel(canvas, 67,43, 0,255,0,255);
+ _assertPixel(canvas, 75,43, 0,255,0,255);
+ _assertPixel(canvas, 82,43, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.worker.js
new file mode 100644
index 0000000000..a8d9c1b92f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.round.worker.js
@@ -0,0 +1,72 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cap.round
+// Description:lineCap 'round' is rendered correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineCap 'round' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineCap = 'round';
+ ctx.lineWidth = 20;
+
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(35-tol, 15);
+ ctx.arc(25, 15, 10-tol, 0, Math.PI, true);
+ ctx.arc(25, 35, 10-tol, Math.PI, 0, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(85+tol, 15);
+ ctx.arc(75, 15, 10+tol, 0, Math.PI, true);
+ ctx.arc(75, 35, 10+tol, Math.PI, 0, true);
+ ctx.fill();
+
+ _assertPixel(canvas, 17,6, 0,255,0,255);
+ _assertPixel(canvas, 25,6, 0,255,0,255);
+ _assertPixel(canvas, 32,6, 0,255,0,255);
+ _assertPixel(canvas, 17,43, 0,255,0,255);
+ _assertPixel(canvas, 25,43, 0,255,0,255);
+ _assertPixel(canvas, 32,43, 0,255,0,255);
+
+ _assertPixel(canvas, 67,6, 0,255,0,255);
+ _assertPixel(canvas, 75,6, 0,255,0,255);
+ _assertPixel(canvas, 82,6, 0,255,0,255);
+ _assertPixel(canvas, 67,43, 0,255,0,255);
+ _assertPixel(canvas, 75,43, 0,255,0,255);
+ _assertPixel(canvas, 82,43, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.html
new file mode 100644
index 0000000000..0d2988877d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cap.square</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cap.square</h1>
+<p class="desc">lineCap 'square' is rendered correctly</p>
+
+
+<script>
+var t = async_test("lineCap 'square' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 5, 20, 40);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 5, 20, 40);
+
+ _assertPixel(canvas, 25,4, 0,255,0,255);
+ _assertPixel(canvas, 25,5, 0,255,0,255);
+ _assertPixel(canvas, 25,6, 0,255,0,255);
+ _assertPixel(canvas, 25,44, 0,255,0,255);
+ _assertPixel(canvas, 25,45, 0,255,0,255);
+ _assertPixel(canvas, 25,46, 0,255,0,255);
+
+ _assertPixel(canvas, 75,4, 0,255,0,255);
+ _assertPixel(canvas, 75,5, 0,255,0,255);
+ _assertPixel(canvas, 75,6, 0,255,0,255);
+ _assertPixel(canvas, 75,44, 0,255,0,255);
+ _assertPixel(canvas, 75,45, 0,255,0,255);
+ _assertPixel(canvas, 75,46, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.worker.js
new file mode 100644
index 0000000000..6776e9891b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.square.worker.js
@@ -0,0 +1,56 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cap.square
+// Description:lineCap 'square' is rendered correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineCap 'square' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 5, 20, 40);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 5, 20, 40);
+
+ _assertPixel(canvas, 25,4, 0,255,0,255);
+ _assertPixel(canvas, 25,5, 0,255,0,255);
+ _assertPixel(canvas, 25,6, 0,255,0,255);
+ _assertPixel(canvas, 25,44, 0,255,0,255);
+ _assertPixel(canvas, 25,45, 0,255,0,255);
+ _assertPixel(canvas, 25,46, 0,255,0,255);
+
+ _assertPixel(canvas, 75,4, 0,255,0,255);
+ _assertPixel(canvas, 75,5, 0,255,0,255);
+ _assertPixel(canvas, 75,6, 0,255,0,255);
+ _assertPixel(canvas, 75,44, 0,255,0,255);
+ _assertPixel(canvas, 75,45, 0,255,0,255);
+ _assertPixel(canvas, 75,46, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.html
new file mode 100644
index 0000000000..6334891f56
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cap.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cap.valid</h1>
+<p class="desc">Setting lineCap to valid values works</p>
+
+
+<script>
+var t = async_test("Setting lineCap to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineCap = 'butt'
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'round';
+ _assertSame(ctx.lineCap, 'round', "ctx.lineCap", "'round'");
+
+ ctx.lineCap = 'square';
+ _assertSame(ctx.lineCap, 'square', "ctx.lineCap", "'square'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.worker.js
new file mode 100644
index 0000000000..0010fd61dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cap.valid.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cap.valid
+// Description:Setting lineCap to valid values works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting lineCap to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineCap = 'butt'
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+
+ ctx.lineCap = 'round';
+ _assertSame(ctx.lineCap, 'round', "ctx.lineCap", "'round'");
+
+ ctx.lineCap = 'square';
+ _assertSame(ctx.lineCap, 'square', "ctx.lineCap", "'square'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.html
new file mode 100644
index 0000000000..83e2a758fd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.cross</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(110, 50);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(100, 60);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.worker.js
new file mode 100644
index 0000000000..5dc95c25f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.cross.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.cross
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(110, 50);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(100, 60);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.html
new file mode 100644
index 0000000000..7f50257288
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.defaults</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.defaults</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+ _assertSame(ctx.lineJoin, 'miter', "ctx.lineJoin", "'miter'");
+ _assertSame(ctx.miterLimit, 10, "ctx.miterLimit", "10");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.worker.js
new file mode 100644
index 0000000000..1a59b81027
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.defaults.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.defaults
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+ _assertSame(ctx.lineCap, 'butt', "ctx.lineCap", "'butt'");
+ _assertSame(ctx.lineJoin, 'miter', "ctx.lineJoin", "'miter'");
+ _assertSame(ctx.miterLimit, 10, "ctx.miterLimit", "10");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.html
new file mode 100644
index 0000000000..0d1aa4be30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.fill.noop</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.fill.noop</h1>
+<p class="desc">Filling a line draws nothing</p>
+
+
+<script>
+var t = async_test("Filling a line draws nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 20;
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(90, 30);
+ ctx.fill();
+ _assertPixel(canvas, 50,24, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,26, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.worker.js
new file mode 100644
index 0000000000..e3e8c14b3f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.fill.noop.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.fill.noop
+// Description:Filling a line draws nothing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Filling a line draws nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 20;
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(90, 30);
+ ctx.fill();
+ _assertPixel(canvas, 50,24, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,26, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.html
new file mode 100644
index 0000000000..fa67e3b3f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.invalid.strokestyle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.invalid.strokestyle</h1>
+<p class="desc">Verify correct behavior of canvas on an invalid strokeStyle()</p>
+
+
+<script>
+var t = async_test("Verify correct behavior of canvas on an invalid strokeStyle()");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = 'rgb(0, 255, 0)';
+ ctx.strokeStyle = 'nonsense';
+ ctx.lineWidth = 200;
+ ctx.moveTo(0,100);
+ ctx.lineTo(200,100);
+ ctx.stroke();
+ var imageData = ctx.getImageData(0, 0, 200, 200);
+ var imgdata = imageData.data;
+ _assert(imgdata[4] == 0, "imgdata[\""+(4)+"\"] == 0");
+ _assert(imgdata[5] == 255, "imgdata[\""+(5)+"\"] == 255");
+ _assert(imgdata[6] == 0, "imgdata[\""+(6)+"\"] == 0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.worker.js
new file mode 100644
index 0000000000..5726b1835a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.invalid.strokestyle.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.invalid.strokestyle
+// Description:Verify correct behavior of canvas on an invalid strokeStyle()
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify correct behavior of canvas on an invalid strokeStyle()");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.strokeStyle = 'rgb(0, 255, 0)';
+ ctx.strokeStyle = 'nonsense';
+ ctx.lineWidth = 200;
+ ctx.moveTo(0,100);
+ ctx.lineTo(200,100);
+ ctx.stroke();
+ var imageData = ctx.getImageData(0, 0, 200, 200);
+ var imgdata = imageData.data;
+ _assert(imgdata[4] == 0, "imgdata[\""+(4)+"\"] == 0");
+ _assert(imgdata[5] == 255, "imgdata[\""+(5)+"\"] == 255");
+ _assert(imgdata[6] == 0, "imgdata[\""+(6)+"\"] == 0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.html
new file mode 100644
index 0000000000..70e3896281
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.bevel</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.bevel</h1>
+<p class="desc">lineJoin 'bevel' is rendered correctly</p>
+
+
+<script>
+var t = async_test("lineJoin 'bevel' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.lineTo(40-tol, 20);
+ ctx.lineTo(30, 10+tol);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.lineTo(90+tol, 20);
+ ctx.lineTo(80, 10-tol);
+ ctx.fill();
+
+ _assertPixel(canvas, 34,16, 0,255,0,255);
+ _assertPixel(canvas, 34,15, 0,255,0,255);
+ _assertPixel(canvas, 35,15, 0,255,0,255);
+ _assertPixel(canvas, 36,15, 0,255,0,255);
+ _assertPixel(canvas, 36,14, 0,255,0,255);
+
+ _assertPixel(canvas, 84,16, 0,255,0,255);
+ _assertPixel(canvas, 84,15, 0,255,0,255);
+ _assertPixel(canvas, 85,15, 0,255,0,255);
+ _assertPixel(canvas, 86,15, 0,255,0,255);
+ _assertPixel(canvas, 86,14, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.worker.js
new file mode 100644
index 0000000000..efd884debe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.bevel.worker.js
@@ -0,0 +1,75 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.bevel
+// Description:lineJoin 'bevel' is rendered correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineJoin 'bevel' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.lineTo(40-tol, 20);
+ ctx.lineTo(30, 10+tol);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.lineTo(90+tol, 20);
+ ctx.lineTo(80, 10-tol);
+ ctx.fill();
+
+ _assertPixel(canvas, 34,16, 0,255,0,255);
+ _assertPixel(canvas, 34,15, 0,255,0,255);
+ _assertPixel(canvas, 35,15, 0,255,0,255);
+ _assertPixel(canvas, 36,15, 0,255,0,255);
+ _assertPixel(canvas, 36,14, 0,255,0,255);
+
+ _assertPixel(canvas, 84,16, 0,255,0,255);
+ _assertPixel(canvas, 84,15, 0,255,0,255);
+ _assertPixel(canvas, 85,15, 0,255,0,255);
+ _assertPixel(canvas, 86,15, 0,255,0,255);
+ _assertPixel(canvas, 86,14, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.html
new file mode 100644
index 0000000000..60650adeb3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.closed</h1>
+<p class="desc">Line joins are drawn at the corner of a closed rectangle</p>
+
+
+<script>
+var t = async_test("Line joins are drawn at the corner of a closed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.worker.js
new file mode 100644
index 0000000000..4882311961
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.closed.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.closed
+// Description:Line joins are drawn at the corner of a closed rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Line joins are drawn at the corner of a closed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.html
new file mode 100644
index 0000000000..be1f7e393e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.invalid</h1>
+<p class="desc">Setting lineJoin to invalid values is ignored</p>
+
+
+<script>
+var t = async_test("Setting lineJoin to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineJoin = 'bevel'
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'invalid';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'ROUND';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round\0';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round ';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = "";
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'butt';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.worker.js
new file mode 100644
index 0000000000..aa5a35c0c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.invalid.worker.js
@@ -0,0 +1,47 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.invalid
+// Description:Setting lineJoin to invalid values is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting lineJoin to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineJoin = 'bevel'
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'invalid';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'ROUND';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round\0';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round ';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = "";
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'butt';
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.html
new file mode 100644
index 0000000000..c26e883c40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.miter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.miter</h1>
+<p class="desc">lineJoin 'miter' is rendered correctly</p>
+
+
+<script>
+var t = async_test("lineJoin 'miter' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 30, 20);
+ ctx.fillRect(20, 10, 20, 30);
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 30, 20);
+ ctx.fillRect(70, 10, 20, 30);
+
+ _assertPixel(canvas, 38,12, 0,255,0,255);
+ _assertPixel(canvas, 39,11, 0,255,0,255);
+ _assertPixel(canvas, 40,10, 0,255,0,255);
+ _assertPixel(canvas, 41,9, 0,255,0,255);
+ _assertPixel(canvas, 42,8, 0,255,0,255);
+
+ _assertPixel(canvas, 88,12, 0,255,0,255);
+ _assertPixel(canvas, 89,11, 0,255,0,255);
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 91,9, 0,255,0,255);
+ _assertPixel(canvas, 92,8, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.worker.js
new file mode 100644
index 0000000000..7b70000100
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.miter.worker.js
@@ -0,0 +1,66 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.miter
+// Description:lineJoin 'miter' is rendered correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineJoin 'miter' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 30, 20);
+ ctx.fillRect(20, 10, 20, 30);
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 30, 20);
+ ctx.fillRect(70, 10, 20, 30);
+
+ _assertPixel(canvas, 38,12, 0,255,0,255);
+ _assertPixel(canvas, 39,11, 0,255,0,255);
+ _assertPixel(canvas, 40,10, 0,255,0,255);
+ _assertPixel(canvas, 41,9, 0,255,0,255);
+ _assertPixel(canvas, 42,8, 0,255,0,255);
+
+ _assertPixel(canvas, 88,12, 0,255,0,255);
+ _assertPixel(canvas, 89,11, 0,255,0,255);
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 91,9, 0,255,0,255);
+ _assertPixel(canvas, 92,8, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.html
new file mode 100644
index 0000000000..d801e5c367
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.open</h1>
+<p class="desc">Line joins are not drawn at the corner of an unclosed rectangle</p>
+
+
+<script>
+var t = async_test("Line joins are not drawn at the corner of an unclosed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.worker.js
new file mode 100644
index 0000000000..2a1242436d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.open.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.open
+// Description:Line joins are not drawn at the corner of an unclosed rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Line joins are not drawn at the corner of an unclosed rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.html
new file mode 100644
index 0000000000..0826406b9d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.parallel</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.parallel</h1>
+<p class="desc">Line joins are drawn at 180-degree joins</p>
+
+
+<script>
+var t = async_test("Line joins are drawn at 180-degree joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 300;
+ ctx.lineJoin = 'round';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(-100, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.worker.js
new file mode 100644
index 0000000000..f5b67dc5a2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.parallel.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.parallel
+// Description:Line joins are drawn at 180-degree joins
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Line joins are drawn at 180-degree joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 300;
+ ctx.lineJoin = 'round';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(-100, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.html
new file mode 100644
index 0000000000..70f6a8e517
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.round</h1>
+<p class="desc">lineJoin 'round' is rendered correctly</p>
+
+
+<script>
+var t = async_test("lineJoin 'round' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'round';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.arc(30, 20, 10-tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.arc(80, 20, 10+tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ _assertPixel(canvas, 36,14, 0,255,0,255);
+ _assertPixel(canvas, 36,13, 0,255,0,255);
+ _assertPixel(canvas, 37,13, 0,255,0,255);
+ _assertPixel(canvas, 38,13, 0,255,0,255);
+ _assertPixel(canvas, 38,12, 0,255,0,255);
+
+ _assertPixel(canvas, 86,14, 0,255,0,255);
+ _assertPixel(canvas, 86,13, 0,255,0,255);
+ _assertPixel(canvas, 87,13, 0,255,0,255);
+ _assertPixel(canvas, 88,13, 0,255,0,255);
+ _assertPixel(canvas, 88,12, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.worker.js
new file mode 100644
index 0000000000..33b135d604
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.round.worker.js
@@ -0,0 +1,73 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.round
+// Description:lineJoin 'round' is rendered correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineJoin 'round' is rendered correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'round';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.arc(30, 20, 10-tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.arc(80, 20, 10+tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ _assertPixel(canvas, 36,14, 0,255,0,255);
+ _assertPixel(canvas, 36,13, 0,255,0,255);
+ _assertPixel(canvas, 37,13, 0,255,0,255);
+ _assertPixel(canvas, 38,13, 0,255,0,255);
+ _assertPixel(canvas, 38,12, 0,255,0,255);
+
+ _assertPixel(canvas, 86,14, 0,255,0,255);
+ _assertPixel(canvas, 86,13, 0,255,0,255);
+ _assertPixel(canvas, 87,13, 0,255,0,255);
+ _assertPixel(canvas, 88,13, 0,255,0,255);
+ _assertPixel(canvas, 88,12, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.html
new file mode 100644
index 0000000000..3b3935d1f7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.join.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.join.valid</h1>
+<p class="desc">Setting lineJoin to valid values works</p>
+
+
+<script>
+var t = async_test("Setting lineJoin to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineJoin = 'bevel'
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'round';
+ _assertSame(ctx.lineJoin, 'round', "ctx.lineJoin", "'round'");
+
+ ctx.lineJoin = 'miter';
+ _assertSame(ctx.lineJoin, 'miter', "ctx.lineJoin", "'miter'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.worker.js
new file mode 100644
index 0000000000..d1bdfd8845
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.join.valid.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.join.valid
+// Description:Setting lineJoin to valid values works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting lineJoin to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineJoin = 'bevel'
+ _assertSame(ctx.lineJoin, 'bevel', "ctx.lineJoin", "'bevel'");
+
+ ctx.lineJoin = 'round';
+ _assertSame(ctx.lineJoin, 'round', "ctx.lineJoin", "'round'");
+
+ ctx.lineJoin = 'miter';
+ _assertSame(ctx.lineJoin, 'miter', "ctx.lineJoin", "'miter'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.html
new file mode 100644
index 0000000000..587062ed12
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.acute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.acute</h1>
+<p class="desc">Miter joins are drawn correctly with acute angles</p>
+
+
+<script>
+var t = async_test("Miter joins are drawn correctly with acute angles");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 2.614;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 2.613;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.worker.js
new file mode 100644
index 0000000000..0ab5c9050c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.acute.worker.js
@@ -0,0 +1,47 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.acute
+// Description:Miter joins are drawn correctly with acute angles
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Miter joins are drawn correctly with acute angles");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 2.614;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 2.613;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.html
new file mode 100644
index 0000000000..1ae5c55aef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.exceeded</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.exceeded</h1>
+<p class="desc">Miter joins are not drawn when the miter limit is exceeded</p>
+
+
+<script>
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201); // slightly non-right-angle to avoid being a special case
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.worker.js
new file mode 100644
index 0000000000..41c4716c88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.exceeded.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.exceeded
+// Description:Miter joins are not drawn when the miter limit is exceeded
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201); // slightly non-right-angle to avoid being a special case
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.html
new file mode 100644
index 0000000000..eacd95b811
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.invalid</h1>
+<p class="desc">Setting miterLimit to invalid values is ignored</p>
+
+
+<script>
+var t = async_test("Setting miterLimit to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.miterLimit = 1.5;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 0;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -1;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = Infinity;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -Infinity;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = NaN;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 'string';
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = true;
+ _assertSame(ctx.miterLimit, 1, "ctx.miterLimit", "1");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = false;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.worker.js
new file mode 100644
index 0000000000..62f250670c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.invalid.worker.js
@@ -0,0 +1,55 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.invalid
+// Description:Setting miterLimit to invalid values is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting miterLimit to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.miterLimit = 1.5;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 0;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -1;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = Infinity;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -Infinity;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = NaN;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 'string';
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = true;
+ _assertSame(ctx.miterLimit, 1, "ctx.miterLimit", "1");
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = false;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.html
new file mode 100644
index 0000000000..bad3cedc0e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.lineedge</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.lineedge</h1>
+<p class="desc">Miter joins are not drawn when the miter limit is exceeded at the corners of a zero-height rectangle</p>
+
+
+<script>
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded at the corners of a zero-height rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.strokeRect(100, 25, 200, 0);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.worker.js
new file mode 100644
index 0000000000..2b61bc8975
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.lineedge.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.lineedge
+// Description:Miter joins are not drawn when the miter limit is exceeded at the corners of a zero-height rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded at the corners of a zero-height rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.strokeRect(100, 25, 200, 0);
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.html
new file mode 100644
index 0000000000..efab533fba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.obtuse</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.obtuse</h1>
+<p class="desc">Miter joins are drawn correctly with obtuse angles</p>
+
+
+<script>
+var t = async_test("Miter joins are drawn correctly with obtuse angles");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 1600;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.083;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.082;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.worker.js
new file mode 100644
index 0000000000..42b1cd37b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.obtuse.worker.js
@@ -0,0 +1,47 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.obtuse
+// Description:Miter joins are drawn correctly with obtuse angles
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Miter joins are drawn correctly with obtuse angles");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 1600;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.083;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.082;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.html
new file mode 100644
index 0000000000..5bb4e63230
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.rightangle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.rightangle</h1>
+<p class="desc">Miter joins are not drawn when the miter limit is exceeded, on exact right angles</p>
+
+
+<script>
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded, on exact right angles");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 200);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.worker.js
new file mode 100644
index 0000000000..42e995deed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.rightangle.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.rightangle
+// Description:Miter joins are not drawn when the miter limit is exceeded, on exact right angles
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Miter joins are not drawn when the miter limit is exceeded, on exact right angles");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 200);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.html
new file mode 100644
index 0000000000..3c569ea655
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.valid</h1>
+<p class="desc">Setting miterLimit to valid values works</p>
+
+
+<script>
+var t = async_test("Setting miterLimit to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.miterLimit = 1.5;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = "1e1";
+ _assertSame(ctx.miterLimit, 10, "ctx.miterLimit", "10");
+
+ ctx.miterLimit = 1/1024;
+ _assertSame(ctx.miterLimit, 1/1024, "ctx.miterLimit", "1/1024");
+
+ ctx.miterLimit = 1000;
+ _assertSame(ctx.miterLimit, 1000, "ctx.miterLimit", "1000");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.worker.js
new file mode 100644
index 0000000000..cb0548abd0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.valid.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.valid
+// Description:Setting miterLimit to valid values works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting miterLimit to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.miterLimit = 1.5;
+ _assertSame(ctx.miterLimit, 1.5, "ctx.miterLimit", "1.5");
+
+ ctx.miterLimit = "1e1";
+ _assertSame(ctx.miterLimit, 10, "ctx.miterLimit", "10");
+
+ ctx.miterLimit = 1/1024;
+ _assertSame(ctx.miterLimit, 1/1024, "ctx.miterLimit", "1/1024");
+
+ ctx.miterLimit = 1000;
+ _assertSame(ctx.miterLimit, 1000, "ctx.miterLimit", "1000");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.html
new file mode 100644
index 0000000000..da87cf4a29
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.miter.within</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.miter.within</h1>
+<p class="desc">Miter joins are drawn when the miter limit is not quite exceeded</p>
+
+
+<script>
+var t = async_test("Miter joins are drawn when the miter limit is not quite exceeded");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.416;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.worker.js
new file mode 100644
index 0000000000..1d9aacb817
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.miter.within.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.miter.within
+// Description:Miter joins are drawn when the miter limit is not quite exceeded
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Miter joins are drawn when the miter limit is not quite exceeded");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.416;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.html
new file mode 100644
index 0000000000..d3d13ece09
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.union</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.union</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 24);
+ ctx.lineTo(100, 25);
+ ctx.lineTo(0, 26);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 25,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 25,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.worker.js
new file mode 100644
index 0000000000..f2c7b2c4ce
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.union.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.union
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 24);
+ ctx.lineTo(100, 25);
+ ctx.lineTo(0, 26);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 25,1, 0,255,0,255);
+ _assertPixel(canvas, 48,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 25,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.html
new file mode 100644
index 0000000000..88e38294bf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.width.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.width.basic</h1>
+<p class="desc">lineWidth determines the width of line strokes</p>
+
+
+<script>
+var t = async_test("lineWidth determines the width of line strokes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 20;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 14,25, 0,255,0,255);
+ _assertPixel(canvas, 15,25, 0,255,0,255);
+ _assertPixel(canvas, 16,25, 0,255,0,255);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 34,25, 0,255,0,255);
+ _assertPixel(canvas, 35,25, 0,255,0,255);
+ _assertPixel(canvas, 36,25, 0,255,0,255);
+
+ _assertPixel(canvas, 64,25, 0,255,0,255);
+ _assertPixel(canvas, 65,25, 0,255,0,255);
+ _assertPixel(canvas, 66,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 84,25, 0,255,0,255);
+ _assertPixel(canvas, 85,25, 0,255,0,255);
+ _assertPixel(canvas, 86,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.worker.js
new file mode 100644
index 0000000000..012d9b11bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.basic.worker.js
@@ -0,0 +1,58 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.width.basic
+// Description:lineWidth determines the width of line strokes
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineWidth determines the width of line strokes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 20;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 14,25, 0,255,0,255);
+ _assertPixel(canvas, 15,25, 0,255,0,255);
+ _assertPixel(canvas, 16,25, 0,255,0,255);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 34,25, 0,255,0,255);
+ _assertPixel(canvas, 35,25, 0,255,0,255);
+ _assertPixel(canvas, 36,25, 0,255,0,255);
+
+ _assertPixel(canvas, 64,25, 0,255,0,255);
+ _assertPixel(canvas, 65,25, 0,255,0,255);
+ _assertPixel(canvas, 66,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 84,25, 0,255,0,255);
+ _assertPixel(canvas, 85,25, 0,255,0,255);
+ _assertPixel(canvas, 86,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.html
new file mode 100644
index 0000000000..481a316822
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.width.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.width.invalid</h1>
+<p class="desc">Setting lineWidth to invalid values is ignored</p>
+
+
+<script>
+var t = async_test("Setting lineWidth to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 1.5;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 0;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -1;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = Infinity;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -Infinity;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = NaN;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 'string';
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = true;
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = false;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.worker.js
new file mode 100644
index 0000000000..3a10cb48e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.invalid.worker.js
@@ -0,0 +1,55 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.width.invalid
+// Description:Setting lineWidth to invalid values is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting lineWidth to invalid values is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 1.5;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 0;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -1;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = Infinity;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -Infinity;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = NaN;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 'string';
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = true;
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = false;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.html
new file mode 100644
index 0000000000..9ac77a9c49
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.width.scaledefault</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.width.scaledefault</h1>
+<p class="desc">Default lineWidth strokes are affected by scale transformations</p>
+
+
+<script>
+var t = async_test("Default lineWidth strokes are affected by scale transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(50, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.moveTo(0, 0.5);
+ ctx.lineTo(2, 0.5);
+ ctx.stroke();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 50,5, 0,255,0,255);
+ _assertPixel(canvas, 50,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.worker.js
new file mode 100644
index 0000000000..8d8089476b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.scaledefault.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.width.scaledefault
+// Description:Default lineWidth strokes are affected by scale transformations
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Default lineWidth strokes are affected by scale transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(50, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.moveTo(0, 0.5);
+ ctx.lineTo(2, 0.5);
+ ctx.stroke();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 50,5, 0,255,0,255);
+ _assertPixel(canvas, 50,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.html
new file mode 100644
index 0000000000..ea8f285dc4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.width.transformed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.width.transformed</h1>
+<p class="desc">Line stroke widths are affected by scale transformations</p>
+
+
+<script>
+var t = async_test("Line stroke widths are affected by scale transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 4;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.save();
+ ctx.scale(5, 1);
+ ctx.beginPath();
+ ctx.moveTo(5, 15);
+ ctx.lineTo(5, 35);
+ ctx.stroke();
+ ctx.restore();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.save();
+ ctx.scale(-5, 1);
+ ctx.beginPath();
+ ctx.moveTo(-15, 15);
+ ctx.lineTo(-15, 35);
+ ctx.stroke();
+ ctx.restore();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 14,25, 0,255,0,255);
+ _assertPixel(canvas, 15,25, 0,255,0,255);
+ _assertPixel(canvas, 16,25, 0,255,0,255);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 34,25, 0,255,0,255);
+ _assertPixel(canvas, 35,25, 0,255,0,255);
+ _assertPixel(canvas, 36,25, 0,255,0,255);
+
+ _assertPixel(canvas, 64,25, 0,255,0,255);
+ _assertPixel(canvas, 65,25, 0,255,0,255);
+ _assertPixel(canvas, 66,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 84,25, 0,255,0,255);
+ _assertPixel(canvas, 85,25, 0,255,0,255);
+ _assertPixel(canvas, 86,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.worker.js
new file mode 100644
index 0000000000..67fc0a29bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.transformed.worker.js
@@ -0,0 +1,64 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.width.transformed
+// Description:Line stroke widths are affected by scale transformations
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Line stroke widths are affected by scale transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 4;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.save();
+ ctx.scale(5, 1);
+ ctx.beginPath();
+ ctx.moveTo(5, 15);
+ ctx.lineTo(5, 35);
+ ctx.stroke();
+ ctx.restore();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.save();
+ ctx.scale(-5, 1);
+ ctx.beginPath();
+ ctx.moveTo(-15, 15);
+ ctx.lineTo(-15, 35);
+ ctx.stroke();
+ ctx.restore();
+ ctx.fillRect(65, 15, 20, 20);
+
+ _assertPixel(canvas, 14,25, 0,255,0,255);
+ _assertPixel(canvas, 15,25, 0,255,0,255);
+ _assertPixel(canvas, 16,25, 0,255,0,255);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 34,25, 0,255,0,255);
+ _assertPixel(canvas, 35,25, 0,255,0,255);
+ _assertPixel(canvas, 36,25, 0,255,0,255);
+
+ _assertPixel(canvas, 64,25, 0,255,0,255);
+ _assertPixel(canvas, 65,25, 0,255,0,255);
+ _assertPixel(canvas, 66,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ _assertPixel(canvas, 84,25, 0,255,0,255);
+ _assertPixel(canvas, 85,25, 0,255,0,255);
+ _assertPixel(canvas, 86,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.html b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.html
new file mode 100644
index 0000000000..813433da3f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.line.width.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.line.width.valid</h1>
+<p class="desc">Setting lineWidth to valid values works</p>
+
+
+<script>
+var t = async_test("Setting lineWidth to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 1.5;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = "1e1";
+ _assertSame(ctx.lineWidth, 10, "ctx.lineWidth", "10");
+
+ ctx.lineWidth = 1/1024;
+ _assertSame(ctx.lineWidth, 1/1024, "ctx.lineWidth", "1/1024");
+
+ ctx.lineWidth = 1000;
+ _assertSame(ctx.lineWidth, 1000, "ctx.lineWidth", "1000");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.worker.js
new file mode 100644
index 0000000000..195575f7de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/line-styles/2d.line.width.valid.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.line.width.valid
+// Description:Setting lineWidth to valid values works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Setting lineWidth to valid values works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 1.5;
+ _assertSame(ctx.lineWidth, 1.5, "ctx.lineWidth", "1.5");
+
+ ctx.lineWidth = "1e1";
+ _assertSame(ctx.lineWidth, 10, "ctx.lineWidth", "10");
+
+ ctx.lineWidth = 1/1024;
+ _assertSame(ctx.lineWidth, 1/1024, "ctx.lineWidth", "1/1024");
+
+ ctx.lineWidth = 1000;
+ _assertSame(ctx.lineWidth, 1000, "ctx.lineWidth", "1000");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html
new file mode 100644
index 0000000000..1cc0e5bffd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-converttoblob">
+<script id="myWorker" type="text/worker">
+self.onmessage = function(e) {
+};
+</script>
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+function drawCanvas(ctx)
+{
+ ctx.fillStyle = "red";
+ ctx.fillRect(0, 0, 5, 5);
+ ctx.fillStyle = "green";
+ ctx.fillRect(5, 0, 5, 5);
+ ctx.fillStyle = "blue";
+ ctx.fillRect(0, 5, 5, 5);
+ ctx.fillStyle = "black";
+ ctx.fillRect(5, 5, 5, 5);
+}
+
+function compareImages(image1, image2)
+{
+ var canvas1 = document.createElement('canvas');
+ var canvas2 = document.createElement('canvas');
+ canvas1.width = canvas1.height = 10;
+ canvas2.width = canvas2.height = 10;
+ var ctx1 = canvas1.getContext('2d');
+ var ctx2 = canvas1.getContext('2d');
+ ctx1.drawImage(image1, 0, 0);
+ ctx2.drawImage(image2, 0, 0);
+ var data1 = ctx1.getImageData(0, 0, 10, 10).data;
+ var data2 = ctx2.getImageData(0, 0, 10, 10).data;
+ assert_equals(data1.length, data2.length);
+ var imageMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ if (data1[i] != data2[i]) {
+ imageMatched = false;
+ break;
+ }
+ }
+ assert_true(imageMatched);
+}
+
+function testConvertToBlob(t, typeVal, qualityVal) {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var oCtx = offscreenCanvas.getContext('2d');
+ drawCanvas(oCtx);
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ drawCanvas(ctx);
+ var imageLoadedCounter = 0;
+
+ var image1 = new Image();
+ var image2 = new Image();
+ var promise;
+ if (typeVal == "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob();
+ else if (typeVal == "empty" && qualityVal != "empty")
+ promise = offscreenCanvas.convertToBlob({quality: qualityVal});
+ else if (typeVal != "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob({type: typeVal});
+ else
+ promise = offscreenCanvas.convertToBlob({type: typeVal, quality: qualityVal});
+ promise.then(function(blob2) {
+ image2.src = URL.createObjectURL(blob2);
+ if (typeVal == "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ });
+ } else if (typeVal == "empty" && qualityVal != "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, "image/png", qualityVal);
+ } else if (typeVal != "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, 1.0);
+ } else {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, qualityVal);
+ }
+ image1.onload = image2.onload = t.step_func(function() {
+ imageLoadedCounter++;
+ if (imageLoadedCounter == 2) {
+ compareImages(image1, image2);
+ t.done();
+ }
+ });
+ });
+}
+
+async_test(function(t) {
+ testConvertToBlob(t, "empty", "empty");
+ testConvertToBlob(t, "empty", 1.0);
+ testConvertToBlob(t, "empty", 0.2);
+}, "Test that convertToBlob with default type produces correct result");
+
+async_test(function(t) {
+ testConvertToBlob(t, "image/png", "empty");
+ testConvertToBlob(t, "image/png", 1.0);
+ testConvertToBlob(t, "image/png", 0.2);
+}, "Test that convertToBlob with png produces correct result");
+
+async_test(function(t) {
+ testConvertToBlob(t, "image/jpeg", "empty");
+ testConvertToBlob(t, "image/jpeg", 1.0);
+ testConvertToBlob(t, "image/jpeg", 0.2);
+}, "Test that convertToBlob with jpge produces correct result");
+
+async_test(function(t) {
+ testConvertToBlob(t, "image/webp", "empty");
+ testConvertToBlob(t, "image/webp", 1.0);
+ testConvertToBlob(t, "image/webp", 0.2);
+}, "Test that convertToBlob with webp produces correct result");
+
+async_test(function(t) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ offscreenCanvas.convertToBlob().then(t.step_func_done(function() {
+ assert_false("convertToBlob didn't throw, but should be");
+ }), t.step_func_done(function(e) {
+ assert_true(e instanceof DOMException);
+ assert_equals(e.name, "InvalidStateError");
+ }));
+}, "Test that call convertToBlob on a detached OffscreenCanvas throws exception");
+
+async_test(function(t) {
+ var offscreenCanvas = new OffscreenCanvas(0, 0);
+ offscreenCanvas.convertToBlob().then(t.step_func_done(function() {
+ assert_false("convertToBlob didn't throw, but should be");
+ }), t.step_func_done(function(e) {
+ assert_true(e instanceof DOMException);
+ assert_equals(e.name, "IndexSizeError");
+ }));
+}, "Test that call convertToBlob on a OffscreenCanvas with size 0 throws exception");
+
+async_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ img.crossOrigin = "anonymous";
+ img.onload = t.step_func_done(() => {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext("2d");
+ ctx.drawImage(img, 0, 0);
+ offscreenCanvas.convertToBlob().then(t.step_func_done(function() {
+ assert_false("convertToBlob didn't throw, but should");
+ }), t.step_func_done(function(e) {
+ assert_true(e instanceof DOMException);
+ assert_equals(e.name, "SecurityError");
+ }));
+ });
+}, "Test that call convertToBlob on a OffscreenCanvas with tainted origin throws exception");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html
new file mode 100644
index 0000000000..5c1fa4cf40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html
@@ -0,0 +1,337 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-converttoblob">
+<script id="myWorker" type="text/worker">
+function drawCanvas(ctx)
+{
+ ctx.fillStyle = "red";
+ ctx.fillRect(0, 0, 5, 5);
+ ctx.fillStyle = "green";
+ ctx.fillRect(5, 0, 5, 5);
+ ctx.fillStyle = "blue";
+ ctx.fillRect(0, 5, 5, 5);
+ ctx.fillStyle = "black";
+ ctx.fillRect(5, 5, 5, 5);
+}
+
+function testConvertToBlob(typeVal, qualityVal) {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var oCtx = offscreenCanvas.getContext('2d');
+ drawCanvas(oCtx);
+
+ var promise;
+ if (typeVal == "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob();
+ else if (typeVal == "empty" && qualityVal != "empty")
+ promise = offscreenCanvas.convertToBlob({quality: qualityVal});
+ else if (typeVal != "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob({type: typeVal});
+ else
+ promise = offscreenCanvas.convertToBlob({type: typeVal, quality: qualityVal});
+ promise.then(function(blob) {
+ self.postMessage(blob);
+ });
+}
+
+function testConvertToBlobException1()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ offscreenCanvas.convertToBlob().then(function() {
+ self.postMessage(false);
+ }, function(e) {
+ self.postMessage(e instanceof DOMException && e.name == "InvalidStateError");
+ });
+}
+
+function testConvertToBlobException2()
+{
+ var offscreenCanvas = new OffscreenCanvas(0, 0);
+ offscreenCanvas.convertToBlob().then(function() {
+ self.postMessage(false);
+ }, function(e) {
+ self.postMessage(e instanceof DOMException && e.name == "IndexSizeError");
+ });
+}
+
+function testConvertToBlobException3(bitmap)
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext("2d");
+ ctx.drawImage(bitmap, 0, 0);
+ offscreenCanvas.convertToBlob().then(function() {
+ self.postMessage(false);
+ }, function(e) {
+ self.postMessage(e instanceof DOMException && e.name == "SecurityError");
+ });
+}
+
+self.onmessage = function(e) {
+ if (e.data instanceof ImageBitmap) {
+ testConvertToBlobException3(e.data);
+ return;
+ }
+
+ switch(e.data) {
+ case 'test1':
+ testConvertToBlob("empty", "empty");
+ break;
+ case 'test2':
+ testConvertToBlob("empty", 1.0);
+ break;
+ case 'test3':
+ testConvertToBlob("empty", 0.2);
+ break;
+ case 'test4':
+ testConvertToBlob("image/png", "empty");
+ break;
+ case 'test5':
+ testConvertToBlob("image/png", 1.0);
+ break;
+ case 'test6':
+ testConvertToBlob("image/png", 0.2);
+ break;
+ case 'test7':
+ testConvertToBlob("image/jpeg", "empty");
+ break;
+ case 'test8':
+ testConvertToBlob("image/jpeg", 1.0);
+ break;
+ case 'test9':
+ testConvertToBlob("image/jpeg", 0.2);
+ break;
+ case 'test10':
+ testConvertToBlob("image/webp", "empty");
+ break;
+ case 'test11':
+ testConvertToBlob("image/webp", 1.0);
+ break;
+ case 'test12':
+ testConvertToBlob("image/webp", 0.2);
+ break;
+ case 'test13':
+ testConvertToBlobException1();
+ break;
+ case 'test14':
+ testConvertToBlobException2();
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(test) {
+ var blob = new Blob([document.getElementById("myWorker").textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onerror = test.unreached_func("error");
+ return worker;
+}
+
+function drawCanvas(ctx)
+{
+ ctx.fillStyle = "red";
+ ctx.fillRect(0, 0, 5, 5);
+ ctx.fillStyle = "green";
+ ctx.fillRect(5, 0, 5, 5);
+ ctx.fillStyle = "blue";
+ ctx.fillRect(0, 5, 5, 5);
+ ctx.fillStyle = "black";
+ ctx.fillRect(5, 5, 5, 5);
+}
+
+function compareImages(image1, image2)
+{
+ var canvas1 = document.createElement('canvas');
+ var canvas2 = document.createElement('canvas');
+ canvas1.width = canvas1.height = 10;
+ canvas2.width = canvas2.height = 10;
+ var ctx1 = canvas1.getContext('2d');
+ var ctx2 = canvas1.getContext('2d');
+ ctx1.drawImage(image1, 0, 0);
+ ctx2.drawImage(image2, 0, 0);
+ var data1 = ctx1.getImageData(0, 0, 10, 10).data;
+ var data2 = ctx2.getImageData(0, 0, 10, 10).data;
+ assert_equals(data1.length, data2.length);
+ var imageMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ if (data1[i] != data2[i]) {
+ imageMatched = false;
+ break;
+ }
+ }
+ assert_true(imageMatched);
+}
+
+function compareWithToBlob(t, typeVal, qualityVal, blob2)
+{
+ var image1 = new Image();
+ var image2 = new Image();
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ drawCanvas(ctx);
+ var imageLoadedCounter = 0;
+
+ if (typeVal == "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ });
+ } else if (typeVal == "empty" && qualityVal != "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, "image/png", qualityVal);
+ } else if (typeVal != "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, 1.0);
+ } else {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, qualityVal);
+ }
+ image2.src = URL.createObjectURL(blob2);
+ image1.onload = image2.onload = t.step_func(function() {
+ imageLoadedCounter++;
+ if (imageLoadedCounter == 2) {
+ compareImages(image1, image2);
+ t.done();
+ }
+ });
+}
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "empty", "empty", msg.data);
+ }));
+ worker.postMessage('test1');
+}, "Test that convertToBlob with default arguments produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "empty", 1.0, msg.data);
+ }));
+ worker.postMessage('test2');
+}, "Test that convertToBlob with default type/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "empty", 0.2, msg.data);
+ }));
+ worker.postMessage('test3');
+}, "Test that convertToBlob with default type/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/png", "empty", msg.data);
+ }));
+ worker.postMessage('test4');
+}, "Test that convertToBlob with png/default quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/png", 1.0, msg.data);
+ }));
+ worker.postMessage('test5');
+}, "Test that convertToBlob with png/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/png", 0.2, msg.data);
+ }));
+ worker.postMessage('test6');
+}, "Test that convertToBlob with png/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/jpeg", "empty", msg.data);
+ }));
+ worker.postMessage('test7');
+}, "Test that convertToBlob with jpeg/default quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/jpeg", 1.0, msg.data);
+ }));
+ worker.postMessage('test8');
+}, "Test that convertToBlob with jpeg/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/jpeg", 0.2, msg.data);
+ }));
+ worker.postMessage('test9');
+}, "Test that convertToBlob with jpeg/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/webp", "empty", msg.data);
+ }));
+ worker.postMessage('test10');
+}, "Test that convertToBlob with webp/default quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/webp", 1.0, msg.data);
+ }));
+ worker.postMessage('test11');
+}, "Test that convertToBlob with webp/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/webp", 0.2, msg.data);
+ }));
+ worker.postMessage('test12');
+}, "Test that convertToBlob with webp/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data instanceof Object)
+ return;
+ assert_true(msg.data);
+ t.done();
+ }));
+ worker.postMessage('test13');
+}, "Test that call convertToBlob on a detached OffscreenCanvas throws exception in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ t.done();
+ }));
+ worker.postMessage('test14');
+}, "Test that call convertToBlob on a OffscreenCanvas with size 0 throws exception in a worker");
+
+async_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ img.crossOrigin = "anonymous";
+ img.onload = t.step_func_done(() => {
+ createImageBitmap(img).then(t.step_func_done(bitmap => {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ t.done();
+ }));
+ worker.postMessage(bitmap);
+ }));
+ });
+}, "Test that call convertToBlob on a OffscreenCanvas with tainted origin throws exception in a worker");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html
new file mode 100644
index 0000000000..a5fd7ab066
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>OffscreenCanvas test: 2d.text.draw.generic.family</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script>
+function drawCanvas(ctx, family)
+{
+ ctx.font = '16px ' + family;
+ ctx.fillText(family, 0, 16);
+}
+
+function testDrawGenericFamily(family)
+{
+ let offscreenCanvas = new OffscreenCanvas(88, 24);
+ let oCtx = offscreenCanvas.getContext('2d');
+ drawCanvas(oCtx, family);
+ let canvas = document.createElement('canvas');
+ let ctx = canvas.getContext('2d');
+ drawCanvas(ctx, family);
+
+ let data1 = oCtx.getImageData(0, 0, 88, 24).data;
+ let data2 = ctx.getImageData(0, 0, 88, 24).data;
+ assert_array_equals(data1, data2,
+ "The image data generated by drawing generic font family '" + family +
+ "' should be the same for both OffscreenCanvas and regular canvas");
+}
+
+test(function() {
+ testDrawGenericFamily('sans-serif');
+}, "Test that drawing sans-serif produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('serif');
+}, "Test that drawing serif produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('cursive');
+}, "Test that drawing cursive produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('fantasy');
+}, "Test that drawing fantasy produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('monospace');
+}, "Test that drawing monospace produces the same result between canvas and OffscreenCanvas");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html
new file mode 100644
index 0000000000..7a88c032ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>OffscreenCanvas test: 2d.text.draw.generic.family.w</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script id='myWorker' type='text/worker'>
+self.onmessage = function(e) {
+ let oc = new OffscreenCanvas(88, 24);
+ let ctx = oc.getContext('2d');
+ ctx.font = '32px ' + e.data.family;
+ ctx.fillText(e.data.family, 0, 16);
+ self.postMessage(ctx.getImageData(0, 0, 88, 24).data);
+};
+</script>
+<script>
+function testDrawGenericFamily(t, family)
+{
+ let blob = new Blob([document.getElementById('myWorker').textContent]);
+ let worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ let ctx = document.createElement('canvas').getContext('2d');
+ ctx.font = '32px ' + family;
+ ctx.fillText(family, 0, 16);
+ assert_array_equals(ctx.getImageData(0, 0, 88, 24).data, msg.data,
+ "The image data generated by drawing generic font family '" + family +
+ "' should be the same for both OffscreenCanvas and regular canvas");
+ t.done();
+ });
+ worker.postMessage({family: family});
+}
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'sans-serif');
+}, "Test that drawing sans-serif produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'serif');
+}, "Test that drawing serif produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'cursive');
+}, "Test that drawing cursive produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'fantasy');
+}, "Test that drawing fantasy produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'monospace');
+}, "Test that drawing monospace produces the same result between canvas and OffscreenCanvas in a Worker");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html
new file mode 100644
index 0000000000..25691983f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html
@@ -0,0 +1,27 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="offscreencanvas.filter.js"></script>
+<script>
+var patternCanvas = createPatternCanvas();
+
+var getOffscreenContextForFilter = function(filter, pattern) {
+ var oc = new OffscreenCanvas(80, 80);
+ var offCtx = oc.getContext('2d');
+ offCtx.filter = filter;
+ offCtx.drawImage(pattern, 5, 5);
+ offCtx.drawImage(pattern, 25, 25);
+ offCtx.drawImage(pattern, 45, 45);
+ return offCtx;
+};
+
+var testFilter = function(filter) {
+ var offCtx = getOffscreenContextForFilter(filter, patternCanvas);
+ var ctx = getRegularContextForFilter(filter, patternCanvas);
+ var offImageData = offCtx.getImageData(0, 0, 80, 80).data;
+ var imageData = ctx.getImageData(0, 0, 80, 80).data;
+ matchImageDataResults(offImageData, imageData, filter);
+};
+
+generate_tests(testFilter, [filters]);
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js
new file mode 100644
index 0000000000..cb2e245803
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js
@@ -0,0 +1,48 @@
+var getRegularContextForFilter = function(filter, pattern) {
+ var c = document.createElement("canvas");
+ c.width = c.height = 80;
+ var ctx = c.getContext('2d');
+ ctx.filter = filter;
+ ctx.drawImage(pattern, 5, 5);
+ ctx.drawImage(pattern, 25, 25);
+ ctx.drawImage(pattern, 45, 45);
+ return ctx;
+};
+
+var matchImageDataResults = function(offscreenImage, regularImage, filter) {
+ assert_array_equals(offscreenImage, regularImage,
+ "The image data generated by filter " +
+ filter +
+ " should be the same for both OffscreenCanvas and regular canvas");
+};
+
+var createPatternCanvas = function() {
+ var patternCanvas = document.createElement('canvas');
+ patternCanvas.width = 20;
+ patternCanvas.height = 20;
+ var patternCtx = patternCanvas.getContext('2d');
+ patternCtx.fillStyle = '#A00';
+ patternCtx.fillRect(0, 0, 10, 10);
+ patternCtx.fillStyle = '#0A0';
+ patternCtx.fillRect(10, 0, 10, 10);
+ patternCtx.fillStyle = '#00A';
+ patternCtx.fillRect(0, 10, 10, 10);
+ patternCtx.fillStyle = "#AA0";
+ patternCtx.fillRect(10, 10, 10, 10);
+ return patternCanvas;
+};
+
+var filters = [ "none" ,
+ "blur(10px)" ,
+ "brightness(40%)" ,
+ "contrast(20%)" ,
+ "drop-shadow(0 0 5px green)" ,
+ "grayscale(100%)" ,
+ "invert(100%)" ,
+ "opacity(50%)" ,
+ "saturate(20%)" ,
+ "sepia(100%)" ,
+ "sepia(1) hue-rotate(200deg)",
+ "url(#url)" ];
+
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html
new file mode 100644
index 0000000000..d6ed915b53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html
@@ -0,0 +1,54 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="offscreencanvas.filter.js"></script>
+<script id='myWorker' type='text/worker'>
+self.onmessage = function(e) {
+ var getOffscreenCanvasForFilter = function(filter, pattern) {
+ var oc = new OffscreenCanvas(80, 80);
+ var offCtx = oc.getContext('2d');
+ offCtx.filter = filter;
+ offCtx.drawImage(pattern, 5, 5);
+ offCtx.drawImage(pattern, 25, 25);
+ offCtx.drawImage(pattern, 45, 45);
+ return oc;
+ };
+
+ var filters = e.data.filters;
+ var pattern = e.data.pattern;
+ var ret = [];
+ for (var i = 0; i < filters.length; i++) {
+ var oc = getOffscreenCanvasForFilter(filters[i], pattern);
+ var imageBitmap = oc.transferToImageBitmap();
+ ret.push(imageBitmap);
+ }
+ self.postMessage(ret, ret);
+};
+</script>
+<script>
+var patternCanvas = createPatternCanvas();
+
+// Build a list of image data on regular canvas with different filters
+var listCanvasImageData = [];
+for (var j = 0; j < filters.length; j++) {
+ var ctx = getRegularContextForFilter(filters[j], patternCanvas);
+ listCanvasImageData.push(ctx.getImageData(0, 0, 80, 80).data);
+}
+
+function consumeImageBitmap(patternImage) {
+ async_test(t => {
+ var blob = new Blob([document.getElementById('myWorker').textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ for (var i = 0; i < msg.data.length; ++i) {
+ var outputCtx = document.createElement("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data[i], 0, 0, 80, 80);
+ matchImageDataResults(outputCtx.getImageData(0, 0, 80, 80).data, listCanvasImageData[i], filters[i]);
+ }
+ t.done();
+ });
+ worker.postMessage({filters: filters, pattern: patternImage}, [patternImage]);
+ });
+}
+
+createImageBitmap(patternCanvas).then(consumeImageBitmap);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html
new file mode 100644
index 0000000000..a78524c51d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OffscreenCanvasRenderingContext2D imageSmoothingEnabled test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#the-offscreen-2d-rendering-context">
+<script>
+function createTestImage() {
+ var image = new OffscreenCanvas(100, 50);
+ var imgctx = image.getContext('2d');
+ imgctx.fillStyle = "#F00";
+ imgctx.fillRect(0, 0, 2, 2);
+ imgctx.fillStyle = "#0F0";
+ imgctx.fillRect(0, 0, 1, 1);
+ return image;
+}
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ assert_true(ctx.imageSmoothingEnabled);
+}, "When the context is created, imageSmoothingEnabled must be set to true.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = false;
+ assert_false(ctx.imageSmoothingEnabled);
+}, "On getting imageSmoothingEnabled, the user agent must return the last value it was set to.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing is actually on by default.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = true;
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing works when imageSmoothingEnabled is set to true");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with drawImage().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.fillRect(0, 0, 10, 10);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fillRect and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.scale(10, 10);
+ ctx.rect(0, 0, 10, 10);
+ ctx.fill();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fill() and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.strokeStyle = ctx.createPattern(image, 'repeat');
+ ctx.lineWidth = 5;
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.stroke();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with stroke() and createPattern().");
+
+test(function() {
+ var repaints = 5;
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+
+ function draw() {
+ ctx.clearRect(0, 0, 10, 10);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+ }
+
+ while (repaints > 0) {
+ draw();
+ repaints = repaints - 1;
+ }
+
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) still works after repaints.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js
new file mode 100644
index 0000000000..4c37e84b1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js
@@ -0,0 +1,126 @@
+// spec link: https://html.spec.whatwg.org/#the-offscreen-2d-rendering-context
+
+importScripts("/resources/testharness.js");
+
+function createTestImage() {
+ var image = new OffscreenCanvas(100, 50);
+ var imgctx = image.getContext('2d');
+ imgctx.fillStyle = "#F00";
+ imgctx.fillRect(0, 0, 2, 2);
+ imgctx.fillStyle = "#0F0";
+ imgctx.fillRect(0, 0, 1, 1);
+ return image;
+}
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ assert_true(ctx.imageSmoothingEnabled);
+}, "When the context is created, imageSmoothingEnabled must be set to true.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = false;
+ assert_false(ctx.imageSmoothingEnabled);
+}, "On getting imageSmoothingEnabled, the user agent must return the last value it was set to.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing is actually on by default.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = true;
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing works when imageSmoothingEnabled is set to true");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with drawImage().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.fillRect(0, 0, 10, 10);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fillRect and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.scale(10, 10);
+ ctx.rect(0, 0, 10, 10);
+ ctx.fill();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fill() and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.strokeStyle = ctx.createPattern(image, 'repeat');
+ ctx.lineWidth = 5;
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.stroke();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with stroke() and createPattern().");
+
+test(function() {
+ var repaints = 5;
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+
+ function draw() {
+ ctx.clearRect(0, 0, 10, 10);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+ }
+
+ while (repaints > 0) {
+ draw();
+ repaints = repaints - 1;
+ }
+
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) still works after repaints.");
+
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html
new file mode 100644
index 0000000000..873869ea72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html
new file mode 100644
index 0000000000..4fee600d6c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-layers-expected.html">
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 200) {
+ requestAnimationFrame(draw);
+ } else {
+ document.documentElement.classList.remove("reftest-wait");
+ }
+ }
+ requestAnimationFrame(draw);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html
new file mode 100644
index 0000000000..a0014bb597
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-layers-expected.html">
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = msg => {
+ const offscreen = msg.data.canvas;
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ self.postMessage('setup ready');
+ }
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ worker.addEventListener('message', msg => {
+ if(msg.data == 'setup ready') {
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 1) {
+ document.documentElement.classList.remove("reftest-wait");
+ } else {
+ requestAnimationFrame(draw);
+ }
+ }
+ requestAnimationFrame(draw);
+ }
+ });
+ worker.postMessage({
+ canvas: offscreen
+ }, [offscreen]);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html
new file mode 100644
index 0000000000..8557441f7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,255,1)';
+ ctx.fillRect(60,60,75,50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html
new file mode 100644
index 0000000000..c6925beb74
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-nested-layers-expected.html">
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ // Missing ctx.endLayer() here.
+
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 200) {
+ requestAnimationFrame(draw);
+ } else {
+ document.documentElement.classList.remove("reftest-wait");
+ }
+ }
+ requestAnimationFrame(draw);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html
new file mode 100644
index 0000000000..1a1dc54052
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-nested-layers-expected.html">
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = msg => {
+ const offscreen = msg.data.canvas;
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ // Missing ctx.endLayer() here.
+
+ self.postMessage('setup ready');
+ }
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ worker.addEventListener('message', msg => {
+ if(msg.data == 'setup ready') {
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 1) {
+ document.documentElement.classList.remove("reftest-wait");
+ } else {
+ requestAnimationFrame(draw);
+ }
+ }
+ requestAnimationFrame(draw);
+ }
+ });
+ worker.postMessage({
+ canvas: offscreen
+ }, [offscreen]);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html
new file mode 100644
index 0000000000..1baf7847de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html class="test-wait reftest-wait">
+<script>
+ let url = URL.createObjectURL(new Blob([`
+ let font = new FontFace('Ahem', 'url(/fonts/Ahem.ttf)');
+ self.fonts.add(font);
+ let canvas = new OffscreenCanvas(100, 100);
+ let ctx = canvas.getContext('2d');
+ ctx.font = "10px Ahem";
+ ctx.fillText('Hello', 0, 10);
+ postMessage('done');
+ `], { type: "application/javascript" }));
+ var worker = new Worker(url);
+ worker.onmessage = function() {
+ worker.terminate();
+ URL.revokeObjectURL(url);
+ document.documentElement.className = "";
+ };
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html
new file mode 100644
index 0000000000..a2ad1dcc7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas">
+<script>
+
+test(function() {
+ assert_throws_js(
+ TypeError,
+ () => OffscreenCanvas(100, 50),
+ "Calling OffscreenCanvas constructor without 'new' must throw"
+ );
+}, "OffscreenCanvas constructor called as normal function");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ assert_equals(offscreenCanvas.width, 100);
+ assert_equals(offscreenCanvas.height, 50);
+
+ offscreenCanvas.width = 50;
+ offscreenCanvas.height = 100;
+ assert_equals(offscreenCanvas.width, 50);
+ assert_equals(offscreenCanvas.height, 100);
+}, "Test that calling OffscreenCanvas's constructor generates correct width and height.");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+
+ offscreenCanvas1.width = null;
+ offscreenCanvas1.height = null;
+ assert_equals(offscreenCanvas1.width, 0);
+ assert_equals(offscreenCanvas1.height, 0);
+
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(-1, -1); });
+
+ var offscreenCanvas2 = new OffscreenCanvas(null, null);
+ assert_equals(offscreenCanvas2.width, 0);
+ assert_equals(offscreenCanvas2.height, 0);
+
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = -1; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = -1; });
+
+ var obj = {Name: "John Doe", Age: 30};
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = obj; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = obj; });
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(obj, obj); });
+}, "Test that OffscreenCanvas constructor handles invalid arguments correctly");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js
new file mode 100644
index 0000000000..72cfb728f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js
@@ -0,0 +1,45 @@
+// spec link: https://html.spec.whatwg.org/#dom-offscreencanvas
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t1 = async_test("Test that calling OffscreenCanvas's constructor generates correct width and height.");
+t1.step(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ assert_equals(offscreenCanvas.width, 100);
+ assert_equals(offscreenCanvas.height, 50);
+
+ offscreenCanvas.width = 50;
+ offscreenCanvas.height = 100;
+ assert_equals(offscreenCanvas.width, 50);
+ assert_equals(offscreenCanvas.height, 100);
+ t1.done();
+});
+
+var t2 = async_test("Test that OffscreenCanvas constructor handles invalid arguments correctly in a worker");
+t2.step(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+
+ offscreenCanvas1.width = null;
+ offscreenCanvas1.height = null;
+ assert_equals(offscreenCanvas1.width, 0);
+ assert_equals(offscreenCanvas1.height, 0);
+
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(-1, -1); });
+
+ var offscreenCanvas2 = new OffscreenCanvas(null, null);
+ assert_equals(offscreenCanvas2.width, 0);
+ assert_equals(offscreenCanvas2.height, 0);
+
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = -1; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = -1; });
+
+ var obj = {Name: "John Doe", Age: 30};
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = obj; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = obj; });
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(obj, obj); });
+ t2.done();
+});
+
+done();
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html
new file mode 100644
index 0000000000..51c167ddca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-getcontext">
+<script>
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 1);
+ assert_throws_js(TypeError, function() { offscreenCanvas.getContext('3d'); });
+}, "Test that getContext with un-supported string throws a TypeError.");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ assert_true(ctx1 instanceof OffscreenCanvasRenderingContext2D);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx2 = offscreenCanvas2.getContext('webgl');
+ assert_true(ctx2 instanceof WebGLRenderingContext);
+
+ var offscreenCanvas3 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas3.getContext('webgl2');
+ assert_true(ctx3 instanceof WebGL2RenderingContext);
+}, "Test that getContext with supported string returns correct results");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ var ctx2 = offscreenCanvas1.getContext('webgl');
+ assert_equals(ctx2, null);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas2.getContext('webgl');
+ var ctx4 = offscreenCanvas2.getContext('2d');
+ assert_equals(ctx4, null);
+}, "Test that getContext twice with different context type returns null the second time");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('2d');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that 2dcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('webgl');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that webglcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: false});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,127,0,255, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha disabled makes the OffscreenCanvas opaque");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: true});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha enabled preserves the alpha");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that 'alpha' context creation attribute is true by default");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js
new file mode 100644
index 0000000000..c413791f61
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js
@@ -0,0 +1,77 @@
+// spec link: https://html.spec.whatwg.org/#dom-offscreencanvas-getcontext
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 1);
+ assert_throws_js(TypeError, function() { offscreenCanvas.getContext('3d'); });
+}, "Test that getContext with un-supported string throws a TypeError.");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ assert_true(ctx1 instanceof OffscreenCanvasRenderingContext2D);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx2 = offscreenCanvas2.getContext('webgl');
+ assert_true(ctx2 instanceof WebGLRenderingContext);
+
+ var offscreenCanvas3 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas3.getContext('webgl2');
+ assert_true(ctx3 instanceof WebGL2RenderingContext);
+}, "Test that getContext with supported string returns correct results");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ var ctx2 = offscreenCanvas1.getContext('webgl');
+ assert_equals(ctx2, null);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas2.getContext('webgl');
+ var ctx4 = offscreenCanvas2.getContext('2d');
+ assert_equals(ctx4, null);
+}, "Test that getContext twice with different context type returns null the second time");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('2d');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that 2dcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('webgl');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that webglcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: false});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,127,0,255, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha disabled makes the OffscreenCanvas opaque");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: true});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha enabled preserves the alpha");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that 'alpha' context creation attribute is true by default");
+
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html
new file mode 100644
index 0000000000..3ff0eea153
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html
@@ -0,0 +1,240 @@
+<!DOCTYPE html>
+<title>Test resizing an OffscreenCanvas with a 2d context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<body></body>
+<script>
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ canvas.width = 30;
+ canvas.height = 40;
+ assert_equals(canvas.width, 30);
+ assert_equals(canvas.height, 40);
+}, "Verify that writing to the width and height attributes of an OffscreenCanvas works when there is no context attached.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ canvas.getContext('2d');
+ canvas.width = 30;
+ canvas.height = 40;
+ assert_equals(canvas.width, 30);
+ assert_equals(canvas.height, 40);
+ var image = canvas.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+}, "Verify that writing to the width and height attributes of an OffscreenCanvas works when there is a 2d context attached.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ canvas.getContext('webgl');
+ canvas.width = 30;
+ canvas.height = 40;
+ assert_equals(canvas.width, 30);
+ assert_equals(canvas.height, 40);
+ var image = canvas.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+}, "Verify that writing to the width and height attributes of an OffscreenCanvas works when there is a webgl context attached.");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 2;
+ placeholder.height = 2;
+ var offscreen = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", () => { placeholder.width = 1; });
+ assert_throws_dom("InvalidStateError", () => { placeholder.height = 1; });
+}, "Verify that writing to the width or height attribute of a placeholder canvas throws an exception");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 1;
+ placeholder.height = 1;
+ var offscreen = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", () => { placeholder.width = 1; });
+ assert_throws_dom("InvalidStateError", () => { placeholder.height = 1; });
+}, "Verify that writing to the width or height attribute of a placeholder canvas throws an exception even when not changing the value of the attribute.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ var ctx = canvas.getContext('2d');
+ ctx.lineWidth = 5;
+ canvas.width = 30;
+ assert_equals(ctx.lineWidth, 1);
+ ctx.lineWidth = 5;
+ canvas.height = 40;
+ assert_equals(ctx.lineWidth, 1);
+}, "Verify that resizing a 2d context resets its state.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ var ctx = canvas.getContext('2d');
+ ctx.lineWidth = 5;
+ canvas.width = canvas.width;
+ assert_equals(ctx.lineWidth, 1);
+ ctx.lineWidth = 5;
+ canvas.height = canvas.height;
+ assert_equals(ctx.lineWidth, 1);
+}, "Verify that setting the size of a 2d context to the same size it already had resets its state.");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ document.body.appendChild(placeholder); // So that we can check computed style/
+ placeholder.width = 10;
+ placeholder.height = 20;
+ var offscreen = placeholder.transferControlToOffscreen();
+ var ctx = offscreen.getContext('2d');
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas that has a placeholder works.
+ offscreen.width = 30;
+ offscreen.height = 40;
+ assert_equals(offscreen.width, 30);
+ assert_equals(offscreen.height, 40);
+ var image = offscreen.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas does not directly update the size of its placeholder canvas.
+ assert_equals(placeholder.width, 10);
+ assert_equals(placeholder.height, 20);
+ });
+ var asyncStepsCompleted = 0;
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that the placeholder was not updated synchronously.
+ assert_equals(image.width, 10);
+ assert_equals(image.height, 20);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+
+ function testImage() {
+ if (placeholder.width != 30) {
+ requestAnimationFrame(testImage);
+ } else {
+ t.step(function() {
+ assert_equals(placeholder.width, 30);
+ assert_equals(placeholder.height, 40);
+ var computedStyle = window.getComputedStyle(placeholder);
+ assert_equals(computedStyle.getPropertyValue('width'), "30px");
+ assert_equals(computedStyle.getPropertyValue('height'), "40px");
+ });
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that an image grabbed from the placeholder has the correct dimensions
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+ }
+ }
+
+ requestAnimationFrame(testImage);
+}, "Verify that resizing an OffscreenCanvas with a 2d context propagates the new size to its placeholder canvas asynchronously.");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ document.body.appendChild(placeholder); // So that we can check computed style/
+ placeholder.width = 10;
+ placeholder.height = 20;
+ var offscreen = placeholder.transferControlToOffscreen();
+ var ctx = offscreen.getContext('webgl');
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas that has a placeholder works.
+ offscreen.width = 30;
+ offscreen.height = 40;
+ assert_equals(offscreen.width, 30);
+ assert_equals(offscreen.height, 40);
+ var image = offscreen.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas does not directly update the size of its placeholder canvas.
+ assert_equals(placeholder.width, 10);
+ assert_equals(placeholder.height, 20);
+ });
+ var asyncStepsCompleted = 0;
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that the placeholder was not updated synchronously.
+ assert_equals(image.width, 10);
+ assert_equals(image.height, 20);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+
+ function testImage() {
+ if (placeholder.width != 30) {
+ requestAnimationFrame(testImage);
+ } else {
+ t.step(function() {
+ assert_equals(placeholder.width, 30);
+ assert_equals(placeholder.height, 40);
+ var computedStyle = window.getComputedStyle(placeholder);
+ assert_equals(computedStyle.getPropertyValue('width'), "30px");
+ assert_equals(computedStyle.getPropertyValue('height'), "40px");
+ });
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that an image grabbed from the placeholder has the correct dimensions
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+ }
+ }
+ requestAnimationFrame(testImage);
+}, "Verify that resizing an OffscreenCanvas with a webgl context propagates the new size to its placeholder canvas asynchronously.");
+
+async_test(function(t){
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 1;
+ placeholder.height = 1;
+ var offscreen = placeholder.transferControlToOffscreen();
+ var ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 10;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 10, 10);
+
+ var testCanvas = document.createElement('canvas');
+ testCanvas.width = testCanvas.height = 20;
+ var testCtx = testCanvas.getContext('2d');
+
+ function testImage() {
+ if (placeholder.width != 10) {
+ requestAnimationFrame(testImage);
+ } else {
+ testCtx.drawImage(placeholder, 0, 0);
+ var pixel1 = testCtx.getImageData(9, 9, 1, 1).data;
+ var pixel2 = testCtx.getImageData(9, 10, 1, 1).data;
+ var pixel3 = testCtx.getImageData(10, 9, 1, 1).data;
+ t.step(function() {
+ assert_equals(placeholder.width, 10);
+ assert_equals(placeholder.height, 10);
+ assert_array_equals(pixel1, [0, 255, 0, 255]);
+ assert_array_equals(pixel2, [0, 0, 0, 0]);
+ assert_array_equals(pixel3, [0, 0, 0, 0]);
+ });
+ t.done();
+ }
+ }
+
+ requestAnimationFrame(testImage);
+}, "Verify that drawImage uses the size of the frame as the intinsic size of a placeholder canvas.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html
new file mode 100644
index 0000000000..1960841564
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html
@@ -0,0 +1,12 @@
+<title>Transfer Low-Latency Canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<body></body>
+
+<script>
+test(function(t) {
+ var ctx = document.createElement("canvas").getContext('2d', {desynchronized:true});
+ assert_throws_dom("InvalidStateError", () => { ctx.canvas.transferControlToOffscreen(); });
+}, "Tests that transferring a low latency canvas does not cause a crash. See crbug.com/1255153");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html
new file mode 100644
index 0000000000..6e1adf9591
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-transfertoimagebitmap">
+
+<script id="myWorker" type="text/worker">
+
+self.onmessage = function(e) {
+};
+
+</script>
+
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+test(function() {
+ function testSize(contextType) {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext(contextType);
+ var image = offscreenCanvas.transferToImageBitmap();
+ assert_equals(image.width, 100);
+ assert_equals(image.height, 50);
+ }
+
+ testSize('2d');
+ testSize('webgl');
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct width and height");
+
+test(function() {
+ function testImageBitmapClr(shouldCallTwice, alphaVal) {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: alphaVal});
+ ctx.fillStyle = "#0f0";
+ ctx.fillRect(0, 0, 100, 50);
+ var image = offscreenCanvas.transferToImageBitmap();
+
+ if (shouldCallTwice)
+ image = offscreenCanvas.transferToImageBitmap();
+
+ var drawCanvas = document.createElement('canvas');
+ drawCanvas.width = 100;
+ drawCanvas.height = 50;
+ var dCtx = drawCanvas.getContext('2d');
+ dCtx.drawImage(image, 0, 0);
+
+ if (shouldCallTwice) {
+ if (alphaVal)
+ _assertPixel(drawCanvas, 50,25, 0,0,0,0);
+ else
+ _assertPixel(drawCanvas, 50,25, 0,0,0,255);
+ } else {
+ _assertPixel(drawCanvas, 50,25, 0,255,0,255);
+ }
+ }
+
+ testImageBitmapClr(false, true);
+ testImageBitmapClr(true, true);
+ testImageBitmapClr(true, false);
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct color");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.lineWidth = 10;
+ var image = offscreenCanvas.transferToImageBitmap();
+ assert_equals(ctx.lineWidth, 10);
+}, "Test that transferToImageBitmap won't change context's property");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.rect(0, 0, 25, 50);
+ ctx.clip();
+ ctx.translate(20, 20);
+
+ ctx.fillStyle = '#0f0';
+ var image1 = offscreenCanvas.transferToImageBitmap();
+ // trasnform should be preserved
+ ctx.fillRect(0, 0, 10, 10);
+ var image2 = offscreenCanvas.transferToImageBitmap();
+
+ var drawCanvas = document.createElement('canvas');
+ drawCanvas.width = 100;
+ drawCanvas.height = 50;
+ var dCtx = drawCanvas.getContext('2d');
+ dCtx.drawImage(image2, 0, 0);
+ // Verify that transform was carried over.
+ _assertPixel(drawCanvas, 23,25, 0,255,0,255);
+ // Verify that clip was carried over.
+ _assertPixel(drawCanvas, 27,25, 0,0,0,0);
+}, "Test that transferToImageBitmap preserves transform");
+
+async_test(function(t) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage(offscreenCanvas, [offscreenCanvas]);
+ assert_throws_dom("InvalidStateError", function() { offscreenCanvas.transferToImageBitmap(); });
+ t.done();
+}, "Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ assert_throws_dom("InvalidStateError", function() { offscreenCanvas.transferToImageBitmap(); });
+}, "Test that transferToImageBitmap without a context throws an exception");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html
new file mode 100644
index 0000000000..09a388ed35
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<body>
+ <p>Tests that an ImageBitmap in Offscreen without content does not crash.</p>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+async_test(function(t) {
+ var v1 = document.createElement("canvas");
+ var v2 = v1.transferControlToOffscreen();
+ var v3 = v2.getContext("bitmaprenderer");
+ v2.width = 67;
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html
new file mode 100644
index 0000000000..b51ce0efa2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html
@@ -0,0 +1,201 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-transfertoimagebitmap">
+
+<script id="myWorker" type="text/worker">
+
+function testSize(contextType)
+{
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext(contextType);
+ var image = offscreenCanvas.transferToImageBitmap();
+ if (image.width == 100 && image.height == 50)
+ return true;
+ return false;
+}
+
+function testImageBitmapClr(shouldCallTwice, alphaVal) {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: alphaVal});
+ ctx.fillStyle = "#0f0";
+ ctx.fillRect(0, 0, 100, 50);
+ var image = offscreenCanvas.transferToImageBitmap();
+
+ if (shouldCallTwice)
+ image = offscreenCanvas.transferToImageBitmap();
+ return image;
+}
+
+function isInvalidStateError(funcStr, offscreenCanvas)
+{
+ try {
+ eval(funcStr);
+ } catch (e) {
+ if (e instanceof DOMException && e.name == "InvalidStateError")
+ return true;
+ return false;
+ }
+}
+
+function testImageBitmapSize()
+{
+ return testSize('2d') && testSize('webgl');
+}
+
+function testLineWidthNotAltered()
+{
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.lineWidth = 10;
+ var image = offscreenCanvas.transferToImageBitmap();
+ return ctx.lineWidth == 10;
+}
+
+function testTransformPreserved()
+{
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.rect(0, 0, 25, 50);
+ ctx.clip();
+ ctx.translate(20, 20);
+
+ ctx.fillStyle = '#0f0';
+ var image1 = offscreenCanvas.transferToImageBitmap();
+ // trasnform should be preserved
+ ctx.fillRect(0, 0, 10, 10);
+ var image2 = offscreenCanvas.transferToImageBitmap();
+ return image2;
+}
+
+function testException()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ return isInvalidStateError("offscreenCanvas.transferToImageBitmap()", offscreenCanvas);
+}
+
+self.onmessage = function(e) {
+ switch (e.data) {
+ case 'test1':
+ self.postMessage(testImageBitmapSize());
+ break;
+ case 'test2':
+ self.postMessage(testImageBitmapClr(false, true));
+ break;
+ case 'test3':
+ self.postMessage(testImageBitmapClr(true, true));
+ break;
+ case 'test4':
+ self.postMessage(testImageBitmapClr(true, false));
+ break;
+ case 'test5':
+ self.postMessage(testLineWidthNotAltered());
+ break;
+ case 'test6':
+ self.postMessage(testTransformPreserved());
+ break;
+ case 'test7':
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage({offscreenCanvas: offscreenCanvas}, [offscreenCanvas]);
+ self.postMessage(isInvalidStateError("offscreenCanvas.transferToImageBitmap()", offscreenCanvas));
+ break;
+ case 'test8':
+ self.postMessage(testException());
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(test) {
+ var blob = new Blob([document.getElementById("myWorker").textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onerror = test.unreached_func("error");
+ return worker;
+}
+
+function drawImageBitmap(image, x, y)
+{
+ var drawCanvas = document.createElement('canvas');
+ drawCanvas.width = 100;
+ drawCanvas.height = 50;
+ var dCtx = drawCanvas.getContext('2d');
+ dCtx.drawImage(image, 0, 0);
+ return dCtx.getImageData(x, y, 1, 1).data;
+}
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test1');
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct width and height in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr = drawImageBitmap(msg.data, 50, 25);
+ assert_array_equals(clr, [0, 255, 0, 255]);
+ }));
+ worker.postMessage('test2');
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct color in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr = drawImageBitmap(msg.data, 50, 25);
+ assert_array_equals(clr, [0, 0, 0, 0]);
+ }));
+ worker.postMessage('test3');
+}, "Test that call transferToImageBitmap twice returns an ImageBitmap with correct color in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr = drawImageBitmap(msg.data, 50, 25);
+ assert_array_equals(clr, [0, 0, 0, 255]);
+ }));
+ worker.postMessage('test4');
+}, "Test that call transferToImageBitmap twice on a alpha-disabled context returns an ImageBitmap with correct color in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test5');
+}, "Test that transferToImageBitmap won't change context's property in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr1 = drawImageBitmap(msg.data, 23, 25);
+ assert_array_equals(clr1, [0, 255, 0, 255]);
+ var clr2 = drawImageBitmap(msg.data, 27, 25);
+ assert_array_equals(clr2, [0, 0, 0, 0]);
+ }));
+ worker.postMessage('test6');
+}, "Test that call transferToImageBitmap preserves transform in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test7');
+}, "Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test8');
+}, "Test that call transferToImageBitmap without a context throws an exception in a worker");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html
new file mode 100644
index 0000000000..fea375b2ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-canvas-transfercontroltooffscreen">
+<script>
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ assert_equals(offscreenCanvas.width, 100);
+ assert_equals(offscreenCanvas.height, 50);
+}, "Test that an OffscreenCanvas generated by transferControlToOffscreen gets correct width and height");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", function() { placeholder.getContext('2d'); });
+}, "Test that calling getContext on a placeholder canvas that has already transferred its control throws an exception");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", function() { placeholder.transferControlToOffscreen(); });
+}, "Test that calling transferControlToOffscreen twice throws an exception");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html
new file mode 100644
index 0000000000..1347e7b50b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-canvas-transfercontroltooffscreen">
+
+<script id="myWorker" type="text/worker">
+
+function testSize(offscreenCanvas)
+{
+ if (offscreenCanvas.width == 100 && offscreenCanvas.height == 50)
+ return true;
+ return false;
+}
+
+self.onmessage = function(e) {
+ switch (e.data.msg) {
+ case 'test1':
+ self.postMessage(testSize(e.data.data));
+ break;
+ case 'test2':
+ self.postMessage("");
+ break;
+ case 'test3':
+ self.postMessage("");
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage({msg: 'test1', data: offscreenCanvas}, [offscreenCanvas]);
+}, "Test that an OffscreenCanvas generated by transferControlToOffscreen gets correct width and height when it is transferred to a worker");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_throws_dom("InvalidStateError", function() { placeholder.getContext('2d'); });
+ }));
+ worker.postMessage({msg: 'test2', data: offscreenCanvas}, [offscreenCanvas]);
+}, "Test that calling getContext on a placeholder canvas that is transferred its control to an OffscreenCanvas throws an exception, when the OffscreenCanvas is transferred to a worker");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_throws_dom("InvalidStateError", function() { placeholder.transferControlToOffscreen(); });
+ }));
+ worker.postMessage({msg: 'test3', data: offscreenCanvas}, [offscreenCanvas]);
+}, "Test that calling transferControlToOffscreen twice throws an exception, when its associated OffscreenCanvas is transferred to a worker");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html
new file mode 100644
index 0000000000..76796034e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#offscreencanvas">
+
+<script id="myWorker" type="text/worker">
+
+function test1(offscreenCanvas)
+{
+ return offscreenCanvas.width == 10 && offscreenCanvas.height == 10;
+}
+
+self.onmessage = function(e) {
+ switch(e.data.msg) {
+ case 'test1':
+ self.postMessage(test1(e.data.data));
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+async_test(function(t) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({msg: 'test1', data: offscreenCanvas}, [offscreenCanvas]);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ assert_equals(offscreenCanvas.width, 0);
+ assert_equals(offscreenCanvas.height, 0);
+}, "Test that offscreenCanvas's size is correct after being transferred to a worker.");
+
+
+test(function() {
+ function testException(contextType) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext(contextType);
+ assert_throws_dom("InvalidStateError", function() {
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ });
+ }
+ testException('2d');
+}, "Test that transfer an OffscreenCanvas that already have a 2d context throws exception.");
+
+test(function() {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ assert_throws_dom("DataCloneError", function() {
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ });
+}, "Test that transfer an OffscreenCanvas twice throws exception.");
+
+test(function() {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ assert_throws_dom("InvalidStateError", function() {
+ offscreenCanvas.getContext('2d');
+ });
+}, "Test that calling getContext('2d') on a detached OffscreenCanvas throws exception.");
+
+test(function() {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ assert_throws_dom("InvalidStateError", function() {
+ offscreenCanvas.getContext('webgl');
+ });
+}, "Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception.");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html
new file mode 100644
index 0000000000..38f981e8f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#offscreencanvas">
+
+<script id="myWorker" type="text/worker">
+
+function isInvalidStateError(funcStr, offscreenCanvas)
+{
+ try {
+ eval(funcStr);
+ } catch (e) {
+ if (e instanceof DOMException && e.name == "InvalidStateError")
+ return true;
+ return false;
+ }
+}
+
+function testExceptionWith2DContext()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d');
+ return isInvalidStateError("self.postMessage(offscreenCanvas, [offscreenCanvas])", offscreenCanvas);
+}
+
+function testExceptionWithWebGLContext()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('webgl');
+ return isInvalidStateError("self.postMessage(offscreenCanvas, [offscreenCanvas])", offscreenCanvas);
+}
+
+function testExceptionWithDetachedOffscreenCanvas1()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ return isInvalidStateError("self.postMessage(offscreenCanvas, [offscreenCanvas])", offscreenCanvas);
+}
+
+function testExceptionWithDetachedOffscreenCanvas2()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ return isInvalidStateError("offscreenCanvas.getContext('2d')", offscreenCanvas);
+}
+
+function testExceptionWithDetachedOffscreenCanvas3()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ return isInvalidStateError("offscreenCanvas.getContext('webgl')", offscreenCanvas);
+}
+
+self.onmessage = function(e) {
+ switch(e.data) {
+ case 'test1':
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ break;
+ case 'test2':
+ self.postMessage(testExceptionWith2DContext());
+ break;
+ case 'test3':
+ self.postMessage(testExceptionWithWebGLContext());
+ break;
+ case 'test4':
+ self.postMessage(testExceptionWithDetachedOffscreenCanvas1());
+ break;
+ case 'test5':
+ self.postMessage(testExceptionWithDetachedOffscreenCanvas2());
+ break;
+ case 'test6':
+ self.postMessage(testExceptionWithDetachedOffscreenCanvas3());
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(test) {
+ var blob = new Blob([document.getElementById("myWorker").textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onerror = test.unreached_func("error");
+ return worker;
+}
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_equals(msg.data.width, 10);
+ assert_equals(msg.data.height, 10);
+ }));
+ worker.postMessage('test1');
+}, "Test that OffscreenCanvas's size is correct after being transferred from a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test2');
+}, "Test that transfer an OffscreenCanvas that has a 2d context throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test3');
+}, "Test that transfer an OffscreenCanvas that has a webgl context throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test4');
+}, "Test that transfer an OffscreenCanvas twice throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test5');
+}, "Test that calling getContext('2d') on a detached OffscreenCanvas throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test6');
+}, "Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception in a worker.");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html b/testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html
new file mode 100644
index 0000000000..b3b70ac208
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html
@@ -0,0 +1,40 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Ensure that context2d.getTransform works
+const epsilon = 1e-5;
+const canvas = new OffscreenCanvas(300, 150);
+const ctx = canvas.getContext('2d');
+
+test(function(t) {
+
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that an untransformed matrix is identity");
+
+ ctx.scale(2, 3);
+ transform = ctx.getTransform();
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that context2d scaling works");
+
+ ctx.rotate(Math.PI/2);
+ transform = ctx.getTransform();
+ assert_array_approx_equals(ctx.getTransform().toFloat32Array(),
+ [0, 3, 0, 0, -2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], epsilon,
+ "Assert that context2d rotate works");
+
+ ctx.translate(1, -1);
+ transform = ctx.getTransform();
+ assert_array_approx_equals(ctx.getTransform().toFloat32Array(),
+ [0, 3, 0, 0, -2, 0, 0, 0, 0, 0, 1, 0, 2, 3, 0, 1], epsilon,
+ "Assert context2d translate works.");
+
+ ctx.resetTransform();
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that a reset matrix is identity");
+}, 'This test ensures that getTransform works correctly.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html b/testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html
new file mode 100644
index 0000000000..9fc63a9f49
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>OffscreenCanvas test: 2d.color.space.p3.convertToBlob.p3.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.color.space.p3.convertToBlob.p3.canvas</h1>
+<p class="desc">test if toblob returns p3 data from p3 color space canvas</p>
+
+
+<script>
+var t = async_test("test if toblob returns p3 data from p3 color space canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var offscreenCanvas = new OffscreenCanvas(100, 50);
+var ctx = offscreenCanvas.getContext('2d', {colorSpace: "display-p3"});
+
+ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ctx.fillRect(0, 0, 1, 1);
+ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ctx.fillRect(1, 0, 1, 1);
+ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ctx.fillRect(0, 1, 1, 1);
+ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ctx.fillRect(1, 1, 1, 1);
+expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+var image = new Image();
+image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+});
+
+offscreenCanvas.convertToBlob(function(blob) {
+ var urlCreator = window.URL || window.webkitURL;
+ image.src = urlCreator.createObjectURL(blob);
+}, 'image/png', 1);
+t.done()
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.html
new file mode 100644
index 0000000000..bdd2c7a65f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.angle.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.angle.1</h1>
+<p class="desc">arc() draws pi/2 .. -pi anticlockwise correctly</p>
+
+
+<script>
+var t = async_test("arc() draws pi/2 .. -pi anticlockwise correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.worker.js
new file mode 100644
index 0000000000..1b6a534c23
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.angle.1
+// Description:arc() draws pi/2 .. -pi anticlockwise correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws pi/2 .. -pi anticlockwise correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.html
new file mode 100644
index 0000000000..c92e83abd7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.angle.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.angle.2</h1>
+<p class="desc">arc() draws -3pi/2 .. -pi anticlockwise correctly</p>
+
+
+<script>
+var t = async_test("arc() draws -3pi/2 .. -pi anticlockwise correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -3*Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.worker.js
new file mode 100644
index 0000000000..25598c959d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.2.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.angle.2
+// Description:arc() draws -3pi/2 .. -pi anticlockwise correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws -3pi/2 .. -pi anticlockwise correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -3*Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.html
new file mode 100644
index 0000000000..86170ac8c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.angle.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.angle.3</h1>
+<p class="desc">arc() wraps angles mod 2pi when anticlockwise and end > start+2pi</p>
+
+
+<script>
+var t = async_test("arc() wraps angles mod 2pi when anticlockwise and end > start+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (512+1/2)*Math.PI, (1024-1)*Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.worker.js
new file mode 100644
index 0000000000..6841041d78
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.3.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.angle.3
+// Description:arc() wraps angles mod 2pi when anticlockwise and end > start+2pi
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() wraps angles mod 2pi when anticlockwise and end > start+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (512+1/2)*Math.PI, (1024-1)*Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.html
new file mode 100644
index 0000000000..e694b99f70
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.angle.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.angle.4</h1>
+<p class="desc">arc() draws a full circle when clockwise and end > start+2pi</p>
+
+
+<script>
+var t = async_test("arc() draws a full circle when clockwise and end > start+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (512+1/2)*Math.PI, (1024-1)*Math.PI, false);
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.worker.js
new file mode 100644
index 0000000000..502175165f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.4.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.angle.4
+// Description:arc() draws a full circle when clockwise and end > start+2pi
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws a full circle when clockwise and end > start+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (512+1/2)*Math.PI, (1024-1)*Math.PI, false);
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.html
new file mode 100644
index 0000000000..7f0d9af19f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.angle.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.angle.5</h1>
+<p class="desc">arc() wraps angles mod 2pi when clockwise and start > end+2pi</p>
+
+
+<script>
+var t = async_test("arc() wraps angles mod 2pi when clockwise and start > end+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (1024-1)*Math.PI, (512+1/2)*Math.PI, false);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.worker.js
new file mode 100644
index 0000000000..aa2320a92a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.5.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.angle.5
+// Description:arc() wraps angles mod 2pi when clockwise and start > end+2pi
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() wraps angles mod 2pi when clockwise and start > end+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (1024-1)*Math.PI, (512+1/2)*Math.PI, false);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.html
new file mode 100644
index 0000000000..64cf6daeef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.angle.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.angle.6</h1>
+<p class="desc">arc() draws a full circle when anticlockwise and start > end+2pi</p>
+
+
+<script>
+var t = async_test("arc() draws a full circle when anticlockwise and start > end+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (1024-1)*Math.PI, (512+1/2)*Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.worker.js
new file mode 100644
index 0000000000..929db397d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.angle.6.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.angle.6
+// Description:arc() draws a full circle when anticlockwise and start > end+2pi
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws a full circle when anticlockwise and start > end+2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (1024-1)*Math.PI, (512+1/2)*Math.PI, true);
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.html
new file mode 100644
index 0000000000..ee42c04085
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.default</h1>
+<p class="desc">arc() with missing last argument defaults to clockwise</p>
+
+
+<script>
+var t = async_test("arc() with missing last argument defaults to clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -Math.PI, Math.PI/2);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.worker.js
new file mode 100644
index 0000000000..da74713e4a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.default.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.default
+// Description:arc() with missing last argument defaults to clockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with missing last argument defaults to clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -Math.PI, Math.PI/2);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.html
new file mode 100644
index 0000000000..25414647b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.empty</h1>
+<p class="desc">arc() with an empty path does not draw a straight line to the start point</p>
+
+
+<script>
+var t = async_test("arc() with an empty path does not draw a straight line to the start point");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.worker.js
new file mode 100644
index 0000000000..9b1fbb1a38
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.empty.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.empty
+// Description:arc() with an empty path does not draw a straight line to the start point
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with an empty path does not draw a straight line to the start point");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.html
new file mode 100644
index 0000000000..b8459053f2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.end</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.end</h1>
+<p class="desc">arc() adds the end point of the arc to the subpath</p>
+
+
+<script>
+var t = async_test("arc() adds the end point of the arc to the subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(-100, 0);
+ ctx.arc(-100, 0, 25, -Math.PI/2, Math.PI/2, true);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.worker.js
new file mode 100644
index 0000000000..963640a273
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.end.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.end
+// Description:arc() adds the end point of the arc to the subpath
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() adds the end point of the arc to the subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(-100, 0);
+ ctx.arc(-100, 0, 25, -Math.PI/2, Math.PI/2, true);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.html
new file mode 100644
index 0000000000..6c81b9e0de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.negative</h1>
+<p class="desc">arc() with negative radius throws INDEX_SIZE_ERR</p>
+
+
+<script>
+var t = async_test("arc() with negative radius throws INDEX_SIZE_ERR");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.arc(0, 0, -1, 0, 0, true); });
+ var path = new Path2D();
+ assert_throws_dom("INDEX_SIZE_ERR", function() { path.arc(10, 10, -5, 0, 1, false); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.worker.js
new file mode 100644
index 0000000000..b48eb98474
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.negative.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.negative
+// Description:arc() with negative radius throws INDEX_SIZE_ERR
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with negative radius throws INDEX_SIZE_ERR");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.arc(0, 0, -1, 0, 0, true); });
+ var path = new Path2D();
+ assert_throws_dom("INDEX_SIZE_ERR", function() { path.arc(10, 10, -5, 0, 1, false); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.html
new file mode 100644
index 0000000000..4d9ebe6d87
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.nonempty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.nonempty</h1>
+<p class="desc">arc() with a non-empty path does draw a straight line to the start point</p>
+
+
+<script>
+var t = async_test("arc() with a non-empty path does draw a straight line to the start point");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.worker.js
new file mode 100644
index 0000000000..af6b4c9853
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonempty.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.nonempty
+// Description:arc() with a non-empty path does draw a straight line to the start point
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with a non-empty path does draw a straight line to the start point");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.html
new file mode 100644
index 0000000000..b347a1e27a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.nonfinite</h1>
+<p class="desc">arc() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("arc() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.arc(Infinity, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(-Infinity, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(NaN, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, -Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, NaN, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, -Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, NaN, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, -Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, NaN, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, 0, Infinity, true);
+ ctx.arc(0, 0, 50, 0, -Infinity, true);
+ ctx.arc(0, 0, 50, 0, NaN, true);
+ ctx.arc(Infinity, Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, Infinity, Infinity, true);
+ ctx.arc(Infinity, Infinity, Infinity, 0, Infinity, true);
+ ctx.arc(Infinity, Infinity, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, 50, Infinity, Infinity, true);
+ ctx.arc(Infinity, Infinity, 50, 0, Infinity, true);
+ ctx.arc(Infinity, 0, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, Infinity, Infinity, Infinity, true);
+ ctx.arc(Infinity, 0, Infinity, 0, Infinity, true);
+ ctx.arc(Infinity, 0, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, 50, Infinity, Infinity, true);
+ ctx.arc(Infinity, 0, 50, 0, Infinity, true);
+ ctx.arc(0, Infinity, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, Infinity, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(0, Infinity, Infinity, Infinity, Infinity, true);
+ ctx.arc(0, Infinity, Infinity, 0, Infinity, true);
+ ctx.arc(0, Infinity, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(0, Infinity, 50, Infinity, Infinity, true);
+ ctx.arc(0, Infinity, 50, 0, Infinity, true);
+ ctx.arc(0, 0, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, Infinity, Infinity, Infinity, true);
+ ctx.arc(0, 0, Infinity, 0, Infinity, true);
+ ctx.arc(0, 0, 50, Infinity, Infinity, true);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.worker.js
new file mode 100644
index 0000000000..50ff560e34
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.nonfinite.worker.js
@@ -0,0 +1,72 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.nonfinite
+// Description:arc() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.arc(Infinity, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(-Infinity, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(NaN, 0, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, -Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, NaN, 50, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, -Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, NaN, 0, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, -Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, NaN, 2*Math.PI, true);
+ ctx.arc(0, 0, 50, 0, Infinity, true);
+ ctx.arc(0, 0, 50, 0, -Infinity, true);
+ ctx.arc(0, 0, 50, 0, NaN, true);
+ ctx.arc(Infinity, Infinity, 50, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, Infinity, Infinity, Infinity, true);
+ ctx.arc(Infinity, Infinity, Infinity, 0, Infinity, true);
+ ctx.arc(Infinity, Infinity, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, Infinity, 50, Infinity, Infinity, true);
+ ctx.arc(Infinity, Infinity, 50, 0, Infinity, true);
+ ctx.arc(Infinity, 0, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, Infinity, Infinity, Infinity, true);
+ ctx.arc(Infinity, 0, Infinity, 0, Infinity, true);
+ ctx.arc(Infinity, 0, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(Infinity, 0, 50, Infinity, Infinity, true);
+ ctx.arc(Infinity, 0, 50, 0, Infinity, true);
+ ctx.arc(0, Infinity, Infinity, 0, 2*Math.PI, true);
+ ctx.arc(0, Infinity, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(0, Infinity, Infinity, Infinity, Infinity, true);
+ ctx.arc(0, Infinity, Infinity, 0, Infinity, true);
+ ctx.arc(0, Infinity, 50, Infinity, 2*Math.PI, true);
+ ctx.arc(0, Infinity, 50, Infinity, Infinity, true);
+ ctx.arc(0, Infinity, 50, 0, Infinity, true);
+ ctx.arc(0, 0, Infinity, Infinity, 2*Math.PI, true);
+ ctx.arc(0, 0, Infinity, Infinity, Infinity, true);
+ ctx.arc(0, 0, Infinity, 0, Infinity, true);
+ ctx.arc(0, 0, 50, Infinity, Infinity, true);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.html
new file mode 100644
index 0000000000..bf8a6f1f91
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.scale.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.scale.1</h1>
+<p class="desc">Non-uniformly scaled arcs are the right shape</p>
+
+
+<script>
+var t = async_test("Non-uniformly scaled arcs are the right shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(2, 0.5);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(25, 50, 56, 0, 2*Math.PI, false);
+ ctx.fill();
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-25, 50);
+ ctx.arc(-25, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(75, 50);
+ ctx.arc(75, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, -25);
+ ctx.arc(25, -25, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, 125);
+ ctx.arc(25, 125, 24, 0, 2*Math.PI, false);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.worker.js
new file mode 100644
index 0000000000..5d9619a920
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.1.worker.js
@@ -0,0 +1,49 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.scale.1
+// Description:Non-uniformly scaled arcs are the right shape
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Non-uniformly scaled arcs are the right shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(2, 0.5);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(25, 50, 56, 0, 2*Math.PI, false);
+ ctx.fill();
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-25, 50);
+ ctx.arc(-25, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(75, 50);
+ ctx.arc(75, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, -25);
+ ctx.arc(25, -25, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, 125);
+ ctx.arc(25, 125, 24, 0, 2*Math.PI, false);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.html
new file mode 100644
index 0000000000..95376882cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.scale.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.scale.2</h1>
+<p class="desc">Highly scaled arcs are the right shape</p>
+
+
+<script>
+var t = async_test("Highly scaled arcs are the right shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(100, 100);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 1.2;
+ ctx.beginPath();
+ ctx.arc(0, 0, 0.6, 0, Math.PI/2, false);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.worker.js
new file mode 100644
index 0000000000..c655dad30f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.scale.2.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.scale.2
+// Description:Highly scaled arcs are the right shape
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Highly scaled arcs are the right shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(100, 100);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 1.2;
+ ctx.beginPath();
+ ctx.arc(0, 0, 0.6, 0, Math.PI/2, false);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.html
new file mode 100644
index 0000000000..624b307af7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.selfintersect.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.selfintersect.1</h1>
+<p class="desc">arc() with lineWidth > 2*radius is drawn sensibly</p>
+
+
+<script>
+var t = async_test("arc() with lineWidth > 2*radius is drawn sensibly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(100, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(0, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.worker.js
new file mode 100644
index 0000000000..6c77dbd5b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.1.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.selfintersect.1
+// Description:arc() with lineWidth > 2*radius is drawn sensibly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with lineWidth > 2*radius is drawn sensibly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(100, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(0, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.html
new file mode 100644
index 0000000000..b49aa2437a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.selfintersect.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.selfintersect.2</h1>
+<p class="desc">arc() with lineWidth > 2*radius is drawn sensibly</p>
+
+
+<script>
+var t = async_test("arc() with lineWidth > 2*radius is drawn sensibly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 180;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(100, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 97,1, 0,255,0,255);
+ _assertPixel(canvas, 97,2, 0,255,0,255);
+ _assertPixel(canvas, 97,3, 0,255,0,255);
+ _assertPixel(canvas, 2,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.worker.js
new file mode 100644
index 0000000000..2bbdacd46d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.selfintersect.2.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.selfintersect.2
+// Description:arc() with lineWidth > 2*radius is drawn sensibly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with lineWidth > 2*radius is drawn sensibly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 180;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(100, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 97,1, 0,255,0,255);
+ _assertPixel(canvas, 97,2, 0,255,0,255);
+ _assertPixel(canvas, 97,3, 0,255,0,255);
+ _assertPixel(canvas, 2,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.html
new file mode 100644
index 0000000000..ceb3cb380c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.shape.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.shape.1</h1>
+<p class="desc">arc() from 0 to pi does not draw anything in the wrong half</p>
+
+
+<script>
+var t = async_test("arc() from 0 to pi does not draw anything in the wrong half");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 20,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.worker.js
new file mode 100644
index 0000000000..e1058ae96d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.1.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.shape.1
+// Description:arc() from 0 to pi does not draw anything in the wrong half
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() from 0 to pi does not draw anything in the wrong half");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 20,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.html
new file mode 100644
index 0000000000..a39fd03947
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.shape.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.shape.2</h1>
+<p class="desc">arc() from 0 to pi draws stuff in the right half</p>
+
+
+<script>
+var t = async_test("arc() from 0 to pi draws stuff in the right half");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 20,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.worker.js
new file mode 100644
index 0000000000..d444d9b04e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.2.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.shape.2
+// Description:arc() from 0 to pi draws stuff in the right half
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() from 0 to pi draws stuff in the right half");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 20,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.html
new file mode 100644
index 0000000000..853814aef2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.shape.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.shape.3</h1>
+<p class="desc">arc() from 0 to -pi/2 does not draw anything in the wrong quadrant</p>
+
+
+<script>
+var t = async_test("arc() from 0 to -pi/2 does not draw anything in the wrong quadrant");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(0, 50, 50, 0, -Math.PI/2, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.worker.js
new file mode 100644
index 0000000000..b8008e778b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.3.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.shape.3
+// Description:arc() from 0 to -pi/2 does not draw anything in the wrong quadrant
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() from 0 to -pi/2 does not draw anything in the wrong quadrant");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(0, 50, 50, 0, -Math.PI/2, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.html
new file mode 100644
index 0000000000..e6221947df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.shape.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.shape.4</h1>
+<p class="desc">arc() from 0 to -pi/2 draws stuff in the right quadrant</p>
+
+
+<script>
+var t = async_test("arc() from 0 to -pi/2 draws stuff in the right quadrant");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 150;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 100, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.worker.js
new file mode 100644
index 0000000000..9174641c20
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.4.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.shape.4
+// Description:arc() from 0 to -pi/2 draws stuff in the right quadrant
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() from 0 to -pi/2 draws stuff in the right quadrant");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 150;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 100, 0, -Math.PI/2, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.html
new file mode 100644
index 0000000000..968a1c58c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.shape.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.shape.5</h1>
+<p class="desc">arc() from 0 to 5pi does not draw crazy things</p>
+
+
+<script>
+var t = async_test("arc() from 0 to 5pi does not draw crazy things");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(300, 0, 100, 0, 5*Math.PI, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.worker.js
new file mode 100644
index 0000000000..7f40e4341a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.shape.5.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.shape.5
+// Description:arc() from 0 to 5pi does not draw crazy things
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() from 0 to 5pi does not draw crazy things");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(300, 0, 100, 0, 5*Math.PI, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.html
new file mode 100644
index 0000000000..ced1207a9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.twopie.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.twopie.1</h1>
+<p class="desc">arc() draws nothing when end = start + 2pi-e and anticlockwise</p>
+
+
+<script>
+var t = async_test("arc() draws nothing when end = start + 2pi-e and anticlockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.worker.js
new file mode 100644
index 0000000000..e9abf086e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.twopie.1
+// Description:arc() draws nothing when end = start + 2pi-e and anticlockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws nothing when end = start + 2pi-e and anticlockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.html
new file mode 100644
index 0000000000..4948b10f2f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.twopie.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.twopie.2</h1>
+<p class="desc">arc() draws a full circle when end = start + 2pi-e and clockwise</p>
+
+
+<script>
+var t = async_test("arc() draws a full circle when end = start + 2pi-e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.worker.js
new file mode 100644
index 0000000000..5e9c51e5e3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.2.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.twopie.2
+// Description:arc() draws a full circle when end = start + 2pi-e and clockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws a full circle when end = start + 2pi-e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.html
new file mode 100644
index 0000000000..c4036fc06e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.twopie.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.twopie.3</h1>
+<p class="desc">arc() draws a full circle when end = start + 2pi+e and anticlockwise</p>
+
+
+<script>
+var t = async_test("arc() draws a full circle when end = start + 2pi+e and anticlockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.worker.js
new file mode 100644
index 0000000000..ad72fbd0af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.3.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.twopie.3
+// Description:arc() draws a full circle when end = start + 2pi+e and anticlockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws a full circle when end = start + 2pi+e and anticlockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.html
new file mode 100644
index 0000000000..392f00e652
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.twopie.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.twopie.4</h1>
+<p class="desc">arc() draws nothing when end = start + 2pi+e and clockwise</p>
+
+
+<script>
+var t = async_test("arc() draws nothing when end = start + 2pi+e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.worker.js
new file mode 100644
index 0000000000..15d799209c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.4.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.twopie.4
+// Description:arc() draws nothing when end = start + 2pi+e and clockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws nothing when end = start + 2pi+e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.html
new file mode 100644
index 0000000000..17c66c527c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.twopie.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.twopie.5</h1>
+<p class="desc">arc() draws correctly when start = 2 and end = start + 2pi+e and clockwise</p>
+
+
+<script>
+var t = async_test("arc() draws correctly when start = 2 and end = start + 2pi+e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 2, 2 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ _assertPixel(canvas, 95,25, 0,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.worker.js
new file mode 100644
index 0000000000..8fa29d5a97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.5.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.twopie.5
+// Description:arc() draws correctly when start = 2 and end = start + 2pi+e and clockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws correctly when start = 2 and end = start + 2pi+e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 2, 2 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ _assertPixel(canvas, 95,25, 0,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.html
new file mode 100644
index 0000000000..befec82704
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.twopie.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.twopie.6</h1>
+<p class="desc">arc() draws correctly when start = 5 and end = start + 2pi+e and clockwise</p>
+
+
+<script>
+var t = async_test("arc() draws correctly when start = 5 and end = start + 2pi+e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 5, 5 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ _assertPixel(canvas, 5,25, 0,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.worker.js
new file mode 100644
index 0000000000..546cbce33c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.twopie.6.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.twopie.6
+// Description:arc() draws correctly when start = 5 and end = start + 2pi+e and clockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws correctly when start = 5 and end = start + 2pi+e and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 5, 5 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ _assertPixel(canvas, 5,25, 0,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.html
new file mode 100644
index 0000000000..5757ab3ddf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.zero.1</h1>
+<p class="desc">arc() draws nothing when startAngle = endAngle and anticlockwise</p>
+
+
+<script>
+var t = async_test("arc() draws nothing when startAngle = endAngle and anticlockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.worker.js
new file mode 100644
index 0000000000..5a6d35f691
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.zero.1
+// Description:arc() draws nothing when startAngle = endAngle and anticlockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws nothing when startAngle = endAngle and anticlockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.html
new file mode 100644
index 0000000000..69f8472443
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.zero.2</h1>
+<p class="desc">arc() draws nothing when startAngle = endAngle and clockwise</p>
+
+
+<script>
+var t = async_test("arc() draws nothing when startAngle = endAngle and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.worker.js
new file mode 100644
index 0000000000..a285462fc0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zero.2.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.zero.2
+// Description:arc() draws nothing when startAngle = endAngle and clockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() draws nothing when startAngle = endAngle and clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, false);
+ ctx.stroke();
+ _assertPixel(canvas, 50,20, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.html
new file mode 100644
index 0000000000..7303b7dc1c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arc.zeroradius</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arc.zeroradius</h1>
+<p class="desc">arc() with zero radius draws a line to the start point</p>
+
+
+<script>
+var t = async_test("arc() with zero radius draws a line to the start point");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00'
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 0, 0, Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.worker.js
new file mode 100644
index 0000000000..183acc1bbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arc.zeroradius.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arc.zeroradius
+// Description:arc() with zero radius draws a line to the start point
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arc() with zero radius draws a line to the start point");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00'
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 0, 0, Math.PI, true);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.html
new file mode 100644
index 0000000000..e7558d628f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.coincide.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.coincide.1</h1>
+<p class="desc">arcTo() has no effect if P0 = P1</p>
+
+
+<script>
+var t = async_test("arcTo() has no effect if P0 = P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(0, 25, 50, 1000, 1);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.worker.js
new file mode 100644
index 0000000000..80a74c6d91
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.1.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.coincide.1
+// Description:arcTo() has no effect if P0 = P1
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() has no effect if P0 = P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(0, 25, 50, 1000, 1);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.html
new file mode 100644
index 0000000000..18ac31524d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.coincide.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.coincide.2</h1>
+<p class="desc">arcTo() draws a straight line to P1 if P1 = P2</p>
+
+
+<script>
+var t = async_test("arcTo() draws a straight line to P1 if P1 = P2");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.worker.js
new file mode 100644
index 0000000000..a35ffe6977
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.coincide.2.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.coincide.2
+// Description:arcTo() draws a straight line to P1 if P1 = P2
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() draws a straight line to P1 if P1 = P2");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.html
new file mode 100644
index 0000000000..b9bcc7b238
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.collinear.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.collinear.1</h1>
+<p class="desc">arcTo() with all points on a line, and P1 between P0/P2, draws a straight line to P1</p>
+
+
+<script>
+var t = async_test("arcTo() with all points on a line, and P1 between P0/P2, draws a straight line to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 200, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.worker.js
new file mode 100644
index 0000000000..a814b1af07
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.1.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.collinear.1
+// Description:arcTo() with all points on a line, and P1 between P0/P2, draws a straight line to P1
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() with all points on a line, and P1 between P0/P2, draws a straight line to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 200, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, 100, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.html
new file mode 100644
index 0000000000..f51a7af206
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.collinear.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.collinear.2</h1>
+<p class="desc">arcTo() with all points on a line, and P2 between P0/P1, draws a straight line to P1</p>
+
+
+<script>
+var t = async_test("arcTo() with all points on a line, and P2 between P0/P1, draws a straight line to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 10, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 110, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.worker.js
new file mode 100644
index 0000000000..ba9a75ed4e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.2.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.collinear.2
+// Description:arcTo() with all points on a line, and P2 between P0/P1, draws a straight line to P1
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() with all points on a line, and P2 between P0/P1, draws a straight line to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 10, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 110, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.html
new file mode 100644
index 0000000000..568d0f8f2b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.collinear.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.collinear.3</h1>
+<p class="desc">arcTo() with all points on a line, and P0 between P1/P2, draws a straight line to P1</p>
+
+
+<script>
+var t = async_test("arcTo() with all points on a line, and P0 between P1/P2, draws a straight line to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 0, 25, 1);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, -200, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.worker.js
new file mode 100644
index 0000000000..8087909cb7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.collinear.3.worker.js
@@ -0,0 +1,43 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.collinear.3
+// Description:arcTo() with all points on a line, and P0 between P1/P2, draws a straight line to P1
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() with all points on a line, and P0 between P1/P2, draws a straight line to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 0, 25, 1);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, -200, 25, 1);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..ec1bd0399d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the first control point is added (and nothing is drawn up to it)</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arcTo(100, 50, 200, 50, 0.1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.worker.js
new file mode 100644
index 0000000000..74d71b6253
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.ensuresubpath.1
+// Description:If there is no subpath, the first control point is added (and nothing is drawn up to it)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arcTo(100, 50, 200, 50, 0.1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..60ee458524
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the first control point is added</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the first control point is added");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arcTo(0, 25, 50, 250, 0.1); // adds (x1,y1), draws nothing
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.worker.js
new file mode 100644
index 0000000000..76790925af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.ensuresubpath.2.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.ensuresubpath.2
+// Description:If there is no subpath, the first control point is added
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the first control point is added");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arcTo(0, 25, 50, 250, 0.1); // adds (x1,y1), draws nothing
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.html
new file mode 100644
index 0000000000..c35e293a6f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.negative</h1>
+<p class="desc">arcTo() with negative radius throws an exception</p>
+
+
+<script>
+var t = async_test("arcTo() with negative radius throws an exception");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.arcTo(0, 0, 0, 0, -1); });
+ var path = new Path2D();
+ assert_throws_dom("INDEX_SIZE_ERR", function() { path.arcTo(10, 10, 20, 20, -5); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.worker.js
new file mode 100644
index 0000000000..104e7a6387
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.negative.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.negative
+// Description:arcTo() with negative radius throws an exception
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() with negative radius throws an exception");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.arcTo(0, 0, 0, 0, -1); });
+ var path = new Path2D();
+ assert_throws_dom("INDEX_SIZE_ERR", function() { path.arcTo(10, 10, 20, 20, -5); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.html
new file mode 100644
index 0000000000..9d2256d79c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.nonfinite</h1>
+<p class="desc">arcTo() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("arcTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.arcTo(Infinity, 50, 0, 50, 0);
+ ctx.arcTo(-Infinity, 50, 0, 50, 0);
+ ctx.arcTo(NaN, 50, 0, 50, 0);
+ ctx.arcTo(0, Infinity, 0, 50, 0);
+ ctx.arcTo(0, -Infinity, 0, 50, 0);
+ ctx.arcTo(0, NaN, 0, 50, 0);
+ ctx.arcTo(0, 50, Infinity, 50, 0);
+ ctx.arcTo(0, 50, -Infinity, 50, 0);
+ ctx.arcTo(0, 50, NaN, 50, 0);
+ ctx.arcTo(0, 50, 0, Infinity, 0);
+ ctx.arcTo(0, 50, 0, -Infinity, 0);
+ ctx.arcTo(0, 50, 0, NaN, 0);
+ ctx.arcTo(0, 50, 0, 50, Infinity);
+ ctx.arcTo(0, 50, 0, 50, -Infinity);
+ ctx.arcTo(0, 50, 0, 50, NaN);
+ ctx.arcTo(Infinity, Infinity, 0, 50, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, 50, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.arcTo(Infinity, Infinity, Infinity, 50, Infinity);
+ ctx.arcTo(Infinity, Infinity, 0, Infinity, 0);
+ ctx.arcTo(Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.arcTo(Infinity, Infinity, 0, 50, Infinity);
+ ctx.arcTo(Infinity, 50, Infinity, 50, 0);
+ ctx.arcTo(Infinity, 50, Infinity, Infinity, 0);
+ ctx.arcTo(Infinity, 50, Infinity, Infinity, Infinity);
+ ctx.arcTo(Infinity, 50, Infinity, 50, Infinity);
+ ctx.arcTo(Infinity, 50, 0, Infinity, 0);
+ ctx.arcTo(Infinity, 50, 0, Infinity, Infinity);
+ ctx.arcTo(Infinity, 50, 0, 50, Infinity);
+ ctx.arcTo(0, Infinity, Infinity, 50, 0);
+ ctx.arcTo(0, Infinity, Infinity, Infinity, 0);
+ ctx.arcTo(0, Infinity, Infinity, Infinity, Infinity);
+ ctx.arcTo(0, Infinity, Infinity, 50, Infinity);
+ ctx.arcTo(0, Infinity, 0, Infinity, 0);
+ ctx.arcTo(0, Infinity, 0, Infinity, Infinity);
+ ctx.arcTo(0, Infinity, 0, 50, Infinity);
+ ctx.arcTo(0, 50, Infinity, Infinity, 0);
+ ctx.arcTo(0, 50, Infinity, Infinity, Infinity);
+ ctx.arcTo(0, 50, Infinity, 50, Infinity);
+ ctx.arcTo(0, 50, 0, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.worker.js
new file mode 100644
index 0000000000..1d2c913cfe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.nonfinite.worker.js
@@ -0,0 +1,70 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.nonfinite
+// Description:arcTo() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.arcTo(Infinity, 50, 0, 50, 0);
+ ctx.arcTo(-Infinity, 50, 0, 50, 0);
+ ctx.arcTo(NaN, 50, 0, 50, 0);
+ ctx.arcTo(0, Infinity, 0, 50, 0);
+ ctx.arcTo(0, -Infinity, 0, 50, 0);
+ ctx.arcTo(0, NaN, 0, 50, 0);
+ ctx.arcTo(0, 50, Infinity, 50, 0);
+ ctx.arcTo(0, 50, -Infinity, 50, 0);
+ ctx.arcTo(0, 50, NaN, 50, 0);
+ ctx.arcTo(0, 50, 0, Infinity, 0);
+ ctx.arcTo(0, 50, 0, -Infinity, 0);
+ ctx.arcTo(0, 50, 0, NaN, 0);
+ ctx.arcTo(0, 50, 0, 50, Infinity);
+ ctx.arcTo(0, 50, 0, 50, -Infinity);
+ ctx.arcTo(0, 50, 0, 50, NaN);
+ ctx.arcTo(Infinity, Infinity, 0, 50, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, 50, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.arcTo(Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.arcTo(Infinity, Infinity, Infinity, 50, Infinity);
+ ctx.arcTo(Infinity, Infinity, 0, Infinity, 0);
+ ctx.arcTo(Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.arcTo(Infinity, Infinity, 0, 50, Infinity);
+ ctx.arcTo(Infinity, 50, Infinity, 50, 0);
+ ctx.arcTo(Infinity, 50, Infinity, Infinity, 0);
+ ctx.arcTo(Infinity, 50, Infinity, Infinity, Infinity);
+ ctx.arcTo(Infinity, 50, Infinity, 50, Infinity);
+ ctx.arcTo(Infinity, 50, 0, Infinity, 0);
+ ctx.arcTo(Infinity, 50, 0, Infinity, Infinity);
+ ctx.arcTo(Infinity, 50, 0, 50, Infinity);
+ ctx.arcTo(0, Infinity, Infinity, 50, 0);
+ ctx.arcTo(0, Infinity, Infinity, Infinity, 0);
+ ctx.arcTo(0, Infinity, Infinity, Infinity, Infinity);
+ ctx.arcTo(0, Infinity, Infinity, 50, Infinity);
+ ctx.arcTo(0, Infinity, 0, Infinity, 0);
+ ctx.arcTo(0, Infinity, 0, Infinity, Infinity);
+ ctx.arcTo(0, Infinity, 0, 50, Infinity);
+ ctx.arcTo(0, 50, Infinity, Infinity, 0);
+ ctx.arcTo(0, 50, Infinity, Infinity, Infinity);
+ ctx.arcTo(0, 50, Infinity, 50, Infinity);
+ ctx.arcTo(0, 50, 0, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.html
new file mode 100644
index 0000000000..26dfa640ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.scale</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.scale</h1>
+<p class="desc">arcTo scales the curve, not just the control points</p>
+
+
+<script>
+var t = async_test("arcTo scales the curve, not just the control points");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.scale(0.1, 1);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-1000, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.worker.js
new file mode 100644
index 0000000000..dc41e6d92d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.scale.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.scale
+// Description:arcTo scales the curve, not just the control points
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo scales the curve, not just the control points");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.scale(0.1, 1);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-1000, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.html
new file mode 100644
index 0000000000..f7d10dd61a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.shape.curve1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.shape.curve1</h1>
+<p class="desc">arcTo() curves in the right kind of shape</p>
+
+
+<script>
+var t = async_test("arcTo() curves in the right kind of shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25+tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15-tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 55,19, 0,255,0,255);
+ _assertPixel(canvas, 55,20, 0,255,0,255);
+ _assertPixel(canvas, 55,21, 0,255,0,255);
+ _assertPixel(canvas, 64,22, 0,255,0,255);
+ _assertPixel(canvas, 65,21, 0,255,0,255);
+ _assertPixel(canvas, 72,28, 0,255,0,255);
+ _assertPixel(canvas, 73,27, 0,255,0,255);
+ _assertPixel(canvas, 78,36, 0,255,0,255);
+ _assertPixel(canvas, 79,35, 0,255,0,255);
+ _assertPixel(canvas, 80,44, 0,255,0,255);
+ _assertPixel(canvas, 80,45, 0,255,0,255);
+ _assertPixel(canvas, 80,46, 0,255,0,255);
+ _assertPixel(canvas, 65,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.worker.js
new file mode 100644
index 0000000000..c7e61ab576
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve1.worker.js
@@ -0,0 +1,55 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.shape.curve1
+// Description:arcTo() curves in the right kind of shape
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() curves in the right kind of shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25+tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15-tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 55,19, 0,255,0,255);
+ _assertPixel(canvas, 55,20, 0,255,0,255);
+ _assertPixel(canvas, 55,21, 0,255,0,255);
+ _assertPixel(canvas, 64,22, 0,255,0,255);
+ _assertPixel(canvas, 65,21, 0,255,0,255);
+ _assertPixel(canvas, 72,28, 0,255,0,255);
+ _assertPixel(canvas, 73,27, 0,255,0,255);
+ _assertPixel(canvas, 78,36, 0,255,0,255);
+ _assertPixel(canvas, 79,35, 0,255,0,255);
+ _assertPixel(canvas, 80,44, 0,255,0,255);
+ _assertPixel(canvas, 80,45, 0,255,0,255);
+ _assertPixel(canvas, 80,46, 0,255,0,255);
+ _assertPixel(canvas, 65,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.html
new file mode 100644
index 0000000000..1a023322b2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.shape.curve2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.shape.curve2</h1>
+<p class="desc">arcTo() curves in the right kind of shape</p>
+
+
+<script>
+var t = async_test("arcTo() curves in the right kind of shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25-tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15+tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 55,19, 0,255,0,255);
+ _assertPixel(canvas, 55,20, 0,255,0,255);
+ _assertPixel(canvas, 55,21, 0,255,0,255);
+ _assertPixel(canvas, 64,22, 0,255,0,255);
+ _assertPixel(canvas, 65,21, 0,255,0,255);
+ _assertPixel(canvas, 72,28, 0,255,0,255);
+ _assertPixel(canvas, 73,27, 0,255,0,255);
+ _assertPixel(canvas, 78,36, 0,255,0,255);
+ _assertPixel(canvas, 79,35, 0,255,0,255);
+ _assertPixel(canvas, 80,44, 0,255,0,255);
+ _assertPixel(canvas, 80,45, 0,255,0,255);
+ _assertPixel(canvas, 80,46, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.worker.js
new file mode 100644
index 0000000000..f00e506f30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.curve2.worker.js
@@ -0,0 +1,54 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.shape.curve2
+// Description:arcTo() curves in the right kind of shape
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() curves in the right kind of shape");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25-tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15+tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 55,19, 0,255,0,255);
+ _assertPixel(canvas, 55,20, 0,255,0,255);
+ _assertPixel(canvas, 55,21, 0,255,0,255);
+ _assertPixel(canvas, 64,22, 0,255,0,255);
+ _assertPixel(canvas, 65,21, 0,255,0,255);
+ _assertPixel(canvas, 72,28, 0,255,0,255);
+ _assertPixel(canvas, 73,27, 0,255,0,255);
+ _assertPixel(canvas, 78,36, 0,255,0,255);
+ _assertPixel(canvas, 79,35, 0,255,0,255);
+ _assertPixel(canvas, 80,44, 0,255,0,255);
+ _assertPixel(canvas, 80,45, 0,255,0,255);
+ _assertPixel(canvas, 80,46, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.html
new file mode 100644
index 0000000000..3f8af61215
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.shape.end</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.shape.end</h1>
+<p class="desc">arcTo() does not draw anything from P1 to P2</p>
+
+
+<script>
+var t = async_test("arcTo() does not draw anything from P1 to P2");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.arcTo(-100, 25, 200, 25, 10);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.worker.js
new file mode 100644
index 0000000000..20cc956a44
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.end.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.shape.end
+// Description:arcTo() does not draw anything from P1 to P2
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() does not draw anything from P1 to P2");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.arcTo(-100, 25, 200, 25, 10);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.html
new file mode 100644
index 0000000000..a426a19d8a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.shape.start</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.shape.start</h1>
+<p class="desc">arcTo() draws a straight line from P0 to P1</p>
+
+
+<script>
+var t = async_test("arcTo() draws a straight line from P0 to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(200, 25, 200, 50, 10);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.worker.js
new file mode 100644
index 0000000000..cebf27a821
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.shape.start.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.shape.start
+// Description:arcTo() draws a straight line from P0 to P1
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() draws a straight line from P0 to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(200, 25, 200, 50, 10);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.html
new file mode 100644
index 0000000000..842210138a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.transformation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.transformation</h1>
+<p class="desc">arcTo joins up to the last subpath point correctly</p>
+
+
+<script>
+var t = async_test("arcTo joins up to the last subpath point correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.worker.js
new file mode 100644
index 0000000000..585a500079
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.transformation.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.transformation
+// Description:arcTo joins up to the last subpath point correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo joins up to the last subpath point correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.html
new file mode 100644
index 0000000000..a367385551
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.zero.1</h1>
+<p class="desc">arcTo() with zero radius draws a straight line from P0 to P1</p>
+
+
+<script>
+var t = async_test("arcTo() with zero radius draws a straight line from P0 to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 100, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(0, -25);
+ ctx.arcTo(50, -25, 50, 50, 0);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.worker.js
new file mode 100644
index 0000000000..8f6d979a55
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.1.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.zero.1
+// Description:arcTo() with zero radius draws a straight line from P0 to P1
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() with zero radius draws a straight line from P0 to P1");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 100, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(0, -25);
+ ctx.arcTo(50, -25, 50, 50, 0);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.html
new file mode 100644
index 0000000000..87ab4e13de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.arcTo.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.arcTo.zero.2</h1>
+<p class="desc">arcTo() with zero radius draws a straight line from P0 to P1, even when all points are collinear</p>
+
+
+<script>
+var t = async_test("arcTo() with zero radius draws a straight line from P0 to P1, even when all points are collinear");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 50, 25, 0);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.worker.js
new file mode 100644
index 0000000000..0a45976325
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.arcTo.zero.2.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.arcTo.zero.2
+// Description:arcTo() with zero radius draws a straight line from P0 to P1, even when all points are collinear
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("arcTo() with zero radius draws a straight line from P0 to P1, even when all points are collinear");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 50, 25, 0);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.html
new file mode 100644
index 0000000000..b2043d604d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.beginPath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.beginPath</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.worker.js
new file mode 100644
index 0000000000..35a4acc84d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.beginPath.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.beginPath
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.html
new file mode 100644
index 0000000000..099919060b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.bezierCurveTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.bezierCurveTo.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.bezierCurveTo(100, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.worker.js
new file mode 100644
index 0000000000..e7c86a08f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.basic.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.bezierCurveTo.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.bezierCurveTo(100, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..6a11d1bfbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.bezierCurveTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.bezierCurveTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the first control point is added (and nothing is drawn up to it)</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(100, 50, 200, 50, 200, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 95,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.worker.js
new file mode 100644
index 0000000000..d6ef6f3f1b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.1.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.bezierCurveTo.ensuresubpath.1
+// Description:If there is no subpath, the first control point is added (and nothing is drawn up to it)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(100, 50, 200, 50, 200, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 95,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..d2afd19f66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.bezierCurveTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.bezierCurveTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the first control point is added</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the first control point is added");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(0, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.worker.js
new file mode 100644
index 0000000000..6860e60f4a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.ensuresubpath.2.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.bezierCurveTo.ensuresubpath.2
+// Description:If there is no subpath, the first control point is added
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the first control point is added");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(0, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.html
new file mode 100644
index 0000000000..b49e2f43f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.bezierCurveTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.bezierCurveTo.nonfinite</h1>
+<p class="desc">bezierCurveTo() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("bezierCurveTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(-Infinity, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(NaN, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, -Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, NaN, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, -Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, NaN, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, -Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, NaN, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, -Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, NaN, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, -Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, NaN);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.worker.js
new file mode 100644
index 0000000000..7eaf278f6c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.nonfinite.worker.js
@@ -0,0 +1,104 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.bezierCurveTo.nonfinite
+// Description:bezierCurveTo() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("bezierCurveTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(-Infinity, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(NaN, 50, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, -Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, NaN, 0, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, -Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, NaN, 50, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, -Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, NaN, 0, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, -Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, NaN, 50);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, -Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, 0, NaN);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, Infinity, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(Infinity, 50, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, Infinity, 0, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, 0, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, Infinity, 50, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, Infinity, 50);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, Infinity, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, Infinity, 0, Infinity);
+ ctx.bezierCurveTo(0, 50, 0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.html
new file mode 100644
index 0000000000..10a085e9de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.bezierCurveTo.scaled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.bezierCurveTo.scaled</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-2, 3.1);
+ ctx.bezierCurveTo(-2, -1, 2.1, -1, 2.1, 3.1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.worker.js
new file mode 100644
index 0000000000..5098fe83eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.scaled.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.bezierCurveTo.scaled
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-2, 3.1);
+ ctx.bezierCurveTo(-2, -1, 2.1, -1, 2.1, 3.1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.html
new file mode 100644
index 0000000000..fc6b9273e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.bezierCurveTo.shape</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.bezierCurveTo.shape</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-2000, 3100);
+ ctx.bezierCurveTo(-2000, -1000, 2100, -1000, 2100, 3100);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.worker.js
new file mode 100644
index 0000000000..58f601c5d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.bezierCurveTo.shape.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.bezierCurveTo.shape
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-2000, 3100);
+ ctx.bezierCurveTo(-2000, -1000, 2100, -1000, 2100, 3100);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.html
new file mode 100644
index 0000000000..e90be3d73f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.clip.basic.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.clip.basic.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.worker.js
new file mode 100644
index 0000000000..708608e1e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.1.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.clip.basic.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.html
new file mode 100644
index 0000000000..6426ba2d83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.clip.basic.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.clip.basic.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(-100, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.worker.js
new file mode 100644
index 0000000000..1ec28b6205
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.basic.2.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.clip.basic.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(-100, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.html
new file mode 100644
index 0000000000..30e0d6cba4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.clip.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.clip.empty</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.worker.js
new file mode 100644
index 0000000000..3f91a7e2e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.empty.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.clip.empty
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.html
new file mode 100644
index 0000000000..94607f6d92
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.clip.intersect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.clip.intersect</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50)
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.worker.js
new file mode 100644
index 0000000000..8c0fcb68bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.intersect.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.clip.intersect
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50)
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.html
new file mode 100644
index 0000000000..e9b69282af
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.clip.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.clip.unaffected</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.lineTo(0, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.worker.js
new file mode 100644
index 0000000000..d65a421d33
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.unaffected.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.clip.unaffected
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.lineTo(0, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.html
new file mode 100644
index 0000000000..2fd7c1abbf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.clip.winding.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.clip.winding.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.worker.js
new file mode 100644
index 0000000000..aa89fd55c3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.1.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.clip.winding.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.html
new file mode 100644
index 0000000000..eed064f84e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.clip.winding.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.clip.winding.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.clip();
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.worker.js
new file mode 100644
index 0000000000..5c9cac6bb6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.clip.winding.2.worker.js
@@ -0,0 +1,44 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.clip.winding.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.clip();
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.html
new file mode 100644
index 0000000000..c7596a8f1c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.closePath.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.closePath.empty</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.worker.js
new file mode 100644
index 0000000000..c9a4030e68
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.empty.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.closePath.empty
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.html
new file mode 100644
index 0000000000..771f3d3ca0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.closePath.newline</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.closePath.newline</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.closePath();
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.worker.js
new file mode 100644
index 0000000000..4dff932086
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.newline.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.closePath.newline
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.closePath();
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.html
new file mode 100644
index 0000000000..3dffdeaf5b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.closePath.nextpoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.closePath.nextpoint</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -1000);
+ ctx.closePath();
+ ctx.lineTo(1000, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.worker.js
new file mode 100644
index 0000000000..86d931b671
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.closePath.nextpoint.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.closePath.nextpoint
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -1000);
+ ctx.closePath();
+ ctx.lineTo(1000, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.html
new file mode 100644
index 0000000000..d664c45593
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.ellipse.basics</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.ellipse.basics</h1>
+<p class="desc">Verify canvas throws error when drawing ellipse with negative radii.</p>
+
+
+<script>
+var t = async_test("Verify canvas throws error when drawing ellipse with negative radii.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.ellipse(10, 10, 10, 5, 0, 0, 1, false);
+ ctx.ellipse(10, 10, 10, 0, 0, 0, 1, false);
+ ctx.ellipse(10, 10, -0, 5, 0, 0, 1, false);
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, -2, 5, 0, 0, 1, false); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, 0, -1.5, 0, 0, 1, false); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, -2, -5, 0, 0, 1, false); });
+ ctx.ellipse(80, 0, 10, 4294967277, Math.PI / -84, -Math.PI / 2147483436, false);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.worker.js
new file mode 100644
index 0000000000..60f3e9a673
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.ellipse.basics.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.ellipse.basics
+// Description:Verify canvas throws error when drawing ellipse with negative radii.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify canvas throws error when drawing ellipse with negative radii.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.ellipse(10, 10, 10, 5, 0, 0, 1, false);
+ ctx.ellipse(10, 10, 10, 0, 0, 0, 1, false);
+ ctx.ellipse(10, 10, -0, 5, 0, 0, 1, false);
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, -2, 5, 0, 0, 1, false); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, 0, -1.5, 0, 0, 1, false); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.ellipse(10, 10, -2, -5, 0, 0, 1, false); });
+ ctx.ellipse(80, 0, 10, 4294967277, Math.PI / -84, -Math.PI / 2147483436, false);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.html
new file mode 100644
index 0000000000..6f8fd51de7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.fill.closed.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.fill.closed.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.worker.js
new file mode 100644
index 0000000000..9dad73b579
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.basic.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.fill.closed.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.html
new file mode 100644
index 0000000000..706e38d176
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.fill.closed.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.fill.closed.unaffected</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 10,40, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.worker.js
new file mode 100644
index 0000000000..b6d7982866
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.closed.unaffected.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.fill.closed.unaffected
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 90,10, 0,255,0,255);
+ _assertPixel(canvas, 10,40, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.html
new file mode 100644
index 0000000000..da936fab57
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.fill.overlap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.fill.overlap</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.rect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.rect(10, 10, 80, 30);
+ ctx.fill();
+
+ _assertPixelApprox(canvas, 50,25, 0,127,0,255, 1);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.png b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.png
new file mode 100644
index 0000000000..e2a35d48d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.worker.js
new file mode 100644
index 0000000000..8f30cc7af2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.overlap.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.fill.overlap
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.rect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.rect(10, 10, 80, 30);
+ ctx.fill();
+
+ _assertPixelApprox(canvas, 50,25, 0,127,0,255, 1);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.html
new file mode 100644
index 0000000000..40845a4a77
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.fill.winding.add</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.fill.winding.add</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.worker.js
new file mode 100644
index 0000000000..cdbfc7f3a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.add.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.fill.winding.add
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.html
new file mode 100644
index 0000000000..e0f9c838b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.fill.winding.subtract.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.fill.winding.subtract.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.worker.js
new file mode 100644
index 0000000000..3d6a1660de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.1.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.fill.winding.subtract.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.html
new file mode 100644
index 0000000000..c7de03ae08
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.fill.winding.subtract.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.fill.winding.subtract.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.worker.js
new file mode 100644
index 0000000000..0e8b4c79a5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.2.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.fill.winding.subtract.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.html
new file mode 100644
index 0000000000..3d12380357
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.fill.winding.subtract.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.fill.winding.subtract.3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(120, -20);
+ ctx.lineTo(120, 70);
+ ctx.lineTo(-20, 70);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.worker.js
new file mode 100644
index 0000000000..6c9f07c76a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.fill.winding.subtract.3.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.fill.winding.subtract.3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(120, -20);
+ ctx.lineTo(120, 70);
+ ctx.lineTo(-20, 70);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.html
new file mode 100644
index 0000000000..e2e02f5142
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.initial</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.worker.js
new file mode 100644
index 0000000000..de1a935032
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.initial.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.initial
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.html
new file mode 100644
index 0000000000..ccffcd7d76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.arc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.arc</h1>
+<p class="desc">isPointInPath() works on arcs</p>
+
+
+<script>
+var t = async_test("isPointInPath() works on arcs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.arc(50, 25, 10, 0, Math.PI, false);
+ _assertSame(ctx.isPointInPath(50, 10), false, "ctx.isPointInPath(50, 10)", "false");
+ _assertSame(ctx.isPointInPath(50, 20), false, "ctx.isPointInPath(50, 20)", "false");
+ _assertSame(ctx.isPointInPath(50, 30), true, "ctx.isPointInPath(50, 30)", "true");
+ _assertSame(ctx.isPointInPath(50, 40), false, "ctx.isPointInPath(50, 40)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), false, "ctx.isPointInPath(70, 30)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.worker.js
new file mode 100644
index 0000000000..13510b744f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.arc.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.arc
+// Description:isPointInPath() works on arcs
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() works on arcs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.arc(50, 25, 10, 0, Math.PI, false);
+ _assertSame(ctx.isPointInPath(50, 10), false, "ctx.isPointInPath(50, 10)", "false");
+ _assertSame(ctx.isPointInPath(50, 20), false, "ctx.isPointInPath(50, 20)", "false");
+ _assertSame(ctx.isPointInPath(50, 30), true, "ctx.isPointInPath(50, 30)", "true");
+ _assertSame(ctx.isPointInPath(50, 40), false, "ctx.isPointInPath(50, 40)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), false, "ctx.isPointInPath(70, 30)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.html
new file mode 100644
index 0000000000..7b9673a812
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.basic.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.basic.1</h1>
+<p class="desc">isPointInPath() detects whether the point is inside the path</p>
+
+
+<script>
+var t = async_test("isPointInPath() detects whether the point is inside the path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), true, "ctx.isPointInPath(10, 10)", "true");
+ _assertSame(ctx.isPointInPath(30, 10), false, "ctx.isPointInPath(30, 10)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.worker.js
new file mode 100644
index 0000000000..f51900ea6b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.1.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.basic.1
+// Description:isPointInPath() detects whether the point is inside the path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() detects whether the point is inside the path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), true, "ctx.isPointInPath(10, 10)", "true");
+ _assertSame(ctx.isPointInPath(30, 10), false, "ctx.isPointInPath(30, 10)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.html
new file mode 100644
index 0000000000..3710dd185d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.basic.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.basic.2</h1>
+<p class="desc">isPointInPath() detects whether the point is inside the path</p>
+
+
+<script>
+var t = async_test("isPointInPath() detects whether the point is inside the path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(20, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(30, 10), true, "ctx.isPointInPath(30, 10)", "true");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.worker.js
new file mode 100644
index 0000000000..6024a0dc39
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.2.worker.js
@@ -0,0 +1,24 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.basic.2
+// Description:isPointInPath() detects whether the point is inside the path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() detects whether the point is inside the path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(20, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(30, 10), true, "ctx.isPointInPath(30, 10)", "true");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.html
new file mode 100644
index 0000000000..21cf448de2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.basic</h1>
+<p class="desc">Verify the winding rule in isPointInPath works for for rect path.</p>
+
+
+<script>
+var t = async_test("Verify the winding rule in isPointInPath works for for rect path.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50), true, "ctx.isPointInPath(50, 50)", "true");
+ _assertSame(ctx.isPointInPath(NaN, 50), false, "ctx.isPointInPath(NaN, 50)", "false");
+ _assertSame(ctx.isPointInPath(50, NaN), false, "ctx.isPointInPath(50, NaN)", "false");
+
+ // Testing nonzero isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50, 'nonzero'), true, "ctx.isPointInPath(50, 50, 'nonzero')", "true");
+
+ // Testing evenodd isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50, 'evenodd'), false, "ctx.isPointInPath(50, 50, 'evenodd')", "false");
+
+ // Testing extremely large scale
+ ctx.save();
+ ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0, 'nonzero'), true, "ctx.isPointInPath(0, 0, 'nonzero')", "true");
+ _assertSame(ctx.isPointInPath(0, 0, 'evenodd'), true, "ctx.isPointInPath(0, 0, 'evenodd')", "true");
+ ctx.restore();
+
+ // Check with non-invertible ctm.
+ ctx.save();
+ ctx.scale(0, 0);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0, 'nonzero'), false, "ctx.isPointInPath(0, 0, 'nonzero')", "false");
+ _assertSame(ctx.isPointInPath(0, 0, 'evenodd'), false, "ctx.isPointInPath(0, 0, 'evenodd')", "false");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.worker.js
new file mode 100644
index 0000000000..3e2a95c15e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.basic.worker.js
@@ -0,0 +1,61 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.basic
+// Description:Verify the winding rule in isPointInPath works for for rect path.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify the winding rule in isPointInPath works for for rect path.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50), true, "ctx.isPointInPath(50, 50)", "true");
+ _assertSame(ctx.isPointInPath(NaN, 50), false, "ctx.isPointInPath(NaN, 50)", "false");
+ _assertSame(ctx.isPointInPath(50, NaN), false, "ctx.isPointInPath(50, NaN)", "false");
+
+ // Testing nonzero isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50, 'nonzero'), true, "ctx.isPointInPath(50, 50, 'nonzero')", "true");
+
+ // Testing evenodd isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(50, 50, 'evenodd'), false, "ctx.isPointInPath(50, 50, 'evenodd')", "false");
+
+ // Testing extremely large scale
+ ctx.save();
+ ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0, 'nonzero'), true, "ctx.isPointInPath(0, 0, 'nonzero')", "true");
+ _assertSame(ctx.isPointInPath(0, 0, 'evenodd'), true, "ctx.isPointInPath(0, 0, 'evenodd')", "true");
+ ctx.restore();
+
+ // Check with non-invertible ctm.
+ ctx.save();
+ ctx.scale(0, 0);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0, 'nonzero'), false, "ctx.isPointInPath(0, 0, 'nonzero')", "false");
+ _assertSame(ctx.isPointInPath(0, 0, 'evenodd'), false, "ctx.isPointInPath(0, 0, 'evenodd')", "false");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.html
new file mode 100644
index 0000000000..d348110305
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.bezier</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.bezier</h1>
+<p class="desc">isPointInPath() works on Bezier curves</p>
+
+
+<script>
+var t = async_test("isPointInPath() works on Bezier curves");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(25, 25);
+ ctx.bezierCurveTo(50, -50, 50, 100, 75, 25);
+ _assertSame(ctx.isPointInPath(25, 20), false, "ctx.isPointInPath(25, 20)", "false");
+ _assertSame(ctx.isPointInPath(25, 30), false, "ctx.isPointInPath(25, 30)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), true, "ctx.isPointInPath(30, 20)", "true");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(40, 2), false, "ctx.isPointInPath(40, 2)", "false");
+ _assertSame(ctx.isPointInPath(40, 20), true, "ctx.isPointInPath(40, 20)", "true");
+ _assertSame(ctx.isPointInPath(40, 30), false, "ctx.isPointInPath(40, 30)", "false");
+ _assertSame(ctx.isPointInPath(40, 47), false, "ctx.isPointInPath(40, 47)", "false");
+ _assertSame(ctx.isPointInPath(45, 20), true, "ctx.isPointInPath(45, 20)", "true");
+ _assertSame(ctx.isPointInPath(45, 30), false, "ctx.isPointInPath(45, 30)", "false");
+ _assertSame(ctx.isPointInPath(55, 20), false, "ctx.isPointInPath(55, 20)", "false");
+ _assertSame(ctx.isPointInPath(55, 30), true, "ctx.isPointInPath(55, 30)", "true");
+ _assertSame(ctx.isPointInPath(60, 2), false, "ctx.isPointInPath(60, 2)", "false");
+ _assertSame(ctx.isPointInPath(60, 20), false, "ctx.isPointInPath(60, 20)", "false");
+ _assertSame(ctx.isPointInPath(60, 30), true, "ctx.isPointInPath(60, 30)", "true");
+ _assertSame(ctx.isPointInPath(60, 47), false, "ctx.isPointInPath(60, 47)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), true, "ctx.isPointInPath(70, 30)", "true");
+ _assertSame(ctx.isPointInPath(75, 20), false, "ctx.isPointInPath(75, 20)", "false");
+ _assertSame(ctx.isPointInPath(75, 30), false, "ctx.isPointInPath(75, 30)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.worker.js
new file mode 100644
index 0000000000..894fe5da31
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bezier.worker.js
@@ -0,0 +1,43 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.bezier
+// Description:isPointInPath() works on Bezier curves
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() works on Bezier curves");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(25, 25);
+ ctx.bezierCurveTo(50, -50, 50, 100, 75, 25);
+ _assertSame(ctx.isPointInPath(25, 20), false, "ctx.isPointInPath(25, 20)", "false");
+ _assertSame(ctx.isPointInPath(25, 30), false, "ctx.isPointInPath(25, 30)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), true, "ctx.isPointInPath(30, 20)", "true");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(40, 2), false, "ctx.isPointInPath(40, 2)", "false");
+ _assertSame(ctx.isPointInPath(40, 20), true, "ctx.isPointInPath(40, 20)", "true");
+ _assertSame(ctx.isPointInPath(40, 30), false, "ctx.isPointInPath(40, 30)", "false");
+ _assertSame(ctx.isPointInPath(40, 47), false, "ctx.isPointInPath(40, 47)", "false");
+ _assertSame(ctx.isPointInPath(45, 20), true, "ctx.isPointInPath(45, 20)", "true");
+ _assertSame(ctx.isPointInPath(45, 30), false, "ctx.isPointInPath(45, 30)", "false");
+ _assertSame(ctx.isPointInPath(55, 20), false, "ctx.isPointInPath(55, 20)", "false");
+ _assertSame(ctx.isPointInPath(55, 30), true, "ctx.isPointInPath(55, 30)", "true");
+ _assertSame(ctx.isPointInPath(60, 2), false, "ctx.isPointInPath(60, 2)", "false");
+ _assertSame(ctx.isPointInPath(60, 20), false, "ctx.isPointInPath(60, 20)", "false");
+ _assertSame(ctx.isPointInPath(60, 30), true, "ctx.isPointInPath(60, 30)", "true");
+ _assertSame(ctx.isPointInPath(60, 47), false, "ctx.isPointInPath(60, 47)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), true, "ctx.isPointInPath(70, 30)", "true");
+ _assertSame(ctx.isPointInPath(75, 20), false, "ctx.isPointInPath(75, 20)", "false");
+ _assertSame(ctx.isPointInPath(75, 30), false, "ctx.isPointInPath(75, 30)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.html
new file mode 100644
index 0000000000..fcb4ca8827
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.bigarc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.bigarc</h1>
+<p class="desc">isPointInPath() works on unclosed arcs larger than 2pi</p>
+
+
+<script>
+var t = async_test("isPointInPath() works on unclosed arcs larger than 2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.arc(50, 25, 10, 0, 7, false);
+ _assertSame(ctx.isPointInPath(50, 10), false, "ctx.isPointInPath(50, 10)", "false");
+ _assertSame(ctx.isPointInPath(50, 20), true, "ctx.isPointInPath(50, 20)", "true");
+ _assertSame(ctx.isPointInPath(50, 30), true, "ctx.isPointInPath(50, 30)", "true");
+ _assertSame(ctx.isPointInPath(50, 40), false, "ctx.isPointInPath(50, 40)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), false, "ctx.isPointInPath(70, 30)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.worker.js
new file mode 100644
index 0000000000..40233790c8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.bigarc.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.bigarc
+// Description:isPointInPath() works on unclosed arcs larger than 2pi
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() works on unclosed arcs larger than 2pi");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.arc(50, 25, 10, 0, 7, false);
+ _assertSame(ctx.isPointInPath(50, 10), false, "ctx.isPointInPath(50, 10)", "false");
+ _assertSame(ctx.isPointInPath(50, 20), true, "ctx.isPointInPath(50, 20)", "true");
+ _assertSame(ctx.isPointInPath(50, 30), true, "ctx.isPointInPath(50, 30)", "true");
+ _assertSame(ctx.isPointInPath(50, 40), false, "ctx.isPointInPath(50, 40)", "false");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ _assertSame(ctx.isPointInPath(70, 20), false, "ctx.isPointInPath(70, 20)", "false");
+ _assertSame(ctx.isPointInPath(30, 30), false, "ctx.isPointInPath(30, 30)", "false");
+ _assertSame(ctx.isPointInPath(70, 30), false, "ctx.isPointInPath(70, 30)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.html
new file mode 100644
index 0000000000..aa367b4e56
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.edge</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.edge</h1>
+<p class="desc">isPointInPath() counts points on the path as being inside</p>
+
+
+<script>
+var t = async_test("isPointInPath() counts points on the path as being inside");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0), true, "ctx.isPointInPath(0, 0)", "true");
+ _assertSame(ctx.isPointInPath(10, 0), true, "ctx.isPointInPath(10, 0)", "true");
+ _assertSame(ctx.isPointInPath(20, 0), true, "ctx.isPointInPath(20, 0)", "true");
+ _assertSame(ctx.isPointInPath(20, 10), true, "ctx.isPointInPath(20, 10)", "true");
+ _assertSame(ctx.isPointInPath(20, 20), true, "ctx.isPointInPath(20, 20)", "true");
+ _assertSame(ctx.isPointInPath(10, 20), true, "ctx.isPointInPath(10, 20)", "true");
+ _assertSame(ctx.isPointInPath(0, 20), true, "ctx.isPointInPath(0, 20)", "true");
+ _assertSame(ctx.isPointInPath(0, 10), true, "ctx.isPointInPath(0, 10)", "true");
+ _assertSame(ctx.isPointInPath(10, -0.01), false, "ctx.isPointInPath(10, -0.01)", "false");
+ _assertSame(ctx.isPointInPath(10, 20.01), false, "ctx.isPointInPath(10, 20.01)", "false");
+ _assertSame(ctx.isPointInPath(-0.01, 10), false, "ctx.isPointInPath(-0.01, 10)", "false");
+ _assertSame(ctx.isPointInPath(20.01, 10), false, "ctx.isPointInPath(20.01, 10)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.worker.js
new file mode 100644
index 0000000000..3918a6fe83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.edge.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.edge
+// Description:isPointInPath() counts points on the path as being inside
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() counts points on the path as being inside");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(0, 0), true, "ctx.isPointInPath(0, 0)", "true");
+ _assertSame(ctx.isPointInPath(10, 0), true, "ctx.isPointInPath(10, 0)", "true");
+ _assertSame(ctx.isPointInPath(20, 0), true, "ctx.isPointInPath(20, 0)", "true");
+ _assertSame(ctx.isPointInPath(20, 10), true, "ctx.isPointInPath(20, 10)", "true");
+ _assertSame(ctx.isPointInPath(20, 20), true, "ctx.isPointInPath(20, 20)", "true");
+ _assertSame(ctx.isPointInPath(10, 20), true, "ctx.isPointInPath(10, 20)", "true");
+ _assertSame(ctx.isPointInPath(0, 20), true, "ctx.isPointInPath(0, 20)", "true");
+ _assertSame(ctx.isPointInPath(0, 10), true, "ctx.isPointInPath(0, 10)", "true");
+ _assertSame(ctx.isPointInPath(10, -0.01), false, "ctx.isPointInPath(10, -0.01)", "false");
+ _assertSame(ctx.isPointInPath(10, 20.01), false, "ctx.isPointInPath(10, 20.01)", "false");
+ _assertSame(ctx.isPointInPath(-0.01, 10), false, "ctx.isPointInPath(-0.01, 10)", "false");
+ _assertSame(ctx.isPointInPath(20.01, 10), false, "ctx.isPointInPath(20.01, 10)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.html
new file mode 100644
index 0000000000..d2ebf1e7e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.empty</h1>
+<p class="desc">isPointInPath() works when there is no path</p>
+
+
+<script>
+var t = async_test("isPointInPath() works when there is no path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.isPointInPath(0, 0), false, "ctx.isPointInPath(0, 0)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.worker.js
new file mode 100644
index 0000000000..319836dcb5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.empty.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.empty
+// Description:isPointInPath() works when there is no path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() works when there is no path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.isPointInPath(0, 0), false, "ctx.isPointInPath(0, 0)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.html
new file mode 100644
index 0000000000..ef12d057ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.nonfinite</h1>
+<p class="desc">isPointInPath() returns false for non-finite arguments</p>
+
+
+<script>
+var t = async_test("isPointInPath() returns false for non-finite arguments");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(-100, -50, 200, 100);
+ _assertSame(ctx.isPointInPath(Infinity, 0), false, "ctx.isPointInPath(Infinity, 0)", "false");
+ _assertSame(ctx.isPointInPath(-Infinity, 0), false, "ctx.isPointInPath(-Infinity, 0)", "false");
+ _assertSame(ctx.isPointInPath(NaN, 0), false, "ctx.isPointInPath(NaN, 0)", "false");
+ _assertSame(ctx.isPointInPath(0, Infinity), false, "ctx.isPointInPath(0, Infinity)", "false");
+ _assertSame(ctx.isPointInPath(0, -Infinity), false, "ctx.isPointInPath(0, -Infinity)", "false");
+ _assertSame(ctx.isPointInPath(0, NaN), false, "ctx.isPointInPath(0, NaN)", "false");
+ _assertSame(ctx.isPointInPath(NaN, NaN), false, "ctx.isPointInPath(NaN, NaN)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.worker.js
new file mode 100644
index 0000000000..dfa6eec766
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.nonfinite.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.nonfinite
+// Description:isPointInPath() returns false for non-finite arguments
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() returns false for non-finite arguments");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(-100, -50, 200, 100);
+ _assertSame(ctx.isPointInPath(Infinity, 0), false, "ctx.isPointInPath(Infinity, 0)", "false");
+ _assertSame(ctx.isPointInPath(-Infinity, 0), false, "ctx.isPointInPath(-Infinity, 0)", "false");
+ _assertSame(ctx.isPointInPath(NaN, 0), false, "ctx.isPointInPath(NaN, 0)", "false");
+ _assertSame(ctx.isPointInPath(0, Infinity), false, "ctx.isPointInPath(0, Infinity)", "false");
+ _assertSame(ctx.isPointInPath(0, -Infinity), false, "ctx.isPointInPath(0, -Infinity)", "false");
+ _assertSame(ctx.isPointInPath(0, NaN), false, "ctx.isPointInPath(0, NaN)", "false");
+ _assertSame(ctx.isPointInPath(NaN, NaN), false, "ctx.isPointInPath(NaN, NaN)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.html
new file mode 100644
index 0000000000..ce2ab819dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.outside</h1>
+<p class="desc">isPointInPath() works on paths outside the canvas</p>
+
+
+<script>
+var t = async_test("isPointInPath() works on paths outside the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, -100, 20, 20);
+ ctx.rect(20, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(10, -110), false, "ctx.isPointInPath(10, -110)", "false");
+ _assertSame(ctx.isPointInPath(10, -90), true, "ctx.isPointInPath(10, -90)", "true");
+ _assertSame(ctx.isPointInPath(10, -70), false, "ctx.isPointInPath(10, -70)", "false");
+ _assertSame(ctx.isPointInPath(30, -20), false, "ctx.isPointInPath(30, -20)", "false");
+ _assertSame(ctx.isPointInPath(30, 0), true, "ctx.isPointInPath(30, 0)", "true");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.worker.js
new file mode 100644
index 0000000000..d4db12ef37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.outside.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.outside
+// Description:isPointInPath() works on paths outside the canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() works on paths outside the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, -100, 20, 20);
+ ctx.rect(20, -10, 20, 20);
+ _assertSame(ctx.isPointInPath(10, -110), false, "ctx.isPointInPath(10, -110)", "false");
+ _assertSame(ctx.isPointInPath(10, -90), true, "ctx.isPointInPath(10, -90)", "true");
+ _assertSame(ctx.isPointInPath(10, -70), false, "ctx.isPointInPath(10, -70)", "false");
+ _assertSame(ctx.isPointInPath(30, -20), false, "ctx.isPointInPath(30, -20)", "false");
+ _assertSame(ctx.isPointInPath(30, 0), true, "ctx.isPointInPath(30, 0)", "true");
+ _assertSame(ctx.isPointInPath(30, 20), false, "ctx.isPointInPath(30, 20)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.html
new file mode 100644
index 0000000000..c4abd92764
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.subpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.subpath</h1>
+<p class="desc">isPointInPath() uses the current path, not just the subpath</p>
+
+
+<script>
+var t = async_test("isPointInPath() uses the current path, not just the subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, 0, 20, 20);
+ ctx.beginPath();
+ ctx.rect(20, 0, 20, 20);
+ ctx.closePath();
+ ctx.rect(40, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(30, 10), true, "ctx.isPointInPath(30, 10)", "true");
+ _assertSame(ctx.isPointInPath(50, 10), true, "ctx.isPointInPath(50, 10)", "true");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.worker.js
new file mode 100644
index 0000000000..82df3b7c70
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.subpath.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.subpath
+// Description:isPointInPath() uses the current path, not just the subpath
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() uses the current path, not just the subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(0, 0, 20, 20);
+ ctx.beginPath();
+ ctx.rect(20, 0, 20, 20);
+ ctx.closePath();
+ ctx.rect(40, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(30, 10), true, "ctx.isPointInPath(30, 10)", "true");
+ _assertSame(ctx.isPointInPath(50, 10), true, "ctx.isPointInPath(50, 10)", "true");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.html
new file mode 100644
index 0000000000..bccaa842e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.transform.1</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(50, 0);
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.worker.js
new file mode 100644
index 0000000000..0774fd0d62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.transform.1
+// Description:isPointInPath() handles transformations correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(50, 0);
+ ctx.rect(0, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.html
new file mode 100644
index 0000000000..1e587c1d8a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.transform.2</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(50, 0);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.worker.js
new file mode 100644
index 0000000000..ff4c42c450
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.2.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.transform.2
+// Description:isPointInPath() handles transformations correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(50, 0);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.html
new file mode 100644
index 0000000000..826d7e23c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.transform.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.transform.3</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(-1, 1);
+ ctx.rect(-70, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.worker.js
new file mode 100644
index 0000000000..f6750e9109
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.3.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.transform.3
+// Description:isPointInPath() handles transformations correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(-1, 1);
+ ctx.rect(-70, 0, 20, 20);
+ _assertSame(ctx.isPointInPath(-40, 10), false, "ctx.isPointInPath(-40, 10)", "false");
+ _assertSame(ctx.isPointInPath(10, 10), false, "ctx.isPointInPath(10, 10)", "false");
+ _assertSame(ctx.isPointInPath(49, 10), false, "ctx.isPointInPath(49, 10)", "false");
+ _assertSame(ctx.isPointInPath(51, 10), true, "ctx.isPointInPath(51, 10)", "true");
+ _assertSame(ctx.isPointInPath(69, 10), true, "ctx.isPointInPath(69, 10)", "true");
+ _assertSame(ctx.isPointInPath(71, 10), false, "ctx.isPointInPath(71, 10)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.html
new file mode 100644
index 0000000000..97792d6889
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.transform.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.transform.4</h1>
+<p class="desc">isPointInPath() handles transformations correctly</p>
+
+
+<script>
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(50, 0);
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(0, 50);
+ _assertSame(ctx.isPointInPath(60, 10), false, "ctx.isPointInPath(60, 10)", "false");
+ _assertSame(ctx.isPointInPath(110, 10), true, "ctx.isPointInPath(110, 10)", "true");
+ _assertSame(ctx.isPointInPath(110, 60), false, "ctx.isPointInPath(110, 60)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.worker.js
new file mode 100644
index 0000000000..f43a8a3d1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.transform.4.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.transform.4
+// Description:isPointInPath() handles transformations correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() handles transformations correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.translate(50, 0);
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(0, 50);
+ _assertSame(ctx.isPointInPath(60, 10), false, "ctx.isPointInPath(60, 10)", "false");
+ _assertSame(ctx.isPointInPath(110, 10), true, "ctx.isPointInPath(110, 10)", "true");
+ _assertSame(ctx.isPointInPath(110, 60), false, "ctx.isPointInPath(110, 60)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.html
new file mode 100644
index 0000000000..3920c7de4b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.unclosed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.unclosed</h1>
+<p class="desc">isPointInPath() works on unclosed subpaths</p>
+
+
+<script>
+var t = async_test("isPointInPath() works on unclosed subpaths");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(20, 0);
+ ctx.lineTo(20, 20);
+ ctx.lineTo(0, 20);
+ _assertSame(ctx.isPointInPath(10, 10), true, "ctx.isPointInPath(10, 10)", "true");
+ _assertSame(ctx.isPointInPath(30, 10), false, "ctx.isPointInPath(30, 10)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.worker.js
new file mode 100644
index 0000000000..7b5ffc4a79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.unclosed.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.unclosed
+// Description:isPointInPath() works on unclosed subpaths
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() works on unclosed subpaths");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(20, 0);
+ ctx.lineTo(20, 20);
+ ctx.lineTo(0, 20);
+ _assertSame(ctx.isPointInPath(10, 10), true, "ctx.isPointInPath(10, 10)", "true");
+ _assertSame(ctx.isPointInPath(30, 10), false, "ctx.isPointInPath(30, 10)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.html
new file mode 100644
index 0000000000..b2741a5069
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInPath.winding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInPath.winding</h1>
+<p class="desc">isPointInPath() uses the non-zero winding number rule</p>
+
+
+<script>
+var t = async_test("isPointInPath() uses the non-zero winding number rule");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Create a square ring, using opposite windings to make a hole in the centre
+ ctx.moveTo(0, 0);
+ ctx.lineTo(50, 0);
+ ctx.lineTo(50, 50);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.lineTo(10, 40);
+ ctx.lineTo(40, 40);
+ ctx.lineTo(40, 10);
+ ctx.lineTo(10, 10);
+
+ _assertSame(ctx.isPointInPath(5, 5), true, "ctx.isPointInPath(5, 5)", "true");
+ _assertSame(ctx.isPointInPath(25, 5), true, "ctx.isPointInPath(25, 5)", "true");
+ _assertSame(ctx.isPointInPath(45, 5), true, "ctx.isPointInPath(45, 5)", "true");
+ _assertSame(ctx.isPointInPath(5, 25), true, "ctx.isPointInPath(5, 25)", "true");
+ _assertSame(ctx.isPointInPath(25, 25), false, "ctx.isPointInPath(25, 25)", "false");
+ _assertSame(ctx.isPointInPath(45, 25), true, "ctx.isPointInPath(45, 25)", "true");
+ _assertSame(ctx.isPointInPath(5, 45), true, "ctx.isPointInPath(5, 45)", "true");
+ _assertSame(ctx.isPointInPath(25, 45), true, "ctx.isPointInPath(25, 45)", "true");
+ _assertSame(ctx.isPointInPath(45, 45), true, "ctx.isPointInPath(45, 45)", "true");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.worker.js
new file mode 100644
index 0000000000..094bc05910
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInPath.winding.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInPath.winding
+// Description:isPointInPath() uses the non-zero winding number rule
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInPath() uses the non-zero winding number rule");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Create a square ring, using opposite windings to make a hole in the centre
+ ctx.moveTo(0, 0);
+ ctx.lineTo(50, 0);
+ ctx.lineTo(50, 50);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.lineTo(10, 40);
+ ctx.lineTo(40, 40);
+ ctx.lineTo(40, 10);
+ ctx.lineTo(10, 10);
+
+ _assertSame(ctx.isPointInPath(5, 5), true, "ctx.isPointInPath(5, 5)", "true");
+ _assertSame(ctx.isPointInPath(25, 5), true, "ctx.isPointInPath(25, 5)", "true");
+ _assertSame(ctx.isPointInPath(45, 5), true, "ctx.isPointInPath(45, 5)", "true");
+ _assertSame(ctx.isPointInPath(5, 25), true, "ctx.isPointInPath(5, 25)", "true");
+ _assertSame(ctx.isPointInPath(25, 25), false, "ctx.isPointInPath(25, 25)", "false");
+ _assertSame(ctx.isPointInPath(45, 25), true, "ctx.isPointInPath(45, 25)", "true");
+ _assertSame(ctx.isPointInPath(5, 45), true, "ctx.isPointInPath(5, 45)", "true");
+ _assertSame(ctx.isPointInPath(25, 45), true, "ctx.isPointInPath(25, 45)", "true");
+ _assertSame(ctx.isPointInPath(45, 45), true, "ctx.isPointInPath(45, 45)", "true");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.html
new file mode 100644
index 0000000000..95f7fd721d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInStroke.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInStroke.basic</h1>
+<p class="desc">detects whether point is in the area contained by the stroke of the path</p>
+
+
+<script>
+var t = async_test("detects whether point is in the area contained by the stroke of the path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var canvas = new OffscreenCanvas(100, 50);
+var ctx = canvas.getContext('2d');
+
+ctx.strokeStyle = '#0f0';
+ctx.beginPath();
+ctx.rect(0, 0, 20, 20);
+_assertSame(ctx.isPointInStroke(0, 0), true, "ctx.isPointInStroke(0, 0)", "true");
+_assertSame(ctx.isPointInStroke(30, 10), false, "ctx.isPointInStroke(30, 10)", "false");
+
+var path = new Path2D();
+path.rect(20, 20, 100, 100);
+_assertSame(ctx.isPointInStroke(path, 20, 20), true, "ctx.isPointInStroke(path, 20, 20)", "true");
+_assertSame(ctx.isPointInStroke(path, 120, 20), true, "ctx.isPointInStroke(path, 120, 20)", "true");
+t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.worker.js
new file mode 100644
index 0000000000..844f0ef154
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.basic.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInStroke.basic
+// Description:detects whether point is in the area contained by the stroke of the path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("detects whether point is in the area contained by the stroke of the path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var canvas = new OffscreenCanvas(100, 50);
+var ctx = canvas.getContext('2d');
+
+ctx.strokeStyle = '#0f0';
+ctx.beginPath();
+ctx.rect(0, 0, 20, 20);
+_assertSame(ctx.isPointInStroke(0, 0), true, "ctx.isPointInStroke(0, 0)", "true");
+_assertSame(ctx.isPointInStroke(30, 10), false, "ctx.isPointInStroke(30, 10)", "false");
+
+var path = new Path2D();
+path.rect(20, 20, 100, 100);
+_assertSame(ctx.isPointInStroke(path, 20, 20), true, "ctx.isPointInStroke(path, 20, 20)", "true");
+_assertSame(ctx.isPointInStroke(path, 120, 20), true, "ctx.isPointInStroke(path, 120, 20)", "true");
+t.done();
+
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.html
new file mode 100644
index 0000000000..1ed13b32de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInStroke.scaleddashes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInStroke.scaleddashes</h1>
+<p class="desc">isPointInStroke() should return correct results on dashed paths at high scale factors</p>
+
+
+<script>
+var t = async_test("isPointInStroke() should return correct results on dashed paths at high scale factors");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var scale = 20;
+ ctx.setLineDash([10, 21.4159]); // dash from t=0 to t=10 along the circle
+ ctx.scale(scale, scale);
+ ctx.ellipse(6, 10, 5, 5, 0, 2*Math.PI, false);
+ ctx.stroke();
+
+ // hit-test the beginning of the dash (t=0)
+ _assertSame(ctx.isPointInStroke(11*scale, 10*scale), true, "ctx.isPointInStroke(11*scale, 10*scale)", "true");
+ // hit-test the middle of the dash (t=5)
+ _assertSame(ctx.isPointInStroke(8.70*scale, 14.21*scale), true, "ctx.isPointInStroke(8.70*scale, 14.21*scale)", "true");
+ // hit-test the end of the dash (t=9.8)
+ _assertSame(ctx.isPointInStroke(4.10*scale, 14.63*scale), true, "ctx.isPointInStroke(4.10*scale, 14.63*scale)", "true");
+ // hit-test past the end of the dash (t=10.2)
+ _assertSame(ctx.isPointInStroke(3.74*scale, 14.46*scale), false, "ctx.isPointInStroke(3.74*scale, 14.46*scale)", "false");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.worker.js
new file mode 100644
index 0000000000..240905308e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInStroke.scaleddashes.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInStroke.scaleddashes
+// Description:isPointInStroke() should return correct results on dashed paths at high scale factors
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("isPointInStroke() should return correct results on dashed paths at high scale factors");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var scale = 20;
+ ctx.setLineDash([10, 21.4159]); // dash from t=0 to t=10 along the circle
+ ctx.scale(scale, scale);
+ ctx.ellipse(6, 10, 5, 5, 0, 2*Math.PI, false);
+ ctx.stroke();
+
+ // hit-test the beginning of the dash (t=0)
+ _assertSame(ctx.isPointInStroke(11*scale, 10*scale), true, "ctx.isPointInStroke(11*scale, 10*scale)", "true");
+ // hit-test the middle of the dash (t=5)
+ _assertSame(ctx.isPointInStroke(8.70*scale, 14.21*scale), true, "ctx.isPointInStroke(8.70*scale, 14.21*scale)", "true");
+ // hit-test the end of the dash (t=9.8)
+ _assertSame(ctx.isPointInStroke(4.10*scale, 14.63*scale), true, "ctx.isPointInStroke(4.10*scale, 14.63*scale)", "true");
+ // hit-test past the end of the dash (t=10.2)
+ _assertSame(ctx.isPointInStroke(3.74*scale, 14.46*scale), false, "ctx.isPointInStroke(3.74*scale, 14.46*scale)", "false");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.html
new file mode 100644
index 0000000000..0b4c635fc8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInpath.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInpath.invalid</h1>
+<p class="desc">Verify isPointInPath throws exceptions with invalid inputs.</p>
+
+
+<script>
+var t = async_test("Verify isPointInPath throws exceptions with invalid inputs.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ canvas.height = 200;
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ // Testing invalid enumeration isPointInPath (w/ and w/o Path object');
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(path, 50, 50, 'gazonk'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(50, 50, 'gazonk'); });
+
+ // Testing invalid type isPointInPath with Path object');
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, null); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(path, 50, 50, null); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, undefined); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50, 'evenodd'); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.worker.js
new file mode 100644
index 0000000000..f09eeb7526
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.invalid.worker.js
@@ -0,0 +1,46 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInpath.invalid
+// Description:Verify isPointInPath throws exceptions with invalid inputs.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify isPointInPath throws exceptions with invalid inputs.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ canvas.height = 200;
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ // Testing invalid enumeration isPointInPath (w/ and w/o Path object');
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(path, 50, 50, 'gazonk'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(50, 50, 'gazonk'); });
+
+ // Testing invalid type isPointInPath with Path object');
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(null, 50, 50, null); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(path, 50, 50, null); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath(undefined, 50, 50, undefined); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath([], 50, 50, 'evenodd'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50, 'nonzero'); });
+ assert_throws_js(TypeError, function() { ctx.isPointInPath({}, 50, 50, 'evenodd'); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.html
new file mode 100644
index 0000000000..945855231b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.isPointInpath.multi.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.isPointInpath.multi.path</h1>
+<p class="desc">Verify the winding rule in isPointInPath works for path object.</p>
+
+
+<script>
+var t = async_test("Verify the winding rule in isPointInPath works for path object.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(path, 50, 50), true, "ctx.isPointInPath(path, 50, 50)", "true");
+ _assertSame(ctx.isPointInPath(path, 50, 50, undefined), true, "ctx.isPointInPath(path, 50, 50, undefined)", "true");
+ _assertSame(ctx.isPointInPath(path, NaN, 50), false, "ctx.isPointInPath(path, NaN, 50)", "false");
+ _assertSame(ctx.isPointInPath(path, 50, NaN), false, "ctx.isPointInPath(path, 50, NaN)", "false");
+
+ // Testing nonzero isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(path, 50, 50, 'nonzero'), true, "ctx.isPointInPath(path, 50, 50, 'nonzero')", "true");
+
+ // Testing evenodd isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ assert_false(ctx.isPointInPath(path, 50, 50, 'evenodd'));
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.worker.js
new file mode 100644
index 0000000000..d3d4e60747
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.isPointInpath.multi.path.worker.js
@@ -0,0 +1,44 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.isPointInpath.multi.path
+// Description:Verify the winding rule in isPointInPath works for path object.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify the winding rule in isPointInPath works for path object.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(path, 50, 50), true, "ctx.isPointInPath(path, 50, 50)", "true");
+ _assertSame(ctx.isPointInPath(path, 50, 50, undefined), true, "ctx.isPointInPath(path, 50, 50, undefined)", "true");
+ _assertSame(ctx.isPointInPath(path, NaN, 50), false, "ctx.isPointInPath(path, NaN, 50)", "false");
+ _assertSame(ctx.isPointInPath(path, 50, NaN), false, "ctx.isPointInPath(path, 50, NaN)", "false");
+
+ // Testing nonzero isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ _assertSame(ctx.isPointInPath(path, 50, 50, 'nonzero'), true, "ctx.isPointInPath(path, 50, 50, 'nonzero')", "true");
+
+ // Testing evenodd isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ assert_false(ctx.isPointInPath(path, 50, 50, 'evenodd'));
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.html
new file mode 100644
index 0000000000..e0bc64617f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.lineTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.lineTo.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.worker.js
new file mode 100644
index 0000000000..6e02a1f149
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.basic.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.lineTo.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..2561a2f555
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.lineTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.lineTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the point is added and nothing is drawn</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the point is added and nothing is drawn");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.worker.js
new file mode 100644
index 0000000000..ae4d85735e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.lineTo.ensuresubpath.1
+// Description:If there is no subpath, the point is added and nothing is drawn
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the point is added and nothing is drawn");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..9ca14a1393
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.lineTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.lineTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the point is added and used for subsequent drawing</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the point is added and used for subsequent drawing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.worker.js
new file mode 100644
index 0000000000..b6af21235f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.ensuresubpath.2.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.lineTo.ensuresubpath.2
+// Description:If there is no subpath, the point is added and used for subsequent drawing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the point is added and used for subsequent drawing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.html
new file mode 100644
index 0000000000..a649d3a86d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.lineTo.nextpoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.lineTo.nextpoint</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.worker.js
new file mode 100644
index 0000000000..9e4e24a06d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nextpoint.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.lineTo.nextpoint
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.html
new file mode 100644
index 0000000000..35bc80d336
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.lineTo.nonfinite.details</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.lineTo.nonfinite.details</h1>
+<p class="desc">lineTo() with Infinity/NaN for first arg still converts the second arg</p>
+
+
+<script>
+var t = async_test("lineTo() with Infinity/NaN for first arg still converts the second arg");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ for (var arg1 of [Infinity, -Infinity, NaN]) {
+ var converted = false;
+ ctx.lineTo(arg1, { valueOf: function() { converted = true; return 0; } });
+ _assert(converted, "converted");
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.worker.js
new file mode 100644
index 0000000000..5052c74b94
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.details.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.lineTo.nonfinite.details
+// Description:lineTo() with Infinity/NaN for first arg still converts the second arg
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineTo() with Infinity/NaN for first arg still converts the second arg");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ for (var arg1 of [Infinity, -Infinity, NaN]) {
+ var converted = false;
+ ctx.lineTo(arg1, { valueOf: function() { converted = true; return 0; } });
+ _assert(converted, "converted");
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.html
new file mode 100644
index 0000000000..5a144dd7f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.lineTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.lineTo.nonfinite</h1>
+<p class="desc">lineTo() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("lineTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(Infinity, 50);
+ ctx.lineTo(-Infinity, 50);
+ ctx.lineTo(NaN, 50);
+ ctx.lineTo(0, Infinity);
+ ctx.lineTo(0, -Infinity);
+ ctx.lineTo(0, NaN);
+ ctx.lineTo(Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.worker.js
new file mode 100644
index 0000000000..82921b7cd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.lineTo.nonfinite.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.lineTo.nonfinite
+// Description:lineTo() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("lineTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(Infinity, 50);
+ ctx.lineTo(-Infinity, 50);
+ ctx.lineTo(NaN, 50);
+ ctx.lineTo(0, Infinity);
+ ctx.lineTo(0, -Infinity);
+ ctx.lineTo(0, NaN);
+ ctx.lineTo(Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.html
new file mode 100644
index 0000000000..f9777c6ca8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.moveTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.moveTo.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 10, 50);
+ ctx.moveTo(100, 0);
+ ctx.lineTo(10, 0);
+ ctx.lineTo(10, 50);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 90,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.worker.js
new file mode 100644
index 0000000000..0b1df374b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.basic.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.moveTo.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 10, 50);
+ ctx.moveTo(100, 0);
+ ctx.lineTo(10, 0);
+ ctx.lineTo(10, 50);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 90,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.html
new file mode 100644
index 0000000000..0b56cbbfef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.moveTo.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.moveTo.multiple</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 25);
+ ctx.moveTo(100, 25);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.worker.js
new file mode 100644
index 0000000000..9f49ea8a1b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.multiple.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.moveTo.multiple
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 25);
+ ctx.moveTo(100, 25);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.html
new file mode 100644
index 0000000000..c2594c006e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.moveTo.newsubpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.moveTo.newsubpath</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.moveTo(100, 0);
+ ctx.moveTo(100, 50);
+ ctx.moveTo(0, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.worker.js
new file mode 100644
index 0000000000..e3c9c8529d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.newsubpath.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.moveTo.newsubpath
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.moveTo(100, 0);
+ ctx.moveTo(100, 50);
+ ctx.moveTo(0, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.html
new file mode 100644
index 0000000000..ea3eb34c76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.moveTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.moveTo.nonfinite</h1>
+<p class="desc">moveTo() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("moveTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.moveTo(Infinity, 50);
+ ctx.moveTo(-Infinity, 50);
+ ctx.moveTo(NaN, 50);
+ ctx.moveTo(0, Infinity);
+ ctx.moveTo(0, -Infinity);
+ ctx.moveTo(0, NaN);
+ ctx.moveTo(Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.worker.js
new file mode 100644
index 0000000000..97e2bd72e3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.moveTo.nonfinite.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.moveTo.nonfinite
+// Description:moveTo() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("moveTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.moveTo(Infinity, 50);
+ ctx.moveTo(-Infinity, 50);
+ ctx.moveTo(NaN, 50);
+ ctx.moveTo(0, Infinity);
+ ctx.moveTo(0, -Infinity);
+ ctx.moveTo(0, NaN);
+ ctx.moveTo(Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.html
new file mode 100644
index 0000000000..4d14ce5585
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.quadraticCurveTo.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.quadraticCurveTo.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.quadraticCurveTo(100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.worker.js
new file mode 100644
index 0000000000..161e5f1a65
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.basic.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.quadraticCurveTo.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.quadraticCurveTo(100, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html
new file mode 100644
index 0000000000..3e916f00c7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.quadraticCurveTo.ensuresubpath.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.quadraticCurveTo.ensuresubpath.1</h1>
+<p class="desc">If there is no subpath, the first control point is added (and nothing is drawn up to it)</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(100, 50, 200, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 95,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.worker.js
new file mode 100644
index 0000000000..95dcf2bed1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.1.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.quadraticCurveTo.ensuresubpath.1
+// Description:If there is no subpath, the first control point is added (and nothing is drawn up to it)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the first control point is added (and nothing is drawn up to it)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(100, 50, 200, 50);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 95,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html
new file mode 100644
index 0000000000..2fb05a2658
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.quadraticCurveTo.ensuresubpath.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.quadraticCurveTo.ensuresubpath.2</h1>
+<p class="desc">If there is no subpath, the first control point is added</p>
+
+
+<script>
+var t = async_test("If there is no subpath, the first control point is added");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(0, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.worker.js
new file mode 100644
index 0000000000..4beaf9dae2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.ensuresubpath.2.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.quadraticCurveTo.ensuresubpath.2
+// Description:If there is no subpath, the first control point is added
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("If there is no subpath, the first control point is added");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(0, 25, 100, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.html
new file mode 100644
index 0000000000..ee4f333a08
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.quadraticCurveTo.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.quadraticCurveTo.nonfinite</h1>
+<p class="desc">quadraticCurveTo() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("quadraticCurveTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.quadraticCurveTo(Infinity, 50, 0, 50);
+ ctx.quadraticCurveTo(-Infinity, 50, 0, 50);
+ ctx.quadraticCurveTo(NaN, 50, 0, 50);
+ ctx.quadraticCurveTo(0, Infinity, 0, 50);
+ ctx.quadraticCurveTo(0, -Infinity, 0, 50);
+ ctx.quadraticCurveTo(0, NaN, 0, 50);
+ ctx.quadraticCurveTo(0, 50, Infinity, 50);
+ ctx.quadraticCurveTo(0, 50, -Infinity, 50);
+ ctx.quadraticCurveTo(0, 50, NaN, 50);
+ ctx.quadraticCurveTo(0, 50, 0, Infinity);
+ ctx.quadraticCurveTo(0, 50, 0, -Infinity);
+ ctx.quadraticCurveTo(0, 50, 0, NaN);
+ ctx.quadraticCurveTo(Infinity, Infinity, 0, 50);
+ ctx.quadraticCurveTo(Infinity, Infinity, Infinity, 50);
+ ctx.quadraticCurveTo(Infinity, Infinity, Infinity, Infinity);
+ ctx.quadraticCurveTo(Infinity, Infinity, 0, Infinity);
+ ctx.quadraticCurveTo(Infinity, 50, Infinity, 50);
+ ctx.quadraticCurveTo(Infinity, 50, Infinity, Infinity);
+ ctx.quadraticCurveTo(Infinity, 50, 0, Infinity);
+ ctx.quadraticCurveTo(0, Infinity, Infinity, 50);
+ ctx.quadraticCurveTo(0, Infinity, Infinity, Infinity);
+ ctx.quadraticCurveTo(0, Infinity, 0, Infinity);
+ ctx.quadraticCurveTo(0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.worker.js
new file mode 100644
index 0000000000..a61eb9a8ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.nonfinite.worker.js
@@ -0,0 +1,52 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.quadraticCurveTo.nonfinite
+// Description:quadraticCurveTo() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("quadraticCurveTo() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.quadraticCurveTo(Infinity, 50, 0, 50);
+ ctx.quadraticCurveTo(-Infinity, 50, 0, 50);
+ ctx.quadraticCurveTo(NaN, 50, 0, 50);
+ ctx.quadraticCurveTo(0, Infinity, 0, 50);
+ ctx.quadraticCurveTo(0, -Infinity, 0, 50);
+ ctx.quadraticCurveTo(0, NaN, 0, 50);
+ ctx.quadraticCurveTo(0, 50, Infinity, 50);
+ ctx.quadraticCurveTo(0, 50, -Infinity, 50);
+ ctx.quadraticCurveTo(0, 50, NaN, 50);
+ ctx.quadraticCurveTo(0, 50, 0, Infinity);
+ ctx.quadraticCurveTo(0, 50, 0, -Infinity);
+ ctx.quadraticCurveTo(0, 50, 0, NaN);
+ ctx.quadraticCurveTo(Infinity, Infinity, 0, 50);
+ ctx.quadraticCurveTo(Infinity, Infinity, Infinity, 50);
+ ctx.quadraticCurveTo(Infinity, Infinity, Infinity, Infinity);
+ ctx.quadraticCurveTo(Infinity, Infinity, 0, Infinity);
+ ctx.quadraticCurveTo(Infinity, 50, Infinity, 50);
+ ctx.quadraticCurveTo(Infinity, 50, Infinity, Infinity);
+ ctx.quadraticCurveTo(Infinity, 50, 0, Infinity);
+ ctx.quadraticCurveTo(0, Infinity, Infinity, 50);
+ ctx.quadraticCurveTo(0, Infinity, Infinity, Infinity);
+ ctx.quadraticCurveTo(0, Infinity, 0, Infinity);
+ ctx.quadraticCurveTo(0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.html
new file mode 100644
index 0000000000..92c9377d6f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.quadraticCurveTo.scaled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.quadraticCurveTo.scaled</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-1, 1.05);
+ ctx.quadraticCurveTo(0, -1, 1.2, 1.05);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.worker.js
new file mode 100644
index 0000000000..04fd6e904d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.scaled.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.quadraticCurveTo.scaled
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-1, 1.05);
+ ctx.quadraticCurveTo(0, -1, 1.2, 1.05);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.html
new file mode 100644
index 0000000000..ea42d669cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.quadraticCurveTo.shape</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.quadraticCurveTo.shape</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-1000, 1050);
+ ctx.quadraticCurveTo(0, -1000, 1200, 1050);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.worker.js
new file mode 100644
index 0000000000..9f2afea09d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.quadraticCurveTo.shape.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.quadraticCurveTo.shape
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-1000, 1050);
+ ctx.quadraticCurveTo(0, -1000, 1200, 1050);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.html
new file mode 100644
index 0000000000..e8d874709d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.worker.js
new file mode 100644
index 0000000000..c8eb3838f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.basic.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.html
new file mode 100644
index 0000000000..6f61874390
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.closed</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.rect(100, 50, 100, 100);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.worker.js
new file mode 100644
index 0000000000..f40868730b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.closed.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.closed
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.rect(100, 50, 100, 100);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.html
new file mode 100644
index 0000000000..e4d5ece132
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.end.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.end.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.rect(200, 100, 400, 1000);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.worker.js
new file mode 100644
index 0000000000..fcf9d0526f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.end.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.rect(200, 100, 400, 1000);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.html
new file mode 100644
index 0000000000..d6bad78abb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.end.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.end.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.rect(150, 150, 2000, 2000);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.worker.js
new file mode 100644
index 0000000000..84c1b71d71
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.end.2.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.end.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.rect(150, 150, 2000, 2000);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.html
new file mode 100644
index 0000000000..730e2e887a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.negative</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 50, 25);
+ ctx.rect(100, 0, -50, 25);
+ ctx.rect(0, 50, 50, -25);
+ ctx.rect(100, 50, -50, -25);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.worker.js
new file mode 100644
index 0000000000..44449e85e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.negative.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.negative
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 50, 25);
+ ctx.rect(100, 0, -50, 25);
+ ctx.rect(0, 50, 50, -25);
+ ctx.rect(100, 50, -50, -25);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.html
new file mode 100644
index 0000000000..05b972e045
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.newsubpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.newsubpath</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.rect(200, 25, 1, 1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.worker.js
new file mode 100644
index 0000000000..bdf4a926c1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.newsubpath.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.newsubpath
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.rect(200, 25, 1, 1);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.html
new file mode 100644
index 0000000000..16d76aaf18
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.nonfinite</h1>
+<p class="desc">rect() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("rect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.rect(Infinity, 50, 1, 1);
+ ctx.rect(-Infinity, 50, 1, 1);
+ ctx.rect(NaN, 50, 1, 1);
+ ctx.rect(0, Infinity, 1, 1);
+ ctx.rect(0, -Infinity, 1, 1);
+ ctx.rect(0, NaN, 1, 1);
+ ctx.rect(0, 50, Infinity, 1);
+ ctx.rect(0, 50, -Infinity, 1);
+ ctx.rect(0, 50, NaN, 1);
+ ctx.rect(0, 50, 1, Infinity);
+ ctx.rect(0, 50, 1, -Infinity);
+ ctx.rect(0, 50, 1, NaN);
+ ctx.rect(Infinity, Infinity, 1, 1);
+ ctx.rect(Infinity, Infinity, Infinity, 1);
+ ctx.rect(Infinity, Infinity, Infinity, Infinity);
+ ctx.rect(Infinity, Infinity, 1, Infinity);
+ ctx.rect(Infinity, 50, Infinity, 1);
+ ctx.rect(Infinity, 50, Infinity, Infinity);
+ ctx.rect(Infinity, 50, 1, Infinity);
+ ctx.rect(0, Infinity, Infinity, 1);
+ ctx.rect(0, Infinity, Infinity, Infinity);
+ ctx.rect(0, Infinity, 1, Infinity);
+ ctx.rect(0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.worker.js
new file mode 100644
index 0000000000..307a5ec08f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.nonfinite.worker.js
@@ -0,0 +1,52 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.nonfinite
+// Description:rect() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("rect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.rect(Infinity, 50, 1, 1);
+ ctx.rect(-Infinity, 50, 1, 1);
+ ctx.rect(NaN, 50, 1, 1);
+ ctx.rect(0, Infinity, 1, 1);
+ ctx.rect(0, -Infinity, 1, 1);
+ ctx.rect(0, NaN, 1, 1);
+ ctx.rect(0, 50, Infinity, 1);
+ ctx.rect(0, 50, -Infinity, 1);
+ ctx.rect(0, 50, NaN, 1);
+ ctx.rect(0, 50, 1, Infinity);
+ ctx.rect(0, 50, 1, -Infinity);
+ ctx.rect(0, 50, 1, NaN);
+ ctx.rect(Infinity, Infinity, 1, 1);
+ ctx.rect(Infinity, Infinity, Infinity, 1);
+ ctx.rect(Infinity, Infinity, Infinity, Infinity);
+ ctx.rect(Infinity, Infinity, 1, Infinity);
+ ctx.rect(Infinity, 50, Infinity, 1);
+ ctx.rect(Infinity, 50, Infinity, Infinity);
+ ctx.rect(Infinity, 50, 1, Infinity);
+ ctx.rect(0, Infinity, Infinity, 1);
+ ctx.rect(0, Infinity, Infinity, Infinity);
+ ctx.rect(0, Infinity, 1, Infinity);
+ ctx.rect(0, 50, Infinity, Infinity);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.html
new file mode 100644
index 0000000000..af97fa6d38
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.selfintersect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.selfintersect</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.rect(45, 20, 10, 10);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.worker.js
new file mode 100644
index 0000000000..495d016e80
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.selfintersect.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.selfintersect
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.rect(45, 20, 10, 10);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.html
new file mode 100644
index 0000000000..a7f79c5223
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.winding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.winding</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.rect(0, 0, 50, 50);
+ ctx.rect(100, 50, -50, -50);
+ ctx.rect(0, 25, 100, -25);
+ ctx.rect(100, 25, -100, 25);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.worker.js
new file mode 100644
index 0000000000..45ff0427e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.winding.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.winding
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.rect(0, 0, 50, 50);
+ ctx.rect(100, 50, -50, -50);
+ ctx.rect(0, 25, 100, -25);
+ ctx.rect(100, 25, -100, 25);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.html
new file mode 100644
index 0000000000..45dac2079d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.zero.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(0, 50, 100, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.worker.js
new file mode 100644
index 0000000000..de6fb58c8a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.zero.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(0, 50, 100, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.html
new file mode 100644
index 0000000000..0386ea06e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.zero.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, -100, 0, 250);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.worker.js
new file mode 100644
index 0000000000..8e323d2dd0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.2.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.zero.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, -100, 0, 250);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.html
new file mode 100644
index 0000000000..1299040929
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.zero.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.zero.3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.worker.js
new file mode 100644
index 0000000000..5e6f490c73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.3.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.zero.3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.html
new file mode 100644
index 0000000000..93da1300de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.zero.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.zero.4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.rect(100, 25, 0, 0);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.worker.js
new file mode 100644
index 0000000000..e19da3b917
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.4.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.zero.4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.rect(100, 25, 0, 0);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.html
new file mode 100644
index 0000000000..45a7f03813
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.zero.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.zero.5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.rect(100, 25, 0, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.worker.js
new file mode 100644
index 0000000000..d39a741573
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.5.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.zero.5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.rect(100, 25, 0, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.html
new file mode 100644
index 0000000000..ac74e20660
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.rect.zero.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.rect.zero.6</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.rect(100, 25, 1000, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.worker.js
new file mode 100644
index 0000000000..a567359c30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.rect.zero.6.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.rect.zero.6
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.rect(100, 25, 1000, 0);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.html
new file mode 100644
index 0000000000..f7e80a0119
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.1.radius.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.1.radius.dompoint</h1>
+<p class="desc">Verify that when one radius is given to roundRect(), specified as a DOMPoint, it applies to all corners.</p>
+
+
+<script>
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a DOMPoint, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html
new file mode 100644
index 0000000000..4ea78c32d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.1.radius.dompoint.single argument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.1.radius.dompoint.single argument</h1>
+<p class="desc">Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPoint, it applies to all corners.</p>
+
+
+<script>
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPoint, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, new DOMPoint(40, 20));
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.worker.js
new file mode 100644
index 0000000000..b93d88e89c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.single argument.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.1.radius.dompoint.single argument
+// Description:Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPoint, it applies to all corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPoint, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, new DOMPoint(40, 20));
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.worker.js
new file mode 100644
index 0000000000..06d787437e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompoint.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.1.radius.dompoint
+// Description:Verify that when one radius is given to roundRect(), specified as a DOMPoint, it applies to all corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a DOMPoint, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.html
new file mode 100644
index 0000000000..97084a446f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.1.radius.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.1.radius.dompointinit</h1>
+<p class="desc">Verify that when one radius is given to roundRect(), specified as a DOMPointInit, applies to all corners.</p>
+
+
+<script>
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a DOMPointInit, applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html
new file mode 100644
index 0000000000..10c5c14cff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.1.radius.dompointinit.single.argument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.1.radius.dompointinit.single.argument</h1>
+<p class="desc">Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPointInit, applies to all corners.</p>
+
+
+<script>
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPointInit, applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, {x: 40, y: 20});
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.worker.js
new file mode 100644
index 0000000000..a62370677b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.single.argument.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.1.radius.dompointinit.single.argument
+// Description:Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPointInit, applies to all corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPointInit, applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, {x: 40, y: 20});
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.worker.js
new file mode 100644
index 0000000000..56c8e1fcef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.dompointinit.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.1.radius.dompointinit
+// Description:Verify that when one radius is given to roundRect(), specified as a DOMPointInit, applies to all corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a DOMPointInit, applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.html
new file mode 100644
index 0000000000..33574eeaa0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.1.radius.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.1.radius.double</h1>
+<p class="desc">Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.</p>
+
+
+<script>
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.html
new file mode 100644
index 0000000000..b2c826ff38
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.1.radius.double.single.argument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.1.radius.double.single.argument</h1>
+<p class="desc">Verify that when one radius is given to roundRect() as a non-array argument, specified as a double, it applies to all corners.</p>
+
+
+<script>
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a double, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, 20);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.worker.js
new file mode 100644
index 0000000000..04350e81aa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.single.argument.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.1.radius.double.single.argument
+// Description:Verify that when one radius is given to roundRect() as a non-array argument, specified as a double, it applies to all corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when one radius is given to roundRect() as a non-array argument, specified as a double, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, 20);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.worker.js
new file mode 100644
index 0000000000..ef9da08d61
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.1.radius.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.1.radius.double
+// Description:Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.html
new file mode 100644
index 0000000000..7f67c42ed4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.2.radii.1.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.2.radii.1.dompoint</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.</p>
+
+
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.worker.js
new file mode 100644
index 0000000000..5d0387d0d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompoint.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.2.radii.1.dompoint
+// Description:Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html
new file mode 100644
index 0000000000..2ac0347d96
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.2.radii.1.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.2.radii.1.dompointinit</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left and bottom-right corners.</p>
+
+
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left and bottom-right corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.worker.js
new file mode 100644
index 0000000000..240a36e846
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.dompointinit.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.2.radii.1.dompointinit
+// Description:Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left and bottom-right corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left and bottom-right corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.html
new file mode 100644
index 0000000000..791e59ee7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.2.radii.1.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.2.radii.1.double</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.</p>
+
+
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.worker.js
new file mode 100644
index 0000000000..0fc351c3d6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.1.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.2.radii.1.double
+// Description:Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.html
new file mode 100644
index 0000000000..d0b085e170
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.2.radii.2.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.2.radii.2.dompoint</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.</p>
+
+
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.worker.js
new file mode 100644
index 0000000000..4c6bb06bdb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompoint.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.2.radii.2.dompoint
+// Description:Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html
new file mode 100644
index 0000000000..380dd26997
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.2.radii.2.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.2.radii.2.dompointinit</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right and bottom-left corners.</p>
+
+
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.worker.js
new file mode 100644
index 0000000000..e8cdd2c015
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.dompointinit.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.2.radii.2.dompointinit
+// Description:Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right and bottom-left corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.html
new file mode 100644
index 0000000000..b7b25727b9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.2.radii.2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.2.radii.2.double</h1>
+<p class="desc">Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.</p>
+
+
+<script>
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.worker.js
new file mode 100644
index 0000000000..756328682d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.2.radii.2.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.2.radii.2.double
+// Description:Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.html
new file mode 100644
index 0000000000..b7f3e53261
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.1.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.1.dompoint</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.worker.js
new file mode 100644
index 0000000000..9ac4d731c3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompoint.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.1.dompoint
+// Description:Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html
new file mode 100644
index 0000000000..93f89159b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.1.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.1.dompointinit</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.worker.js
new file mode 100644
index 0000000000..9d00729ce9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.dompointinit.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.1.dompointinit
+// Description:Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.html
new file mode 100644
index 0000000000..976458bc5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.1.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.1.double</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.worker.js
new file mode 100644
index 0000000000..c4e1c4fc03
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.1.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.1.double
+// Description:Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.html
new file mode 100644
index 0000000000..1a89c28d15
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.2.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.2.dompoint</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.worker.js
new file mode 100644
index 0000000000..4e4df3a601
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompoint.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.2.dompoint
+// Description:Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html
new file mode 100644
index 0000000000..82ceb6a7bc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.2.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.2.dompointinit</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.worker.js
new file mode 100644
index 0000000000..1333e2386d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.dompointinit.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.2.dompointinit
+// Description:Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.html
new file mode 100644
index 0000000000..b466956d58
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.2.double</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.worker.js
new file mode 100644
index 0000000000..027dbae52a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.2.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.2.double
+// Description:Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.html
new file mode 100644
index 0000000000..897aeb2685
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.3.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.3.dompoint</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.worker.js
new file mode 100644
index 0000000000..8a80623fc9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompoint.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.3.dompoint
+// Description:Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html
new file mode 100644
index 0000000000..e1e055cb64
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.3.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.3.dompointinit</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.worker.js
new file mode 100644
index 0000000000..d3b188dd7f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.dompointinit.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.3.dompointinit
+// Description:Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.html
new file mode 100644
index 0000000000..a0e0553364
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.3.radii.3.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.3.radii.3.double</h1>
+<p class="desc">Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.worker.js
new file mode 100644
index 0000000000..177e7c5f6d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.3.radii.3.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.3.radii.3.double
+// Description:Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.html
new file mode 100644
index 0000000000..72e8d6ce33
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.1.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.1.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.worker.js
new file mode 100644
index 0000000000..f27f696f1c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompoint.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.1.dompoint
+// Description:Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html
new file mode 100644
index 0000000000..df6bb11e02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.1.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.1.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.worker.js
new file mode 100644
index 0000000000..2f9d2eb30f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.dompointinit.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.1.dompointinit
+// Description:Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ _assertPixel(canvas, 20,1, 255,0,0,255);
+ _assertPixel(canvas, 41,1, 0,255,0,255);
+ _assertPixel(canvas, 1,10, 255,0,0,255);
+ _assertPixel(canvas, 1,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.html
new file mode 100644
index 0000000000..b669a7bb6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.1.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.1.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.worker.js
new file mode 100644
index 0000000000..9658a39cfb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.1.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.1.double
+// Description:Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.html
new file mode 100644
index 0000000000..b387d88990
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.2.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.2.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.worker.js
new file mode 100644
index 0000000000..83d3f66b3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompoint.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.2.dompoint
+// Description:Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html
new file mode 100644
index 0000000000..76719124e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.2.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.2.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.worker.js
new file mode 100644
index 0000000000..21766040b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.dompointinit.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.2.dompointinit
+// Description:Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ _assertPixel(canvas, 79,1, 255,0,0,255);
+ _assertPixel(canvas, 58,1, 0,255,0,255);
+ _assertPixel(canvas, 98,10, 255,0,0,255);
+ _assertPixel(canvas, 98,21, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.html
new file mode 100644
index 0000000000..001cb8429c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.2.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.worker.js
new file mode 100644
index 0000000000..b9935a1bd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.2.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.2.double
+// Description:Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.html
new file mode 100644
index 0000000000..5c8c686086
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.3.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.3.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.worker.js
new file mode 100644
index 0000000000..819b1a3986
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompoint.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.3.dompoint
+// Description:Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html
new file mode 100644
index 0000000000..1b94b7d7ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.3.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.3.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.worker.js
new file mode 100644
index 0000000000..e4bd04aa29
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.dompointinit.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.3.dompointinit
+// Description:Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ _assertPixel(canvas, 79,48, 255,0,0,255);
+ _assertPixel(canvas, 58,48, 0,255,0,255);
+ _assertPixel(canvas, 98,39, 255,0,0,255);
+ _assertPixel(canvas, 98,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.html
new file mode 100644
index 0000000000..2a76d2b7b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.3.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.3.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.worker.js
new file mode 100644
index 0000000000..ada627b469
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.3.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.3.double
+// Description:Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.html
new file mode 100644
index 0000000000..ce86e45e8b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.4.dompoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.4.dompoint</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.worker.js
new file mode 100644
index 0000000000..770291c6f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompoint.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.4.dompoint
+// Description:Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html
new file mode 100644
index 0000000000..f0ec6c3b99
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.4.dompointinit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.4.dompointinit</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPointInit, applies to the bottom-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPointInit, applies to the bottom-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.worker.js
new file mode 100644
index 0000000000..48310c6a30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.dompointinit.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.4.dompointinit
+// Description:Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPointInit, applies to the bottom-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPointInit, applies to the bottom-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ _assertPixel(canvas, 20,48, 255,0,0,255);
+ _assertPixel(canvas, 41,48, 0,255,0,255);
+ _assertPixel(canvas, 1,39, 255,0,0,255);
+ _assertPixel(canvas, 1,28, 0,255,0,255);
+
+ // other corners
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.html
new file mode 100644
index 0000000000..6940622cf0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.4.radii.4.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.4.radii.4.double</h1>
+<p class="desc">Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.</p>
+
+
+<script>
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.worker.js
new file mode 100644
index 0000000000..e97ebd98ff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.4.radii.4.double.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.4.radii.4.double
+// Description:Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.html
new file mode 100644
index 0000000000..97b322971c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.badinput</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.badinput</h1>
+<p class="desc">roundRect() throws or does not throw errors given the strange inputs.</p>
+
+
+<script>
+var t = async_test("roundRect() throws or does not throw errors given the strange inputs.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.roundRect(0, 0, 100, 100, { foo: "bar" }); //=> DOMPointInit
+ ctx.roundRect(0, 0, 100, 100, undefined); //=> "missing" -> 0
+ ctx.roundRect(0, 0, 100, 100, [[]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [[25]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [undefined]); //=> « DOMPointInit »
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, 0n); });
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, { x: 0n }); });
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, [{ x: 0n }]); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.worker.js
new file mode 100644
index 0000000000..461dee89ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.badinput.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.badinput
+// Description:roundRect() throws or does not throw errors given the strange inputs.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("roundRect() throws or does not throw errors given the strange inputs.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.roundRect(0, 0, 100, 100, { foo: "bar" }); //=> DOMPointInit
+ ctx.roundRect(0, 0, 100, 100, undefined); //=> "missing" -> 0
+ ctx.roundRect(0, 0, 100, 100, [[]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [[25]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [undefined]); //=> « DOMPointInit »
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, 0n); });
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, { x: 0n }); });
+ assert_throws_js(TypeError, function() { ctx.roundRect(0, 0, 100, 100, [{ x: 0n }]); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.html
new file mode 100644
index 0000000000..7407ead9cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.closed</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.roundRect(100, 50, 100, 100, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.worker.js
new file mode 100644
index 0000000000..5a9530a683
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.closed.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.closed
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.roundRect(100, 50, 100, 100, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.html
new file mode 100644
index 0000000000..d73b82bee4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.end.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.end.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(200, 100, 400, 1000, [0]);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.worker.js
new file mode 100644
index 0000000000..9428df5bbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.end.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(200, 100, 400, 1000, [0]);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.html
new file mode 100644
index 0000000000..ce9b44ead1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.end.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.end.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.roundRect(150, 150, 2000, 2000, [0]);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.worker.js
new file mode 100644
index 0000000000..28f1ca1787
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.2.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.end.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.roundRect(150, 150, 2000, 2000, [0]);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.html
new file mode 100644
index 0000000000..72183ecd05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.end.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.end.3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(101, 51, 2000, 2000, [500, 500, 500, 500]);
+ ctx.lineTo(-1, -1);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.worker.js
new file mode 100644
index 0000000000..57cb328cfa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.3.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.end.3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(101, 51, 2000, 2000, [500, 500, 500, 500]);
+ ctx.lineTo(-1, -1);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.html
new file mode 100644
index 0000000000..c4fdee5958
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.end.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.end.4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.roundRect(-1, -1, 2000, 2000, [1000, 1000, 1000, 1000]);
+ ctx.lineTo(-150, -150);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.worker.js
new file mode 100644
index 0000000000..a97a37d157
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.end.4.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.end.4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.roundRect(-1, -1, 2000, 2000, [1000, 1000, 1000, 1000]);
+ ctx.lineTo(-150, -150);
+ ctx.stroke();
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 98,1, 0,255,0,255);
+ _assertPixel(canvas, 1,48, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.html
new file mode 100644
index 0000000000..aa83657244
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.negative</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.roundRect(0, 0, 50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 0, -50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(0, 50, 50, -25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 50, -50, -25, [10, 0, 0, 0]);
+ ctx.fill();
+ // All rects drawn
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ // Correct corners are rounded.
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.worker.js
new file mode 100644
index 0000000000..14459238ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.negative.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.negative
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.roundRect(0, 0, 50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 0, -50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(0, 50, 50, -25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 50, -50, -25, [10, 0, 0, 0]);
+ ctx.fill();
+ // All rects drawn
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ // Correct corners are rounded.
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.html
new file mode 100644
index 0000000000..3ad7e5bb5b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.newsubpath</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.newsubpath</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.roundRect(200, 25, 1, 1, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.worker.js
new file mode 100644
index 0000000000..757327c60d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.newsubpath.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.newsubpath
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.roundRect(200, 25, 1, 1, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.html
new file mode 100644
index 0000000000..adddc7b9b8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.nonfinite</h1>
+<p class="desc">roundRect() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("roundRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.roundRect(Infinity, 50, 1, 1, [0]);
+ ctx.roundRect(-Infinity, 50, 1, 1, [0]);
+ ctx.roundRect(NaN, 50, 1, 1, [0]);
+ ctx.roundRect(0, Infinity, 1, 1, [0]);
+ ctx.roundRect(0, -Infinity, 1, 1, [0]);
+ ctx.roundRect(0, NaN, 1, 1, [0]);
+ ctx.roundRect(0, 50, Infinity, 1, [0]);
+ ctx.roundRect(0, 50, -Infinity, 1, [0]);
+ ctx.roundRect(0, 50, NaN, 1, [0]);
+ ctx.roundRect(0, 50, 1, Infinity, [0]);
+ ctx.roundRect(0, 50, 1, -Infinity, [0]);
+ ctx.roundRect(0, 50, 1, NaN, [0]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,NaN]);
+ ctx.roundRect(Infinity, Infinity, 1, 1, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, 1, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, Infinity, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, Infinity, 1, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, 1, Infinity, [0]);
+ ctx.roundRect(Infinity, Infinity, 1, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, 1, 1, [Infinity]);
+ ctx.roundRect(Infinity, 50, Infinity, 1, [0]);
+ ctx.roundRect(Infinity, 50, Infinity, Infinity, [0]);
+ ctx.roundRect(Infinity, 50, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, 50, Infinity, 1, [Infinity]);
+ ctx.roundRect(Infinity, 50, 1, Infinity, [0]);
+ ctx.roundRect(Infinity, 50, 1, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, 50, 1, 1, [Infinity]);
+ ctx.roundRect(0, Infinity, Infinity, 1, [0]);
+ ctx.roundRect(0, Infinity, Infinity, Infinity, [0]);
+ ctx.roundRect(0, Infinity, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(0, Infinity, Infinity, 1, [Infinity]);
+ ctx.roundRect(0, Infinity, 1, Infinity, [0]);
+ ctx.roundRect(0, Infinity, 1, Infinity, [Infinity]);
+ ctx.roundRect(0, Infinity, 1, 1, [Infinity]);
+ ctx.roundRect(0, 50, Infinity, Infinity, [0]);
+ ctx.roundRect(0, 50, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(0, 50, Infinity, 1, [Infinity]);
+ ctx.roundRect(0, 50, 1, Infinity, [Infinity]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, -Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, NaN)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(-Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(NaN, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: -Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: NaN}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: -Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: NaN, y: 10}]);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.worker.js
new file mode 100644
index 0000000000..d6d44d6ffe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.nonfinite.worker.js
@@ -0,0 +1,111 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.nonfinite
+// Description:roundRect() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("roundRect() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.roundRect(Infinity, 50, 1, 1, [0]);
+ ctx.roundRect(-Infinity, 50, 1, 1, [0]);
+ ctx.roundRect(NaN, 50, 1, 1, [0]);
+ ctx.roundRect(0, Infinity, 1, 1, [0]);
+ ctx.roundRect(0, -Infinity, 1, 1, [0]);
+ ctx.roundRect(0, NaN, 1, 1, [0]);
+ ctx.roundRect(0, 50, Infinity, 1, [0]);
+ ctx.roundRect(0, 50, -Infinity, 1, [0]);
+ ctx.roundRect(0, 50, NaN, 1, [0]);
+ ctx.roundRect(0, 50, 1, Infinity, [0]);
+ ctx.roundRect(0, 50, 1, -Infinity, [0]);
+ ctx.roundRect(0, 50, 1, NaN, [0]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,NaN]);
+ ctx.roundRect(0, 50, 1, 1, [Infinity,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [-Infinity,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [NaN,0,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,-Infinity,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,NaN,0,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,-Infinity,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,NaN,0]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,-Infinity]);
+ ctx.roundRect(0, 50, 1, 1, [0,0,0,NaN]);
+ ctx.roundRect(Infinity, Infinity, 1, 1, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, 1, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, Infinity, [0]);
+ ctx.roundRect(Infinity, Infinity, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, Infinity, 1, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, 1, Infinity, [0]);
+ ctx.roundRect(Infinity, Infinity, 1, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, Infinity, 1, 1, [Infinity]);
+ ctx.roundRect(Infinity, 50, Infinity, 1, [0]);
+ ctx.roundRect(Infinity, 50, Infinity, Infinity, [0]);
+ ctx.roundRect(Infinity, 50, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, 50, Infinity, 1, [Infinity]);
+ ctx.roundRect(Infinity, 50, 1, Infinity, [0]);
+ ctx.roundRect(Infinity, 50, 1, Infinity, [Infinity]);
+ ctx.roundRect(Infinity, 50, 1, 1, [Infinity]);
+ ctx.roundRect(0, Infinity, Infinity, 1, [0]);
+ ctx.roundRect(0, Infinity, Infinity, Infinity, [0]);
+ ctx.roundRect(0, Infinity, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(0, Infinity, Infinity, 1, [Infinity]);
+ ctx.roundRect(0, Infinity, 1, Infinity, [0]);
+ ctx.roundRect(0, Infinity, 1, Infinity, [Infinity]);
+ ctx.roundRect(0, Infinity, 1, 1, [Infinity]);
+ ctx.roundRect(0, 50, Infinity, Infinity, [0]);
+ ctx.roundRect(0, 50, Infinity, Infinity, [Infinity]);
+ ctx.roundRect(0, 50, Infinity, 1, [Infinity]);
+ ctx.roundRect(0, 50, 1, Infinity, [Infinity]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, -Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, NaN)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(-Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(NaN, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: -Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: NaN}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: -Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: NaN, y: 10}]);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 90,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.html
new file mode 100644
index 0000000000..db4d871f9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.radius.intersecting.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.radius.intersecting.1</h1>
+<p class="desc">Check that roundRects with intersecting corner arcs are rendered correctly.</p>
+
+
+<script>
+var t = async_test("Check that roundRects with intersecting corner arcs are rendered correctly.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [40, 40, 40, 40]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 2,25, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 97,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.worker.js
new file mode 100644
index 0000000000..27076dbe21
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.1.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.radius.intersecting.1
+// Description:Check that roundRects with intersecting corner arcs are rendered correctly.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that roundRects with intersecting corner arcs are rendered correctly.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [40, 40, 40, 40]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 2,25, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 97,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.html
new file mode 100644
index 0000000000..985942ca36
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.radius.intersecting.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.radius.intersecting.2</h1>
+<p class="desc">Check that roundRects with intersecting corner arcs are rendered correctly.</p>
+
+
+<script>
+var t = async_test("Check that roundRects with intersecting corner arcs are rendered correctly.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [1000, 1000, 1000, 1000]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 2,25, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 97,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.worker.js
new file mode 100644
index 0000000000..5b6de579ae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.intersecting.2.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.radius.intersecting.2
+// Description:Check that roundRects with intersecting corner arcs are rendered correctly.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that roundRects with intersecting corner arcs are rendered correctly.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [1000, 1000, 1000, 1000]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 2,25, 0,255,0,255);
+ _assertPixel(canvas, 50,1, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 50,48, 0,255,0,255);
+ _assertPixel(canvas, 97,25, 0,255,0,255);
+ _assertPixel(canvas, 1,1, 255,0,0,255);
+ _assertPixel(canvas, 98,1, 255,0,0,255);
+ _assertPixel(canvas, 1,48, 255,0,0,255);
+ _assertPixel(canvas, 98,48, 255,0,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.html
new file mode 100644
index 0000000000..60a7bebd24
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.radius.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.radius.negative</h1>
+<p class="desc">roundRect() with negative radius throws an exception</p>
+
+
+<script>
+var t = async_test("roundRect() with negative radius throws an exception");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [-1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [1, -1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(-1, 1), 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(1, -1)])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: -1, y: 1}, 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: 1, y: -1}])});
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.worker.js
new file mode 100644
index 0000000000..554a1ac078
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.negative.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.radius.negative
+// Description:roundRect() with negative radius throws an exception
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("roundRect() with negative radius throws an exception");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [-1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [1, -1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(-1, 1), 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(1, -1)])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: -1, y: 1}, 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: 1, y: -1}])});
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.html
new file mode 100644
index 0000000000..26a69afad0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.radius.noargument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.radius.noargument</h1>
+<p class="desc">Check that roundRect draws a rectangle when no radii are provided.</p>
+
+
+<script>
+var t = async_test("Check that roundRect draws a rectangle when no radii are provided.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(10, 10, 80, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ // upper left corner (10, 10)
+ _assertPixel(canvas, 10,9, 255,0,0,255);
+ _assertPixel(canvas, 9,10, 255,0,0,255);
+ _assertPixel(canvas, 10,10, 0,255,0,255);
+
+ // upper right corner (89, 10)
+ _assertPixel(canvas, 90,10, 255,0,0,255);
+ _assertPixel(canvas, 89,9, 255,0,0,255);
+ _assertPixel(canvas, 89,10, 0,255,0,255);
+
+ // lower right corner (89, 39)
+ _assertPixel(canvas, 89,40, 255,0,0,255);
+ _assertPixel(canvas, 90,39, 255,0,0,255);
+ _assertPixel(canvas, 89,39, 0,255,0,255);
+
+ // lower left corner (10, 30)
+ _assertPixel(canvas, 9,39, 255,0,0,255);
+ _assertPixel(canvas, 10,40, 255,0,0,255);
+ _assertPixel(canvas, 10,39, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.worker.js
new file mode 100644
index 0000000000..1f6227786b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.noargument.worker.js
@@ -0,0 +1,45 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.radius.noargument
+// Description:Check that roundRect draws a rectangle when no radii are provided.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that roundRect draws a rectangle when no radii are provided.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(10, 10, 80, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ // upper left corner (10, 10)
+ _assertPixel(canvas, 10,9, 255,0,0,255);
+ _assertPixel(canvas, 9,10, 255,0,0,255);
+ _assertPixel(canvas, 10,10, 0,255,0,255);
+
+ // upper right corner (89, 10)
+ _assertPixel(canvas, 90,10, 255,0,0,255);
+ _assertPixel(canvas, 89,9, 255,0,0,255);
+ _assertPixel(canvas, 89,10, 0,255,0,255);
+
+ // lower right corner (89, 39)
+ _assertPixel(canvas, 89,40, 255,0,0,255);
+ _assertPixel(canvas, 90,39, 255,0,0,255);
+ _assertPixel(canvas, 89,39, 0,255,0,255);
+
+ // lower left corner (10, 30)
+ _assertPixel(canvas, 9,39, 255,0,0,255);
+ _assertPixel(canvas, 10,40, 255,0,0,255);
+ _assertPixel(canvas, 10,39, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.html
new file mode 100644
index 0000000000..763113e438
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.radius.none</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.radius.none</h1>
+<p class="desc">Check that roundRect throws an RangeError if radii is an empty array.</p>
+
+
+<script>
+var t = async_test("Check that roundRect throws an RangeError if radii is an empty array.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [])});
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.worker.js
new file mode 100644
index 0000000000..fefd1b4dd3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.none.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.radius.none
+// Description:Check that roundRect throws an RangeError if radii is an empty array.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that roundRect throws an RangeError if radii is an empty array.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [])});
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.html
new file mode 100644
index 0000000000..ce330875fb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.radius.toomany</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.radius.toomany</h1>
+<p class="desc">Check that roundRect throws an IndeSizeError if radii has more than four items.</p>
+
+
+<script>
+var t = async_test("Check that roundRect throws an IndeSizeError if radii has more than four items.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 0, 0])});
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.worker.js
new file mode 100644
index 0000000000..e1250afac7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.radius.toomany.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.radius.toomany
+// Description:Check that roundRect throws an IndeSizeError if radii has more than four items.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Check that roundRect throws an IndeSizeError if radii has more than four items.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 0, 0])});
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.html
new file mode 100644
index 0000000000..15ee6f4958
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.selfintersect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.selfintersect</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 100, 50, [0]);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.roundRect(45, 20, 10, 10, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.worker.js
new file mode 100644
index 0000000000..91741110fc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.selfintersect.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.selfintersect
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 100, 50, [0]);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.roundRect(45, 20, 10, 10, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.html
new file mode 100644
index 0000000000..47ecd777dc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.winding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.winding</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 50, 50, [0]);
+ ctx.roundRect(100, 50, -50, -50, [0]);
+ ctx.roundRect(0, 25, 100, -25, [0]);
+ ctx.roundRect(100, 25, -100, 25, [0]);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.worker.js
new file mode 100644
index 0000000000..737e99f373
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.winding.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.winding
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 50, 50, [0]);
+ ctx.roundRect(100, 50, -50, -50, [0]);
+ ctx.roundRect(0, 25, 100, -25, [0]);
+ ctx.roundRect(100, 25, -100, 25, [0]);
+ ctx.fill();
+ _assertPixel(canvas, 25,12, 0,255,0,255);
+ _assertPixel(canvas, 75,12, 0,255,0,255);
+ _assertPixel(canvas, 25,37, 0,255,0,255);
+ _assertPixel(canvas, 75,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.html
new file mode 100644
index 0000000000..772339dcc6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.zero.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.zero.1</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(0, 50, 100, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.worker.js
new file mode 100644
index 0000000000..afcaa20769
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.zero.1
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(0, 50, 100, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.html
new file mode 100644
index 0000000000..2aad5aad41
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.zero.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.zero.2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, -100, 0, 250, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.worker.js
new file mode 100644
index 0000000000..dec178bd20
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.2.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.zero.2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, -100, 0, 250, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.html
new file mode 100644
index 0000000000..485384aaff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.zero.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.zero.3</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, 25, 0, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.worker.js
new file mode 100644
index 0000000000..88adb5c437
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.3.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.zero.3
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, 25, 0, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.html
new file mode 100644
index 0000000000..dfd4821a84
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.zero.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.zero.4</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.worker.js
new file mode 100644
index 0000000000..fe8cc149c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.4.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.zero.4
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.html
new file mode 100644
index 0000000000..931a737e3b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.zero.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.zero.5</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.worker.js
new file mode 100644
index 0000000000..1fd61246a9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.5.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.zero.5
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.html
new file mode 100644
index 0000000000..25ed23d329
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.roundrect.zero.6</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.roundrect.zero.6</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.roundRect(100, 25, 1000, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.worker.js
new file mode 100644
index 0000000000..c4b6fba5b0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.roundrect.zero.6.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.roundrect.zero.6
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.roundRect(100, 25, 1000, 0, [0]);
+ ctx.stroke();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.html
new file mode 100644
index 0000000000..29a95da20d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.empty</h1>
+<p class="desc">Empty subpaths are not stroked</p>
+
+
+<script>
+var t = async_test("Empty subpaths are not stroked");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(40, 25);
+ ctx.moveTo(60, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.worker.js
new file mode 100644
index 0000000000..56ee68f185
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.empty.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.empty
+// Description:Empty subpaths are not stroked
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Empty subpaths are not stroked");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(40, 25);
+ ctx.moveTo(60, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.html
new file mode 100644
index 0000000000..c3847464bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.overlap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.overlap</h1>
+<p class="desc">Stroked subpaths are combined before being drawn</p>
+
+
+<script>
+var t = async_test("Stroked subpaths are combined before being drawn");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 20);
+ ctx.lineTo(100, 20);
+ ctx.moveTo(0, 30);
+ ctx.lineTo(100, 30);
+ ctx.stroke();
+
+ _assertPixelApprox(canvas, 50,25, 0,127,0,255, 1);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.png b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.png
new file mode 100644
index 0000000000..e2a35d48d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.worker.js
new file mode 100644
index 0000000000..0e59d3bfc0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.overlap.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.overlap
+// Description:Stroked subpaths are combined before being drawn
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Stroked subpaths are combined before being drawn");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 20);
+ ctx.lineTo(100, 20);
+ ctx.moveTo(0, 30);
+ ctx.lineTo(100, 30);
+ ctx.stroke();
+
+ _assertPixelApprox(canvas, 50,25, 0,127,0,255, 1);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.html
new file mode 100644
index 0000000000..40a73b85fb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.prune.arc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.prune.arc</h1>
+<p class="desc">Zero-length line segments from arcTo and arc are removed before stroking</p>
+
+
+<script>
+var t = async_test("Zero-length line segments from arcTo and arc are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 150, 25, 10);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(60, 25);
+ ctx.arc(50, 25, 10, 0, 0, false);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.worker.js
new file mode 100644
index 0000000000..880e8146ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.arc.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.prune.arc
+// Description:Zero-length line segments from arcTo and arc are removed before stroking
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Zero-length line segments from arcTo and arc are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 150, 25, 10);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(60, 25);
+ ctx.arc(50, 25, 10, 0, 0, false);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.html
new file mode 100644
index 0000000000..2a3b40068d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.prune.closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.prune.closed</h1>
+<p class="desc">Zero-length line segments from closed paths are removed before stroking</p>
+
+
+<script>
+var t = async_test("Zero-length line segments from closed paths are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.worker.js
new file mode 100644
index 0000000000..e1d271bf6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.closed.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.prune.closed
+// Description:Zero-length line segments from closed paths are removed before stroking
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Zero-length line segments from closed paths are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.closePath();
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.html
new file mode 100644
index 0000000000..45db65d6d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.prune.corner</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.prune.corner</h1>
+<p class="desc">Zero-length line segments are removed before stroking with miters</p>
+
+
+<script>
+var t = async_test("Zero-length line segments are removed before stroking with miters");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.4;
+
+ ctx.beginPath();
+ ctx.moveTo(-1000, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 1000);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.worker.js
new file mode 100644
index 0000000000..fff4ba19e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.corner.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.prune.corner
+// Description:Zero-length line segments are removed before stroking with miters
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Zero-length line segments are removed before stroking with miters");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.4;
+
+ ctx.beginPath();
+ ctx.moveTo(-1000, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 1000);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.html
new file mode 100644
index 0000000000..946395f1c1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.prune.curve</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.prune.curve</h1>
+<p class="desc">Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking</p>
+
+
+<script>
+var t = async_test("Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.quadraticCurveTo(50, 25, 50, 25);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.worker.js
new file mode 100644
index 0000000000..ed960fdbe7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.curve.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.prune.curve
+// Description:Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.quadraticCurveTo(50, 25, 50, 25);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.html
new file mode 100644
index 0000000000..1f0a892455
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.prune.line</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.prune.line</h1>
+<p class="desc">Zero-length line segments from lineTo are removed before stroking</p>
+
+
+<script>
+var t = async_test("Zero-length line segments from lineTo are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.worker.js
new file mode 100644
index 0000000000..a00e73bcbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.line.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.prune.line
+// Description:Zero-length line segments from lineTo are removed before stroking
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Zero-length line segments from lineTo are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.html
new file mode 100644
index 0000000000..ffce538d91
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.prune.rect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.prune.rect</h1>
+<p class="desc">Zero-length line segments from rect and strokeRect are removed before stroking</p>
+
+
+<script>
+var t = async_test("Zero-length line segments from rect and strokeRect are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+
+ ctx.strokeRect(50, 25, 0, 0);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.worker.js
new file mode 100644
index 0000000000..87bb533ff9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.prune.rect.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.prune.rect
+// Description:Zero-length line segments from rect and strokeRect are removed before stroking
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Zero-length line segments from rect and strokeRect are removed before stroking");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+
+ ctx.strokeRect(50, 25, 0, 0);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.html
new file mode 100644
index 0000000000..47639065d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.scale1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.scale1</h1>
+<p class="desc">Stroke line widths are scaled by the current transformation matrix</p>
+
+
+<script>
+var t = async_test("Stroke line widths are scaled by the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.worker.js
new file mode 100644
index 0000000000..3725fdf6e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale1.worker.js
@@ -0,0 +1,49 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.scale1
+// Description:Stroke line widths are scaled by the current transformation matrix
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Stroke line widths are scaled by the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.html
new file mode 100644
index 0000000000..447a6dfadc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.scale2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.scale2</h1>
+<p class="desc">Stroke line widths are scaled by the current transformation matrix</p>
+
+
+<script>
+var t = async_test("Stroke line widths are scaled by the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.worker.js
new file mode 100644
index 0000000000..5223de7816
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.scale2.worker.js
@@ -0,0 +1,51 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.scale2
+// Description:Stroke line widths are scaled by the current transformation matrix
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Stroke line widths are scaled by the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.html
new file mode 100644
index 0000000000..1fb118b122
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.skew</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.skew</h1>
+<p class="desc">Strokes lines are skewed by the current transformation matrix</p>
+
+
+<script>
+var t = async_test("Strokes lines are skewed by the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(49, -50);
+ ctx.lineTo(201, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 283);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.worker.js
new file mode 100644
index 0000000000..e65b796d06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.skew.worker.js
@@ -0,0 +1,65 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.skew
+// Description:Strokes lines are skewed by the current transformation matrix
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Strokes lines are skewed by the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(49, -50);
+ ctx.lineTo(201, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 283);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ _assertPixel(canvas, 0,0, 0,255,0,255);
+ _assertPixel(canvas, 50,0, 0,255,0,255);
+ _assertPixel(canvas, 99,0, 0,255,0,255);
+ _assertPixel(canvas, 0,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 99,25, 0,255,0,255);
+ _assertPixel(canvas, 0,49, 0,255,0,255);
+ _assertPixel(canvas, 50,49, 0,255,0,255);
+ _assertPixel(canvas, 99,49, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.html
new file mode 100644
index 0000000000..3fc9cfad88
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.unaffected</h1>
+<p class="desc">Stroking does not start a new path or subpath</p>
+
+
+<script>
+var t = async_test("Stroking does not start a new path or subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+
+ ctx.closePath();
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.worker.js
new file mode 100644
index 0000000000..d9df1ada94
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.unaffected.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.unaffected
+// Description:Stroking does not start a new path or subpath
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Stroking does not start a new path or subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+
+ ctx.closePath();
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.html
new file mode 100644
index 0000000000..6b980acff2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.stroke.union</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.stroke.union</h1>
+<p class="desc">Strokes in opposite directions are unioned, not subtracted</p>
+
+
+<script>
+var t = async_test("Strokes in opposite directions are unioned, not subtracted");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 40;
+ ctx.moveTo(0, 10);
+ ctx.lineTo(100, 10);
+ ctx.moveTo(100, 40);
+ ctx.lineTo(0, 40);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.worker.js
new file mode 100644
index 0000000000..3b05feec03
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.stroke.union.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.stroke.union
+// Description:Strokes in opposite directions are unioned, not subtracted
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Strokes in opposite directions are unioned, not subtracted");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 40;
+ ctx.moveTo(0, 10);
+ ctx.lineTo(100, 10);
+ ctx.moveTo(100, 40);
+ ctx.lineTo(0, 40);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.html
new file mode 100644
index 0000000000..27f00aae03
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.transformation.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.transformation.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-100, 0);
+ ctx.rect(100, 0, 100, 50);
+ ctx.translate(0, -100);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.worker.js
new file mode 100644
index 0000000000..3ae4c876d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.basic.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.transformation.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-100, 0);
+ ctx.rect(100, 0, 100, 50);
+ ctx.translate(0, -100);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.html
new file mode 100644
index 0000000000..786a5cf589
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.transformation.changing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.transformation.changing</h1>
+<p class="desc">Transformations are applied while building paths, not when drawing</p>
+
+
+<script>
+var t = async_test("Transformations are applied while building paths, not when drawing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.translate(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.translate(-100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(1000, 1000);
+ ctx.rotate(Math.PI/2);
+ ctx.scale(0.1, 0.1);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.worker.js
new file mode 100644
index 0000000000..2d8993c685
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.changing.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.transformation.changing
+// Description:Transformations are applied while building paths, not when drawing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Transformations are applied while building paths, not when drawing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.translate(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.translate(-100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(1000, 1000);
+ ctx.rotate(Math.PI/2);
+ ctx.scale(0.1, 0.1);
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.html b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.html
new file mode 100644
index 0000000000..636491bb0e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.path.transformation.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.path.transformation.multiple</h1>
+<p class="desc">Transformations are applied while building paths, not when drawing</p>
+
+
+<script>
+var t = async_test("Transformations are applied while building paths, not when drawing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.translate(-100, 0);
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ ctx.translate(100, 0);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.translate(0, -50);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ ctx.translate(0, 50);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.worker.js b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.worker.js
new file mode 100644
index 0000000000..a9fb37d0de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/path-objects/2d.path.transformation.multiple.worker.js
@@ -0,0 +1,42 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.path.transformation.multiple
+// Description:Transformations are applied while building paths, not when drawing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Transformations are applied while building paths, not when drawing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.translate(-100, 0);
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ ctx.translate(100, 0);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.translate(0, -50);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ ctx.translate(0, 50);
+ ctx.stroke();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.html
new file mode 100644
index 0000000000..f6932386df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create1.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create1.basic</h1>
+<p class="desc">createImageData(imgdata) exists and returns something</p>
+
+
+<script>
+var t = async_test("createImageData(imgdata) exists and returns something");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(ctx.createImageData(ctx.createImageData(1, 1)), null, "ctx.createImageData(ctx.createImageData(1, 1))", "null");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.worker.js
new file mode 100644
index 0000000000..30f3ed21b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.basic.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create1.basic
+// Description:createImageData(imgdata) exists and returns something
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(imgdata) exists and returns something");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(ctx.createImageData(ctx.createImageData(1, 1)), null, "ctx.createImageData(ctx.createImageData(1, 1))", "null");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.html
new file mode 100644
index 0000000000..627b09b8bd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create1.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create1.initial</h1>
+<p class="desc">createImageData(imgdata) returns transparent black data of the right size</p>
+
+
+<script>
+var t = async_test("createImageData(imgdata) returns transparent black data of the right size");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata1 = ctx.getImageData(0, 0, 10, 20);
+ var imgdata2 = ctx.createImageData(imgdata1);
+ _assertSame(imgdata2.data.length, imgdata1.data.length, "imgdata2.data.length", "imgdata1.data.length");
+ _assertSame(imgdata2.width, imgdata1.width, "imgdata2.width", "imgdata1.width");
+ _assertSame(imgdata2.height, imgdata1.height, "imgdata2.height", "imgdata1.height");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata2.data.length; ++i)
+ if (imgdata2.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.worker.js
new file mode 100644
index 0000000000..da0856a487
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.initial.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create1.initial
+// Description:createImageData(imgdata) returns transparent black data of the right size
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(imgdata) returns transparent black data of the right size");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata1 = ctx.getImageData(0, 0, 10, 20);
+ var imgdata2 = ctx.createImageData(imgdata1);
+ _assertSame(imgdata2.data.length, imgdata1.data.length, "imgdata2.data.length", "imgdata1.data.length");
+ _assertSame(imgdata2.width, imgdata1.width, "imgdata2.width", "imgdata1.width");
+ _assertSame(imgdata2.height, imgdata1.height, "imgdata2.height", "imgdata1.height");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata2.data.length; ++i)
+ if (imgdata2.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.html
new file mode 100644
index 0000000000..b5a95172b6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create1.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create1.zero</h1>
+<p class="desc">createImageData(null) throws TypeError</p>
+
+
+<script>
+var t = async_test("createImageData(null) throws TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createImageData(null); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.worker.js
new file mode 100644
index 0000000000..1eabd9ebf3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create1.zero.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create1.zero
+// Description:createImageData(null) throws TypeError
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(null) throws TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createImageData(null); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.html
new file mode 100644
index 0000000000..7c265f5209
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.basic</h1>
+<p class="desc">createImageData(sw, sh) exists and returns something</p>
+
+
+<script>
+var t = async_test("createImageData(sw, sh) exists and returns something");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(ctx.createImageData(1, 1), null, "ctx.createImageData(1, 1)", "null");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.worker.js
new file mode 100644
index 0000000000..26ca82929c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.basic.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.basic
+// Description:createImageData(sw, sh) exists and returns something
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(sw, sh) exists and returns something");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(ctx.createImageData(1, 1), null, "ctx.createImageData(1, 1)", "null");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.html
new file mode 100644
index 0000000000..ca54641a30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.double</h1>
+<p class="desc">createImageData(w, h) double is converted to long</p>
+
+
+<script>
+var t = async_test("createImageData(w, h) double is converted to long");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.createImageData(-10.01, -10.99);
+ _assertSame(imgdata1.width, 10, "imgdata1.width", "10");
+ _assertSame(imgdata1.height, 10, "imgdata1.height", "10");
+ _assertSame(imgdata2.width, 10, "imgdata2.width", "10");
+ _assertSame(imgdata2.height, 10, "imgdata2.height", "10");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.worker.js
new file mode 100644
index 0000000000..2f3416d0f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.double.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.double
+// Description:createImageData(w, h) double is converted to long
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(w, h) double is converted to long");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.createImageData(-10.01, -10.99);
+ _assertSame(imgdata1.width, 10, "imgdata1.width", "10");
+ _assertSame(imgdata1.height, 10, "imgdata1.height", "10");
+ _assertSame(imgdata2.width, 10, "imgdata2.width", "10");
+ _assertSame(imgdata2.height, 10, "imgdata2.height", "10");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.html
new file mode 100644
index 0000000000..566a9a30db
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.initial</h1>
+<p class="desc">createImageData(sw, sh) returns transparent black data of the right size</p>
+
+
+<script>
+var t = async_test("createImageData(sw, sh) returns transparent black data of the right size");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.createImageData(10, 20);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ _assert(imgdata.width < imgdata.height, "imgdata.width < imgdata.height");
+ _assert(imgdata.width > 0, "imgdata.width > 0");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; ++i)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.worker.js
new file mode 100644
index 0000000000..472e1dfd2e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.initial.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.initial
+// Description:createImageData(sw, sh) returns transparent black data of the right size
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(sw, sh) returns transparent black data of the right size");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.createImageData(10, 20);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ _assert(imgdata.width < imgdata.height, "imgdata.width < imgdata.height");
+ _assert(imgdata.width > 0, "imgdata.width > 0");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; ++i)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.html
new file mode 100644
index 0000000000..75b8f737fd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.large</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.large</h1>
+<p class="desc">createImageData(sw, sh) works for sizes much larger than the canvas</p>
+
+
+<script>
+var t = async_test("createImageData(sw, sh) works for sizes much larger than the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.createImageData(1000, 2000);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ _assert(imgdata.width < imgdata.height, "imgdata.width < imgdata.height");
+ _assert(imgdata.width > 0, "imgdata.width > 0");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; i += 7813) // check ~1024 points (assuming normal scaling)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.worker.js
new file mode 100644
index 0000000000..e2b85bfb69
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.large.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.large
+// Description:createImageData(sw, sh) works for sizes much larger than the canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(sw, sh) works for sizes much larger than the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.createImageData(1000, 2000);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ _assert(imgdata.width < imgdata.height, "imgdata.width < imgdata.height");
+ _assert(imgdata.width > 0, "imgdata.width > 0");
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; i += 7813) // check ~1024 points (assuming normal scaling)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ _assert(isTransparentBlack, "isTransparentBlack");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.html
new file mode 100644
index 0000000000..b41153c282
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.negative</h1>
+<p class="desc">createImageData(sw, sh) takes the absolute magnitude of the size arguments</p>
+
+
+<script>
+var t = async_test("createImageData(sw, sh) takes the absolute magnitude of the size arguments");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.createImageData(10, 20);
+ var imgdata2 = ctx.createImageData(-10, 20);
+ var imgdata3 = ctx.createImageData(10, -20);
+ var imgdata4 = ctx.createImageData(-10, -20);
+ _assertSame(imgdata1.data.length, imgdata2.data.length, "imgdata1.data.length", "imgdata2.data.length");
+ _assertSame(imgdata2.data.length, imgdata3.data.length, "imgdata2.data.length", "imgdata3.data.length");
+ _assertSame(imgdata3.data.length, imgdata4.data.length, "imgdata3.data.length", "imgdata4.data.length");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.worker.js
new file mode 100644
index 0000000000..62ed60e102
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.negative.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.negative
+// Description:createImageData(sw, sh) takes the absolute magnitude of the size arguments
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(sw, sh) takes the absolute magnitude of the size arguments");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.createImageData(10, 20);
+ var imgdata2 = ctx.createImageData(-10, 20);
+ var imgdata3 = ctx.createImageData(10, -20);
+ var imgdata4 = ctx.createImageData(-10, -20);
+ _assertSame(imgdata1.data.length, imgdata2.data.length, "imgdata1.data.length", "imgdata2.data.length");
+ _assertSame(imgdata2.data.length, imgdata3.data.length, "imgdata2.data.length", "imgdata3.data.length");
+ _assertSame(imgdata3.data.length, imgdata4.data.length, "imgdata3.data.length", "imgdata4.data.length");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.html
new file mode 100644
index 0000000000..dd6f6d8350
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.nonfinite</h1>
+<p class="desc">createImageData() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("createImageData() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createImageData(Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(-Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(Infinity, Infinity); });
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ assert_throws_js(TypeError, function() { ctx.createImageData(posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(neginfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(nanobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, neginfobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, nanobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(posinfobj, posinfobj); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.worker.js
new file mode 100644
index 0000000000..18825588bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.nonfinite.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.nonfinite
+// Description:createImageData() throws TypeError if arguments are not finite
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.createImageData(Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(-Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(Infinity, Infinity); });
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ assert_throws_js(TypeError, function() { ctx.createImageData(posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(neginfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(nanobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, neginfobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(10, nanobj); });
+ assert_throws_js(TypeError, function() { ctx.createImageData(posinfobj, posinfobj); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.html
new file mode 100644
index 0000000000..0ecb2fb686
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.round</h1>
+<p class="desc">createImageData(w, h) is rounded the same as getImageData(0, 0, w, h)</p>
+
+
+<script>
+var t = async_test("createImageData(w, h) is rounded the same as getImageData(0, 0, w, h)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, 10.01, 10.99);
+ _assertSame(imgdata1.width, imgdata2.width, "imgdata1.width", "imgdata2.width");
+ _assertSame(imgdata1.height, imgdata2.height, "imgdata1.height", "imgdata2.height");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.worker.js
new file mode 100644
index 0000000000..b416a02bf2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.round.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.round
+// Description:createImageData(w, h) is rounded the same as getImageData(0, 0, w, h)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(w, h) is rounded the same as getImageData(0, 0, w, h)");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, 10.01, 10.99);
+ _assertSame(imgdata1.width, imgdata2.width, "imgdata1.width", "imgdata2.width");
+ _assertSame(imgdata1.height, imgdata2.height, "imgdata1.height", "imgdata2.height");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.html
new file mode 100644
index 0000000000..2f7082a7f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.create2.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.create2.zero</h1>
+<p class="desc">createImageData(sw, sh) throws INDEX_SIZE_ERR if size is zero</p>
+
+
+<script>
+var t = async_test("createImageData(sw, sh) throws INDEX_SIZE_ERR if size is zero");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(10, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0.99, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(10, 0.1); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.worker.js
new file mode 100644
index 0000000000..7f57c33898
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.create2.zero.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.create2.zero
+// Description:createImageData(sw, sh) throws INDEX_SIZE_ERR if size is zero
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(sw, sh) throws INDEX_SIZE_ERR if size is zero");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(10, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(0.99, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.createImageData(10, 0.1); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.html
new file mode 100644
index 0000000000..d810e64e38
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.basic</h1>
+<p class="desc">getImageData() exists and returns something</p>
+
+
+<script>
+var t = async_test("getImageData() exists and returns something");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(ctx.getImageData(0, 0, 100, 50), null, "ctx.getImageData(0, 0, 100, 50)", "null");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.worker.js
new file mode 100644
index 0000000000..f28268be17
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.basic.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.basic
+// Description:getImageData() exists and returns something
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() exists and returns something");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertDifferent(ctx.getImageData(0, 0, 100, 50), null, "ctx.getImageData(0, 0, 100, 50)", "null");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.html
new file mode 100644
index 0000000000..825d00a5c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.clamp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.clamp</h1>
+<p class="desc">getImageData() clamps colors to the range [0, 255]</p>
+
+
+<script>
+var t = async_test("getImageData() clamps colors to the range [0, 255]");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgb(-100, -200, -300)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgb(256, 300, 400)';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata1.data[1], 0, "imgdata1.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata1.data[2], 0, "imgdata1.data[\""+(2)+"\"]", "0");
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ _assertSame(imgdata2.data[0], 255, "imgdata2.data[\""+(0)+"\"]", "255");
+ _assertSame(imgdata2.data[1], 255, "imgdata2.data[\""+(1)+"\"]", "255");
+ _assertSame(imgdata2.data[2], 255, "imgdata2.data[\""+(2)+"\"]", "255");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.worker.js
new file mode 100644
index 0000000000..fb4e76584a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.clamp.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.clamp
+// Description:getImageData() clamps colors to the range [0, 255]
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() clamps colors to the range [0, 255]");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgb(-100, -200, -300)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgb(256, 300, 400)';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata1.data[1], 0, "imgdata1.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata1.data[2], 0, "imgdata1.data[\""+(2)+"\"]", "0");
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ _assertSame(imgdata2.data[0], 255, "imgdata2.data[\""+(0)+"\"]", "255");
+ _assertSame(imgdata2.data[1], 255, "imgdata2.data[\""+(1)+"\"]", "255");
+ _assertSame(imgdata2.data[2], 255, "imgdata2.data[\""+(2)+"\"]", "255");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.html
new file mode 100644
index 0000000000..64fc7c7c12
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.double</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.double</h1>
+<p class="desc">createImageData(w, h) double is converted to long</p>
+
+
+<script>
+var t = async_test("createImageData(w, h) double is converted to long");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.getImageData(0, 0, 10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, -10.01, -10.99);
+ _assertSame(imgdata1.width, 10, "imgdata1.width", "10");
+ _assertSame(imgdata1.height, 10, "imgdata1.height", "10");
+ _assertSame(imgdata2.width, 10, "imgdata2.width", "10");
+ _assertSame(imgdata2.height, 10, "imgdata2.height", "10");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.worker.js
new file mode 100644
index 0000000000..316dce7f80
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.double.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.double
+// Description:createImageData(w, h) double is converted to long
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("createImageData(w, h) double is converted to long");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.getImageData(0, 0, 10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, -10.01, -10.99);
+ _assertSame(imgdata1.width, 10, "imgdata1.width", "10");
+ _assertSame(imgdata1.height, 10, "imgdata1.height", "10");
+ _assertSame(imgdata2.width, 10, "imgdata2.width", "10");
+ _assertSame(imgdata2.height, 10, "imgdata2.height", "10");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.html
new file mode 100644
index 0000000000..dfc5d106ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.invalid</h1>
+<p class="desc">Verify getImageData() behavior in invalid cases.</p>
+
+
+<script>
+var t = async_test("Verify getImageData() behavior in invalid cases.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ imageData = ctx.getImageData(0,0,2,2);
+ var testValues = [NaN, true, false, "\"garbage\"", "-1",
+ "0", "1", "2", Infinity, -Infinity,
+ -5, -0.5, 0, 0.5, 5,
+ 5.4, 255, 256, null, undefined];
+ var testResults = [0, 1, 0, 0, 0,
+ 0, 1, 2, 255, 0,
+ 0, 0, 0, 0, 5,
+ 5, 255, 255, 0, 0];
+ for (var i = 0; i < testValues.length; i++) {
+ imageData.data[0] = testValues[i];
+ _assert(imageData.data[0] == testResults[i], "imageData.data[\""+(0)+"\"] == testResults[\""+(i)+"\"]");
+ }
+ imageData.data['foo']='garbage';
+ _assert(imageData.data['foo'] == 'garbage', "imageData.data['foo'] == 'garbage'");
+ imageData.data[-1]='garbage';
+ _assert(imageData.data[-1] == undefined, "imageData.data[-1] == undefined");
+ imageData.data[17]='garbage';
+ _assert(imageData.data[17] == undefined, "imageData.data[\""+(17)+"\"] == undefined");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.worker.js
new file mode 100644
index 0000000000..5206899b6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.invalid.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.invalid
+// Description:Verify getImageData() behavior in invalid cases.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Verify getImageData() behavior in invalid cases.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ imageData = ctx.getImageData(0,0,2,2);
+ var testValues = [NaN, true, false, "\"garbage\"", "-1",
+ "0", "1", "2", Infinity, -Infinity,
+ -5, -0.5, 0, 0.5, 5,
+ 5.4, 255, 256, null, undefined];
+ var testResults = [0, 1, 0, 0, 0,
+ 0, 1, 2, 255, 0,
+ 0, 0, 0, 0, 5,
+ 5, 255, 255, 0, 0];
+ for (var i = 0; i < testValues.length; i++) {
+ imageData.data[0] = testValues[i];
+ _assert(imageData.data[0] == testResults[i], "imageData.data[\""+(0)+"\"] == testResults[\""+(i)+"\"]");
+ }
+ imageData.data['foo']='garbage';
+ _assert(imageData.data['foo'] == 'garbage', "imageData.data['foo'] == 'garbage'");
+ imageData.data[-1]='garbage';
+ _assert(imageData.data[-1] == undefined, "imageData.data[-1] == undefined");
+ imageData.data[17]='garbage';
+ _assert(imageData.data[17] == undefined, "imageData.data[\""+(17)+"\"] == undefined");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.html
new file mode 100644
index 0000000000..3e05c3c92b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.large.crash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.large.crash</h1>
+<p class="desc">Test that canvas crash when image data cannot be allocated.</p>
+
+
+<script>
+var t = async_test("Test that canvas crash when image data cannot be allocated.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 0xffffffff, 2147483647, 10); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.worker.js
new file mode 100644
index 0000000000..62c1771dba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.large.crash.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.large.crash
+// Description:Test that canvas crash when image data cannot be allocated.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test that canvas crash when image data cannot be allocated.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 0xffffffff, 2147483647, 10); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.html
new file mode 100644
index 0000000000..47b5fcd166
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.length</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.length</h1>
+<p class="desc">getImageData() returns a correctly-sized Uint8ClampedArray</p>
+
+
+<script>
+var t = async_test("getImageData() returns a correctly-sized Uint8ClampedArray");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.worker.js
new file mode 100644
index 0000000000..74ed6015f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.length.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.length
+// Description:getImageData() returns a correctly-sized Uint8ClampedArray
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns a correctly-sized Uint8ClampedArray");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data.length, imgdata.width*imgdata.height*4, "imgdata.data.length", "imgdata.width*imgdata.height*4");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.html
new file mode 100644
index 0000000000..62cad61848
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.nonfinite</h1>
+<p class="desc">getImageData() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("getImageData() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(-Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(NaN, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, -Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, NaN, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, Infinity, Infinity); });
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(neginfobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(nanobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, neginfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, nanobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, neginfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, nanobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, neginfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, nanobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, posinfobj, posinfobj); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.worker.js
new file mode 100644
index 0000000000..98b4913a13
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonfinite.worker.js
@@ -0,0 +1,70 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.nonfinite
+// Description:getImageData() throws TypeError if arguments are not finite
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(-Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(NaN, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, -Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, NaN, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, Infinity, Infinity); });
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(neginfobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(nanobj, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, neginfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, nanobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, neginfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, nanobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, neginfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, 10, nanobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, posinfobj, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(posinfobj, 10, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, posinfobj, 10); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, posinfobj, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, posinfobj, 10, posinfobj); });
+ assert_throws_js(TypeError, function() { ctx.getImageData(10, 10, posinfobj, posinfobj); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.html
new file mode 100644
index 0000000000..3a97d9dc05
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.nonpremul</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.nonpremul</h1>
+<p class="desc">getImageData() returns non-premultiplied colors</p>
+
+
+<script>
+var t = async_test("getImageData() returns non-premultiplied colors");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(10, 10, 10, 10);
+ _assert(imgdata.data[0] > 200, "imgdata.data[\""+(0)+"\"] > 200");
+ _assert(imgdata.data[1] > 200, "imgdata.data[\""+(1)+"\"] > 200");
+ _assert(imgdata.data[2] > 200, "imgdata.data[\""+(2)+"\"] > 200");
+ _assert(imgdata.data[3] > 100, "imgdata.data[\""+(3)+"\"] > 100");
+ _assert(imgdata.data[3] < 200, "imgdata.data[\""+(3)+"\"] < 200");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.worker.js
new file mode 100644
index 0000000000..1a8da5efb0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.nonpremul.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.nonpremul
+// Description:getImageData() returns non-premultiplied colors
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns non-premultiplied colors");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(10, 10, 10, 10);
+ _assert(imgdata.data[0] > 200, "imgdata.data[\""+(0)+"\"] > 200");
+ _assert(imgdata.data[1] > 200, "imgdata.data[\""+(1)+"\"] > 200");
+ _assert(imgdata.data[2] > 200, "imgdata.data[\""+(2)+"\"] > 200");
+ _assert(imgdata.data[3] > 100, "imgdata.data[\""+(3)+"\"] > 100");
+ _assert(imgdata.data[3] < 200, "imgdata.data[\""+(3)+"\"] < 200");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.html
new file mode 100644
index 0000000000..04eb67902f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.order.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.order.alpha</h1>
+<p class="desc">getImageData() returns A in the fourth component</p>
+
+
+<script>
+var t = async_test("getImageData() returns A in the fourth component");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assert(imgdata.data[3] < 200, "imgdata.data[\""+(3)+"\"] < 200");
+ _assert(imgdata.data[3] > 100, "imgdata.data[\""+(3)+"\"] > 100");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.worker.js
new file mode 100644
index 0000000000..4338436a6d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.alpha.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.order.alpha
+// Description:getImageData() returns A in the fourth component
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns A in the fourth component");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assert(imgdata.data[3] < 200, "imgdata.data[\""+(3)+"\"] < 200");
+ _assert(imgdata.data[3] > 100, "imgdata.data[\""+(3)+"\"] > 100");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.html
new file mode 100644
index 0000000000..f5b315c60a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.order.cols</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.order.cols</h1>
+<p class="desc">getImageData() returns leftmost columns first</p>
+
+
+<script>
+var t = async_test("getImageData() returns leftmost columns first");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 2, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[Math.round(imgdata.width/2*4)], 255, "imgdata.data[Math.round(imgdata.width/2*4)]", "255");
+ _assertSame(imgdata.data[Math.round((imgdata.height/2)*imgdata.width*4)], 0, "imgdata.data[Math.round((imgdata.height/2)*imgdata.width*4)]", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.worker.js
new file mode 100644
index 0000000000..6a070e2516
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.cols.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.order.cols
+// Description:getImageData() returns leftmost columns first
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns leftmost columns first");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 2, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[Math.round(imgdata.width/2*4)], 255, "imgdata.data[Math.round(imgdata.width/2*4)]", "255");
+ _assertSame(imgdata.data[Math.round((imgdata.height/2)*imgdata.width*4)], 0, "imgdata.data[Math.round((imgdata.height/2)*imgdata.width*4)]", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.html
new file mode 100644
index 0000000000..661b437fb1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.order.rgb</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.order.rgb</h1>
+<p class="desc">getImageData() returns R then G then B</p>
+
+
+<script>
+var t = async_test("getImageData() returns R then G then B");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#48c';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0x44, "imgdata.data[\""+(0)+"\"]", "0x44");
+ _assertSame(imgdata.data[1], 0x88, "imgdata.data[\""+(1)+"\"]", "0x88");
+ _assertSame(imgdata.data[2], 0xCC, "imgdata.data[\""+(2)+"\"]", "0xCC");
+ _assertSame(imgdata.data[3], 255, "imgdata.data[\""+(3)+"\"]", "255");
+ _assertSame(imgdata.data[4], 0x44, "imgdata.data[\""+(4)+"\"]", "0x44");
+ _assertSame(imgdata.data[5], 0x88, "imgdata.data[\""+(5)+"\"]", "0x88");
+ _assertSame(imgdata.data[6], 0xCC, "imgdata.data[\""+(6)+"\"]", "0xCC");
+ _assertSame(imgdata.data[7], 255, "imgdata.data[\""+(7)+"\"]", "255");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.worker.js
new file mode 100644
index 0000000000..4e5974f9f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rgb.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.order.rgb
+// Description:getImageData() returns R then G then B
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns R then G then B");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#48c';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0x44, "imgdata.data[\""+(0)+"\"]", "0x44");
+ _assertSame(imgdata.data[1], 0x88, "imgdata.data[\""+(1)+"\"]", "0x88");
+ _assertSame(imgdata.data[2], 0xCC, "imgdata.data[\""+(2)+"\"]", "0xCC");
+ _assertSame(imgdata.data[3], 255, "imgdata.data[\""+(3)+"\"]", "255");
+ _assertSame(imgdata.data[4], 0x44, "imgdata.data[\""+(4)+"\"]", "0x44");
+ _assertSame(imgdata.data[5], 0x88, "imgdata.data[\""+(5)+"\"]", "0x88");
+ _assertSame(imgdata.data[6], 0xCC, "imgdata.data[\""+(6)+"\"]", "0xCC");
+ _assertSame(imgdata.data[7], 255, "imgdata.data[\""+(7)+"\"]", "255");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.html
new file mode 100644
index 0000000000..6d8ee2eba5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.order.rows</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.order.rows</h1>
+<p class="desc">getImageData() returns topmost rows first</p>
+
+
+<script>
+var t = async_test("getImageData() returns topmost rows first");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 2);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[Math.floor(imgdata.width/2*4)], 0, "imgdata.data[Math.floor(imgdata.width/2*4)]", "0");
+ _assertSame(imgdata.data[(imgdata.height/2)*imgdata.width*4], 255, "imgdata.data[(imgdata.height/2)*imgdata.width*4]", "255");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.worker.js
new file mode 100644
index 0000000000..1d3226a503
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.order.rows.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.order.rows
+// Description:getImageData() returns topmost rows first
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns topmost rows first");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 2);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[Math.floor(imgdata.width/2*4)], 0, "imgdata.data[Math.floor(imgdata.width/2*4)]", "0");
+ _assertSame(imgdata.data[(imgdata.height/2)*imgdata.width*4], 255, "imgdata.data[(imgdata.height/2)*imgdata.width*4]", "255");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.html
new file mode 100644
index 0000000000..8db1a42ca1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.range</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.range</h1>
+<p class="desc">getImageData() returns values in the range [0, 255]</p>
+
+
+<script>
+var t = async_test("getImageData() returns values in the range [0, 255]");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ _assertSame(imgdata2.data[0], 255, "imgdata2.data[\""+(0)+"\"]", "255");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.worker.js
new file mode 100644
index 0000000000..2e962beeae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.range.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.range
+// Description:getImageData() returns values in the range [0, 255]
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns values in the range [0, 255]");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ _assertSame(imgdata2.data[0], 255, "imgdata2.data[\""+(0)+"\"]", "255");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.html
new file mode 100644
index 0000000000..f939ba82df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.rounding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.rounding</h1>
+<p class="desc">Test the handling of non-integer source coordinates in getImageData().</p>
+
+
+<script>
+var t = async_test("Test the handling of non-integer source coordinates in getImageData().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ function testDimensions(sx, sy, sw, sh, width, height)
+ {
+ imageData = ctx.getImageData(sx, sy, sw, sh);
+ _assert(imageData.width == width, "imageData.width == width");
+ _assert(imageData.height == height, "imageData.height == height");
+ }
+
+ testDimensions(0, 0, 20, 10, 20, 10);
+
+ testDimensions(.1, .2, 20, 10, 20, 10);
+ testDimensions(.9, .8, 20, 10, 20, 10);
+
+ testDimensions(0, 0, 20.9, 10.9, 20, 10);
+ testDimensions(0, 0, 20.1, 10.1, 20, 10);
+
+ testDimensions(-1, -1, 20, 10, 20, 10);
+
+ testDimensions(-1.1, 0, 20, 10, 20, 10);
+ testDimensions(-1.9, 0, 20, 10, 20, 10);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.worker.js
new file mode 100644
index 0000000000..b2cd08ade2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.rounding.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.rounding
+// Description:Test the handling of non-integer source coordinates in getImageData().
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Test the handling of non-integer source coordinates in getImageData().");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ function testDimensions(sx, sy, sw, sh, width, height)
+ {
+ imageData = ctx.getImageData(sx, sy, sw, sh);
+ _assert(imageData.width == width, "imageData.width == width");
+ _assert(imageData.height == height, "imageData.height == height");
+ }
+
+ testDimensions(0, 0, 20, 10, 20, 10);
+
+ testDimensions(.1, .2, 20, 10, 20, 10);
+ testDimensions(.9, .8, 20, 10, 20, 10);
+
+ testDimensions(0, 0, 20.9, 10.9, 20, 10);
+ testDimensions(0, 0, 20.1, 10.1, 20, 10);
+
+ testDimensions(-1, -1, 20, 10, 20, 10);
+
+ testDimensions(-1.1, 0, 20, 10, 20, 10);
+ testDimensions(-1.9, 0, 20, 10, 20, 10);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.html
new file mode 100644
index 0000000000..a4b9c7e41c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.source.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.source.negative</h1>
+<p class="desc">getImageData() works with negative width and height, and returns top-to-bottom left-to-right</p>
+
+
+<script>
+var t = async_test("getImageData() works with negative width and height, and returns top-to-bottom left-to-right");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+
+ var imgdata1 = ctx.getImageData(85, 25, -10, -10);
+ _assertSame(imgdata1.data[0], 255, "imgdata1.data[\""+(0)+"\"]", "255");
+ _assertSame(imgdata1.data[1], 255, "imgdata1.data[\""+(1)+"\"]", "255");
+ _assertSame(imgdata1.data[2], 255, "imgdata1.data[\""+(2)+"\"]", "255");
+ _assertSame(imgdata1.data[3], 255, "imgdata1.data[\""+(3)+"\"]", "255");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+0], 0, "imgdata1.data[imgdata1.data.length-4+0]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+1], 0, "imgdata1.data[imgdata1.data.length-4+1]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+2], 0, "imgdata1.data[imgdata1.data.length-4+2]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+3], 255, "imgdata1.data[imgdata1.data.length-4+3]", "255");
+
+ var imgdata2 = ctx.getImageData(0, 0, -1, -1);
+ _assertSame(imgdata2.data[0], 0, "imgdata2.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata2.data[1], 0, "imgdata2.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata2.data[2], 0, "imgdata2.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata2.data[3], 0, "imgdata2.data[\""+(3)+"\"]", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.worker.js
new file mode 100644
index 0000000000..62929918ec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.negative.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.source.negative
+// Description:getImageData() works with negative width and height, and returns top-to-bottom left-to-right
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() works with negative width and height, and returns top-to-bottom left-to-right");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+
+ var imgdata1 = ctx.getImageData(85, 25, -10, -10);
+ _assertSame(imgdata1.data[0], 255, "imgdata1.data[\""+(0)+"\"]", "255");
+ _assertSame(imgdata1.data[1], 255, "imgdata1.data[\""+(1)+"\"]", "255");
+ _assertSame(imgdata1.data[2], 255, "imgdata1.data[\""+(2)+"\"]", "255");
+ _assertSame(imgdata1.data[3], 255, "imgdata1.data[\""+(3)+"\"]", "255");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+0], 0, "imgdata1.data[imgdata1.data.length-4+0]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+1], 0, "imgdata1.data[imgdata1.data.length-4+1]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+2], 0, "imgdata1.data[imgdata1.data.length-4+2]", "0");
+ _assertSame(imgdata1.data[imgdata1.data.length-4+3], 255, "imgdata1.data[imgdata1.data.length-4+3]", "255");
+
+ var imgdata2 = ctx.getImageData(0, 0, -1, -1);
+ _assertSame(imgdata2.data[0], 0, "imgdata2.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata2.data[1], 0, "imgdata2.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata2.data[2], 0, "imgdata2.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata2.data[3], 0, "imgdata2.data[\""+(3)+"\"]", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.html
new file mode 100644
index 0000000000..ca11da80d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.source.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.source.outside</h1>
+<p class="desc">getImageData() returns transparent black outside the canvas</p>
+
+
+<script>
+var t = async_test("getImageData() returns transparent black outside the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#08f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var imgdata1 = ctx.getImageData(-10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata1.data[1], 0, "imgdata1.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata1.data[2], 0, "imgdata1.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata1.data[3], 0, "imgdata1.data[\""+(3)+"\"]", "0");
+
+ var imgdata2 = ctx.getImageData(10, -5, 1, 1);
+ _assertSame(imgdata2.data[0], 0, "imgdata2.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata2.data[1], 0, "imgdata2.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata2.data[2], 0, "imgdata2.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata2.data[3], 0, "imgdata2.data[\""+(3)+"\"]", "0");
+
+ var imgdata3 = ctx.getImageData(200, 5, 1, 1);
+ _assertSame(imgdata3.data[0], 0, "imgdata3.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata3.data[1], 0, "imgdata3.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata3.data[2], 0, "imgdata3.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata3.data[3], 0, "imgdata3.data[\""+(3)+"\"]", "0");
+
+ var imgdata4 = ctx.getImageData(10, 60, 1, 1);
+ _assertSame(imgdata4.data[0], 0, "imgdata4.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata4.data[1], 0, "imgdata4.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata4.data[2], 0, "imgdata4.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata4.data[3], 0, "imgdata4.data[\""+(3)+"\"]", "0");
+
+ var imgdata5 = ctx.getImageData(100, 10, 1, 1);
+ _assertSame(imgdata5.data[0], 0, "imgdata5.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata5.data[1], 0, "imgdata5.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata5.data[2], 0, "imgdata5.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata5.data[3], 0, "imgdata5.data[\""+(3)+"\"]", "0");
+
+ var imgdata6 = ctx.getImageData(0, 10, 1, 1);
+ _assertSame(imgdata6.data[0], 0, "imgdata6.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata6.data[1], 136, "imgdata6.data[\""+(1)+"\"]", "136");
+ _assertSame(imgdata6.data[2], 255, "imgdata6.data[\""+(2)+"\"]", "255");
+ _assertSame(imgdata6.data[3], 255, "imgdata6.data[\""+(3)+"\"]", "255");
+
+ var imgdata7 = ctx.getImageData(-10, 10, 20, 20);
+ _assertSame(imgdata7.data[ 0*4+0], 0, "imgdata7.data[ 0*4+0]", "0");
+ _assertSame(imgdata7.data[ 0*4+1], 0, "imgdata7.data[ 0*4+1]", "0");
+ _assertSame(imgdata7.data[ 0*4+2], 0, "imgdata7.data[ 0*4+2]", "0");
+ _assertSame(imgdata7.data[ 0*4+3], 0, "imgdata7.data[ 0*4+3]", "0");
+ _assertSame(imgdata7.data[ 9*4+0], 0, "imgdata7.data[ 9*4+0]", "0");
+ _assertSame(imgdata7.data[ 9*4+1], 0, "imgdata7.data[ 9*4+1]", "0");
+ _assertSame(imgdata7.data[ 9*4+2], 0, "imgdata7.data[ 9*4+2]", "0");
+ _assertSame(imgdata7.data[ 9*4+3], 0, "imgdata7.data[ 9*4+3]", "0");
+ _assertSame(imgdata7.data[10*4+0], 0, "imgdata7.data[10*4+0]", "0");
+ _assertSame(imgdata7.data[10*4+1], 136, "imgdata7.data[10*4+1]", "136");
+ _assertSame(imgdata7.data[10*4+2], 255, "imgdata7.data[10*4+2]", "255");
+ _assertSame(imgdata7.data[10*4+3], 255, "imgdata7.data[10*4+3]", "255");
+ _assertSame(imgdata7.data[19*4+0], 0, "imgdata7.data[19*4+0]", "0");
+ _assertSame(imgdata7.data[19*4+1], 136, "imgdata7.data[19*4+1]", "136");
+ _assertSame(imgdata7.data[19*4+2], 255, "imgdata7.data[19*4+2]", "255");
+ _assertSame(imgdata7.data[19*4+3], 255, "imgdata7.data[19*4+3]", "255");
+ _assertSame(imgdata7.data[20*4+0], 0, "imgdata7.data[20*4+0]", "0");
+ _assertSame(imgdata7.data[20*4+1], 0, "imgdata7.data[20*4+1]", "0");
+ _assertSame(imgdata7.data[20*4+2], 0, "imgdata7.data[20*4+2]", "0");
+ _assertSame(imgdata7.data[20*4+3], 0, "imgdata7.data[20*4+3]", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.worker.js
new file mode 100644
index 0000000000..35fa83d668
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.outside.worker.js
@@ -0,0 +1,81 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.source.outside
+// Description:getImageData() returns transparent black outside the canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns transparent black outside the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#08f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var imgdata1 = ctx.getImageData(-10, 5, 1, 1);
+ _assertSame(imgdata1.data[0], 0, "imgdata1.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata1.data[1], 0, "imgdata1.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata1.data[2], 0, "imgdata1.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata1.data[3], 0, "imgdata1.data[\""+(3)+"\"]", "0");
+
+ var imgdata2 = ctx.getImageData(10, -5, 1, 1);
+ _assertSame(imgdata2.data[0], 0, "imgdata2.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata2.data[1], 0, "imgdata2.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata2.data[2], 0, "imgdata2.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata2.data[3], 0, "imgdata2.data[\""+(3)+"\"]", "0");
+
+ var imgdata3 = ctx.getImageData(200, 5, 1, 1);
+ _assertSame(imgdata3.data[0], 0, "imgdata3.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata3.data[1], 0, "imgdata3.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata3.data[2], 0, "imgdata3.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata3.data[3], 0, "imgdata3.data[\""+(3)+"\"]", "0");
+
+ var imgdata4 = ctx.getImageData(10, 60, 1, 1);
+ _assertSame(imgdata4.data[0], 0, "imgdata4.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata4.data[1], 0, "imgdata4.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata4.data[2], 0, "imgdata4.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata4.data[3], 0, "imgdata4.data[\""+(3)+"\"]", "0");
+
+ var imgdata5 = ctx.getImageData(100, 10, 1, 1);
+ _assertSame(imgdata5.data[0], 0, "imgdata5.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata5.data[1], 0, "imgdata5.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata5.data[2], 0, "imgdata5.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata5.data[3], 0, "imgdata5.data[\""+(3)+"\"]", "0");
+
+ var imgdata6 = ctx.getImageData(0, 10, 1, 1);
+ _assertSame(imgdata6.data[0], 0, "imgdata6.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata6.data[1], 136, "imgdata6.data[\""+(1)+"\"]", "136");
+ _assertSame(imgdata6.data[2], 255, "imgdata6.data[\""+(2)+"\"]", "255");
+ _assertSame(imgdata6.data[3], 255, "imgdata6.data[\""+(3)+"\"]", "255");
+
+ var imgdata7 = ctx.getImageData(-10, 10, 20, 20);
+ _assertSame(imgdata7.data[ 0*4+0], 0, "imgdata7.data[ 0*4+0]", "0");
+ _assertSame(imgdata7.data[ 0*4+1], 0, "imgdata7.data[ 0*4+1]", "0");
+ _assertSame(imgdata7.data[ 0*4+2], 0, "imgdata7.data[ 0*4+2]", "0");
+ _assertSame(imgdata7.data[ 0*4+3], 0, "imgdata7.data[ 0*4+3]", "0");
+ _assertSame(imgdata7.data[ 9*4+0], 0, "imgdata7.data[ 9*4+0]", "0");
+ _assertSame(imgdata7.data[ 9*4+1], 0, "imgdata7.data[ 9*4+1]", "0");
+ _assertSame(imgdata7.data[ 9*4+2], 0, "imgdata7.data[ 9*4+2]", "0");
+ _assertSame(imgdata7.data[ 9*4+3], 0, "imgdata7.data[ 9*4+3]", "0");
+ _assertSame(imgdata7.data[10*4+0], 0, "imgdata7.data[10*4+0]", "0");
+ _assertSame(imgdata7.data[10*4+1], 136, "imgdata7.data[10*4+1]", "136");
+ _assertSame(imgdata7.data[10*4+2], 255, "imgdata7.data[10*4+2]", "255");
+ _assertSame(imgdata7.data[10*4+3], 255, "imgdata7.data[10*4+3]", "255");
+ _assertSame(imgdata7.data[19*4+0], 0, "imgdata7.data[19*4+0]", "0");
+ _assertSame(imgdata7.data[19*4+1], 136, "imgdata7.data[19*4+1]", "136");
+ _assertSame(imgdata7.data[19*4+2], 255, "imgdata7.data[19*4+2]", "255");
+ _assertSame(imgdata7.data[19*4+3], 255, "imgdata7.data[19*4+3]", "255");
+ _assertSame(imgdata7.data[20*4+0], 0, "imgdata7.data[20*4+0]", "0");
+ _assertSame(imgdata7.data[20*4+1], 0, "imgdata7.data[20*4+1]", "0");
+ _assertSame(imgdata7.data[20*4+2], 0, "imgdata7.data[20*4+2]", "0");
+ _assertSame(imgdata7.data[20*4+3], 0, "imgdata7.data[20*4+3]", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.html
new file mode 100644
index 0000000000..6cb848a718
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.source.size</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.source.size</h1>
+<p class="desc">getImageData() returns bigger ImageData for bigger source rectangle</p>
+
+
+<script>
+var t = async_test("getImageData() returns bigger ImageData for bigger source rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.getImageData(0, 0, 10, 10);
+ var imgdata2 = ctx.getImageData(0, 0, 20, 20);
+ _assert(imgdata2.width > imgdata1.width, "imgdata2.width > imgdata1.width");
+ _assert(imgdata2.height > imgdata1.height, "imgdata2.height > imgdata1.height");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.worker.js
new file mode 100644
index 0000000000..e54d75b97a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.source.size.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.source.size
+// Description:getImageData() returns bigger ImageData for bigger source rectangle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() returns bigger ImageData for bigger source rectangle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata1 = ctx.getImageData(0, 0, 10, 10);
+ var imgdata2 = ctx.getImageData(0, 0, 20, 20);
+ _assert(imgdata2.width > imgdata1.width, "imgdata2.width > imgdata1.width");
+ _assert(imgdata2.height > imgdata1.height, "imgdata2.height > imgdata1.height");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.html
new file mode 100644
index 0000000000..66a0b80fd7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.unaffected</h1>
+<p class="desc">getImageData() is not affected by context state</p>
+
+
+<script>
+var t = async_test("getImageData() is not affected by context state");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50)
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.rect(0, 0, 5, 5);
+ ctx.clip();
+ var imgdata = ctx.getImageData(0, 0, 50, 50);
+ ctx.restore();
+ ctx.putImageData(imgdata, 50, 0);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.worker.js
new file mode 100644
index 0000000000..8bf0dddb06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.unaffected.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.unaffected
+// Description:getImageData() is not affected by context state
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() is not affected by context state");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50)
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.rect(0, 0, 5, 5);
+ ctx.clip();
+ var imgdata = ctx.getImageData(0, 0, 50, 50);
+ ctx.restore();
+ ctx.putImageData(imgdata, 50, 0);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.html
new file mode 100644
index 0000000000..042a8bc5f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.get.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.get.zero</h1>
+<p class="desc">getImageData() throws INDEX_SIZE_ERR if size is zero</p>
+
+
+<script>
+var t = async_test("getImageData() throws INDEX_SIZE_ERR if size is zero");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0.1, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, 0.99); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, -0.1, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, -0.99); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.worker.js
new file mode 100644
index 0000000000..ed31030d3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.get.zero.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.get.zero
+// Description:getImageData() throws INDEX_SIZE_ERR if size is zero
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("getImageData() throws INDEX_SIZE_ERR if size is zero");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0, 0); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 0.1, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, 0.99); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, -0.1, 10); });
+ assert_throws_dom("INDEX_SIZE_ERR", function() { ctx.getImageData(1, 1, 10, -0.99); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.html
new file mode 100644
index 0000000000..aa6073eedf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.clamp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.clamp</h1>
+<p class="desc">ImageData.data clamps numbers to [0, 255]</p>
+
+
+<script>
+var t = async_test("ImageData.data clamps numbers to [0, 255]");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 300;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -100;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 200+Math.pow(2, 32);
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -200-Math.pow(2, 32);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Math.pow(10, 39);
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Math.pow(10, 39);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Infinity;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Infinity;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.worker.js
new file mode 100644
index 0000000000..260198fe2f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.clamp.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.clamp
+// Description:ImageData.data clamps numbers to [0, 255]
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData.data clamps numbers to [0, 255]");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 300;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -100;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 200+Math.pow(2, 32);
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -200-Math.pow(2, 32);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Math.pow(10, 39);
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Math.pow(10, 39);
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Infinity;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Infinity;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.html
new file mode 100644
index 0000000000..af692a7f9f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.nan</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.nan</h1>
+<p class="desc">ImageData.data converts NaN to 0</p>
+
+
+<script>
+var t = async_test("ImageData.data converts NaN to 0");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = NaN;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "cheese";
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.worker.js
new file mode 100644
index 0000000000..4ebfedb1e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.nan.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.nan
+// Description:ImageData.data converts NaN to 0
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData.data converts NaN to 0");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = NaN;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "cheese";
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.html
new file mode 100644
index 0000000000..68a6f63868
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.properties</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.properties</h1>
+<p class="desc">ImageData objects have the right properties</p>
+
+
+<script>
+var t = async_test("ImageData objects have the right properties");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(typeof(imgdata.width), 'number', "typeof(imgdata.width)", "'number'");
+ _assertSame(typeof(imgdata.height), 'number', "typeof(imgdata.height)", "'number'");
+ _assertSame(typeof(imgdata.data), 'object', "typeof(imgdata.data)", "'object'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.worker.js
new file mode 100644
index 0000000000..e428e0d4fc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.properties.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.properties
+// Description:ImageData objects have the right properties
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData objects have the right properties");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ _assertSame(typeof(imgdata.width), 'number', "typeof(imgdata.width)", "'number'");
+ _assertSame(typeof(imgdata.height), 'number', "typeof(imgdata.height)", "'number'");
+ _assertSame(typeof(imgdata.data), 'object', "typeof(imgdata.data)", "'object'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.html
new file mode 100644
index 0000000000..2ab5f47f64
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.readonly</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.readonly</h1>
+<p class="desc">ImageData objects properties are read-only</p>
+
+
+<script>
+var t = async_test("ImageData objects properties are read-only");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ var w = imgdata.width;
+ var h = imgdata.height;
+ var d = imgdata.data;
+ imgdata.width = 123;
+ imgdata.height = 123;
+ imgdata.data = [100,100,100,100];
+ _assertSame(imgdata.width, w, "imgdata.width", "w");
+ _assertSame(imgdata.height, h, "imgdata.height", "h");
+ _assertSame(imgdata.data, d, "imgdata.data", "d");
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[1], 0, "imgdata.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata.data[2], 0, "imgdata.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata.data[3], 0, "imgdata.data[\""+(3)+"\"]", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.worker.js
new file mode 100644
index 0000000000..d02f0f319a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.readonly.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.readonly
+// Description:ImageData objects properties are read-only
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData objects properties are read-only");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ var w = imgdata.width;
+ var h = imgdata.height;
+ var d = imgdata.data;
+ imgdata.width = 123;
+ imgdata.height = 123;
+ imgdata.data = [100,100,100,100];
+ _assertSame(imgdata.width, w, "imgdata.width", "w");
+ _assertSame(imgdata.height, h, "imgdata.height", "h");
+ _assertSame(imgdata.data, d, "imgdata.data", "d");
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ _assertSame(imgdata.data[1], 0, "imgdata.data[\""+(1)+"\"]", "0");
+ _assertSame(imgdata.data[2], 0, "imgdata.data[\""+(2)+"\"]", "0");
+ _assertSame(imgdata.data[3], 0, "imgdata.data[\""+(3)+"\"]", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.html
new file mode 100644
index 0000000000..fbde0cd940
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.round</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.round</h1>
+<p class="desc">ImageData.data rounds numbers with round-to-zero</p>
+
+
+<script>
+var t = async_test("ImageData.data rounds numbers with round-to-zero");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 0.499;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 0.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 0.501;
+ _assertSame(imgdata.data[0], 1, "imgdata.data[\""+(0)+"\"]", "1");
+ imgdata.data[0] = 1.499;
+ _assertSame(imgdata.data[0], 1, "imgdata.data[\""+(0)+"\"]", "1");
+ imgdata.data[0] = 1.5;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 1.501;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 2.5;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 3.5;
+ _assertSame(imgdata.data[0], 4, "imgdata.data[\""+(0)+"\"]", "4");
+ imgdata.data[0] = 252.5;
+ _assertSame(imgdata.data[0], 252, "imgdata.data[\""+(0)+"\"]", "252");
+ imgdata.data[0] = 253.5;
+ _assertSame(imgdata.data[0], 254, "imgdata.data[\""+(0)+"\"]", "254");
+ imgdata.data[0] = 254.5;
+ _assertSame(imgdata.data[0], 254, "imgdata.data[\""+(0)+"\"]", "254");
+ imgdata.data[0] = 256.5;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = -0.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = -1.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.worker.js
new file mode 100644
index 0000000000..8ef3c02793
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.round.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.round
+// Description:ImageData.data rounds numbers with round-to-zero
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData.data rounds numbers with round-to-zero");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 0.499;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 0.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = 0.501;
+ _assertSame(imgdata.data[0], 1, "imgdata.data[\""+(0)+"\"]", "1");
+ imgdata.data[0] = 1.499;
+ _assertSame(imgdata.data[0], 1, "imgdata.data[\""+(0)+"\"]", "1");
+ imgdata.data[0] = 1.5;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 1.501;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 2.5;
+ _assertSame(imgdata.data[0], 2, "imgdata.data[\""+(0)+"\"]", "2");
+ imgdata.data[0] = 3.5;
+ _assertSame(imgdata.data[0], 4, "imgdata.data[\""+(0)+"\"]", "4");
+ imgdata.data[0] = 252.5;
+ _assertSame(imgdata.data[0], 252, "imgdata.data[\""+(0)+"\"]", "252");
+ imgdata.data[0] = 253.5;
+ _assertSame(imgdata.data[0], 254, "imgdata.data[\""+(0)+"\"]", "254");
+ imgdata.data[0] = 254.5;
+ _assertSame(imgdata.data[0], 254, "imgdata.data[\""+(0)+"\"]", "254");
+ imgdata.data[0] = 256.5;
+ _assertSame(imgdata.data[0], 255, "imgdata.data[\""+(0)+"\"]", "255");
+ imgdata.data[0] = -0.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ imgdata.data[0] = -1.5;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.html
new file mode 100644
index 0000000000..27ed5a3504
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.set</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.set</h1>
+<p class="desc">ImageData.data can be modified</p>
+
+
+<script>
+var t = async_test("ImageData.data can be modified");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ _assertSame(imgdata.data[0], 100, "imgdata.data[\""+(0)+"\"]", "100");
+ imgdata.data[0] = 200;
+ _assertSame(imgdata.data[0], 200, "imgdata.data[\""+(0)+"\"]", "200");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.worker.js
new file mode 100644
index 0000000000..f9b755f7e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.set.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.set
+// Description:ImageData.data can be modified
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData.data can be modified");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ _assertSame(imgdata.data[0], 100, "imgdata.data[\""+(0)+"\"]", "100");
+ imgdata.data[0] = 200;
+ _assertSame(imgdata.data[0], 200, "imgdata.data[\""+(0)+"\"]", "200");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.html
new file mode 100644
index 0000000000..a437c475a5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.string</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.string</h1>
+<p class="desc">ImageData.data converts strings to numbers with ToNumber</p>
+
+
+<script>
+var t = async_test("ImageData.data converts strings to numbers with ToNumber");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "110";
+ _assertSame(imgdata.data[0], 110, "imgdata.data[\""+(0)+"\"]", "110");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "0x78";
+ _assertSame(imgdata.data[0], 120, "imgdata.data[\""+(0)+"\"]", "120");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = " +130e0 ";
+ _assertSame(imgdata.data[0], 130, "imgdata.data[\""+(0)+"\"]", "130");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.worker.js
new file mode 100644
index 0000000000..84cff6a48c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.string.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.string
+// Description:ImageData.data converts strings to numbers with ToNumber
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData.data converts strings to numbers with ToNumber");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "110";
+ _assertSame(imgdata.data[0], 110, "imgdata.data[\""+(0)+"\"]", "110");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "0x78";
+ _assertSame(imgdata.data[0], 120, "imgdata.data[\""+(0)+"\"]", "120");
+ imgdata.data[0] = 100;
+ imgdata.data[0] = " +130e0 ";
+ _assertSame(imgdata.data[0], 130, "imgdata.data[\""+(0)+"\"]", "130");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.html
new file mode 100644
index 0000000000..81462339e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.object.undefined</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.object.undefined</h1>
+<p class="desc">ImageData.data converts undefined to 0</p>
+
+
+<script>
+var t = async_test("ImageData.data converts undefined to 0");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = undefined;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.worker.js
new file mode 100644
index 0000000000..825f8c102f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.object.undefined.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.object.undefined
+// Description:ImageData.data converts undefined to 0
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("ImageData.data converts undefined to 0");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = undefined;
+ _assertSame(imgdata.data[0], 0, "imgdata.data[\""+(0)+"\"]", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.html
new file mode 100644
index 0000000000..dcd2d92ab1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.alpha</h1>
+<p class="desc">putImageData() puts non-solid image data correctly</p>
+
+
+<script>
+var t = async_test("putImageData() puts non-solid image data correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.25)';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,64, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.png b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.png
new file mode 100644
index 0000000000..5428c65524
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.worker.js
new file mode 100644
index 0000000000..25d8047189
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.alpha.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.alpha
+// Description:putImageData() puts non-solid image data correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() puts non-solid image data correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.25)';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,64, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.html
new file mode 100644
index 0000000000..257e0330d5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.basic</h1>
+<p class="desc">putImageData() puts image data from getImageData() onto the canvas</p>
+
+
+<script>
+var t = async_test("putImageData() puts image data from getImageData() onto the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.worker.js
new file mode 100644
index 0000000000..6a68ffa792
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.basic.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.basic
+// Description:putImageData() puts image data from getImageData() onto the canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() puts image data from getImageData() onto the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.html
new file mode 100644
index 0000000000..648ded588c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.clip</h1>
+<p class="desc">putImageData() is not affected by clipping regions</p>
+
+
+<script>
+var t = async_test("putImageData() is not affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.worker.js
new file mode 100644
index 0000000000..b665edbb33
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.clip.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.clip
+// Description:putImageData() is not affected by clipping regions
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() is not affected by clipping regions");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.html
new file mode 100644
index 0000000000..08f35117dd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.created</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.created</h1>
+<p class="desc">putImageData() puts image data from createImageData() onto the canvas</p>
+
+
+<script>
+var t = async_test("putImageData() puts image data from createImageData() onto the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.createImageData(100, 50);
+ for (var i = 0; i < imgdata.data.length; i += 4) {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ imgdata.data[i+2] = 0;
+ imgdata.data[i+3] = 255;
+ }
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.worker.js
new file mode 100644
index 0000000000..f249efb4bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.created.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.created
+// Description:putImageData() puts image data from createImageData() onto the canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() puts image data from createImageData() onto the canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.createImageData(100, 50);
+ for (var i = 0; i < imgdata.data.length; i += 4) {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ imgdata.data[i+2] = 0;
+ imgdata.data[i+3] = 255;
+ }
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.html
new file mode 100644
index 0000000000..cddd0451a4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.cross</h1>
+<p class="desc">putImageData() accepts image data got from a different canvas</p>
+
+
+<script>
+var t = async_test("putImageData() accepts image data got from a different canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50)
+ var imgdata = ctx2.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.worker.js
new file mode 100644
index 0000000000..0ec4ed142f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.cross.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.cross
+// Description:putImageData() accepts image data got from a different canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() accepts image data got from a different canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50)
+ var imgdata = ctx2.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.html
new file mode 100644
index 0000000000..cea4d0d477
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.dirty.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.dirty.negative</h1>
+<p class="desc">putImageData() handles negative-sized dirty rectangles correctly</p>
+
+
+<script>
+var t = async_test("putImageData() handles negative-sized dirty rectangles correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 20, 20, -20, -20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.worker.js
new file mode 100644
index 0000000000..0a7af64049
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.negative.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.dirty.negative
+// Description:putImageData() handles negative-sized dirty rectangles correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() handles negative-sized dirty rectangles correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 20, 20, -20, -20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.html
new file mode 100644
index 0000000000..7b67ef2253
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.dirty.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.dirty.outside</h1>
+<p class="desc">putImageData() handles dirty rectangles outside the canvas correctly</p>
+
+
+<script>
+var t = async_test("putImageData() handles dirty rectangles outside the canvas correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+
+ ctx.putImageData(imgdata, 100, 20, 20, 20, -20, -20);
+ ctx.putImageData(imgdata, 200, 200, 0, 0, 100, 50);
+ ctx.putImageData(imgdata, 40, 20, -30, -20, 30, 20);
+ ctx.putImageData(imgdata, -30, 20, 0, 0, 30, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,45, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.worker.js
new file mode 100644
index 0000000000..87b88a09f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.outside.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.dirty.outside
+// Description:putImageData() handles dirty rectangles outside the canvas correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() handles dirty rectangles outside the canvas correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+
+ ctx.putImageData(imgdata, 100, 20, 20, 20, -20, -20);
+ ctx.putImageData(imgdata, 200, 200, 0, 0, 100, 50);
+ ctx.putImageData(imgdata, 40, 20, -30, -20, 30, 20);
+ ctx.putImageData(imgdata, -30, 20, 0, 0, 30, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 98,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 1,45, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.html
new file mode 100644
index 0000000000..9f571427a8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.dirty.rect1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.dirty.rect1</h1>
+<p class="desc">putImageData() only modifies areas inside the dirty rectangle, using width and height</p>
+
+
+<script>
+var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using width and height");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 0, 0, 20, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.worker.js
new file mode 100644
index 0000000000..6feea9556d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect1.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.dirty.rect1
+// Description:putImageData() only modifies areas inside the dirty rectangle, using width and height
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using width and height");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 0, 0, 20, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.html
new file mode 100644
index 0000000000..997c1136a1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.dirty.rect2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.dirty.rect2</h1>
+<p class="desc">putImageData() only modifies areas inside the dirty rectangle, using x and y</p>
+
+
+<script>
+var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using x and y");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(60, 30, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, -20, -10, 60, 30, 20, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.worker.js
new file mode 100644
index 0000000000..752a3cdf1e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.rect2.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.dirty.rect2
+// Description:putImageData() only modifies areas inside the dirty rectangle, using x and y
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() only modifies areas inside the dirty rectangle, using x and y");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(60, 30, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, -20, -10, 60, 30, 20, 20);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 35,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 65,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,15, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,45, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.html
new file mode 100644
index 0000000000..fb90a017e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.dirty.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.dirty.zero</h1>
+<p class="desc">putImageData() with zero-sized dirty rectangle puts nothing</p>
+
+
+<script>
+var t = async_test("putImageData() with zero-sized dirty rectangle puts nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0, 0, 0, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.worker.js
new file mode 100644
index 0000000000..2fa474c07f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.dirty.zero.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.dirty.zero
+// Description:putImageData() with zero-sized dirty rectangle puts nothing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() with zero-sized dirty rectangle puts nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0, 0, 0, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.html
new file mode 100644
index 0000000000..337701aaf5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.modified</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.modified</h1>
+<p class="desc">putImageData() puts modified image data correctly</p>
+
+
+<script>
+var t = async_test("putImageData() puts modified image data correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(45, 20, 10, 10)
+ var imgdata = ctx.getImageData(45, 20, 10, 10);
+ for (var i = 0, len = imgdata.width*imgdata.height*4; i < len; i += 4)
+ {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ }
+ ctx.putImageData(imgdata, 45, 20);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.worker.js
new file mode 100644
index 0000000000..846a9dbc2d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.modified.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.modified
+// Description:putImageData() puts modified image data correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() puts modified image data correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(45, 20, 10, 10)
+ var imgdata = ctx.getImageData(45, 20, 10, 10);
+ for (var i = 0, len = imgdata.width*imgdata.height*4; i < len; i += 4)
+ {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ }
+ ctx.putImageData(imgdata, 45, 20);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.html
new file mode 100644
index 0000000000..eb61d38e9e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.nonfinite</h1>
+<p class="desc">putImageData() throws TypeError if arguments are not finite</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<script>
+var t = async_test("putImageData() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, -Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, NaN, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, -Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, NaN, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, Infinity); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.worker.js
new file mode 100644
index 0000000000..479ad69467
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.nonfinite.worker.js
@@ -0,0 +1,104 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.nonfinite
+// Description:putImageData() throws TypeError if arguments are not finite
+// Note:<p class="notes">Defined in "Web IDL" (draft)
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() throws TypeError if arguments are not finite");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, -Infinity, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, NaN, 10, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, -Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, NaN, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, -Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, NaN, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, -Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, NaN, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, -Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, NaN, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, -Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, 10, NaN); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, Infinity, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, Infinity, 10, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, Infinity, 10, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, Infinity, 10, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, 10); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, Infinity, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, Infinity, 10, Infinity); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 10, 10, 10, 10, Infinity, Infinity); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.html
new file mode 100644
index 0000000000..89fe06686d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.null</h1>
+<p class="desc">putImageData() with null imagedata throws TypeError</p>
+
+
+<script>
+var t = async_test("putImageData() with null imagedata throws TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.putImageData(null, 0, 0); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.worker.js
new file mode 100644
index 0000000000..2f021b638e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.null.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.null
+// Description:putImageData() with null imagedata throws TypeError
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() with null imagedata throws TypeError");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ assert_throws_js(TypeError, function() { ctx.putImageData(null, 0, 0); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.html
new file mode 100644
index 0000000000..6c32376006
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.path</h1>
+<p class="desc">putImageData() does not affect the current path</p>
+
+
+<script>
+var t = async_test("putImageData() does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.rect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.putImageData(imgdata, 0, 0);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.worker.js
new file mode 100644
index 0000000000..66781a922d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.path.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.path
+// Description:putImageData() does not affect the current path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.rect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.putImageData(imgdata, 0, 0);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.html
new file mode 100644
index 0000000000..3d3e99b135
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.unaffected</h1>
+<p class="desc">putImageData() is not affected by context state</p>
+
+
+<script>
+var t = async_test("putImageData() is not affected by context state");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.translate(100, 50);
+ ctx.scale(0.1, 0.1);
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.worker.js
new file mode 100644
index 0000000000..75adb865ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unaffected.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.unaffected
+// Description:putImageData() is not affected by context state
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() is not affected by context state");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.translate(100, 50);
+ ctx.scale(0.1, 0.1);
+ ctx.putImageData(imgdata, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.html
new file mode 100644
index 0000000000..d78fa4e035
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.unchanged</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.unchanged</h1>
+<p class="desc">putImageData(getImageData(...), ...) has no effect</p>
+
+
+<script>
+var t = async_test("putImageData(getImageData(...), ...) has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var i = 0;
+ for (var y = 0; y < 16; ++y) {
+ for (var x = 0; x < 16; ++x, ++i) {
+ ctx.fillStyle = 'rgba(' + i + ',' + (Math.floor(i*1.5) % 256) + ',' + (Math.floor(i*23.3) % 256) + ',' + (i/256) + ')';
+ ctx.fillRect(x, y, 1, 1);
+ }
+ }
+ var imgdata1 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ var olddata = [];
+ for (var i = 0; i < imgdata1.data.length; ++i)
+ olddata[i] = imgdata1.data[i];
+
+ ctx.putImageData(imgdata1, 0.1, 0.2);
+
+ var imgdata2 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ for (var i = 0; i < imgdata2.data.length; ++i) {
+ _assertSame(olddata[i], imgdata2.data[i], "olddata[\""+(i)+"\"]", "imgdata2.data[\""+(i)+"\"]");
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.worker.js
new file mode 100644
index 0000000000..5e9362fab2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.unchanged.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.unchanged
+// Description:putImageData(getImageData(...), ...) has no effect
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData(getImageData(...), ...) has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var i = 0;
+ for (var y = 0; y < 16; ++y) {
+ for (var x = 0; x < 16; ++x, ++i) {
+ ctx.fillStyle = 'rgba(' + i + ',' + (Math.floor(i*1.5) % 256) + ',' + (Math.floor(i*23.3) % 256) + ',' + (i/256) + ')';
+ ctx.fillRect(x, y, 1, 1);
+ }
+ }
+ var imgdata1 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ var olddata = [];
+ for (var i = 0; i < imgdata1.data.length; ++i)
+ olddata[i] = imgdata1.data[i];
+
+ ctx.putImageData(imgdata1, 0.1, 0.2);
+
+ var imgdata2 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ for (var i = 0; i < imgdata2.data.length; ++i) {
+ _assertSame(olddata[i], imgdata2.data[i], "olddata[\""+(i)+"\"]", "imgdata2.data[\""+(i)+"\"]");
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.html b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.html
new file mode 100644
index 0000000000..1488e7b111
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.imageData.put.wrongtype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.imageData.put.wrongtype</h1>
+<p class="desc">putImageData() does not accept non-ImageData objects</p>
+
+
+<script>
+var t = async_test("putImageData() does not accept non-ImageData objects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = { width: 1, height: 1, data: [255, 0, 0, 255] };
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.putImageData("cheese", 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(42, 0, 0); });
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.worker.js b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.worker.js
new file mode 100644
index 0000000000..bf4d6dfedf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/pixel-manipulation/2d.imageData.put.wrongtype.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.imageData.put.wrongtype
+// Description:putImageData() does not accept non-ImageData objects
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("putImageData() does not accept non-ImageData objects");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var imgdata = { width: 1, height: 1, data: [255, 0, 0, 255] };
+ assert_throws_js(TypeError, function() { ctx.putImageData(imgdata, 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.putImageData("cheese", 0, 0); });
+ assert_throws_js(TypeError, function() { ctx.putImageData(42, 0, 0); });
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.html
new file mode 100644
index 0000000000..7396ca120a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.basic</h1>
+<p class="desc">reset clears to transparent black</p>
+
+
+<script>
+var t = async_test("reset clears to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ ctx.reset();
+ _assertPixel(canvas, 0,0, 0,0,0,0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ _assertPixel(canvas, 25,50, 0,0,0,0);
+ _assertPixel(canvas, 100,50, 0,0,0,0);
+ _assertPixel(canvas, 0,50, 0,0,0,0);
+ _assertPixel(canvas, 100,0, 0,0,0,0);
+ t.done();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.worker.js
new file mode 100644
index 0000000000..180bffbfac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.basic.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.basic
+// Description:reset clears to transparent black
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("reset clears to transparent black");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ ctx.reset();
+ _assertPixel(canvas, 0,0, 0,0,0,0);
+ _assertPixel(canvas, 50,25, 0,0,0,0);
+ _assertPixel(canvas, 25,50, 0,0,0,0);
+ _assertPixel(canvas, 100,50, 0,0,0,0);
+ _assertPixel(canvas, 0,50, 0,0,0,0);
+ _assertPixel(canvas, 100,0, 0,0,0,0);
+ t.done();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow-expected.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow-expected.html
new file mode 100644
index 0000000000..182f7e40cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.drop_shadow</title>
+<h1>2d.reset.render.drop_shadow</h1>
+<p class="desc">check that drop shadows are correctly rendered after reset</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(100, 100, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.html
new file mode 100644
index 0000000000..e6decd1687
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.drop_shadow-expected.html">
+<title>Canvas test: 2d.reset.render.drop_shadow</title>
+<h1>2d.reset.render.drop_shadow</h1>
+<p class="desc">check that drop shadows are correctly rendered after reset</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(500, 500);
+ const ctx = canvas.getContext('2d');
+
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = "red";
+ ctx.shadowBlur = 10;
+
+ ctx.reset();
+
+ ctx.fillRect(100, 100, 100, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.w.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.w.html
new file mode 100644
index 0000000000..0ceba2f91d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.drop_shadow.w.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.reset.render.drop_shadow-expected.html">
+<title>Canvas test: 2d.reset.render.drop_shadow</title>
+<h1>2d.reset.render.drop_shadow</h1>
+<p class="desc">check that drop shadows are correctly rendered after reset</p>
+<canvas id="canvas" width="500" height="500">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(500, 500);
+ const ctx = canvas.getContext('2d');
+
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = "red";
+ ctx.shadowBlur = 10;
+
+ ctx.reset();
+
+ ctx.fillRect(100, 100, 100, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation-expected.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation-expected.html
new file mode 100644
index 0000000000..1f9d247634
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.global_composite_operation</title>
+<h1>2d.reset.render.global_composite_operation</h1>
+<p class="desc">check that canvas correctly renders rectangles with the default global composite operation after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(10, 10, 100, 100);
+ ctx.fillRect(50, 50, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.html
new file mode 100644
index 0000000000..4ddcc8d8f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.global_composite_operation-expected.html">
+<title>Canvas test: 2d.reset.render.global_composite_operation</title>
+<h1>2d.reset.render.global_composite_operation</h1>
+<p class="desc">check that canvas correctly renders rectangles with the default global composite operation after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = "xor";
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 100, 100);
+ ctx.fillRect(50, 50, 100, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.w.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.w.html
new file mode 100644
index 0000000000..6df07a47e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.global_composite_operation.w.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.reset.render.global_composite_operation-expected.html">
+<title>Canvas test: 2d.reset.render.global_composite_operation</title>
+<h1>2d.reset.render.global_composite_operation</h1>
+<p class="desc">check that canvas correctly renders rectangles with the default global composite operation after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = "xor";
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 100, 100);
+ ctx.fillRect(50, 50, 100, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line-expected.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line-expected.html
new file mode 100644
index 0000000000..dcd648d8c3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.line</title>
+<h1>2d.reset.render.line</h1>
+<p class="desc">check that lines are correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.moveTo(100, 100);
+ ctx.lineTo(100, 300);
+ ctx.lineTo(300, 300);
+ ctx.lineTo(300, 100);
+ ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.html
new file mode 100644
index 0000000000..0e478cbf9d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.line-expected.html">
+<title>Canvas test: 2d.reset.render.line</title>
+<h1>2d.reset.render.line</h1>
+<p class="desc">check that lines are correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 10;
+ ctx.lineCap = "round";
+ ctx.lineJoin = "bevel";
+ ctx.lineDashOffset = 10;
+ ctx.setLineDash([20]);
+
+ ctx.reset();
+
+ ctx.beginPath();
+ ctx.moveTo(100, 100);
+ ctx.lineTo(100, 300);
+ ctx.lineTo(300, 300);
+ ctx.lineTo(300, 100);
+ ctx.stroke();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.w.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.w.html
new file mode 100644
index 0000000000..e85f155b32
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.line.w.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.reset.render.line-expected.html">
+<title>Canvas test: 2d.reset.render.line</title>
+<h1>2d.reset.render.line</h1>
+<p class="desc">check that lines are correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 10;
+ ctx.lineCap = "round";
+ ctx.lineJoin = "bevel";
+ ctx.lineDashOffset = 10;
+ ctx.setLineDash([20]);
+
+ ctx.reset();
+
+ ctx.beginPath();
+ ctx.moveTo(100, 100);
+ ctx.lineTo(100, 300);
+ ctx.lineTo(300, 300);
+ ctx.lineTo(300, 100);
+ ctx.stroke();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc-expected.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc-expected.html
new file mode 100644
index 0000000000..c359e6b42e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.misc</title>
+<h1>2d.reset.render.misc</h1>
+<p class="desc">check that canvas correctly renders rectangles after reset (states not covered by other tests)</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(0, 0, 100, 100);
+ ctx.strokeRect(150, 150, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.html
new file mode 100644
index 0000000000..b471327d98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.misc-expected.html">
+<title>Canvas test: 2d.reset.render.misc</title>
+<h1>2d.reset.render.misc</h1>
+<p class="desc">check that canvas correctly renders rectangles after reset (states not covered by other tests)</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = "red";
+ ctx.strokeStyle = "red";
+ ctx.globalAlpha = 0.5;
+ ctx.filter = "blur(2px)";
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 100, 100);
+ ctx.strokeRect(150, 150, 100, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.w.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.w.html
new file mode 100644
index 0000000000..eef646df66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.misc.w.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.reset.render.misc-expected.html">
+<title>Canvas test: 2d.reset.render.misc</title>
+<h1>2d.reset.render.misc</h1>
+<p class="desc">check that canvas correctly renders rectangles after reset (states not covered by other tests)</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = "red";
+ ctx.strokeStyle = "red";
+ ctx.globalAlpha = 0.5;
+ ctx.filter = "blur(2px)";
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 100, 100);
+ ctx.strokeRect(150, 150, 100, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit-expected.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit-expected.html
new file mode 100644
index 0000000000..c91f485c53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit-expected.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.miter_limit</title>
+<h1>2d.reset.render.miter_limit</h1>
+<p class="desc">check that the lines are correctly rendered with the default miter limit after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 100);
+ for (let i = 0; i < 24; i++) {
+ const dy = i % 2 === 0 ? 25 : -25;
+ ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+ }
+ ctx.stroke();
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.html
new file mode 100644
index 0000000000..c00284bbc2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.miter_limit-expected.html">
+<title>Canvas test: 2d.reset.render.miter_limit</title>
+<h1>2d.reset.render.miter_limit</h1>
+<p class="desc">check that the lines are correctly rendered with the default miter limit after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.miterLimit = 6;
+
+ ctx.reset();
+
+ ctx.lineWidth = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 100);
+ for (let i = 0; i < 24; i++) {
+ const dy = i % 2 === 0 ? 25 : -25;
+ ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+ }
+ ctx.stroke();
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.w.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.w.html
new file mode 100644
index 0000000000..1f98606b55
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.miter_limit.w.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.reset.render.miter_limit-expected.html">
+<title>Canvas test: 2d.reset.render.miter_limit</title>
+<h1>2d.reset.render.miter_limit</h1>
+<p class="desc">check that the lines are correctly rendered with the default miter limit after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.miterLimit = 6;
+
+ ctx.reset();
+
+ ctx.lineWidth = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 100);
+ for (let i = 0; i < 24; i++) {
+ const dy = i % 2 === 0 ? 25 : -25;
+ ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+ }
+ ctx.stroke();
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text-expected.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text-expected.html
new file mode 100644
index 0000000000..7221483b02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.render.text</title>
+<h1>2d.reset.render.text</h1>
+<p class="desc">check that text is correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillText("Lorem ipsum dolor sit amet, consectetur adipiscing elit", 0, 10);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.html
new file mode 100644
index 0000000000..31e35db71c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.render.text-expected.html">
+<title>Canvas test: 2d.reset.render.text</title>
+<h1>2d.reset.render.text</h1>
+<p class="desc">check that text is correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "24px serif";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "hanging";
+ ctx.direction = "rtl";
+ ctx.letterSpacing = "10px";
+ ctx.fontKerning = "none";
+ ctx.fontStretch = "semi-condensed";
+ ctx.fontVariantCaps = "tilting-caps";
+ ctx.textRendering = "optimizeLegibility";
+ ctx.wordSpacing = "20px";
+
+ ctx.reset();
+
+ ctx.fillText("Lorem ipsum dolor sit amet, consectetur adipiscing elit", 0, 10);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.w.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.w.html
new file mode 100644
index 0000000000..6ddfd9fd9d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.render.text.w.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.reset.render.text-expected.html">
+<title>Canvas test: 2d.reset.render.text</title>
+<h1>2d.reset.render.text</h1>
+<p class="desc">check that text is correctly rendered after reset</p>
+<canvas id="canvas" width="400" height="400">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(400, 400);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "24px serif";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "hanging";
+ ctx.direction = "rtl";
+ ctx.letterSpacing = "10px";
+ ctx.fontKerning = "none";
+ ctx.fontStretch = "semi-condensed";
+ ctx.fontVariantCaps = "tilting-caps";
+ ctx.textRendering = "optimizeLegibility";
+ ctx.wordSpacing = "20px";
+
+ ctx.reset();
+
+ ctx.fillText("Lorem ipsum dolor sit amet, consectetur adipiscing elit", 0, 10);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip-expected.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip-expected.html
new file mode 100644
index 0000000000..974b37fab4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.reset.state.clip</title>
+<h1>2d.reset.state.clip</h1>
+<p class="desc">check that the clip is reset</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillRect(0, 0, 200, 200);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.html
new file mode 100644
index 0000000000..2f53da7a49
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.reset.state.clip-expected.html">
+<title>Canvas test: 2d.reset.state.clip</title>
+<h1>2d.reset.state.clip</h1>
+<p class="desc">check that the clip is reset</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.clip();
+
+ ctx.fillRect(0, 0, 200, 200);
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 200, 200);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.w.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.w.html
new file mode 100644
index 0000000000..ec4bdf5b23
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.clip.w.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.reset.state.clip-expected.html">
+<title>Canvas test: 2d.reset.state.clip</title>
+<h1>2d.reset.state.clip</h1>
+<p class="desc">check that the clip is reset</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.clip();
+
+ ctx.fillRect(0, 0, 200, 200);
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 200, 200);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.html
new file mode 100644
index 0000000000..f2dc299e97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.direction</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.direction</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.direction;
+
+ ctx.direction = 'rtl';
+ _assert(ctx.direction == 'rtl', "ctx.direction == 'rtl'");
+
+ ctx.reset();
+ _assert(ctx.direction == default_value, "ctx.direction == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.worker.js
new file mode 100644
index 0000000000..3d6b835050
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.direction.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.direction
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.direction;
+
+ ctx.direction = 'rtl';
+ _assert(ctx.direction == 'rtl', "ctx.direction == 'rtl'");
+
+ ctx.reset();
+ _assert(ctx.direction == default_value, "ctx.direction == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.html
new file mode 100644
index 0000000000..1fdd2b773a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.fill_style</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.fill_style</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fillStyle;
+
+ ctx.fillStyle = '#ffffff';
+ _assert(ctx.fillStyle == '#ffffff', "ctx.fillStyle == '#ffffff'");
+
+ ctx.reset();
+ _assert(ctx.fillStyle == default_value, "ctx.fillStyle == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.worker.js
new file mode 100644
index 0000000000..c574824b0a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.fill_style.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.fill_style
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fillStyle;
+
+ ctx.fillStyle = '#ffffff';
+ _assert(ctx.fillStyle == '#ffffff', "ctx.fillStyle == '#ffffff'");
+
+ ctx.reset();
+ _assert(ctx.fillStyle == default_value, "ctx.fillStyle == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.html
new file mode 100644
index 0000000000..af898c7dd1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.filter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.filter</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.filter;
+
+ ctx.filter = 'blur(10px)';
+ _assert(ctx.filter == 'blur(10px)', "ctx.filter == 'blur(10px)'");
+
+ ctx.reset();
+ _assert(ctx.filter == default_value, "ctx.filter == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.worker.js
new file mode 100644
index 0000000000..f781a9fa34
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.filter.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.filter
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.filter;
+
+ ctx.filter = 'blur(10px)';
+ _assert(ctx.filter == 'blur(10px)', "ctx.filter == 'blur(10px)'");
+
+ ctx.reset();
+ _assert(ctx.filter == default_value, "ctx.filter == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.html
new file mode 100644
index 0000000000..69bbf3261e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.font</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.font</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.font;
+
+ ctx.font = '16px sans-serif';
+ _assert(ctx.font == '16px sans-serif', "ctx.font == '16px sans-serif'");
+
+ ctx.reset();
+ _assert(ctx.font == default_value, "ctx.font == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.worker.js
new file mode 100644
index 0000000000..ea6f4cc184
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.font
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.font;
+
+ ctx.font = '16px sans-serif';
+ _assert(ctx.font == '16px sans-serif', "ctx.font == '16px sans-serif'");
+
+ ctx.reset();
+ _assert(ctx.font == default_value, "ctx.font == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.html
new file mode 100644
index 0000000000..27954486c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.font_kerning</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.font_kerning</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fontKerning;
+
+ ctx.fontKerning = 'normal';
+ _assert(ctx.fontKerning == 'normal', "ctx.fontKerning == 'normal'");
+
+ ctx.reset();
+ _assert(ctx.fontKerning == default_value, "ctx.fontKerning == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.worker.js
new file mode 100644
index 0000000000..1c21249b9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_kerning.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.font_kerning
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fontKerning;
+
+ ctx.fontKerning = 'normal';
+ _assert(ctx.fontKerning == 'normal', "ctx.fontKerning == 'normal'");
+
+ ctx.reset();
+ _assert(ctx.fontKerning == default_value, "ctx.fontKerning == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.html
new file mode 100644
index 0000000000..b1e2217320
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.font_stretch</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.font_stretch</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fontStretch;
+
+ ctx.fontStretch = 'ultra-condensed';
+ _assert(ctx.fontStretch == 'ultra-condensed', "ctx.fontStretch == 'ultra-condensed'");
+
+ ctx.reset();
+ _assert(ctx.fontStretch == default_value, "ctx.fontStretch == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.worker.js
new file mode 100644
index 0000000000..71bb6aff48
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_stretch.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.font_stretch
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fontStretch;
+
+ ctx.fontStretch = 'ultra-condensed';
+ _assert(ctx.fontStretch == 'ultra-condensed', "ctx.fontStretch == 'ultra-condensed'");
+
+ ctx.reset();
+ _assert(ctx.fontStretch == default_value, "ctx.fontStretch == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.html
new file mode 100644
index 0000000000..7d0518c021
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.font_variant_caps</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.font_variant_caps</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fontVariantCaps;
+
+ ctx.fontVariantCaps = 'unicase';
+ _assert(ctx.fontVariantCaps == 'unicase', "ctx.fontVariantCaps == 'unicase'");
+
+ ctx.reset();
+ _assert(ctx.fontVariantCaps == default_value, "ctx.fontVariantCaps == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.worker.js
new file mode 100644
index 0000000000..e89a8bda42
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.font_variant_caps.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.font_variant_caps
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.fontVariantCaps;
+
+ ctx.fontVariantCaps = 'unicase';
+ _assert(ctx.fontVariantCaps == 'unicase', "ctx.fontVariantCaps == 'unicase'");
+
+ ctx.reset();
+ _assert(ctx.fontVariantCaps == default_value, "ctx.fontVariantCaps == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.html
new file mode 100644
index 0000000000..fe87aedf84
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.global_alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.global_alpha</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.globalAlpha;
+
+ ctx.globalAlpha = 0.5;
+ _assert(ctx.globalAlpha == 0.5, "ctx.globalAlpha == 0.5");
+
+ ctx.reset();
+ _assert(ctx.globalAlpha == default_value, "ctx.globalAlpha == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.worker.js
new file mode 100644
index 0000000000..518a8cbb46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_alpha.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.global_alpha
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.globalAlpha;
+
+ ctx.globalAlpha = 0.5;
+ _assert(ctx.globalAlpha == 0.5, "ctx.globalAlpha == 0.5");
+
+ ctx.reset();
+ _assert(ctx.globalAlpha == default_value, "ctx.globalAlpha == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.html
new file mode 100644
index 0000000000..647349c94a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.global_composite_operation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.global_composite_operation</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.globalCompositeOperation;
+
+ ctx.globalCompositeOperation = 'destination-over';
+ _assert(ctx.globalCompositeOperation == 'destination-over', "ctx.globalCompositeOperation == 'destination-over'");
+
+ ctx.reset();
+ _assert(ctx.globalCompositeOperation == default_value, "ctx.globalCompositeOperation == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.worker.js
new file mode 100644
index 0000000000..c94f15b232
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.global_composite_operation.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.global_composite_operation
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.globalCompositeOperation;
+
+ ctx.globalCompositeOperation = 'destination-over';
+ _assert(ctx.globalCompositeOperation == 'destination-over', "ctx.globalCompositeOperation == 'destination-over'");
+
+ ctx.reset();
+ _assert(ctx.globalCompositeOperation == default_value, "ctx.globalCompositeOperation == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.html
new file mode 100644
index 0000000000..c0b7f29aca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.image_smoothing_enabled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.image_smoothing_enabled</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.imageSmoothingEnabled;
+
+ ctx.imageSmoothingEnabled = false;
+ _assert(ctx.imageSmoothingEnabled == false, "ctx.imageSmoothingEnabled == false");
+
+ ctx.reset();
+ _assert(ctx.imageSmoothingEnabled == default_value, "ctx.imageSmoothingEnabled == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.worker.js
new file mode 100644
index 0000000000..7010fb899c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_enabled.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.image_smoothing_enabled
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.imageSmoothingEnabled;
+
+ ctx.imageSmoothingEnabled = false;
+ _assert(ctx.imageSmoothingEnabled == false, "ctx.imageSmoothingEnabled == false");
+
+ ctx.reset();
+ _assert(ctx.imageSmoothingEnabled == default_value, "ctx.imageSmoothingEnabled == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.html
new file mode 100644
index 0000000000..12a6367ff9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.image_smoothing_quality</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.image_smoothing_quality</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.imageSmoothingQuality;
+
+ ctx.imageSmoothingQuality = 'high';
+ _assert(ctx.imageSmoothingQuality == 'high', "ctx.imageSmoothingQuality == 'high'");
+
+ ctx.reset();
+ _assert(ctx.imageSmoothingQuality == default_value, "ctx.imageSmoothingQuality == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.worker.js
new file mode 100644
index 0000000000..7429666691
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.image_smoothing_quality.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.image_smoothing_quality
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.imageSmoothingQuality;
+
+ ctx.imageSmoothingQuality = 'high';
+ _assert(ctx.imageSmoothingQuality == 'high', "ctx.imageSmoothingQuality == 'high'");
+
+ ctx.reset();
+ _assert(ctx.imageSmoothingQuality == default_value, "ctx.imageSmoothingQuality == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.html
new file mode 100644
index 0000000000..addb0c62ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.letter_spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.letter_spacing</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.letterSpacing;
+
+ ctx.letterSpacing = '12px';
+ _assert(ctx.letterSpacing == '12px', "ctx.letterSpacing == '12px'");
+
+ ctx.reset();
+ _assert(ctx.letterSpacing == default_value, "ctx.letterSpacing == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.worker.js
new file mode 100644
index 0000000000..e0dfae0a8e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.letter_spacing.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.letter_spacing
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.letterSpacing;
+
+ ctx.letterSpacing = '12px';
+ _assert(ctx.letterSpacing == '12px', "ctx.letterSpacing == '12px'");
+
+ ctx.reset();
+ _assert(ctx.letterSpacing == default_value, "ctx.letterSpacing == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.html
new file mode 100644
index 0000000000..9b528b3448
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.line_cap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.line_cap</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineCap;
+
+ ctx.lineCap = 'square';
+ _assert(ctx.lineCap == 'square', "ctx.lineCap == 'square'");
+
+ ctx.reset();
+ _assert(ctx.lineCap == default_value, "ctx.lineCap == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.worker.js
new file mode 100644
index 0000000000..c3a94f2cbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_cap.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.line_cap
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineCap;
+
+ ctx.lineCap = 'square';
+ _assert(ctx.lineCap == 'square', "ctx.lineCap == 'square'");
+
+ ctx.reset();
+ _assert(ctx.lineCap == default_value, "ctx.lineCap == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.html
new file mode 100644
index 0000000000..fd4edf58cf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.line_dash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.line_dash</h1>
+<p class="desc">check that the line dash is reset</p>
+
+
+<script>
+var t = async_test("check that the line dash is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.setLineDash([1, 2]);
+
+ ctx.reset();
+ _assert(ctx.getLineDash().length == 0, "ctx.getLineDash().length == 0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.worker.js
new file mode 100644
index 0000000000..841475d876
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.line_dash
+// Description:check that the line dash is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the line dash is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.setLineDash([1, 2]);
+
+ ctx.reset();
+ _assert(ctx.getLineDash().length == 0, "ctx.getLineDash().length == 0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.html
new file mode 100644
index 0000000000..475db49f82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.line_dash_offset</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.line_dash_offset</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineDashOffset;
+
+ ctx.lineDashOffset = 1.0;
+ _assert(ctx.lineDashOffset == 1.0, "ctx.lineDashOffset == 1.0");
+
+ ctx.reset();
+ _assert(ctx.lineDashOffset == default_value, "ctx.lineDashOffset == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.worker.js
new file mode 100644
index 0000000000..5e0d8dd47c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_dash_offset.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.line_dash_offset
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineDashOffset;
+
+ ctx.lineDashOffset = 1.0;
+ _assert(ctx.lineDashOffset == 1.0, "ctx.lineDashOffset == 1.0");
+
+ ctx.reset();
+ _assert(ctx.lineDashOffset == default_value, "ctx.lineDashOffset == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.html
new file mode 100644
index 0000000000..793c1d9bc4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.line_join</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.line_join</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineJoin;
+
+ ctx.lineJoin = 'bevel';
+ _assert(ctx.lineJoin == 'bevel', "ctx.lineJoin == 'bevel'");
+
+ ctx.reset();
+ _assert(ctx.lineJoin == default_value, "ctx.lineJoin == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.worker.js
new file mode 100644
index 0000000000..9d5e76757b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_join.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.line_join
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineJoin;
+
+ ctx.lineJoin = 'bevel';
+ _assert(ctx.lineJoin == 'bevel', "ctx.lineJoin == 'bevel'");
+
+ ctx.reset();
+ _assert(ctx.lineJoin == default_value, "ctx.lineJoin == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.html
new file mode 100644
index 0000000000..8cdf45f700
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.line_width</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.line_width</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineWidth;
+
+ ctx.lineWidth = 1;
+ _assert(ctx.lineWidth == 1, "ctx.lineWidth == 1");
+
+ ctx.reset();
+ _assert(ctx.lineWidth == default_value, "ctx.lineWidth == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.worker.js
new file mode 100644
index 0000000000..1e28f93d66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.line_width.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.line_width
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.lineWidth;
+
+ ctx.lineWidth = 1;
+ _assert(ctx.lineWidth == 1, "ctx.lineWidth == 1");
+
+ ctx.reset();
+ _assert(ctx.lineWidth == default_value, "ctx.lineWidth == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.html
new file mode 100644
index 0000000000..7f47f9a6be
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.miter_limit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.miter_limit</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.miterLimit;
+
+ ctx.miterLimit = 1.0;
+ _assert(ctx.miterLimit == 1.0, "ctx.miterLimit == 1.0");
+
+ ctx.reset();
+ _assert(ctx.miterLimit == default_value, "ctx.miterLimit == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.worker.js
new file mode 100644
index 0000000000..3945a662d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.miter_limit.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.miter_limit
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.miterLimit;
+
+ ctx.miterLimit = 1.0;
+ _assert(ctx.miterLimit == 1.0, "ctx.miterLimit == 1.0");
+
+ ctx.reset();
+ _assert(ctx.miterLimit == default_value, "ctx.miterLimit == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.html
new file mode 100644
index 0000000000..e7075ce102
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.shadow_blur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.shadow_blur</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowBlur;
+
+ ctx.shadowBlur = 10.0;
+ _assert(ctx.shadowBlur == 10.0, "ctx.shadowBlur == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowBlur == default_value, "ctx.shadowBlur == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.worker.js
new file mode 100644
index 0000000000..d82933169d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_blur.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.shadow_blur
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowBlur;
+
+ ctx.shadowBlur = 10.0;
+ _assert(ctx.shadowBlur == 10.0, "ctx.shadowBlur == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowBlur == default_value, "ctx.shadowBlur == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.html
new file mode 100644
index 0000000000..ad928acf46
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.shadow_color</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.shadow_color</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowColor;
+
+ ctx.shadowColor = '#ff0000';
+ _assert(ctx.shadowColor == '#ff0000', "ctx.shadowColor == '#ff0000'");
+
+ ctx.reset();
+ _assert(ctx.shadowColor == default_value, "ctx.shadowColor == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.worker.js
new file mode 100644
index 0000000000..6ad1e83be2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_color.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.shadow_color
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowColor;
+
+ ctx.shadowColor = '#ff0000';
+ _assert(ctx.shadowColor == '#ff0000', "ctx.shadowColor == '#ff0000'");
+
+ ctx.reset();
+ _assert(ctx.shadowColor == default_value, "ctx.shadowColor == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.html
new file mode 100644
index 0000000000..e3742e4dbc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.shadow_offset_x</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.shadow_offset_x</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowOffsetX;
+
+ ctx.shadowOffsetX = 10.0;
+ _assert(ctx.shadowOffsetX == 10.0, "ctx.shadowOffsetX == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowOffsetX == default_value, "ctx.shadowOffsetX == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.worker.js
new file mode 100644
index 0000000000..4247820a4d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_x.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.shadow_offset_x
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowOffsetX;
+
+ ctx.shadowOffsetX = 10.0;
+ _assert(ctx.shadowOffsetX == 10.0, "ctx.shadowOffsetX == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowOffsetX == default_value, "ctx.shadowOffsetX == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.html
new file mode 100644
index 0000000000..64901ef492
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.shadow_offset_y</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.shadow_offset_y</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowOffsetY;
+
+ ctx.shadowOffsetY = 10.0;
+ _assert(ctx.shadowOffsetY == 10.0, "ctx.shadowOffsetY == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowOffsetY == default_value, "ctx.shadowOffsetY == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.worker.js
new file mode 100644
index 0000000000..0c1926ebca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.shadow_offset_y.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.shadow_offset_y
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.shadowOffsetY;
+
+ ctx.shadowOffsetY = 10.0;
+ _assert(ctx.shadowOffsetY == 10.0, "ctx.shadowOffsetY == 10.0");
+
+ ctx.reset();
+ _assert(ctx.shadowOffsetY == default_value, "ctx.shadowOffsetY == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.html
new file mode 100644
index 0000000000..79803b2828
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.stroke_style</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.stroke_style</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.strokeStyle;
+
+ ctx.strokeStyle = '#ffffff';
+ _assert(ctx.strokeStyle == '#ffffff', "ctx.strokeStyle == '#ffffff'");
+
+ ctx.reset();
+ _assert(ctx.strokeStyle == default_value, "ctx.strokeStyle == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.worker.js
new file mode 100644
index 0000000000..99e1eaaed5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.stroke_style.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.stroke_style
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.strokeStyle;
+
+ ctx.strokeStyle = '#ffffff';
+ _assert(ctx.strokeStyle == '#ffffff', "ctx.strokeStyle == '#ffffff'");
+
+ ctx.reset();
+ _assert(ctx.strokeStyle == default_value, "ctx.strokeStyle == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.html
new file mode 100644
index 0000000000..c1a33d6f3d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.text_align</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.text_align</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.textAlign;
+
+ ctx.textAlign = 'end';
+ _assert(ctx.textAlign == 'end', "ctx.textAlign == 'end'");
+
+ ctx.reset();
+ _assert(ctx.textAlign == default_value, "ctx.textAlign == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.worker.js
new file mode 100644
index 0000000000..087d3195e4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_align.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.text_align
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.textAlign;
+
+ ctx.textAlign = 'end';
+ _assert(ctx.textAlign == 'end', "ctx.textAlign == 'end'");
+
+ ctx.reset();
+ _assert(ctx.textAlign == default_value, "ctx.textAlign == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.html
new file mode 100644
index 0000000000..acb56e4a00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.text_baseline</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.text_baseline</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.textBaseline;
+
+ ctx.textBaseline = 'middle';
+ _assert(ctx.textBaseline == 'middle', "ctx.textBaseline == 'middle'");
+
+ ctx.reset();
+ _assert(ctx.textBaseline == default_value, "ctx.textBaseline == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.worker.js
new file mode 100644
index 0000000000..9a171326cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_baseline.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.text_baseline
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.textBaseline;
+
+ ctx.textBaseline = 'middle';
+ _assert(ctx.textBaseline == 'middle', "ctx.textBaseline == 'middle'");
+
+ ctx.reset();
+ _assert(ctx.textBaseline == default_value, "ctx.textBaseline == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.html
new file mode 100644
index 0000000000..174dbb3801
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.text_rendering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.text_rendering</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.textRendering;
+
+ ctx.textRendering = 'geometricPrecision';
+ _assert(ctx.textRendering == 'geometricPrecision', "ctx.textRendering == 'geometricPrecision'");
+
+ ctx.reset();
+ _assert(ctx.textRendering == default_value, "ctx.textRendering == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.worker.js
new file mode 100644
index 0000000000..df4cea1044
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.text_rendering.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.text_rendering
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.textRendering;
+
+ ctx.textRendering = 'geometricPrecision';
+ _assert(ctx.textRendering == 'geometricPrecision', "ctx.textRendering == 'geometricPrecision'");
+
+ ctx.reset();
+ _assert(ctx.textRendering == default_value, "ctx.textRendering == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.html
new file mode 100644
index 0000000000..c30e459b30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.transformation_matrix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.transformation_matrix</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(2, 2);
+
+ ctx.reset();
+ _assert(ctx.getTransform().isIdentity, "ctx.getTransform().isIdentity");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.worker.js
new file mode 100644
index 0000000000..2ae105c20e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.transformation_matrix.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.transformation_matrix
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.scale(2, 2);
+
+ ctx.reset();
+ _assert(ctx.getTransform().isIdentity, "ctx.getTransform().isIdentity");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.html b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.html
new file mode 100644
index 0000000000..470db2cf98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.reset.state.word_spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.reset.state.word_spacing</h1>
+<p class="desc">check that the state is reset</p>
+
+
+<script>
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.wordSpacing;
+
+ ctx.wordSpacing = '12px';
+ _assert(ctx.wordSpacing == '12px', "ctx.wordSpacing == '12px'");
+
+ ctx.reset();
+ _assert(ctx.wordSpacing == default_value, "ctx.wordSpacing == default_value");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.worker.js
new file mode 100644
index 0000000000..9e8c717f78
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/reset/2d.reset.state.word_spacing.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.reset.state.word_spacing
+// Description:check that the state is reset
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("check that the state is reset");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ const default_value = ctx.wordSpacing;
+
+ ctx.wordSpacing = '12px';
+ _assert(ctx.wordSpacing == '12px', "ctx.wordSpacing == '12px'");
+
+ ctx.reset();
+ _assert(ctx.wordSpacing == default_value, "ctx.wordSpacing == default_value");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/set-proprietary-font-names-001-crash.html b/testing/web-platform/tests/html/canvas/offscreen/set-proprietary-font-names-001-crash.html
new file mode 100644
index 0000000000..dfa661655e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/set-proprietary-font-names-001-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE>
+<title>Setting font of offscreen</title>
+<script src="/css/css-fonts/support/font-family-keywords.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#font-family-prop">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1056386">
+<script>
+ let ctx = (new OffscreenCanvas(1024, 50)).getContext('2d');
+ function setFont(keyword) { ctx.font = `12px ${keyword}` };
+ kNonGenericFontFamilyKeywords.forEach(setFont);
+ kGenericFontFamilyKeywords.forEach(keyword => {
+ setFont(`-webkit-${keyword}`);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.html
new file mode 100644
index 0000000000..765cf41b21
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.alpha.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.alpha.1</h1>
+<p class="desc">Shadow color alpha components are used</p>
+
+
+<script>
+var t = async_test("Shadow color alpha components are used");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(255, 0, 0, 0.01)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 4);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.worker.js
new file mode 100644
index 0000000000..869aa017e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.1.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.alpha.1
+// Description:Shadow color alpha components are used
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadow color alpha components are used");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(255, 0, 0, 0.01)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 4);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.html
new file mode 100644
index 0000000000..be2209608f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.alpha.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.alpha.2</h1>
+<p class="desc">Shadow color alpha components are used</p>
+
+
+<script>
+var t = async_test("Shadow color alpha components are used");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.5)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.worker.js
new file mode 100644
index 0000000000..dce91f2e4d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.2.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.alpha.2
+// Description:Shadow color alpha components are used
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadow color alpha components are used");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.5)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.html
new file mode 100644
index 0000000000..7239b3cea7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.alpha.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.alpha.3</h1>
+<p class="desc">Shadows are affected by globalAlpha</p>
+
+
+<script>
+var t = async_test("Shadows are affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.5;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.worker.js
new file mode 100644
index 0000000000..93fc665ced
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.3.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.alpha.3
+// Description:Shadows are affected by globalAlpha
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.5;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.html
new file mode 100644
index 0000000000..468db2a716
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.alpha.4</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.alpha.4</h1>
+<p class="desc">Shadows with alpha components are correctly affected by globalAlpha</p>
+
+
+<script>
+var t = async_test("Shadows with alpha components are correctly affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.707)';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.707;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.worker.js
new file mode 100644
index 0000000000..2be63d3eff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.4.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.alpha.4
+// Description:Shadows with alpha components are correctly affected by globalAlpha
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows with alpha components are correctly affected by globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.707)';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.707;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.html
new file mode 100644
index 0000000000..92ac9717f2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.alpha.5</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.alpha.5</h1>
+<p class="desc">Shadows of shapes with alpha components are drawn correctly</p>
+
+
+<script>
+var t = async_test("Shadows of shapes with alpha components are drawn correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgba(64, 0, 0, 0.5)';
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.worker.js
new file mode 100644
index 0000000000..016616ac8c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.alpha.5.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.alpha.5
+// Description:Shadows of shapes with alpha components are drawn correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows of shapes with alpha components are drawn correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgba(64, 0, 0, 0.5)';
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.html
new file mode 100644
index 0000000000..21225883df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowBlur.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowBlur.initial</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.worker.js
new file mode 100644
index 0000000000..7643f0edfa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.initial.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowBlur.initial
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.html
new file mode 100644
index 0000000000..51d1a4a804
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowBlur.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowBlur.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -2;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = Infinity;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -Infinity;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = NaN;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = 'string';
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = true;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = false;
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.worker.js
new file mode 100644
index 0000000000..2e3a28b789
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.invalid.worker.js
@@ -0,0 +1,48 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowBlur.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -2;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = Infinity;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -Infinity;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = NaN;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = 'string';
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = true;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = false;
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.html
new file mode 100644
index 0000000000..06495f6c31
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowBlur.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowBlur.valid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowBlur = 1;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 0.5;
+ _assertSame(ctx.shadowBlur, 0.5, "ctx.shadowBlur", "0.5");
+
+ ctx.shadowBlur = 1e6;
+ _assertSame(ctx.shadowBlur, 1e6, "ctx.shadowBlur", "1e6");
+
+ ctx.shadowBlur = 0;
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.worker.js
new file mode 100644
index 0000000000..d0292edaec
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowBlur.valid.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowBlur.valid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowBlur = 1;
+ _assertSame(ctx.shadowBlur, 1, "ctx.shadowBlur", "1");
+
+ ctx.shadowBlur = 0.5;
+ _assertSame(ctx.shadowBlur, 0.5, "ctx.shadowBlur", "0.5");
+
+ ctx.shadowBlur = 1e6;
+ _assertSame(ctx.shadowBlur, 1e6, "ctx.shadowBlur", "1e6");
+
+ ctx.shadowBlur = 0;
+ _assertSame(ctx.shadowBlur, 0, "ctx.shadowBlur", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.html
new file mode 100644
index 0000000000..92a8bd1500
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowColor.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowColor.initial</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.shadowColor, 'rgba(0, 0, 0, 0)', "ctx.shadowColor", "'rgba(0, 0, 0, 0)'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.worker.js
new file mode 100644
index 0000000000..8b1ec38d7f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.initial.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowColor.initial
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.shadowColor, 'rgba(0, 0, 0, 0)', "ctx.shadowColor", "'rgba(0, 0, 0, 0)'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.html
new file mode 100644
index 0000000000..ac1d9e7333
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowColor.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowColor.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'bogus';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'red bogus';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = ctx;
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = undefined;
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.worker.js
new file mode 100644
index 0000000000..c962f2508d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.invalid.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowColor.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'bogus';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'red bogus';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = ctx;
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = undefined;
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.html
new file mode 100644
index 0000000000..340f11f54f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowColor.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowColor.valid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowColor = 'lime';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = 'RGBA(0,255, 0,0)';
+ _assertSame(ctx.shadowColor, 'rgba(0, 255, 0, 0)', "ctx.shadowColor", "'rgba(0, 255, 0, 0)'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.worker.js
new file mode 100644
index 0000000000..6d7eb0504d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowColor.valid.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowColor.valid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowColor = 'lime';
+ _assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
+
+ ctx.shadowColor = 'RGBA(0,255, 0,0)';
+ _assertSame(ctx.shadowColor, 'rgba(0, 255, 0, 0)', "ctx.shadowColor", "'rgba(0, 255, 0, 0)'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.html
new file mode 100644
index 0000000000..52be5ad532
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowOffset.initial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowOffset.initial</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.worker.js
new file mode 100644
index 0000000000..b7b399b371
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.initial.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowOffset.initial
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.html
new file mode 100644
index 0000000000..d789284b0a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowOffset.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowOffset.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = Infinity;
+ ctx.shadowOffsetY = Infinity;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = -Infinity;
+ ctx.shadowOffsetY = -Infinity;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = NaN;
+ ctx.shadowOffsetY = NaN;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = 'string';
+ ctx.shadowOffsetY = 'string';
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = true;
+ ctx.shadowOffsetY = true;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 1, "ctx.shadowOffsetY", "1");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = false;
+ ctx.shadowOffsetY = false;
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.worker.js
new file mode 100644
index 0000000000..9eb89cd581
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.invalid.worker.js
@@ -0,0 +1,62 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowOffset.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = Infinity;
+ ctx.shadowOffsetY = Infinity;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = -Infinity;
+ ctx.shadowOffsetY = -Infinity;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = NaN;
+ ctx.shadowOffsetY = NaN;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = 'string';
+ ctx.shadowOffsetY = 'string';
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = true;
+ ctx.shadowOffsetY = true;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 1, "ctx.shadowOffsetY", "1");
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = false;
+ ctx.shadowOffsetY = false;
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.html
new file mode 100644
index 0000000000..82f5f0118d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.attributes.shadowOffset.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.attributes.shadowOffset.valid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 0.5;
+ ctx.shadowOffsetY = 0.25;
+ _assertSame(ctx.shadowOffsetX, 0.5, "ctx.shadowOffsetX", "0.5");
+ _assertSame(ctx.shadowOffsetY, 0.25, "ctx.shadowOffsetY", "0.25");
+
+ ctx.shadowOffsetX = -0.5;
+ ctx.shadowOffsetY = -0.25;
+ _assertSame(ctx.shadowOffsetX, -0.5, "ctx.shadowOffsetX", "-0.5");
+ _assertSame(ctx.shadowOffsetY, -0.25, "ctx.shadowOffsetY", "-0.25");
+
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+
+ ctx.shadowOffsetX = 1e6;
+ ctx.shadowOffsetY = 1e6;
+ _assertSame(ctx.shadowOffsetX, 1e6, "ctx.shadowOffsetX", "1e6");
+ _assertSame(ctx.shadowOffsetY, 1e6, "ctx.shadowOffsetY", "1e6");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.worker.js
new file mode 100644
index 0000000000..5881c5bca5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.attributes.shadowOffset.valid.worker.js
@@ -0,0 +1,45 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.attributes.shadowOffset.valid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ _assertSame(ctx.shadowOffsetX, 1, "ctx.shadowOffsetX", "1");
+ _assertSame(ctx.shadowOffsetY, 2, "ctx.shadowOffsetY", "2");
+
+ ctx.shadowOffsetX = 0.5;
+ ctx.shadowOffsetY = 0.25;
+ _assertSame(ctx.shadowOffsetX, 0.5, "ctx.shadowOffsetX", "0.5");
+ _assertSame(ctx.shadowOffsetY, 0.25, "ctx.shadowOffsetY", "0.25");
+
+ ctx.shadowOffsetX = -0.5;
+ ctx.shadowOffsetY = -0.25;
+ _assertSame(ctx.shadowOffsetX, -0.5, "ctx.shadowOffsetX", "-0.5");
+ _assertSame(ctx.shadowOffsetY, -0.25, "ctx.shadowOffsetY", "-0.25");
+
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ _assertSame(ctx.shadowOffsetX, 0, "ctx.shadowOffsetX", "0");
+ _assertSame(ctx.shadowOffsetY, 0, "ctx.shadowOffsetY", "0");
+
+ ctx.shadowOffsetX = 1e6;
+ ctx.shadowOffsetY = 1e6;
+ _assertSame(ctx.shadowOffsetX, 1e6, "ctx.shadowOffsetX", "1e6");
+ _assertSame(ctx.shadowOffsetY, 1e6, "ctx.shadowOffsetY", "1e6");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.html
new file mode 100644
index 0000000000..46c5032448
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.blur.high</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.blur.high</h1>
+<p class="desc">Shadows look correct for large blurs</p>
+
+
+<script>
+var t = async_test("Shadows look correct for large blurs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 100;
+ ctx.fillRect(-200, -200, 200, 400);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.worker.js
new file mode 100644
index 0000000000..8bd06da381
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high-manual.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.blur.high
+// Description:Shadows look correct for large blurs
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows look correct for large blurs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 100;
+ ctx.fillRect(-200, -200, 200, 400);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high.png
new file mode 100644
index 0000000000..743640b79f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.high.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.html
new file mode 100644
index 0000000000..19a42a683c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.blur.low</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.blur.low</h1>
+<p class="desc">Shadows look correct for small blurs</p>
+
+
+<script>
+var t = async_test("Shadows look correct for small blurs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 25;
+ for (var x = 0; x < 100; ++x) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(x, 0, 1, 50);
+ ctx.clip();
+ ctx.shadowBlur = x;
+ ctx.fillRect(-200, -200, 500, 200);
+ ctx.restore();
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.worker.js
new file mode 100644
index 0000000000..67bc8c424e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low-manual.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.blur.low
+// Description:Shadows look correct for small blurs
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows look correct for small blurs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 25;
+ for (var x = 0; x < 100; ++x) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(x, 0, 1, 50);
+ ctx.clip();
+ ctx.shadowBlur = x;
+ ctx.fillRect(-200, -200, 500, 200);
+ ctx.restore();
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low.png
new file mode 100644
index 0000000000..99fb651c21
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.blur.low.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.html
new file mode 100644
index 0000000000..cae4235e8c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.canvas.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.canvas.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent canvases</p>
+
+
+<script>
+var t = async_test("Shadows are drawn correctly for partially-transparent canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'rgba(255, 0, 0, 0.5)';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.worker.js
new file mode 100644
index 0000000000..0fa74a2070
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.alpha.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.canvas.alpha
+// Description:Shadows are drawn correctly for partially-transparent canvases
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn correctly for partially-transparent canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'rgba(255, 0, 0, 0.5)';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.html
new file mode 100644
index 0000000000..f5784e914f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.canvas.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.canvas.basic</h1>
+<p class="desc">Shadows are drawn for canvases</p>
+
+
+<script>
+var t = async_test("Shadows are drawn for canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.worker.js
new file mode 100644
index 0000000000..6a2d916a75
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.basic.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.canvas.basic
+// Description:Shadows are drawn for canvases
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn for canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.html
new file mode 100644
index 0000000000..62b3f83272
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.canvas.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.canvas.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent canvases</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn for transparent canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.worker.js
new file mode 100644
index 0000000000..1b02897d62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.1.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.canvas.transparent.1
+// Description:Shadows are not drawn for transparent canvases
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn for transparent canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.html
new file mode 100644
index 0000000000..0f80453523
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.canvas.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.canvas.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of canvases</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn for transparent parts of canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.drawImage(canvas2, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(canvas2, -50, -50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.worker.js
new file mode 100644
index 0000000000..6e468e28e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.canvas.transparent.2.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.canvas.transparent.2
+// Description:Shadows are not drawn for transparent parts of canvases
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn for transparent parts of canvases");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.drawImage(canvas2, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(canvas2, -50, -50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.html
new file mode 100644
index 0000000000..4df101d513
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.clip.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.clip.1</h1>
+<p class="desc">Shadows of clipped shapes are still drawn within the clipping region</p>
+
+
+<script>
+var t = async_test("Shadows of clipped shapes are still drawn within the clipping region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.worker.js
new file mode 100644
index 0000000000..6b05ceb226
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.1.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.clip.1
+// Description:Shadows of clipped shapes are still drawn within the clipping region
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows of clipped shapes are still drawn within the clipping region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.html
new file mode 100644
index 0000000000..6a09ea53f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.clip.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.clip.2</h1>
+<p class="desc">Shadows are not drawn outside the clipping region</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn outside the clipping region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.worker.js
new file mode 100644
index 0000000000..4cdfcfdcfc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.2.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.clip.2
+// Description:Shadows are not drawn outside the clipping region
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn outside the clipping region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.html
new file mode 100644
index 0000000000..d1732e9f5b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.clip.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.clip.3</h1>
+<p class="desc">Shadows of clipped shapes are still drawn within the clipping region</p>
+
+
+<script>
+var t = async_test("Shadows of clipped shapes are still drawn within the clipping region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.worker.js
new file mode 100644
index 0000000000..6f8b79d1fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.clip.3.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.clip.3
+// Description:Shadows of clipped shapes are still drawn within the clipping region
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows of clipped shapes are still drawn within the clipping region");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.html
new file mode 100644
index 0000000000..d6014fbd45
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.composite.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.composite.1</h1>
+<p class="desc">Shadows are drawn using globalCompositeOperation</p>
+
+
+<script>
+var t = async_test("Shadows are drawn using globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, 0, 200, 50);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.worker.js
new file mode 100644
index 0000000000..28637706e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.composite.1
+// Description:Shadows are drawn using globalCompositeOperation
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn using globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, 0, 200, 50);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.html
new file mode 100644
index 0000000000..87f2e3ba0f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.composite.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.composite.2</h1>
+<p class="desc">Shadows are drawn using globalCompositeOperation</p>
+
+
+<script>
+var t = async_test("Shadows are drawn using globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-10, -10, 120, 70);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.worker.js
new file mode 100644
index 0000000000..a144446ec5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.composite.2
+// Description:Shadows are drawn using globalCompositeOperation
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn using globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-10, -10, 120, 70);
+
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.html
new file mode 100644
index 0000000000..f3cafa983a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.composite.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.composite.3</h1>
+<p class="desc">Areas outside shadows are drawn correctly with destination-out</p>
+
+
+<script>
+var t = async_test("Areas outside shadows are drawn correctly with destination-out");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 10;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(200, 0, 100, 50);
+
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.worker.js
new file mode 100644
index 0000000000..ba29487eea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.composite.3.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.composite.3
+// Description:Areas outside shadows are drawn correctly with destination-out
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Areas outside shadows are drawn correctly with destination-out");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 10;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(200, 0, 100, 50);
+
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.html
new file mode 100644
index 0000000000..a25be8ebcf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.enable.blur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.enable.blur</h1>
+<p class="desc">Shadows are drawn if shadowBlur is set</p>
+
+
+<script>
+var t = async_test("Shadows are drawn if shadowBlur is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.worker.js
new file mode 100644
index 0000000000..859ae8b612
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.enable.blur
+// Description:Shadows are drawn if shadowBlur is set
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn if shadowBlur is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.html
new file mode 100644
index 0000000000..de85b7366b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.enable.off.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.enable.off.1</h1>
+<p class="desc">Shadows are not drawn when only shadowColor is set</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn when only shadowColor is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.worker.js
new file mode 100644
index 0000000000..7ccd1351fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.1.worker.js
@@ -0,0 +1,25 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.enable.off.1
+// Description:Shadows are not drawn when only shadowColor is set
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn when only shadowColor is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.html
new file mode 100644
index 0000000000..a57cb66aff
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.enable.off.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.enable.off.2</h1>
+<p class="desc">Shadows are not drawn when only shadowColor is set</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn when only shadowColor is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.worker.js
new file mode 100644
index 0000000000..e068a7ab54
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.off.2.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.enable.off.2
+// Description:Shadows are not drawn when only shadowColor is set
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn when only shadowColor is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.html
new file mode 100644
index 0000000000..70b0a84dcf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.enable.x</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.enable.x</h1>
+<p class="desc">Shadows are drawn if shadowOffsetX is set</p>
+
+
+<script>
+var t = async_test("Shadows are drawn if shadowOffsetX is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.worker.js
new file mode 100644
index 0000000000..975c621ffa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.enable.x
+// Description:Shadows are drawn if shadowOffsetX is set
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn if shadowOffsetX is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.html
new file mode 100644
index 0000000000..df8bcc2ba2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.enable.y</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.enable.y</h1>
+<p class="desc">Shadows are drawn if shadowOffsetY is set</p>
+
+
+<script>
+var t = async_test("Shadows are drawn if shadowOffsetY is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.worker.js
new file mode 100644
index 0000000000..1dae1000a3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.enable.y
+// Description:Shadows are drawn if shadowOffsetY is set
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn if shadowOffsetY is set");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.html
new file mode 100644
index 0000000000..199dd6c3fa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.gradient.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.gradient.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent gradient fills</p>
+
+
+<script>
+var t = async_test("Shadows are drawn correctly for partially-transparent gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(255,0,0,0.5)');
+ gradient.addColorStop(1, 'rgba(255,0,0,0.5)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.worker.js
new file mode 100644
index 0000000000..0f9a6449c4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.alpha.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.gradient.alpha
+// Description:Shadows are drawn correctly for partially-transparent gradient fills
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn correctly for partially-transparent gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(255,0,0,0.5)');
+ gradient.addColorStop(1, 'rgba(255,0,0,0.5)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.html
new file mode 100644
index 0000000000..6746f03f3d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.gradient.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.gradient.basic</h1>
+<p class="desc">Shadows are drawn for gradient fills</p>
+
+
+<script>
+var t = async_test("Shadows are drawn for gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(1, '#f00');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.worker.js
new file mode 100644
index 0000000000..068b48bb6f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.basic.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.gradient.basic
+// Description:Shadows are drawn for gradient fills
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn for gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(1, '#f00');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.html
new file mode 100644
index 0000000000..7e7309e976
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.gradient.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.gradient.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent gradient fills</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn for transparent gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.worker.js
new file mode 100644
index 0000000000..1102848ecb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.1.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.gradient.transparent.1
+// Description:Shadows are not drawn for transparent gradient fills
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn for transparent gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.html
new file mode 100644
index 0000000000..8259eb9ae5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.gradient.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.gradient.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of gradient fills</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn for transparent parts of gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(0.499, '#f00');
+ gradient.addColorStop(0.5, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.worker.js
new file mode 100644
index 0000000000..5fcf4ee93d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.gradient.transparent.2.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.gradient.transparent.2
+// Description:Shadows are not drawn for transparent parts of gradient fills
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn for transparent parts of gradient fills");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(0.499, '#f00');
+ gradient.addColorStop(0.5, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.html
new file mode 100644
index 0000000000..f7f797fd0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.image.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.image.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent images</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ var response = await fetch('/images/transparent50.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+}, "Shadows are drawn correctly for partially-transparent images");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js
new file mode 100644
index 0000000000..827bd2ce65
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.image.alpha
+// Description:Shadows are drawn correctly for partially-transparent images
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ var response = await fetch('/images/transparent50.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+}, "Shadows are drawn correctly for partially-transparent images");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.html
new file mode 100644
index 0000000000..5b959fdd92
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.image.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.image.basic</h1>
+<p class="desc">Shadows are drawn for images</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+}, "Shadows are drawn for images");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js
new file mode 100644
index 0000000000..2d02232c68
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.image.basic
+// Description:Shadows are drawn for images
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+}, "Shadows are drawn for images");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.html
new file mode 100644
index 0000000000..7e2536c052
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.image.scale</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.image.scale</h1>
+<p class="desc">Shadows are drawn correctly for scaled images</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, 0, 100, 50, -10, -50, 240, 50);
+
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Shadows are drawn correctly for scaled images");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js
new file mode 100644
index 0000000000..2c10381f98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.image.scale
+// Description:Shadows are drawn correctly for scaled images
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, 0, 100, 50, -10, -50, 240, 50);
+
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Shadows are drawn correctly for scaled images");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.html
new file mode 100644
index 0000000000..7744ee153b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.image.section</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.image.section</h1>
+<p class="desc">Shadows are not drawn for areas outside image source rectangles</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#f00';
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 50, 0, 50, 50, 0, -50, 50, 50);
+
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Shadows are not drawn for areas outside image source rectangles");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js
new file mode 100644
index 0000000000..94affdfb6a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.image.section
+// Description:Shadows are not drawn for areas outside image source rectangles
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#f00';
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 50, 0, 50, 50, 0, -50, 50, 50);
+
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Shadows are not drawn for areas outside image source rectangles");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.html
new file mode 100644
index 0000000000..30db23ce76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.image.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.image.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent images</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ var response = await fetch('/images/transparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+}, "Shadows are not drawn for transparent images");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js
new file mode 100644
index 0000000000..c5421d7042
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.image.transparent.1
+// Description:Shadows are not drawn for transparent images
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ var response = await fetch('/images/transparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 0, -50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+}, "Shadows are not drawn for transparent images");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.html
new file mode 100644
index 0000000000..9b3b206a26
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.image.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.image.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of images</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(img, -50, -50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+}, "Shadows are not drawn for transparent parts of images");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js
new file mode 100644
index 0000000000..071c584fa8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.image.transparent.2
+// Description:Shadows are not drawn for transparent parts of images
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ ctx.drawImage(img, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(img, -50, -50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+}, "Shadows are not drawn for transparent parts of images");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.html
new file mode 100644
index 0000000000..cc0d1adb97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.offset.negativeX</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.offset.negativeX</h1>
+<p class="desc">Shadows can be offset with negative x</p>
+
+
+<script>
+var t = async_test("Shadows can be offset with negative x");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = -50;
+ ctx.fillRect(50, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.worker.js
new file mode 100644
index 0000000000..14c709a2a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeX.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.offset.negativeX
+// Description:Shadows can be offset with negative x
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows can be offset with negative x");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = -50;
+ ctx.fillRect(50, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.html
new file mode 100644
index 0000000000..82094cf2f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.offset.negativeY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.offset.negativeY</h1>
+<p class="desc">Shadows can be offset with negative y</p>
+
+
+<script>
+var t = async_test("Shadows can be offset with negative y");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = -25;
+ ctx.fillRect(0, 25, 100, 25);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.worker.js
new file mode 100644
index 0000000000..036c6154cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.negativeY.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.offset.negativeY
+// Description:Shadows can be offset with negative y
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows can be offset with negative y");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = -25;
+ ctx.fillRect(0, 25, 100, 25);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.html
new file mode 100644
index 0000000000..7094e98ecf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.offset.positiveX</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.offset.positiveX</h1>
+<p class="desc">Shadows can be offset with positive x</p>
+
+
+<script>
+var t = async_test("Shadows can be offset with positive x");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.worker.js
new file mode 100644
index 0000000000..176aca45f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveX.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.offset.positiveX
+// Description:Shadows can be offset with positive x
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows can be offset with positive x");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.html
new file mode 100644
index 0000000000..75e9b0affa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.offset.positiveY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.offset.positiveY</h1>
+<p class="desc">Shadows can be offset with positive y</p>
+
+
+<script>
+var t = async_test("Shadows can be offset with positive y");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 25;
+ ctx.fillRect(0, 0, 100, 25);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.worker.js
new file mode 100644
index 0000000000..6b4d0c6a21
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.offset.positiveY.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.offset.positiveY
+// Description:Shadows can be offset with positive y
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows can be offset with positive y");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 25;
+ ctx.fillRect(0, 0, 100, 25);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.html
new file mode 100644
index 0000000000..498051465b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.outside</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.outside</h1>
+<p class="desc">Shadows of shapes outside the visible area can be offset onto the visible area</p>
+
+
+<script>
+var t = async_test("Shadows of shapes outside the visible area can be offset onto the visible area");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.fillRect(-100, 0, 25, 50);
+ ctx.shadowOffsetX = -100;
+ ctx.fillRect(175, 0, 25, 50);
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 100;
+ ctx.fillRect(25, -100, 50, 25);
+ ctx.shadowOffsetY = -100;
+ ctx.fillRect(25, 125, 50, 25);
+ _assertPixel(canvas, 12,25, 0,255,0,255);
+ _assertPixel(canvas, 87,25, 0,255,0,255);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.worker.js
new file mode 100644
index 0000000000..d2a071f474
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.outside.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.outside
+// Description:Shadows of shapes outside the visible area can be offset onto the visible area
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows of shapes outside the visible area can be offset onto the visible area");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.fillRect(-100, 0, 25, 50);
+ ctx.shadowOffsetX = -100;
+ ctx.fillRect(175, 0, 25, 50);
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 100;
+ ctx.fillRect(25, -100, 50, 25);
+ ctx.shadowOffsetY = -100;
+ ctx.fillRect(25, 125, 50, 25);
+ _assertPixel(canvas, 12,25, 0,255,0,255);
+ _assertPixel(canvas, 87,25, 0,255,0,255);
+ _assertPixel(canvas, 50,12, 0,255,0,255);
+ _assertPixel(canvas, 50,37, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.html
new file mode 100644
index 0000000000..c45654f999
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.pattern.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.pattern.alpha</h1>
+<p class="desc">Shadows are drawn correctly for partially-transparent fill patterns</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/transparent50.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+
+}, "Shadows are drawn correctly for partially-transparent fill patterns");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.png b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.png
new file mode 100644
index 0000000000..8764e89b37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js
new file mode 100644
index 0000000000..41a826a158
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.pattern.alpha
+// Description:Shadows are drawn correctly for partially-transparent fill patterns
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/transparent50.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
+ t.done();
+}, "Shadows are drawn correctly for partially-transparent fill patterns");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.html
new file mode 100644
index 0000000000..4e372c0932
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.pattern.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.pattern.basic</h1>
+<p class="desc">Shadows are drawn for fill patterns</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+}, "Shadows are drawn for fill patterns");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js
new file mode 100644
index 0000000000..7a8f7e8479
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.pattern.basic
+// Description:Shadows are drawn for fill patterns
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/red.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+}, "Shadows are drawn for fill patterns");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.html
new file mode 100644
index 0000000000..72fa7b29f8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.pattern.transparent.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.pattern.transparent.1</h1>
+<p class="desc">Shadows are not drawn for transparent fill patterns</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/transparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+
+}, "Shadows are not drawn for transparent fill patterns");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js
new file mode 100644
index 0000000000..8414db6d08
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.pattern.transparent.1
+// Description:Shadows are not drawn for transparent fill patterns
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/transparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+}, "Shadows are not drawn for transparent fill patterns");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.html
new file mode 100644
index 0000000000..6ad8ece009
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.pattern.transparent.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.pattern.transparent.2</h1>
+<p class="desc">Shadows are not drawn for transparent parts of fill patterns</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+
+}, "Shadows are not drawn for transparent parts of fill patterns");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js
new file mode 100644
index 0000000000..726a42d6cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.pattern.transparent.2
+// Description:Shadows are not drawn for transparent parts of fill patterns
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var response = await fetch('/images/redtransparent.png')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+}, "Shadows are not drawn for transparent parts of fill patterns");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.html
new file mode 100644
index 0000000000..c569865a60
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.stroke.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.stroke.basic</h1>
+<p class="desc">Shadows are drawn for strokes</p>
+
+
+<script>
+var t = async_test("Shadows are drawn for strokes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, -25);
+ ctx.lineTo(100, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.worker.js
new file mode 100644
index 0000000000..9d7d0ec321
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.basic.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.stroke.basic
+// Description:Shadows are drawn for strokes
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn for strokes");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, -25);
+ ctx.lineTo(100, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.html
new file mode 100644
index 0000000000..b170f88cf6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.stroke.cap.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.stroke.cap.1</h1>
+<p class="desc">Shadows are not drawn for areas outside stroke caps</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn for areas outside stroke caps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'butt';
+ ctx.moveTo(-50, -25);
+ ctx.lineTo(0, -25);
+ ctx.moveTo(100, -25);
+ ctx.lineTo(150, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.worker.js
new file mode 100644
index 0000000000..582915d28b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.1.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.stroke.cap.1
+// Description:Shadows are not drawn for areas outside stroke caps
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn for areas outside stroke caps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'butt';
+ ctx.moveTo(-50, -25);
+ ctx.lineTo(0, -25);
+ ctx.moveTo(100, -25);
+ ctx.lineTo(150, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.html
new file mode 100644
index 0000000000..8bbb016bd5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.stroke.cap.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.stroke.cap.2</h1>
+<p class="desc">Shadows are drawn for stroke caps</p>
+
+
+<script>
+var t = async_test("Shadows are drawn for stroke caps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'square';
+ ctx.moveTo(25, -25);
+ ctx.lineTo(75, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.worker.js
new file mode 100644
index 0000000000..13108bec9e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.cap.2.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.stroke.cap.2
+// Description:Shadows are drawn for stroke caps
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn for stroke caps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'square';
+ ctx.moveTo(25, -25);
+ ctx.lineTo(75, -25);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,25, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.html
new file mode 100644
index 0000000000..f0ef2d250e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.stroke.join.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.stroke.join.1</h1>
+<p class="desc">Shadows are not drawn for areas outside stroke joins</p>
+
+
+<script>
+var t = async_test("Shadows are not drawn for areas outside stroke joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.worker.js
new file mode 100644
index 0000000000..0cc1b5f196
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.1.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.stroke.join.1
+// Description:Shadows are not drawn for areas outside stroke joins
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are not drawn for areas outside stroke joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.html
new file mode 100644
index 0000000000..f2800aa19a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.stroke.join.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.stroke.join.2</h1>
+<p class="desc">Shadows are drawn for stroke joins</p>
+
+
+<script>
+var t = async_test("Shadows are drawn for stroke joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.worker.js
new file mode 100644
index 0000000000..925f65a28e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.2.worker.js
@@ -0,0 +1,40 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.stroke.join.2
+// Description:Shadows are drawn for stroke joins
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn for stroke joins");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.html
new file mode 100644
index 0000000000..390e6a8048
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.stroke.join.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.stroke.join.3</h1>
+<p class="desc">Shadows are drawn for stroke joins respecting miter limit</p>
+
+
+<script>
+var t = async_test("Shadows are drawn for stroke joins respecting miter limit");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 0.1;
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100); // (not an exact right angle, to avoid some other bug in Firefox 3)
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.worker.js
new file mode 100644
index 0000000000..827a6d6662
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.stroke.join.3.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.stroke.join.3
+// Description:Shadows are drawn for stroke joins respecting miter limit
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows are drawn for stroke joins respecting miter limit");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 0.1;
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100); // (not an exact right angle, to avoid some other bug in Firefox 3)
+ ctx.stroke();
+
+ _assertPixel(canvas, 1,1, 0,255,0,255);
+ _assertPixel(canvas, 48,48, 0,255,0,255);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,48, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.html
new file mode 100644
index 0000000000..23946b20a5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.transform.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.transform.1</h1>
+<p class="desc">Shadows take account of transformations</p>
+
+
+<script>
+var t = async_test("Shadows take account of transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.translate(100, 100);
+ ctx.fillRect(-100, -150, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.worker.js
new file mode 100644
index 0000000000..6ac8da4c97
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.1.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.transform.1
+// Description:Shadows take account of transformations
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadows take account of transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.translate(100, 100);
+ ctx.fillRect(-100, -150, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.html b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.html
new file mode 100644
index 0000000000..edcd20bdca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.shadow.transform.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.shadow.transform.2</h1>
+<p class="desc">Shadow offsets are not affected by transformations</p>
+
+
+<script>
+var t = async_test("Shadow offsets are not affected by transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.rotate(Math.PI)
+ ctx.fillRect(-100, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.worker.js
new file mode 100644
index 0000000000..e9e745a2ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.transform.2.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.shadow.transform.2
+// Description:Shadow offsets are not affected by transformations
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Shadow offsets are not affected by transformations");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.rotate(Math.PI)
+ ctx.fillRect(-100, 0, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.html
new file mode 100644
index 0000000000..7af2c772f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.align.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.align.default</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.worker.js
new file mode 100644
index 0000000000..adee16c062
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.default.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.align.default
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.html
new file mode 100644
index 0000000000..a7679d656e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.align.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.align.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'bogus';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'END';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end ';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end\0';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.worker.js
new file mode 100644
index 0000000000..dfd4c66256
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.invalid.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.align.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'bogus';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'END';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end ';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end\0';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.html
new file mode 100644
index 0000000000..eef6b29633
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.align.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.align.valid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textAlign = 'start';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'end';
+ _assertSame(ctx.textAlign, 'end', "ctx.textAlign", "'end'");
+
+ ctx.textAlign = 'left';
+ _assertSame(ctx.textAlign, 'left', "ctx.textAlign", "'left'");
+
+ ctx.textAlign = 'right';
+ _assertSame(ctx.textAlign, 'right', "ctx.textAlign", "'right'");
+
+ ctx.textAlign = 'center';
+ _assertSame(ctx.textAlign, 'center', "ctx.textAlign", "'center'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.worker.js
new file mode 100644
index 0000000000..767d6425e7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.align.valid.worker.js
@@ -0,0 +1,35 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.align.valid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textAlign = 'start';
+ _assertSame(ctx.textAlign, 'start', "ctx.textAlign", "'start'");
+
+ ctx.textAlign = 'end';
+ _assertSame(ctx.textAlign, 'end', "ctx.textAlign", "'end'");
+
+ ctx.textAlign = 'left';
+ _assertSame(ctx.textAlign, 'left', "ctx.textAlign", "'left'");
+
+ ctx.textAlign = 'right';
+ _assertSame(ctx.textAlign, 'right', "ctx.textAlign", "'right'");
+
+ ctx.textAlign = 'center';
+ _assertSame(ctx.textAlign, 'center', "ctx.textAlign", "'center'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.html
new file mode 100644
index 0000000000..525a02ff9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.baseline.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.baseline.default</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.textBaseline, 'alphabetic', "ctx.textBaseline", "'alphabetic'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.worker.js
new file mode 100644
index 0000000000..667adb8c51
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.default.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.baseline.default
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.textBaseline, 'alphabetic', "ctx.textBaseline", "'alphabetic'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.html
new file mode 100644
index 0000000000..f30fad63b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.baseline.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.baseline.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'bogus';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'MIDDLE';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle ';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle\0';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js
new file mode 100644
index 0000000000..4310f81fba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.baseline.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'bogus';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'MIDDLE';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle ';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle\0';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.html
new file mode 100644
index 0000000000..9ff843c19f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.baseline.valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.baseline.valid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textBaseline = 'top';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'hanging';
+ _assertSame(ctx.textBaseline, 'hanging', "ctx.textBaseline", "'hanging'");
+
+ ctx.textBaseline = 'middle';
+ _assertSame(ctx.textBaseline, 'middle', "ctx.textBaseline", "'middle'");
+
+ ctx.textBaseline = 'alphabetic';
+ _assertSame(ctx.textBaseline, 'alphabetic', "ctx.textBaseline", "'alphabetic'");
+
+ ctx.textBaseline = 'ideographic';
+ _assertSame(ctx.textBaseline, 'ideographic', "ctx.textBaseline", "'ideographic'");
+
+ ctx.textBaseline = 'bottom';
+ _assertSame(ctx.textBaseline, 'bottom', "ctx.textBaseline", "'bottom'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.worker.js
new file mode 100644
index 0000000000..2f422f58a8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.baseline.valid.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.baseline.valid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textBaseline = 'top';
+ _assertSame(ctx.textBaseline, 'top', "ctx.textBaseline", "'top'");
+
+ ctx.textBaseline = 'hanging';
+ _assertSame(ctx.textBaseline, 'hanging', "ctx.textBaseline", "'hanging'");
+
+ ctx.textBaseline = 'middle';
+ _assertSame(ctx.textBaseline, 'middle', "ctx.textBaseline", "'middle'");
+
+ ctx.textBaseline = 'alphabetic';
+ _assertSame(ctx.textBaseline, 'alphabetic', "ctx.textBaseline", "'alphabetic'");
+
+ ctx.textBaseline = 'ideographic';
+ _assertSame(ctx.textBaseline, 'ideographic', "ctx.textBaseline", "'ideographic'");
+
+ ctx.textBaseline = 'bottom';
+ _assertSame(ctx.textBaseline, 'bottom', "ctx.textBaseline", "'bottom'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.html
new file mode 100644
index 0000000000..0f7a2f4a02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.align.center</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.align.center</h1>
+<p class="desc">textAlign center is the center of the em squares (not the bounding box)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'center';
+ ctx.fillText('DD', 50, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign center is the center of the em squares (not the bounding box)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js
new file mode 100644
index 0000000000..44f093cdac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.align.center
+// Description:textAlign center is the center of the em squares (not the bounding box)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'center';
+ ctx.fillText('DD', 50, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textAlign center is the center of the em squares (not the bounding box)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.html
new file mode 100644
index 0000000000..a9e6dfe901
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.align.end.ltr</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.align.end.ltr</h1>
+<p class="desc">textAlign end with ltr is the right edge</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign end with ltr is the right edge");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js
new file mode 100644
index 0000000000..4bd0f5a314
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.align.end.ltr
+// Description:textAlign end with ltr is the right edge
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textAlign end with ltr is the right edge");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.html
new file mode 100644
index 0000000000..b0d0b66a9c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.align.end.rtl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.align.end.rtl</h1>
+<p class="desc">textAlign end with rtl is the left edge</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'rtl';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign end with rtl is the left edge");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js
new file mode 100644
index 0000000000..b3e62d8538
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.align.end.rtl
+// Description:textAlign end with rtl is the left edge
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'rtl';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textAlign end with rtl is the left edge");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.html
new file mode 100644
index 0000000000..563596d757
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.align.left</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.align.left</h1>
+<p class="desc">textAlign left is the left of the first em square (not the bounding box)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'left';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign left is the left of the first em square (not the bounding box)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js
new file mode 100644
index 0000000000..c7dc0bed42
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.align.left
+// Description:textAlign left is the left of the first em square (not the bounding box)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'left';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textAlign left is the left of the first em square (not the bounding box)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.html
new file mode 100644
index 0000000000..227a8f981f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.align.right</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.align.right</h1>
+<p class="desc">textAlign right is the right of the last em square (not the bounding box)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign right is the right of the last em square (not the bounding box)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js
new file mode 100644
index 0000000000..58fe85e0b7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.align.right
+// Description:textAlign right is the right of the last em square (not the bounding box)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textAlign right is the right of the last em square (not the bounding box)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.html
new file mode 100644
index 0000000000..bd7e69451d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.align.start.ltr</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.align.start.ltr</h1>
+<p class="desc">textAlign start with ltr is the left edge</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign start with ltr is the left edge");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js
new file mode 100644
index 0000000000..c0793d9af6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.align.start.ltr
+// Description:textAlign start with ltr is the left edge
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textAlign start with ltr is the left edge");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.html
new file mode 100644
index 0000000000..cadc8a4644
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.align.start.rtl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.align.start.rtl</h1>
+<p class="desc">textAlign start with rtl is the right edge</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'rtl';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textAlign start with rtl is the right edge");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js
new file mode 100644
index 0000000000..ed706c287a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.align.start.rtl
+// Description:textAlign start with rtl is the right edge
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'rtl';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 100, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textAlign start with rtl is the right edge");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.html
new file mode 100644
index 0000000000..ff72b3219d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.baseline.alphabetic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.baseline.alphabetic</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'alphabetic';
+ ctx.fillText('CC', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js
new file mode 100644
index 0000000000..a98a5863d9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.baseline.alphabetic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'alphabetic';
+ ctx.fillText('CC', 0, 37.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.html
new file mode 100644
index 0000000000..f75a94243d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.baseline.bottom</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.baseline.bottom</h1>
+<p class="desc">textBaseline bottom is the bottom of the em square (not the bounding box)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'bottom';
+ ctx.fillText('CC', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textBaseline bottom is the bottom of the em square (not the bounding box)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js
new file mode 100644
index 0000000000..30f60c574b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.baseline.bottom
+// Description:textBaseline bottom is the bottom of the em square (not the bounding box)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'bottom';
+ ctx.fillText('CC', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textBaseline bottom is the bottom of the em square (not the bounding box)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.html
new file mode 100644
index 0000000000..a29db78385
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.baseline.hanging</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.baseline.hanging</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'hanging';
+ ctx.fillText('CC', 0, 12.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js
new file mode 100644
index 0000000000..638506a279
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.baseline.hanging
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'hanging';
+ ctx.fillText('CC', 0, 12.5);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.html
new file mode 100644
index 0000000000..6bda1b4f30
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.baseline.ideographic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.baseline.ideographic</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'ideographic';
+ ctx.fillText('CC', 0, 31.25);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js
new file mode 100644
index 0000000000..7c682fb5f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.baseline.ideographic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'ideographic';
+ ctx.fillText('CC', 0, 31.25);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.html
new file mode 100644
index 0000000000..b0186e6549
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.baseline.middle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.baseline.middle</h1>
+<p class="desc">textBaseline middle is the middle of the em square (not the bounding box)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'middle';
+ ctx.fillText('CC', 0, 25);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textBaseline middle is the middle of the em square (not the bounding box)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js
new file mode 100644
index 0000000000..c373ab6c72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.baseline.middle
+// Description:textBaseline middle is the middle of the em square (not the bounding box)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'middle';
+ ctx.fillText('CC', 0, 25);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textBaseline middle is the middle of the em square (not the bounding box)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.html
new file mode 100644
index 0000000000..163d27dec7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.baseline.top</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.baseline.top</h1>
+<p class="desc">textBaseline top is the top of the em square (not the bounding box)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'top';
+ ctx.fillText('CC', 0, 0);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+
+}, "textBaseline top is the top of the em square (not the bounding box)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js
new file mode 100644
index 0000000000..effc4418c3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.baseline.top
+// Description:textBaseline top is the top of the em square (not the bounding box)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'top';
+ ctx.fillText('CC', 0, 0);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
+ t.done();
+}, "textBaseline top is the top of the em square (not the bounding box)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.html
new file mode 100644
index 0000000000..af1cb9d94a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.basic</h1>
+<p class="desc">fillText draws filled text</p>
+
+
+<script>
+var t = async_test("fillText draws filled text");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.worker.js
new file mode 100644
index 0000000000..ef00236fbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic-manual.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.basic
+// Description:fillText draws filled text
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText draws filled text");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic.png b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic.png
new file mode 100644
index 0000000000..70d7b046cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.basic.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.html
new file mode 100644
index 0000000000..8f22b9f093
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.maxWidth.NaN</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.maxWidth.NaN</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, NaN);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.worker.js
new file mode 100644
index 0000000000..fd1dff136e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.NaN.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.maxWidth.NaN
+// Description:fillText handles maxWidth correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, NaN);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.html
new file mode 100644
index 0000000000..2dca5a75ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.maxWidth.bound</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.maxWidth.bound</h1>
+<p class="desc">fillText handles maxWidth based on line size, not bounding box size</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('DD', 0, 37.5, 100);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "fillText handles maxWidth based on line size, not bounding box size");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js
new file mode 100644
index 0000000000..adcb45faaa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.maxWidth.bound
+// Description:fillText handles maxWidth based on line size, not bounding box size
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('DD', 0, 37.5, 100);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "fillText handles maxWidth based on line size, not bounding box size");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html
new file mode 100644
index 0000000000..fa7277ed7a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.maxWidth.fontface</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.maxWidth.fontface</h1>
+<p class="desc">fillText works on @font-face fonts</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillText('EEEE', -50, 37.5, 40);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "fillText works on @font-face fonts");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js
new file mode 100644
index 0000000000..8df519b7d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.maxWidth.fontface
+// Description:fillText works on @font-face fonts
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillText('EEEE', -50, 37.5, 40);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "fillText works on @font-face fonts");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.html
new file mode 100644
index 0000000000..7a0cf11b39
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.maxWidth.large</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.maxWidth.large</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35, 200);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.worker.js
new file mode 100644
index 0000000000..3e200e89ed
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large-manual.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.maxWidth.large
+// Description:fillText handles maxWidth correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35, 200);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large.png b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large.png
new file mode 100644
index 0000000000..70d7b046cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.large.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.html
new file mode 100644
index 0000000000..4411fbfced
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.maxWidth.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.maxWidth.negative</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, -1);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.worker.js
new file mode 100644
index 0000000000..50076b524c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.negative.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.maxWidth.negative
+// Description:fillText handles maxWidth correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, -1);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.html
new file mode 100644
index 0000000000..94988fc92e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.maxWidth.small</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.maxWidth.small</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', -100, 35, 90);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.worker.js
new file mode 100644
index 0000000000..5d5ef4e391
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.small.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.maxWidth.small
+// Description:fillText handles maxWidth correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', -100, 35, 90);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.html
new file mode 100644
index 0000000000..a789a84dbe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.maxWidth.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.maxWidth.zero</h1>
+<p class="desc">fillText handles maxWidth correctly</p>
+
+
+<script>
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, 0);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.worker.js
new file mode 100644
index 0000000000..7dea281f98
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.zero.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.maxWidth.zero
+// Description:fillText handles maxWidth correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText handles maxWidth correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, 0);
+ _assertGreen(ctx, 100, 50);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.html
new file mode 100644
index 0000000000..98e329b054
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.rtl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.rtl</h1>
+<p class="desc">fillText respects Right-To-Left Override characters</p>
+
+
+<script>
+var t = async_test("fillText respects Right-To-Left Override characters");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('\u202eFAIL \xa0 \xa0 SSAP', 5, 35);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.worker.js
new file mode 100644
index 0000000000..ee24a0450f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl-manual.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.rtl
+// Description:fillText respects Right-To-Left Override characters
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText respects Right-To-Left Override characters");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('\u202eFAIL \xa0 \xa0 SSAP', 5, 35);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl.png b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl.png
new file mode 100644
index 0000000000..70d7b046cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.rtl.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.html
new file mode 100644
index 0000000000..7124c6eafd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fill.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fill.unaffected</h1>
+<p class="desc">fillText does not start a new path or subpath</p>
+
+
+<script>
+var t = async_test("fillText does not start a new path or subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.worker.js
new file mode 100644
index 0000000000..0e250286d4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.unaffected.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fill.unaffected
+// Description:fillText does not start a new path or subpath
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("fillText does not start a new path or subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.html
new file mode 100644
index 0000000000..5d4f93b4f3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fontface</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fontface</h1>
+<p class="desc"></p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.html
new file mode 100644
index 0000000000..82c9b31e66
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fontface.notinpage</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fontface.notinpage</h1>
+<p class="desc">@font-face fonts should work even if they are not used in the page</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "@font-face fonts should work even if they are not used in the page");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js
new file mode 100644
index 0000000000..628cb39506
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fontface.notinpage
+// Description:@font-face fonts should work even if they are not used in the page
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "@font-face fonts should work even if they are not used in the page");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.html
new file mode 100644
index 0000000000..1a6ba78fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.fontface.repeat</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.fontface.repeat</h1>
+<p class="desc">Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+
+ await new Promise(resolve => t.step_timeout(resolve, 500));
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js
new file mode 100644
index 0000000000..7567c09558
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fontface.repeat
+// Description:Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+
+ await new Promise(resolve => t.step_timeout(resolve, 500));
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js
new file mode 100644
index 0000000000..02257f0a06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.fontface
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.html
new file mode 100644
index 0000000000..de456a2b96
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.kern.consistent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.kern.consistent</h1>
+<p class="desc">Stroked and filled text should have exactly the same kerning so it overlaps</p>
+
+
+<script>
+var t = async_test("Stroked and filled text should have exactly the same kerning so it overlaps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 3;
+ ctx.font = '20px Arial, sans-serif';
+ ctx.fillText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.fillText('ToToToToToToTo', -50, 45);
+ ctx.strokeText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.strokeText('ToToToToToToTo', -50, 45);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.worker.js
new file mode 100644
index 0000000000..0d1c9470ee
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.kern.consistent-manual.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.kern.consistent
+// Description:Stroked and filled text should have exactly the same kerning so it overlaps
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Stroked and filled text should have exactly the same kerning so it overlaps");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 3;
+ ctx.font = '20px Arial, sans-serif';
+ ctx.fillText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.fillText('ToToToToToToTo', -50, 45);
+ ctx.strokeText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.strokeText('ToToToToToToTo', -50, 45);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.html
new file mode 100644
index 0000000000..55828fc72c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.space.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.space.basic</h1>
+<p class="desc">U+0020 is rendered the correct size (1em wide)</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', -100, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "U+0020 is rendered the correct size (1em wide)");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js
new file mode 100644
index 0000000000..ef09925c55
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.space.basic
+// Description:U+0020 is rendered the correct size (1em wide)
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', -100, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "U+0020 is rendered the correct size (1em wide)");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.html
new file mode 100644
index 0000000000..f00a187e23
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.space.collapse.end</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.space.collapse.end</h1>
+<p class="desc">Space characters at the end of a line are NOT collapsed</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('EE ', 100, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+
+}, "Space characters at the end of a line are NOT collapsed");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js
new file mode 100644
index 0000000000..9a9afa1e74
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.space.collapse.end
+// Description:Space characters at the end of a line are NOT collapsed
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('EE ', 100, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+ t.done();
+}, "Space characters at the end of a line are NOT collapsed");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.html
new file mode 100644
index 0000000000..792abe4deb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.space.collapse.nonspace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.space.collapse.nonspace</h1>
+<p class="desc">Non-space characters are not converted to U+0020 and collapsed</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E\x0b EE', -150, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Non-space characters are not converted to U+0020 and collapsed");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js
new file mode 100644
index 0000000000..a12c2f9a48
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.space.collapse.nonspace
+// Description:Non-space characters are not converted to U+0020 and collapsed
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E\x0b EE', -150, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Non-space characters are not converted to U+0020 and collapsed");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.html
new file mode 100644
index 0000000000..83e89270f9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.space.collapse.other</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.space.collapse.other</h1>
+<p class="desc">Space characters are converted to U+0020, and are NOT collapsed</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dEE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+
+}, "Space characters are converted to U+0020, and are NOT collapsed");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js
new file mode 100644
index 0000000000..bbf9e684be
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.space.collapse.other
+// Description:Space characters are converted to U+0020, and are NOT collapsed
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dEE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+ t.done();
+}, "Space characters are converted to U+0020, and are NOT collapsed");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.html
new file mode 100644
index 0000000000..5fc96ffd7d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.space.collapse.space</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.space.collapse.space</h1>
+<p class="desc">Space characters are converted to U+0020, and are NOT collapsed</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+
+}, "Space characters are converted to U+0020, and are NOT collapsed");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js
new file mode 100644
index 0000000000..e5ab7b67f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.space.collapse.space
+// Description:Space characters are converted to U+0020, and are NOT collapsed
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
+ t.done();
+}, "Space characters are converted to U+0020, and are NOT collapsed");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.html
new file mode 100644
index 0000000000..c07994360d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.space.collapse.start</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.space.collapse.start</h1>
+<p class="desc">Space characters at the start of a line are NOT collapsed</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText(' EE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 255,0,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+
+}, "Space characters at the start of a line are NOT collapsed");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js
new file mode 100644
index 0000000000..20fec7801f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.space.collapse.start
+// Description:Space characters at the start of a line are NOT collapsed
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText(' EE', 0, 37.5);
+ _assertPixelApprox(canvas, 25,25, 255,0,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ t.done();
+}, "Space characters at the start of a line are NOT collapsed");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.html
new file mode 100644
index 0000000000..62ac8884cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.stroke.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.stroke.basic</h1>
+<p class="desc">strokeText draws stroked text</p>
+
+
+<script>
+var t = async_test("strokeText draws stroked text");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 1;
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeText('PASS', 5, 35);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.worker.js
new file mode 100644
index 0000000000..33d6e32467
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic-manual.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.stroke.basic
+// Description:strokeText draws stroked text
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeText draws stroked text");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 1;
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeText('PASS', 5, 35);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic.png b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic.png
new file mode 100644
index 0000000000..fb3b5b830d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.basic.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.html
new file mode 100644
index 0000000000..76afc1b006
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.draw.stroke.unaffected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.draw.stroke.unaffected</h1>
+<p class="desc">strokeText does not start a new path or subpath</p>
+
+
+<script>
+var t = async_test("strokeText does not start a new path or subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeStyle = '#f00';
+ ctx.strokeText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.worker.js
new file mode 100644
index 0000000000..15c3b61700
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.stroke.unaffected.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.draw.stroke.unaffected
+// Description:strokeText does not start a new path or subpath
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("strokeText does not start a new path or subpath");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeStyle = '#f00';
+ ctx.strokeText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 5,45, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.html
new file mode 100644
index 0000000000..965d451020
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.absolute.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.absolute.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with absolute length</p>
+
+
+<script>
+var t = async_test("Testing letter spacing and word spacing with absolute length");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.letterSpacing = '3px';
+ _assertSame(ctx.letterSpacing, '3px', "ctx.letterSpacing", "'3px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.wordSpacing = '5px';
+ _assertSame(ctx.letterSpacing, '3px', "ctx.letterSpacing", "'3px'");
+ _assertSame(ctx.wordSpacing, '5px', "ctx.wordSpacing", "'5px'");
+
+ ctx.letterSpacing = '-1px';
+ ctx.wordSpacing = '-1px';
+ _assertSame(ctx.letterSpacing, '-1px', "ctx.letterSpacing", "'-1px'");
+ _assertSame(ctx.wordSpacing, '-1px', "ctx.wordSpacing", "'-1px'");
+
+ ctx.letterSpacing = '1PX';
+ ctx.wordSpacing = '10PX';
+ _assertSame(ctx.letterSpacing, '1px', "ctx.letterSpacing", "'1px'");
+ _assertSame(ctx.wordSpacing, '10px', "ctx.wordSpacing", "'10px'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.worker.js
new file mode 100644
index 0000000000..2b42b3e477
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.absolute.spacing.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.absolute.spacing
+// Description:Testing letter spacing and word spacing with absolute length
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing letter spacing and word spacing with absolute length");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.letterSpacing = '3px';
+ _assertSame(ctx.letterSpacing, '3px', "ctx.letterSpacing", "'3px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.wordSpacing = '5px';
+ _assertSame(ctx.letterSpacing, '3px', "ctx.letterSpacing", "'3px'");
+ _assertSame(ctx.wordSpacing, '5px', "ctx.wordSpacing", "'5px'");
+
+ ctx.letterSpacing = '-1px';
+ ctx.wordSpacing = '-1px';
+ _assertSame(ctx.letterSpacing, '-1px', "ctx.letterSpacing", "'-1px'");
+ _assertSame(ctx.wordSpacing, '-1px', "ctx.wordSpacing", "'-1px'");
+
+ ctx.letterSpacing = '1PX';
+ ctx.wordSpacing = '10PX';
+ _assertSame(ctx.letterSpacing, '1px', "ctx.letterSpacing", "'1px'");
+ _assertSame(ctx.wordSpacing, '10px', "ctx.wordSpacing", "'10px'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.html
new file mode 100644
index 0000000000..a3f0f7133e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.font-relative.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.font-relative.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with font-relative length</p>
+
+
+<script>
+var t = async_test("Testing letter spacing and word spacing with font-relative length");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.letterSpacing = '1EX';
+ ctx.wordSpacing = '1EM';
+ _assertSame(ctx.letterSpacing, '1ex', "ctx.letterSpacing", "'1ex'");
+ _assertSame(ctx.wordSpacing, '1em', "ctx.wordSpacing", "'1em'");
+
+ ctx.letterSpacing = '1ch';
+ ctx.wordSpacing = '1ic';
+ _assertSame(ctx.letterSpacing, '1ch', "ctx.letterSpacing", "'1ch'");
+ _assertSame(ctx.wordSpacing, '1ic', "ctx.wordSpacing", "'1ic'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.worker.js
new file mode 100644
index 0000000000..cd653208e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.font-relative.spacing.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.font-relative.spacing
+// Description:Testing letter spacing and word spacing with font-relative length
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing letter spacing and word spacing with font-relative length");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ ctx.letterSpacing = '1EX';
+ ctx.wordSpacing = '1EM';
+ _assertSame(ctx.letterSpacing, '1ex', "ctx.letterSpacing", "'1ex'");
+ _assertSame(ctx.wordSpacing, '1em', "ctx.wordSpacing", "'1em'");
+
+ ctx.letterSpacing = '1ch';
+ ctx.wordSpacing = '1ic';
+ _assertSame(ctx.letterSpacing, '1ch', "ctx.letterSpacing", "'1ch'");
+ _assertSame(ctx.wordSpacing, '1ic', "ctx.wordSpacing", "'1ic'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.html
new file mode 100644
index 0000000000..23f353513b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.fontKerning</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.fontKerning</h1>
+<p class="desc">Testing basic functionalities of fontKerning for canvas</p>
+
+
+<script>
+var t = async_test("Testing basic functionalities of fontKerning for canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "normal";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ width_normal = ctx.measureText("TAWATAVA").width;
+ ctx.fontKerning = "none";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ width_none = ctx.measureText("TAWATAVA").width;
+ _assert(width_normal < width_none, "width_normal < width_none");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.html
new file mode 100644
index 0000000000..7f6b57921e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.fontKerning.with.uppercase</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.fontKerning.with.uppercase</h1>
+<p class="desc">Testing basic functionalities of fontKerning for canvas</p>
+
+
+<script>
+var t = async_test("Testing basic functionalities of fontKerning for canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "Normal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "normal";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ ctx.fontKerning = "Auto";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "noRmal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NoRMal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NORMAL";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+
+ ctx.fontKerning = "None";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "none";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ ctx.fontKerning = "Auto";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nOne";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nonE";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NONE";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.worker.js
new file mode 100644
index 0000000000..4e0f32c692
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.worker.js
@@ -0,0 +1,53 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.fontKerning.with.uppercase
+// Description:Testing basic functionalities of fontKerning for canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing basic functionalities of fontKerning for canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "Normal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "normal";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ ctx.fontKerning = "Auto";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "noRmal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NoRMal";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NORMAL";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+
+ ctx.fontKerning = "None";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "none";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ ctx.fontKerning = "Auto";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nOne";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nonE";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NONE";
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.worker.js
new file mode 100644
index 0000000000..ec64214c7f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.fontKerning
+// Description:Testing basic functionalities of fontKerning for canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing basic functionalities of fontKerning for canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.fontKerning, "auto", "ctx.fontKerning", "\"auto\"");
+ ctx.fontKerning = "normal";
+ _assertSame(ctx.fontKerning, "normal", "ctx.fontKerning", "\"normal\"");
+ width_normal = ctx.measureText("TAWATAVA").width;
+ ctx.fontKerning = "none";
+ _assertSame(ctx.fontKerning, "none", "ctx.fontKerning", "\"none\"");
+ width_none = ctx.measureText("TAWATAVA").width;
+ _assert(width_normal < width_none, "width_normal < width_none");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.html
new file mode 100644
index 0000000000..95405a18a5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.fontStretch.settings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.fontStretch.settings</h1>
+<p class="desc">Testing value setting of fontStretch in Canvas</p>
+
+
+<script>
+var t = async_test("Testing value setting of fontStretch in Canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Setting textRendering with lower cases
+ ctx.fontStretch = "ultra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-condensed", "ctx.fontStretch", "\"ultra-condensed\"");
+
+ ctx.fontStretch = "extra-condensed";
+ _assertSame(ctx.fontStretch, "extra-condensed", "ctx.fontStretch", "\"extra-condensed\"");
+
+ ctx.fontStretch = "condensed";
+ _assertSame(ctx.fontStretch, "condensed", "ctx.fontStretch", "\"condensed\"");
+
+ ctx.fontStretch = "semi-condensed";
+ _assertSame(ctx.fontStretch, "semi-condensed", "ctx.fontStretch", "\"semi-condensed\"");
+
+ ctx.fontStretch = "normal";
+ _assertSame(ctx.fontStretch, "normal", "ctx.fontStretch", "\"normal\"");
+
+ ctx.fontStretch = "semi-expanded";
+ _assertSame(ctx.fontStretch, "semi-expanded", "ctx.fontStretch", "\"semi-expanded\"");
+
+ ctx.fontStretch = "expanded";
+ _assertSame(ctx.fontStretch, "expanded", "ctx.fontStretch", "\"expanded\"");
+
+ ctx.fontStretch = "extra-expanded";
+ _assertSame(ctx.fontStretch, "extra-expanded", "ctx.fontStretch", "\"extra-expanded\"");
+
+ ctx.fontStretch = "ultra-expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ // Setting fontStretch with lower cases and upper cases word,
+ // these values should be ignored.
+ ctx.fontStretch = "ulTra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Extra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "cOndensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Semi-Condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "normaL";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "semi-Expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "eXtra-expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "abcd";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.worker.js
new file mode 100644
index 0000000000..ff10a742b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontStretch.settings.worker.js
@@ -0,0 +1,77 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.fontStretch.settings
+// Description:Testing value setting of fontStretch in Canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing value setting of fontStretch in Canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Setting textRendering with lower cases
+ ctx.fontStretch = "ultra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-condensed", "ctx.fontStretch", "\"ultra-condensed\"");
+
+ ctx.fontStretch = "extra-condensed";
+ _assertSame(ctx.fontStretch, "extra-condensed", "ctx.fontStretch", "\"extra-condensed\"");
+
+ ctx.fontStretch = "condensed";
+ _assertSame(ctx.fontStretch, "condensed", "ctx.fontStretch", "\"condensed\"");
+
+ ctx.fontStretch = "semi-condensed";
+ _assertSame(ctx.fontStretch, "semi-condensed", "ctx.fontStretch", "\"semi-condensed\"");
+
+ ctx.fontStretch = "normal";
+ _assertSame(ctx.fontStretch, "normal", "ctx.fontStretch", "\"normal\"");
+
+ ctx.fontStretch = "semi-expanded";
+ _assertSame(ctx.fontStretch, "semi-expanded", "ctx.fontStretch", "\"semi-expanded\"");
+
+ ctx.fontStretch = "expanded";
+ _assertSame(ctx.fontStretch, "expanded", "ctx.fontStretch", "\"expanded\"");
+
+ ctx.fontStretch = "extra-expanded";
+ _assertSame(ctx.fontStretch, "extra-expanded", "ctx.fontStretch", "\"extra-expanded\"");
+
+ ctx.fontStretch = "ultra-expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ // Setting fontStretch with lower cases and upper cases word,
+ // these values should be ignored.
+ ctx.fontStretch = "ulTra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Extra-condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "cOndensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Semi-Condensed";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "normaL";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "semi-Expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "Expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "eXtra-expanded";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+
+ ctx.fontStretch = "abcd";
+ _assertSame(ctx.fontStretch, "ultra-expanded", "ctx.fontStretch", "\"ultra-expanded\"");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.html
new file mode 100644
index 0000000000..9756d095ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.fontVariant.settings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.fontVariant.settings</h1>
+<p class="desc">Testing basic functionalities of fontVariant for canvas</p>
+
+
+<script>
+var t = async_test("Testing basic functionalities of fontVariant for canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Setting fontVariantCaps with lower cases
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "normal";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "small-caps";
+ _assertSame(ctx.fontVariantCaps, "small-caps", "ctx.fontVariantCaps", "\"small-caps\"");
+
+ ctx.fontVariantCaps = "all-small-caps";
+ _assertSame(ctx.fontVariantCaps, "all-small-caps", "ctx.fontVariantCaps", "\"all-small-caps\"");
+
+ ctx.fontVariantCaps = "petite-caps";
+ _assertSame(ctx.fontVariantCaps, "petite-caps", "ctx.fontVariantCaps", "\"petite-caps\"");
+
+ ctx.fontVariantCaps = "all-petite-caps";
+ _assertSame(ctx.fontVariantCaps, "all-petite-caps", "ctx.fontVariantCaps", "\"all-petite-caps\"");
+
+ ctx.fontVariantCaps = "unicase";
+ _assertSame(ctx.fontVariantCaps, "unicase", "ctx.fontVariantCaps", "\"unicase\"");
+
+ ctx.fontVariantCaps = "titling-caps";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+
+ // Setting fontVariantCaps with mixed-case values is not valid
+ ctx.fontVariantCaps = "nORmal";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+
+ ctx.fontVariantCaps = "normal";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "smaLL-caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "all-small-CAPS";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "pEtitE-caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "All-Petite-Caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "uNIcase";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "titling-CAPS";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ // Setting fontVariantCaps with non-existing font variant.
+ ctx.fontVariantCaps = "titling-caps";
+ ctx.fontVariantCaps = "abcd";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.worker.js
new file mode 100644
index 0000000000..30c78388ae
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.fontVariant.settings.worker.js
@@ -0,0 +1,74 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.fontVariant.settings
+// Description:Testing basic functionalities of fontVariant for canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing basic functionalities of fontVariant for canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Setting fontVariantCaps with lower cases
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "normal";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "small-caps";
+ _assertSame(ctx.fontVariantCaps, "small-caps", "ctx.fontVariantCaps", "\"small-caps\"");
+
+ ctx.fontVariantCaps = "all-small-caps";
+ _assertSame(ctx.fontVariantCaps, "all-small-caps", "ctx.fontVariantCaps", "\"all-small-caps\"");
+
+ ctx.fontVariantCaps = "petite-caps";
+ _assertSame(ctx.fontVariantCaps, "petite-caps", "ctx.fontVariantCaps", "\"petite-caps\"");
+
+ ctx.fontVariantCaps = "all-petite-caps";
+ _assertSame(ctx.fontVariantCaps, "all-petite-caps", "ctx.fontVariantCaps", "\"all-petite-caps\"");
+
+ ctx.fontVariantCaps = "unicase";
+ _assertSame(ctx.fontVariantCaps, "unicase", "ctx.fontVariantCaps", "\"unicase\"");
+
+ ctx.fontVariantCaps = "titling-caps";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+
+ // Setting fontVariantCaps with mixed-case values is not valid
+ ctx.fontVariantCaps = "nORmal";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+
+ ctx.fontVariantCaps = "normal";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "smaLL-caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "all-small-CAPS";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "pEtitE-caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "All-Petite-Caps";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "uNIcase";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ ctx.fontVariantCaps = "titling-CAPS";
+ _assertSame(ctx.fontVariantCaps, "normal", "ctx.fontVariantCaps", "\"normal\"");
+
+ // Setting fontVariantCaps with non-existing font variant.
+ ctx.fontVariantCaps = "titling-caps";
+ ctx.fontVariantCaps = "abcd";
+ _assertSame(ctx.fontVariantCaps, "titling-caps", "ctx.fontVariantCaps", "\"titling-caps\"");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.html
new file mode 100644
index 0000000000..d16b175ad6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.invalid.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.invalid.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with invalid units</p>
+
+
+<script>
+var t = async_test("Testing letter spacing and word spacing with invalid units");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ }
+ test_word_spacing('0s');
+ test_word_spacing('1min');
+ test_word_spacing('1deg');
+ test_word_spacing('1pp');
+ test_word_spacing('initial');
+ test_word_spacing('inherit');
+ test_word_spacing('normal');
+ test_word_spacing('none');
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.worker.js
new file mode 100644
index 0000000000..06207b2c6c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.invalid.spacing.worker.js
@@ -0,0 +1,38 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.invalid.spacing
+// Description:Testing letter spacing and word spacing with invalid units
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing letter spacing and word spacing with invalid units");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ }
+ test_word_spacing('0s');
+ test_word_spacing('1min');
+ test_word_spacing('1deg');
+ test_word_spacing('1pp');
+ test_word_spacing('initial');
+ test_word_spacing('inherit');
+ test_word_spacing('normal');
+ test_word_spacing('none');
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.html
new file mode 100644
index 0000000000..f8b453418c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.letterSpacing.change.font</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.letterSpacing.change.font</h1>
+<p class="desc">Set letter spacing and word spacing to font dependent value and verify it works after font change.</p>
+
+
+<script>
+var t = async_test("Set letter spacing and word spacing to font dependent value and verify it works after font change.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ // Get the width for 'Hello World' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World').width;
+
+ ctx.letterSpacing = '1em';
+ _assertSame(ctx.letterSpacing, '1em', "ctx.letterSpacing", "'1em'");
+ // 1em = 10px. Add 10px after each letter in "Hello World",
+ // makes it 110px longer.
+ var width_with_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 110, 0.1, "letter-spacing error");
+
+ // Changing font to 20px. Without resetting the spacing, 1em letterSpacing
+ // is now 20px, so it's suppose to be 220px longer without any letterSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World').width;
+ // Now calculate the reference spacing for "Hello World" with no spacing.
+ ctx.letterSpacing = '0em';
+ width_normal = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 220, 0.1, "letter-spacing error after font change");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.worker.js
new file mode 100644
index 0000000000..510845c885
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.change.font.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.letterSpacing.change.font
+// Description:Set letter spacing and word spacing to font dependent value and verify it works after font change.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Set letter spacing and word spacing to font dependent value and verify it works after font change.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ // Get the width for 'Hello World' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World').width;
+
+ ctx.letterSpacing = '1em';
+ _assertSame(ctx.letterSpacing, '1em', "ctx.letterSpacing", "'1em'");
+ // 1em = 10px. Add 10px after each letter in "Hello World",
+ // makes it 110px longer.
+ var width_with_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 110, 0.1, "letter-spacing error");
+
+ // Changing font to 20px. Without resetting the spacing, 1em letterSpacing
+ // is now 20px, so it's suppose to be 220px longer without any letterSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World').width;
+ // Now calculate the reference spacing for "Hello World" with no spacing.
+ ctx.letterSpacing = '0em';
+ width_normal = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 220, 0.1, "letter-spacing error after font change");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.html
new file mode 100644
index 0000000000..a565770a16
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.letterSpacing.measure</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.letterSpacing.measure</h1>
+<p class="desc">Testing letter spacing with different length units</p>
+
+
+<script>
+var t = async_test("Testing letter spacing with different length units");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ var width_normal = ctx.measureText('Hello World').width;
+
+ function test_letter_spacing(value, difference_spacing, epsilon) {
+ ctx.letterSpacing = value;
+ _assertSame(ctx.letterSpacing, value, "ctx.letterSpacing", "value");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ width_with_letter_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_letter_spacing, width_normal + difference_spacing, epsilon, "letter spacing doesn't work.");
+ }
+
+ // The first value is the letter Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 11 letters
+ // in 'hello world', so the length difference is always letterSpacing * 11.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 33, 0.1],
+ ['5px', 55, 0.1],
+ ['-2px', -22, 0.1],
+ ['1em', 110, 0.1],
+ ['-0.1em', -11, 0.1],
+ ['1in', 1056, 0.1],
+ ['-0.1cm', -41.65, 0.2],
+ ['-0.6mm', -24,95, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_letter_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.worker.js
new file mode 100644
index 0000000000..630bf1d486
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.letterSpacing.measure.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.letterSpacing.measure
+// Description:Testing letter spacing with different length units
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing letter spacing with different length units");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ var width_normal = ctx.measureText('Hello World').width;
+
+ function test_letter_spacing(value, difference_spacing, epsilon) {
+ ctx.letterSpacing = value;
+ _assertSame(ctx.letterSpacing, value, "ctx.letterSpacing", "value");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ width_with_letter_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_letter_spacing, width_normal + difference_spacing, epsilon, "letter spacing doesn't work.");
+ }
+
+ // The first value is the letter Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 11 letters
+ // in 'hello world', so the length difference is always letterSpacing * 11.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 33, 0.1],
+ ['5px', 55, 0.1],
+ ['-2px', -22, 0.1],
+ ['1em', 110, 0.1],
+ ['-0.1em', -11, 0.1],
+ ['1in', 1056, 0.1],
+ ['-0.1cm', -41.65, 0.2],
+ ['-0.6mm', -24,95, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_letter_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.html
new file mode 100644
index 0000000000..6c0b3756d3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.measure.direction</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.measure.direction</h1>
+<p class="desc">Measurement should follow text direction</p>
+
+
+<script>
+var t = async_test("Measurement should follow text direction");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.direction = "ltr";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+ ctx.direction = "rtl";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.worker.js
new file mode 100644
index 0000000000..852f372101
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.direction.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.measure.direction
+// Description:Measurement should follow text direction
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Measurement should follow text direction");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.direction = "ltr";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+ ctx.direction = "rtl";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.html
new file mode 100644
index 0000000000..335014cfd6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.measure.rtl.text</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.measure.rtl.text</h1>
+<p class="desc">Measurement should follow canvas direction instead text direction</p>
+
+
+<script>
+var t = async_test("Measurement should follow canvas direction instead text direction");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ metrics = ctx.measureText('اَلْعَرَبÙيَّةÙ');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.worker.js
new file mode 100644
index 0000000000..09b0e9b8d0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.rtl.text.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.measure.rtl.text
+// Description:Measurement should follow canvas direction instead text direction
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Measurement should follow canvas direction instead text direction");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ metrics = ctx.measureText('اَلْعَرَبÙيَّةÙ');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.html
new file mode 100644
index 0000000000..4c1ca193e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.measure.textAlign</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.measure.textAlign</h1>
+<p class="desc">Measurement should be related to textAlignment</p>
+
+
+<script>
+var t = async_test("Measurement should be related to textAlignment");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textAlign = "right";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");
+
+ ctx.textAlign = "left"
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.worker.js
new file mode 100644
index 0000000000..bc6f167be5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.measure.textAlign.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.measure.textAlign
+// Description:Measurement should be related to textAlignment
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Measurement should be related to textAlignment");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.textAlign = "right";
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");
+
+ ctx.textAlign = "left"
+ metrics = ctx.measureText('hello');
+ _assert(metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.html
new file mode 100644
index 0000000000..48a592b839
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.nonfinite.spacing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.nonfinite.spacing</h1>
+<p class="desc">Testing letter spacing and word spacing with nonfinite inputs</p>
+
+
+<script>
+var t = async_test("Testing letter spacing and word spacing with nonfinite inputs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ }
+ test_word_spacing(NaN);
+ test_word_spacing(Infinity);
+ test_word_spacing(-Infinity);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.worker.js
new file mode 100644
index 0000000000..4cf20b28c0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.nonfinite.spacing.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.nonfinite.spacing
+// Description:Testing letter spacing and word spacing with nonfinite inputs
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing letter spacing and word spacing with nonfinite inputs");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ }
+ test_word_spacing(NaN);
+ test_word_spacing(Infinity);
+ test_word_spacing(-Infinity);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.html
new file mode 100644
index 0000000000..52b34deaab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.textRendering.settings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.textRendering.settings</h1>
+<p class="desc">Testing basic functionalities of textRendering in Canvas</p>
+
+
+<script>
+var t = async_test("Testing basic functionalities of textRendering in Canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Setting textRendering with correct case.
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizeSpeed";
+ _assertSame(ctx.textRendering, "optimizeSpeed", "ctx.textRendering", "\"optimizeSpeed\"");
+
+ ctx.textRendering = "optimizeLegibility";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "geometricPrecision";
+ _assertSame(ctx.textRendering, "geometricPrecision", "ctx.textRendering", "\"geometricPrecision\"");
+
+ ctx.textRendering = "auto";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ // Setting textRendering with incorrect case is ignored.
+ ctx.textRendering = "OPtimizeSpeed";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "OPtimizELEgibility";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "GeometricPrecision";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizespeed";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizelegibility";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "geometricprecision";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizeLegibility";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "AUTO";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "Auto";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ // Setting textRendering with non-existing font variant.
+ ctx.textRendering = "abcd";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "normal";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "auto";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.worker.js
new file mode 100644
index 0000000000..a4381963f7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.textRendering.settings.worker.js
@@ -0,0 +1,76 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.textRendering.settings
+// Description:Testing basic functionalities of textRendering in Canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing basic functionalities of textRendering in Canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Setting textRendering with correct case.
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizeSpeed";
+ _assertSame(ctx.textRendering, "optimizeSpeed", "ctx.textRendering", "\"optimizeSpeed\"");
+
+ ctx.textRendering = "optimizeLegibility";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "geometricPrecision";
+ _assertSame(ctx.textRendering, "geometricPrecision", "ctx.textRendering", "\"geometricPrecision\"");
+
+ ctx.textRendering = "auto";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ // Setting textRendering with incorrect case is ignored.
+ ctx.textRendering = "OPtimizeSpeed";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "OPtimizELEgibility";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "GeometricPrecision";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizespeed";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizelegibility";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "geometricprecision";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+
+ ctx.textRendering = "optimizeLegibility";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "AUTO";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "Auto";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ // Setting textRendering with non-existing font variant.
+ ctx.textRendering = "abcd";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "normal";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "";
+ _assertSame(ctx.textRendering, "optimizeLegibility", "ctx.textRendering", "\"optimizeLegibility\"");
+
+ ctx.textRendering = "auto";
+ _assertSame(ctx.textRendering, "auto", "ctx.textRendering", "\"auto\"");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.html
new file mode 100644
index 0000000000..3df660ea3e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.wordSpacing.change.font</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.wordSpacing.change.font</h1>
+<p class="desc">Set word spacing and word spacing to font dependent value and verify it works after font change.</p>
+
+
+<script>
+var t = async_test("Set word spacing and word spacing to font dependent value and verify it works after font change.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ // Get the width for 'Hello World, again' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ ctx.wordSpacing = '1em';
+ _assertSame(ctx.wordSpacing, '1em', "ctx.wordSpacing", "'1em'");
+ // 1em = 10px. Add 10px after each word in "Hello World, again",
+ // makes it 20px longer.
+ var width_with_spacing = ctx.measureText('Hello World, again').width;
+ _assertSame(width_with_spacing, width_normal + 20, "width_with_spacing", "width_normal + 20");
+
+ // Changing font to 20px. Without resetting the spacing, 1em wordSpacing
+ // is now 20px, so it's suppose to be 40px longer without any wordSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World, again').width;
+ // Now calculate the reference spacing for "Hello World, again" with no spacing.
+ ctx.wordSpacing = '0em';
+ width_normal = ctx.measureText('Hello World, again').width;
+ _assertSame(width_with_spacing, width_normal + 40, "width_with_spacing", "width_normal + 40");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.worker.js
new file mode 100644
index 0000000000..e1415ea287
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.change.font.worker.js
@@ -0,0 +1,41 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.wordSpacing.change.font
+// Description:Set word spacing and word spacing to font dependent value and verify it works after font change.
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Set word spacing and word spacing to font dependent value and verify it works after font change.");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ // Get the width for 'Hello World, again' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ ctx.wordSpacing = '1em';
+ _assertSame(ctx.wordSpacing, '1em', "ctx.wordSpacing", "'1em'");
+ // 1em = 10px. Add 10px after each word in "Hello World, again",
+ // makes it 20px longer.
+ var width_with_spacing = ctx.measureText('Hello World, again').width;
+ _assertSame(width_with_spacing, width_normal + 20, "width_with_spacing", "width_normal + 20");
+
+ // Changing font to 20px. Without resetting the spacing, 1em wordSpacing
+ // is now 20px, so it's suppose to be 40px longer without any wordSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World, again').width;
+ // Now calculate the reference spacing for "Hello World, again" with no spacing.
+ ctx.wordSpacing = '0em';
+ width_normal = ctx.measureText('Hello World, again').width;
+ _assertSame(width_with_spacing, width_normal + 40, "width_with_spacing", "width_normal + 40");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.html
new file mode 100644
index 0000000000..7a1d867d0f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.drawing.style.wordSpacing.measure</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.drawing.style.wordSpacing.measure</h1>
+<p class="desc">Testing word spacing with different length units</p>
+
+
+<script>
+var t = async_test("Testing word spacing with different length units");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ function test_word_spacing(value, difference_spacing, epsilon) {
+ ctx.wordSpacing = value;
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, value, "ctx.wordSpacing", "value");
+ width_with_word_spacing = ctx.measureText('Hello World, again').width;
+ assert_approx_equals(width_with_word_spacing, width_normal + difference_spacing, epsilon, "word spacing doesn't work.");
+ }
+
+ // The first value is the word Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 2 words
+ // in 'Hello World, again', so the length difference is always wordSpacing * 2.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 6, 0.1],
+ ['5px', 10, 0.1],
+ ['-2px', -4, 0.1],
+ ['1em', 20, 0.1],
+ ['-0.5em', -10, 0.1],
+ ['1in', 192, 0.1],
+ ['-0.1cm', -7.57, 0.2],
+ ['-0.6mm', -4.54, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_word_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.worker.js
new file mode 100644
index 0000000000..b7acecb0eb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.drawing.style.wordSpacing.measure.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.drawing.style.wordSpacing.measure
+// Description:Testing word spacing with different length units
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing word spacing with different length units");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, '0px', "ctx.wordSpacing", "'0px'");
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ function test_word_spacing(value, difference_spacing, epsilon) {
+ ctx.wordSpacing = value;
+ _assertSame(ctx.letterSpacing, '0px', "ctx.letterSpacing", "'0px'");
+ _assertSame(ctx.wordSpacing, value, "ctx.wordSpacing", "value");
+ width_with_word_spacing = ctx.measureText('Hello World, again').width;
+ assert_approx_equals(width_with_word_spacing, width_normal + difference_spacing, epsilon, "word spacing doesn't work.");
+ }
+
+ // The first value is the word Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 2 words
+ // in 'Hello World, again', so the length difference is always wordSpacing * 2.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 6, 0.1],
+ ['5px', 10, 0.1],
+ ['-2px', -4, 0.1],
+ ['1em', 20, 0.1],
+ ['-0.5em', -10, 0.1],
+ ['1in', 192, 0.1],
+ ['-0.1cm', -7.57, 0.2],
+ ['-0.6mm', -4.54, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_word_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.html
new file mode 100644
index 0000000000..59828a5db4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.default</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.font, '10px sans-serif', "ctx.font", "'10px sans-serif'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.worker.js
new file mode 100644
index 0000000000..fa51ffc871
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.default.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.default
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ _assertSame(ctx.font, '10px sans-serif', "ctx.font", "'10px sans-serif'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.html
new file mode 100644
index 0000000000..bb39cc4a3c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.parse.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.parse.basic</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '20px serif';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20PX SERIF';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.worker.js
new file mode 100644
index 0000000000..3596aab66b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.basic.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.parse.basic
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '20px serif';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20PX SERIF';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.html
new file mode 100644
index 0000000000..69453437fe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.parse.complex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.parse.complex</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'small-caps italic 400 12px/2 Unknown Font, sans-serif';
+ _assert(['italic small-caps 12px "Unknown Font", sans-serif', 'italic small-caps 12px Unknown Font, sans-serif'].includes(ctx.font), "['italic small-caps 12px \"Unknown Font\", sans-serif', 'italic small-caps 12px Unknown Font, sans-serif'].includes(ctx.font)");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.worker.js
new file mode 100644
index 0000000000..71d039cfac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.parse.complex
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'small-caps italic 400 12px/2 Unknown Font, sans-serif';
+ _assert(['italic small-caps 12px "Unknown Font", sans-serif', 'italic small-caps 12px Unknown Font, sans-serif'].includes(ctx.font), "['italic small-caps 12px \"Unknown Font\", sans-serif', 'italic small-caps 12px Unknown Font, sans-serif'].includes(ctx.font)");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.html
new file mode 100644
index 0000000000..af94ac720a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.parse.complex2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.parse.complex2</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'small-caps italic 400 12px/2 "Unknown Font #2", sans-serif';
+ _assertSame(ctx.font, 'italic small-caps 12px "Unknown Font #2", sans-serif', "ctx.font", "'italic small-caps 12px \"Unknown Font #2\", sans-serif'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.worker.js
new file mode 100644
index 0000000000..45575f8814
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.complex2.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.parse.complex2
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'small-caps italic 400 12px/2 "Unknown Font #2", sans-serif';
+ _assertSame(ctx.font, 'italic small-caps 12px "Unknown Font #2", sans-serif', "ctx.font", "'italic small-caps 12px \"Unknown Font #2\", sans-serif'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.html
new file mode 100644
index 0000000000..989014fb37
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.parse.family</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.parse.family</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '20px cursive,fantasy,monospace,sans-serif,serif,UnquotedFont,"QuotedFont\\\\\\","';
+ _assertSame(ctx.font, '20px cursive, fantasy, monospace, sans-serif, serif, UnquotedFont, "QuotedFont\\\\\\","', "ctx.font", "'20px cursive, fantasy, monospace, sans-serif, serif, UnquotedFont, \"QuotedFont\\\\\\\\\\\\\",\"'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.worker.js
new file mode 100644
index 0000000000..68aefdd0df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.family.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.parse.family
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '20px cursive,fantasy,monospace,sans-serif,serif,UnquotedFont,"QuotedFont\\\\\\","';
+ _assertSame(ctx.font, '20px cursive, fantasy, monospace, sans-serif, serif, UnquotedFont, "QuotedFont\\\\\\","', "ctx.font", "'20px cursive, fantasy, monospace, sans-serif, serif, UnquotedFont, \"QuotedFont\\\\\\\\\\\\\",\"'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.html
new file mode 100644
index 0000000000..8b4fd01d02
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.parse.invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.parse.invalid</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '20px serif';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'bogus';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'inherit';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px {bogus}';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px initial';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px default';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px inherit';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px revert';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x)';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x, 10px serif)';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '1em serif; background: green; margin: 10px';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.worker.js
new file mode 100644
index 0000000000..31f374579e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.invalid.worker.js
@@ -0,0 +1,67 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.parse.invalid
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '20px serif';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'bogus';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'inherit';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px {bogus}';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px initial';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px default';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px inherit';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '10px revert';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x)';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x, 10px serif)';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+
+ ctx.font = '20px serif';
+ ctx.font = '1em serif; background: green; margin: 10px';
+ _assertSame(ctx.font, '20px serif', "ctx.font", "'20px serif'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.html
new file mode 100644
index 0000000000..79ae6a38e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.parse.system</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.parse.system</h1>
+<p class="desc">System fonts must be computed to explicit values</p>
+
+
+<script>
+var t = async_test("System fonts must be computed to explicit values");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'message-box';
+ _assertDifferent(ctx.font, 'message-box', "ctx.font", "'message-box'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js
new file mode 100644
index 0000000000..b965ea763a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.parse.system
+// Description:System fonts must be computed to explicit values
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("System fonts must be computed to explicit values");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'message-box';
+ _assertDifferent(ctx.font, 'message-box', "ctx.font", "'message-box'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.html
new file mode 100644
index 0000000000..89f8515967
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.parse.tiny</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.parse.tiny</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '1px sans-serif';
+ _assertSame(ctx.font, '1px sans-serif', "ctx.font", "'1px sans-serif'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js
new file mode 100644
index 0000000000..aba023e336
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.parse.tiny
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '1px sans-serif';
+ _assertSame(ctx.font, '1px sans-serif', "ctx.font", "'1px sans-serif'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.html
new file mode 100644
index 0000000000..a32be3d65c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.relative_size</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.relative_size</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '1em sans-serif';
+ _assertSame(ctx.font, '10px sans-serif', "ctx.font", "'10px sans-serif'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js
new file mode 100644
index 0000000000..9e1247cfe0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js
@@ -0,0 +1,23 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.relative_size
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = '1em sans-serif';
+ _assertSame(ctx.font, '10px sans-serif', "ctx.font", "'10px sans-serif'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.html
new file mode 100644
index 0000000000..cac9c369b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.font.weight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.font.weight</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'italic 400 12px serif';
+ _assertSame(ctx.font, 'italic 12px serif', "ctx.font", "'italic 12px serif'");
+
+ ctx.font = 'italic 300 12px serif';
+ _assertSame(ctx.font, 'italic 300 12px serif', "ctx.font", "'italic 300 12px serif'");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.worker.js
new file mode 100644
index 0000000000..8f653c3656
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.font.weight.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.font.weight
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = 'italic 400 12px serif';
+ _assertSame(ctx.font, 'italic 12px serif', "ctx.font", "'italic 12px serif'");
+
+ ctx.font = 'italic 300 12px serif';
+ _assertSame(ctx.font, 'italic 300 12px serif', "ctx.font", "'italic 300 12px serif'");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1-expected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1-expected.html
new file mode 100644
index 0000000000..e2cef0d77a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps1</title>
+<h1>2d.text.fontVariantCaps1</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.html
new file mode 100644
index 0000000000..3c216f07da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps1-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps1</title>
+<h1>2d.text.fontVariantCaps1</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "small-caps";
+ // This should render the same as font = "small-caps 32px serif".
+ ctx.fillText("Hello World", 20, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.w.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.w.html
new file mode 100644
index 0000000000..4bc1b36e17
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps1.w.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.text.fontVariantCaps1-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps1</title>
+<h1>2d.text.fontVariantCaps1</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "small-caps";
+ // This should render the same as font = "small-caps 32px serif".
+ ctx.fillText("Hello World", 20, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2-unexpected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2-unexpected.html
new file mode 100644
index 0000000000..dbcb8abd76
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2-unexpected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML OffscreenCanvas reference</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+
+<canvas id="c1"></canvas>
+<br>
+<canvas id="c2"></canvas>
+
+<script>
+for (let c = 1; c <= 2; ++c) {
+ let ctx = document.getElementById("c" + c).getContext("2d");
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.html
new file mode 100644
index 0000000000..e5bcff1831
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.fontVariantCaps2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.fontVariantCaps2</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+
+
+<script>
+var t = async_test("Testing small caps setting in fontVariant");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // "mismatch" test, to verify that small-caps does change the rendering.
+ smallCaps_len = ctx.measureText("Hello World").width;
+
+ ctx.font = "32px serif";
+ normalCaps_len = ctx.measureText("Hello World").width;
+ _assert(smallCaps_len != normalCaps_len, "smallCaps_len != normalCaps_len");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.worker.js
new file mode 100644
index 0000000000..89f4f48c73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps2.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.fontVariantCaps2
+// Description:Testing small caps setting in fontVariant
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Testing small caps setting in fontVariant");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // "mismatch" test, to verify that small-caps does change the rendering.
+ smallCaps_len = ctx.measureText("Hello World").width;
+
+ ctx.font = "32px serif";
+ normalCaps_len = ctx.measureText("Hello World").width;
+ _assert(smallCaps_len != normalCaps_len, "smallCaps_len != normalCaps_len");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3-expected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3-expected.html
new file mode 100644
index 0000000000..cf2d5ae119
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps3</title>
+<h1>2d.text.fontVariantCaps3</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("hello world", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.html
new file mode 100644
index 0000000000..48699a640f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps3-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps3</title>
+<h1>2d.text.fontVariantCaps3</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "all-small-caps";
+ // This should render the same as using font = "small-caps 32px serif"
+ // with all the underlying text in lowercase.
+ ctx.fillText("Hello World", 20, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.w.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.w.html
new file mode 100644
index 0000000000..cd5c1db818
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps3.w.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.text.fontVariantCaps3-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps3</title>
+<h1>2d.text.fontVariantCaps3</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "all-small-caps";
+ // This should render the same as using font = "small-caps 32px serif"
+ // with all the underlying text in lowercase.
+ ctx.fillText("Hello World", 20, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4-expected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4-expected.html
new file mode 100644
index 0000000000..3813fd3684
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps4</title>
+<h1>2d.text.fontVariantCaps4</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("hello world", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.html
new file mode 100644
index 0000000000..b1b81b81e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps4-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps4</title>
+<h1>2d.text.fontVariantCaps4</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps overrides the small-caps setting from the font attribute
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.fillText("Hello World", 20, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.w.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.w.html
new file mode 100644
index 0000000000..0bae66fcd4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps4.w.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.text.fontVariantCaps4-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps4</title>
+<h1>2d.text.fontVariantCaps4</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps overrides the small-caps setting from the font attribute
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.fillText("Hello World", 20, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5-expected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5-expected.html
new file mode 100644
index 0000000000..4bda4ec4b5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps5</title>
+<h1>2d.text.fontVariantCaps5</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.html
new file mode 100644
index 0000000000..2a6f7b5f73
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps5-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps5</title>
+<h1>2d.text.fontVariantCaps5</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps 'normal' does not override the setting from the font attribute.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "normal";
+ ctx.fillText("Hello World", 20, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.w.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.w.html
new file mode 100644
index 0000000000..8c59f5b517
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps5.w.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.text.fontVariantCaps5-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps5</title>
+<h1>2d.text.fontVariantCaps5</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps 'normal' does not override the setting from the font attribute.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "normal";
+ ctx.fillText("Hello World", 20, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6-expected.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6-expected.html
new file mode 100644
index 0000000000..af9c736aea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.text.fontVariantCaps6</title>
+<h1>2d.text.fontVariantCaps6</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.html
new file mode 100644
index 0000000000..c33684d388
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.text.fontVariantCaps6-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps6</title>
+<h1>2d.text.fontVariantCaps6</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ // fontVariantCaps is reset when the font attribute is set.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.w.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.w.html
new file mode 100644
index 0000000000..47f70d812b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.fontVariantCaps6.w.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.text.fontVariantCaps6-expected.html">
+<title>Canvas test: 2d.text.fontVariantCaps6</title>
+<h1>2d.text.fontVariantCaps6</h1>
+<p class="desc">Testing small caps setting in fontVariant</p>
+<canvas id="canvas" width="100" height="50">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(100, 50);
+ const ctx = canvas.getContext('2d');
+
+ // fontVariantCaps is reset when the font attribute is set.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.html
new file mode 100644
index 0000000000..105efc0794
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.actualBoundingBox</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.actualBoundingBox</h1>
+<p class="desc">Testing actualBoundingBox</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ ctx.baseline = 'alphabetic'
+ // Different platforms may render text slightly different.
+ // Values that are nominally expected to be zero might actually vary by a
+ // pixel or so if the UA accounts for antialiasing at glyph edges, so we
+ // allow a slight deviation.
+ _assert(Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1");
+ _assert(ctx.measureText('A').actualBoundingBoxRight >= 50, "ctx.measureText('A').actualBoundingBoxRight >= 50");
+ _assert(ctx.measureText('A').actualBoundingBoxAscent >= 35, "ctx.measureText('A').actualBoundingBoxAscent >= 35");
+ _assert(Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1");
+
+ _assert(ctx.measureText('D').actualBoundingBoxLeft >= 48, "ctx.measureText('D').actualBoundingBoxLeft >= 48");
+ _assert(ctx.measureText('D').actualBoundingBoxLeft <= 52, "ctx.measureText('D').actualBoundingBoxLeft <= 52");
+ _assert(ctx.measureText('D').actualBoundingBoxRight >= 75, "ctx.measureText('D').actualBoundingBoxRight >= 75");
+ _assert(ctx.measureText('D').actualBoundingBoxRight <= 80, "ctx.measureText('D').actualBoundingBoxRight <= 80");
+ _assert(ctx.measureText('D').actualBoundingBoxAscent >= 35, "ctx.measureText('D').actualBoundingBoxAscent >= 35");
+ _assert(ctx.measureText('D').actualBoundingBoxAscent <= 40, "ctx.measureText('D').actualBoundingBoxAscent <= 40");
+ _assert(ctx.measureText('D').actualBoundingBoxDescent >= 12, "ctx.measureText('D').actualBoundingBoxDescent >= 12");
+ _assert(ctx.measureText('D').actualBoundingBoxDescent <= 15, "ctx.measureText('D').actualBoundingBoxDescent <= 15");
+
+ _assert(Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37");
+
+}, "Testing actualBoundingBox");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js
new file mode 100644
index 0000000000..c3d18ff878
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js
@@ -0,0 +1,46 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.actualBoundingBox
+// Description:Testing actualBoundingBox
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ ctx.baseline = 'alphabetic'
+ // Different platforms may render text slightly different.
+ // Values that are nominally expected to be zero might actually vary by a
+ // pixel or so if the UA accounts for antialiasing at glyph edges, so we
+ // allow a slight deviation.
+ _assert(Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1");
+ _assert(ctx.measureText('A').actualBoundingBoxRight >= 50, "ctx.measureText('A').actualBoundingBoxRight >= 50");
+ _assert(ctx.measureText('A').actualBoundingBoxAscent >= 35, "ctx.measureText('A').actualBoundingBoxAscent >= 35");
+ _assert(Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1, "Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1");
+
+ _assert(ctx.measureText('D').actualBoundingBoxLeft >= 48, "ctx.measureText('D').actualBoundingBoxLeft >= 48");
+ _assert(ctx.measureText('D').actualBoundingBoxLeft <= 52, "ctx.measureText('D').actualBoundingBoxLeft <= 52");
+ _assert(ctx.measureText('D').actualBoundingBoxRight >= 75, "ctx.measureText('D').actualBoundingBoxRight >= 75");
+ _assert(ctx.measureText('D').actualBoundingBoxRight <= 80, "ctx.measureText('D').actualBoundingBoxRight <= 80");
+ _assert(ctx.measureText('D').actualBoundingBoxAscent >= 35, "ctx.measureText('D').actualBoundingBoxAscent >= 35");
+ _assert(ctx.measureText('D').actualBoundingBoxAscent <= 40, "ctx.measureText('D').actualBoundingBoxAscent <= 40");
+ _assert(ctx.measureText('D').actualBoundingBoxDescent >= 12, "ctx.measureText('D').actualBoundingBoxDescent >= 12");
+ _assert(ctx.measureText('D').actualBoundingBoxDescent <= 15, "ctx.measureText('D').actualBoundingBoxDescent <= 15");
+
+ _assert(Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1, "Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85");
+ _assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37");
+ t.done();
+}, "Testing actualBoundingBox");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.html
new file mode 100644
index 0000000000..2eec005cb8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.advances</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.advances</h1>
+<p class="desc">Testing width advances</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ // Some platforms may return '-0'.
+ _assertSame(Math.abs(ctx.measureText('Hello').advances[0]), 0, "Math.abs(ctx.measureText('Hello').advances[\""+(0)+"\"])", "0");
+ // Different platforms may render text slightly different.
+ _assert(ctx.measureText('Hello').advances[1] >= 36, "ctx.measureText('Hello').advances[\""+(1)+"\"] >= 36");
+ _assert(ctx.measureText('Hello').advances[2] >= 58, "ctx.measureText('Hello').advances[\""+(2)+"\"] >= 58");
+ _assert(ctx.measureText('Hello').advances[3] >= 70, "ctx.measureText('Hello').advances[\""+(3)+"\"] >= 70");
+ _assert(ctx.measureText('Hello').advances[4] >= 80, "ctx.measureText('Hello').advances[\""+(4)+"\"] >= 80");
+
+ var tm = ctx.measureText('Hello');
+ _assertSame(ctx.measureText('Hello').advances[0], tm.advances[0], "ctx.measureText('Hello').advances[\""+(0)+"\"]", "tm.advances[\""+(0)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[1], tm.advances[1], "ctx.measureText('Hello').advances[\""+(1)+"\"]", "tm.advances[\""+(1)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[2], tm.advances[2], "ctx.measureText('Hello').advances[\""+(2)+"\"]", "tm.advances[\""+(2)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[3], tm.advances[3], "ctx.measureText('Hello').advances[\""+(3)+"\"]", "tm.advances[\""+(3)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[4], tm.advances[4], "ctx.measureText('Hello').advances[\""+(4)+"\"]", "tm.advances[\""+(4)+"\"]");
+
+}, "Testing width advances");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js
new file mode 100644
index 0000000000..4b02f06f2d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.advances
+// Description:Testing width advances
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ // Some platforms may return '-0'.
+ _assertSame(Math.abs(ctx.measureText('Hello').advances[0]), 0, "Math.abs(ctx.measureText('Hello').advances[\""+(0)+"\"])", "0");
+ // Different platforms may render text slightly different.
+ _assert(ctx.measureText('Hello').advances[1] >= 36, "ctx.measureText('Hello').advances[\""+(1)+"\"] >= 36");
+ _assert(ctx.measureText('Hello').advances[2] >= 58, "ctx.measureText('Hello').advances[\""+(2)+"\"] >= 58");
+ _assert(ctx.measureText('Hello').advances[3] >= 70, "ctx.measureText('Hello').advances[\""+(3)+"\"] >= 70");
+ _assert(ctx.measureText('Hello').advances[4] >= 80, "ctx.measureText('Hello').advances[\""+(4)+"\"] >= 80");
+
+ var tm = ctx.measureText('Hello');
+ _assertSame(ctx.measureText('Hello').advances[0], tm.advances[0], "ctx.measureText('Hello').advances[\""+(0)+"\"]", "tm.advances[\""+(0)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[1], tm.advances[1], "ctx.measureText('Hello').advances[\""+(1)+"\"]", "tm.advances[\""+(1)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[2], tm.advances[2], "ctx.measureText('Hello').advances[\""+(2)+"\"]", "tm.advances[\""+(2)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[3], tm.advances[3], "ctx.measureText('Hello').advances[\""+(3)+"\"]", "tm.advances[\""+(3)+"\"]");
+ _assertSame(ctx.measureText('Hello').advances[4], tm.advances[4], "ctx.measureText('Hello').advances[\""+(4)+"\"]", "tm.advances[\""+(4)+"\"]");
+ t.done();
+}, "Testing width advances");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.html
new file mode 100644
index 0000000000..9d0cc1268d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.baselines</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.baselines</h1>
+<p class="desc">Testing baselines</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(Math.abs(ctx.measureText('A').alphabeticBaseline), 0, "Math.abs(ctx.measureText('A').alphabeticBaseline)", "0");
+ _assertSame(ctx.measureText('A').ideographicBaseline, 6.25, "ctx.measureText('A').ideographicBaseline", "6.25");
+ _assertSame(ctx.measureText('A').hangingBaseline, 25, "ctx.measureText('A').hangingBaseline", "25");
+
+ _assertSame(Math.abs(ctx.measureText('ABCD').alphabeticBaseline), 0, "Math.abs(ctx.measureText('ABCD').alphabeticBaseline)", "0");
+ _assertSame(ctx.measureText('ABCD').ideographicBaseline, 6.25, "ctx.measureText('ABCD').ideographicBaseline", "6.25");
+ _assertSame(ctx.measureText('ABCD').hangingBaseline, 25, "ctx.measureText('ABCD').hangingBaseline", "25");
+
+}, "Testing baselines");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js
new file mode 100644
index 0000000000..ddb48f4fe0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.baselines
+// Description:Testing baselines
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(Math.abs(ctx.measureText('A').alphabeticBaseline), 0, "Math.abs(ctx.measureText('A').alphabeticBaseline)", "0");
+ _assertSame(ctx.measureText('A').ideographicBaseline, 6.25, "ctx.measureText('A').ideographicBaseline", "6.25");
+ _assertSame(ctx.measureText('A').hangingBaseline, 25, "ctx.measureText('A').hangingBaseline", "25");
+
+ _assertSame(Math.abs(ctx.measureText('ABCD').alphabeticBaseline), 0, "Math.abs(ctx.measureText('ABCD').alphabeticBaseline)", "0");
+ _assertSame(ctx.measureText('ABCD').ideographicBaseline, 6.25, "ctx.measureText('ABCD').ideographicBaseline", "6.25");
+ _assertSame(ctx.measureText('ABCD').hangingBaseline, 25, "ctx.measureText('ABCD').hangingBaseline", "25");
+ t.done();
+}, "Testing baselines");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.html
new file mode 100644
index 0000000000..2e2e5dde67
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.emHeights-low-ascent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.emHeights-low-ascent</h1>
+<p class="desc">Testing emHeights with reduced ascent metric</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-ascent256", "url('/fonts/CanvasTest-ascent256.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 20, "ctx.measureText('A').emHeightAscent", "20");
+ _assertSame(ctx.measureText('A').emHeightDescent, 20, "ctx.measureText('A').emHeightDescent", "20");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 20, "ctx.measureText('ABCD').emHeightAscent", "20");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 20, "ctx.measureText('ABCD').emHeightDescent", "20");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+
+}, "Testing emHeights with reduced ascent metric");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js
new file mode 100644
index 0000000000..09ee787309
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.emHeights-low-ascent
+// Description:Testing emHeights with reduced ascent metric
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-ascent256", "url('/fonts/CanvasTest-ascent256.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 20, "ctx.measureText('A').emHeightAscent", "20");
+ _assertSame(ctx.measureText('A').emHeightDescent, 20, "ctx.measureText('A').emHeightDescent", "20");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 20, "ctx.measureText('ABCD').emHeightAscent", "20");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 20, "ctx.measureText('ABCD').emHeightDescent", "20");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+ t.done();
+}, "Testing emHeights with reduced ascent metric");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.html
new file mode 100644
index 0000000000..e39c08ce82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.emHeights-zero-descent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.emHeights-zero-descent</h1>
+<p class="desc">Testing emHeights with zero descent metric</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-descent0", "url('/fonts/CanvasTest-descent0.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightAscent", "40");
+ _assertSame(ctx.measureText('A').emHeightDescent, 0, "ctx.measureText('A').emHeightDescent", "0");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightAscent", "40");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 0, "ctx.measureText('ABCD').emHeightDescent", "0");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+
+}, "Testing emHeights with zero descent metric");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js
new file mode 100644
index 0000000000..a2f09782c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.emHeights-zero-descent
+// Description:Testing emHeights with zero descent metric
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-descent0", "url('/fonts/CanvasTest-descent0.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightAscent", "40");
+ _assertSame(ctx.measureText('A').emHeightDescent, 0, "ctx.measureText('A').emHeightDescent", "0");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightAscent", "40");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 0, "ctx.measureText('ABCD').emHeightDescent", "0");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+ t.done();
+}, "Testing emHeights with zero descent metric");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.html
new file mode 100644
index 0000000000..0764279df3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.emHeights</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.emHeights</h1>
+<p class="desc">Testing emHeights</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 30, "ctx.measureText('A').emHeightAscent", "30");
+ _assertSame(ctx.measureText('A').emHeightDescent, 10, "ctx.measureText('A').emHeightDescent", "10");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 30, "ctx.measureText('ABCD').emHeightAscent", "30");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 10, "ctx.measureText('ABCD').emHeightDescent", "10");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+
+}, "Testing emHeights");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js
new file mode 100644
index 0000000000..0385d3d737
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.emHeights
+// Description:Testing emHeights
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').emHeightAscent, 30, "ctx.measureText('A').emHeightAscent", "30");
+ _assertSame(ctx.measureText('A').emHeightDescent, 10, "ctx.measureText('A').emHeightDescent", "10");
+ _assertSame(ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent, 40, "ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent", "40");
+
+ _assertSame(ctx.measureText('ABCD').emHeightAscent, 30, "ctx.measureText('ABCD').emHeightAscent", "30");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent, 10, "ctx.measureText('ABCD').emHeightDescent", "10");
+ _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
+ t.done();
+}, "Testing emHeights");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.html
new file mode 100644
index 0000000000..51eb2c822c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.fontBoundingBox-reduced-ascent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.fontBoundingBox-reduced-ascent</h1>
+<p class="desc">Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-ascent256", "url('/fonts/CanvasTest-ascent256.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 10, "ctx.measureText('A').fontBoundingBoxAscent", "10");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 10, "ctx.measureText('ABCD').fontBoundingBoxAscent", "10");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+
+}, "Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js
new file mode 100644
index 0000000000..dea67ff8f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.fontBoundingBox-reduced-ascent
+// Description:Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-ascent256", "url('/fonts/CanvasTest-ascent256.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 10, "ctx.measureText('A').fontBoundingBoxAscent", "10");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 10, "ctx.measureText('ABCD').fontBoundingBoxAscent", "10");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+ t.done();
+}, "Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.html
new file mode 100644
index 0000000000..ea65e58b5c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.fontBoundingBox-zero-descent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.fontBoundingBox-zero-descent</h1>
+<p class="desc">Testing fontBoundingBox for OffscreenCanvas with zero descent metric</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-descent0", "url('/fonts/CanvasTest-descent0.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 30, "ctx.measureText('A').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 0, "ctx.measureText('A').fontBoundingBoxDescent", "0");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 0, "ctx.measureText('ABCD').fontBoundingBoxDescent", "0");
+
+}, "Testing fontBoundingBox for OffscreenCanvas with zero descent metric");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js
new file mode 100644
index 0000000000..99a886d015
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.fontBoundingBox-zero-descent
+// Description:Testing fontBoundingBox for OffscreenCanvas with zero descent metric
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest-descent0", "url('/fonts/CanvasTest-descent0.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 30, "ctx.measureText('A').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 0, "ctx.measureText('A').fontBoundingBoxDescent", "0");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 0, "ctx.measureText('ABCD').fontBoundingBoxDescent", "0");
+ t.done();
+}, "Testing fontBoundingBox for OffscreenCanvas with zero descent metric");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.html
new file mode 100644
index 0000000000..cb8b0c558d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.fontBoundingBox.ahem</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.fontBoundingBox.ahem</h1>
+<p class="desc">Testing fontBoundingBox for font ahem</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("Ahem", "url('/fonts/Ahem.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px Ahem';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 40, "ctx.measureText('A').fontBoundingBoxAscent", "40");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 40, "ctx.measureText('ABCD').fontBoundingBoxAscent", "40");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+
+}, "Testing fontBoundingBox for font ahem");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js
new file mode 100644
index 0000000000..255bcd108a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.fontBoundingBox.ahem
+// Description:Testing fontBoundingBox for font ahem
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("Ahem", "url('/fonts/Ahem.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px Ahem';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 40, "ctx.measureText('A').fontBoundingBoxAscent", "40");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 40, "ctx.measureText('ABCD').fontBoundingBoxAscent", "40");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+ t.done();
+}, "Testing fontBoundingBox for font ahem");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.html
new file mode 100644
index 0000000000..36b11f31d7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.fontBoundingBox</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.fontBoundingBox</h1>
+<p class="desc">Testing fontBoundingBox measurements</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 30, "ctx.measureText('A').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+
+}, "Testing fontBoundingBox measurements");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js
new file mode 100644
index 0000000000..b2cde04c0d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.fontBoundingBox
+// Description:Testing fontBoundingBox measurements
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ _assertSame(ctx.measureText('A').fontBoundingBoxAscent, 30, "ctx.measureText('A').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
+
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
+ _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
+ t.done();
+}, "Testing fontBoundingBox measurements");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.html
new file mode 100644
index 0000000000..fea1904083
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.width.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.width.basic</h1>
+<p class="desc">The width of character is same as font used</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText('A').width, 50, "ctx.measureText('A').width", "50");
+ _assertSame(ctx.measureText('AA').width, 100, "ctx.measureText('AA').width", "100");
+ _assertSame(ctx.measureText('ABCD').width, 200, "ctx.measureText('ABCD').width", "200");
+
+ ctx.font = '100px CanvasTest';
+ _assertSame(ctx.measureText('A').width, 100, "ctx.measureText('A').width", "100");
+
+}, "The width of character is same as font used");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js
new file mode 100644
index 0000000000..866ad8b3c5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js
@@ -0,0 +1,27 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.width.basic
+// Description:The width of character is same as font used
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText('A').width, 50, "ctx.measureText('A').width", "50");
+ _assertSame(ctx.measureText('AA').width, 100, "ctx.measureText('AA').width", "100");
+ _assertSame(ctx.measureText('ABCD').width, 200, "ctx.measureText('ABCD').width", "200");
+
+ ctx.font = '100px CanvasTest';
+ _assertSame(ctx.measureText('A').width, 100, "ctx.measureText('A').width", "100");
+ t.done();
+}, "The width of character is same as font used");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.html
new file mode 100644
index 0000000000..11bd029433
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.width.empty</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.width.empty</h1>
+<p class="desc">The empty string has zero width</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText("").width, 0, "ctx.measureText(\"\").width", "0");
+
+}, "The empty string has zero width");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js
new file mode 100644
index 0000000000..6fb89c5ba7
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.width.empty
+// Description:The empty string has zero width
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText("").width, 0, "ctx.measureText(\"\").width", "0");
+ t.done();
+}, "The empty string has zero width");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.html
new file mode 100644
index 0000000000..38d5084298
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.measure.width.space</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.measure.width.space</h1>
+<p class="desc">Space characters are converted to U+0020 and NOT collapsed</p>
+
+
+<script>
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ document.fonts.add(f);
+ await document.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText('A B').width, 150, "ctx.measureText('A B').width", "150");
+ _assertSame(ctx.measureText('A B').width, 200, "ctx.measureText('A B').width", "200");
+ _assertSame(ctx.measureText('A \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dB').width, 650, "ctx.measureText('A \\x09\\x0a\\x0c\\x0d \\x09\\x0a\\x0c\\x0dB').width", "650");
+ _assert(ctx.measureText('A \x0b B').width >= 200, "ctx.measureText('A \\x0b B').width >= 200");
+
+ _assertSame(ctx.measureText(' AB').width, 150, "ctx.measureText(' AB').width", "150");
+ _assertSame(ctx.measureText('AB ').width, 150, "ctx.measureText('AB ').width", "150");
+
+}, "Space characters are converted to U+0020 and NOT collapsed");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js
new file mode 100644
index 0000000000..d832feec5f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.measure.width.space
+// Description:Space characters are converted to U+0020 and NOT collapsed
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+promise_test(async t => {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var f = new FontFace("CanvasTest", "url('/fonts/CanvasTest.ttf')");
+ f.load();
+ self.fonts.add(f);
+ await self.fonts.ready;
+ ctx.font = '50px CanvasTest';
+ _assertSame(ctx.measureText('A B').width, 150, "ctx.measureText('A B').width", "150");
+ _assertSame(ctx.measureText('A B').width, 200, "ctx.measureText('A B').width", "200");
+ _assertSame(ctx.measureText('A \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dB').width, 650, "ctx.measureText('A \\x09\\x0a\\x0c\\x0d \\x09\\x0a\\x0c\\x0dB').width", "650");
+ _assert(ctx.measureText('A \x0b B').width >= 200, "ctx.measureText('A \\x0b B').width >= 200");
+
+ _assertSame(ctx.measureText(' AB').width, 150, "ctx.measureText(' AB').width", "150");
+ _assertSame(ctx.measureText('AB ').width, 150, "ctx.measureText('AB ').width", "150");
+ t.done();
+}, "Space characters are converted to U+0020 and NOT collapsed");
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.html b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.html
new file mode 100644
index 0000000000..89d8fe3c7a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.text.setFont.mathFont</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.text.setFont.mathFont</h1>
+<p class="desc">crbug.com/1212190, make sure offscreencanvas doesn't crash with Math Font</p>
+
+
+<script>
+var t = async_test("crbug.com/1212190, make sure offscreencanvas doesn't crash with Math Font");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = "math serif";
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.worker.js
new file mode 100644
index 0000000000..b756b45dc5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.setFont.mathFont.worker.js
@@ -0,0 +1,22 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.text.setFont.mathFont
+// Description:crbug.com/1212190, make sure offscreencanvas doesn't crash with Math Font
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("crbug.com/1212190, make sure offscreencanvas doesn't crash with Math Font");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.font = "math serif";
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch-ref.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch-ref.html
new file mode 100644
index 0000000000..00ecdccad3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = document.getElementById("c");
+var ctx = canvas.getContext('2d');
+
+function draw() {
+ ctx.font = '25px test';
+ ctx.fillText("P", 10, 40);
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+document.fonts.add(f);
+
+f.load().then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html
new file mode 100644
index 0000000000..0cbd5e3c1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "condensed";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html
new file mode 100644
index 0000000000..5ec853985f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "expanded";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html
new file mode 100644
index 0000000000..12843c53b8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = extra-condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "extra-condensed";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "extra-condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html
new file mode 100644
index 0000000000..20d801718a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = extra-expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "extra-expanded";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "extra-expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html
new file mode 100644
index 0000000000..786cb408e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = normal and shows as fail for
+// fontStretch = expanded or condensed.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/fail.woff)');
+f.stretch = "expanded";
+document.fonts.add(f);
+
+var f1 = new FontFace('test', 'url(/fonts/pass.woff)');
+document.fonts.add(f1);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+f2.stretch = "condensed";
+document.fonts.add(f2);
+
+Promise.all([f.load(), f1.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html
new file mode 100644
index 0000000000..04bd5a80d0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = semi-condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "semi-condensed";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "semi-condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html
new file mode 100644
index 0000000000..d86b730687
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = semi-expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "semi-expanded";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "semi-expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html
new file mode 100644
index 0000000000..d78f6d0078
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = ultra-condensed and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "ultra-condensed";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "ultra-condensed";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html
new file mode 100644
index 0000000000..8b8f6f5698
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Canvas test: 2d.text.fontStretch</title>
+<link rel="match" href="canvas.2d.fontStretch-ref.html">
+<canvas id="c" class="output"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+var canvas = new OffscreenCanvas(300, 150);
+var ctx = canvas.getContext('2d');
+
+// P shows as Pass for fontStretch = ultra-expanded and shows as fail for
+// fontStretch = fail.
+function draw() {
+ ctx.font = '25px test';
+ ctx.fontStretch = "ultra-expanded";
+ ctx.fillText("P", 10, 40);
+ document.getElementById('c')
+ .getContext("bitmaprenderer")
+ .transferFromImageBitmap(canvas.transferToImageBitmap());
+}
+
+var f = new FontFace('test', 'url(/fonts/pass.woff)');
+f.stretch = "ultra-expanded";
+document.fonts.add(f);
+
+var f2 = new FontFace('test', 'url(/fonts/fail.woff)');
+document.fonts.add(f2);
+
+Promise.all([f.load(), f2.load()]).then(draw);
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.html
new file mode 100644
index 0000000000..208043fbf6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.bitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.bitmap</h1>
+<p class="desc">save()/restore() does not affect the current bitmap</p>
+
+
+<script>
+var t = async_test("save()/restore() does not affect the current bitmap");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.worker.js
new file mode 100644
index 0000000000..09c7dcca90
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.bitmap.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.bitmap
+// Description:save()/restore() does not affect the current bitmap
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() does not affect the current bitmap");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.html
new file mode 100644
index 0000000000..f4d6b6f6e1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.clip</h1>
+<p class="desc">save()/restore() affects the clipping path</p>
+
+
+<script>
+var t = async_test("save()/restore() affects the clipping path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 1, 1);
+ ctx.clip();
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.worker.js
new file mode 100644
index 0000000000..a7bc0615e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.clip.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.clip
+// Description:save()/restore() affects the clipping path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() affects the clipping path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 1, 1);
+ ctx.clip();
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.html
new file mode 100644
index 0000000000..789e1e8d2b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.fillStyle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.fillStyle</h1>
+<p class="desc">save()/restore() works for fillStyle</p>
+
+
+<script>
+var t = async_test("save()/restore() works for fillStyle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.fillStyle;
+ ctx.save();
+ ctx.fillStyle = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.fillStyle, old, "ctx.fillStyle", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.fillStyle = "#ff0000";
+ old = ctx.fillStyle;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.fillStyle, old, "ctx.fillStyle", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.worker.js
new file mode 100644
index 0000000000..46cf6d1daa
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.fillStyle.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.fillStyle
+// Description:save()/restore() works for fillStyle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for fillStyle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.fillStyle;
+ ctx.save();
+ ctx.fillStyle = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.fillStyle, old, "ctx.fillStyle", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.fillStyle = "#ff0000";
+ old = ctx.fillStyle;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.fillStyle, old, "ctx.fillStyle", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.html
new file mode 100644
index 0000000000..15b59725ab
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.globalAlpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.globalAlpha</h1>
+<p class="desc">save()/restore() works for globalAlpha</p>
+
+
+<script>
+var t = async_test("save()/restore() works for globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.globalAlpha;
+ ctx.save();
+ ctx.globalAlpha = 0.5;
+ ctx.restore();
+ _assertSame(ctx.globalAlpha, old, "ctx.globalAlpha", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.globalAlpha = 0.5;
+ old = ctx.globalAlpha;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.globalAlpha, old, "ctx.globalAlpha", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.worker.js
new file mode 100644
index 0000000000..765f8b00a3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalAlpha.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.globalAlpha
+// Description:save()/restore() works for globalAlpha
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for globalAlpha");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.globalAlpha;
+ ctx.save();
+ ctx.globalAlpha = 0.5;
+ ctx.restore();
+ _assertSame(ctx.globalAlpha, old, "ctx.globalAlpha", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.globalAlpha = 0.5;
+ old = ctx.globalAlpha;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.globalAlpha, old, "ctx.globalAlpha", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html
new file mode 100644
index 0000000000..1efb24aa9b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.globalCompositeOperation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.globalCompositeOperation</h1>
+<p class="desc">save()/restore() works for globalCompositeOperation</p>
+
+
+<script>
+var t = async_test("save()/restore() works for globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.globalCompositeOperation;
+ ctx.save();
+ ctx.globalCompositeOperation = "copy";
+ ctx.restore();
+ _assertSame(ctx.globalCompositeOperation, old, "ctx.globalCompositeOperation", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.globalCompositeOperation = "copy";
+ old = ctx.globalCompositeOperation;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "copy"
+ ctx.save();
+ _assertSame(ctx.globalCompositeOperation, old, "ctx.globalCompositeOperation", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.worker.js
new file mode 100644
index 0000000000..cd38d56c51
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.globalCompositeOperation.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.globalCompositeOperation
+// Description:save()/restore() works for globalCompositeOperation
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for globalCompositeOperation");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.globalCompositeOperation;
+ ctx.save();
+ ctx.globalCompositeOperation = "copy";
+ ctx.restore();
+ _assertSame(ctx.globalCompositeOperation, old, "ctx.globalCompositeOperation", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.globalCompositeOperation = "copy";
+ old = ctx.globalCompositeOperation;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "copy"
+ ctx.save();
+ _assertSame(ctx.globalCompositeOperation, old, "ctx.globalCompositeOperation", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.html
new file mode 100644
index 0000000000..5e651d0c7b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.lineCap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.lineCap</h1>
+<p class="desc">save()/restore() works for lineCap</p>
+
+
+<script>
+var t = async_test("save()/restore() works for lineCap");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineCap;
+ ctx.save();
+ ctx.lineCap = "round";
+ ctx.restore();
+ _assertSame(ctx.lineCap, old, "ctx.lineCap", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineCap = "round";
+ old = ctx.lineCap;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "round"
+ ctx.save();
+ _assertSame(ctx.lineCap, old, "ctx.lineCap", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.worker.js
new file mode 100644
index 0000000000..9895b44b00
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineCap.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.lineCap
+// Description:save()/restore() works for lineCap
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for lineCap");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineCap;
+ ctx.save();
+ ctx.lineCap = "round";
+ ctx.restore();
+ _assertSame(ctx.lineCap, old, "ctx.lineCap", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineCap = "round";
+ old = ctx.lineCap;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "round"
+ ctx.save();
+ _assertSame(ctx.lineCap, old, "ctx.lineCap", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.html
new file mode 100644
index 0000000000..20f53fec50
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.lineJoin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.lineJoin</h1>
+<p class="desc">save()/restore() works for lineJoin</p>
+
+
+<script>
+var t = async_test("save()/restore() works for lineJoin");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineJoin;
+ ctx.save();
+ ctx.lineJoin = "round";
+ ctx.restore();
+ _assertSame(ctx.lineJoin, old, "ctx.lineJoin", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineJoin = "round";
+ old = ctx.lineJoin;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "round"
+ ctx.save();
+ _assertSame(ctx.lineJoin, old, "ctx.lineJoin", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.worker.js
new file mode 100644
index 0000000000..e8c85825f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineJoin.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.lineJoin
+// Description:save()/restore() works for lineJoin
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for lineJoin");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineJoin;
+ ctx.save();
+ ctx.lineJoin = "round";
+ ctx.restore();
+ _assertSame(ctx.lineJoin, old, "ctx.lineJoin", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineJoin = "round";
+ old = ctx.lineJoin;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "round"
+ ctx.save();
+ _assertSame(ctx.lineJoin, old, "ctx.lineJoin", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.html
new file mode 100644
index 0000000000..e234e3efc2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.lineWidth</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.lineWidth</h1>
+<p class="desc">save()/restore() works for lineWidth</p>
+
+
+<script>
+var t = async_test("save()/restore() works for lineWidth");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineWidth;
+ ctx.save();
+ ctx.lineWidth = 0.5;
+ ctx.restore();
+ _assertSame(ctx.lineWidth, old, "ctx.lineWidth", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineWidth = 0.5;
+ old = ctx.lineWidth;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.lineWidth, old, "ctx.lineWidth", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.worker.js
new file mode 100644
index 0000000000..997748c377
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.lineWidth.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.lineWidth
+// Description:save()/restore() works for lineWidth
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for lineWidth");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.lineWidth;
+ ctx.save();
+ ctx.lineWidth = 0.5;
+ ctx.restore();
+ _assertSame(ctx.lineWidth, old, "ctx.lineWidth", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.lineWidth = 0.5;
+ old = ctx.lineWidth;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.lineWidth, old, "ctx.lineWidth", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.html
new file mode 100644
index 0000000000..1df2b2420f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.miterLimit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.miterLimit</h1>
+<p class="desc">save()/restore() works for miterLimit</p>
+
+
+<script>
+var t = async_test("save()/restore() works for miterLimit");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.miterLimit;
+ ctx.save();
+ ctx.miterLimit = 0.5;
+ ctx.restore();
+ _assertSame(ctx.miterLimit, old, "ctx.miterLimit", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.miterLimit = 0.5;
+ old = ctx.miterLimit;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.miterLimit, old, "ctx.miterLimit", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.worker.js
new file mode 100644
index 0000000000..eb5adffe06
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.miterLimit.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.miterLimit
+// Description:save()/restore() works for miterLimit
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for miterLimit");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.miterLimit;
+ ctx.save();
+ ctx.miterLimit = 0.5;
+ ctx.restore();
+ _assertSame(ctx.miterLimit, old, "ctx.miterLimit", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.miterLimit = 0.5;
+ old = ctx.miterLimit;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 0.5
+ ctx.save();
+ _assertSame(ctx.miterLimit, old, "ctx.miterLimit", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.html
new file mode 100644
index 0000000000..a86851af68
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.path</h1>
+<p class="desc">save()/restore() does not affect the current path</p>
+
+
+<script>
+var t = async_test("save()/restore() does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 100, 50);
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.worker.js
new file mode 100644
index 0000000000..d174de5a50
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.path.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.path
+// Description:save()/restore() does not affect the current path
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() does not affect the current path");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 100, 50);
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.html
new file mode 100644
index 0000000000..044ee1b4bb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.shadowBlur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.shadowBlur</h1>
+<p class="desc">save()/restore() works for shadowBlur</p>
+
+
+<script>
+var t = async_test("save()/restore() works for shadowBlur");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowBlur;
+ ctx.save();
+ ctx.shadowBlur = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowBlur, old, "ctx.shadowBlur", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowBlur = 5;
+ old = ctx.shadowBlur;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowBlur, old, "ctx.shadowBlur", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.worker.js
new file mode 100644
index 0000000000..00cd6f8317
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowBlur.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.shadowBlur
+// Description:save()/restore() works for shadowBlur
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for shadowBlur");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowBlur;
+ ctx.save();
+ ctx.shadowBlur = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowBlur, old, "ctx.shadowBlur", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowBlur = 5;
+ old = ctx.shadowBlur;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowBlur, old, "ctx.shadowBlur", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.html
new file mode 100644
index 0000000000..0e8b4293e0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.shadowColor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.shadowColor</h1>
+<p class="desc">save()/restore() works for shadowColor</p>
+
+
+<script>
+var t = async_test("save()/restore() works for shadowColor");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowColor;
+ ctx.save();
+ ctx.shadowColor = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.shadowColor, old, "ctx.shadowColor", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowColor = "#ff0000";
+ old = ctx.shadowColor;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.shadowColor, old, "ctx.shadowColor", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.worker.js
new file mode 100644
index 0000000000..13c033f396
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowColor.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.shadowColor
+// Description:save()/restore() works for shadowColor
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for shadowColor");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowColor;
+ ctx.save();
+ ctx.shadowColor = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.shadowColor, old, "ctx.shadowColor", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowColor = "#ff0000";
+ old = ctx.shadowColor;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.shadowColor, old, "ctx.shadowColor", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.html
new file mode 100644
index 0000000000..11aa658f3e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.shadowOffsetX</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.shadowOffsetX</h1>
+<p class="desc">save()/restore() works for shadowOffsetX</p>
+
+
+<script>
+var t = async_test("save()/restore() works for shadowOffsetX");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowOffsetX;
+ ctx.save();
+ ctx.shadowOffsetX = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowOffsetX, old, "ctx.shadowOffsetX", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowOffsetX = 5;
+ old = ctx.shadowOffsetX;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowOffsetX, old, "ctx.shadowOffsetX", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.worker.js
new file mode 100644
index 0000000000..a6ad922647
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetX.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.shadowOffsetX
+// Description:save()/restore() works for shadowOffsetX
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for shadowOffsetX");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowOffsetX;
+ ctx.save();
+ ctx.shadowOffsetX = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowOffsetX, old, "ctx.shadowOffsetX", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowOffsetX = 5;
+ old = ctx.shadowOffsetX;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowOffsetX, old, "ctx.shadowOffsetX", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.html
new file mode 100644
index 0000000000..c238f0ec94
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.shadowOffsetY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.shadowOffsetY</h1>
+<p class="desc">save()/restore() works for shadowOffsetY</p>
+
+
+<script>
+var t = async_test("save()/restore() works for shadowOffsetY");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowOffsetY;
+ ctx.save();
+ ctx.shadowOffsetY = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowOffsetY, old, "ctx.shadowOffsetY", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowOffsetY = 5;
+ old = ctx.shadowOffsetY;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowOffsetY, old, "ctx.shadowOffsetY", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.worker.js
new file mode 100644
index 0000000000..323fcd1434
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.shadowOffsetY.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.shadowOffsetY
+// Description:save()/restore() works for shadowOffsetY
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for shadowOffsetY");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.shadowOffsetY;
+ ctx.save();
+ ctx.shadowOffsetY = 5;
+ ctx.restore();
+ _assertSame(ctx.shadowOffsetY, old, "ctx.shadowOffsetY", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.shadowOffsetY = 5;
+ old = ctx.shadowOffsetY;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against 5
+ ctx.save();
+ _assertSame(ctx.shadowOffsetY, old, "ctx.shadowOffsetY", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.html
new file mode 100644
index 0000000000..4a8750867f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.stack</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.stack</h1>
+<p class="desc">save()/restore() can be nested as a stack</p>
+
+
+<script>
+var t = async_test("save()/restore() can be nested as a stack");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 1;
+ ctx.save();
+ ctx.lineWidth = 2;
+ ctx.save();
+ ctx.lineWidth = 3;
+ _assertSame(ctx.lineWidth, 3, "ctx.lineWidth", "3");
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 2, "ctx.lineWidth", "2");
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.worker.js
new file mode 100644
index 0000000000..460df4ec83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stack.worker.js
@@ -0,0 +1,31 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.stack
+// Description:save()/restore() can be nested as a stack
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() can be nested as a stack");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.lineWidth = 1;
+ ctx.save();
+ ctx.lineWidth = 2;
+ ctx.save();
+ ctx.lineWidth = 3;
+ _assertSame(ctx.lineWidth, 3, "ctx.lineWidth", "3");
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 2, "ctx.lineWidth", "2");
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 1, "ctx.lineWidth", "1");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.html
new file mode 100644
index 0000000000..d536155e64
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.stackdepth</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.stackdepth</h1>
+<p class="desc">save()/restore() stack depth is not unreasonably limited</p>
+
+
+<script>
+var t = async_test("save()/restore() stack depth is not unreasonably limited");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var limit = 512;
+ for (var i = 1; i < limit; ++i)
+ {
+ ctx.save();
+ ctx.lineWidth = i;
+ }
+ for (var i = limit-1; i > 0; --i)
+ {
+ _assertSame(ctx.lineWidth, i, "ctx.lineWidth", "i");
+ ctx.restore();
+ }
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.worker.js
new file mode 100644
index 0000000000..c494d9ddf8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.stackdepth.worker.js
@@ -0,0 +1,32 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.stackdepth
+// Description:save()/restore() stack depth is not unreasonably limited
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() stack depth is not unreasonably limited");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ var limit = 512;
+ for (var i = 1; i < limit; ++i)
+ {
+ ctx.save();
+ ctx.lineWidth = i;
+ }
+ for (var i = limit-1; i > 0; --i)
+ {
+ _assertSame(ctx.lineWidth, i, "ctx.lineWidth", "i");
+ ctx.restore();
+ }
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.html
new file mode 100644
index 0000000000..561205acd9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.strokeStyle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.strokeStyle</h1>
+<p class="desc">save()/restore() works for strokeStyle</p>
+
+
+<script>
+var t = async_test("save()/restore() works for strokeStyle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.strokeStyle;
+ ctx.save();
+ ctx.strokeStyle = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.strokeStyle, old, "ctx.strokeStyle", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.strokeStyle = "#ff0000";
+ old = ctx.strokeStyle;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.strokeStyle, old, "ctx.strokeStyle", "old");
+ ctx.restore();
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.worker.js
new file mode 100644
index 0000000000..878a6d68cb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.strokeStyle.worker.js
@@ -0,0 +1,36 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.strokeStyle
+// Description:save()/restore() works for strokeStyle
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() works for strokeStyle");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Test that restore() undoes any modifications
+ var old = ctx.strokeStyle;
+ ctx.save();
+ ctx.strokeStyle = "#ff0000";
+ ctx.restore();
+ _assertSame(ctx.strokeStyle, old, "ctx.strokeStyle", "old");
+
+ // Also test that save() doesn't modify the values
+ ctx.strokeStyle = "#ff0000";
+ old = ctx.strokeStyle;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against "#ff0000"
+ ctx.save();
+ _assertSame(ctx.strokeStyle, old, "ctx.strokeStyle", "old");
+ ctx.restore();
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.html
new file mode 100644
index 0000000000..79ecdece53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.transformation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.transformation</h1>
+<p class="desc">save()/restore() affects the current transformation matrix</p>
+
+
+<script>
+var t = async_test("save()/restore() affects the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.translate(200, 0);
+ ctx.restore();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(-200, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.worker.js
new file mode 100644
index 0000000000..1647dfb790
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.transformation.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.transformation
+// Description:save()/restore() affects the current transformation matrix
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("save()/restore() affects the current transformation matrix");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.translate(200, 0);
+ ctx.restore();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(-200, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.html b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.html
new file mode 100644
index 0000000000..a6713adde2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.state.saverestore.underflow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.state.saverestore.underflow</h1>
+<p class="desc">restore() with an empty stack has no effect</p>
+
+
+<script>
+var t = async_test("restore() with an empty stack has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ for (var i = 0; i < 16; ++i)
+ ctx.restore();
+ ctx.lineWidth = 0.5;
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 0.5, "ctx.lineWidth", "0.5");
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.worker.js b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.worker.js
new file mode 100644
index 0000000000..8ed4bcbc10
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/the-canvas-state/2d.state.saverestore.underflow.worker.js
@@ -0,0 +1,26 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.state.saverestore.underflow
+// Description:restore() with an empty stack has no effect
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("restore() with an empty stack has no effect");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ for (var i = 0; i < 16; ++i)
+ ctx.restore();
+ ctx.lineWidth = 0.5;
+ ctx.restore();
+ _assertSame(ctx.lineWidth, 0.5, "ctx.lineWidth", "0.5");
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.html
new file mode 100644
index 0000000000..d438c27433
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.order</h1>
+<p class="desc">Transformations are applied in the right order</p>
+
+
+<script>
+var t = async_test("Transformations are applied in the right order");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 1);
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -50, 50, 50);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.worker.js
new file mode 100644
index 0000000000..219cbe8421
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.order.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.order
+// Description:Transformations are applied in the right order
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Transformations are applied in the right order");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 1);
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -50, 50, 50);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.html
new file mode 100644
index 0000000000..fb0f295062
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.rotate.direction</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.rotate.direction</h1>
+<p class="desc">rotate() is clockwise</p>
+
+
+<script>
+var t = async_test("rotate() is clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -100, 50, 100);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.worker.js
new file mode 100644
index 0000000000..b92b7d8368
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.direction.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.rotate.direction
+// Description:rotate() is clockwise
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("rotate() is clockwise");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -100, 50, 100);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.html
new file mode 100644
index 0000000000..dbde38370d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.rotate.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.rotate.nonfinite</h1>
+<p class="desc">rotate() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("rotate() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.rotate(Infinity);
+ ctx.rotate(-Infinity);
+ ctx.rotate(NaN);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.worker.js
new file mode 100644
index 0000000000..52b6605e45
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.nonfinite.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.rotate.nonfinite
+// Description:rotate() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("rotate() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.rotate(Infinity);
+ ctx.rotate(-Infinity);
+ ctx.rotate(NaN);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.html
new file mode 100644
index 0000000000..28006028ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.rotate.radians</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.rotate.radians</h1>
+<p class="desc">rotate() uses radians</p>
+
+
+<script>
+var t = async_test("rotate() uses radians");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI); // should fail obviously if this is 3.1 degrees
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.worker.js
new file mode 100644
index 0000000000..0851691156
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.radians.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.rotate.radians
+// Description:rotate() uses radians
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("rotate() uses radians");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI); // should fail obviously if this is 3.1 degrees
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.html
new file mode 100644
index 0000000000..aea14ceddf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.rotate.wrap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.rotate.wrap</h1>
+<p class="desc">rotate() wraps large positive values correctly</p>
+
+
+<script>
+var t = async_test("rotate() wraps large positive values correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI * (1 + 4096)); // == pi (mod 2*pi)
+ // We need about pi +/- 0.001 in order to get correct-looking results
+ // 32-bit floats can store pi*4097 with precision 2^-10, so that should
+ // be safe enough on reasonable implementations
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,2, 0,255,0,255);
+ _assertPixel(canvas, 98,47, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.worker.js
new file mode 100644
index 0000000000..4d0c2f5b5d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrap.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.rotate.wrap
+// Description:rotate() wraps large positive values correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("rotate() wraps large positive values correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI * (1 + 4096)); // == pi (mod 2*pi)
+ // We need about pi +/- 0.001 in order to get correct-looking results
+ // 32-bit floats can store pi*4097 with precision 2^-10, so that should
+ // be safe enough on reasonable implementations
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,2, 0,255,0,255);
+ _assertPixel(canvas, 98,47, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.html
new file mode 100644
index 0000000000..7363007a4d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.rotate.wrapnegative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.rotate.wrapnegative</h1>
+<p class="desc">rotate() wraps large negative values correctly</p>
+
+
+<script>
+var t = async_test("rotate() wraps large negative values correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(-Math.PI * (1 + 4096));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,2, 0,255,0,255);
+ _assertPixel(canvas, 98,47, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.worker.js
new file mode 100644
index 0000000000..932ecf6512
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.wrapnegative.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.rotate.wrapnegative
+// Description:rotate() wraps large negative values correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("rotate() wraps large negative values correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(-Math.PI * (1 + 4096));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ _assertPixel(canvas, 98,2, 0,255,0,255);
+ _assertPixel(canvas, 98,47, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.html
new file mode 100644
index 0000000000..965d0e0768
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.rotate.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.rotate.zero</h1>
+<p class="desc">rotate() by 0 does nothing</p>
+
+
+<script>
+var t = async_test("rotate() by 0 does nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.worker.js
new file mode 100644
index 0000000000..659c9fd7a0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.rotate.zero.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.rotate.zero
+// Description:rotate() by 0 does nothing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("rotate() by 0 does nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.html
new file mode 100644
index 0000000000..d4063f7129
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.scale.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.scale.basic</h1>
+<p class="desc">scale() works</p>
+
+
+<script>
+var t = async_test("scale() works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 4);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 12.5);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.worker.js
new file mode 100644
index 0000000000..5e79daf3f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.basic.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.scale.basic
+// Description:scale() works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("scale() works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 4);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 12.5);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.html
new file mode 100644
index 0000000000..ae8f954a71
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.scale.large</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.scale.large</h1>
+<p class="desc">scale() with large scale factors works</p>
+
+<p class="notes">Not really that large at all, but it hits the limits in Firefox.
+<script>
+var t = async_test("scale() with large scale factors works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(1e5, 1e5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 1, 1);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.worker.js
new file mode 100644
index 0000000000..6d71627728
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.large.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.scale.large
+// Description:scale() with large scale factors works
+// Note:<p class="notes">Not really that large at all, but it hits the limits in Firefox.
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("scale() with large scale factors works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(1e5, 1e5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 1, 1);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.html
new file mode 100644
index 0000000000..6a899d1dd6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.scale.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.scale.multiple</h1>
+<p class="desc">Multiple scale()s combine</p>
+
+
+<script>
+var t = async_test("Multiple scale()s combine");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.worker.js
new file mode 100644
index 0000000000..921e95aa72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.multiple.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.scale.multiple
+// Description:Multiple scale()s combine
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("Multiple scale()s combine");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.html
new file mode 100644
index 0000000000..68c5780332
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.scale.negative</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.scale.negative</h1>
+<p class="desc">scale() with negative scale factors works</p>
+
+
+<script>
+var t = async_test("scale() with negative scale factors works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.scale(-1, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.scale(1, -1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, -50, 50, 50);
+ ctx.restore();
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.worker.js
new file mode 100644
index 0000000000..cb0f01c618
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.negative.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.scale.negative
+// Description:scale() with negative scale factors works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("scale() with negative scale factors works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.scale(-1, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.scale(1, -1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, -50, 50, 50);
+ ctx.restore();
+ _assertPixel(canvas, 25,25, 0,255,0,255);
+ _assertPixel(canvas, 75,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.html
new file mode 100644
index 0000000000..5fd95629c9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.scale.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.scale.nonfinite</h1>
+<p class="desc">scale() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("scale() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.scale(Infinity, 0.1);
+ ctx.scale(-Infinity, 0.1);
+ ctx.scale(NaN, 0.1);
+ ctx.scale(0.1, Infinity);
+ ctx.scale(0.1, -Infinity);
+ ctx.scale(0.1, NaN);
+ ctx.scale(Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.worker.js
new file mode 100644
index 0000000000..35f9c90e71
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.nonfinite.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.scale.nonfinite
+// Description:scale() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("scale() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.scale(Infinity, 0.1);
+ ctx.scale(-Infinity, 0.1);
+ ctx.scale(NaN, 0.1);
+ ctx.scale(0.1, Infinity);
+ ctx.scale(0.1, -Infinity);
+ ctx.scale(0.1, NaN);
+ ctx.scale(Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.html
new file mode 100644
index 0000000000..b4269bd140
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.scale.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.scale.zero</h1>
+<p class="desc">scale() with a scale factor of zero works</p>
+
+
+<script>
+var t = async_test("scale() with a scale factor of zero works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.scale(0, 1);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.translate(0, 25);
+ ctx.scale(1, 0);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.worker.js
new file mode 100644
index 0000000000..9e90127cfe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.scale.zero.worker.js
@@ -0,0 +1,39 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.scale.zero
+// Description:scale() with a scale factor of zero works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("scale() with a scale factor of zero works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.scale(0, 1);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.translate(0, 25);
+ ctx.scale(1, 0);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.html
new file mode 100644
index 0000000000..0395c561bf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.setTransform.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.setTransform.multiple</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.setTransform(1/2,0, 0,1/2, 0,0);
+ ctx.setTransform();
+ ctx.setTransform(2,0, 0,2, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ _assertPixel(canvas, 75,35, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.worker.js
new file mode 100644
index 0000000000..e3e6daf878
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.multiple.worker.js
@@ -0,0 +1,30 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.setTransform.multiple
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.setTransform(1/2,0, 0,1/2, 0,0);
+ ctx.setTransform();
+ ctx.setTransform(2,0, 0,2, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ _assertPixel(canvas, 75,35, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.html
new file mode 100644
index 0000000000..474b20a10b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.setTransform.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.setTransform.nonfinite</h1>
+<p class="desc">setTransform() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("setTransform() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.setTransform(Infinity, 0, 0, 0, 0, 0);
+ ctx.setTransform(-Infinity, 0, 0, 0, 0, 0);
+ ctx.setTransform(NaN, 0, 0, 0, 0, 0);
+ ctx.setTransform(0, Infinity, 0, 0, 0, 0);
+ ctx.setTransform(0, -Infinity, 0, 0, 0, 0);
+ ctx.setTransform(0, NaN, 0, 0, 0, 0);
+ ctx.setTransform(0, 0, Infinity, 0, 0, 0);
+ ctx.setTransform(0, 0, -Infinity, 0, 0, 0);
+ ctx.setTransform(0, 0, NaN, 0, 0, 0);
+ ctx.setTransform(0, 0, 0, Infinity, 0, 0);
+ ctx.setTransform(0, 0, 0, -Infinity, 0, 0);
+ ctx.setTransform(0, 0, 0, NaN, 0, 0);
+ ctx.setTransform(0, 0, 0, 0, Infinity, 0);
+ ctx.setTransform(0, 0, 0, 0, -Infinity, 0);
+ ctx.setTransform(0, 0, 0, 0, NaN, 0);
+ ctx.setTransform(0, 0, 0, 0, 0, Infinity);
+ ctx.setTransform(0, 0, 0, 0, 0, -Infinity);
+ ctx.setTransform(0, 0, 0, 0, 0, NaN);
+ ctx.setTransform(Infinity, Infinity, 0, 0, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, 0, 0);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, 0, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, 0, 0, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, 0, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, 0, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, Infinity, 0);
+ ctx.setTransform(Infinity, 0, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, 0, Infinity);
+ ctx.setTransform(Infinity, 0, 0, Infinity, 0, 0);
+ ctx.setTransform(Infinity, 0, 0, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, 0, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, 0, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, 0, 0, 0, Infinity, 0);
+ ctx.setTransform(Infinity, 0, 0, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, 0, 0, 0, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, 0, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, 0, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, Infinity, 0);
+ ctx.setTransform(0, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, 0, Infinity);
+ ctx.setTransform(0, Infinity, 0, Infinity, 0, 0);
+ ctx.setTransform(0, Infinity, 0, Infinity, Infinity, 0);
+ ctx.setTransform(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.setTransform(0, Infinity, 0, 0, Infinity, 0);
+ ctx.setTransform(0, Infinity, 0, 0, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, 0, 0, 0, Infinity);
+ ctx.setTransform(0, 0, Infinity, Infinity, 0, 0);
+ ctx.setTransform(0, 0, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(0, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, 0, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(0, 0, Infinity, 0, Infinity, 0);
+ ctx.setTransform(0, 0, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(0, 0, Infinity, 0, 0, Infinity);
+ ctx.setTransform(0, 0, 0, Infinity, Infinity, 0);
+ ctx.setTransform(0, 0, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, 0, 0, Infinity, 0, Infinity);
+ ctx.setTransform(0, 0, 0, 0, Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.worker.js
new file mode 100644
index 0000000000..d99ea52dea
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.nonfinite.worker.js
@@ -0,0 +1,105 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.setTransform.nonfinite
+// Description:setTransform() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("setTransform() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.setTransform(Infinity, 0, 0, 0, 0, 0);
+ ctx.setTransform(-Infinity, 0, 0, 0, 0, 0);
+ ctx.setTransform(NaN, 0, 0, 0, 0, 0);
+ ctx.setTransform(0, Infinity, 0, 0, 0, 0);
+ ctx.setTransform(0, -Infinity, 0, 0, 0, 0);
+ ctx.setTransform(0, NaN, 0, 0, 0, 0);
+ ctx.setTransform(0, 0, Infinity, 0, 0, 0);
+ ctx.setTransform(0, 0, -Infinity, 0, 0, 0);
+ ctx.setTransform(0, 0, NaN, 0, 0, 0);
+ ctx.setTransform(0, 0, 0, Infinity, 0, 0);
+ ctx.setTransform(0, 0, 0, -Infinity, 0, 0);
+ ctx.setTransform(0, 0, 0, NaN, 0, 0);
+ ctx.setTransform(0, 0, 0, 0, Infinity, 0);
+ ctx.setTransform(0, 0, 0, 0, -Infinity, 0);
+ ctx.setTransform(0, 0, 0, 0, NaN, 0);
+ ctx.setTransform(0, 0, 0, 0, 0, Infinity);
+ ctx.setTransform(0, 0, 0, 0, 0, -Infinity);
+ ctx.setTransform(0, 0, 0, 0, 0, NaN);
+ ctx.setTransform(Infinity, Infinity, 0, 0, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, 0, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, Infinity, 0, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, 0, 0);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, 0, Infinity, 0);
+ ctx.setTransform(Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, Infinity, 0, 0, 0, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, 0, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, 0, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, Infinity, 0);
+ ctx.setTransform(Infinity, 0, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, Infinity, 0, 0, Infinity);
+ ctx.setTransform(Infinity, 0, 0, Infinity, 0, 0);
+ ctx.setTransform(Infinity, 0, 0, Infinity, Infinity, 0);
+ ctx.setTransform(Infinity, 0, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, 0, Infinity, 0, Infinity);
+ ctx.setTransform(Infinity, 0, 0, 0, Infinity, 0);
+ ctx.setTransform(Infinity, 0, 0, 0, Infinity, Infinity);
+ ctx.setTransform(Infinity, 0, 0, 0, 0, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, 0, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, 0, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, Infinity, 0);
+ ctx.setTransform(0, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, Infinity, 0, 0, Infinity);
+ ctx.setTransform(0, Infinity, 0, Infinity, 0, 0);
+ ctx.setTransform(0, Infinity, 0, Infinity, Infinity, 0);
+ ctx.setTransform(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.setTransform(0, Infinity, 0, 0, Infinity, 0);
+ ctx.setTransform(0, Infinity, 0, 0, Infinity, Infinity);
+ ctx.setTransform(0, Infinity, 0, 0, 0, Infinity);
+ ctx.setTransform(0, 0, Infinity, Infinity, 0, 0);
+ ctx.setTransform(0, 0, Infinity, Infinity, Infinity, 0);
+ ctx.setTransform(0, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, 0, Infinity, Infinity, 0, Infinity);
+ ctx.setTransform(0, 0, Infinity, 0, Infinity, 0);
+ ctx.setTransform(0, 0, Infinity, 0, Infinity, Infinity);
+ ctx.setTransform(0, 0, Infinity, 0, 0, Infinity);
+ ctx.setTransform(0, 0, 0, Infinity, Infinity, 0);
+ ctx.setTransform(0, 0, 0, Infinity, Infinity, Infinity);
+ ctx.setTransform(0, 0, 0, Infinity, 0, Infinity);
+ ctx.setTransform(0, 0, 0, 0, Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.html
new file mode 100644
index 0000000000..2d24d61f4b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.setTransform.skewed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.setTransform.skewed</h1>
+<p class="desc"></p>
+
+
+<script>
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.setTransform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ _assertPixel(canvas, 21,11, 0,255,0,255);
+ _assertPixel(canvas, 79,11, 0,255,0,255);
+ _assertPixel(canvas, 21,39, 0,255,0,255);
+ _assertPixel(canvas, 79,39, 0,255,0,255);
+ _assertPixel(canvas, 39,19, 0,255,0,255);
+ _assertPixel(canvas, 61,19, 0,255,0,255);
+ _assertPixel(canvas, 39,31, 0,255,0,255);
+ _assertPixel(canvas, 61,31, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.worker.js
new file mode 100644
index 0000000000..161f4087e5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.setTransform.skewed.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.setTransform.skewed
+// Description:
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.setTransform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ _assertPixel(canvas, 21,11, 0,255,0,255);
+ _assertPixel(canvas, 79,11, 0,255,0,255);
+ _assertPixel(canvas, 21,39, 0,255,0,255);
+ _assertPixel(canvas, 79,39, 0,255,0,255);
+ _assertPixel(canvas, 39,19, 0,255,0,255);
+ _assertPixel(canvas, 61,19, 0,255,0,255);
+ _assertPixel(canvas, 39,31, 0,255,0,255);
+ _assertPixel(canvas, 61,31, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.html
new file mode 100644
index 0000000000..d88b3bc03c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.transform.identity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.transform.identity</h1>
+<p class="desc">transform() with the identity matrix does nothing</p>
+
+
+<script>
+var t = async_test("transform() with the identity matrix does nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,0, 0,1, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.worker.js
new file mode 100644
index 0000000000..67202628c6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.identity.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.transform.identity
+// Description:transform() with the identity matrix does nothing
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("transform() with the identity matrix does nothing");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,0, 0,1, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.html
new file mode 100644
index 0000000000..a7dfbe5f34
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.transform.multiply</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.transform.multiply</h1>
+<p class="desc">transform() multiplies the CTM</p>
+
+
+<script>
+var t = async_test("transform() multiplies the CTM");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,2, 3,4, 5,6);
+ ctx.transform(-2,1, 3/2,-1/2, 1,-2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.worker.js
new file mode 100644
index 0000000000..aa89a7d8ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.multiply.worker.js
@@ -0,0 +1,29 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.transform.multiply
+// Description:transform() multiplies the CTM
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("transform() multiplies the CTM");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,2, 3,4, 5,6);
+ ctx.transform(-2,1, 3/2,-1/2, 1,-2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.html
new file mode 100644
index 0000000000..c4cd459c8b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.transform.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.transform.nonfinite</h1>
+<p class="desc">transform() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("transform() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.transform(Infinity, 0, 0, 0, 0, 0);
+ ctx.transform(-Infinity, 0, 0, 0, 0, 0);
+ ctx.transform(NaN, 0, 0, 0, 0, 0);
+ ctx.transform(0, Infinity, 0, 0, 0, 0);
+ ctx.transform(0, -Infinity, 0, 0, 0, 0);
+ ctx.transform(0, NaN, 0, 0, 0, 0);
+ ctx.transform(0, 0, Infinity, 0, 0, 0);
+ ctx.transform(0, 0, -Infinity, 0, 0, 0);
+ ctx.transform(0, 0, NaN, 0, 0, 0);
+ ctx.transform(0, 0, 0, Infinity, 0, 0);
+ ctx.transform(0, 0, 0, -Infinity, 0, 0);
+ ctx.transform(0, 0, 0, NaN, 0, 0);
+ ctx.transform(0, 0, 0, 0, Infinity, 0);
+ ctx.transform(0, 0, 0, 0, -Infinity, 0);
+ ctx.transform(0, 0, 0, 0, NaN, 0);
+ ctx.transform(0, 0, 0, 0, 0, Infinity);
+ ctx.transform(0, 0, 0, 0, 0, -Infinity);
+ ctx.transform(0, 0, 0, 0, 0, NaN);
+ ctx.transform(Infinity, Infinity, 0, 0, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, 0, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, 0, Infinity, 0);
+ ctx.transform(Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, 0, 0, Infinity);
+ ctx.transform(Infinity, Infinity, 0, Infinity, 0, 0);
+ ctx.transform(Infinity, Infinity, 0, Infinity, Infinity, 0);
+ ctx.transform(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.transform(Infinity, Infinity, 0, 0, Infinity, 0);
+ ctx.transform(Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, 0, 0, 0, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, 0, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, 0, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, Infinity, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, 0, Infinity, Infinity, 0, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, Infinity, 0);
+ ctx.transform(Infinity, 0, Infinity, 0, Infinity, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, 0, Infinity);
+ ctx.transform(Infinity, 0, 0, Infinity, 0, 0);
+ ctx.transform(Infinity, 0, 0, Infinity, Infinity, 0);
+ ctx.transform(Infinity, 0, 0, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, 0, 0, Infinity, 0, Infinity);
+ ctx.transform(Infinity, 0, 0, 0, Infinity, 0);
+ ctx.transform(Infinity, 0, 0, 0, Infinity, Infinity);
+ ctx.transform(Infinity, 0, 0, 0, 0, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, 0, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, 0, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, Infinity, 0);
+ ctx.transform(0, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, 0, Infinity);
+ ctx.transform(0, Infinity, 0, Infinity, 0, 0);
+ ctx.transform(0, Infinity, 0, Infinity, Infinity, 0);
+ ctx.transform(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.transform(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.transform(0, Infinity, 0, 0, Infinity, 0);
+ ctx.transform(0, Infinity, 0, 0, Infinity, Infinity);
+ ctx.transform(0, Infinity, 0, 0, 0, Infinity);
+ ctx.transform(0, 0, Infinity, Infinity, 0, 0);
+ ctx.transform(0, 0, Infinity, Infinity, Infinity, 0);
+ ctx.transform(0, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(0, 0, Infinity, Infinity, 0, Infinity);
+ ctx.transform(0, 0, Infinity, 0, Infinity, 0);
+ ctx.transform(0, 0, Infinity, 0, Infinity, Infinity);
+ ctx.transform(0, 0, Infinity, 0, 0, Infinity);
+ ctx.transform(0, 0, 0, Infinity, Infinity, 0);
+ ctx.transform(0, 0, 0, Infinity, Infinity, Infinity);
+ ctx.transform(0, 0, 0, Infinity, 0, Infinity);
+ ctx.transform(0, 0, 0, 0, Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.worker.js
new file mode 100644
index 0000000000..c96af44b59
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.nonfinite.worker.js
@@ -0,0 +1,105 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.transform.nonfinite
+// Description:transform() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("transform() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.transform(Infinity, 0, 0, 0, 0, 0);
+ ctx.transform(-Infinity, 0, 0, 0, 0, 0);
+ ctx.transform(NaN, 0, 0, 0, 0, 0);
+ ctx.transform(0, Infinity, 0, 0, 0, 0);
+ ctx.transform(0, -Infinity, 0, 0, 0, 0);
+ ctx.transform(0, NaN, 0, 0, 0, 0);
+ ctx.transform(0, 0, Infinity, 0, 0, 0);
+ ctx.transform(0, 0, -Infinity, 0, 0, 0);
+ ctx.transform(0, 0, NaN, 0, 0, 0);
+ ctx.transform(0, 0, 0, Infinity, 0, 0);
+ ctx.transform(0, 0, 0, -Infinity, 0, 0);
+ ctx.transform(0, 0, 0, NaN, 0, 0);
+ ctx.transform(0, 0, 0, 0, Infinity, 0);
+ ctx.transform(0, 0, 0, 0, -Infinity, 0);
+ ctx.transform(0, 0, 0, 0, NaN, 0);
+ ctx.transform(0, 0, 0, 0, 0, Infinity);
+ ctx.transform(0, 0, 0, 0, 0, -Infinity);
+ ctx.transform(0, 0, 0, 0, 0, NaN);
+ ctx.transform(Infinity, Infinity, 0, 0, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, 0, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, 0, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, 0, Infinity, 0);
+ ctx.transform(Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, Infinity, 0, 0, Infinity);
+ ctx.transform(Infinity, Infinity, 0, Infinity, 0, 0);
+ ctx.transform(Infinity, Infinity, 0, Infinity, Infinity, 0);
+ ctx.transform(Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, 0, Infinity, 0, Infinity);
+ ctx.transform(Infinity, Infinity, 0, 0, Infinity, 0);
+ ctx.transform(Infinity, Infinity, 0, 0, Infinity, Infinity);
+ ctx.transform(Infinity, Infinity, 0, 0, 0, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, 0, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, 0, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, Infinity, 0);
+ ctx.transform(Infinity, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, 0, Infinity, Infinity, 0, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, Infinity, 0);
+ ctx.transform(Infinity, 0, Infinity, 0, Infinity, Infinity);
+ ctx.transform(Infinity, 0, Infinity, 0, 0, Infinity);
+ ctx.transform(Infinity, 0, 0, Infinity, 0, 0);
+ ctx.transform(Infinity, 0, 0, Infinity, Infinity, 0);
+ ctx.transform(Infinity, 0, 0, Infinity, Infinity, Infinity);
+ ctx.transform(Infinity, 0, 0, Infinity, 0, Infinity);
+ ctx.transform(Infinity, 0, 0, 0, Infinity, 0);
+ ctx.transform(Infinity, 0, 0, 0, Infinity, Infinity);
+ ctx.transform(Infinity, 0, 0, 0, 0, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, 0, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, 0, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, Infinity, 0);
+ ctx.transform(0, Infinity, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(0, Infinity, Infinity, Infinity, 0, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, Infinity, 0);
+ ctx.transform(0, Infinity, Infinity, 0, Infinity, Infinity);
+ ctx.transform(0, Infinity, Infinity, 0, 0, Infinity);
+ ctx.transform(0, Infinity, 0, Infinity, 0, 0);
+ ctx.transform(0, Infinity, 0, Infinity, Infinity, 0);
+ ctx.transform(0, Infinity, 0, Infinity, Infinity, Infinity);
+ ctx.transform(0, Infinity, 0, Infinity, 0, Infinity);
+ ctx.transform(0, Infinity, 0, 0, Infinity, 0);
+ ctx.transform(0, Infinity, 0, 0, Infinity, Infinity);
+ ctx.transform(0, Infinity, 0, 0, 0, Infinity);
+ ctx.transform(0, 0, Infinity, Infinity, 0, 0);
+ ctx.transform(0, 0, Infinity, Infinity, Infinity, 0);
+ ctx.transform(0, 0, Infinity, Infinity, Infinity, Infinity);
+ ctx.transform(0, 0, Infinity, Infinity, 0, Infinity);
+ ctx.transform(0, 0, Infinity, 0, Infinity, 0);
+ ctx.transform(0, 0, Infinity, 0, Infinity, Infinity);
+ ctx.transform(0, 0, Infinity, 0, 0, Infinity);
+ ctx.transform(0, 0, 0, Infinity, Infinity, 0);
+ ctx.transform(0, 0, 0, Infinity, Infinity, Infinity);
+ ctx.transform(0, 0, 0, Infinity, 0, Infinity);
+ ctx.transform(0, 0, 0, 0, Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.html
new file mode 100644
index 0000000000..2f3c0453e3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.transform.skewed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.transform.skewed</h1>
+<p class="desc">transform() with skewy matrix transforms correctly</p>
+
+
+<script>
+var t = async_test("transform() with skewy matrix transforms correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.transform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ _assertPixel(canvas, 21,11, 0,255,0,255);
+ _assertPixel(canvas, 79,11, 0,255,0,255);
+ _assertPixel(canvas, 21,39, 0,255,0,255);
+ _assertPixel(canvas, 79,39, 0,255,0,255);
+ _assertPixel(canvas, 39,19, 0,255,0,255);
+ _assertPixel(canvas, 61,19, 0,255,0,255);
+ _assertPixel(canvas, 39,31, 0,255,0,255);
+ _assertPixel(canvas, 61,31, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.worker.js
new file mode 100644
index 0000000000..c7ebaf9a83
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.transform.skewed.worker.js
@@ -0,0 +1,50 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.transform.skewed
+// Description:transform() with skewy matrix transforms correctly
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("transform() with skewy matrix transforms correctly");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.transform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ _assertPixel(canvas, 21,11, 0,255,0,255);
+ _assertPixel(canvas, 79,11, 0,255,0,255);
+ _assertPixel(canvas, 21,39, 0,255,0,255);
+ _assertPixel(canvas, 79,39, 0,255,0,255);
+ _assertPixel(canvas, 39,19, 0,255,0,255);
+ _assertPixel(canvas, 61,19, 0,255,0,255);
+ _assertPixel(canvas, 39,31, 0,255,0,255);
+ _assertPixel(canvas, 61,31, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.html
new file mode 100644
index 0000000000..f68225c077
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.translate.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.translate.basic</h1>
+<p class="desc">translate() works</p>
+
+
+<script>
+var t = async_test("translate() works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.worker.js
new file mode 100644
index 0000000000..f377ab8a48
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.basic.worker.js
@@ -0,0 +1,28 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.translate.basic
+// Description:translate() works
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("translate() works");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ _assertPixel(canvas, 90,40, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.html b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.html
new file mode 100644
index 0000000000..ebab949b4c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.transformation.translate.nonfinite</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.transformation.translate.nonfinite</h1>
+<p class="desc">translate() with Infinity/NaN is ignored</p>
+
+
+<script>
+var t = async_test("translate() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.translate(Infinity, 0.1);
+ ctx.translate(-Infinity, 0.1);
+ ctx.translate(NaN, 0.1);
+ ctx.translate(0.1, Infinity);
+ ctx.translate(0.1, -Infinity);
+ ctx.translate(0.1, NaN);
+ ctx.translate(Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.worker.js
new file mode 100644
index 0000000000..7ad6fce2ba
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/transformations/2d.transformation.translate.nonfinite.worker.js
@@ -0,0 +1,37 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.transformation.translate.nonfinite
+// Description:translate() with Infinity/NaN is ignored
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("translate() with Infinity/NaN is ignored");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ ctx.translate(Infinity, 0.1);
+ ctx.translate(-Infinity, 0.1);
+ ctx.translate(NaN, 0.1);
+ ctx.translate(0.1, Infinity);
+ ctx.translate(0.1, -Infinity);
+ ctx.translate(0.1, NaN);
+ ctx.translate(Infinity, Infinity);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.html b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.html
new file mode 100644
index 0000000000..e1391e9010
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.color.space.p3.to.p3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.color.space.p3.to.p3</h1>
+<p class="desc">test getImageData with display-p3 and uint8 from display p3 uint8 canvas</p>
+
+
+<script>
+var t = async_test("test getImageData with display-p3 and uint8 from display p3 uint8 canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d', {colorSpace: "display-p3"});
+
+ var color_style = 'rgb(50, 100, 150)';
+ // [0.24304, 0.38818, 0.57227, 1.0] * 255 = [62, 99, 146, 255]
+ var pixel_expected = [62, 99, 146, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "display-p3", storageFormat: "uint8"}).data;
+ _assertSame(pixel.length, pixel_expected.length, "pixel.length", "pixel_expected.length");
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.worker.js
new file mode 100644
index 0000000000..844cd93f81
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.p3.worker.js
@@ -0,0 +1,34 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.color.space.p3.to.p3
+// Description:test getImageData with display-p3 and uint8 from display p3 uint8 canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("test getImageData with display-p3 and uint8 from display p3 uint8 canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d', {colorSpace: "display-p3"});
+
+ var color_style = 'rgb(50, 100, 150)';
+ // [0.24304, 0.38818, 0.57227, 1.0] * 255 = [62, 99, 146, 255]
+ var pixel_expected = [62, 99, 146, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "display-p3", storageFormat: "uint8"}).data;
+ _assertSame(pixel.length, pixel_expected.length, "pixel.length", "pixel_expected.length");
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.html b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.html
new file mode 100644
index 0000000000..0628744421
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.color.space.p3.to.srgb</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.color.space.p3.to.srgb</h1>
+<p class="desc">test getImageData with srsb and uint8 from display p3 uint8 canvas</p>
+
+
+<script>
+var t = async_test("test getImageData with srsb and uint8 from display p3 uint8 canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d', {colorSpace: "display-p3"});
+
+ var color_style = 'rgb(50, 100, 150)';
+ var pixel_expected = [50, 100, 150, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "srgb", storageFormat: "uint8"}).data;
+ _assertSame(pixel.length, pixel_expected.length, "pixel.length", "pixel_expected.length");
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+ t.done();
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.worker.js b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.worker.js
new file mode 100644
index 0000000000..619c59076d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/wide-gamut-canvas/2d.color.space.p3.to.srgb.worker.js
@@ -0,0 +1,33 @@
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:2d.color.space.p3.to.srgb
+// Description:test getImageData with srsb and uint8 from display p3 uint8 canvas
+// Note:
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t = async_test("test getImageData with srsb and uint8 from display p3 uint8 canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+ var canvas = new OffscreenCanvas(100, 50);
+ var ctx = canvas.getContext('2d', {colorSpace: "display-p3"});
+
+ var color_style = 'rgb(50, 100, 150)';
+ var pixel_expected = [50, 100, 150, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "srgb", storageFormat: "uint8"}).data;
+ _assertSame(pixel.length, pixel_expected.length, "pixel.length", "pixel_expected.length");
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+ t.done();
+});
+done();
diff --git a/testing/web-platform/tests/html/canvas/resources/2x2.png b/testing/web-platform/tests/html/canvas/resources/2x2.png
new file mode 100644
index 0000000000..9be31562b3
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/2x2.png
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-frame.css b/testing/web-platform/tests/html/canvas/resources/canvas-frame.css
new file mode 100644
index 0000000000..0c97a680d1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-frame.css
@@ -0,0 +1,21 @@
+body {
+ font-size: small;
+ font-family: sans-serif;
+}
+
+p {
+ line-height: 0;
+}
+
+p:first-child {
+ display: inline;
+}
+
+h1 {
+ display: inline;
+}
+
+iframe, object {
+ border: 1px black solid;
+ margin: 2px;
+}
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-frame.css.headers b/testing/web-platform/tests/html/canvas/resources/canvas-frame.css.headers
new file mode 100644
index 0000000000..e13897f157
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-frame.css.headers
@@ -0,0 +1 @@
+Content-Type: text/css; charset=utf-8
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-index.css b/testing/web-platform/tests/html/canvas/resources/canvas-index.css
new file mode 100644
index 0000000000..ef35864bc0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-index.css
@@ -0,0 +1,31 @@
+body {
+ font-size: small;
+ font-family: sans-serif;
+}
+
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+h3 {
+ display: inline;
+ font-size: medium;
+}
+
+h3 + p {
+ display: inline;
+ margin-left: 0.5em;
+}
+
+li {
+ list-style-type: none;
+}
+
+ul {
+ padding-left: 2em;
+ margin-left: 0;
+}
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-index.css.headers b/testing/web-platform/tests/html/canvas/resources/canvas-index.css.headers
new file mode 100644
index 0000000000..e13897f157
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-index.css.headers
@@ -0,0 +1 @@
+Content-Type: text/css; charset=utf-8
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-spec.css b/testing/web-platform/tests/html/canvas/resources/canvas-spec.css
new file mode 100644
index 0000000000..5882acb68e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-spec.css
@@ -0,0 +1,50 @@
+.testrefs {
+ font-size: small;
+ margin-left: 0.2em;
+ margin-right: 0.2em;
+ border-bottom: none !important;
+
+ font-weight: normal;
+ font-style: normal;
+ white-space: normal;
+ font-family: sans-serif;
+}
+
+.kw-must, .kw-required {
+ background: #fda;
+}
+
+.kw-should {
+ background: #ffa;
+}
+
+.kw-none {
+ background: #dfa;
+}
+
+
+pre.idl .testrefs :link {
+ color: #00c;
+}
+
+pre.idl .testrefs :visited {
+ color: #609;
+}
+
+.testrefs a:hover {
+ background: transparent;
+ text-decoration: none;
+}
+
+.testrefs:before {
+ content: '[';
+}
+
+.testrefs:after {
+ content: ']';
+}
+
+.testrefs a:first-child {
+ font-weight: bold;
+ text-decoration: none;
+}
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-spec.css.headers b/testing/web-platform/tests/html/canvas/resources/canvas-spec.css.headers
new file mode 100644
index 0000000000..e13897f157
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-spec.css.headers
@@ -0,0 +1 @@
+Content-Type: text/css; charset=utf-8
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-tests.css b/testing/web-platform/tests/html/canvas/resources/canvas-tests.css
new file mode 100644
index 0000000000..e006e812de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-tests.css
@@ -0,0 +1,134 @@
+html.fail {
+ background: #f66;
+}
+html.pass {
+ background: #6f6;
+}
+html.needs_check {
+ background: #99f;
+}
+
+body {
+ font-size: small;
+ font-family: sans-serif;
+ color: black;
+}
+
+a:link {
+ color: #00c;
+}
+a:visited {
+ color: #808;
+}
+
+body.framed {
+ font-size: x-small;
+}
+
+h1 {
+ font-size: larger;
+ margin: 0;
+ padding-left: 0.5em;
+ text-indent: -0.5em;
+}
+
+p {
+ margin: 0;
+}
+
+p.notes {
+ margin-bottom: 0.5em;
+ font-style: italic;
+}
+
+ul {
+ margin: 0;
+ margin-bottom: 0.5em;
+ padding: 0;
+ padding-left: 1em;
+}
+
+.refs {
+ font-style: italic;
+ margin-bottom: 0.5em;
+}
+
+.refs ul {
+ display: inline;
+ margin: 0;
+ padding: 0;
+}
+
+.refs li {
+ display: inline;
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+}
+
+canvas {
+ display: none;
+ visibility: hidden;
+ border: 2px #f0f solid;
+ background: url(../images/background.png);
+}
+
+img.expected {
+ display: none;
+ border: 2px #f0f solid;
+ background: url(../images/background.png);
+}
+
+iframe {
+ border: 2px #f0f solid;
+}
+
+.output {
+ display: none;
+}
+
+.show_output .output, .needs_check .output {
+ display: block !important;
+ visibility: visible !important;
+}
+
+.show_output #show_output {
+ display: none;
+}
+
+.resource {
+ visibility: hidden;
+ height: 0;
+}
+
+.fallback {
+ font-size: 2em;
+ font-weight: bold;
+ color: #a00;
+}
+
+
+html.minimal body {
+ color: white;
+}
+html.fail.minimal {
+ background: #f00;
+}
+html.pass.minimal {
+ background: #080;
+}
+html.needs_check.minimal {
+ background: #008;
+}
+.minimal #d {
+ display: none !important;
+}
+.minimal .expectedtext {
+ visibility: hidden !important;
+}
+#passtext, #failtext {
+ display: none;
+}
+.minimal.pass #passtext, .minimal.fail #failtext {
+ display: block;
+}
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-tests.css.headers b/testing/web-platform/tests/html/canvas/resources/canvas-tests.css.headers
new file mode 100644
index 0000000000..e13897f157
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-tests.css.headers
@@ -0,0 +1 @@
+Content-Type: text/css; charset=utf-8
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-tests.js b/testing/web-platform/tests/html/canvas/resources/canvas-tests.js
new file mode 100644
index 0000000000..c6e2873283
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-tests.js
@@ -0,0 +1,221 @@
+function _valToString(val)
+{
+ if (val === undefined || val === null)
+ return '[' + typeof(val) + ']';
+ return val.toString() + '[' + typeof(val) + ']';
+}
+
+function _assert(cond, text)
+{
+ assert_true(!!cond, text);
+}
+
+function _assertSame(a, b, text_a, text_b)
+{
+ var msg = text_a + ' === ' + text_b + ' (got ' + _valToString(a) +
+ ', expected ' + _valToString(b) + ')';
+ assert_equals(a, b, msg);
+}
+
+function _assertDifferent(a, b, text_a, text_b)
+{
+ var msg = text_a + ' !== ' + text_b + ' (got ' + _valToString(a) +
+ ', expected not ' + _valToString(b) + ')';
+ assert_not_equals(a, b, msg);
+}
+
+
+function _getPixel(canvas, x,y)
+{
+ var ctx = canvas.getContext('2d');
+ var imgdata = ctx.getImageData(x, y, 1, 1);
+ return [ imgdata.data[0], imgdata.data[1], imgdata.data[2], imgdata.data[3] ];
+}
+
+function _assertPixel(canvas, x, y, r, g, b, a)
+{
+ var c = _getPixel(canvas, x,y);
+ assert_equals(c[0], r, 'Red channel of the pixel at (' + x + ', ' + y + ')');
+ assert_equals(c[1], g, 'Green channel of the pixel at (' + x + ', ' + y + ')');
+ assert_equals(c[2], b, 'Blue channel of the pixel at (' + x + ', ' + y + ')');
+ assert_equals(c[3], a, 'Alpha channel of the pixel at (' + x + ', ' + y + ')');
+}
+
+function _assertPixelApprox(canvas, x, y, r, g, b, a, tolerance)
+{
+ var c = _getPixel(canvas, x,y);
+ assert_approx_equals(c[0], r, tolerance, 'Red channel of the pixel at (' + x + ', ' + y + ')');
+ assert_approx_equals(c[1], g, tolerance, 'Green channel of the pixel at (' + x + ', ' + y + ')');
+ assert_approx_equals(c[2], b, tolerance, 'Blue channel of the pixel at (' + x + ', ' + y + ')');
+ assert_approx_equals(c[3], a, tolerance, 'Alpha channel of the pixel at (' + x + ', ' + y + ')');
+}
+
+function _assertMatricesApproxEqual(matA, matB)
+{
+ A = matA.toFloat32Array();
+ B = matB.toFloat32Array();
+ assert_equals(A.length, B.length);
+ for (var i = 0; i < A.length; i++) {
+ assert_approx_equals(A[i], B[i], 10e-6);
+ }
+}
+
+function rad2deg(angle_in_radians) {
+ return angle_in_radians / Math.PI * 180;
+}
+
+function deg2rad(angle_in_degrees) {
+ return angle_in_degrees / 180 * Math.PI;
+}
+
+let _deferred = false;
+
+function deferTest() {
+ _deferred = true;
+}
+
+function _addTest(testFn, attributes={})
+{
+ on_event(window, "load", function()
+ {
+ t.step(function() {
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d', attributes);
+ t.step(testFn, window, canvas, ctx);
+ });
+
+ if (!_deferred) {
+ t.done();
+ }
+ });
+}
+
+function _assertGreen(ctx, canvasWidth, canvasHeight)
+{
+ var testColor = function(d, idx, expected) {
+ assert_equals(d[idx], expected, "d[" + idx + "]", String(expected));
+ };
+ var imagedata = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
+ var w = imagedata.width, h = imagedata.height, d = imagedata.data;
+ for (var i = 0; i < h; ++i) {
+ for (var j = 0; j < w; ++j) {
+ testColor(d, 4 * (w * i + j) + 0, 0);
+ testColor(d, 4 * (w * i + j) + 1, 255);
+ testColor(d, 4 * (w * i + j) + 2, 0);
+ testColor(d, 4 * (w * i + j) + 3, 255);
+ }
+ }
+}
+
+function addCrossOriginYellowImage()
+{
+ var img = new Image();
+ img.id = "yellow.png";
+ img.className = "resource";
+ img.src = get_host_info().HTTP_REMOTE_ORIGIN + "/images/yellow.png";
+ document.body.appendChild(img);
+}
+
+function addCrossOriginRedirectYellowImage()
+{
+ var img = new Image();
+ img.id = "yellow.png";
+ img.className = "resource";
+ img.src = get_host_info().HTTP_ORIGIN + "/common/redirect.py?location=" +
+ get_host_info().HTTP_REMOTE_ORIGIN + "/images/yellow.png";
+ document.body.appendChild(img);
+}
+
+function forEachCanvasSource(crossOriginUrl, sameOriginUrl, callback) {
+ function makeImage() {
+ return new Promise((resolve, reject) => {
+ const image = new Image();
+ image.onload = () => resolve(image);
+ image.onerror = reject;
+ image.src = crossOriginUrl + "/images/red.png";
+ });
+ }
+
+ const arguments = [
+ {
+ name: "cross-origin HTMLImageElement",
+ factory: makeImage,
+ },
+
+ {
+ name: "cross-origin SVGImageElement",
+ factory: () => {
+ return new Promise((resolve, reject) => {
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ image.onload = () => resolve(image);
+ image.onerror = reject;
+ image.setAttribute("externalResourcesRequired", "true");
+ image.setAttributeNS("http://www.w3.org/1999/xlink", 'xlink:href', crossOriginUrl + "/images/red.png");
+ document.body.appendChild(image);
+ });
+ },
+ },
+
+ {
+ name: "cross-origin HTMLVideoElement",
+ factory: () => {
+ return new Promise((resolve, reject) => {
+ const video = document.createElement("video");
+ video.oncanplaythrough = () => resolve(video);
+ video.preload = "auto";
+ video.onerror = reject;
+ video.src = getVideoURI(crossOriginUrl + "/media/movie_300");
+ });
+ },
+ },
+
+ {
+ name: "redirected to cross-origin HTMLVideoElement",
+ factory: () => {
+ return new Promise((resolve, reject) => {
+ const video = document.createElement("video");
+ video.oncanplaythrough = () => resolve(video);
+ video.preload = "auto";
+ video.onerror = reject;
+ video.src = "/common/redirect.py?location=" + getVideoURI(crossOriginUrl + "/media/movie_300");
+ });
+ },
+ },
+
+ {
+ name: "redirected to same-origin HTMLVideoElement",
+ factory: () => {
+ return new Promise((resolve, reject) => {
+ const video = document.createElement("video");
+ video.oncanplaythrough = () => resolve(video);
+ video.preload = "auto";
+ video.onerror = reject;
+ video.src = crossOriginUrl + "/common/redirect.py?location=" + getVideoURI(sameOriginUrl + "/media/movie_300");
+ });
+ },
+ },
+
+ {
+ name: "unclean HTMLCanvasElement",
+ factory: () => {
+ return makeImage().then(image => {
+ const canvas = document.createElement("canvas");
+ const context = canvas.getContext("2d");
+ context.drawImage(image, 0, 0);
+ return canvas;
+ });
+ },
+ },
+
+ {
+ name: "unclean ImageBitmap",
+ factory: () => {
+ return makeImage().then(createImageBitmap);
+ },
+ },
+ ];
+
+ for (let { name, factory } of arguments) {
+ callback(name, factory);
+ }
+}
diff --git a/testing/web-platform/tests/html/canvas/resources/canvas-tests.js.headers b/testing/web-platform/tests/html/canvas/resources/canvas-tests.js.headers
new file mode 100644
index 0000000000..6805c323df
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/resources/canvas-tests.js.headers
@@ -0,0 +1 @@
+Content-Type: text/javascript; charset=utf-8
diff --git a/testing/web-platform/tests/html/canvas/tools/gentest.py b/testing/web-platform/tests/html/canvas/tools/gentest.py
new file mode 100644
index 0000000000..36cb7fe0f6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/gentest.py
@@ -0,0 +1,18 @@
+# To use this script:
+# -make a python virtual environment somewhere (it doesn't matter where)
+# python3 -m venv venv
+# -enter the virtual environment
+# source venv/bin/activate
+# -install required packages in the venv
+# pip3 install cairocffi jinja2 pyyaml
+# -change to the directory with this script and run it
+# python3 gentest.py
+
+from gentestutils import genTestUtils
+from gentestutilsunion import genTestUtils_union
+
+genTestUtils('../element', '../element', 'templates.yaml',
+ 'name2dir-canvas.yaml', False)
+genTestUtils('../offscreen', '../offscreen', 'templates.yaml',
+ 'name2dir-offscreen.yaml', True)
+genTestUtils_union('name2dir.yaml')
diff --git a/testing/web-platform/tests/html/canvas/tools/gentest_union.py b/testing/web-platform/tests/html/canvas/tools/gentest_union.py
new file mode 100644
index 0000000000..38a37a81ac
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/gentest_union.py
@@ -0,0 +1,3 @@
+from gentestutilsunion import genTestUtils_union
+
+genTestUtils_union('name2dir.yaml')
diff --git a/testing/web-platform/tests/html/canvas/tools/gentestutils.py b/testing/web-platform/tests/html/canvas/tools/gentestutils.py
new file mode 100644
index 0000000000..8fa33e3975
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/gentestutils.py
@@ -0,0 +1,369 @@
+# Current code status:
+#
+# This was originally written by Philip Taylor for use at
+# http://philip.html5.org/tests/canvas/suite/tests/
+#
+# It has been adapted for use with the Web Platform Test Suite suite at
+# https://github.com/web-platform-tests/wpt/
+#
+# The original version had a number of now-removed features (multiple versions
+# of each test case of varying verbosity, Mozilla mochitests, semi-automated
+# test harness). It also had a different directory structure.
+
+# To update or add test cases:
+#
+# * Modify the tests*.yaml files.
+# - 'name' is an arbitrary hierarchical name to help categorise tests.
+# - 'desc' is a rough description of what behaviour the test aims to test.
+# - 'code' is JavaScript code to execute, with some special commands starting
+# with '@'
+# - 'expected' is what the final canvas output should be: a string 'green' or
+# 'clear' (100x50 images in both cases), or a string 'size 100 50' (or any
+# other size) followed by Python code using Pycairo to generate the image.
+#
+# * Run "./build.sh".
+# This requires a few Python modules which might not be ubiquitous.
+# It will usually emit some warnings, which ideally should be fixed but can
+# generally be safely ignored.
+#
+# * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
+
+from typing import List, Mapping
+
+import re
+import importlib
+import os
+import pathlib
+import sys
+import textwrap
+
+try:
+ import cairocffi as cairo # type: ignore
+except ImportError:
+ import cairo
+
+try:
+ # Compatible and lots faster.
+ import syck as yaml # type: ignore
+except ImportError:
+ import yaml
+
+
+class Error(Exception):
+ """Base class for all exceptions raised by this module"""
+
+
+class InvalidTestDefinitionError(Error):
+ """Raised on invalid test definition."""
+
+
+def _simpleEscapeJS(string: str) -> str:
+ return string.replace('\\', '\\\\').replace('"', '\\"')
+
+
+def _escapeJS(string: str) -> str:
+ string = _simpleEscapeJS(string)
+ # Kind of an ugly hack, for nicer failure-message output.
+ string = re.sub(r'\[(\w+)\]', r'[\\""+(\1)+"\\"]', string)
+ return string
+
+
+def _expand_nonfinite(method: str, argstr: str, tail: str) -> str:
+ """
+ >>> print _expand_nonfinite('f', '<0 a>, <0 b>', ';')
+ f(a, 0);
+ f(0, b);
+ f(a, b);
+ >>> print _expand_nonfinite('f', '<0 a>, <0 b c>, <0 d>', ';')
+ f(a, 0, 0);
+ f(0, b, 0);
+ f(0, c, 0);
+ f(0, 0, d);
+ f(a, b, 0);
+ f(a, b, d);
+ f(a, 0, d);
+ f(0, b, d);
+ """
+ # argstr is "<valid-1 invalid1-1 invalid2-1 ...>, ..." (where usually
+ # 'invalid' is Infinity/-Infinity/NaN).
+ args = []
+ for arg in argstr.split(', '):
+ match = re.match('<(.*)>', arg)
+ if match is None:
+ raise InvalidTestDefinitionError(
+ f"Expected arg to match format '<(.*)>', but was: {arg}")
+ a = match.group(1)
+ args.append(a.split(' '))
+ calls = []
+ # Start with the valid argument list.
+ call = [args[j][0] for j in range(len(args))]
+ # For each argument alone, try setting it to all its invalid values:
+ for i in range(len(args)):
+ for a in args[i][1:]:
+ c2 = call[:]
+ c2[i] = a
+ calls.append(c2)
+ # For all combinations of >= 2 arguments, try setting them to their
+ # first invalid values. (Don't do all invalid values, because the
+ # number of combinations explodes.)
+ def f(c: List[str], start: int, depth: int) -> None:
+ for i in range(start, len(args)):
+ if len(args[i]) > 1:
+ a = args[i][1]
+ c2 = c[:]
+ c2[i] = a
+ if depth > 0:
+ calls.append(c2)
+ f(c2, i + 1, depth + 1)
+
+ f(call, 0, 0)
+
+ return '\n'.join('%s(%s)%s' % (method, ', '.join(c), tail) for c in calls)
+
+
+def _get_test_sub_dir(name: str, name_to_sub_dir: Mapping[str, str]) -> str:
+ for prefix in sorted(name_to_sub_dir.keys(), key=len, reverse=True):
+ if name.startswith(prefix):
+ return name_to_sub_dir[prefix]
+ raise InvalidTestDefinitionError(
+ 'Test "%s" has no defined target directory mapping' % name)
+
+
+def _expand_test_code(code: str) -> str:
+ code = re.sub(r'@nonfinite ([^(]+)\(([^)]+)\)(.*)', lambda m:
+ _expand_nonfinite(m.group(1), m.group(2), m.group(3)),
+ code) # Must come before '@assert throws'.
+
+ code = re.sub(r'@assert pixel (\d+,\d+) == (\d+,\d+,\d+,\d+);',
+ r'_assertPixel(canvas, \1, \2);', code)
+
+ code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+);',
+ r'_assertPixelApprox(canvas, \1, \2, 2);', code)
+
+ code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+) \+/- (\d+);',
+ r'_assertPixelApprox(canvas, \1, \2, \3);', code)
+
+ code = re.sub(r'@assert throws (\S+_ERR) (.*);',
+ r'assert_throws_dom("\1", function() { \2; });', code)
+
+ code = re.sub(r'@assert throws (\S+Error) (.*);',
+ r'assert_throws_js(\1, function() { \2; });', code)
+
+ code = re.sub(
+ r'@assert (.*) === (.*);', lambda m: '_assertSame(%s, %s, "%s", "%s");'
+ % (m.group(1), m.group(2), _escapeJS(m.group(1)), _escapeJS(m.group(2))
+ ), code)
+
+ code = re.sub(
+ r'@assert (.*) !== (.*);', lambda m:
+ '_assertDifferent(%s, %s, "%s", "%s");' % (m.group(1), m.group(
+ 2), _escapeJS(m.group(1)), _escapeJS(m.group(2))), code)
+
+ code = re.sub(
+ r'@assert (.*) =~ (.*);', lambda m: 'assert_regexp_match(%s, %s);' % (
+ m.group(1), m.group(2)), code)
+
+ code = re.sub(
+ r'@assert (.*);', lambda m: '_assert(%s, "%s");' % (m.group(
+ 1), _escapeJS(m.group(1))), code)
+
+ code = re.sub(r' @moz-todo', '', code)
+
+ code = re.sub(r'@moz-UniversalBrowserRead;', '', code)
+
+ assert ('@' not in code)
+
+ return code
+
+
+_CANVAS_SIZE_REGEX = re.compile(r'(?P<width>.*), (?P<height>.*)',
+ re.MULTILINE | re.DOTALL)
+
+
+def _get_canvas_size(test: Mapping[str, str]):
+ size = test.get('size', '100, 50')
+ match = _CANVAS_SIZE_REGEX.match(size)
+ if not match:
+ raise InvalidTestDefinitionError(
+ 'Invalid canvas size "%s" in test %s. Expected a string matching '
+ 'this pattern: "%%s, %%s" %% (width, height)' %
+ (size, test['name']))
+ return match.group('width'), match.group('height')
+
+
+def _generate_test(test: Mapping[str, str], templates: Mapping[str, str],
+ sub_dir: str, test_output_dir: str, image_output_dir: str,
+ is_offscreen_canvas: bool):
+ name = test['name']
+
+ if test.get('expected', '') == 'green' and re.search(
+ r'@assert pixel .* 0,0,0,0;', test['code']):
+ print('Probable incorrect pixel test in %s' % name)
+
+ code = _expand_test_code(test['code'].strip())
+ code = textwrap.indent(code, ' ')
+
+ expectation_html = ''
+ if 'expected' in test and test['expected'] is not None:
+ expected = test['expected']
+ expected_img = None
+ if expected == 'green':
+ expected_img = '/images/green-100x50.png'
+ elif expected == 'clear':
+ expected_img = '/images/clear-100x50.png'
+ else:
+ if ';' in expected:
+ print('Found semicolon in %s' % name)
+ expected = re.sub(
+ r'^size (\d+) (\d+)',
+ r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
+ r'\ncr = cairo.Context(surface)', expected)
+
+ expected += ("\nsurface.write_to_png('%s.png')\n" %
+ os.path.join(image_output_dir, sub_dir, name))
+ eval(compile(expected, '<test %s>' % name, 'exec'), {},
+ {'cairo': cairo})
+ expected_img = '%s.png' % name
+
+ if expected_img:
+ expectation_html = (
+ '<p class="output expectedtext">Expected output:<p>'
+ '<img src="%s" class="output expected" id="expected" '
+ 'alt="">' % expected_img)
+
+ canvas = ' ' + test['canvas'] if 'canvas' in test else ''
+ width, height = _get_canvas_size(test)
+
+ notes = '<p class="notes">%s' % test['notes'] if 'notes' in test else ''
+
+ timeout = ('\n<meta name="timeout" content="%s">' %
+ test['timeout'] if 'timeout' in test else '')
+ timeout_js = ('// META: timeout=%s\n' % test['timeout']
+ if 'timeout' in test else '')
+
+ images = ''
+ for src in test.get('images', []):
+ img_id = src.split('/')[-1]
+ if '/' not in src:
+ src = '../images/%s' % src
+ images += '<img src="%s" id="%s" class="resource">\n' % (src, img_id)
+ for src in test.get('svgimages', []):
+ img_id = src.split('/')[-1]
+ if '/' not in src:
+ src = '../images/%s' % src
+ images += ('<svg><image xlink:href="%s" id="%s" class="resource">'
+ '</svg>\n' % (src, img_id))
+ images = images.replace('../images/', '/images/')
+
+ fonts = ''
+ fonthack = ''
+ for font in test.get('fonts', []):
+ fonts += ('@font-face {\n font-family: %s;\n'
+ ' src: url("/fonts/%s.ttf");\n}\n' % (font, font))
+ # Browsers require the font to actually be used in the page.
+ if test.get('fonthack', 1):
+ fonthack += ('<span style="font-family: %s; position: '
+ 'absolute; visibility: hidden">A</span>\n' % font)
+ if fonts:
+ fonts = '<style>\n%s</style>\n' % fonts
+
+ fallback = test.get('fallback',
+ '<p class="fallback">FAIL (fallback content)</p>')
+
+ desc = test.get('desc', '')
+ escaped_desc = _simpleEscapeJS(desc)
+
+ attributes = test.get('attributes', '')
+ if attributes:
+ context_args = "'2d', %s" % attributes.strip()
+ attributes = ', ' + attributes.strip()
+ else:
+ context_args = "'2d'"
+
+ template_params = {
+ 'name': name,
+ 'desc': desc,
+ 'escaped_desc': escaped_desc,
+ 'notes': notes,
+ 'images': images,
+ 'fonts': fonts,
+ 'fonthack': fonthack,
+ 'timeout': timeout,
+ 'timeout_js': timeout_js,
+ 'canvas': canvas,
+ 'width': width,
+ 'height': height,
+ 'expected': expectation_html,
+ 'code': code,
+ 'fallback': fallback,
+ 'attributes': attributes,
+ 'context_args': context_args
+ }
+
+ test_path = os.path.join(test_output_dir, sub_dir, name)
+ if 'manual' in test:
+ test_path += '-manual'
+
+ if is_offscreen_canvas:
+ pathlib.Path(f'{test_path}.html').write_text(
+ templates['offscreen'] % template_params, 'utf-8')
+ pathlib.Path(f'{test_path}.worker.js').write_text(
+ templates['worker'] % template_params, 'utf-8')
+ else:
+ pathlib.Path(f'{test_path}.html').write_text(
+ templates['element'] % template_params, 'utf-8')
+
+
+def genTestUtils(TESTOUTPUTDIR: str, IMAGEOUTPUTDIR: str, TEMPLATEFILE: str,
+ NAME2DIRFILE: str, ISOFFSCREENCANVAS: bool) -> None:
+ # Run with --test argument to run unit tests
+ if len(sys.argv) > 1 and sys.argv[1] == '--test':
+ doctest = importlib.import_module('doctest')
+ doctest.testmod()
+ sys.exit()
+
+ templates = yaml.safe_load(pathlib.Path(TEMPLATEFILE).read_text())
+ name_to_sub_dir = yaml.safe_load(pathlib.Path(NAME2DIRFILE).read_text())
+
+ tests = []
+ test_yaml_directory = 'yaml/element'
+ if ISOFFSCREENCANVAS:
+ test_yaml_directory = 'yaml/offscreen'
+ TESTSFILES = [
+ os.path.join(test_yaml_directory, f)
+ for f in os.listdir(test_yaml_directory) if f.endswith('.yaml')
+ ]
+ for t in sum(
+ [yaml.safe_load(pathlib.Path(f).read_text()) for f in TESTSFILES], []):
+ if 'DISABLED' in t:
+ continue
+ if 'meta' in t:
+ eval(compile(t['meta'], '<meta test>', 'exec'), {},
+ {'tests': tests})
+ else:
+ tests.append(t)
+
+ # Ensure the test output directories exist.
+ testdirs = [TESTOUTPUTDIR, IMAGEOUTPUTDIR]
+ for sub_dir in set(name_to_sub_dir.values()):
+ testdirs.append('%s/%s' % (TESTOUTPUTDIR, sub_dir))
+ for d in testdirs:
+ try:
+ os.mkdir(d)
+ except FileExistsError:
+ pass # Ignore if it already exists.
+
+ used_tests = {}
+ for test in tests:
+ name = test['name']
+ print('\r(%s)' % name, ' ' * 32, '\t')
+
+ if name in used_tests:
+ print('Test %s is defined twice' % name)
+ used_tests[name] = 1
+
+ sub_dir = _get_test_sub_dir(name, name_to_sub_dir)
+ _generate_test(test, templates, sub_dir, TESTOUTPUTDIR, IMAGEOUTPUTDIR,
+ ISOFFSCREENCANVAS)
+
+ print()
diff --git a/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
new file mode 100644
index 0000000000..cf141f2f07
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
@@ -0,0 +1,535 @@
+# Current code status:
+#
+# This was originally written by Philip Taylor for use at
+# http://philip.html5.org/tests/canvas/suite/tests/
+#
+# It has been adapted for use with the Web Platform Test Suite suite at
+# https://github.com/web-platform-tests/wpt/
+#
+# The original version had a number of now-removed features (multiple versions
+# of each test case of varying verbosity, Mozilla mochitests, semi-automated
+# test harness). It also had a different directory structure.
+
+# To update or add test cases:
+#
+# * Modify the tests*.yaml files.
+# - 'name' is an arbitrary hierarchical name to help categorise tests.
+# - 'desc' is a rough description of what behaviour the test aims to test.
+# - 'code' is JavaScript code to execute, with some special commands starting
+# with '@'.
+# - 'expected' is what the final canvas output should be: a string 'green' or
+# 'clear' (100x50 images in both cases), or a string 'size 100 50' (or any
+# other size) followed by Python code using Pycairo to generate the image.
+#
+# * Run "./build.sh".
+# This requires a few Python modules which might not be ubiquitous.
+# It will usually emit some warnings, which ideally should be fixed but can
+# generally be safely ignored.
+#
+# * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
+
+from typing import Any, List, Mapping, Optional, Set, Tuple
+
+import re
+import collections
+import dataclasses
+import enum
+import importlib
+import itertools
+import jinja2
+import os
+import pathlib
+import sys
+
+try:
+ import cairocffi as cairo # type: ignore
+except ImportError:
+ import cairo
+
+try:
+ # Compatible and lots faster.
+ import syck as yaml # type: ignore
+except ImportError:
+ import yaml
+
+
+class Error(Exception):
+ """Base class for all exceptions raised by this module"""
+
+
+class InvalidTestDefinitionError(Error):
+ """Raised on invalid test definition."""
+
+
+def _doubleQuoteEscape(string: str) -> str:
+ return string.replace('\\', '\\\\').replace('"', '\\"')
+
+
+def _escapeJS(string: str) -> str:
+ string = _doubleQuoteEscape(string)
+ # Kind of an ugly hack, for nicer failure-message output.
+ string = re.sub(r'\[(\w+)\]', r'[\\""+(\1)+"\\"]', string)
+ return string
+
+
+def _unroll(text: str) -> str:
+ """Unrolls text with all possible permutations of the parameter lists.
+
+ Example:
+ >>> print _unroll('f = {<a | b>: <1 | 2 | 3>};')
+ // a
+ f = {a: 1};
+ f = {a: 2};
+ f = {a: 3};
+ // b
+ f = {b: 1};
+ f = {b: 2};
+ f = {b: 3};
+ """
+ patterns = [] # type: List[Tuple[str, List[str]]]
+ while True:
+ match = re.search(r'<([^>]+)>', text)
+ if not match:
+ break
+ key = f'@unroll_pattern_{len(patterns)}'
+ values = text[match.start(1):match.end(1)]
+ text = text[:match.start(0)] + key + text[match.end(0):]
+ patterns.append((key, [value.strip() for value in values.split('|')]))
+
+ def unroll_patterns(text: str,
+ patterns: List[Tuple[str, List[str]]],
+ label: Optional[str] = None) -> List[str]:
+ if not patterns:
+ return [text]
+ patterns = patterns.copy()
+ key, values = patterns.pop(0)
+ return (['// ' + label] if label else []) + list(
+ itertools.chain.from_iterable(
+ unroll_patterns(text.replace(key, value), patterns, value)
+ for value in values))
+
+ result = '\n'.join(unroll_patterns(text, patterns))
+ return result
+
+
+def _expand_nonfinite(method: str, argstr: str, tail: str) -> str:
+ """
+ >>> print _expand_nonfinite('f', '<0 a>, <0 b>', ';')
+ f(a, 0);
+ f(0, b);
+ f(a, b);
+ >>> print _expand_nonfinite('f', '<0 a>, <0 b c>, <0 d>', ';')
+ f(a, 0, 0);
+ f(0, b, 0);
+ f(0, c, 0);
+ f(0, 0, d);
+ f(a, b, 0);
+ f(a, b, d);
+ f(a, 0, d);
+ f(0, b, d);
+ """
+ # argstr is "<valid-1 invalid1-1 invalid2-1 ...>, ..." (where usually
+ # 'invalid' is Infinity/-Infinity/NaN).
+ args = []
+ for arg in argstr.split(', '):
+ match = re.match('<(.*)>', arg)
+ if match is None:
+ raise InvalidTestDefinitionError(
+ f"Expected arg to match format '<(.*)>', but was: {arg}")
+ a = match.group(1)
+ args.append(a.split(' '))
+ calls = []
+ # Start with the valid argument list.
+ call = [args[j][0] for j in range(len(args))]
+ # For each argument alone, try setting it to all its invalid values:
+ for i in range(len(args)):
+ for a in args[i][1:]:
+ c2 = call[:]
+ c2[i] = a
+ calls.append(c2)
+ # For all combinations of >= 2 arguments, try setting them to their
+ # first invalid values. (Don't do all invalid values, because the
+ # number of combinations explodes.)
+ def f(c: List[str], start: int, depth: int) -> None:
+ for i in range(start, len(args)):
+ if len(args[i]) > 1:
+ a = args[i][1]
+ c2 = c[:]
+ c2[i] = a
+ if depth > 0:
+ calls.append(c2)
+ f(c2, i + 1, depth + 1)
+
+ f(call, 0, 0)
+
+ return '\n'.join('%s(%s)%s' % (method, ', '.join(c), tail) for c in calls)
+
+
+def _get_test_sub_dir(name: str, name_to_sub_dir: Mapping[str, str]) -> str:
+ for prefix in sorted(name_to_sub_dir.keys(), key=len, reverse=True):
+ if name.startswith(prefix):
+ return name_to_sub_dir[prefix]
+ raise InvalidTestDefinitionError(
+ 'Test "%s" has no defined target directory mapping' % name)
+
+
+def _remove_extra_newlines(text: str) -> str:
+ """Remove newlines if a backslash is found at end of line."""
+ # Lines ending with '\' gets their newline character removed.
+ text = re.sub(r'\\\n', '', text, flags=re.MULTILINE | re.DOTALL)
+
+ # Lines ending with '\-' gets their newline and any leading white spaces on
+ # the following line removed.
+ text = re.sub(r'\\-\n\s*', '', text, flags=re.MULTILINE | re.DOTALL)
+ return text
+
+def _expand_test_code(code: str) -> str:
+ code = _remove_extra_newlines(code)
+
+ # Unroll expressions with a cross-product-style parameter expansion.
+ code = re.sub(r'@unroll ([^;]*;)', lambda m: _unroll(m.group(1)), code)
+
+ code = re.sub(r'@nonfinite ([^(]+)\(([^)]+)\)(.*)', lambda m:
+ _expand_nonfinite(m.group(1), m.group(2), m.group(3)),
+ code) # Must come before '@assert throws'.
+
+ code = re.sub(r'@assert pixel (\d+,\d+) == (\d+,\d+,\d+,\d+);',
+ r'_assertPixel(canvas, \1, \2);', code)
+
+ code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+);',
+ r'_assertPixelApprox(canvas, \1, \2, 2);', code)
+
+ code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+) \+/- (\d+);',
+ r'_assertPixelApprox(canvas, \1, \2, \3);', code)
+
+ code = re.sub(r'@assert throws (\S+_ERR) (.*);',
+ r'assert_throws_dom("\1", function() { \2; });', code)
+
+ code = re.sub(r'@assert throws (\S+Error) (.*);',
+ r'assert_throws_js(\1, function() { \2; });', code)
+
+ code = re.sub(
+ r'@assert (.*) === (.*);', lambda m: '_assertSame(%s, %s, "%s", "%s");'
+ % (m.group(1), m.group(2), _escapeJS(m.group(1)), _escapeJS(m.group(2))
+ ), code)
+
+ code = re.sub(
+ r'@assert (.*) !== (.*);', lambda m:
+ '_assertDifferent(%s, %s, "%s", "%s");' % (m.group(1), m.group(
+ 2), _escapeJS(m.group(1)), _escapeJS(m.group(2))), code)
+
+ code = re.sub(
+ r'@assert (.*) =~ (.*);', lambda m: 'assert_regexp_match(%s, %s);' % (
+ m.group(1), m.group(2)), code)
+
+ code = re.sub(
+ r'@assert (.*);', lambda m: '_assert(%s, "%s");' % (m.group(
+ 1), _escapeJS(m.group(1))), code)
+
+ code = re.sub(r' @moz-todo', '', code)
+
+ code = re.sub(r'@moz-UniversalBrowserRead;', '', code)
+
+ assert ('@' not in code)
+
+ return code
+
+
+class CanvasType(str, enum.Enum):
+ HTML_CANVAS = 'htmlcanvas'
+ OFFSCREEN_CANVAS = 'offscreencanvas'
+ WORKER = 'worker'
+
+
+def _get_enabled_canvas_types(test: Mapping[str, Any]) -> Set[CanvasType]:
+ return {CanvasType(t.lower()) for t in test.get('canvasType', CanvasType)}
+
+
+@dataclasses.dataclass
+class TestConfig:
+ out_dir: str
+ image_out_dir: str
+
+
+def _validate_test(test: Mapping[str, Any]):
+ if test.get('expected', '') == 'green' and re.search(
+ r'@assert pixel .* 0,0,0,0;', test['code']):
+ print('Probable incorrect pixel test in %s' % test['name'])
+
+ if 'size' in test and (not isinstance(test['size'], list)
+ or len(test['size']) != 2):
+ raise InvalidTestDefinitionError(
+ f'Invalid canvas size "{test["size"]}" in test {test["name"]}. '
+ 'Expected an array with two numbers.')
+
+ if 'test_type' in test and test['test_type'] != 'promise':
+ raise InvalidTestDefinitionError(
+ f'Test {test["name"]}\' test_type is invalid, it only accepts '
+ '"promise" now for creating promise test type in the template '
+ 'file.')
+
+ if 'reference' in test and 'html_reference' in test:
+ raise InvalidTestDefinitionError(
+ f'Test {test["name"]} is invalid, "reference" and "html_reference" '
+ 'can\'t both be specified at the same time.')
+
+
+def _render_template(jinja_env: jinja2.Environment,
+ template: jinja2.Template,
+ params: Mapping[str, Any]) -> str:
+ """Renders the specified jinja template.
+
+ The template is repetitively rendered until no more changes are observed.
+ This allows for template parameters to refer to other template parameters.
+ """
+ rendered = template.render(params)
+ previous = ''
+ while rendered != previous:
+ previous = rendered
+ template = jinja_env.from_string(rendered)
+ rendered = template.render(params)
+ return rendered
+
+
+def _render(jinja_env: jinja2.Environment, template_name: str,
+ params: Mapping[str, Any]):
+ params = dict(params)
+ params.update({
+ # Render the code on its own, as it could contain templates expanding
+ # to multuple lines. This is needed to get proper indentation of the
+ # code in the main template.
+ 'code': _render_template(jinja_env,
+ jinja_env.from_string(params['code']),
+ params)
+ })
+
+ return _render_template(jinja_env, jinja_env.get_template(template_name),
+ params)
+
+
+def _write_reference_test(jinja_env: jinja2.Environment,
+ params: Mapping[str, Any],
+ enabled_tests: Set[CanvasType],
+ canvas_path: str, offscreen_path: str):
+ if CanvasType.HTML_CANVAS in enabled_tests:
+ html_params = dict(params)
+ html_params.update({'canvas_type': CanvasType.HTML_CANVAS.value})
+ pathlib.Path(f'{canvas_path}.html').write_text(
+ _render(jinja_env, "reftest_element.html", html_params), 'utf-8')
+ if CanvasType.OFFSCREEN_CANVAS in enabled_tests:
+ offscreen_params = dict(params)
+ offscreen_params.update({
+ 'canvas_type': CanvasType.OFFSCREEN_CANVAS.value
+ })
+ pathlib.Path(f'{offscreen_path}.html').write_text(
+ _render(jinja_env, "reftest_offscreen.html", offscreen_params),
+ 'utf-8')
+ if CanvasType.WORKER in enabled_tests:
+ worker_params = dict(params)
+ worker_params.update({'canvas_type': CanvasType.WORKER.value})
+ pathlib.Path(f'{offscreen_path}.w.html').write_text(
+ _render(jinja_env, "reftest_worker.html", worker_params), 'utf-8')
+
+ js_ref = params.get('reference', '')
+ html_ref = params.get('html_reference', '')
+ ref_params = dict(params)
+ ref_params.update({
+ 'is_test_reference': True,
+ 'code': js_ref or html_ref
+ })
+ ref_template_name = 'reftest_element.html' if js_ref else 'reftest.html'
+ if CanvasType.HTML_CANVAS in enabled_tests:
+ pathlib.Path(f'{canvas_path}-expected.html').write_text(
+ _render(jinja_env, ref_template_name, ref_params), 'utf-8')
+ if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_tests:
+ pathlib.Path(f'{offscreen_path}-expected.html').write_text(
+ _render(jinja_env, ref_template_name, ref_params), 'utf-8')
+
+
+def _write_testharness_test(jinja_env: jinja2.Environment,
+ params: Mapping[str, Any],
+ enabled_tests: Set[CanvasType],
+ canvas_path: str,
+ offscreen_path: str):
+ # Create test cases for canvas and offscreencanvas.
+ if CanvasType.HTML_CANVAS in enabled_tests:
+ html_params = dict(params)
+ html_params.update({'canvas_type': CanvasType.HTML_CANVAS.value})
+ pathlib.Path(f'{canvas_path}.html').write_text(
+ _render(jinja_env, "testharness_element.html", html_params),
+ 'utf-8')
+
+ if CanvasType.OFFSCREEN_CANVAS in enabled_tests:
+ offscreen_params = dict(params)
+ offscreen_params.update({
+ 'canvas_type': CanvasType.OFFSCREEN_CANVAS.value
+ })
+ pathlib.Path(f'{offscreen_path}.html').write_text(
+ _render(jinja_env, "testharness_offscreen.html", offscreen_params),
+ 'utf-8')
+
+ if CanvasType.WORKER in enabled_tests:
+ worker_params = dict(params)
+ worker_params.update({'canvas_type': CanvasType.WORKER.value})
+ pathlib.Path(f'{offscreen_path}.worker.js').write_text(
+ _render(jinja_env, "testharness_worker.js", worker_params),
+ 'utf-8')
+
+
+def _generate_test(test: Mapping[str, Any], jinja_env: jinja2.Environment,
+ sub_dir: str, enabled_tests: Set[CanvasType],
+ html_canvas_cfg: TestConfig,
+ offscreen_canvas_cfg: TestConfig) -> None:
+ _validate_test(test)
+
+ name = test['name']
+
+ expected_img = None
+ if 'expected' in test and test['expected'] is not None:
+ expected = test['expected']
+ if expected == 'green':
+ expected_img = '/images/green-100x50.png'
+ elif expected == 'clear':
+ expected_img = '/images/clear-100x50.png'
+ else:
+ if ';' in expected:
+ print('Found semicolon in %s' % name)
+ expected = re.sub(
+ r'^size (\d+) (\d+)',
+ r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
+ r'\ncr = cairo.Context(surface)', expected)
+
+ if CanvasType.HTML_CANVAS in enabled_tests:
+ expected_canvas = (
+ expected + "\nsurface.write_to_png('%s.png')\n" %
+ os.path.join(html_canvas_cfg.image_out_dir, sub_dir, name))
+ eval(compile(expected_canvas, '<test %s>' % name, 'exec'), {},
+ {'cairo': cairo})
+
+ if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_tests:
+ expected_offscreen = (
+ expected +
+ "\nsurface.write_to_png('%s.png')\n" % os.path.join(
+ offscreen_canvas_cfg.image_out_dir, sub_dir, name))
+ eval(compile(expected_offscreen, '<test %s>' % name, 'exec'),
+ {}, {'cairo': cairo})
+
+ expected_img = '%s.png' % name
+
+ # Defaults:
+ params = {
+ 'desc': '',
+ 'size': [100, 50],
+ }
+
+ params.update(test)
+ params.update({
+ 'code': _expand_test_code(test['code']),
+ 'expected_img': expected_img
+ })
+
+ canvas_path = os.path.join(html_canvas_cfg.out_dir, sub_dir, name)
+ offscreen_path = os.path.join(offscreen_canvas_cfg.out_dir, sub_dir, name)
+ if 'manual' in test:
+ canvas_path += '-manual'
+ offscreen_path += '-manual'
+
+ if 'reference' in test or 'html_reference' in test:
+ _write_reference_test(jinja_env, params, enabled_tests,
+ canvas_path, offscreen_path)
+ else:
+ _write_testharness_test(jinja_env, params, enabled_tests, canvas_path,
+ offscreen_path)
+
+
+def genTestUtils_union(NAME2DIRFILE: str) -> None:
+ CANVASOUTPUTDIR = '../element'
+ CANVASIMAGEOUTPUTDIR = '../element'
+ OFFSCREENCANVASOUTPUTDIR = '../offscreen'
+ OFFSCREENCANVASIMAGEOUTPUTDIR = '../offscreen'
+
+ jinja_env = jinja2.Environment(
+ loader=jinja2.PackageLoader("gentestutilsunion"),
+ keep_trailing_newline=True,
+ trim_blocks=True,
+ lstrip_blocks=True)
+
+ jinja_env.filters['double_quote_escape'] = _doubleQuoteEscape
+
+ # Run with --test argument to run unit tests.
+ if len(sys.argv) > 1 and sys.argv[1] == '--test':
+ doctest = importlib.import_module('doctest')
+ doctest.testmod()
+ sys.exit()
+
+ name_to_sub_dir = yaml.safe_load(pathlib.Path(NAME2DIRFILE).read_text())
+
+ tests = []
+ test_yaml_directory = 'yaml-new'
+ TESTSFILES = [
+ os.path.join(test_yaml_directory, f)
+ for f in os.listdir(test_yaml_directory) if f.endswith('.yaml')
+ ]
+ for t in sum(
+ [yaml.safe_load(pathlib.Path(f).read_text()) for f in TESTSFILES], []):
+ if 'DISABLED' in t:
+ continue
+ if 'meta' in t:
+ eval(compile(t['meta'], '<meta test>', 'exec'), {},
+ {'tests': tests})
+ else:
+ tests.append(t)
+
+ # Ensure the test output directories exist.
+ testdirs = [
+ CANVASOUTPUTDIR, OFFSCREENCANVASOUTPUTDIR, CANVASIMAGEOUTPUTDIR,
+ OFFSCREENCANVASIMAGEOUTPUTDIR
+ ]
+ for sub_dir in set(name_to_sub_dir.values()):
+ testdirs.append('%s/%s' % (CANVASOUTPUTDIR, sub_dir))
+ testdirs.append('%s/%s' % (OFFSCREENCANVASOUTPUTDIR, sub_dir))
+ for d in testdirs:
+ try:
+ os.mkdir(d)
+ except FileExistsError:
+ pass # Ignore if it already exists,
+
+ used_tests = collections.defaultdict(set)
+ for original_test in tests:
+ variants = original_test.get('variants', {'': dict()})
+ for variant_name, variant_params in variants.items():
+ test = original_test.copy()
+ if variant_name or variant_params:
+ # Append variant name. Variant names starting with '_' are
+ # not appended, which is useful to create variants with the same
+ # name in different folders (element vs. offscreen).
+ if not variant_name.startswith('_'):
+ test['name'] += '.' + variant_name
+ test.update(variant_params)
+
+ name = test['name']
+ print('\r(%s)' % name, ' ' * 32, '\t')
+
+ enabled_canvas_types = _get_enabled_canvas_types(test)
+
+ already_tested = used_tests[name].intersection(
+ enabled_canvas_types)
+ if already_tested:
+ raise InvalidTestDefinitionError(
+ f'Test {name} is defined twice for types {already_tested}')
+ used_tests[name].update(enabled_canvas_types)
+
+ sub_dir = _get_test_sub_dir(name, name_to_sub_dir)
+ _generate_test(
+ test,
+ jinja_env,
+ sub_dir,
+ enabled_canvas_types,
+ html_canvas_cfg=TestConfig(
+ out_dir=CANVASOUTPUTDIR,
+ image_out_dir=CANVASIMAGEOUTPUTDIR),
+ offscreen_canvas_cfg=TestConfig(
+ out_dir=OFFSCREENCANVASOUTPUTDIR,
+ image_out_dir=OFFSCREENCANVASIMAGEOUTPUTDIR))
+
+ print()
diff --git a/testing/web-platform/tests/html/canvas/tools/name2dir-canvas.yaml b/testing/web-platform/tests/html/canvas/tools/name2dir-canvas.yaml
new file mode 100644
index 0000000000..1e0caff5b9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/name2dir-canvas.yaml
@@ -0,0 +1,26 @@
+2d.transformation: "transformations"
+2d.color.space: 'wide-gamut-canvas'
+2d.composite: "compositing"
+2d.coordinatespace: "conformance-requirements"
+2d.missingargs: "conformance-requirements"
+2d.type.delete: "conformance-requirements"
+2d.voidreturn: "conformance-requirements"
+2d.drawImage: "drawing-images-to-the-canvas"
+2d.clearRect: "drawing-rectangles-to-the-canvas"
+2d.fillRect: "drawing-rectangles-to-the-canvas"
+2d.strokeRect: "drawing-rectangles-to-the-canvas"
+2d.text: "text"
+2d.fillStyle: "fill-and-stroke-styles"
+2d.gradient: "fill-and-stroke-styles"
+2d.pattern: "fill-and-stroke-styles"
+2d.strokeStyle: "fill-and-stroke-styles"
+2d.path: "path-objects"
+2d.imageData: "pixel-manipulation"
+2d.reset: "reset"
+2d.shadow: "shadows"
+2d.filter: "filters"
+2d.state: "the-canvas-state"
+2d.scrollPathIntoView: "scroll"
+2d.video: "video"
+2d.canvas.host: "canvas-host"
+2d.canvas.context: "canvas-context"
diff --git a/testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml b/testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml
new file mode 100644
index 0000000000..c52acb793b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml
@@ -0,0 +1,22 @@
+2d.state: "the-canvas-state"
+2d.transformation: "transformations"
+2d.color.space: "wide-gamut-canvas"
+2d.composite: "compositing"
+2d.fillStyle: "fill-and-stroke-styles"
+2d.gradient: "fill-and-stroke-styles"
+2d.pattern: "fill-and-stroke-styles"
+2d.strokeStyle: "fill-and-stroke-styles"
+2d.shadow: "shadows"
+2d.filter: "filters"
+2d.clearRect: "drawing-rectangles-to-the-canvas"
+2d.fillRect: "drawing-rectangles-to-the-canvas"
+2d.strokeRect: "drawing-rectangles-to-the-canvas"
+2d.drawImage: "drawing-images-to-the-canvas"
+2d.imageData: "pixel-manipulation"
+2d.path: "path-objects"
+2d.text: "text"
+2d.coordinatespace: "conformance-requirements"
+2d.missingargs: "conformance-requirements"
+2d.voidreturn: "conformance-requirements"
+2d.canvas.host: "canvas-host"
+2d.canvas.context: "canvas-context" \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/tools/name2dir.yaml b/testing/web-platform/tests/html/canvas/tools/name2dir.yaml
new file mode 100644
index 0000000000..ae69c15378
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/name2dir.yaml
@@ -0,0 +1,25 @@
+2d.transformation: "transformations"
+2d.color.space: 'wide-gamut-canvas'
+2d.composite: "compositing"
+2d.conformance.requirements: "conformance-requirements"
+2d.drawImage: "drawing-images-to-the-canvas"
+2d.clearRect: "drawing-rectangles-to-the-canvas"
+2d.fillRect: "drawing-rectangles-to-the-canvas"
+2d.strokeRect: "drawing-rectangles-to-the-canvas"
+2d.text: "text"
+2d.fillStyle: "fill-and-stroke-styles"
+2d.gradient: "fill-and-stroke-styles"
+2d.pattern: "fill-and-stroke-styles"
+2d.strokeStyle: "fill-and-stroke-styles"
+2d.line: "line-styles"
+2d.path: "path-objects"
+2d.imageData: "pixel-manipulation"
+2d.reset: "reset"
+2d.shadow: "shadows"
+2d.filter: "filters"
+2d.layer: "layers"
+2d.state: "the-canvas-state"
+2d.scrollPathIntoView: "scroll"
+2d.video: "video"
+2d.canvas.host: "canvas-host"
+2d.canvas.context: "canvas-context"
diff --git a/testing/web-platform/tests/html/canvas/tools/templates.yaml b/testing/web-platform/tests/html/canvas/tools/templates.yaml
new file mode 100644
index 0000000000..0806872cbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates.yaml
@@ -0,0 +1,80 @@
+offscreen: |
+ <!DOCTYPE html>
+ <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+ <title>OffscreenCanvas test: %(name)s</title>%(timeout)s
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/html/canvas/resources/canvas-tests.js"></script>
+
+ <h1>%(name)s</h1>
+ <p class="desc">%(desc)s</p>
+
+ %(notes)s
+ <script>
+ var t = async_test("%(escaped_desc)s");
+ var t_pass = t.done.bind(t);
+ var t_fail = t.step_func(function(reason) {
+ throw reason;
+ });
+ t.step(function() {
+
+ var canvas = new OffscreenCanvas(%(width)s, %(height)s);
+ var ctx = canvas.getContext(%(context_args)s);
+
+ %(code)s
+
+ });
+ </script>
+
+
+worker: |
+ %(timeout_js)s// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+ // OffscreenCanvas test in a worker:%(name)s
+ // Description:%(desc)s
+ // Note:%(notes)s
+
+ importScripts("/resources/testharness.js");
+ importScripts("/html/canvas/resources/canvas-tests.js");
+
+ var t = async_test("%(escaped_desc)s");
+ var t_pass = t.done.bind(t);
+ var t_fail = t.step_func(function(reason) {
+ throw reason;
+ });
+ t.step(function() {
+
+ var canvas = new OffscreenCanvas(%(width)s, %(height)s);
+ var ctx = canvas.getContext(%(context_args)s);
+
+ %(code)s
+ });
+ done();
+
+
+element: |
+ <!DOCTYPE html>
+ <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+ <title>Canvas test: %(name)s</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/html/canvas/resources/canvas-tests.js"></script>
+ <link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+ %(fonts)s<body class="show_output">
+
+ <h1>%(name)s</h1>
+ <p class="desc">%(desc)s</p>
+
+ %(notes)s
+ %(fonthack)s<p class="output">Actual output:</p>
+ <canvas id="c" class="output" width="%(width)s" height="%(height)s"%(canvas)s>%(fallback)s</canvas>
+ %(expected)s
+ <ul id="d"></ul>
+ <script>
+ var t = async_test("%(escaped_desc)s");
+ _addTest(function(canvas, ctx) {
+
+ %(code)s
+
+ }%(attributes)s);
+ </script>
+ %(images)s
diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest.html
new file mode 100644
index 0000000000..4c9affbbbb
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: {{ name }}</title>
+<h1>{{ name }}</h1>
+<p class="desc">{{ desc }}</p>
+{% if notes %}<p class="notes">{{ notes }}{% endif %}
+
+{{ code | trim }}
+{% for image in images %}
+<img src="/images/{{ image }}" id="{{ image }}" class="resource">
+{% endfor -%}
+{% for svgimage in svgimages %}
+<svg><image xlink:href="/images/{{ svgimage }}" id="{{ svgimage }}" class="resource"></svg>
+{% endfor -%}
diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html
new file mode 100644
index 0000000000..6684e9c615
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+{% if test_type == 'promise' %}<html class="reftest-wait">
+{% endif %}
+{% if not is_test_reference %}
+<link rel="match" href="{{ name }}-expected.html">
+{% if fuzzy %}<meta name=fuzzy content="{{ fuzzy }}">
+{% endif %}
+{% endif %}
+{% if timeout %}<meta name="timeout" content="{{ timeout }}">
+{% endif %}
+<title>Canvas test: {{ name }}</title>
+<h1>{{ name }}</h1>
+<p class="desc">{{ desc }}</p>
+{% if notes %}<p class="notes">{{ notes }}{% endif %}
+<canvas id="canvas" width="{{ size[0] }}" height="{{ size[1] }}"{{ canvas }}>
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script{% if test_type == 'promise' %} type="module"{% endif %}>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+
+ {{ code | trim | indent(2) }}
+ {% if test_type == 'promise' %}
+ document.documentElement.classList.remove("reftest-wait");
+ {% endif %}
+</script>
+{% for image in images %}
+<img src="/images/{{ image }}" id="{{ image }}" class="resource">
+{% endfor -%}
+{% for svgimage in svgimages %}
+<svg><image xlink:href="/images/{{ svgimage }}" id="{{ svgimage }}" class="resource"></svg>
+{% endfor -%}
+{% if test_type == 'promise' %}</html>{% endif %}
diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html
new file mode 100644
index 0000000000..1d0d93dfa1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+{% if test_type == 'promise' %}<html class="reftest-wait">
+{% endif %}
+<link rel="match" href="{{ name }}-expected.html">
+{% if fuzzy %}<meta name=fuzzy content="{{ fuzzy }}">
+{% endif %}
+{% if timeout %}<meta name="timeout" content="{{ timeout }}">
+{% endif %}
+<title>Canvas test: {{ name }}</title>
+<h1>{{ name }}</h1>
+<p class="desc">{{ desc }}</p>
+{% if notes %}<p class="notes">{{ notes }}{% endif %}
+<canvas id="canvas" width="{{ size[0] }}" height="{{ size[1] }}"{{ canvas }}>
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script{% if test_type == 'promise' %} type="module"{% endif %}>
+ const canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
+ const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+
+ {{ code | trim | indent(2) }}
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}).drawImage(canvas, 0, 0);
+ {% if test_type == 'promise' %}
+ document.documentElement.classList.remove("reftest-wait");
+ {% endif %}
+</script>
+{% if test_type == 'promise' %}</html>{% endif %}
diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html
new file mode 100644
index 0000000000..e636d3634b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="{{ name }}-expected.html">
+{% if fuzzy %}<meta name=fuzzy content="{{ fuzzy }}">
+{% endif %}
+{% if timeout %}<meta name="timeout" content="{{ timeout }}">
+{% endif %}
+<title>Canvas test: {{ name }}</title>
+<h1>{{ name }}</h1>
+<p class="desc">{{ desc }}</p>
+{% if notes %}<p class="notes">{{ notes }}{% endif %}
+<canvas id="canvas" width="{{ size[0] }}" height="{{ size[1] }}"{{ canvas }}>
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = {% if test_type == 'promise' %}async {% endif %}function(e) {
+ const canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
+ const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+
+ {{ code | trim | indent(4) }}
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html b/testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html
new file mode 100644
index 0000000000..56cde7936a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: {{ name }}</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+{% if fonts %}
+<style>
+{% for font in fonts %}
+@font-face {
+ font-family: {{ font }};
+ src: url("/fonts/{{ font }}.ttf");
+}
+{% endfor %}
+</style>
+{% endif %}
+<body class="show_output">
+
+<h1>{{ name }}</h1>
+<p class="desc">{{ desc }}</p>
+
+{% if notes %}<p class="notes">{{ notes }}
+{% else %}
+
+{% endif %}
+{% if fonts and not font_unused_in_dom %}
+{% for font in fonts %}
+<span style="font-family: {{ font }}; position: absolute; visibility: hidden">A</span>
+{% endfor %}
+{% endif %}
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="{{ size[0] }}" height="{{ size[1] }}"
+ {{- canvas}}><p class="fallback">FAIL (fallback content)</p></canvas>
+{% if expected_img %}
+<p class="output expectedtext">Expected output:<p><img src="{{ expected_img }}" class="output expected" id="expected" alt="">
+{%- endif +%}
+<ul id="d"></ul>
+<script>
+
+{#- Promise vs. async test header: +#}
+{% if test_type == 'promise' %}
+promise_test(async t => {
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+{% else %}
+var t = async_test("{{ desc | double_quote_escape }}");
+_addTest(function(canvas, ctx) {
+{% endif %}
+
+ {# Test body: #}
+ {{ code | trim | indent(2) }}
+
+{# Promise vs. async test footer: #}
+{% if test_type == 'promise' %}
+}, "{{ desc }}");
+{% else %}
+}{% if attributes %}, {{ attributes }}{% endif %});
+{% endif -%}
+
+</script>
+{% for image in images %}
+<img src="/images/{{ image }}" id="{{ image }}" class="resource">
+{% endfor -%}
+{% for svgimage in svgimages %}
+<svg><image xlink:href="/images/{{ svgimage }}" id="{{ svgimage }}" class="resource"></svg>
+{% endfor +%}
diff --git a/testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html b/testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html
new file mode 100644
index 0000000000..8ebbff77a6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: {{ name }}</title>
+{% if timeout %}<meta name="timeout" content="{{ timeout }}">{% endif %}
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>{{ name }}</h1>
+<p class="desc">{{ desc }}</p>
+
+{% if notes %}<p class="notes">{{ notes }}{% endif +%}
+<script>
+
+{#- Promise vs. async test header: +#}
+{% if test_type == 'promise' %}
+promise_test(async t => {
+{% else %}
+var t = async_test("{{ desc | double_quote_escape }}");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+{% endif %}
+
+{# Test body: #}
+ var canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
+ var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+
+ {{ code | trim | indent(2)}}
+
+{#- Promise vs. async test footer: +#}
+{% if test_type == 'promise' %}
+
+}, "{{ desc }}");
+{% else %}
+ t.done();
+
+});
+{% endif %}
+</script>
+{% for svgimage in svgimages %}
+<svg><image xlink:href="/images/{{ svgimage }}" id="{{ svgimage }}" class="resource"></svg>
+{% endfor %}
diff --git a/testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js b/testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js
new file mode 100644
index 0000000000..f1f04e7bb6
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js
@@ -0,0 +1,35 @@
+{% if timeout %}// META: timeout={{ timeout }}{% endif %}
+// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
+// OffscreenCanvas test in a worker:{{ name }}
+// Description:{{ desc }}
+// Note:{% if notes %}<p class="notes">{{ notes }}{% endif +%}
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+{# Promise vs. async test header: #}
+{% if test_type == 'promise' %}
+promise_test(async t => {
+{% else %}
+var t = async_test("{{ desc | double_quote_escape }}");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+{% endif %}
+
+{# Test body: #}
+ var canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
+ var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+
+ {{ code | trim | indent(2)}}
+ t.done();
+
+{#- Promise vs. async test footer: +#}
+{% if test_type == 'promise' %}
+}, "{{ desc }}");
+{% else %}
+});
+{% endif %}
+done();
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml
new file mode 100644
index 0000000000..39556caf0a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml
@@ -0,0 +1,319 @@
+- name: 2d.color.space.p3.to.p3
+ desc: test getImageData with display-p3 and uint8 from display p3 uint8 canvas
+ attributes: '{colorSpace: "display-p3"}'
+ code: |
+ var color_style = 'rgb(50, 100, 150)';
+ // [0.24304, 0.38818, 0.57227, 1.0] * 255 = [62, 99, 146, 255]
+ var pixel_expected = [62, 99, 146, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "display-p3", storageFormat: "uint8"}).data;
+ @assert pixel.length === pixel_expected.length;
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+
+- name: 2d.color.space.p3.to.srgb
+ desc: test getImageData with srsb and uint8 from display p3 uint8 canvas
+ attributes: '{colorSpace: "display-p3"}'
+ code: |
+ var color_style = 'rgb(50, 100, 150)';
+ var pixel_expected = [50, 100, 150, 255];
+ var epsilon = 2;
+ ctx.fillStyle = color_style;
+ ctx.fillRect(0, 0, 10, 10);
+
+ var pixel = ctx.getImageData(5, 5, 1, 1, {colorSpace: "srgb", storageFormat: "uint8"}).data;
+ @assert pixel.length === pixel_expected.length;
+ assert_approx_equals(pixel[0], pixel_expected[0], 2);
+ assert_approx_equals(pixel[1], pixel_expected[1], 2);
+ assert_approx_equals(pixel[2], pixel_expected[2], 2);
+ assert_approx_equals(pixel[3], pixel_expected[3], 2);
+
+- name: 2d.color.space.p3.toBlob.p3.canvas
+ desc: test if toblob returns p3 data from p3 color space canvas
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ ctx.fillRect(0, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ ctx.fillRect(1, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ ctx.fillRect(0, 1, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ ctx.fillRect(1, 1, 1, 1);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+
+ canvas.toBlob(function(blob) {
+ var urlCreator = window.URL || window.webkitURL;
+ image.src = urlCreator.createObjectURL(blob);
+ }, 'image/png', 1);
+
+- name: 2d.color.space.p3.toDataURL.p3.canvas
+ desc: test if toDataURL returns p3 data from canvas with p3 color space
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ ctx.fillRect(0, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ ctx.fillRect(1, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ ctx.fillRect(0, 1, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ ctx.fillRect(1, 1, 1, 1);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ image.src = canvas.toDataURL();
+
+- name: 2d.color.space.p3.toDataURL.jpeg.p3.canvas
+ desc: test if toDataURL('image/jpeg') returns p3 data from canvas with p3 color space
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ ctx.fillRect(0, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ ctx.fillRect(1, 0, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ ctx.fillRect(0, 1, 1, 1);
+ ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ ctx.fillRect(1, 1, 1, 1);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ image.src = canvas.toDataURL("image/jpeg");
+
+- name: 2d.color.space.p3.toBlob.with.putImageData
+ desc: Use putImageData to put some p3 data in canvas and test if toBlob returns the same data
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ canvas.width = 2;
+ canvas.height = 2;
+
+ // Create an ImageData using createImageData and populate its data array.
+ var image_data = ctx.createImageData(canvas.width, canvas.height, {colorSpace: "display-p3"});
+ var color_data = [[255, 100, 150, 1.0], [255, 100, 150, 0.5],
+ [255, 100, 150, 0.5], [255, 100, 150, 0]];
+ var data = image_data.data;
+ for (var i = 0; i < data.length / 4; ++i) {
+ data[4*i + 0] = color_data[i][0];
+ data[4*i + 1] = color_data[i][1];
+ data[4*i + 2] = color_data[i][2];
+ data[4*i + 3] = color_data[i][3];
+ }
+ ctx.putImageData(image_data, 0, 0);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ canvas.toBlob(function(blob) {
+ var urlCreator = window.URL || window.webkitURL;
+ image.src = urlCreator.createObjectURL(blob);
+ }, 'image/png', 1);
+
+- name: 2d.color.space.p3.toDataURL.with.putImageData
+ desc: Use putImageData to put some p3 data in canvas and test if toDataURL returns the same data
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ canvas.width = 2;
+ canvas.height = 2;
+
+ // Create an ImageData using createImageData and populate its data array.
+ var image_data = ctx.createImageData(canvas.width, canvas.height, {colorSpace: "display-p3"});
+ var color_data = [[255, 100, 150, 1.0], [255, 100, 150, 0.5],
+ [255, 100, 150, 0.5], [255, 100, 150, 0]];
+ var data = image_data.data;
+ for (var i = 0; i < data.length / 4; ++i) {
+ data[4*i + 0] = color_data[i][0];
+ data[4*i + 1] = color_data[i][1];
+ data[4*i + 2] = color_data[i][2];
+ data[4*i + 3] = color_data[i][3];
+ }
+ ctx.putImageData(image_data, 0, 0);
+ expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+ var image = new Image();
+ image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+ });
+ image.src = canvas.toDataURL();
+
+- name: 2d.color.space.p3.fillText
+ desc: Test if fillText can be used with a solid display-p3 color
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType: ['HTMLCanvas']
+ code: |
+ deferTest();
+
+ const fullRedInP3 = [255, 0, 0, 255];
+ const sRGBRedInP3 = [234, 51, 35, 255];
+
+ canvas.width = 100;
+ canvas.height = 100;
+
+ let f = new FontFace("Ahem", "url(/fonts/Ahem.ttf)");
+ document.fonts.add(f);
+ f.load().then(function() {
+ t.step(function() {
+ ctx.font = "40px Ahem";
+
+ ctx.fillStyle = "#f00";
+ ctx.fillText("A", 0, 50);
+
+ ctx.fillStyle = "black";
+ ctx.fillStyle = "color(display-p3 100% 0 0)";
+ ctx.fillText("A", 50, 50);
+
+ let pixels = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: "display-p3" }).data;
+ let pixelAt = function(x, y) {
+ let offset = (y * canvas.width + x) * 4;
+ return [pixels[offset], pixels[offset + 1], pixels[offset + 2], pixels[offset + 3]];
+ };
+
+ assert_array_equals(pixelAt(25, 25), sRGBRedInP3);
+ assert_array_equals(pixelAt(75, 25), fullRedInP3);
+
+ t.done();
+ });
+ });
+
+- name: 2d.color.space.p3.strokeText
+ desc: Test if strokeText can be used with a solid display-p3 color
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ deferTest();
+
+ const fullRedInP3 = [255, 0, 0, 255];
+ const sRGBRedInP3 = [234, 51, 35, 255];
+
+ canvas.width = 100;
+ canvas.height = 100;
+
+ let f = new FontFace("Ahem", "url(/fonts/Ahem.ttf)");
+ document.fonts.add(f);
+ f.load().then(function() {
+ t.step(function() {
+ ctx.font = "40px Ahem";
+
+ ctx.strokeStyle = "#f00";
+ ctx.lineWidth = 20;
+ ctx.strokeText("A", 0, 50);
+
+ ctx.strokeStyle = "black";
+ ctx.strokeStyle = "color(display-p3 100% 0 0)";
+ ctx.strokeText("A", 50, 50);
+
+ let pixels = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: "display-p3" }).data;
+ let pixelAt = function(x, y) {
+ let offset = (y * canvas.width + x) * 4;
+ return [pixels[offset], pixels[offset + 1], pixels[offset + 2], pixels[offset + 3]];
+ };
+
+ assert_array_equals(pixelAt(25, 25), sRGBRedInP3);
+ assert_array_equals(pixelAt(75, 25), fullRedInP3);
+
+ t.done();
+ });
+ });
+
+- name: 2d.color.space.p3.fillText.shadow
+ desc: Test if fillText can be used with a display-p3 shadow color
+ attributes: '{colorSpace: "display-p3"}'
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ deferTest();
+
+ const fullRedInP3 = [255, 0, 0, 255];
+ const sRGBRedInP3 = [234, 51, 35, 255];
+
+ canvas.width = 100;
+ canvas.height = 100;
+
+ let f = new FontFace("Ahem", "url(/fonts/Ahem.ttf)");
+ document.fonts.add(f);
+ f.load().then(function() {
+ t.step(function() {
+ ctx.font = "40px Ahem";
+
+ ctx.fillStyle = "black";
+ ctx.shadowBlur = 4;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = "#f00";
+ ctx.fillText("A", 0, 0);
+
+ ctx.shadowColor = "black";
+ ctx.shadowColor = "color(display-p3 100% 0 0)";
+ ctx.fillText("A", 50, 0);
+
+ let pixels = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: "display-p3" }).data;
+ let pixelAt = function(x, y) {
+ let offset = (y * canvas.width + x) * 4;
+ return [pixels[offset], pixels[offset + 1], pixels[offset + 2], pixels[offset + 3]];
+ };
+
+ assert_array_equals(pixelAt(25, 25), sRGBRedInP3);
+ assert_array_equals(pixelAt(75, 25), fullRedInP3);
+
+ t.done();
+ });
+ });
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml
new file mode 100644
index 0000000000..bd7fae1d62
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml
@@ -0,0 +1,232 @@
+- name: 2d.composite.globalAlpha.range
+ code: |
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ @assert ctx.globalAlpha === a;
+ ctx.globalAlpha = 1.1;
+ @assert ctx.globalAlpha === a;
+ ctx.globalAlpha = -0.1;
+ @assert ctx.globalAlpha === a;
+ ctx.globalAlpha = 0;
+ @assert ctx.globalAlpha === 0;
+ ctx.globalAlpha = 1;
+ @assert ctx.globalAlpha === 1;
+
+- name: 2d.composite.globalAlpha.invalid
+ code: |
+ ctx.globalAlpha = 0.5;
+ // This may not set it to exactly 0.5 if it is rounded/quantised, so
+ // remember for future comparisons.
+ var a = ctx.globalAlpha;
+ ctx.globalAlpha = Infinity;
+ @assert ctx.globalAlpha === a;
+ ctx.globalAlpha = -Infinity;
+ @assert ctx.globalAlpha === a;
+ ctx.globalAlpha = NaN;
+ @assert ctx.globalAlpha === a;
+
+- name: 2d.composite.globalAlpha.default
+ code: |
+ @assert ctx.globalAlpha === 1.0;
+
+- name: 2d.composite.globalAlpha.fill
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 2,253,0,255;
+ expected: green
+
+- name: 2d.composite.globalAlpha.canvas
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.drawImage(canvas2, 0, 0);
+ @assert pixel 50,25 ==~ 2,253,0,255;
+ expected: green
+
+- name: 2d.composite.globalAlpha.canvaspattern
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = ctx.createPattern(canvas2, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 2,253,0,255;
+ expected: green
+
+- name: 2d.composite.globalAlpha.canvascopy
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.globalCompositeOperation = 'copy'
+ ctx.globalAlpha = 0.51;
+ ctx.drawImage(canvas2, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,130;
+ expected: green
+
+
+- name: 2d.composite.operation.get
+ code: |
+ var modes = ['source-atop', 'source-in', 'source-out', 'source-over',
+ 'destination-atop', 'destination-in', 'destination-out', 'destination-over',
+ 'lighter', 'copy', 'xor'];
+ for (var i = 0; i < modes.length; ++i)
+ {
+ ctx.globalCompositeOperation = modes[i];
+ @assert ctx.globalCompositeOperation === modes[i];
+ }
+
+- name: 2d.composite.operation.unrecognised
+ code: |
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'nonexistent';
+ @assert ctx.globalCompositeOperation === 'xor';
+
+- name: 2d.composite.operation.darker
+ code: |
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'darker';
+ @assert ctx.globalCompositeOperation === 'xor';
+
+- name: 2d.composite.operation.over
+ code: |
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'over';
+ @assert ctx.globalCompositeOperation === 'xor';
+
+- name: 2d.composite.operation.clear
+ code: |
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'clear';
+ @assert ctx.globalCompositeOperation === 'clear';
+
+- name: 2d.composite.operation.highlight
+ code: |
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'highlight';
+ @assert ctx.globalCompositeOperation === 'xor';
+
+- name: 2d.composite.operation.nullsuffix
+ code: |
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'source-over\0';
+ @assert ctx.globalCompositeOperation === 'xor';
+
+- name: 2d.composite.operation.casesensitive
+ code: |
+ ctx.globalCompositeOperation = 'xor';
+ ctx.globalCompositeOperation = 'Source-over';
+ @assert ctx.globalCompositeOperation === 'xor';
+
+- name: 2d.composite.operation.default
+ code: |
+ @assert ctx.globalCompositeOperation === 'source-over';
+
+
+- name: 2d.composite.globalAlpha.image
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ @assert pixel 50,25 ==~ 2,253,0,255;
+ expected: green
+
+- name: 2d.composite.globalAlpha.canvas
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ @assert pixel 50,25 ==~ 2,253,0,255;
+
+- name: 2d.composite.globalAlpha.imagepattern
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 2,253,0,255;
+ expected: green
+
+- name: 2d.composite.globalAlpha.canvaspattern
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = ctx.createPattern(offscreenCanvas2, 'no-repeat');
+ // Avoiding any potential alpha = 0 optimisations.
+ ctx.globalAlpha = 0.01;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 2,253,0,255;
+
+- name: 2d.composite.globalAlpha.canvascopy
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'copy'
+ ctx.globalAlpha = 0.51;
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,130;
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml
new file mode 100644
index 0000000000..3483d115f4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml
@@ -0,0 +1,178 @@
+- name: 2d.conformance.requirements.delete
+ desc: window.CanvasRenderingContext2D is Configurable
+ notes: &bindings Defined in "Web IDL" (draft)
+ canvasType: ['HTMLCanvas']
+ code: |
+ @assert window.CanvasRenderingContext2D !== undefined;
+ @assert delete window.CanvasRenderingContext2D === true;
+ @assert window.CanvasRenderingContext2D === undefined;
+
+- name: 2d.conformance.requirements.basics
+ desc: void methods return undefined
+ notes: *bindings
+ code: |
+ @assert ctx.save() === undefined;
+ @assert ctx.restore() === undefined;
+ @assert ctx.scale(1, 1) === undefined;
+ @assert ctx.rotate(0) === undefined;
+ @assert ctx.translate(0, 0) === undefined;
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ @assert ctx.transform(1, 0, 0, 1, 0, 0) === undefined;
+ }
+ if (ctx.setTransform) {
+ @assert ctx.setTransform(1, 0, 0, 1, 0, 0) === undefined;
+ @assert ctx.setTransform() === undefined;
+ }
+ @assert ctx.clearRect(0, 0, 0, 0) === undefined;
+ @assert ctx.fillRect(0, 0, 0, 0) === undefined;
+ @assert ctx.strokeRect(0, 0, 0, 0) === undefined;
+ @assert ctx.beginPath() === undefined;
+ @assert ctx.closePath() === undefined;
+ @assert ctx.moveTo(0, 0) === undefined;
+ @assert ctx.lineTo(0, 0) === undefined;
+ @assert ctx.quadraticCurveTo(0, 0, 0, 0) === undefined;
+ @assert ctx.bezierCurveTo(0, 0, 0, 0, 0, 0) === undefined;
+ @assert ctx.arcTo(0, 0, 0, 0, 1) === undefined;
+ @assert ctx.rect(0, 0, 0, 0) === undefined;
+ @assert ctx.arc(0, 0, 1, 0, 0, true) === undefined;
+ @assert ctx.fill() === undefined;
+ @assert ctx.stroke() === undefined;
+ @assert ctx.clip() === undefined;
+ if (ctx.fillText) {
+ @assert ctx.fillText('test', 0, 0) === undefined;
+ @assert ctx.strokeText('test', 0, 0) === undefined;
+ }
+ if (ctx.putImageData) {
+ @assert ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0) === undefined;
+ }
+ @assert ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0) === undefined;
+ @assert ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white') === undefined;
+
+- name: 2d.conformance.requirements.missingargs
+ desc: Missing arguments cause TypeError
+ code: |
+ @assert throws TypeError ctx.scale();
+ @assert throws TypeError ctx.scale(1);
+ @assert throws TypeError ctx.rotate();
+ @assert throws TypeError ctx.translate();
+ @assert throws TypeError ctx.translate(0);
+ if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
+ @assert throws TypeError ctx.transform();
+ @assert throws TypeError ctx.transform(1);
+ @assert throws TypeError ctx.transform(1, 0);
+ @assert throws TypeError ctx.transform(1, 0, 0);
+ @assert throws TypeError ctx.transform(1, 0, 0, 1);
+ @assert throws TypeError ctx.transform(1, 0, 0, 1, 0);
+ }
+ if (ctx.setTransform) {
+ @assert throws TypeError ctx.setTransform(1);
+ @assert throws TypeError ctx.setTransform(1, 0);
+ @assert throws TypeError ctx.setTransform(1, 0, 0);
+ @assert throws TypeError ctx.setTransform(1, 0, 0, 1);
+ @assert throws TypeError ctx.setTransform(1, 0, 0, 1, 0);
+ }
+ @assert throws TypeError ctx.createLinearGradient();
+ @assert throws TypeError ctx.createLinearGradient(0);
+ @assert throws TypeError ctx.createLinearGradient(0, 0);
+ @assert throws TypeError ctx.createLinearGradient(0, 0, 1);
+ @assert throws TypeError ctx.createRadialGradient();
+ @assert throws TypeError ctx.createRadialGradient(0);
+ @assert throws TypeError ctx.createRadialGradient(0, 0);
+ @assert throws TypeError ctx.createRadialGradient(0, 0, 1);
+ @assert throws TypeError ctx.createRadialGradient(0, 0, 1, 0);
+ @assert throws TypeError ctx.createRadialGradient(0, 0, 1, 0, 0);
+ @assert throws TypeError ctx.createPattern(canvas);
+ @assert throws TypeError ctx.clearRect();
+ @assert throws TypeError ctx.clearRect(0);
+ @assert throws TypeError ctx.clearRect(0, 0);
+ @assert throws TypeError ctx.clearRect(0, 0, 0);
+ @assert throws TypeError ctx.fillRect();
+ @assert throws TypeError ctx.fillRect(0);
+ @assert throws TypeError ctx.fillRect(0, 0);
+ @assert throws TypeError ctx.fillRect(0, 0, 0);
+ @assert throws TypeError ctx.strokeRect();
+ @assert throws TypeError ctx.strokeRect(0);
+ @assert throws TypeError ctx.strokeRect(0, 0);
+ @assert throws TypeError ctx.strokeRect(0, 0, 0);
+ @assert throws TypeError ctx.moveTo();
+ @assert throws TypeError ctx.moveTo(0);
+ @assert throws TypeError ctx.lineTo();
+ @assert throws TypeError ctx.lineTo(0);
+ @assert throws TypeError ctx.quadraticCurveTo();
+ @assert throws TypeError ctx.quadraticCurveTo(0);
+ @assert throws TypeError ctx.quadraticCurveTo(0, 0);
+ @assert throws TypeError ctx.quadraticCurveTo(0, 0, 0);
+ @assert throws TypeError ctx.bezierCurveTo();
+ @assert throws TypeError ctx.bezierCurveTo(0);
+ @assert throws TypeError ctx.bezierCurveTo(0, 0);
+ @assert throws TypeError ctx.bezierCurveTo(0, 0, 0);
+ @assert throws TypeError ctx.bezierCurveTo(0, 0, 0, 0);
+ @assert throws TypeError ctx.bezierCurveTo(0, 0, 0, 0, 0);
+ @assert throws TypeError ctx.arcTo();
+ @assert throws TypeError ctx.arcTo(0);
+ @assert throws TypeError ctx.arcTo(0, 0);
+ @assert throws TypeError ctx.arcTo(0, 0, 0);
+ @assert throws TypeError ctx.arcTo(0, 0, 0, 0);
+ @assert throws TypeError ctx.rect();
+ @assert throws TypeError ctx.rect(0);
+ @assert throws TypeError ctx.rect(0, 0);
+ @assert throws TypeError ctx.rect(0, 0, 0);
+ @assert throws TypeError ctx.arc();
+ @assert throws TypeError ctx.arc(0);
+ @assert throws TypeError ctx.arc(0, 0);
+ @assert throws TypeError ctx.arc(0, 0, 1);
+ @assert throws TypeError ctx.arc(0, 0, 1, 0);
+ // (6th argument to arc is optional)
+ if (ctx.isPointInPath) {
+ @assert throws TypeError ctx.isPointInPath();
+ @assert throws TypeError ctx.isPointInPath(0);
+ }
+ if (ctx.drawFocusRing) {
+ @assert throws TypeError ctx.drawFocusRing();
+ @assert throws TypeError ctx.drawFocusRing(canvas);
+ @assert throws TypeError ctx.drawFocusRing(canvas, 0);
+ }
+ if (ctx.fillText) {
+ @assert throws TypeError ctx.fillText();
+ @assert throws TypeError ctx.fillText('test');
+ @assert throws TypeError ctx.fillText('test', 0);
+ @assert throws TypeError ctx.strokeText();
+ @assert throws TypeError ctx.strokeText('test');
+ @assert throws TypeError ctx.strokeText('test', 0);
+ @assert throws TypeError ctx.measureText();
+ }
+ @assert throws TypeError ctx.drawImage();
+ @assert throws TypeError ctx.drawImage(canvas);
+ @assert throws TypeError ctx.drawImage(canvas, 0);
+ // TODO: n >= 3 args on drawImage could be either a valid overload,
+ // or too few for another overload, or too many for another
+ // overload - what should happen?
+ if (ctx.createImageData) {
+ @assert throws TypeError ctx.createImageData();
+ @assert throws TypeError ctx.createImageData(1);
+ }
+ if (ctx.getImageData) {
+ @assert throws TypeError ctx.getImageData();
+ @assert throws TypeError ctx.getImageData(0);
+ @assert throws TypeError ctx.getImageData(0, 0);
+ @assert throws TypeError ctx.getImageData(0, 0, 1);
+ }
+ if (ctx.putImageData) {
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ @assert throws TypeError ctx.putImageData();
+ @assert throws TypeError ctx.putImageData(imgdata);
+ @assert throws TypeError ctx.putImageData(imgdata, 0);
+ }
+ var g = ctx.createLinearGradient(0, 0, 0, 0);
+ @assert throws TypeError g.addColorStop(); @moz-todo
+ @assert throws TypeError g.addColorStop(0); @moz-todo
+
+
+- name: 2d.conformance.requirements.drawings
+ desc: void methods return undefined
+ images:
+ - yellow.png
+ canvasType: ['HTMLCanvas']
+ code: |
+ @assert ctx.drawImage(document.getElementById('yellow.png'), 0, 0, 1, 1, 0, 0, 0, 0) === undefined;
+
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml
new file mode 100644
index 0000000000..93c556288d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml
@@ -0,0 +1,640 @@
+- name: 2d.drawImage.canvas
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.drawImage(canvas2, 0, 0);
+
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+
+ ctx.drawImage(document.createElement('canvas'), 0, 0);
+
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.self.1
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.drawImage(canvas, 50, 0);
+
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.self.2
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 1, 100, 49);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 1);
+ ctx.drawImage(canvas, 0, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 2);
+
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.null
+ code: |
+ @assert throws TypeError ctx.drawImage(null, 0, 0);
+
+- name: 2d.drawImage.wrongtype
+ desc: Incorrect image types in drawImage do not match any defined overloads, so
+ WebIDL throws a TypeError
+ code: |
+ @assert throws TypeError ctx.drawImage(undefined, 0, 0);
+ @assert throws TypeError ctx.drawImage(0, 0, 0);
+ @assert throws TypeError ctx.drawImage("", 0, 0);
+
+- name: 2d.drawImage.wrongtype.paragraph
+ desc: Incorrect image types in drawImage do not match any defined overloads, so
+ WebIDL throws a TypeError
+ notes: &bindings Defined in "Web IDL" (draft)
+ canvasType: ['HTMLCanvas']
+ code: |
+ @assert throws TypeError ctx.drawImage(document.createElement('p'), 0, 0);
+
+- name: 2d.drawImage.incomplete.nosrc
+ canvasType: ['HTMLCanvas']
+ mozilla: {throws: !!null ''}
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = new Image();
+ ctx.drawImage(img, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.incomplete.immediate
+ canvasType: ['HTMLCanvas']
+ images:
+ - red.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = new Image();
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ ctx.drawImage(img, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.drawImage.incomplete.reload
+ canvasType: ['HTMLCanvas']
+ images:
+ - yellow.png
+ - red.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('yellow.png');
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm,
+ // and resets the image to the "unavailable" state.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ ctx.drawImage(img, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.drawImage.incomplete.emptysrc
+ canvasType: ['HTMLCanvas']
+ images:
+ - red.png
+ mozilla: {throws: !!null ''}
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('red.png');
+ img.src = "";
+ ctx.drawImage(img, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.incomplete.removedsrc
+ canvasType: ['HTMLCanvas']
+ images:
+ - red.png
+ mozilla: {throws: !!null ''}
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = document.getElementById('red.png');
+ img.removeAttribute('src');
+ ctx.drawImage(img, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.nonexistent
+ canvasType: ['HTMLCanvas']
+ images:
+ - not-found-at-all.png
+ code: |
+ var img = document.getElementById('not-found-at-all.png');
+ @assert throws INVALID_STATE_ERR ctx.drawImage(img, 0, 0);
+
+- name: 2d.drawImage.zerocanvas
+ desc: drawImage with zero-sized canvas as the source shoud throw exception
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 0;
+ canvas2.height = 50;
+ @assert throws INVALID_STATE_ERR ctx.drawImage(canvas2, 0, 0);
+
+ canvas2.width = 50;
+ canvas2.height = 0;
+ @assert throws INVALID_STATE_ERR ctx.drawImage(canvas2, 0, 0);
+
+ canvas2.width = 0;
+ canvas2.height = 0;
+ @assert throws INVALID_STATE_ERR ctx.drawImage(canvas2, 0, 0);
+
+- name: 2d.drawImage.animated.gif
+ desc: drawImage() of an animated GIF draws the first frame
+ canvasType: ['HTMLCanvas']
+ images:
+ - anim-gr.gif
+ code: |
+ deferTest();
+ step_timeout(t.step_func_done(function () {
+ ctx.drawImage(document.getElementById('anim-gr.gif'), 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ }), 500);
+ expected: green
+
+# TODO: drawImage shadows
+
+- name: 2d.drawImage.3arg
+ test_type: promise
+ code: |
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0);
+ ctx.drawImage(bitmap_red, -100, 0);
+ ctx.drawImage(bitmap_red, 100, 0);
+ ctx.drawImage(bitmap_red, 0, -50);
+ ctx.drawImage(bitmap_red, 0, 50);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+
+- name: 2d.drawImage.5arg
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+
+- name: 2d.drawImage.9arg.basic
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, 50);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.9arg.sourcepos
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 140, 20, 100, 50, 0, 0, 100, 50);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.9arg.sourcesize
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/rgrg-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 0, 256, 256, 0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 51, 26);
+ ctx.fillRect(49, 24, 51, 26);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+ @assert pixel 20,20 ==~ 0,255,0,255;
+ @assert pixel 80,20 ==~ 0,255,0,255;
+ @assert pixel 20,30 ==~ 0,255,0,255;
+ @assert pixel 80,30 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.9arg.destpos
+ test_type: promise
+ code: |
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 0, 0, 100, 50, -100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -50, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 50);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+
+- name: 2d.drawImage.9arg.destsize
+ test_type: promise
+ code: |
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 1, 1, 1, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, -50, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 100, 0, 50, 50);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, -25, 100, 25);
+ ctx.drawImage(bitmap_red, 0, 0, 100, 50, 0, 50, 100, 25);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+
+- name: 2d.drawImage.canvas
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ @assert pixel 0,0 ==~ 0,255,0,255;
+ @assert pixel 99,0 ==~ 0,255,0,255;
+ @assert pixel 0,49 ==~ 0,255,0,255;
+ @assert pixel 99,49 ==~ 0,255,0,255;
+
+- name: 2d.drawImage.zerocanvas
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(0, 10);
+ @assert throws INVALID_STATE_ERR ctx.drawImage(offscreenCanvas2, 0, 0);
+
+ offscreenCanvas2.width = 10;
+ offscreenCanvas2.height = 0;
+ @assert throws INVALID_STATE_ERR ctx.drawImage(offscreenCanvas2, 0, 0);
+
+ offscreenCanvas2.width = 0;
+ offscreenCanvas2.height = 0;
+ @assert throws INVALID_STATE_ERR ctx.drawImage(offscreenCanvas2, 0, 0);
+
+- name: 2d.drawImage.floatsource
+ test_type: promise
+ code: |
+ const response = await fetch('/images/green.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ Expected: green
+
+- name: 2d.drawImage.zerosource
+ desc: drawImage with zero-sized source rectangle draws nothing without exception
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 10, 10, 0, 1, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50);
+ ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.zerosource.image
+ desc: drawImage with zero-sized source rectangle from image draws nothing without exception
+ test_type: promise
+ canvasType: ['HTMLCanvas', 'OffscreenCanvas']
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ function loadImage(src) {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = () => resolve(img);
+ img.onerror = (err) => reject(err);
+ img.src = src;
+ });
+ }
+ const img1 = await loadImage('/images/red-zerowidth.svg');
+ const img2 = await loadImage('/images/red-zeroheight.svg');
+ const img3 = await loadImage('/images/red-zerosize.svg');
+
+ ctx.drawImage(img1, 0, 0, 100, 50);
+ ctx.drawImage(img2, 0, 0, 100, 50);
+ ctx.drawImage(img3, 0, 0, 100, 50);
+ _assertPixel(canvas, 50, 25, 0, 255, 0, 255);
+ expected: green
+
+
+- name: 2d.drawImage.negativesource
+ desc: Negative source width/height represents the correct rectangle
+ mozilla: {throws: !!null ''}
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, -100, 50, 0, 0, 50, 50);
+ ctx.drawImage(bitmap, 100, 128, -100, -50, 50, 0, 50, 50);
+ @assert pixel 1,1 ==~ 0,255,0,255;
+ @assert pixel 1,48 ==~ 0,255,0,255;
+ @assert pixel 98,1 ==~ 0,255,0,255;
+ @assert pixel 98,48 ==~ 0,255,0,255;
+ @assert pixel 48,1 ==~ 0,255,0,255;
+ @assert pixel 48,48 ==~ 0,255,0,255;
+ @assert pixel 51,1 ==~ 0,255,0,255;
+ @assert pixel 51,48 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.negativedest
+ desc: Negative destination width/height represents the correct rectangle
+ mozilla: {throws: !!null ''}
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 100, 78, 50, 50, 0, 50, 50, -50);
+ ctx.drawImage(bitmap, 100, 128, 50, -50, 100, 50, -50, -50);
+ @assert pixel 1,1 ==~ 0,255,0,255;
+ @assert pixel 1,48 ==~ 0,255,0,255;
+ @assert pixel 98,1 ==~ 0,255,0,255;
+ @assert pixel 98,48 ==~ 0,255,0,255;
+ @assert pixel 48,1 ==~ 0,255,0,255;
+ @assert pixel 48,48 ==~ 0,255,0,255;
+ @assert pixel 51,1 ==~ 0,255,0,255;
+ @assert pixel 51,48 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.negativedir
+ desc: Negative dimensions do not affect the direction of the image
+ mozilla: {throws: !!null ''}
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/ggrr-256x256.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+ ctx.drawImage(bitmap, 0, 178, 50, -100, 0, 0, 50, 100);
+ ctx.drawImage(bitmap, 0, 78, 50, 100, 50, 100, 50, -100);
+ @assert pixel 1,1 ==~ 0,255,0,255;
+ @assert pixel 1,48 ==~ 0,255,0,255;
+ @assert pixel 98,1 ==~ 0,255,0,255;
+ @assert pixel 98,48 ==~ 0,255,0,255;
+ @assert pixel 48,1 ==~ 0,255,0,255;
+ @assert pixel 48,48 ==~ 0,255,0,255;
+ @assert pixel 51,1 ==~ 0,255,0,255;
+ @assert pixel 51,48 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.outsidesource
+ DISABLED: fix this to match the current spec (transparent black outside source)
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ const response_red = await fetch('/images/red.png');
+ const blob_red = await response_red.blob();
+ const bitmap_red = await createImageBitmap(blob_red);
+
+ const response_green = await fetch('/images/green.png');
+ const blob_green = await response_green.blob();
+ const bitmap_green = await createImageBitmap(blob_green);
+ ctx.drawImage(bitmap_green, 10.5, 10.5, 89.5, 39.5, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 5.5, 5.5, -5.5, -5.5, 0, 0, 100, 50);
+ ctx.drawImage(bitmap_green, 100, 50, -5, -5, 0, 0, 100, 50);
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, -0.001, 0, 100, 50, 0, 0, 100, 50);
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, -0.001, 100, 50, 0, 0, 100, 50);
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, 100.001, 50, 0, 0, 100, 50);
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, 100, 50.001, 0, 0, 100, 50);
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 50, 0, 50.001, 50, 0, 0, 100, 50); @moz-todo
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, -5, 5, 0, 0, 100, 50);
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, 5, -5, 0, 0, 100, 50);
+ @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 110, 60, -20, -20, 0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.drawImage.broken
+ test_type: promise
+ code: |
+ const response = await fetch('/images/broken.png');
+ const blob = await response.blob();
+
+ await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(blob), 'The source image could not be decoded.');
+ expected: green
+
+- name: 2d.drawImage.svg
+ desc: drawImage() of an SVG image
+ test_type: promise
+ canvasType: ['HTMLCanvas', 'OffscreenCanvas']
+ code: |
+ const img = new Image();
+ const imageLoadPromise = new Promise((resolve, reject) => {
+ img.onload = () => resolve();
+ img.onerror = (err) => reject(err);
+ });
+ img.src = '/images/green.svg';
+ await imageLoadPromise;
+
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50, 25, 0, 255, 0, 255, 2);
+ expected: green
+
+- name: 2d.drawImage.path
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ ctx.fill();
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.transform
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(100, 0);
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.alpha
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0;
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.clip
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(-10, -10, 1, 1);
+ ctx.clip();
+ ctx.drawImage(bitmap, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.composite
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-over';
+ const response = await fetch('/images/red.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.nowrap
+ desc: Stretched images do not get pixels wrapping around the edges
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ ctx.drawImage(bitmap, -1950, 0, 2000, 50);
+ @assert pixel 45,25 ==~ 0,255,0,255;
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 55,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.drawImage.nonfinite
+ desc: drawImage() with Infinity/NaN is ignored
+ test_type: promise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ const response = await fetch('/images/redtransparent.png');
+ const blob = await response.blob();
+ const bitmap = await createImageBitmap(blob);
+
+ @nonfinite ctx.drawImage(<bitmap>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
+ @nonfinite ctx.drawImage(<bitmap>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ @nonfinite ctx.drawImage(<bitmap>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml
new file mode 100644
index 0000000000..408e932abe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml
@@ -0,0 +1,469 @@
+- name: 2d.clearRect.basic
+ desc: clearRect clears to transparent black
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.clearRect.path
+ desc: clearRect does not affect the current path
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 16, 16);
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.clearRect.zero
+ desc: clearRect of zero pixels has no effect
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 100, 0);
+ ctx.clearRect(0, 0, 0, 50);
+ ctx.clearRect(0, 0, 0, 0);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.clearRect.negative
+ desc: clearRect of negative sizes works
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.clearRect(0, 0, 50, 25);
+ ctx.clearRect(100, 0, -50, 25);
+ ctx.clearRect(0, 50, 50, -25);
+ ctx.clearRect(100, 50, -50, -25);
+ @assert pixel 25,12 == 0,0,0,0;
+ @assert pixel 75,12 == 0,0,0,0;
+ @assert pixel 25,37 == 0,0,0,0;
+ @assert pixel 75,37 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.clearRect.transform
+ desc: clearRect is affected by transforms
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.clearRect(0, -5, 10, 5);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.clearRect.globalalpha
+ desc: clearRect is not affected by globalAlpha
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalAlpha = 0.1;
+ ctx.clearRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.clearRect.globalcomposite
+ desc: clearRect is not affected by globalCompositeOperation
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.clearRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.clearRect.clip
+ desc: clearRect is affected by clipping regions
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.clearRect.shadow
+ desc: clearRect does not draw shadows
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.clearRect(0, -50, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.clearRect.nonfinite
+ desc: clearRect() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @nonfinite ctx.clearRect(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.fillRect.basic
+ desc: fillRect works
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillRect.path
+ desc: fillRect does not affect the current path
+ code: |
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillRect.zero
+ desc: fillRect of zero pixels has no effect
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 0);
+ ctx.fillRect(0, 0, 0, 50);
+ ctx.fillRect(0, 0, 0, 0);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillRect.negative
+ desc: fillRect of negative sizes works
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillRect(100, 0, -50, 25);
+ ctx.fillRect(0, 50, 50, -25);
+ ctx.fillRect(100, 50, -50, -25);
+ @assert pixel 25,12 == 0,255,0,255;
+ @assert pixel 75,12 == 0,255,0,255;
+ @assert pixel 25,37 == 0,255,0,255;
+ @assert pixel 75,37 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillRect.transform
+ desc: fillRect is affected by transforms
+ code: |
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -5, 10, 5);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+# don't bother testing globalalpha, globalcomposite because they're already heavily used by other test cases
+
+- name: 2d.fillRect.clip
+ desc: fillRect is affected by clipping regions
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillRect.shadow
+ desc: fillRect draws shadows
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillRect.nonfinite
+ desc: fillRect() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ @nonfinite ctx.fillRect(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.strokeRect.basic
+ desc: strokeRect works
+ code: |
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.path
+ desc: strokeRect does not affect the current path
+ code: |
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(0, 0, 16, 16);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.zero.1
+ desc: strokeRect of 0x0 pixels draws nothing
+ code: |
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.strokeRect(50, 25, 0, 0);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.strokeRect.zero.2
+ desc: strokeRect of 0x0 pixels draws nothing, including caps and joins
+ code: |
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(50, 25, 0, 0);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.strokeRect.zero.3
+ desc: strokeRect of Nx0 pixels draws a straight line
+ code: |
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 25, 100, 0);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.zero.4
+ desc: strokeRect of Nx0 pixels draws a closed line with no caps
+ code: |
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 250;
+ ctx.lineCap = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.strokeRect.zero.5
+ desc: strokeRect of Nx0 pixels draws a closed line with joins
+ code: |
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 250;
+ ctx.lineJoin = 'round';
+ ctx.strokeRect(100, 25, 100, 0);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.negative
+ desc: strokeRect of negative sizes works
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 25;
+ ctx.strokeRect(12, 12, 26, 1);
+ ctx.strokeRect(88, 12, -26, 1);
+ ctx.strokeRect(12, 38, 26, -1);
+ ctx.strokeRect(88, 38, -26, -1);
+ @assert pixel 25,12 == 0,255,0,255;
+ @assert pixel 75,12 == 0,255,0,255;
+ @assert pixel 25,37 == 0,255,0,255;
+ @assert pixel 75,37 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.transform
+ desc: fillRect is affected by transforms
+ code: |
+ ctx.scale(10, 10);
+ ctx.translate(0, 5);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 5;
+ ctx.strokeRect(2.5, -2.6, 5, 0.2);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.globalalpha
+ desc: strokeRect is affected by globalAlpha
+ code: |
+ ctx.globalAlpha = 0;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.strokeRect.globalcomposite
+ desc: strokeRect is not affected by globalCompositeOperation
+ code: |
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,0,0,0;
+ expected: clear
+
+- name: 2d.strokeRect.clip
+ desc: strokeRect is affected by clipping regions
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.rect(0, 0, 16, 16);
+ ctx.clip();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 16);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.shadow
+ desc: strokeRect draws shadows
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0;
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.strokeRect(0, -75, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeRect.nonfinite
+ desc: strokeRect() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 150;
+ @nonfinite ctx.strokeRect(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillStyle.colorObject
+ desc: ctx.fillStyle works with color objects
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ ctx.fillStyle = {r: 1, g: 0, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 255,0,0,255;
+ ctx.fillStyle = {r: 0, g: 0, b: 1};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,255,255;
+ ctx.fillStyle = {r: 0.2, g: 0.4, b: 0.6};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 51,102,153,255;
+ ctx.fillStyle = {r: 0, g: 1, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ ctx.fillStyle = {r: -1, g: 0, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,255;
+ ctx.fillStyle = {r: 0, g: 2, b: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillStyle.colorObject.transparency
+ desc: ctx.fillStyle with color objects has transparency
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: 0};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,0;
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: -1};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,0;
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: 0.5};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,128;
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.fillStyle = {r: 0, g: 1, b: 0, a: 1};
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeStyle.colorObject
+ desc: ctx.strokeStyle works with color objects
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = {r: 1, g: 0, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 255,0,0,255;
+ ctx.strokeStyle = {r: 0, g: 0, b: 1};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,0,255,255;
+ ctx.strokeStyle = {r: 0.2, g: 0.4, b: 0.6};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 51,102,153,255;
+ ctx.strokeStyle = {r: 0, g: 1, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,255,0,255;
+ ctx.strokeStyle = {r: -1, g: 0, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,0,0,255;
+ ctx.strokeStyle = {r: 0, g: 2, b: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.strokeStyle.colorObject.transparency
+ desc: ctx.strokeStyle with color objects has transparency
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: 0};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,0,0,0;
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: -1};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,0,0,0;
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: 0.5};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,255,0,128;
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.strokeStyle = {r: 0, g: 1, b: 0, a: 1};
+ ctx.strokeRect(25, 24, 50, 2);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml
new file mode 100644
index 0000000000..c992af6e7c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml
@@ -0,0 +1,2104 @@
+- name: 2d.fillStyle.parse.current.basic
+ desc: currentColor is computed from the canvas element
+ canvasType: ['HtmlCanvas']
+ code: |
+ canvas.setAttribute('style', 'color: #0f0');
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'currentColor';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillStyle.parse.current.changed
+ desc: currentColor is computed when the attribute is set, not when it is painted
+ canvasType: ['HtmlCanvas']
+ code: |
+ canvas.setAttribute('style', 'color: #0f0');
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = 'currentColor';
+ canvas.setAttribute('style', 'color: #f00');
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillStyle.parse.current.removed
+ desc: currentColor is solid black when the canvas element is not in a document
+ canvasType: ['HtmlCanvas']
+ code: |
+ // Try not to let it undetectably incorrectly pick up opaque-black
+ // from other parts of the document:
+ document.body.parentNode.setAttribute('style', 'color: #f00');
+ document.body.setAttribute('style', 'color: #f00');
+ canvas.setAttribute('style', 'color: #f00');
+
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillStyle = 'currentColor';
+ ctx2.fillRect(0, 0, 100, 50);
+ ctx.drawImage(canvas2, 0, 0);
+
+ document.body.parentNode.removeAttribute('style');
+ document.body.removeAttribute('style');
+
+ @assert pixel 50,25 == 0,0,0,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0, 0, 0)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.fillStyle.invalidstring
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = 'invalid';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillStyle.invalidtype
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillStyle = null;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.fillStyle.get.solid
+ code: |
+ ctx.fillStyle = '#fa0';
+ @assert ctx.fillStyle === '#ffaa00';
+
+- name: 2d.fillStyle.get.semitransparent
+ code: |
+ ctx.fillStyle = 'rgba(255,255,255,0.45)';
+ @assert ctx.fillStyle =~ /^rgba\(255, 255, 255, 0\.4\d+\)$/;
+
+- name: 2d.fillStyle.get.halftransparent
+ code: |
+ ctx.fillStyle = 'rgba(255,255,255,0.5)';
+ @assert ctx.fillStyle === 'rgba(255, 255, 255, 0.5)';
+
+- name: 2d.fillStyle.get.transparent
+ code: |
+ ctx.fillStyle = 'rgba(0,0,0,0)';
+ @assert ctx.fillStyle === 'rgba(0, 0, 0, 0)';
+
+- name: 2d.fillStyle.default
+ code: |
+ @assert ctx.fillStyle === '#000000';
+
+- name: 2d.fillStyle.toStringFunctionCallback
+ desc: Passing a function in to ctx.fillStyle or ctx.strokeStyle with a toString callback works as specified
+ code: |
+ ctx.fillStyle = { toString: function() { return "#008000"; } };
+ @assert ctx.fillStyle === "#008000";
+ ctx.fillStyle = {};
+ @assert ctx.fillStyle === "#008000";
+ ctx.fillStyle = 800000;
+ @assert ctx.fillStyle === "#008000";
+ @assert throws TypeError ctx.fillStyle = { toString: function() { throw new TypeError; } };
+ ctx.strokeStyle = { toString: function() { return "#008000"; } };
+ @assert ctx.strokeStyle === "#008000";
+ ctx.strokeStyle = {};
+ @assert ctx.strokeStyle === "#008000";
+ ctx.strokeStyle = 800000;
+ @assert ctx.strokeStyle === "#008000";
+ @assert throws TypeError ctx.strokeStyle = { toString: function() { throw new TypeError; } };
+
+- name: 2d.strokeStyle.default
+ code: |
+ @assert ctx.strokeStyle === '#000000';
+
+
+- name: 2d.gradient.object.type
+ desc: window.CanvasGradient exists and has the right properties
+ notes: &bindings Defined in "Web IDL" (draft)
+ code: |
+ {% set root = 'self' if canvas_type == 'worker' else 'window' %}
+ @assert {{ root }}.CanvasGradient !== undefined;
+ @assert {{ root }}.CanvasGradient.prototype.addColorStop !== undefined;
+
+- name: 2d.gradient.object.return
+ desc: createLinearGradient() and createRadialGradient() returns objects implementing
+ CanvasGradient
+ code: |
+ {% set root = 'self' if canvas_type == 'worker' else 'window' %}
+ {{ root }}.CanvasGradient.prototype.thisImplementsCanvasGradient = true;
+
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ @assert g1.addColorStop !== undefined;
+ @assert g1.thisImplementsCanvasGradient === true;
+
+ var g2 = ctx.createRadialGradient(0, 0, 10, 0, 0, 20);
+ @assert g2.addColorStop !== undefined;
+ @assert g2.thisImplementsCanvasGradient === true;
+
+- name: 2d.gradient.interpolate.solid
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.interpolate.color
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 25,25 ==~ 191,191,63,255 +/- 3;
+ @assert pixel 50,25 ==~ 127,127,127,255 +/- 3;
+ @assert pixel 75,25 ==~ 63,63,191,255 +/- 3;
+ expected: |
+ size 100 50
+ g = cairo.LinearGradient(0, 0, 100, 0)
+ g.add_color_stop_rgb(0, 1,1,0)
+ g.add_color_stop_rgb(1, 0,0,1)
+ cr.set_source(g)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.gradient.interpolate.alpha
+ code: |
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(0,0,255, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 25,25 ==~ 191,191,63,255 +/- 3;
+ @assert pixel 50,25 ==~ 127,127,127,255 +/- 3;
+ @assert pixel 75,25 ==~ 63,63,191,255 +/- 3;
+ expected: |
+ size 100 50
+ g = cairo.LinearGradient(0, 0, 100, 0)
+ g.add_color_stop_rgb(0, 1,1,0)
+ g.add_color_stop_rgb(1, 0,0,1)
+ cr.set_source(g)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.gradient.interpolate.coloralpha
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'rgba(255,255,0, 0)');
+ g.addColorStop(1, 'rgba(0,0,255, 1)');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 25,25 ==~ 190,190,65,65 +/- 3;
+ @assert pixel 50,25 ==~ 126,126,128,128 +/- 3;
+ @assert pixel 75,25 ==~ 62,62,192,192 +/- 3;
+ expected: |
+ size 100 50
+ g = cairo.LinearGradient(0, 0, 100, 0)
+ g.add_color_stop_rgba(0, 1,1,0, 0)
+ g.add_color_stop_rgba(1, 0,0,1, 1)
+ cr.set_source(g)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.gradient.interpolate.outside
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(25, 0, 75, 0);
+ g.addColorStop(0.4, '#0f0');
+ g.addColorStop(0.6, '#0f0');
+
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 20,25 ==~ 0,255,0,255;
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 80,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.interpolate.zerosize.fill
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ @assert pixel 40,20 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.interpolate.zerosize.stroke
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.rect(20, 20, 60, 10);
+ ctx.stroke();
+ @assert pixel 19,19 == 0,255,0,255;
+ @assert pixel 20,19 == 0,255,0,255;
+ @assert pixel 21,19 == 0,255,0,255;
+ @assert pixel 19,20 == 0,255,0,255;
+ @assert pixel 20,20 == 0,255,0,255;
+ @assert pixel 21,20 == 0,255,0,255;
+ @assert pixel 19,21 == 0,255,0,255;
+ @assert pixel 20,21 == 0,255,0,255;
+ @assert pixel 21,21 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.interpolate.zerosize.fillRect
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 40,20 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.gradient.interpolate.zerosize.strokeRect
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.strokeRect(20, 20, 60, 10);
+ @assert pixel 19,19 == 0,255,0,255;
+ @assert pixel 20,19 == 0,255,0,255;
+ @assert pixel 21,19 == 0,255,0,255;
+ @assert pixel 19,20 == 0,255,0,255;
+ @assert pixel 20,20 == 0,255,0,255;
+ @assert pixel 21,20 == 0,255,0,255;
+ @assert pixel 19,21 == 0,255,0,255;
+ @assert pixel 20,21 == 0,255,0,255;
+ @assert pixel 21,21 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.interpolate.zerosize.fillText
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.fillText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+ expected: green
+
+- name: 2d.gradient.interpolate.zerosize.strokeText
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.strokeStyle = g;
+ ctx.font = '100px sans-serif';
+ ctx.strokeText("AA", 0, 50);
+ _assertGreen(ctx, 100, 50);
+ expected: green
+
+
+- name: 2d.gradient.interpolate.vertical
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,12 ==~ 191,191,63,255 +/- 10;
+ @assert pixel 50,25 ==~ 127,127,127,255 +/- 5;
+ @assert pixel 50,37 ==~ 63,63,191,255 +/- 10;
+ expected: |
+ size 100 50
+ g = cairo.LinearGradient(0, 0, 0, 50)
+ g.add_color_stop_rgb(0, 1,1,0)
+ g.add_color_stop_rgb(1, 0,0,1)
+ cr.set_source(g)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.gradient.interpolate.multiple
+ code: |
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.5, '#0ff');
+ g.addColorStop(1, '#f0f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ @assert pixel 50,25 ==~ 127,255,127,255 +/- 3;
+ @assert pixel 100,25 ==~ 0,255,255,255 +/- 3;
+ @assert pixel 150,25 ==~ 127,127,255,255 +/- 3;
+ expected: |
+ size 200 50
+ g = cairo.LinearGradient(0, 0, 200, 0)
+ g.add_color_stop_rgb(0.0, 1,1,0)
+ g.add_color_stop_rgb(0.5, 0,1,1)
+ g.add_color_stop_rgb(1.0, 1,0,1)
+ cr.set_source(g)
+ cr.rectangle(0, 0, 200, 50)
+ cr.fill()
+
+- name: 2d.gradient.interpolate.overlap
+ code: |
+ canvas.width = 200;
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0, '#ff0');
+ g.addColorStop(0.25, '#00f');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.25, '#ff0');
+ g.addColorStop(0.5, '#00f');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.75, '#00f');
+ g.addColorStop(0.75, '#f00');
+ g.addColorStop(0.75, '#ff0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.5, '#ff0');
+ g.addColorStop(1, '#00f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 200, 50);
+ @assert pixel 49,25 ==~ 0,0,255,255 +/- 16;
+ @assert pixel 51,25 ==~ 255,255,0,255 +/- 16;
+ @assert pixel 99,25 ==~ 0,0,255,255 +/- 16;
+ @assert pixel 101,25 ==~ 255,255,0,255 +/- 16;
+ @assert pixel 149,25 ==~ 0,0,255,255 +/- 16;
+ @assert pixel 151,25 ==~ 255,255,0,255 +/- 16;
+ expected: |
+ size 200 50
+ g = cairo.LinearGradient(0, 0, 50, 0)
+ g.add_color_stop_rgb(0, 1,1,0)
+ g.add_color_stop_rgb(1, 0,0,1)
+ cr.set_source(g)
+ cr.rectangle(0, 0, 50, 50)
+ cr.fill()
+
+ g = cairo.LinearGradient(50, 0, 100, 0)
+ g.add_color_stop_rgb(0, 1,1,0)
+ g.add_color_stop_rgb(1, 0,0,1)
+ cr.set_source(g)
+ cr.rectangle(50, 0, 50, 50)
+ cr.fill()
+
+ g = cairo.LinearGradient(100, 0, 150, 0)
+ g.add_color_stop_rgb(0, 1,1,0)
+ g.add_color_stop_rgb(1, 0,0,1)
+ cr.set_source(g)
+ cr.rectangle(100, 0, 50, 50)
+ cr.fill()
+
+ g = cairo.LinearGradient(150, 0, 200, 0)
+ g.add_color_stop_rgb(0, 1,1,0)
+ g.add_color_stop_rgb(1, 0,0,1)
+ cr.set_source(g)
+ cr.rectangle(150, 0, 50, 50)
+ cr.fill()
+
+- name: 2d.gradient.interpolate.overlap2
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ var ps = [ 0, 1/10, 1/4, 1/3, 1/2, 3/4, 1 ];
+ for (var p = 0; p < ps.length; ++p)
+ {
+ g.addColorStop(ps[p], '#0f0');
+ for (var i = 0; i < 15; ++i)
+ g.addColorStop(ps[p], '#f00');
+ g.addColorStop(ps[p], '#0f0');
+ }
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 30,25 == 0,255,0,255;
+ @assert pixel 40,25 == 0,255,0,255;
+ @assert pixel 60,25 == 0,255,0,255;
+ @assert pixel 80,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.empty
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = ctx.createLinearGradient(0, 0, 0, 50);
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.object.update
+ code: |
+ var g = ctx.createLinearGradient(-100, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ g.addColorStop(0.1, '#0f0');
+ g.addColorStop(0.9, '#0f0');
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.object.compare
+ code: |
+ var g1 = ctx.createLinearGradient(0, 0, 100, 0);
+ var g2 = ctx.createLinearGradient(0, 0, 100, 0);
+ @assert g1 !== g2;
+ ctx.fillStyle = g1;
+ @assert ctx.fillStyle === g1;
+
+- name: 2d.gradient.object.crosscanvas
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var g = {{ create_canvas }}.getContext('2d').createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+ variants:
+ _HtmlCanvas:
+ canvasType: ['HtmlCanvas']
+ create_canvas: document.createElement('canvas')
+ _OffscreenCanvas:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ create_canvas: new OffscreenCanvas(100, 50)
+
+- name: 2d.gradient.object.current
+ canvasType: ['HtmlCanvas']
+ code: |
+ canvas.setAttribute('style', 'color: #f00');
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, 'currentColor');
+ g.addColorStop(1, 'currentColor');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 0,0,0,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0, 0, 0)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.gradient.object.invalidoffset
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ @assert throws INDEX_SIZE_ERR g.addColorStop(-1, '#000');
+ @assert throws INDEX_SIZE_ERR g.addColorStop(2, '#000');
+ @assert throws TypeError g.addColorStop(Infinity, '#000');
+ @assert throws TypeError g.addColorStop(-Infinity, '#000');
+ @assert throws TypeError g.addColorStop(NaN, '#000');
+
+- name: 2d.gradient.object.invalidcolor
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ @assert throws SYNTAX_ERR g.addColorStop(0, "");
+ @assert throws SYNTAX_ERR g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)');
+ @assert throws SYNTAX_ERR g.addColorStop(0, 'null');
+ @assert throws SYNTAX_ERR g.addColorStop(0, 'undefined');
+ @assert throws SYNTAX_ERR g.addColorStop(0, null);
+ @assert throws SYNTAX_ERR g.addColorStop(0, undefined);
+
+ var g = ctx.createRadialGradient(0, 0, 0, 100, 0, 0);
+ @assert throws SYNTAX_ERR g.addColorStop(0, "");
+ @assert throws SYNTAX_ERR g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)');
+ @assert throws SYNTAX_ERR g.addColorStop(0, 'null');
+ @assert throws SYNTAX_ERR g.addColorStop(0, 'undefined');
+ @assert throws SYNTAX_ERR g.addColorStop(0, null);
+ @assert throws SYNTAX_ERR g.addColorStop(0, undefined);
+
+
+- name: 2d.gradient.linear.nonfinite
+ desc: createLinearGradient() throws TypeError if arguments are not finite
+ notes: *bindings
+ code: |
+ @nonfinite @assert throws TypeError ctx.createLinearGradient(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
+
+- name: 2d.gradient.linear.transform.1
+ desc: Linear gradient coordinates are relative to the coordinate space at the time
+ of filling
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.linear.transform.2
+ desc: Linear gradient coordinates are relative to the coordinate space at the time
+ of filling
+ code: |
+ ctx.translate(100, 0);
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-150, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.linear.transform.3
+ desc: Linear gradient transforms do not experience broken caching effects
+ code: |
+ var g = ctx.createLinearGradient(0, 0, 200, 0);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.25, '#0f0');
+ g.addColorStop(0.75, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(-50, 0);
+ ctx.fillRect(50, 0, 100, 50);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.negative
+ desc: createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative
+ code: |
+ @assert throws INDEX_SIZE_ERR ctx.createRadialGradient(0, 0, -0.1, 0, 0, 1);
+ @assert throws INDEX_SIZE_ERR ctx.createRadialGradient(0, 0, 1, 0, 0, -0.1);
+ @assert throws INDEX_SIZE_ERR ctx.createRadialGradient(0, 0, -0.1, 0, 0, -0.1);
+
+- name: 2d.gradient.radial.nonfinite
+ desc: createRadialGradient() throws TypeError if arguments are not finite
+ notes: *bindings
+ code: |
+ @nonfinite @assert throws TypeError ctx.createRadialGradient(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>);
+
+- name: 2d.gradient.radial.inside1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 100, 50, 25, 200);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.inside2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.inside3
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.993, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.outside1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 10, 200, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.outside2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.outside3
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.001, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.touch1
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(150, 25, 50, 200, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255; @moz-todo
+ @assert pixel 50,1 == 0,255,0,255; @moz-todo
+ @assert pixel 98,1 == 0,255,0,255; @moz-todo
+ @assert pixel 1,25 == 0,255,0,255; @moz-todo
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 1,48 == 0,255,0,255; @moz-todo
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 98,48 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.gradient.radial.touch2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(0.01, '#0f0');
+ g.addColorStop(0.99, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.touch3
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, -15, 25, 140, -30, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255; @moz-todo
+ @assert pixel 50,1 == 0,255,0,255; @moz-todo
+ @assert pixel 98,1 == 0,255,0,255; @moz-todo
+ @assert pixel 1,25 == 0,255,0,255; @moz-todo
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 1,48 == 0,255,0,255; @moz-todo
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 98,48 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.gradient.radial.equal
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(50, 25, 20, 50, 25, 20);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255; @moz-todo
+ @assert pixel 50,1 == 0,255,0,255; @moz-todo
+ @assert pixel 98,1 == 0,255,0,255; @moz-todo
+ @assert pixel 1,25 == 0,255,0,255; @moz-todo
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 1,48 == 0,255,0,255; @moz-todo
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 98,48 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.gradient.radial.cone.behind
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(120, 25, 10, 211, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255; @moz-todo
+ @assert pixel 50,1 == 0,255,0,255; @moz-todo
+ @assert pixel 98,1 == 0,255,0,255; @moz-todo
+ @assert pixel 1,25 == 0,255,0,255; @moz-todo
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 1,48 == 0,255,0,255; @moz-todo
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 98,48 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.gradient.radial.cone.front
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(311, 25, 10, 210, 25, 100);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.cone.bottom
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 101);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.cone.top
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(230, 25, 100, 100, 25, 101);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.cone.beside
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(0, 100, 40, 100, 100, 50);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255; @moz-todo
+ @assert pixel 50,1 == 0,255,0,255; @moz-todo
+ @assert pixel 98,1 == 0,255,0,255; @moz-todo
+ @assert pixel 1,25 == 0,255,0,255; @moz-todo
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 1,48 == 0,255,0,255; @moz-todo
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1; @moz-todo
+ @assert pixel 98,48 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.gradient.radial.cone.cylinder
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 100);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.cone.shape1
+ code: |
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(30+tol, 40);
+ ctx.lineTo(110, -20+tol);
+ ctx.lineTo(110, 100-tol);
+ ctx.fill();
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.cone.shape2
+ code: |
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
+ g.addColorStop(0, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(30-tol, 40);
+ ctx.lineTo(110, -20-tol);
+ ctx.lineTo(110, 100+tol);
+ ctx.fill();
+
+ @assert pixel 1,1 == 0,255,0,255; @moz-todo
+ @assert pixel 50,1 == 0,255,0,255; @moz-todo
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255; @moz-todo
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 1,48 == 0,255,0,255; @moz-todo
+ @assert pixel 50,48 ==~ 0,255,0,255 +/- 1;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.transform.1
+ desc: Radial gradient coordinates are relative to the coordinate space at the time
+ of filling
+ code: |
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.transform.2
+ desc: Radial gradient coordinates are relative to the coordinate space at the time
+ of filling
+ code: |
+ ctx.translate(100, 0);
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.translate(-50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.radial.transform.3
+ desc: Radial gradient transforms do not experience broken caching effects
+ code: |
+ var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(0.5, '#0f0');
+ g.addColorStop(0.51, '#f00');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.translate(50, 25);
+ ctx.scale(10, 10);
+ ctx.fillRect(-5, -2.5, 10, 5);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.gradient.conic.positive.rotation
+ desc: Conic gradient with positive rotation
+ code: |
+ const g = ctx.createConicGradient(3*Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 25,15 ==~ 255,0,0,255 +/- 3;
+ @assert pixel 75,40 ==~ 0,255,0,255 +/- 3;
+ expected: green
+
+- name: 2d.gradient.conic.negative.rotation
+ desc: Conic gradient with negative rotation
+ code: |
+ const g = ctx.createConicGradient(-Math.PI/2, 50, 25);
+ // It's red in the upper right region and green on the lower left region
+ g.addColorStop(0, "#f00");
+ g.addColorStop(0.25, "#0f0");
+ g.addColorStop(0.50, "#0f0");
+ g.addColorStop(0.75, "#f00");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 25,15 ==~ 255,0,0,255 +/- 3;
+ @assert pixel 75,40 ==~ 0,255,0,255 +/- 3;
+ expected: green
+
+- name: 2d.gradient.conic.invalid.inputs
+ desc: Conic gradient function with invalid inputs
+ code: |
+ @nonfinite @assert throws TypeError ctx.createConicGradient(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>);
+
+ const g = ctx.createConicGradient(0, 0, 25);
+ @nonfinite @assert throws TypeError g.addColorStop(<Infinity -Infinity NaN>, <'#f00'>);
+ @nonfinite @assert throws SYNTAX_ERR g.addColorStop(<0>, <Infinity -Infinity NaN>);
+
+- name: 2d.pattern.basic.type
+ images:
+ - green.png
+ code: |
+ {% set root = 'self' if canvas_type == 'worker' else 'window' %}
+ @assert {{ root }}.CanvasPattern !== undefined;
+
+ {{ root }}.CanvasPattern.prototype.thisImplementsCanvasPattern = true;
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ @assert pattern.thisImplementsCanvasPattern;
+ variants: &load-image-variant-definition
+ _HtmlCanvas:
+ canvasType: ['HtmlCanvas']
+ load_image: var img = document.getElementById('{{ (images or svgimages)[0] }}');
+ _OffscreenCanvas:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ test_type: promise
+ load_image: |-
+ var response = await fetch('/images/{{ (images or svgimages)[0] }}')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+
+- name: 2d.pattern.basic.image
+ images:
+ - green.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.basic.canvas
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: &create-canvas2-variant-definition
+ _HtmlCanvas:
+ canvasType: ['HtmlCanvas']
+ create_canvas2: |-
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ _OffscreenCanvas:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ create_canvas2: |-
+ var canvas2 = new OffscreenCanvas(100, 50);
+
+- name: 2d.pattern.basic.zerocanvas
+ code: |
+ canvas.width = 0;
+ canvas.height = 10;
+ @assert canvas.width === 0;
+ @assert canvas.height === 10;
+ @assert throws INVALID_STATE_ERR ctx.createPattern(canvas, 'repeat');
+
+ canvas.width = 10;
+ canvas.height = 0;
+ @assert canvas.width === 10;
+ @assert canvas.height === 0;
+ @assert throws INVALID_STATE_ERR ctx.createPattern(canvas, 'repeat');
+
+ canvas.width = 0;
+ canvas.height = 0;
+ @assert canvas.width === 0;
+ @assert canvas.height === 0;
+ @assert throws INVALID_STATE_ERR ctx.createPattern(canvas, 'repeat');
+
+- name: 2d.pattern.basic.nocontext
+ code: |
+ {{ create_canvas2 }}
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.pattern.transform.identity
+ code: |
+ {{ create_canvas2 }}
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform(new DOMMatrix());
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.pattern.transform.infinity
+ code: |
+ {{ create_canvas2 }}
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ pattern.setTransform({a: Infinity});
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.pattern.transform.invalid
+ code: |
+ {{ create_canvas2 }}
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ @assert throws TypeError pattern.setTransform({a: 1, m11: 2});
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.pattern.image.undefined
+ notes: *bindings
+ code: |
+ @assert throws TypeError ctx.createPattern(undefined, 'repeat');
+
+- name: 2d.pattern.image.null
+ notes: *bindings
+ code: |
+ @assert throws TypeError ctx.createPattern(null, 'repeat');
+
+- name: 2d.pattern.image.string
+ notes: *bindings
+ code: |
+ @assert throws TypeError ctx.createPattern('../images/red.png', 'repeat');
+
+- name: 2d.pattern.image.incomplete.nosrc
+ canvasType: ['HtmlCanvas']
+ code: |
+ var img = new Image();
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.image.incomplete.immediate
+ canvasType: ['HtmlCanvas']
+ images:
+ - red.png
+ code: |
+ var img = new Image();
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ @assert ctx.createPattern(img, 'repeat') === null; @moz-todo
+
+- name: 2d.pattern.image.incomplete.reload
+ canvasType: ['HtmlCanvas']
+ images:
+ - yellow.png
+ - red.png
+ code: |
+ var img = document.getElementById('yellow.png');
+ img.src = '../images/red.png';
+ // This triggers the "update the image data" algorithm,
+ // and resets the image to the "unavailable" state.
+ // The image will not go to the "completely available" state
+ // until a fetch task in the networking task source is processed,
+ // so the image must not be fully decodable yet:
+ @assert ctx.createPattern(img, 'repeat') === null; @moz-todo
+
+- name: 2d.pattern.image.incomplete.emptysrc
+ canvasType: ['HtmlCanvas']
+ images:
+ - red.png
+ mozilla: {throws: !!null ''}
+ code: |
+ var img = document.getElementById('red.png');
+ img.src = "";
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.image.incomplete.removedsrc
+ canvasType: ['HtmlCanvas']
+ images:
+ - red.png
+ mozilla: {throws: !!null ''}
+ code: |
+ var img = document.getElementById('red.png');
+ img.removeAttribute('src');
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.image.broken
+ canvasType: ['HtmlCanvas']
+ images:
+ - broken.png
+ code: |
+ var img = document.getElementById('broken.png');
+ @assert throws INVALID_STATE_ERR ctx.createPattern(img, 'repeat');
+
+- name: 2d.pattern.image.nonexistent
+ canvasType: ['HtmlCanvas']
+ images:
+ - no-such-image-really.png
+ code: |
+ var img = document.getElementById('no-such-image-really.png');
+ @assert throws INVALID_STATE_ERR ctx.createPattern(img, 'repeat');
+
+- name: 2d.pattern.svgimage.nonexistent
+ canvasType: ['HtmlCanvas']
+ svgimages:
+ - no-such-image-really.png
+ code: |
+ var img = document.getElementById('no-such-image-really.png');
+ @assert throws INVALID_STATE_ERR ctx.createPattern(img, 'repeat');
+
+- name: 2d.pattern.image.nonexistent-but-loading
+ canvasType: ['HtmlCanvas']
+ code: |
+ var img = document.createElement("img");
+ img.src = "/images/no-such-image-really.png";
+ @assert ctx.createPattern(img, 'repeat') === null;
+ var img = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ img.src = "/images/no-such-image-really.png";
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.image.nosrc
+ canvasType: ['HtmlCanvas']
+ code: |
+ var img = document.createElement("img");
+ @assert ctx.createPattern(img, 'repeat') === null;
+ var img = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.image.zerowidth
+ canvasType: ['HtmlCanvas']
+ images:
+ - red-zerowidth.svg
+ code: |
+ var img = document.getElementById('red-zerowidth.svg');
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.image.zeroheight
+ canvasType: ['HtmlCanvas']
+ images:
+ - red-zeroheight.svg
+ code: |
+ var img = document.getElementById('red-zeroheight.svg');
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.svgimage.zerowidth
+ canvasType: ['HtmlCanvas']
+ svgimages:
+ - red-zerowidth.svg
+ code: |
+ var img = document.getElementById('red-zerowidth.svg');
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.svgimage.zeroheight
+ canvasType: ['HtmlCanvas']
+ svgimages:
+ - red-zeroheight.svg
+ code: |
+ var img = document.getElementById('red-zeroheight.svg');
+ @assert ctx.createPattern(img, 'repeat') === null;
+
+- name: 2d.pattern.repeat.empty
+ images:
+ - green-1x1.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, "");
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 200, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.repeat.null
+ code: |
+ @assert ctx.createPattern(canvas, null) != null;
+
+- name: 2d.pattern.repeat.undefined
+ code: |
+ @assert throws SYNTAX_ERR ctx.createPattern(canvas, undefined);
+
+- name: 2d.pattern.repeat.unrecognised
+ code: |
+ @assert throws SYNTAX_ERR ctx.createPattern(canvas, "invalid");
+
+- name: 2d.pattern.repeat.unrecognisednull
+ code: |
+ @assert throws SYNTAX_ERR ctx.createPattern(canvas, "null");
+
+- name: 2d.pattern.repeat.case
+ code: |
+ @assert throws SYNTAX_ERR ctx.createPattern(canvas, "Repeat");
+
+- name: 2d.pattern.repeat.nullsuffix
+ code: |
+ @assert throws SYNTAX_ERR ctx.createPattern(canvas, "repeat\0");
+
+- name: 2d.pattern.modify.image1
+ canvasType: ['HtmlCanvas']
+ images:
+ - green.png
+ code: |
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ deferTest();
+ img.onload = t.step_func_done(function ()
+ {
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ });
+ img.src = '/images/red.png';
+ expected: green
+
+- name: 2d.pattern.modify.image2
+ canvasType: ['HtmlCanvas']
+ images:
+ - green.png
+ code: |
+ var img = document.getElementById('green.png');
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 100, 50);
+ deferTest();
+ img.onload = t.step_func_done(function ()
+ {
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ });
+ img.src = '/images/red.png';
+ expected: green
+
+- name: 2d.pattern.modify.canvas1
+ canvasType: ['HtmlCanvas']
+ code: |
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.pattern.modify.canvas2
+ code: |
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.pattern.crosscanvas
+ images:
+ - green.png
+ code: |
+ {{ load_image }}
+
+ var pattern = {{ create_canvas }}.getContext('2d').createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+ variants:
+ _HtmlCanvas:
+ canvasType: ['HtmlCanvas']
+ load_image: var img = document.getElementById('{{ images[0] }}');
+ create_canvas: document.createElement('canvas')
+ _OffscreenCanvas:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ test_type: promise
+ load_image: |-
+ var response = await fetch('/images/{{ images[0] }}')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+ create_canvas: new OffscreenCanvas(100, 50)
+
+- name: 2d.pattern.paint.norepeat.basic
+ images:
+ - green.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.norepeat.outside
+ images:
+ - red.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+ ctx.fillRect(-100, 0, 100, 50);
+ ctx.fillRect(0, 50, 100, 50);
+ ctx.fillRect(100, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.norepeat.coord1
+ images:
+ - green.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.norepeat.coord2
+ images:
+ - green.png
+ code: |
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 0);
+ ctx.fillRect(-50, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.norepeat.coord3
+ images:
+ - red.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeat.basic
+ images:
+ - green-16x16.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeat.outside
+ images:
+ - green-16x16.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(50, 25);
+ ctx.fillRect(-50, -25, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeat.coord1
+ images:
+ - rgrg-256x256.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeat.coord2
+ images:
+ - ggrr-256x256.png
+ code: |
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeat.coord3
+ images:
+ - rgrg-256x256.png
+ code: |
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-128, -78);
+ ctx.fillRect(128, 78, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeatx.basic
+ images:
+ - green-16x16.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 16);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeatx.outside
+ images:
+ - red-16x16.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeatx.coord1
+ images:
+ - red-16x16.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat-x');
+ ctx.fillStyle = pattern;
+ ctx.translate(0, 16);
+ ctx.fillRect(0, -16, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 16);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 98,25 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeaty.basic
+ images:
+ - green-16x16.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 16, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeaty.outside
+ images:
+ - red-16x16.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.repeaty.coord1
+ images:
+ - red-16x16.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat-y');
+ ctx.fillStyle = pattern;
+ ctx.translate(48, 0);
+ ctx.fillRect(-48, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 16, 50);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.orientation.image
+ desc: Image patterns do not get flipped when painted
+ images:
+ - rrgg-256x256.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.save();
+ ctx.translate(0, -103);
+ ctx.fillRect(0, 103, 100, 50);
+ ctx.restore();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.pattern.paint.orientation.canvas
+ desc: Canvas patterns do not get flipped when painted
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 25);
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 25, 100, 25);
+
+ var pattern = ctx.createPattern(canvas2, 'no-repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 25);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.pattern.animated.gif
+ desc: createPattern() of an animated GIF draws the first frame
+ canvasType: ['HtmlCanvas']
+ images:
+ - anim-gr.gif
+ code: |
+ deferTest();
+ step_timeout(function () {
+ var pattern = ctx.createPattern(document.getElementById('anim-gr.gif'), 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 50, 50);
+ step_timeout(t.step_func_done(function () {
+ ctx.fillRect(50, 0, 50, 50);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ }), 250);
+ }, 250);
+ expected: green
+
+- name: 2d.fillStyle.CSSRGB
+ desc: CSSRGB works as color input
+ canvasType: ['HtmlCanvas', 'OffscreenCanvas']
+ code: |
+ ctx.fillStyle = new CSSRGB(1, 0, 1);
+ @assert ctx.fillStyle === '#ff00ff';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 255,0,255,255;
+
+ const color = new CSSRGB(0, CSS.percent(50), 0);
+ ctx.fillStyle = color;
+ @assert ctx.fillStyle === '#008000';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,128,0,255;
+ color.g = 0;
+ ctx.fillStyle = color;
+ @assert ctx.fillStyle === '#000000';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,255;
+
+ color.alpha = 0;
+ ctx.fillStyle = color;
+ @assert ctx.fillStyle === 'rgba(0, 0, 0, 0)';
+ ctx.reset();
+ color.alpha = 0.5;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,0,0,128;
+
+ ctx.fillStyle = new CSSHSL(CSS.deg(0), 1, 1).toRGB();
+ @assert ctx.fillStyle === '#ffffff';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 255,255,255,255;
+
+ color.alpha = 1;
+ color.g = 1;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+ expected: green
+
+- name: 2d.fillStyle.CSSHSL
+ desc: CSSHSL works as color input
+ canvasType: ['HtmlCanvas', 'OffscreenCanvas']
+ code: |
+ ctx.fillStyle = new CSSHSL(CSS.deg(180), 0.5, 0.5);
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ 64,191,191,255 +/- 3;
+
+ const color = new CSSHSL(CSS.deg(180), 1, 1);
+ ctx.fillStyle = color;
+ @assert ctx.fillStyle === '#ffffff';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 255,255,255,255;
+ color.l = 0.5;
+ ctx.fillStyle = color;
+ @assert ctx.fillStyle === '#00ffff';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,255,255;
+
+ ctx.fillStyle = new CSSRGB(1, 0, 1).toHSL();
+ @assert ctx.fillStyle === '#ff00ff';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 255,0,255,255;
+
+ color.h = CSS.deg(120);
+ color.s = 1;
+ color.l = 0.5;
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 100, 50);
+ expected: green
+
+- name: 2d.fillStyle.colormix
+ desc: color-mix works as color input
+ canvasType: ['HtmlCanvas', 'OffscreenCanvas', 'Worker']
+ code: |
+ ctx.fillStyle = "color-mix(in srgb, red, blue)";
+ @assert ctx.fillStyle === 'color(srgb 0.5 0 0.5)';
+ ctx.fillStyle = "color-mix(in srgb, red, color(srgb 1 0 0))";
+ @assert ctx.fillStyle === 'color(srgb 1 0 0)';
+
+- name: 2d.fillStyle.colormix.currentcolor
+ desc: color-mix works as color input with currentcolor
+ canvasType: ['HtmlCanvas']
+ code: |
+ canvas.setAttribute('style', 'color: magenta');
+ ctx.fillStyle = "color-mix(in srgb, black, currentcolor)";
+ @assert ctx.fillStyle === 'color(srgb 0.5 0 0.5)';
+ ctx.strokeStyle = "color-mix(in srgb, black, currentcolor)";
+ @assert ctx.strokeStyle === 'color(srgb 0.5 0 0.5)';
+
+- name: 2d.strokeStyle.colormix
+ desc: color-mix works as color input
+ canvasType: ['HtmlCanvas', 'OffscreenCanvas']
+ code: |
+ ctx.strokeStyle = "color-mix(in srgb, red, blue)";
+ @assert ctx.strokeStyle === 'color(srgb 0.5 0 0.5)';
+ ctx.strokeStyle = "color-mix(in srgb, red, color(srgb 1 0 0))";
+ @assert ctx.strokeStyle === 'color(srgb 1 0 0)';
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml
new file mode 100644
index 0000000000..01c83a33e2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml
@@ -0,0 +1,680 @@
+- name: 2d.filter.value
+ desc: test if ctx.filter works correctly
+ code: |
+ @assert ctx.filter == 'none';
+ ctx.filter = 'blur(5px)';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.save();
+ ctx.filter = 'none';
+ @assert ctx.filter == 'none';
+ ctx.restore();
+ @assert ctx.filter == 'blur(5px)';
+
+ ctx.filter = 'blur(10)';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = 'blur 10px';
+ @assert ctx.filter == 'blur(5px)';
+
+ ctx.filter = 'inherit';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = 'initial';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = 'unset';
+ @assert ctx.filter == 'blur(5px)';
+
+ ctx.filter = '';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = null;
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = undefined;
+ @assert ctx.filter == 'blur(5px)';
+
+ ctx.filter = 'blur( 5px)';
+ assert_equals(ctx.filter, 'blur( 5px)');
+
+- name: 2d.filter.canvasFilterObject.tentative
+ desc: Test CanvasFilter() object
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ @assert ctx.filter == 'none';
+ ctx.filter = 'blur(5px)';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1, 2]});
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ ctx.filter = new CanvasFilter([
+ {name: 'gaussianBlur', stdDeviation: 5},
+ {name: 'gaussianBlur', stdDeviation: 10}
+ ]);
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.filter = ctx.filter;
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ ctx.filter = 'blur(5px)';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = 'none';
+ @assert ctx.filter == 'none';
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ ctx.filter = 'this string is not a filter and should do nothing';
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+
+- name: 2d.filter.canvasFilterObject.tentative
+ desc: Test CanvasFilter() object
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ @assert ctx.filter == 'none';
+ ctx.filter = 'blur(5px)';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1, 2]});
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ ctx.filter = new CanvasFilter([
+ {name: 'gaussianBlur', stdDeviation: 5},
+ {name: 'gaussianBlur', stdDeviation: 10}
+ ]);
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ var canvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.filter = ctx.filter;
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+ ctx.filter = 'blur(5px)';
+ @assert ctx.filter == 'blur(5px)';
+ ctx.filter = 'none';
+ @assert ctx.filter == 'none';
+ ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 5});
+ ctx.filter = 'this string is not a filter and should do nothing';
+ @assert ctx.filter.toString() == '[object CanvasFilter]';
+
+- name: 2d.filter.canvasFilterObject.blur.exceptions.tentative
+ desc: Test exceptions on CanvasFilter() blur.object
+ code: |
+ @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur'});
+ @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: undefined});
+ @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 'foo'});
+ @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1,2,3]});
+ @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: NaN});
+ @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: {}});
+
+- name: 2d.filter.canvasFilterObject.colorMatrix.tentative
+ desc: Test the functionality of ColorMatrix filters in CanvasFilter objects
+ code: |
+ @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: undefined});
+ @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: 'foo'});
+ @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: null});
+ @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3]});
+ @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 'a']});
+ @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, Infinity]});
+ ctx.fillStyle = '#f00';
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 0});
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 10,10 ==~ 255,0,0,255;
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 90});
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 10,10 ==~ 0,91,0,255;
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 180});
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 10,10 ==~ 0,109,109,255;
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 270});
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 10,10 ==~ 109,18,255,255;
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'saturate', values: 0.5});
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 10,10 ==~ 155,27,27,255;
+ ctx.clearRect(0, 0, 100, 50);
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'luminanceToAlpha'});
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 10,10 ==~ 0,0,0,54;
+ ctx.filter = new CanvasFilter({name: 'colorMatrix', values: [
+ 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0
+ ]});
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 25);
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 25, 50, 25);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(50, 25, 50, 25);
+ @assert pixel 10,10 ==~ 0,255,0,255;
+ @assert pixel 60,10 ==~ 0,255,0,255;
+ @assert pixel 10,30 ==~ 0,255,0,255;
+ @assert pixel 60,30 ==~ 0,255,0,255;
+
+- name: 2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative
+ desc: Test exceptions on CanvasFilter() convolveMatrix
+ code: |
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix'});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', divisor: 2});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: null});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: 1});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0]]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 'a'], [0]]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], 0]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0, Infinity]]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: []});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1, 2, 3, 4]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], []]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 2], []]});
+ @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], [1, 2]]});
+ // This should not throw an error
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[]]});
+ ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1]]});
+
+- name: 2d.filter.canvasFilterObject.componentTransfer.linear.tentative
+ desc: Test pixels on CanvasFilter() componentTransfer with linear type
+ code: |
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, slopes, intercepts) {
+ return [
+ Math.max(0, Math.min(1, inputColor[0]/255 * slopes[0] + intercepts[0])) * 255,
+ Math.max(0, Math.min(1, inputColor[1]/255 * slopes[1] + intercepts[1])) * 255,
+ Math.max(0, Math.min(1, inputColor[2]/255 * slopes[2] + intercepts[2])) * 255,
+ ];
+ }
+
+ const slopes = [0.5, 1.2, -0.2];
+ const intercepts = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'linear', slope: slopes[0], intercept: intercepts[0]},
+ funcG: {type: 'linear', slope: slopes[1], intercept: intercepts[1]},
+ funcB: {type: 'linear', slope: slopes[2], intercept: intercepts[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, slopes, intercepts);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+- name: 2d.filter.canvasFilterObject.componentTransfer.identity.tentative
+ desc: Test pixels on CanvasFilter() componentTransfer with identity type
+ code: |
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'identity'},
+ funcG: {type: 'identity'},
+ funcB: {type: 'identity'},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`,
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixel(canvas, 5, 5, color[0],color[1],color[2],255);
+ }
+
+- name: 2d.filter.canvasFilterObject.componentTransfer.gamma.tentative
+ desc: Test pixels on CanvasFilter() componentTransfer with gamma type
+ code: |
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getColor(inputColor, amplitude, exponent, offset) {
+ return [
+ Math.max(0, Math.min(1, Math.pow(inputColor[0]/255, exponent[0]) * amplitude[0] + offset[0])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[1]/255, exponent[1]) * amplitude[1] + offset[1])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[2]/255, exponent[2]) * amplitude[2] + offset[2])) * 255,
+ ];
+ }
+
+ const amplitudes = [2, 1.1, 0.5];
+ const exponents = [5, 3, 1];
+ const offsets = [0.25, 0, 0.5];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'gamma', amplitude: amplitudes[0], exponent: exponents[0], offset: offsets[0]},
+ funcG: {type: 'gamma', amplitude: amplitudes[1], exponent: exponents[1], offset: offsets[1]},
+ funcB: {type: 'gamma', amplitude: amplitudes[2], exponent: exponents[2], offset: offsets[2]},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, amplitudes, exponents, offsets);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+- name: 2d.filter.canvasFilterObject.componentTransfer.table.tentative
+ desc: Test pixels on CanvasFilter() componentTransfer with table type
+ code: |
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length - 1;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k] + (C - k/n) * n * (V[k + 1] - V[k]);
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'table', tableValues: tableValuesR},
+ funcG: {type: 'table', tableValues: tableValuesG},
+ funcB: {type: 'table', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+- name: 2d.filter.canvasFilterObject.componentTransfer.discrete.tentative
+ desc: Test pixels on CanvasFilter() componentTransfer with discrete type
+ code: |
+ // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
+ function getTransformedValue(C, V) {
+ // Get the right interval
+ const n = V.length;
+ const k = C == 1 ? n - 1 : Math.floor(C * n);
+ return V[k];
+ }
+
+ function getColor(inputColor, tableValues) {
+ const result = [0, 0, 0];
+ for (const i in inputColor) {
+ const C = inputColor[i]/255;
+ const Cprime = getTransformedValue(C, tableValues[i]);
+ result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
+ }
+ return result;
+ }
+
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ funcR: {type: 'discrete', tableValues: tableValuesR},
+ funcG: {type: 'discrete', tableValues: tableValuesG},
+ funcB: {type: 'discrete', tableValues: tableValuesB},
+ });
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (const color of inputColors) {
+ let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ }
+
+- name: 2d.filter.canvasFilterObject.gaussianBlur.tentative
+ desc: Test CanvasFilter() with gaussianBlur.
+ size: [100, 100]
+ code: |
+ ctx.fillStyle = 'teal';
+ ctx.filter = new CanvasFilter({
+ name: 'gaussianBlur',
+ stdDeviation: [{{ blur_x }}, {{blur_y}}],
+ });
+ ctx.fillRect(25, 25, 50, 50);
+ html_reference: |
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="{{ size[0] }}" height="{{ size[1] }}"
+ color-interpolation-filters="sRGB">
+ <filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
+ <feGaussianBlur stdDeviation="{{ blur_x }} {{blur_y}}" />
+ </filter>
+ <rect x="25" y="25" width="50" height="50"
+ fill="teal" filter="url(#blur)" />
+ </svg>
+ variants:
+ x-only:
+ blur_x: 4
+ blur_y: 0
+ mostly-x:
+ blur_x: 4
+ blur_y: 1
+ isotropic:
+ blur_x: 4
+ blur_y: 4
+ mostly-y:
+ blur_x: 1
+ blur_y: 4
+ y-only:
+ blur_x: 0
+ blur_y: 4
+
+- name: 2d.filter.canvasFilterObject.dropShadow.tentative
+ desc: Test CanvasFilter() dropShadow object.
+ size: [520, 420]
+ code: |
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(0, 0, {{ size[0] }}, 50);
+ ctx.fillRect(0, 100, {{ size[0] }}, 50);
+ ctx.fillRect(0, 200, {{ size[0] }}, 50);
+ ctx.fillRect(0, 300, {{ size[0] }}, 50);
+
+ ctx.fillStyle = 'crimson';
+
+ // Parameter defaults.
+ ctx.filter = new CanvasFilter({name: 'dropShadow'});
+ ctx.fillRect(10, 10, 80, 80);
+
+ // All parameters specified.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple', floodOpacity: 0.7});
+ ctx.fillRect(110, 10, 80, 80);
+
+ // Named color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 110, 80, 80);
+
+ // System color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'LinkText'});
+ ctx.fillRect(110, 110, 80, 80);
+
+ // Numerical color.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 1)'});
+ ctx.fillRect(210, 110, 80, 80);
+
+ // Transparent floodColor.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)'});
+ ctx.fillRect(310, 110, 80, 80);
+
+ // Transparent floodColor and floodOpacity.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)', floodOpacity: 0.7});
+ ctx.fillRect(410, 110, 80, 80);
+
+ // No blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 0,
+ floodColor: 'purple'});
+ ctx.fillRect(10, 210, 80, 80);
+
+ // Single float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple'});
+ ctx.fillRect(110, 210, 80, 80);
+
+ // Single negative float blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: -5,
+ floodColor: 'purple'});
+ ctx.fillRect(210, 210, 80, 80);
+
+ // Two floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [3, 5],
+ floodColor: 'purple'});
+ ctx.fillRect(310, 210, 80, 80);
+
+ // Two negative floats (X&Y) blur.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [-3, -5],
+ floodColor: 'purple'});
+ ctx.fillRect(410, 210, 80, 80);
+
+ // Degenerate parameter values.
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: [-5], dy: [], stdDeviation: null,
+ floodColor: 'purple', floodOpacity: [2]});
+ ctx.fillRect(10, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: null, dy: '5', stdDeviation: [[-5], ['3']],
+ floodColor: 'purple', floodOpacity: '0.8'});
+ ctx.fillRect(110, 310, 80, 80);
+
+ ctx.filter = new CanvasFilter(
+ {name: 'dropShadow', dx: true, dy: ['10'], stdDeviation: false,
+ floodColor: 'purple', floodOpacity: ['0.4']});
+ ctx.fillRect(210, 310, 80, 80);
+ html_reference: |
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width={{ size[0] }} height={{ size[1] }}
+ color-interpolation-filters="sRGB">
+ <rect x=0 y=0 width=100% height=50 fill="teal" />
+ <rect x=0 y=100 width=100% height=50 fill="teal" />
+ <rect x=0 y=200 width=100% height=50 fill="teal" />
+ <rect x=0 y=300 width=100% height=50 fill="teal" />
+
+ <rect x=10 y=10 width=80 height=80 fill="crimson"
+ filter="drop-shadow(2px 2px 2px black)"/>
+ <rect x=110 y=10 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 5px rgba(128, 0, 128, 0.7))"/>
+
+ <rect x=10 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px purple)"/>
+ <rect x=110 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px LinkText)"/>
+ <rect x=210 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 1))"/>
+ <rect x=310 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 0.7))"/>
+ <rect x=410 y=110 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 3px rgba(20, 50, 130, 0.49))"/>
+
+ <rect x=10 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+ <rect x=110 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 5px purple)"/>
+ <rect x=210 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+ <filter id="separable-filter"
+ x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx=9 dy=12 stdDeviation="3 5" flood-color="purple"/>
+ </filter>
+ <rect x=310 y=210 width=80 height=80 fill="crimson"
+ filter="url(#separable-filter)"/>
+ <rect x=410 y=210 width=80 height=80 fill="crimson"
+ filter="drop-shadow(9px 12px 0px purple)"/>
+
+ <rect x=10 y=310 width=80 height=80 fill="crimson"
+ filter="drop-shadow(-5px 0px 0px purple)"/>
+ <filter id="separable-filter-degenerate"
+ x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx=0 dy=5 stdDeviation="0 3"
+ flood-color="rgba(128, 0, 128, 0.8)"/>
+ </filter>
+ <rect x=110 y=310 width=80 height=80 fill="crimson"
+ filter="url(#separable-filter-degenerate)"/>
+ <rect x=210 y=310 width=80 height=80 fill="crimson"
+ filter="drop-shadow(1px 10px 0px rgba(128, 0, 128, 0.4))"/>
+ </svg>
+
+- name: 2d.filter.canvasFilterObject.dropShadow.exceptions.tentative
+ desc: Test exceptions on CanvasFilter() dropShadow object
+ code: |
+ @unroll @assert new CanvasFilter({\-
+ name: 'dropShadow', \-
+ <dx | dy | floodOpacity>: \-
+ <10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>});
+ @unroll @assert new CanvasFilter({\-
+ name: 'dropShadow', \-
+ <stdDeviation>: \-
+ <10 | -1 | 0.5 | null | true | false | [] | [20] | '30' | \-
+ [10, -1] | [0.5, null] | [true, false] | [[], [20]] | \-
+ ['30', ['40']]>});
+ @unroll @assert new CanvasFilter({\-
+ name: 'dropShadow', \-
+ <floodColor>: \-
+ <'red' | 'canvas' | 'rgba(4, -3, 0.5, 1)' | '#aabbccdd' | '#abcd'>});
+
+ @unroll @assert throws TypeError new CanvasFilter({\-
+ name: 'dropShadow', \-
+ <dx | dy | floodOpacity>: \-
+ <NaN | Infinity | -Infinity | undefined | 'test' | {} | [1, 2]>});
+ @unroll @assert throws TypeError new CanvasFilter({\-
+ name: 'dropShadow', \-
+ <stdDeviation>: \-
+ <NaN | Infinity | -Infinity | undefined | 'test' | {} | [1, 2, 3] | \-
+ [1, NaN] | [1, Infinity] | [1, -Infinity] | [1, undefined] | \-
+ [1, 'test'] | [1, {}] | [1, [2, 3]]>});
+ @unroll @assert throws TypeError new CanvasFilter({\-
+ name: 'dropShadow', \-
+ <floodColor>: \-
+ <'test' | 'rgba(NaN, 3, 2, 1)' | 10 | undefined | null | NaN>});
+
+- name: 2d.filter.canvasFilterObject.turbulence.inputTypes.tentative
+ desc: Test exceptions on CanvasFilter() turbulence object
+ code: |
+ const errorTestCases = [
+ {baseFrequency: {}},
+ {baseFrequency: -1},
+ {baseFrequency: [0, -1]},
+ {baseFrequency: NaN},
+ {baseFrequency: Infinity},
+ {baseFrequency: undefined},
+ {baseFrequency: -Infinity},
+ {baseFrequency: 'test'},
+
+ {numOctaves: {}},
+ {numOctaves: -1},
+ {numOctaves: NaN},
+ {numOctaves: Infinity},
+ {numOctaves: undefined},
+ {numOctaves: -Infinity},
+ {numOctaves: [1, 1]},
+ {numOctaves: 'test'},
+
+ {seed: {}},
+ {seed: NaN},
+ {seed: Infinity},
+ {seed: undefined},
+ {seed: -Infinity},
+ {seed: [1, 1]},
+ {seed: 'test'},
+
+ {stitchTiles: {}},
+ {stitchTiles: NaN},
+ {stitchTiles: Infinity},
+ {stitchTiles: undefined},
+ {stitchTiles: -Infinity},
+ {stitchTiles: [1, 1]},
+ {stitchTiles: 'test'},
+ {stitchTiles: null},
+ {stitchTiles: []},
+ {stitchTiles: [10]},
+ {stitchTiles: 30},
+ {stitchTiles: false},
+ {stitchTiles: true},
+ {stitchTiles: '10'},
+ {stitchTiles: -1},
+
+ {type: {}},
+ {type: NaN},
+ {type: Infinity},
+ {type: undefined},
+ {type: -Infinity},
+ {type: [1, 1]},
+ {type: 'test'},
+ {type: null},
+ {type: []},
+ {type: [10]},
+ {type: 30},
+ {type: false},
+ {type: true},
+ {type: '10'},
+ {type: -1},
+ ]
+
+ // null and [] = 0 when parsed as number
+ const workingTestCases = [
+ {baseFrequency: null},
+ {baseFrequency: []},
+ {baseFrequency: [10]},
+ {baseFrequency: [10, 3]},
+ {baseFrequency: 30},
+ {baseFrequency: false},
+ {baseFrequency: true},
+ {baseFrequency: '10'},
+
+ {numOctaves: null},
+ {numOctaves: []},
+ {numOctaves: [10]},
+ {numOctaves: 30},
+ {numOctaves: false},
+ {numOctaves: true},
+ {numOctaves: '10'},
+
+ {seed: null},
+ {seed: []},
+ {seed: [10]},
+ {seed: 30},
+ {seed: false},
+ {seed: true},
+ {seed: '10'},
+ {seed: -1},
+
+ {stitchTiles: 'stitch'},
+ {stitchTiles: 'noStitch'},
+
+ {type: 'fractalNoise'},
+ {type: 'turbulence'},
+ ]
+
+ for (testCase of errorTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ @assert throws TypeError new CanvasFilter(filterOptions);
+ }
+
+ for (testCase of workingTestCases) {
+ const filterOptions = {...{name: 'turbulence'}, ...testCase};
+ @assert new CanvasFilter(filterOptions) != null;
+ }
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml
new file mode 100644
index 0000000000..a44cb2ea2c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml
@@ -0,0 +1,1022 @@
+- name: 2d.layer.global-states
+ desc: Checks that layers correctly use global render states.
+ size: [200, 200]
+ code: |
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ {{ render_states }}
+
+ ctx.beginLayer();
+
+ // Enable compositing in the layer to validate that draw calls in the layer
+ // won't individually composite with the background.
+ ctx.globalCompositeOperation = 'screen';
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ reference: |
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ {{ render_states }}
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.globalCompositeOperation = 'screen';
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(50, 50, 75, 50);
+ ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2.fillRect(70, 70, 75, 50);
+
+ ctx.drawImage(canvas2, 0, 0);
+ variants: &global-state-variants
+ no-global-states:
+ render_states: // No global states.
+ alpha:
+ render_states: ctx.globalAlpha = 0.6;
+ blending:
+ render_states: ctx.globalCompositeOperation = 'multiply';
+ composite:
+ render_states: ctx.globalCompositeOperation = 'source-in';
+ shadow:
+ render_states: |-
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+ alpha.blending:
+ render_states: |-
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ alpha.composite:
+ render_states: |-
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ alpha.shadow:
+ render_states: |-
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+ alpha.blending.shadow:
+ render_states: |-
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+ alpha.composite.shadow:
+ render_states: |-
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+ blending.shadow:
+ render_states: |-
+ ctx.globalCompositeOperation = 'multiply';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+ composite.shadow:
+ render_states: |-
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
+ ctx.shadowBlur = 3;
+
+- name: 2d.layer.global-states.filter
+ desc: Checks that layers with filters correctly use global render states.
+ size: [200, 200]
+ code: |
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ {{ render_states }}
+
+ ctx.beginLayer({filter: [
+ {name: 'colorMatrix', values: [0.393, 0.769, 0.189, 0, 0,
+ 0.349, 0.686, 0.168, 0, 0,
+ 0.272, 0.534, 0.131, 0, 0,
+ 0, 0, 0, 1, 0]},
+ {name: 'componentTransfer',
+ funcA: {type: "table", tableValues: [0, 0.7]}},
+ {name: 'dropShadow', dx: 5, dy: 5, floodColor: '#81e'}]});
+
+ ctx.fillStyle = 'rgba(200, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 200, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ reference: |
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="{{ size[0] }}" height="{{ size[1] }}"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feColorMatrix
+ type="matrix"
+ values="0.393 0.769 0.189 0 0
+ 0.349 0.686 0.168 0 0
+ 0.272 0.534 0.131 0 0
+ 0 0 0 1 0" />
+ <feComponentTransfer>
+ <feFuncA type="table" tableValues="0 0.7"></feFuncA>
+ </feComponentTransfer>
+ <feDropShadow dx="5" dy="5" flood-color="#81e" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="75" height="50" fill="rgba(200, 0, 0, 1)"/>
+ <rect x="70" y="70" width="75" height="50" fill="rgba(0, 200, 0, 1)"/>
+ </g>
+ </svg>`;
+
+ const img = new Image();
+ img.width = {{ size[0] }};
+ img.height = {{ size[1] }};
+ img.onload = () => {
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+ var circle = new Path2D();
+ circle.arc(90, 90, 45, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ {{ render_states }}
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+ variants: *global-state-variants
+
+- name: 2d.layer.global-filter
+ desc: Tests that layers ignore the global context filter.
+ size: [150, 100]
+ code: |
+ ctx.filter = 'blur(5px)'
+
+ ctx.beginLayer();
+ ctx.fillRect(10, 10, 30, 30); // `ctx.filter` applied to draw call.
+ ctx.endLayer();
+
+ ctx.beginLayer();
+ ctx.filter = 'none';
+ ctx.fillRect(60, 10, 30, 30); // Should not be filted by the layer.
+ ctx.endLayer();
+
+ ctx.fillRect(110, 10, 30, 30); // `ctx.filter` is still set.
+ reference: |
+ ctx.fillRect(60, 10, 30, 30);
+ ctx.filter = 'blur(5px)'
+ ctx.fillRect(10, 10, 30, 30);
+ ctx.fillRect(110, 10, 30, 30);
+
+- name: 2d.layer.nested
+ desc: Tests nested canvas layers.
+ size: [200, 200]
+ code: |
+ var circle = new Path2D();
+ circle.arc(90, 90, 40, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ ctx.endLayer();
+ reference: |
+ var circle = new Path2D();
+ circle.arc(90, 90, 40, 0, 2 * Math.PI);
+ ctx.fill(circle);
+
+ ctx.globalCompositeOperation = 'source-in';
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+
+ ctx2.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx2.fillRect(60, 60, 75, 50);
+
+ ctx2.globalAlpha = 0.5;
+
+ canvas3 = document.createElement("canvas");
+ ctx3 = canvas3.getContext("2d");
+
+ ctx3.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx3.fillRect(50, 50, 75, 50);
+ ctx3.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx3.fillRect(70, 70, 75, 50);
+
+ ctx2.drawImage(canvas3, 0, 0);
+ ctx.drawImage(canvas2, 0, 0);
+
+
+- name: 2d.layer.restore-style
+ desc: Test that ensure layers restores style values upon endLayer.
+ size: [200, 200]
+ fuzzy: maxDifference=0-1; totalPixels=0-950
+ code: |
+ ctx.fillStyle = 'rgba(0,0,255,1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(70, 70, 75, 50);
+ reference: |
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ canvas2 = document.createElement("canvas");
+ ctx2 = canvas2.getContext("2d");
+ ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2.fillRect(60, 60, 75, 50);
+ ctx.drawImage(canvas2, 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+
+- name: 2d.layer.layer-rendering-state-reset-in-layer
+ desc: Tests that layers ignore the global context filter.
+ code: |
+ ctx.globalAlpha = 0.5;
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#0000ff';
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 20;
+ ctx.shadowBlur = 30;
+
+ @assert ctx.globalAlpha === 0.5;
+ @assert ctx.globalCompositeOperation === 'xor';
+ @assert ctx.shadowColor === '#0000ff';
+ @assert ctx.shadowOffsetX === 10;
+ @assert ctx.shadowOffsetY === 20;
+ @assert ctx.shadowBlur === 30;
+
+ ctx.beginLayer();
+
+ @assert ctx.globalAlpha === 1.0;
+ @assert ctx.globalCompositeOperation === 'source-over';
+ @assert ctx.shadowColor === 'rgba(0, 0, 0, 0)';
+ @assert ctx.shadowOffsetX === 0;
+ @assert ctx.shadowOffsetY === 0;
+ @assert ctx.shadowBlur === 0;
+
+ ctx.endLayer();
+
+ @assert ctx.globalAlpha === 0.5;
+ @assert ctx.globalCompositeOperation === 'xor';
+ @assert ctx.shadowColor === '#0000ff';
+ @assert ctx.shadowOffsetX === 10;
+ @assert ctx.shadowOffsetY === 20;
+ @assert ctx.shadowBlur === 30;
+
+- name: 2d.layer.clip-outside
+ desc: Check clipping set outside the layer
+ size: [100, 100]
+ code: |
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+ reference: |
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+ ctx2.endLayer();
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.drawImage(canvas2, 0, 0);
+
+- name: 2d.layer.clip-inside
+ desc: Check clipping set inside the layer
+ size: [100, 100]
+ code: |
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+ reference: |
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginPath();
+ ctx2.rect(15, 15, 70, 70);
+ ctx2.clip();
+
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx.drawImage(canvas2, 0, 0);
+ ctx.endLayer();
+
+- name: 2d.layer.clip-inside-and-outside
+ desc: Check clipping set inside and outside the layer
+ size: [100, 100]
+ code: |
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 80);
+ ctx.endLayer();
+ reference: |
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+
+ ctx2.beginPath();
+ ctx2.rect(15, 15, 70, 70);
+ ctx2.clip();
+
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(10, 10, 80, 80);
+
+ const canvas3 = new OffscreenCanvas(200, 200);
+ const ctx3 = canvas3.getContext('2d');
+
+ ctx3.beginLayer({filter: {name: "gaussianBlur", stdDeviation: 12}});
+ ctx3.drawImage(canvas2, 0, 0);
+ ctx3.endLayer();
+
+ ctx.beginPath();
+ ctx.rect(15, 15, 70, 70);
+ ctx.clip();
+ ctx.drawImage(canvas3, 0, 0);
+
+- name: 2d.layer.flush-on-frame-presentation
+ desc: Check that layers state stack is flushed and rebuilt on frame renders.
+ size: [200, 200]
+ canvasType: ['HTMLCanvas']
+ test_type: "promise"
+ code: |
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ reference: |
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+- name: 2d.layer.malformed-operations
+ desc: >-
+ Check that exceptions are thrown for operations that are malformed while
+ layers are open.
+ size: [200, 200]
+ code: |
+ {{ setup }}
+ // Shouldn't throw on its own.
+ {{ operation }};
+ // Make sure the exception isn't caused by calling the function twice.
+ {{ operation }};
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ assert_throws_dom("InvalidStateError",
+ () => {{ operation }});
+ variants:
+ createPattern:
+ operation: ctx.createPattern(canvas, 'repeat')
+ drawImage:
+ setup: |-
+ const canvas2 = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
+ const ctx2 = canvas2.getContext('2d');
+ operation: |-
+ ctx2.drawImage(canvas, 0, 0)
+ getImageData:
+ operation: ctx.getImageData(0, 0, {{ size[0] }}, {{ size[1] }})
+ putImageData:
+ setup: |-
+ const canvas2 = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
+ const ctx2 = canvas2.getContext('2d')
+ const data = ctx2.getImageData(0, 0, 1, 1);
+ operation: |-
+ ctx.putImageData(data, 0, 0)
+ toDataURL:
+ canvasType: ['HTMLCanvas']
+ operation: canvas.toDataURL()
+ transferToImageBitmap:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ operation: canvas.transferToImageBitmap()
+
+- name: 2d.layer.malformed-operations-with-promises
+ desc: >-
+ Check that exceptions are thrown for operations that are malformed while
+ layers are open.
+ size: [200, 200]
+ test_type: "promise"
+ code: |
+ // Shouldn't throw on its own.
+ await {{ operation }};
+ // Make sure the exception isn't caused by calling the function twice.
+ await {{ operation }};
+ // Calling again inside a layer should throw.
+ ctx.beginLayer();
+ await promise_rejects_dom(t, 'InvalidStateError', {{ operation }});
+ variants:
+ convertToBlob:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ operation: |-
+ canvas.convertToBlob()
+ createImageBitmap:
+ operation: createImageBitmap(canvas)
+ toBlob:
+ canvasType: ['HTMLCanvas']
+ operation: |-
+ new Promise(resolve => canvas.toBlob(resolve))
+
+- name: 2d.layer.several-complex
+ desc: >-
+ Test to ensure beginlayer works for filter, alpha and shadow, even with
+ consecutive layers.
+ size: [500, 500]
+ fuzzy: maxDifference=0-3; totalPixels=0-6318
+ code: |
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 95, 70);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'orange';
+ ctx.shadowBlur = 3
+
+ for (let i = 0; i < 5; i++) {
+ ctx.beginLayer();
+
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(60 + i, 40 + i, 75, 50);
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(80 + i, 60 + i, 75, 50);
+
+ ctx.endLayer();
+ }
+ reference: |
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(50, 50, 95, 70);
+
+ ctx.globalAlpha = 0.5;
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'orange';
+ ctx.shadowBlur = 3;
+
+ var canvas2 = [5];
+ var ctx2 = [5];
+
+ for (let i = 0; i < 5; i++) {
+ canvas2[i] = document.createElement("canvas");
+ ctx2[i] = canvas2[i].getContext("2d");
+ ctx2[i].fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx2[i].fillRect(60, 40, 75, 50);
+ ctx2[i].fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx2[i].fillRect(80, 60, 75, 50);
+
+ ctx.drawImage(canvas2[i], i, i);
+ }
+
+- name: 2d.layer.reset
+ desc: Checks that reset discards any pending layers.
+ code: |
+ // Global states:
+ ctx.globalAlpha = 0.3;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -3;
+ ctx.shadowOffsetY = 3;
+ ctx.shadowColor = 'rgba(0, 30, 0, 0.3)';
+ ctx.shadowBlur = 3;
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -3, dy: 3}});
+
+ // Layer states:
+ ctx.globalAlpha = 0.6;
+ ctx.globalCompositeOperation = 'source-in';
+ ctx.shadowOffsetX = -6;
+ ctx.shadowOffsetY = 6;
+ ctx.shadowColor = 'rgba(0, 60, 0, 0.6)';
+ ctx.shadowBlur = 3;
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 75, 50);
+ reference:
+ ctx.fillRect(10, 10, 75, 50);
+
+- name: 2d.layer.clearRect.partial
+ desc: clearRect inside a layer can clear a portion of the layer content.
+ size: [100, 100]
+ code: |
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.clearRect(30, 30, 60, 30);
+ ctx.endLayer();
+ reference: |
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 10);
+ ctx.fillRect(20, 60, 80, 10);
+ ctx.fillRect(20, 20, 10, 50);
+ ctx.fillRect(90, 20, 10, 50);
+
+- name: 2d.layer.clearRect.full
+ desc: clearRect inside a layer can clear all of the layer content.
+ size: [100, 100]
+ code: |
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'red';
+ ctx.fillRect(20, 20, 80, 50);
+ ctx.fillStyle = 'green';
+ ctx.clearRect(0, 0, {{ size[0] }}, {{ size[1] }});
+ ctx.endLayer();
+ reference: |
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(10, 10, 80, 50);
+
+- name: 2d.layer.valid-calls
+ desc: No exception raised on {{ variant_desc }}.
+ variants:
+ save:
+ variant_desc: lone save() calls
+ code: ctx.save();
+ beginLayer:
+ variant_desc: lone beginLayer() calls
+ code: ctx.beginLayer();
+ restore:
+ variant_desc: lone restore() calls
+ code: ctx.restore();
+ save_restore:
+ variant_desc: save() + restore()
+ code: |-
+ ctx.save();
+ ctx.restore();
+ save_reset_restore:
+ variant_desc: save() + reset() + restore()
+ code: |-
+ ctx.save();
+ ctx.reset();
+ ctx.restore();
+ beginLayer-endLayer:
+ variant_desc: beginLayer() + endLayer()
+ code: |-
+ ctx.beginLayer();
+ ctx.save();
+ save-beginLayer:
+ variant_desc: save() + beginLayer()
+ code: |-
+ ctx.save();
+ ctx.beginLayer();
+ beginLayer-save:
+ variant_desc: beginLayer() + save()
+ code: |-
+ ctx.beginLayer();
+ ctx.save();
+
+- name: 2d.layer.invalid-calls
+ desc: Raises exception on {{ variant_desc }}.
+ code: |
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ {{ call_sequence | indent(2) }}
+ });
+ variants:
+ endLayer:
+ variant_desc: lone endLayer calls
+ call_sequence: ctx.endLayer();
+ save-endLayer:
+ variant_desc: save() + endLayer()
+ call_sequence: |-
+ ctx.save();
+ ctx.endLayer();
+ beginLayer-restore:
+ variant_desc: beginLayer() + restore()
+ call_sequence: |-
+ ctx.beginLayer();
+ ctx.restore();
+ save-beginLayer-restore:
+ variant_desc: save() + beginLayer() + restore()
+ call_sequence: |-
+ ctx.save();
+ ctx.beginLayer();
+ ctx.restore();
+ beginLayer-save-endLayer:
+ variant_desc: beginLayer() + save() + endLayer()
+ call_sequence: |-
+ ctx.beginLayer();
+ ctx.save();
+ ctx.endLayer();
+ beginLayer-reset-endLayer:
+ variant_desc: beginLayer() + reset() + endLayer()
+ call_sequence: |-
+ ctx.beginLayer();
+ ctx.reset();
+ ctx.endLayer();
+
+- name: 2d.layer.exceptions-are-no-op
+ desc: Checks that the context state is left unchanged if beginLayer throws.
+ code: |
+ // Get `beginLayer` to throw while parsing the filter.
+ assert_throws_js(TypeError,
+ () => ctx.beginLayer({filter: {name: 'colorMatrix',
+ values: 'foo'}}));
+ // `beginLayer` shouldn't have opened the layer, so `endLayer` should throw.
+ assert_throws_dom("InvalidStateError", () => ctx.endLayer());
+
+- name: 2d.layer.cross-layer-paths
+ desc: Checks that path defined in a layer is usable outside.
+ code: |
+ ctx.beginLayer();
+ ctx.translate(50, 0);
+ ctx.moveTo(0, 0);
+ ctx.endLayer();
+ ctx.lineTo(50, 100);
+ ctx.stroke();
+ reference:
+ ctx.moveTo(50, 0);
+ ctx.lineTo(50, 100);
+ ctx.stroke();
+
+- name: 2d.layer.beginLayer-options
+ desc: Checks beginLayer works for different option parameter values
+ code: |
+ ctx.beginLayer(); ctx.endLayer();
+ ctx.beginLayer(null); ctx.endLayer();
+ ctx.beginLayer(undefined); ctx.endLayer();
+ ctx.beginLayer([]); ctx.endLayer();
+ ctx.beginLayer({}); ctx.endLayer();
+
+ @assert throws TypeError ctx.beginLayer('');
+ @assert throws TypeError ctx.beginLayer(0);
+ @assert throws TypeError ctx.beginLayer(1);
+ @assert throws TypeError ctx.beginLayer(true);
+ @assert throws TypeError ctx.beginLayer(false);
+
+ ctx.beginLayer({filter: null}); ctx.endLayer();
+ ctx.beginLayer({filter: undefined}); ctx.endLayer();
+ ctx.beginLayer({filter: []}); ctx.endLayer();
+ ctx.beginLayer({filter: {}}); ctx.endLayer();
+ ctx.beginLayer({filter: {name: "unknown"}}); ctx.endLayer();
+ ctx.beginLayer({filter: ''}); ctx.endLayer();
+
+ // These cases don't throw TypeError since they can be casted to a
+ // DOMString.
+ ctx.beginLayer({filter: 0}); ctx.endLayer();
+ ctx.beginLayer({filter: 1}); ctx.endLayer();
+ ctx.beginLayer({filter: true}); ctx.endLayer();
+ ctx.beginLayer({filter: false}); ctx.endLayer();
+
+- name: 2d.layer.blur-from-outside-canvas
+ desc: Checks blur leaking inside from drawing outside the canvas
+ size: [200, 200]
+ code: |
+ {{ clipping }}
+
+ ctx.beginLayer({filter: [ {name: 'gaussianBlur', stdDeviation: 30} ]});
+
+ ctx.fillStyle = 'turquoise';
+ ctx.fillRect(201, 50, 100, 100);
+ ctx.fillStyle = 'indigo';
+ ctx.fillRect(50, 201, 100, 100);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(-1, 50, -100, 100);
+ ctx.fillStyle = 'brown';
+ ctx.fillRect(50, -1, 100, -100);
+
+ ctx.endLayer();
+ reference: |
+ const svg = `
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="{{ size[0] }}" height="{{ size[1] }}"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="30" />
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="201" y="50" width="100" height="100" fill="turquoise"/>
+ <rect x="50" y="201" width="100" height="100" fill="indigo"/>
+ <rect x="-101" y="50" width="100" height="100" fill="orange"/>
+ <rect x="50" y="-101" width="100" height="100" fill="brown"/>
+ </g>
+ </svg>`;
+ const img = new Image();
+ img.width = {{ size[0] }};
+ img.height = {{ size[1] }};
+ img.onload = () => {
+ {{ clipping | indent(4) }}
+
+ ctx.drawImage(img, 0, 0);
+ };
+ img.src = 'data:image/svg+xml;base64,' + btoa(svg);
+ variants:
+ no-clipping:
+ clipping: // No clipping.
+ with-clipping:
+ clipping: |-
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+- name: 2d.layer.shadow-from-outside-canvas
+ desc: Checks shadow produced by object drawn outside the canvas
+ size: [200, 200]
+ code: |
+ {{ distance }}
+
+ {{ clipping }}
+
+ ctx.beginLayer({filter: [
+ {name: 'dropShadow', dx: -({{ size[0] }} + delta),
+ dy: -({{ size[1] }} + delta), stdDeviation: 0,
+ floodColor: 'green'},
+ ]});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect({{ size[0] }} + delta, {{ size[1] }} + delta, 100, 100);
+
+ ctx.endLayer();
+ reference: |
+ {{ distance }}
+
+ {{ clipping }}
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 100, 100);
+ variants:
+ short-distance:
+ distance: |-
+ const delta = 1;
+ clipping: // No clipping.
+ short-distance-with-clipping:
+ distance: |-
+ const delta = 1;
+ clipping: |-
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+ long-distance:
+ distance: |-
+ const delta = 10000;
+ clipping: // No clipping.
+ long-distance-with-clipping:
+ distance: |-
+ const delta = 10000;
+ clipping: |-
+ const clipRegion = new Path2D();
+ clipRegion.rect(20, 20, 160, 160);
+ ctx.clip(clipRegion);
+
+- name: 2d.layer.opaque-canvas
+ desc: Checks that layer blending works inside opaque canvas
+ size: [300, 300]
+ code: |
+ {% if canvas_type == 'htmlcanvas' %}
+ const canvas2 = document.createElement('canvas');
+ canvas2.width = 200;
+ canvas2.height = 200;
+ {% else %}
+ const canvas2 = new OffscreenCanvas(200, 200);
+ {% endif %}
+ const ctx2 = canvas2.getContext('2d', {alpha: false});
+
+ ctx2.fillStyle = 'purple';
+ ctx2.fillRect(10, 10, 100, 100);
+
+ ctx2.beginLayer({filter: {name: 'dropShadow', dx: -10, dy: 10,
+ stdDeviation: 0,
+ floodColor: 'rgba(200, 100, 50, 0.5)'}});
+ ctx2.fillStyle = 'green';
+ ctx2.fillRect(50, 50, 100, 100);
+ ctx2.globalAlpha = 0.8;
+ ctx2.fillStyle = 'yellow';
+ ctx2.fillRect(75, 25, 100, 100);
+ ctx2.endLayer();
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 300, 300);
+ ctx.drawImage(canvas2, 0, 0);
+ reference: |
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 300, 300);
+
+ ctx.fillStyle = 'black';
+ ctx.fillRect(0, 0, 200, 200);
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(10, 10, 100, 100);
+
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'green';
+ ctx2.fillRect(50, 50, 100, 100);
+ ctx2.globalAlpha = 0.8;
+ ctx2.fillStyle = 'yellow';
+ ctx2.fillRect(75, 25, 100, 100);
+
+ ctx.shadowColor = 'rgba(200, 100, 50, 0.5)';
+ ctx.shadowOffsetX = -10;
+ ctx.shadowOffsetY = 10;
+ ctx.drawImage(canvas2, 0, 0);
+
+- name: 2d.layer.css-filters
+ desc: Checks that beginLayer works with a CSS filter string as input.
+ size: [200, 200]
+ code: &filter-test-code |
+ ctx.beginLayer({filter: {{ ctx_filter }}});
+
+ ctx.fillStyle = 'teal';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+ html_reference: &filter-test-reference |
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="{{ size[0] }}" height="{{ size[1] }}"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ {{ svg_filter | indent(4) }}
+ </filter>
+ <g filter="url(#filter)">
+ <rect x="50" y="50" width="100" height="100" fill="teal"/>
+ </g>
+ </svg>
+ variants:
+ blur:
+ ctx_filter: |-
+ 'blur(10px)'
+ svg_filter: |-
+ <feGaussianBlur stdDeviation="10" />
+ shadow:
+ ctx_filter: |-
+ 'drop-shadow(-10px -10px 5px purple)'
+ svg_filter: |-
+ <feDropShadow dx="-10" dy="-10" stdDeviation="5" flood-color="purple" />
+ blur-and-shadow:
+ ctx_filter: |-
+ 'blur(5px) drop-shadow(10px 10px 5px orange)'
+ svg_filter: |-
+ <feGaussianBlur stdDeviation="5" />
+ <feDropShadow dx="10" dy="10" stdDeviation="5" flood-color="orange" />
+
+- name: 2d.layer.anisotropic-blur
+ desc: Checks that layers allow gaussian blur with separate X and Y components.
+ size: [200, 200]
+ code: *filter-test-code
+ html_reference: *filter-test-reference
+ variants:
+ x-only:
+ ctx_filter: |-
+ { name: 'gaussianBlur', stdDeviation: [4, 0] }
+ svg_filter: |-
+ <feGaussianBlur stdDeviation="4 0" />
+ mostly-x:
+ ctx_filter: |-
+ { name: 'gaussianBlur', stdDeviation: [4, 1] }
+ svg_filter: |-
+ <feGaussianBlur stdDeviation="4 1" />
+ isotropic:
+ ctx_filter: |-
+ { name: 'gaussianBlur', stdDeviation: [4, 4] }
+ svg_filter: |-
+ <feGaussianBlur stdDeviation="4 4" />
+ mostly-y:
+ ctx_filter: |-
+ { name: 'gaussianBlur', stdDeviation: [1, 4] }
+ svg_filter: |-
+ <feGaussianBlur stdDeviation="1 4" />
+ y-only:
+ ctx_filter: |-
+ { name: 'gaussianBlur', stdDeviation: [0, 4] }
+ svg_filter: |-
+ <feGaussianBlur stdDeviation="0 4" />
+
+- name: 2d.layer.nested-filters
+ desc: Checks that nested layers work properly when both apply filters.
+ size: [400, 200]
+ code: |
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -20, dy: -20,
+ stdDeviation: 0, floodColor: 'yellow'}});
+ ctx.beginLayer({filter: 'drop-shadow(-10px -10px 0 blue)'});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'drop-shadow(20px 20px 0 blue)'});
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: 10, dy: 10,
+ stdDeviation: 0, floodColor: 'yellow'}});
+
+ ctx.fillStyle = 'red';
+ ctx.fillRect(250, 50, 100, 100);
+
+ ctx.endLayer();
+ ctx.endLayer();
+ reference: |
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(20, 20, 100, 100);
+ ctx.fillRect(30, 30, 100, 100);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(40, 40, 100, 100);
+ ctx.fillStyle = 'red';
+ ctx.fillRect(50, 50, 100, 100);
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(280, 80, 100, 100);
+ ctx.fillRect(270, 70, 100, 100);
+ ctx.fillStyle = 'yellow';
+ ctx.fillRect(260, 60, 100, 100);
+ ctx.fillStyle = 'red';
+ ctx.fillRect(250, 50, 100, 100);
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/line-styles.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/line-styles.yaml
new file mode 100644
index 0000000000..47bf3af97e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/line-styles.yaml
@@ -0,0 +1,954 @@
+- name: 2d.line.defaults
+ code: |
+ @assert ctx.lineWidth === 1;
+ @assert ctx.lineCap === 'butt';
+ @assert ctx.lineJoin === 'miter';
+ @assert ctx.miterLimit === 10;
+
+- name: 2d.line.width.basic
+ desc: lineWidth determines the width of line strokes
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 20;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ @assert pixel 14,25 == 0,255,0,255;
+ @assert pixel 15,25 == 0,255,0,255;
+ @assert pixel 16,25 == 0,255,0,255;
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 34,25 == 0,255,0,255;
+ @assert pixel 35,25 == 0,255,0,255;
+ @assert pixel 36,25 == 0,255,0,255;
+
+ @assert pixel 64,25 == 0,255,0,255;
+ @assert pixel 65,25 == 0,255,0,255;
+ @assert pixel 66,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ @assert pixel 84,25 == 0,255,0,255;
+ @assert pixel 85,25 == 0,255,0,255;
+ @assert pixel 86,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.width.transformed
+ desc: Line stroke widths are affected by scale transformations
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 4;
+ // Draw a green line over a red box, to check the line is not too small
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.save();
+ ctx.scale(5, 1);
+ ctx.beginPath();
+ ctx.moveTo(5, 15);
+ ctx.lineTo(5, 35);
+ ctx.stroke();
+ ctx.restore();
+
+ // Draw a green box over a red line, to check the line is not too large
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.save();
+ ctx.scale(-5, 1);
+ ctx.beginPath();
+ ctx.moveTo(-15, 15);
+ ctx.lineTo(-15, 35);
+ ctx.stroke();
+ ctx.restore();
+ ctx.fillRect(65, 15, 20, 20);
+
+ @assert pixel 14,25 == 0,255,0,255;
+ @assert pixel 15,25 == 0,255,0,255;
+ @assert pixel 16,25 == 0,255,0,255;
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 34,25 == 0,255,0,255;
+ @assert pixel 35,25 == 0,255,0,255;
+ @assert pixel 36,25 == 0,255,0,255;
+
+ @assert pixel 64,25 == 0,255,0,255;
+ @assert pixel 65,25 == 0,255,0,255;
+ @assert pixel 66,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ @assert pixel 84,25 == 0,255,0,255;
+ @assert pixel 85,25 == 0,255,0,255;
+ @assert pixel 86,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.width.scaledefault
+ desc: Default lineWidth strokes are affected by scale transformations
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(50, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.moveTo(0, 0.5);
+ ctx.lineTo(2, 0.5);
+ ctx.stroke();
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ @assert pixel 50,5 == 0,255,0,255;
+ @assert pixel 50,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.width.valid
+ desc: Setting lineWidth to valid values works
+ code: |
+ ctx.lineWidth = 1.5;
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = "1e1";
+ @assert ctx.lineWidth === 10;
+
+ ctx.lineWidth = 1/1024;
+ @assert ctx.lineWidth === 1/1024;
+
+ ctx.lineWidth = 1000;
+ @assert ctx.lineWidth === 1000;
+
+- name: 2d.line.width.invalid
+ desc: Setting lineWidth to invalid values is ignored
+ code: |
+ ctx.lineWidth = 1.5;
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 0;
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -1;
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = Infinity;
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = -Infinity;
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = NaN;
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = 'string';
+ @assert ctx.lineWidth === 1.5;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = true;
+ @assert ctx.lineWidth === 1;
+
+ ctx.lineWidth = 1.5;
+ ctx.lineWidth = false;
+ @assert ctx.lineWidth === 1.5;
+
+- name: 2d.line.cap.butt
+ desc: lineCap 'butt' is rendered correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'butt';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 15, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 15, 20, 20);
+
+ @assert pixel 25,14 == 0,255,0,255;
+ @assert pixel 25,15 == 0,255,0,255;
+ @assert pixel 25,16 == 0,255,0,255;
+ @assert pixel 25,34 == 0,255,0,255;
+ @assert pixel 25,35 == 0,255,0,255;
+ @assert pixel 25,36 == 0,255,0,255;
+
+ @assert pixel 75,14 == 0,255,0,255;
+ @assert pixel 75,15 == 0,255,0,255;
+ @assert pixel 75,16 == 0,255,0,255;
+ @assert pixel 75,34 == 0,255,0,255;
+ @assert pixel 75,35 == 0,255,0,255;
+ @assert pixel 75,36 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.cap.round
+ desc: lineCap 'round' is rendered correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineCap = 'round';
+ ctx.lineWidth = 20;
+
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(35-tol, 15);
+ ctx.arc(25, 15, 10-tol, 0, Math.PI, true);
+ ctx.arc(25, 35, 10-tol, Math.PI, 0, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(85+tol, 15);
+ ctx.arc(75, 15, 10+tol, 0, Math.PI, true);
+ ctx.arc(75, 35, 10+tol, Math.PI, 0, true);
+ ctx.fill();
+
+ @assert pixel 17,6 == 0,255,0,255;
+ @assert pixel 25,6 == 0,255,0,255;
+ @assert pixel 32,6 == 0,255,0,255;
+ @assert pixel 17,43 == 0,255,0,255;
+ @assert pixel 25,43 == 0,255,0,255;
+ @assert pixel 32,43 == 0,255,0,255;
+
+ @assert pixel 67,6 == 0,255,0,255;
+ @assert pixel 75,6 == 0,255,0,255;
+ @assert pixel 82,6 == 0,255,0,255;
+ @assert pixel 67,43 == 0,255,0,255;
+ @assert pixel 75,43 == 0,255,0,255;
+ @assert pixel 82,43 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.cap.square
+ desc: lineCap 'square' is rendered correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(15, 5, 20, 40);
+ ctx.beginPath();
+ ctx.moveTo(25, 15);
+ ctx.lineTo(25, 35);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(75, 15);
+ ctx.lineTo(75, 35);
+ ctx.stroke();
+ ctx.fillRect(65, 5, 20, 40);
+
+ @assert pixel 25,4 == 0,255,0,255;
+ @assert pixel 25,5 == 0,255,0,255;
+ @assert pixel 25,6 == 0,255,0,255;
+ @assert pixel 25,44 == 0,255,0,255;
+ @assert pixel 25,45 == 0,255,0,255;
+ @assert pixel 25,46 == 0,255,0,255;
+
+ @assert pixel 75,4 == 0,255,0,255;
+ @assert pixel 75,5 == 0,255,0,255;
+ @assert pixel 75,6 == 0,255,0,255;
+ @assert pixel 75,44 == 0,255,0,255;
+ @assert pixel 75,45 == 0,255,0,255;
+ @assert pixel 75,46 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.cap.open
+ desc: Line caps are drawn at the corners of an unclosed rectangle
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.lineTo(200, 200);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.cap.closed
+ desc: Line caps are not drawn at the corners of an unclosed rectangle
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineCap = 'square';
+ ctx.lineWidth = 400;
+
+ ctx.beginPath();
+ ctx.moveTo(200, 200);
+ ctx.lineTo(200, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 200);
+ ctx.closePath();
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.cap.valid
+ desc: Setting lineCap to valid values works
+ code: |
+ ctx.lineCap = 'butt'
+ @assert ctx.lineCap === 'butt';
+
+ ctx.lineCap = 'round';
+ @assert ctx.lineCap === 'round';
+
+ ctx.lineCap = 'square';
+ @assert ctx.lineCap === 'square';
+
+- name: 2d.line.cap.invalid
+ desc: Setting lineCap to invalid values is ignored
+ code: |
+ ctx.lineCap = 'butt'
+ @assert ctx.lineCap === 'butt';
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'invalid';
+ @assert ctx.lineCap === 'butt';
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'ROUND';
+ @assert ctx.lineCap === 'butt';
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round\0';
+ @assert ctx.lineCap === 'butt';
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'round ';
+ @assert ctx.lineCap === 'butt';
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = "";
+ @assert ctx.lineCap === 'butt';
+
+ ctx.lineCap = 'butt';
+ ctx.lineCap = 'bevel';
+ @assert ctx.lineCap === 'butt';
+
+- name: 2d.line.fill.noop
+ desc: Filling a line draws nothing
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 20;
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(90, 30);
+ ctx.fill();
+ @assert pixel 50,24 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 50,26 == 0,255,0,255;
+
+- name: 2d.line.join.bevel
+ desc: lineJoin 'bevel' is rendered correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.lineTo(40-tol, 20);
+ ctx.lineTo(30, 10+tol);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.lineTo(90+tol, 20);
+ ctx.lineTo(80, 10-tol);
+ ctx.fill();
+
+ @assert pixel 34,16 == 0,255,0,255;
+ @assert pixel 34,15 == 0,255,0,255;
+ @assert pixel 35,15 == 0,255,0,255;
+ @assert pixel 36,15 == 0,255,0,255;
+ @assert pixel 36,14 == 0,255,0,255;
+
+ @assert pixel 84,16 == 0,255,0,255;
+ @assert pixel 84,15 == 0,255,0,255;
+ @assert pixel 85,15 == 0,255,0,255;
+ @assert pixel 86,15 == 0,255,0,255;
+ @assert pixel 86,14 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.join.round
+ desc: lineJoin 'round' is rendered correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var tol = 1; // tolerance to avoid antialiasing artifacts
+
+ ctx.lineJoin = 'round';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 20, 20);
+ ctx.fillRect(20, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(30, 20);
+ ctx.arc(30, 20, 10-tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 20, 20);
+ ctx.fillRect(70, 20, 20, 20);
+ ctx.beginPath();
+ ctx.moveTo(80, 20);
+ ctx.arc(80, 20, 10+tol, 0, 2*Math.PI, true);
+ ctx.fill();
+
+ @assert pixel 36,14 == 0,255,0,255;
+ @assert pixel 36,13 == 0,255,0,255;
+ @assert pixel 37,13 == 0,255,0,255;
+ @assert pixel 38,13 == 0,255,0,255;
+ @assert pixel 38,12 == 0,255,0,255;
+
+ @assert pixel 86,14 == 0,255,0,255;
+ @assert pixel 86,13 == 0,255,0,255;
+ @assert pixel 87,13 == 0,255,0,255;
+ @assert pixel 88,13 == 0,255,0,255;
+ @assert pixel 88,12 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.join.miter
+ desc: lineJoin 'miter' is rendered correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 20;
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+
+ ctx.fillRect(10, 10, 30, 20);
+ ctx.fillRect(20, 10, 20, 30);
+
+ ctx.beginPath();
+ ctx.moveTo(10, 20);
+ ctx.lineTo(30, 20);
+ ctx.lineTo(30, 40);
+ ctx.stroke();
+
+
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+
+ ctx.beginPath();
+ ctx.moveTo(60, 20);
+ ctx.lineTo(80, 20);
+ ctx.lineTo(80, 40);
+ ctx.stroke();
+
+ ctx.fillRect(60, 10, 30, 20);
+ ctx.fillRect(70, 10, 20, 30);
+
+ @assert pixel 38,12 == 0,255,0,255;
+ @assert pixel 39,11 == 0,255,0,255;
+ @assert pixel 40,10 == 0,255,0,255;
+ @assert pixel 41,9 == 0,255,0,255;
+ @assert pixel 42,8 == 0,255,0,255;
+
+ @assert pixel 88,12 == 0,255,0,255;
+ @assert pixel 89,11 == 0,255,0,255;
+ @assert pixel 90,10 == 0,255,0,255;
+ @assert pixel 91,9 == 0,255,0,255;
+ @assert pixel 92,8 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.join.open
+ desc: Line joins are not drawn at the corner of an unclosed rectangle
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.join.closed
+ desc: Line joins are drawn at the corner of a closed rectangle
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineJoin = 'miter';
+ ctx.lineWidth = 200;
+
+ ctx.beginPath();
+ ctx.moveTo(100, 50);
+ ctx.lineTo(100, 1000);
+ ctx.lineTo(1000, 1000);
+ ctx.lineTo(1000, 50);
+ ctx.closePath();
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.join.parallel
+ desc: Line joins are drawn at 180-degree joins
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 300;
+ ctx.lineJoin = 'round';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(-100, 25);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.join.valid
+ desc: Setting lineJoin to valid values works
+ code: |
+ ctx.lineJoin = 'bevel'
+ @assert ctx.lineJoin === 'bevel';
+
+ ctx.lineJoin = 'round';
+ @assert ctx.lineJoin === 'round';
+
+ ctx.lineJoin = 'miter';
+ @assert ctx.lineJoin === 'miter';
+
+- name: 2d.line.join.invalid
+ desc: Setting lineJoin to invalid values is ignored
+ code: |
+ ctx.lineJoin = 'bevel'
+ @assert ctx.lineJoin === 'bevel';
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'invalid';
+ @assert ctx.lineJoin === 'bevel';
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'ROUND';
+ @assert ctx.lineJoin === 'bevel';
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round\0';
+ @assert ctx.lineJoin === 'bevel';
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'round ';
+ @assert ctx.lineJoin === 'bevel';
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = "";
+ @assert ctx.lineJoin === 'bevel';
+
+ ctx.lineJoin = 'bevel';
+ ctx.lineJoin = 'butt';
+ @assert ctx.lineJoin === 'bevel';
+
+- name: 2d.line.miter.exceeded
+ desc: Miter joins are not drawn when the miter limit is exceeded
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201); // slightly non-right-angle to avoid being a special case
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.miter.acute
+ desc: Miter joins are drawn correctly with acute angles
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 2.614;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 2.613;
+ ctx.beginPath();
+ ctx.moveTo(100, 1000);
+ ctx.lineTo(100, 100);
+ ctx.lineTo(1000, 1000);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.miter.obtuse
+ desc: Miter joins are drawn correctly with obtuse angles
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 1600;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.083;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.082;
+ ctx.beginPath();
+ ctx.moveTo(800, 10000);
+ ctx.lineTo(800, 300);
+ ctx.lineTo(10000, -8900);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.miter.rightangle
+ desc: Miter joins are not drawn when the miter limit is exceeded, on exact right
+ angles
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 200);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.miter.lineedge
+ desc: Miter joins are not drawn when the miter limit is exceeded at the corners
+ of a zero-height rectangle
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#f00';
+ ctx.miterLimit = 1.414;
+ ctx.beginPath();
+ ctx.strokeRect(100, 25, 200, 0);
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.miter.within
+ desc: Miter joins are drawn when the miter limit is not quite exceeded
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.miterLimit = 1.416;
+ ctx.beginPath();
+ ctx.moveTo(200, 1000);
+ ctx.lineTo(200, 200);
+ ctx.lineTo(1000, 201);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.miter.valid
+ desc: Setting miterLimit to valid values works
+ code: |
+ ctx.miterLimit = 1.5;
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = "1e1";
+ @assert ctx.miterLimit === 10;
+
+ ctx.miterLimit = 1/1024;
+ @assert ctx.miterLimit === 1/1024;
+
+ ctx.miterLimit = 1000;
+ @assert ctx.miterLimit === 1000;
+
+- name: 2d.line.miter.invalid
+ desc: Setting miterLimit to invalid values is ignored
+ code: |
+ ctx.miterLimit = 1.5;
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 0;
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -1;
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = Infinity;
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = -Infinity;
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = NaN;
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = 'string';
+ @assert ctx.miterLimit === 1.5;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = true;
+ @assert ctx.miterLimit === 1;
+
+ ctx.miterLimit = 1.5;
+ ctx.miterLimit = false;
+ @assert ctx.miterLimit === 1.5;
+
+- name: 2d.line.cross
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(110, 50);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(100, 60);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.union
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 24);
+ ctx.lineTo(100, 25);
+ ctx.lineTo(0, 26);
+ ctx.closePath();
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 25,1 == 0,255,0,255;
+ @assert pixel 48,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 25,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.line.invalid.strokestyle
+ desc: Verify correct behavior of canvas on an invalid strokeStyle()
+ code: |
+ ctx.strokeStyle = 'rgb(0, 255, 0)';
+ ctx.strokeStyle = 'nonsense';
+ ctx.lineWidth = 200;
+ ctx.moveTo(0,100);
+ ctx.lineTo(200,100);
+ ctx.stroke();
+ var imageData = ctx.getImageData(0, 0, 200, 200);
+ var imgdata = imageData.data;
+ @assert imgdata[4] == 0;
+ @assert imgdata[5] == 255;
+ @assert imgdata[6] == 0;
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/path-objects.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/path-objects.yaml
new file mode 100644
index 0000000000..b1754b9495
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/path-objects.yaml
@@ -0,0 +1,3403 @@
+- name: 2d.path.initial
+ #mozilla: { bug: TODO }
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.beginPath
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.moveTo.basic
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.rect(0, 0, 10, 50);
+ ctx.moveTo(100, 0);
+ ctx.lineTo(10, 0);
+ ctx.lineTo(10, 50);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 90,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.moveTo.newsubpath
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.moveTo(100, 0);
+ ctx.moveTo(100, 50);
+ ctx.moveTo(0, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.moveTo.multiple
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 25);
+ ctx.moveTo(100, 25);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.moveTo.nonfinite
+ desc: moveTo() with Infinity/NaN is ignored
+ code: |
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.moveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.closePath.empty
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.closePath.newline
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.closePath();
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.closePath.nextpoint
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -1000);
+ ctx.closePath();
+ ctx.lineTo(1000, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.lineTo.ensuresubpath.1
+ desc: If there is no subpath, the point is added and nothing is drawn
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(100, 50);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.lineTo.ensuresubpath.2
+ desc: If there is no subpath, the point is added and used for subsequent drawing
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.lineTo.basic
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.lineTo.nextpoint
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.lineTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.lineTo.nonfinite
+ desc: lineTo() with Infinity/NaN is ignored
+ code: |
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.lineTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.lineTo.nonfinite.details
+ desc: lineTo() with Infinity/NaN for first arg still converts the second arg
+ code: |
+ for (var arg1 of [Infinity, -Infinity, NaN]) {
+ var converted = false;
+ ctx.lineTo(arg1, { valueOf: function() { converted = true; return 0; } });
+ @assert converted;
+ }
+ expected: clear
+
+- name: 2d.path.quadraticCurveTo.ensuresubpath.1
+ desc: If there is no subpath, the first control point is added (and nothing is drawn
+ up to it)
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(100, 50, 200, 50);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 95,45 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.quadraticCurveTo.ensuresubpath.2
+ desc: If there is no subpath, the first control point is added
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.quadraticCurveTo(0, 25, 100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 5,45 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.quadraticCurveTo.basic
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.quadraticCurveTo(100, 25, 100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.quadraticCurveTo.shape
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-1000, 1050);
+ ctx.quadraticCurveTo(0, -1000, 1200, 1050);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.quadraticCurveTo.scaled
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-1, 1.05);
+ ctx.quadraticCurveTo(0, -1, 1.2, 1.05);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.quadraticCurveTo.nonfinite
+ desc: quadraticCurveTo() with Infinity/NaN is ignored
+ code: |
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.quadraticCurveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.bezierCurveTo.ensuresubpath.1
+ desc: If there is no subpath, the first control point is added (and nothing is drawn
+ up to it)
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(100, 50, 200, 50, 200, 50);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 95,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.bezierCurveTo.ensuresubpath.2
+ desc: If there is no subpath, the first control point is added
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.bezierCurveTo(0, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 5,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.bezierCurveTo.basic
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.bezierCurveTo(100, 25, 100, 25, 100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.bezierCurveTo.shape
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 55;
+ ctx.beginPath();
+ ctx.moveTo(-2000, 3100);
+ ctx.bezierCurveTo(-2000, -1000, 2100, -1000, 2100, 3100);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.bezierCurveTo.scaled
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(1000, 1000);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 0.055;
+ ctx.beginPath();
+ ctx.moveTo(-2, 3.1);
+ ctx.bezierCurveTo(-2, -1, 2.1, -1, 2.1, 3.1);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.bezierCurveTo.nonfinite
+ desc: bezierCurveTo() with Infinity/NaN is ignored
+ code: |
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.bezierCurveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.ensuresubpath.1
+ desc: If there is no subpath, the first control point is added (and nothing is drawn
+ up to it)
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arcTo(100, 50, 200, 50, 0.1);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.ensuresubpath.2
+ desc: If there is no subpath, the first control point is added
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arcTo(0, 25, 50, 250, 0.1); // adds (x1,y1), draws nothing
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.coincide.1
+ desc: arcTo() has no effect if P0 = P1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(0, 25, 50, 1000, 1);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 100, 25, 1);
+ ctx.stroke();
+
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 50,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.coincide.2
+ desc: arcTo() draws a straight line to P1 if P1 = P2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 25, 1);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.collinear.1
+ desc: arcTo() with all points on a line, and P1 between P0/P2, draws a straight
+ line to P1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 200, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, 100, 25, 1);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.collinear.2
+ desc: arcTo() with all points on a line, and P2 between P0/P1, draws a straight
+ line to P1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 10, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 110, 25, 1);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.collinear.3
+ desc: arcTo() with all points on a line, and P0 between P1/P2, draws a straight
+ line to P1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 1);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 0, 25, 1);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(-100, 25);
+ ctx.arcTo(0, 25, -200, 25, 1);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.shape.curve1
+ desc: arcTo() curves in the right kind of shape
+ code: |
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25+tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15-tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 55,19 == 0,255,0,255;
+ @assert pixel 55,20 == 0,255,0,255;
+ @assert pixel 55,21 == 0,255,0,255;
+ @assert pixel 64,22 == 0,255,0,255;
+ @assert pixel 65,21 == 0,255,0,255;
+ @assert pixel 72,28 == 0,255,0,255;
+ @assert pixel 73,27 == 0,255,0,255;
+ @assert pixel 78,36 == 0,255,0,255;
+ @assert pixel 79,35 == 0,255,0,255;
+ @assert pixel 80,44 == 0,255,0,255;
+ @assert pixel 80,45 == 0,255,0,255;
+ @assert pixel 80,46 == 0,255,0,255;
+ @assert pixel 65,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.shape.curve2
+ desc: arcTo() curves in the right kind of shape
+ code: |
+ var tol = 1.5; // tolerance to avoid antialiasing artifacts
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.rect(10, 20, 45, 10);
+ ctx.moveTo(80, 45);
+ ctx.arc(55, 45, 25-tol, 0, -Math.PI/2, true);
+ ctx.arc(55, 45, 15+tol, -Math.PI/2, 0, false);
+ ctx.fill();
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(10, 25);
+ ctx.arcTo(75, 25, 75, 60, 20);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 55,19 == 0,255,0,255;
+ @assert pixel 55,20 == 0,255,0,255;
+ @assert pixel 55,21 == 0,255,0,255;
+ @assert pixel 64,22 == 0,255,0,255;
+ @assert pixel 65,21 == 0,255,0,255;
+ @assert pixel 72,28 == 0,255,0,255;
+ @assert pixel 73,27 == 0,255,0,255;
+ @assert pixel 78,36 == 0,255,0,255;
+ @assert pixel 79,35 == 0,255,0,255;
+ @assert pixel 80,44 == 0,255,0,255;
+ @assert pixel 80,45 == 0,255,0,255;
+ @assert pixel 80,46 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.shape.start
+ desc: arcTo() draws a straight line from P0 to P1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(200, 25, 200, 50, 10);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.shape.end
+ desc: arcTo() does not draw anything from P1 to P2
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.beginPath();
+ ctx.moveTo(-100, -100);
+ ctx.arcTo(-100, 25, 200, 25, 10);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.negative
+ desc: arcTo() with negative radius throws an exception
+ code: |
+ @assert throws INDEX_SIZE_ERR ctx.arcTo(0, 0, 0, 0, -1);
+ var path = new Path2D();
+ @assert throws INDEX_SIZE_ERR path.arcTo(10, 10, 20, 20, -5);
+
+- name: 2d.path.arcTo.zero.1
+ desc: arcTo() with zero radius draws a straight line from P0 to P1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, 100, 100, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(0, -25);
+ ctx.arcTo(50, -25, 50, 50, 0);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.zero.2
+ desc: arcTo() with zero radius draws a straight line from P0 to P1, even when all
+ points are collinear
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arcTo(100, 25, -100, 25, 0);
+ ctx.stroke();
+
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 25);
+ ctx.arcTo(200, 25, 50, 25, 0);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.transformation
+ desc: arcTo joins up to the last subpath point correctly
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-100, 0);
+ ctx.fill();
+
+ @assert pixel 0,0 == 0,255,0,255;
+ @assert pixel 50,0 == 0,255,0,255;
+ @assert pixel 99,0 == 0,255,0,255;
+ @assert pixel 0,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 99,25 == 0,255,0,255;
+ @assert pixel 0,49 == 0,255,0,255;
+ @assert pixel 50,49 == 0,255,0,255;
+ @assert pixel 99,49 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.scale
+ desc: arcTo scales the curve, not just the control points
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 50);
+ ctx.translate(100, 0);
+ ctx.scale(0.1, 1);
+ ctx.arcTo(50, 50, 50, 0, 50);
+ ctx.lineTo(-1000, 0);
+ ctx.fill();
+
+ @assert pixel 0,0 == 0,255,0,255;
+ @assert pixel 50,0 == 0,255,0,255;
+ @assert pixel 99,0 == 0,255,0,255;
+ @assert pixel 0,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 99,25 == 0,255,0,255;
+ @assert pixel 0,49 == 0,255,0,255;
+ @assert pixel 50,49 == 0,255,0,255;
+ @assert pixel 99,49 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arcTo.nonfinite
+ desc: arcTo() with Infinity/NaN is ignored
+ code: |
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.arcTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,45 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.path.arc.empty
+ desc: arc() with an empty path does not draw a straight line to the start point
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.nonempty
+ desc: arc() with a non-empty path does draw a straight line to the start point
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 5, 0, 2*Math.PI, true);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.end
+ desc: arc() adds the end point of the arc to the subpath
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(-100, 0);
+ ctx.arc(-100, 0, 25, -Math.PI/2, Math.PI/2, true);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.default
+ desc: arc() with missing last argument defaults to clockwise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -Math.PI, Math.PI/2);
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.angle.1
+ desc: arc() draws pi/2 .. -pi anticlockwise correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.angle.2
+ desc: arc() draws -3pi/2 .. -pi anticlockwise correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, -3*Math.PI/2, -Math.PI, true);
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.angle.3
+ desc: arc() wraps angles mod 2pi when anticlockwise and end > start+2pi
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (512+1/2)*Math.PI, (1024-1)*Math.PI, true);
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.angle.4
+ desc: arc() draws a full circle when clockwise and end > start+2pi
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (512+1/2)*Math.PI, (1024-1)*Math.PI, false);
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.angle.5
+ desc: arc() wraps angles mod 2pi when clockwise and start > end+2pi
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(100, 0);
+ ctx.arc(100, 0, 150, (1024-1)*Math.PI, (512+1/2)*Math.PI, false);
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.angle.6
+ desc: arc() draws a full circle when anticlockwise and start > end+2pi
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 60, (1024-1)*Math.PI, (512+1/2)*Math.PI, true);
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.zero.1
+ desc: arc() draws nothing when startAngle = endAngle and anticlockwise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, true);
+ ctx.stroke();
+ @assert pixel 50,20 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.zero.2
+ desc: arc() draws nothing when startAngle = endAngle and clockwise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 0, false);
+ ctx.stroke();
+ @assert pixel 50,20 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.twopie.1
+ desc: arc() draws nothing when end = start + 2pi-e and anticlockwise
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, true);
+ ctx.stroke();
+ @assert pixel 50,20 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.twopie.2
+ desc: arc() draws a full circle when end = start + 2pi-e and clockwise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, false);
+ ctx.stroke();
+ @assert pixel 50,20 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.twopie.3
+ desc: arc() draws a full circle when end = start + 2pi+e and anticlockwise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, true);
+ ctx.stroke();
+ @assert pixel 50,20 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.twopie.4
+ desc: arc() draws nothing when end = start + 2pi+e and clockwise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, false);
+ ctx.stroke();
+ @assert pixel 50,20 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.twopie.5
+ desc: arc() draws correctly when start = 2 and end = start + 2pi+e and clockwise
+ code: |
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 2, 2 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ @assert pixel 95,25 == 0,0,0,255;
+
+- name: 2d.path.arc.twopie.6
+ desc: arc() draws correctly when start = 5 and end = start + 2pi+e and clockwise
+ code: |
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arc(50, 25, 50, 5, 5 + 2*Math.PI, false);
+ ctx.closePath();
+ ctx.fill();
+ @assert pixel 5,25 == 0,0,0,255;
+
+- name: 2d.path.arc.shape.1
+ desc: arc() from 0 to pi does not draw anything in the wrong half
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, false);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 20,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.shape.2
+ desc: arc() from 0 to pi draws stuff in the right half
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(50, 50, 50, 0, Math.PI, true);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 20,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.shape.3
+ desc: arc() from 0 to -pi/2 does not draw anything in the wrong quadrant
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 100;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(0, 50, 50, 0, -Math.PI/2, false);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255; @moz-todo
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.shape.4
+ desc: arc() from 0 to -pi/2 draws stuff in the right quadrant
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 150;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 100, 0, -Math.PI/2, true);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.shape.5
+ desc: arc() from 0 to 5pi does not draw crazy things
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(300, 0, 100, 0, 5*Math.PI, false);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.selfintersect.1
+ desc: arc() with lineWidth > 2*radius is drawn sensibly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 200;
+ ctx.strokeStyle = '#f00';
+ ctx.beginPath();
+ ctx.arc(100, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(0, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ @assert pixel 1,1 == 0,255,0,255; @moz-todo
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.selfintersect.2
+ desc: arc() with lineWidth > 2*radius is drawn sensibly
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 180;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(-50, 50, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(100, 0, 25, 0, -Math.PI/2, true);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,10 == 0,255,0,255;
+ @assert pixel 97,1 == 0,255,0,255;
+ @assert pixel 97,2 == 0,255,0,255;
+ @assert pixel 97,3 == 0,255,0,255;
+ @assert pixel 2,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.negative
+ desc: arc() with negative radius throws INDEX_SIZE_ERR
+ code: |
+ @assert throws INDEX_SIZE_ERR ctx.arc(0, 0, -1, 0, 0, true);
+ var path = new Path2D();
+ @assert throws INDEX_SIZE_ERR path.arc(10, 10, -5, 0, 1, false);
+
+- name: 2d.path.arc.zeroradius
+ desc: arc() with zero radius draws a line to the start point
+ code: |
+ ctx.fillStyle = '#f00'
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.lineWidth = 50;
+ ctx.strokeStyle = '#0f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 25);
+ ctx.arc(200, 25, 0, 0, Math.PI, true);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.scale.1
+ desc: Non-uniformly scaled arcs are the right shape
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(2, 0.5);
+ ctx.fillStyle = '#0f0';
+ ctx.beginPath();
+ ctx.arc(25, 50, 56, 0, 2*Math.PI, false);
+ ctx.fill();
+ ctx.fillStyle = '#f00';
+ ctx.beginPath();
+ ctx.moveTo(-25, 50);
+ ctx.arc(-25, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(75, 50);
+ ctx.arc(75, 50, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, -25);
+ ctx.arc(25, -25, 24, 0, 2*Math.PI, false);
+ ctx.moveTo(25, 125);
+ ctx.arc(25, 125, 24, 0, 2*Math.PI, false);
+ ctx.fill();
+
+ @assert pixel 0,0 == 0,255,0,255;
+ @assert pixel 50,0 == 0,255,0,255;
+ @assert pixel 99,0 == 0,255,0,255;
+ @assert pixel 0,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 99,25 == 0,255,0,255;
+ @assert pixel 0,49 == 0,255,0,255;
+ @assert pixel 50,49 == 0,255,0,255;
+ @assert pixel 99,49 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.scale.2
+ desc: Highly scaled arcs are the right shape
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.scale(100, 100);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 1.2;
+ ctx.beginPath();
+ ctx.arc(0, 0, 0.6, 0, Math.PI/2, false);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 50,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.arc.nonfinite
+ desc: arc() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.arc(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <2*Math.PI Infinity -Infinity NaN>, <true>);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,45 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.path.rect.basic
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.newsubpath
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.rect(200, 25, 1, 1);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.closed
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.rect(100, 50, 100, 100);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.end.1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.rect(200, 100, 400, 1000);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.end.2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.rect(150, 150, 2000, 2000);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.zero.1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(0, 50, 100, 0);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.zero.2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, -100, 0, 250);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.zero.3
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.zero.4
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.rect(100, 25, 0, 0);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.zero.5
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.rect(100, 25, 0, 0);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.rect.zero.6
+ #mozilla: { bug: TODO }
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.rect(100, 25, 1000, 0);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.rect.negative
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.rect(0, 0, 50, 25);
+ ctx.rect(100, 0, -50, 25);
+ ctx.rect(0, 50, 50, -25);
+ ctx.rect(100, 50, -50, -25);
+ ctx.fill();
+ @assert pixel 25,12 == 0,255,0,255;
+ @assert pixel 75,12 == 0,255,0,255;
+ @assert pixel 25,37 == 0,255,0,255;
+ @assert pixel 75,37 == 0,255,0,255;
+
+- name: 2d.path.rect.winding
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.rect(0, 0, 50, 50);
+ ctx.rect(100, 50, -50, -50);
+ ctx.rect(0, 25, 100, -25);
+ ctx.rect(100, 25, -100, 25);
+ ctx.fill();
+ @assert pixel 25,12 == 0,255,0,255;
+ @assert pixel 75,12 == 0,255,0,255;
+ @assert pixel 25,37 == 0,255,0,255;
+ @assert pixel 75,37 == 0,255,0,255;
+
+- name: 2d.path.rect.selfintersect
+ #mozilla: { bug: TODO }
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.rect(45, 20, 10, 10);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.rect.nonfinite
+ desc: rect() with Infinity/NaN is ignored
+ code: |
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.rect(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.newsubpath
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-50, 25);
+ ctx.roundRect(200, 25, 1, 1, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.closed
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.roundRect(100, 50, 100, 100, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.end.1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(200, 100, 400, 1000, [0]);
+ ctx.lineTo(-2000, -1000);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.end.2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 450;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'bevel';
+ ctx.roundRect(150, 150, 2000, 2000, [0]);
+ ctx.lineTo(160, 160);
+ ctx.stroke();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.end.3
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.roundRect(101, 51, 2000, 2000, [500, 500, 500, 500]);
+ ctx.lineTo(-1, -1);
+ ctx.stroke();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.end.4
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 10;
+ ctx.roundRect(-1, -1, 2000, 2000, [1000, 1000, 1000, 1000]);
+ ctx.lineTo(-150, -150);
+ ctx.stroke();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.zero.1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(0, 50, 100, 0, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.zero.2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, -100, 0, 250, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.zero.3
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.beginPath();
+ ctx.roundRect(50, 25, 0, 0, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.zero.4
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 50;
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.lineTo(0, 25);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.zero.5
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 0);
+ ctx.roundRect(100, 25, 0, 0, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.zero.6
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.5;
+ ctx.lineWidth = 200;
+ ctx.beginPath();
+ ctx.roundRect(100, 25, 1000, 0, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.negative
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#0f0';
+ ctx.roundRect(0, 0, 50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 0, -50, 25, [10, 0, 0, 0]);
+ ctx.roundRect(0, 50, 50, -25, [10, 0, 0, 0]);
+ ctx.roundRect(100, 50, -50, -25, [10, 0, 0, 0]);
+ ctx.fill();
+ // All rects drawn
+ @assert pixel 25,12 == 0,255,0,255;
+ @assert pixel 75,12 == 0,255,0,255;
+ @assert pixel 25,37 == 0,255,0,255;
+ @assert pixel 75,37 == 0,255,0,255;
+ // Correct corners are rounded.
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.winding
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.beginPath();
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 50, 50, [0]);
+ ctx.roundRect(100, 50, -50, -50, [0]);
+ ctx.roundRect(0, 25, 100, -25, [0]);
+ ctx.roundRect(100, 25, -100, 25, [0]);
+ ctx.fill();
+ @assert pixel 25,12 == 0,255,0,255;
+ @assert pixel 75,12 == 0,255,0,255;
+ @assert pixel 25,37 == 0,255,0,255;
+ @assert pixel 75,37 == 0,255,0,255;
+
+- name: 2d.path.roundrect.selfintersect
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.roundRect(0, 0, 100, 50, [0]);
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 90;
+ ctx.beginPath();
+ ctx.roundRect(45, 20, 10, 10, [0]);
+ ctx.stroke();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.nonfinite
+ desc: roundRect() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ @nonfinite ctx.roundRect(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <[0] [Infinity] [-Infinity] [NaN] [Infinity,0] [-Infinity,0] [NaN,0] [0,Infinity] [0,-Infinity] [0,NaN] [Infinity,0,0] [-Infinity,0,0] [NaN,0,0] [0,Infinity,0] [0,-Infinity,0] [0,NaN,0] [0,0,Infinity] [0,0,-Infinity] [0,0,NaN] [Infinity,0,0,0] [-Infinity,0,0,0] [NaN,0,0,0] [0,Infinity,0,0] [0,-Infinity,0,0] [0,NaN,0,0] [0,0,Infinity,0] [0,0,-Infinity,0] [0,0,NaN,0] [0,0,0,Infinity] [0,0,0,-Infinity] [0,0,0,NaN]>);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, -Infinity)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(10, NaN)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(-Infinity, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [new DOMPoint(NaN, 10)]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: -Infinity}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: 10, y: NaN}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: -Infinity, y: 10}]);
+ ctx.roundRect(0, 0, 100, 100, [{x: NaN, y: 10}]);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 90,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.roundrect.4.radii.1.double
+ desc: Verify that when four radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.1.dompoint
+ desc: Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.1.dompointinit
+ desc: Verify that when four radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.2.double
+ desc: Verify that when four radii are given to roundRect(), the second radius, specified as a double, applies to the top-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.2.dompoint
+ desc: Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.2.dompointinit
+ desc: Verify that when four radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.3.double
+ desc: Verify that when four radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.3.dompoint
+ desc: Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.3.dompointinit
+ desc: Verify that when four radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.4.double
+ desc: Verify that when four radii are given to roundRect(), the fourth radius, specified as a double, applies to the bottom-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.4.radii.4.dompoint
+ desc: Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPoint, applies to the bottom-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.4.radii.4.dompointinit
+ desc: Verify that when four radii are given to roundRect(), the fourth radius, specified as a DOMPointInit, applies to the bottom-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.1.double
+ desc: Verify that when three radii are given to roundRect(), the first radius, specified as a double, applies to the top-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.1.dompoint
+ desc: Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.1.dompointinit
+ desc: Verify that when three radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.2.double
+ desc: Verify that when three radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.3.radii.2.dompoint
+ desc: Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.2.dompointinit
+ desc: Verify that when three radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.3.double
+ desc: Verify that when three radii are given to roundRect(), the third radius, specified as a double, applies to the bottom-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.3.dompoint
+ desc: Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPoint, applies to the bottom-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.3.radii.3.dompointinit
+ desc: Verify that when three radii are given to roundRect(), the third radius, specified as a DOMPointInit, applies to the bottom-right corner.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.2.radii.1.double
+ desc: Verify that when two radii are given to roundRect(), the first radius, specified as a double, applies to the top-left and bottom-right corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.2.radii.1.dompoint
+ desc: Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPoint, applies to the top-left and bottom-right corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20), 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.2.radii.1.dompointinit
+ desc: Verify that when two radii are given to roundRect(), the first radius, specified as a DOMPointInit, applies to the top-left and bottom-right corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}, 0]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 98,1 == 0,255,0,255;
+ @assert pixel 1,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.2.radii.2.double
+ desc: Verify that when two radii are given to roundRect(), the second radius, specified as a double, applies to the top-right and bottom-left corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, 20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.2.radii.2.dompoint
+ desc: Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPoint, applies to the top-right and bottom-left corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.2.radii.2.dompointinit
+ desc: Verify that when two radii are given to roundRect(), the second radius, specified as a DOMPointInit, applies to the top-right and bottom-left corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [0, {x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+ // other corners
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+
+- name: 2d.path.roundrect.1.radius.double
+ desc: Verify that when one radius is given to roundRect(), specified as a double, it applies to all corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [20]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.1.radius.double.single.argument
+ desc: Verify that when one radius is given to roundRect() as a non-array argument, specified as a double, it applies to all corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, 20);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.1.radius.dompoint
+ desc: Verify that when one radius is given to roundRect(), specified as a DOMPoint, it applies to all corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [new DOMPoint(40, 20)]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+- name: 2d.path.roundrect.1.radius.dompoint.single argument
+ desc: Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPoint, it applies to all corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, new DOMPoint(40, 20));
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+- name: 2d.path.roundrect.1.radius.dompointinit
+ desc: Verify that when one radius is given to roundRect(), specified as a DOMPointInit, applies to all corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [{x: 40, y: 20}]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+- name: 2d.path.roundrect.1.radius.dompointinit.single.argument
+ desc: Verify that when one radius is given to roundRect() as a non-array argument, specified as a DOMPointInit, applies to all corners.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, {x: 40, y: 20});
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ // top-left corner
+ @assert pixel 20,1 == 255,0,0,255;
+ @assert pixel 41,1 == 0,255,0,255;
+ @assert pixel 1,10 == 255,0,0,255;
+ @assert pixel 1,21 == 0,255,0,255;
+
+ // top-right corner
+ @assert pixel 79,1 == 255,0,0,255;
+ @assert pixel 58,1 == 0,255,0,255;
+ @assert pixel 98,10 == 255,0,0,255;
+ @assert pixel 98,21 == 0,255,0,255;
+
+ // bottom-right corner
+ @assert pixel 79,48 == 255,0,0,255;
+ @assert pixel 58,48 == 0,255,0,255;
+ @assert pixel 98,39 == 255,0,0,255;
+ @assert pixel 98,28 == 0,255,0,255;
+
+ // bottom-left corner
+ @assert pixel 20,48 == 255,0,0,255;
+ @assert pixel 41,48 == 0,255,0,255;
+ @assert pixel 1,39 == 255,0,0,255;
+ @assert pixel 1,28 == 0,255,0,255;
+
+- name: 2d.path.roundrect.radius.intersecting.1
+ desc: Check that roundRects with intersecting corner arcs are rendered correctly.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [40, 40, 40, 40]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 2,25 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 50,48 == 0,255,0,255;
+ @assert pixel 97,25 == 0,255,0,255;
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.radius.intersecting.2
+ desc: Check that roundRects with intersecting corner arcs are rendered correctly.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(0, 0, 100, 50, [1000, 1000, 1000, 1000]);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 2,25 == 0,255,0,255;
+ @assert pixel 50,1 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 50,48 == 0,255,0,255;
+ @assert pixel 97,25 == 0,255,0,255;
+ @assert pixel 1,1 == 255,0,0,255;
+ @assert pixel 98,1 == 255,0,0,255;
+ @assert pixel 1,48 == 255,0,0,255;
+ @assert pixel 98,48 == 255,0,0,255;
+
+- name: 2d.path.roundrect.radius.none
+ desc: Check that roundRect throws an RangeError if radii is an empty array.
+ code: |
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [])});
+
+- name: 2d.path.roundrect.radius.noargument
+ desc: Check that roundRect draws a rectangle when no radii are provided.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.roundRect(10, 10, 80, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ // upper left corner (10, 10)
+ @assert pixel 10,9 == 255,0,0,255;
+ @assert pixel 9,10 == 255,0,0,255;
+ @assert pixel 10,10 == 0,255,0,255;
+
+ // upper right corner (89, 10)
+ @assert pixel 90,10 == 255,0,0,255;
+ @assert pixel 89,9 == 255,0,0,255;
+ @assert pixel 89,10 == 0,255,0,255;
+
+ // lower right corner (89, 39)
+ @assert pixel 89,40 == 255,0,0,255;
+ @assert pixel 90,39 == 255,0,0,255;
+ @assert pixel 89,39 == 0,255,0,255;
+
+ // lower left corner (10, 30)
+ @assert pixel 9,39 == 255,0,0,255;
+ @assert pixel 10,40 == 255,0,0,255;
+ @assert pixel 10,39 == 0,255,0,255;
+
+- name: 2d.path.roundrect.radius.toomany
+ desc: Check that roundRect throws an IndeSizeError if radii has more than four items.
+ code: |
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 100, 50, [0, 0, 0, 0, 0])});
+
+- name: 2d.path.roundrect.radius.negative
+ desc: roundRect() with negative radius throws an exception
+ code: |
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [-1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [1, -1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(-1, 1), 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [new DOMPoint(1, -1)])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: -1, y: 1}, 1])});
+ assert_throws_js(RangeError, () => { ctx.roundRect(0, 0, 0, 0, [{x: 1, y: -1}])});
+
+- name: 2d.path.roundrect.badinput
+ desc: roundRect() throws or does not throw errors given the strange inputs.
+ code: |
+ ctx.roundRect(0, 0, 100, 100, { foo: "bar" }); //=> DOMPointInit
+ ctx.roundRect(0, 0, 100, 100, undefined); //=> "missing" -> 0
+ ctx.roundRect(0, 0, 100, 100, [[]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [[25]]); //=> « DOMPointInit »
+ ctx.roundRect(0, 0, 100, 100, [undefined]); //=> « DOMPointInit »
+ @assert throws TypeError ctx.roundRect(0, 0, 100, 100, 0n);
+ @assert throws TypeError ctx.roundRect(0, 0, 100, 100, { x: 0n });
+ @assert throws TypeError ctx.roundRect(0, 0, 100, 100, [{ x: 0n }]);
+
+- name: 2d.path.ellipse.basics
+ desc: Verify canvas throws error when drawing ellipse with negative radii.
+ code: |
+ ctx.ellipse(10, 10, 10, 5, 0, 0, 1, false);
+ ctx.ellipse(10, 10, 10, 0, 0, 0, 1, false);
+ ctx.ellipse(10, 10, -0, 5, 0, 0, 1, false);
+ @assert throws INDEX_SIZE_ERR ctx.ellipse(10, 10, -2, 5, 0, 0, 1, false);
+ @assert throws INDEX_SIZE_ERR ctx.ellipse(10, 10, 0, -1.5, 0, 0, 1, false);
+ @assert throws INDEX_SIZE_ERR ctx.ellipse(10, 10, -2, -5, 0, 0, 1, false);
+ ctx.ellipse(80, 0, 10, 4294967277, Math.PI / -84, -Math.PI / 2147483436, false);
+
+- name: 2d.path.fill.overlap
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.rect(0, 0, 100, 50);
+ ctx.closePath();
+ ctx.rect(10, 10, 80, 30);
+ ctx.fill();
+
+ @assert pixel 50,25 ==~ 0,127,0,255 +/- 1;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0, 0.5, 0)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.path.fill.winding.add
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.fill.winding.subtract.1
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.fill.winding.subtract.2
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.fill.winding.subtract.3
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(120, -20);
+ ctx.lineTo(120, 70);
+ ctx.lineTo(-20, 70);
+ ctx.lineTo(-20, -20);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.fill.closed.basic
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.fill.closed.unaffected
+ code: |
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ @assert pixel 90,10 == 0,255,0,255;
+ @assert pixel 10,40 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.stroke.overlap
+ desc: Stroked subpaths are combined before being drawn
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, 20);
+ ctx.lineTo(100, 20);
+ ctx.moveTo(0, 30);
+ ctx.lineTo(100, 30);
+ ctx.stroke();
+
+ @assert pixel 50,25 ==~ 0,127,0,255 +/- 1;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0, 0.5, 0)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.path.stroke.union
+ desc: Strokes in opposite directions are unioned, not subtracted
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 40;
+ ctx.moveTo(0, 10);
+ ctx.lineTo(100, 10);
+ ctx.moveTo(100, 40);
+ ctx.lineTo(0, 40);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.stroke.unaffected
+ desc: Stroking does not start a new path or subpath
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.lineWidth = 50;
+ ctx.moveTo(-100, 25);
+ ctx.lineTo(-100, -100);
+ ctx.lineTo(200, -100);
+ ctx.lineTo(200, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+
+ ctx.closePath();
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.stroke.scale1
+ desc: Stroke line widths are scaled by the current transformation matrix
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.scale(50, 25);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ @assert pixel 0,0 == 0,255,0,255;
+ @assert pixel 50,0 == 0,255,0,255;
+ @assert pixel 99,0 == 0,255,0,255;
+ @assert pixel 0,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 99,25 == 0,255,0,255;
+ @assert pixel 0,49 == 0,255,0,255;
+ @assert pixel 50,49 == 0,255,0,255;
+ @assert pixel 99,49 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.stroke.scale2
+ desc: Stroke line widths are scaled by the current transformation matrix
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(25, 12.5, 50, 25);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.beginPath();
+ ctx.rect(-25, -12.5, 150, 75);
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ ctx.scale(25, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ @assert pixel 0,0 == 0,255,0,255;
+ @assert pixel 50,0 == 0,255,0,255;
+ @assert pixel 99,0 == 0,255,0,255;
+ @assert pixel 0,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 99,25 == 0,255,0,255;
+ @assert pixel 0,49 == 0,255,0,255;
+ @assert pixel 50,49 == 0,255,0,255;
+ @assert pixel 99,49 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.stroke.skew
+ desc: Strokes lines are skewed by the current transformation matrix
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(49, -50);
+ ctx.lineTo(201, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 283);
+ ctx.strokeStyle = '#0f0';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(-150, 0);
+ ctx.moveTo(49, -50);
+ ctx.lineTo(199, -50);
+ ctx.rotate(Math.PI/4);
+ ctx.scale(1, 142);
+ ctx.strokeStyle = '#f00';
+ ctx.stroke();
+ ctx.restore();
+
+ @assert pixel 0,0 == 0,255,0,255;
+ @assert pixel 50,0 == 0,255,0,255;
+ @assert pixel 99,0 == 0,255,0,255;
+ @assert pixel 0,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 99,25 == 0,255,0,255;
+ @assert pixel 0,49 == 0,255,0,255;
+ @assert pixel 50,49 == 0,255,0,255;
+ @assert pixel 99,49 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.stroke.empty
+ desc: Empty subpaths are not stroked
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(40, 25);
+ ctx.moveTo(60, 25);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.stroke.prune.line
+ desc: Zero-length line segments from lineTo are removed before stroking
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.stroke.prune.closed
+ desc: Zero-length line segments from closed paths are removed before stroking
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.lineTo(50, 25);
+ ctx.closePath();
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.stroke.prune.curve
+ desc: Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed
+ before stroking
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.quadraticCurveTo(50, 25, 50, 25);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.stroke.prune.arc
+ desc: Zero-length line segments from arcTo and arc are removed before stroking
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(50, 25);
+ ctx.arcTo(50, 25, 150, 25, 10);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(60, 25);
+ ctx.arc(50, 25, 10, 0, 0, false);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.stroke.prune.rect
+ desc: Zero-length line segments from rect and strokeRect are removed before stroking
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 100;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.rect(50, 25, 0, 0);
+ ctx.stroke();
+
+ ctx.strokeRect(50, 25, 0, 0);
+
+ @assert pixel 50,25 == 0,255,0,255; @moz-todo
+ expected: green
+
+- name: 2d.path.stroke.prune.corner
+ desc: Zero-length line segments are removed before stroking with miters
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 400;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 1.4;
+
+ ctx.beginPath();
+ ctx.moveTo(-1000, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 200);
+ ctx.lineTo(-100, 1000);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.transformation.basic
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(-100, 0);
+ ctx.rect(100, 0, 100, 50);
+ ctx.translate(0, -100);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.transformation.multiple
+ # TODO: change this name
+ desc: Transformations are applied while building paths, not when drawing
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.translate(-100, 0);
+ ctx.rect(0, 0, 100, 50);
+ ctx.fill();
+ ctx.translate(100, 0);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 50;
+ ctx.translate(0, -50);
+ ctx.moveTo(0, 25);
+ ctx.lineTo(100, 25);
+ ctx.stroke();
+ ctx.translate(0, 50);
+ ctx.stroke();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.transformation.changing
+ desc: Transformations are applied while building paths, not when drawing
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.moveTo(0, 0);
+ ctx.translate(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.translate(-100, 0);
+ ctx.lineTo(0, 0);
+ ctx.translate(1000, 1000);
+ ctx.rotate(Math.PI/2);
+ ctx.scale(0.1, 0.1);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.path.clip.empty
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.clip.basic.1
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.clip.basic.2
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(-100, 0, 100, 50);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.clip.intersect
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50)
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.clip.winding.1
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.clip.winding.2
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.beginPath();
+ ctx.moveTo(-10, -10);
+ ctx.lineTo(110, -10);
+ ctx.lineTo(110, 60);
+ ctx.lineTo(-10, 60);
+ ctx.lineTo(-10, -10);
+ ctx.clip();
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.lineTo(0, 0);
+ ctx.clip();
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.path.clip.unaffected
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(100, 50);
+ ctx.lineTo(100, 0);
+ ctx.clip();
+
+ ctx.lineTo(0, 0);
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+
+
+- name: 2d.path.isPointInPath.basic.1
+ desc: isPointInPath() detects whether the point is inside the path
+ code: |
+ ctx.rect(0, 0, 20, 20);
+ @assert ctx.isPointInPath(10, 10) === true;
+ @assert ctx.isPointInPath(30, 10) === false;
+
+- name: 2d.path.isPointInPath.basic.2
+ desc: isPointInPath() detects whether the point is inside the path
+ code: |
+ ctx.rect(20, 0, 20, 20);
+ @assert ctx.isPointInPath(10, 10) === false;
+ @assert ctx.isPointInPath(30, 10) === true;
+
+- name: 2d.path.isPointInPath.edge
+ desc: isPointInPath() counts points on the path as being inside
+ code: |
+ ctx.rect(0, 0, 20, 20);
+ @assert ctx.isPointInPath(0, 0) === true;
+ @assert ctx.isPointInPath(10, 0) === true;
+ @assert ctx.isPointInPath(20, 0) === true;
+ @assert ctx.isPointInPath(20, 10) === true;
+ @assert ctx.isPointInPath(20, 20) === true;
+ @assert ctx.isPointInPath(10, 20) === true;
+ @assert ctx.isPointInPath(0, 20) === true;
+ @assert ctx.isPointInPath(0, 10) === true;
+ @assert ctx.isPointInPath(10, -0.01) === false;
+ @assert ctx.isPointInPath(10, 20.01) === false;
+ @assert ctx.isPointInPath(-0.01, 10) === false;
+ @assert ctx.isPointInPath(20.01, 10) === false;
+
+- name: 2d.path.isPointInPath.empty
+ desc: isPointInPath() works when there is no path
+ code: |
+ @assert ctx.isPointInPath(0, 0) === false;
+
+- name: 2d.path.isPointInPath.subpath
+ desc: isPointInPath() uses the current path, not just the subpath
+ code: |
+ ctx.rect(0, 0, 20, 20);
+ ctx.beginPath();
+ ctx.rect(20, 0, 20, 20);
+ ctx.closePath();
+ ctx.rect(40, 0, 20, 20);
+ @assert ctx.isPointInPath(10, 10) === false;
+ @assert ctx.isPointInPath(30, 10) === true;
+ @assert ctx.isPointInPath(50, 10) === true;
+
+- name: 2d.path.isPointInPath.outside
+ desc: isPointInPath() works on paths outside the canvas
+ code: |
+ ctx.rect(0, -100, 20, 20);
+ ctx.rect(20, -10, 20, 20);
+ @assert ctx.isPointInPath(10, -110) === false;
+ @assert ctx.isPointInPath(10, -90) === true;
+ @assert ctx.isPointInPath(10, -70) === false;
+ @assert ctx.isPointInPath(30, -20) === false;
+ @assert ctx.isPointInPath(30, 0) === true;
+ @assert ctx.isPointInPath(30, 20) === false;
+
+- name: 2d.path.isPointInPath.unclosed
+ desc: isPointInPath() works on unclosed subpaths
+ code: |
+ ctx.moveTo(0, 0);
+ ctx.lineTo(20, 0);
+ ctx.lineTo(20, 20);
+ ctx.lineTo(0, 20);
+ @assert ctx.isPointInPath(10, 10) === true;
+ @assert ctx.isPointInPath(30, 10) === false;
+
+- name: 2d.path.isPointInPath.arc
+ desc: isPointInPath() works on arcs
+ code: |
+ ctx.arc(50, 25, 10, 0, Math.PI, false);
+ @assert ctx.isPointInPath(50, 10) === false;
+ @assert ctx.isPointInPath(50, 20) === false;
+ @assert ctx.isPointInPath(50, 30) === true;
+ @assert ctx.isPointInPath(50, 40) === false;
+ @assert ctx.isPointInPath(30, 20) === false;
+ @assert ctx.isPointInPath(70, 20) === false;
+ @assert ctx.isPointInPath(30, 30) === false;
+ @assert ctx.isPointInPath(70, 30) === false;
+
+- name: 2d.path.isPointInPath.bigarc
+ desc: isPointInPath() works on unclosed arcs larger than 2pi
+ opera: {bug: 320937}
+ code: |
+ ctx.arc(50, 25, 10, 0, 7, false);
+ @assert ctx.isPointInPath(50, 10) === false;
+ @assert ctx.isPointInPath(50, 20) === true;
+ @assert ctx.isPointInPath(50, 30) === true;
+ @assert ctx.isPointInPath(50, 40) === false;
+ @assert ctx.isPointInPath(30, 20) === false;
+ @assert ctx.isPointInPath(70, 20) === false;
+ @assert ctx.isPointInPath(30, 30) === false;
+ @assert ctx.isPointInPath(70, 30) === false;
+
+- name: 2d.path.isPointInPath.bezier
+ desc: isPointInPath() works on Bezier curves
+ code: |
+ ctx.moveTo(25, 25);
+ ctx.bezierCurveTo(50, -50, 50, 100, 75, 25);
+ @assert ctx.isPointInPath(25, 20) === false;
+ @assert ctx.isPointInPath(25, 30) === false;
+ @assert ctx.isPointInPath(30, 20) === true;
+ @assert ctx.isPointInPath(30, 30) === false;
+ @assert ctx.isPointInPath(40, 2) === false;
+ @assert ctx.isPointInPath(40, 20) === true;
+ @assert ctx.isPointInPath(40, 30) === false;
+ @assert ctx.isPointInPath(40, 47) === false;
+ @assert ctx.isPointInPath(45, 20) === true;
+ @assert ctx.isPointInPath(45, 30) === false;
+ @assert ctx.isPointInPath(55, 20) === false;
+ @assert ctx.isPointInPath(55, 30) === true;
+ @assert ctx.isPointInPath(60, 2) === false;
+ @assert ctx.isPointInPath(60, 20) === false;
+ @assert ctx.isPointInPath(60, 30) === true;
+ @assert ctx.isPointInPath(60, 47) === false;
+ @assert ctx.isPointInPath(70, 20) === false;
+ @assert ctx.isPointInPath(70, 30) === true;
+ @assert ctx.isPointInPath(75, 20) === false;
+ @assert ctx.isPointInPath(75, 30) === false;
+
+- name: 2d.path.isPointInPath.winding
+ desc: isPointInPath() uses the non-zero winding number rule
+ code: |
+ // Create a square ring, using opposite windings to make a hole in the centre
+ ctx.moveTo(0, 0);
+ ctx.lineTo(50, 0);
+ ctx.lineTo(50, 50);
+ ctx.lineTo(0, 50);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.lineTo(10, 40);
+ ctx.lineTo(40, 40);
+ ctx.lineTo(40, 10);
+ ctx.lineTo(10, 10);
+
+ @assert ctx.isPointInPath(5, 5) === true;
+ @assert ctx.isPointInPath(25, 5) === true;
+ @assert ctx.isPointInPath(45, 5) === true;
+ @assert ctx.isPointInPath(5, 25) === true;
+ @assert ctx.isPointInPath(25, 25) === false;
+ @assert ctx.isPointInPath(45, 25) === true;
+ @assert ctx.isPointInPath(5, 45) === true;
+ @assert ctx.isPointInPath(25, 45) === true;
+ @assert ctx.isPointInPath(45, 45) === true;
+
+- name: 2d.path.isPointInPath.transform.1
+ desc: isPointInPath() handles transformations correctly
+ code: |
+ ctx.translate(50, 0);
+ ctx.rect(0, 0, 20, 20);
+ @assert ctx.isPointInPath(-40, 10) === false;
+ @assert ctx.isPointInPath(10, 10) === false;
+ @assert ctx.isPointInPath(49, 10) === false;
+ @assert ctx.isPointInPath(51, 10) === true;
+ @assert ctx.isPointInPath(69, 10) === true;
+ @assert ctx.isPointInPath(71, 10) === false;
+
+- name: 2d.path.isPointInPath.transform.2
+ desc: isPointInPath() handles transformations correctly
+ code: |
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(50, 0);
+ @assert ctx.isPointInPath(-40, 10) === false;
+ @assert ctx.isPointInPath(10, 10) === false;
+ @assert ctx.isPointInPath(49, 10) === false;
+ @assert ctx.isPointInPath(51, 10) === true;
+ @assert ctx.isPointInPath(69, 10) === true;
+ @assert ctx.isPointInPath(71, 10) === false;
+
+- name: 2d.path.isPointInPath.transform.3
+ desc: isPointInPath() handles transformations correctly
+ code: |
+ ctx.scale(-1, 1);
+ ctx.rect(-70, 0, 20, 20);
+ @assert ctx.isPointInPath(-40, 10) === false;
+ @assert ctx.isPointInPath(10, 10) === false;
+ @assert ctx.isPointInPath(49, 10) === false;
+ @assert ctx.isPointInPath(51, 10) === true;
+ @assert ctx.isPointInPath(69, 10) === true;
+ @assert ctx.isPointInPath(71, 10) === false;
+
+- name: 2d.path.isPointInPath.transform.4
+ desc: isPointInPath() handles transformations correctly
+ code: |
+ ctx.translate(50, 0);
+ ctx.rect(50, 0, 20, 20);
+ ctx.translate(0, 50);
+ @assert ctx.isPointInPath(60, 10) === false;
+ @assert ctx.isPointInPath(110, 10) === true;
+ @assert ctx.isPointInPath(110, 60) === false;
+
+- name: 2d.path.isPointInPath.nonfinite
+ desc: isPointInPath() returns false for non-finite arguments
+ code: |
+ ctx.rect(-100, -50, 200, 100);
+ @assert ctx.isPointInPath(Infinity, 0) === false;
+ @assert ctx.isPointInPath(-Infinity, 0) === false;
+ @assert ctx.isPointInPath(NaN, 0) === false;
+ @assert ctx.isPointInPath(0, Infinity) === false;
+ @assert ctx.isPointInPath(0, -Infinity) === false;
+ @assert ctx.isPointInPath(0, NaN) === false;
+ @assert ctx.isPointInPath(NaN, NaN) === false;
+
+
+- name: 2d.path.isPointInStroke.scaleddashes
+ desc: isPointInStroke() should return correct results on dashed paths at high scale
+ factors
+ code: |
+ var scale = 20;
+ ctx.setLineDash([10, 21.4159]); // dash from t=0 to t=10 along the circle
+ ctx.scale(scale, scale);
+ ctx.ellipse(6, 10, 5, 5, 0, 2*Math.PI, false);
+ ctx.stroke();
+
+ // hit-test the beginning of the dash (t=0)
+ @assert ctx.isPointInStroke(11*scale, 10*scale) === true;
+ // hit-test the middle of the dash (t=5)
+ @assert ctx.isPointInStroke(8.70*scale, 14.21*scale) === true;
+ // hit-test the end of the dash (t=9.8)
+ @assert ctx.isPointInStroke(4.10*scale, 14.63*scale) === true;
+ // hit-test past the end of the dash (t=10.2)
+ @assert ctx.isPointInStroke(3.74*scale, 14.46*scale) === false;
+
+- name: 2d.path.isPointInPath.basic
+ desc: Verify the winding rule in isPointInPath works for for rect path.
+ code: |
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ @assert ctx.isPointInPath(50, 50) === true;
+ @assert ctx.isPointInPath(NaN, 50) === false;
+ @assert ctx.isPointInPath(50, NaN) === false;
+
+ // Testing nonzero isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ @assert ctx.isPointInPath(50, 50, 'nonzero') === true;
+
+ // Testing evenodd isPointInPath
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.rect(25, 25, 50, 50);
+ @assert ctx.isPointInPath(50, 50, 'evenodd') === false;
+
+ // Testing extremely large scale
+ ctx.save();
+ ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ @assert ctx.isPointInPath(0, 0, 'nonzero') === true;
+ @assert ctx.isPointInPath(0, 0, 'evenodd') === true;
+ ctx.restore();
+
+ // Check with non-invertible ctm.
+ ctx.save();
+ ctx.scale(0, 0);
+ ctx.beginPath();
+ ctx.rect(-10, -10, 20, 20);
+ @assert ctx.isPointInPath(0, 0, 'nonzero') === false;
+ @assert ctx.isPointInPath(0, 0, 'evenodd') === false;
+ ctx.restore();
+
+- name: 2d.path.isPointInpath.multi.path
+ desc: Verify the winding rule in isPointInPath works for path object.
+ code: |
+ canvas.width = 200;
+ canvas.height = 200;
+
+ // Testing default isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ @assert ctx.isPointInPath(path, 50, 50) === true;
+ @assert ctx.isPointInPath(path, 50, 50, undefined) === true;
+ @assert ctx.isPointInPath(path, NaN, 50) === false;
+ @assert ctx.isPointInPath(path, 50, NaN) === false;
+
+ // Testing nonzero isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ @assert ctx.isPointInPath(path, 50, 50, 'nonzero') === true;
+
+ // Testing evenodd isPointInPath with Path object');
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ assert_false(ctx.isPointInPath(path, 50, 50, 'evenodd'));
+
+- name: 2d.path.isPointInpath.invalid
+ desc: Verify isPointInPath throws exceptions with invalid inputs.
+ code: |
+ canvas.width = 200;
+ canvas.height = 200;
+ path = new Path2D();
+ path.rect(0, 0, 100, 100);
+ path.rect(25, 25, 50, 50);
+ // Testing invalid enumeration isPointInPath (w/ and w/o Path object');
+ @assert throws TypeError ctx.isPointInPath(path, 50, 50, 'gazonk');
+ @assert throws TypeError ctx.isPointInPath(50, 50, 'gazonk');
+
+ // Testing invalid type isPointInPath with Path object');
+ @assert throws TypeError ctx.isPointInPath(null, 50, 50);
+ @assert throws TypeError ctx.isPointInPath(null, 50, 50, 'nonzero');
+ @assert throws TypeError ctx.isPointInPath(null, 50, 50, 'evenodd');
+ @assert throws TypeError ctx.isPointInPath(null, 50, 50, null);
+ @assert throws TypeError ctx.isPointInPath(path, 50, 50, null);
+ @assert throws TypeError ctx.isPointInPath(undefined, 50, 50);
+ @assert throws TypeError ctx.isPointInPath(undefined, 50, 50, 'nonzero');
+ @assert throws TypeError ctx.isPointInPath(undefined, 50, 50, 'evenodd');
+ @assert throws TypeError ctx.isPointInPath(undefined, 50, 50, undefined);
+ @assert throws TypeError ctx.isPointInPath([], 50, 50);
+ @assert throws TypeError ctx.isPointInPath([], 50, 50, 'nonzero');
+ @assert throws TypeError ctx.isPointInPath([], 50, 50, 'evenodd');
+ @assert throws TypeError ctx.isPointInPath({}, 50, 50);
+ @assert throws TypeError ctx.isPointInPath({}, 50, 50, 'nonzero');
+ @assert throws TypeError ctx.isPointInPath({}, 50, 50, 'evenodd');
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml
new file mode 100644
index 0000000000..0643b047b1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml
@@ -0,0 +1,1042 @@
+- name: 2d.imageData.create2.basic
+ desc: createImageData(sw, sh) exists and returns something
+ code: |
+ @assert ctx.createImageData(1, 1) !== null;
+
+- name: 2d.imageData.create1.basic
+ desc: createImageData(imgdata) exists and returns something
+ code: |
+ @assert ctx.createImageData(ctx.createImageData(1, 1)) !== null;
+
+- name: 2d.imageData.create2.type
+ desc: createImageData(sw, sh) returns an ImageData object containing a Uint8ClampedArray
+ object
+ canvasType: ['HTMLCanvas']
+ code: |
+ @assert window.ImageData !== undefined;
+ @assert window.Uint8ClampedArray !== undefined;
+ window.ImageData.prototype.thisImplementsImageData = true;
+ window.Uint8ClampedArray.prototype.thisImplementsUint8ClampedArray = true;
+ var imgdata = ctx.createImageData(1, 1);
+ @assert imgdata.thisImplementsImageData;
+ @assert imgdata.data.thisImplementsUint8ClampedArray;
+
+- name: 2d.imageData.create1.type
+ desc: createImageData(imgdata) returns an ImageData object containing a Uint8ClampedArray
+ object
+ canvasType: ['HTMLCanvas']
+ code: |
+ @assert window.ImageData !== undefined;
+ @assert window.Uint8ClampedArray !== undefined;
+ window.ImageData.prototype.thisImplementsImageData = true;
+ window.Uint8ClampedArray.prototype.thisImplementsUint8ClampedArray = true;
+ var imgdata = ctx.createImageData(ctx.createImageData(1, 1));
+ @assert imgdata.thisImplementsImageData;
+ @assert imgdata.data.thisImplementsUint8ClampedArray;
+
+- name: 2d.imageData.create2.this
+ desc: createImageData(sw, sh) should throw when called with the wrong |this|
+ canvasType: ['HTMLCanvas']
+ notes: &bindings Defined in "Web IDL" (draft)
+ code: |
+ @assert throws TypeError CanvasRenderingContext2D.prototype.createImageData.call(null, 1, 1); @moz-todo
+ @assert throws TypeError CanvasRenderingContext2D.prototype.createImageData.call(undefined, 1, 1); @moz-todo
+ @assert throws TypeError CanvasRenderingContext2D.prototype.createImageData.call({}, 1, 1); @moz-todo
+
+- name: 2d.imageData.create1.this
+ desc: createImageData(imgdata) should throw when called with the wrong |this|
+ canvasType: ['HTMLCanvas']
+ notes: *bindings
+ code: |
+ var imgdata = ctx.createImageData(1, 1);
+ @assert throws TypeError CanvasRenderingContext2D.prototype.createImageData.call(null, imgdata); @moz-todo
+ @assert throws TypeError CanvasRenderingContext2D.prototype.createImageData.call(undefined, imgdata); @moz-todo
+ @assert throws TypeError CanvasRenderingContext2D.prototype.createImageData.call({}, imgdata); @moz-todo
+
+- name: 2d.imageData.create2.initial
+ desc: createImageData(sw, sh) returns transparent black data of the right size
+ code: |
+ var imgdata = ctx.createImageData(10, 20);
+ @assert imgdata.data.length === imgdata.width*imgdata.height*4;
+ @assert imgdata.width < imgdata.height;
+ @assert imgdata.width > 0;
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; ++i)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ @assert isTransparentBlack;
+
+- name: 2d.imageData.create1.initial
+ desc: createImageData(imgdata) returns transparent black data of the right size
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata1 = ctx.getImageData(0, 0, 10, 20);
+ var imgdata2 = ctx.createImageData(imgdata1);
+ @assert imgdata2.data.length === imgdata1.data.length;
+ @assert imgdata2.width === imgdata1.width;
+ @assert imgdata2.height === imgdata1.height;
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata2.data.length; ++i)
+ if (imgdata2.data[i] !== 0)
+ isTransparentBlack = false;
+ @assert isTransparentBlack;
+
+- name: 2d.imageData.create2.large
+ desc: createImageData(sw, sh) works for sizes much larger than the canvas
+ code: |
+ var imgdata = ctx.createImageData(1000, 2000);
+ @assert imgdata.data.length === imgdata.width*imgdata.height*4;
+ @assert imgdata.width < imgdata.height;
+ @assert imgdata.width > 0;
+ var isTransparentBlack = true;
+ for (var i = 0; i < imgdata.data.length; i += 7813) // check ~1024 points (assuming normal scaling)
+ if (imgdata.data[i] !== 0)
+ isTransparentBlack = false;
+ @assert isTransparentBlack;
+
+- name: 2d.imageData.create2.negative
+ desc: createImageData(sw, sh) takes the absolute magnitude of the size arguments
+ code: |
+ var imgdata1 = ctx.createImageData(10, 20);
+ var imgdata2 = ctx.createImageData(-10, 20);
+ var imgdata3 = ctx.createImageData(10, -20);
+ var imgdata4 = ctx.createImageData(-10, -20);
+ @assert imgdata1.data.length === imgdata2.data.length;
+ @assert imgdata2.data.length === imgdata3.data.length;
+ @assert imgdata3.data.length === imgdata4.data.length;
+
+- name: 2d.imageData.create2.zero
+ desc: createImageData(sw, sh) throws INDEX_SIZE_ERR if size is zero
+ code: |
+ @assert throws INDEX_SIZE_ERR ctx.createImageData(10, 0);
+ @assert throws INDEX_SIZE_ERR ctx.createImageData(0, 10);
+ @assert throws INDEX_SIZE_ERR ctx.createImageData(0, 0);
+ @assert throws INDEX_SIZE_ERR ctx.createImageData(0.99, 10);
+ @assert throws INDEX_SIZE_ERR ctx.createImageData(10, 0.1);
+
+- name: 2d.imageData.create2.nonfinite
+ desc: createImageData() throws TypeError if arguments are not finite
+ notes: *bindings
+ code: |
+ @nonfinite @assert throws TypeError ctx.createImageData(<10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>);
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ @nonfinite @assert throws TypeError ctx.createImageData(<10 posinfobj neginfobj nanobj>, <10 posinfobj neginfobj nanobj>);
+
+- name: 2d.imageData.create1.zero
+ desc: createImageData(null) throws TypeError
+ code: |
+ @assert throws TypeError ctx.createImageData(null);
+
+- name: 2d.imageData.create2.double
+ desc: createImageData(w, h) double is converted to long
+ code: |
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.createImageData(-10.01, -10.99);
+ @assert imgdata1.width === 10;
+ @assert imgdata1.height === 10;
+ @assert imgdata2.width === 10;
+ @assert imgdata2.height === 10;
+
+- name: 2d.imageData.get.double
+ desc: createImageData(w, h) double is converted to long
+ code: |
+ var imgdata1 = ctx.getImageData(0, 0, 10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, -10.01, -10.99);
+ @assert imgdata1.width === 10;
+ @assert imgdata1.height === 10;
+ @assert imgdata2.width === 10;
+ @assert imgdata2.height === 10;
+
+- name: 2d.imageData.create2.round
+ desc: createImageData(w, h) is rounded the same as getImageData(0, 0, w, h)
+ code: |
+ var imgdata1 = ctx.createImageData(10.01, 10.99);
+ var imgdata2 = ctx.getImageData(0, 0, 10.01, 10.99);
+ @assert imgdata1.width === imgdata2.width;
+ @assert imgdata1.height === imgdata2.height;
+
+- name: 2d.imageData.create.and.resize
+ desc: Verify no crash when resizing an image bitmap to zero.
+ canvasType: ['HTMLCanvas']
+ images:
+ - red.png
+ code: |
+ var image = new Image();
+ image.onload = t.step_func(function() {
+ var options = { resizeHeight: 0 };
+ var p1 = createImageBitmap(image, options);
+ p1.catch(function(error){});
+ t.done();
+ });
+ image.src = 'red.png';
+
+- name: 2d.imageData.get.basic
+ desc: getImageData() exists and returns something
+ code: |
+ @assert ctx.getImageData(0, 0, 100, 50) !== null;
+
+- name: 2d.imageData.get.type
+ canvasType: ['HTMLCanvas']
+ desc: getImageData() returns an ImageData object containing a Uint8ClampedArray
+ object
+ code: |
+ @assert window.ImageData !== undefined;
+ @assert window.Uint8ClampedArray !== undefined;
+ window.ImageData.prototype.thisImplementsImageData = true;
+ window.Uint8ClampedArray.prototype.thisImplementsUint8ClampedArray = true;
+ var imgdata = ctx.getImageData(0, 0, 1, 1);
+ @assert imgdata.thisImplementsImageData;
+ @assert imgdata.data.thisImplementsUint8ClampedArray;
+
+- name: 2d.imageData.get.zero
+ desc: getImageData() throws INDEX_SIZE_ERR if size is zero
+ code: |
+ @assert throws INDEX_SIZE_ERR ctx.getImageData(1, 1, 10, 0);
+ @assert throws INDEX_SIZE_ERR ctx.getImageData(1, 1, 0, 10);
+ @assert throws INDEX_SIZE_ERR ctx.getImageData(1, 1, 0, 0);
+ @assert throws INDEX_SIZE_ERR ctx.getImageData(1, 1, 0.1, 10);
+ @assert throws INDEX_SIZE_ERR ctx.getImageData(1, 1, 10, 0.99);
+ @assert throws INDEX_SIZE_ERR ctx.getImageData(1, 1, -0.1, 10);
+ @assert throws INDEX_SIZE_ERR ctx.getImageData(1, 1, 10, -0.99);
+
+- name: 2d.imageData.get.nonfinite
+ desc: getImageData() throws TypeError if arguments are not finite
+ notes: *bindings
+ code: |
+ @nonfinite @assert throws TypeError ctx.getImageData(<10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>);
+ var posinfobj = { valueOf: function() { return Infinity; } },
+ neginfobj = { valueOf: function() { return -Infinity; } },
+ nanobj = { valueOf: function() { return -Infinity; } };
+ @nonfinite @assert throws TypeError ctx.getImageData(<10 posinfobj neginfobj nanobj>, <10 posinfobj neginfobj nanobj>, <10 posinfobj neginfobj nanobj>, <10 posinfobj neginfobj nanobj>);
+
+- name: 2d.imageData.get.source.outside
+ desc: getImageData() returns transparent black outside the canvas
+ code: |
+ ctx.fillStyle = '#08f';
+ ctx.fillRect(0, 0, 100, 50);
+
+ var imgdata1 = ctx.getImageData(-10, 5, 1, 1);
+ @assert imgdata1.data[0] === 0;
+ @assert imgdata1.data[1] === 0;
+ @assert imgdata1.data[2] === 0;
+ @assert imgdata1.data[3] === 0;
+
+ var imgdata2 = ctx.getImageData(10, -5, 1, 1);
+ @assert imgdata2.data[0] === 0;
+ @assert imgdata2.data[1] === 0;
+ @assert imgdata2.data[2] === 0;
+ @assert imgdata2.data[3] === 0;
+
+ var imgdata3 = ctx.getImageData(200, 5, 1, 1);
+ @assert imgdata3.data[0] === 0;
+ @assert imgdata3.data[1] === 0;
+ @assert imgdata3.data[2] === 0;
+ @assert imgdata3.data[3] === 0;
+
+ var imgdata4 = ctx.getImageData(10, 60, 1, 1);
+ @assert imgdata4.data[0] === 0;
+ @assert imgdata4.data[1] === 0;
+ @assert imgdata4.data[2] === 0;
+ @assert imgdata4.data[3] === 0;
+
+ var imgdata5 = ctx.getImageData(100, 10, 1, 1);
+ @assert imgdata5.data[0] === 0;
+ @assert imgdata5.data[1] === 0;
+ @assert imgdata5.data[2] === 0;
+ @assert imgdata5.data[3] === 0;
+
+ var imgdata6 = ctx.getImageData(0, 10, 1, 1);
+ @assert imgdata6.data[0] === 0;
+ @assert imgdata6.data[1] === 136;
+ @assert imgdata6.data[2] === 255;
+ @assert imgdata6.data[3] === 255;
+
+ var imgdata7 = ctx.getImageData(-10, 10, 20, 20);
+ @assert imgdata7.data[ 0*4+0] === 0;
+ @assert imgdata7.data[ 0*4+1] === 0;
+ @assert imgdata7.data[ 0*4+2] === 0;
+ @assert imgdata7.data[ 0*4+3] === 0;
+ @assert imgdata7.data[ 9*4+0] === 0;
+ @assert imgdata7.data[ 9*4+1] === 0;
+ @assert imgdata7.data[ 9*4+2] === 0;
+ @assert imgdata7.data[ 9*4+3] === 0;
+ @assert imgdata7.data[10*4+0] === 0;
+ @assert imgdata7.data[10*4+1] === 136;
+ @assert imgdata7.data[10*4+2] === 255;
+ @assert imgdata7.data[10*4+3] === 255;
+ @assert imgdata7.data[19*4+0] === 0;
+ @assert imgdata7.data[19*4+1] === 136;
+ @assert imgdata7.data[19*4+2] === 255;
+ @assert imgdata7.data[19*4+3] === 255;
+ @assert imgdata7.data[20*4+0] === 0;
+ @assert imgdata7.data[20*4+1] === 0;
+ @assert imgdata7.data[20*4+2] === 0;
+ @assert imgdata7.data[20*4+3] === 0;
+
+- name: 2d.imageData.get.source.negative
+ desc: getImageData() works with negative width and height, and returns top-to-bottom
+ left-to-right
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+
+ var imgdata1 = ctx.getImageData(85, 25, -10, -10);
+ @assert imgdata1.data[0] === 255;
+ @assert imgdata1.data[1] === 255;
+ @assert imgdata1.data[2] === 255;
+ @assert imgdata1.data[3] === 255;
+ @assert imgdata1.data[imgdata1.data.length-4+0] === 0;
+ @assert imgdata1.data[imgdata1.data.length-4+1] === 0;
+ @assert imgdata1.data[imgdata1.data.length-4+2] === 0;
+ @assert imgdata1.data[imgdata1.data.length-4+3] === 255;
+
+ var imgdata2 = ctx.getImageData(0, 0, -1, -1);
+ @assert imgdata2.data[0] === 0;
+ @assert imgdata2.data[1] === 0;
+ @assert imgdata2.data[2] === 0;
+ @assert imgdata2.data[3] === 0;
+
+- name: 2d.imageData.get.source.size
+ desc: getImageData() returns bigger ImageData for bigger source rectangle
+ code: |
+ var imgdata1 = ctx.getImageData(0, 0, 10, 10);
+ var imgdata2 = ctx.getImageData(0, 0, 20, 20);
+ @assert imgdata2.width > imgdata1.width;
+ @assert imgdata2.height > imgdata1.height;
+
+- name: 2d.imageData.get.nonpremul
+ desc: getImageData() returns non-premultiplied colors
+ code: |
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(10, 10, 10, 10);
+ @assert imgdata.data[0] > 200;
+ @assert imgdata.data[1] > 200;
+ @assert imgdata.data[2] > 200;
+ @assert imgdata.data[3] > 100;
+ @assert imgdata.data[3] < 200;
+
+- name: 2d.imageData.get.range
+ desc: getImageData() returns values in the range [0, 255]
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ @assert imgdata1.data[0] === 0;
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ @assert imgdata2.data[0] === 255;
+
+- name: 2d.imageData.get.clamp
+ desc: getImageData() clamps colors to the range [0, 255]
+ code: |
+ ctx.fillStyle = 'rgb(-100, -200, -300)';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgb(256, 300, 400)';
+ ctx.fillRect(20, 10, 60, 10);
+ var imgdata1 = ctx.getImageData(10, 5, 1, 1);
+ @assert imgdata1.data[0] === 0;
+ @assert imgdata1.data[1] === 0;
+ @assert imgdata1.data[2] === 0;
+ var imgdata2 = ctx.getImageData(30, 15, 1, 1);
+ @assert imgdata2.data[0] === 255;
+ @assert imgdata2.data[1] === 255;
+ @assert imgdata2.data[2] === 255;
+
+- name: 2d.imageData.get.length
+ desc: getImageData() returns a correctly-sized Uint8ClampedArray
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ @assert imgdata.data.length === imgdata.width*imgdata.height*4;
+
+- name: 2d.imageData.get.order.cols
+ desc: getImageData() returns leftmost columns first
+ code: |
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 2, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ @assert imgdata.data[0] === 0;
+ @assert imgdata.data[Math.round(imgdata.width/2*4)] === 255;
+ @assert imgdata.data[Math.round((imgdata.height/2)*imgdata.width*4)] === 0;
+
+- name: 2d.imageData.get.order.rows
+ desc: getImageData() returns topmost rows first
+ code: |
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 2);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ @assert imgdata.data[0] === 0;
+ @assert imgdata.data[Math.floor(imgdata.width/2*4)] === 0;
+ @assert imgdata.data[(imgdata.height/2)*imgdata.width*4] === 255;
+
+- name: 2d.imageData.get.order.rgb
+ desc: getImageData() returns R then G then B
+ code: |
+ ctx.fillStyle = '#48c';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ @assert imgdata.data[0] === 0x44;
+ @assert imgdata.data[1] === 0x88;
+ @assert imgdata.data[2] === 0xCC;
+ @assert imgdata.data[3] === 255;
+ @assert imgdata.data[4] === 0x44;
+ @assert imgdata.data[5] === 0x88;
+ @assert imgdata.data[6] === 0xCC;
+ @assert imgdata.data[7] === 255;
+
+- name: 2d.imageData.get.order.alpha
+ desc: getImageData() returns A in the fourth component
+ code: |
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
+ ctx.fillRect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ @assert imgdata.data[3] < 200;
+ @assert imgdata.data[3] > 100;
+
+- name: 2d.imageData.get.unaffected
+ desc: getImageData() is not affected by context state
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50)
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.rect(0, 0, 5, 5);
+ ctx.clip();
+ var imgdata = ctx.getImageData(0, 0, 50, 50);
+ ctx.restore();
+ ctx.putImageData(imgdata, 50, 0);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.get.large.crash
+ desc: Test that canvas crash when image data cannot be allocated.
+ code: |
+ @assert throws TypeError ctx.getImageData(10, 0xffffffff, 2147483647, 10);
+
+- name: 2d.imageData.get.rounding
+ desc: Test the handling of non-integer source coordinates in getImageData().
+ code: |
+ function testDimensions(sx, sy, sw, sh, width, height)
+ {
+ imageData = ctx.getImageData(sx, sy, sw, sh);
+ @assert imageData.width == width;
+ @assert imageData.height == height;
+ }
+
+ testDimensions(0, 0, 20, 10, 20, 10);
+
+ testDimensions(.1, .2, 20, 10, 20, 10);
+ testDimensions(.9, .8, 20, 10, 20, 10);
+
+ testDimensions(0, 0, 20.9, 10.9, 20, 10);
+ testDimensions(0, 0, 20.1, 10.1, 20, 10);
+
+ testDimensions(-1, -1, 20, 10, 20, 10);
+
+ testDimensions(-1.1, 0, 20, 10, 20, 10);
+ testDimensions(-1.9, 0, 20, 10, 20, 10);
+
+- name: 2d.imageData.get.invalid
+ desc: Verify getImageData() behavior in invalid cases.
+ code: |
+ imageData = ctx.getImageData(0,0,2,2);
+ var testValues = [NaN, true, false, "\"garbage\"", "-1",
+ "0", "1", "2", Infinity, -Infinity,
+ -5, -0.5, 0, 0.5, 5,
+ 5.4, 255, 256, null, undefined];
+ var testResults = [0, 1, 0, 0, 0,
+ 0, 1, 2, 255, 0,
+ 0, 0, 0, 0, 5,
+ 5, 255, 255, 0, 0];
+ for (var i = 0; i < testValues.length; i++) {
+ imageData.data[0] = testValues[i];
+ @assert imageData.data[0] == testResults[i];
+ }
+ imageData.data['foo']='garbage';
+ @assert imageData.data['foo'] == 'garbage';
+ imageData.data[-1]='garbage';
+ @assert imageData.data[-1] == undefined;
+ imageData.data[17]='garbage';
+ @assert imageData.data[17] == undefined;
+
+- name: 2d.imageData.object.properties
+ desc: ImageData objects have the right properties
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ @assert typeof(imgdata.width) === 'number';
+ @assert typeof(imgdata.height) === 'number';
+ @assert typeof(imgdata.data) === 'object';
+
+- name: 2d.imageData.object.readonly
+ desc: ImageData objects properties are read-only
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ var w = imgdata.width;
+ var h = imgdata.height;
+ var d = imgdata.data;
+ imgdata.width = 123;
+ imgdata.height = 123;
+ imgdata.data = [100,100,100,100];
+ @assert imgdata.width === w;
+ @assert imgdata.height === h;
+ @assert imgdata.data === d;
+ @assert imgdata.data[0] === 0;
+ @assert imgdata.data[1] === 0;
+ @assert imgdata.data[2] === 0;
+ @assert imgdata.data[3] === 0;
+
+- name: 2d.imageData.object.ctor.size
+ canvasType: ['HTMLCanvas']
+ desc: ImageData has a usable constructor
+ code: |
+ @assert window.ImageData !== undefined;
+
+ var imgdata = new window.ImageData(2, 3);
+ @assert imgdata.width === 2;
+ @assert imgdata.height === 3;
+ @assert imgdata.data.length === 2 * 3 * 4;
+ for (var i = 0; i < imgdata.data.length; ++i) {
+ @assert imgdata.data[i] === 0;
+ }
+
+- name: 2d.imageData.object.ctor.basics
+ canvasType: ['HTMLCanvas']
+ desc: Testing different type of ImageData constructor
+ code: |
+ function setRGBA(imageData, i, rgba)
+ {
+ var s = i * 4;
+ imageData[s] = rgba[0];
+ imageData[s + 1] = rgba[1];
+ imageData[s + 2] = rgba[2];
+ imageData[s + 3] = rgba[3];
+ }
+
+ function getRGBA(imageData, i)
+ {
+ var result = [];
+ var s = i * 4;
+ for (var j = 0; j < 4; j++) {
+ result[j] = imageData[s + j];
+ }
+ return result;
+ }
+
+ function assertArrayEquals(actual, expected)
+ {
+ @assert typeof actual === "object";
+ @assert actual !== null;
+ @assert "length" in actual === true;
+ @assert actual.length === expected.length;
+ for (var i = 0; i < actual.length; i++) {
+ @assert actual.hasOwnProperty(i) === expected.hasOwnProperty(i);
+ @assert actual[i] === expected[i];
+ }
+ }
+
+ @assert ImageData !== undefined;
+ imageData = new ImageData(100, 50);
+
+ @assert imageData !== null;
+ @assert imageData.data !== null;
+ @assert imageData.width === 100;
+ @assert imageData.height === 50;
+ assertArrayEquals(getRGBA(imageData.data, 4), [0, 0, 0, 0]);
+
+ var testColor = [0, 255, 255, 128];
+ setRGBA(imageData.data, 4, testColor);
+ assertArrayEquals(getRGBA(imageData.data, 4), testColor);
+
+ @assert throws TypeError ImageData(1, 1);
+ @assert throws TypeError new ImageData(10);
+ @assert throws INDEX_SIZE_ERR new ImageData(0, 10);
+ @assert throws INDEX_SIZE_ERR new ImageData(10, 0);
+ @assert throws INDEX_SIZE_ERR new ImageData('width', 'height');
+ @assert throws INDEX_SIZE_ERR new ImageData(1 << 31, 1 << 31);
+ @assert throws TypeError new ImageData(new Uint8ClampedArray(0));
+ @assert throws INDEX_SIZE_ERR new ImageData(new Uint8Array(100), 25);
+ @assert throws INVALID_STATE_ERR new ImageData(new Uint8ClampedArray(27), 2);
+ @assert throws INDEX_SIZE_ERR new ImageData(new Uint8ClampedArray(28), 7, 0);
+ @assert throws INDEX_SIZE_ERR new ImageData(new Uint8ClampedArray(104), 14);
+ @assert throws INDEX_SIZE_ERR new ImageData(new Uint8ClampedArray([12, 34, 168, 65328]), 1, 151);
+ @assert throws TypeError new ImageData(self, 4, 4);
+ @assert throws TypeError new ImageData(null, 4, 4);
+ @assert throws INDEX_SIZE_ERR new ImageData(imageData.data, 0);
+ @assert throws INDEX_SIZE_ERR new ImageData(imageData.data, 13);
+ @assert throws INDEX_SIZE_ERR new ImageData(imageData.data, 1 << 31);
+ @assert throws INDEX_SIZE_ERR new ImageData(imageData.data, 'biggish');
+ @assert throws INDEX_SIZE_ERR new ImageData(imageData.data, 1 << 24, 1 << 31);
+ @assert new ImageData(new Uint8ClampedArray(28), 7).height === 1;
+
+ imageDataFromData = new ImageData(imageData.data, 100);
+ @assert imageDataFromData.width === 100;
+ @assert imageDataFromData.height === 50;
+ @assert imageDataFromData.data === imageData.data;
+ assertArrayEquals(getRGBA(imageDataFromData.data, 10), getRGBA(imageData.data, 10));
+ setRGBA(imageData.data, 10, testColor);
+ assertArrayEquals(getRGBA(imageDataFromData.data, 10), getRGBA(imageData.data, 10));
+
+ var data = new Uint8ClampedArray(400);
+ data[22] = 129;
+ imageDataFromData = new ImageData(data, 20, 5);
+ @assert imageDataFromData.width === 20;
+ @assert imageDataFromData.height === 5;
+ @assert imageDataFromData.data === data;
+ assertArrayEquals(getRGBA(imageDataFromData.data, 2), getRGBA(data, 2));
+ setRGBA(imageDataFromData.data, 2, testColor);
+ assertArrayEquals(getRGBA(imageDataFromData.data, 2), getRGBA(data, 2));
+
+ if (window.SharedArrayBuffer) {
+ @assert throws TypeError new ImageData(new Uint16Array(new SharedArrayBuffer(32)), 4, 2);
+ }
+
+- name: 2d.imageData.object.ctor.array
+ desc: ImageData has a usable constructor
+ canvasType: ['HTMLCanvas']
+ code: |
+ @assert window.ImageData !== undefined;
+
+ var array = new Uint8ClampedArray(8);
+ var imgdata = new window.ImageData(array, 1, 2);
+ @assert imgdata.width === 1;
+ @assert imgdata.height === 2;
+ @assert imgdata.data === array;
+
+- name: 2d.imageData.object.ctor.array.bounds
+ desc: ImageData has a usable constructor
+ canvasType: ['HTMLCanvas']
+ code: |
+ @assert window.ImageData !== undefined;
+
+ @assert throws INVALID_STATE_ERR new ImageData(new Uint8ClampedArray(0), 1);
+ @assert throws INVALID_STATE_ERR new ImageData(new Uint8ClampedArray(3), 1);
+ @assert throws INDEX_SIZE_ERR new ImageData(new Uint8ClampedArray(4), 0);
+ @assert throws INDEX_SIZE_ERR new ImageData(new Uint8ClampedArray(4), 1, 2);
+ @assert throws TypeError new ImageData(new Uint8Array(8), 1, 2);
+ @assert throws TypeError new ImageData(new Int8Array(8), 1, 2);
+
+- name: 2d.imageData.object.set
+ desc: ImageData.data can be modified
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ @assert imgdata.data[0] === 100;
+ imgdata.data[0] = 200;
+ @assert imgdata.data[0] === 200;
+
+- name: 2d.imageData.object.undefined
+ desc: ImageData.data converts undefined to 0
+ webidl:
+ - es-octet
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = undefined;
+ @assert imgdata.data[0] === 0;
+
+- name: 2d.imageData.object.nan
+ desc: ImageData.data converts NaN to 0
+ webidl:
+ - es-octet
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = NaN;
+ @assert imgdata.data[0] === 0;
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "cheese";
+ @assert imgdata.data[0] === 0;
+
+- name: 2d.imageData.object.string
+ desc: ImageData.data converts strings to numbers with ToNumber
+ webidl:
+ - es-octet
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "110";
+ @assert imgdata.data[0] === 110;
+ imgdata.data[0] = 100;
+ imgdata.data[0] = "0x78";
+ @assert imgdata.data[0] === 120;
+ imgdata.data[0] = 100;
+ imgdata.data[0] = " +130e0 ";
+ @assert imgdata.data[0] === 130;
+
+- name: 2d.imageData.object.clamp
+ desc: ImageData.data clamps numbers to [0, 255]
+ webidl:
+ - es-octet
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 300;
+ @assert imgdata.data[0] === 255;
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -100;
+ @assert imgdata.data[0] === 0;
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = 200+Math.pow(2, 32);
+ @assert imgdata.data[0] === 255;
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -200-Math.pow(2, 32);
+ @assert imgdata.data[0] === 0;
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Math.pow(10, 39);
+ @assert imgdata.data[0] === 255;
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Math.pow(10, 39);
+ @assert imgdata.data[0] === 0;
+
+ imgdata.data[0] = 100;
+ imgdata.data[0] = -Infinity;
+ @assert imgdata.data[0] === 0;
+ imgdata.data[0] = 100;
+ imgdata.data[0] = Infinity;
+ @assert imgdata.data[0] === 255;
+
+- name: 2d.imageData.object.round
+ desc: ImageData.data rounds numbers with round-to-zero
+ webidl:
+ - es-octet
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ imgdata.data[0] = 0.499;
+ @assert imgdata.data[0] === 0;
+ imgdata.data[0] = 0.5;
+ @assert imgdata.data[0] === 0;
+ imgdata.data[0] = 0.501;
+ @assert imgdata.data[0] === 1;
+ imgdata.data[0] = 1.499;
+ @assert imgdata.data[0] === 1;
+ imgdata.data[0] = 1.5;
+ @assert imgdata.data[0] === 2;
+ imgdata.data[0] = 1.501;
+ @assert imgdata.data[0] === 2;
+ imgdata.data[0] = 2.5;
+ @assert imgdata.data[0] === 2;
+ imgdata.data[0] = 3.5;
+ @assert imgdata.data[0] === 4;
+ imgdata.data[0] = 252.5;
+ @assert imgdata.data[0] === 252;
+ imgdata.data[0] = 253.5;
+ @assert imgdata.data[0] === 254;
+ imgdata.data[0] = 254.5;
+ @assert imgdata.data[0] === 254;
+ imgdata.data[0] = 256.5;
+ @assert imgdata.data[0] === 255;
+ imgdata.data[0] = -0.5;
+ @assert imgdata.data[0] === 0;
+ imgdata.data[0] = -1.5;
+ @assert imgdata.data[0] === 0;
+
+
+
+- name: 2d.imageData.put.null
+ desc: putImageData() with null imagedata throws TypeError
+ code: |
+ @assert throws TypeError ctx.putImageData(null, 0, 0);
+
+- name: 2d.imageData.put.nonfinite
+ desc: putImageData() throws TypeError if arguments are not finite
+ notes: *bindings
+ code: |
+ var imgdata = ctx.getImageData(0, 0, 10, 10);
+ @nonfinite @assert throws TypeError ctx.putImageData(<imgdata>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>);
+ @nonfinite @assert throws TypeError ctx.putImageData(<imgdata>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>, <10 Infinity -Infinity NaN>);
+
+- name: 2d.imageData.put.basic
+ desc: putImageData() puts image data from getImageData() onto the canvas
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.created
+ desc: putImageData() puts image data from createImageData() onto the canvas
+ code: |
+ var imgdata = ctx.createImageData(100, 50);
+ for (var i = 0; i < imgdata.data.length; i += 4) {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ imgdata.data[i+2] = 0;
+ imgdata.data[i+3] = 255;
+ }
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.wrongtype
+ desc: putImageData() does not accept non-ImageData objects
+ code: |
+ var imgdata = { width: 1, height: 1, data: [255, 0, 0, 255] };
+ @assert throws TypeError ctx.putImageData(imgdata, 0, 0);
+ @assert throws TypeError ctx.putImageData("cheese", 0, 0);
+ @assert throws TypeError ctx.putImageData(42, 0, 0);
+ expected: green
+
+- name: 2d.imageData.put.cross
+ desc: putImageData() accepts image data got from a different canvas
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50)
+ var imgdata = ctx2.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.cross
+ desc: putImageData() accepts image data got from a different canvas
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ ctx2.fillStyle = '#0f0';
+ ctx2.fillRect(0, 0, 100, 50)
+ var imgdata = ctx2.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.alpha
+ desc: putImageData() puts non-solid image data correctly
+ code: |
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.25)';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,64;
+ expected: |
+ size 100 50
+ cr.set_source_rgba(0, 1, 0, 0.25)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.imageData.put.modified
+ desc: putImageData() puts modified image data correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(45, 20, 10, 10)
+ var imgdata = ctx.getImageData(45, 20, 10, 10);
+ for (var i = 0, len = imgdata.width*imgdata.height*4; i < len; i += 4)
+ {
+ imgdata.data[i] = 0;
+ imgdata.data[i+1] = 255;
+ }
+ ctx.putImageData(imgdata, 45, 20);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.dirty.zero
+ desc: putImageData() with zero-sized dirty rectangle puts nothing
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.putImageData(imgdata, 0, 0, 0, 0, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.dirty.rect1
+ desc: putImageData() only modifies areas inside the dirty rectangle, using width
+ and height
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 0, 0, 20, 20);
+
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 35,25 ==~ 0,255,0,255;
+ @assert pixel 65,25 ==~ 0,255,0,255;
+ @assert pixel 50,15 ==~ 0,255,0,255;
+ @assert pixel 50,45 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.dirty.rect2
+ desc: putImageData() only modifies areas inside the dirty rectangle, using x and
+ y
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(60, 30, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, -20, -10, 60, 30, 20, 20);
+
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 35,25 ==~ 0,255,0,255;
+ @assert pixel 65,25 ==~ 0,255,0,255;
+ @assert pixel 50,15 ==~ 0,255,0,255;
+ @assert pixel 50,45 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.dirty.negative
+ desc: putImageData() handles negative-sized dirty rectangles correctly
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 20, 20)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(40, 20, 20, 20)
+ ctx.putImageData(imgdata, 40, 20, 20, 20, -20, -20);
+
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 35,25 ==~ 0,255,0,255;
+ @assert pixel 65,25 ==~ 0,255,0,255;
+ @assert pixel 50,15 ==~ 0,255,0,255;
+ @assert pixel 50,45 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.dirty.outside
+ desc: putImageData() handles dirty rectangles outside the canvas correctly
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+
+ ctx.putImageData(imgdata, 100, 20, 20, 20, -20, -20);
+ ctx.putImageData(imgdata, 200, 200, 0, 0, 100, 50);
+ ctx.putImageData(imgdata, 40, 20, -30, -20, 30, 20);
+ ctx.putImageData(imgdata, -30, 20, 0, 0, 30, 20);
+
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 98,15 ==~ 0,255,0,255;
+ @assert pixel 98,25 ==~ 0,255,0,255;
+ @assert pixel 98,45 ==~ 0,255,0,255;
+ @assert pixel 1,5 ==~ 0,255,0,255;
+ @assert pixel 1,25 ==~ 0,255,0,255;
+ @assert pixel 1,45 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.unchanged
+ desc: putImageData(getImageData(...), ...) has no effect
+ code: |
+ var i = 0;
+ for (var y = 0; y < 16; ++y) {
+ for (var x = 0; x < 16; ++x, ++i) {
+ ctx.fillStyle = 'rgba(' + i + ',' + (Math.floor(i*1.5) % 256) + ',' + (Math.floor(i*23.3) % 256) + ',' + (i/256) + ')';
+ ctx.fillRect(x, y, 1, 1);
+ }
+ }
+ var imgdata1 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ var olddata = [];
+ for (var i = 0; i < imgdata1.data.length; ++i)
+ olddata[i] = imgdata1.data[i];
+
+ ctx.putImageData(imgdata1, 0.1, 0.2);
+
+ var imgdata2 = ctx.getImageData(0.1, 0.2, 15.8, 15.9);
+ for (var i = 0; i < imgdata2.data.length; ++i) {
+ @assert olddata[i] === imgdata2.data[i];
+ }
+
+- name: 2d.imageData.put.unaffected
+ desc: putImageData() is not affected by context state
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.globalAlpha = 0.1;
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.translate(100, 50);
+ ctx.scale(0.1, 0.1);
+ ctx.putImageData(imgdata, 0, 0);
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.clip
+ desc: putImageData() is not affected by clipping regions
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50)
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.putImageData(imgdata, 0, 0);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.imageData.put.path
+ desc: putImageData() does not affect the current path
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50)
+ ctx.rect(0, 0, 100, 50);
+ var imgdata = ctx.getImageData(0, 0, 100, 50);
+ ctx.putImageData(imgdata, 0, 0);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml
new file mode 100644
index 0000000000..086fb04e04
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml
@@ -0,0 +1,286 @@
+- name: 2d.reset.basic
+ desc: reset clears to transparent black
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ ctx.reset();
+ @assert pixel 0,0 == 0,0,0,0;
+ @assert pixel 50,25 == 0,0,0,0;
+ @assert pixel 25,50 == 0,0,0,0;
+ @assert pixel 100,50 == 0,0,0,0;
+ @assert pixel 0,50 == 0,0,0,0;
+ @assert pixel 100,0 == 0,0,0,0;
+ t.done();
+
+- name: 2d.reset.state
+ desc: check that the state is reset
+ code: |
+ const default_value = ctx.{{ state_name }};
+
+ ctx.{{ state_name }} = {{ new_value }};
+ @assert ctx.{{ state_name }} == {{ new_value }};
+
+ ctx.reset();
+ @assert ctx.{{ state_name }} == default_value;
+
+ variants:
+ letter_spacing:
+ state_name: letterSpacing
+ new_value: "'12px'"
+
+ word_spacing:
+ state_name: wordSpacing
+ new_value: "'12px'"
+
+ fill_style:
+ state_name: fillStyle
+ new_value: "'#ffffff'"
+
+ stroke_style:
+ state_name: strokeStyle
+ new_value: "'#ffffff'"
+
+ filter:
+ state_name: filter
+ new_value: "'blur(10px)'"
+
+ font:
+ state_name: font
+ new_value: "'25px sans-serif'"
+
+ global_alpha:
+ state_name: globalAlpha
+ new_value: 0.5
+
+ global_composite_operation:
+ state_name: globalCompositeOperation
+ new_value: "'destination-over'"
+
+ line_width:
+ state_name: lineWidth
+ new_value: 1
+
+ line_cap:
+ state_name: lineCap
+ new_value: "'square'"
+
+ line_join:
+ state_name: lineJoin
+ new_value: "'bevel'"
+
+ miter_limit:
+ state_name: miterLimit
+ new_value: 1.0
+
+ line_dash_offset:
+ state_name: lineDashOffset
+ new_value: 1.0
+
+ shadow_offset_x:
+ state_name: shadowOffsetX
+ new_value: 10.0
+
+ shadow_offset_y:
+ state_name: shadowOffsetY
+ new_value: 10.0
+
+ shadow_blur:
+ state_name: shadowBlur
+ new_value: 10.0
+
+ shadow_color:
+ state_name: shadowColor
+ new_value: "'#ff0000'"
+
+ font:
+ state_name: font
+ new_value: "'16px sans-serif'"
+
+ text_align:
+ state_name: textAlign
+ new_value: "'end'"
+
+ text_baseline:
+ state_name: textBaseline
+ new_value: "'middle'"
+
+ direction:
+ state_name: direction
+ new_value: "'rtl'"
+
+ font_kerning:
+ state_name: fontKerning
+ new_value: "'normal'"
+
+ font_stretch:
+ state_name: fontStretch
+ new_value: "'ultra-condensed'"
+
+ font_variant_caps:
+ state_name: fontVariantCaps
+ new_value: "'unicase'"
+
+ text_rendering:
+ state_name: textRendering
+ new_value: "'geometricPrecision'"
+
+ image_smoothing_enabled:
+ state_name: imageSmoothingEnabled
+ new_value: "false"
+
+ image_smoothing_quality:
+ state_name: imageSmoothingQuality
+ new_value: "'high'"
+
+- name: 2d.reset.state.transformation_matrix
+ desc: check that the state is reset
+ code: |
+ ctx.scale(2, 2);
+
+ ctx.reset();
+ @assert ctx.getTransform().isIdentity;
+
+- name: 2d.reset.state.clip
+ desc: check that the clip is reset
+ size: [200, 200]
+ code: |
+ ctx.beginPath();
+ ctx.rect(0, 0, 100, 100);
+ ctx.clip();
+
+ ctx.fillRect(0, 0, 200, 200);
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 200, 200);
+ reference: |
+ ctx.fillRect(0, 0, 200, 200);
+
+- name: 2d.reset.state.line_dash
+ desc: check that the line dash is reset
+ code: |
+ ctx.setLineDash([1, 2]);
+
+ ctx.reset();
+ @assert ctx.getLineDash().length == 0;
+
+- name: 2d.reset.render.drop_shadow
+ desc: check that drop shadows are correctly rendered after reset
+ size: [500, 500]
+ code: |
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = "red";
+ ctx.shadowBlur = 10;
+
+ ctx.reset();
+
+ ctx.fillRect(100, 100, 100, 100);
+ reference: |
+ ctx.fillRect(100, 100, 100, 100);
+
+- name: 2d.reset.render.text
+ desc: check that text is correctly rendered after reset
+ size: [400, 400]
+ code: |
+ ctx.font = "24px serif";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "hanging";
+ ctx.direction = "rtl";
+ ctx.letterSpacing = "10px";
+ ctx.fontKerning = "none";
+ ctx.fontStretch = "semi-condensed";
+ ctx.fontVariantCaps = "tilting-caps";
+ ctx.textRendering = "optimizeLegibility";
+ ctx.wordSpacing = "20px";
+
+ ctx.reset();
+
+ ctx.fillText("Lorem ipsum dolor sit amet, consectetur adipiscing elit", 0, 10);
+ reference: |
+ ctx.fillText("Lorem ipsum dolor sit amet, consectetur adipiscing elit", 0, 10);
+
+- name: 2d.reset.render.line
+ desc: check that lines are correctly rendered after reset
+ size: [400, 400]
+ code: |
+ ctx.lineWidth = 10;
+ ctx.lineCap = "round";
+ ctx.lineJoin = "bevel";
+ ctx.lineDashOffset = 10;
+ ctx.setLineDash([20]);
+
+ ctx.reset();
+
+ ctx.beginPath();
+ ctx.moveTo(100, 100);
+ ctx.lineTo(100, 300);
+ ctx.lineTo(300, 300);
+ ctx.lineTo(300, 100);
+ ctx.stroke();
+ reference: |
+ ctx.beginPath();
+ ctx.moveTo(100, 100);
+ ctx.lineTo(100, 300);
+ ctx.lineTo(300, 300);
+ ctx.lineTo(300, 100);
+ ctx.stroke();
+
+- name: 2d.reset.render.miter_limit
+ desc: check that the lines are correctly rendered with the default miter limit after reset
+ size: [400, 400]
+ code: |
+ ctx.miterLimit = 6;
+
+ ctx.reset();
+
+ ctx.lineWidth = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 100);
+ for (let i = 0; i < 24; i++) {
+ const dy = i % 2 === 0 ? 25 : -25;
+ ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+ }
+ ctx.stroke();
+ reference: |
+ ctx.lineWidth = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 100);
+ for (let i = 0; i < 24; i++) {
+ const dy = i % 2 === 0 ? 25 : -25;
+ ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+ }
+ ctx.stroke();
+
+- name: 2d.reset.render.global_composite_operation
+ desc: check that canvas correctly renders rectangles with the default global composite operation after reset
+ size: [400, 400]
+ code: |
+ ctx.globalCompositeOperation = "xor";
+
+ ctx.reset();
+
+ ctx.fillRect(10, 10, 100, 100);
+ ctx.fillRect(50, 50, 100, 100);
+ reference: |
+ ctx.fillRect(10, 10, 100, 100);
+ ctx.fillRect(50, 50, 100, 100);
+
+- name: 2d.reset.render.misc
+ desc: check that canvas correctly renders rectangles after reset (states not covered by other tests)
+ size: [400, 400]
+ code: |
+ ctx.fillStyle = "red";
+ ctx.strokeStyle = "red";
+ ctx.globalAlpha = 0.5;
+ ctx.filter = "blur(2px)";
+
+ ctx.reset();
+
+ ctx.fillRect(0, 0, 100, 100);
+ ctx.strokeRect(150, 150, 100, 100);
+ reference: |
+ ctx.fillRect(0, 0, 100, 100);
+ ctx.strokeRect(150, 150, 100, 100);
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml
new file mode 100644
index 0000000000..dd088aa396
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml
@@ -0,0 +1,76 @@
+- name: 2d.scrollPathIntoView.basic
+ desc: scrollPathIntoView() works
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; left: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ ctx.beginPath();
+ ctx.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView();
+ var rect = canvas.getBoundingClientRect();
+ @assert Math.round(rect.top) === -8;
+ @assert Math.round(rect.left) === 200;
+
+- name: 2d.scrollPathIntoView.verticalLR
+ desc: scrollPathIntoView() works in vertical-lr writing mode
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ document.documentElement.style.cssText = 'writing-mode: vertical-lr';
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; left: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ ctx.beginPath();
+ ctx.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView();
+ var rect = canvas.getBoundingClientRect();
+ @assert Math.round(rect.top) === 100;
+ @assert Math.round(rect.left) === -4;
+
+- name: 2d.scrollPathIntoView.verticalRL
+ desc: scrollPathIntoView() works in vertical-rl writing mode
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ document.documentElement.style.cssText = 'writing-mode: vertical-rl';
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; right: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ ctx.beginPath();
+ ctx.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView();
+ var rect = canvas.getBoundingClientRect();
+ var viewportWidth = document.scrollingElement.clientWidth;
+ var canvasWidth = canvas.width;
+ @assert Math.round(rect.top) === 100;
+ @assert Math.round(rect.right) === viewportWidth + (canvasWidth - 4 - 16);
+
+- name: 2d.scrollPathIntoView.path
+ desc: scrollPathIntoView() with path argument works
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ var div = document.createElement('div');
+ div.style.cssText = 'width: 200vw; height: 200vh';
+ document.body.appendChild(div);
+ canvas.style.cssText = 'position: absolute; top: 100px; left: 200px; border: none;';
+ window.scrollTo(0, 0);
+
+ var path = new Path2D();
+ path.rect(4, 8, 16, 32);
+ ctx.scrollPathIntoView(path);
+ var rect = canvas.getBoundingClientRect();
+ @assert Math.round(rect.top) === -8;
+ @assert Math.round(rect.left) === 200;
+
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml
new file mode 100644
index 0000000000..953ab2c555
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml
@@ -0,0 +1,1090 @@
+- name: 2d.shadow.attributes.shadowBlur.initial
+ code: |
+ @assert ctx.shadowBlur === 0;
+
+- name: 2d.shadow.attributes.shadowBlur.valid
+ code: |
+ ctx.shadowBlur = 1;
+ @assert ctx.shadowBlur === 1;
+
+ ctx.shadowBlur = 0.5;
+ @assert ctx.shadowBlur === 0.5;
+
+ ctx.shadowBlur = 1e6;
+ @assert ctx.shadowBlur === 1e6;
+
+ ctx.shadowBlur = 0;
+ @assert ctx.shadowBlur === 0;
+
+- name: 2d.shadow.attributes.shadowBlur.invalid
+ code: |
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -2;
+ @assert ctx.shadowBlur === 1;
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = Infinity;
+ @assert ctx.shadowBlur === 1;
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = -Infinity;
+ @assert ctx.shadowBlur === 1;
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = NaN;
+ @assert ctx.shadowBlur === 1;
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = 'string';
+ @assert ctx.shadowBlur === 1;
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = true;
+ @assert ctx.shadowBlur === 1;
+
+ ctx.shadowBlur = 1;
+ ctx.shadowBlur = false;
+ @assert ctx.shadowBlur === 0;
+
+- name: 2d.shadow.attributes.shadowOffset.initial
+ code: |
+ @assert ctx.shadowOffsetX === 0;
+ @assert ctx.shadowOffsetY === 0;
+
+- name: 2d.shadow.attributes.shadowOffset.valid
+ code: |
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ @assert ctx.shadowOffsetX === 1;
+ @assert ctx.shadowOffsetY === 2;
+
+ ctx.shadowOffsetX = 0.5;
+ ctx.shadowOffsetY = 0.25;
+ @assert ctx.shadowOffsetX === 0.5;
+ @assert ctx.shadowOffsetY === 0.25;
+
+ ctx.shadowOffsetX = -0.5;
+ ctx.shadowOffsetY = -0.25;
+ @assert ctx.shadowOffsetX === -0.5;
+ @assert ctx.shadowOffsetY === -0.25;
+
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ @assert ctx.shadowOffsetX === 0;
+ @assert ctx.shadowOffsetY === 0;
+
+ ctx.shadowOffsetX = 1e6;
+ ctx.shadowOffsetY = 1e6;
+ @assert ctx.shadowOffsetX === 1e6;
+ @assert ctx.shadowOffsetY === 1e6;
+
+- name: 2d.shadow.attributes.shadowOffset.invalid
+ code: |
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = Infinity;
+ ctx.shadowOffsetY = Infinity;
+ @assert ctx.shadowOffsetX === 1;
+ @assert ctx.shadowOffsetY === 2;
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = -Infinity;
+ ctx.shadowOffsetY = -Infinity;
+ @assert ctx.shadowOffsetX === 1;
+ @assert ctx.shadowOffsetY === 2;
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = NaN;
+ ctx.shadowOffsetY = NaN;
+ @assert ctx.shadowOffsetX === 1;
+ @assert ctx.shadowOffsetY === 2;
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = 'string';
+ ctx.shadowOffsetY = 'string';
+ @assert ctx.shadowOffsetX === 1;
+ @assert ctx.shadowOffsetY === 2;
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = true;
+ ctx.shadowOffsetY = true;
+ @assert ctx.shadowOffsetX === 1;
+ @assert ctx.shadowOffsetY === 1;
+
+ ctx.shadowOffsetX = 1;
+ ctx.shadowOffsetY = 2;
+ ctx.shadowOffsetX = false;
+ ctx.shadowOffsetY = false;
+ @assert ctx.shadowOffsetX === 0;
+ @assert ctx.shadowOffsetY === 0;
+
+- name: 2d.shadow.attributes.shadowColor.initial
+ code: |
+ @assert ctx.shadowColor === 'rgba(0, 0, 0, 0)';
+
+- name: 2d.shadow.attributes.shadowColor.valid
+ code: |
+ ctx.shadowColor = 'lime';
+ @assert ctx.shadowColor === '#00ff00';
+
+ ctx.shadowColor = 'RGBA(0,255, 0,0)';
+ @assert ctx.shadowColor === 'rgba(0, 255, 0, 0)';
+
+- name: 2d.shadow.attributes.shadowColor.current.basic
+ desc: currentColor is computed from the canvas element
+ canvasType: ['HtmlCanvas']
+ code: |
+ canvas.style.color = '#0f0';
+ ctx.shadowColor = 'currentColor';
+ @assert ctx.shadowColor === '#00ff00';
+
+- name: 2d.shadow.attributes.shadowColor.current.changed
+ desc: currentColor is computed when the attribute is set, not when it is painted
+ canvasType: ['HtmlCanvas']
+ code: |
+ canvas.style.color = '#0f0';
+ ctx.shadowColor = 'currentColor';
+ canvas.style.color = '#f00';
+ @assert ctx.shadowColor === '#00ff00';
+
+- name: 2d.shadow.attributes.shadowColor.current.removed
+ desc: currentColor is solid black when the canvas element is not in a document
+ canvasType: ['HtmlCanvas']
+ code: |
+ // Try not to let it undetectably incorrectly pick up opaque-black
+ // from other parts of the document:
+ document.documentElement.style.color = '#f00';
+ document.body.style.color = '#f00';
+ canvas.style.color = '#f00';
+
+ canvas.remove();
+ ctx.shadowColor = 'currentColor';
+ @assert ctx.shadowColor === '#000000';
+
+- name: 2d.shadow.attributes.shadowColor.invalid
+ code: |
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'bogus';
+ @assert ctx.shadowColor === '#00ff00';
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = 'red bogus';
+ @assert ctx.shadowColor === '#00ff00';
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = ctx;
+ @assert ctx.shadowColor === '#00ff00';
+
+ ctx.shadowColor = '#00ff00';
+ ctx.shadowColor = undefined;
+ @assert ctx.shadowColor === '#00ff00';
+
+- name: 2d.shadow.enable.off.1
+ desc: Shadows are not drawn when only shadowColor is set
+ code: |
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.enable.off.2
+ desc: Shadows are not drawn when only shadowColor is set
+ code: |
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#f00';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.enable.blur
+ desc: Shadows are drawn if shadowBlur is set
+ code: |
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowBlur = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.enable.x
+ desc: Shadows are drawn if shadowOffsetX is set
+ code: |
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.enable.y
+ desc: Shadows are drawn if shadowOffsetY is set
+ code: |
+ ctx.globalCompositeOperation = 'destination-atop';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 0.1;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.offset.positiveX
+ desc: Shadows can be offset with positive x
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.offset.negativeX
+ desc: Shadows can be offset with negative x
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = -50;
+ ctx.fillRect(50, 0, 50, 50);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.offset.positiveY
+ desc: Shadows can be offset with positive y
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 25;
+ ctx.fillRect(0, 0, 100, 25);
+ @assert pixel 50,12 == 0,255,0,255;
+ @assert pixel 50,37 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.offset.negativeY
+ desc: Shadows can be offset with negative y
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = -25;
+ ctx.fillRect(0, 25, 100, 25);
+ @assert pixel 50,12 == 0,255,0,255;
+ @assert pixel 50,37 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.outside
+ desc: Shadows of shapes outside the visible area can be offset onto the visible
+ area
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.fillRect(-100, 0, 25, 50);
+ ctx.shadowOffsetX = -100;
+ ctx.fillRect(175, 0, 25, 50);
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 100;
+ ctx.fillRect(25, -100, 50, 25);
+ ctx.shadowOffsetY = -100;
+ ctx.fillRect(25, 125, 50, 25);
+ @assert pixel 12,25 == 0,255,0,255;
+ @assert pixel 87,25 == 0,255,0,255;
+ @assert pixel 50,12 == 0,255,0,255;
+ @assert pixel 50,37 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.clip.1
+ desc: Shadows of clipped shapes are still drawn within the clipping region
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(50, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.clip.2
+ desc: Shadows are not drawn outside the clipping region
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.restore();
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.clip.3
+ desc: Shadows of clipped shapes are still drawn within the clipping region
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(0, 0, 50, 50);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 50;
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.stroke.basic
+ desc: Shadows are drawn for strokes
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.moveTo(0, -25);
+ ctx.lineTo(100, -25);
+ ctx.stroke();
+
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.stroke.cap.1
+ desc: Shadows are not drawn for areas outside stroke caps
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'butt';
+ ctx.moveTo(-50, -25);
+ ctx.lineTo(0, -25);
+ ctx.moveTo(100, -25);
+ ctx.lineTo(150, -25);
+ ctx.stroke();
+
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.stroke.cap.2
+ desc: Shadows are drawn for stroke caps
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.beginPath();
+ ctx.lineWidth = 50;
+ ctx.lineCap = 'square';
+ ctx.moveTo(25, -25);
+ ctx.lineTo(75, -25);
+ ctx.stroke();
+
+ @assert pixel 1,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.stroke.join.1
+ desc: Shadows are not drawn for areas outside stroke joins
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'bevel';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.stroke.join.2
+ desc: Shadows are drawn for stroke joins
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100);
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.stroke.join.3
+ desc: Shadows are drawn for stroke joins respecting miter limit
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#f00';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.lineWidth = 200;
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 0.1;
+ ctx.beginPath();
+ ctx.moveTo(-200, -50);
+ ctx.lineTo(-150, -50);
+ ctx.lineTo(-151, -100); // (not an exact right angle, to avoid some other bug in Firefox 3)
+ ctx.stroke();
+
+ @assert pixel 1,1 == 0,255,0,255;
+ @assert pixel 48,48 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,48 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.image.basic
+ desc: Shadows are drawn for images
+ images:
+ - red.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ {{ load_image }}
+ ctx.drawImage(img, 0, -50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+ variants: &load-image-variant-definition
+ _HtmlCanvas:
+ canvasType: ['HtmlCanvas']
+ load_image: var img = document.getElementById('{{ images[0] }}');
+ _OffscreenCanvas:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ test_type: promise
+ load_image: |-
+ var response = await fetch('/images/{{ images[0] }}')
+ var blob = await response.blob();
+ var img = await createImageBitmap(blob);
+
+- name: 2d.shadow.image.transparent.1
+ desc: Shadows are not drawn for transparent images
+ images:
+ - transparent.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ {{ load_image }}
+ ctx.drawImage(img, 0, -50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.image.transparent.2
+ desc: Shadows are not drawn for transparent parts of images
+ images:
+ - redtransparent.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ {{ load_image }}
+ ctx.drawImage(img, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(img, -50, -50);
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.image.alpha
+ desc: Shadows are drawn correctly for partially-transparent images
+ images:
+ - transparent50.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ {{ load_image }}
+ ctx.drawImage(img, 0, -50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.image.section
+ desc: Shadows are not drawn for areas outside image source rectangles
+ images:
+ - redtransparent.png
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#f00';
+ {{ load_image }}
+ ctx.drawImage(img, 50, 0, 50, 50, 0, -50, 50, 50);
+
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.image.scale
+ desc: Shadows are drawn correctly for scaled images
+ images:
+ - redtransparent.png
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ {{ load_image }}
+ ctx.drawImage(img, 0, 0, 100, 50, -10, -50, 240, 50);
+
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.canvas.basic
+ desc: Shadows are drawn for canvases
+ code: |
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+ variants: &create-canvas2-variant-definition
+ _HtmlCanvas:
+ canvasType: ['HtmlCanvas']
+ create_canvas2: |-
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 100;
+ canvas2.height = 50;
+ _OffscreenCanvas:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ create_canvas2: |-
+ var canvas2 = new OffscreenCanvas(100, 50);
+
+- name: 2d.shadow.canvas.transparent.1
+ desc: Shadows are not drawn for transparent canvases
+ code: |
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.drawImage(canvas2, 0, -50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.shadow.canvas.transparent.2
+ desc: Shadows are not drawn for transparent parts of canvases
+ code: |
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 50, 50);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.drawImage(canvas2, 50, -50);
+ ctx.shadowColor = '#f00';
+ ctx.drawImage(canvas2, -50, -50);
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.shadow.canvas.alpha
+ desc: Shadows are drawn correctly for partially-transparent canvases
+ code: |
+ {{ create_canvas2 }}
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'rgba(255, 0, 0, 0.5)';
+ ctx2.fillRect(0, 0, 100, 50);
+
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.drawImage(canvas2, 0, -50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ variants: *create-canvas2-variant-definition
+
+- name: 2d.shadow.pattern.basic
+ desc: Shadows are drawn for fill patterns
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ images:
+ - red.png
+ code: |
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.pattern.transparent.1
+ desc: Shadows are not drawn for transparent fill patterns
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ images:
+ - transparent.png
+ code: |
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.pattern.transparent.2
+ desc: Shadows are not drawn for transparent parts of fill patterns
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ images:
+ - redtransparent.png
+ code: |
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.pattern.alpha
+ desc: Shadows are drawn correctly for partially-transparent fill patterns
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ images:
+ - transparent50.png
+ code: |
+ {{ load_image }}
+ var pattern = ctx.createPattern(img, 'repeat');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ variants: *load-image-variant-definition
+
+- name: 2d.shadow.gradient.basic
+ desc: Shadows are drawn for gradient fills
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ code: |
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(1, '#f00');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#0f0';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.gradient.transparent.1
+ desc: Shadows are not drawn for transparent gradient fills
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ code: |
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetY = 50;
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.gradient.transparent.2
+ desc: Shadows are not drawn for transparent parts of gradient fills
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ code: |
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, '#f00');
+ gradient.addColorStop(0.499, '#f00');
+ gradient.addColorStop(0.5, 'rgba(0,0,0,0)');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, 0, 50, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.gradient.alpha
+ desc: Shadows are drawn correctly for partially-transparent gradient fills
+ # http://bugs.webkit.org/show_bug.cgi?id=15266
+ code: |
+ var gradient = ctx.createLinearGradient(0, 0, 100, 0);
+ gradient.addColorStop(0, 'rgba(255,0,0,0.5)');
+ gradient.addColorStop(1, 'rgba(255,0,0,0.5)');
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#00f';
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.shadow.transform.1
+ desc: Shadows take account of transformations
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.translate(100, 100);
+ ctx.fillRect(-100, -150, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.transform.2
+ desc: Shadow offsets are not affected by transformations
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowOffsetY = 50;
+ ctx.shadowColor = '#0f0';
+ ctx.rotate(Math.PI)
+ ctx.fillRect(-100, 0, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.blur.low
+ desc: Shadows look correct for small blurs
+ manual:
+ code: |
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 25;
+ for (var x = 0; x < 100; ++x) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(x, 0, 1, 50);
+ ctx.clip();
+ ctx.shadowBlur = x;
+ ctx.fillRect(-200, -200, 500, 200);
+ ctx.restore();
+ }
+ expected: |
+ size 100 50
+ import math
+ cr.set_source_rgb(0, 0, 1)
+ cr.rectangle(0, 0, 1, 25)
+ cr.fill()
+ cr.set_source_rgb(1, 1, 0)
+ cr.rectangle(0, 25, 1, 25)
+ cr.fill()
+ for x in range(1, 100):
+ sigma = x/2.0
+ filter = []
+ for i in range(-24, 26):
+ filter.append(math.exp(-i*i / (2*sigma*sigma)) / (math.sqrt(2*math.pi)*sigma))
+ accum = [0]
+ for f in filter:
+ accum.append(accum[-1] + f)
+ for y in range(0, 50):
+ cr.set_source_rgb(accum[y], accum[y], 1-accum[y])
+ cr.rectangle(x, y, 1, 1)
+ cr.fill()
+
+- name: 2d.shadow.blur.high
+ desc: Shadows look correct for large blurs
+ manual:
+ code: |
+ ctx.fillStyle = '#ff0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 100;
+ ctx.fillRect(-200, -200, 200, 400);
+ expected: |
+ size 100 50
+ import math
+ sigma = 100.0/2
+ filter = []
+ for i in range(-200, 100):
+ filter.append(math.exp(-i*i / (2*sigma*sigma)) / (math.sqrt(2*math.pi)*sigma))
+ accum = [0]
+ for f in filter:
+ accum.append(accum[-1] + f)
+ for x in range(0, 100):
+ cr.set_source_rgb(accum[x+200], accum[x+200], 1-accum[x+200])
+ cr.rectangle(x, 0, 1, 50)
+ cr.fill()
+
+- name: 2d.shadow.alpha.1
+ desc: Shadow color alpha components are used
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(255, 0, 0, 0.01)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 ==~ 0,255,0,255 +/- 4;
+ expected: green
+
+- name: 2d.shadow.alpha.2
+ desc: Shadow color alpha components are used
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.5)';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.shadow.alpha.3
+ desc: Shadows are affected by globalAlpha
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.5;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.shadow.alpha.4
+ desc: Shadows with alpha components are correctly affected by globalAlpha
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00'; // (work around broken Firefox globalAlpha caching)
+ ctx.shadowColor = 'rgba(0, 0, 255, 0.707)';
+ ctx.shadowOffsetY = 50;
+ ctx.globalAlpha = 0.707;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.shadow.alpha.5
+ desc: Shadows of shapes with alpha components are drawn correctly
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = 'rgba(64, 0, 0, 0.5)';
+ ctx.shadowColor = '#00f';
+ ctx.shadowOffsetY = 50;
+ ctx.fillRect(0, -50, 100, 50);
+
+ @assert pixel 50,25 ==~ 127,0,127,255;
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0.5, 0, 0.5)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+
+- name: 2d.shadow.composite.1
+ desc: Shadows are drawn using globalCompositeOperation
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowOffsetX = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, 0, 200, 50);
+
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.composite.2
+ desc: Shadows are drawn using globalCompositeOperation
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'xor';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 1;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-10, -10, 120, 70);
+
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
+
+- name: 2d.shadow.composite.3
+ desc: Areas outside shadows are drawn correctly with destination-out
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.shadowColor = '#f00';
+ ctx.shadowBlur = 10;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(200, 0, 100, 50);
+
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 50,25 ==~ 0,255,0,255;
+ expected: green
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml
new file mode 100644
index 0000000000..ca945c2953
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml
@@ -0,0 +1,1680 @@
+- name: 2d.text.font.parse.basic
+ code: |
+ ctx.font = '20px serif';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20PX SERIF';
+ @assert ctx.font === '20px serif'; @moz-todo
+
+- name: 2d.text.font.parse.tiny
+ code: |
+ ctx.font = '1px sans-serif';
+ @assert ctx.font === '1px sans-serif';
+
+- name: 2d.text.font.parse.complex
+ code: |
+ ctx.font = 'small-caps italic 400 12px/2 Unknown Font, sans-serif';
+ @assert ['italic small-caps 12px "Unknown Font", sans-serif', 'italic small-caps 12px Unknown Font, sans-serif'].includes(ctx.font);
+
+- name: 2d.text.font.parse.complex2
+ code: |
+ ctx.font = 'small-caps italic 400 12px/2 "Unknown Font #2", sans-serif';
+ @assert ctx.font === 'italic small-caps 12px "Unknown Font #2", sans-serif';
+
+- name: 2d.text.font.parse.family
+ code: |
+ ctx.font = '20px cursive,fantasy,monospace,sans-serif,serif,UnquotedFont,"QuotedFont\\\\\\","';
+ @assert ctx.font === '20px cursive, fantasy, monospace, sans-serif, serif, UnquotedFont, "QuotedFont\\\\\\","';
+
+ # TODO:
+ # 2d.text.font.parse.size.absolute
+ # xx-small x-small small medium large x-large xx-large
+ # 2d.text.font.parse.size.relative
+ # smaller larger
+ # 2d.text.font.parse.size.length.relative
+ # em ex px
+ # 2d.text.font.parse.size.length.absolute
+ # in cm mm pt pc
+
+- name: 2d.text.font.parse.size.percentage
+ canvas: 'style="font-size: 144px"'
+ canvasType: ['HtmlCanvas']
+ code: |
+ ctx.font = '50% serif';
+ @assert ctx.font === '72px serif'; @moz-todo
+ canvas.setAttribute('style', 'font-size: 100px');
+ @assert ctx.font === '72px serif'; @moz-todo
+
+- name: 2d.text.font.parse.size.percentage.default
+ canvasType: ['HtmlCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.font = '1000% serif';
+ @assert ctx2.font === '100px serif'; @moz-todo
+
+- name: 2d.text.font.parse.system
+ desc: System fonts must be computed to explicit values
+ code: |
+ ctx.font = 'message-box';
+ @assert ctx.font !== 'message-box';
+
+- name: 2d.text.font.parse.invalid
+ code: |
+ ctx.font = '20px serif';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = '';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = 'bogus';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = 'inherit';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = '10px {bogus}';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = '10px initial';
+ @assert ctx.font === '20px serif'; @moz-todo
+
+ ctx.font = '20px serif';
+ ctx.font = '10px default';
+ @assert ctx.font === '20px serif'; @moz-todo
+
+ ctx.font = '20px serif';
+ ctx.font = '10px inherit';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = '10px revert';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x)';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = 'var(--x, 10px serif)';
+ @assert ctx.font === '20px serif';
+
+ ctx.font = '20px serif';
+ ctx.font = '1em serif; background: green; margin: 10px';
+ @assert ctx.font === '20px serif';
+
+- name: 2d.text.font.default
+ code: |
+ @assert ctx.font === '10px sans-serif';
+
+- name: 2d.text.font.relative_size
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = document.createElement('canvas');
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.font = '1em sans-serif';
+ @assert ctx2.font === '10px sans-serif';
+
+- name: 2d.text.font.relative_size
+ canvasType: ['OffscreenCanvas', 'Worker']
+ code: |
+ ctx.font = '1em sans-serif';
+ @assert ctx.font === '10px sans-serif';
+
+- name: 2d.text.font.weight
+ code: |
+ ctx.font = 'italic 400 12px serif';
+ @assert ctx.font === 'italic 12px serif';
+
+ ctx.font = 'italic 300 12px serif';
+ @assert ctx.font === 'italic 300 12px serif';
+
+- name: 2d.text.align.valid
+ code: |
+ ctx.textAlign = 'start';
+ @assert ctx.textAlign === 'start';
+
+ ctx.textAlign = 'end';
+ @assert ctx.textAlign === 'end';
+
+ ctx.textAlign = 'left';
+ @assert ctx.textAlign === 'left';
+
+ ctx.textAlign = 'right';
+ @assert ctx.textAlign === 'right';
+
+ ctx.textAlign = 'center';
+ @assert ctx.textAlign === 'center';
+
+- name: 2d.text.align.invalid
+ code: |
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'bogus';
+ @assert ctx.textAlign === 'start';
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'END';
+ @assert ctx.textAlign === 'start';
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end ';
+ @assert ctx.textAlign === 'start';
+
+ ctx.textAlign = 'start';
+ ctx.textAlign = 'end\0';
+ @assert ctx.textAlign === 'start';
+
+- name: 2d.text.align.default
+ code: |
+ @assert ctx.textAlign === 'start';
+
+- name: 2d.text.baseline.valid
+ code: |
+ ctx.textBaseline = 'top';
+ @assert ctx.textBaseline === 'top';
+
+ ctx.textBaseline = 'hanging';
+ @assert ctx.textBaseline === 'hanging';
+
+ ctx.textBaseline = 'middle';
+ @assert ctx.textBaseline === 'middle';
+
+ ctx.textBaseline = 'alphabetic';
+ @assert ctx.textBaseline === 'alphabetic';
+
+ ctx.textBaseline = 'ideographic';
+ @assert ctx.textBaseline === 'ideographic';
+
+ ctx.textBaseline = 'bottom';
+ @assert ctx.textBaseline === 'bottom';
+
+- name: 2d.text.baseline.invalid
+ code: |
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'bogus';
+ @assert ctx.textBaseline === 'top';
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'MIDDLE';
+ @assert ctx.textBaseline === 'top';
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle ';
+ @assert ctx.textBaseline === 'top';
+
+ ctx.textBaseline = 'top';
+ ctx.textBaseline = 'middle\0';
+ @assert ctx.textBaseline === 'top';
+
+- name: 2d.text.baseline.default
+ code: |
+ @assert ctx.textBaseline === 'alphabetic';
+
+- name: 2d.text.draw.baseline.top
+ desc: textBaseline top is the top of the em square (not the bounding box)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'top';
+ ctx.fillText('CC', 0, 0);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: &load-font-variant-definition
+ _HtmlCanvas:
+ canvasType: ['HtmlCanvas']
+ load_font: |-
+ await document.fonts.ready;
+ _OffscreenCanvas:
+ canvasType: ['OffscreenCanvas', 'Worker']
+ load_font: |-
+ var f = new FontFace("{{ fonts[0] }}", "url('/fonts/{{ fonts[0] }}.ttf')");
+ f.load();
+ {% set root = 'self' if canvas_type == 'worker' else 'document' %}
+ {{ root }}.fonts.add(f);
+ await {{ root }}.fonts.ready;
+
+- name: 2d.text.draw.baseline.bottom
+ desc: textBaseline bottom is the bottom of the em square (not the bounding box)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'bottom';
+ ctx.fillText('CC', 0, 50);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.baseline.middle
+ desc: textBaseline middle is the middle of the em square (not the bounding box)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'middle';
+ ctx.fillText('CC', 0, 25);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.baseline.alphabetic
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'alphabetic';
+ ctx.fillText('CC', 0, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.baseline.ideographic
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'ideographic';
+ ctx.fillText('CC', 0, 31.25);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255; @moz-todo
+ @assert pixel 95,45 ==~ 0,255,0,255; @moz-todo
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.baseline.hanging
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textBaseline = 'hanging';
+ ctx.fillText('CC', 0, 12.5);
+ @assert pixel 5,5 ==~ 0,255,0,255; @moz-todo
+ @assert pixel 95,5 ==~ 0,255,0,255; @moz-todo
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.space.collapse.space
+ desc: Space characters are converted to U+0020, and are NOT collapsed
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', 0, 37.5);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 255,0,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.space.collapse.other
+ desc: Space characters are converted to U+0020, and are NOT collapsed
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dEE', 0, 37.5);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 255,0,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.space.collapse.start
+ desc: Space characters at the start of a line are NOT collapsed
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText(' EE', 0, 37.5);
+ @assert pixel 25,25 ==~ 255,0,0,255; @moz-todo
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.space.collapse.end
+ desc: Space characters at the end of a line are NOT collapsed
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('EE ', 100, 37.5);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 255,0,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.width.space
+ desc: Space characters are converted to U+0020 and NOT collapsed
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ @assert ctx.measureText('A B').width === 150;
+ @assert ctx.measureText('A B').width === 200;
+ @assert ctx.measureText('A \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dB').width === 650;
+ @assert ctx.measureText('A \x0b B').width >= 200;
+
+ @assert ctx.measureText(' AB').width === 150;
+ @assert ctx.measureText('AB ').width === 150;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.drawing.style.measure.rtl.text
+ desc: Measurement should follow canvas direction instead text direction
+ code: |
+ metrics = ctx.measureText('اَلْعَرَبÙيَّةÙ');
+ @assert metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight;
+
+ metrics = ctx.measureText('hello');
+ @assert metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight;
+
+- name: 2d.text.drawing.style.measure.textAlign
+ desc: Measurement should be related to textAlignment
+ code: |
+ ctx.textAlign = "right";
+ metrics = ctx.measureText('hello');
+ @assert metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight;
+
+ ctx.textAlign = "left"
+ metrics = ctx.measureText('hello');
+ @assert metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight;
+
+- name: 2d.text.drawing.style.measure.direction
+ desc: Measurement should follow text direction
+ code: |
+ ctx.direction = "ltr";
+ metrics = ctx.measureText('hello');
+ @assert metrics.actualBoundingBoxLeft < metrics.actualBoundingBoxRight;
+
+ ctx.direction = "rtl";
+ metrics = ctx.measureText('hello');
+ @assert metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight;
+
+- name: 2d.text.draw.fill.basic
+ desc: fillText draws filled text
+ manual:
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35);
+ expected: &passfill |
+ size 100 50
+ cr.set_source_rgb(0, 0, 0)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ cr.set_source_rgb(0, 1, 0)
+ cr.select_font_face("Arial")
+ cr.set_font_size(35)
+ cr.translate(5, 35)
+ cr.text_path("PASS")
+ cr.fill()
+
+- name: 2d.text.draw.fill.unaffected
+ desc: fillText does not start a new path or subpath
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 5,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.text.draw.fill.rtl
+ desc: fillText respects Right-To-Left Override characters
+ manual:
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.strokeStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('\u202eFAIL \xa0 \xa0 SSAP', 5, 35);
+ expected: *passfill
+
+- name: 2d.text.draw.fill.maxWidth.large
+ desc: fillText handles maxWidth correctly
+ manual:
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('PASS', 5, 35, 200);
+ expected: *passfill
+
+- name: 2d.text.draw.fill.maxWidth.small
+ desc: fillText handles maxWidth correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', -100, 35, 90);
+ _assertGreen(ctx, 100, 50);
+ expected: green
+
+- name: 2d.text.draw.fill.maxWidth.zero
+ desc: fillText handles maxWidth correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, 0);
+ _assertGreen(ctx, 100, 50);
+ expected: green
+
+- name: 2d.text.draw.fill.maxWidth.negative
+ desc: fillText handles maxWidth correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, -1);
+ _assertGreen(ctx, 100, 50);
+ expected: green
+
+- name: 2d.text.draw.fill.maxWidth.NaN
+ desc: fillText handles maxWidth correctly
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.font = '35px Arial, sans-serif';
+ ctx.fillText('fail fail fail fail fail', 5, 35, NaN);
+ _assertGreen(ctx, 100, 50);
+ expected: green
+
+- name: 2d.text.draw.stroke.basic
+ desc: strokeText draws stroked text
+ manual:
+ code: |
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.strokeStyle = '#0f0';
+ ctx.fillStyle = '#f00';
+ ctx.lineWidth = 1;
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeText('PASS', 5, 35);
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0, 0, 0)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ cr.set_source_rgb(0, 1, 0)
+ cr.select_font_face("Arial")
+ cr.set_font_size(35)
+ cr.set_line_width(1)
+ cr.translate(5, 35)
+ cr.text_path("PASS")
+ cr.stroke()
+
+- name: 2d.text.draw.stroke.unaffected
+ desc: strokeText does not start a new path or subpath
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.moveTo(0, 0);
+ ctx.lineTo(100, 0);
+
+ ctx.font = '35px Arial, sans-serif';
+ ctx.strokeStyle = '#f00';
+ ctx.strokeText('FAIL', 5, 35);
+
+ ctx.lineTo(100, 50);
+ ctx.lineTo(0, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 5,45 == 0,255,0,255;
+ expected: green
+
+- name: 2d.text.draw.kern.consistent
+ desc: Stroked and filled text should have exactly the same kerning so it overlaps
+ manual:
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.strokeStyle = '#0f0';
+ ctx.lineWidth = 3;
+ ctx.font = '20px Arial, sans-serif';
+ ctx.fillText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.fillText('ToToToToToToTo', -50, 45);
+ ctx.strokeText('VAVAVAVAVAVAVA', -50, 25);
+ ctx.strokeText('ToToToToToToTo', -50, 45);
+ expected: green
+
+# CanvasTest is:
+# A = (0, 0) to (1em, 0.75em) (above baseline)
+# B = (0, 0) to (1em, -0.25em) (below baseline)
+# C = (0, -0.25em) to (1em, 0.75em) (the em square) plus some Xs above and below
+# D = (0, -0.25em) to (1em, 0.75em) (the em square) plus some Xs left and right
+# E = (0, -0.25em) to (1em, 0.75em) (the em square)
+# space = empty, 1em wide
+#
+# At 50px, "E" will fill the canvas vertically
+# At 67px, "A" will fill the canvas vertically
+#
+# Ideographic baseline is 0.125em above alphabetic
+# Mathematical baseline is 0.375em above alphabetic
+# Hanging baseline is 0.500em above alphabetic
+
+- name: 2d.text.draw.fill.maxWidth.fontface
+ desc: fillText works on @font-face fonts
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillText('EEEE', -50, 37.5, 40);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.fill.maxWidth.bound
+ desc: fillText handles maxWidth based on line size, not bounding box size
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('DD', 0, 37.5, 100);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.fontface
+ fonts:
+ - CanvasTest
+ test_type: promise
+ code: |
+ {{ load_font }}
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.fontface.repeat
+ desc: Draw with the font immediately, then wait a bit until and draw again. (This
+ crashes some version of WebKit.)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ font_unused_in_dom: true
+ code: |
+ {{ load_font }}
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+
+ await new Promise(resolve => t.step_timeout(resolve, 500));
+ ctx.fillText('AA', 0, 50);
+ _assertPixelApprox(canvas, 5,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
+ _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.fontface.notinpage
+ desc: '@font-face fonts should work even if they are not used in the page'
+ test_type: promise
+ fonts:
+ - CanvasTest
+ font_unused_in_dom: true
+ code: |
+ {{ load_font }}
+ ctx.font = '67px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('AA', 0, 50);
+ @assert pixel 5,5 ==~ 0,255,0,255; @moz-todo
+ @assert pixel 95,5 ==~ 0,255,0,255; @moz-todo
+ @assert pixel 25,25 ==~ 0,255,0,255; @moz-todo
+ @assert pixel 75,25 ==~ 0,255,0,255; @moz-todo
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.align.left
+ desc: textAlign left is the left of the first em square (not the bounding box)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'left';
+ ctx.fillText('DD', 0, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.align.right
+ desc: textAlign right is the right of the last em square (not the bounding box)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'right';
+ ctx.fillText('DD', 100, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.align.start.ltr
+ desc: textAlign start with ltr is the left edge
+ canvas: dir="ltr"
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ {% if canvas_type != 'htmlcanvas' %}
+ ctx.direction = 'ltr';
+ {% endif %}
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 0, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.align.start.rtl
+ desc: textAlign start with rtl is the right edge
+ canvas: dir="rtl"
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ {% if canvas_type != 'htmlcanvas' %}
+ ctx.direction = 'rtl';
+ {% endif %}
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'start';
+ ctx.fillText('DD', 100, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.align.end.ltr
+ desc: textAlign end with ltr is the right edge
+ test_type: promise
+ canvas: dir="ltr"
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ {% if canvas_type != 'htmlcanvas' %}
+ ctx.direction = 'ltr';
+ {% endif %}
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 100, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.align.end.rtl
+ desc: textAlign end with rtl is the left edge
+ canvas: dir="rtl"
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ {% if canvas_type != 'htmlcanvas' %}
+ ctx.direction = 'rtl';
+ {% endif %}
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'end';
+ ctx.fillText('DD', 0, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.align.center
+ desc: textAlign center is the center of the em squares (not the bounding box)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.textAlign = 'center';
+ ctx.fillText('DD', 50, 37.5);
+ @assert pixel 5,5 ==~ 0,255,0,255;
+ @assert pixel 95,5 ==~ 0,255,0,255;
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ @assert pixel 5,45 ==~ 0,255,0,255;
+ @assert pixel 95,45 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+
+- name: 2d.text.draw.space.basic
+ desc: U+0020 is rendered the correct size (1em wide)
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E EE', -100, 37.5);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.draw.space.collapse.nonspace
+ desc: Non-space characters are not converted to U+0020 and collapsed
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillText('E\x0b EE', -150, 37.5);
+ @assert pixel 25,25 ==~ 0,255,0,255;
+ @assert pixel 75,25 ==~ 0,255,0,255;
+ expected: green
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.width.basic
+ desc: The width of character is same as font used
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ @assert ctx.measureText('A').width === 50;
+ @assert ctx.measureText('AA').width === 100;
+ @assert ctx.measureText('ABCD').width === 200;
+
+ ctx.font = '100px CanvasTest';
+ @assert ctx.measureText('A').width === 100;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.width.empty
+ desc: The empty string has zero width
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ @assert ctx.measureText("").width === 0;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.advances
+ desc: Testing width advances
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ // Some platforms may return '-0'.
+ @assert Math.abs(ctx.measureText('Hello').advances[0]) === 0;
+ // Different platforms may render text slightly different.
+ @assert ctx.measureText('Hello').advances[1] >= 36;
+ @assert ctx.measureText('Hello').advances[2] >= 58;
+ @assert ctx.measureText('Hello').advances[3] >= 70;
+ @assert ctx.measureText('Hello').advances[4] >= 80;
+
+ var tm = ctx.measureText('Hello');
+ @assert ctx.measureText('Hello').advances[0] === tm.advances[0];
+ @assert ctx.measureText('Hello').advances[1] === tm.advances[1];
+ @assert ctx.measureText('Hello').advances[2] === tm.advances[2];
+ @assert ctx.measureText('Hello').advances[3] === tm.advances[3];
+ @assert ctx.measureText('Hello').advances[4] === tm.advances[4];
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.actualBoundingBox
+ desc: Testing actualBoundingBox
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ ctx.baseline = 'alphabetic'
+ // Different platforms may render text slightly different.
+ // Values that are nominally expected to be zero might actually vary by a
+ // pixel or so if the UA accounts for antialiasing at glyph edges, so we
+ // allow a slight deviation.
+ @assert Math.abs(ctx.measureText('A').actualBoundingBoxLeft) <= 1;
+ @assert ctx.measureText('A').actualBoundingBoxRight >= 50;
+ @assert ctx.measureText('A').actualBoundingBoxAscent >= 35;
+ @assert Math.abs(ctx.measureText('A').actualBoundingBoxDescent) <= 1;
+
+ @assert ctx.measureText('D').actualBoundingBoxLeft >= 48;
+ @assert ctx.measureText('D').actualBoundingBoxLeft <= 52;
+ @assert ctx.measureText('D').actualBoundingBoxRight >= 75;
+ @assert ctx.measureText('D').actualBoundingBoxRight <= 80;
+ @assert ctx.measureText('D').actualBoundingBoxAscent >= 35;
+ @assert ctx.measureText('D').actualBoundingBoxAscent <= 40;
+ @assert ctx.measureText('D').actualBoundingBoxDescent >= 12;
+ @assert ctx.measureText('D').actualBoundingBoxDescent <= 15;
+
+ @assert Math.abs(ctx.measureText('ABCD').actualBoundingBoxLeft) <= 1;
+ @assert ctx.measureText('ABCD').actualBoundingBoxRight >= 200;
+ @assert ctx.measureText('ABCD').actualBoundingBoxAscent >= 85;
+ @assert ctx.measureText('ABCD').actualBoundingBoxDescent >= 37;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.fontBoundingBox
+ desc: Testing fontBoundingBox measurements
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert ctx.measureText('A').fontBoundingBoxAscent === 30;
+ @assert ctx.measureText('A').fontBoundingBoxDescent === 10;
+
+ @assert ctx.measureText('ABCD').fontBoundingBoxAscent === 30;
+ @assert ctx.measureText('ABCD').fontBoundingBoxDescent === 10;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.fontBoundingBox.ahem
+ desc: Testing fontBoundingBox for font ahem
+ test_type: promise
+ fonts:
+ - Ahem
+ code: |
+ {{ load_font }}
+ ctx.font = '50px Ahem';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert ctx.measureText('A').fontBoundingBoxAscent === 40;
+ @assert ctx.measureText('A').fontBoundingBoxDescent === 10;
+ @assert ctx.measureText('ABCD').fontBoundingBoxAscent === 40;
+ @assert ctx.measureText('ABCD').fontBoundingBoxDescent === 10;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.fontBoundingBox-reduced-ascent
+ desc: Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric
+ test_type: promise
+ fonts:
+ - CanvasTest-ascent256
+ code: |
+ {{ load_font }}
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert ctx.measureText('A').fontBoundingBoxAscent === 10;
+ @assert ctx.measureText('A').fontBoundingBoxDescent === 10;
+
+ @assert ctx.measureText('ABCD').fontBoundingBoxAscent === 10;
+ @assert ctx.measureText('ABCD').fontBoundingBoxDescent === 10;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.fontBoundingBox-zero-descent
+ desc: Testing fontBoundingBox for OffscreenCanvas with zero descent metric
+ test_type: promise
+ fonts:
+ - CanvasTest-descent0
+ code: |
+ {{ load_font }}
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert ctx.measureText('A').fontBoundingBoxAscent === 30;
+ @assert ctx.measureText('A').fontBoundingBoxDescent === 0;
+
+ @assert ctx.measureText('ABCD').fontBoundingBoxAscent === 30;
+ @assert ctx.measureText('ABCD').fontBoundingBoxDescent === 0;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.emHeights
+ desc: Testing emHeights
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '40px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert ctx.measureText('A').emHeightAscent === 30;
+ @assert ctx.measureText('A').emHeightDescent === 10;
+ @assert ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent === 40;
+
+ @assert ctx.measureText('ABCD').emHeightAscent === 30;
+ @assert ctx.measureText('ABCD').emHeightDescent === 10;
+ @assert ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent === 40;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.emHeights-low-ascent
+ desc: Testing emHeights with reduced ascent metric
+ test_type: promise
+ fonts:
+ - CanvasTest-ascent256
+ code: |
+ {{ load_font }}
+ ctx.font = '40px CanvasTest-ascent256';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert ctx.measureText('A').emHeightAscent === 20;
+ @assert ctx.measureText('A').emHeightDescent === 20;
+ @assert ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent === 40;
+
+ @assert ctx.measureText('ABCD').emHeightAscent === 20;
+ @assert ctx.measureText('ABCD').emHeightDescent === 20;
+ @assert ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent === 40;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.emHeights-zero-descent
+ desc: Testing emHeights with zero descent metric
+ test_type: promise
+ fonts:
+ - CanvasTest-descent0
+ code: |
+ {{ load_font }}
+ ctx.font = '40px CanvasTest-descent0';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert ctx.measureText('A').emHeightAscent === 40;
+ @assert ctx.measureText('A').emHeightDescent === 0;
+ @assert ctx.measureText('A').emHeightDescent + ctx.measureText('A').emHeightAscent === 40;
+
+ @assert ctx.measureText('ABCD').emHeightAscent === 40;
+ @assert ctx.measureText('ABCD').emHeightDescent === 0;
+ @assert ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent === 40;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.measure.baselines
+ desc: Testing baselines
+ test_type: promise
+ fonts:
+ - CanvasTest
+ code: |
+ {{ load_font }}
+ ctx.font = '50px CanvasTest';
+ ctx.direction = 'ltr';
+ ctx.align = 'left'
+ @assert Math.abs(ctx.measureText('A').alphabeticBaseline) === 0;
+ @assert ctx.measureText('A').ideographicBaseline === 6.25;
+ @assert ctx.measureText('A').hangingBaseline === 25;
+
+ @assert Math.abs(ctx.measureText('ABCD').alphabeticBaseline) === 0;
+ @assert ctx.measureText('ABCD').ideographicBaseline === 6.25;
+ @assert ctx.measureText('ABCD').hangingBaseline === 25;
+ variants: *load-font-variant-definition
+
+- name: 2d.text.drawing.style.absolute.spacing
+ desc: Testing letter spacing and word spacing with absolute length
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+
+ ctx.letterSpacing = '3px';
+ @assert ctx.letterSpacing === '3px';
+ @assert ctx.wordSpacing === '0px';
+
+ ctx.wordSpacing = '5px';
+ @assert ctx.letterSpacing === '3px';
+ @assert ctx.wordSpacing === '5px';
+
+ ctx.letterSpacing = '-1px';
+ ctx.wordSpacing = '-1px';
+ @assert ctx.letterSpacing === '-1px';
+ @assert ctx.wordSpacing === '-1px';
+
+ ctx.letterSpacing = '1PX';
+ ctx.wordSpacing = '10PX';
+ @assert ctx.letterSpacing === '1px';
+ @assert ctx.wordSpacing === '10px';
+
+- name: 2d.text.drawing.style.font-relative.spacing
+ desc: Testing letter spacing and word spacing with font-relative length
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+
+ ctx.letterSpacing = '1EX';
+ ctx.wordSpacing = '1EM';
+ @assert ctx.letterSpacing === '1ex';
+ @assert ctx.wordSpacing === '1em';
+
+ ctx.letterSpacing = '1ch';
+ ctx.wordSpacing = '1ic';
+ @assert ctx.letterSpacing === '1ch';
+ @assert ctx.wordSpacing === '1ic';
+
+- name: 2d.text.drawing.style.nonfinite.spacing
+ desc: Testing letter spacing and word spacing with nonfinite inputs
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ @assert ctx.wordSpacing === '0px';
+ @assert ctx.letterSpacing === '0px';
+ }
+ @nonfinite test_word_spacing(<0 NaN Infinity -Infinity>);
+
+- name: 2d.text.drawing.style.invalid.spacing
+ desc: Testing letter spacing and word spacing with invalid units
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+
+ function test_word_spacing(value) {
+ ctx.wordSpacing = value;
+ ctx.letterSpacing = value;
+ @assert ctx.wordSpacing === '0px';
+ @assert ctx.letterSpacing === '0px';
+ }
+ @nonfinite test_word_spacing(< '0s' '1min' '1deg' '1pp' 'initial' 'inherit' 'normal' 'none'>);
+
+- name: 2d.text.drawing.style.letterSpacing.measure
+ desc: Testing letter spacing with different length units
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+ var width_normal = ctx.measureText('Hello World').width;
+
+ function test_letter_spacing(value, difference_spacing, epsilon) {
+ ctx.letterSpacing = value;
+ @assert ctx.letterSpacing === value;
+ @assert ctx.wordSpacing === '0px';
+ width_with_letter_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_letter_spacing, width_normal + difference_spacing, epsilon, "letter spacing doesn't work.");
+ }
+
+ // The first value is the letter Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 11 letters
+ // in 'hello world', so the length difference is always letterSpacing * 11.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 33, 0.1],
+ ['5px', 55, 0.1],
+ ['-2px', -22, 0.1],
+ ['1em', 110, 0.1],
+ ['-0.1em', -11, 0.1],
+ ['1in', 1056, 0.1],
+ ['-0.1cm', -41.65, 0.2],
+ ['-0.6mm', -24,95, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_letter_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+
+- name: 2d.text.drawing.style.wordSpacing.measure
+ desc: Testing word spacing with different length units
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ function test_word_spacing(value, difference_spacing, epsilon) {
+ ctx.wordSpacing = value;
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === value;
+ width_with_word_spacing = ctx.measureText('Hello World, again').width;
+ assert_approx_equals(width_with_word_spacing, width_normal + difference_spacing, epsilon, "word spacing doesn't work.");
+ }
+
+ // The first value is the word Spacing to be set, the second value the
+ // change in length of string 'Hello World', note that there are 2 words
+ // in 'Hello World, again', so the length difference is always wordSpacing * 2.
+ // and the third value is the acceptable differencee for the length change,
+ // note that unit such as 1cm/1mm doesn't map to an exact pixel value.
+ test_cases = [['3px', 6, 0.1],
+ ['5px', 10, 0.1],
+ ['-2px', -4, 0.1],
+ ['1em', 20, 0.1],
+ ['-0.5em', -10, 0.1],
+ ['1in', 192, 0.1],
+ ['-0.1cm', -7.57, 0.2],
+ ['-0.6mm', -4.54, 0.2]]
+
+ for (const test_case of test_cases) {
+ test_word_spacing(test_case[0], test_case[1], test_case[2]);
+ }
+
+- name: 2d.text.drawing.style.letterSpacing.change.font
+ desc: Set letter spacing and word spacing to font dependent value and verify it works after font change.
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+ // Get the width for 'Hello World' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World').width;
+
+ ctx.letterSpacing = '1em';
+ @assert ctx.letterSpacing === '1em';
+ // 1em = 10px. Add 10px after each letter in "Hello World",
+ // makes it 110px longer.
+ var width_with_spacing = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 110, 0.1, "letter-spacing error");
+
+ // Changing font to 20px. Without resetting the spacing, 1em letterSpacing
+ // is now 20px, so it's suppose to be 220px longer without any letterSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World').width;
+ // Now calculate the reference spacing for "Hello World" with no spacing.
+ ctx.letterSpacing = '0em';
+ width_normal = ctx.measureText('Hello World').width;
+ assert_approx_equals(width_with_spacing, width_normal + 220, 0.1, "letter-spacing error after font change");
+
+- name: 2d.text.drawing.style.wordSpacing.change.font
+ desc: Set word spacing and word spacing to font dependent value and verify it works after font change.
+ code: |
+ @assert ctx.letterSpacing === '0px';
+ @assert ctx.wordSpacing === '0px';
+ // Get the width for 'Hello World, again' at default size, 10px.
+ var width_normal = ctx.measureText('Hello World, again').width;
+
+ ctx.wordSpacing = '1em';
+ @assert ctx.wordSpacing === '1em';
+ // 1em = 10px. Add 10px after each word in "Hello World, again",
+ // makes it 20px longer.
+ var width_with_spacing = ctx.measureText('Hello World, again').width;
+ @assert width_with_spacing === width_normal + 20;
+
+ // Changing font to 20px. Without resetting the spacing, 1em wordSpacing
+ // is now 20px, so it's suppose to be 40px longer without any wordSpacing set.
+ ctx.font = '20px serif';
+ width_with_spacing = ctx.measureText('Hello World, again').width;
+ // Now calculate the reference spacing for "Hello World, again" with no spacing.
+ ctx.wordSpacing = '0em';
+ width_normal = ctx.measureText('Hello World, again').width;
+ @assert width_with_spacing === width_normal + 40;
+
+- name: 2d.text.drawing.style.fontKerning
+ desc: Testing basic functionalities of fontKerning for canvas
+ code: |
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "normal";
+ @assert ctx.fontKerning === "normal";
+ width_normal = ctx.measureText("TAWATAVA").width;
+ ctx.fontKerning = "none";
+ @assert ctx.fontKerning === "none";
+ width_none = ctx.measureText("TAWATAVA").width;
+ @assert width_normal < width_none;
+
+- name: 2d.text.drawing.style.fontKerning.with.uppercase
+ desc: Testing basic functionalities of fontKerning for canvas
+ code: |
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "Normal";
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "normal";
+ @assert ctx.fontKerning === "normal";
+ ctx.fontKerning = "Auto";
+ @assert ctx.fontKerning === "normal";
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "noRmal";
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NoRMal";
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NORMAL";
+ @assert ctx.fontKerning === "auto";
+
+ ctx.fontKerning = "None";
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "none";
+ @assert ctx.fontKerning === "none";
+ ctx.fontKerning = "Auto";
+ @assert ctx.fontKerning === "none";
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nOne";
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "nonE";
+ @assert ctx.fontKerning === "auto";
+ ctx.fontKerning = "auto";
+ ctx.fontKerning = "NONE";
+ @assert ctx.fontKerning === "auto";
+
+- name: 2d.text.drawing.style.fontVariant.settings
+ desc: Testing basic functionalities of fontVariant for canvas
+ code: |
+ // Setting fontVariantCaps with lower cases
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "normal";
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "small-caps";
+ @assert ctx.fontVariantCaps === "small-caps";
+
+ ctx.fontVariantCaps = "all-small-caps";
+ @assert ctx.fontVariantCaps === "all-small-caps";
+
+ ctx.fontVariantCaps = "petite-caps";
+ @assert ctx.fontVariantCaps === "petite-caps";
+
+ ctx.fontVariantCaps = "all-petite-caps";
+ @assert ctx.fontVariantCaps === "all-petite-caps";
+
+ ctx.fontVariantCaps = "unicase";
+ @assert ctx.fontVariantCaps === "unicase";
+
+ ctx.fontVariantCaps = "titling-caps";
+ @assert ctx.fontVariantCaps === "titling-caps";
+
+ // Setting fontVariantCaps with mixed-case values is not valid
+ ctx.fontVariantCaps = "nORmal";
+ @assert ctx.fontVariantCaps === "titling-caps";
+
+ ctx.fontVariantCaps = "normal";
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "smaLL-caps";
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "all-small-CAPS";
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "pEtitE-caps";
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "All-Petite-Caps";
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "uNIcase";
+ @assert ctx.fontVariantCaps === "normal";
+
+ ctx.fontVariantCaps = "titling-CAPS";
+ @assert ctx.fontVariantCaps === "normal";
+
+ // Setting fontVariantCaps with non-existing font variant.
+ ctx.fontVariantCaps = "titling-caps";
+ ctx.fontVariantCaps = "abcd";
+ @assert ctx.fontVariantCaps === "titling-caps";
+
+- name: 2d.text.drawing.style.textRendering.settings
+ desc: Testing basic functionalities of textRendering in Canvas
+ code: |
+ // Setting textRendering with correct case.
+ @assert ctx.textRendering === "auto";
+
+ ctx.textRendering = "optimizeSpeed";
+ @assert ctx.textRendering === "optimizeSpeed";
+
+ ctx.textRendering = "optimizeLegibility";
+ @assert ctx.textRendering === "optimizeLegibility";
+
+ ctx.textRendering = "geometricPrecision";
+ @assert ctx.textRendering === "geometricPrecision";
+
+ ctx.textRendering = "auto";
+ @assert ctx.textRendering === "auto";
+
+ // Setting textRendering with incorrect case is ignored.
+ ctx.textRendering = "OPtimizeSpeed";
+ @assert ctx.textRendering === "auto";
+
+ ctx.textRendering = "OPtimizELEgibility";
+ @assert ctx.textRendering === "auto";
+
+ ctx.textRendering = "GeometricPrecision";
+ @assert ctx.textRendering === "auto";
+
+ ctx.textRendering = "optimizespeed";
+ @assert ctx.textRendering === "auto";
+
+ ctx.textRendering = "optimizelegibility";
+ @assert ctx.textRendering === "auto";
+
+ ctx.textRendering = "geometricprecision";
+ @assert ctx.textRendering === "auto";
+
+ ctx.textRendering = "optimizeLegibility";
+ @assert ctx.textRendering === "optimizeLegibility";
+
+ ctx.textRendering = "AUTO";
+ @assert ctx.textRendering === "optimizeLegibility";
+
+ ctx.textRendering = "Auto";
+ @assert ctx.textRendering === "optimizeLegibility";
+
+ // Setting textRendering with non-existing font variant.
+ ctx.textRendering = "abcd";
+ @assert ctx.textRendering === "optimizeLegibility";
+
+ ctx.textRendering = "normal";
+ @assert ctx.textRendering === "optimizeLegibility";
+
+ ctx.textRendering = "";
+ @assert ctx.textRendering === "optimizeLegibility";
+
+ ctx.textRendering = "auto";
+ @assert ctx.textRendering === "auto";
+
+- name: 2d.text.drawing.style.fontStretch.settings
+ desc: Testing value setting of fontStretch in Canvas
+ code: |
+ // Setting textRendering with lower cases
+ ctx.fontStretch = "ultra-condensed";
+ @assert ctx.fontStretch === "ultra-condensed";
+
+ ctx.fontStretch = "extra-condensed";
+ @assert ctx.fontStretch === "extra-condensed";
+
+ ctx.fontStretch = "condensed";
+ @assert ctx.fontStretch === "condensed";
+
+ ctx.fontStretch = "semi-condensed";
+ @assert ctx.fontStretch === "semi-condensed";
+
+ ctx.fontStretch = "normal";
+ @assert ctx.fontStretch === "normal";
+
+ ctx.fontStretch = "semi-expanded";
+ @assert ctx.fontStretch === "semi-expanded";
+
+ ctx.fontStretch = "expanded";
+ @assert ctx.fontStretch === "expanded";
+
+ ctx.fontStretch = "extra-expanded";
+ @assert ctx.fontStretch === "extra-expanded";
+
+ ctx.fontStretch = "ultra-expanded";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ // Setting fontStretch with lower cases and upper cases word,
+ // these values should be ignored.
+ ctx.fontStretch = "ulTra-condensed";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "Extra-condensed";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "cOndensed";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "Semi-Condensed";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "normaL";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "semi-Expanded";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "Expanded";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "eXtra-expanded";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+ ctx.fontStretch = "abcd";
+ @assert ctx.fontStretch === "ultra-expanded";
+
+- name: 2d.text.fontVariantCaps1
+ desc: Testing small caps setting in fontVariant
+ code: |
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "small-caps";
+ // This should render the same as font = "small-caps 32px serif".
+ ctx.fillText("Hello World", 20, 100);
+ reference: |
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("Hello World", 20, 100);
+
+- name: 2d.text.fontVariantCaps2
+ desc: Testing small caps setting in fontVariant
+ code: |
+ ctx.font = "small-caps 32px serif";
+ // "mismatch" test, to verify that small-caps does change the rendering.
+ smallCaps_len = ctx.measureText("Hello World").width;
+
+ ctx.font = "32px serif";
+ normalCaps_len = ctx.measureText("Hello World").width;
+ @assert smallCaps_len != normalCaps_len;
+
+- name: 2d.text.fontVariantCaps3
+ desc: Testing small caps setting in fontVariant
+ code: |
+ ctx.font = "32px serif";
+ ctx.fontVariantCaps = "all-small-caps";
+ // This should render the same as using font = "small-caps 32px serif"
+ // with all the underlying text in lowercase.
+ ctx.fillText("Hello World", 20, 100);
+ reference: |
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("hello world", 20, 100);
+
+- name: 2d.text.fontVariantCaps4
+ desc: Testing small caps setting in fontVariant
+ code: |
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps overrides the small-caps setting from the font attribute
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.fillText("Hello World", 20, 100);
+ reference: |
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("hello world", 20, 100);
+
+- name: 2d.text.fontVariantCaps5
+ desc: Testing small caps setting in fontVariant
+ code: |
+ ctx.font = "small-caps 32px serif";
+ // fontVariantCaps 'normal' does not override the setting from the font attribute.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "normal";
+ ctx.fillText("Hello World", 20, 100);
+ reference: |
+ ctx.font = "small-caps 32px serif";
+ ctx.fillText("Hello World", 20, 100);
+
+- name: 2d.text.fontVariantCaps6
+ desc: Testing small caps setting in fontVariant
+ code: |
+ // fontVariantCaps is reset when the font attribute is set.
+ // (spec unclear, cf. https://github.com/whatwg/html/issues/8103)
+ ctx.fontVariantCaps = "all-small-caps";
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+ reference: |
+ ctx.font = "32px serif";
+ ctx.fillText("Hello World", 20, 100);
+
+- name: 2d.text.setFont.mathFont
+ desc: crbug.com/1212190, make sure offscreencanvas doesn't crash with Math Font
+ code: |
+ ctx.font = "math serif";
+
+# TODO: shadows, alpha, composite, clip
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/the-canvas-state.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/the-canvas-state.yaml
new file mode 100644
index 0000000000..0452086154
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/the-canvas-state.yaml
@@ -0,0 +1,89 @@
+- name: 2d.state.saverestore.transformation
+ desc: save()/restore() affects the current transformation matrix
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.translate(200, 0);
+ ctx.restore();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(-200, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.state.saverestore.clip
+ desc: save()/restore() affects the clipping path
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 1, 1);
+ ctx.clip();
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.state.saverestore.path
+ desc: save()/restore() does not affect the current path
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.rect(0, 0, 100, 50);
+ ctx.restore();
+ ctx.fillStyle = '#0f0';
+ ctx.fill();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.state.saverestore.bitmap
+ desc: save()/restore() does not affect the current bitmap
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.save();
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.state.saverestore.stack
+ desc: save()/restore() can be nested as a stack
+ code: |
+ ctx.lineWidth = 1;
+ ctx.save();
+ ctx.lineWidth = 2;
+ ctx.save();
+ ctx.lineWidth = 3;
+ @assert ctx.lineWidth === 3;
+ ctx.restore();
+ @assert ctx.lineWidth === 2;
+ ctx.restore();
+ @assert ctx.lineWidth === 1;
+
+- name: 2d.state.saverestore.stackdepth
+ desc: save()/restore() stack depth is not unreasonably limited
+ code: |
+ var limit = 512;
+ for (var i = 1; i < limit; ++i)
+ {
+ ctx.save();
+ ctx.lineWidth = i;
+ }
+ for (var i = limit-1; i > 0; --i)
+ {
+ @assert ctx.lineWidth === i;
+ ctx.restore();
+ }
+
+- name: 2d.state.saverestore.underflow
+ desc: restore() with an empty stack has no effect
+ code: |
+ for (var i = 0; i < 16; ++i)
+ ctx.restore();
+ ctx.lineWidth = 0.5;
+ ctx.restore();
+ @assert ctx.lineWidth === 0.5;
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/transformations.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/transformations.yaml
new file mode 100644
index 0000000000..0d2265be7a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/transformations.yaml
@@ -0,0 +1,356 @@
+- name: 2d.transformation.order
+ desc: Transformations are applied in the right order
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 1);
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -50, 50, 50);
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.transformation.scale.basic
+ desc: scale() works
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(2, 4);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 12.5);
+ @assert pixel 90,40 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.scale.zero
+ desc: scale() with a scale factor of zero works
+ code: |
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.translate(50, 0);
+ ctx.scale(0, 1);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.translate(0, 25);
+ ctx.scale(1, 0);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.restore();
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.scale.negative
+ desc: scale() with negative scale factors works
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.save();
+ ctx.scale(-1, 1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-50, 0, 50, 50);
+ ctx.restore();
+
+ ctx.save();
+ ctx.scale(1, -1);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(50, -50, 50, 50);
+ ctx.restore();
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.scale.large
+ desc: scale() with large scale factors works
+ notes: Not really that large at all, but it hits the limits in Firefox.
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(1e5, 1e5);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 1, 1);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.scale.nonfinite
+ desc: scale() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ @nonfinite ctx.scale(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.scale.multiple
+ desc: Multiple scale()s combine
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.scale(Math.sqrt(2), Math.sqrt(2));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ @assert pixel 90,40 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.transformation.rotate.zero
+ desc: rotate() by 0 does nothing
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.rotate.radians
+ desc: rotate() uses radians
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI); // should fail obviously if this is 3.1 degrees
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.rotate.direction
+ desc: rotate() is clockwise
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI / 2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, -100, 50, 100);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.rotate.wrap
+ desc: rotate() wraps large positive values correctly
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(Math.PI * (1 + 4096)); // == pi (mod 2*pi)
+ // We need about pi +/- 0.001 in order to get correct-looking results
+ // 32-bit floats can store pi*4097 with precision 2^-10, so that should
+ // be safe enough on reasonable implementations
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,2 == 0,255,0,255;
+ @assert pixel 98,47 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.rotate.wrapnegative
+ desc: rotate() wraps large negative values correctly
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.rotate(-Math.PI * (1 + 4096));
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ @assert pixel 98,2 == 0,255,0,255;
+ @assert pixel 98,47 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.rotate.nonfinite
+ desc: rotate() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ @nonfinite ctx.rotate(<0.1 Infinity -Infinity NaN>);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.translate.basic
+ desc: translate() works
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 50);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -50, 100, 50);
+ @assert pixel 90,40 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.translate.nonfinite
+ desc: translate() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ @nonfinite ctx.translate(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+
+- name: 2d.transformation.transform.identity
+ desc: transform() with the identity matrix does nothing
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,0, 0,1, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.transform.skewed
+ desc: transform() with skewy matrix transforms correctly
+ code: |
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.transform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ @assert pixel 21,11 == 0,255,0,255;
+ @assert pixel 79,11 == 0,255,0,255;
+ @assert pixel 21,39 == 0,255,0,255;
+ @assert pixel 79,39 == 0,255,0,255;
+ @assert pixel 39,19 == 0,255,0,255;
+ @assert pixel 61,19 == 0,255,0,255;
+ @assert pixel 39,31 == 0,255,0,255;
+ @assert pixel 61,31 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.transform.multiply
+ desc: transform() multiplies the CTM
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.transform(1,2, 3,4, 5,6);
+ ctx.transform(-2,1, 3/2,-1/2, 1,-2);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.transform.nonfinite
+ desc: transform() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ @nonfinite ctx.transform(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.setTransform.skewed
+ code: |
+ // Create green with a red square ring inside it
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(20, 10, 60, 30);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(40, 20, 20, 10);
+
+ // Draw a skewed shape to fill that gap, to make sure it is aligned correctly
+ ctx.setTransform(1,4, 2,3, 5,6);
+ // Post-transform coordinates:
+ // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
+ // Hence pre-transform coordinates:
+ var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
+ [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
+ [-7.4,11.2]];
+ ctx.beginPath();
+ ctx.moveTo(pts[0][0], pts[0][1]);
+ for (var i = 0; i < pts.length; ++i)
+ ctx.lineTo(pts[i][0], pts[i][1]);
+ ctx.fill();
+ @assert pixel 21,11 == 0,255,0,255;
+ @assert pixel 79,11 == 0,255,0,255;
+ @assert pixel 21,39 == 0,255,0,255;
+ @assert pixel 79,39 == 0,255,0,255;
+ @assert pixel 39,19 == 0,255,0,255;
+ @assert pixel 61,19 == 0,255,0,255;
+ @assert pixel 39,31 == 0,255,0,255;
+ @assert pixel 61,31 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.setTransform.multiple
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.setTransform(1/2,0, 0,1/2, 0,0);
+ ctx.setTransform();
+ ctx.setTransform(2,0, 0,2, 0,0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 50, 25);
+ @assert pixel 75,35 == 0,255,0,255;
+ expected: green
+
+- name: 2d.transformation.setTransform.nonfinite
+ desc: setTransform() with Infinity/NaN is ignored
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+
+ ctx.translate(100, 10);
+ @nonfinite ctx.setTransform(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
+
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(-100, -10, 100, 50);
+
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml
new file mode 100644
index 0000000000..f9b48fb8da
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml
@@ -0,0 +1,10 @@
+- name: 2d.video.invalid
+ desc: Verify test doesn't crash with invalid video.
+ canvasType:
+ ['HTMLCanvas']
+ code: |
+ var v = document.createElement('video');
+ v.play();
+ // Test is deliberately not waiting for the 'playing' event to fire.
+ ctx.createPattern(v, 'repeat-x');
+ ctx.drawImage(v, 0, 0);
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml/element/meta.yaml b/testing/web-platform/tests/html/canvas/tools/yaml/element/meta.yaml
new file mode 100644
index 0000000000..5fd8b68498
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml/element/meta.yaml
@@ -0,0 +1,539 @@
+- meta: |
+ cases = [
+ ("zero", "0", 0),
+ ("empty", "", None),
+ ("onlyspace", " ", None),
+ ("space", " 100", 100),
+ ("whitespace", "\r\n\t\f100", 100),
+ ("plus", "+100", 100),
+ ("minus", "-100", None),
+ ("octal", "0100", 100),
+ ("hex", "0x100", 0),
+ ("exp", "100e1", 100),
+ ("decimal", "100.999", 100),
+ ("percent", "100%", 100),
+ ("em", "100em", 100),
+ ("junk", "#!?", None),
+ ("trailingjunk", "100#!?", 100),
+ ]
+ def gen(name, string, exp, code):
+ if exp is None:
+ code += "@assert canvas.width === 300;\n@assert canvas.height === 150;\n"
+ expected = "size 300 150"
+ else:
+ code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp)
+ expected = "size %s %s" % (exp, exp)
+
+ # With "100%", Opera gets canvas.width = 100 but renders at 100% of the frame width,
+ # so check the CSS display width
+ code += '@assert window.getComputedStyle(canvas, null).getPropertyValue("width") === "%spx";\n' % (exp, )
+
+ code += "@assert canvas.getAttribute('width') === %r;\n" % string
+ code += "@assert canvas.getAttribute('height') === %r;\n" % string
+
+ if exp == 0:
+ expected = None # can't generate zero-sized PNGs for the expected image
+
+ return code, expected
+
+ for name, string, exp in cases:
+ code = ""
+ code, expected = gen(name, string, exp, code)
+ # We need to replace \r with &#xD; because \r\n gets converted to \n in the HTML parser.
+ htmlString = string.replace('\r', '&#xD;')
+ tests.append( {
+ "name": "2d.canvas.host.size.attributes.parse.%s" % name,
+ "desc": "Parsing of non-negative integers",
+ "size": '%s, %s' % (htmlString, htmlString),
+ "code": code,
+ "expected": expected
+ } )
+
+ for name, string, exp in cases:
+ code = "canvas.setAttribute('width', %r);\ncanvas.setAttribute('height', %r);\n" % (string, string)
+ code, expected = gen(name, string, exp, code)
+ tests.append( {
+ "name": "2d.canvas.host.size.attributes.setAttribute.%s" % name,
+ "desc": "Parsing of non-negative integers in setAttribute",
+ "size": '50, 50',
+ "code": code,
+ "expected": expected
+ } )
+
+- meta: |
+ state = [ # some non-default values to test with
+ ('strokeStyle', '"#ff0000"'),
+ ('fillStyle', '"#ff0000"'),
+ ('globalAlpha', 0.5),
+ ('lineWidth', 0.5),
+ ('lineCap', '"round"'),
+ ('lineJoin', '"round"'),
+ ('miterLimit', 0.5),
+ ('shadowOffsetX', 5),
+ ('shadowOffsetY', 5),
+ ('shadowBlur', 5),
+ ('shadowColor', '"#ff0000"'),
+ ('globalCompositeOperation', '"copy"'),
+ ('font', '"25px serif"'),
+ ('textAlign', '"center"'),
+ ('textBaseline', '"bottom"'),
+ ]
+ for key,value in state:
+ tests.append( {
+ 'name': '2d.state.saverestore.%s' % key,
+ 'desc': 'save()/restore() works for %s' % key,
+ 'code':
+ """// Test that restore() undoes any modifications
+ var old = ctx.%(key)s;
+ ctx.save();
+ ctx.%(key)s = %(value)s;
+ ctx.restore();
+ @assert ctx.%(key)s === old;
+
+ // Also test that save() doesn't modify the values
+ ctx.%(key)s = %(value)s;
+ old = ctx.%(key)s;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against %(value)s
+ ctx.save();
+ @assert ctx.%(key)s === old;
+ ctx.restore();
+ """ % { 'key':key, 'value':value }
+ } )
+
+ tests.append( {
+ 'name': '2d.canvas.host.initial.reset.2dstate',
+ 'desc': 'Resetting the canvas state resets 2D state variables',
+ 'code':
+ """canvas.width = 100;
+ var default_val;
+ """ + "".join(
+ """
+ default_val = ctx.%(key)s;
+ ctx.%(key)s = %(value)s;
+ canvas.width = 100;
+ @assert ctx.%(key)s === default_val;
+ """ % { 'key':key, 'value':value }
+ for key,value in state),
+ } )
+
+- meta: |
+ # Composite operation tests
+ # <http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2007-March/010608.html>
+ ops = [
+ # name FA FB
+ ('source-over', '1', '1-aA'),
+ ('destination-over', '1-aB', '1'),
+ ('source-in', 'aB', '0'),
+ ('destination-in', '0', 'aA'),
+ ('source-out', '1-aB', '0'),
+ ('destination-out', '0', '1-aA'),
+ ('source-atop', 'aB', '1-aA'),
+ ('destination-atop', '1-aB', 'aA'),
+ ('xor', '1-aB', '1-aA'),
+ ('copy', '1', '0'),
+ ('lighter', '1', '1'),
+ ('clear', '0', '0'),
+ ]
+
+ # The ones that change the output when src = (0,0,0,0):
+ ops_trans = [ 'source-in', 'destination-in', 'source-out', 'destination-atop', 'copy' ];
+
+ def calc_output(A, B, FA_code, FB_code):
+ (RA, GA, BA, aA) = A
+ (RB, GB, BB, aB) = B
+ rA, gA, bA = RA*aA, GA*aA, BA*aA
+ rB, gB, bB = RB*aB, GB*aB, BB*aB
+
+ FA = eval(FA_code)
+ FB = eval(FB_code)
+
+ rO = rA*FA + rB*FB
+ gO = gA*FA + gB*FB
+ bO = bA*FA + bB*FB
+ aO = aA*FA + aB*FB
+
+ rO = min(255, rO)
+ gO = min(255, gO)
+ bO = min(255, bO)
+ aO = min(1, aO)
+
+ if aO:
+ RO = rO / aO
+ GO = gO / aO
+ BO = bO / aO
+ else: RO = GO = BO = 0
+
+ return (RO, GO, BO, aO)
+
+ def to_test(color):
+ r, g, b, a = color
+ return '%d,%d,%d,%d' % (round(r), round(g), round(b), round(a*255))
+ def to_cairo(color):
+ r, g, b, a = color
+ return '%f,%f,%f,%f' % (r/255., g/255., b/255., a)
+
+ for (name, src, dest) in [
+ ('solid', (255, 255, 0, 1.0), (0, 255, 255, 1.0)),
+ ('transparent', (0, 0, 255, 0.75), (0, 255, 0, 0.5)),
+ # catches the atop, xor and lighter bugs in Opera 9.10
+ ]:
+ for op, FA_code, FB_code in ops:
+ expected = calc_output(src, dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ """ % (dest, op, src, to_test(expected)),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%s)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % to_cairo(expected),
+ } )
+
+ for (name, src, dest) in [ ('image', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ expected = calc_output(src, dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'images': [ 'yellow75.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ """ % (dest, op, to_test(expected)),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%s)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % to_cairo(expected),
+ } )
+
+ for (name, src, dest) in [ ('canvas', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ expected = calc_output(src, dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'images': [ 'yellow75.png' ],
+ 'code': """
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = canvas.width;
+ canvas2.height = canvas.height;
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(document.getElementById('yellow75.png'), 0, 0);
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.drawImage(canvas2, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ """ % (dest, op, to_test(expected)),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%s)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % to_cairo(expected),
+ } )
+
+
+ for (name, src, dest) in [ ('uncovered.fill', (0, 0, 255, 0.75), (0, 255, 0, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.fillStyle = 'rgba%s';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ """ % (dest, op, src, to_test(expected0)),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%s)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % (to_cairo(expected0)),
+ } )
+
+ for (name, src, dest) in [ ('uncovered.image', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'images': [ 'yellow.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.drawImage(document.getElementById('yellow.png'), 40, 40, 10, 10, 40, 50, 10, 10);
+ @assert pixel 15,15 ==~ %s +/- 5;
+ @assert pixel 50,25 ==~ %s +/- 5;
+ """ % (dest, op, to_test(expected0), to_test(expected0)),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%s)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % (to_cairo(expected0)),
+ } )
+
+ for (name, src, dest) in [ ('uncovered.nocontext', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var canvas2 = document.createElement('canvas');
+ ctx.drawImage(canvas2, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ """ % (dest, op, to_test(expected0)),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%s)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % (to_cairo(expected0)),
+ } )
+
+ for (name, src, dest) in [ ('uncovered.pattern', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'images': [ 'yellow.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.fillStyle = ctx.createPattern(document.getElementById('yellow.png'), 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ """ % (dest, op, to_test(expected0)),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%s)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % (to_cairo(expected0)),
+ } )
+
+ for op, FA_code, FB_code in ops:
+ tests.append( {
+ 'name': '2d.composite.clip.%s' % (op),
+ 'desc': 'fill() does not affect pixels outside the clip region.',
+ 'code': """
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ """ % (op),
+ 'expected': 'green'
+ } )
+
+- meta: |
+ # Color parsing tests
+
+ # Try most of the CSS3 Color <color> values - http://www.w3.org/TR/css3-color/#colorunits
+ big_float = '1' + ('0' * 39)
+ big_double = '1' + ('0' * 310)
+ for name, string, r,g,b,a, notes in [
+ ('html4', 'limE', 0,255,0,255, ""),
+ ('hex3', '#0f0', 0,255,0,255, ""),
+ ('hex4', '#0f0f', 0,255,0,255, ""),
+ ('hex6', '#00fF00', 0,255,0,255, ""),
+ ('hex8', '#00ff00ff', 0,255,0,255, ""),
+ ('rgb-num', 'rgb(0,255,0)', 0,255,0,255, ""),
+ ('rgb-clamp-1', 'rgb(-1000, 1000, -1000)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-2', 'rgb(-200%, 200%, -200%)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-3', 'rgb(-2147483649, 4294967298, -18446744073709551619)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-4', 'rgb(-'+big_float+', '+big_float+', -'+big_float+')', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-5', 'rgb(-'+big_double+', '+big_double+', -'+big_double+')', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-percent', 'rgb(0% ,100% ,0%)', 0,255,0,255, 'CSS3 Color says "The integer value 255 corresponds to 100%". (In particular, it is not 254...)'),
+ ('rgb-eof', 'rgb(0, 255, 0', 0,255,0,255, ""), # see CSS2.1 4.2 "Unexpected end of style sheet"
+ ('rgba-solid-1', 'rgba( 0 , 255 , 0 , 1 )', 0,255,0,255, ""),
+ ('rgba-solid-2', 'rgba( 0 , 255 , 0 , 1.0 )', 0,255,0,255, ""),
+ ('rgba-solid-3', 'rgba( 0 , 255 , 0 , +1 )', 0,255,0,255, ""),
+ ('rgba-solid-4', 'rgba( -0 , 255 , +0 , 1 )', 0,255,0,255, ""),
+ ('rgba-num-1', 'rgba( 0 , 255 , 0 , .499 )', 0,255,0,127, ""),
+ ('rgba-num-2', 'rgba( 0 , 255 , 0 , 0.499 )', 0,255,0,127, ""),
+ ('rgba-percent', 'rgba(0%,100%,0%,0.499)', 0,255,0,127, ""), # 0.499*255 rounds to 127, both down and nearest, so it should be safe
+ ('rgba-clamp-1', 'rgba(0, 255, 0, -2)', 0,0,0,0, ""),
+ ('rgba-clamp-2', 'rgba(0, 255, 0, 2)', 0,255,0,255, ""),
+ ('rgba-eof', 'rgba(0, 255, 0, 1', 0,255,0,255, ""),
+ ('transparent-1', 'transparent', 0,0,0,0, ""),
+ ('transparent-2', 'TrAnSpArEnT', 0,0,0,0, ""),
+ ('hsl-1', 'hsl(120, 100%, 50%)', 0,255,0,255, ""),
+ ('hsl-2', 'hsl( -240 , 100% , 50% )', 0,255,0,255, ""),
+ ('hsl-3', 'hsl(360120, 100%, 50%)', 0,255,0,255, ""),
+ ('hsl-4', 'hsl(-360240, 100%, 50%)', 0,255,0,255, ""),
+ ('hsl-5', 'hsl(120.0, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('hsl-6', 'hsl(+120, +100%, +50%)', 0,255,0,255, ""),
+ ('hsl-clamp-1', 'hsl(120, 200%, 50%)', 0,255,0,255, ""),
+ ('hsl-clamp-2', 'hsl(120, -200%, 49.9%)', 127,127,127,255, ""),
+ ('hsl-clamp-3', 'hsl(120, 100%, 200%)', 255,255,255,255, ""),
+ ('hsl-clamp-4', 'hsl(120, 100%, -200%)', 0,0,0,255, ""),
+ ('hsla-1', 'hsla(120, 100%, 50%, 0.499)', 0,255,0,127, ""),
+ ('hsla-2', 'hsla( 120.0 , 100.0% , 50.0% , 1 )', 0,255,0,255, ""),
+ ('hsla-clamp-1', 'hsla(120, 200%, 50%, 1)', 0,255,0,255, ""),
+ ('hsla-clamp-2', 'hsla(120, -200%, 49.9%, 1)', 127,127,127,255, ""),
+ ('hsla-clamp-3', 'hsla(120, 100%, 200%, 1)', 255,255,255,255, ""),
+ ('hsla-clamp-4', 'hsla(120, 100%, -200%, 1)', 0,0,0,255, ""),
+ ('hsla-clamp-5', 'hsla(120, 100%, 50%, 2)', 0,255,0,255, ""),
+ ('hsla-clamp-6', 'hsla(120, 100%, 0%, -2)', 0,0,0,0, ""),
+ ('svg-1', 'gray', 128,128,128,255, ""),
+ ('svg-2', 'grey', 128,128,128,255, ""),
+ # css-color-4 rgb() color function
+ # https://drafts.csswg.org/css-color/#numeric-rgb
+ ('css-color-4-rgb-1', 'rgb(0, 255.0, 0)', 0,255,0,255, ""),
+ ('css-color-4-rgb-2', 'rgb(0, 255, 0, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgb-3', 'rgb(0, 255, 0, 20%)', 0,255,0,51, ""),
+ ('css-color-4-rgb-4', 'rgb(0 255 0)', 0,255,0,255, ""),
+ ('css-color-4-rgb-5', 'rgb(0 255 0 / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgb-6', 'rgb(0 255 0 / 20%)', 0,255,0,51, ""),
+ ('css-color-4-rgba-1', 'rgba(0, 255.0, 0)', 0,255,0,255, ""),
+ ('css-color-4-rgba-2', 'rgba(0, 255, 0, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgba-3', 'rgba(0, 255, 0, 20%)', 0,255,0,51, ""),
+ ('css-color-4-rgba-4', 'rgba(0 255 0)', 0,255,0,255, ""),
+ ('css-color-4-rgba-5', 'rgba(0 255 0 / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgba-6', 'rgba(0 255 0 / 20%)', 0,255,0,51, ""),
+ # css-color-4 hsl() color function
+ # https://drafts.csswg.org/css-color/#the-hsl-notation
+ ('css-color-4-hsl-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsl-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsl-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
+ ('css-color-4-hsl-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsl-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsla-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsla-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
+ ('css-color-4-hsla-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsla-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
+ # currentColor is handled later
+ ]:
+ # TODO: test by retrieving fillStyle, instead of actually drawing?
+ # TODO: test strokeStyle, shadowColor in the same way
+ test = {
+ 'name': '2d.fillStyle.parse.%s' % name,
+ 'notes': notes,
+ 'code': """
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '%s';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == %d,%d,%d,%d;
+ """ % (string, r,g,b,a),
+ 'expected': """size 100 50
+ cr.set_source_rgba(%f, %f, %f, %f)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ """ % (r/255., g/255., b/255., a/255.),
+ }
+ tests.append(test)
+
+ # Also test that invalid colors are ignored
+ for name, string in [
+ ('hex1', '#f'),
+ ('hex2', '#f0'),
+ ('hex3', '#g00'),
+ ('hex4', '#fg00'),
+ ('hex5', '#ff000'),
+ ('hex6', '#fg0000'),
+ ('hex7', '#ff0000f'),
+ ('hex8', '#fg0000ff'),
+ ('rgb-1', 'rgb(255.0, 0, 0,)'),
+ ('rgb-2', 'rgb(100%, 0, 0)'),
+ ('rgb-3', 'rgb(255, - 1, 0)'),
+ ('rgba-1', 'rgba(100%, 0, 0, 1)'),
+ ('rgba-2', 'rgba(255, 0, 0, 1. 0)'),
+ ('rgba-3', 'rgba(255, 0, 0, 1.)'),
+ ('rgba-4', 'rgba(255, 0, 0, '),
+ ('rgba-5', 'rgba(255, 0, 0, 1,)'),
+ ('hsl-1', 'hsl(0%, 100%, 50%)'),
+ ('hsl-2', 'hsl(z, 100%, 50%)'),
+ ('hsl-3', 'hsl(0, 0, 50%)'),
+ ('hsl-4', 'hsl(0, 100%, 0)'),
+ ('hsl-5', 'hsl(0, 100.%, 50%)'),
+ ('hsl-6', 'hsl(0, 100%, 50%,)'),
+ ('hsla-1', 'hsla(0%, 100%, 50%, 1)'),
+ ('hsla-2', 'hsla(0, 0, 50%, 1)'),
+ ('hsla-3', 'hsla(0, 0, 50%, 1,)'),
+ ('name-1', 'darkbrown'),
+ ('name-2', 'firebrick1'),
+ ('name-3', 'red blue'),
+ ('name-4', '"red"'),
+ ('name-5', '"red'),
+ # css-color-4 color function
+ # comma and comma-less expressions should not mix together.
+ ('css-color-4-rgb-1', 'rgb(255, 0, 0 / 1)'),
+ ('css-color-4-rgb-2', 'rgb(255 0 0, 1)'),
+ ('css-color-4-rgb-3', 'rgb(255, 0 0)'),
+ ('css-color-4-rgba-1', 'rgba(255, 0, 0 / 1)'),
+ ('css-color-4-rgba-2', 'rgba(255 0 0, 1)'),
+ ('css-color-4-rgba-3', 'rgba(255, 0 0)'),
+ ('css-color-4-hsl-1', 'hsl(0, 100%, 50% / 1)'),
+ ('css-color-4-hsl-2', 'hsl(0 100% 50%, 1)'),
+ ('css-color-4-hsl-3', 'hsl(0, 100% 50%)'),
+ ('css-color-4-hsla-1', 'hsla(0, 100%, 50% / 1)'),
+ ('css-color-4-hsla-2', 'hsla(0 100% 50%, 1)'),
+ ('css-color-4-hsla-3', 'hsla(0, 100% 50%)'),
+ # trailing slash
+ ('css-color-4-rgb-4', 'rgb(0 0 0 /)'),
+ ('css-color-4-rgb-5', 'rgb(0, 0, 0 /)'),
+ ('css-color-4-hsl-4', 'hsl(0 100% 50% /)'),
+ ('css-color-4-hsl-5', 'hsl(0, 100%, 50% /)'),
+ ]:
+ test = {
+ 'name': '2d.fillStyle.parse.invalid.%s' % name,
+ 'code': """
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '%s'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ """ % string,
+ 'expected': 'green'
+ }
+ tests.append(test)
+
+ # Some can't have positive tests, only negative tests, because we don't know what color they're meant to be
+ for name, string in [
+ ('system', 'ThreeDDarkShadow'),
+ #('flavor', 'flavor'), # removed from latest CSS3 Color drafts
+ ]:
+ test = {
+ 'name': '2d.fillStyle.parse.%s' % name,
+ 'code': """
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '%s';
+ @assert ctx.fillStyle =~ /^#(?!(FF0000|ff0000|f00)$)/; // test that it's not red
+ """ % (string,),
+ }
+ tests.append(test) \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml/element/the-canvas-element.yaml b/testing/web-platform/tests/html/canvas/tools/yaml/element/the-canvas-element.yaml
new file mode 100644
index 0000000000..1ecf8ccdbf
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml/element/the-canvas-element.yaml
@@ -0,0 +1,143 @@
+- name: 2d.canvas.context.exists
+ desc: The 2D context is implemented
+ code: |
+ @assert canvas.getContext('2d') !== null;
+
+- name: 2d.canvas.context.invalid.args
+ desc: Calling getContext with invalid arguments.
+ code: |
+ @assert canvas.getContext('') === null;
+ @assert canvas.getContext('2d#') === null;
+ @assert canvas.getContext('This is clearly not a valid context name.') === null;
+ @assert canvas.getContext('2d\0') === null;
+ @assert canvas.getContext('2\uFF44') === null;
+ @assert canvas.getContext('2D') === null;
+ @assert throws TypeError canvas.getContext();
+ @assert canvas.getContext('null') === null;
+ @assert canvas.getContext('undefined') === null;
+
+- name: 2d.canvas.context.extraargs.create
+ desc: The 2D context doesn't throw with extra getContext arguments (new context)
+ code: |
+ @assert document.createElement("canvas").getContext('2d', false, {}, [], 1, "2") !== null;
+ @assert document.createElement("canvas").getContext('2d', 123) !== null;
+ @assert document.createElement("canvas").getContext('2d', "test") !== null;
+ @assert document.createElement("canvas").getContext('2d', undefined) !== null;
+ @assert document.createElement("canvas").getContext('2d', null) !== null;
+ @assert document.createElement("canvas").getContext('2d', Symbol.hasInstance) !== null;
+
+- name: 2d.canvas.context.extraargs.cache
+ desc: The 2D context doesn't throw with extra getContext arguments (cached)
+ code: |
+ @assert canvas.getContext('2d', false, {}, [], 1, "2") !== null;
+ @assert canvas.getContext('2d', 123) !== null;
+ @assert canvas.getContext('2d', "test") !== null;
+ @assert canvas.getContext('2d', undefined) !== null;
+ @assert canvas.getContext('2d', null) !== null;
+ @assert canvas.getContext('2d', Symbol.hasInstance) !== null;
+
+- name: 2d.canvas.context.type.exists
+ desc: The 2D context interface is a property of 'window'
+ notes: &bindings Defined in "Web IDL" (draft)
+ code: |
+ @assert window.CanvasRenderingContext2D;
+
+- name: 2d.canvas.context.type.prototype
+ desc: window.CanvasRenderingContext2D.prototype are not [[Writable]] and not [[Configurable]],
+ and its methods are [[Configurable]].
+ notes: *bindings
+ code: |
+ @assert window.CanvasRenderingContext2D.prototype;
+ @assert window.CanvasRenderingContext2D.prototype.fill;
+ window.CanvasRenderingContext2D.prototype = null;
+ @assert window.CanvasRenderingContext2D.prototype;
+ delete window.CanvasRenderingContext2D.prototype;
+ @assert window.CanvasRenderingContext2D.prototype;
+ window.CanvasRenderingContext2D.prototype.fill = 1;
+ @assert window.CanvasRenderingContext2D.prototype.fill === 1;
+ delete window.CanvasRenderingContext2D.prototype.fill;
+ @assert window.CanvasRenderingContext2D.prototype.fill === undefined;
+
+- name: 2d.canvas.context.type.replace
+ desc: Interface methods can be overridden
+ notes: *bindings
+ code: |
+ var fillRect = window.CanvasRenderingContext2D.prototype.fillRect;
+ window.CanvasRenderingContext2D.prototype.fillRect = function (x, y, w, h)
+ {
+ this.fillStyle = '#0f0';
+ fillRect.call(this, x, y, w, h);
+ };
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.canvas.context.type.extend
+ desc: Interface methods can be added
+ notes: *bindings
+ code: |
+ window.CanvasRenderingContext2D.prototype.fillRectGreen = function (x, y, w, h)
+ {
+ this.fillStyle = '#0f0';
+ this.fillRect(x, y, w, h);
+ };
+ ctx.fillStyle = '#f00';
+ ctx.fillRectGreen(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.canvas.context.unique
+ desc: getContext('2d') returns the same object
+ code: |
+ @assert canvas.getContext('2d') === canvas.getContext('2d');
+
+- name: 2d.canvas.context.shared
+ desc: getContext('2d') returns objects which share canvas state
+ code: |
+ var ctx2 = canvas.getContext('2d');
+ ctx.fillStyle = '#f00';
+ ctx2.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ expected: green
+
+- name: 2d.canvas.host.scaled
+ desc: CSS-scaled canvases get drawn correctly
+ size: 50, 25
+ canvas: 'style="width: 100px; height: 50px"'
+ manual:
+ code: |
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 50, 25);
+ ctx.fillStyle = '#0ff';
+ ctx.fillRect(0, 0, 25, 10);
+ expected: |
+ size 100 50
+ cr.set_source_rgb(0, 0, 1)
+ cr.rectangle(0, 0, 100, 50)
+ cr.fill()
+ cr.set_source_rgb(0, 1, 1)
+ cr.rectangle(0, 0, 50, 20)
+ cr.fill()
+
+- name: 2d.canvas.host.reference
+ desc: CanvasRenderingContext2D.canvas refers back to its canvas
+ code: |
+ @assert ctx.canvas === canvas;
+
+- name: 2d.canvas.host.readonly
+ desc: CanvasRenderingContext2D.canvas is readonly
+ code: |
+ var c = document.createElement('canvas');
+ var d = ctx.canvas;
+ @assert c !== d;
+ ctx.canvas = c;
+ @assert ctx.canvas === d;
+
+- name: 2d.canvas.context.prototype
+ desc: checks CanvasRenderingContext2D prototype
+ code: |
+ @assert Object.getPrototypeOf(CanvasRenderingContext2D.prototype) === Object.prototype;
+ @assert Object.getPrototypeOf(ctx) === CanvasRenderingContext2D.prototype;
+ t.done();
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml b/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml
new file mode 100644
index 0000000000..7b44fd9f26
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/meta.yaml
@@ -0,0 +1,539 @@
+- meta: |
+ state = [ # some non-default values to test with
+ ('strokeStyle', '"#ff0000"'),
+ ('fillStyle', '"#ff0000"'),
+ ('globalAlpha', 0.5),
+ ('lineWidth', 0.5),
+ ('lineCap', '"round"'),
+ ('lineJoin', '"round"'),
+ ('miterLimit', 0.5),
+ ('shadowOffsetX', 5),
+ ('shadowOffsetY', 5),
+ ('shadowBlur', 5),
+ ('shadowColor', '"#ff0000"'),
+ ('globalCompositeOperation', '"copy"'),
+ ]
+ for key,value in state:
+ tests.append( {
+ 'name': '2d.state.saverestore.%s' % key,
+ 'desc': 'save()/restore() works for %s' % key,
+ 'code':
+ """// Test that restore() undoes any modifications
+ var old = ctx.%(key)s;
+ ctx.save();
+ ctx.%(key)s = %(value)s;
+ ctx.restore();
+ @assert ctx.%(key)s === old;
+
+ // Also test that save() doesn't modify the values
+ ctx.%(key)s = %(value)s;
+ old = ctx.%(key)s;
+ // we're not interested in failures caused by get(set(x)) != x (e.g.
+ // from rounding), so compare against 'old' instead of against %(value)s
+ ctx.save();
+ @assert ctx.%(key)s === old;
+ ctx.restore();
+ t.done();
+ """ % { 'key':key, 'value':value }
+ } )
+
+ tests.append( {
+ 'name': '2d.canvas.host.initial.reset.2dstate',
+ 'desc': 'Resetting the canvas state resets 2D state variables',
+ 'code':
+ """canvas.width = 100;
+ var default_val;
+ """ + "".join(
+ """
+ default_val = ctx.%(key)s;
+ ctx.%(key)s = %(value)s;
+ canvas.width = 100;
+ @assert ctx.%(key)s === default_val;
+ """ % { 'key':key, 'value':value }
+ for key,value in state) + "\nt.done();",
+ } )
+
+- meta: |
+ # Composite operation tests
+ # <http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2007-March/010608.html>
+ ops = [
+ # name FA FB
+ ('source-over', '1', '1-aA'),
+ ('destination-over', '1-aB', '1'),
+ ('source-in', 'aB', '0'),
+ ('destination-in', '0', 'aA'),
+ ('source-out', '1-aB', '0'),
+ ('destination-out', '0', '1-aA'),
+ ('source-atop', 'aB', '1-aA'),
+ ('destination-atop', '1-aB', 'aA'),
+ ('xor', '1-aB', '1-aA'),
+ ('copy', '1', '0'),
+ ('lighter', '1', '1'),
+ ('clear', '0', '0'),
+ ]
+
+ # The ones that change the output when src = (0,0,0,0):
+ ops_trans = [ 'source-in', 'destination-in', 'source-out', 'destination-atop', 'copy' ];
+
+ def calc_output(A, B, FA_code, FB_code):
+ RA, GA, BA, aA = A
+ RB, GB, BB, aB = B
+ rA, gA, bA = RA*aA, GA*aA, BA*aA
+ rB, gB, bB = RB*aB, GB*aB, BB*aB
+
+ FA = eval(FA_code)
+ FB = eval(FB_code)
+
+ rO = rA*FA + rB*FB
+ gO = gA*FA + gB*FB
+ bO = bA*FA + bB*FB
+ aO = aA*FA + aB*FB
+
+ rO = min(255, rO)
+ gO = min(255, gO)
+ bO = min(255, bO)
+ aO = min(1, aO)
+
+ if aO:
+ RO = rO / aO
+ GO = gO / aO
+ BO = bO / aO
+ else: RO = GO = BO = 0
+
+ return (RO, GO, BO, aO)
+
+ def to_test(color):
+ r, g, b, a = color
+ return '%d,%d,%d,%d' % (round(r), round(g), round(b), round(a*255))
+ def to_cairo(color):
+ r, g, b, a = color
+ return '%f,%f,%f,%f' % (r/255., g/255., b/255., a)
+
+ for (name, src, dest) in [
+ ('solid', (255, 255, 0, 1.0), (0, 255, 255, 1.0)),
+ ('transparent', (0, 0, 255, 0.75), (0, 255, 0, 0.5)),
+ # catches the atop, xor and lighter bugs in Opera 9.10
+ ]:
+ for op, FA_code, FB_code in ops:
+ expected = calc_output(src, dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ t.done();
+ """ % (dest, op, src, to_test(expected)),
+ } )
+
+ for (name, src, dest) in [ ('image', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ expected = calc_output(src, dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'images': [ 'yellow75.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(expected)),
+ } )
+
+ for (name, src, dest) in [ ('canvas', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ expected = calc_output(src, dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'images': [ 'yellow75.png' ],
+ 'code': """
+ var offscreenCanvas2 = new OffscreenCanvas(canvas.width, canvas.height);
+ var ctx2 = offscreenCanvas2.getContext('2d');
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow75.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx2.drawImage(bitmap, 0, 0);
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(expected)),
+ } )
+
+ for (name, src, dest) in [ ('uncovered.fill', (0, 0, 255, 0.75), (0, 255, 0, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ new_test = {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.fillStyle = 'rgba%s';
+ ctx.translate(0, 25);
+ ctx.fillRect(0, 50, 100, 50);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ t.done();
+ """ % (dest, op, src, to_test(expected0)),
+ }
+ if op == 'destination-in':
+ new_test['timeout'] = 'long'
+ tests.append(new_test)
+
+ for (name, src, dest) in [ ('uncovered.image', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'images': [ 'yellow.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+ @assert pixel 15,15 ==~ %s +/- 5;
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(expected0), to_test(expected0)),
+ } )
+
+ for (name, src, dest) in [ ('uncovered.nocontext', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ ctx.drawImage(offscreenCanvas2, 0, 0);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ t.done();
+ """ % (dest, op, to_test(expected0)),
+ } )
+
+ for (name, src, dest) in [ ('uncovered.pattern', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
+ for op, FA_code, FB_code in ops:
+ if op not in ops_trans: continue
+ expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
+ tests.append( {
+ 'name': '2d.composite.%s.%s' % (name, op),
+ 'desc': 'Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
+ 'images': [ 'yellow.png' ],
+ 'code': """
+ ctx.fillStyle = 'rgba%s';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ var promise = new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/yellow.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ promise.then(function(response) {
+ return createImageBitmap(response).then(bitmap => {
+ ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+ ctx.fillRect(0, 50, 100, 50);
+ @assert pixel 50,25 ==~ %s +/- 5;
+ });
+ }).then(t_pass, t_fail);
+ """ % (dest, op, to_test(expected0)),
+ } )
+
+ for op, FA_code, FB_code in ops:
+ tests.append( {
+ 'name': '2d.composite.clip.%s' % (op),
+ 'desc': 'fill() does not affect pixels outside the clip region.',
+ 'code': """
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.globalCompositeOperation = '%s';
+ ctx.rect(-20, -20, 10, 10);
+ ctx.clip();
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ @assert pixel 25,25 == 0,255,0,255;
+ @assert pixel 75,25 == 0,255,0,255;
+ t.done();
+ """ % (op),
+ } )
+
+- meta: |
+ # Color parsing tests
+
+ # Try most of the CSS3 Color <color> values - http://www.w3.org/TR/css3-color/#colorunits
+ big_float = '1' + ('0' * 39)
+ big_double = '1' + ('0' * 310)
+ for name, string, r,g,b,a, notes in [
+ ('html4', 'limE', 0,255,0,255, ""),
+ ('hex3', '#0f0', 0,255,0,255, ""),
+ ('hex4', '#0f0f', 0,255,0,255, ""),
+ ('hex6', '#00fF00', 0,255,0,255, ""),
+ ('hex8', '#00ff00ff', 0,255,0,255, ""),
+ ('rgb-num', 'rgb(0,255,0)', 0,255,0,255, ""),
+ ('rgb-clamp-1', 'rgb(-1000, 1000, -1000)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-2', 'rgb(-200%, 200%, -200%)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-3', 'rgb(-2147483649, 4294967298, -18446744073709551619)', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-4', 'rgb(-'+big_float+', '+big_float+', -'+big_float+')', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-clamp-5', 'rgb(-'+big_double+', '+big_double+', -'+big_double+')', 0,255,0,255, 'Assumes colors are clamped to [0,255].'),
+ ('rgb-percent', 'rgb(0% ,100% ,0%)', 0,255,0,255, 'CSS3 Color says "The integer value 255 corresponds to 100%". (In particular, it is not 254...)'),
+ ('rgb-eof', 'rgb(0, 255, 0', 0,255,0,255, ""), # see CSS2.1 4.2 "Unexpected end of style sheet"
+ ('rgba-solid-1', 'rgba( 0 , 255 , 0 , 1 )', 0,255,0,255, ""),
+ ('rgba-solid-2', 'rgba( 0 , 255 , 0 , 1.0 )', 0,255,0,255, ""),
+ ('rgba-solid-3', 'rgba( 0 , 255 , 0 , +1 )', 0,255,0,255, ""),
+ ('rgba-solid-4', 'rgba( -0 , 255 , +0 , 1 )', 0,255,0,255, ""),
+ ('rgba-num-1', 'rgba( 0 , 255 , 0 , .499 )', 0,255,0,127, ""),
+ ('rgba-num-2', 'rgba( 0 , 255 , 0 , 0.499 )', 0,255,0,127, ""),
+ ('rgba-percent', 'rgba(0%,100%,0%,0.499)', 0,255,0,127, ""), # 0.499*255 rounds to 127, both down and nearest, so it should be safe
+ ('rgba-clamp-1', 'rgba(0, 255, 0, -2)', 0,0,0,0, ""),
+ ('rgba-clamp-2', 'rgba(0, 255, 0, 2)', 0,255,0,255, ""),
+ ('rgba-eof', 'rgba(0, 255, 0, 1', 0,255,0,255, ""),
+ ('transparent-1', 'transparent', 0,0,0,0, ""),
+ ('transparent-2', 'TrAnSpArEnT', 0,0,0,0, ""),
+ ('hsl-1', 'hsl(120, 100%, 50%)', 0,255,0,255, ""),
+ ('hsl-2', 'hsl( -240 , 100% , 50% )', 0,255,0,255, ""),
+ ('hsl-3', 'hsl(360120, 100%, 50%)', 0,255,0,255, ""),
+ ('hsl-4', 'hsl(-360240, 100%, 50%)', 0,255,0,255, ""),
+ ('hsl-5', 'hsl(120.0, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('hsl-6', 'hsl(+120, +100%, +50%)', 0,255,0,255, ""),
+ ('hsl-clamp-1', 'hsl(120, 200%, 50%)', 0,255,0,255, ""),
+ ('hsl-clamp-2', 'hsl(120, -200%, 49.9%)', 127,127,127,255, ""),
+ ('hsl-clamp-3', 'hsl(120, 100%, 200%)', 255,255,255,255, ""),
+ ('hsl-clamp-4', 'hsl(120, 100%, -200%)', 0,0,0,255, ""),
+ ('hsla-1', 'hsla(120, 100%, 50%, 0.499)', 0,255,0,127, ""),
+ ('hsla-2', 'hsla( 120.0 , 100.0% , 50.0% , 1 )', 0,255,0,255, ""),
+ ('hsla-clamp-1', 'hsla(120, 200%, 50%, 1)', 0,255,0,255, ""),
+ ('hsla-clamp-2', 'hsla(120, -200%, 49.9%, 1)', 127,127,127,255, ""),
+ ('hsla-clamp-3', 'hsla(120, 100%, 200%, 1)', 255,255,255,255, ""),
+ ('hsla-clamp-4', 'hsla(120, 100%, -200%, 1)', 0,0,0,255, ""),
+ ('hsla-clamp-5', 'hsla(120, 100%, 50%, 2)', 0,255,0,255, ""),
+ ('hsla-clamp-6', 'hsla(120, 100%, 0%, -2)', 0,0,0,0, ""),
+ ('svg-1', 'gray', 128,128,128,255, ""),
+ ('svg-2', 'grey', 128,128,128,255, ""),
+ # css-color-4 rgb() color function
+ # https://drafts.csswg.org/css-color/#numeric-rgb
+ ('css-color-4-rgb-1', 'rgb(0, 255.0, 0)', 0,255,0,255, ""),
+ ('css-color-4-rgb-2', 'rgb(0, 255, 0, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgb-3', 'rgb(0, 255, 0, 20%)', 0,255,0,51, ""),
+ ('css-color-4-rgb-4', 'rgb(0 255 0)', 0,255,0,255, ""),
+ ('css-color-4-rgb-5', 'rgb(0 255 0 / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgb-6', 'rgb(0 255 0 / 20%)', 0,255,0,51, ""),
+ ('css-color-4-rgba-1', 'rgba(0, 255.0, 0)', 0,255,0,255, ""),
+ ('css-color-4-rgba-2', 'rgba(0, 255, 0, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgba-3', 'rgba(0, 255, 0, 20%)', 0,255,0,51, ""),
+ ('css-color-4-rgba-4', 'rgba(0 255 0)', 0,255,0,255, ""),
+ ('css-color-4-rgba-5', 'rgba(0 255 0 / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-rgba-6', 'rgba(0 255 0 / 20%)', 0,255,0,51, ""),
+ # css-color-4 hsl() color function
+ # https://drafts.csswg.org/css-color/#the-hsl-notation
+ ('css-color-4-hsl-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsl-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsl-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
+ ('css-color-4-hsl-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsl-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsl-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsla-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsla-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
+ ('css-color-4-hsla-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
+ ('css-color-4-hsla-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
+ ('css-color-4-hsla-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
+ # currentColor is handled later
+ ]:
+ # TODO: test by retrieving fillStyle, instead of actually drawing?
+ # TODO: test strokeStyle, shadowColor in the same way
+ test = {
+ 'name': '2d.fillStyle.parse.%s' % name,
+ 'notes': notes,
+ 'code': """
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '%s';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == %d,%d,%d,%d;
+ t.done();
+ """ % (string, r,g,b,a),
+ }
+ tests.append(test)
+
+ # Also test that invalid colors are ignored
+ for name, string in [
+ ('hex1', '#f'),
+ ('hex2', '#f0'),
+ ('hex3', '#g00'),
+ ('hex4', '#fg00'),
+ ('hex5', '#ff000'),
+ ('hex6', '#fg0000'),
+ ('hex7', '#ff0000f'),
+ ('hex8', '#fg0000ff'),
+ ('rgb-1', 'rgb(255.0, 0, 0,)'),
+ ('rgb-2', 'rgb(100%, 0, 0)'),
+ ('rgb-3', 'rgb(255, - 1, 0)'),
+ ('rgba-1', 'rgba(100%, 0, 0, 1)'),
+ ('rgba-2', 'rgba(255, 0, 0, 1. 0)'),
+ ('rgba-3', 'rgba(255, 0, 0, 1.)'),
+ ('rgba-4', 'rgba(255, 0, 0, '),
+ ('rgba-5', 'rgba(255, 0, 0, 1,)'),
+ ('hsl-1', 'hsl(0%, 100%, 50%)'),
+ ('hsl-2', 'hsl(z, 100%, 50%)'),
+ ('hsl-3', 'hsl(0, 0, 50%)'),
+ ('hsl-4', 'hsl(0, 100%, 0)'),
+ ('hsl-5', 'hsl(0, 100.%, 50%)'),
+ ('hsl-6', 'hsl(0, 100%, 50%,)'),
+ ('hsla-1', 'hsla(0%, 100%, 50%, 1)'),
+ ('hsla-2', 'hsla(0, 0, 50%, 1)'),
+ ('hsla-3', 'hsla(0, 0, 50%, 1,)'),
+ ('name-1', 'darkbrown'),
+ ('name-2', 'firebrick1'),
+ ('name-3', 'red blue'),
+ ('name-4', '"red"'),
+ ('name-5', '"red'),
+ # css-color-4 color function
+ # comma and comma-less expressions should not mix together.
+ ('css-color-4-rgb-1', 'rgb(255, 0, 0 / 1)'),
+ ('css-color-4-rgb-2', 'rgb(255 0 0, 1)'),
+ ('css-color-4-rgb-3', 'rgb(255, 0 0)'),
+ ('css-color-4-rgba-1', 'rgba(255, 0, 0 / 1)'),
+ ('css-color-4-rgba-2', 'rgba(255 0 0, 1)'),
+ ('css-color-4-rgba-3', 'rgba(255, 0 0)'),
+ ('css-color-4-hsl-1', 'hsl(0, 100%, 50% / 1)'),
+ ('css-color-4-hsl-2', 'hsl(0 100% 50%, 1)'),
+ ('css-color-4-hsl-3', 'hsl(0, 100% 50%)'),
+ ('css-color-4-hsla-1', 'hsla(0, 100%, 50% / 1)'),
+ ('css-color-4-hsla-2', 'hsla(0 100% 50%, 1)'),
+ ('css-color-4-hsla-3', 'hsla(0, 100% 50%)'),
+ # trailing slash
+ ('css-color-4-rgb-4', 'rgb(0 0 0 /)'),
+ ('css-color-4-rgb-5', 'rgb(0, 0, 0 /)'),
+ ('css-color-4-hsl-4', 'hsl(0 100% 50% /)'),
+ ('css-color-4-hsl-5', 'hsl(0, 100%, 50% /)'),
+ ]:
+ test = {
+ 'name': '2d.fillStyle.parse.invalid.%s' % name,
+ 'code': """
+ ctx.fillStyle = '#0f0';
+ try { ctx.fillStyle = '%s'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ t.done();
+ """ % string,
+ }
+ tests.append(test)
+
+ # Some can't have positive tests, only negative tests, because we don't know what color they're meant to be
+ for name, string in [
+ ('system', 'ThreeDDarkShadow'),
+ #('flavor', 'flavor'), # removed from latest CSS3 Color drafts
+ ]:
+ test = {
+ 'name': '2d.fillStyle.parse.%s' % name,
+ 'code': """
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = '%s';
+ @assert ctx.fillStyle =~ /^#(?!(FF0000|ff0000|f00)$)/; // test that it's not red
+ t.done();
+ """ % (string,),
+ }
+ tests.append(test)
+
+- meta: |
+ cases = [
+ ("zero", "0", 0),
+ ("empty", "", 0),
+ ("onlyspace", " ", 0),
+ ("space", " 100", 100),
+ ("whitespace", "\t\f100", 100),
+ ("plus", "+100", 100),
+ ("minus", "-100", "exception"),
+ ("octal", "0100", 100),
+ ("hex", "0x100", 0x100),
+ ("exp", "100e1", 100e1),
+ ("decimal", "100.999", 100),
+ ("percent", "100%", "exception"),
+ ("em", "100em", "exception"),
+ ("junk", "#!?", "exception"),
+ ("trailingjunk", "100#!?", "exception"),
+ ]
+ def gen(name, string, exp, code):
+ if exp is None:
+ code += "canvas.width = '%s';\ncanvas.height = '%s';\n" % (string, string)
+ code += "@assert canvas.width === 100;\n@assert canvas.height === 50;\n"
+ expected = None
+ elif exp == "exception":
+ code += "@assert throws TypeError canvas.width = '%s';\n" % string
+ expected = None
+ else:
+ code += "canvas.width = '%s';\ncanvas.height = '%s';\n" % (string, string)
+ code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp)
+ expected = None
+
+ code += "t.done();\n"
+
+ if exp == 0:
+ expected = None # can't generate zero-sized PNGs for the expected image
+
+ return code, expected
+
+ for name, string, exp in cases:
+ code = ""
+ code, expected = gen(name, string, exp, code)
+ tests.append( {
+ "name": "2d.canvas.host.size.attributes.parse.%s" % name,
+ "desc": "Parsing of non-negative integers",
+ "code": code,
+ } )
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/the-offscreen-canvas.yaml b/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/the-offscreen-canvas.yaml
new file mode 100644
index 0000000000..ccabe094b8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/tools/yaml/offscreen/the-offscreen-canvas.yaml
@@ -0,0 +1,269 @@
+- name: 2d.canvas.host.reference
+ desc: canvas refers back to its canvas
+ code: |
+ @assert ctx.canvas === canvas;
+ t.done();
+
+- name: 2d.canvas.host.readonly
+ desc: canvas is readonly
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ var d = ctx.canvas;
+ @assert offscreenCanvas2 !== d;
+ ctx.canvas = offscreenCanvas2;
+ @assert ctx.canvas === d;
+ t.done();
+
+- name: 2d.canvas.context.exists
+ desc: The 2D context is implemented
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert offscreenCanvas2.getContext('2d') !== null;
+ t.done();
+
+- name: 2d.canvas.context.extraargs.create
+ desc: The 2D context doesn't throw with extra getContext arguments (new context)
+ code: |
+ @assert (new OffscreenCanvas(100, 50)).getContext('2d', false, {}, [], 1, "2") !== null;
+ @assert (new OffscreenCanvas(100, 50)).getContext('2d', 123) !== null;
+ @assert (new OffscreenCanvas(100, 50)).getContext('2d', "test") !== null;
+ @assert (new OffscreenCanvas(100, 50)).getContext('2d', undefined) !== null;
+ @assert (new OffscreenCanvas(100, 50)).getContext('2d', null) !== null;
+ @assert (new OffscreenCanvas(100, 50)).getContext('2d', Symbol.hasInstance) !== null;
+ t.done();
+
+- name: 2d.canvas.context.extraargs.cache
+ desc: The 2D context doesn't throw with extra getContext arguments (cached)
+ code: |
+ @assert canvas.getContext('2d', false, {}, [], 1, "2") !== null;
+ @assert canvas.getContext('2d', 123) !== null;
+ @assert canvas.getContext('2d', "test") !== null;
+ @assert canvas.getContext('2d', undefined) !== null;
+ @assert canvas.getContext('2d', null) !== null;
+ @assert canvas.getContext('2d', Symbol.hasInstance) !== null;
+ t.done();
+
+- name: 2d.canvas.context.unique
+ desc: getContext('2d') returns the same object
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert offscreenCanvas2.getContext('2d') === offscreenCanvas2.getContext('2d');
+ t.done();
+
+- name: 2d.canvas.context.shared
+ desc: getContext('2d') returns objects which share canvas state
+ code: |
+ var ctx2 = canvas.getContext('2d');
+ ctx.fillStyle = '#f00';
+ ctx2.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ t.done();
+
+- name: 2d.canvas.context.emptystring
+ desc: getContext with empty string returns null
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert throws TypeError offscreenCanvas2.getContext("");
+ t.done();
+
+- name: 2d.canvas.context.unrecognised.badname
+ desc: getContext with unrecognised context name returns null
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert throws TypeError offscreenCanvas2.getContext('This is not an implemented context in any real browser');
+ t.done();
+
+- name: 2d.canvas.context.unrecognised.badsuffix
+ desc: Context name "2d" plus a suffix is unrecognised
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert throws TypeError offscreenCanvas2.getContext("2d#");
+ t.done();
+
+- name: 2d.canvas.context.unrecognised.nullsuffix
+ desc: Context name "2d" plus a "\0" suffix is unrecognised
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert throws TypeError offscreenCanvas2.getContext("2d\0");
+ t.done();
+
+- name: 2d.canvas.context.unrecognised.unicode
+ desc: Context name which kind of looks like "2d" is unrecognised
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert throws TypeError offscreenCanvas2.getContext("2\uFF44");
+ t.done();
+
+- name: 2d.canvas.context.casesensitive
+ desc: Context name "2D" is unrecognised; matching is case sensitive
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert throws TypeError offscreenCanvas2.getContext('2D');
+ t.done();
+
+- name: 2d.canvas.context.arguments.missing
+ code: |
+ var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+ @assert throws TypeError offscreenCanvas2.getContext(); @moz-todo
+ t.done();
+
+
+- name: 2d.canvas.host.initial.color
+ desc: Initial state is transparent black
+ code: |
+ @assert pixel 20,20 == 0,0,0,0;
+ t.done();
+
+- name: 2d.canvas.host.initial.reset.different
+ desc: Changing size resets canvas to transparent black
+ code: |
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ @assert pixel 20,20 == 255,0,0,255;
+ canvas.width = 50;
+ @assert pixel 20,20 == 0,0,0,0;
+ t.done();
+
+- name: 2d.canvas.host.initial.reset.same
+ desc: Setting size (not changing the value) resets canvas to transparent black
+ code: |
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 50, 50);
+ @assert pixel 20,20 == 255,0,0,255;
+ canvas.width = 100;
+ @assert pixel 20,20 == 0,0,0,0;
+ t.done();
+
+- name: 2d.canvas.host.initial.reset.path
+ desc: Resetting the canvas state resets the current path
+ code: |
+ canvas.width = 100;
+ ctx.rect(0, 0, 100, 50);
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fill();
+ @assert pixel 20,20 == 0,0,0,0;
+ t.done();
+
+- name: 2d.canvas.host.initial.reset.clip
+ desc: Resetting the canvas state resets the current clip region
+ code: |
+ canvas.width = 100;
+ ctx.rect(0, 0, 1, 1);
+ ctx.clip();
+ canvas.width = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 20,20 == 0,255,0,255;
+ t.done();
+
+- name: 2d.canvas.host.initial.reset.transform
+ desc: Resetting the canvas state resets the current transformation matrix
+ code: |
+ canvas.width = 100;
+ ctx.scale(0.1, 0.1);
+ canvas.width = 100;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 20,20 == 0,255,0,255;
+ t.done();
+
+- name: 2d.canvas.host.initial.reset.gradient
+ desc: Resetting the canvas state does not invalidate any existing gradients
+ code: |
+ canvas.width = 50;
+ var g = ctx.createLinearGradient(0, 0, 100, 0);
+ g.addColorStop(0, '#0f0');
+ g.addColorStop(1, '#0f0');
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ t.done();
+
+- name: 2d.canvas.host.initial.reset.pattern
+ desc: Resetting the canvas state does not invalidate any existing patterns
+ code: |
+ canvas.width = 30;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 30, 50);
+ var p = ctx.createPattern(canvas, 'repeat-x');
+ canvas.width = 100;
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = p;
+ ctx.fillRect(0, 0, 100, 50);
+ @assert pixel 50,25 == 0,255,0,255;
+ t.done();
+
+- name: 2d.canvas.host.size.attributes.idl.set.zero
+ desc: Setting width/height IDL attributes to 0
+ code: |
+ canvas.width = 0;
+ canvas.height = 0;
+ @assert canvas.width === 0;
+ @assert canvas.height === 0;
+ t.done();
+
+- name: 2d.canvas.host.size.attributes.idl
+ desc: Getting/setting width/height IDL attributes
+ webidl:
+ - es-unsigned-long
+ code: |
+ canvas.width = "100";
+ canvas.height = "100";
+ @assert canvas.width === 100;
+ @assert canvas.height === 100;
+ canvas.width = "+1.5e2";
+ canvas.height = "0x96";
+ @assert canvas.width === 150;
+ @assert canvas.height === 150;
+ canvas.width = 301.999;
+ canvas.height = 301.001;
+ @assert canvas.width === 301;
+ @assert canvas.height === 301;
+ @assert throws TypeError canvas.width = "400x";
+ @assert throws TypeError canvas.height = "foo";
+ @assert canvas.width === 301;
+ @assert canvas.height === 301;
+ t.done();
+
+- name: 2d.canvas.host.size.attributes.default
+ desc: Default width/height when attributes are missing
+ code: |
+ @assert canvas.width === 100;
+ @assert canvas.height === 50;
+ t.done();
+
+- name: 2d.canvas.host.size.attributes.reflect.setidl
+ desc: Setting IDL attributes updates IDL and content attributes
+ code: |
+ canvas.width = 120;
+ canvas.height = 60;
+ @assert canvas.width === 120;
+ @assert canvas.height === 60;
+ t.done();
+
+- name: 2d.canvas.host.size.attributes.reflect.setidlzero
+ desc: Setting IDL attributes to 0 updates IDL and content attributes
+ code: |
+ canvas.width = 0;
+ canvas.height = 0;
+ @assert canvas.width === 0;
+ @assert canvas.height === 0;
+ t.done();
+
+- name: 2d.canvas.host.size.large
+ notes: Not sure how reasonable this is, but the spec doesn't say there's an upper
+ limit on the size.
+ code: |
+ var n = 2147483647; // 2^31 - 1, which should be supported by any sensible definition of "long"
+ canvas.width = n;
+ canvas.height = n;
+ @assert canvas.width === n;
+ @assert canvas.height === n;
+ t.done();
diff --git a/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html
new file mode 100644
index 0000000000..837fa438a3
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Popup Cross-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+ Verifies that element.requestFullscreen() calls from a cross-origin popup without user activation
+ work if and only if the opener has user activation and it delegates the capability.
+
+ https://wicg.github.io/capability-delegation/spec.html
+</div>
+
+<script>
+ let popup = null;
+
+ function testCrossOriginPopupFullscreenDelegation(capability, activate, expectation) {
+ const message = {"type": "make-fullscreen-request"};
+ const origin = "https://{{hosts[alt][www]}}:{{ports[https][0]}}";
+ const expectationType = expectation ? "succeeds" : "fails";
+ const delegationType = capability ? "with delegation" : "without delegation";
+ const activationType = activate ? "with user activation" : "with no user activation";
+
+ promise_test(async () => {
+ const data = await postCapabilityDelegationMessage(popup, message, origin, capability, activate);
+ assert_equals(data.result, expectation ? "success" : "failure");
+ }, `Fullscreen requests from a cross-origin popup ${expectationType} ${delegationType} from an opener ${activationType}`);
+ }
+
+ promise_setup(async () => {
+ // Make sure the recipient popup has loaded.
+ popup = window.open("https://{{hosts[alt][www]}}:{{ports[https][0]}}/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html",
+ "", "width=300,height=200");
+ return getMessageData("recipient-loaded", popup);
+ });
+
+ testCrossOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+ testCrossOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/false);
+ testCrossOriginPopupFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html
new file mode 100644
index 0000000000..42bb8703d8
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Popup Same-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+ Verifies that element.requestFullscreen() calls from a same-origin popup without user activation
+ work if and only if the opener has user activation and it delegates the capability.
+
+ https://wicg.github.io/capability-delegation/spec.html
+</div>
+
+<script>
+ let popup = null;
+
+ function testSameOriginPopupFullscreenDelegation(capability, activate, expectation) {
+ const message = {"type": "make-fullscreen-request"};
+ const expectationType = expectation ? "succeeds" : "fails";
+ const delegationType = capability ? "with delegation" : "without delegation";
+ const activationType = activate ? "with user activation" : "with no user activation";
+
+ promise_test(async () => {
+ const data = await postCapabilityDelegationMessage(popup, message, location.origin, capability, activate);
+ assert_equals(data.result, expectation ? "success" : "failure");
+ }, `Fullscreen requests from a same-origin popup ${expectationType} ${delegationType} from an opener ${activationType}`);
+ }
+
+ promise_setup(async () => {
+ // Make sure the recipient popup has loaded.
+ popup = window.open("./resources/delegate-fullscreen-request-recipient.html",
+ "", "width=300,height=200");
+ return getMessageData("recipient-loaded", popup);
+ });
+
+ testSameOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+ testSameOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/false);
+ testSameOriginPopupFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html
new file mode 100644
index 0000000000..517860c896
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Subframe Cross-Origin</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+ Verifies that element.requestFullscreen() calls from a cross-origin subframe without user
+ activation work if and only if the top frame has user activation and it delegates the capability.
+
+ https://wicg.github.io/capability-delegation/spec.html
+
+ See wpt/html/user-activation/propagation*.html for frame tree user activation visibility tests.
+</div>
+
+<iframe allow="fullscreen" width="300px" height="50px">
+</iframe>
+
+<script>
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ document.querySelector("iframe").src = origin + "/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html";
+
+ function testCrossOriginSubframeFullscreenDelegation(capability, activate, expectation) {
+ const message = {"type": "make-fullscreen-request"};
+ const expectationType = expectation ? "succeeds" : "fails";
+ const delegationType = capability ? "with delegation" : "without delegation";
+ const activationType = activate ? "with user activation" : "with no user activation";
+
+ promise_test(async () => {
+ const data = await postCapabilityDelegationMessage(frames[0], message, origin, capability, activate);
+ assert_equals(data.result, expectation ? "success" : "failure");
+ }, `Fullscreen requests from a cross-origin subframe ${expectationType} ${delegationType} from an opener ${activationType}`);
+ }
+
+ promise_setup(async () => {
+ // Make sure the recipient iframe has loaded.
+ return getMessageData("recipient-loaded", frames[0]);
+ });
+
+ testCrossOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+ testCrossOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/false);
+ testCrossOriginSubframeFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html
new file mode 100644
index 0000000000..16cbbfd3e9
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Subframe Same-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+ Verifies that element.requestFullscreen() calls from a same-origin subframe without user
+ activation work if and only if the top frame has user activation, regardless of whether it
+ delegates the capability or not.
+
+ https://wicg.github.io/capability-delegation/spec.html
+
+ See wpt/html/user-activation/propagation*.html for frame tree user activation visibility tests.
+</div>
+
+<iframe allow="fullscreen" width="300px" height="50px"
+ src="./resources/delegate-fullscreen-request-recipient.html">
+</iframe>
+
+<script>
+ function testSameOriginSubframeFullscreenDelegation(capability, activate, expectation) {
+ const message = {"type": "make-fullscreen-request"};
+ const expectationType = expectation ? "succeeds" : "fails";
+ const delegationType = capability ? "with delegation" : "without delegation";
+ const activationType = activate ? "with user activation" : "with no user activation";
+
+ promise_test(async () => {
+ const data = await postCapabilityDelegationMessage(frames[0], message, window.location, capability, activate);
+ assert_equals(data.result, expectation ? "success" : "failure");
+ }, `Fullscreen requests from a same-origin subframe ${expectationType} ${delegationType} from an opener ${activationType}`);
+ }
+
+ promise_setup(async () => {
+ // Make sure the recipient iframe has loaded.
+ return getMessageData("recipient-loaded", frames[0]);
+ });
+
+ testSameOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+ testSameOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/true);
+ testSameOriginSubframeFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/testing/web-platform/tests/html/capability-delegation/delegation-consumes-activation.https.tentative.html b/testing/web-platform/tests/html/capability-delegation/delegation-consumes-activation.https.tentative.html
new file mode 100644
index 0000000000..a538f29493
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/delegation-consumes-activation.https.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/whatwg/html/issues/4008
+-->
+<title>Capability Delegation: Consumes User Activation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+ Test that capability delegation consumes transient user activation.
+
+ https://wicg.github.io/capability-delegation/spec.html
+</div>
+
+<iframe width="300px" height="50px"></iframe>
+
+<script>
+ function sendCapabilityDelegationMessageIgnoringException(origin, capability) {
+ try {
+ frames[0].postMessage("any_message", {targetOrigin: origin, delegate: capability});
+ } catch (e) {}
+ }
+
+ let capability_to_delegate;
+
+ promise_setup(async () => {
+ capability_to_delegate = await findOneCapabilitySupportingDelegation();
+ assert_true(!!capability_to_delegate, "The user agent supports delegating at least one capability");
+ });
+
+ promise_test(async () => {
+ assert_false(navigator.userActivation.isActive);
+
+ await test_driver.bless();
+ assert_true(navigator.userActivation.isActive, "User activation is available initially");
+
+ sendCapabilityDelegationMessageIgnoringException("/", "blah");
+ assert_true(navigator.userActivation.isActive,
+ "User activation is not consumed by delegation of an unknown delegation");
+
+ sendCapabilityDelegationMessageIgnoringException("*", capability_to_delegate);
+ assert_true(navigator.userActivation.isActive,
+ "User activation is not consumed by known delegation to disallowed targetOrigin");
+
+ sendCapabilityDelegationMessageIgnoringException("/", capability_to_delegate);
+ assert_false(navigator.userActivation.isActive,
+ "User activation is consumed by supported delegation");
+
+ }, "Capability delegation consumes transient user activation");
+</script>
diff --git a/testing/web-platform/tests/html/capability-delegation/delegation-sender-checks.tentative.html b/testing/web-platform/tests/html/capability-delegation/delegation-sender-checks.tentative.html
new file mode 100644
index 0000000000..4fa8a2d433
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/delegation-sender-checks.tentative.html
@@ -0,0 +1,60 @@
+!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/WICG/capability-delegation
+-->
+<title>Capability Delegation sender checks</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+ Verifies that capability delegation related error checks in <a
+ href="https://wicg.github.io/capability-delegation/spec.html#monkey-patch-to-html-initiating-delegation">HTML
+ postMessage algorithm</a> are enforced correctly.
+</div>
+
+<iframe width="300px" height="50px"></iframe>
+
+<script>
+ const frame = frames[0];
+ const message = "any_message";
+ const activate = false;
+
+ let capability_to_delegate;
+
+ promise_setup(async () => {
+ capability_to_delegate = await findOneCapabilitySupportingDelegation();
+ assert_true(!!capability_to_delegate, "The user agent supports delegating at least one capability");
+ });
+
+ promise_test(async () => {
+ try {
+ await postCapabilityDelegationMessage(frame, message, "/", "blah", activate);
+ assert_unreached();
+ } catch (exception) {
+ assert_equals(exception.name, "NotSupportedError");
+ }
+ }, "Delegating an unsupported capability throws an exception");
+
+ promise_test(async () => {
+ try {
+ await postCapabilityDelegationMessage(frame, message, "*", capability_to_delegate, activate);
+ assert_unreached();
+ } catch (exception) {
+ assert_equals(exception.name, "NotAllowedError");
+ }
+ }, "Delegating to targetOrigin='*' throws an exception");
+
+ promise_test(async () => {
+ try {
+ await postCapabilityDelegationMessage(frame, message, "/", capability_to_delegate, activate);
+ assert_unreached();
+ } catch (exception) {
+ assert_equals(exception.name, "NotAllowedError");
+ }
+ }, "Delegating without user activation throws an exception");
+</script>
diff --git a/testing/web-platform/tests/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html b/testing/web-platform/tests/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html
new file mode 100644
index 0000000000..11daf738d6
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Capability Delegation of Fullscreen Requests test recipient</title>
+<body>Capability Delegation of Fullscreen Requests test recipient body</body>
+
+<script>
+ const initiator = window.opener ? window.opener : window.top;
+ initiator.postMessage({"type": "recipient-loaded"}, "*");
+
+ function reportResult(msg) {
+ initiator.postMessage({"type": "result", "result": msg}, "*");
+ }
+
+ document.addEventListener('fullscreenchange', async () => {
+ if (document.fullscreenElement) {
+ await document.exitFullscreen();
+ reportResult("success");
+ }
+ });
+
+ document.addEventListener('fullscreenerror', () => {
+ reportResult("failure");
+ });
+
+ window.addEventListener("message", e => {
+ if (e.data.type == "make-fullscreen-request") {
+ document.body.requestFullscreen();
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/capability-delegation/resources/utils.js b/testing/web-platform/tests/html/capability-delegation/resources/utils.js
new file mode 100644
index 0000000000..575028763e
--- /dev/null
+++ b/testing/web-platform/tests/html/capability-delegation/resources/utils.js
@@ -0,0 +1,55 @@
+// Returns a Promise that gets resolved with `event.data` when `window` receives from `source` a
+// "message" event whose `event.data.type` matches the string `message_data_type`.
+function getMessageData(message_data_type, source) {
+ return new Promise(resolve => {
+ function waitAndRemove(e) {
+ if (e.source != source || !e.data || e.data.type != message_data_type)
+ return;
+ window.removeEventListener("message", waitAndRemove);
+ resolve(e.data);
+ }
+ window.addEventListener("message", waitAndRemove);
+ });
+}
+
+// A helper that simulates user activation on the current frame if `activate` is true, then posts
+// `message` to `frame` with the target `origin` and specified `capability` to delegate. This helper
+// awaits and returns a Promise fulfilled with the result message sent in reply from `frame`.
+// However, if the `postMessage` call fails, the helper returns a Promise rejected with the
+// exception.
+async function postCapabilityDelegationMessage(frame, message, origin, capability, activate) {
+ let result_promise = getMessageData("result", frame);
+
+ if (activate)
+ await test_driver.bless();
+
+ let postMessageOptions = {targetOrigin: origin};
+ if (capability)
+ postMessageOptions["delegate"] = capability;
+ try {
+ frame.postMessage(message, postMessageOptions);
+ } catch (exception) {
+ return Promise.reject(exception);
+ }
+
+ return await result_promise;
+}
+
+// Returns the name of a capability for which `postMessage` delegation is supported by the user
+// agent, or undefined if no such capability is found.
+async function findOneCapabilitySupportingDelegation() {
+ const capabilities = ["fullscreen", "payment", "display-capture"];
+
+ for (let i = 0; i < capabilities.length; i++) {
+ try {
+ await postCapabilityDelegationMessage(window, "any_message", "/", capabilities[i], false);
+ assert_unreached();
+ } catch (exception) {
+ if (exception.name != "NotSupportedError")
+ return capabilities[i];
+ // Ignore all other exceptions to continue searching through the list.
+ }
+ };
+
+ return undefined;
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/META.yml b/testing/web-platform/tests/html/cross-origin-embedder-policy/META.yml
new file mode 100644
index 0000000000..dc7010880b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/META.yml
@@ -0,0 +1,9 @@
+spec: https://html.spec.whatwg.org/multipage/origin.html#coep
+suggested_reviewers:
+ - mikewest
+ - jugglinmike
+ - arturjanc
+ - lweichselbaum
+ - hemeryar
+ - ParisMeuleman
+ - valenting
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/README.md b/testing/web-platform/tests/html/cross-origin-embedder-policy/README.md
new file mode 100644
index 0000000000..16179eb013
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/README.md
@@ -0,0 +1 @@
+See `../cross-origin-opener-policy/README.md`.
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html
new file mode 100644
index 0000000000..2dc73c7561
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/script-factory.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script>
+ const origins = get_host_info();
+
+ promise_test(async t => {
+ const popup = window.open();
+ t.add_cleanup(() => { popup.close(); });
+
+ let data_from_popup = () => new Promise(resolve =>
+ window.addEventListener("message", (({ data }) => resolve(data))));
+
+ let check_result = (data, text) => {
+ assert_equals(data.origin, origin);
+ assert_true(data.sameOriginNoCORPSuccess,
+ text + ": Same-origin without CORP did not succeed");
+ assert_true(data.crossOriginNoCORPFailure,
+ text + ": Cross-origin without CORP did not fail");
+ };
+
+ // Check if COEP is inherited by the popup.
+ let script = popup.document.createElement('script');
+ script.innerHTML =
+ `${createScript(window.origin, origins.HTTPS_REMOTE_ORIGIN, "opener")}`;
+ popup.document.body.appendChild(script);
+ check_result(await data_from_popup(), "Initial empty document");
+
+ // Navigate the popup away.
+ popup.location = origins.HTTPS_REMOTE_ORIGIN +
+ "/html/cross-origin-embedder-policy/resources/postmessage-ready.html";
+ assert_equals(await new Promise(resolve =>
+ window.addEventListener("message", msg => resolve(msg.data))),
+ "ready");
+
+ // Navigate the popup to about:blank and wait for it.
+ popup.location = "about:blank";
+ await t.step_wait(
+ condition = () => {
+ try {
+ return popup.location.href === "about:blank";
+ } catch {}
+ return false;
+ },
+ description = "Wait for the popup to navigate.",
+ timeout=3000,
+ interval=50);
+
+ // Check again if COEP is inherited.
+ script = popup.document.createElement('script');
+ script.innerHTML =
+ `${createScript(window.origin, origins.HTTPS_REMOTE_ORIGIN, "opener")}`;
+ popup.document.body.appendChild(script);
+ check_result(await data_from_popup(), "Non-initial about:blank document");
+ }, `Cross-Origin-Embedder-Policy is inherited by about:blank popup.`);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/about-blank-popup.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html
new file mode 100644
index 0000000000..ce72f247ef
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<div id=log></div>
+<script>
+const origins = get_host_info();
+[
+ {
+ "origin": origins.HTTPS_ORIGIN,
+ "crossOrigin": origins.HTTPS_REMOTE_ORIGIN
+ },
+ {
+ "origin": origins.HTTPS_REMOTE_ORIGIN,
+ "crossOrigin": origins.HTTPS_NOTSAMESITE_ORIGIN
+ },
+ {
+ "origin": origins.HTTPS_NOTSAMESITE_ORIGIN,
+ "crossOrigin": origins.HTTPS_ORIGIN
+ }
+].forEach(({ origin, crossOrigin }) => {
+ ["subframe", "navigate", "popup"].forEach(variant => {
+ async_test(t => {
+ const id = token();
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => { frame.remove(); });
+ const path = new URL("resources/blob-url-factory.html", window.location).pathname;
+ frame.src = `${origin}${path}?id=${id}&variant=${variant}&crossOrigin=${crossOrigin}`;
+ window.addEventListener("message", t.step_func(({ data }) => {
+ if (data.id !== id) {
+ return;
+ }
+ assert_equals(data.origin, origin);
+ assert_true(data.sameOriginNoCORPSuccess, "Same-origin without CORP did not succeed");
+ assert_true(data.crossOriginNoCORPFailure, "Cross-origin without CORP did not fail");
+ t.done();
+ }));
+ document.body.append(frame);
+ }, `Cross-Origin-Embedder-Policy and blob: URL from ${origin} in subframe via ${variant}`);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/blob.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/block-local-documents-inheriting-none.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/block-local-documents-inheriting-none.https.html
new file mode 100644
index 0000000000..cf5176606e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/block-local-documents-inheriting-none.https.html
@@ -0,0 +1,112 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<div id=log></div>
+
+<script>
+const script = `
+ <script>
+ top.postMessage({event: "loaded", type: location.protocol}, "*");
+ <\/script>`;
+
+const test_cases = [
+ {name: "data", url: encodeURI(`data:text/html,${script}`)},
+ {name: "blob", url: URL.createObjectURL(new Blob([script], { type: "text/html" }))},
+ {name: "about", url: "about:blank"},
+ ];
+
+const observeReports = async (frame) => {
+ const reports = [];
+
+ const observer = new frame.contentWindow.ReportingObserver(
+ rs => reports.push(...rs.map(r => r.toJSON()))
+ );
+ observer.observe();
+
+ // Wait for reports. Use a timeout to catch both expected and unexpected
+ // reports.
+ await new Promise(resolve => step_timeout(resolve, 3000));
+ return reports;
+};
+
+promise_test(async t => {
+ const this_window_token = token();
+
+ // Expect the nested frame to not load, since they inherit COEP: none from the
+ // top frame, which is incompatible with first_frame's COEP: require-corp.
+ const received_events = [];
+ addEventListener("message", event => {
+ if(event.data.event == "loaded")
+ received_events.push(`Nested ${event.data.type} loaded!`);
+ });
+
+ // Create an iframe with COEP: require-corp
+ const first_iframe = document.createElement("iframe");
+ t.add_cleanup( () => first_iframe.remove() );
+ first_iframe.src = "/common/blank.html?pipe=header(cross-origin-embedder-policy,require-corp)";
+ let iframe_load_promise = new Promise( resolve => first_iframe.addEventListener("load", resolve) );
+
+ document.body.append(first_iframe);
+ await iframe_load_promise;
+
+ const reportPromise = observeReports(first_iframe);
+ // 1. Create nested frames.
+ // They initially navigate to blank.html and have COEP: require-corp set.
+ // This initial navigation is required because it uses the parent frame as the
+ // initiator. That is first_iframe is the initiator, while we want top to be
+ // the initiator for this test, which will be done in step 4 with a second
+ // navigation from that blank.html document to the local scheme one.
+ const nested_frames = {};
+ const nested_frames_promises = [];
+ test_cases.forEach(test => {
+ nested_frame = document.createElement("iframe");
+ nested_frame.src = "/common/blank.html?pipe=header(cross-origin-embedder-policy,require-corp)";
+ t.add_cleanup( () => nested_frame.remove() );
+ nested_frames_promises.push(new Promise( resolve => nested_frame.addEventListener("load", resolve) ) );
+ first_iframe.contentDocument.body.append(nested_frame);
+ nested_frames[test.name] = nested_frame;
+ });
+
+ // 2. Wait for the loads of all iframes to complete.
+ await Promise.all(nested_frames_promises);
+
+ // 3. Navigate a dummy frame. This is required because some browsers (Chrome)
+ // might consider the first navigation in 4. as a redirect otherwise.
+ const dummy_Frame = document.createElement("iframe");
+ t.add_cleanup( () => dummy_Frame.remove() );
+ dummy_Frame.src = "/common/blank.html";
+ iframe_load_promise = new Promise( resolve => dummy_Frame.addEventListener("load", resolve) );
+ document.body.append(dummy_Frame);
+ await iframe_load_promise;
+
+ // 4. Navigate nested frames to a local scheme document.
+ // COEP should be inherited from the initiator or blobURL's creator (top in both
+ // cases), this results in COEP being none and the documents not being allowed
+ // to load under the COEP: require-corp iframe (first_iframe).
+ test_cases.forEach(test => {
+ // Top navigates nested_frame_[test.name] to a test.url
+ const frame = nested_frames[test.name];
+ // Use frame.contentDocument.location to ensure the initiator is this (top)
+ // frame. frame.src is not used here as this makes the parent of the nested
+ // frame (first_iframe) the initiator.
+ frame.contentDocument.location = test.url;
+ });
+
+ // 5. Wait and validate reports.
+ const reports = await reportPromise;
+ assert_equals(reports.length, test_cases.length);
+ test_cases.forEach(test => {
+ assert_true(reports.some( report => {
+ return report.type == 'coep' &&
+ report.body.type == 'navigation' &&
+ report.body.blockedURL == test.url;
+ }), `No report matched for test "${test.name}"`);
+ });
+ // Also verify that no message was sent by the nested frames and stored in
+ // received_events.
+ assert_equals(received_events.length, 0);
+}, "Prevent local scheme documents from loading within a COEP: require-corp iframe if they inherit COEP: none");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
new file mode 100644
index 0000000000..f4b2599141
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<html>
+<head>
+ <title>
+ Check COEP report are send for CacheStorage requests in DedicatedWorker
+ </title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/common/utils.js"></script>
+ <script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+ </script>
+ <script src="./resources/cache-storage-reporting.js"> </script>
+</head>
+<script>
+
+promise_test(async (t) => {
+ const worker_url = local(encode(worker_path + header_coep));
+ const worker = new Worker(worker_url);
+ const mc = new MessageChannel();
+ worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+ const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "enforce");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, worker_url);
+}, "COEP support on DedicatedWorker.")
+
+promise_test(async (t) => {
+ const worker_url = local(encode(worker_path + header_coep_report_only));
+ const worker = new Worker(worker_url);
+ const mc = new MessageChannel();
+ worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+ const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, worker_url);
+}, "COEP-Report-Only support on DedicatedWorker.")
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
new file mode 100644
index 0000000000..b998ba7926
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+<head>
+ <title>
+ Check COEP report are send for CacheStorage requests in Document.
+ </title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/common/utils.js"></script>
+ </script>
+ <script src="./resources/cache-storage-reporting.js"></script>
+</head>
+<script>
+
+async function waitReports(frame) {
+ return await new Promise((resolve) => {
+ const observer = new frame.contentWindow.ReportingObserver((reports) => {
+ observer.disconnect();
+ resolve(reports.map(r => r.toJSON()));
+ });
+ observer.observe();
+
+ frame.contentWindow.eval(eval_script);
+ });
+}
+
+promise_test(async (t) => {
+ const iframe_url = local(encode(iframe_path + header_coep));
+ const iframe = await makeIframe(t, iframe_url);
+ const reports = await waitReports(iframe);
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "enforce");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, iframe_url);
+}, "COEP support on document.")
+
+promise_test(async (t) => {
+ const iframe_url = local(encode(iframe_path + header_coep_report_only));
+ const iframe = await makeIframe(t, iframe_url);
+ const reports = await waitReports(iframe);
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, iframe_url);
+}, "COEP-Report-Only support on document.")
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html
new file mode 100644
index 0000000000..96a328b2cc
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+<head>
+ <title>
+ Check COEP report are send for CacheStorage requests in ServiceWorker.
+ </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/common/utils.js"></script>
+ <script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+ </script>
+ <script src="./resources/cache-storage-reporting.js"></script>
+</head>
+<script>
+
+promise_test(async (t) => {
+ const worker_url = local(encode(worker_path + header_coep));
+ // As we don't want the service worker to control any page, generate a
+ // one-time scope.
+ const SCOPE = new URL(`resources/${token()}.html`, location).pathname;
+ const reg =
+ await service_worker_unregister_and_register(t, worker_url, SCOPE);
+ add_completion_callback(() => reg.unregister());
+ const worker = reg.installing || reg.waiting || reg.active;
+ const mc = new MessageChannel();
+ worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+ const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+ assert_not_equals(reports, 'TIMEOUT');
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "enforce");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, worker_url);
+}, "COEP support on ServiceWorker.");
+
+promise_test(async (t) => {
+ const worker_url = local(encode(worker_path + header_coep_report_only));
+ // As we don't want the service worker to control any page, generate a
+ // one-time scope.
+ const SCOPE = new URL(`resources/${token()}.html`, location).pathname;
+ const reg =
+ await service_worker_unregister_and_register(t, worker_url, SCOPE);
+ add_completion_callback(() => reg.unregister());
+ const worker = reg.installing || reg.waiting || reg.active;
+ const mc = new MessageChannel();
+ worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+ const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+ assert_not_equals(reports, 'TIMEOUT');
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, worker_url);
+}, "COEP-Report-Only support on ServiceWorker.");
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
new file mode 100644
index 0000000000..34af988fc6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+<head>
+ <title>
+ Check COEP report are send for CacheStorage requests in SharedWorker
+ </title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/common/utils.js"></script>
+ <script src="./resources/cache-storage-reporting.js"> </script>
+</head>
+<script>
+
+promise_test(async (t) => {
+ const worker_url = local(encode(worker_path + header_coep));
+ const worker = new SharedWorker(worker_url);
+ const mc = new MessageChannel();
+ worker.port.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+ const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "enforce");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, worker_url);
+}, "COEP support on SharedWorker.")
+
+promise_test(async (t) => {
+ const worker_url = local(encode(worker_path + header_coep_report_only));
+ const worker = new SharedWorker(worker_url);
+ const mc = new MessageChannel();
+ worker.port.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+ const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+ assert_equals(reports.length, 1);
+ const report = reports[0];
+ assert_equals(report.body.blockedURL, image_url);
+ assert_equals(report.body.type, "corp");
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.destination, "");
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, worker_url);
+}, "COEP-Report-Only support on SharedWorker.")
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/coep-frame-javascript.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/coep-frame-javascript.https.html
new file mode 100644
index 0000000000..089019dc2e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/coep-frame-javascript.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/script-factory.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(({ data }) => {
+ assert_equals(data.id, "");
+ assert_equals(data.origin, window.origin);
+ assert_true(data.sameOriginNoCORPSuccess);
+ assert_true(data.crossOriginNoCORPFailure, "Cross-origin without CORP did not fail");
+ }));
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = "resources/coep-frame.html";
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ frame.src = `javascript:${encodeURIComponent(createScript(window.origin, get_host_info().HTTPS_NOTSAMESITE_ORIGIN))}`;
+ });
+ document.body.append(frame);
+}, "Cross-Origin-Embedder-Policy frame and javascript: URLs");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/coep-on-response-from-service-worker.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/coep-on-response-from-service-worker.https.html
new file mode 100644
index 0000000000..b25b04ca4a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/coep-on-response-from-service-worker.https.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const FRAME_URL = 'resources/coep-frame.html'
+const SCOPE = new URL(FRAME_URL, location).pathname;
+const SCRIPT = 'resources/sw.js?';
+
+// This is similar to
+// none-sw-from-require-corp.https.html, but there is one difference:
+// In this file, the frame controlled by the service worker comes from
+// the service worker, but on none-sw-from-require-corp.https.html
+// the main document comes from the network directly. Hence the tests
+// here test whether COEP is set correctly for documents coming from
+// service workers.
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN + '/html/cross-origin-embedder-policy/');
+}
+
+let registration;
+let frame;
+
+promise_test(async (t) => {
+ registration = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ await wait_for_state(t, registration.installing, 'activated')
+ frame = await with_iframe(FRAME_URL);
+}, 'setup');
+
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await w.fetch('resources/nothing-same-origin-corp.txt', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await w.fetch('/common/blank.html', {mode: 'no-cors'});
+}, 'making a same-origin request for no CORP');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await w.fetch('resources/nothing-cross-origin-corp.js', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await promise_rejects_js(
+ t, w.TypeError,
+ w.fetch(remote('resources/nothing-same-origin-corp.txt'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await promise_rejects_js(
+ t, w.TypeError, w.fetch(remote('/common/blank.html'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for no CORP');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await w.fetch(
+ remote('resources/nothing-cross-origin-corp.js'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await promise_rejects_js(
+ t, w.TypeError,
+ w.fetch(remote('resources/nothing-same-origin-corp.txt?passthrough'),
+ {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await promise_rejects_js(
+ t, w.TypeError,
+ w.fetch(remote('/common/blank.html?passthrough'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for no CORP [PASS THROUGH]');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await w.fetch(
+ remote('resources/nothing-cross-origin-corp.js?passthrough'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ await promise_rejects_js(
+ t, w.TypeError, w.fetch(remote('/common/blank.html'), {mode: 'cors'}));
+}, 'making a cross-origin request with CORS without ACAO');
+
+promise_test(async (t) => {
+ const w = frame.contentWindow;
+ const URL = remote(
+ '/common/blank.html?pipe=header(access-control-allow-origin,*');
+ await w.fetch(URL, {mode: 'cors'});
+}, 'making a cross-origin request with CORS');
+
+promise_test(async () => {
+ frame.remove();
+ await registration.unregister();
+}, 'teardown');
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/META.yml b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/META.yml
new file mode 100644
index 0000000000..2bf6754a6b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/META.yml
@@ -0,0 +1,7 @@
+spec: To be defined.
+suggested_reviewers:
+ - annevk
+ - arthursonzogni
+ - arturjanc
+ - camillelamy
+ - mikewest
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/README.md b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/README.md
new file mode 100644
index 0000000000..86654525dd
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/README.md
@@ -0,0 +1,3 @@
+# Related documents:
+- https://github.com/mikewest/credentiallessness/
+- https://github.com/w3ctag/design-reviews/issues/582
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache-storage.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache-storage.https.window.js
new file mode 100644
index 0000000000..573e6ac9cb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache-storage.https.window.js
@@ -0,0 +1,150 @@
+// META: timeout=long
+// META: variant=?document
+// META: variant=?dedicated_worker
+// META: variant=?shared_worker
+// META: variant=?service_worker
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+// Fetch a resource and store it into CacheStorage from |storer| context. Then
+// check if it can be retrieved via CacheStorage.match from |retriever| context.
+const cacheStorageTest = (
+ description,
+ storer,
+ retriever,
+ resource_headers,
+ request_credential_mode,
+ expectation
+) => {
+ promise_test_parallel(async test => {
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const url = cross_origin + "/common/square.png?pipe=" + resource_headers +
+ `&${token()}`;
+ const this_token = token();
+
+ // Fetch a request from |stored|. Store the opaque response into
+ // CacheStorage.
+ send(storer, `
+ const cache = await caches.open("v1");
+ const fetch_request = new Request("${url}", {
+ mode: 'no-cors',
+ credentials: '${request_credential_mode}'
+ });
+ const fetch_response = await fetch(fetch_request);
+ await cache.put(fetch_request, fetch_response);
+ send("${this_token}", "stored");
+ `);
+ assert_equals(await receive(this_token), "stored");
+
+ // Retrieved it from |retriever|.
+ send(retriever, `
+ const cache = await caches.open("v1");
+ try {
+ const response = await cache.match("${url}");
+ send("${this_token}", "retrieved");
+ } catch (error) {
+ send("${this_token}", "error");
+ }
+ `);
+ assert_equals(await receive(this_token), expectation);
+ }, description);
+};
+
+// Execute the same set of tests for every type of execution contexts:
+// Documents, DedicatedWorkers, SharedWorkers, and ServiceWorkers. The results
+// should be independent of the context.
+const environment = location.search.substr(1);
+const constructor = environments[environment];
+
+const context_none = constructor(coep_none)[0];
+const context_credentialless = constructor(coep_credentialless)[0];
+const context_require_corp = constructor(coep_require_corp)[0];
+
+cacheStorageTest(`[${environment}] unsafe-none => unsafe-none`,
+ context_none,
+ context_none,
+ "",
+ "include",
+ "retrieved");
+cacheStorageTest(`[${environment}] unsafe-none => credentialless`,
+ context_none,
+ context_credentialless,
+ "",
+ "include",
+ "error");
+cacheStorageTest(`[${environment}] unsafe-none => credentialless (omit)`,
+ context_none,
+ context_credentialless,
+ "",
+ "omit",
+ "retrieved");
+cacheStorageTest(`[${environment}] unsafe-none => credentialless + CORP`,
+ context_none,
+ context_credentialless,
+ corp_cross_origin,
+ "include",
+ "retrieved");
+cacheStorageTest(`[${environment}] unsafe-none => require-corp`,
+ context_none,
+ context_require_corp,
+ "",
+ "include",
+ "error");
+cacheStorageTest(`[${environment}] unsafe-none => require-corp (omit)`,
+ context_none,
+ context_require_corp,
+ "",
+ "include",
+ "error");
+cacheStorageTest(`[${environment}] unsafe-none => require-corp + CORP`,
+ context_none,
+ context_require_corp,
+ corp_cross_origin,
+ "include",
+ "retrieved");
+
+cacheStorageTest(`[${environment}] credentialless => unsafe-none`,
+ context_credentialless,
+ context_none,
+ "",
+ "include",
+ "retrieved");
+cacheStorageTest(`[${environment}] credentialless => credentialless`,
+ context_credentialless,
+ context_credentialless,
+ "",
+ "include",
+ "retrieved");
+cacheStorageTest(`[${environment}] credentialless => require-corp`,
+ context_credentialless,
+ context_require_corp,
+ "",
+ "include",
+ "error");
+cacheStorageTest(`[${environment}] credentialless => require-corp + CORP`,
+ context_credentialless,
+ context_require_corp,
+ corp_cross_origin,
+ "include",
+ "retrieved");
+
+cacheStorageTest(`[${environment}] require_corp => unsafe-none`,
+ context_require_corp,
+ context_none,
+ corp_cross_origin,
+ "include",
+ "retrieved");
+cacheStorageTest(`[${environment}] require_corp => credentialless`,
+ context_require_corp,
+ context_credentialless,
+ corp_cross_origin,
+ "include",
+ "retrieved");
+cacheStorageTest(`[${environment}] require_corp => require-corp`,
+ context_require_corp,
+ context_require_corp,
+ corp_cross_origin,
+ "include",
+ "retrieved");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache.window.js
new file mode 100644
index 0000000000..7d961804a0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cache.window.js
@@ -0,0 +1,84 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+// With COEP:credentialless, requesting a resource without credentials MUST NOT
+// return a response requested with credentials. This would be a security
+// issue, since COEP:credentialless can be used to enable crossOriginIsolation.
+//
+// The test the behavior of the HTTP cache:
+// 1. b.com stores cookie.
+// 2. a.com(COEP:unsafe-none): request b.com's resource.
+// 3. a.com(COEP:credentialless): request b.com's resource.
+//
+// The first time, the resource is requested with credentials. The response is
+// served with Cache-Control: max-age=31536000. It enters the cache.
+// The second time, the resource is requested without credentials. The response
+// in the cache must not be returned.
+
+const cookie_key = "coep_cache_key";
+const cookie_value = "coep_cache_value";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+const GetCookie = (response) => {
+ return parseCookies(JSON.parse(response))[cookie_key];
+}
+
+// "same_origin" document with COEP:unsafe-none.
+const w_coep_none_token = token();
+const w_coep_none_url = same_origin + executor_path + coep_none +
+ `&uuid=${w_coep_none_token}`
+const w_coep_none = window.open(w_coep_none_url);
+add_completion_callback(() => w_coep_none.close());
+
+// "same_origin" document with COEP:credentialles.
+const w_coep_credentialless_token = token();
+const w_coep_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_coep_credentialless_token}`
+const w_coep_credentialless = window.open(w_coep_credentialless_url);
+add_completion_callback(() => w_coep_credentialless.close());
+
+const this_token = token();
+
+// A request toward a "cross-origin" cacheable response.
+const request_token = token();
+const request_url = cacheableShowRequestHeaders(cross_origin, request_token);
+
+promise_setup(async test => {
+ await setCookie(cross_origin, cookie_key, cookie_value + cookie_same_site_none);
+}, "Set cookie");
+
+// The "same-origin" COEP:unsafe-none document fetchs a "cross-origin"
+// resource. The request is sent with credentials.
+promise_setup(async test => {
+ send(w_coep_none_token, `
+ await fetch("${request_url}", {
+ mode : "no-cors",
+ credentials: "include",
+ });
+ send("${this_token}", "Resource fetched");
+ `);
+
+ assert_equals(await receive(this_token), "Resource fetched");
+ assert_equals(await receive(request_token).then(GetCookie), cookie_value);
+}, "Cache a response requested with credentials");
+
+// The "same-origin" COEP:credentialless document fetches the same resource
+// without credentials. The HTTP cache must not be used. Instead a second
+// request must be made without credentials.
+promise_test(async test => {
+ send(w_coep_credentialless_token, `
+ await fetch("${request_url}", {
+ mode : "no-cors",
+ credentials: "include",
+ });
+ send("${this_token}", "Resource fetched");
+ `);
+
+ assert_equals(await receive(this_token), "Resource fetched");
+
+ test.step_timeout(test.unreached_func("The HTTP cache has been used"), 1500);
+ assert_equals(await receive(request_token).then(GetCookie), undefined);
+}, "The HTTP cache must not be used");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cross-origin-isolated.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cross-origin-isolated.window.js
new file mode 100644
index 0000000000..361739f283
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/cross-origin-isolated.window.js
@@ -0,0 +1,49 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+const http = get_host_info().HTTP_ORIGIN;
+const https = get_host_info().HTTPS_ORIGIN;
+
+let crossOriginIsolatedTest = (
+ description,
+ origin ,
+ headers,
+ expect_crossOriginIsolated) => {
+ promise_test_parallel(async test => {
+ const w_token = token();
+ const w_url = origin + executor_path + headers + `&uuid=${w_token}`;
+ const w = window.open(w_url)
+ add_completion_callback(() => w.close());
+
+ const this_token = token();
+ send(w_token, `
+ if (window.crossOriginIsolated)
+ send("${this_token}", "crossOriginIsolated");
+ else
+ send("${this_token}", "not isolated")
+ `);
+ assert_equals(await receive(this_token), expect_crossOriginIsolated);
+ }, description);
+}
+
+crossOriginIsolatedTest("Main crossOriginIsolated case:",
+ https, coop_same_origin +
+ coep_credentialless, "crossOriginIsolated");
+
+crossOriginIsolatedTest("Missing HTTPS:",
+ http, coop_same_origin +
+ coep_credentialless, "not isolated");
+
+crossOriginIsolatedTest("Missing COOP:same-origin:",
+ https, coep_credentialless, "not isolated");
+
+crossOriginIsolatedTest("Report-only:",
+ https, coop_same_origin +
+ coep_report_only_credentialless, "not isolated");
+
+crossOriginIsolatedTest("Report-only + enforced:",
+ https, coop_same_origin +
+ coep_credentialless +
+ coep_report_only_credentialless, "crossOriginIsolated");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/dedicated-worker.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/dedicated-worker.https.window.js
new file mode 100644
index 0000000000..b06ec56210
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/dedicated-worker.https.window.js
@@ -0,0 +1,137 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = "credentialless_dedicated_worker";
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+promise_test(async test => {
+
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // One window with COEP:none. (control)
+ const w_control_token = token();
+ const w_control_url = same_origin + executor_path +
+ coep_none + `&uuid=${w_control_token}`
+ const w_control = window.open(w_control_url);
+ add_completion_callback(() => w_control.close());
+
+ // One window with COEP:credentialless. (experiment)
+ const w_credentialless_token = token();
+ const w_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_credentialless_token}`;
+ const w_credentialless = window.open(w_credentialless_url);
+ add_completion_callback(() => w_credentialless.close());
+
+ let GetCookie = (response) => {
+ const headers_credentialless = JSON.parse(response);
+ return parseCookies(headers_credentialless)[cookie_key];
+ }
+
+ const dedicatedWorkerTest = function(
+ description, origin, coep_for_worker,
+ expected_cookies_control,
+ expected_cookies_credentialless) {
+ promise_test_parallel(async t => {
+ // Create workers for both window.
+ const worker_token_1 = token();
+ const worker_token_2 = token();
+
+ // Used to check for errors creating the DedicatedWorker.
+ const worker_error_1 = token();
+ const worker_error_2 = token();
+
+ const w_worker_src_1 = same_origin + executor_worker_path +
+ coep_for_worker + `&uuid=${worker_token_1}`;
+ send(w_control_token, `
+ new Worker("${w_worker_src_1}", {});
+ worker.onerror = () => {
+ send("${worker_error_1}", "Worker blocked");
+ }
+ `);
+
+ const w_worker_src_2 = same_origin + executor_worker_path +
+ coep_for_worker + `&uuid=${worker_token_2}`;
+ send(w_credentialless_token, `
+ const worker = new Worker("${w_worker_src_2}", {});
+ worker.onerror = () => {
+ send("${worker_error_2}", "Worker blocked");
+ }
+ `);
+
+ // Fetch resources with the workers.
+ const request_token_1 = token();
+ const request_token_2 = token();
+ const request_url_1 = showRequestHeaders(origin, request_token_1);
+ const request_url_2 = showRequestHeaders(origin, request_token_2);
+
+ send(worker_token_1, `
+ fetch("${request_url_1}", {mode: 'no-cors', credentials: 'include'})
+ `);
+ send(worker_token_2, `
+ fetch("${request_url_2}", {mode: 'no-cors', credentials: 'include'});
+ `);
+
+ const response_control = await Promise.race([
+ receive(worker_error_1),
+ receive(request_token_1).then(GetCookie)
+ ]);
+ assert_equals(response_control,
+ expected_cookies_control,
+ "coep:none => ");
+
+ const response_credentialless = await Promise.race([
+ receive(worker_error_2),
+ receive(request_token_2).then(GetCookie)
+ ]);
+ assert_equals(response_credentialless,
+ expected_cookies_credentialless,
+ "coep:credentialless => ");
+ }, `fetch ${description}`)
+ };
+
+ dedicatedWorkerTest("same-origin + credentialless worker",
+ same_origin, coep_credentialless,
+ cookie_same_origin,
+ cookie_same_origin);
+
+ dedicatedWorkerTest("same-origin + require_corp worker",
+ same_origin, coep_require_corp,
+ cookie_same_origin,
+ cookie_same_origin);
+
+ dedicatedWorkerTest("same-origin",
+ same_origin, coep_none,
+ cookie_same_origin,
+ "Worker blocked");
+
+ dedicatedWorkerTest("cross-origin",
+ cross_origin, coep_none,
+ cookie_cross_origin,
+ "Worker blocked" // Owner's policy is credentialles, so we can't
+ // create a worker with coep_none.
+ );
+
+ dedicatedWorkerTest("cross-origin + credentialless worker",
+ cross_origin, coep_credentialless,
+ undefined, // Worker created successfully with credentialless, and fetch doesn't get credentials
+ undefined // Worker created successfully with credentialless, and fetch doesn't get credentials
+ );
+
+ dedicatedWorkerTest("cross-origin + require_corp worker",
+ cross_origin, coep_require_corp,
+ cookie_cross_origin,
+ cookie_cross_origin // The worker's policy is require_corp and doing a
+ // fetch within it has nothing to do with the Owner's policy.
+ );
+})
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/fetch.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/fetch.https.window.js
new file mode 100644
index 0000000000..6ea94d0a19
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/fetch.https.window.js
@@ -0,0 +1,127 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+promise_test(async test => {
+ const same_origin = get_host_info().HTTPS_ORIGIN;
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const cookie_key = "coep_credentialless_fetch";
+ const cookie_same_origin = "same_origin";
+ const cookie_cross_origin = "cross_origin";
+
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // One window with COEP:none. (control)
+ const w_control_token = token();
+ const w_control_url = same_origin + executor_path +
+ coep_none + `&uuid=${w_control_token}`
+ const w_control = window.open(w_control_url);
+ add_completion_callback(() => w_control.close());
+
+ // One window with COEP:credentialless. (experiment)
+ const w_credentialless_token = token();
+ const w_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_credentialless_token}`;
+ const w_credentialless = window.open(w_credentialless_url);
+ add_completion_callback(() => w_credentialless.close());
+
+ const fetchTest = function(
+ description, origin, mode, credentials,
+ expected_cookies_control,
+ expected_cookies_credentialless)
+ {
+ promise_test_parallel(async test => {
+ const token_1 = token();
+ const token_2 = token();
+
+ send(w_control_token, `
+ fetch("${showRequestHeaders(origin, token_1)}", {
+ mode:"${mode}",
+ credentials: "${credentials}",
+ });
+ `);
+ send(w_credentialless_token, `
+ fetch("${showRequestHeaders(origin, token_2)}", {
+ mode:"${mode}",
+ credentials: "${credentials}",
+ });
+ `);
+
+ const headers_control = JSON.parse(await receive(token_1));
+ const headers_credentialless = JSON.parse(await receive(token_2));
+
+ assert_equals(parseCookies(headers_control)[cookie_key],
+ expected_cookies_control,
+ "coep:none => ");
+ assert_equals(parseCookies(headers_credentialless)[cookie_key],
+ expected_cookies_credentialless,
+ "coep:credentialless => ");
+ }, `fetch ${description}`)
+ };
+
+ // Cookies are never sent with credentials='omit'
+ fetchTest("same-origin + no-cors + credentials:omit",
+ same_origin, 'no-cors', 'omit',
+ undefined,
+ undefined);
+ fetchTest("same-origin + cors + credentials:omit",
+ same_origin, 'cors', 'omit',
+ undefined,
+ undefined);
+ fetchTest("cross-origin + no-cors + credentials:omit",
+ cross_origin, 'no-cors', 'omit',
+ undefined,
+ undefined);
+ fetchTest("cross-origin + cors + credentials:omit",
+ cross_origin, 'cors', 'omit',
+ undefined,
+ undefined);
+
+ // Same-origin request contains Cookies.
+ fetchTest("same-origin + no-cors + credentials:include",
+ same_origin, 'no-cors', 'include',
+ cookie_same_origin,
+ cookie_same_origin);
+ fetchTest("same-origin + cors + credentials:include",
+ same_origin, 'cors', 'include',
+ cookie_same_origin,
+ cookie_same_origin);
+ fetchTest("same-origin + no-cors + credentials:same-origin",
+ same_origin, 'no-cors', 'same-origin',
+ cookie_same_origin,
+ cookie_same_origin);
+ fetchTest("same-origin + cors + credentials:same-origin",
+ same_origin, 'cors', 'same-origin',
+ cookie_same_origin,
+ cookie_same_origin);
+
+ // Cross-origin CORS requests contains Cookies, if credentials mode is set to
+ // 'include'. This does not depends on COEP.
+ fetchTest("cross-origin + cors + credentials:include",
+ cross_origin, 'cors', 'include',
+ cookie_cross_origin,
+ cookie_cross_origin);
+ fetchTest("cross-origin + cors + same-origin-credentials",
+ cross_origin, 'cors', 'same-origin',
+ undefined,
+ undefined);
+
+ // Cross-origin no-CORS requests includes Cookies when:
+ // 1. credentials mode is 'include'
+ // 2. COEP: is not credentialless.
+ fetchTest("cross-origin + no-cors + credentials:include",
+ cross_origin, 'no-cors', 'include',
+ cookie_cross_origin,
+ undefined);
+
+ fetchTest("cross-origin + no-cors + credentials:same-origin",
+ cross_origin, 'no-cors', 'same-origin',
+ undefined,
+ undefined);
+}, "");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-credentialless.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-credentialless.https.window.js
new file mode 100644
index 0000000000..f9d9fcb932
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-credentialless.https.window.js
@@ -0,0 +1,37 @@
+// META: variant=?1-4
+// META: variant=?5-9
+// META: variant=?9-last
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+// META: script=./resources/iframeTest.js
+// META: script=/common/subset-tests.js
+
+const parent_coep_credentialless = newWindow(coep_credentialless);
+subsetTest(iframeTest, "COEP:credentialless embeds same-origin COEP:none",
+ parent_coep_credentialless, same_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTest, "COEP:credentialless embeds cross-origin COEP:none",
+ parent_coep_credentialless, cross_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTest, "COEP:credentialless embeds same-origin COEP:credentialless",
+ parent_coep_credentialless, same_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:credentialless embeds cross-origin COEP:credentialless",
+ parent_coep_credentialless, cross_origin, coep_credentialless, EXPECT_BLOCK);
+subsetTest(iframeTest, "COEP:credentialless embeds same-origin COEP:require-corp",
+ parent_coep_credentialless, same_origin, coep_require_corp, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:credentialless embeds cross-origin COEP:require-corp",
+ parent_coep_credentialless, cross_origin, coep_require_corp, EXPECT_BLOCK);
+
+// Using CORP:cross-origin might unblock previously blocked iframes.
+subsetTest(iframeTestCORP, "COEP:credentialless embeds same-origin COEP:none",
+ parent_coep_credentialless, same_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTestCORP, "COEP:credentialless embeds cross-origin COEP:none",
+ parent_coep_credentialless, cross_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTestCORP, "COEP:credentialless embeds same-origin COEP:credentialless",
+ parent_coep_credentialless, same_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTestCORP, "COEP:credentialless embeds cross-origin COEP:credentialless",
+ parent_coep_credentialless, cross_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTestCORP, "COEP:credentialless embeds same-origin COEP:require-corp",
+ parent_coep_credentialless, same_origin, coep_require_corp, EXPECT_LOAD);
+subsetTest(iframeTestCORP, "COEP:credentialless embeds cross-origin COEP:require-corp",
+ parent_coep_credentialless, cross_origin, coep_require_corp, EXPECT_LOAD);
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-none.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-none.https.window.js
new file mode 100644
index 0000000000..4f50b8d407
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-none.https.window.js
@@ -0,0 +1,22 @@
+// META: variant=?1-4
+// META: variant=?5-last
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+// META: script=./resources/iframeTest.js
+// META: script=/common/subset-tests.js
+
+const parent_coep_none = newWindow(coep_none);
+subsetTest(iframeTest, "COEP:none embeds same-origin COEP:none",
+ parent_coep_none, same_origin, coep_none, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:none embeds cross-origin COEP:none",
+ parent_coep_none, cross_origin, coep_none, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:none embeds same-origin COEP:credentialless",
+ parent_coep_none, same_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:none embeds cross-origin COEP:credentialless",
+ parent_coep_none, cross_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:none embeds same-origin COEP:require-corp",
+ parent_coep_none, same_origin, coep_require_corp, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:none embeds cross-origin COEP:require-corp",
+ parent_coep_none, cross_origin, coep_require_corp, EXPECT_LOAD);
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-redirect.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-redirect.https.window.js
new file mode 100644
index 0000000000..a9f6a1c88e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-redirect.https.window.js
@@ -0,0 +1,41 @@
+// META: variant=?1-4
+// META: variant=?5-last
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+// META: script=./resources/iframeTest.js
+promise_test(async test => {
+ const test_token = token();
+ const child_token = token();
+
+ const redirecting_child_url = cross_origin + "/common/dispatcher/executor.html?uuid=" + child_token;
+
+ const child_url =
+ same_origin +
+ "/html/cross-origin-embedder-policy/credentialless/resources/redirect_none_to_credentialless.py?redirectTo=" + redirecting_child_url;
+
+ await send(newWindow(coep_credentialless), `
+ let iframe = document.createElement("iframe");
+ iframe.src = "${child_url}";
+ document.body.appendChild(iframe);
+ `);
+
+ await send(child_token, `
+ send("${test_token}", "load");
+ `);
+
+ // There are no interoperable ways to check an iframe failed to load. So a
+ // timeout is being used.
+ // See https://github.com/whatwg/html/issues/125
+ // Use a shorter timeout when it is expected to be reached.
+ // - The long delay reduces the false-positive rate. False-positive causes
+ // stability problems on bot, so a big delay is used to vanish them.
+ // https://crbug.com/1215956.
+ // - The short delay avoids delaying too much the test(s) for nothing and
+ // timing out. False-negative are not a problem, they just need not to
+ // overwhelm the true-negative, which is trivial to get.
+ step_timeout(()=>send(test_token, "block"), 6000);
+
+ assert_equals(await receive(test_token), EXPECT_LOAD);
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-require-corp.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-require-corp.https.window.js
new file mode 100644
index 0000000000..a70d4ff8fc
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe-coep-require-corp.https.window.js
@@ -0,0 +1,38 @@
+// META: variant=?1-4
+// META: variant=?5-9
+// META: variant=?9-last
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+// META: script=./resources/iframeTest.js
+// META: script=/common/subset-tests.js
+
+const parent_coep_require_corp = newWindow(coep_require_corp);
+
+subsetTest(iframeTest, "COEP:require-corp embeds same-origin COEP:none",
+ parent_coep_require_corp, same_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTest, "COEP:require-corp embeds cross-origin COEP:none",
+ parent_coep_require_corp, cross_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTest, "COEP:require-corp embeds same-origin COEP:credentialless",
+ parent_coep_require_corp, same_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:require-corp embeds cross-origin COEP:credentialless",
+ parent_coep_require_corp, cross_origin, coep_credentialless, EXPECT_BLOCK);
+subsetTest(iframeTest, "COEP:require-corp embeds same-origin COEP:require-corp",
+ parent_coep_require_corp, same_origin, coep_require_corp, EXPECT_LOAD);
+subsetTest(iframeTest, "COEP:require-corp embeds cross-origin COEP:require-corp",
+ parent_coep_require_corp, cross_origin, coep_require_corp, EXPECT_BLOCK);
+
+// Using CORP:cross-origin might unblock previously blocked iframes.
+subsetTest(iframeTestCORP, "COEP:require-corp embeds same-origin COEP:none",
+ parent_coep_require_corp, same_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTestCORP, "COEP:require-corp embeds cross-origin COEP:none",
+ parent_coep_require_corp, cross_origin, coep_none, EXPECT_BLOCK);
+subsetTest(iframeTestCORP, "COEP:require-corp embeds same-origin COEP:credentialless",
+ parent_coep_require_corp, same_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTestCORP, "COEP:require-corp embeds cross-origin COEP:credentialless",
+ parent_coep_require_corp, cross_origin, coep_credentialless, EXPECT_LOAD);
+subsetTest(iframeTestCORP, "COEP:require-corp embeds same-origin COEP:require-corp",
+ parent_coep_require_corp, same_origin, coep_require_corp, EXPECT_LOAD);
+subsetTest(iframeTestCORP, "COEP:require-corp embeds cross-origin COEP:require-corp",
+ parent_coep_require_corp, cross_origin, coep_require_corp, EXPECT_LOAD);
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe.window.js
new file mode 100644
index 0000000000..d7a9c1e170
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/iframe.window.js
@@ -0,0 +1,47 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = "coep_redirect";
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+// Operate on a window with COEP:credentialless.
+const w_token = token();
+const w_url = same_origin + executor_path + coep_credentialless +
+ `&uuid=${w_token}`
+const w = window.open(w_url);
+add_completion_callback(() => w.close());
+
+// Check whether COEP:credentialless applies to navigation request. It
+// shouldn't.
+const iframeTest = function(name, origin, expected_cookies) {
+ promise_test_parallel(async test => {
+ const token_request = token();
+ const url = showRequestHeaders(origin, token_request);
+
+ send(w_token, `
+ const iframe = document.createElement("iframe");
+ iframe.src = "${url}";
+ document.body.appendChild(iframe);
+ `);
+
+ const headers = JSON.parse(await receive(token_request));
+ assert_equals(parseCookies(headers)[cookie_key], expected_cookies);
+ }, name)
+};
+
+promise_test_parallel(async test => {
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ iframeTest("same-origin", same_origin, cookie_same_origin);
+ iframeTest("cross-origin", cross_origin, cookie_cross_origin);
+}, "Setup");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/image.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/image.https.window.js
new file mode 100644
index 0000000000..2e9166d1bb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/image.https.window.js
@@ -0,0 +1,97 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+promise_test_parallel(async test => {
+ const same_origin = get_host_info().HTTPS_ORIGIN;
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const cookie_key = "coep_credentialless_image";
+ const cookie_same_origin = "same_origin";
+ const cookie_cross_origin = "cross_origin";
+
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // One window with COEP:none. (control)
+ const w_control_token = token();
+ const w_control_url = same_origin + executor_path +
+ coep_none + `&uuid=${w_control_token}`
+ const w_control = window.open(w_control_url);
+ add_completion_callback(() => w_control.close());
+
+ // One window with COEP:credentialless. (experiment)
+ const w_credentialless_token = token();
+ const w_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_credentialless_token}`;
+ const w_credentialless = window.open(w_credentialless_url);
+ add_completion_callback(() => w_credentialless.close());
+
+ let imgTest = function(
+ description, origin, mode,
+ expected_cookies_control,
+ expected_cookies_credentialless)
+ {
+ promise_test_parallel(async test => {
+ const token_1 = token();
+ const token_2 = token();
+
+ send(w_control_token, `
+ let img = document.createElement("img");
+ img.src = "${showRequestHeaders(origin, token_1)}";
+ ${mode};
+ document.body.appendChild(img);
+ `);
+ send(w_credentialless_token, `
+ let img = document.createElement("img");
+ img.src = "${showRequestHeaders(origin, token_2)}";
+ ${mode};
+ document.body.appendChild(img);
+ `);
+
+ const headers_control = JSON.parse(await receive(token_1));
+ const headers_credentialless = JSON.parse(await receive(token_2));
+
+ assert_equals(parseCookies(headers_control)[cookie_key],
+ expected_cookies_control,
+ "coep:none => ");
+ assert_equals(parseCookies(headers_credentialless)[cookie_key],
+ expected_cookies_credentialless,
+ "coep:credentialless => ");
+ }, `image ${description}`)
+ };
+
+ // Same-origin request always contains Cookies:
+ imgTest("same-origin + undefined",
+ same_origin, '',
+ cookie_same_origin,
+ cookie_same_origin);
+ imgTest("same-origin + anonymous",
+ same_origin, 'img.crossOrigin="anonymous"',
+ cookie_same_origin,
+ cookie_same_origin);
+ imgTest("same-origin + use-credentials",
+ same_origin, 'img.crossOrigin="use-credentials"',
+ cookie_same_origin,
+ cookie_same_origin);
+
+ // Cross-origin request contains cookies in the following cases:
+ // - COEP:credentialless is not set.
+ // - img.crossOrigin is `use-credentials`.
+ imgTest("cross-origin + undefined",
+ cross_origin, '',
+ cookie_cross_origin,
+ undefined);
+ imgTest("cross-origin + anonymous",
+ cross_origin, 'img.crossOrigin="anonymous"',
+ undefined,
+ undefined);
+ imgTest("cross-origin + use-credentials",
+ cross_origin, 'img.crossOrigin="use-credentials"',
+ cookie_cross_origin,
+ cookie_cross_origin);
+}, "Main");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/link.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/link.https.window.js
new file mode 100644
index 0000000000..0a0f8eef66
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/link.https.window.js
@@ -0,0 +1,99 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+promise_test_parallel(async test => {
+ const same_origin = get_host_info().HTTPS_ORIGIN;
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const cookie_key = "coep_credentialless_link";
+ const cookie_same_origin = "same_origin";
+ const cookie_cross_origin = "cross_origin";
+
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // One window with COEP:none. (control)
+ const w_control_token = token();
+ const w_control_url = same_origin + executor_path +
+ coep_none + `&uuid=${w_control_token}`
+ const w_control = window.open(w_control_url);
+ add_completion_callback(() => w_control.close());
+
+ // One window with COEP:credentialless. (experiment)
+ const w_credentialless_token = token();
+ const w_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_credentialless_token}`;
+ const w_credentialless = window.open(w_credentialless_url);
+ add_completion_callback(() => w_credentialless.close());
+
+ let linkTest = function(
+ description, origin, mode,
+ expected_cookies_control,
+ expected_cookies_credentialless)
+ {
+ promise_test_parallel(async test => {
+ const token_1 = token();
+ const token_2 = token();
+
+ send(w_control_token, `
+ let link = document.createElement("link");
+ link.href = "${showRequestHeaders(origin, token_1)}";
+ link.rel = "stylesheet";
+ ${mode}
+ document.head.appendChild(link);
+ `);
+ send(w_credentialless_token, `
+ let link = document.createElement("link");
+ link.href = "${showRequestHeaders(origin, token_2)}";
+ link.rel = "stylesheet";
+ ${mode}
+ document.head.appendChild(link);
+ `);
+
+ const headers_control = JSON.parse(await receive(token_1));
+ const headers_credentialless = JSON.parse(await receive(token_2));
+
+ assert_equals(parseCookies(headers_control)[cookie_key],
+ expected_cookies_control,
+ "coep:none => ");
+ assert_equals(parseCookies(headers_credentialless)[cookie_key],
+ expected_cookies_credentialless,
+ "coep:credentialless => ");
+ }, `link ${description}`)
+ };
+
+ // Same-origin request always contains Cookies:
+ linkTest("same-origin + undefined",
+ same_origin, '',
+ cookie_same_origin,
+ cookie_same_origin);
+ linkTest("same-origin + anonymous",
+ same_origin, 'link.crossOrigin="anonymous"',
+ cookie_same_origin,
+ cookie_same_origin);
+ linkTest("same-origin + use-credentials",
+ same_origin, 'link.crossOrigin="use-credentials"',
+ cookie_same_origin,
+ cookie_same_origin);
+
+ // Cross-origin request contains cookies in the following cases:
+ // - COEP:credentialless is not set.
+ // - link.crossOrigin is `use-credentials`.
+ linkTest("cross-origin + undefined",
+ cross_origin, '',
+ cookie_cross_origin,
+ undefined);
+ linkTest("cross-origin + anonymous",
+ cross_origin, 'link.crossOrigin="anonymous"',
+ undefined,
+ undefined);
+ linkTest("cross-origin + use-credentials",
+ cross_origin, 'link.crossOrigin="use-credentials"',
+ cookie_cross_origin,
+ cookie_cross_origin);
+}, "Main");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/redirect.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/redirect.window.js
new file mode 100644
index 0000000000..db8ca08d36
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/redirect.window.js
@@ -0,0 +1,55 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = "coep_redirect";
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+// Operate on a window with COEP:credentialless.:
+const w_token = token();
+const w_url = same_origin + executor_path + coep_credentialless +
+ `&uuid=${w_token}`
+const w = window.open(w_url);
+add_completion_callback(() => w.close());
+
+let redirectTest = function(name,
+ redirect_origin,
+ final_origin,
+ expected_cookies) {
+ promise_test_parallel(async test => {
+ const token_request = token();
+ const url = redirect_origin + "/common/redirect.py?location=" +
+ encodeURIComponent(showRequestHeaders(final_origin, token_request));
+
+ send(w_token, `
+ const img = document.createElement("img");
+ img.src = "${url}";
+ document.body.appendChild(img);
+ `);
+
+ const headers = JSON.parse(await receive(token_request));
+ assert_equals(parseCookies(headers)[cookie_key], expected_cookies);
+ }, name)
+};
+
+promise_test_parallel(async test => {
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ redirectTest("same-origin -> same-origin",
+ same_origin, same_origin, cookie_same_origin);
+ redirectTest("same-origin -> cross-origin",
+ same_origin, cross_origin, undefined)
+ redirectTest("cross-origin -> same-origin",
+ cross_origin, same_origin, undefined);
+ redirectTest("cross-origin -> cross-origin",
+ cross_origin, cross_origin, undefined);
+}, "Setup");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-navigation.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-navigation.https.window.js
new file mode 100644
index 0000000000..1d62996e38
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-navigation.https.window.js
@@ -0,0 +1,139 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=./resources/common.js
+const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
+const COEP = '|header(cross-origin-embedder-policy,credentialless)';
+const COEP_RO =
+ '|header(cross-origin-embedder-policy-report-only,credentialless)';
+const CORP_CROSS_ORIGIN =
+ '|header(cross-origin-resource-policy,cross-origin)';
+const FRAME_URL = `${ORIGIN}/common/blank.html?pipe=`;
+const REMOTE_FRAME_URL = `${REMOTE_ORIGIN}/common/blank.html?pipe=`;
+
+function checkCorpReport(report, contextUrl, blockedUrl, disposition) {
+ assert_equals(report.type, 'coep');
+ assert_equals(report.url, contextUrl);
+ assert_equals(report.body.type, 'corp');
+ assert_equals(report.body.blockedURL, blockedUrl);
+ assert_equals(report.body.disposition, disposition);
+ assert_equals(report.body.destination, 'iframe');
+}
+
+function checkCoepMismatchReport(report, contextUrl, blockedUrl, disposition) {
+ assert_equals(report.type, 'coep');
+ assert_equals(report.url, contextUrl);
+ assert_equals(report.body.type, 'navigation');
+ assert_equals(report.body.blockedURL, blockedUrl);
+ assert_equals(report.body.disposition, disposition);
+}
+
+function loadFrame(document, url) {
+ return new Promise((resolve, reject) => {
+ const frame = document.createElement('iframe');
+ frame.src = url;
+ frame.onload = () => resolve(frame);
+ frame.onerror = reject;
+ document.body.appendChild(frame);
+ });
+}
+
+// |parentSuffix| is a suffix for the parent frame URL.
+// |targetUrl| is a URL for the target frame.
+async function loadFrames(test, parentSuffix, targetUrl) {
+ const frame = await loadFrame(document, FRAME_URL + parentSuffix);
+ test.add_cleanup(() => frame.remove());
+ // Here we don't need "await". This loading may or may not succeed, and
+ // we're not interested in the result.
+ loadFrame(frame.contentDocument, targetUrl);
+
+ return frame;
+}
+
+async function observeReports(global, expected_count) {
+ const reports = [];
+ const receivedEveryReports = new Promise(resolve => {
+ if (expected_count == 0)
+ resolve();
+
+ const observer = new global.ReportingObserver((rs) => {
+ for (const r of rs) {
+ reports.push(r.toJSON());
+ }
+ if (expected_count <= reports.length)
+ resolve();
+ });
+ observer.observe();
+
+ });
+
+ // Wait 500ms more to catch additionnal unexpected reports.
+ await receivedEveryReports;
+ await new Promise(r => step_timeout(r, 500));
+ return reports;
+}
+
+function desc(headers) {
+ return headers === '' ? '(none)' : headers;
+}
+
+// CASES is a list of test case. Each test case consists of:
+// parent_headers: the suffix of the URL of the parent frame.
+// target_headers: the suffix of the URL of the target frame.
+// expected_reports: one of:
+// 'CORP': CORP violation
+// 'CORP-RO': CORP violation (report only)
+// 'NAV': COEP mismatch between the frames.
+// 'NAV-RO': COEP mismatch between the frames (report only).
+const reportingTest = function(
+ parent_headers, target_headers, expected_reports) {
+ // These tests are very slow, so they must be run in parallel using
+ // async_test.
+ promise_test_parallel(async t => {
+ const targetUrl = REMOTE_FRAME_URL + target_headers;
+ const parent = await loadFrames(t, parent_headers, targetUrl);
+ const contextUrl = parent.src ? parent.src : 'about:blank';
+ const reports = await observeReports(
+ parent.contentWindow,
+ expected_reports.length
+ );
+ assert_equals(reports.length, expected_reports.length);
+ for (let i = 0; i < reports.length; i += 1) {
+ const report = reports[i];
+ switch (expected_reports[i]) {
+ case 'CORP':
+ checkCorpReport(report, contextUrl, targetUrl, 'enforce');
+ break;
+ case 'CORP-RO':
+ checkCorpReport(report, contextUrl, targetUrl, 'reporting');
+ break;
+ case 'NAV':
+ checkCoepMismatchReport(report, contextUrl, targetUrl, 'enforce');
+ break;
+ case 'NAV-RO':
+ checkCoepMismatchReport(report, contextUrl, targetUrl, 'reporting');
+ break;
+ default:
+ assert_unreached(
+ 'Unexpected report exception: ' + expected_reports[i]);
+ }
+ }
+ }, `parent: ${desc(parent_headers)}, target: ${desc(target_headers)}, `);
+}
+
+reportingTest('', '', []);
+reportingTest('', COEP, []);
+reportingTest(COEP, COEP, ['CORP']);
+reportingTest(COEP, '', ['CORP']);
+
+reportingTest('', CORP_CROSS_ORIGIN, []);
+reportingTest(COEP, CORP_CROSS_ORIGIN, ['NAV']);
+
+reportingTest('', COEP + CORP_CROSS_ORIGIN, []);
+reportingTest(COEP, COEP + CORP_CROSS_ORIGIN, []);
+
+reportingTest(COEP_RO, COEP, ['CORP-RO']);
+reportingTest(COEP_RO, '', ['CORP-RO', 'NAV-RO']);
+reportingTest(COEP_RO, CORP_CROSS_ORIGIN, ['NAV-RO']);
+reportingTest(COEP_RO, COEP + CORP_CROSS_ORIGIN, []);
+
+reportingTest(COEP, COEP_RO + CORP_CROSS_ORIGIN, ['NAV']);
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-subresource-corp.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-subresource-corp.https.window.js
new file mode 100644
index 0000000000..ab583fd49e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/reporting-subresource-corp.https.window.js
@@ -0,0 +1,74 @@
+// META: timeout=long
+// META: script=/common/utils.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
+const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
+const BASE = "/html/cross-origin-embedder-policy/resources";
+const REPORTING_FRAME_URL = `${ORIGIN}${BASE}/reporting-empty-frame.html` +
+ '?pipe=header(cross-origin-embedder-policy,credentialless)' +
+ '&token=${token()}';
+
+async function observeReports(global, expected_count) {
+ const reports = [];
+ const receivedEveryReports = new Promise(resolve => {
+ if (expected_count == 0)
+ resolve();
+
+ const observer = new global.ReportingObserver((rs) => {
+ for (const r of rs) {
+ reports.push(r.toJSON());
+ }
+ if (expected_count <= reports.length)
+ resolve();
+ });
+ observer.observe();
+
+ });
+
+ await receivedEveryReports;
+ // Wait 1000ms more to catch additionnal unexpected reports.
+ await new Promise(r => step_timeout(r, 1000));
+ return reports;
+}
+
+async function fetchInFrame(t, frameUrl, url, expected_count) {
+ const frame = await with_iframe(frameUrl);
+ t.add_cleanup(() => frame.remove());
+
+ const init = { mode: 'no-cors', cache: 'no-store' };
+ let future_reports = observeReports(frame.contentWindow, expected_count);
+ await frame.contentWindow.fetch(url, init).catch(() => {});
+
+ return await future_reports;
+}
+
+function checkReport(report, contextUrl, blockedUrl, disposition, destination) {
+ assert_equals(report.type, 'coep');
+ assert_equals(report.url, contextUrl);
+ assert_equals(report.body.type, 'corp');
+ assert_equals(report.body.blockedURL, blockedUrl);
+ assert_equals(report.body.disposition, disposition);
+ assert_equals(report.body.destination, destination);
+}
+
+// A redirection is used, so that the initial request is same-origin and is
+// proxyied through the service worker. The ServiceWorker is COEP:unsafe-none,
+// so it will make the cross-origin request with credentials. The fetch will
+// succeed, but the response will be blocked by CORP when entering the
+// COEP:credentialless document.
+// https://github.com/w3c/ServiceWorker/issues/1592
+promise_test(async (t) => {
+ const url = `${ORIGIN}/common/redirect.py?location=` +
+ encodeURIComponent(`${REMOTE_ORIGIN}/common/text-plain.txt`);
+ const WORKER_URL = `${ORIGIN}${BASE}/sw.js`;
+ const reg = await service_worker_unregister_and_register(
+ t, WORKER_URL, REPORTING_FRAME_URL);
+ t.add_cleanup(() => reg.unregister());
+ const worker = reg.installing || reg.waiting || reg.active;
+ worker.addEventListener('error', t.unreached_func('Worker.onerror'));
+ await wait_for_state(t, worker, 'activated');
+
+ const reports = await fetchInFrame(t, REPORTING_FRAME_URL, url, 1);
+ assert_equals(reports.length, 1);
+ checkReport(reports[0], REPORTING_FRAME_URL, url, 'enforce', '');
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/common.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/common.js
new file mode 100644
index 0000000000..ce21c766f6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/common.js
@@ -0,0 +1,134 @@
+const executor_path = '/common/dispatcher/executor.html?pipe=';
+const executor_worker_path = '/common/dispatcher/executor-worker.js?pipe=';
+const executor_service_worker_path = '/common/dispatcher/executor-service-worker.js?pipe=';
+
+// COEP
+const coep_none =
+ '|header(Cross-Origin-Embedder-Policy,none)';
+const coep_credentialless =
+ '|header(Cross-Origin-Embedder-Policy,credentialless)';
+const coep_require_corp =
+ '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+// COEP-Report-Only
+const coep_report_only_credentialless =
+ '|header(Cross-Origin-Embedder-Policy-Report-Only,credentialless)';
+
+// COOP
+const coop_same_origin =
+ '|header(Cross-Origin-Opener-Policy,same-origin)';
+
+// CORP
+const corp_cross_origin =
+ '|header(Cross-Origin-Resource-Policy,cross-origin)';
+
+const cookie_same_site_none = ';SameSite=None;Secure';
+
+// Test using the modern async/await primitives are easier to read/write.
+// However they run sequentially, contrary to async_test. This is the parallel
+// version, to avoid timing out.
+let promise_test_parallel = (promise, description) => {
+ async_test(test => {
+ promise(test)
+ .then(() => test.done())
+ .catch(test.step_func(error => { throw error; }));
+ }, description);
+};
+
+// Add a cookie |cookie_key|=|cookie_value| on an |origin|.
+// Note: cookies visibility depends on the path of the document. Those are set
+// from a document from: /html/cross-origin-embedder-policy/credentialless/. So
+// the cookie is visible to every path underneath.
+const setCookie = async (origin, cookie_key, cookie_value) => {
+ const popup_token = token();
+ const popup_url = origin + executor_path + `&uuid=${popup_token}`;
+ const popup = window.open(popup_url);
+
+ const reply_token = token();
+ send(popup_token, `
+ document.cookie = "${cookie_key}=${cookie_value}";
+ send("${reply_token}", "done");
+ `);
+ assert_equals(await receive(reply_token), "done");
+ popup.close();
+}
+
+let parseCookies = function(headers_json) {
+ if (!headers_json["cookie"])
+ return {};
+
+ return headers_json["cookie"]
+ .split(';')
+ .map(v => v.split('='))
+ .reduce((acc, v) => {
+ acc[v[0].trim()] = v[1].trim();
+ return acc;
+ }, {});
+}
+
+// Open a new window with a given |origin|, loaded with COEP:credentialless. The
+// new document will execute any scripts sent toward the token it returns.
+const newCredentiallessWindow = (origin) => {
+ const main_document_token = token();
+ const url = origin + executor_path + coep_credentialless +
+ `&uuid=${main_document_token}`;
+ const context = window.open(url);
+ add_completion_callback(() => w.close());
+ return main_document_token;
+};
+
+// Create a new iframe, loaded with COEP:credentialless.
+// The new document will execute any scripts sent toward the token it returns.
+const newCredentiallessIframe = (parent_token, child_origin) => {
+ const sub_document_token = token();
+ const iframe_url = child_origin + executor_path + coep_credentialless +
+ `&uuid=${sub_document_token}`;
+ send(parent_token, `
+ let iframe = document.createElement("iframe");
+ iframe.src = "${iframe_url}";
+ document.body.appendChild(iframe);
+ `)
+ return sub_document_token;
+};
+
+// A common interface for building the 4 type of execution contexts:
+// It outputs: [
+// - The token to communicate with the environment.
+// - A promise resolved when the environment encounters an error.
+// ]
+const environments = {
+ document: headers => {
+ const tok = token();
+ const url = window.origin + executor_path + headers + `&uuid=${tok}`;
+ const context = window.open(url);
+ add_completion_callback(() => context.close());
+ return [tok, new Promise(resolve => {})];
+ },
+
+ dedicated_worker: headers => {
+ const tok = token();
+ const url = window.origin + executor_worker_path + headers + `&uuid=${tok}`;
+ const context = new Worker(url);
+ return [tok, new Promise(resolve => context.onerror = resolve)];
+ },
+
+ shared_worker: headers => {
+ const tok = token();
+ const url = window.origin + executor_worker_path + headers + `&uuid=${tok}`;
+ const context = new SharedWorker(url);
+ return [tok, new Promise(resolve => context.onerror = resolve)];
+ },
+
+ service_worker: headers => {
+ const tok = token();
+ const url = window.origin + executor_worker_path + headers + `&uuid=${tok}`;
+ const scope = url; // Generate a one-time scope for service worker.
+ const error = new Promise(resolve => {
+ navigator.serviceWorker.register(url, {scope: scope})
+ .then(registration => {
+ add_completion_callback(() => registration.unregister());
+ }, /* catch */ resolve);
+ });
+ return [tok, error];
+ },
+};
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/iframeTest.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/iframeTest.js
new file mode 100644
index 0000000000..501a864d46
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/iframeTest.js
@@ -0,0 +1,85 @@
+// One document embeds another in an iframe. Both are loaded from the network.
+// Depending on the response headers:
+// - Cross-Origin-Embedder-Policy (COEP)
+// - Cross-Origin-Resource-Policy (CORP)
+// The child must load or must be blocked.
+//
+// What to do for:
+// - COEP:credentialless
+// - COEP:credentialless-on-children
+// is currently an active open question. This test will be updated/completed
+// later.
+
+// There are no interoperable ways to check an iframe failed to load. So a
+// timeout is being used. See https://github.com/whatwg/html/issues/125
+// Moreover, we want to track progress, managing timeout explicitly allows to
+// get a per-test results, even in case of failure of one.
+setup({ explicit_timeout: true });
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+// Open a new window loaded with the given |headers|. The new document will
+// execute any script sent toward the token it returns.
+const newWindow = (headers) => {
+ const executor_token = token();
+ const url = same_origin + executor_path + headers + `&uuid=${executor_token}`;
+ const w = window.open(url);
+ add_completion_callback(() => w.close());
+ return executor_token;
+};
+
+const EXPECT_LOAD = "load";
+const EXPECT_BLOCK = "block";
+
+// Load in iframe. Control both the parent and the child headers. Check whether
+// it loads or not.
+const iframeTest = function(
+ description,
+ parent_token,
+ child_origin,
+ child_headers,
+ expectation
+) {
+ promise_test_parallel(async test => {
+ const test_token = token();
+
+ const child_token = token();
+ const child_url = child_origin + executor_path + child_headers +
+ `&uuid=${child_token}`;
+
+ await send(parent_token, `
+ let iframe = document.createElement("iframe");
+ iframe.src = "${child_url}";
+ document.body.appendChild(iframe);
+ `);
+
+ await send(child_token, `
+ send("${test_token}", "load");
+ `);
+
+ // There are no interoperable ways to check an iframe failed to load. So a
+ // timeout is being used.
+ // See https://github.com/whatwg/html/issues/125
+ // Use a shorter timeout when it is expected to be reached.
+ // - The long delay reduces the false-positive rate. False-positive causes
+ // stability problems on bot, so a big delay is used to vanish them.
+ // https://crbug.com/1215956.
+ // - The short delay avoids delaying too much the test(s) for nothing and
+ // timing out. False-negative are not a problem, they just need not to
+ // overwhelm the true-negative, which is trivial to get.
+ step_timeout(()=>send(test_token, "block"), expectation == EXPECT_BLOCK
+ ? 2000
+ : 6000
+ );
+
+ assert_equals(await receive(test_token), expectation);
+ }, description);
+}
+
+// A decorated version of iframeTest, adding CORP:cross-origin to the child.
+const iframeTestCORP = function() {
+ arguments[0] += ", CORP:cross-origin"; // description
+ arguments[3] += corp_cross_origin; // child_headers
+ iframeTest(...arguments);
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/redirect_none_to_credentialless.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/redirect_none_to_credentialless.py
new file mode 100644
index 0000000000..7f0c35e93e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/resources/redirect_none_to_credentialless.py
@@ -0,0 +1,14 @@
+COEP_HEADER = b"|header(cross-origin-embedder-policy, credentialless)"
+CORP_HEADER = b"|header(cross-origin-resource-policy, cross-origin)"
+
+def main(request, response):
+ """
+ Causes a redirection that the initial response doesn't have credentialless and the
+ redirected response has credentialless.
+ """
+ response.status = 302
+
+ location = \
+ request.GET[b'redirectTo'] + b"&pipe=" + COEP_HEADER + CORP_HEADER
+
+ response.headers.set(b"Location", location)
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/script.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/script.https.window.js
new file mode 100644
index 0000000000..96bf7b08db
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/script.https.window.js
@@ -0,0 +1,99 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+window.onload = function() {
+ promise_test_parallel(async test => {
+ const same_origin = get_host_info().HTTPS_ORIGIN;
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const cookie_key = "coep_credentialless_script";
+ const cookie_same_origin = "same_origin";
+ const cookie_cross_origin = "cross_origin";
+
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // One window with COEP:none. (control)
+ const w_control_token = token();
+ const w_control_url = same_origin + executor_path +
+ coep_none + `&uuid=${w_control_token}`
+ const w_control = window.open(w_control_url);
+ add_completion_callback(() => w_control.close());
+
+ // One window with COEP:credentialless. (experiment)
+ const w_credentialless_token = token();
+ const w_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_credentialless_token}`;
+ const w_credentialless = window.open(w_credentialless_url);
+ add_completion_callback(() => w_credentialless.close());
+
+ let scriptTest = function(
+ description, origin, mode,
+ expected_cookies_control,
+ expected_cookies_credentialless)
+ {
+ promise_test_parallel(async test => {
+ const token_1 = token();
+ const token_2 = token();
+
+ send(w_control_token, `
+ let script = document.createElement("script");
+ script.src = "${showRequestHeaders(origin, token_1)}";
+ ${mode};
+ document.body.appendChild(script);
+ `);
+ send(w_credentialless_token, `
+ let script = document.createElement("script");
+ script.src = "${showRequestHeaders(origin, token_2)}";
+ ${mode};
+ document.body.appendChild(script);
+ `);
+
+ const headers_control = JSON.parse(await receive(token_1));
+ const headers_credentialless = JSON.parse(await receive(token_2));
+
+ assert_equals(parseCookies(headers_control)[cookie_key],
+ expected_cookies_control,
+ "coep:none => ");
+ assert_equals(parseCookies(headers_credentialless)[cookie_key],
+ expected_cookies_credentialless,
+ "coep:credentialless => ");
+ }, `script ${description}`)
+ };
+
+ // Same-origin request always contains Cookies:
+ scriptTest("same-origin + undefined",
+ same_origin, '',
+ cookie_same_origin,
+ cookie_same_origin);
+ scriptTest("same-origin + anonymous",
+ same_origin, 'script.crossOrigin="anonymous"',
+ cookie_same_origin,
+ cookie_same_origin);
+ scriptTest("same-origin + use-credentials",
+ same_origin, 'script.crossOrigin="use-credentials"',
+ cookie_same_origin,
+ cookie_same_origin);
+
+ // Cross-origin request contains cookies in the following cases:
+ // - COEP:credentialless is not set.
+ // - script.crossOrigin is `use-credentials`.
+ scriptTest("cross-origin + undefined",
+ cross_origin, '',
+ cookie_cross_origin,
+ undefined);
+ scriptTest("cross-origin + anonymous",
+ cross_origin, 'script.crossOrigin="anonymous"',
+ undefined,
+ undefined);
+ scriptTest("cross-origin + use-credentials",
+ cross_origin, 'script.crossOrigin="use-credentials"',
+ cookie_cross_origin,
+ cookie_cross_origin);
+ }, "Main");
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-credentialless-proxy.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-credentialless-proxy.https.window.js
new file mode 100644
index 0000000000..8ffea04325
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-credentialless-proxy.https.window.js
@@ -0,0 +1,85 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+promise_test(async test => {
+ const this_token_1 = token();
+ const this_token_2 = token();
+
+ // Register a COEP:credentialless ServiceWorker.
+ const sw_token = token();
+ const sw_url =
+ executor_service_worker_path + coep_credentialless + `&uuid=${sw_token}`;
+ // Executors should be controlled by the service worker.
+ const scope = executor_path;
+ const sw_registration =
+ await service_worker_unregister_and_register(test, sw_url, scope);
+ test.add_cleanup(() => sw_registration.unregister());
+ await wait_for_state(test, sw_registration.installing, 'activated');
+
+ // Configure the ServiceWorker to proxy the fetch requests. Wait for the
+ // worker to be installed and activated.
+ send(sw_token, `
+ fetchHandler = event => {
+ if (!event.request.url.includes("/proxied"))
+ return;
+
+ send("${this_token_1}", "ServiceWorker: Proxying");
+
+ // Response with a cross-origin no-cors resource.
+ const url = "${cross_origin}" + "/common/blank.html";
+
+ event.respondWith(new Promise(async resolve => {
+ try {
+ let response = await fetch(url, {
+ mode: "no-cors",
+ credentials: "include"
+ });
+ send("${this_token_1}", "ServiceWorker: Fetch success");
+ resolve(response);
+ } catch (error) {
+ send("${this_token_1}", "ServiceWorker: Fetch failure");
+ resolve(new Response("", {status: 400}));
+ }
+ }));
+ }
+
+ await clients.claim();
+
+ send("${this_token_1}", serviceWorker.state);
+ `)
+ assert_equals(await receive(this_token_1), "activated");
+
+ // Create a COEP:credentialless document.
+ const document_token = environments["document"](coep_credentialless)[0];
+
+ // The document fetches a same-origin no-cors resource. The requests needs to
+ // be same-origin to be handled by the ServiceWorker.
+ send(document_token, `
+ try {
+ const response = await fetch("/proxied", { mode: "no-cors", });
+
+ send("${this_token_2}", "Document: Fetch success");
+ } catch (error) {
+ send("${this_token_2}", "Document: Fetch error");
+ }
+ `);
+
+ // The COEP:credentialless ServiceWorker is able to handle the cross-origin
+ // no-cors request, requested with credentials.
+ assert_equals(await receive(this_token_1), "ServiceWorker: Proxying");
+ assert_equals(await receive(this_token_1), "ServiceWorker: Fetch success");
+
+ // The COEP:credentialless Document is allowed by CORP to get it.
+ assert_equals(await receive(this_token_2), "Document: Fetch success");
+
+ // test.add_cleanup doesn't allow waiting for a promise. Unregistering a
+ // ServiceWorker is an asynchronous operation. It might not be completed on
+ // time for the next test. Do it here for extra flakiness safety.
+ await sw_registration.unregister()
+}, "COEP:credentialless ServiceWorker");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-none-proxy.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-none-proxy.https.window.js
new file mode 100644
index 0000000000..c2a6f227e4
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker-coep-none-proxy.https.window.js
@@ -0,0 +1,87 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+promise_test(async test => {
+ const this_token_1 = token();
+ const this_token_2 = token();
+
+ // Register a COEP:none ServiceWorker.
+ const sw_token = token();
+ const sw_url = executor_service_worker_path + coep_none + `&uuid=${sw_token}`;
+ // Executors should be controlled by the service worker.
+ const scope = executor_path;
+ const sw_registration =
+ await service_worker_unregister_and_register(test, sw_url, scope);
+ test.add_cleanup(() => sw_registration.unregister());
+ await wait_for_state(test, sw_registration.installing, 'activated');
+
+ // Configure the ServiceWorker to proxy the fetch requests. Wait for the
+ // worker to be installed and activated.
+ send(sw_token, `
+ fetchHandler = event => {
+ if (!event.request.url.includes("/proxied"))
+ return;
+
+ send("${this_token_1}", "ServiceWorker: Proxying");
+
+ // Response with a cross-origin no-cors resource.
+ const url = "${cross_origin}" + "/common/blank.html";
+
+ event.respondWith(new Promise(async resolve => {
+ try {
+ let response = await fetch(url, {
+ mode: "no-cors",
+ credentials: "include"
+ });
+ send("${this_token_1}", "ServiceWorker: Fetch success");
+ resolve(response);
+ } catch (error) {
+ send("${this_token_1}", "ServiceWorker: Fetch failure");
+ resolve(new Response("", {status: 400}));
+ }
+ }));
+ }
+
+ await clients.claim();
+
+ send("${this_token_1}", serviceWorker.state);
+ `)
+ assert_equals(await receive(this_token_1), "activated");
+
+ // Create a COEP:credentialless document.
+ const document_token = environments["document"](coep_credentialless)[0];
+
+ // The document fetches a same-origin no-cors resource. The requests needs to
+ // be same-origin to be handled by the ServiceWorker.
+ send(document_token, `
+ try {
+ const response = await fetch("/proxied", {
+ mode: "no-cors",
+ credentials: "include"
+ });
+
+ send("${this_token_2}", "Document: Fetch success");
+ } catch (error) {
+ send("${this_token_2}", "Document: Fetch error");
+ }
+ `);
+
+ // The COEP:unsafe-none ServiceWorker is able to handle the cross-origin
+ // no-cors request, requested with credentials.
+ assert_equals(await receive(this_token_1), "ServiceWorker: Proxying");
+ assert_equals(await receive(this_token_1), "ServiceWorker: Fetch success");
+
+ // However, the COEP:credentialless Document is disallowed by CORP to get it.
+ assert_equals(await receive(this_token_2), "Document: Fetch error");
+
+ // test.add_cleanup doesn't allow waiting for a promise. Unregistering a
+ // ServiceWorker is an asynchronous operation. It might not be completed on
+ // time for the next test. Do it here for extra flakiness safety.
+ await sw_registration.unregister()
+}, "COEP:unsafe-none ServiceWorker");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker.https.window.js
new file mode 100644
index 0000000000..4fc0061c57
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/service-worker.https.window.js
@@ -0,0 +1,113 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
+// META: script=./resources/common.js
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = "credentialless_service_worker";
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+promise_test(async t => {
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // One iframe with COEP:none. (control)
+ const w_control_token = token();
+ const w_control_url = same_origin + executor_path +
+ coep_none + `&uuid=${w_control_token}`
+ const w_control = document.createElement("iframe");
+ w_control.src = w_control_url;
+ document.body.appendChild(w_control);
+
+ // One iframe with COEP:credentialless. (experiment)
+ const w_credentialless_token = token();
+ const w_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_credentialless_token}`;
+ const w_credentialless = document.createElement("iframe");
+ w_credentialless.src = w_credentialless_url;
+ document.body.appendChild(w_credentialless);
+
+ const serviceWorkerTest = function(
+ description, origin, coep_for_worker,
+ expected_cookies_control,
+ expected_cookies_credentialless)
+ {
+ promise_test(async test => {
+ // Create workers for both window.
+ const control_worker_token = token();
+ const credentialless_worker_token = token();
+
+ const w_control_worker_src = same_origin + executor_worker_path +
+ coep_for_worker + `&uuid=${control_worker_token}`;
+ const w_control_worker_reg =
+ await service_worker_unregister_and_register(
+ test, w_control_worker_src, w_control_url);
+
+ const w_credentialless_worker_src = same_origin + executor_worker_path +
+ coep_for_worker + `&uuid=${credentialless_worker_token}`;
+ const w_credentialless_worker_reg =
+ await service_worker_unregister_and_register(
+ test, w_credentialless_worker_src, w_credentialless_url);
+
+ // Fetch resources from the workers.
+ const control_request_token = token();
+ const credentialless_request_token = token();
+ const control_request_url = showRequestHeaders(origin, control_request_token);
+ const credentialless_request_url = showRequestHeaders(origin, credentialless_request_token);
+ send(control_worker_token, `
+ fetch("${control_request_url}", {
+ mode: 'no-cors',
+ credentials: 'include'
+ })
+ `);
+ send(credentialless_worker_token, `
+ fetch("${credentialless_request_url}", {
+ mode: 'no-cors',
+ credentials: 'include'
+ })
+ `);
+
+ // Retrieve the resource request headers.
+ const headers_control = JSON.parse(await receive(control_request_token));
+ const headers_credentialless = JSON.parse(await receive(credentialless_request_token));
+
+ assert_equals(parseCookies(headers_control)[cookie_key],
+ expected_cookies_control,
+ "coep:none => ");
+ assert_equals(parseCookies(headers_credentialless)[cookie_key],
+ expected_cookies_credentialless,
+ "coep:credentialless => ");
+
+ w_control_worker_reg.unregister();
+ w_credentialless_worker_reg.unregister();
+ }, `fetch ${description}`)
+ };
+
+ serviceWorkerTest("same-origin",
+ same_origin, coep_none,
+ cookie_same_origin,
+ cookie_same_origin);
+
+ serviceWorkerTest("same-origin + credentialless worker",
+ same_origin, coep_credentialless,
+ cookie_same_origin,
+ cookie_same_origin);
+
+ serviceWorkerTest("cross-origin",
+ cross_origin, coep_none,
+ cookie_cross_origin,
+ cookie_cross_origin);
+
+ serviceWorkerTest("cross-origin + credentialless worker",
+ cross_origin, coep_credentialless,
+ undefined,
+ undefined);
+})
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/shared-worker.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/shared-worker.https.window.js
new file mode 100644
index 0000000000..5e259a5acc
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/shared-worker.https.window.js
@@ -0,0 +1,93 @@
+// META: variant=?request_origin=same_origin&worker_coep=none&window_coep=none
+// META: variant=?request_origin=same_origin&worker_coep=none&window_coep=credentialless
+// META: variant=?request_origin=same_origin&worker_coep=credentialless&window_coep=none
+// META: variant=?request_origin=same_origin&worker_coep=credentialless&window_coep=credentialless
+// META: variant=?request_origin=cross_origin&worker_coep=none&window_coep=none
+// META: variant=?request_origin=cross_origin&worker_coep=none&window_coep=credentialless
+// META: variant=?request_origin=cross_origin&worker_coep=credentialless&window_coep=none
+// META: variant=?request_origin=cross_origin&worker_coep=credentialless&window_coep=credentialless
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+// Test description:
+// Request a resource from a SharedWorker. Check the request's cookies.
+//
+// Variant:
+// - The Window COEP policy: none or credentialless.
+// - The SharedWorker COEP policy: none or credentialless.
+// - The SharedWorker's request URL origin: same-origin or cross-origin.
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = token();
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+const variants = new URLSearchParams(window.location.search);
+const window_coep = variants.get('window_coep') == 'none'
+ ? coep_none
+ : coep_credentialless;
+const worker_coep = variants.get('worker_coep') == 'none'
+ ? coep_none
+ : coep_credentialless;
+const request_origin = variants.get('request_origin') == 'same-origin'
+ ? same_origin
+ : cross_origin;
+
+// When using COEP:credentialless: cross-origin no-cors request do not include
+// credentials. Note: This must not depend on the window's COEP policy.
+const worker_expected_cookie =
+ request_origin == same_origin
+ ? cookie_same_origin
+ : (worker_coep == coep_credentialless
+ ? undefined
+ : cookie_cross_origin);
+
+// From a JSON representing the `response` HTTP headers key-values, return the
+// cookie corresponding to the `cookie_key`.
+const get_cookie = (response) => {
+ const headers_credentialless = JSON.parse(response);
+ return parseCookies(headers_credentialless)[cookie_key];
+}
+
+promise_test(async test => {
+ // 0. Populate cookies for the two origins.
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // 1. Create the popup with the `window_coep` COEP policy:
+ const popup = environments.document(window_coep)[0];
+
+ // 2. Create the worker with the `worker_coep` COEP policy:
+ const worker_token = token();
+ const worker_error = token();
+ const worker_src = same_origin + executor_worker_path + worker_coep +
+ `&uuid=${worker_token}`;
+ send(popup, `
+ let worker = new SharedWorker("${worker_src}", {});
+ worker.onerror = () => {
+ send("${worker_error}", "Worker blocked");
+ }
+ `);
+
+ // 3. Request the resource from the worker, with the `request_origin` origin.
+ const request_token = token();
+ const request_url = showRequestHeaders(request_origin, request_token);
+ send(worker_token, `fetch("${request_url}", {
+ mode: 'no-cors',
+ credentials: 'include',
+ })`);
+ const request_cookie = await Promise.race([
+ receive(worker_error),
+ receive(request_token).then(get_cookie)
+ ]);
+
+ assert_equals(request_cookie, worker_expected_cookie);
+})
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js
new file mode 100644
index 0000000000..0410b48564
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js
@@ -0,0 +1,53 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = "coep_credentialless_image";
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+promise_setup(async test => {
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+}, "Setup cookies");
+
+const videoTest = function(description, origin, mode, expected_cookie) {
+ promise_test(async test => {
+ const video_token = token();
+
+ let video = document.createElement("video");
+ video.src = showRequestHeaders(origin, video_token);
+ video.autoplay = true;
+ if (mode)
+ video.crossOrigin = mode;
+ document.body.appendChild(video);
+
+ const headers = JSON.parse(await receive(video_token));
+
+ assert_equals(parseCookies(headers)[cookie_key], expected_cookie);
+ }, `video ${description}`)
+};
+
+// Same-origin request always contains Cookies:
+videoTest("same-origin + undefined",
+ same_origin, undefined, cookie_same_origin);
+videoTest("same-origin + anonymous",
+ same_origin, 'anonymous', cookie_same_origin);
+videoTest("same-origin + use-credentials",
+ same_origin, 'use-credentials', cookie_same_origin);
+
+// Cross-origin request contains cookies, only when sent in CORS mode, using
+// crossOrigin = "use-credentials".
+videoTest("cross-origin + undefined",
+ cross_origin, '', undefined);
+videoTest("cross-origin + anonymous",
+ cross_origin, 'anonymous', undefined);
+videoTest("cross-origin + use-credentials",
+ cross_origin, 'use-credentials', cookie_cross_origin);
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js.headers
new file mode 100644
index 0000000000..68fde79c91
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/video.https.window.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy:credentialless
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/websocket.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/websocket.https.window.js
new file mode 100644
index 0000000000..2fe8d02879
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/credentialless/websocket.https.window.js
@@ -0,0 +1,77 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./resources/common.js
+
+promise_test_parallel(async test => {
+ const same_origin = get_host_info().HTTPS_ORIGIN;
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const cookie_key = "coep_credentialless_websocket";
+ const cookie_same_origin = "same_origin";
+ const cookie_cross_origin = "cross_origin";
+
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin +
+ cookie_same_site_none),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin +
+ cookie_same_site_none),
+ ]);
+
+ // One window with COEP:none. (control)
+ const w_control_token = token();
+ const w_control_url = same_origin + executor_path +
+ coep_none + `&uuid=${w_control_token}`
+ const w_control = window.open(w_control_url);
+ add_completion_callback(() => w_control.close());
+
+ // One window with COEP:credentialless. (experiment)
+ const w_credentialless_token = token();
+ const w_credentialless_url = same_origin + executor_path +
+ coep_credentialless + `&uuid=${w_credentialless_token}`;
+ const w_credentialless = window.open(w_credentialless_url);
+ add_completion_callback(() => w_credentialless.close());
+
+ let WebSocketTest = function(
+ description, origin,
+ expected_cookies_control,
+ expected_cookies_credentialless)
+ {
+ promise_test_parallel(async test => {
+ const token_1 = token();
+ const token_2 = token();
+
+ const origin_for_websocket = origin.replace("https", "wss");
+
+ send(w_control_token, `
+ var ws = new WebSocket("${showRequestHeaders(origin_for_websocket, token_1)}");
+ `);
+
+ send(w_credentialless_token, `
+ var ws = new WebSocket("${showRequestHeaders(origin_for_websocket, token_2)}");
+ `);
+
+ const headers_control = JSON.parse(await receive(token_1));
+ const headers_credentialless = JSON.parse(await receive(token_2));
+
+ assert_equals(parseCookies(headers_control)[cookie_key],
+ expected_cookies_control,
+ "coep:none => ");
+ assert_equals(parseCookies(headers_credentialless)[cookie_key],
+ expected_cookies_credentialless,
+ "coep:credentialless => ");
+ }, `WebSocket ${description}`)
+ };
+
+ // Same-origin request always contains Cookies:
+ WebSocketTest("same-origin",
+ same_origin,
+ cookie_same_origin,
+ cookie_same_origin);
+
+ // Cross-origin request also always contains Cookies:
+ WebSocketTest("cross-origin",
+ cross_origin,
+ cookie_cross_origin,
+ cookie_cross_origin);
+}, "Main");
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js
new file mode 100644
index 0000000000..9190303206
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js
@@ -0,0 +1,74 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./credentialless/resources/common.js
+// META: script=./resources/common.js
+
+const cors_coep_headers = coep_require_corp + corp_cross_origin;
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+const newIframe = async (
+ test,
+ parent_origin,
+ parent_headers,
+ child_origin,
+ child_headers
+) => {
+ const [future_child, future_error] =
+ await createIsolatedFrame(parent_origin, parent_headers);
+ future_error.then(test.unreached_func('cannot create isolated iframe.'));
+
+ const child = await future_child;
+ add_completion_callback(() => child.remove());
+
+ const grand_child_token = token();
+ const grand_child = child.contentDocument.createElement('iframe');
+ grand_child.src = child_origin + executor_path + child_headers +
+ `&uuid=${grand_child_token}`;
+ child.contentDocument.body.appendChild(grand_child);
+ add_completion_callback(() => grand_child.remove());
+
+ return grand_child_token;
+};
+
+const childFrameIsCrossOriginIsolated = async (
+ test,
+ child_origin,
+ parent_permission_coi
+) => {
+ let parent_headers = cors_coep_headers;
+ const child_headers = cors_coep_headers;
+ if (parent_permission_coi !== undefined) {
+ // Escape right parenthesis in WPT pipe:
+ parent_permission_coi = parent_permission_coi.replace(')', '\\)');
+ parent_headers += `|header(permissions-policy,` +
+ `cross-origin-isolated=${parent_permission_coi})`;
+ }
+ const parent_origin = same_origin;
+ const iframe = await newIframe(
+ test,
+ parent_origin,
+ parent_headers,
+ child_origin,
+ child_headers);
+ return IsCrossOriginIsolated(iframe);
+}
+
+const generate_iframe_test = async (origin, isolation, expect_coi) => {
+ promise_test_parallel(async (test) => {
+ const isCrossOriginIsolated =
+ await childFrameIsCrossOriginIsolated(test, origin, isolation);
+ assert_equals(isCrossOriginIsolated, expect_coi)
+ }, `iframe (origin: ${origin}) cross origin isolated (${isolation}) ` +
+ `permission test`);
+}
+
+generate_iframe_test(same_origin, undefined, true);
+generate_iframe_test(same_origin, '*', true);
+generate_iframe_test(same_origin, 'self', true);
+generate_iframe_test(same_origin, '()', false);
+generate_iframe_test(cross_origin, undefined, false);
+generate_iframe_test(cross_origin, '*', false);
+generate_iframe_test(cross_origin, 'self', false);
+generate_iframe_test(cross_origin, '()', false); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js.headers
new file mode 100644
index 0000000000..3b7825def9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js
new file mode 100644
index 0000000000..d9431cdb50
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js
@@ -0,0 +1,170 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=./credentialless/resources/common.js
+// META: script=./resources/common.js
+
+const cors_coep_headers = coep_require_corp + corp_cross_origin;
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const dedicatedWorkerPostMessage = `
+ self.addEventListener('message', (e) => {
+ e.data.port.postMessage(self.crossOriginIsolated);
+ });
+`;
+
+const postMessageIsWorkerCrossOriginIsolated = async (
+ test,
+ frame,
+ worker_url
+) => {
+ const worker = new frame.contentWindow.Worker(worker_url);
+ const mc = new MessageChannel();
+ worker.postMessage({port: mc.port2}, [mc.port2]);
+ worker.onerror = test.unreached_func('cannot create dedicated worker');
+ return (await new Promise(r => mc.port1.onmessage = r)).data;
+}
+
+const isDataDedicatedWorkerCrossOriginIsolated = async (
+ test,
+ parent_headers
+) => {
+ const [future_child, future_error] =
+ await createIsolatedFrame('', parent_headers);
+ future_error.then(test.unreached_func('cannot create isolated iframe'));
+
+ const child = await future_child;
+ add_completion_callback(() => child.remove());
+
+ const worker_url =
+ `data:application/javascript;base64,${btoa(dedicatedWorkerPostMessage)}`;
+ return postMessageIsWorkerCrossOriginIsolated(test, child, worker_url);
+}
+
+const isBlobURLDedicatedWorkerCrossOriginIsolated = async(
+ test,
+ parent_headers
+) => {
+ const [future_child, future_error] =
+ await createIsolatedFrame("", parent_headers);
+ future_error.then(test.unreached_func('cannot create isolated iframe'));
+
+ const child = await future_child;
+ add_completion_callback(() => child.remove());
+
+ const blob =
+ new Blob([dedicatedWorkerPostMessage], {type: 'text/plaintext'});
+ const workerURL = URL.createObjectURL(blob);
+ return postMessageIsWorkerCrossOriginIsolated(test, child, workerURL)
+}
+
+const isHTTPSDedicatedWorkerCrossOriginIsolated = async(
+ test,
+ parent_headers
+) => {
+ const [future_child, future_error] =
+ await createIsolatedFrame("", parent_headers);
+ future_error.then(test.unreached_func('cannot create isolated iframe'));
+
+ const child = await future_child;
+ add_completion_callback(() => child.remove());
+
+ const worker_token = token();
+ const workerURL =
+ `${executor_worker_path}${cors_coep_headers}&uuid=${worker_token}`;
+ const worker = new child.contentWindow.Worker(workerURL);
+ return IsCrossOriginIsolated(worker_token);
+}
+
+const sharedWorkerIsCrossOriginIsolated = async(
+ test,
+ withCoopCoep
+) => {
+ const [worker, future_error] =
+ environments.shared_worker(withCoopCoep ? cors_coep_headers : "");
+ future_error.then(test.unreached_func('cannot create shared worker.'));
+ return IsCrossOriginIsolated(worker);
+}
+
+const serviceWorkerIsCrossOriginIsolated = async(
+ test,
+ withCoopCoep
+) => {
+ const [worker, future_error] =
+ environments.service_worker(withCoopCoep ? cors_coep_headers : "");
+ future_error.then(test.unreached_func('cannot create service worker.'));
+ return IsCrossOriginIsolated(worker);
+}
+
+const dedicatedWorkerIsCrossOriginIsolated = async (
+ test,
+ scheme,
+ parent_permission_coi
+) => {
+ let parent_headers = cors_coep_headers;
+ if (parent_permission_coi !== undefined) {
+ // Escape right parenthesis in WPT cors_coep_headers:
+ parent_permission_coi = parent_permission_coi.replace(')', '\\)');
+ parent_headers += `|header(permissions-policy,` +
+ `cross-origin-isolated=${parent_permission_coi})`;
+ }
+ switch (scheme) {
+ case 'https':
+ return isHTTPSDedicatedWorkerCrossOriginIsolated(test, parent_headers);
+ case 'data':
+ return isDataDedicatedWorkerCrossOriginIsolated(test, parent_headers);
+ case 'blob':
+ return isBlobURLDedicatedWorkerCrossOriginIsolated(test, parent_headers);
+ default:
+ assert_unreached("wrong scheme for dedicated worker test.");
+ }
+}
+
+const generate_shared_worker_test = async (withCoopCoep, expected) => {
+ promise_test_parallel(async (test) => {
+ const isCrossOriginIsolated =
+ await sharedWorkerIsCrossOriginIsolated(test, withCoopCoep);
+ assert_equals(isCrossOriginIsolated, expected)
+ }, `shared_worker (withCoopCoep: ${withCoopCoep}) ` +
+ `cross origin isolated permission test`);
+}
+
+const generate_dedicated_worker_test = async (
+ scheme,
+ parent_permission_coi,
+ expected
+) => {
+ promise_test_parallel(async (test) => {
+ const isCrossOriginIsolated =
+ await dedicatedWorkerIsCrossOriginIsolated(test, scheme, parent_permission_coi);
+ assert_equals(isCrossOriginIsolated, expected)
+ }, `dedicated_worker (scheme: ${scheme}) cross origin ` +
+ `isolated (${parent_permission_coi}) permission test`);
+}
+
+const generate_service_worker_test = async (withCoopCoep, expected) => {
+ promise_test_parallel(async (test) => {
+ const isCrossOriginIsolated =
+ await serviceWorkerIsCrossOriginIsolated(test, withCoopCoep);
+ assert_equals(isCrossOriginIsolated, expected)
+ }, `service_worker (withCoopCoep: ${withCoopCoep}) ` +
+ `cross origin isolated permission test`);
+}
+
+generate_shared_worker_test(false, false);
+generate_shared_worker_test(true, true);
+
+generate_dedicated_worker_test('https', undefined, true);
+generate_dedicated_worker_test('https', '*', true);
+generate_dedicated_worker_test('https', 'self', true);
+generate_dedicated_worker_test('https', '()', false);
+generate_dedicated_worker_test('data', undefined, false);
+generate_dedicated_worker_test('data', '*', false);
+generate_dedicated_worker_test('data', 'self', false);
+generate_dedicated_worker_test('blob', undefined, true);
+generate_dedicated_worker_test('blob', '*', true);
+generate_dedicated_worker_test('blob', 'self', true);
+generate_dedicated_worker_test('blob', '()', false);
+
+generate_service_worker_test(false, false);
+generate_service_worker_test(true, true); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js.headers
new file mode 100644
index 0000000000..3b7825def9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cross-origin-isolated-permission-worker.https.window.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html
new file mode 100644
index 0000000000..f2878dfc54
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/script-factory.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(({ data }) => {
+ assert_equals(data.id, "");
+ assert_equals(data.origin, "null");
+ assert_false(data.sameOriginNoCORPSuccess); // This is effectively a no-op for this test
+ assert_true(data.crossOriginNoCORPFailure, "Cross-origin without CORP did not fail");
+ }));
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = `data:text/html,<script>${encodeURIComponent(createScript("null", window.origin))}<\/script>`;
+ document.body.append(frame);
+}, "Cross-Origin-Embedder-Policy and data: URLs");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/data.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https.html
new file mode 100644
index 0000000000..2c97e6f875
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https.html
@@ -0,0 +1,128 @@
+<!doctype html>
+<html>
+<title> Check enforcement of COEP in a DedicatedWorker using CacheStorage. </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+// See also: ./shared-worker-cache-storage.https.html
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+const iframe_path = "./resources/iframe.html?pipe=";
+const dedicated_worker_path = "./universal-worker.js?pipe=";
+const ressource_path = "/images/blue.png?pipe=";
+
+const coep_header= {
+ "coep-none" : "",
+ "coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
+}
+
+const corp_header = {
+ "corp-undefined": "",
+ "corp-cross-origin": "|header(Cross-Origin-Resource-Policy,cross-origin)",
+}
+
+// Check enforcement of COEP in a DedicatedWorker using CacheStorage.
+//
+// 1) Fetch a response from a document with COEP:none. Store it in the
+// CacheStorage. The response is cross-origin without any CORS header.
+// 2) From an iframe, start a DedicatedWorker and try to retrieve the response
+// from the CacheStorage.
+//
+// Test parameters:
+// - |iframe_coep| the COEP header of the iframe's document response
+// - |worker_coep| the COEP header of the DedicatedWorker's script response.
+// - |response_corp| the CORP header of the response.
+//
+// Test expectations:
+// |result|
+// - "success" when the worker is able to fetch the response from the
+// CacheStorage,
+// - "failure" when the worker is not able to fetch the response from the
+// CacheStorage, and
+// - "error" when it is unable to create a worker.
+// https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global
+function check(
+ // Test parameters:
+ iframe_coep,
+ worker_coep,
+ response_corp,
+
+ // Test expectations:
+ result) {
+
+ promise_test(async (t) => {
+ // 1) Fetch a response from a document with COEP:none. Store it in the
+ // CacheStorage. The response is cross-origin without any CORS header.
+ const resource_path = ressource_path + corp_header[response_corp];
+ const resource_url = remote(resource_path);
+ const fetch_request = new Request(resource_url, {mode: 'no-cors'});
+ const cache = await caches.open('v1');
+ const fetch_response = await fetch(fetch_request);
+ await cache.put(fetch_request, fetch_response);
+
+ // 2) From an iframe, start a DedicatedWorker and try to retrieve the
+ // response from the CacheStorage.
+ const worker_url = dedicated_worker_path + coep_header[worker_coep];
+ const worker_eval = `
+ (async function() {
+ const cache = await caches.open('v1');
+ const request = new Request('${resource_url}', {
+ mode: 'no-cors'
+ });
+ try {
+ const response = await cache.match(request);
+ postMessage('success');
+ } catch(error) {
+ postMessage('failure');
+ }
+ })()
+ `;
+
+ const iframe_url = iframe_path + coep_header[iframe_coep];
+ const iframe_eval = `
+ (async function() {
+ const w = new Worker('${worker_url}');
+ const worker_response = new Promise(resolve => w.onmessage = resolve);
+ w.onerror = () => parent.postMessage('error');
+ w.postMessage(\`${worker_eval}\`);
+ const response = await worker_response;
+ parent.postMessage(response.data);
+ })();
+ `;
+
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = iframe_url;
+ const iframe_loaded = new Promise(resolve => iframe.onload = resolve);
+ document.body.appendChild(iframe);
+ await iframe_loaded;
+
+ const iframe_response = new Promise(resolve => {
+ window.addEventListener("message", resolve);
+ })
+ iframe.contentWindow.postMessage(iframe_eval);
+
+ const {data} = await iframe_response;
+ assert_equals(data, result);
+ }, `${iframe_coep} ${worker_coep} ${response_corp}`)
+}
+
+// -----------------------------------------------------------------------------
+// iframe_coep , worker_coep , response_corp , loaded
+// -----------------------------------------------------------------------------
+check("coep-none" , "coep-none" , "corp-cross-origin" , "success");
+check("coep-none" , "coep-none" , "corp-undefined" , "success");
+check("coep-none" , "coep-require-corp" , "corp-cross-origin" , "success");
+check("coep-none" , "coep-require-corp" , "corp-undefined" , "failure");
+check("coep-require-corp" , "coep-none" , "corp-cross-origin" , "error");
+check("coep-require-corp" , "coep-none" , "corp-undefined" , "error");
+check("coep-require-corp" , "coep-require-corp" , "corp-cross-origin" , "success");
+check("coep-require-corp" , "coep-require-corp" , "corp-undefined" , "failure");
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker.https.html
new file mode 100644
index 0000000000..1ba624181c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/dedicated-worker.https.html
@@ -0,0 +1,214 @@
+<!doctype html>
+<title>COEP and dedicated worker</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/worker-support.js"></script>
+<body>
+<script>
+
+const targetUrl = resolveUrl("/common/blank.html", {
+ host: get_host_info().REMOTE_HOST,
+}).href;
+
+function workerUrl(options) {
+ return resolveUrl("resources/dedicated-worker.js", options);
+}
+
+async function createWorker(t, url, options) {
+ const { ownerCoep, workerOptions } = options || {};
+
+ const frameUrl = resolveUrl("/common/blank.html", {
+ coep: ownerCoep,
+ });
+ const frame = await withIframe(t, frameUrl);
+
+ return new frame.contentWindow.Worker(url, workerOptions);
+}
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl());
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'LOADED');
+}, 'COEP: none worker in COEP: none frame');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl(), {
+ ownerCoep: "require-corp",
+ });
+ await new Promise(resolve => {
+ worker.onerror = resolve;
+ });
+}, 'COEP: none worker in COEP: require-corp frame');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }));
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, 'COEP: require-corp worker in COEP: none frame');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }), {
+ ownerCoep: "require-corp",
+ });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, 'COEP: require-corp worker in COEP: require-corp frame');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl(), {
+ workerOptions: { type: 'module' },
+ });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'LOADED');
+}, 'COEP: none module worker in COEP: none frame');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl(), {
+ ownerCoep: "require-corp",
+ workerOptions: { type: 'module' },
+ });
+ await new Promise(resolve => {
+ worker.onerror = resolve;
+ });
+}, 'COEP: none module worker in COEP: require-corp frame');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }), {
+ workerOptions: { type: 'module' },
+ });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, 'COEP: require-corp module worker in COEP: none frame');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }), {
+ ownerCoep: "require-corp",
+ workerOptions: { type: 'module' },
+ });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, 'COEP: require-corp module worker in COEP: require-corp frame');
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "blob",
+ });
+
+ const worker = await createWorker(t, url, { ownerCoep: "require-corp" });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP for blob URL.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "blob",
+ });
+
+ const worker = await createWorker(t, url);
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP from blob URL creator, not owner.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "data",
+ });
+
+ const worker = await createWorker(t, url, { ownerCoep: "require-corp" });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP for data URL.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "data",
+ });
+
+ const worker = await createWorker(t, url);
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'LOADED');
+}, "COEP: worker inherits COEP from owner, not data URL creator.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "filesystem",
+ });
+
+ const worker = await createWorker(t, url, { ownerCoep: "require-corp" });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP for filesystem URL.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "filesystem",
+ });
+
+ const worker = await createWorker(t, url);
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP from filesystem URL creator, not owner.");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/header-parsing.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/header-parsing.https.html
new file mode 100644
index 0000000000..7a25eed51f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/header-parsing.https.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+'use strict';
+function createIframe(t, values) {
+ const parent = document.createElement('iframe');
+ const child = document.createElement('iframe');
+ const params = values.map((value) => {
+ const percentEncodedValue = typeof value === "object" ? value.percentEncoded : encodeURIComponent(value);
+ return `value=${percentEncodedValue}`;
+ });
+ parent.setAttribute('src', `resources/empty-coep.py?${params.join("&")}`);
+ document.body.appendChild(parent);
+ t.add_cleanup(() => parent.remove());
+
+ return new Promise((resolve, reject) => {
+ parent.onload = resolve;
+ parent.onerror = () =>
+ reject(new Error(`failed to load from ${parent.src}`));
+ })
+ .then(() => {
+ child.setAttribute('src', '/common/blank.html');
+ parent.contentDocument.body.appendChild(child);
+ return new Promise((resolve) => {
+ child.onload = resolve;
+ child.onerror = () =>
+ reject(new Error(`failed to load from ${child.src}`));
+ });
+ })
+ .then(() => child);
+}
+
+[
+ [],
+ [''],
+ ['jibberish'],
+ [{ percentEncoded: 'require%FFcorp' }], // non-ASCII byte
+ ['require-corp;'],
+ ['\u000brequire-corp\u000b'], // vertical tab
+ ['\u000crequire-corp\u000c'], // form feed
+ ['\u000drequire-corp\u000d'], // carriage return
+ ['Require-corp'],
+ ['"require-corp"'], // HTTP structured header "string" item
+ [':cmVxdWlyZS1jb3Jw:'], // HTTP structured header "byte sequence" item
+ ['require-corp;\tfoo=bar'],
+ ['require-corp require-corp'],
+ ['require-corp,require-corp'],
+ ['require-corp', 'require-corp'],
+ ['', 'require-corp'],
+ ['require-corp', ''],
+].forEach((values) => {
+ promise_test((t) => {
+ return createIframe(t, values)
+ .then((child) => {
+ assert_not_equals(child.contentDocument, null);
+ });
+ }, 'navigation allowed for ' + JSON.stringify(values));
+});
+
+[
+ ['require-corp'],
+ [' require-corp '],
+ ['\trequire-corp\t'], // leading and trailing OWS is not part of the field-value per HTTP
+ [' \trequire-corp'],
+ ['require-corp\t '],
+ ['require-corp; foo=bar'],
+ ['require-corp;require-corp'],
+ ['require-corp; report-to="data:', '"'], // `require-corp; report-to="data:, "`
+
+].forEach((values) => {
+ promise_test((t) => {
+ return createIframe(t, values)
+ .then((child) => {
+ assert_equals(child.contentDocument, null);
+ });
+ }, 'navigation blocked for ' + JSON.stringify(values));
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/iframe-history-none-require-corp.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/iframe-history-none-require-corp.https.html
new file mode 100644
index 0000000000..0e7ef8108b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/iframe-history-none-require-corp.https.html
@@ -0,0 +1,54 @@
+<meta name="timeout" content="long">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+
+promise_test(async test => {
+ // TODO(arthursonzogni): Consider switching toward another message passing
+ // API like:
+ // /common/dispatcher/dispatcher.js
+ const bc = new BroadcastChannel(token());
+ const futureMessage = () => {
+ return new Promise(resolve => {
+ bc.onmessage = event => resolve(event.data);
+ });
+ };
+
+ const prefix = document.URL.substr(0, document.URL.lastIndexOf('/'))
+ const attribute = `?channelName=${bc.name}`;
+ const url_coep_none =
+ prefix + "/resources/navigate-none.sub.html" + attribute;
+ const url_coep_require_corp =
+ prefix + "/resources/navigate-require-corp.sub.html" + attribute;
+
+ const w = window.open();
+ test.add_cleanup(() => w.close());
+
+ // Navigate to COEP:unsafe-none.
+ w.location.href = url_coep_none;
+ assert_equals(await futureMessage(), "loaded");
+ assert_equals(w.location.href, url_coep_none);
+
+ // For unknown reasons so far. Waiting in between the different navigations
+ // avoids flakes.
+ await new Promise(resolve => test.step_timeout(resolve, 1000));
+
+ // Navigate to COEP:require-corp.
+ w.location.href = url_coep_require_corp;
+ assert_equals(await futureMessage(), "loaded");
+ assert_equals(w.location.href, url_coep_require_corp);
+
+ // For unknown reasons so far. Waiting in between the different navigations
+ // avoids flakes.
+ await new Promise(resolve => test.step_timeout(resolve, 1000));
+
+ // Navigate back to COEP:unsafe-none, using the history API.
+ // Note: `url_coep_none` already take the BFCache into account.
+ w.history.back();
+ assert_equals(await futureMessage(), "loaded");
+ assert_equals(w.location.href, url_coep_none);
+}, `"none" top-level: navigating a frame back from "require-corp" should succeed`);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html
new file mode 100644
index 0000000000..60edf00312
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/script-factory.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(({ data }) => {
+ assert_equals(data.id, "");
+ assert_equals(data.origin, window.origin);
+ assert_true(data.sameOriginNoCORPSuccess);
+ assert_true(data.crossOriginNoCORPFailure, "Cross-origin without CORP did not fail");
+ }));
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = `javascript:${encodeURIComponent(createScript(window.origin, get_host_info().HTTPS_NOTSAMESITE_ORIGIN))}`;
+ document.body.append(frame);
+}, "Cross-Origin-Embedder-Policy and javascript: URLs");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/javascript.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/meta-http-equiv.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/meta-http-equiv.https.html
new file mode 100644
index 0000000000..d35df3135a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/meta-http-equiv.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp"><!-- should not be supported -->
+<title>Cross-Origin-Embedder-Policy in &lt;meta http-equiv></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = "/common/blank.html";
+ document.body.append(frame);
+ assert_equals(frame.contentDocument.URL, "about:blank");
+ assert_equals(frame.contentDocument.body.localName, "body");
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.URL, `${location.protocol}//${location.host}/common/blank.html`);
+ assert_equals(frame.contentDocument.body.localName, "body");
+ });
+}, `<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp"> top-level: navigating a frame to "none" should not fail`);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html
new file mode 100644
index 0000000000..e6261f8388
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Current page</title>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/current.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/worker.js
new file mode 100644
index 0000000000..44103842a4
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/current/worker.js
@@ -0,0 +1 @@
+postMessage('current');
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html
new file mode 100644
index 0000000000..d8bd1ae2c0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Incumbent page</title>
+
+<iframe src="../current/current.html" id="c"></iframe>
+
+<script>
+ const current = document.querySelector("#c").contentWindow;
+
+ window.hello = () => {
+ const worker = new current.Worker('worker.js');
+ worker.onmessage = e => { parent.postMessage(e.data, '*'); }
+ };
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/incumbent.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/worker.js
new file mode 100644
index 0000000000..03f02a8690
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/incumbent/worker.js
@@ -0,0 +1 @@
+postMessage('incumbent');
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/worker.js
new file mode 100644
index 0000000000..fcc521e313
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/worker.js
@@ -0,0 +1 @@
+postMessage('entry');
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html
new file mode 100644
index 0000000000..e1f16a61e1
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Multiple globals for Worker constructor: COEP reports</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This is the entry global -->
+
+<iframe src="incumbent/incumbent.html"></iframe>
+<button onclick="" id="button">Hello</button>
+
+<script>
+async function observeReports(global) {
+ const reports = [];
+ const observer = new global.ReportingObserver((rs) => {
+ for (const r of rs) {
+ reports.push(r.toJSON());
+ }
+ });
+ observer.observe();
+
+ // Wait 5000ms for reports to settle.
+ await new Promise(r => step_timeout(r, 5000));
+ return reports;
+}
+
+async_test((t) => {
+ onload = t.step_func(() => {
+ Promise.all([
+ observeReports(window),
+ observeReports(frames[0]),
+ observeReports(frames[0].frames[0])
+ ]).then(t.step_func_done(([entry, incumbent, current]) => {
+ assert_equals(entry.length, 0);
+ assert_equals(incumbent.length, 0);
+ assert_equals(current.length, 1);
+ const report = current[0];
+ assert_equals(report.type, 'coep');
+ assert_equals(report.url, new URL('current/current.html', location.href).href);
+ assert_equals(report.body.type, 'worker initialization');
+ assert_equals(report.body.blockedURL, new URL('current/worker.js', location.href).href);
+ assert_equals(report.body.disposition, 'enforce');
+ }));
+
+ frames[0].hello();
+ });
+ onmessage = t.unreached_func('worker should have been blocked by COEP');
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/multi-globals/workers-coep-report.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html
new file mode 100644
index 0000000000..6e1573cd64
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.src = get_host_info().HTTP_NOTSAMESITE_ORIGIN + new URL("resources/iframe.html", location).pathname;
+ window.onmessage = t.step_func_done(({ data }) => {
+ assert_equals(data, "success");
+ });
+ frame.onload = t.step_func(() => {
+ frame.contentWindow.postMessage("parent.postMessage('success', '*');", "*");
+ });
+}, "COEP requires a secure context");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/no-secure-context.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html
new file mode 100644
index 0000000000..7fed1fe581
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ let i = 0;
+ frame.onload = t.step_func(() => {
+ i++;
+ assert_equals(frame.contentDocument.URL, "about:blank");
+ frame.src = "about:blank";
+ if (i == 2) {
+ t.done();
+ }
+ });
+ document.body.append(frame);
+}, "Cross-Origin-Embedder-Policy and about:blank");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/non-initial-about-blank.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/none-load-from-cache-storage.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-load-from-cache-storage.https.html
new file mode 100644
index 0000000000..177ae8d11b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-load-from-cache-storage.https.html
@@ -0,0 +1,173 @@
+<!doctype html>
+<html>
+<title> Retrieve resources from CacheStorage with Cross-Origin-Embedder-Policy: require-corp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+
+/*
+ This document does NOT define the Cross-Origin-Embedder-Policy header.
+ Cross-Origin Embedder Policy Editor's draft: https://mikewest.github.io/corpp/
+
+ This test is retrieving same-origin and cross-origin resources from the
+ CacheStorage. The resources are generated from the ServiceWorker or from the
+ network with the header Cross-Origin-Resource-Policy being one of:
+ - 'same-origin'
+ - 'cross-origin'
+ - <undefined>
+*/
+
+promise_test(async (t) => {
+ const SCOPE = new URL(location.href).pathname;
+ const SCRIPT =
+ 'resources/sw-store-to-cache-storage.js?' +
+ `pipe=header(service-worker-allowed,${SCOPE})`;
+
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ add_completion_callback(() => reg.unregister());
+ await new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('controllerchange', resolve);
+ });
+}, 'setting up');
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+function local(path) {
+ return new URL(path, location.origin);
+}
+
+// Send a message to the currently active ServiceWorker and wait for its
+// response.
+function executeCommandInServiceWorker(command) {
+ return new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('message', e => resolve(e.data));
+ navigator.serviceWorker.controller.postMessage(command);
+ });
+}
+
+// Try loading an image from a |response|. Return a Promise resolving or
+// rejecting depending on the image loading result.
+const loadFailure = {name: "Image.onerror"};
+function readImageFromResponse(response) {
+ return new Promise((resolve, reject) => {
+ const img = document.createElement("img");
+ img.onload = resolve.bind(this, "");
+ img.onerror = reject.bind(this, loadFailure);
+ response.blob().then(blob => {
+ img.src = URL.createObjectURL(blob);
+ document.body.appendChild(img);
+ })
+ })
+}
+
+const image_path = "/images/blue.png?pipe=";
+
+const corp_header = {
+ "":"",
+ "corp-undefined": "",
+ "corp-same-origin": "|header(Cross-Origin-Resource-Policy,same-origin)",
+ "corp-cross-origin": "|header(Cross-Origin-Resource-Policy,cross-origin)",
+}
+
+const cors_header = {
+ "":"",
+ "cors-disabled": "",
+ "cors-enabled": "|header(Access-Control-Allow-Origin,*)",
+}
+
+function test(
+ // Test parameters:
+ request_source, request_origin, request_mode, response_cors, response_corp,
+ // Test expectations:
+ response_stored, response_type) {
+ promise_test(async (t) => {
+ // 0. Start from an empty CacheStorage.
+ await caches.delete("v1");
+
+ // 1. Make the ServiceWorker to request the ressource and store it into the
+ // CacheStorage.
+ const path = image_path +
+ corp_header[response_corp] +
+ cors_header[response_cors];
+ const url = (request_origin === "same-origin" ? local : remote)(path);
+ const command = {
+ url: url.href,
+ mode: request_mode,
+ source: request_source,
+ };
+
+ assert_equals(await executeCommandInServiceWorker(command), response_stored);
+ if (response_stored === "not-stored") {
+ return;
+ }
+
+ // 2. Make this document to retrieve it from the CacheStorage.
+ const cache = await caches.open('v1');
+ const response = await cache.match(url);
+
+ assert_equals(response.type, response_type);
+
+ if (request_source === "service-worker") {
+ assert_equals("foo", await response.text());
+ return;
+ }
+
+ // Opaque response are not readable.
+ if (response_type === "opaque") {
+ await promise_rejects_exactly(t, loadFailure, readImageFromResponse(response));
+ return;
+ }
+
+ await readImageFromResponse(response);
+ }, `Fetch ${request_origin} ${request_mode} ${response_cors} ${response_corp} from ${request_source} and CacheStorage.`)
+}
+
+// Responses generated from the ServiceWorker.
+{
+ test("service-worker", "cross-origin", "cors", "", "", "stored", "default");
+ test("service-worker", "cross-origin", "no-cors", "", "", "stored", "default");
+ test("service-worker", "same-origin", "cors", "", "", "stored", "default");
+ test("service-worker", "same-origin", "no-cors", "", "", "stored", "default");
+}
+
+// Responses generated from a same-origin server.
+{
+ const t = test.bind(this, "network", "same-origin");
+ t("cors", "cors-disabled", "corp-cross-origin", "stored", "basic");
+ t("cors", "cors-disabled", "corp-same-origin", "stored", "basic");
+ t("cors", "cors-disabled", "corp-undefined", "stored", "basic");
+ t("cors", "cors-enabled", "corp-cross-origin", "stored", "basic");
+ t("cors", "cors-enabled", "corp-same-origin", "stored", "basic");
+ t("cors", "cors-enabled", "corp-undefined", "stored", "basic");
+ t("no-cors", "cors-disabled", "corp-cross-origin", "stored", "basic");
+ t("no-cors", "cors-disabled", "corp-same-origin", "stored", "basic");
+ t("no-cors", "cors-disabled", "corp-undefined", "stored", "basic");
+ t("no-cors", "cors-enabled", "corp-cross-origin", "stored", "basic");
+ t("no-cors", "cors-enabled", "corp-same-origin", "stored", "basic");
+ t("no-cors", "cors-enabled", "corp-undefined", "stored", "basic");
+}
+
+// Responses generated from a cross-origin server.
+{
+ const t = test.bind(this, "network", "cross-origin");
+ t("cors", "cors-disabled", "corp-cross-origin", "not-stored");
+ t("cors", "cors-disabled", "corp-same-origin", "not-stored");
+ t("cors", "cors-disabled", "corp-undefined", "not-stored");
+ t("cors", "cors-enabled", "corp-cross-origin", "stored", "cors");
+ t("cors", "cors-enabled", "corp-same-origin", "stored", "cors");
+ t("cors", "cors-enabled", "corp-undefined", "stored", "cors");
+ t("no-cors", "cors-disabled", "corp-cross-origin", "stored", "opaque");
+ t("no-cors", "cors-disabled", "corp-same-origin", "not-stored");
+ t("no-cors", "cors-disabled", "corp-undefined", "stored", "opaque");
+ t("no-cors", "cors-enabled", "corp-cross-origin", "stored", "opaque");
+ t("no-cors", "cors-enabled", "corp-same-origin", "not-stored");
+ t("no-cors", "cors-enabled", "corp-undefined", "stored", "opaque");
+}
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-none.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-none.https.html
new file mode 100644
index 0000000000..6d2cf0f64f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-none.https.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const SCOPE = new URL(location.href).pathname;
+const SCRIPT =
+ 'resources/sw.js?' +
+ `pipe=header(service-worker-allowed,${SCOPE})`;
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN + '/html/cross-origin-embedder-policy/');
+}
+
+promise_test(async (t) => {
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ add_completion_callback(() => {
+ reg.unregister();
+ });
+ await new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('controllerchange', resolve);
+ });
+}, 'setting up');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-same-origin-corp.txt', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await fetch('/common/blank.html', {mode: 'no-cors'});
+}, 'making a same-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-cross-origin-corp.js', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await fetch(remote('/common/blank.html'), {mode: 'no-cors'});
+}, 'making a cross-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt?passthrough'),
+ {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await fetch(remote('/common/blank.html?passthrough'), {mode: 'no-cors'});
+}, 'making a cross-origin request for no CORP [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js?passthrough'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, fetch(remote('/common/blank.html'), {mode: 'cors'}));
+}, 'making a cross-origin request with CORS without ACAO');
+
+promise_test(async (t) => {
+ const URL = remote(
+ '/common/blank.html?pipe=header(access-control-allow-origin,*)');
+ await fetch(URL, {mode: 'cors'});
+}, 'making a cross-origin request with CORS');
+
+promise_test(async (t) => {
+ const URL = remote('/fetch/api/resources/preflight.py?allow_headers=hoge');
+ await fetch(URL, {mode: 'cors', headers: {'hoge': 'fuga'}});
+}, 'making a cross-origin request with CORS-preflight');
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html
new file mode 100644
index 0000000000..dc1209c40d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const SCOPE = new URL(location.href).pathname;
+const SCRIPT =
+ 'resources/sw.js?' +
+ `pipe=header(service-worker-allowed,${SCOPE})`;
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN + '/html/cross-origin-embedder-policy/');
+}
+
+promise_test(async (t) => {
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ add_completion_callback(() => {
+ reg.unregister();
+ });
+ await new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('controllerchange', resolve);
+ });
+}, 'setting up');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-same-origin-corp.txt', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await fetch('/common/blank.html', {mode: 'no-cors'});
+}, 'making a same-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-cross-origin-corp.js', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, fetch(remote('/common/blank.html'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt?passthrough'),
+ {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('/common/blank.html?passthrough'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for no CORP [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js?passthrough'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, fetch(remote('/common/blank.html'), {mode: 'cors'}));
+}, 'making a cross-origin request with CORS without ACAO');
+
+promise_test(async (t) => {
+ const URL = remote(
+ '/common/blank.html?pipe=header(access-control-allow-origin,*)');
+ await fetch(URL, {mode: 'cors'});
+}, 'making a cross-origin request with CORS');
+
+promise_test(async (t) => {
+ const URL = remote('/fetch/api/resources/preflight.py?allow_headers=hoge');
+ await fetch(URL, {mode: 'cors', headers: {'hoge': 'fuga'}});
+}, 'making a cross-origin request with CORS-preflight');
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html.headers
new file mode 100644
index 0000000000..8df98474b5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/none-sw-from-require-corp.https.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html
new file mode 100644
index 0000000000..cf9b34b4ca
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html
@@ -0,0 +1,91 @@
+<meta name="timeout" content="long">
+<title>Cross-Origin-Embedder-Policy header and nested navigable resource without such header</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script> <!-- Use token() to allow running tests in parallel -->
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+
+const HOST = get_host_info();
+const BASE = new URL("resources", location).pathname;
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func_done(() => {
+ assert_not_equals(frame.contentDocument, null);
+ });
+ frame.src = "/common/blank.html";
+ document.body.append(frame);
+ assert_equals(frame.contentDocument.body.localName, "body");
+}, `"none" top-level: navigating a frame to "none" should succeed`);
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ const blank = "/common/blank.html";
+ let firstNavOk = false;
+ frame.onload = t.step_func(() => {
+ if (!firstNavOk) {
+ assert_not_equals(frame.contentDocument, null);
+ firstNavOk = true;
+ } else {
+ assert_not_equals(frame.contentDocument, null);
+ assert_equals(frame.contentWindow.location.pathname, blank);
+ t.done();
+ }
+ });
+ frame.src = `resources/navigate-require-corp.sub.html?to=${blank}`;
+ document.body.append(frame);
+ assert_equals(frame.contentDocument.body.localName, "body");
+}, `"none" top-level: navigating a frame from "require-corp" to "none" should succeed`);
+
+async_test(t => {
+ let pageLoaded = false;
+ // TODO(arthursonzogni): Consider switching toward another message passing
+ // API like:
+ // /common/dispatcher/dispatcher.js
+ const bc = new BroadcastChannel(token());
+ let finished = false;
+ let doneCheck = _ => {
+ if (finished && pageLoaded) {
+ t.done();
+ }
+ }
+ bc.onmessage = t.step_func((event) => {
+ pageLoaded = true;
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+
+ doneCheck();
+ });
+
+ const bc2 = new BroadcastChannel(token());
+ bc2.onmessage = t.step_func((event) => {
+ finished = true;
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+
+ doneCheck();
+ });
+
+ const win = window.open(`resources/navigate-require-corp.sub.html?channelName=${bc.name}&to=navigate-none.sub.html?channelName=${bc2.name}`, "_blank", "noopener");
+ assert_equals(win, null);
+}, `"require-corp" top-level noopener popup: navigating to "none" should succeed`);
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const id = token();
+ t.add_cleanup(() => frame.remove());
+ window.addEventListener('message', t.step_func((e) => {
+ if (e.data === id) {
+ // Loaded!
+ t.done();
+ }
+ }));
+ frame.src = `${HOST.HTTPS_NOTSAMESITE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?token=${id}`;
+ document.body.append(frame);
+}, 'CORP: same-site is not checked.');
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html.headers
new file mode 100644
index 0000000000..43c44cffd6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/none.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: unknown-should-be-parsed-as-null
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js
new file mode 100644
index 0000000000..f4d59955af
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js
@@ -0,0 +1,2 @@
+// META: global=window,worker,sharedworker-module,serviceworker-module
+test(t => assert_equals(crossOriginEmbedderPolicy, "credentialless"));
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js.headers
new file mode 100644
index 0000000000..32523a6978
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: credentialless
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js
new file mode 100644
index 0000000000..f6019c2457
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js
@@ -0,0 +1,2 @@
+// META: global=window,worker,sharedworker-module,serviceworker-module
+test(t => assert_equals(crossOriginEmbedderPolicy, "require-corp"));
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-unsafe-none.tentative.https.any.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-unsafe-none.tentative.https.any.js
new file mode 100644
index 0000000000..d2890901eb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reflection-unsafe-none.tentative.https.any.js
@@ -0,0 +1,2 @@
+// META: global=window,worker,sharedworker-module,serviceworker-module
+test(t => assert_equals(crossOriginEmbedderPolicy, "unsafe-none"));
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html
new file mode 100644
index 0000000000..ff9e5b64a0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Cross-Origin-Embedder-Policy-Report-Only header does not affect the actual behavior</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script> <!-- Use token() to allow running tests in parallel -->
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+const HOST = get_host_info();
+const BASE = new URL("resources", location).pathname;
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func_done(() => {
+ assert_not_equals(frame.contentDocument, null);
+ });
+ frame.src = "/common/blank.html";
+ document.body.append(frame);
+ assert_equals(frame.contentDocument.body.localName, "body");
+}, `"none" top-level: navigating a frame to "none" should succeed`);
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ const blank = "/common/blank.html";
+ let firstNavOk = false;
+ frame.onload = t.step_func(() => {
+ if (!firstNavOk) {
+ assert_not_equals(frame.contentDocument, null);
+ firstNavOk = true;
+ } else {
+ assert_not_equals(frame.contentDocument, null);
+ assert_equals(frame.contentWindow.location.pathname, blank);
+ t.done();
+ }
+ });
+ frame.src = `resources/navigate-require-corp.sub.html?to=${blank}`;
+ document.body.append(frame);
+ assert_equals(frame.contentDocument.body.localName, "body");
+}, `"none" top-level: navigating a frame from "require-corp" to "none" should succeed`);
+
+async_test(t => {
+ const w = window.open(`resources/navigate-none.sub.html?to=navigate-require-corp.sub.html`, "window_name");
+ t.add_cleanup(() => w.close());
+
+ w.onload = t.step_func(() => {
+ w.history.back();
+ t.step_timeout(() => {
+ assert_not_equals(w.document, null);
+ t.done();
+ }, 1500);
+ });
+}, `"none" top-level: navigating a frame back from "require-corp" should succeed`);
+
+async_test(t => {
+ let pageLoaded = false;
+ const bc = new BroadcastChannel(token());
+ let finished = false;
+ let doneCheck = _ => {
+ if (finished && pageLoaded) {
+ t.done();
+ }
+ }
+ bc.onmessage = t.step_func((event) => {
+ pageLoaded = true;
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+
+ doneCheck();
+ });
+
+ const bc2 = new BroadcastChannel(token());
+ bc2.onmessage = t.step_func((event) => {
+ finished = true;
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+
+ doneCheck();
+ });
+
+ const win = window.open(`resources/navigate-require-corp.sub.html?channelName=${bc.name}&to=navigate-none.sub.html?channelName=${bc2.name}`, "_blank", "noopener");
+ assert_equals(win, null);
+}, `"require-corp" top-level noopener popup: navigating to "none" should succeed`);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html.headers
new file mode 100644
index 0000000000..289659a41f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/report-only-require-corp.https.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy-report-only: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-navigation.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-navigation.https.html
new file mode 100644
index 0000000000..dea8947818
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-navigation.https.html
@@ -0,0 +1,170 @@
+<!doctype html>
+<html>
+<meta name="timeout" content="long">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./credentialless/resources/common.js"></script>
+<script>
+const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
+const COEP = '|header(cross-origin-embedder-policy,require-corp)';
+const COEP_RO =
+ '|header(cross-origin-embedder-policy-report-only,require-corp)';
+const CORP_CROSS_ORIGIN =
+ '|header(cross-origin-resource-policy,cross-origin)';
+const CSP_FRAME_ANCESTORS_NONE =
+ '|header(content-security-policy,frame-ancestors \'none\')';
+const XFRAMEOPTIONS_DENY =
+ '|header(x-frame-options,deny)';
+const FRAME_URL = `${ORIGIN}/common/blank.html?pipe=`;
+const REMOTE_FRAME_URL = `${REMOTE_ORIGIN}/common/blank.html?pipe=`;
+
+function checkCorpReport(report, contextUrl, blockedUrl, disposition) {
+ assert_equals(report.type, 'coep');
+ assert_equals(report.url, contextUrl);
+ assert_equals(report.body.type, 'corp');
+ assert_equals(report.body.blockedURL, blockedUrl);
+ assert_equals(report.body.disposition, disposition);
+ assert_equals(report.body.destination, 'iframe');
+}
+
+function checkCoepMismatchReport(report, contextUrl, blockedUrl, disposition) {
+ assert_equals(report.type, 'coep');
+ assert_equals(report.url, contextUrl);
+ assert_equals(report.body.type, 'navigation');
+ assert_equals(report.body.blockedURL, blockedUrl);
+ assert_equals(report.body.disposition, disposition);
+}
+
+function loadFrame(document, url) {
+ return new Promise((resolve, reject) => {
+ const frame = document.createElement('iframe');
+ frame.src = url;
+ frame.onload = () => resolve(frame);
+ frame.onerror = reject;
+ document.body.appendChild(frame);
+ });
+}
+
+// |parentSuffix| is a suffix for the parent frame URL.
+// When |withEmptyFrame| is true, this function creates an empty frame
+// between the parent and target frames.
+// |targetUrl| is a URL for the target frame.
+async function loadFrames(test, parentSuffix, withEmptyFrame, targetUrl) {
+ const frame = await loadFrame(document, FRAME_URL + parentSuffix);
+ test.add_cleanup(() => frame.remove());
+ let parent;
+ if (withEmptyFrame) {
+ parent = frame.contentDocument.createElement('iframe');
+ frame.contentDocument.body.appendChild(parent);
+ } else {
+ parent = frame;
+ }
+ // Here we don't need "await". This loading may or may not succeed, and
+ // we're not interested in the result.
+ loadFrame(parent.contentDocument, targetUrl);
+
+ return parent;
+}
+
+async function observeReports(global, expected_count) {
+ const reports = [];
+ const receivedEveryReports = new Promise(resolve => {
+ if (expected_count == 0)
+ resolve();
+
+ const observer = new global.ReportingObserver((rs) => {
+ for (const r of rs) {
+ reports.push(r.toJSON());
+ }
+ if (expected_count <= reports.length)
+ resolve();
+ });
+ observer.observe();
+
+ });
+
+ // Wait 5000 ms more to catch additionnal unexpected reports.
+ await receivedEveryReports;
+ await new Promise(r => step_timeout(r, 5000));
+ return reports;
+}
+
+// CASES is a list of test case. Each test case consists of:
+// parent: the suffix of the URL of the parent frame.
+// target: the suffix of the URL of the target frame.
+// reports: the expectation of reports to be made. Each report is one of:
+// 'CORP': CORP violation
+// 'CORP-RO' CORP violation (report only)
+// 'NAV': COEP mismatch between the frames.
+// 'NAV-RO': COEP mismatch between the frames (report only).
+const CASES = [
+ { parent: '', target: '', reports: [] },
+ { parent: '', target: COEP, reports: [] },
+ { parent: COEP, target: COEP, reports: ['CORP'] },
+ { parent: COEP, target: '', reports: ['CORP'] },
+
+ { parent: '', target: CORP_CROSS_ORIGIN, reports: [] },
+ { parent: COEP, target: CORP_CROSS_ORIGIN, reports: ['NAV'] },
+
+ { parent: '', target: COEP + CORP_CROSS_ORIGIN, reports: [] },
+ { parent: COEP, target: COEP + CORP_CROSS_ORIGIN, reports: [] },
+
+ { parent: COEP_RO, target: COEP, reports: ['CORP-RO'] },
+ { parent: COEP_RO, target: '', reports: ['CORP-RO', 'NAV-RO'] },
+ { parent: COEP_RO, target: CORP_CROSS_ORIGIN, reports: ['NAV-RO'] },
+ { parent: COEP_RO, target: COEP + CORP_CROSS_ORIGIN, reports: [] },
+
+ { parent: COEP, target: COEP_RO + CORP_CROSS_ORIGIN, reports: ['NAV'] },
+
+ // Test ordering of CSP frame-ancestors, COEP, and X-Frame-Options
+ { parent: COEP, target: CORP_CROSS_ORIGIN + CSP_FRAME_ANCESTORS_NONE, reports: [] },
+ { parent: COEP, target: CORP_CROSS_ORIGIN + XFRAMEOPTIONS_DENY, reports: ['NAV'] },
+];
+
+for (const testcase of CASES) {
+ for (const withEmptyFrame of [false, true]) {
+ function desc(s) {
+ return s === '' ? '(none)' : s;
+ }
+ // These tests are very slow, so they must be run in parallel using
+ // async_test.
+ async_test(t => {
+ const targetUrl = REMOTE_FRAME_URL + testcase.target;
+ loadFrames(t, testcase.parent, withEmptyFrame, targetUrl)
+ .then(t.step_func(parent => {
+ const contextUrl = parent.src ? parent.src : 'about:blank';
+ observeReports(parent.contentWindow, testcase.reports.length)
+ .then(t.step_func(reports => {
+ assert_equals(reports.length, testcase.reports.length);
+ for (let i = 0; i < reports.length; i += 1) {
+ const report = reports[i];
+ switch (testcase.reports[i]) {
+ case 'CORP':
+ checkCorpReport(report, contextUrl, targetUrl, 'enforce');
+ break;
+ case 'CORP-RO':
+ checkCorpReport(report, contextUrl, targetUrl, 'reporting');
+ break;
+ case 'NAV':
+ checkCoepMismatchReport(report, contextUrl, targetUrl, 'enforce');
+ break;
+ case 'NAV-RO':
+ checkCoepMismatchReport(report, contextUrl, targetUrl, 'reporting');
+ break;
+ default:
+ assert_unreached(
+ 'Unexpected report expeaction: ' + testcase.reports[i]);
+ }
+ }
+ t.done();
+ })).catch(t.step_func(e => { throw e; }));
+ })).catch(t.step_func(e => { throw e; }));
+ }, `parent: ${desc(testcase.parent)}, target: ${desc(testcase.target)}, ` +
+ `with empty frame: ${withEmptyFrame}`);
+ }
+}
+
+</script>
+</body></html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html
new file mode 100644
index 0000000000..e56124a4a0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html
@@ -0,0 +1,206 @@
+<!doctype html>
+<html>
+<meta name="timeout" content="long">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
+const BASE = new URL("resources", location).pathname
+const FRAME_URL = `${ORIGIN}/common/blank.html` +
+ '?pipe=header(cross-origin-embedder-policy,require-corp)' +
+ `|header(cross-origin-embedder-policy-report-only,require-corp)`;
+const WORKER_URL = `${ORIGIN}${BASE}/reporting-worker.js` +
+ '?pipe=header(cross-origin-embedder-policy,require-corp)' +
+ `|header(cross-origin-embedder-policy-report-only,require-corp)`;
+const REPORTING_FRAME_URL = `${ORIGIN}${BASE}/reporting-empty-frame.html` +
+ '?pipe=header(cross-origin-embedder-policy,require-corp)' +
+ `|header(cross-origin-embedder-policy-report-only,require-corp)`;
+
+async function observeReports(global, expected_count) {
+ const reports = [];
+ const receivedEveryReports = new Promise(resolve => {
+ if (expected_count == 0)
+ resolve();
+
+ const observer = new global.ReportingObserver((rs) => {
+ for (const r of rs) {
+ reports.push(r.toJSON());
+ }
+ if (expected_count <= reports.length)
+ resolve();
+ });
+ observer.observe();
+
+ });
+
+ await receivedEveryReports;
+ // Wait 500ms more to catch additionnal unexpected reports.
+ await new Promise(r => step_timeout(r, 500));
+ return reports;
+}
+
+function checkReport(report, contextUrl, blockedUrl, disposition, destination) {
+ assert_equals(report.type, 'coep');
+ assert_equals(report.url, contextUrl);
+ assert_equals(report.body.type, 'corp');
+ assert_equals(report.body.blockedURL, blockedUrl);
+ assert_equals(report.body.disposition, disposition);
+ assert_equals(report.body.destination, destination);
+}
+
+async function fetchInFrame(t, frameUrl, url, expected_count) {
+ const frame = await with_iframe(frameUrl);
+ t.add_cleanup(() => frame.remove());
+
+ const init = { mode: 'no-cors', cache: 'no-store' };
+ let future_reports = observeReports(frame.contentWindow, expected_count);
+ await frame.contentWindow.fetch(url, init).catch(() => {});
+
+ return await future_reports;
+}
+
+async function fetchInWorker(workerOrPort, url) {
+ const script =
+ `fetch('${url}', {mode: 'no-cors', cache: 'no-store'}).catch(() => {});`;
+ const mc = new MessageChannel();
+ workerOrPort.postMessage({script, port: mc.port2}, [mc.port2]);
+ return (await new Promise(r => mc.port1.onmessage = r)).data;
+}
+
+// We want to test several URLs in various environments (document,
+// dedicated worker, shared worser, service worker). As expectations
+// are independent of environment except for the context URLs in reports,
+// we define ENVIRONMENTS and CASES to reduce the code duplication.
+//
+// ENVIRONMENTS is a list of dictionaries. Each dictionary consists of:
+// - tag: the name of the environment
+// - contextUrl: the URL of the environment settings object
+// - run: an async function which generates reports
+// - test: a testharness Test object
+// - url: the URL for a test case (see below)
+//
+// CASES is a list of test cases. Each test case consists of:
+// - name: the name of the test case
+// - url: the URL of the test case
+// - check: a function to check the results
+// - reports: the generated reports
+// - url: the URL of the test case
+// - contextUrl: the URL of the environment settings object (see
+// ENVORONMENTS)
+
+const ENVIRONMENTS = [{
+ tag: 'document',
+ contextUrl: FRAME_URL,
+ run: async (test, url, expected_count) => {
+ return await fetchInFrame(test, FRAME_URL, url, expected_count);
+ },
+}, {
+ tag: 'dedicated worker',
+ contextUrl: WORKER_URL,
+ run: async (test, url, expected_count) => {
+ const worker = new Worker(WORKER_URL);
+ worker.addEventListener('error', test.unreached_func('Worker.onerror'));
+ test.add_cleanup(() => worker.terminate());
+ return await fetchInWorker(worker, url);
+ },
+}, {
+ tag: 'shared worker',
+ contextUrl: WORKER_URL,
+ run: async (test, url, expected_count) => {
+ const worker = new SharedWorker(WORKER_URL);
+ worker.addEventListener('error', test.unreached_func('Worker.onerror'));
+ return await fetchInWorker(worker.port, url);
+ },
+}, {
+ tag: 'service worker',
+ contextUrl: WORKER_URL,
+ run: async (test, url, expected_count) => {
+ // As we don't want the service worker to control any page, generate a
+ // one-time scope.
+ const SCOPE = new URL(`resources/${token()}.html`, location).pathname;
+ const reg =
+ await service_worker_unregister_and_register(test, WORKER_URL, SCOPE);
+ test.add_cleanup(() => reg.unregister());
+ const worker = reg.installing || reg.waiting || reg.active;
+ worker.addEventListener('error', test.unreached_func('Worker.onerror'));
+ return await fetchInWorker(worker, url);
+ },
+}, {
+ tag: 'between service worker and page',
+ contextUrl: REPORTING_FRAME_URL,
+ run: async (test, url, expected_count) => {
+ // Here we use a Service Worker without COEP.
+ const WORKER_URL = `${ORIGIN}${BASE}/sw.js`;
+ const reg = await service_worker_unregister_and_register(
+ test, WORKER_URL, REPORTING_FRAME_URL);
+ test.add_cleanup(() => reg.unregister());
+ const worker = reg.installing || reg.waiting || reg.active;
+ worker.addEventListener('error', test.unreached_func('Worker.onerror'));
+ return await fetchInFrame(
+ test, REPORTING_FRAME_URL, url, expected_count);
+ },
+}];
+
+const CASES = [{
+ name: 'same-origin',
+ url: '/common/text-plain.txt',
+ expected_count: 0,
+ check: (reports, url, contextUrl) => {}
+}, {
+ name: 'blocked by CORP: same-origin',
+ url: `${REMOTE_ORIGIN}${BASE}/nothing-same-origin-corp.txt`,
+ expected_count: 0,
+ check: (reports, url, contextUrl) => {}
+}, {
+ name: 'blocked due to COEP',
+ url: `${REMOTE_ORIGIN}/common/text-plain.txt`,
+ expected_count: 2,
+ check: (reports, contextUrl, url) => {
+ checkReport(reports[0], contextUrl, url, 'reporting', '');
+ checkReport(reports[1], contextUrl, url, 'enforce', '');
+ }
+}, {
+ name: 'blocked during redirect',
+ url: `${ORIGIN}/common/redirect.py?location=` +
+ encodeURIComponent(`${REMOTE_ORIGIN}/common/text-plain.txt`),
+ expected_count: 2,
+ check: (reports, contextUrl, url) => {
+ checkReport(reports[0], contextUrl, url, 'reporting', '');
+ checkReport(reports[1], contextUrl, url, 'enforce', '');
+ },
+}];
+
+for (const env of ENVIRONMENTS) {
+ for (const testcase of CASES) {
+ promise_test(async (t) => {
+ const reports = await env.run(
+ t, testcase.url, testcase.expected_count);
+
+ assert_equals(reports.length, testcase.expected_count);
+ testcase.check(reports, env.contextUrl, testcase.url);
+ }, `[${env.tag}] ${testcase.name}`);
+ }
+}
+
+// A test for a non-empty destination.
+promise_test(async (t) => {
+ const frame = await with_iframe(FRAME_URL);
+ t.add_cleanup(() => frame.remove());
+
+ const url = `${REMOTE_ORIGIN}/common/utils.js`;
+ const script = frame.contentDocument.createElement('script');
+ script.src = url;
+ const future_reports = observeReports(frame.contentWindow, 2);
+ frame.contentDocument.body.appendChild(script);
+
+ const reports = await future_reports;
+ assert_equals(reports.length, 2);
+ checkReport(reports[0], FRAME_URL, url, 'reporting', 'script');
+ checkReport(reports[1], FRAME_URL, url, 'enforce', 'script');
+}, 'destination: script');
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-document-reporting-endpoint.https.window.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-document-reporting-endpoint.https.window.js
new file mode 100644
index 0000000000..09800db2b8
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-document-reporting-endpoint.https.window.js
@@ -0,0 +1,140 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+
+// This file consists of tests for COEP reporting using Reporting-Endpoints
+// header. It exclusively tests that reports can be sent to Reporting-Endpoint
+// configured endpoint.
+const { REMOTE_ORIGIN } = get_host_info();
+
+const REPORT_ENDPOINT = token();
+const REPORT_ONLY_ENDPOINT = token();
+const FRAME_URL = `resources/reporting-empty-frame.html` +
+ `?pipe=header(cross-origin-embedder-policy,require-corp;report-to="endpoint")` +
+ `|header(cross-origin-embedder-policy-report-only,require-corp;report-to="report-only-endpoint")` +
+ `|header(reporting-endpoints, endpoint="/html/cross-origin-embedder-policy/resources/report.py?key=${REPORT_ENDPOINT}"\\, report-only-endpoint="/html/cross-origin-embedder-policy/resources/report.py?key=${REPORT_ONLY_ENDPOINT}")`;
+
+function wait(ms) {
+ return new Promise(resolve => step_timeout(resolve, ms));
+}
+
+async function fetchReports(endpoint) {
+ const res = await fetch(`resources/report.py?key=${endpoint}`, {
+ cache: 'no-store'
+ });
+ if (res.status == 200) {
+ return await res.json();
+ }
+ return [];
+}
+
+async function fetchCoepReport(
+ endpoint, type, blockedUrl, contextUrl, disposition) {
+ blockedUrl = new URL(blockedUrl, location).href;
+ contextUrl = new URL(contextUrl, location).href;
+ const reports = await fetchReports(endpoint);
+ return reports.find(r => (
+ r.type == 'coep' &&
+ r.url == contextUrl &&
+ r.body.type == type &&
+ r.body.blockedURL === blockedUrl &&
+ r.body.disposition === disposition));
+}
+
+async function checkCorpReportExists(
+ endpoint, blockedUrl, contextUrl, destination, disposition) {
+ blockedUrl = new URL(blockedUrl, location).href;
+ contextUrl = new URL(contextUrl, location).href;
+ contextUrl.replace(REPORT_ENDPOINT, "REPORT_ENDPOINT_UUID");
+ contextUrl.replace(REPORT_ONLY_ENDPOINT, "REPORT_ONLY_ENDPOINT_UUID");
+ const report = await fetchCoepReport(
+ endpoint, 'corp', blockedUrl, contextUrl, disposition);
+ assert_true(!!report,
+ `A corp report with blockedURL ${blockedUrl.split("?")[0]} ` +
+ `and url ${contextUrl} is not found.`);
+ assert_equals(report.body.destination, destination);
+}
+
+async function checkNavigationReportExists(
+ endpoint, blockedUrl, contextUrl, disposition) {
+ blockedUrl = new URL(blockedUrl, location).href;
+ contextUrl = new URL(contextUrl, location).href;
+ contextUrl.replace(REPORT_ENDPOINT, "REPORT_ENDPOINT_UUID");
+ contextUrl.replace(REPORT_ONLY_ENDPOINT, "REPORT_ONLY_ENDPOINT_UUID");
+ const report = await fetchCoepReport(
+ endpoint, 'navigation', blockedUrl, contextUrl, disposition);
+ assert_true(!!report,
+ `A navigation report with blockedURL ${blockedUrl.split("?")[0]} ` +
+ `and url ${contextUrl} is not found.`);
+}
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = FRAME_URL;
+ await new Promise(resolve => {
+ iframe.addEventListener('load', resolve, { once: true });
+ document.body.appendChild(iframe);
+ });
+
+ const url = `${REMOTE_ORIGIN}/common/text-plain.txt?${token()}`;
+ const init = { mode: 'no-cors', cache: 'no-store' };
+ // The response comes from cross-origin, and doesn't have a CORP
+ // header, so it is blocked.
+ iframe.contentWindow.fetch(url, init).catch(() => { });
+
+ // Wait for reports to be uploaded.
+ await wait(1000);
+ await checkCorpReportExists(
+ REPORT_ENDPOINT, url, iframe.src, '', 'enforce');
+ await checkCorpReportExists(
+ REPORT_ONLY_ENDPOINT, url, iframe.src, '', 'reporting');
+}, 'subresource CORP');
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = FRAME_URL;
+ await new Promise(resolve => {
+ iframe.addEventListener('load', resolve, { once: true });
+ document.body.appendChild(iframe);
+ });
+
+ const url = `${REMOTE_ORIGIN}/common/blank.html?${token()}`;
+ // The nested frame comes from cross-origin and doesn't have a CORP
+ // header, so it is blocked.
+ const nested = iframe.contentWindow.document.createElement('iframe');
+ nested.src = url;
+ iframe.contentWindow.document.body.appendChild(nested);
+
+ // Wait for reports to be uploaded.
+ await wait(1000);
+ await checkCorpReportExists(
+ REPORT_ENDPOINT, url, iframe.src, 'iframe', 'enforce');
+ await checkCorpReportExists(
+ REPORT_ONLY_ENDPOINT, url, iframe.src, 'iframe', 'reporting');
+}, 'navigation CORP on cross origin');
+
+promise_test(async (t) => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = FRAME_URL;
+ const targetUrl = `/common/blank.html?${token()}`;
+ iframe.addEventListener('load', t.step_func(() => {
+ const nested = iframe.contentDocument.createElement('iframe');
+ nested.src = targetUrl;
+ // |nested| doesn't have COEP whereas |iframe| has, so it is blocked.
+ iframe.contentDocument.body.appendChild(nested);
+ }), { once: true });
+
+ document.body.appendChild(iframe);
+
+ // Wait for reports to be uploaded.
+ await wait(1000);
+ await checkNavigationReportExists(
+ REPORT_ENDPOINT, targetUrl, iframe.src, 'enforce');
+ await checkNavigationReportExists(
+ REPORT_ONLY_ENDPOINT, targetUrl, iframe.src, 'reporting');
+}, 'navigation CORP on same origin');
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html
new file mode 100644
index 0000000000..39c3de7076
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html
@@ -0,0 +1,209 @@
+<!doctype html>
+<html>
+<meta name="timeout" content="long">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+// This file consists of tests for COEP reporting. The tests make COEP
+// violations and see whether reports are sent to the network as specified.
+// We only have basic tests in this file - one for each kind of reports,
+// because we can also test the reporting functionality with ReportingObserver,
+// and that way is faster, easier to debug, and less flaky.
+//
+// For more detailed tests and tests with workers, see tests in other files
+// such as
+// - reporting-navigation.https.html
+// - reporting-subresource-corp.https.html
+// - cache-storage-reporting*.https.html
+// .
+
+const { REMOTE_ORIGIN } = get_host_info();
+const BASE = new URL("resources", location).pathname
+const FRAME_URL = `resources/reporting-empty-frame.html` +
+ `?pipe=header(cross-origin-embedder-policy,require-corp;report-to="endpoint")` +
+ `|header(cross-origin-embedder-policy-report-only,require-corp;report-to="report-only-endpoint")`;
+const WORKER_URL = `resources/shared-worker.js` +
+ '?pipe=header(cross-origin-embedder-policy,require-corp;report-to="endpoint")' +
+ `|header(cross-origin-embedder-policy-report-only,require-corp;report-to="report-only-endpoint")`;
+const REPORT_UUID = "4d8b6d86-c9a8-47c1-871b-111169a8f79c";
+const REPORT_ONLY_UUID = "5d7c1e33-ef88-43c2-9ca3-c67ff300b8c2";
+
+function wait(ms) {
+ return new Promise(resolve => step_timeout(resolve, ms));
+}
+
+async function fetchReports(endpoint) {
+ const res = await fetch(`resources/report.py?key=${endpoint}`, {cache: 'no-store'});
+ if (res.status == 200) {
+ return await res.json();
+ }
+ return [];
+}
+
+async function checkCorpReportExistence(endpoint, blockedUrl, contextUrl, destination, disposition) {
+ blockedUrl = new URL(blockedUrl, location).href;
+ contextUrl = new URL(contextUrl, location).href;
+
+ const timeout = 3000;
+ const retryDelay = 200;
+ for (let i = 0; i * retryDelay < timeout; i++) {
+ const reports = await fetchReports(endpoint);
+ for (const report of reports) {
+ if (report.type !== 'coep' || report.url !== contextUrl ||
+ report.body.type !== 'corp') {
+ continue;
+ }
+ if (report.body.blockedURL === blockedUrl &&
+ report.body.disposition === disposition) {
+ assert_equals(report.body.destination, destination);
+ return;
+ }
+ }
+ await wait(retryDelay);
+ }
+ assert_unreached(`A report whose blockedURL is ${blockedUrl.split("?")[0]} and url is ${contextUrl} is not found.`);
+}
+
+async function checkNavigationReportExistence(endpoint, blockedUrl, contextUrl, disposition) {
+ blockedUrl = new URL(blockedUrl, location).href;
+ contextUrl = new URL(contextUrl, location).href;
+ const timeout = 3000;
+ const retryDelay = 200;
+ for (let i = 0; i * retryDelay < timeout; i++) {
+ const reports = await fetchReports(endpoint);
+ for (const report of reports) {
+ if (report.type !== 'coep' || report.url !== contextUrl ||
+ report.body.type !== 'navigation') {
+ continue;
+ }
+ if (report.body.blockedURL === blockedUrl &&
+ report.body.disposition === disposition) {
+ return;
+ }
+ }
+ await wait(retryDelay);
+ }
+ assert_unreached(`A report whose blockedURL is ${blockedUrl.split("?")[0]} and url is ${contextUrl} is not found.`);
+}
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = FRAME_URL
+ document.body.appendChild(iframe);
+ await new Promise(resolve => {
+ iframe.addEventListener('load', resolve, {once: true});
+ });
+
+ const url = `${REMOTE_ORIGIN}/common/text-plain.txt?${token()}`;
+ const init = { mode: 'no-cors', cache: 'no-store' };
+ // The response comes from cross-origin, and doesn't have a CORP
+ // header, so it is blocked.
+ iframe.contentWindow.fetch(url, init).catch(() => {});
+
+ await checkCorpReportExistence(REPORT_UUID, url, iframe.src, '', 'enforce');
+ await checkCorpReportExistence(
+ REPORT_ONLY_UUID, url, iframe.src, '', 'reporting');
+}, 'subresource CORP');
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = FRAME_URL
+ document.body.appendChild(iframe);
+ await new Promise(resolve => {
+ iframe.addEventListener('load', resolve, {once: true});
+ });
+
+ const w = iframe.contentWindow;
+
+ function attachFrame(url) {
+ const frame = w.document.createElement('iframe');
+ frame.src = url;
+ w.document.body.appendChild(frame);
+ }
+
+ const url = `${REMOTE_ORIGIN}/common/blank.html?${token()}`;
+ // The nested frame comes from cross-origin and doesn't have a CORP
+ // header, so it is blocked.
+ attachFrame(url);
+
+ await checkCorpReportExistence(
+ REPORT_UUID, url, iframe.src, 'iframe', 'enforce');
+ await checkCorpReportExistence(
+ REPORT_ONLY_UUID, url, iframe.src, 'iframe', 'reporting');
+}, 'navigation CORP');
+
+promise_test(async (t) => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = FRAME_URL;
+ const targetUrl = `/common/blank.html?${token()}`;
+ iframe.addEventListener('load', t.step_func(() => {
+ const nested = iframe.contentDocument.createElement('iframe');
+ nested.src = targetUrl;
+ // |nested| doesn't have COEP whereas |iframe| has, so it is blocked.
+ iframe.contentDocument.body.appendChild(nested);
+ }), {once: true});
+
+ document.body.appendChild(iframe);
+
+ await checkNavigationReportExistence(
+ REPORT_UUID, targetUrl, iframe.src, 'enforce');
+ await checkNavigationReportExistence(
+ REPORT_ONLY_UUID, targetUrl, iframe.src, 'reporting');
+}, 'COEP violation on nested frame navigation');
+
+promise_test(async (t) => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = 'resources/reporting-empty-frame-multiple-headers.html.asis';
+ const targetUrl = `/common/blank.html?${token()}`;
+
+ iframe.addEventListener('load', t.step_func(() => {
+ const nested = iframe.contentDocument.createElement('iframe');
+ nested.src = targetUrl;
+ // |nested| doesn't have COEP whereas |iframe| has, so it is blocked.
+ iframe.contentDocument.body.appendChild(nested);
+ }), {once: true});
+
+ document.body.appendChild(iframe);
+
+ await checkNavigationReportExistence(
+ REPORT_UUID, targetUrl, iframe.src, 'enforce');
+ await checkNavigationReportExistence(
+ REPORT_ONLY_UUID, targetUrl, iframe.src, 'reporting');
+
+}, 'Two COEP headers, split inside report-to value');
+
+// Shared worker do not support observer currently, so add test for endpoint
+// here.
+promise_test(async (t) => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = FRAME_URL;
+ const targetUrl = `${REMOTE_ORIGIN}/common/blank.html?${token()}`;
+ document.body.appendChild(iframe);
+
+ const worker = new iframe.contentWindow.SharedWorker(WORKER_URL);
+ worker.port.start();
+ const script =
+ `fetch('${targetUrl}', {mode: 'no-cors', cache: 'no-store'}).catch(e => {});`;
+ worker.addEventListener('error', t.unreached_func('Worker.onerror'));
+ worker.port.postMessage(script);
+
+ await checkCorpReportExistence(
+ REPORT_UUID, targetUrl, WORKER_URL, 'iframe', 'enforce');
+ await checkCorpReportExistence(
+ REPORT_ONLY_UUID, targetUrl, WORKER_URL, 'iframe', 'reporting');
+}, 'Shared worker fetch');
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html.sub.headers
new file mode 100644
index 0000000000..fe2f651dae
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html.sub.headers
@@ -0,0 +1 @@
+Reporting-Endpoints: endpoint="https://{{host}}:{{ports[https][0]}}//html/cross-origin-embedder-policy/resources/report.py?key=4d8b6d86-c9a8-47c1-871b-111169a8f79c", report-only-endpoint="/html/cross-origin-embedder-policy/resources/report.py?key=5d7c1e33-ef88-43c2-9ca3-c67ff300b8c2"
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-frame-owner.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-frame-owner.https.html
new file mode 100644
index 0000000000..331ad898eb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-frame-owner.https.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<html>
+<head>
+<title>Check COEP reports are sent to iframe for 'new Worker()' failure</title>
+</head>
+<body>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const {ORIGIN} = get_host_info();
+const RESOURCES_PATH= new URL("resources", location).pathname;
+const iframe_path = "worker-owner-frame.html?pipe=";
+const worker_path = "universal-worker.js?pipe=";
+
+const coep_header= {
+ "coep-none" : "",
+ "coep-report-only" :
+ "header(Cross-Origin-Embedder-Policy-Report-Only,require-corp)",
+ "coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
+};
+
+function checkReport(report, url, blocked_url, disposition) {
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, url);
+ assert_equals(report.body.type, "worker initialization");
+ assert_equals(report.body.blockedURL, blocked_url);
+ assert_equals(report.body.disposition, disposition);
+}
+
+// Test parameters:
+// - `owner_coep` the COEP header of the iframe document's response.
+// - `worker_coep` the COEP header of the DedicatedWorker's script response.
+//
+// Test expectations:
+// - `length` the length of reports.
+// - `disposition` the disposition in a report's body. Empty string if the
+// length of reports is expected to be 0.
+function check(
+ // Test parameters:
+ owner_coep,
+ worker_coep,
+ // Test expectations:
+ length,
+ disposition) {
+ promise_test(async (t) => {
+ const worker_url = worker_path + coep_header[worker_coep];
+ const iframe_url = iframe_path + coep_header[owner_coep];
+ const iframe = await with_iframe("./resources/" + iframe_url);
+ t.add_cleanup(() => iframe.remove());
+
+ const iframe_response = new Promise(resolve => window.onmessage = resolve);
+ iframe.contentWindow.startWorkerAndObserveReports(worker_url, length > 0);
+
+ const {data} = await iframe_response;
+ assert_equals(data.length, length);
+ if (data.length > 0) {
+ const blocked_url = `${ORIGIN}${RESOURCES_PATH}/${worker_url}`;
+ const url = `${ORIGIN}${RESOURCES_PATH}/${iframe_url}`;
+ checkReport(
+ data[0],
+ url,
+ blocked_url,
+ disposition
+ );
+ }
+ }, `Reporting to ${owner_coep} frame with ${worker_coep} worker`);
+}
+
+// -----------------------------------------------------------------------------
+// owner_coep , worker_coep , length , disposition
+// -----------------------------------------------------------------------------
+check("coep-none" , "coep-none" , 0 , "");
+check("coep-none" , "coep-report-only" , 0 , "");
+check("coep-none" , "coep-require-corp" , 0 , "");
+check("coep-report-only" , "coep-none" , 1 , "reporting");
+check("coep-report-only" , "coep-report-only" , 1 , "reporting");
+check("coep-report-only" , "coep-require-corp" , 0 , "");
+check("coep-require-corp" , "coep-none" , 1 , "enforce");
+check("coep-require-corp" , "coep-report-only" , 1 , "enforce");
+check("coep-require-corp" , "coep-require-corp" , 0 , "");
+
+</script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-worker-owner.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-worker-owner.https.html
new file mode 100644
index 0000000000..c0010876fa
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-to-worker-owner.https.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<html>
+<head>
+<title>Check COEP reports are sent to parent worker for 'new Worker()' failure</title>
+</head>
+<body>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const {ORIGIN} = get_host_info();
+const RESOURCES_PATH= new URL("resources", location).pathname;
+const parent_worker_path = "worker-owner.js?pipe=";
+const worker_path = "universal-worker.js?pipe=";
+
+const coep_header= {
+ "coep-none" : "",
+ "coep-report-only" :
+ "header(Cross-Origin-Embedder-Policy-Report-Only,require-corp)",
+ "coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
+};
+
+function checkReport(report, url, blocked_url, disposition) {
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, url);
+ assert_equals(report.body.type, "worker initialization");
+ assert_equals(report.body.blockedURL, blocked_url);
+ assert_equals(report.body.disposition, disposition);
+}
+
+// Test parameters:
+// - `owner_coep` the COEP header of the parent DedicatedWorker's script
+// response.
+// - `worker_coep` the COEP header of the DedicatedWorker's script response.
+//
+// Test expectations:
+// - `length` the length of reports.
+// - `disposition` the disposition in a report's body. Empty string if the
+// length of reports is expected to be 0.
+function check(
+ // Test parameters:
+ owner_coep,
+ worker_coep,
+ // Test expectations:
+ length,
+ disposition) {
+ promise_test(async (t) => {
+ const worker_url = worker_path + coep_header[worker_coep];
+ const parent_worker_url = parent_worker_path + coep_header[owner_coep];
+ const parent_worker = new Worker('./resources/' + parent_worker_url);
+
+ const worker_response =
+ new Promise(resolve => parent_worker.onmessage = resolve);
+ parent_worker.postMessage(
+ {worker_url: worker_url, wait_for_report: length > 0});
+
+ const {data} = await worker_response;
+ assert_equals(data.length, length);
+ if (data.length > 0) {
+ const blocked_url = `${ORIGIN}${RESOURCES_PATH}/${worker_url}`;
+ const url = `${ORIGIN}${RESOURCES_PATH}/${parent_worker_url}`;
+ checkReport(
+ data[0],
+ url,
+ blocked_url,
+ disposition
+ );
+ }
+ }, `Reporting to ${owner_coep} worker with ${worker_coep} worker`);
+}
+
+// -----------------------------------------------------------------------------
+// owner_coep , worker_coep , length , disposition
+// -----------------------------------------------------------------------------
+check("coep-none" , "coep-none" , 0 , "");
+check("coep-none" , "coep-report-only" , 0 , "");
+check("coep-none" , "coep-require-corp" , 0 , "");
+check("coep-report-only" , "coep-none" , 1 , "reporting");
+check("coep-report-only" , "coep-report-only" , 1 , "reporting");
+check("coep-report-only" , "coep-require-corp" , 0 , "");
+check("coep-require-corp" , "coep-none" , 1 , "enforce");
+check("coep-require-corp" , "coep-report-only" , 1 , "enforce");
+check("coep-require-corp" , "coep-require-corp" , 0 , "");
+
+</script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html
new file mode 100644
index 0000000000..945333b83d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.addEventListener("DOMContentLoaded", resolve);
+ });
+}, "Wait for the DOM to be built.");
+
+promise_test(async t => {
+ let iframe = document.createElement("iframe");
+ let iframe_loaded = new Promise(resolve => iframe.onload = resolve);
+ iframe.src = "about:blank";
+ document.body.appendChild(iframe);
+
+ // The about:blank document can load.
+ await iframe_loaded;
+ assert_not_equals(iframe.contentDocument, null);
+}, "about:blank can always be embedded by a 'require-corp' document");
+
+promise_test(async t => {
+ let iframe_C = document.createElement("iframe");
+ let iframe_B = document.createElement("iframe");
+ iframe_B.src = "about:blank";
+ iframe_C.src = "/common/blank.html";
+ let iframe_B_loaded = new Promise(resolve => iframe_B.onload = resolve);
+ document.body.appendChild(iframe_B);
+
+ // The about:blank frame must be able to load.
+ await iframe_B_loaded;
+ assert_not_equals(iframe_B.contentDocument, null);
+ iframe_B.contentDocument.body.appendChild(iframe_C);
+
+
+ // The document nested under about:blank must not load because it does not
+ // specify the Cross-Origin-Embedder-Policy: require-corp header.
+ // An error page must be displayed instead.
+ // See https://github.com/whatwg/html/issues/125 for why a timeout is used
+ // here. Long term all network error handling should be similar and have a
+ // reliable event.
+ assert_equals(iframe_C.contentWindow.location.href, "about:blank");
+ assert_not_equals(iframe_C.contentDocument, null);
+ await t.step_wait(() => iframe_C.contentDocument === null);
+}, "A(B(C)) A=require-corp, B=about:blank, C=no-require-corp => C can't load");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html.headers
new file mode 100644
index 0000000000..8df98474b5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-blank.https.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html
new file mode 100644
index 0000000000..5d06286d91
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html
@@ -0,0 +1,48 @@
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.addEventListener("DOMContentLoaded", resolve);
+ });
+}, "Wait for the DOM to be built.");
+
+promise_test(async t => {
+ let iframe = document.createElement("iframe");
+ let iframe_loaded = new Promise(resolve => iframe.onload = resolve);
+ iframe.srcdoc = "loaded document";
+ document.body.appendChild(iframe);
+
+ // The about:srcdoc document can load.
+ await iframe_loaded;
+ assert_not_equals(iframe.contentDocument, null);
+ assert_equals(iframe.contentDocument.body.innerText, "loaded document");
+}, "about:srcdoc can always be embedded by a 'require-corp' document");
+
+promise_test(async t => {
+ let iframe_C = document.createElement("iframe");
+ let iframe_B = document.createElement("iframe");
+ iframe_B.srcdoc = "dummy content";
+ iframe_C.src = "/common/blank.html";
+ let iframe_B_loaded = new Promise(resolve => iframe_B.onload = resolve);
+ document.body.appendChild(iframe_B);
+
+ // The about:srcdoc frame must be able to load.
+ await iframe_B_loaded;
+ assert_not_equals(iframe_B.contentDocument, null);
+ assert_equals(iframe_B.contentDocument.body.innerText, "dummy content");
+ iframe_B.contentDocument.body.appendChild(iframe_C);
+
+ // The document nested under about:srcdoc must not load because it does not
+ // specify the Cross-Origin-Embedder-Policy: require-corp header.
+ // An error page must be displayed instead.
+ // See https://github.com/whatwg/html/issues/125 for why a timeout is used
+ // here. Long term all network error handling should be similar and have a
+ // reliable event.
+ assert_equals(iframe_C.contentWindow.location.href, "about:blank");
+ assert_not_equals(iframe_C.contentDocument, null);
+ await t.step_wait(() => iframe_C.contentDocument === null);
+}, "A(B(C)) A=require-corp, B=about:srcdoc, C=no-require-corp => C can't load");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html.headers
new file mode 100644
index 0000000000..8df98474b5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-about-srcdoc.https.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html
new file mode 100644
index 0000000000..269698bc1a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<html>
+<title> Images on a page Cross-Origin-Embedder-Policy: require-corp should load the same from the cache or network</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script>
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+//
+// This test loads a same-origin iframe resources/load-corp-images.html with
+// Cross-Origin-Embedder-Policy: require-corp
+// The iframe loads two cross origin images, one with a
+// Cross-Origin-Resource-Policy: cross-origin header, and one without.
+// We expect the image with the header to load successfully and the one without
+// to fail to load.
+// After the first load we then reload the iframe, with the same expectations
+// for the image loads when they are loaded from the cache.
+//
+
+const RUNS = ["NETWORK", "CACHED"];
+const RESOURCE_DESC = ["No CORP image", "CORP image"];
+
+let EXPECTED_LOADS = {
+ [`${RUNS[0]} - ${RESOURCE_DESC[0]}`]: false,
+ [`${RUNS[0]} - ${RESOURCE_DESC[1]}`]: true,
+ [`${RUNS[1]} - ${RESOURCE_DESC[0]}`]: false,
+ [`${RUNS[1]} - ${RESOURCE_DESC[1]}`]: true,
+}
+
+let TESTS = {};
+for (let t in EXPECTED_LOADS) {
+ TESTS[t] = async_test(t);
+}
+
+window.addEventListener("load", async () => {
+ const t = async_test("main_test");
+ const iframe = document.createElement("iframe");
+ // The token attribute is used to ensure the resource has never been seen by
+ // the HTTP cache. This can be useful if the cache isn't properly flushed in
+ // between two tests.
+ iframe.src = `resources/load-corp-images.html?revalidate=false&token=${token()}`;
+ let runCount = 0;
+ window.addEventListener("message", (event) => {
+ // After the first done event we reload the iframe.
+ if (event.data.done) {
+ ++runCount;
+ if (runCount < RUNS.length) {
+ iframe.contentWindow.location.reload();
+ } else {
+ // After the second done event the test is finished.
+ t.done();
+ }
+ return;
+ }
+
+ // Check that each image either loads or doesn't based on the expectations
+ let testName = `${RUNS[runCount]} - ${event.data.corp ? RESOURCE_DESC[1] : RESOURCE_DESC[0]}`;
+ let test = TESTS[testName];
+ test.step(() => {
+ assert_equals(event.data.loaded, EXPECTED_LOADS[testName], `${testName} should ${EXPECTED_LOADS[testName] ? "" : "not"} succeed`);
+ });
+ test.done();
+ }, false);
+ document.body.appendChild(iframe);
+});
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-cached-images.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html
new file mode 100644
index 0000000000..489230a776
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html
@@ -0,0 +1,179 @@
+<!doctype html>
+<html>
+<title> Retrieve resources from CacheStorage with Cross-Origin-Embedder-Policy: require-corp</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+
+/*
+ This document has the header Cross-Origin-Embedder-Policy: require-corp.
+ Cross-Origin Embedder Policy Editor's draft: https://mikewest.github.io/corpp/
+
+ This test is retrieving same-origin and cross-origin resources from the
+ CacheStorage. The resources are generated from the ServiceWorker or from the
+ network with the header Cross-Origin-Resource-Policy being one of:
+ - 'same-origin'
+ - 'cross-origin'
+ - <undefined>
+*/
+
+promise_test(async (t) => {
+ const SCOPE = new URL(location.href).pathname;
+ const SCRIPT =
+ 'resources/sw-store-to-cache-storage.js?' +
+ `pipe=header(service-worker-allowed,${SCOPE})`;
+
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ add_completion_callback(() => reg.unregister());
+ await new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('controllerchange', resolve);
+ });
+}, 'setting up');
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+function local(path) {
+ return new URL(path, location.origin);
+}
+
+// Send a message to the currently active ServiceWorker and wait for its
+// response.
+function executeCommandInServiceWorker(command) {
+ return new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('message', e => resolve(e.data));
+ navigator.serviceWorker.controller.postMessage(command);
+ });
+}
+
+// Try loading an image from a |response|. Return a Promise resolving or
+// rejecting depending on the image loading result.
+const loadFailure = {name: "Image.onerror"};
+function readImageFromResponse(response) {
+ return new Promise((resolve, reject) => {
+ const img = document.createElement("img");
+ img.onload = resolve.bind(this, "");
+ img.onerror = reject.bind(this, loadFailure);
+ response.blob().then(blob => {
+ img.src = URL.createObjectURL(blob);
+ document.body.appendChild(img);
+ })
+ })
+}
+
+const image_path = "/images/blue.png?pipe=";
+
+const corp_header = {
+ "":"",
+ "corp-undefined": "",
+ "corp-same-origin": "|header(Cross-Origin-Resource-Policy,same-origin)",
+ "corp-cross-origin": "|header(Cross-Origin-Resource-Policy,cross-origin)",
+}
+
+const cors_header = {
+ "":"",
+ "cors-disabled": "",
+ "cors-enabled": "|header(Access-Control-Allow-Origin,*)",
+}
+
+function test(
+ // Test parameters:
+ request_source, request_origin, request_mode, response_cors, response_corp,
+ // Test expectations:
+ response_stored, response_type) {
+ promise_test(async (t) => {
+ // 0. Start from an empty CacheStorage.
+ await caches.delete("v1");
+
+ // 1. Store a cross-origin no-cors response generated from the SW into the
+ // CacheStorage.
+ const path = image_path +
+ corp_header[response_corp] +
+ cors_header[response_cors];
+ const url = (request_origin === "same-origin" ? local : remote)(path);
+ const command = {
+ url: url.href,
+ mode: request_mode,
+ source: request_source,
+ };
+
+ assert_equals(await executeCommandInServiceWorker(command), response_stored);
+ if (response_stored === "not-stored") {
+ return;
+ }
+
+ // 2. Retrieve it from the CacheStorage.
+ const cache = await caches.open('v1');
+
+ if (response_type === 'error') {
+ await promise_rejects_js(t, TypeError, cache.match(url));
+ return;
+ }
+
+ const response = await cache.match(url);
+
+ assert_equals(response.type, response_type);
+
+ if (request_source === "service-worker") {
+ assert_equals("foo", await response.text());
+ return;
+ }
+
+ // Opaque response can't be read from the document.
+ if (response_type === "opaque") {
+ await promise_rejects_exactly(t, loadFailure, readImageFromResponse(response));
+ return;
+ }
+
+ await readImageFromResponse(response);
+ }, `Fetch ${request_origin} ${request_mode} ${response_cors} ${response_corp} from ${request_source} and CacheStorage.`)
+}
+
+// Responses generated from the ServiceWorker.
+{
+ test("service-worker", "cross-origin", "cors", "", "", "stored", "default");
+ test("service-worker", "cross-origin", "no-cors", "", "", "stored", "default");
+ test("service-worker", "same-origin", "cors", "", "", "stored", "default");
+ test("service-worker", "same-origin", "no-cors", "", "", "stored", "default");
+}
+
+// Responses generated from a same-origin server.
+{
+ const t = test.bind(this, "network", "same-origin");
+ t("cors", "cors-disabled", "corp-cross-origin", "stored", "basic");
+ t("cors", "cors-disabled", "corp-same-origin", "stored", "basic");
+ t("cors", "cors-disabled", "corp-undefined", "stored", "basic");
+ t("cors", "cors-enabled", "corp-cross-origin", "stored", "basic");
+ t("cors", "cors-enabled", "corp-same-origin", "stored", "basic");
+ t("cors", "cors-enabled", "corp-undefined", "stored", "basic");
+ t("no-cors", "cors-disabled", "corp-cross-origin", "stored", "basic");
+ t("no-cors", "cors-disabled", "corp-same-origin", "stored", "basic");
+ t("no-cors", "cors-disabled", "corp-undefined", "stored", "basic");
+ t("no-cors", "cors-enabled", "corp-cross-origin", "stored", "basic");
+ t("no-cors", "cors-enabled", "corp-same-origin", "stored", "basic");
+ t("no-cors", "cors-enabled", "corp-undefined", "stored", "basic");
+}
+
+// Responses generated from a cross-origin server.
+{
+ const t = test.bind(this, "network", "cross-origin");
+ t("cors", "cors-disabled", "corp-cross-origin", "not-stored");
+ t("cors", "cors-disabled", "corp-same-origin", "not-stored");
+ t("cors", "cors-disabled", "corp-undefined", "not-stored");
+ t("cors", "cors-enabled", "corp-cross-origin", "stored", "cors");
+ t("cors", "cors-enabled", "corp-same-origin", "stored", "cors");
+ t("cors", "cors-enabled", "corp-undefined", "stored", "cors");
+ t("no-cors", "cors-disabled", "corp-cross-origin", "stored", "opaque");
+ t("no-cors", "cors-disabled", "corp-same-origin", "not-stored");
+ t("no-cors", "cors-disabled", "corp-undefined", "stored", "error");
+ t("no-cors", "cors-enabled", "corp-cross-origin", "stored", "opaque");
+ t("no-cors", "cors-enabled", "corp-same-origin", "not-stored");
+ t("no-cors", "cors-enabled", "corp-undefined", "stored", "error");
+}
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html.headers
new file mode 100644
index 0000000000..8df98474b5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-load-from-cache-storage.https.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-revalidated-images.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-revalidated-images.https.html
new file mode 100644
index 0000000000..420190aad3
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-revalidated-images.https.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<html>
+<title> Images on a page Cross-Origin-Embedder-Policy: require-corp should load the same from the cache or network, even with revalidation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script>
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+//
+// This test loads a same-origin iframe resources/load-corp-images.html with
+// Cross-Origin-Embedder-Policy: require-corp
+// The iframe loads two cross origin images, one with a
+// Cross-Origin-Resource-Policy: cross-origin header, and one without.
+// We expect the image with the header to load successfully and the one without
+// to fail to load.
+// After the first load we then reload the iframe, with the same expectations
+// for the image loads when they are loaded from the cache. Because of the
+// revalidate directive, we will receive a 304 response instead of directly
+// using the cache response.
+//
+
+const RUNS = ["NETWORK", "CACHED"];
+const RESOURCE_DESC = ["No CORP image", "CORP image"];
+
+let EXPECTED_LOADS = {
+ [`${RUNS[0]} - ${RESOURCE_DESC[0]}`]: false,
+ [`${RUNS[0]} - ${RESOURCE_DESC[1]}`]: true,
+ [`${RUNS[1]} - ${RESOURCE_DESC[0]}`]: false,
+ [`${RUNS[1]} - ${RESOURCE_DESC[1]}`]: true,
+}
+
+let TESTS = {};
+for (let t in EXPECTED_LOADS) {
+ TESTS[t] = async_test(t);
+}
+
+window.addEventListener("load", async () => {
+ const t = async_test("main_test");
+ const iframe = document.createElement("iframe");
+ // The token attribute is used to ensure the resource has never been seen by
+ // the HTTP cache. This can be useful if the cache isn't properly flushed in
+ // between two tests.
+ iframe.src = `resources/load-corp-images.html?revalidate=true&token=${token()}`;
+ let runCount = 0;
+ window.addEventListener("message", (event) => {
+ // After the first done event we reload the iframe.
+ if (event.data.done) {
+ ++runCount;
+ if (runCount < RUNS.length) {
+ iframe.contentWindow.location.reload();
+ } else {
+ // After the second done event the test is finished.
+ t.done();
+ }
+ return;
+ }
+
+ // Check that each image either loads or doesn't based on the expectations
+ let testName = `${RUNS[runCount]} - ${event.data.corp ? RESOURCE_DESC[1] : RESOURCE_DESC[0]}`;
+ let test = TESTS[testName];
+ test.step(() => {
+ assert_equals(event.data.loaded, EXPECTED_LOADS[testName], `${testName} should ${EXPECTED_LOADS[testName] ? "" : "not"} succeed`);
+ });
+ test.done();
+ }, false);
+ document.body.appendChild(iframe);
+});
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-none.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-none.https.html
new file mode 100644
index 0000000000..451761e818
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-none.https.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const SCOPE = new URL(location.href).pathname;
+const SCRIPT =
+ 'resources/sw.js?' +
+ `pipe=header(service-worker-allowed,${SCOPE})|` +
+ 'header(cross-origin-embedder-policy,require-corp)';
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN + '/html/cross-origin-embedder-policy/');
+}
+
+promise_test(async (t) => {
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ add_completion_callback(() => {
+ reg.unregister();
+ });
+ await new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('controllerchange', resolve);
+ });
+}, 'setting up');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-same-origin-corp.txt', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await fetch('/common/blank.html', {mode: 'no-cors'});
+}, 'making a same-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-cross-origin-corp.js', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, fetch(remote('/common/blank.html'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt?passthrough'),
+ {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await fetch(remote('/common/blank.html?passthrough'), {mode: 'no-cors'});
+}, 'making a cross-origin request for no CORP [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js?passthrough'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, fetch(remote('/common/blank.html'), {mode: 'cors'}));
+}, 'making a cross-origin request with CORS without ACAO');
+
+promise_test(async (t) => {
+ const URL = remote(
+ '/common/blank.html?pipe=header(access-control-allow-origin,*)');
+ await fetch(URL, {mode: 'cors'});
+}, 'making a cross-origin request with CORS');
+
+promise_test(async (t) => {
+ const URL = remote('/fetch/api/resources/preflight.py?allow_headers=hoge');
+ await fetch(URL, {mode: 'cors', headers: {'hoge': 'fuga'}});
+}, 'making a cross-origin request with CORS-preflight');
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html
new file mode 100644
index 0000000000..c270e3a0d6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const SCOPE = new URL(location.href).pathname;
+const SCRIPT =
+ 'resources/sw.js?' +
+ `pipe=header(service-worker-allowed,${SCOPE})|` +
+ 'header(cross-origin-embedder-policy,require-corp)';
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN + '/html/cross-origin-embedder-policy/');
+}
+
+promise_test(async (t) => {
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ add_completion_callback(() => {
+ reg.unregister();
+ });
+ await new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('controllerchange', resolve);
+ });
+}, 'setting up');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-same-origin-corp.txt', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await fetch('/common/blank.html', {mode: 'no-cors'});
+}, 'making a same-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch('resources/nothing-cross-origin-corp.js', {mode: 'no-cors'});
+}, 'making a same-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, fetch(remote('/common/blank.html'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for no CORP');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('resources/nothing-same-origin-corp.txt?passthrough'),
+ {mode: 'no-cors'}));
+}, 'making a cross-origin request for CORP: same-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ fetch(remote('/common/blank.html?passthrough'), {mode: 'no-cors'}));
+}, 'making a cross-origin request for no CORP [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await fetch(
+ remote('resources/nothing-cross-origin-corp.js?passthrough'),
+ {mode: 'no-cors'});
+}, 'making a cross-origin request for CORP: cross-origin [PASS THROUGH]');
+
+promise_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, fetch(remote('/common/blank.html'), {mode: 'cors'}));
+}, 'making a cross-origin request with CORS without ACAO');
+
+promise_test(async (t) => {
+ const URL = remote(
+ '/common/blank.html?pipe=header(access-control-allow-origin,*)');
+ await fetch(URL, {mode: 'cors'});
+}, 'making a cross-origin request with CORS');
+
+promise_test(async (t) => {
+ const URL = remote('/fetch/api/resources/preflight.py?allow_headers=hoge');
+ await fetch(URL, {mode: 'cors', headers: {'hoge': 'fuga'}});
+}, 'making a cross-origin request with CORS-preflight');
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html.headers
new file mode 100644
index 0000000000..8df98474b5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw-from-require-corp.https.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw.https.html
new file mode 100644
index 0000000000..05272d41a4
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-sw.https.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<title>Cross Origin Embedder Policy: requests initiated from a service worker with 'require-corp'</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+'use strict';
+
+const SCRIPT = 'resources/require-corp-sw.js';
+const SCOPE = 'resources/in-scope';
+let worker = null;
+
+promise_test(async t => {
+ const registration =
+ await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ promise_test(async t => registration.unregister(), 'Clean up global state');
+ worker = registration.installing;
+ await wait_for_state(t, worker, 'activated');
+}, 'Set up global state');
+
+promise_test(async t => {
+ const p = new Promise(resolve =>
+ navigator.serviceWorker.addEventListener('message', resolve,
+ {once: true}));
+ worker.postMessage('WithCorp');
+ assert_equals((await p).data, 'opaque');
+}, "fetch() to 'CORP: cross-origin' response should succeed.");
+
+promise_test(async t => {
+ const p = new Promise(resolve =>
+ navigator.serviceWorker.addEventListener('message', resolve,
+ {once: true}));
+ worker.postMessage('WithoutCorp');
+ assert_equals((await p).data, 'Exception: TypeError');
+}, "fetch() to no CORP response should not succeed.");
+
+promise_test(async t => {
+ const scope = `${SCOPE}-2`;
+ await service_worker_unregister(t, scope);
+ const promise = navigator.serviceWorker.register(
+ 'resources/require-corp-sw-import-scripts.js', {scope});
+ await promise_rejects_js(t, TypeError, promise, 'register() should fail.');
+}, 'importScripts() fails for a script with no corp.');
+
+promise_test(async t => {
+ const scope = `${SCOPE}-3`;
+ await service_worker_unregister(t, scope);
+ const registration = await navigator.serviceWorker.register(
+ 'resources/require-corp-sw-import-scripts.js?corp=cross-origin', {scope});
+ t.add_cleanup(() => registration.unregister());
+}, 'importScripts() succeeds for a script with corp: cross-origin.');
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-worker-script-revalidation.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-worker-script-revalidation.html
new file mode 100644
index 0000000000..74794967fb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp-worker-script-revalidation.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>COEP and dedicated worker</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/worker-support.js"></script>
+<body>
+<script>
+
+promise_test(async (t) => {
+ const worker1 = new Worker("/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py");
+ worker1.onerror = t.unreached_func('Worker.onerror should not be called for first worker');
+ worker1.postMessage("foo");
+ const result1 = await waitForMessage(worker1);
+ assert_equals(result1.data, 'LOADED');
+
+ // Load the worker a second time, which should trigger revalidation of the cached resource.
+ const worker2 = new Worker("/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py");
+ worker2.onerror = t.unreached_func('Worker.onerror should not be called worker second worker');
+ worker2.postMessage("foo");
+ const result2 = await waitForMessage(worker2);
+ assert_equals(result2.data, 'LOADED');
+}, 'COEP: require-corp with revalidated worker script');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html
new file mode 100644
index 0000000000..ad64a485c0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html
@@ -0,0 +1,251 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Cross-Origin-Embedder-Policy header and nested navigable resource without such header</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+<div id=log></div>
+<script>
+const HOST = get_host_info();
+const BASE = new URL("resources", location).pathname;
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = "/common/blank.html";
+ document.body.append(frame);
+ // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
+ // timeout is used here. Long term all network error handling should be similar and have a
+ // reliable event.
+ assert_equals(frame.contentDocument.body.localName, "body");
+ t.step_wait_func_done(() => frame.contentDocument === null);
+}, `"require-corp" top-level: navigating a frame to "none" should fail`);
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ const bc = new BroadcastChannel(token());
+ bc.onmessage = t.step_func((event) => {
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+ t.step_wait_func_done(() => frame.contentDocument === null);
+ });
+ frame.src = `resources/navigate-require-corp.sub.html?channelName=${bc.name}&to=/common/blank.html`;
+ document.body.append(frame);
+ assert_equals(frame.contentDocument.body.localName, "body");
+}, `"require-corp" top-level: navigating a frame from "require-corp" to "none" should fail`);
+
+async_test(t => {
+ let pageLoaded = false;
+ const bc = new BroadcastChannel(token());
+ let finished = false;
+ bc.onmessage = t.step_func((event) => {
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+ pageLoaded = true;
+ });
+
+ const bc2 = new BroadcastChannel(token());
+ bc2.onmessage = t.step_func_done((event) => {
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+ assert_equals(pageLoaded, true);
+ });
+
+ const win = window.open(`resources/navigate-none.sub.html?channelName=${bc.name}&to=navigate-none.sub.html?channelName=${bc2.name}`, "_blank", "noopener");
+ assert_equals(win, null);
+}, `"require-corp" top-level: creating a noopener "none" popup should succeed`);
+
+async_test(t => {
+ let pageLoaded = false;
+ const bc = new BroadcastChannel(token());
+ bc.onmessage = t.step_func_done((event) => {
+ pageLoaded = true;
+ let payload = event.data;
+ assert_equals(payload, "loaded");
+ });
+
+ const win = window.open(`resources/navigate-none.sub.html?channelName=${bc.name}&to=/common/blank.html`, "_blank");
+ t.add_cleanup(() => win.close());
+}, `"require-corp" top-level: creating a "none" popup should succeed.`);
+
+[
+ {
+ "name": "",
+ "title": "as popup"
+ },
+ {
+ "name": "noopener",
+ "title": "as noopener popup"
+ },
+ {
+ "name": "clear opener",
+ "title": "as popup with opener set to null"
+ }
+].forEach(({name, title}) => {
+ async_test(t => {
+ let pageLoaded = false;
+ const bc = new BroadcastChannel(token());
+ bc.onmessage = t.step_func(event => {
+ pageLoaded = true;
+ const payload = event.data;
+ assert_equals(payload, "loaded");
+ });
+
+ const bc2 = new BroadcastChannel(token());
+ bc2.onmessage = t.step_func_done(event => {
+ const payload = event.data;
+ assert_equals(payload, "loaded");
+ assert_equals(pageLoaded, true);
+ });
+
+ let clearOpener = "";
+ if (name === "clear opener") {
+ clearOpener = "&clearOpener=true"
+ }
+
+ let noopener = undefined;
+ if (name === "noopener") {
+ noopener = "noopener"
+ }
+
+ const win = window.open(`resources/navigate-require-corp.sub.html?channelName=${bc.name}${clearOpener}&to=navigate-none.sub.html?channelName=${bc2.name}`, "_blank", noopener);
+ if (name === "noopener") {
+ assert_equals(win, null);
+ } else {
+ t.add_cleanup(() => win.close());
+ }
+ }, `"require-corp" top-level (${title}): navigating to "none" should succeed`);
+});
+
+promise_test(async t => {
+ const response = await fetch(get_host_info().HTTPS_REMOTE_ORIGIN+"/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js", {mode: "no-cors"});
+ assert_equals(response.type, "opaque");
+}, `"require-corp" top-level: fetch() to CORP: cross-origin response should succeed`);
+
+promise_test(t => {
+ return promise_rejects_js(t, TypeError, fetch(get_host_info().HTTPS_REMOTE_ORIGIN+"/common/blank.html", {mode: "no-cors"}));
+}, `"require-corp" top-level: fetch() to response without CORP should fail`);
+
+promise_test(t => {
+ const w = window.open();
+ return promise_rejects_js(t, w.TypeError, w.fetch(get_host_info().HTTPS_REMOTE_ORIGIN+"/common/blank.html", {mode: "no-cors"}));
+}, `"require-corp" top-level: fetch() to response without CORP through a WindowProxy should fail`);
+
+async_test(t => {
+ let w = window.open();
+ const frame = w.document.createElement("iframe");
+ t.add_cleanup(() => {
+ w.close();
+ frame.remove();
+ });
+ frame.src = "/common/blank.html";
+ document.body.append(frame);
+ // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
+ // timeout is used here. Long term all network error handling should be similar and have a
+ // reliable event.
+ assert_equals(frame.contentDocument.body.localName, "body");
+ t.step_wait_func_done(() => frame.contentDocument === null);
+}, `"require-corp" top-level: navigating an iframe to a page without CORP, through a WindowProxy, should fail`);
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const id = token();
+ t.add_cleanup(() => frame.remove());
+ window.addEventListener('message', t.step_func((e) => {
+ if (e.data === id) {
+ // Loaded!
+ t.done();
+ }
+ }));
+ // REMOTE_ORIGIN is cross-origin, same-site.
+ frame.src = `${HOST.HTTPS_REMOTE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?token=${id}`;
+ document.body.append(frame);
+}, 'CORP: same-site is checked and allowed.');
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const id = token();
+ t.add_cleanup(() => frame.remove());
+ let loaded = false;
+ window.addEventListener('message', t.step_func((e) => {
+ if (e.data === id) {
+ loaded = true;
+ }
+ }));
+ t.step_timeout(() => {
+ // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
+ // timeout is used here. Long term all network error handling should be similar and have a
+ // reliable event.
+ assert_false(loaded);
+ t.done();
+ }, 2000);
+
+ // NOTESAMESITE_ORIGIN is cross-origin, cross-site.
+ frame.src = `${HOST.HTTPS_NOTSAMESITE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?token=${id}`;
+ document.body.append(frame);
+}, 'CORP: same-site is checked and blocked.');
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const bc = new BroadcastChannel(token());
+ t.add_cleanup(() => frame.remove());
+ bc.onmessage = t.step_func_done((event) => {
+ const payload = event.data;
+ assert_equals(payload, "loaded");
+ });
+
+ const dest = `${HOST.ORIGIN}${BASE}/navigate-require-corp.sub.html?channelName=${bc.name}`;
+ // REMOTE_ORIGIN is cross-origin, same-site.
+ frame.src = `${HOST.REMOTE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?to=${encodeURIComponent(dest)}`;
+ document.body.append(frame);
+}, 'navigation CORP is checked with the parent frame, not the navigation source - to be allowed');
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const bc = new BroadcastChannel(token());
+ t.add_cleanup(() => frame.remove());
+ let loaded = false;
+ bc.onmessage = t.step_func((event) => {
+ loaded = true;
+ });
+ t.step_timeout(() => {
+ // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
+ // timeout is used here. Long term all network error handling should be similar and have a
+ // reliable event.
+ assert_false(loaded);
+ t.done();
+ }, 2000);
+
+ const dest = `${HOST.REMOTE_ORIGIN}${BASE}/navigate-require-corp.sub.html?channelName=${bc.name}`;
+ // REMOTE_ORIGIN is cross-origin, same-site.
+ frame.src = `${HOST.REMOTE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?to=${encodeURIComponent(dest)}`;
+ document.body.append(frame);
+}, 'navigation CORP is checked with the parent frame, not the navigation source - to be blocked');
+
+async_test(t => {
+ const bc = new BroadcastChannel(token());
+ let loaded = false;
+ bc.onmessage = t.step_func((event) => {
+ loaded = true;
+ });
+ t.step_timeout(() => {
+ // Make sure the iframe didn't load. See
+ // https://github.com/whatwg/html/issues/125 for why a timeout is used
+ // here. Long term all network error handling should be similar and have a
+ // reliable event.
+ assert_false(loaded);
+ t.done();
+ }, 2000);
+
+ const dest = `${HOST.ORIGIN}${BASE}/navigate-require-corp.sub.html?channelName=${bc.name}`;
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ // |dest| is a same-origin URL and hence not blocked by CORP but reidirect.py
+ // is a cross-origin (actually cross-site) URL, so blocked by CORP.
+ frame.src = `${HOST.HTTPS_NOTSAMESITE_ORIGIN}/common/redirect.py?location=${encodeURIComponent(dest)}`;
+ document.body.append(frame);
+}, 'navigation CORP is checked for each redirect');
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/require-corp.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html
new file mode 100644
index 0000000000..928d404672
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html
@@ -0,0 +1,24 @@
+<body>
+<script src="script-factory.js"></script>
+<script>
+const query = new URLSearchParams(window.location.search);
+const id = query.get("id");
+const variant = query.get("variant");
+let parent = "parent";
+if (variant === "subframe") {
+ parent = "parent.parent";
+} else if (variant === "popup") {
+ parent = "opener.parent";
+}
+const blob = new Blob([`<script>${createScript(window.origin, query.get("crossOrigin"), parent, id)}<\/script>`], { type: "text/html" });
+const blobURL = URL.createObjectURL(blob);
+if (variant === "subframe") {
+ const frame = document.createElement("iframe");
+ frame.src = blobURL;
+ document.body.append(frame);
+} else if (variant === "popup") {
+ window.open(blobURL);
+} else {
+ window.location = blobURL;
+}
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js
new file mode 100644
index 0000000000..86dff9c845
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js
@@ -0,0 +1,63 @@
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN).href;
+}
+
+function local(path) {
+ return new URL(path, location.origin).href;
+}
+
+function encode(url) {
+ return encodeURI(url).replace(/\;/g, '%3B');
+}
+
+const resource_path = (new URL('./resources', location)).pathname;
+const header_coep = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+const header_coep_report_only =
+ '|header(Cross-Origin-Embedder-Policy-Report-Only,require-corp)';
+
+const iframe_path = resource_path + '/iframe.html?pipe=';
+const worker_path = resource_path + '/reporting-worker.js?pipe=';
+const image_url = remote('/images/blue.png');
+
+// This script attempt to load a COEP:require-corp CORP:undefined response from
+// the CacheStorage.
+//
+// Executed from different context:
+// - A Document
+// - A ServiceWorker
+// - A DedicatedWorker
+// - A SharedWorker
+//
+// The context has either COEP or COEP-Report-Only defined.
+const eval_script = `
+ (async function() {
+ try {
+ const cache = await caches.open('v1');
+ const request = new Request('${image_url}', { mode: 'no-cors' });
+ const response = await cache.match(request);
+ } catch(e) {
+ }
+ })()
+`;
+
+promise_setup(async (t) => {
+ const cache = await caches.open('v1');
+ const request = new Request(image_url, {mode: 'no-cors'});
+ const response = await fetch(request);
+ await cache.put(request, response);
+}, 'Setup: store a CORS:cross-origin COEP:none response into CacheStorage')
+
+async function makeIframe(test, iframe_url) {
+ const iframe = document.createElement('iframe');
+ test.add_cleanup(() => iframe.remove());
+ iframe.src = iframe_url;
+ const iframe_loaded = new Promise(resolve => iframe.onload = resolve);
+ document.body.appendChild(iframe);
+ await iframe_loaded;
+ return iframe;
+}
+
+function wait(ms) {
+ return new Promise(resolve => step_timeout(resolve, ms));
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html
new file mode 100644
index 0000000000..78c1331132
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<p><code>Cross-Origin-Embedder-Policy: require-corp</code> header set.
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js
new file mode 100644
index 0000000000..8f038a7278
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js
@@ -0,0 +1,19 @@
+async function createIsolatedFrame(origin, headers) {
+ const parent = document.createElement('iframe');
+ const parent_loaded = new Promise(r => parent.onload = () => { r(parent); });
+ const error = new Promise(r => parent.onerror = r);
+ parent.src = origin + "/common/blank.html?pipe=" + headers;
+ parent.anonymous = false;
+ document.body.appendChild(parent);
+ return [parent_loaded, error];
+}
+
+async function IsCrossOriginIsolated(from_token) {
+ const reply_token = token();
+ send(from_token, `
+ send("${reply_token}", self.crossOriginIsolated);
+ `);
+ const reply = await receive(reply_token);
+ assert_true(reply.match(/true|false/) != null);
+ return reply == 'true';
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py
new file mode 100644
index 0000000000..e507846181
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py
@@ -0,0 +1,31 @@
+import json
+import base64
+
+# A 1x1 PNG image.
+# Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+IMAGE = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="
+
+def main(request, response):
+ response.headers.set(b'Access-Control-Allow-Origin', b'*')
+ response.headers.set(b'Access-Control-Allow-Methods', b'OPTIONS, GET, POST')
+ response.headers.set(b'Access-Control-Allow-Headers', b'Content-Type')
+
+ # CORS preflight
+ if request.method == u'OPTIONS':
+ return u''
+
+ if b'true' == request.GET.get(b'revalidate', None):
+ response.headers.set(b'Cache-Control', b'max-age=0, must-revalidate')
+ else:
+ response.headers.set(b'Cache-Control', b'max-age=3600');
+
+ if b'some-etag' == request.headers.get(b'If-None-Match', None):
+ response.status = 304
+ return u''
+
+ if request.GET.get(b'corp-cross-origin', None):
+ response.headers.set(b'Cross-Origin-Resource-Policy', b'cross-origin')
+
+ response.headers.set(b'Etag', b'some-etag')
+ response.headers.set(b'Content-Type', b'image/png')
+ return base64.b64decode(IMAGE)
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py
new file mode 100755
index 0000000000..eef86d1c55
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+
+def main(request, response):
+ headers = []
+ if request.headers.get(b'if-none-match', None):
+ status = 304, u"Not Modified"
+ return status, headers, u""
+ else:
+ headers.append((b"Content-Type", b"text/javascript"))
+ headers.append((b"Cross-Origin-Embedder-Policy", b"require-corp"))
+ headers.append((b"Cache-Control", b"private, max-age=0, must-revalidate"))
+ headers.append((b"ETag", b"abcdef"))
+ status = 200, u"OK"
+ return status, headers, u"self.onmessage = (e) => { self.postMessage('LOADED'); };"
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js
new file mode 100644
index 0000000000..66f3cc3d41
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js
@@ -0,0 +1,7 @@
+self.onmessage = (e) => {
+ fetch(e.data, {mode: 'no-cors'}).then(() => {
+ self.postMessage('LOADED');
+ }, () => {
+ self.postMessage('FAILED');
+ });
+};
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py
new file mode 100644
index 0000000000..d0e547b130
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py
@@ -0,0 +1,7 @@
+def main(request, response):
+ headers = [(b'Content-Type', b'text/html')]
+
+ for value in request.GET.get_list(b'value'):
+ headers.append((b'Cross-Origin-Embedder-Policy', value))
+
+ return (200, headers, u'')
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html
new file mode 100644
index 0000000000..6b0f96221d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Fetch and create Blob</title>
+<script>
+ async function responseToBlob(response) {
+ let blob;
+ try {
+ blob = await response.blob();
+ } catch (e) {
+ return { error: `blob error: ${e.name}` };
+ }
+
+ return { url: URL.createObjectURL(blob) };
+ }
+
+ async function responseToData(response) {
+ const mimeType = response.headers.get("content-type");
+
+ let text;
+ try {
+ text = await response.text();
+ } catch(e) {
+ return { error: `text error: ${e.name}` };
+ }
+
+ return { url: `data:${mimeType},${encodeURIComponent(text)}` };
+ }
+
+ async function responseToFilesystem(response) {
+ if (!window.webkitRequestFileSystem) {
+ return { error: "unimplemented" };
+ }
+
+ let blob;
+ try {
+ blob = await response.blob();
+ } catch (e) {
+ return { error: `blob error: ${e.name}` };
+ }
+
+ const fs = await new Promise(resolve => {
+ window.webkitRequestFileSystem(window.TEMPORARY, 1024*1024, resolve);
+ });
+
+ const file = await new Promise(resolve => {
+ fs.root.getFile('fetch-and-create-url', { create: true }, resolve);
+ });
+
+ const writer = await new Promise(resolve => file.createWriter(resolve));
+
+ try {
+ await new Promise((resolve, reject) => {
+ writer.onwriteend = resolve;
+ writer.onerror = reject;
+ writer.write(blob);
+ });
+ } catch (e) {
+ return { error: `file write error: ${e.name}` };
+ }
+
+ return { url: file.toURL() };
+ }
+
+ async function responseToScheme(response, scheme) {
+ switch (scheme) {
+ case "blob":
+ return responseToBlob(response);
+ case "data":
+ return responseToData(response);
+ case "filesystem":
+ return responseToFilesystem(response);
+ default:
+ return { error: `unknown scheme: ${scheme}` };
+ }
+ }
+
+ async function fetchToScheme(url, scheme) {
+ let response;
+ try {
+ response = await fetch(url);
+ } catch (e) {
+ return { error: `fetch error: ${e.name}` };
+ }
+
+ return responseToScheme(response, scheme);
+ }
+
+ const params = new URL(window.location).searchParams;
+ fetchToScheme(params.get("url"), params.get("scheme"))
+ .then((message) => { parent.postMessage(message, "*"); });
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js
new file mode 100644
index 0000000000..bd60d07952
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js
@@ -0,0 +1,6 @@
+self.addEventListener('message', async (e) => {
+ const param = e.data;
+ // Ignore network error.
+ await fetch(param.url, param.init).catch(() => {});
+ self.postMessage(param.url);
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html
new file mode 100644
index 0000000000..a6b74ad924
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html
@@ -0,0 +1,3 @@
+<script>
+ window.addEventListener("message", message => eval(message.data));
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html
new file mode 100644
index 0000000000..288610046e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+let params = new URLSearchParams(location.search);
+let token = params.get('token');
+let revalidate = params.get('revalidate');
+
+let image_path = `/html/cross-origin-embedder-policy/resources/corp-image.py?token=${token}&revalidate=${revalidate}`;
+
+window.addEventListener("load", async () => {
+ await new Promise(resolve => {
+ let img = document.createElement("img");
+ img.src = remote(image_path);
+ img.onload = () => { window.parent.postMessage({corp: false, loaded: true}, "*"); resolve(); };
+ img.onerror = (e) => { window.parent.postMessage({corp: false, loaded: false}, "*"); resolve(); };
+ document.body.appendChild(img);
+ });
+
+ await new Promise(resolve => {
+ let img = document.createElement("img");
+ img.src = remote(image_path + "&corp-cross-origin=1");
+ img.onload = () => { window.parent.postMessage({corp: true, loaded: true}, "*"); resolve(); };
+ img.onerror = (e) => { window.parent.postMessage({corp: true, loaded: false}, "*"); resolve(); };
+ document.body.appendChild(img);
+ });
+
+ window.parent.postMessage({done: true}, "*")
+});
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers
new file mode 100644
index 0000000000..8df98474b5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html
new file mode 100644
index 0000000000..f1437ba90a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script>
+ let current = new URL(window.location.href);
+ let navigateTo = current.searchParams.get("to");
+ let channelName = current.searchParams.get("channelName");
+ let postMessageTo = current.searchParams.get("postMessageTo");
+ current.search = "";
+ if (navigateTo) {
+ let next = new URL(navigateTo, current);
+ window.addEventListener("load", () => {
+ window.location.href = next.href;
+ });
+ }
+
+ let target = undefined;
+ if (channelName) {
+ target = new BroadcastChannel(channelName);
+ } else if (postMessageTo) {
+ target = eval(postMessageTo);
+ }
+
+ if (target) {
+ // Broadcast only once the DOM is loaded, so that the caller can
+ // access reliably this document's body.
+ window.addEventListener("DOMContentLoaded", () =>
+ target.postMessage("loaded", "*"));
+
+ // The page can also be restored from the back-forward cache:
+ window.addEventListener('pageshow', function(event) {
+ if (event.persisted)
+ target.postMessage("loaded", "*");
+ });
+ }
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html
new file mode 100644
index 0000000000..910317d29b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script>
+ const current = new URL(window.location.href);
+ const token = current.searchParams.get("token");
+ const navigateTo = current.searchParams.get("to");
+ const channelName = current.searchParams.get("channelName");
+ const clearOpener = current.searchParams.get("clearOpener");
+
+ if (clearOpener) {
+ window.opener = null;
+ }
+
+ current.search = "";
+ if (navigateTo) {
+ let next = new URL(navigateTo, current);
+ window.addEventListener("load", () => {
+ window.location = next.href;
+ });
+ }
+
+ if (channelName) {
+ let bc = new BroadcastChannel(channelName);
+ bc.postMessage("loaded");
+ }
+
+ if (parent !== window && token) {
+ parent.postMessage(token, "*");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers
new file mode 100644
index 0000000000..56d0ac3428
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: same-site
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html
new file mode 100644
index 0000000000..0a3c4dd8d7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script>
+ let current = new URL(window.location.href);
+ let navigateTo = current.searchParams.get("to");
+ let channelName = current.searchParams.get("channelName");
+ let clearOpener = current.searchParams.get("clearOpener");
+
+ if (clearOpener) {
+ window.opener = null;
+ }
+
+ current.search = "";
+ if (navigateTo) {
+ let next = new URL(navigateTo, current);
+ window.addEventListener("load", () => {
+ window.location.href = next.href;
+ });
+ }
+
+ if (channelName) {
+ let bc = new BroadcastChannel(channelName);
+ bc.postMessage("loaded");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js
new file mode 100644
index 0000000000..662e9364f9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js
@@ -0,0 +1 @@
+/* Just an empty JS file */
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers
new file mode 100644
index 0000000000..1b88136c01
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt
new file mode 100644
index 0000000000..b9ba801f78
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt
@@ -0,0 +1 @@
+nothing with same-origin CORP
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers
new file mode 100644
index 0000000000..30ddeac2e7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers
@@ -0,0 +1 @@
+Cross-Origin-Resource-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html
new file mode 100644
index 0000000000..3282711dbc
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ opener.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py
new file mode 100644
index 0000000000..100c642d6c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py
@@ -0,0 +1,41 @@
+import json
+
+def main(request, response):
+ response.headers.set(b'Access-Control-Allow-Origin', b'*')
+ response.headers.set(b'Access-Control-Allow-Methods', b'OPTIONS, GET, POST')
+ response.headers.set(b'Access-Control-Allow-Headers', b'Content-Type')
+ response.headers.set(b'Cache-Control', b'no-cache, no-store, must-revalidate')
+
+ # CORS preflight
+ if request.method == u'OPTIONS':
+ return u''
+
+ uuidMap = {
+ b'endpoint': b'01234567-0123-0123-0123-0123456789AB',
+ b'report-only-endpoint': b'01234567-0123-0123-0123-0123456789CD'
+ }
+ key = 0
+ if b'endpoint' in request.GET:
+ key = uuidMap.get(request.GET[b'endpoint'], 0)
+
+ if b'key' in request.GET:
+ key = request.GET[b'key']
+
+ if key == 0:
+ response.status = 400
+ return u'invalid endpoint'
+
+ path = u'/'.join(request.url_parts.path.split(u'/')[:-1]) + u'/'
+ if request.method == u'POST':
+ reports = request.server.stash.take(key, path) or []
+ for report in json.loads(request.body):
+ reports.append(report)
+ request.server.stash.put(key, reports, path)
+ return u'done'
+
+ if request.method == u'GET':
+ response.headers.set(b'Content-Type', b'application/json')
+ return json.dumps(request.server.stash.take(key, path) or [])
+
+ response.status = 400
+ return u'invalid method'
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis
new file mode 100644
index 0000000000..c0b352f1c7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis
@@ -0,0 +1,9 @@
+HTTP/1.1 200 OK
+Content-Type: text/html
+cross-origin-embedder-policy: require-corp; foo="
+cross-origin-embedder-policy: "; report-to="endpoint"
+cross-origin-embedder-policy-report-only: require-corp; foo="
+cross-origin-embedder-policy-report-only: "; report-to="report-only-endpoint"
+
+<!doctype html>
+<meta charset="utf-8">
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html
new file mode 100644
index 0000000000..b1579add2e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<html>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js
new file mode 100644
index 0000000000..0f8a2ce4c8
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js
@@ -0,0 +1,25 @@
+function run({script, port}) {
+ const reports = [];
+ const observer = new ReportingObserver((rs) => {
+ for (const r of rs) {
+ reports.push(r.toJSON());
+ }
+ });
+ // Wait 200ms for reports to settle.
+ setTimeout(() => {
+ observer.disconnect();
+ port.postMessage(reports);
+ }, 200);
+ observer.observe();
+
+ // This eval call may generate some reports.
+ eval(script);
+}
+
+// For DedicatedWorker and ServiceWorker
+self.addEventListener('message', (e) => run(e.data));
+
+// For SharedWorker
+self.addEventListener('connect', (e) => {
+ e.ports[0].onmessage = (ev) => run(ev.data);
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js
new file mode 100644
index 0000000000..e652c5bf30
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js
@@ -0,0 +1,23 @@
+// Service worker with 'COEP: require-corp' response header.
+// This service worker issues a network request to import scripts with or
+// without CORP response header.
+
+importScripts("/common/get-host-info.sub.js");
+
+function url_for_empty_js(corp) {
+ const url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ url.pathname = '/service-workers/service-worker/resources/empty.js';
+ if (corp) {
+ url.searchParams.set(
+ 'pipe', `header(Cross-Origin-Resource-Policy, ${corp})`);
+ }
+ return url.href;
+}
+
+const params = new URL(location.href).searchParams;
+
+if (params.get('corp') === 'cross-origin') {
+ importScripts(url_for_empty_js('cross-origin'));
+} else {
+ importScripts(url_for_empty_js());
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js
new file mode 100644
index 0000000000..fedecee9ea
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js
@@ -0,0 +1,27 @@
+// Service worker with 'COEP: require-corp' response header.
+// This service worker issues a network request for a resource with or without
+// CORP response header.
+
+importScripts("/common/get-host-info.sub.js");
+
+self.addEventListener('message', e => {
+ e.waitUntil((async () => {
+ let result;
+ try {
+ let url;
+ if (e.data === 'WithCorp') {
+ url = get_host_info().HTTPS_REMOTE_ORIGIN +
+ '/html/cross-origin-embedder-policy/resources/' +
+ 'nothing-cross-origin-corp.js';
+ } else if (e.data === 'WithoutCorp') {
+ url = get_host_info().HTTPS_REMOTE_ORIGIN + '/common/blank.html';
+ }
+ const response = await fetch(url, { mode: 'no-cors' });
+ result = response.type;
+ } catch (error) {
+ result = `Exception: ${error.name}`;
+ } finally {
+ e.source.postMessage(result);
+ }
+ })());
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js
new file mode 100644
index 0000000000..ac7a1fda06
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js
@@ -0,0 +1,30 @@
+// This creates a serialized <script> element that is useful for blob/data/srcdoc-style tests.
+
+function createScript(sameOrigin, crossOrigin, type="parent", id="") {
+ return `const data = { id: "${id}",
+ opener: !!window.opener,
+ origin: window.origin,
+ sameOriginNoCORPSuccess: false,
+ crossOriginNoCORPFailure: false };
+function record(promise, token, expectation) {
+ return promise.then(() => data[token] = expectation, () => data[token] = !expectation);
+}
+
+const records = [
+ record(fetch("${crossOrigin}/common/blank.html", { mode: "no-cors" }), "crossOriginNoCORPFailure", false)
+];
+
+if ("${sameOrigin}" !== "null") {
+ records.push(record(fetch("${sameOrigin}/common/blank.html", { mode: "no-cors" }), "sameOriginNoCORPSuccess", true));
+}
+
+Promise.all(records).then(() => {
+ // Using BroadcastChannel is useful for blob: URLs, which are always same-origin
+ if ("${type}" === "channel") {
+ const bc = new BroadcastChannel("${id}");
+ bc.postMessage(data);
+ } else {
+ window.${type}.postMessage(data, "*");
+ }
+});`;
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py
new file mode 100644
index 0000000000..112d7ecbeb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py
@@ -0,0 +1,24 @@
+body = b'''
+'use strict';
+
+onconnect = (event) => {
+ const port = event.ports[0];
+
+ port.onmessage = (event) => {
+ fetch(event.data, { mode: 'no-cors' })
+ .then(
+ () => port.postMessage('success'),
+ () => port.postMessage('failure')
+ );
+ };
+
+ port.postMessage('ready');
+};'''
+
+def main(request, response):
+ headers = [(b'Content-Type', b'text/javascript')]
+
+ for value in request.GET.get_list(b'value'):
+ headers.append((b'Cross-Origin-Embedder-Policy', value))
+
+ return (200, headers, body)
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js
new file mode 100644
index 0000000000..c5f2c3cc2c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js
@@ -0,0 +1,7 @@
+onconnect = (event) => {
+ const port = event.ports[0];
+ port.onmessage = (event) => {
+ eval(event.data);
+ };
+ port.postMessage('ready');
+};
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js
new file mode 100644
index 0000000000..00b9e9395a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js
@@ -0,0 +1,31 @@
+self.addEventListener('activate', (e) => {
+ e.waitUntil(clients.claim());
+});
+
+self.addEventListener('message', (e) => {
+ e.waitUntil((async () => {
+
+ const url = new URL(e.data.url);
+ const request = new Request(url, {mode: e.data.mode});
+ const cache = await caches.open('v1');
+
+ let response;
+ switch(e.data.source) {
+ case "service-worker":
+ response = new Response('foo');
+ break;
+
+ case "network":
+ try {
+ response = await fetch(request);
+ } catch(error) {
+ e.source.postMessage('not-stored');
+ return;
+ }
+ break;
+ }
+
+ await cache.put(request, response);
+ e.source.postMessage('stored');
+ })());
+})
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js
new file mode 100644
index 0000000000..57f0b41ba5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js
@@ -0,0 +1,12 @@
+self.addEventListener('activate', (e) => {
+ e.waitUntil(clients.claim());
+});
+
+self.addEventListener('fetch', (e) => {
+ const url = new URL(e.request.url);
+ if (url.searchParams.has('passthrough')) {
+ return;
+ }
+
+ e.respondWith(fetch(e.request));
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js
new file mode 100644
index 0000000000..5d46edcde2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js
@@ -0,0 +1 @@
+onmessage = message => eval(message.data);
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner-frame.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner-frame.html
new file mode 100644
index 0000000000..509c904d2b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner-frame.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<script src="worker-owner.js"></script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner.js
new file mode 100644
index 0000000000..d1f172a0b8
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner.js
@@ -0,0 +1,36 @@
+const is_worker = !('window' in self);
+const parent_or_self = is_worker ? self : self.parent;
+
+function startWorkerAndObserveReports(worker_url, wait_for_report) {
+ const worker = new Worker(worker_url);
+ const result_promise = new Promise(resolve => {
+ worker.onmessage = _ => resolve('success');
+ worker.onerror = _ => resolve('error');
+ });
+ worker.postMessage("postMessage('reply to owner from worker');");
+
+ const report_promise = new Promise(resolve => {
+ const observer = new ReportingObserver(reports => {
+ observer.disconnect();
+ resolve(reports.map(r => r.toJSON()));
+ });
+ observer.observe();
+ });
+
+ if (wait_for_report) {
+ Promise.all([result_promise, report_promise]).then(results => {
+ parent_or_self.postMessage(results[1]);
+ });
+ } else {
+ result_promise.then(result => {
+ parent_or_self.postMessage([]);
+ });
+ }
+}
+
+if (is_worker) {
+ onmessage = e => {
+ startWorkerAndObserveReports(e.data.worker_url, e.data.wait_for_report);
+ };
+}
+
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-support.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-support.js
new file mode 100644
index 0000000000..860ee6826c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-support.js
@@ -0,0 +1,81 @@
+// Configures `url` such that the response carries a `COEP: ${value}` header.
+//
+// `url` must be a `URL` instance.
+function setCoep(url, value) {
+ url.searchParams
+ .set("pipe", `header(cross-origin-embedder-policy,${value})`);
+}
+
+// Resolves the given `relativeUrl` relative to the current window's location.
+//
+// `options` can contain the following keys:
+//
+// - `coep`: value passed to `setCoep()`, if present.
+// - `host`: overrides the host of the returned URL.
+//
+// Returns a `URL` instance.
+function resolveUrl(relativeUrl, options) {
+ const url = new URL(relativeUrl, window.location);
+
+ if (options !== undefined) {
+ const { coep, host } = options;
+ if (coep !== undefined) {
+ setCoep(url, coep);
+ }
+ if (host !== undefined) {
+ url.host = host;
+ }
+ }
+
+ return url;
+}
+
+// Adds an iframe loaded from `url` to the current document, waiting for it to
+// load before returning.
+//
+// The returned iframe is removed from the document at the end of the test `t`.
+async function withIframe(t, url) {
+ const frame = document.createElement("iframe");
+ frame.src = url;
+
+ t.add_cleanup(() => frame.remove());
+
+ const loadedPromise = new Promise(resolve => {
+ frame.addEventListener('load', resolve, {once: true});
+ });
+ document.body.append(frame);
+ await loadedPromise;
+
+ return frame;
+}
+
+// Asynchronously waits for a single "message" event on the given `target`.
+function waitForMessage(target) {
+ return new Promise(resolve => {
+ target.addEventListener('message', resolve, {once: true});
+ });
+}
+
+// Fetches `url` from a document with COEP `creatorCoep`, then serializes it
+// and returns a URL pointing to the fetched body with the given `scheme`.
+//
+// - `creatorCoep` is passed to `setCoep()`.
+// - `scheme` may be one of: "blob", "data" or "filesystem".
+//
+// The returned URL is valid until the end of the test `t`.
+async function createLocalUrl(t, { url, creatorCoep, scheme }) {
+ const frameUrl = resolveUrl("resources/fetch-and-create-url.html", {
+ coep: creatorCoep,
+ });
+ frameUrl.searchParams.set("url", url);
+ frameUrl.searchParams.set("scheme", scheme);
+
+ const messagePromise = waitForMessage(window);
+ const frame = await withIframe(t, frameUrl);
+
+ const evt = await messagePromise;
+ const message = evt.data;
+ assert_equals(message.error, undefined, "url creation error");
+
+ return message.url;
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html
new file mode 100644
index 0000000000..7a29ac1944
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(({ data }) => {
+ assert_equals(data.origin, "null");
+ assert_true(data.sameOriginWithoutCORP, "Request to same-origin resource without CORP did not fail");
+ assert_true(data.sameOriginWithSameOriginCORP, "Request to same-origin resource with same-origin CORP did not fail");
+ assert_true(data.sameOriginWithCrossOriginCORP, "Request to same-origin resource with cross-origin CORP did not succeed");
+ assert_true(data.crossOriginWithCrossOriginCORP, "Request to cross-origin resource with cross-origin CORP did not succeed");
+ }));
+
+ const origins = get_host_info();
+ const frame = document.createElement("iframe");
+ const nothingCrossOriginCORP = new URL("resources/nothing-cross-origin-corp.js", window.location).pathname;
+ const nothingSameOriginCORP = new URL("resources/nothing-same-origin-corp.txt", window.location).pathname;
+ frame.sandbox = "allow-scripts";
+ frame.srcdoc = `<script>
+const data = { sameOriginWithoutCORP: false,
+ sameOriginWithSameOriginCORP: false,
+ sameOriginWithCrossOriginCORP: false,
+ crossOriginWithCrossOriginCORP: false,
+ origin: self.origin };
+function record(promise, token, expectation) {
+ return promise.then(() => data[token] = expectation, () => data[token] = !expectation);
+}
+Promise.all([
+ record(fetch("/common/blank.html", { mode: "no-cors" }), "sameOriginWithoutCORP", false),
+ record(fetch("${nothingSameOriginCORP}", { mode: "no-cors" }), "sameOriginWithSameOriginCORP", false),
+ record(fetch("${nothingCrossOriginCORP}", { mode: "no-cors" }), "sameOriginWithCrossOriginCORP", true),
+ record(fetch("${origins.HTTPS_NOTSAMESITE_ORIGIN}${nothingCrossOriginCORP}", { mode: "no-cors" }), "crossOriginWithCrossOriginCORP", true)
+]).then(() => parent.postMessage(data, "*"));
+<\/script>`;
+ document.body.append(frame);
+}, "Cross-Origin-Embedder-Policy and sandbox");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/sandbox.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/service-worker-cache-storage.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/service-worker-cache-storage.https.html
new file mode 100644
index 0000000000..873f06ce4f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/service-worker-cache-storage.https.html
@@ -0,0 +1,117 @@
+<!doctype html>
+<html>
+<title> Check enforcement of COEP in a ServiceWorker using CacheStorage. </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+// See also: ./dedicated-worker-cache-storage.https.html
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+const iframe_path = "./resources/iframe.html?pipe=";
+const service_worker_path = "./resources/universal-worker.js?pipe=";
+const ressource_path = "/images/blue.png?pipe=";
+
+const coep_header= {
+ "coep-none" : "",
+ "coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
+}
+
+const corp_header = {
+ "corp-undefined" : "",
+ "corp-cross-origin" : "|header(Cross-Origin-Resource-Policy,cross-origin)",
+}
+
+// Send a message to the |worker| and wait for its response.
+function executeCommandInServiceWorker(worker, command) {
+ const channel = new MessageChannel();
+ const response = new Promise(resolve => channel.port1.onmessage = resolve);
+ worker.postMessage(command, [ channel.port2 ]);
+ return response;
+}
+
+// Check enforcement of COEP in a ServiceWorker using CacheStorage.
+//
+// 1) Fetch a response from a document with COEP:none. Store it in the
+// CacheStorage. The response is cross-origin without any CORS header.
+// 2) From a ServiceWorker, retrieve the response from the CacheStorage.
+//
+// Test parameters:
+// - |worker_coep| the COEP header of the ServiceWorker's script response.
+// - |response_corp| the CORP header of the response.
+//
+// Test expectations:
+// |loaded| is true whenever the worker is able to fetch the response from
+// the CacheStorage. According to the specification:
+// https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global
+// it must be false when:
+// - |worker_coep| is 'coep-require-corp' and
+// - |response-corp| is 'corp-undefined'.
+function check(
+ // Test parameters:
+ worker_coep,
+ response_corp,
+
+ // Test expectations:
+ loaded) {
+
+ promise_test(async (t) => {
+ // 1) Fetch a response from a document with COEP:none. Store it in the
+ // CacheStorage. The response is cross-origin without any CORS header.
+ const resource_path = ressource_path + corp_header[response_corp];
+ const resource_url = remote(resource_path);
+ const fetch_request = new Request(resource_url, {mode: 'no-cors'});
+ const cache = await caches.open('v1');
+ const fetch_response = await fetch(fetch_request);
+ await cache.put(fetch_request, fetch_response);
+
+ // 2) Start a ServiceWorker.
+ const SCOPE= new URL(location.href).pathname;
+ const service_worker_allowed = `|header(service-worker-allowed,${SCOPE})`;
+ const SCRIPT =
+ service_worker_path +
+ coep_header[worker_coep] +
+ service_worker_allowed;
+
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ add_completion_callback(() => reg.unregister());
+
+ // Start talking to the ServiceWorker, no matter its state.
+ const worker = reg.installing || reg.waiting || reg.active;
+
+ // 3) From the service worker, try to retrieve the response from the
+ // CacheStorage.
+ const response = executeCommandInServiceWorker(worker, `
+ (async function() {
+ const cache = await caches.open('v1');
+ const request = new Request('${resource_url}', {
+ mode: 'no-cors'
+ });
+ try {
+ const response = await cache.match(request);
+ message.ports[0].postMessage('success');
+ } catch(error) {
+ message.ports[0].postMessage('error');
+ }
+ })()
+ `);
+ const {data} = await response;
+ assert_equals(data === "success", loaded);
+ }, `A ServiceWorker with ${worker_coep} use CacheStorage to get a ${response_corp} response.`)
+}
+
+// ------------------------------------------------------
+// worker_coep , response_corp , loaded
+// ------------------------------------------------------
+check("coep-none" , "corp-undefined" , true);
+check("coep-none" , "corp-cross-origin" , true);
+check("coep-require-corp" , "corp-undefined" , false);
+check("coep-require-corp" , "corp-cross-origin" , true);
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/shared-workers.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/shared-workers.https.html
new file mode 100644
index 0000000000..2558f2dd0b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/shared-workers.https.html
@@ -0,0 +1,228 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>COEP - policy derivation for Shared Workers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/worker-support.js"></script>
+<body>
+<p>Verify the Cross-Origin Embedder Policy for Shared Workers by performing a
+cross-domain "fetch" request for a resource that does not specify a COEP. Only
+Shared Workers with the default COEP should be able to successfully perform
+this operation.</p>
+<script>
+'use strict';
+
+const testUrl = resolveUrl("resources/empty-coep.py", {
+ host: get_host_info().REMOTE_HOST,
+}).href;
+
+function makeWorkerUrl(options) {
+ return resolveUrl("resources/shared-worker-fetch.js.py", options);
+}
+
+/**
+ * Create a Shared Worker within an iframe
+ *
+ * @param {object} t - a testharness.js subtest instance (used to reset global
+ * state)
+ * @param {string} url - the URL from which the Shared Worker should be
+ * created
+ * @param {string} options.ownerCoep - the Cross-Origin Embedder Policy of the
+ iframe
+ */
+async function createWorker(t, url, options) {
+ const { ownerCoep } = options || {};
+ const frameUrl = resolveUrl("/common/blank.html", { coep: ownerCoep });
+
+ const iframe = await withIframe(t, frameUrl);
+
+ const sw = new iframe.contentWindow.SharedWorker(url);
+ sw.onerror = t.unreached_func('SharedWorker.onerror should not be called');
+
+ await new Promise((resolve) => {
+ sw.port.addEventListener('message', resolve, { once: true });
+ sw.port.start();
+ });
+
+ return sw;
+}
+
+/**
+ * Instruct a Shared Worker to fetch from a specified URL and report on the
+ * success of the operation.
+ *
+ * @param {SharedWorker} worker
+ * @param {string} url - the URL that the worker should fetch
+ */
+function fetchFromWorker(worker, url) {
+ return new Promise((resolve) => {
+ worker.port.postMessage(url);
+ worker.port.addEventListener(
+ 'message', (event) => resolve(event.data), { once: true }
+ );
+ });
+};
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, makeWorkerUrl());
+ const result = await fetchFromWorker(worker, testUrl);
+ assert_equals(result, 'success');
+}, 'default policy (derived from response)');
+
+promise_test(async (t) => {
+ const worker = await createWorker(t, makeWorkerUrl({ coep: 'require-corp' }));
+ const result = await fetchFromWorker(worker, testUrl);
+ assert_equals(result, 'failure');
+}, '"require-corp" (derived from response)');
+
+promise_test(async (t) => {
+ const blobUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "blob",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
+}, 'default policy (derived from owner set due to use of local scheme - blob URL)');
+
+promise_test(async (t) => {
+ const blobUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "blob",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, 'require-corp (derived from blob URL creator)');
+
+promise_test(async (t) => {
+ const blobUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "blob",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl, { ownerCoep: 'require-corp' }),
+ createWorker(t, blobUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, '"require-corp" (derived from owner set due to use of local scheme - blob URL)');
+
+promise_test(async (t) => {
+ const dataUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "data",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
+}, 'default policy (derived from owner set due to use of local scheme - data URL)');
+
+promise_test(async (t) => {
+ const dataUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "data",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
+}, 'default policy (not derived from data URL creator)');
+
+promise_test(async (t) => {
+ const dataUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "data",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl, { ownercoep: 'require-corp' }),
+ createWorker(t, dataUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, '"require-corp" (derived from owner set due to use of local scheme - data URL)');
+
+promise_test(async (t) => {
+ const filesystemUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "filesystem",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
+}, 'default policy (derived from owner set due to use of local scheme - filesystem URL)');
+
+promise_test(async (t) => {
+ const filesystemUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "filesystem",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, 'require-corp (derived from filesystem URL creator)');
+
+promise_test(async (t) => {
+ const filesystemUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "filesystem",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl, { ownerCoep: 'require-corp' }),
+ createWorker(t, filesystemUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, '"require-corp" (derived from owner set due to use of local scheme - filesystem URL)');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html
new file mode 100644
index 0000000000..2937c13381
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/script-factory.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(({ data }) => {
+ assert_equals(data.id, "");
+ assert_equals(data.origin, window.origin);
+ assert_true(data.sameOriginNoCORPSuccess, "Same-origin without CORP did not succeed");
+ assert_true(data.crossOriginNoCORPFailure, "Cross-origin without CORP did not fail");
+ }));
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.srcdoc = `<script>${createScript(window.origin, get_host_info().HTTPS_NOTSAMESITE_ORIGIN)}<\/script>`;
+ document.body.append(frame);
+}, "Cross-Origin-Embedder-Policy and srcdoc");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/srcdoc.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html
new file mode 100644
index 0000000000..e96c7f7e5d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Test that local scheme workers inherit COEP: require-corp from the creating document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ promise_test(async t => {
+ let sameOrigin = "{{location[server]}}";
+ let crossOrigin = "https://{{hosts[][www]}}:{{ports[https][1]}}";
+
+ let testHarness = await fetch(`${sameOrigin}/resources/testharness.js`)
+ .then(r => r.text());
+
+ // Test that fetching same-origin is allowed by COEP.
+ let same_origin_allowed_test = testName => `
+ promise_test(async t => {
+ return fetch("${sameOrigin}/common/blank.html", { mode: "no-cors" });
+ }, "${testName}: Same origin should be allowed.");
+ `;
+
+ // For data URLs, since everything is cross-origin in that case.
+ let same_origin_blocked_test = testName => `
+ promise_test(t => {
+ return promise_rejects_js(
+ t, TypeError,
+ fetch("${sameOrigin}/common/blank.html", { mode: "no-cors" }));
+ }, "${testName}: Same origin should be blocked.");
+ `;
+
+ // Test that fetching cross-origin is blocked by COEP.
+ let cross_origin_blocked_test = testName => `
+ promise_test(t => {
+ return promise_rejects_js(
+ t, TypeError,
+ fetch("${crossOrigin}/common/blank.html", { mode: "no-cors" }));
+ }, "${testName}: Cross origin should be blocked.");
+ `;
+
+ let blob_string = testName => testHarness +
+ same_origin_allowed_test(testName) +
+ cross_origin_blocked_test(testName) + "done();";
+
+ let data_string = testName => testHarness +
+ same_origin_blocked_test(testName) +
+ cross_origin_blocked_test(testName) + "done();";
+
+ let blob_url = context => {
+ let blob = new Blob([blob_string(`blob URL ${context}`)],
+ { type: 'application/javascript' });
+ return URL.createObjectURL(blob);
+ };
+
+ await fetch_tests_from_worker(new Worker(blob_url("dedicated worker")));
+ await fetch_tests_from_worker(new SharedWorker(blob_url("shared worker")));
+
+ let data_url = context => `data:application/javascript,` +
+ `${encodeURIComponent(data_string("data URL " + context))}`;
+
+ await fetch_tests_from_worker(new Worker(data_url("dedicated worker")));
+ await fetch_tests_from_worker(new Worker(data_url("shared worker")));
+ });
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/worker-inheritance.sub.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/META.yml b/testing/web-platform/tests/html/cross-origin-opener-policy/META.yml
new file mode 100644
index 0000000000..b9d578d22f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/META.yml
@@ -0,0 +1,9 @@
+spec: https://html.spec.whatwg.org/multipage/origin.html#cross-origin-opener-policies
+suggested_reviewers:
+ - mikewest
+ - jugglinmike
+ - arturjanc
+ - lweichselbaum
+ - hemeryar
+ - ParisMeuleman
+ - valenting
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/README.md b/testing/web-platform/tests/html/cross-origin-opener-policy/README.md
new file mode 100644
index 0000000000..3f080c82d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/README.md
@@ -0,0 +1,11 @@
+This directory as well as `../cross-origin-embedder-policy/` contains tests for `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy`. Some light background reading:
+
+* [COOP and COEP explained](https://docs.google.com/document/d/1zDlfvfTJ_9e8Jdc8ehuV4zMEu9ySMCiTGMS9y0GU92k/edit)
+* [COOP processing model](https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e) (also defines interaction with COEP)
+* [COEP processing model](https://mikewest.github.io/corpp/)
+* [Open COOP issues](https://github.com/whatwg/html/labels/topic%3A%20cross-origin-opener-policy)
+* [Open COEP issues](https://github.com/whatwg/html/labels/topic%3A%20cross-origin-embedder-policy)
+
+Notes:
+
+* Top-level navigation to a `data:` URL does not work in Chrome and Firefox and is therefore not tested. (This should probably be standardized.)
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html
new file mode 100644
index 0000000000..eda150eb34
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy and a blob URL popup</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script>
+async_test(t => {
+ window.test = t; // Make the test available globally so the blob URL can use it
+ window.furtherPopup = null;
+
+ const bc = new BroadcastChannel(token());
+ bc.onmessage = t.step_func_done(({ data }) => {
+ assert_equals(data.name.length, 0);
+ assert_false(data.opener);
+ assert_true(furtherPopup.closed);
+ });
+
+ const blobContents = `<script>
+const w = window.open("${get_host_info().HTTPS_REMOTE_ORIGIN}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=x&coep=x&channel=${bc.name}", "${bc.name}");
+window.opener.furtherPopup = w;
+<\/script>`;
+ const blob = new Blob([blobContents], { type: "text/html" });
+ const blobURL = URL.createObjectURL(blob);
+ const popup = window.open(blobURL);
+ t.add_cleanup(() => {
+ // Close the popups once the test is complete.
+ // The browsing context of the second popup is closed hence use the
+ // broadcast channel to trigger the closure.
+ bc.postMessage("close");
+ popup.close();
+ });
+ popup.onload = t.step_func(() => {
+ assert_equals(popup.opener, window);
+ assert_equals(popup.location.href, blobURL);
+ assert_equals(popup.document.URL, blobURL);
+ assert_equals(popup.origin, window.origin);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/blob-popup.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html
new file mode 100644
index 0000000000..08f70181fc
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy: blob URL popup</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=../cross-origin-embedder-policy/resources/script-factory.js></script>
+<script>
+["window.open()", "<a>", "<a rel=noopener>"].forEach(type => {
+ promise_test(t => {
+ const origins = get_host_info();
+ const id = `tut mir leid ${type}`;
+ const blob = new Blob([`<script>${createScript(origins.ORIGIN, origins.HTTPS_REMOTE_ORIGIN, "channel", id)}<\/script>`], {type: "text/html"});
+ const blobURL = URL.createObjectURL(blob);
+ const bc = new BroadcastChannel(id);
+
+ if (type === "window.open()") {
+ const popup = window.open(blobURL);
+ t.add_cleanup(() => popup.close());
+ popup.onload = t.step_func(() => {
+ assert_equals(popup.opener, window);
+ assert_equals(popup.location.href, blobURL);
+ assert_equals(popup.document.URL, blobURL);
+ assert_equals(popup.origin, window.origin);
+ });
+ } else {
+ const a = document.createElement("a");
+ a.target = type;
+ if (type === "<a rel=noopener>") {
+ a.rel = "noopener";
+ }
+ a.href = blobURL;
+ a.click();
+ }
+
+ return new Promise(resolve => {
+ bc.onmessage = t.step_func(({ data }) => {
+ assert_equals(data.id, id);
+ assert_equals(data.origin, window.origin);
+ assert_true(data.sameOriginNoCORPSuccess, "Same-origin without CORP did not succeed");
+ assert_true(data.crossOriginNoCORPFailure, "Cross-origin without CORP did not fail");
+ if (type === "<a rel=noopener>") {
+ assert_false(data.opener, 'opener');
+ } else {
+ assert_true(data.opener, 'opener');
+ }
+ resolve();
+ });
+ });
+ }, `COOP+COEP blob URL popup: ${type}`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-blob-popup.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html
new file mode 100644
index 0000000000..714a4b6c42
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy: a navigating popup</title>
+<meta name=timeout content=long>
+<meta name=variant content=?0-1>
+<meta name=variant content=?2-3>
+<meta name=variant content=?4-last>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/subset-tests.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/common.js"></script>
+<script>
+[
+ {
+ "title": "coop/coep",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": true
+ },
+ {
+ "title": "no coop/coep",
+ "coop": "",
+ "coep": "require-corp",
+ "opener": false
+ },
+ {
+ "title": "coop/no coep",
+ "coop": "same-origin",
+ "coep": "",
+ "opener": false
+ },
+ {
+ "title": "no coop/no coep",
+ "coop": "",
+ "coep": "",
+ "opener": false
+ },
+ {
+ "title": "coop unsafe-none/coep",
+ "coop": "unsafe-none",
+ "coep": "require-corp",
+ "opener": false
+ },
+ {
+ "title": "coop unsafe-none/no coep",
+ "coop": "unsafe-none",
+ "coep": "",
+ "opener": false
+ }
+].forEach((variant, i) => {
+ // Only run specified variants
+ if (!shouldRunSubTest(i)) {
+ return;
+ }
+
+ ["same-origin", "same-site"].forEach(site => {
+ const title = `Popup navigating to ${site} with ${variant.title}`;
+ const channel = title.replace(/ /g,"-");
+ const navigateHost = site === "same-origin" ? SAME_ORIGIN : SAME_SITE;
+ const navigateURL = `${navigateHost.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${variant.coop}&coep=${variant.coep}&channel=${channel}`;
+ const opener = site === "same-origin" ? variant.opener : false;
+
+ async_test(t => {
+ // For each test we open a COOP: same-origin/COEP: require-corp document in a popup and then
+ // navigate that to either a same-origin (site=="same-origin") or same-site (site=="same-site")
+ // document whose COOP and COEP are set as per the top-most array. We then verify that this
+ // document has the correct opener for its specific setup.
+ url_test(t, `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=require-corp&navigate=${encodeURIComponent(navigateURL)}`, channel, opener);
+ }, title);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html
new file mode 100644
index 0000000000..83f8f8a33d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy: redirects</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/common.js"></script>
+<script>
+const coopCOEPPath = new URL("resources/coop-coep.py", window.location).pathname;
+
+[
+ {
+ "title": "coop/coep to coop/coep",
+ "redirectCOOP": "same-origin",
+ "redirectCOEP": "require-corp",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": true
+ },
+ {
+ "title": "coop/coep to no coop/coep",
+ "redirectCOOP": "same-origin",
+ "redirectCOEP": "require-corp",
+ "coop": "",
+ "coep": "require-corp",
+ "opener": false
+ },
+ {
+ "title": "no coop/no coep to coop/coep",
+ "redirectCOOP": "",
+ "redirectCOEP": "",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": false
+ },
+ {
+ "title": "coop/no coep to coop/coep",
+ "redirectCOOP": "same-origin",
+ "redirectCOEP": "",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": false
+ },
+ {
+ "title": "coop unsafe-none/coep to coop/coep",
+ "redirectCOOP": "unsafe-none",
+ "redirectCOEP": "require-corp",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": false
+ },
+ {
+ "title": "coop unsafe-none/coep to coop unsafe-inherit/coep",
+ "redirectCOOP": "unsafe-none",
+ "redirectCOEP": "require-corp",
+ "coop": "unsafe-none",
+ "coep": "require-corp",
+ "opener": false
+ }
+].forEach(variant => {
+ const title = `Redirect from ${variant.title}`;
+ async_test(t => {
+ const channel = title.replace(/ /g,"-");
+ const redirectLocation = `${SAME_ORIGIN.origin}${coopCOEPPath}?coop=${variant.coop}&coep=${variant.coep}&channel=${channel}`;
+ url_test(t, `${SAME_ORIGIN.origin}${coopCOEPPath}?coop=${variant.redirectCOOP}&coep=${variant.redirectCOEP}&redirect=${encodeURIComponent(redirectLocation)}`, channel, variant.opener);
+ }, title);
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-redirect.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html
new file mode 100644
index 0000000000..3f6256bcd2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+[
+ {
+ "title": "popup with coop/coep",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none/coep",
+ "coop": "unsafe-none",
+ "coep": "require-corp",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none without coep",
+ "coop": "unsafe-none",
+ "coep": "",
+ "opener": "severed"
+ },
+ {
+ "title": "popup without coep",
+ "coop": "same-origin",
+ "coep": "",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop: variant.coop, coep: variant.coep }, variant.opener);
+});
+
+test(() => {
+ assert_true(window.crossOriginIsolated);
+}, "Bonus: window.crossOriginIsolated");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-cross-origin.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html
new file mode 100644
index 0000000000..66e7aaf88e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+[
+ {
+ "title": "popup with coop/coep",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none/coep",
+ "coop": "unsafe-none",
+ "coep": "require-corp",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none without coep",
+ "coop": "unsafe-none",
+ "coep": "",
+ "opener": "severed"
+ },
+ {
+ "title": "popup without coep",
+ "coop": "same-origin",
+ "coep": "",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop, coep: variant.coep }, variant.opener);
+});
+
+test(() => {
+ assert_true(window.crossOriginIsolated);
+}, "Bonus: window.crossOriginIsolated");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-origin.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html
new file mode 100644
index 0000000000..abce659790
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+[
+ {
+ "title": "popup with coop/coep",
+ "coop": "same-origin",
+ "coep": "require-corp",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none/coep",
+ "coop": "unsafe-none",
+ "coep": "require-corp",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none without coep",
+ "coop": "unsafe-none",
+ "coep": "",
+ "opener": "severed"
+ },
+ {
+ "title": "popup without coep",
+ "coop": "same-origin",
+ "coep": "",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop, coep: variant.coep }, variant.opener);
+});
+
+test(() => {
+ assert_true(window.crossOriginIsolated);
+}, "Bonus: window.crossOriginIsolated");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-with-same-site.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html
new file mode 100644
index 0000000000..4b94435119
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>Sandboxed Cross-Origin-Opener-Policy popup should result in a network error</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+<div id=log>
+<script>
+[
+ "allow-popups allow-scripts allow-same-origin",
+ "allow-popups allow-scripts",
+].forEach(sandboxValue => {
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ const channel = new BroadcastChannel(token());
+ channel.onmessage = t.unreached_func("A COOP popup was created from a sandboxed frame");
+ t.add_cleanup(() => frame.remove());
+ frame.sandbox = sandboxValue;
+ frame.srcdoc = `<script>
+ const popup = window.open("resources/coop-coep.py?coop=same-origin&coep=&channel=${channel.name}");
+ <\/script>`;
+ document.body.append(frame);
+ addEventListener('load', t.step_func(() => {
+ // This uses a timeout to give some time for incorrect implementations to broadcast. A
+ // theoretical testdriver.js API for browsing contexts could be used to speed this up.
+ t.step_timeout(() => {
+ t.done()
+ }, 1500);
+ }));
+ }, `<iframe sandbox="${sandboxValue}"> ${document.title}`);
+});
+
+// Verify that the popup does not have sandboxing flags set
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const channel = new BroadcastChannel(token());
+ channel.onmessage = t.step_func_done();
+ t.add_cleanup(() => frame.remove());
+ frame.sandbox = "allow-popups allow-scripts allow-popups-to-escape-sandbox";
+ frame.srcdoc = `<script>
+window.open("resources/coop-coep.py?coop=same-origin&coep=require-corp&channel=${channel.name}");
+<\/script>`;
+ document.body.append(frame);
+}, `<iframe sandbox="allow-popups allow-scripts allow-popups-to-escape-sandbox"> ${document.title}`);
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const channel = new BroadcastChannel(token());
+ frame.sandbox = "allow-scripts allow-same-origin";
+ frame.name = `iframe-${channel.name}`;
+ frame.src = `resources/coop-coep.py?coop=same-origin&coep=require-corp&channel=${channel.name}`;
+ channel.onmessage = t.step_func( event => {
+ const payload = event.data;
+ assert_equals(payload.name, frame.name, "name");
+ t.done();
+ });
+ t.add_cleanup(() => frame.remove());
+ document.body.append(frame);
+}, `Iframe with sandbox and COOP must load.`);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html.headers
new file mode 100644
index 0000000000..4fff9d9fba
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-coep-sandbox.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox-navigate.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox-navigate.https.html
new file mode 100644
index 0000000000..be2d83214a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox-navigate.https.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>CSP sandbox popup navigate to Cross-Origin-Opener-Policy document should work</title>
+<meta name="timeout" content="long">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+<div id=log>
+<script>
+[
+ "allow-popups allow-scripts allow-same-origin",
+ "allow-popups allow-scripts",
+].forEach(sandboxValue => {
+ async_test(t => {
+ const channel = new BroadcastChannel(token());
+ let popup;
+ channel.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.name, '', 'e.data.name');
+ assert_false(e.data.opener, 'e.data.opener');
+ // `popup` is still the WindowProxy that holds the CSP sandbox document, not the
+ // after-navigation COOP document. The CSP sandbox only applies to the before navigation
+ // document/window.
+ assert_true(popup.closed, 'popup.closed');
+ // Same-origin check (with the CSP sandbox document) should not throw when 'allow-same-origin'
+ if (sandboxValue.includes('allow-same-origin')) {
+ assert_true(!!popup.document, 'same-origin check');
+ } else {
+ assert_throws_dom("SecurityError", () => { popup.document; }, 'same-origin check');
+ }
+ });
+ const navigateTo = `/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=&channel=${channel.name}`;
+ popup = window.open(`resources/csp-sandbox.py?coop=&coep=&sandbox=${sandboxValue}&channel=&navigate=${encodeURIComponent(navigateTo)}`, sandboxValue.replace(/ /g, '_'));
+ t.add_cleanup(() => { popup.close(); });
+ addEventListener('load', t.step_func(() => {
+ t.step_timeout(() => {
+ assert_unreached('Navigation from CSP sandbox to COOP document failed')
+ }, 10000);
+ }));
+ }, `CSP: sandbox ${sandboxValue}; ${document.title}`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox.https.html
new file mode 100644
index 0000000000..259d484df2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-csp-sandbox.https.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>CSP sandboxed Cross-Origin-Opener-Policy popup should result in a network error</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+<div id=log>
+<script>
+[
+ "allow-popups allow-scripts allow-same-origin",
+ "allow-popups allow-scripts",
+].forEach(sandboxValue => {
+ async_test(t => {
+ const channel = new BroadcastChannel(token());
+ channel.onmessage = t.unreached_func("A COOP popup was created from a CSP-sandboxed popup");
+ const popup = window.open(`resources/csp-sandbox.py?coop=same-origin&coep=&sandbox=${sandboxValue}&channel=${channel.name}`);
+ t.add_cleanup(() => { popup.close(); });
+ addEventListener('load', t.step_func(() => {
+ t.step_timeout(() => {
+ t.done()
+ }, 1500);
+ }));
+ }, `CSP: sandbox ${sandboxValue}; ${document.title}`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigate-same-origin-csp-sandbox.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigate-same-origin-csp-sandbox.html
new file mode 100644
index 0000000000..83113de376
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigate-same-origin-csp-sandbox.html
@@ -0,0 +1,63 @@
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="./resources/common.js"></script>
+<script>
+
+const executor_path = '/common/dispatcher/executor.html?pipe=';
+
+const https_origin = get_host_info().HTTPS_ORIGIN;
+const coop_same_origin =
+ '|header(Cross-Origin-Opener-Policy,same-origin)';
+const csp_sandbox =
+ '|header(Content-Security-Policy, sandbox allow-scripts)';
+
+promise_test(async test => {
+ const driver_token = token();
+
+ // 1. Start from a COOP:same-origin document.
+ const opener_token = token();
+ const opener_url = https_origin + executor_path + coop_same_origin +
+ `&uuid=${opener_token}`;
+ const w = window.open(opener_url);
+ add_completion_callback(() => w.close());
+
+ // 2. It opens a popups, and then navigates the popup toward a same-origin
+ // COOP:same-origin document with CSP:sandbox
+ const openee_token = token();
+ const openee_url = https_origin + executor_path + coop_same_origin +
+ csp_sandbox + `&uuid=${openee_token}`;
+ send(opener_token, `
+ openee = window.open("${openee_url}");
+ `);
+ add_completion_callback(() => send(openee_token, "close()"));
+
+ // Because of CSP:sandbox, the popup is not considered same-origin with
+ // its openee. Check the openee/opener relationship is now closed.
+ send(openee_token, `
+ if (opener)
+ send("${driver_token}", "Error: have opener");
+ else
+ send("${driver_token}", "Success: no opener");
+ `);
+ assert_equals(await receive(driver_token), "Success: no opener");
+
+ // Technically, the opener's "openee" WindowProxy should appear as closed at
+ // this time. The popup loaded a new document, and at least two fetch requests
+ // were made. This is more than enough. However, in theory, there is nothing
+ // to guarantee we can observe "openee.close". Wait a bit to ensure this will
+ // never flake.
+ await new Promise(r => test.step_timeout(r, 1000));
+
+ send(opener_token, `
+ if (openee.closed)
+ send("${driver_token}", "Success: openee closed");
+ else
+ send("${driver_token}", "Error: can still access openee");
+ `);
+ assert_equals(await receive(driver_token), "Success: openee closed");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-history-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-history-popup.https.html
new file mode 100644
index 0000000000..a061be992c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-history-popup.https.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy: a navigating popup that then goes back in history</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/common.js"></script>
+<script>
+const title = `Popup navigating to other origin with COOP: same-origin and back in history`;
+const channel = title.replace(/ /g,"-");
+const opener = false;
+const openerDOMAccess = false;
+const navigateURL = `${CROSS_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=&navHistory=-1`;
+
+async_test(t => {
+ url_test(t, `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=&coep=&navigate=${encodeURIComponent(navigateURL)}&avoidBackAndForth=1&channel=${channel}`, channel, opener, openerDOMAccess);
+}, title);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html
new file mode 100644
index 0000000000..ef610a488f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy: a navigated popup</title>
+<!-- In particular this is different from coep-navigate-popup.https.html as this document initiates
+ the navigation (and uses same-origin-allow-popups and no COEP as without that it cannot be
+ observed). COOP should work identically, but implementations might have used the wrong
+ authority. -->
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+<script>
+async_test(t => {
+ const noCOOP = "/common/blank.html";
+ const popupName = token();
+ const popup = window.open(noCOOP, popupName);
+ const channel = new BroadcastChannel(token());
+ // Close the popup once the test is complete.
+ // The browsing context is closed after the navigation hence use the broadcast channel
+ // to trigger the closure.
+ t.add_cleanup(() => {
+ channel.postMessage("close");
+ });
+ popup.onload = t.step_func(() => {
+ assert_equals(popup.name, popupName);
+ assert_equals(new URL(popup.document.URL).pathname, noCOOP);
+ channel.onmessage = t.step_func_done(event => {
+ const payload = event.data;
+ // The name should be empty, but we're checking the length rather than a
+ // string comparison to "" to keep the random token out of error messages.
+ assert_equals(payload.name.length, 0);
+ assert_false(payload.opener);
+ assert_true(popup.closed);
+ });
+ const coop = `resources/coop-coep.py?coop=same-origin&coep=&channel=${channel.name}`;
+ popup.location = coop;
+ });
+}, "Open a popup to a document without COOP, then navigate it to a document with");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-navigated-popup.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html
new file mode 100644
index 0000000000..a6c63654a9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<title>
+ Cross-Origin-Opener-Policy: opener is lost because the opener navigates.
+</title>
+<!--
+ COOP tests usually assume that the opener is lost because it navigated to a
+ page that triggered a browsing context group swap. It can also happen when
+ the opener navigates instead. This test verifies the behavior.
+-->
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script>
+
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const coop_same_origin_header =
+ '|header(Cross-Origin-Opener-Policy,same-origin)';
+const coop_unsafe_none_header =
+ '|header(Cross-Origin-Opener-Policy,unsafe-none)';
+
+function getExecutorPath(uuid, origin, coop_header) {
+ return origin.origin + executor_path + coop_header + `&uuid=${uuid}`;
+}
+
+// Note: Because we can not navigate the main page to verify the behavior,
+// we instead create another layer of popup, and navigate the intermediate
+// one. We can verify the opener behavior from this page, and the openee
+// behavior from the second popup.
+promise_test(async t => {
+ // Set up dispatcher communications.
+ const first_popup_token = token();
+ const post_navigate_first_popup_token = token();
+ const second_popup_token = token();
+ const reply_token = token();
+
+ const first_popup_url = getExecutorPath(
+ first_popup_token,
+ SAME_ORIGIN,
+ coop_same_origin_header);
+
+ const post_navigate_first_popup_url = getExecutorPath(
+ post_navigate_first_popup_token,
+ SAME_ORIGIN,
+ coop_unsafe_none_header);
+
+ const second_popup_url = getExecutorPath(
+ second_popup_token,
+ SAME_ORIGIN,
+ coop_same_origin_header);
+
+ // We open the first popup and then ping it, it will respond after loading.
+ const first_popup = window.open(first_popup_url);
+ send(first_popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), "Popup loaded");
+
+ // We open the second popup and the ping it, it will respond after loading.
+ send(first_popup_token,
+ `opener.second_popup_url = window.open('${second_popup_url}');`);
+ send(second_popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), "Popup loaded");
+
+ // Both popups are now loaded. We navigate the middle one to a page that
+ // does not have COOP, this should trigger a browsing context group swap.
+ send(first_popup_token, `location.href = '${post_navigate_first_popup_url}'`);
+ send(post_navigate_first_popup_token,
+ `send('${reply_token}', 'Popup navigated');`);
+ assert_equals(await receive(reply_token), "Popup navigated");
+
+ // Give some time for things to settle across processes etc. before
+ // proceeding with verifications.
+ await new Promise((resolve, reject) => { t.step_timeout(resolve, 1500); });
+
+ // The reference held by the main page to the first popup should be closed.
+ assert_equals(first_popup.closed, true);
+
+ // The second popup, opened by the first one should have its opener unset.
+ send(second_popup_token, `send('${reply_token}', opener);`);
+ assert_equals(await receive(reply_token), "");
+
+}, "Verify that having the opener navigate instead of the openee also triggers COOP swaps.");
+ </script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-popup-opener-navigates.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write.html
new file mode 100644
index 0000000000..82dd4541b6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script>
+
+/*
+ Regression test for: https://crbug.com/1216244
+ From a window using Cross-Origin-Opener-Policy:same-origin-allow-popup, open
+ a new blank window and navigate it cross-origin using document.write and a
+ meta refresh. The openee/opener relationship must hold.
+*/
+
+const executor_path = '/common/dispatcher/executor.html?pipe=';
+const coep_soap =
+ "|header(Cross-Origin-Opener-Policy,same-origin-allow-popups)";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+promise_test(async t => {
+ // This window:
+ const this_window_token = token();
+
+ // The opener, using COEP:same-origin-allow-popups:
+ const opener_token = token();
+ const opener_url = same_origin + executor_path + coep_soap +
+ `&uuid=${opener_token}`;
+ const opener = window.open(opener_url);
+
+ // Open a blank window, then use document.write and a meta refresh to navigate
+ // cross-origin.
+ const openee_token = token();
+ const openee_url = cross_origin + executor_path + `&uuid=${openee_token}`;
+ send(opener_token, `
+ openee = window.open();
+ openee.document.write(\`
+ <meta http-equiv="refresh" content="0; url=${openee_url}">
+ \`);
+ openee.document.close();
+ `);
+
+ // Check the openee is loaded without access to the opener.
+ send(openee_token, `
+ send("${this_window_token}", opener == null)
+ `);
+ assert_equals(await receive(this_window_token), "true", "opener == null");
+
+ // To get the state of the openee reflected into the opener's process, waiting
+ // for the openee' document to load and the various fetch() with the
+ // dispatcher should be largely enough. However these aren't causal guarantee.
+ // So wait a bit to be sure:
+ await new Promise(r => t.step_timeout(r, 1000));
+
+ // Check the opener see the openee as 'closed' after the navigation.
+ send(opener_token, `
+ send("${this_window_token}", openee.closed)
+ `);
+ assert_equals(await receive(this_window_token), "true", "openee.closed");
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-cuts-opener.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-cuts-opener.https.html
new file mode 100644
index 0000000000..47e6d0d6fe
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-cuts-opener.https.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<title>
+ Sandboxed Cross-Origin-Opener-Policy popup should cut the opener if necessary
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<body>
+<script>
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const coop_same_origin_header =
+ '|header(Cross-Origin-Opener-Policy,same-origin)';
+const coop_unsafe_none_header =
+ '|header(Cross-Origin-Opener-Policy,unsafe-none)';
+
+function getExecutorPath(uuid, origin, coop_header) {
+ return origin.origin + executor_path + coop_header + `&uuid=${uuid}`;
+}
+
+[
+ "allow-popups allow-scripts allow-same-origin",
+ "allow-popups allow-scripts",
+].forEach(sandboxValue => {
+ async_test(t => {
+ // Set up dispatcher communications.
+ const iframe_token = token();
+ const popup_token = token();
+ const main_frame_token_for_popup = token();
+ const main_frame_token_for_iframe = token();
+
+ // Create a sandboxed iframe.
+ const iframe = document.createElement("iframe");
+ iframe.sandbox = sandboxValue;
+ iframe.src = getExecutorPath(iframe_token, SAME_ORIGIN,
+ coop_unsafe_none_header);
+ document.body.append(iframe);
+ t.add_cleanup(() => iframe.remove());
+
+ // Open a COOP popup from the sandboxed iframe.
+ const popup_url = getExecutorPath(popup_token,
+ SAME_ORIGIN,
+ coop_same_origin_header);
+ send(iframe_token, `window.popup = window.open('${popup_url}')`);
+
+ // This should fail. We ping the popup, if we get an answer it loaded.
+ send(popup_token, `
+ send('${main_frame_token_for_popup}', 'Popup loaded');
+ `);
+ receive(main_frame_token_for_popup)
+ .then(t.unreached_func("A COOP popup was created from a sandboxed frame"));
+
+ // We delay probing the popup.closed property to give it time to settle.
+ t.step_timeout(() => {
+ send(iframe_token,
+ `send('${main_frame_token_for_iframe}', window.popup.closed);`);
+ }, 1500);
+ receive(main_frame_token_for_iframe)
+ .then(t.step_func_done(data => assert_equals(data, "true")));
+
+ }, `<iframe sandbox="${sandboxValue}"> ${document.title}`);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-redirects-cuts-opener.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-redirects-cuts-opener.https.html
new file mode 100644
index 0000000000..01f60b425d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox-redirects-cuts-opener.https.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<title>
+ Sandboxed Cross-Origin-Opener-Policy popup should cut the opener if necessary
+ including during redirects.
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<body>
+<script>
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const coop_same_origin_header =
+ '|header(Cross-Origin-Opener-Policy,same-origin)';
+const coop_unsafe_none_header =
+ '|header(Cross-Origin-Opener-Policy,unsafe-none)';
+
+function getExecutorPath(uuid, origin) {
+ return origin.origin + executor_path + `&uuid=${uuid}`;
+}
+
+[
+ "allow-popups allow-scripts allow-same-origin",
+ "allow-popups allow-scripts",
+].forEach(sandboxValue => {
+ async_test(t => {
+ // Set up dispatcher communications.
+ const iframe_token = token();
+ const popup_token = token();
+ const main_frame_token_for_popup = token();
+ const main_frame_token_for_iframe = token();
+
+ // Create a sandboxed iframe.
+ const iframe = document.createElement("iframe");
+ iframe.sandbox = sandboxValue;
+ iframe.src = getExecutorPath(iframe_token, SAME_ORIGIN);
+ document.body.append(iframe);
+ t.add_cleanup(() => iframe.remove());
+
+ // Open a COOP popup from the sandboxed iframe.
+ // Instead of navigating directly we go through a redirect.
+ const popup_url = getExecutorPath(popup_token, SAME_ORIGIN);
+ const redirect_url = SAME_ORIGIN.origin + "/common/redirect.py?pipe=" +
+ coop_same_origin_header + "&location=" +
+ encodeURIComponent(popup_url);
+ send(iframe_token, `window.popup = window.open('${redirect_url}')`);
+
+ // This should fail. We ping the popup, if we get an answer it loaded.
+ send(popup_token, `
+ send('${main_frame_token_for_popup}', 'Popup loaded');
+ `);
+ receive(main_frame_token_for_popup)
+ .then(t.unreached_func("A COOP popup was created from a sandboxed frame"));
+
+ // We delay probing the popup.closed property to give it time to settle.
+ t.step_timeout(() => {
+ send(iframe_token,
+ `send('${main_frame_token_for_iframe}', window.popup.closed);`);
+ }, 1500);
+ receive(main_frame_token_for_iframe)
+ .then(t.step_func_done(data => assert_equals(data, "true")));
+
+ }, `<iframe sandbox="${sandboxValue}"> ${document.title}`);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html
new file mode 100644
index 0000000000..6f250c1b09
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>Sandboxed Cross-Origin-Opener-Policy popup should result in a network error</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+<div id=log>
+<script>
+[
+ "allow-popups allow-scripts allow-same-origin",
+ "allow-popups allow-scripts",
+].forEach(sandboxValue => {
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ const channel = new BroadcastChannel(token());
+ channel.onmessage = t.unreached_func("A COOP popup was created from a sandboxed frame");
+ t.add_cleanup(() => frame.remove());
+ frame.sandbox = sandboxValue;
+ frame.srcdoc = `<script>
+ const popup = window.open("resources/coop-coep.py?coop=same-origin&coep=&channel=${channel.name}");
+ <\/script>`;
+ document.body.append(frame);
+ addEventListener('load', t.step_func(() => {
+ // This uses a timeout to give some time for incorrect implementations to broadcast. A
+ // theoretical testdriver.js API for browsing contexts could be used to speed this up.
+ t.step_timeout(() => {
+ t.done()
+ }, 1500);
+ }));
+ }, `<iframe sandbox="${sandboxValue}"> ${document.title}`);
+});
+
+// Verify that the popup does not have sandboxing flags set
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const channel = new BroadcastChannel(token());
+ channel.onmessage = t.step_func_done();
+ t.add_cleanup(() => frame.remove());
+ frame.sandbox = "allow-popups allow-scripts allow-popups-to-escape-sandbox";
+ frame.srcdoc = `<script>
+window.open("resources/coop-coep.py?coop=same-origin&coep=&channel=${channel.name}");
+<\/script>`;
+ document.body.append(frame);
+}, `<iframe sandbox="allow-popups allow-scripts allow-popups-to-escape-sandbox"> ${document.title}`);
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ const channel = new BroadcastChannel(token());
+ frame.sandbox = "allow-scripts allow-same-origin";
+ frame.name = `iframe-${channel.name}`;
+ frame.src = `resources/coop-coep.py?coop=same-origin&coep=&channel=${channel.name}`;
+ channel.onmessage = t.step_func( event => {
+ const payload = event.data;
+ assert_equals(payload.name, frame.name, "name");
+ t.done();
+ });
+ t.add_cleanup(() => frame.remove());
+ document.body.append(frame);
+}, `Iframe with sandbox and COOP must load.`);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coop-sandbox.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-failures.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-failures.https.html
new file mode 100644
index 0000000000..55b126ff37
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-failures.https.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-4">
+<meta name="variant" content="?5-8">
+<meta name="variant" content="?9-12">
+<meta name="variant" content="?12-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/subset-tests.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ // None of the following should be recognized as "same-origin" (hence the
+ // preserved opener).
+ {
+ "title": "coop with semi-column",
+ "coop": "same-origin;",
+ },
+ {
+ "title": "coop with vertical tab",
+ "coop": "\u000bsame-origin\u000b",
+ },
+ {
+ "title": "coop with form feed",
+ "coop": "\u000csame-origin\u000c",
+ },
+ {
+ "title": "coop with carriage return",
+ "coop": "\u000dsame-origin\u000d",
+ },
+ {
+ "title": "coop with capital letter",
+ "coop": "Same-origin",
+ },
+ {
+ "title": "coop with bad structured header 1",
+ "coop": "same-origin;\tfoo=bar",
+ },
+ {
+ "title": "coop with bad structured header 2",
+ "coop": "same-origin ;foo=bar",
+ },
+ {
+ "title": "coop with bad structured header 3",
+ "coop": "same-origin; foo=bar;",
+ },
+ {
+ "title": "coop as a structured header 'string' item",
+ "coop": "\"same-origin\"",
+ },
+ {
+ "title": "coop as a structured header 'byte sequence' item",
+ "coop": ":c2FtZS1vcmlnaW4=:",
+ },
+ {
+ "title": "coop as a structured header 'boolean' item",
+ "coop": "?1",
+ },
+ {
+ "title": "coop as a structured header 'integer or decimal' item",
+ "coop": "1",
+ },
+ {
+ "title": "coop as an unrecognized structured header type",
+ "coop": "$same-origin",
+ },
+ {
+ "title": "coop with duplicate value",
+ "coop": "same-origin same-origin",
+ },
+ {
+ // Note: comma must be escaped here to not mess with the WPT pipe function.
+ "title": "coop with duplicate value, separated by a comma",
+ "coop": "same-origin\\,same-origin",
+ },
+ {
+ "title": "coop with preceding asterisk character",
+ "coop": "*same-origin ",
+ }
+].forEach(variant => {
+ subsetTest(popup_test, `Parsing ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, "preserved");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html
new file mode 100644
index 0000000000..b5f20f88f4
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+ popup_test(
+ `Simple document with non-ascii COOP header opening a same-origin popup`,
+ SAME_ORIGIN,
+ "unsafe-none",
+ "preserved");
+</script>
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html.headers
new file mode 100644
index 0000000000..54e44a7113
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-non-ascii.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same%FForigin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html
new file mode 100644
index 0000000000..a1430cbf57
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+ popup_test(
+ `Simple document with duplicated COOP header opening a same-origin popup`,
+ SAME_ORIGIN,
+ "unsafe-none",
+ "preserved");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html.headers
new file mode 100644
index 0000000000..85c58be8a1
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-repeated.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-successes.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-successes.https.html
new file mode 100644
index 0000000000..8e055669ad
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/header-parsing-successes.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ // All of the following should be recognized as "same-origin" (hence the
+ // severed opener link).
+ {
+ "title": "coop with leading space",
+ "coop": " same-origin",
+ },
+ {
+ "title": "coop with trailing space",
+ "coop": "same-origin ",
+ },
+ {
+ "title": "coop with leading tab",
+ "coop": "\tsame-origin",
+ },
+ {
+ "title": "coop with trailing tab",
+ "coop": "same-origin\t",
+ },
+ {
+ "title": "coop with duplicate value, separated by semi-column",
+ "coop": "same-origin;same-origin",
+ },
+ {
+ "title": "coop with valid structured header",
+ "coop": "same-origin; foo=bar",
+ }
+].forEach(variant => {
+ popup_test(`Parsing ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, "severed");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
new file mode 100644
index 0000000000..8368dc4c81
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Historical: Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy: a navigating popup</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="../resources/common.js"></script>
+<script src="/common/subset-tests.js"></script>
+<script>
+[
+ {
+ "title": "coop unsafe-inherit/coep",
+ "coop": "unsafe-inherit", // historical value, equivalent to "unsafe-none"
+ "coep": "require-corp",
+ "opener": false
+ },
+ {
+ "title": "coop unsafe-inherit/no coep",
+ "coop": "unsafe-inherit", // historical value, equivalent to "unsafe-none"
+ "coep": "",
+ "opener": false
+ }
+].forEach((variant) => {
+ ["same-origin", "same-site"].forEach((site) => {
+ const title = `Popup navigating to ${site} with ${variant.title}`;
+ const channel = title.replace(/ /g,"-");
+ const navigateHost = site === "same-origin" ? SAME_ORIGIN : SAME_SITE;
+ const navigateURL = `${navigateHost.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${variant.coop}&coep=${variant.coep}&channel=${channel}`;
+ const opener = site === "same-origin" ? variant.opener : false;
+
+ async_test(t => {
+ // For each test we open a COOP: same-origin/COEP: require-corp document in a popup and then
+ // navigate that to either a document with same origin (site=="same-origin") or
+ // not-same-origin (site=="same-site") whose COOP and COEP are set as per the top-most array.
+ // We then verify that this document has the correct opener for its specific setup.
+ url_test(t, `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=same-origin&coep=require-corp&navigate=${encodeURIComponent(navigateURL)}`, channel, opener);
+ }, title);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html
new file mode 100644
index 0000000000..21e0875f41
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..a19f4400ce
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-cross-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin unsafe-allow-outgoing
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html
new file mode 100644
index 0000000000..89b0b4934e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..a19f4400ce
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin unsafe-allow-outgoing
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html
new file mode 100644
index 0000000000..fb3330365f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html.headers
new file mode 100644
index 0000000000..a19f4400ce
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-origin-unsafe-allow-outgoing-with-same-site.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin unsafe-allow-outgoing
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html
new file mode 100644
index 0000000000..21e0875f41
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..ab7b289481
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-cross-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-site unsafe-allow-outgoing
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html
new file mode 100644
index 0000000000..89b0b4934e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..ab7b289481
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-site unsafe-allow-outgoing
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html
new file mode 100644
index 0000000000..fb3330365f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html.headers
new file mode 100644
index 0000000000..ab7b289481
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-unsafe-allow-outgoing-with-same-site.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-site unsafe-allow-outgoing
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html
new file mode 100644
index 0000000000..21e0875f41
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..34bd099a30
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-cross-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-site
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html
new file mode 100644
index 0000000000..89b0b4934e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..34bd099a30
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-site
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html
new file mode 100644
index 0000000000..fb3330365f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/common.js"></script>
+<script src="../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html.headers
new file mode 100644
index 0000000000..34bd099a30
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/historical/popup-same-site-with-same-site.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-site
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html
new file mode 100644
index 0000000000..099424790a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="resources/common.js"></script>
+<script src="resources/iframe-test.js"></script>
+
+<body>
+<script>
+// This document has COOP: same-origin-allow-popups. The popup has COOP:
+// same-origin-allow-popups. Therefore there should only be an opener and name
+// if the frameOrigin and popupOrigin are same-origin with this document.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: same-origin-allow-popups to popup COOP: same-origin-allow-popups via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'same-origin-allow-popups' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin-allow-popups.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html
new file mode 100644
index 0000000000..34699c8d08
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="resources/common.js"></script>
+<script src="resources/iframe-test.js"></script>
+
+<body>
+<script>
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: same-origin-allow-popups to popup COOP: same-origin via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'same-origin' },
+ variant.opener);
+});
+</script>
+</body>
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html
new file mode 100644
index 0000000000..29fb5cfa2d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="resources/common.js"></script>
+<script src="resources/iframe-test.js"></script>
+
+<body>
+<script>
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "preserved"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "preserved"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "preserved"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: same-origin-allow-popups to popup COOP: unsafe-none via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'unsafe-none' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html
new file mode 100644
index 0000000000..7d31256584
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="resources/common.js"></script>
+<script src="resources/iframe-test.js"></script>
+
+<body>
+<script>
+
+// This document has COOP "same-origin". The popup has COOP "same-origin". Therefore there should
+// only be an opener and name if the frameOrigin and popupOrigin are same-origin with this document.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "noopener"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "noopener"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "noopener"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "noopener"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "noopener"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "noopener"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: same-origin to popup COOP: same-origin via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'same-origin' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html
new file mode 100644
index 0000000000..847f75665b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="resources/common.js"></script>
+<script src="resources/iframe-test.js"></script>
+
+<body>
+<script>
+// This document has COOP: same-origin. The popup has no COOP. Therefore there
+// should be no opener or name.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "noopener"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "noopener"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "noopener"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "noopener"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "noopener"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "noopener"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: same-origin to popup COOP: unsafe-none via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'unsafe-none' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html
new file mode 100644
index 0000000000..8158b9f4d5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="resources/common.js"></script>
+<script src="resources/iframe-test.js"></script>
+
+<body>
+<script>
+// This document has COOP: unsafe-none. The popup has COOP: same-origin.
+// Therefore there should be no opener or name.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: unsafe-none to popup COOP: same-origin via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'same-origin' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html.headers
new file mode 100644
index 0000000000..073ce7adfb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: unsafe-none
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html
new file mode 100644
index 0000000000..74ceeb290d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="resources/common.js"></script>
+<script src="resources/iframe-test.js"></script>
+
+<body>
+<script>
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "preserved"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "preserved"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "preserved"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: unsafe-none to popup COOP: unsafe-none via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'unsafe-none' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html.headers
new file mode 100644
index 0000000000..073ce7adfb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: unsafe-none
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/javascript-url.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/javascript-url.https.html
new file mode 100644
index 0000000000..8ebb3ccd4a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/javascript-url.https.html
@@ -0,0 +1,199 @@
+<!DOCTYPE html>
+<title>Cross-Origin-Opener-Policy and a "javascript:" URL popup</title>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-10">
+<meta name="variant" content="?11-12">
+<meta name="variant" content="?13-14">
+<meta name="variant" content="?15-16">
+<meta name="variant" content="?17-last">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/subset-tests.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+
+<p>According to HTML's navigate algorithm, requests to <code>javascript:</code>
+URLs should inherit the cross-origin opener policy of the active document. To
+observe this, each subtest uses the following procedure.</p>
+
+<ol>
+ <li>create popup with a given COOP (the <code>parentCOOP</code>)</li>
+ <li>navigate the popup to a <code>javascript:</code> URL (the new document is
+ expected to inherit the <code>parentCOOP</code>)</li>
+ <li>from the popup, create a second popup window with a given COOP (the
+ <code>childCOOP</code>)</li>
+</ol>
+
+<p>Both popup windows inspect state and report back to the test.</p>
+
+<pre>
+ .---- test ----.
+ | open(https:) |
+ | parentCOOP | .----- subject -------.
+ | '---------> | --------. |
+ | | | v |
+ | | | assign(javascript:) |
+ | | | (COOP under test) |
+ | | | | |
+ | | | open(https:) |
+ | | | childCOOP | .- child -.
+ | | | '--------------> | |
+ | | '---------------------' '---------'
+ | | | |
+ | validate | <--status---+---------------------'
+ '--------------'
+</pre>
+
+<script>
+'use strict';
+
+function getExecutorPath(uuid, origin, coopHeader) {
+ const executorPath = '/common/dispatcher/executor.html?';
+ const coopHeaderPipe =
+ `|header(Cross-Origin-Opener-Policy,${encodeURIComponent(coopHeader)})`;
+ return origin + executorPath + `uuid=${uuid}` + '&pipe=' + coopHeaderPipe;
+}
+
+function assert_isolated(results) {
+ assert_equals(results.childName, '', 'child name');
+ assert_false(results.childOpener, 'child opener');
+ // The test subject's reference to the "child" window must report "closed"
+ // when COOP enforces isolation because the document initially created during
+ // the window open steps must be discarded when a new document object is
+ // created at the end of the navigation.
+ assert_true(results.childClosed, 'child closed');
+}
+
+function assert_not_isolated(results, expectedName) {
+ assert_equals(results.childName, expectedName, 'child name');
+ assert_true(results.childOpener, 'child opener');
+ assert_false(results.childClosed, 'child closed');
+}
+
+async function javascript_url_test(parentCOOP, childCOOP, origin, resultsVerification) {
+ promise_test(async t => {
+ const parentToken = token();
+ const childToken = token();
+ const responseToken = token();
+
+ const parentURL = getExecutorPath(
+ parentToken,
+ SAME_ORIGIN.origin,
+ parentCOOP);
+ const childURL = getExecutorPath(
+ childToken,
+ origin.origin,
+ childCOOP);
+
+ // Open a first popup, referred to as the parent popup, and wait for it to
+ // load.
+ window.open(parentURL);
+ send(parentToken, `send('${responseToken}', 'Done loading');`);
+ assert_equals(await receive(responseToken), 'Done loading');
+
+ // Make sure the parent popup is removed once the test has run, keeping a
+ // clean state.
+ add_completion_callback(() => {
+ send(parentToken, 'close');
+ });
+
+ // Navigate the popup to the javascript URL. It should inherit the current
+ // document's COOP. Because we're navigating to a page that is not an
+ // executor, we lose access to easy messaging, making things a bit more
+ // complicated. We use a predetermined scenario of communication that
+ // enables us to retrieve whether the child popup appears closed from the
+ // parent popup.
+ //
+ // Notes:
+ // - Splitting the script tag prevents HTML parsing to kick in.
+ // - The innermost double quotes need a triple backslash, because it goes
+ // through two rounds of consuming escape characters (\\\" -> \" -> ").
+ // - The javascript URL does not accept \n characters so we need to use
+ // a new template literal for each line.
+ send(parentToken,
+ `location.assign("javascript:'` +
+ // Include dispatcher.js to have access to send() and receive().
+ `<script src=\\\"/common/dispatcher/dispatcher.js\\\"></scr` + `ipt>` +
+ `<script> (async () => {` +
+
+ // Open the child popup and keep a handle to it.
+ `const w = open(\\\"${childURL}\\\", \\\"${childToken}\\\");` +
+
+ // We wait for the main frame to query the w.closed property.
+ `await receive(\\\"${parentToken}\\\");` +
+ `send(\\\"${responseToken}\\\", w.closed);` +
+
+ // Finally we wait for the cleanup indicating that this popup can be
+ // closed.
+ `await receive(\\\"${parentToken}\\\");` +
+ `close();` +
+ `})()</scr` + `ipt>'");`
+ );
+
+ // Make sure the javascript navigation ran, and the child popup was created.
+ send(childToken, `send('${responseToken}', 'Done loading');`);
+ assert_equals(await receive(responseToken), 'Done loading');
+
+ // Make sure the child popup is removed once the test has run, keeping a
+ // clean state.
+ add_completion_callback(() => {
+ send(childToken, `close()`);
+ });
+
+ // Give some time for things to settle across processes etc. before
+ // proceeding with verifications.
+ await new Promise(resolve => { t.step_timeout(resolve, 500); });
+
+ // Gather information about the child popup and verify that they match what
+ // we expect.
+ const results = {};
+ send(parentToken, 'query');
+ results.childClosed = await receive(responseToken) === 'true';
+
+ send(childToken, `send('${responseToken}', opener != null);`);
+ results.childOpener = await receive(responseToken) === 'true';
+
+ send(childToken, `send('${responseToken}', name);`);
+ results.childName = await receive(responseToken);
+
+ resultsVerification(results, childToken);
+ }, `navigation: ${origin.name}; ` + `parentCOOP: ${parentCOOP}; ` +
+ `childCOOP: ${childCOOP}`);
+}
+
+const tests = [
+ ['unsafe-none', 'unsafe-none', SAME_ORIGIN, assert_not_isolated],
+ ['unsafe-none', 'unsafe-none', SAME_SITE, assert_not_isolated],
+ ['unsafe-none', 'same-origin-allow-popups', SAME_ORIGIN, assert_isolated],
+ ['unsafe-none', 'same-origin-allow-popups', SAME_SITE, assert_isolated],
+ ['unsafe-none', 'same-origin', SAME_ORIGIN, assert_isolated],
+ ['unsafe-none', 'same-origin', SAME_SITE, assert_isolated],
+ ['same-origin-allow-popups', 'unsafe-none', SAME_ORIGIN, assert_not_isolated],
+ ['same-origin-allow-popups', 'unsafe-none', SAME_SITE, assert_not_isolated],
+ ['same-origin-allow-popups', 'same-origin-allow-popups', SAME_ORIGIN, assert_not_isolated],
+ ['same-origin-allow-popups', 'same-origin-allow-popups', SAME_SITE, assert_isolated],
+ ['same-origin-allow-popups', 'same-origin', SAME_ORIGIN, assert_isolated],
+ ['same-origin-allow-popups', 'same-origin', SAME_SITE, assert_isolated],
+ ['same-origin', 'unsafe-none', SAME_ORIGIN, assert_isolated],
+ ['same-origin', 'unsafe-none', SAME_SITE, assert_isolated],
+ ['same-origin', 'same-origin-allow-popups', SAME_ORIGIN, assert_isolated],
+ ['same-origin', 'same-origin-allow-popups', SAME_SITE, assert_isolated],
+ ['same-origin', 'same-origin', SAME_ORIGIN, assert_not_isolated],
+ ['same-origin', 'same-origin', SAME_SITE, assert_isolated],
+].forEach(([parentCOOP, childCOOP, origin, expectation]) => {
+ subsetTest(
+ javascript_url_test,
+ parentCOOP,
+ childCOOP,
+ origin,
+ expectation);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/navigate-to-aboutblank.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/navigate-to-aboutblank.https.html
new file mode 100644
index 0000000000..b2145cfa56
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/navigate-to-aboutblank.https.html
@@ -0,0 +1,193 @@
+<title>
+ This tests the inheritance of COOP for navigations to about:blank.
+</title>
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+
+<p>Non-initial empty documents (about:blank) should inherit their
+ cross-origin-opener-policy from the navigation's initiator top level document,
+ if the initiator and its top level document are same-origin, or default
+ (unsafe-none) otherwise.
+</p>
+
+<ol>
+ <li>Create the opener popup with a given COOP <code>openerCOOP</code>.</li>
+ <li>Add iframe to the opener popup that is either same-origin or
+ cross-origin.
+ </li>
+ <li>Opener's iframe opens a new window, to a network document with <code>openeeCOOP</code>.</li>
+ <li>Opener's iframe navigates the openee popup to about:blank.</li>
+</ol>
+
+<script>
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const coop_same_origin_header =
+ '|header(Cross-Origin-Opener-Policy,same-origin)';
+const coep_require_corp_header =
+ '|header(Cross-Origin-Embedder-Policy,require-corp)';
+const coop_same_origin_plus_coep_header =
+ coop_same_origin_header + coep_require_corp_header;
+const coop_same_origin_allow_popups_header =
+ '|header(Cross-Origin-Opener-Policy,same-origin-allow-popups)';
+const coop_unsafe_none_header =
+ '|header(Cross-Origin-Opener-Policy,unsafe-none)';
+
+function navigateToAboutBlankTest(
+ opener_COOP_header,
+ iframe_origin,
+ openee_COOP_header,
+ openee_origin,
+ iframe_header,
+ expect_openee_closed
+){
+ return promise_test(async t => {
+ const this_window_token = token();
+ const opener_token = token();
+ const openee_token = token();
+ const iframe_token = token();
+
+ const opener_url = same_origin + executor_path + opener_COOP_header +
+ `&uuid=${opener_token}`;
+ const openee_url = openee_origin + executor_path + openee_COOP_header +
+ `&uuid=${openee_token}`;
+ const iframe_url = iframe_origin + executor_path + iframe_header + `&uuid=${iframe_token}`;
+
+ t.add_cleanup(() => {
+ send(openee_token, "window.close()");
+ send(opener_token, "window.close()");
+ });
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url, opener_token);
+
+ // 2. Create the iframe.
+ send(opener_token, `
+ iframe = document.createElement('iframe');
+ iframe.src = "${iframe_url}";
+ document.body.appendChild(iframe);
+ `);
+
+ // 3. The iframe opens its openee window.
+ send(iframe_token, `
+ window.openee = window.open(
+ '${openee_url.replace(/,/g, '\\,')}',
+ "${openee_token}"
+ );
+ `);
+
+ // 4. Ensure the popup is fully loaded.
+ send(openee_token, `send("${this_window_token}", "Ack");`);
+ assert_equals(await receive(this_window_token), "Ack");
+
+ // 5. The iframe navigates openee to about:blank.
+ send(iframe_token, `
+ window.openee_blank = window.open('about:blank', "${openee_token}");
+ (async function() {
+ const timeout = 2000;
+ const retry_delay = 100;
+ for(let i = 0; i * retry_delay < timeout; ++i) {
+ // A try-catch block is used, because of same-origin policy,
+ // preventing access to the document before committing about:blank.
+ try {
+ if (window.openee_blank.closed ||
+ window.openee_blank.document.location.href == "about:blank") {
+ send("${this_window_token}", "about:blank loaded");
+ return;
+ }
+ } catch(e) {}
+ await new Promise(resolve => setTimeout(resolve, retry_delay));
+ }
+ send("${this_window_token}", "about:blank not loaded");
+ })()
+ `);
+ assert_equals(await receive(this_window_token), "about:blank loaded");
+
+ // 6. Retrieve and check the results.
+ send(iframe_token, `
+ send("${this_window_token}", window.openee.closed);
+ `);
+ assert_equals(await receive(this_window_token), `${expect_openee_closed}`);
+ }, `Navigate to about:blank from iframe with opener.top \
+COOP: ${opener_COOP_header}, iframe origin: ${iframe_origin}, \
+openee COOP: ${openee_COOP_header}, openee origin: ${openee_origin}.`);
+};
+
+// iframe same-origin with its top-level embedder:
+// initial empty document and about:blank navigations initiated from the
+// same-origin iframe will inherit the COOP from the iframe's top-level embedder.
+
+// Since all navigations of openee are within same-origin pages with the
+// same COOP value, there are no browsing context group switches.
+navigateToAboutBlankTest(
+ coop_same_origin_header,
+ same_origin,
+ coop_same_origin_header,
+ same_origin,
+ "",
+ false
+);
+
+// Since all navigations of openee are within same-origin pages with the
+// same COOP value, there are no browsing context group switches.
+// Test with both COOP and COEP.
+navigateToAboutBlankTest(
+ coop_same_origin_plus_coep_header,
+ same_origin,
+ coop_same_origin_plus_coep_header,
+ same_origin,
+ coep_require_corp_header,
+ false
+);
+
+// Since all navigations of openee are within same-origin pages with the
+// same COOP value, there are no browsing context group switches.
+navigateToAboutBlankTest(
+ coop_same_origin_allow_popups_header,
+ same_origin,
+ coop_same_origin_allow_popups_header,
+ same_origin,
+ "",
+ false
+);
+
+// The first openee navigation, from initial empty document to
+// cross-origin will not switch the browsing context group, thanks to the
+// same-origin-allow-popups behavior.
+// The second openee navigation, to about:blank, will inherit from the
+// iniatiator's, the iframe, top. Navigating from a COOP: unsafe-none page to
+// a COOP: same-origin-allow-popups page causes a browsing context group
+// switch.
+navigateToAboutBlankTest(
+ coop_same_origin_allow_popups_header,
+ same_origin,
+ coop_unsafe_none_header,
+ cross_origin,
+ "",
+ true
+);
+
+// iframe cross-origin with its top-level embedder:
+// initial empty document and about:blank navigations initiated from the
+// cross-origin iframe will default COOP to unsafe-none.
+
+// The navigation from the initial empty document and the cross_origin url
+// does not cause a browsing context group switch
+// (both have COOP: unsafe-none).
+// The navigation from the cross-origin url to about:blank does not cause a
+// browsing context group swich, about:blank defaulted its COOP value to
+// unsafe-none.
+navigateToAboutBlankTest(
+ coop_same_origin_allow_popups_header,
+ cross_origin,
+ coop_unsafe_none_header,
+ cross_origin,
+ "",
+ false
+);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/navigate-top-to-aboutblank.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/navigate-top-to-aboutblank.https.html
new file mode 100644
index 0000000000..fe605baf45
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/navigate-top-to-aboutblank.https.html
@@ -0,0 +1,171 @@
+<title>
+ This tests the inheritance of COOP for navigations of the top document to about:blank.
+</title>
+<meta name=timeout content=long>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+
+
+<p>Non-initial empty documents (about:blank) should inherit their
+ cross-origin-opener-policy from the navigation's initiator top level document,
+ if the initiator and its top level document are same-origin, or default (to
+ unsafe-none) otherwise.
+</p>
+
+<ol>
+ <li>Create the opener popup with a given COOP <code>openerCOOP</code>.</li>
+ <li>Add iframe to the opener popup that is either same-origin or
+ cross-origin.
+ </li>
+ <li>Opener opens a new window, to a network document with the same origin and
+ COOP value as opener.</li>
+ <li>Opener's iframe navigates its parent frame (opener) to about:blank.</li>
+ <li>Verify the openee still has access to its opener.</li>
+</ol>
+
+<script>
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const coop_same_origin_header =
+ '|header(Cross-Origin-Opener-Policy,same-origin)';
+const coop_same_origin_allow_popups_header =
+ '|header(Cross-Origin-Opener-Policy,same-origin-allow-popups)';
+const coop_unsafe_none_header =
+ '|header(Cross-Origin-Opener-Policy,unsafe-none)';
+
+function navigateToAboutBlankTest(
+ COOP_header,
+ iframe_origin,
+ expect_opener_closed
+){
+ return promise_test(async t => {
+ const this_window_token = token();
+ const opener_token = token();
+ const openee_token = token();
+ const iframe_token = token();
+
+ const opener_url = same_origin + executor_path + COOP_header +
+ `&uuid=${opener_token}`;
+ const openee_url = same_origin + executor_path + COOP_header +
+ `&uuid=${openee_token}`;
+ const iframe_url = iframe_origin + executor_path + `&uuid=${iframe_token}`;
+
+ t.add_cleanup(() => {
+ send(opener_token, "window.close()");
+ send(openee_token, "window.close()");
+ });
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url, opener_token);
+
+ // 2. Create the iframe.
+ send(opener_token, `
+ iframe = document.createElement('iframe');
+ iframe.src = "${iframe_url}";
+ document.body.appendChild(iframe);
+ `);
+
+ // 3. The opener opens openee window.
+ send(opener_token, `
+ window.openee = window.open(
+ '${openee_url.replace(/,/g, '\\,')}'
+ );
+ `);
+
+ // 4. Ensure the popup is fully loaded.
+ send(openee_token, `send("${this_window_token}", "Ack");`);
+ assert_equals(await receive(this_window_token), "Ack");
+
+ // 5. The iframe navigates its top-level document to about:blank. It needs
+ // to receive a user action as it may be cross-origin and it navigates top
+ // to a cross-origin document.
+ // https://github.com/WICG/interventions/issues/16
+ send(iframe_token, addScriptAndTriggerOnload(
+ "/resources/testdriver.js",
+ `${addScriptAndTriggerOnload("/resources/testdriver-vendor.js",
+ `
+ test_driver.bless('navigate top to about:blank', () => {
+ open("about:blank", "_top");
+ });
+ `)}
+ `));
+
+ // 6. Ensure opener is fully loaded and then retrieve the results.
+ send(openee_token, `
+ (async function() {
+ const timeout = 2000;
+ const retry_delay = 100;
+ for(let i = 0; i * retry_delay < timeout; ++i) {
+ // A try-catch block is used, because of same-origin policy,
+ // which may prevent the access to the opener if its origin changed,
+ // after a navigation to about:blank from the cross origin iframe.
+ try {
+ if (
+ window.opener === null ||
+ window.opener.closed ||
+ window.opener.document.location.href == "about:blank") {
+ send("${this_window_token}", "about:blank loaded");
+ return;
+ }
+ } catch(e) {
+ // The exception is thrown when about:blank is loaded and is
+ // cross-origin with openee.
+ send("${this_window_token}", "about:blank loaded");
+ return;
+ }
+ await new Promise(resolve => setTimeout(resolve, retry_delay));
+ }
+ send("${this_window_token}", "about:blank NOT loaded");
+ })()
+ `);
+ assert_equals(await receive(this_window_token), "about:blank loaded");
+
+ // 7. Retrieve and check the results.
+ send(openee_token, `
+ send(
+ "${this_window_token}",
+ window.opener === null || window.opener.closed);
+ `);
+
+ assert_equals(await receive(this_window_token), `${expect_opener_closed}`);
+ }, `Navigate top to about:blank from iframe with \
+opener COOP: ${COOP_header}, iframe origin: ${iframe_origin}`);
+};
+
+// iframe same-origin with its top-level embedder:
+// initial empty document and about:blank navigations initiated from the
+// same-origin iframe will inherit COOP from the iframe's top-level embedder.
+
+// Opener's navigation to about:blank stays in the same browsing context group.
+navigateToAboutBlankTest(
+ coop_same_origin_header,
+ same_origin,
+ false
+);
+
+// iframe cross-origin with its top-level embedder:
+// initial empty document and about:blank navigations initiated from the
+// cross-origin iframe will default COOP to unsafe-none.
+
+// Opener's navigation to about:blank doesn't inherit COOP, leading to a
+// browsing context group switch.
+navigateToAboutBlankTest(
+ coop_same_origin_header,
+ cross_origin,
+ true
+);
+
+// Same origin allow popups allows the navigation of top to the cross-origin
+// about:blank (origin inherited from the iframe) page, which does not have COOP
+// (initiator is a cross origin iframe).
+navigateToAboutBlankTest(
+ coop_same_origin_allow_popups_header,
+ cross_origin,
+ false
+);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html
new file mode 100644
index 0000000000..db15d5c676
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta name=timeout content=long>
+<title>Cross-Origin-Opener-Policy requires secure contexts</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+async_test(t => {
+ const popup = window.open("resources/call-functionCalledByOpenee.html");
+ t.add_cleanup(() => {
+ popup.close();
+ });
+ window.functionCalledByOpenee = t.step_func_done(() => {
+ assert_false(popup.closed);
+ });
+ assert_equals(window, popup.opener);
+}, "Cross-Origin-Opener-Policy only works over secure contexts");
+
+test(() => {
+ assert_false(window.crossOriginIsolated);
+}, "Bonus: window.crossOriginIsolated");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/no-https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-coop-by-sw.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-coop-by-sw.https.html
new file mode 100644
index 0000000000..69c2db1ad9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-coop-by-sw.https.html
@@ -0,0 +1,136 @@
+<meta name="timeout" content="long">
+<meta name="variant" content="?1-4">
+<meta name="variant" content="?5-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/subset-tests.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+
+const executor_path = '/common/dispatcher/executor.html?pipe=';
+const executor_service_worker_path = '/common/dispatcher/executor-service-worker.js?pipe=';
+
+const coop_header = `|header(Cross-Origin-Opener-Policy,same-origin)`;
+const coep_header = `|header(Cross-Origin-Embedder-Policy,require-corp)`;
+
+const https_origin = get_host_info().HTTPS_ORIGIN;
+
+const swap_browsing_context_group = true;
+const keep_browsing_context_group = false;
+
+const opener_basic = "";
+const opener_coi = coop_header + coep_header;
+
+const sw_basic = "";
+const sw_coi = coop_header + coep_header;
+
+const openee_basic = {
+ 'content-type': 'text/html',
+};
+const openee_coi = {
+ 'content-type': 'text/html',
+ 'cross-origin-embedder-policy': 'require-corp',
+ 'cross-origin-opener-policy': 'same-origin',
+};
+
+// A document opens a popup. The popup's document is served from a synthetic
+// response by a ServiceWorker. Check how cross-origin isolation works in this
+// case.
+const popupCoopBySwTest = (
+ description,
+ // Test parameters:
+ opener_headers,
+ openee_headers,
+ sw_headers,
+ // Test expectations:
+ expected_browing_context_group
+) => {
+ subsetTest(promise_test, async test => {
+ const driver_token = token();
+
+ // 1. Create the opener.
+ const opener_token = token();
+ const opener_url = https_origin + executor_path + opener_headers +
+ `&uuid=${opener_token}`;
+ const opener_window = window.open(opener_url);
+ test.add_cleanup(() => opener_window.close());
+
+ // 2. Define the openee's URL as being served by the service worker.
+ const openee_url = https_origin + "/common/dispatcher/proxied?" + token();
+
+ // 3. Register, install and activate a ServiceWorker serving the openee_url.
+ const sw_token = token();
+ const sw_url = https_origin + executor_service_worker_path + sw_headers +
+ `&uuid=${sw_token}`;
+ const sw_scope = openee_url; // One-time scope, because of the token.
+
+ const sw_registration =
+ await service_worker_unregister_and_register(test, sw_url, sw_scope);
+ test.add_cleanup(() => sw_registration.unregister());
+ await wait_for_state(test, sw_registration.installing, 'activated');
+
+ send(sw_token, `
+ fetchHandler = event => {
+ if (!event.request.url.includes("proxied"))
+ return;
+
+ const response = new Response(\`
+ <script src="/common/dispatcher/dispatcher.js"></scr\`+\`ipt>
+ <script>
+ send("${driver_token}", opener ? "opener is set"
+ : "opener is null");
+ </scr\` + \`ipt>
+ \`, {
+ status: 200,
+ headers: ${JSON.stringify(openee_headers)},
+ });
+ event.respondWith(response);
+ }
+
+ await clients.claim();
+
+ send("${driver_token}", serviceWorker.state);
+ `)
+ assert_equals(await receive(driver_token), "activated");
+
+ // 4. The opener opens a popup. Its document is a synthetic response served
+ // from the Service Worker.
+ send(opener_token, `
+ window.open("${openee_url}");
+ `);
+
+ assert_equals(await receive(driver_token),
+ (expected_browing_context_group == swap_browsing_context_group)
+ ? "opener is null"
+ : "opener is set");
+ }, description);
+};
+
+popupCoopBySwTest("opener:basic, openee:basic, sw:basic",
+ opener_basic, openee_basic, sw_basic,
+ keep_browsing_context_group);
+popupCoopBySwTest("opener:basic, openee:basic, sw:coi",
+ opener_basic, openee_basic, sw_coi,
+ keep_browsing_context_group);
+popupCoopBySwTest("opener:basic, openee:coi, sw:basic",
+ opener_basic, openee_coi, sw_basic,
+ swap_browsing_context_group);
+popupCoopBySwTest("opener:basic, openee:coi, sw:coi",
+ opener_basic, openee_coi, sw_coi,
+ swap_browsing_context_group);
+popupCoopBySwTest("opener:coi, openee:basic, sw:basic",
+ opener_coi, openee_basic, sw_basic,
+ swap_browsing_context_group);
+popupCoopBySwTest("opener:coi, openee:basic, sw:coi",
+ opener_coi, openee_basic, sw_coi,
+ swap_browsing_context_group);
+popupCoopBySwTest("opener:coi, openee:coi, sw:basic",
+ opener_coi, openee_coi, sw_basic,
+ keep_browsing_context_group);
+popupCoopBySwTest("opener:coi, openee:coi, sw:coi",
+ opener_coi, openee_coi, sw_coi,
+ keep_browsing_context_group);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-meta-http-equiv.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-meta-http-equiv.https.html
new file mode 100644
index 0000000000..157f7aef46
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-meta-http-equiv.https.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin"><!-- should not be supported -->
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup without coop",
+ "coop": "",
+ "same_origin_opener": "preserved",
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, variant.coop, variant.same_origin_opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html
new file mode 100644
index 0000000000..6568ac3fdb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<meta name="variant" content="?0-1">
+<meta name="variant" content="?2-3">
+<meta name="variant" content="?4-5">
+<meta name="variant" content="?6-7">
+<meta name="variant" content="?8-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/subset-tests.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+
+<div id=log></div>
+<script>
+
+function url_test_cache(t, url, responseToken, iframeToken, hasOpener) {
+ return new Promise(async (resolve, reject) => {
+ try {
+ await dispatcher_url_test(t, url, responseToken, iframeToken, hasOpener, undefined /* OpenerDomAccess */, resolve);
+ } catch(e) {
+ reject(e);
+ }
+ }).then(() => {
+ return new Promise(async (resolve, reject) => {
+ try {
+ // Test the same url for cache.
+ // Note that at this point the popup from the first
+ // `dispatcher_url_test()` call hasn't been closed yet, but once this
+ // test has finished both will be cleaned up.
+ await dispatcher_url_test(t, url, responseToken, iframeToken, hasOpener, undefined /* OpenerDomAccess */, resolve);
+ } catch(e) {
+ reject(e);
+ }
+ });
+ });
+}
+
+// Redirect from hostA to hostB with same coop and coep.
+// Cache the hostA page if redirectCache is true.
+// Cache the hostB page if destCache is true.
+function coop_redirect_cache_test(t, hostA, hostB, coop, coep, redirectCache, destCache, hasOpener) {
+ let redirectUrl = `${hostA.origin}/html/cross-origin-opener-policy/resources/coop-coep.py`;
+ let redirectCacheString = redirectCache ? "&cache=1" : "";
+ let destCacheString = destCache ? "&cache=1" : "";
+ let responseToken = token();
+ let iframeToken = token();
+ let destUrl = `${hostB.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${coop}&coep=${coep}${destCacheString}&responseToken=${responseToken}&iframeToken=${iframeToken}`;
+ let url = `${redirectUrl}?coop=${coop}&coep=${coep}${redirectCacheString}&redirect=${encodeURIComponent(destUrl)}`;
+
+ return url_test_cache(t, url, responseToken, iframeToken, hasOpener);
+}
+
+function run_redirect_cache_tests(documentCOOPValueTitle, testArray) {
+ testArray.forEach((test, i) => {
+ // Only run specified variants
+ if (!shouldRunSubTest(i)) {
+ return;
+ }
+
+ promise_test(t => {
+ return coop_redirect_cache_test(t, test[0], test[1], "same-origin", "require-corp", test[2], test[3], test[4]);
+ }, `${documentCOOPValueTitle} document opening popup redirect from ${test[0].origin} to ${test[1].origin} with redirectCache ${test[2]} and destCache ${test[3]}`);
+ });
+}
+
+const tests = [
+ // popup Origin, final Origin, isCacheRedirect, isCacheDestination, hasOpener
+ // Origin A->A->B
+ [SAME_ORIGIN, CROSS_ORIGIN, true, false, false],
+ [SAME_ORIGIN, CROSS_ORIGIN, false, true, false],
+ [SAME_ORIGIN, CROSS_ORIGIN, true, true, false],
+
+ // Origin A->B->B
+ [CROSS_ORIGIN, SAME_ORIGIN, true, false, false],
+ [CROSS_ORIGIN, SAME_ORIGIN, false, true, false],
+ [CROSS_ORIGIN, SAME_ORIGIN, true, true, false],
+
+ // Origin A->B->C
+ [SAME_SITE, CROSS_ORIGIN, true, false, false],
+ [SAME_SITE, CROSS_ORIGIN, false, true, false],
+ [SAME_SITE, CROSS_ORIGIN, true, true, false],
+];
+
+run_redirect_cache_tests("same-origin", tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-cache.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-same-origin-allow-popups.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-same-origin-allow-popups.https.html
new file mode 100644
index 0000000000..73119a76f8
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-redirect-same-origin-allow-popups.https.html
@@ -0,0 +1,96 @@
+<title>
+ Tests the interaction of COOP same-origin-allow-popups with redirects in a
+ newly opened popup.
+</title>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+
+<div id=log></div>
+<script>
+
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const same_origin = {
+ host: get_host_info().HTTPS_ORIGIN,
+ name: "Same origin"
+};
+const cross_origin = {
+ host: get_host_info().HTTPS_REMOTE_ORIGIN,
+ name: "Cross origin"
+};
+const coep_header = '|header(Cross-Origin-Embedder-Policy,unsafe-none)';
+
+// Tests the interaction of COOP same-origin-allow-popups with redirects in a
+// newly created popup.
+// 1- Creates a page with origin SAME_ORIGIN and COOP same-origin-allow-popups.
+// 2- This page opens a popup.
+// 3- The popup navigates and gets a redirect response with COOP unsafe none
+// and origin either SAME_ORIGIN or CROSS_ORIGIN
+// 4- The popup follows the redirect and ends up on a final page with COOP
+// same-origin-allow-popups and origin SAME_ORIGIN
+// 5- The popup and its opener should no longer be in the same browsing context
+// group (ie the popup doesn't have an opener and the window that opened the
+// popup sees it as closed).
+function redirect_test(popup_redirect_origin) {
+ promise_test(async t => {
+ // Identifies the test window.
+ const this_window_token = token();
+
+ // Identifies the first window that will open the popup. It has COOP
+ // same-origin-allow-popups.
+ const opener_token= token();
+ const same_origin_allow_popups_header =
+ `|header(Cross-Origin-Opener-Policy,same-origin-allow-popups)`;
+ const opener_url = same_origin.host + executor_path +
+ same_origin_allow_popups_header + `&uuid=${opener_token}`;
+
+ // Identifies the popup. It will initial try to navigate to
+ // popup_redirect_origin, which has COOP unsafe-none. The navigation is
+ // then redirected to a final response of SAME_ORIGIN and COOP
+ // same-origin-allow-popups.
+ const popup_token = token();
+ const popup_final_url = same_origin.host + executor_path +
+ same_origin_allow_popups_header + `&uuid=${popup_token}`;
+ const redirect_header = 'status(302)' +
+ `|header(Location,${encodeURIComponent(
+ popup_final_url
+ .replace(/,/g, "\\,")
+ .replace(/\\\\,/g, "\\\\\\,")
+ .replace(/\(/g, "%28")
+ .replace(/\)/g, "%29"))})`;
+ const popup_initial_url = popup_redirect_origin.host + executor_path +
+ redirect_header + `&uuid=${popup_token}`;
+
+ // 1. Create the initial window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The initial window opens a popup.
+ send(opener_token, `
+ popup = window.open("${popup_initial_url}");
+ `);
+ t.add_cleanup(() => send(popup_token, "window.close()"));
+
+ // 3. Check the opener status on the popup.
+ send(popup_token, `
+ send("${this_window_token}", window.opener !== null);
+ `);
+ assert_equals(await receive(this_window_token), "false", "opener");
+
+ // 4. Check the status of the popup from the initial window.
+ send(opener_token, `
+ send("${this_window_token}", popup.closed);
+ `);
+ assert_equals(await receive(this_window_token), "true", "popup.closed");
+
+ }, `${popup_redirect_origin.name} popup redirects to same-origin with same-origin-allow-popups`);
+}
+
+redirect_test(same_origin);
+redirect_test(cross_origin);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html
new file mode 100644
index 0000000000..d025faeb67
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop : variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-cross-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html
new file mode 100644
index 0000000000..438e424284
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "preserved"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop : variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html
new file mode 100644
index 0000000000..d1b6b60d7c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-allow-popups-with-same-site.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html
new file mode 100644
index 0000000000..2e58ea4553
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Cross-Origin-Opener-Policy: about:blank</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+async_test(t => {
+ const popup = window.open("resources/coop-coep.py?coop=same-origin&coep=&navigate=about:blank");
+ t.add_cleanup(() => popup.close());
+ assert_equals(window, popup.opener);
+
+ popup.onload = t.step_func(() => {
+ assert_true(popup.location.href.endsWith("&navigate=about:blank"));
+ // Use wait_for_callback as about:blank cannot message back.
+ t.step_wait_func_done(() => popup.location.href === "about:blank");
+ });
+}, "Navigating a popup to about:blank");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-non-initial-about-blank.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html
new file mode 100644
index 0000000000..bd1e6848c4
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-cross-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html
new file mode 100644
index 0000000000..caf4b173c2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html
new file mode 100644
index 0000000000..93ba9c9888
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-same-origin-with-same-site.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html
new file mode 100644
index 0000000000..31ac569491
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..073ce7adfb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-cross-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: unsafe-none
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html
new file mode 100644
index 0000000000..18f87048c3
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..073ce7adfb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: unsafe-none
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html
new file mode 100644
index 0000000000..d1b6b60d7c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html.headers
new file mode 100644
index 0000000000..073ce7adfb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unsafe-none-with-same-site.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: unsafe-none
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-cross-origin.https.html
new file mode 100644
index 0000000000..31ac569491
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-cross-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-origin.https.html
new file mode 100644
index 0000000000..18f87048c3
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-origin.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-site.https.html
new file mode 100644
index 0000000000..d1b6b60d7c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-unspecified-with-same-site.https.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with jibberish coop",
+ "coop": "jibberish",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop",
+ "coop": "same-site",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 2",
+ "coop": "same-site unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with obsolete coop 3",
+ "coop": "same-origin unsafe-allow-outgoing",
+ "opener": "preserved"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html
new file mode 100644
index 0000000000..e337e69cc0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/common.js"></script>
+<script src="resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with structured coop unsafe-none",
+ "coop": "unsafe-none; report-to=endpoint",
+ "opener": "severed",
+ },
+ {
+ "title": "popup with structured coop same-origin",
+ "coop": "same-origin; report-to=endpoint",
+ "opener": "preserved",
+ },
+ {
+ "title": "popup with structured coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups; report-to=endpoint",
+ "opener": "severed",
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN, { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/popup-with-structured-header.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/META.yml b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/META.yml
new file mode 100644
index 0000000000..0db28208a6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/META.yml
@@ -0,0 +1,6 @@
+suggested_reviewers:
+ - ArthurSonzogni
+ - ParisMeuleman
+ - camillelamy
+ - hemeryar
+ - mikewest
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro.https.html
new file mode 100644
index 0000000000..a7e83cc0d9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro.https.html
@@ -0,0 +1,82 @@
+<title>
+ COOP reports are to the opener when the opener used COOP-RO+COEP and then it
+ tries to access a same-origin openee.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP and a reporter.
+ const opener_report_token= token();
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(opener_report_token);
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. This is same origin with the "opener".
+ const openee_report_token = token();
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens it openee.
+ send(opener_token, `
+ openee = window.open("${openee_requested_url}");
+ send("${this_window_token}", "ACK 1");
+ `);
+ assert_equals("ACK 1", await receive(this_window_token));
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. Ensure the openee's document to be loaded.
+ send(openee_token, `
+ send("${this_window_token}", "ACK 2");
+ `);
+ assert_equals("ACK 2", await receive(this_window_token));
+
+ // 4. The opener tries to access its openee.
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(openee);")
+ );
+ // 5. Check a report sent to the opener.
+ let report =
+ await receiveReport(opener_report_token, "access-from-coop-page-to-openee")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_found(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, openee_url);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, openee_requested_url);
+}, name);
+
+runTest(false, "access-from-coop-page-to-openee, same-origin");
+runTest(true , "access-from-coop-page-to-openee, same-origin + redirect");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro_cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro_cross-origin.https.html
new file mode 100644
index 0000000000..fe72a2299f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-openee_coop-ro_cross-origin.https.html
@@ -0,0 +1,85 @@
+<title>
+ COOP reports are to the opener when the opener used COOP-RO+COEP and then it
+ tries to access a cross-origin openee.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP and a reporter.
+ const opener_report_token= token();
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(opener_report_token);
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. This is cross origin with the "opener".
+ const openee_report_token= token();
+ const openee_token = token();
+ const openee_url = cross_origin + executor_path + `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens it openee.
+ send(opener_token, `
+ openee = window.open("${openee_requested_url}");
+ send("${this_window_token}", "ACK 1");
+ `);
+ assert_equals("ACK 1", await receive(this_window_token));
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. Ensure the openee's document to be loaded.
+ send(openee_token, `
+ send("${this_window_token}", "ACK 2");
+ `);
+ assert_equals("ACK 2", await receive(this_window_token));
+
+ // 4. The opener tries to access its openee.
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(openee);")
+ );
+
+
+ // 5. Check a report sent to the opener.
+ let report =
+ await receiveReport(opener_report_token, "access-from-coop-page-to-openee")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_found(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, "");
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, openee_requested_url);
+}, name);
+
+runTest(false, "access-from-coop-page-to-openee, cross-origin");
+runTest(true , "access-from-coop-page-to-openee, cross-origin + redirect");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro.https.html
new file mode 100644
index 0000000000..005339a06e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro.https.html
@@ -0,0 +1,63 @@
+<title>
+ COOP reports are sent when the openee used COOP-RO+COEP and then tries to
+ access its same-origin opener.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ const report_token = token();
+ const openee_token = token();
+
+ const opener_url = location.href;
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = same_origin + executor_path +
+ reportTo.header + reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+ const openee = window.open(openee_requested_url);
+ t.add_cleanup(() => send(openee_token, "window.close()"))
+
+ // 1. Try to access the opener. A report is sent, because of COOP-RO+COEP.
+
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener);")
+ );
+
+ // 2. Check a report is sent to the openee.
+ let report =
+ await receiveReport(report_token, "access-from-coop-page-to-opener")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, openee_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_found(report);
+ assert_equals(report.body.openerURL, opener_url);
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, opener_url);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, name);
+
+runTest(false, "access-from-coop-page-to-opener, same-origin");
+runTest(true , "access-from-coop-page-to-opener, same-origin + redirect");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro_cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro_cross-origin.https.html
new file mode 100644
index 0000000000..eedfaa557f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-opener_coop-ro_cross-origin.https.html
@@ -0,0 +1,63 @@
+<title>
+ COOP reports are sent when the openee used COOP-RO+COEP and then tries to
+ access its cross-origin opener.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ const report_token = token();
+ const openee_token = token();
+
+ const opener_origin = location.origin + '/';
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = cross_origin + executor_path +
+ reportTo.header + reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+ const openee = window.open(openee_requested_url);
+ t.add_cleanup(() => send(openee_token, "window.close()"))
+
+ // 1. Try to access the opener. A report is sent, because of COOP-RO+COEP.
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener);")
+ );
+
+ // 2. Check a report is sent to the openee.
+ let report =
+ await receiveReport(report_token, "access-from-coop-page-to-opener")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, openee_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_found(report);
+ assert_equals(report.body.openerURL, "");
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, opener_origin);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, name);
+
+runTest(false, "access-from-coop-page-to-opener, cross-origin");
+runTest(true , "access-from-coop-page-to-opener, cross-origin + redirect");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro.https.html
new file mode 100644
index 0000000000..90df0e4e99
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro.https.html
@@ -0,0 +1,92 @@
+<title>
+ One window accesses a second one. They are aren't related by an opener/openee
+ relationship. The first window has set
+ Cross-Origin-Opener-Policy-Report-Only:same-origin, so it receives a
+ "access-from-coop-page-to-other" report.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+let escapeComma = url => url.replace(/,/g, '\\,');
+
+promise_test(async t => {
+ const report_token= token();
+ const report_to = reportToHeaders(report_token);
+
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. With COOP:same-origin + reporter.
+ const opener_token = token();
+ const opener_url = same_origin + executor_path + report_to.header +
+ report_to.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. With COOP:same-origin + reporter.
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + report_to.header +
+ report_to.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+
+ // The "other" window.
+ const other_token = token();
+ const other_url = same_origin + executor_path + report_to.header +
+ `&uuid=${other_token}`;
+
+ t.add_cleanup(() => {
+ send(opener_token, "window.close()")
+ send(openee_token, "window.close()")
+ send(other_token, "window.close()")
+ })
+
+ // 1. Create the "opener" window.
+ let opener_window_proxy = window.open(opener_url);
+
+ // 2. Create the "openee" window.
+ send(opener_token, `
+ window.openee = window.open('${escapeComma(openee_url)}');
+ `);
+
+ // 3. Create the "other" window.
+ send(openee_token, `
+ window.other = window.open('${escapeComma(other_url)}');
+ `);
+
+ // 4. Wait for "other" to load its document.
+ send(other_token, `send('${this_window_token}', "Loaded");`);
+ assert_equals(await receive(this_window_token), "Loaded");
+
+ // 5. "opener" accesses "other" window, through "openee".
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(openee.other);")
+ );
+
+ // 6. Check a report is sent to the openee.
+ let report =
+ await receiveReport(report_token, "access-from-coop-page-to-other")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_found(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, other_url.replace(/"/g, '%22'));
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, "access-from-coop-page-to-other (COOP-RO)");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro_cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro_cross-origin.https.html
new file mode 100644
index 0000000000..f0d60c2531
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-from-coop-page-to-other_coop-ro_cross-origin.https.html
@@ -0,0 +1,93 @@
+<title>
+ One window accesses a second one. They are aren't related by an opener/openee
+ relationship. The first window has set
+ Cross-Origin-Opener-Policy-Report-Only:same-origin, so it receives a
+ "access-from-coop-page-to-other" report.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin= get_host_info().HTTPS_ORIGIN;
+const cross_origin= get_host_info().HTTPS_REMOTE_ORIGIN;
+
+let escapeComma = url => url.replace(/,/g, '\\,');
+
+promise_test(async t => {
+ const report_token= token();
+ const report_to = reportToHeaders(report_token);
+
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. With COOP:same-origin + reporter.
+ const opener_token = token();
+ const opener_url = same_origin + executor_path + report_to.header +
+ report_to.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. With COOP:same-origin + reporter.
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + report_to.header +
+ report_to.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+
+ // The "other" window.
+ const other_token = token();
+ const other_url = cross_origin + executor_path + report_to.header +
+ `&uuid=${other_token}`;
+
+ t.add_cleanup(() => {
+ send(opener_token, "window.close()")
+ send(openee_token, "window.close()")
+ send(other_token, "window.close()")
+ })
+
+ // 1. Create the "opener" window.
+ let opener_window_proxy = window.open(opener_url);
+
+ // 2. Create the "openee" window.
+ send(opener_token, `
+ window.openee = window.open('${escapeComma(openee_url)}');
+ `);
+
+ // 3. Create the "other" window.
+ send(openee_token, `
+ window.other = window.open('${escapeComma(other_url)}');
+ `);
+
+ // 4. Wait for "other" to load its document.
+ send(other_token, `send('${this_window_token}', "Loaded");`);
+ assert_equals(await receive(this_window_token), "Loaded");
+
+ // 5. "opener" accesses "other" window, through "openee".
+
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(openee.other);")
+ );
+
+ // 6. Check a report is sent to the openee.
+ let report =
+ await receiveReport(report_token, "access-from-coop-page-to-other")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_found(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, "");
+ assert_equals(report.body.referrer, undefined);
+}, "access-from-coop-page-to-other (COOP-RO)");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro.https.html
new file mode 100644
index 0000000000..9f0a8821a4
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro.https.html
@@ -0,0 +1,77 @@
+<title>
+ COOP reports are to the opener when the opener used COOP-RO+COEP and then its
+ same-origin openee tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP and a reporter.
+ const opener_report_token= token();
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(opener_report_token);
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. This is same origin with the "opener".
+ const openee_report_token= token();
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens its openee.
+ send(opener_token, `
+ openee = window.open("${openee_requested_url}");
+ send("${this_window_token}", "ACK 1");
+ `);
+ assert_equals("ACK 1", await receive(this_window_token));
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. The openee tries to access its opener.
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener);")
+ );
+
+ // 4. Check a report sent to the opener.
+ let report =
+ await receiveReport(opener_report_token, "access-to-coop-page-from-openee")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, openee_url);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, openee_requested_url);
+}, name);
+
+runTest(false, "access-to-coop-page-from-openee, same-origin");
+runTest(true , "access-to-coop-page-from-openee, same-origin + redirect");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro_cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro_cross-origin.https.html
new file mode 100644
index 0000000000..d9577836d9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-openee_coop-ro_cross-origin.https.html
@@ -0,0 +1,79 @@
+<title>
+ COOP reports are to the opener when the opener used COOP-RO+COEP and then its
+ cross-origin openee tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cross_origin= get_host_info().HTTPS_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP and a reporter.
+ const opener_report_token= token();
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(opener_report_token);
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. This is cross origin with the "opener".
+ const openee_report_token= token();
+ const openee_token = token();
+ const openee_url = cross_origin + executor_path + `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens its openee.
+ send(opener_token, `
+ openee = window.open("${openee_requested_url}");
+ send("${this_window_token}", "ACK 1");
+ `);
+ assert_equals("ACK 1", await receive(this_window_token));
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. The openee tries to access its opener.
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener);")
+ );
+
+ // 4. Check a report sent to the opener.
+ let report =
+ await receiveReport(opener_report_token, "access-to-coop-page-from-openee")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, "");
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, openee_requested_url);
+}, name);
+
+runTest(false, "access-to-coop-page-from-openee, cross-origin");
+runTest(true , "access-to-coop-page-from-openee, cross-origin + redirect)");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro.https.html
new file mode 100644
index 0000000000..8a643d762c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro.https.html
@@ -0,0 +1,67 @@
+<title>
+ COOP reports are sent when the openee used COOP-RO+COEP and then its
+ same-origin opener tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ const report_token = token();
+ const openee_token = token();
+ const opener_token = token(); // The current test window.
+
+ const opener_url = location.href;
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = same_origin + executor_path + reportTo.header +
+ reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+
+ const openee = window.open(openee_requested_url);
+ t.add_cleanup(() => send(openee_token, "window.close()"))
+
+ // 1. Make sure the new document to be loaded.
+ send(openee_token, `
+ send("${opener_token}", "Ready");
+ `);
+ let reply = await receive(opener_token);
+ assert_equals(reply, "Ready");
+
+ // 2. Try to access the openee. A report is sent, because of COOP-RO+COEP.
+ tryAccess(openee);
+
+ // 3. Check a report is sent to the openee.
+ let report =
+ await receiveReport(report_token, "access-to-coop-page-from-opener")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, openee_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, opener_url);
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, opener_url);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, name);
+
+runTest(false, "access-to-coop-page-from-opener, same-origin");
+runTest(true , "access-to-coop-page-from-opener, same-origin + redirect");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro_cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro_cross-origin.https.html
new file mode 100644
index 0000000000..7e1ae870a7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-opener_coop-ro_cross-origin.https.html
@@ -0,0 +1,68 @@
+<title>
+ COOP reports are sent when the openee used COOP-RO+COEP and then its
+ cross-origin opener tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const redirect_path = directory + "/resources/redirect.py?";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+let runTest = (openee_redirect, name) => promise_test(async t => {
+ const report_token = token();
+ const openee_token = token();
+ const opener_token = token(); // The current test window.
+
+ const opener_origin = location.origin + '/';
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = cross_origin + executor_path +
+ reportTo.header + reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+ const openee_redirect_url = same_origin + redirect_path + openee_url
+ const openee_requested_url = openee_redirect ? openee_redirect_url
+ : openee_url;
+
+
+ const openee = window.open(openee_requested_url);
+ t.add_cleanup(() => send(openee_token, "window.close()"))
+
+ // 1. Make sure the new document to be loaded.
+ send(openee_token, `
+ send("${opener_token}", "Ready");
+ `);
+ let reply = await receive(opener_token);
+ assert_equals(reply, "Ready");
+
+ // 2. Try to access the openee. A report is sent, because of COOP-RO+COEP.
+ tryAccess(openee);
+
+ // 3. Check a report is sent to the openee.
+ let report =
+ await receiveReport(report_token, "access-to-coop-page-from-opener")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, openee_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, "");
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, opener_origin);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, name);
+
+runTest(false, "access-to-coop-page-from-opener, cross-origin");
+runTest(true , "access-to-coop-page-from-opener, cross-origin + redirect");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro.https.html
new file mode 100644
index 0000000000..b73bab8610
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro.https.html
@@ -0,0 +1,82 @@
+<title>
+ One window accesses a second one. They are aren't related by an opener/openee
+ relationship. The second window has set
+ Cross-Origin-Opener-Policy-Report-Only:same-origin, so it receives a
+ "access-to-coop-page-from-other" report.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window.
+ const opener_token = token();
+ const opener_url = same_origin + executor_path + `&uuid=${opener_token}`;
+
+ // The "openee" window. With COOP:same-origin + reporter.
+ const openee_report_token= token();
+ const openee_token = token();
+ const openee_reportTo = reportToHeaders(openee_report_token);
+ const openee_url = same_origin + executor_path + openee_reportTo.header +
+ openee_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+
+ // The "other" window.
+ const other_token = token();
+ const other_url = same_origin + executor_path + `&uuid=${other_token}`;
+
+ t.add_cleanup(() => {
+ send(opener_token, "window.close()")
+ send(openee_token, "window.close()")
+ send(other_token, "window.close()")
+ })
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+
+ // 2. The opener opens its openee and the other window.
+ send(opener_token, `
+ window.openee = window.open('${openee_url.replace(/,/g, '\\,')}');
+ window.other = window.open('${other_url}');
+ `);
+
+ // 3. Make sure the openee is loaded.
+ send(openee_token, `send("${this_window_token}", "Loaded");`);
+ assert_equals(await receive(this_window_token), "Loaded");
+
+ // 4. The "other" window attempts to access the openee though the opener.
+ send(other_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener.openee);")
+ );
+
+ // 4. Check a report sent to the openee.
+ let report =
+ await receiveReport(openee_report_token, "access-to-coop-page-from-other")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, openee_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, other_url);
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, "access-to-coop-page-from-other (COOP-RO)");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro_cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro_cross-origin.https.html
new file mode 100644
index 0000000000..c86daa3dca
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/access-to-coop-page-from-other_coop-ro_cross-origin.https.html
@@ -0,0 +1,83 @@
+<title>
+ One window accesses a second one. They are aren't related by an opener/openee
+ relationship. The second window has set
+ Cross-Origin-Opener-Policy-Report-Only:same-origin, so it receives a
+ "access-to-coop-page-from-other" report.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window.
+ const opener_token = token();
+ const opener_url = same_origin + executor_path + `&uuid=${opener_token}`;
+
+ // The "openee" window. With COOP:same-origin + reporter.
+ const openee_report_token= token();
+ const openee_token = token();
+ const openee_reportTo = reportToHeaders(openee_report_token);
+ const openee_url = cross_origin + executor_path + openee_reportTo.header +
+ openee_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+
+ // The "other" window.
+ const other_token = token();
+ const other_url = same_origin + executor_path + `&uuid=${other_token}`;
+
+ t.add_cleanup(() => {
+ send(opener_token, "window.close()")
+ send(openee_token, "window.close()")
+ send(other_token, "window.close()")
+ })
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+
+ // 2. The opener opens its openee and the other window.
+ send(opener_token, `
+ window.openee = window.open('${openee_url.replace(/,/g, '\\,')}');
+ window.other = window.open('${other_url}');
+ `);
+
+ // 3. Make sure the openee is loaded.
+ send(openee_token, `send("${this_window_token}", "Loaded");`);
+ assert_equals(await receive(this_window_token), "Loaded");
+
+ // 4. The "other" window attempts to access the openee though the opener.
+ send(other_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener.openee);")
+ );
+
+ // 4. Check a report sent to the openee.
+ let report =
+ await receiveReport(openee_report_token, "access-to-coop-page-from-other")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, openee_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, "");
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, "access-to-coop-page-from-other (COOP-RO)");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-blur.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-blur.https.html
new file mode 100644
index 0000000000..849bf6579a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-blur.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.blur() access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("blur", w => w.blur());
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-close.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-close.https.html
new file mode 100644
index 0000000000..7696600488
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-close.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.close() access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("close", w => w.close());
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-closed.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-closed.https.html
new file mode 100644
index 0000000000..c678d18a80
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-closed.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.closed access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("closed", w => w.closed);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-focus.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-focus.https.html
new file mode 100644
index 0000000000..363c0d294f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-focus.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.focus() access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("focus", w => w.focus());
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html
new file mode 100644
index 0000000000..fc1925045f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-frames.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.frames access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("frames", w => w.frames);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html
new file mode 100644
index 0000000000..b6c5f5acb1
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html
@@ -0,0 +1,66 @@
+<title> Check reports are sent for the indexed getter</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+let origin = [
+ ["cross-origin" , get_host_info().HTTPS_REMOTE_ORIGIN ] ,
+ ["same-site" , get_host_info().HTTPS_ORIGIN ] ,
+];
+
+let testCase = [
+//[operation , expectReport ] ,
+ [w => w[0] , true ], // Existing iframe.
+ [w => w[1] , false ], // Out of bounds (positive).
+ [w => w[-1] , false ], // Out of bounds (negative).
+];
+
+origin.forEach(([origin_name, origin]) => {
+ testCase.forEach(([op, expectReport]) => {
+ promise_test(async t => {
+ const opener_token = token();
+ const openee_token = token();
+
+ const openee_url = origin+ executor_path + `&uuid=${openee_token}`;
+ const openee = window.open(openee_url);
+ t.add_cleanup(() => send(openee_token, "window.close()"))
+
+ // 1. Create an iframe in the openee.
+ send(openee_token, `
+ let iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+
+ send("${opener_token}", "openee loaded");
+ `);
+ let reply = await receive(opener_token);
+ assert_equals(reply, "openee loaded");
+
+ // 2. Try to access the openee.
+ let observer = new ReportingObserver(()=>{});
+ observer.observe();
+ try {op(openee)} catch(e) {}
+ let reports = observer.takeRecords();
+ observer.disconnect();
+
+ // 3. Check the received reports.
+ if (expectReport) {
+ assert_equals(reports.length, 1);
+ assert_equals(reports[0].type, "coop-access-violation");
+ assert_equals(reports[0].body.property, "indexed");
+ } else {
+ assert_equals(reports.length, 0);
+ }
+
+ }, `${origin_name} > ${op}`);
+});
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html.headers
new file mode 100644
index 0000000000..64f4d5fedf
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-indexed-getter.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy-Report-Only: same-origin; report-to="none"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-length.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-length.https.html
new file mode 100644
index 0000000000..a9f3614cb5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-length.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.length access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("length", w => w.length);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-get.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-get.https.html
new file mode 100644
index 0000000000..442817748d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-get.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.location access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("location", w => w.location);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html
new file mode 100644
index 0000000000..e42f084821
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.location access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("location", w => w.location = "#");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html
new file mode 100644
index 0000000000..27be9a48d1
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html
@@ -0,0 +1,71 @@
+<title> Check reports are sent for the indexed getter</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script>
+
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+let crossOrigin = ["cross-origin" , get_host_info().HTTPS_REMOTE_ORIGIN ];
+let sameOrigin = ["same-site" , get_host_info().HTTPS_ORIGIN ];
+
+let testCase = [
+//[ operation , origin , expectReport ],
+ [ w => w["iframeName"] , sameOrigin , true ],
+ [ w => w["iframeName"] , crossOrigin , true ],
+ [ w => w["divID"] , sameOrigin , true ],
+ [ w => w["divID"] , crossOrigin , false ],
+ [ w => w["existingGlobal"] , sameOrigin , false ],
+ [ w => w["existingGlobal"] , crossOrigin , false ],
+ [ w => w["missingGlobal"] , sameOrigin , false ],
+ [ w => w["missingGlobal"] , crossOrigin , false ],
+];
+
+testCase.forEach(([op, [origin_name, origin], expectReport]) => {
+ promise_test(async t => {
+ const opener_token = token();
+ const openee_token = token();
+
+ const openee_url = origin + executor_path + `&uuid=${openee_token}`;
+ const openee = window.open(openee_url);
+ t.add_cleanup(() => send(openee_token, "window.close()"))
+
+ // 1. Make sure the new document to be loaded. Populate the document.
+ send(openee_token, `
+ let iframe = document.createElement("iframe");
+ iframe.name = "iframeName";
+ document.body.appendChild(iframe);
+
+ let div = document.createElement("div");
+ div.id = "divID";
+ document.body.appendChild(div);
+
+ window.existingGlobal = "test";
+
+ send("${opener_token}", "Ready");
+ `);
+ let reply = await receive(opener_token);
+ assert_equals(reply, "Ready");
+
+ // 2. Try to access the openee.
+ let observer = new ReportingObserver(()=>{});
+ observer.observe();
+ try {op(openee)} catch(e) {}
+ let reports = observer.takeRecords();
+ observer.disconnect();
+
+ // 3. Check the received reports.
+ if (expectReport) {
+ assert_equals(reports.length, 1);
+ assert_equals(reports[0].type, "coop-access-violation");
+ assert_equals(reports[0].body.property, "named");
+ } else {
+ assert_equals(reports.length, 0);
+ }
+
+ }, `${origin_name} > ${op}`);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html.headers
new file mode 100644
index 0000000000..64f4d5fedf
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-named-getter.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy-Report-Only: same-origin; report-to="none"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-get.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-get.https.html
new file mode 100644
index 0000000000..b99dfdc562
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-get.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.opener access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("opener", w => w.opener);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html
new file mode 100644
index 0000000000..10c251140b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-opener-set.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.opener access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("opener", w => w.opener = "", /* expectReport = */false);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-1.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-1.https.html
new file mode 100644
index 0000000000..a9168fdaa5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-1.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.postMessage(arg1, arg2) access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("postMessage", w => w.postMessage("", ""));
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-2.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-2.https.html
new file mode 100644
index 0000000000..4341f245d5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-postmessage-2.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.postMessage(arg1) access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("postMessage", w => w.postMessage(""));
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html
new file mode 100644
index 0000000000..7a7d5a3fec
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-self.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.self access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("self", w => w.self);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-top.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-top.https.html
new file mode 100644
index 0000000000..1b75ecc105
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-top.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.top access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("top", w => w.top);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html
new file mode 100644
index 0000000000..07278b4a11
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/property-window.https.html
@@ -0,0 +1,13 @@
+<title> Check openee.window access is checked</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty("window", w => w.window);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/report-to-both_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/report-to-both_coop-ro.https.html
new file mode 100644
index 0000000000..46cdc6eb27
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/report-to-both_coop-ro.https.html
@@ -0,0 +1,124 @@
+<title>
+ Both the openee and the opener have a COOP reporter. The report are sent to
+ both side.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const origin_opener = get_host_info().HTTPS_ORIGIN;
+const origin_openee = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+let escapeComma = url => url.replace(/,/g, '\\,');
+
+let genericSetup = async function(test) {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP and a reporter.
+ const opener_report_token= token();
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(opener_report_token);
+ const opener_url = origin_opener+ executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. This has COOP and a reporter.
+ const openee_report_token= token();
+ const openee_token = token();
+ const openee_reportTo = reportToHeaders(openee_report_token);
+ const openee_url = origin_openee + executor_path + openee_reportTo.header +
+ openee_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+
+ // Cleanup at the end of the test.
+ test.add_cleanup(() => {
+ send(openee_token, 'window.close()');
+ send(opener_token, 'window.close()');
+ });
+
+ // 1. Spawn the opener and the openee windows.
+ window.open(opener_url);
+ send(opener_token, `
+ openee = window.open('${escapeComma(openee_url)}');
+ `);
+
+ // 2. Wait for both to be loaded.
+ send(openee_token, `send('${this_window_token}', 'ACK');`);
+ assert_equals(await receive(this_window_token), 'ACK');
+
+ return [
+ this_window_token,
+ opener_token, opener_report_token, opener_url,
+ openee_token, openee_report_token, openee_url,
+ ];
+}
+
+let assert_generic_coop_report = function(report) {
+ assert_equals(report.type, "coop");
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+}
+
+promise_test(async test => {
+ let [
+ this_window_token,
+ opener_token, opener_report_token, opener_url,
+ openee_token, openee_report_token, openee_url,
+ ] = await genericSetup(test);
+
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(openee);")
+ );
+
+ let report_opener =
+ await receiveReport(opener_report_token, "access-from-coop-page-to-openee")
+ let report_openee =
+ await receiveReport(openee_report_token, "access-to-coop-page-from-opener")
+
+ assert_generic_coop_report(report_openee);
+ assert_generic_coop_report(report_opener);
+
+ assert_equals(report_opener.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report_openee.url, openee_url.replace(/"/g, '%22'));
+ assert_source_location_found(report_opener);
+ assert_source_location_missing(report_openee);
+}, "Access from opener")
+
+promise_test(async test => {
+ let [
+ this_window_token,
+ opener_token, opener_report_token, opener_url,
+ openee_token, openee_report_token, openee_url,
+ ] = await genericSetup(test);
+
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener);")
+ );
+
+ let report_opener =
+ await receiveReport(opener_report_token, "access-to-coop-page-from-openee")
+ let report_openee =
+ await receiveReport(openee_report_token, "access-from-coop-page-to-opener")
+
+ assert_generic_coop_report(report_openee);
+ assert_generic_coop_report(report_opener);
+
+ assert_equals(report_opener.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report_openee.url, openee_url.replace(/"/g, '%22'));
+ assert_source_location_missing(report_opener);
+ assert_source_location_found(report_openee);
+}, "Access from openee")
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html
new file mode 100644
index 0000000000..375c627d27
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html
@@ -0,0 +1,275 @@
+<!doctype html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>
+ Check the ReportingObserver(s) are notified about the coop-access-violation
+ events.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_site = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
+const corp_header = '|header(Cross-Origin-Resource-Policy,cross-origin)';
+
+promise_test(async t => {
+ // This test window.
+ const this_window_token = token();
+
+ // The "opener" window, using COOP-Report-Only and a reporter.
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(token());
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window, NOT using COOP.
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + `&uuid=${openee_token}`;
+
+ // 1. Create the opener window.
+ window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close();"));
+
+ // 2. The opener opens its openee.
+ send(opener_token, `openee = window.open('${openee_url}');`);
+ t.add_cleanup(() => send(openee_token, `window.close();`));
+
+ // 3. Wait for the openee to load its document.
+ send(openee_token, `send("${this_window_token}", "Ready");`);
+ assert_equals(await receive(this_window_token), "Ready");
+
+ // 4. The opener tries to access its openee. All reports for blocked access
+ // from the COOP page should notify the ReportingObservers.
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js", `
+ let observer = new ReportingObserver(()=>{});
+ observer.observe();
+ tryAccess(openee);
+ let reports = observer.takeRecords();
+ send("${this_window_token}", JSON.stringify(reports));
+ observer.disconnect();
+ `));
+
+ let report_access_from = JSON.parse(await receive(this_window_token));
+ assert_equals(report_access_from.length, 1, "No report received.");
+ assert_equals(report_access_from[0].type, "coop-access-violation");
+ assert_equals(report_access_from[0].url, opener_url.replace(/"/g, '%22'));
+ assert_source_location_found(report_access_from[0])
+ assert_equals(report_access_from[0].body.type,
+ "access-from-coop-page-to-openee");
+ assert_equals(report_access_from[0].body.openeeURL, openee_url);
+ assert_equals(report_access_from[0].body.openerURL, undefined);
+ assert_equals(report_access_from[0].body.otherDocumentURL, undefined);
+
+ // 5. The openee tries to access its opener. No reports for blocked access
+ // to the COOP page should be dispatched.
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js", `
+ let observer = new ReportingObserver(()=>{});
+ observer.observe();
+ tryAccess(opener);
+ let reports = observer.takeRecords();
+ send("${this_window_token}", JSON.stringify(reports));
+ observer.disconnect();
+ `));
+ let report_access_to = JSON.parse(await receive(this_window_token));
+ assert_equals(report_access_to.length, 0, "Unexpected report received.");
+}, "Opener COOP");
+
+promise_test(async t => {
+ // This test window.
+ const this_window_token = token();
+
+ // The "opener" window, NOT using COOP.
+ const opener_token = token();
+ const opener_url = same_origin + executor_path + `&uuid=${opener_token}`;
+
+ // The "openee" window, using COOP-Report-Only and a reporter.
+ const openee_token = token();
+ const openee_reportTo = reportToHeaders(token());
+ const openee_url = same_origin + executor_path + openee_reportTo.header +
+ openee_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+
+ // 1. Create the opener window.
+ window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close();"));
+
+ // 2. The opener opens its openee.
+ send(opener_token,
+ `openee = window.open('${openee_url.replace(/,/g, '\\,')}');`);
+ t.add_cleanup(() => send(openee_token, `window.close();`));
+
+ // 3. The openee tries to access its opener. All reports for blocked access
+ // from the COOP page should notify the ReportingObservers.
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js", `
+ let observer = new ReportingObserver(()=>{});
+ observer.observe();
+ tryAccess(opener);
+ let reports = observer.takeRecords();
+ send("${this_window_token}", JSON.stringify(reports));
+ observer.disconnect();
+ `));
+ let report_access_from = JSON.parse(await receive(this_window_token));
+ assert_equals(report_access_from.length, 1, "No report received.");
+ assert_equals(report_access_from[0].type, "coop-access-violation");
+ assert_equals(report_access_from[0].url, openee_url.replace(/"/g, '%22'));
+ assert_true(report_access_from[0].body.sourceFile.includes("try-access.js"));
+ assert_source_location_found(report_access_from[0])
+ assert_equals(report_access_from[0].body.type,
+ "access-from-coop-page-to-opener");
+ assert_equals(report_access_from[0].body.openeeURL, undefined);
+ assert_equals(report_access_from[0].body.openerURL, opener_url);
+ assert_equals(report_access_from[0].body.otherDocumentURL, undefined);
+
+ // 4. The opener tries to access its openee. No reports for blocked access
+ // to the COOP page should be dispatched.
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js", `
+ let observer = new ReportingObserver(()=>{});
+ observer.observe();
+ tryAccess(openee);
+ let reports = observer.takeRecords();
+ send("${this_window_token}", JSON.stringify(reports));
+ observer.disconnect();
+ `));
+ let report_access_to = JSON.parse(await receive(this_window_token));
+ assert_equals(report_access_to.length, 0, "Unexpected report received.");
+}, "Openee COOP");
+
+promise_test(async t => {
+ // This test window.
+ const this_window_token = token();
+
+ // The "opener" window, using COOP-Report-Only and a reporter.
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(token());
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "opener's iframe", same-origin with its parent.
+ const opener_iframe_token = token();
+ const opener_iframe_url = same_origin + executor_path + coep_header +
+ `&uuid=${opener_iframe_token}`;
+
+ // The "openee" window, NOT using COOP.
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + coep_header +
+ `&uuid=${openee_token}`;
+
+ // 1. Create the opener window.
+ window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close();"));
+
+ // 2. The opener opens an iframe, and install a ReportingObserver to catch
+ // future accesses.
+ send(opener_token, `
+ iframe = document.createElement("iframe");
+ iframe.src = "${opener_iframe_url}";
+ document.body.appendChild(iframe);
+
+ let observer = new ReportingObserver(reports => {
+ send("${this_window_token}", JSON.stringify(reports));
+ observer.disconnect();
+ });
+ observer.observe();
+ `);
+
+ // 3. The iframe opens the openee.
+ send(opener_iframe_token, `openee = window.open('${openee_url}');`);
+ t.add_cleanup(() => send(openee_token, `window.close();`));
+
+ // 4. Wait for the openee to load its document.
+ send(openee_token, `send("${this_window_token}", "Ready");`);
+ assert_equals(await receive(this_window_token), "Ready");
+
+ // 4. The opener's iframe tries to access the openee. This is an
+ // "access-from-coop-page" from a same-origin iframe, so the
+ // ReportingObserver(s) are notified.
+ send(opener_iframe_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js", `tryAccess(openee);`));
+
+ let reports = await receive(this_window_token);
+ reports = JSON.parse(reports);
+ assert_equals(reports.length, 1, "No report received.");
+ assert_equals(reports[0].type, "coop-access-violation");
+ assert_equals(reports[0].url, opener_url.replace(/"/g, '%22'));
+ assert_true(reports[0].body.sourceFile.includes("try-access.js"));
+ assert_source_location_found(reports[0]);
+ assert_equals(reports[0].body.type,
+ "access-from-coop-page-to-openee");
+ assert_equals(reports[0].body.openeeURL, openee_url);
+ assert_equals(reports[0].body.openerURL, undefined);
+ assert_equals(reports[0].body.otherDocumentURL, undefined);
+}, "Access from same-origin iframe")
+
+promise_test(async t => {
+ // This test window.
+ const this_window_token = token();
+
+ // The "opener" window, using COOP-Report-Only and a reporter.
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(token());
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "opener's iframe", same-origin with its parent.
+ const opener_iframe_token = token();
+ const opener_iframe_url = cross_site + executor_path + coep_header +
+ corp_header +
+ `&uuid=${opener_iframe_token}`;
+
+ // The "openee" window, NOT using COOP.
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + coep_header +
+ `&uuid=${openee_token}`;
+
+ // 1. Create the opener window.
+ window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close();"));
+
+ // 2. The opener opens an iframe, and install a ReportingObserver to catch
+ // future accesses.
+ send(opener_token, `
+ iframe = document.createElement("iframe");
+ iframe.src = "${opener_iframe_url}";
+ document.body.appendChild(iframe);
+
+ let observer = new ReportingObserver(reports => {
+ send("${this_window_token}", JSON.stringify(reports));
+ observer.disconnect();
+ });
+ observer.observe();
+ `);
+
+ // 3. The iframe opens the openee.
+ send(opener_iframe_token, `openee = window.open('${openee_url}');`);
+ t.add_cleanup(() => send(openee_token, `window.close();`));
+
+ // 4. Wait for the openee to load its document.
+ send(openee_token, `send("${this_window_token}", "Ready");`);
+ assert_equals(await receive(this_window_token), "Ready");
+
+ // 5. The opener's iframe tries to access the openee. This is an
+ // "access-from-coop-page" from a cross-site iframe. The ReportingObservers
+ // from the main document aren't notified.
+ send(opener_iframe_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js", `tryAccess(openee);`));
+
+ let reports = await receive(this_window_token, 2000);
+ assert_equals(reports, "timeout", "Unexpected report received.");
+}, "Access from cross-site iframe")
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html
new file mode 100644
index 0000000000..7bfdab1330
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html
@@ -0,0 +1,86 @@
+
+<meta name=timeout content=long>
+<title>A test with both COOP and COOP report only setup using Reporting-Endpoints header</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=2aee31d2-cd11-43bd-b34d-5f081ca3b2b4&report_only_id=d18f1779-e2ab-4a7a-8b1c-44e3a6f440f5"></script>
+
+<script>
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a cross-origin popup with both normal and report-only COOP. Four
+ // reports are sent.
+ [
+ CROSS_ORIGIN,
+ `same-origin-allow-popups; report-to="${popupReportEndpoint.name}"`,
+ "require-corp",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "require-corp",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": reportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin-plus-coep",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin-plus-coep",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ]
+];
+
+runNavigationDocumentReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html.sub.headers
new file mode 100644
index 0000000000..de48445f38
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-only-four-reports.https.html.sub.headers
@@ -0,0 +1,6 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups; report-to="coop-report-endpoint"
+Cross-Origin-Opener-Policy-Report-Only: same-origin; report-to="coop-report-only-endpoint"
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Embedder-Policy-Report-Only: require-corp
+Referrer-Policy: origin
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=2aee31d2-cd11-43bd-b34d-5f081ca3b2b4", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=d18f1779-e2ab-4a7a-8b1c-44e3a6f440f5"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-to-both_coop-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-to-both_coop-ro.https.html
new file mode 100644
index 0000000000..409628c15c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/report-to-both_coop-ro.https.html
@@ -0,0 +1,124 @@
+<title>
+ Both the openee and the opener have a COOP reporter. The report are sent to
+ both side.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const origin_opener = get_host_info().HTTPS_ORIGIN;
+const origin_openee = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+let escapeComma = url => url.replace(/,/g, '\\,');
+
+let genericSetup = async function(test) {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP and a reporter.
+ const opener_token = token();
+ const opener_report_token = reportToken();
+ const opener_reporting = reportingEndpointsHeaders(opener_report_token);
+ const opener_url = origin_opener+ executor_path + opener_reporting.header +
+ opener_reporting.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. This has COOP and a reporter.
+ const openee_token = token();
+ const openee_report_token = reportToken();
+ const openee_reporting = reportingEndpointsHeaders(openee_report_token);
+ const openee_url = origin_openee + executor_path + openee_reporting.header +
+ openee_reporting.coopReportOnlySameOriginHeader + coep_header +
+ `&uuid=${openee_token}`;
+
+ // Cleanup at the end of the test.
+ test.add_cleanup(() => {
+ send(openee_token, 'window.close()');
+ send(opener_token, 'window.close()');
+ });
+
+ // 1. Spawn the opener and the openee windows.
+ window.open(opener_url);
+ send(opener_token, `
+ openee = window.open('${escapeComma(openee_url)}');
+ `);
+
+ // 2. Wait for both to be loaded.
+ send(openee_token, `send('${this_window_token}', 'ACK');`);
+ assert_equals(await receive(this_window_token), 'ACK');
+
+ return [
+ this_window_token,
+ opener_token, opener_report_token, opener_url,
+ openee_token, openee_report_token, openee_url,
+ ];
+}
+
+let assert_generic_coop_report = function(report) {
+ assert_equals(report.type, "coop");
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "same-origin-plus-coep");
+ assert_equals(report.body.property, "blur");
+}
+
+promise_test(async test => {
+ let [
+ this_window_token,
+ opener_token, opener_report_token, opener_url,
+ openee_token, openee_report_token, openee_url,
+ ] = await genericSetup(test);
+
+ send(opener_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(openee);")
+ );
+
+ let report_opener =
+ await receiveReport(opener_report_token, "access-from-coop-page-to-openee")
+ let report_openee =
+ await receiveReport(openee_report_token, "access-to-coop-page-from-opener")
+
+ assert_generic_coop_report(report_openee);
+ assert_generic_coop_report(report_opener);
+
+ assert_equals(report_opener.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report_openee.url, openee_url.replace(/"/g, '%22'));
+ assert_source_location_found(report_opener);
+ assert_source_location_missing(report_openee);
+}, "Access from opener")
+
+promise_test(async test => {
+ let [
+ this_window_token,
+ opener_token, opener_report_token, opener_url,
+ openee_token, openee_report_token, openee_url,
+ ] = await genericSetup(test);
+
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener);")
+ );
+
+ let report_opener =
+ await receiveReport(opener_report_token, "access-to-coop-page-from-openee")
+ let report_openee =
+ await receiveReport(openee_report_token, "access-from-coop-page-to-opener")
+
+ assert_generic_coop_report(report_openee);
+ assert_generic_coop_report(report_opener);
+
+ assert_equals(report_opener.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report_openee.url, openee_url.replace(/"/g, '%22'));
+ assert_source_location_missing(report_opener);
+ assert_source_location_found(report_openee);
+}, "Access from openee")
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-same-origin-allow-popups.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-same-origin-allow-popups.https.html
new file mode 100644
index 0000000000..b2ff818d56
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-same-origin-allow-popups.https.html
@@ -0,0 +1,111 @@
+<title>
+ Tests the redirect interaction with COOP same-origin-allow-popups.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script>
+
+const same_origin = {
+ host: get_host_info().HTTPS_ORIGIN,
+ name: "Same origin"
+};
+const cross_origin = {
+ host: get_host_info().HTTPS_REMOTE_ORIGIN,
+ name: "Cross origin"
+};
+
+// Tests the redirect interaction with COOP same-origin-allow-popups and
+// reporting:
+// 1 - open the opener document on origin same_origin wit COOP
+// same-origin-allow-popups.
+// 2 - opener opens popup with document on origin popup_origin, no COOP and a
+// redirect header (HTTP 302, location).
+// 3 - redirection to a document with origin same_origin and COOP
+// same-origin-allow-popups.
+//
+// The navigation (2) to the first document of the popup stays in the same
+// browsing context group due to the same-origin-allow-popups COOP of the
+// opener.
+// The redirect (3) to the final document does since it compares the
+// popup_origin/unsafe-none document with the
+// same-origin/same-origin-allow-popups document.
+//
+// A opens B, B redirects to C.
+//
+// Document Origin COOP
+// -------- ------------ ------------------------
+// A same-origin same-origin-allow-popups
+// B popup-origin unsafe-none
+// C same-origin same-origin-allow-popups
+function redirect_test(popup_origin) {
+ promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP same-origin-allow-popups and a
+ // reporter.
+ const opener_token = token();
+ const opener_report_token = reportToken();
+ const opener_reporting = reportingEndpointsHeaders(opener_report_token);
+ const opener_url = same_origin.host + executor_path +
+ opener_reporting.header + opener_reporting.coopSameOriginAllowPopupsHeader +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window.
+ // The initial document does not have COOP and is on popup_origin, it
+ // redirects to a same-origin (with the opener) document with COOP
+ // same-origin-allow-popups.
+ const openee_token = token();
+ const openee_redirect_url = same_origin.host + executor_path +
+ opener_reporting.header + opener_reporting.coopSameOriginAllowPopupsHeader +
+ `&uuid=${openee_token}`;
+ const redirect_header = 'status(302)' +
+ `|header(Location,${encodeURIComponent(
+ openee_redirect_url
+ .replace(/,/g, "\\,")
+ .replace(/\\\\,/g, "\\\\\\,")
+ .replace(/\(/g, "%28")
+ .replace(/\)/g, "%29"))})`;
+ const openee_url = popup_origin.host + executor_path + redirect_header +
+ `&uuid=${openee_token}`;
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens its openee.
+ send(opener_token, `
+ openee = window.open("${openee_url}");
+ `);
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. Check the opener status on the openee.
+ send(openee_token, `
+ send("${this_window_token}", opener !== null);
+ `);
+ assert_equals(await receive(this_window_token), "false", "opener");
+
+ // 4. Check the openee status on the opener.
+ send(opener_token, `
+ send("${this_window_token}", openee.closed);
+ `);
+ assert_equals(await receive(this_window_token), "true", "openee.closed");
+
+ // 5. Check a report sent to the openee.
+ let report = await receiveReport(
+ opener_report_token,
+ "navigation-to-response");
+ assert_equals(report.type, "coop");
+ assert_equals(report.body.disposition, "enforce");
+ assert_equals(report.body.effectivePolicy, "same-origin-allow-popups");
+ }, `${popup_origin.name} openee redirected to same-origin with same-origin-allow-popups`);
+}
+
+redirect_test(same_origin);
+redirect_test(cross_origin);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-unsafe-none.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-unsafe-none.https.html
new file mode 100644
index 0000000000..bd89856305
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/document-reporting/reporting-redirect-with-unsafe-none.https.html
@@ -0,0 +1,130 @@
+<title>
+ Tests the redirect interaction with COOP unsafe-none.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script>
+
+const same_origin = {
+ host: get_host_info().HTTPS_ORIGIN,
+ name: "Same origin"
+};
+const cross_origin = {
+ host: get_host_info().HTTPS_REMOTE_ORIGIN,
+ name: "Cross origin"
+};
+
+// Repeated call receive() to fetch all reports received within 1 second.
+async function fetchReportsByID(uuid){
+ let timeStart = new Date().getTime();
+ const reports = [];
+ while(new Date().getTime() - timeStart < 1000) {
+ // Promise.race is used to timeout since receive() has no timeout mechanism.
+ reports.push(...await Promise.race([
+ receive(uuid).then(JSON.parse),
+ new Promise(resolve => step_timeout(resolve, 1000, []))
+ ]));
+ }
+ return reports;
+}
+
+function fetchReportByType(reports, type){
+ return reports.filter((report)=> (report.body.type === type));
+}
+
+ // Tests the redirect interaction with COOP unsafe-none and reporting:
+ // 1 - open the opener document on origin same_origin with COOP
+ // unsafe-none.
+ // 2 - opener opens popup with document on origin popup_origin, with COOP
+ // same-origin, Reporting-Endpoints header and a redirect header
+ // (HTTP 302, location).
+ // 3 - redirection to a document with origin same-origin and COOP
+ // unsafe-none.
+ //
+ // Navigation 2) should generate a report sent to B's reporter(navigation-to).
+ // Navigation 3) should generate a report sent to B's reporter(navigation-from).
+ //
+ // A opens B, B redirects to C.
+ //
+ // Document Origin COOP
+ // -------- ------------ ------------------------
+ // A same-origin unsafe-none
+ // B popup-origin same-origin
+ // C same-origin unsafe-none
+function redirect_test(popup_origin) {
+ promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP unsafe-none and no reporter.
+ const opener_token = token();
+ const opener_url = same_origin.host + executor_path +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window.
+ // The initial document have COOP, reporter and is on popup_origin, it
+ // redirects to a same-origin (with the opener) document with no COOP.
+ const openee_token = token();
+ const openee_report_token = reportToken();
+ const openee_reporting = reportingEndpointsHeaders(openee_report_token);
+ const openee_redirect_url = same_origin.host + executor_path +
+ `&uuid=${openee_token}`;
+ const redirect_header = '|status(302)' +
+ `|header(Location,${encodeURIComponent(
+ openee_redirect_url)})`;
+ const openee_url = (popup_origin.host + executor_path
+ + openee_reporting.header + openee_reporting.coopSameOriginHeader
+ + redirect_header + `&uuid=${openee_token}`)
+ .replace(/,/g, "\\,")
+ .replace(/\\\\,/g, "\\\\\\,")
+ .replace(/\(/g, "%28")
+ .replace(/\)/g, "%29");
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens its openee.
+ send(opener_token, `
+ openee = window.open(\`${openee_url}\`);
+ `);
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. Check the opener status on the openee.
+ send(openee_token, `
+ send("${this_window_token}", opener !== null);
+ `);
+ assert_equals(await receive(this_window_token), "false", "opener");
+
+ // 4. Check the openee status on the opener.
+ send(opener_token, `
+ send("${this_window_token}", openee.closed);
+ `);
+ assert_equals(await receive(this_window_token), "true", "openee.closed");
+
+ // 5. Check a report sent to B's reporting endpoint when A opens B.
+ const reports = await fetchReportsByID(openee_report_token);
+ const navigationToReport = fetchReportByType(
+ reports, "navigation-to-response");
+ assert_equals(navigationToReport.length, 1);
+ assert_equals(navigationToReport[0].type, "coop");
+ assert_equals(navigationToReport[0].body.disposition, "enforce");
+ assert_equals(navigationToReport[0].body.effectivePolicy, "same-origin");
+ // 6. Check a report sent to B's reporting endpoint when B redirects to C.
+ const navigationFromReport = fetchReportByType(
+ reports, "navigation-from-response");
+ assert_equals(navigationFromReport.length, 1);
+ assert_equals(navigationFromReport[0].type, "coop");
+ assert_equals(navigationFromReport[0].body.disposition, "enforce");
+ assert_equals(navigationFromReport[0].body.effectivePolicy, "same-origin");
+ }, `${popup_origin.name} openee redirected to same-origin with unsafe-none`);
+}
+
+redirect_test(same_origin);
+redirect_test(cross_origin);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html
new file mode 100644
index 0000000000..ca1471ccc0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html
@@ -0,0 +1,86 @@
+<meta name=timeout content=long>
+<title>A test with both COOP and COOP report only setup</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=47b45e17-51c5-4691-bdd5-8f343bbfcf42&report_only_id=3eb3ad1d-872e-4ea8-8b40-0e98783a0683"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a cross-origin popup with both normal and report-only COOP. Four
+ // reports are sent.
+ [
+ CROSS_ORIGIN,
+ `same-origin-allow-popups; report-to="${popupReportEndpoint.name}"`,
+ "require-corp",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "require-corp",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": reportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin-plus-coep",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin-plus-coep",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ]
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html.sub.headers
new file mode 100644
index 0000000000..50c3045bb6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-four-reports.https.html.sub.headers
@@ -0,0 +1,6 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups; report-to="coop-report-endpoint"
+Cross-Origin-Opener-Policy-Report-Only: same-origin; report-to="coop-report-only-endpoint"
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Embedder-Policy-Report-Only: require-corp
+Referrer-Policy: origin
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=47b45e17-51c5-4691-bdd5-8f343bbfcf42", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=3eb3ad1d-872e-4ea8-8b40-0e98783a0683" \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html
new file mode 100644
index 0000000000..cca2e7e1ae
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html
@@ -0,0 +1,71 @@
+
+<meta name=timeout content=long>
+<title>Report only tests for an opener without any COOP/COOP report only set</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a same-origin popup with a same-origin COOP report-only value, which
+ // would cause a browsing context group swap, hence a report is sent.
+ [
+ SAME_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ [
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": `${location.href}`, // previous documnent url
+ "referrer": `${location.origin}/`, // referrer (origin, as dictated by the referrer policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with a same-origin COOP report-only value, which
+ // would cause a browsing context group swap, hence a report is sent.
+ [
+ CROSS_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ [
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer (origin, as dictated by the referrer policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html.headers
new file mode 100644
index 0000000000..5b29739bbd
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-from-unsafe-none.https.html.headers
@@ -0,0 +1 @@
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html
new file mode 100644
index 0000000000..52b1f2a09f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html
@@ -0,0 +1,96 @@
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=380ca360-d1ae-4329-b1dd-69cea49cd705&report_only_id=cf9ac91d-6c5d-4489-a420-10be9402ef84"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a cross-origin popup without any COOP setup, the current document
+ // (opener) report-only would cause a browsing context group swap, hence a
+ // report is sent to the corresponding endpoint.
+ [
+ CROSS_ORIGIN,
+ "",
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ ]
+ ],
+ // Open a cross-origin popup with a same-origin COOP report-only value, which
+ // would cause a browsing context group swap, hence a report is sent to both
+ // endpoints.
+ [
+ CROSS_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ [
+ {
+ "endpoint": reportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with a same-origin COOP report-only value, the two
+ // COOP-report-only values match, hence no virtual browsing context group swap
+ // happens and no report is sent.
+ [
+ SAME_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html.sub.headers
new file mode 100644
index 0000000000..04bc49906b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html.sub.headers
@@ -0,0 +1,3 @@
+Cross-Origin-Opener-Policy-Report-Only: same-origin; report-to="coop-report-only-endpoint"
+Referrer-Policy: origin
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=380ca360-d1ae-4329-b1dd-69cea49cd705", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=cf9ac91d-6c5d-4489-a420-10be9402ef84"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html
new file mode 100644
index 0000000000..148c700ee5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html
@@ -0,0 +1,32 @@
+
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a cross-origin popup with COOP report-only with coep, which mismatches
+ // with the current document (opener) COOP (unsafe-none) and COOP report-only
+ // (same-origin) values.
+ [
+ SAME_ORIGIN,
+ "",
+ "require-corp",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html.headers
new file mode 100644
index 0000000000..58ab03394a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep-report-only.https.html.headers
@@ -0,0 +1,3 @@
+Cross-Origin-Opener-Policy-Report-Only: same-origin
+Cross-Origin-Embedder-Policy-Report-Only: require-corp
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html
new file mode 100644
index 0000000000..148c700ee5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html
@@ -0,0 +1,32 @@
+
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a cross-origin popup with COOP report-only with coep, which mismatches
+ // with the current document (opener) COOP (unsafe-none) and COOP report-only
+ // (same-origin) values.
+ [
+ SAME_ORIGIN,
+ "",
+ "require-corp",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html.headers
new file mode 100644
index 0000000000..2ba7ffb592
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-with-coep.https.html.headers
@@ -0,0 +1,3 @@
+Cross-Origin-Opener-Policy-Report-Only: same-origin
+Cross-Origin-Embedder-Policy: require-corp
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html
new file mode 100644
index 0000000000..8a63682c69
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html
@@ -0,0 +1,73 @@
+
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a cross-origin popup with COOP report-only with coep, which mismatches
+ // with the current document (opener) COOP (unsafe-none) and COOP report-only
+ // (same-origin) values.
+ [
+ SAME_ORIGIN,
+ "",
+ "require-corp",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ [
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin-plus-coep",
+ "previousResponseURL": `${location.href}`,
+ "referrer": `${location.origin}/`,
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with COOP report-only with coep report-only,
+ // which mismatches with the current document (opener) COOP (unsafe-none) and
+ // COOP report-only (same-origin) values.
+ [
+ SAME_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "require-corp",
+ [
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin-plus-coep",
+ "previousResponseURL": `${location.href}`,
+ "referrer": `${location.origin}/`,
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html.headers
new file mode 100644
index 0000000000..9a8445a43e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy-Report-Only: same-origin
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-opener.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-opener.https.html
new file mode 100644
index 0000000000..893dfa20b8
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-opener.https.html
@@ -0,0 +1,67 @@
+<title>
+ Reports a browsing context group switch when an opener with COOP navigates.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+let escapeComma = url => url.replace(/,/g, '\\,');
+
+promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window.
+ const opener_token = token();
+ const opener_url = same_origin + executor_path + `&uuid=${opener_token}`;
+
+ // The "openee" window.
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + `&uuid=${openee_token}`;
+
+ // The "final" url the opener will navigate to. It has COOP and a reporter.
+ const final_report_token = reportToken();
+ const final_token = token();
+ const final_reportTo = reportingEndpointsHeaders(final_report_token);
+ const final_url = same_origin + executor_path + final_reportTo.header +
+ final_reportTo.coopSameOriginHeader +`&uuid=${final_token}`;
+
+ // 1. Create the opener window and ensure it doesn't have an opener.
+ let opener_window_proxy = window.open(opener_url, '_blank', 'noopener');
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens a window.
+ send(opener_token, `
+ openee = window.open('${escapeComma(openee_url)}');
+ `);
+
+ // 3. Ensure the openee loads.
+ send(openee_token, `
+ send("${this_window_token}", "ACK");
+ `);
+ assert_equals("ACK", await receive(this_window_token));
+
+ // 4. The opener navigates.
+ send(opener_token, `
+ location.replace('${escapeComma(final_url)}');
+ `);
+
+ // 5. Check a report was sent to the opener.
+ let report =
+ await receiveReport(final_report_token, "navigation-to-response")
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, final_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "enforce");
+ assert_equals(report.body.effectivePolicy, "same-origin");
+ assert_equals(report.body.previousResponseURL, opener_url.replace(/"/g, '%22'));
+}, "navigation-report-from-opener-navigation");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html
new file mode 100644
index 0000000000..b625b285cf
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html
@@ -0,0 +1,85 @@
+<title>Cross-Origin-Opener-Policy: a navigated popup with reporting</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=df3cde15-b00b-4a59-b6e2-498b67a6146e&report_only_id=ebf3a415-7a74-42e1-92d1-e600b1bbe22e"></script>
+
+<script>
+
+// This test does the following:
+// 1 - This document has COOP: same-origin-allow-popups; report-to="coop-report-endpoint"
+// 2 - Open a popup on a same-origin page without COOP, with the coop-popup-report-endpoint
+// 3 - Navigate the popup to a same-origin page with COOP, with the coop-redirect-report-endpoint
+// it verifies that the reports are properly send for the browsing context switch
+// during the navigation in the popup (step 3). The current document (the opener)
+// endpoint should not receive any report as no switch ocurred on 2.
+promise_test( async t => {
+ const callbackToken = token();
+ const noCoopToken = token();
+ const coopToken= token();
+ await reportingTest(async resolve => {
+ const noCOOPUrl = executor_path +
+ convertToWPTHeaderPipe(getReportingEndpointsHeader(location.origin)) +
+ `|header(Cross-Origin-Opener-Policy,${encodeURIComponent(`unsafe-none; report-to="${popupReportEndpoint.name}"`)})` +
+ `&uuid=${noCoopToken}`;
+ const coopUrl = executor_path +
+ convertToWPTHeaderPipe(getReportingEndpointsHeader(location.origin)) +
+ `|header(Cross-Origin-Opener-Policy,${encodeURIComponent(`same-origin; report-to="${redirectReportEndpoint.name}"`)})` +
+ `&uuid=${coopToken}`;
+
+ // 1. Open a popup without COOP and with reporting. COOP does not trigger
+ // a browsing context group switch because the current document is
+ // same-origin-allow-popups
+ const popup = window.open(noCOOPUrl);
+ t.add_cleanup(() => send(noCoopToken, "window.close()"));
+
+ // 2. Navigate the popup to a COOP document, which switches the browsing
+ // context group.
+ send(noCoopToken, `window.location = "${coopUrl}";`);
+ t.add_cleanup(() => send(coopToken, "window.close()"));
+
+ // 3. Make sure the new document is loaded.
+ send(coopToken, `
+ send("${callbackToken}", "Ready");
+ `);
+ let reply = await receive(callbackToken);
+ resolve();
+ },
+ "", // executor token for the report replacements, unused in this test
+ [
+ // Reports expected for the navigation from "noCOOP" to "coop"
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "unsafe-none",
+ "nextResponseURL": RegExp(`uuid=${coopToken}$`),
+ "type": "navigation-from-response"
+ },
+ "url": RegExp(`uuid=${noCoopToken}$`),
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": redirectReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": RegExp(`uuid=${noCoopToken}$`),
+ "referrer": RegExp(`uuid=${noCoopToken}$`),
+ "type": "navigation-to-response"
+ },
+ "url": RegExp(`uuid=${coopToken}$`),
+ "type": "coop"
+ }
+ },
+ ]);
+}, "Open a popup to a document without COOP, then navigate it to a document with");
+
+verifyRemainingReports();
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html.sub.headers
new file mode 100644
index 0000000000..a6a27c2d3e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-popup.https.html.sub.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups; report-to="coop-report-endpoint"
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=df3cde15-b00b-4a59-b6e2-498b67a6146e", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=ebf3a415-7a74-42e1-92d1-e600b1bbe22e"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html
new file mode 100644
index 0000000000..d674e2e449
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html
@@ -0,0 +1,126 @@
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=6a739c25-0ec5-4832-b4a3-847281006857&report_only_id=f91209ee-b3a3-474b-b337-d663533745fb"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report only, popup COEP report only, expected reports
+
+ // Open a same-origin popup with a same-origin COOP and no COEP. Produces two
+ // reports (one from and one to). Both pages being same origin, the
+ // next/pervious document urls are available.
+ [
+ SAME_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": `${location.href}`, // previous documnent url
+ "referrer": `${location.origin}/`, // referrer (origin, as dictated by the referrer policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with a same-origin-allow-popup COOP and noCOEP.
+ // Produces two reports (one from and one to). Both pages being cross origin,
+ // the next/pervious document urls are not available and the initial document
+ // url/referrer are used instead.
+ [
+ CROSS_ORIGIN,
+ `same-origin-allow-popups; report-to="${popupReportEndpoint.name}"`,
+ "require-corp",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "previousResponseURL": ``,
+ "referrer": `${location.origin}/`, // referrer (origin, as dictated by the referrer policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with a same-origin COOP and COEP, and no reporting.
+ // Produces one navigation-from-report for this document (the opener). The
+ // pages being cross origin, the next/pervious document urls are not available
+ // and the initial document url/referrer are used instead.
+ [
+ CROSS_ORIGIN,
+ `same-origin`,
+ "require-corp",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-allow-popups",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // initial navigation URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html.sub.headers
new file mode 100644
index 0000000000..3e213a95a3
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-allow-popups-report-to.https.html.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=6a739c25-0ec5-4832-b4a3-847281006857", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=f91209ee-b3a3-474b-b337-d663533745fb"
+Cross-Origin-Opener-Policy: same-origin-allow-popups; report-to="coop-report-endpoint"
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html
new file mode 100644
index 0000000000..88b180702f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html
@@ -0,0 +1,173 @@
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=edbbace3-40ca-4640-8d50-dc6e52acc1da&report_only_id=f65cf51a-ca6f-4028-a2c3-0c06183faa13"></script>
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report only, popup COEP report only, expected reports
+
+ // Open and navigate a popup to a same-origin page with the same COOP-COEP
+ // settings: no browsing context group switch hence no report expected.
+ [
+ SAME_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "require-corp",
+ "",
+ "",
+ []
+ ],
+ // Open a same-origin popup with a same-origin COOP but no COEP. Produces two
+ // reports (one from and one to). The from report has an effectivePolicy of
+ // same-origin-plus-coep, both pages being same origin, the entire
+ // next/pervious document urls are available.
+ [
+ SAME_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-plus-coep",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next destination url
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": `${location.href}`, // previous document url
+ "referrer": `${location.origin}/`, // referrer (origin, as dictated by the referrer policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with a same-origin COOP and COEP. Produces two
+ // reports (one from and one to). The from report has an effectivePolicy of
+ // same-origin-plus-coep, both pages being cross origin, the next/pervious
+ // document urls are not available and the initial document url/referrer are
+ // used instead.
+ [
+ CROSS_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "require-corp",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-plus-coep",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // initial navigation url
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-plus-coep",
+ "previousResponseURL": ``,
+ "referrer": `${location.origin}/`, // referrer (origin, as dictated by the referrer policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with a same-origin COOP report only. One report
+ // is sent to this page's endpoint, but none to the report-only endpoint.
+ [
+ SAME_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "require-corp",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-plus-coep",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // initial navigation url
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with a same-origin COOP report only. A report is
+ // sent to both this page's endpoint and the popup's.
+ [
+ CROSS_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "require-corp",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-plus-coep",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // initial navigation url
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportOnlyEndpoint,
+ "report": {
+ "body": {
+ "disposition": "reporting",
+ "effectivePolicy": "same-origin-plus-coep",
+ "previousResponseURL": ``,
+ "referrer": `${location.origin}/`, // referrer (origin, as dictated by the referrer policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html.sub.headers
new file mode 100644
index 0000000000..0f78bdb2d0
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-coep-report-to.https.html.sub.headers
@@ -0,0 +1,4 @@
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=edbbace3-40ca-4640-8d50-dc6e52acc1da", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=f65cf51a-ca6f-4028-a2c3-0c06183faa13"
+Cross-Origin-Opener-Policy: same-origin; report-to="coop-report-endpoint"
+Cross-Origin-Embedder-Policy: require-corp
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html
new file mode 100644
index 0000000000..47bb67cc4b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html
@@ -0,0 +1,216 @@
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=6aad9729-8642-4894-91d9-a4d44707cd4a&report_only_id=69eb1838-6a03-4cda-97b0-c126ffcb9e8a"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report only, popup COEP report only, expected reports
+
+ // Open a popup on a same-origin page, with a compatible COOP.
+ // This is a sanity check that no report are produced.
+ [
+ SAME_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ []
+ ],
+ // Open a cross-origin popup with a same-origin COOP. Produces two
+ // reports (one from and one to). The from report has an effectivePolicy of
+ // same-origin (corresponding to the current document), both pages being
+ // cross origin, the next/pervious document urls are not available and the
+ // initial document url/referrer are used instead.
+ [
+ CROSS_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/,
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": "",
+ "referrer": '', // referrer (empty due to the Referrer Policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with a unsafe-none COOP and no COEP. COOP switches
+ // the browsing context group and hence produces two reports (one from and one
+ // to). This test verifies that unsafe-none properly sends report.
+ [
+ SAME_ORIGIN,
+ `unsafe-none; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/,
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "unsafe-none",
+ "previousResponseURL": `${location.href}`,
+ "referrer": '', // referrer (empty due to the Referrer Policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with a same-origin COOP and COEP. The difference
+ // of COEP values leads to the browsing context group switch and produces two
+ // reports. This verifies that the navigation-to-document report has an
+ // effectivePolicy of same-origin-plus-coep.
+ [
+ SAME_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "require-corp",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/,
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin-plus-coep",
+ "previousResponseURL": `${location.href}`,
+ "referrer": '', // referrer (empty due to the Referrer Policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with no COOP (but reporting) and no COEP.
+ // Produces two reports. The pages being cross origin, the next/pervious
+ // document urls are not available and the initial document url/referrer are
+ // used instead.
+ [
+ CROSS_ORIGIN,
+ `unsafe-none; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/,
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "unsafe-none",
+ "previousResponseURL": "",
+ "referrer": '', // referrer (empty due to the Referrer Policy)
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with no COOP (without reporting) and no COEP.
+ // Produces one report to this page (opener) endpoint.
+ // This verifies that the navigated-to-document's COOP report values do not
+ // impact the navigated-from-document's COOP.
+ [
+ SAME_ORIGIN,
+ "unsafe-none",
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/,
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ }
+ ]
+ ]
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html.sub.headers
new file mode 100644
index 0000000000..79c851a86c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin-report-to.https.html.sub.headers
@@ -0,0 +1,3 @@
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=6aad9729-8642-4894-91d9-a4d44707cd4a", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=69eb1838-6a03-4cda-97b0-c126ffcb9e8a"
+Cross-Origin-Opener-Policy: same-origin; report-to="coop-report-endpoint"
+Referrer-Policy: no-referrer
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html
new file mode 100644
index 0000000000..3a8f343f37
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html
@@ -0,0 +1,110 @@
+<meta name=timeout content=long>
+<title>reporting same origin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report only, popup COEP report only, expected reports
+
+ // Open a cross-origin popup with a same-origin COOP and no COEP. COOP
+ // switches the browsing context group and hence produces one report.
+ // This test verifies that the navigated to document properly sends a
+ // navigation-to report. The navigationURI is the referrer.
+ [
+ CROSS_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with a unsafe-none COOP and no COEP. COOP switches
+ // the browsing context group and hence produces one report.
+ // This test verifies that having different policies on same origin documents
+ // still properly produces report to the navigated-to-document.
+ [
+ SAME_ORIGIN,
+ `unsafe-none; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "unsafe-none",
+ "previousResponseURL": `${location.href}`,
+ "referrer": `${location.href}`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with a unsafe-none COOP (with reporting) and no
+ // COEP. COOP switches the browsing context group and hence produces one
+ // reports to the unsafe-none document. This test verifies that unsafe-none
+ // properly sends report in that configuration.
+ [
+ CROSS_ORIGIN,
+ `unsafe-none; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "previousResponseURL": "",
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with a same-origin COOP Report only value, the
+ // report only matches the previous document COOP value, no report is sent.
+ [
+ SAME_ORIGIN,
+ "",
+ "",
+ `same-origin; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html
new file mode 100644
index 0000000000..2563dbb01f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html
@@ -0,0 +1,126 @@
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=1f79b0d5-c2a2-4e0b-8e8c-651af2321964&report_only_id=c50700c8-db1e-4224-b06f-4c6a95a5f4be"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report only, popup COEP report only, expected reports
+
+ // Open a same-origin popup with a same-origin COOP with reporting and no COEP.
+ // COOP switches the browsing context group and hence produces two reports
+ // (one from and one to). This test verifies that unsafe-none (from the opener)
+ // properly sends a report.
+ [
+ SAME_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "unsafe-none",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": `${location.href}`, // previous document url
+ "referrer": `${location.href}`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a same-origin popup with a same-origin COOP (no reporting)and no COEP.
+ // COOP switches the browsing context group and hence produces one report for
+ // the navigated from document (this page, the opener). This test differs with
+ // the previous one as it assert that the navigated to document's COOP reporting
+ // values do not interfere.
+ [
+ SAME_ORIGIN,
+ `same-origin`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "unsafe-none",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ }
+ ]
+ ],
+ // Open a cross-origin popup with a same-origin COOP and no COEP. COOP switches
+ // the browsing context group and hence produces two reports.
+ [
+ CROSS_ORIGIN,
+ `same-origin; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ [
+ {
+ "endpoint": reportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "unsafe-none",
+ "nextResponseURL": /uuid=EXECUTOR_UUID$/, // next document URL
+ "type": "navigation-from-response"
+ },
+ "url": `${location.href}`,
+ "type": "coop"
+ }
+ },
+ {
+ "endpoint": popupReportEndpoint,
+ "report": {
+ "body": {
+ "disposition": "enforce",
+ "effectivePolicy": "same-origin",
+ "previousResponseURL": ``,
+ "referrer": `${location.origin}/`, // referrer
+ "type": "navigation-to-response"
+ },
+ "url": /uuid=EXECUTOR_UUID$/,
+ "type": "coop"
+ }
+ }
+ ]
+ ]
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html.sub.headers
new file mode 100644
index 0000000000..f1f18d6708
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-popup-unsafe-none-report-to.https.html.sub.headers
@@ -0,0 +1,2 @@
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=1f79b0d5-c2a2-4e0b-8e8c-651af2321964", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=c50700c8-db1e-4224-b06f-4c6a95a5f4be"
+Cross-Origin-Opener-Policy: unsafe-none; report-to="coop-report-endpoint"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html
new file mode 100644
index 0000000000..cd2f6b67b3
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html
@@ -0,0 +1,111 @@
+<title>
+ Tests the redirect interaction with COOP same-origin-allow-popups.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = {
+ host: get_host_info().HTTPS_ORIGIN,
+ name: "Same origin"
+};
+const cross_origin = {
+ host: get_host_info().HTTPS_REMOTE_ORIGIN,
+ name: "Cross origin"
+};
+
+// Tests the redirect interaction with COOP same-origin-allow-popups and
+// reporting:
+// 1 - open the opener document on origin same_origin with COOP
+// same-origin-allow-popups.
+// 2 - opener opens popup with document on origin popup_origin, no COOP and a
+// redirect header (HTTP 302, location).
+// 3 - redirection to a document with origin same_origin and COOP
+// same-origin-allow-popups.
+//
+// The navigation (2) to the first document of the popup stays in the same
+// browsing context group due to the same-origin-allow-popups COOP of the
+// opener.
+// The redirect (3) to the final document does since it compares the
+// popup_origin/unsafe-none document with the
+// same-origin/same-origin-allow-popups document.
+//
+// A opens B, B redirects to C.
+//
+// Document Origin COOP
+// -------- ------------ ------------------------
+// A same-origin same-origin-allow-popups
+// B popup-origin unsafe-none
+// C same-origin same-origin-allow-popups
+function redirect_test( popup_origin ) {
+ promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP same-origin-allow-popups and a
+ // reporter.
+ const opener_report_token = reportToken();
+ const opener_token = token();
+ const opener_reportTo = reportingEndpointsHeaders(opener_report_token);
+ const opener_url = same_origin.host + executor_path +
+ opener_reportTo.header + opener_reportTo.coopSameOriginAllowPopupsHeader +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window.
+ // The initial document does not have COOP and is on popup_origin, it
+ // redirects to a same-origin (with the opener) document with COOP
+ // same-origin-allow-popups.
+ const openee_token = token();
+ const openee_redirect_url = same_origin.host + executor_path +
+ opener_reportTo.header + opener_reportTo.coopSameOriginAllowPopupsHeader +
+ `&uuid=${openee_token}`;
+ const redirect_header = 'status(302)' +
+ `|header(Location,${encodeURIComponent(
+ openee_redirect_url
+ .replace(/,/g, "\\,")
+ .replace(/\\\\,/g, "\\\\\\,")
+ .replace(/\(/g, "%28")
+ .replace(/\)/g, "%29"))})`;
+ const openee_url = popup_origin.host + executor_path + redirect_header +
+ `&uuid=${openee_token}`;
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens its openee.
+ send(opener_token, `
+ openee = window.open("${openee_url}");
+ `);
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. Check the opener status on the openee.
+ send(openee_token, `
+ send("${this_window_token}", opener !== null);
+ `);
+ assert_equals(await receive(this_window_token), "false", "opener");
+
+ // 4. Check the openee status on the opener.
+ send(opener_token, `
+ send("${this_window_token}", openee.closed);
+ `);
+ assert_equals(await receive(this_window_token), "true", "openee.closed");
+
+ // 5. Check a report sent to the openee.
+ let report = await receiveReport(
+ opener_report_token,
+ "navigation-to-response");
+ assert_equals(report.type, "coop");
+ assert_equals(report.body.disposition, "enforce");
+ assert_equals(report.body.effectivePolicy, "same-origin-allow-popups");
+ }, `${popup_origin.name} openee redirected to same-origin with same-origin-allow-popups`);
+}
+
+redirect_test(same_origin);
+redirect_test(cross_origin);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/reporting-common.js b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/reporting-common.js
new file mode 100644
index 0000000000..70bb4897f5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/reporting-common.js
@@ -0,0 +1,422 @@
+const executor_path = "/common/dispatcher/executor.html?pipe=";
+const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+// Report endpoint keys must start with a lower case alphabet character.
+// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-header-structure-15#section-4.2.3.3
+const reportToken = () => {
+ return token().replace(/./, 'a');
+}
+
+const isWPTSubEnabled = "{{GET[pipe]}}".includes("sub");
+
+const getReportEndpointURL = (reportID) =>
+ `/reporting/resources/report.py?reportID=${reportID}`;
+
+const reportEndpoint = {
+ name: "coop-report-endpoint",
+ reportID: isWPTSubEnabled ? "{{GET[report_id]}}" : token(),
+ reports: []
+};
+const reportOnlyEndpoint = {
+ name: "coop-report-only-endpoint",
+ reportID: isWPTSubEnabled ? "{{GET[report_only_id]}}" : token(),
+ reports: []
+};
+const popupReportEndpoint = {
+ name: "coop-popup-report-endpoint",
+ reportID: token(),
+ reports: []
+};
+const popupReportOnlyEndpoint = {
+ name: "coop-popup-report-only-endpoint",
+ reportID: token(),
+ reports: []
+};
+const redirectReportEndpoint = {
+ name: "coop-redirect-report-endpoint",
+ reportID: token(),
+ reports: []
+};
+const redirectReportOnlyEndpoint = {
+ name: "coop-redirect-report-only-endpoint",
+ reportID: token(),
+ reports: []
+};
+
+const reportEndpoints = [
+ reportEndpoint,
+ reportOnlyEndpoint,
+ popupReportEndpoint,
+ popupReportOnlyEndpoint,
+ redirectReportEndpoint,
+ redirectReportOnlyEndpoint
+];
+
+// Allows RegExps to be pretty printed when printing unmatched expected reports.
+Object.defineProperty(RegExp.prototype, "toJSON", {
+ value: RegExp.prototype.toString
+});
+
+function wait(ms) {
+ return new Promise(resolve => step_timeout(resolve, ms));
+}
+
+// Check whether a |report| is a "opener breakage" COOP report.
+function isCoopOpenerBreakageReport(report) {
+ if (report.type != "coop")
+ return false;
+
+ if (report.body.type != "navigation-from-response" &&
+ report.body.type != "navigation-to-response") {
+ return false;
+ }
+
+ return true;
+}
+
+async function clearReportsOnServer(host) {
+ const res = await fetch(
+ '/reporting/resources/report.py', {
+ method: "POST",
+ body: JSON.stringify({
+ op: "DELETE",
+ reportIDs: reportEndpoints.map(endpoint => endpoint.reportID)
+ })
+ });
+ assert_equals(res.status, 200, "reports cleared");
+}
+
+async function pollReports(endpoint) {
+ const res = await fetch(getReportEndpointURL(endpoint.reportID),
+ { cache: 'no-store' });
+ if (res.status !== 200) {
+ return;
+ }
+ for (const report of await res.json()) {
+ if (isCoopOpenerBreakageReport(report))
+ endpoint.reports.push(report);
+ }
+}
+
+// Recursively check that all members of expectedReport are present or matched
+// in report.
+// Report may have members not explicitly expected by expectedReport.
+function isObjectAsExpected(report, expectedReport) {
+ if (( report === undefined || report === null
+ || expectedReport === undefined || expectedReport === null )
+ && report !== expectedReport ) {
+ return false;
+ }
+ if (expectedReport instanceof RegExp && typeof report === "string") {
+ return expectedReport.test(report);
+ }
+ // Perform this check now, as RegExp and strings above have different typeof.
+ if (typeof report !== typeof expectedReport)
+ return false;
+ if (typeof expectedReport === 'object') {
+ return Object.keys(expectedReport).every(key => {
+ return isObjectAsExpected(report[key], expectedReport[key]);
+ });
+ }
+ return report == expectedReport;
+}
+
+async function checkForExpectedReport(expectedReport) {
+ return new Promise( async (resolve, reject) => {
+ const polls = 20;
+ const waitTime = 200;
+ for (var i=0; i < polls; ++i) {
+ pollReports(expectedReport.endpoint);
+ for (var j=0; j<expectedReport.endpoint.reports.length; ++j){
+ if (isObjectAsExpected(expectedReport.endpoint.reports[j],
+ expectedReport.report)){
+ expectedReport.endpoint.reports.splice(j,1);
+ resolve();
+ return;
+ }
+ };
+ await wait(waitTime);
+ }
+ reject(
+ replaceTokensInReceivedReport(
+ "No report matched the expected report for endpoint: "
+ + expectedReport.endpoint.name
+ + ", expected report: " + JSON.stringify(expectedReport.report)
+ + ", within available reports: "
+ + JSON.stringify(expectedReport.endpoint.reports)
+ ));
+ });
+}
+
+function replaceFromRegexOrString(str, match, value) {
+ if (str instanceof RegExp) {
+ return RegExp(str.source.replace(match, value));
+ }
+ return str.replace(match, value);
+}
+
+// Replace generated values in regexes and strings of an expected report:
+// EXECUTOR_UUID: the uuid generated with token().
+function replaceValuesInExpectedReport(expectedReport, executorUuid) {
+ if (expectedReport.report.body !== undefined) {
+ if (expectedReport.report.body.nextResponseURL !== undefined) {
+ expectedReport.report.body.nextResponseURL = replaceFromRegexOrString(
+ expectedReport.report.body.nextResponseURL, "EXECUTOR_UUID",
+ executorUuid);
+ }
+ if (expectedReport.report.body.previousResponseURL !== undefined) {
+ expectedReport.report.body.previousResponseURL = replaceFromRegexOrString(
+ expectedReport.report.body.previousResponseURL, "EXECUTOR_UUID",
+ executorUuid);
+ }
+ if (expectedReport.report.body.referrer !== undefined) {
+ expectedReport.report.body.referrer = replaceFromRegexOrString(
+ expectedReport.report.body.referrer, "EXECUTOR_UUID",
+ executorUuid);
+ }
+ }
+ if (expectedReport.report.url !== undefined) {
+ expectedReport.report.url = replaceFromRegexOrString(
+ expectedReport.report.url, "EXECUTOR_UUID", executorUuid);
+ }
+ return expectedReport;
+}
+
+function replaceTokensInReceivedReport(str) {
+ return str.replace(/.{8}-.{4}-.{4}-.{4}-.{12}/g, `(uuid)`);
+}
+
+// Run a test then check that all expected reports are present.
+async function reportingTest(testFunction, executorToken, expectedReports) {
+ await new Promise(testFunction);
+ expectedReports = Array.from(
+ expectedReports,
+ report => replaceValuesInExpectedReport(report, executorToken) );
+ await Promise.all(Array.from(expectedReports, checkForExpectedReport));
+}
+
+function convertToWPTHeaderPipe([name, value]) {
+ return `header(${name}, ${encodeURIComponent(value)})`;
+}
+
+function getReportToHeader(host) {
+ return [
+ "Report-To",
+ reportEndpoints.map(
+ reportEndpoint => {
+ const reportToJSON = {
+ 'group': `${reportEndpoint.name}`,
+ 'max_age': 3600,
+ 'endpoints': [{
+ 'url': `${host}${getReportEndpointURL(reportEndpoint.reportID)}`
+ }]
+ };
+ // Escape comma as required by wpt pipes.
+ return JSON.stringify(reportToJSON)
+ .replace(/,/g, '\\,')
+ .replace(/\(/g, '\\\(')
+ .replace(/\)/g, '\\\)=');
+ }
+ ).join("\\, ")];
+}
+
+function getReportingEndpointsHeader(host) {
+ return [
+ "Reporting-Endpoints",
+ reportEndpoints.map(reportEndpoint => {
+ return `${reportEndpoint.name}="${host}${getReportEndpointURL(reportEndpoint.reportID)}"`;
+ }).join("\\, ")];
+}
+
+// Return Report and Report-Only policy headers.
+function getPolicyHeaders(coop, coep, coopRo, coepRo) {
+ return [
+ [`Cross-Origin-Opener-Policy`, coop],
+ [`Cross-Origin-Embedder-Policy`, coep],
+ [`Cross-Origin-Opener-Policy-Report-Only`, coopRo],
+ [`Cross-Origin-Embedder-Policy-Report-Only`, coepRo]];
+}
+
+function navigationReportingTest(testName, host, coop, coep, coopRo, coepRo,
+ expectedReports) {
+ const executorToken = token();
+ const callbackToken = token();
+ promise_test(async t => {
+ await reportingTest(async resolve => {
+ const openee_headers = [
+ getReportingEndpointsHeader(host.origin),
+ ...getPolicyHeaders(coop, coep, coopRo, coepRo)
+ ].map(convertToWPTHeaderPipe);
+ const openee_url = host.origin + executor_path +
+ openee_headers.join('|') + `&uuid=${executorToken}`;
+ const openee = window.open(openee_url);
+ const uuid = token();
+ t.add_cleanup(() => send(uuid, "window.close()"));
+
+ // 1. Make sure the new document is loaded.
+ send(executorToken, `
+ send("${callbackToken}", "Ready");
+ `);
+ let reply = await receive(callbackToken);
+ assert_equals(reply, "Ready");
+ resolve();
+ }, executorToken, expectedReports);
+ }, `coop reporting test ${testName} to ${host.name} with ${coop}, ${coep}, ${coopRo}, ${coepRo}`);
+}
+
+function navigationDocumentReportingTest(testName, host, coop, coep, coopRo,
+ coepRo, expectedReports) {
+ const executorToken = token();
+ const callbackToken = token();
+ promise_test(async t => {
+ const openee_headers = [
+ getReportingEndpointsHeader(host.origin),
+ ...getPolicyHeaders(coop, coep, coopRo, coepRo)
+ ].map(convertToWPTHeaderPipe);
+ const openee_url = host.origin + executor_path +
+ openee_headers.join('|') + `&uuid=${executorToken}`;
+ window.open(openee_url);
+ t.add_cleanup(() => send(executorToken, "window.close()"));
+ // Have openee window send a message through dispatcher, once we receive
+ // the Ready message from dispatcher it means the openee is fully loaded.
+ send(executorToken, `
+ send("${callbackToken}", "Ready");
+ `);
+ let reply = await receive(callbackToken);
+ assert_equals(reply, "Ready");
+
+ await wait(1000);
+
+ expectedReports = expectedReports.map(
+ (report) => replaceValuesInExpectedReport(report, executorToken));
+ return Promise.all(expectedReports.map(
+ async ({ endpoint, report: expectedReport }) => {
+ await pollReports(endpoint);
+ for (let report of endpoint.reports) {
+ assert_true(isObjectAsExpected(report, expectedReport),
+ `report received for endpoint: ${endpoint.name} ${JSON.stringify(report)} should match ${JSON.stringify(expectedReport)}`);
+ }
+ assert_equals(endpoint.reports.length, 1, `has exactly one report for ${endpoint.name}`)
+ }));
+ }, `coop document reporting test ${testName} to ${host.name} with ${coop}, ${coep}, ${coopRo}, ${coepRo}`);
+}
+
+// Run an array of reporting tests then verify there's no reports that were not
+// expected.
+// Tests' elements contain: host, coop, coep, coop-report-only,
+// coep-report-only, expectedReports.
+// See isObjectAsExpected for explanations regarding the matching behavior.
+async function runNavigationReportingTests(testName, tests) {
+ await clearReportsOnServer();
+ tests.forEach(test => {
+ navigationReportingTest(testName, ...test);
+ });
+ verifyRemainingReports();
+}
+
+// Run an array of reporting tests using Reporting-Endpoints header then
+// verify there's no reports that were not expected.
+// Tests' elements contain: host, coop, coep, coop-report-only,
+// coep-report-only, expectedReports.
+// See isObjectAsExpected for explanations regarding the matching behavior.
+function runNavigationDocumentReportingTests(testName, tests) {
+ clearReportsOnServer();
+ tests.forEach(test => {
+ navigationDocumentReportingTest(testName, ...test);
+ });
+}
+
+function verifyRemainingReports() {
+ promise_test(t => {
+ return Promise.all(reportEndpoints.map(async (endpoint) => {
+ await pollReports(endpoint);
+ assert_equals(endpoint.reports.length, 0, `${endpoint.name} should be empty`);
+ }));
+ }, "verify remaining reports");
+}
+
+const receiveReport = async function(uuid, type) {
+ while(true) {
+ let reports = await Promise.race([
+ receive(uuid),
+ new Promise(resolve => {
+ step_timeout(resolve, 1000, "timeout");
+ })
+ ]);
+ if (reports == "timeout")
+ return "timeout";
+ reports = JSON.parse(reports);
+ for(report of reports) {
+ if (report?.body?.type == type)
+ return report;
+ }
+ }
+}
+
+const coopHeaders = function (uuid) {
+ // Use a custom function instead of convertToWPTHeaderPipe(), to avoid
+ // encoding double quotes as %22, which messes with the reporting endpoint
+ // registration.
+ let getHeader = (uuid, coop_value, is_report_only) => {
+ const header_name =
+ is_report_only ?
+ "Cross-Origin-Opener-Policy-Report-Only":
+ "Cross-Origin-Opener-Policy";
+ return `|header(${header_name},${coop_value}%3Breport-to="${uuid}")`;
+ }
+
+ return {
+ coopSameOriginHeader:
+ getHeader(uuid, "same-origin", is_report_only = false),
+ coopSameOriginAllowPopupsHeader:
+ getHeader(uuid, "same-origin-allow-popups", is_report_only = false),
+ coopRestrictPropertiesHeader:
+ getHeader(uuid, "restrict-properties", is_report_only = false),
+ coopReportOnlySameOriginHeader:
+ getHeader(uuid, "same-origin", is_report_only = true),
+ coopReportOnlySameOriginAllowPopupsHeader:
+ getHeader(uuid, "same-origin-allow-popups", is_report_only = true),
+ coopReportOnlyRestrictPropertiesHeader:
+ getHeader(uuid, "restrict-properties", is_report_only = true),
+ };
+}
+
+// Build a set of headers to tests the reporting API. This defines a set of
+// matching 'Report-To', 'Cross-Origin-Opener-Policy' and
+// 'Cross-Origin-Opener-Policy-Report-Only' headers.
+const reportToHeaders = function(uuid) {
+ const report_endpoint_url = dispatcher_path + `?uuid=${uuid}`;
+ let reportToJSON = {
+ 'group': `${uuid}`,
+ 'max_age': 3600,
+ 'endpoints': [
+ {'url': report_endpoint_url.toString()},
+ ]
+ };
+ reportToJSON = JSON.stringify(reportToJSON)
+ .replace(/,/g, '\\,')
+ .replace(/\(/g, '\\\(')
+ .replace(/\)/g, '\\\)=');
+
+ return {
+ header: `|header(report-to,${reportToJSON})`,
+ ...coopHeaders(uuid)
+ };
+};
+
+// Build a set of headers to tests the reporting API. This defines a set of
+// matching 'Reporting-Endpoints', 'Cross-Origin-Opener-Policy' and
+// 'Cross-Origin-Opener-Policy-Report-Only' headers.
+const reportingEndpointsHeaders = function (uuid) {
+ // Report endpoint keys must start with a lower case alphabet:
+ // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-header-structure-15#section-4.2.3.3
+ assert_true(uuid.match(/^[a-z].*/) != null, 'Use reportToken() instead.');
+
+ const report_endpoint_url = dispatcher_path + `?uuid=${uuid}`;
+ const reporting_endpoints_header = `${uuid}="${report_endpoint_url}"`;
+
+ return {
+ header: `|header(Reporting-Endpoints,${reporting_endpoints_header})`,
+ ...coopHeaders(uuid)
+ };
+};
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/test-access-property.js b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/test-access-property.js
new file mode 100644
index 0000000000..a405202431
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/test-access-property.js
@@ -0,0 +1,65 @@
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+const origin = [
+ ["same-origin" , same_origin ],
+ ["cross-origin", cross_origin],
+];
+let escapeComma = url => url.replace(/,/g, '\\,');
+
+let testAccessProperty = (
+ property,
+ op,
+ expectReport = true,
+ use_restrict_properties = false,
+) => {
+ origin.forEach(([origin_name, origin]) => {
+ promise_test(async t => {
+ const this_window_token = token();
+
+ // The opener window:
+ const opener_token = token();
+ const opener_url = get_host_info().HTTP_ORIGIN + executor_path +
+ `&uuid=${opener_token}`;
+
+ // The openee window:
+ const openee_token = token();
+ const openee_report_token = token();
+ const openee_report_to = reportToHeaders(openee_report_token);
+ const coop_ro_header =
+ use_restrict_properties
+ ? openee_report_to.coopReportOnlyRestrictPropertiesHeader
+ : openee_report_to.coopReportOnlySameOriginHeader;
+ const openee_url = origin + executor_path + openee_report_to.header +
+ coop_ro_header + coep_header + `&uuid=${openee_token}`;
+
+ t.add_cleanup(() => {
+ send(opener_token, "window.close()")
+ send(openee_token, "window.close()")
+ });
+
+ // Open the two windows. Wait for them to be loaded.
+ window.open(opener_url);
+ send(opener_token, `
+ window.openee = window.open('${escapeComma(openee_url)}');
+ `);
+ send(openee_token, `send("${this_window_token}", "Ready");`);
+ assert_equals(await receive(this_window_token), "Ready");
+
+ // 2. Try to access the openee.
+ send(opener_token, `(${op})(openee);`);
+
+ // 3. Fetch reports sent to the openee.
+ let report = await receiveReport(openee_report_token,
+ "access-to-coop-page-from-opener");
+ if (expectReport) {
+ assert_equals(report.body.property, property);
+ } else {
+ // "timeout" should be returned if no such reports are received.
+ assert_equals(report, "timeout");
+ }
+
+
+ }, `${origin_name} > ${op}`);
+ })
+};
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/try-access.js b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/try-access.js
new file mode 100644
index 0000000000..a06cb07904
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/reporting/resources/try-access.js
@@ -0,0 +1,20 @@
+// A function trying to access to |w| through a "CrossOrigin" attribute (blur).
+// This function is kept in its own file to ensure the source location of the
+// call stays constant.
+function tryAccess(w) {
+ try {
+ w.blur();
+ } catch(e) {}
+}
+
+function assert_source_location_found(report) {
+ assert_true(report.body.sourceFile.includes("try-access.js"));
+ assert_equals(report.body.lineNumber, 6);
+ assert_equals(report.body.columnNumber, 7);
+}
+
+function assert_source_location_missing(report) {
+ assert_equals(report.body.sourceFile, undefined);
+ assert_equals(report.body.lineNumber, undefined);
+ assert_equals(report.body.columnNumber, undefined);
+}
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resource-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/resource-popup.https.html
new file mode 100644
index 0000000000..481cceb72f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resource-popup.https.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Cross-Origin-Opener-Policy forces browsing context switch in various popup document types</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<p>These tests create a "parent" popup window which is an HTML document with a
+specific Cross-Origin-Opener-Policy. The parent creates a "child" popup window
+which is a non-HTML document with a Cross-Origin-Opener-Policy of its own. The
+parent waits for the child's location to change from "<code>about:blank</code>"
+and then inspects its <code>name</code> and <code>closed</code> properties
+which it reports back to the test context.</p>
+
+<p>The HTTP `Refresh` header is used to ensure the child eventually closes
+itself (since proper observance of COOP will prevent the parent from closing
+the child in some cases).</p>
+
+<script>
+'use strict';
+
+const coop_resource_test = ({parentCoop, resourceCoop, resource, resourceName, validate}) => {
+ async_test((t) => {
+ const bc = new BroadcastChannel(token());
+ bc.onmessage = t.step_func_done(({data}) => validate(data));
+ const childLocation = resource +
+ `?pipe=header(Refresh,2;url=/html/cross-origin-opener-policy/resources/resource-cleanup.html?channel=${bc.name})` +
+ (resourceCoop ? `|header(Cross-Origin-Opener-Policy,${resourceCoop})` : '');
+ const parentLocation = 'resources/resource-popup.html' +
+ `?channel_name=${bc.name}` +
+ `&resource=${encodeURIComponent(childLocation)}` +
+ `&resource_name=${resourceName}` +
+ (parentCoop ? `&pipe=header(Cross-Origin-Opener-Policy,${parentCoop})` : '');
+
+ open(parentLocation);
+
+ t.add_cleanup(() => {
+ // Close the "parent" popup and the "child" popup if it has already
+ // redirected to the HTML document.
+ bc.postMessage(null);
+ // Prepare to close the "child" popup in the case that it has not yet
+ // redirected to the the HTML document.
+ bc.onmessage = () => bc.postMessage(null);
+ });
+ }, `${resource} - parent COOP: "${parentCoop}"; child COOP: "${resourceCoop}"`);
+};
+
+const resources = [
+ '/common/dummy.xml',
+ '/images/red.png',
+ '/common/text-plain.txt',
+ '/media/2x2-green.mp4',
+];
+
+for (const resource of resources) {
+ coop_resource_test({
+ parentCoop: '',
+ resourceCoop: 'same-origin',
+ resource,
+ resourceName: 'foobar',
+ validate(data) {
+ assert_equals(data.name, null);
+ assert_equals(data.closed, true);
+ }
+ });
+
+ coop_resource_test({
+ parentCoop: 'same-origin',
+ resourceCoop: '',
+ resource,
+ resourceName: 'foobar',
+ validate(data) {
+ assert_equals(data.name, null);
+ assert_equals(data.closed, true);
+ }
+ });
+
+ coop_resource_test({
+ parentCoop: 'same-origin',
+ resourceCoop: 'same-origin',
+ resource,
+ resourceName: 'foobar',
+ validate(data) {
+ assert_equals(data.name, 'foobar');
+ assert_equals(data.closed, false);
+ }
+ });
+}
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/call-functionCalledByOpenee.html b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/call-functionCalledByOpenee.html
new file mode 100644
index 0000000000..d0ff0b723e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/call-functionCalledByOpenee.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+window.opener.functionCalledByOpenee();
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/common.js b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/common.js
new file mode 100644
index 0000000000..a005cb8a20
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/common.js
@@ -0,0 +1,86 @@
+// To use the functions below, be sure to include the following files in your
+// test:
+// - "/common/get-host-info.sub.js" to get the different origin values.
+
+const SAME_ORIGIN = {origin: get_host_info().HTTPS_ORIGIN, name: "SAME_ORIGIN"};
+const SAME_SITE = {origin: get_host_info().HTTPS_REMOTE_ORIGIN, name: "SAME_SITE"};
+const CROSS_ORIGIN = {origin: get_host_info().HTTPS_NOTSAMESITE_ORIGIN, name: "CROSS_ORIGIN"}
+
+function addScriptAndTriggerOnload(src, onload){
+ return `script = document.createElement("script");
+ script.src= "${src}" ;
+ script.onload = () => {
+ ${onload}
+ };
+ document.head.append(script);`
+}
+
+function verify_window(callback, w, hasOpener) {
+ // If there's no opener, the w must be closed:
+ assert_equals(w.closed, !hasOpener, 'w.closed');
+ // Opener's access on w.length is possible only if hasOpener:
+ assert_equals(w.length, hasOpener? 1: 0, 'w.length');
+ callback();
+}
+
+function validate_results(callback, test, w, channelName, hasOpener, openerDOMAccess, payload) {
+ assert_equals(payload.name, hasOpener ? channelName : "", 'name');
+ assert_equals(payload.opener, hasOpener, 'opener');
+ // TODO(zcorpan): add openerDOMAccess expectations to all tests
+ if (openerDOMAccess !== undefined) {
+ assert_equals(payload.openerDOMAccess, openerDOMAccess, 'openerDOMAccess');
+ }
+
+ // The window proxy in Chromium might still reflect the previous frame,
+ // until its unloaded. This delays the verification of w here.
+ if( !w.closed && w.length == 0) {
+ test.step_timeout( () => {
+ verify_window(callback, w, hasOpener);
+ }, 500);
+ } else {
+ verify_window(callback, w, hasOpener);
+ }
+}
+
+// Note: This function is deprecated and should not be used by new tests.
+// Instead, use `dispatcher_url_test()`.
+function url_test(t, url, channelName, hasOpener, openerDOMAccess, callback) {
+ if (callback === undefined) {
+ callback = () => { t.done(); };
+ }
+ const bc = new BroadcastChannel(channelName);
+ bc.onmessage = t.step_func(event => {
+ const payload = event.data;
+ validate_results(callback, t, w, channelName, hasOpener, openerDOMAccess, payload);
+ });
+
+ const w = window.open(url, channelName);
+
+ // Close the popup once the test is complete.
+ // The browsing context might be closed hence use the broadcast channel
+ // to trigger the closure.
+ t.add_cleanup(() => {
+ bc.postMessage("close");
+ });
+}
+
+// Similar to `url_test()` above except that this uses a dispatcher instead of
+// BroadcastChannel (useful in cases where the context we are testing in is a
+// third-party iframe that doesn't share a partition with the top-level
+// site).
+async function dispatcher_url_test(t, url, responseToken, iframeToken, hasOpener, openerDOMAccess, callback) {
+
+ const w = window.open(url, responseToken);
+
+ // Close the popup once the test is complete.
+ // The browsing context might be closed hence we'll have the iframe trigger
+ // the closure by sending it a 'close' message.
+ t.add_cleanup(async () => {
+ await send(iframeToken, "close");
+ });
+
+ var payload = await receive(responseToken);
+ payload = JSON.parse(payload);
+ validate_results(callback, t, w, responseToken, hasOpener, openerDOMAccess, payload);
+}
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-coep.py b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-coep.py
new file mode 100644
index 0000000000..d8e3bf0d42
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-coep.py
@@ -0,0 +1,84 @@
+import json
+
+def main(request, response):
+ requestData = request.GET
+ if request.method == u"POST":
+ requestData = request.POST
+
+ coop = requestData.first(b"coop")
+ coopReportOnly = requestData.first(b"coop-report-only", None)
+ coep = requestData.first(b"coep")
+ coepReportOnly = requestData.first(b"coep-report-only", None)
+ redirect = requestData.first(b"redirect", None)
+ if coop != b"":
+ response.headers.set(b"Cross-Origin-Opener-Policy", coop)
+ if coopReportOnly is not None:
+ response.headers.set(b"Cross-Origin-Opener-Policy-Report-Only", coopReportOnly)
+ if coep != b"":
+ response.headers.set(b"Cross-Origin-Embedder-Policy", coep)
+ if coepReportOnly is not None:
+ response.headers.set(b"Cross-Origin-Embedder-Policy-Report-Only", coepReportOnly)
+ if b'cache' in requestData:
+ response.headers.set(b'Cache-Control', b'max-age=3600')
+ host = request.url_parts[1]
+
+ if redirect != None:
+ response.status = 302
+ response.headers.set(b"Location", redirect)
+ return
+
+ # Collect relevant params to be visible to response JS
+ params = {}
+ for key in (b"navHistory", b"avoidBackAndForth", b"navigate", b"channel", b"responseToken", b"iframeToken"):
+ value = requestData.first(key, None)
+ params[key.decode()] = value and value.decode()
+
+ response.content = b"""
+<!doctype html>
+<meta charset=utf-8>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/fully-loaded.js"></script>
+<body>
+<script>
+ const params = %s;
+ const navHistory = params.navHistory;
+ const avoidBackAndForth = params.avoidBackAndForth;
+ const navigate = params.navigate;
+ if (navHistory !== null) {
+ fullyLoaded().then(() => {
+ history.go(Number(navHistory));
+ });
+ } else if (navigate !== null && (history.length === 1 || !avoidBackAndForth)) {
+ fullyLoaded().then(() => {
+ self.location = navigate;
+ });
+ } else {
+ let openerDOMAccessAllowed = false;
+ try {
+ openerDOMAccessAllowed = !!self.opener.document.URL;
+ } catch(ex) {
+ }
+ // Handle the response from the frame, closing the popup once the
+ // test completes.
+ addEventListener("message", event => {
+ if (event.data == "close") {
+ close();
+ }
+ });
+ iframe = document.createElement("iframe");
+ iframe.onload = () => {
+ const payload = { name: self.name, opener: !!self.opener, openerDOMAccess: openerDOMAccessAllowed };
+ iframe.contentWindow.postMessage(payload, "*");
+ };
+ const channelName = params.channel;
+ const responseToken = params.responseToken;
+ const iframeToken = params.iframeToken;
+ iframe.src = `${get_host_info().HTTPS_ORIGIN}/html/cross-origin-opener-policy/resources/postback.html` +
+ `?channel=${encodeURIComponent(channelName)}` +
+ `&responseToken=${responseToken}` +
+ `&iframeToken=${iframeToken}`;
+ document.body.appendChild(iframe);
+ }
+</script>
+</body>
+""" % json.dumps(params).encode("utf-8")
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis
new file mode 100644
index 0000000000..082478e159
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis
@@ -0,0 +1,24 @@
+HTTP/1.1 200 OK
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Opener-Policy: same-origin
+Server: BaseHTTP/0.3 Python/2.7.15+
+Date: Wed, 18 Dec 2019 00:47:08 GMT
+
+<!doctype html>
+<meta charset=utf-8>
+<script src="/common/get-host-info.sub.js"></script>
+<iframe></iframe>
+<script>
+ const navigate = new URL(location).searchParams.get("navigate");
+ if (navigate !== null) {
+ self.location = navigate;
+ } else {
+ const iframe = document.querySelector("iframe");
+ iframe.onload = () => {
+ const payload = { name: self.name, opener: !!self.opener };
+ iframe.contentWindow.postMessage(payload, "*");
+ };
+ const channelName = new URL(location).searchParams.get("channel");
+ iframe.src = `${get_host_info().HTTPS_ORIGIN}/html/cross-origin-opener-policy/resources/postback.html?channel=${channelName}`;
+ }
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/csp-sandbox.py b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/csp-sandbox.py
new file mode 100644
index 0000000000..6cf21aeccf
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/csp-sandbox.py
@@ -0,0 +1,29 @@
+def main(request, response):
+ coop = request.GET.first(b"coop")
+ coep = request.GET.first(b"coep")
+ sandbox = request.GET.first(b"sandbox")
+ if coop != "":
+ response.headers.set(b"Cross-Origin-Opener-Policy", coop)
+ if coep != "":
+ response.headers.set(b"Cross-Origin-Embedder-Policy", coep)
+ response.headers.set(b"Content-Security-Policy", b"sandbox " + sandbox + b";")
+
+ # Open a popup to coop-coep.py with the same parameters (except sandbox)
+ response.content = b"""
+<!doctype html>
+<meta charset=utf-8>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/fully-loaded.js"></script>
+<script>
+ const params = new URL(location).searchParams;
+ params.delete("sandbox");
+ const navigate = params.get("navigate");
+ if (navigate) {
+ fullyLoaded().then(() => {
+ self.location = navigate;
+ });
+ } else {
+ window.open(`/html/cross-origin-opener-policy/resources/coop-coep.py?${params}`);
+ }
+</script>
+"""
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/fully-loaded.js b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/fully-loaded.js
new file mode 100644
index 0000000000..d40e00af43
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/fully-loaded.js
@@ -0,0 +1,10 @@
+// Return a promise, which resolves when new navigations aren't considered
+// client-side redirects anymore.
+//
+// Note: A long `setTimeout` is used, because client-side redirect is an
+// heuristic and isn't clearly specified.
+function fullyLoaded() {
+ return new Promise((resolve, reject) => {
+ addEventListener('load', () => setTimeout(resolve, 2000))
+ });
+}
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/iframe-test.js b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/iframe-test.js
new file mode 100644
index 0000000000..f13e768690
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/iframe-test.js
@@ -0,0 +1,241 @@
+// To use the functions below, be sure to include the following files in your
+// test:
+// - "/common/get-host-info.sub.js" to get the different origin values.
+// - "common.js" to have the origins easily available.
+// - "/common/dispatcher/dispatcher.js" for cross-origin messaging.
+// - "/common/utils.js" for token().
+
+function getBaseExecutorPath(origin) {
+ return origin + '/common/dispatcher/executor.html';
+}
+
+function getHeadersPipe(headers) {
+ const coop_header = headers.coop ?
+ `|header(Cross-Origin-Opener-Policy,${encodeURIComponent(headers.coop)})` : '';
+ const coep_header = headers.coep ?
+ `|header(Cross-Origin-Embedder-Policy,${encodeURIComponent(headers.coep)})` : '';
+ return coop_header + coep_header;
+}
+
+function getExecutorPath(uuid, origin, headers) {
+ return getBaseExecutorPath(origin) +
+ `?uuid=${uuid}` +
+ `&pipe=${getHeadersPipe(headers)}`;
+}
+
+function evaluate(target_token, script) {
+ const reply_token = token();
+ send(target_token, `send('${reply_token}', ${script});`);
+ return receive(reply_token);
+}
+
+// Return true if an opened iframe can access |property| on a stored
+// window.popup object without throwing an error.
+function iframeCanAccessProperty(iframe_token, property) {
+ const reply_token = token();
+ send(iframe_token,
+ `try {
+ const unused = window.popup['${property}'];
+ send('${reply_token}', 'true')
+ } catch (errors) {
+ send('${reply_token}', 'false')
+ }`);
+ return receive(reply_token);
+}
+
+// Returns the script necessary to open a popup, given the method in
+// `popup_via`. Supported methods are 'window_open' that leverages
+// window.open(), 'anchor' that creates an <a> HTML element and clicks on it,
+// and 'form' that creates a form and submits it.
+function popupOpeningScript(popup_via, popup_url, popup_origin, headers,
+ popup_token) {
+ if (popup_via === 'window_open')
+ return `window.popup = window.open('${popup_url}', '${popup_token}');`;
+
+ if (popup_via === 'anchor') {
+ return `
+ const anchor = document.createElement('a');
+ anchor.href = '${popup_url}';
+ anchor.rel = "opener";
+ anchor.target = '${popup_token}';
+ anchor.innerText = "anchor";
+ document.body.appendChild(anchor);
+ anchor.click();
+ `;
+ }
+
+ if (popup_via === "form") {
+ return `
+ const form = document.createElement("form");
+ form.action = '${getBaseExecutorPath(popup_origin.origin)}';
+ form.target = '${popup_token}';
+ form.method = 'GET';
+ const add_param = (name, value) => {
+ const input = document.createElement("input");
+ input.name = name;
+ input.value = value;
+ form.appendChild(input);
+ };
+ add_param("uuid", "${popup_token}");
+ add_param("pipe", "${getHeadersPipe(headers)}");
+ document.body.appendChild(form);
+ form.submit();
+ `;
+ }
+
+ assert_not_reached('Unrecognized popup opening method.');
+}
+
+function promise_test_parallel(promise, description) {
+ async_test(test => {
+ promise(test)
+ .then(() => test.done())
+ .catch(test.step_func(error => { throw error; }));
+ }, description);
+};
+
+// Verifies that a popup with origin `popup_origin` and headers `headers` has
+// the expected `opener_state` after being opened from an iframe with origin
+// `iframe_origin`.
+function iframe_test(description, iframe_origin, popup_origin, headers,
+ expected_opener_state) {
+ for (const popup_via of ['window_open', 'anchor','form']) {
+ promise_test_parallel(async t => {
+ const iframe_token = token();
+ const popup_token = token();
+ const reply_token = token();
+
+ const frame = document.createElement("iframe");
+ const iframe_url = getExecutorPath(
+ iframe_token,
+ iframe_origin.origin,
+ {});
+
+ frame.src = iframe_url;
+ document.body.append(frame);
+
+ send(iframe_token, `send('${reply_token}', 'Iframe loaded');`);
+ assert_equals(await receive(reply_token), 'Iframe loaded');
+
+ const popup_url = getExecutorPath(
+ popup_token,
+ popup_origin.origin,
+ headers);
+
+ // We open popup and then ping it, it will respond after loading.
+ send(iframe_token, popupOpeningScript(popup_via, popup_url, popup_origin,
+ headers, popup_token));
+ send(popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), 'Popup loaded');
+
+ // Make sure the popup and the iframe are removed once the test has run,
+ // keeping a clean state.
+ add_completion_callback(() => {
+ frame.remove();
+ send(popup_token, `close()`);
+ });
+
+ // Give some time for things to settle across processes etc. before
+ // proceeding with verifications.
+ await new Promise(resolve => { t.step_timeout(resolve, 500); });
+
+ // Verify that the opener is in the state we expect it to be in.
+ switch (expected_opener_state) {
+ case 'preserved': {
+ assert_equals(
+ await evaluate(popup_token, 'opener != null'), "true",
+ 'Popup has an opener?');
+ assert_equals(
+ await evaluate(popup_token, `name === '${popup_token}'`), "true",
+ 'Popup has a name?');
+
+ // When the popup was created using window.open, we've kept a handle
+ // and we can do extra verifications.
+ if (popup_via === 'window_open') {
+ assert_equals(
+ await evaluate(iframe_token, 'popup != null'), "true",
+ 'Popup handle is non-null in iframe?');
+ assert_equals(
+ await evaluate(iframe_token, 'popup.closed'), "false",
+ 'Popup appears closed from iframe?');
+ assert_equals(
+ await iframeCanAccessProperty(iframe_token, "document"),
+ popup_origin === iframe_origin ? "true" : "false",
+ 'Iframe has dom access to the popup?');
+ assert_equals(
+ await iframeCanAccessProperty(iframe_token, "frames"), "true",
+ 'Iframe has cross origin access to the popup?');
+ }
+ break;
+ }
+ case 'restricted': {
+ assert_equals(
+ await evaluate(popup_token, 'opener != null'), "true",
+ 'Popup has an opener?');
+ assert_equals(
+ await evaluate(popup_token, `name === ''`), "true",
+ 'Popup name is cleared?');
+
+ // When the popup was created using window.open, we've kept a handle
+ // and we can do extra verifications.
+ if (popup_via === 'window_open') {
+ assert_equals(
+ await evaluate(iframe_token, 'popup != null'), "true",
+ 'Popup handle is non-null in iframe?');
+ assert_equals(
+ await evaluate(iframe_token, 'popup.closed'), "false",
+ 'Popup appears closed from iframe?');
+ assert_equals(
+ await iframeCanAccessProperty(iframe_token, "document"), "false",
+ 'Iframe has dom access to the popup?');
+ assert_equals(
+ await iframeCanAccessProperty(iframe_token, "frames"), "false",
+ 'Iframe has cross origin access to the popup?');
+ assert_equals(
+ await iframeCanAccessProperty(iframe_token, "closed"), "true",
+ 'Iframe has limited cross origin access to the popup?');
+ }
+ break;
+ }
+ case 'severed': {
+ assert_equals(await evaluate(popup_token, 'opener != null'), "false",
+ 'Popup has an opener?');
+ assert_equals(
+ await evaluate(popup_token, `name === ''`), "true",
+ 'Popup name is cleared?');
+
+ // When the popup was created using window.open, we've kept a handle
+ // and we can do extra verifications.
+ if (popup_via === 'window_open') {
+ assert_equals(
+ await evaluate(iframe_token, 'popup != null'), "true",
+ 'Popup handle is non-null in iframe?');
+ assert_equals(
+ await evaluate(iframe_token, 'popup.closed'), "true",
+ 'Popup appears closed from iframe?');
+ }
+ break;
+ }
+ case 'noopener': {
+ assert_equals(await evaluate(popup_token, 'opener != null'), "false",
+ 'Popup has an opener?');
+ assert_equals(
+ await evaluate(popup_token, `name === ''`), "true",
+ 'Popup name is cleared?');
+
+ // When the popup was created using window.open, we've kept a handle
+ // and we can do extra verifications.
+ if (popup_via === 'window_open') {
+ assert_equals(
+ await evaluate(iframe_token, 'popup == null'), "true",
+ 'Popup handle is null in iframe?');
+ }
+ break;
+ }
+ default:
+ assert_not_reached('Unrecognized opener state: ' +
+ expected_opener_state);
+ }
+ }, `${description} with ${popup_via}`);
+ }
+}
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/popup-test.js b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/popup-test.js
new file mode 100644
index 0000000000..c2717bb135
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/popup-test.js
@@ -0,0 +1,99 @@
+// To use the functions below, be sure to include the following files in your
+// test:
+// - "/common/get-host-info.sub.js" to get the different origin values.
+// - "common.js" to have the origins easily available.
+// - "/common/dispatcher/dispatcher.js" for cross-origin messaging.
+// - "/common/utils.js" for token().
+
+function getExecutorPath(uuid, origin, headers) {
+ const executor_path = '/common/dispatcher/executor.html?';
+ const coop_header = headers.coop ?
+ `|header(Cross-Origin-Opener-Policy,${encodeURIComponent(headers.coop)})` : '';
+ const coep_header = headers.coep ?
+ `|header(Cross-Origin-Embedder-Policy,${encodeURIComponent(headers.coep)})` : '';
+ return origin +
+ executor_path +
+ `uuid=${uuid}` +
+ '&pipe=' + coop_header + coep_header;
+}
+
+function getPopupHasOpener(popup_token) {
+ const reply_token = token();
+ send(popup_token, `send('${reply_token}', window.opener != null);`);
+ return receive(reply_token);
+}
+
+// Return true if |object|.|property| can be called without throwing an error.
+function canAccessProperty(object, property) {
+ try {
+ const unused = object[property];
+ return true;
+ } catch (errors) {
+ return false;
+ }
+}
+
+// Verifies that a popup with origin `origin` and headers `headers` has
+// the expected `opener_state` after being opened.
+async function popup_test(description, origin, headers, expected_opener_state) {
+ promise_test(async t => {
+ const popup_token = token();
+ const reply_token = token();
+
+ const popup_url = getExecutorPath(
+ popup_token,
+ origin.origin,
+ headers);
+
+ // We open popup and then ping it, it will respond after loading.
+ const popup = window.open(popup_url);
+ send(popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), 'Popup loaded');
+
+ // Make sure the popup will be closed once the test has run, keeping a clean
+ // state.
+ t.add_cleanup(() => {
+ send(popup_token, `close()`);
+ });
+
+ // Give some time for things to settle across processes etc. before
+ // proceeding with verifications.
+ await new Promise(resolve => { t.step_timeout(resolve, 500); });
+
+ // Verify that the opener is in the state we expect it to be in.
+ switch (expected_opener_state) {
+ case 'preserved': {
+ assert_false(popup.closed, 'Popup is closed from opener?');
+ assert_true(await getPopupHasOpener(popup_token) === "true",
+ 'Popup has nulled opener?');
+ assert_equals(canAccessProperty(popup, "document"),
+ origin === SAME_ORIGIN,
+ 'Main page has dom access to the popup?');
+ assert_true(canAccessProperty(popup, "frames"),
+ 'Main page has cross origin access to the popup?');
+ break;
+ }
+ case 'restricted': {
+ assert_false(popup.closed, 'Popup is closed from opener?');
+ assert_true(await getPopupHasOpener(popup_token) === "true",
+ 'Popup has nulled opener?');
+ assert_false(canAccessProperty(popup, "document"),
+ 'Main page has dom access to the popup?');
+ assert_false(canAccessProperty(popup, "frames"),
+ 'Main page has cross origin access to the popup?');
+ assert_true(canAccessProperty(popup, "closed"),
+ 'Main page has limited cross origin access to the popup?');
+ break;
+ }
+ case 'severed': {
+ assert_true(popup.closed, 'Popup is closed from opener?');
+ assert_false(await getPopupHasOpener(popup_token) === "true",
+ 'Popup has nulled opener?');
+ break;
+ }
+ default:
+ assert_unreached(true, "Unrecognized opener relationship: " + expected_opener_state);
+ }
+ }, description);
+}
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html
new file mode 100644
index 0000000000..35b3be5c93
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script>
+const params = new URL(location).searchParams;
+const channelName = params.get("channel");
+const responseToken = params.get("responseToken");
+const iframeToken = params.get("iframeToken");
+
+// If the channel parameter is set, use the BroadcastChannel-based communication
+// method. Otherwise, use the dispatcher (useful in cases where this is embedded
+// in a third-party iframe that doesn't share a partition with the top-level
+// site).
+if (channelName != "null") {
+ const bc = new BroadcastChannel(channelName);
+ // Handle the close message from the test-cleanup, forwarding it to the
+ // top level document, as this iframe and the opening document might not
+ // be able to close the popup.
+ bc.onmessage = event => {
+ if (event.data == "close") {
+ top.postMessage("close", "*");
+ }
+ };
+
+ window.addEventListener("message", event => {
+ bc.postMessage(event.data);
+ });
+
+} else {
+ window.addEventListener("message", event => {
+ send(responseToken, JSON.stringify(event.data));
+ });
+
+ async function waitToClose() {
+ response = await receive(iframeToken);
+ // Handle the close message from the test-cleanup, forwarding it to the
+ // top level document, as this iframe and the opening document might not
+ // be able to close the popup.
+ if (response == "close") {
+ top.postMessage("close", "*");
+ }
+ }
+ waitToClose();
+}
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/postback.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/redirect.py b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/redirect.py
new file mode 100644
index 0000000000..88dbd60fae
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/redirect.py
@@ -0,0 +1,5 @@
+from wptserve.utils import isomorphic_encode
+
+def main(request, response):
+ response.status = 302
+ response.headers.set(b"Location", isomorphic_encode(request.url[request.url.find(u'?')+1:]))
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-cleanup.html b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-cleanup.html
new file mode 100644
index 0000000000..3ae5587c7d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-cleanup.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Redirect destination for non-HTML documents to close themselves</title>
+<script>
+'use strict';
+const channel = new URL(location).searchParams.get('channel');
+const bc = new BroadcastChannel(channel);
+bc.onmessage = () => close();
+bc.postMessage({name: 'FAIL', closed: 'FAIL' });
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-popup.html b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-popup.html
new file mode 100644
index 0000000000..ee08bcb496
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/resource-popup.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title></title>
+<script>
+'use strict';
+const params = new URL(location).searchParams;
+const bc = new BroadcastChannel(params.get('channel_name'));
+const win = open(params.get('resource'), params.get('resource_name'));
+
+bc.onmessage = () => {
+ win.close();
+ close();
+};
+const id = setInterval(() => {
+ if (win.closed || win.location.href !== 'about:blank') {
+ clearInterval(id);
+ const winName = (() => {
+ try {
+ return win.name;
+ } catch (e) {
+ return null;
+ }
+ })();
+ bc.postMessage({name: winName || null, closed: win.closed});
+ }
+}, 100);
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/universal-worker.js b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/universal-worker.js
new file mode 100644
index 0000000000..2441679372
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/universal-worker.js
@@ -0,0 +1,2 @@
+onmessage = message => eval(message.data);
+onfetch = event => fetchHandler(event);
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/README.md b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/README.md
new file mode 100644
index 0000000000..b3c24c3f82
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/README.md
@@ -0,0 +1,9 @@
+Because this test suite is run as a virtual suite and it's quite deep in the
+folders, we have to use abbreviations for the test names to not run over 200
+characters, which is problematic on Windows.
+
+* unspecified -> "u"
+* unsafe-none -> "un"
+* same-origin -> "so"
+* same-origin-allow-popups -> "soap"
+* restrict-properties -> omitted
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-closed.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-closed.https.html
new file mode 100644
index 0000000000..1c315b35d7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-closed.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title> Check openee.closed access is allowed for COOP: restrict-properties</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty(
+ "closed",
+ w => w.closed,
+ expectReport = false,
+ use_restrict_properties = true
+);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-openee-rp-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-openee-rp-ro.https.html
new file mode 100644
index 0000000000..7a96f4f576
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-openee-rp-ro.https.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<title>
+ COOP reports are sent to the openee when the openee used COOP-RO:
+ restrict-properties and its same-origin opener tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+promise_test(async t => {
+ const report_token = token();
+ const openee_token = token();
+ const opener_token = token(); // The current test window.
+
+ const opener_url = location.href;
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = same_origin + executor_path + reportTo.header +
+ reportTo.coopReportOnlyRestrictPropertiesHeader +
+ `&uuid=${openee_token}`;
+
+ const openee = window.open(openee_url);
+ t.add_cleanup(() => send(openee_token, "window.close()"))
+
+ // 1. Make sure the new document to be loaded.
+ send(openee_token, `
+ send("${opener_token}", "Ready");
+ `);
+ let reply = await receive(opener_token);
+ assert_equals(reply, "Ready");
+
+ // 2. Try to access the openee. A report is sent, because of COOP-RO:
+ // restrict-properties.
+ tryAccess(openee);
+
+ // 3. Check a report is sent to the openee.
+ let report =
+ await receiveReport(report_token, "access-to-coop-page-from-opener");
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, openee_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "restrict-properties");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, opener_url);
+ assert_equals(report.body.openeeURL, undefined);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, opener_url);
+ assert_equals(report.body.initialPopupURL, undefined);
+}, "access-reporting-openee-rp-ro");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-opener-rp-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-opener-rp-ro.https.html
new file mode 100644
index 0000000000..9e1e85b16a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-opener-rp-ro.https.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<title>
+ COOP reports are sent to the opener when the opener used COOP-RO:
+ restrict-properties and its same-origin openee tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+promise_test(async t => {
+ // The test window.
+ const this_window_token = token();
+
+ // The "opener" window. This has COOP and a reporter.
+ const opener_report_token= token();
+ const opener_token = token();
+ const opener_reportTo = reportToHeaders(opener_report_token);
+ const opener_url = same_origin + executor_path + opener_reportTo.header +
+ opener_reportTo.coopReportOnlyRestrictPropertiesHeader +
+ `&uuid=${opener_token}`;
+
+ // The "openee" window. This is same origin with the "opener".
+ const openee_report_token= token();
+ const openee_token = token();
+ const openee_url = same_origin + executor_path + `&uuid=${openee_token}`;
+
+ // 1. Create the opener window.
+ let opener_window_proxy = window.open(opener_url);
+ t.add_cleanup(() => send(opener_token, "window.close()"));
+
+ // 2. The opener opens its openee.
+ send(opener_token, `
+ openee = window.open("${openee_url}");
+ send("${this_window_token}", "ACK 1");
+ `);
+ assert_equals("ACK 1", await receive(this_window_token));
+ t.add_cleanup(() => send(openee_token, "window.close()"));
+
+ // 3. The openee tries to access its opener.
+ send(openee_token, addScriptAndTriggerOnload(
+ directory + "/reporting/resources/try-access.js",
+ "tryAccess(opener);")
+ );
+
+ // 4. Check a report sent to the opener.
+ let report =
+ await receiveReport(opener_report_token, "access-to-coop-page-from-openee");
+ assert_equals(report.type, "coop");
+ assert_equals(report.url, opener_url.replace(/"/g, '%22'));
+ assert_equals(report.body.disposition, "reporting");
+ assert_equals(report.body.effectivePolicy, "restrict-properties");
+ assert_equals(report.body.property, "blur");
+ assert_source_location_missing(report);
+ assert_equals(report.body.openerURL, undefined);
+ assert_equals(report.body.openeeURL, openee_url);
+ assert_equals(report.body.otherDocumentURL, undefined);
+ assert_equals(report.body.referrer, undefined);
+ assert_equals(report.body.initialPopupURL, openee_url);
+}, "access-reporting-opener-rp-ro");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-post-message.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-post-message.https.html
new file mode 100644
index 0000000000..5bc718e2a8
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/access-reporting-post-message.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title> Check openee.postMessage() access is allowed for COOP: restrict-properties</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/test-access-property.js"></script>
+<script>
+
+testAccessProperty(
+ "postMessage",
+ w => w.postMessage("message", "*"),
+ expectReport = false,
+ use_restrict_properties = true
+);
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html
new file mode 100644
index 0000000000..e5c8775174
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+promise_test(async t => {
+ const popup_token = token();
+ const second_popup_token = token();
+ const reply_token = token();
+
+ const unsafe_none_url = getExecutorPath(
+ popup_token,
+ SAME_ORIGIN.origin,
+ { coop: "unsafe-none"});
+
+ const restrict_properties_url = getExecutorPath(
+ second_popup_token,
+ SAME_ORIGIN.origin,
+ { coop: "restrict-properties"});
+
+ // We open popup and then ping it, it will respond after loading.
+ const popup = window.open(unsafe_none_url);
+ send(popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), 'Popup loaded');
+
+ // Make sure the popup will be closed once the test has run, keeping a clean
+ // state.
+ t.add_cleanup(() => {
+ send(popup_token, `close()`);
+ });
+
+ // Now navigate this popup to a restrict-properties page.
+ send(popup_token, `document.location = '${restrict_properties_url}'`);
+ send(second_popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), 'Popup loaded');
+
+ // Navigate again to the original page.
+ send(second_popup_token, `document.location = '${unsafe_none_url}'`);
+ send(popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), 'Popup loaded');
+
+ // Give some time for things to settle across processes etc. before
+ // proceeding with verifications.
+ await new Promise(resolve => { t.step_timeout(resolve, 500); });
+
+ // Verify that we have full access to the popup.
+ assert_false(popup.closed, 'Popup is closed from opener?');
+ assert_true(await getPopupHasOpener(popup_token) === "true",
+ 'Popup has nulled opener?');
+ assert_true(canAccessProperty(popup, "document"),
+ 'Main page has dom access to the popup?');
+ assert_true(canAccessProperty(popup, "frames"),
+ 'Main page has cross origin access to the popup?');
+
+}, "COOP: restrict-properties has no impact in a navigation chain between " +
+ "multiple unsafe-none pages.");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html.headers
new file mode 100644
index 0000000000..073ce7adfb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/coop-rp-in-navigation-chain.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: unsafe-none
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js
new file mode 100644
index 0000000000..1247400a4e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js
@@ -0,0 +1,144 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+
+const executor_path = '/common/dispatcher/executor.html?pipe=';
+const cross_origin = get_host_info().OTHER_ORIGIN;
+const same_origin = get_host_info().ORIGIN;
+const coep_require_corp_header =
+ '|header(Cross-Origin-Embedder-Policy,require-corp)';
+const corp_cross_origin_header =
+ '|header(Cross-Origin-Resource-Policy,cross-origin)';
+const coop_restrict_properties_header =
+ '|header(Cross-Origin-Opener-Policy,restrict-properties)';
+
+function iframePopupAboutBlankTest(
+ origin, {expectedCrossOriginIsolated}, description) {
+ promise_test(async t => {
+ assert_true(crossOriginIsolated, 'Is main frame crossOriginIsolated?');
+ assert_true(
+ 'SharedArrayBuffer' in globalThis,
+ 'Is SharedArrayBuffer defined in main frame?');
+
+ const reply_token = token();
+ const iframe_token = token();
+
+ const iframe = document.createElement('iframe');
+ iframe.src = origin + executor_path + coep_require_corp_header +
+ corp_cross_origin_header + `&uuid=${iframe_token}`;
+ document.body.appendChild(iframe);
+
+ send(iframe_token, `send('${reply_token}', 'Iframe loaded');`);
+ assert_equals(await receive(reply_token), 'Iframe loaded');
+
+ send(iframe_token, `
+ window.popup = window.open();
+ send('${reply_token}', popup === null);
+ `);
+ assert_equals(await receive(reply_token), 'false', 'Is popup handle null?');
+
+ send(
+ iframe_token,
+ `send('${reply_token}', popup.window.crossOriginIsolated);`);
+ assert_equals(
+ await receive(reply_token), `${expectedCrossOriginIsolated}`,
+ 'Is popup crossOriginIsolated?');
+
+ send(iframe_token, `
+ send('${reply_token}', 'SharedArrayBuffer' in popup.window.globalThis);
+ `);
+ assert_equals(
+ await receive(reply_token), `${expectedCrossOriginIsolated}`,
+ 'Is SharedArrayBuffer defined in popup?');
+
+ // Test whether the popup's subframe is crossOriginIsolated
+ const popup_iframe_token = token();
+ const popup_iframe_src = origin + executor_path + coep_require_corp_header +
+ corp_cross_origin_header + `&uuid=${popup_iframe_token}`;
+ send(iframe_token, `
+ const iframe = window.popup.document.createElement('iframe');
+ iframe.src = '${popup_iframe_src}';
+ popup.document.body.appendChild(iframe);
+ `);
+
+ send(popup_iframe_token, `
+ send('${reply_token}', 'Iframe in popup loaded');
+ `);
+ assert_equals(await receive(reply_token), 'Iframe in popup loaded');
+
+ send(popup_iframe_token, `
+ send('${reply_token}', crossOriginIsolated);
+ `);
+ assert_equals(
+ await receive(reply_token), `${expectedCrossOriginIsolated}`,
+ 'Is iframe in popup crossOriginIsolated?');
+
+ send(popup_iframe_token, `
+ send('${reply_token}', 'SharedArrayBuffer' in globalThis);
+ `);
+ assert_equals(
+ await receive(reply_token), `${expectedCrossOriginIsolated}`,
+ 'Is SharedArrayBuffer defined in iframe in popup?');
+
+ // Test whether a nested iframe is crossOriginIsolated
+ const popup_nested_iframe_token = token();
+ const popup_nested_iframe_src = origin + executor_path +
+ coep_require_corp_header + corp_cross_origin_header +
+ `&uuid=${popup_nested_iframe_token}`;
+ send(iframe_token, `
+ blank_iframe = popup.document.createElement('iframe');
+ blank_iframe.src = '';
+ popup.document.body.appendChild(blank_iframe);
+ nested_iframe =
+ blank_iframe.contentDocument.createElement('iframe');
+ nested_iframe.src = '${popup_nested_iframe_src}';
+ blank_iframe.contentDocument.body.appendChild(nested_iframe);
+ `);
+
+ send(popup_nested_iframe_token, `
+ send('${reply_token}', 'Nested iframe in popup loaded');
+ `);
+ assert_equals(await receive(reply_token), 'Nested iframe in popup loaded');
+
+ send(popup_nested_iframe_token, `
+ send('${reply_token}', crossOriginIsolated);
+ `);
+ assert_equals(
+ await receive(reply_token), `${expectedCrossOriginIsolated}`,
+ 'Is nested iframe in popup crossOriginIsolated?');
+
+ send(popup_nested_iframe_token, `
+ send('${reply_token}', 'SharedArrayBuffer' in globalThis);
+ `);
+ assert_equals(
+ await receive(reply_token), `${expectedCrossOriginIsolated}`,
+ 'Is SharedArrayBuffer defined in nested iframe in popup?');
+
+ // Navigate the popup out of the initial empty document, with COOP:RP and
+ // COEP: require-corp. Expect to be crossOriginIsolated.
+ const popup_token = token();
+ const popup_src = origin + executor_path + coop_restrict_properties_header +
+ coep_require_corp_header + `&uuid=${popup_token}`;
+ send(iframe_token, `popup.window.location = '${popup_src}';`);
+
+ send(popup_token, `send('${reply_token}', 'Popup loaded');`);
+ assert_equals(await receive(reply_token), 'Popup loaded');
+
+ send(popup_token, `send('${reply_token}', crossOriginIsolated);`);
+ assert_equals(
+ await receive(reply_token), 'true',
+ 'Is popup crossOriginIsolated after navigation?');
+
+ send(popup_token, `
+ send('${reply_token}', 'SharedArrayBuffer' in globalThis);
+ `);
+ assert_equals(
+ await receive(reply_token), 'true',
+ 'Is SharedArrayBuffer defined in popup after navigation?');
+ }, description);
+}
+
+iframePopupAboutBlankTest(
+ cross_origin, {expectedCrossOriginIsolated: false}, 'Cross-origin iframe');
+iframePopupAboutBlankTest(
+ same_origin, {expectedCrossOriginIsolated: true}, 'Same-origin iframe');
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js.headers
new file mode 100644
index 0000000000..19d0dbe4e1
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-about-blank.https.window.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: restrict-properties
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html
new file mode 100644
index 0000000000..8cf2679e19
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-1">
+<meta name="variant" content="?2-2">
+<meta name="variant" content="?3-3">
+<meta name="variant" content="?4-4">
+<meta name="variant" content="?5-5">
+<meta name="variant" content="?6-6">
+<meta name="variant" content="?7-7">
+<meta name="variant" content="?8-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/iframe-test.js"></script>
+
+<body>
+<script>
+
+// This document has COOP: restrict-properties. The popup has COOP: same-origin.
+// Opening from an iframe should not be different from opening from the main
+// frame and the opener should be severed.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: restrict-properties to popup COOP: same-origin via an iframe, ` +
+ `with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'same-origin' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html.headers
new file mode 100644
index 0000000000..d5c99062d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: restrict-properties
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html
new file mode 100644
index 0000000000..f3af3ca7db
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/iframe-test.js"></script>
+
+<body>
+<script>
+
+
+// This document has COOP: restrict-properties. The popup has COOP:
+// same-origin-allow-popups. Opening from an iframe should not be different from
+// opening from the main frame and the opener should be severed.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "severed"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "severed"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: restrict-properties to popup COOP: same-origin-allow-popups ` +
+ `via an iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'same-origin-allow-popups' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html.headers
new file mode 100644
index 0000000000..d5c99062d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: restrict-properties
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html
new file mode 100644
index 0000000000..560dfd9051
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/iframe-test.js"></script>
+
+<body>
+<script>
+
+// This document has COOP: restrict-properties. The popup has COOP: unsafe-none.
+// Opening from an iframe should not be different from opening from the main
+// frame and the opener should be severed.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "restricted"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "restricted"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "restricted"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "restricted"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "restricted"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "restricted"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "restricted"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "restricted"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "restricted"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: restrict-properties to popup COOP: unsafe-none via an iframe, ` +
+ `with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'unsafe-none' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html.headers
new file mode 100644
index 0000000000..d5c99062d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: restrict-properties
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html
new file mode 100644
index 0000000000..17840724d9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name="variant" content="?1-2">
+<meta name="variant" content="?3-4">
+<meta name="variant" content="?5-6">
+<meta name="variant" content="?7-8">
+<meta name="variant" content="?9-last">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/dispatcher/dispatcher.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/common/subset-tests.js></script>
+<script src=/common/utils.js></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/iframe-test.js"></script>
+
+<body>
+<script>
+
+// This document has COOP: restrict-properties. The popup has COOP:
+// restrict-properties. Opening from an iframe should not be different from
+// opening from the main frame and the opener should be restricted if
+// cross-origin.
+[
+ {
+ "title": "same origin iframe, same origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same site iframe, same origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "cross origin iframe, same origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_ORIGIN,
+ "opener": "preserved"
+ },
+ {
+ "title": "same origin iframe, same site popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "restricted"
+ },
+ {
+ "title": "same site iframe, same site popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": SAME_SITE,
+ "opener": "restricted"
+ },
+ {
+ "title": "cross origin iframe, same site popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": SAME_SITE,
+ "opener": "restricted"
+ },
+ {
+ "title": "same origin iframe, cross origin popup",
+ "iframeOrigin": SAME_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "restricted"
+ },
+ {
+ "title": "same site iframe, cross origin popup",
+ "iframeOrigin": SAME_SITE,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "restricted"
+ },
+ {
+ "title": "cross origin iframe, cross origin popup",
+ "iframeOrigin": CROSS_ORIGIN,
+ "popupOrigin": CROSS_ORIGIN,
+ "opener": "restricted"
+ }
+].forEach(variant => {
+ subsetTest(
+ iframe_test,
+ `COOP: restrict-properties to popup COOP: restrict-properties via an ` +
+ `iframe, with ${variant.title}`,
+ variant.iframeOrigin,
+ variant.popupOrigin,
+ { coop: 'restrict-properties' },
+ variant.opener);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html.headers
new file mode 100644
index 0000000000..d5c99062d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: restrict-properties
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/named_targeting.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/named_targeting.https.html
new file mode 100644
index 0000000000..10929847ee
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/named_targeting.https.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+
+<script>
+
+async function createCoopRestrictPropertiesPopup(popupToken, name) {
+ const url = SAME_ORIGIN.origin + '/common/dispatcher/executor.html' +
+ `?uuid=${popupToken}` +
+ '&pipe=|header(Cross-Origin-Opener-Policy, restrict-properties)';
+ const popup = window.open(url, name);
+ add_completion_callback(() => popup.close());
+
+ // Wait for the popup to be loaded.
+ const replyToken = token();
+ send(popupToken, `send('${replyToken}', 'Done loading')`);
+ assert_equals(await receive(replyToken), 'Done loading');
+
+ return popup;
+}
+
+promise_test(async t => {
+ // Start by opening a first COOP: restrict-properties popup. No name is set to
+ // begin with.
+ const popupToken1 = token();
+ const popup1 = await createCoopRestrictPropertiesPopup(popupToken1, '');
+
+ // Once the popup is live, explicitly set a name.
+ const name = token();
+ send(popupToken1, `window.name = '${name}'`);
+
+ // To make sure this name has been propagated to other processes, send a dummy
+ // message from the popup to the main page, and wait for it to be received.
+ // It should be delivered after the name change is replicated.
+ const message_waiter = new Promise(resolve => {
+ onmessage = (event) => {
+ if (event.data == 'Waited enough') { resolve(); }
+ }
+ });
+ send(popupToken1, `opener.postMessage('Waited enough', '*')`);
+ await message_waiter;
+
+ // Finally, call window.open with the same name. This should not resolve
+ // across browsing context groups, and create a brand new popup.
+ const popupToken2 = token();
+ const popup2 = await createCoopRestrictPropertiesPopup(popupToken2, name);
+ assert_not_equals(popup1, popup2,
+ 'Named targeting resolved across isolation boundaries');
+
+}, 'Verify that named targeting does not work across isolation boundaries.');
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html
new file mode 100644
index 0000000000..e5313a6e22
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "severed",
+ "origin": SAME_ORIGIN
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "severed",
+ "origin": SAME_SITE
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "severed",
+ "origin": CROSS_ORIGIN
+ }
+].forEach(variant => {
+ popup_test(`${variant.origin.name} ${variant.title}`, variant.origin,
+ { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html.headers
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-so.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html
new file mode 100644
index 0000000000..595a10a84b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": SAME_ORIGIN
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": SAME_SITE
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": CROSS_ORIGIN
+ }
+].forEach(variant => {
+ popup_test(`${variant.origin.name} ${variant.title}`, variant.origin,
+ { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html.headers
new file mode 100644
index 0000000000..d83ed86fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-soap.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin-allow-popups
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-u.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-u.https.html
new file mode 100644
index 0000000000..595a10a84b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-u.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": SAME_ORIGIN
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": SAME_SITE
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": CROSS_ORIGIN
+ }
+].forEach(variant => {
+ popup_test(`${variant.origin.name} ${variant.title}`, variant.origin,
+ { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html
new file mode 100644
index 0000000000..595a10a84b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": SAME_ORIGIN
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": SAME_SITE
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted",
+ "origin": CROSS_ORIGIN
+ }
+].forEach(variant => {
+ popup_test(`${variant.origin.name} ${variant.title}`, variant.origin,
+ { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html.headers
new file mode 100644
index 0000000000..073ce7adfb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-un.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: unsafe-none
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html
new file mode 100644
index 0000000000..a84d52584e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "restricted"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "restricted"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted"
+ }
+].forEach(variant => {
+ popup_test(`Cross-origin ${variant.title}`, CROSS_ORIGIN,
+ { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html.headers
new file mode 100644
index 0000000000..d5c99062d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-cross-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: restrict-properties
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html
new file mode 100644
index 0000000000..c0020fa23a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "restricted"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "restricted"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "preserved"
+ }
+].forEach(variant => {
+ popup_test(`Same-origin ${variant.title}`, SAME_ORIGIN,
+ { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html.headers
new file mode 100644
index 0000000000..d5c99062d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-origin.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: restrict-properties
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html
new file mode 100644
index 0000000000..7d115ac7e6
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../../resources/common.js"></script>
+<script src="../../resources/popup-test.js"></script>
+<script>
+
+[
+ {
+ "title": "popup with empty coop",
+ "coop": "",
+ "opener": "restricted"
+ },
+ {
+ "title": "popup with coop unsafe-none",
+ "coop": "unsafe-none",
+ "opener": "restricted"
+ },
+ {
+ "title": "popup with coop same-origin",
+ "coop": "same-origin",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop same-origin-allow-popups",
+ "coop": "same-origin-allow-popups",
+ "opener": "severed"
+ },
+ {
+ "title": "popup with coop restrict-properties",
+ "coop": "restrict-properties",
+ "opener": "restricted"
+ }
+].forEach(variant => {
+ popup_test(`Same-site ${variant.title}`, SAME_SITE,
+ { coop: variant.coop }, variant.opener);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html.headers
new file mode 100644
index 0000000000..d5c99062d2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/popup-with-same-site.https.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: restrict-properties
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html
new file mode 100644
index 0000000000..9bc171a269
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<title>
+ Verify that we consider browsing context group reuse for COOP reporting.
+</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=f1e361ab5854f2dcfe0224b19bc53199&report_only_id=b6fe666b74547291d52d72790adde05c"></script>
+<script>
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+promise_test(async test => {
+ // To receive reports use the same hard-coded value as the one passed in the
+ // headers and to "reporting-common.js".
+ const report_token = "b6fe666b74547291d52d72790adde05c";
+ const reportTo = reportToHeaders(report_token);
+
+ // 1. Open a popup without any COOP. It should be in a
+ // different virtual browsing context group.
+ const opener_token = token(); // For this window.
+ const initial_openee_token = token();
+ const initial_openee_url = cross_origin + executor_path +
+ `&uuid=${initial_openee_token}`;
+ let openee = window.open(initial_openee_url);
+
+ // 2. Navigate the openee to a COOP-RO: restrict-properties page. If the
+ // policy was enforced, it would live in the same browsing context group as
+ // this page. The virtual browsing context group should similarly be equal.
+ // Note: We omit the reporting endpoint header, because it is not possible to
+ // easily escape it. Since it is not necessary in this test, we skip it.
+ const final_openee_token = token();
+ const final_openee_url = same_origin + executor_path +
+ reportTo.coopReportOnlyRestrictPropertiesHeader +
+ `&uuid=${final_openee_token}`;
+
+ send(initial_openee_token, `location.href = '${final_openee_url}';`);
+ test.add_cleanup(() => send(final_openee_token, "window.close()"));
+
+ // Wait for the final openee to load.
+ send(final_openee_token,
+ `send("${opener_token}", "Ready");
+ `);
+ assert_equals(await receive(opener_token), "Ready");
+
+ // 3. Try to access the openee from the opener. No report should be sent.
+ tryAccess(openee);
+
+ let report =
+ await receiveReport(report_token, "access-from-coop-page-to-openee")
+ assert_equals(report, "timeout");
+
+}, "access-reporting-browsing-context-group-reuse");
+
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html.sub.headers
new file mode 100644
index 0000000000..33abadd83d
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-bcg-reuse.https.html.sub.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy-Report-Only: restrict-properties; report-to="coop-report-only-endpoint"
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=f1e361ab5854f2dcfe0224b19bc53199", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=b6fe666b74547291d52d72790adde05c"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html
new file mode 100644
index 0000000000..b89030f218
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<meta name=timeout content=long>
+<title>Opening a restrict-properties</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=fb054dadb3a9ec17b5cd5c0152d2a7dd&report_only_id=c265b07fbb3bffa2cd2a5179d686ced2"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a same-origin popup with COOP unsafe-none, which mismatches with the
+ // current document (opener) COOP report-only (restrict-properties) values.
+ [
+ SAME_ORIGIN,
+ "unsafe-none",
+ "",
+ "",
+ "",
+ []
+ ],
+
+ // Open a cross-origin popup with COOP unsafe-none, which mismatches with the
+ // current document (opener) COOP report-only (restrict-properties) values.
+ [
+ CROSS_ORIGIN,
+ "unsafe-none",
+ "",
+ "",
+ "",
+ []
+ ],
+
+ // Open a same-origin popup with COOP restrict-properties, which matches with
+ // the current document (opener) COOP report-only (restrict-properties) value.
+ [
+ SAME_ORIGIN,
+ "restrict-properties",
+ "",
+ "",
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html.sub.headers
new file mode 100644
index 0000000000..07ecad96f2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp-ro.https.html.sub.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy-Report-Only: restrict-properties; report-to="coop-report-only-endpoint"
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=fb054dadb3a9ec17b5cd5c0152d2a7dd", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=c265b07fbb3bffa2cd2a5179d686ced2"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html
new file mode 100644
index 0000000000..6b31f7e009
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<meta name=timeout content=long>
+<title>Opening a restrict-properties</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script
+ src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js?pipe=sub&report_id=ed5a8be35e4e21c2ba960e6574e0a32c&report_only_id=fa22ddc676642edae42c75defb82ba2e"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a same-origin popup with COOP unsafe-none, which mismatches
+ // with the current document (opener) COOP (restrict-properties) values.
+ [
+ SAME_ORIGIN,
+ "unsafe-none",
+ "",
+ "",
+ "",
+ []
+ ],
+
+ // Open a cross-origin popup with COOP unsafe-none, which mismatches
+ // with the current document (opener) COOP (restrict-properties) values.
+ [
+ CROSS_ORIGIN,
+ "unsafe-none",
+ "",
+ "",
+ "",
+ []
+ ],
+
+ // Open a same-origin popup with COOP restrict-properties, which matches with
+ // the current document (opener) COOP (restrict-properties) value.
+ [
+ SAME_ORIGIN,
+ "restrict-properties",
+ "",
+ "",
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html.sub.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html.sub.headers
new file mode 100644
index 0000000000..a61e2919c8
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-from-rp.https.html.sub.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: restrict-properties; report-to="coop-report-endpoint"
+Reporting-Endpoints: coop-report-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=ed5a8be35e4e21c2ba960e6574e0a32c", coop-report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=fa22ddc676642edae42c75defb82ba2e"
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html
new file mode 100644
index 0000000000..c47e59cd8f
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a same-origin popup with COOP report-only restrict-properties, which
+ // mismatches with the current document (opener) COOP (unsafe-none).
+ [
+ SAME_ORIGIN,
+ "",
+ "",
+ `restrict-properties; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ []
+ ],
+
+ // Open a cross-origin popup with COOP report-only restrict-properties, which
+ // mismatches with the current document (opener) COOP (unsafe-none).
+ [
+ CROSS_ORIGIN,
+ "",
+ "",
+ `restrict-properties; report-to="${popupReportOnlyEndpoint.name}"`,
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html.headers
new file mode 100644
index 0000000000..16903320bb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp-ro.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: unsafe-none
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html
new file mode 100644
index 0000000000..ff60e8c5af
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<meta name=timeout content=long>
+<title>reporting same origin with report-to</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/cross-origin-opener-policy/resources/common.js"></script>
+<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script>
+
+<script>
+
+let tests = [
+ // popup origin, popup COOP, popup COEP, popup COOP report-only, popup COEP report-only, expected reports
+
+ // Open a same-origin popup with COOP report-only restrict-properties, which
+ // mismatches with the current document (opener) COOP (unsafe-none).
+ [
+ SAME_ORIGIN,
+ `restrict-properties; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ []
+ ],
+
+ // Open a cross-origin popup with COOP report-only restrict-properties, which
+ // mismatches with the current document (opener) COOP (unsafe-none).
+ [
+ CROSS_ORIGIN,
+ `restrict-properties; report-to="${popupReportEndpoint.name}"`,
+ "",
+ "",
+ "",
+ []
+ ],
+];
+
+runNavigationReportingTests(document.title, tests);
+
+</script>
+
diff --git a/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html.headers b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html.headers
new file mode 100644
index 0000000000..16903320bb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/tentative/restrict-properties/reporting-to-rp.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: unsafe-none
+Referrer-Policy: origin
diff --git a/testing/web-platform/tests/html/dom/aria-attribute-reflection.html b/testing/web-platform/tests/html/dom/aria-attribute-reflection.html
new file mode 100644
index 0000000000..fa5e9ad5c7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/aria-attribute-reflection.html
@@ -0,0 +1,487 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8" />
+<title>Element Reflection for ARIA properties</title>
+<link rel=help href="https://wicg.github.io/aom/spec/aria-reflection.html">
+<link rel="author" title="Meredith Lane" href="meredithl@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+function testNullable(element, jsAttr, contentAttr) {
+ var originalValue = element[jsAttr];
+ assert_false(originalValue === null);
+ element[jsAttr] = null;
+ assert_equals(element[jsAttr], null);
+ assert_false(element.hasAttribute(contentAttr));
+ // Setting to undefined results in same state as setting to null.
+ element[jsAttr] = originalValue;
+ element[jsAttr] = undefined;
+ assert_equals(element[jsAttr], null);
+ assert_false(element.hasAttribute(contentAttr));
+}
+</script>
+
+<div id="role" role="button"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("role");
+ assert_equals(element.role, "button");
+ element.role = "checkbox";
+ assert_equals(element.getAttribute("role"), "checkbox");
+ testNullable(element, "role", "role");
+}, "role attribute reflects.");
+</script>
+
+<div id="atomic" aria-atomic="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("atomic");
+ assert_equals(element.ariaAtomic, "true");
+ element.ariaAtomic = "false";
+ assert_equals(element.getAttribute("aria-atomic"), "false");
+ testNullable(element, "ariaAtomic", "aria-atomic");
+}, "aria-atomic attribute reflects.");
+</script>
+
+<div id="autocomplete" aria-autocomplete="list"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("autocomplete");
+ assert_equals(element.ariaAutoComplete, "list");
+ element.ariaAutoComplete = "inline";
+ assert_equals(element.getAttribute("aria-autocomplete"), "inline");
+ testNullable(element, "ariaAutoComplete", "aria-autocomplete");
+}, "aria-autocomplete attribute reflects.");
+</script>
+
+<div id="busy" aria-busy="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("busy");
+ assert_equals(element.ariaBusy, "true");
+ element.ariaBusy = "false";
+ assert_equals(element.getAttribute("aria-busy"), "false");
+ testNullable(element, "ariaBusy", "aria-busy");
+}, "aria-busy attribute reflects.");
+</script>
+
+<div id="checked" aria-checked="mixed"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("checked");
+ assert_equals(element.ariaChecked, "mixed");
+ element.ariaChecked = "true";
+ assert_equals(element.getAttribute("aria-checked"), "true");
+ testNullable(element, "ariaChecked", "aria-checked");
+}, "aria-checked attribute reflects.");
+</script>
+
+<div id="colcount" aria-colcount="5"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("colcount");
+ assert_equals(element.ariaColCount, "5");
+ element.ariaColCount = "6";
+ assert_equals(element.getAttribute("aria-colcount"), "6");
+ testNullable(element, "ariaColCount", "aria-colcount");
+}, "aria-colcount attribute reflects.");
+</script>
+
+<div id="colindex" aria-colindex="1"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("colindex");
+ assert_equals(element.ariaColIndex, "1");
+ element.ariaColIndex = "2";
+ assert_equals(element.getAttribute("aria-colindex"), "2");
+ testNullable(element, "ariaColIndex", "aria-colindex");
+}, "aria-colindex attribute reflects.");
+</script>
+
+<div id="colspan" aria-colspan="2"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("colspan");
+ assert_equals(element.ariaColSpan, "2");
+ element.ariaColSpan = "3";
+ assert_equals(element.getAttribute("aria-colspan"), "3");
+ testNullable(element, "ariaColSpan", "aria-colspan");
+}, "aria-colspan attribute reflects.");
+</script>
+
+<div id="current" aria-current="page"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("current");
+ assert_equals(element.ariaCurrent, "page");
+ element.ariaCurrent = "step";
+ assert_equals(element.getAttribute("aria-current"), "step");
+ testNullable(element, "ariaCurrent", "aria-current");
+}, "aria-current attribute reflects.");
+</script>
+
+<div id="disabled" aria-disabled="true"></div>
+
+<div id="description" aria-description="cold as ice"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("description");
+ assert_equals(element.ariaDescription, "cold as ice");
+ element.ariaDescription = "hot as fire";
+ assert_equals(element.getAttribute("aria-description"), "hot as fire");
+ testNullable(element, "ariaDescription", "aria-description");
+}, "aria-description attribute reflects.");
+</script>
+
+<script>
+test(function(t) {
+ var element = document.getElementById("disabled");
+ assert_equals(element.ariaDisabled, "true");
+ element.ariaDisabled = "false";
+ assert_equals(element.getAttribute("aria-disabled"), "false");
+ testNullable(element, "ariaDisabled", "aria-disabled");
+}, "aria-disabled attribute reflects.");
+</script>
+
+<div id="expanded" aria-expanded="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("expanded");
+ assert_equals(element.ariaExpanded, "true");
+ element.ariaExpanded = "false";
+ assert_equals(element.getAttribute("aria-expanded"), "false");
+ testNullable(element, "ariaExpanded", "aria-expanded");
+}, "aria-expanded attribute reflects.");
+</script>
+
+<div id="haspopup" aria-haspopup="menu"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("haspopup");
+ assert_equals(element.ariaHasPopup, "menu");
+ element.ariaHasPopup = "listbox";
+ assert_equals(element.getAttribute("aria-haspopup"), "listbox");
+ testNullable(element, "ariaHasPopup", "aria-haspopup");
+}, "aria-haspopup attribute reflects.");
+</script>
+
+<div id="hidden" aria-hidden="true" tabindex="-1"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("hidden");
+ assert_equals(element.ariaHidden, "true");
+ element.ariaHidden = "false";
+ assert_equals(element.getAttribute("aria-hidden"), "false");
+ testNullable(element, "ariaHidden", "aria-hidden");
+}, "aria-hidden attribute reflects.");
+</script>
+
+<div id="invalid" aria-invalid="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("invalid");
+ assert_equals(element.ariaInvalid, "true");
+ element.ariaInvalid = "grammar";
+ assert_equals(element.getAttribute("aria-invalid"), "grammar");
+ testNullable(element, "ariaInvalid", "aria-invalid");
+}, "aria-invalid attribute reflects.");
+</script>
+
+<div id="keyshortcuts" aria-keyshortcuts="x"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("keyshortcuts");
+ assert_equals(element.ariaKeyShortcuts, "x");
+ element.ariaKeyShortcuts = "y";
+ assert_equals(element.getAttribute("aria-keyshortcuts"), "y");
+ testNullable(element, "ariaKeyShortcuts", "aria-keyshortcuts");
+}, "aria-keyshortcuts attribute reflects.");
+</script>
+
+<div id="label" aria-label="x"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("label");
+ assert_equals(element.ariaLabel, "x");
+ element.ariaLabel = "y";
+ assert_equals(element.getAttribute("aria-label"), "y");
+ testNullable(element, "ariaLabel", "aria-label");
+}, "aria-label attribute reflects.");
+</script>
+
+<div id="level" aria-level="1"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("level");
+ assert_equals(element.ariaLevel, "1");
+ element.ariaLevel = "2";
+ assert_equals(element.getAttribute("aria-level"), "2");
+ testNullable(element, "ariaLevel", "aria-level");
+}, "aria-level attribute reflects.");
+</script>
+
+<div id="live" aria-live="polite"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("live");
+ assert_equals(element.ariaLive, "polite");
+ element.ariaLive = "assertive";
+ assert_equals(element.getAttribute("aria-live"), "assertive");
+ testNullable(element, "ariaLive", "aria-live");
+}, "aria-live attribute reflects.");
+</script>
+
+<div id="modal" aria-modal="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("modal");
+ assert_equals(element.ariaModal, "true");
+ element.ariaModal = "false";
+ assert_equals(element.getAttribute("aria-modal"), "false");
+ testNullable(element, "ariaModal", "aria-modal");
+}, "aria-modal attribute reflects.");
+</script>
+
+<div id="multiline" aria-multiline="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("multiline");
+ assert_equals(element.ariaMultiLine, "true");
+ element.ariaMultiLine = "false";
+ assert_equals(element.getAttribute("aria-multiline"), "false");
+ testNullable(element, "ariaMultiLine", "aria-multiline");
+}, "aria-multiline attribute reflects.");
+</script>
+
+<div id="multiselectable" aria-multiselectable="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("multiselectable");
+ assert_equals(element.ariaMultiSelectable, "true");
+ element.ariaMultiSelectable = "false";
+ assert_equals(element.getAttribute("aria-multiselectable"), "false");
+ testNullable(element, "ariaMultiSelectable", "aria-multiselectable");
+}, "aria-multiselectable attribute reflects.");
+</script>
+
+<div id="orientation" aria-orientation="vertical"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("orientation");
+ assert_equals(element.ariaOrientation, "vertical");
+ element.ariaOrientation = "horizontal";
+ assert_equals(element.getAttribute("aria-orientation"), "horizontal");
+ testNullable(element, "ariaOrientation", "aria-orientation");
+}, "aria-orientation attribute reflects.");
+</script>
+
+<div id="placeholder" aria-placeholder="x"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("placeholder");
+ assert_equals(element.ariaPlaceholder, "x");
+ element.ariaPlaceholder = "y";
+ assert_equals(element.getAttribute("aria-placeholder"), "y");
+ testNullable(element, "ariaPlaceholder", "aria-placeholder");
+}, "aria-placeholder attribute reflects.");
+</script>
+
+<div id="posinset" aria-posinset="10"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("posinset");
+ assert_equals(element.ariaPosInSet, "10");
+ element.ariaPosInSet = "11";
+ assert_equals(element.getAttribute("aria-posinset"), "11");
+ testNullable(element, "ariaPosInSet", "aria-posinset");
+}, "aria-posinset attribute reflects.");
+</script>
+
+<button id="pressed" aria-pressed="true"></button>
+
+<script>
+test(function(t) {
+ var element = document.getElementById("pressed");
+ assert_equals(element.ariaPressed, "true");
+ element.ariaPressed = "false";
+ assert_equals(element.getAttribute("aria-pressed"), "false");
+ testNullable(element, "ariaPressed", "aria-pressed");
+}, "aria-pressed attribute reflects.");
+</script>
+
+<div id="readonly" aria-readonly="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("readonly");
+ assert_equals(element.ariaReadOnly, "true");
+ element.ariaReadOnly = "false";
+ assert_equals(element.getAttribute("aria-readonly"), "false");
+ testNullable(element, "ariaReadOnly", "aria-readonly");
+}, "aria-readonly attribute reflects.");
+</script>
+
+<div id="relevant" aria-relevant="text"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("relevant");
+ assert_equals(element.ariaRelevant, "text");
+ element.ariaRelevant = "removals";
+ assert_equals(element.getAttribute("aria-relevant"), "removals");
+ testNullable(element, "ariaRelevant", "aria-relevant");
+}, "aria-relevant attribute reflects.");
+</script>
+
+<div id="required" aria-required="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("required");
+ assert_equals(element.ariaRequired, "true");
+ element.ariaRequired = "false";
+ assert_equals(element.getAttribute("aria-required"), "false");
+ testNullable(element, "ariaRequired", "aria-required");
+}, "aria-required attribute reflects.");
+</script>
+
+<div id="roledescription" aria-roledescription="x"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("roledescription");
+ assert_equals(element.ariaRoleDescription, "x");
+ element.ariaRoleDescription = "y";
+ assert_equals(element.getAttribute("aria-roledescription"), "y");
+ testNullable(element, "ariaRoleDescription", "aria-roledescription");
+}, "aria-roledescription attribute reflects.");
+</script>
+
+<div id="rowcount" aria-rowcount="10"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("rowcount");
+ assert_equals(element.ariaRowCount, "10");
+ element.ariaRowCount = "11";
+ assert_equals(element.getAttribute("aria-rowcount"), "11");
+ testNullable(element, "ariaRowCount", "aria-rowcount");
+}, "aria-rowcount attribute reflects.");
+</script>
+
+<div id="rowindex" aria-rowindex="1"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("rowindex");
+ assert_equals(element.ariaRowIndex, "1");
+ element.ariaRowIndex = "2";
+ assert_equals(element.getAttribute("aria-rowindex"), "2");
+ testNullable(element, "ariaRowIndex", "aria-rowindex");
+}, "aria-rowindex attribute reflects.");
+</script>
+
+<div id="rowspan" aria-rowspan="2"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("rowspan");
+ assert_equals(element.ariaRowSpan, "2");
+ element.ariaRowSpan = "3";
+ assert_equals(element.getAttribute("aria-rowspan"), "3");
+ testNullable(element, "ariaRowSpan", "aria-rowspan");
+}, "aria-rowspan attribute reflects.");
+</script>
+
+<div id="selected" aria-selected="true"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("selected");
+ assert_equals(element.ariaSelected, "true");
+ element.ariaSelected = "false";
+ assert_equals(element.getAttribute("aria-selected"), "false");
+ testNullable(element, "ariaSelected", "aria-selected");
+}, "aria-selected attribute reflects.");
+</script>
+
+<div id="setsize" aria-setsize="10"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("setsize");
+ assert_equals(element.ariaSetSize, "10");
+ element.ariaSetSize = "11";
+ assert_equals(element.getAttribute("aria-setsize"), "11");
+ testNullable(element, "ariaSetSize", "aria-setsize");
+}, "aria-setsize attribute reflects.");
+</script>
+
+<div id="sort" aria-sort="descending"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("sort");
+ assert_equals(element.ariaSort, "descending");
+ element.ariaSort = "ascending";
+ assert_equals(element.getAttribute("aria-sort"), "ascending");
+ testNullable(element, "ariaSort", "aria-sort");
+}, "aria-sort attribute reflects.");
+</script>
+
+<div id="valuemax" aria-valuemax="99"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("valuemax");
+ assert_equals(element.ariaValueMax, "99");
+ element.ariaValueMax = "100";
+ assert_equals(element.getAttribute("aria-valuemax"), "100");
+ testNullable(element, "ariaValueMax", "aria-valuemax");
+}, "aria-valuemax attribute reflects.");
+</script>
+
+<div id="valuemin" aria-valuemin="3"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("valuemin");
+ assert_equals(element.ariaValueMin, "3");
+ element.ariaValueMin = "2";
+ assert_equals(element.getAttribute("aria-valuemin"), "2");
+ testNullable(element, "ariaValueMin", "aria-valuemin");
+}, "aria-valuemin attribute reflects.");
+</script>
+
+<div id="valuenow" aria-valuenow="50"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("valuenow");
+ assert_equals(element.ariaValueNow, "50");
+ element.ariaValueNow = "51";
+ assert_equals(element.getAttribute("aria-valuenow"), "51");
+ testNullable(element, "ariaValueNow", "aria-valuenow");
+}, "aria-valuenow attribute reflects.");
+</script>
+
+<div id="valuetext" aria-valuetext="50%"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("valuetext");
+ assert_equals(element.ariaValueText, "50%");
+ element.ariaValueText = "51%";
+ assert_equals(element.getAttribute("aria-valuetext"), "51%");
+ testNullable(element, "ariaValueText", "aria-valuetext");
+}, "aria-valuetext attribute reflects.");
+</script>
+
+<div id="braillelabel" aria-braillelabel="x"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("braillelabel");
+ assert_equals(element.ariaBrailleLabel, "x");
+ element.ariaBrailleLabel = "y";
+ assert_equals(element.getAttribute("aria-braillelabel"), "y");
+ testNullable(element, "ariaBrailleLabel", "aria-braillelabel");
+}, "aria-braillelabel attribute reflects.");
+</script>
+
+<div id="brailleroledescription" aria-brailleroledescription="x"></div>
+<script>
+test(function(t) {
+ var element = document.getElementById("brailleroledescription");
+ assert_equals(element.ariaBrailleRoleDescription, "x");
+ element.ariaBrailleRoleDescription = "y";
+ assert_equals(element.getAttribute("aria-brailleroledescription"), "y");
+ testNullable(element, "ariaBrailleRoleDescription", "aria-brailleroledescription");
+}, "aria-brailleroledescription attribute reflects.");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/dom/aria-element-reflection-disconnected.html b/testing/web-platform/tests/html/dom/aria-element-reflection-disconnected.html
new file mode 100644
index 0000000000..d3acc35e9f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/aria-element-reflection-disconnected.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8" />
+<link rel=help href="https://html.spec.whatwg.org/#attr-associated-element">
+<link rel="author" href="masonf@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=single_element>
+ <input aria-activedescendant=foo>
+ <p id=foo></p>
+</div>
+
+<div id=array>
+ <input aria-describedby="foo1 foo2">
+ <div id=targets>
+ <p id=foo1></p>
+ <p id=foo2></p>
+ </div>
+</div>
+
+<script>
+ function isIterable(obj) {
+ if (obj === null)
+ return false;
+ return typeof obj[Symbol.iterator] === 'function';
+ }
+ function toSet(elementOrList) {
+ if (!isIterable(elementOrList)) {
+ return new Set([elementOrList]);
+ }
+ return new Set(elementOrList);
+ }
+ function assert_equal_elements(arr1, arr2, msg) {
+ msg = msg || "Arrays not equal";
+ arr1 = toSet(arr1);
+ arr2 = toSet(arr2);
+ assert_true(arr1.size === arr2.size &&
+ [...arr1].every((x) => arr2.has(x)), msg);
+ }
+ function single_test(container, targets, contentAttr, idlAttr, isSingleTarget) {
+ // Start with idref-based reference:
+ const el = container.querySelector('input');
+ assert_true(el.getAttribute(contentAttr) != null && el.getAttribute(contentAttr) != '','Should start with idref attribute');
+ assert_equal_elements(el[idlAttr],targets);
+ container.remove();
+ assert_equal_elements(el[idlAttr],targets,'idrefs should continue to work when target is disconnected');
+ document.body.appendChild(container);
+ assert_equal_elements(el[idlAttr],targets,'functional when reconnected');
+
+ // Now set up an attr-associated element:
+ el[idlAttr] = isSingleTarget ? targets[0] : targets;
+ assert_equal_elements(el[idlAttr],targets);
+ assert_equals(el.getAttribute(contentAttr),'','Content attribute is present but empty');
+ container.remove();
+ assert_equal_elements(el[idlAttr],targets,'attr-associated element still functional');
+ assert_equals(el.getAttribute(contentAttr),'','Attribute still blank');
+ document.body.appendChild(container);
+ assert_equal_elements(el[idlAttr],targets,'still functional when reconnected');
+
+ // Sanity check:
+ el.removeAttribute(contentAttr);
+ assert_equal_elements(el[idlAttr],null);
+ assert_equals(el.getAttribute(contentAttr),null);
+ container.remove();
+ assert_equal_elements(el[idlAttr],null);
+ assert_equals(el.getAttribute(contentAttr),null);
+ document.body.appendChild(container);
+ }
+
+ test(() => {
+ const container = document.getElementById('single_element');
+ const targets = [container.querySelector('#foo')];
+ single_test(container, targets, 'aria-activedescendant', 'ariaActiveDescendantElement', true);
+ },'Element references should stay valid when content is disconnected (single element)');
+
+ test(() => {
+ const container = document.getElementById('array');
+ const targets = Array.from(container.querySelector('#targets').children);
+ single_test(container, targets, 'aria-describedby', 'ariaDescribedByElements', false);
+ },'Element references should stay valid when content is disconnected (element array)');
+</script>
+
diff --git a/testing/web-platform/tests/html/dom/aria-element-reflection.html b/testing/web-platform/tests/html/dom/aria-element-reflection.html
new file mode 100644
index 0000000000..bdf2450708
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/aria-element-reflection.html
@@ -0,0 +1,846 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Element Reflection for aria-activedescendant and aria-errormessage</title>
+ <link rel=help href="https://whatpr.org/html/3917/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:element">
+ <link rel="author" title="Meredith Lane" href="meredithl@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <div id="activedescendant" aria-activedescendant="x"></div>
+
+ <div id="parentListbox" role="listbox" aria-activedescendant="i1">
+ <div role="option" id="i1">Item 1</div>
+ <div role="option" id="i2">Item 2</div>
+ </div>
+
+ <script>
+ test(function(t) {
+ assert_equals(activedescendant.ariaActiveDescendantElement, null,
+ "invalid ID for relationship returns null");
+
+ // Element reference should be set if the content attribute was included.
+ assert_equals(parentListbox.getAttribute("aria-activedescendant"), "i1", "check content attribute after parsing.");
+ assert_equals(parentListbox.ariaActiveDescendantElement, i1, "check idl attribute after parsing.");
+ assert_equals(parentListbox.ariaActiveDescendantElement, parentListbox.ariaActiveDescendantElement, "check idl attribute caching after parsing.");
+
+ // If we set the content attribute, the element reference should reflect this.
+ parentListbox.setAttribute("aria-activedescendant", "i2");
+ assert_equals(parentListbox.ariaActiveDescendantElement, i2, "setting the content attribute updates the element reference.");
+ assert_equals(parentListbox.ariaActiveDescendantElement, parentListbox.ariaActiveDescendantElement, "check idl attribute caching after update.");
+
+ // Setting the element reference should set the empty string in the content attribute.
+ parentListbox.ariaActiveDescendantElement = i1;
+ assert_equals(parentListbox.ariaActiveDescendantElement, i1, "getter should return the right element reference.");
+ assert_equals(parentListbox.getAttribute("aria-activedescendant"), "", "content attribute should be empty.");
+
+ // Both content and IDL attribute should be nullable.
+ parentListbox.ariaActiveDescendantElement = null;
+ assert_equals(parentListbox.ariaActiveDescendantElement, null);
+ assert_false(parentListbox.hasAttribute("aria-activedescendant"));
+ assert_equals(parentListbox.getAttribute("aria-activedescendant"), null, "nullifying the idl attribute removes the content attribute.");
+
+ // Setting content attribute to non-existent or non compatible element should nullify the IDL attribute.
+ // Reset the element to an existant one.
+ parentListbox.setAttribute("aria-activedescendant", "i1");
+ assert_equals(parentListbox.ariaActiveDescendantElement, i1, "reset attribute.");
+
+ parentListbox.setAttribute("aria-activedescendant", "non-existent-element");
+ assert_equals(parentListbox.getAttribute("aria-activedescendant"), "non-existent-element");
+ assert_equals(parentListbox.ariaActiveDescendantElement, null,"non-DOM content attribute should null the element reference");
+ }, "aria-activedescendant element reflection");
+ </script>
+
+ <div id="parentListbox2" role="listbox" aria-activedescendant="option1">
+ <div role="option" id="option1">Item 1</div>
+ <div role="option" id="option2">Item 2</div>
+ </div>
+
+ <script>
+ test(function(t) {
+ const option1 = document.getElementById("option1");
+ const option2 = document.getElementById("option2");
+ assert_equals(parentListbox2.ariaActiveDescendantElement, option1);
+ option1.removeAttribute("id");
+ option2.setAttribute("id", "option1");
+ const option2Duplicate = document.getElementById("option1");
+ assert_equals(option2, option2Duplicate);
+
+ assert_equals(parentListbox2.ariaActiveDescendantElement, option2);
+ }, "If the content attribute is set directly, the IDL attribute getter always returns the first element whose ID matches the content attribute.");
+ </script>
+
+ <div id="blankIdParent" role="listbox">
+ <div role="option" id="multiple-id"></div>
+ <div role="option" id="multiple-id"></div>
+ </div>
+
+ <script>
+ test(function(t) {
+ // Get second child of parent. This violates the setting of a reflected element
+ // as it will not be the first child of the parent with that ID, which should
+ // result in an empty string for the content attribute.
+ blankIdParent.ariaActiveDescendantElement = blankIdParent.children[1];
+ assert_true(blankIdParent.hasAttribute("aria-activedescendant"));
+ assert_equals(blankIdParent.getAttribute("aria-activedescendant"), "");
+ assert_equals(blankIdParent.ariaActiveDescendantElement, blankIdParent.children[1]);
+ }, "Setting the IDL attribute to an element which is not the first element in DOM order with its ID causes the content attribute to be an empty string");
+ </script>
+
+ <div id="outerContainer">
+ <p id="lightParagraph">Hello world!</p>
+ <span id="shadowHost">
+ </span>
+ </div>
+
+ <script>
+ test(function(t) {
+ const shadow = shadowHost.attachShadow({mode: "open"});
+ const link = document.createElement("a");
+ shadow.appendChild(link);
+
+ assert_equals(lightParagraph.ariaActiveDescendantElement, null);
+
+ // The given element crosses a shadow dom boundary, so it cannot be
+ // set as an element reference.
+ lightParagraph.ariaActiveDescendantElement = link;
+ assert_equals(lightParagraph.ariaActiveDescendantElement, null);
+
+ // The given element crosses a shadow dom boundary (upwards), so
+ // can be used as an element reference, but the content attribute
+ // should reflect the empty string.
+ link.ariaActiveDescendantElement = lightParagraph;
+ assert_equals(link.ariaActiveDescendantElement, lightParagraph);
+ assert_equals(link.getAttribute("aria-activedescendant"), "");
+ }, "Setting an element reference that crosses into a shadow tree is disallowed, but setting one that is in a shadow inclusive ancestor is allowed.");
+ </script>
+
+ <input id="startTime" ></input>
+ <span id="errorMessage">Invalid Time</span>
+
+ <script>
+ test(function(t) {
+ startTime.ariaErrorMessageElements = [errorMessage];
+ assert_equals(startTime.getAttribute("aria-errormessage"), "");
+ assert_array_equals(startTime.ariaErrorMessageElements, [errorMessage]);
+
+ startTime.ariaErrorMessageElements = [];
+ assert_array_equals(startTime.ariaErrorMessageElements, []);
+ assert_equals(startTime.getAttribute("aria-errormessage"), "");
+
+ startTime.setAttribute("aria-errormessage", "errorMessage");
+ assert_array_equals(startTime.ariaErrorMessageElements, [errorMessage]);
+
+ }, "aria-errormessage");
+
+ test(function (t) {
+ assert_false('ariaErrorMessageElement' in startTime);
+ }, 'ariaErrorMessageElement is not defined')
+
+ </script>
+
+ <label>
+ Password:
+ <input id="passwordField" type="password" aria-details="pw">
+ </label>
+
+ <ul>
+ <li id="listItem1">First description.</li>
+ <li id="listItem2">Second description.</li>
+ </ul>
+
+ <script>
+
+ test(function(t) {
+ assert_array_equals(passwordField.ariaDetailsElements, []);
+ passwordField.ariaDetailsElements = [ listItem1 ];
+ assert_equals(passwordField.getAttribute("aria-details"), "");
+ assert_array_equals(passwordField.ariaDetailsElements, [ listItem1 ]);
+
+ passwordField.ariaDetailsElements = [ listItem2 ];
+ assert_equals(passwordField.getAttribute("aria-details"), "");
+ assert_array_equals(passwordField.ariaDetailsElements, [ listItem2 ]);
+ }, "aria-details");
+ </script>
+
+ <div id="deletionParent" role="listbox" aria-activedescendant="contentAttrElement">
+ <div role="option" id="contentAttrElement">Item 1</div>
+ <div role="option" id="idlAttrElement">Item 2</div>
+ </div>
+
+ <script>
+
+ test(function(t) {
+ const contentAttrElement = document.getElementById("contentAttrElement");
+ const idlAttrElement = document.getElementById("idlAttrElement");
+
+ assert_equals(deletionParent.getAttribute("aria-activedescendant"), "contentAttrElement");
+ assert_equals(deletionParent.ariaActiveDescendantElement, contentAttrElement);
+
+ // Deleting an element set via the content attribute.
+ deletionParent.removeChild(contentAttrElement);
+ assert_equals(deletionParent.getAttribute("aria-activedescendant"), "contentAttrElement");
+
+ // As it was not explitly set, the attr-associated-element is computed from the content attribute,
+ // and since descendant1 has been removed from the DOM, it is not valid.
+ assert_equals(deletionParent.ariaActiveDescendantElement, null);
+
+ // Deleting an element set via the IDL attribute.
+ deletionParent.ariaActiveDescendantElement = idlAttrElement;
+ assert_equals(deletionParent.getAttribute("aria-activedescendant"), "");
+
+ deletionParent.removeChild(idlAttrElement);
+ assert_equals(deletionParent.ariaActiveDescendantElement, null);
+
+ // The content attribute is still empty.
+ assert_equals(deletionParent.getAttribute("aria-activedescendant"), "");
+ }, "Deleting a reflected element should return null for the IDL attribute and the content attribute will be empty.");
+ </script>
+
+ <div id="parentNode" role="listbox" aria-activedescendant="changingIdElement">
+ <div role="option" id="changingIdElement">Item 1</div>
+ <div role="option" id="persistantIDElement">Item 2</div>
+ </div>
+
+ <script>
+ test(function(t) {
+ const changingIdElement = document.getElementById("changingIdElement");
+ assert_equals(parentNode.ariaActiveDescendantElement, changingIdElement);
+
+ // Modify the id attribute.
+ changingIdElement.setAttribute("id", "new-id");
+
+ // The content attribute still reflects the old id, and we expect the
+ // Element reference to be null as there is no DOM node with id "original"
+ assert_equals(parentNode.getAttribute("aria-activedescendant"), "changingIdElement");
+ assert_equals(parentNode.ariaActiveDescendantElement, null, "Element set via content attribute with a changed id will return null on getting");
+
+ parentNode.ariaActiveDescendantElement = changingIdElement;
+ assert_equals(parentNode.getAttribute("aria-activedescendant"), "");
+ assert_equals(parentNode.ariaActiveDescendantElement, changingIdElement);
+
+ // The explicitly set element takes precendance over the content attribute.
+ // This means that we still return the same element reference, but the
+ // content attribute is empty.
+ changingIdElement.setAttribute("id", "newer-id");
+ assert_equals(parentNode.ariaActiveDescendantElement, changingIdElement, "explicitly set element is still present even after the id has been changed");
+ assert_equals(parentNode.getAttribute("aria-activedescendant"), "", "content attribute is empty.");
+ }, "Changing the ID of an element doesn't lose the reference.");
+ </script>
+
+ <!-- TODO(chrishall): change naming scheme to inner/outer -->
+ <div id="lightParent" role="listbox">
+ <div id="lightElement" role="option">Hello world!</div>
+ </div>
+ <div id="shadowHostElement"></div>
+
+ <script>
+ test(function(t) {
+ const lightElement = document.getElementById("lightElement");
+ const shadowRoot = shadowHostElement.attachShadow({mode: "open"});
+
+ assert_equals(lightParent.ariaActiveDescendantElement, null, 'null before');
+ assert_equals(lightParent.getAttribute('aria-activedescendant'), null, 'null before');
+
+ lightParent.ariaActiveDescendantElement = lightElement;
+ assert_equals(lightParent.ariaActiveDescendantElement, lightElement);
+ assert_equals(lightParent.getAttribute('aria-activedescendant'), "");
+
+ // Move the referenced element into shadow DOM.
+ // This will cause the computed attr-associated element to be null as the
+ // referenced element will no longer be in a valid scope.
+ // The underlying reference is kept intact, so if the referenced element is
+ // later restored to a valid scope the computed attr-associated element will
+ // then reflect
+ shadowRoot.appendChild(lightElement);
+ assert_equals(lightParent.ariaActiveDescendantElement, null, "computed attr-assoc element should be null as referenced element is in an invalid scope");
+ assert_equals(lightParent.getAttribute("aria-activedescendant"), "");
+
+ // Move the referenced element back into light DOM.
+ // Since the underlying reference was kept intact, after moving the
+ // referenced element back to a valid scope should be reflected in the
+ // computed attr-associated element.
+ lightParent.appendChild(lightElement);
+ assert_equals(lightParent.ariaActiveDescendantElement, lightElement, "computed attr-assoc element should be restored as referenced element is back in a valid scope");
+ assert_equals(lightParent.getAttribute("aria-activedescendant"), "");
+ }, "Reparenting an element into a descendant shadow scope hides the element reference.");
+ </script>
+
+ <div id='fruitbowl' role='listbox'>
+ <div id='apple' role='option'>I am an apple</div>
+ <div id='pear' role='option'>I am a pear</div>
+ <div id='banana' role='option'>I am a banana</div>
+ </div>
+ <div id='shadowFridge'></div>
+
+ <script>
+ test(function(t) {
+ const shadowRoot = shadowFridge.attachShadow({mode: "open"});
+ const banana = document.getElementById("banana");
+
+ fruitbowl.ariaActiveDescendantElement = apple;
+ assert_equals(fruitbowl.ariaActiveDescendantElement, apple);
+ assert_equals(fruitbowl.getAttribute("aria-activedescendant"), "");
+
+ // Move the referenced element into shadow DOM.
+ shadowRoot.appendChild(apple);
+ assert_equals(fruitbowl.ariaActiveDescendantElement, null, "computed attr-assoc element should be null as referenced element is in an invalid scope");
+ // The content attribute is still empty.
+ assert_equals(fruitbowl.getAttribute("aria-activedescendant"), "");
+
+ // let us rename our banana to an apple
+ banana.setAttribute("id", "apple");
+ const lyingBanana = document.getElementById("apple");
+ assert_equals(lyingBanana, banana);
+
+ // our ariaActiveDescendantElement thankfully isn't tricked.
+ // this is thanks to the underlying reference being kept intact, it is
+ // checked and found to be in an invalid scope.
+ assert_equals(fruitbowl.ariaActiveDescendantElement, null);
+ // our content attribute is empty.
+ assert_equals(fruitbowl.getAttribute("aria-activedescendant"), "");
+
+ // when we remove our IDL attribute, the content attribute is also thankfully cleared.
+ fruitbowl.ariaActiveDescendantElement = null;
+ assert_equals(fruitbowl.ariaActiveDescendantElement, null);
+ assert_equals(fruitbowl.getAttribute("aria-activedescendant"), null);
+ }, "Reparenting referenced element cannot cause retargeting of reference.");
+ </script>
+
+ <div id='toaster' role='listbox'></div>
+ <div id='shadowPantry'></div>
+
+ <script>
+ test(function(t) {
+ const shadowRoot = shadowPantry.attachShadow({mode: "open"});
+
+ // Our toast starts in the shadowPantry.
+ const toast = document.createElement("div");
+ toast.setAttribute("id", "toast");
+ shadowRoot.appendChild(toast);
+
+ // Prepare my toast for toasting
+ toaster.ariaActiveDescendantElement = toast;
+ assert_equals(toaster.ariaActiveDescendantElement, null);
+ assert_equals(toaster.getAttribute("aria-activedescendant"), "");
+
+ // Time to make some toast
+ toaster.appendChild(toast);
+ assert_equals(toaster.ariaActiveDescendantElement, toast);
+ // Current spec behaviour:
+ assert_equals(toaster.getAttribute("aria-activedescendant"), "");
+ }, "Element reference set in invalid scope remains intact throughout move to valid scope.");
+ </script>
+
+ <div id="billingElementContainer">
+ <div id="billingElement">Billing</div>
+ </div>
+ <div>
+ <div id="nameElement">Name</div>
+ <input type="text" id="input1" aria-labelledby="billingElement nameElement"/>
+ </div>
+ <div>
+ <div id="addressElement">Address</div>
+ <input type="text" id="input2"/>
+ </div>
+
+ <script>
+ test(function(t) {
+ const billingElement = document.getElementById("billingElement")
+ assert_array_equals(input1.ariaLabelledByElements, [billingElement, nameElement], "parsed content attribute sets element references.");
+ assert_equals(input1.ariaLabelledByElements, input1.ariaLabelledByElements, "check idl attribute caching after parsing");
+ assert_equals(input2.ariaLabelledByElements, null, "Testing missing content attribute after parsing.");
+
+ input2.ariaLabelledByElements = [billingElement, addressElement];
+ assert_array_equals(input2.ariaLabelledByElements, [billingElement, addressElement], "Testing IDL setter/getter.");
+ assert_equals(input1.ariaLabelledByElements, input1.ariaLabelledByElements, "check idl attribute caching after update");
+ assert_equals(input2.getAttribute("aria-labelledby"), "");
+
+ // Remove the billingElement from the DOM.
+ // As it was explicitly set the underlying association will remain intact,
+ // but it will be hidden until the element is moved back into a valid scope.
+ billingElement.remove();
+ assert_array_equals(input2.ariaLabelledByElements, [addressElement], "Computed ariaLabelledByElements shouldn't include billing when out of scope.");
+
+ // Insert the billingElement back into the DOM and check that it is visible
+ // again, as the underlying association should have been kept intact.
+ billingElementContainer.appendChild(billingElement);
+ assert_array_equals(input2.ariaLabelledByElements, [billingElement, addressElement], "Billing element back in scope.");
+
+ input2.ariaLabelledByElements = [];
+ assert_array_equals(input2.ariaLabelledByElements, [], "Testing IDL setter/getter for empty array.");
+ assert_equals(input2.getAttribute("aria-labelledby"), "");
+
+ input1.removeAttribute("aria-labelledby");
+ assert_equals(input1.ariaLabelledByElements, null);
+
+ input1.setAttribute("aria-labelledby", "nameElement addressElement");
+ assert_array_equals(input1.ariaLabelledByElements, [nameElement, addressElement],
+ "computed value after setting attribute directly");
+
+ input1.ariaLabelledByElements = null;
+ assert_false(input1.hasAttribute("aria-labelledby", "Nullifying the IDL attribute should remove the content attribute."));
+ }, "aria-labelledby.");
+ </script>
+
+ <ul role="tablist">
+ <li role="presentation"><a id="link1" role="tab" aria-controls="panel1">Tab 1</a></li>
+ <li role="presentation"><a id="link2" role="tab">Tab 2</a></li>
+ </ul>
+
+ <div role="tabpanel" id="panel1"></div>
+ <div role="tabpanel" id="panel2"></div>
+
+ <script>
+ test(function(t) {
+ assert_array_equals(link1.ariaControlsElements, [panel1]);
+ assert_equals(link2.ariaControlsElements, null);
+
+ link2.setAttribute("aria-controls", "panel1 panel2");
+ assert_array_equals(link2.ariaControlsElements, [panel1, panel2]);
+
+ link1.ariaControlsElements = [];
+ assert_equals(link1.getAttribute("aria-controls"), "");
+
+ link2.ariaControlsElements = [panel1, panel2];
+ assert_equals(link2.getAttribute("aria-controls"), "");
+ assert_array_equals(link2.ariaControlsElements, [panel1, panel2]);
+
+ link2.removeAttribute("aria-controls");
+ assert_equals(link2.ariaControlsElements, null);
+
+ link2.ariaControlsElements = [panel1, panel2];
+ assert_equals(link2.getAttribute("aria-controls"), "");
+ assert_array_equals(link2.ariaControlsElements, [panel1, panel2]);
+
+ link2.ariaControlsElements = null;
+ assert_false(link2.hasAttribute("aria-controls", "Nullifying the IDL attribute should remove the content attribute."));
+ }, "aria-controls.");
+ </script>
+
+ <a id="describedLink" aria-describedby="description1 description2">Fruit</a>
+ <div id="description1">Delicious</div>
+ <div id="description2">Nutritious</div>
+
+ <script>
+ test(function(t) {
+ assert_array_equals(describedLink.ariaDescribedByElements, [description1, description2]);
+
+ describedLink.ariaDescribedByElements = [description1, description2];
+ assert_equals(describedLink.getAttribute("aria-describedby"), "");
+ assert_array_equals(describedLink.ariaDescribedByElements, [description1, description2]);
+
+ describedLink.ariaDescribedByElements = [];
+ assert_equals(describedLink.getAttribute("aria-describedby"), "");
+
+ describedLink.setAttribute("aria-describedby", "description1");
+ assert_array_equals(describedLink.ariaDescribedByElements, [description1]);
+
+ describedLink.removeAttribute("aria-describedby");
+ assert_equals(describedLink.ariaDescribedByElements, null);
+
+ describedLink.ariaDescribedByElements = [description1, description2];
+ assert_equals(describedLink.getAttribute("aria-describedby"), "");
+ assert_array_equals(describedLink.ariaDescribedByElements, [description1, description2]);
+
+ describedLink.ariaDescribedByElements = null;
+ assert_false(describedLink.hasAttribute("aria-describedby", "Nullifying the IDL attribute should remove the content attribute."));
+ }, "aria-describedby.");
+ </script>
+
+ <h2 id="titleHeading" aria-flowto="article1 article2">Title</h2>
+ <div>Next</div>
+ <article id="article2">Content2</article>
+ <article id="article1">Content1</article>
+
+ <script>
+ test(function(t) {
+ const article1 = document.getElementById("article1");
+ const article2 = document.getElementById("article2");
+
+ assert_array_equals(titleHeading.ariaFlowToElements, [article1, article2]);
+
+ titleHeading.ariaFlowToElements = [article1, article2];
+ assert_equals(titleHeading.getAttribute("aria-flowto"), "");
+ assert_array_equals(titleHeading.ariaFlowToElements, [article1, article2]);
+
+ titleHeading.ariaFlowToElements = [];
+ assert_equals(titleHeading.getAttribute("aria-flowto"), "");
+
+ titleHeading.setAttribute("aria-flowto", "article1");
+ assert_array_equals(titleHeading.ariaFlowToElements, [article1]);
+
+ titleHeading.removeAttribute("aria-flowto");
+ assert_equals(titleHeading.ariaFlowToElements, null);
+
+ titleHeading.ariaFlowToElements = [article1, article2];
+ assert_equals(titleHeading.getAttribute("aria-flowto"), "");
+ assert_array_equals(titleHeading.ariaFlowToElements, [article1, article2]);
+
+ titleHeading.ariaFlowToElements = null;
+ assert_false(titleHeading.hasAttribute("aria-flowto", "Nullifying the IDL attribute should remove the content attribute."));
+ }, "aria-flowto.");
+ </script>
+
+ <ul>
+ <li id="listItemOwner" aria-owns="child1 child2">Parent</li>
+ </ul>
+ <ul>
+ <li id="child1">Child 1</li>
+ <li id="child2">Child 2</li>
+ </ul>
+ <script>
+ test(function(t) {
+ assert_array_equals(listItemOwner.ariaOwnsElements, [child1, child2]);
+
+ listItemOwner.removeAttribute("aria-owns");
+ assert_equals(listItemOwner.ariaOwnsElements, null);
+
+ listItemOwner.ariaOwnsElements = [child1, child2];
+ assert_equals(listItemOwner.getAttribute("aria-owns"), "");
+ assert_array_equals(listItemOwner.ariaOwnsElements, [child1, child2]);
+
+ listItemOwner.ariaOwnsElements = [];
+ assert_equals(listItemOwner.getAttribute("aria-owns"), "");
+
+ listItemOwner.setAttribute("aria-owns", "child1");
+ assert_array_equals(listItemOwner.ariaOwnsElements, [child1]);
+
+ listItemOwner.ariaOwnsElements = [child1, child2];
+ assert_equals(listItemOwner.getAttribute("aria-owns"), "");
+ assert_array_equals(listItemOwner.ariaOwnsElements, [child1, child2]);
+
+ listItemOwner.ariaOwnsElements = null;
+ assert_false(listItemOwner.hasAttribute("aria-owns", "Nullifying the IDL attribute should remove the content attribute."));
+ }, "aria-owns.");
+ </script>
+
+ <div id="lightDomContainer">
+ <h2 id="lightDomHeading" aria-flowto="shadowChild1 shadowChild2">Light DOM Heading</h2>
+ <div id="host"></div>
+ <p id="lightDomText1">Light DOM text</p>
+ <p id="lightDomText2">Light DOM text</p>
+ </div>
+
+ <script>
+ test(function(t) {
+ const shadowRoot = host.attachShadow({mode: "open"});
+ const shadowChild1 = document.createElement("article");
+ shadowChild1.setAttribute("id", "shadowChild1");
+ shadowRoot.appendChild(shadowChild1);
+ const shadowChild2 = document.createElement("article");
+ shadowChild2.setAttribute("id", "shadowChild1");
+ shadowRoot.appendChild(shadowChild2);
+
+ // The elements in the content attribute are in a "darker" tree - they
+ // enter a shadow encapsulation boundary, so not be associated any more.
+ assert_array_equals(lightDomHeading.ariaFlowToElements, []);
+
+ // These elements are in a shadow including ancestor, i.e "lighter" tree.
+ // Valid for the IDL attribute, but content attribute should be null.
+ shadowChild1.ariaFlowToElements = [lightDomText1, lightDomText2];
+ assert_equals(shadowChild1.getAttribute("aria-flowto"), "", "empty content attribute for elements that cross shadow boundaries.");
+
+ // These IDs belong to a different scope, so the attr-associated-element
+ // cannot be computed.
+ shadowChild2.setAttribute("aria-flowto", "lightDomText1 lightDomText2");
+ assert_array_equals(shadowChild2.ariaFlowToElements, []);
+
+ // Elements that cross into shadow DOM are dropped, only reflect the valid
+ // elements in IDL and in the content attribute.
+ lightDomHeading.ariaFlowToElements = [shadowChild1, shadowChild2, lightDomText1, lightDomText2];
+ assert_array_equals(lightDomHeading.ariaFlowToElements, [lightDomText1, lightDomText2], "IDL should only include valid elements");
+ assert_equals(lightDomHeading.getAttribute("aria-flowto"), "", "empty content attribute if any given elements cross shadow boundaries");
+
+ // Using a mixture of elements in the same scope and in a shadow including
+ // ancestor should set the IDL attribute, but should reflect the empty
+ // string in the content attribute.
+ shadowChild1.removeAttribute("aria-flowto");
+ shadowChild1.ariaFlowToElements = [shadowChild1, lightDomText1];
+ assert_equals(shadowChild1.getAttribute("aria-flowto"), "", "Setting IDL elements with a mix of scopes should reflect an empty string in the content attribute")
+
+ }, "shadow DOM behaviour for FrozenArray element reflection.");
+ </script>
+
+ <div id="describedButtonContainer">
+ <div id="buttonDescription1">Delicious</div>
+ <div id="buttonDescription2">Nutritious</div>
+ <div id="outerShadowHost"></div>
+ </div>
+
+ <script>
+ test(function(t) {
+ const description1 = document.getElementById("buttonDescription1");
+ const description2 = document.getElementById("buttonDescription2");
+ const outerShadowRoot = outerShadowHost.attachShadow({mode: "open"});
+ const innerShadowHost = document.createElement("div");
+ outerShadowRoot.appendChild(innerShadowHost);
+ const innerShadowRoot = innerShadowHost.attachShadow({mode: "open"});
+
+ // Create an element, add some attr associated light DOM elements and append it to the outer shadow root.
+ const describedElement = document.createElement("button");
+ describedButtonContainer.appendChild(describedElement);
+ describedElement.ariaDescribedByElements = [description1, description2];
+
+ // All elements were in the same scope, so elements are gettable and the content attribute is empty.
+ assert_array_equals(describedElement.ariaDescribedByElements, [description1, description2], "same scope reference");
+ assert_equals(describedElement.getAttribute("aria-describedby"), "");
+
+ outerShadowRoot.appendChild(describedElement);
+
+ // Explicitly set attr-associated-elements should still be gettable because we are referencing elements in a lighter scope.
+ // The content attr is empty.
+ assert_array_equals(describedElement.ariaDescribedByElements, [description1, description2], "lighter scope reference");
+ assert_equals(describedElement.getAttribute("aria-describedby"), "");
+
+ // Move the explicitly set elements into a deeper shadow DOM to test the relationship should not be gettable.
+ innerShadowRoot.appendChild(description1);
+ innerShadowRoot.appendChild(description2);
+
+ // Explicitly set elements are no longer retrievable, because they are no longer in a valid scope.
+ assert_array_equals(describedElement.ariaDescribedByElements, [], "invalid scope reference");
+ assert_equals(describedElement.getAttribute("aria-describedby"), "");
+
+ // Move into the same shadow scope as the explicitly set elements to test that the elements are gettable.
+ innerShadowRoot.appendChild(describedElement);
+ assert_array_equals(describedElement.ariaDescribedByElements, [description1, description2], "restored valid scope reference");
+ assert_equals(describedElement.getAttribute("aria-describedby"), "");
+ }, "Moving explicitly set elements across shadow DOM boundaries.");
+ </script>
+
+ <div id="sameScopeContainer">
+ <div id="labeledby" aria-labeledby="headingLabel1 headingLabel2">Misspelling</div>
+ <div id="headingLabel1">Wonderful</div>
+ <div id="headingLabel2">Fantastic</div>
+
+ <div id="headingShadowHost"></div>
+ </div>
+
+ <script>
+ test(function(t) {
+ const shadowRoot = headingShadowHost.attachShadow({mode: "open"});
+ const headingElement = document.createElement("h1");
+ const headingLabel1 = document.getElementById("headingLabel1")
+ const headingLabel2 = document.getElementById("headingLabel2")
+ shadowRoot.appendChild(headingElement);
+
+ assert_array_equals(labeledby.ariaLabelledByElements, [headingLabel1, headingLabel2], "aria-labeled by is supported by IDL getter.");
+
+ // Explicitly set elements are in a lighter shadow DOM, so that's ok.
+ headingElement.ariaLabelledByElements = [headingLabel1, headingLabel2];
+ assert_array_equals(headingElement.ariaLabelledByElements, [headingLabel1, headingLabel2], "Lighter elements are gettable when explicitly set.");
+ assert_equals(headingElement.getAttribute("aria-labelledby"), "");
+
+ // Move into Light DOM, explicitly set elements should still be gettable.
+ // Note that the content attribute is still empty.
+ sameScopeContainer.appendChild(headingElement);
+ assert_array_equals(headingElement.ariaLabelledByElements, [headingLabel1, headingLabel2], "Elements are all in same scope, so gettable.");
+ assert_equals(headingElement.getAttribute("aria-labelledby"), "", "Content attribute is empty.");
+
+ // Reset the association, the content attribute is sitll empty.
+ headingElement.ariaLabelledByElements = [headingLabel1, headingLabel2];
+ assert_equals(headingElement.getAttribute("aria-labelledby"), "");
+
+ // Remove the referring element from the DOM, elements are no longer longer exposed,
+ // underlying internal reference is still kept intact.
+ headingElement.remove();
+ assert_array_equals(headingElement.ariaLabelledByElements, [], "Element is no longer in the document, so references should no longer be exposed.");
+ assert_equals(headingElement.getAttribute("aria-labelledby"), "");
+
+ // Insert it back in.
+ sameScopeContainer.appendChild(headingElement);
+ assert_array_equals(headingElement.ariaLabelledByElements, [headingLabel1, headingLabel2], "Element is restored to valid scope, so should be gettable.");
+ assert_equals(headingElement.getAttribute("aria-labelledby"), "");
+
+ // Remove everything from the DOM, nothing is exposed again.
+ headingLabel1.remove();
+ headingLabel2.remove();
+ assert_array_equals(headingElement.ariaLabelledByElements, []);
+ assert_equals(headingElement.getAttribute("aria-labelledby"), "");
+ assert_equals(document.getElementById("headingLabel1"), null);
+ assert_equals(document.getElementById("headingLabel2"), null);
+
+ // Reset the association.
+ headingElement.ariaLabelledByElements = [headingLabel1, headingLabel2];
+ assert_array_equals(headingElement.ariaLabelledByElements, []);
+ assert_equals(headingElement.getAttribute("aria-labelledby"), "");
+ }, "Moving explicitly set elements around within the same scope, and removing from the DOM.");
+ </script>
+
+ <input id="input">
+ <optgroup>
+ <option id="first">First option</option>
+ <option id="second">Second option</option>
+ </optgroup>
+
+ <script>
+ test(function(t) {
+ input.ariaActiveDescendantElement = first;
+ first.parentElement.appendChild(first);
+
+ assert_equals(input.ariaActiveDescendantElement, first);
+ }, "Reparenting.");
+ </script>
+
+ <div id='fromDiv'></div>
+
+ <script>
+ test(function(t) {
+ const toSpan = document.createElement('span');
+ toSpan.setAttribute("id", "toSpan");
+ fromDiv.ariaActiveDescendantElement = toSpan;
+
+ assert_equals(fromDiv.ariaActiveDescendantElement, null, "Referenced element not inserted into document, so is in an invalid scope.");
+ assert_equals(fromDiv.getAttribute("aria-activedescendant"), "", "Invalid scope, so content attribute not set.");
+
+ fromDiv.appendChild(toSpan);
+ assert_equals(fromDiv.ariaActiveDescendantElement, toSpan, "Referenced element now inserted into the document.");
+ assert_equals(fromDiv.getAttribute("aria-activedescendant"), "", "Content attribute remains empty, as it is only updated at set time.");
+
+ }, "Attaching element reference before it's inserted into the DOM.");
+ </script>
+
+ <div id='originalDocumentDiv'></div>
+
+ <script>
+ test(function(t) {
+ const newDoc = document.implementation.createHTMLDocument('new document');
+ const newDocSpan = newDoc.createElement('span');
+ newDoc.body.appendChild(newDocSpan);
+
+ // Create a reference across documents.
+ originalDocumentDiv.ariaActiveDescendantElement = newDocSpan;
+
+ assert_equals(originalDocumentDiv.ariaActiveDescendantElement, null, "Cross-document is an invalid scope, so reference will not be visible.");
+ assert_equals(fromDiv.getAttribute("aria-activedescendant"), "", "Invalid scope when set, so content attribute not set.");
+
+ // "Move" span to first document.
+ originalDocumentDiv.appendChild(newDocSpan);
+
+ // Implementation defined: moving object into same document from other document may cause reference to become visible.
+ assert_equals(originalDocumentDiv.ariaActiveDescendantElement, newDocSpan, "Implementation defined: moving object back *may* make reference visible.");
+ assert_equals(fromDiv.getAttribute("aria-activedescendant"), "", "Invalid scope when set, so content attribute not set.");
+ }, "Cross-document references and moves.");
+ </script>
+
+
+ <script>
+ test(function(t) {
+ const otherDoc = document.implementation.createHTMLDocument('otherDoc');
+ const otherDocDiv = otherDoc.createElement('div');
+ const otherDocSpan = otherDoc.createElement('span');
+ otherDocDiv.appendChild(otherDocSpan);
+ otherDoc.body.appendChild(otherDocDiv);
+
+ otherDocDiv.ariaActiveDescendantElement = otherDocSpan;
+ assert_equals(otherDocDiv.ariaActiveDescendantElement, otherDocSpan, "Setting reference on a different document.");
+
+ // Adopt element from other oducment.
+ document.body.appendChild(document.adoptNode(otherDocDiv));
+ assert_equals(otherDocDiv.ariaActiveDescendantElement, otherDocSpan, "Reference should be kept on the new document too.");
+ }, "Adopting element keeps references.");
+ </script>
+
+ <div id="cachingInvariantMain"></div>
+ <div id="cachingInvariantElement1"></div>
+ <div id="cachingInvariantElement2"></div>
+ <div id="cachingInvariantElement3"></div>
+ <div id="cachingInvariantElement4"></div>
+ <div id="cachingInvariantElement5"></div>
+
+ <script>
+ test(function(t) {
+ cachingInvariantMain.ariaControlsElements = [cachingInvariantElement1, cachingInvariantElement2];
+ cachingInvariantMain.ariaDescribedByElements = [cachingInvariantElement3, cachingInvariantElement4];
+ cachingInvariantMain.ariaDetailsElements = [cachingInvariantElement5];
+ cachingInvariantMain.ariaFlowToElements = [cachingInvariantElement1, cachingInvariantElement3];
+ cachingInvariantMain.ariaLabelledByElements = [cachingInvariantElement2, cachingInvariantElement4];
+ cachingInvariantMain.ariaOwnsElements = [cachingInvariantElement1, cachingInvariantElement2, cachingInvariantElement3];
+
+ let ariaControlsElementsArray = cachingInvariantMain.ariaControlsElements;
+ let ariaDescribedByElementsArray = cachingInvariantMain.ariaDescribedByElements;
+ let ariaDetailsElementsArray = cachingInvariantMain.ariaDetailsElements;
+ let ariaFlowToElementsArray = cachingInvariantMain.ariaFlowToElements;
+ let ariaLabelledByElementsArray = cachingInvariantMain.ariaLabelledByElements;
+ let ariaOwnsElementsArray = cachingInvariantMain.ariaOwnsElements;
+
+ assert_equals(ariaControlsElementsArray, cachingInvariantMain.ariaControlsElements, "Caching invariant for ariaControlsElements");
+ assert_equals(ariaDescribedByElementsArray, cachingInvariantMain.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements");
+ assert_equals(ariaDetailsElementsArray, cachingInvariantMain.ariaDetailsElements, "Caching invariant for ariaDetailsElements");
+ assert_equals(ariaFlowToElementsArray, cachingInvariantMain.ariaFlowToElements, "Caching invariant for ariaFlowToElements");
+ assert_equals(ariaLabelledByElementsArray, cachingInvariantMain.ariaLabelledByElements, "Caching invariant for ariaLabelledByElements");
+ assert_equals(ariaOwnsElementsArray, cachingInvariantMain.ariaOwnsElements, "Caching invariant for ariaOwnsElements");
+
+ // Ensure that stale values don't continue to be cached
+ cachingInvariantMain.ariaControlsElements = [cachingInvariantElement4, cachingInvariantElement5];
+ cachingInvariantMain.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2];
+ cachingInvariantMain.ariaDetailsElements = [cachingInvariantElement3];
+ cachingInvariantMain.ariaFlowToElements = [cachingInvariantElement4, cachingInvariantElement5];
+ cachingInvariantMain.ariaLabelledByElements = [cachingInvariantElement1, cachingInvariantElement2];
+ cachingInvariantMain.ariaOwnsElements = [cachingInvariantElement3, cachingInvariantElement4, cachingInvariantElement1];
+
+ ariaControlsElementsArray = cachingInvariantMain.ariaControlsElements;
+ ariaDescribedByElementsArray = cachingInvariantMain.ariaDescribedByElements;
+ ariaDetailsElementsArray = cachingInvariantMain.ariaDetailsElements;
+ ariaFlowToElementsArray = cachingInvariantMain.ariaFlowToElements;
+ ariaLabelledByElementsArray = cachingInvariantMain.ariaLabelledByElements;
+ ariaOwnsElementsArray = cachingInvariantMain.ariaOwnsElements;
+
+ assert_equals(ariaControlsElementsArray, cachingInvariantMain.ariaControlsElements, "Caching invariant for ariaControlsElements");
+ assert_equals(ariaDescribedByElementsArray, cachingInvariantMain.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements");
+ assert_equals(ariaDetailsElementsArray, cachingInvariantMain.ariaDetailsElements, "Caching invariant for ariaDetailsElements");
+ assert_equals(ariaFlowToElementsArray, cachingInvariantMain.ariaFlowToElements, "Caching invariant for ariaFlowToElements");
+ assert_equals(ariaLabelledByElementsArray, cachingInvariantMain.ariaLabelledByElements, "Caching invariant for ariaLabelledByElements");
+ assert_equals(ariaOwnsElementsArray, cachingInvariantMain.ariaOwnsElements, "Caching invariant for ariaOwnsElements");
+
+ }, "Caching invariant different attributes.");
+ </script>
+
+ <div id="cachingInvariantMain1"></div>
+ <div id="cachingInvariantMain2"></div>
+
+ <script>
+ test(function(t) {
+ cachingInvariantMain1.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2];
+ cachingInvariantMain2.ariaDescribedByElements = [cachingInvariantElement3, cachingInvariantElement4];
+
+ let ariaDescribedByElementsArray1 = cachingInvariantMain1.ariaDescribedByElements;
+ let ariaDescribedByElementsArray2 = cachingInvariantMain2.ariaDescribedByElements;
+
+ assert_equals(ariaDescribedByElementsArray1, cachingInvariantMain1.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements in one elemnt");
+ assert_equals(ariaDescribedByElementsArray2, cachingInvariantMain2.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements in onother elemnt");
+ }, "Caching invariant different elements.");
+ </script>
+
+ <div id="badInputValues"></div>
+ <div id="badInputValues2"></div>
+
+ <script>
+ test(function(t) {
+ assert_throws_js(TypeError, () => { badInputValues.ariaActiveDescendantElement = "a string"; });
+ assert_throws_js(TypeError, () => { badInputValues.ariaActiveDescendantElement = 1; });
+ assert_throws_js(TypeError, () => { badInputValues.ariaActiveDescendantElement = [ badInputValues2 ]; });
+
+ assert_throws_js(TypeError, () => { badInputValues.ariaControlsElements = "a string" });
+ assert_throws_js(TypeError, () => { badInputValues.ariaControlsElements = 1 });
+ assert_throws_js(TypeError, () => { badInputValues.ariaControlsElements = [1, 2, 3] });
+ assert_throws_js(TypeError, () => { badInputValues.ariaControlsElements = badInputValues2 });
+ }, "Passing values of the wrong type should throw a TypeError");
+ </script>
+
+ <!-- TODO(chrishall): add additional GC test covering:
+ if an element is in an invalid scope but attached to the document, it's
+ not GC'd;
+ -->
+
+ <!-- TODO(chrishall): add additional GC test covering:
+ if an element is not attached to the document, but is in a tree fragment
+ which is not GC'd because there is a script reference to another element
+ in the tree fragment, and the relationship is valid because it is between
+ two elements in that tree fragment, the relationship is exposed *and* the
+ element is not GC'd
+ -->
+
+</html>
diff --git a/testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir-ref.html b/testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir-ref.html
new file mode 100644
index 0000000000..88ccd4b9b5
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#the-directionality">
+<style>
+div { width: 100px; height: 100px; background: green; }
+</style>
+</head>
+<body>
+<div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir.html b/testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir.html
new file mode 100644
index 0000000000..629cee8738
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/directionality/bdi-element-invalid-dir.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#the-directionality">
+<link rel="match" href="bdi-element-invalid-dir-ref.html">
+<style>
+div { position: relative; width: 100px; height: 100px; background: red; }
+bdi { width: 100px; height: 100px; display: block; }
+span { display: inline-block; width: 50px; height: 100px; background: green; color: green; }
+#left { background: green; position: absolute; left: 0px; top: 0px; }
+</style>
+</head>
+<body>
+<div><bdi dir="foo"><span id="left"></span><span>&#x05EA;</span></bdi></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.body.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.body.html
new file mode 100644
index 0000000000..77de1d93b5
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.body.html
@@ -0,0 +1,227 @@
+<!DOCTYPE html>
+<title>Document.body</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-body">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function createDocument() {
+ var doc = document.implementation.createHTMLDocument("");
+ doc.removeChild(doc.documentElement);
+ return doc;
+}
+test(function() {
+ var doc = createDocument();
+ assert_equals(doc.body, null);
+}, "Childless document");
+test(function() {
+ var doc = createDocument();
+ doc.appendChild(doc.createElement("html"));
+ assert_equals(doc.body, null);
+}, "Childless html element");
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ var b =
+ html.appendChild(doc.createElement("body"));
+ html.appendChild(doc.createElement("frameset"));
+ assert_equals(doc.body, b);
+}, "Body followed by frameset inside the html element");
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ var f =
+ html.appendChild(doc.createElement("frameset"));
+ html.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, f);
+}, "Frameset followed by body inside the html element");
+test(function() {
+ var doc = createDocument();
+ var html =
+ doc.appendChild(doc.createElementNS("http://example.org/test", "html"));
+ html.appendChild(doc.createElement("body"));
+ html.appendChild(doc.createElement("frameset"));
+ assert_equals(doc.body, null);
+}, "Body followed by frameset inside a non-HTML html element");
+test(function() {
+ var doc = createDocument();
+ var html =
+ doc.appendChild(doc.createElementNS("http://example.org/test", "html"));
+ html.appendChild(doc.createElement("frameset"));
+ html.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, null);
+}, "Frameset followed by body inside a non-HTML html element");
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ html.appendChild(
+ doc.createElementNS("http://example.org/test", "body"));
+ var b = html.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, b);
+}, "Non-HTML body followed by body inside the html element");
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ html.appendChild(
+ doc.createElementNS("http://example.org/test", "frameset"));
+ var b = html.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, b);
+}, "Non-HTML frameset followed by body inside the html element");
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ var x = html.appendChild(doc.createElement("x"));
+ x.appendChild(doc.createElement("body"));
+ var body = html.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, body);
+}, "Body inside an x element followed by a body");
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ var x = html.appendChild(doc.createElement("x"));
+ x.appendChild(doc.createElement("frameset"));
+ var frameset = html.appendChild(doc.createElement("frameset"));
+ assert_equals(doc.body, frameset);
+}, "Frameset inside an x element followed by a frameset");
+
+// Root node is not a html element.
+test(function() {
+ var doc = createDocument();
+ doc.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, null);
+}, "Body as the root node");
+test(function() {
+ var doc = createDocument();
+ doc.appendChild(doc.createElement("frameset"));
+ assert_equals(doc.body, null);
+}, "Frameset as the root node");
+test(function() {
+ var doc = createDocument();
+ var body = doc.appendChild(doc.createElement("body"));
+ body.appendChild(doc.createElement("frameset"));
+ assert_equals(doc.body, null);
+}, "Body as the root node with a frameset child");
+test(function() {
+ var doc = createDocument();
+ var frameset = doc.appendChild(doc.createElement("frameset"));
+ frameset.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, null);
+}, "Frameset as the root node with a body child");
+test(function() {
+ var doc = createDocument();
+ doc.appendChild(doc.createElementNS("http://example.org/test", "body"));
+ assert_equals(doc.body, null);
+}, "Non-HTML body as the root node");
+test(function() {
+ var doc = createDocument();
+ doc.appendChild(doc.createElementNS("http://example.org/test", "frameset"));
+ assert_equals(doc.body, null);
+}, "Non-HTML frameset as the root node");
+
+test(function() {
+ assert_not_equals(document.body, null);
+ assert_true(document.body instanceof HTMLBodyElement, "should be HTMLBodyElement");
+ assert_equals(document.body.tagName, "BODY");
+}, "existing document's body");
+
+
+var originalBody = document.body;
+test(function() {
+ assert_throws_js(TypeError, function() {
+ document.body = "text"
+ })
+ assert_equals(document.body, originalBody);
+}, "Setting document.body to a string.")
+test(function() {
+ assert_throws_dom("HierarchyRequestError", function() {
+ document.body = document.createElement("div")
+ })
+ assert_equals(document.body, originalBody);
+}, "Setting document.body to a div element.")
+test(function() {
+ var doc = createDocument();
+ assert_throws_dom("HierarchyRequestError", function() {
+ doc.body = doc.createElement("body")
+ })
+ assert_equals(doc.body, null);
+}, "Setting document.body when there's no root element.")
+test(function() {
+ var doc = document.implementation.createHTMLDocument();
+
+ var new_body = doc.createElement("body");
+ assert_true(new_body instanceof HTMLBodyElement, "should be HTMLBodyElement");
+ assert_equals(new_body.tagName, "BODY");
+
+ doc.body = new_body;
+ assert_equals(doc.body, new_body);
+}, "Setting document.body to a new body element.");
+test(function() {
+ var doc = document.implementation.createHTMLDocument();
+
+ var new_frameset = doc.createElement("frameset");
+ assert_true(new_frameset instanceof HTMLFrameSetElement, "should be HTMLFrameSetElement");
+ assert_equals(new_frameset.tagName, "FRAMESET");
+
+ doc.body = new_frameset;
+ assert_equals(doc.body, new_frameset, "test6-3, append frameset to a new document");
+}, "Setting document.body to a new frameset element.");
+
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ var f =
+ html.appendChild(doc.createElement("frameset"));
+ assert_equals(doc.body, f);
+
+ var b = doc.createElement("body");
+ doc.body = b;
+
+ assert_equals(f.parentNode, null,
+ "Frameset should have been removed from the tree");
+ assert_equals(doc.body, b, "Body should be the new doc.body");
+}, "Setting document.body to a body will replace an existing frameset if there is one.");
+
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ var b =
+ html.appendChild(doc.createElement("body"));
+ assert_equals(doc.body, b);
+
+ var f = doc.createElement("frameset");
+ doc.body = f;
+
+ assert_equals(b.parentNode, null,
+ "Body should have been removed from the tree");
+ assert_equals(doc.body, f, "Frameset should be the new doc.body");
+}, "Setting document.body to a frameset will replace an existing body if there is one.");
+
+test(function() {
+ var doc = createDocument();
+ var html = doc.appendChild(doc.createElement("html"));
+ var b =
+ html.appendChild(doc.createElement("body"));
+ var f1 = html.appendChild(doc.createElement("frameset"));
+ assert_equals(doc.body, b);
+
+ var f2 = doc.createElement("frameset");
+ doc.body = f2;
+
+ assert_equals(b.parentNode, null,
+ "Body should have been removed from the tree");
+ assert_equals(f1.parentNode, html,
+ "Frameset following body should still be in the tree.");
+ assert_equals(doc.body, f2, "New frameset should be the new doc.body");
+ assert_equals(f2.nextSibling, f1, "New frameset should have replaced the body");
+}, "Setting document.body to a frameset will replace the first existing body/frameset.");
+
+test(function() {
+ var doc = createDocument();
+ doc.appendChild(doc.createElement("test"));
+ var new_body = doc.createElement("body");
+ doc.body = new_body;
+ assert_equals(doc.documentElement.firstChild, new_body, "new_body should be inserted");
+ assert_equals(doc.body, null, "Getter should return null when the root is not html");
+}, "Setting document.body to a new body element when the root element is a test element.");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.currentScript.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.currentScript.html
new file mode 100644
index 0000000000..245bae98ee
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.currentScript.html
@@ -0,0 +1,219 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Document.currentScript</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-document-currentscript">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id="log"></div>
+<script>
+var data = {
+ "parse-inline" : [],
+ "parse-ext" : [],
+ "dom-inline" : [],
+ "dom-ext" : [],
+ "nested" : ["nested-outer","nested-inner","nested-outer"],
+ "script-load-error" : [null],
+ "script-window-error" : ["script-error-compile","script-error-runtime"],
+ "timeout" : [null],
+ "eval" : [],
+ "xhr-test" : [],
+ "script-svg" : [],
+ "script-async" : [],
+ "script-defer" : [],
+ "script-async-false" : [],
+ "iframe-src" : [],
+ "cross-origin" : [null],
+ "document-write" : [],
+ "microtask": [],
+};
+
+var expected = {};
+var actual = {};
+
+Object.keys(data).forEach(function(id) {
+ var test_expected = data[id];
+ if(test_expected.length == 0) {
+ test_expected = [id];
+ }
+ expected[id] = test_expected;
+ actual[id] = [];
+});
+
+var tests = {};
+setup({allow_uncaught_exception : true});
+
+Object.keys(expected).forEach(function(id) {
+ var testmsg = "Script " + id;
+ tests[id] = async_test(testmsg);
+});
+
+function verify(id) {
+ tests[id].step(function() {
+ actual[id].push(document.currentScript);
+ })
+}
+
+function finish(id) {
+ tests[id].step(function() {
+ assert_array_equals(actual[id],expected[id].map(function(id) {
+ return document.getElementById(id);
+ }));
+ this.done();
+ })
+}
+
+</script>
+
+<!-- Test parser inserted scripts -->
+<script id="parse-inline">
+verify('parse-inline');
+finish('parse-inline')
+</script>
+<script id="parse-ext" src="data:text/plain,verify('parse-ext')"></script>
+<script>finish('parse-ext');</script>
+
+<!-- Test DOM inserted scripts -->
+<script>
+var s = document.createElement("script");
+s.textContent = "verify('dom-inline');";
+s.id = "dom-inline";
+document.body.appendChild(s);
+finish('dom-inline');
+
+s = document.createElement("script");
+s.src = "data:text/plain,verify('dom-ext');";
+s.id = "dom-ext";
+s.onload = function() {
+ finish('dom-ext');
+}
+document.body.appendChild(s);
+</script>
+
+<!-- Test Nested scripts -->
+<script id="nested-outer">
+ verify("nested");
+ var s = document.createElement("script");
+ s.textContent = "verify('nested')";
+ s.id = "nested-inner";
+ document.body.appendChild(s);
+ verify("nested");
+ finish('nested');
+</script>
+
+<!-- Test script load error event listener -->
+<script>
+function testLoadFail() {
+ verify('script-load-error');
+ finish('script-load-error');
+}
+</script>
+
+<script src="http://some.nonexistant.test/fail" id="script-load-error" onerror="testLoadFail()">
+</script>
+
+<!-- Test for runtime and compile time errors -->
+<script>
+ window.onerror = function() {
+ verify('script-window-error');
+ }
+
+ var s = document.createElement("script");
+ s.id = "script-error-compile";
+ s.textContent = "{";
+ document.body.appendChild(s);
+
+ window.onerror = function() {
+ verify('script-window-error');
+ }
+
+ s = document.createElement("script");
+ s.id = "script-error-runtime";
+ s.textContent = "undefinedfn();";
+ document.body.appendChild(s);
+
+ finish('script-window-error');
+</script>
+
+<!-- Verify in setTimeout -->
+<script>
+ setTimeout(function() {
+ verify('timeout');
+ finish('timeout');
+ },0);
+</script>
+
+<!-- Verify in eval -->
+<script id="eval">
+ eval('verify("eval")');
+ finish("eval");
+</script>
+
+<!-- Verify in synchronous xhr -->
+<script id="xhr-test">
+ var request = new XMLHttpRequest();
+ request.open('GET','/',false);
+ request.send(null);
+
+ if(request.status === 200) {
+ verify('xhr-test');
+ finish('xhr-test');
+ }
+</script>
+
+<!-- Testing script within svg -->
+<svg>
+ <script id="script-svg">
+ verify('script-svg');
+ finish('script-svg');
+ </script>
+</svg>
+
+<!-- Test script async and defer -->
+<script id='script-async' async src='data:text/plain,verify("script-async"),finish("script-async")'></script>
+
+<script id='script-defer' defer src='data:text/plain,verify("script-defer"),finish("script-defer")'></script>
+
+<!-- Test async = false dynamic script loading -->
+<script>
+ var s = document.createElement("script");
+ s.id = "script-async-false";
+ s.src = "data:text/plain,verify('script-async-false');"
+ s.onload = function() {
+ finish('script-async-false');
+ }
+ s.async = false;
+ document.body.appendChild(s);
+</script>
+
+<!-- Verify in iframe javascript uri scheme -->
+<iframe src="javascript:parent.verify('iframe-src'),parent.finish('iframe-src')"
+ style="visibility:hidden;display:none">
+</iframe>
+
+<!-- Testing cross origin script -->
+<script>
+var s = document.createElement("script");
+s.id = "cross-origin";
+s.src = get_host_info(). HTTP_REMOTE_ORIGIN + "/html/dom/documents/dom-tree-accessors/cross-domain.js"
+s.onload = function() {
+ verify('cross-origin')
+ finish('cross-origin');
+}
+document.body.appendChild(s);
+
+</script>
+
+<!-- Testing document.write -->
+<script>
+document.write('<script id="document-write">verify("document-write"); finish("document-write");</' + 'script>');
+</script>
+
+<!-- Testing microtask -->
+<script id="microtask">
+ Promise.resolve().then(() => {
+ verify("microtask");
+ finish("microtask");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.getElementsByClassName-null-undef.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.getElementsByClassName-null-undef.html
new file mode 100644
index 0000000000..dc132e5ec7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Document.getElementsByClassName-null-undef.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>getElementsByClassName and null/undefined</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-getelementsbyclassname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<p id="p1"></p>
+<p class="undefined" id="p2"></p>
+<p class="null" id="p3"></p>
+<p class="undefined null" id="p4"></p>
+</div>
+<script>
+test(function() {
+ var wrapper = document.getElementById("test");
+ assert_equals(document.getElementsByClassName(undefined).length, 2);
+ assert_equals(document.getElementsByClassName(undefined)[0],
+ document.getElementById("p2"));
+ assert_equals(document.getElementsByClassName(undefined)[1],
+ document.getElementById("p4"));
+/*
+ assert_equals(document.getElementsByClassName(null).length, 2);
+ assert_equals(document.getElementsByClassName(null)[0],
+ document.getElementById("p3"));
+ assert_equals(document.getElementsByClassName(null)[1],
+ document.getElementById("p4"));
+*/
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Element.getElementsByClassName-null-undef.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Element.getElementsByClassName-null-undef.html
new file mode 100644
index 0000000000..b4d9241647
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/Element.getElementsByClassName-null-undef.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>getElementsByClassName and null/undefined</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-getelementsbyclassname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<p id="p1"></p>
+<p class="undefined" id="p2"></p>
+<p class="null" id="p3"></p>
+<p class="undefined null" id="p4"></p>
+</div>
+<script>
+test(function() {
+ var wrapper = document.getElementById("test");
+ assert_equals(wrapper.getElementsByClassName(undefined).length, 2);
+ assert_equals(wrapper.getElementsByClassName(undefined)[0],
+ document.getElementById("p2"));
+ assert_equals(wrapper.getElementsByClassName(undefined)[1],
+ document.getElementById("p4"));
+/*
+ assert_equals(wrapper.getElementsByClassName(null).length, 2);
+ assert_equals(wrapper.getElementsByClassName(null)[0],
+ document.getElementById("p3"));
+ assert_equals(wrapper.getElementsByClassName(null)[1],
+ document.getElementById("p4"));
+*/
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/cross-domain.js b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/cross-domain.js
new file mode 100644
index 0000000000..32effe3c45
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/cross-domain.js
@@ -0,0 +1 @@
+//test script to check cross-domain script execution as in Document.currentScript.sub.html \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.embeds-document.plugins-01.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.embeds-document.plugins-01.html
new file mode 100644
index 0000000000..e710798915
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.embeds-document.plugins-01.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<title>document.embeds and document.plugins</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-embeds">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-plugins">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.embeds, document.embeds,
+ "embeds should be constant");
+ assert_equals(document.plugins, document.plugins,
+ "plugins should be constant");
+ assert_equals(document.embeds, document.plugins,
+ "embeds should be the same as plugins");
+ assert_equals(document.embeds.length, 0);
+ assert_equals(document.plugins.length, 0);
+}, "No plugins");
+
+test(function() {
+ var embed = document.body.appendChild(document.createElement("embed"));
+ this.add_cleanup(function() { document.body.removeChild(embed) });
+
+ assert_array_equals(document.embeds, [embed]);
+ assert_array_equals(document.plugins, [embed]);
+
+ assert_equals(document.embeds, document.embeds,
+ "embeds should be constant");
+ assert_equals(document.plugins, document.plugins,
+ "plugins should be constant");
+ assert_equals(document.embeds, document.plugins,
+ "embeds should be the same as plugins");
+}, "One plugin");
+
+test(function() {
+ var embed1 = document.createElement("embed"),
+ embed2 = document.createElement("embed");
+
+ document.body.appendChild(embed2);
+ this.add_cleanup(function() { document.body.removeChild(embed2) });
+ document.body.insertBefore(embed1, embed2);
+ this.add_cleanup(function() { document.body.removeChild(embed1) });
+
+ assert_array_equals(document.embeds, [embed1, embed2]);
+ assert_array_equals(document.plugins, [embed1, embed2]);
+
+ assert_equals(document.embeds, document.embeds,
+ "embeds should be constant");
+ assert_equals(document.plugins, document.plugins,
+ "plugins should be constant");
+ assert_equals(document.embeds, document.plugins,
+ "embeds should be the same as plugins");
+}, "Two plugins");
+
+test(function() {
+ var embed1 = document.createElement("embed"),
+ embed2 = document.createElement("embed");
+ document.body.appendChild(embed1);
+ this.add_cleanup(function() { document.body.removeChild(embed1) });
+ var embeds = document.embeds;
+ assert_true(embeds instanceof HTMLCollection);
+ assert_equals(embeds.length, 1);
+
+ document.body.appendChild(embed2);
+ assert_equals(embeds.length, 2);
+
+ document.body.removeChild(embed2);
+ assert_equals(embeds.length, 1);
+}, "Document.embeds should be a live collection");
+
+test(function() {
+ var embed1 = document.createElement("embed"),
+ embed2 = document.createElement("embed");
+ document.body.appendChild(embed1);
+ this.add_cleanup(function() { document.body.removeChild(embed1) });
+ var pls = document.plugins;
+ assert_true(pls instanceof HTMLCollection);
+ assert_equals(pls.length, 1);
+
+ document.body.appendChild(embed2);
+ assert_equals(pls.length, 2);
+
+ document.body.removeChild(embed2);
+ assert_equals(pls.length, 1);
+}, "Document.plugins should be a live collection");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.forms.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.forms.html
new file mode 100644
index 0000000000..f354c57477
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.forms.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Document.forms</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<form id="form1">
+<input type="button" name="thebutton" value="alpha">
+<input type=radio name=r1 value=a>
+<input type=radio name=r1 value=b>
+<input type=radio name=r1 value=c>
+<input type=radio name=r1 value=d>
+<input type=radio name=r1 value=e>
+</form>
+
+<form id="form2">
+<input type="button" name="thebutton" value="alpha">
+<input type=radio name=r1 value="a">
+<input type=radio name=r1 value="b">
+<input type=radio name=r1 value="c">
+<input type=radio name=r1 value="d">
+<input type=radio name=r1 value="e">
+</form>
+
+<form id=""></form>
+<script>
+test(function() {
+ assert_equals(document.forms.length, 3);
+ assert_equals(document.forms[0].id, "form1");
+ assert_equals(document.forms[1].id, "form2");
+ assert_equals(document.forms.form1.id, "form1");
+ assert_equals(document.forms.form2.id, "form2");
+ assert_equals(document.forms.item(0).id, "form1");
+ assert_equals(document.forms.item(1).id, "form2");
+ assert_equals(document.forms.namedItem("form1").id, "form1");
+ assert_equals(document.forms.namedItem("form2").id, "form2");
+}, "document.forms")
+
+test(function() {
+ // The `item` method takes one *numeric* argument. Passing a string to `item`
+ // results in that string getting converted to 0
+ assert_equals(document.forms.item("form1").id, "form1");
+ assert_equals(document.forms.item("form2").id, "form1");
+}, "document.forms.item with string arg")
+
+test(function() {
+ assert_equals(document.forms[""], undefined);
+ assert_equals(document.forms.namedItem(""), null);
+}, "document.forms with empty string")
+
+test(function() {
+ var result = [];
+ for (var p in document.forms) {
+ result.push(p);
+ }
+ // https://webidl.spec.whatwg.org/#property-enumeration
+ // If the object supports indexed properties, then the object’s supported
+ // property indices are enumerated first, in numerical order.
+ assert_array_equals(result.splice(0, 3), ["0", "1", "2"]);
+ // [...]
+ // Finally, any enumerable own properties or properties from the object’s
+ // prototype chain are then enumerated, in no defined order.
+ assert_array_equals(result.sort(), ["item", "namedItem", "length"].sort())
+}, "document.forms iteration")
+
+test(function() {
+ var result = Object.getOwnPropertyNames(document.forms);
+ assert_array_equals(result, ["0", "1", "2", "form1", "form2"])
+}, "document.forms getOwnPropertyNames")
+
+test(function() {
+ var forms = document.forms;
+ assert_true(forms instanceof HTMLCollection);
+ assert_equals(forms.length, 3);
+
+ var form = document.createElement("form");
+ document.body.appendChild(form);
+ assert_equals(forms.length, 4);
+
+ document.body.removeChild(form);
+ assert_equals(forms.length, 3);
+}, "Document.forms should be a live collection");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByClassName-same.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByClassName-same.html
new file mode 100644
index 0000000000..a91c838d8a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByClassName-same.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Calling getElementsByClassName with the same argument</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-getelementsbyclassname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<div class="abcd"></div>
+</div>
+<script>
+test(function() {
+ var list1 = document.getElementsByClassName("abcd");
+ var list2 = document.getElementsByClassName("abcd");
+ assert_true(list1 === list2 || list1 !== list2);
+}, "The user agent may return the same object as the object returned by the earlier call.");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case-xhtml.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case-xhtml.xhtml
new file mode 100644
index 0000000000..f15edb6f1d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case-xhtml.xhtml
@@ -0,0 +1,21 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>getElementsByName and case</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="test">
+<div name="abcd"></div>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByName("ABCD").length, 0);
+ assert_equals(document.getElementsByName("abcd").length, 1);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case.html
new file mode 100644
index 0000000000..9a82e6805c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-case.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>getElementsByName and case</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<div name="abcd"></div>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByName("ABCD").length, 0);
+ assert_equals(document.getElementsByName("abcd").length, 1);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id-xhtml.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id-xhtml.xhtml
new file mode 100644
index 0000000000..51b9e22f84
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id-xhtml.xhtml
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>getElementsByName and ids</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="test">
+<div id="abcd"></div>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByName("abcd").length, 0);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id.html
new file mode 100644
index 0000000000..099215d3a7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-id.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>getElementsByName and ids</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<div id="abcd"></div>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByName("abcd").length, 0);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-interface.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-interface.html
new file mode 100644
index 0000000000..97646e39cf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-interface.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Document.getElementsByName: interfaces</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var collection = document.getElementsByName("name");
+ assert_class_string(collection, "NodeList");
+ assert_true(collection instanceof NodeList, "Should be a NodeList");
+ assert_false(collection instanceof HTMLCollection,
+ "Should not be a HTMLCollection");
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-liveness.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-liveness.html
new file mode 100644
index 0000000000..74aad6954f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-liveness.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Document.getElementsByName: liveness</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var input = document.createElement("input"),
+ embed = document.createElement("embed");
+ input.setAttribute("name", "test");
+ input.setAttribute("type", "text");
+ embed.setAttribute("name", "test");
+ document.body.appendChild(input);
+ this.add_cleanup(function() { document.body.removeChild(input) });
+ var e = document.getElementsByName("test");
+ assert_true(e instanceof NodeList);
+ assert_equals(e.length, 1);
+
+ document.body.appendChild(embed);
+ assert_equals(e.length, 2);
+
+ document.body.removeChild(embed);
+ assert_equals(e.length, 1);
+}, "Document.getElementsByName() should be a live collection");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace-xhtml.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace-xhtml.xhtml
new file mode 100644
index 0000000000..e09ece7b1f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace-xhtml.xhtml
@@ -0,0 +1,32 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>getElementsByName and foreign namespaces</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="test">
+<p name="math"><math name="math" xmlns="http://www.w3.org/1998/Math/MathML">
+<mi>a</mi>
+<mo>+</mo>
+<mi>b</mi>
+</math></p>
+<p name="svg"><svg width="300" height="100" name="svg" xmlns="http://www.w3.org/2000/svg">
+<rect width="300" height="100" fill="rgb(0,0,255)"/>
+</svg></p>
+</div>
+<script>
+test(function() {
+ var ps = document.getElementById("test")
+ .getElementsByTagName("p");
+ assert_equals(document.getElementsByName("math").length, 1);
+ assert_equals(document.getElementsByName("math")[0], ps[0]);
+ assert_equals(document.getElementsByName("svg").length, 1);
+ assert_equals(document.getElementsByName("svg")[0], ps[1]);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace.html
new file mode 100644
index 0000000000..63b6260424
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-namespace.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>getElementsByName and foreign namespaces</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<p name="math"><math name="math">
+<mi>a</mi>
+<mo>+</mo>
+<mi>b</mi>
+</math>
+<p name="svg"><svg width="300" height="100" name="svg">
+<rect width="300" height="100" fill="rgb(0,0,255)"/>
+</svg>
+</div>
+<script>
+test(function() {
+ var ps = document.getElementById("test")
+ .getElementsByTagName("p");
+ assert_equals(document.getElementsByName("math").length, 1);
+ assert_equals(document.getElementsByName("math")[0], ps[0]);
+ assert_equals(document.getElementsByName("svg").length, 1);
+ assert_equals(document.getElementsByName("svg")[0], ps[1]);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements-xhtml.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements-xhtml.xhtml
new file mode 100644
index 0000000000..c2dc99a55d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements-xhtml.xhtml
@@ -0,0 +1,126 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>getElementsByName and newly introduced HTML elements</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="test">
+<section name="section"></section>
+<article name="article"></article>
+<aside name="aside"></aside>
+<hgroup name="hgroup"></hgroup>
+<header name="header"></header>
+<footer name="footer"></footer>
+<nav name="nav"></nav>
+<dialog name="dialog"></dialog>
+<figure name="figure"></figure>
+<audio name="audio"></audio>
+<video name="video"></video>
+<embed name="embed"></embed>
+<mark name="mark"></mark>
+<meter name="meter"></meter>
+<progress name="progress"></progress>
+<time name="time"></time>
+<canvas name="canvas"></canvas>
+<command name="command"></command>
+<menu name="menu"></menu>
+<details name="details"></details>
+<datalist name="datalist"></datalist>
+<keygen name="keygen"></keygen>
+<output name="output"></output>
+<ruby name="ruby"></ruby>
+<rt name="rt"></rt>
+<rp name="rp"></rp>
+<source name="source"/>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByName("section").length, 1);
+ assert_equals(document.getElementsByName("section")[0],
+ document.getElementsByTagName("section")[0]);
+ assert_equals(document.getElementsByName("article").length, 1);
+ assert_equals(document.getElementsByName("article")[0],
+ document.getElementsByTagName("article")[0]);
+ assert_equals(document.getElementsByName("aside").length, 1);
+ assert_equals(document.getElementsByName("aside")[0],
+ document.getElementsByTagName("aside")[0]);
+ assert_equals(document.getElementsByName("hgroup").length, 1);
+ assert_equals(document.getElementsByName("hgroup")[0],
+ document.getElementsByTagName("hgroup")[0]);
+ assert_equals(document.getElementsByName("header").length, 1);
+ assert_equals(document.getElementsByName("header")[0],
+ document.getElementsByTagName("header")[0]);
+ assert_equals(document.getElementsByName("footer").length, 1);
+ assert_equals(document.getElementsByName("footer")[0],
+ document.getElementsByTagName("footer")[0]);
+ assert_equals(document.getElementsByName("nav").length, 1);
+ assert_equals(document.getElementsByName("nav")[0],
+ document.getElementsByTagName("nav")[0]);
+ assert_equals(document.getElementsByName("dialog").length, 1);
+ assert_equals(document.getElementsByName("dialog")[0],
+ document.getElementsByTagName("dialog")[0]);
+ assert_equals(document.getElementsByName("figure").length, 1);
+ assert_equals(document.getElementsByName("figure")[0],
+ document.getElementsByTagName("figure")[0]);
+ assert_equals(document.getElementsByName("audio").length, 1);
+ assert_equals(document.getElementsByName("audio")[0],
+ document.getElementsByTagName("audio")[0]);
+ assert_equals(document.getElementsByName("video").length, 1);
+ assert_equals(document.getElementsByName("video")[0],
+ document.getElementsByTagName("video")[0]);
+ assert_equals(document.getElementsByName("embed").length, 1);
+ assert_equals(document.getElementsByName("embed")[0],
+ document.getElementsByTagName("embed")[0]);
+ assert_equals(document.getElementsByName("mark").length, 1);
+ assert_equals(document.getElementsByName("mark")[0],
+ document.getElementsByTagName("mark")[0]);
+ assert_equals(document.getElementsByName("meter").length, 1);
+ assert_equals(document.getElementsByName("meter")[0],
+ document.getElementsByTagName("meter")[0]);
+ assert_equals(document.getElementsByName("progress").length, 1);
+ assert_equals(document.getElementsByName("progress")[0],
+ document.getElementsByTagName("progress")[0]);
+ assert_equals(document.getElementsByName("time").length, 1);
+ assert_equals(document.getElementsByName("time")[0],
+ document.getElementsByTagName("time")[0]);
+ assert_equals(document.getElementsByName("canvas").length, 1);
+ assert_equals(document.getElementsByName("canvas")[0],
+ document.getElementsByTagName("canvas")[0]);
+ assert_equals(document.getElementsByName("command").length, 1);
+ assert_equals(document.getElementsByName("command")[0],
+ document.getElementsByTagName("command")[0]);
+ assert_equals(document.getElementsByName("menu").length, 1);
+ assert_equals(document.getElementsByName("menu")[0],
+ document.getElementsByTagName("menu")[0]);
+ assert_equals(document.getElementsByName("details").length, 1);
+ assert_equals(document.getElementsByName("details")[0],
+ document.getElementsByTagName("details")[0]);
+ assert_equals(document.getElementsByName("datalist").length, 1);
+ assert_equals(document.getElementsByName("datalist")[0],
+ document.getElementsByTagName("datalist")[0]);
+ assert_equals(document.getElementsByName("keygen").length, 1);
+ assert_equals(document.getElementsByName("keygen")[0],
+ document.getElementsByTagName("keygen")[0]);
+ assert_equals(document.getElementsByName("output").length, 1);
+ assert_equals(document.getElementsByName("output")[0],
+ document.getElementsByTagName("output")[0]);
+ assert_equals(document.getElementsByName("ruby").length, 1);
+ assert_equals(document.getElementsByName("ruby")[0],
+ document.getElementsByTagName("ruby")[0]);
+ assert_equals(document.getElementsByName("rt").length, 1);
+ assert_equals(document.getElementsByName("rt")[0],
+ document.getElementsByTagName("rt")[0]);
+ assert_equals(document.getElementsByName("rp").length, 1);
+ assert_equals(document.getElementsByName("rp")[0],
+ document.getElementsByTagName("rp")[0]);
+ assert_equals(document.getElementsByName("source").length, 1);
+ assert_equals(document.getElementsByName("source")[0],
+ document.getElementsByTagName("source")[0]);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements.html
new file mode 100644
index 0000000000..2ab42b9733
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-newelements.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>getElementsByName and newly introduced HTML elements</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<section name="section"></section>
+<article name="article"></article>
+<aside name="aside"></aside>
+<hgroup name="hgroup"></hgroup>
+<header name="header"></header>
+<footer name="footer"></footer>
+<nav name="nav"></nav>
+<dialog name="dialog"></dialog>
+<figure name="figure"></figure>
+<audio name="audio"></audio>
+<video name="video"></video>
+<embed name="embed"></embed>
+<mark name="mark"></mark>
+<meter name="meter"></meter>
+<progress name="progress"></progress>
+<time name="time"></time>
+<canvas name="canvas"></canvas>
+<command name="command"></command>
+<menu name="menu"></menu>
+<details name="details"></details>
+<datalist name="datalist"></datalist>
+<keygen name="keygen"></keygen>
+<output name="output"></output>
+<ruby name="ruby"></ruby>
+<rt name="rt"></rt>
+<rp name="rp"></rp>
+<source name="source">
+</div>
+<script>
+var testDiv = document.getElementById("test");
+for (var i = 0; i < testDiv.children.length; i++) {
+ var name = testDiv.children[i].getAttribute("name");
+ test(function() {
+ assert_equals(document.getElementsByName(name).length, 1);
+ assert_equals(document.getElementsByName(name)[0],
+ document.getElementsByTagName(name)[0]);
+ }, 'getElementsByName("' + name + '")');
+}
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef-xhtml.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef-xhtml.xhtml
new file mode 100644
index 0000000000..06d182860b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef-xhtml.xhtml
@@ -0,0 +1,35 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Calling getElementsByName with null and undefined</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname"/>
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-DOMString"/>
+<link rel="help" href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf#page=57"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var n = document.createElement("div");
+ n.setAttribute("name", "null");
+
+ document.body.appendChild(n);
+ this.add_cleanup(function() { document.body.removeChild(n) });
+
+ assert_equals(document.getElementsByName(null)[0], n);
+}, "getElementsByName(null)");
+
+test(function() {
+ var u = document.createElement("div");
+ u.setAttribute("name", "undefined");
+
+ document.body.appendChild(u);
+ this.add_cleanup(function() { document.body.removeChild(u) });
+
+ assert_equals(document.getElementsByName(undefined)[0], u);
+}, "getElementsByName(undefined)");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef.html
new file mode 100644
index 0000000000..f1dfbf9e39
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-null-undef.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Calling getElementsByName with null and undefined</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-DOMString">
+<link rel="help" href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf#page=57">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var n = document.createElement("div");
+ n.setAttribute("name", "null");
+
+ document.body.appendChild(n);
+ this.add_cleanup(function() { document.body.removeChild(n) });
+
+ assert_equals(document.getElementsByName(null)[0], n);
+}, "getElementsByName(null)");
+
+test(function() {
+ var u = document.createElement("div");
+ u.setAttribute("name", "undefined");
+
+ document.body.appendChild(u);
+ this.add_cleanup(function() { document.body.removeChild(u) });
+
+ assert_equals(document.getElementsByName(undefined)[0], u);
+}, "getElementsByName(undefined)");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param-xhtml.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param-xhtml.xhtml
new file mode 100644
index 0000000000..e57e9d5c0d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param-xhtml.xhtml
@@ -0,0 +1,28 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>getElementsByName and the param element</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="test">
+<param name="test1"/>
+<object>
+<param name="test2"/>
+</object>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByName("test1").length, 1);
+ assert_equals(document.getElementsByName("test1")[0],
+ document.getElementsByTagName("param")[0]);
+ assert_equals(document.getElementsByName("test2").length, 1);
+ assert_equals(document.getElementsByName("test2")[0],
+ document.getElementsByTagName("param")[1]);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param.html
new file mode 100644
index 0000000000..aa1bb01bfa
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-param.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>getElementsByName and the param element</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<param name="test1">
+<object>
+<param name="test2">
+</object>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByName("test1").length, 1);
+ assert_equals(document.getElementsByName("test1")[0],
+ document.getElementsByTagName("param")[0]);
+ assert_equals(document.getElementsByName("test2").length, 1);
+ assert_equals(document.getElementsByName("test2")[0],
+ document.getElementsByTagName("param")[1]);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-same.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-same.html
new file mode 100644
index 0000000000..f122857a2b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.getElementsByName/document.getElementsByName-same.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Calling getElementsByName with the same argument</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<div name="abcd"></div>
+</div>
+<script>
+test(function() {
+ var list1 = document.getElementsByName("abcd");
+ var list2 = document.getElementsByName("abcd");
+ assert_true(list1 === list2 || list1 !== list2);
+}, "The user agent may return the same object as the object returned by the earlier call.");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-01.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-01.html
new file mode 100644
index 0000000000..f919d79a32
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-01.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>document.head</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-head">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var head = document.getElementsByTagName("head")[0];
+ assert_equals(document.head, head);
+ document.head = "";
+ assert_equals(document.head, head);
+ document.head = document.createElement("head");
+ assert_equals(document.head, head);
+ document.documentElement.appendChild(document.createElement("head"));
+ assert_equals(document.head, head);
+ var head2 = document.createElement("head");
+ document.documentElement.insertBefore(head2, head);
+ assert_equals(document.head, head2);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-02.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-02.html
new file mode 100644
index 0000000000..d0189574e2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.head-02.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>document.head</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-head">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var HTML = "http://www.w3.org/1999/xhtml";
+test(function() {
+ var head = document.getElementsByTagName("head")[0];
+ assert_equals(document.head, head);
+ var head2 = document.createElementNS(HTML, "blah:head");
+ document.documentElement.insertBefore(head2, head);
+ assert_equals(document.head, head2);
+ var head3 = document.createElementNS("http://www.example.org/", "blah:head");
+ document.documentElement.insertBefore(head3, head2);
+ assert_equals(document.head, head2);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.images.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.images.html
new file mode 100644
index 0000000000..10ebe5ee8a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.images.html
@@ -0,0 +1,119 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Document.images</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<div id=test>
+<img>
+<img id=x><img name=y><img id=z1 name=z2>
+<img id=a><img id=a>
+<img name=b><img name=b>
+<img id=><img name=>
+<input type=image name=input>
+</div>
+<script>
+function assert_all(aAssertFunc, aCollection) {
+ for (var i = 0; i < aCollection.length; ++i) {
+ aAssertFunc(aCollection[i]);
+ }
+}
+
+var XHTML = "http://www.w3.org/1999/xhtml";
+var div, images, c;
+
+setup(function() {
+ div = document.getElementById("test");
+ var foreign =
+ div.appendChild(document.createElementNS("http://example.org", "img"));
+ foreign.setAttribute("id", "f");
+
+ images = [].slice.call(div.getElementsByTagNameNS(XHTML, "img"));
+
+ c = document.images;
+});
+
+test(function() {
+ assert_equals(c.length, 10);
+ assert_array_equals(c, images);
+
+ assert_all(function (aElement) {
+ assert_equals(aElement.namespaceURI, XHTML);
+ }, c);
+}, "document.images should contain all HTML img elements");
+
+test(function() {
+ assert_equals(c.x, images[1]);
+ assert_equals(c.namedItem("x"), images[1]);
+ assert_true("x" in c, '"x" in c');
+}, "img with id");
+
+test(function() {
+ assert_equals(c.y, images[2]);
+ assert_equals(c.namedItem("y"), images[2]);
+ assert_true("y" in c, '"y" in c');
+}, "img with name");
+
+test(function() {
+ assert_equals(c.z1, images[3]);
+ assert_equals(c.namedItem("z1"), images[3]);
+ assert_true("z1" in c, '"z1" in c');
+ assert_equals(c.z2, images[3]);
+ assert_equals(c.namedItem("z2"), images[3]);
+ assert_true("z2" in c, '"z2" in c');
+}, "img with id and name");
+
+test(function() {
+ assert_equals(c.a, images[4]);
+ assert_equals(c.namedItem("a"), images[4]);
+ assert_true("a" in c, '"a" in c');
+}, "Two img elements with the same id");
+
+test(function() {
+ assert_equals(c.b, images[6]);
+ assert_equals(c.namedItem("b"), images[6]);
+ assert_true("b" in c, '"b" in c');
+}, "Two img elements with the same name");
+
+test(function() {
+ assert_equals(c.c, undefined);
+ assert_equals(c.namedItem("c"), null);
+ assert_false("c" in c, '"c" in c');
+}, "Unknown name should not be in the collection");
+
+test(function() {
+ assert_equals(c.f, undefined);
+ assert_equals(c.namedItem("f"), null);
+ assert_false("f" in c, '"f" in c');
+}, "Foreign element should not be in the collection");
+
+test(function() {
+ assert_equals(c.input, undefined);
+ assert_equals(c.namedItem("input"), null);
+ assert_false("input" in c, '"input" in c');
+ var input = div.getElementsByTagName("input")[0];
+ assert_all(function (aElement) {
+ assert_not_equals(aElement.namespaceURI, input);
+ }, c);
+}, "Input elements should not be in the collection");
+
+test(function() {
+ assert_equals(c[""], undefined);
+ assert_equals(c.namedItem(""), null);
+ assert_false("" in c, '"" in c');
+}, "The empty string should not be in the collections");
+
+test(function() {
+ var div = document.getElementById("test");
+ var imgs = document.images;
+ assert_true(imgs instanceof HTMLCollection);
+ assert_equals(imgs.length, 10);
+
+ var img = document.createElement("img");
+ div.appendChild(img);
+ assert_equals(imgs.length, 11);
+
+ div.removeChild(img);
+ assert_equals(imgs.length, 10);
+}, "Document.images should be a live collection");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.links.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.links.html
new file mode 100644
index 0000000000..69c7d8c52c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.links.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Document.links</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<div id=test>
+<a href=""></a>
+<a href=""></a>
+</div>
+<script>
+test(function() {
+ var div = document.getElementById("test");
+ var links = document.links;
+ assert_true(links instanceof HTMLCollection);
+ assert_equals(links.length, 2);
+
+ var a = document.createElement("a");
+ a.setAttribute("href", "");
+ div.appendChild(a);
+ assert_equals(links.length, 3);
+
+ div.removeChild(a);
+ assert_equals(links.length, 2);
+}, "Document.links should be a live collection");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.scripts.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.scripts.html
new file mode 100644
index 0000000000..82d3db14b2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.scripts.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Document.scripts</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ var scripts = document.scripts;
+ assert_true(scripts instanceof HTMLCollection);
+ assert_equals(scripts.length, 3);
+
+ var script = document.createElement("script");
+ document.body.appendChild(script);
+ assert_equals(scripts.length, 4);
+
+ document.body.removeChild(script);
+ assert_equals(scripts.length, 3);
+}, "Document.scripts should be a live collection");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-01.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-01.html
new file mode 100644
index 0000000000..05ddab9ca4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-01.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>document.title with head blown away</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.title, "document.title with head blown away");
+})
+test(function() {
+ var head = document.getElementsByTagName("head")[0];
+ assert_true(!!head, "Head gone?!")
+ head.parentNode.removeChild(head);
+ assert_false(!!document.getElementsByTagName("head")[0], "Head still there?!")
+ document.title = "FAIL";
+ assert_equals(document.title, "");
+})
+test(function() {
+ var title2 = document.createElement("title");
+ title2.appendChild(document.createTextNode("PASS"));
+ document.body.appendChild(title2);
+ assert_equals(document.title, "PASS");
+})
+test(function() {
+ var title3 = document.createElement("title");
+ title3.appendChild(document.createTextNode("PASS2"));
+ document.documentElement.insertBefore(title3, document.body);
+ assert_equals(document.title, "PASS2");
+})
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-02.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-02.xhtml
new file mode 100644
index 0000000000..917b8787df
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-02.xhtml
@@ -0,0 +1,37 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.title with head blown away</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.title, "document.title with head blown away");
+})
+test(function() {
+ var head = document.getElementsByTagName("head")[0];
+ assert_true(!!head, "Head gone?!")
+ head.parentNode.removeChild(head);
+ assert_false(!!document.getElementsByTagName("head")[0], "Head still there?!")
+ document.title = "FAIL";
+ assert_equals(document.title, "");
+})
+test(function() {
+ var title2 = document.createElement("title");
+ title2.appendChild(document.createTextNode("PASS"));
+ document.body.appendChild(title2);
+ assert_equals(document.title, "PASS");
+})
+test(function() {
+ var title3 = document.createElement("title");
+ title3.appendChild(document.createTextNode("PASS2"));
+ document.documentElement.insertBefore(title3, document.body);
+ assert_equals(document.title, "PASS2");
+})
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-03.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-03.html
new file mode 100644
index 0000000000..952c29db5f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-03.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title> document.title and space normalization </title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function test_title(set, expected) {
+ test(function() {
+ document.title = set;
+ assert_equals(document.title, expected);
+ }, "document.title after setting to " + format_value(set));
+}
+
+test(function() {
+ // Single space characters must be normalized. (WHATWG r4353)
+ assert_equals(document.title, "document.title and space normalization");
+}, "document.title initial value");
+
+test_title("one space", "one space");
+test_title("two spaces", "two spaces");
+test_title("one\ttab", "one tab");
+test_title("two\t\ttabs", "two tabs");
+test_title("one\nnewline", "one newline");
+test_title("two\n\nnewlines", "two newlines");
+test_title("one\fform feed", "one form feed");
+test_title("two\f\fform feeds", "two form feeds");
+test_title("one\rcarriage return", "one carriage return");
+test_title("two\r\rcarriage returns", "two carriage returns");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-04.xhtml b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-04.xhtml
new file mode 100644
index 0000000000..fbe891650a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-04.xhtml
@@ -0,0 +1,48 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title> document.title and space normalization </title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ // Single space characters must be normalized. (WHATWG r4353)
+ assert_equals(document.title, "document.title and space normalization");
+
+ document.title = "one space";
+ assert_equals(document.title, "one space");
+
+ document.title = "two spaces";
+ assert_equals(document.title, "two spaces");
+
+ document.title = "one\ttab";
+ assert_equals(document.title, "one tab");
+
+ document.title = "two\t\ttabs";
+ assert_equals(document.title, "two tabs");
+
+ document.title = "one\nnewline";
+ assert_equals(document.title, "one newline");
+
+ document.title = "two\n\nnewlines";
+ assert_equals(document.title, "two newlines");
+
+ document.title = "one\fform feed";
+ assert_equals(document.title, "one form feed");
+
+ document.title = "two\f\fform feeds";
+ assert_equals(document.title, "two form feeds");
+
+ document.title = "one\rcarriage return";
+ assert_equals(document.title, "one carriage return");
+
+ document.title = "two\r\rcarriage returns";
+ assert_equals(document.title, "two carriage returns");
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-05.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-05.html
new file mode 100644
index 0000000000..df6ffc30f8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-05.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<title>document.title and White_Space characters</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var White_Space = [
+ "\u000B",
+ "\u0085",
+ "\u00A0",
+ "\u1680",
+ "\u180E",
+ "\u2000",
+ "\u2001",
+ "\u2002",
+ "\u2003",
+ "\u2004",
+ "\u2005",
+ "\u2006",
+ "\u2007",
+ "\u2008",
+ "\u2009",
+ "\u200A",
+ "\u2028",
+ "\u2029",
+ "\u202F",
+ "\u205F",
+ "\u3000"
+];
+
+White_Space.forEach(function(character, i) {
+ test(function() {
+ var s = character + "a" + character + character + "b" + character + "c" +
+ String(i) + character;
+ document.title = s;
+ assert_equals(document.title, s);
+ }, "Removing whitespace in document.title: U+" +
+ character.charCodeAt(0).toString(16));
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-06.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-06.html
new file mode 100644
index 0000000000..a80723f238
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-06.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.title and the empty string</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<meta name="assert" content="On setting document.title to the empty string, no text node must be created.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var head = document.documentElement.firstChild;
+ head.removeChild(head.firstChild);
+ assert_equals(document.title, "");
+ document.title = "";
+ assert_equals(document.title, "");
+ assert_true(head.lastChild instanceof HTMLTitleElement, "Need a title element.");
+ assert_equals(head.lastChild.firstChild, null);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-07.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-07.html
new file mode 100644
index 0000000000..9723d3f811
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-07.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Document.title and DOMImplementation.createHTMLDocument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/dom/nodes/DOMImplementation-createHTMLDocument.js"></script>
+<div id="log"></div>
+<script>
+createHTMLDocuments(function(doc, expectedtitle, normalizedtitle) {
+ assert_equals(doc.title, normalizedtitle)
+})
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-08.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-08.html
new file mode 100644
index 0000000000..a643b75f4e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-08.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.title, "");
+}, "No title element");
+
+test(function() {
+ var title = document.createElement("title");
+ title.appendChild(document.createTextNode("PASS"));
+ document.head.appendChild(title);
+ assert_equals(document.title, "PASS");
+
+ title.appendChild(document.createTextNode("PASS2"));
+ title.appendChild(document.createTextNode("PASS3"));
+ assert_equals(document.title, "PASSPASS2PASS3");
+}, "title element contains multiple child text nodes");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html
new file mode 100644
index 0000000000..a3273f626c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
+var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+
+function newSVGDocument() {
+ return document.implementation.createDocument(SVG_NAMESPACE, "svg", null);
+}
+
+function assertIsSVGTitle(element, expectedText) {
+ assert_equals(element.namespaceURI, SVG_NAMESPACE);
+ assert_equals(element.localName, "title");
+ assert_equals(element.textContent, expectedText);
+}
+
+test(function() {
+ var doc = newSVGDocument();
+ assert_equals(doc.title, "");
+ var child = doc.createElementNS(SVG_NAMESPACE, "x-child");
+ doc.documentElement.appendChild(child);
+ doc.title = "foo";
+ assertIsSVGTitle(doc.documentElement.firstChild, "foo");
+ assert_equals(doc.title, "foo");
+}, "No title element in SVG document");
+
+test(function() {
+ var doc = newSVGDocument();
+ var title = doc.createElementNS(SVG_NAMESPACE, "title");
+ title.textContent = "foo";
+ doc.documentElement.appendChild(title)
+ assert_equals(doc.title, "foo");
+ doc.title += "bar";
+ assert_equals(title.textContent, "foobar");
+ assert_equals(title.childNodes.length, 1);
+ assert_true(title.childNodes[0] instanceof Text);
+ assert_equals(doc.title, "foobar");
+ doc.title = "";
+ assert_equals(title.textContent, "");
+ assert_equals(doc.title, "");
+ assert_equals(title.childNodes.length, 0);
+}, "Title element in SVG document");
+
+test(function() {
+ var doc = newSVGDocument();
+ var title = doc.createElementNS(SVG_NAMESPACE, "title");
+ title.textContent = "foo";
+ var child = doc.createElementNS(SVG_NAMESPACE, "x-child");
+ child.appendChild(title);
+ doc.documentElement.appendChild(child);
+ assert_equals(doc.title, "");
+
+ // Now test that on setting, we create a new element and don't change the
+ // existing one
+ doc.title = "bar";
+ assert_equals(title.textContent, "foo");
+ assertIsSVGTitle(doc.documentElement.firstChild, "bar");
+ assert_equals(doc.title, "bar");
+}, "Title element not child of SVG root");
+
+test(function() {
+ var doc = newSVGDocument();
+ var title = doc.createElementNS(HTML_NAMESPACE, "title");
+ title.textContent = "foo";
+ doc.documentElement.appendChild(title);
+ assert_equals(doc.title, "");
+}, "Title element not in SVG namespace");
+
+test(function() {
+ // "SVG" != "svg"
+ var doc = document.implementation.createDocument(SVG_NAMESPACE, "SVG", null);
+
+ // Per spec, this does nothing
+ doc.title = "foo";
+ assert_equals(doc.documentElement.childNodes.length, 0);
+ assert_equals(doc.title, "");
+
+ // An SVG title is ignored by .title
+ doc.documentElement.appendChild(doc.createElementNS(SVG_NAMESPACE, "title"));
+ doc.documentElement.lastChild.textContent = "foo";
+ assert_equals(doc.title, "");
+
+ // But an HTML title is respected
+ doc.documentElement.appendChild(doc.createElementNS(HTML_NAMESPACE, "title"));
+ doc.documentElement.lastChild.textContent = "bar";
+ assert_equals(doc.title, "bar");
+
+ // Even if it's not a child of the root
+ var div = doc.createElementNS(HTML_NAMESPACE, "div");
+ div.appendChild(doc.documentElement.lastChild);
+ doc.documentElement.appendChild(div);
+ assert_equals(doc.title, "bar");
+}, 'Root element not named "svg"');
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-not-in-html-svg.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-not-in-html-svg.html
new file mode 100644
index 0000000000..40eccd3de3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-not-in-html-svg.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+function newXMLDocument() {
+ return document.implementation.createDocument(null, "foo", null);
+}
+
+test(function() {
+ var doc = newXMLDocument();
+ assert_equals(doc.title, "");
+ doc.title = "fail";
+ assert_equals(doc.title, "");
+}, "Should not be able to set document title in XML document");
+
+test(function() {
+ var doc = newXMLDocument();
+ doc.documentElement.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "html:title"));
+ assert_equals(doc.title, "");
+ doc.title = "fail";
+ assert_equals(doc.title, "");
+}, "Should not be able to set document title in XML document with html:title element");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-01.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-01.html
new file mode 100644
index 0000000000..2b428aa65b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-01.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: img id &amp; name</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<img id="a" name="b">
+</div>
+<script>
+test(function() {
+ assert_equals(document.a, document.getElementsByTagName("img")[0]);
+ assert_equals(document['a'], document.getElementsByTagName("img")[0]);
+ assert_equals(document.b, document.getElementsByTagName("img")[0]);
+ assert_equals(document['b'], document.getElementsByTagName("img")[0]);
+}, "img elements that have a name and id attribute, should be accessible by both values.");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-02.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-02.html
new file mode 100644
index 0000000000..8c3155e7e4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-02.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: iframes</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<iframe name="test1"></iframe>
+
+<iframe name="test2"></iframe>
+<iframe name="test2"></iframe>
+
+<iframe name="test3"></iframe>
+<img name="test3">
+
+<img name="test4">
+<iframe name="test4"></iframe>
+
+<iframe id="test5"></iframe>
+
+<iframe name="test6" id="fail"></iframe>
+
+<iframe name="fail" id="test7"></iframe>
+
+<iframe name="42"></iframe>
+</div>
+<script>
+test(function() {
+ var iframe = document.getElementsByTagName("iframe")[0];
+ assert_equals(iframe.name, "test1");
+
+ assert_true("test1" in document, '"test1" in document should be true');
+ assert_equals(document.test1, iframe.contentWindow);
+}, "If the only named item is an iframe, the contentWindow should be returned.");
+
+test(function() {
+ var iframe1 = document.getElementsByTagName("iframe")[1];
+ assert_equals(iframe1.name, "test2");
+ var iframe2 = document.getElementsByTagName("iframe")[2];
+ assert_equals(iframe2.name, "test2");
+
+ assert_true("test2" in document, '"test2" in document should be true');
+ var collection = document.test2;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [iframe1, iframe2]);
+}, "If there are two iframes, a collection should be returned.");
+
+test(function() {
+ var iframe = document.getElementsByTagName("iframe")[3];
+ assert_equals(iframe.name, "test3");
+ var img = document.getElementsByTagName("img")[0];
+ assert_equals(img.name, "test3");
+
+ assert_true("test3" in document, '"test3" in document should be true');
+ var collection = document.test3;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [iframe, img]);
+}, "If there are an iframe and another element (iframe first), a collection should be returned.");
+
+test(function() {
+ var iframe = document.getElementsByTagName("iframe")[4];
+ assert_equals(iframe.name, "test4");
+ var img = document.getElementsByTagName("img")[1];
+ assert_equals(img.name, "test4");
+
+ assert_true("test4" in document, '"test4" in document should be true');
+ var collection = document.test4;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [img, iframe]);
+}, "If there are an iframe and another element (iframe last), a collection should be returned.");
+
+test(function() {
+ assert_false("test5" in document, '"test5" in document should be false');
+ assert_equals(document.test5, undefined);
+}, "If an iframe has an id and no name, it should not be returned.");
+
+test(function() {
+ var iframe = document.getElementsByTagName("iframe")[6];
+ assert_equals(iframe.name, "test6");
+
+ assert_true("test6" in document, '"test6" in document should be true');
+ assert_equals(document.test6, iframe.contentWindow);
+}, "If an iframe has a name and a different id, it should be returned by its name.");
+
+test(function() {
+ assert_false("test7" in document, '"test7" in document should be false');
+ assert_equals(document.test7, undefined);
+}, "If an iframe has an id and a different name, it should not be returned by its id.");
+
+test(function() {
+ var iframe = document.getElementsByTagName("iframe")[8];
+ assert_equals(iframe.name, "42");
+
+ assert_true(42 in document, '42 in document should be true');
+ assert_equals(document[42], iframe.contentWindow);
+}, "An iframe whose name looks like an array index should work.");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-03.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-03.html
new file mode 100644
index 0000000000..be2ca173b0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-03.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: applets</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<applet name=test1></applet>
+<script>
+test(function() {
+ var applet = document.getElementsByTagName("applet")[0];
+ assert_equals(applet.name, undefined);
+
+ assert_false("test1" in document, '"test1" in document should be false');
+ assert_equals(document.test1, undefined);
+}, "applet elements are (mostly) gone");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-04.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-04.html
new file mode 100644
index 0000000000..b7c3ef8e9b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-04.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: forms</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form name=test1></form>
+
+<form name=test2></form>
+<form name=test2></form>
+
+<form id=test3></form>
+
+<form id=test4></form>
+<form id=test4></form>
+
+<form name=test5></form>
+<form id=test5></form>
+
+<form id=test6></form>
+<form name=test6></form>
+
+<form id=test7 name=fail></form>
+
+<form name=test8 id=fail></form>
+</div>
+<script>
+test(function() {
+ var form = document.getElementsByTagName("form")[0];
+ assert_equals(form.name, "test1");
+
+ assert_true("test1" in document, '"test1" in document should be true');
+ assert_equals(document.test1, form);
+}, "If there is one form, it should be returned (name)");
+
+test(function() {
+ var form1 = document.getElementsByTagName("form")[1];
+ assert_equals(form1.name, "test2");
+ var form2 = document.getElementsByTagName("form")[2];
+ assert_equals(form2.name, "test2");
+
+ assert_true("test2" in document, '"test2" in document should be true');
+ var collection = document.test2;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [form1, form2]);
+}, "If there are two forms, a collection should be returned. (name)");
+
+test(function() {
+ var form = document.getElementsByTagName("form")[3];
+ assert_equals(form.id, "test3");
+
+ assert_false("test3" in document, '"test3" in document should be false');
+ assert_equals(document.test3, undefined);
+}, "If there is one form, it should not be returned (id)");
+
+test(function() {
+ var form1 = document.getElementsByTagName("form")[4];
+ assert_equals(form1.id, "test4");
+ var form2 = document.getElementsByTagName("form")[5];
+ assert_equals(form2.id, "test4");
+
+ assert_false("test4" in document, '"test4" in document should be false');
+ assert_equals(document.test4, undefined);
+}, "If there are two forms, nothing should be returned. (id)");
+
+test(function() {
+ var form1 = document.getElementsByTagName("form")[6];
+ assert_equals(form1.name, "test5");
+ var form2 = document.getElementsByTagName("form")[7];
+ assert_equals(form2.id, "test5");
+
+ assert_true("test5" in document, '"test5" in document should be true');
+ assert_equals(document.test5, form1);
+}, "If there are two forms, a collection should be returned. (name and id)");
+
+test(function() {
+ var form1 = document.getElementsByTagName("form")[8];
+ assert_equals(form1.id, "test6");
+ var form2 = document.getElementsByTagName("form")[9];
+ assert_equals(form2.name, "test6");
+
+ assert_true("test6" in document, '"test6" in document should be true');
+ assert_equals(document.test6, form2);
+}, "If there are two forms, a collection should be returned. (id and name)");
+
+test(function() {
+ var form = document.getElementsByTagName("form")[10];
+ assert_equals(form.id, "test7");
+
+ assert_false("test7" in document, '"test7" in document should be false');
+ assert_equals(document.test7, undefined);
+}, "A name shouldn't affect getting an form by id");
+
+test(function() {
+ var form = document.getElementsByTagName("form")[11];
+ assert_equals(form.name, "test8");
+
+ assert_true("test8" in document, '"test8" in document should be true');
+ assert_equals(document.test8, form);
+}, "An id shouldn't affect getting an form by name");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-05.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-05.html
new file mode 100644
index 0000000000..843ce35796
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-05.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: embeds</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<embed name=test1></embed>
+
+<embed name=test2></embed>
+<embed name=test2></embed>
+
+<embed id=test3></embed>
+
+<embed id=test4></embed>
+<embed id=test4></embed>
+
+<embed name=test5></embed>
+<embed id=test5></embed>
+
+<embed id=test6></embed>
+<embed name=test6></embed>
+
+<embed id=test7 name=fail></embed>
+
+<embed name=test8 id=fail></embed>
+</div>
+<script>
+test(function() {
+ var embed = document.getElementsByTagName("embed")[0];
+ assert_equals(embed.name, "test1");
+
+ assert_true("test1" in document, '"test1" in document should be true');
+ assert_equals(document.test1, embed);
+}, "If there is one embed, it should be returned (name)");
+
+test(function() {
+ var embed1 = document.getElementsByTagName("embed")[1];
+ assert_equals(embed1.name, "test2");
+ var embed2 = document.getElementsByTagName("embed")[2];
+ assert_equals(embed2.name, "test2");
+
+ assert_true("test2" in document, '"test2" in document should be true');
+ var collection = document.test2;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [embed1, embed2]);
+}, "If there are two embeds, a collection should be returned. (name)");
+
+test(function() {
+ var embed = document.getElementsByTagName("embed")[3];
+ assert_equals(embed.id, "test3");
+
+ assert_false("test3" in document, '"test3" in document should be false');
+ assert_equals(document.test3, undefined);
+}, "If there is one embed, it should not be returned (id)");
+
+test(function() {
+ var embed1 = document.getElementsByTagName("embed")[4];
+ assert_equals(embed1.id, "test4");
+ var embed2 = document.getElementsByTagName("embed")[5];
+ assert_equals(embed2.id, "test4");
+
+ assert_false("test4" in document, '"test4" in document should be false');
+ assert_equals(document.test4, undefined);
+}, "If there are two embeds, nothing should be returned. (id)");
+
+test(function() {
+ var embed1 = document.getElementsByTagName("embed")[6];
+ assert_equals(embed1.name, "test5");
+ var embed2 = document.getElementsByTagName("embed")[7];
+ assert_equals(embed2.id, "test5");
+
+ assert_true("test5" in document, '"test5" in document should be true');
+ assert_equals(document.test5, embed1);
+}, "If there are two embeds, a collection should be returned. (name and id)");
+
+test(function() {
+ var embed1 = document.getElementsByTagName("embed")[8];
+ assert_equals(embed1.id, "test6");
+ var embed2 = document.getElementsByTagName("embed")[9];
+ assert_equals(embed2.name, "test6");
+
+ assert_true("test6" in document, '"test6" in document should be true');
+ assert_equals(document.test6, embed2);
+}, "If there are two embeds, a collection should be returned. (id and name)");
+
+test(function() {
+ var embed = document.getElementsByTagName("embed")[10];
+ assert_equals(embed.id, "test7");
+
+ assert_false("test7" in document, '"test7" in document should be false');
+ assert_equals(document.test7, undefined);
+}, "A name shouldn't affect getting an embed by id");
+
+test(function() {
+ var embed = document.getElementsByTagName("embed")[11];
+ assert_equals(embed.name, "test8");
+
+ assert_true("test8" in document, '"test8" in document should be true');
+ assert_equals(document.test8, embed);
+}, "An id shouldn't affect getting an embed by name");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html
new file mode 100644
index 0000000000..15a72b5f6b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: imgs</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<img name=test1>
+
+<img name=test2>
+<img name=test2>
+
+<img id=test3>
+
+<img id=test4>
+<img id=test4 name="">
+
+<img name=test5>
+<img id=test5>
+
+<img id=test6>
+<img name=test6>
+
+<img id=test7 name=fail>
+
+<img name=test8 id=fail>
+</div>
+<script>
+test(function() {
+ var img = document.getElementsByTagName("img")[0];
+ assert_equals(img.name, "test1");
+
+ assert_true("test1" in document, '"test1" in document should be true');
+ assert_equals(document.test1, img);
+}, "If there is one img, it should be returned (name)");
+
+test(function() {
+ var img1 = document.getElementsByTagName("img")[1];
+ assert_equals(img1.name, "test2");
+ var img2 = document.getElementsByTagName("img")[2];
+ assert_equals(img2.name, "test2");
+
+ assert_true("test2" in document, '"test2" in document should be true');
+ var collection = document.test2;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [img1, img2]);
+}, "If there are two imgs, a collection should be returned. (name)");
+
+test(function() {
+ var img = document.getElementsByTagName("img")[3];
+ assert_equals(img.id, "test3");
+
+ assert_false("test3" in document, '"test3" in document should be false');
+ assert_equals(document.test3, undefined);
+}, "If there is one img, it should not be returned (id)");
+
+test(function() {
+ var img1 = document.getElementsByTagName("img")[4];
+ assert_equals(img1.id, "test4");
+ var img2 = document.getElementsByTagName("img")[5];
+ assert_equals(img2.id, "test4");
+
+ assert_false("test4" in document, '"test4" in document should be false');
+ assert_equals(document.test4, undefined);
+}, "If there are two imgs, nothing should be returned. (id)");
+
+test(function() {
+ var img1 = document.getElementsByTagName("img")[6];
+ assert_equals(img1.name, "test5");
+ var img2 = document.getElementsByTagName("img")[7];
+ assert_equals(img2.id, "test5");
+
+ assert_true("test5" in document, '"test5" in document should be true');
+ assert_equals(document.test5, img1);
+}, "If there are two imgs, the one with a name should be returned. (name and id)");
+
+test(function() {
+ var img1 = document.getElementsByTagName("img")[8];
+ assert_equals(img1.id, "test6");
+ var img2 = document.getElementsByTagName("img")[9];
+ assert_equals(img2.name, "test6");
+
+ assert_true("test6" in document, '"test6" in document should be true');
+ assert_equals(document.test6, img2);
+}, "If there are two imgs, the one with a name should be returned. (id and name)");
+
+test(function() {
+ var img = document.getElementsByTagName("img")[10];
+ assert_equals(img.id, "test7");
+
+ assert_true("test7" in document, '"test7" in document should be true');
+ assert_equals(document.test7, img);
+}, "A name should affect getting an img by id");
+
+test(function() {
+ var img = document.getElementsByTagName("img")[11];
+ assert_equals(img.name, "test8");
+
+ assert_true("test8" in document, '"test8" in document should be true');
+ assert_equals(document.test8, img);
+}, "An id shouldn't affect getting an img by name");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-07.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-07.html
new file mode 100644
index 0000000000..fc3f06c01b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-07.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: objects</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<object name=test1></object>
+
+<object name=test2></object>
+<object name=test2></object>
+
+<object id=test3></object>
+
+<object id=test4></object>
+<object id=test4></object>
+
+<object name=test5></object>
+<object id=test5></object>
+
+<object id=test6></object>
+<object name=test6></object>
+
+<object id=test7 name=fail></object>
+
+<object name=test8 id=fail></object>
+</div>
+<script>
+test(function() {
+ var object = document.getElementsByTagName("object")[0];
+ assert_equals(object.name, "test1");
+
+ assert_true("test1" in document, '"test1" in document should be true');
+ assert_equals(document.test1, object);
+}, "If there is one object, it should be returned (name)");
+
+test(function() {
+ var object1 = document.getElementsByTagName("object")[1];
+ assert_equals(object1.name, "test2");
+ var object2 = document.getElementsByTagName("object")[2];
+ assert_equals(object2.name, "test2");
+
+ assert_true("test2" in document, '"test2" in document should be true');
+ var collection = document.test2;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [object1, object2]);
+}, "If there are two objects, a collection should be returned. (name)");
+
+test(function() {
+ var object = document.getElementsByTagName("object")[3];
+ assert_equals(object.id, "test3");
+
+ assert_true("test3" in document, '"test3" in document should be true');
+ assert_equals(document.test3, object);
+}, "If there is one object, it should be returned (id)");
+
+test(function() {
+ var object1 = document.getElementsByTagName("object")[4];
+ assert_equals(object1.id, "test4");
+ var object2 = document.getElementsByTagName("object")[5];
+ assert_equals(object2.id, "test4");
+
+ assert_true("test4" in document, '"test4" in document should be true');
+ var collection = document.test4;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [object1, object2]);
+}, "If there are two objects, a collection should be returned. (id)");
+
+test(function() {
+ var object1 = document.getElementsByTagName("object")[6];
+ assert_equals(object1.name, "test5");
+ var object2 = document.getElementsByTagName("object")[7];
+ assert_equals(object2.id, "test5");
+
+ assert_true("test5" in document, '"test5" in document should be true');
+ var collection = document.test5;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [object1, object2]);
+}, "If there are two objects, a collection should be returned. (name and id)");
+
+test(function() {
+ var object1 = document.getElementsByTagName("object")[8];
+ assert_equals(object1.id, "test6");
+ var object2 = document.getElementsByTagName("object")[9];
+ assert_equals(object2.name, "test6");
+
+ assert_true("test6" in document, '"test6" in document should be true');
+ var collection = document.test6;
+ assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
+ assert_array_equals(collection, [object1, object2]);
+}, "If there are two objects, a collection should be returned. (id and name)");
+
+test(function() {
+ var object = document.getElementsByTagName("object")[10];
+ assert_equals(object.id, "test7");
+
+ assert_true("test7" in document, '"test7" in document should be true');
+ assert_equals(document.test7, object);
+}, "A name shouldn't affect getting an object by id");
+
+test(function() {
+ var object = document.getElementsByTagName("object")[11];
+ assert_equals(object.name, "test8");
+
+ assert_true("test8" in document, '"test8" in document should be true');
+ assert_equals(document.test8, object);
+}, "An id shouldn't affect getting an object by name");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-08.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-08.html
new file mode 100644
index 0000000000..bb024d9e78
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-08.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: duplicate id attributes for object and img</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<div id=test2></div>
+<object id=test2></object>
+
+<div id=test3></div>
+<img id=test3 name=non-empty>
+</div>
+<script>
+test(function() {
+ var object = document.querySelector("object");
+ assert_equals(object.id, "test2");
+
+ assert_true("test2" in document);
+ assert_equals(document.test2, object);
+}, "If there is a div and object with same id, the object should be returned");
+
+test(function() {
+ var img = document.querySelector("img");
+ assert_equals(img.id, "test3");
+
+ assert_true("test3" in document);
+ assert_equals(document.test3, img);
+}, "If there is a div and img with same id, the img should be returned");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-names.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-names.html
new file mode 100644
index 0000000000..3f76d85a1b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-names.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named items: supported property names</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<embed name="exposed_embed">
+ <embed name="not_exposed_embed">
+ </embed>
+</embed>
+<form name="form">
+</form>
+<iframe name="iframe">
+</iframe>
+<img name="img">
+<object name="exposed_object_with_name">
+ <object name="not_exposed_object_with_name">
+ </object>
+</object>
+<object id="exposed_object_with_id">
+ <object id="not_exposed_object_with_id">
+ </object>
+</object>
+<img name="img_with_id" id="img_id">
+<img id="img_with_just_id">
+<template id="template">
+ <img name="img_in_template">
+</template>
+<img name="42">
+<script>
+var names = Object.getOwnPropertyNames(document);
+
+test(function() {
+ assert_true(names.includes("exposed_embed"))
+}, "An embed name appears in a document's property names if the embed is exposed.");
+
+test(function() {
+ assert_false(names.includes("not_exposed_embed"))
+}, "An embed name does not appears in a document's property names if the embed is inside another embed.");
+
+test(function() {
+ assert_true(names.includes("form"))
+}, "A form name appears in a document's property names.");
+
+test(function() {
+ assert_true(names.includes("iframe"))
+}, "An iframe name appears in a document's property names.");
+
+test(function() {
+ assert_true(names.includes("img"))
+}, "An img name appears in a document's property names when the img has no id.");
+
+test(function() {
+ assert_true(names.includes("exposed_object"))
+}, "An object name appears in a document's property names if the object is exposed.");
+
+test(function() {
+ assert_true(names.includes("exposed_object_with_id"))
+}, "An object id appears in a document's property names if the object is exposed.");
+
+test(function() {
+ assert_false(names.includes("not_exposed_object_with_name"))
+}, "An object name does not appear in a document's property names if the object is inside another object.");
+
+test(function() {
+ assert_false(names.includes("not_exposed_object_with_id"))
+}, "An object id does not appear in a document's property names if the object is inside another object.");
+
+test(function() {
+ assert_true(names.includes("img_with_id"))
+}, "An img name appears in a document's property names when the img has an id.");
+
+test(function() {
+ assert_true(names.includes("img_id"))
+}, "An img id appears in a document's property names when the img has a name.");
+
+test(function() {
+ assert_false(names.includes("img_with_just_id"))
+}, "An img id does not appear in a document's property names when the img has no name.");
+
+test(function() {
+ assert_true(names.includes("42"))
+}, "A document's property names can include integer strings.");
+
+test(function() {
+ assert_false(names.includes("template"))
+}, "A template name does not appear in a document's property names.");
+
+test(function() {
+ assert_false(names.includes("img_in_template"))
+}, "An img name does not appear in a document's property names when the img is in a template's document fragment.");
+
+test(function() {
+ var form_index = names.indexOf("form");
+ assert_equals(names.indexOf("iframe"), form_index + 1);
+ assert_equals(names.indexOf("img"), form_index + 2);
+ assert_greater_than(names.indexOf("img_id"), names.indexOf("img"));
+ assert_greater_than(names.indexOf("42"), names.indexOf("img_id"));
+}, "A document's property names appear in tree order.");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-01.html b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-01.html
new file mode 100644
index 0000000000..218a3fe843
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-01.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>document.compatMode: Standards</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-compatmode">
+<body>
+<div id="log"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(document.compatMode, "CSS1Compat");
+})
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-02.html b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-02.html
new file mode 100644
index 0000000000..6da40d61ee
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-02.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<title>document.compatMode: Almost standards</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-compatmode">
+<body>
+<div id="log"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(document.compatMode, "CSS1Compat");
+})
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-03.html b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-03.html
new file mode 100644
index 0000000000..3d55d6e835
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-03.html
@@ -0,0 +1,12 @@
+<title>document.compatMode: Quirks</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-compatmode">
+<body>
+<div id="log"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(document.compatMode, "BackCompat");
+})
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-04.xhtml b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-04.xhtml
new file mode 100644
index 0000000000..a71c1d9dd3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-04.xhtml
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.compatMode: Standards</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-compatmode"/>
+</head>
+<body>
+<div id="log"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(document.compatMode, "CSS1Compat");
+})
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-05.xhtml b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-05.xhtml
new file mode 100644
index 0000000000..3fde06e5af
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-05.xhtml
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.compatMode: Standards</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-compatmode"/>
+</head>
+<body>
+<div id="log"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(document.compatMode, "CSS1Compat");
+})
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-06.xhtml b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-06.xhtml
new file mode 100644
index 0000000000..eb64dfb90e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-compatmode-06.xhtml
@@ -0,0 +1,17 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.compatMode: Standards</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-compatmode"/>
+</head>
+<body>
+<div id="log"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(document.compatMode, "CSS1Compat");
+})
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-cookie.html b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-cookie.html
new file mode 100644
index 0000000000..2af65effeb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-cookie.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>document.cookie</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#resource-metadata-management">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+const TEST_CASES = [
+ {value: "", expected: "", test: "Empty value"},
+ {value: "a=b", expected: "a=b", test: "A simple cookie"},
+ {value: "b=A\0Z", expected: "", test: "A null char"},
+];
+
+test(function(){
+ assert_equals(document.cookie, "");
+}, "document has no cookie");
+
+for (const i in TEST_CASES) {
+ const t = TEST_CASES[i];
+ test(() => {
+ document.cookie = t.value;
+ assert_equals(document.cookie, t.expected);
+
+ // Cleanup
+ if (document.cookie.includes("=")) {
+ document.cookie = document.cookie.split("=")[0] + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
+ assert_equals(document.cookie, "");
+ }
+ }, t.name);
+}
+
+test(function(){
+ var doc = document.implementation.createHTMLDocument("doc");
+ assert_equals(doc.cookie, "");
+ doc.cookie = "test=foobar";
+ assert_equals(doc.cookie, "");
+}, "getting cookie for a cookie-averse document returns empty string, setting does nothing");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified-01.html b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified-01.html
new file mode 100644
index 0000000000..81f67390d1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified-01.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>document.lastModified should return current local time</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/document-lastModified-utils.js"></script>
+<div id="log"></div>
+<script>
+ var last_modified = document.lastModified;
+
+ test(function() {
+ assert_regexp_match(last_modified, DOCUMENT_LASTMODIFIED_REGEX,
+ "Format should match the pattern \"NN/NN/NNNN NN:NN:NN\".");
+ }, "Date returned by lastModified is in the form \"MM/DD/YYYY hh:mm:ss\".");
+
+ test(function() {
+ assert_document_lastmodified_string_approximately_now(last_modified);
+ }, "Date returned by lastModified is current at page load");
+
+ var t = async_test("Date returned by lastModified is current after timeout.");
+ t.step_timeout(function() {
+ assert_document_lastmodified_string_approximately_now(document.lastModified);
+ t.done();
+ }, 4000);
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html
new file mode 100644
index 0000000000..9e0a07d8eb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>document.lastModified</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#resource-metadata-management">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ test(function(){
+ var date = new Date("Thu, 01 Jan 1970 01:23:45 GMT");
+ var result = ('0' + (date.getMonth()+1)).slice(-2) + '/' + ('0' + date.getDate()).slice(-2) + '/' + date.getFullYear() + " " + [date.getHours(),date.getMinutes(),date.getSeconds()].map(function(n){return ("0" + n).slice(-2);}).join(":");
+ assert_equals(document.lastModified, result);
+ }, "lastModified should return the last modified date and time");
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html.headers b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html.headers
new file mode 100644
index 0000000000..377e3b52dc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-lastModified.html.headers
@@ -0,0 +1 @@
+Last-Modified: Thu, 01 Jan 1970 01:23:45 GMT
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-readyState.html b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-readyState.html
new file mode 100644
index 0000000000..8c91e0a001
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/document-readyState.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>document.readyState</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#resource-metadata-management">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var t1 = async_test("readyState equals 'complete' when the document has loaded"),
+ t2 = async_test("readyState equals 'interactive' when the document is finished parsing"),
+ t3 = async_test("readystatechange event is fired each time document.readyState changes");
+
+ window.onload = t1.step_func_done(function(){
+ assert_equals(document.readyState, "complete");
+ });
+
+ document.addEventListener("DOMContentLoaded", function(event) {
+ t2.step(function() {
+ assert_equals(document.readyState, "interactive")
+ });
+ t2.done();
+ });
+
+ var states = [document.readyState];
+ document.onreadystatechange = t3.step_func(function(){
+ states.push(document.readyState);
+ if (document.readyState === "complete") {
+ assert_array_equals(states, ["loading", "interactive", "complete"]);
+ t3.done();
+ }
+ })
+</script>
diff --git a/testing/web-platform/tests/html/dom/documents/resource-metadata-management/support/document-lastModified-utils.js b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/support/document-lastModified-utils.js
new file mode 100644
index 0000000000..bbcde1894a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/resource-metadata-management/support/document-lastModified-utils.js
@@ -0,0 +1,78 @@
+const DOCUMENT_LASTMODIFIED_REGEX = /^([0-9]{2})\/([0-9]{2})\/([0-9]{4}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/;
+
+function assert_document_lastmodified_string_approximately_now(str) {
+ // We want to test that |str| was a time in the user's local
+ // timezone generated within a few seconds prior to the present.
+ // This requires some care, since it is possible that:
+ // - the few second difference may have crossed a
+ // year/month/day/hour/minute boundary
+ // - the few second difference may have crossed a change in the
+ // local timezone's UTC offset
+ // - the local time might be one that has multiple valid UTC
+ // representations (for example, because it's in the hour
+ // following a shift from summer time to winter time)
+ // We will make some assumptions to do this:
+ // - local time's UTC offset doesn't change more than once per
+ // minute
+ // - local time's UTC offset only changes by integral numbers of
+ // minutes
+
+ // The date must be equal to or earlier than the present time.
+ var dmax = new Date();
+
+ // The date must be equal to or later than 2.5 seconds ago.
+ var TOLERANCE_MILLISECONDS = 2500;
+ var dmin = new Date();
+ dmin.setTime(dmax.getTime() - TOLERANCE_MILLISECONDS);
+
+ // Extract the year/month/date/hours/minutes/seconds from str. It
+ // is important that we do *not* try to construct a Date object from
+ // these, since the core of the date object is a timestamp in UTC,
+ // and there are cases (such as the hour on each side of a change
+ // from summer time to winter time) where there are multiple
+ // possible UTC timestamps for a given YYYY-MM-DD HH:MM:SS, and
+ // constructing a Date object would pick one of them, which might be
+ // the wrong one. However, we already have the right one in dmin
+ // and dmax, so we should instead extract local time from those
+ // rather than converting these values to UTC.
+ var m = DOCUMENT_LASTMODIFIED_REGEX.exec(str);
+ var syear = Number(m[3]);
+ var smonth = Number(m[1]) - 1; // match Javascript 0-based months
+ var sdate = Number(m[2]);
+ var shours = Number(m[4]);
+ var sminutes = Number(m[5]);
+ var sseconds = Number(m[6]);
+
+ if (dmin.getFullYear() == dmax.getFullYear() &&
+ dmin.getMonth() == dmax.getMonth() &&
+ dmin.getDate() == dmax.getDate() &&
+ dmin.getHours() == dmax.getHours() &&
+ dmin.getMinutes() == dmax.getMinutes()) {
+ // min and max have the same minute
+ assert_equals(smonth, dmin.getMonth(), "month");
+ assert_equals(sdate, dmin.getDate(), "date");
+ assert_equals(syear, dmin.getFullYear(), "year");
+ assert_equals(shours, dmin.getHours(), "hours");
+ assert_equals(sminutes, dmin.getMinutes(), "minutes");
+ assert_true(dmin.getSeconds() <= sseconds &&
+ sseconds <= dmax.getSeconds(), "seconds");
+ } else if (dmin.getFullYear() == syear &&
+ dmin.getMonth() == smonth &&
+ dmin.getDate() == sdate &&
+ dmin.getHours() == shours &&
+ dmin.getMinutes() == sminutes) {
+ // actual value has the same minute as min
+ assert_true(dmin.getSeconds() <= sseconds, "dmin.getSeconds() <= sseconds");
+ assert_true(57 <= dmin.getSeconds(), "unexpected local time rules (dmin match)");
+ } else if (dmax.getFullYear() == syear &&
+ dmax.getMonth() == smonth &&
+ dmax.getDate() == sdate &&
+ dmax.getHours() == shours &&
+ dmax.getMinutes() == sminutes) {
+ // actual value has the same minute as max
+ assert_true(sseconds <= dmax.getSeconds(), "sseconds <= dmax.getSeconds()");
+ assert_true(dmax.getSeconds() <= 2, "unexpected local time rules (dmax match)");
+ } else {
+ assert_unreached("unexpected local time rules (no match)");
+ }
+}
diff --git a/testing/web-platform/tests/html/dom/elements-embedded.js b/testing/web-platform/tests/html/dom/elements-embedded.js
new file mode 100644
index 0000000000..c5b4520cc6
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-embedded.js
@@ -0,0 +1,156 @@
+var embeddedElements = {
+ picture: {},
+ img: {
+ // Conforming
+ alt: "string",
+ src: "url",
+ srcset: "string",
+ crossOrigin: {type: "enum", keywords: ["anonymous", "use-credentials"], nonCanon:{"": "anonymous"}, isNullable: true, defaultVal: null, invalidVal: "anonymous"},
+ useMap: "string",
+ isMap: "boolean",
+ width: {type: "unsigned long", customGetter: true},
+ height: {type: "unsigned long", customGetter: true},
+ referrerPolicy: {type: "enum", keywords: ["", "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url"]},
+ decoding: {type: "enum", keywords: ["async", "sync", "auto"], defaultVal: "auto", invalidVal: "auto"},
+
+ // Obsolete
+ name: "string",
+ lowsrc: {type: "url"},
+ align: "string",
+ hspace: "unsigned long",
+ vspace: "unsigned long",
+ longDesc: "url",
+ border: {type: "string", treatNullAsEmptyString: true},
+ },
+ iframe: {
+ // Conforming
+ src: "url",
+ srcdoc: "string",
+ name: "string",
+ sandbox: "settable tokenlist",
+ allowFullscreen: "boolean",
+ allowUserMedia: "boolean",
+ width: "string",
+ height: "string",
+ referrerPolicy: {type: "enum", keywords: ["", "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url"]},
+ delegateStickyUserActivation: {type: "enum", keywords: ["vibration", "media"], defaultVal: null},
+
+ // Obsolete
+ align: "string",
+ scrolling: "string",
+ frameBorder: "string",
+ longDesc: "url",
+ marginHeight: {type: "string", treatNullAsEmptyString: true},
+ marginWidth: {type: "string", treatNullAsEmptyString: true}
+ },
+ embed: {
+ // Conforming
+ src: "url",
+ type: "string",
+ width: "string",
+ height: "string",
+
+ // Obsolete
+ align: "string",
+ name: "string"
+ },
+ object: {
+ // Conforming
+ data: "url",
+ type: "string",
+ name: "string",
+ useMap: "string",
+ width: "string",
+ height: "string",
+
+ // Obsolete
+ align: "string",
+ archive: "string",
+ code: "string",
+ declare: "boolean",
+ hspace: "unsigned long",
+ standby: "string",
+ vspace: "unsigned long",
+ codeBase: "url",
+ codeType: "string",
+ border: {type: "string", treatNullAsEmptyString: true}
+ },
+ param: {
+ // Conforming
+ name: "string",
+ value: "string",
+
+ // Obsolete
+ type: "string",
+ valueType: "string"
+ },
+ video: {
+ // HTMLMediaElement
+ src: "url",
+ crossOrigin: {type: "enum", keywords: ["anonymous", "use-credentials"], nonCanon:{"": "anonymous"}, isNullable: true, defaultVal: null, invalidVal: "anonymous"},
+ // As with "keytype", we have no missing value default defined here.
+ preload: {type: "enum", keywords: ["none", "metadata", "auto"], nonCanon: {"": "auto"}, defaultVal: null},
+ autoplay: "boolean",
+ loop: "boolean",
+ controls: "boolean",
+ controlsList: {type: "tokenlist", domAttrName: "controlsList"},
+ defaultMuted: {type: "boolean", domAttrName: "muted"},
+
+ width: "unsigned long",
+ height: "unsigned long",
+ poster: "url",
+ playsInline: "boolean",
+ },
+ audio: {
+ // HTMLMediaElement
+ src: "url",
+ crossOrigin: {type: "enum", keywords: ["anonymous", "use-credentials"], nonCanon:{"": "anonymous"}, isNullable: true, defaultVal: null, invalidVal: "anonymous"},
+ // As with "keytype", we have no missing value default defined here.
+ preload: {type: "enum", keywords: ["none", "metadata", "auto"], nonCanon: {"": "auto"}, defaultVal: null},
+ autoplay: "boolean",
+ loop: "boolean",
+ controls: "boolean",
+ defaultMuted: {type: "boolean", domAttrName: "muted"}
+ },
+ source: {
+ src: "url",
+ type: "string",
+ srcset: "string",
+ sizes: "string",
+ media: "string"
+ },
+ track: {
+ kind: {type: "enum", keywords: ["subtitles", "captions", "descriptions", "chapters", "metadata"], defaultVal: "subtitles", invalidVal: "metadata"},
+ src: "url",
+ srclang: "string",
+ label: "string",
+ "default": "boolean"
+ },
+ canvas: {
+ width: {type: "unsigned long", defaultVal: 300},
+ height: {type: "unsigned long", defaultVal: 150}
+ },
+ map: {
+ name: "string"
+ },
+ area: {
+ // Conforming
+ alt: "string",
+ coords: "string",
+ shape: "string",
+ target: "string",
+ download: "string",
+ ping: "string",
+ rel: "string",
+ relList: {type: "tokenlist", domAttrName: "rel"},
+ referrerPolicy: {type: "enum", keywords: ["", "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url"]},
+
+ // HTMLHyperlinkElementUtils
+ href: "url",
+
+ // Obsolete
+ noHref: "boolean"
+ },
+};
+
+mergeElements(embeddedElements);
diff --git a/testing/web-platform/tests/html/dom/elements-forms-weekmonth.js b/testing/web-platform/tests/html/dom/elements-forms-weekmonth.js
new file mode 100644
index 0000000000..b13a21d6d8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-forms-weekmonth.js
@@ -0,0 +1,42 @@
+var formElements = {
+ input: {
+ // Conforming
+ accept: "string",
+ alt: "string",
+ autocomplete: {type: "string", customGetter: true},
+ defaultChecked: {type: "boolean", domAttrName: "checked"},
+ dirName: "string",
+ disabled: "boolean",
+ // "formAction" has magic hard-coded in reflection.js
+ formAction: "url",
+ formEnctype: {type: "enum", keywords: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], invalidVal: "application/x-www-form-urlencoded"},
+ formMethod: {type: "enum", keywords: ["get", "post"], invalidVal: "get"},
+ formNoValidate: "boolean",
+ formTarget: "string",
+ height: {type: "unsigned long", customGetter: true},
+ max: "string",
+ maxLength: "limited long",
+ min: "string",
+ minLength: "limited long",
+ multiple: "boolean",
+ name: "string",
+ pattern: "string",
+ placeholder: "string",
+ readOnly: "boolean",
+ required: "boolean",
+ // https://html.spec.whatwg.org/#attr-input-size
+ size: {type: "limited unsigned long", defaultVal: 20},
+ src: "url",
+ step: "string",
+ type: {type: "enum", keywords: ["month", "week"],
+ defaultVal: "text"},
+ width: {type: "unsigned long", customGetter: true},
+ defaultValue: {type: "string", domAttrName: "value"},
+
+ // Obsolete
+ align: "string",
+ useMap: "string",
+ },
+};
+
+mergeElements(formElements);
diff --git a/testing/web-platform/tests/html/dom/elements-forms.js b/testing/web-platform/tests/html/dom/elements-forms.js
new file mode 100644
index 0000000000..c43bab9dce
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-forms.js
@@ -0,0 +1,128 @@
+var formElements = {
+ form: {
+ acceptCharset: {type: "string", domAttrName: "accept-charset"},
+ // "action" has magic hard-coded in reflection.js
+ action: "url",
+ autocomplete: {type: "enum", keywords: ["on", "off"], defaultVal: "on"},
+ enctype: {type: "enum", keywords: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], defaultVal: "application/x-www-form-urlencoded"},
+ encoding: {type: "enum", keywords: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], defaultVal: "application/x-www-form-urlencoded", domAttrName: "enctype"},
+ method: {type: "enum", keywords: ["get", "post", "dialog"], defaultVal: "get"},
+ name: "string",
+ noValidate: "boolean",
+ target: "string",
+ },
+ fieldset: {
+ disabled: "boolean",
+ name: "string",
+ },
+ legend: {
+ // Obsolete
+ align: "string",
+ },
+ label: {
+ htmlFor: {type: "string", domAttrName: "for"},
+ },
+ input: {
+ // Conforming
+ accept: "string",
+ alt: "string",
+ autocomplete: {type: "string", customGetter: true},
+ defaultChecked: {type: "boolean", domAttrName: "checked"},
+ dirName: "string",
+ disabled: "boolean",
+ // "formAction" has magic hard-coded in reflection.js
+ formAction: "url",
+ formEnctype: {type: "enum", keywords: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], invalidVal: "application/x-www-form-urlencoded"},
+ formMethod: {type: "enum", keywords: ["get", "post"], invalidVal: "get"},
+ formNoValidate: "boolean",
+ formTarget: "string",
+ height: {type: "unsigned long", customGetter: true},
+ max: "string",
+ maxLength: "limited long",
+ min: "string",
+ minLength: "limited long",
+ multiple: "boolean",
+ name: "string",
+ pattern: "string",
+ placeholder: "string",
+ readOnly: "boolean",
+ required: "boolean",
+ // https://html.spec.whatwg.org/#attr-input-size
+ size: {type: "limited unsigned long", defaultVal: 20},
+ src: "url",
+ step: "string",
+ type: {type: "enum", keywords: ["hidden", "text", "search", "tel",
+ "url", "email", "password", "date",
+ "time", "datetime-local", "number", "range", "color", "checkbox",
+ "radio", "file", "submit", "image", "reset", "button"], defaultVal:
+ "text"},
+ width: {type: "unsigned long", customGetter: true},
+ defaultValue: {type: "string", domAttrName: "value"},
+
+ // Obsolete
+ align: "string",
+ useMap: "string",
+ },
+ button: {
+ disabled: "boolean",
+ // "formAction" has magic hard-coded in reflection.js
+ formAction: "url",
+ formEnctype: {type: "enum", keywords: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], invalidVal: "application/x-www-form-urlencoded"},
+ formMethod: {type: "enum", keywords: ["get", "post", "dialog"], invalidVal: "get"},
+ formNoValidate: "boolean",
+ formTarget: "string",
+ name: "string",
+ type: {type: "enum", keywords: ["submit", "reset", "button"], defaultVal: "submit"},
+ value: "string"
+ },
+ select: {
+ autocomplete: {type: "string", customGetter: true},
+ disabled: "boolean",
+ multiple: "boolean",
+ name: "string",
+ required: "boolean",
+ size: {type: "unsigned long", defaultVal: 0},
+ },
+ datalist: {},
+ optgroup: {
+ disabled: "boolean",
+ label: "string",
+ },
+ option: {
+ disabled: "boolean",
+ label: {type: "string", customGetter: true},
+ defaultSelected: {type: "boolean", domAttrName: "selected"},
+ value: {type: "string", customGetter: true},
+ },
+ textarea: {
+ autocomplete: {type: "string", customGetter: true},
+ cols: {type: "limited unsigned long with fallback", defaultVal: 20},
+ dirName: "string",
+ disabled: "boolean",
+ maxLength: "limited long",
+ minLength: "limited long",
+ name: "string",
+ placeholder: "string",
+ readOnly: "boolean",
+ required: "boolean",
+ rows: {type: "limited unsigned long with fallback", defaultVal: 2},
+ wrap: "string",
+ },
+ output: {
+ htmlFor: {type: "settable tokenlist", domAttrName: "for" },
+ name: "string",
+ },
+ progress: {
+ max: {type: "limited double", defaultVal: 1.0},
+ },
+ meter: {
+ value: {type: "double", customGetter: true},
+ min: {type: "double", customGetter: true},
+ max: {type: "double", customGetter: true},
+ low: {type: "double", customGetter: true},
+ high: {type: "double", customGetter: true},
+ optimum: {type: "double", customGetter: true},
+ },
+};
+
+mergeElements(formElements);
diff --git a/testing/web-platform/tests/html/dom/elements-grouping.js b/testing/web-platform/tests/html/dom/elements-grouping.js
new file mode 100644
index 0000000000..4c9a29131a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-grouping.js
@@ -0,0 +1,57 @@
+var groupingElements = {
+ p: {
+ // Obsolete
+ align: "string",
+ },
+ hr: {
+ // Obsolete
+ align: "string",
+ color: "string",
+ noShade: "boolean",
+ size: "string",
+ width: "string",
+ },
+ pre: {
+ // Obsolete
+ width: "long",
+ },
+ blockquote: {
+ cite: "url",
+ },
+ ol: {
+ // Conforming
+ reversed: "boolean",
+ start: {type: "long", defaultVal: 1},
+ type: "string",
+
+ // Obsolete
+ compact: "boolean",
+ },
+ ul: {
+ // Obsolete
+ compact: "boolean",
+ type: "string",
+ },
+ li: {
+ // Conforming
+ value: "long",
+
+ // Obsolete
+ type: "string",
+ },
+ dl: {
+ // Obsolete
+ compact: "boolean",
+ },
+ dt: {},
+ dd: {},
+ figure: {},
+ figcaption: {},
+ main: {},
+ div: {
+ // Obsolete
+ align: "string",
+ },
+};
+
+mergeElements(groupingElements);
diff --git a/testing/web-platform/tests/html/dom/elements-metadata.js b/testing/web-platform/tests/html/dom/elements-metadata.js
new file mode 100644
index 0000000000..49d7bb25ad
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-metadata.js
@@ -0,0 +1,50 @@
+var metadataElements = {
+ head: {},
+ title: {},
+ base: {
+ href: {type: "url", customGetter: true},
+ target: "string",
+ },
+ link: {
+ // Conforming
+ href: "url",
+ crossOrigin: {type: "enum", keywords: ["anonymous", "use-credentials"], nonCanon:{"": "anonymous"}, isNullable: true, defaultVal: null, invalidVal: "anonymous"},
+ rel: "string",
+ as: {
+ type: "enum",
+ keywords: ["fetch", "audio", "document", "embed", "font", "image", "manifest", "object", "report", "script", "sharedworker", "style", "track", "video", "worker", "xslt"],
+ defaultVal: "",
+ invalidVal: ""
+ },
+ relList: {type: "tokenlist", domAttrName: "rel"},
+ media: "string",
+ nonce: "string",
+ integrity: "string",
+ hreflang: "string",
+ type: "string",
+ sizes: "settable tokenlist",
+ referrerPolicy: {type: "enum", keywords: ["", "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url"]},
+
+ // Obsolete
+ charset: "string",
+ rev: "string",
+ target: "string",
+ },
+ meta: {
+ // Conforming
+ name: "string",
+ httpEquiv: {type: "string", domAttrName: "http-equiv"},
+ content: "string",
+ media: "string",
+
+ // Obsolete
+ scheme: "string",
+ },
+ style: {
+ media: "string",
+ nonce: "string",
+ type: "string",
+ },
+};
+
+mergeElements(metadataElements);
diff --git a/testing/web-platform/tests/html/dom/elements-misc.js b/testing/web-platform/tests/html/dom/elements-misc.js
new file mode 100644
index 0000000000..1a74c54797
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-misc.js
@@ -0,0 +1,60 @@
+var miscElements = {
+ // "The root element" section
+ html: {
+ // Obsolete
+ version: "string",
+ },
+
+ // "Scripting" section
+ script: {
+ src: "url",
+ type: "string",
+ noModule: "boolean",
+ charset: "string",
+ // TODO: async attribute (complicated).
+ defer: "boolean",
+ crossOrigin: {type: "enum", keywords: ["anonymous", "use-credentials"], nonCanon:{"": "anonymous"}, isNullable: true, defaultVal: null, invalidVal: "anonymous"},
+ integrity: "string",
+
+ // Obsolete
+ event: "string",
+ htmlFor: {type: "string", domAttrName: "for"},
+ },
+ noscript: {},
+
+ template: {},
+ slot: {
+ name: "string",
+ },
+
+ // "Edits" section
+ ins: {
+ cite: "url",
+ dateTime: "string",
+ },
+ del: {
+ cite: "url",
+ dateTime: "string",
+ },
+
+ // "Interactive elements" section
+ details: {
+ open: "boolean",
+ },
+ summary: {},
+ menu: {
+ // Obsolete
+ compact: "boolean",
+ },
+ dialog: {
+ open: "boolean",
+ },
+
+ // Global attributes should exist even on unknown elements
+ undefinedelement: {
+ enterKeyHint: {type: "enum", keywords: ["enter", "done", "go", "next", "previous", "search", "send"]},
+ inputMode: {type: "enum", keywords: ["none", "text", "tel", "url", "email", "numeric", "decimal", "search"]},
+ },
+};
+
+mergeElements(miscElements);
diff --git a/testing/web-platform/tests/html/dom/elements-obsolete.js b/testing/web-platform/tests/html/dom/elements-obsolete.js
new file mode 100644
index 0000000000..3ef9e9f997
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-obsolete.js
@@ -0,0 +1,50 @@
+var obsoleteElements = {
+ marquee: {
+ behavior: {
+ type: {
+ type: "enum",
+ keywords: ["scroll", "slide", "alternate"],
+ defaultVal: "scroll"
+ },
+ },
+ bgColor: "string",
+ direction: {
+ type: {
+ type: "enum",
+ keywords: ["up", "right", "down", "left"],
+ defaultVal: "left"
+ },
+ },
+ height: "string",
+ hspace: "unsigned long",
+ scrollAmount: {type: "unsigned long", defaultVal: 6},
+ scrollDelay: {type: "unsigned long", defaultVal: 85},
+ trueSpeed: "boolean",
+ vspace: "unsigned long",
+ width: "string",
+ },
+ frameset: {
+ cols: "string",
+ rows: "string",
+ },
+ frame: {
+ name: "string",
+ scrolling: "string",
+ src: "url",
+ frameBorder: "string",
+ longDesc: "url",
+ noResize: "boolean",
+ marginHeight: {type: "string", treatNullAsEmptyString: true},
+ marginWidth: {type: "string", treatNullAsEmptyString: true},
+ },
+ dir: {
+ compact: "boolean",
+ },
+ font: {
+ color: {type: "string", treatNullAsEmptyString: true},
+ face: "string",
+ size: "string",
+ },
+};
+
+mergeElements(obsoleteElements);
diff --git a/testing/web-platform/tests/html/dom/elements-sections.js b/testing/web-platform/tests/html/dom/elements-sections.js
new file mode 100644
index 0000000000..bbad85e513
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-sections.js
@@ -0,0 +1,64 @@
+var sectionElements = {
+ body: {
+ // Obsolete
+ text: {type: "string", treatNullAsEmptyString: true},
+ link: {type: "string", treatNullAsEmptyString: true},
+ vLink: {type: "string", treatNullAsEmptyString: true},
+ aLink: {type: "string", treatNullAsEmptyString: true},
+ bgColor: {type: "string", treatNullAsEmptyString: true},
+ background: "string",
+ },
+ article: {},
+ section: {},
+ nav: {},
+ aside: {},
+ h1: {
+ // Obsolete
+ align: "string",
+ },
+ h2: {
+ // Obsolete
+ align: "string",
+ },
+ h3: {
+ // Obsolete
+ align: "string",
+ },
+ h4: {
+ // Obsolete
+ align: "string",
+ },
+ h5: {
+ // Obsolete
+ align: "string",
+ },
+ h6: {
+ // Obsolete
+ align: "string",
+ },
+ hgroup: {},
+ header: {},
+ footer: {},
+ address: {},
+};
+
+mergeElements(sectionElements);
+
+extraTests.push(function() {
+ ReflectionTests.reflects({type: "enum", keywords: ["ltr", "rtl", "auto"]}, "dir", document, "dir", document.documentElement);
+ // TODO: these behave differently if the body element is a frameset. Also
+ // should probably test with multiple bodies.
+ ReflectionTests.reflects({type: "string", treatNullAsEmptyString: true}, "fgColor", document, "text", document.body);
+ ReflectionTests.reflects({type: "string", treatNullAsEmptyString: true}, "linkColor", document, "link", document.body);
+ ReflectionTests.reflects({type: "string", treatNullAsEmptyString: true}, "vlinkColor", document, "vlink", document.body);
+ ReflectionTests.reflects({type: "string", treatNullAsEmptyString: true}, "alinkColor", document, "alink", document.body);
+ ReflectionTests.reflects({type: "string", treatNullAsEmptyString: true}, "bgColor", document, "bgcolor", document.body);
+ // Edge remains RTL if we don't do this, despite removing the attribute
+ document.dir = "ltr";
+ // Don't mess up the colors :)
+ document.documentElement.removeAttribute("dir");
+ var attrs = ["text", "bgcolor", "link", "alink", "vlink"];
+ for (var i = 0; i < attrs.length; i++) {
+ document.body.removeAttribute(attrs[i]);
+ }
+});
diff --git a/testing/web-platform/tests/html/dom/elements-tabular.js b/testing/web-platform/tests/html/dom/elements-tabular.js
new file mode 100644
index 0000000000..88fc8d31ec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-tabular.js
@@ -0,0 +1,109 @@
+// Up-to-date as of 2013-04-12.
+var tabularElements = {
+ table: {
+ // Obsolete
+ align: "string",
+ border: "string",
+ frame: "string",
+ rules: "string",
+ summary: "string",
+ width: "string",
+ bgColor: {type: "string", treatNullAsEmptyString: true},
+ cellPadding: {type: "string", treatNullAsEmptyString: true},
+ cellSpacing: {type: "string", treatNullAsEmptyString: true},
+ },
+ caption: {
+ // Obsolete
+ align: "string",
+ },
+ colgroup: {
+ span: {type: "clamped unsigned long", defaultVal: 1, min: 1, max: 1000},
+
+ // Obsolete
+ align: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ vAlign: "string",
+ width: "string",
+ },
+ col: {
+ // Conforming
+ span: {type: "clamped unsigned long", defaultVal: 1, min: 1, max: 1000},
+
+ // Obsolete
+ align: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ vAlign: "string",
+ width: "string",
+ },
+ tbody: {
+ // Obsolete
+ align: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ vAlign: "string",
+ },
+ thead: {
+ // Obsolete
+ align: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ vAlign: "string",
+ },
+ tfoot: {
+ // Obsolete
+ align: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ vAlign: "string",
+ },
+ tr: {
+ // Obsolete
+ align: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ vAlign: "string",
+ bgColor: {type: "string", treatNullAsEmptyString: true},
+ },
+ td: {
+ // HTMLTableCellElement (Conforming)
+ colSpan: {type: "clamped unsigned long", defaultVal: 1, min: 1, max: 1000},
+ rowSpan: {type: "clamped unsigned long", defaultVal: 1, min: 0, max: 65534},
+ headers: "string",
+ scope: {type: "enum", keywords: ["row", "col", "rowgroup", "colgroup"]},
+ abbr: "string",
+
+ // HTMLTableCellElement (Obsolete)
+ align: "string",
+ axis: "string",
+ height: "string",
+ width: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ noWrap: "boolean",
+ vAlign: "string",
+ bgColor: {type: "string", treatNullAsEmptyString: true},
+ },
+ th: {
+ // HTMLTableCellElement (Conforming)
+ colSpan: {type: "clamped unsigned long", defaultVal: 1, min: 1, max: 1000},
+ rowSpan: {type: "clamped unsigned long", defaultVal: 1, min: 0, max: 65534},
+ headers: "string",
+ scope: {type: "enum", keywords: ["row", "col", "rowgroup", "colgroup"]},
+ abbr: "string",
+
+ // HTMLTableCellElement (Obsolete)
+ align: "string",
+ axis: "string",
+ height: "string",
+ width: "string",
+ ch: {type: "string", domAttrName: "char"},
+ chOff: {type: "string", domAttrName: "charoff"},
+ noWrap: "boolean",
+ vAlign: "string",
+ bgColor: {type: "string", treatNullAsEmptyString: true},
+ },
+};
+
+mergeElements(tabularElements);
diff --git a/testing/web-platform/tests/html/dom/elements-text.js b/testing/web-platform/tests/html/dom/elements-text.js
new file mode 100644
index 0000000000..f71df48ee3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements-text.js
@@ -0,0 +1,63 @@
+// Up-to-date as of 2013-04-19.
+var textElements = {
+ a: {
+ // Conforming
+ target: "string",
+ download: "string",
+ ping: "string",
+ rel: "string",
+ relList: {type: "tokenlist", domAttrName: "rel"},
+ hreflang: "string",
+ type: "string",
+ referrerPolicy: {type: "enum", keywords: ["", "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url"]},
+
+ // HTMLHyperlinkElementUtils
+ href: "url",
+
+ // Obsolete
+ coords: "string",
+ charset: "string",
+ name: "string",
+ rev: "string",
+ shape: "string",
+ },
+ em: {},
+ strong: {},
+ small: {},
+ s: {},
+ cite: {},
+ q: {
+ cite: "url",
+ },
+ dfn: {},
+ abbr: {},
+ ruby: {},
+ rt: {},
+ rp: {},
+ data: {
+ value: "string",
+ },
+ time: {
+ dateTime: "string",
+ },
+ code: {},
+ var: {},
+ samp: {},
+ kbd: {},
+ sub: {},
+ sup: {},
+ i: {},
+ b: {},
+ u: {},
+ mark: {},
+ bdi: {},
+ bdo: {},
+ span: {},
+ br: {
+ // Obsolete
+ clear: "string",
+ },
+ wbr: {},
+};
+
+mergeElements(textElements);
diff --git a/testing/web-platform/tests/html/dom/elements/elements-in-the-dom/historical.html b/testing/web-platform/tests/html/dom/elements/elements-in-the-dom/historical.html
new file mode 100644
index 0000000000..078ce29cc0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/elements-in-the-dom/historical.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Historical HTMLElement features</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<script>
+[
+ // https://github.com/whatwg/html/commit/389ec2620d89e9480ef8847bf016abdfa92427bc
+ "commandType",
+ "commandLabel",
+ "commandIcon",
+ "commandHidden",
+ "commandDisabled",
+ "commandChecked",
+ "commandTriggers",
+ // https://github.com/whatwg/html/pull/2402
+ "dropzone",
+].forEach(function(member) {
+ test(function() {
+ assert_false(member in document.body);
+ assert_false(member in document.createElement('div'));
+ }, 'HTMLElement member must be nuked: ' + member);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/elements-in-the-dom/unknown-element.html b/testing/web-platform/tests/html/dom/elements/elements-in-the-dom/unknown-element.html
new file mode 100644
index 0000000000..16a53e5e88
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/elements-in-the-dom/unknown-element.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>HTMLUnknownElement</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#htmlunknownelement">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+[
+ "applet",
+ "attachment",
+ "layer",
+ "nolayer",
+ "xxx"
+].forEach(name => {
+ test(() => {
+ const elt = document.createElement("xxx");
+ assert_true(window.HTMLUnknownElement && elt instanceof HTMLUnknownElement, "not an instance of HTMLUnknownElement");
+ assert_true(window.HTMLSpanElement && !(elt instanceof HTMLSpanElement), "an instance of HTMLSpanElement");
+ }, `<${name}> is an HTMLUnknownElement`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/.htaccess b/testing/web-platform/tests/html/dom/elements/global-attributes/.htaccess
new file mode 100644
index 0000000000..94e9a4f190
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/.htaccess
@@ -0,0 +1,16 @@
+AddType 'text/html; charset=UTF-8' html
+<Files 'the-lang-attribute-003.html'>
+AddLanguage 'ko' .html
+</Files>
+<Files 'the-lang-attribute-005.html'>
+AddLanguage 'zh' .html
+</Files>
+<Files 'the-lang-attribute-006.html'>
+AddLanguage 'zh' .html
+</Files>
+<Files 'the-lang-attribute-009.html'>
+AddLanguage 'ko' .html
+</Files>
+<Files 'the-lang-attribute-011.html'>
+AddLanguage 'ko,zh,ja' .html
+</Files>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/classlist-nonstring.html b/testing/web-platform/tests/html/dom/elements/global-attributes/classlist-nonstring.html
new file mode 100644
index 0000000000..044f5e8b1b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/classlist-nonstring.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>classList: non-string contains</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#classes">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#domtokenlist">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-DOMString">
+<link rel="help" href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf#page=57">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<ul>
+<li class=undefined>
+<li class=null>
+<li class=0>
+<li class=NaN>
+<li class=Infinity>
+<li class=-Infinity>
+</ul>
+<script>
+var items = document.getElementById("test")
+ .getElementsByTagName("li");
+var tests = [undefined, null, -0, +0, NaN, +Infinity, -Infinity];
+var results = [
+ [true, false, false, false, false, false, false], // "undefined"
+ [false, true, false, false, false, false, false], // "null"
+ [false, false, true, true, false, false, false], // "0"
+ [false, false, false, false, true, false, false], // "NaN"
+ [false, false, false, false, false, true, false], // "Infinity"
+ [false, false, false, false, false, false, true ] // "-Infinity"
+];
+</script>
+</div>
+<script>
+test(function() {
+ for (var i = 0, il = items.length; i < il; ++i) {
+ test(function() {
+ for (var j = 0, jl = tests.length; j < jl; ++j) {
+ assert_equals(items[i].classList.contains(tests[j]), results[i][j]);
+ }
+ })
+ }
+})
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/custom-attrs.html b/testing/web-platform/tests/html/dom/elements/global-attributes/custom-attrs.html
new file mode 100644
index 0000000000..a1e41dac25
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/custom-attrs.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Element Custom Attributes</title>
+ <link rel="author" title="Bruno de Oliveira Abinader" href="mailto:bruno.d@partner.samsung.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-dataset">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#xml">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/dom/nodes/attributes.js"></script>
+ </head>
+ <body>
+ <h1>Element Custom Attributes</h1>
+ <div id="log"></div>
+ <script>
+ test(function() {
+ var div = document.createElement("div");
+ div.setAttributeNS("foo", "data-my-custom-attr", "first");
+ div.setAttributeNS("bar", "data-my-custom-attr", "second");
+ div.dataset.myCustomAttr = "third";
+
+ assert_equals(div.attributes.length, 3);
+ attributes_are(div, [["data-my-custom-attr", "first", "foo"],
+ ["data-my-custom-attr", "second", "bar"],
+ ["data-my-custom-attr", "third", null]]);
+ }, "Setting an Element's dataset property should not interfere with namespaced attributes with same name");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/data_unicode_attr.html b/testing/web-platform/tests/html/dom/elements/global-attributes/data_unicode_attr.html
new file mode 100644
index 0000000000..17077dafd1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/data_unicode_attr.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>HTML Test: dataset attribute</title>
+<link rel="author" title="ElegantPig" href="mailto:neil.ep@hotmail.com">
+<link rel="author" title="Xiaojun Wu" href="mailto:xiaojunx.a.wu@intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id='log'></div>
+<div id="d1" data-weapons="laser 2" data-中文属性="中文"></div>
+<script>
+
+test(function() {
+ var d1 = document.getElementById("d1");
+ assert_equals(d1.dataset.weapons, "laser 2");
+}, "dataset - SBCS");
+
+test(function() {
+ var d1 = document.getElementById("d1");
+ assert_equals(d1.dataset.中文属性, "中文");
+}, "dataset - UNICODE");
+
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-binding.window.js b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-binding.window.js
new file mode 100644
index 0000000000..e0e85677d1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-binding.window.js
@@ -0,0 +1,45 @@
+[9, "x"].forEach(function(key) {
+ test(function() {
+ var element = document.createElement("div");
+ var dataset = element.dataset;
+
+ var value = "value for " + this.name;
+
+ assert_equals(dataset[key], undefined);
+
+ element.setAttribute("data-" + key, value);
+ assert_equals(element.getAttribute("data-" + key), value);
+ assert_equals(dataset[key], value);
+
+ var propdesc = Object.getOwnPropertyDescriptor(dataset, key);
+ assert_not_equals(propdesc, undefined);
+ assert_equals(propdesc.value, value);
+ assert_true(propdesc.writable);
+ assert_true(propdesc.enumerable);
+ assert_true(propdesc.configurable);
+ }, "Getting property descriptor for key " + key);
+
+ test(function() {
+ var element = document.createElement("div");
+ var dataset = element.dataset;
+
+ var proto = "proto getter for " + this.name;
+ var calledSetter = [];
+ Object.defineProperty(DOMStringMap.prototype, key, {
+ "get": function() { return proto; },
+ "set": this.unreached_func("Should not call [[Set]] on prototype"),
+ "configurable": true,
+ });
+ this.add_cleanup(function() {
+ delete DOMStringMap.prototype[key];
+ });
+
+ var value = "value for " + this.name;
+
+ assert_equals(dataset[key], proto);
+ assert_equals(element.getAttribute("data-" + key), null);
+ assert_equals(dataset[key] = value, value);
+ assert_equals(dataset[key], value);
+ assert_equals(element.getAttribute("data-" + key), value);
+ }, "Setting property for key " + key + " with accessor property on prototype");
+});
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-delete.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-delete.html
new file mode 100644
index 0000000000..1440118f6d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-delete.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Dataset - Delete</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Dataset - Delete</h1>
+ <div id="log"></div>
+ <script>
+ function testDelete(attr, prop)
+ {
+ var d = document.createElement("div");
+ d.setAttribute(attr, "value");
+ delete d.dataset[prop];
+ return d.hasAttribute(attr) === false && d.getAttribute(attr) != "value";
+ }
+
+ function testDeleteNoAdd(prop)
+ {
+ var d = document.createElement("div");
+ delete d.dataset[prop];
+ return true;
+ }
+
+ test(function() { assert_true(testDelete('data-foo', 'foo')); },
+ "Deleting element.dataset['foo'] should also remove an attribute with name 'data-foo' should it exist.");
+ test(function() { assert_true(testDelete('data-foo-bar', 'fooBar')); },
+ "Deleting element.dataset['fooBar'] should also remove an attribute with name 'data-foo-bar' should it exist.");
+ test(function() { assert_true(testDelete('data--', '-')); },
+ "Deleting element.dataset['-'] should also remove an attribute with name 'data--' should it exist.");
+ test(function() { assert_true(testDelete('data--foo', 'Foo')); },
+ "Deleting element.dataset['Foo'] should also remove an attribute with name 'data--foo' should it exist.");
+ test(function() {
+ var d = document.createElement("div");
+ d.setAttribute('data--foo', "value");
+ assert_equals(d.dataset['-foo'], undefined);
+ assert_false('-foo' in d.dataset);
+ delete d.dataset['-foo'];
+ assert_true(d.hasAttribute('data--foo'));
+ assert_equals(d.getAttribute('data--foo'), "value");
+ }, "Deleting element.dataset['-foo'] should not remove an attribute with name 'data--foo' should it exist.");
+ test(function() { assert_true(testDelete('data---foo', '-Foo')); },
+ "Deleting element.dataset['-Foo'] should also remove an attribute with name 'data---foo' should it exist.");
+ test(function() { assert_true(testDelete('data-', '')); },
+ "Deleting element.dataset[''] should also remove an attribute with name 'data-' should it exist.");
+ test(function() { assert_true(testDelete('data-\xE0', '\xE0')); },
+ "Deleting element.dataset['\xE0'] should also remove an attribute with name 'data-\xE0' should it exist.");
+ test(function() { assert_true(testDeleteNoAdd('foo')); },
+ "Deleting element.dataset['foo'] should not throw if even if the element does now have an attribute with the name data-foo.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-enumeration.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-enumeration.html
new file mode 100644
index 0000000000..4b1063379c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-enumeration.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Dataset - Enumeration</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Dataset - Enumeration</h1>
+ <div id="log"></div>
+ <script>
+ function testEnumeration(array)
+ {
+ var d = document.createElement("div");
+ for (var i = 0; i < array.length; ++i)
+ d.setAttribute(array[i], "value");
+
+ var count = 0;
+ for (var item in d.dataset)
+ count++;
+
+ return count;
+ }
+
+ test(function() { assert_equals(testEnumeration(['data-foo', 'data-bar', 'data-baz']), 3); },
+ "A dataset should be enumeratable.");
+ test(function() { assert_equals(testEnumeration(['data-foo', 'data-bar', 'dataFoo']), 2); },
+ "Only attributes who qualify as dataset properties should be enumeratable in the dataset.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-get.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-get.html
new file mode 100644
index 0000000000..ab4078c4fb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-get.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Dataset - Get</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Dataset - Get</h1>
+ <div id="log"></div>
+ <script>
+ function testGet(attr, expected)
+ {
+ var d = document.createElement("div");
+ d.setAttribute(attr, "value");
+ return d.dataset[expected] == "value";
+ }
+
+ test(function() { assert_true(testGet('data-foo', 'foo')); },
+ "Getting element.dataset['foo'] should return the value of element.getAttribute('data-foo')'");
+ test(function() { assert_true(testGet('data-foo-bar', 'fooBar')); },
+ "Getting element.dataset['fooBar'] should return the value of element.getAttribute('data-foo-bar')'");
+ test(function() { assert_true(testGet('data--', '-')); },
+ "Getting element.dataset['-'] should return the value of element.getAttribute('data--')'");
+ test(function() { assert_true(testGet('data--foo', 'Foo')); },
+ "Getting element.dataset['Foo'] should return the value of element.getAttribute('data--foo')'");
+ test(function() { assert_true(testGet('data---foo', '-Foo')); },
+ "Getting element.dataset['-Foo'] should return the value of element.getAttribute('data---foo')'");
+ test(function() { assert_true(testGet('data-Foo', 'foo')); },
+ "Getting element.dataset['foo'] should return the value of element.getAttribute('data-Foo')'");
+ test(function() { assert_true(testGet('data-', '')); },
+ "Getting element.dataset[''] should return the value of element.getAttribute('data-')'");
+ test(function() { assert_true(testGet('data-\xE0', '\xE0')); },
+ "Getting element.dataset['\xE0'] should return the value of element.getAttribute('data-\xE0')'");
+ test(function() { assert_true(testGet('data-to-string', 'toString')); },
+ "Getting element.dataset['toString'] should return the value of element.getAttribute('data-to-string')'");
+
+ function matchesNothingInDataset(attr)
+ {
+ var d = document.createElement("div");
+ d.setAttribute(attr, "value");
+
+ if (!d.dataset)
+ return false;
+
+ var count = 0;
+ for (var item in d.dataset)
+ count++;
+ return count == 0;
+ }
+
+ test(function() { assert_true(matchesNothingInDataset('dataFoo')); },
+ "Tests that an attribute named dataFoo does not make an entry in the dataset DOMStringMap.");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-prototype.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-prototype.html
new file mode 100644
index 0000000000..6b16618461
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-prototype.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Dataset - element.dataset is an instance of DOMStringMap</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Dataset - element.dataset is an instance of DOMStringMap</h1>
+ <div id="log"></div>
+ <script>
+ test(function() { assert_true(document.createElement("div").dataset instanceof window.DOMStringMap); },
+ "An elements dataset property is an instance of a DOMStringMap");
+ test(function() {
+ var dataset = document.createElement("div").dataset;
+ assert_true("toString" in dataset, '"toString" in dataset');
+ assert_equals(dataset.toString, Object.prototype.toString);
+ assert_false("expando" in dataset, '"expando" in dataset');
+ assert_equals(dataset.expando, undefined);
+ Object.prototype.expando = 42;
+ assert_true("expando" in dataset, '"expando" in dataset');
+ assert_equals(dataset.expando, 42);
+ }, "Properties on Object.prototype should shine through.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-set.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-set.html
new file mode 100644
index 0000000000..a5bc177f50
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset-set.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Dataset - Set</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Dataset - Set</h1>
+ <div id="log"></div>
+ <script>
+ function testSet(prop, expected)
+ {
+ var d = document.createElement("div");
+ d.dataset[prop] = "value";
+ return d.getAttribute(expected) == "value";
+ }
+
+ test(function() { assert_true(testSet('foo', 'data-foo')); },
+ "Setting element.dataset['foo'] should also change the value of element.getAttribute('data-foo')");
+ test(function() { assert_true(testSet('fooBar', 'data-foo-bar')); },
+ "Setting element.dataset['fooBar'] should also change the value of element.getAttribute('data-foo-bar')");
+ test(function() { assert_true(testSet('-', 'data--')); },
+ "Setting element.dataset['-'] should also change the value of element.getAttribute('data--')");
+ test(function() { assert_true(testSet('Foo', 'data--foo')); },
+ "Setting element.dataset['Foo'] should also change the value of element.getAttribute('data--foo')");
+ test(function() { assert_true(testSet('-Foo', 'data---foo')); },
+ "Setting element.dataset['-Foo'] should also change the value of element.getAttribute('data---foo')");
+ test(function() { assert_true(testSet('', 'data-')); },
+ "Setting element.dataset[''] should also change the value of element.getAttribute('data-')");
+ test(function() { assert_true(testSet('\xE0', 'data-\xE0')); },
+ "Setting element.dataset['\xE0'] should also change the value of element.getAttribute('data-\xE0')");
+ test(function() { assert_throws_dom('SYNTAX_ERR', function() { testSet('-foo', 'dummy') }); },
+ "Setting element.dataset['-foo'] should throw a SYNTAX_ERR");
+ test(function() { assert_throws_dom('INVALID_CHARACTER_ERR', function() { testSet('foo\x20', 'dummy') }); },
+ "Setting element.dataset['foo\x20'] should throw an INVALID_CHARACTER_ERR");
+ test(function() { assert_throws_dom('INVALID_CHARACTER_ERR', function() { testSet('\u037Efoo', 'dummy') }); },
+ "Setting element.dataset['\u037Efoo'] should throw an INVALID_CHARACTER_ERR");
+ test(function() { assert_true(testSet('\u0BC6foo', 'data-\u0BC6foo')); },
+ "Setting element.dataset['\u0BC6foo'] should also change the value of element.getAttribute('\u0BC6foo')");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dataset.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset.html
new file mode 100644
index 0000000000..a4a16d014d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dataset.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>dataset: should exist and work on HTML and SVG elements, but not random elements</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var div = document.createElement("div");
+test(function() {
+ assert_true(div.dataset instanceof DOMStringMap);
+}, "HTML elements should have a .dataset");
+test(function() {
+ assert_false("foo" in div.dataset);
+ assert_equals(div.dataset.foo, undefined);
+}, "Should return 'undefined' before setting an attribute")
+test(function() {
+ div.setAttribute("data-foo", "value");
+ assert_true("foo" in div.dataset);
+ assert_equals(div.dataset.foo, "value");
+}, "Should return 'value' if that's the value")
+test(function() {
+ div.setAttribute("data-foo", "");
+ assert_true("foo" in div.dataset);
+ assert_equals(div.dataset.foo, "");
+}, "Should return the empty string if that's the value")
+test(function() {
+ div.removeAttribute("data-foo");
+ assert_false("foo" in div.dataset);
+ assert_equals(div.dataset.foo, undefined);
+}, "Should return 'undefined' after removing an attribute")
+test(function() {
+ assert_equals(document.createElementNS("test", "test").dataset, undefined);
+}, "Should not have a .dataset on random elements");
+test(function() {
+ var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
+ assert_true(svg.dataset instanceof DOMStringMap);
+}, "SVG elements should have a .dataset");
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js
new file mode 100644
index 0000000000..0d4e4b82d9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js
@@ -0,0 +1,115 @@
+test(() => {
+ assert_true(document.documentElement.matches(":dir(ltr)"));
+}, "Root element has a direction");
+
+test(() => {
+ const ele = document.createElement("foobar");
+ assert_true(ele.matches(":dir(ltr)"));
+}, "Element outside the document tree has a direction");
+
+test(() => {
+ const ele = document.createElementNS("foobar", "foobar");
+ assert_true(ele.matches(":dir(ltr)"));
+}, "Non-HTML element outside the document tree has a direction");
+
+test(() => {
+ const ele = document.createElement("foobar");
+ ele.dir = "rtl";
+ const ele2 = document.createElement("foobar");
+ ele.append(ele2);
+ assert_true(ele2.matches(":dir(rtl)"));
+ ele.dir = "ltr";
+ assert_true(ele2.matches(":dir(ltr)"), "direction after dynamic change");
+}, "Element without direction has parent element direction");
+
+test(() => {
+ const ele = document.createElement("foobar");
+ ele.dir = "rtl";
+ const ele2 = document.createElementNS("foobar", "foobar");
+ ele.append(ele2);
+ assert_true(ele2.matches(":dir(rtl)"));
+ ele.dir = "ltr";
+ assert_true(ele2.matches(":dir(ltr)"), "direction after dynamic change");
+}, "Non-HTML element without direction has parent element direction");
+
+test(() => {
+ let container1 = document.createElement("div");
+ document.body.appendChild(container1);
+ let container2 = document.createElement("div");
+
+ for (let container of [container1, container2]) {
+ container.dir = "rtl";
+ let e = document.createElement("div");
+ assert_true(e.matches(":dir(ltr)"));
+ container.appendChild(e);
+ assert_false(e.matches(":dir(ltr)"));
+ e.remove();
+ assert_true(e.matches(":dir(ltr)"));
+ }
+
+ container1.remove();
+}, "dir inheritance is correct after insertion and removal from document");
+
+test(() => {
+ const ele = document.createElement("foobar");
+ ele.dir = "auto";
+ const ele2 = document.createElementNS("foobar", "foobar");
+ ele.append(ele2);
+ const text = document.createTextNode("\u05D0\u05D1\u05D2");
+ ele2.append(text);
+ assert_true(ele.matches(":dir(rtl)"), "is RTL before change");
+ assert_true(ele2.matches(":dir(rtl)"), "child is RTL before change");
+ text.data = "ABC";
+ assert_true(ele.matches(":dir(ltr)"), "is LTR after change");
+ assert_true(ele2.matches(":dir(ltr)"), "child is LTR after change");
+}, "Non-HTML element text contents influence dir=auto");
+
+test(() => {
+ const e1 = document.createElement("div");
+ e1.dir = "auto";
+ const e2 = document.createElement("div");
+ e2.dir = "auto";
+ e2.innerText = "A";
+ e1.append(e2);
+ assert_true(e1.matches(":dir(ltr)"), "parent is LTR before changes");
+ assert_true(e2.matches(":dir(ltr)"), "child is LTR before changes");
+ e2.removeAttribute("dir");
+ assert_true(e1.matches(":dir(ltr)"), "parent is LTR after removing dir attribute on child");
+ assert_true(e2.matches(":dir(ltr)"), "child is LTR after removing dir attribute on child");
+ e2.firstChild.data = "\u05D0";
+ assert_false(e1.matches(":dir(ltr)"), "parent is RTL after changing text in child");
+ assert_false(e2.matches(":dir(ltr)"), "child is RTL after changing text in child");
+}, "text changes apply to dir=auto on further ancestor after removing dir=auto from closer ancestor");
+
+for (const bdi_test of [
+ { markup: "<bdi dir=ltr>A</bdi>", expected: "ltr", desc: "dir=ltr with LTR contents" },
+ { markup: "<bdi dir=ltr>\u05d0</bdi>", expected: "ltr", desc: "dir=ltr with RTL contents" },
+ { markup: "<bdi dir=ltr></bdi>", expected: "ltr", desc: "dir=ltr empty" },
+ { markup: "<bdi dir=rtl>A</bdi>", expected: "rtl", desc: "dir=rtl with LTR contents" },
+ { markup: "<bdi dir=rtl>\u05d0</bdi>", expected: "rtl", desc: "dir=rtl with RTL contents" },
+ { markup: "<bdi dir=rtl></bdi>", expected: "rtl", desc: "dir=rtl empty" },
+ { markup: "<bdi dir=auto>A</bdi>", expected: "ltr", desc: "dir=auto with LTR contents" },
+ { markup: "<bdi dir=auto>\u05d0</bdi>", expected: "rtl", desc: "dir=auto with RTL contents" },
+ { markup: "<bdi dir=auto></bdi>", expected: "parent", desc: "dir=auto empty" },
+ { markup: "<bdi>A</bdi>", expected: "ltr", desc: "no dir attribute with LTR contents" },
+ { markup: "<bdi>\u05d0</bdi>", expected: "rtl", desc: "no dir attribute with RTL contents" },
+ { markup: "<bdi></bdi>", expected: "parent", desc: "no dir attribute empty" },
+]) {
+ for (const parent_dir of [ "ltr", "rtl" ]) {
+ test(() => {
+ const parent_element = document.createElement("div");
+ parent_element.dir = parent_dir;
+ document.body.appendChild(parent_element);
+ parent_element.innerHTML = bdi_test.markup;
+ const bdi_element = parent_element.querySelector("bdi");
+ let expected = bdi_test.expected;
+ if (expected == "parent") {
+ expected = parent_dir;
+ }
+ const not_expected = (expected == "ltr") ? "rtl" : "ltr";
+ assert_true(bdi_element.matches(`:dir(${expected})`));
+ assert_false(bdi_element.matches(`:dir(${not_expected})`));
+ parent_element.remove();
+ }, `directionality of bdi elements: ${bdi_test.desc} in ${parent_dir} parent`);
+ }
+}
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-div-append-child.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-div-append-child.html
new file mode 100644
index 0000000000..e69f64b3a9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-div-append-child.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>HTML Test: input with dir=auto, then append a child</title>
+<meta charset="utf-8">
+<meta name="assert" content="The dir global attribute set to auto applies when a child is appended" />
+<link rel="author" title="HTML5 bidi test WG" href="mailto:japhet@chromium.org" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="div" dir="auto"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(div).direction, "ltr");
+ div.appendChild(document.createTextNode('اختبر SomeText'));
+ assert_equals(getComputedStyle(div).direction, "rtl");
+}, 'dir auto: updates on appendChild');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-dynamic-changes.window.js b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-dynamic-changes.window.js
new file mode 100644
index 0000000000..a0cf4aae7b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-dynamic-changes.window.js
@@ -0,0 +1,276 @@
+function setup_tree(light_tree, shadow_tree) {
+ let body = document.body;
+ let old_length = body.childNodes.length;
+ body.insertAdjacentHTML("beforeend", light_tree.trim());
+ if (body.childNodes.length != old_length + 1) {
+ throw "unexpected markup";
+ }
+ let result = body.lastChild;
+ if (shadow_tree) {
+ let shadow = result.querySelector("#root").attachShadow({mode: "open"});
+ shadow.innerHTML = shadow_tree.trim();
+ return [result, shadow];
+ }
+ return result;
+}
+
+test(t => {
+ let a = setup_tree(`
+ <div id="a" dir="auto">
+ <div id="b"></div>
+ hello
+ </div>
+ `);
+
+ let acs = getComputedStyle(a);
+ assert_true(a.matches(":dir(ltr)"), ":dir(ltr) matches before insertion");
+ assert_false(a.matches(":dir(rtl)"), ":dir(rtl) does not match before insertion");
+ assert_equals(acs.direction, "ltr", "CSSdirection before insertion");
+ b.innerHTML = "\u05D0";
+ assert_false(a.matches(":dir(ltr)"), ":dir(ltr) does not match after insertion");
+ assert_true(a.matches(":dir(rtl)"), ":dir(rtl) matches after insertion");
+ assert_equals(acs.direction, "rtl", "CSSdirection after insertion");
+
+ a.remove();
+}, "dynamic insertion of RTL text in a child element");
+
+test(() => {
+ let div_rtlchar = document.createElement("div");
+ div_rtlchar.innerHTML = "\u05D0";
+
+ let container1 = document.createElement("div");
+ document.body.appendChild(container1);
+ let container2 = document.createElement("div");
+
+ for (let container of [container1, container2]) {
+ container.dir = "auto";
+ assert_true(container.matches(":dir(ltr)"));
+ container.appendChild(div_rtlchar);
+ assert_false(container.matches(":dir(ltr)"));
+ div_rtlchar.remove();
+ assert_true(container.matches(":dir(ltr)"));
+ }
+
+ container1.remove();
+}, "dir=auto changes for content insertion and removal, in and out of document");
+
+test(() => {
+ let tree, shadow;
+ [tree, shadow] = setup_tree(`
+ <div>
+ <div id="root">
+ <span id="l">A</span>
+ <span id="r">\u05D0</span>
+ </div>
+ </div>
+ `, `
+ <slot id="one" name="one" dir="auto">\u05D0</slot>
+ <slot id="two" dir="auto"></slot>
+ `);
+
+ let one = shadow.getElementById("one");
+ let two = shadow.getElementById("two");
+ let l = tree.querySelector("#l");
+ let r = tree.querySelector("#r");
+ assert_false(one.matches(":dir(ltr)"), "#one while empty");
+ assert_true(two.matches(":dir(ltr)"), "#two with both spans");
+ l.slot = "one";
+ assert_true(one.matches(":dir(ltr)"), "#one with LTR child span");
+ assert_false(two.matches(":dir(ltr)"), "#two with RTL child span");
+ r.slot = "one";
+ assert_true(one.matches(":dir(ltr)"), "#one with both child spans");
+ assert_true(two.matches(":dir(ltr)"), "#two while empty");
+ l.slot = "";
+ assert_false(one.matches(":dir(ltr)"), "#one with RTL child span");
+ assert_true(two.matches(":dir(ltr)"), "#two with LTR child span");
+
+ tree.remove();
+}, "dir=auto changes for slot reassignment");
+
+test(() => {
+ let tree, shadow;
+ [tree, shadow] = setup_tree(`
+ <div dir=auto>
+ <div id=root>
+ <div id=text>A</div>
+ </div>
+ </div>
+ `, `
+ <div dir=ltr>
+ <slot id=slot dir=auto></slot>
+ </div>
+ `);
+
+ let text = tree.querySelector("#text");
+ let slot = shadow.querySelector("#slot");
+
+ assert_true(tree.matches(":dir(ltr)"), "node tree ancestor before first text change");
+ assert_true(slot.matches(":dir(ltr)"), "slot before first text change");
+ text.innerText = "\u05D0";
+ assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after first text change");
+ assert_false(slot.matches(":dir(ltr)"), "slot after first text change");
+ tree.dir = "rtl";
+ assert_false(tree.matches(":dir(ltr)"), "node tree ancestor before second text change");
+ assert_false(slot.matches(":dir(ltr)"), "slot before second text change");
+ text.innerText = "A";
+ assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after second text change");
+ assert_true(slot.matches(":dir(ltr)"), "slot after second text change");
+ slot.dir = "ltr";
+ assert_false(tree.matches(":dir(ltr)"), "node tree ancestor before third text change");
+ assert_true(slot.matches(":dir(ltr)"), "slot before third text change");
+ text.innerText = "\u05D0";
+ assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after third text change");
+ assert_true(slot.matches(":dir(ltr)"), "slot after third text change");
+ slot.dir = "auto";
+ tree.dir = "auto";
+ assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after fourth text change");
+ assert_false(slot.matches(":dir(ltr)"), "slot after fourth text change");
+ text.innerText = "A";
+ assert_true(tree.matches(":dir(ltr)"), "node tree ancestor before fourth text change");
+ assert_true(slot.matches(":dir(ltr)"), "slot before fourth text change");
+ slot.dir = "rtl";
+ assert_true(tree.matches(":dir(ltr)"), "node tree ancestor before fifth text change");
+ assert_false(slot.matches(":dir(ltr)"), "slot before fifth text change");
+ text.innerText = "\u05D0";
+ assert_false(tree.matches(":dir(ltr)"), "node tree ancestor before fifth text change");
+ assert_false(slot.matches(":dir(ltr)"), "slot before fifth text change");
+
+ tree.remove();
+}, "text changes affecting both slot and ancestor with dir=auto");
+
+test(() => {
+ let tree = setup_tree(`
+ <div dir="auto">
+ <span id="a1">A</span>
+ <span id="aleph1">\u05D0</span>
+ <span id="a2">A</span>
+ <span id="aleph2">\u05D0</span>
+ </div>
+ `);
+
+ let a1 = tree.querySelector("#a1");
+ let aleph1 = tree.querySelector("#aleph1");
+ assert_true(tree.matches(":dir(ltr)"), "initial state");
+ assert_false(tree.matches(":dir(rtl)"), "initial state");
+ a1.dir = "ltr";
+ assert_false(tree.matches(":dir(ltr)"), "after change 1");
+ a1.dir = "invalid";
+ assert_true(tree.matches(":dir(ltr)"), "after change 2");
+ a1.dir = "rtl";
+ assert_false(tree.matches(":dir(ltr)"), "after change 3");
+ a1.removeAttribute("dir");
+ assert_true(tree.matches(":dir(ltr)"), "after change 4");
+ a1.dir = "invalid";
+ assert_true(tree.matches(":dir(ltr)"), "after change 5");
+ a1.dir = "rtl";
+ assert_false(tree.matches(":dir(ltr)"), "after change 6");
+ aleph1.dir = "auto";
+ assert_true(tree.matches(":dir(ltr)"), "after change 7");
+ aleph1.dir = "invalid";
+ assert_false(tree.matches(":dir(ltr)"), "after change 8");
+
+ tree.remove();
+}, "dynamic changes to subtrees excluded as a result of the dir attribute");
+
+test(() => {
+ let tree = setup_tree(`
+ <div dir="auto">
+ <!-- element goes here -->
+ </div>
+ `);
+
+ let element = document.createElementNS("namespace", "element");
+ let text = document.createTextNode("\u05D0");
+ element.appendChild(text);
+ tree.prepend(element);
+ assert_not_equals(element.namespaceURI, tree.namespaceURI);
+
+ assert_true(tree.matches(":dir(rtl)"), "initial state");
+ assert_false(tree.matches(":dir(ltr)"), "initial state");
+ text.data = "A";
+ assert_true(tree.matches(":dir(ltr)"), "after dynamic change");
+ assert_false(tree.matches(":dir(rtl)"), "after dynamic change");
+
+ tree.remove();
+}, "dynamic changes inside of non-HTML elements");
+
+test(() => {
+ let tree, shadow;
+ [tree, shadow] = setup_tree(`
+ <div dir="auto">
+ <div id="root">
+ <element xmlns="namespace">A</element>
+ \u05D0
+ </div>
+ </div>
+ `, `
+ <div dir="ltr">
+ <slot dir="auto">\u05D0</slot>
+ </div>
+ `);
+
+ let element = tree.querySelector("element");
+ let slot = shadow.querySelector("slot");
+ let text = element.firstChild;
+
+ assert_true(tree.matches(":dir(ltr)"), "initial state (tree)");
+ assert_true(element.matches(":dir(ltr)"), "initial state (element)");
+ assert_true(slot.matches(":dir(ltr)"), "initial state (slot)");
+
+ text.data = "\u05D0";
+
+ assert_true(tree.matches(":dir(rtl)"), "state after first change (tree)");
+ assert_true(element.matches(":dir(rtl)"), "state after first change (element)");
+ assert_true(slot.matches(":dir(rtl)"), "state after first change (slot)");
+
+ text.data = "";
+
+ assert_true(tree.matches(":dir(rtl)"), "state after second change (tree)");
+ assert_true(element.matches(":dir(rtl)"), "state after second change (element)");
+ assert_true(slot.matches(":dir(rtl)"), "state after second change (slot)");
+
+ tree.remove();
+}, "slotted non-HTML elements");
+
+test(() => {
+ let tree, shadow;
+ [tree, shadow] = setup_tree(`
+ <div>
+ <div id="root">
+ <!-- element goes here -->
+ \u05D0
+ </div>
+ </div>
+ `, `
+ <div dir="ltr">
+ <slot></slot>
+ </div>
+ `);
+
+ let element = document.createElementNS("namespace", "element");
+ let text = document.createTextNode("A");
+ element.appendChild(text);
+ tree.querySelector("#root").prepend(element);
+
+ assert_not_equals(element.namespaceURI, tree.namespaceURI);
+
+ assert_true(tree.matches(":dir(ltr)"), "initial state (tree)");
+ assert_true(element.matches(":dir(ltr)"), "initial state (element)");
+
+ tree.dir = "auto";
+
+ assert_true(tree.matches(":dir(ltr)"), "state after making dir=auto (tree)");
+ assert_true(element.matches(":dir(ltr)"), "state after making dir=auto (element)");
+
+ text.data = "\u05D0";
+
+ assert_true(tree.matches(":dir(rtl)"), "state after first change (tree)");
+ assert_true(element.matches(":dir(rtl)"), "state after first change (element)");
+
+ text.data = "";
+
+ assert_true(tree.matches(":dir(rtl)"), "state after second change (tree)");
+ assert_true(element.matches(":dir(rtl)"), "state after second change (element)");
+
+ tree.remove();
+}, "slotted non-HTML elements after dynamically assigning dir=auto, and dir attribute ignored on non-HTML elements");
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-form-associated.window.js b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-form-associated.window.js
new file mode 100644
index 0000000000..3af7eb3c2e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-auto-form-associated.window.js
@@ -0,0 +1,71 @@
+// Keep this mostly synchronized with
+// html/semantics/forms/attributes-common-to-form-controls/dirname-only-if-applies.html
+// except that won't have "reset" and "button" as those don't submit their value
+[
+ "hidden",
+ "text",
+ "search",
+ "tel",
+ "url",
+ "email",
+ "password",
+ "submit",
+ "reset",
+ "button"
+].forEach(type => {
+ test(t => {
+ const input = document.createElement("input");
+ t.add_cleanup(() => input.remove());
+ input.type = type;
+ assert_equals(input.type, type);
+ document.body.append(input);
+
+ input.setAttribute("value", "\u05D0"); // The Hebrew letter Alef (strongly RTL)
+ assert_true(input.matches(":dir(ltr)"));
+ input.removeAttribute("value");
+
+ input.dir = "auto";
+ input.setAttribute("value", "\u05D0");
+ assert_true(input.matches(":dir(rtl)"));
+ input.removeAttribute("value");
+ assert_true(input.matches(":dir(ltr)"));
+
+ input.value = "\u05D0";
+ assert_true(input.matches(":dir(rtl)"));
+ }, `<input dir=auto type=${type}> directionality`);
+});
+
+[
+ "date",
+ "month",
+ "week",
+ "time",
+ "datetime-local",
+ "number",
+ "range",
+ "color",
+ "checkbox",
+ "radio",
+ // "file" // value setter throws
+ "image"
+].forEach(type => {
+ test(t => {
+ const input = document.createElement("input");
+ t.add_cleanup(() => input.remove());
+ input.type = type;
+ assert_equals(input.type, type);
+ input.dir = "auto";
+ input.value = "\u05D0"; // The Hebrew letter Alef (strongly RTL)
+ document.body.append(input);
+ assert_true(input.matches(":dir(ltr)"));
+ }, `<input dir=auto type=${type}> directionality`);
+});
+
+test(t => {
+ const input = document.createElement("textarea");
+ t.add_cleanup(() => input.remove());
+ input.dir = "auto";
+ input.value = "\u05D0"; // The Hebrew letter Alef (strongly RTL)
+ document.body.append(input);
+ assert_true(input.matches(":dir(rtl)"));
+}, `<textarea dir=auto> directionality`);
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-bdi-script.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-bdi-script.html
new file mode 100644
index 0000000000..3008043093
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-bdi-script.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>HTML Test: BDI: script adds a bdi element with R text and the direction should be RTL</title>
+<meta charset="utf-8">
+<meta name="assert" content="The dir global attribute defaults to auto on the bdi element" />
+<link rel="author" title="HTML5 bidi test WG" href="mailto:myid.shin@igalia.com" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-bdi-element" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="test1"><bdi>اختبر SomeText</bdi><br></div>
+<div id="test2"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(test1.firstChild).direction, "rtl");
+
+ const bdi = document.createElement("bdi");
+ var text = document.createTextNode('اختبر SomeText');
+ bdi.append(text);
+ test2.append(bdi);
+
+ assert_equals(getComputedStyle(test2.firstChild).direction, "rtl");
+}, 'BDI test: Directionality');
+</script>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01-ref.html
new file mode 100644
index 0000000000..3462b908b3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01.html
new file mode 100644
index 0000000000..2d6983cb14
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-01.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-01-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot dir="ltr"></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02-ref.html
new file mode 100644
index 0000000000..8f358cf647
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02.html
new file mode 100644
index 0000000000..0553ef9a33
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-02.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-02-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot dir="ltr"></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03-ref.html
new file mode 100644
index 0000000000..a2f574820f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p dir="rtl">paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03.html
new file mode 100644
index 0000000000..5986a9d0f7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-03.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-03-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot dir="rtl"></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04-ref.html
new file mode 100644
index 0000000000..5724b5b84d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p dir="rtl">اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04.html
new file mode 100644
index 0000000000..aa2ab9dc93
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-04.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-04-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot dir="rtl"></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05-ref.html
new file mode 100644
index 0000000000..13c5b16240
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05.html
new file mode 100644
index 0000000000..597cb8d5b8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-05.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-05-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot dir="auto"></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06-ref.html
new file mode 100644
index 0000000000..ca54b3bb2c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p dir="rtl">اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06.html
new file mode 100644
index 0000000000..6cfaf7fcec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-06.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the slot, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-06-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the &lt;slot&gt;, paragraph in the light tree</p>
+<div id="host"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot dir="auto"></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07-ref.html
new file mode 100644
index 0000000000..0a2de4d398
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="ltr"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07.html
new file mode 100644
index 0000000000..dd11f4a605
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-07.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-07-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="ltr"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08-ref.html
new file mode 100644
index 0000000000..09d8b24094
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="ltr"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08.html
new file mode 100644
index 0000000000..8b1377d32a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-08.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-08-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="ltr"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09-ref.html
new file mode 100644
index 0000000000..73b9cb1487
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="rtl"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09.html
new file mode 100644
index 0000000000..d9c66e7931
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-09.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-09-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="rtl"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10-ref.html
new file mode 100644
index 0000000000..391e5047f9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="rtl"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10.html
new file mode 100644
index 0000000000..ad35af808e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-10.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-10-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="rtl"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11-ref.html
new file mode 100644
index 0000000000..ae7e849c97
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="auto"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11.html
new file mode 100644
index 0000000000..31e04f065e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-11.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-11-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="auto"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <slot></slot>
+ `;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12-ref.html
new file mode 100644
index 0000000000..6f6b40218a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="rtl"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12.html
new file mode 100644
index 0000000000..f6183a98d9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-12.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-12-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the light tree</p>
+<div id="host" dir="rtl"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<slot></slot>`;
+ result.innerHTML += html_direction(host.firstChild) + " / " + getComputedStyle(host.firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13-ref.html
new file mode 100644
index 0000000000..a6ec54186f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="ltr"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13.html
new file mode 100644
index 0000000000..35a00e6d28
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-13.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-13-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="ltr"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p>paragraph.</p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14-ref.html
new file mode 100644
index 0000000000..73d4eab962
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="ltr"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14.html
new file mode 100644
index 0000000000..6aa4a639fd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-14.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-14-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="ltr"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p>اختبر.</p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15-ref.html
new file mode 100644
index 0000000000..0139997e65
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="rtl"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: rtl / rtl.</p>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15.html
new file mode 100644
index 0000000000..37c05d283c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-15.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-15-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="rtl"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p>paragraph.</p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16-ref.html
new file mode 100644
index 0000000000..926b7a900d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="rtl"><p>اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16.html
new file mode 100644
index 0000000000..2a8bb64a1b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-16.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-16-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="rtl"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p>اختبر.</p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17-ref.html
new file mode 100644
index 0000000000..b17c3e191d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="auto"><p>paragraph.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17.html
new file mode 100644
index 0000000000..7016a27174
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-17.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-17-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="auto"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p>paragraph.</p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18-ref.html
new file mode 100644
index 0000000000..6155a9f2c9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="ltr"><p dir="">اختبر.</p></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18.html
new file mode 100644
index 0000000000..6796dd18ea
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-18.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the shadow host, paragraph in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-18-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the shadow host, paragraph in the shadow tree</p>
+<div id="host" dir="auto"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p>اختبر.</p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19-ref.html
new file mode 100644
index 0000000000..61a9592a4a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="ltr">text.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19.html
new file mode 100644
index 0000000000..a345a7b60d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-19.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-19-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="ltr"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `text.`;
+ result.innerHTML += html_direction(host) + " / " + getComputedStyle(host).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20-ref.html
new file mode 100644
index 0000000000..2e81381772
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="ltr">اختبر.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20.html
new file mode 100644
index 0000000000..0072d9b6c9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-20.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-20-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="ltr"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `اختبر.`;
+ result.innerHTML += html_direction(host) + " / " + getComputedStyle(host).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21-ref.html
new file mode 100644
index 0000000000..58c8d426f2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="rtl">text.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21.html
new file mode 100644
index 0000000000..5b9a4aa184
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-21.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-21-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="rtl"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `text.`;
+ result.innerHTML += html_direction(host) + " / " + getComputedStyle(host).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22-ref.html
new file mode 100644
index 0000000000..6721da534f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="rtl">اختبر.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: rtl / rtl.</p>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22.html
new file mode 100644
index 0000000000..009ba460ce
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-22.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-22-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="rtl"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `اختبر.`;
+ result.innerHTML += html_direction(host) + " / " + getComputedStyle(host).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23-ref.html
new file mode 100644
index 0000000000..bf06a44847
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="auto">text.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23.html
new file mode 100644
index 0000000000..dea79b93e1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-23.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-23-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="auto"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `text.`;
+ result.innerHTML += html_direction(host) + " / " + getComputedStyle(host).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24-ref.html
new file mode 100644
index 0000000000..8453e94533
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="ltr">اختبر.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24.html
new file mode 100644
index 0000000000..d26ccb270a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-24.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the div (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-24-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the div (shadow host), text in the shadow tree</p>
+<div id="host" dir="auto"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the host’s text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `اختبر.`;
+ result.innerHTML += html_direction(host) + " / " + getComputedStyle(host).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25-ref.html
new file mode 100644
index 0000000000..5a2f6d5777
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+<p dir="ltr">paragraph.</p>
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25.html
new file mode 100644
index 0000000000..508b8260ff
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-25.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-25-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+paragraph.
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p dir="ltr"><slot></slot></p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26-ref.html
new file mode 100644
index 0000000000..41e1dbc8a8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+<p dir="ltr">اختبر.</p>
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26.html
new file mode 100644
index 0000000000..b19cb976f8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-26.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-26-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+اختبر.
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p dir="ltr"><slot></slot></p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27-ref.html
new file mode 100644
index 0000000000..075230d055
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+<p dir="rtl">paragraph.</p>
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27.html
new file mode 100644
index 0000000000..8380f17b21
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-27.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-27-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+paragraph.
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p dir="rtl"><slot></slot></p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28-ref.html
new file mode 100644
index 0000000000..580f5fe3b1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+<p dir="rtl">اختبر.</p>
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28.html
new file mode 100644
index 0000000000..3a0d6756b3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-28.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-28-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+اختبر.
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p dir="rtl"><slot></slot></p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29-ref.html
new file mode 100644
index 0000000000..4b949acb09
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+<p dir="ltr">para.</p>
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29.html
new file mode 100644
index 0000000000..46cbf8d291
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-29.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-29-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+para.
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p dir="auto"><slot></slot></p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30-ref.html
new file mode 100644
index 0000000000..be2cc44abd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+div p {width: 50%; border: 1px dotted;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+<p dir="ltr">اختبر.</p>
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30.html
new file mode 100644
index 0000000000..2a3e56e62c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-30.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on a paragraph in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-30-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on a paragraph in the shadow tree, text in the light tree</p>
+<div id="host">
+اختبر.
+</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the paragraph is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>p {width: 50%; border: 1px dotted;}</style><p dir="auto"><slot></slot></p>`;
+ result.innerHTML += html_direction(root.querySelector('p')) + " / " + getComputedStyle(root.querySelector('p')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31-ref.html
new file mode 100644
index 0000000000..1c5b5b11a8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the slot, span in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the slot, span in the light tree</p>
+<div id="host" dir="ltr"><span dir="rtl" style="unicode-bidi: normal">RTL.</span></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the span is: ltr / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31.html
new file mode 100644
index 0000000000..d48a0f8c04
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-31.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the slot, span in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-31-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the slot, span in the light tree</p>
+<div id="host" dir="ltr"><span>RTL.</span></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the span is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<slot dir="rtl"></slot>`;
+ result.innerHTML += html_direction(host.querySelector('span')) + " / " + getComputedStyle(host.querySelector('span')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32-ref.html
new file mode 100644
index 0000000000..6e5d9a96e8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, span in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, span in the shadow tree</p>
+<div id="host" dir="rtl"><span>RTL.</span></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the shadow span is: rtl / rtl.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32.html
new file mode 100644
index 0000000000..ac9c9e4579
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-32.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=rtl on the shadow host, span in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-32-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on the shadow host, span in the shadow tree</p>
+<div id="host" dir="rtl"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the shadow span is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<style>span {border: 1px solid silver;}</style><span>RTL.</span>`;
+ result.innerHTML += html_direction(root.querySelector('span')) + " / " + getComputedStyle(root.querySelector('span')).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33-ref.html
new file mode 100644
index 0000000000..cdf89d78bb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the span (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on the span (shadow host), text in the shadow tree</p>
+<span id="host" dir="ltr">اختبر.</span>
+<p id="result">The HTML direction / computed CSS `direction` value for the shadow host is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33.html
new file mode 100644
index 0000000000..814e3b34d6
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-33.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on the span (shadow host), text in the shadow tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-33-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on the span (shadow host), text in the shadow tree</p>
+<span id="host" dir="auto"></span>
+<p id="result">The HTML direction / computed CSS `direction` value for the shadow host is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `اختبر.`;
+ result.innerHTML += html_direction(host) + " / " + getComputedStyle(host).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34-ref.html
new file mode 100644
index 0000000000..4cedca8e50
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on a span in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` on a span in the shadow tree, text in the light tree</p>
+<div id="host"><span dir="ltr">اختبر.</span></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the shadow span is: ltr / ltr.</p>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34.html
new file mode 100644
index 0000000000..07f75d216a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-34.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto on a span in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-34-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` on a span in the shadow tree, text in the light tree</p>
+<div id="host">اختبر.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the shadow span is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<span dir="auto"><slot></slot></span>`;
+ result.innerHTML += html_direction(root.querySelector("span")) + " / " + getComputedStyle(root.querySelector("span")).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35-ref.html
new file mode 100644
index 0000000000..fdcc2657f3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a div in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div#host {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>`dir=ltr` on a div in the shadow tree, text in the light tree</p>
+<div id="host" dir="rtl"><div dir="ltr"><span>اختبر.</span></div></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the slot is: ltr / ltr.</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35.html
new file mode 100644
index 0000000000..8d063f44e6
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-35.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a div in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-35-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=ltr` on a div in the shadow tree, text in the light tree</p>
+<div id="host" dir="rtl"><span>اختبر.</span></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the slot is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<div dir="ltr"><slot></slot></div>`;
+ result.innerHTML += html_direction(root.querySelector("div").firstChild) + " / " + getComputedStyle(root.querySelector("div").firstChild).direction + '.';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36-ref.html
new file mode 100644
index 0000000000..f8af117e3b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto and text in a slot in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` and text in a slot in the shadow tree, text in the light tree</p>
+<div id="host">English.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the light tree text is: ltr / ltr (on the slot) and ltr / ltr (on the host).</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36.html
new file mode 100644
index 0000000000..ff4f11b3a6
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-36.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto and text in a slot in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-36-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` and text in a slot in the shadow tree, text in the light tree</p>
+<div id="host">English.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the light tree text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<slot dir=auto>اختبر</slot>`;
+ result.innerHTML += html_direction(root.firstChild) + " / " + getComputedStyle(root.firstChild).direction + " (on the " + root.firstChild.localName + ') and ' + html_direction(host) + " / " + getComputedStyle(host).direction + ' (on the ' + host.id + ').';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37-ref.html
new file mode 100644
index 0000000000..5941b23c02
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto and text in a div in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` and text in a div in the shadow tree, text in the light tree</p>
+<div id="host" dir="rtl">اختبر.123 456.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the light tree text is: rtl / rtl (on the div) and ltr / ltr (on the host).</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37.html
new file mode 100644
index 0000000000..ba0480636a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-37.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto and text in a div in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-37-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` and text in a div in the shadow tree, text in the light tree</p>
+<div id="host">123 456.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the light tree text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<div dir="auto">اختبر.<slot></slot></div>`;
+ result.innerHTML += html_direction(root.firstChild) + " / " + getComputedStyle(root.firstChild).direction + " (on the " + root.firstChild.localName + ') and ' + html_direction(host) + " / " + getComputedStyle(host).direction + ' (on the ' + host.id + ').';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38-ref.html
new file mode 100644
index 0000000000..f8af117e3b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto and text in a slot in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>`dir=auto` and text in a slot in the shadow tree, text in the light tree</p>
+<div id="host">English.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the light tree text is: ltr / ltr (on the slot) and ltr / ltr (on the host).</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38.html
new file mode 100644
index 0000000000..e8f25eb568
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-38.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=auto and text in a slot in the shadow tree, text in the light tree</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-38-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=auto` and text in a slot in the shadow tree, text in the light tree</p>
+<div id="host">English.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the light tree text is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<slot dir=auto>اختبر.</slot>`;
+ result.innerHTML += html_direction(root.firstChild) + " / " + getComputedStyle(root.firstChild).direction + " (on the " + root.firstChild.localName + ') and ' + html_direction(host) + " / " + getComputedStyle(host).direction + ' (on the ' + host.id + ').';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39-ref.html
new file mode 100644
index 0000000000..8f75e9877c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: text in the light tree, slotted node</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+</head>
+<body>
+
+<p>text in the light tree, slotted node</p>
+<div id="host">slotted text.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the defaultContent when there is a slotted node: ltr / ltr (on the div).</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39.html
new file mode 100644
index 0000000000..72fdd665fc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-39.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: text in the light tree, slotted node</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-39-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>text in the light tree, slotted node</p>
+<div id="host">slotted text.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the defaultContent when there is a slotted node: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<slot dir=auto><div>&#1571;&#1582;&#1576;&#1575;&#1585;</div>.</slot>`;
+ result.innerHTML += html_direction(root.firstChild.firstChild) + " / " + getComputedStyle(root.firstChild.firstChild).direction + " (on the " + root.firstChild.firstChild.localName + ').';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40-ref.html
new file mode 100644
index 0000000000..c1c074812b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: text in the light tree, no slotted node</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+</style>
+</head>
+<body>
+
+<p>text in the light tree, no slotted node</p>
+<div id="host" dir="rtl">&#1571;&#1582;&#1576;&#1575;&#1585;.</div>
+<p id="result">The HTML direction / computed CSS `direction` value for the defaultContent when there is not a slotted node: rtl / rtl (on the div).</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40.html
new file mode 100644
index 0000000000..567f4fa9da
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-40.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: text in the light tree, no slotted node</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-40-ref.html">
+<style type="text/css">
+body {width: 600px;}
+div {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>text in the light tree, no slotted node</p>
+<div id="host"></div>
+<p id="result">The HTML direction / computed CSS `direction` value for the defaultContent when there is not a slotted node: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<slot dir=auto><div>&#1571;&#1582;&#1576;&#1575;&#1585;.</div></slot>`;
+ result.innerHTML += html_direction(root.firstChild.firstChild) + " / " + getComputedStyle(root.firstChild.firstChild).direction + " (on the " + root.firstChild.firstChild.localName + ').';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41-ref.html
new file mode 100644
index 0000000000..b52e08df20
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a div in the shadow tree, dir=rtl on the shadow host, no slotted text in the light or dark trees</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<style type="text/css">
+body {width: 600px;}
+#host {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+</head>
+<body>
+
+<p>`dir=rtl` on a div in the shadow tree, `dir=ltr` on the shadow host, no slotted text in the light or dark trees</p>
+<div id="host" dir="ltr"><div dir="rtl"><span></span></div></div>
+<p id="result">The HTML direction / computed CSS `direction` value is: rtl / rtl (on the slot).</p>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41.html
new file mode 100644
index 0000000000..6c025fb3de
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-41.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>[dir] and shadow slots: dir=ltr on a div in the shadow tree, dir=rtl on the shadow host, no slotted text in the light or dark trees</title>
+<link rel="author" title="Eric Meyer" href="mailto:emeyer@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-dir-attribute">
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<link rel="help" href="https://github.com/whatwg/html/pull/9796">
+<link rel="match" href="dir-shadow-41-ref.html">
+<style type="text/css">
+body {width: 600px;}
+#host {border: 1px solid gray; margin: 1em; padding: 0.25em;}
+span {border: 1px solid silver;}
+
+</style>
+<script src="dir-shadow-utils.js"></script>
+</head>
+<body>
+
+<p>`dir=rtl` on a div in the shadow tree, `dir=ltr` on the shadow host, no slotted text in the light or dark trees</p>
+<div id="host" dir="ltr"><span slot="x1"></span></div>
+<p id="result">The HTML direction / computed CSS `direction` value is: </p>
+
+<script type="text/javascript">
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = `<div dir="rtl"><slot dir="auto" name="x1"></slot></div>`;
+ result.innerHTML += html_direction(root.querySelector("div[dir=rtl]").firstChild) + " / " + getComputedStyle(root.querySelector("div[dir=rtl]").firstChild).direction + " (on the " + root.querySelector("div[dir=rtl]").firstChild.localName + ').';
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-utils.js b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-utils.js
new file mode 100644
index 0000000000..c7d89cf908
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-shadow-utils.js
@@ -0,0 +1,8 @@
+function html_direction(element) {
+ let is_ltr = element.matches(":dir(ltr)");
+ let is_rtl = element.matches(":dir(rtl)");
+ if (is_ltr == is_rtl) {
+ return "error";
+ }
+ return is_ltr ? "ltr" : "rtl";
+}
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-slots-directionality.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-slots-directionality.html
new file mode 100644
index 0000000000..db783fc55c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-slots-directionality.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<title>HTML Test: dir=auto|rtl with slots, and direction should be RTL</title>
+<meta charset="UTF-8">
+<meta name="author" title="Miyoung Shin" href="mailto:myid.shin@igalia.com">
+<meta name="author" title="L. David Baron" href="mailto:dbaron@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute"/>
+<link rel="help" href="https://github.com/whatwg/html/issues/3699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="host1"><span></span></div>
+<div id="host2" dir="rtl"></div>
+<span id="host3" dir="auto"></span>
+<div id="host4">اختبر</div>
+<div id="host5"></div>
+<div id="host6">اختبر</div>
+<script>
+
+test(() => {
+ let root1 = host1.attachShadow({mode:"open"});
+ root1.innerHTML = '<slot dir="rtl"></slot>';
+ let span = host1.firstChild;
+ assert_equals(getComputedStyle(span).direction, "rtl");
+ assert_true(span.matches(":dir(ltr)"));
+}, 'Slots: Directionality: dir=rtl on slot');
+
+test(() => {
+ let root2 = host2.attachShadow({mode:"open"});
+ root2.innerHTML = '<span></span>';
+ let span = root2.querySelector("span");
+ assert_equals(getComputedStyle(span).direction, "rtl");
+ assert_true(span.matches(":dir(rtl)"));
+}, 'Slots: Directionality: dir=rtl on host');
+
+test(() => {
+ let root3 = host3.attachShadow({mode:"open"});
+ root3.innerHTML = `اختبر`;
+ let span = host3;
+ assert_equals(getComputedStyle(span).direction, "ltr");
+ assert_true(span.matches(":dir(ltr)"));
+}, 'Slots: Directionality: dir=auto on host with Arabic shadow tree content');
+
+test(() => {
+ let root4 = host4.attachShadow({mode:"open"});
+ root4.innerHTML = '<span dir="auto"><slot></slot></span>';
+ let span = root4.querySelector("span");
+ assert_equals(getComputedStyle(span).direction, "ltr");
+ assert_true(span.matches(":dir(ltr)"));
+}, 'Slots: Directionality: dir=auto in shadow tree with Arabic light tree content');
+
+test(() => {
+ let root5 = host5.attachShadow({mode:"open"});
+ root5.innerHTML = '<span dir="auto"><slot>اختبر</slot></span>';
+ let span = root5.querySelector("span");
+ assert_equals(getComputedStyle(span).direction, "ltr");
+ assert_true(span.matches(":dir(ltr)"));
+}, 'Slots: Directionality: dir=auto in shadow tree with Arabic shadow tree content');
+
+test(() => {
+ let root6 = host6.attachShadow({mode:"open"});
+ root6.innerHTML = '<slot dir="auto"></slot>';
+ let span = root6.querySelector("slot");
+ assert_equals(getComputedStyle(span).direction, "rtl");
+ assert_true(span.matches(":dir(rtl)"));
+}, 'Slots: Directionality: dir=auto on slot with Arabic light tree content');
+
+test(() => {
+ let host = document.createElement("div");
+ host.dir = "rtl";
+ document.body.appendChild(host);
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = '<section dir="ltr"><div dir="auto"><slot></slot>A</div></section>';
+ let div = root.querySelector("div");
+ assert_true(div.matches(":dir(rtl)"));
+ host.remove();
+}, 'slot provides its directionality (from host) to a dir=auto container');
+
+test(() => {
+ let host = document.createElement("div");
+ document.body.appendChild(host);
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = '<div dir="auto"><span dir="ltr">A</span>\u05D0</div><slot></slot>';
+ let div = root.querySelector("div");
+ assert_true(div.matches(":dir(rtl)"));
+ host.remove();
+}, 'children with dir attribute are skipped by dir=auto');
+
+test(() => {
+ let host = document.createElement("div");
+ document.body.appendChild(host);
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = '<div dir="auto"><slot dir="ltr"></slot>\u05D0</div>';
+ let div = root.querySelector("div");
+ assert_true(div.matches(":dir(rtl)"));
+ host.remove();
+}, 'slot with dir attribute is skipped by dir=auto');
+
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L-ref.html
new file mode 100644
index 0000000000..de6e13b3a3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with EN, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A since digits are not strongly
+ directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L.html
new file mode 100644
index 0000000000..fa8d793bd0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-L.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with EN, then L</title>
+ <link rel="match" href="dir_auto-EN-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A since digits are not strongly
+ directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R-ref.html
new file mode 100644
index 0000000000..15bd618dc1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with EN, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef since digits are not strongly
+ directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R.html
new file mode 100644
index 0000000000..7165de583d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-EN-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with EN, then R</title>
+ <link rel="match" href="dir_auto-EN-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef since digits are not strongly
+ directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L-ref.html
new file mode 100644
index 0000000000..23da64ed9b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="ltr">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L.html
new file mode 100644
index 0000000000..3896bcb76b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-L.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with L</title>
+ <link rel="match" href="dir_auto-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L-ref.html
new file mode 100644
index 0000000000..c7977d189f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then EN, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="ltr">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L.html
new file mode 100644
index 0000000000..21ca0338dc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-L.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then EN, then L</title>
+ <link rel="match" href="dir_auto-N-EN-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">.-=123ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R-ref.html
new file mode 100644
index 0000000000..aae50bc721
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then EN, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="rtl">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R.html
new file mode 100644
index 0000000000..b10a52b1a8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then EN, then R</title>
+ <link rel="match" href="dir_auto-N-EN-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-ref.html
new file mode 100644
index 0000000000..0d938b2e16
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN-ref.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then EN, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, ignoring neutrals and numbers.
+ If there is no strong character, as in this test, the direction defaults to the parent." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="ltr">@123!</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">@123!</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">@123!</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">@123!</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN.html
new file mode 100644
index 0000000000..467b4d0939
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-EN.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then EN, then L</title>
+ <link rel="match" href="dir_auto-N-EN-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, ignoring neutrals and numbers.
+ If there is no strong character, as in this test, the direction defaults to the parent." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">@123!</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">@123!</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">@123!</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">@123!</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L-ref.html
new file mode 100644
index 0000000000..4bbaca1e31
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A since neutrals are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="ltr">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L.html
new file mode 100644
index 0000000000..945fa06779
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-L.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then L</title>
+ <link rel="match" href="dir_auto-N-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Latin letter A since neutrals are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="ltr">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="ltr">.-=ABC&#x05D0;&#x05D1;&#x05D2;.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R-ref.html
new file mode 100644
index 0000000000..7a1daeddde
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef since neutrals are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="rtl">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R.html
new file mode 100644
index 0000000000..bf27a16a22
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-N-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with N, then R</title>
+ <link rel="match" href="dir_auto-N-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef since neutrals are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">.-=&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R-ref.html
new file mode 100644
index 0000000000..c9dc5301b4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="rtl">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R.html
new file mode 100644
index 0000000000..69a62fc637
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with R</title>
+ <link rel="match" href="dir_auto-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <p dir="auto">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="auto">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <p dir="rtl">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ <div dir="rtl">
+ <p dir="rtl">&#x05D0;&#x05D1;&#x05D2;ABC.</p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L-ref.html
new file mode 100644
index 0000000000..4e42a11a91
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L-ref.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with L within contained element</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, including text within contained elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ &#x05D6; - The Hebrew letter Zayin (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="ltr"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L.html
new file mode 100644
index 0000000000..f71f318bfd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-L.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with L within contained element</title>
+ <link rel="match" href="dir_auto-contained-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, including text within contained elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ &#x05D6; - The Hebrew letter Zayin (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><div><div>ABC&#x05D0;&#x05D1;&#x05D2;.</div>&#x05D3;&#x05D4;</div>&#x05D5;</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R-ref.html
new file mode 100644
index 0000000000..a3938bdf85
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with R within contained element</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, including text within contained elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="rtl"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R.html
new file mode 100644
index 0000000000..2ba63426e3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with R within contained element</title>
+ <link rel="match" href="dir_auto-contained-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, including text within contained elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><div><div>&#x05D0;&#x05D1;&#x05D2;ABC.</div>XY</div>Z</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L-ref.html
new file mode 100644
index 0000000000..470220c80d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with bdi, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring bdi elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="ltr"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L.html
new file mode 100644
index 0000000000..f35abfe3fd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-L.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with bdi, then L</title>
+ <link rel="match" href="dir_auto-contained-bdi-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring bdi elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><bdi>&#x05D3;&#x05D4;&#x05D5;</bdi>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R-ref.html
new file mode 100644
index 0000000000..94475aaa92
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with bdi, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring bdi elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="rtl"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R.html
new file mode 100644
index 0000000000..8ac3244618
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-bdi-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with bdi, then R</title>
+ <link rel="match" href="dir_auto-contained-bdi-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring bdi elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><bdi>DEF</bdi>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L-ref.html
new file mode 100644
index 0000000000..7c9f931d3d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L.html
new file mode 100644
index 0000000000..1f424682fa
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-L.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir, then L</title>
+ <link rel="match" href="dir_auto-contained-dir-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R-ref.html
new file mode 100644
index 0000000000..c6748dc85f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="rtl"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R.html
new file mode 100644
index 0000000000..daab191498
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir, then R</title>
+ <link rel="match" href="dir_auto-contained-dir-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><p dir="ltr">DEF</p>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L-ref.html
new file mode 100644
index 0000000000..53c60421f8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir=auto, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L.html
new file mode 100644
index 0000000000..f491f61658
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-L.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir=auto, then L</title>
+ <link rel="match" href="dir_auto-contained-dir_auto-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><p dir="auto">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><p dir="auto">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><p dir="rtl">&#x05D3;&#x05D4;&#x05D5;</p>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R-ref.html
new file mode 100644
index 0000000000..41871f04a1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir=auto, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="rtl"><p dir="ltr">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><p dir="ltr">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><p dir="ltr">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><p dir="ltr">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R.html
new file mode 100644
index 0000000000..e3131c89b3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-dir_auto-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with dir=auto, then R</title>
+ <link rel="match" href="dir_auto-contained-dir_auto-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text while ignoring contained elements with an explicit dir of their own.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><p dir="auto">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><p dir="auto">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><p dir="ltr">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><p dir="ltr">DEF</p>.-=123&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L-ref.html
new file mode 100644
index 0000000000..aca07de7ef
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with script, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant script elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="ltr"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L.html
new file mode 100644
index 0000000000..59a2e77751
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-L.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with script, then L</title>
+ <link rel="match" href="dir_auto-contained-script-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant script elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><script>&#x05D0; = 3;</script>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html
new file mode 100644
index 0000000000..aa27d2f45b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with script, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant script elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ <script>var x;</script>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="rtl"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R.html
new file mode 100644
index 0000000000..ee002766a0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-script-R.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with script, then R</title>
+ <link rel="match" href="dir_auto-contained-script-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant script elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ <script>var x;</script>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><script>x = 3;</script>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L-ref.html
new file mode 100644
index 0000000000..2ec4f02ec2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with style, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant style elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="ltr"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L.html
new file mode 100644
index 0000000000..cc74d4c939
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-L.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with style, then L</title>
+ <link rel="match" href="dir_auto-contained-style-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant style elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><style>body {color:black;}</style>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R-ref.html
new file mode 100644
index 0000000000..9ad9d7109a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with style, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant style elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="rtl"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R.html
new file mode 100644
index 0000000000..4aa70cdb2e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-style-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with style, then R</title>
+ <link rel="match" href="dir_auto-contained-style-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant style elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><style>body {color:black;}</style>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L-ref.html
new file mode 100644
index 0000000000..411099f7b4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with textarea, then L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant textarea elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="ltr"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L.html
new file mode 100644
index 0000000000..0de041fe01
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-L.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with textarea, then L</title>
+ <link rel="match" href="dir_auto-contained-textarea-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant textarea elements.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ &#x05D3; - The Hebrew letter Dalet (strongly RTL).
+ &#x05D4; - The Hebrew letter He (strongly RTL).
+ &#x05D5; - The Hebrew letter Vav (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="ltr"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="ltr"><textarea>&#x05D3;&#x05D4;&#x05D5;</textarea>ABC&#x05D0;&#x05D1;&#x05D2;.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R-ref.html
new file mode 100644
index 0000000000..351431fb2b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with textarea, then R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant textarea elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="rtl"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R.html
new file mode 100644
index 0000000000..852de6073c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-contained-textarea-R.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, start with textarea, then R</title>
+ <link rel="match" href="dir_auto-contained-textarea-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of descendant text while ignoring descendant textarea elements.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <div dir="auto"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="auto"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <div dir="rtl"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ <div dir="rtl">
+ <div dir="rtl"><textarea>DEF</textarea>&#x05D0;&#x05D1;&#x05D2;ABC.</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L-ref.html
new file mode 100644
index 0000000000..198d081c26
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with EN+L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since digits are not strongly
+ directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L.html
new file mode 100644
index 0000000000..d5ade6c096
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-L.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with EN+L</title>
+ <link rel="match" href="dir_auto-input-EN-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since digits are not strongly
+ directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R-ref.html
new file mode 100644
index 0000000000..303afc3c6b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with EN+R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since digits are not strongly
+ directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R.html
new file mode 100644
index 0000000000..08ef3ecd70
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-EN-R.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with EN+R</title>
+ <link rel="match" href="dir_auto-input-EN-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since digits are not strongly
+ directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L-ref.html
new file mode 100644
index 0000000000..cb3621aa61
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L.html
new file mode 100644
index 0000000000..0a23f2b86d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-L.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with L</title>
+ <link rel="match" href="dir_auto-input-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="ABC&#x05d0;&#x05d1;&#x05d2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L-ref.html
new file mode 100644
index 0000000000..3d0f2cf3cc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+EN+L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L.html
new file mode 100644
index 0000000000..03f85526da
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-L.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+EN+L</title>
+ <link rel="match" href="dir_auto-input-N-EN-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R-ref.html
new file mode 100644
index 0000000000..26bf27d619
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+EN+R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R.html
new file mode 100644
index 0000000000..13193d3d72
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-R.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+EN+R</title>
+ <link rel="match" href="dir_auto-input-N-EN-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-ref.html
new file mode 100644
index 0000000000..33f75b730b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, all N+EN</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value, or to LTR if there is no such
+ character.
+ In this test, there is no strongly directional character in the value,
+ thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN.html
new file mode 100644
index 0000000000..03df3c6dcc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-EN.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, all N+EN</title>
+ <link rel="match" href="dir_auto-input-N-EN-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value, or to LTR if there is no such
+ character.
+ In this test, there is no strongly directional character in the value,
+ thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value="@123!" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="@123!" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L-ref.html
new file mode 100644
index 0000000000..b6a89a1d72
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L.html
new file mode 100644
index 0000000000..9c1d3bceec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-L.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+L</title>
+ <link rel="match" href="dir_auto-input-N-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R-ref.html
new file mode 100644
index 0000000000..bcd5430441
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R.html
new file mode 100644
index 0000000000..dbf54f7344
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-N-R.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with N+R</title>
+ <link rel="match" href="dir_auto-input-N-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R-ref.html
new file mode 100644
index 0000000000..217972e82d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R.html
new file mode 100644
index 0000000000..6d2612b316
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-R.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, start with R</title>
+ <link rel="match" href="dir_auto-input-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="auto" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L-ref.html
new file mode 100644
index 0000000000..879e20d6cf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with EN+L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since digits are not strongly
+ directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L.html
new file mode 100644
index 0000000000..d0a9e2bb9b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-L.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with EN+L</title>
+ <link rel="match" href="dir_auto-input-script-EN-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since digits are not strongly
+ directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '123ABC\u05D0\u05D1\u05D2.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R-ref.html
new file mode 100644
index 0000000000..15781e2524
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with EN+R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since digits are not strongly
+ directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R.html
new file mode 100644
index 0000000000..e444b90dc2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-EN-R.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with EN+R</title>
+ <link rel="match" href="dir_auto-input-script-EN-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since digits are not strongly
+ directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '123\u05D0\u05D1\u05D2ABC.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L-ref.html
new file mode 100644
index 0000000000..0feef25047
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L.html
new file mode 100644
index 0000000000..e6aa700ad0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-L.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with L</title>
+ <link rel="match" href="dir_auto-input-script-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A, thus the direction must be
+ resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = 'ABC\u05D0\u05D1\u05D2.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L-ref.html
new file mode 100644
index 0000000000..6d6902f314
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+EN+L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L.html
new file mode 100644
index 0000000000..7905cee946
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-L.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+EN+L</title>
+ <link rel="match" href="dir_auto-input-script-N-EN-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '.-=123ABC\u05D0\u05D1\u05D2.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=123ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R-ref.html
new file mode 100644
index 0000000000..53dd892096
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+EN+R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R.html
new file mode 100644
index 0000000000..95faa72ea7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-R.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+EN+R</title>
+ <link rel="match" href="dir_auto-input-script-N-EN-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals and digits are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '.-=123\u05D0\u05D1\u05D2ABC.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=123&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-ref.html
new file mode 100644
index 0000000000..11697e53eb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to all N+EN</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value, or to LTR if there is no such
+ character.
+ In this test, there is no strongly directional character in the value,
+ thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN.html
new file mode 100644
index 0000000000..2721affaef
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-EN.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to all N+EN</title>
+ <link rel="match" href="dir_auto-input-script-N-EN-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value, or to LTR if there is no such
+ character.
+ In this test, there is no strongly directional character in the value,
+ thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '@123!';
+ }
+ };
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value="@123!" />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L-ref.html
new file mode 100644
index 0000000000..fd7cb10fbd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+L</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L.html
new file mode 100644
index 0000000000..2ff24db28c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-L.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+L</title>
+ <link rel="match" href="dir_auto-input-script-N-L-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Latin letter A since neutrals are not
+ strongly directional, thus the direction must be resolved as LTR." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '.-=ABC\u05D0\u05D1\u05D2.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="&#x05D0;" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="ltr" value=".-=ABC&#x05D0;&#x05D1;&#x05D2;." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R-ref.html
new file mode 100644
index 0000000000..0fa6da249a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R.html
new file mode 100644
index 0000000000..0663b28ad2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-N-R.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with N+R</title>
+ <link rel="match" href="dir_auto-input-script-N-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef since neutrals are not
+ strongly directional, thus the direction must be resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '.-=\u05D0\u05D1\u05D2ABC.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value=".-=&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R-ref.html
new file mode 100644
index 0000000000..12b2d1925f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with R</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R.html
new file mode 100644
index 0000000000..07becaaccd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-input-script-R.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: input with dir=auto, script assigns to start with R</title>
+ <link rel="match" href="dir_auto-input-script-R-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction of an input element is set according to
+ the first strong character of its value.
+ In this test, it is the Hebrew letter Alef, thus the direction must be
+ resolved as RTL." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x05D1; - The Hebrew letter Bet (strongly RTL).
+ &#x05D2; - The Hebrew letter Gimel (strongly RTL).
+ This test makes sure that the direction is set correctly for an input whose value is set
+ dynamically by script.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var inputs = test.getElementsByTagName('input');
+ for (var i = 0; i != inputs.length; i++) {
+ inputs[i].value = '\u05D0\u05D1\u05D2ABC.';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="auto" value="a" />
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ <div dir="rtl">
+ <input type="text" dir="rtl" value="&#x05D0;&#x05D1;&#x05D2;ABC." />
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate-ref.html
new file mode 100644
index 0000000000..858a7db233
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, isolated in LTR text</title>
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, but the element behaves externally as a neutral character.
+ In this test, it allows a preceding R to form a single directional run
+ with a succeeding number." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ &#x202D;1 a! &#x05D0;&#x202C;
+ </div>
+ <div dir="rtl">
+ &#x202D;a !&#x05D0; 1&#x202C;
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ &#x202D;1 a! &#x05D0;&#x202C;
+ </div>
+ <div dir="rtl">
+ &#x202D;a !&#x05D0; 1&#x202C;
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate.html
new file mode 100644
index 0000000000..14272c0531
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-isolate.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: dir=auto, isolated in LTR text</title>
+ <link rel="match" href="dir_auto-isolate-ref.html" />
+ <link rel="author" title="Matitiahu Allouche" href="mailto:matitiahu.allouche@google.com" />
+ <link rel="author" title="Oren Roth" href="mailto:oren.roth@gmail.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text, but the element behaves externally as a neutral character.
+ In this test, it allows a preceding R to form a single directional run
+ with a succeeding number." />
+ <style>
+ input, textarea {
+ font-size:1em;
+ }
+ body {
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ &#x05D0; <span dir="auto">a!</span> 1
+ </div>
+ <div dir="rtl">
+ a <span dir="auto">&#x05D0;!</span> 1
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ &#x202D;1 a! &#x05D0;&#x202C;
+ </div>
+ <div dir="rtl">
+ &#x202D;a !&#x05D0; 1&#x202C;
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN-ref.html
new file mode 100644
index 0000000000..c951c30b20
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN-ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <pre dir="ltr">
+@123!
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="ltr">
+@123!
+ </pre>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <pre dir="ltr">
+@123!
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="ltr">
+@123!
+ </pre>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN.html
new file mode 100644
index 0000000000..cd721d725e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-EN.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: pre with dir=auto, all N+EN</title>
+ <link rel="match" href="dir_auto-pre-N-EN-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level.
+ If there is no strong character, as in this test, the direction defaults to LTR." />
+ <style>
+ body {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x200E; - LRM, the invisible left-to-right mark (strongly LTR).
+ &#x200F; - RLM, the invisible right-to-left mark (strongly RTL).
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <pre dir="auto">
+@123!
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="auto">
+@123!
+ </pre>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <pre dir="ltr">
+@123!
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="ltr">
+@123!
+ </pre>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs-ref.html
new file mode 100644
index 0000000000..2d9caf062d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs-ref.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <pre dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </pre>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <pre dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </pre>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs.html
new file mode 100644
index 0000000000..adca24d88f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-N-between-Rs.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: pre with dir=auto, all-N between all-Rs</title>
+ <link rel="match" href="dir_auto-pre-N-between-Rs-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level.
+ If there is no strong character, as in this test, the direction defaults to LTR." />
+ <style>
+ body {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ The ...! paragraph, being neutral, is supposed to be displayed LTR (i.e. as ...!, not as !...)
+ despite both the paragraph before it and the paragraph after it being all-RTL, which makes the
+ element as a whole RTL.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <pre dir="auto">
+&#x05D0;
+...!
+&#x05D0;
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="auto">
+&#x05D0;
+...!
+&#x05D0;
+ </pre>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <pre dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </pre>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed-ref.html
new file mode 100644
index 0000000000..10bd02433b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed-ref.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <pre dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </pre>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <pre dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </pre>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed.html
new file mode 100644
index 0000000000..906365621d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-pre-mixed.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: pre with dir=auto, mixed L and R paragraphs</title>
+ <link rel="match" href="dir_auto-pre-mixed-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level." />
+ <style>
+ body {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x200E; - LRM, the invisible left-to-right mark (strongly LTR).
+ &#x200F; - RLM, the invisible right-to-left mark (strongly RTL).
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <pre dir="auto">
+@&#x200E;123&#x200F;!
+!&#x200F;123&#x200E;@
+@123&#x200E;&#x200F;!
+!123&#x200F;&#x200E;@
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="auto">
+@&#x200E;123&#x200F;!
+!&#x200F;123&#x200E;@
+@123&#x200E;&#x200F;!
+!123&#x200F;&#x200E;@
+ </pre>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <pre dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </pre>
+ </div>
+ <div dir="rtl">
+ <pre dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </pre>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN-ref.html
new file mode 100644
index 0000000000..253b84459e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN-ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN.html
new file mode 100644
index 0000000000..f0fa2161a1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-EN.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: textarea with dir=auto, all N+EN</title>
+ <link rel="match" href="dir_auto-textarea-N-EN-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level.
+ If there is no strong character, as in this test, the direction defaults to LTR." />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x200E; - LRM, the invisible left-to-right mark (strongly LTR).
+ &#x200F; - RLM, the invisible right-to-left mark (strongly RTL).
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="2" dir="auto">
+@123!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="auto">
+@123!
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs-ref.html
new file mode 100644
index 0000000000..afeef08cbf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs-ref.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs.html
new file mode 100644
index 0000000000..b5850e1c3c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: textarea with dir=auto, all-N between all-Rs</title>
+ <link rel="match" href="dir_auto-textarea-N-between-Rs-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level.
+ If there is no strong character, the direction defaults to LTR." />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ The ...! paragraph, being neutral, is supposed to be displayed LTR (i.e. as ...!, not as !...)
+ despite both the paragraph before it and the paragraph after it being all-RTL, which makes the
+ element as a whole RTL.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="4" dir="auto">
+&#x05D0;
+...!
+&#x05D0;
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="auto">
+&#x05D0;
+...!
+&#x05D0;
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed-ref.html
new file mode 100644
index 0000000000..a5a84480f6
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed-ref.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="5" dir="rtl">
+!123@
+!123@
+!123@
+!123@
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="5" dir="rtl">
+!123@
+!123@
+!123@
+!123@
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed.html
new file mode 100644
index 0000000000..4947124c99
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-mixed.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: textarea with dir=auto, mixed L and R paragraphs</title>
+ <link rel="match" href="dir_auto-textarea-mixed-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level." />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x200E; - LRM, the invisible left-to-right mark (strongly LTR).
+ &#x200F; - RLM, the invisible right-to-left mark (strongly RTL).
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="5" dir="auto">
+!&#x200F;123&#x200E;@
+@&#x200E;123&#x200F;!
+!123&#x200F;&#x200E;@
+@123&#x200E;&#x200F;!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="auto">
+@&#x200E;123&#x200F;!
+!&#x200F;123&#x200E;@
+@123&#x200E;&#x200F;!
+!123&#x200F;&#x200E;@
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="5" dir="rtl">
+!123@
+!123@
+!123@
+!123@
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN-ref.html
new file mode 100644
index 0000000000..253b84459e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN-ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN.html
new file mode 100644
index 0000000000..3c674e2f82
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-EN.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: textarea with dir=auto, script assigns to all N+EN</title>
+ <link rel="match" href="dir_auto-textarea-script-N-EN-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level.
+ If there is no strong character, as in this test, the direction defaults to LTR." />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x200E; - LRM, the invisible left-to-right mark (strongly LTR).
+ &#x200F; - RLM, the invisible right-to-left mark (strongly RTL).
+ This test makes sure that the direction is set correctly for a textarea whose value is set
+ dynamically by script.
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var textareas = test.getElementsByTagName('textarea');
+ for (var i = 0; i != textareas.length; i++) {
+ textareas[i].value = '@123!\n';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <textarea rows="2" dir="auto">
+&#x200F;
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="auto">
+&#x200F;
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="2" dir="ltr">
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs-ref.html
new file mode 100644
index 0000000000..e523313252
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;</textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;</textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;</textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;</textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs.html
new file mode 100644
index 0000000000..f5e53667e5
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: textarea with dir=auto, script assigns to all-N between all-Rs</title>
+ <link rel="match" href="dir_auto-textarea-script-N-between-Rs-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level.
+ If there is no strong character, as in this test, the direction defaults to LTR." />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; - The Hebrew letter Alef (strongly RTL).
+ This test makes sure that the direction is set correctly for a textarea whose value is set
+ dynamically by script.
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ The ...! paragraph, being neutral, is supposed to be displayed LTR (i.e. as ...!, not as !...)
+ despite both the paragraph before it and the paragraph after it being all-RTL, which makes the
+ element as a whole RTL.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var textareas = test.getElementsByTagName('textarea');
+ for (var i = 0; i != textareas.length; i++) {
+ textareas[i].value = '\u05D0\n...!\n\u05D0';
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <textarea rows="4" dir="auto">
+LTR text
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="auto">
+LTR text
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;</textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="4" dir="rtl">
+&#x05D0;
+!...
+&#x05D0;</textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed-ref.html
new file mode 100644
index 0000000000..a5a84480f6
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed-ref.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="test">
+ <div dir="ltr">
+ <textarea rows="5" dir="rtl">
+!123@
+!123@
+!123@
+!123@
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="5" dir="rtl">
+!123@
+!123@
+!123@
+!123@
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed.html b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed.html
new file mode 100644
index 0000000000..f0c6d4fe44
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir_auto-textarea-script-mixed.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: textarea with dir=auto, script assigns to mixed L and R paragraphs</title>
+ <link rel="match" href="dir_auto-textarea-script-mixed-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dir-attribute" />
+ <link rel="help" href="http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi0" />
+ <meta name="assert" content="
+ When dir='auto', the direction is set according to the first strong character
+ of the text.
+ For textarea and pre elements, the heuristic is applied on a per-paragraph level." />
+ <style>
+ body, textarea {
+ font-size:18px;
+ text-align:left;
+ }
+ textarea {
+ resize: none;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x200E; - LRM, the invisible left-to-right mark (strongly LTR).
+ &#x200F; - RLM, the invisible right-to-left mark (strongly RTL).
+ This test makes sure that the direction is set correctly for a textarea whose value is set
+ dynamically by script.
+ We use text-align:left because neither the dir="auto" nor the unicode-bidi:plaintext
+ specification states whether text-align:start and text-align:end should obey the paragraph
+ direction or the direction property in a unicode-bidi:plaintext element.
+ </div>
+ <div id="test" class="test">
+ <script>
+ window.onload = function() {
+ var test = document.getElementById('test');
+ var textareas = test.getElementsByTagName('textarea');
+ for (var i = 0; i != textareas.length; i++) {
+ var input = textareas[i];
+ if (input.parentNode.dir == 'ltr') {
+ // Assign a value whose first strong is RTL.
+ input.value =
+ '!\u200F123\u200E@\n' +
+ '@\u200E123\u200F!\n' +
+ '!123\u200F\u200E@\n' +
+ '@123\u200E\u200F!\n';
+ } else {
+ // Assign a value whose first strong is LTR.
+ input.value =
+ '@\u200E123\u200F!\n' +
+ '!\u200F123\u200E@\n' +
+ '@123\u200E\u200F!\n' +
+ '!123\u200F\u200E@\n';
+ }
+ }
+ }
+ </script>
+ <div dir="ltr">
+ <textarea rows="5" dir="auto">
+&#x200E;
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="auto">
+&#x200F;
+ </textarea>
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ <textarea rows="5" dir="rtl">
+!123@
+!123@
+!123@
+!123@
+ </textarea>
+ </div>
+ <div dir="rtl">
+ <textarea rows="5" dir="ltr">
+@123!
+@123!
+@123!
+@123!
+ </textarea>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/document-dir.html b/testing/web-platform/tests/html/dom/elements/global-attributes/document-dir.html
new file mode 100644
index 0000000000..675b4bc9d9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/document-dir.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html dir="LTR">
+<title>document.dir</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-dir">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#reflect">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.dir, "ltr");
+ assert_equals(document.documentElement.getAttribute("dir"), "LTR");
+}, "Markup attribute")
+test(function() {
+ document.dir = "x-garbage";
+ assert_equals(document.dir, "");
+ assert_equals(document.documentElement.getAttribute("dir"), "x-garbage");
+}, "Setting the idl attribute to a garbage value")
+test(function() {
+ document.dir = "";
+ assert_true(document.documentElement.hasAttribute("dir"), "Attribute should still be around");
+ assert_equals(document.dir, "");
+ assert_equals(document.documentElement.getAttribute("dir"), "");
+}, "Setting the idl attribute to the empty string")
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/id-attribute.html b/testing/web-platform/tests/html/dom/elements/global-attributes/id-attribute.html
new file mode 100644
index 0000000000..660a7274a3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/id-attribute.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>The id attribute</title>
+<meta charset=utf8>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-id-attribute">
+<style>
+
+#abcd {
+ position: absolute;
+ z-index: 1;
+}
+
+#ABCD {
+ position: absolute;
+ z-index: 2;
+}
+
+#a\ b {
+ position: absolute;
+ z-index: 3;
+}
+
+#xyz {
+ position: absolute;
+ z-index: 4;
+}
+
+#foobar {
+ position: absolute;
+ z-index: 5;
+}
+
+#åèiöú {
+ position: absolute;
+ z-index: 6;
+}
+
+</style>
+</head>
+<body>
+<h1>The id attribute</h1>
+<div id="log"></div>
+<i id="abcd"></i>
+<i id="ABCD"></i>
+<i id="a b"></i>
+<i id="åèiöú"></i>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ // id is associated for purposes of getElementById
+ test(function() {
+ assert_equals(document.getElementById("abcd"), document.getElementsByTagName("i")[0]);
+ }, "User agents must associate the element with an id value for purposes of getElementById.");
+
+ test(function() {
+ assert_equals(document.getElementById("ABCD"), document.getElementsByTagName("i")[1]);
+ }, "Association is exact and therefore case-sensitive for getElementById.");
+
+ test(function() {
+ assert_equals(document.getElementById("a b"), document.getElementsByTagName("i")[2]);
+ }, "Spaces are allowed in an id and still make an association for getElementByID.");
+
+ test(function() {
+ assert_equals(document.getElementById("åèiöú"), document.getElementsByTagName("i")[3]);
+ }, "Non-ASCII is allowed in an id and still make an association for getElementById.");
+
+
+ // id is associated for purposes of CSS
+ test(function() {
+ assert_equals(document.defaultView.getComputedStyle(document.getElementById("abcd")).zIndex, "1");
+ }, "User agents must associate the element with an id value for purposes of CSS.");
+
+ test(function() {
+ assert_equals(document.defaultView.getComputedStyle(document.getElementById("ABCD")).zIndex, "2");
+ }, "Association for CSS is exact and therefore case-sensitive.");
+
+ test(function() {
+ assert_equals(document.defaultView.getComputedStyle(document.getElementById("a b")).zIndex, "3");
+ }, "Spaces are allowed in an id and still make an association.");
+
+ test(function() {
+ assert_equals(document.defaultView.getComputedStyle(document.getElementById("åèiöú")).zIndex, "6");
+ }, "Non-ASCII is allowed in an id and still make an association for CSS.");
+
+
+ // id IDL attribute reflects the content attribute
+ var firstSpan = document.getElementById("abcd");
+
+ test(function() {
+ assert_equals(firstSpan.id, "abcd");
+ }, "The id IDL attribute must reflect the id content attribute, for getting.");
+
+ test(function() {
+ firstSpan.id = "xyz";
+ assert_equals(firstSpan.getAttribute("id"), "xyz");
+ }, "The id IDL attribute must reflect the id content attribute, for setting via IDL attribute.");
+
+ test(function() {
+ assert_equals(document.getElementById("xyz"), firstSpan);
+ }, "After setting id via id attribute, getElementById find the element by the new id.");
+
+ test(function() {
+ assert_equals(document.getElementById("abcd"), null);
+ }, "After setting id via id attribute, getElementById doesn't find the element by the old id.");
+
+ test(function() {
+ assert_equals(document.defaultView.getComputedStyle(firstSpan).zIndex, "4");
+ }, "After setting id via id attribute, CSS association is via the new ID.");
+
+ test(function() {
+ firstSpan.setAttribute("id", "foobar");
+ assert_equals(firstSpan.id, "foobar");
+ }, "The id IDL attribute must reflect the id content attribute, for setting via setAttribute.");
+
+ test(function() {
+ assert_equals(document.getElementById("foobar"), firstSpan);
+ }, "After setting id via setAttribute attribute, getElementById find the element by the new id.");
+
+ test(function() {
+ assert_equals(document.getElementById("xyz"), null);
+ }, "After setting id via setAttribute attribute, getElementById doesn't find the element by the old id.");
+
+ test(function() {
+ assert_equals(document.defaultView.getComputedStyle(firstSpan).zIndex, "5");
+ }, "After setting id via setAttribute attribute, CSS association is via the new ID.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/id-name-specialcase.html b/testing/web-platform/tests/html/dom/elements/global-attributes/id-name-specialcase.html
new file mode 100644
index 0000000000..77e4100b70
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/id-name-specialcase.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>HTML5: test id with none pure alpha characters </title>
+<link rel="author" title="justin.shen" href=mailto:cosmichut@msn.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div style="display:none">
+ <input id="123" value="123"></input>
+ <input id="1test" value="1test"></input>
+ <input id="_test" value="_test"></input>
+ <input id="." value="."></input>
+ <input id="中国" value="china"></input>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementById("123").value, "123");
+}, "id with digits only");
+test(function() {
+ assert_equals(document.getElementById("1test").value, "1test");
+},"id start with digits");
+test(function() {
+ assert_equals(document.getElementById("_test").value, "_test");
+},"id start with underscore");
+test(function() {
+ assert_equals(document.getElementById(".").value, ".");
+},"id with punctuation only");
+test(function() {
+ assert_equals(document.getElementById("中国").value, "china");
+},"id with chinese character");
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/id-name.html b/testing/web-platform/tests/html/dom/elements/global-attributes/id-name.html
new file mode 100644
index 0000000000..7fdac993b2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/id-name.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>id and name attributes and getElementById</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-id-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<div name="abcd"></div>
+<p name="abcd" id="abcd"></p>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementById("abcd").nodeName, "P");
+ assert_equals(document.getElementById("abcd").localName, "p");
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute-shadow.window.js b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute-shadow.window.js
new file mode 100644
index 0000000000..2a59a56c05
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute-shadow.window.js
@@ -0,0 +1,81 @@
+const TESTS = [
+ {
+ title: "lang only on slot",
+ light_tree: `
+ <div id="host" data-expected="en-US"><span data-expected="en-US"></span></div>
+ `,
+ shadow_tree: `
+ <slot lang="en-AU" data-expected="en-AU"></slot>
+ `,
+ },
+ {
+ title: "lang only on host",
+ light_tree: `
+ <div id="host" lang="en-AU" data-expected="en-AU"><span data-expected="en-AU"></span></div>
+ `,
+ shadow_tree: `
+ <slot data-expected="en-AU"></slot>
+ `,
+ },
+ {
+ title: "lang on host and slot",
+ light_tree: `
+ <div id="host" lang="en-AU" data-expected="en-AU"><span data-expected="en-AU"></span></div>
+ `,
+ shadow_tree: `
+ <slot lang="en-GB" data-expected="en-GB"></slot>
+ `,
+ },
+ {
+ title: "lang on host and slotted element",
+ light_tree: `
+ <div id="host" lang="en-AU" data-expected="en-AU"><span lang="en-GB" data-expected="en-GB"></span></div>
+ `,
+ shadow_tree: `
+ <slot data-expected="en-AU"></slot>
+ `,
+ },
+ {
+ title: "lang on host and slot and slotted element",
+ light_tree: `
+ <div id="host" lang="en-AU" data-expected="en-AU"><span lang="en-GB" data-expected="en-GB"></span></div>
+ `,
+ shadow_tree: `
+ <slot lang="en-NZ" data-expected="en-NZ"></slot>
+ `,
+ },
+ {
+ title: "lang on slot inherits from parent",
+ light_tree: `
+ <div id="host" lang="en-GB" data-expected="en-GB"><span lang="en-US" data-expected="en-US"></span></div>
+ `,
+ shadow_tree: `
+ <div lang="en-CA" data-expected="en-CA">
+ <slot data-expected="en-CA"></slot>
+ </div>
+ `,
+ },
+];
+
+const container = document.createElement("div");
+document.body.append(container);
+container.lang = "en-US";
+
+for (const obj of TESTS) {
+ test(() => {
+ container.innerHTML = obj.light_tree;
+ let shadow = container.querySelector("#host").attachShadow({mode: "open"});
+ shadow.innerHTML = obj.shadow_tree;
+ for (const element of Array.from(container.querySelectorAll("[data-expected]")).concat(Array.from(shadow.querySelectorAll("[data-expected]")))) {
+ const expected = element.getAttribute("data-expected");
+ assert_true(element.matches(`:lang(${expected})`), `element matches expected language ${expected}`);
+ for (const other_lang of ["en-US", "en-AU", "en-GB", "en-NZ", "en-CA"]) {
+ if (expected != other_lang) {
+ assert_false(element.matches(`:lang(${other_lang})`), `element does not match language ${other_lang}`);
+ }
+ }
+ }
+ }, obj.title);
+}
+
+container.remove();
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute.window.js b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute.window.js
new file mode 100644
index 0000000000..de0c03e6f8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-attribute.window.js
@@ -0,0 +1,16 @@
+test(() => {
+ const container = document.createElement("div");
+ document.body.append(container);
+ container.setAttribute("lang", "en-CA");
+
+ const child = document.createElementNS("div", "test");
+ container.append(child);
+ child.setAttribute("lang", "en-NZ");
+
+ assert_true(container.matches(":lang(en-CA)"), "container matches en-CA");
+ assert_true(child.matches(":lang(en-CA)"), "child matches en-CA");
+ assert_false(container.matches(":lang(en-NZ)"), "container does not match en-NZ");
+ assert_false(child.matches(":lang(en-NZ)"), "child does not match en-NZ");
+
+ container.remove();
+}, "unnamespaced lang attribute only works on elements in the HTML namespace");
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01-ref.html
new file mode 100644
index 0000000000..eacf73da16
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Languages</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes">
+<link rel="help" href="http://www.w3.org/TR/CSS2/selector.html#lang">
+<meta name="flags" content="css21">
+<style>
+#test > * { background: limegreen; }
+</style>
+<body>
+<p>All lines below should have a green background.</p>
+<div id="test">
+<div><p>{}{lang}{en}</p></div>
+<div><p>{}{xml:lang}{en}</p></div>
+<div><div><p>Parent: {}{lang}{en}</p></div></div>
+<div><div><p>Parent: {}{xml:lang}{en}</p></div></div>
+<div><p>{xml}{lang}{en}</p></div>
+<div><p>{xml}{lang}{en} - {lang}{de}</p></div>
+<div><p>{xml}{lang}{de} - {lang}{en}</p></div>
+</div>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01.html b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01.html
new file mode 100644
index 0000000000..04d8b74e2d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xmllang-01.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>Languages</title>
+<link rel="match" href="lang-xmllang-01-ref.html">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes">
+<link rel="help" href="http://www.w3.org/TR/CSS2/selector.html#lang">
+<meta name="flags" content="css21">
+<style>
+#test #a :lang(en) { background: limegreen; }
+#test #b :lang(nl) { background: limegreen; }
+#test #c :lang(en) { background: limegreen; }
+#test #d :lang(nl) { background: limegreen; }
+#test #e :lang(en) { background: limegreen; }
+#test #f :lang(en) { background: limegreen; }
+#test #g :lang(de) { background: limegreen; }
+</style>
+<body>
+<p>All lines below should have a green background.</p>
+<div id="test" lang="nl">
+<div id="a"><p lang="en">{}{lang}{en}</p></div>
+<div id="b"><p xml:lang="en">{}{xml:lang}{en}</p></div>
+<div id="c"><div lang="en"><p>Parent: {}{lang}{en}</p></div></div>
+<div id="d"><div xml:lang="en"><p>Parent: {}{xml:lang}{en}</p></div></div>
+</div>
+<script>
+try {
+ var XML = "http://www.w3.org/XML/1998/namespace";
+ var container = document.getElementById("test");
+
+ var div = document.createElement("div");
+ div.id = "e";
+ var testNode = document.createElement("p");
+ testNode.appendChild(document.createTextNode("{xml}{lang}{en}"));
+ testNode.setAttributeNS(XML, "xml:lang", "en");
+ div.appendChild(testNode);
+ container.appendChild(div);
+
+ div = document.createElement("div");
+ div.id = "f";
+ testNode = document.createElement("p");
+ testNode.appendChild(document.createTextNode("{xml}{lang}{en} - {lang}{de}"));
+ testNode.setAttributeNS(XML, "xml:lang", "en");
+ testNode.setAttributeNS(null, "lang", "de");
+ div.appendChild(testNode);
+ container.appendChild(div);
+
+ div = document.createElement("div");
+ div.id = "g";
+ testNode = document.createElement("p");
+ testNode.appendChild(document.createTextNode("{xml}{lang}{de} - {lang}{en}"));
+ testNode.setAttributeNS(XML, "xml:lang", "de");
+ testNode.setAttributeNS(null, "lang", "en");
+ container.appendChild(testNode);
+ div.appendChild(testNode);
+ container.appendChild(div);
+} catch (e) {
+}
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy-ref.html
new file mode 100644
index 0000000000..b2037182b9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Invalid languages</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<meta name="flags" content="css21">
+<style>#testp { color: green; }</style>
+<body>
+<div id="test">
+<p id="testp" lang="xyzzy">ABC</p>
+</div>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy.html b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy.html
new file mode 100644
index 0000000000..b950e1fac9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/lang-xyzzy.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Invalid languages</title>
+<link rel="match" href="lang-xyzzy-ref.html">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes">
+<link rel="help" href="http://www.w3.org/TR/CSS2/selector.html#lang">
+<meta name="flags" content="css21">
+<style>:lang(xyzzy) { color: green; }</style>
+<body>
+<div id="test">
+<p id="testp" lang="xyzzy">ABC</p>
+</div>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/mapped-attribute-adopt-001.html b/testing/web-platform/tests/html/dom/elements/global-attributes/mapped-attribute-adopt-001.html
new file mode 100644
index 0000000000..66ff3d64f1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/mapped-attribute-adopt-001.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>Adoption doesn't mess with mapped attributes</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1636516">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div hidden="unlikely">Should be hidden</div>
+<script>
+test(function() {
+ var tmpl = document.createElement("template");
+ var fragment = tmpl.content;
+ var newEl = document.createElement("div");
+ newEl.setAttribute("hidden", "unlikely");
+ fragment.append(newEl);
+ document.adoptNode(newEl);
+ assert_equals(
+ getComputedStyle(document.querySelector("div")).display,
+ "none",
+ "hidden attribute should have an effect"
+ );
+}, "Adoption of an unrelated node shouldn't prevent mapped attributes from applying");
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/style-01-ref.html b/testing/web-platform/tests/html/dom/elements/global-attributes/style-01-ref.html
new file mode 100644
index 0000000000..be8175e61d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/style-01-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>The style attribute</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-attribute">
+<link rel="help" href="http://www.w3.org/TR/css-style-attr/#syntax">
+<link rel="help" href="http://www.w3.org/TR/CSS21/cascade.html#cascading-order">
+<link rel="help" href="http://www.w3.org/TR/CSS21/cascade.html#specificity">
+<style>
+#test p { background: limegreen; }
+</style>
+<div id="test">
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+<p>This line should have a green background.
+</div>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/style-01.html b/testing/web-platform/tests/html/dom/elements/global-attributes/style-01.html
new file mode 100644
index 0000000000..c0e0995806
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/style-01.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>The style attribute</title>
+<link rel="match" href="style-01-ref.html">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-attribute">
+<link rel="help" href="http://www.w3.org/TR/css-style-attr/#syntax">
+<link rel="help" href="http://www.w3.org/TR/CSS21/cascade.html#cascading-order">
+<link rel="help" href="http://www.w3.org/TR/CSS21/cascade.html#specificity">
+<style>
+#idsel { background: red; }
+#idsel2 { background: limegreen !important; }
+</style>
+<div id="test">
+<p style="background:limegreen">This line should have a green background.
+<p style="/**/background:limegreen">This line should have a green background.
+<p style="background/**/:limegreen">This line should have a green background.
+<p style="background:/**/limegreen">This line should have a green background.
+<p style="background:limegreen/**/">This line should have a green background.
+<p id="idsel1" style="background:limegreen">This line should have a green background.
+<p id="idsel2" style="background:red">This line should have a green background.
+<p style="background:limegreen; background:r/**/ed">This line should have a green background.
+<p style="background:limegreen;}">This line should have a green background.
+<p style="};background:limegreen">This line should have a green background.
+<p style="background:red;};background:limegreen">This line should have a green background.
+<p style="background:limegreen;{background:red}">This line should have a green background.
+</div>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-001.tentative.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-001.tentative.html
new file mode 100644
index 0000000000..c9faa6908a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-001.tentative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/whatwg/html/pull/9144">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+#anchor {
+ width: 100px;
+ height: 100px;
+ margin-left: 50px;
+ margin-top: 50px;
+ background: orange;
+}
+#target {
+ position: absolute;
+ left: anchor(right);
+ top: anchor(top);
+ width: 100px;
+ height: 100px;
+ background: lime;
+}
+</style>
+<div id="anchor"></div>
+<div id="target" anchor="anchor"></div>
+
+<script>
+test(() => {
+ assert_equals(target.offsetLeft, 150);
+ assert_equals(target.offsetTop, 50);
+}, 'The anchor attribute should position the target element next to its implicit anchor');
+
+test(() => {
+ assert_equals(target.anchorElement, anchor);
+}, 'The element.anchorElement IDL should reflect the element pointed to by the anchor attribute');
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-002.tentative.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-002.tentative.html
new file mode 100644
index 0000000000..a1a237a2ee
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-002.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/whatwg/html/pull/9144">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+.anchor {
+ width: 100px;
+ height: 100px;
+ margin-left: 50px;
+ margin-top: 50px;
+ background: orange;
+}
+.target {
+ position: absolute;
+ left: anchor(right, 123px);
+ top: anchor(top, 456px);
+ width: 100px;
+ height: 100px;
+ background: lime;
+}
+</style>
+<div class="anchor" id="anchor1"></div>
+<div class="anchor" id="anchor2"></div>
+<div class="target" id="target1" anchor="anchor1"></div>
+<div class="target" id="target2" anchor="anchor1"></div>
+
+<script>
+test(() => {
+ document.body.offsetLeft; // Force layout
+ target1.setAttribute('anchor', 'anchor2');
+ assert_equals(target1.offsetLeft, 150);
+ assert_equals(target1.offsetTop, 200);
+
+ target1.setAttribute('anchor', 'anchor1');
+ assert_equals(target1.offsetLeft, 150);
+ assert_equals(target1.offsetTop, 50);
+}, 'Layout should be updated when anchor attribute changes to another element');
+
+test(() => {
+ document.body.offsetLeft; // Force layout
+ target2.setAttribute('anchor', 'nonexist-anchor');
+ assert_equals(target2.offsetLeft, 123);
+ assert_equals(target2.offsetTop, 456);
+}, 'Layout should be updated when anchor attribute changes to a non-existent element');
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003-crash.tentative.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003-crash.tentative.html
new file mode 100644
index 0000000000..207275d9db
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003-crash.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Tests that using the `anchor` attribute with inline containing block does not crash</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/9144">
+<link rel="help" href="https://crbug.com/1486148">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+
+<style>
+.target {
+ position: absolute;
+ top: anchor(top);
+ left: anchor(right);
+}
+</style>
+
+<div id="anchor">foo</div>
+
+<span style="position: relative">
+ <div anchor="anchor2" class="target">bar</div>
+</span>
+
+<span style="position: sticky">
+ <div anchor="anchor2" class="target">bar</div>
+</span>
+
+<span style="backdrop-filter: blur(1px)">
+ <div anchor="anchor2" class="target">bar</div>
+</span>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003.tentative.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003.tentative.html
new file mode 100644
index 0000000000..ec2d8d5ead
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-anchor-attribute-003.tentative.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>Tests that ::before, ::after and ::backdrop pseudo elements use originating element's implicit anchor</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/9144">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#implicit">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+#anchor {
+ width: 100px;
+ height: 100px;
+ margin-left: 150px;
+ margin-top: 50px;
+ background: orange;
+}
+#target::before, #target::after {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: lime;
+}
+#target::before{
+ content: '';
+ right: anchor(left);
+ top: anchor(top);
+}
+#target::after{
+ content: '';
+ left: anchor(right);
+ top: anchor(top);
+}
+
+dialog::backdrop {
+ top: calc(anchor(top) - 10px);
+ right: calc(anchor(right) - 10px);
+ bottom: calc(anchor(bottom) - 10px);
+ left: calc(anchor(left) - 10px);
+ background: lime;
+}
+
+</style>
+<div id="anchor"></div>
+<div id="target" anchor="anchor"></div>
+<dialog id="dialog" anchor="anchor"></div>
+
+<script>
+test(() => {
+ let style = getComputedStyle(target, '::before');
+ assert_equals(style.left, '50px');
+ assert_equals(style.top, '50px');
+}, "::before uses originating element's implicit anchor");
+
+test(() => {
+ let style = getComputedStyle(target, '::after');
+ assert_equals(style.left, '250px');
+ assert_equals(style.top, '50px');
+}, "::after uses originating element's implicit anchor");
+
+test(() => {
+ dialog.showModal();
+ let style = getComputedStyle(dialog, '::backdrop');
+ assert_equals(style.left, '140px');
+ assert_equals(style.top, '40px');
+ assert_equals(style.width, '120px');
+ assert_equals(style.height, '120px');
+}, "::backdrop uses originating element's implicit anchor");
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-001.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-001.html
new file mode 100644
index 0000000000..c2966f3620
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-001.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="ko" >
+<head>
+<meta charset="utf-8"/>
+<title>lang attribute in html tag</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The browser will recognize a language declared in a lang attribute on the html tag.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-002.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-002.html
new file mode 100644
index 0000000000..205bc35f2d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-002.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html xml:lang="ko" >
+<head>
+<meta charset="utf-8"/>
+<title>xml:lang attribute in html tag</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 50);
+}, "The browser will NOT recognize a language declared in an xml:lang attribute on the html tag.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html
new file mode 100644
index 0000000000..717aa12e68
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html >
+<head>
+<meta charset="utf-8"/>
+<title>HTTP header</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The browser will recognize a language declared in the HTTP header, when there is no internal language declaration.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html.headers b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html.headers
new file mode 100644
index 0000000000..0c47ecd4fa
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-003.html.headers
@@ -0,0 +1 @@
+Content-Language: ko
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-004.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-004.html
new file mode 100644
index 0000000000..ff36f75add
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-004.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html >
+<head>
+<meta charset="utf-8"/>
+ <meta http-equiv="Content-Language" content="ko" >
+<title>pragma-set default</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The browser will recognize a language declared in a meta element in the head using http-equiv='Content-Language' content='..' (with a single language tag value), when there is no other language declaration inside the document.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html
new file mode 100644
index 0000000000..63fb8e3bbb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="ko" >
+<head>
+<meta charset="utf-8"/>
+<title>HTTP header and html lang</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "If there is a conflict between the language declarations in the HTTP header and the html element using lang, the browser will recognize the language declared in the html element.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html.headers b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html.headers
new file mode 100644
index 0000000000..1b971b697a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-005.html.headers
@@ -0,0 +1 @@
+Content-Language: zh
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html
new file mode 100644
index 0000000000..ede4912025
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html >
+<head>
+<meta charset="utf-8"/>
+ <meta http-equiv="Content-Language" content="ko" >
+<title>HTTP header and meta element</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "If there is a conflict between the language declarations in the HTTP header and the Content-Language meta element, the UA will recognize the language declared in the meta element.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html.headers b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html.headers
new file mode 100644
index 0000000000..1b971b697a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-006.html.headers
@@ -0,0 +1 @@
+Content-Language: zh
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-007.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-007.html
new file mode 100644
index 0000000000..8fafef036f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-007.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html lang="ko" >
+<head>
+<meta charset="utf-8"/>
+ <meta http-equiv="Content-Language" content="zh" >
+<title>html lang and meta elements</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "If there is a conflict between the language declared using lang in the html element and that in the meta element, the UA will recognize the language declared in the html element.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-008.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-008.html
new file mode 100644
index 0000000000..3be54154c1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-008.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html >
+<head>
+<meta charset="utf-8"/>
+<title>lang="..." vs lang=""</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test" lang="ko"><div id="box" lang="">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 50);
+}, "If an element contains a lang attribute with an empty value, the value of a lang attribute higher up the document tree will no longer be applied to the content of that element.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html
new file mode 100644
index 0000000000..3a927028ef
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="" >
+<head>
+<meta charset="utf-8"/>
+<title>lang="" vs HTTP</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 50);
+}, "If the HTTP header contains a language declaration but the html element uses an empty lang value, the UA will not recognize the language declared in the HTTP header.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html.headers b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html.headers
new file mode 100644
index 0000000000..0c47ecd4fa
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-009.html.headers
@@ -0,0 +1 @@
+Content-Language: ko
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-010.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-010.html
new file mode 100644
index 0000000000..2c21737471
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-010.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html lang="" >
+<head>
+<meta charset="utf-8"/>
+ <meta http-equiv="Content-Language" content="ko" >
+<title>lang="" vs meta Content-Language</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-lang-and-xml:lang-attributes'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+#box:lang(ko) { width: 100px; }
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 50);
+}, "If the meta Content-Language element contains a language declaration but the html element uses an empty lang value, the UA will not recognize the language declared in the meta Content-Language element.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-011.html.headers b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-011.html.headers
new file mode 100644
index 0000000000..827b4348f4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-lang-attribute-011.html.headers
@@ -0,0 +1 @@
+Content-Language: ko,zh,ja
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-007.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-007.html
new file mode 100644
index 0000000000..abce2858a2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-007.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>no translate attribute</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-translate-attribute'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type='text/css'>
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&nbsp;</div></div>
+
+
+<script>
+test(function() {
+assert_true(document.getElementById('box').translate);
+}, "In the default case, ie. with no translate attribute in the page, javascript will detect the translation mode of text as translate-enabled.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-008.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-008.html
new file mode 100644
index 0000000000..70486fe59b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-008.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>translate=yes</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-translate-attribute'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type='text/css'>
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box" translate="yes">&nbsp;</div></div>
+
+
+<script>
+test(function() {
+assert_true(document.getElementById('box').translate);
+}, "If the translate attribute is set to yes, javascript will detect the translation mode of text as translate-enabled.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-009.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-009.html
new file mode 100644
index 0000000000..1ab49b0307
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-009.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>translate=no</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-translate-attribute'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type='text/css'>
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box" translate="no">&nbsp;</div></div>
+
+
+<script>
+test(function() {
+assert_false(document.getElementById('box').translate);
+}, "If the translate attribute is set to no, javascript will detect the translation mode of text as no-translate.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-010.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-010.html
new file mode 100644
index 0000000000..c45965e004
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-010.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>translate inherits no</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-translate-attribute'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type='text/css'>
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box" translate="no">&nbsp; <span id="spantest">&nbsp;</span></div></div>
+
+
+<script>
+test(function() {
+assert_false(document.getElementById('spantest').translate);
+}, "If the translate attribute is set to no, javascript will detect the translation mode of elements inside that element with no translate flag as no-translate.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-011.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-011.html
new file mode 100644
index 0000000000..101f70e2e7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-011.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>translate=yes inside translate=no</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-translate-attribute'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type='text/css'>
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box" translate="no">&nbsp; <span id="spantest" translate="yes">&nbsp;</span></div></div>
+
+
+<script>
+test(function() {
+assert_true(document.getElementById('spantest').translate);
+}, "If the translate attribute is set to yes on an element inside an element with the translate attribute set to no, javascript will detect the translation mode of text in the inner element as translate-enabled.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-012.html b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-012.html
new file mode 100644
index 0000000000..1d81cfd8b1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/the-translate-attribute-012.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>translate=""</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-translate-attribute'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type='text/css'>
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box" translate="">&nbsp;</div></div>
+
+
+<script>
+test(function() {
+assert_true(document.getElementById('box').translate);
+}, "If the translate attribute is set to a null string, javascript will detect the translation mode of text as translate-enabled.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/title-manual.html b/testing/web-platform/tests/html/dom/elements/global-attributes/title-manual.html
new file mode 100644
index 0000000000..d781172bba
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/title-manual.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<title>The title attribute</title>
+<style>
+div > * { display: inline }
+link::before { content: "link" }
+</style>
+<p>Hover each word below. The tooltip for each of them should be "PASS".</p>
+<div title=PASS>div <link> <style>style</style> <dfn>dfn</dfn> <abbr>abbr</abbr> <menuitem>menuitem</menuitem></div>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/translate-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/dom/elements/global-attributes/translate-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..dedf559b98
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/translate-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-translate">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="@translate values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!--
+ We wrap the <span> elements under test with <div> elements so the invalid
+ value default (inherit) can be distinguished from true through the IDL
+ attribute. The inherit state would otherwise yield true because inheritance
+ would go all the way to :root, whose translation mode is translate-enabled
+ because it’s also in the inherit state.
+-->
+<div translate="no"><span translate="yes"></span></div>
+<div translate="no"><span translate="YeS"></span></div>
+<div translate="no"><span translate="yeſ"></span></div>
+<script>
+const span = document.querySelectorAll("span");
+
+test(() => {
+ assert_equals(span[0].translate, true, "lowercase valid");
+ assert_equals(span[1].translate, true, "mixed case valid");
+ assert_equals(span[2].translate, false, "non-ASCII invalid");
+}, "keyword yes");
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/translate-inherit-no-parent-element.html b/testing/web-platform/tests/html/dom/elements/global-attributes/translate-inherit-no-parent-element.html
new file mode 100644
index 0000000000..370225c7f7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/translate-inherit-no-parent-element.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>The translate attribute inherit state when there's no parent element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ const div = document.createElement("div");
+ assert_true(div.translate);
+}, 'No parent node');
+
+test(() => {
+ const div = document.createElement("div");
+ const frag = document.createDocumentFragment();
+ frag.append(div);
+ assert_true(div.translate);
+}, 'DocumentFragment parent node');
+
+for (const translateValue of ['yes', 'no']) {
+ test(() => {
+ const div = document.createElement("div");
+ const myElement = document.createElement("my-element");
+ myElement.setAttribute('translate', translateValue);
+ myElement.attachShadow({mode: 'open'});
+ myElement.shadowRoot.append(div);
+ assert_true(div.translate);
+ }, `ShadowRoot parent node whose shadow host has translate=${translateValue}`);
+}
+
+test(() => {
+ assert_true(document.documentElement.translate);
+}, 'Document parent node');
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/translate-non-html-translation-mode.html b/testing/web-platform/tests/html/dom/elements/global-attributes/translate-non-html-translation-mode.html
new file mode 100644
index 0000000000..3bd7f6ace9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/global-attributes/translate-non-html-translation-mode.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Non-HTML elements have a translation mode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(() => {
+ const svgContainer = document.createElement("svg");
+ const foreignObject = document.createElement("foreignObject");
+ svgContainer.appendChild(foreignObject);
+ const div = document.createElement("div");
+ foreignObject.appendChild(div);
+
+ assert_true(div.translate);
+ }, 'Non-HTML elements default to translate-enabled');
+
+ test(() => {
+ const outerDiv = document.createElement("div");
+ outerDiv.translate = true;
+ assert_true(outerDiv.translate);
+
+ const svgContainer = document.createElement("svg");
+ outerDiv.appendChild(svgContainer);
+ const foreignObject = document.createElement("foreignObject");
+ svgContainer.appendChild(foreignObject);
+ const div = document.createElement("div");
+ foreignObject.appendChild(div);
+
+ assert_true(div.translate);
+ }, "Non-HTML elements inherit their parent's translation-enabled state");
+
+ test(() => {
+ const outerDiv = document.createElement("div");
+ outerDiv.translate = false;
+ assert_false(outerDiv.translate);
+
+ const svgContainer = document.createElement("svg");
+ outerDiv.appendChild(svgContainer);
+ const foreignObject = document.createElement("foreignObject");
+ svgContainer.appendChild(foreignObject);
+ const div = document.createElement("div");
+ foreignObject.appendChild(div);
+
+ assert_false(div.translate);
+ }, "Non-HTML elements inherit their parent's no-translation state");
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/images/bypass-cache-revalidation.html b/testing/web-platform/tests/html/dom/elements/images/bypass-cache-revalidation.html
new file mode 100644
index 0000000000..38cdd876da
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/images/bypass-cache-revalidation.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Cached images can bypass revalidation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<div id="imageDiv1"></div>
+<div id="imageDiv2"></div>
+<canvas id="canvas"></canvas>
+<script>
+
+function getImagePixel(image)
+{
+ canvas.getContext("2d").drawImage(image, 0, 0, 10, 10);
+ return canvas.getContext("2d").getImageData(0, 0, 1, 1).data;
+}
+
+let resolve;
+promise_test(async (t) => {
+ const url = "image.py?id=" + token();
+
+ let promise = new Promise(r => resolve = r);
+ imageDiv1.innerHTML = `<img src="${url}" onload="resolve()"></img>`;
+ await promise;
+
+ const url2 = "image.py?id=" + token();
+ promise = new Promise(r => resolve = r);
+ imageDiv1.innerHTML = `<img src="${url2}" onload="resolve()"></img>`;
+ await promise;
+
+ promise = new Promise(r => resolve = r);
+ imageDiv2.innerHTML = `<img id="image2" src="${url}" onload="resolve()"></img>`;
+ await promise;
+
+ assert_array_equals(getImagePixel(image2), [0, 255, 0, 255]);
+}, "Images can bypass no-cache");
+</script>
+
diff --git a/testing/web-platform/tests/html/dom/elements/images/image.py b/testing/web-platform/tests/html/dom/elements/images/image.py
new file mode 100644
index 0000000000..b8bb34e618
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/images/image.py
@@ -0,0 +1,28 @@
+import os.path
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+
+ key = request.GET[b'id']
+ alreadyServedRequest = False
+ try:
+ alreadyServedRequest = request.server.stash.take(key)
+ except (KeyError, ValueError) as e:
+ pass
+
+ if alreadyServedRequest:
+ body = open(os.path.join(os.path.dirname(isomorphic_decode(__file__)), u"../../../../images/red.png"), u"rb").read()
+ else:
+ request.server.stash.put(key, True);
+ body = open(os.path.join(os.path.dirname(isomorphic_decode(__file__)), u"../../../../images/green.png"), u"rb").read()
+ pass
+
+ response.writer.write_status(200)
+ response.writer.write_header(b"etag", b"abcdef")
+ response.writer.write_header(b"content-length", len(body))
+ response.writer.write_header(b"content-type", b"image/png")
+ response.writer.write_header(b"cache-control", b"public, max-age=31536000, no-cache")
+ response.writer.end_headers()
+
+ response.writer.write(body)
diff --git a/testing/web-platform/tests/html/dom/elements/name-content-attribute-and-property.html b/testing/web-platform/tests/html/dom/elements/name-content-attribute-and-property.html
new file mode 100644
index 0000000000..082ed281fe
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/name-content-attribute-and-property.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>Only certain HTML elements reflect the name content attribute as a property</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+<div id="log"></div>
+<script>
+ function doesReflect(tagName) {
+ var element = document.createElement(tagName);
+ element.setAttribute("name", "foo");
+ assert_equals(element.getAttribute("name"), "foo", "setAttribute should change content attribute");
+ element.name = "bar";
+ assert_equals(element.getAttribute("name"), "bar", "assignment to .name should change content attribute");
+ }
+
+ function doesNotReflect(tagName) {
+ var element = document.createElement(tagName);
+ element.setAttribute("name", "foo");
+ assert_equals(element.getAttribute("name"), "foo", "setAttribute should change content attribute");
+ element.name = "bar";
+ assert_equals(element.getAttribute("name"), "foo", "assignment to .name should not change content attribute");
+ }
+
+ var reflectingTagNames = [
+ "a", "button", "embed", "fieldset", "form", "frame",
+ "iframe", "img", "input", "map", "meta", "object", "output",
+ "param", "select", "slot", "textarea",
+ ];
+
+ // Optionally add "details" to reflectingTagNames since Chromium is
+ // prototyping the proposal at
+ // https://open-ui.org/components/accordion.explainer that adds this
+ // reflection to "details" as well.
+ // TODO(https://crbug.com/1444057): This runtime check should eventually be
+ // removed, and depending on the outcome of the proposal details should
+ // possibly be added to reflectingTagNames unconditionally.
+ if ("name" in HTMLDetailsElement.prototype) {
+ reflectingTagNames.push("details");
+ }
+
+ const old_and_new_elements = [...HTML5_ELEMENTS, ...HTML5_DEPRECATED_ELEMENTS];
+
+ const nonReflectingTagNames = old_and_new_elements.filter(x => !reflectingTagNames.includes(x));
+
+ reflectingTagNames.forEach(function(tagName) {
+ test(function() {
+ doesReflect(tagName)
+ }, tagName + " element's name property reflects its content attribute");
+ });
+
+ nonReflectingTagNames.forEach(function(tagName) {
+ test(function() {
+ doesNotReflect(tagName)
+ }, tagName + " element's name property does not reflect its content attribute");
+ });
+</script>
+
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001a.html
new file mode 100644
index 0000000000..16a308a2f7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001a.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following number, opposite direction</title>
+<link rel="author" title="Richard Ishida" href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel="match" href='reference/dir-isolation-001-ref.html'>
+<meta name="assert" content='Element content with a dir attribute is treated as a neutral character and directionally isolated from a following number.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="rtl">&#x5d0;</span> 3</div>
+<div dir="ltr"><span dir="rtl">a</span> 3</div>
+<div dir="rtl"><span dir="ltr">&#x5d0;</span> 3</div>
+<div dir="rtl"><span dir="ltr">a</span> 3</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; 3&#x202c;</div>
+<div dir="ltr">&#x202d;a 3&#x202c;</div>
+<div dir="rtl">&#x202d;3 &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3 a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001b.html
new file mode 100644
index 0000000000..197f49aa24
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001b.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following number, auto</title>
+<link rel="author" title="Richard Ishida" href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel="match" href='reference/dir-isolation-001-ref.html'>
+<meta name="assert" content='Element content with a dir attribute is treated as a neutral character and directionally isolated from a following number.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="auto">&#x5d0;</span> 3</div>
+<div dir="ltr"><span dir="auto">a</span> 3</div>
+<div dir="rtl"><span dir="auto">&#x5d0;</span> 3</div>
+<div dir="rtl"><span dir="auto">a</span> 3</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; 3&#x202c;</div>
+<div dir="ltr">&#x202d;a 3&#x202c;</div>
+<div dir="rtl">&#x202d;3 &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3 a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001c.html
new file mode 100644
index 0000000000..95ec6c739d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001c.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following number, same direction</title>
+<link rel="author" title="Richard Ishida" href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel="match" href='reference/dir-isolation-001-ref.html'>
+<meta name="assert" content='Element content with a dir attribute is treated as a neutral character and directionally isolated from a following number.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="ltr">&#x5d0;</span> 3</div>
+<div dir="ltr"><span dir="ltr">a</span> 3</div>
+<div dir="rtl"><span dir="rtl">&#x5d0;</span> 3</div>
+<div dir="rtl"><span dir="rtl">a</span> 3</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; 3&#x202c;</div>
+<div dir="ltr">&#x202d;a 3&#x202c;</div>
+<div dir="rtl">&#x202d;3 &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3 a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002a.html
new file mode 100644
index 0000000000..7b7029a269
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002a.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following number with intervening neutrals, opposite direction</title>
+<link rel="author" title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-002a-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from a following number.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="rtl">> &#x5d0; ></span> > 3 ></div>
+<div dir="ltr"><span dir="rtl">> a ></span> > 3 ></div>
+<div dir="rtl"><span dir="ltr">> &#x5d0; ></span> > 3 ></div>
+<div dir="rtl"><span dir="ltr">> a ></span> > 3 ></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&lt; &#x5d0; &lt; &gt; 3 &gt;&#x202c;</div>
+<div dir="ltr">&#x202d;&lt; a &lt; &gt; 3 &gt;&#x202c;</div>
+<div dir="rtl">&#x202d;&lt; 3 &lt; &gt; &#x5d0; &gt;&#x202c;</div>
+<div dir="rtl">&#x202d;&lt; 3 &lt; &gt; a &gt;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002b.html
new file mode 100644
index 0000000000..d448de5080
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002b.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following number with intervening neutrals, auto</title>
+<link rel="author" title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-002b-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from a following number.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="auto">> &#x5d0; ></span> > 3 ></div>
+<div dir="ltr"><span dir="auto">> a ></span> > 3 ></div>
+<div dir="rtl"><span dir="auto">> &#x5d0; ></span> > 3 ></div>
+<div dir="rtl"><span dir="auto">> a ></span> > 3 ></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&lt; &#x5d0; &lt; &gt; 3 &gt;&#x202c;</div>
+<div dir="ltr">&#x202d;&gt; a &gt; &gt; 3 &gt;&#x202c;</div>
+<div dir="rtl">&#x202d;&lt; 3 &lt; &lt; &#x5d0; &lt;&#x202c;</div>
+<div dir="rtl">&#x202d;&lt; 3 &lt; &gt; a &gt;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002c.html
new file mode 100644
index 0000000000..e88fb1c845
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002c.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following number with intervening neutrals, same direction</title>
+<link rel="author" title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-002c-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from a following number.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="ltr">&gt; &#x5d0; &gt;</span> &gt; 3 &gt;</div>
+<div dir="ltr"><span dir="ltr">&gt; a &gt;</span> &gt; 3 &gt;</div>
+<div dir="rtl"><span dir="rtl">&gt; &#x5d0; &gt;</span> &gt; 3 &gt;</div>
+<div dir="rtl"><span dir="rtl">&gt; a &gt;</span> &gt; 3 &gt;</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&gt; &#x5d0; &gt; &gt; 3 &gt;&#x202c;</div>
+<div dir="ltr">&#x202d;&gt; a &gt; &gt; 3 &gt;&#x202c;</div>
+<div dir="rtl">&#x202d;&lt; 3 &lt; &lt; &#x5d0; &lt;&#x202c;</div>
+<div dir="rtl">&#x202d;&lt; 3 &lt; &lt; a &lt;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003a.html
new file mode 100644
index 0000000000..9cf65c8184
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003a.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from immediately following number, opposite direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-003-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and is directionally isolated from a following number, even with no intervening white space.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="rtl">&#x5d0;</span>3</div>
+<div dir="ltr"><span dir="rtl">a</span>3</div>
+<div dir="rtl"><span dir="ltr">&#x5d0;</span>3</div>
+<div dir="rtl"><span dir="ltr">a</span>3</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0;3&#x202c;</div>
+<div dir="ltr">&#x202d;a3&#x202c;</div>
+<div dir="rtl">&#x202d;3&#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003b.html
new file mode 100644
index 0000000000..2c6b553089
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003b.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from immediately following number, auto</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-003-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and is directionally isolated from a following number, even with no intervening white space.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="auto">&#x5d0;</span>3</div>
+<div dir="ltr"><span dir="auto">a</span>3</div>
+<div dir="rtl"><span dir="auto">&#x5d0;</span>3</div>
+<div dir="rtl"><span dir="auto">a</span>3</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0;3&#x202c;</div>
+<div dir="ltr">&#x202d;a3&#x202c;</div>
+<div dir="rtl">&#x202d;3&#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003c.html
new file mode 100644
index 0000000000..ac8735122b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-003c.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from immediately following number, same direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-003-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and is directionally isolated from a following number, even with no intervening white space.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="ltr">&#x5d0;</span>3</div>
+<div dir="ltr"><span dir="ltr">a</span>3</div>
+<div dir="rtl"><span dir="rtl">&#x5d0;</span>3</div>
+<div dir="rtl"><span dir="rtl">a</span>3</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0;3&#x202c;</div>
+<div dir="ltr">&#x202d;a3&#x202c;</div>
+<div dir="rtl">&#x202d;3&#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004a.html
new file mode 100644
index 0000000000..27a674ccdc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004a.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: numbers isolated from preceding text, opposite direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-004-ref.html'>
+<meta name='assert' content='Numeric element content with a dir attribute is treated as a neutral character and directionally isolated from preceding text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; <span dir="rtl">3</span></div>
+<div dir="ltr">a <span dir="rtl">3</span></div>
+<div dir="rtl">&#x5d0; <span dir="ltr">3</span></div>
+<div dir="rtl">a <span dir="ltr">3</span></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; 3&#x202c;</div>
+<div dir="ltr">&#x202d;a 3&#x202c;</div>
+<div dir="rtl">&#x202d;3 &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3 a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004b.html
new file mode 100644
index 0000000000..6fe74393ae
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004b.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: numbers isolated from preceding text, auto</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-004-ref.html'>
+<meta name='assert' content='Numeric element content with a dir attribute is treated as a neutral character and directionally isolated from preceding text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; <span dir="auto">3</span></div>
+<div dir="ltr">a <span dir="auto">3</span></div>
+<div dir="rtl">&#x5d0; <span dir="auto">3</span></div>
+<div dir="rtl">a <span dir="auto">3</span></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; 3&#x202c;</div>
+<div dir="ltr">&#x202d;a 3&#x202c;</div>
+<div dir="rtl">&#x202d;3 &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3 a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004c.html
new file mode 100644
index 0000000000..43d994b2f0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004c.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: numbers isolated from preceding text, same direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-004-ref.html'>
+<meta name='assert' content='Numeric element content with a dir attribute is treated as a neutral character and directionally isolated from preceding text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; <span dir="ltr">3</span></div>
+<div dir="ltr">a <span dir="ltr">3</span></div>
+<div dir="rtl">&#x5d0; <span dir="rtl">3</span></div>
+<div dir="rtl">a <span dir="rtl">3</span></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; 3&#x202c;</div>
+<div dir="ltr">&#x202d;a 3&#x202c;</div>
+<div dir="rtl">&#x202d;3 &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;3 a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005a.html
new file mode 100644
index 0000000000..2fbddbd71f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005a.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following text, opposite direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-005-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="rtl">&#x5d0;</span> &#x5d1;...</div>
+<div dir="ltr"><span dir="rtl">a</span> b...</div>
+<div dir="rtl"><span dir="ltr">a</span> b...</div>
+<div dir="rtl"><span dir="ltr">&#x5d0;</span> &#x5d1;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; &#x5d1;...&#x202c;</div>
+<div dir="ltr">&#x202d;a b...&#x202c;</div>
+<div dir="rtl">&#x202d;...b a&#x202c;</div>
+<div dir="rtl">&#x202d;...&#x5d1; &#x5d0;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005b.html
new file mode 100644
index 0000000000..d61e258f21
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005b.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following text, auto</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-005-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="auto">&#x5d0;</span> &#x5d1;...</div>
+<div dir="ltr"><span dir="auto">a</span> b...</div>
+<div dir="rtl"><span dir="auto">a</span> b...</div>
+<div dir="rtl"><span dir="auto">&#x5d0;</span> &#x5d1;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; &#x5d1;...&#x202c;</div>
+<div dir="ltr">&#x202d;a b...&#x202c;</div>
+<div dir="rtl">&#x202d;...b a&#x202c;</div>
+<div dir="rtl">&#x202d;...&#x5d1; &#x5d0;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005c.html
new file mode 100644
index 0000000000..d544275b2f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005c.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following text, same direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-005-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="ltr">&#x5d0;</span> &#x5d1;...</div>
+<div dir="ltr"><span dir="ltr">a</span> b...</div>
+<div dir="rtl"><span dir="rtl">a</span> b...</div>
+<div dir="rtl"><span dir="rtl">&#x5d0;</span> &#x5d1;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; &#x5d1;...&#x202c;</div>
+<div dir="ltr">&#x202d;a b...&#x202c;</div>
+<div dir="rtl">&#x202d;...b a&#x202c;</div>
+<div dir="rtl">&#x202d;...&#x5d1; &#x5d0;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006a.html
new file mode 100644
index 0000000000..430df00d9f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006a.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following text with intervening neutrals, opposite direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-006-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text despite intervening neutrals.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="rtl">&gt; &#x5d0; &gt;</span> &gt; &#x5d1; &gt;...</div>
+<div dir="rtl"><span dir="ltr">&gt; a &gt;</span> &gt; b &gt;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&lt; &#x5d0; &lt; &gt; &#x5d1; &gt;...&#x202c;</div>
+<div dir="rtl">&#x202d;...&lt; b &lt; &gt; a &gt;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006b.html
new file mode 100644
index 0000000000..a6da487152
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006b.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following text with intervening neutrals, auto</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-006-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text despite intervening neutrals.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="auto">&gt; &#x5d0; &gt;</span> &gt; &#x5d1; &gt;...</div>
+<div dir="rtl"><span dir="auto">&gt; a &gt;</span> &gt; b &gt;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&lt; &#x5d0; &lt; &gt; &#x5d1; &gt;...&#x202c;</div>
+<div dir="rtl">&#x202d;...&lt; b &lt; &gt; a &gt;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006c.html
new file mode 100644
index 0000000000..3407d37f38
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006c.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from following text with intervening neutrals, same direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-006c-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text despite intervening neutrals.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="ltr">&gt; &#x5d0; &gt;</span> &gt; &#x5d1; &gt;...</div>
+<div dir="rtl"><span dir="rtl">&gt; a &gt;</span> &gt; b &gt;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&gt; &#x5d0; &gt; &gt; &#x5d1; &gt;...&#x202c;</div>
+<div dir="rtl">&#x202d;...&lt; b &lt; &lt; a &lt;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007a.html
new file mode 100644
index 0000000000..e8b37b1b97
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007a.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from immediately following text, opposite direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-007-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text even with no intervening white space.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="rtl">&#x5d0;</span>&#x5d1;...</div>
+<div dir="ltr"><span dir="rtl">a</span>b...</div>
+<div dir="rtl"><span dir="ltr">a</span>b...</div>
+<div dir="rtl"><span dir="ltr">&#x5d0;</span>&#x5d1;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0;&#x5d1;...&#x202c;</div>
+<div dir="ltr">&#x202d;ab...&#x202c;</div>
+<div dir="rtl">&#x202d;...ba&#x202c;</div>
+<div dir="rtl">&#x202d;...&#x5d1;&#x5d0;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007b.html
new file mode 100644
index 0000000000..c54e63de7c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007b.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from immediately following text, auto</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-007-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text even with no intervening white space.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="auto">&#x5d0;</span>&#x5d1;...</div>
+<div dir="ltr"><span dir="auto">a</span>b...</div>
+<div dir="rtl"><span dir="auto">a</span>b...</div>
+<div dir="rtl"><span dir="auto">&#x5d0;</span>&#x5d1;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0;&#x5d1;...&#x202c;</div>
+<div dir="ltr">&#x202d;ab...&#x202c;</div>
+<div dir="rtl">&#x202d;...ba&#x202c;</div>
+<div dir="rtl">&#x202d;...&#x5d1;&#x5d0;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007c.html
new file mode 100644
index 0000000000..b9c5219b8d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-007c.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from immediately following text, same direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-007-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from following text even with no intervening white space.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr"><span dir="ltr">&#x5d0;</span>&#x5d1;...</div>
+<div dir="ltr"><span dir="ltr">a</span>b...</div>
+<div dir="rtl"><span dir="rtl">a</span>b...</div>
+<div dir="rtl"><span dir="rtl">&#x5d0;</span>&#x5d1;...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0;&#x5d1;...&#x202c;</div>
+<div dir="ltr">&#x202d;ab...&#x202c;</div>
+<div dir="rtl">&#x202d;...ba&#x202c;</div>
+<div dir="rtl">&#x202d;...&#x5d1;&#x5d0;&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008a.html
new file mode 100644
index 0000000000..1455fd552b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008a.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from preceding text, opposite direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-008-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from preceding text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; <span dir="rtl">&#x5d1;</span></div>
+<div dir="ltr">a <span dir="rtl">b</span></div>
+<div dir="rtl">&#x5d0; <span dir="ltr">&#x5d1;</span></div>
+<div dir="rtl">a <span dir="ltr">b</span></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; &#x5d1;&#x202c;</div>
+<div dir="ltr">&#x202d;a b&#x202c;</div>
+<div dir="rtl">&#x202d;&#x5d1; &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;b a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008b.html
new file mode 100644
index 0000000000..f12e6d67bf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008b.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from preceding text, auto</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-008-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from preceding text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; <span dir="auto">&#x5d1;</span></div>
+<div dir="ltr">a <span dir="auto">b</span></div>
+<div dir="rtl">&#x5d0; <span dir="auto">&#x5d1;</span></div>
+<div dir="rtl">a <span dir="auto">b</span></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; &#x5d1;&#x202c;</div>
+<div dir="ltr">&#x202d;a b&#x202c;</div>
+<div dir="rtl">&#x202d;&#x5d1; &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;b a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008c.html
new file mode 100644
index 0000000000..b1754cf23e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008c.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>The dir attribute: isolated from preceding text, same direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-008-ref.html'>
+<meta name='assert' content='Element content with a dir attribute is treated as a neutral character and directionally isolated from preceding text.'>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!--Notes:
+Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+The punctuation is moved around in the source to make it easier to do visual comparisons when the test is run.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; <span dir="ltr">&#x5d1;</span></div>
+<div dir="ltr">a <span dir="ltr">b</span></div>
+<div dir="rtl">&#x5d0; <span dir="rtl">&#x5d1;</span></div>
+<div dir="rtl">a <span dir="rtl">b</span></div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d0; &#x5d1;&#x202c;</div>
+<div dir="ltr">&#x202d;a b&#x202c;</div>
+<div dir="rtl">&#x202d;&#x5d1; &#x5d0;&#x202c;</div>
+<div dir="rtl">&#x202d;b a&#x202c;</div>
+</div>
+</body></html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009a.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009a.html
new file mode 100644
index 0000000000..63a9706bae
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009a.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from surrounding text, opposite direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-009-ref.html'>
+<meta name='assert' content="Element content with a dir attribute is treated as a neutral character and directionally isolated from surrounding text.">
+<style type='text/css'>
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!-- Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+If the BDI in the test's first DIV were a SPAN, its b would prevent the &#x5d0; and the &#x5d1;
+from forming a single RTL run and thus keep the &gt;s between from being mirrored into &lt;s.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; &gt; <span dir="rtl">&gt; b &gt;</span> &gt; &#x5d2;...</div>
+<div dir="rtl">a &gt; <span dir="ltr">&gt; &#x5d1; &gt;</span> &gt; c...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d2; &lt; &lt; b &lt; &lt; &#x5d0;...&#x202c;</div>
+<div dir="rtl">&#x202d;...a &gt; &gt; &#x5d1; &gt; &gt; c&#x202c;</div>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html
new file mode 100644
index 0000000000..57098fa75a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from surrounding text, auto</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-009b-ref.html'>
+<meta name='assert' content="Element content with a dir attribute is treated as a neutral character and directionally isolated from surrounding text.">
+<style type='text/css'>
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!-- Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+If the BDI in the test's first DIV were a SPAN, its b would prevent the &#x5d0; and the &#x5d1;
+from forming a single RTL run and thus keep the &gt;s between from being mirrored into &lt;s.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; &gt; <span dir="auto">&gt; b &gt;</span> &gt; &#x5d2;...</div>
+<div dir="rtl">a &gt; <span dir="auto">&gt; &#x5d1; &gt;</span> &gt; c...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d2; &lt; &gt; b &gt; &lt; &#x5d0;...&#x202c;</div>
+<div dir="rtl">&#x202d;...a &gt; &lt; &#x5d1; &lt; &gt; c&#x202c;</div>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html
new file mode 100644
index 0000000000..4aac3184ee
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from surrounding text, same direction</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel="help" href='http://www.w3.org/TR/html5/dom.html#requirements-relating-to-the-bidirectional-algorithm'>
+<link rel='match' href='reference/dir-isolation-009b-ref.html'>
+<meta name='assert' content="Element content with a dir attribute is treated as a neutral character and directionally isolated from surrounding text.">
+<style type='text/css'>
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<!-- Key to entities used below:
+&#x5d0; ... &#x5d5; - The first six Hebrew letters (strongly RTL).
+&#x202d; - The LRO (left-to-right-override) formatting character.
+&#x202c; - The PDF (pop directional formatting) formatting character; closes LRO.
+If the BDI in the test's first DIV were a SPAN, its b would prevent the &#x5d0; and the &#x5d1;
+from forming a single RTL run and thus keep the &gt;s between from being mirrored into &lt;s.
+-->
+<div class="test">
+<div dir="ltr">&#x5d0; &gt; <span dir="ltr">&gt; b &gt;</span> &gt; &#x5d2;...</div>
+<div dir="rtl">a &gt; <span dir="rtl">&gt; &#x5d1; &gt;</span> &gt; c...</div>
+</div>
+<div class="ref">
+<div dir="ltr">&#x202d;&#x5d2; &lt; &gt; b &gt; &lt; &#x5d0;...&#x202c;</div>
+<div dir="rtl">&#x202d;...a &gt; &lt; &#x5d1; &lt; &gt; c&#x202c;</div>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-001-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-001-ref.html
new file mode 100644
index 0000000000..b5882eb7a3
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-001-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from following number, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1488; 3&#8236;</div><div dir="ltr">&#8237;a 3&#8236;</div><div dir="rtl">&#8237;3 &#1488;&#8236;</div><div dir="rtl">&#8237;3 a&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1488; 3&#8236;</div><div dir="ltr">&#8237;a 3&#8236;</div><div dir="rtl">&#8237;3 &#1488;&#8236;</div><div dir="rtl">&#8237;3 a&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002a-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002a-ref.html
new file mode 100644
index 0000000000..f28559b5a1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002a-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from following number with intervening neutrals, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&lt; &#1488; &lt; &gt; 3 &gt;&#8236;</div><div dir="ltr">&#8237;&lt; a &lt; &gt; 3 &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &gt; &#1488; &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &gt; a &gt;&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&lt; &#1488; &lt; &gt; 3 &gt;&#8236;</div><div dir="ltr">&#8237;&lt; a &lt; &gt; 3 &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &gt; &#1488; &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &gt; a &gt;&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002b-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002b-ref.html
new file mode 100644
index 0000000000..d4eda2189d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002b-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from following number with intervening neutrals, auto</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&lt; &#1488; &lt; &gt; 3 &gt;&#8236;</div><div dir="ltr">&#8237;&gt; a &gt; &gt; 3 &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &lt; &#1488; &lt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &gt; a &gt;&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&lt; &#1488; &lt; &gt; 3 &gt;&#8236;</div><div dir="ltr">&#8237;&gt; a &gt; &gt; 3 &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &lt; &#1488; &lt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &gt; a &gt;&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002c-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002c-ref.html
new file mode 100644
index 0000000000..6c21d0147d
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-002c-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from following number with intervening neutrals, same direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&gt; &#1488; &gt; &gt; 3 &gt;&#8236;</div><div dir="ltr">&#8237;&gt; a &gt; &gt; 3 &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &lt; &#1488; &lt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &lt; a &lt;&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&gt; &#1488; &gt; &gt; 3 &gt;&#8236;</div><div dir="ltr">&#8237;&gt; a &gt; &gt; 3 &gt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &lt; &#1488; &lt;&#8236;</div><div dir="rtl">&#8237;&lt; 3 &lt; &lt; a &lt;&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-003-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-003-ref.html
new file mode 100644
index 0000000000..4c29838ee4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-003-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from immediately following number, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1488;3&#8236;</div><div dir="ltr">&#8237;a3&#8236;</div><div dir="rtl">&#8237;3&#1488;&#8236;</div><div dir="rtl">&#8237;3a&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1488;3&#8236;</div><div dir="ltr">&#8237;a3&#8236;</div><div dir="rtl">&#8237;3&#1488;&#8236;</div><div dir="rtl">&#8237;3a&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-004-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-004-ref.html
new file mode 100644
index 0000000000..cb83dde584
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-004-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: numbers isolated from preceding text, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1488; 3&#8236;</div><div dir="ltr">&#8237;a 3&#8236;</div><div dir="rtl">&#8237;3 &#1488;&#8236;</div><div dir="rtl">&#8237;3 a&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1488; 3&#8236;</div><div dir="ltr">&#8237;a 3&#8236;</div><div dir="rtl">&#8237;3 &#1488;&#8236;</div><div dir="rtl">&#8237;3 a&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-005-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-005-ref.html
new file mode 100644
index 0000000000..4a6c301aa1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-005-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from following text, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1488; &#1489;...&#8236;</div><div dir="ltr">&#8237;a b...&#8236;</div><div dir="rtl">&#8237;...b a&#8236;</div><div dir="rtl">&#8237;...&#1489; &#1488;&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1488; &#1489;...&#8236;</div><div dir="ltr">&#8237;a b...&#8236;</div><div dir="rtl">&#8237;...b a&#8236;</div><div dir="rtl">&#8237;...&#1489; &#1488;&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006-ref.html
new file mode 100644
index 0000000000..0f6b7bbbd0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from following text with intervening neutrals, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&lt; &#1488; &lt; &gt; &#1489; &gt;...&#8236;</div><div dir="rtl">&#8237;...&lt; b &lt; &gt; a &gt;&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&lt; &#1488; &lt; &gt; &#1489; &gt;...&#8236;</div><div dir="rtl">&#8237;...&lt; b &lt; &gt; a &gt;&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006c-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006c-ref.html
new file mode 100644
index 0000000000..0347c0910c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-006c-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from following text with intervening neutrals, same direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&gt; &#1488; &gt; &gt; &#1489; &gt;...&#8236;</div><div dir="rtl">&#8237;...&lt; b &lt; &lt; a &lt;&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&gt; &#1488; &gt; &gt; &#1489; &gt;...&#8236;</div><div dir="rtl">&#8237;...&lt; b &lt; &lt; a &lt;&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-007-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-007-ref.html
new file mode 100644
index 0000000000..665153d649
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-007-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from immediately following text, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1488;&#1489;...&#8236;</div><div dir="ltr">&#8237;ab...&#8236;</div><div dir="rtl">&#8237;...ba&#8236;</div><div dir="rtl">&#8237;...&#1489;&#1488;&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1488;&#1489;...&#8236;</div><div dir="ltr">&#8237;ab...&#8236;</div><div dir="rtl">&#8237;...ba&#8236;</div><div dir="rtl">&#8237;...&#1489;&#1488;&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-008-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-008-ref.html
new file mode 100644
index 0000000000..8eb90f8b79
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-008-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from preceding text, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1488; &#1489;&#8236;</div><div dir="ltr">&#8237;a b&#8236;</div><div dir="rtl">&#8237;&#1489; &#1488;&#8236;</div><div dir="rtl">&#8237;b a&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1488; &#1489;&#8236;</div><div dir="ltr">&#8237;a b&#8236;</div><div dir="rtl">&#8237;&#1489; &#1488;&#8236;</div><div dir="rtl">&#8237;b a&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009-ref.html
new file mode 100644
index 0000000000..1d2f57c6cf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from surrounding text, opposite direction</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1490; &lt; &lt; b &lt; &lt; &#1488;...&#8236;</div><div dir="rtl">&#8237;...a &gt; &gt; &#1489; &gt; &gt; c&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1490; &lt; &lt; b &lt; &lt; &#1488;...&#8236;</div><div dir="rtl">&#8237;...a &gt; &gt; &#1489; &gt; &gt; c&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009b-ref.html b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009b-ref.html
new file mode 100644
index 0000000000..30ee14c6c0
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/reference/dir-isolation-009b-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: isolated from surrounding text, auto</title>
+<style type="text/css">
+.test, .ref { font-size: 150%; border: 1px solid orange; margin: 10px; margin-right: 200px; padding: 5px; clear: both; }
+input { margin: 5px; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if the two boxes are identical.</p>
+<div class="ref"><div dir="ltr">&#8237;&#1490; &lt; &gt; b &gt; &lt; &#1488;...&#8236;</div><div dir="rtl">&#8237;...a &gt; &lt; &#1489; &lt; &gt; c&#8236;</div></div>
+<div class="ref"><div dir="ltr">&#8237;&#1490; &lt; &gt; b &gt; &lt; &#1488;...&#8236;</div><div dir="rtl">&#8237;...a &gt; &lt; &#1489; &lt; &gt; c&#8236;</div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/dynamic-getter.html b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/dynamic-getter.html
new file mode 100644
index 0000000000..e34fcf5ac1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/dynamic-getter.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<title>innerText/outerText getter test with dynamic style changes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="container"></div>
+<script>
+let container = document.querySelector('#container');
+
+function testText(html, expectedPlain, msg, mutate) {
+ test(function() {
+ container.innerHTML = html;
+
+ // Cause a flush of style and layout
+ document.body.offsetTop;
+
+ mutate();
+
+ var e = document.getElementById('target');
+ if (!e) {
+ e = container.firstChild;
+ }
+ assert_equals(e.innerText, expectedPlain, "innerText");
+ assert_equals(e.outerText, expectedPlain, "outerText");
+ container.textContext = '';
+ }, msg + ' (' + format_value(html) + ')');
+}
+
+function setStyle(id, attr, value) {
+ let el = document.getElementById(id);
+ if (el) {
+ el.style[attr] = value;
+ }
+}
+
+testText("<div id='target'><div id='child'>abc", "ABC",
+ "text-transform applied to child element", function() {
+ setStyle("child", "text-transform", "uppercase");
+ });
+testText("<div id='parent'><div id='target'>abc", "ABC",
+ "text-transform applied to parent element", function() {
+ setStyle("parent", "text-transform", "uppercase");
+ });
+
+testText("<div id='target'>abc<div id='child'>def", "abc",
+ "display: none applied to child element", function() {
+ setStyle("child", "display", "none");
+ });
+testText("<div id='parent'>invisible<div id='target'>abc", "abc",
+ "display: none applied to parent element", function() {
+ setStyle("parent", "display", "none");
+ });
+
+testText("<div id='target'>abc", "abc\ndef",
+ "insert node into sub-tree", function() {
+ let el = document.getElementById("target");
+ if (el) {
+ let c = document.createTextNode("def");
+ let d = document.createElement("div");
+ d.appendChild(c);
+ el.appendChild(d);
+ }
+ });
+
+testText("<div id='target'>abc<div id='remove'>def", "abc",
+ "remove node from sub-tree", function() {
+ let el = document.getElementById("target");
+ let victim = document.getElementById("remove");
+ if (el && victim) {
+ el.removeChild(victim);
+ }
+ });
+
+testText("<div id='target'>", "abcdef",
+ "insert whole sub-tree", function() {
+ var el = document.getElementById("target");
+ if (el) {
+ var def = document.createTextNode("def");
+ var s = document.createElement("span");
+ s.appendChild(def);
+
+ var abc = document.createTextNode("abc");
+ var d = document.createElement("div");
+ d.appendChild(abc);
+ d.appendChild(s);
+ el.appendChild(d);
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-first-letter-marker-multicol.html b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-first-letter-marker-multicol.html
new file mode 100644
index 0000000000..3b579dca1c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-first-letter-marker-multicol.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Test innerText/outerText for a combination of a list item with ::first-letter in multicol</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#dom-innertext">
+<link rel="help" href="https://crbug.com/1174985">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #item { display: list-item; }
+ #item::first-letter { background: lime; }
+ .col { column-count: 1; }
+</style>
+<div id="item" class="col"><div class="col">PASS</div></div>
+<script>
+ test(() => {
+ assert_equals(item.innerText, "PASS", "innerText");
+ assert_equals(item.outerText, "PASS", "outerText");
+ }, "");
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-tests.js b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-tests.js
new file mode 100644
index 0000000000..fd32e8d69a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter-tests.js
@@ -0,0 +1,401 @@
+testText("<div>abc", "abc", "Simplest possible test");
+
+/**** white-space:normal ****/
+
+testText("<div> abc", "abc", "Leading whitespace removed");
+testText("<div>abc ", "abc", "Trailing whitespace removed");
+testText("<div>abc def", "abc def", "Internal whitespace compressed");
+testText("<div>abc\ndef", "abc def", "\\n converted to space");
+testText("<div>abc\rdef", "abc def", "\\r converted to space");
+testText("<div>abc\tdef", "abc def", "\\t converted to space");
+testText("<div>abc <br>def", "abc\ndef", "Trailing whitespace before hard line break removed");
+testText("<div>abc<br> def", "abc\ndef", "Leading whitespace after hard line break removed");
+
+/**** <pre> ****/
+
+testText("<pre> abc", " abc", "Leading whitespace preserved");
+testText("<pre>abc ", "abc ", "Trailing whitespace preserved");
+testText("<pre>abc def", "abc def", "Internal whitespace preserved");
+testText("<pre>abc\ndef", "abc\ndef", "\\n preserved");
+testText("<pre>abc\rdef", "abc\ndef", "\\r converted to newline");
+testText("<pre>abc\tdef", "abc\tdef", "\\t preserved");
+testText("<div><pre>abc</pre><pre>def</pre>", "abc\ndef", "Two <pre> siblings");
+
+/**** <div style="white-space:pre"> ****/
+
+testText("<div style='white-space:pre'> abc", " abc", "Leading whitespace preserved");
+testText("<div style='white-space:pre'>abc ", "abc ", "Trailing whitespace preserved");
+testText("<div style='white-space:pre'>abc def", "abc def", "Internal whitespace preserved");
+testText("<div style='white-space:pre'>abc\ndef", "abc\ndef", "\\n preserved");
+testText("<div style='white-space:pre'>abc\rdef", "abc\ndef", "\\r converted to newline");
+testText("<div style='white-space:pre'>abc\tdef", "abc\tdef", "\\t preserved");
+
+/**** <span style="white-space:pre"> ****/
+
+testText("<span style='white-space:pre'> abc", " abc", "Leading whitespace preserved");
+testText("<span style='white-space:pre'>abc ", "abc ", "Trailing whitespace preserved");
+testText("<span style='white-space:pre'>abc def", "abc def", "Internal whitespace preserved");
+testText("<span style='white-space:pre'>abc\ndef", "abc\ndef", "\\n preserved");
+testText("<span style='white-space:pre'>abc\rdef", "abc\ndef", "\\r converted to newline");
+testText("<span style='white-space:pre'>abc\tdef", "abc\tdef", "\\t preserved");
+
+/**** <div style="white-space:pre-line"> ****/
+
+testText("<div style='white-space:pre-line'> abc", "abc", "Leading whitespace removed");
+testText("<div style='white-space:pre-line'>abc ", "abc", "Trailing whitespace removed");
+testText("<div style='white-space:pre-line'>abc def", "abc def", "Internal whitespace collapsed");
+testText("<div style='white-space:pre-line'>abc\ndef", "abc\ndef", "\\n preserved");
+testText("<div style='white-space:pre-line'>abc\rdef", "abc\ndef", "\\r converted to newline");
+testText("<div style='white-space:pre-line'>abc\tdef", "abc def", "\\t converted to space");
+
+/**** Collapsing whitespace across element boundaries ****/
+
+testText("<div><span>abc </span> def", "abc def", "Whitespace collapses across element boundaries");
+testText("<div><span>abc </span><span></span> def", "abc def", "Whitespace collapses across element boundaries");
+testText("<div><span>abc </span><span style='white-space:pre'></span> def", "abc def", "Whitespace collapses across element boundaries");
+testText("<div>abc <input> def", "abc def", "Whitespace around <input> should not be collapsed");
+testText("<div>abc <span style='display:inline-block'></span> def", "abc def", "Whitespace around inline-block should not be collapsed");
+testText("<div>abc <span style='display:inline-block'> def </span> ghi", "abc def ghi", "Trailing space at end of inline-block should be collapsed");
+testText("<div><input> <div>abc</div>", "abc", "Whitespace between <input> and block should be collapsed");
+testText("<div><span style='inline-block'></span> <div>abc</div>", "abc", "Whitespace between inline-block and block should be collapsed");
+testText("<div>abc <img> def", "abc def", "Whitespace around <img> should not be collapsed");
+testText("<div>abc <img width=1 height=1> def", "abc def", "Whitespace around <img> should not be collapsed");
+testText("<div><img> abc", " abc", "Leading whitesapce should not be collapsed");
+testText("<div>abc <img>", "abc ", "Trailing whitesapce should not be collapsed");
+testText("<div>abc <b></b> def", "abc def", "Whitespace around empty span should be collapsed");
+testText("<div>abc <b><i></i></b> def", "abc def", "Whitespace around empty spans should be collapsed");
+testText("<div><canvas></canvas> abc", " abc", "<canvas> should not collapse following space");
+testText("<div>abc <img style='display:block'> def", "abc\ndef", "Replaced element <img> with display:block should be treated as block-level");
+testText("<div>abc <canvas style='display:block'></canvas> def", "abc\ndef", "Replaced element <canvas> with display:block should be treated as block-level");
+
+/**** Soft line breaks ****/
+
+testText("<div style='width:0'>abc def", "abc def", "Soft line breaks ignored");
+testText("<div style='width:0'>abc-def", "abc-def", "Soft line break at hyphen ignored");
+testText("<div style='width:0'><span>abc</span> <span>def</span>", "abc def", "Whitespace text node preserved");
+
+/**** Soft line breaks when word-break:break-word is in effect ****/
+/* (based on Testcase #2 at https://bugzilla.mozilla.org/show_bug.cgi?id=1241631) */
+
+testText("<div style='width:1px; word-break:break-word'>Hello Kitty</div>", "Hello Kitty", "Soft breaks ignored in presence of word-break:break-word");
+testText("<div style='width:1px; word-break:break-word'><x>Hello</x> <x>Kitty</x></div>", "Hello Kitty", "Element boundaries ignored for soft break handling (1)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello</x> <x> Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (1)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello</x><x> Kitty</x></div>", "Hello Kitty", "Element boundaries ignored for soft break handling (2)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello </x> <x>Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (2)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello </x><x>Kitty</x></div>", "Hello Kitty", "Element boundaries ignored for soft break handling (3)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello </x><x> Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (3)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello </x> <x> Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (4)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello</x> Kitty</div>", "Hello Kitty", "Element boundaries ignored for soft break handling (4)");
+testText("<div style='width:1px; word-break:break-word'><x>Hello </x>Kitty</div>", "Hello Kitty", "Element boundaries ignored for soft break handling (5)");
+testText("<div style='width:1px; word-break:break-word; text-transform:uppercase'>Hello Kitty</div>", "HELLO KITTY", "Soft breaks ignored, text-transform applied");
+testText("<div style='width:1px; word-break:break-word'>Hello<br> Kitty</div>", "Hello\nKitty", "<br> returned as newline, following space collapsed");
+testText("<div style='width:1px; word-break:break-word'>Hello <br>Kitty</div>", "Hello\nKitty", "<br> returned as newline, preceding space collapsed");
+testText("<div style='width:1px; word-break:break-word'><x>Hello </x> <br> <x> Kitty</x></div>", "Hello\nKitty", "<br> returned as newline, adjacent spaces collapsed across element boundaries");
+
+/**** first-line/first-letter ****/
+
+testText("<div class='first-line-uppercase' style='width:0'>abc def", "ABC def", "::first-line styles applied");
+testText("<div class='first-letter-uppercase' style='width:0'>abc def", "Abc def", "::first-letter styles applied");
+testText("<div class='first-letter-float' style='width:0'>abc def", "abc def", "::first-letter float ignored");
+
+/**** &nbsp; ****/
+
+testText("<div>&nbsp;", "\xA0", "&nbsp; preserved");
+
+/**** display:none ****/
+
+testText("<div style='display:none'>abc", "abc", "display:none container");
+testText("<div style='display:none'>abc def", "abc def", "No whitespace compression in display:none container");
+testText("<div style='display:none'> abc def ", " abc def ", "No removal of leading/trailing whitespace in display:none container");
+testText("<div>123<span style='display:none'>abc", "123", "display:none child not rendered");
+testText("<div style='display:none'><span id='target'>abc", "abc", "display:none container with non-display-none target child");
+testTextInSVG("<div id='target'>abc", "abc", "non-display-none child of svg");
+testTextInSVG("<div style='display:none' id='target'>abc", "abc", "display:none child of svg");
+testTextInSVG("<div style='display:none'><div id='target'>abc", "abc", "child of display:none child of svg");
+
+/**** display:contents ****/
+
+if (CSS.supports("display", "contents")) {
+ testText("<div style='display:contents'>abc", "abc", "display:contents container");
+ testText("<div><div style='display:contents'>abc", "abc", "display:contents container");
+ testText("<div>123<span style='display:contents'>abc", "123abc", "display:contents rendered");
+ testText("<div style='display:contents'> ", "", "display:contents not processed via textContent");
+ testText("<div><div style='display:contents'> ", "", "display:contents not processed via textContent");
+}
+
+/**** visibility:hidden ****/
+
+testText("<div style='visibility:hidden'>abc", "", "visibility:hidden container");
+testText("<div>123<span style='visibility:hidden'>abc", "123", "visibility:hidden child not rendered");
+testText("<div style='visibility:hidden'>123<span style='visibility:visible'>abc", "abc", "visibility:visible child rendered");
+
+/**** visibility:collapse ****/
+
+testText("<table><tbody style='visibility:collapse'><tr><td>abc", "", "visibility:collapse row-group");
+testText("<table><tr style='visibility:collapse'><td>abc", "", "visibility:collapse row");
+testText("<table><tr><td style='visibility:collapse'>abc", "", "visibility:collapse cell");
+testText("<table><tbody style='visibility:collapse'><tr><td style='visibility:visible'>abc", "abc",
+ "visibility:collapse row-group with visible cell");
+testText("<table><tr style='visibility:collapse'><td style='visibility:visible'>abc", "abc",
+ "visibility:collapse row with visible cell");
+testText("<div style='display:flex'><span style='visibility:collapse'>1</span><span>2</span></div>",
+ "2", "visibility:collapse honored on flex item");
+testText("<div style='display:grid'><span style='visibility:collapse'>1</span><span>2</span></div>",
+ "2", "visibility:collapse honored on grid item");
+
+/**** opacity:0 ****/
+
+testText("<div style='opacity:0'>abc", "abc", "opacity:0 container");
+testText("<div style='opacity:0'>abc def", "abc def", "Whitespace compression in opacity:0 container");
+testText("<div style='opacity:0'> abc def ", "abc def", "Remove leading/trailing whitespace in opacity:0 container");
+testText("<div>123<span style='opacity:0'>abc", "123abc", "opacity:0 child rendered");
+
+/**** generated content ****/
+
+testText("<div class='before'>", "", "Generated content not included");
+testText("<div><div class='before'>", "", "Generated content on child not included");
+
+/**** innerText on replaced elements ****/
+
+testText("<button>abc", "abc", "<button> contents preserved");
+testText("<fieldset>abc", "abc", "<fieldset> contents preserved");
+testText("<fieldset><legend>abc", "abc", "<fieldset> <legend> contents preserved");
+testText("<input type='text' value='abc'>", "", "<input> contents ignored");
+testText("<textarea>abc", "", "<textarea> contents ignored");
+testText("<iframe>abc", "", "<iframe> contents ignored");
+testText("<iframe><div id='target'>abc", "", "<iframe> contents ignored");
+testText("<iframe src='data:text/html,abc'>", "","<iframe> subdocument ignored");
+testText("<audio style='display:block'>abc", "", "<audio> contents ignored");
+testText("<audio style='display:block'><source id='target' class='poke' style='display:block'>", "abc", "<audio> contents ok for element not being rendered");
+testText("<audio style='display:block'><source id='target' class='poke' style='display:none'>", "abc", "<audio> contents ok for element not being rendered");
+testText("<video>abc", "", "<video> contents ignored");
+testText("<video style='display:block'><source id='target' class='poke' style='display:block'>", "abc", "<video> contents ok for element not being rendered");
+testText("<video style='display:block'><source id='target' class='poke' style='display:none'>", "abc", "<video> contents ok for element not being rendered");
+testText("<canvas>abc", "", "<canvas> contents ignored");
+testText("<canvas><div id='target'>abc", "abc", "<canvas><div id='target'> contents ok for element not being rendered");
+testText("<img alt='abc'>", "", "<img> alt text ignored");
+testText("<img src='about:blank' class='poke'>", "", "<img> contents ignored");
+testText("<div><svg><text>abc</text></svg></div>", "abc", "<svg> text contents preserved");
+testText("<div><svg><defs><text>abc</text></defs></svg></div>", "", "<svg><defs> text contents ignored");
+testText("<div><svg><stop>abc</stop></svg></div>", "", "<svg> non-rendered text ignored");
+testText("<svg><foreignObject><span id='target'>abc</span></foreignObject></svg>", "abc", "<foreignObject> contents preserved");
+
+/**** <select>, <optgroup> & <option> ****/
+
+testText("<select size='1'><option>abc</option><option>def", "abc\ndef", "<select size='1'> contents of options preserved");
+testText("<select size='2'><option>abc</option><option>def", "abc\ndef", "<select size='2'> contents of options preserved");
+testText("<select size='1'><option id='target'>abc</option><option>def", "abc", "<select size='1'> contents of target option preserved");
+testText("<select size='2'><option id='target'>abc</option><option>def", "abc", "<select size='2'> contents of target option preserved");
+testText("<div>a<select></select>bc", "abc", "empty <select>");
+testText("<div>a<select><optgroup></select>bc", "a\nbc", "empty <optgroup> in <select>");
+testText("<div>a<select><option></select>bc", "a\nbc", "empty <option> in <select>");
+testText("<select class='poke'></select>", "", "<select> containing text node child");
+testText("<select><optgroup class='poke-optgroup'></select>", "", "<optgroup> containing <optgroup>");
+testText("<select><optgroup><option>abc</select>", "abc", "<optgroup> containing <option>");
+testText("<select><option class='poke-div'>123</select>", "123\nabc", "<div> in <option>");
+testText("<div>a<optgroup></optgroup>bc", "a\nbc", "empty <optgroup> in <div>");
+testText("<div>a<optgroup>123</optgroup>bc", "a\nbc", "<optgroup> in <div>");
+testText("<div>a<option></option>bc", "a\nbc", "empty <option> in <div>");
+testText("<div>a<option>123</option>bc", "a\n123\nbc", "<option> in <div>");
+
+/**** innerText on replaced element children ****/
+
+testText("<div><button>abc", "abc", "<button> contents preserved");
+testText("<div><fieldset>abc", "abc", "<fieldset> contents preserved");
+testText("<div><fieldset><legend>abc", "abc", "<fieldset> <legend> contents preserved");
+testText("<div><input type='text' value='abc'>", "", "<input> contents ignored");
+testText("<div><textarea>abc", "", "<textarea> contents ignored");
+testText("<div><select size='1'><option>abc</option><option>def", "abc\ndef", "<select size='1'> contents of options preserved");
+testText("<div><select size='2'><option>abc</option><option>def", "abc\ndef", "<select size='2'> contents of options preserved");
+testText("<div><iframe>abc", "", "<iframe> contents ignored");
+testText("<div><iframe src='data:text/html,abc'>", ""," <iframe> subdocument ignored");
+testText("<div><audio>abc", "", "<audio> contents ignored");
+testText("<div><video>abc", "", "<video> contents ignored");
+testText("<div><canvas>abc", "", "<canvas> contents ignored");
+testText("<div><img alt='abc'>", "", "<img> alt text ignored");
+
+/**** Lines around blocks ****/
+
+testText("<div>123<div>abc</div>def", "123\nabc\ndef", "Newline at block boundary");
+testText("<div>123<span style='display:block'>abc</span>def", "123\nabc\ndef", "Newline at display:block boundary");
+testText("<div>abc<div></div>def", "abc\ndef", "Empty block induces single line break");
+testText("<div>abc<div></div><div></div>def", "abc\ndef", "Consecutive empty blocks ignored");
+testText("<div><p>abc", "abc", "No blank lines around <p> alone");
+testText("<div><p>abc</p> ", "abc", "No blank lines around <p> followed by only collapsible whitespace");
+testText("<div> <p>abc</p>", "abc", "No blank lines around <p> preceded by only collapsible whitespace");
+testText("<div><p>abc<p>def", "abc\n\ndef", "Blank line between consecutive <p>s");
+testText("<div><p>abc</p> <p>def", "abc\n\ndef", "Blank line between consecutive <p>s separated only by collapsible whitespace");
+testText("<div><p>abc</p><div></div><p>def", "abc\n\ndef", "Blank line between consecutive <p>s separated only by empty block");
+testText("<div><p>abc</p><div>123</div><p>def", "abc\n\n123\n\ndef", "Blank lines between <p>s separated by non-empty block");
+testText("<div>abc<div><p>123</p></div>def", "abc\n\n123\n\ndef", "Blank lines around a <p> in its own block");
+testText("<div>abc<p>def", "abc\n\ndef", "Blank line before <p>");
+testText("<div><p>abc</p>def", "abc\n\ndef", "Blank line after <p>");
+testText("<div><p>abc<p></p><p></p><p>def", "abc\n\ndef", "One blank line between <p>s, ignoring empty <p>s");
+testText("<div style='visibility:hidden'><p><span style='visibility:visible'>abc</span></p>\n<div style='visibility:visible'>def</div>",
+ "abc\ndef", "Invisible <p> doesn't induce extra line breaks");
+testText("<div>abc<div style='margin:2em'>def", "abc\ndef", "No blank lines around <div> with margin");
+testText("<div>123<span style='display:inline-block'>abc</span>def", "123abcdef", "No newlines at display:inline-block boundary");
+testText("<div>123<span style='display:inline-block'> abc </span>def", "123abcdef", "Leading/trailing space removal at display:inline-block boundary");
+testText("<div>123<p style='margin:0px'>abc</p>def", "123\n\nabc\n\ndef", "Blank lines around <p> even without margin");
+testText("<div>123<h1>abc</h1>def", "123\nabc\ndef", "No blank lines around <h1>");
+testText("<div>123<h2>abc</h2>def", "123\nabc\ndef", "No blank lines around <h2>");
+testText("<div>123<h3>abc</h3>def", "123\nabc\ndef", "No blank lines around <h3>");
+testText("<div>123<h4>abc</h4>def", "123\nabc\ndef", "No blank lines around <h4>");
+testText("<div>123<h5>abc</h5>def", "123\nabc\ndef", "No blank lines around <h5>");
+testText("<div>123<h6>abc</h6>def", "123\nabc\ndef", "No blank lines around <h6>");
+
+/**** Spans ****/
+
+testText("<div>123<span>abc</span>def", "123abcdef", "<span> boundaries are irrelevant");
+testText("<div>123 <span>abc</span> def", "123 abc def", "<span> boundaries are irrelevant");
+testText("<div style='width:0'>123 <span>abc</span> def", "123 abc def", "<span> boundaries are irrelevant");
+testText("<div>123<em>abc</em>def", "123abcdef", "<em> gets no special treatment");
+testText("<div>123<b>abc</b>def", "123abcdef", "<b> gets no special treatment");
+testText("<div>123<i>abc</i>def", "123abcdef", "<i> gets no special treatment");
+testText("<div>123<strong>abc</strong>def", "123abcdef", "<strong> gets no special treatment");
+testText("<div>123<tt>abc</tt>def", "123abcdef", "<tt> gets no special treatment");
+testText("<div>123<code>abc</code>def", "123abcdef", "<code> gets no special treatment");
+
+/**** Soft hyphen ****/
+
+testText("<div>abc&shy;def", "abc\xADdef", "soft hyphen preserved");
+testText("<div style='width:0'>abc&shy;def", "abc\xADdef", "soft hyphen preserved");
+
+/**** Tables ****/
+
+testText("<div><table style='white-space:pre'> <td>abc</td> </table>", "abc", "Ignoring non-rendered table whitespace");
+testText("<div><table><tr><td>abc<td>def</table>", "abc\tdef", "Tab-separated table cells");
+testText("<div><table><tr><td>abc<td><td>def</table>", "abc\t\tdef", "Tab-separated table cells including empty cells");
+testText("<div><table><tr><td>abc<td><td></table>", "abc\t\t", "Tab-separated table cells including trailing empty cells");
+testText("<div><table><tr><td>abc<tr><td>def</table>", "abc\ndef", "Newline-separated table rows");
+testText("<div>abc<table><td>def</table>ghi", "abc\ndef\nghi", "Newlines around table");
+testText("<div><table style='border-collapse:collapse'><tr><td>abc<td>def</table>", "abc\tdef",
+ "Tab-separated table cells in a border-collapse table");
+testText("<div><table><tfoot>x</tfoot><tbody>y</tbody></table>", "xy", "tfoot not reordered");
+testText("<table><tfoot><tr><td>footer</tfoot><thead><tr><td style='visibility:collapse'>thead</thead><tbody><tr><td>tbody</tbody></table>",
+ "footer\n\ntbody", "");
+testText("<table><tr><td id=target>abc</td><td>def</td>", "abc", "No tab on table-cell itself");
+testText("<table><tr id=target><td>abc</td><td>def</td></tr><tr id=target><td>ghi</td><td>jkl</td></tr>", "abc\tdef", "No newline on table-row itself");
+
+/**** Table captions ****/
+
+testText("<div><table><tr><td>abc<caption>def</caption></table>", "abc\ndef", "Newline between cells and caption");
+
+/**** display:table ****/
+
+testText("<div><div class='table'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>",
+ "abc\tdef", "Tab-separated table cells");
+testText("<div><div class='table'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>",
+ "abc\ndef", "Newline-separated table rows");
+testText("<div>abc<div class='table'><span class='cell'>def</span></div>ghi", "abc\ndef\nghi", "Newlines around table");
+
+/**** display:inline-table ****/
+
+testText("<div><div class='itable'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>", "abc\tdef", "Tab-separated table cells");
+testText("<div><div class='itable'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>",
+ "abc\ndef", "Newline-separated table rows");
+testText("<div>abc<div class='itable'><span class='cell'>def</span></div>ghi", "abcdefghi", "No newlines around inline-table");
+testText("<div>abc<div class='itable'><span class='row'><span class='cell'>def</span></span>\n<span class='row'><span class='cell'>123</span></span></div>ghi",
+ "abcdef\n123ghi", "Single newline in two-row inline-table");
+
+/**** display:table-row/table-cell/table-caption ****/
+testText("<div style='display:table-row'>", "", "display:table-row on the element itself");
+testText("<div style='display:table-cell'>", "", "display:table-cell on the element itself");
+testText("<div style='display:table-caption'>", "", "display:table-caption on the element itself");
+
+/**** Lists ****/
+
+testText("<div><ol><li>abc", "abc", "<ol> list items get no special treatment");
+testText("<div><ul><li>abc", "abc", "<ul> list items get no special treatment");
+
+/**** Misc elements ****/
+
+testText("<div><script style='display:block'>abc", "abc", "display:block <script> is rendered");
+testText("<div><style style='display:block'>abc", "abc", "display:block <style> is rendered");
+testText("<div><noscript style='display:block'>abc", "", "display:block <noscript> is not rendered (it's not parsed!)");
+testText("<div><template style='display:block'>abc", "",
+ "display:block <template> contents are not rendered (the contents are in a different document)");
+testText("<div>abc<br>def", "abc\ndef", "<br> induces line break");
+testText("<div>abc<br>", "abc\n", "<br> induces line break even at end of block");
+testText("<div><br class='poke'>", "\n", "<br> content ignored");
+testText("<div>abc<hr>def", "abc\ndef", "<hr> induces line break");
+testText("<div>abc<hr><hr>def", "abc\ndef", "<hr><hr> induces just one line break");
+testText("<div>abc<hr><hr><hr>def", "abc\ndef", "<hr><hr><hr> induces just one line break");
+testText("<div><hr class='poke'>", "abc", "<hr> content rendered");
+testText("<div>abc<!--comment-->def", "abcdef", "comment ignored");
+testText("<br>", "", "<br>");
+testText("<p>", "", "empty <p>");
+testText("<div>", "", "empty <div>");
+
+/**** text-transform ****/
+
+testText("<div><div style='text-transform:uppercase'>abc", "ABC", "text-transform is applied");
+testText("<div><div style='text-transform:uppercase'>Ma\xDF", "MASS", "text-transform handles es-zet");
+testText("<div><div lang='tr' style='text-transform:uppercase'>i \u0131", "\u0130 I", "text-transform handles Turkish casing");
+
+/**** block-in-inline ****/
+
+testText("<div>abc<span>123<div>456</div>789</span>def", "abc123\n456\n789def", "block-in-inline doesn't add unnecessary newlines");
+
+/**** floats ****/
+
+testText("<div>abc<div style='float:left'>123</div>def", "abc\n123\ndef", "floats induce a block boundary");
+testText("<div>abc<span style='float:left'>123</span>def", "abc\n123\ndef", "floats induce a block boundary");
+testText("<div style='float:left'>123", "123", "float on the element itself");
+
+/**** position ****/
+
+testText("<div>abc<div style='position:absolute'>123</div>def", "abc\n123\ndef", "position:absolute induces a block boundary");
+testText("<div>abc<span style='position:absolute'>123</span>def", "abc\n123\ndef", "position:absolute induces a block boundary");
+testText("<div style='position:absolute'>123", "123", "position:absolute on the element itself");
+testText("<div>abc<div style='position:relative'>123</div>def", "abc\n123\ndef", "position:relative has no effect");
+testText("<div>abc<span style='position:relative'>123</span>def", "abc123def", "position:relative has no effect");
+
+/**** text-overflow:ellipsis ****/
+
+testText("<div style='overflow:hidden'>abc", "abc", "overflow:hidden ignored");
+// XXX Chrome skips content with width:0 or height:0 and overflow:hidden;
+// should we spec that?
+testText("<div style='width:0; overflow:hidden'>abc", "abc", "overflow:hidden ignored even with zero width");
+testText("<div style='height:0; overflow:hidden'>abc", "abc", "overflow:hidden ignored even with zero height");
+testText("<div style='width:0; overflow:hidden; text-overflow:ellipsis'>abc", "abc", "text-overflow:ellipsis ignored");
+
+/**** Support on non-HTML elements ****/
+
+testText("<svg>abc", undefined, "innerText not supported on SVG elements");
+testText("<math>abc", undefined, "innerText not supported on MathML elements");
+
+/**** Ruby ****/
+
+testText("<div><ruby>abc<rt>def</rt></ruby>", "abcdef", "<rt> and no <rp>");
+testText("<div><ruby>abc<rp>(</rp><rt>def</rt><rp>)</rp></ruby>", "abcdef", "<rp>");
+testText("<div><rp>abc</rp>", "", "Lone <rp>");
+testText("<div><rp style='visibility:hidden'>abc</rp>", "", "visibility:hidden <rp>");
+testText("<div><rp style='display:block'>abc</rp>def", "abc\ndef", "display:block <rp>");
+testText("<div><rp style='display:block'> abc </rp>def", "abc\ndef", "display:block <rp> with whitespace");
+testText("<div><select class='poke-rp'></select>", "", "<rp> in a <select>");
+
+/**** Shadow DOM ****/
+
+if ("attachShadow" in document.body) {
+ testText("<div class='shadow'>", "", "Shadow DOM contents ignored");
+ testText("<div><div class='shadow'>", "", "Shadow DOM contents ignored");
+}
+
+/**** Flexbox ****/
+
+if (CSS.supports('display', 'flex')) {
+ testText("<div style='display:flex'><div style='order:1'>1</div><div>2</div></div>",
+ "1\n2", "CSS 'order' property ignored");
+ testText("<div style='display:flex'><span>1</span><span>2</span></div>",
+ "1\n2", "Flex items blockified");
+}
+
+/**** Grid ****/
+
+if (CSS.supports('display', 'grid')) {
+ testText("<div style='display:grid'><div style='order:1'>1</div><div>2</div></div>",
+ "1\n2", "CSS 'order' property ignored");
+ testText("<div style='display:grid'><span>1</span><span>2</span></div>",
+ "1\n2", "Grid items blockified");
+}
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter.html b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter.html
new file mode 100644
index 0000000000..ffb3d34fe9
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/getter.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<title>innerText/outerText getter test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.before::before { content:'abc'; }
+.table { display:table; }
+.itable { display:inline-table; }
+.row { display:table-row; }
+.cell { display:table-cell; }
+.first-line-uppercase::first-line { text-transform:uppercase; }
+.first-letter-uppercase::first-letter { text-transform:uppercase; }
+.first-letter-float::first-letter { float:left; }
+</style>
+<div id="container"></div>
+<svg id="svgContainer"></svg>
+<script>
+let container = document.querySelector('#container');
+let svgContainer = document.querySelector('#svgContainer');
+function testText(html, expectedPlain, msg) {
+ textTextInContainer(container, html, expectedPlain, msg);
+}
+function testTextInSVG(html, expectedPlain, msg) {
+ textTextInContainer(svgContainer, html, expectedPlain, msg);
+}
+function textTextInContainer(cont, html, expectedPlain, msg) {
+ test(function() {
+ container.innerHTML = html;
+ if (cont != container) {
+ while (container.firstChild) {
+ cont.appendChild(container.firstChild);
+ }
+ }
+ var e = document.getElementById('target');
+ if (!e) {
+ e = cont.firstChild;
+ }
+ var pokes = document.getElementsByClassName('poke');
+ for (var i = 0; i < pokes.length; ++i) {
+ pokes[i].textContent = 'abc';
+ }
+ ['rp', 'optgroup', 'div'].forEach(function(tag) {
+ pokes = document.getElementsByClassName('poke-' + tag);
+ for (var i = 0; i < pokes.length; ++i) {
+ var el = document.createElement(tag);
+ el.textContent = "abc";
+ pokes[i].appendChild(el);
+ }
+ });
+ var shadows = document.getElementsByClassName('shadow');
+ for (var i = 0; i < shadows.length; ++i) {
+ var s = shadows[i].attachShadow({ mode: "open" });
+ s.textContent = 'abc';
+ }
+ while (e && e.nodeType != Node.ELEMENT_NODE) {
+ e = e.nextSibling;
+ }
+ assert_equals(e.innerText, expectedPlain, "innerText");
+ assert_equals(e.outerText, expectedPlain, "outerText");
+ cont.textContent = '';
+ }, msg + ' (' + format_value(html) + ')');
+}
+</script>
+<script src="getter-tests.js"></script>
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-domnoderemoved-crash.html b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-domnoderemoved-crash.html
new file mode 100644
index 0000000000..94043caf69
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-domnoderemoved-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1411135">
+
+<div id=parentelement><div id=childelement>hello world</div></div>
+
+<script>
+ let removed = false;
+ childelement.addEventListener('DOMNodeRemoved', () => {
+ if (!removed) {
+ removed = true;
+ childelement.remove();
+ }
+ });
+ parentelement.innerText = 'hello world';
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter-tests.js b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter-tests.js
new file mode 100644
index 0000000000..99ae5ec185
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter-tests.js
@@ -0,0 +1,42 @@
+testText("<div>", "abc", "abc", "Simplest possible test");
+testHTML("<div>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in non-white-space:pre elements");
+testHTML("<pre>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <pre> element");
+testHTML("<textarea>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <textarea> element");
+testHTML("<div style='white-space:pre'>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in white-space:pre element");
+testHTML("<div>", "abc\rdef", "abc<br>def", "CRs convert to <br> in non-white-space:pre elements");
+testHTML("<pre>", "abc\rdef", "abc<br>def", "CRs convert to <br> in <pre> element");
+testHTML("<div>", "abc\r\ndef", "abc<br>def", "Newline/CR pair converts to <br> in non-white-space:pre element");
+testHTML("<div>", "abc\n\ndef", "abc<br><br>def", "Newline/newline pair converts to two <br>s in non-white-space:pre element");
+testHTML("<div>", "abc\r\rdef", "abc<br><br>def", "CR/CR pair converts to two <br>s in non-white-space:pre element");
+testHTML("<div style='white-space:pre'>", "abc\rdef", "abc<br>def", "CRs convert to <br> in white-space:pre element");
+testText("<div>", "abc<def", "abc<def", "< preserved");
+testText("<div>", "abc>def", "abc>def", "> preserved");
+testText("<div>", "abc&", "abc&", "& preserved");
+testText("<div>", "abc\"def", "abc\"def", "\" preserved");
+testText("<div>", "abc\'def", "abc\'def", "\' preserved");
+testHTML("<svg>", "abc", "", "innerText not supported on SVG elements");
+testHTML("<math>", "abc", "", "innerText not supported on MathML elements");
+testText("<div>", "abc\0def", "abc\0def", "Null characters preserved");
+testText("<div>", "abc\tdef", "abc\tdef", "Tabs preserved");
+testText("<div>", " abc", " abc", "Leading whitespace preserved");
+testText("<div>", "abc ", "abc ", "Trailing whitespace preserved");
+testText("<div>", "abc def", "abc def", "Whitespace not compressed");
+testText("<div>abc\n\n", "abc", "abc", "Existing text deleted");
+testText("<div><br>", "abc", "abc", "Existing <br> deleted");
+testHTML("<div>", "", "", "Assigning the empty string");
+testHTML("<div>", null, "", "Assigning null");
+testHTML("<div>", undefined, "undefined", "Assigning undefined");
+testHTML("<div>", "\rabc", "<br>abc", "Start with CR");
+testHTML("<div>", "\nabc", "<br>abc", "Start with LF");
+testHTML("<div>", "\r\nabc", "<br>abc", "Start with CRLF");
+testHTML("<div>", "abc\r", "abc<br>", "End with CR");
+testHTML("<div>", "abc\n", "abc<br>", "End with LF");
+testHTML("<div>", "abc\r\n", "abc<br>", "End with CRLF");
+
+// Setting innerText on these should not throw
+["area", "base", "basefont", "bgsound", "br", "col", "embed", "frame", "hr",
+"image", "img", "input", "keygen", "link", "menuitem", "meta", "param",
+"source", "track", "wbr", "colgroup", "frameset", "head", "html", "table",
+"tbody", "tfoot", "thead", "tr"].forEach(function(tag) {
+ testText(document.createElement(tag), "abc", "abc", "innerText on <" + tag + "> element");
+});
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter.html b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter.html
new file mode 100644
index 0000000000..a835a164ed
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/innertext-setter.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<title>innerText setter test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="container"></div>
+<script>
+// As of March 2017, WebKit and Blink have inconsistent results depending on
+// rendered or not. setupTest() tests a rendered case, and setupTestDetached()
+// tests a not-rendered case.
+
+function setupTest(context, plain) {
+ var container = document.getElementById("container");
+ // context is either a string or an element node
+ if (typeof context === "string") {
+ container.innerHTML = context;
+ } else {
+ container.innerHTML = "";
+ container.appendChild(context);
+ }
+ var e = container.firstChild;
+ while (e && e.nodeType != Node.ELEMENT_NODE) {
+ e = e.nextSibling;
+ }
+ e.offsetWidth;
+ var oldChild = e.firstChild;
+ e.innerText = plain;
+ return [e, oldChild];
+}
+
+function setupTestDetached(context, plain) {
+ var detachedContainer = document.createElement("div");
+ // context is either a string or an element node
+ if (typeof context === "string") {
+ detachedContainer.innerHTML = context;
+ } else {
+ detachedContainer.innerHTML = "";
+ detachedContainer.appendChild(context);
+ }
+ var e = detachedContainer.firstChild;
+ while (e && e.nodeType != Node.ELEMENT_NODE) {
+ e = e.nextSibling;
+ }
+ var oldChild = e.firstChild;
+ e.innerText = plain;
+ return [e, oldChild];
+}
+
+function assertNewSingleTextNode(newChild, expectedText, oldChild) {
+ assert_not_equals(newChild, null, "Should have a child");
+ assert_equals(newChild.nodeType, Node.TEXT_NODE, "Child should be a text node");
+ assert_equals(newChild.nextSibling, null, "Should have only one child");
+ assert_equals(newChild.data, expectedText);
+ assert_not_equals(newChild, oldChild, "Child should be a *new* text node");
+}
+
+function assertNoEmptyTextChild(parent) {
+ for (var child = parent.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType === Node.TEXT_NODE) {
+ assert_not_equals(child.data, "", "Should not have empty text nodes");
+ }
+ }
+}
+
+function testText(context, plain, expectedText, msg) {
+ test(function(){
+ var arr = setupTest(context, plain);
+ assertNewSingleTextNode(arr[0].firstChild, expectedText, arr[1]);
+ }, msg);
+ test(function() {
+ var arr = setupTestDetached(context, plain);
+ assertNewSingleTextNode(arr[0].firstChild, expectedText, arr[1]);
+ }, msg + ", detached");
+}
+
+function testHTML(context, plain, expectedHTML, msg) {
+ test(function(){
+ var e = setupTest(context, plain)[0];
+ assert_equals(e.innerHTML, expectedHTML);
+ assertNoEmptyTextChild(e);
+ }, msg);
+ test(function() {
+ var e = setupTestDetached(context, plain)[0];
+ assert_equals(e.innerHTML, expectedHTML);
+ assertNoEmptyTextChild(e);
+ }, msg + ", detached");
+}
+</script>
+<script src="innertext-setter-tests.js"></script>
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/multiple-text-nodes.window.js b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/multiple-text-nodes.window.js
new file mode 100644
index 0000000000..07c55e9669
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/multiple-text-nodes.window.js
@@ -0,0 +1,16 @@
+async_test(t => {
+ const div = document.body.appendChild(document.createElement("div"));
+ t.add_cleanup(() => div.remove());
+ const t1 = div.appendChild(new Text(""));
+ div.appendChild(new Text(""));
+ const t2 = div.appendChild(new Text(""));
+ const t3 = div.appendChild(new Text(""));
+ t.step_timeout(() => {
+ t1.data = "X";
+ t2.data = " ";
+ t3.data = "Y";
+ assert_equals(div.innerText, "X Y", "innerText");
+ assert_equals(div.outerText, "X Y", "outerText");
+ t.done();
+ }, 100);
+}, "Ensure multiple text nodes get rendered properly");
diff --git a/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/outertext-setter.html b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/outertext-setter.html
new file mode 100644
index 0000000000..953bedc9a8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/the-innertext-and-outertext-properties/outertext-setter.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<title>outerText setter test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<ul>
+ <li>A <span id="testReplacePrevious">B</span></li>
+ <li><span id="testReplaceFollowing">A</span> B</li>
+ <li>A <span id="testReplaceBoth">B</span> C</li>
+ <li><span id="testRemove">Testing</span> removing node using outerText.</li>
+ <li><span id="testNewlines">Replace this child with lots of newlines</span></li>
+</ul>
+
+<div id="container"></div>
+
+<script>
+"use strict";
+
+test(() => {
+ const node = document.getElementById("testReplacePrevious");
+ const parent = node.parentNode;
+
+ node.outerText = "Replaced";
+
+ assert_equals(parent.innerHTML, "A Replaced");
+ assert_equals(parent.childNodes.length, 1, "It got merged with the previous text node");
+}, "Replacing a node and merging with the previous text node");
+
+test(() => {
+ const node = document.getElementById("testReplaceFollowing");
+ const parent = node.parentNode;
+
+ node.outerText = "Replaced";
+
+ assert_equals(parent.innerHTML, "Replaced B");
+ assert_equals(parent.childNodes.length, 1, "It got merged with the following text node");
+}, "Replacing a node and merging with the following text node");
+
+test(() => {
+ const node = document.getElementById("testReplaceBoth");
+ const parent = node.parentNode;
+
+ node.outerText = "Replaced";
+
+ assert_equals(parent.innerHTML, "A Replaced C");
+ assert_equals(parent.childNodes.length, 1, "It got merged with the previous and following text node");
+}, "Replacing a node and merging with the previous and following text node");
+
+test(t => {
+ const container = document.getElementById("container");
+ t.add_cleanup(() => { container.textContent = ""; });
+
+ container.append("A", "B", document.createElement("span"), "D", "E");
+ assert_equals(container.childNodes.length, 5, "Precondition check: five separate nodes");
+
+ const node = container.childNodes[2];
+ node.outerText = "Replaced";
+
+ assert_equals(container.innerHTML, "ABReplacedDE");
+ assert_equals(container.childNodes.length, 3, "It got merged with the previous and following text node");
+ assert_equals(container.childNodes[0].data, "A");
+ assert_equals(container.childNodes[1].data, "BReplacedD");
+ assert_equals(container.childNodes[2].data, "E");
+}, "Only merges with the previous and following text nodes, does not completely normalize");
+
+test(t => {
+ const container = document.getElementById("container");
+ t.add_cleanup(() => { container.textContent = ""; });
+
+ container.append(document.createElement("span"));
+ const node = container.childNodes[0];
+ node.outerText = "";
+
+ assert_equals(container.childNodes.length, 1, "Creates text node for the empty string");
+ assert_equals(container.childNodes[0].data, "");
+}, "Empty string");
+
+test(t => {
+ const container = document.getElementById("container");
+ t.add_cleanup(() => { container.textContent = ""; });
+
+ container.append("1", "2", document.createElement("span"), "3", "4");
+ const node = container.childNodes[2];
+ node.outerText = "";
+
+ assert_equals(container.childNodes.length, 3, "It got merged with the previous and following text node");
+ assert_equals(container.childNodes[0].data, "1");
+ assert_equals(container.childNodes[1].data, "23");
+ assert_equals(container.childNodes[2].data, "4");
+}, "Empty string with surrounding text nodes");
+
+test(t => {
+ const node = document.getElementById("testNewlines");
+ const parent = node.parentNode;
+
+ node.outerText = "\n\r\n\r";
+
+ assert_equals(parent.innerHTML, "<br><br><br>");
+ assert_equals(parent.childNodes.length, 3);
+ assert_equals(parent.childNodes[0].localName, "br", "node 1");
+ assert_equals(parent.childNodes[1].localName, "br", "node 2");
+ assert_equals(parent.childNodes[2].localName, "br", "node 3");
+}, "Setting outerText to a bunch of newlines creates a bunch of <br>s with no text nodes");
+
+test(() => {
+ const node = document.getElementById("testRemove");
+ const parent = node.parentNode;
+
+ node.outerText = "";
+
+ assert_equals(parent.innerHTML, " removing node using outerText.");
+}, "Removing a node");
+
+test(() => {
+ const node = document.createElement("span");
+
+ assert_throws_dom("NoModificationAllowedError", () => { node.outerText = ""; });
+}, "Detached node");
+
+testText("<div>", "abc", "abc", "Simplest possible test");
+testHTML("<div>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in non-white-space:pre elements");
+testHTML("<pre>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <pre> element");
+testHTML("<textarea>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <textarea> element");
+testHTML("<div style='white-space:pre'>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in white-space:pre element");
+testHTML("<div>", "abc\rdef", "abc<br>def", "CRs convert to <br> in non-white-space:pre elements");
+testHTML("<pre>", "abc\rdef", "abc<br>def", "CRs convert to <br> in <pre> element");
+testHTML("<div>", "abc\r\ndef", "abc<br>def", "Newline/CR pair converts to <br> in non-white-space:pre element");
+testHTML("<div>", "abc\n\ndef", "abc<br><br>def", "Newline/newline pair converts to two <br>s in non-white-space:pre element");
+testHTML("<div>", "abc\r\rdef", "abc<br><br>def", "CR/CR pair converts to two <br>s in non-white-space:pre element");
+testHTML("<div style='white-space:pre'>", "abc\rdef", "abc<br>def", "CRs convert to <br> in white-space:pre element");
+testText("<div>", "abc<def", "abc<def", "< preserved");
+testText("<div>", "abc>def", "abc>def", "> preserved");
+testText("<div>", "abc&", "abc&", "& preserved");
+testText("<div>", "abc\"def", "abc\"def", "\" preserved");
+testText("<div>", "abc\'def", "abc\'def", "\' preserved");
+testHTML("<svg>", "abc", "<svg></svg>", "outerText not supported on SVG elements");
+testHTML("<math>", "abc", "<math></math>", "outerText not supported on MathML elements");
+testText("<div>", "abc\0def", "abc\0def", "Null characters preserved");
+testText("<div>", "abc\tdef", "abc\tdef", "Tabs preserved");
+testText("<div>", " abc", " abc", "Leading whitespace preserved");
+testText("<div>", "abc ", "abc ", "Trailing whitespace preserved");
+testText("<div>", "abc def", "abc def", "Whitespace not compressed");
+testText("<div>abc\n\n", "abc", "abc", "Existing text deleted");
+testText("<div><br>", "abc", "abc", "Existing <br> deleted");
+testHTML("<div>", "", "", "Assigning the empty string");
+testHTML("<div>", null, "", "Assigning null");
+testHTML("<div>", undefined, "undefined", "Assigning undefined");
+testHTML("<div>", "\rabc", "<br>abc", "Start with CR");
+testHTML("<div>", "\nabc", "<br>abc", "Start with LF");
+testHTML("<div>", "\r\nabc", "<br>abc", "Start with CRLF");
+testHTML("<div>", "abc\r", "abc<br>", "End with CR");
+testHTML("<div>", "abc\n", "abc<br>", "End with LF");
+testHTML("<div>", "abc\r\n", "abc<br>", "End with CRLF");
+
+function testText(startingHTML, outerText, expected, description) {
+ test(t => {
+ const container = document.getElementById("container");
+ t.add_cleanup(() => { container.textContent = ""; });
+
+ container.innerHTML = startingHTML;
+ const elementToReplace = container.firstElementChild;
+
+ elementToReplace.outerText = outerText;
+ assert_equals(container.textContent, expected);
+ }, description);
+}
+
+function testHTML(startingHTML, outerText, expected, description) {
+ test(t => {
+ const container = document.getElementById("container");
+ t.add_cleanup(() => { container.textContent = ""; });
+
+ container.innerHTML = startingHTML;
+ const elementToReplace = container.firstElementChild;
+
+ elementToReplace.outerText = outerText;
+ assert_equals(container.innerHTML, expected);
+ }, description);
+}
+</script>
diff --git a/testing/web-platform/tests/html/dom/elements/wai-aria/README.md b/testing/web-platform/tests/html/dom/elements/wai-aria/README.md
new file mode 100644
index 0000000000..bea30702d1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/elements/wai-aria/README.md
@@ -0,0 +1 @@
+The test suite for WAI-ARIA is available at: <https://www.w3.org/WAI/PF/testharness/>.
diff --git a/testing/web-platform/tests/html/dom/historical.html b/testing/web-platform/tests/html/dom/historical.html
new file mode 100644
index 0000000000..396e57a391
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/historical.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<title>Historical HTML APIs</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<attachment></attachment>
+<applet name=war align=left></applet>
+<layer></layer>
+<nolayer></nolayer>
+<script>
+test(() => {
+ assert_array_equals(document.applets, []);
+}, "document.applets is always empty");
+
+[
+ "attachment",
+ "applet",
+ "layer",
+ "nolayer"
+].forEach(name => {
+ test(() => {
+ const ap = document.getElementsByTagName(name)[0];
+ assert_true(ap instanceof window.HTMLUnknownElement);
+ }, `<${name}> is HTMLUnknownElement`);
+});
+
+test(() => {
+ assert_equals(self.HTMLAppletElement, undefined);
+}, "HTMLAppletElement is no more")
+
+test(() => {
+ assert_equals(document.all.war, undefined);
+}, "document.all cannot find applet")
+
+test(() => {
+ assert_equals(document.war, undefined);
+}, "document cannot find applet")
+
+test(() => {
+ assert_equals(self.war, undefined);
+}, "window cannot find applet")
+
+test(() => {
+ assert_equals(self.getComputedStyle(document.getElementsByTagName("applet")[0], "").cssFloat, "none");
+}, "applet is not styled")
+
+// removed in https://github.com/whatwg/html/commit/e383ae23776362cafb2fb4bbba70c8c9080d4b0f
+test(() => {
+ assert_false("HTMLTableDataCellElement" in window);
+}, "HTMLTableDataCellElement interface is removed")
+
+test(() => {
+ assert_false("HTMLTableHeaderCellElement" in window);
+}, "HTMLTableHeaderCellElement interface is removed")
+</script>
diff --git a/testing/web-platform/tests/html/dom/idlharness-shadowrealm.window.js b/testing/web-platform/tests/html/dom/idlharness-shadowrealm.window.js
new file mode 100644
index 0000000000..ef71b74e50
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/idlharness-shadowrealm.window.js
@@ -0,0 +1,2 @@
+// META: script=/resources/idlharness-shadowrealm.js
+idl_test_shadowrealm(["html"], ["wai-aria", "SVG", "cssom", "touch-events", "uievents", "dom", "xhr", "FileAPI", "mediacapture-streams", "performance-timeline"]);
diff --git a/testing/web-platform/tests/html/dom/idlharness.https.html b/testing/web-platform/tests/html/dom/idlharness.https.html
new file mode 100644
index 0000000000..7d693d3c0a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/idlharness.https.html
@@ -0,0 +1,241 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTML IDL tests</title>
+<meta name=timeout content=long>
+<meta name="variant" content="?include=(Document|Window)">
+<meta name="variant" content="?include=HTML.*">
+<meta name="variant" content="?exclude=(Document|Window|HTML.*)">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/subset-tests-by-key.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src=/resources/WebIDLParser.js></script>
+<script src=/resources/idlharness.js></script>
+
+<h1>HTML IDL tests</h1>
+<div id=log></div>
+
+<script>
+"use strict";
+var errorVideo; // used to get a MediaError object
+var iframe; // used to get a Document object (can't use `document` because some test clears the page)
+setup(function() {
+ errorVideo = document.createElement('video');
+ errorVideo.src = 'data:,';
+ errorVideo.preload = 'auto';
+ iframe = document.createElement('iframe');
+ iframe.hidden = true;
+ document.body.appendChild(iframe);
+});
+
+function createInput(type) {
+ var input = document.createElement('input');
+ input.type = type;
+ return input;
+}
+
+const waitForLoad = new Promise(resolve => { addEventListener('load', resolve); })
+
+idl_test(
+ ['html'],
+ ['wai-aria', 'SVG', 'cssom', 'touch-events', 'uievents', 'dom', 'xhr', 'FileAPI', 'mediacapture-streams', 'performance-timeline'],
+ async idlArray => {
+ self.documentWithHandlers = new Document();
+ const handler = function(e) {};
+ for (const callback of idlArray.members['GlobalEventHandlers'].members) {
+ if (callback.idlType && callback.idlType.idlType === 'EventHandler') {
+ documentWithHandlers[callback.name] = handler;
+ }
+ }
+ idlArray.add_untested_idls('typedef Window WindowProxy;');
+
+ idlArray.add_objects({
+ NodeList: ['document.getElementsByName("name")'],
+ HTMLAllCollection: ['document.all'],
+ HTMLFormControlsCollection: ['document.createElement("form").elements'],
+ RadioNodeList: [],
+ HTMLOptionsCollection: ['document.createElement("select").options'],
+ DOMStringMap: ['document.head.dataset'],
+ Transferable: [],
+ Document: ['iframe.contentDocument', 'new Document()', 'documentWithHandlers'],
+ XMLDocument: ['document.implementation.createDocument(null, "", null)'],
+ HTMLElement: [
+ 'document.createElement("noscript")',
+ 'Object.assign(document.createElement("noscript"),{popover:"auto"})',
+ ], // more tests in html/semantics/interfaces.js
+ HTMLUnknownElement: ['document.createElement("bgsound")'], // more tests in html/semantics/interfaces.js
+ HTMLHtmlElement: ['document.createElement("html")'],
+ HTMLHeadElement: ['document.createElement("head")'],
+ HTMLTitleElement: ['document.createElement("title")'],
+ HTMLBaseElement: ['document.createElement("base")'],
+ HTMLLinkElement: ['document.createElement("link")'],
+ HTMLMetaElement: ['document.createElement("meta")'],
+ HTMLStyleElement: ['document.createElement("style")'],
+ HTMLScriptElement: ['document.createElement("script")'],
+ HTMLBodyElement: ['document.createElement("body")'],
+ HTMLHeadingElement: ['document.createElement("h1")'],
+ HTMLParagraphElement: ['document.createElement("p")'],
+ HTMLHRElement: ['document.createElement("hr")'],
+ HTMLPreElement: [
+ 'document.createElement("pre")',
+ 'document.createElement("listing")',
+ 'document.createElement("xmp")',
+ ],
+ HTMLQuoteElement: [
+ 'document.createElement("blockquote")',
+ 'document.createElement("q")',
+ ],
+ HTMLOlistElement: ['document.createElement("ol")'],
+ HTMLUlistElement: ['document.createElement("ul")'],
+ HTMLLIElement: ['document.createElement("li")'],
+ HTMLDlistElement: ['document.createElement("dl")'],
+ HTMLDivElement: ['document.createElement("div")'],
+ HTMLAnchorElement: ['document.createElement("a")'],
+ HTMLDataElement: ['document.createElement("data")'],
+ HTMLTimeElement: ['document.createElement("time")'],
+ HTMLSpanElement: ['document.createElement("span")'],
+ HTMLBRElement: ['document.createElement("br")'],
+ HTMLModElement: [
+ 'document.createElement("ins")',
+ 'document.createElement("del")',
+ ],
+ HTMLPictureElement: ['document.createElement("picture")'],
+ HTMLImageElement: ['document.createElement("img")', 'new Image()'],
+ HTMLIFrameElement: ['document.createElement("iframe")'],
+ HTMLEmbedElement: ['document.createElement("embed")'],
+ HTMLObjectElement: ['document.createElement("object")'],
+ HTMLParamElement: ['document.createElement("param")'],
+ HTMLVideoElement: ['document.createElement("video")'],
+ HTMLAudioElement: ['document.createElement("audio")', 'new Audio()'],
+ HTMLSourceElement: ['document.createElement("source")'],
+ HTMLTrackElement: ['document.createElement("track")'],
+ HTMLMediaElement: [],
+ MediaError: ['errorVideo.error'],
+ AudioTrackList: [],
+ AudioTrack: [],
+ VideoTrackList: [],
+ VideoTrack: [],
+ TextTrackList: ['document.createElement("video").textTracks'],
+ TextTrack: ['document.createElement("track").track'],
+ TextTrackCueList: ['document.createElement("video").addTextTrack("subtitles").cues'],
+ TextTrackCue: [],
+ DataCue: [],
+ TimeRanges: ['document.createElement("video").buffered'],
+ TrackEvent: ['new TrackEvent("addtrack", {track:document.createElement("track").track})'],
+ HTMLTemplateElement: ['document.createElement("template")'],
+ HTMLSlotElement: ['document.createElement("slot")'],
+ HTMLCanvasElement: ['document.createElement("canvas")'],
+ CanvasRenderingContext2D: ['document.createElement("canvas").getContext("2d")'],
+ CanvasGradient: [],
+ CanvasPattern: [],
+ ToggleEvent: ['new ToggleEvent("beforetoggle")'],
+ TextMetrics: [],
+ ImageData: ['new ImageData(10, 10)'],
+ HTMLMapElement: ['document.createElement("map")'],
+ HTMLAreaElement: ['document.createElement("area")'],
+ HTMLTableElement: ['document.createElement("table")'],
+ HTMLTableCaptionElement: ['document.createElement("caption")'],
+ HTMLTableColElement: [
+ 'document.createElement("colgroup")',
+ 'document.createElement("col")',
+ ],
+ HTMLTableSectionElement: [
+ 'document.createElement("tbody")',
+ 'document.createElement("thead")',
+ 'document.createElement("tfoot")',
+ ],
+ HTMLTableRowElement: ['document.createElement("tr")'],
+ HTMLTableCellElement: [
+ 'document.createElement("td")',
+ 'document.createElement("th")',
+ ],
+ HTMLFormElement: ['document.createElement("form")'],
+ HTMLFieldsetElement: ['document.createElement("fieldset")'],
+ HTMLLegendElement: ['document.createElement("legend")'],
+ HTMLLabelElement: ['document.createElement("label")'],
+ HTMLInputElement: [
+ 'document.createElement("input")',
+ 'createInput("text")',
+ 'createInput("hidden")',
+ 'createInput("search")',
+ 'createInput("tel")',
+ 'createInput("url")',
+ 'createInput("email")',
+ 'createInput("password")',
+ 'createInput("date")',
+ 'createInput("month")',
+ 'createInput("week")',
+ 'createInput("time")',
+ 'createInput("datetime-local")',
+ 'createInput("number")',
+ 'createInput("range")',
+ 'createInput("color")',
+ 'createInput("checkbox")',
+ 'createInput("radio")',
+ 'createInput("file")',
+ 'createInput("submit")',
+ 'createInput("image")',
+ 'createInput("reset")',
+ 'createInput("button")'
+ ],
+ HTMLButtonElement: ['document.createElement("button")'],
+ HTMLSelectElement: ['document.createElement("select")'],
+ HTMLDataListElement: ['document.createElement("datalist")'],
+ HTMLOptGroupElement: ['document.createElement("optgroup")'],
+ HTMLOptionElement: ['document.createElement("option")', 'new Option()'],
+ HTMLTextAreaElement: ['document.createElement("textarea")'],
+ HTMLOutputElement: ['document.createElement("output")'],
+ HTMLProgressElement: ['document.createElement("progress")'],
+ HTMLMeterElement: ['document.createElement("meter")'],
+ ValidityState: ['document.createElement("input").validity'],
+ FormDataEvent: ['new FormDataEvent("formdata", { formData: new FormData() })'],
+ HTMLDetailsElement: ['document.createElement("details")'],
+ HTMLMenuElement: ['document.createElement("menu")'],
+ Window: ['window'],
+ BarProp: [],
+ History: ['window.history'],
+ Location: ['window.location'],
+ PopStateEvent: ['new PopStateEvent("popstate", { data: {} })'],
+ HashChangeEvent: [],
+ PageTransitionEvent: [],
+ BeforeUnloadEvent: [],
+ WindowModal: [],
+ DOMParser: ['new DOMParser()'],
+ Navigator: ['window.navigator'],
+ External: ['window.external'],
+ DataTransfer: [],
+ DataTransferItemList: [],
+ DataTransferItem: [],
+ DragEvent: [],
+ NavigatorUserMediaError: [],
+ MediaStream: [],
+ MediaStreamTrack: [],
+ MediaStreamRecorder: [],
+ PeerConnection: [],
+ MediaStreamEvent: [],
+ ErrorEvent: [],
+ EventSource: ['new EventSource("http://invalid")'],
+ AbstractWorker: [],
+ Worker: [],
+ SharedWorker: [],
+ MessageEvent: ['new MessageEvent("message", { data: 5 })'],
+ MessageChannel: [],
+ MessagePort: [],
+ HTMLMarqueeElement: ['document.createElement("marquee")'],
+ HTMLFrameSetElement: ['document.createElement("frameset")'],
+ HTMLFrameElement: ['document.createElement("frame")'],
+ HTMLDirectoryElement: ['document.createElement("dir")'],
+ HTMLFontElement: ['document.createElement("font")'],
+ DOMStringList: ['location.ancestorOrigins'],
+ Storage: [
+ 'localStorage',
+ 'sessionStorage',
+ ],
+ StorageEvent: ['new StorageEvent("storage")']
+ });
+ idlArray.prevent_multiple_testing('HTMLElement');
+ await waitForLoad;
+ }
+);
+
+</script>
diff --git a/testing/web-platform/tests/html/dom/idlharness.worker.js b/testing/web-platform/tests/html/dom/idlharness.worker.js
new file mode 100644
index 0000000000..88942ddfea
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/idlharness.worker.js
@@ -0,0 +1,22 @@
+"use strict";
+
+importScripts("/resources/testharness.js");
+importScripts("/resources/WebIDLParser.js", "/resources/idlharness.js");
+
+idl_test(
+ ["html"],
+ ["wai-aria", "dom", "cssom", "touch-events", "uievents", "performance-timeline"],
+ idlArray => {
+ idlArray.add_untested_idls('typedef Window WindowProxy;');
+ idlArray.add_objects({
+ WorkerLocation: ['self.location'],
+ WorkerNavigator: ['self.navigator'],
+ EventSource: ['new EventSource("http://invalid")'],
+ Worker: [],
+ MessageEvent: ['new MessageEvent("message", { data: 5 })'],
+ DedicatedWorkerGlobalScope: ['self'],
+ });
+ }
+);
+
+done();
diff --git a/testing/web-platform/tests/html/dom/new-harness.js b/testing/web-platform/tests/html/dom/new-harness.js
new file mode 100644
index 0000000000..49f8e40ad7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/new-harness.js
@@ -0,0 +1,11 @@
+// We override only the things we need to -- the rest we'll just inherit from
+// original-harness.js. Polymorphism, kind of.
+ReflectionHarness.conformanceTesting = true;
+
+ReflectionHarness.test = function(fun, description) {
+ test(fun, this.getTypeDescription() + ": " + description);
+}
+
+ReflectionHarness.assertEquals = assert_equals;
+
+ReflectionHarness.assertThrows = assert_throws_dom;
diff --git a/testing/web-platform/tests/html/dom/original-harness.js b/testing/web-platform/tests/html/dom/original-harness.js
new file mode 100644
index 0000000000..89a8067033
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/original-harness.js
@@ -0,0 +1,339 @@
+var ReflectionHarness = {};
+
+// @private
+ReflectionHarness.passed = document.getElementById("passed");
+ReflectionHarness.failed = document.getElementById("failed");
+
+/**
+ * In conformance testing mode, all tests will be run. Otherwise, we'll skip
+ * tests for attributes that have an entirely incorrect type.
+ */
+ReflectionHarness.conformanceTesting = false;
+
+/**
+ * Returns a string representing val. Basically just adds quotes for strings,
+ * and passes through other recognized types literally.
+ *
+ * @public
+ */
+ReflectionHarness.stringRep = function(val) {
+ if (val === null) {
+ // typeof is object, so the switch isn't useful
+ return "null";
+ }
+ // In JavaScript, -0 === 0 and String(-0) == "0", so we have to
+ // special-case.
+ if (val === -0 && 1/val === -Infinity) {
+ return "-0";
+ }
+ switch (typeof val) {
+ case "string":
+ for (var i = 0; i < 32; i++) {
+ var replace = "\\";
+ switch (i) {
+ case 0: replace += "0"; break;
+ case 1: replace += "x01"; break;
+ case 2: replace += "x02"; break;
+ case 3: replace += "x03"; break;
+ case 4: replace += "x04"; break;
+ case 5: replace += "x05"; break;
+ case 6: replace += "x06"; break;
+ case 7: replace += "x07"; break;
+ case 8: replace += "b"; break;
+ case 9: replace += "t"; break;
+ case 10: replace += "n"; break;
+ case 11: replace += "v"; break;
+ case 12: replace += "f"; break;
+ case 13: replace += "r"; break;
+ case 14: replace += "x0e"; break;
+ case 15: replace += "x0f"; break;
+ case 16: replace += "x10"; break;
+ case 17: replace += "x11"; break;
+ case 18: replace += "x12"; break;
+ case 19: replace += "x13"; break;
+ case 20: replace += "x14"; break;
+ case 21: replace += "x15"; break;
+ case 22: replace += "x16"; break;
+ case 23: replace += "x17"; break;
+ case 24: replace += "x18"; break;
+ case 25: replace += "x19"; break;
+ case 26: replace += "x1a"; break;
+ case 27: replace += "x1b"; break;
+ case 28: replace += "x1c"; break;
+ case 29: replace += "x1d"; break;
+ case 30: replace += "x1e"; break;
+ case 31: replace += "x1f"; break;
+ }
+ val = val.replace(String.fromCharCode(i), replace);
+ }
+ return '"' + val.replace('"', '\\"') + '"';
+ case "boolean":
+ case "undefined":
+ case "number":
+ return val + "";
+ default:
+ return typeof val + ' "' + val + '"';
+ }
+}
+
+/**
+ * An object representing info about the current test, used for printing out
+ * nice messages and so forth.
+ */
+ReflectionHarness.currentTestInfo = {};
+
+/**
+ * .test() sets this, and it's used by .assertEquals()/.assertThrows().
+ * Calling .test() recursively is an error.
+ */
+ReflectionHarness.currentTestDescription = null;
+
+/**
+ * Run a group of one or more assertions. If any exceptions are thrown, catch
+ * them and report a failure.
+ */
+ReflectionHarness.test = function(fn, description) {
+ if (this.currentTestDescription) {
+ throw "TEST BUG: test() may not be called recursively!";
+ }
+ this.currentTestDescription = description;
+ try {
+ fn();
+ // Not throwing is a success
+ this.success();
+ } catch(err) {
+ this.failure("Exception thrown during tests with " + description);
+ }
+ this.currentTestDescription = null;
+}
+
+/**
+ * If question === answer, output a success, else report a failure with the
+ * given description. Currently success and failure both increment counters,
+ * and failures output a message to a <ul>. Which <ul> is decided by the type
+ * parameter -- different attribute types are separated for readability.
+ *
+ * @public
+ */
+ReflectionHarness.assertEquals = function(expected, actual, description) {
+ // Special-case -0 yay!
+ if (expected === 0 && actual === 0 && 1/expected === 1/actual) {
+ this.increment(this.passed);
+ } else if (expected === actual) {
+ this.increment(this.passed);
+ } else {
+ this.increment(this.failed);
+ this.reportFailure(this.currentTestDescription +
+ (description ? " followed by " + description : "") +
+ ' (expected ' + this.stringRep(actual) + ', got ' +
+ this.stringRep(expected) + ')');
+ }
+}
+
+/**
+ * If calling fn causes a DOMException of the type given by the string
+ * exceptionName (e.g., "IndexSizeError"), output a success. Otherwise, report
+ * a failure.
+ *
+ * @public
+ */
+ReflectionHarness.assertThrows = function(exceptionName, fn) {
+ try {
+ fn();
+ } catch (e) {
+ if (e instanceof DOMException && (e.code == DOMException[exceptionName] ||
+ e.name == exceptionName)) {
+ this.increment(this.passed);
+ return true;
+ }
+ }
+ this.increment(this.failed);
+ this.reportFailure(this.currentTestDescription + " must throw " +
+ exceptionName);
+ return false;
+}
+
+/**
+ * Get a description of the current type, e.g., "a.href".
+ */
+ReflectionHarness.getTypeDescription = function() {
+ var domNode = this.currentTestInfo.domObj.tagName.toLowerCase();
+ var idlNode = this.currentTestInfo.idlObj.nodeName.toLowerCase();
+ var domName = this.currentTestInfo.domName;
+ var idlName = this.currentTestInfo.idlName;
+ var comment = this.currentTestInfo.data.comment;
+ var typeDesc = idlNode + "." + idlName;
+ if (!comment && (domNode != idlNode || domName != idlName)) {
+ comment = "<" + domNode + " " + domName + ">";
+ }
+ if (comment) {
+ typeDesc += " (" + comment + ")";
+ }
+ return typeDesc;
+}
+
+/**
+ * Report a failure with the given description, adding context from the
+ * currentTestInfo member.
+ *
+ * @private
+ */
+ReflectionHarness.reportFailure = function(description) {
+ var typeDesc = this.getTypeDescription();
+ var idlName = this.currentTestInfo.idlName;
+ var comment = this.currentTestInfo.data.comment;
+ typeDesc = typeDesc.replace("&", "&amp;").replace("<", "&lt;");
+ description = description.replace("&", "&amp;").replace("<", "&lt;");
+
+ var type = this.currentTestInfo.data.type;
+
+ // Special case for undefined attributes, which we don't want getting in
+ // the way of everything else.
+ if (description.search('^typeof IDL attribute \\(expected ".*", got "undefined"\\)$') != -1) {
+ type = "undefined";
+ }
+
+ var done = false;
+ var ul = document.getElementById("errors-" + type.replace(" ", "-"));
+ if (ul === null) {
+ ul = document.createElement("ul");
+ ul.id = "errors-" + type.replace(" ", "-");
+ var div = document.getElementById("errors");
+ p = document.createElement("p");
+ if (type == "undefined") {
+ div.parentNode.insertBefore(ul, div.nextSibling);
+ p.innerHTML = "These IDL attributes were of undefined type, presumably representing unimplemented features (cordoned off into a separate section for tidiness):";
+ } else {
+ div.appendChild(ul);
+ p.innerHTML = "Errors for type " + type + ":";
+ }
+ ul.parentNode.insertBefore(p, ul);
+ } else if (type != "undefined") {
+ var existingErrors = ul.getElementsByClassName("desc");
+ for (var i = 0; i < existingErrors.length; i++) {
+ if (existingErrors[i].innerHTML == description) {
+ var typeSpan = existingErrors[i].parentNode.getElementsByClassName("type")[0];
+ // Check if we have lots of the same error for the same
+ // attribute. If so, we want to collapse them -- the exact
+ // elements that exhibit the error aren't going to be important
+ // to report in this case, and it can take a lot of space if
+ // there's an error in a global attribute like dir or id.
+ var types = typeSpan.innerHTML.split(", ");
+ var count = 0;
+ for (var i = 0; i < types.length; i++) {
+ if (types[i].search("^\\([0-9]* elements\\)\\." + idlName + "$") != -1) {
+ types[i] = "(" + (1 + parseInt(/[0-9]+/.exec(types[i])[0])) + " elements)." + idlName;
+ typeSpan.innerHTML = types.join(", ");
+ return;
+ } else if (types[i].search("\\." + idlName + "$") != -1) {
+ count++;
+ }
+ }
+ if (comment || count < 10) {
+ // Just add the extra error to the end, not many duplicates
+ // (or we have a comment)
+ typeSpan.innerHTML += ", " + typeDesc;
+ } else {
+ var filteredTypes = types.filter(function(type) { return type.search("\\." + idlName + "$") == -1; });
+ if (filteredTypes.length) {
+ typeSpan.innerHTML = filteredTypes.join(", ") + ", ";
+ } else {
+ typeSpan.innerHTML = "";
+ }
+ typeSpan.innerHTML += "(" + (types.length - filteredTypes.length) + " elements)." + idlName;
+ }
+ return;
+ }
+ }
+ }
+
+ if (type == "undefined") {
+ ul.innerHTML += "<li>" + typeDesc;
+ } else {
+ ul.innerHTML += "<li><span class=\"type\">" + typeDesc + "</span>: <span class=\"desc\">" + description + "</span>";
+ }
+}
+
+/**
+ * Shorthand function for when we have a failure outside of
+ * assertEquals()/assertThrows(). Generally used when the failure is an
+ * exception thrown unexpectedly or such, something not equality-based.
+ *
+ * @public
+ */
+ReflectionHarness.failure = function(message) {
+ this.increment(this.failed);
+ this.reportFailure(message);
+}
+
+/**
+ * Shorthand function for when we have a success outside of
+ * assertEquals()/assertThrows().
+ *
+ * @public
+ */
+ReflectionHarness.success = function() {
+ this.increment(this.passed);
+}
+
+/**
+ * Increment the count in either "passed" or "failed". el should always be one
+ * of those two variables. The implementation of this function amuses me.
+ *
+ * @private
+ */
+ReflectionHarness.increment = function(el) {
+ el.innerHTML = parseInt(el.innerHTML) + 1;
+ var percent = document.getElementById("percent");
+ var passed = document.getElementById("passed");
+ var failed = document.getElementById("failed");
+ percent.innerHTML = (parseInt(passed.innerHTML)/(parseInt(passed.innerHTML) + parseInt(failed.innerHTML))*100).toPrecision(3);
+}
+
+/**
+ * Hide all displayed errors matching a given regex, so it's easier to filter
+ * out repetitive failures. TODO: Fix this so it works right with the new
+ * "lump many errors in one <li>" thing.
+ *
+ * @private (kind of, only called in the original reflection.html)
+ */
+ReflectionHarness.maskErrors = function(regex) {
+ var uls = document.getElementsByTagName("ul");
+ for (var i = 0; i < uls.length; i++) {
+ var lis = uls[i].children;
+ for (var j = 0; j < lis.length; j++) {
+ if (regex !== "" && lis[j].innerHTML.match(regex)) {
+ lis[j].style.display = "none";
+ } else {
+ lis[j].style.display = "list-item";
+ }
+ }
+ }
+}
+
+// Now for some stuff that has nothing to do with ReflectionHarness and
+// everything to do with initialization needed for reflection.js, which seems
+// pointless to put in an extra file.
+
+var elements = {};
+
+var extraTests = [];
+
+/**
+ * Used for combining a number of small arrays of element data into one big
+ * one.
+ */
+function mergeElements(src) {
+ for (var key in src) {
+ if (!src.hasOwnProperty(key)) {
+ // This is inherited from a prototype or something.
+ continue;
+ }
+
+ if (key in elements) {
+ elements[key] = elements[key].concat(src[key]);
+ } else {
+ elements[key] = src[key];
+ }
+ }
+}
diff --git a/testing/web-platform/tests/html/dom/reflection-embedded.html b/testing/web-platform/tests/html/dom/reflection-embedded.html
new file mode 100644
index 0000000000..0a362f817a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-embedded.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: embedded elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-embedded.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-forms-weekmonth.html b/testing/web-platform/tests/html/dom/reflection-forms-weekmonth.html
new file mode 100644
index 0000000000..ad72da27c7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-forms-weekmonth.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: week and month input types</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-forms-weekmonth.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-forms.html b/testing/web-platform/tests/html/dom/reflection-forms.html
new file mode 100644
index 0000000000..2fe251a6f4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-forms.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: form elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-forms.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-grouping.html b/testing/web-platform/tests/html/dom/reflection-grouping.html
new file mode 100644
index 0000000000..e59f5f569c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-grouping.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: grouping elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-grouping.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-metadata.html b/testing/web-platform/tests/html/dom/reflection-metadata.html
new file mode 100644
index 0000000000..f21b6863ec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-metadata.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: metadata elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-metadata.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-misc.html b/testing/web-platform/tests/html/dom/reflection-misc.html
new file mode 100644
index 0000000000..915f6fad47
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-misc.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: miscellaneous elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-misc.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-obsolete.html b/testing/web-platform/tests/html/dom/reflection-obsolete.html
new file mode 100644
index 0000000000..0aa439813e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-obsolete.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: obsolete elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-obsolete.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-original.html b/testing/web-platform/tests/html/dom/reflection-original.html
new file mode 100644
index 0000000000..0f7b43e375
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-original.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>HTML5 reflection tests</title>
+<meta name=timeout content=long>
+<p>This is <em>not</em> the authoritative conformance test suite for
+reflection. The authoritative tests can be found here, split up into sections:
+
+<ul>
+ <li><a href=reflection-metadata.html>Metadata elements</a>
+ <li><a href=reflection-sections.html>Section elements</a>
+ <li><a href=reflection-grouping.html>Grouping elements</a>
+ <li><a href=reflection-text.html>Text elements</a>
+ <li><a href=reflection-embedded.html>Embedded elements</a>
+ <li><a href=reflection-tabular.html>Tabular elements</a>
+ <li><a href=reflection-forms.html>Form elements</a>
+ <li><a href=reflection-misc.html>Miscellaneous elements</a>
+ <li><a href=reflection-obsolete.html>Obsolete elements</a>
+</ul>
+
+<p>This test suite is provided for implementers' convenience in debugging
+failures. It groups similar failures in a fashion that should help fix them.
+It is not intended to be suitable for incorporation into automated testing
+frameworks.
+
+<p>Filter out errors matching a regex (operates on HTML not text, you have to manually escape entities): <input oninput="maskErrors(this.value)">
+
+<p>Passed: <span id=passed>0</span> (<span id=percent></span>%). Failed: <span id=failed>0</span>. Time to complete: <span id=time>0</span> s.
+
+<div id=errors></div>
+
+<script src=original-harness.js></script>
+<script src=elements-metadata.js></script>
+<script src=elements-sections.js></script>
+<script src=elements-grouping.js></script>
+<script src=elements-text.js></script>
+<script src=elements-embedded.js></script>
+<script src=elements-tabular.js></script>
+<script src=elements-forms.js></script>
+<script src=elements-misc.js></script>
+<script src=elements-obsolete.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-sections.html b/testing/web-platform/tests/html/dom/reflection-sections.html
new file mode 100644
index 0000000000..2235665414
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-sections.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: section elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-sections.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-tabular.html b/testing/web-platform/tests/html/dom/reflection-tabular.html
new file mode 100644
index 0000000000..f790da253a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-tabular.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: tabular elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-tabular.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection-text.html b/testing/web-platform/tests/html/dom/reflection-text.html
new file mode 100644
index 0000000000..a2dc6f6154
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection-text.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>HTML5 reflection tests: text elements</title>
+<meta name=timeout content=long>
+<p>Implementers looking to fix bugs might want to use the <a
+href=reflection-original.html>original version</a> of this suite's test
+framework, which conveniently aggregates similar errors and only reports
+failures. This file is (part of) the authoritative conformance test suite, and
+is suitable for incorporation into automated test suites.
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=original-harness.js></script>
+<script src=new-harness.js></script>
+<script src=elements-text.js></script>
+<script src=reflection.js></script>
diff --git a/testing/web-platform/tests/html/dom/reflection.js b/testing/web-platform/tests/html/dom/reflection.js
new file mode 100644
index 0000000000..b2c3b30aae
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/reflection.js
@@ -0,0 +1,1065 @@
+ReflectionTests = {};
+
+ReflectionTests.start = new Date().getTime();
+
+/**
+ * Resolve the given URL to an absolute URL, relative to the current document's
+ * address. There's no API that I know of that exposes this directly, so we
+ * actually just create an <a> element, set its href, and stitch together the
+ * various properties. Seems to work. We don't try to reimplement the
+ * algorithm here, because we're not concerned with its correctness -- we're
+ * only testing HTML reflection, not Web Addresses.
+ *
+ * Return the input if the URL couldn't be resolved, per the spec for
+ * reflected URL attributes.
+ *
+ * It seems like IE9 doesn't implement URL decomposition attributes correctly
+ * for <a>, which causes all these tests to fail. Ideally I'd do this in some
+ * other way, but the failure does stem from an incorrect implementation of
+ * HTML, so I'll leave it alone for now.
+ *
+ * TODO: This relies on reflection to test reflection, so it could mask bugs.
+ * Either get a JS implementation of the "resolve a URL" algorithm, or just
+ * specify expected values manually here. It shouldn't be too hard to write
+ * special cases for all the values we test.
+ */
+ReflectionTests.resolveUrl = function(url) {
+ url = String(url);
+ var el = document.createElement("a");
+ el.href = url;
+ var ret = el.protocol + "//" + el.host + el.pathname + el.search + el.hash;
+ if (ret == "//") {
+ return url;
+ } else {
+ return ret;
+ }
+};
+
+/**
+ * The "rules for parsing non-negative integers" from the HTML spec. They're
+ * mostly used for reflection, so here seems like as good a place to test them
+ * as any. Returns false on error.
+ */
+ReflectionTests.parseNonneg = function(input) {
+ var value = this.parseInt(input);
+ if (value === false || value < 0) {
+ return false;
+ }
+ return value;
+};
+
+/**
+ * The "rules for parsing integers" from the HTML spec. Returns false on
+ * error.
+ */
+ReflectionTests.parseInt = function(input) {
+ var position = 0;
+ var sign = 1;
+ // Skip whitespace
+ while (input.length > position && /^[ \t\n\f\r]$/.test(input[position])) {
+ position++;
+ }
+ if (position >= input.length) {
+ return false;
+ }
+ if (input[position] == "-") {
+ sign = -1;
+ position++;
+ } else if (input[position] == "+") {
+ position++;
+ }
+ if (position >= input.length) {
+ return false;
+ }
+ if (!/^[0-9]$/.test(input[position])) {
+ return false;
+ }
+ var value = 0;
+ while (input.length > position && /^[0-9]$/.test(input[position])) {
+ value *= 10;
+ // Don't use parseInt even for single-digit strings . . .
+ value += input.charCodeAt(position) - "0".charCodeAt(0);
+ position++;
+ }
+ if (value === 0) {
+ return 0;
+ }
+ return sign * value;
+};
+
+/**
+ * The "rules for parsing floating-point number values" from the HTML spec.
+ * Returns false on error.
+ */
+ReflectionTests.parseFloat = function(input) {
+ var position = 0;
+ var value = 1;
+ var divisor = 1;
+ var exponent = 1;
+ // Skip whitespace
+ while (input.length > position && /^[ \t\n\f\r]$/.test(input[position])) {
+ position++;
+ }
+ if (position >= input.length) {
+ return false;
+ }
+ if (input[position] == "-") {
+ value = -1;
+ divisor = -1;
+ position++;
+ } else if (input[position] == "+") {
+ position++;
+ }
+ if (position >= input.length) {
+ return false;
+ }
+ if (input[position] == "." && position+1 < input.length && /^[0-9]$/.test(input[position+1])) {
+ value = 0;
+ // Use "else" branches rather than "jump to label fraction"
+ } else if (!/^[0-9]$/.test(input[position])) {
+ return false;
+ } else {
+ var val = 0;
+ while (input.length > position && /^[0-9]$/.test(input[position])) {
+ val *= 10;
+ // Don't use parseInt even for single-digit strings . . .
+ val += input.charCodeAt(position) - "0".charCodeAt(0);
+ position++;
+ }
+ value *= val;
+ }
+ // Use nested "if" tests rather than "jump to label conversion" or "skip"
+ // Fraction:
+ if (input.length > position && input[position] == ".") {
+ position++;
+ while (input.length > position && /^[0-9]$/.test(input[position])) {
+ divisor *= 10;
+ // Don't use parseInt even for single-digit strings . . .
+ value += (input.charCodeAt(position) - "0".charCodeAt(0)) / divisor;
+ position++;
+ }
+ }
+ if (input.length > position && (input[position] == "e" || input[position] == "E")) {
+ position++;
+ if (input.length > position) {
+ if (input[position] == "-") {
+ exponent = -1;
+ position++;
+ } else if (input[position] == "+") {
+ position++;
+ }
+ if (input.length > position && /^[0-9]$/.test(input[position])) {
+ var exp = 0;
+ do {
+ exp *= 10;
+ // Don't use parseInt even for single-digit strings . . .
+ exp += input.charCodeAt(position) - "0".charCodeAt(0);
+ position++;
+ } while (input.length > position && /^[0-9]$/.test(input[position]));
+ exponent *= exp;
+ value *= Math.pow(10, exponent);
+ }
+ }
+ }
+ // Conversion:
+ if (!Number.isFinite(value)) {
+ return false;
+ }
+ if (value === 0) {
+ return 0;
+ }
+ return value;
+}
+
+// Used in initializing typeMap
+var binaryString = "\x00\x01\x02\x03\x04\x05\x06\x07 "
+ + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f "
+ + "\x10\x11\x12\x13\x14\x15\x16\x17 "
+ + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ";
+var maxInt = 2147483647;
+var minInt = -2147483648;
+var maxUnsigned = 4294967295;
+
+/**
+ * Array containing the tests and other information for each type of reflected
+ * attribute. Meaning of keys:
+ *
+ * "jsType": What typeof idlObj[idlName] is supposed to be.
+ * "defaultVal": The default value to be returned if the attribute is not
+ * present and no default is specifically set for this attribute.
+ * "domTests": What values to test with setAttribute().
+ * "domExpected": What values to expect with IDL get after setAttribute().
+ * Defaults to the same as domTests.
+ * "idlTests": What values to test with IDL set. Defaults to domTests.
+ * "idlDomExpected": What to expect from getAttribute() after IDL set.
+ * Defaults to idlTests.
+ * "idlIdlExpected": What to expect from IDL get after IDL set. Defaults to
+ * idlDomExpected.
+ *
+ * Note that all tests/expected values are only baselines, and can be expanded
+ * with additional tests hardcoded into the function for particular types if
+ * necessary. For example, a special codepath is used for enums, and for
+ * IDL setters which throw an exception. null means "defaultVal" is the
+ * expected value. Expected DOM values are cast to strings by adding "".
+ *
+ * TODO: Test strings that aren't valid UTF-16. Desired behavior is not clear
+ * here at the time of writing, see
+ * http://www.w3.org/Bugs/Public/show_bug.cgi?id=12100
+ *
+ * TODO: Test deleting an IDL attribute, and maybe doing other fun stuff to it.
+ *
+ * TODO: Test IDL sets of integer types to out-of-range or other weird values.
+ * WebIDL says to wrap, but I'm not sure offhand if that's what we want.
+ *
+ * TODO: tokenlist, settable tokenlist, limited
+ */
+
+
+ReflectionTests.typeMap = {
+ /**
+ * "If a reflecting IDL attribute is a DOMString but doesn't fall into any
+ * of the above categories, then the getting and setting must be done in a
+ * transparent, case-preserving manner."
+ *
+ * The data object passed to reflects() can contain an optional key
+ * treatNullAsEmptyString, whose value is ignored. If it does contain the
+ * key, null will be cast to "" instead of "null", per WebIDL
+ * [TreatNullAs=EmptyString].
+ */
+ "string": {
+ "jsType": "string",
+ "defaultVal": "",
+ "domTests": ["", " " + binaryString + " foo ", undefined, 7, 1.5, "5%", "+100", ".5", true,
+ false, {"test": 6}, NaN, +Infinity, -Infinity, "\0", null,
+ {"toString":function(){return "test-toString";}},
+ {"valueOf":function(){return "test-valueOf";}, toString:null}
+ ]
+ },
+ /**
+ * "If a reflecting IDL attribute is a USVString attribute whose content
+ * attribute is defined to contain a URL, then on getting, if the content
+ * attribute is absent, the IDL attribute must return the empty string.
+ * Otherwise, the IDL attribute must parse the value of the content
+ * attribute relative to the element's node document and if that is
+ * successful, return the resulting URL string. If parsing fails, then the
+ * value of the content attribute must be returned instead, converted to a
+ * USVString. On setting, the content attribute must be set to the specified
+ * new value."
+ *
+ * Also HTMLHyperLinkElementUtils href, used by a.href and area.href
+ */
+ "url": {
+ "jsType": "string",
+ "defaultVal": "",
+ "domTests": ["", " foo ", "http://site.example/",
+ "//site.example/path???@#l", binaryString, undefined, 7, 1.5, "5%", "+100", ".5", true,
+ false, {"test": 6}, NaN, +Infinity, -Infinity, "\0", null,
+ {"toString":function(){return "test-toString";}},
+ {"valueOf":function(){return "test-valueOf";}, toString:null}],
+ "domExpected": ReflectionTests.resolveUrl,
+ "idlIdlExpected": ReflectionTests.resolveUrl
+ },
+ /**
+ * "If a reflecting IDL attribute is a DOMString whose content attribute is
+ * an enumerated attribute, and the IDL attribute is limited to only known
+ * values, then, on getting, the IDL attribute must return the conforming
+ * value associated with the state the attribute is in (in its canonical
+ * case), or the empty string if the attribute is in a state that has no
+ * associated keyword value; and on setting, if the new value is an ASCII
+ * case-insensitive match for one of the keywords given for that attribute,
+ * then the content attribute must be set to the conforming value
+ * associated with the state that the attribute would be in if set to the
+ * given new value, otherwise, if the new value is the empty string, then
+ * the content attribute must be removed, otherwise, the content attribute
+ * must be set to the given new value."
+ *
+ * "Some attributes are defined as taking one of a finite set of keywords.
+ * Such attributes are called enumerated attributes. The keywords are each
+ * defined to map to a particular state (several keywords might map to the
+ * same state, in which case some of the keywords are synonyms of each
+ * other; additionally, some of the keywords can be said to be
+ * non-conforming, and are only in the specification for historical
+ * reasons). In addition, two default states can be given. The first is the
+ * invalid value default, the second is the missing value default.
+ *
+ * . . .
+ *
+ * When the attribute is specified, if its value is an ASCII
+ * case-insensitive match for one of the given keywords then that keyword's
+ * state is the state that the attribute represents. If the attribute value
+ * matches none of the given keywords, but the attribute has an invalid
+ * value default, then the attribute represents that state. Otherwise, if
+ * the attribute value matches none of the keywords but there is a missing
+ * value default state defined, then that is the state represented by the
+ * attribute. Otherwise, there is no default, and invalid values must be
+ * ignored.
+ *
+ * When the attribute is not specified, if there is a missing value default
+ * state defined, then that is the state represented by the (missing)
+ * attribute. Otherwise, the absence of the attribute means that there is
+ * no state represented."
+ *
+ * This is only used for enums that are limited to known values, not other
+ * enums (those are treated as generic strings by the spec). The data
+ * object passed to reflects() can contain these keys:
+ *
+ * "defaultVal": missing value default (defaults to "")
+ * "invalidVal": invalid value default (defaults to defaultVal)
+ * "keywords": array of keywords as given by the spec (required)
+ * "nonCanon": dictionary mapping non-canonical values to their
+ * canonical equivalents (defaults to {})
+ * "isNullable": Indicates if attribute is nullable (defaults to false)
+ *
+ * Tests are mostly hardcoded into reflects(), since they depend on the
+ * keywords. All expected values are computed in reflects() using a helper
+ * function.
+ */
+ "enum": {
+ "jsType": "string",
+ "defaultVal": "",
+ "domTests": ["", " " + binaryString + " foo ", undefined, 7, 1.5, "5%", "+100", ".5", true,
+ false, {"test": 6}, NaN, +Infinity, -Infinity, "\0", null,
+ {"toString":function(){return "test-toString";}},
+ {"valueOf":function(){return "test-valueOf";}, toString:null}]
+ },
+ /**
+ * "If a reflecting IDL attribute is a boolean attribute, then on getting
+ * the IDL attribute must return true if the content attribute is set, and
+ * false if it is absent. On setting, the content attribute must be removed
+ * if the IDL attribute is set to false, and must be set to the empty
+ * string if the IDL attribute is set to true. (This corresponds to the
+ * rules for boolean content attributes.)"
+ */
+ "boolean": {
+ "jsType": "boolean",
+ "defaultVal": false,
+ "domTests": ["", " foo ", undefined, null, 7, 1.5, "5%", "+100", ".5", true, false,
+ {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+ {"toString":function(){return "test-toString";}},
+ {"valueOf":function(){return "test-valueOf";}, toString:null}],
+ "domExpected": function(val) {
+ return true;
+ }
+ },
+ /**
+ * "If a reflecting IDL attribute is a signed integer type (long) then, on
+ * getting, the content attribute must be parsed according to the rules for
+ * parsing signed integers, and if that is successful, and the value is in
+ * the range of the IDL attribute's type, the resulting value must be
+ * returned. If, on the other hand, it fails or returns an out of range
+ * value, or if the attribute is absent, then the default value must be
+ * returned instead, or 0 if there is no default value. On setting, the
+ * given value must be converted to the shortest possible string
+ * representing the number as a valid integer and then that string must be
+ * used as the new content attribute value."
+ */
+ "long": {
+ "jsType": "number",
+ "defaultVal": 0,
+ "domTests": [-36, -1, 0, 1, maxInt, minInt, maxInt + 1, minInt - 1,
+ maxUnsigned, maxUnsigned + 1, "", "-", "+", "-1", "-0", "0", "1",
+ " " + binaryString + " foo ",
+ // Test various different whitespace. Only 20, 9, A, C,
+ // and D are whitespace.
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ undefined, 1.5, "5%", "+100", ".5", true, false, {"test": 6}, NaN, +Infinity,
+ -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "domExpected": function(val) {
+ var parsed = ReflectionTests.parseInt(String(val));
+ if (parsed === false || parsed > maxInt || parsed < minInt) {
+ return null;
+ }
+ return parsed;
+ },
+ "idlTests": [-36, -1, 0, 1, 2147483647, -2147483648],
+ "idlDomExpected": [-36, -1, 0, 1, 2147483647, -2147483648]
+ },
+ /**
+ * "If a reflecting IDL attribute is a signed integer type (long) that is
+ * limited to only non-negative numbers then, on getting, the content
+ * attribute must be parsed according to the rules for parsing non-negative
+ * integers, and if that is successful, and the value is in the range of
+ * the IDL attribute's type, the resulting value must be returned. If, on
+ * the other hand, it fails or returns an out of range value, or if the
+ * attribute is absent, the default value must be returned instead, or −1
+ * if there is no default value. On setting, if the value is negative, the
+ * user agent must fire an INDEX_SIZE_ERR exception. Otherwise, the given
+ * value must be converted to the shortest possible string representing the
+ * number as a valid non-negative integer and then that string must be used
+ * as the new content attribute value."
+ */
+ "limited long": {
+ "jsType": "number",
+ "defaultVal": -1,
+ "domTests": [minInt - 1, minInt, -36, -1, -0, 0, 1, maxInt, maxInt + 1,
+ maxUnsigned, maxUnsigned + 1, "", "-", "+", "-1", "-0", "0", "1",
+ " " + binaryString + " foo ",
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ undefined, 1.5, "5%", "+100", ".5", true, false, {"test": 6}, NaN, +Infinity,
+ -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "domExpected": function(val) {
+ var parsed = ReflectionTests.parseNonneg(String(val));
+ if (parsed === false || parsed > maxInt || parsed < minInt) {
+ return null;
+ }
+ return parsed;
+ },
+ "idlTests": [minInt, -36, -1, 0, 1, maxInt],
+ "idlDomExpected": [null/*exception*/, null/*exception*/, null/*exception*/, 0, 1, maxInt]
+ },
+ /**
+ * "If a reflecting IDL attribute is an unsigned integer type (unsigned
+ * long) then, on getting, the content attribute must be parsed according
+ * to the rules for parsing non-negative integers, and if that is
+ * successful, and the value is in the range 0 to 2147483647 inclusive, the
+ * resulting value must be returned. If, on the other hand, it fails or
+ * returns an out of range value, or if the attribute is absent, the
+ * default value must be returned instead, or 0 if there is no default
+ * value. On setting, the given value must be converted to the shortest
+ * possible string representing the number as a valid non-negative integer
+ * and then that string must be used as the new content attribute value."
+ */
+ "unsigned long": {
+ "jsType": "number",
+ "defaultVal": 0,
+ "domTests": [minInt - 1, minInt, -36, -1, 0, 1, 257, maxInt,
+ maxInt + 1, maxUnsigned, maxUnsigned + 1, "", "-", "+", "-1", "-0", "0", "1",
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ " " + binaryString + " foo ", undefined, 1.5, "5%", "+100", ".5", true, false,
+ {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "domExpected": function(val) {
+ var parsed = ReflectionTests.parseNonneg(String(val));
+ // Note maxInt, not maxUnsigned.
+ if (parsed === false || parsed < 0 || parsed > maxInt) {
+ return null;
+ }
+ return parsed;
+ },
+ "idlTests": [0, 1, 257, maxInt, "-0", maxInt + 1, maxUnsigned],
+ "idlIdlExpected": [0, 1, 257, maxInt, 0, null, null],
+ "idlDomExpected": [0, 1, 257, maxInt, 0, null, null],
+ },
+ /**
+ * "If a reflecting IDL attribute is an unsigned integer type (unsigned
+ * long) that is limited to only non-negative numbers greater than zero,
+ * then the behavior is similar to the previous case, but zero is not
+ * allowed. On getting, the content attribute must first be parsed
+ * according to the rules for parsing non-negative integers, and if that is
+ * successful, and the value is in the range 1 to 2147483647 inclusive, the
+ * resulting value must be returned. If, on the other hand, it fails or
+ * returns an out of range value, or if the attribute is absent, the
+ * default value must be returned instead, or 1 if there is no default
+ * value. On setting, if the value is zero, the user agent must fire an
+ * INDEX_SIZE_ERR exception. Otherwise, the given value must be converted
+ * to the shortest possible string representing the number as a valid
+ * non-negative integer and then that string must be used as the new
+ * content attribute value."
+ */
+ "limited unsigned long": {
+ "jsType": "number",
+ "defaultVal": 1,
+ "domTests": [minInt - 1, minInt, -36, -1, 0, 1, maxInt,
+ maxInt + 1, maxUnsigned, maxUnsigned + 1, "", "-", "+", "-1", "-0", "0", "1",
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ " " + binaryString + " foo ", undefined, 1.5, "5%", "+100", ".5", true, false,
+ {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "domExpected": function(val) {
+ var parsed = ReflectionTests.parseNonneg(String(val));
+ // Note maxInt, not maxUnsigned.
+ if (parsed === false || parsed < 1 || parsed > maxInt) {
+ return null;
+ }
+ return parsed;
+ },
+ "idlTests": [0, 1, maxInt, maxInt + 1, maxUnsigned],
+ "idlDomExpected": [null/*exception*/, 1, maxInt, null, null]
+ },
+ /**
+ * "If a reflecting IDL attribute has an unsigned integer type (unsigned
+ * long) that is limited to only non-negative numbers greater than zero
+ * with fallback, then the behaviour is similar to the previous case, but
+ * disallowed values are converted to the default value. On getting, the
+ * content attribute must first be parsed according to the rules for
+ * parsing non-negative integers, and if that is successful, and the value
+ * is in the range 1 to 2147483647 inclusive, the resulting value must be
+ * returned. If, on the other hand, it fails or returns an out of range
+ * value, or if the attribute is absent, the default value must be returned
+ * instead. On setting, first, if the new value is in the range 1 to
+ * 2147483647, then let n be the new value, otherwise let n be the default
+ * value; then, n must be converted to the shortest possible string
+ * representing the number as a valid non-negative integer and that string
+ * must be used as the new content attribute value."
+ */
+ "limited unsigned long with fallback": {
+ "jsType": "number",
+ "domTests": [minInt - 1, minInt, -36, -1, 0, 1, maxInt,
+ maxInt + 1, maxUnsigned, maxUnsigned + 1, "", "-", "+", "-1", "-0", "0", "1",
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ " " + binaryString + " foo ", undefined, 1.5, "5%", "+100", ".5", true, false,
+ {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "domExpected": function(val) {
+ var parsed = ReflectionTests.parseNonneg(String(val));
+ // Note maxInt, not maxUnsigned.
+ if (parsed === false || parsed < 1 || parsed > maxInt) {
+ return null;
+ }
+ return parsed;
+ },
+ "idlTests": [0, 1, maxInt, maxInt + 1, maxUnsigned],
+ "idlDomExpected": [null, 1, maxInt, null, null]
+ },
+ /**
+ * "If a reflecting IDL attribute has an unsigned integer type (unsigned
+ * long) that is clamped to the range [min, max], then on getting, the
+ * content attribute must first be parsed according to the rules for
+ * parsing non-negative integers, and if that is successful, and the value
+ * is between min and max inclusive, the resulting value must be returned.
+ * If it fails, the default value must be returned. If it succeeds but the
+ * value is less than min, min must be returned. If it succeeds but the
+ * value is greater than max, max must be returned. On setting, it behaves
+ * the same as a regular reflected unsigned integer."
+ *
+ * The data object passed to reflects must contain the keys defaultVal,
+ * min, and max. As with enum, domExpected is generated later once we have
+ * access to the min and max.
+ */
+ "clamped unsigned long": {
+ "jsType": "number",
+ "domTests": [minInt - 1, minInt, -36, -1, 0, 1, maxInt,
+ maxInt + 1, maxUnsigned, maxUnsigned + 1, "", "-", "+", "-1", "-0", "0", "1",
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ " " + binaryString + " foo ", undefined, 1.5, "5%", "+100", ".5", true, false,
+ {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "idlTests": [0, 1, 257, maxInt, "-0", maxInt + 1, maxUnsigned],
+ "idlDomExpected": [0, 1, 257, maxInt, 0, null, null],
+ },
+ /**
+ * "If a reflecting IDL attribute is a floating point number type (double),
+ * then, on getting, the content attribute must be parsed according to the
+ * rules for parsing floating point number values, and if that is
+ * successful, the resulting value must be returned. If, on the other hand,
+ * it fails, or if the attribute is absent, the default value must be
+ * returned instead, or 0.0 if there is no default value. On setting, the
+ * given value must be converted to the best representation of the number
+ * as a floating point number and then that string must be used as the new
+ * content attribute value."
+ *
+ * TODO: Check this:
+ *
+ * "Except where otherwise specified, if an IDL attribute that is a
+ * floating point number type (double) is assigned an Infinity or
+ * Not-a-Number (NaN) value, a NOT_SUPPORTED_ERR exception must be raised."
+ */
+ "double": {
+ "jsType": "number",
+ "defaultVal": 0.0,
+ "domTests": [minInt - 1, minInt, -36, -1, 0, 1, maxInt,
+ maxInt + 1, maxUnsigned, maxUnsigned + 1, "", "-", "+",
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ " " + binaryString + " foo ", undefined, 1.5, "5%", "+100", ".5", true, false,
+ "1.", "1e2", "1e+2", "1e-2", "1E2", "1E+2", "1E-2", "1.e2", "1.0e2",
+ "1. 1", "1 .1", "1. e2", "1 .e2", "1 e2", "1e 2", "1e -2", "1e- 2",
+ "1.8e308", "-1.8e308",
+ {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "domExpected": function (val) {
+ var parsed = ReflectionTests.parseFloat(String(val));
+ if (parsed === false) {
+ return null;
+ }
+ return parsed;
+ },
+ "idlTests": [ -10000000000, -1, -0, 0, 1, 10000000000,
+ 1e-10, 1e-4, 1.5, 1e25 ],
+ "idlIdlExpected": function (val) {
+ // This is a bit heavy-weight but hopefully will give values
+ // that compare "better" (without introducing some tolerance)
+ // when the test cases are expanded with more values.
+ return ReflectionTests.parseFloat(String(val));
+ }
+ },
+ /**
+ * Reflected IDL attribute of type double, limited to only positive values,
+ * are similar to the previous case with the following exceptions:
+ *
+ * - on getting, if the parsed value is not greater than 0, then return
+ * the default value
+ * - on setting, if the value is not greater than 0, then return (leaving)
+ * the attribute to its previous value.
+ */
+ "limited double": {
+ "jsType": "number",
+ "defaultVal": 0.0,
+ "domTests": [minInt - 1, minInt, -36, -1, 0, 1, maxInt,
+ maxInt + 1, maxUnsigned, maxUnsigned + 1, "", "-", "+",
+ "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+ "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+ "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+ "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7", "\u30007",
+ "\t\u000B7", "\n\u000B7","\f\u000B7", "\r\u000B7", "\x20\u000B7", "7\u000B",
+ " " + binaryString + " foo ", undefined, 1.5, "5%", "+100", ".5", true, false,
+ "1.", "1e2", "1e+2", "1e-2", "1E2", "1E+2", "1E-2", "1.e2", "1.0e2",
+ "1. 1", "1 .1", "1. e2", "1 .e2", "1 e2", "1e 2", "1e -2", "1e- 2",
+ "1.8e308", "-1.8e308",
+ {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+ {toString:function() {return 2;}, valueOf: null},
+ {valueOf:function() {return 3;}, toString: null}],
+ "domExpected": function (val) {
+ var parsed = ReflectionTests.parseFloat(String(val));
+ if (parsed === false || parsed <= 0) {
+ return null;
+ }
+ return parsed;
+ },
+ "idlTests": [ -10000000000, -1, -0, 0, 1, 10000000000,
+ 1e-10, 1e-4, 1.5, 1e25 ],
+ "idlIdlExpected": function (val) {
+ // Non-positive values are special-cased below, as they
+ // should be ignored, leaving the current value unchanged
+
+ // This is a bit heavy-weight but hopefully will give values
+ // that compare "better" (without introducing some tolerance)
+ // when the test cases are expanded with more values.
+ return ReflectionTests.parseFloat(String(val));
+ }
+ }
+};
+
+for (var type in ReflectionTests.typeMap) {
+ var props = ReflectionTests.typeMap[type];
+ var cast = window[props.jsType[0].toUpperCase() + props.jsType.slice(1)];
+ if (props.domExpected === undefined) {
+ props.domExpected = props.domTests.map(cast);
+ } else if (typeof props.domExpected == "function") {
+ props.domExpected = props.domTests.map(props.domExpected);
+ }
+ if (props.idlTests === undefined) {
+ props.idlTests = props.domTests;
+ }
+ if (props.idlDomExpected === undefined) {
+ props.idlDomExpected = props.idlTests.map(cast);
+ } else if (typeof props.idlDomExpected == "function") {
+ props.idlDomExpected = props.idlTests.map(props.idlDomExpected);
+ }
+ if (props.idlIdlExpected === undefined) {
+ props.idlIdlExpected = props.idlDomExpected;
+ } else if (typeof props.idlIdlExpected == "function") {
+ props.idlIdlExpected = props.idlTests.map(props.idlIdlExpected);
+ }
+}
+
+/**
+ * Tests that the JavaScript attribute named idlName on the object idlObj
+ * reflects the DOM attribute named domName on domObj. The data argument is an
+ * object that must contain at least one key, "type", which contains the
+ * expected type of the IDL attribute ("string", "enum", etc.). The "comment"
+ * key will add a parenthesized comment in the type info if there's a test
+ * failure, to indicate that there's something special about the element you're
+ * testing (like it has an attribute set to some value). Other keys in the
+ * data object are type-specific, e.g., "defaultVal" for numeric types. If the
+ * data object is a string, it's converted to {"type": data}. If idlObj is a
+ * string, we set idlObj = domObj = document.createElement(idlObj).
+ */
+ReflectionTests.reflects = function(data, idlName, idlObj, domName, domObj) {
+ // Do some setup first so that getTypeDescription() works in testWrapper()
+ if (typeof data == "string") {
+ data = {type: data};
+ }
+ if (domName === undefined) {
+ domName = idlName;
+ }
+ if (typeof idlObj == "string") {
+ idlObj = document.createElement(idlObj);
+ }
+ if (domObj === undefined) {
+ domObj = idlObj;
+ }
+
+ // Note: probably a hack? This kind of assumes that the variables here
+ // won't change over the course of the tests, which is wrong, but it's
+ // probably safe enough. Just don't read stuff that will change.
+ ReflectionHarness.currentTestInfo = {data: data, idlName: idlName, idlObj: idlObj, domName: domName, domObj: domObj};
+
+ // If we don't recognize the type, testing is impossible.
+ if (this.typeMap[data.type] === undefined) {
+ if (unimplemented.indexOf(data.type) == -1) {
+ unimplemented.push(data.type);
+ }
+ return;
+ }
+
+ var typeInfo = this.typeMap[data.type];
+
+ if (typeof data.isNullable == "undefined") {
+ data.isNullable = false;
+ }
+
+ // Test that typeof idlObj[idlName] is correct. If not, further tests are
+ // probably pointless, so bail out if we're not running conformance tests.
+ var expectedType = data.isNullable && data.defaultVal === null ? "object"
+ : typeInfo.jsType;
+ ReflectionHarness.test(function() {
+ ReflectionHarness.assertEquals(typeof idlObj[idlName], expectedType);
+ }, "typeof IDL attribute");
+
+ if (!ReflectionHarness.conformanceTesting &&
+ typeof idlObj[idlName] !== expectedType) {
+ return;
+ }
+
+ // Test default
+ var defaultVal = data.defaultVal;
+ if (defaultVal === undefined) {
+ defaultVal = typeInfo.defaultVal;
+ }
+ if ((domObj.localName === "form" && domName === "action") ||
+ (["button", "input"].includes(domObj.localName) &&
+ domName === "formAction")) {
+ // Hard-coded special case
+ defaultVal = domObj.ownerDocument.URL;
+ }
+ if (!data.customGetter && (defaultVal !== null || data.isNullable)) {
+ ReflectionHarness.test(function() {
+ ReflectionHarness.assertEquals(idlObj[idlName], defaultVal);
+ }, "IDL get with DOM attribute unset");
+ }
+
+ var domTests = typeInfo.domTests.slice(0);
+ var domExpected = typeInfo.domExpected.map(function(val) { return val === null ? defaultVal : val; });
+ var idlTests = typeInfo.idlTests.slice(0);
+ var idlDomExpected = typeInfo.idlDomExpected.map(function(val) { return val === null ? defaultVal : val; });
+ var idlIdlExpected = typeInfo.idlIdlExpected.map(function(val) { return val === null ? defaultVal : val; });
+ switch (data.type) {
+ // Extra tests and other special-casing
+ case "boolean":
+ domTests.push(domName);
+ domExpected.push(true);
+ break;
+
+ case "enum":
+ // Whee, enum is complicated.
+ if (typeof data.invalidVal == "undefined") {
+ data.invalidVal = defaultVal;
+ }
+ if (typeof data.nonCanon == "undefined") {
+ data.nonCanon = {};
+ }
+ for (var i = 0; i < data.keywords.length; i++) {
+ if (data.keywords[i] != "") {
+ domTests.push(data.keywords[i], "x" + data.keywords[i], data.keywords[i] + "\0");
+ idlTests.push(data.keywords[i], "x" + data.keywords[i], data.keywords[i] + "\0");
+ }
+
+ if (data.keywords[i].length > 1) {
+ var sliced = data.keywords[i].slice(1);
+ // If slicing a value yields another valid value, then skip it since it results in duplicate tests.
+ if (data.keywords.indexOf(sliced) == -1) {
+ domTests.push(sliced);
+ idlTests.push(sliced);
+ }
+ }
+
+ if (data.keywords[i] != data.keywords[i].toLowerCase()) {
+ domTests.push(data.keywords[i].toLowerCase());
+ idlTests.push(data.keywords[i].toLowerCase());
+ }
+ if (data.keywords[i] != data.keywords[i].toUpperCase()) {
+ domTests.push(data.keywords[i].toUpperCase());
+ idlTests.push(data.keywords[i].toUpperCase());
+ }
+ if (data.keywords[i].indexOf("k") != -1) {
+ domTests.push(data.keywords[i].replace(/k/g, "\u212A"));
+ idlTests.push(data.keywords[i].replace(/k/g, "\u212A"));
+ }
+ if (data.keywords[i].indexOf("s") != -1) {
+ domTests.push(data.keywords[i].replace(/s/g, "\u017F"));
+ idlTests.push(data.keywords[i].replace(/s/g, "\u017F"));
+ }
+ }
+
+ // Per spec, the expected DOM values are the same as the value we set
+ // it to.
+ if (!data.isNullable) {
+ idlDomExpected = idlTests.slice(0);
+ } else {
+ idlDomExpected = [];
+ for (var i = 0; i < idlTests.length; i++) {
+ idlDomExpected.push((idlTests[i] === null || idlTests[i] === undefined) ? null : idlTests[i]);
+ }
+ }
+
+ // Now we have the fun of calculating what the expected IDL values are.
+ domExpected = [];
+ idlIdlExpected = [];
+ for (var i = 0; i < domTests.length; i++) {
+ domExpected.push(this.enumExpected(data.keywords, data.nonCanon, data.invalidVal, domTests[i]));
+ }
+ for (var i = 0; i < idlTests.length; i++) {
+ if (data.isNullable && (idlTests[i] === null || idlTests[i] === undefined)) {
+ idlIdlExpected.push(null);
+ } else {
+ idlIdlExpected.push(this.enumExpected(data.keywords, data.nonCanon, data.invalidVal, idlTests[i]));
+ }
+ }
+ break;
+
+ case "string":
+ if ("treatNullAsEmptyString" in data) {
+ for (var i = 0; i < idlTests.length; i++) {
+ if (idlTests[i] === null) {
+ idlDomExpected[i] = idlIdlExpected[i] = "";
+ }
+ }
+ }
+ break;
+
+ case "clamped unsigned long":
+ [data.min - 1, data.min, data.max, data.max + 1].forEach(function(val) {
+ if (domTests.indexOf(val) == -1) {
+ domTests.push(val);
+ }
+ if (idlTests.indexOf(val) == -1 && 0 <= val && val <= maxUnsigned) {
+ idlTests.push(val);
+ if (typeof val != "number") {
+ val = ReflectionTests.parseNonneg(val);
+ }
+ idlDomExpected.push(val > maxInt ? null : val);
+ }
+ });
+
+ // Rewrite expected values
+ domExpected = domTests.map(function(val) {
+ var parsed = ReflectionTests.parseNonneg(String(val));
+ if (parsed === false) {
+ return defaultVal;
+ }
+ if (parsed < data.min) {
+ return data.min;
+ }
+ if (parsed > data.max) {
+ return data.max;
+ }
+ return parsed;
+ });
+ idlIdlExpected = idlTests.map(function(val) {
+ if (typeof val != "number") {
+ val = ReflectionTests.parseNonneg(val);
+ }
+ if (val < 0 || val > maxUnsigned) {
+ throw "Test bug: val should be an unsigned long";
+ }
+ if (val > maxInt) {
+ return defaultVal;
+ }
+ if (val < data.min) {
+ return data.min;
+ }
+ if (val > data.max) {
+ return data.max;
+ }
+ return val;
+ });
+ break;
+ }
+ if (domObj.tagName.toLowerCase() == "canvas" && (domName == "width" || domName == "height")) {
+ // Opera tries to allocate a canvas with the given width and height, so
+ // it OOMs when given excessive sizes. This is permissible under the
+ // hardware-limitations clause, so cut out those checks. TODO: Must be
+ // a way to make this more succinct.
+ domTests = domTests.filter(function(element, index, array) { return domExpected[index] < 1000; });
+ domExpected = domExpected.filter(function(element, index, array) { return element < 1000; });
+ idlTests = idlTests.filter(function(element, index, array) { return idlIdlExpected[index] < 1000; });
+ idlDomExpected = idlDomExpected.filter(function(element, index, array) { return idlIdlExpected[index] < 1000; });
+ idlIdlExpected = idlIdlExpected.filter(function(element, index, array) { return idlIdlExpected[index] < 1000; });
+ }
+ if ((domObj.localName === "form" && domName === "action") ||
+ (["button", "input"].includes(domObj.localName) &&
+ domName === "formAction")) {
+ // Hard-coded special case
+ for (var i = 0; i < domTests.length; i++) {
+ if (domTests[i] === "") {
+ domExpected[i] = domObj.ownerDocument.URL;
+ }
+ }
+ for (var i = 0; i < idlTests.length; i++) {
+ if (idlTests[i] === "") {
+ idlIdlExpected[i] = domObj.ownerDocument.URL;
+ }
+ }
+ }
+ if (data.customGetter) {
+ // These are reflected only on setting, not getting
+ domTests = [];
+ domExpected = [];
+ idlIdlExpected = idlIdlExpected.map(() => null);
+ }
+
+ for (var i = 0; i < domTests.length; i++) {
+ if (domExpected[i] === null && !data.isNullable) {
+ // If you follow all the complicated logic here, you'll find that
+ // this will only happen if there's no expected value at all (like
+ // for tabIndex, where the default is too complicated). So skip
+ // the test.
+ continue;
+ }
+ ReflectionHarness.test(function() {
+ domObj.setAttribute(domName, domTests[i]);
+ ReflectionHarness.assertEquals(domObj.getAttribute(domName),
+ String(domTests[i]), "getAttribute()");
+ ReflectionHarness.assertEquals(idlObj[idlName], domExpected[i],
+ "IDL get");
+ }, "setAttribute() to " + ReflectionHarness.stringRep(domTests[i]));
+ }
+
+ for (var i = 0; i < idlTests.length; i++) {
+ ReflectionHarness.test(function() {
+ if ((data.type == "limited long" && idlTests[i] < 0) ||
+ (data.type == "limited unsigned long" && idlTests[i] == 0)) {
+ ReflectionHarness.assertThrows("IndexSizeError", function() {
+ idlObj[idlName] = idlTests[i];
+ });
+ } else if (data.type == "limited double" && idlTests[i] <= 0) {
+ domObj.setAttribute(domName, "previous value");
+ var previousIdl = idlObj[idlName]; // should be the default value
+ idlObj[idlName] = idlTests[i];
+ ReflectionHarness.assertEquals(domObj.getAttribute(domName),
+ "previous value", "getAttribute()");
+ ReflectionHarness.assertEquals(idlObj[idlName], previousIdl, "IDL get");
+ } else {
+ idlObj[idlName] = idlTests[i];
+ if (data.type == "boolean") {
+ // Special case yay
+ ReflectionHarness.assertEquals(domObj.hasAttribute(domName),
+ Boolean(idlTests[i]), "hasAttribute()");
+ } else if (idlDomExpected[i] !== null || data.isNullable) {
+ var expected = idlDomExpected[i] + "";
+ if (data.isNullable && idlDomExpected[i] === null) {
+ expected = null;
+ }
+ ReflectionHarness.assertEquals(domObj.getAttribute(domName), expected,
+ "getAttribute()");
+ }
+ if (idlIdlExpected[i] !== null || data.isNullable) {
+ ReflectionHarness.assertEquals(idlObj[idlName], idlIdlExpected[i], "IDL get");
+ }
+ }
+ }, "IDL set to " + ReflectionHarness.stringRep(idlTests[i]));
+ }
+};
+
+function toASCIILowerCase(str) {
+ return str.replace(/[A-Z]/g, function(m) { return m.toLowerCase(); });
+}
+
+/**
+ * If we have an enumerated attribute limited to the array of values in
+ * keywords, with nonCanon being a map of non-canonical values to their
+ * canonical equivalents, and invalidVal being the invalid value default (or ""
+ * for none), then what would we expect from an IDL get if the content
+ * attribute is equal to contentVal?
+ */
+ReflectionTests.enumExpected = function(keywords, nonCanon, invalidVal, contentVal) {
+ var ret = invalidVal;
+ for (var i = 0; i < keywords.length; i++) {
+ if (toASCIILowerCase(String(contentVal)) === toASCIILowerCase(keywords[i])) {
+ ret = keywords[i];
+ break;
+ }
+ }
+ if (typeof nonCanon[ret] != "undefined") {
+ return nonCanon[ret];
+ }
+ return ret;
+};
+
+/**
+ * Now we have the data structures that tell us which elements have which
+ * attributes.
+ *
+ * The elements object (which must have been defined in earlier files) is a map
+ * from element name to an object whose keys are IDL attribute names and whose
+ * values are types. A type is of the same format as
+ * ReflectionTests.reflects() accepts, except that there's an extra optional
+ * domAttrName key that gets passed as the fourth argument to reflects() if
+ * it's provided. (TODO: drop the fourth and fifth reflects() arguments and
+ * make it take them from the dictionary instead?)
+ */
+
+// Now we actually run all the tests.
+var unimplemented = [];
+for (var element in elements) {
+ ReflectionTests.reflects("string", "title", element);
+ ReflectionTests.reflects("string", "lang", element);
+ ReflectionTests.reflects({type: "enum", keywords: ["ltr", "rtl", "auto"]}, "dir", element);
+ ReflectionTests.reflects("string", "className", element, "class");
+ ReflectionTests.reflects("tokenlist", "classList", element, "class");
+ ReflectionTests.reflects("boolean", "autofocus", element);
+ ReflectionTests.reflects("boolean", "hidden", element);
+ ReflectionTests.reflects("string", "accessKey", element);
+ // Don't try to test the defaultVal -- it should be either 0 or -1, but the
+ // rules are complicated, and a lot of them are SHOULDs.
+ ReflectionTests.reflects({type: "long", defaultVal: null}, "tabIndex", element);
+ // TODO: classList, contextMenu, itemProp, itemRef
+
+ for (var idlAttrName in elements[element]) {
+ var type = elements[element][idlAttrName];
+ ReflectionTests.reflects(type, idlAttrName, element,
+ typeof type == "object" && "domAttrName" in type ? type.domAttrName : idlAttrName);
+ }
+}
+
+for (var i = 0; i < extraTests.length; i++) {
+ extraTests[i]();
+}
+
+var time = document.getElementById("time");
+if (time) {
+ time.innerHTML = (new Date().getTime() - ReflectionTests.start)/1000;
+}
+
+if (unimplemented.length) {
+ var p = document.createElement("p");
+ p.textContent = "(Note: missing tests for types " + unimplemented.join(", ") + ".)";
+ document.body.appendChild(p);
+}
diff --git a/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html b/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html
new file mode 100644
index 0000000000..c33b411eb4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>Tests the 'blocking' IDL attribute on link, script and style elements</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// Tests that the 'blocking' attribute follows the IDL:
+// [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
+
+test(() => {
+ const link = document.createElement('link');
+ assert_true(link.blocking.supports('render'));
+ assert_false(link.blocking.supports('asdf'));
+}, "Supported tokens of the 'blocking' IDL attribute of the link element");
+
+test(() => {
+ const link = document.createElement('link');
+ link.blocking = 'asdf';
+ assert_equals(link.blocking.value, 'asdf');
+}, "Setting the 'blocking' IDL attribute of the link element");
+
+test(() => {
+ const script = document.createElement('script');
+ assert_true(script.blocking.supports('render'));
+ assert_false(script.blocking.supports('asdf'));
+}, "Supported tokens of the 'blocking' IDL attribute of the script element");
+
+test(() => {
+ const script = document.createElement('script');
+ script.blocking = 'asdf';
+ assert_equals(script.blocking.value, 'asdf');
+}, "Setting the 'blocking' IDL attribute of the script element");
+
+test(() => {
+ const style = document.createElement('style');
+ assert_true(style.blocking.supports('render'));
+ assert_false(style.blocking.supports('asdf'));
+}, "Supported tokens of the 'blocking' IDL attribute of the style element");
+
+test(() => {
+ const style = document.createElement('style');
+ style.blocking = 'asdf';
+ assert_equals(style.blocking.value, 'asdf');
+}, "Setting the 'blocking' IDL attribute of the style element");
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html
new file mode 100644
index 0000000000..36567f9d54
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>`link rel=expect` defers frames until href element is parsed</title>
+
+<link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "blocking defers frames until full parsing");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html
new file mode 100644
index 0000000000..3c907597f7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Frames starts after href element is parsed before the end</title>
+
+<link rel=expect href="#third" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("third")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "blocking defers until needed element is parsed");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html
new file mode 100644
index 0000000000..2858798a35
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding link in the head has an effect</title>
+
+<script>
+let link = document.createElement("link");
+link.rel = "expect";
+link.href = "#last";
+link.blocking = "render";
+document.head.appendChild(link)
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding link in the head defers frames");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html
new file mode 100644
index 0000000000..f45f558720
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing link in the head has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.remove();
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing link in the head makes it no longer blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html
new file mode 100644
index 0000000000..098a3c5767
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing blocking attr in the head has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.blocking = ""
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing 'blocking' makes it no longer blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html
new file mode 100644
index 0000000000..223e42109e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding blocking attr in the head has an effect</title>
+
+<link id=link rel=expect href="#last">
+<script>
+link.blocking = "render"
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding 'blocking=render' in the head makes it blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html
new file mode 100644
index 0000000000..9aa0aeea79
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute that doesn't match makes the link not apply</title>
+
+<link rel=expect href="#last" blocking="render" media="(max-width: 10px)">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "link with non-matching media has no effect");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html
new file mode 100644
index 0000000000..e671dda19c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the head to apply</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(max-width: 10px)">
+<script>
+link.media = "(min-width: 10px)";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to matching causes link to have an effect");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html
new file mode 100644
index 0000000000..8498816ea5
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the head to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(min-width: 10px)">
+<script>
+link.media = "(max-width: 10px)";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to non-matching makes it non blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html
new file mode 100644
index 0000000000..ef6f709012
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the head to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.rel = "stylesheet";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to non-expect makes it non blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html
new file mode 100644
index 0000000000..dee82d8c59
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the head to apply</title>
+
+<link id=link rel=stylesheet href="#last" blocking="render">
+<script>
+link.rel = "expect";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to expect in the head causes it to be blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html
new file mode 100644
index 0000000000..4110e54c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the head to apply</title>
+
+<link id=link rel=expect href="" blocking="render">
+<script>
+link.href = "#last";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding href in the head makes it blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html
new file mode 100644
index 0000000000..ecd97be86a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the head to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.href = "";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing href makes it no longer blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html
new file mode 100644
index 0000000000..ea8948de42
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Blocking link added in the body has no effect</title>
+
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "link in the body has no effect");
+</script>
+</head>
+<body>
+<link rel=expect href="#last" blocking="render">
+<script>
+let link = document.createElement("link");
+link.rel = "rel";
+link.href = "#last";
+link.blocking = "render";
+document.head.appendChild(link);
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html
new file mode 100644
index 0000000000..a775ee4174
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Blocking link removed in the body has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing link the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.remove();
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html
new file mode 100644
index 0000000000..8968c5dacd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing blocking attr in the body has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing 'blocking' in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.blocking = "";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html
new file mode 100644
index 0000000000..2d3b574721
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding blocking attr in the body has no effect</title>
+
+<link id=link rel=expect href="#last">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding 'blocking=render' in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.blocking = "render"
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html
new file mode 100644
index 0000000000..76e6394b5b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the body to apply, but has no effect</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(max-width: 10px)">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to matching in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.media = "(min-width: 10px)";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html
new file mode 100644
index 0000000000..80a7019edc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the body to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(min-width: 10px)">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to non-matching in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.media = "(max-width: 10px)";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html
new file mode 100644
index 0000000000..10019c943f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the body to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to non-expect in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.rel = "stylesheet";
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html
new file mode 100644
index 0000000000..1ca2114689
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the body to apply, but has no effect</title>
+
+<link id=link rel=stylesheet href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to expect in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.rel = "expect";
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html
new file mode 100644
index 0000000000..5dfbcac30a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the body to apply, but has no effect</title>
+
+<link id=link rel=expect href="" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding href in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.href = "#last";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html
new file mode 100644
index 0000000000..8fe8b6a8c8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the body to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing href in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.href = "";
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html
new file mode 100644
index 0000000000..19e4020fb7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Unknown href causes the whole document to be blocked</title>
+
+<link rel=expect href="#unknown" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "unknown href causes the whole document to be blocked");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html
new file mode 100644
index 0000000000..689ae69f45
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding an id to parsed element satisfies render block</title>
+
+<link rel=expect href="#first" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("first")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding an id in the body satisfies render block");
+</script>
+</head>
+<body>
+ <div id="willbefirst"></div>
+ <script>
+ willbefirst.id = "first";
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html
new file mode 100644
index 0000000000..6abfc43b8b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing id keeps render block satisfied</title>
+
+<link rel=expect href="#first" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("wasfirst")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing id after it was renderer keeps render block satisfied");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ first.id = "wasfirst";
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html
new file mode 100644
index 0000000000..56f88e0fc2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Unknown href causes the whole document to be blocked</title>
+
+<link id=link rel=expect href="#unknown" blocking="render">
+<script>
+link.href = "#stillunknown";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "unknown href causes the whole document to be blocked (with href changes!)");
+</script>
+</head>
+<body>
+ <div id="notfirst"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html
new file mode 100644
index 0000000000..a64d542c4a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Multiple links and all but one removed</title>
+
+<link rel=expect href="#third" blocking="render">
+<link id=one rel=expect href="#third" blocking="render">
+<link id=two rel=expect href="#third" blocking="render">
+<link id=three rel=expect href="#third" blocking="render">
+<link id=four rel=expect href="#third" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("third")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing some links but not all keeps at least the matching link blocking");
+
+one.remove();
+two.remove();
+</script>
+</head>
+<body>
+<script>
+three.remove();
+four.remove();
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html b/testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html
new file mode 100644
index 0000000000..a4c32ea037
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Tests when script is not implicitly potentially render-blocking</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/7894">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<!--
+ The test is marked "optional" because even when the document is not
+ render-blocked, the user agent is still free to take other factors, which are
+ not limited by the spec, into consideration and therefore decide not to
+ render. However, it is still more desirable if rendering starts
+ immediately/soon.
+-->
+
+<script class="test" data="parser-inserted async script" async
+ src="support/dummy-1.js?pipe=trickle(d1)&async"></script>
+<script class="test" data="parser-inserted defer script" defer
+ src="support/dummy-1.js?pipe=trickle(d1)&defer"></script>
+<script class="test" data="parser-inserted module script" type="module"
+ src="support/dummy-1.mjs?pipe=trickle(d1)"></script>
+<script class="test" data="parser-inserted async module script" type="module"
+ async src="support/dummy-1.mjs?pipe=trickle(d1)&async"></script>
+
+<script>
+function addTestScriptElement(title, attributes) {
+ let element = document.createElement('script');
+ element.className = 'test';
+ element.setAttribute('data', title);
+ Object.assign(element, attributes);
+ document.head.appendChild(element);
+}
+
+addTestScriptElement('script-inserted script', {src: 'support/dummy-1.js?pipe=trickle(d1)&dynamic'});
+addTestScriptElement('script-inserted sync script', {async: false, src: 'support/dummy-1.js?pipe=trickle(d1)&dynamicSync'});
+addTestScriptElement('script-inserted module script', {type: 'module', src: 'support/dummy-1.mjs?pipe=trickle(d1)&dynamic'});
+</script>
+
+<div id="dummy">Some text</div>
+
+<script>
+const testElements = [...document.querySelectorAll('.test')];
+const loadObservers = testElements.map(element => new LoadObserver(element));
+
+promise_setup(async () => {
+ // Test cases are run after rendering is unblocked.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+});
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ async () => assert_false(loadObservers[index].finished),
+ testElements[index].getAttribute('data') + ' is not implicitly render-blocking');
+}
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ () => loadObservers[index].load,
+ testElements[index].getAttribute('data') + ' should eventually be loaded and evaluated');
+}
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html
new file mode 100644
index 0000000000..8d391144b2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-blocking script elements are implicitly render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Add some renderable content before parser inserts body
+document.documentElement.appendChild(document.createTextNode('text'));
+
+// Test must be setup before the parser-blocking script
+test_render_blocking(
+ () => assert_equals(window.dummy, 1),
+ 'Parser-blocking script is evaluated');
+</script>
+
+<script src="support/dummy-1.js?pipe=trickle(d1)"></script>
+
+<div>Some more text</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html
new file mode 100644
index 0000000000..50a2add277
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<head>
+<title>Parser-inserted async inline module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.did_execute_script = false;
+</script>
+<script type="module" blocking="render" async>
+ import "/loading/resources/dummy.js?pipe=trickle(d1)";
+ window.did_execute_script = true;
+</script>
+</head>
+<div id="dummy">some text</div>
+
+<script>
+ promise_test(async t => {
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ assert_true(window.did_execute_script, "Parser-inserted async render-blocking inline module script should execute before rAF callback");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html
new file mode 100644
index 0000000000..4b2216dfcb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-inserted async script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script id="async-script" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)">
+</script>
+
+<div>Some text</div>
+
+<script>
+const asyncScript = document.getElementById('async-script');
+test_render_blocking(
+ asyncScript,
+ () => assert_equals(window.dummy, 1),
+ 'Parser-inserted render-blocking async script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html
new file mode 100644
index 0000000000..1ae8caf2ec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-inserted defer script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script id="defer-script" defer blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)">
+</script>
+
+<div>Some text</div>
+
+<script>
+const deferScript = document.getElementById('defer-script');
+test_render_blocking(
+ deferScript,
+ () => assert_equals(window.dummy, 1),
+ 'Parser-inserted render-blocking defer script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html
new file mode 100644
index 0000000000..af2ac46aaf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<head>
+<title>Parser-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.did_execute_script = false;
+</script>
+<script type="module" blocking="render">
+ import "/loading/resources/dummy.js?pipe=trickle(d1)";
+ window.did_execute_script = true;
+</script>
+</head>
+<div id="dummy">some text</div>
+
+<script>
+ promise_test(async t => {
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ assert_true(window.did_execute_script, "Parser-inserted render-blocking inline module script should execute before rAF callback");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html
new file mode 100644
index 0000000000..2bca88e73c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script id="module-script" type="module" blocking="render"
+ src="support/dummy-1.mjs?pipe=trickle(d1)">
+</script>
+
+<div id="dummy">some text</div>
+
+<script>
+const moduleScript = document.getElementById('module-script');
+test_render_blocking(
+ moduleScript,
+ () => assert_equals(document.getElementById('dummy').textContent, '1'),
+ 'Parser-inserted render-blocking module script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html
new file mode 100644
index 0000000000..9a358aa493
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Parser-inserted style elements are implicitly render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+<script>
+// Test case must be set up before the stylesheet, because the stylesheet is
+// script-blocking, which means we can't set it up while the stylesheet is
+// loading.
+test_render_blocking(() => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+}, 'Render-blocking stylesheet is applied');
+</script>
+<style>
+@import url('support/target-red.css?pipe=trickle(d1)');
+</style>
+<div class="target">
+ This should be red
+</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html
new file mode 100644
index 0000000000..0a771448fd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Parser-inserted stylesheet links are implicitly render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+<script>
+// Test case must be set up before the stylesheet, because the stylesheet is
+// script-blocking, which means we can't set it up while the stylesheet is
+// loading.
+test_render_blocking(() => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+}, 'Render-blocking stylesheet is applied');
+</script>
+<link rel="stylesheet" href="support/target-red.css?pipe=trickle(d1)">
+<div class="target">
+ This should be red
+</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html
new file mode 100644
index 0000000000..451d2f3695
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Synchronous script element still blocks rendering after removing `blocking=render`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Test script must be added before the synchronous script because the
+// synchronous script is parser-blocking.
+
+promise_setup(async () => {
+ let script = await nodeInserted(document.head, node => node.id === 'script');
+ script.blocking = '';
+
+ // Also inserts some contents for non-compliant UA to render
+ document.body = document.createElement('body');
+ document.body.appendChild(document.createTextNode('Some text'));
+});
+
+test_render_blocking(
+ () => assert_equals(window.dummy, 1),
+ 'Render-blocking script is loaded and evaluated');
+</script>
+
+<script id="script" blocking="render" src="support/dummy-1.js?pipe=trickle(d1)"></script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html
new file mode 100644
index 0000000000..31d4b56838
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Parser-inserted style element still blocks rendering after removing `blocking=render`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Test script must be added before the style element because the style
+// element is script-blocking.
+
+promise_setup(async () => {
+ let sheet = await nodeInserted(document.head, node => node.id === 'sheet');
+ sheet.blocking = '';
+});
+
+test_render_blocking(
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
+
+<style id="sheet" blocking="render">
+@import url("support/target-red.css?pipe=trickle(d1)");
+</style>
+
+<div class="target">Some text</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html
new file mode 100644
index 0000000000..1248b90b23
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Parser-inserted stylesheet link still blocks rendering after removing `blocking=render`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Test script must be added before the stylesheet link because the stylesheet
+// link is script-blocking.
+
+promise_setup(async () => {
+ let sheet = await nodeInserted(document.head, node => node.id === 'sheet');
+ sheet.blocking = '';
+});
+
+test_render_blocking(
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
+
+<link id="sheet" rel="stylesheet" blocking="render"
+ href="support/target-red.css?pipe=trickle(d1)">
+
+<div class="target">Some text</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html
new file mode 100644
index 0000000000..c73e3c6452
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<title>Removing `blocking=render` should unblock rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#blocking-attribute">
+<link rel="help" href="https://html.spec.whatwg.org/C/#rendering-opportunity">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<!--
+ The test is marked "optional" because even when the document is no longer
+ render-blocked, the user agent is still free to take other factors, which are
+ not limited by the spec, into consideration and therefore decide not to
+ render. However, it is still more desirable if rendering starts
+ immediately/soon.
+-->
+
+<script class="test" data="parser-inserted async script" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&async"></script>
+<script class="test" data="parser-inserted defer script" defer blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&defer"></script>
+<script class="test" data="parser-inserted module script" type="module"
+ blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)"></script>
+<script class="test" data="parser-inserted async module script" type="module"
+ async blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)&async"></script>
+
+<!--
+ No test for parser-inserted stylesheets and synchronous scripts because they
+ are render-blocking by default, so removing `blocking=render` does not unblock
+ rendering.
+-->
+
+<script>
+function addRenderBlockingElement(tag, title, attributes, optional_text) {
+ let element = document.createElement(tag);
+ element.className = 'test';
+ element.setAttribute('data', title);
+ element.blocking = 'render';
+ Object.assign(element, attributes);
+ if (optional_text)
+ element.textContent = optional_text;
+ document.head.appendChild(element);
+}
+
+addRenderBlockingElement(
+ 'link', 'script-inserted stylesheet link',
+ {rel: 'stylesheet', blocking: 'render', href: 'support/target-red.css?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'script', 'script-inserted script',
+ {src: 'support/dummy-1.js?pipe=trickle(d1)&dynamic'});
+addRenderBlockingElement(
+ 'script', 'script-inserted module script',
+ {type: 'module', src: 'support/dummy-1.mjs?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'style', 'script-inserted inline style', {},
+ '@import url("support/target-red.css?pipe=trickle(d1)&imported&dynamic")');
+</script>
+
+<div id="dummy">Some text</div>
+
+<script>
+const testElements = [...document.querySelectorAll('.test')];
+const loadObservers = testElements.map(element => new LoadObserver(element));
+
+promise_setup(async () => {
+ for (let element of testElements)
+ element.blocking = '';
+
+ // Test cases are run after rendering is unblocked.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+});
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ async () => assert_false(loadObservers[index].finished),
+ 'Render-blocking on ' + testElements[index].getAttribute('data') + ' is cancellable');
+}
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ () => loadObservers[index].load,
+ 'Loading of ' + testElements[index].getAttribute('data') + ' should eventually succeed');
+}
+</script>
+
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html b/testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html
new file mode 100644
index 0000000000..ad49c48c2e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<title>Removing render-blocking element should unblock rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#blocking-attribute">
+<link rel="help" href="https://html.spec.whatwg.org/C/#rendering-opportunity">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<!--
+ The test is marked "optional" because even when the document is no longer
+ render-blocked, the user agent is still free to take other factors, which are
+ not limited by the spec, into consideration and therefore decide not to
+ render. However, it is still more desirable if rendering starts
+ immediately/soon.
+-->
+
+<script class="test" data="parser-inserted async script" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&async"></script>
+<script class="test" data="parser-inserted defer script" defer blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&defer"></script>
+<script class="test" data="parser-inserted module script" type="module"
+ blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)"></script>
+<script class="test" data="parser-inserted async module script" type="module"
+ async blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)&async"></script>
+
+<!--
+ No test for parser-inserted stylesheets and synchronous scripts because
+ they are script-blocking or even parser-blocking, and they do not have new
+ behaviors to test about.
+-->
+
+<script>
+function addRenderBlockingElement(tag, title, attributes, optional_text) {
+ let element = document.createElement(tag);
+ element.className = 'test';
+ element.setAttribute('data', title);
+ element.blocking = 'render';
+ Object.assign(element, attributes);
+ if (optional_text)
+ element.textContent = optional_text;
+ document.head.appendChild(element);
+}
+
+addRenderBlockingElement(
+ 'link', 'script-inserted stylesheet link',
+ {rel: 'stylesheet', blocking: 'render', href: 'support/target-red.css?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'script', 'script-inserted script',
+ {src: 'support/dummy-1.js?pipe=trickle(d1)&dynamic'});
+addRenderBlockingElement(
+ 'script', 'script-inserted module script',
+ {type: 'module', src: 'support/dummy-1.mjs?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'style', 'script-inserted inline style', {},
+ '@import url("support/target-red.css?pipe=trickle(d1)&imported&dynamic")');
+</script>
+
+<div id="dummy">Some text</div>
+
+<script>
+const testElements = [...document.querySelectorAll('.test')];
+const loadObservers = testElements.map(element => new LoadObserver(element));
+
+promise_setup(async () => {
+ for (let element of testElements)
+ element.remove();
+
+ // Test cases are run after rendering is unblocked.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+});
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ async () => assert_false(loadObservers[index].finished),
+ 'Render-blocking on ' + testElements[index].getAttribute('data') + ' is cancellable');
+
+ // The loading can either continue or cancel. This test does not assert it.
+ loadObservers[index].load.catch(() => {});
+}
+</script>
+
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html b/testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html
new file mode 100644
index 0000000000..5f6e8b34d1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Removed render-blocking script should not indefinitely block rendering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script id="target" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)"></script>
+<script>
+promise_test(async () => {
+ const target = document.getElementById('target');
+ const newDoc = document.implementation.createHTMLDocument('new document');
+ newDoc.documentElement.appendChild(target);
+
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ // reqeustAnimationFrame() should be eventually run, but the script removed
+ // while pending should not be run.
+ assert_equals(window.dummy, undefined);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html
new file mode 100644
index 0000000000..576c0b321a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<head>
+<title>Script-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.did_execute_script = false;
+ const script = document.createElement("script");
+ script.type = "module";
+ script.blocking = "render";
+ script.textContent = `
+ import "/loading/resources/dummy.js?pipe=trickle(d1)";
+ window.did_execute_script = true;
+ `;
+ document.head.append(script);
+</script>
+</head>
+<div id="dummy">some text</div>
+
+<script>
+ promise_test(async t => {
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ assert_true(window.did_execute_script, "Script-inserted render-blocking inline module script should execute before rAF callback");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html
new file mode 100644
index 0000000000..73f0d3cdf4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Script-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const moduleScript = document.createElement('script');
+moduleScript.type = 'module';
+moduleScript.blocking = 'render';
+moduleScript.src = 'support/dummy-1.mjs?pipe=trickle(d1)';
+document.head.appendChild(moduleScript);
+</script>
+
+<div id="dummy">some text</div>
+
+<script>
+test_render_blocking(
+ moduleScript,
+ () => assert_equals(document.getElementById('dummy').textContent, '1'),
+ 'Script-inserted render-blocking module script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html
new file mode 100644
index 0000000000..faf346b4dd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Script-inserted script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const script = document.createElement('script');
+script.src = 'support/dummy-1.js?pipe=trickle(d1)';
+script.blocking = 'render';
+document.head.appendChild(script);
+</script>
+
+<div>Some text</div>
+
+<script>
+test_render_blocking(
+ script,
+ () => assert_equals(window.dummy, 1),
+ 'Script-inserted render-blocking script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html
new file mode 100644
index 0000000000..683706af50
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Script-inserted style elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const style = document.createElement('style');
+style.blocking = 'render';
+style.textContent = "@import url('support/target-red.css?pipe=trickle(d1)');";
+document.head.appendChild(style);
+</script>
+
+<div class="target">
+ This should be red
+</div>
+
+<script>
+test_render_blocking(
+ style,
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html
new file mode 100644
index 0000000000..46755387d7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>Script-inserted stylesheet links with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const stylesheet = document.createElement('link');
+stylesheet.rel = 'stylesheet';
+stylesheet.href = 'support/target-red.css?pipe=trickle(d1)';
+stylesheet.blocking = 'render';
+document.head.appendChild(stylesheet);
+</script>
+
+<div class="target">
+ This should be red
+</div>
+
+<script>
+test_render_blocking(
+ stylesheet,
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js
new file mode 100644
index 0000000000..597772cf64
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js
@@ -0,0 +1 @@
+window.dummy = 1;
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs
new file mode 100644
index 0000000000..9b85a21033
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs
@@ -0,0 +1 @@
+document.getElementById('dummy').textContent = 1;
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/target-red.css b/testing/web-platform/tests/html/dom/render-blocking/support/target-red.css
new file mode 100644
index 0000000000..a387acd4ec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/target-red.css
@@ -0,0 +1,3 @@
+.target {
+ color: red;
+}
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js b/testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js
new file mode 100644
index 0000000000..71d0d68096
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js
@@ -0,0 +1,118 @@
+// Observes the `load` event of an EventTarget, or the finishing of a resource
+// given its url. Requires `/preload/resources/preload_helper.js` for the latter
+// usage.
+class LoadObserver {
+ constructor(target) {
+ this.finishTime = null;
+ this.load = new Promise((resolve, reject) => {
+ if (target.addEventListener) {
+ target.addEventListener('load', ev => {
+ this.finishTime = ev.timeStamp;
+ resolve(ev);
+ });
+ target.addEventListener('error', reject);
+ } else if (typeof target === 'string') {
+ const observer = new PerformanceObserver(() => {
+ if (numberOfResourceTimingEntries(target)) {
+ this.finishTime = performance.now();
+ resolve();
+ }
+ });
+ observer.observe({type: 'resource', buffered: true});
+ } else {
+ reject('Unsupported target for LoadObserver');
+ }
+ });
+ }
+
+ get finished() {
+ return this.finishTime !== null;
+ }
+}
+
+// Observes the insertion of a script/parser-blocking element into DOM via
+// MutationObserver, so that we can access the element before it's loaded.
+function nodeInserted(parentNode, predicate) {
+ return new Promise(resolve => {
+ function callback(mutationList) {
+ for (let mutation of mutationList) {
+ for (let node of mutation.addedNodes) {
+ if (predicate(node))
+ resolve(node);
+ }
+ }
+ }
+ new MutationObserver(callback).observe(parentNode, {childList: true});
+ });
+}
+
+function createAutofocusTarget() {
+ const autofocusTarget = document.createElement('textarea');
+ autofocusTarget.setAttribute('autofocus', '');
+ // We may not have a body element at this point if we are testing a
+ // script-blocking stylesheet. Hence, the new element is added to
+ // documentElement.
+ document.documentElement.appendChild(autofocusTarget);
+ return autofocusTarget;
+}
+
+function createScrollTarget() {
+ const scrollTarget = document.createElement('div');
+ scrollTarget.style.overflow = 'scroll';
+ scrollTarget.style.height = '100px';
+ const scrollContent = document.createElement('div');
+ scrollContent.style.height = '200px';
+ scrollTarget.appendChild(scrollContent);
+ document.documentElement.appendChild(scrollTarget);
+ return scrollTarget;
+}
+
+function createAnimationTarget() {
+ const style = document.createElement('style');
+ style.textContent = `
+ @keyframes anim {
+ from { height: 100px; }
+ to { height: 200px; }
+ }
+ `;
+ const animationTarget = document.createElement('div');
+ animationTarget.style.backgroundColor = 'green';
+ animationTarget.style.height = '50px';
+ animationTarget.style.animation = 'anim 100ms';
+ document.documentElement.appendChild(style);
+ document.documentElement.appendChild(animationTarget);
+ return animationTarget;
+}
+
+// Error margin for comparing timestamps of paint and load events, in case they
+// are reported by different threads.
+const epsilon = 50;
+
+function test_render_blocking(optionalElementOrUrl, finalTest, finalTestTitle) {
+ // Ideally, we should observe the 'load' event on the specific render-blocking
+ // elements. However, this is not possible for script-blocking stylesheets, so
+ // we have to observe the 'load' event on 'window' instead.
+ if (!(optionalElementOrUrl instanceof HTMLElement) &&
+ typeof optionalElementOrUrl !== 'string') {
+ finalTestTitle = finalTest;
+ finalTest = optionalElementOrUrl;
+ optionalElementOrUrl = undefined;
+ }
+ const loadObserver = new LoadObserver(optionalElementOrUrl || window);
+
+ promise_test(async test => {
+ assert_implements(window.PerformancePaintTiming);
+
+ await test.step_wait(() => performance.getEntriesByType('paint').length);
+
+ assert_true(loadObserver.finished);
+ for (let entry of performance.getEntriesByType('paint')) {
+ assert_greater_than(entry.startTime, loadObserver.finishTime - epsilon,
+ `${entry.name} should occur after loading render-blocking resources`);
+ }
+ }, 'Rendering is blocked before render-blocking resources are loaded');
+
+ promise_test(test => {
+ return loadObserver.load.then(() => finalTest(test));
+ }, finalTestTitle);
+}
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/utils.js b/testing/web-platform/tests/html/dom/render-blocking/support/utils.js
new file mode 100644
index 0000000000..8a9a537e96
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/utils.js
@@ -0,0 +1,5 @@
+function generateParserDelay(seconds = 1) {
+ document.write(`
+ <script src="/loading/resources/dummy.js?pipe=trickle(d${seconds})"></script>
+ `);
+}
diff --git a/testing/web-platform/tests/html/dom/resources/self-origin-subframe.html b/testing/web-platform/tests/html/dom/resources/self-origin-subframe.html
new file mode 100644
index 0000000000..8759362bdf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/resources/self-origin-subframe.html
@@ -0,0 +1,22 @@
+<script>
+ window.onmessage = function(e){
+ if (e.data == "getOrigin") {
+ parent.postMessage(self.origin, "*");
+ } else if (e.data == "setDomainAndGetOrigin") {
+ var oldDomain = document.domain;
+ try {
+ document.domain = document.domain.replace(/^[^.]*./, "");
+ } catch (e) {
+ parent.postMessage("THREW WHEN SETTING DOMAIN: " + e, "*");
+ return;
+ }
+ if (oldDomain === document.domain) {
+ parent.postMessage("FAILED TO SET DOMAIN", "*");
+ } else {
+ parent.postMessage(self.origin, "*");
+ }
+ } else {
+ parent.postMessage("UNEXPECTED MESSAGE", "*");
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/dom/self-origin.any.js b/testing/web-platform/tests/html/dom/self-origin.any.js
new file mode 100644
index 0000000000..c103a32144
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/self-origin.any.js
@@ -0,0 +1,5 @@
+"use strict";
+
+test(function() {
+ assert_equals(self.origin, "http://" + location.host);
+}, "self.origin should be correct");
diff --git a/testing/web-platform/tests/html/dom/self-origin.sub.html b/testing/web-platform/tests/html/dom/self-origin.sub.html
new file mode 100644
index 0000000000..aba2b3016a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/self-origin.sub.html
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe></iframe>
+<iframe id="blob-test"></iframe> <!-- will get blob: URI -->
+<iframe src="javascript:'javascript'"></iframe>
+<iframe srcdoc="srcdoc"></iframe>
+<!-- Use the non-default HTTP port so we can make sure it gets included in
+ self.origin -->
+<iframe src="http://{{domains[www1]}}:{{ports[http][1]}}{{location[path]}}/../resources/self-origin-subframe.html"></iframe>
+<!-- Using the Unicode version on purpose, we expect to get back the Punycode
+ version in self.origin -->
+<iframe src="http://élève.{{domains[]}}:{{ports[http][1]}}{{location[path]}}/../resources/self-origin-subframe.html"></iframe>
+<iframe src="resources/self-origin-subframe.html" sandbox="allow-scripts"></iframe>
+<script type="application/javascript">
+test(function() {
+ var blob = new Blob(['blob']);
+ var url = URL.createObjectURL(blob);
+ document.getElementById("blob-test").src = url;
+}, "Assigning blob url");
+
+/* Each message test is a four things: window to send message to, message to
+ send, expected response, async test to use. */
+var messageTests = [
+ [ frames[4], "getOrigin", "http://{{domains[www1]}}:{{ports[http][1]}}",
+ async_test("Should have the right origin for cross-origin subframe") ],
+ [ frames[4], "setDomainAndGetOrigin", "http://{{domains[www1]}}:{{ports[http][1]}}",
+ async_test("Should have the right origin for cross-origin subframe after setting document.domain") ],
+ [ frames[5], "getOrigin", "http://xn--lve-6lad.{{domains[]}}:{{ports[http][1]}}",
+ async_test("Should have the right origin for IDN subframe") ],
+ [ frames[5], "setDomainAndGetOrigin", "http://xn--lve-6lad.{{domains[]}}:{{ports[http][1]}}",
+ async_test("Should have the right origin for IDN subframe after setting document.domain") ],
+ [ frames[6], "getOrigin", "null",
+ async_test("Should have the right origin for sandboxed iframe") ],
+];
+
+var curTest = 0;
+function nextMessageTest() {
+ if (curTest == messageTests.length) {
+ return;
+ }
+
+ var testData = messageTests[curTest];
+ testData[0].postMessage(testData[1], "*");
+}
+
+window.onmessage = function(e) {
+ var testData = messageTests[curTest++];
+ testData[3].step(function() {
+ assert_equals(e.data, testData[2])
+ });
+ testData[3].done();
+ nextMessageTest();
+}
+
+addEventListener("load", nextMessageTest);
+
+test(function() {
+ assert_equals(self.origin, "http://{{location[host]}}");
+}, "We should have the right origin for our page");
+
+var t1 = async_test("about:blank subframe origins");
+addEventListener("load", t1.step_func_done(function() {
+ assert_equals(frames[0].location.origin, "null",
+ "Should have the right location origin for about:blank iframe");
+ assert_equals(frames[0].origin, "http://{{location[host]}}",
+ "Should have the right origin for about:blank iframe");
+}));
+
+var t2 = async_test("blob: subframe origins");
+addEventListener("load", t2.step_func_done(function() {
+ assert_equals(frames[1].location.origin, "http://{{location[host]}}",
+ "Should have the right location origin for blob: iframe");
+ assert_equals(frames[1].origin, "http://{{location[host]}}",
+ "Should have the right origin for blob: iframe");
+}));
+
+var t3 = async_test("javascript: subframe origins");
+addEventListener("load", t3.step_func_done(function() {
+ assert_equals(frames[2].origin, "http://{{location[host]}}",
+ "Should have the right origin for javascript: iframe");
+}));
+
+var t4 = async_test("srcdoc subframe origins");
+addEventListener("load", t4.step_func_done(function() {
+ assert_equals(frames[3].location.origin, "null",
+ "Should have the right location origin for srcdoc iframe");
+ assert_equals(frames[3].origin, "http://{{location[host]}}",
+ "Should have the right origin for srcdoc iframe");
+}));
+</script>
diff --git a/testing/web-platform/tests/html/dom/usvstring-reflection.https.html b/testing/web-platform/tests/html/dom/usvstring-reflection.https.html
new file mode 100644
index 0000000000..775cb49281
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/usvstring-reflection.https.html
@@ -0,0 +1,139 @@
+<!doctype html>
+<title>USVString test relate to url</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="../../webrtc/RTCPeerConnection-helper.js"></script>
+<div id=log></div>
+<script>
+// Unpaired surrogate codepoints present in USVString are replaced
+// with U+FFFD. %EF%BF%BD is UTF-8 encoding of U+FFFD.
+'use strict';
+test(() => {
+ location.hash = '\uD999';
+ assert_equals(location.hash, '#%EF%BF%BD');
+}, "location.hash : unpaired surrogate codepoint should be replaced with U+FFFD");
+
+test(() => {
+ var w = window.open("about:blank#\uD800");
+ assert_equals(w.location.href, 'about:blank#%EF%BF%BD');
+ w.location.href = 'about:blank#\uD999';
+ assert_equals(w.location.href, 'about:blank#%EF%BF%BD');
+}, "location.href : unpaired surrogate codepoint should be replaced with U+FFFD");
+
+test(() => {
+ var w = window.open("about:blank#\uD800");
+ assert_equals(w.location.hash, '#%EF%BF%BD');
+}, "window.open : unpaired surrogate codepoint should be replaced with U+FFFD");
+
+test(() => {
+ var w = document.open("about:blank#\uD800", "", "");
+ assert_equals(w.location.hash, '#%EF%BF%BD');
+}, "document.open : unpaired surrogate codepoint should be replaced with U+FFFD");
+
+test(() => {
+ var element = document.createElement("a");
+ element.ping = '\uD989';
+ assert_equals(element.ping, '\uFFFD');
+}, "anchor : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var element = document.createElement("area");
+ element.ping = '\uDA99';
+ assert_equals(element.ping, '\uFFFD');
+}, "area : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var element = document.createElement("base");
+ element.href = '\uD989';
+ assert_equals(element.href.endsWith('%EF%BF%BD'), true);
+}, "base : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var src = new EventSource('\uD899');
+ assert_equals(src.url.endsWith('%EF%BF%BD'), true);
+}, "EventSource : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var element = document.createElement("frame");
+ element.src = '\uDCA9';
+ element.longDesc = '\uDCA8';
+ assert_equals(element.src.endsWith('%EF%BF%BD'), true);
+ assert_equals(element.longDesc.endsWith('%EF%BF%BD'), true);
+}, "frame : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var element = document.createElement("iframe");
+ element.src = '\uDC89';
+ element.longDesc = '\uDC88';
+ assert_equals(element.src.endsWith('%EF%BF%BD'), true);
+ assert_equals(element.longDesc.endsWith('%EF%BF%BD'), true);
+}, "iframe : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var element = document.createElement("link");
+ element.href = '\uDB89';
+ assert_equals(element.href.endsWith('%EF%BF%BD'), true);
+}, "link : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var element = document.createElement("source");
+ element.src = '\uDDDD';
+ element.srcset = '\uD800';
+ assert_equals(element.src.endsWith('%EF%BF%BD'), true);
+ assert_equals(element.srcset, '\uFFFD');
+}, "source : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ const event = new StorageEvent('storage', {
+ url: window.location.href + '\uD999',
+ });
+ assert_equals(event.url, window.location.href + "\uFFFD");
+}, "storage event : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ var wsocket = new EventSource('ws://www.example.com/socketserve\uD899/');
+ assert_true(wsocket.url.endsWith('ws://www.example.com/socketserve%EF%BF%BD/'));
+}, "websocket url : unpaired surrogate codepoint should be replaced with U+FFFD")
+
+test(() => {
+ try {
+ navigator.sendBeacon("resources/\uD800blank.txt");
+ assert_true(true);
+ } catch (e) {
+ assert_true(false);
+ }
+}, "sendBeacon URL: unpaired surrogate codepoint should not make any exceptions.")
+
+test(() => {
+ // This shouldn't throw an exception.
+ window.navigator.registerProtocolHandler('web+myprotocol', "custom-scheme\uD800/url=%s", "title");
+}, "RegisterProtocolHandler URL: unpaired surrogate codepoint should not make any exceptions.")
+
+test(() => {
+ // This shouldn't throw an exception.
+ window.navigator.unregisterProtocolHandler('web+myprotocol', "custom-scheme\uD800/url=%s");
+}, "UnregisterProtocolHandler URL: unpaired surrogate codepoint should not make any exceptions.")
+
+test(() => {
+ var w = window.open("about:blank#\uD800");
+ assert_equals(w.document.URL, 'about:blank#%EF%BF%BD');
+ assert_equals(w.document.documentURI, 'about:blank#%EF%BF%BD');
+}, "Document URLs: unpaired surrogate codepoint should be replaced with U+FFFD")
+
+promise_test(t => {
+ const sendString = 'hello\uD999';
+ const receiveString = 'hello\uFFFD';
+
+ return createDataChannelPair(t)
+ .then(([channel1, channel2]) => {
+ channel1.send(sendString);
+ return awaitMessage(channel2)
+ }).then(message => {
+ assert_equals(typeof message, 'string',
+ 'Expect message to be a string');
+
+ assert_equals(message, receiveString);
+ });
+}, "RTCDataChannel.send: unpaired surrogate codepoint should be replaced with U+FFFD.")
+
+</script>
diff --git a/testing/web-platform/tests/html/editing/activation/click.html b/testing/web-platform/tests/html/editing/activation/click.html
new file mode 100644
index 0000000000..edbc477db6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/activation/click.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLElement#click</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ var element = document.createElement("div");
+ var received = false;
+ element.addEventListener("click", this.step_func(function(e) {
+ received = true;
+ assert_false(e.isTrusted, "Event should not be trusted")
+ }));
+ element.click();
+ assert_true(received, "click event should have been dispatched synchronously");
+})
+</script>
diff --git a/testing/web-platform/tests/html/editing/activation/click_checkbox.html b/testing/web-platform/tests/html/editing/activation/click_checkbox.html
new file mode 100644
index 0000000000..ab81cbf063
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/activation/click_checkbox.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>Interaction of UI input and the click in progress flag</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<p>When you mouse click the checkbox below it should not be checked:</p>
+<p><input type=checkbox onclick=this.click() id="target"></p>
+<p>Now keyboard "click" the checkbox and confirm it's still not checked.</p>
+<script>
+promise_test(async t => {
+ var target = document.getElementById("target");
+ var received = false;
+ target.addEventListener("click", t.step_func(function(e) {
+ received = true;
+ assert_false(target.checked, "The checkbox should not be checked")
+ }));
+
+ await test_driver.click(target);
+ assert_true(received, "click event should have been dispatched synchronously");
+});
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/README b/testing/web-platform/tests/html/editing/dnd/README
new file mode 100644
index 0000000000..b0b8207a6c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/README
@@ -0,0 +1,23 @@
+This is a test suite for the drag and drop API described in the HTML5
+specification:
+
+ http://dev.w3.org/html5/spec/dnd.html#dnd
+
+Tests in target-origin/ relate to a proposed spec extension and are not covered
+by HTML5 drafts at the time of writing. Contact Opera Sofware for details, and
+mention CT-1656.
+
+Tests in synthetic/ relate to incomplete parts of the HTML5 specification,
+which allows synthetic events to be created. For compatibility with others,
+the dataTransfer parameter allows null, undefined and other objects. Objects
+will create a synthetic dataTransfer. To provide maximum functionality,
+synthetic dataTransfer will have its own synthetic data store, detached from
+the real data store used by real drag events (actual user interaction). For
+security, real dataTransfer objects will remember the real event's protection
+status inside synthetic events (the spec bases their protection only on the
+event type, and does not consider the difference between real and synthetic
+events).
+
+Tests in platform/plugin are based on assumed "good" behaviour, where the
+appropriate events are passed to the plugin. Platforms should determine if any
+deviations from the expected results are problematic.
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/001.html b/testing/web-platform/tests/html/editing/dnd/canvas/001.html
new file mode 100644
index 0000000000..2d3b4dd8ed
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/001.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Canvas drag and drop carrying image as dataURL</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list'));
+ document.querySelector('div').appendChild(c);}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('canvas').toDataURL('image/png'));}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+async function test() {
+ const canvas = document.querySelector('canvas');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ addImage(event);
+ const img = document.querySelector('img');
+ assert_equals(img.src, canvas.toDataURL('image/png'));
+ return true;
+ }
+
+ dragDropTest(canvas, div, onDropCallBack, 'Dragging the canvas to the bottom div should turn it green');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/002.html b/testing/web-platform/tests/html/editing/dnd/canvas/002.html
new file mode 100644
index 0000000000..ddfa7c4ade
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/002.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Drag and drop: dropping block element onto canvas</title>
+<style type="text/css">
+div
+ {width:20px;
+ height:20px;
+ background-color:green;}
+</style>
+<script type="application/ecmascript">
+function paint(color)
+ {var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.fillStyle = 'green';
+ c.beginPath();
+ c.moveTo(0,0);
+ c.lineTo(100,0);
+ c.lineTo(100,100);
+ c.lineTo(0,100);
+ c.closePath();
+ c.fill();}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body onload="paint('gray')">
+<div draggable="true" ondragstart="start(event)"></div>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p>
+ <canvas width="100" height="100" ondragenter="event.preventDefault()" ondragover="return false">Canvas</canvas>
+</p>
+<script>
+async function test(){
+ const canvas = document.querySelector('canvas');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ paint(event.dataTransfer.getData('text/plain'));
+ let style = getComputedStyle(div);
+ let currentColor = "rgb(0, 128, 0)";
+ assert_equals(style.getPropertyValue("background-color"), currentColor);
+ return true;
+ }
+
+ dragDropTest(div, canvas, onDropCallBack, 'Dragging the canvas to the bottom div should turn it green');
+};
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/003-1.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/003-1.xhtml
new file mode 100644
index 0000000000..22adda8720
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/003-1.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:20px;
+ height:20px;
+ background-color:green;}
+body
+ /* Center the div in this iframe since we know the iframe size (it's fixed
+ * in the parent page to 'width:150px; height:150px;') */
+ {margin-top:65px;
+ margin-left:65px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/003.html b/testing/web-platform/tests/html/editing/dnd/canvas/003.html
new file mode 100644
index 0000000000..b479341db1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/003.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Drag and drop from iframe: dropping block element onto canvas</title>
+<style type="text/css">
+iframe
+ {width:150px;
+ height:150px;
+ border-style:none;}
+</style>
+<script>
+function paint(color)
+ {var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.fillStyle = color;
+ c.beginPath();
+ c.moveTo(0,0);
+ c.lineTo(100,0);
+ c.lineTo(100,100);
+ c.lineTo(0,100);
+ c.closePath();
+ c.fill();}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body onload="paint('gray')">
+<p><iframe src="003-1.xhtml">Green box</iframe></p>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p>
+ <canvas width="100" height="100" ondragenter="event.preventDefault()" ondragover="return false">Canvas</canvas>
+</p>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const iframe = document.querySelector('iframe');
+ const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
+ const div = innerDoc.querySelector('div');
+ const canvas = document.querySelector('canvas');
+ function onDropCallBack(event) {
+ paint(event.dataTransfer.getData('text/plain'));
+ let style = window.getComputedStyle(canvas);
+ let currentColor = "rgba(0, 0, 0, 0)";
+ assert_equals(style.getPropertyValue("background-color"), currentColor);
+ return true;
+ }
+
+ dragDropTest(iframe, canvas, onDropCallBack, 'Dragging the canvas to the bottom iframe should turn it green');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/004.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/004.xhtml
new file mode 100644
index 0000000000..5b03ed22f5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/004.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from object: dropping block element onto canvas</title>
+<style type="text/css">
+object
+ {width:150px;
+ height:150px;
+ border-style:none;}
+</style>
+<script type="application/ecmascript">
+function paint(color)
+ {var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.fillStyle = color;
+ c.beginPath();
+ c.moveTo(0,0);
+ c.lineTo(100,0);
+ c.lineTo(100,100);
+ c.lineTo(0,100);
+ c.closePath();
+ c.fill();}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body onload="paint('gray')">
+<p><object type="application/xhtml+xml" data="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3ECanvas%20drag%20and%20drop%3C/title%3E%3Cstyle%20type%3D%22text/css%22%3Ediv%7Bwidth%3A20px%3Bheight%3A20px%3Bbackground-color%3Agreen%3B%7D%3C/style%3E%3Cscript%20type%3D%22application/ecmascript%22%3Efunction%20start%28event%29%7Bevent.dataTransfer.effectAllowed%20%3D%20%27copy%27%3Bevent.dataTransfer.setData%28%27text/plain%27%2C%20%27green%27%29%3B%7D%3C/script%3E%3C/head%3E%3Cbody%3E%3Cdiv%20draggable%3D%22true%22%20ondragstart%3D%22start%28event%29%22/%3E%3C/body%3E%3C/html%3E">Green box</object></p>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p>
+ <canvas width="100" height="100" ondragenter="event.preventDefault()" ondragover="return false" ondrop="paint(event.dataTransfer.getData('text/plain'))">Canvas</canvas>
+</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/005.html b/testing/web-platform/tests/html/editing/dnd/canvas/005.html
new file mode 100644
index 0000000000..047011d071
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/005.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Drag and drop to iframe: dropping block element onto canvas</title>
+<style type="text/css">
+div
+ {width:20px;
+ height:20px;
+ background-color:green;}
+iframe
+ {width:200px;
+ height:200px;
+ border-style:none;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"></div>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p><iframe src="helper-drop-here-canvas.xhtml">Canvas</iframe></p>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const iframe = document.querySelector('iframe');
+ const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
+ const div = document.querySelector('div');
+ const canvas = innerDoc.querySelector('canvas');
+ function onDropCallBack(event) {
+ let style = window.getComputedStyle(canvas);
+ let currentColor = "rgba(0, 0, 0, 0)";
+ assert_equals(style.getPropertyValue("background-color"), currentColor);
+ return true;
+ }
+
+ dragDropTest(div, canvas, onDropCallBack, 'Dragging the div to the bottom iframe should turn it green', iframe);
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/006.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/006.xhtml
new file mode 100644
index 0000000000..39274f4213
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/006.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop to object: dropping block element onto canvas</title>
+<style type="text/css">
+div
+ {width:20px;
+ height:20px;
+ background-color:green;}
+object
+ {width:200px;
+ height:200px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p><object type="application/xhtml+xml" data="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3ECanvas%20drag%20and%20drop%3C/title%3E%3Cscript%20type%3D%22application/ecmascript%22%3Efunction%20paint%28color%29%7Bvar%20canvas%20%3D%20document.querySelector%28%27canvas%27%29%2Cc%20%3D%20canvas.getContext%28%272d%27%29%3Bc.fillStyle%20%3D%20color%3Bc.beginPath%28%29%3Bc.moveTo%280%2C0%29%3Bc.lineTo%28100%2C0%29%3Bc.lineTo%28100%2C100%29%3Bc.lineTo%280%2C100%29%3Bc.closePath%28%29%3Bc.fill%28%29%3B%7Dfunction%20start%28event%29%7Bevent.dataTransfer.effectAllowed%20%3D%20%27copy%27%3Bevent.dataTransfer.setData%28%27text/plain%27%2C%20document.querySelector%28%27input%27%29.value%29%3B%7D%3C/script%3E%3C/head%3E%3Cbody%20onload%3D%22paint%28%27gray%27%29%22%3E%3Cp%3E%3Ccanvas%20width%3D%22100%22%20height%3D%22100%22%20ondragenter%3D%22event.preventDefault%28%29%22%20ondragover%3D%22return%20false%22%20ondrop%3D%22paint%28event.dataTransfer.getData%28%27text/plain%27%29%29%22%3ECanvas%3C/canvas%3E%3C/p%3E%3C/body%3E%3C/html%3E">Canvas</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/007.html b/testing/web-platform/tests/html/editing/dnd/canvas/007.html
new file mode 100644
index 0000000000..d0ff4f64ba
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/007.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Drag and drop between iframes: dropping block element onto canvas</title>
+<style type="text/css">
+iframe
+ {width:300px;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-green-box.xhtml">Green box</iframe></p>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p><iframe src="helper-drop-here-canvas.xhtml" id="iframe-2">Canvas</iframe></p>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const iframe = document.querySelector('iframe');
+ const iframeTwo = document.getElementById('iframe-2');
+ const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
+ const div = innerDoc.querySelector('div');
+ const innerDocTwo = iframeTwo.contentDocument || iframe.contentWindow.document;
+ const canvas = innerDocTwo.querySelector('canvas');
+ function onDropCallBack(event) {
+ let style = window.getComputedStyle(canvas);
+ let currentColor = "rgba(0, 0, 0, 0)";
+ assert_equals(style.getPropertyValue("background-color"), currentColor);
+ return true;
+ }
+
+ dragDropTest(iframe, canvas, onDropCallBack, 'Dragging the iframe to the bottom iframe should turn it green', iframeTwo);
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/008.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/008.xhtml
new file mode 100644
index 0000000000..aa85769165
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/008.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from iframe to object: dropping block element onto canvas</title>
+<style type="text/css">
+iframe, object
+ {width:300px;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-green-box.xhtml">Green box</iframe></p>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p><object type="application/xhtml+xml" data="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3ECanvas%20drag%20and%20drop%3C/title%3E%3Cscript%20type%3D%22application/ecmascript%22%3Efunction%20paint%28color%29%7Bvar%20canvas%20%3D%20document.querySelector%28%27canvas%27%29%2Cc%20%3D%20canvas.getContext%28%272d%27%29%3Bc.fillStyle%20%3D%20color%3Bc.beginPath%28%29%3Bc.moveTo%280%2C0%29%3Bc.lineTo%28100%2C0%29%3Bc.lineTo%28100%2C100%29%3Bc.lineTo%280%2C100%29%3Bc.closePath%28%29%3Bc.fill%28%29%3B%7Dfunction%20start%28event%29%7Bevent.dataTransfer.effectAllowed%20%3D%20%27copy%27%3Bevent.dataTransfer.setData%28%27text/plain%27%2C%20document.querySelector%28%27input%27%29.value%29%3B%7D%3C/script%3E%3C/head%3E%3Cbody%20onload%3D%22paint%28%27gray%27%29%22%3E%3Cp%3E%3Ccanvas%20width%3D%22100%22%20height%3D%22100%22%20ondragenter%3D%22event.preventDefault%28%29%22%20ondragover%3D%22return%20false%22%20ondrop%3D%22paint%28event.dataTransfer.getData%28%27text/plain%27%29%29%22%3ECanvas%3C/canvas%3E%3C/p%3E%3C/body%3E%3C/html%3E">Canvas</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/009.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/009.xhtml
new file mode 100644
index 0000000000..94e95c6a98
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/009.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop between objects: dropping block element onto canvas</title>
+<style type="text/css">
+object
+ {width:300px;
+ height:200px;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3ECanvas%20drag%20and%20drop%3C/title%3E%3Cstyle%20type%3D%22text/css%22%3Ediv%7Bwidth%3A20px%3Bheight%3A20px%3Bbackground-color%3Agreen%3B%7D%3C/style%3E%3Cscript%20type%3D%22application/ecmascript%22%3Efunction%20start%28event%29%7Bevent.dataTransfer.effectAllowed%20%3D%20%27copy%27%3Bevent.dataTransfer.setData%28%27text/plain%27%2C%20%27green%27%29%3B%7D%3C/script%3E%3C/head%3E%3Cbody%3E%3Cdiv%20draggable%3D%22true%22%20ondragstart%3D%22start%28event%29%22/%3E%3C/body%3E%3C/html%3E">Green box</object></p>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+<p><object type="application/xhtml+xml" data="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3ECanvas%20drag%20and%20drop%3C/title%3E%3Cscript%20type%3D%22application/ecmascript%22%3Efunction%20paint%28color%29%7Bvar%20canvas%20%3D%20document.querySelector%28%27canvas%27%29%2Cc%20%3D%20canvas.getContext%28%272d%27%29%3Bc.fillStyle%20%3D%20color%3Bc.beginPath%28%29%3Bc.moveTo%280%2C0%29%3Bc.lineTo%28100%2C0%29%3Bc.lineTo%28100%2C100%29%3Bc.lineTo%280%2C100%29%3Bc.closePath%28%29%3Bc.fill%28%29%3B%7Dfunction%20start%28event%29%7Bevent.dataTransfer.effectAllowed%20%3D%20%27copy%27%3Bevent.dataTransfer.setData%28%27text/plain%27%2C%20document.querySelector%28%27input%27%29.value%29%3B%7D%3C/script%3E%3C/head%3E%3Cbody%20onload%3D%22paint%28%27gray%27%29%22%3E%3Cp%3E%3Ccanvas%20width%3D%22100%22%20height%3D%22100%22%20ondragenter%3D%22event.preventDefault%28%29%22%20ondragover%3D%22return%20false%22%20ondrop%3D%22paint%28event.dataTransfer.getData%28%27text/plain%27%29%29%22%3ECanvas%3C/canvas%3E%3C/p%3E%3C/body%3E%3C/html%3E">Canvas</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/010-1.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/010-1.xhtml
new file mode 100644
index 0000000000..19f43581c7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/010-1.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:20px;
+ height:20px;
+ background-color:green;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Drag green box above to the gray canvas below. Canvas should turn green when you drop green box on it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/010.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/010.xhtml
new file mode 100644
index 0000000000..0b24a3082f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/010.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop between frames: dropping block element onto canvas</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="010-1.xhtml"/>
+<frame src="helper-drop-here-canvas.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/011.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/011.xhtml
new file mode 100644
index 0000000000..40da4e9677
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/011.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop between dataURL frames: dropping block element onto canvas</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3ECanvas%20drag%20and%20drop%3C/title%3E%3Cstyle%20type%3D%22text/css%22%3Ediv%7Bwidth%3A20px%3Bheight%3A20px%3Bbackground-color%3Agreen%3B%7D%3C/style%3E%3Cscript%20type%3D%22application/ecmascript%22%3Efunction%20start%28event%29%7Bevent.dataTransfer.effectAllowed%20%3D%20%27copy%27%3Bevent.dataTransfer.setData%28%27text/plain%27%2C%20%27green%27%29%3B%7D%3C/script%3E%3C/head%3E%3Cbody%3E%3Cdiv%20draggable%3D%22true%22%20ondragstart%3D%22start%28event%29%22/%3E%3Cp%3EDrag%20green%20box%20above%20to%20the%20gray%20canvas%20below.%20Canvas%20should%20turn%20green%20when%20you%20drop%20green%20box%20on%20it.%3C/p%3E%3C/body%3E%3C/html%3E"/>
+<frame src="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3ECanvas%20drag%20and%20drop%3C/title%3E%3Cscript%20type%3D%22application/ecmascript%22%3Efunction%20paint%28color%29%7Bvar%20canvas%20%3D%20document.querySelector%28%27canvas%27%29%2Cc%20%3D%20canvas.getContext%28%272d%27%29%3Bc.fillStyle%20%3D%20color%3Bc.beginPath%28%29%3Bc.moveTo%280%2C0%29%3Bc.lineTo%28100%2C0%29%3Bc.lineTo%28100%2C100%29%3Bc.lineTo%280%2C100%29%3Bc.closePath%28%29%3Bc.fill%28%29%3B%7Dfunction%20start%28event%29%7Bevent.dataTransfer.effectAllowed%20%3D%20%27copy%27%3Bevent.dataTransfer.setData%28%27text/plain%27%2C%20document.querySelector%28%27input%27%29.value%29%3B%7D%3C/script%3E%3C/head%3E%3Cbody%20onload%3D%22paint%28%27gray%27%29%22%3E%3Cp%3E%3Ccanvas%20width%3D%22100%22%20height%3D%22100%22%20ondragenter%3D%22event.preventDefault%28%29%22%20ondragover%3D%22return%20false%22%20ondrop%3D%22paint%28event.dataTransfer.getData%28%27text/plain%27%29%29%22%3ECanvas%3C/canvas%3E%3C/p%3E%3C/body%3E%3C/html%3E"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/012.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/012.xhtml
new file mode 100644
index 0000000000..b4f30c652b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/012.xhtml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop and vertical scrolling: dropping block element onto canvas inside scrollable container</title>
+<style type="text/css">
+div[draggable]
+ {width:20px;
+ height:20px;
+ background-color:green;}
+p + div
+ {height:100px;
+ width:150px;
+ overflow-y:scroll;}
+canvas
+ {display:block;
+ margin-top:100px;}
+</style>
+<script type="application/ecmascript">
+function paint(color)
+ {var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.fillStyle = color;
+ c.beginPath();
+ c.moveTo(0,0);
+ c.lineTo(100,0);
+ c.lineTo(100,100);
+ c.lineTo(0,100);
+ c.closePath();
+ c.fill();}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body onload="paint('gray')">
+<div draggable="true" ondragstart="start(event)"/>
+<p>You should be able to drag green box above to the gray canvas at the bottom of scrollable container (dragging towards the bottom edge triggers scrolling). Canvas should be repainted to match dropped color.</p>
+<div>
+ <canvas width="100" height="100" ondragenter="event.preventDefault()" ondragover="return false" ondrop="paint(event.dataTransfer.getData('text/plain'))">Canvas</canvas>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/013.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/013.xhtml
new file mode 100644
index 0000000000..e2e3646c86
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/013.xhtml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop and horizontal scrolling: dropping block element onto canvas inside scrollable container</title>
+<style type="text/css">
+div[draggable]
+ {width:20px;
+ height:20px;
+ background-color:green;}
+p + div
+ {height:150px;
+ width:100px;
+ overflow-x:scroll;}
+canvas
+ {display:block;
+ margin-left:100px;}
+</style>
+<script type="application/ecmascript">
+function paint(color)
+ {var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.fillStyle = color;
+ c.beginPath();
+ c.moveTo(0,0);
+ c.lineTo(100,0);
+ c.lineTo(100,100);
+ c.lineTo(0,100);
+ c.closePath();
+ c.fill();}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body onload="paint('gray')">
+<div draggable="true" ondragstart="start(event)"/>
+<p>You should be able to drag green box above to the gray canvas at the right edge of scrollable container (dragging towards the right edge triggers scrolling). Canvas should be repainted to match dropped color.</p>
+<div>
+ <canvas width="100" height="100" ondragenter="event.preventDefault()" ondragover="return false" ondrop="paint(event.dataTransfer.getData('text/plain'))">Canvas</canvas>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/014.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/014.xhtml
new file mode 100644
index 0000000000..425c97b6c4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/014.xhtml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop and scrolling: dropping block element onto canvas inside scrollable container</title>
+<style type="text/css">
+div[draggable]
+ {width:20px;
+ height:20px;
+ background-color:green;}
+p + div
+ {height:100px;
+ width:100px;
+ overflow:scroll;}
+canvas
+ {display:block;
+ margin:100px 0 0 100px;}
+</style>
+<script type="application/ecmascript">
+function paint(color)
+ {var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.fillStyle = color;
+ c.beginPath();
+ c.moveTo(0,0);
+ c.lineTo(100,0);
+ c.lineTo(100,100);
+ c.lineTo(0,100);
+ c.closePath();
+ c.fill();}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body onload="paint('gray')">
+<div draggable="true" ondragstart="start(event)"/>
+<p>You should be able to drag green box above to the gray canvas in the right-bottom corner of the scrollable container (dragging towards the corner triggers scrolling). Canvas should be repainted to match dropped color.</p>
+<div>
+ <canvas width="100" height="100" ondragenter="event.preventDefault()" ondragover="return false" ondrop="paint(event.dataTransfer.getData('text/plain'))">Canvas</canvas>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/cross-domain/001-manual.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/cross-domain/001-manual.xhtml
new file mode 100644
index 0000000000..08512add47
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/cross-domain/001-manual.xhtml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross-domain canvas data must not populate the dataTransfer</title>
+<script src="../../resources/crossorigin.sub.js"></script>
+<style type="text/css">
+div {
+ width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;
+}
+p:first-child {
+ padding-left:12px;
+}
+#image { visibility: hidden; }
+</style>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true">Canvas</canvas>
+</p>
+<p>Drag the navy square above to the box below.</p>
+<div></div>
+<p><img id="image" alt="" width="100" height="100" /></p>
+
+<script><![CDATA[
+document.getElementsByTagName("img")[0].src = crossOriginUrl("www", "../../resources/100x100-navy.png");
+
+window.onload = function() {
+ var canvas = document.getElementsByTagName('canvas')[0], div = document.getElementsByTagName('div')[0], failed = [];
+ var context = canvas.getContext('2d');
+ var image = document.getElementById('image');
+ context.drawImage(image, 0, 0);
+ div.ondragover = div.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ div.ondrop = canvas.ondragstart = function(e) {
+ if( e.type == 'dragstart' ) {
+ e.dataTransfer.setData('Text', 'dummy text');
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ for( var i = 0; i < e.dataTransfer.types.length; i++ ) {
+ if( e.dataTransfer.types[i].match(/image\//) ) {
+ failed[failed.length] = e.dataTransfer.types[i];
+ }
+ }
+ if( e.type == 'drop' ) {
+ e.preventDefault();
+ document.getElementsByTagName('p')[1].innerHTML = failed.length ? ( 'FAIL (found ' + failed.join() + ')' ) : 'PASS';
+ }
+ };
+};
+]]></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/helper-drag-me-green-box.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/helper-drag-me-green-box.xhtml
new file mode 100644
index 0000000000..91a9fa9808
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/helper-drag-me-green-box.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:20px;
+ height:20px;
+ background-color:green;}
+body
+ /* Center the div in this iframe since we know the iframe size (it's fixed
+ * in the parent page to 'width:300px; height:200px;') */
+ {margin-top:90px;
+ margin-left:140px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/></body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/canvas/helper-drop-here-canvas.xhtml b/testing/web-platform/tests/html/editing/dnd/canvas/helper-drop-here-canvas.xhtml
new file mode 100644
index 0000000000..db305689f5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/canvas/helper-drop-here-canvas.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas drag and drop: helper file</title>
+<script type="application/ecmascript">
+function paint(color)
+ {var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.fillStyle = color;
+ c.beginPath();
+ c.moveTo(0,0);
+ c.lineTo(100,0);
+ c.lineTo(100,100);
+ c.lineTo(0,100);
+ c.closePath();
+ c.fill();}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain', 'green');}
+</script>
+</head>
+<body onload="paint('gray')">
+<p>
+ <canvas width="100" height="100" ondragenter="event.preventDefault()" ondragover="return false" ondrop="paint(event.dataTransfer.getData('text/plain'))">Canvas</canvas>
+</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/crashers/dialog-001.html b/testing/web-platform/tests/html/editing/dnd/crashers/dialog-001.html
new file mode 100644
index 0000000000..9ab62787b0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/crashers/dialog-001.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>drag &amp; drop – crash when drag is interrupted by dialogs</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var doneonce = false;
+ document.getElementsByTagName('div')[0].ondragstart = function(e) {
+ alert( doneonce ? 'Dismiss this dialog. PASS if the browser does not crash.' : 'Dismiss this dialog. The browser should not crash. Without re-focusing the page first, try dragging the orange square a second time. If a second alert does not appear, release the drag, and then try dragging the orange square a third time.' );
+ doneonce = true;
+ };
+};
+</script>
+
+<div draggable='true' itemscope></div><div></div>
+
+<p>Try to drag the orange square onto the blue square.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/cross-document/001-1.html b/testing/web-platform/tests/html/editing/dnd/cross-document/001-1.html
new file mode 100644
index 0000000000..1b3540b035
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/cross-document/001-1.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cross-document data drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var blue = document.getElementsByTagName('div')[0], fails = [];
+ blue.ondragover = blue.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( e.dataTransfer.getData('text') ) {
+ fails[fails.length] = '"' + e.dataTransfer.getData('text') + '" exposed during event ' + e.type;
+ }
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ if( !e.dataTransfer.types.length ) {
+ fails[fails.length] = 'no types found during event drop';
+ }
+ var foundtext = false;
+ for( var i = 0; i < e.dataTransfer.types.length; i++ ) {
+ if( e.dataTransfer.types[i] == 'text/plain' ) {
+ foundtext = true;
+ break;
+ }
+ }
+ if( !foundtext ) {
+ fails[fails.length] = 'text/plain type not found during event drop';
+ }
+ if( e.dataTransfer.getData('text') != 'dummy text' ) {
+ fails[fails.length] = 'getData returned ' + e.dataTransfer.getData('text') + ' instead of "dummy text"';
+ }
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL:<br>' + fails.join('<br>') ) : 'PASS';
+ };
+};
+</script>
+
+<p>Drag the orange square onto the blue square. Fail if this text does not change.</p>
+<div></div>
+
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/cross-document/001.html b/testing/web-platform/tests/html/editing/dnd/cross-document/001.html
new file mode 100644
index 0000000000..dd9906e8c1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/cross-document/001.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - simple cross-document data drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text', 'dummy text');
+ };
+};
+</script>
+
+<div draggable="true"></div>
+<p><iframe src="001-1.html" height="300" width="500"></iframe></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/cross-document/002-manual.html b/testing/web-platform/tests/html/editing/dnd/cross-document/002-manual.html
new file mode 100644
index 0000000000..0a549d3804
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/cross-document/002-manual.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cross-domain cross-document data drop</title>
+<script src="../resources/crossorigin.sub.js"></script>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text', 'dummy text');
+ };
+};
+</script>
+
+<div draggable="true"></div>
+<p><iframe height="300" width="500"></iframe></p>
+<script>document.getElementsByTagName("iframe")[0].src = crossOriginUrl("www", "001-1.html");</script>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/cross-document/003-1.html b/testing/web-platform/tests/html/editing/dnd/cross-document/003-1.html
new file mode 100644
index 0000000000..2cefd83209
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/cross-document/003-1.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cross-document data drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var blue = document.getElementsByTagName('div')[0], fails = [];
+ blue.ondragover = blue.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( !parent.evs[e.type] ) { parent.evs[e.type] = {}; }
+ parent.evs[e.type].dataTransfer = e.dataTransfer;
+ parent.evs[e.type].items = e.dataTransfer.items;
+ parent.evs[e.type].types = e.dataTransfer.types;
+ parent.evs[e.type].files = e.dataTransfer.files;
+ if( parent.evs[e.type].dataTransfer != e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning anything during '+e.type;
+ } else if( parent.evs[e.type].items !== e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning anything during '+e.type;
+ } else if( parent.evs[e.type].types !== e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning anything during '+e.type;
+ } else if( parent.evs[e.type].files !== e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning the same object during '+e.type;
+ }
+ //http://dev.w3.org/html5/spec/dnd.html#datatransfer
+ //"The * attribute must return a * object associated with the DataTransfer object."
+ //Note that it is associated with the DataTransfer object, *not* the data store
+ //http://dev.w3.org/html5/spec/dnd.html#dragevent
+ //"when a user agent is required to fire a DND event named e at an element, using a particular drag data store...
+ //Let dataTransfer be a newly created DataTransfer object associated with the given drag data store."
+ //A new DataTransfer object therefore means a new set of properties, not the same ones as last event
+ if( parent.evs.dragstart.dataTransfer === e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.items && parent.evs.dragstart.items === e.dataTransfer.items ) {
+ fails[fails.length] = '.items is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.types && parent.evs.dragstart.types === e.dataTransfer.types ) {
+ fails[fails.length] = '.types is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.files && parent.evs.dragstart.files === e.dataTransfer.files ) {
+ fails[fails.length] = '.files is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ };
+ blue.ondrop = function(e) {
+ parent.evs[e.type] = {};
+ parent.evs[e.type].dataTransfer = e.dataTransfer;
+ parent.evs[e.type].items = e.dataTransfer.items;
+ parent.evs[e.type].types = e.dataTransfer.types;
+ parent.evs[e.type].files = e.dataTransfer.files;
+ if( parent.evs[e.type].dataTransfer !== e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning anything during '+e.type;
+ } else if( parent.evs[e.type].items !== e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning anything during '+e.type;
+ } else if( parent.evs[e.type].types !== e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning anything during '+e.type;
+ } else if( parent.evs[e.type].files !== e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning the same object during '+e.type;
+ }
+ if( parent.evs.dragstart.dataTransfer === e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.items && parent.evs.dragstart.items === e.dataTransfer.items ) {
+ fails[fails.length] = '.items is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.types && parent.evs.dragstart.types === e.dataTransfer.types ) {
+ fails[fails.length] = '.types is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.files && parent.evs.dragstart.files === e.dataTransfer.files ) {
+ fails[fails.length] = '.files is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL:<br>' + fails.join('<br>') ) : 'PASS';
+ };
+};
+</script>
+
+<p>Drag the orange square onto the blue square. Fail if this text does not change.</p>
+<div></div>
+
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/cross-document/003.html b/testing/web-platform/tests/html/editing/dnd/cross-document/003.html
new file mode 100644
index 0000000000..ba44f9b770
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/cross-document/003.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cross-document variable retention within event handlers</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+var evs = {};
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text', 'dummy text');
+ evs[e.type] = {};
+ evs[e.type].dataTransfer = e.dataTransfer;
+ evs[e.type].items = e.dataTransfer.items;
+ evs[e.type].types = e.dataTransfer.types;
+ evs[e.type].files = e.dataTransfer.files;
+ };
+};
+</script>
+
+<div draggable="true"></div>
+<p><iframe src="003-1.html" height="300" width="500"></iframe></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/001.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/001.xhtml
new file mode 100644
index 0000000000..d46170d61b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/001.xhtml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.setData/getData during canvas drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondrag="dragElement(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+data[1] = canvas.toDataURL('image/png');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/002.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/002.xhtml
new file mode 100644
index 0000000000..b9fb47d765
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/002.xhtml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.setData/getData during PNG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/003.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/003.xhtml
new file mode 100644
index 0000000000..e7baaa37ea
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/003.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.setData/getData during SVG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/004.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/004.xhtml
new file mode 100644
index 0000000000..c7f8dc8db6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/004.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.setData/getData during text input selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="start(event)" ondrag="dragElement(event)"/></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/005.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/005.xhtml
new file mode 100644
index 0000000000..78655c82b7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/005.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.setData/getData during selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)" ondrag="dragElement(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/006.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/006.xhtml
new file mode 100644
index 0000000000..c6c96842a9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/006.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.setData/getData during link drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,1', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/007.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/007.xhtml
new file mode 100644
index 0000000000..b6b3cdafe6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/007.xhtml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.setData/getData during block element drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('p + div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the gray box below and drop it. Gray box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/008.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/008.xhtml
new file mode 100644
index 0000000000..2d80f751f0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/008.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Influence of reload during drag and drop on datastore</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ window.location.reload();}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('p + div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the gray box below and drop it. Gray box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/009-1.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/009-1.xhtml
new file mode 100644
index 0000000000..fd2c117524
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/009-1.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Redirect during drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('body').setAttribute('style','background-color:teal;color:white;');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)">
+<p>Drop box here. Page should turn green and test results should appear below.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/009.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/009.xhtml
new file mode 100644
index 0000000000..9d6c321f63
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/009.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Influence of redirect during drag and drop on datastore</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:navy;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ window.location = '009-1.xhtml'}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Try to drag box above. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/010-1.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/010-1.xhtml
new file mode 100644
index 0000000000..086ae709ad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/010-1.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>History navigation during drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {margin:200px 0 0 200px;
+ width:200px;
+ height:100px;
+ color:white;
+ background-color:navy;}
+div[ondragenter]:before
+ {display:block;
+ content:"";
+ border-style:solid;
+ position:relative;
+ top:-50px;
+ left:-200px;
+ border-width:100px;
+ border-color:transparent navy transparent transparent;}
+</style>
+</head>
+<body>
+<p>Drag box to the blue arrow but don't drop it yet. You should be returned back to start page.</p>
+<div ondragenter="event.preventDefault()" ondragover="history.go(-1)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/010.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/010.xhtml
new file mode 100644
index 0000000000..5df966ba1b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/010.xhtml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Influence of history navigation during drag and drop on datastore</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ window.location = '010-1.xhtml'}
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('p + div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Drag green box above. You will be redirected to new page. When you return back drop it on the gray box below. Gray box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/011.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/011.xhtml
new file mode 100644
index 0000000000..96e38993f0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/011.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop datastore: dragging element to iframe</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:navy;}
+iframe
+ {width:500px;
+ height:500px;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ }
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Drag box above to the frame below.</p>
+<iframe src="helper-drop-box-here.xhtml">XHTML document</iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/012.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/012.xhtml
new file mode 100644
index 0000000000..42986c0b58
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/012.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop datastore: dragging element to object</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:navy;}
+object
+ {width:500px;
+ height:500px;
+ border:solid medium navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ }
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Drag box above to the frame below.</p>
+<object type="application/xhtml+xml" data="helper-drop-box-here.xhtml">XHTML document</object>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/013-1.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/013-1.xhtml
new file mode 100644
index 0000000000..538a964552
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/013-1.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop datastore: helper file</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ }
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Drag box above to the frame below.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/013.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/013.xhtml
new file mode 100644
index 0000000000..312a66d1f2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/013.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop datastore: dragging element between frames</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="013-1.xhtml"/>
+<frame src="helper-drop-box-here.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/014-1.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/014-1.xhtml
new file mode 100644
index 0000000000..1163bde5af
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/014-1.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop datastore: helper file</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,1', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link from one frame to the silver box in another frame and drop it. Silver box should turn green and test results should appear.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/014.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/014.xhtml
new file mode 100644
index 0000000000..e1892c5a34
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/014.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop datastore: dragging element between two instances of document</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="014-1.xhtml"/>
+<frame src="014-1.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/015-manual.html b/testing/web-platform/tests/html/editing/dnd/datastore/015-manual.html
new file mode 100644
index 0000000000..604c1d4370
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/015-manual.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Using dataTransfer in new thread</title>
+ <style type="text/css">
+blockquote { height: 100px; width: 100px; background: orange; margin: 0; padding: 0; float: left; }
+blockquote + blockquote { background: blue; }
+blockquote + blockquote + blockquote { background: fuchsia; }
+blockquote + div { clear: left; }
+ </style>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+setup(function () {},{explicit_done:true,explicit_timeout:true});
+window.onload = function () {
+
+ var orange = document.getElementsByTagName('blockquote')[0],
+ blue = document.getElementsByTagName('blockquote')[1],
+ fuchsia = document.getElementsByTagName('blockquote')[2],
+ evtdone = {};
+
+ orange.ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dragstart real data');
+ var dataTransfer = e.dataTransfer;
+ setTimeout(function () {
+ test(function () {
+ assert_equals( dataTransfer.getData('text'), '', 'step 1' );
+ dataTransfer.setData('text','new thread after dragstart');
+ assert_equals( dataTransfer.getData('text'), '', 'step 2' );
+ },'dragstart data store should be protected after new thread starts');
+ },0);
+ };
+
+ fuchsia.ondragenter = fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ };
+
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ var dataTransfer = e.dataTransfer;
+ setTimeout(function () {
+ test(function () {
+ assert_equals( dataTransfer.getData('text'), '', 'step 1' );
+ dataTransfer.setData('text','new thread after dragstart');
+ assert_equals( dataTransfer.getData('text'), '', 'step 2' );
+ },'drop data store should be protected after new thread starts');
+ done();
+ },0);
+ };
+
+};
+ </script>
+ </head>
+ <body>
+ <p>Drag the orange square over the blue square then the fuchsia square, then release it.</p>
+ <blockquote draggable="true"></blockquote>
+ <blockquote></blockquote>
+ <blockquote></blockquote>
+ <div id="log"></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/016.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/016.xhtml
new file mode 100644
index 0000000000..6370fe5240
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/016.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData during PNG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,FAIL', 'FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length > l)
+ {say('items.length (dragover) : FAIL' + event.dataTransfer.items.length)}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/017.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/017.xhtml
new file mode 100644
index 0000000000..ef0cf9fe11
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/017.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData during SVG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E', 'FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length > l)
+ {say('items.length (dragover) : FAIL' + event.dataTransfer.items.length)}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/018.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/018.xhtml
new file mode 100644
index 0000000000..f68d722a99
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/018.xhtml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData during text input selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length > l)
+ {say('items.length (dragover) : FAIL' + event.dataTransfer.items.length)}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="start(event)" ondrag="dragElement(event)"/></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/019.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/019.xhtml
new file mode 100644
index 0000000000..0734a23164
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/019.xhtml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData during selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length > l)
+ {say('items.length (dragover) : FAIL' + event.dataTransfer.items.length)}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)" ondrag="dragElement(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/020.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/020.xhtml
new file mode 100644
index 0000000000..b57ab102ad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/020.xhtml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData during link drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,FAIL', 'FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length > l)
+ {say('items.length (dragover) : FAIL' + event.dataTransfer.items.length)}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/021.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/021.xhtml
new file mode 100644
index 0000000000..428a5cf392
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/021.xhtml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData during block element drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length > l)
+ {say('items.length (dragover) : FAIL' + event.dataTransfer.items.length)}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('p + div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/022.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/022.xhtml
new file mode 100644
index 0000000000..660a4b4435
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/022.xhtml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData during canvas drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length > l)
+ {say('items.length (dragover) : FAIL' + event.dataTransfer.items.length)}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length < l)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondrag="dragElement(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+data[1] = canvas.toDataURL('image/png');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/023.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/023.xhtml
new file mode 100644
index 0000000000..fd236ca98a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/023.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.clearData and reload during block element drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.clearData(dataTypes[i]);}
+ window.location.reload();}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ }
+function dataDrop(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('p + div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/024.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/024.xhtml
new file mode 100644
index 0000000000..93a35f3160
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/024.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Clear datastore data during PNG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,FAIL', 'FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ event.dataTransfer.clearData();
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length != 0)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/025.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/025.xhtml
new file mode 100644
index 0000000000..6b803518b7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/025.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Clear datastore data during SVG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E', 'FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ event.dataTransfer.clearData();
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length != 0)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/026.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/026.xhtml
new file mode 100644
index 0000000000..50523e2d90
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/026.xhtml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Clear datastore data during text input selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ event.dataTransfer.clearData();
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length != 0)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="start(event)" ondrag="dragElement(event)"/></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/027.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/027.xhtml
new file mode 100644
index 0000000000..9d69ddddc9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/027.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Clear datastore data during selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'],
+l = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ event.dataTransfer.clearData();
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length != 0)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)" ondrag="dragElement(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/028.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/028.xhtml
new file mode 100644
index 0000000000..145864fce3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/028.xhtml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Clear datastore data during link drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,FAIL', 'FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ event.dataTransfer.clearData();
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length != 0)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/029.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/029.xhtml
new file mode 100644
index 0000000000..f752f3c900
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/029.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Clear datastore data during block element drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ event.dataTransfer.clearData();
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length != 0)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('p + div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/030.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/030.xhtml
new file mode 100644
index 0000000000..ad9bee4ce1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/030.xhtml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Clear datastore data during canvas drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['FAIL', 'data:text/plain,FAIL', '<result>FAIL</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">FAIL</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>FAIL</p></body></html>', 'FAIL'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ l = event.dataTransfer.items.length;
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ event.dataTransfer.clearData();
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length != 0)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length == 0)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == '')?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondrag="dragElement(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+data[1] = canvas.toDataURL('image/png');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/031.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/031.xhtml
new file mode 100644
index 0000000000..138142632c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/031.xhtml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items during canvas drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondrag="dragElement(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+data[1] = canvas.toDataURL('image/png');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/032.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/032.xhtml
new file mode 100644
index 0000000000..130ce38821
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/032.xhtml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items during PNG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/033.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/033.xhtml
new file mode 100644
index 0000000000..a91be79917
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/033.xhtml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items during SVG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/034.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/034.xhtml
new file mode 100644
index 0000000000..89a1a78dd3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/034.xhtml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items during text input selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="start(event)" ondrag="dragElement(event)"/></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/035.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/035.xhtml
new file mode 100644
index 0000000000..37b85bdb0b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/035.xhtml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items during selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)" ondrag="dragElement(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/036.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/036.xhtml
new file mode 100644
index 0000000000..4799bc222e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/036.xhtml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items during link drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,1', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/037.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/037.xhtml
new file mode 100644
index 0000000000..75ebf11ab1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/037.xhtml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items during block element drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('p + div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the gray box below and drop it. Gray box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/038.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/038.xhtml
new file mode 100644
index 0000000000..afb6902416
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/038.xhtml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items and getData during canvas drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.items.clear();
+ event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondrag="dragElement(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+data[1] = canvas.toDataURL('image/png');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/039.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/039.xhtml
new file mode 100644
index 0000000000..fd73adab2a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/039.xhtml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items and getData during PNG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/040.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/040.xhtml
new file mode 100644
index 0000000000..fe545fa9ad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/040.xhtml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items and getData during SVG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/041.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/041.xhtml
new file mode 100644
index 0000000000..433dcf090f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/041.xhtml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items and getData during text input selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="start(event)" ondrag="dragElement(event)"/></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/042.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/042.xhtml
new file mode 100644
index 0000000000..785889d981
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/042.xhtml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items and getData during selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)" ondrag="dragElement(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/043.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/043.xhtml
new file mode 100644
index 0000000000..76fe70a99b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/043.xhtml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items and getData during link drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,1', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/044.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/044.xhtml
new file mode 100644
index 0000000000..d3e08515df
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/044.xhtml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.items and getData during block element drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.items.clear();
+ event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('p + div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the gray box below and drop it. Gray box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/045.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/045.xhtml
new file mode 100644
index 0000000000..a98cba4ed8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/045.xhtml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text and url aliases</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text', 'url', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('p + div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the gray box below and drop it. Gray box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/046.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/046.xhtml
new file mode 100644
index 0000000000..a766f52ecf
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/046.xhtml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.types during canvas drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.items.clear();
+ event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragstart): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrag): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragenter): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragover): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrop): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondrag="dragElement(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+data[1] = canvas.toDataURL('image/png');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/047.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/047.xhtml
new file mode 100644
index 0000000000..c3148bff70
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/047.xhtml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.types during PNG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragstart): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrag): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragenter): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragover): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrop): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/048.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/048.xhtml
new file mode 100644
index 0000000000..c87793b6de
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/048.xhtml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.types during SVG image drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragstart): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrag): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragenter): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragover): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrop): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><img ondragstart="start(event)" ondrag="dragElement(event)" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/049.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/049.xhtml
new file mode 100644
index 0000000000..06e2107ae4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/049.xhtml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.types during text input selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragstart): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrag): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragenter): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragover): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrop): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="start(event)" ondrag="dragElement(event)"/></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/050.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/050.xhtml
new file mode 100644
index 0000000000..1cf766fed7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/050.xhtml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.types during selection drag and drop</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragstart): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrag): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragenter): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragover): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrop): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)" ondrag="dragElement(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/051.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/051.xhtml
new file mode 100644
index 0000000000..378af23f4b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/051.xhtml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.types during link drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,1', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {delete event.dataTransfer.items[i-1]}
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragstart): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrag): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragenter): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragover): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrop): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link above to the silver box below and drop it. Silver box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/052.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/052.xhtml
new file mode 100644
index 0000000000..597d31ac64
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/052.xhtml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>dataTransfer.types during block element drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:green;}
+p + div
+ {background-color:gray;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+e = 0, result = true;
+function start(event)
+ {event.dataTransfer.items.clear();
+ event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.items.add(data[i],dataTypes[i])}
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondragstart): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragstart): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ }
+function dragElement(event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrag): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function enterElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragenter): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ }
+function overElement(event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondragover): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ }
+function dataDrop(event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ if(event.dataTransfer.types.length != dataTypes.length)
+ {say('types.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.types[i] != dataTypes[i])
+ {say('Types (ondrop): FAIL (dataTransfer.types[' + i + '] returns wrong value)')}
+ if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData (ondrop): FAIL (getData(' + dataTypes[i] + ') returns wrong data)')}
+ }
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ document.querySelector('p + div').setAttribute('style','background-color:' + (result?'green':'red'));}
+function say(it)
+ {result = false;
+ document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)" ondrag="dragElement(event)"/>
+<p>Drag green box above to the gray box below and drop it. Gray box should turn green.</p>
+<div ondragenter="enterElement(event)" ondragover="overElement(event)" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/053.html b/testing/web-platform/tests/html/editing/dnd/datastore/053.html
new file mode 100644
index 0000000000..a893b8e489
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/053.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Adding a file to dnd data store</title>
+ <style type="text/css">
+span { display: inline-block; height: 100px; width: 100px; background: orange; }
+span + span { background: blue; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('span')[0];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'copy';
+ var filein = document.getElementsByTagName('input')[0];
+ if( !filein.files ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file API is not supported.';
+ return;
+ }
+ if( !filein.files[0] ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - no file was found in the file input.';
+ return;
+ }
+ var thefile = filein.files[0];
+ try {
+ e.dataTransfer.items.add(thefile);
+ } catch(err) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - error when adding file';
+ e.preventDefault();
+ return;
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file was not attached to data store';
+ e.preventDefault();
+ return;
+ }
+ };
+ var drop = document.getElementsByTagName('span')[1];
+ drop.ondragenter = drop.ondragover = function (e) {
+ e.preventDefault();
+ };
+ drop.ondrop = function (e) {
+ e.preventDefault();
+ if( document.getElementsByTagName('p')[0].innerHTML ) { return; }
+ if( e.dataTransfer.files.length != 1 ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file was not attached to data store during drop';
+ e.preventDefault();
+ return;
+ }
+ if( !window.FileReader ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No FileReader constructor';
+ e.preventDefault();
+ return;
+ }
+ var reader = new FileReader();
+ reader.onload = function () {
+ if( !reader.result ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No file data after load';
+ } else if( !document.getElementsByTagName('p')[0].innerHTML ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'PASS';
+ }
+ };
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ setTimeout(function () {
+ if( !reader.result ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No file data after timeout';
+ }
+ },1000);
+ };
+};
+ </script>
+ </head>
+ <body>
+ <ol>
+ <li>Select a non-empty file on your computer using the following input: <input type="file"></li>
+ <li>Drag the orange square onto the blue square and release it:<br><span draggable="true"></span> <span></span><br>
+ If a prompt appears, accept it.</li>
+ <li>Fail if new text does not appear below.</li>
+ </ol>
+ <p></p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/054.html b/testing/web-platform/tests/html/editing/dnd/datastore/054.html
new file mode 100644
index 0000000000..cd42e63398
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/054.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Adding a file to dnd data store with drag out of window</title>
+ <style type="text/css">
+span { display: inline-block; height: 100px; width: 100px; background: orange; }
+span + span { background: blue; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('span')[0];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'copy';
+ var filein = document.getElementsByTagName('input')[0];
+ if( !filein.files ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file API is not supported.';
+ return;
+ }
+ if( !filein.files[0] ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - no file was found in the file input.';
+ return;
+ }
+ var thefile = filein.files[0];
+ try {
+ e.dataTransfer.items.add(thefile);
+ } catch(err) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - error when adding file';
+ e.preventDefault();
+ return;
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file was not attached to data store';
+ e.preventDefault();
+ return;
+ }
+ };
+ var drop = document.getElementsByTagName('span')[1];
+ drop.ondragenter = drop.ondragover = function (e) {
+ e.preventDefault();
+ };
+ drop.ondrop = function (e) {
+ e.preventDefault();
+ if( document.getElementsByTagName('p')[0].innerHTML ) { return; }
+ if( e.dataTransfer.files.length != 1 ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file was not attached to data store during drop';
+ e.preventDefault();
+ return;
+ }
+ if( !window.FileReader ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No FileReader constructor';
+ e.preventDefault();
+ return;
+ }
+ var reader = new FileReader();
+ reader.onload = function () {
+ if( !reader.result ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No file data after load';
+ } else if( !document.getElementsByTagName('p')[0].innerHTML ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'PASS';
+ }
+ };
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ setTimeout(function () {
+ if( !reader.result ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No file data after timeout';
+ }
+ },1000);
+ };
+};
+ </script>
+ </head>
+ <body>
+ <ol>
+ <li>Select a non-empty file on your computer using the following input: <input type="file"></li>
+ <li>Drag the orange square outside the browser window (not over the taskbar), then back onto the blue square and release it:<br><span draggable="true"></span> <span></span><br>
+ If a prompt appears, accept it.</li>
+ <li>Fail if new text does not appear below.</li>
+ </ol>
+ <p></p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/055.html b/testing/web-platform/tests/html/editing/dnd/datastore/055.html
new file mode 100644
index 0000000000..ac2e64ed35
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/055.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<html>
+ <head>
+ <title>text/uri-list conversion</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], fails = [];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('url','http://example.com/');
+ if( !e.dataTransfer.getData('url') ) {
+ document.getElementsByTagName('p')[0].innerHTML = "FAIL - getData('url') returned nothing";
+ } else if( e.dataTransfer.getData('url') != e.dataTransfer.getData('text/uri-list') ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - text/uri-list is not the same as url';
+ } else {
+
+ e.dataTransfer.setData('url','#foo\r\n http://example.com/#bar \r\n http://example.org/');
+ if( e.dataTransfer.getData('url') != 'http://example.com/#bar' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - only the first URL should be returned - "'+e.dataTransfer.getData('url')+'"';
+ } else if( e.dataTransfer.getData('text/uri-list') != '#foo\r\n http://example.com/#bar \r\n http://example.org/' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - text/uri-list should return the full data';
+ } else {
+
+ e.dataTransfer.setData('url',' ');
+ if( e.dataTransfer.getData('url') ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - space was not a valid URL so an empty string should have been returned';
+ } else {
+ document.getElementsByTagName('p')[0].innerHTML = 'PASS';
+ }
+
+ }
+
+ }
+ e.preventDefault();
+ };
+};
+ </script>
+ </head>
+ <body>
+ <div draggable="true"></div>
+ <p>Attempt to drag the orange square.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/056.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/056.xhtml
new file mode 100644
index 0000000000..c9a3e700de
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/056.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Caseinsensitivity in dataTransfer.setData/getData</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,1', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData((i%2 == 0)?(dataTypes[i].toUpperCase()):(dataTypes[i].replace(/i/g,'I')), data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData((i%2 == 0)?(dataTypes[i].replace(/t/g,'T')):(dataTypes[i].toUpperCase())) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragElement(event)">Drag me</a></p>
+<p>Drag link above to the silver box below and drop it. Silver box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/057.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/057.xhtml
new file mode 100644
index 0000000000..b4f5659d27
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/057.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Various data item type strings in dataTransfer.setData/getData</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'http://', '<?xml version="1.0" encoding="utf-8"?>', '<html xmlns="http://www.w3.org/1999/xhtml"/>', '<mn>1</mn>', '☺', 'type="text/html"', '[({#;:,.`~*-_=+\|/%!?&$@^})]'],
+data = ['Drag me', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ say('items.length (dragstart) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ }
+function dragElement(event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ }
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(string' + i + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('div').setAttribute('style','background-color:green');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)" ondrag="dragElement(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. Navy box should turn green and test results should appear below.</p>
+<div ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)"/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/058.html b/testing/web-platform/tests/html/editing/dnd/datastore/058.html
new file mode 100644
index 0000000000..c1e7ad95b6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/058.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dropping file into dropzone</title>
+ <style type="text/css">
+span { display: inline-block; height: 100px; width: 100px; background: orange; }
+span + span { background: blue; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('span')[0];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'copy';
+ var filein = document.getElementsByTagName('input')[0];
+ if( !filein.files ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file API is not supported.';
+ return;
+ }
+ if( !filein.files[0] ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - no file was found in the file input.';
+ return;
+ }
+ var thefile = filein.files[0];
+ try {
+ e.dataTransfer.items.add(thefile);
+ } catch(err) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - error when adding file';
+ e.preventDefault();
+ return;
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file was not attached to data store';
+ e.preventDefault();
+ return;
+ }
+ };
+ document.getElementsByTagName('span')[1].ondrop = function (e) {
+ e.preventDefault();
+ if( document.getElementsByTagName('p')[0].innerHTML ) { return; }
+ if( e.dataTransfer.files.length != 1 ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file was not attached to data store during drop';
+ e.preventDefault();
+ return;
+ }
+ if( !window.FileReader ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No FileReader constructor';
+ e.preventDefault();
+ return;
+ }
+ var reader = new FileReader();
+ reader.onload = function () {
+ if( !reader.result ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No file data after load';
+ } else if( !document.getElementsByTagName('p')[0].innerHTML ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'PASS';
+ }
+ };
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ setTimeout(function () {
+ if( !reader.result ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'No file data after timeout';
+ }
+ },1000);
+ };
+};
+ </script>
+ </head>
+ <body>
+ <ol>
+ <li>Select a non-empty plain text file on your computer using the following input: <input type="file"></li>
+ <li>Drag the orange square onto the blue square and release it:<br><span draggable="true"></span> <span dropzone="copy file:text/plain"></span><br>
+ If a prompt appears, accept it.</li>
+ <li>Fail if new text does not appear below.</li>
+ </ol>
+ <p></p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-constructor-001.html b/testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-constructor-001.html
new file mode 100644
index 0000000000..26f7421b65
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-constructor-001.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>DataTransfer constructor test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ var dt = new DataTransfer();
+ assert_equals(dt.dropEffect, "none");
+ assert_equals(dt.effectAllowed, "none");
+ assert_equals(dt.items.length, 0);
+ assert_equals(dt.types.length, 0);
+}, "Verify DataTransfer constructor")
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-types.html b/testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-types.html
new file mode 100644
index 0000000000..cd9568987e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/datatransfer-types.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>DataTransfer types attribute test</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#the-datatransfer-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ const dt = new DataTransfer();
+ assert_true(Object.isFrozen(dt.types), "types must be a FrozenArray<>");
+ assert_true(Array.isArray(dt.types), "A FrozenArray<> must be an Array");
+ assert_equals(dt.types.length, 0, "types must be originally empty");
+ assert_equals(dt.types, dt.types,
+ "types must return the same object when the data store item list has not changed");
+
+ const dt2 = new DataTransfer();
+ assert_not_equals(dt2.types, dt.types,
+ "Different DataTransfer objects must return different FrozenArrays");
+}, "type's state on DataTransfer creation");
+
+test(() => {
+ const dt = new DataTransfer();
+ dt.setData("text/plain", "foo");
+
+ let old_types = dt.types;
+ assert_equals(old_types, dt.types);
+
+ // Clearing the data store via DataTransferItemList changes
+ // DataTransfer's data store, so types will return a new FrozenArray.
+ dt.items.clear();
+ assert_not_equals(old_types, dt.types);
+
+ // Clearing an empty list does not change it.
+ old_types = dt.types;
+ dt.items.clear();
+ assert_equals(old_types, dt.types);
+
+ // Removing a non-existent item from the data store does not change it.
+ dt.setData("text/plain", "foo");
+ old_types = dt.types;
+ dt.items.remove(42);
+ assert_equals(old_types, dt.types);
+
+ // Removing a valid item from the data store changes it.
+ dt.items.remove(0);
+ assert_equals(dt.items.length, 0);
+ assert_not_equals(old_types, dt.types);
+
+ // Adding a new item to the list changes it, types will return a new
+ // FrozenArray.
+ old_types = dt.types;
+ dt.items.add("foo", "text/plain");
+ assert_equals(dt.items.length, 1);
+ assert_not_equals(old_types, dt.types);
+
+ // Failing to add a new item via DataTransferItemList does not
+ // change the underlying data store.
+ old_types = dt.types;
+ assert_throws_dom("NotSupportedError", () => {
+ dt.items.add("bar", "text/plain");
+ }, "Adding an item whose type is already present throws an exception");
+ assert_equals(dt.items.length, 1);
+ assert_equals(old_types, dt.types);
+}, "Relationship between types and items");
+
+test(() => {
+ const dt = new DataTransfer();
+ dt.setData("text/plain", "foo");
+
+ let old_types = dt.types;
+ assert_equals(old_types, dt.types);
+
+ // Replacing the text/plain item causes the underlying data store item list
+ // to change, so types will return a new FrozenArray.
+ dt.setData("text/plain", "bar");
+ assert_equals(dt.types.length, 1);
+ assert_not_equals(old_types, dt.types);
+ old_types = dt.types;
+
+ // Adding a new item causes the underlying data store item list to change, so
+ // types will return a new FrozenArray.
+ dt.setData("text/uri-list", "baz quux");
+ assert_equals(dt.types.length, 2);
+ assert_not_equals(old_types, dt.types);
+
+ // Removing the text/uri-list item causes the underlying data store item list
+ // to change, so even though the item list only has a text/plain item, types
+ // will return a new FrozenArray that does not match |old_types|.
+ dt.clearData("text/uri-list");
+ assert_equals(dt.types.length, 1);
+ assert_not_equals(old_types, dt.types);
+ old_types = dt.types;
+
+ // This clearData() call did not change the underlying item list, so types is
+ // still the same as |old_types|.
+ dt.clearData("text/uri-list");
+ assert_equals(dt.types.length, 1);
+ assert_equals(old_types, dt.types);
+
+ dt.clearData();
+ old_types = dt.types;
+
+ // Clearing an already empty list does not change the underlying item list,
+ // so types is stil the same as |old_types|.
+ dt.clearData();
+ assert_equals(old_types, dt.types);
+}, "type's identity");
+
+test(() => {
+ const dt = new DataTransfer();
+ const types = dt.types;
+ dt.types = 42;
+ assert_equals(dt.types, types);
+}, "Verify type is a read-only attribute");
+
+test(() => {
+ const dt = new DataTransfer();
+ assert_array_equals(dt.types, []);
+
+ // The added File is respected
+ dt.items.add(new File(["abc"], "test.txt"));
+ assert_array_equals(dt.types, ["Files"]);
+
+ // "Files" is always last even after setting data
+ dt.setData("text/plain", "test");
+ assert_array_equals(dt.types, ["text/plain", "Files"]);
+
+ // Removing the File changes types
+ dt.items.remove(0);
+ assert_array_equals(dt.types, ["text/plain"]);
+
+ // Adding back a File as the second item works correctly.
+ dt.items.add(new File(["abc"], "test.txt"));
+ assert_array_equals(dt.types, ["text/plain", "Files"]);
+}, "DataTransfer containing files");
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/datatransferitemlist-remove.html b/testing/web-platform/tests/html/editing/dnd/datastore/datatransferitemlist-remove.html
new file mode 100644
index 0000000000..19316181c5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/datatransferitemlist-remove.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>DataTransferItemList remove() method</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+// https://github.com/whatwg/html/issues/2925
+test(() => {
+ const dt = new DataTransfer();
+
+ // Must not throw
+ dt.items.remove(0);
+ dt.items.remove(1);
+
+ dt.items.add("data", "text/plain");
+
+ // Must not throw
+ dt.items.remove(1);
+}, "remove()ing an out-of-bounds index does nothing");
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/datastore/helper-drop-box-here.xhtml b/testing/web-platform/tests/html/editing/dnd/datastore/helper-drop-box-here.xhtml
new file mode 100644
index 0000000000..d6b9d6fcef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/datastore/helper-drop-box-here.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop datastore: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/plain', 'text/uri-list', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['PASS', 'data:text/plain,1', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'];
+function enterElement(event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ say('items.length (dragenter) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));}
+function dataDrop(event)
+ {say('items.length (drop) : ' + ((event.dataTransfer.items.length >= dataTypes.length)?'PASS':'FAIL'));
+ for(var i = 0; i != dataTypes.length; i++)
+ {say('getData(' + dataTypes[i] + ') : ' + ((event.dataTransfer.getData(dataTypes[i]) == data[i])?'PASS':'FAIL'));}
+ document.querySelector('body').setAttribute('style','background-color:teal;color:white;');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+]]>
+</script>
+</head>
+<body ondragenter="enterElement(event)" ondragover="return false;" ondrop="dataDrop(event)">
+<p>Drop box here. Frame should turn green and test results should appear below.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dom/draggable.html b/testing/web-platform/tests/html/editing/dnd/dom/draggable.html
new file mode 100644
index 0000000000..600b0ee350
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dom/draggable.html
@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>drag &amp; drop – draggable attribute</title>
+<style>div#test_elements { display: none; }</style>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<noscript><p>Enable JavaScript and reload.</p></noscript>
+
+<div id='log'></div>
+
+<div id='test_elements'>
+
+ <div id='defaults'>
+ <a href='#'>.</a>
+ <div></div>
+ <img src='../resources/1x1-transparent.gif'>
+ </div>
+
+ <div id='draggable'>
+ <a draggable='true' href='#'>.</a>
+ <div draggable='true'></div>
+ <img draggable='true' src='../resources/1x1-transparent.gif'>
+ </div>
+
+ <div id='draggable_false'>
+ <a draggable='false' href='#'>.</a>
+ <div draggable='false'></div>
+ <img draggable='false' src='../resources/1x1-transparent.gif'>
+ </div>
+
+ <div id='draggable_auto'>
+ <a draggable='auto' href='#'>.</a>
+ <div draggable='auto'></div>
+ <img draggable='auto' src='../resources/1x1-transparent.gif'>
+ </div>
+
+ <div id='draggable_foo'>
+ <a draggable='foo' href='#'>.</a>
+ <div draggable='foo'></div>
+ <img draggable='foo' src='../resources/1x1-transparent.gif'>
+ </div>
+
+ <div id='changable_true'>
+ <a href='#'>.</a>
+ <div></div>
+ <img src='../resources/1x1-transparent.gif'>
+ </div>
+
+ <div id='changable_false'>
+ <a href='#'>.</a>
+ <div></div>
+ <img src='../resources/1x1-transparent.gif'>
+ </div>
+
+ <div id='changable_foo'>
+ <a href='#'>.</a>
+ <div></div>
+ <img src='../resources/1x1-transparent.gif'>
+ </div>
+
+
+</div>
+
+<script>
+var foo = document.getElementById('foo');
+
+/* Does the .draggable property exist? */
+test(function () {
+ assert_idl_attribute(document.querySelector('#defaults a'), 'draggable');
+}, 'an <a> element should have a draggable property');
+
+test(function () {
+ assert_idl_attribute(document.querySelector('#defaults div'), 'draggable');
+}, 'a <div> element should have a draggable property');
+
+test(function () {
+ assert_idl_attribute(document.querySelector('#defaults img'), 'draggable');
+}, 'an <img> element should have a draggable property');
+
+
+/* Check the default values on different types of elements */
+test(function () {
+ assert_true(document.querySelector('#defaults a').draggable);
+}, 'an <a> element should be draggable by default');
+
+test(function () {
+ assert_false(document.querySelector('#defaults div').draggable);
+}, 'a <div> element should not be draggable by default');
+
+test(function () {
+ assert_true(document.querySelector('#defaults img').draggable);
+}, 'an <img> element should be draggable by default');
+
+
+/* If draggable="true" is set, all the elements should be draggable */
+test(function () {
+ assert_true(document.querySelector('#draggable a').draggable);
+}, 'an <a> element with draggable="true" should be draggable');
+
+test(function () {
+ assert_true(document.querySelector('#draggable div').draggable);
+}, 'a <div> element with draggable="true" should be draggable');
+
+test(function () {
+ assert_true(document.querySelector('#draggable img').draggable);
+}, 'an <img> element with draggable="true" should be draggable');
+
+
+/* If draggable="false" is set, none of the elements should be draggable */
+test(function () {
+ assert_false(document.querySelector('#draggable_false a').draggable);
+}, 'an <a> element with draggable="false" should not be draggable');
+
+test(function () {
+ assert_false(document.querySelector('#draggable_false div').draggable);
+}, 'a <div> element with draggable="false" should not be draggable');
+
+test(function () {
+ assert_false(document.querySelector('#draggable_false img').draggable);
+}, 'an <img> element with draggable="false" should not be draggable');
+
+
+/* If draggable="auto" is set, fall back to the defaults */
+test(function () {
+ assert_true(document.querySelector('#draggable_auto a').draggable);
+}, 'an <a> element with draggable="auto" should be draggable');
+
+test(function () {
+ assert_false(document.querySelector('#draggable_auto div').draggable);
+}, 'a <div> element with draggable="auto" should not be draggable');
+
+test(function () {
+ assert_true(document.querySelector('#draggable_auto img').draggable);
+}, 'an <img> element with draggable="auto" should be draggable');
+
+
+/* If draggable="foo" is set, fall back to the defaults */
+test(function () {
+ assert_true(document.querySelector('#draggable_foo a').draggable);
+}, 'an <a> element with draggable="foo" should be draggable');
+
+test(function () {
+ assert_false(document.querySelector('#draggable_foo div').draggable);
+}, 'a <div> element with draggable="foo" should not be draggable');
+
+test(function () {
+ assert_true(document.querySelector('#draggable_foo img').draggable);
+}, 'an <img> element with draggable="foo" should be draggable');
+
+
+/* Setting the element.droppable attribute to true for all elements */
+test(function () {
+ document.querySelector('#changable_true a').draggable = true;
+ assert_true(document.querySelector('#changable_true a').draggable);
+}, 'an <a> element with the draggable property set to true through a script should be draggable');
+
+test(function () {
+ document.querySelector('#changable_true div').draggable = true;
+ assert_true(document.querySelector('#changable_true div').draggable);
+}, 'a <div> element with the draggable property set to true through a script should be draggable');
+
+test(function () {
+ document.querySelector('#changable_true img').draggable = true;
+ assert_true(document.querySelector('#changable_true img').draggable);
+}, 'an <img> element with the draggable property set to true through a script should be draggable');
+
+
+/* Setting the element.droppable attribute to false for all elements */
+test(function () {
+ document.querySelector('#changable_false a').draggable = false;
+ assert_false(document.querySelector('#changable_false a').draggable);
+}, 'an <a> element with the draggable property set to false through a script should not be draggable');
+
+test(function () {
+ document.querySelector('#changable_false div').draggable = false;
+ assert_false(document.querySelector('#changable_false div').draggable);
+}, 'a <div> element with the draggable property set to false through a script should not be draggable');
+
+test(function () {
+ document.querySelector('#changable_false img').draggable = false;
+ assert_false(document.querySelector('#changable_false img').draggable);
+}, 'an <img> element with the draggable property set to false through a script should not be draggable');
+
+
+/* Setting the element.droppable attribute to "foo" for all elements */
+test(function () {
+ document.querySelector('#changable_foo a').draggable = 'foo';
+ assert_true(document.querySelector('#changable_foo a').draggable);
+}, 'an <a> element with the draggable property set to "foo" through a script should be draggable');
+
+test(function () {
+ document.querySelector('#changable_foo div').draggable = 'auto';
+ assert_true(document.querySelector('#changable_foo div').draggable);
+}, 'a <div> element with the draggable property set to "foo" through a script should be draggable');
+
+test(function () {
+ document.querySelector('#changable_foo img').draggable = 'foo';
+ assert_true(document.querySelector('#changable_foo img').draggable);
+}, 'an <img> element with the draggable property set to "foo" through a script should be draggable');
+</script>
+
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dom/events.html b/testing/web-platform/tests/html/editing/dnd/dom/events.html
new file mode 100644
index 0000000000..e2b521f270
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dom/events.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>drag &amp; drop – events</title>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+
+<noscript><p>Enable JavaScript and reload.</p></noscript>
+
+<div id='log'></div>
+
+<script>
+
+var element = document.createElement('div');
+
+test(function () {
+ assert_equals(element.ondragstart, null);
+}, 'element.ondragstart initial value');
+
+test(function () {
+ assert_equals(element.ondrag, null);
+}, 'element.ondrag must initial value');
+
+test(function () {
+ assert_equals(element.ondragenter, null);
+}, 'element.ondragenter initial value');
+
+test(function () {
+ assert_equals(element.ondragleave, null);
+}, 'element.ondragleave initial value');
+
+test(function () {
+ assert_equals(element.ondragover, null);
+}, 'element.ondragover initial value');
+
+test(function () {
+ assert_equals(element.ondrop, null);
+}, 'element.ondrop initial value');
+
+test(function () {
+ assert_equals(element.ondragend, null);
+}, 'element.ondragend initial value');
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dom/specials.html b/testing/web-platform/tests/html/editing/dnd/dom/specials.html
new file mode 100644
index 0000000000..4327eac872
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dom/specials.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Exposing drag &amp; drop events on document and window</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <div id="log">Enable script and reload</div>
+ <script type="text/javascript">
+var allEvents = ['ondragstart','ondrag','ondragover','ondragenter','ondragleave','ondrop','ondragend'];
+var allObjects = [['window',window],['document',document],['HTMLElement',document.createElement('div')]];
+var fails = [];
+for( var i = 0; i < allObjects.length; i++ ) {
+ for( var j = 0; j < allEvents.length; j++ ) {
+ test(function () {
+ assert_true(allEvents[j] in allObjects[i][1]);
+ }, allEvents[j] + ' in ' + allObjects[i][0]);
+ }
+}
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/draggable-areas/border-radius.html b/testing/web-platform/tests/html/editing/dnd/draggable-areas/border-radius.html
new file mode 100644
index 0000000000..f767850f9e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/draggable-areas/border-radius.html
@@ -0,0 +1,25 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – draggable area boundaries, border-radius</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ border-radius: 100px;
+}
+div {
+ border: 1px solid black;
+ height: 200px;
+ width: 200px;
+}
+</style>
+
+<ol>
+ <li>Try dragging the white area within the black square, outside the blue
+ circle. It should <em>not</em> be draggable.</li>
+ <li>Drag the blue circle below. It should be draggable.</li>
+</ol>
+
+<div><a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a></div>
diff --git a/testing/web-platform/tests/html/editing/dnd/draggable-areas/border.html b/testing/web-platform/tests/html/editing/dnd/draggable-areas/border.html
new file mode 100644
index 0000000000..1b6f8e9557
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/draggable-areas/border.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – draggable areas – border</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ border: 10px solid orange;
+}
+</style>
+
+<p>Try dragging the orange border of the blue box below, in a downwards direction. It should be draggable.</p>
+
+<a href='#' draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a>
diff --git a/testing/web-platform/tests/html/editing/dnd/draggable-areas/box-shadow.html b/testing/web-platform/tests/html/editing/dnd/draggable-areas/box-shadow.html
new file mode 100644
index 0000000000..4fc40cb0f8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/draggable-areas/box-shadow.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – draggable areas – box-shadow</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ box-shadow: 10px 10px 0 orange;
+}
+</style>
+
+<p>Try dragging the orange area along the sides of the blue box below. It should <em>not</em> be draggable.</p>
+
+<a href='#' draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a>
diff --git a/testing/web-platform/tests/html/editing/dnd/draggable-areas/outline.html b/testing/web-platform/tests/html/editing/dnd/draggable-areas/outline.html
new file mode 100644
index 0000000000..8872997fc0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/draggable-areas/outline.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – draggable areas – outline</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ outline: 10px solid orange;
+}
+</style>
+
+<p>Try dragging the orange border of the blue box below. It should <em>not</em> be draggable.</p>
+
+<a href='#' draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a>
diff --git a/testing/web-platform/tests/html/editing/dnd/draggable-areas/transform.html b/testing/web-platform/tests/html/editing/dnd/draggable-areas/transform.html
new file mode 100644
index 0000000000..6800c64ad4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/draggable-areas/transform.html
@@ -0,0 +1,22 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – draggable area boundaries – transformed elements</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ margin-left: 100px;
+ -moz-transform: rotate(-45deg) skew(15deg, 15deg);
+ -o-transform: rotate(-45deg) skew(15deg, 15deg);
+ -webkit-transform: rotate(-45deg) skew(15deg, 15deg);
+ transform: rotate(-45deg) skew(15deg, 15deg);
+}
+</style>
+<ol>
+ <li>Try dragging the blue box below by clicking and holding <em>just</em>
+ outside its skewed edges. It should <em>not</em> be draggable.</li>
+ <li><p>Drag the blue box below. It should be draggable.</p>
+
+<a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'">TEST</a>
diff --git a/testing/web-platform/tests/html/editing/dnd/draggable-areas/z-index.html b/testing/web-platform/tests/html/editing/dnd/draggable-areas/z-index.html
new file mode 100644
index 0000000000..b1f08cb789
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/draggable-areas/z-index.html
@@ -0,0 +1,35 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 008</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: 100px;
+ left: 10px;
+ z-index: 1;
+}
+
+div {
+ background-color: orange;
+ position: absolute;
+ height: 200px;
+ width: 200px;
+ top: 150px;
+ left: 20px;
+ z-index: 2;
+ opacity: 0.9;
+}
+
+
+</style>
+
+<p>Click and hold the part of the orange box that overlaps the blue box. Then
+move your pointing device. The blue box should <em>not</em> be dragged.
+
+<a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'">TEST</a>
+
+<div></div>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/001.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/001.xhtml
new file mode 100644
index 0000000000..c34aef9734
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/001.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: allowed effects 'copy','move','link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i] &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/002.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/002.xhtml
new file mode 100644
index 0000000000..19da353097
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/002.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop: allowed effects 'copy','move','link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], i = 0;
+function selectText()
+ {document.querySelector('input').select()}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i] &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p><input value="Drag me" ondragstart="start(event)"/></p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/003.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/003.xhtml
new file mode 100644
index 0000000000..b114c3770e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/003.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: allowed effects 'copy','move','link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], i = 0;
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i] &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag link and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/004.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/004.xhtml
new file mode 100644
index 0000000000..715792f8ef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/004.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>PNG image drag and drop: allowed effects 'copy','move','link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], i = 0;
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i] &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p><img ondragstart="start(event)" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag circle and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/005.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/005.xhtml
new file mode 100644
index 0000000000..a6c237c29d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/005.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG image drag and drop: allowed effects 'copy','move','link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], i = 0;
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i] &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p><img ondragstart="start(event)" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag circle and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/006.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/006.xhtml
new file mode 100644
index 0000000000..3faea1803f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/006.xhtml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas drag and drop: allowed effects 'copy','move','link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], i = 0;
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i] &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondragenter="event.preventDefault()" ondragover="return false" ondrop="addImage(event)">Canvas</canvas>
+</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag canvas pattern and drop it onto any of the green boxes.</p>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/007.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/007.xhtml
new file mode 100644
index 0000000000..b326b25ee4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/007.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: allowed effects 'copy','copyLink','copyMove'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','copyLink','copyMove'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == 'copy' &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/008.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/008.xhtml
new file mode 100644
index 0000000000..d1ec557e73
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/008.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: allowed effects 'link','linkMove','uninitialized'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['link','linkMove','copyMove'], i = 0;
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i].substring(0,4) &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag link and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/009.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/009.xhtml
new file mode 100644
index 0000000000..e9e41c6f83
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/009.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop: allowed effects 'move','uninitialized'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+</style>
+<script type="application/ecmascript">
+var effects = ['move','uninitialized'], i = 0;
+function selectText()
+ {document.querySelector('input').select()}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == 'move' &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%2;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i];
+ event.dataTransfer.dropEffect = 'move'}
+</script>
+</head>
+<body onload="selectText()">
+<p><input value="Drag me" ondragstart="start(event)"/></p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/010.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/010.xhtml
new file mode 100644
index 0000000000..3758fdd32a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/010.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: allowed effects 'all','uninitialized'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+</style>
+<script type="application/ecmascript">
+var effects = ['all','uninitialized'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == 'copy' &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%2;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/011.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/011.xhtml
new file mode 100644
index 0000000000..ab81380feb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/011.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: allowed effects 'link','copyLink','linkMove'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['link','copyLink','linkMove'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i].substring(0,4) &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i];}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/012.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/012.xhtml
new file mode 100644
index 0000000000..a19e6d42d1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/012.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: allowed effects 'move','copyMove','linkMove'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['move','copyMove','linkMove'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i].substring(0,4) &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i];}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/013.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/013.xhtml
new file mode 100644
index 0000000000..5dc10ea385
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/013.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: allowed effects 'copy','all','uninitialized'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','all','uninitialized'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == 'copy' &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/014.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/014.xhtml
new file mode 100644
index 0000000000..91bc6efb3e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/014.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: allowed effects 'copy','copyMove','invalid'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','all','uninitialized'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == 'copy' &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i]}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/015.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/015.xhtml
new file mode 100644
index 0000000000..952abd2e15
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/015.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: dropzone 'copy','move' and 'link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));
+ selectText();}
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)" dropzone="copy string:text/plain"/>
+<div ondrop="dropSelection(event,1)" dropzone="move string:text/plain"/>
+<div ondrop="dropSelection(event,2)" dropzone="link string:text/plain"/>
+<p>Drag me</p>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/016.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/016.xhtml
new file mode 100644
index 0000000000..a1b80c5198
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/016.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop: dropzone 'copy','move' and 'link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function selectText()
+ {document.querySelector('input').select()}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));
+ selectText();}
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)" dropzone="copy string:text/plain"/>
+<div ondrop="dropSelection(event,1)" dropzone="move string:text/plain"/>
+<div ondrop="dropSelection(event,2)" dropzone="link string:text/plain"/>
+<p><input value="Drag me"/></p>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/017.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/017.xhtml
new file mode 100644
index 0000000000..36fdbc873f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/017.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: dropzone 'copy','move' and 'link'</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));}
+</script>
+</head>
+<body>
+<div ondrop="dropSelection(event,0)" dropzone="copy string:text/uri-list"/>
+<div ondrop="dropSelection(event,1)" dropzone="move string:text/uri-list"/>
+<div ondrop="dropSelection(event,2)" dropzone="link string:text/uri-list"/>
+<p><a href="data:text/plain,1">Drag me</a></p>
+<p>You should be able to drag link and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/018.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/018.xhtml
new file mode 100644
index 0000000000..2a84d75e39
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/018.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: setting dropzone attribute ondragstart</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));
+ selectText();}
+function start(event)
+ {for(var i = 0; i != 3; i++)
+ {document.querySelectorAll('div')[i].setAttribute('dropzone',effects[i] + ' string:text/plain')}
+ }
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)"/>
+<div ondrop="dropSelection(event,1)"/>
+<div ondrop="dropSelection(event,2)"/>
+<p ondragstart="start(event)">Drag me</p>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/019.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/019.xhtml
new file mode 100644
index 0000000000..66ca95c424
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/019.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop: setting dropzone attribute ondragstart</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function selectText()
+ {document.querySelector('input').select()}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));
+ selectText();}
+function start(event)
+ {for(var i = 0; i != 3; i++)
+ {document.querySelectorAll('div')[i].setAttribute('dropzone',effects[i] + ' string:text/plain')}
+ }
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)"/>
+<div ondrop="dropSelection(event,1)"/>
+<div ondrop="dropSelection(event,2)"/>
+<p ondragstart="start(event)"><input value="Drag me" ondragstart="start(event)"/></p>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/020.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/020.xhtml
new file mode 100644
index 0000000000..a06f41968d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/020.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: setting dropzone attribute ondragstart</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));}
+function start(event)
+ {for(var i = 0; i != 3; i++)
+ {document.querySelectorAll('div')[i].setAttribute('dropzone',effects[i] + ' string:text/uri-list')}
+ }
+</script>
+</head>
+<body>
+<div ondrop="dropSelection(event,0)"/>
+<div ondrop="dropSelection(event,1)"/>
+<div ondrop="dropSelection(event,2)"/>
+<p ondragstart="start(event)"><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>You should be able to drag link and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/021.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/021.xhtml
new file mode 100644
index 0000000000..441c860b73
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/021.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: removing dropzone attribute ondragstart</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:maroon;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:purple;}
+div:nth-child(3)
+ {background-color:fuchsia;}
+</style>
+<script type="application/ecmascript">
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode(' FAIL '));
+ selectText();}
+function start(event)
+ {for(var i = 0; i != 3; i++)
+ {document.querySelectorAll('div')[i].removeAttribute('dropzone')}
+ }
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)" dropzone="copy string:text/plain"/>
+<div ondrop="dropSelection(event,1)" dropzone="move string:text/plain"/>
+<div ondrop="dropSelection(event,2)" dropzone="link string:text/plain"/>
+<p ondragstart="start(event)">Drag me</p>
+<p>You should not be able to drop selection onto any of the red boxes above.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/022.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/022.xhtml
new file mode 100644
index 0000000000..7318d556ca
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/022.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: changing dropzone attribute ondragstart</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));
+ selectText();}
+function start(event)
+ {for(var i = 0; i != 3; i++)
+ {document.querySelectorAll('div')[i].setAttribute('dropzone',effects[i] + ' string:text/plain')}
+ }
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)" dropzone="link string:text/plain"/>
+<div ondrop="dropSelection(event,1)" dropzone="copy string:text/plain"/>
+<div ondrop="dropSelection(event,2)" dropzone="move string:text/plain"/>
+<p ondragstart="start(event)">Drag me</p>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/023.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/023.xhtml
new file mode 100644
index 0000000000..eb5dd04f22
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/023.xhtml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>drofEffect after drop event is cancelled</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], result = false, e = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event,element)
+ {event.preventDefault();
+ e = element;
+ if(event.dataTransfer.dropEffect == effects[element])
+ {result = true}
+ selectText();}
+function endDrag(event)
+ {document.querySelectorAll('div')[e].appendChild(document.createTextNode((result &amp;&amp; event.dataTransfer.dropEffect == effects[e])?' PASS ':' FAIL '));}
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)" dropzone="copy string:text/plain"/>
+<div ondrop="dropSelection(event,1)" dropzone="move string:text/plain"/>
+<div ondrop="dropSelection(event,2)" dropzone="link string:text/plain"/>
+<p ondragend="endDrag(event)">Drag me</p>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/024.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/024.xhtml
new file mode 100644
index 0000000000..ec5acdb949
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/024.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: invalid value of effectAllowed</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'], i = 0;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event)
+ {event.target.appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[i] &amp;&amp; event.dataTransfer.effectAllowed == effects[i])?' PASS ':' FAIL '));
+ i = (i + 1)%3;
+ selectText();}
+function start(event)
+ {event.dataTransfer.effectAllowed = effects[i];
+ event.dataTransfer.effectAllowed = 'fail';}
+</script>
+</head>
+<body onload="selectText()">
+<p ondragstart="start(event)">Drag me</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="dropSelection(event)"/>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/025.html b/testing/web-platform/tests/html/editing/dnd/drop/025.html
new file mode 100644
index 0000000000..491006b3ff
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/025.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - writing to dropEffect</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], fails = [], doneonce = false, seenevent = {};
+ orange.ondragstart = function(e) {
+ e.dataTransfer.setData('Text', 'dummy text');
+ e.dataTransfer.effectAllowed = 'all';
+ if( seenevent[e.type] ) { return; }
+ seenevent[e.type] = true;
+ if( e.dataTransfer.dropEffect != 'none' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of none';
+ }
+ try{ e.dataTransfer.dropEffect = 'move' } catch(err) {
+ fails[fails.length] = e.type + ' dropEffect threw on setting';
+ }
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of move (seems to be readonly)';
+ }
+ };
+ orange.ondrag = orange.nextSibling.ondragleave = function(e) {
+ if( seenevent[e.type] ) { return; }
+ seenevent[e.type] = true;
+ if( e.dataTransfer.dropEffect != 'none' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of none';
+ }
+ try{ e.dataTransfer.dropEffect = 'move' } catch(err) {
+ fails[fails.length] = e.type + ' dropEffect threw on setting';
+ }
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of move (seems to be readonly)';
+ }
+ };
+ orange.nextSibling.ondragenter = function(e) {
+ e.preventDefault();
+ if( seenevent[e.type] ) { return; }
+ seenevent[e.type] = true;
+ if( e.dataTransfer.dropEffect != 'copy' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of copy';
+ }
+ try{ e.dataTransfer.dropEffect = 'move' } catch(err) {
+ fails[fails.length] = e.type + ' dropEffect threw on setting';
+ }
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of move (seems to be readonly)';
+ }
+ };
+ orange.nextSibling.ondragover = function(e) {
+ e.preventDefault();
+ if( seenevent[e.type] ) {
+ e.dataTransfer.dropEffect = 'link';
+ return;
+ }
+ if( !doneonce ) {
+ if( e.dataTransfer.dropEffect != 'copy' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of copy';
+ }
+ try{ e.dataTransfer.dropEffect = 'move' } catch(err) {
+ fails[fails.length] = e.type + ' dropEffect threw on setting';
+ }
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of move (seems to be readonly)';
+ }
+ doneonce = true;
+ } else {
+ seenevent[e.type] = true;
+ if( e.dataTransfer.dropEffect != 'copy' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of copy on second attempt';
+ }
+ try{ e.dataTransfer.dropEffect = 'link' } catch(err2) {
+ fails[fails.length] = e.type + ' dropEffect threw on setting';
+ }
+ if( e.dataTransfer.dropEffect != 'link' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of link on second attempt (seems to be readonly)';
+ }
+ doneonce = true;
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ e.preventDefault();
+ if( seenevent[e.type] ) { return; }
+ seenevent[e.type] = true;
+ if( e.dataTransfer.dropEffect != 'link' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of link';
+ }
+ try{ e.dataTransfer.dropEffect = 'move' } catch(err) {
+ fails[fails.length] = e.type + ' dropEffect threw on setting';
+ }
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of move (seems to be readonly)';
+ }
+ };
+ orange.ondragend = function(e) {
+ if( seenevent[e.type] ) { return; }
+ seenevent[e.type] = true;
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ //under-specified in the spec, but part of the spec related to cancelling a drag says:
+ //"set the current drag operation to the value of the dropEffect attribute of the DragEvent
+ //object's dataTransfer object as it stood after the event dispatch finished."
+ //this does not cover successful drags, but it makes sense to be consistent
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of move';
+ }
+ try{ e.dataTransfer.dropEffect = 'copy' } catch(err) {
+ fails[fails.length] = e.type + ' dropEffect threw on setting';
+ }
+ if( e.dataTransfer.dropEffect != 'copy' ) {
+ fails[fails.length] = e.type + ' dropEffect ' + e.dataTransfer.dropEffect + ' instead of copy (seems to be readonly)';
+ }
+ if( !seenevent.dragstart ) {
+ fails[fails.length] = 'dragstart did not fire';
+ }
+ if( !seenevent.drag ) {
+ fails[fails.length] = 'drag did not fire';
+ }
+ if( !seenevent.dragenter ) {
+ fails[fails.length] = 'dragenter did not fire';
+ }
+ if( !seenevent.dragover ) {
+ fails[fails.length] = 'dragover did not fire enough times';
+ }
+ if( !seenevent.dragleave ) {
+ fails[fails.length] = 'dragleave did not fire';
+ }
+ if( !seenevent.drop ) {
+ fails[fails.length] = 'drop did not fire';
+ }
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ };
+};
+
+</script>
+
+<div draggable='true'></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/026.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/026.xhtml
new file mode 100644
index 0000000000..f7f838b763
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/026.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: multiple values in dropzone</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:olive;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:green;}
+div:nth-child(3)
+ {background-color:teal;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','move','link'];
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode((event.dataTransfer.dropEffect == effects[element])?' PASS ':' FAIL '));
+ selectText();}
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)" dropzone="copy string:text/plain string:text/x-example"/>
+<div ondrop="dropSelection(event,1)" dropzone="move file:text/plain string:text/plain"/>
+<div ondrop="dropSelection(event,2)" dropzone="link file:image/png string:text/plain"/>
+<p>Drag me</p>
+<p>You should be able to drag selection and drop it onto any of the green boxes.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/027.xhtml b/testing/web-platform/tests/html/editing/dnd/drop/027.xhtml
new file mode 100644
index 0000000000..5e2b41544b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/027.xhtml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: non matching values in dropzone</title>
+<style type="text/css">
+div
+ {display:inline-block;
+ vertical-align:top;
+ background-color:maroon;
+ color:white;
+ padding:20px;
+ width:100px;
+ height:100px;}
+div:nth-child(2)
+ {background-color:purple;}
+div:nth-child(3)
+ {background-color:fuchsia;}
+</style>
+<script type="application/ecmascript">
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropSelection(event,element)
+ {document.querySelectorAll('div')[element].appendChild(document.createTextNode(' FAIL '));
+ selectText();}
+</script>
+</head>
+<body onload="selectText()">
+<div ondrop="dropSelection(event,0)" dropzone="copy file:text/plain"/>
+<div ondrop="dropSelection(event,1)" dropzone="move file:text/plain string:text/x-example"/>
+<div ondrop="dropSelection(event,2)" dropzone="link string:text/x-example"/>
+<p ondragstart="start(event)">Drag me</p>
+<p>You should not be able to drop selection onto any of the red boxes above.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/drop/028.html b/testing/web-platform/tests/html/editing/dnd/drop/028.html
new file mode 100644
index 0000000000..ab5725ad00
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/drop/028.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dropping element with uninitialized effectAllowed</title>
+ <style type="text/css">
+div, p {
+ margin: 0.2em;
+ height: 70px;
+ width: 400px;
+ background: orange;
+ color: black;
+}
+p {
+ background: navy;
+ color: white;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var dragmicro = document.getElementsByTagName('div')[0];
+ dragmicro.ondragstart = function (e) {
+ e.dataTransfer.setData('text/plain','dummy text');
+ };
+ var droptarget = document.getElementsByTagName('p')[0];
+ droptarget.ondragenter = droptarget.ondragover = function (e) {
+ e.preventDefault();
+ };
+ droptarget.ondrop = function (e) {
+ e.preventDefault();
+ this.innerHTML = 'PASS';
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div draggable="true">Drag this rectangle.</div>
+ <p>Drop onto this rectangle. Fail if this text does not change.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/001.xhtml b/testing/web-platform/tests/html/editing/dnd/dropzone/001.xhtml
new file mode 100644
index 0000000000..aa929f5724
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/001.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropzone attribute: copy</title>
+<style type="text/css">
+div
+ {background-color:navy;
+ width:40px;
+ height:40px;
+ padding:40px;
+ color:white;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<div dropzone="copy string:text/plain" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'))"/>
+<p>You should be able to drag selection to navy box below. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/002.xhtml b/testing/web-platform/tests/html/editing/dnd/dropzone/002.xhtml
new file mode 100644
index 0000000000..1c4f483716
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/002.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropzone attribute: move</title>
+<style type="text/css">
+div
+ {background-color:navy;
+ width:40px;
+ height:40px;
+ padding:40px;
+ color:white;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me"/></p>
+<div dropzone="move string:text/plain" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'))"/>
+<p>You should be able to drag selection to navy box below. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/003.xhtml b/testing/web-platform/tests/html/editing/dnd/dropzone/003.xhtml
new file mode 100644
index 0000000000..baf359bc52
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/003.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropzone attribute: link</title>
+<style type="text/css">
+div
+ {background-color:navy;
+ width:40px;
+ height:40px;
+ padding:40px;
+ color:white;}
+</style>
+</head>
+<body>
+<p><a href="data:text/plain,1">Drag me</a></p>
+<div dropzone="link string:text/plain" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'))"/>
+<p>You should be able to drag link to navy box below. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/004.xhtml b/testing/web-platform/tests/html/editing/dnd/dropzone/004.xhtml
new file mode 100644
index 0000000000..b367435e1c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/004.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropzone and link drag and drop: allowed effects 'link','copyLink','linkMove' and 'all'</title>
+<style type="text/css">
+div
+ {background-color:gray;
+ width:200px;
+ height:200px;}
+</style>
+<script type="application/ecmascript">
+var effects = ['link','copyLink','linkMove','all'];
+function start(event)
+ {var e = parseInt(event.target.href.substring(16));
+ event.dataTransfer.effectAllowed = effects[e];}
+function finish(event)
+ {var e = parseInt(event.dataTransfer.getData('text/uri-list').substring(16,17));
+ document.querySelectorAll('a')[e].firstChild.nodeValue = (event.dataTransfer.dropEffect == 'link' &amp;&amp; event.dataTransfer.effectAllowed == effects[e])?'PASS':'FAIL';}
+</script>
+</head>
+<body>
+<p>Drag links one by one and drop them into gray box below, link text should be updated as you drop them.</p>
+<p ondragstart="start(event)">
+ <a href="data:text/plain,0">Link</a>
+ <a href="data:text/plain,1">Link</a>
+ <a href="data:text/plain,2">Link</a>
+ <a href="data:text/plain,3">Link</a>
+</p>
+<div dropzone="link string:text/uri-list" ondrop="finish(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/005.xhtml b/testing/web-platform/tests/html/editing/dnd/dropzone/005.xhtml
new file mode 100644
index 0000000000..bdc61fde7c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/005.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropzone and link drag and drop: allowed effects 'copy','copyMove','copyLink' and 'all'</title>
+<style type="text/css">
+div
+ {background-color:gray;
+ width:200px;
+ height:200px;}
+</style>
+<script type="application/ecmascript">
+var effects = ['copy','copyMove','copyLink','all'];
+function start(event)
+ {var e = parseInt(event.target.href.substring(16));
+ event.dataTransfer.effectAllowed = effects[e];}
+function finish(event)
+ {var e = parseInt(event.dataTransfer.getData('text/uri-list').substring(16,17));
+ document.querySelectorAll('a')[e].firstChild.nodeValue = (event.dataTransfer.dropEffect == 'copy' &amp;&amp; event.dataTransfer.effectAllowed == effects[e])?'PASS':'FAIL';}
+</script>
+</head>
+<body>
+<p>Drag links one by one and drop them into gray box below, link text should be updated as you drop them.</p>
+<p ondragstart="start(event)">
+ <a href="data:text/plain,0">Link</a>
+ <a href="data:text/plain,1">Link</a>
+ <a href="data:text/plain,2">Link</a>
+ <a href="data:text/plain,3">Link</a>
+</p>
+<div dropzone="copy string:text/uri-list" ondrop="finish(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/006.xhtml b/testing/web-platform/tests/html/editing/dnd/dropzone/006.xhtml
new file mode 100644
index 0000000000..a0bca3312d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/006.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropzone and link drag and drop: allowed effects 'move','copyMove','linkMove' and 'all'</title>
+<style type="text/css">
+div
+ {background-color:gray;
+ width:200px;
+ height:200px;}
+</style>
+<script type="application/ecmascript">
+var effects = ['move','copyMove','linkMove','all'];
+function start(event)
+ {var e = parseInt(event.target.href.substring(16));
+ event.dataTransfer.effectAllowed = effects[e];}
+function finish(event)
+ {var e = parseInt(event.dataTransfer.getData('text/uri-list').substring(16,17));
+ document.querySelectorAll('a')[e].firstChild.nodeValue = (event.dataTransfer.dropEffect == 'move' &amp;&amp; event.dataTransfer.effectAllowed == effects[e])?'PASS':'FAIL';}
+</script>
+</head>
+<body>
+<p>Drag links one by one and drop them into gray box below, link text should be updated as you drop them.</p>
+<p ondragstart="start(event)">
+ <a href="data:text/plain,0">Link</a>
+ <a href="data:text/plain,1">Link</a>
+ <a href="data:text/plain,2">Link</a>
+ <a href="data:text/plain,3">Link</a>
+</p>
+<div dropzone="move string:text/uri-list" ondrop="finish(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/007.html b/testing/web-platform/tests/html/editing/dnd/dropzone/007.html
new file mode 100644
index 0000000000..ef0627a1e4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/007.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dropzone should not affect the dropEffect seen by dragenter and dragover</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ var drop = document.getElementsByTagName('div')[1], dragenter, dragover;
+ drop.ondragenter = function (e) {
+ dragenter = e.dataTransfer.dropEffect;
+ };
+ drop.ondragover = function (e) {
+ dragover = e.dataTransfer.dropEffect;
+ };
+ drop.ondrop = function (e) {
+ var sequence = ([dragenter,dragover,e.dataTransfer.dropEffect]).join('=&gt;')
+ var desiredsequence = (['copy','copy','link']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div dropzone="link string:text/plain"></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/008.html b/testing/web-platform/tests/html/editing/dnd/dropzone/008.html
new file mode 100644
index 0000000000..4213e9f557
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/008.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dropzone should not affect the dropEffect if dragover is cancelled</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ var drop = document.getElementsByTagName('div')[1], dragenter, dragover;
+ drop.ondragenter = function (e) {
+ dragenter = e.dataTransfer.dropEffect;
+ e.preventDefault();
+ };
+ drop.ondragover = function (e) {
+ dragover = e.dataTransfer.dropEffect;
+ e.preventDefault();
+ };
+ drop.ondrop = function (e) {
+ var sequence = ([dragenter,dragover,e.dataTransfer.dropEffect]).join('=&gt;')
+ var desiredsequence = (['copy','copy','copy']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div dropzone="link string:text/plain"></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/009.html b/testing/web-platform/tests/html/editing/dnd/dropzone/009.html
new file mode 100644
index 0000000000..c5ae10739a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/009.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dropping selection onto dropzone with JS disabled</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Disable JavaScript</li>
+ <li>Select some text in this sentence.</li>
+ <li dropzone="copy string:text/plain">Drag the selection over this text.</li>
+ <li>If supported by the platform, the mouse cursor should show the drop-allowed cursor.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/dropzone/010.html b/testing/web-platform/tests/html/editing/dnd/dropzone/010.html
new file mode 100644
index 0000000000..44e112b2ba
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/dropzone/010.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dropping selection onto dropzone with no padding</title>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <ol>
+ <li>Select some text in this sentence.</li>
+ <li dropzone="copy string:text/plain" ondrop="this.parentNode.getElementsByTagName('li')[2].textContent='PASS';">Drag the selection over this text and release it.</li>
+ <li>This text should change.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/001-1.xhtml b/testing/web-platform/tests/html/editing/dnd/events/001-1.xhtml
new file mode 100644
index 0000000000..001d0b5c90
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/001-1.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)">Drag me</p>
+<p>Drag selected text to the frame below and drop it there. Both circles should turn green once text is dropped into lower frame.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/001.xhtml b/testing/web-platform/tests/html/editing/dnd/events/001.xhtml
new file mode 100644
index 0000000000..4c8e7d563e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/001.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of selection between frames</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="001-1.xhtml"/>
+<frame src="helper-drop-here-body-circle.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/002-1.xhtml b/testing/web-platform/tests/html/editing/dnd/events/002-1.xhtml
new file mode 100644
index 0000000000..7e575b2eb6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/002-1.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="document.querySelector('input').select()" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)"><input value="Drag me"/></p>
+<p>Drag selected text to the frame below and drop it there. Both circles should turn green once text is dropped into lower frame.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/002.xhtml b/testing/web-platform/tests/html/editing/dnd/events/002.xhtml
new file mode 100644
index 0000000000..9bd76db260
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/002.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of text input selection between frames</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="002-1.xhtml"/>
+<frame src="helper-drop-here-body-circle.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/003-1.xhtml b/testing/web-platform/tests/html/editing/dnd/events/003-1.xhtml
new file mode 100644
index 0000000000..2c7d3b9191
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/003-1.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragleave="leavePage(event)">
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondragend="endDrag(event)">Drag me</a></p>
+<p>Drag link to the frame below and drop it there. Both circles should turn green once link is dropped into lower frame.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/003.xhtml b/testing/web-platform/tests/html/editing/dnd/events/003.xhtml
new file mode 100644
index 0000000000..c853e931d5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/003.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of link between frames</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="003-1.xhtml"/>
+<frame src="helper-drop-here-body-circle.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/004.xhtml b/testing/web-platform/tests/html/editing/dnd/events/004.xhtml
new file mode 100644
index 0000000000..85753621bb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/004.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of selection to iframe</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+iframe
+ {width:100%;
+ height:500px;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)">Drag me</p>
+<p>Drag selected text to the frame below and drop it there. Both circles should turn green once text is dropped into lower frame.</p>
+<div/>
+<pre/>
+<iframe src="helper-drop-here-body-circle.xhtml">XHTML document</iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/005.xhtml b/testing/web-platform/tests/html/editing/dnd/events/005.xhtml
new file mode 100644
index 0000000000..4ea0a9058d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/005.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of text input selection to iframe</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+iframe
+ {width:100%;
+ height:500px;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="document.querySelector('input').select()" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)"><input value="Drag me"/></p>
+<p>Drag selected text to the frame below and drop it there. Both circles should turn green once text is dropped into lower frame.</p>
+<div/>
+<pre/>
+<iframe src="helper-drop-here-body-circle.xhtml">XHTML document</iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/006.xhtml b/testing/web-platform/tests/html/editing/dnd/events/006.xhtml
new file mode 100644
index 0000000000..f9d659f31a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/006.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of link to iframe</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+iframe
+ {width:100%;
+ height:500px;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragleave="leavePage(event)">
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondragend="endDrag(event)">Drag me</a></p>
+<p>Drag link to the frame below and drop it there. Both circles should turn green once link is dropped into lower frame.</p>
+<div/>
+<pre/>
+<iframe src="helper-drop-here-body-circle.xhtml">XHTML document</iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/007.xhtml b/testing/web-platform/tests/html/editing/dnd/events/007.xhtml
new file mode 100644
index 0000000000..3c1c217a89
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/007.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of selection to object</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+object
+ {width:100%;
+ height:500px;
+ border:solid medium navy;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)">Drag me</p>
+<p>Drag selected text to the frame below and drop it there. Both circles should turn green once text is dropped into lower frame.</p>
+<div/>
+<pre/>
+<object type="application/xhtml+xml" data="helper-drop-here-body-circle.xhtml">XHTML document</object>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/008.xhtml b/testing/web-platform/tests/html/editing/dnd/events/008.xhtml
new file mode 100644
index 0000000000..e22695a692
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/008.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of text input selection to object</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+object
+ {width:100%;
+ height:500px;
+ border:solid medium navy;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="document.querySelector('input').select()" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)"><input value="Drag me"/></p>
+<p>Drag selected text to the frame below and drop it there. Both circles should turn green once text is dropped into lower frame.</p>
+<div/>
+<pre/>
+<object type="application/xhtml+xml" data="helper-drop-here-body-circle.xhtml">XHTML document</object>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/009.xhtml b/testing/web-platform/tests/html/editing/dnd/events/009.xhtml
new file mode 100644
index 0000000000..d2ddd32bdc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/009.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of link to object</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+object
+ {width:100%;
+ height:500px;
+ border:solid medium navy;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragleave="leavePage(event)">
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondragend="endDrag(event)">Drag me</a></p>
+<p>Drag link to the frame below and drop it there. Both circles should turn green once link is dropped into lower frame.</p>
+<div/>
+<pre/>
+<object type="application/xhtml+xml" data="helper-drop-here-body-circle.xhtml">XHTML document</object>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/010.xhtml b/testing/web-platform/tests/html/editing/dnd/events/010.xhtml
new file mode 100644
index 0000000000..bbc2254282
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/010.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of selection from iframe</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+iframe
+ {width:100%;
+ height:300px;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function enterPage(event)
+ {event.preventDefault();
+ if(step++ > 0)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragenter should fire before dragover and drop');}
+ }
+function overPage(event)
+ {event.preventDefault();
+ if(step++ > 1)
+ {setColor('green green silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function dropIt(event)
+ {if(step++ > 1)
+ {setColor('green');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragenter="enterPage(event)" ondragover="overPage(event)" ondrop="dropIt(event)">
+<iframe src="helper-drag-me-p-with-circle.xhtml">XHTML document</iframe>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/011.xhtml b/testing/web-platform/tests/html/editing/dnd/events/011.xhtml
new file mode 100644
index 0000000000..3ec4e5c40b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/011.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of text input selection from iframe</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+iframe
+ {width:100%;
+ height:300px;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function enterPage(event)
+ {event.preventDefault();
+ if(step++ > 0)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragenter should fire before dragover and drop');}
+ }
+function overPage(event)
+ {event.preventDefault();
+ if(step++ > 1)
+ {setColor('green green silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function dropIt(event)
+ {if(step++ > 1)
+ {setColor('green');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragenter="enterPage(event)" ondragover="overPage(event)" ondrop="dropIt(event)">
+<iframe src="helper-drag-me-input-with-circle.xhtml">XHTML document</iframe>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/012.xhtml b/testing/web-platform/tests/html/editing/dnd/events/012.xhtml
new file mode 100644
index 0000000000..0cebe5d895
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/012.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of link from iframe</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+iframe
+ {width:100%;
+ height:300px;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function enterPage(event)
+ {event.preventDefault();
+ if(step++ > 0)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragenter should fire before dragover and drop');}
+ }
+function overPage(event)
+ {event.preventDefault();
+ if(step++ > 1)
+ {setColor('green green silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function dropIt(event)
+ {if(step++ > 1)
+ {setColor('green');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragenter="enterPage(event)" ondragover="overPage(event)" ondrop="dropIt(event)">
+<iframe src="helper-drag-me-link-with-circle.xhtml">XHTML document</iframe>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/013.xhtml b/testing/web-platform/tests/html/editing/dnd/events/013.xhtml
new file mode 100644
index 0000000000..3e6c19c402
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/013.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of selection from object</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+object
+ {width:100%;
+ height:300px;
+ border:solid medium navy;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function enterPage(event)
+ {event.preventDefault();
+ if(step++ > 0)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragenter should fire before dragover and drop');}
+ }
+function overPage(event)
+ {event.preventDefault();
+ if(step++ > 1)
+ {setColor('green green silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function dropIt(event)
+ {if(step++ > 1)
+ {setColor('green');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragenter="enterPage(event)" ondragover="overPage(event)" ondrop="dropIt(event)">
+<object type="application/xhtml+xml" data="helper-drag-me-p-with-circle.xhtml">XHTML document</object>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/014.xhtml b/testing/web-platform/tests/html/editing/dnd/events/014.xhtml
new file mode 100644
index 0000000000..09abedd1f1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/014.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of text input selection from object</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+object
+ {width:100%;
+ height:300px;
+ border:solid medium navy;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function enterPage(event)
+ {event.preventDefault();
+ if(step++ > 0)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragenter should fire before dragover and drop');}
+ }
+function overPage(event)
+ {event.preventDefault();
+ if(step++ > 1)
+ {setColor('green green silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function dropIt(event)
+ {if(step++ > 1)
+ {setColor('green');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragenter="enterPage(event)" ondragover="overPage(event)" ondrop="dropIt(event)">
+<object type="application/xhtml+xml" data="helper-drag-me-input-with-circle.xhtml">XHTML document</object>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/015.xhtml b/testing/web-platform/tests/html/editing/dnd/events/015.xhtml
new file mode 100644
index 0000000000..7baf086b77
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/015.xhtml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of link from object</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+object
+ {width:100%;
+ height:300px;
+ border:solid medium navy;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function enterPage(event)
+ {event.preventDefault();
+ if(step++ > 0)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragenter should fire before dragover and drop');}
+ }
+function overPage(event)
+ {event.preventDefault();
+ if(step++ > 1)
+ {setColor('green green silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function dropIt(event)
+ {if(step++ > 1)
+ {setColor('green');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragenter="enterPage(event)" ondragover="overPage(event)" ondrop="dropIt(event)">
+<object type="application/xhtml+xml" data="helper-drag-me-link-with-circle.xhtml">XHTML document</object>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/016.xhtml b/testing/web-platform/tests/html/editing/dnd/events/016.xhtml
new file mode 100644
index 0000000000..e6f9972a4d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/016.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: events after dragenter is cancelled</title>
+<script type="application/ecmascript">
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function start(event)
+ {event.preventDefault();}
+function dragMe(event)
+ {say('FAIL (drag event should not fire if dragstart is cancelled)');}
+function enterBody(event)
+ {event.preventDefault();
+ say('FAIL (dragover event should not fire if dragstart is cancelled)');}
+function overBody(event)
+ {event.preventDefault();
+ say('FAIL (dragover event should not fire if dragstart is cancelled)');}
+function dropIt(event)
+ {say('FAIL (drop event should not fire if dragstart is cancelled)');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+</script>
+</head>
+<body onload="selectText()" ondragenter="enterBody(event)" ondragover="overBody(event)" ondrop="dropIt(event)">
+<p ondragstart="start(event)" ondrag="dragMe(event)">Try to drag me</p>
+<p>You should not be able to drag text selection above.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/017.xhtml b/testing/web-platform/tests/html/editing/dnd/events/017.xhtml
new file mode 100644
index 0000000000..0a372b5337
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/017.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop: events after dragenter is cancelled</title>
+<script type="application/ecmascript">
+function selectText()
+ {document.querySelector('input').select()}
+function start(event)
+ {event.preventDefault();}
+function dragMe(event)
+ {say('FAIL (drag event should not fire if dragstart is cancelled)');}
+function enterBody(event)
+ {event.preventDefault();
+ say('FAIL (dragover event should not fire if dragstart is cancelled)');}
+function overBody(event)
+ {event.preventDefault();
+ say('FAIL (dragover event should not fire if dragstart is cancelled)');}
+function dropIt(event)
+ {say('FAIL (drop event should not fire if dragstart is cancelled)');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+</script>
+</head>
+<body onload="selectText()" ondragenter="enterBody(event)" ondragover="overBody(event)" ondrop="dropIt(event)">
+<p ondragstart="start(event)"><input value="Try to drag me" ondrag="dragMe(event)"/></p>
+<p>You should not be able to drag text selection above.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/018.xhtml b/testing/web-platform/tests/html/editing/dnd/events/018.xhtml
new file mode 100644
index 0000000000..4aa8ba0997
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/018.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: events after dragenter is cancelled</title>
+<script type="application/ecmascript">
+function start(event)
+ {event.preventDefault();}
+function dragMe(event)
+ {say('FAIL (drag event should not fire if dragstart is cancelled)');}
+function enterBody(event)
+ {event.preventDefault();
+ say('FAIL (dragover event should not fire if dragstart is cancelled)');}
+function overBody(event)
+ {event.preventDefault();
+ say('FAIL (dragover event should not fire if dragstart is cancelled)');}
+function dropIt(event)
+ {say('FAIL (drop event should not fire if dragstart is cancelled)');}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+</script>
+</head>
+<body ondragenter="enterBody(event)" ondragover="overBody(event)" ondrop="dropIt(event)">
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="dragMe(event)">Try to drag me</a></p>
+<p>You should not be able to drag link above.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/019.xhtml b/testing/web-platform/tests/html/editing/dnd/events/019.xhtml
new file mode 100644
index 0000000000..4e718dda27
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/019.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: events after drag is cancelled</title>
+<script type="application/ecmascript">
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dragMe(event)
+ {event.preventDefault();}
+function dropIt(event)
+ {say('FAIL (no drop should occur after drag is cancelled)')}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+</script>
+</head>
+<body onload="selectText()" dropzone="copy string:text/plain" ondrop="dropIt(event)">
+<p ondrag="dragMe(event)">Try to drag me</p>
+<p>You should not be able to drop text selection above.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/020.xhtml b/testing/web-platform/tests/html/editing/dnd/events/020.xhtml
new file mode 100644
index 0000000000..4a1d60a647
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/020.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop: events after drag is cancelled</title>
+<script type="application/ecmascript">
+function selectText()
+ {document.querySelector('input').select()}
+function dragMe(event)
+ {event.preventDefault();}
+function dropIt(event)
+ {say('FAIL (no drop should occur after drag is cancelled)')}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+</script>
+</head>
+<body onload="selectText()" dropzone="copy string:text/plain" ondrop="dropIt(event)">
+<p><input value="Try to drag me" ondrag="dragMe(event)"/></p>
+<p>You should not be able to drop text selection above.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/021.xhtml b/testing/web-platform/tests/html/editing/dnd/events/021.xhtml
new file mode 100644
index 0000000000..6cca911f1d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/021.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: events after drag is cancelled</title>
+<script type="application/ecmascript">
+function dragMe(event)
+ {event.preventDefault();}
+function dropIt(event)
+ {say('FAIL (no drop should occur after drag is cancelled)')}
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+</script>
+</head>
+<body dropzone="copy string:text/uri-list" ondrop="dropIt(event)">
+<p><a href="data:text/plain,1" ondrag="dragMe(event)">Try to drag me</a></p>
+<p>You should not be able to drag link above.</p>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/022.xhtml b/testing/web-platform/tests/html/editing/dnd/events/022.xhtml
new file mode 100644
index 0000000000..98773f2cb6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/022.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: events after drag and drop is cancelled</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;}
+</style>
+<script type="application/ecmascript">
+var result = true;
+function selectText()
+ {window.getSelection().selectAllChildren(document.querySelector('p'))}
+function dropIt(event)
+ {result = false;
+ setColor('maroon');
+ say('drop event : FAIL (no drop should occur once drag and drop is cancelled)')}
+function endDrag(event)
+ {if(result)
+ {setColor('teal')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="selectText()" dropzone="copy string:text/plain" ondrop="dropIt(event)">
+<p ondragstart="setColor('teal silver silver silver')" ondragend="endDrag(event)">Drag me</p>
+<p>Drag selected text and press Esc before you drop it. Circle below should turn green once drag and drop is cancelled.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/023.xhtml b/testing/web-platform/tests/html/editing/dnd/events/023.xhtml
new file mode 100644
index 0000000000..fadf76bf27
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/023.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop: events after drag and drop is cancelled</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;}
+</style>
+<script type="application/ecmascript">
+var result = true;
+function selectText()
+ {document.querySelector('input').select()}
+function dropIt(event)
+ {result = false;
+ setColor('maroon');
+ say('drop event : FAIL (no drop should occur once drag and drop is cancelled)')}
+function endDrag(event)
+ {if(result)
+ {setColor('teal')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="selectText()" dropzone="copy string:text/plain" ondrop="dropIt(event)">
+<p><input value="Drag me" ondragstart="setColor('teal silver silver silver')" ondragend="endDrag(event)"/></p>
+<p>Drag selected text and press Esc before you drop it. Circle below should turn green once drag and drop is cancelled.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/024.xhtml b/testing/web-platform/tests/html/editing/dnd/events/024.xhtml
new file mode 100644
index 0000000000..9afa8f709a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/024.xhtml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: events after drag and drop is cancelled</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;}
+</style>
+<script type="application/ecmascript">
+var result = true;
+function dropIt(event)
+ {result = false;
+ setColor('maroon');
+ say('drop event : FAIL (no drop should occur once drag and drop is cancelled)')}
+function endDrag(event)
+ {if(result)
+ {setColor('teal')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'));}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body dropzone="copy string:text/uri-list" ondrop="dropIt(event)">
+<p><a href="data:text/plain,1" ondragstart="setColor('teal silver silver silver')" ondragend="endDrag(event)">Drag me</a></p>
+<p>Drag link and press Esc before you drop it. Circle below should turn green once drag and drop is cancelled.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/025.html b/testing/web-platform/tests/html/editing/dnd/events/025.html
new file mode 100644
index 0000000000..b4bbbc547e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/025.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragover repeating</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var numsecs = 5, maxpolltime = 0.550, minpolltime = 0.150;
+ var blue = document.getElementsByTagName('div')[1], p = document.getElementsByTagName('p')[0];
+ var numfired = 0, readytocount = false;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'all';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ blue.ondrop = function (e) {
+ e.preventDefault();
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( readytocount ) { numfired++; }
+ };
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ p.innerHTML = 'Keep the mouse perfectly still...';
+ //give the tester a second to get ready
+ setTimeout(function () {
+ readytocount = true;
+ numfired = 0;
+ p.innerHTML = 'Keep the mouse perfectly still for '+numsecs+' seconds';
+ var countsecs = numsecs;
+ var intr = setInterval(function () {
+ countsecs--;
+ if( countsecs ) {
+ p.innerHTML = 'Keep the mouse perfectly still for '+countsecs+' seconds';
+ } else {
+ clearInterval(intr);
+ var passed = numfired >= Math.floor( numsecs / maxpolltime ) && numfired <= Math.floor( numsecs / minpolltime );
+ document.getElementsByTagName('p')[0].innerHTML = ( passed ? 'PASS' : 'FAIL' ) +
+ '<br><br>(Fired ' + numfired + ' times in ' + numsecs + ' seconds, must be between ' +
+ Math.floor( numsecs / maxpolltime ) + ' and ' + Math.floor( numsecs / minpolltime ) + ')<br>You can release the drag now';
+ }
+ },1000);
+ },1000);
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <p>Drag the orange square over the blue square, then keep the mouse perfectly still until the result appears.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/026.html b/testing/web-platform/tests/html/editing/dnd/events/026.html
new file mode 100644
index 0000000000..816155a09d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/026.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag repeating</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var numsecs = 5, maxpolltime = 0.550, minpolltime = 0.150;
+ var orange = document.getElementsByTagName('div')[0], p = document.getElementsByTagName('p')[0];
+ var numfired = 0, readytocount = false;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'all';
+ e.dataTransfer.setData('text','dummy text');
+ p.innerHTML = 'Keep the mouse perfectly still...';
+ //give the tester a second to get ready
+ setTimeout(function () {
+ readytocount = true;
+ numfired = 0;
+ p.innerHTML = 'Keep the mouse perfectly still for '+numsecs+' seconds';
+ var countsecs = numsecs;
+ var intr = setInterval(function () {
+ countsecs--;
+ if( countsecs ) {
+ p.innerHTML = 'Keep the mouse perfectly still for '+countsecs+' seconds';
+ } else {
+ clearInterval(intr);
+ var passed = numfired >= Math.floor( numsecs / maxpolltime ) && numfired <= Math.floor( numsecs / minpolltime );
+ document.getElementsByTagName('p')[0].innerHTML = ( passed ? 'PASS' : 'FAIL' ) +
+ '<br><br>(Fired ' + numfired + ' times in ' + numsecs + ' seconds, must be between ' +
+ Math.floor( numsecs / maxpolltime ) + ' and ' + Math.floor( numsecs / minpolltime ) + ')<br>You can release the drag now';
+ }
+ },1000);
+ },1000);
+ };
+ orange.ondrag = function (e) {
+ if( readytocount ) { numfired++; }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <p>Drag the orange square sideways until the drag placeholder appears, then keep the mouse perfectly still until the result appears.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/027.xhtml b/testing/web-platform/tests/html/editing/dnd/events/027.xhtml
new file mode 100644
index 0000000000..983bcd298b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/027.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>PNG image drag and drop: 'return false' should not cancel event</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body onload="document.querySelector('img').addEventListener('drag',function (){return false},false)">
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy'" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag green circle to the silver box below and drop it. It should be copied to the box once you drop it there.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="addImage(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/028.xhtml b/testing/web-platform/tests/html/editing/dnd/events/028.xhtml
new file mode 100644
index 0000000000..35c69766f6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/028.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: 'return false' should not cancel event</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('a').addEventListener('drag',function (){return false},false)">
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy'">Drag me</a></p>
+<p>Drag link above to the navy box below and drop it. You should see word PASS once you drop it in the box.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'))"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/029.html b/testing/web-platform/tests/html/editing/dnd/events/029.html
new file mode 100644
index 0000000000..70c243d993
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/029.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop without cancelling dragenter</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], sequence = [];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ drag.ondragenter = drag.ondragover = function (e) {
+ e.preventDefault();
+ };
+ var drop = document.getElementsByTagName('div')[1], dragoverhasfired = false;
+ drop.ondragenter = function (e) {
+ dragoverhasfired = true; //events targeting body will be ignored until this event has fired
+ sequence[sequence.length] = 'drop.dragenter';
+ };
+ drop.ondragover = function (e) {
+ e.preventDefault();
+ if( sequence[sequence.length-1] != 'drop.dragover' ) {
+ sequence[sequence.length] = 'drop.dragover';
+ }
+ };
+ drop.ondrop = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = 'drop.ondrop';
+ };
+ document.body.ondragenter = function (e) {
+ if( e.target != this ) { return; }
+ if( dragoverhasfired ) {
+ sequence[sequence.length] = 'body.dragenter';
+ }
+ };
+ document.body.ondragover = function (e) {
+ if( e.target != this ) { return; }
+ if( dragoverhasfired ) {
+ if( sequence[sequence.length-1] != 'body.dragover' ) {
+ sequence[sequence.length] = 'body.dragover';
+ }
+ }
+ };
+ drag.ondragend = function (e) {
+ sequence = sequence.join('=&gt;')
+ var desiredsequence = (['drop.dragenter','body.dragenter','body.dragover']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><div></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/030.html b/testing/web-platform/tests/html/editing/dnd/events/030.html
new file mode 100644
index 0000000000..8dc573474d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/030.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop without cancelling dragenter on body</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ margin-left: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+//If dragenter is cancelled the body should then become the target element, receiving both a dragenter and a dragover event.
+//When the body is the actual immediate user selection (first time over the body), that means it gets *2* dragenter events
+//Then when a div becomes immediate user selection (blue) but does not cancel the dragenter event, dragenter does not need
+//to fire on body because body is already the current target element
+//Then when the body is the actual immediate user selection again (second time over the body), it is already the current
+//target element, so does not get a dragenter event
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], beforecount = 0, aftercount = 0, switchcount = false;
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ drag.ondragenter = drag.ondrop = drag.ondragover = function (e) {
+ e.preventDefault();
+ };
+ var drop = document.getElementsByTagName('div')[1];
+ drop.ondragenter = function (e) {
+ switchcount = true;
+ };
+ drop.ondragover = function (e) {
+ e.preventDefault();
+ };
+ document.body.ondragenter = function (e) {
+ if( e.target != this ) { return; }
+ if( switchcount ) { aftercount++; } else { beforecount++; }
+ };
+ drag.ondragend = function (e) {
+ document.getElementsByTagName('div')[2].innerHTML = ( beforecount == 2 && aftercount == 0 ) ? 'PASS' : ( 'FAIL, dragenter fired on body '+beforecount+' time(s) before blue.ondragenter and '+aftercount+' time(s) afterwards, instead of 2 and 0 times respectively' );
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><div></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square, then back to the orange square, and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/031-1.html b/testing/web-platform/tests/html/editing/dnd/events/031-1.html
new file mode 100644
index 0000000000..b70082ba21
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/031-1.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Non-rendered body should still become current target element</title>
+ <style type="text/css">
+html { background: blue; }
+ </style>
+ </head>
+ <body>
+
+ <script type="text/javascript">
+var seentypes = {};
+document.body.ondragenter = document.body.ondragover = document.body.ondrop = function (e) {
+ e.preventDefault();
+ //document.body.innerHTML += e.type;
+ if( e.type == 'drop' ) {
+ document.body.innerHTML = ( seentypes.dragenter && seentypes.dragover ) ? 'PASS' : 'FAIL';
+ } else {
+ seentypes[e.type] = true;
+ }
+}
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/031.html b/testing/web-platform/tests/html/editing/dnd/events/031.html
new file mode 100644
index 0000000000..92220e30e1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/031.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Non-rendered body should still become current target element</title>
+ <style type="text/css">
+span { background: orange; display: inline-block; height: 200px; width: 200px; }
+iframe { border: none; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('span')[0].ondragstart = function (e) {
+ e.dataTransfer.setData('text','dummy text');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Drag the orange square onto the blue square and release it. Fail if text does not appear in the blue square.</p>
+ <p><span draggable="true"></span> <iframe height="200" width="200" src="031-1.html"></iframe></p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/032.html b/testing/web-platform/tests/html/editing/dnd/events/032.html
new file mode 100644
index 0000000000..a928a3c425
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/032.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop passing over body without cancelling dragenter</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ margin-left: 200px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+//this test enforces the following spec statement:
+//"if this immediate user selection is not the same as the current target element"
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], sequence = [];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ drag.ondragenter = function (e) {
+ sequence[sequence.length] = 'drag.dragenter';
+ };
+ drag.ondragover = function (e) {
+ if( sequence[sequence.length-1] != 'drag.dragover' ) {
+ sequence[sequence.length] = 'drag.dragover';
+ }
+ };
+ var drop = document.getElementsByTagName('div')[1];
+ drop.ondragenter = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = 'drop.dragenter';
+ };
+ drop.ondragover = function (e) {
+ e.preventDefault();
+ if( sequence[sequence.length-1] != 'drop.dragover' ) {
+ sequence[sequence.length] = 'drop.dragover';
+ }
+ };
+ drop.ondrop = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = 'drop.ondrop';
+ };
+ document.body.ondragenter = function (e) {
+ sequence[sequence.length] = ( e.target == this ) ? 'body.dragenter' : 'bubble.dragenter';
+ };
+ document.body.ondragover = function (e) {
+ if( e.target != this ) { return; }
+ if( sequence[sequence.length-1] != 'body.dragover' ) {
+ sequence[sequence.length] = 'body.dragover';
+ }
+ };
+ drag.ondragend = function (e) {
+ sequence = sequence.join('=&gt;')
+ var desiredsequence = (['drag.dragenter','bubble.dragenter','body.dragenter','body.dragover','drop.dragenter','bubble.dragenter','drop.dragover','drop.ondrop']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><div></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/033.html b/testing/web-platform/tests/html/editing/dnd/events/033.html
new file mode 100644
index 0000000000..d06bee3d8d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/033.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop without cancelling dragenter from non-target to non-target</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], sequence = [];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ drag.ondragenter = function (e) {
+ sequence[sequence.length] = 'drag.dragenter';
+ };
+ drag.ondragover = function (e) {
+ if( sequence[sequence.length-1] != 'drag.dragover' ) {
+ sequence[sequence.length] = 'drag.dragover';
+ }
+ };
+ drag.ondragleave = function (e) {
+ sequence[sequence.length] = 'drag.dragleave';
+ };
+ var drop = document.getElementsByTagName('div')[1];
+ drop.ondragenter = function (e) {
+ sequence[sequence.length] = 'drop.dragenter';
+ };
+ drop.ondragover = function (e) {
+ if( sequence[sequence.length-1] != 'drop.dragover' ) {
+ sequence[sequence.length] = 'drop.dragover';
+ }
+ };
+ drop.ondrop = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = 'drop.ondrop';
+ };
+ document.body.ondragenter = function (e) {
+ sequence[sequence.length] = ( e.target == this ) ? 'body.dragenter' : 'bubble.dragenter';
+ };
+ document.body.ondragover = function (e) {
+ if( e.target != this ) { return; }
+ if( sequence[sequence.length-1] != 'body.dragover' ) {
+ sequence[sequence.length] = 'body.dragover';
+ }
+ };
+ drag.ondragend = function (e) {
+ sequence = sequence.join('=&gt;')
+ var desiredsequence = (['drag.dragenter','bubble.dragenter','body.dragenter','body.dragover','drop.dragenter','bubble.dragenter','body.dragover']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><div></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/034.html b/testing/web-platform/tests/html/editing/dnd/events/034.html
new file mode 100644
index 0000000000..ea8867ba2c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/034.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop with cancelling dragenter on body</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ margin-left: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], beforecount = 0, aftercount = 0, switchcount = false;
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ drag.ondragenter = drag.ondrop = drag.ondragover = function (e) {
+ e.preventDefault();
+ };
+ var drop = document.getElementsByTagName('div')[1];
+ drop.ondragenter = function (e) {
+ switchcount = true;
+ };
+ drop.ondragover = function (e) {
+ e.preventDefault();
+ };
+ document.body.ondragenter = function (e) {
+ if( e.target != this ) { return; }
+ e.preventDefault(); //don't cancel when it bubbles from the child elements
+ if( switchcount ) { aftercount++; } else { beforecount++; }
+ };
+ drag.ondragend = function (e) {
+ document.getElementsByTagName('div')[2].innerHTML = ( beforecount == 1 && aftercount == 0 ) ? 'PASS' : ( 'FAIL, dragenter fired on body '+beforecount+' time(s) before blue.ondragenter and '+aftercount+' time(s) afterwards, instead of 1 and 0 times respectively' );
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><div></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square, then back to the orange square, and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/035.html b/testing/web-platform/tests/html/editing/dnd/events/035.html
new file mode 100644
index 0000000000..8a7cb17491
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/035.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop passing over body with cancelling dragenter</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ margin-left: 200px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+//this test enforces the following spec statement:
+//"if this immediate user selection is not the same as the current target element"
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], sequence = [];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ drag.ondragenter = function (e) {
+ sequence[sequence.length] = 'drag.dragenter';
+ };
+ drag.ondragover = function (e) {
+ if( sequence[sequence.length-1] != 'drag.dragover' ) {
+ sequence[sequence.length] = 'drag.dragover';
+ }
+ };
+ var drop = document.getElementsByTagName('div')[1];
+ drop.ondragenter = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = 'drop.dragenter';
+ };
+ drop.ondragover = function (e) {
+ e.preventDefault();
+ if( sequence[sequence.length-1] != 'drop.dragover' ) {
+ sequence[sequence.length] = 'drop.dragover';
+ }
+ };
+ drop.ondrop = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = 'drop.ondrop';
+ };
+ document.body.ondragenter = function (e) {
+ sequence[sequence.length] = ( e.target == this ) ? 'body.dragenter' : 'bubble.dragenter';
+ if( e.target != this ) { return; }
+ e.preventDefault(); //don't cancel when it bubbles from the child elements
+ };
+ document.body.ondragover = function (e) {
+ if( e.target != this ) { return; }
+ if( sequence[sequence.length-1] != 'body.dragover' ) {
+ sequence[sequence.length] = 'body.dragover';
+ }
+ };
+ drag.ondragend = function (e) {
+ sequence = sequence.join('=&gt;')
+ var desiredsequence = (['drag.dragenter','bubble.dragenter','body.dragenter','body.dragover','drop.dragenter','bubble.dragenter','drop.dragover','drop.ondrop']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><div></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/036.html b/testing/web-platform/tests/html/editing/dnd/events/036.html
new file mode 100644
index 0000000000..1c66f18483
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/036.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop passing over body with all events handled at body</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ margin-left: 200px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('div')[0], drop = document.getElementsByTagName('div')[1], sequence = [];
+ function targ(el) {
+ if( el == drag ) { return 'drag'; }
+ else if( el == drop ) { return 'drop'; }
+ else if( el == document.body ) { return 'body'; }
+ else { return el; }
+ }
+ document.body.ondragstart = function (e) {
+ e.dataTransfer.setData('text','data');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ document.body.ondragenter = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = targ(e.target)+'.ondragenter';
+ };
+ document.body.ondragover = function (e) {
+ e.preventDefault();
+ var seqname = targ(e.target)+'.ondragover';
+ if( sequence[sequence.length-1] != seqname ) {
+ sequence[sequence.length] = seqname;
+ }
+ };
+ document.body.ondrop = function (e) {
+ e.preventDefault();
+ sequence[sequence.length] = targ(e.target)+'.ondrop';
+ sequence[sequence.length] = e.dataTransfer.getData('text');
+ };
+ document.body.ondragend = function (e) {
+ sequence[sequence.length] = targ(e.target)+'.ondragend';
+ sequence = sequence.join('=&gt;')
+ var desiredsequence = (['drag.ondragenter','drag.ondragover','body.ondragenter','body.ondragover','drop.ondragenter','drop.ondragover','drop.ondrop','data','drag.ondragend']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><div></div>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square and release it. For the entire duration of the drag, if supported by the platform, the mouse cursor should show as a drop-allowed or drop-copy-allowed cursor.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/037-proposed.xhtml b/testing/web-platform/tests/html/editing/dnd/events/037-proposed.xhtml
new file mode 100644
index 0000000000..3c94177f02
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/037-proposed.xhtml
@@ -0,0 +1,86 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Drag and drop without cancelling dragenter and without body</title>
+ <style type="text/css">
+html {
+ padding:20px;
+}
+head + div {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+head + div + div {
+ height: 100px;
+ width: 100px;
+ margin-left: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript"><![CDATA[
+//Drag passes from orange to root element. Dragenter fires at root element.
+//Dragenter is not cancelled. Body does not exist. Dragenter fires at root element again.
+//Drag passes from root element to blue. Dragenter fires at blue.
+//Dragenter is not cancelled. Body does not exist. Current target element is root element.
+//Drag passes from blue to root element. Current target element is already root element.
+//Drag passes from root element to orange. Dragenter fires at orange, and is cancelled.
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], sequence = [];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ orange.ondragenter = orange.ondrop = function (e) {
+ sequence[sequence.length] = 'orange.'+e.type;
+ e.preventDefault();
+ };
+ orange.ondragleave = function (e) {
+ sequence[sequence.length] = 'orange.dragleave';
+ };
+ orange.ondragover = function (e) {
+ if( sequence[sequence.length-1] != 'orange.dragover' ) {
+ sequence[sequence.length] = 'orange.dragover';
+ }
+ e.preventDefault();
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = blue.ondragover = blue.ondragleave = function (e) {
+ sequence[sequence.length] = 'blue.'+e.type;
+ };
+ document.documentElement.ondragenter = document.documentElement.ondragleave = function (e) {
+ if( e.target != this ) { return; }
+ sequence[sequence.length] = 'html.'+e.type;
+ };
+ document.documentElement.ondragover = function (e) {
+ if( e.target != this ) { return; }
+ if( sequence[sequence.length-1] != 'html.dragover' ) {
+ sequence[sequence.length] = 'html.dragover';
+ }
+ };
+ document.ondragenter = document.ondragleave = document.ondragover = document.ondragleave = function (e) {
+ if( e.target != this ) { return; }
+ sequence[sequence.length] = 'document.'+e.type;
+ };
+ orange.ondragend = function (e) {
+ sequence = sequence.join('=>')
+ var desiredsequence = (['orange.dragenter','orange.dragover','html.dragenter','html.dragenter','orange.dragleave','html.dragover','blue.dragenter','html.dragover','orange.dragenter','html.dragleave','orange.dragover','orange.drop']).join('=>')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].textContent = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].textContent = 'FAIL, got: '+sequence+' instead of: '+desiredsequence;
+ }
+ };
+};
+ ]]></script>
+ </head>
+ <!--body-->
+
+ <div draggable="true"></div><div></div>
+ <div>&#160;</div>
+ <p>Drag the orange square onto the blue square, then back to the orange square, and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <!--/body-->
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/037-spec.xhtml b/testing/web-platform/tests/html/editing/dnd/events/037-spec.xhtml
new file mode 100644
index 0000000000..c4c76fe806
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/037-spec.xhtml
@@ -0,0 +1,88 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Drag and drop without cancelling dragenter and without body (spec compliant)</title>
+ <style type="text/css">
+head + div {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+head + div + div {
+ height: 100px;
+ width: 100px;
+ margin-left: 100px;
+ background: blue;
+ display: inline-block;
+}
+ </style>
+ <script type="text/javascript"><![CDATA[
+//Drag passes from orange to root element. Dragenter fires at root element.
+//Dragenter is not cancelled. Body does not exist. Dragenter fires at document.
+//Spec says the body (which does not exist) becomes current target element => null.
+//Drag passes from root element to blue. Dragenter fires at blue.
+//Dragenter is not cancelled. Body does not exist. Current target element is null.
+//Dragenter fires at document. Current target is set to null again.
+//Drag passes from blue to root element. Dragenter fires at root element.
+//Dragenter is not cancelled. Body does not exist. Current target element is null.
+//Dragenter is not cancelled. Body does not exist. Dragenter fires at document.
+//Current target is set to null. Yet again.
+//Drag passes from root element to orange. Dragenter fires at orange, and is cancelled.
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], sequence = [];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ orange.ondragenter = orange.ondrop = function (e) {
+ sequence[sequence.length] = 'orange.'+e.type;
+ e.preventDefault();
+ };
+ orange.ondragleave = function (e) {
+ sequence[sequence.length] = 'orange.dragleave';
+ };
+ orange.ondragover = function (e) {
+ if( sequence[sequence.length-1] != 'orange.dragover' ) {
+ sequence[sequence.length] = 'orange.dragover';
+ }
+ e.preventDefault();
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = blue.ondragover = blue.ondragleave = function (e) {
+ sequence[sequence.length] = 'blue.'+e.type;
+ };
+ document.documentElement.ondragenter = document.documentElement.ondragleave = function (e) {
+ if( e.target != this ) { return; }
+ sequence[sequence.length] = 'html.'+e.type;
+ };
+ document.documentElement.ondragover = function (e) {
+ if( e.target != this ) { return; }
+ if( sequence[sequence.length-1] != 'html.dragover' ) {
+ sequence[sequence.length] = 'html.dragover';
+ }
+ };
+ document.ondragenter = document.ondragleave = document.ondragover = document.ondragleave = function (e) {
+ if( e.target != this ) { return; }
+ sequence[sequence.length] = 'document.'+e.type;
+ };
+ orange.ondragend = function (e) {
+ sequence = sequence.join('=>')
+ var desiredsequence = (['orange.dragenter','orange.dragover','html.dragenter','document.dragenter','orange.dragleave','blue.dragenter','document.dragenter','document.dragenter','orange.dragenter','orange.dragover','orange.drop']).join('=>')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[2].textContent = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[2].textContent = 'FAIL, got: '+sequence+' instead of: '+desiredsequence;
+ }
+ };
+};
+ ]]></script>
+ </head>
+ <!--body-->
+
+ <div draggable="true"></div><div></div>
+ <div>&#160;</div>
+ <p>Drag the orange square onto the blue square, then back to the orange square, and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <!--/body-->
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/038-proposed.html b/testing/web-platform/tests/html/editing/dnd/events/038-proposed.html
new file mode 100644
index 0000000000..ee1cc8ed0e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/038-proposed.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Drag and drop without cancelling dragenter and without body or html</title>
+ <style type="text/css">
+body > div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+iframe {
+ height: 100px;
+ width: 100px;
+ margin-left: 100px;
+ display: inline-block;
+ border: none;
+}
+ </style>
+ <script type="text/javascript">
+//Drag passes from parent to blue. Dragenter fires at blue. Root element is deleted.
+//Dragenter is not cancelled. Body does not exist. Root element does not exist.
+//Current target element is set to null. Drag now points at unrendered document - null.
+//Current target element remains null.
+//Drag passes over parent to orange. Dragenter fires at orange, and is cancelled.
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], sequence = [];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','hello');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ orange.ondragenter = orange.ondrop = function (e) {
+ sequence[sequence.length] = 'orange.'+e.type;
+ e.preventDefault();
+ };
+ orange.ondragleave = function (e) {
+ sequence[sequence.length] = 'orange.dragleave';
+ };
+ orange.ondragover = function (e) {
+ if( sequence[sequence.length-1] != 'orange.dragover' ) {
+ sequence[sequence.length] = 'orange.dragover';
+ }
+ e.preventDefault();
+ };
+ var blue = document.getElementsByTagName('iframe')[0].contentDocument;
+ if( !blue.documentElement ) { blue.appendChild(blue.createElement('html')); }
+ blue.documentElement.style.margin = '0';
+ blue.documentElement.style.padding = '0';
+ if( !blue.body ) { blue.documentElement.appendChild(blue.createElement('body')); }
+ blue.body.style.margin = '0';
+ blue.body.style.padding = '0';
+ var bluediv = blue.body.appendChild(blue.createElement('div'));
+ bluediv.style.height = '100px';
+ bluediv.style.width = '100px';
+ bluediv.style.background = 'blue';
+ bluediv.ondragenter = bluediv.ondragover = function (e) {
+ sequence[sequence.length] = 'blue.'+e.type;
+ if( blue.documentElement ) { blue.removeChild(blue.documentElement); }
+ };
+ blue.ondragenter = blue.ondragover = blue.ondragleave = function (e) {
+ if( e.target != this ) { return; }
+ sequence[sequence.length] = 'bluedocument.'+e.type;
+ };
+ orange.ondragend = function (e) {
+ sequence = sequence.join('=&gt;')
+ var desiredsequence = (['orange.dragenter','orange.dragover','orange.dragleave','blue.dragenter','orange.dragenter','orange.dragover','orange.drop']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[1].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[1].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div><iframe src="about:blank"></iframe>
+ <div>&nbsp;</div>
+ <p>Drag the orange square onto the blue square, then back to the orange square, and release it.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/drag-event-div-manual.html b/testing/web-platform/tests/html/editing/dnd/events/drag-event-div-manual.html
new file mode 100644
index 0000000000..79c0c4332d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/drag-event-div-manual.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire drag event when dragging a div element</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire drag event when dragging a div element"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DragEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "drag";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DragEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire drag event when dragging a div element</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Click and drag the red box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If the user agent is still performing the previous iteration of the sequence (if any) when the next iteration becomes due, abort these steps for this iteration (effectively "skipping missed frames" of the drag-and-drop operation).
+ Fire a DND event named drag event at the source node. If this event is canceled, the user agent must set the current drag operation to "none" (no drag operation).
+ </p>
+ <div id="target" style="border:2px red solid; width:200px; height:50px" draggable="true"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/drag-event-manual.html b/testing/web-platform/tests/html/editing/dnd/events/drag-event-manual.html
new file mode 100644
index 0000000000..d278b864bb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/drag-event-manual.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire drag event during the drag and drop processing</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire drag event during the drag and drop processing"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DragEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "drag";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DragEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire drag event during the drag and drop processing</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Click and drag the blue image
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If the user agent is still performing the previous iteration of the sequence (if any) when the next iteration becomes due, abort these steps for this iteration (effectively "skipping missed frames" of the drag-and-drop operation).
+ Fire a DND event named drag event at the source node. If this event is canceled, the user agent must set the current drag operation to "none" (no drag operation).
+ </p>
+ <img src="/images/blue.png" style="width:200px; height:100px" draggable="true" id="target"/>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/dragend-event-manual.html b/testing/web-platform/tests/html/editing/dnd/events/dragend-event-manual.html
new file mode 100644
index 0000000000..8bfb1fb7b6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/dragend-event-manual.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire dragend event during the drag and drop processing</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire dragend event during the drag and drop processing"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DragendEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "dragend";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DragendEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire dragend event during the drag and drop processing</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Drag the blue image
+ <li> Drop it on the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If the drag operation failed or succeeded, fire a DND event named dragend at the source node.
+ </p>
+ <img src="/images/blue.png" style="width:200px; height:100px" draggable="true" id="target"/>
+ <br /><br />
+ <input type="text" style="border:2px green solid; width:200px; height:50px"></input>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/dragenter-event-manual.html b/testing/web-platform/tests/html/editing/dnd/events/dragenter-event-manual.html
new file mode 100644
index 0000000000..e81b32949c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/dragenter-event-manual.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire dragenter event during the drag and drop processing</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire dragenter event during the drag and drop processing"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DragenterEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "dragenter";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DragenterEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire dragenter event during the drag and drop processing</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Select the text inside the red box
+ <li> Drag it and enter the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If the user is indicating a different immediate user selection than during the last iteration (or if this is the first iteration), and if this immediate user selection is not the same as the current target element, then update the current target element as follows:
+ - If the new immediate user selection is null, Set the current target element to null also.
+ - If the new immediate user selection is in a non-DOM document or application, Set the current target element to the immediate user selection.
+ - Otherwise, Fire a DND event named dragenter at the immediate user selection.
+ </p>
+ <div style="border:2px red solid; width:200px; height:50px">SampleText</div>
+ <br /><br />
+ <input type="text" id="target" style="border:2px green solid; width:200px; height:50px"></input>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/dragleave-event-manual.html b/testing/web-platform/tests/html/editing/dnd/events/dragleave-event-manual.html
new file mode 100644
index 0000000000..f6a405915f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/dragleave-event-manual.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire dragleave event during the drag and drop processing</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire dragleave event during the drag and drop processing"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DragleaveEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "dragleave";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DragleaveEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire dragleave event during the drag and drop processing</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Select the text inside the red box
+ <li> Drag it over the blue box and drop it on the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If there is a change in the current target element, and if the previous target element was not null or a part of a non-DOM document, then fire a DND event named dragleave at the previous target element.
+ </p>
+ <div style="border:2px red solid; width:100px">SampleText</div>
+ <br />
+ <div id="target" style="border:2px blue solid; width:200px; height:50px"></div>
+ <br />
+ <input type="text" style="border:2px green solid; width:200px; height:50px"></input>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/dragover-event-manual.html b/testing/web-platform/tests/html/editing/dnd/events/dragover-event-manual.html
new file mode 100644
index 0000000000..f8d99241d5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/dragover-event-manual.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire dragover event during the drag and drop processing</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire dragover event during the drag and drop processing"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DragoverEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "dragover";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DragoverEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire dragover event during the drag and drop processing</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Select the text inside the red box.
+ <li> Drag it, hover over the green box and then release the mouse
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If the current target element is a DOM element, then fire a DND event named dragover at this current target element
+ </p>
+ <div style="border:2px red solid; width:100px">SampleText</div>
+ <br /><br />
+ <div id="target" style="border:2px green solid; width:200px; height:100px"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/dragstart-event-manual.html b/testing/web-platform/tests/html/editing/dnd/events/dragstart-event-manual.html
new file mode 100644
index 0000000000..20786648da
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/dragstart-event-manual.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire dragstart event during the drag and drop processing</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire dragstart event during the drag and drop processing"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DragstartEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "dragstart";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DragstartEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire dragstart event during the drag and drop processing</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Click and drag the red box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If it is an element that is being dragged, then set the drag data store elements list to contain just the source node.
+ Fire a DND event named dragstart at the source node.
+ </p>
+ <div id="target" style="border:2px red solid; width:200px; height:50px" draggable="true"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/drop-event-manual.html b/testing/web-platform/tests/html/editing/dnd/events/drop-event-manual.html
new file mode 100644
index 0000000000..2897bd5713
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/drop-event-manual.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Fire drop event during the drag and drop processing</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model"/>
+ <meta name="assert" content="Fire drop event during the drag and drop processing"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DropEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "drop";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DropEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Fire drop event during the drag and drop processing</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Select the text inside the red box
+ <li> Drag it and drop it on the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ </p>
+ <p>
+ If the drag operation was a success, if the current target element is a DOM element, fire a DND event named drop at it.
+ </p>
+ <div style="border:2px red solid; width:100px">SampleText</div>
+ <br /><br />
+ <input type="text" id="target" style="border:2px green solid; width:200px; height:50px"></input>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-1.html b/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-1.html
new file mode 100644
index 0000000000..4019610533
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-1.html
@@ -0,0 +1,205 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - event sequence for cross-document drag</title>
+<style type="text/css">
+ /* use margins instead of padding to make sure the body begins at the top of the page */
+ html, body {
+ margin: 0;
+ }
+ body {
+ padding: 116px 8px 8px;
+ }
+ #testhere div {
+ height: 100px;
+ width: 100px;
+ position: absolute;
+ top: 8px;
+ }
+ #orange {
+ background-color: orange;
+ left: 8px;
+ }
+ #fuchsia {
+ background-color: fuchsia;
+ left: 158px;
+ }
+</style>
+
+<script>
+parent.setup(function () {},{explicit_done:true,explicit_timeout:true});
+window.onload = function () {
+ var orange = document.querySelector('#orange')
+ var fuchsia = document.querySelector('#fuchsia')
+ var body = document.body;
+
+ orange.ondragstart = function (e) {
+ parent.events.push('doc1.orange.ondragstart');
+ e.dataTransfer.effectAllowed = 'all';
+ e.dataTransfer.setData('Text', 'foo');
+ };
+ orange.ondrag = function () { parent.events.push('doc1.orange.ondrag'); };
+ orange.ondragenter = function () { parent.events.push('doc1.orange.ondragenter'); };
+ orange.ondragover = function () { parent.events.push('doc1.orange.ondragover'); };
+ orange.ondrop = function () { parent.events.push('doc1.orange.ondrop'); return false; };
+ orange.ondragend = function () { parent.events.push('doc1.orange.ondragend'); setTimeout(finish,100); };
+ orange.onmousedown = function () { parent.events.push('doc1.orange.onmousedown'); };
+ orange.onmouseup = function () { parent.events.push('doc1.orange.onmouseup'); };
+
+ /* Events for the fuchsia box */
+ fuchsia.ondragstart = function () { parent.events.push('doc1.pink.ondragstart'); };
+ fuchsia.ondrag = function () { parent.events.push('doc1.pink.ondrag'); };
+ fuchsia.ondragenter = function () { parent.events.push('doc1.pink.ondragenter'); };
+ fuchsia.ondragover = function () { parent.events.push('doc1.pink.ondragover'); };
+ fuchsia.ondrop = function () { parent.events.push('doc1.pink.ondrop'); return false; };
+ fuchsia.ondragend = function () { parent.events.push('doc1.pink.ondragend'); };
+ fuchsia.onmousedown = function () { parent.events.push('doc1.pink.onmousedown'); };
+ fuchsia.onmouseup = function () { parent.events.push('doc1.pink.onmouseup'); };
+
+ /* Events for the page body */
+ body.ondragstart = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.ondragstart': 'doc1.bubble.ondragstart' ); };
+ body.ondrag = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.ondrag': 'doc1.bubble.ondrag' ); };
+ body.ondragenter = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.ondragenter': 'doc1.bubble.ondragenter' ); };
+ body.ondragover = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.ondragover': 'doc1.bubble.ondragover' ); };
+ body.ondrop = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.ondrop': 'doc1.bubble.ondrop' ); };
+ body.ondragend = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.ondragend': 'doc1.bubble.ondragend' ); };
+ body.onmousedown = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.onmousedown': 'doc1.bubble.onmousedown' ); };
+ body.onmouseup = function (e) { parent.events.push( ( e.target == body ) ? 'doc1.body.onmouseup': 'doc1.bubble.onmouseup' ); };
+
+ function finish(e) {
+ var i, evindex;
+ var events = parent.events.join('-');
+ /*
+ Normalise; reduce repeating event sequences to only 2 occurrences.
+ This makes the final event sequence predictable, no matter how many times the drag->dragover sequences repeat.
+ Two occurrances are kept in each case to allow testing to make sure the sequence really is repeating.
+ */
+ //spec compliant - div dragenter is not cancelled, so body dragenter fires and body becomes current target
+ //repeats while drag is over orange or fuchsia or the body
+ events = events.replace(/(-doc1\.orange\.ondrag-doc1\.bubble\.ondrag-doc1\.body\.ondragover){3,}/g,'$1$1');
+ events = events.replace(/(-doc1\.orange\.ondrag-doc1\.bubble\.ondrag-doc2\.body\.ondragover){3,}/g,'$1$1');
+ //repeats while dragging over yellow
+ events = events.replace(/(-doc1\.orange\.ondrag-doc1\.bubble\.ondrag-doc2\.yellow\.ondragover-doc2\.bubble\.ondragover){3,}/g,'$1$1');
+ //repeats while dragging over blue
+ events = events.replace(/(-doc1\.orange\.ondrag-doc1\.bubble\.ondrag-doc2\.blue\.ondragover-doc2\.bubble\.ondragover){3,}/g,'$1$1');
+ //non-spec-compliant repeats while dragging over orange
+ events = events.replace(/(-doc1\.orange\.ondrag-doc1\.bubble\.ondrag-doc1\.orange\.ondragover-doc1\.bubble\.ondragover){3,}/g,'$1$1');
+ //non-spec-compliant repeats while dragging over fuchsia
+ events = events.replace(/(-doc1\.orange\.ondrag-doc1\.bubble\.ondrag-doc1\.pink\.ondragover-doc1\.bubble\.ondragover){3,}/g,'$1$1');
+ events = events.split(/-/g);
+
+ parent.test(function () {
+ parent.assert_array_equals(events,
+
+ ['doc1.orange.onmousedown', //mouse down
+ 'doc1.bubble.onmousedown',
+
+ 'doc1.orange.ondragstart', //dragging begins
+ 'doc1.bubble.ondragstart',
+
+ 'doc1.orange.ondrag', //mouse is over orange
+ 'doc1.bubble.ondrag',
+ 'doc1.orange.ondragenter', //not cancelled
+ 'doc1.bubble.ondragenter',
+ 'doc1.body.ondragenter', //so body becomes current target, and the event fires there as well
+ 'doc1.body.ondragover',
+
+ 'doc1.orange.ondrag', //start repeating (some over orange, some over body)
+ 'doc1.bubble.ondrag',
+ 'doc1.body.ondragover',
+ 'doc1.orange.ondrag', //...twice to make sure it actually repeats
+ 'doc1.bubble.ondrag',
+ 'doc1.body.ondragover', //end repeating
+
+ 'doc1.orange.ondrag', //mouse moves over pink
+ 'doc1.bubble.ondrag',
+ 'doc1.pink.ondragenter', //not cancelled
+ 'doc1.bubble.ondragenter',
+ 'doc1.body.ondragover', //so body becomes current target, but since it was already the target, dragenter does not need to fire again
+
+ 'doc1.orange.ondrag', //start repeating (some over pink, some over body)
+ 'doc1.bubble.ondrag',
+ 'doc1.body.ondragover',
+ 'doc1.orange.ondrag', //...twice to make sure it actually repeats
+ 'doc1.bubble.ondrag',
+ 'doc1.body.ondragover', //end repeating
+
+ 'doc1.orange.ondrag', //mouse moves over second frame
+ 'doc1.bubble.ondrag',
+ 'doc2.body.ondragenter', //not cancelled
+ 'doc2.body.ondragenter', //so it fires again and sets body as current target
+ 'doc2.body.ondragover',
+
+ 'doc1.orange.ondrag', //start repeating (over second body)
+ 'doc1.bubble.ondrag',
+ 'doc2.body.ondragover',
+ 'doc1.orange.ondrag', //...twice to make sure it actually repeats
+ 'doc1.bubble.ondrag',
+ 'doc2.body.ondragover', //end repeating
+
+ 'doc1.orange.ondrag', //mouse moves over yellow
+ 'doc1.bubble.ondrag',
+ 'doc2.yellow.ondragenter',
+ 'doc2.bubble.ondragenter',
+ 'doc2.yellow.ondragover',
+ 'doc2.bubble.ondragover',
+
+ 'doc1.orange.ondrag', //start repeating (over yellow)
+ 'doc1.bubble.ondrag',
+ 'doc2.yellow.ondragover',
+ 'doc2.bubble.ondragover',
+ 'doc1.orange.ondrag', //...twice to make sure it actually repeats
+ 'doc1.bubble.ondrag',
+ 'doc2.yellow.ondragover',
+ 'doc2.bubble.ondragover', //end repeating
+
+ 'doc1.orange.ondrag', //mouse moves over body
+ 'doc1.bubble.ondrag',
+ 'doc2.body.ondragenter', //not cancelled
+ 'doc2.body.ondragenter', //so it fires again and sets body as current target
+ 'doc2.body.ondragover',
+
+ 'doc1.orange.ondrag', //start repeating (over body)
+ 'doc1.bubble.ondrag',
+ 'doc2.body.ondragover',
+ 'doc1.orange.ondrag', //...twice to make sure it actually repeats
+ 'doc1.bubble.ondrag',
+ 'doc2.body.ondragover', //end repeating
+
+ 'doc1.orange.ondrag', //mouse moves over blue
+ 'doc1.bubble.ondrag',
+ 'doc2.blue.ondragenter',
+ 'doc2.bubble.ondragenter',
+ 'doc2.blue.ondragover',
+ 'doc2.bubble.ondragover',
+
+ 'doc1.orange.ondrag', //start repeating (over blue)
+ 'doc1.bubble.ondrag',
+ 'doc2.blue.ondragover',
+ 'doc2.bubble.ondragover',
+ 'doc1.orange.ondrag', //...twice to make sure it actually repeats
+ 'doc1.bubble.ondrag',
+ 'doc2.blue.ondragover',
+ 'doc2.bubble.ondragover', //end repeating
+
+ 'doc2.blue.ondrop', //release
+ 'doc2.bubble.ondrop',
+ 'doc1.orange.ondragend',
+ 'doc1.bubble.ondragend']
+
+ );
+ }, 'Overall sequence');
+ var div = parent.document.createElement("div");
+ div.setAttribute("id", "log");
+ parent.document.documentElement.appendChild(div);
+ parent.done();
+ document.body.appendChild(parent.document.querySelector("div"));
+ }
+};
+</script>
+
+<div id="testhere">
+<div draggable='true' id='orange'></div>
+<div id='fuchsia'></div>
+</div>
+
+<p>If you have already clicked on this page, reload it.</p>
+<p>Use your pointing device to slowly drag the orange square over the pink square, then the grey square, then the yellow square, then the blue square, and release it over the blue square (make sure the mouse remains over each square for at least 1 second, and over the gaps between squares for at least 1 second). Fail if no new text appears below.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-2.html b/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-2.html
new file mode 100644
index 0000000000..343fc09543
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-HELPER-2.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - event sequence for cross-document drag</title>
+<style type="text/css">
+ /* use margins instead of padding to make sure the body begins at the top of the page */
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ }
+ #testhere div {
+ height: 100px;
+ width: 100px;
+ position: absolute;
+ top: 8px;
+ }
+ body::before {
+ height: 100px;
+ width: 100px;
+ position: absolute;
+ top: 8px;
+ left: 0px;
+ content: "";
+ background-color: silver;
+ }
+ #yellow {
+ background-color: yellow;
+ left: 150px;
+ }
+ #blue {
+ background-color: navy;
+ left: 300px;
+ }
+</style>
+
+<script>
+window.onload = function () {
+ var yellow = document.querySelector('#yellow')
+ var blue = document.querySelector('#blue')
+ var body = document.body;
+
+ /* Events for the fuchsia box */
+ yellow.ondragstart = function () { parent.events.push('doc2.yellow.ondragstart'); };
+ yellow.ondrag = function () { parent.events.push('doc2.yellow.ondrag'); };
+ yellow.ondragenter = function () { parent.events.push('doc2.yellow.ondragenter'); return false; };
+ yellow.ondragover = function () { parent.events.push('doc2.yellow.ondragover'); return false; };
+ yellow.ondrop = function () { parent.events.push('doc2.yellow.ondrop'); return false; };
+ yellow.ondragend = function () { parent.events.push('doc2.yellow.ondragend'); };
+ yellow.onmousedown = function () { parent.events.push('doc2.yellow.onmousedown'); };
+ yellow.onmouseup = function () { parent.events.push('doc2.yellow.onmouseup'); };
+
+ /* Events for the blue box (droppable) */
+ blue.ondragstart = function () { parent.events.push('doc2.blue.ondragstart'); };
+ blue.ondrag = function () { parent.events.push('doc2.blue.ondrag'); };
+ blue.ondragenter = function () { parent.events.push('doc2.blue.ondragenter'); return false; };
+ blue.ondragover = function () { parent.events.push('doc2.blue.ondragover'); return false; };
+ blue.ondrop = function () { parent.events.push('doc2.blue.ondrop'); return false; };
+ blue.ondragend = function () { parent.events.push('doc2.blue.ondragend'); };
+ blue.onmousedown = function () { parent.events.push('doc2.blue.onmousedown'); };
+ blue.onmouseup = function () { parent.events.push('doc2.blue.onmouseup'); };
+
+ /* Events for the page body */
+ body.ondragstart = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.ondragstart': 'doc2.bubble.ondragstart' ); };
+ body.ondrag = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.ondrag': 'doc2.bubble.ondrag' ); };
+ body.ondragenter = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.ondragenter': 'doc2.bubble.ondragenter' ); };
+ body.ondragover = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.ondragover': 'doc2.bubble.ondragover' ); };
+ body.ondrop = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.ondrop': 'doc2.bubble.ondrop' ); };
+ body.ondragend = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.ondragend': 'doc2.bubble.ondragend' ); };
+ body.onmousedown = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.onmousedown': 'doc2.bubble.onmousedown' ); };
+ body.onmouseup = function (e) { parent.events.push( ( e.target == body ) ? 'doc2.body.onmouseup': 'doc2.bubble.onmouseup' ); };
+
+};
+</script>
+
+<div id="testhere">
+<div id='yellow'></div>
+<div id='blue'></div>
+</div>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-manual.html b/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-manual.html
new file mode 100644
index 0000000000..9428a87760
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/events-cross-document-suite-manual.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - event sequence for cross-document drag</title>
+<script type="text/javascript" src="/resources/testharness.js"></script>
+<script type="text/javascript" src="/resources/testharnessreport.js"></script>
+<script>
+var events = new Array();
+</script>
+
+<frameset cols="308,*" frameborder="no" border="0">
+ <frame src="events-cross-document-suite-HELPER-1.html">
+ <frame src="events-cross-document-suite-HELPER-2.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/events-file-suite-manual.html b/testing/web-platform/tests/html/editing/dnd/events/events-file-suite-manual.html
new file mode 100644
index 0000000000..22a66f5e9a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/events-file-suite-manual.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - event sequence for file drops</title>
+<script type="text/javascript" src="/resources/testharness.js"></script>
+<script type="text/javascript" src="/resources/testharnessreport.js"></script>
+<style type="text/css">
+ /* use margins instead of padding to make sure the body begins at the top of the page */
+ html, body {
+ margin: 0;
+ }
+ body {
+ padding: 116px 8px 8px;
+ }
+ body::before {
+ height: 108px;
+ width: 108px;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ content: "";
+ background-color: orange;
+ }
+ #testhere div {
+ height: 100px;
+ width: 100px;
+ position: absolute;
+ top: 8px;
+ }
+ #fuchsia {
+ background-color: fuchsia;
+ left: 158px;
+ }
+ #yellow {
+ background-color: yellow;
+ left: 308px;
+ }
+ #blue {
+ background-color: navy;
+ left: 458px;
+ }
+</style>
+
+<script>
+setup(function () {},{explicit_done:true,explicit_timeout:true});
+window.onload = function () {
+ var fuchsia = document.querySelector('#fuchsia')
+ var yellow = document.querySelector('#yellow')
+ var blue = document.querySelector('#blue')
+ var body = document.body;
+
+ var events = new Array
+
+ /* Events for the fuchsia box */
+ fuchsia.ondragstart = function () { events.push('pink.ondragstart'); };
+ fuchsia.ondrag = function () { events.push('pink.ondrag'); };
+ fuchsia.ondragenter = function () { events.push('pink.ondragenter'); };
+ fuchsia.ondragover = function () { events.push('pink.ondragover'); };
+ fuchsia.ondragleave = function () { events.push('pink.ondragleave'); };
+ fuchsia.ondrop = function () { events.push('pink.ondrop'); return false; };
+ fuchsia.ondragend = function () { events.push('pink.ondragend'); };
+ fuchsia.onmousedown = function () { events.push('pink.onmousedown'); };
+ fuchsia.onmouseup = function () { events.push('pink.onmouseup'); };
+
+ /* Events for the fuchsia box */
+ yellow.ondragstart = function () { events.push('yellow.ondragstart'); };
+ yellow.ondrag = function () { events.push('yellow.ondrag'); };
+ yellow.ondragenter = function () { events.push('yellow.ondragenter'); return false; };
+ yellow.ondragover = function () { events.push('yellow.ondragover'); return false; };
+ yellow.ondragleave = function () { events.push('yellow.ondragleave'); };
+ yellow.ondrop = function () { events.push('yellow.ondrop'); return false; };
+ yellow.ondragend = function () { events.push('yellow.ondragend'); };
+ yellow.onmousedown = function () { events.push('yellow.onmousedown'); };
+ yellow.onmouseup = function () { events.push('yellow.onmouseup'); };
+
+ /* Events for the blue box (droppable) */
+ blue.ondragstart = function () { events.push('blue.ondragstart'); };
+ blue.ondrag = function () { events.push('blue.ondrag'); };
+ blue.ondragenter = function () { events.push('blue.ondragenter'); return false; };
+ blue.ondragover = function () { events.push('blue.ondragover'); return false; };
+ blue.ondragleave = function () { events.push('blue.ondragleave'); };
+ blue.ondrop = function () { events.push('blue.ondrop'); return false; };
+ blue.ondragend = function () { events.push('blue.ondragend'); };
+ blue.onmousedown = function () { events.push('blue.onmousedown'); };
+ blue.onmouseup = function () { events.push('blue.onmouseup'); };
+
+ /* Events for the page body */
+ body.ondragstart = function (e) { events.push( ( e.target == body ) ? 'body.ondragstart': 'bubble.ondragstart' ); };
+ body.ondrag = function (e) { events.push( ( e.target == body ) ? 'body.ondrag': 'bubble.ondrag' ); };
+ body.ondragenter = function (e) { events.push( ( e.target == body ) ? 'body.ondragenter': 'bubble.ondragenter' ); };
+ body.ondragover = function (e) { events.push( ( e.target == body ) ? 'body.ondragover': 'bubble.ondragover' ); };
+ body.ondragleave = function (e) { events.push( ( e.target == body ) ? 'body.ondragleave': 'bubble.ondragleave' ); };
+ body.ondrop = function (e) { events.push( ( e.target == body ) ? 'body.ondrop': 'bubble.ondrop' ); setTimeout(finish,200); };
+ body.ondragend = function (e) { events.push( ( e.target == body ) ? 'body.ondragend': 'bubble.ondragend' ); };
+ body.onmousedown = function (e) { events.push( ( e.target == body ) ? 'body.onmousedown': 'bubble.onmousedown' ); };
+ body.onmouseup = function (e) { events.push( ( e.target == body ) ? 'body.onmouseup': 'bubble.onmouseup' ); };
+
+ function finish(e) {
+ var i, evindex;
+ events = events.join('-');
+ /*
+ Normalise; reduce repeating event sequences to only 2 occurrences.
+ This makes the final event sequence predictable, no matter how many times the drag->dragover sequences repeat.
+ Two occurrances are kept in each case to allow testing to make sure the sequence really is repeating.
+ */
+ //spec compliant - div dragenter is not cancelled, so body dragenter fires and body becomes current target
+ //repeats while drag is over fuchsia or the body
+ events = events.replace(/(-body\.ondragover){3,}/g,'$1$1');
+ //repeats while dragging over yellow
+ events = events.replace(/(-yellow\.ondragover-bubble\.ondragover){3,}/g,'$1$1');
+ //repeats while dragging over blue
+ events = events.replace(/(-blue\.ondragover-bubble\.ondragover){3,}/g,'$1$1');
+ //non-spec-compliant repeats while dragging over fuchsia
+ events = events.replace(/(-pink\.ondragover-bubble\.ondragover){3,}/g,'$1$1');
+ events = events.split(/-/g);
+
+ test(function () {
+ assert_array_equals(events,
+
+ [/* 1 */ 'body.ondragenter', //mouse moves over body, which does not cancel event
+ /* 2 */ 'body.ondragenter', //so it fires again and sets body as current target
+
+ /* 3 */ 'body.ondragover', //start repeating over body
+ /* 4 */ 'body.ondragover', //...twice to make sure it actually repeats
+
+ /* 5 */ 'pink.ondragenter', //mouse moves over pink - not cancelled
+ /* 6 */ 'bubble.ondragenter',
+
+ /* 7 */ 'body.ondragover', //so body becomes current target, but since it was already the target, dragenter does not need to fire again
+ /* 8 */ 'body.ondragover', //...twice to make sure it actually repeats
+
+ /* 9 */ 'yellow.ondragenter', //mouse moves over yellow
+ /* 10 */ 'bubble.ondragenter',
+ /* 11 */ 'body.ondragleave',
+
+ /* 12 */ 'yellow.ondragover', //start repeating (over yellow)
+ /* 13 */ 'bubble.ondragover',
+ /* 14 */ 'yellow.ondragover', //...twice to make sure it actually repeats
+ /* 15 */ 'bubble.ondragover', //end repeating
+
+ /* 16 */ 'body.ondragenter', //mouse moves over body, not cancelled
+ /* 17 */ 'body.ondragenter', //so it fires again and sets body as current target
+ /* 18 */ 'yellow.ondragleave',
+ /* 19 */ 'bubble.ondragleave',
+
+ /* 20 */ 'body.ondragover', //start repeating (over body)
+ /* 21 */ 'body.ondragover', //...twice to make sure it actually repeats
+
+ /* 22 */ 'blue.ondragenter', //mouse moves over blue
+ /* 23 */ 'bubble.ondragenter',
+ /* 24 */ 'body.ondragleave',
+
+ /* 25 */ 'blue.ondragover', //start repeating (over blue)
+ /* 26 */ 'bubble.ondragover',
+ /* 27 */ 'blue.ondragover', //...twice to make sure it actually repeats
+ /* 28 */ 'bubble.ondragover',
+
+ /* 29 */ 'blue.ondrop', //release
+ /* 30 */ 'bubble.ondrop']
+
+ );
+ }, 'Overall sequence');
+
+ done();
+ }
+};
+</script>
+
+<div id="testhere">
+<div id='fuchsia'></div>
+<div id='yellow'></div>
+<div id='blue'></div>
+</div>
+
+<p>If you have already clicked on this page, reload it.</p>
+<p>Use your pointing device to slowly drag a file from your system's file manager, over the orange square (ensure that this is the first part of the page that you drag the file over, not an otherwise blank part of the page), then the pink square, then the yellow square, then the blue square, and release it over the blue square (make sure the mouse remains over each square for at least 1 second, and over the gaps between squares for at least 1 second). If a prompt appears, accept it. Fail if no new text appears below.</p>
+
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-001-manual.html b/testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-001-manual.html
new file mode 100644
index 0000000000..0315ccdc86
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-001-manual.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>drag &amp; drop – events should not fire with non-draggable elements – 001</title>
+<style type="text/css">
+ div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ div + p {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+window.onload = function() {
+
+ var elem = document.getElementsByTagName('div')[0];
+ var pass = true;
+
+ function fail() {
+ pass = false;
+ }
+
+ elem.addEventListener('drag',fail,false);
+ elem.addEventListener('dragend',fail,false);
+ elem.addEventListener('dragenter',fail,false);
+ elem.addEventListener('dragleave',fail,false);
+ elem.addEventListener('dragover',fail,false);
+ elem.addEventListener('dragstart',fail,false);
+ elem.addEventListener('drop',fail,false);
+
+ elem.ondrag = fail;
+ elem.ondragend = fail;
+ elem.ondragenter = fail;
+ elem.ondragleave = fail;
+ elem.ondragover = fail;
+ elem.ondragstart = fail;
+ elem.ondrop = fail;
+
+ elem.onmouseup = function () {
+ setTimeout(function () {
+ if (pass == true) {
+ document.body.innerHTML = 'PASS';
+ } else {
+ document.body.innerHTML = 'FAIL';
+ }
+ }, 100 );
+ };
+
+}
+</script>
+
+<div></div>
+
+<p>Click once on the orange box above, without moving the mouse while
+clicking. The word &quot;PASS&quot; should appear.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-002-manual.html b/testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-002-manual.html
new file mode 100644
index 0000000000..c890cb482e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/events-non-draggable-002-manual.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>drag &amp; drop – events should not fire with non-draggable elements – 002</title>
+<style type="text/css">
+ div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ div + div {
+ background-color: navy;
+ left: 250px;
+ }
+
+ div + p {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+window.onload = function() {
+
+ var orange = document.getElementsByTagName('div')[0];
+ var blue = document.getElementsByTagName('div')[1];
+ var body = document.body;
+
+ var pass = true;
+
+ function fail() {
+ pass = false;
+ }
+
+ body.addEventListener('drag',fail,false);
+ body.addEventListener('dragend',fail,false);
+ body.addEventListener('dragenter',fail,false);
+ body.addEventListener('dragleave',fail,false);
+ body.addEventListener('dragover',fail,false);
+ body.addEventListener('dragstart',fail,false);
+ body.addEventListener('drop',fail,false);
+
+ body.ondrag = fail;
+ body.ondragend = fail;
+ body.ondragenter = fail;
+ body.ondragleave = fail;
+ body.ondragover = fail;
+ body.ondragstart = fail;
+ body.ondrop = fail;
+
+ orange.addEventListener('drag',fail,false);
+ orange.addEventListener('dragend',fail,false);
+ orange.addEventListener('dragenter',fail,false);
+ orange.addEventListener('dragleave',fail,false);
+ orange.addEventListener('dragover',fail,false);
+ orange.addEventListener('dragstart',fail,false);
+ orange.addEventListener('drop',fail,false);
+
+ orange.ondrag = fail;
+ orange.ondragend = fail;
+ orange.ondragenter = fail;
+ orange.ondragleave = fail;
+ orange.ondragover = fail;
+ orange.ondragstart = fail;
+ orange.ondrop = fail;
+
+ blue.addEventListener('drag',fail,false);
+ blue.addEventListener('dragend',fail,false);
+ blue.addEventListener('dragenter',fail,false);
+ blue.addEventListener('dragleave',fail,false);
+ blue.addEventListener('dragover',fail,false);
+ blue.addEventListener('dragstart',fail,false);
+ blue.addEventListener('drop',fail,false);
+
+ blue.ondrag = fail;
+ blue.ondragend = fail;
+ blue.ondragenter = fail;
+ blue.ondragleave = fail;
+ blue.ondragover = fail;
+ blue.ondragstart = fail;
+ blue.ondrop = fail;
+
+ body.onmouseup = function () {
+ setTimeout(function () {
+ if (pass == true) {
+ document.body.innerHTML = 'PASS';
+ } else {
+ document.body.innerHTML = 'FAIL';
+ }
+ }, 100 );
+ };
+
+}
+</script>
+
+<div></div>
+<div></div>
+
+<p>Use your pointing device to drag from the orange box to the blue box. The
+word &quot;PASS&quot; should appear.
diff --git a/testing/web-platform/tests/html/editing/dnd/events/events-suite-manual.html b/testing/web-platform/tests/html/editing/dnd/events/events-suite-manual.html
new file mode 100644
index 0000000000..16c6583dc8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/events-suite-manual.html
@@ -0,0 +1,371 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - event sequence for draggable elements</title>
+<script type="text/javascript" src="/resources/testharness.js"></script>
+<script type="text/javascript" src="/resources/testharnessreport.js"></script>
+<style type="text/css">
+ /* use margins instead of padding to make sure the body begins at the top of the page */
+ html, body {
+ margin: 0;
+ }
+ body {
+ padding: 116px 8px 8px;
+ }
+ #testhere div {
+ height: 100px;
+ width: 100px;
+ position: absolute;
+ top: 8px;
+ }
+ #orange {
+ background-color: orange;
+ left: 8px;
+ }
+ #fuchsia {
+ background-color: fuchsia;
+ left: 158px;
+ }
+ #yellow {
+ background-color: yellow;
+ left: 308px;
+ }
+ #blue {
+ background-color: navy;
+ left: 458px;
+ }
+</style>
+
+<script>
+setup(function () {},{explicit_done:true,explicit_timeout:true});
+window.onload = function () {
+ var orange = document.querySelector('#orange')
+ var fuchsia = document.querySelector('#fuchsia')
+ var yellow = document.querySelector('#yellow')
+ var blue = document.querySelector('#blue')
+ var body = document.body;
+
+ var events = new Array
+
+ orange.ondragstart = function (e) {
+ events.push('orange.ondragstart');
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'foo');
+ };
+ orange.ondrag = function () { events.push('orange.ondrag'); };
+ orange.ondragenter = function () { events.push('orange.ondragenter'); };
+ orange.ondragover = function () { events.push('orange.ondragover'); };
+ orange.ondragleave = function () { events.push('orange.ondragleave'); };
+ orange.ondrop = function () { events.push('orange.ondrop'); return false; };
+ orange.ondragend = function () { events.push('orange.ondragend'); };
+ orange.onmousedown = function () { events.push('orange.onmousedown'); };
+ orange.onmouseup = function () { events.push('orange.onmouseup'); };
+
+ /* Events for the fuchsia box */
+ fuchsia.ondragstart = function () { events.push('pink.ondragstart'); };
+ fuchsia.ondrag = function () { events.push('pink.ondrag'); };
+ fuchsia.ondragenter = function () { events.push('pink.ondragenter'); };
+ fuchsia.ondragover = function () { events.push('pink.ondragover'); };
+ fuchsia.ondragleave = function () { events.push('pink.ondragleave'); };
+ fuchsia.ondrop = function () { events.push('pink.ondrop'); return false; };
+ fuchsia.ondragend = function () { events.push('pink.ondragend'); };
+ fuchsia.onmousedown = function () { events.push('pink.onmousedown'); };
+ fuchsia.onmouseup = function () { events.push('pink.onmouseup'); };
+
+ /* Events for the fuchsia box */
+ yellow.ondragstart = function () { events.push('yellow.ondragstart'); };
+ yellow.ondrag = function () { events.push('yellow.ondrag'); };
+ yellow.ondragenter = function () { events.push('yellow.ondragenter'); return false; };
+ yellow.ondragover = function () { events.push('yellow.ondragover'); return false; };
+ yellow.ondragleave = function () { events.push('yellow.ondragleave'); };
+ yellow.ondrop = function () { events.push('yellow.ondrop'); return false; };
+ yellow.ondragend = function () { events.push('yellow.ondragend'); };
+ yellow.onmousedown = function () { events.push('yellow.onmousedown'); };
+ yellow.onmouseup = function () { events.push('yellow.onmouseup'); };
+
+ /* Events for the blue box (droppable) */
+ blue.ondragstart = function () { events.push('blue.ondragstart'); };
+ blue.ondrag = function () { events.push('blue.ondrag'); };
+ blue.ondragenter = function () { events.push('blue.ondragenter'); return false; };
+ blue.ondragover = function () { events.push('blue.ondragover'); return false; };
+ blue.ondragleave = function () { events.push('blue.ondragleave'); };
+ blue.ondrop = function () { events.push('blue.ondrop'); return false; };
+ blue.ondragend = function () { events.push('blue.ondragend'); };
+ blue.onmousedown = function () { events.push('blue.onmousedown'); };
+ blue.onmouseup = function () { events.push('blue.onmouseup'); };
+
+ /* Events for the page body */
+ body.ondragstart = function (e) { events.push( ( e.target == body ) ? 'body.ondragstart': 'bubble.ondragstart' ); };
+ body.ondrag = function (e) { events.push( ( e.target == body ) ? 'body.ondrag': 'bubble.ondrag' ); };
+ body.ondragenter = function (e) { events.push( ( e.target == body ) ? 'body.ondragenter': 'bubble.ondragenter' ); };
+ body.ondragover = function (e) { events.push( ( e.target == body ) ? 'body.ondragover': 'bubble.ondragover' ); };
+ body.ondragleave = function (e) { events.push( ( e.target == body ) ? 'body.ondragleave': 'bubble.ondragleave' ); };
+ body.ondrop = function (e) { events.push( ( e.target == body ) ? 'body.ondrop': 'bubble.ondrop' ); };
+ body.ondragend = function (e) { events.push( ( e.target == body ) ? 'body.ondragend': 'bubble.ondragend' ); setTimeout(finish,100); };
+ body.onmousedown = function (e) { events.push( ( e.target == body ) ? 'body.onmousedown': 'bubble.onmousedown' ); };
+ body.onmouseup = function (e) { events.push( ( e.target == body ) ? 'body.onmouseup': 'bubble.onmouseup' ); };
+
+ function finish(e) {
+ var i, evindex;
+ events = events.join('-');
+ /*
+ Normalise; reduce repeating event sequences to only 2 occurrences.
+ This makes the final event sequence predictable, no matter how many times the drag->dragover sequences repeat.
+ Two occurrances are kept in each case to allow testing to make sure the sequence really is repeating.
+ */
+ //spec compliant - div dragenter is not cancelled, so body dragenter fires and body becomes current target
+ //repeats while drag is over orange or fuchsia or the body
+ events = events.replace(/(-orange\.ondrag-bubble\.ondrag-body\.ondragover){3,}/g,'$1$1');
+ //repeats while dragging over yellow
+ events = events.replace(/(-orange\.ondrag-bubble\.ondrag-yellow\.ondragover-bubble\.ondragover){3,}/g,'$1$1');
+ //repeats while dragging over blue
+ events = events.replace(/(-orange\.ondrag-bubble\.ondrag-blue\.ondragover-bubble\.ondragover){3,}/g,'$1$1');
+ //non-spec-compliant repeats while dragging over orange
+ events = events.replace(/(-orange\.ondrag-bubble\.ondrag-orange\.ondragover-bubble\.ondragover){3,}/g,'$1$1');
+ //non-spec-compliant repeats while dragging over fuchsia
+ events = events.replace(/(-orange\.ondrag-bubble\.ondrag-pink\.ondragover-bubble\.ondragover){3,}/g,'$1$1');
+ events = events.split(/-/g);
+
+ test(function () {
+ assert_array_equals(events,
+
+ [/* 1 */ 'orange.onmousedown', //mouse down
+ /* 2 */ 'bubble.onmousedown',
+
+ /* 3 */ 'orange.ondragstart', //dragging begins
+ /* 4 */ 'bubble.ondragstart',
+
+ /* 5 */ 'orange.ondrag', //mouse is over orange
+ /* 6 */ 'bubble.ondrag',
+ /* 7 */ 'orange.ondragenter', //not cancelled
+ /* 8 */ 'bubble.ondragenter',
+ /* 9 */ 'body.ondragenter', //so body becomes current target, and the event fires there as well
+ /* 10 */ 'body.ondragover',
+
+ /* 11 */ 'orange.ondrag', //start repeating (some over orange, some over body)
+ /* 12 */ 'bubble.ondrag',
+ /* 13 */ 'body.ondragover',
+ /* 14 */ 'orange.ondrag', //...twice to make sure it actually repeats
+ /* 15 */ 'bubble.ondrag',
+ /* 16 */ 'body.ondragover', //end repeating
+
+ /* 17 */ 'orange.ondrag', //mouse moves over pink
+ /* 18 */ 'bubble.ondrag',
+ /* 19 */ 'pink.ondragenter', //not cancelled
+ /* 20 */ 'bubble.ondragenter',
+ /* 21 */ 'body.ondragover', //so body becomes current target, but since it was already the target, dragenter does not need to fire again
+
+ /* 22 */ 'orange.ondrag', //start repeating (some over pink, some over body)
+ /* 23 */ 'bubble.ondrag',
+ /* 24 */ 'body.ondragover',
+ /* 25 */ 'orange.ondrag', //...twice to make sure it actually repeats
+ /* 26 */ 'bubble.ondrag',
+ /* 27 */ 'body.ondragover', //end repeating
+
+ /* 28 */ 'orange.ondrag', //mouse moves over yellow
+ /* 29 */ 'bubble.ondrag',
+ /* 30 */ 'yellow.ondragenter',
+ /* 31 */ 'bubble.ondragenter',
+ /* 32 */ 'body.ondragleave',
+ /* 33 */ 'yellow.ondragover',
+ /* 34 */ 'bubble.ondragover',
+
+ /* 35 */ 'orange.ondrag', //start repeating (over yellow)
+ /* 36 */ 'bubble.ondrag',
+ /* 37 */ 'yellow.ondragover',
+ /* 38 */ 'bubble.ondragover',
+ /* 39 */ 'orange.ondrag', //...twice to make sure it actually repeats
+ /* 40 */ 'bubble.ondrag',
+ /* 41 */ 'yellow.ondragover',
+ /* 42 */ 'bubble.ondragover', //end repeating
+
+ /* 43 */ 'orange.ondrag', //mouse moves over body
+ /* 44 */ 'bubble.ondrag',
+ /* 45 */ 'body.ondragenter', //not cancelled
+ /* 46 */ 'body.ondragenter', //so it fires again and sets body as current target
+ /* 47 */ 'yellow.ondragleave',
+ /* 48 */ 'bubble.ondragleave',
+ /* 49 */ 'body.ondragover',
+
+ /* 50 */ 'orange.ondrag', //start repeating (over body)
+ /* 51 */ 'bubble.ondrag',
+ /* 52 */ 'body.ondragover',
+ /* 53 */ 'orange.ondrag', //...twice to make sure it actually repeats
+ /* 54 */ 'bubble.ondrag',
+ /* 55 */ 'body.ondragover', //end repeating
+
+ /* 56 */ 'orange.ondrag', //mouse moves over blue
+ /* 57 */ 'bubble.ondrag',
+ /* 58 */ 'blue.ondragenter',
+ /* 59 */ 'bubble.ondragenter',
+ /* 60 */ 'body.ondragleave',
+ /* 61 */ 'blue.ondragover',
+ /* 62 */ 'bubble.ondragover',
+
+ /* 63 */ 'orange.ondrag', //start repeating (over blue)
+ /* 64 */ 'bubble.ondrag',
+ /* 65 */ 'blue.ondragover',
+ /* 66 */ 'bubble.ondragover',
+ /* 67 */ 'orange.ondrag', //...twice to make sure it actually repeats
+ /* 68 */ 'bubble.ondrag',
+ /* 69 */ 'blue.ondragover',
+ /* 70 */ 'bubble.ondragover', //end repeating
+
+ /* 71 */ 'blue.ondrop', //release
+ /* 72 */ 'bubble.ondrop',
+ /* 73 */ 'orange.ondragend',
+ /* 74 */ 'bubble.ondragend']
+
+ );
+ }, 'Overall sequence');
+
+ /* ondragstart */
+ test(function () { assert_true( events.indexOf('orange.ondragstart') != -1 ); }, "orange.ondragstart should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'orange.ondragstart') return e; }).length, 1); }, "orange.ondragstart should fire 1 time");
+ test(function () { assert_equals( events[2], 'orange.ondragstart' ); }, "orange.ondragstart should be event handler #3");
+ test(function () { assert_equals( events.indexOf('pink.ondragstart'), -1 ); }, "pink.ondragstart should not fire");
+ test(function () { assert_equals( events.indexOf('yellow.ondragstart'), -1 ); }, "yellow.ondragstart should not fire");
+ test(function () { assert_equals( events.indexOf('blue.ondragstart'), -1 ); }, "blue.ondragstart should not fire");
+ test(function () { assert_equals( events.indexOf('body.ondragstart'), -1 ); }, "ondragstart should not fire at the body");
+ test(function () { assert_true( events.indexOf('bubble.ondragstart') != -1 ); }, "ondragstart should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.ondragstart') return e; }).length, 1); }, "ondragstart should only bubble to body 1 time");
+ test(function () { assert_equals( events[3], 'bubble.ondragstart' ); }, "ondragstart should bubble to body as event handler #4");
+
+ /* ondrag */
+ test(function () { assert_true( events.indexOf('orange.ondrag') != -1 ); }, "orange.ondrag should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'orange.ondrag') return e; }).length, 15); }, "orange.ondrag should fire 15 times");
+ for( var i = 0, evindex = [4,10,13,16,21,24,27,34,38,42,49,52,55,62,66]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'orange.ondrag' ); }, "orange.ondrag should be event handler #"+(evindex[i]+1));
+ }
+ test(function () { assert_equals( events.indexOf('pink.ondrag'), -1 ); }, "pink.ondrag should not fire");
+ test(function () { assert_equals( events.indexOf('yellow.ondrag'), -1 ); }, "yellow.ondrag should not fire");
+ test(function () { assert_equals( events.indexOf('blue.ondrag'), -1 ); }, "blue.ondrag should not fire");
+ test(function () { assert_equals( events.indexOf('body.ondrag'), -1 ); }, "ondrag should not fire at the body");
+ test(function () { assert_true( events.indexOf('bubble.ondrag') != -1 ); }, "ondrag should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.ondrag') return e; }).length, 15); }, "ondrag should bubble to body 15 times");
+ for( var i = 0, evindex = [5,11,14,17,22,25,28,35,39,43,50,53,56,63,67]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'bubble.ondrag' ); }, "ondrag should bubble to body as event handler #"+(evindex[i]+1));
+ }
+
+ /* ondragenter */
+ test(function () { assert_true( events.indexOf('orange.ondragenter') != -1 ); }, "orange.ondragenter should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'orange.ondragenter') return e; }).length, 1); }, "orange.ondragenter should fire 1 time");
+ test(function () { assert_equals( events[6], 'orange.ondragenter' ); }, "orange.ondragenter should be event handler #7");
+ test(function () { assert_true( events.indexOf('pink.ondragenter') != -1 ); }, "pink.ondragenter should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'pink.ondragenter') return e; }).length, 1); }, "pink.ondragenter should fire 1 time");
+ test(function () { assert_equals( events[18], 'pink.ondragenter' ); }, "pink.ondragenter should be event handler #19");
+ test(function () { assert_true( events.indexOf('yellow.ondragenter') != -1 ); }, "yellow.ondragenter should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'yellow.ondragenter') return e; }).length, 1); }, "yellow.ondragenter should fire 1 time");
+ test(function () { assert_equals( events[29], 'yellow.ondragenter' ); }, "yellow.ondragenter should be event handler #30");
+ test(function () { assert_true( events.indexOf('blue.ondragenter') != -1 ); }, "blue.ondragenter should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'blue.ondragenter') return e; }).length, 1); }, "blue.ondragenter should fire 1 time");
+ test(function () { assert_equals( events[57], 'blue.ondragenter' ); }, "blue.ondragenter should be event handler #58");
+ test(function () { assert_true( events.indexOf('body.ondragenter') != -1 ); }, "ondragenter should fire at body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'body.ondragenter') return e; }).length, 3); }, "ondragenter should fire at body 2 times");
+ for( var i = 0, evindex = [8,44,45]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'body.ondragenter' ); }, "ondragenter should fire at body as event handler #"+(evindex[i]+1));
+ }
+ test(function () { assert_true( events.indexOf('bubble.ondragenter') != -1 ); }, "ondragenter should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.ondragenter') return e; }).length, 4); }, "ondragenter should bubble to body 4 times");
+ for( var i = 0, evindex = [7,19,30,58]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'bubble.ondragenter' ); }, "ondragenter should bubble to body as event handler #"+(evindex[i]+1));
+ }
+
+ /* ondragover */
+ test(function () { assert_equals( events.indexOf('orange.ondragover'), -1 ); }, "orange.ondragover should not fire");
+ test(function () { assert_equals( events.indexOf('pink.ondragover'), -1 ); }, "pink.ondragover should not fire");
+ test(function () { assert_true( events.indexOf('yellow.ondragover') != -1 ); }, "yellow.ondragover should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'yellow.ondragover') return e; }).length, 3); }, "yellow.ondragover should fire 3 times");
+ for( var i = 0, evindex = [32,36,40]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'yellow.ondragover' ); }, "yellow.ondragover should be event handler #"+(evindex[i]+1));
+ }
+ test(function () { assert_true( events.indexOf('blue.ondragover') != -1 ); }, "blue.ondragover should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'blue.ondragover') return e; }).length, 3); }, "blue.ondragover should fire 9 times");
+ for( var i = 0, evindex = [60,64,68]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'blue.ondragover' ); }, "blue.ondragover should be event handler #"+(evindex[i]+1));
+ }
+ test(function () { assert_true( events.indexOf('body.ondragover') != -1 ); }, "ondragover should fire at body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'body.ondragover') return e; }).length, 9); }, "ondragover should fire at body 2 times");
+ for( var i = 0, evindex = [9,12,15,20,23,26,48,51,54]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'body.ondragover' ); }, "ondragover should fire at body as event handler #"+(evindex[i]+1));
+ }
+ test(function () { assert_true( events.indexOf('bubble.ondragover') != -1 ); }, "ondragover should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.ondragover') return e; }).length, 6); }, "ondragover should bubble to body 6 times");
+ for( var i = 0, evindex = [33,37,41,61,65,69]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'bubble.ondragover' ); }, "ondragover should bubble to body as event handler #"+(evindex[i]+1));
+ }
+
+ /* ondragleave */
+ test(function () { assert_equals( events.indexOf('orange.ondragleave'), -1 ); }, "orange.ondragleave should not fire");
+ test(function () { assert_equals( events.indexOf('pink.ondragleave'), -1 ); }, "pink.ondragleave should not fire");
+ test(function () { assert_true( events.indexOf('yellow.ondragleave') != -1 ); }, "yellow.ondragleave should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'yellow.ondragleave') return e; }).length, 1); }, "yellow.ondragleave should fire 1 time");
+ test(function () { assert_equals( events[46], 'yellow.ondragleave' ); }, "yellow.ondragleave should be event handler #47");
+ test(function () { assert_equals( events.indexOf('blue.ondragleave'), -1 ); }, "blue.ondragleave should not fire");
+ test(function () { assert_true( events.indexOf('body.ondragleave') != -1 ); }, "ondragleave should fire at body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'body.ondragleave') return e; }).length, 2); }, "ondragleave should fire at body 2 times");
+ for( var i = 0, evindex = [31,59]; i < evindex.length; i++ ) {
+ test(function () { assert_equals( events[evindex[i]], 'body.ondragleave' ); }, "ondragleave should fire at body as event handler #"+(evindex[i]+1));
+ }
+ test(function () { assert_true( events.indexOf('bubble.ondragleave') != -1 ); }, "ondragleave should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.ondragleave') return e; }).length, 1); }, "ondragleave should bubble to body 1 time");
+ test(function () { assert_equals( events[47], 'bubble.ondragleave' ); }, "ondragleave should bubble to body as event handler #48");
+
+ /* ondrop */
+ test(function () { assert_equals( events.indexOf('orange.ondrop'), -1 ); }, "orange.ondrop should not fire");
+ test(function () { assert_equals( events.indexOf('pink.ondrop'), -1 ); }, "pink.ondrop should not fire");
+ test(function () { assert_equals( events.indexOf('yellow.ondrop'), -1 ); }, "yellow.ondrop should not fire");
+ test(function () { assert_true( events.indexOf('blue.ondrop') != -1 ); }, "blue.ondrop should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'blue.ondrop') return e; }).length, 1); }, "blue.ondrop should fire 1 time");
+ test(function () { assert_equals( events[70], 'blue.ondrop' ); }, "blue.ondrop should be event handler #71");
+ test(function () { assert_equals( events.indexOf('body.ondrop'), -1 ); }, "ondrop should not fire at body");
+ test(function () { assert_true( events.indexOf('bubble.ondrop') != -1 ); }, "ondrop should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.ondrop') return e; }).length, 1); }, "ondrop should bubble to body 1 time");
+ test(function () { assert_equals( events[71], 'bubble.ondrop' ); }, "ondrop should bubble to body as event handler #72");
+
+ /* ondragend */
+ test(function () { assert_true( events.indexOf('orange.ondragend') != -1 ); }, "orange.ondragend should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'orange.ondragend') return e; }).length, 1); }, "orange.ondragend should fire 1 time");
+ test(function () { assert_equals( events[72], 'orange.ondragend' ); }, "orange.ondragend should be event handler #73");
+ test(function () { assert_equals( events.indexOf('pink.ondragend'), -1 ); }, "pink.ondragend should not fire");
+ test(function () { assert_equals( events.indexOf('yellow.ondragend'), -1 ); }, "yellow.ondragend should not fire");
+ test(function () { assert_equals( events.indexOf('blue.ondragend'), -1 ); }, "blue.ondragend should not fire");
+ test(function () { assert_equals( events.indexOf('body.ondragend'), -1 ); }, "ondragend should not fire at body");
+ test(function () { assert_true( events.indexOf('bubble.ondragend') != -1 ); }, "ondragend should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.ondragend') return e; }).length, 1); }, "ondragend should bubble to body 1 time");
+ test(function () { assert_equals( events[73], 'bubble.ondragend' ); }, "ondragend should bubble to body as event handler #74");
+
+ /* onmousedown */
+ test(function () { assert_true( events.indexOf('orange.onmousedown') != -1 ); }, "orange.onmousedown should fire");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'orange.onmousedown') return e; }).length, 1); }, "orange.onmousedown should fire 1 time");
+ test(function () { assert_equals( events[0], 'orange.onmousedown' ); }, "orange.onmousedown should be event handler #1");
+ test(function () { assert_equals( events.indexOf('pink.onmousedown'), -1 ); }, "pink.onmousedown should not fire");
+ test(function () { assert_equals( events.indexOf('yellow.onmousedown'), -1 ); }, "yellow.onmousedown should not fire");
+ test(function () { assert_equals( events.indexOf('blue.onmousedown'), -1 ); }, "blue.onmousedown should not fire");
+ test(function () { assert_equals( events.indexOf('body.onmousedown'), -1 ); }, "onmousedown should not fire at body");
+ test(function () { assert_true( events.indexOf('bubble.onmousedown') != -1 ); }, "onmousedown should bubble to body");
+ test(function () { assert_equals( events.filter(function (e) { if (e == 'bubble.onmousedown') return e; }).length, 1); }, "onmousedown should bubble to body 1 time");
+ test(function () { assert_equals( events[1], 'bubble.onmousedown' ); }, "onmousedown should bubble to body as event handler #1");
+
+ /* onmouseup */
+ test(function () { assert_equals( events.indexOf('orange.onmouseup'), -1 ); }, "orange.onmouseup should not fire");
+ test(function () { assert_equals( events.indexOf('pink.onmouseup'), -1 ); }, "pink.onmouseup should not fire");
+ test(function () { assert_equals( events.indexOf('yellow.onmouseup'), -1 ); }, "yellow.onmouseup should not fire");
+ test(function () { assert_equals( events.indexOf('blue.onmouseup'), -1 ); }, "blue.onmouseup should not fire");
+ test(function () { assert_equals( events.indexOf('body.onmouseup'), -1 ); }, "onmouseup should not fire at body");
+ test(function () { assert_equals( events.indexOf('bubble.onmouseup'), -1 ); }, "onmouseup should not bubble to body");
+
+ done();
+ }
+};
+</script>
+
+<div id="testhere">
+<div draggable='true' id='orange'></div>
+<div id='fuchsia'></div>
+<div id='yellow'></div>
+<div id='blue'></div>
+</div>
+
+<p>If you have already clicked on this page, reload it.</p>
+<p>Use your pointing device to slowly drag the orange square over the pink square then the yellow square, then the blue square, and release it over the blue square (make sure the mouse remains over each square for at least 1 second, and over the gaps between squares for at least 1 second). Fail if no new text appears below.</p>
+
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-input-with-circle.xhtml b/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-input-with-circle.xhtml
new file mode 100644
index 0000000000..966afe4400
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-input-with-circle.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="document.querySelector('input').select()" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)"><input value="Drag me"/></p>
+<p>Drag selected text out of frame and drop it somewhere on the page. Both circles should turn green once text is dropped.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-link-with-circle.xhtml b/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-link-with-circle.xhtml
new file mode 100644
index 0000000000..c85f228619
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-link-with-circle.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragleave="leavePage(event)">
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondragend="endDrag(event)">Drag me</a></p>
+<p>Drag link out of frame and drop it somewhere on the page. Both circles should turn green once link is dropped.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-p-with-circle.xhtml b/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-p-with-circle.xhtml
new file mode 100644
index 0000000000..efbd4b7614
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/helper-drag-me-p-with-circle.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function start(event)
+ {if(step++ == 1)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragstart should be first event to fire.')}
+ }
+function leavePage(event)
+ {if(step++ > 1)
+ {setColor('green green silver silver')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragleave should fire after dragstart.')}
+ }
+function endDrag(event)
+ {if(step++ > 2)
+ {setColor('green')}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragend should fire after dragstart and dragleave.')}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))" ondragleave="leavePage(event)">
+<p ondragstart="start(event)" ondragend="endDrag(event)">Drag me</p>
+<p>Drag selected text out of frame and drop it somewhere on the page. Both circles should turn green once text is dropped.</p>
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/helper-drop-here-body-circle.xhtml b/testing/web-platform/tests/html/editing/dnd/events/helper-drop-here-body-circle.xhtml
new file mode 100644
index 0000000000..bb8c0e36f0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/helper-drop-here-body-circle.xhtml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:0;
+ height:0;
+ border:solid 50px silver;
+ border-radius:50px;
+ margin-left:auto;}
+</style>
+<script type="application/ecmascript">
+var step = 1;
+function enterPage(event)
+ {event.preventDefault();
+ if(step++ > 0)
+ {setColor('green silver silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragenter should fire before dragover and drop');}
+ }
+function overPage(event)
+ {event.preventDefault();
+ if(step++ > 1)
+ {setColor('green green silver silver');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function dropIt(event)
+ {if(step++ > 1)
+ {setColor('green');}
+ else
+ {step = 0;
+ setColor('maroon');
+ say('Dragover should fire after dragenter');}
+ }
+function say(it)
+ {document.querySelector('pre').appendChild(document.createTextNode(it + '\n'))}
+function setColor(c)
+ {document.querySelector('div').setAttribute('style','border-color:' + c)}
+</script>
+</head>
+<body ondragenter="enterPage(event)" ondragover="overPage(event)" ondrop="dropIt(event)">
+<div/>
+<pre/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/historical-manual.html b/testing/web-platform/tests/html/editing/dnd/events/historical-manual.html
new file mode 100644
index 0000000000..0e2147222d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/historical-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>Historical drag-and-drop features</title>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.test-square {
+ width: 100px;
+ height: 100px;
+}
+
+#draggable {
+ background: orange;
+}
+
+#dropzone {
+ background: blue;
+}
+</style>
+
+<p>Drag the orange square onto the blue square and release it.</p>
+
+<div draggable="true" id="draggable" class="test-square" ondragstart="event.dataTransfer.setData('text/plain', null)"></div>
+<div id="dropzone" class="test-square"></div>
+
+<script>
+"use strict";
+
+async_test(t => {
+ let dragexitCount = 0;
+ document.addEventListener("dragexit", () => {
+ ++dragexitCount;
+ });
+
+ // Prevent the event to allow drop
+ document.addEventListener("dragover", e => {
+ e.preventDefault();
+ });
+
+ document.addEventListener("drop", t.step_func_done(() => {
+ assert_equals(dragexitCount, 0);
+ }));
+}, `dragexit must not fire during drag-and-drop`);
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/events/relatedTarget-attribute-manual.html b/testing/web-platform/tests/html/editing/dnd/events/relatedTarget-attribute-manual.html
new file mode 100644
index 0000000000..c5a897d68e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/events/relatedTarget-attribute-manual.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+ <head>
+ <title>relatedTarget attribute for dragenter and dragleave events</title>
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ #outerdiv {
+ padding: 50px;
+ background: blue;
+ }
+ #innerdiv {
+ width:200px;
+ height:100px;
+ background: green;
+ }
+ </style>
+ <script>
+ var drag_test = async_test("dragenter and dragleave are correctly fired.");
+ var got_dragenter = false;
+ var got_dragleave = false;
+ function run() {
+ var draggable = document.getElementById("draggable");
+ var innerdiv = document.getElementById("innerdiv");
+ draggable.addEventListener("dragstart", (e) => {
+ e.dataTransfer.setData("text", draggable.innerHTML);
+ });
+ innerdiv.addEventListener("dragenter", (e) => {
+ if (!got_dragenter) {
+ test(function() {
+ assert_equals(e.relatedTarget.id, "outerdiv", "dragenter event should have the correct relatedTarget.");
+ }, "dragenter event should have the correct relatedTarget.");
+ got_dragenter = true;
+ }
+ });
+ innerdiv.addEventListener("dragleave", (e) => {
+ if (!got_dragleave) {
+ test(function() {
+ assert_equals(e.relatedTarget.id, "outerdiv", "dragleave event should have the correct relatedTarget.");
+ }, "dragleave event should have the correct relatedTarget.");
+ got_dragleave = true;
+ if (got_dragenter)
+ drag_test.done();
+ }
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Drag & Drop: relatedTarget attribute for dragenter and dragleave events</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>Test Description:
+ <ol>
+ <li>Drag the text into the green box.</li>
+ <li>Without releasing the drag, drag the text out of the green box.</li>
+ </ol>
+ </h4>
+ <br>
+ <div id="draggable" draggable="true">Drag this text</br>over the green box</div>
+ <div id="outerdiv">
+ <div id="innerdiv"></div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/001.html b/testing/web-platform/tests/html/editing/dnd/file/001.html
new file mode 100644
index 0000000000..b911920041
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/001.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - simple file drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+var filename = 'fail.png', filesize = '759', filetype = 'image/png';
+var fails = [], finished = false;
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 0 for '+e.type;
+ }
+ };
+ orange.ondrop = function(e) {
+ e.preventDefault();
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ finish();
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 1 for '+e.type;
+ }
+ if( !e.dataTransfer.files[0] ) {
+ fails[fails.length] = 'no dataTransfer.files[0] for drop';
+ finish();
+ return;
+ }
+ if( e.dataTransfer.files[0].size != filesize ) {
+ fails[fails.length] = 'dataTransfer.files[0].size '+e.dataTransfer.files[0].size+' instead of '+filesize;
+ }
+ /*
+ if( !e.dataTransfer.files[0].lastModified ) {
+ fails[fails.length] = 'no dataTransfer.files[0].lastModified';
+ }
+ */
+ if( e.dataTransfer.files[0].name != filename ) {
+ fails[fails.length] = 'dataTransfer.files[0].name '+e.dataTransfer.files[0].name+' instead of '+filename;
+ }
+ if( e.dataTransfer.files[0].type != filetype ) {
+ fails[fails.length] = 'dataTransfer.files[0].type '+e.dataTransfer.files[0].type+' instead of '+filetype;
+ }
+ if( !window.FileReader ) {
+ fails[fails.length] = 'No FileReader constructor';
+ finish();
+ return;
+ }
+ var reader = new FileReader();
+ reader.onload = function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after load';
+ }
+ if( reader.result.length != filesize ) {
+ fails[fails.length] = 'File data length '+reader.result.length+' instead of '+filesize;
+ }
+ finish();
+ };
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ setTimeout(function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after timeout';
+ }
+ finish();
+ },1000);
+ };
+
+};
+function finish() {
+ if( finished ) { return; }
+ finished = true;
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+}
+</script>
+
+<div></div>
+
+<p>Save <a href="../resources/fail.png">this image</a> to your desktop. Use your pointing device to drag the saved file from your desktop onto the orange box, and release it. If a confirmation dialog appears, accept it. Fail if nothing happens, or if the browser simply displays the image.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/002.html b/testing/web-platform/tests/html/editing/dnd/file/002.html
new file mode 100644
index 0000000000..c8d633d6d2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/002.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - multiple file drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+var filename1 = 'fail.png', filesize1 = '759', filetype1 = 'image/png', filename2 = 'fail.txt', filesize2 = '4', filetype2 = 'text/plain';
+var fails = [], finished = false, donecount = 0;
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 0 for '+e.type;
+ }
+ };
+ orange.ondrop = function(e) {
+ e.preventDefault();
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ finish();
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length != 2 ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 2 for '+e.type;
+ }
+ if( !e.dataTransfer.files[0] ) {
+ fails[fails.length] = 'no dataTransfer.files[0] for drop';
+ finish();
+ return;
+ }
+ if( !e.dataTransfer.files[1] ) {
+ fails[fails.length] = 'no dataTransfer.files[1] for drop';
+ finish();
+ return;
+ }
+ //allow files to be dropped in any order, since this will be determined by the OS
+ var i0 = 0, i1 = 1;
+ if( e.dataTransfer.files[0].name == filename2 ) {
+ i0 = 1;
+ i1 = 0;
+ }
+ if( e.dataTransfer.files[i0].size != filesize1 ) {
+ fails[fails.length] = 'dataTransfer.files['+i0+'].size '+e.dataTransfer.files[i0].size+' instead of '+filesize1;
+ }
+ /*
+ if( !e.dataTransfer.files[i0].lastModified ) {
+ fails[fails.length] = 'no dataTransfer.files['+i0+'].lastModified';
+ }
+ */
+ if( e.dataTransfer.files[i0].name != filename1 ) {
+ fails[fails.length] = 'dataTransfer.files['+i0+'].name '+e.dataTransfer.files[i0].name+' instead of '+filename1;
+ }
+ if( e.dataTransfer.files[i0].type != filetype1 ) {
+ fails[fails.length] = 'dataTransfer.files['+i0+'].type '+e.dataTransfer.files[i0].type+' instead of '+filetype1;
+ }
+ if( e.dataTransfer.files[i1].size != filesize2 ) {
+ fails[fails.length] = 'dataTransfer.files['+i1+'].size '+e.dataTransfer.files[i1].size+' instead of '+filesize2;
+ }
+ /*
+ if( !e.dataTransfer.files[i1].lastModified ) {
+ fails[fails.length] = 'no dataTransfer.files['+i1+'].lastModified';
+ }
+ */
+ if( e.dataTransfer.files[i1].name != filename2 ) {
+ fails[fails.length] = 'dataTransfer.files['+i1+'].name '+e.dataTransfer.files[i1].name+' instead of '+filename2;
+ }
+ if( e.dataTransfer.files[i1].type != filetype2 ) {
+ fails[fails.length] = 'dataTransfer.files['+i1+'].type '+e.dataTransfer.files[i1].type+' instead of '+filetype2;
+ }
+ if( !window.FileReader ) {
+ fails[fails.length] = 'No FileReader constructor';
+ finish();
+ return;
+ }
+ var reader1 = new FileReader();
+ reader1.readAsBinaryString(e.dataTransfer.files[i0]);
+ reader1.onload = function () {
+ if( !reader1.result ) {
+ fails[fails.length] = 'No files['+i0+'] data after load';
+ }
+ if( reader1.result.length != filesize1 ) {
+ fails[fails.length] = 'files['+i0+'] file data length '+reader1.result.length+' instead of '+filesize1;
+ }
+ if( donecount++ ) {
+ finish();
+ }
+ };
+ var reader2 = new FileReader();
+ reader2.onload = function () {
+ if( !reader2.result ) {
+ fails[fails.length] = 'No files['+i1+'] data after load';
+ }
+ if( reader2.result.length != filesize2 ) {
+ fails[fails.length] = 'files['+i1+'] file data length '+reader2.result.length+' instead of '+filesize2;
+ }
+ if( donecount++ ) {
+ finish();
+ }
+ };
+ reader2.readAsBinaryString(e.dataTransfer.files[i1]);
+ setTimeout(function () {
+ if( !reader1.result ) {
+ fails[fails.length] = 'No files['+i0+'] data after timeout';
+ }
+ if( !reader2.result ) {
+ fails[fails.length] = 'No files['+i1+'] data after timeout';
+ }
+ finish();
+ },1000);
+ };
+
+};
+function finish() {
+ if( finished ) { return; }
+ finished = true;
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+}
+</script>
+
+<div></div>
+
+<p>Save <a href="../resources/fail.png">this image</a> and <a href="fail.txt">this text file</a> to your desktop. Use your pointing device to drag both saved files (at the same time) from your desktop onto the orange box, and release them. If a confirmation dialog appears, accept it. Fail if nothing happens, or if the browser simply displays one/both of the files.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/003.html b/testing/web-platform/tests/html/editing/dnd/file/003.html
new file mode 100644
index 0000000000..51e5a5a79c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/003.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - prompting before exposing files</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+var filename = 'fail.png', filesize = '759', filetype = 'image/png';
+var fails = [], finished = false;
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ orange.ondrop = function(e) {
+ e.preventDefault();
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ finish();
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 1 for '+e.type;
+ }
+ if( !e.dataTransfer.files[0] ) {
+ fails[fails.length] = 'no dataTransfer.files[0] for drop';
+ finish();
+ return;
+ }
+ if( e.dataTransfer.files[0].size != filesize ) {
+ fails[fails.length] = 'dataTransfer.files[0].size '+e.dataTransfer.files[0].size+' instead of '+filesize;
+ }
+ /*
+ if( !e.dataTransfer.files[0].lastModified ) {
+ fails[fails.length] = 'no dataTransfer.files[0].lastModified';
+ }
+ */
+ if( e.dataTransfer.files[0].name != filename ) {
+ fails[fails.length] = 'dataTransfer.files[0].name '+e.dataTransfer.files[0].name+' instead of '+filename;
+ }
+ if( e.dataTransfer.files[0].type != filetype ) {
+ fails[fails.length] = 'dataTransfer.files[0].type '+e.dataTransfer.files[0].type+' instead of '+filetype;
+ }
+ if( !window.FileReader ) {
+ fails[fails.length] = 'No FileReader constructor';
+ finish();
+ return;
+ }
+ var reader = new FileReader();
+ reader.onload = function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after load';
+ }
+ if( reader.result.length != filesize ) {
+ fails[fails.length] = 'File data length '+reader.result.length+' instead of '+filesize;
+ }
+ finish();
+ };
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ setTimeout(function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after timeout';
+ }
+ finish();
+ },1000);
+ };
+
+};
+function finish() {
+ if( finished ) { return; }
+ finished = true;
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+}
+</script>
+
+<div></div>
+
+<p>Save <a href="../resources/fail.png">this image</a> to your desktop. Use your pointing device to drag the saved file from your desktop onto the orange box, and release it. <strong>A confirmation dialog must appear, allowing you to choose to cancel the upload</strong>. Accept it. Fail if nothing happens, or if the browser simply displays the image, or if &quot;PASS&quot; appears as the page text <strong>before</strong> you have accepted the upload.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/004.html b/testing/web-platform/tests/html/editing/dnd/file/004.html
new file mode 100644
index 0000000000..ee0d63455f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/004.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cancelling the dropped file upload</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+var fails = [];
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ orange.ondrop = function(e) {
+ //if the browser simulates a drop, it must do so with an empty FileList
+ e.preventDefault();
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ finish();
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 0 for '+e.type;
+ }
+ if( e.dataTransfer.files[0] ) {
+ fails[fails.length] = 'dataTransfer.files[0] exists for drop';
+ finish();
+ }
+ };
+
+};
+function finish() {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+}
+</script>
+
+<div></div>
+
+<p>Save <a href="../resources/pass.png">this image</a> to your desktop. Use your pointing device to drag the saved file from your desktop onto the orange box, and release it. <strong>A confirmation dialog must appear, allowing you to choose to cancel the upload</strong>. Refuse it. Pass if nothing happens, or if the browser simply displays the image.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/005.html b/testing/web-platform/tests/html/editing/dnd/file/005.html
new file mode 100644
index 0000000000..afb40e2199
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/005.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging text onto a file input</title>
+
+<ol>
+ <li>Save <a href="../resources/filler.html">this file</a> to your computer.</li>
+ <li>Write the full /path/and/name to that file, into the first input below, then select all of the text you just entered.</li>
+ <li>Drag selected text to the file input. If no prompt appears, and the text is not added to the file input, pass and ignore further steps.</li>
+ <li>If a prompt appears, accept it. Fail if the file input's value is set without any prompts.</li>
+ <li>If a prompt appears; fail if the file input's value is not set after accepting the prompt.</li>
+</ol>
+<p><input value="/tmp/filler.html"></p>
+<p><input type="file"></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/006.html b/testing/web-platform/tests/html/editing/dnd/file/006.html
new file mode 100644
index 0000000000..d25c5491e4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/006.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cancelling dragging text onto a file input</title>
+
+<ol>
+ <li>Save <a href="../resources/filler.html">this file</a> to your computer.</li>
+ <li>Write the full /path/and/name to that file, into the first input below, then select all of the text you just entered.</li>
+ <li>Drag selected text to the file input. If no prompt appears, and the text is not added to the file input, pass and ignore further steps.</li>
+ <li>If a prompt appears, refuse it. Fail if the file input's value is set without any prompts.</li>
+ <li>If a prompt appears; fail if the file input's value is set after refusing the prompt.</li>
+</ol>
+<p><input value="/tmp/filler.html"></p>
+<p><input type="file"></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/007.html b/testing/web-platform/tests/html/editing/dnd/file/007.html
new file mode 100644
index 0000000000..046220bdb6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/007.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop for large file</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+//JavaScript can support file sizes up to 9007199254740992 bytes ... in theory
+//This test uses a more sane value, just for the sake of UI testing - 32 MB
+var filesize1 = 33554432, filesize2 = 134217728;
+var fails = [], finished = false;
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 0 for '+e.type;
+ }
+ };
+ orange.ondrop = function(e) {
+ e.preventDefault();
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ finish();
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 1 for '+e.type;
+ }
+ if( !e.dataTransfer.files[0] ) {
+ fails[fails.length] = 'no dataTransfer.files[0] for drop';
+ finish();
+ return;
+ }
+ if( e.dataTransfer.files[0].size != filesize1 && e.dataTransfer.files[0].size != filesize2 ) {
+ fails[fails.length] = 'dataTransfer.files[0].size '+e.dataTransfer.files[0].size+' instead of '+filesize1+' or '+filesize2;
+ }
+ /*
+ if( !e.dataTransfer.files[0].lastModified ) {
+ fails[fails.length] = 'no dataTransfer.files[0].lastModified';
+ }
+ */
+ if( !window.FileReader ) {
+ fails[fails.length] = 'No FileReader constructor';
+ finish();
+ return;
+ }
+ var reader = new FileReader();
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ reader.onload = function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after load';
+ }
+ if( reader.result.length != filesize1 && reader.result.length != filesize2 ) {
+ fails[fails.length] = 'File data length '+reader.result.length+' instead of '+filesize1+' or '+filesize2;
+ }
+ finish();
+ };
+ setTimeout(function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after timeout';
+ }
+ finish();
+ },1000);
+ };
+
+};
+function finish() {
+ if( finished ) { return; }
+ finished = true;
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS if the UI did not lock up';
+}
+</script>
+
+<div></div>
+
+<p>Save <a href="../resources/32mb.py">32MB.txt</a> to your desktop. Use your pointing device to drag the saved file from your desktop onto the orange box, and release it. If a confirmation dialog appears, accept it. Fail if this text is not replaced with a pass message. Fail if the UI locks up immediately after dropping the file.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/008.html b/testing/web-platform/tests/html/editing/dnd/file/008.html
new file mode 100644
index 0000000000..4975158209
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/008.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dropping folders</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+var fails = [], finished = false;
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ } else if( e.dataTransfer.files.length ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 0 for '+e.type;
+ }
+ };
+ orange.ondrop = function(e) {
+ e.preventDefault();
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ finish();
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ //browsers represent it as a single file (name matching the folder)
+ //also allow no files, since that is a valid solution
+ if( e.dataTransfer.files.length > 1 ) {
+ //dropping the contents of the folder would be crazy, since there could literally be millions of files, or the entire disk contents
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 1 for '+e.type;
+ }
+ if( !e.dataTransfer.files[0] ) {
+ finish();
+ return;
+ }
+ /*
+ Windows 7 sometimes randomly assigns size to folders, and that is presented to the browser.
+ Strangely, packing and unpacking that folder can remove its size.
+ Since this is an OS quirk that we have no control over, the test will not check the size.
+ if( e.dataTransfer.files[0].size ) {
+ fails[fails.length] = 'dataTransfer.files[0].size '+e.dataTransfer.files[0].size+' instead of 0';
+ }
+ */
+ /*
+ if( !e.dataTransfer.files[0].lastModified ) {
+ fails[fails.length] = 'no dataTransfer.files[0].lastModified';
+ }
+ */
+ if( !window.FileReader ) {
+ fails[fails.length] = 'No FileReader constructor';
+ finish();
+ return;
+ }
+ var reader = new FileReader();
+ reader.onload = function () {
+ fails[fails.length] = 'File managed to load even though it was a folder '+e.type;
+ finish();
+ };
+ reader.onerror = function () {
+ finish();
+ };
+ try {
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ } catch(err) {
+ fails[fails.length] = 'Threw an error when trying to read the file '+e.type;
+ finish();
+ return;
+ }
+ setTimeout(function () {
+ fails[fails.length] = 'Onerror failed to fire '+reader.error.code;
+ finish();
+ },1000);
+ };
+
+};
+function finish() {
+ if( finished ) { return; }
+ finished = true;
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+}
+</script>
+
+<div></div>
+
+<p>Drag a folder containing at least 2 files, from your computer's file manager, onto the orange box above. If a no-drop cursor was shown and no text changes when the folder is dropped, pass and ignore further conditions. If a prompt appears, accept it. Fail if the mouse cursor makes it look like it will work but nothing happens.</p>
+<p>This test needs to be repeated with:</p>
+<ul>
+ <li>A regular folder containing at least 2 items</li>
+ <li>A disk drive (if your OS exposes them) containing at least 2 items</li>
+ <li>The system trash/recycle bin folder (if your OS exposes one) containing at least 2 items</li>
+ <li>The &quot;My Computer&quot; folder (if your OS provides it)</li>
+ <li>Your &quot;My Documents&quot; folder (if your OS provides it)</li>
+ <li>A folder that you do not have permissions to access</li>
+</ul>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/009.html b/testing/web-platform/tests/html/editing/dnd/file/009.html
new file mode 100644
index 0000000000..72381863f2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/009.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - no dnd event listeners</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<!-- This test assumes that the browser's default behaviour is to open dropped files. Test 010 continues with this assumption. -->
+
+<div></div>
+
+<p>Save <a href="../resources/pass.png">this image</a> to your desktop. Use your pointing device to drag the saved file from your desktop onto the orange box, and release it. Fail if nothing happens.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/010.html b/testing/web-platform/tests/html/editing/dnd/file/010.html
new file mode 100644
index 0000000000..331cafc5ef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/010.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - blocked drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+//This test assumes that if the page does not want to use the drop, that the browser will revert to default behaviour of opening the file
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'none';
+ };
+ orange.ondrop = function(e) {
+ e.preventDefault();
+ };
+
+};
+</script>
+
+<div></div>
+
+<p>Save <a href="../resources/pass.png">this image</a> to your desktop. Use your pointing device to drag the saved file from your desktop onto the orange box, and release it. Fail if nothing happens.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/011.html b/testing/web-platform/tests/html/editing/dnd/file/011.html
new file mode 100644
index 0000000000..a265e7b4dd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/011.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - simple file drop with dropzone attribute</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+var filename = 'fail.png', filesize = '759', filetype = 'image/png';
+var fails = [], finished = false;
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragenter = function(e) {
+ e.dataTransfer.dropEffect = 'copy';
+ };
+/* orange.ondragover = function(e) {
+ e.preventDefault();
+ };
+*/
+ orange.ondrop = function(e) {
+ e.preventDefault();
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = 'No dataTransfer.files for '+e.type;
+ }
+ if( !window.FileList ) {
+ fails[fails.length] = 'No FileList interface object';
+ finish();
+ return;
+ }
+ if( !( e.dataTransfer.files instanceof FileList ) ) {
+ fails[fails.length] = 'dataTransfer.files is not a FileList';
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ fails[fails.length] = 'dataTransfer.files.length is '+e.dataTransfer.files.length+' instead of 1 for '+e.type;
+ }
+ if( !e.dataTransfer.files[0] ) {
+ fails[fails.length] = 'no dataTransfer.files[0] for drop';
+ finish();
+ return;
+ }
+ if( e.dataTransfer.files[0].size != filesize ) {
+ fails[fails.length] = 'dataTransfer.files[0].size '+e.dataTransfer.files[0].size+' instead of '+filesize;
+ }
+ /*
+ if( !e.dataTransfer.files[0].lastModified ) {
+ fails[fails.length] = 'no dataTransfer.files[0].lastModified';
+ }
+ */
+ if( e.dataTransfer.files[0].name != filename ) {
+ fails[fails.length] = 'dataTransfer.files[0].name '+e.dataTransfer.files[0].name+' instead of '+filename;
+ }
+ if( e.dataTransfer.files[0].type != filetype ) {
+ fails[fails.length] = 'dataTransfer.files[0].type '+e.dataTransfer.files[0].type+' instead of '+filetype;
+ }
+ if( !window.FileReader ) {
+ fails[fails.length] = 'No FileReader constructor';
+ finish();
+ return;
+ }
+ var reader = new FileReader();
+ reader.onload = function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after load';
+ }
+ if( reader.result.length != filesize ) {
+ fails[fails.length] = 'File data length '+reader.result.length+' instead of '+filesize;
+ }
+ finish();
+ };
+ reader.readAsBinaryString(e.dataTransfer.files[0]);
+ setTimeout(function () {
+ if( !reader.result ) {
+ fails[fails.length] = 'No file data after timeout';
+ }
+ finish();
+ },1000);
+ };
+
+};
+function finish() {
+ if( finished ) { return; }
+ finished = true;
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+}
+</script>
+
+<div dropzone="copy file:image/png"></div>
+
+<p>Save <a href="../resources/fail.png">this image</a> to your desktop. Use your pointing device to drag the saved file from your desktop onto the orange box, and release it. If a confirmation dialog appears, accept it. Fail if nothing happens, or if the browser simply displays the image.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/fail.txt b/testing/web-platform/tests/html/editing/dnd/file/fail.txt
new file mode 100644
index 0000000000..fc26162516
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/fail.txt
@@ -0,0 +1 @@
+FAIL \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/001.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/001.html
new file mode 100644
index 0000000000..52f6e8d01f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/001.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for basic server name</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ if( location.protocol != 'http:' && location.protocol != 'https:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'This test should be loaded over http or https.';
+ return;
+ }
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {
+ e.preventDefault();
+ };
+ document.getElementsByTagName('span')[0].innerHTML = location.hostname;
+};
+</script>
+
+<div></div>
+
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, correctly identifying the server name as <span></span></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/002.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/002.html
new file mode 100644
index 0000000000..f913aedfac
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/002.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for server name and document.domain</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ if( location.protocol != 'http:' && location.protocol != 'https:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'This test should be loaded over http or https.';
+ return;
+ }
+ if( !location.hostname.match(/[^\.]\.[^\.]+\.[^\.]/) ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'This test should be loaded from a subdomain that allows document.domain to be set to a parent domain (try using a server name that contains at least two or three dots, for example www.example.org).';
+ return;
+ }
+ var realhost = location.hostname, newdomain = location.hostname.replace(/^[^.]+\./,'');
+ try {
+ document.domain = location.hostname.replace(/^[^.]+\./,'');
+ } catch(e) {
+ document.getElementsByTagName('p')[0].innerHTML = 'This test should be loaded from a subdomain that allows document.domain to be set to a parent domain.';
+ return;
+ }
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {
+ e.preventDefault();
+ };
+ document.getElementsByTagName('span')[0].innerHTML = realhost;
+};
+</script>
+
+<div></div>
+
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, identifying the server name as <span></span></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/003.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/003.html
new file mode 100644
index 0000000000..ada495bd19
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/003.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for ftp server name</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ if( location.protocol != 'ftp:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'This test should be loaded over ftp.';
+ return;
+ }
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {
+ e.preventDefault();
+ };
+ document.getElementsByTagName('span')[0].innerHTML = location.hostname;
+};
+</script>
+
+<div></div>
+
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, correctly identifying the server name as <span></span></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/004.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/004.html
new file mode 100644
index 0000000000..0232638ef8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/004.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for file:</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ if( location.protocol != 'file:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'Save this page to your local filesystem, and load it from there.';
+ return;
+ }
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+</script>
+
+<div></div>
+
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, either showing the server name as localhost, or otherwise identifying this file as the target of the upload.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/005.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/005.html
new file mode 100644
index 0000000000..3a6c356c2e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/005.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for special cases</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ if( location.protocol == 'file:' || location.protocol == 'http:' || location.protocol == 'https:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'Use the source of this file as the source of special-case URLs within your browser, such as scriptable chrome: or opera: or attachment: URLs (eg. opera:config and send yourself the file as an email attachments and open the attachment in the browser).';
+ return;
+ }
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+</script>
+
+<div></div>
+
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, either showing the server name as unknown, or otherwise identifying this URL as the target of the upload.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/006.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/006.html
new file mode 100644
index 0000000000..a0f919595b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/006.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for data URI with inherited origin</title>
+<style>
+iframe { border: none; height: 250px; width: 250px; }
+</style>
+
+<script>
+window.onload = function() {
+ if( location.protocol != 'http:' && location.protocol != 'https:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'This test should be loaded over http or https.';
+ return;
+ }
+ var url = "data:text/html,"+escape(
+'<!DOCTYPE html>\
+<title>drag &amp; drop - file drop prompt for data URI with inherited origin<\/title>\
+<style>\
+ body > div {\
+ height: 200px;\
+ width: 200px;\
+ background-color: orange;\
+ }\
+<\/style>\
+<script>\
+window.onload = function() {\
+ var orange = document.getElementsByTagName("div")[0];\
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {\
+ e.preventDefault();\
+ };\
+};\
+<\/script>\
+<div><\/div>'
+ );
+ var frame = document.createElement('iframe');
+ frame.setAttribute('src',url);
+ document.body.appendChild(frame);
+ document.getElementsByTagName('span')[0].innerHTML = location.hostname;
+};
+</script>
+
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, correctly identifying the server name <span></span>.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/007.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/007.html
new file mode 100644
index 0000000000..be839f99c9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/007.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for data URI without inherited origin</title>
+<script>
+window.onload = function() {
+ var url = "data:text/html,"+escape(
+'<!DOCTYPE html>\
+<title>drag &amp; drop - file drop prompt for data URI with inherited origin<\/title>\
+<style>\
+ body > div {\
+ height: 200px;\
+ width: 200px;\
+ background-color: orange;\
+ }\
+<\/style>\
+<script>\
+window.onload = function() {\
+ var orange = document.getElementsByTagName("div")[0];\
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {\
+ e.preventDefault();\
+ };\
+};\
+<\/script>\
+<div><\/div>\
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, either showing the server name as unknown, or otherwise identifying this URL as the target of the upload.<\/p>'
+ );
+ document.getElementsByTagName('p')[1].textContent = url;
+};
+</script>
+
+<p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+<p></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/008.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/008.html
new file mode 100644
index 0000000000..903808ea0d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/008.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for javascript URL with inherited origin</title>
+<style>
+iframe { border: none; height: 250px; width: 250px; }
+</style>
+
+<script>
+window.onload = function() {
+ if( location.protocol != 'http:' && location.protocol != 'https:' ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'This test should be loaded over http or https.';
+ return;
+ }
+ var url = "javascript:'"+escape(
+'<!DOCTYPE html>\
+<title>drag &amp; drop - file drop prompt for data URI with inherited origin<\/title>\
+<style>\
+ body > div {\
+ height: 200px;\
+ width: 200px;\
+ background-color: orange;\
+ }\
+<\/style>\
+<script>\
+window.onload = function() {\
+ var orange = document.getElementsByTagName("div")[0];\
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {\
+ e.preventDefault();\
+ };\
+};\
+<\/script>\
+<div><\/div>'
+ +"'");
+ var frame = document.createElement('iframe');
+ frame.setAttribute('src',url);
+ document.body.appendChild(frame);
+ document.getElementsByTagName('span')[0].innerHTML = location.hostname;
+};
+</script>
+
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, correctly identifying the server name <span></span></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/file/prompt/009.html b/testing/web-platform/tests/html/editing/dnd/file/prompt/009.html
new file mode 100644
index 0000000000..b620ad6f15
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/file/prompt/009.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - file drop prompt for javascript URL without inherited origin</title>
+<script>
+window.onload = function() {
+ var url = "javascript:'"+escape(
+'<!DOCTYPE html>\
+<title>drag &amp; drop - file drop prompt for data URI with inherited origin<\/title>\
+<style>\
+ body > div {\
+ height: 200px;\
+ width: 200px;\
+ background-color: orange;\
+ }\
+<\/style>\
+<script>\
+window.onload = function() {\
+ var orange = document.getElementsByTagName("div")[0];\
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {\
+ e.preventDefault();\
+ };\
+};\
+<\/script>\
+<div><\/div>\
+<p>Drag a file from your desktop onto the orange square. A prompt should appear, either showing the server name as unknown, or otherwise identifying this URL as the target of the upload (it may alternatively identify the security context about:blank).<\/p>'
+ +"'");
+ document.getElementsByTagName('p')[1].textContent = url;
+};
+</script>
+
+<p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+<p></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/historical.html b/testing/web-platform/tests/html/editing/dnd/historical.html
new file mode 100644
index 0000000000..5cba688ff8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/historical.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Historical drag-and-drop features</title>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ const potentialBadLocations = [
+ window,
+ document,
+ HTMLElement.prototype,
+ SVGElement.prototype,
+ Document.prototype,
+ HTMLDocument.prototype,
+ Element.prototype
+ ];
+ for (const location of potentialBadLocations) {
+ assert_false("ondragexit" in location,
+ `${location.constructor.name} must not have a property "ondragexit"`);
+ }
+}, `ondragexit must not be present on the GlobalEventHandlers locations`);
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/001.html b/testing/web-platform/tests/html/editing/dnd/images/001.html
new file mode 100644
index 0000000000..dcc31664e7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/001.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Image drag and drop</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="../resources/circle.png" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+/>
+<script>
+async function test() {
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ addImage(event);
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the image to the bottom div should copy the image there"');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/002.html b/testing/web-platform/tests/html/editing/dnd/images/002.html
new file mode 100644
index 0000000000..9756eb26da
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/002.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>dataURL image drag and drop</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+/>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ addImage(event);
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the dataURL PNG image to the bottom div should copy the image there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/003.html b/testing/web-platform/tests/html/editing/dnd/images/003.html
new file mode 100644
index 0000000000..e2b3ac1a8d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/003.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<head>
+<title>Image drag and drop outside browser window</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above outside the window and then back inside and drop in the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/004.html b/testing/web-platform/tests/html/editing/dnd/images/004.html
new file mode 100644
index 0000000000..829608f537
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/004.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Object with image drag and drop</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('object').getAttribute('data'));}
+</script>
+</head>
+<body>
+<p><object draggable="true" ondragstart="start(event)" type="image/png" data="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle">PNG image</object></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+/>
+</body>
+<script>
+async function test() {
+ const object = document.querySelector('object');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ addImage(event);
+ assert_equals(object.getAttribute('data'), event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+
+ dragDropTest(object, div, onDropCallBack, 'Dragging the object to the bottom div should copy the image there');
+}
+test();
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/005.html b/testing/web-platform/tests/html/editing/dnd/images/005.html
new file mode 100644
index 0000000000..c625f44685
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/005.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>JPG image drag and drop</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/EAaIAAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKCxAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6AQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgsRAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/AABEIAGQAZAMBIgACEQEDEQH/2gAMAwEAAhEDEQA/AP7+KKK8v+Mvxl+Gn7P3w08V/F74veK9P8GfD/wZp51HXdd1EuVRS6wWtlZWsCy3mp6tqd5LBp+k6Tp8FxqGp6hcQWdnBLPKiHOtWo4ejVxGIq06FChTnVrVqs406VKlTi5VKlSpJqMIQinKUpNKKV2Y4jEUMJQrYrFVqWHw2HpTr4jEV5xpUaFGlFzqVatSbUKdOnBOU5yajGKbbsekXV1a2Frc319cwWdlZwTXV5eXU0dva2trbxtLcXNzcSskUEEESNLNNKyxxxqzuwUE1+B37ZX/AAcDfsvfAK61bwV8BNOm/aZ+ImnyS2U+p6Fqa6J8ItIvIzsk8zx19mv5/FjQEiVE8G6VqOiX6rJB/wAJZYTg7f54f+Clf/BX34z/ALc2tax4C8H3OrfCv9mS1v2TR/h7ZXX2bXfHVvayA2mt/FTULKd11SeaRBfWvhC0nbwvob/ZVZdd1bTo/EU/47V/KHHn0gsQ61bLeB4wp0YN055/iqKqVKzWnNl2ErL2dOn/AC18XTqyqLbDUbKcv4l8TPpR4t4jEZR4cQp0cPTcqU+JsZQVatXa0csqwNePsqVH+TE46nWnVT0wmHtGcv14+PH/AAXF/wCCiHxvub+Kx+LsPwV8NXe8ReGfgpo1t4Ra0jPCGPxndtrHxEFxs/1kkPi63gaTMkVrB8qJ+bPjH44fGr4iXLXnxA+L/wAUfHN2+4vdeMfH/ivxNcvu+9un1rVr2Rt38WW5715bRX87ZpxLxDnVSVTNs6zTMJS6YrG4irBeUKTn7KnHtCnCMV0R/KWc8XcU8Q1ZVs84hznNJTd7Y3McVWpR1vy06EqnsaMO1OlThCP2Yo19P17XNJm+0aVrOraZcbt/n6fqN3Zzb+u/zbeaN93+1nNfV/ws/wCChP7b/wAF7q2ufh3+1N8a9JhtMeRo2qeONX8X+Fl29N3g/wAZTeIPCsvp+90d8r8p+XivjmiuHCZlmOXzVXAY/G4GqndVMJiq+Gmn3U6M4SX3nm4DN81yqoq2WZnmGXVou8auAxuJwlRNdVPD1Kck/mf01/swf8HJ3xp8KXOmaF+1d8LvD/xY8OhkhvvHfw4it/BHxDt4f+Wt/c+HJZT4F8S3I6R6fp6/D+DDZa8ymJP6if2Vv21/2bP2zvCT+K/gB8SNL8VNYxRN4i8JXYbRvHnhGSU7BH4l8I6h5eq2MLTboLbV4Y7rQNTljl/sjVtQjQvX+YXXoXwt+K/xI+CXjnQviX8JfGniDwB478NXQu9G8S+G7+Sw1C2bjzbeXbmC+068jzb6lpOoQ3Wl6pZvLZajZ3VpNLC/7Pwf48cVZHVpUM/m+JMruoz+scsc1ow6yoY2y+sSXxOGO9q6nwLEUF7x/QXAX0l+NOG61HC8T1JcW5LeMan1pxjneHp7OeHzGy+tSXxOnmKryrP3FisNfnP9V6ivwp/4JQf8FjfCv7altYfBP43f2N4G/af0+ykNh9kA0/wr8Y7HT7dprrVPCsMsjLpXi+0tYpLvX/B3msk9vHPr3hkyaYmp6T4a/dav7H4e4iyjinK6Gb5Lio4rB1tP5atCskvaYbE0vio4indc1OXRxnBzpThOX99cK8V5Fxpk2Gz7h7GxxuAxGj+zXw1eKTq4TGUX72HxVHmXPTl0calOU6NSnUkUUUV7h9GVru7tNPtLq/v7q3srGyt5ru9vbuaO2tLS0to2muLm5uJmSGC3ghR5ZppXWOKNWd2Cgmv8/L/gr9/wUq1r9uX40XPg7wFrF9a/syfCvVrqx+HukRtJbW/jvXbbzrHUPiprdr8rzTaorz2vg+2vRv0Pwu6sttp+ra74hjm/ob/4OB/2y7v4Bfsv6b8A/BWrSaf8RP2mJdU0PVLiyl2XmkfCHRFtv+E5fehLW7+Lbi/0vwdGkqbL/Q7/AMXCCRZ7HK/wtV/Jn0gePKzxEeB8srOFGnClic/nTlrWqVEq2Ey6Vv8Al3Tp8mLrx2qSq4Zaexmpfw/9KPxMxDxcPDjKMRKnh6VOhjOJp0pWderVUa+ByqTX/LmlS9njsTDVVZ1sItPYTUiiiiv5aP4vCiiigAooooAKKKKANnw94h13wlr2jeKfC+sal4e8SeHdUsdb0HXdGvJ9O1bRtY0u5jvNO1PTb+1eO5s76yu4Yri1uYJElhmjV0YMK/0If+CRf/BROz/b0+ARj8YXFpa/tBfCSPStA+LWnRJDax+Io7qKZNA+JOk2kISGKx8VpY3S6vZWscUWj+JrPU7aK1tdJudDa6/zxa+5P+CdP7XWr/sUftY/DT4zw3d4ngz+0U8JfFnSrXzGGufDDxJcW1t4mie1jBa9utE2WnizRbYbfM1/w/palxGZAf1Dwp47r8E8SUHWqy/sLM6lLCZxRb/dwhJ8tLMEtlVwMpe0k0rzw3t6O84uP7L4J+JWJ8O+LcNKvXn/AKt5zVo4HPsO5fuqdOcuShmkY7Ktl05+1lJLmqYT6zQ3qRlH/S8oqnp9/Y6tYWWqaZd29/pupWltf6ffWkqT2t7Y3kKXFpd208ZMc1vcQSJNDKhKSRurKSDVyv8AQhO+q1T1TXU/1LTTSad09U1s13R/nq/8Fw/jvc/G/wD4KI/F+zivzeeGvgvHo3wT8MQhhssz4PtjdeMoSq/J53/CxtY8YI8nMphitoZD/o6JH+RNen/G3xlc/Eb4z/Fz4g3jmW78d/E7x74yupCdxkufE/irVdbnctxuLSXrHOBnNeYV/mHxJmlTOuIM6zapJylmGaY3Fa9IVcROVKC/u06XJTgukIpH+OPF2c1OIeKOIc8qyc5ZrnOY41X+zTr4qpKjTX9ylR5KVNfZhCK6BRRRXhnzoUUUUAFFFFABRRRQAUUUUAf6JX/BFr483Xx8/wCCeHwR1HV9Q/tHxN8MYNW+CviSZpfOlWT4eXK2fhZLh2Jka6f4eXfg24uXmJllmneYlvMDH9Vq/gD/AOCc/wC3h4v/AGXvgl4o8A6BdGGz1f4p634wkUTXEf8ApN/4S8EaK7YikRf9V4fhGcZ4+lfff/D4H4l/9BB//Aq9/wDj9f1XknjdWwWS5RgqsKdWrhMrwGFqVZqTnUnQwtKlOpN9ZTlFyk+rZ/bHDn0iq+XcPZFl9anSrVsDk2V4OrWqKUqlWphsFQozqVJdZzlByk+rZ/LrrlhNpetavplzu+06dql/YXG773nWl3Lby7s87t8Zz71lV9i/8FB/hZc/Bf8Abe/am+HVxbfY4dI+NfjnU9FgwRt8LeLtXm8Y+D2+b/nr4V1/R5s/dbflSVINfHVfzFmWEqZfmOPwFVctXBY3FYSonuqmGrzozT9JQZ/HGbYCrlea5nllZONbLswxmAqp7qrhMRUw80/SVNhRRRXCecFFFFABRRRQAUUUUAFFFFAHv3wr+Feu+OPD95qumTarHBb6zcaewsSfK82Ky0+5JbH/AC02XaZ/2dtel/8ADPPi/wD5+/EX5tX9Mn/BCL9jPwZ48/YeuviJ8R9HjuZvHXxr8fat4UnNtBKZPC2j6T4Q8FtlriJj8vinwt4oj+Q+X8n9/fX7P/8ADBnwE/6AEP8A4Baf/wDGK/Xst8Lc6zDLsBj6WFrSpY3BYXF02oTs4YmhCtBrXrGaP3jKPBniLNMpyvM6ODryo5jl2Cx1KSpztKni8NTrwa8nGomfzN/8HJv7MFz4S+Nnww/au0HTHHh34teHofh147voUYwwfETwPbSP4eub+Y8JceJfAfk6fp0K8GH4f38h2t9/+Zev9PP9tj9lXwl+2f8As1/Ej4A+K2hspPFGli88IeIpIzI/hHx7ox+3+EfEsewGbybLVEjg1e3tzHLqXh+71fSPMSPUHNf5pvxW+F3jn4J/Efxn8JviXoN34Z8d+AdfvvDfiXRbxfntdQsZMeZBL/q7zTr6Aw6hpWpW5ez1TTLq01GylmtLqGV/X8eOD6uRcVTz7D0n/ZfEjeI54r3KOaxivrtCVtpYj/foOWtR1cQo/wACR7f0l+Aq3DfGlTibC0X/AGNxbJ4r2kV7mHzuMV/aOHm+ksVZZjTcv4sq+KUNMNM8+ooor8KP5sCiiigAooooAKKKKACt/wAK+GNf8b+J/DngzwppdzrfijxdrukeGfDejWSh7zVte16/t9L0jTLVSQGuL6/ure1hBIBklXJFYFf0zf8ABvV+wJd/EP4lT/ts/EnRpo/Afwru7zR/gxb31uVt/FvxKkgmsdX8VW6y8XWkfD6ynltbScRNBL4y1CCWzvEv/B2oW9fT8H8M4zi/iHLsiwal/tVZPFVktMJgabUsXipdF7KlfkUv4lZ0qK96pFH2PAXB+P474qynhrARl/ttdPGYhK8cDltJqeOxs/sr2NC/s1K3tcRKjQXv1Yo/q4/ZT+BWlfsy/s3/AAY+A2j+S0Pwy8A6H4ev7q3/ANVqniMW/wBt8W64vyR8694pvNY1l/3afPfN8q9K+gqKK/0qwuGo4LC4bB4aCp4fCUKOGw9NbU6NCnGlSgvKMIpH+u2CweHy7B4TL8JTVHCYHDUMHhaUdqWHw1KNGjTXlCnCMV6BX4Uf8Fjf+CUNt+2n4V/4Xf8ABPT7Kw/af8DaMtp9gMlvp9j8Y/Cmn75IvCuqXU7w2tp4u0lXl/4Q/X7uSOCeNj4Y12ZdNk0jUvDf7r0V5XEXD2V8U5Rislzeh7bB4qPSyrUKsf4WJw1Sz9niKMvepys1vCcZ0pzhLxeK+Fcm40yLG8PZ9hvrGAxsd1aNfDV460cZhKrUvY4rDy96nOzXxU6kalGdSnL/ACePEPh7XvCWu6x4X8U6Nqnh3xJ4e1K80bXdB1uxudL1jRtW06d7W/03U9OvI4buyvrO5jkgubW4ijmhlRkdQwrGr/Q4/wCCin/BIv4Cft6WVz4xhMPwk/aEtrSOLTvi1oOlRXMXiNLSFYLTSfiVoMc1iniqxjgjitLLWFubTxNosMVrHbald6TaNoV1/FV+13/wTq/aw/Yo1i7h+M/w01H/AIQxLz7LpXxZ8JJceJPhfrgdxHavD4mtraP+xLq8Y4t9E8WWnh/X5NrsulmICVv4T478KuI+Ca9as6FTM8i5m6OcYWlKUIU+izClHmlgaq0TdT/Z5v8AhV5u8Y/5reJXgnxb4d4mvXlhquccN8zeHz7BUZTpwpX92OaUYc88urxVlKVX/ZKkn+4xNR80Y/DlFFFfl5+NhRRRQAUVv+GPCvifxtr+l+FPBnhzXfFvijW7lbLRvDnhnSL/AF7XtXvHyUtNM0jS4Lq/vrlgCVhtbeWQgE7eK/pH/YD/AODer4lfEO80X4k/tszXfws8BJLBf2/wY0e+gf4meK4VIlS38V6tZSXFj8P9Hu12C5tLWe/8ZyQG6spYPB9+sGoL9PwzwfxDxfjFg8iy6tiveSr4pp08DhE/t4rFyXsqWnvKF3WqWtRpVJe6fY8H8BcVcd4+OA4byqvjPfUcRjXF0stwMX/y8xuOkvY0Uo+8qd5YiqlahRqztE/OT/gmP/wTH+Jf/BQD4lwXNzBqvg/9nbwfqsA+J3xOEAi+0+UI7p/A3gZ7qN7fVPGmqW7xiaYR3On+EtPuU1vW0lkl0bRtd/0Hfhr8N/BPwf8AAPhL4X/Dfw9Y+FPAvgXQ7Hw54Y8P6arC103S9PiEcKb5Gee6uZTuub6/u5Z77Ub6a4v7+4uLy4nmc+G/w18A/B/wT4e+G/wv8JaH4F8C+FLFdN8P+GPDljFp+l6bahmkfZDGMy3N1O8t3f31y019qN9PcX9/cXF5cTTv3Ff3T4ceHGW8AZbKEJLG5zjVH+08z5eXm5dY4XCxfvUsHSlqk/3lep+9q/8ALqlR/wBJPCbwmynwvymdOnOOYcQZhGH9sZxycvPy6xwWCi/eo5fRlqk/3mJqfv69rUaNAooor9IP1sKKKKACqd/YWGq2V1puqWVpqWnX0EtrfWF/bw3lleWsylJra6tbhJILiCVCUkhlRo3UlWUirlFG+j1TBpNNNXT0aezXZn5TfHj/AIIr/wDBO/493N/q198Eofhf4l1De0viL4Katc/Dtkkk5eaPwpaJefDv7Q7/AL2S4m8GSzyyEtK77mz/ACn/ALev/BO/4K/st+Lb7Qfh/wCJ/ijq9pbPdKknjHWvCl/cnyDhdzaL4J8Px8/xYhHtiv8AQJr+On/gsR/yUjVv+u2o/wDoQr+UfG/JMlwNSNXBZRleDq1aXPUqYXAYXDzqTb1nOdKlGUpPrKTbP4l+kZw5w9ltWFfLsiybAVq1KNWtVwWWYLC1atSU/eqVJ0KEJTnLrKTbZ/PJ8LfhrofjfX20rVbrVre3F6Lffp89pFNs3hc7rixuk3f9s8e1f1c/sX/8EJP2H/H/AIM0n4kfES7+NfjiabyvP8Kap4+0jR/C02UEh3N4N8H+F/FS5+7+68UR/L0+b5q/mI/Z8/5HOT/sKL/6NFf6Cn7Av/JBNF/7Y/8ApOtfmXhZluXZhneGpY/AYLHUnXgnTxeFoYmDV9nCtCcX9x+PeCuUZVmnEWEo5nlmX5jRlXpKVLH4LD4um05vRwxFOpFr5Hq/wI/ZR/Zu/Zj0r+x/gL8F/APwyhkhFvd6h4e0OD/hJNVhBBVdd8XX/wBs8Va/t2ja2tazfsuBg8V9CUUV/emGwuFwVGGGweGoYTDUlanh8NRp0KNNdoUqUYwiv8KR/plg8Fg8uw9PB5fhMNgcJRXLRwuDoUsNh6Ue1OjRjCnBeUYoKKKK3OkKKKKAAP/Z" alt="JPG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+/>
+</body>
+<script>
+async function test() {
+ const img = document.querySelector('img');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ addImage(event);
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the dataURL JPG image to the bottom div should copy the image there');
+}
+test();
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/006.html b/testing/web-platform/tests/html/editing/dnd/images/006.html
new file mode 100644
index 0000000000..4a8b159f65
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/006.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>dataURL image drag and drop from iframe</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+iframe
+ {width:150px;
+ height:150px;
+ border-style:none;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><iframe src="helper-drag-me-data-url-image.xhtml">XHTML with image</iframe></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+/>
+<script>
+async function test() {
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const iframe = document.querySelector('iframe');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ addImage(event);
+ var img = document.querySelector('img');
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+ dragDropTest(iframe, div, onDropCallBack, 'Dragging a dataURL image from an iframe to a div should copy it there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/007.html b/testing/web-platform/tests/html/editing/dnd/images/007.html
new file mode 100644
index 0000000000..afacc9205c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/007.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>dataURL image drag and drop to iframe</title>
+<style type="text/css">
+iframe
+ {width:160px;
+ height:160px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<p><iframe src="helper-drop-image-here.xhtml">XHTML with image</iframe></p>
+<script>
+async function test () {
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const iframe = document.querySelector('iframe');
+ const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
+ const div = innerDoc.querySelector('div');
+ function onDropCallBack(event) {
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+ dragDropTest(img, div, onDropCallBack, 'Dragging a dataURL image to an iframe should copy it there', iframe);
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/008.html b/testing/web-platform/tests/html/editing/dnd/images/008.html
new file mode 100644
index 0000000000..2747c60ff6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/008.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>dataURL image drag and drop between iframes</title>
+<style type="text/css">
+iframe
+ {width:160px;
+ height:160px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-data-url-image.xhtml">XHTML with image</iframe></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<p><iframe src="helper-drop-image-here.xhtml" id="iframe-2">XHTML with image</iframe></p>
+<script>
+async function test() {
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const iframe = document.querySelector('iframe');
+ const iframeTwo = document.getElementById('iframe-2');
+ const innerDoc = iframeTwo.contentDocument || iframeTwo.contentWindow.document;
+ const div = innerDoc.querySelector('div');
+ function onDropCallBack(event) {
+ const innerDoc1 = iframe.contentDocument || iframe.contentWindow.document;
+ const img = innerDoc1.querySelector('img');
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+ dragDropTest(iframe, div, onDropCallBack, 'Dragging a dataURL image within an iframe to another iframe should copy it there', iframeTwo);
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/009.html b/testing/web-platform/tests/html/editing/dnd/images/009.html
new file mode 100644
index 0000000000..ebb252ab0b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/009.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Image drag and drop from iframe</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+iframe
+ {width:150px;
+ height:150px;
+ border-style:none;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><iframe src="helper-circle.xhtml">XHTML with image</iframe></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+/>
+<script>
+async function test() {
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const iframe = document.querySelector('iframe');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ addImage(event);
+ var img = document.querySelector('img');
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+ dragDropTest(iframe, div, onDropCallBack, 'Dragging an image within an iframe to a div should copy it there');
+};
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/010.html b/testing/web-platform/tests/html/editing/dnd/images/010.html
new file mode 100644
index 0000000000..d41b36c261
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/010.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Image drag and drop to iframe</title>
+<style type="text/css">
+p:first-child
+ {padding-left:20px;}
+iframe
+ {width:160px;
+ height:160px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><img src="../resources/circle.png" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<p><iframe src="helper-drop-image-here.xhtml">XHTML with image</iframe></p>
+<script>
+async function test() {
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const iframe = document.querySelector('iframe');
+ const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
+ const div = innerDoc.querySelector('div');
+ function onDropCallBack(event) {
+ assert_equals(img.src, event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ return true;
+ }
+ dragDropTest(img, div, onDropCallBack, 'Dragging an image to an iframe should copy it there', iframe)
+};
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/011.xhtml b/testing/web-platform/tests/html/editing/dnd/images/011.xhtml
new file mode 100644
index 0000000000..905ce2c4f8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/011.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop between iframes</title>
+<style type="text/css">
+iframe
+ {width:160px;
+ height:160px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-circle.xhtml">XHTML with image</iframe></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<p><iframe src="helper-drop-image-here.xhtml">XHTML with image</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/012-1.xhtml b/testing/web-platform/tests/html/editing/dnd/images/012-1.xhtml
new file mode 100644
index 0000000000..3dc4f80641
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/012-1.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/images/012.xhtml b/testing/web-platform/tests/html/editing/dnd/images/012.xhtml
new file mode 100644
index 0000000000..d323d0a02e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/012.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of dataURL image between frames</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="012-1.xhtml"/>
+<frame src="helper-drop-image-here.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/013-1.xhtml b/testing/web-platform/tests/html/editing/dnd/images/013-1.xhtml
new file mode 100644
index 0000000000..7a78528c84
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/013-1.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+</head>
+<body>
+<p><img src="../resources/circle.png" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/images/013.xhtml b/testing/web-platform/tests/html/editing/dnd/images/013.xhtml
new file mode 100644
index 0000000000..ffa7f3b74b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/013.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of image between frames</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="013-1.xhtml"/>
+<frame src="helper-drop-image-here.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/014-1.xhtml b/testing/web-platform/tests/html/editing/dnd/images/014-1.xhtml
new file mode 100644
index 0000000000..13d8e43f98
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/014-1.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ background-color:silver;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="../resources/circle.png" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle from one frame to the silver box in <strong>another</strong> frame. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/014.xhtml b/testing/web-platform/tests/html/editing/dnd/images/014.xhtml
new file mode 100644
index 0000000000..1acb985a8d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/014.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of image between two instances of document</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="014-1.xhtml"/>
+<frame src="014-1.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/015.html b/testing/web-platform/tests/html/editing/dnd/images/015.html
new file mode 100644
index 0000000000..ed1aed28bc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/015.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Dropping image on horizontal scrollbar of a scrollable block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:3em;
+ height:3em;
+ margin-top:1em;
+ font-size:1.5em;
+ white-space:nowrap;
+ overflow-x:scroll;}
+p:first-child
+ {padding-left:1em;}
+img
+ {width:5px;
+ height:5px;}
+</style>
+<script type="application/ecmascript">
+function checkImage(event)
+ {document.querySelector('div').firstChild.nodeValue = (document.querySelector('img').getAttribute('src') == event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''))?'PASS':'FAIL';}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag little square above and drop it on scrollbar below. You should see word PASS when you drop it on scrollbar.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false">↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓</div>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ checkImage(event);
+ assert_true(div.firstChild.nodeValue == 'PASS');
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the image to the horizontal scrollbar within a scrollable block element should copy it there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/016.html b/testing/web-platform/tests/html/editing/dnd/images/016.html
new file mode 100644
index 0000000000..1b02bf876b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/016.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Dropping image on vertical scrollbar of a scrollable block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:2em;
+ height:5em;
+ margin-top:1em;
+ font-size:1.5em;
+ overflow-y:scroll;}
+p:first-child
+ {padding-left:1em;}
+img
+ {width:5px;
+ height:5px;}
+</style>
+<script type="application/ecmascript">
+function checkImage(event)
+ {document.querySelector('div').firstChild.nodeValue = (document.querySelector('img').getAttribute('src') == event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''))?'P A S S':'F A I L';}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag little square above and drop it on scrollbar below. You should see word PASS when you drop it on scrollbar.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false">→ → → → → → → → → → → → → → → → → → → →</div>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ checkImage(event);
+ assert_true(div.firstChild.nodeValue == 'P A S S');
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the image to the vertical scrollbar within a scrollable block element should copy it there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/017.html b/testing/web-platform/tests/html/editing/dnd/images/017.html
new file mode 100644
index 0000000000..bbf85f04f8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/017.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Dropping image on horizontal page scrollbar</title>
+<style type="text/css">
+p:first-child
+ {padding-left:1em;}
+div
+ {position:fixed;
+ bottom:0;
+ left:0;}
+img
+ {width:5px;
+ height:5px;}
+body
+ {width:3000px;}
+</style>
+<script type="application/ecmascript">
+function dragImage()
+ {event.dataTransfer.effectAllowed = 'copy';}
+function dropImage(event)
+ {document.querySelector('div').firstChild.nodeValue = 'PASS';}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="dragImage()"/></p>
+<p>Drag little square above and drop it on vertical scrollbar. Word PASS should appear near scrollbar once you drop it.</p>
+<div>↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓</div>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ dropImage(event);
+ assert_equals('PASS', div.firstChild.nodeValue);
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the image to the horizontal scrollbar should copy it there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/018.html b/testing/web-platform/tests/html/editing/dnd/images/018.html
new file mode 100644
index 0000000000..3fa61b07d7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/018.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Dropping image on vertical page scrollbar</title>
+<style type="text/css">
+p:first-child
+ {padding-left:1em;}
+div
+ {width:1ex;
+ margin-left:auto;}
+img
+ {width:5px;
+ height:5px;}
+body
+ {height:3000px;}
+</style>
+<script type="application/ecmascript">
+function dragImage()
+ {event.dataTransfer.effectAllowed = 'copy';}
+function dropImage(event)
+ {document.querySelector('div').firstChild.nodeValue = 'P A S S';}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="dragImage()"/></p>
+<p>Drag little square above and drop it on vertical scrollbar. Word PASS should appear near scrollbar once you drop it.</p>
+<div>→ → → → → → → → → → → → → → → → → → → → → → → → → → → → → → → → → → → →</div>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const div = document.querySelector('div');
+ function onDropCallBack(event) {
+ dropImage(event);
+ assert_equals('P A S S', div.firstChild.nodeValue);
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the image to the vertical scrollbar should copy it there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/021.html b/testing/web-platform/tests/html/editing/dnd/images/021.html
new file mode 100644
index 0000000000..62476c16f5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/021.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Dropping image on horizontal iframe scrollbar</title>
+<style type="text/css">
+p:first-child
+ {padding-left:1em;}
+iframe
+ {height:3em;
+ width:4em;}
+img
+ {width:5px;
+ height:5px;}
+</style>
+<script type="application/ecmascript">
+function dragImage()
+ {event.dataTransfer.effectAllowed = 'copy';}
+function dropImage(event)
+ {document.querySelector('p + p').firstChild.nodeValue = 'PASS';}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="dragImage()"/></p>
+<p>Drag little square above and drop it on horizontal scrollbar. Word PASS should appear once you drop it.</p>
+<iframe src="helper-drop-horizontal-scrollbar.xhtml">XHTML document</iframe>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const iframe = document.querySelector('iframe');
+ const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
+ const div = innerDoc.querySelector('div');
+ function onDropCallBack(event) {
+ dropImage(event);
+ assert_equals('PASS', document.querySelector('p + p').firstChild.nodeValue);
+ return true;
+ }
+
+ dragDropTest(img, div, onDropCallBack, 'Dragging the image to the horizontal iframe scrollbar should copy it there', iframe);
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/022.xhtml b/testing/web-platform/tests/html/editing/dnd/images/022.xhtml
new file mode 100644
index 0000000000..a85f8c10f1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/022.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropping image on vertical iframe scrollbar</title>
+<style type="text/css">
+p:first-child
+ {padding-left:1em;}
+iframe
+ {height:6em;
+ width:3em;}
+img
+ {width:5px;
+ height:5px;}
+</style>
+<script type="application/ecmascript">
+function dragImage()
+ {event.dataTransfer.effectAllowed = 'copy';}
+function dropImage(event)
+ {document.querySelector('p + p').firstChild.nodeValue = 'PASS';}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="dragImage()" ondragend="dropImage(event)"/></p>
+<p>Drag little square above and drop it on vertical scrollbar. Word PASS should appear once you drop it.</p>
+<iframe src="helper-drop-vertical-scrollbar.xhtml">XHTML document</iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/023.html b/testing/web-platform/tests/html/editing/dnd/images/023.html
new file mode 100644
index 0000000000..4765d792f4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/023.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Dropping image on horizontal object scrollbar</title>
+<style type="text/css">
+p:first-child
+ {padding-left:1em;}
+object
+ {height:3em;
+ width:4em;}
+img
+ {width:5px;
+ height:5px;}
+</style>
+<script type="application/ecmascript">
+function dragImage()
+ {event.dataTransfer.effectAllowed = 'copy';}
+function dropImage(event)
+ {document.querySelector('p + p').firstChild.nodeValue = 'PASS';}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="dragImage()"/></p>
+<p>Drag little square above and drop it on horizontal scrollbar. Word PASS should appear once you drop it.</p>
+<object type="application/xhtml+xml" data="helper-drop-horizontal-scrollbar.xhtml">XHTML document</object>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const object = document.querySelector('object');
+ function onDropCallBack(event) {
+ dropImage(event);
+ assert_equals('PASS', document.querySelector('p + p').firstChild.nodeValue);
+ return true;
+ }
+
+ dragDropTest(img, object, onDropCallBack, 'Dragging the image to the horizontal object scrollbar should copy it there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/024.html b/testing/web-platform/tests/html/editing/dnd/images/024.html
new file mode 100644
index 0000000000..0e695cf1df
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/024.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/test-helper.js"></script>
+<head>
+<title>Dropping image on vertical object scrollbar</title>
+<style type="text/css">
+p:first-child
+ {padding-left:1em;}
+object
+ {height:6em;
+ width:3em;}
+img
+ {width:5px;
+ height:5px;}
+</style>
+<script type="application/ecmascript">
+function dragImage()
+ {event.dataTransfer.effectAllowed = 'copy';}
+function dropImage(event)
+ {document.querySelector('p + p').firstChild.nodeValue = 'PASS';}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="dragImage()"/></p>
+<p>Drag little square above and drop it on vertical scrollbar. Word PASS should appear once you drop it.</p>
+<object type="application/xhtml+xml" data="helper-drop-vertical-scrollbar.xhtml">XHTML document</object>
+<script>
+async function test(){
+ await new Promise(loaded => window.addEventListener("load", loaded));
+ const img = document.querySelector('img');
+ const object = document.querySelector('object');
+ function onDropCallBack(event) {
+ dropImage(event);
+ assert_equals('PASS', document.querySelector('p + p').firstChild.nodeValue);
+ return true;
+ }
+
+ dragDropTest(img, object, onDropCallBack, 'Dragging the image to the vertical object scrollbar should copy it there');
+}
+test();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/025.xhtml b/testing/web-platform/tests/html/editing/dnd/images/025.xhtml
new file mode 100644
index 0000000000..c077aa8b61
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/025.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of image to scrollable container with horizontal scrollbar</title>
+<style type="text/css">
+p + div
+ {height:150px;
+ width:150px;
+ overflow-x:scroll;}
+div[ondragenter]
+ {margin-left:200px;
+ width:105px;
+ min-height:105px;
+ text-align:center;
+ padding:10px;
+ background-color:silver;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div > div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the silver box inside scrollable container below (dragging towards edge of container triggers scrolling).</p>
+<p>It should be copied to the silver box once you drop it there.</p>
+<div>
+ <div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+ />
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/026.xhtml b/testing/web-platform/tests/html/editing/dnd/images/026.xhtml
new file mode 100644
index 0000000000..23063c7b4d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/026.xhtml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of image to scrollable container with vertical scrollbar</title>
+<style type="text/css">
+p + div
+ {height:150px;
+ width:150px;
+ overflow-y:scroll;}
+div[ondragenter]
+ {margin-top:200px;
+ width:105px;
+ min-height:105px;
+ text-align:center;
+ padding:10px;
+ background-color:silver;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div > div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the silver box inside scrollable container below (dragging towards edge of container triggers scrolling).</p>
+<p>It should be copied to the silver box once you drop it there.</p>
+<div>
+↓↓↓↓↓↓↓↓↓
+ <div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+ />
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/027.xhtml b/testing/web-platform/tests/html/editing/dnd/images/027.xhtml
new file mode 100644
index 0000000000..aa40e7e480
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/027.xhtml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of image to scrollable container</title>
+<style type="text/css">
+p + div
+ {height:150px;
+ width:150px;
+ overflow:scroll;}
+div[ondragenter]
+ {margin:200px 0 0 200px;
+ width:105px;
+ min-height:105px;
+ text-align:center;
+ padding:10px;
+ background-color:silver;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div > div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the silver box inside scrollable container below (dragging towards edge of container triggers scrolling).</p>
+<p>It should be copied to the silver box once you drop it there.</p>
+<div>
+↘
+ <div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+ />
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/028.xhtml b/testing/web-platform/tests/html/editing/dnd/images/028.xhtml
new file mode 100644
index 0000000000..fe4474cceb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/028.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop and url alias</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('url'));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above outside the window and then back inside and drop in the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/cross-domain/001-manual.xhtml b/testing/web-platform/tests/html/editing/dnd/images/cross-domain/001-manual.xhtml
new file mode 100644
index 0000000000..f064633410
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/cross-domain/001-manual.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross-domain image data must not populate the dataTransfer</title>
+<script src="../../resources/crossorigin.sub.js"></script>
+<style type="text/css">
+div {
+ width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;
+}
+</style>
+</head>
+<body>
+<p><img id="image" draggable="true" alt="" width="100" height="100" /></p>
+<p>Drag the navy square above to the box below.</p>
+<div></div>
+<script><![CDATA[
+
+document.getElementsByTagName("img")[0].src = crossOriginUrl("www", "../../resources/100x100-navy.png");
+
+window.onload = function() {
+ var image = document.getElementsByTagName('img')[0], div = document.getElementsByTagName('div')[0], failed = [];
+ div.ondragover = div.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ div.ondrop = image.ondragstart = function(e) {
+ if( e.type == 'dragstart' ) {
+ e.dataTransfer.setData('Text', 'dummy text');
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ for( var i = 0; i < e.dataTransfer.types.length; i++ ) {
+ if( e.dataTransfer.types[i].match(/image\//) ) {
+ failed[failed.length] = e.dataTransfer.types[i];
+ }
+ }
+ if( e.type == 'drop' ) {
+ e.preventDefault();
+ document.getElementsByTagName('p')[1].innerHTML = failed.length ? ( 'FAIL (found ' + failed.join() + ')' ) : 'PASS';
+ }
+ };
+};
+]]></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/helper-circle.xhtml b/testing/web-platform/tests/html/editing/dnd/images/helper-circle.xhtml
new file mode 100644
index 0000000000..1df3ee3fa6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/helper-circle.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+</head>
+<body>
+<p><img src="../resources/circle.png" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/images/helper-drag-me-data-url-image.xhtml b/testing/web-platform/tests/html/editing/dnd/images/helper-drag-me-data-url-image.xhtml
new file mode 100644
index 0000000000..640b42b4df
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/helper-drag-me-data-url-image.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/images/helper-drop-horizontal-scrollbar.xhtml b/testing/web-platform/tests/html/editing/dnd/images/helper-drop-horizontal-scrollbar.xhtml
new file mode 100644
index 0000000000..ca1a677cc8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/helper-drop-horizontal-scrollbar.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<div>↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓</div>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/images/helper-drop-image-here.xhtml b/testing/web-platform/tests/html/editing/dnd/images/helper-drop-image-here.xhtml
new file mode 100644
index 0000000000..78fa33bf09
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/helper-drop-image-here.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/images/helper-drop-vertical-scrollbar.xhtml b/testing/web-platform/tests/html/editing/dnd/images/helper-drop-vertical-scrollbar.xhtml
new file mode 100644
index 0000000000..2c3913bfc3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/images/helper-drop-vertical-scrollbar.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Image drag and drop: helper file</title>
+<style type="text/css">
+div
+ {width:1ex;}
+</style>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false">
+<div>→ → → → → → → → → → → → → → → → → → → →</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactive/frames-1.html b/testing/web-platform/tests/html/editing/dnd/interactive/frames-1.html
new file mode 100644
index 0000000000..b1a4f5fdef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactive/frames-1.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop should allow dragging of iframes and object iframes</title>
+ <style type="text/css">
+html { background: black; color: white; }
+ </style>
+ </head>
+ <body>
+
+ <script type="text/javascript">
+if( self == top ) {
+ document.write('<p>This is a helper file, not a testcase.<\/p>');
+}
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactive/frames.html b/testing/web-platform/tests/html/editing/dnd/interactive/frames.html
new file mode 100644
index 0000000000..ae14232e5a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactive/frames.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop should allow dragging of iframes and object iframes</title>
+ <style type="text/css">
+iframe, object {
+ border: 10px solid orange;
+ background: blue;
+ padding: 10px;
+ height: 100px;
+ width: 100px;
+}
+ </style>
+ </head>
+ <body>
+
+ <p>It should be possible to drag the following two blocks by both their orange and blue borders.</p>
+ <p><iframe draggable="true" src="frames-1.html"></iframe></p>
+ <p><object draggable="true" data="frames-1.html"></object></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactive/object-retention.html b/testing/web-platform/tests/html/editing/dnd/interactive/object-retention.html
new file mode 100644
index 0000000000..d1d2603421
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactive/object-retention.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - variable retention within event handlers</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+ body > div + div {
+ margin-top: 10px;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], fails = [], evs = {};
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ var foo = {};
+ e.dataTransfer.setData('text', foo);
+ if( e.dataTransfer.getData('text') === foo ) {
+ fails[fails.length] = 'object was not cast to string';
+ }
+ evs[e.type] = {};
+ evs[e.type].dataTransfer = e.dataTransfer;
+ evs[e.type].items = e.dataTransfer.items;
+ evs[e.type].types = e.dataTransfer.types;
+ evs[e.type].files = e.dataTransfer.files;
+ //"The same object must be returned each time."
+ if( evs[e.type].dataTransfer !== e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning anything during '+e.type;
+ } else if( evs[e.type].items !== e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning anything during '+e.type;
+ } else if( evs[e.type].types !== e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning anything during '+e.type;
+ } else if( evs[e.type].files !== e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning the same object during '+e.type;
+ }
+ };
+ blue.ondragover = blue.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( !evs[e.type] ) { evs[e.type] = {}; }
+ evs[e.type].dataTransfer = e.dataTransfer;
+ evs[e.type].items = e.dataTransfer.items;
+ evs[e.type].types = e.dataTransfer.types;
+ evs[e.type].files = e.dataTransfer.files;
+ if( evs[e.type].dataTransfer != e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning anything during '+e.type;
+ } else if( evs[e.type].items !== e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning anything during '+e.type;
+ } else if( evs[e.type].types !== e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning anything during '+e.type;
+ } else if( evs[e.type].files !== e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning the same object during '+e.type;
+ }
+ //http://dev.w3.org/html5/spec/dnd.html#datatransfer
+ //"The * attribute must return a * object associated with the DataTransfer object."
+ //Note that it is associated with the DataTransfer object, *not* the data store
+ //http://dev.w3.org/html5/spec/dnd.html#dragevent
+ //"when a user agent is required to fire a DND event named e at an element, using a particular drag data store...
+ //Let dataTransfer be a newly created DataTransfer object associated with the given drag data store."
+ //A new DataTransfer object therefore means a new set of properties, not the same ones as last event
+ if( evs.dragstart.dataTransfer === e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.items && evs.dragstart.items === e.dataTransfer.items ) {
+ fails[fails.length] = '.items is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.types && evs.dragstart.types === e.dataTransfer.types ) {
+ fails[fails.length] = '.types is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.files && evs.dragstart.files === e.dataTransfer.files ) {
+ fails[fails.length] = '.files is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ evs[e.type] = {};
+ evs[e.type].dataTransfer = e.dataTransfer;
+ evs[e.type].items = e.dataTransfer.items;
+ evs[e.type].types = e.dataTransfer.types;
+ evs[e.type].files = e.dataTransfer.files;
+ if( evs[e.type].dataTransfer !== e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning anything during '+e.type;
+ } else if( evs[e.type].items !== e.dataTransfer.items ) {
+ fails[fails.length] = '.items is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning anything during '+e.type;
+ } else if( evs[e.type].types !== e.dataTransfer.types ) {
+ fails[fails.length] = '.types is not returning the same object during '+e.type;
+ }
+ if( !e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning anything during '+e.type;
+ } else if( evs[e.type].files !== e.dataTransfer.files ) {
+ fails[fails.length] = '.files is not returning the same object during '+e.type;
+ }
+ if( evs.dragstart.dataTransfer === e.dataTransfer ) {
+ fails[fails.length] = '.dataTransfer is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.items && evs.dragstart.items === e.dataTransfer.items ) {
+ fails[fails.length] = '.items is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.types && evs.dragstart.types === e.dataTransfer.types ) {
+ fails[fails.length] = '.types is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ if( e.dataTransfer.files && evs.dragstart.files === e.dataTransfer.files ) {
+ fails[fails.length] = '.files is returning the same object during '+e.type+' as it did during dragstart';
+ }
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL:<br>' + fails.join('<br>') ) : 'PASS';
+ };
+};
+</script>
+
+<p>Drag the orange square onto the blue square. Fail if this text does not change.</p>
+<div draggable="true"></div>
+<div></div>
+
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactive/plugins.html b/testing/web-platform/tests/html/editing/dnd/interactive/plugins.html
new file mode 100644
index 0000000000..70eb97bf42
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactive/plugins.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop should not remove styling of plugin object elements</title>
+ <style type="text/css">
+div {
+ border: 10px solid orange;
+ background: yellow;
+ padding: 10px;
+ height: 140px;
+ width: 140px;
+}
+object {
+ border: 10px solid gray;
+ background: fuchsia;
+ padding: 10px;
+ height: 100px;
+ width: 100px;
+}
+ </style>
+ </head>
+ <body>
+
+ <p>Drag the following block by the orange border. The drag placeholder should contain all inner borders, but may optionally show white or pink instead of the navy square.</p>
+ <div draggable="true" ondragstart="event.dataTransfer.setData('Text','dummy text');"><object data="../resources/boxnavy.swf"></object></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/001.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/001.html
new file mode 100644
index 0000000000..25acccbef9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/001.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Select input inside draggable element</title>
+ <style type="text/css">
+select { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should open and select items in the dropdown, and should <strong>not</strong> drag the block or text.</p>
+ <div draggable="true">
+ <select>
+ <option>Option 1</option>
+ <option>Option 2</option>
+ <option>Option 3</option>
+ </select>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/002.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/002.html
new file mode 100644
index 0000000000..146676e5d0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/002.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Select multiple input inside draggable element</title>
+ <style type="text/css">
+select { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should open and select items in the dropdown, and should <strong>not</strong> drag the block or text.</p>
+ <div draggable="true">
+ <select multiple size="3">
+ <option>Option 1</option>
+ <option>Option 2</option>
+ <option>Option 3</option>
+ </select>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/003.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/003.html
new file mode 100644
index 0000000000..76ba256831
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/003.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable select</title>
+ <style type="text/css">
+select { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('select')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should open and select items in the dropdown, and should <strong>not</strong> drag the block or text.</p>
+ <div>
+ <select draggable="true">
+ <option>Option 1</option>
+ <option>Option 2</option>
+ <option>Option 3</option>
+ </select>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/004.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/004.html
new file mode 100644
index 0000000000..9bd02771f0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/004.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable select multiple</title>
+ <style type="text/css">
+select { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('select')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should open and select items in the dropdown, and should <strong>not</strong> drag the block or text.</p>
+ <div>
+ <select multiple size="3" draggable="true">
+ <option>Option 1</option>
+ <option>Option 2</option>
+ <option>Option 3</option>
+ </select>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/005.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/005.html
new file mode 100644
index 0000000000..c08a5104db
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/005.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Text input inside draggable element</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should focus the dummy text. Use your mouse to select part of the dummy text. It should <strong>not</strong> drag the block or text in either case.</p>
+ <div draggable="true">
+ <input value="Dummy text">
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/006.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/006.html
new file mode 100644
index 0000000000..46b1dc58d4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/006.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable text input</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('input')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should focus the dummy text. Use your mouse to select part of the dummy text. It should <strong>not</strong> drag the block or text in either case.</p>
+ <div>
+ <input value="Dummy text" draggable="true">
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/007.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/007.html
new file mode 100644
index 0000000000..51d558ccf2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/007.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Textarea inside draggable element</title>
+ <style type="text/css">
+textarea { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It may optionally focus the dummy text. Use your mouse to select part of the dummy text, moving the mouse vertically and horizontally. It should select the text. Try to drag the input's scrollbar thumb. It should <strong>not</strong> drag the block or text in any case.</p>
+ <div draggable="true">
+<textarea rows="5" cols="50" wrap="off">Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</textarea>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/008.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/008.html
new file mode 100644
index 0000000000..780c82de20
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/008.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable textarea</title>
+ <style type="text/css">
+textarea { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('textarea')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It may optionally focus the dummy text. Use your mouse to select part of the dummy text, moving the mouse vertically and horizontally. It should select the text. Try to drag the input's scrollbar thumb. It should <strong>not</strong> drag the block or text in any case.</p>
+ <div>
+<textarea rows="5" cols="50" wrap="off">Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+Dummy text
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</textarea>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/009.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/009.html
new file mode 100644
index 0000000000..08859bbdae
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/009.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Input button inside draggable element</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p>
+ <div draggable="true">
+ <input type="button" value="Dummy text">
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/010.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/010.html
new file mode 100644
index 0000000000..48d67943d8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/010.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable input button</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('input')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p>
+ <div>
+ <input type="button" value="Dummy text" draggable="true">
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/011.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/011.html
new file mode 100644
index 0000000000..315307a5ef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/011.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Button inside draggable element</title>
+ <style type="text/css">
+button { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p>
+ <div draggable="true">
+ <button>Dummy text</button>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/012.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/012.html
new file mode 100644
index 0000000000..46a3724024
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/012.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable button</title>
+ <style type="text/css">
+button { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('button')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p>
+ <div>
+ <button draggable="true">Dummy text</button>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/015.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/015.html
new file mode 100644
index 0000000000..28e8966bd4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/015.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Contenteditable inside draggable element</title>
+ <style type="text/css">
+div div { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the text or the orange block. Use your mouse to select the dummy text. It should <strong>not</strong> drag the text or the orange block.</p>
+ <div draggable="true">
+ <div contenteditable="true">Dummy text</div>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/016.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/016.html
new file mode 100644
index 0000000000..53145787b3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/016.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable contenteditable element</title>
+ <style type="text/css">
+div div { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[1].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the text or the orange block. Use your mouse to select the dummy text. It should <strong>not</strong> drag the text or the orange block.</p>
+ <div>
+ <div draggable="true" contenteditable="true">Dummy text</div>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/017.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/017.html
new file mode 100644
index 0000000000..b99e554682
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/017.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Scrollable element inside draggable element</title>
+ <style type="text/css">
+div div { width: 300px; height: 100px; overflow: auto; border: 1px solid orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>It should be possible to drag the scrollbar thumbs of the box below without dragging the whole box.</p>
+ <div draggable="true">
+ <div>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</div>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/018.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/018.html
new file mode 100644
index 0000000000..57a5aaf417
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/018.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Draggable scrollable element</title>
+ <style type="text/css">
+div div { width: 300px; height: 100px; overflow: auto; border: 1px solid orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[1].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>It should be possible to drag the scrollbar thumbs of the box below without dragging the whole box.</p>
+ <div>
+ <div draggable="true">Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>Dummy text<br>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</div>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/019.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/019.html
new file mode 100644
index 0000000000..79d3c4ec47
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/019.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Disabled text input with inside draggable element</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. Use your mouse to attempt to select part of the dummy text. It should <strong>not</strong> drag the block or text in either case.</p>
+ <div draggable="true">
+ <input value="Dummy text" disabled>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/020.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/020.html
new file mode 100644
index 0000000000..a64c74d05b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/020.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Disabled draggable text input</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('input')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. Use your mouse to attempt to select part of the dummy text. It should <strong>not</strong> drag the block or text in either case.</p>
+ <div>
+ <input value="Dummy text" draggable="true" disabled>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/021.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/021.html
new file mode 100644
index 0000000000..eee0efaef2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/021.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Readonly text input with inside draggable element</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. Use your mouse to attempt to select part of the dummy text. It should <strong>not</strong> drag the block or text in either case.</p>
+ <div draggable="true">
+ <input value="Dummy text" readonly>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/022.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/022.html
new file mode 100644
index 0000000000..48d85d5f42
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/interactiveelements/022.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Readonly draggable text input</title>
+ <style type="text/css">
+input { border: 1px solid orange; border-top-width: 20px; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('input')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Press your mouse button down on the orange block and drag downwards. Use your mouse to attempt to select part of the dummy text. It should <strong>not</strong> drag the block or text in either case.</p>
+ <div>
+ <input value="Dummy text" draggable="true" readonly>
+ </div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/media/001.xhtml b/testing/web-platform/tests/html/editing/dnd/media/001.xhtml
new file mode 100644
index 0000000000..28984b5448
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/media/001.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Video drag and drop</title>
+<style type="text/css">
+canvas
+ {background-color:silver;}
+</style>
+<script type="application/ecmascript">
+var draggedFrame = 'data:text/plain,FAIL';
+function dropIt(event)
+ {document.querySelector('p + p').firstChild.nodeValue = (draggedFrame == event.dataTransfer.getData('text/uri-list'))?'PASS':'FAIL';}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ var canvas = document.querySelector('canvas'),
+ c = canvas.getContext('2d');
+ c.drawImage(document.querySelector('video'),0,0,640,360);
+ draggedFrame = canvas.toDataURL('image/png');
+ event.dataTransfer.setData('text/uri-list',draggedFrame);}
+</script>
+<script src="/common/media.js"></script>
+</head>
+<body dropzone="copy string:text/uri-list" ondrop="dropIt(event)">
+<p>
+ <video draggable="true" ondragstart="start(event)" controls="true"/>
+ <script>
+ var video = document.querySelector('video');
+ video.src = getVideoURI('/media/movie_5');
+ </script>
+</p>
+<p>Drag video and drop it somewhere on the page. Dragged frame should be copied to the canvas below and you should see word PASS once you drop video.</p>
+<p>
+ <canvas width="640" height="360">Canvas</canvas>
+</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/000.html b/testing/web-platform/tests/html/editing/dnd/microdata/000.html
new file mode 100644
index 0000000000..b6e641d08f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/000.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - no microdata for no itemscope</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items collection'; }
+ if( md.items.length != 0 ) { return 'unexpected items found'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true'></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/001.html b/testing/web-platform/tests/html/editing/dnd/microdata/001.html
new file mode 100644
index 0000000000..3f13565868
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/001.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for non looping simple drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.appendChild( makeEl('meta',{itemprop:'foo',content:'test'}) );
+ orange.appendChild( makeEl('audio',{itemprop:'bar',src:'test'},'fail') );
+ orange.appendChild( makeEl('embed',{itemprop:'foo',src:'test'}) );
+ orange.appendChild( makeEl('iframe',{itemprop:'bar',src:'test'},'fail') );
+ orange.appendChild( makeEl('img',{itemprop:'foo',src:'test'}) );
+ orange.appendChild( makeEl('source',{itemprop:'bar',src:'test'}) );
+ orange.appendChild( makeEl('track',{itemprop:'foo',src:'test'}) );
+ orange.appendChild( makeEl('video',{itemprop:'bar',src:'test'},'fail') );
+ orange.appendChild( makeEl('a',{itemprop:'foo',href:'test'},'fail') );
+ orange.appendChild( makeEl('area',{itemprop:'bar',href:'test'}) );
+ orange.appendChild( makeEl('link',{itemprop:'foo',href:'test'}) );
+ orange.appendChild( makeEl('object',{itemprop:'bar',data:'test'},'fail') );
+ orange.appendChild( makeEl('time',{itemprop:'foo'},'fail') );
+ orange.appendChild( makeEl('time',{itemprop:'baz',datetime:'test'},'fail') );
+ orange.appendChild( makeEl('div',{itemprop:'baz'},'test') );
+ orange.appendChild( makeEl('madeuponthespot',{itemprop:'foo'},'test') );
+ orange.appendChild( makeEl('madeuponthespot',{itemprop:'foo',content:'test',src:'test',href:'test',data:'test',datetime:'test',value:'test'},'test') );
+ orange.appendChild( makeEl('input',{itemprop:'foo',value:'test'},'test') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items || md.items.length != 1 ) { return 'no items'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( !md.items[0].properties.bar ) { return 'no properties.bar'; }
+ if( !md.items[0].properties.baz ) { return 'no properties.baz'; }
+ if( md.items[0].properties.foo.length != 10 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 10'; }
+ if( md.items[0].properties.bar.length != 6 ) { return 'properties.bar length '+md.items[0].properties.bar.length+' instead of 6'; }
+ if( md.items[0].properties.baz.length != 2 ) { return 'properties.baz length '+md.items[0].properties.baz.length+' instead of 2'; }
+ for( i = 0; i < 10; i++ ) {
+ if( md.items[0].properties.foo[i] != orange.properties.namedItem('foo').getValues()[i] ) { return 'properties.foo['+i+'] <i>'+md.items[0].properties.foo[i]+'</i> instead of <i>'+orange.properties.namedItem('foo').getValues()[i]+'</i>'; }
+ }
+ for( i = 0; i < 6; i++ ) {
+ if( md.items[0].properties.bar[i] != orange.properties.namedItem('bar').getValues()[i] ) { return 'properties.bar['+i+'] <i>'+md.items[0].properties.bar[i]+'</i> instead of <i>'+orange.properties.namedItem('bar').getValues()[i]+'</i>'; }
+ }
+ for( i = 0; i < 2; i++ ) {
+ if( md.items[0].properties.baz[i] != orange.properties.namedItem('baz').getValues()[i] ) { return 'properties.baz['+i+'] <i>'+md.items[0].properties.baz[i]+'</i> instead of <i>'+orange.properties.namedItem('baz').getValues()[i]+'</i>'; }
+ }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/002.html b/testing/web-platform/tests/html/editing/dnd/microdata/002.html
new file mode 100644
index 0000000000..4e9a5e6de1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/002.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with itemref</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div *, span {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.parentNode.insertBefore( makeEl('span',{itemprop:'foo',id:'id1'},'dummytext1 '), orange );
+ orange.parentNode.appendChild( makeEl('span',{itemprop:'bar',id:'id2'},'dummytext2 ') );
+ orange.parentNode.appendChild( makeEl('span',{itemprop:'foo',id:'id3'},'dummytext3 ') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items || md.items.length != 1 ) { return 'no items'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( !md.items[0].properties.bar ) { return 'no properties.bar'; }
+ if( md.items[0].properties.foo.length != 2 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 2'; }
+ if( md.items[0].properties.bar.length != 1 ) { return 'properties.bar length '+md.items[0].properties.bar.length+' instead of 1'; }
+ for( i = 0; i < 2; i++ ) {
+ if( md.items[0].properties.foo[i] != orange.properties.namedItem('foo').getValues()[i] ) { return 'properties.foo['+i+'] <i>'+md.items[0].properties.foo[i]+'</i> instead of <i>'+orange.properties.namedItem('foo').getValues()[i]+'</i>'; }
+ }
+ if( md.items[0].properties.bar[0] != orange.properties.namedItem('bar').getValues()[0] ) { return 'properties.bar[0] <i>'+md.items[0].properties.bar[i]+'</i> instead of <i>'+orange.properties.namedItem('bar').getValues()[0]+'</i>'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope itemref='id3 id2 id1'></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/003.html b/testing/web-platform/tests/html/editing/dnd/microdata/003.html
new file mode 100644
index 0000000000..1bd0ced463
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/003.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata changes after dragstart</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.appendChild( makeEl('meta',{itemprop:'foo',content:'test'}) );
+ orange.appendChild( makeEl('audio',{itemprop:'bar',src:'test'},'fail') );
+ orange.appendChild( makeEl('embed',{itemprop:'foo',src:'test'}) );
+ orange.appendChild( makeEl('iframe',{itemprop:'bar',src:'test'},'fail') );
+ orange.appendChild( makeEl('img',{itemprop:'foo',src:'test'}) );
+ orange.appendChild( makeEl('source',{itemprop:'bar',src:'test'}) );
+ orange.appendChild( makeEl('track',{itemprop:'foo',src:'test'}) );
+ orange.appendChild( makeEl('video',{itemprop:'bar',src:'test'},'fail') );
+ orange.appendChild( makeEl('a',{itemprop:'foo',href:'test'},'fail') );
+ orange.appendChild( makeEl('area',{itemprop:'bar',href:'test'}) );
+ orange.appendChild( makeEl('link',{itemprop:'foo',href:'test'}) );
+ orange.appendChild( makeEl('object',{itemprop:'bar',data:'test'},'fail') );
+ orange.appendChild( makeEl('time',{itemprop:'foo'},'fail') );
+ orange.appendChild( makeEl('time',{itemprop:'baz',datetime:'test'},'fail') );
+ orange.appendChild( makeEl('div',{itemprop:'baz'},'test') );
+ orange.appendChild( makeEl('madeuponthespot',{itemprop:'foo'},'test') );
+ orange.appendChild( makeEl('madeuponthespot',{itemprop:'foo',content:'test',src:'test',href:'test',data:'test',datetime:'test',value:'test'},'test') );
+ orange.appendChild( makeEl('input',{itemprop:'foo',value:'test'},'test') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ //microdata should be stored and reused after the dragstart event
+ //removing the item should not cause the microdata tohave disappeared when the drop event fires
+ this.itemScope = false;
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err, md = e.dataTransfer.getData('application/microdata+json');
+ orange.itemScope = true;
+ if( err = checkprops(md) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items || md.items.length != 1 ) { return 'no items'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( !md.items[0].properties.bar ) { return 'no properties.bar'; }
+ if( !md.items[0].properties.baz ) { return 'no properties.baz'; }
+ if( md.items[0].properties.foo.length != 10 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 10'; }
+ if( md.items[0].properties.bar.length != 6 ) { return 'properties.bar length '+md.items[0].properties.bar.length+' instead of 6'; }
+ if( md.items[0].properties.baz.length != 2 ) { return 'properties.baz length '+md.items[0].properties.baz.length+' instead of 2'; }
+ for( i = 0; i < 10; i++ ) {
+ if( md.items[0].properties.foo[i] != orange.properties.namedItem('foo').getValues()[i] ) { return 'properties.foo['+i+'] <i>'+md.items[0].properties.foo[i]+'</i> instead of <i>'+orange.properties.namedItem('foo').getValues()[i]+'</i>'; }
+ }
+ for( i = 0; i < 6; i++ ) {
+ if( md.items[0].properties.bar[i] != orange.properties.namedItem('bar').getValues()[i] ) { return 'properties.bar['+i+'] <i>'+md.items[0].properties.bar[i]+'</i> instead of <i>'+orange.properties.namedItem('bar').getValues()[i]+'</i>'; }
+ }
+ for( i = 0; i < 2; i++ ) {
+ if( md.items[0].properties.baz[i] != orange.properties.namedItem('baz').getValues()[i] ) { return 'properties.baz['+i+'] <i>'+md.items[0].properties.baz[i]+'</i> instead of <i>'+orange.properties.namedItem('baz').getValues()[i]+'</i>'; }
+ }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/004.html b/testing/web-platform/tests/html/editing/dnd/microdata/004.html
new file mode 100644
index 0000000000..7ed821e54f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/004.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with nested item as property</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.appendChild( makeEl('div',{itemprop:'foo',itemscope:'itemscope'},'') );
+ orange.appendChild( makeEl('div',{itemprop:'foo',itemscope:'itemscope'},'') );
+ orange.lastChild.appendChild( makeEl('div',{itemprop:'bar'},'test') );
+ orange.appendChild( makeEl('div',{itemprop:'bar',itemscope:'itemscope'},'') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( !md.items[0].properties.bar ) { return 'no properties.bar'; }
+ if( md.items[0].properties.foo.length != 2 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 2'; }
+ if( md.items[0].properties.bar.length != 1 ) { return 'properties.bar length '+md.items[0].properties.bar.length+' instead of 1'; }
+
+ if( !md.items[0].properties.foo[0] ) { return 'properties.foo[0] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>{properties:{}}</i>'; }
+ if( !md.items[0].properties.foo[0].properties ) { return 'properties.foo[0].properties <i>'+md.items[0].properties.foo[0].properties+'</i> instead of <i>{}</i>'; }
+
+ if( !md.items[0].properties.foo[1] ) { return 'properties.foo[1] <i>'+md.items[0].properties.foo[1]+'</i> instead of <i>{properties:{bar:[test]}}</i>'; }
+ if( !md.items[0].properties.foo[1].properties ) { return 'properties.foo[1].properties <i>'+md.items[0].properties.foo[1].properties+'</i> instead of <i>{bar:[test]}</i>'; }
+ if( !md.items[0].properties.foo[1].properties.bar ) { return 'properties.foo[1].properties.bar <i>'+md.items[0].properties.foo[1].properties.bar+'</i> instead of <i>[test]</i>'; }
+ if( !md.items[0].properties.foo[1].properties.bar.length ) { return 'properties.foo[1].properties.bar.length <i>'+md.items[0].properties.foo[1].properties.bar.length+'</i> instead of 1'; }
+ if( md.items[0].properties.foo[1].properties.bar[0] != 'test') { return 'properties.foo[1].properties.bar[0] <i>'+md.items[0].properties.foo[1].properties.bar[0]+'</i> instead of <i>test</i>'; }
+
+ if( !md.items[0].properties.bar[0] ) { return 'properties.bar[0] <i>'+md.items[0].properties.bar[0]+'</i> instead of <i>{properties:{}}</i>'; }
+ if( !md.items[0].properties.bar[0].properties ) { return 'properties.bar[0].properties <i>'+md.items[0].properties.bar[0].properties+'</i> instead of <i>{}</i>'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/005.html b/testing/web-platform/tests/html/editing/dnd/microdata/005.html
new file mode 100644
index 0000000000..ae83f6d5f5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/005.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with nested item as non-property</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.appendChild( makeEl('div',{itemprop:'foo',itemscope:'itemscope'},'') );
+ orange.appendChild( makeEl('div',{itemscope:'itemscope'},'') );
+ orange.lastChild.appendChild( makeEl('div',{itemprop:'bar'},'test') );
+ orange.appendChild( makeEl('div',{itemprop:'bar',itemscope:'itemscope'},'') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/md/#drag-and-drop
+ //"The user agent must take the list of dragged nodes and extract the microdata from those nodes into a JSON form"
+ // http://dev.w3.org/html5/spec/dnd.html#drag-and-drop-processing-model
+ //"the list of dragged nodes contains only the source node, if any."
+ //nested items should only be in the items list if they are in a dragged *selection*
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( !md.items[0].properties.bar ) { return 'no properties.bar'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( md.items[0].properties.bar.length != 1 ) { return 'properties.bar length '+md.items[0].properties.bar.length+' instead of 1'; }
+
+ if( !md.items[0].properties.foo[0] ) { return 'properties.foo[0] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>{properties:{}}</i>'; }
+ if( !md.items[0].properties.foo[0].properties ) { return 'properties.foo[0].properties <i>'+md.items[0].properties.foo[0].properties+'</i> instead of <i>{}</i>'; }
+
+ if( !md.items[0].properties.bar[0] ) { return 'properties.bar[0] <i>'+md.items[0].properties.bar[0]+'</i> instead of <i>{properties:{}}</i>'; }
+ if( !md.items[0].properties.bar[0].properties ) { return 'properties.bar[0].properties <i>'+md.items[0].properties.bar[0].properties+'</i> instead of <i>{}</i>'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/006.html b/testing/web-platform/tests/html/editing/dnd/microdata/006.html
new file mode 100644
index 0000000000..b9e2ba229e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/006.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with type and id</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.appendChild( makeEl('div',{itemprop:'foo',itemscope:'itemscope',itemtype:'http://example.com/',id:'id2',itemid:'http://example.com/bar'},'') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( md.items[0].type != orange.getAttribute('itemtype') ) { return 'items[0].type <i>'+md.items[0].type+'</i> instead of <i>'+orange.getAttribute('itemtype')+'</i>'; }
+ if( md.items[0].id != orange.getAttribute('itemid') ) { return 'items[0].id <i>'+md.items[0].id+'</i> instead of <i>'+orange.getAttribute('itemid')+'</i>'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( !md.items[0].properties.foo[0] ) { return 'properties.foo[0] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>{...}</i>'; }
+ if( md.items[0].properties.foo[0].type != orange.firstChild.getAttribute('itemtype') ) { return 'items[0].properties.foo[0].type <i>'+md.items[0].properties.foo[0].type+'</i> instead of <i>'+orange.firstChild.getAttribute('itemtype')+'</i>'; }
+ if( md.items[0].properties.foo[0].id != orange.firstChild.getAttribute('itemid') ) { return 'items[0].properties.foo[0].id <i>'+md.items[0].properties.foo[0].id+'</i> instead of <i>'+orange.firstChild.getAttribute('itemid')+'</i>'; }
+ if( !md.items[0].properties.foo[0].properties ) { return 'properties.foo[0].properties <i>'+md.items[0].properties.foo[0].properties+'</i> instead of <i>{}</i>'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope id='id1' itemtype='http://example.org/' itemid='http://example.org/foo'></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/007.html b/testing/web-platform/tests/html/editing/dnd/microdata/007.html
new file mode 100644
index 0000000000..e6eb87d82a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/007.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with multiply named item as property</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.appendChild( makeEl('div',{itemprop:'foo bar',itemscope:'itemscope'},'') );
+ orange.lastChild.appendChild( makeEl('div',{itemprop:'baz'},'test') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( !md.items[0].properties.bar ) { return 'no properties.bar'; }
+ if( md.items[0].properties.baz ) { return 'properties.baz should not exist'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( md.items[0].properties.bar.length != 1 ) { return 'properties.bar length '+md.items[0].properties.bar.length+' instead of 1'; }
+
+ if( !md.items[0].properties.foo[0] ) { return 'properties.foo[1] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>{properties:{baz:[test]}}</i>'; }
+ if( !md.items[0].properties.foo[0].properties ) { return 'properties.foo[0].properties <i>'+md.items[0].properties.foo[0].properties+'</i> instead of <i>{baz:[test]}</i>'; }
+ if( !md.items[0].properties.foo[0].properties.baz ) { return 'properties.foo[0].properties.baz <i>'+md.items[0].properties.foo[0].properties.baz+'</i> instead of <i>[test]</i>'; }
+ if( !md.items[0].properties.foo[0].properties.baz.length ) { return 'properties.foo[0].properties.baz.length <i>'+md.items[0].properties.foo[0].properties.baz.length+'</i> instead of 1'; }
+ if( md.items[0].properties.foo[0].properties.baz[0] != 'test') { return 'properties.foo[0].properties.baz[0] <i>'+md.items[0].properties.foo[0].properties.baz[0]+'</i> instead of <i>test</i>'; }
+
+ if( !md.items[0].properties.bar[0] ) { return 'properties.bar[1] <i>'+md.items[0].properties.bar[0]+'</i> instead of <i>{properties:{baz:[test]}}</i>'; }
+ if( !md.items[0].properties.bar[0].properties ) { return 'properties.bar[0].properties <i>'+md.items[0].properties.bar[0].properties+'</i> instead of <i>{baz:[test]}</i>'; }
+ if( !md.items[0].properties.bar[0].properties.baz ) { return 'properties.bar[0].properties.baz <i>'+md.items[0].properties.bar[0].properties.baz+'</i> instead of <i>[test]</i>'; }
+ if( !md.items[0].properties.bar[0].properties.baz.length ) { return 'properties.bar[0].properties.baz.length <i>'+md.items[0].properties.bar[0].properties.baz.length+'</i> instead of 1'; }
+ if( md.items[0].properties.bar[0].properties.baz[0] != 'test') { return 'properties.bar[0].properties.baz[0] <i>'+md.items[0].properties.bar[0].properties.baz[0]+'</i> instead of <i>test</i>'; }
+
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/008.html b/testing/web-platform/tests/html/editing/dnd/microdata/008.html
new file mode 100644
index 0000000000..d4cd797817
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/008.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - no microdata for selection with no items</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ span span {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items collection'; }
+ if( md.items.length != 0 ) { return 'unexpected items found'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div>01<span>23</span>45<span>67</span>89</div>
+
+<p>Use your pointing device to select the text substring "12345678" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/009.html b/testing/web-platform/tests/html/editing/dnd/microdata/009.html
new file mode 100644
index 0000000000..637cabe017
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/009.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection surrounding one item</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ span * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.childNodes[1].appendChild( makeEl('span',{itemprop:'foo'},'test') );
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items || md.items.length != 1 ) { return 'no items'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( md.items[0].properties.foo[0] != orange.childNodes[1].properties.namedItem('foo').getValues()[0] ) { return 'properties.foo[0] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>'+orange.childNodes[1].properties.namedItem('foo').getValues()[0]+'</i>'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div>01<span itemscope>23</span>45<span>67</span>89</div>
+
+<p>Use your pointing device to select the text substring "12345678" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/010.html b/testing/web-platform/tests/html/editing/dnd/microdata/010.html
new file mode 100644
index 0000000000..53a6321a3e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/010.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection surrounding multiple items</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ span * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.childNodes[1].appendChild( makeEl('span',{itemprop:'foo'},'test') );
+ orange.childNodes[3].appendChild( makeEl('span',{itemprop:'bar'},'test') );
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 2 ) { return md.items.length+' items instead of 2'; }
+ if( !md.items[0].properties ) { return 'no items[0].properties'; }
+ if( !md.items[0].properties.foo ) { return 'no items[0].properties.foo'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'items[0].properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( md.items[0].properties.foo[0] != orange.childNodes[1].properties.namedItem('foo').getValues()[0] ) { return 'items[0].properties.foo[0] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>'+orange.childNodes[1].properties.namedItem('foo').getValues()[0]+'</i>'; }
+ if( !md.items[1].properties ) { return 'no items[1].properties'; }
+ if( !md.items[1].properties.bar ) { return 'no items[1].properties.bar'; }
+ if( md.items[1].properties.bar.length != 1 ) { return 'items[1].properties.bar length '+md.items[1].properties.bar.length+' instead of 1'; }
+ if( md.items[1].properties.bar[0] != orange.childNodes[1].properties.namedItem('foo').getValues()[0] ) { return 'items[1].properties.bar[0] <i>'+md.items[1].properties.bar[0]+'</i> instead of <i>'+orange.childNodes[1].properties.namedItem('foo').getValues()[0]+'</i>'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div>01<span itemscope>23</span>45<span itemscope>67</span>89</div>
+
+<p>Use your pointing device to select the text substring "12345678" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/011.html b/testing/web-platform/tests/html/editing/dnd/microdata/011.html
new file mode 100644
index 0000000000..819e5ceab0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/011.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection partially intersecting multiple items</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ span * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.childNodes[1].appendChild( makeEl('span',{itemprop:'foo'},'test') );
+ orange.childNodes[3].appendChild( makeEl('span',{itemprop:'bar'},'test') );
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/spec/dnd.html#list-of-dragged-nodes
+ //"If it is a selection that is being dragged, then the list of dragged nodes contains, in tree order, every node that is partially or completely included in the selection (including all their ancestors)."
+ //given this "ancestors" situation, what is to stop every item in the body from being included? oversight perhaps? tests 18-20 cover this more extensively
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 2 ) { return md.items.length+' items instead of 2'; }
+ if( !md.items[0].properties ) { return 'no items[0].properties'; }
+ if( !md.items[0].properties.foo ) { return 'no items[0].properties.foo'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'items[0].properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( md.items[0].properties.foo[0] != orange.childNodes[1].properties.namedItem('foo').getValues()[0] ) { return 'items[0].properties.foo[0] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>'+orange.childNodes[1].properties.namedItem('foo').getValues()[0]+'</i>'; }
+ if( !md.items[1].properties ) { return 'no items[1].properties'; }
+ if( !md.items[1].properties.bar ) { return 'no items[1].properties.bar'; }
+ if( md.items[1].properties.bar.length != 1 ) { return 'items[1].properties.bar length '+md.items[1].properties.bar.length+' instead of 1'; }
+ if( md.items[1].properties.bar[0] != orange.childNodes[1].properties.namedItem('foo').getValues()[0] ) { return 'items[1].properties.bar[0] <i>'+md.items[1].properties.bar[0]+'</i> instead of <i>'+orange.childNodes[1].properties.namedItem('foo').getValues()[0]+'</i>'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div>01<span itemscope>23</span>45<span itemscope>67</span>89</div>
+
+<p>Use your pointing device to select the text substring "3456" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/012.html b/testing/web-platform/tests/html/editing/dnd/microdata/012.html
new file mode 100644
index 0000000000..307d610e90
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/012.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection surrounding nested property items</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ span * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.childNodes[1].appendChild( makeEl('span',{itemscope:'itemscope',itemprop:'foo'},'test') );
+ orange.childNodes[1].lastChild.appendChild( makeEl('span',{itemprop:'bar'},'test') );
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ //items should be represented at the top level only if they do not have the itemprop attribute
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( !md.items[0].properties ) { return 'no items[0].properties'; }
+ if( !md.items[0].properties.foo ) { return 'no items[0].properties.foo'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'items[0].properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( !md.items[0].properties.foo[0].properties ) { return 'items[1].properties.foo[0].properties <i>'+md.items[0].properties.foo[0].properties+'</i> instead of <i>{bar:[test]}</i>'; }
+ if( !md.items[0].properties.foo[0].properties.bar ) { return 'items[1].properties.foo[0].properties.bar <i>'+md.items[0].properties.foo[0].properties.bar+'</i> instead of <i>[test]</i>'; }
+ if( md.items[0].properties.foo[0].properties.bar.length != 1 ) { return 'items[1].properties.foo[0].properties.bar.length <i>'+md.items[0].properties.foo[0].properties.bar.length+'</i> instead of 1'; }
+ if( md.items[0].properties.foo[0].properties.bar[0] != 'test') { return 'items[1].properties.foo[0].properties.bar[0] <i>'+md.items[0].properties.foo[0].properties.bar[0]+'</i> instead of <i>test</i>'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div>01<span itemscope>23</span>45<span itemscope itemprop="baz">67</span>89</div>
+
+<p>Use your pointing device to select the text substring "12345678" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/013.html b/testing/web-platform/tests/html/editing/dnd/microdata/013.html
new file mode 100644
index 0000000000..000a9d7f05
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/013.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection surrounding nested non-property items</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ span * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.childNodes[1].appendChild( makeEl('span',{itemscope:'itemscope'},'test') );
+ orange.childNodes[1].lastChild.appendChild( makeEl('span',{itemprop:'bar'},'test') );
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ //*all* items should be represented at the top level, even if they are nested
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 2 ) { return md.items.length+' items instead of 2'; }
+ if( !md.items[0].properties ) { return 'no items[0].properties'; }
+ if( md.items[0].properties.bar ) { return 'unexpected items[0].properties.bar'; }
+
+ if( !md.items[1].properties ) { return 'no items[1].properties'; }
+ if( !md.items[1].properties.bar ) { return 'no items[1].properties.bar'; }
+ if( md.items[1].properties.bar.length != 1 ) { return 'items[1].properties.bar length '+md.items[1].properties.bar.length+' instead of 1'; }
+ if( md.items[1].properties.bar[0] != 'test' ) { return 'items[1].properties.bar[0] <i>'+md.items[1].properties.bar[0]+'</i> instead of <i>test</i>'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div>01<span itemscope>23</span>45<span>67</span>89</div>
+
+<p>Use your pointing device to select the text substring "12345678" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/014.html b/testing/web-platform/tests/html/editing/dnd/microdata/014.html
new file mode 100644
index 0000000000..785c059a31
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/014.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with sibling itemref loop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div *, span {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/md/#extracting-json
+ //"check if the element is a top-level microdata item, and if it is"
+ //as it has itemprop, it is not a top-level item, and should be ignored
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 0 ) { return md.items.length+' items instead of 0'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope itemprop='foo' id='id1' itemref='id2'></div><div id='id2' itemprop='bar' itemscope itemref='id1'></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/015.html b/testing/web-platform/tests/html/editing/dnd/microdata/015.html
new file mode 100644
index 0000000000..36f2643293
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/015.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with parent itemref loop</title>
+<style>
+ blockquote > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ blockquote > div *, span {
+ display: none;
+ }
+ blockquote > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ blockquote > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/md/#extracting-json
+ //"check if the element is a top-level microdata item, and if it is"
+ //as it has itemprop, it is not a top-level item, and should be ignored
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 0 ) { return md.items.length+' items instead of 0'; }
+ return '';
+}
+
+</script>
+
+<blockquote id='id1' itemprop='bar' itemscope>
+<div draggable='true' itemscope itemprop='foo' itemref='id1'></div><div></div><div></div>
+</blockquote>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/016.html b/testing/web-platform/tests/html/editing/dnd/microdata/016.html
new file mode 100644
index 0000000000..153098bf89
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/016.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with nested sibling itemref loop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div *, span {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/md/#extracting-json
+ //"If value is an item, then: If value is in memory, then let value be the string "ERROR"."
+ /*
+Loop detection happens only after the loop has been created, at which point it returns a property value
+of "ERROR" instead of the value which has already been encountered on the stringifying stack.
+Should create the following construct:
+{
+ items:[
+ {
+ properties:{
+ foo:[
+ {
+ properties:{
+ bar:[
+ {
+ properties:{
+ foo:[
+ "ERROR"
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ],
+ bar:[
+ {
+ properties:{
+ foo:[
+ {
+ properties:{
+ bar:[
+ "ERROR"
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
+ */
+
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( !md.items[0].properties ) { return 'no items[0].properties'; }
+ if( !md.items[0].properties.foo ) { return 'no items[0].properties.foo'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'items[0].properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( !md.items[0].properties.foo[0].properties ) { return 'items[1].properties.foo[0].properties <i>'+md.items[0].properties.foo[0].properties+'</i> instead of object'; }
+ if( !md.items[0].properties.foo[0].properties.bar ) { return 'items[1].properties.foo[0].properties.bar <i>'+md.items[0].properties.foo[0].properties.bar+'</i> instead of array'; }
+ if( md.items[0].properties.foo[0].properties.bar.length != 1 ) { return 'items[1].properties.foo[0].properties.bar.length <i>'+md.items[0].properties.foo[0].properties.bar.length+'</i> instead of 1'; }
+ if( !md.items[0].properties.foo[0].properties.bar[0].properties ) { return 'items[1].properties.foo[0].properties.bar[0].properties <i>'+md.properties.foo[0].properties.bar[0].properties+'</i> instead of object'; }
+ if( !md.items[0].properties.foo[0].properties.bar[0].properties.foo ) { return 'items[1].properties.foo[0].properties.bar[0].properties.foo <i>'+md.items[0].properties.foo[0].properties.bar[0].properties.foo+'</i> instead of array'; }
+ if( md.items[0].properties.foo[0].properties.bar[0].properties.foo.length != 1 ) { return 'items[1].properties.foo[0].properties.bar[0].properties.foo.length <i>'+md.items[0].properties.foo[0].properties.bar[0].properties.foo.length+'</i> instead of 1'; }
+ if( md.items[0].properties.foo[0].properties.bar[0].properties.foo[0] != 'ERROR' ) { return 'items[1].properties.foo[0].properties.bar[0].properties.foo.length <i>'+md.items[0].properties.foo[0].properties.bar[0].properties.foo[0]+'</i> instead of <i>ERROR</a>'; }
+
+ if( !md.items[0].properties.bar ) { return 'no items[0].properties.bar'; }
+ if( md.items[0].properties.bar.length != 1 ) { return 'items[0].properties.bar length '+md.items[0].properties.bar.length+' instead of 1'; }
+ if( !md.items[0].properties.bar[0].properties ) { return 'items[1].properties.bar[0].properties <i>'+md.items[0].properties.bar[0].properties+'</i> instead of object'; }
+ if( !md.items[0].properties.bar[0].properties.foo ) { return 'items[1].properties.bar[0].properties.foo <i>'+md.items[0].properties.bar[0].properties.foo+'</i> instead of array'; }
+ if( md.items[0].properties.bar[0].properties.foo.length != 1 ) { return 'items[1].properties.bar[0].properties.foo.length <i>'+md.items[0].properties.bar[0].properties.foo.length+'</i> instead of 1'; }
+ if( !md.items[0].properties.bar[0].properties.foo[0].properties ) { return 'items[1].properties.bar[0].properties.foo[0].properties <i>'+md.properties.bar[0].properties.foo[0].properties+'</i> instead of object'; }
+ if( !md.items[0].properties.bar[0].properties.foo[0].properties.bar ) { return 'items[1].properties.bar[0].properties.foo[0].properties.bar <i>'+md.items[0].properties.bar[0].properties.foo[0].properties.bar+'</i> instead of array'; }
+ if( md.items[0].properties.bar[0].properties.foo[0].properties.bar.length != 1 ) { return 'items[1].properties.bar[0].properties.foo[0].properties.bar.length <i>'+md.items[0].properties.bar[0].properties.foo[0].properties.bar.length+'</i> instead of 1'; }
+ if( md.items[0].properties.bar[0].properties.foo[0].properties.bar[0] != 'ERROR' ) { return 'items[1].properties.bar[0].properties.foo[0].properties.bar.length <i>'+md.items[0].properties.bar[0].properties.foo[0].properties.bar[0]+'</i> instead of <i>ERROR</a>'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope><div itemscope itemprop='foo' id='id1' itemref='id2'></div><div itemscope itemprop='bar' id='id2' itemref='id1'></div></div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/017.html b/testing/web-platform/tests/html/editing/dnd/microdata/017.html
new file mode 100644
index 0000000000..dab270e643
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/017.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata with nested parent itemref loop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div *, span {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/md/#extracting-json
+ //"If value is an item, then: If value is in memory, then let value be the string "ERROR"."
+ /*
+Loop detection happens only after the loop has been created, at which point it returns a property value
+of "ERROR" instead of the value which has already been encountered on the stringifying stack.
+Should create the following construct:
+{
+ items:[
+ {
+ properties:{
+ foo:[
+ {
+ properties:{
+ bar:[
+ {
+ properties:{
+ foo:[
+ "ERROR"
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
+ */
+
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( !md.items[0].properties ) { return 'no items[0].properties'; }
+ if( !md.items[0].properties.foo ) { return 'no items[0].properties.foo'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'items[0].properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( !md.items[0].properties.foo[0].properties ) { return 'items[1].properties.foo[0].properties <i>'+md.items[0].properties.foo[0].properties+'</i> instead of object'; }
+ if( !md.items[0].properties.foo[0].properties.bar ) { return 'items[1].properties.foo[0].properties.bar <i>'+md.items[0].properties.foo[0].properties.bar+'</i> instead of array'; }
+ if( md.items[0].properties.foo[0].properties.bar.length != 1 ) { return 'items[1].properties.foo[0].properties.bar.length <i>'+md.items[0].properties.foo[0].properties.bar.length+'</i> instead of 1'; }
+ if( !md.items[0].properties.foo[0].properties.bar[0].properties ) { return 'items[1].properties.foo[0].properties.bar[0].properties <i>'+md.properties.foo[0].properties.bar[0].properties+'</i> instead of object'; }
+ if( !md.items[0].properties.foo[0].properties.bar[0].properties.foo ) { return 'items[1].properties.foo[0].properties.bar[0].properties.foo <i>'+md.items[0].properties.foo[0].properties.bar[0].properties.foo+'</i> instead of array'; }
+ if( md.items[0].properties.foo[0].properties.bar[0].properties.foo.length != 1 ) { return 'items[1].properties.foo[0].properties.bar[0].properties.foo.length <i>'+md.items[0].properties.foo[0].properties.bar[0].properties.foo.length+'</i> instead of 1'; }
+ if( md.items[0].properties.foo[0].properties.bar[0].properties.foo[0] != 'ERROR' ) { return 'items[1].properties.foo[0].properties.bar[0].properties.foo.length <i>'+md.items[0].properties.foo[0].properties.bar[0].properties.foo[0]+'</i> instead of <i>ERROR</a>'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope>
+ <span id='id1' itemprop='foo' itemscope><span itemscope itemprop='bar' itemref='id1'></span></span>
+</div><div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/018.html b/testing/web-platform/tests/html/editing/dnd/microdata/018.html
new file mode 100644
index 0000000000..357ba4f0fe
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/018.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection partially intersecting a single item</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/spec/dnd.html#list-of-dragged-nodes
+ //"If it is a selection that is being dragged, then the list of dragged nodes contains, in tree order, every node that is partially or completely included in the selection (including all their ancestors)."
+ //this test checks that the parent of the text node is included
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return md.items.length+' items instead of 1'; }
+ if( md.items[0].id != 'http://example.com/item1' ) { return 'items[0].id incorrect'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div itemscope itemid="http://example.com/item1">abc</div>
+
+<p>Use your pointing device to select the text substring "b" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/019.html b/testing/web-platform/tests/html/editing/dnd/microdata/019.html
new file mode 100644
index 0000000000..11525214e2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/019.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection partially intersecting nested items</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 200px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/spec/dnd.html#list-of-dragged-nodes
+ //"If it is a selection that is being dragged, then the list of dragged nodes contains, in tree order, every node that is partially or completely included in the selection (including all their ancestors)."
+ //this test checks that all ancestors of the text node are included
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 2 ) { return md.items.length+' items instead of 2'; }
+ if( md.items[0].id != 'http://example.com/item1' ) { return 'items[0].id incorrect'; }
+ if( md.items[1].id != 'http://example.com/item2' ) { return 'items[1].id incorrect'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div itemscope itemid="http://example.com/item1">a<span itemscope itemid="http://example.com/item2">bcd</span>e</div>
+
+<p>Use your pointing device to select the text substring "d" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/020.html b/testing/web-platform/tests/html/editing/dnd/microdata/020.html
new file mode 100644
index 0000000000..33667224ca
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/020.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata for selection partially intersecting multiple items but not siblings</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 400px;
+ background-color: fuchsia;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ top: 116px;
+ }
+ body > div + div + div {
+ background-color: orange;
+ top: 224px;
+ }
+ p:first-of-type {
+ margin-top: 350px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[2];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.previousSibling.ondragenter = orange.previousSibling.ondragleave = orange.previousSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.previousSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ // http://dev.w3.org/html5/spec/dnd.html#list-of-dragged-nodes
+ //"If it is a selection that is being dragged, then the list of dragged nodes contains, in tree order, every node that is partially or completely included in the selection (including all their ancestors)."
+ //this test checks that all ancestors of the selection's end points are included, and all elements enclosed by the selection are included, but no non-selected siblings of ancestors are included
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 5 ) { return md.items.length+' items instead of 5'; }
+ if( md.items[0].id != 'http://example.com/item1' ) { return 'items[0].id incorrect'; }
+ if( md.items[1].id != 'http://example.com/item4' ) { return 'items[1].id incorrect'; }
+ if( md.items[2].id != 'http://example.com/item5' ) { return 'items[2].id incorrect'; }
+ if( md.items[3].id != 'http://example.com/item6' ) { return 'items[3].id incorrect'; }
+ if( md.items[4].id != 'http://example.com/item7' ) { return 'items[4].id incorrect'; }
+ return '';
+}
+
+</script>
+
+<div></div><div></div><div itemscope itemid="http://example.com/item1"><span itemscope itemid="http://example.com/item2">ab<span itemscope itemid="http://example.com/item3">cd</span>ef</span><span itemscope itemid="http://example.com/item4">gh<span itemscope itemid="http://example.com/item5">ij</span>kl</span><span itemscope itemid="http://example.com/item6">mn<span itemscope itemid="http://example.com/item7">op</span>qr</span><span itemscope itemid="http://example.com/item8">st<span itemscope itemid="http://example.com/item9">uv</span>wx</span></div>
+
+<p>Use your pointing device to select the text substring "hijklmnopq" above, drag the selection upwards to the pink box,
+then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/021.html b/testing/web-platform/tests/html/editing/dnd/microdata/021.html
new file mode 100644
index 0000000000..62e1864bef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/021.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - microdata when addElement is used</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div * {
+ display: none;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+function makeEl(eltype,props,contents) {
+ var elem = document.createElement(eltype);
+ for( var i in props ) {
+ if( props.hasOwnProperty(i) ) {
+ elem.setAttribute(i,props[i]);
+ }
+ }
+ if( contents ) {
+ elem.innerHTML = contents;
+ }
+ return elem;
+}
+
+var orange, fails = [], doneonce = false;
+window.onload = function() {
+ orange = document.getElementsByTagName('div')[0];
+
+ orange.appendChild( makeEl('div',{itemprop:'foo'},'test') );
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.addElement(document.getElementById('side1'));
+ e.dataTransfer.addElement(document.getElementById('side2'));
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ };
+ orange.nextSibling.ondragenter = orange.nextSibling.ondragleave = orange.nextSibling.ondragover =
+ orange.ondrag = orange.ondragend = function(e) {
+ if( e.type == 'dragover' || e.type == 'dragenter' ) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ }
+ if( e.dataTransfer.getData('application/microdata+json') ) {
+ fails[fails.length] = e.type + ' unexpectedly had microdata (security restriction)';
+ }
+ };
+ orange.nextSibling.ondrop = function(e) {
+ var err;
+ if( err = checkprops(e.dataTransfer.getData('application/microdata+json')) ) {
+ fails[fails.length] = e.type + ' ' + err;
+ }
+ if( e.type != 'drop' ) { return; }
+ if( doneonce ) { return; }
+ doneonce = true;
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL: ' + fails.join('<br>') ) : 'PASS';
+ fails = [];
+ }, 200 );
+ };
+
+};
+function checkprops(md) {
+ var i;
+ if( !md ) { return 'no microdata'; }
+ md = JSON.parse(md);
+ if( !md.items ) { return 'no items'; }
+ if( md.items.length != 1 ) { return 'items.length '+md.items.length+' instead of 1'; }
+ if( !md.items[0].properties ) { return 'no properties'; }
+ if( !md.items[0].properties.foo ) { return 'no properties.foo'; }
+ if( md.items[0].properties.bar ) { return 'unexpected properties.bar'; }
+ if( md.items[0].properties.foo.length != 1 ) { return 'properties.foo length '+md.items[0].properties.foo.length+' instead of 1'; }
+ if( md.items[0].properties.foo[0] != orange.properties.namedItem('foo').getValues()[0] ) { return 'properties.foo[0] <i>'+md.items[0].properties.foo[0]+'</i> instead of <i>'+orange.properties.namedItem('foo').getValues()[0]+'</i>'; }
+ return '';
+}
+
+</script>
+
+<div draggable='true' itemscope></div><div></div><div></div>
+<h4 itemscope id="side1">xxx</h4><h4 itemprop="bar" id="side2">yyy</h4>
+
+<p>Use your pointing device to drag the orange box to the pink box, then back to the blue box, and release it.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/microdata/test b/testing/web-platform/tests/html/editing/dnd/microdata/test
new file mode 100644
index 0000000000..42d41d980e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/microdata/test
@@ -0,0 +1,2 @@
+<!doctype html>
+<!-- The tests in this folder point to this file in e.g. <img src> -->
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/001-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/001-1.xhtml
new file mode 100644
index 0000000000..a059966787
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/001-1.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('body').appendChild(c);}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false" ondrop="addImage(event)">
+<p>Drop canvas now, it should be copied to this page once you drop it here.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/001.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/001.xhtml
new file mode 100644
index 0000000000..2e8a2ca65e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/001.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas cross page drag and drop</title>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('canvas').toDataURL('image/png'));
+ window.location = '001-1.xhtml';}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern. You should be redirected to the new page and be able to drop it there.</p>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/002.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/002.xhtml
new file mode 100644
index 0000000000..a3d9f97d04
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/002.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>PNG image cross page drag and drop</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-image-now.xhtml'" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/003.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/003.xhtml
new file mode 100644
index 0000000000..89c53d3812
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/003.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG image cross page drag and drop</title>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-image-now.xhtml'" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="PNG circle"/></p>
+<p>Drag circle above. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/004.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/004.xhtml
new file mode 100644
index 0000000000..9cd885de34
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/004.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection cross page drag and drop</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-now.xhtml'"/></p>
+<p>Drag selected text. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/005.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/005.xhtml
new file mode 100644
index 0000000000..887ae39318
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/005.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection cross page drag and drop</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-now.xhtml'">Drag me</p>
+<p>Drag selected text. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/006.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/006.xhtml
new file mode 100644
index 0000000000..4cb8d3e1c7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/006.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link cross page drag and drop</title>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-link-now.xhtml'">Drag me</a></p>
+<p>Drag link above. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/007-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/007-1.xhtml
new file mode 100644
index 0000000000..821edf8cbd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/007-1.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false" ondrop="event.preventDefault();document.querySelector('p').firstChild.nodeValue = event.dataTransfer.getData('text/plain')">
+<p>Drop box now, you should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/007.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/007.xhtml
new file mode 100644
index 0000000000..a40bc26053
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/007.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop with text/plain data</title>
+<style type="text/css">
+div[ondragstart]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/plain','PASS');window.location = '007-1.xhtml'"/>
+<p>Drag blue box. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/008-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/008-1.xhtml
new file mode 100644
index 0000000000..2ae9f4f935
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/008-1.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false" ondrop="event.preventDefault();document.querySelector('p').firstChild.nodeValue = event.dataTransfer.getData('text/uri-list').substr(16,4)">
+<p>Drop box now, you should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/008.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/008.xhtml
new file mode 100644
index 0000000000..e6dbab4e46
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/008.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop with text/uri-list data</title>
+<style type="text/css">
+div[ondragstart]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/uri-list','data:text/plain,PASS');window.location = '008-1.xhtml'"/>
+<p>Drag blue box. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/009-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/009-1.xhtml
new file mode 100644
index 0000000000..54e23c7643
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/009-1.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="window.location = 'helper-drop-link-now.xhtml'">
+<p>Don't drop link yet, you should be redirected to another page.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/009.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/009.xhtml
new file mode 100644
index 0000000000..b93cdff31d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/009.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop through three pages</title>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = '009-1.xhtml'">Drag me</a></p>
+<p>Drag link above. You should be redirected to the new page.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/010-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/010-1.xhtml
new file mode 100644
index 0000000000..e3d6b4d7d0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/010-1.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {margin:200px 0 0 200px;
+ width:200px;
+ height:100px;
+ color:white;
+ background-color:navy;}
+div[ondragenter]:before
+ {display:block;
+ content:"";
+ border-style:solid;
+ position:relative;
+ top:-50px;
+ left:-200px;
+ border-width:100px;
+ border-color:transparent navy transparent transparent;}
+</style>
+</head>
+<body>
+<p>Drag link to the blue arrow but don't drop it yet. You should be returned back to start page.</p>
+<div ondragenter="event.preventDefault()" ondragover="history.go(-1)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/010.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/010.xhtml
new file mode 100644
index 0000000000..74aabea371
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/010.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop and history navigation roundtrip</title>
+<script type="application/ecmascript">
+function checkLink(event)
+ {document.querySelector('a').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = '010-1.xhtml'" ondragenter="event.preventDefault()" ondrop="checkLink(event)" ondragover="return false">Drag me around and drop here once you return back</a></p>
+<p>Drag link around. You will be redirected to new page. When you return back drop link on itself. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/011-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/011-1.xhtml
new file mode 100644
index 0000000000..95a4015a7a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/011-1.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {margin:200px 0 0 200px;
+ width:200px;
+ height:100px;
+ color:white;
+ background-color:navy;}
+div[ondragenter]:before
+ {display:block;
+ content:"";
+ border-style:solid;
+ position:relative;
+ top:-50px;
+ left:-200px;
+ border-width:100px;
+ border-color:transparent navy transparent transparent;}
+</style>
+</head>
+<body>
+<p>Drag canvas to the blue arrow but don't drop it yet. You should be returned back to start page.</p>
+<div ondragenter="event.preventDefault()" ondragover="history.go(-1)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/011.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/011.xhtml
new file mode 100644
index 0000000000..56955a899f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/011.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas drag and drop and history navigation roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('canvas').toDataURL('image/png'));
+ window.location = '011-1.xhtml'}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondragenter="event.preventDefault()" ondragover="return false" ondrop="addImage(event)">Canvas</canvas>
+</p>
+<p>Drag canvas around. You will be redirected to new page. When you return back drop canvas on itself. It should be duplicated once you drop it.</p>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/012.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/012.xhtml
new file mode 100644
index 0000000000..e9d72dbca5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/012.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>PNG image drag and drop and history navigation roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drag-image-dont-drop.xhtml'" ondragenter="event.preventDefault()" ondrop="addImage(event)" ondragover="return false" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle around. You will be redirected to new page. When you return back drop circle on itself. It should be duplicated once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/013.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/013.xhtml
new file mode 100644
index 0000000000..d263a37862
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/013.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG image drag and drop and history navigation roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drag-image-dont-drop.xhtml'" ondragenter="event.preventDefault()" ondrop="addImage(event)" ondragover="return false" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle around. You will be redirected to new page. When you return back drop circle on itself. It should be duplicated once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/014.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/014.xhtml
new file mode 100644
index 0000000000..5896797754
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/014.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop and history navigation roundtrip</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="window.location = 'helper-drag-selection-dont-drop.xhtml'" ondragenter="event.preventDefault()" ondragover="return false" ondrop="event.preventDefault();this.value = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'"/></p>
+<p>Drag selected text around. You will be redirected to new page. When you return back drop selection on itself. You should see word PASS once you drop it.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/015.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/015.xhtml
new file mode 100644
index 0000000000..cb194a52c0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/015.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop and history navigation roundtrip</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="window.location = 'helper-drag-selection-dont-drop.xhtml'" ondragenter="event.preventDefault()" ondragover="return false" ondrop="document.querySelector('span').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">Drag me</span></p>
+<p>Drag selected text around. You will be redirected to new page. When you return back drop selection on itself. You should see word PASS once you drop it.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/016-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/016-1.xhtml
new file mode 100644
index 0000000000..564469779e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/016-1.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+p
+ {border:solid medium navy;
+ height:200px;
+ padding:1em;
+ margin:0;}
+div
+ {margin:100px;
+ padding:50px;}
+img
+ {display:block;
+ margin:1em;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<div ondragenter="window.location.reload()">
+<p ondragenter="event.stopPropagation()" dropzone="copy string:text/uri-list" ondrop="addImage(event)">Drop canvas here, it should be copied to this page once you drop it here.</p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/016.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/016.xhtml
new file mode 100644
index 0000000000..d70fd4d542
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/016.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during canvas cross page drag and drop</title>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('canvas').toDataURL('image/png'));
+ window.location = '016-1.xhtml';}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern. You should be redirected to the new page and be able to drop it there.</p>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/017.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/017.xhtml
new file mode 100644
index 0000000000..ea2dea268c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/017.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during PNG image cross page drag and drop</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-here-reload.xhtml'" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle above. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/018.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/018.xhtml
new file mode 100644
index 0000000000..b8849c4a35
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/018.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during SVG image cross page drag and drop</title>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-here-reload.xhtml'" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="PNG circle"/></p>
+<p>Drag circle above. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/019.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/019.xhtml
new file mode 100644
index 0000000000..f09041604f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/019.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during text input selection cross page drag and drop</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-selection-here.xhtml'"/></p>
+<p>Drag selected text. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/020.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/020.xhtml
new file mode 100644
index 0000000000..cf653bcad8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/020.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during selection cross page drag and drop</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = 'helper-drop-selection-here.xhtml'">Drag me</p>
+<p>Drag selected text. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/021-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/021-1.xhtml
new file mode 100644
index 0000000000..75af8a61d6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/021-1.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+p
+ {border:solid medium navy;
+ height:200px;
+ padding:1em;
+ margin:0;}
+div
+ {margin:100px;
+ padding:50px;}
+</style>
+<script type="application/ecmascript">
+function checkLink(event)
+ {document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'}
+</script>
+</head>
+<body>
+<div ondragenter="window.location.reload()">
+<p ondragenter="event.stopPropagation()" dropzone="copy string:text/uri-list" ondrop="checkLink(event)">Drop link here, you should see word PASS once you drop it.</p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/021.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/021.xhtml
new file mode 100644
index 0000000000..5c7fad0ca4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/021.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during link cross page drag and drop</title>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location = '021-1.xhtml'">Drag me</a></p>
+<p>Drag link above. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/022-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/022-1.xhtml
new file mode 100644
index 0000000000..1aa795b892
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/022-1.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+p
+ {border:solid medium navy;
+ height:200px;
+ padding:1em;
+ margin:0;}
+div
+ {margin:100px;
+ padding:50px;}
+</style>
+</head>
+<body>
+<div ondragenter="window.location.reload()">
+<p ondragenter="event.stopPropagation()" dropzone="copy string:text/plain" ondrop="event.preventDefault();document.querySelector('p').firstChild.nodeValue = event.dataTransfer.getData('text/plain')">Drop box here, you should see word PASS once you drop it.</p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/022.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/022.xhtml
new file mode 100644
index 0000000000..38cbef3eed
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/022.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during cross page drag and drop with text/plain data</title>
+<style type="text/css">
+div[ondragstart]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/plain','PASS');window.location = '022-1.xhtml'"/>
+<p>Drag blue box. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/023-1.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/023-1.xhtml
new file mode 100644
index 0000000000..e29cdd5d0f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/023-1.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+p
+ {border:solid medium navy;
+ height:200px;
+ padding:1em;
+ margin:0;}
+div
+ {margin:100px;
+ padding:50px;}
+</style>
+</head>
+<body>
+<div ondragenter="window.location.reload()">
+<p ondragenter="event.stopPropagation()" dropzone="copy string:text/uri-list" ondrop="event.preventDefault();document.querySelector('p').firstChild.nodeValue = event.dataTransfer.getData('text/uri-list').substr(16,4)">Drop box here, you should see word PASS once you drop it.</p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/023.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/023.xhtml
new file mode 100644
index 0000000000..4f7b80939b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/023.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during cross page drag and drop with text/uri-list data</title>
+<style type="text/css">
+div[ondragstart]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/uri-list','data:text/plain,PASS');window.location = '023-1.xhtml'"/>
+<p>Drag blue box. You should be redirected to the new page and be able to drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-image-dont-drop.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-image-dont-drop.xhtml
new file mode 100644
index 0000000000..2967d315c1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-image-dont-drop.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {margin:200px 0 0 200px;
+ width:200px;
+ height:100px;
+ color:white;
+ background-color:navy;}
+div[ondragenter]:before
+ {display:block;
+ content:"";
+ border-style:solid;
+ position:relative;
+ top:-50px;
+ left:-200px;
+ border-width:100px;
+ border-color:transparent navy transparent transparent;}
+</style>
+</head>
+<body>
+<p>Drag image to the blue arrow but don't drop it yet. You should be returned back to start page.</p>
+<div ondragenter="event.preventDefault()" ondragover="history.go(-1)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-selection-dont-drop.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-selection-dont-drop.xhtml
new file mode 100644
index 0000000000..db1edb81d6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drag-selection-dont-drop.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {margin:200px 0 0 200px;
+ width:200px;
+ height:100px;
+ color:white;
+ background-color:navy;}
+div[ondragenter]:before
+ {display:block;
+ content:"";
+ border-style:solid;
+ position:relative;
+ top:-50px;
+ left:-200px;
+ border-width:100px;
+ border-color:transparent navy transparent transparent;}
+</style>
+</head>
+<body>
+<p>Drag selection to the blue arrow but don't drop it yet. You should be returned back to start page.</p>
+<div ondragenter="event.preventDefault()" ondragover="history.go(-1)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-here-reload.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-here-reload.xhtml
new file mode 100644
index 0000000000..a046503651
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-here-reload.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+p
+ {border:solid medium navy;
+ height:200px;
+ padding:1em;
+ margin:0;}
+div
+ {margin:100px;
+ padding:50px;}
+img
+ {display:block;
+ margin:1em;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<div ondragenter="window.location.reload()">
+<p ondragenter="event.stopPropagation()" dropzone="copy string:text/uri-list" ondrop="addImage(event)">Drop image here, it should be copied to this page once you drop it here.</p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-image-now.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-image-now.xhtml
new file mode 100644
index 0000000000..ccd38b5588
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-image-now.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('body').appendChild(c);}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false" ondrop="addImage(event)">
+<p>Drop image now, it should be copied to this page once you drop it here.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-link-now.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-link-now.xhtml
new file mode 100644
index 0000000000..286b5586da
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-link-now.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+<script type="application/ecmascript">
+function checkLink(event)
+ {document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'}
+</script>
+</head>
+<body ondragenter="event.preventDefault()" ondrop="checkLink(event)" ondragover="return false">
+<p>Drop link now, you should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-now.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-now.xhtml
new file mode 100644
index 0000000000..046c4bbd21
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-now.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+html, body
+ {height:100%;}
+</style>
+</head>
+<body ondragenter="event.preventDefault()" ondragover="return false" ondrop="event.preventDefault();document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<p>Drop selection now, you should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-selection-here.xhtml b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-selection-here.xhtml
new file mode 100644
index 0000000000..074f5b3d89
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/navigation/helper-drop-selection-here.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross page drag and drop: helper file</title>
+<style type="text/css">
+p
+ {border:solid medium navy;
+ height:200px;
+ padding:1em;
+ margin:0;}
+div
+ {margin:100px;
+ padding:50px;}
+</style>
+</head>
+<body>
+<div ondragenter="window.location.reload()">
+<p ondragenter="event.stopPropagation()" dropzone="copy string:text/plain" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">Drop selection here, you should see word PASS once you drop it.</p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/001.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/001.xhtml
new file mode 100644
index 0000000000..56924d7a7f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/001.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback overlay size</title>
+<style type="text/css">
+img
+ {height:100px;
+ width:100px;}
+</style>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Try to drag green box above. Size of feedback overlay should match size of green box.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/002.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/002.xhtml
new file mode 100644
index 0000000000..b8d2e8cb05
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/002.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Setting drag image during drag and drop</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/003.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/003.xhtml
new file mode 100644
index 0000000000..344a7da7db
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/003.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/004.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/004.xhtml
new file mode 100644
index 0000000000..2ba73e3737
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/004.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Resetting drag image on dragenter</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {width:100px;
+ height:100px;
+ background-color:silver;
+ margin-top:20px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+function resetImage(event)
+ {event.preventDefault();
+ event.dataTransfer.setDragImage(document.querySelector('strong'), 1, 1);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above to the silver box. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+<div ondragenter="resetImage(event)" ondragover="return false"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/005.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/005.xhtml
new file mode 100644
index 0000000000..bf156f36fa
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/005.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore on dragenter</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {width:100px;
+ height:100px;
+ background-color:silver;
+ margin-top:20px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+function resetImage(event)
+ {event.preventDefault();
+ event.dataTransfer.addElement(document.querySelector('strong'));}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above to the silver box. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+<div ondragenter="resetImage(event)" ondragover="return false"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/006.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/006.xhtml
new file mode 100644
index 0000000000..4c0295bf9b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/006.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Resetting drag image on dragover</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {width:100px;
+ height:100px;
+ background-color:silver;
+ margin-top:20px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+function resetImage(event)
+ {event.preventDefault();
+ event.dataTransfer.setDragImage(document.querySelector('strong'), 1, 1);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above to the silver box. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+<div ondragenter="event.preventDefault()" ondragover="resetImage(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/007.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/007.xhtml
new file mode 100644
index 0000000000..d91aae5056
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/007.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore on dragover</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {width:100px;
+ height:100px;
+ background-color:silver;
+ margin-top:20px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+function resetImage(event)
+ {event.preventDefault()
+ event.dataTransfer.addElement(document.querySelector('strong'));}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above to the silver box. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+<div ondragenter="event.preventDefault()" ondragover="resetImage(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/008.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/008.xhtml
new file mode 100644
index 0000000000..9d01553c54
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/008.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Resetting drag image on drag</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+function resetImage(event)
+ {event.dataTransfer.setDragImage(document.querySelector('strong'), 1, 1);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="resetImage(event)">Drag me</a></p>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/009.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/009.xhtml
new file mode 100644
index 0000000000..23c786e9a0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/009.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore on drag</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+function resetImage(event)
+ {event.dataTransfer.addElement(document.querySelector('strong'));}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)" ondrag="resetImage(event)">Drag me</a></p>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/010.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/010.xhtml
new file mode 100644
index 0000000000..5108bdd5e9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/010.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Resetting drag image on new drag</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+var i = 0;
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelectorAll('span')[i++%2], 1, 1);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Drag link above around the page drop it and try to drag again.</p>
+<p>First time you drag it you should see word <span>Odd</span> in feedback overlay, second time overlay should change to <span>Even</span>.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/011.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/011.xhtml
new file mode 100644
index 0000000000..5fc17de1f4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/011.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Feedback image for circle</title>
+<style type="text/css">
+body
+ {background-color:silver;
+ margin:0;}
+div
+ {background-color:white;}
+</style>
+</head>
+<body>
+<div><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></div>
+<p>Try to drag green circle above. Feedback overlay should be a circle.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/012.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/012.xhtml
new file mode 100644
index 0000000000..49739bc2b7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/012.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Feedback image and border-radius</title>
+<style type="text/css">
+body
+ {background-color:silver;
+ margin:0;}
+div
+ {background-color:white;}
+div > div
+ {display:block;
+ width:100px;
+ height:100px;
+ border:solid thin green;
+ border-radius:50px;}
+</style>
+</head>
+<body>
+<div>
+ <div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/>
+</div>
+<p>Try to drag green circle above. Feedback overlay should be a circle.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/013.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/013.xhtml
new file mode 100644
index 0000000000..07ac6b8e53
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/013.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Feedback image and CSS transforms</title>
+<style type="text/css">
+body
+ {background-color:silver;
+ margin:0;}
+div
+ {background-color:white;}
+div > div
+ {display:block;
+ width:100px;
+ height:87px;
+ transform-origin:bottom right;
+ transform:skew(-30deg);
+ -o-transform-origin:bottom right;
+ -o-transform:skew(-30deg);
+ background-color:green;}
+</style>
+</head>
+<body>
+<div>
+ <div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/>
+</div>
+<p>Try to drag green rhomb above. Feedback overlay should not be rectangular.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/014.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/014.xhtml
new file mode 100644
index 0000000000..8c07f69a18
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/014.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Setting drag image during drag and drop of selection</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p ondragstart="start(event)">Drag me</p>
+<p>Try to drag selection above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/015.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/015.xhtml
new file mode 100644
index 0000000000..3e416524ee
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/015.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore during drag and drop of selection</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p ondragstart="start(event)">Drag me</p>
+<p>Try to drag selection above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/016.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/016.xhtml
new file mode 100644
index 0000000000..081ee91330
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/016.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Setting drag image during drag and drop of PNG image</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+img
+ {height:100px;
+ width:100px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+</script>
+</head>
+<body>
+<p ondragstart="start(event)">
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel"/>
+</p>
+<p>Try to drag green box above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/017.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/017.xhtml
new file mode 100644
index 0000000000..7b1d9320ff
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/017.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore during drag and drop of PNG image</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+img
+ {height:100px;
+ width:100px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+</script>
+</head>
+<body>
+<p ondragstart="start(event)">
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel"/>
+</p>
+<p>Try to drag green box above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/018.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/018.xhtml
new file mode 100644
index 0000000000..b82080a7fb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/018.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Setting drag image during drag and drop of SVG image</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+</script>
+</head>
+<body>
+<p><img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle" ondragstart="start(event)"/></p>
+<p>Try to drag green circle above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/019.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/019.xhtml
new file mode 100644
index 0000000000..325be4e8c3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/019.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore during drag and drop of SVG image</title>
+<style type="text/css">
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+</script>
+</head>
+<body>
+<p><img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle" ondragstart="start(event)"/></p>
+<p>Try to drag green circle above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/020.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/020.xhtml
new file mode 100644
index 0000000000..f5d1a2636d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/020.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Setting drag image during drag and drop of block element</title>
+<style type="text/css">
+div
+ {width:100px;
+ height:100px;
+ background-color:navy;}
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Try to drag blue box above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/021.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/021.xhtml
new file mode 100644
index 0000000000..268ef17c8c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/021.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Adding element to datastore during drag and drop of block element</title>
+<style type="text/css">
+div
+ {width:100px;
+ height:100px;
+ background-color:navy;}
+span
+ {color:green;
+ background-color:yellow;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('span'));}
+</script>
+</head>
+<body>
+<div draggable="true" ondragstart="start(event)"/>
+<p>Try to drag blue box above. You should see word <span>PASS</span> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/022.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/022.xhtml
new file mode 100644
index 0000000000..fedc0344ed
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/022.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Block element drag and drop: changing draggable attribute</title>
+<style type="text/css">
+div
+ {width:100px;
+ height:100px;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('div').setAttribute('draggable','false')">
+<div draggable="true" ondragstart="document.querySelector('p').firstChild.nodeValue = 'FAIL'"/>
+<p>You should not be able to drag blue box.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/023.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/023.xhtml
new file mode 100644
index 0000000000..c3f7ddf51b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/023.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: changing draggable attribute</title>
+</head>
+<body onload="document.querySelector('a').setAttribute('draggable','false')">
+<p><a href="data:text/plain,1" ondragstart="document.querySelector('p').firstChild.nodeValue = 'FAIL'">Try to drag me</a></p>
+<p>You should not be able to drag link above.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/024.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/024.xhtml
new file mode 100644
index 0000000000..a7a3eb3320
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/024.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>PNG image drag and drop: changing draggable attribute</title>
+<style type="text/css">
+img
+ {width:100px;
+ height:100px;}
+</style>
+</head>
+<body onload="document.querySelector('img').setAttribute('draggable','false')">
+<p><img ondragstart="document.querySelector('p').firstChild.nodeValue = 'FAIL'" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel"/></p>
+<p>You should not be able to drag green box.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/025.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/025.xhtml
new file mode 100644
index 0000000000..ac2005377b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/025.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG image drag and drop: changing draggable attribute</title>
+<style type="text/css">
+img
+ {width:100px;
+ height:100px;}
+</style>
+</head>
+<body onload="document.querySelector('img').setAttribute('draggable','false')">
+<p><img ondragstart="document.querySelector('p').firstChild.nodeValue = 'FAIL'" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>You should not be able to drag green circle.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/026.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/026.xhtml
new file mode 100644
index 0000000000..8d4b07411d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/026.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping links: zero height</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {width:4em;
+ padding:1em;
+ line-height:0;}
+a
+ {background-color:white;
+ padding:1ex;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <a href="data:text/plain,1" ondragstart="start(event,'strong')">    </a>
+ <a href="data:text/plain,2" ondragstart="start(event,'span')">Link</a>
+</div>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/027.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/027.xhtml
new file mode 100644
index 0000000000..e34e2e88f1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/027.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping links: negative margin</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {height:2em;}
+div + div
+ {margin-top:-2em;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <a href="data:text/plain,1" ondragstart="start(event,'strong')">   </a>
+</div>
+<div>
+ <a href="data:text/plain,2" ondragstart="start(event,'span')">Link</a>
+</div>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/028.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/028.xhtml
new file mode 100644
index 0000000000..0afd8a70cc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/028.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping links: relative position</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {height:2em;}
+div + div
+ {position:relative;
+ top:-2em;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <a href="data:text/plain,1" ondragstart="start(event,'strong')">   </a>
+</div>
+<div>
+ <a href="data:text/plain,2" ondragstart="start(event,'span')">Link</a>
+</div>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/029.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/029.xhtml
new file mode 100644
index 0000000000..8741e8a374
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/029.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping links: absolute position</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {height:2em;
+ position:absolute;
+ top:10px;
+ left:10px;}
+div + div
+ {z-index:2;}
+p
+ {margin-top:3em;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <a href="data:text/plain,1" ondragstart="start(event,'strong')">   </a>
+</div>
+<div>
+ <a href="data:text/plain,2" ondragstart="start(event,'span')">Link</a>
+</div>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/030.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/030.xhtml
new file mode 100644
index 0000000000..3dcfae69fc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/030.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping links: fixed position</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {height:2em;
+ position:fixed;
+ top:10px;
+ left:10px;}
+div + div
+ {z-index:2;}
+p
+ {margin-top:3em;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <a href="data:text/plain,1" ondragstart="start(event,'strong')">   </a>
+</div>
+<div>
+ <a href="data:text/plain,2" ondragstart="start(event,'span')">Link</a>
+</div>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/031.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/031.xhtml
new file mode 100644
index 0000000000..009f19a9d1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/031.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping images: negative margin</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+img
+ {display:block;
+ width:100px;
+ height:100px;}
+img + img
+ {margin-top:-100px;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <img ondragstart="start(event,'strong')" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8DwHwAFAAH/VscvDQAAAABJRU5ErkJggg=="/>
+ <img ondragstart="start(event,'span')" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNgaGD4DwAChAGAJVtEDQAAAABJRU5ErkJggg=="/>
+</div>
+<p>Try to drag box above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/032.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/032.xhtml
new file mode 100644
index 0000000000..d041ec9d5f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/032.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping images: absolute position</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {height:100px;
+ position:relative;}
+img
+ {display:block;
+ position:absolute;
+ top:0;
+ bottom:0;
+ width:100px;
+ height:100px;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <img ondragstart="start(event,'strong')" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8DwHwAFAAH/VscvDQAAAABJRU5ErkJggg=="/>
+ <img ondragstart="start(event,'span')" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNgaGD4DwAChAGAJVtEDQAAAABJRU5ErkJggg=="/>
+</div>
+<p>Try to drag box above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/033.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/033.xhtml
new file mode 100644
index 0000000000..b91f58181a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/033.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping images: fixed position</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+div
+ {height:100px;
+ position:relative;}
+img
+ {display:block;
+ position:fixed;
+ top:0;
+ bottom:0;
+ width:100px;
+ height:100px;}
+</style>
+<script type="application/ecmascript">
+function start(event,feedback)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector(feedback));}
+</script>
+</head>
+<body>
+<div>
+ <img ondragstart="start(event,'strong')" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4z8DwHwAFAAH/VscvDQAAAABJRU5ErkJggg=="/>
+ <img ondragstart="start(event,'span')" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNgaGD4DwAChAGAJVtEDQAAAABJRU5ErkJggg=="/>
+</div>
+<p>Try to drag box above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay all the time.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/034.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/034.xhtml
new file mode 100644
index 0000000000..3fbbfcce32
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/034.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of overlapping elements: negative margins</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ margin-top:-50px;
+ background-color:navy;}
+div:nth-child(odd)
+ {background-color:maroon;
+ margin-left:50px;}
+div[draggable]
+ {background-color:teal;}
+</style>
+</head>
+<body>
+<div/>
+<div/>
+<div draggable="true"/>
+<div/>
+<div/>
+<div draggable="true"/>
+<div/>
+<div/>
+<p>Only green areas should be draggable.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/035.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/035.xhtml
new file mode 100644
index 0000000000..c2a13f7ea6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/035.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of floated overlapping elements: negative margins</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ float:left;
+ margin-left:-50px;
+ background-color:navy;}
+div:nth-child(odd)
+ {background-color:maroon;
+ margin-top:50px;}
+div[draggable]
+ {background-color:teal;}
+</style>
+</head>
+<body>
+<p>Only green areas should be draggable.</p>
+<div/>
+<div/>
+<div draggable="true"/>
+<div/>
+<div/>
+<div draggable="true"/>
+<div/>
+<div/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/036.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/036.xhtml
new file mode 100644
index 0000000000..94ba9f24e4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/036.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback when multiple elements are added to dragstore</title>
+<style type="text/css">
+div > div
+ {height:100px;
+ width:100px;
+ float:left;
+ background-color:navy;}
+div + div
+ {margin-left:-60px;
+ background-color:maroon;}
+div[draggable]
+ {background-color:teal;
+ margin-top:50px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {var div = document.querySelectorAll('div > div:nth-child(odd)');
+ event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != div.length; i++)
+ {event.dataTransfer.addElement(div[i]);}
+ }
+</script>
+</head>
+<body>
+<p>Try to drag green box below. Feedback overlay should include all three boxes when you drag green one.</p>
+<div ondragstart="start(event)">
+ <div/>
+ <div draggable="true"/>
+ <div/>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/038.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/038.xhtml
new file mode 100644
index 0000000000..d146ecf8ab
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/038.xhtml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Resetting drag image after element was added to dragstore</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelector('strong'));
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/039.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/039.xhtml
new file mode 100644
index 0000000000..49ce01c912
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/039.xhtml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Resetting drag image during drag and drop of link</title>
+<style type="text/css">
+span, strong
+ {color:green;
+ background-color:yellow;}
+strong
+ {color:red;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('strong'), 1, 1);
+ event.dataTransfer.setDragImage(document.querySelector('span'), 1, 1);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above. You should see word <span>PASS</span> not <strong>FAIL</strong> in feedback overlay.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/040.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/040.xhtml
new file mode 100644
index 0000000000..0bad3d2f65
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/040.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cursor position and drag image</title>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('canvas'), 50, 50);}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="start(event)">Drag me</a></p>
+<p>Try to drag link above. Feedback overlay should be based on canvas below and mouse pointer should be anchored in its center.</p>
+<p>
+ <canvas width="100" height="100">Canvas</canvas>
+</p>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/041.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/041.xhtml
new file mode 100644
index 0000000000..73e8c4dc3b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/041.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cursor position and drag image after new element is added to dragstore</title>
+<script type="application/ecmascript">
+function start(event,element)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelectorAll('canvas')[element]);}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event,1)">Canvas</canvas>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event,0)">Canvas</canvas>
+</p>
+<p>Try to drag canvas above. Feedback overlay should include both canvases and mouse pointer should be anchored in dragged ones center.</p>
+<script type="application/ecmascript">
+var canvases = document.querySelectorAll('canvas');
+paintCanvas(canvases[0],'navy');
+paintCanvas(canvases[1],'green');
+function paintCanvas(canvas,color)
+ {var c = canvas.getContext('2d');
+ for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?color:'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+ }
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/042.html b/testing/web-platform/tests/html/editing/dnd/overlay/042.html
new file mode 100644
index 0000000000..574153fc0d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/042.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging elements that overlay each other</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: navy;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ height: 100px;
+ width: 100px;
+ background-color: orange;
+ left: 58px;
+ top: 58px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 258px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var passed = true, orange = document.getElementsByTagName('div')[1], blue = document.getElementsByTagName('div')[0], fuchsia = document.getElementsByTagName('div')[2];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'PASS');
+ };
+ blue.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'FAIL');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ //it's possible this could get called twice if the browser drags both items, so it uses the "passed" variable to make sure
+ //that if blue gets dropped first, it remains false when orange then gets dropped
+ passed = passed && ( e.dataTransfer.getData('text/plain') == 'PASS' );
+ document.getElementsByTagName('p')[0].innerHTML = passed ? 'PASS' : 'FAIL';
+ };
+
+};
+
+</script>
+
+<div draggable='true'></div><div draggable='true'></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then release it. While dragging, the drag placeholder should show that only the orange box is being dragged.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/043.html b/testing/web-platform/tests/html/editing/dnd/overlay/043.html
new file mode 100644
index 0000000000..49108b9dbb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/043.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging nested draggable elements</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: navy;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div div {
+ height: 100px;
+ width: 100px;
+ background-color: orange;
+ position: absolute;
+ left: 50px;
+ top: 50px;
+ }
+ body > div + div {
+ background-color: fuchsia;
+ height: 100px;
+ width: 100px;
+ left: 258px;
+ top: 58px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var passed = true, orange = document.getElementsByTagName('div')[1], blue = document.getElementsByTagName('div')[0], fuchsia = document.getElementsByTagName('div')[2];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'child targeted');
+ };
+ blue.ondragstart = function(e) {
+ if( e.target == this ) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'parent targeted');
+ } else {
+ e.dataTransfer.setData('extra/data', 'parent bubble');
+ }
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ e.preventDefault();
+ //it's possible this could get called twice if the browser drags both items, so it uses the "passed" variable to make sure
+ //that if blue gets dropped first, it remains false when orange then gets dropped
+ passed = passed && ( e.dataTransfer.getData('text/plain') == 'child targeted' ) && ( e.dataTransfer.getData('extra/data') == 'parent bubble' );
+ document.getElementsByTagName('p')[0].innerHTML = passed ? 'PASS' : 'FAIL';
+ };
+
+};
+
+</script>
+
+<div draggable='true'><div draggable='true'></div></div><div></div>
+
+<p>Use your pointing device to drag the orange box to the pink box, then release it. While dragging, the drag placeholder should show that only the orange box is being dragged.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/044.html b/testing/web-platform/tests/html/editing/dnd/overlay/044.html
new file mode 100644
index 0000000000..6632738207
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/044.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging selections inside draggable elements</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: fuchsia;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], fuchsia = document.getElementsByTagName('div')[1];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('extra/data', 'parent bubble');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ e.preventDefault();
+ var passed = ( e.dataTransfer.getData('text/plain') == 'text dummy' ) && ( e.dataTransfer.getData('extra/data') == 'parent bubble' );
+ document.getElementsByTagName('p')[0].innerHTML = passed ? 'PASS' : 'FAIL';
+ };
+ var range = document.createRange();
+ range.selectNodeContents(orange);
+ range.setStart(orange.firstChild,6);
+ range.setEnd(orange.firstChild,16);
+ window.getSelection().addRange(range);
+
+};
+
+</script>
+
+<div draggable='true'>Dummy text dummy text</div><div></div>
+
+<p>Use your pointing device to <strong>drag the selected text</strong> to the pink box, then release it. While dragging, the drag placeholder should show that only the selected text is being dragged.</p>
+<p>(If no text is selected, you will need to use your browser's functionality to select &quot;text dummy&quot; in the orange box.)</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/045.html b/testing/web-platform/tests/html/editing/dnd/overlay/045.html
new file mode 100644
index 0000000000..1efed6a110
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/045.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging iframes overlaying draggable elements</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: fuchsia;
+ left: 258px;
+ }
+ iframe {
+ border: 5px solid yellow;
+ height: 130px;
+ width: 130px;
+ position: absolute;
+ top: 38px;
+ left: 38px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], fuchsia = document.getElementsByTagName('div')[1];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'FAIL');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ e.preventDefault();
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL';
+ };
+
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Use your pointing device to begin dragging inside the yellow border (not on any scrollbars that may appear), over to the pink box, then release it. Pass if nothing is dragged, and if this text does not change.
+<iframe src="about:blank"></iframe></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/046.html b/testing/web-platform/tests/html/editing/dnd/overlay/046.html
new file mode 100644
index 0000000000..8d03cd5d95
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/046.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging iframes inside draggable elements</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: fuchsia;
+ left: 258px;
+ }
+ iframe {
+ border: 5px solid yellow;
+ height: 130px;
+ width: 130px;
+ position: absolute;
+ top: 30px;
+ left: 30px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], fuchsia = document.getElementsByTagName('div')[1];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'FAIL');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ e.preventDefault();
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL';
+ };
+
+};
+
+</script>
+
+<div draggable='true'><iframe src="about:blank"></iframe></div><div></div>
+
+<p>Use your pointing device to begin dragging inside the yellow border (not on any scrollbars that may appear), over to the pink box, then release it. Pass if nothing is dragged, and if this text does not change.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/047.html b/testing/web-platform/tests/html/editing/dnd/overlay/047.html
new file mode 100644
index 0000000000..9b4c09ab93
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/047.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging objects overlaying draggable elements</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: fuchsia;
+ left: 258px;
+ }
+ object {
+ border: 5px solid yellow;
+ height: 130px;
+ width: 130px;
+ position: absolute;
+ top: 38px;
+ left: 38px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], fuchsia = document.getElementsByTagName('div')[1];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'FAIL');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ e.preventDefault();
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL';
+ };
+
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Use your pointing device to begin dragging inside the yellow border (not on any scrollbars that may appear), over to the pink box, then release it. Pass if nothing is dragged, and if this text does not change.
+<object data="about:blank"></object></p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/048.html b/testing/web-platform/tests/html/editing/dnd/overlay/048.html
new file mode 100644
index 0000000000..54bb72c5ed
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/048.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - dragging objects inside draggable elements</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: fuchsia;
+ left: 258px;
+ }
+ object {
+ border: 5px solid yellow;
+ height: 130px;
+ width: 130px;
+ position: absolute;
+ top: 30px;
+ left: 30px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script>
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], fuchsia = document.getElementsByTagName('div')[1];
+
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/plain', 'FAIL');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ e.preventDefault();
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL';
+ };
+
+};
+
+</script>
+
+<div draggable='true'><object data="about:blank"></object></div><div></div>
+
+<p>Use your pointing device to begin dragging inside the yellow border (not on any scrollbars that may appear), over to the pink box, then release it. Pass if nothing is dragged, and if this text does not change.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/049.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/049.xhtml
new file mode 100644
index 0000000000..70c57acea7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/049.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible image: negative margins</title>
+<style type="text/css">
+p:first-child
+ {margin:-50px 0 0 -50px;}
+img
+ {height:100px;
+ width:100px;}
+</style>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Try to drag green box above. Feedback overlay should be green square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/050.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/050.xhtml
new file mode 100644
index 0000000000..28f218f813
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/050.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible image: relative position</title>
+<style type="text/css">
+p:first-child
+ {margin:0;
+ position:relative;
+ top:-50px;
+ left:-50px;}
+img
+ {display:block;
+ height:100px;
+ width:100px;}
+</style>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Try to drag green box above. Feedback overlay should be green square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/051.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/051.xhtml
new file mode 100644
index 0000000000..fc85f01480
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/051.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible image: absolute position</title>
+<style type="text/css">
+img
+ {position:absolute;
+ top:-50px;
+ left:-50px;}
+p + p
+ {margin-top:100px;}
+img
+ {height:100px;
+ width:100px;}
+</style>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Try to drag green box above. Feedback overlay should be green square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/052.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/052.xhtml
new file mode 100644
index 0000000000..af40bf20b2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/052.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible image: fixed position</title>
+<style type="text/css">
+img
+ {position:fixed;
+ top:-50px;
+ left:-50px;}
+p + p
+ {margin-top:100px;}
+img
+ {height:100px;
+ width:100px;}
+</style>
+</head>
+<body>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==" alt="PNG green pixel" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Try to drag green box above. Feedback overlay should be green square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/053.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/053.xhtml
new file mode 100644
index 0000000000..2d427fdcc2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/053.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible element: negative margins</title>
+<style type="text/css">
+div
+ {margin:-160px 0 0 -160px;
+ height:200px;
+ width:200px;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/>
+<p>Try to drag blue box above. Feedback overlay should be square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/054.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/054.xhtml
new file mode 100644
index 0000000000..f46c325118
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/054.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible element: relative position</title>
+<style type="text/css">
+div
+ {position:relative;
+ top:-150px;
+ left:-150px;
+ height:200px;
+ width:200px;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/>
+<p>Try to drag blue box above. Feedback overlay should be square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/055.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/055.xhtml
new file mode 100644
index 0000000000..d693a60b9b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/055.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible element: absolute position</title>
+<style type="text/css">
+div
+ {position:absolute;
+ top:-150px;
+ left:-150px;
+ height:200px;
+ width:200px;
+ background-color:navy;}
+p
+ {margin-top:100px;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/>
+<p>Try to drag blue box above. Feedback overlay should be square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/056.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/056.xhtml
new file mode 100644
index 0000000000..77e86f97fb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/056.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible element: fixed position</title>
+<style type="text/css">
+div
+ {position:fixed;
+ top:-150px;
+ left:-150px;
+ height:200px;
+ width:200px;
+ background-color:navy;}
+p
+ {margin-top:100px;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/>
+<p>Try to drag blue box above. Feedback overlay should be square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/057.xhtml b/testing/web-platform/tests/html/editing/dnd/overlay/057.xhtml
new file mode 100644
index 0000000000..0e22142f19
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/057.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag feedback for partly visible float: negative margins</title>
+<style type="text/css">
+p
+ {margin-top:0;}
+div
+ {float:left;
+ margin:-150px 0 0 -150px;
+ height:200px;
+ width:200px;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/>
+<p>Try to drag blue box above. Feedback overlay should be square.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/058.html b/testing/web-platform/tests/html/editing/dnd/overlay/058.html
new file mode 100644
index 0000000000..2fa4b60fb6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/058.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>drag &amp; drop - dragging elements by children that extend out of them</title>
+<style type="text/css">
+div {
+ height: 200px;
+ width: 200px;
+ background: blue;
+ white-space: nowrap;
+}
+span {
+ display: inline-block;
+ width: 210px;
+ height: 100px;
+}
+span + span {
+ background: orange;
+ width: 100px;
+}
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+};
+</script>
+<div draggable="true"><span></span><span></span></div>
+<p>Drag the orange square sideways. Pass if the drag placeholder shows that both the blue and orange squares are being dragged.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/059.html b/testing/web-platform/tests/html/editing/dnd/overlay/059.html
new file mode 100644
index 0000000000..0afb72e029
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/059.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>drag &amp; drop - dragging elements by text that extends out of them</title>
+<style type="text/css">
+div {
+ height: 200px;
+ width: 200px;
+ background: blue;
+ white-space: nowrap;
+}
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+};
+</script>
+<div draggable="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Drag me</div>
+<p>Drag the above text sideways. Pass if the drag placeholder shows that both the text and blue square are being dragged.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/060.html b/testing/web-platform/tests/html/editing/dnd/overlay/060.html
new file mode 100644
index 0000000000..0a90cadd4a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/060.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>drag &amp; drop - dragging elements by children that are positioned outside them</title>
+<style type="text/css">
+div {
+ height: 200px;
+ width: 200px;
+ background: blue;
+ white-space: nowrap;
+ position: relative;
+}
+span {
+ display: block;
+ top: 0;
+ left: 210px;
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: orange;
+}
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+};
+</script>
+<div draggable="true"><span></span></div>
+<p>Drag the orange square sideways. Pass if the drag placeholder shows that both the blue and orange squares are being dragged.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/061.html b/testing/web-platform/tests/html/editing/dnd/overlay/061.html
new file mode 100644
index 0000000000..3081676bf0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/061.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>drag &amp; drop - dragging elements by overflowing children that are positioned outside them</title>
+<style type="text/css">
+div {
+ height: 200px;
+ width: 200px;
+ background: blue;
+ white-space: nowrap;
+ position: relative;
+}
+span {
+ display: block;
+ top: 0;
+ left: 210px;
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: yellow;
+}
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+};
+</script>
+<div draggable="true"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Drag me</span></div>
+<p>Drag the above text sideways. Pass if the drag placeholder shows that the text and both the yellow and blue squares are being dragged.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/062.html b/testing/web-platform/tests/html/editing/dnd/overlay/062.html
new file mode 100644
index 0000000000..d48577e451
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/062.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Selection spanning hidden elements</title>
+ <script type="text/javascript">
+window.onload = function () {
+ var range = document.createRange(), p = document.getElementsByTagName('p')[0];
+ range.selectNodeContents(p);
+ range.setStart(p.firstChild.firstChild,4);
+ range.setEnd(p.lastChild.firstChild,5);
+ window.getSelection().addRange(range);
+};
+ </script>
+ </head>
+ <body>
+
+ <p><span style="display:none">FAILPASS_</span>drag<span style="display:none">_THIS_</span>text<span style="display:none">_PASSFAIL</span></p>
+ <p><textarea rows="3" cols="50"></textarea></p>
+ <p>Drag the selected text into the input box. The drag placeholder should match the visible text that is being dragged. When dropped, either &quot;dragtext&quot; or &quot;PASS_drag_THIS_text_PASS&quot; should appear in the input.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-001.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-001.html
new file mode 100644
index 0000000000..05f69fa61c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-001.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 001</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ box-shadow: 10px 10px 10px gray;
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box. It may optionally also include the box's shadow.</p>
+
+<a href='#' draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-002.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-002.html
new file mode 100644
index 0000000000..de3b2e296e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-002.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 002</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ border: 10px solid orange;
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box, including the orange border.</p>
+
+<a href='#' draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-003.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-003.html
new file mode 100644
index 0000000000..adb0a3581c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-003.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 002</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ outline: 10px solid orange;
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box. It may optionally also include include the orange border.</p>
+
+<a href='#' draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-004.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-004.html
new file mode 100644
index 0000000000..d6a5da41c0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-004.html
@@ -0,0 +1,19 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 004</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ box-shadow: 10px 10px 10px gray;
+}
+div {
+ background-color: orange;
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box. It may optionally also include the box's shadow, but must not include the orange rectangle.</p>
+
+<div><a href='#' draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a></div>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-005.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-005.html
new file mode 100644
index 0000000000..2bbdb3cf73
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-005.html
@@ -0,0 +1,15 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 005</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: rgba(0,0,255,0.5);
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box, including the text within it.</p>
+
+<a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'">TEST</a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-006.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-006.html
new file mode 100644
index 0000000000..c067878f22
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-006.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 006</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ opacity: 0.5;
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box, including the text within it.</p>
+
+<a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'">TEST</a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-007.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-007.html
new file mode 100644
index 0000000000..56ba9594f2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-007.html
@@ -0,0 +1,20 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 007</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ margin-left: 100px;
+ -moz-transform: rotate(-45deg) skew(15deg, 15deg);
+ -o-transform: rotate(-45deg) skew(15deg, 15deg);
+ -webkit-transform: rotate(-45deg) skew(15deg, 15deg);
+ transform: rotate(-45deg) skew(15deg, 15deg);
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box, including the text within it, and with the same rotation.</p>
+
+<a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'">TEST</a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-008.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-008.html
new file mode 100644
index 0000000000..7a78f814e1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-008.html
@@ -0,0 +1,31 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 008</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: green;
+ position: absolute;
+ top: 100px;
+ left: 10px;
+ z-index: 1;
+}
+
+a + a {
+ background-color: red;
+ position: absolute;
+ top: 150px;
+ left: 20px;
+ z-index: 2;
+}
+
+
+</style>
+
+<p>Drag the green box below downwards. The drag placeholder should resemble the green box, including the text within it. It may optionally be a complete square, or the same shape as the visible part of the green box. There should be no red in the drag placeholder.</p>
+
+<a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'">TEST</a>
+
+<a href='#'>TEST</a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-009.html b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-009.html
new file mode 100644
index 0000000000..0f36fe0834
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/heavy-styling-009.html
@@ -0,0 +1,16 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay for heavily styled elements – 009</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ border-radius: 50px;
+}
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should resemble the blue box, including the rounded corners.</p>
+
+<a draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></a>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-001.html b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-001.html
new file mode 100644
index 0000000000..ecc7ebbf3b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-001.html
@@ -0,0 +1,22 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay of elements partly outside the viewport – 001</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: 10px;
+ right: -100px;
+}
+
+p {
+ margin-right: 200px;
+}
+</style>
+
+<a href='#'></a>
+
+<p>Drag the blue box on the right downwards. The drag placeholder should ideally be a blue square. It may optionally be a rectangle the same shape as the visible part of the blue box. No part of the UI should be dragged with the box.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-002.html b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-002.html
new file mode 100644
index 0000000000..3878c31d0a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-002.html
@@ -0,0 +1,22 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay of elements partly outside the viewport – 002</title>
+<style>
+img {
+ height: 200px;
+ width: 200px;
+ position: absolute;
+ top: 10px;
+ right: -100px;
+}
+
+p {
+ margin-right: 200px;
+}
+</style>
+
+
+<img alt=''
+src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAACRJREFUOE9jZGBoYCARADWQhEhTDXIOScaPaiAyuEaDlXBAAQBiw3iCaRp9LAAAAABJRU5ErkJggg%3D%3D'>
+
+<p>Drag the blue box on the right. The drag placeholder should ideally be a blue square. It may optionally be a rectangle the same shape as the visible part of the blue box. No part of the UI should be dragged with the box.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-003.html b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-003.html
new file mode 100644
index 0000000000..895a008f98
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-003.html
@@ -0,0 +1,22 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay of elements partly outside the viewport – 003</title>
+<style>
+a {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: -100px;
+ right: -100px;
+}
+
+p {
+ margin-right: 200px;
+}
+</style>
+
+<a href='#'></a>
+
+<p>Drag the blue box on the right downwards. The drag placeholder should ideally be a blue square twice as high and wide as the visible part of the blue box. It may optionally be a square the same size as the visible part of the blue box. No part of the UI should be dragged with the box.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-004.html b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-004.html
new file mode 100644
index 0000000000..61bc22eefe
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-004.html
@@ -0,0 +1,21 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay of elements partly outside the viewport – 004</title>
+<style>
+img {
+ height: 200px;
+ width: 200px;
+ position: absolute;
+ top: -100px;
+ right: -100px;
+}
+
+p {
+ margin-right: 200px;
+}
+</style>
+
+<img alt=''
+src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAACRJREFUOE9jZGBoYCARADWQhEhTDXIOScaPaiAyuEaDlXBAAQBiw3iCaRp9LAAAAABJRU5ErkJggg%3D%3D'>
+
+<p>Drag the blue box on the right. The drag placeholder should ideally be a blue square twice as high and wide as the visible part of the blue box. It may optionally be a square the same size as the visible part of the blue box. No part of the UI should be dragged with the box.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-005.html b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-005.html
new file mode 100644
index 0000000000..26e4bff2eb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-005.html
@@ -0,0 +1,22 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay of elements partly outside the viewport – 005</title>
+<style>
+div {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: 10px;
+ right: -100px;
+}
+
+p {
+ margin-right: 200px;
+}
+</style>
+
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></div>
+
+<p>Drag the blue box on the right. The drag placeholder should ideally be a blue square. It may optionally be a rectangle the same shape as the visible part of the blue box. No part of the UI should be dragged with the box.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-006.html b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-006.html
new file mode 100644
index 0000000000..d10f06dfdd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/outside-viewport-006.html
@@ -0,0 +1,22 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay of elements partly outside the viewport – 006</title>
+<style>
+div {
+ display: block;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ position: absolute;
+ top: -100px;
+ right: -100px;
+}
+
+p {
+ margin-right: 200px;
+}
+</style>
+
+<div draggable="true" ondragstart="event.dataTransfer.effectAllowed ='copy'"></div>
+
+<p>Drag the blue box on the right. The drag placeholder should ideally be a blue square twice as high and wide as the visible part of the blue box. It may optionally be a square the same size as the visible part of the blue box. No part of the UI should be dragged with the box.</p>
diff --git a/testing/web-platform/tests/html/editing/dnd/overlay/oversized-001.html b/testing/web-platform/tests/html/editing/dnd/overlay/oversized-001.html
new file mode 100644
index 0000000000..e13f6b190b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/overlay/oversized-001.html
@@ -0,0 +1,18 @@
+<!DOCTYPe html>
+<meta charset='utf-8'>
+<title>drag and drop – feedback overlay of oversized element</title>
+<style>
+html, body, p + p { height: 100%; width: 100%; }
+a {
+ display: block;
+ height: 100%;
+ width: 100%;
+ background-color: blue;
+ border: 50px solid orange; /* makes it higher and wider than the viewport */
+}
+
+</style>
+
+<p>Drag the blue box below downwards. The drag placeholder should ideally be a blue rectangle with an orange border on all sides. It may optionally match the visible part of the blue-and-orange box. It may optionally be a rectangle with the same pattern and size as the visible part of the blue-and-orange box. It may optionally be shrunk to a manageable size. No part of the UI should be dragged with the box.</p>
+
+<p><a href='#'></a></p>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/alttab.html b/testing/web-platform/tests/html/editing/dnd/platform/alttab.html
new file mode 100644
index 0000000000..b03c5dfd6a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/alttab.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - alt+tab while dragging</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where alt+tab (or some equivalent) switches applications.</p>
+ <p>Ensure that at least one other application is open. Select this text. Drag the selection downwards a little, then alt+tab (or your system's equivalent) to the other application. Pass if the drag placeholder continues to follow the mouse/pointing device. Release the drag. Pass if the drag placeholder disappears.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cancel-middle-click.html b/testing/web-platform/tests/html/editing/dnd/platform/cancel-middle-click.html
new file mode 100644
index 0000000000..40fcff41fc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cancel-middle-click.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Drag and drop with middle click</title>
+ <style type="text/css">
+div {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ float: left;
+}
+div + div {
+ background: blue;
+}
+div + div + div {
+ background: fuchsia;
+}
+ol {
+ clear: left;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <ol>
+ <li>Drag the orange square over the blue square.</li>
+ <li>Without releasing the drag, click the middle mouse button.</li>
+ <li>If the platform's normal behaviour is to cancel a drag (eg. Windows and Unix+KDE), then the drag should be cancelled;<ul>
+ <li>The drag placeholder should disappear, and the cursor should return to the normal mouse cursor.</li>
+ <li>Move the mouse over the pink square and release the drag. The mouse cursor should remain the normal mouse cursor.</li>
+ </ul></li>
+ <li>If the platform's normal behaviour is not to cancel a drag (eg. Mac and Unix+Gnome), then the drag should not be cancelled;<ul>
+ <li>The drag placeholder should not disappear, and the cursor should be the no-drop cursor.</li>
+ <li>Move the mouse over the pink square and release the drag. The drag placeholder should disappear, and the cursor should return to the normal mouse cursor.</li>
+ </ul></li>
+ <li>Fail in either case if an inappropriate middle click function begins (eg. paste-and-go).</li>
+ </ol>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cancel-right-click.html b/testing/web-platform/tests/html/editing/dnd/platform/cancel-right-click.html
new file mode 100644
index 0000000000..e5159716f4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cancel-right-click.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Drag and drop with right click</title>
+ <style type="text/css">
+div {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ float: left;
+}
+div + div {
+ background: blue;
+}
+div + div + div {
+ background: fuchsia;
+}
+ol {
+ clear: left;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+};
+ </script>
+ </head>
+ <body>
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <ol>
+ <li>Drag the orange square over the blue square.</li>
+ <li>Without releasing the drag, click the right mouse button.</li>
+ <li>If the platform's normal behaviour is to cancel a drag (eg. Windows and Unix+KDE), then the drag should be cancelled;<ul>
+ <li>The drag placeholder should disappear, and the cursor should return to the normal mouse cursor.</li>
+ <li>Move the mouse over the pink square and release the drag. The mouse cursor should remain the normal mouse cursor.</li>
+ </ul></li>
+ <li>If the platform's normal behaviour is not to cancel a drag (eg. Mac and Unix+Gnome), then the drag should not be cancelled;<ul>
+ <li>The drag placeholder should not disappear, and the cursor should be the no-drop cursor.</li>
+ <li>Move the mouse over the pink square and release the drag. The drag placeholder should disappear, and the cursor should return to the normal mouse cursor.</li>
+ </ul></li>
+ <li>Fail in either case if an inappropriate right click function begins (eg. context menu opens).</li>
+ </ol>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/close-drag-001-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-001-manual.html
new file mode 100644
index 0000000000..fc8db3db7d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-001-manual.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>drag &amp; drop - closing a popup while a drag is in operation</title>
+<style type="text/css">
+p.gone, ul { display: none; }
+p.gone + ul { display: block; }
+</style>
+<script type="text/javascript">
+window.onload = function() {
+ if( location.href.match(/#popup$/) ) { document.getElementsByTagName('p')[0].className = 'gone'; }
+ document.getElementsByTagName('ul')[0].ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ setTimeout(function () { window.close(); },100);
+ };
+};
+</script>
+<p><a href="javascript:alert('Click the link normally');" onclick="window.open('close-drag-001.html#popup','_blank');return false;">Open this page in a popup</a>.</p>
+<ul draggable='true'>
+ <li>Drag this text downwards, and do not release the drag.</li>
+ <li>The browser may optionally cancel the drag. The browser may optionally close the popup. Fail if the drag placeholder gets stuck. Fail if the browser crashes. Fail if anything horrible happens. Fail if your pet kitten gets sick.</li>
+ <li>Release the drag.</li>
+ <li>Fail if the drag placeholder gets stuck. Fail if the browser crashes.</li>
+</ul>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/close-drag-002-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-002-manual.html
new file mode 100644
index 0000000000..35e8a5a537
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-002-manual.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>drag &amp; drop - closing the only window while a drag is in operation</title>
+<style type="text/css">
+p.gone, ul { display: none; }
+p.gone + ul { display: block; }
+</style>
+<script type="text/javascript">
+window.onload = function() {
+ if( location.href.match(/#popup$/) ) { document.getElementsByTagName('p')[0].className = 'gone'; }
+ document.getElementsByTagName('ul')[0].ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ setTimeout(function () { window.close(); },100);
+ };
+};
+</script>
+<p><a href="javascript:alert('Click the link normally');" onclick="window.open('close-drag-002.html#popup','_blank');return false;">Open this page in a popup</a>.</p>
+<ul draggable='true'>
+ <li>Close all pages/browser windows except the popup.</li>
+ <li>Drag this text downwards, and do not release the drag.</li>
+ <li>The browser may optionally cancel the drag. The browser may optionally close the popup/window. Fail if the drag placeholder gets stuck. Fail if the browser crashes. Fail if anything horrible happens. Fail if zombie Michael Jackson resurects.</li>
+ <li>Release the drag.</li>
+ <li>Fail if the drag placeholder gets stuck. Fail if the browser crashes.</li>
+</ul>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/close-drag-003-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-003-manual.html
new file mode 100644
index 0000000000..7a118a2208
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-003-manual.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>drag &amp; drop - closing a popup while a drag is in operation out of the window</title>
+<style type="text/css">
+p.gone, ul { display: none; }
+p.gone + ul { display: block; }
+</style>
+<script type="text/javascript">
+window.onload = function() {
+ if( location.href.match(/#popup$/) ) { document.getElementsByTagName('p')[0].className = 'gone'; }
+ document.getElementsByTagName('ul')[0].ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ setTimeout(function () { window.close(); },2000);
+ };
+};
+</script>
+<p><a href="javascript:alert('Click the link normally');" onclick="window.open('close-drag-003.html#popup','_blank');return false;">Open this page in a popup</a>.</p>
+<ul draggable='true'>
+ <li>Close all pages/browser windows except the popup.</li>
+ <li>Ensure that the browser window is not maximised.</li>
+ <li>Quickly drag this text out of the browser window (not over the taskbar), and do not release the drag.</li>
+ <li>The browser may optionally cancel the drag. The browser may optionally close the popup/window. Fail if the drag placeholder gets stuck. Fail if the browser crashes. Fail if anything horrible happens. Fail if your grandmother does not invite you over for Christmas.</li>
+ <li>After 2 seconds, fail if the address field shows that the page has closed, but it is still visibly rendered.</li>
+ <li>Continue dragging back into the browser window (if it is still open).</li>
+ <li>Release the drag.</li>
+ <li>Fail if the drag placeholder gets stuck. Fail if the browser crashes or hangs.</li>
+</ul>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/close-drag-004-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-004-manual.html
new file mode 100644
index 0000000000..c93f5a3508
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-004-manual.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>drag &amp; drop - manually closing a tab while a drag is in operation</title>
+<style type="text/css">
+p.gone, ul { display: none; }
+p.gone + ul { display: block; }
+</style>
+<script type="text/javascript">
+window.onload = function() {
+ if( location.href.match(/#popup$/) ) { document.getElementsByTagName('p')[0].className = 'gone'; }
+ document.getElementsByTagName('ul')[0].ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+</script>
+<p><a href="#popup" target="_blank">Open this page in a new tab</a>.</p>
+<ul draggable='true'>
+ <li>Drag this text downwards, and do not release the drag.</li>
+ <li>Use a keyboard shortcut (eg. Ctrl+W on Windows) to close the tab.</li>
+ <li>The browser may optionally cancel the drag. The browser may optionally close the tab. Fail if the drag placeholder gets stuck. Fail if the browser crashes.</li>
+</ul>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/close-drag-005-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-005-manual.html
new file mode 100644
index 0000000000..1a364113a3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-005-manual.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>drag &amp; drop - manually closing a window while a drag is in operation</title>
+<script type="text/javascript">
+window.onload = function() {
+ document.getElementsByTagName('ul')[0].ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+</script>
+<ul draggable='true'>
+ <li>Drag this text downwards, and do not release the drag.</li>
+ <li>Use a keyboard shortcut (eg. Alt+F4 on Windows) to close the window.</li>
+ <li>The browser may optionally cancel the drag. The browser may optionally close the window. Fail if the drag placeholder gets stuck. Fail if the browser crashes.</li>
+</ul>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/close-drag-006-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-006-manual.html
new file mode 100644
index 0000000000..12f95349e8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/close-drag-006-manual.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>drag &amp; drop - dropping on window decoration after source tab is closed</title>
+<style type="text/css">
+p.gone, ul { display: none; }
+p.gone + ul { display: block; }
+</style>
+<script type="text/javascript">
+window.onload = function() {
+ if( location.href.match(/#popup$/) ) { document.getElementsByTagName('p')[0].className = 'gone'; }
+ document.getElementsByTagName('ul')[0].ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ setTimeout(function () { window.close(); },2000);
+ };
+};
+</script>
+<p><a href="javascript:alert('Click the link normally');" onclick="window.open('close-drag-006.html#popup','_blank');return false;">Open this page in a popup</a>.</p>
+<ul draggable='true'>
+ <li>Ensure the browser window is not maximised.</li>
+ <li>Quickly drag this text upwards out of the browser window, and do not release the drag.</li>
+ <li>This tab should close after a couple of seconds.</li>
+ <li>Dragging downwards over the browser window's title bar, then release the drag.</li>
+ <li>Fail if the drag placeholder gets stuck. Fail if the browser crashes.</li>
+</ul>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/001.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/001.html
new file mode 100644
index 0000000000..953927f917
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/001.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - no special cursor for draggable item</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<div draggable='true'></div><div></div>
+
+<!-- In theory a "move" cursor would be better, but browsers have decided to allow the author to choose that
+- this test checks for browser compatibility. -->
+<p>Move your mouse over the orange box and blue box. It should show the same default mouse cursor in both cases.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/002.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/002.html
new file mode 100644
index 0000000000..9cca00077f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/002.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - CSS cursor must be settable on draggable item</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ cursor: pointer;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<div draggable='true'></div><div></div>
+
+<p>Move your mouse over the orange box and blue box. It should show the same &quot;pointer&quot; mouse cursor in both cases (the one normally used for links).</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/003.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/003.html
new file mode 100644
index 0000000000..3e4407ff69
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/003.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - should show no-drop for non-dropzones</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Use your mouse to drag the orange box over the blue box. While dragging, the mouse cursor should appear as a &quot;no-drop&quot; cursor.<br>
+Release the drag. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/004.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/004.html
new file mode 100644
index 0000000000..8bd8dbfd5e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/004.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - no-drop should override CSS cursor when dragging</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div:first-child {
+ cursor: pointer;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Move your mouse over the orange box. It should show the &quot;pointer&quot; cursor (normally used for links).<br>
+Use your mouse to drag the orange box over the blue box. While dragging, the mouse cursor should appear as a &quot;no-drop&quot; cursor.<br>
+Release the drag. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/005.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/005.html
new file mode 100644
index 0000000000..d5b3378827
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/005.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - should show no-drop for refused-dropzones</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+ blue.ondragenter = blue.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'none';
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Use your mouse to drag the orange box over the blue box. While dragging, the mouse cursor should appear as a &quot;no-drop&quot; cursor.<br>
+Release the drag. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/006.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/006.html
new file mode 100644
index 0000000000..1d36e2e6ad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/006.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - should show drop-allowed for dropzones</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+ blue.ondragenter = blue.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Use your mouse to drag the orange box over the blue box. While dragging over the blue box, the mouse cursor should appear as a &quot;drop-allowed&quot; or &quot;drop-clopy-allowed&quot; cursor.<br>
+Release the drag. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/007.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/007.html
new file mode 100644
index 0000000000..70b9506637
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/007.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - should show drop-allowed for move dropzones</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'move';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+ blue.ondragenter = blue.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'move';
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Use your mouse to drag the orange box over the blue box. While dragging over the blue box, the mouse cursor should appear as a &quot;drop-allowed&quot; or &quot;drop-move-allowed&quot; cursor.<br>
+Release the drag. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/008.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/008.html
new file mode 100644
index 0000000000..827e636cee
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/008.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cursor should show drop-allowed for file drops</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ left: 500px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var blue = document.getElementsByTagName('div')[1], fuchsia = document.getElementsByTagName('div')[2];
+ blue.ondragenter = blue.ondragover = function(e) {
+ //this test assumes the browser will default to opening the file if the page does not want it
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'none';
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ fuchsia.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+
+</script>
+
+<div></div><div></div><div></div>
+
+<p>Use your mouse to drag a *.html file from your computer's desktop over the three coloured squares on this page. The mouse cursor should appear as a &quot;drop-allowed&quot; cursor over all three squares.<br>
+Release the drag over the pink square. If a prompt appears, accept it. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<p>This test assumes the browser will open dropped files natively if they are not handled by a script.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/009.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/009.html
new file mode 100644
index 0000000000..7917ba320e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/009.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - should show drop-allowed for link dropzones</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ }
+ body > div + div {
+ background-color: navy;
+ left: 250px;
+ }
+ p:first-of-type {
+ margin-top: 220px;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'link';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+ blue.ondragenter = blue.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'link';
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+
+</script>
+
+<div draggable='true'></div><div></div>
+
+<p>Use your mouse to drag the orange box over the blue box. While dragging over the blue box, the mouse cursor should appear as a &quot;drop-allowed&quot; or &quot;drop-link-allowed&quot; cursor.<br>
+Release the drag. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/010.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/010.html
new file mode 100644
index 0000000000..d62bcba27a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/010.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - drag ending over draggable element</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background: orange url(../../resources/100x100-navy.png) repeat-y scroll right top;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'link';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+};
+
+</script>
+
+<div draggable='true'></div>
+
+<p>Use your mouse to drag the orange box over the blue box, and release. While dragging over the blue box, the mouse cursor should appear as a &quot;no-drop&quot; cursor.<br>
+Release the drag. The cursor should revert to the default mouse cursor.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/cursors/011.html b/testing/web-platform/tests/html/editing/dnd/platform/cursors/011.html
new file mode 100644
index 0000000000..657245ebae
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/cursors/011.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - cursors after dragging outside window</title>
+<style>
+ body > div {
+ height: 100px;
+ width: 100px;
+ display: inline-block;
+ background: orange;
+ }
+ body > div + div {
+ background: blue;
+ color: black;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'all';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+ var copydiv = document.getElementsByTagName('div')[1];
+ copydiv.ondragenter = copydiv.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ var movediv = document.getElementsByTagName('div')[2];
+ movediv.ondragenter = movediv.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'move';
+ };
+ var linkdiv = document.getElementsByTagName('div')[3];
+ linkdiv.ondragenter = linkdiv.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'link';
+ };
+ var nodrop = document.getElementsByTagName('div')[4];
+ nodrop.ondragenter = nodrop.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'none';
+ };
+};
+
+</script>
+
+<div draggable='true'>&nbsp;</div>
+<div>Copy</div>
+<div>Move</div>
+<div>Link</div>
+<div>No-drop</div>
+
+<p>Use your mouse to drag the orange box out of the browser window (not over the system taskbar), then back into the browser window, and over each of the blue squares in turn.
+While dragging over the squares, the mouse cursor should be the one given by the text in the relevant square.</p>
+<p>This test only applies to platforms with a mouse cursor that can change to indicate drop status.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/drag-keypress-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/drag-keypress-manual.html
new file mode 100644
index 0000000000..113c10dc68
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/drag-keypress-manual.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - pressing tab while dragging</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+ body {
+ height: 5000px;
+ }
+ p {
+ margin-top: 1000px;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ window.scrollBy(0,1000);
+ document.getElementsByTagName('div')[0].ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text', 'dummy text');
+ };
+};
+</script>
+<noscript>Enable JavaScript and reload</noscript>
+<p>Drag the orange square. While still dragging, press the Tab key on your keyboard. Fail if the page scrolls.</p>
+<div draggable="true"></div>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/drag-link-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/drag-link-manual.html
new file mode 100644
index 0000000000..2da4195083
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/drag-link-manual.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging vs selecting links</title>
+ </head>
+ <body>
+
+ <p><a href="">Test link, test link, test link, test link, test link, test link, test link, test link, test link</a></p>
+ <p>Drag the test link above. When dragging vertically, it should drag the link. When dragging horizontally, it should select the text within the link.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/drag-to-title-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/drag-to-title-manual.html
new file mode 100644
index 0000000000..fee1299b83
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/drag-to-title-manual.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Dropping onto the title bar and UI</title>
+<script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('ul')[0].ondragstart = function () {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','c');
+ };
+};
+</script>
+<ul draggable="true">
+ <li>Drag this text upwards to the browser window's title bar.</li>
+ <li>Release the drag. Fail if the drag placeholder does not disappear.</li>
+ <li>Start dragging again over a blank part of the page (below the text). Fail if the placeholder starts following the mouse again.</li>
+ <li>Release the drag over the browser's UI (e.g. the address bar). Fail if the browser crashes.</li>
+</ul>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/file-drop-position.html b/testing/web-platform/tests/html/editing/dnd/platform/file-drop-position.html
new file mode 100644
index 0000000000..7657f359b6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/file-drop-position.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - mouse coordinates during drop</title>
+<style>
+ body > div {
+ height: 5px;
+ width: 5px;
+ background-color: orange;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragover = orange.ondragenter = orange.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+</script>
+
+<div></div>
+
+<p>Save <a href="../resources/fail.png">this image</a> to your desktop. Minimise your browser. Use your pointing device to drag the saved file from your desktop <strong>via your browser's button on your operating system's taskbar</strong> (so that it maximises your browser), onto the small orange box above this text, and release it. If a confirmation dialog appears, accept it. Fail if the browser simply displays the image.</p>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/file-os-to-os.html b/testing/web-platform/tests/html/editing/dnd/platform/file-os-to-os.html
new file mode 100644
index 0000000000..a67905d888
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/file-os-to-os.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging items from the OS to the OS, via the browser window</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dragging and dropping of files - eg. your system's file manager. Ensure that two application windows are open for the external application, showing different folders.</p>
+ <p>Select a file in the first external application window. Drag the file over the browser window, then over the other external application window and release it. Pass if the file is copied/moved to the second window, as expected by the system.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/file-to-system.html b/testing/web-platform/tests/html/editing/dnd/platform/file-to-system.html
new file mode 100644
index 0000000000..f80d5f43c2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/file-to-system.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging a file to the system</title>
+ <style type="text/css">
+span { display: inline-block; height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var drag = document.getElementsByTagName('span')[0];
+ drag.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'copy';
+ var filein = document.getElementsByTagName('input')[0];
+ if( !filein.files ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file API is not supported.';
+ return;
+ }
+ if( !filein.files[0] ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - no file was found in the file input.';
+ return;
+ }
+ var thefile = filein.files[0];
+ try {
+ e.dataTransfer.items.add(thefile);
+ } catch(err) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - error when adding file';
+ e.preventDefault();
+ return;
+ }
+ if( e.dataTransfer.files.length != 1 ) {
+ document.getElementsByTagName('p')[0].innerHTML = 'FAIL - file was not attached to data store';
+ e.preventDefault();
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+ <div>This test only applies to platforms where dropping a file onto a folder in the system's file manager copies/moves the file to that folder.</div>
+ <ol>
+ <li>Open an empty folder in your system's file manager.</li>
+ <li>Select a non-empty file on your computer using the following input: <input type="file"></li>
+ <li>Drag the orange square onto the folder in your system's file manager, and release it:<br><span draggable="true"></span></li>
+ <li>Pass if the file is copied to the folder.</li>
+ </ol>
+ <p></p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/html-to-os-HELPER-FILE.html b/testing/web-platform/tests/html/editing/dnd/platform/html-to-os-HELPER-FILE.html
new file mode 100644
index 0000000000..56fa975d43
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/html-to-os-HELPER-FILE.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging HTML onto the OS - helper file</title>
+ </head>
+ <body>
+
+ <p>This is the helper file. Drop the paragraph here --&gt;</p>
+ <script type="text/javascript">
+document.body.contentEditable = true;
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/html-to-os.html b/testing/web-platform/tests/html/editing/dnd/platform/html-to-os.html
new file mode 100644
index 0000000000..5aaf33b42b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/html-to-os.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging HTML onto the OS</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of HTML from other applications - eg. Google Chrome and Internet Explorer (not Firefox). Load <a href="html-to-os-HELPER-FILE.html">the helper file</a> in the external application.</p>
+ <p draggable="true">Drag this paragraph to the other application and release it. De-select the text in that application if it is selected. Pass if &quot;Pass if this text is on a green background&quot; appears in the other application, and if it has a green background, and if the drag placeholder disappears when the drag is released.</p>
+ <script type="text/javascript">
+document.getElementsByTagName('p')[2].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/html', '<span style="background:lime;color:black;">Pass if this text is on a green background</span>');
+};
+ </script>
+ <p>Disable JavaScript in this browser and repeat the test. Pass if the entire dragged paragraph appears in the other application. Vendors may optionally choose to ignore this requirement.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/html-unicode-to-os.html b/testing/web-platform/tests/html/editing/dnd/platform/html-unicode-to-os.html
new file mode 100644
index 0000000000..433d59d103
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/html-unicode-to-os.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging HTML onto the OS</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of unicode HTML from other applications - eg. Google Chrome (not Firefox or Internet Explorer). Load <a href="html-to-os-HELPER-FILE.html">the helper file</a> in the external application.</p>
+ <p draggable="true">Drag this paragraph to the other application and release it. De-select the text in that application if it is selected. Pass if &quot;Pass if this text is on a green background 中文×ידישруÑÑкий&quot; appears in the other application, and if it has a green background, and if the drag placeholder disappears when the drag is released.</p>
+ <script type="text/javascript">
+document.getElementsByTagName('p')[2].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text/html', '<span style="background:lime;color:black;">Pass if this text is on a green background 中文×ידישруÑÑкий</span>');
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/inputs-no-js.html b/testing/web-platform/tests/html/editing/dnd/platform/inputs-no-js.html
new file mode 100644
index 0000000000..f5f2fbc402
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/inputs-no-js.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dropping selections with JS disabled</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Disable JavaScript</li>
+ <li>Select some text in <input type="text" value="this input"> and drag it into the following input: <input type="text" value=""> - the text you dragged should appear in there.</li>
+ <li>Select some text in this sentence and drag it into the following input: <input type="text" value=""> - the text you dragged should appear in there.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/001.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/001.html
new file mode 100644
index 0000000000..563cee9ea5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/001.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>Interrupted drag with second drag attempt</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], fuchsia = document.getElementsByTagName('div')[2];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = function (e) {
+ alert('JS alert');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ };
+ fuchsia.ondrop = function (e) {
+ document.getElementsByTagName('ol')[0].textContent = e.dataTransfer.getData('text');
+ e.preventDefault();
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square.</li>
+ <li>An alert may appear. Do not dismiss it. If an alert does not appear, PASS, and ignore any further steps.</li>
+ <li>Drag a file that your browser cannot open natively from your computer onto a blank part of the page. If a download dialog appears at any point after this, cancel it, and return to this page.</li>
+ <li>Dismiss the alert without using the left mouse button (eg. use keyboard, or mouse gestures such as gesture-down,gesture-right).</li>
+ <li>Do a short drag and drop within the pink square. If nothing happens, PASS, and ignore any further steps.</li>
+ <li>The word &quot;PASS&quot; should appear in place of this text.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/002.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/002.html
new file mode 100644
index 0000000000..2091f39a7f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/002.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>Interrupted drag with second drag attempt and tab change</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+ body > div + div + div {
+ background-color: fuchsia;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], fuchsia = document.getElementsByTagName('div')[2];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = function (e) {
+ alert('JS alert');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ };
+ fuchsia.ondrop = function (e) {
+ document.getElementsByTagName('ol')[0].textContent = e.dataTransfer.getData('text');
+ e.preventDefault();
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+<div></div>
+
+<ol>
+ <li>Make sure at least one other tab is open.</li>
+ <li>Drag the orange square over the blue square.</li>
+ <li>An alert may appear. Do not dismiss it. If an alert does not appear, PASS, and ignore any further steps.</li>
+ <li>Click the other tab to focus it (or focus it in whatever way works). If you cannot change tabs, PASS, and ignore any further steps.</li>
+ <li>Drag a file that your browser cannot open natively from your computer and drop onto that page, then click the tab for this page to return here. If a download dialog appears at any point after this, cancel it, and return to this page.</li>
+ <li>Dismiss the alert without using the left mouse button (eg. use keyboard, or mouse gestures such as gesture-down,gesture-right).</li>
+ <li>Do a short drag and drop within the pink square. If nothing happens, PASS, and ignore any further steps.</li>
+ <li>The word &quot;PASS&quot; should appear in place of this text.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/003.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/003.html
new file mode 100644
index 0000000000..c09f9a53ec
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/003.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>Interrupted drag with second in-document drag attempt</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+ body > div + div + div {
+ background-color: yellow;
+ }
+ body > div + div + div + div {
+ background-color: fuchsia;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], yellow = document.getElementsByTagName('div')[2], fuchsia = document.getElementsByTagName('div')[3];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ yellow.ondragstart = function (e) {
+ e.dataTransfer.setData('text','FAIL');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = function (e) {
+ alert('JS alert');
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ };
+ fuchsia.ondrop = function (e) {
+ document.getElementsByTagName('ol')[0].textContent = e.dataTransfer.getData('text');
+ e.preventDefault();
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Open this page in two separate tabs, and start on tab #1.</li>
+ <li>Drag the orange square over the blue square.</li>
+ <li>An alert may appear. Do not dismiss it. If an alert does not appear, PASS, and ignore any further steps.</li>
+ <li>Click the other tab to focus it (or focus it in whatever way works). If you cannot change tabs, PASS, and ignore any further steps.</li>
+ <li>On tab #2, drag the yellow square over the blue square. If you cannot drag the yellow square, PASS, and ignore any further steps.</li>
+ <li>An alert may appear. Do not dismiss it.</li>
+ <li>Return to tab #1.</li>
+ <li>Dismiss the alert without using the left mouse button (eg. use keyboard, or mouse gestures such as gesture-down,gesture-right).</li>
+ <li>Do a short drag and drop within the pink square. If nothing happens, PASS, and ignore any further steps.</li>
+ <li>The word &quot;PASS&quot; should appear in place of this text.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/004.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/004.html
new file mode 100644
index 0000000000..41492fe992
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/004.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>File drag during prompt for upload</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragenter = orange.ondragover = function (e) {
+ e.preventDefault();
+ };
+ orange.ondrop = function (e) {
+ e.preventDefault();
+ document.getElementsByTagName('ol')[0].innerHTML = ( e.dataTransfer.files[0] && e.dataTransfer.files[0].name == 'pass.txt' ) ? 'PASS' : 'FAIL';
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Save <a href="pass.txt">pass.txt</a> and <a href="fail.txt">fail.txt</a> onto your computer.</li>
+ <li>Drag pass.txt from your computer onto the orange square.</li>
+ <li>A prompt should appear. Do not dismiss it. If a prompt does not appear, ignore any further steps, and check the tests in ../../file/</li>
+ <li>Drag fail.txt from your computer onto a blank part of this page. Fail if this page is replaced.</li>
+ <li>Accept the prompt. Fail if nothing happens.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/005.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/005.html
new file mode 100644
index 0000000000..a0bdae3f49
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/005.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>File drag and tab change during prompt for upload</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragenter = orange.ondragover = function (e) {
+ e.preventDefault();
+ };
+ orange.ondrop = function (e) {
+ e.preventDefault();
+ document.getElementsByTagName('ol')[0].innerHTML = ( e.dataTransfer.files[0] && e.dataTransfer.files[0].name );
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Save <a href="file1.txt">file1.txt</a> and <a href="file2.txt">file2.txt</a> onto your computer.</li>
+ <li>Open this page in two separate tabs, and start on tab #1.</li>
+ <li>Drag file1.txt from your computer onto the orange square.</li>
+ <li>A prompt should appear. Do not dismiss it. If a prompt does not appear, ignore any further steps, and check the tests in ../../file/</li>
+ <li>Click the other tab to focus it (or focus it in whatever way works). If you cannot change tabs, PASS, and ignore any further steps.</li>
+ <li>Drag file2.txt from your computer onto the orange square. Fail if this text (but not the orange square) is replaced without any prompt.</li>
+ <li>If a prompt appears, do not dismiss it.</li>
+ <li>Return to tab #1.</li>
+ <li>Accept the prompt. Fail if it cannot be accepted. Pass if this text (but not the orange square) is replaced with the text: file1.txt</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/006.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/006.html
new file mode 100644
index 0000000000..73cfb2d696
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/006.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Alert during dragstart</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], hasfired = false;
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ if( hasfired ) { return; }
+ hasfired = true;
+ alert('JS alert');
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Drag the orange square downwards until the drag placeholder appears.</li>
+ <li>An alert may appear - release the mouse over the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards again. Fail if that is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/007.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/007.html
new file mode 100644
index 0000000000..6c8daba200
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/007.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Alert during drag</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], hasfired = false;
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ orange.ondrag = function (e) {
+ if( hasfired ) { return; }
+ hasfired = true;
+ alert('JS alert');
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Drag the orange square downwards until the drag placeholder appears.</li>
+ <li>An alert may appear - release the mouse over the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards again. Fail if that is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/008.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/008.html
new file mode 100644
index 0000000000..2339e29431
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/008.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Alert during dragenter</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ alert('JS alert');
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square.</li>
+ <li>An alert may appear - release the mouse over the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/009.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/009.html
new file mode 100644
index 0000000000..e19b1c5d72
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/009.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<title>Alert during dragover</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], hasfired = false;
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( hasfired ) { return; }
+ hasfired = true;
+ alert('JS alert');
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square.</li>
+ <li>An alert may appear - release the mouse over the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/010.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/010.html
new file mode 100644
index 0000000000..583746bbad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/010.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>Alert during dragleave</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ blue.ondragleave = function (e) {
+ alert('JS alert');
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square, then back over the orange square.</li>
+ <li>An alert may appear - release the mouse over the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/011.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/011.html
new file mode 100644
index 0000000000..8b72b63768
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/011.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Alert during drop</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ blue.ondrop = function (e) {
+ e.preventDefault();
+ alert('JS alert');
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square, then release it.</li>
+ <li>An alert may appear - release the mouse over the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right).</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/012.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/012.html
new file mode 100644
index 0000000000..1b31468b43
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/012.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Alert during dragend</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], hasfired = false;
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ blue.ondrop = function (e) {
+ e.preventDefault();
+ };
+ orange.ondragend = function (e) {
+ if( hasfired ) { return; }
+ hasfired = true;
+ alert('JS alert');
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square, then release it.</li>
+ <li>An alert may appear - release the mouse over the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right).</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/013.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/013.html
new file mode 100644
index 0000000000..9e9747601a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/013.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - drag interrupted by alert must not break mouse interaction with UI</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+ body > div + div {
+ margin-top: 10px;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text', 'dummy text');
+ };
+ blue.ondragover = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ if( !window.doneonce ) {
+ alert('2. It should also ideally be possible to dismiss this dialog with your mouse/pointing device (do not use mouse gestures).');
+ }
+ window.doneonce = true;
+ };
+ blue.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ alert('1. It should ideally be possible to dismiss this dialog with your mouse/pointing device (do not use mouse gestures).');
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ };
+};
+</script>
+
+<p>Drag the orange square onto the blue square.</p>
+<div draggable="true"></div>
+<div></div>
+
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/014.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/014.html
new file mode 100644
index 0000000000..23f6a3e4db
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/014.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - drop interrupted by alert must not break mouse interaction with UI</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ }
+ body > div + div {
+ margin-top: 10px;
+ height: 200px;
+ width: 200px;
+ background-color: blue;
+ }
+</style>
+
+<script>
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text', 'dummy text');
+ };
+ blue.ondragover = blue.ondragenter = function(e) {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'copy';
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ alert('It should ideally be possible to dismiss this dialog with your mouse/pointing device (do not use mouse gestures).');
+ };
+};
+</script>
+
+<p>Drag the orange square onto the blue square.</p>
+<div draggable="true"></div>
+<div></div>
+
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/015.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/015.html
new file mode 100644
index 0000000000..650c15ac3e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/015.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Alert during dragstart with release over dialog</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], hasfired = false;
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ if( hasfired ) { return; }
+ hasfired = true;
+ alert('Release the mouse over this dialog');
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Drag the orange square downwards until the drag placeholder appears.</li>
+ <li>An alert may appear - release the mouse over the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards again. Fail if that is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/016.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/016.html
new file mode 100644
index 0000000000..d4c42941e3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/016.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Alert during drag with release over dialog</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], hasfired = false;
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ orange.ondrag = function (e) {
+ if( hasfired ) { return; }
+ hasfired = true;
+ alert('Release the mouse over this dialog');
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Drag the orange square downwards until the drag placeholder appears.</li>
+ <li>An alert may appear - release the mouse over the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards again. Fail if that is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/017.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/017.html
new file mode 100644
index 0000000000..1c10d8a370
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/017.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Alert during dragenter with release over dialog</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ alert('Release the mouse over this dialog');
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square.</li>
+ <li>An alert may appear - release the mouse over the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/018.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/018.html
new file mode 100644
index 0000000000..c0067d00d8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/018.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<title>Alert during dragover with release over dialog</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1], hasfired = false;
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( hasfired ) { return; }
+ hasfired = true;
+ alert('Release the mouse over this dialog');
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square.</li>
+ <li>An alert may appear - release the mouse over the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/019.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/019.html
new file mode 100644
index 0000000000..dca273d9d4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/019.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>Alert during dragleave with release over dialog</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+ body > div + div {
+ background-color: navy;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], blue = document.getElementsByTagName('div')[1];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','PASS');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ blue.ondragleave = function (e) {
+ alert('Release the mouse over this dialog');
+ };
+};
+</script>
+<div draggable="true"></div>
+<div></div>
+
+<ol>
+ <li>Drag the orange square over the blue square, then back over the orange square.</li>
+ <li>An alert may appear - release the mouse over the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+ <li>Try to drag the orange square downwards. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/020.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/020.html
new file mode 100644
index 0000000000..e10fa46f2a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/020.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>Alert during dragenter for file drag</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragenter = function (e) {
+ e.preventDefault();
+ alert('JS alert');
+ };
+ orange.ondragover = orange.ondrop = function (e) {
+ e.preventDefault();
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Drag a file (one that your browser cannot open natively, such as an executable file) from your system file manager over the orange square.</li>
+ <li>An alert may appear - release the mouse over a blank part of the page, not the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag and accept any prompt to upload the file.</li>
+ <li>If a file download dialog appears, cancel it and return to this page.</li>
+ <li>Fail if the alert reappears.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/021.html b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/021.html
new file mode 100644
index 0000000000..d393e0846c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/021.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>Alert during dragenter for file drag with release over dialog</title>
+<style>
+ body > div {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragenter = function (e) {
+ e.preventDefault();
+ alert('JS alert');
+ };
+ orange.ondragover = orange.ondrop = function (e) {
+ e.preventDefault();
+ };
+};
+</script>
+<div draggable="true"></div>
+
+<ol>
+ <li>Drag a file (one that your browser cannot open natively, such as an executable file) from your system file manager over the orange square.</li>
+ <li>An alert may appear - release the mouse over the dialog. Dismiss it (you may need to use keyboard or mouse gestures such as gesture-down,gesture-right). If it does not appear, release the drag and accept any prompt to upload the file.</li>
+ <li>If a file download dialog appears, cancel it and return to this page.</li>
+ <li>Fail if the alert reappears.</li>
+ <li>The mouse cursor may continue to show that a drag is in operation. If so, attempt to select some of this text with the mouse before continuing to the text step.</li>
+ <li>Fail if the mouse continues to show that a drag is in operation.</li>
+ <li>Try to select some text in this sentence. Fail if it is not possible.</li>
+</ol>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/fail.txt b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/fail.txt
new file mode 100644
index 0000000000..fc26162516
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/fail.txt
@@ -0,0 +1 @@
+FAIL \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/file1.txt b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/file1.txt
new file mode 100644
index 0000000000..6c8db5df2b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/file1.txt
@@ -0,0 +1 @@
+file 1 \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/file2.txt b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/file2.txt
new file mode 100644
index 0000000000..dd4128ed9e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/file2.txt
@@ -0,0 +1 @@
+file 2 \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/interrupt/pass.txt b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/pass.txt
new file mode 100644
index 0000000000..fc26162516
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/interrupt/pass.txt
@@ -0,0 +1 @@
+FAIL \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/keyboardshortcuts.html b/testing/web-platform/tests/html/editing/dnd/platform/keyboardshortcuts.html
new file mode 100644
index 0000000000..441ffd3df1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/keyboardshortcuts.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Keyboard shortcuts during drag/drop</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Select some text in this sentence and begin dragging it.</li>
+ <li>While dragging, use your keyboard shortcut to reload the page. It should work without cancelling the drag.</li>
+ <li>While dragging, use your keyboard shortcut to select all text on the page. It should work without cancelling the drag.</li>
+ <li>While dragging, use your keyboard shortcut to open a new tab. It should work without cancelling the drag.</li>
+ <li>While dragging, use your keyboard shortcut to switch to another tab. It should work without cancelling the drag.</li>
+ <li>While dragging, use your keyboard shortcuts to go back and forward in history (<a href="#next">use this link first to add a history entry if needed</a>). It should work without cancelling the drag.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/all.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/all.html
new file mode 100644
index 0000000000..bb76d3a0cd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/all.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with 'all'</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var countedDrops = 0, bde, bdo, bdo2, fde, fdo, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'all';
+ bde = bdo = bdo2 = fde = fdo = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ if( !bde ) { bde = e.dataTransfer.dropEffect; }
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( !fde ) { fde = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var ode = e.dataTransfer.dropEffect, temparray;
+ if( bde == bdo && bdo2 == fdr && fde == fdr && fdo == fdr && ode == fdr ) {
+ if( bde == fdr ) {
+ tmparray = [fdr];
+ } else {
+ tmparray = [bde,fdr];
+ }
+ } else if( bde == bdo && bdo2 == fdo && fde == fdo && !fdr ) {
+ tmparray = [bde,fdo,ode];
+ } else {
+ tmparray = [bde,bdo,bdo2,fde,fdo,fdr,ode];
+ }
+ tmparray[ tmparray.length - 1 ] = '<strong>' + tmparray[ tmparray.length - 1 ] + '<\/strong>';
+ document.getElementsByTagName('div')[3].innerHTML = (++countedDrops) + '. ' + tmparray.join('=&gt;');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the drag, then the keys</li>
+ <li>Check that the correct drop effect is produced in each case. If the mouse cursor can change to indicate the final type of drop effect that will take place, it should change to the relevant type (the one in bold) once the modifier keys are pressed.</li>
+ </ol>
+ <!-- Windows Explorer uses Ctrl for copy, Shift for move, Alt or Ctrl+Shift for link. -->
+ <!-- Anything else invalidates the modifiers, and reverts to default. -->
+ <table>
+ <caption>Windows</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Alt</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>copy</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- KDE Konqueror uses Ctrl for copy, Shift for move, Ctrl+Shift for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <!-- Gnome Nautilus uses Ctrl for copy, Shift for move, Ctrl+Shift for link (and Alt to open a context menu). -->
+ <!-- Anything else (including Alt when combined with other modifiers) is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Unix/Linux</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td>copy=&gt;<strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- Mac Finder uses Option for copy, Command for move, Command+Option for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Mac</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Option/alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+
+ <tr><td>Ctrl+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift+Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift+Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Ctrl+Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Shift+Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copy.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copy.html
new file mode 100644
index 0000000000..597fdc03a9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copy.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with 'copy'</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var countedDrops = 0, bde, bdo, bdo2, fde, fdo, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'copy';
+ bde = bdo = bdo2 = fde = fdo = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ if( !bde ) { bde = e.dataTransfer.dropEffect; }
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( !fde ) { fde = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var ode = e.dataTransfer.dropEffect, temparray;
+ if( bde == bdo && bdo2 == fdr && fde == fdr && fdo == fdr && ode == fdr ) {
+ if( bde == fdr ) {
+ tmparray = [fdr];
+ } else {
+ tmparray = [bde,fdr];
+ }
+ } else if( bde == bdo && bdo2 == fdo && fde == fdo && !fdr ) {
+ tmparray = [bde,fdo,ode];
+ } else {
+ tmparray = [bde,bdo,bdo2,fde,fdo,fdr,ode];
+ }
+ tmparray[ tmparray.length - 1 ] = '<strong>' + tmparray[ tmparray.length - 1 ] + '<\/strong>';
+ document.getElementsByTagName('div')[3].innerHTML = (++countedDrops) + '. ' + tmparray.join('=&gt;');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the drag, then the keys</li>
+ <li>Check that the correct drop effect is produced in each case. If the mouse cursor can change to indicate the final type of drop effect that will take place, it should change to the relevant type (the one in bold) once the modifier keys are pressed.</li>
+ </ol>
+ <!-- Windows Explorer uses Ctrl for copy, Shift for move, Alt or Ctrl+Shift for link. -->
+ <!-- Anything else invalidates the modifiers, and reverts to default. -->
+ <table>
+ <caption>Windows</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>copy</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- KDE Konqueror uses Ctrl for copy, Shift for move, Ctrl+Shift for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <!-- Gnome Nautilus uses Ctrl for copy, Shift for move, Ctrl+Shift for link (and Alt to open a context menu). -->
+ <!-- Anything else (including Alt when combined with other modifiers) is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Unix/Linux</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- Mac Finder uses Option for copy, Command for move, Command+Option for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Mac</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Option/alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift+Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift+Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ </tbody>
+ </table>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copylink.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copylink.html
new file mode 100644
index 0000000000..042031d004
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copylink.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with 'copyLink'</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var countedDrops = 0, bde, bdo, bdo2, fde, fdo, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'copyLink';
+ bde = bdo = bdo2 = fde = fdo = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ if( !bde ) { bde = e.dataTransfer.dropEffect; }
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( !fde ) { fde = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var ode = e.dataTransfer.dropEffect, temparray;
+ if( bde == bdo && bdo2 == fdr && fde == fdr && fdo == fdr && ode == fdr ) {
+ if( bde == fdr ) {
+ tmparray = [fdr];
+ } else {
+ tmparray = [bde,fdr];
+ }
+ } else if( bde == bdo && bdo2 == fdo && fde == fdo && !fdr ) {
+ tmparray = [bde,fdo,ode];
+ } else {
+ tmparray = [bde,bdo,bdo2,fde,fdo,fdr,ode];
+ }
+ tmparray[ tmparray.length - 1 ] = '<strong>' + tmparray[ tmparray.length - 1 ] + '<\/strong>';
+ document.getElementsByTagName('div')[3].innerHTML = (++countedDrops) + '. ' + tmparray.join('=&gt;');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the drag, then the keys</li>
+ <li>Check that the correct drop effect is produced in each case. If the mouse cursor can change to indicate the final type of drop effect that will take place, it should change to the relevant type (the one in bold) once the modifier keys are pressed.</li>
+ </ol>
+ <!-- Windows Explorer uses Ctrl for copy, Shift for move, Alt or Ctrl+Shift for link. -->
+ <!-- Anything else invalidates the modifiers, and reverts to default. -->
+ <table>
+ <caption>Windows</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>copy</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- KDE Konqueror uses Ctrl for copy, Shift for move, Ctrl+Shift for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <!-- Gnome Nautilus uses Ctrl for copy, Shift for move, Ctrl+Shift for link (and Alt to open a context menu). -->
+ <!-- Anything else (including Alt when combined with other modifiers) is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Unix/Linux</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td>copy=&gt;<strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- Mac Finder uses Option for copy, Command for move, Command+Option for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Mac</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Option/alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift+Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift+Command</td><td>copy=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+ <tr><td>Shift+Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Command+Option</td><td>copy=&gt;<strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copymove.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copymove.html
new file mode 100644
index 0000000000..b03e4f2677
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/copymove.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with 'copyMove'</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var countedDrops = 0, bde, bdo, bdo2, fde, fdo, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'copyMove';
+ bde = bdo = bdo2 = fde = fdo = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ if( !bde ) { bde = e.dataTransfer.dropEffect; }
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( !fde ) { fde = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var ode = e.dataTransfer.dropEffect, temparray;
+ if( bde == bdo && bdo2 == fdr && fde == fdr && fdo == fdr && ode == fdr ) {
+ if( bde == fdr ) {
+ tmparray = [fdr];
+ } else {
+ tmparray = [bde,fdr];
+ }
+ } else if( bde == bdo && bdo2 == fdo && fde == fdo && !fdr ) {
+ tmparray = [bde,fdo,ode];
+ } else {
+ tmparray = [bde,bdo,bdo2,fde,fdo,fdr,ode];
+ }
+ tmparray[ tmparray.length - 1 ] = '<strong>' + tmparray[ tmparray.length - 1 ] + '<\/strong>';
+ document.getElementsByTagName('div')[3].innerHTML = (++countedDrops) + '. ' + tmparray.join('=&gt;');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the drag, then the keys</li>
+ <li>Check that the correct drop effect is produced in each case. If the mouse cursor can change to indicate the final type of drop effect that will take place, it should change to the relevant type (the one in bold) once the modifier keys are pressed.</li>
+ </ol>
+ <!-- Windows Explorer uses Ctrl for copy, Shift for move, Alt or Ctrl+Shift for link. -->
+ <!-- Anything else invalidates the modifiers, and reverts to default. -->
+ <table>
+ <caption>Windows</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Alt</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>copy</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- KDE Konqueror uses Ctrl for copy, Shift for move, Ctrl+Shift for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <!-- Gnome Nautilus uses Ctrl for copy, Shift for move, Ctrl+Shift for link (and Alt to open a context menu). -->
+ <!-- Anything else (including Alt when combined with other modifiers) is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Unix/Linux</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Alt+Shift</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- Mac Finder uses Option for copy, Command for move, Command+Option for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Mac</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Option/alt</td><td><strong>copy</strong></td></tr>
+ <tr><td>Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+
+ <tr><td>Ctrl+Shift</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Shift+Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Option</td><td><strong>copy</strong></td></tr>
+ <tr><td>Ctrl+Shift+Command</td><td>copy=&gt;<strong>move</strong></td></tr>
+ <tr><td>Ctrl+Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Command+Option</td><td>copy=&gt;link=&gt;<strong>none</strong></td></tr>
+ </tbody>
+ </table>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/link.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/link.html
new file mode 100644
index 0000000000..db13709f65
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/link.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with 'link'</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var countedDrops = 0, bde, bdo, bdo2, fde, fdo, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'link';
+ bde = bdo = bdo2 = fde = fdo = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ if( !bde ) { bde = e.dataTransfer.dropEffect; }
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( !fde ) { fde = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var ode = e.dataTransfer.dropEffect, temparray;
+ if( bde == bdo && bdo2 == fdr && fde == fdr && fdo == fdr && ode == fdr ) {
+ if( bde == fdr ) {
+ tmparray = [fdr];
+ } else {
+ tmparray = [bde,fdr];
+ }
+ } else if( bde == bdo && bdo2 == fdo && fde == fdo && !fdr ) {
+ tmparray = [bde,fdo,ode];
+ } else {
+ tmparray = [bde,bdo,bdo2,fde,fdo,fdr,ode];
+ }
+ tmparray[ tmparray.length - 1 ] = '<strong>' + tmparray[ tmparray.length - 1 ] + '<\/strong>';
+ document.getElementsByTagName('div')[3].innerHTML = (++countedDrops) + '. ' + tmparray.join('=&gt;');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the drag, then the keys</li>
+ <li>Check that the correct drop effect is produced in each case. If the mouse cursor can change to indicate the final type of drop effect that will take place, it should change to the relevant type (the one in bold) once the modifier keys are pressed.</li>
+ </ol>
+ <!-- Windows Explorer uses Ctrl for copy, Shift for move, Alt or Ctrl+Shift for link. -->
+ <!-- Anything else invalidates the modifiers, and reverts to default. -->
+ <table>
+ <caption>Windows</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift</td><td>link=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>link</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- KDE Konqueror uses Ctrl for copy, Shift for move, Ctrl+Shift for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <!-- Gnome Nautilus uses Ctrl for copy, Shift for move, Ctrl+Shift for link (and Alt to open a context menu). -->
+ <!-- Anything else (including Alt when combined with other modifiers) is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Unix/Linux</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift</td><td>link=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt+Shift</td><td>link=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- Mac Finder uses Option for copy, Command for move, Command+Option for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Mac</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>link</strong></td></tr>
+ <tr><td>Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Option/alt</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Command</td><td>link=&gt;move=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Option</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Command</td><td>link=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Option</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Command</td><td>link=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Command+Option</td><td><strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Option</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Shift+Command</td><td>link=&gt;move=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Command+Option</td><td><strong>link</strong></td></tr>
+ <tr><td>Shift+Command+Option</td><td><strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Command+Option</td><td><strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/linkmove.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/linkmove.html
new file mode 100644
index 0000000000..ed564a0f3a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/linkmove.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with 'linkMove'</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var countedDrops = 0, bde, bdo, bdo2, fde, fdo, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'linkMove';
+ bde = bdo = bdo2 = fde = fdo = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ if( !bde ) { bde = e.dataTransfer.dropEffect; }
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( !fde ) { fde = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var ode = e.dataTransfer.dropEffect, temparray;
+ if( bde == bdo && bdo2 == fdr && fde == fdr && fdo == fdr && ode == fdr ) {
+ if( bde == fdr ) {
+ tmparray = [fdr];
+ } else {
+ tmparray = [bde,fdr];
+ }
+ } else if( bde == bdo && bdo2 == fdo && fde == fdo && !fdr ) {
+ tmparray = [bde,fdo,ode];
+ } else {
+ tmparray = [bde,bdo,bdo2,fde,fdo,fdr,ode];
+ }
+ tmparray[ tmparray.length - 1 ] = '<strong>' + tmparray[ tmparray.length - 1 ] + '<\/strong>';
+ document.getElementsByTagName('div')[3].innerHTML = (++countedDrops) + '. ' + tmparray.join('=&gt;');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the drag, then the keys</li>
+ <li>Check that the correct drop effect is produced in each case. If the mouse cursor can change to indicate the final type of drop effect that will take place, it should change to the relevant type (the one in bold) once the modifier keys are pressed.</li>
+ </ol>
+ <!-- Windows Explorer uses Ctrl for copy, Shift for move, Alt or Ctrl+Shift for link. -->
+ <!-- Anything else invalidates the modifiers, and reverts to default. -->
+ <table>
+ <caption>Windows</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift</td><td>link=&gt;<strong>move</strong></td></tr>
+ <tr><td>Alt</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>link</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- KDE Konqueror uses Ctrl for copy, Shift for move, Ctrl+Shift for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <!-- Gnome Nautilus uses Ctrl for copy, Shift for move, Ctrl+Shift for link (and Alt to open a context menu). -->
+ <!-- Anything else (including Alt when combined with other modifiers) is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Unix/Linux</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift</td><td>link=&gt;<strong>move</strong></td></tr>
+ <tr><td>Alt</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt+Shift</td><td>link=&gt;<strong>move</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- Mac Finder uses Option for copy, Command for move, Command+Option for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Mac</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>link</strong></td></tr>
+ <tr><td>Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Option/alt</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Command</td><td>link=&gt;<strong>move</strong></td></tr>
+
+ <tr><td>Ctrl+Shift</td><td><strong>link</strong></td></tr>
+ <tr><td>Ctrl+Option</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Command</td><td>link=&gt;<strong>move</strong></td></tr>
+ <tr><td>Shift+Option</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Command</td><td>link=&gt;<strong>move</strong></td></tr>
+ <tr><td>Command+Option</td><td><strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Option</td><td>link=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Shift+Command</td><td>link=&gt;<strong>move</strong></td></tr>
+ <tr><td>Ctrl+Command+Option</td><td><strong>link</strong></td></tr>
+ <tr><td>Shift+Command+Option</td><td><strong>link</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Command+Option</td><td><strong>link</strong></td></tr>
+ </tbody>
+ </table>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/move.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/move.html
new file mode 100644
index 0000000000..7e9caf12ca
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/move.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with 'move'</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var countedDrops = 0, bde, bdo, bdo2, fde, fdo, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'move';
+ bde = bdo = bdo2 = fde = fdo = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ if( !bde ) { bde = e.dataTransfer.dropEffect; }
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( !fde ) { fde = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var ode = e.dataTransfer.dropEffect, temparray;
+ if( bde == bdo && bdo2 == fdr && fde == fdr && fdo == fdr && ode == fdr ) {
+ if( bde == fdr ) {
+ tmparray = [fdr];
+ } else {
+ tmparray = [bde,fdr];
+ }
+ } else if( bde == bdo && bdo2 == fdo && fde == fdo && !fdr ) {
+ tmparray = [bde,fdo,ode];
+ } else {
+ tmparray = [bde,bdo,bdo2,fde,fdo,fdr,ode];
+ }
+ tmparray[ tmparray.length - 1 ] = '<strong>' + tmparray[ tmparray.length - 1 ] + '<\/strong>';
+ document.getElementsByTagName('div')[3].innerHTML = (++countedDrops) + '. ' + tmparray.join('=&gt;');
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the drag, then the keys</li>
+ <li>Check that the correct drop effect is produced in each case. If the mouse cursor can change to indicate the final type of drop effect that will take place, it should change to the relevant type (the one in bold) once the modifier keys are pressed.</li>
+ </ol>
+ <!-- Windows Explorer uses Ctrl for copy, Shift for move, Alt or Ctrl+Shift for link. -->
+ <!-- Anything else invalidates the modifiers, and reverts to default. -->
+ <table>
+ <caption>Windows</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>None</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl</td><td>move=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift</td><td><strong>move</strong></td></tr>
+ <tr><td>Alt</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td><strong>move</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td><strong>move</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- KDE Konqueror uses Ctrl for copy, Shift for move, Ctrl+Shift for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <!-- Gnome Nautilus uses Ctrl for copy, Shift for move, Ctrl+Shift for link (and Alt to open a context menu). -->
+ <!-- Anything else (including Alt when combined with other modifiers) is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Unix/Linux</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl</td><td>move=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift</td><td><strong>move</strong></td></tr>
+ <tr><td>Alt</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl+Shift</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Alt</td><td>move=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Alt+Shift</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl+Alt+Shift</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+ </tbody>
+ </table>
+ <!-- Mac Finder uses Option for copy, Command for move, Command+Option for link. -->
+ <!-- Anything else is ignored completely, and has no effect on the modifier sequence. -->
+ <table>
+ <caption>Mac</caption>
+ <thead>
+ <tr><th>Modifier</th><th>Drop effect</th></tr>
+ </thead>
+ <tbody>
+ <tr><td><strong>none</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl</td><td><strong>move</strong></td></tr>
+ <tr><td>Shift</td><td><strong>move</strong></td></tr>
+ <tr><td>Option/alt</td><td>move=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Command</td><td><strong>move</strong></td></tr>
+
+ <tr><td>Ctrl+Shift</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl+Option</td><td>move=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Command</td><td><strong>move</strong></td></tr>
+ <tr><td>Shift+Option</td><td>move=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Command</td><td><strong>move</strong></td></tr>
+ <tr><td>Command+Option</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Option</td><td>move=&gt;copy=&gt;<strong>none</strong></td></tr>
+ <tr><td>Ctrl+Shift+Command</td><td><strong>move</strong></td></tr>
+ <tr><td>Ctrl+Command+Option</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+ <tr><td>Shift+Command+Option</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+
+ <tr><td>Ctrl+Shift+Command+Option</td><td>move=&gt;link=&gt;<strong>none</strong></td></tr>
+ </tbody>
+ </table>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzone.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzone.html
new file mode 100644
index 0000000000..973d240878
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzone.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys being used with a dropzone attribute</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text/plain','http://example.com/');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondrop = function (e) {
+ //dropzone overrides the modifier, always, and ignores effectAllowed
+ e.preventDefault();
+ document.getElementsByTagName('div')[3].innerHTML = ( e.dataTransfer.dropEffect == 'link' ) ? 'PASS' : 'FAIL';
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div dropzone="link string:text/plain"></div>
+ <div>&nbsp;</div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform to request a &quot;move&quot; drop effect (eg. Shift on Windows/Unix/Linux, Command on Mac)</li>
+ <li>Continue dragging over the pink square</li>
+ <li>If supported by the platform, the mouse cursor should show that a &quot;link&quot; drop effect will be used</li>
+ <li>Release the drag, then the keys</li>
+ <li>Fail if no new text appears above this list</li>
+ </ol>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzoneevents.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzoneevents.html
new file mode 100644
index 0000000000..8c5d5334e1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/onlydropzoneevents.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys being used with a dropzone attribute and dragenter/dragover events</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text/plain','http://example.com/');
+ e.dataTransfer.effectAllowed = 'copy';
+ };
+ var fuchsia = document.getElementsByTagName('div')[2], fde, fdo;
+ fuchsia.ondragenter = function (e) {
+ fde = e.dataTransfer.dropEffect;
+ };
+ fuchsia.ondragover = function (e) {
+ fdo = e.dataTransfer.dropEffect;
+ };
+ fuchsia.ondrop = function (e) {
+ //dropzone overrides the modifier, always, and ignores effectAllowed
+ e.preventDefault();
+ var sequence = ([fde,fdo,e.dataTransfer.dropEffect]).join('=&gt;')
+ var desiredsequence = (['move','move','link']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[3].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[3].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div dropzone="link string:text/plain"></div>
+ <div>&nbsp;</div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform to request a &quot;move&quot; drop effect (eg. Shift on Windows/Unix/Linux, Command on Mac)</li>
+ <li>Continue dragging over the pink square</li>
+ <li>If supported by the platform, the mouse cursor should show that a &quot;link&quot; drop effect will be used</li>
+ <li>Release the drag, then the keys</li>
+ <li>Fail if no new text appears above this list</li>
+ </ol>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrag-manual.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrag-manual.html
new file mode 100644
index 0000000000..91c0c584bb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrag-manual.html
@@ -0,0 +1,125 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys being released before end of drag</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+div:first-child + div + div + div {
+ height: 100px;
+ width: 100px;
+ background: yellow;
+ display: inline-block;
+}
+div {
+ font-family: monospace;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var bde, bdo, bdo2, fde, fdo, fdo2, yde, ydo, ydr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'linkMove';
+ bde = bdo = bdo2 = fde = fdo = fdo2 = yde = ydo = ydr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ bde = e.dataTransfer.dropEffect;
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ fde = e.dataTransfer.dropEffect;
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ fdo2 = e.dataTransfer.dropEffect;
+ };
+ var yellow = document.getElementsByTagName('div')[3];
+ yellow.ondragenter = function (e) {
+ e.preventDefault();
+ yde = e.dataTransfer.dropEffect;
+ };
+ yellow.ondragover = function (e) {
+ e.preventDefault();
+ if( !ydo ) { ydo = e.dataTransfer.dropEffect; }
+ };
+ yellow.ondrop = function (e) {
+ e.preventDefault();
+ ydr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var sequence = ([bde,bdo,bdo2,fde,fdo,fdo2,yde,ydo,ydr,e.dataTransfer.dropEffect]).join('=&gt;')
+ var desiredsequence = (['link','link','move','move','move','link','link','link','link','link']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[4].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[4].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform to request a &quot;move&quot; drop effect (eg. Shift on Windows/Unix/Linux, Command on Mac)</li>
+ <li>If supported by the platform, the mouse cursor should show that a &quot;move&quot; drop effect will be used</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the modifier keys</li>
+ <li>If supported by the platform, the mouse cursor should show that a &quot;link&quot; drop effect will be used</li>
+ <li>Continue dragging over the yellow square</li>
+ <li>Release the drag</li>
+ <li>Fail if no new text appears above this list</li>
+ </ol>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrop.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrop.html
new file mode 100644
index 0000000000..adaa92e8e2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/releasemodifiersdrop.html
@@ -0,0 +1,108 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys being released before drop</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+div {
+ font-family: monospace;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var bde, bdo, bdo2, fde, fdo, fdo2, fdr;
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'linkMove';
+ bde = bdo = bdo2 = fde = fdo = fdo2 = fdr = '';
+ };
+ var blue = document.getElementsByTagName('div')[1];
+ blue.ondragenter = function (e) {
+ e.preventDefault();
+ bde = e.dataTransfer.dropEffect;
+ };
+ blue.ondragover = function (e) {
+ e.preventDefault();
+ if( !bdo ) {
+ bdo = e.dataTransfer.dropEffect;
+ }
+ //bdo2 always collects dropEffect, since it can change multiple times in rapid succession when pressing multiple modifiers
+ //test cares about the last dropEffect that was seen before leaving the blue square, and that will be stored in bdo2
+ bdo2 = e.dataTransfer.dropEffect;
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ fde = e.dataTransfer.dropEffect;
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( !fdo ) { fdo = e.dataTransfer.dropEffect; }
+ fdo2 = e.dataTransfer.dropEffect;
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ fdr = e.dataTransfer.dropEffect;
+ };
+ orange.ondragend = function (e) {
+ var sequence = ([bde,bdo,bdo2,fde,fdo,fdo2,fdr,e.dataTransfer.dropEffect]).join('=&gt;')
+ var desiredsequence = (['link','link','move','move','move','link','link','link']).join('=&gt;')
+ if( sequence == desiredsequence ) {
+ document.getElementsByTagName('div')[3].innerHTML = 'PASS';
+ } else {
+ document.getElementsByTagName('div')[3].innerHTML = 'FAIL, got:<br>'+sequence+'<br>instead of:<br>'+desiredsequence;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div><strong>&nbsp;</strong></div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform to request a &quot;move&quot; drop effect (eg. Shift on Windows/Unix/Linux, Command on Mac)</li>
+ <li>If supported by the platform, the mouse cursor should show that a &quot;move&quot; drop effect will be used</li>
+ <li>Continue dragging over the pink square</li>
+ <li>Release the modifier keys, and wait for at least half a second</li>
+ <li>If supported by the platform, the mouse cursor should show that a &quot;link&quot; drop effect will be used</li>
+ <li>Release the drag, then the keys</li>
+ <li>Fail if no new text appears above this list</li>
+ </ol>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/modifiers/scriptmodified.html b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/scriptmodified.html
new file mode 100644
index 0000000000..c5b17b463f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/modifiers/scriptmodified.html
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Modifier keys selecting dropEffect with script overriding it</title>
+ <style type="text/css">
+div:first-child {
+ height: 100px;
+ width: 100px;
+ background: orange;
+ display: inline-block;
+}
+div:first-child + div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+ display: inline-block;
+}
+div:first-child + div + div {
+ height: 100px;
+ width: 100px;
+ background: fuchsia;
+ display: inline-block;
+}
+table {
+ display: inline-table;
+ margin-right: 1em;
+ border-collapse: collapse;
+}
+table, th, td {
+ border: 1px solid black;
+}
+thead th {
+ background: silver;
+ color: black;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var dragenterbefore = '', dragenterafter = '', dragoverbefore = '', dragoverafter = '';
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','http://example.com/');
+ e.dataTransfer.effectAllowed = 'all';
+ };
+ var fuchsia = document.getElementsByTagName('div')[2];
+ fuchsia.ondragenter = function (e) {
+ e.preventDefault();
+ if( e.dataTransfer.dropEffect != 'link' ) {
+ dragenterbefore = e.dataTransfer.dropEffect;
+ }
+ try {
+ e.dataTransfer.dropEffect = 'move';
+ } catch(e) {}
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ dragenterafter = e.dataTransfer.dropEffect;
+ }
+ };
+ fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( e.dataTransfer.dropEffect != 'link' ) {
+ dragoverbefore = e.dataTransfer.dropEffect;
+ }
+ try {
+ e.dataTransfer.dropEffect = 'move';
+ } catch(e) {}
+ if( e.dataTransfer.dropEffect != 'move' ) {
+ dragoverafter = e.dataTransfer.dropEffect;
+ }
+ };
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ document.getElementsByTagName('div')[3].innerHTML = ( dragenterbefore || dragenterafter || dragoverbefore || dragoverafter || e.dataTransfer.dropEffect != 'move' ) ? ( 'FAIL' +
+ ( dragenterbefore ? ( '<br>dragenter.dropEffect was '+dragenterbefore+' instead of link' ) : '' ) +
+ ( dragenterafter ? ( '<br>dragenter.dropEffect after writing was '+dragenterafter+' instead of move' ) : '' ) +
+ ( dragoverbefore ? ( '<br>dragover.dropEffect was '+dragoverbefore+' instead of link' ) : '' ) +
+ ( dragoverafter ? ( '<br>dragover.dropEffect after writing was '+dragoverafter+' instead of move' ) : '' ) +
+ ( ( e.dataTransfer.dropEffect != 'move' ) ? ( '<br>drop.dropEffect was '+e.dataTransfer.dropEffect+' instead of move' ) : '' )
+ ) : 'PASS';
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+ <div></div>
+ <div></div>
+ <div>&nbsp;</div>
+ <ol>
+ <li>Drag the orange square over the blue square</li>
+ <li>Press the relevant modifier keys for your platform to request a &quot;link&quot; drop effect (eg. Alt on Windows, Ctrl+Shift on Unix/Linux, Command+Option on Mac)</li>
+ <li>Continue dragging over the pink square</li>
+ <li>If supported by the platform, the mouse cursor should show that a &quot;move&quot; drop effect will be used</li>
+ <li>Release the drag, then the keys</li>
+ <li>Fail if no new text appears above this list</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/moving-window.html b/testing/web-platform/tests/html/editing/dnd/platform/moving-window.html
new file mode 100644
index 0000000000..a11c101800
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/moving-window.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>drag &amp; drop - moving windows must not start a drag</title>
+<script type="text/javascript">
+
+window.onload = function() {
+ var li1 = document.getElementsByTagName('li')[3], li2 = document.getElementsByTagName('li')[4];
+ li1.ondragstart = li2.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ this.innerHTML = 'FAIL';
+ };
+ li1.onmousedown = function () { window.moveBy(0,10); };
+ li2.onmousedown = function () { setTimeout(function () {
+ window.moveBy(0,10);
+ },10); };
+};
+
+</script>
+<p></p>
+<ol>
+ <li onclick="window.open(location.href,'_blank','width=500,height=300');">Click here to open this page in a popup window.</li>
+ <li>Ensure that this popup window is not maximised (or tab, in the case of tabs being rendered as an MDI).</li>
+ <li>Ensure that your browser settings allow browser windows to be moved by scripts.</li>
+ <li draggable='true'>Press your mouse down on this text but do not move it afterwards. Fail if a drag operation has started (eg. if the mouse cursor shows that you are dragging something, or if some drag placeholder text appears, or if this text changes).</li>
+ <li draggable='true'>Press your mouse down on this text but do not move it afterwards. Fail if a drag operation has started (eg. if the mouse cursor shows that you are dragging something, or if some drag placeholder text appears, or if this text changes).</li>
+</ol>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/overlappingwindows.html b/testing/web-platform/tests/html/editing/dnd/platform/overlappingwindows.html
new file mode 100644
index 0000000000..ed707478da
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/overlappingwindows.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<title>Dropping on always-on-top application windows that overlay the browser</title>
+<style>
+ html, body, ol {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ width: 100%;
+ background: blue;
+ color: black;
+ list-style-position: inside;
+ }
+ div {
+ height: 100px;
+ width: 100px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-top: -50px;
+ margin-left: -50px;
+ background: orange;
+ }
+</style>
+
+<script type="text/javascript">
+
+window.onload = function() {
+ var orange = document.getElementsByTagName('div')[0];
+ orange.ondragstart = function(e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'dummy text');
+ };
+ var blue = document.getElementsByTagName('ol')[0];
+ blue.ondragenter = blue.ondragover = function(e) {
+ e.preventDefault();
+ };
+ blue.ondrop = function(e) {
+ e.preventDefault();
+ this.innerHTML = 'FAIL';
+ };
+};
+
+</script>
+
+<ol>
+ <li>Position the browser window so that the blue part of this page extends behind the system taskbar.</li>
+ <li>Use your mouse to drag the orange box over a part of the taskbar that overlays the blue part of this page.</li>
+ <li>If supported by the platform, the mouse cursor should <em>not</em> show the browser's custom &quot;copy&quot; cursor, and should instead show the system's expected cursor for dropping on that part of the taskbar.</li>
+ <li>Release the drag. Fail if the text on this page changes.</li>
+ <li>Reload and repeat this test for:<ul>
+ <li>Where the blue part of this page extends under an always-on-top window (eg. the Windows Task Manager).</li>
+ <li>Where the blue part of this page extends under an always-on-top notification (eg. a system tray info balloon).</li>
+ </ul></li>
+</ol>
+<div draggable='true'></div>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/placeholderposition1.html b/testing/web-platform/tests/html/editing/dnd/platform/placeholderposition1.html
new file mode 100644
index 0000000000..b79bd4fbce
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/placeholderposition1.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - position of the placeholder for a dragged element</title>
+ <style type="text/css">
+div { background: orange; color: black; width: 200px; padding: 10px; border: 10px solid orange; margin: 10px; }
+ </style>
+ </head>
+ <body>
+
+ <div draggable="true">Drag the orange block around the page (and only over the page), using the pixel in its top-left corner. When dragging, the top-left corner of the dragged placeholder should exactly match the position of the mouse cursor.</div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/placeholderposition2.html b/testing/web-platform/tests/html/editing/dnd/platform/placeholderposition2.html
new file mode 100644
index 0000000000..373309d1a9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/placeholderposition2.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - position of the placeholder for a dragged selection</title>
+ <style type="text/css">
+p { cursor: default; }
+ </style>
+ </head>
+ <body>
+
+ <p>Select the first word in this sentence. Drag the selection downwards, using the pixel in the top-left corner of the selection highlight. When dragging, the top-left corner of the dragged placeholder should exactly match the position of the mouse cursor.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/plugindrop.html b/testing/web-platform/tests/html/editing/dnd/platform/plugindrop.html
new file mode 100644
index 0000000000..c34e60c4a9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/plugindrop.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop sequence should end when dropping over a plugin</title>
+ <style type="text/css">
+div {
+ background: orange;
+ height: 100px;
+ width: 100px;
+}
+object {
+ height: 100px;
+ width: 100px;
+}
+ </style>
+ </head>
+ <body>
+
+ <p>Use your pointing device to drag the orange square onto the blue square, and release it. The drag placeholder should disappear after releasing (or as the pointer moves over the blue square). Try dragging the orange square again. Fail if it does not respond when trying to drag it.</p>
+ <div draggable="true" ondragstart="event.dataTransfer.setData('Text','dummy text');"></div>
+ <p><object data="../resources/boxnavy.swf"></object></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-between-ui.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-between-ui.html
new file mode 100644
index 0000000000..e2fc81089d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-between-ui.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging text from UI widgets to each other</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Select some text in the address bar.</li>
+ <li>Drag the selection to another text input in the UI, and release it.</li>
+ <li>Pass if:<ol>
+ <li>A visible representation of the selected text appears to be dragged.</li>
+ <li>The mouse cursor shows that the drop will be allowed over the input.</li>
+ <li>The selected text appears in the input.</li>
+ </ol></li>
+ <li>Repeat the test with other UI text inputs as drag source/destination.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-from-os.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-from-os.html
new file mode 100644
index 0000000000..cdda1f40ca
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-from-os.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging a selection from the OS</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that allows dragging of selections into other applications - eg. Wordpad (write.exe) on Windows. Ensure that the external application is open.</p>
+ <p>Move the browser window so it sits about 200 pixels down from the top of the screen.</p>
+ <p>Subtest 1. Write some text into the external application (if needed), containing both unicode and Latin characters. Select the text in the external application, and drag the selection into the following input:<br><textarea rows="3" cols="50"></textarea><br>Pass if the text you selected appears in the input.</p>
+ <p>Subtest 2. Select the text in the external application, and drag the selection into the following block:
+ <span style="background:orange;display:block;min-height:100px;width:300px;" ondragenter="return false;" ondragover="return false;" ondrop="this.innerHTML = arguments[0].dataTransfer.getData('text/plain');return false;"></span>
+ Pass if the text you selected appears in the block.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-from-ui.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-from-ui.html
new file mode 100644
index 0000000000..a2a5d09f96
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-from-ui.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging text from UI widgets</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Select some text in the address bar.</li>
+ <li>Drag the selection to the following textarea, and release it: <br><textarea rows="3" cols="50"></textarea></li>
+ <li>Pass if:<ol>
+ <li>A visible representation of the selected text appears to be dragged.</li>
+ <li>The mouse cursor shows that the drop will be allowed over the textarea.</li>
+ <li>The selected text appears in the textarea.</li>
+ </ol></li>
+ <li>Repeat the test with other UI text inputs, including ones that allow linebreaks (if any).</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-to-os.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-to-os.html
new file mode 100644
index 0000000000..d596014695
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-to-os.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging a selection onto the OS</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of text from other applications - eg. Wordpad (write.exe) on Windows. Ensure that the external application is open.</p>
+ <p>Select this text. Drag the selection to the other application and release it. Pass if the text you selected appears in the other application, and the drag placeholder disappears when the drag is released.</p>
+ <p>Disable JavaScript and repeat the test. It should still pass.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui-via.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui-via.html
new file mode 100644
index 0000000000..11eb2092e2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui-via.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging text to UI widgets via window decoration</title>
+ </head>
+ <body>
+
+ <p>This test assumes that the address bar is positioned below the window's title bar, and above the page. If your browser uses an alternative layout, adjust your testing accordingly to ensure that the testing interacts with the edges of the window decoration.</p>
+ <ol>
+ <li>Select some text on this page.</li>
+ <li>Drag the selection to your browser's title bar.</li>
+ <li>Drag the selection back to your browser's address field. Fail if the mouse cursor shows that the text cannot be dropped.</li>
+ <li>Fail if the cursor and/or input focus caret respond in the wrong position (ie. if the drag operation seems to think the address field is offset from its actual location).</li>
+ <li>Release it. Pass if the selected text appears in the address field.</li>
+ <li>Repeat the test with other UI text inputs.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui.html
new file mode 100644
index 0000000000..43b0877f8b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-to-ui.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging text to UI widgets</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Select some text on this page.</li>
+ <li>Drag the selection to your browser's address field. Fail if the mouse cursor shows that the text cannot be dropped.</li>
+ <li>Release it. Pass if the selected text appears in the address field.</li>
+ <li>Repeat the test with other UI text inputs.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-ui-to-self.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-ui-to-self.html
new file mode 100644
index 0000000000..897a258838
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-ui-to-self.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging text from UI widgets to themselves</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Select all text in the address bar.</li>
+ <li>Drag the selection around a little, and release it over the address bar again.</li>
+ <li>Try to select the text in this sentence.</li>
+ <li>Pass if:<ol>
+ <li>A visible representation of the selected text appears to be dragged.</li>
+ <li>The mouse cursor shows that the drop will be allowed over the address bar.</li>
+ <li>The text on this page can be selected afterwards.</li>
+ </ol></li>
+ <li>Repeat the test with other UI text inputs as drag source/destination.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/selection-unicode-to-os.html b/testing/web-platform/tests/html/editing/dnd/platform/selection-unicode-to-os.html
new file mode 100644
index 0000000000..e43fac6173
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/selection-unicode-to-os.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging a selection containing unicode onto the OS</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of text from other applications, and supports unicode - eg. Wordpad (write.exe) on Windows. Ensure that the external application is open.</p>
+ <p>Select the following non-English text --> 中文×ידישруÑÑкий &lt;-- Drag the selection to the other application and release it. Pass if the text you selected appears in the other application, and the drag placeholder disappears when the drag is released.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/taskbardrop.html b/testing/web-platform/tests/html/editing/dnd/platform/taskbardrop.html
new file mode 100644
index 0000000000..b96606cd37
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/taskbardrop.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dropping on the taskbar</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where dragging over taskbar buttons will switch between applications.</p>
+ <p>Ensure that at least one other application is open and visible on the taskbar. Maximise this browser window. Select this text. Drag the selection downwards, over a blank part of the system taskbar, and release the drag. Drag the selection again, over the taskbar button for the other application (without passing over any other buttons on the taskbar). Fail if the drag placeholder does not appear when dragging the selection. Pass if the other application becomes focused.</p>
+ <p>Repeat this test while dropping on other parts of the taskbar, such as:</p>
+ <ul>
+ <li>The Start menu button (or your system's equivalent)</li>
+ <li>The Quick Launch bar (or your system's equivalent)</li>
+ <li>The System Tray (or your system's equivalent)</li>
+ <li>The expander button for the System Tray (or your system's equivalent)</li>
+ <li>The button for another application</li>
+ <li>The label for a folder toolbar - drop a folder onto the Windows taskbar to create one (or your system's equivalent)</li>
+ <li>The menu button for a folder toolbar (or your system's equivalent)</li>
+ </ul>
+ <p><strong>WARNING: different operating system versions handle this differently - Windows XP Taskbar in particular can cause several more problems than the Windows 7 Taskbar</strong></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/taskbarminimise.html b/testing/web-platform/tests/html/editing/dnd/platform/taskbarminimise.html
new file mode 100644
index 0000000000..36ffe2f5c9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/taskbarminimise.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - minimising using the taskbar</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where dragging over the taskbar (or a specific button on it) will minimise all applications.</p>
+ <p>Select this text. Drag the selection downwards, over a blank part of the system taskbar (or a minimise-all button if provided by the system). Hold the drag until all applications have minimised, then drag upwards over the desktop. Pass if the drag placeholder is still visible.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/text-os-to-os.html b/testing/web-platform/tests/html/editing/dnd/platform/text-os-to-os.html
new file mode 100644
index 0000000000..95ca8b597c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/text-os-to-os.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging items from the OS to the OS, via the browser window</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of text from other applications - eg. Wordpad (write.exe) on Windows. Ensure that the external application is open.</p>
+ <p>Select some text in the external application. Drag the selection over the browser window, then back to the other application and release it. Pass if the text you selected appears in the other application.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/platform/text-to-os.html b/testing/web-platform/tests/html/editing/dnd/platform/text-to-os.html
new file mode 100644
index 0000000000..1bf754e2c3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/platform/text-to-os.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head>
+ <title>drag and drop - dragging plain text onto the OS</title>
+ </head>
+ <body>
+
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of text from other applications - eg. Wordpad (write.exe) on Windows. Ensure that the external application is open.</p>
+ <p draggable="true">Drag this paragraph to the other application and release it. Pass if &quot;PASS&quot; appears in the other application, and the drag placeholder disappears when the drag is released.</p>
+ <script type="text/javascript">
+document.getElementsByTagName('p')[2].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('Text', 'PASS');
+};
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/001.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/001.xhtml
new file mode 100644
index 0000000000..a4c0cba89e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/001.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during canvas drag and drop roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('canvas').toDataURL('image/png'));
+ window.location.reload();}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondragenter="event.preventDefault()" ondragover="return false" ondrop="addImage(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern around page and then drag it back and drop on itself. It should be duplicated once you drop it.</p>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/002.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/002.xhtml
new file mode 100644
index 0000000000..19ba5d0373
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/002.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during PNG image drag and drop roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location.reload()" ondragenter="event.preventDefault()" ondrop="addImage(event)" ondragover="return false" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle around page and then drag it back and drop on itself. It should be duplicated once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/003.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/003.xhtml
new file mode 100644
index 0000000000..133df3525c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/003.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during SVG image drag and drop roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location.reload()" ondragenter="event.preventDefault()" ondrop="addImage(event)" ondragover="return false" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle around page and then drag it back and drop on itself. It should be duplicated once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/004.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/004.xhtml
new file mode 100644
index 0000000000..26489b5626
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/004.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during text input selection drag and drop roundtrip</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="window.location.reload()" ondragenter="event.preventDefault();" ondragover="return false" ondrop="event.preventDefault();this.value = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'"/></p>
+<p>Drag selected text around page and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/005.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/005.xhtml
new file mode 100644
index 0000000000..1e0e1bb828
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/005.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during selection drag and drop roundtrip</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="window.location.reload()" ondragenter="event.preventDefault()" ondragover="return false" ondrop="document.querySelector('span').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">Drag me</span></p>
+<p>Drag selected text around page and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/006.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/006.xhtml
new file mode 100644
index 0000000000..9d633554f1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/006.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during link drag and drop roundtrip</title>
+<script type="application/ecmascript">
+function checkLink(event)
+ {document.querySelector('a').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy';window.location.reload()" ondragenter="event.preventDefault()" ondrop="checkLink(event)" ondragover="return false">Drag me</a></p>
+<p>Drag link around page and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/007.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/007.xhtml
new file mode 100644
index 0000000000..b5ab3e785c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/007.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop roundtrip with text/plain data</title>
+<style type="text/css">
+div[ondragenter]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ draggable="true"
+ ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/plain',' PASS ');window.location.reload();"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+<p>Drag blue box around page and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/008.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/008.xhtml
new file mode 100644
index 0000000000..1da66103e4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/008.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop roundtrip with text/uri-list data</title>
+<style type="text/css">
+div[ondragenter]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ draggable="true"
+ ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/uri-list','data:text/plain,PASS');window.location.reload()"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/uri-list').substr(16,4) + ' '))"
+/>
+<p>Drag blue box around page and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/009-1.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/009-1.xhtml
new file mode 100644
index 0000000000..9e98b93204
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/009-1.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop: helper file</title>
+</head>
+<body ondragleave="window.location.reload()">
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy'">Drag me</a></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/009.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/009.xhtml
new file mode 100644
index 0000000000..421e562dc7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/009.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during link drag and drop from object</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body dropzone="copy string:text/uri-list" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="009-1.xhtml">XHTML document</object>
+<p>Drag link above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/010-1.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/010-1.xhtml
new file mode 100644
index 0000000000..51d2012f79
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/010-1.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop: helper file</title>
+</head>
+<body onload="document.querySelector('input').select()" ondragleave="window.location.reload()">
+<p><input value="Drag me"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/010.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/010.xhtml
new file mode 100644
index 0000000000..c214dff6de
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/010.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during text input selection drag and drop from object</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body dropzone="copy string:text/plain" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="010-1.xhtml">XHTML document</object>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/011-1.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/011-1.xhtml
new file mode 100644
index 0000000000..61c46c57ff
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/011-1.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop: helper file</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))" ondragleave="window.location.reload()">
+<p>Drag me</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/011.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/011.xhtml
new file mode 100644
index 0000000000..d24f5003fd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/011.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during selection drag and drop from object</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body dropzone="copy string:text/plain" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="011-1.xhtml">XHTML document</object>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/012-1.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/012-1.xhtml
new file mode 100644
index 0000000000..b7f0ab11a1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/012-1.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop: helper file</title>
+</head>
+<body ondragleave="window.location.reload()">
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/012-2.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/012-2.xhtml
new file mode 100644
index 0000000000..84bd41f9e4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/012-2.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/reload/012.xhtml b/testing/web-platform/tests/html/editing/dnd/reload/012.xhtml
new file mode 100644
index 0000000000..d4028cc720
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/reload/012.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Reload during drag and drop of image between frames</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="012-1.xhtml"/>
+<frame src="012-2.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/001.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/001.xhtml
new file mode 100644
index 0000000000..f8cd7575f8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/001.xhtml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing canvas element during drag and drop</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('canvas').toDataURL('image/png'));
+ document.querySelector('p').removeChild(canvas);}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern to the silver box below and drop it. It should be copied to the box once you drop it there.</p>
+<div ondragover="return false" ondrop="addImage(event)"/>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/002.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/002.xhtml
new file mode 100644
index 0000000000..8ebf1e2b8a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/002.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing image element during drag and drop of PNG image</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';document.querySelector('p').removeChild(document.querySelector('img'))" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag green circle to the silver box below and drop it. It should be copied to the box once you drop it there.</p>
+<div ondragover="return false" ondrop="addImage(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/003.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/003.xhtml
new file mode 100644
index 0000000000..e7a88093a6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/003.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing image element during drag and drop of SVG image</title>
+<style type="text/css">
+div
+ {height:100px;
+ width:100px;
+ padding:20px;
+ background-color:silver;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy';document.querySelector('p').removeChild(document.querySelector('img'))" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag green circle to the silver box below and drop it. It should be copied to the box once you drop it there.</p>
+<div ondragover="return false" ondrop="addImage(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/004.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/004.xhtml
new file mode 100644
index 0000000000..4544b2e0a0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/004.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing input element during drag and drop of selection</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="document.querySelector('p').removeChild(document.querySelector('input'))"/></p>
+<p>Drag selection above to the navy box below and drop it. You should see word PASS once you drop it in the box.</p>
+<div ondragover="return false" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'))"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/005.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/005.xhtml
new file mode 100644
index 0000000000..da07f5bdd4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/005.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing span element during drag and drop of selection</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="document.querySelector('p').removeChild(document.querySelector('span'))">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. You should see word PASS once you drop it in the box.</p>
+<div ondragover="return false" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'))"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/006.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/006.xhtml
new file mode 100644
index 0000000000..86c1e66c39
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/006.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing a element during drag and drop of link</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy';document.querySelector('p').removeChild(document.querySelector('a'))">Drag me</a></p>
+<p>Drag link above to the navy box below and drop it. You should see word PASS once you drop it in the box.</p>
+<div ondragover="return false" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'))"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/007.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/007.xhtml
new file mode 100644
index 0000000000..e82c29bc75
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/007.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing div element during drag and drop with text/plain data</title>
+<style type="text/css">
+div
+ {width:40px;
+ height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:gray;}
+p + div
+ {background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ draggable="true"
+ ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/plain','PASS');document.querySelector('body').removeChild(document.querySelector('div'))"
+/>
+<p>Drag gray box above to the navy box below and drop it. You should see word PASS once you drop it.</p>
+<div
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/008.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/008.xhtml
new file mode 100644
index 0000000000..c52a1c9cf0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/008.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing div element during drag and drop with text/uri-list data</title>
+<style type="text/css">
+div
+ {width:40px;
+ height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:gray;}
+p + div
+ {background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ draggable="true"
+ ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/uri-list','data:text/plain,PASS');document.querySelector('body').removeChild(document.querySelector('div'))"
+/>
+<p>Drag gray box above to the navy box below and drop it. You should see word PASS once you drop it.</p>
+<div
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/uri-list').substr(16,4)))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/009.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/009.xhtml
new file mode 100644
index 0000000000..7ccf07402e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/009.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Changing iframe content during drag and drop of link</title>
+<style type="text/css">
+iframe
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('iframe').src = 'data:text/plain,Drop%20link%20outside%20this%20frame'" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'">
+<iframe src="helper-drag-me-link.xhtml">XHTML document</iframe>
+<p>Drag link above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/010.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/010.xhtml
new file mode 100644
index 0000000000..151cbaeec8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/010.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Changing object content during drag and drop of link</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('object').setAttribute('data','data:text/plain,Drop%20link%20outside%20this%20frame')" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="helper-drag-me-link.xhtml">XHTML document</object>
+<p>Drag link above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/011.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/011.xhtml
new file mode 100644
index 0000000000..43b04516a6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/011.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Changing iframe content during drag and drop of text input selection</title>
+<style type="text/css">
+iframe
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('iframe').src = 'data:text/plain,Drop%20selection%20outside%20this%20frame'" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<iframe src="helper-drag-me-input.xhtml">XHTML document</iframe>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/012.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/012.xhtml
new file mode 100644
index 0000000000..1bff73a477
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/012.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Changing object content during drag and drop of text input selection</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('object').setAttribute('data','data:text/plain,Drop%20selection%20outside%20this%20frame')" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="helper-drag-me-input.xhtml">XHTML document</object>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/013.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/013.xhtml
new file mode 100644
index 0000000000..8567afc52f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/013.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Changing iframe content during drag and drop of selection</title>
+<style type="text/css">
+iframe
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('iframe').src = 'data:text/plain,Drop%20selection%20outside%20this%20frame'" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<iframe src="helper-drag-me-p.xhtml">XHTML document</iframe>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/014.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/014.xhtml
new file mode 100644
index 0000000000..c3da31ef5e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/014.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Changing object content during drag and drop of selection</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('object').setAttribute('data','data:text/plain,Drop%20selection%20outside%20this%20frame')" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="helper-drag-me-p.xhtml">XHTML document</object>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/015.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/015.xhtml
new file mode 100644
index 0000000000..59f2db7982
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/015.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing iframe during drag and drop of link</title>
+<style type="text/css">
+iframe
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('body').removeChild(document.querySelector('iframe'))" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'">
+<iframe src="helper-drag-me-link.xhtml">XHTML document</iframe>
+<p>Drag link above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/016.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/016.xhtml
new file mode 100644
index 0000000000..2f71d92f30
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/016.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing object during drag and drop of link</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('body').removeChild(document.querySelector('object'))" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="helper-drag-me-link.xhtml">XHTML document</object>
+<p>Drag link above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/017.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/017.xhtml
new file mode 100644
index 0000000000..dfc8ed8cc0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/017.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing iframe during drag and drop of text input selection</title>
+<style type="text/css">
+iframe
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('body').removeChild(document.querySelector('iframe'))" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<iframe src="helper-drag-me-input.xhtml">XHTML document</iframe>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/018.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/018.xhtml
new file mode 100644
index 0000000000..aaa35b7b47
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/018.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing object during drag and drop of text input selection</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('body').removeChild(document.querySelector('object'))" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="helper-drag-me-input.xhtml">XHTML document</object>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/019.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/019.xhtml
new file mode 100644
index 0000000000..d8df327f33
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/019.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing iframe during drag and drop of selection</title>
+<style type="text/css">
+iframe
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('body').removeChild(document.querySelector('iframe'))" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<iframe src="helper-drag-me-p.xhtml">XHTML document</iframe>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/020.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/020.xhtml
new file mode 100644
index 0000000000..6e56c4b8ff
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/020.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing object during drag and drop of selection</title>
+<style type="text/css">
+object
+ {height:100px;
+ width:300px;
+ border:solid medium navy;}
+</style>
+</head>
+<body ondragenter="event.preventDefault();document.querySelector('body').removeChild(document.querySelector('object'))" ondragover="return false" ondrop="document.querySelector('p').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">
+<object type="application/xhtml+xml" data="helper-drag-me-p.xhtml">XHTML document</object>
+<p>Drag selection above out of frame and drop it somewhere on the page. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/021.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/021.xhtml
new file mode 100644
index 0000000000..51d7c22015
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/021.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Removing a element during drag and drop of url</title>
+<style type="text/css">
+div
+ {min-height:100px;
+ width:100px;
+ padding:20px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy';document.querySelector('p').removeChild(document.querySelector('a'))">Drag me</a></p>
+<p>Drag selection above to the navy box below and drop it. You should see word PASS once you drop it in the box.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="document.querySelector('div').appendChild(document.createTextNode((event.dataTransfer.getData('url') == 'data:text/plain,1')?'PASS':'FAIL'))"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/022-1.html b/testing/web-platform/tests/html/editing/dnd/remove/022-1.html
new file mode 100644
index 0000000000..fe65c60453
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/022-1.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Removing drop targetted document before the queue is processed</title>
+<style>
+ html, body, div {
+ height: 100%;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ background-color: blue;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var blue = document.getElementsByTagName('div')[0];
+ blue.ondragenter = blue.ondragover = blue.ondrop = function (e) {
+ e.preventDefault();
+ };
+ window.addEventListener('message',function (){
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET','/common/blank.html?pipe=trickle(d5)',false);
+ xhr.send(null); //should not end within the lifetime of this document
+ },false);
+};
+</script>
+<div></div>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/022.html b/testing/web-platform/tests/html/editing/dnd/remove/022.html
new file mode 100644
index 0000000000..17e8e0c449
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/022.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Removing drop targetted document before the queue is processed</title>
+<style>
+ span, iframe {
+ height: 200px;
+ width: 200px;
+ background-color: orange;
+ display: inline-block;
+ border: none;
+ }
+</style>
+<script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('span')[0];
+ orange.ondragstart = function (e) {
+ e.dataTransfer.setData('text','dummy text');
+ e.dataTransfer.effectAllowed = 'all';
+ document.getElementsByTagName('iframe')[0].contentWindow.postMessage('dummy message','*');
+ setTimeout(function () {
+ document.getElementsByTagName('p')[0].removeChild(document.getElementsByTagName('iframe')[0]);
+ },4000);
+ };
+};
+</script>
+<p><span draggable="true"></span> <iframe height="200" width="200" src="022-1.html"></iframe></p>
+
+<p>Drag the orange square over the blue square, then release it. Wait 5 seconds for the blue square to disappear. Pass if you can select this text.</li>
+<noscript><p>Enable JavaScript and reload</p></noscript>
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-input.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-input.xhtml
new file mode 100644
index 0000000000..ca87466cd2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-input.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-link.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-link.xhtml
new file mode 100644
index 0000000000..9062014b45
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-link.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop: helper file</title>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy'">Drag me</a></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-p.xhtml b/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-p.xhtml
new file mode 100644
index 0000000000..5e11544c4e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/remove/helper-drag-me-p.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/100x100-navy.png b/testing/web-platform/tests/html/editing/dnd/resources/100x100-navy.png
new file mode 100644
index 0000000000..ee8f953c00
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/100x100-navy.png
Binary files differ
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/1x1-transparent.gif b/testing/web-platform/tests/html/editing/dnd/resources/1x1-transparent.gif
new file mode 100644
index 0000000000..e565824aaf
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/1x1-transparent.gif
Binary files differ
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/32mb.py b/testing/web-platform/tests/html/editing/dnd/resources/32mb.py
new file mode 100644
index 0000000000..8513f227e9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/32mb.py
@@ -0,0 +1,12 @@
+thirty_two_megabytes = 32 * 1024 * 1024
+chunk = b'ab' * 512 * 512
+chunk_length = len(chunk)
+
+def main(request, response):
+ def content():
+ bytes_sent = 0
+ while bytes_sent < thirty_two_megabytes:
+ yield chunk
+ bytes_sent += chunk_length
+
+ return [(b"Content-Type", b"text/plain")], content()
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/boxnavy.swf b/testing/web-platform/tests/html/editing/dnd/resources/boxnavy.swf
new file mode 100644
index 0000000000..c4ef5889a3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/boxnavy.swf
Binary files differ
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/circle.png b/testing/web-platform/tests/html/editing/dnd/resources/circle.png
new file mode 100644
index 0000000000..8bb141cb8c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/circle.png
Binary files differ
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/crossorigin.sub.js b/testing/web-platform/tests/html/editing/dnd/resources/crossorigin.sub.js
new file mode 100644
index 0000000000..d6aaa18ad7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/crossorigin.sub.js
@@ -0,0 +1,13 @@
+var httpHostMain = '{{domains[]}}'; //name of the server that this page must accessed over port 80
+var httpHostAlias = '{{domains[www]}}'; //another hostname (must be a subdomain so document.domain can be set to a higher domain) that accesses the same content, over HTTP
+var httpsHostAlias = httpHostAlias; //another hostname (can be same as httpHostAlias) that accesses the same content, over HTTPS port
+var httpPortAlias = {{ports[http][0]}}; //another port that accesses the same content on the current hostname, over HTTP
+//XXX HTTPS
+//var httpsPortAlias = ports[https][0]; //another port that accesses the same content on the httpsHostAlias, over HTTPS
+var httpsPortAlias = 8443;
+
+function crossOriginUrl(subdomain, relative_url) {
+ var a = document.createElement("a");
+ a.href = relative_url;
+ return a.href.replace(location.href.replace("://", "://" + subdomain + "."));
+}
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/dragdrop_support.js b/testing/web-platform/tests/html/editing/dnd/resources/dragdrop_support.js
new file mode 100644
index 0000000000..f5a1d6417f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/dragdrop_support.js
@@ -0,0 +1,9 @@
+function AddEventListenersForElement(evt, callback, capture, element)
+{
+ element.addEventListener(evt, callback, capture);
+}
+
+function LogTestResult(result)
+{
+ document.getElementById("test_result").firstChild.data = result;
+}
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/fail.png b/testing/web-platform/tests/html/editing/dnd/resources/fail.png
new file mode 100644
index 0000000000..b593380333
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/fail.png
Binary files differ
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/filler.html b/testing/web-platform/tests/html/editing/dnd/resources/filler.html
new file mode 100644
index 0000000000..6ca9868ac2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/filler.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title>Filler text</title>
+</head>
+<body>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text</p>
+ <p>Filler text (end)</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/pass.png b/testing/web-platform/tests/html/editing/dnd/resources/pass.png
new file mode 100644
index 0000000000..2fa1e0ac06
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/pass.png
Binary files differ
diff --git a/testing/web-platform/tests/html/editing/dnd/resources/test-helper.js b/testing/web-platform/tests/html/editing/dnd/resources/test-helper.js
new file mode 100644
index 0000000000..fa9ec20c62
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/resources/test-helper.js
@@ -0,0 +1,50 @@
+'use strict';
+
+// Moves the pointer to the center of `element`. If `element` is contained within an `iframe`, use
+// the `iframe` parameter to indicate which `iframe` contains `element`. This function
+// returns a promise that will resolve once the pointer has been moved.
+
+const mouseMoveToCenter = (element, iframe = undefined) => {
+ let clientRect = element.getBoundingClientRect();
+ let centerX = (clientRect.left + clientRect.right) / 2;
+ let centerY = (clientRect.top + clientRect.bottom) / 2;
+ if(iframe != undefined) {
+ clientRect = iframe.getBoundingClientRect();
+ centerX += clientRect.left;
+ centerY += clientRect.top;
+ }
+ return new test_driver.Actions()
+ .pointerMove(Math.ceil(centerX), Math.ceil(centerY))
+ .send();
+};
+
+// The dragDropTest function can be used for tests which require the drag and drop movement.
+// `dragElement` takes the element that needs to be dragged and `dropElement` is the element which
+// you want to drop the `dragElement` on. `onDropCallback` is called on the onDrop handler and the
+// test will only pass if this functions returns true. Also, if the `dropElement` is inside an
+// iframe, use the optional `iframe` parameter to specify an iframe element that contains the
+// `dropElement` to ensure that tests with an iframe pass.
+
+function dragDropTest(dragElement, dropElement, onDropCallBack, testDescription, iframe = undefined) {
+ promise_test((t) => new Promise(async (resolve, reject) => {
+ dropElement.addEventListener('drop', t.step_func((event) => {
+ if (onDropCallBack(event) == true) {
+ resolve();
+ } else {
+ reject();
+ }
+ }));
+ try {
+ await mouseMoveToCenter(dragElement);
+ await new test_driver.Actions()
+ .pointerDown()
+ .send();
+ await mouseMoveToCenter(dropElement, iframe);
+ await new test_driver.Actions()
+ .pointerUp()
+ .send();
+ } catch (e) {
+ reject(e);
+ }
+ }, testDescription));
+}
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/001.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/001.xhtml
new file mode 100644
index 0000000000..235eca64a1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/001.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Canvas drag and drop roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/uri-list', document.querySelector('canvas').toDataURL('image/png'));}
+</script>
+</head>
+<body>
+<p>
+ <canvas width="100" height="100" draggable="true" ondragstart="start(event)" ondragenter="event.preventDefault()" ondragover="return false" ondrop="addImage(event)">Canvas</canvas>
+</p>
+<p>Drag canvas pattern outside browser window and then drag it back and drop on itself. It should be duplicated once you drop it.</p>
+<script type="application/ecmascript">
+var canvas = document.querySelector('canvas'),
+c = canvas.getContext('2d');
+for(var x = 0; x != 50; x++)
+ {c.fillStyle = (x%2 == 0)?'navy':'white';
+ c.beginPath();
+ c.moveTo(x,x);
+ c.lineTo(100-x,x);
+ c.lineTo(100-x,100-x);
+ c.lineTo(x,100-x);
+ c.closePath();
+ c.fill();}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/002.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/002.xhtml
new file mode 100644
index 0000000000..84bfda2d09
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/002.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>PNG image drag and drop roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy'" ondragenter="event.preventDefault()" ondrop="addImage(event)" ondragover="return false" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAEq0lEQVR42u1dvU4bQRAeR0RCCilSp0peIoKGB4DSXYRDlcZlFClKBI4bB/kV/AymIDwCTahiicaioEOxhCsjWVTJziyKMfYZn+9ud3bvG2mEZB13u9+nndmf2Rki7fKFXtEBbVKD9o22jB4bPaND6hm9Mjo0enevw/vfevKMfbYl/8vv4HdBUkvFALhltG2A7dN3+pur8jv53fwN/hZkjjRo3eiuAatjdJA7CcnkDOSb/G1uQ6mFzUeDagaQrvl764yEJOU22LbUymXaGrRhOt40OvJOQvLIGUkbP9GLeIn4SM9NJ+tOTVI+Jq0ubY9sVFRNxy6DIWKWmEvpQ/ByQNumI+fBEjHrZ86lT8HJV3ptGn8aDRGzxJxKHwMZFe9Mg6+jJWNCyrX0VbUc0nuj4+jJmPiWsfRZodN+ZvSoNETMjpYjwUCFfKaXpjEnpSVjQsqJYOF5ZLw1elF6MiakXAgmnpw378DegIgZUm4EGw8jA2QsIuUbvXHpM2CmljFfhfsUO5uCA0/j6AudfZV5arv6WuVHcYs+ALwqKTkvHnmLoEwr8CJW9Llts9iNwvj3plzsfeWyIRnzrq37kfIzq6naBpA5a6bzlJgOl/SMkl+rklEFgIX5k5THwTYgIdwzcP2jhM/o19KsOeoArnBS6suaqo2gQnXCJWSwXNwXB4gBMFekNBeTwSGUmiMK4yNktDhsleNaAZRrUvYWmasuQHJOSDdpdKyriEIvo9maexWC70gAIF/bKTvzzFUH4HgbJZ3HdFSw9vC6lfKHpq7X8X07AOOblK2HhLQBindC2g/9Rx+gePcj/cnqHIDoUFm1c9gjwNAy/d0kyXIAMLSYrQ8kqScAhhbH3iLJBwIwtBByTJKkBWBoIeSMJHMOwNBCyG+SdEYAQ4tTvyLJMQUwtBAyJEn8BTC0EHIHQhQSApOlzGTBqaty6jzVAhhaCOlhYahuYYitE02EHGNzURchLZItX4ChhZB9HFBpUjmgwhGuHv0feI0gBw1T3j7CgHT5jzYC5XQRsjUdSsrhjADGl7ka0EylBgRb+ySkMy9rww7A8Waududf2MH9Qh9k3CbXLsGVNh/mqrvojuEeQHI+Qmq4Fq1ndIyeruaDxAEuCWk+ncmB0z3gepubtQenMUHyGTWELJl8xk6B15CeqVAyLtPXtUICsyJnVivWs+J0dAAwbzLOkQRTk2YuKsapTQFkXqPjlDILEinnRcZ1fpXdkGo866xqnH9FNyTjz0JIQZXcuPQCAE5rqo6oMEFBl7RknBRfTg8lj5Yl48JdGT0ueIWiYIvIuHFfPg9l85LJcF42b+JTUFjysZnyVlhy2qfA0asovTo9+0JxYnWC8t0KBQXuFYrdkIy3mBj3LbeNQrejZTuqOlbcl8znGTqcfjXoM3pboqgaPhEPxda1qgcVYsRt5TanDkgISWzcV1N1hCS3jdu4dNxUDMIhlBzXysHGGspkcBtsW2pPh3fGLrZ2ya5cXHFp0qxJ6si3E68EQCpy344vQRZxO5jfye+2d/oqgHsV02Z3lvcl9QTnA+EkLZw5h9MZcY4pTvxldXj/W0+esc+25H/5HQGYon+Q5f+MwFz+8QAAAABJRU5ErkJggg==" alt="PNG circle"/></p>
+<p>Drag circle outside browser window and then drag it back and drop on itself. It should be duplicated once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/003.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/003.xhtml
new file mode 100644
index 0000000000..6cbdbf3bb8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/003.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG image drag and drop roundtrip</title>
+<style type="text/css">
+img
+ {margin:0 2px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,''));
+ document.querySelector('p').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img ondragstart="event.dataTransfer.effectAllowed = 'copy'" ondragenter="event.preventDefault()" ondrop="addImage(event)" ondragover="return false" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle"/></p>
+<p>Drag circle outside browser window and then drag it back and drop on itself. It should be duplicated once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/004.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/004.xhtml
new file mode 100644
index 0000000000..0b31a5989b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/004.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Text input selection drag and drop roundtrip</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragenter="event.preventDefault()" ondragover="return false" ondrop="event.preventDefault();this.value = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'"/></p>
+<p>Drag selected text outside browser window and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/005.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/005.xhtml
new file mode 100644
index 0000000000..e482fb2a0f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/005.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop roundtrip</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragenter="event.preventDefault()" ondragover="return false" ondrop="document.querySelector('span').firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL'">Drag me</span></p>
+<p>Drag selected text outside browser window and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/006.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/006.xhtml
new file mode 100644
index 0000000000..45e477aa29
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/006.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Link drag and drop roundtrip</title>
+<script type="application/ecmascript">
+function checkLink(event)
+ {document.querySelector('a').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL'}
+</script>
+</head>
+<body>
+<p><a href="data:text/plain,1" ondragstart="event.dataTransfer.effectAllowed = 'copy'" ondragenter="event.preventDefault()" ondrop="checkLink(event)" ondragover="return false">Drag me</a></p>
+<p>Drag link outside browser window and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/007.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/007.xhtml
new file mode 100644
index 0000000000..a46cb33ee6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/007.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop roundtrip with text/plain data</title>
+<style type="text/css">
+div[ondragenter]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ draggable="true"
+ ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/plain',' PASS ')"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+<p>Drag blue box outside browser window and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/roundtrip/008.xhtml b/testing/web-platform/tests/html/editing/dnd/roundtrip/008.xhtml
new file mode 100644
index 0000000000..33caeebefe
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/roundtrip/008.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop roundtrip with text/uri-list data</title>
+<style type="text/css">
+div[ondragenter]
+ {width:40px;
+ min-height:40px;
+ margin-top:20px;
+ padding:40px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ draggable="true"
+ ondragstart="event.dataTransfer.effectAllowed = 'copy';event.dataTransfer.setData('text/uri-list','data:text/plain,PASS')"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/uri-list').substr(16,4) + ' '))"
+/>
+<p>Drag blue box outside browser window and then drag it back and drop on itself. You should see word PASS once you drop it.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/001.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/001.xhtml
new file mode 100644
index 0000000000..37122c8251
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/001.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/002.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/002.xhtml
new file mode 100644
index 0000000000..c96a5e62a6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/002.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from search input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="search" value="Selection"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/003.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/003.xhtml
new file mode 100644
index 0000000000..eed4491a1c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/003.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from tel input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="tel" value="123456789"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/004.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/004.xhtml
new file mode 100644
index 0000000000..a27dad9f42
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/004.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from url input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected url to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="url" value="http://example.org"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/005.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/005.xhtml
new file mode 100644
index 0000000000..3ef9f805f2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/005.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from email input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected email to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="email" value="mail@example.org"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/006.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/006.xhtml
new file mode 100644
index 0000000000..fe5cdf40e3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/006.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from number input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="number" value="123456789"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/007.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/007.xhtml
new file mode 100644
index 0000000000..ad7379b72e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/007.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from short text input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected date to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input size="5" value="Drag me"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/008.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/008.xhtml
new file mode 100644
index 0000000000..4d0437213e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/008.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from password input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>You should not be able to drag and drop selection from password field to the blue box.</p>
+<p><input type="password" value="FAIL"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/009.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/009.xhtml
new file mode 100644
index 0000000000..3bc8b7b654
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/009.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from readonly text input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input readonly="readonly" value="Drag me"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/010.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/010.xhtml
new file mode 100644
index 0000000000..220edea2eb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/010.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from texarea to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/011.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/011.xhtml
new file mode 100644
index 0000000000..df75fc964b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/011.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/012.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/012.xhtml
new file mode 100644
index 0000000000..c6cf54ea98
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/012.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from search input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><input type="search" value="Selection"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/013.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/013.xhtml
new file mode 100644
index 0000000000..9799480ee9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/013.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from tel input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><input type="tel" value="123456789"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/014.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/014.xhtml
new file mode 100644
index 0000000000..75bfed79c0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/014.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from url input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected url to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><input type="url" value="http://opera.com"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/015.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/015.xhtml
new file mode 100644
index 0000000000..546502a5e4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/015.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from email input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected email to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><input type="email" value="mail@example.org"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/016.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/016.xhtml
new file mode 100644
index 0000000000..d2767c4f7a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/016.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from number input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><input type="number" value="123456789"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/017.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/017.xhtml
new file mode 100644
index 0000000000..58e7de831d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/017.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from short text input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected date to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><input size="5" value="Drag me"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/018.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/018.xhtml
new file mode 100644
index 0000000000..76483483c3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/018.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from password input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>You should not be able to drag and drop selection from password field to the textarea.</p>
+<p><input type="password" value="FAIL"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/019.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/019.xhtml
new file mode 100644
index 0000000000..63de9a58df
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/019.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from readonly input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><input readonly="readonly" value="Drag me"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/020.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/020.xhtml
new file mode 100644
index 0000000000..98c263abb9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/020.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between textareas</title>
+<style type="text/css">
+p:last-child > textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+p:first-child + p > textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/021.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/021.xhtml
new file mode 100644
index 0000000000..9f44f264b0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/021.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/022.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/022.xhtml
new file mode 100644
index 0000000000..434e989a85
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/022.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from search input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input type="search" value="Selection"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/023.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/023.xhtml
new file mode 100644
index 0000000000..d1e3f5dff5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/023.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from tel input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input type="tel" value="123456789"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/024.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/024.xhtml
new file mode 100644
index 0000000000..4b3b024370
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/024.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from url input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected url to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input type="url" value="http://example.org"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/025.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/025.xhtml
new file mode 100644
index 0000000000..2f5391eb45
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/025.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from email input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected email to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input type="email" value="mail@example.org"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/026.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/026.xhtml
new file mode 100644
index 0000000000..6c376c38e2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/026.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from number input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="number" value="123456789"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/027.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/027.xhtml
new file mode 100644
index 0000000000..2a7a067901
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/027.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from short input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected date to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input size="5" value="Drag me"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/028.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/028.xhtml
new file mode 100644
index 0000000000..e422a18497
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/028.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from password input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>You should not be able to drag and drop selection from password field to the blue box.</p>
+<p><input type="password" value="FAIL"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/029.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/029.xhtml
new file mode 100644
index 0000000000..e101dda24d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/029.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from readonly input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input readonly="readonly" value="Drag me"/></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/030.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/030.xhtml
new file mode 100644
index 0000000000..deec97e5e8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/030.xhtml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/031.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/031.xhtml
new file mode 100644
index 0000000000..b3b3be923c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/031.xhtml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging two lines of text selection from textarea to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(25,56)">
+<p>
+<textarea>
+Here is textarea
+with selection that
+spans two lines.
+Drag selected text to the blue box.
+Selected text should be moved to the blue box once you drop it there.
+</textarea>
+</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/032.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/032.xhtml
new file mode 100644
index 0000000000..55358f98cf
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/032.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging multiline text selection from textarea to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(24,58)">
+<p>
+<textarea>
+Here is textarea with selection
+that spans
+three lines. Drag selected text to the blue box.
+Copy of selection should end up in the blue box once you drop it there.
+</textarea>
+</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/033.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/033.xhtml
new file mode 100644
index 0000000000..a8011e54b8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/033.xhtml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging two lines of text selection between textareas</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(25,56)">
+<p>
+<textarea>
+Here is textarea
+with selection that
+spans two lines.
+Drag selected text to the textarea below.
+Selected text should be moved to second textarea once you drop it there.
+</textarea>
+</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/034.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/034.xhtml
new file mode 100644
index 0000000000..34ceec09a0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/034.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging multiline text selection between textareas</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(24,58)">
+<p>
+<textarea>
+Here is textarea with selection
+that spans
+three lines. Drag selected text to the textarea below.
+Selected text should be moved to second textarea once you drop it there.
+</textarea>
+</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/035.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/035.xhtml
new file mode 100644
index 0000000000..54259f8a10
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/035.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging two lines of text selection from textarea to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(25,56)">
+<p>
+<textarea>
+Here is textarea
+with selection that
+spans two lines.
+Drag selected text to the blue box.
+Selected text should be moved to the blue box once you drop it there.
+</textarea>
+</p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/036.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/036.xhtml
new file mode 100644
index 0000000000..7c152a21e3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/036.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging multiline text selection from textarea to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(24,58)">
+<p>
+<textarea>
+Here is textarea with selection
+that spans
+three lines. Drag selected text to the blue box.
+Selected text should be moved to the blue box once you drop it there.
+</textarea>
+</p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/037.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/037.xhtml
new file mode 100644
index 0000000000..fc06f3a069
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/037.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop from text input to block element</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-input.xhtml"/>
+<frame src="helper-drop-here-blue-box.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/038.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/038.xhtml
new file mode 100644
index 0000000000..5843f5830c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/038.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop from textarea input to block element</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-textarea-to-other-blue-box.xhtml"/>
+<frame src="helper-drop-here-blue-box.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/039.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/039.xhtml
new file mode 100644
index 0000000000..f5f823b956
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/039.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop from text input to textarea</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-input-to-other-textarea.xhtml"/>
+<frame src="helper-drop-here-textarea.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/040.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/040.xhtml
new file mode 100644
index 0000000000..91605aff79
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/040.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop between textareas</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-textarea-to-other-textarea.xhtml"/>
+<frame src="helper-drop-here-textarea.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/041.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/041.xhtml
new file mode 100644
index 0000000000..73a8834722
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/041.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop from text input to contenteditable element</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-input.xhtml"/>
+<frame src="helper-drop-here-blue-box-contenteditable.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/042.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/042.xhtml
new file mode 100644
index 0000000000..686accb246
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/042.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop from textarea to contenteditable element</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-textarea-to-other-blue-box.xhtml"/>
+<frame src="helper-drop-here-blue-box-contenteditable.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/043.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/043.xhtml
new file mode 100644
index 0000000000..bc296d5bc5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/043.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop between text inputs</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-input-to-other-input.xhtml"/>
+<frame src="helper-drop-here-input.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/044.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/044.xhtml
new file mode 100644
index 0000000000..0dd958edc8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/044.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross frame selection drag and drop from textarea to text input</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="helper-drag-me-textarea-to-other-input.xhtml"/>
+<frame src="helper-drop-here-input.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/045.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/045.xhtml
new file mode 100644
index 0000000000..fe48994f40
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/045.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to iframe</title>
+<style type="text/css">
+iframe
+ {width:350px;
+ height:150px;
+ border-style:none;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><iframe src="helper-drop-here-blue-box.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/046.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/046.xhtml
new file mode 100644
index 0000000000..1743473882
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/046.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to iframe</title>
+<style type="text/css">
+iframe
+ {width:350px;
+ height:150px;
+ border-style:none;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><iframe src="helper-drop-here-blue-box.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/047.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/047.xhtml
new file mode 100644
index 0000000000..420269282e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/047.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to textarea in iframe</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:400px;
+ border-style:none;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><iframe src="helper-drop-here-textarea.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/048.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/048.xhtml
new file mode 100644
index 0000000000..7b2610cfc7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/048.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to another textarea in iframe</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:400px;
+ border-style:none;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><iframe src="helper-drop-here-textarea.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/049.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/049.xhtml
new file mode 100644
index 0000000000..791c0180eb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/049.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to contenteditable element in iframe</title>
+<style type="text/css">
+iframe
+ {width:350px;
+ height:150px;
+ border-style:none;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><iframe src="helper-drop-here-blue-box-contenteditable.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/050.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/050.xhtml
new file mode 100644
index 0000000000..53b3997400
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/050.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to contenteditable element in iframe</title>
+<style type="text/css">
+iframe
+ {width:350px;
+ height:150px;
+ border-style:none;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><iframe src="helper-drop-here-blue-box-contenteditable.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/051.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/051.xhtml
new file mode 100644
index 0000000000..3cd1145f1a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/051.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in iframe to block element</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input.xhtml">XHTML document</iframe></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/052.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/052.xhtml
new file mode 100644
index 0000000000..843115017a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/052.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in iframe to block element</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-textarea-to-other-blue-box.xhtml">XHTML document</iframe></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/053.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/053.xhtml
new file mode 100644
index 0000000000..f5f9c44a11
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/053.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in iframe to textarea</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input-to-other-textarea.xhtml">XHTML document</iframe></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/054.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/054.xhtml
new file mode 100644
index 0000000000..ebc9fcf0da
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/054.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in iframe to another textarea</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-textarea-to-other-textarea.xhtml">XHTML document</iframe></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/055.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/055.xhtml
new file mode 100644
index 0000000000..29488c3a60
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/055.xhtml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in iframe to contenteditable element</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input.xhtml">XHTML document</iframe></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/056.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/056.xhtml
new file mode 100644
index 0000000000..72a51c64b8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/056.xhtml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in iframe to contenteditable element</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-textarea-to-other-blue-box.xhtml">XHTML document</iframe></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/057.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/057.xhtml
new file mode 100644
index 0000000000..950bccbdd0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/057.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in iframe to another input element</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input-to-other-input.xhtml">XHTML document</iframe></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/058.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/058.xhtml
new file mode 100644
index 0000000000..d8a6380c38
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/058.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in iframe to input element</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:200px;
+ border-style:none;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-textarea-to-other-input.xhtml">XHTML document</iframe></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/059.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/059.xhtml
new file mode 100644
index 0000000000..b67547d482
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/059.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop from text input to block element</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-blue-box.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/060.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/060.xhtml
new file mode 100644
index 0000000000..7ad6952222
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/060.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop from textarea to block element</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-textarea-to-other-blue-box.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-blue-box.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/061.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/061.xhtml
new file mode 100644
index 0000000000..371673ffa1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/061.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop from text input to textarea</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input-to-other-textarea.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-textarea.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/062.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/062.xhtml
new file mode 100644
index 0000000000..5f38444f90
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/062.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop between textareas</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-textarea-to-other-textarea.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-textarea.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/063.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/063.xhtml
new file mode 100644
index 0000000000..68d4f33509
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/063.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop from text input to contenteditable element</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-blue-box-contenteditable.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/064-1.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/064-1.xhtml
new file mode 100644
index 0000000000..bf4df42290
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/064-1.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Selection should be moved to the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/064.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/064.xhtml
new file mode 100644
index 0000000000..9e6be0d034
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/064.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop from textarea to contenteditable element</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="064-1.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-blue-box-contenteditable.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/065.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/065.xhtml
new file mode 100644
index 0000000000..0d8d21f04c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/065.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop between text inputs</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input-to-other-input.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-input.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/066.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/066.xhtml
new file mode 100644
index 0000000000..36907bc492
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/066.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross iframe selection drag and drop from textarea to text input</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-textarea-to-other-input.xhtml">XHTML document</iframe></p>
+<p><iframe src="helper-drop-here-input.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/067-1.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/067-1.xhtml
new file mode 100644
index 0000000000..a908282bb7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/067-1.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drag-me-input.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/067-2.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/067-2.xhtml
new file mode 100644
index 0000000000..22f8b55020
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/067-2.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+iframe
+ {width:100%;
+ height:200px;
+ border-style:none;}
+</style>
+</head>
+<body>
+<p><iframe src="helper-drop-here-blue-box.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/067.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/067.xhtml
new file mode 100644
index 0000000000..2d33465fd4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/067.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Nested cross frame selection drag and drop from text input to block element</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="067-1.xhtml"/>
+<frame src="067-2.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/068-1.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/068-1.xhtml
new file mode 100644
index 0000000000..c9fc41b478
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/068-1.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<frameset rows="100%, 0%">
+<frame src="helper-drag-me-textarea-to-other-textarea.xhtml"/>
+<frame src="data:text/plain,1"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/068-2.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/068-2.xhtml
new file mode 100644
index 0000000000..b639b16fc1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/068-2.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<frameset rows="100%, 0%">
+<frame src="helper-drop-here-textarea.xhtml"/>
+<frame src="data:text/plain,2"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/068.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/068.xhtml
new file mode 100644
index 0000000000..5f051697cd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/068.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Nested cross frame selection drag and drop between textareas</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="068-1.xhtml"/>
+<frame src="068-2.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/069.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/069.xhtml
new file mode 100644
index 0000000000..149a41df08
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/069.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop to textarea in iframe</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:400px;
+ border-style:none;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><iframe src="helper-drop-here-textarea.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/070.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/070.xhtml
new file mode 100644
index 0000000000..7255b2b21e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/070.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop to contenteditable element in iframe</title>
+<style type="text/css">
+iframe
+ {width:500px;
+ height:400px;
+ border-style:none;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><iframe src="helper-drop-here-blue-box-contenteditable.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/071.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/071.xhtml
new file mode 100644
index 0000000000..73e6722c00
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/071.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between text inputs of different size</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/072.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/072.xhtml
new file mode 100644
index 0000000000..343cc3fc0c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/072.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from search to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><input type="search" value="Selection"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/073.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/073.xhtml
new file mode 100644
index 0000000000..2a082b8a11
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/073.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from tel to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><input type="tel" value="123456789"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/074.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/074.xhtml
new file mode 100644
index 0000000000..b8966e71ba
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/074.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from url to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected url to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><input type="url" value="http://example.org"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/075.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/075.xhtml
new file mode 100644
index 0000000000..75af080efc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/075.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from email to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected email to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><input type="email" value="mail@example.org"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/076.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/076.xhtml
new file mode 100644
index 0000000000..c9c732e7e1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/076.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from number to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the input below. Copy of selection should end up in the input once you drop it there.</p>
+<p><input type="number" value="123456789"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/077.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/077.xhtml
new file mode 100644
index 0000000000..ecd4ab3801
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/077.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from short text input to another text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected date to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><input size="5" value="Drag me"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/078.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/078.xhtml
new file mode 100644
index 0000000000..853cf63b60
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/078.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from password to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>You should not be able to drag and drop selection from password field to the input below.</p>
+<p><input type="password" value="FAIL"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/079.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/079.xhtml
new file mode 100644
index 0000000000..1ad458df4b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/079.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from readonly text input to another text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><input readonly="readonly" value="Drag me"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/080.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/080.xhtml
new file mode 100644
index 0000000000..3ebc0aafd2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/080.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to text input</title>
+<style type="text/css">
+input
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/081.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/081.xhtml
new file mode 100644
index 0000000000..c65dd26a3f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/081.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Events during selection drag and drop to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;
+ margin-top:20px;}
+.hidden
+ {visibility:hidden;}
+</style>
+<script type="application/ecmascript">
+function evtChange()
+ {document.querySelectorAll('tt')[0].firstChild.nodeValue = 'PASS'}
+function evtInput()
+ {document.querySelectorAll('tt')[1].firstChild.nodeValue = 'PASS'}
+function evtDrop()
+ {document.querySelectorAll('tt')[2].firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL (selection is not properly added to data store)'}
+function evtKeypress()
+ {document.querySelectorAll('tt')[3].firstChild.nodeValue = 'FAIL (even is not supposed to fire)'}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Drag selected text to the input below. Follow check results link once you drop selection in the input.</p>
+<p><input placeholder="Drop selection here" onchange="evtChange()" oninput="evtInput()" onkeypress="evtKeypress()" ondrop="evtDrop()"/></p>
+<p><a href="javascript:document.querySelector('ul').removeAttribute('class')">Check results</a></p>
+<ul class="hidden">
+<li>Change event: <tt>FAIL (even did not fire)</tt></li>
+<li>Input event: <tt>FAIL (even did not fire)</tt></li>
+<li>Drop event: <tt>FAIL (even did not fire)</tt></li>
+<li>Keypress event: <tt>PASS</tt></li>
+</ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/082.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/082.xhtml
new file mode 100644
index 0000000000..57c2a53f4d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/082.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Events during selection drag and drop to search input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;
+ margin-top:20px;}
+.hidden
+ {visibility:hidden;}
+</style>
+<script type="application/ecmascript">
+function evtChange()
+ {document.querySelectorAll('tt')[0].firstChild.nodeValue = 'PASS'}
+function evtInput()
+ {document.querySelectorAll('tt')[1].firstChild.nodeValue = 'PASS'}
+function evtDrop()
+ {document.querySelectorAll('tt')[2].firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'Drag me')?'PASS':'FAIL (selection is not properly added to data store)'}
+function evtKeypress()
+ {document.querySelectorAll('tt')[3].firstChild.nodeValue = 'FAIL (even is not supposed to fire)'}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Drag selected text to the input below. Follow check results link once you drop selection in the input.</p>
+<p><input type="search" placeholder="Drop selection here" onchange="evtChange()" oninput="evtInput()" onkeypress="evtKeypress()" ondrop="evtDrop()"/></p>
+<p><a href="javascript:document.querySelector('ul').removeAttribute('class')">Check results</a></p>
+<ul class="hidden">
+<li>Change event: <tt>FAIL (even did not fire)</tt></li>
+<li>Input event: <tt>FAIL (even did not fire)</tt></li>
+<li>Drop event: <tt>FAIL (even did not fire)</tt></li>
+<li>Keypress event: <tt>PASS</tt></li>
+</ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/083.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/083.xhtml
new file mode 100644
index 0000000000..ba527ae4e2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/083.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Events during selection drag and drop to tel input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;
+ margin-top:20px;}
+.hidden
+ {visibility:hidden;}
+</style>
+<script type="application/ecmascript">
+function evtChange()
+ {document.querySelectorAll('tt')[0].firstChild.nodeValue = 'PASS'}
+function evtInput()
+ {document.querySelectorAll('tt')[1].firstChild.nodeValue = 'PASS'}
+function evtDrop()
+ {document.querySelectorAll('tt')[2].firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == '123456789')?'PASS':'FAIL (selection is not properly added to data store)'}
+function evtKeypress()
+ {document.querySelectorAll('tt')[3].firstChild.nodeValue = 'FAIL (even is not supposed to fire)'}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>123456789</p>
+<p>Drag selected number to the input below. Follow check results link once you drop selection in the input.</p>
+<p><input type="tel" placeholder="Drop selection here" onchange="evtChange()" oninput="evtInput()" onkeypress="evtKeypress()" ondrop="evtDrop()"/></p>
+<p><a href="javascript:document.querySelector('ul').removeAttribute('class')">Check results</a></p>
+<ul class="hidden">
+<li>Change event: <tt>FAIL (even did not fire)</tt></li>
+<li>Input event: <tt>FAIL (even did not fire)</tt></li>
+<li>Drop event: <tt>FAIL (even did not fire)</tt></li>
+<li>Keypress event: <tt>PASS</tt></li>
+</ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/084.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/084.xhtml
new file mode 100644
index 0000000000..f602d712bd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/084.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Events during selection drag and drop to url input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;
+ margin-top:20px;}
+.hidden
+ {visibility:hidden;}
+</style>
+<script type="application/ecmascript">
+function evtChange()
+ {document.querySelectorAll('tt')[0].firstChild.nodeValue = 'PASS'}
+function evtInput()
+ {document.querySelectorAll('tt')[1].firstChild.nodeValue = 'PASS'}
+function evtDrop()
+ {document.querySelectorAll('tt')[2].firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'http://example.org')?'PASS':'FAIL (selection is not properly added to data store)'}
+function evtKeypress()
+ {document.querySelectorAll('tt')[3].firstChild.nodeValue = 'FAIL (even is not supposed to fire)'}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>http://example.org</p>
+<p>Drag selected url to the input below. Follow check results link once you drop selection in the input.</p>
+<p><input type="url" placeholder="Drop selection here" onchange="evtChange()" oninput="evtInput()" onkeypress="evtKeypress()" ondrop="evtDrop()"/></p>
+<p><a href="javascript:document.querySelector('ul').removeAttribute('class')">Check results</a></p>
+<ul class="hidden">
+<li>Change event: <tt>FAIL (even did not fire)</tt></li>
+<li>Input event: <tt>FAIL (even did not fire)</tt></li>
+<li>Drop event: <tt>FAIL (even did not fire)</tt></li>
+<li>Keypress event: <tt>PASS</tt></li>
+</ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/085.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/085.xhtml
new file mode 100644
index 0000000000..842288f558
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/085.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Events during selection drag and drop to email input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;
+ margin-top:20px;}
+.hidden
+ {visibility:hidden;}
+</style>
+<script type="application/ecmascript">
+function evtChange()
+ {document.querySelectorAll('tt')[0].firstChild.nodeValue = 'PASS'}
+function evtInput()
+ {document.querySelectorAll('tt')[1].firstChild.nodeValue = 'PASS'}
+function evtDrop()
+ {document.querySelectorAll('tt')[2].firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == 'mail@example.org')?'PASS':'FAIL (selection is not properly added to data store)'}
+function evtKeypress()
+ {document.querySelectorAll('tt')[3].firstChild.nodeValue = 'FAIL (even is not supposed to fire)'}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>mail@example.org</p>
+<p>Drag selected address to the input below. Follow check results link once you drop selection in the input.</p>
+<p><input type="email" placeholder="Drop selection here" onchange="evtChange()" oninput="evtInput()" onkeypress="evtKeypress()" ondrop="evtDrop()"/></p>
+<p><a href="javascript:document.querySelector('ul').removeAttribute('class')">Check results</a></p>
+<ul class="hidden">
+<li>Change event: <tt>FAIL (even did not fire)</tt></li>
+<li>Input event: <tt>FAIL (even did not fire)</tt></li>
+<li>Drop event: <tt>FAIL (even did not fire)</tt></li>
+<li>Keypress event: <tt>PASS</tt></li>
+</ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/086.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/086.xhtml
new file mode 100644
index 0000000000..349d7795e7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/086.xhtml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Events during selection drag and drop to number input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;
+ margin-top:20px;}
+.hidden
+ {visibility:hidden;}
+</style>
+<script type="application/ecmascript">
+function evtChange()
+ {document.querySelectorAll('tt')[0].firstChild.nodeValue = 'PASS'}
+function evtInput()
+ {document.querySelectorAll('tt')[1].firstChild.nodeValue = 'PASS'}
+function evtDrop()
+ {document.querySelectorAll('tt')[2].firstChild.nodeValue = (event.dataTransfer.getData('text/plain') == '123456789')?'PASS':'FAIL (selection is not properly added to data store)'}
+function evtKeypress()
+ {document.querySelectorAll('tt')[3].firstChild.nodeValue = 'FAIL (even is not supposed to fire)'}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>123456789</p>
+<p>Drag selected address to the input below. Follow check results link once you drop selection in the input.</p>
+<p><input type="number" placeholder="Drop selection here" onchange="evtChange()" oninput="evtInput()" onkeypress="evtKeypress()" ondrop="evtDrop()"/></p>
+<p><a href="javascript:document.querySelector('ul').removeAttribute('class')">Check results</a></p>
+<ul class="hidden">
+<li>Change event: <tt>FAIL (even did not fire)</tt></li>
+<li>Input event: <tt>FAIL (even did not fire)</tt></li>
+<li>Drop event: <tt>FAIL (even did not fire)</tt></li>
+<li>Keypress event: <tt>PASS</tt></li>
+</ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/087.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/087.xhtml
new file mode 100644
index 0000000000..64e02c1ef7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/087.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Scrolling during selection drag and drop to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;
+ margin-top:200px;}
+div
+ {height:150px;
+ width:500px;
+ overflow-y:scroll;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<div>
+<p>You should be able to drag selected text to the input at the bottom of scrollable container (dragging towards the bottom edge triggers scrolling).</p>
+<input placeholder="Drop selection here"/>
+<p>Copy of selection should end up in the input once you drop it there.</p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/088.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/088.xhtml
new file mode 100644
index 0000000000..4900b13c24
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/088.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Vertical scrolling during selection drag and drop to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;}
+div > p:first-child
+ {margin-bottom:200px;}
+body > div
+ {height:150px;
+ width:500px;
+ overflow-y:scroll;}
+div > div
+ {height:400px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<div>
+ <div>
+ <p>You should be able to drag selected text to the input at the bottom of scrollable container (dragging towards the bottom edge triggers scrolling).</p>
+ <input placeholder="Drop selection here"/>
+ <p>Copy of selection should end up in the input once you drop it there.</p>
+ </div>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/089.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/089.xhtml
new file mode 100644
index 0000000000..26185e3389
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/089.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Scrolling during selection drag and drop to iframe</title>
+<style type="text/css">
+iframe
+ {width:350px;
+ height:150px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p><iframe src="helper-scroll-then-drop-input.xhtml">XHTML document</iframe></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/090.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/090.xhtml
new file mode 100644
index 0000000000..62dfa0c6ef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/090.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Scrolling during selection drag and drop to object</title>
+<style type="text/css">
+object
+ {width:350px;
+ height:150px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p><object type="application/xhtml+xml" data="helper-scroll-then-drop-input.xhtml">XHTML document</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/091-1.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/091-1.xhtml
new file mode 100644
index 0000000000..5e11544c4e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/091-1.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/091-2.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/091-2.xhtml
new file mode 100644
index 0000000000..12d5cef8a7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/091-2.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;}
+p:first-child
+ {margin-bottom:1000px;}
+</style>
+</head>
+<body>
+<p>You should be able to drag selected text to the input at the bottom of scrollable container (dragging towards the bottom edge triggers scrolling).</p>
+<input placeholder="Drop selection here"/>
+<p>Copy of selection should end up in the input once you drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/091.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/091.xhtml
new file mode 100644
index 0000000000..bc85f2231b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/091.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Scrolling during selection drag and drop to frame</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="091-1.xhtml"/>
+<frame src="091-2.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/092.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/092.xhtml
new file mode 100644
index 0000000000..30d53f0f7d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/092.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to element in object</title>
+<style type="text/css">
+object
+ {width:350px;
+ height:150px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><object type="application/xhtml+xml" data="helper-drop-here-blue-box.xhtml">XHTML document</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/093.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/093.xhtml
new file mode 100644
index 0000000000..ffc32ce77e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/093.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to element in object</title>
+<style type="text/css">
+object
+ {width:350px;
+ height:150px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><object type="application/xhtml+xml" data="helper-drop-here-blue-box.xhtml">XHTML document</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/094.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/094.xhtml
new file mode 100644
index 0000000000..bd4d63ecf5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/094.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to textarea in object</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:400px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><object type="application/xhtml+xml" data="helper-drop-here-textarea.xhtml">XHTML document</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/095.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/095.xhtml
new file mode 100644
index 0000000000..8035f79900
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/095.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to another textarea in object</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:400px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><object type="application/xhtml+xml" data="helper-drop-here-textarea.xhtml">XHTML document</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/096.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/096.xhtml
new file mode 100644
index 0000000000..508d085824
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/096.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input to contenteditable element in object</title>
+<style type="text/css">
+object
+ {width:350px;
+ height:150px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><object type="application/xhtml+xml" data="helper-drop-here-blue-box-contenteditable.xhtml">XHTML document</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/097.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/097.xhtml
new file mode 100644
index 0000000000..915fa31356
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/097.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea to contenteditable element in object</title>
+<style type="text/css">
+object
+ {width:350px;
+ height:150px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+<p><object type="application/xhtml+xml" data="helper-drop-here-blue-box-contenteditable.xhtml">XHTML document</object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/098.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/098.xhtml
new file mode 100644
index 0000000000..10dc9b085a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/098.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in object to block element</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-input.xhtml">XHTML document</object></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/099.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/099.xhtml
new file mode 100644
index 0000000000..79fbd954d8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/099.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in object to block element</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-textarea-to-other-blue-box.xhtml">XHTML document</object></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/100.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/100.xhtml
new file mode 100644
index 0000000000..8e0ab2567d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/100.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in object to textarea</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-input-to-other-textarea.xhtml">XHTML document</object></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/101.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/101.xhtml
new file mode 100644
index 0000000000..4c712c089f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/101.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in object to another textarea</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-textarea-to-other-textarea.xhtml">XHTML document</object></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/102.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/102.xhtml
new file mode 100644
index 0000000000..1420b35ab6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/102.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in object to contenteditable element</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-input.xhtml">XHTML document</object></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/103.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/103.xhtml
new file mode 100644
index 0000000000..fef3439911
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/103.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in object to contenteditable element</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-textarea-to-other-blue-box.xhtml">XHTML document</object></p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/104.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/104.xhtml
new file mode 100644
index 0000000000..62f174685f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/104.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input in object to another text input</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-input-to-other-input.xhtml">XHTML document</object></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/105.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/105.xhtml
new file mode 100644
index 0000000000..d4b0aa5d6d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/105.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea in object to text input</title>
+<style type="text/css">
+object
+ {width:500px;
+ height:200px;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body>
+<p><object type="application/xhtml+xml" data="helper-drag-me-textarea-to-other-input.xhtml">XHTML document</object></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/106.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/106.xhtml
new file mode 100644
index 0000000000..9accdc0758
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/106.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from textarea input in dataURL frame to block element</title>
+</head>
+<frameset rows="50%, 50%">
+<frame src="data:application/xhtml+xml,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Ctitle%3EDragging%20selection%3C/title%3E%3C/head%3E%3Cbody%20onload%3D%22document.querySelector%28%27input%27%29.select%28%29%22%3E%3Cp%3EDrag%20selected%20text%20to%20the%20blue%20box.%20Copy%20of%20selection%20should%20end%20up%20in%20the%20blue%20box%20once%20you%20drop%20it%20there.%3C/p%3E%3Cp%3E%3Cinput%20value%3D%22Drag%20me%22/%3E%3C/p%3E%3C/body%3E%3C/html%3E"/>
+<frame src="helper-drop-here-blue-box.xhtml"/>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/107-1.html b/testing/web-platform/tests/html/editing/dnd/selection/107-1.html
new file mode 100644
index 0000000000..5dc99143f9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/107-1.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>Dragging selection breaks layout</title>
+<p>Select all text in the input below, drag it downwards a little, and release it. Pass if you can still edit the contents of the input.</p>
+<p><input value="SelectAndDrag"/></p>
+<script type="text/javascript">
+document.getElementsByTagName('input')[0].select();
+</script>
+<noscript><p>Enable JavaScript and reload</p></noscript> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/107.html b/testing/web-platform/tests/html/editing/dnd/selection/107.html
new file mode 100644
index 0000000000..8a930e665b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/107.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>Dragging selection breaks layout</title>
+<frameset rows="100%">
+ <frame src="107-1.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/108-1.html b/testing/web-platform/tests/html/editing/dnd/selection/108-1.html
new file mode 100644
index 0000000000..9359fed430
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/108-1.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Dragging custom selection breaks layout</title>
+<p>Select this sentence, drag it downwards a little, and release it. Pass if you can now select this sentence instead.</p> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/108.html b/testing/web-platform/tests/html/editing/dnd/selection/108.html
new file mode 100644
index 0000000000..8e9ce72301
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/108.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>Dragging custom selection breaks layout</title>
+<frameset rows="100%">
+ <frame src="108-1.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/109.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/109.xhtml
new file mode 100644
index 0000000000..fcf78f0878
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/109.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from text input to RTL textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><input dir="rtl" value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<p><textarea dir="rtl" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/110.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/110.xhtml
new file mode 100644
index 0000000000..16d7aebb15
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/110.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from text input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div dir="rtl">
+<p><input value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/111.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/111.xhtml
new file mode 100644
index 0000000000..95117474d6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/111.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from RTL element to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p dir="rtl">&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p dir="rtl"><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/112.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/112.xhtml
new file mode 100644
index 0000000000..f335fc065f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/112.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p dir="rtl"><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/113.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/113.xhtml
new file mode 100644
index 0000000000..1d66332b6b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/113.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop between textareas</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><textarea>&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<p><textarea dir="rtl" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/114.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/114.xhtml
new file mode 100644
index 0000000000..65a1de5ff9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/114.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop between RTL textareas</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div dir="rtl">
+<p><textarea>&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/115.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/115.xhtml
new file mode 100644
index 0000000000..5eb4ec5b3b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/115.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop between RTL inputs</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><input dir="rtl" value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<p><input dir="rtl" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/116.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/116.xhtml
new file mode 100644
index 0000000000..1e57aaf137
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/116.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop between text inputs</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div dir="rtl">
+<p><input value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/117.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/117.xhtml
new file mode 100644
index 0000000000..8be3d0e697
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/117.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from RTL element to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p dir="rtl">&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the input below. Copy of selection should end up in the input once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p dir="rtl"><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/118.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/118.xhtml
new file mode 100644
index 0000000000..e5ead99fd0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/118.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the input below. Copy of selection should end up in the input once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p dir="rtl"><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/119.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/119.xhtml
new file mode 100644
index 0000000000..71507c1ab6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/119.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from textarea to RTL text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><textarea>&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<p><input dir="rtl" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/120.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/120.xhtml
new file mode 100644
index 0000000000..57be0f7ce7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/120.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from textarea to text input</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div dir="rtl">
+<p><textarea>&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<p><input placeholder="Drop selection here"/></p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/121.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/121.xhtml
new file mode 100644
index 0000000000..a5a709c3b9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/121.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from RTL text input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Selected text should be moved to the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><input dir="rtl" value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<p><div dir="rtl" contenteditable="true"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/122.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/122.xhtml
new file mode 100644
index 0000000000..84ec8c61ab
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/122.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from text input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Selected text should be moved to the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div dir="rtl">
+<p><input value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<p><div contenteditable="true"/></p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/123.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/123.xhtml
new file mode 100644
index 0000000000..68ff2e8218
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/123.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from RTL element to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p dir="rtl">&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p dir="rtl"><div contenteditable="true"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/124.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/124.xhtml
new file mode 100644
index 0000000000..5c5c1b822f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/124.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p dir="rtl"><div contenteditable="true"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/125.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/125.xhtml
new file mode 100644
index 0000000000..fc4b686dcc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/125.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from textarea to RTL contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><textarea>&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<p><div dir="rtl" contenteditable="true"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/126.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/126.xhtml
new file mode 100644
index 0000000000..80b2e415ef
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/126.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from textarea to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<div dir="rtl">
+<p><textarea>&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<p><div contenteditable="true"/></p>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/127.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/127.xhtml
new file mode 100644
index 0000000000..20d73973e4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/127.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from text input to RTL element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><input dir="rtl" value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<div
+ dir="rtl"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/128.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/128.xhtml
new file mode 100644
index 0000000000..4814fa29c3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/128.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from text input to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div dir="rtl">
+<p><input value="&#x202E;FAIL|SSAP&#x202C;"/></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div > div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/129.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/129.xhtml
new file mode 100644
index 0000000000..8bbc4d81e8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/129.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from RTL element to another RTL element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p dir="rtl">&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div
+ dir="rtl"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/130.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/130.xhtml
new file mode 100644
index 0000000000..f567e09f99
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/130.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>&#x202E;FAIL|SSAP&#x202C;</p>
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div
+ dir="rtl"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/131.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/131.xhtml
new file mode 100644
index 0000000000..9e0539615b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/131.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from textarea to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea>&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<div
+ dir="rtl"
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/132.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/132.xhtml
new file mode 100644
index 0000000000..d3d57dd843
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/132.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>RTL text selection drag and drop from RTL textarea to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,11)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea dir="rtl">&#x202E;FAIL|SSAP&#x202C;</textarea></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/133.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/133.xhtml
new file mode 100644
index 0000000000..40196315f7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/133.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Multielement selection drag and drop</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('div'))">
+<div>
+ <p>Drag selected text to the textarea.</p>
+ <p>Copy of selection should end up in the textarea once you drop it there.</p>
+</div>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/134.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/134.xhtml
new file mode 100644
index 0000000000..939e295723
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/134.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop after selecting whole page content</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('body'))">
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/135.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/135.xhtml
new file mode 100644
index 0000000000..44c8c39dea
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/135.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop after manually selecting whole page content</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p>Select whole page (Ctrl+A) and try to drag selection to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/136.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/136.xhtml
new file mode 100644
index 0000000000..d005718219
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/136.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>BiDi text selection drag and drop to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF</p>
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/137.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/137.xhtml
new file mode 100644
index 0000000000..8cec81cb6e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/137.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Multielement BiDi text selection drag and drop to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>PASS|LIAF <span dir="rtl">&#x202E;FAIL|SSAP&#x202C;</span> PASS|LIAF</p>
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/138.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/138.xhtml
new file mode 100644
index 0000000000..eda68f1f12
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/138.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>BiDi text selection drag and drop from text input to textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><input value="PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF"/></p>
+<p><textarea dir="rtl" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/139.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/139.xhtml
new file mode 100644
index 0000000000..03c6a6bc2a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/139.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>BiDi text selection drag and drop between textareas</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,32)">
+<p>Drag selected text to the textarea. Selected text should be moved to the textarea once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><textarea>PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF</textarea></p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/140.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/140.xhtml
new file mode 100644
index 0000000000..fb0c3ca198
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/140.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>BiDi text selection drag and drop between text inputs</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the input below. Selected text should be moved to the input once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><input value="PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/141.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/141.xhtml
new file mode 100644
index 0000000000..79f550f05c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/141.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>BiDi text selection drag and drop to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF</p>
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<p><div contenteditable="true"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/142.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/142.xhtml
new file mode 100644
index 0000000000..1522a0cdbd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/142.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>BiDi text selection drag and drop to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF</p>
+<p>Drag selected text to the blue box. Copy of selection should end up in the box once you drop it there. Text direction should be preserved during drag and drop.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/143.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/143.xhtml
new file mode 100644
index 0000000000..f38e1bfb4e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/143.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Large text selection drag and drop</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {var p = document.querySelector('p:last-child'), s = p.firstChild.nodeValue;
+ for(var i = 0; i != 12; i++)
+ {s = s + ' ' + s;}
+ p.firstChild.nodeValue = 'Start of selection. ' + s + ' End of selection';
+ window.getSelection().selectAllChildren(p);}
+</script>
+</head>
+<body onload="start()">
+<p>Try to drag selection below to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+<p>Large selection.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/144.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/144.xhtml
new file mode 100644
index 0000000000..90dd440f6c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/144.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from text input and text/plain aliases</title>
+<style type="text/css">
+div
+ {min-height:40px;
+ width:40px;
+ padding:40px;
+ text-align:center;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('text/plain','PASS');}
+function checkText(event)
+ {var a = event.dataTransfer.getData('text/plain'),
+ b = event.dataTransfer.getData('text'),
+ c = event.dataTransfer.getData('TEXT'),
+ d = event.dataTransfer.getData('TexT');
+ document.querySelector('div').appendChild(document.createTextNode((a == b &amp;&amp; b == c &amp;&amp; c == d &amp;&amp; d == 'PASS')?' PASS ':' FAIL '))}
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me" ondragstart="start(event)"/></p>
+<p>Drag selection above to the navy box below and drop it. You should see word PASS once you drop it.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="checkText(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/145.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/145.xhtml
new file mode 100644
index 0000000000..addc92b412
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/145.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from inline element and text/plain aliases</title>
+<style type="text/css">
+div
+ {min-height:40px;
+ width:40px;
+ padding:40px;
+ text-align:center;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+function start(event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setData('Text','PASS');}
+function checkText(event)
+ {var a = event.dataTransfer.getData('text/plain'),
+ b = event.dataTransfer.getData('text'),
+ c = event.dataTransfer.getData('TEXT'),
+ d = event.dataTransfer.getData('TexT');
+ document.querySelector('div').appendChild(document.createTextNode((a == b &amp;&amp; b == c &amp;&amp; c == d &amp;&amp; d == 'PASS')?'PASS':'FAIL'))}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('span'))">
+<p><span ondragstart="start(event)">Drag me</span></p>
+<p>Drag selection above to the navy box below and drop it. You should see word PASS once you drop it.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="checkText(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/146.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/146.xhtml
new file mode 100644
index 0000000000..bc8d1f015c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/146.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop and text/plain aliases</title>
+<style type="text/css">
+div
+ {min-height:40px;
+ width:40px;
+ padding:40px;
+ text-align:center;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+function checkText(event)
+ {var a = event.dataTransfer.getData('text/plain'),
+ b = event.dataTransfer.getData('text'),
+ c = event.dataTransfer.getData('TEXT'),
+ d = event.dataTransfer.getData('TexT');
+ document.querySelector('div').appendChild(document.createTextNode((a == b &amp;&amp; b == c &amp;&amp; c == d &amp;&amp; d == 'Drag me')?'PASS':'FAIL'))}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Drag selection above to the navy box below and drop it. You should see word PASS once you drop it.</p>
+<div ondragenter="event.preventDefault()" ondragover="return false" ondrop="checkText(event)"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/147.html b/testing/web-platform/tests/html/editing/dnd/selection/147.html
new file mode 100644
index 0000000000..814fe1b74e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/147.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Selection should not be cleared on failed drop</title>
+ </head>
+ <body>
+
+ <ol>
+ <li>Select some text in this sentence and begin dragging it.</li>
+ <li>Drop it on this text.</li>
+ <li>Pass if the text in step 1 is still selected.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/148.html b/testing/web-platform/tests/html/editing/dnd/selection/148.html
new file mode 100644
index 0000000000..3552ad6f2d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/148.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Selection in input should not be cleared on failed drop</title>
+ </head>
+ <body>
+
+ <ol>
+ <li><textarea rows="3" cols="60">Select some text in this sentence and begin dragging it.</textarea></li>
+ <li>Drop it on this text.</li>
+ <li>Pass if the text in step 1 is still selected.</li>
+ </ol>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/149.html b/testing/web-platform/tests/html/editing/dnd/selection/149.html
new file mode 100644
index 0000000000..5371121191
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/149.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging selections from multiline inputs should show editing caret</title>
+ </head>
+ <body>
+
+ <p>Select one word in the first input. Drag the selection around over the same input, and over the other inputs. It should show the input editing caret at the relevant insertion point within the target input's text.</p>
+ <p><textarea rows="3" cols="60">Dummy text, dummy text, dummy text, dummy text, dummy text,
+dummy text, dummy text, dummy text, dummy text, dummy text</textarea></p>
+ <p><textarea rows="3" cols="60">Dummy text, dummy text, dummy text, dummy text, dummy text,
+dummy text, dummy text, dummy text, dummy text, dummy text</textarea></p>
+ <p><input value="Dummy text, dummy text"></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/150.html b/testing/web-platform/tests/html/editing/dnd/selection/150.html
new file mode 100644
index 0000000000..2861ad86f7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/150.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Dragging selections from single-line inputs should show editing caret</title>
+ </head>
+ <body>
+
+ <p>Select one word in the first input. Drag the selection around over the same input, and over the other inputs. It should show the input editing caret at the relevant insertion point within the target input's text.</p>
+ <p><input value="Dummy text, dummy text"></p>
+ <p><textarea rows="3" cols="60">Dummy text, dummy text, dummy text, dummy text, dummy text,
+dummy text, dummy text, dummy text, dummy text, dummy text</textarea></p>
+ <p><input value="Dummy text, dummy text"></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/151.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/151.xhtml
new file mode 100644
index 0000000000..ed06757109
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/151.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropping selection in readonly text input</title>
+<style type="text/css">
+input
+ {padding:1em;
+ width:300px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Try to drag selected text into the input below. You should not be able to drop it here.</p>
+<p><input readonly="readonly" placeholder="Try to drop selected text here" onchange="document.querySelector('p+p').firstChild.nodeValue = 'FAIL'"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/152.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/152.xhtml
new file mode 100644
index 0000000000..4bf402cf15
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/152.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropping selection in readonly textarea</title>
+<style type="text/css">
+textarea
+ {padding:1em;
+ width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Try to drag selected text into the input below. You should not be able to drop it here.</p>
+<p><textarea readonly="readonly" placeholder="Try to drop selected text here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/153.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/153.xhtml
new file mode 100644
index 0000000000..bb3eb29f0d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/153.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropping selection from text input into readonly text input</title>
+<style type="text/css">
+input
+ {padding:1em;
+ width:300px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me"/></p>
+<p>Try to drag selected text into the input below. You should not be able to drop it here.</p>
+<p><input readonly="readonly" placeholder="Try to drop selected text here" onchange="document.querySelector('p+p').firstChild.nodeValue = 'FAIL'"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/154.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/154.xhtml
new file mode 100644
index 0000000000..d6a269acfa
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/154.xhtml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dropping selection from text input into readonly textarea</title>
+<style type="text/css">
+textarea
+ {padding:1em;
+ width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me"/></p>
+<p>Try to drag selected text into the input below. You should not be able to drop it here.</p>
+<p><textarea readonly="readonly" placeholder="Try to drop selected text here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/155.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/155.xhtml
new file mode 100644
index 0000000000..c3b4fa5bf6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/155.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop from readonly textarea to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea readonly="readonly">Drag me</textarea></p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/156.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/156.xhtml
new file mode 100644
index 0000000000..cbdcdd826c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/156.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between text inputs</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Selected text should be moved to the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/157.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/157.xhtml
new file mode 100644
index 0000000000..e754b67d4e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/157.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between search inputs</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the second input box. Copy of selection should end up in the second input once you drop it there.</p>
+<p><input type="search" value="Selection"/></p>
+<p><input type="search" placeholder="Drop selection here"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/158.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/158.xhtml
new file mode 100644
index 0000000000..a17ac4cf19
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/158.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between tel inputs</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the second input box. Copy of selection should end up in the second input once you drop it there.</p>
+<p><input type="tel" value="123456789"/></p>
+<p><input type="tel" placeholder="Drop selection here"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/159.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/159.xhtml
new file mode 100644
index 0000000000..b8987acc56
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/159.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between url inputs</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the second input box. Copy of selection should end up in the second input once you drop it there.</p>
+<p><input type="url" value="http://example.org"/></p>
+<p><input type="url" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/160.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/160.xhtml
new file mode 100644
index 0000000000..8bb0297e47
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/160.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between email inputs</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the second input box. Copy of selection should end up in the second input once you drop it there.</p>
+<p><input type="email" value="mail@example.org"/></p>
+<p><input type="email" placeholder="Drop selection here"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/161.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/161.xhtml
new file mode 100644
index 0000000000..f292bff1dd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/161.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between number inputs</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the second input box. Copy of selection should end up in the second input once you drop it there.</p>
+<p><input type="number" value="123456789"/></p>
+<p><input type="number" placeholder="Drop selection here"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/162.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/162.xhtml
new file mode 100644
index 0000000000..4c385c922f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/162.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop between contenteditable elements</title>
+<style type="text/css">
+div + div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('div'))">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<div contenteditable="true">Drag me</div>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/163.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/163.xhtml
new file mode 100644
index 0000000000..51ed45bdf6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/163.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Moving text between text inputs</title>
+<script type="application/ecmascript">
+function check()
+ {var input = document.querySelectorAll('input');
+ document.querySelector('p').firstChild.nodeValue = (input[0].value == '' &amp;&amp; input[1].value == 'Drag me')?'PASS':'FAIL';}
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. You should see word PASS once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<p><input ondragenter="event.preventDefault()" ondragover="event.preventDefault();event.dataTransfer.effectAllowed = 'move'" ondrop="window.setTimeout('check()',100)" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/164.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/164.xhtml
new file mode 100644
index 0000000000..4b7cb8c048
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/164.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Moving text between search inputs</title>
+<script type="application/ecmascript">
+function check()
+ {var input = document.querySelectorAll('input');
+ document.querySelector('p').firstChild.nodeValue = (input[0].value == '' &amp;&amp; input[1].value == 'Selection')?'PASS':'FAIL';}
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="search" value="Selection"/></p>
+<p><input ondragenter="event.preventDefault()" ondragover="event.preventDefault();event.dataTransfer.effectAllowed = 'move'" ondrop="window.setTimeout('check()',100)" type="search" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/165.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/165.xhtml
new file mode 100644
index 0000000000..ed6e0c6f98
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/165.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Moving text between tel inputs</title>
+<script type="application/ecmascript">
+function check()
+ {var input = document.querySelectorAll('input');
+ document.querySelector('p').firstChild.nodeValue = (input[0].value == '' &amp;&amp; input[1].value == '123456789')?'PASS':'FAIL';}
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected number to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="tel" value="123456789"/></p>
+<p><input ondragenter="event.preventDefault()" ondragover="event.preventDefault();event.dataTransfer.effectAllowed = 'move'" ondrop="window.setTimeout('check()',100)" type="tel" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/166.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/166.xhtml
new file mode 100644
index 0000000000..467cf6eeac
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/166.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Moving text between url inputs</title>
+<script type="application/ecmascript">
+function check()
+ {var input = document.querySelectorAll('input');
+ document.querySelector('p').firstChild.nodeValue = (input[0].value == '' &amp;&amp; input[1].value == 'http://example.org')?'PASS':'FAIL';}
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected url to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="url" value="http://example.org"/></p>
+<p><input ondragenter="event.preventDefault()" ondragover="event.preventDefault();event.dataTransfer.effectAllowed = 'move'" ondrop="window.setTimeout('check()',100)" type="url" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/167.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/167.xhtml
new file mode 100644
index 0000000000..b9f7beb973
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/167.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Moving text between email inputs</title>
+<script type="application/ecmascript">
+function check()
+ {var input = document.querySelectorAll('input');
+ document.querySelector('p').firstChild.nodeValue = (input[0].value == '' &amp;&amp; input[1].value == 'mail@example.org')?'PASS':'FAIL';}
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected email to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input type="email" value="mail@example.org"/></p>
+<p><input ondragenter="event.preventDefault()" ondragover="event.preventDefault();event.dataTransfer.effectAllowed = 'move'" ondrop="window.setTimeout('check()',100)" type="email" placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/168.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/168.xhtml
new file mode 100644
index 0000000000..a2c19c453d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/168.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Moving text between contenteditable elements</title>
+<style type="text/css">
+div + div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+function check()
+ {var div = document.querySelectorAll('div');
+ document.querySelector('p').firstChild.nodeValue = (!div[0].firstChild.nodeValue &amp;&amp; div[1].firstChild.nodeValue == 'Drag me')?'PASS':('FAIL(' + div[0].firstChild.nodeValue + ')');}
+</script>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('div'))">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<div contenteditable="true" ondragstart="event.dataTransfer.effectAllowed = 'move'">Drag me</div>
+<div ondragenter="event.preventDefault()" ondragover="event.preventDefault();" ondrop="window.setTimeout('check()',100)" contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/169.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/169.xhtml
new file mode 100644
index 0000000000..ce4dd21399
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/169.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Moving text from text input to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+<script type="application/ecmascript">
+function check()
+ {document.querySelector('p').firstChild.nodeValue = (document.querySelector('input').value == '' &amp;&amp; document.querySelector('div').firstChild.nodeValue == 'Drag me')?'PASS':('FAIL(' + div[0].firstChild.nodeValue + ')');}
+</script>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+<div ondragenter="event.preventDefault()" ondragover="event.preventDefault();event.dataTransfer.effectAllowed = 'move'" ondrop="window.setTimeout('check()',100)" contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/170.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/170.xhtml
new file mode 100644
index 0000000000..cbcf3dc164
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/170.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging multiline text selection from scrolled textarea to block element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(163,185)">
+<p>
+<textarea>
+Here is textarea with selection at the end of it.
+Drag selected text to the blue box.
+Copy of selection should end up in the blue box once you drop it there.
+Try to drag this text.
+</textarea>
+</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/171.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/171.xhtml
new file mode 100644
index 0000000000..c8c16b0b16
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/171.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging text selection from scrollable container to block element</title>
+<style type="text/css">
+div
+ {width:300px;
+ height:100px;}
+div:first-child
+ {overflow-y:scroll;}
+div[ondragenter]
+ {margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+textarea
+ {width:300px;
+ height:100px;}
+p+p
+ {margin-top:200px;
+ padding-bottom:50px;}
+</style>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p + p'))">
+<div>
+<p>
+Here is scrollable container with text selection at the end of it.
+Drag selected text to the blue box.
+Copy of selection should end up in the blue box once you drop it there.
+</p>
+<p>Try to drag this text.</p>
+</div>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div + div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/172.html b/testing/web-platform/tests/html/editing/dnd/selection/172.html
new file mode 100644
index 0000000000..3e805e764b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/172.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Selection dragging should drag minimal HTML context</title>
+ <style type="text/css">
+p + div {
+ background: orange;
+ width: 4em;
+}
+div + div {
+ border: 1px solid black;
+ min-height: 5em;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var output = document.getElementsByTagName('div')[1];
+ output.ondragenter = output.ondragover = function (e) { e.preventDefault(); };
+ output.ondrop = function (e) {
+ e.preventDefault();
+ var drophtml = e.dataTransfer.getData('text/html').toUpperCase().replace(/\sSTYLE="[^"]*"/g,'').replace(/[\r\n]+/g,'\\n');
+ var droptext = e.dataTransfer.getData('text/plain').replace(/[\r\n]+/g,'\\n');
+ var expectedhtml = '<P><SPAN>C</SPAN>D</P><P>E<SPAN>F</SPAN></P>';
+ var expectedtext = 'CD\\nEF';
+ if( drophtml != expectedhtml ) {
+ output.innerHTML = 'FAIL - normalised dropped HTML was:<br>'+drophtml.replace(/</g,'&lt;');
+ } else if( droptext != expectedtext ) {
+ output.innerHTML = 'FAIL - normalised dropped text was:<br>'+droptext.replace(/</g,'&lt;');
+ } else {
+ output.innerHTML = 'PASS';
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Select the text from &quot;C&quot; to &quot;F&quot; (including both of those characters) in the text below.</p>
+ <div>
+ <p><span>A</span><span>BC</span>D</p><p>E<span>FG</span><span>H</span></p>
+ </div>
+ <div>Drag the selection and drop it here.</div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/173.html b/testing/web-platform/tests/html/editing/dnd/selection/173.html
new file mode 100644
index 0000000000..8c074f7c5d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/173.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Selection dragging should drag no HTML when inside a single element</title>
+ <style type="text/css">
+p + div {
+ background: orange;
+ width: 4em;
+}
+div + div {
+ border: 1px solid black;
+ min-height: 5em;
+}
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var output = document.getElementsByTagName('div')[1];
+ output.ondragenter = output.ondragover = function (e) { e.preventDefault(); };
+ output.ondrop = function (e) {
+ e.preventDefault();
+ var drophtml = e.dataTransfer.getData('text/html').toUpperCase().replace(/\sSTYLE="[^"]*"/g,'').replace(/[\r\n]+/g,'\\n');
+ var droptext = e.dataTransfer.getData('text/plain').replace(/[\r\n]+/g,'\\n');
+ var expectedhtml = 'B';
+ var expectedtext = 'B';
+ if( drophtml != expectedhtml ) {
+ output.innerHTML = 'FAIL - normalised dropped HTML was:<br>'+drophtml.replace(/</g,'&lt;');
+ } else if( droptext != expectedtext ) {
+ output.innerHTML = 'FAIL - normalised dropped text was:<br>'+droptext.replace(/</g,'&lt;');
+ } else {
+ output.innerHTML = 'PASS';
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <p>Select &quot;B&quot; in the text below.</p>
+ <div>
+ <p>ABC</p>
+ </div>
+ <div>Drag the selection and drop it here.</div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-input.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-input.xhtml
new file mode 100644
index 0000000000..f005092f6e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-input.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the input field below. Copy of selection should end up in the input once you drop it there.</p>
+<p><input value="Drag me"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-textarea.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-textarea.xhtml
new file mode 100644
index 0000000000..767e5a43e2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input-to-other-textarea.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><input value="Drag me"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input.xhtml
new file mode 100644
index 0000000000..9d12a89dd7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-input.xhtml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><input value="Drag me"/></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-blue-box.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-blue-box.xhtml
new file mode 100644
index 0000000000..e49967e174
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-blue-box.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-input.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-input.xhtml
new file mode 100644
index 0000000000..bf37852588
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-input.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the input field below. Copy of selection should end up in the input once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-textarea.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-textarea.xhtml
new file mode 100644
index 0000000000..e7b72990af
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drag-me-textarea-to-other-textarea.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p>Drag selected text to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea>Drag me</textarea></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box-contenteditable.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box-contenteditable.xhtml
new file mode 100644
index 0000000000..67601b961c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box-contenteditable.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box.xhtml
new file mode 100644
index 0000000000..575db934d8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-blue-box.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-input.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-input.xhtml
new file mode 100644
index 0000000000..bca49a016c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-input.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;}
+</style>
+</head>
+<body>
+<p><input placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-textarea.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-textarea.xhtml
new file mode 100644
index 0000000000..c7617f5be8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-drop-here-textarea.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/selection/helper-scroll-then-drop-input.xhtml b/testing/web-platform/tests/html/editing/dnd/selection/helper-scroll-then-drop-input.xhtml
new file mode 100644
index 0000000000..af2aeae393
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/selection/helper-scroll-then-drop-input.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selection drag and drop: helper file</title>
+<style type="text/css">
+input[placeholder]
+ {width:300px;
+ height:50px;}
+p:first-child
+ {margin-bottom:200px;}
+</style>
+</head>
+<body>
+<p>You should be able to drag selected text to the input at the bottom of scrollable container (dragging towards the bottom edge triggers scrolling).</p>
+<input placeholder="Drop selection here"/>
+<p>Copy of selection should end up in the input once you drop it there.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/001.svg b/testing/web-platform/tests/html/editing/dnd/svg/001.svg
new file mode 100644
index 0000000000..055a7b278b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/001.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from text element to textArea</title>
+<text x="10" y="30" font-size="20">Select part of this text and drag selection to</text>
+<text x="10" y="60" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="90" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="110" width="480" height="380" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="110" width="480" height="380" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/002.svg b/testing/web-platform/tests/html/editing/dnd/svg/002.svg
new file mode 100644
index 0000000000..45ab566e66
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/002.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from editable text element to textArea</title>
+<text x="10" y="30" editable="simple" font-size="20">Select part of this text and drag selection to</text>
+<text x="10" y="60" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="90" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="110" width="480" height="380" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="110" width="480" height="380" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/003.svg b/testing/web-platform/tests/html/editing/dnd/svg/003.svg
new file mode 100644
index 0000000000..e59527938b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/003.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from tspan elements to textArea</title>
+<text x="10" y="30" font-size="20">
+ <tspan>Select part</tspan>
+ <tspan>of this text</tspan>
+ <tspan>and drag selection to</tspan>
+</text>
+<text x="10" y="60" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="90" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="110" width="480" height="380" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="110" width="480" height="380" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/004.svg b/testing/web-platform/tests/html/editing/dnd/svg/004.svg
new file mode 100644
index 0000000000..000f52730c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/004.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from tref element to textArea</title>
+<defs>
+ <text id="text">Select me</text>
+</defs>
+<text x="10" y="30" font-size="20" fill="navy"><tref xlink:href="#text"/></text>
+<text x="10" y="60" font-size="20">Select text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/005.svg b/testing/web-platform/tests/html/editing/dnd/svg/005.svg
new file mode 100644
index 0000000000..9bb3bd68bb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/005.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from textArea to textArea</title>
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+<textArea x="10" y="110" width="480" height="380" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="110" width="480" height="380" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/006.svg b/testing/web-platform/tests/html/editing/dnd/svg/006.svg
new file mode 100644
index 0000000000..5edeba6971
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/006.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from text element to editable text element</title>
+<text x="10" y="30" font-size="20">Select part of this text and drag selection to</text>
+<text x="10" y="60" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="90" font-size="20">copies once it's dropped in the box.</text>
+<text x="10" y="200" font-size="50" editable="simple">                             </text>
+<rect x="10" y="160" width="480" height="50" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/007.svg b/testing/web-platform/tests/html/editing/dnd/svg/007.svg
new file mode 100644
index 0000000000..7d8bcf8d4d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/007.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection between editable text elements</title>
+<text x="10" y="30" font-size="20" editable="simple">Select part of this text and drag selection to</text>
+<text x="10" y="60" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="90" font-size="20">copies once it's dropped in the box.</text>
+<text x="10" y="200" font-size="50" editable="simple">                             </text>
+<rect x="10" y="160" width="480" height="50" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/008.svg b/testing/web-platform/tests/html/editing/dnd/svg/008.svg
new file mode 100644
index 0000000000..51065c9441
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/008.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from tspan element to editable text element</title>
+<text x="10" y="30" font-size="20">
+ <tspan>Select part</tspan>
+ <tspan>of this text</tspan>
+ <tspan>and drag selection to</tspan>
+</text>
+<text x="10" y="60" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="90" font-size="20">copies once it's dropped in the box.</text>
+<text x="10" y="200" font-size="50" editable="simple">                             </text>
+<rect x="10" y="160" width="480" height="50" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/009.svg b/testing/web-platform/tests/html/editing/dnd/svg/009.svg
new file mode 100644
index 0000000000..1996d25680
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/009.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from tref element to editable text element</title>
+<defs>
+ <text id="text">Select me</text>
+</defs>
+<text x="10" y="30" font-size="20" fill="navy"><tref xlink:href="#text"/></text>
+<text x="10" y="60" font-size="20">Select text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<text x="10" y="200" font-size="50" editable="simple">                             </text>
+<rect x="10" y="160" width="480" height="50" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/010.svg b/testing/web-platform/tests/html/editing/dnd/svg/010.svg
new file mode 100644
index 0000000000..c5cce18678
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/010.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from textArea to editable text element</title>
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+<text x="10" y="200" font-size="50" editable="simple">                             </text>
+<rect x="10" y="160" width="480" height="50" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/011.svg b/testing/web-platform/tests/html/editing/dnd/svg/011.svg
new file mode 100644
index 0000000000..5618a89e29
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/011.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from textpath to textArea</title>
+<defs>
+ <path id="path" d="M 10 40 C 10 20 480 20 480 40"/>
+</defs>
+<text fill="navy">
+ <textPath xlink:href="#path">Some selectable text rendered along curved text path</textPath>
+</text>
+<text x="10" y="60" font-size="20">Select part of text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/012.svg b/testing/web-platform/tests/html/editing/dnd/svg/012.svg
new file mode 100644
index 0000000000..8a50f06c97
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/012.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from editable textpath to textArea</title>
+<defs>
+ <path id="path" d="M 10 40 C 10 20 480 20 480 40"/>
+</defs>
+<text fill="navy" editable="simple">
+ <textPath xlink:href="#path">Some selectable text rendered along curved text path</textPath>
+</text>
+<text x="10" y="60" font-size="20">Select part of text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/013-1.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/013-1.xhtml
new file mode 100644
index 0000000000..7fc593e07e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/013-1.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from SVG: helper file</title>
+<style type="text/css">
+textarea
+ {width:400px;
+ height:300px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/013.svg b/testing/web-platform/tests/html/editing/dnd/svg/013.svg
new file mode 100644
index 0000000000..9dc0a47e5f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/013.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from text element to XHTML textarea in foreignObject</title>
+<text x="10" y="30" font-size="20">Select part of this text and drag selection to</text>
+<text x="10" y="60" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="90" font-size="20">copies once it's dropped in the box.</text>
+<switch>
+ <foreignObject x="10" y="120" width="480" height="380" requiredExtensions="http://www.w3.org/1999/xhtml" xlink:href="013-1.xhtml"/>
+ <text x="10" y="150" font-size="20">Skip the test (foreignObject is not supported).</text>
+</switch>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/014-1.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/014-1.xhtml
new file mode 100644
index 0000000000..93d6ab7d2f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/014-1.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from SVG: helper file</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/014.svg b/testing/web-platform/tests/html/editing/dnd/svg/014.svg
new file mode 100644
index 0000000000..acee8efbf4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/014.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from textArea to contenteditable element in foreignObject</title>
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+<switch>
+ <foreignObject x="10" y="120" width="480" height="380" requiredExtensions="http://www.w3.org/1999/xhtml" xlink:href="014-1.xhtml"/>
+ <text x="10" y="150" font-size="20">Skip the test (foreignObject is not supported).</text>
+</switch>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/015-1.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/015-1.xhtml
new file mode 100644
index 0000000000..f5217a91e9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/015-1.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from SVG: helper file</title>
+</head>
+<body>
+<input placeholder="Drop selection here"/>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/015.svg b/testing/web-platform/tests/html/editing/dnd/svg/015.svg
new file mode 100644
index 0000000000..de484d2b81
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/015.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from textArea to XHTML input in foreignObject</title>
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+<switch>
+ <foreignObject x="10" y="120" width="480" height="380" requiredExtensions="http://www.w3.org/1999/xhtml" xlink:href="015-1.xhtml"/>
+ <text x="10" y="150" font-size="20">Skip the test (foreignObject is not supported).</text>
+</switch>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/016-1.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/016-1.xhtml
new file mode 100644
index 0000000000..d735cce60f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/016-1.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from SVG: helper file</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<div
+ ondragenter="event.preventDefault();event.dataTransfer.effectAllowed = 'copy'"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/016.svg b/testing/web-platform/tests/html/editing/dnd/svg/016.svg
new file mode 100644
index 0000000000..fe873924ad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/016.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG text selection from textArea to foreignObject</title>
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+<switch>
+ <foreignObject x="10" y="120" width="480" height="380" requiredExtensions="http://www.w3.org/1999/xhtml" xlink:href="016-1.xhtml"/>
+ <text x="10" y="150" font-size="20">Skip the test (foreignObject is not supported).</text>
+</switch>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/017.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/017.xhtml
new file mode 100644
index 0000000000..598a2c6f3d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/017.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection to SVG textArea</title>
+</head>
+<body onload="window.getSelection().selectAllChildren(document.querySelector('p'))">
+<p>Drag me</p>
+<p>Drag selection above to the box below. Copy of selection should end up in the box once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/018.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/018.xhtml
new file mode 100644
index 0000000000..03ac442a36
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/018.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from XHTML text input to SVG textArea</title>
+</head>
+<body onload="document.querySelector('input').select()">
+<p><input value="Drag me"/></p>
+<p>Drag selection above to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/019.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/019.xhtml
new file mode 100644
index 0000000000..9064a6049a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/019.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from XHTML textarea to SVG textArea</title>
+<style type="text/css">
+textarea
+ {height:1.5em;
+ width:7em;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(0,7)">
+<p><textarea>Drag me</textarea></p>
+<p>Drag selection above to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/020.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/020.xhtml
new file mode 100644
index 0000000000..7d96b4f12e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/020.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging multiline selection from XHTML textarea to SVG textArea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body onload="var t = document.querySelector('textarea');t.select();t.setSelectionRange(25,56)">
+<p>
+<textarea>
+Here is textarea
+with selection that
+spans two lines.
+Drag selected text to the blue box.
+Copy of selection should end up in the blue box once you drop it there.
+</textarea>
+</p>
+<p>Drag selection above to the textarea below. Copy of selection should end up in the textarea once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/021.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/021.xhtml
new file mode 100644
index 0000000000..4d82e74428
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/021.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG text element to XHTML element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy">Select me</text>
+</svg>
+<p>Select the text above and drag selection to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/022.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/022.xhtml
new file mode 100644
index 0000000000..7354aeef8b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/022.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG editable text element to XHTML element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy" editable="simple">Select me</text>
+</svg>
+<p>Select the text above and drag selection to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/023.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/023.xhtml
new file mode 100644
index 0000000000..65ede4bd8b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/023.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG textArea to XHTML element</title>
+<style type="text/css">
+div[ondragenter]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500px" height="100px" viewBox="0 0 500 100">
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+</svg>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/plain')))"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/024.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/024.xhtml
new file mode 100644
index 0000000000..5b4116c6c2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/024.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG text element to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy">Select me</text>
+</svg>
+<p>Select the text above and drag selection to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/025.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/025.xhtml
new file mode 100644
index 0000000000..86d68018fd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/025.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG editable text element to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy" editable="simple">Select me</text>
+</svg>
+<p>Select the text above and drag selection to the blue box. Copy of selection should end up in the blue box once you drop it there.</p>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/026.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/026.xhtml
new file mode 100644
index 0000000000..78699f1c6e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/026.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG textArea to contenteditable element</title>
+<style type="text/css">
+div[contenteditable]
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500px" height="100px" viewBox="0 0 500 100">
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+</svg>
+<div contenteditable="true"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/027.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/027.xhtml
new file mode 100644
index 0000000000..5dbce70ce9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/027.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG text element to XHTML textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy">Select me</text>
+</svg>
+<p>Select the text above and drag selection to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/028.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/028.xhtml
new file mode 100644
index 0000000000..d60b518cd0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/028.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG editable text element to XHTML textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy" editable="simple">Select me</text>
+</svg>
+<p>Select the text above and drag selection to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/029.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/029.xhtml
new file mode 100644
index 0000000000..d469841fad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/029.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging selection from SVG textArea to XHTML textarea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500px" height="100px" viewBox="0 0 500 100">
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select part of this text and drag selection to the box below.
+Content of selection should be copied once it's dropped in the box.</textArea>
+</svg>
+<p>Select the text above and drag selection to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/030-1.svg b/testing/web-platform/tests/html/editing/dnd/svg/030-1.svg
new file mode 100644
index 0000000000..62798a7e11
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/030-1.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="100px" viewBox="0 0 100 100">
+<circle cx="50" cy="50" r="50" fill="green"/>
+</svg>
+
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/030.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/030.xhtml
new file mode 100644
index 0000000000..b20d646815
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/030.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG image drag and drop</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list'));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="030-1.svg" alt="SVG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/031.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/031.xhtml
new file mode 100644
index 0000000000..81c355d6c1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/031.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG dataURL image drag and drop</title>
+<style type="text/css">
+div[ondragenter]
+ {width:105px;
+ min-height:105px;
+ text-align:center;
+ margin-top:20px;
+ padding:10px;
+ border:solid thin navy;}
+p:first-child
+ {padding-left:12px;}
+</style>
+<script type="application/ecmascript">
+function addImage(event)
+ {var c = document.createElement('img');
+ c.setAttribute('src',event.dataTransfer.getData('text/uri-list'));
+ document.querySelector('div').appendChild(c);}
+</script>
+</head>
+<body>
+<p><img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22100px%22%20height%3D%22100px%22%20viewBox%3D%220%200%20100%20100%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22green%22/%3E%3C/svg%3E" alt="SVG circle" ondragstart="event.dataTransfer.effectAllowed = 'copy'"/></p>
+<p>Drag circle above to the box below. It should be copied to the box once you drop it there.</p>
+<div
+ ondragenter="event.preventDefault()"
+ ondragover="return false"
+ ondrop="addImage(event)"
+/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/032.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/032.xhtml
new file mode 100644
index 0000000000..dc5592d475
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/032.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross fragment drag and drop of SVG text selection from text element to textArea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy">Select me</text>
+</svg>
+<p>Drag selection above to the textarea below. Copy of selection should end up in the textarea once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/033.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/033.xhtml
new file mode 100644
index 0000000000..7d55fec093
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/033.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross fragment drag and drop of SVG text selection from editable text element to textArea</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy" editable="simple">Select me</text>
+</svg>
+<p>Drag selection above to the textarea below. Copy of selection should end up in the textarea once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/034.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/034.xhtml
new file mode 100644
index 0000000000..5ac9dacc5c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/034.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross fragment drag and drop of SVG text selection from textArea to editable text element</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500px" height="100px" viewBox="0 0 500 100">
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select me.</textArea>
+</svg>
+<p>Drag selection above to the textarea below. Copy of selection should end up in the textarea once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/035.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/035.xhtml
new file mode 100644
index 0000000000..261ae2e995
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/035.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Cross fragment drag and drop between SVG textAreas</title>
+<style type="text/css">
+textarea
+ {width:300px;
+ height:100px;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500px" height="100px" viewBox="0 0 500 100">
+<textArea x="10" y="10" width="480" height="90" font-size="20">Select me.</textArea>
+</svg>
+<p>Drag selection above to the textarea below. Copy of selection should end up in the textarea once you drop it there.</p>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400px" height="50px" viewBox="0 0 400 50">
+<text x="10" y="40" font-size="50" editable="simple">                        </text>
+<rect x="10" y="0" width="380" height="50" stroke="black" stroke-width="1" fill="none"/>
+</svg>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/036-1.svg b/testing/web-platform/tests/html/editing/dnd/svg/036-1.svg
new file mode 100644
index 0000000000..d16862da64
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/036-1.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+<text x="10" y="45" font-size="30" fill="navy">Select me</text>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/036.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/036.xhtml
new file mode 100644
index 0000000000..cb774b3ebc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/036.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging text selection between different SVG images</title>
+</head>
+<body>
+<p><object type="image/svg+xml" data="036-1.svg">SVG Image</object></p>
+<p>Drag selection above to the box below. Copy of selection should end up in the box once you drop it there.</p>
+<p><object type="image/svg+xml" data="helper-drop-selection-here-textArea.svg">SVG Image</object></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/037.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/037.xhtml
new file mode 100644
index 0000000000..b97f99cc46
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/037.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging text selection from dataURL image to another SVG image</title>
+</head>
+<body>
+<object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22200px%22%20height%3D%2250px%22%20viewBox%3D%220%200%20200%2050%22%3E%3Ctext%20x%3D%2210%22%20y%3D%2245%22%20font-size%3D%2230%22%20fill%3D%22navy%22%3ESelect%20me%3C/text%3E%3C/svg%3E">SVG Image</object>
+<p>Drag selection above to the box below. Copy of selection should end up in the box once you drop it there.</p>
+<p><object type="image/svg+xml" data="helper-drop-selection-here-textArea.svg">SVG Image</object></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/038.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/038.xhtml
new file mode 100644
index 0000000000..c74b1db9cc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/038.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging text selection between different SVG dataURL images</title>
+</head>
+<body>
+<p><object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22200px%22%20height%3D%2250px%22%20viewBox%3D%220%200%20200%2050%22%3E%3Ctext%20x%3D%2210%22%20y%3D%2245%22%20font-size%3D%2230%22%20fill%3D%22navy%22%3ESelect%20me%3C/text%3E%3C/svg%3E">SVG Image</object></p>
+<p>Drag selection above to the box below. Copy of selection should end up in the box once you drop it there.</p>
+<p><object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.2%22%20width%3D%22500px%22%20height%3D%22300px%22%20viewBox%3D%220%200%20500%20300%22%3E%3CtextArea%20x%3D%2210%22%20y%3D%2210%22%20width%3D%22480%22%20height%3D%22280%22%20font-size%3D%2220%22%20editable%3D%22simple%22%20pointer-events%3D%22boundingBox%22%3EDrop%20selection%20here%3C/textArea%3E%3Crect%20x%3D%2210%22%20y%3D%2210%22%20width%3D%22480%22%20height%3D%22280%22%20stroke%3D%22black%22%20stroke-width%3D%221%22%20fill%3D%22none%22/%3E%3C/svg%3E">SVG Image</object></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/039-1.svg b/testing/web-platform/tests/html/editing/dnd/svg/039-1.svg
new file mode 100644
index 0000000000..7023ddd938
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/039-1.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="250px" height="50px" viewBox="0 0 250 50">
+<textArea x="10" y="10" width="200" height="50" font-size="20">Select me.</textArea>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/039.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/039.xhtml
new file mode 100644
index 0000000000..77bc7e9c9d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/039.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging textArea selection between different SVG images</title>
+</head>
+<body>
+<p><object type="image/svg+xml" data="039-1.svg">SVG Image</object></p>
+<p>Drag selection above to the box below. Copy of selection should end up in the box once you drop it there.</p>
+<p><object type="image/svg+xml" data="helper-drop-selection-here-textArea.svg">SVG Image</object></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/040.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/040.xhtml
new file mode 100644
index 0000000000..6069563a91
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/040.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging textArea selection from dataURL image to another SVG image</title>
+</head>
+<body>
+<p><object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22250px%22%20height%3D%2250px%22%20viewBox%3D%220%200%20250%2050%22%3E%3CtextArea%20x%3D%2210%22%20y%3D%2210%22%20width%3D%22200%22%20height%3D%2250%22%20font-size%3D%2220%22%3ESelect%20me.%3C/textArea%3E%3C/svg%3E">SVG Image</object></p>
+<p>Drag selection above to the box below. Copy of selection should end up in the box once you drop it there.</p>
+<p><object type="image/svg+xml" data="helper-drop-selection-here-textArea.svg">SVG Image</object></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/041.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/041.xhtml
new file mode 100644
index 0000000000..326bafe639
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/041.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Dragging textArea selection between different SVG dataURL images</title>
+</head>
+<body>
+<p><object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22250px%22%20height%3D%2250px%22%20viewBox%3D%220%200%20250%2050%22%3E%3CtextArea%20x%3D%2210%22%20y%3D%2210%22%20width%3D%22200%22%20height%3D%2250%22%20font-size%3D%2220%22%3ESelect%20me.%3C/textArea%3E%3C/svg%3E">SVG Image</object></p>
+<p>Drag selection above to the box below. Copy of selection should end up in the box once you drop it there.</p>
+<p><object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.2%22%20width%3D%22500px%22%20height%3D%22300px%22%20viewBox%3D%220%200%20500%20300%22%3E%3CtextArea%20x%3D%2210%22%20y%3D%2210%22%20width%3D%22480%22%20height%3D%22280%22%20font-size%3D%2220%22%20editable%3D%22simple%22%20pointer-events%3D%22boundingBox%22%3EDrop%20selection%20here%3C/textArea%3E%3Crect%20x%3D%2210%22%20y%3D%2210%22%20width%3D%22480%22%20height%3D%22280%22%20stroke%3D%22black%22%20stroke-width%3D%221%22%20fill%3D%22none%22/%3E%3C/svg%3E">SVG Image</object></p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/042.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/042.xhtml
new file mode 100644
index 0000000000..8226e49593
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/042.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from downsized SVG image</title>
+<style type="text/css">
+object
+ {width:100px;
+ height:30px;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22200px%22%20height%3D%2250px%22%20viewBox%3D%220%200%20200%2050%22%3E%3Ctext%20x%3D%2210%22%20y%3D%2245%22%20font-size%3D%2230%22%20fill%3D%22navy%22%3ESelect%20me%3C/text%3E%3C/svg%3E">SVG Image</object></p>
+<p>Select the text above and drag selection to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/043.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/043.xhtml
new file mode 100644
index 0000000000..e11a4d82b2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/043.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop from enlarged SVG image</title>
+<style type="text/css">
+object
+ {width:300px;
+ height:75px;}
+textarea
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;}
+</style>
+</head>
+<body>
+<p><object type="image/svg+xml" data="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20version%3D%221.1%22%20width%3D%22200px%22%20height%3D%2250px%22%20viewBox%3D%220%200%20200%2050%22%3E%3Ctext%20x%3D%2210%22%20y%3D%2245%22%20font-size%3D%2230%22%20fill%3D%22navy%22%3ESelect%20me%3C/text%3E%3C/svg%3E">SVG Image</object></p>
+<p>Select the text above and drag selection to the textarea. Copy of selection should end up in the textarea once you drop it there.</p>
+<p><textarea placeholder="Drop selection here"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/044.svg b/testing/web-platform/tests/html/editing/dnd/svg/044.svg
new file mode 100644
index 0000000000..6dfcb4d663
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/044.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of RTL SVG text selection from text element to textArea</title>
+<text x="10" y="30" font-size="20" fill="navy">&#x202E;FAIL|SSAP&#x202C;</text>
+<text x="10" y="60" font-size="20">Select text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/045.svg b/testing/web-platform/tests/html/editing/dnd/svg/045.svg
new file mode 100644
index 0000000000..00dee4564c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/045.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of BiDi SVG text selection from text element to textArea</title>
+<text x="10" y="30" font-size="20" fill="navy">PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF</text>
+<text x="10" y="60" font-size="20">Select text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/046.svg b/testing/web-platform/tests/html/editing/dnd/svg/046.svg
new file mode 100644
index 0000000000..20c27a75d9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/046.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of BiDi SVG text selection from tspan elements to textArea</title>
+<text x="10" y="30" font-size="20" fill="navy">
+ <tspan>PASS|LIAF</tspan>
+ <tspan>&#x202E;FAIL|SSAP&#x202C;</tspan>
+ <tspan>PASS|LIAF</tspan>
+</text>
+<text x="10" y="60" font-size="20">Select text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/047.svg b/testing/web-platform/tests/html/editing/dnd/svg/047.svg
new file mode 100644
index 0000000000..e10a5c5665
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/047.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of RTL SVG text selection from tref element to textArea</title>
+<defs>
+ <text id="text">&#x202E;FAIL|SSAP&#x202C;</text>
+</defs>
+<text x="10" y="30" font-size="20" fill="navy"><tref xlink:href="#text"/></text>
+<text x="10" y="60" font-size="20">Select text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/048.svg b/testing/web-platform/tests/html/editing/dnd/svg/048.svg
new file mode 100644
index 0000000000..5a90f2144c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/048.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of BiDi SVG text selection from tref element to textArea</title>
+<defs>
+ <text id="text">PASS|LIAF &#x202E;FAIL|SSAP&#x202C; PASS|LIAF</text>
+</defs>
+<text x="10" y="30" font-size="20" fill="navy"><tref xlink:href="#text"/></text>
+<text x="10" y="60" font-size="20">Select text above and drag selection to</text>
+<text x="10" y="90" font-size="20">the box below. Content of selection should be</text>
+<text x="10" y="120" font-size="20">copies once it's dropped in the box.</text>
+<textArea x="10" y="130" width="480" height="360" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="130" width="480" height="360" stroke="black" stroke-width="1" fill="none"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/049.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/049.xhtml
new file mode 100644
index 0000000000..de81c7b313
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/049.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selecting text in SVG text element inside draggable container</title>
+</head>
+<body>
+<div draggable="true">
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="200px" height="50px" viewBox="0 0 200 50">
+ <text x="10" y="45" font-size="30" fill="navy" editable="simple">Select me</text>
+ </svg>
+</div>
+<p>You should be able to select text above</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/050.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/050.xhtml
new file mode 100644
index 0000000000..a8d73771b9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/050.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Selecting text in SVG textArea element inside draggable container</title>
+</head>
+<body>
+<div draggable="true">
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="100px" viewBox="0 0 500 100">
+ <textArea x="10" y="10" width="480" height="90" font-size="20" editable="simple">You should be able to select this text</textArea>
+ </svg>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/051.xhtml b/testing/web-platform/tests/html/editing/dnd/svg/051.xhtml
new file mode 100644
index 0000000000..36a15c5491
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/051.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag and drop of link from SVG fragment to XHTML</title>
+<style type="text/css">
+div
+ {width:300px;
+ height:100px;
+ margin-top:20px;
+ padding:10px;
+ color:white;
+ background-color:navy;}
+</style>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="200px" height="50px" viewBox="0 0 200 50">
+ <a xlink:href="data:text/plain,PASS">
+ <text x="10" y="45" font-size="30" fill="navy">Drag me</text>
+ </a>
+</svg>
+<p>Drag link to the blue box. You should see word PASS once you drop it there.</p>
+<div dropzone="copy string:text/uri-list" ondrop="document.querySelector('div').appendChild(document.createTextNode(event.dataTransfer.getData('text/uri-list').substring(16)))"/>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/052.svg b/testing/web-platform/tests/html/editing/dnd/svg/052.svg
new file mode 100644
index 0000000000..5a780cb4e9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/052.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of text link inside SVG</title>
+<a xlink:href="data:text/plain,PASS">
+ <text x="10" y="50" font-size="30" fill="navy">Drag me</text>
+</a>
+<textArea x="10" y="70" width="480" height="170" font-size="30">Drag link above and drop it in the gray box below. You should see word PASS once you drop it.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+var a = document.querySelector('a'), rect = document.querySelector('rect'), text = document.querySelector('textArea');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy'}
+,false);
+rect.addEventListener('dragenter',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('dragover',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('drop',
+function (event)
+ {text.firstChild.nodeValue = event.dataTransfer.getData('text/uri-list').substring(16,20)}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/053.svg b/testing/web-platform/tests/html/editing/dnd/svg/053.svg
new file mode 100644
index 0000000000..6c1c4e64eb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/053.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of circle link inside SVG</title>
+<a xlink:href="data:text/plain,PASS">
+ <circle cx="50" cy="50" r="50" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">Drag green circle above and drop it in the gray box below. Gray box should turn green once you drop it.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+var a = document.querySelector('a'), rect = document.querySelector('rect');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy'}
+,false);
+rect.addEventListener('dragenter',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('dragover',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('drop',
+function (event)
+ {if(event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,PASS')
+ {rect.setAttribute('fill','green');}
+ }
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/054.svg b/testing/web-platform/tests/html/editing/dnd/svg/054.svg
new file mode 100644
index 0000000000..b0daf03705
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/054.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of animated circle link inside SVG</title>
+<a xlink:href="data:text/plain,PASS">
+ <circle cx="50" cy="50" r="10" fill="green">
+ <animate attributeName="r" to="50" dur="10s" begin="0s" fill="freeze"/>
+ </circle>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">
+Drag green circle above and drop it in the gray box below.
+Size of feedback image should match size of circle
+and gray box should turn green.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+var a = document.querySelector('a'), rect = document.querySelector('rect');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy'}
+,false);
+rect.addEventListener('dragenter',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('dragover',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('drop',
+function (event)
+ {if(event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,PASS')
+ {rect.setAttribute('fill','green');}
+ }
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/055.svg b/testing/web-platform/tests/html/editing/dnd/svg/055.svg
new file mode 100644
index 0000000000..ee519baac8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/055.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Selection and drag and drop of link inside SVG</title>
+<a xlink:href="data:text/plain,PASS">
+ <rect x="10" y="10" width="80" height="80" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">Select part of this text. Once text is selected drag green square above and drop it in the gray box below. Gray box should turn green.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+var a = document.querySelector('a'), rect = document.querySelector('svg > rect');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy'}
+,false);
+rect.addEventListener('dragenter',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('dragover',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('drop',
+function (event)
+ {if(event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,PASS')
+ {rect.setAttribute('fill','green');}
+ }
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/056.svg b/testing/web-platform/tests/html/editing/dnd/svg/056.svg
new file mode 100644
index 0000000000..088b375a6e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/056.svg
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>dataTransfer.setData/getData during SVG link drag and drop</title>
+<a xlink:href="data:text/plain,PASS">
+ <polygon points="0,0 100,0 0,100" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="15">Drag green triangle above and drop it in the gray box below. Gray box should turn green.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,PASS', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+a = document.querySelector('a'), rect = document.querySelector('rect'), text = document.querySelector('textArea'), result = true;
+a.addEventListener('dragstart',
+ function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragstart) : FAIL');}
+ },false);
+a.addEventListener('drag',
+ function (event)
+ {for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during drag)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL')}
+ },false);
+rect.addEventListener('dragenter',
+ function (event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragenter)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragenter) : FAIL')}
+ },false);
+rect.addEventListener('dragover',
+ function (event)
+ {event.preventDefault();
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], 'FAIL');
+ if(event.dataTransfer.getData(dataTypes[i]))
+ {say('getData(' + dataTypes[i] + ') : FAIL (data store should not be readable during dragover)')}
+ }
+ if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (dragover) : FAIL');}
+ },false);
+rect.addEventListener('drop',
+ function (event)
+ {if(event.dataTransfer.items.length < dataTypes.length)
+ {say('items.length (drop) : FAIL');}
+ for(var i = 0; i != dataTypes.length; i++)
+ {if(event.dataTransfer.getData(dataTypes[i]) != data[i])
+ {say('getData(' + dataTypes[i] + ') : FAIL');}
+ }
+ rect.setAttribute('fill',result?'green':'red');
+ },false);
+function say(it)
+ {text.appendChild(document.createTextNode(it + '.'));
+ result = false;}
+]]>
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/057.svg b/testing/web-platform/tests/html/editing/dnd/svg/057.svg
new file mode 100644
index 0000000000..165a45f393
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/057.svg
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>dataTransfer.items during SVG link drag and drop</title>
+<a xlink:href="data:text/plain,PASS">
+ <polygon points="0,0 100,0 100,100" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="15">Drag green triangle above and drop it in the gray box below. Gray box should turn green.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+<![CDATA[
+var dataTypes = ['text/uri-list', 'text/plain', 'application/xml', 'application/xhtml+xml', 'application/mathml+xml', 'image/svg+xml', 'text/html', 'text/x-example'],
+data = ['data:text/plain,PASS', 'PASS', '<result>PASS</result>', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Data store item</title></head><body><p>PASS</p></body></html>', '<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1</mn></math>', '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" viewBox="0 0 100 50"><text x="0" y="40" font-size="40" fill="green">PASS</text></svg>', '<!DOCTYPE html><html><head><title>Data store item</title></head><body><p>PASS</p></body></html>', 'PASS'],
+a = document.querySelector('a'), rect = document.querySelector('rect'), text = document.querySelector('textArea'), e = 0, result = true;
+a.addEventListener('dragstart',
+ function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ for(var i = 0; i != dataTypes.length; i++)
+ {event.dataTransfer.setData(dataTypes[i], data[i]);}
+ for(var i = event.dataTransfer.items.length; i != 0; i--)
+ {if(dataTypes.indexOf(event.dataTransfer.items[i-1].type) == -1)
+ {delete event.dataTransfer.items[i-1]}
+ }
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragstart) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragstart): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragstart): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ },false);
+a.addEventListener('drag',
+ function (event)
+ {event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrag) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrag): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrag): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ },false);
+rect.addEventListener('dragenter',
+ function (event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragenter) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragenter): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragenter): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to drag event handler)')}
+ }
+ );
+ }
+ },false);
+rect.addEventListener('dragover',
+ function (event)
+ {event.preventDefault();
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondragover) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondragover): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondragover): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should not reveal data to dragover event handler)')}
+ }
+ );
+ }
+ },false);
+rect.addEventListener('drop',
+ function (event)
+ {e = 0;
+ event.dataTransfer.items.clear();
+ if(event.dataTransfer.items.length != dataTypes.length)
+ {say('items.length (ondrop) : FAIL (items.length should be' + dataTypes.length + ')')}
+ for(var i = 0; i != event.dataTransfer.items.length; i++)
+ {delete event.dataTransfer.items[i];
+ if(event.dataTransfer.items[i].kind != 'string')
+ {say('Item kind (ondrop): FAIL (items[' + i + '].kind should be string)')}
+ if(event.dataTransfer.items[i].type != dataTypes[i])
+ {say('Item type (ondrop): FAIL (items[' + i + '].type should be' + dataTypes[i] + ')')}
+ event.dataTransfer.items[i].getAsString(
+ function ()
+ {if(arguments[0] != data[e++])
+ {say('getAsString : FAIL (items[' + i + '].getAsString should pass' + data[e] + ')')}
+ }
+ );
+ }
+ rect.setAttribute('fill',result?'green':'red');
+ },false);
+function say(it)
+ {text.appendChild(document.createTextNode(it + '.'));
+ result = false;}
+]]>
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/058.svg b/testing/web-platform/tests/html/editing/dnd/svg/058.svg
new file mode 100644
index 0000000000..d2bb91da8d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/058.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>SVG link drag and drop: allowed effects 'copy','move','link' and 'none'</title>
+<a xlink:href="data:text/plain,1">
+ <circle cx="50" cy="50" r="40" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">You should be able to drag green circle and drop it onto any of the boxes below. Choosen box should turn green once circle is dropped on it.</textArea>
+<rect x="10" y="300" width="100" height="100" fill="silver"/>
+<rect x="120" y="300" width="100" height="100" fill="gray"/>
+<rect x="230" y="300" width="100" height="100" fill="gray"/>
+<rect x="340" y="300" width="100" height="100" fill="black"/>
+<script type="application/ecmascript">
+var a = document.querySelector('a'), rect = document.querySelectorAll('rect'),
+effects = ['copy','move','link','all'], e = 0;
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = effects[e]}
+,false);
+for(var i = 0; i != rect.length; i++)
+ {rect[i].addEventListener('dragenter',
+ function (event)
+ {event.preventDefault();
+ event.dataTransfer.effectAllowed = effects[e];}
+ ,false);
+ rect[i].addEventListener('dragover',
+ function (event)
+ {event.preventDefault()}
+ ,false);
+ rect[i].addEventListener('drop',
+ function (event)
+ {if(event.dataTransfer.dropEffect == effects[e] &amp;&amp; event.dataTransfer.effectAllowed == effects[e] &amp;&amp; i != 3)
+ {event.target.setAttribute('fill','green');}
+ e = (e+1)%3;}
+ ,false);}
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/059-1.svg b/testing/web-platform/tests/html/editing/dnd/svg/059-1.svg
new file mode 100644
index 0000000000..492e9511a1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/059-1.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>SVG link drag and drop: helper file</title>
+<textArea x="10" y="10" width="480" height="200" font-size="30">Drop circle onto gray box below. Box should turn green once circle is dropped.</textArea>
+<rect x="10" y="220" width="480" height="270" fill="gray"/>
+<script type="application/ecmascript">
+var rect = document.querySelector('rect');
+rect.addEventListener('dragenter',
+function (event)
+ {event.preventDefault();}
+,false);
+rect.addEventListener('dragover',
+function (event)
+ {event.preventDefault();}
+,false);
+rect.addEventListener('drop',
+function (event)
+ {rect.setAttribute('fill',(event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'green':'red');}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/059.svg b/testing/web-platform/tests/html/editing/dnd/svg/059.svg
new file mode 100644
index 0000000000..3cc7815c69
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/059.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>SVG link cross page drag and drop</title>
+<a xlink:href="data:text/plain,1">
+ <circle cx="50" cy="50" r="40" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">Drag circle above. You should be redirected to the new page and be able to drop it there.</textArea>
+<script type="application/ecmascript">
+document.querySelector('a').addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ window.location = '059-1.svg'}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/060-1.svg b/testing/web-platform/tests/html/editing/dnd/svg/060-1.svg
new file mode 100644
index 0000000000..3dc6689e59
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/060-1.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>SVG link drag and drop: helper file</title>
+<textArea x="10" y="10" width="480" height="200" font-size="30">Drag circle over arrow below but don't drop it yet. You should be returned back to start page.</textArea>
+<polygon points="10,400 100,300 100,350 480,350 480,450 100,450 100,500 10,400" fill="navy"/>
+<script type="application/ecmascript">
+document.querySelector('polygon').addEventListener('dragenter',
+function (event)
+ {window.location = '060.svg'}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/060.svg b/testing/web-platform/tests/html/editing/dnd/svg/060.svg
new file mode 100644
index 0000000000..fa7159abad
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/060.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>SVG link drag and drop and history navigation roundtrip</title>
+<a xlink:href="data:text/plain,1">
+ <circle cx="50" cy="50" r="40" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">Drag circle above. You will be redirected to new page. When you return back drop circle on itself. You should see word PASS once you drop it.</textArea>
+<script type="application/ecmascript">
+var a = document.querySelector('a');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ window.location = '060-1.svg'}
+,false);
+a.addEventListener('dragenter',
+function (event)
+ {event.preventDefault();}
+,false);
+a.addEventListener('dragover',
+function (event)
+ {event.preventDefault();}
+,false);
+a.addEventListener('drop',
+function (event)
+ {document.querySelector('textArea').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL';}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/061.svg b/testing/web-platform/tests/html/editing/dnd/svg/061.svg
new file mode 100644
index 0000000000..20ed8a8360
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/061.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>SVG link drag and drop roundtrip</title>
+<a xlink:href="data:text/plain,1">
+ <circle cx="50" cy="50" r="40" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">Drag circle outside browser window and then drag it back and drop on itself. You should see word PASS once you drop it.</textArea>
+<script type="application/ecmascript">
+var a = document.querySelector('a');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';}
+,false);
+a.addEventListener('dragenter',
+function (event)
+ {event.preventDefault();}
+,false);
+a.addEventListener('dragover',
+function (event)
+ {event.preventDefault();}
+,false);
+a.addEventListener('drop',
+function (event)
+ {document.querySelector('textArea').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL';}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/062.svg b/testing/web-platform/tests/html/editing/dnd/svg/062.svg
new file mode 100644
index 0000000000..2cbf96e0ea
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/062.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Setting drag image during drag and drop of SVG link</title>
+<a xlink:href="data:text/plain,1">
+ <circle cx="60" cy="60" r="50" fill="gray"/>
+</a>
+<textArea x="10" y="200" width="480" height="200" font-size="30">Try to drag circle. Drag feedback should look like green rectangle.</textArea>
+<rect x="300" y="10" width="100" height="100" fill="green"/>
+<script type="application/ecmascript">
+var a = document.querySelector('a'), rect = document.querySelector('rect');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelector('rect'), 50, 50);}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/063.svg b/testing/web-platform/tests/html/editing/dnd/svg/063.svg
new file mode 100644
index 0000000000..3f570b6adb
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/063.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Adding element to datastore while dragging SVG links</title>
+<a xlink:href="data:text/plain,1">
+ <rect x="10" y="10" width="100" height="100" fill="green"/>
+</a>
+<a xlink:href="data:text/plain,2">
+ <rect x="300" y="10" width="100" height="100" fill="teal"/>
+</a>
+<textArea x="10" y="200" width="480" height="200" font-size="30">Try to drag one of boxes above. Drag feedback should include both boxes.</textArea>
+<script type="application/ecmascript">
+var a = document.querySelectorAll('a');
+a[0].addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelectorAll('rect')[1]);}
+,false);
+a[1].addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.addElement(document.querySelectorAll('rect')[0]);}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/064.svg b/testing/web-platform/tests/html/editing/dnd/svg/064.svg
new file mode 100644
index 0000000000..5369de9f44
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/064.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of overlapping SVG links</title>
+<a xlink:href="data:text/plain,1">
+ <rect x="10" y="10" width="100" height="100" fill="silver"/>
+</a>
+<a xlink:href="data:text/plain,2">
+ <rect x="10" y="10" width="100" height="100" fill="gray"/>
+</a>
+ <rect x="160" y="10" width="100" height="100" fill="green"/>
+ <rect x="310" y="10" width="100" height="100" fill="maroon"/>
+<textArea x="10" y="200" width="480" height="200" font-size="30">Try to drag gray boxe above. Drag feedback should be green, not red.</textArea>
+<script type="application/ecmascript">
+var a = document.querySelectorAll('a');
+a[0].addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelectorAll('rect')[3], 50, 50);}
+,false);
+a[1].addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ event.dataTransfer.setDragImage(document.querySelectorAll('rect')[2], 50, 50);}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/065.svg b/testing/web-platform/tests/html/editing/dnd/svg/065.svg
new file mode 100644
index 0000000000..89e158f2f9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/065.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Reload during SVG link drag and drop roundtrip</title>
+<a xlink:href="data:text/plain,1">
+ <circle cx="50" cy="50" r="40" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">Drag circle around page and then drag it back and drop on itself. You should see word PASS once you drop it.</textArea>
+<script type="application/ecmascript">
+var a = document.querySelector('a');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ window.location.reload()}
+,false);
+a.addEventListener('dragenter',
+function (event)
+ {event.preventDefault();}
+,false);
+a.addEventListener('dragover',
+function (event)
+ {event.preventDefault();}
+,false);
+a.addEventListener('drop',
+function (event)
+ {document.querySelector('textArea').firstChild.nodeValue = (event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')?'PASS':'FAIL';}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/066.svg b/testing/web-platform/tests/html/editing/dnd/svg/066.svg
new file mode 100644
index 0000000000..f5066ac357
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/066.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Removing dragged element during drag and drop of SVG link</title>
+<a xlink:href="data:text/plain,1">
+ <circle cx="50" cy="50" r="50" fill="green"/>
+</a>
+<textArea x="10" y="100" width="480" height="200" font-size="30">Drag green circle above and drop it in the gray box below. Gray box should turn green once you drop it.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+var a = document.querySelector('a'), rect = document.querySelector('rect');
+a.addEventListener('dragstart',
+function (event)
+ {event.dataTransfer.effectAllowed = 'copy';
+ document.documentElement.removeChild(a);}
+,false);
+rect.addEventListener('dragenter',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('dragover',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('drop',
+function (event)
+ {if(event.dataTransfer.getData('text/uri-list').replace(/\r\n$/,'') == 'data:text/plain,1')
+ {rect.setAttribute('fill','green');}
+ }
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/067.svg b/testing/web-platform/tests/html/editing/dnd/svg/067.svg
new file mode 100644
index 0000000000..9156a0ff2e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/067.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px" viewBox="0 0 500 500">
+<title>Drag and drop of SVG links</title>
+<a xlink:href="data:text/plain,olive">
+ <circle cx="50" cy="50" r="50" fill="olive"/>
+</a>
+<a xlink:href="data:text/plain,green">
+ <circle cx="200" cy="50" r="50" fill="green"/>
+</a>
+<a xlink:href="data:text/plain,teal">
+ <circle cx="350" cy="50" r="50" fill="teal"/>
+</a>
+<textArea x="10" y="120" width="480" height="200" font-size="30">Drag one of green circles above and drop it in the gray box below. Gray box should turn green.</textArea>
+<rect x="10" y="300" width="480" height="190" fill="gray"/>
+<script type="application/ecmascript">
+var a = document.querySelectorAll('a'), rect = document.querySelector('rect');
+for(var i = 0; i != a.length; i++)
+ {a[i].addEventListener('dragstart',
+ function (event)
+ {event.dataTransfer.effectAllowed = 'copy';}
+ ,false);}
+rect.addEventListener('dragenter',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('dragover',
+function (event)
+ {event.preventDefault()}
+,false);
+rect.addEventListener('drop',
+function (event)
+ {rect.setAttribute('fill',event.dataTransfer.getData('text/uri-list').substr(16).replace(/\r\n$/,''));}
+,false);
+</script>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/dnd/svg/helper-drop-selection-here-textArea.svg b/testing/web-platform/tests/html/editing/dnd/svg/helper-drop-selection-here-textArea.svg
new file mode 100644
index 0000000000..783c164eb2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/svg/helper-drop-selection-here-textArea.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="500px" height="300px" viewBox="0 0 500 300">
+<textArea x="10" y="10" width="480" height="280" font-size="20" editable="simple" pointer-events="boundingBox">Drop selection here</textArea>
+<rect x="10" y="10" width="480" height="280" stroke="black" stroke-width="1" fill="none"/>
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/dnd/synthetic/001.html b/testing/web-platform/tests/html/editing/dnd/synthetic/001.html
new file mode 100644
index 0000000000..c0bf8a5776
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/synthetic/001.html
@@ -0,0 +1,115 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Synthetic drag events</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script type="text/javascript">
+test(function() {
+ assert_own_property(window,'DragEvent');
+}, 'window.DragEvent should be exposed' );
+test(function() {
+ var evt = new DragEvent('dragstart');
+ assert_false( !!evt.initDragEvent, 'initDragEvent' );
+ assert_true( !!evt.initMouseEvent, 'initMouseEvent' );
+ assert_true( !!evt.initUIEvent, 'initUIEvent' );
+ assert_true( !!evt.initEvent, 'initEvent' );
+}, 'DragEvent should have all of the inherited init*Event methods' );
+
+//cannot test non-synthetic dataTransfer objects as the param here because that needs a real DragEvent to create a proper one with global storage
+//will be tested in another file
+test(function() {
+ var evt = new DragEvent('dragstart');
+ evt.initMouseEvent('dragstart', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 1, document.body);
+}, 'initMouseEvent should not throw' );
+test(function() {
+ var evt = new DragEvent('dragstart');
+ evt.initUIEvent('dragstart', true, true, window, 1);
+}, 'initUIEvent should not throw' );
+test(function() {
+ var evt = new DragEvent('dragstart');
+ evt.initEvent('dragstart', true, true);
+}, 'initEvent should not throw' );
+
+test(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:null}), div = document.createElement('div'), ranlistener = false;
+ div.ondragstart = function () { ranlistener = true; };
+ div.dispatchEvent(evt);
+ assert_true(ranlistener);
+}, 'DragEvent constructor with null as the dataTransfer parameter should be able to fire the event' );
+test(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:undefined}), div = document.createElement('div'), ranlistener = false;
+ div.ondragstart = function () { ranlistener = true; };
+ div.dispatchEvent(evt);
+ assert_true(ranlistener);
+}, 'DragEvent constructor with undefined as the dataTransfer parameter should be able to fire the event' );
+test(function() {
+ assert_throws_js(TypeError, function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:{}});
+ });
+}, 'DragEvent constructor with custom object as the dataTransfer parameter should throw TypeError' );
+test(function() {
+ var evt = new DragEvent('dragstart'), div = document.createElement('div'), ranlistener = false;
+ div.ondragstart = function () { ranlistener = true; };
+ evt.initMouseEvent('dragstart', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 1, document.body);
+ div.dispatchEvent(evt);
+ assert_true(ranlistener);
+}, 'initMouseEvent should be able to fire the event' );
+test(function() {
+ var evt = new DragEvent('dragstart'), div = document.createElement('div'), ranlistener = false;
+ div.ondragstart = function () { ranlistener = true; };
+ evt.initUIEvent('dragstart', true, true, window, 1);
+ div.dispatchEvent(evt);
+ assert_true(ranlistener);
+}, 'initUIEvent should be able to fire the event' );
+test(function() {
+ var evt = new DragEvent('dragstart'), div = document.createElement('div'), ranlistener = false;
+ div.ondragstart = function () { ranlistener = true; };
+ evt.initEvent('dragstart', true, true);
+ div.dispatchEvent(evt);
+ assert_true(ranlistener);
+}, 'initEvent should be able to fire the event' );
+
+test(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:null}), div = document.createElement('div'), dTrans = 'fail';
+ div.ondragstart = function (e) { dTrans = e.dataTransfer };
+ div.dispatchEvent(evt);
+ assert_equals(dTrans,null);
+}, 'DragEvent constructor with null as the dataTransfer parameter should give null as the dataTransfer' );
+test(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:undefined}), div = document.createElement('div'), dTrans = 'fail';
+ div.ondragstart = function (e) { dTrans = e.dataTransfer };
+ div.dispatchEvent(evt);
+ assert_equals(dTrans,null);
+}, 'DragEvent constructor with undefined as the dataTransfer parameter should give null as the dataTransfer' );
+test(function() {
+ var evt = new DragEvent('dragstart'), div = document.createElement('div'), dTrans = 'fail';
+ div.ondragstart = function (e) { dTrans = e.dataTransfer };
+ evt.initMouseEvent('dragstart', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 1, document.body);
+ div.dispatchEvent(evt);
+ assert_equals(dTrans,null);
+}, 'initMouseEvent should give null as the dataTransfer' );
+test(function() {
+ var evt = new DragEvent('dragstart'), div = document.createElement('div'), dTrans = 'fail';
+ div.ondragstart = function (e) { dTrans = e.dataTransfer };
+ evt.initUIEvent('dragstart', true, true, window, 1);
+ div.dispatchEvent(evt);
+ assert_equals(dTrans,null);
+}, 'initUIEvent should give null as the dataTransfer' );
+test(function() {
+ var evt = new DragEvent('dragstart'), div = document.createElement('div'), dTrans = 'fail';
+ div.ondragstart = function (e) { dTrans = e.dataTransfer };
+ evt.initEvent('dragstart', true, true);
+ div.dispatchEvent(evt);
+ assert_equals(dTrans,null);
+}, 'initEvent should give null as the dataTransfer' );
+
+//cannot test that synthetic event does not use the same data store as non-synthetic event because that needs a real DragEvent to create a proper one with global storage
+//will be tested in another file
+ </script>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/synthetic/005-manual.html b/testing/web-platform/tests/html/editing/dnd/synthetic/005-manual.html
new file mode 100644
index 0000000000..4e0b00dab2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/synthetic/005-manual.html
@@ -0,0 +1,340 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Synthetic events with real data store must inherit protection status from real events</title>
+ <style type="text/css">
+blockquote { height: 100px; width: 100px; background: orange; margin: 0; padding: 0; float: left; }
+blockquote + blockquote { background: blue; }
+blockquote + blockquote + blockquote { background: fuchsia; }
+blockquote + div { clear: left; }
+ </style>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+setup(function () {},{explicit_done:true,explicit_timeout:true});
+window.onload = function () {
+
+ var orange = document.getElementsByTagName('blockquote')[0],
+ blue = document.getElementsByTagName('blockquote')[1],
+ fuchsia = document.getElementsByTagName('blockquote')[2],
+ evtdone = {};
+
+ orange.ondragstart = function (e) {
+ evtdone[e.type] = true;
+ e.dataTransfer.effectAllowed = 'copy';
+
+ var t = async_test(e.type+' should share its data with the synthetic event');
+ blue.ondragstart = function (e) {
+ t.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart real data', 'step 1' );
+ e.dataTransfer.setData('text','dragstart-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-dragstart synthetic data', 'step 2' );
+ });
+ };
+ t.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ e.dataTransfer.setData('text','dragstart real data'); //changing in between steps, just to make sure it uses the underlying data store, not a temporary clone
+ blue.dispatchEvent(evt);
+ });
+ t.done();
+
+ test(function() {
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-dragstart synthetic data' );
+ }, e.type+' should see the data from the synthetic event' );
+
+ var t2 = async_test(e.type+' should share its protection status with the synthetic event');
+ blue.ondrag = function (e) {
+ t2.step(function() {
+ e.dataTransfer.setData('text','dragstart-drag synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag synthetic data' );
+ });
+ };
+ t2.step(function() {
+ var evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t2.done();
+
+ var t3 = async_test(e.type+' should share its protection status with the nested synthetic event');
+ blue.ondrag = function (e) {
+ blue.ondragend = function (e) {
+ t3.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag synthetic data', 'step1' );
+ e.dataTransfer.setData('text','dragstart-drag-dragend synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data', 'step2' );
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('dragend', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t3.done();
+
+ test(function() {
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data' );
+ }, e.type+' should see the data from the nested synthetic event' );
+ };
+
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ orange.ondrag = blue.ondragleave = function (e) {
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ var evtype = e.type;
+
+ var t = async_test(e.type+' should share its data with the synthetic event');
+ blue.ondragstart = function (e) {
+ t.step(function() {
+ assert_true( e.dataTransfer.items.length > 0, 'items.length' );
+ });
+ };
+ t.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t.done();
+
+ var t2 = async_test(e.type+' should share its protection status with the synthetic event');
+ blue.ondragstart = function (e) {
+ t2.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 1' );
+ e.dataTransfer.setData('text',evtype+'-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 2' );
+ });
+ };
+ t2.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t2.done();
+
+ test(function() {
+ assert_equals( e.dataTransfer.getData('text'), '' );
+ }, e.type+' protection status should not be modified by the synthetic event' );
+
+ var t3 = async_test(e.type+' should share its protection status with the nested synthetic event');
+ blue.ondragstart = function (e) {
+ var div = document.createElement('div');
+ div.ondragstart = function (e) {
+ t3.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step1' );
+ e.dataTransfer.setData('text',evtype+'dragstart-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step2' );
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('dragend', {dataTransfer:e.dataTransfer});
+ div.dispatchEvent(evt);
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t3.done();
+ };
+
+ fuchsia.ondragenter = fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ var evtype = e.type;
+
+ var t = async_test(e.type+' should share its data with the synthetic event');
+ blue.ondragstart = function (e) {
+ t.step(function() {
+ assert_true( e.dataTransfer.items.length > 0, 'items.length' );
+ });
+ };
+ t.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t.done();
+
+ var t2 = async_test(e.type+' should share its protection status with the synthetic event');
+ blue.ondragstart = function (e) {
+ t2.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 1' );
+ e.dataTransfer.setData('text',evtype+'-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 2' );
+ });
+ };
+ t2.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t2.done();
+
+ test(function() {
+ assert_equals( e.dataTransfer.getData('text'), '' );
+ }, e.type+' protection status should not be modified by the synthetic event' );
+
+ var t3 = async_test(e.type+' should share its protection status with the nested synthetic event');
+ blue.ondragstart = function (e) {
+ var div = document.createElement('div');
+ div.ondragstart = function (e) {
+ t3.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step1' );
+ e.dataTransfer.setData('text',evtype+'dragstart-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step2' );
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('dragend', {dataTransfer:e.dataTransfer});
+ div.dispatchEvent(evt);
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t3.done();
+ };
+
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ var evtype = e.type;
+
+ var t = async_test(e.type+' should share its data with the synthetic event');
+ blue.ondragstart = function (e) {
+ t.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data' );
+ });
+ };
+ t.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t.done();
+
+ var t2 = async_test(e.type+' should share its protection status with the synthetic event');
+ blue.ondragstart = function (e) {
+ t2.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data', 'step 1' );
+ e.dataTransfer.setData('text',evtype+'-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data', 'step 2' );
+ });
+ };
+ t2.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t2.done();
+
+ test(function() {
+ e.dataTransfer.setData('text','drop synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data' );
+ }, e.type+' protection status should not be modified by the synthetic event' );
+
+ var t3 = async_test(e.type+' should share its protection status with the nested synthetic event');
+ blue.ondragstart = function (e) {
+ var div = document.createElement('div');
+ div.ondragstart = function (e) {
+ t3.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data', 'step 1' );
+ e.dataTransfer.setData('text',evtype+'dragstart-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), 'dragstart-drag-dragend synthetic data', 'step 2' );
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('dragend', {dataTransfer:e.dataTransfer});
+ div.dispatchEvent(evt);
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t3.done();
+ };
+
+ orange.ondragend = function (e) {
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ var evtype = e.type;
+
+ var t = async_test(e.type+' should share its data with the synthetic event');
+ blue.ondragstart = function (e) {
+ t.step(function() {
+ assert_true( e.dataTransfer.items.length > 0, 'items.length' );
+ });
+ };
+ t.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t.done();
+
+ var t2 = async_test(e.type+' should share its protection status with the synthetic event');
+ blue.ondragstart = function (e) {
+ t2.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 1' );
+ e.dataTransfer.setData('text',evtype+'-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 2' );
+ });
+ };
+ t2.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t2.done();
+
+ test(function() {
+ assert_equals( e.dataTransfer.getData('text'), '' );
+ }, e.type+' protection status should not be modified by the synthetic event' );
+
+ var t3 = async_test(e.type+' should share its protection status with the nested synthetic event');
+ blue.ondragstart = function (e) {
+ var div = document.createElement('div');
+ div.ondragstart = function (e) {
+ t3.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step1' );
+ e.dataTransfer.setData('text',evtype+'dragstart-dragstart synthetic data');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step2' );
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('dragend', {dataTransfer:e.dataTransfer});
+ div.dispatchEvent(evt);
+ });
+ };
+ t3.step(function() {
+ var evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t3.done();
+
+ test(function() {
+ var failtxt = '- Reload and try again';
+ assert_true( evtdone.dragstart, 'dragstart event was not tested'+failtxt );
+ assert_true( evtdone.drag, 'drag event was not tested'+failtxt );
+ assert_true( evtdone.dragenter, 'dragenter event was not tested'+failtxt );
+ assert_true( evtdone.dragleave, 'dragleave event was not tested'+failtxt );
+ assert_true( evtdone.dragover, 'dragover event was not tested'+failtxt );
+ assert_true( evtdone.drop, 'drop event was not tested'+failtxt );
+ assert_true( evtdone.dragend, 'dragend event was not tested'+failtxt );
+ }, 'all event types must now have been tested' );
+ done();
+ };
+
+};
+ </script>
+ </head>
+ <body>
+ <p>Drag the orange square over the blue square then the fuchsia square, then release it.</p>
+ <blockquote draggable="true"></blockquote>
+ <blockquote></blockquote>
+ <blockquote></blockquote>
+ <div id="log"></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/synthetic/006-manual.html b/testing/web-platform/tests/html/editing/dnd/synthetic/006-manual.html
new file mode 100644
index 0000000000..e7d1677b14
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/synthetic/006-manual.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Synthetic events using real dataTransfer in new thread</title>
+ <style type="text/css">
+blockquote { height: 100px; width: 100px; background: orange; margin: 0; padding: 0; float: left; }
+blockquote + blockquote { background: blue; }
+blockquote + blockquote + blockquote { background: fuchsia; }
+blockquote + div { clear: left; }
+ </style>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+setup(function () {},{explicit_done:true,explicit_timeout:true});
+window.onload = function () {
+
+ var orange = document.getElementsByTagName('blockquote')[0],
+ blue = document.getElementsByTagName('blockquote')[1],
+ fuchsia = document.getElementsByTagName('blockquote')[2],
+ evtdone = {};
+
+ orange.ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dragstart real data');
+ var dataTransfer = e.dataTransfer;
+ setTimeout(function () {
+ var t = async_test('new thread should see data store in protected mode after dragstart');
+ blue.ondragstart = function (e) {
+ t.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 1' );
+ e.dataTransfer.setData('text','new thread after dragstart');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 2' );
+ });
+ };
+ t.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t.done();
+ },0);
+ };
+
+ fuchsia.ondragenter = fuchsia.ondragover = function (e) {
+ e.preventDefault();
+ };
+
+ fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ var dataTransfer = e.dataTransfer;
+ setTimeout(function () {
+ var t = async_test('new thread should see data store in protected mode after drop');
+ blue.ondragstart = function (e) {
+ t.step(function() {
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 1' );
+ e.dataTransfer.setData('text','new thread after dragstart');
+ assert_equals( e.dataTransfer.getData('text'), '', 'step 2' );
+ });
+ };
+ t.step(function() {
+ var evt = new DragEvent('dragstart', {dataTransfer:dataTransfer});
+ blue.dispatchEvent(evt);
+ });
+ t.done();
+ done();
+ },0);
+ };
+
+};
+ </script>
+ </head>
+ <body>
+ <p>Drag the orange square over the blue square then the fuchsia square, then release it.</p>
+ <blockquote draggable="true"></blockquote>
+ <blockquote></blockquote>
+ <blockquote></blockquote>
+ <div id="log"></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/001-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/001-manual.html
new file mode 100644
index 0000000000..ff8572e937
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/001-manual.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<html>
+ <head>
+ <title>allowTargetOrigin syntax</title>
+ <style type="text/css">
+blockquote { height: 100px; width: 100px; background: orange; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+setup(function () {},{explicit_done:true});
+window.onload = function () {
+ document.getElementsByTagName('blockquote')[0].ondragstart = function (e) {
+ test(function() {
+ assert_true( !!e.dataTransfer.allowTargetOrigin );
+ }, 'allowTargetOrigin should be supported' );
+ test(function() {
+ assert_throws_js( TypeError, function () { e.dataTransfer.allowTargetOrigin(); } );
+ }, 'no parameter should throw TypeError' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin(''); } );
+ }, 'empty string should be an invalid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('*');
+ }, '* should be a valid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('/');
+ }, '/ should be a valid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('/foo'); } );
+ }, '/foo should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('foo'); } );
+ }, 'foo should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('//foo'); } );
+ }, '//foo should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('http://'); } );
+ }, 'http:// should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('http://*'); } );
+ }, 'http://* should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('http://foo*'); } );
+ }, 'http://foo* should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('http://foo.*'); } );
+ }, 'http://foo.* should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('http://*.foo'); } );
+ }, 'http://*.foo should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('http://foo:bar'); } );
+ }, 'http://foo:bar should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('http://foo:bar@'); } );
+ }, 'http://foo:bar@ should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('file:'); } );
+ }, 'file: should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('file://'); } );
+ }, 'file:// should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('data:'); } );
+ }, 'data: should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('data:text/html'); } );
+ }, 'data:text/html should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('file://localhost/'); } );
+ }, 'file://localhost/ should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('file:///'); } );
+ }, 'file:/// should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('data:text/html,'); } );
+ }, 'data:text/html, should be an invalid URL' );
+ test(function() {
+ assert_throws_dom( 'SYNTAX_ERR', function () { e.dataTransfer.allowTargetOrigin('javascript:'); } );
+ }, 'javascript: should be an invalid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('http://foo');
+ }, 'http://foo should be a valid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('http://foo.bar');
+ }, 'http://foo.bar should be a valid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('http://foo/bar');
+ }, 'http://foo/bar should be a valid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('http://foo:123');
+ }, 'http://foo:123 should be a valid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('http://foo:bar@baz');
+ }, 'http://foo:bar@baz should be a valid URL' );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('http://foo:bar@baz:123/qux');
+ }, 'http://foo:bar@baz:123/qux should be a valid URL' );
+ done();
+ };
+};
+ </script>
+ </head>
+ <body>
+ <blockquote draggable="true"></blockquote>
+ <div id="log">Drag the orange square above until the drag placeholder appears, then release it.</div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/002-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/002-manual.html
new file mode 100644
index 0000000000..d7e6c83a2a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/002-manual.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<html>
+ <head>
+ <title>allowTargetOrigin events</title>
+ <style type="text/css">
+blockquote { height: 100px; width: 100px; background: orange; margin: 0; padding: 0; float: left; }
+blockquote + blockquote { background: blue; }
+blockquote + blockquote + blockquote { background: fuchsia; }
+blockquote + div { clear: left; }
+ </style>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+setup(function () {},{explicit_done:true});
+window.onload = function () {
+ var orange = document.getElementsByTagName('blockquote')[0],
+ blue = document.getElementsByTagName('blockquote')[1],
+ fuchsia = document.getElementsByTagName('blockquote')[2],
+ evtdone = {};
+ orange.ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_true( !!e.dataTransfer.allowTargetOrigin );
+ }, 'allowTargetOrigin should exist in '+e.type );
+ test(function() {
+ e.dataTransfer.allowTargetOrigin('*');
+ }, 'allowTargetOrigin should work in '+e.type );
+ };
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ orange.ondrag = blue.ondragleave = function (e) {
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_true( !!e.dataTransfer.allowTargetOrigin );
+ }, 'allowTargetOrigin should exist in '+e.type );
+ test(function() {
+ assert_throws_dom( 'SECURITY_ERR', function () { e.dataTransfer.allowTargetOrigin('*'); } );
+ }, 'allowTargetOrigin should throw a SECURITY_ERR in '+e.type );
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_true( !!e.dataTransfer.allowTargetOrigin );
+ }, 'allowTargetOrigin should exist in '+e.type );
+ test(function() {
+ assert_throws_dom( 'SECURITY_ERR', function () { e.dataTransfer.allowTargetOrigin('*'); } );
+ }, 'allowTargetOrigin should throw a SECURITY_ERR in '+e.type );
+ };
+ orange.ondragend = function (e) {
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_true( !!e.dataTransfer.allowTargetOrigin );
+ }, 'allowTargetOrigin should exist in '+e.type );
+ test(function() {
+ assert_throws_dom( 'SECURITY_ERR', function () { e.dataTransfer.allowTargetOrigin('*'); } );
+ }, 'allowTargetOrigin should throw a SECURITY_ERR in '+e.type );
+ test(function() {
+ var failtxt = '- Reload and try again';
+ assert_true( evtdone.dragstart, 'dragstart event was not tested'+failtxt );
+ assert_true( evtdone.drag, 'drag event was not tested'+failtxt );
+ assert_true( evtdone.dragenter, 'dragenter event was not tested'+failtxt );
+ assert_true( evtdone.dragleave, 'dragleave event was not tested'+failtxt );
+ assert_true( evtdone.dragover, 'dragover event was not tested'+failtxt );
+ assert_true( evtdone.drop, 'drop event was not tested'+failtxt );
+ assert_true( evtdone.dragend, 'dragend event was not tested'+failtxt );
+ }, 'all event types must now have been tested' );
+ done();
+ };
+};
+ </script>
+ </head>
+ <body>
+ <blockquote draggable="true"></blockquote>
+ <blockquote></blockquote>
+ <blockquote></blockquote>
+ <div id="log">Drag the orange square over the blue square then the fuchsia square, then release it.</div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/003-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/003-manual.html
new file mode 100644
index 0000000000..febc2b0da6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/003-manual.html
@@ -0,0 +1,95 @@
+<!doctype html>
+<html>
+ <head>
+ <title>allowTargetOrigin valid syntax</title>
+ <style type="text/css">
+div { float: left; height: 100px; width: 100px; margin-right: 10px; background: orange; }
+iframe { height: 100px; width: 100px; border: none; }
+.note { float: right; color: silver; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+function addNote(el,str) {
+ var par = document.createElement(el);
+ par.textContent = str;
+ document.body.appendChild(par);
+}
+function testFrame(text,frameorigin,framepath) {
+ var persist = arguments;
+ addNote('p',(done++)+'. '+text);
+ var div = document.createElement('div');
+ var frame = document.createElement('iframe');
+ frame.src = frameorigin+framepath;
+ div.draggable = true;
+ div.ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ for( var i = 3; i < persist.length; i++ ) {
+ e.dataTransfer.allowTargetOrigin(persist[i]);
+ }
+ };
+ var par = document.createElement('p');
+ par.className = 'note';
+ par.appendChild(document.createTextNode('Target: '+frameorigin));
+ par.appendChild(document.createElement('br'));
+ par.appendChild(document.createTextNode('Allowing: '+([]).slice.call(persist,3).join(' and ')));
+ if( framepath.match(/\?domain\b/) ) {
+ par.appendChild(document.createElement('br'));
+ par.appendChild(document.createTextNode('document.domain set to parent domain'));
+ }
+ document.body.appendChild(par);
+ document.body.appendChild(div);
+ document.body.appendChild(frame);
+}
+var done = 1;
+window.onload = function () {
+ var allowText = 'Drag the orange box below over the blue box the right, and release it. Fail if nothing happens in the blue box.';
+ var blockText = 'Drag the orange box below over the pink box the right, and release it. Pass if nothing happens in the pink box.';
+ var allowHelper = location.pathname.replace(/[^\/]*$/,'HELPER-mustallow.html');
+ var blockHelper = location.pathname.replace(/[^\/]*$/,'HELPER-mustblock.html');
+ if( location.hostname != httpHostMain || location.host != httpHostMain ) {
+ addNote('p','This test must be loaded over http:\/\/'+httpHostMain+'\/');
+ } else {
+ /* 01 */ testFrame(allowText,'http://'+httpHostMain,allowHelper);
+ /* 02 */ testFrame(allowText,'http://'+httpHostAlias,allowHelper);
+ /* 03 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'*');
+ /* 04 */ testFrame(allowText,'http://'+httpHostAlias,allowHelper,'*');
+ /* 05 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'/');
+ /* 06 */ testFrame(blockText,'http://'+httpHostAlias,blockHelper,'/');
+ /* 07 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'http://'+httpHostMain);
+ /* 08 */ testFrame(blockText,'http://'+httpHostMain+':'+httpPortAlias,blockHelper,'http://'+httpHostMain);
+ /* 09 */ testFrame(blockText,'http://'+httpHostAlias,blockHelper,'http://'+httpHostMain);
+ /* 10 */ testFrame(blockText,'http://'+httpHostAlias,blockHelper,'http://'+httpHostMain);
+ /* 11 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'http://'+httpHostMain+':80');
+ /* 12 */ testFrame(blockText,'http://'+httpHostMain+':'+httpPortAlias,blockHelper,'http://'+httpHostMain+':80');
+ /* 13 */ testFrame(allowText,'http://'+httpHostMain+':'+httpPortAlias,allowHelper,'http://'+httpHostMain+':'+httpPortAlias);
+ /* 14 */ testFrame(blockText,'http://'+httpHostMain,blockHelper,'http://'+httpHostMain+':'+httpPortAlias);
+ /* 15 */ testFrame(blockText,'https://'+httpsHostAlias,blockHelper,'http://'+httpsHostAlias);
+ /* 16 */ testFrame(allowText,'https://'+httpsHostAlias,allowHelper,'https://'+httpsHostAlias);
+ /* 17 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'http://foo:bar@'+httpHostMain+'/baz');
+ /* 18 */ testFrame(allowText,'http://foo:bar@'+httpHostMain,allowHelper,'http://'+httpHostMain);
+ /* 19 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'http://'+httpHostAlias,'/');
+ /* 20 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'/','http://'+httpHostAlias);
+ /* 21 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'http://'+httpHostAlias,'*');
+ /* 22 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'http://'+httpHostAlias,'http://'+httpHostMain);
+ /* 23 */ testFrame(allowText,'http://'+httpHostAlias,allowHelper,'http://'+httpHostAlias,'http://'+httpHostMain);
+ /* 24 */ testFrame(blockText,'http://'+httpHostAlias,blockHelper,'http://dummy','http://'+httpHostMain);
+ /* 25 */ testFrame(blockText,'https://'+httpsHostAlias,blockHelper,'https://'+httpsHostAlias+':'+httpsPortAlias);
+ /* 26 */ testFrame(blockText,'https://'+httpsHostAlias+':'+httpsPortAlias,blockHelper,'https://'+httpsHostAlias);
+ /* 27 */ testFrame(allowText,'https://'+httpsHostAlias+':'+httpsPortAlias,allowHelper,'https://'+httpsHostAlias+':'+httpsPortAlias);
+ window.xhr = new XMLHttpRequest();
+ xhr.open('GET',allowHelper,false);
+ xhr.send(null);
+ /* 28 */ testFrame(allowText,'data:text/html,',escape(xhr.responseText),'http://'+httpHostMain);
+ /* 29 */ testFrame(allowText,'javascript:','parent.xhr.responseText','http://'+httpHostMain);
+ /* 30 */ testFrame(blockText,'http://'+httpHostAlias,blockHelper,'http://'+httpHostAlias.replace(/^[^.]+\./,''));
+ /* 31 */ testFrame(allowText,'http://'+httpHostAlias,allowHelper+'?domain','http://'+httpHostAlias);
+ /* 32 */ testFrame(blockText,'http://'+httpHostAlias,blockHelper+'?domain','http://'+httpHostAlias.replace(/^[^.]+\./,''));
+ }
+};
+ </script>
+ </head>
+ <body>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/004-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/004-1.html
new file mode 100644
index 0000000000..94e4308743
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/004-1.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Data URI does not match absolute HTTP URL</title>
+ <style type="text/css">
+html, body { margin: 0; padding: 0; }
+div { height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ e.dataTransfer.allowTargetOrigin('http://'+httpHostMain);
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/004-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/004-manual.html
new file mode 100644
index 0000000000..a540e77b39
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/004-manual.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Data URI does not match absolute HTTP URL</title>
+ </head>
+ <body>
+ <p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <script type="text/javascript">
+document.write('data:text/html,'+escape(
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Data URI does not match absolute HTTP URL<\/title>\
+ <style type="text/css">\
+iframe { border: none; height: 150px; width: 150px; }\
+ <\/style>\
+ <script type="text/javascript">\
+window.onload = function () {\
+ document.body.ondragenter = document.body.ondragleave = document.body.ondragover = document.body.ondrop = function (e) {\
+ e.preventDefault();\
+ document.body.innerHTML = "FAIL";\
+ };\
+};\
+ <\/script>\
+ <\/head>\
+ <body>\
+ <p>Drag the orange square below over this text, and release it. Pass if this text does not change.<\/p>\
+ <p><iframe src="'+location.href.replace(/\.html$/,'-1.html')+'"><\/iframe><\/p>\
+ <\/body>\
+<\/html>'));
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/005-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/005-1.html
new file mode 100644
index 0000000000..74156fcd7c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/005-1.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>JavaScript URI does not match absolute HTTP URL</title>
+ <style type="text/css">
+html, body { margin: 0; padding: 0; }
+div { height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ e.dataTransfer.allowTargetOrigin('http://'+httpHostMain);
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/005-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/005-manual.html
new file mode 100644
index 0000000000..54ceec889b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/005-manual.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>JavaScript URI does not match absolute HTTP URL</title>
+ </head>
+ <body>
+ <p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <script type="text/javascript">
+document.write('javascript:unescape("'+escape(
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>JavaScript does not match absolute HTTP URL<\/title>\
+ <style type=\'text/css\'>\
+iframe { border: none; height: 150px; width: 150px; }\
+ <\/style>\
+ <script type=\'text/javascript\'>\
+window.onload = function () {\
+ document.body.ondragenter = document.body.ondragleave = document.body.ondragover = document.body.ondrop = function (e) {\
+ e.preventDefault();\
+ document.body.innerHTML = \'FAIL\';\
+ };\
+};\
+ <\/script>\
+ <\/head>\
+ <body>\
+ <p>Drag the orange square below over this text, and release it. Pass if this text does not change.<\/p>\
+ <p><iframe src=\''+location.href.replace(/\.html$/,'-1.html')+'\'><\/iframe><\/p>\
+ <\/body>\
+<\/html>')+'")');
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/006-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/006-manual.html
new file mode 100644
index 0000000000..f60430b4c0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/006-manual.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Data URI does not match /</title>
+ </head>
+ <body>
+ <!--
+/ sets an absolute URL pointing to the document's unique identifier - used as the script origin.
+The script origin will in fact be inherited from the parent page, which is actually the same data URI.
+That part works.
+However, when it comes to matching against it, it will not match, as the global identifier does not
+match because the origin does not match the scheme/host/port tuple required.
+ -->
+ <p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <script type="text/javascript">
+document.write('data:text/html,'+escape(
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Data URI does not match /<\/title>\
+ <style type="text/css">\
+html, body { margin: 0; padding: 0; }\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { border: none; height: 150px; width: 150px; }\
+ <\/style>\
+ <\/head>\
+ <body>\
+ <script type="text/javascript">\
+if( self == top ) {\
+ document.body.ondragenter = document.body.ondragleave = document.body.ondragover = document.body.ondrop = function (e) {\
+ e.preventDefault();\
+ document.body.innerHTML = "FAIL";\
+ };\
+ document.write("<p>Drag the orange square below over this text, and release it. Pass if this text does not change.<\\\/p>");\
+ document.write("<p><iframe src=\\""+location.href+"\\"><\\\/iframe><\\\/p>");\
+} else {\
+ document.write("<div draggable=\\"true\\"><\\\/div>");\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ e.dataTransfer.allowTargetOrigin("/");\
+ };\
+}\
+ <\/script>\
+ <\/body>\
+<\/html>'));
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/007-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/007-manual.html
new file mode 100644
index 0000000000..d3b45100b6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/007-manual.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Data URI does not match its own URL</title>
+ </head>
+ <body>
+ <!--
+Sets an absolute URL pointing to the data URI.
+The script origin will in fact be inherited from the parent page, which is actually the same data URI.
+That part works.
+However, when it comes to matching against it, it will not match, as the global identifier used as the
+script origin does not match because the origin does not match the scheme/host/port tuple required.
+ -->
+ <p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <script type="text/javascript">
+document.write('data:text/html,'+escape(
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Data URI does not match its own URL<\/title>\
+ <style type="text/css">\
+html, body { margin: 0; padding: 0; }\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { border: none; height: 150px; width: 150px; }\
+ <\/style>\
+ <\/head>\
+ <body>\
+ <script type="text/javascript">\
+if( self == top ) {\
+ document.body.ondragenter = document.body.ondragleave = document.body.ondragover = document.body.ondrop = function (e) {\
+ e.preventDefault();\
+ document.body.innerHTML = "FAIL";\
+ };\
+ document.write("<p>Drag the orange square below over this text, and release it. Pass if this text does not change.<\\\/p>");\
+ document.write("<p><iframe src=\\""+location.href+"\\"><\\\/iframe><\\\/p>");\
+} else {\
+ document.write("<div draggable=\\"true\\"><\\\/div>");\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ e.dataTransfer.allowTargetOrigin(location.href);\
+ };\
+}\
+ <\/script>\
+ <\/body>\
+<\/html>'));
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/008-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/008-manual.html
new file mode 100644
index 0000000000..f8a7daf022
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/008-manual.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Data URI does match *</title>
+ </head>
+ <body>
+ <!--
+* allows any URL at all, so it should work
+ -->
+ <p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <script type="text/javascript">
+document.write('data:text/html,'+escape(
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Data URI does match *<\/title>\
+ <style type="text/css">\
+html, body { margin: 0; padding: 0; }\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { border: none; height: 150px; width: 150px; }\
+ <\/style>\
+ <\/head>\
+ <body>\
+ <script type="text/javascript">\
+var seentypes = {};\
+if( self == top ) {\
+ document.body.ondragenter = document.body.ondragover = document.body.ondrop = function (e) {\
+ e.preventDefault();\
+ if( e.type == "drop" ) {\
+ document.body.innerHTML = ( seentypes.dragenter && seentypes.dragover ) ? "PASS" : "FAIL";\
+ } else {\
+ seentypes[e.type] = true;\
+ }\
+ };\
+ document.write("<p>Drag the orange square below over this text, and release it. Fail if this text does not change.<\\\/p>");\
+ document.write("<p><iframe src=\\""+location.href+"\\"><\\\/iframe><\\\/p>");\
+} else {\
+ document.write("<div draggable=\\"true\\"><\\\/div>");\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ e.dataTransfer.allowTargetOrigin("*");\
+ };\
+}\
+ <\/script>\
+ <\/body>\
+<\/html>'));
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/009-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/009-manual.html
new file mode 100644
index 0000000000..181ca85121
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/009-manual.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>* should not prevent dropping on external applications</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = "copy";
+ e.dataTransfer.setData("text","PASS");
+ e.dataTransfer.allowTargetOrigin("*");
+ };
+};
+ </script>
+ </head>
+ <body>
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of text from other applications - eg. Wordpad (write.exe) on Windows. Ensure that the external application is open.</p>
+ <p>Drag the orange block to the other application and release it. Pass if the word &quot;PASS&quot; appears in the other application.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div draggable="true"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/010-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/010-manual.html
new file mode 100644
index 0000000000..4fd893f052
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/010-manual.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>A URL should prevent dropping on external applications</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = "copy";
+ e.dataTransfer.setData("text","FAIL");
+ e.dataTransfer.allowTargetOrigin("http://foo");
+ };
+};
+ </script>
+ </head>
+ <body>
+ <p>This test is only relevant on platforms where it is possible to switch applications in mid-drag (eg. alt+tab, dragging over taskbar buttons, dragging between restored windows).</p>
+ <p>This testcase requires an external application that accepts dropping of text from other applications - eg. Wordpad (write.exe) on Windows. Ensure that the external application is open.</p>
+ <p>Drag the orange block to the other application and release it. Fail if the word &quot;FAIL&quot; appears in the other application.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div draggable="true"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/011-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/011-manual.html
new file mode 100644
index 0000000000..d68e03ad49
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/011-manual.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+ <head>
+ <title>allowTargetOrigin should only block dragenter, dragover, dragleave and drop events</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; margin: 0; padding: 0; float: left; }
+div + div { background: blue; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], evtdone = {}, fails = [];
+ orange.ondragstart = function (e) {
+ evtdone[e.type] = true;
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ try {
+ e.dataTransfer.allowTargetOrigin('http://example.com');
+ } catch(e) {
+ fails[fails.length] = 'allowTargetOrigin threw an error: '+e;
+ }
+ };
+ orange.ondragenter = orange.ondragover = orange.ondrop = function (e) {
+ e.preventDefault();
+ evtdone[e.type] = true;
+ };
+ orange.ondrag = orange.ondragleave = function (e) {
+ evtdone[e.type] = true;
+ };
+ orange.ondragend = function (e) {
+ evtdone[e.type] = true;
+ if( !evtdone.dragstart ) {
+ fails[fails.length] = 'dragstart did not fire - how did that happen?';
+ }
+ if( !evtdone.drag ) {
+ fails[fails.length] = 'drag did not fire';
+ }
+ if( !evtdone.dragend ) {
+ fails[fails.length] = 'dragend did not fire - OK, who broke the testcase?';
+ }
+ if( evtdone.dragenter ) {
+ fails[fails.length] = 'dragenter fired';
+ }
+ if( evtdone.dragover ) {
+ fails[fails.length] = 'dragover fired';
+ }
+ if( evtdone.dragleave ) {
+ fails[fails.length] = 'dragleave fired';
+ }
+ if( evtdone.drop ) {
+ fails[fails.length] = 'drop fired';
+ }
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL:<br>' + fails.join('<br>') ) : 'PASS';
+ };
+};
+ </script>
+ </head>
+ <body>
+ <p>Drag the orange square over the blue square then back to the orange square, then release it. Fail if this text does not change.</p>
+ <div draggable="true"></div>
+ <div></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/012-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/012-manual.html
new file mode 100644
index 0000000000..997e8ef801
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/012-manual.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+ <head>
+ <title>allowTargetOrigin after leaving browser window</title>
+ <style type="text/css">
+div { float: left; height: 100px; width: 100px; margin-right: 10px; background: orange; }
+iframe { height: 100px; width: 100px; border: none; }
+.note { float: right; color: silver; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+function addNote(el,str) {
+ var par = document.createElement(el);
+ par.textContent = str;
+ document.body.appendChild(par);
+}
+function testFrame(text,frameorigin,framepath) {
+ var persist = arguments;
+ addNote('p',(done++)+'. '+text);
+ var div = document.createElement('div');
+ var frame = document.createElement('iframe');
+ frame.src = frameorigin+framepath;
+ div.draggable = true;
+ div.ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ for( var i = 3; i < persist.length; i++ ) {
+ e.dataTransfer.allowTargetOrigin(persist[i]);
+ }
+ };
+ var par = document.createElement('p');
+ par.className = 'note';
+ par.appendChild(document.createTextNode('Target: '+frameorigin));
+ par.appendChild(document.createElement('br'));
+ par.appendChild(document.createTextNode('Allowing: '+([]).slice.call(persist,3).join(' and ')));
+ if( framepath.match(/\?domain\b/) ) {
+ par.appendChild(document.createElement('br'));
+ par.appendChild(document.createTextNode('document.domain set to parent domain'));
+ }
+ document.body.appendChild(par);
+ document.body.appendChild(div);
+ document.body.appendChild(frame);
+}
+var done = 1;
+window.onload = function () {
+ var allowText = 'Drag the orange box below outside the browser window (not onto the system taskbar) then back over the blue box the right, and release it. Fail if nothing happens in the blue box.';
+ var blockText = 'Drag the orange box below outside the browser window (not onto the system taskbar) then back over the pink box the right, and release it. Pass if nothing happens in the pink box.';
+ var allowHelper = location.pathname.replace(/[^\/]*$/,'HELPER-mustallow.html');
+ var blockHelper = location.pathname.replace(/[^\/]*$/,'HELPER-mustblock.html');
+ if( location.hostname != httpHostMain || location.host != httpHostMain ) {
+ addNote('p','This test must be loaded over http:\/\/'+httpHostMain+'\/');
+ } else {
+ /* 07 */ testFrame(allowText,'http://'+httpHostMain,allowHelper,'http://'+httpHostMain);
+ /* 09 */ testFrame(blockText,'http://'+httpHostAlias,blockHelper,'http://'+httpHostMain);
+ }
+};
+ </script>
+ </head>
+ <body>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/013-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/013-manual.html
new file mode 100644
index 0000000000..92da7647da
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/013-manual.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+ <head>
+ <title>A URL should prevent dropping on UI</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = "copy";
+ e.dataTransfer.setData("text","FAIL");
+ e.dataTransfer.allowTargetOrigin("http://foo");
+ };
+};
+ </script>
+ </head>
+ <body>
+ <p>This test is only relevant on platforms where it is possible to drop data onto the browser UI (eg. the address field).</p>
+ <p>Drag the orange block to the address field and release it. Fail if the word &quot;FAIL&quot; appears in the address field. Repeat for other UI fields.</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div draggable="true"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/101-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/101-manual.html
new file mode 100644
index 0000000000..23f5cf9baf
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/101-manual.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<html>
+ <head>
+ <title>.origin for all events</title>
+ <style type="text/css">
+blockquote { height: 100px; width: 100px; background: orange; margin: 0; padding: 0; float: left; }
+blockquote + blockquote { background: blue; }
+blockquote + blockquote + blockquote { background: fuchsia; }
+blockquote + div { clear: left; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+setup(function () {},{explicit_done:true});
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ done();
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var orange = document.getElementsByTagName('blockquote')[0],
+ blue = document.getElementsByTagName('blockquote')[1],
+ fuchsia = document.getElementsByTagName('blockquote')[2],
+ evtdone = {};
+ orange.ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_equals( e.dataTransfer.origin, origin );
+ }, '.origin should exist in '+e.type );
+ test(function () {
+ //WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter
+ //ES5 makes [[Put]] fail but not throw
+ var failed = false, oldorigin = e.dataTransfer.origin;
+ try {
+ e.dataTransfer.origin = 'http://example.com';
+ } catch(e) {
+ failed = e;
+ }
+ assert_equals(e.dataTransfer.origin,oldorigin);
+ assert_false(failed,'an error was thrown');
+ }, '.origin must be read-only in '+e.type);
+ };
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ orange.ondrag = blue.ondragleave = function (e) {
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_equals( e.dataTransfer.origin, origin );
+ }, '.origin should exist in '+e.type );
+ test(function () {
+ var failed = false, oldorigin = e.dataTransfer.origin;
+ try {
+ e.dataTransfer.origin = 'http://example.com';
+ } catch(e) {
+ failed = e;
+ }
+ assert_equals(e.dataTransfer.origin,oldorigin);
+ assert_false(failed,'an error was thrown');
+ }, '.origin must be read-only in '+e.type);
+ };
+ fuchsia.ondragenter = fuchsia.ondragover = fuchsia.ondrop = function (e) {
+ e.preventDefault();
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_equals( e.dataTransfer.origin, origin );
+ }, '.origin should exist in '+e.type );
+ test(function () {
+ var failed = false, oldorigin = e.dataTransfer.origin;
+ try {
+ e.dataTransfer.origin = 'http://example.com';
+ } catch(e) {
+ failed = e;
+ }
+ assert_equals(e.dataTransfer.origin,oldorigin);
+ assert_false(failed,'an error was thrown');
+ }, '.origin must be read-only in '+e.type);
+ };
+ orange.ondragend = function (e) {
+ if( evtdone[e.type] ) { return; }
+ evtdone[e.type] = true;
+ test(function() {
+ assert_equals( e.dataTransfer.origin, origin );
+ }, '.origin should exist in '+e.type );
+ test(function () {
+ var failed = false, oldorigin = e.dataTransfer.origin;
+ try {
+ e.dataTransfer.origin = 'http://example.com';
+ } catch(e) {
+ failed = e;
+ }
+ assert_equals(e.dataTransfer.origin,oldorigin);
+ assert_false(failed,'an error was thrown');
+ }, '.origin must be read-only in '+e.type);
+ test(function() {
+ var failtxt = '- Reload and try again';
+ assert_true( evtdone.dragstart, 'dragstart event was not tested'+failtxt );
+ assert_true( evtdone.drag, 'drag event was not tested'+failtxt );
+ assert_true( evtdone.dragenter, 'dragenter event was not tested'+failtxt );
+ assert_true( evtdone.dragleave, 'dragleave event was not tested'+failtxt );
+ assert_true( evtdone.dragover, 'dragover event was not tested'+failtxt );
+ assert_true( evtdone.drop, 'drop event was not tested'+failtxt );
+ assert_true( evtdone.dragend, 'dragend event was not tested'+failtxt );
+ }, 'all event types must now have been tested' );
+ done();
+ };
+};
+ </script>
+ </head>
+ <body>
+ <blockquote draggable="true"></blockquote>
+ <blockquote></blockquote>
+ <blockquote></blockquote>
+ <div id="log">Drag the orange square over the blue square then the fuchsia square, then release it.</div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/102-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/102-manual.html
new file mode 100644
index 0000000000..7fc9d58f70
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/102-manual.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site to itself</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/103-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/103-1.html
new file mode 100644
index 0000000000..837135b364
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/103-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site with user/pass/port to itself</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/103-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/103-manual.html
new file mode 100644
index 0000000000..945b22cd36
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/103-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site with user/pass/port to itself</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://foo:bar@'+httpHostMain+':80'+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/104-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/104-1.html
new file mode 100644
index 0000000000..6503b5f56b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/104-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site with non-default port to itself</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain+':'+httpPortAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/104-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/104-manual.html
new file mode 100644
index 0000000000..a881e5eda3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/104-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site with non-default port to itself</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://'+httpHostMain+':'+httpPortAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/105-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/105-1.html
new file mode 100644
index 0000000000..00bae5f16c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/105-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site to site with non-default port</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+':'+httpPortAlias+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/105-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/105-manual.html
new file mode 100644
index 0000000000..39a44f542a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/105-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site to site with non-default port</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://'+httpHostMain+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/106-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/106-1.html
new file mode 100644
index 0000000000..e7f985c8ea
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/106-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site with non-default port to site</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain+':'+httpPortAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://'+httpHostMain+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/106-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/106-manual.html
new file mode 100644
index 0000000000..30db964c44
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/106-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for http site with non-default port to site</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://'+httpHostMain+':'+httpPortAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/107-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/107-1.html
new file mode 100644
index 0000000000..a3c8200519
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/107-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site to itself</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'https://'+httpsHostAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/107-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/107-manual.html
new file mode 100644
index 0000000000..136b61c4e5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/107-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site to itself</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'https://'+httpsHostAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/108-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/108-1.html
new file mode 100644
index 0000000000..4c11a7cd5d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/108-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site with non-default port to itself</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'https://'+httpsHostAlias+':'+httpsPortAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/108-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/108-manual.html
new file mode 100644
index 0000000000..e73e592db1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/108-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site with non-default port to itself</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'https://'+httpsHostAlias+':'+httpsPortAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/109-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/109-1.html
new file mode 100644
index 0000000000..4ce1ad10b9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/109-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site to site with non-default port</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'https://'+httpsHostAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+':'+httpsPortAlias+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/109-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/109-manual.html
new file mode 100644
index 0000000000..7919281dd2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/109-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site to site with non-default port</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'https://'+httpsHostAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/110-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/110-1.html
new file mode 100644
index 0000000000..cd64c530f0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/110-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site with non-default port to site</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'https://'+httpsHostAlias+':'+httpsPortAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = 'https://'+httpsHostAlias+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/110-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/110-manual.html
new file mode 100644
index 0000000000..8b13ff1292
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/110-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for https site with non-default port to site</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'https://'+httpsHostAlias+':'+httpsPortAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/111-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/111-manual.html
new file mode 100644
index 0000000000..57f2c9fbb3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/111-manual.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for file: to http:</title>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var datastr =
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Origin for file: to http:<\/title>\
+ <style type="text/css">\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { width: 500px; height: 120px; border: none; }\
+ <\/style>\
+ <script type="text/javascript">\
+window.onload = function () {\
+ var origin = "null (string)";\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ };\
+ document.getElementsByTagName("span")[0].textContent = origin;\
+ var iframe = document.createElement("iframe");\
+ iframe.src = "'+location.href.replace(/[^\/]*$/,'HELPER-showorigin.html')+'";\
+ document.body.insertBefore(iframe,document.getElementsByTagName("div")[0]);\
+};\
+ <\/script>\
+ <\/head>\
+ <body>\
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>\
+ <span><\/span><\/p>\
+ <div draggable="true"></div>\
+ <\/body>\
+<\/html>';
+ document.getElementsByTagName('a')[0].href = 'data:text/html,'+escape(datastr);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p><a href="">Download the linked file to your disk</a>, and open it locally. Follow further instructions in that file.</p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/112-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/112-manual.html
new file mode 100644
index 0000000000..c19638c9ba
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/112-manual.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for data: with inherited http origin to http:</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var datastr =
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Origin for data: with inherited http origin to http:<\/title>\
+ <style type="text/css">\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { width: 500px; height: 120px; border: none; }\
+ <\/style>\
+ <script type="text/javascript">\
+window.onload = function () {\
+ var origin = "http://'+httpHostMain+'";\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ };\
+ document.getElementsByTagName("span")[0].textContent = origin;\
+ var iframe = document.createElement("iframe");\
+ iframe.src = "'+location.href.replace(/[^\/]*$/,'HELPER-showorigin.html')+'";\
+ document.body.insertBefore(iframe,document.getElementsByTagName("div")[0]);\
+};\
+ <\/script>\
+ <\/head>\
+ <body>\
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>\
+ <span><\/span><\/p>\
+ <div draggable="true"></div>\
+ <\/body>\
+<\/html>';
+ var iframe = document.createElement('iframe');
+ iframe.src = 'data:text/html,'+escape(datastr);
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/113-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/113-manual.html
new file mode 100644
index 0000000000..aa4b2b1cd3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/113-manual.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for data: with no inherited origin to http:</title>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ </head>
+ <body>
+
+ <p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <script type="text/javascript">
+var origin = 'http://'+httpHostMain;
+if( location.href.indexOf(origin+'/') ) {
+ document.write('This must be tested on '+origin+'/');
+} else {
+ document.write("data:text/html,"+escape(
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Origin for data: with no inherited origin to http:<\/title>\
+ <style type="text/css">\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { width: 500px; height: 120px; border: none; }\
+ <\/style>\
+ <script type="text/javascript">\
+window.onload = function () {\
+ var origin = "null (string)";\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ };\
+ document.getElementsByTagName("span")[0].textContent = origin;\
+ var iframe = document.createElement("iframe");\
+ iframe.src = "'+location.href.replace(/[^\/]*$/,'HELPER-showorigin.html')+'";\
+ document.body.insertBefore(iframe,document.getElementsByTagName("div")[0]);\
+};\
+ <\/script>\
+ <\/head>\
+ <body>\
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>\
+ <span><\/span><\/p>\
+ <div draggable="true"></div>\
+ <\/body>\
+<\/html>'));
+}
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/114-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/114-manual.html
new file mode 100644
index 0000000000..9c7e7ff338
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/114-manual.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for javascript: with inherited http origin to http:</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var datastr =
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Origin for javascript: with inherited http origin to http:<\/title>\
+ <style type="text/css">\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { width: 500px; height: 120px; border: none; }\
+ <\/style>\
+ <script type="text/javascript">\
+window.onload = function () {\
+ var origin = "http://'+httpHostMain+'";\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ };\
+ document.getElementsByTagName("span")[0].textContent = origin;\
+ var iframe = document.createElement("iframe");\
+ iframe.src = "'+location.href.replace(/[^\/]*$/,'HELPER-showorigin.html')+'";\
+ document.body.insertBefore(iframe,document.getElementsByTagName("div")[0]);\
+};\
+ <\/script>\
+ <\/head>\
+ <body>\
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>\
+ <span><\/span><\/p>\
+ <div draggable="true"></div>\
+ <\/body>\
+<\/html>';
+ var iframe = document.createElement('iframe');
+ iframe.src = "javascript:'"+escape(datastr)+"'";
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/115-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/115-manual.html
new file mode 100644
index 0000000000..133b2200ff
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/115-manual.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for javascript: with no inherited origin to http:</title>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ </head>
+ <body>
+
+ <p>Load the following URL in a new tab (copy &amp; paste it into the address bar):</p>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ <script type="text/javascript">
+var origin = 'http://'+httpHostMain;
+if( location.href.indexOf(origin+'/') ) {
+ document.write('This must be tested on '+origin+'/');
+} else {
+ document.write("javascript:'"+escape(
+'<!doctype html>\
+<html>\
+ <head>\
+ <title>Origin for javascript: with no inherited origin to http:<\/title>\
+ <style type="text/css">\
+div { height: 100px; width: 100px; background: orange; }\
+iframe { width: 500px; height: 120px; border: none; }\
+ <\/style>\
+ <script type="text/javascript">\
+window.onload = function () {\
+ var origin = "null (string)";\
+ document.getElementsByTagName("div")[0].ondragstart = function (e) {\
+ e.dataTransfer.effectAllowed = "copy";\
+ e.dataTransfer.setData("text","dummy text");\
+ };\
+ document.getElementsByTagName("span")[0].textContent = origin;\
+ var iframe = document.createElement("iframe");\
+ iframe.src = "'+location.href.replace(/[^\/]*$/,'HELPER-showorigin.html')+'";\
+ document.body.insertBefore(iframe,document.getElementsByTagName("div")[0]);\
+};\
+ <\/script>\
+ <\/head>\
+ <body>\
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>\
+ <span><\/span><\/p>\
+ <div draggable="true"></div>\
+ <\/body>\
+<\/html>')+"'");
+}
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/116-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/116-manual.html
new file mode 100644
index 0000000000..dd6dbf57b0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/116-manual.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for dropped files</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ document.getElementsByTagName('span')[0].textContent = 'null (string)';
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag a small file from your computer onto the blue square and release it. If a prompt appears, accept it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/117-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/117-1.html
new file mode 100644
index 0000000000..40b0885f33
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/117-1.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for site with document.domain set to a parent domain</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ document.domain = httpsHostAlias.replace(/^[^.]+\./,'');
+ var iframe = document.createElement('iframe');
+ iframe.src = origin+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/117-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/117-manual.html
new file mode 100644
index 0000000000..88676a4b77
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/117-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin for site with document.domain set to a parent domain</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://'+httpHostAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/118-1.html b/testing/web-platform/tests/html/editing/dnd/target-origin/118-1.html
new file mode 100644
index 0000000000..5161661a76
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/118-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin after leaving browser window</title>
+ <style type="text/css">
+div { height: 100px; width: 100px; background: orange; }
+iframe { width: 500px; height: 120px; border: none; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain+':'+httpPortAlias;
+ document.getElementsByTagName('div')[0].ondragstart = function (e) {
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ };
+ document.getElementsByTagName('span')[0].textContent = origin;
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://'+httpHostMain+location.pathname.replace(/[^\/]*$/,'HELPER-showorigin.html');
+ document.body.insertBefore(iframe,document.getElementsByTagName('div')[0]);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <p>Drag the orange square out of the browser window (not onto the system taskbar) then back onto the blue square and release it. The blue square should be replaced with the text:<br>
+ <span></span></p>
+ <div draggable="true"></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/118-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/118-manual.html
new file mode 100644
index 0000000000..0b9df2292d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/118-manual.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Origin after leaving browser window</title>
+ <style type="text/css">
+html, body, iframe { display: block; width: 100%; height: 100%; border: none; margin: 0; padding: 0; }
+ </style>
+ <script type="text/javascript" src="../resources/crossorigin.sub.js"></script>
+ <script type="text/javascript">
+window.onload = function () {
+ var origin = 'http://'+httpHostMain;
+ if( location.href.indexOf(origin+'/') ) {
+ document.body.innerHTML = 'This must be tested on '+origin+'/';
+ return;
+ }
+ var iframe = document.createElement('iframe');
+ iframe.src = 'http://'+httpHostMain+':'+httpPortAlias+location.pathname.replace(/.html$/,'-1.html');
+ document.body.appendChild(iframe);
+};
+ </script>
+ </head>
+ <body>
+
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/201-manual.html b/testing/web-platform/tests/html/editing/dnd/target-origin/201-manual.html
new file mode 100644
index 0000000000..e19d5289cc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/201-manual.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+ <head>
+ <title>allowTargetOrigin with real dataTransfer should block dragenter, dragover, dragleave and drop synthetic events</title>
+ <style type="text/css">
+p + div { height: 100px; width: 100px; background: orange; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var orange = document.getElementsByTagName('div')[0], targ = document.getElementsByTagName('div')[1], evtdone = {}, fails = [];
+ orange.ondragstart = function (e) {
+ var evt;
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ try {
+ e.dataTransfer.allowTargetOrigin('http://example.com');
+ } catch(e) {
+ fails[fails.length] = 'allowTargetOrigin threw an error: '+e;
+ }
+ try {
+ evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragenter', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragover', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragleave', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('drop', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragend', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ } catch(e) {
+ fails[fails.length] = 'Synthetic event threw an error: '+e;
+ }
+ if( !evtdone.dragstart ) {
+ fails[fails.length] = 'dragstart did not fire';
+ }
+ if( !evtdone.drag ) {
+ fails[fails.length] = 'drag did not fire';
+ }
+ if( !evtdone.dragend ) {
+ fails[fails.length] = 'dragend did not fire';
+ }
+ if( evtdone.dragenter ) {
+ fails[fails.length] = 'dragenter fired';
+ }
+ if( evtdone.dragover ) {
+ fails[fails.length] = 'dragover fired';
+ }
+ if( evtdone.dragleave ) {
+ fails[fails.length] = 'dragleave fired';
+ }
+ if( evtdone.drop ) {
+ fails[fails.length] = 'drop fired';
+ }
+ document.getElementsByTagName('p')[0].innerHTML = fails.length ? ( 'FAIL:<br>' + fails.join('<br>') ) : 'PASS';
+ };
+ targ.ondragstart = function (e) {
+ evtdone[e.type] = true;
+ };
+ targ.ondragenter = targ.ondragover = targ.ondrop = function (e) {
+ e.preventDefault();
+ evtdone[e.type] = true;
+ };
+ targ.ondrag = targ.ondragleave = function (e) {
+ evtdone[e.type] = true;
+ };
+ targ.ondragend = function (e) {
+ evtdone[e.type] = true;
+ };
+};
+ </script>
+ </head>
+ <body>
+ <p>Drag the orange square to the right until the drag placeholder appears, then release it. Fail if this text does not change.</p>
+ <div draggable="true"></div>
+ <div></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/202.html b/testing/web-platform/tests/html/editing/dnd/target-origin/202.html
new file mode 100644
index 0000000000..84f3f2ee96
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/202.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+ <head>
+ <title>allowTargetOrigin with fake dataTransfer should block dragenter, dragover, dragleave and drop synthetic events</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div draggable="true"></div>
+ <div></div>
+ <noscript><p>Enable JavaScript and reload</p></noscript>
+ <div id="log"></div>
+
+ <script type="text/javascript">
+test(function () {
+ var dragsource = document.getElementsByTagName('div')[0], targ = document.getElementsByTagName('div')[1], evtdone = {};
+ dragsource.ondragstart = function (e) {
+ var evt;
+ evtdone.initial = true;
+ e.dataTransfer.effectAllowed = 'copy';
+ e.dataTransfer.setData('text','dummy text');
+ e.dataTransfer.allowTargetOrigin('http://example.com');
+ evt = new DragEvent('dragstart', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('drag', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragenter', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragover', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragleave', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('drop', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ evt = new DragEvent('dragend', {dataTransfer:e.dataTransfer});
+ targ.dispatchEvent(evt);
+ };
+ targ.ondragstart = function (e) {
+ evtdone[e.type] = true;
+ };
+ targ.ondragenter = targ.ondragover = targ.ondrop = function (e) {
+ e.preventDefault();
+ evtdone[e.type] = true;
+ };
+ targ.ondrag = targ.ondragleave = function (e) {
+ evtdone[e.type] = true;
+ };
+ targ.ondragend = function (e) {
+ evtdone[e.type] = true;
+ };
+ var creatorevent = document.createEvent('DragEvent');
+ creatorevent.initDragEvent('dragstart', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 1, document.body, {});
+ dragsource.dispatchEvent(creatorevent);
+ assert_true(evtdone.dragstart, 'dragstart must fire');
+ assert_true(evtdone.drag, 'drag must fire');
+ assert_true(evtdone.dragend, 'dragend must fire');
+ assert_false(evtdone.dragenter, 'dragenter must not fire');
+ assert_false(evtdone.dragover, 'dragover must not fire');
+ assert_false(evtdone.dragleave, 'dragleave must not fire');
+ assert_false(evtdone.drop, 'drop must not fire');
+}, "allowTargetOrigin with fake dataTransfer should block events");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustallow.html b/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustallow.html
new file mode 100644
index 0000000000..c0d3aa022c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustallow.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Must be allowed</title>
+ <style type="text/css">
+html { background: blue; }
+html, body { margin: 0; padding: 0; height: 100%; width: 100%; }
+ </style>
+ </head>
+ <body>
+ <script type="text/javascript">
+if( location.search && location.search.indexOf('domain') != -1 ) {
+ document.domain = location.hostname.replace(/^[^.]+\./,'');
+}
+var seentypes = {};
+document.body.ondragenter = document.body.ondragover = document.body.ondrop = function (e) {
+ e.preventDefault();
+ if( e.type == 'drop' ) {
+ document.body.innerHTML = ( seentypes.dragenter && seentypes.dragover && e.dataTransfer.getData('text') == 'dummy text' ) ? 'PASS' : 'FAIL';
+ } else {
+ seentypes[e.type] = true;
+ }
+}
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustblock.html b/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustblock.html
new file mode 100644
index 0000000000..c7c69ee6b0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-mustblock.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Must be blocked</title>
+ <style type="text/css">
+html { background: fuchsia; }
+html, body { margin: 0; padding: 0; height: 100%; width: 100%; }
+ </style>
+ </head>
+ <body>
+ <script type="text/javascript">
+if( location.search && location.search.indexOf('domain') != -1 ) {
+ document.domain = location.hostname.replace(/^[^.]+\./,'');
+}
+document.body.ondragenter = document.body.ondragleave = document.body.ondragover = document.body.ondrop = function (e) {
+ e.preventDefault();
+ document.body.innerHTML = 'FAIL';
+}
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-showorigin.html b/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-showorigin.html
new file mode 100644
index 0000000000..057f969dd2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/target-origin/HELPER-showorigin.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Readout of .origin</title>
+ <style type="text/css">
+html, body { margin: 0; padding: 0; }
+div { height: 100px; width: 100px; background: blue; }
+ </style>
+ <script type="text/javascript">
+window.onload = function () {
+ var blue = document.getElementsByTagName('div')[0];
+ blue.ondragenter = blue.ondragover = function (e) {
+ e.preventDefault();
+ };
+ blue.ondrop = function (e) {
+ e.preventDefault();
+ if( e.dataTransfer.origin === 'null' ) {
+ document.body.innerHTML = 'null (string)';
+ } else {
+ document.body.innerHTML = e.dataTransfer.origin;
+ }
+ };
+};
+ </script>
+ </head>
+ <body>
+
+ <div></div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/DataTransfer-types-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/DataTransfer-types-manual.html
new file mode 100644
index 0000000000..0a62997f69
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/DataTransfer-types-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>DataTransferItem Test: types - files</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/dnd.html#dom-datatransfer-types"/>
+<meta name="flags" content="interact">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p><div id="div" style="border: 2px green solid; width: 200px; height: 200px;"></div></p>
+
+<h2>Test steps:</h2>
+<p>Drag a file enter the green box, then drop file out</p>
+
+<script>
+
+let div = document.getElementById("div");
+
+setup({explicit_done: true});
+setup({explicit_timeout: true});
+
+on_event(div, "dragenter", evt => {
+ let type = evt.dataTransfer.types[0];
+ test(() => {
+ assert_equals(type, "Files");
+ }, "Check if one of the types will be the string 'Files' when drag a file");
+ done();
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/dndTransferCases-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/dndTransferCases-manual.html
new file mode 100644
index 0000000000..6081b5d42c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/dndTransferCases-manual.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_data_item_kind_string</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='author' title='Domenic Denicola' href='mailto:d@domenic.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-datatransfer-interface'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #drag {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='drag' draggable>blue text</div>
+ <div id='drop' dropzone='copy string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drag;
+ setup(function() {
+ drag = document.querySelector('#drag');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drag, 'dragstart', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.effectAllowed, 'uninitialized');
+ }, 'effectAllowed should be "uninitialized"');
+
+ test(function() {
+ assert_equals(event.dataTransfer.types.constructor, Array, 'should be an array');
+ assert_true(Object.isFrozen(event.dataTransfer.types), 'should be frozen');
+ }, 'types should be a frozen array');
+
+ test(function() {
+ assert_false('contains' in event.dataTransfer.types);
+ assert_false('item' in event.dataTransfer.types);
+ }, 'types should not have any of the historical methods');
+
+ test(function() {
+ assert_equals(event.dataTransfer.types, event.dataTransfer.types);
+ }, 'types should return the same object from multiple reads (assuming no changes)');
+
+ test(function() {
+ var before = event.dataTransfer.types;
+ event.dataTransfer.clearData();
+ assert_not_equals(event.dataTransfer.types, before);
+ }, 'types should return a different object after changes');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/effectAllowed-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/effectAllowed-manual.html
new file mode 100644
index 0000000000..08540b906a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/effectAllowed-manual.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Set a value to effectAllowed attribute</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#datatransfer"/>
+ <meta name="assert" content="Set a value to effectAllowed attribute"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var TARGETEVENT1, TARGETEVENT2, TARGET1, TARGET2;
+
+ function DragstartEvent(evt)
+ {
+ if ((TARGET1 == evt.target) && (TARGETEVENT1 == evt.type))
+ {
+ evt.dataTransfer.effectAllowed = "move";
+ }
+ }
+ function DragenterEvent(evt)
+ {
+ if ((TARGET2 == evt.target) && (TARGETEVENT2 == evt.type))
+ {
+ if("move" == evt.dataTransfer.effectAllowed)
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+ }
+
+ TARGETEVENT1 = "dragstart";
+ TARGETEVENT2 = "dragenter";
+
+ window.onload = function()
+ {
+ TARGET1 = document.getElementById("target1");
+ TARGET2 = document.getElementById("target2");
+ AddEventListenersForElement(TARGETEVENT1, DragstartEvent, false, TARGET1);
+ AddEventListenersForElement(TARGETEVENT2, DragenterEvent, false, TARGET2);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Set a value to effectAllowed attribute</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Drag the blue image and enter the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#datatransfer
+ </p>
+ <p>
+ On setting, if the new value is one of "none", "copy", "copyLink", "copyMove", "link", "linkMove", "move", "all", or "uninitialized", then the attribute's current value must be set to the new value.
+ </p>
+ <img src="/images/blue.png" style="width:200px; height:100px" draggable="true" id="target1"/>
+ <br /><br />
+ <input type="text" id="target2" style="border:2px green solid; width:200px; height:50px"></input>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/files-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/files-manual.html
new file mode 100644
index 0000000000..7de0b4bbce
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/files-manual.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: files attribute returns a FileList</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#datatransfer"/>
+ <meta name="assert" content="files attribute returns a FileList"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DropEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ var files = evt.dataTransfer.files;
+ if(('[object FileList]' == files))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ function DragenterEvent(evt)
+ {
+ evt.preventDefault();
+ }
+
+ function DragoverEvent(evt)
+ {
+ evt.preventDefault();
+ }
+
+ EVENT = "drop";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DropEvent, false, TARGET);
+ AddEventListenersForElement("dragenter", DragenterEvent, false, TARGET);
+ AddEventListenersForElement("dragover", DragoverEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: files attribute returns a FileList</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Drag a file and drop it in the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#datatransfer
+ </p>
+ <p>
+ The files attribute must return a live FileList sequence consisting of File objects representing the files.
+ </p>
+ <textarea type="text" id="target" style="border:2px green solid; width:200px; height:50px"></textarea>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedDragDataTransfer-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedDragDataTransfer-manual.html
new file mode 100644
index 0000000000..6d84f54efc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedDragDataTransfer-manual.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: drag DataTransfer protected status</title>
+ <link rel='author' title='Nika Layzell' href='mailto:nika@thelayzells.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-datatransfer-interface'>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ #drag {
+ width: 100px;
+ height: 100px;
+ display: inline-block;
+ color: #fff;
+ background-color: #f00;
+ }
+ #drop {
+ width: 100px;
+ height: 100px;
+ display: inline-block;
+ color: #fff;
+ background-color: #00f;
+ }
+ </style>
+ </head>
+
+ <body>
+ <h3>Instructions</h3>
+ <p>
+ Drag the box labeled "drag" to the box labeled "drop" and release.
+ </p>
+
+ <div id="drag" draggable="true">drag</div>
+ <div id="drop">drop</div>
+
+ <div id="log"> </div>
+
+ <script>
+ var MIME = "text/plain";
+
+ var drop;
+ setup(function() {
+ drop = document.querySelector("#drop");
+ }, {explicit_done: true, explicit_timeout: true});
+
+ var STATUS_PROTECTED = "protected";
+ var STATUS_READONLY = "readonly";
+ var STATUS_READWRITE = "readwrite";
+ var STATUS_DISCONNECTED = "disconnected";
+ function status(dt) {
+ // Check if we can write to it.
+ try {
+ dt.setData("text/html", "_test");
+
+ if (dt.getData("text/html") == "_test") {
+ dt.clearData("text/html");
+ assert_true(!dt.getData("text/html"), "ClearData should work...");
+ return STATUS_READWRITE;
+ }
+ } catch(e) {}
+
+ // If we can read the data then we're readonly
+ if (dt.getData(MIME)) {
+ return STATUS_READONLY;
+ }
+
+ // If we can see that items exist (and read types) then we're protected
+ if (dt.items.length > 0) {
+ return STATUS_PROTECTED;
+ }
+
+ // Otherwise we've been disconnected.
+ return STATUS_DISCONNECTED;
+ };
+
+ var drag_dt = null;
+ var over_dt = null;
+ var drop_dt = null;
+ on_event(document.body, "dragstart", function(e) {
+ drag_dt = e.dataTransfer;
+ over_dt = null;
+ drop_dt = null;
+ drag_dt.setData(MIME, "b");
+ test(function() {
+ assert_equals(status(drag_dt), STATUS_READWRITE,
+ "drag_dt must be readwrite during dragstart");
+ }, "dragstart event status");
+ });
+ on_event(drop, "dragover", function(e) {
+ if (!over_dt) {
+ over_dt = e.dataTransfer;
+ test(function() {
+ assert_equals(status(drag_dt), STATUS_DISCONNECTED,
+ "drag_dt mustbe disconnected during dragover");
+ assert_equals(status(over_dt), STATUS_PROTECTED,
+ "over_dt mustbe protected during dragover");
+ }, "dragover event status");
+ test(function() {
+ assert_not_equals(drag_dt, over_dt,
+ "drag_dt must be a different DataTransfer object than over_dt");
+ }, "dragover event identity");
+ }
+ e.preventDefault();
+ });
+ on_event(drop, "drop", function(e) {
+ drop_dt = e.dataTransfer;
+ test(function() {
+ assert_equals(status(drag_dt), STATUS_DISCONNECTED,
+ "drag_dt mustbe disconnected during drop");
+ assert_equals(status(over_dt), STATUS_DISCONNECTED,
+ "over_dt mustbe disconnected during drop");
+ assert_equals(status(drop_dt), STATUS_READONLY,
+ "drop_dt mustbe readonly during drop");
+ }, "drop event status");
+ test(function() {
+ assert_not_equals(drop_dt, over_dt,
+ "drop_dt must be a different DataTransfer object than over_dt");
+ assert_not_equals(drop_dt, drag_dt,
+ "drop_dt must be a different DataTransfer object than drag_dt");
+ }, "drop event identity");
+ test(function() {
+ assert_equals(drop_dt.getData(MIME), "b",
+ "the data should have been persisted");
+ }, "drop event data");
+ e.preventDefault();
+
+ setTimeout(function() {
+ test(function() {
+ assert_equals(status(drag_dt), STATUS_DISCONNECTED,
+ "drag_dt mustbe disconnected after drop");
+ assert_equals(status(over_dt), STATUS_DISCONNECTED,
+ "over_dt mustbe disconnected after drop");
+ assert_equals(status(drop_dt), STATUS_DISCONNECTED,
+ "drop_dt mustbe disconnected after drop");
+ }, "after drop event status");
+ done();
+ }, 0);
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedPasteDataTransfer-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedPasteDataTransfer-manual.html
new file mode 100644
index 0000000000..20bf9c7a9a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/protectedPasteDataTransfer-manual.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: paste DataTransfer protected status</title>
+ <link rel='author' title='Nika Layzell' href='mailto:nika@thelayzells.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-datatransfer-interface'>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h3>Instructions</h3>
+ <p>
+ Select the text in the text box and press Ctrl-C followed by Ctrl-V.
+ </p>
+
+ <input type="text" id="input" value="text">
+
+ <div id="log"> </div>
+
+ <script>
+ var MIME = "text/plain";
+
+ var input;
+ setup(function() {
+ input = document.querySelector("#input");
+ }, {explicit_done: true, explicit_timeout: true});
+
+ var STATUS_PROTECTED = "protected";
+ var STATUS_READONLY = "readonly";
+ var STATUS_READWRITE = "readwrite";
+ var STATUS_DISCONNECTED = "disconnected";
+ function status(dt) {
+ // Check if we can write to it.
+ try {
+ dt.setData("text/html", "_test");
+
+ if (dt.getData("text/html") == "_test") {
+ dt.clearData("text/html");
+ assert_true(!dt.getData("text/html"), "ClearData should work...");
+ return STATUS_READWRITE;
+ }
+ } catch(e) {}
+
+ // If we can read the data then we're readonly
+ if (dt.getData(MIME)) {
+ return STATUS_READONLY;
+ }
+
+ // If we can see that items exist (and read types) then we're protected
+ if (dt.items.length > 0) {
+ return STATUS_PROTECTED;
+ }
+
+ // Otherwise we've been disconnected.
+ return STATUS_DISCONNECTED;
+ };
+
+ let copy_dt = null;
+ let paste_dt = null;
+ on_event(input, "copy", function(e) {
+ copy_dt = e.clipboardData;
+ paste_dt = null;
+ copy_dt.setData(MIME, "b");
+
+ test(function() {
+ assert_equals(status(copy_dt), STATUS_READWRITE,
+ "copy_dt must be readwrite during copy");
+ }, "copy event status");
+
+ e.preventDefault();
+ });
+ on_event(input, "paste", function(e) {
+ paste_dt = e.clipboardData;
+
+ test(function() {
+ assert_equals(status(copy_dt), STATUS_DISCONNECTED,
+ "copy_dt mustbe disconnected during paste");
+ assert_equals(status(paste_dt), STATUS_READONLY,
+ "paste_dt mustbe readonly during paste");
+ }, "paste event status");
+ test(function() {
+ assert_not_equals(copy_dt != paste_dt,
+ "copy_dt must be a different DataTransfer object than paste_dt");
+ }, "paste event identity");
+ test(function() {
+ assert_equals(paste_dt.getData(MIME), "b",
+ "the data should have been persisted");
+ }, "paste event data");
+
+ e.preventDefault();
+
+ setTimeout(function() {
+ test(function() {
+ assert_equals(status(copy_dt), STATUS_DISCONNECTED,
+ "copy_dt mustbe disconnected after paste");
+ assert_equals(status(paste_dt), STATUS_DISCONNECTED,
+ "paste_dt mustbe disconnected after paste");
+ }, "after paste event status");
+ done();
+ }, 0);
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/setData-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/setData-manual.html
new file mode 100644
index 0000000000..f0f7cae600
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/setData-manual.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: Add an item to the drag data store item list whose data is the string given by setData method's second argument</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#datatransfer"/>
+ <meta name="assert" content="Add an item to the drag data store item list whose data is the string given by setData method's second argument"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var TARGETEVENT1, TARGETEVENT2, TARGET1, TARGET2;
+
+ function DragstartEvent(evt)
+ {
+ if ((TARGET1 == evt.target) && (TARGETEVENT1 == evt.type))
+ {
+ evt.dataTransfer.setData("text", "SetText");
+ }
+ }
+ function DropEvent(evt)
+ {
+ if ((TARGET2 == evt.target) && (TARGETEVENT2 == evt.type))
+ {
+ if("SetText" == evt.dataTransfer.getData("text"))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+ }
+
+ TARGETEVENT1 = "dragstart";
+ TARGETEVENT2 = "drop";
+
+ window.onload = function()
+ {
+ TARGET1 = document.getElementById("target1");
+ TARGET2 = document.getElementById("target2");
+ AddEventListenersForElement(TARGETEVENT1, DragstartEvent, false, TARGET1);
+ AddEventListenersForElement(TARGETEVENT2, DropEvent, false, TARGET2);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: Add an item to the drag data store item list whose data is the string given by setData method's second argument</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Drag the blue image and drop it in the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#datatransfer
+ </p>
+ <p>
+ If format equals "text", change it to "text/plain".
+ Remove the item in the drag data store item list whose kind is Plain Unicode string and whose type string is equal to format, if there is one.
+ Add an item to the drag data store item list whose kind is Plain Unicode string, whose type string is equal to format, and whose data is the string given by the method's second argument.
+ </p>
+ <img src="/images/blue.png" style="width:200px; height:100px" draggable="customValue2" id="target1"/>
+ <br /><br />
+ <input type="text" id="target2" style="border:2px green solid; width:200px; height:50px"></input>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/types-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/types-manual.html
new file mode 100644
index 0000000000..1730c4bc73
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/types-manual.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML5 Drag and Drop: types attribute returns a DOMStringList</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <link rel="help" href="http://dev.w3.org/html5/spec/dnd.html#datatransfer"/>
+ <meta name="assert" content="types attribute returns a DOMStringList"/>
+ <script src="../resources/dragdrop_support.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ var EVENT, TARGET;
+
+ function DropEvent(evt)
+ {
+ if ((TARGET == evt.target) && (EVENT == evt.type))
+ {
+ var types = evt.dataTransfer.types;
+ if(('[object DOMStringList]' == types))
+ {
+ LogTestResult("PASS");
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+ else
+ {
+ LogTestResult("FAIL");
+ }
+ }
+
+ EVENT = "drop";
+
+ window.onload = function()
+ {
+ TARGET = document.getElementById("target");
+ AddEventListenersForElement(EVENT, DropEvent, false, TARGET);
+ }
+ </script>
+ </head>
+ <body>
+ <pre>Description: types attribute returns a DOMStringList</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_result'>Manual</td>
+ <td id='test_assertion'>Test passes if if the word "PASS" appears to the left after following the steps below.
+ <div id="manualsteps">
+ Steps:
+ <ol>
+ <li> Select the text inside the red box
+ <li> Drag and drop it in the green box
+ </ol>
+ </div>
+ </td>
+ </tr>
+ </table>
+ <p>
+ http://dev.w3.org/html5/spec/dnd.html#datatransfer
+ </p>
+ <p>
+ The types attribute must return a live DOMStringList.
+ </p>
+ <div style="border:2px red solid; width:200px; height:50px">SampleText</div>
+ <br /><br />
+ <input type="text" id="target" style="border:2px green solid; width:200px; height:50px"></input>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransferitem-interface/getAsString-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransferitem-interface/getAsString-manual.html
new file mode 100644
index 0000000000..c328f0031b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-datatransferitem-interface/getAsString-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>DataTransferItem Test: getAsString()</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p><input type="text" value="dragcharacters" style="border:2px blue solid; width:200px; height: 100px;"/></p>
+<p><input id="container" type="text" style="border:2px green solid; width:200px; height: 100px;"/></p>
+
+<p>Select all characters in blue box and drag to green box then drop on the green box</p>
+
+<script>
+
+setup({explicit_done : true, explicit_timeout : true});
+
+let container = document.getElementById("container");
+
+on_event(container, "drop", evt => {
+ let item = evt.dataTransfer.items[0];
+
+ test(() => {
+ let file1 = item.getAsFile();
+ assert_equals(file1, null);
+ }, "Check if DataTransferItem.getAsFile return null if drag data item kind is not File");
+
+ let data;
+ item.getAsString(str => {
+ data = str;
+ });
+ setTimeout(() => {
+ test(() => {
+ assert_equals(data, "dragcharacters");
+ }, "Check if DataTransferItem.getAsString return the dragged string");
+ done();
+ }, 0);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dragevent-interface/dragevent-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dragevent-interface/dragevent-manual.html
new file mode 100644
index 0000000000..e4d754e459
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dragevent-interface/dragevent-manual.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: DragEvent</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#dndevents'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #drag {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='drag' draggable>blue text</div>
+ <div id='drop' dropzone='copy string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drag, element;
+ var Events = ['ondragstart', 'ondrag', 'ondragover', 'ondragenter', 'ondragleave', 'ondrop', 'ondragend'];
+
+ setup(function() {
+ drag = document.querySelector('#drag');
+ element = document.createElement('div');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ for(var i=0; i< Events.length; i++) {
+ test(function() {
+ assert_true(Events[i] in document, 'Check ' + Events[i] + ' in document');
+ }, 'Check ' + Events[i] + ' in document');
+ }
+
+ test(function() {
+ assert_inherits(element, 'ondragstart', 'Check if have ondragstart attribute');
+ }, 'Check if have ondragstart attribute');
+
+ test(function() {
+ assert_inherits(element, 'ondrag', 'Check if have ondrag attribute');
+ }, 'Check if have ondrag attribute');
+
+ test(function() {
+ assert_inherits(element, 'ondragenter', 'Check if have ondragenter attribute');
+ }, 'Check if have ondragenter attribute');
+
+ test(function() {
+ assert_inherits(element, 'ondragleave', 'Check if have dragleave attribute');
+ }, 'Check if have dragleave attribute');
+
+ test(function() {
+ assert_inherits(element, 'ondragover', 'Check if have dragover attribute');
+ }, 'Check if have dragover attribute');
+
+ test(function() {
+ assert_inherits(element, 'ondrop', 'Check if have ondrop attribute');
+ }, 'Check if have ondrop attribute');
+
+ test(function() {
+ assert_inherits(element, 'ondragend', 'Check if have ondragend attribute');
+ }, 'Check if have ondragend attribute');
+
+ on_event(drag, 'dragstart', function(event) {
+ test(function() {
+ assert_equals(event.type, 'dragstart', 'Check if the dragstart event captured');
+ }, 'Check if the dragstart event captured');
+ });
+
+ on_event(drag, 'dragenter', function(event) {
+ test(function() {
+ assert_equals(event.type, 'dragenter', 'Check if the dragenter event captured');
+ }, 'Check if the dragenter event captured');
+ });
+
+ on_event(drag, 'dragend', function(event) {
+ test(function() {
+ assert_equals(event.type, 'dragend', 'Check if the dragend event captured');
+ }, 'Check if the dragend event captured');
+ done();
+ });
+
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..8c33a6c25b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#the-draggable-attribute">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="@draggable values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!--
+ We use <img> elements here so the invalid value default (auto) can be
+ distinguished from false through the IDL attribute. Most other elements
+ return false from the IDL attribute for the auto state too.
+-->
+<img draggable="false">
+<img draggable="FaLsE">
+<img draggable="falſe">
+<script>
+const img = document.querySelectorAll("img");
+
+test(() => {
+ assert_equals(img[0].draggable, false, "lowercase valid");
+ assert_equals(img[1].draggable, false, "mixed case valid");
+ assert_equals(img[2].draggable, true, "non-ASCII invalid");
+}, "keyword false");
+</script>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable_attribute.html b/testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable_attribute.html
new file mode 100644
index 0000000000..cd9073e105
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-draggable-attribute/draggable_attribute.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: draggable_attribute</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-draggable-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <script src='/html/semantics/interfaces.js'></script>
+ </head>
+
+ <body>
+ <div id='log'> </div>
+
+ <script>
+ elements.forEach(function(a) {
+ test(function() {
+ var eElement = document.createElement(a[0]);
+ assert_inherits(eElement, 'draggable', 'Element ' + a[0] +' should have draggable property');
+ }, 'Element ' + a[0] +' should have draggable property');
+ });
+
+ function run_test(element, element_name, exp) {
+ if (exp) {
+ assert_true(element.draggable, 'Element ' + element_name +' should be draggable');
+ } else {
+ assert_false(element.draggable, 'Element ' + element_name +' should not be draggable');
+ }
+ }
+
+ function run_idl_test(element, element_name, exp) {
+ if (exp) {
+ assert_equals(element.getAttribute('draggable'), 'true', 'Element ' + element_name +' should be draggable');
+ } else {
+ assert_equals(element.getAttribute('draggable'), 'false', 'Element ' + element_name +' should not be draggable');
+ }
+ }
+
+ elements.forEach(function(a) {
+
+ test(function() {
+ //Default values for elements
+ //If the element is an img element, or, if the element is an a element with an href content attribute,
+ //the draggable IDL attribute must return true.
+ var eElement = document.createElement(a[0]);
+ switch (a[0]) {
+ case 'a':
+ eElement.setAttribute('href', 'http://w3.org');
+ run_test(eElement, 'a', true);
+ break;
+ case 'img':
+ run_test(eElement, 'img', true);
+ break;
+ default:
+ run_test(eElement, a[0], false);
+ }
+
+ //If an element's draggable content attribute has the state true,
+ //the draggable IDL attribute must return true.
+ eElement.setAttribute('draggable', 'true');
+ run_test(eElement, a[0] + ' draggable=\'true\'', true);
+
+ //If an element's draggable content attribute has the state false,
+ //the draggable IDL attribute must return false.
+ eElement.setAttribute('draggable', 'false');
+ run_test(eElement, a[0] + ' draggable=\'false\'', false);
+
+ //auto values for elements
+ //The element's draggable content attribute has the state auto.
+ //If the element is an img element, or, if the element is an a element with an href content attribute,
+ //the draggable IDL attribute must return true.
+ switch (a[0]) {
+ case 'a':
+ eElement.setAttribute('href', 'http://w3.org');
+ eElement.setAttribute('draggable', 'auto');
+ run_test(eElement, 'Element ' + 'a' + ' draggable=\'auto\'', true);
+ break;
+ case 'img':
+ eElement.setAttribute('draggable', 'auto');
+ run_test(eElement, 'Element ' + 'img' + ' draggable=\'auto\'', true);
+ break;
+ default:
+ run_test(eElement, 'Element ' + a[0] + ' draggable=\'auto\'', false);
+ }
+
+ //Foo values for elements
+ //The element's draggable content attribute value is not enumerated (true, false, auto) but unexpected.
+ //Fallback to defaults
+ switch (a[0]) {
+ case 'a':
+ eElement.setAttribute('href', 'http://w3.org');
+ eElement.setAttribute('draggable', 'foo');
+ run_test(eElement, 'Element ' + 'a' + ' draggable=\'foo\'', true);
+ break;
+ case 'img':
+ eElement.setAttribute('draggable', 'foo');
+ run_test(eElement, 'Element ' + 'img' + ' draggable=\'foo\'', true);
+ break;
+ default:
+ run_test(eElement, 'Element ' + a[0] + ' draggable=\'foo\'', false);
+ }
+
+ //An element with a draggable attribute should also have a title attribute
+ //that names the element for the purpose of non-visual interactions.
+ eElement.setAttribute('title', 'foo as title value');
+ assert_equals(typeof eElement.title, 'string', '<' + a[0] + '> draggable block has title attribute');
+
+ //If the draggable IDL attribute is set to the value false,
+ //the draggable content attribute must be set to the literal value false.
+ eElement.draggable = false;
+ run_idl_test(eElement, a[0] + '.getAttribute(\'draggable\') is \'false\'', false);
+
+ //If the draggable IDL attribute is set to the value true,
+ //the draggable content attribute must be set to the literal value true.
+ eElement.draggable = true;
+ run_idl_test(eElement, a[0] + '.getAttribute(\'draggable\') is \'true\'', true);
+ }, 'Element ' + a[0] +' draggable attribute test');
+
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute.html
new file mode 100644
index 0000000000..feb73eccfe
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <meta name="flags" content="may">
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ </head>
+
+ <body>
+ <div id='log'> </div>
+
+ <script>
+ var drop_element;
+
+ setup(function() { drop_element = document.createElement('div'); });
+
+ test(function() {
+ //Empty values for elements
+ drop_element.dropzone = '';
+ assert_not_equals(drop_element.dropzone, undefined, 'div.dropzone should not be undefined if it\'s been set');
+ }, 'div.dropzone should not be undefined if it\'s been set');
+
+ test(function() {
+ drop_element.dropzone = null;
+ assert_not_equals(drop_element.dropzone, null, 'div.dropzone should not be null');
+ }, 'div.dropzone should not be null');
+
+ test(function() {
+ //The dropzone IDL attribute must reflect the content attribute of the same name.
+ drop_element.setAttribute('dropzone', 'copy file:image/png file:image/gif file:image/jpeg');
+ assert_equals(drop_element.dropzone, 'copy file:image/png file:image/gif file:image/jpeg', 'div dropzone idl attribute must reflect the content attribute of the same name');
+ }, 'div dropzone idl attribute must reflect the content attribute of the same name');
+
+ test(function() {
+ //The dropzone content attribute is set to the literal value when the idl attribute value is set.
+ drop_element.dropzone = 'copy file:image/png file:image/gif file:image/jpeg';
+ assert_equals(drop_element.getAttribute('dropzone'), 'copy file:image/png file:image/gif file:image/jpeg', 'div dropzone content attribute is set to the literal value');
+ }, 'div dropzone content attribute is set to the literal value');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_file_type-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_file_type-manual.html
new file mode 100644
index 0000000000..9027559dc2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_file_type-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_data_item_file_type</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 160px;
+ height: 160px;
+ padding: 20px;
+ color: white;
+ }
+ img {
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <img src='/images/blue.png' alt='blue image' />
+ <div>Save the blue image (image/png) above to your desktop, drag the image from desktop to the blue text to rectangular box in browser.</div>
+ <div id='drop' dropzone='copy file:image/png'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ var item = event.dataTransfer.items[0];
+ test(function() {
+ assert_equals(item.type, 'image/png', 'data item type is "image/png"');
+ }, 'data item type is "image/png"');
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_file-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_file-manual.html
new file mode 100644
index 0000000000..7a77c12414
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_file-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_data_item_kind_file</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 160px;
+ height: 160px;
+ padding: 20px;
+ color: white;
+ }
+ img {
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <img src='/images/blue.png' alt='blue image' />
+ <div>Save the blue image (image/png) above to your desktop, drag the image from desktop to the blue text to rectangular box in browser.</div>
+ <div id='drop' dropzone='copy file:image/png'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ var item = event.dataTransfer.items[0];
+ test(function() {
+ assert_equals(item.kind, 'file', 'data item kind is file');
+ }, 'data item kind is file');
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_string-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_string-manual.html
new file mode 100644
index 0000000000..b8a22e613b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_kind_string-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_data_item_kind_string</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='copy string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ var item = event.dataTransfer.items[0];
+ test(function() {
+ assert_equals(item.kind, 'string', 'data item kind is string');
+ }, 'data item kind is string');
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_string_type-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_string_type-manual.html
new file mode 100644
index 0000000000..7a5dccf4e2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_data_item_string_type-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_data_item_string_type</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='copy string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ var item = event.dataTransfer.items[0];
+ test(function() {
+ assert_equals(item.type, 'text/plain', 'data item type is "text/plain"');
+ }, 'data item type is "text/plain"');
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element-manual.html
new file mode 100644
index 0000000000..f80604afa2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element-manual.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_inputbox_element</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ input {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select all the inputbox text then drag to rectangular box.</div>
+ <input draggable='true' type='text' value='hello world'></input>
+ <div id='drop' dropzone='move string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'move', 'dropzone content attribute value is "move"');
+ }, 'dropzone content attribute value is "move"');
+
+ test(function() {
+ var item = event.dataTransfer.items[0];
+ assert_equals(event.dataTransfer.getData(item.type), 'hello world', 'The dropped string value is the inputbox text you selected.');
+ }, 'The dropped string value is the inputbox text you selected.');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element_dbcs-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element_dbcs-manual.html
new file mode 100644
index 0000000000..a7f8ad1aa7
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_inputbox_element_dbcs-manual.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_inputbox_element_dbcs</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ input {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select all the inputbox DBCS (Double Byte Character Set) text then drag to rectangular box.</div>
+ <input draggable='true' type='text' value='您好,互è”网。'></input>
+ <div id='drop' dropzone='move string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ var item = item = event.dataTransfer.items[0];
+ assert_equals(event.dataTransfer.getData(item.type), '您好,互è”网。', 'The dropped string value is the inputbox text you selected.');
+ }, 'The dropped string value is the inputbox text you selected.');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_link_element-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_link_element-manual.html
new file mode 100644
index 0000000000..c6e5756ad6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_link_element-manual.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_element_link</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ div { margin: 20px 0px;}
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the all the text of link below to rectangular box.</div>
+ <a href="http://w3.org" title="World Wide Web Consortium">w3.org</a>
+ <div id='drop' dropzone='link string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'link', 'dropzone content attribute value is "link"');
+ }, 'dropzone content attribute value is "link"');
+
+ test(function() {
+ var item = item = event.dataTransfer.items[0];
+ assert_equals(event.dataTransfer.getData(item.type), 'w3.org', 'The dropped link value is "w3.org"');
+ }, 'The dropped link value is "w3.org"');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_COpy-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_COpy-manual.html
new file mode 100644
index 0000000000..17ec7a36a3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_COpy-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_case_insensitive_COpy</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='COpy string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'copy', 'dropzone content attribute value is case insensitive_COpy');
+ }, 'dropzone content attribute value is case insensitive_COpy');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_STRING-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_STRING-manual.html
new file mode 100644
index 0000000000..f118ba00bc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_STRING-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_case_insensitive_STRING</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='copy STRING:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'copy', 'dropzone content attribute value is case insensitive_STRING');
+ }, 'dropzone content attribute value is case insensitive_STRING');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_String_-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_String_-manual.html
new file mode 100644
index 0000000000..9b84803806
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_String_-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_case_insensitive_String</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='link String:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'link', 'dropzone content attribute value is case insensitive_String');
+ }, 'dropzone content attribute value is case insensitive_String');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_linK-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_linK-manual.html
new file mode 100644
index 0000000000..e4364db735
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_case_insensitive_linK-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_case_insensitive_linK</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='liNK string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'link', 'dropzone content attribute value is case insensitive_linK');
+ }, 'dropzone content attribute value is case insensitive_linK');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_copy-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_copy-manual.html
new file mode 100644
index 0000000000..dcc7c97acd
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_copy-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_copy</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='copy string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'copy', 'dropzone content attribute value is "copy"');
+ }, 'dropzone content attribute value is "copy"');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_foo-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_foo-manual.html
new file mode 100644
index 0000000000..f9606f1b9f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_foo-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_foo</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='foo string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'copy', 'dropzone content attribute value is "copy" if it\'s not one of three feedback values (copy, move, and link) specified');
+ }, 'dropzone content attribute value is "copy" if it\'s not one of three feedback values (copy, move, and link) specified');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_link-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_link-manual.html
new file mode 100644
index 0000000000..a4ba819656
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_link-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_link</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='link string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'link', 'dropzone content attribute value is "link"');
+ }, 'dropzone content attribute value is "link"');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_move-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_move-manual.html
new file mode 100644
index 0000000000..dfad88ba61
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_move-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_move</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='move string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'move', 'dropzone content attribute value is "move"');
+ }, 'dropzone content attribute value is "move"');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_bar_move-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_bar_move-manual.html
new file mode 100644
index 0000000000..3243774c93
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_bar_move-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_multiple_values_foo_bar_move</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='foo bar move string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'move', 'dropzone content attribute value is the first matched value of "foo bar move"');
+ }, 'dropzone content attribute value is the first matched value of "foo bar move"');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_link-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_link-manual.html
new file mode 100644
index 0000000000..fd9e23d03a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_foo_link-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_multiple_values_foo_link</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='foo link string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'link', 'dropzone content attribute value is the first matched value of "foo link"');
+ }, 'dropzone content attribute value is the first matched value of "foo link"');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_move_copy-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_move_copy-manual.html
new file mode 100644
index 0000000000..50ea9369f2
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_multiple_values_move_copy-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_multiple_values_move_copy</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='move copy string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'move', 'dropzone content attribute value is the first matched value of "move copy"');
+ }, 'dropzone content attribute value is the first matched value of "move copy"');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_not_specified-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_not_specified-manual.html
new file mode 100644
index 0000000000..152c30022b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_not_specified-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_not_specified</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='string:text/plain'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'copy', 'dropzone content attribute value is "copy" if it\'s not specified');
+ }, 'dropzone content attribute value is "copy" if it\'s not specified');
+
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_unordered_unique_space_separated-manual.html b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_unordered_unique_space_separated-manual.html
new file mode 100644
index 0000000000..24c5b032ab
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/dnd/the-dropzone-attribute/dropzone_attribute_value_unordered_unique_space_separated-manual.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>HTML Test: dropzone_attribute_value_unordered_unique_space_separated</title>
+ <link rel='author' title='Intel' href='http://www.intel.com'>
+ <link rel='help' href='https://html.spec.whatwg.org/multipage/#the-dropzone-attribute'>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+ <style>
+ #drop {
+ border: 2px solid black;
+ width: 100px;
+ height: 100px;
+ padding: 20px;
+ }
+ #select {
+ color: blue;
+ margin: 20px auto;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div>Select and drag the blue text to rectangular box.</div>
+ <div id='select' draggable>blue text</div>
+ <div id='drop' dropzone='string:text/plain copy'></div>
+ <div id='log'> </div>
+
+ <script>
+ var drop;
+ setup(function() {
+ drop = document.querySelector('#drop');
+ }, {explicit_done: true, explicit_timeout: true});
+
+ on_event(drop, 'drop', function(event) {
+
+ test(function() {
+ assert_equals(event.dataTransfer.dropEffect, 'copy', 'unordered_unique_space_separated "string:text/plain copy", drop effect is copy');
+ }, 'dropzone content attribute value is unordered_unique_space_separated "string:text/plain copy", drop effect is copy');
+ test(function() {
+ item = event.dataTransfer.items[0];
+ assert_equals(item.kind, 'string', 'unordered_unique_space_separated "string:text/plain copy", data item kind is string');
+ }, 'dropzone content attribute value is unordered_unique_space_separated "string:text/plain copy", data item kind is string');
+ test(function() {
+ item = event.dataTransfer.items[0];
+ assert_equals(item.type, 'text/plain', 'dropzone content attribute value is unordered_unique_space_separated "string:text/plain copy", data item type is "text/plain"');
+ }, 'dropzone content attribute value is unordered_unique_space_separated "string:text/plain copy", data item type is "text/plain"');
+ done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/editing-0/autocapitalization/autocapitalize.html b/testing/web-platform/tests/html/editing/editing-0/autocapitalization/autocapitalize.html
new file mode 100644
index 0000000000..49ee14329c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/autocapitalization/autocapitalize.html
@@ -0,0 +1,688 @@
+<!DOCTYPE html>
+<html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#autocapitalization">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+test(function() {
+ assert_true('autocapitalize' in document.createElement('input'));
+}, "Test that the autocapitalize is available on HTMLInputElement.")
+
+test(function() {
+ assert_true('autocapitalize' in document.createElement('textarea'));
+}, "Test that the autocapitalize is available on HTMLTextAreaElement.")
+
+test(function() {
+ assert_true('autocapitalize' in document.createElement('div'));
+}, "Test that the autocapitalize is available on div.")
+
+test(function() {
+ var elements = [ document.createElement('input'),
+ document.createElement('textarea'),
+ document.createElement('div') ];
+
+ elements.forEach(function(e) {
+ e.autocapitalize = 'on';
+ assert_equals(e.autocapitalize, 'sentences');
+
+ e.autocapitalize = 'off';
+ assert_equals(e.autocapitalize, 'none');
+ });
+}, "Test deprecated values of autocapitalize.");
+
+test(function() {
+ var elements = [ document.createElement('input'),
+ document.createElement('textarea'),
+ document.createElement('div') ];
+ var knownValues = [ 'none', 'characters', 'words', 'sentences' ];
+
+ elements.forEach(function(e) {
+ // Default value.
+ assert_equals(e.autocapitalize, '');
+
+ // Empty value.
+ e.autocapitalize = '';
+ assert_equals(e.autocapitalize, '');
+ assert_equals(e.getAttribute('autocapitalize'), '');
+ e.setAttribute('autocapitalize', '');
+ assert_equals(e.autocapitalize, '');
+ assert_equals(e.getAttribute('autocapitalize'), '');
+ assert_equals(e.autocapitalize, '');
+
+ // Invalid value.
+ e.autocapitalize = 'foo';
+ assert_equals(e.autocapitalize, 'sentences');
+ assert_equals(e.getAttribute('autocapitalize'), 'foo');
+ e.setAttribute('autocapitalize', 'bar');
+ assert_equals(e.autocapitalize, 'sentences');
+ assert_equals(e.getAttribute('autocapitalize'), 'bar');
+
+ // Default value.
+ e.removeAttribute('autocapitalize');
+ assert_equals(e.autocapitalize, '');
+ assert_equals(e.getAttribute('autocapitalize'), null);
+
+ // Case insensitive.
+ e.setAttribute('autocapitalize', 'NoNe');
+ assert_equals(e.autocapitalize, 'none');
+ assert_equals(e.getAttribute('autocapitalize'), 'NoNe');
+ e.autocapitalize = 'WORDS';
+ assert_equals(e.autocapitalize, 'words');
+ assert_equals(e.getAttribute('autocapitalize'), 'WORDS');
+
+ knownValues.forEach(function(value) {
+ e.setAttribute('autocapitalize', value);
+ assert_equals(e.autocapitalize, value);
+ assert_equals(e.getAttribute('autocapitalize'), value);
+
+ e.removeAttribute('autocapitalize');
+
+ e.autocapitalize = value;
+ assert_equals(e.autocapitalize, value);
+ assert_equals(e.getAttribute('autocapitalize'), value);
+
+ e.removeAttribute('autocapitalize');
+ });
+ });
+}, "Test reflection of autocapitalize.");
+
+test(function() {
+var testData = [ 'text',
+ 'search',
+ 'email',
+ 'url',
+ 'tel',
+ 'number',
+ 'date',
+ 'color',
+ 'password' ];
+
+ testData.forEach(function(data) {
+ const input = document.createElement('input');
+ input.type = data;
+ assert_equals(input.autocapitalize, '');
+
+ // Verify that wrapping the input element in a form doesn't change the
+ // defaults.
+ const form = document.createElement('form');
+ form.appendChild(input);
+ assert_equals(input.autocapitalize, '');
+ });
+}, "Test that the IDL attribute returns the empty string if the content "
++ "attribute is not set.")
+
+test(function() {
+ const testData = [
+ {
+ formValue: null,
+ formElementValue: null,
+ inheritedResult: '',
+ uninheritedResult: '',
+ },
+ {
+ formValue: null,
+ formElementValue: '',
+ inheritedResult: '',
+ uninheritedResult: '',
+ },
+ {
+ formValue: null,
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: null,
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: null,
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: null,
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: null,
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: null,
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: null,
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: '',
+ formElementValue: null,
+ inheritedResult: '',
+ uninheritedResult: '',
+ },
+ {
+ formValue: '',
+ formElementValue: '',
+ inheritedResult: '',
+ uninheritedResult: '',
+ },
+ {
+ formValue: '',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: '',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: '',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: '',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: '',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: '',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: '',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'on',
+ formElementValue: null,
+ inheritedResult: 'sentences',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'on',
+ formElementValue: '',
+ inheritedResult: 'sentences',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'on',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'on',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'on',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'on',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: 'on',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: 'on',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'on',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'off',
+ formElementValue: null,
+ inheritedResult: 'none',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'off',
+ formElementValue: '',
+ inheritedResult: 'none',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'off',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'off',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'off',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'off',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: 'off',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: 'off',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'off',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'none',
+ formElementValue: null,
+ inheritedResult: 'none',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'none',
+ formElementValue: '',
+ inheritedResult: 'none',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'none',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'none',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'none',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'none',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: 'none',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: 'none',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'none',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: null,
+ inheritedResult: 'characters',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: '',
+ inheritedResult: 'characters',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'characters',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'words',
+ formElementValue: null,
+ inheritedResult: 'words',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'words',
+ formElementValue: '',
+ inheritedResult: 'words',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'words',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'words',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'words',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'words',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: 'words',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: 'words',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'words',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: null,
+ inheritedResult: 'sentences',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: '',
+ inheritedResult: 'sentences',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'sentences',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: null,
+ inheritedResult: 'sentences',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: '',
+ inheritedResult: 'sentences',
+ uninheritedResult: '',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: 'on',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: 'off',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: 'none',
+ inheritedResult: 'none',
+ uninheritedResult: 'none',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: 'characters',
+ inheritedResult: 'characters',
+ uninheritedResult: 'characters',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: 'words',
+ inheritedResult: 'words',
+ uninheritedResult: 'words',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: 'sentences',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ {
+ formValue: 'foo',
+ formElementValue: 'foo',
+ inheritedResult: 'sentences',
+ uninheritedResult: 'sentences',
+ },
+ ];
+
+ const formElements = [
+ {element: 'button', inherits: true},
+ {element: 'fieldset', inherits: true},
+ {element: 'img', inherits: false},
+ {element: 'input', inherits: true},
+ {element: 'object', inherits: false},
+ {element: 'output', inherits: true},
+ {element: 'select', inherits: true},
+ {element: 'textarea', inherits: true},
+ ];
+
+ const form = document.createElement('form');
+ form.id = 'form';
+ document.body.appendChild(form);
+
+ testData.forEach(data => {
+ form.removeAttribute('autocapitalize');
+
+ if (data.formValue !== null) {
+ form.setAttribute('autocapitalize', data.formValue);
+ }
+
+ formElements.forEach(elementData => {
+ const element = document.createElement(elementData.element);
+ form.appendChild(element);
+
+ const element2 = document.createElement(elementData.element);
+ element2.setAttribute('form', 'form');
+ document.body.appendChild(element2);
+
+ if (data.formElementValue !== null) {
+ element.setAttribute('autocapitalize', data.formElementValue);
+ element2.setAttribute('autocapitalize', data.formElementValue);
+ }
+
+ const descriptionSuffix = 'with "' + data.formValue
+ + '" and form element with "'+ data.formElementValue + '"';
+
+ if (elementData.inherits) {
+ assert_equals(element.autocapitalize, data.inheritedResult,
+ `${elementData.element} element with form parent `
+ + `${descriptionSuffix}`);
+ assert_equals(element2.autocapitalize, data.inheritedResult,
+ `${elementData.element} element with form owner attribute`
+ + ` set ${descriptionSuffix}`);
+ } else {
+ assert_equals(element.autocapitalize, data.uninheritedResult,
+ `${elementData.element} element with form parent `
+ + `${descriptionSuffix}`);
+ assert_equals(element2.autocapitalize, data.uninheritedResult,
+ `${elementData.element} element with form owner attribute`
+ + `set ${descriptionSuffix}`);
+ }
+ });
+ });
+}, "Test inheriting values from a form.")
+
+test(function() {
+ const testData = [ 'text',
+ 'search',
+ 'email',
+ 'url',
+ 'tel',
+ 'number',
+ 'date',
+ 'color',
+ 'password' ];
+
+ testData.forEach(function(data) {
+ const form = document.createElement('form');
+ form.setAttribute('autocapitalize', 'sentences');
+ const input = document.createElement('input');
+ input.setAttribute('type', data);
+ form.appendChild(input);
+
+ assert_equals(input.autocapitalize, 'sentences');
+ });
+}, "Verify that even input types that are never autocapitalized support the "
++ "IDL interface.")
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html
new file mode 100644
index 0000000000..42da515920
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>contentEditable inherit from light tree parent</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#contenteditable">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>You should see the word PASS two times below and no FAIL.</p>
+<div id="host1" contenteditable><div>FAILPASS</div></div>
+<div id="host2" contenteditable><div>FAILPASS</div></div>
+<script>
+ test(() => {
+ const root = host1.attachShadow({mode:"open"});
+ root.innerHTML = "<slot></slot>";
+ const text = host1.firstChild.firstChild;
+ const selection = window.getSelection();
+ selection.collapse(text, 0);
+ selection.extend(text, 4);
+ host1.focus();
+ document.execCommand("delete");
+ host1.blur();
+ assert_equals(text.data, "PASS", "Text should be PASS after FAIL is deleted");
+ }, "Slotted child of contenteditable host should be editable - slot direct child of shadow root");
+
+ test(() => {
+ const root = host2.attachShadow({mode:"open"});
+ root.innerHTML = "<div><slot></slot></div>";
+ const text = host2.firstChild.firstChild;
+ const selection = window.getSelection();
+ selection.collapse(text, 0);
+ selection.extend(text, 4);
+ host2.focus();
+ document.execCommand("delete");
+ host2.blur();
+ assert_equals(text.data, "PASS", "Text should be PASS after FAIL is deleted");
+ }, "Slotted child of contenteditable host should be editable - slot wrapped in shadow tree ancestor");
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..0125b95667
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-contenteditable">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="@contenteditable values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+function testValue(value, isValid) {
+ const valueLower = value.toLowerCase();
+
+ test(() => {
+ const el = document.createElement('div');
+ if (valueLower !== "inherit") {
+ el.setAttribute('contenteditable', value);
+ }
+ assert_equals(el.contentEditable, isValid ? valueLower : "inherit");
+ }, `IDL attribute getter for attribute value "${value}"`);
+
+ test(() => {
+ const el = document.createElement('div');
+ if (isValid) {
+ el.contentEditable = value;
+ assert_equals(el.getAttribute('contenteditable'), valueLower === "inherit" ? null : valueLower);
+ } else {
+ assert_throws_dom("SyntaxError", () => {
+ el.contentEditable = value;
+ });
+ }
+ }, `IDL attribute setter for value "${value}"`);
+}
+
+const valid = ["true", "false", "inherit", "plaintext-only"]; // "inherit" is treated specially
+const invalid = ["foobar", "falſe", "plaıntext-only", "plaİntext-only"];
+
+for (const value of valid) {
+ testValue(value, true);
+ testValue(value.toUpperCase(), true);
+}
+
+for (const value of invalid) {
+ testValue(value, false);
+}
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height-ref.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height-ref.html
new file mode 100644
index 0000000000..e88e904f96
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>CSS test reference</title>
+<style>
+ [contenteditable] {
+ outline: 1px solid black;
+ outline-offset: -1px;
+ }
+</style>
+<div contenteditable></div>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height.html
new file mode 100644
index 0000000000..8470b02c2b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-overflow-height.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Overflow still allows contenteditable elements to have height</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1681375">
+<link rel="match" href="contenteditable-overflow-height-ref.html">
+<style>
+ [contenteditable] {
+ outline: 1px solid black;
+ outline-offset: -1px;
+ overflow: hidden;
+ }
+</style>
+<div contenteditable></div>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block-ref.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block-ref.html
new file mode 100644
index 0000000000..fe68571013
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Test reference</title>
+<div>
+ Foo
+ <div></div>
+ Bar
+</div>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block.html
new file mode 100644
index 0000000000..7bcc611072
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/contenteditable-with-empty-block.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>contenteditable doesn't cause inner empty blocks to grow.</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1628770">
+<link rel="match" href="contenteditable-with-empty-block-ref.html">
+<div contenteditable>
+ Foo
+ <div></div>
+ Bar
+</div>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/selection-in-contentEditable-at-turning-designMode-on-off.tentative.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/selection-in-contentEditable-at-turning-designMode-on-off.tentative.html
new file mode 100644
index 0000000000..4ef9d9003d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/selection-in-contentEditable-at-turning-designMode-on-off.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>selection in contenteditable should not be changed when designMode is turned on/off</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe srcdoc="<body contenteditable>abc</body>"></iframe>
+<script>
+ const test_load = async_test("Selection in contenteditable shouldn't be reinitialized when changing designMode");
+ window.addEventListener("load", test_load.step_func_done(() => {
+ let iframe = document.querySelector("iframe");
+ let iframeSelection = iframe.contentDocument.getSelection();
+ iframe.focus();
+ iframeSelection.collapse(iframe.contentDocument.body, 1);
+ function summariseRange(range) {
+ if (!range) {
+ return "null";
+ }
+ return `(${range.startContainer.nodeName}, ${range.startOffset}) - (${range.endContainer.nodeName}, ${range.endOffset})`;
+ }
+ let maybeNormalizedRangeSummary = summariseRange(iframeSelection.getRangeAt(0));
+ assert_in_array(maybeNormalizedRangeSummary, ["(BODY, 1) - (BODY, 1)", "(#text, 3) - (#text, 3)"],
+ "Selection collapsed at end of <body> can be either as-is or normalized to the end of the text node");
+ iframe.contentDocument.designMode = "on";
+ assert_equals(summariseRange(iframeSelection.getRangeAt(0)), maybeNormalizedRangeSummary,
+ "Turning designMode on at load event shouldn't change selection in contenteditable");
+ iframe.contentDocument.designMode = "off";
+ assert_equals(summariseRange(iframeSelection.getRangeAt(0)), maybeNormalizedRangeSummary,
+ "Turning designMode off at load event shouldn't change selection in contenteditable");
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/contenteditable/user-interaction-editing-contenteditable.html b/testing/web-platform/tests/html/editing/editing-0/contenteditable/user-interaction-editing-contenteditable.html
new file mode 100644
index 0000000000..19b21863e0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/contenteditable/user-interaction-editing-contenteditable.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Editing: contentEditable attribute test</title>
+ <link rel="author" title="Baidu" href="mailto: guopengcheng@baidu.com" />
+ <link
+ rel="help"
+ href="https://html.spec.whatwg.org/multipage/#contenteditable"
+ />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <div id="log"></div>
+ </head>
+ <body>
+ <script>
+ function testContentEditable(variationFunc, title, expectIsContentEditable, expectContentEditable) {
+ test(() => {
+ const div = document.createElement("div");
+ variationFunc(div);
+ assert_equals(div.isContentEditable, expectIsContentEditable, 'isContentEditable');
+ assert_equals(div.contentEditable, expectContentEditable, 'contentEditable');
+ }, title);
+ }
+
+ testContentEditable(el => {
+ }, "no contenteditable attribute", false, "inherit");
+
+ testContentEditable(el => {
+ el.setAttribute("contenteditable", "");
+ }, "empty contentEditable attribute", true, "true");
+
+ testContentEditable(el => {
+ el.contentEditable = "true";
+ }, 'set contentEditable = "true"', true, "true");
+
+ testContentEditable(el => {
+ el.contentEditable = "false";
+ }, 'set contentEditable = "false"', false, "false");
+
+ testContentEditable(el => {
+ const parent = document.createElement("div");
+ parent.appendChild(el);
+ parent.contentEditable = "true";
+ }, 'set parent element contentEditable = "true"', true, "inherit");
+
+ testContentEditable(el => {
+ const parent = document.createElement("div");
+ parent.appendChild(el);
+ parent.contentEditable = "false";
+ }, 'set parent element contentEditable = "false"', false, "inherit");
+
+ testContentEditable(el => {
+ el.contentEditable = "true";
+ el.removeAttribute("contenteditable");
+ }, 'set contentEditable = "true" and then remove contenteditable attribute', false, "inherit");
+
+ testContentEditable(el => {
+ el.setAttribute("contenteditable", "plaintext-only");
+ }, "contentEditable=plaintext-only attribute", true, "plaintext-only");
+
+ testContentEditable(el => {
+ const parent = document.createElement("div");
+ parent.appendChild(el);
+ parent.contentEditable = "plaintext-only";
+ }, 'set parent element contentEditable = "plaintext-only"', true, "inherit");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/original-id.json b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/original-id.json
new file mode 100644
index 0000000000..8b7c4b838c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/original-id.json
@@ -0,0 +1 @@
+{"original_id":"making-entire-documents-editable:-the-designmode-idl-attribute"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg
new file mode 100644
index 0000000000..fd2fde0fbe
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg:svg xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/1999/xhtml"
+ width="100%" height="100%" viewBox="0 0 800 600">
+ <svg:title>Editing: designMode attribute test</svg:title>
+ <head>
+ <link rel="author" title="Baidu" href="mailto: guopengcheng@baidu.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#making-entire-documents-editable:-the-designmode-idl-attribute"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <div id="log"></div>
+ </head>
+ <body>
+ <script type="text/javascript"><![CDATA[
+ test(function() {
+ assert_equals(document.designMode, "off", "check for designMode value");
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandSupported("delete") });
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+ }, "initial designMode attribute");
+ document.designMode="on";
+ test(function() {
+ assert_equals(document.designMode, "on", "check for designMode value");
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandSupported("delete") });
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+ }, "set designMode = \"on\"");
+ document.designMode="off";
+ test(function() {
+ assert_equals(document.designMode,"off", "check for designMode value");
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandSupported("delete") });
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+ }, "set designMode = \"off\"");
+ ]]></script>
+ </body>
+</svg:svg>
diff --git a/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml
new file mode 100644
index 0000000000..f26cd56453
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Editing: designMode attribute test</title>
+ <link rel="author" title="Baidu" href="mailto: guopengcheng@baidu.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#making-entire-documents-editable:-the-designmode-idl-attribute"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <div id="log"></div>
+ </head>
+ <body>
+ <script type="text/javascript"><![CDATA[
+ test(function() {
+ assert_equals(document.designMode, "off", "check for designMode value");
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandSupported("delete") });
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+ }, "initial designMode attribute");
+ document.designMode="on";
+ test(function() {
+ assert_equals(document.designMode, "on", "check for designMode value");
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandSupported("delete") });
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+ }, "set designMode = \"on\"");
+ document.designMode="off";
+ test(function() {
+ assert_equals(document.designMode,"off", "check for designMode value");
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandSupported("delete") });
+ assert_throws_dom("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+ }, "set designMode = \"off\"");
+ ]]></script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html
new file mode 100644
index 0000000000..79d2dc8ba0
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Editing: designMode attribute test</title>
+ <link rel="author" title="Baidu" href="mailto: guopengcheng@baidu.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#making-entire-documents-editable:-the-designmode-idl-attribute"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <div id="log"></div>
+ </head>
+ <body>
+ <script type="text/javascript">
+ test(function() {
+ assert_equals(document.designMode, "off", "check for designMode value");
+ assert_true(document.queryCommandSupported("delete"));
+ assert_false(document.queryCommandEnabled("delete"));
+ }, "initial designMode attribute");
+ document.designMode="on";
+ test(function() {
+ assert_equals(document.designMode, "on", "check for designMode value");
+ assert_true(document.queryCommandSupported("delete"));
+ assert_true(document.queryCommandEnabled("delete"));
+ }, "set designMode = \"on\"");
+ document.designMode="off";
+ test(function() {
+ assert_equals(document.designMode,"off", "check for designMode value");
+ assert_true(document.queryCommandSupported("delete"));
+ assert_false(document.queryCommandEnabled("delete"));
+ }, "set designMode = \"off\"");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/references/spelling-markers-001-ref.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/references/spelling-markers-001-ref.html
new file mode 100644
index 0000000000..68dcc54702
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/references/spelling-markers-001-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Reference file for spellcheck tests</title>
+
+<div>This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.</div>
+
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spellcheck-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spellcheck-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..9f00f1dff1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spellcheck-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-spellcheck">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="@spellcheck values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!--
+ While <div> and <span> aren’t defined as “checkable for the purposes of this
+ featureâ€, this has no effect on the attribute’s state.
+
+ We wrap the <span> elements under test with <div> elements so the checking
+ enabled algorithm stops at step 4 (ancestor content attribute), before steps
+ relying on user-agent-defined behavior (see [#concept-spellcheck-default]).
+-->
+<div spellcheck="true"><span spellcheck="false"></span></div>
+<div spellcheck="true"><span spellcheck="FaLsE"></span></div>
+<div spellcheck="true"><span spellcheck="falſe"></span></div>
+<script>
+const span = document.querySelectorAll("span");
+
+test(() => {
+ assert_equals(span[0].spellcheck, false, "lowercase valid");
+ assert_equals(span[1].spellcheck, false, "mixed case valid");
+ assert_equals(span[2].spellcheck, true, "non-ASCII invalid");
+}, "keyword false");
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-001.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-001.html
new file mode 100644
index 0000000000..7dd891374b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-001.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck on editing hosts</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to editing hosts when they become non editable">
+
+<div id="test" contenteditable=true>This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.</div>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus and then blur
+ test.focus();
+ test.blur();
+ test.removeAttribute("contenteditable");
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-002.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-002.html
new file mode 100644
index 0000000000..b361b93040
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-002.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck on editable elements</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to editable elements when they become non editable">
+
+<div id="test" contenteditable=true>This test passes if there is no visual marker indicating the <span id=child>spellinnnnnggg</span> mistake in this sentence, and fails otherwise.</div>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus and then blur
+ test.focus();
+ test.blur();
+ var child = document.getElementById("child");
+ child.setAttribute("contenteditable", false);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-003.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-003.html
new file mode 100644
index 0000000000..d1a6aa3a9b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-003.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck on editing hosts while keeping them editable</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to editing hosts when the spellcheck attribute becomes false">
+
+<div id="test" spellcheck=true contenteditable=true>This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.</div>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus and then blur
+ test.focus();
+ test.blur();
+ test.setAttribute("spellcheck", false);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-004.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-004.html
new file mode 100644
index 0000000000..c718e77bb8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-004.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck on editable elements while keeping them editable</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to editable elements when the spellcheck attribute becomes false">
+
+<div id="test" spellcheck=true contenteditable=true>This test passes if there is no visual marker indicating the <span id=child>spellinnnnnggg</span> mistake in this sentence, and fails otherwise.</div>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus then blur
+ test.focus();
+ test.blur();
+ var child = document.getElementById("child");
+ child.setAttribute("spellcheck", false);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-005.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-005.html
new file mode 100644
index 0000000000..705ee7b67f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-005.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck on editable elements via an ancestor</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to editable elements when the spellcheck attribute becomes false on an ancestor">
+
+<div id="test" contenteditable=true>This test passes if there is no visual marker indicating the <span id=child><span>spellinnnnnggg</span></span> mistake in this sentence, and fails otherwise.</div>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus then blur
+ test.focus();
+ test.blur();
+ var child = document.getElementById("child");
+ child.setAttribute("spellcheck", false);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-006.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-006.html
new file mode 100644
index 0000000000..512d473f13
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-006.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck via an ancestor of the editing host</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying when the spellcheck attribute becomes false on an ancestor, without restrcting the search to the nearest editing host">
+
+<div id=parent>
+ <div id=test contenteditable=true>This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.</div>
+</div>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus then blur
+ test.focus();
+ test.blur();
+ var p = document.getElementById("parent");
+ p.setAttribute("spellcheck", false);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-007.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-007.html
new file mode 100644
index 0000000000..31b3755f3a
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-007.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck by making textareas readonly</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to textareas when they become readonly">
+
+<style>
+#test {
+ /* Match the ref */
+ all: initial;
+ width: 100%;
+ display: block;
+ font-family: inherit;
+}
+</style>
+
+<textarea id=test>This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.</textarea>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus then blur
+ test.focus();
+ test.blur();
+ test.setAttribute("readonly", true);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-008.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-008.html
new file mode 100644
index 0000000000..f891acff42
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-008.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck by making textareas disabled</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to textareas when they become disabled">
+
+<style>
+#test {
+ /* Match the ref */
+ all: initial;
+ width: 100%;
+ display: block;
+ font-family: inherit;
+}
+</style>
+
+<textarea id=test>This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.</textarea>
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus then blur
+ test.focus();
+ test.blur();
+ test.setAttribute("disabled", true);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-009.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-009.html
new file mode 100644
index 0000000000..96eb87d2f4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-009.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck by making input elements readonly</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to input elements when they become readonly">
+
+<style>
+#test {
+ /* Match the ref */
+ all: initial;
+ width: 100%;
+ display: block;
+ font-family: inherit;
+}
+</style>
+
+<input type=text id=test value="This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.">
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus then blur
+ test.focus();
+ test.blur();
+ test.setAttribute("readonly", true);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-010.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-010.html
new file mode 100644
index 0000000000..16275f10e9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/spelling-markers-010.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Turning off spellcheck by making input elements disabled</title>
+<link rel=match href="references/spelling-markers-001-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interaction.html#spelling-and-grammar-checking">
+<meta name=assert content="Spellchecking stops applying to input elements when they become disabled">
+
+<style>
+#test {
+ /* Match the ref */
+ all: initial;
+ width: 100%;
+ display: block;
+ font-family: inherit;
+}
+</style>
+
+<input type=text id=test value="This test passes if there is no visual marker indicating the spellinnnnnggg mistake in this sentence, and fails otherwise.">
+
+<script>
+ var test = document.getElementById("test");
+ // Force spellcheck by focus then blur
+ test.focus();
+ test.blur();
+ test.setAttribute("disabled", true);
+</script>
diff --git a/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/user-interaction-editing-spellcheck.html b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/user-interaction-editing-spellcheck.html
new file mode 100644
index 0000000000..c8bdaafdb8
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/spelling-and-grammar-checking/user-interaction-editing-spellcheck.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Editing: spellcheck attribute test</title>
+ <link rel="author" title="Baidu" href="mailto: guopengcheng@baidu.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#spelling-and-grammar-checking"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <textarea id="testText1" spellcheck="true">Test textarea with spellcheck is true</textarea>
+ <textarea id="testText2" spellcheck="false">Test textarea with spellcheck is false</textarea>
+ <script type="text/javascript">
+ test(function() {
+ assert_true(document.getElementById("testText1").spellcheck, "check for testText1 spellcheck value");
+ assert_false(document.getElementById("testText2").spellcheck, "check for testText2 spellcheck value");
+ }, "Getting spellcheck IDL attribute");
+ test(function() {
+ var testElement = document.createElement("testElement");
+ testElement.contentEditable = true;
+ testElement.spellcheck = true;
+ assert_true(testElement.spellcheck, "check for testElement.spellcheck value");
+ assert_equals(testElement.getAttribute("spellcheck"), "true");
+ }, "Setting spellcheck IDL attribute to true");
+ test(function() {
+ var testElement = document.createElement("testElement");
+ testElement.contentEditable = true;
+ testElement.spellcheck = false;
+ assert_false(testElement.spellcheck, "check for testText2 spellcheck value");
+ assert_equals(testElement.getAttribute("spellcheck"), "false");
+ }, "Setting spellcheck IDL attribute to false");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/editing/editing-0/writing-suggestions/writingsuggestions.html b/testing/web-platform/tests/html/editing/editing-0/writing-suggestions/writingsuggestions.html
new file mode 100644
index 0000000000..49e75000d3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/editing-0/writing-suggestions/writingsuggestions.html
@@ -0,0 +1,457 @@
+<!DOCTYPE html>
+<title>Tests for the writingsuggestions attribute</title>
+<link rel='author' title='Sanket Joshi' href='mailto:sajos@microsoft.com'>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#writingsuggestions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+customElements.define('test-custom-element', class extends HTMLElement {});
+
+test(function() {
+ assert_true('writingSuggestions' in document.createElement('input'));
+}, 'Test that the writingsuggestions attribute is available on HTMLInputElement.');
+
+test(function() {
+ assert_true('writingSuggestions' in document.createElement('textarea'));
+}, 'Test that the writingsuggestions attribute is available on HTMLTextAreaElement.');
+
+test(function() {
+ assert_true('writingSuggestions' in document.createElement('div'));
+}, 'Test that the writingsuggestions attribute is available on HTMLDivElement.');
+
+test(function() {
+ assert_true('writingSuggestions' in document.createElement('span'));
+}, 'Test that the writingsuggestions attribute is available on HTMLSpanElement.');
+
+test(function() {
+ assert_true('writingSuggestions' in document.createElement('test-custom-element'));
+}, 'Test that the writingsuggestions attribute is available on custom elements.');
+
+function testSetAttributeDirectly(IDLValue, contentValue, expectedIDLValue, expectedContentValue, testDescription) {
+ test(function() {
+ const elements = [document.createElement('input'),
+ document.createElement('textarea'),
+ document.createElement('div'),
+ document.createElement('span'),
+ document.createElement('test-custom-element') ];
+
+ elements.forEach(function(element) {
+ if (IDLValue != undefined) {
+ element.writingSuggestions = IDLValue;
+ }
+ if (contentValue != undefined) {
+ element.setAttribute('writingsuggestions', contentValue);
+ }
+ assert_equals(element.writingSuggestions, expectedIDLValue);
+ assert_equals(element.getAttribute('writingsuggestions'), expectedContentValue);
+ });
+ }, testDescription);
+}
+
+// Test setting either the `writingsuggestions` IDL or content attribute to some variation of 'true' directly on the target element.
+testSetAttributeDirectly('true', undefined, 'true', 'true', 'Test setting the `writingsuggestions` IDL attribute to `true` directly on the target element.');
+testSetAttributeDirectly(undefined, 'true', 'true', 'true', 'Test setting the `writingsuggestions` content attribute to `true` directly on the target element.');
+testSetAttributeDirectly(true, undefined, 'true', 'true', 'Test setting the `writingsuggestions` IDL attribute to boolean `true` directly on the target element.');
+testSetAttributeDirectly(undefined, true, 'true', 'true', 'Test setting the `writingsuggestions` content attribute to boolean `true` directly on the target element.');
+testSetAttributeDirectly('TrUe', undefined, 'true', 'true', 'Test setting the `writingsuggestions` IDL attribute to `TrUe` directly on the target element.');
+testSetAttributeDirectly(undefined, 'TrUe', 'true', 'TrUe', 'Test setting the `writingsuggestions` content attribute to `TrUe` directly on the target element.');
+
+// Test setting either the `writingsuggestions` IDL or content attribute to some variation of 'false' directly on the target element.
+testSetAttributeDirectly('false', undefined, 'false', 'false', 'Test setting the `writingsuggestions` IDL attribute to `false` directly on the target element.');
+testSetAttributeDirectly(undefined, 'false', 'false', 'false', 'Test setting the `writingsuggestions` content attribute to `false` directly on the target element.');
+testSetAttributeDirectly(false, undefined, 'false', 'false', 'Test setting the `writingsuggestions` IDL attribute to boolean `false` directly on the target element.');
+testSetAttributeDirectly(undefined, false, 'false', 'false', 'Test setting the `writingsuggestions` content attribute to boolean `false` directly on the target element.');
+testSetAttributeDirectly('FaLsE', undefined, 'false', 'false', 'Test setting the `writingsuggestions` IDL attribute to `FaLsE` directly on the target element.');
+testSetAttributeDirectly(undefined, 'FaLsE', 'false', 'FaLsE', 'Test setting the `writingsuggestions` content attribute to `FaLsE` directly on the target element.');
+
+// Test setting either the `writingsuggestions` IDL or content attribute to the empty string directly on the target element.
+testSetAttributeDirectly('', undefined, 'true', 'true', 'Test setting the `writingsuggestions` IDL attribute to the empty string directly on the target element.');
+testSetAttributeDirectly(undefined, '', 'true', '', 'Test setting the `writingsuggestions` content attribute to the empty string directly on the target element.');
+
+// Test setting either the `writingsuggestions` IDL or content attribute to an invalid value directly on the target element.
+testSetAttributeDirectly('foo', undefined, 'true', 'true', 'Test setting the `writingsuggestions` IDL attribute to an invalid value directly on the target element.');
+testSetAttributeDirectly(undefined, 'foo', 'true', 'foo', 'Test setting the `writingsuggestions` content attribute to an invalid value directly on the target element.');
+
+// Test setting neither the `writingsuggestions` IDL nor content attribute directly on the target element.
+testSetAttributeDirectly(undefined, undefined, 'true', null, 'Test the writing suggestions state when the `writingsuggestions` attribute is missing.');
+
+// Test setting the content attribute after the IDL attribute and making sure the IDL and content attributes are properly reflected.
+testSetAttributeDirectly('true', 'false', 'false', 'false', 'Test setting the `writingsuggestions` content attribute to `false` after the IDL attribute was set to `true`.');
+testSetAttributeDirectly('true', '', 'true', '', 'Test setting the `writingsuggestions` content attribute to the empty string after the IDL attribute was set to `true`.');
+testSetAttributeDirectly('true', 'foo', 'true', 'foo', 'Test setting the `writingsuggestions` content attribute to an invalid value after the IDL attribute was set to `true`.');
+testSetAttributeDirectly('true', 'TrUe', 'true', 'TrUe', 'Test setting the `writingsuggestions` content attribute to `TrUe` after the IDL attribute was set to `true`.');
+testSetAttributeDirectly('true', 'FaLsE', 'false', 'FaLsE', 'Test setting the `writingsuggestions` content attribute to `FaLsE` after the IDL attribute was set to `true`.');
+testSetAttributeDirectly('true', true, 'true', 'true', 'Test setting the `writingsuggestions` content attribute to boolean `true` after the IDL attribute was set to `true`.');
+testSetAttributeDirectly('true', false, 'false', 'false', 'Test setting the `writingsuggestions` content attribute to boolean `false` after the IDL attribute was set to `true`.');
+
+testSetAttributeDirectly('false', 'true', 'true', 'true', 'Test setting the `writingsuggestions` content attribute to `true` after the IDL attribute was set to `false`.');
+testSetAttributeDirectly('false', '', 'true', '', 'Test setting the `writingsuggestions` content attribute to the empty string after the IDL attribute was set to `false`.');
+testSetAttributeDirectly('false', 'foo', 'true', 'foo', 'Test setting the `writingsuggestions` content attribute to an invalid value after the IDL attribute was set to `false`.');
+testSetAttributeDirectly('false', 'TrUe', 'true', 'TrUe', 'Test setting the `writingsuggestions` content attribute to `TrUe` after the IDL attribute was set to `false`.');
+testSetAttributeDirectly('false', 'FaLsE', 'false', 'FaLsE', 'Test setting the `writingsuggestions` content attribute to `FaLsE` after the IDL attribute was set to `false`.');
+testSetAttributeDirectly('false', true, 'true', 'true', 'Test setting the `writingsuggestions` content attribute to boolean `true` after the IDL attribute was set to `false`.');
+testSetAttributeDirectly('false', false, 'false', 'false', 'Test setting the `writingsuggestions` content attribute to boolean `false` after the IDL attribute was set to `false`.');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<input writingsuggestions />', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<textarea writingsuggestions></textarea>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<div writingsuggestions></div>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<span writingsuggestions></span>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.writingSuggestions, 'true');
+ assert_equals(element.getAttribute('writingsuggestions'), '');
+ });
+}, 'Test setting the `writingsuggestions` attribute with a missing value directly on the target element.');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions="true"><input /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="true"><textarea></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="true"><div></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="true"><span></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'true');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), 'true');
+ assert_equals(element.writingSuggestions, 'true');
+ assert_equals(element.getAttribute('writingsuggestions'), null);
+ });
+}, 'Test setting the `writingsuggestions` attribute to "true" on a parent element.');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions=""><input /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions=""><textarea></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions=""><div></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions=""><span></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'true');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), '');
+ assert_equals(element.writingSuggestions, 'true');
+ assert_equals(element.getAttribute('writingsuggestions'), null);
+ });
+}, 'Test setting the `writingsuggestions` attribute to an empty string on a parent element.');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions="false"><input /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><textarea></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><div></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><span></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'false');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), 'false');
+ assert_equals(element.writingSuggestions, 'false');
+ assert_equals(element.getAttribute('writingsuggestions'), null);
+ });
+}, 'Test setting the `writingsuggestions` attribute to "false" on a parent element.');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions="foo"><input /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="foo"><textarea></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="foo"><div></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="foo"><span></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'true');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), 'foo');
+ assert_equals(element.writingSuggestions, 'true');
+ assert_equals(element.getAttribute('writingsuggestions'), null);
+ });
+}, 'Test setting the `writingsuggestions` attribute to an invalid value on a parent element.');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions="true"><input writingsuggestions="false" /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="true"><textarea writingsuggestions="false"></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="true"><div writingsuggestions="false"></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="true"><span writingsuggestions="false"></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'true');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), 'true');
+ assert_equals(element.writingSuggestions, 'false');
+ assert_equals(element.getAttribute('writingsuggestions'), 'false');
+ });
+}, 'Test overriding the parent element\'s `writingsuggestions` attribute from "true" to "false".');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions=""><input writingsuggestions="false" /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions=""><textarea writingsuggestions="false"></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions=""><div writingsuggestions="false"></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions=""><span writingsuggestions="false"></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'true');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), '');
+ assert_equals(element.writingSuggestions, 'false');
+ assert_equals(element.getAttribute('writingsuggestions'), 'false');
+ });
+}, 'Test overriding the parent element\'s `writingsuggestions` attribute from the empty string to "false".');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions="false"><input writingsuggestions="true" /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><textarea writingsuggestions="true"></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><div writingsuggestions="true"></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><span writingsuggestions="true"></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'false');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), 'false');
+ assert_equals(element.writingSuggestions, 'true');
+ assert_equals(element.getAttribute('writingsuggestions'), 'true');
+ });
+}, 'Test overriding the parent element\'s `writingsuggestions` attribute from "false" to "true".');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions="false"><input writingsuggestions="foo" /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><textarea writingsuggestions="foo"></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><div writingsuggestions="foo"></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><span writingsuggestions="foo"></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'false');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), 'false');
+ assert_equals(element.writingSuggestions, 'true');
+ assert_equals(element.getAttribute('writingsuggestions'), 'foo');
+ });
+}, 'Test overriding the parent element\'s `writingsuggestions` attribute from "false" to an invalid value.');
+
+test(function() {
+ const elements = [ new DOMParser().parseFromString('<html><body writingsuggestions="false"><input writingsuggestions="" /></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><textarea writingsuggestions=""></textarea></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><div writingsuggestions=""></div></body></html>', 'text/html').body.firstElementChild,
+ new DOMParser().parseFromString('<html><body writingsuggestions="false"><span writingsuggestions=""></span></body></html>', 'text/html').body.firstElementChild ];
+
+ elements.forEach(function(element) {
+ assert_equals(element.parentElement.writingSuggestions, 'false');
+ assert_equals(element.parentElement.getAttribute('writingsuggestions'), 'false');
+ assert_equals(element.writingSuggestions, 'true');
+ assert_equals(element.getAttribute('writingsuggestions'), '');
+ });
+}, 'Test overriding the parent element\'s `writingsuggestions` attribute from "false" to the empty string.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input /><textarea></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test turning off writing suggestions for an entire document.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input writingsuggestions="true" /><textarea></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on an input element from "false" to "true".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input /><textarea writingsuggestions="true"></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a textarea element from "false" to "true".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input /><textarea></textarea><div writingsuggestions="true"></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a div element from "false" to "true".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input /><textarea></textarea><div></div><span writingsuggestions="true"></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a span element from "false" to "true".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input writingsuggestions=""><textarea></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on an input element from "false" to the empty string.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input><textarea writingsuggestions=""></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a textarea element from "false" to the empty string.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input><textarea></textarea><div writingsuggestions=""></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a div element from "false" to the empty string.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input><textarea></textarea><div></div><span writingsuggestions=""></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a span element from "false" to the empty string.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input writingsuggestions="foo"><textarea></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on an input element from "false" to an invalid value.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input><textarea writingsuggestions="foo"></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a textarea element from "false" to an invalid value.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input><textarea></textarea><div writingsuggestions="foo"></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a div element from "false" to an invalid value.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="false"><body><input><textarea></textarea><div></div><span writingsuggestions="foo"></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'false');
+ assert_equals(doc.body.writingSuggestions, 'false');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a span element from "false" to an invalid value.');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="true"><body><input writingsuggestions="false"><textarea></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on an input element from "true" to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="true"><body><input><textarea writingsuggestions="false"></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a textarea element from "true" to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="true"><body><input><textarea></textarea><div writingsuggestions="false"></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a div element from "true" to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="true"><body><input><textarea></textarea><div></div><span writingsuggestions="false"></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a span element from "true" to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions=""><body><input writingsuggestions="false"><textarea></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on an input element from the empty string to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions=""><body><input><textarea writingsuggestions="false"></textarea><div></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a textarea element from the empty string to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions=""><body><input><textarea></textarea><div writingsuggestions="false"></div><span></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'false');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'true');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a div element from the empty string to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions=""><body><input><textarea></textarea><div></div><span writingsuggestions="false"></span></body></html>', 'text/html');
+ assert_equals(doc.documentElement.writingSuggestions, 'true');
+ assert_equals(doc.body.writingSuggestions, 'true');
+ assert_equals(doc.querySelector('input').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('textarea').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('div').writingSuggestions, 'true');
+ assert_equals(doc.querySelector('span').writingSuggestions, 'false');
+}, 'Test overriding a non-parent ancestor element\'s `writingsuggestions` attribute on a span element from the empty string to "false".');
+
+test(function() {
+ const doc = new DOMParser().parseFromString('<html writingsuggestions="true"><body><div contenteditable="true"><span>Writing suggestions allowed.</span> <span writingsuggestions="false">Writing suggestions not allowed.</span></div></body></html>', 'text/html');
+ const div = doc.querySelector('div');
+ const span1 = doc.querySelector('span');
+ const span2 = doc.querySelector('span:last-child');
+ assert_equals(div.writingSuggestions, 'true');
+ assert_equals(span1.writingSuggestions, 'true');
+ assert_equals(span2.writingSuggestions, 'false');
+}, 'Test that for continuous text on the screen, writing suggestions may be allowed in one part but not another.');
+
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event.html b/testing/web-platform/tests/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event.html
new file mode 100644
index 0000000000..55619661ac
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: tabindex - focus, click</title>
+<link rel="author" title="Intel" href="www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<h2>Test steps</h2>
+<p>Focus on the button below by "Tab" key, then press "Enter" key</p>
+
+<p><button type="button">Test tabIndex</button></p>
+
+<script>
+
+setup({explicit_done: true});
+setup({explicit_timeout: true});
+
+promise_test(async t => {
+ const button = document.querySelector("button");
+ on_event(button, "click", () => {
+ test(() => {
+ assert_true(document.activeElement == button, "Focus on the button by Tab key");
+ }, "Check if click event will be fired when press the 'enter' key while the element is focused");
+ done();
+ });
+
+ window.focus();
+ document.activeElement?.blur();
+ getSelection().collapse(document.querySelector("p"), 0);
+ const enterKey = '\uE007';
+ await test_driver.send_keys(button, enterKey);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-element-fragment-navigation.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-element-fragment-navigation.html
new file mode 100644
index 0000000000..812a55f318
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-element-fragment-navigation.html
@@ -0,0 +1,200 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=parentid>
+ <div id=hiddenid>
+ <div id=childid>hello</div>
+ </div>
+</div>
+
+<div id=spacer style="height:4000px">spacer</div>
+
+<script>
+test(() => {
+ window.location.hash = '';
+ hiddenid.hidden = 'until-found';
+ window.location.hash = '#hiddenid';
+ assert_false(hiddenid.hasAttribute('hidden'));
+}, 'Verifies that fragment navigation reveals hidden=until-found elements.');
+
+test(() => {
+ window.location.hash = '';
+ parentid.hidden = 'until-found';
+ hiddenid.hidden = 'until-found';
+ childid.hidden = 'until-found';
+ window.location.hash = 'childid';
+ assert_false(parentid.hasAttribute('hidden'), 'parentid should not have the hidden attribute.');
+ assert_false(hiddenid.hasAttribute('hidden'), 'hiddenid should not have the hidden attribute.');
+ assert_false(childid.hasAttribute('hidden'), 'childid should not have the hidden attribute.');
+}, 'Verifies that fragment navigation reveals all parent hidden=until-found elements.');
+
+test(() => {
+ window.location.hash = '';
+ hiddenid.hidden = 'until-found';
+ let beforematchFiredOnParent = false;
+ let beforematchFiredOnHidden = false;
+ let beforematchFiredOnChild = false;
+ parentid.onbeforematch = () => beforematchFiredOnParent = true;
+ hiddenid.onbeforematch = () => beforematchFiredOnHidden = true;
+ childid.onbeforematch = () => beforematchFiredOnChild = true;
+
+ window.location.hash = '#childid';
+ assert_true(beforematchFiredOnParent, 'beforematch should have been fired on parentid.');
+ assert_true(beforematchFiredOnHidden, 'beforematch should have been fired on hiddenid.');
+ assert_false(beforematchFiredOnChild, 'beforematch should not have been fired on childid.');
+}, 'Verifies that the beforematch event is fired synchronously and bubbles after fragment navigation.');
+
+test(t => {
+ window.location.hash = '';
+ window.scrollTo(0, 0);
+ assert_true(window.pageYOffset === 0, 'Scroll should reset at the beginning of the test.');
+
+ const target = document.createElement('div');
+ target.textContent = 'target';
+ target.id = 'target';
+ target.hidden = 'until-found';
+ document.body.appendChild(target);
+ const spacer = document.createElement('div');
+ spacer.style.height = '4000px';
+ t.add_cleanup(() => {
+ target.remove();
+ spacer.remove();
+ });
+
+ let beforematchCalled = false;
+ target.onbeforematch = () => {
+ assert_equals(window.pageYOffset, 0, 'scrolling should happen after beforematch is fired.');
+ beforematchCalled = true;
+ // Move the target down the page.
+ document.body.appendChild(spacer);
+ target.remove();
+ document.body.appendChild(target);
+ };
+
+ window.location.hash = '#target';
+ assert_true(beforematchCalled, 'The beforematch event should have been fired.');
+
+ const offsetAfterMatch = window.pageYOffset;
+ assert_not_equals(offsetAfterMatch, 0, 'Fragment navigation should have scrolled down the page to the target element.');
+ target.scrollIntoView();
+ assert_equals(offsetAfterMatch, window.pageYOffset, `The scroll after beforematch should be the same as scrolling directly to the element's final destination.`);
+}, 'Verifies that when a beforematch event handler moves a matching element, we scroll to its final location.');
+
+test(t => {
+ window.location.hash = '';
+ const foo = document.createElement('div');
+ foo.textContent = 'foo';
+ foo.id = 'foo';
+ foo.hidden = 'until-found';
+ document.body.appendChild(foo);
+
+ const bar = document.createElement('div');
+ bar.textContent = 'bar';
+ bar.id = 'bar';
+ bar.hidden = 'until-found';
+ document.body.appendChild(bar);
+
+ t.add_cleanup(() => {
+ foo.remove();
+ bar.remove();
+ });
+
+ let beforematchFiredOnFoo = false;
+ foo.onbeforematch = () => beforematchFiredOnFoo = true;
+ let beforematchFiredOnBar = false;
+ bar.onbeforematch = () => beforematchFiredOnBar = true;
+
+ window.location.hash = '#bar';
+
+ assert_false(beforematchFiredOnFoo, 'foo was not navigated to, so it should not get the beforematch event.');
+ assert_true(beforematchFiredOnBar, 'bar was navigated to, so it should get the beforematch event.');
+ assert_true(window.pageYOffset > 0, 'the page should be scrolled down to bar.');
+}, 'Verifies that the beforematch event is fired on the right element when there are multiple hidden=until-found elements.');
+
+test(t => {
+ window.location.hash = '';
+ window.scrollTo(0, 0);
+ assert_true(window.pageYOffset === 0, 'Scroll should reset at the beginning of the test.');
+
+ const div = document.createElement('div');
+ div.textContent = 'detach';
+ div.id = 'detach';
+ div.hidden = 'until-found';
+ document.body.appendChild(div);
+ t.add_cleanup(() => div.remove());
+
+ let beforematchCalled = false;
+ div.onbeforematch = () => {
+ div.remove();
+ beforematchCalled = true;
+ };
+
+ window.location.hash = '#detach';
+
+ assert_true(beforematchCalled, 'beforematch should be called when window.location.hash is set to #detach.');
+ assert_true(window.pageYOffset === 0, 'The page should not be scrolled down to where #detach used to be.');
+}, 'Verifies that no scrolling occurs when an element selected by the fragment identifier is detached by the beforematch event handler.');
+
+test(t => {
+ window.location.hash = '';
+ window.scrollTo(0, 0);
+ assert_true(window.pageYOffset === 0, 'Scroll should reset at the beginning of the test.');
+
+ const div = document.createElement('div');
+ div.textContent = 'displaynone';
+ div.id = 'displaynone';
+ div.hidden = 'until-found';
+ document.body.appendChild(div);
+ t.add_cleanup(() => div.remove());
+
+ let beforematchCalled = false;
+ div.addEventListener('beforematch', () => {
+ div.style = 'display: none';
+ beforematchCalled = true;
+ });
+
+ window.location.hash = '#displaynone';
+
+ assert_true(beforematchCalled, 'beforematch should be called when window.location.hash is set to #displaynone.');
+ assert_true(window.pageYOffset === 0, 'The page should not be scrolled down to where #displaynone used to be.');
+}, `No scrolling should occur when the beforematch event handler sets the target element's style to display: none.`);
+
+test(t => {
+ window.location.hash = '';
+ window.scrollTo(0, 0);
+ assert_true(window.pageYOffset === 0, 'Scroll should reset at the beginning of the test.');
+
+ const div = document.createElement('div');
+ div.textContent = 'visibilityhidden';
+ div.id = 'visibilityhidden';
+ div.hidden = 'until-found';
+ document.body.appendChild(div);
+ t.add_cleanup(() => div.remove());
+
+ let beforematchCalled = false;
+ div.addEventListener('beforematch', () => {
+ div.style = 'visibility: hidden';
+ beforematchCalled = true;
+ });
+
+ window.location.hash = '#visibilityhidden';
+
+ assert_true(beforematchCalled, 'beforematch should be called when window.location.hash is set to #visibilityhidden.');
+ assert_true(window.pageYOffset !== 0, 'The page should be scrolled down to where #visibilityhidden is.');
+}, `Scrolling should still occur when beforematch sets visiblity:hidden on the target element.`);
+
+test(t => {
+ window.location.hash = '';
+ const div = document.createElement('div');
+ div.id = 'target';
+ div.textContent = 'target';
+ document.body.appendChild(div);
+ t.add_cleanup(() => div.remove());
+ div.addEventListener('beforematch', t.unreached_func('beforematch should not be fired without hidden=until-found.'));
+ window.location.hash = '#target';
+}, 'Verifies that the beforematch event is not fired on elements without hidden=until-found.');
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-scroll-to-text-fragment.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-scroll-to-text-fragment.html
new file mode 100644
index 0000000000..dddab4c6ba
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/beforematch-scroll-to-text-fragment.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>beforematch fired on ScrollToTextFragment</title>
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script src="/common/utils.js"></script>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<script>
+promise_test(t => new Promise((resolve, reject) => {
+ const key = token();
+ test_driver.bless('Open a scroll to text fragment URL', () => {
+ window.open(
+ `resources/beforematch-scroll-to-text-fragment-basic.html?key=${key}#:~:text=foo`,
+ '_blank',
+ 'noopener');
+ });
+ fetchResults(key, resolve, reject);
+}).then(results => {
+ assert_equals(results.pageYOffsetDuringBeforematch, 0,
+ 'Scrolling should happen after beforematch is fired.');
+ assert_true(results.beforematchFiredOnFoo,
+ 'Foo was searched for, so it should get a beforematch event.');
+ assert_false(results.beforematchFiredOnBar,
+ 'Bar was not searched for, so it should not get a beforematch event.');
+ assert_true(results.pageYOffsetAfterRaf > 0,
+ 'The page should be scrolled down to foo.');
+}), 'Verifies that the beforematch event is fired on the matching element of a ScrollToTextFragment navigation.');
+
+promise_test(t => new Promise((resolve, reject) => {
+ const key = token();
+ test_driver.bless('Open a scroll to text fragment URL', () => {
+ window.open(
+ `resources/beforematch-scroll-to-text-fragment-with-anchor.html?key=${key}#:~:text=foo`,
+ '_blank',
+ 'noopener');
+ });
+ fetchResults(key, resolve, reject);
+}).then(results => {
+ assert_true(results.beforematchFiredOnFoo,
+ 'foo was searched for, so it should get the beforematch event.');
+ assert_false(results.beforematchFiredOnBar,
+ 'bar should not get the beforematch event despite being the target of an element fragment due to the text fragment.');
+}), 'Verifies that beforematch is only fired on elements targeted by a text fragment when there is both a text fragment and an element fragment.');
+
+promise_test(t => new Promise((resolve, reject) => {
+ const key = token();
+ test_driver.bless('Open a scroll to text fragment URL', () => {
+ window.open(
+ `resources/beforematch-scroll-to-text-fragment-bubble.html?key=${key}#:~:text=foo`,
+ '_blank',
+ 'noopener');
+ });
+ fetchResults(key, resolve, reject);
+}).then(results => {
+ assert_true(results.beforematchFiredOnChild,
+ 'The element containing the searched text should have beforematch fired on it.');
+ assert_true(results.beforematchFiredOnParent,
+ 'The parent element of the element containing the matching text should have the beforematch event fired on it because the event should bubble.');
+}), 'Verifies that the beforematch event bubbles with scroll to text fragment.');
+
+// TODO(jarhar): Write more tests here once we decide on a behavior here: https://github.com/WICG/display-locking/issues/150
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1-ref.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1-ref.html
new file mode 100644
index 0000000000..7346ce919d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<p>This line should be visible.
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1a.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1a.html
new file mode 100644
index 0000000000..036bfc88b5
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1a.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=match href=hidden-1-ref.html>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-hidden-attribute>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hidden-elements>
+<p>This line should be visible.
+<p hidden>This line should not be visible.
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1b.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1b.html
new file mode 100644
index 0000000000..3d8b05d349
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1b.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=match href=hidden-1-ref.html>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-hidden-attribute>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hidden-elements>
+<style>
+p { display: none; }
+[hidden] { display: block; }
+</style>
+<p hidden>This line should be visible.
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1c.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1c.html
new file mode 100644
index 0000000000..8a8cc63c48
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1c.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=match href=hidden-1-ref.html>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-hidden-attribute>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hidden-elements>
+<p hidden>This line should be visible.
+<p>This line should not be visible.
+<script>
+document.getElementsByTagName("p")[0].hidden = false;
+document.getElementsByTagName("p")[1].hidden = true;
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1d.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1d.html
new file mode 100644
index 0000000000..e759148f22
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1d.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=match href=hidden-1-ref.html>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-hidden-attribute>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hidden-elements>
+<p hidden>This line should be visible.
+<p>This line should not be visible.
+<script>
+document.getElementsByTagName("p")[0].removeAttribute("hidden");
+document.getElementsByTagName("p")[1].setAttribute("hidden", "");
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1e.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1e.html
new file mode 100644
index 0000000000..2f3f3c617e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1e.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=match href=hidden-1-ref.html>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-hidden-attribute>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hidden-elements>
+<style>
+p { display: block; }
+</style>
+<p hidden>This line should be visible.
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1f.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1f.html
new file mode 100644
index 0000000000..6df30a6143
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1f.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=match href=hidden-1-ref.html>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-hidden-attribute>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hidden-elements>
+<style>
+p { display: block !important; }
+</style>
+<p hidden>This line should be visible.
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1g.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1g.html
new file mode 100644
index 0000000000..849d61fe1e
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-1g.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The hidden attribute</title>
+<link rel=match href=hidden-1-ref.html>
+<link rel=author title=Ms2ger href=mailto:Ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-hidden-attribute>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hidden-elements>
+<p>This line should be visible.
+<p hidden=hidden>This line should not be visible.
+<p hidden=blue>This line should not be visible.
+<p hidden=true>This line should not be visible.
+<p hidden=false>This line should not be visible.
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2-ref.svg b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2-ref.svg
new file mode 100644
index 0000000000..10931bfcab
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2-ref.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20">
+<metadata>
+ <link xmlns="http://www.w3.org/1999/xhtml" rel="author" title="Ms2ger"
+ href="mailto:Ms2ger@gmail.com"/>
+</metadata>
+<rect height="20" width="20"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2.svg b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2.svg
new file mode 100644
index 0000000000..a5f08f6b27
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-2.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20">
+<metadata>
+ <link xmlns="http://www.w3.org/1999/xhtml" rel="match" href="hidden-2-ref.svg" />
+ <link xmlns="http://www.w3.org/1999/xhtml" rel="author" title="Ms2ger"
+ href="mailto:Ms2ger@gmail.com"/>
+ <link xmlns="http://www.w3.org/1999/xhtml" rel="help"
+ href="https://html.spec.whatwg.org/multipage/#the-hidden-attribute"/>
+ <link xmlns="http://www.w3.org/1999/xhtml" rel="help"
+ href="https://html.spec.whatwg.org/multipage/#hidden-elements"/>
+</metadata>
+<rect hidden="" height="20" width="20"/>
+</svg>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-idl.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-idl.html
new file mode 100644
index 0000000000..331b63f93f
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-idl.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/7475">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div>hello</div>
+<script>
+const div = document.querySelector('div');
+
+function runPropertyTest(assignedValue, expectedValue, expectedAttribute) {
+ test(() => {
+ div.hidden = assignedValue;
+ assert_equals(div.hidden, expectedValue,
+ `div.hidden = ${JSON.stringify(assignedValue)} should return ${JSON.stringify(expectedValue)}`);
+ assert_equals(div.getAttribute('hidden'), expectedAttribute,
+ `div.hidden = ${JSON.stringify(assignedValue)} should set the hidden attribute to ${JSON.stringify(expectedAttribute)}`);
+ }, `div.hidden = ${Number.isNaN(assignedValue) ? 'NaN' : JSON.stringify(assignedValue)}`);
+}
+
+function runAttributeTest(assignedAttribute, expectedValue) {
+ test(() => {
+ div.setAttribute('hidden', assignedAttribute);
+ assert_equals(div.hidden, expectedValue);
+ }, `div.setAttribute('hidden', ${JSON.stringify(assignedAttribute)}) should make div.hidden return ${JSON.stringify(expectedValue)}`);
+}
+
+runPropertyTest(false, false, null);
+runPropertyTest(true, true, '');
+runPropertyTest('foo', true, '');
+runPropertyTest('false', true, '');
+runPropertyTest('', false, null);
+
+runAttributeTest('false', true);
+runAttributeTest('foo', true);
+
+runPropertyTest('until-found', 'until-found', 'until-found');
+runPropertyTest('UNTIL-FOUND', 'until-found', 'until-found');
+runPropertyTest('UnTiL-FoUnD', 'until-found', 'until-found');
+runPropertyTest('unt\u0131l-found', true, '');
+runPropertyTest('unt\u0130l-found', true, '');
+
+runPropertyTest(null, false, null);
+runPropertyTest(undefined, false, null);
+
+runPropertyTest(1, true, '');
+runPropertyTest(0, false, null);
+runPropertyTest(NaN, false, null);
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-ua-stylesheet.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-ua-stylesheet.html
new file mode 100644
index 0000000000..913ecc037c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-ua-stylesheet.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/rendering.html#hiddenCSS">
+<link rel=help href="https://github.com/whatwg/html/pull/7475#issuecomment-1069313217">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=div>hello world</div>
+<table id=table>
+ <colgroup id=colgroup>
+ <col id=col></col>
+ </colgroup>
+</table>
+
+<script>
+function testDisplayNone(description) {
+ test(() => {
+ assert_equals(getComputedStyle(div).display, 'none',
+ `${description} should make the div display:none.`);
+ assert_equals(getComputedStyle(div).contentVisibility, 'visible',
+ `${description} should not affect the div's content-visibility property.`);
+ }, description);
+}
+
+function testCVHidden(description) {
+ test(() => {
+ assert_equals(getComputedStyle(div).display, 'block',
+ `${description} should not affect the div's display property.`);
+ assert_equals(getComputedStyle(div).contentVisibility, 'hidden',
+ `${description} should make the div content-visibility:hidden.`);
+ }, description);
+}
+
+function testNormal(description) {
+ test(() => {
+ assert_equals(getComputedStyle(div).display, 'block',
+ `${description} should not affect the div's display property.`);
+ assert_equals(getComputedStyle(div).contentVisibility, 'visible',
+ `${description} should not affect the div's content-visibility property.`);
+ }, description);
+}
+
+test(() => {
+ div.removeAttribute('hidden');
+ testNormal(`div.removeAttribute('hidden')`);
+
+ div.setAttribute('hidden', '');
+ testDisplayNone(`div.setAttribute('hidden', '')`);
+
+ div.setAttribute('hidden', 'asdf');
+ testDisplayNone(`div.setAttribute('hidden', 'asdf')`);
+
+ div.setAttribute('hidden', 'until-found');
+ testCVHidden(`div.setAttribute('hidden', 'until-found')`);
+
+ div.setAttribute('hidden', 'UNTIL-FOUND');
+ testCVHidden(`div.setAttribute('hidden', 'UNTIL-FOUND')`);
+
+ div.setAttribute('hidden', 'UnTiL-FoUnD');
+ testCVHidden(`div.setAttribute('hidden', 'UnTiL-FoUnD')`);
+
+ div.setAttribute('hidden', '0');
+ testDisplayNone(`div.setAttribute('hidden', '0')`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-001.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-001.html
new file mode 100644
index 0000000000..3c87b985dc
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-001.html
@@ -0,0 +1,49 @@
+<!doctype HTML>
+
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>content-visibility changes after a delay</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="./resources/container-ref.html">
+<meta name="assert" content="scrollIntoView has no effect on hidden=until-found">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+#container {
+ width: 150px;
+ height: 150px;
+ background: lightblue;
+}
+#child {
+ width: 50px;
+ height: 50px;
+ background: lightgreen;
+}
+</style>
+
+<div id=container hidden=until-found>
+ Text.
+ <div id=child></div>
+</div>
+
+
+<script>
+
+function runTest() {
+ document.getElementById("container").scrollIntoView();
+ takeScreenshot();
+}
+
+window.onload = requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ runTest();
+ });
+ });
+});
+
+</script>
+</html>
+
+
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-002.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-002.html
new file mode 100644
index 0000000000..a454e3a009
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-002.html
@@ -0,0 +1,43 @@
+<!doctype HTML>
+<meta charset="utf8">
+<title>Content Visibility: tab order navigation ignores hidden=until-found subtrees</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<meta name="assert" content="tab order navigation ignores hidden=until-found subtrees.">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input id=one type=text></input>
+<div hidden=until-found>
+ <input id=two type=text></input>
+ <input id=three type=text></input>
+ <input id=four type=text></input>
+</div>
+<input id=five type=text></input>
+
+<script>
+async_test((t) => {
+ const tab = "\t";
+ async function step1() {
+ await test_driver.send_keys(document.body, tab);
+ t.step(() => {
+ assert_equals(document.activeElement, document.getElementById("one"));
+ });
+ requestAnimationFrame(step2);
+ }
+
+ async function step2() {
+ await test_driver.send_keys(document.body, tab);
+ t.step(() => {
+ assert_equals(document.activeElement, document.getElementById("five"));
+ });
+ t.done();
+ }
+
+ window.onload = () => { requestAnimationFrame(step1); };
+}, "Tab order navigation skips hidden=until-found subtrees");
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-004.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-004.html
new file mode 100644
index 0000000000..3ca6feb7a3
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-004.html
@@ -0,0 +1,28 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>hidden=until-found does not paint</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="./resources/container-ref.html">
+<meta name="assert" content="content-visibility subtrees are not painted">
+
+<style>
+#container {
+ width: 150px;
+ height: 150px;
+ background: lightblue;
+}
+#child {
+ width: 50px;
+ height: 50px;
+ background: lightgreen;
+}
+</style>
+
+<div id=container hidden=until-found>
+ Text.
+ <div id=child></div>
+ <span>inline child</span>
+</div>
+</html>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005-ref.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005-ref.html
new file mode 100644
index 0000000000..0ca77849a4
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005-ref.html
@@ -0,0 +1,17 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Content Visibility: hidden-matchable and size contained (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+div {
+ border: 1px solid black;
+}
+</style>
+
+Test passes if there is a empty div with border below.
+<div></div>
+</html>
+
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005.html
new file mode 100644
index 0000000000..fabf636ec9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-005.html
@@ -0,0 +1,22 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>hidden=until-found and size contained</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="hidden-until-found-005-ref.html">
+<meta name="assert" content="hidden=until-found puts in size containment">
+
+<style>
+div {
+ border: 1px solid black;
+}
+</style>
+
+Test passes if there is a empty div with border below.
+<div hidden=until-found>
+ This text is not visible, and neither should be the div below.
+ <div style="width: 100px; height: 100px; background: red"></div>
+</div>
+</html>
+
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-006-ref.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-006-ref.html
new file mode 100644
index 0000000000..e14ba82c27
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-006-ref.html
@@ -0,0 +1,40 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>content-visibility hidden-matchable + scrollIntoView (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+.spacer {
+ height: 1000px;
+ background: lightgreen;
+}
+#container {
+ width: 150px;
+ height: 150px;
+ background: lightblue;
+}
+#target {
+ position: relative;
+ top: 75px;
+ visibility: hidden;
+
+ width: 50px;
+ height: 50px;
+ background: red;
+}
+</style>
+
+<div>top of the page</div>
+<div class=spacer></div>
+<div id=container>
+ <div id=target></div>
+</div>
+<div class=spacer></div>
+<div>bottom of the page</div>
+
+<script>
+onload = () => document.getElementById("target").scrollIntoView();
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-007.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-007.html
new file mode 100644
index 0000000000..7b11a92cc9
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-007.html
@@ -0,0 +1,52 @@
+<!doctype HTML>
+
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>hidden=until-found + focus</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="./resources/spacer-and-container-ref.html">
+<meta name="assert" content="focus does not scroll or focus element under hidden=until-found">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+.spacer {
+ width: 150px;
+ height: 3000px;
+ background: lightblue;
+}
+#container {
+ width: 150px;
+ height: 150px;
+ background: lightblue;
+}
+#target {
+ position: relative;
+ top: 75px;
+
+ width: 50px;
+ height: 50px;
+ background: red;
+}
+</style>
+
+<div class=spacer></div>
+<div id=container hidden=until-found>
+ <div id=target tabindex=0></div>
+</div>
+
+<script>
+function runTest() {
+ document.getElementById("target").focus();
+ requestAnimationFrame(takeScreenshot);
+}
+
+window.onload = requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ runTest();
+ });
+ });
+});
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-text-fragment.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-text-fragment.html
new file mode 100644
index 0000000000..05220f7ce1
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/hidden-until-found-text-fragment.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>beforematch fired on ScrollToTextFragment</title>
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script src="/common/utils.js"></script>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<script>
+promise_test(t => new Promise((resolve, reject) => {
+ const key = token();
+ test_driver.bless('Open a scroll to text fragment URL', () => {
+ window.open(
+ `resources/hidden-until-found-text-fragment.html?key=${key}#:~:text=foo`,
+ '_blank',
+ 'noopener');
+ });
+ fetchResults(key, resolve, reject);
+}).then(results => {
+ assert_false(results.targetHasHiddenAttribute,
+ 'The matching hidden=until-found element should have its hidden attribute removed so it can be scrolled to.');
+ assert_true(results.pageYOffsetAfterRaf > 0,
+ 'The page should be scrolled down to foo.');
+}), 'Verifies that the beforematch event is fired on the matching element of a ScrollToTextFragment navigation.');
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-basic.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-basic.html
new file mode 100644
index 0000000000..884e9a67fa
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-basic.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<!-- This test is navigated to with the fragment #:~:text=foo -->
+
+<body>
+ <div style="height: 4000px;">spacer</div>
+ <script>
+ const results = {};
+
+ const foo = document.createElement('div');
+ foo.textContent = 'foo';
+ foo.hidden = 'until-found';
+ document.body.appendChild(foo);
+ window.beforematchFiredOnFoo = false;
+ foo.addEventListener('beforematch', () => {
+ // This should be zero. Scrolling should happen after beforematch is
+ // fired.
+ results.pageYOffsetDuringBeforematch = window.pageYOffset;
+ window.beforematchFiredOnFoo = true;
+ });
+
+ const bar = document.createElement('div');
+ bar.textContent = 'bar';
+ document.body.appendChild(bar);
+ window.beforematchFiredOnBar = false;
+ bar.addEventListener('beforematch', () => {
+ window.beforematchFiredOnBar = true;
+ });
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(async () => {
+ // This should be true. Foo was searched for, so it should get a
+ // beforematch event.
+ results.beforematchFiredOnFoo = window.beforematchFiredOnFoo;
+ // This should be false. Bar was not searched for, so it should not get
+ // a beforematch event.
+ results.beforematchFiredOnBar = window.beforematchFiredOnBar;
+ // This should be greater than zero. The page should be scrolled down
+ // to foo.
+ results.pageYOffsetAfterRaf = window.pageYOffset;
+
+ params = new URLSearchParams(window.location.search);
+ stashResultsThenClose(params.get('key'), results);
+ });
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-bubble.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-bubble.html
new file mode 100644
index 0000000000..abfe8ddd54
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-bubble.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<!-- This test is navigated to with the fragment #:~:text=foo -->
+
+<div id=parentid>
+ <div id=childid hidden=until-found>foo</div>
+</div>
+
+<script>
+ const results = {
+ beforematchFiredOnChild: false,
+ beforematchFiredOnParent: false
+ };
+
+ childid.addEventListener('beforematch', () => {
+ results.beforematchFiredOnChild = true;
+ });
+
+ parentid.addEventListener('beforematch', () => {
+ results.beforematchFiredOnParent = true;
+ });
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ const params = new URLSearchParams(window.location.search);
+ stashResultsThenClose(params.get('key'), results);
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-with-anchor.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-with-anchor.html
new file mode 100644
index 0000000000..3506c6e60c
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/beforematch-scroll-to-text-fragment-with-anchor.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<!-- This test is navigated to with the fragment #bar:~:text=foo -->
+
+<body>
+ <div id=foo hidden=until-found>foo</div>
+ <div id=bar hidden=until-found>bar</div>
+ <script>
+ window.beforematchFiredOnFoo = false;
+ foo.addEventListener('beforematch', () => {
+ window.beforematchFiredOnFoo = true;
+ });
+
+ window.beforematchFiredOnBar = false;
+ bar.addEventListener('beforematch', () => {
+ window.beforematchFiredOnBar = true;
+ });
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ const results = {};
+ // This should be true. Foo was searched for, so it should get the
+ // beforematch event.
+ results.beforematchFiredOnFoo = window.beforematchFiredOnFoo;
+ // This should be false. Bar should not get the beforematch event
+ // despite being the target of an element fragment due to the text
+ // fragment.
+ results.beforematchFiredOnBar = window.beforematchFiredOnBar;
+
+ params = new URLSearchParams(window.location.search);
+ stashResultsThenClose(params.get('key'), results);
+ });
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/container-ref.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/container-ref.html
new file mode 100644
index 0000000000..e2df59014b
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/container-ref.html
@@ -0,0 +1,17 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>CSS Content Visibility: container (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+#container {
+ width: 150px;
+ height: 150px;
+ background: lightblue;
+}
+</style>
+
+<div id=container></div>
+
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/hidden-until-found-text-fragment.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/hidden-until-found-text-fragment.html
new file mode 100644
index 0000000000..fe89b407e6
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/hidden-until-found-text-fragment.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<!-- This test is navigated to with the fragment #:~:text=foo -->
+
+<body>
+ <div style="height: 4000px;">spacer</div>
+ <div id=target hidden=until-found>foo</div>
+ <script>
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ const results = {};
+ // This should be false. The hidden=until-found attribute should be
+ // removed in response to ScrollToTextFragment.
+ results.targetHasHiddenAttribute = document.getElementById('target').hasAttribute('hidden');
+ // This should be greater than zero. The page should be scrolled down
+ // to foo.
+ results.pageYOffsetAfterRaf = window.pageYOffset;
+
+ params = new URLSearchParams(window.location.search);
+ stashResultsThenClose(params.get('key'), results);
+ });
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/spacer-and-container-ref.html b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/spacer-and-container-ref.html
new file mode 100644
index 0000000000..816a6c9a9d
--- /dev/null
+++ b/testing/web-platform/tests/html/editing/the-hidden-attribute/resources/spacer-and-container-ref.html
@@ -0,0 +1,23 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Content Visibility: spacer and a container (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+.spacer {
+ width: 150px;
+ height: 3000px;
+ background: lightblue;
+}
+#container {
+ contain: style layout;
+ width: 150px;
+ height: 150px;
+}
+</style>
+
+<div class="spacer"></div>
+<div id="container"></div>
+</html>
diff --git a/testing/web-platform/tests/html/embedded-content/the-img-element/attr-img-fetchpriority.html b/testing/web-platform/tests/html/embedded-content/the-img-element/attr-img-fetchpriority.html
new file mode 100644
index 0000000000..40daf95870
--- /dev/null
+++ b/testing/web-platform/tests/html/embedded-content/the-img-element/attr-img-fetchpriority.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Fetch Priority - Image element</title>
+<meta name="author" title="Dominic Farolino" href="mailto:domfarolino@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<img id=img1 src=/images/green.png fetchpriority=high>
+<img id=img2 src=/images/green.png fetchpriority=low>
+<img id=img3 src=/images/green.png fetchpriority=auto>
+<img id=img4 src=/images/green.png fetchpriority=xyz>
+<img id=img5 src=/images/green.png>
+
+<script>
+ test(() => {
+ assert_equals(img1.fetchPriority, "high", "high fetchPriority is a valid IDL value on the image element");
+ assert_equals(img2.fetchPriority, "low", "low fetchPriority is a valid IDL value on the image element");
+ assert_equals(img3.fetchPriority, "auto", "auto fetchPriority is a valid IDL value on the image element");
+ assert_equals(img4.fetchPriority, "auto", "invalid fetchPriority reflects as 'auto' IDL attribute on the image element");
+ assert_equals(img5.fetchPriority, "auto", "missing fetchPriority reflects as 'auto' IDL attribute on the image element");
+ }, "fetchpriority attribute on <img> elements should reflect valid IDL values");
+
+ test(() => {
+ const img = new Image();
+ assert_equals(img.fetchPriority, "auto");
+ }, "fetchPriority of new Image() is 'auto'");
+</script>
diff --git a/testing/web-platform/tests/html/iana/application-x-www-form-urlencoded/original-id.json b/testing/web-platform/tests/html/iana/application-x-www-form-urlencoded/original-id.json
new file mode 100644
index 0000000000..6a52a7a5ff
--- /dev/null
+++ b/testing/web-platform/tests/html/iana/application-x-www-form-urlencoded/original-id.json
@@ -0,0 +1 @@
+{"original_id":"application/x-www-form-urlencoded"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/iana/application-xhtml-xml/original-id.json b/testing/web-platform/tests/html/iana/application-xhtml-xml/original-id.json
new file mode 100644
index 0000000000..b376ccb40f
--- /dev/null
+++ b/testing/web-platform/tests/html/iana/application-xhtml-xml/original-id.json
@@ -0,0 +1 @@
+{"original_id":"application/xhtml+xml"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/iana/multipart-x-mixed-replace/original-id.json b/testing/web-platform/tests/html/iana/multipart-x-mixed-replace/original-id.json
new file mode 100644
index 0000000000..2cf8d77704
--- /dev/null
+++ b/testing/web-platform/tests/html/iana/multipart-x-mixed-replace/original-id.json
@@ -0,0 +1 @@
+{"original_id":"multipart/x-mixed-replace"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/iana/text-cache-manifest/original-id.json b/testing/web-platform/tests/html/iana/text-cache-manifest/original-id.json
new file mode 100644
index 0000000000..b9055c2cde
--- /dev/null
+++ b/testing/web-platform/tests/html/iana/text-cache-manifest/original-id.json
@@ -0,0 +1 @@
+{"original_id":"text/cache-manifest"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/iana/text-html/original-id.json b/testing/web-platform/tests/html/iana/text-html/original-id.json
new file mode 100644
index 0000000000..ff0ed4fbe6
--- /dev/null
+++ b/testing/web-platform/tests/html/iana/text-html/original-id.json
@@ -0,0 +1 @@
+{"original_id":"text/html"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/iana/web-scheme-prefix/original-id.json b/testing/web-platform/tests/html/iana/web-scheme-prefix/original-id.json
new file mode 100644
index 0000000000..9db9bc7bf5
--- /dev/null
+++ b/testing/web-platform/tests/html/iana/web-scheme-prefix/original-id.json
@@ -0,0 +1 @@
+{"original_id":"web+-scheme-prefix"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist.html b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist.html
new file mode 100644
index 0000000000..6e6e4312a0
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<title>DOMStringList</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+// Returns a promise that resolves to a DOMStringList with
+// the requested entries. Relies on Indexed DB.
+function createDOMStringList(entries) {
+ return new Promise((resolve, reject) => {
+ const dbname = String(self.location + Math.random());
+ const request = indexedDB.open(dbname);
+ request.onerror = () => reject(request.error);
+ request.onupgradeneeded = () => {
+ const db = request.result;
+ entries.forEach(entry => db.createObjectStore(entry));
+ const dsl = db.objectStoreNames;
+ resolve(dsl);
+ request.transaction.abort();
+ }
+ });
+}
+
+function dsl_test(entries, func, description) {
+ promise_test(t => createDOMStringList(entries).then(dsl => func(t, dsl)),
+ description);
+}
+
+dsl_test(['a', 'b', 'c'], (t, dsl) => {
+ assert_equals(dsl.length, 3, 'length attribute');
+}, 'DOMStringList: length attribute');
+
+dsl_test(['a', 'b', 'c'], (t, dsl) => {
+ assert_equals(dsl.item(0), 'a', 'item method');
+ assert_equals(dsl.item(1), 'b', 'item method');
+ assert_equals(dsl.item(2), 'c', 'item method');
+ assert_equals(dsl.item(3), null, 'item method out of range');
+ assert_equals(dsl.item(-1), null, 'item method out of range');
+ assert_throws_js(TypeError, () => dsl.item(),
+ 'item method should throw if called without enough args');
+}, 'DOMStringList: item() method');
+
+dsl_test(['a', 'b', 'c'], (t, dsl) => {
+ assert_equals(dsl[0], 'a', 'indexed getter');
+ assert_equals(dsl[1], 'b', 'indexed getter');
+ assert_equals(dsl[2], 'c', 'indexed getter');
+ assert_equals(dsl[3], undefined, 'indexed getter out of range');
+ assert_equals(dsl[-1], undefined, 'indexed getter out of range');
+}, 'DOMStringList: indexed getter');
+
+dsl_test(['a', 'b', 'c'], (t, dsl) => {
+ assert_true(dsl.contains('a'), 'contains method matched');
+ assert_true(dsl.contains('b'), 'contains method matched');
+ assert_true(dsl.contains('c'), 'contains method matched');
+ assert_false(dsl.contains(''), 'contains method unmatched');
+ assert_false(dsl.contains('d'), 'contains method unmatched');
+ assert_throws_js(TypeError, () => dsl.contains(),
+ 'contains method should throw if called without enough args');
+}, 'DOMStringList: contains() method');
+
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/historical.html b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/historical.html
new file mode 100644
index 0000000000..91142c864e
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/historical.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>Historical HTML*Collection features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<form id=form><input name=foo></form>
+<select id=select><option name=bar></select>
+<div id=dupe>
+<div id=dupe>
+<script>
+test(function() {
+ var collection = document.getElementById('form').elements;
+ assert_equals(typeof collection, 'object', 'typeof');
+ assert_throws_js(TypeError, function() {
+ collection('foo');
+ });
+}, 'HTMLFormControlsCollection legacycaller should not be supported');
+
+test(function() {
+ var collection = document.getElementById('select').options;
+ assert_equals(typeof collection, 'object', 'typeof');
+ assert_throws_js(TypeError, function() {
+ collection('bar');
+ });
+}, 'HTMLOptionsCollection legacycaller should not be supported');
+
+test(function() {
+ var collection = document.all('dupe', 0);
+ // If the second argument were used, it would return the first item of the
+ // collection instead of the whole collection.
+ assert_equals(collection.length, 2, 'length');
+}, 'HTMLAllCollection legacycaller with two arguments should not be supported');
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
new file mode 100644
index 0000000000..14faa2128e
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
@@ -0,0 +1,334 @@
+<!DOCTYPE HTML>
+<html id="root">
+<head>
+<title>HTMLAllCollection Tests</title>
+<link rel="author" title="Dan Druta" href="mailto:dan.druta@att.com"/>
+<link rel="author" title="Philip Jägenstedt" href="mailto:philip@foolip.org"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/infrastructure.html#the-htmlallcollection-interface"/>
+<meta name="flags" content="TOKENS" />
+<meta name="assert" content="TEST ASSERTION"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body id="tags">
+<img src="../../../../images/green.png" name="picture">
+<a name="foo"></a>
+<a name="foo"></a>
+<span id="42"></span>
+<span id="043"></span>
+<div id="4294967294"></div>
+<div id="4294967295"></div>
+<div id="4294967296"></div>
+<div id="undefined"></div>
+<div id="null"></div>
+<div name="divwithname"></div>
+<div id="-0"></div>
+<div id="log"></div>
+<script>
+var anchors = document.querySelectorAll("a");
+var divs = document.querySelectorAll("div");
+var scripts = document.querySelectorAll("script");
+var spans = document.querySelectorAll("span");
+
+test(function() {
+ assert_true(document.all instanceof HTMLAllCollection);
+}, "document.all is an HTMLAllCollection");
+
+test(function() {
+ assert_equals(document.all.length, 25);
+}, "length attribute");
+
+// indexed property getter
+
+test(function() {
+ assert_equals(document.all[0], document.documentElement);
+ assert_equals(document.all[-0], document.documentElement);
+ assert_equals(document.all[24], scripts[2]);
+}, "indexed property getter");
+
+test(function() {
+ assert_equals(document.all[-1], undefined);
+ assert_equals(document.all[25], undefined);
+ assert_equals(document.all[42], undefined);
+ assert_equals(document.all[43], undefined);
+ assert_equals(document.all[4294967294], undefined);
+ assert_equals(document.all[4294967295], divs[1]);
+ assert_equals(document.all[4294967296], divs[2]);
+}, "indexed property getter out of range");
+
+// named property getter
+
+test(function() {
+ assert_equals(document.all["root"], document.documentElement);
+ assert_equals(document.all["flags"].content, "TOKENS");
+ assert_equals(document.all["picture"].tagName, "IMG");
+}, "named property getter");
+
+test(function() {
+ assert_equals(document.all.root, document.documentElement);
+ assert_equals(document.all.flags.content, "TOKENS");
+ assert_equals(document.all.picture.tagName, "IMG");
+}, "named property getter with dot syntax");
+
+test(function() {
+ assert_equals(document.all[""], undefined);
+ assert_equals(document.all["noname"], undefined);
+ assert_equals(document.all.noname, undefined);
+ assert_equals(document.all["divwithname"], undefined);
+ assert_equals(document.all.divwithname, undefined);
+}, "named property getter with invalid name");
+
+test(function() {
+ var collection = document.all["foo"];
+ assert_equals(collection.length, 2);
+ assert_equals(collection[0], anchors[0]);
+ assert_equals(collection[1], anchors[1]);
+}, "named property getter returning collection");
+
+test(function() {
+ assert_equals(document.all["0"], document.documentElement);
+ assert_equals(document.all["24"], document.scripts[2]);
+ assert_equals(document.all["25"], undefined);
+ assert_equals(document.all["42"], undefined);
+ assert_equals(document.all["43"], undefined);
+}, "named property getter with \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all["00"], undefined);
+ assert_equals(document.all["042"], undefined);
+ assert_equals(document.all["043"], spans[1]);
+ assert_equals(document.all["4294967294"], undefined);
+ assert_equals(document.all["4294967295"], divs[1]);
+ assert_equals(document.all["4294967296"], divs[2]);
+ assert_equals(document.all["-0"], divs[6]);
+}, "named property getter with invalid \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all[undefined], divs[3]);
+}, "named property getter with undefined");
+
+test(function() {
+ assert_equals(document.all[null], divs[4]);
+}, "named property getter with null");
+
+// namedItem method
+
+test(function() {
+ assert_equals(document.all.namedItem("root"), document.documentElement);
+ assert_equals(document.all.namedItem("flags").content, "TOKENS");
+ assert_equals(document.all.namedItem("picture").tagName, "IMG");
+}, "namedItem method");
+
+test(function() {
+ assert_equals(document.all.namedItem(""), null);
+ assert_equals(document.all.namedItem("noname"), null);
+ assert_equals(document.all.namedItem("divwithname"), null);
+}, "namedItem method with invalid name");
+
+test(function() {
+ var collection = document.all.namedItem("foo");
+ assert_equals(collection.length, 2);
+ assert_equals(collection[0], anchors[0]);
+ assert_equals(collection[1], anchors[1]);
+}, "namedItem method returning collection");
+
+test(function() {
+ assert_equals(document.all.namedItem("0"), null);
+ assert_equals(document.all.namedItem("23"), null);
+ assert_equals(document.all.namedItem("24"), null);
+ assert_equals(document.all.namedItem("42"), spans[0]);
+ assert_equals(document.all.namedItem("43"), null);
+}, "namedItem method with \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all.namedItem("00"), null);
+ assert_equals(document.all.namedItem("042"), null);
+ assert_equals(document.all.namedItem("043"), spans[1]);
+ assert_equals(document.all.namedItem("4294967294"), divs[0]);
+ assert_equals(document.all.namedItem("4294967295"), divs[1]);
+ assert_equals(document.all.namedItem("4294967296"), divs[2]);
+ assert_equals(document.all.namedItem("-0"), divs[6]);
+}, "namedItem method with invalid \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all.namedItem(undefined), divs[3]);
+}, "namedItem method with undefined");
+
+test(function() {
+ assert_equals(document.all.namedItem(null), divs[4]);
+}, "namedItem method with null");
+
+test(function() {
+ assert_equals(document.all.namedItem.length, 1);
+ assert_throws_js(TypeError, function() {
+ document.all.namedItem();
+ });
+}, "namedItem method with no argument");
+
+// legacy caller
+
+test(function() {
+ assert_equals(document.all("root"), document.documentElement);
+ assert_equals(document.all("flags").content, "TOKENS");
+ assert_equals(document.all("picture").tagName, "IMG");
+}, "legacy caller");
+
+test(function() {
+ assert_equals(document.all(""), null);
+ assert_equals(document.all("noname"), null);
+ assert_equals(document.all("divwithname"), null);
+}, "legacy caller with invalid name");
+
+test(function() {
+ var collection = document.all("foo");
+ assert_equals(collection.length, 2);
+ assert_equals(collection[0], anchors[0]);
+ assert_equals(collection[1], anchors[1]);
+}, "legacy caller returning collection");
+
+test(function() {
+ assert_equals(document.all("0"), document.documentElement);
+ assert_equals(document.all("24"), document.scripts[2]);
+ assert_equals(document.all("25"), null);
+ assert_equals(document.all("42"), null);
+ assert_equals(document.all("43"), null);
+}, "legacy caller with \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all(0), document.documentElement);
+ assert_equals(document.all(24), document.scripts[2]);
+ assert_equals(document.all(25), null);
+ assert_equals(document.all(42), null);
+ assert_equals(document.all(43), null);
+}, "legacy caller with \"array index property name\" as number");
+
+test(function() {
+ assert_equals(document.all("00"), null);
+ assert_equals(document.all("042"), null);
+ assert_equals(document.all("043"), spans[1]);
+ assert_equals(document.all("4294967294"), null);
+ assert_equals(document.all("4294967295"), divs[1]);
+ assert_equals(document.all("4294967296"), divs[2]);
+ assert_equals(document.all("-0"), divs[6]);
+}, "legacy caller with invalid \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all(undefined), null);
+}, "legacy caller with undefined");
+
+test(function() {
+ assert_equals(document.all(null), divs[4]);
+}, "legacy caller with null");
+
+test(function() {
+ assert_equals(document.all(), null);
+}, "legacy caller with no argument");
+
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new document.all("picture");
+ }, "New should not work on document.all()");
+
+ // https://esdiscuss.org/topic/isconstructor#content-11
+ assert_throws_js(TypeError, function() {
+ new (new Proxy(document.all, {
+ construct: function() {
+ return {};
+ }
+ }));
+ }, "Proxies should treat document.all() as not-a-constructor");
+}, "legacy caller is not a constructor");
+
+test(function() {
+ [undefined, null, {}, document.body].forEach(function(thisValue) {
+ assert_equals(Function.prototype.call.call(document.all, thisValue, "043"), spans[1]);
+ });
+}, "legacy caller with arbitrary this value");
+
+// item method
+
+test(function() {
+ assert_equals(document.all.item("root"), document.documentElement);
+ assert_equals(document.all.item("flags").content, "TOKENS");
+ assert_equals(document.all.item("picture").tagName, "IMG");
+}, "item method");
+
+test(function() {
+ assert_equals(document.all.item(""), null);
+ assert_equals(document.all.item("noname"), null);
+ assert_equals(document.all.item("divwithname"), null);
+}, "item method with invalid name");
+
+test(function() {
+ var collection = document.all.item("foo");
+ assert_equals(collection.length, 2);
+ assert_equals(collection[0], anchors[0]);
+ assert_equals(collection[1], anchors[1]);
+}, "item method returning collection");
+
+test(function() {
+ assert_equals(document.all.item("0"), document.documentElement);
+ assert_equals(document.all.item("24"), document.scripts[2]);
+ assert_equals(document.all.item("25"), null);
+ assert_equals(document.all.item("42"), null);
+ assert_equals(document.all.item("43"), null);
+}, "item method with \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all.item(0), document.documentElement);
+ assert_equals(document.all.item(24), document.scripts[2]);
+ assert_equals(document.all.item(25), null);
+ assert_equals(document.all.item(42), null);
+ assert_equals(document.all.item(43), null);
+}, "item method with \"array index property name\" as number");
+
+test(function() {
+ assert_equals(document.all.item("00"), null);
+ assert_equals(document.all.item("042"), null);
+ assert_equals(document.all.item("043"), spans[1]);
+ assert_equals(document.all.item("4294967294"), null);
+ assert_equals(document.all.item("4294967295"), divs[1]);
+ assert_equals(document.all.item("4294967296"), divs[2]);
+ assert_equals(document.all.item("-0"), divs[6]);
+}, "item method with invalid \"array index property name\"");
+
+test(function() {
+ assert_equals(document.all.item(undefined), null);
+}, "item method with undefined");
+
+test(function() {
+ assert_equals(document.all.item(null), divs[4]);
+}, "item method with null");
+
+test(function() {
+ assert_equals(document.all.item.length, 0);
+ assert_equals(document.all.item(), null);
+}, "item method with no argument");
+
+// live HTMLCollection
+
+test(function() {
+ var collections = [
+ document.all["foo"],
+ document.all.namedItem("foo"),
+ document.all("foo"),
+ document.all.item("foo"),
+ ];
+ // a new HTMLCollection is created for each call
+ for (var i = 0; i < collections.length; i++) {
+ assert_true(collections[i] instanceof HTMLCollection);
+ for (var j = i + 1; j < collections.length; j++) {
+ assert_not_equals(collections[i], collections[j]);
+ }
+ }
+ for (var c of collections) {
+ assert_equals(c.length, 2);
+ }
+ anchors[0].name = "bar";
+ for (var c of collections) {
+ assert_equals(c.length, 1);
+ }
+}, "collections are new live HTMLCollection instances");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html
new file mode 100644
index 0000000000..5591e190b3
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: the HTMLFormControlsCollection interface</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/common-dom-interfaces.html#htmlformcontrolscollection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form id="f1">
+ <input type="radio" id="r1" name="ra">
+ <keygen id="kg" name="key"></keygen> <!-- we test that it does *not* appear in form.elements -->
+</form>
+<form id="f2">
+ <table>
+ <tr>
+ <td>
+ <input type="checkbox" id="cb">
+ <input type="checkbox" name="cb">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <button id="btn"></button>
+ <button name="btn"></button>
+ </td>
+ </tr>
+ </table>
+</form>
+
+<script>
+
+var coll1, coll2, rdo;
+
+setup(function () {
+ rdo = document.getElementById("r1");
+ coll1 = document.forms[0].elements;
+ coll2 = document.forms[1].elements;
+});
+
+//length
+test(function () {
+ assert_equals(coll1.length, 1, "The length attribute is incorrect.");
+ assert_equals(coll2.length, 4, "The length attribute is incorrect.");
+}, "The length attribute must return the number of elements in the form");
+
+//getter - index
+test(function () {
+ assert_equals(coll1.item(0), rdo, "HTMLFormControlsCollection.item(index) should return the 'input' element in radio status.");
+}, "HTMLFormControlsCollection.item(index) must return the indexed item");
+
+test(function () {
+ assert_equals(coll1[0], rdo, "HTMLFormControlsCollection[index] should return the 'input' element in radio status.");
+}, "HTMLFormControlsCollection[index] must return the indexed item");
+
+//getter - name
+test(function () {
+ assert_throws_js(TypeError, function() { coll1("r1") });
+}, "HTMLFormControlsCollection is not callable");
+
+test(function () {
+ assert_equals(coll1["r1"], rdo, "HTMLFormControlsCollection[name] should return the 'input' element in radio status.");
+}, "HTMLFormControlsCollection[name] must return the named item");
+
+//getter - namedItem
+test(function () {
+ assert_equals(coll1.namedItem("r1"), rdo, "HTMLFormControlsCollection.namedItem(name) should return the 'input' element in radio status.");
+}, "HTMLFormControlsCollection.namedItem(name) must return the named item");
+
+test(function () {
+ assert_true(coll1.namedItem("r1") instanceof Element, "Can not return 'Element' object.");
+}, "The namedItem(name) must return an Element");
+
+test(function () {
+ assert_true(coll2.namedItem("cb") instanceof RadioNodeList, "Can not return 'RadioNodeList' object.");
+}, "The namedItem(name) must return RadioNodeList");
+
+test(function () {
+ assert_equals(coll1.namedItem(""), null, "The return value of namedItem() should be null.");
+}, "The namedItem(name) must return null if the name is empty");
+
+test(function () {
+ assert_equals(coll1.namedItem("test"), null, "The return value of namedItem() should be null.");
+}, "The namedItem(name) must return null if there is no matched element");
+
+test(function () {
+ assert_equals(coll1.namedItem("r1"), document.getElementById("r1"), "Controls can be named by 'id' attribute.");
+ assert_equals(coll1.namedItem("ra"), document.getElementById("r1"), "Controls can be named by 'name' attribute.");
+}, "Controls can be indexed by id or name attribute");
+
+test(function () {
+ assert_equals(coll1.namedItem("kg"), null, "Keygen does not show up when queried by id.");
+ assert_equals(coll1.namedItem("key"), null, "Keygen does not show up when queried by name.");
+}, "Keygen controls do not show up at all");
+
+test(function () {
+ assert_equals(coll2.namedItem("btn").length, 2, "The length attribute should be 2.");
+}, "The namedItem(name) must return the items with id or name attribute");
+
+//various controls in fieldset and form
+var containers = ["form", "fieldset"],
+ controls = ["button", "fieldset", "input", "object", "output", "select", "textarea"];
+for (var m = 0; m < containers.length; m++) {
+ test(function () {
+ var container = document.createElement(containers[m]);
+ var len = controls.length;
+ for (var n = 0; n < len; n++)
+ container.appendChild(document.createElement(controls[n]));
+ document.body.appendChild(container);
+ assert_equals(container.elements.length, len, "The length should be " + len + ".");
+ }, "The HTMLFormControlsCollection interface is used for collections of listed elements in " + containers[m] + " element");
+}
+
+//Check the controls' order
+test(function () {
+ var opt = document.forms[1].insertBefore(document.createElement("output"), document.forms[1].firstChild);
+ assert_array_equals(document.forms[1].elements,
+ [opt, document.getElementsByTagName("input")[1], document.getElementsByTagName("input")[2],
+ document.getElementsByTagName("button")[0], document.getElementsByTagName("button")[1]]);
+}, "The controls in the form element must be sorted in tree order");
+
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmloptionscollection.html b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmloptionscollection.html
new file mode 100644
index 0000000000..130716a9cc
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/htmloptionscollection.html
@@ -0,0 +1,216 @@
+<!doctype html>
+<title>HTMLOptionsCollection</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#htmloptionscollection-0">
+<select id=a>
+ <option>1</option>
+ <option>2</option>
+ <option>3</option>
+<!--Note whitespace is important-->
+ <option>4</option>
+ <option>5</option>
+</select>
+
+<select id=b>
+ <option id=b1>1</option>
+ <option name=b2>2</option>
+ <option id=b3>3</option>
+ <option id=b3>4</option>
+ <option name=b4>5</option>
+ <option name=b4>6</option>
+ <option id=b5>7</option>
+ <option name=b5>8</option>
+ <option id=b6 name=b7>9</option>
+ <option id=b6 name=b6>10</option>
+ <option id=b8 name=b9>11</option>
+</select>
+
+<script>
+var a;
+var a_opts;
+var a_original_innerHTML;
+var b;
+var b_opts;
+
+setup(function() {
+ a = document.getElementById("a");
+ a_opts = a.options;
+ a_original_innerHTML = a.innerHTML;
+ a.innerHTML = a_original_innerHTML;
+ b = document.getElementById("b");
+ b_opts = b.options;
+ b_original_innerHTML = b.innerHTML;
+ b.innerHTML = b_original_innerHTML;
+})
+
+function assert_values_equals(coll, expected_values, message) {
+ actual = [];
+ for (var i=0; i<coll.length; i++) {
+ actual.push(coll[i].value);
+ }
+ assert_array_equals(actual, expected_values, message);
+}
+
+test(function() {
+ assert_equals(5, a_opts.length);
+}, "Original length");
+
+test(function() {
+ a.innerHTML = a_original_innerHTML;
+ a_opts.value = "3";
+ a_opts.length = 5;
+ assert_equals(a_opts.length, 5);
+ assert_equals(a_opts.value, "3");
+}, "Setting length to original value has no effect");
+
+test(function() {
+ a.innerHTML = a_original_innerHTML;
+ a.value = 3;
+ a_opts.length = 3;
+ assert_equals(3, a_opts.length, "Correct length");
+ assert_values_equals(a_opts, ["1","2","3"], "Correct elements remain")
+ assert_equals(a_opts.value, "3", "Correct value set");
+ assert_equals(a.childNodes.length, 11, "Correct number of child nodes")
+}, "Setting length to shorter value");
+
+test(function() {
+ a.innerHTML = a_original_innerHTML;
+ a.value = 3;
+ a_opts.length = 7;
+ assert_equals(a_opts.length, 7, "Correct length");
+ assert_values_equals(a_opts, ["1","2","3","4","5","",""], "Correct elements inserted")
+ assert_equals(a.value, "3", "Correct value set");
+ assert_equals(a.childNodes.length, 15, "Correct number of child nodes")
+}, "Setting length to longer value");
+
+test(function() {
+ a.innerHTML = a_original_innerHTML;
+ var newChild = document.createElement("p");
+ var newOption = document.createElement("option");
+ newOption.textContent = "6";
+ newChild.appendChild(newOption);
+ a.appendChild(newChild);
+ a.value = 3;
+ assert_equals(a_opts.length, 5, "Correct length");
+ assert_values_equals(a_opts, ["1","2","3","4","5"], "Correct elements inserted")
+ assert_equals(a.value, "3", "Correct value set");
+}, "Insert <p><option>6</option></p> into <select>");
+
+test(function() {
+ a.innerHTML = a_original_innerHTML;
+ var newChild = document.createElement("select");
+ var newOption = document.createElement("option");
+ newOption.textContent = "6";
+ newChild.appendChild(newOption);
+ a.appendChild(newChild);
+ a.value = 3;
+ assert_equals(a_opts.length, 5, "Correct length");
+ assert_values_equals(a_opts, ["1","2","3","4","5"], "Correct elements inserted")
+ assert_equals(a.value, "3", "Correct value set");
+}, "Insert <select><option>6</option></select> into <select>");
+
+test(function() {
+ //This tests the spec but it is probably wrong here; see bug 12665
+ a.innerHTML = a_original_innerHTML;
+ var newChild = document.createElement("optgroup");
+ var newOption = document.createElement("option");
+ newOption.textContent = "6";
+ newChild.appendChild(newOption);
+ a.appendChild(newChild);
+ a.value = 3;
+ assert_equals(a_opts.length, 6, "Correct length");
+ assert_values_equals(a_opts, ["1","2","3","4","5", "6"], "Correct elements inserted")
+ assert_equals(a.value, "3", "Correct value set");
+}, "Insert <optgroup><option>6</option></optgroup> into <select>");
+
+test(function() {
+ a.innerHTML = a_original_innerHTML;
+ var newChild = document.createElement("optgroup");
+ var newChild1 = document.createElement("optgroup");
+ var newOption = document.createElement("option");
+ newOption.textContent = "6";
+ newChild.appendChild(newChild1);
+ newChild1.appendChild(newOption);
+ a.appendChild(newChild);
+ a.value = 3;
+ assert_equals(a_opts.length, 5, "Correct length");
+ assert_values_equals(a_opts, ["1","2","3","4","5"], "Correct elements inserted")
+ assert_equals(a.value, "3", "Correct value set");
+}, "Insert <optgroup><optgroup><option>6</option></optgroup></optgroup> into <select>");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b1").value, "1");
+}, "namedItem id attribute");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b2").value, "2");
+}, "namedItem name attribute");
+
+test(function() {
+ assert_equals(b_opts.namedItem("c"), null);
+}, "namedItem doesn't match anything");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b3").value, "3");
+}, "namedItem multiple IDs");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b4").value, "5");
+}, "namedItem multiple names");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b5").value, "7");
+}, "namedItem multiple name and ID");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b6").value, "9");
+}, "namedItem multiple name and ID with multiple attributes");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b8").value, "11");
+}, "namedItem id attribute multiple attributes one element");
+
+test(function() {
+ assert_equals(b_opts.namedItem("b9").value, "11");
+}, "namedItem name attribute multiple attributes one element");
+
+test(function() {
+ assert_true(b_opts[0] instanceof HTMLOptionElement);
+ assert_equals(b_opts[0].innerHTML, "1");
+}, "HTMLOptionsCollection [index] method return the item with index");
+
+test(function() {
+ assert_true(b_opts["b2"] instanceof HTMLOptionElement);
+ assert_equals(b_opts["b2"].innerHTML, "2");
+}, "HTMLOptionsCollection [name] method return the item with name");
+
+test(function() {
+ assert_true(b_opts.item(0) instanceof HTMLOptionElement);
+ assert_equals(b_opts.item(0).innerHTML, "1");
+}, "HTMLOptionsCollection.item(index) method return the item with index");
+
+test(function() {
+ assert_true(b_opts.item("b2") instanceof HTMLOptionElement);
+ assert_equals(b_opts.item("b2").innerHTML, "1");
+}, "HTMLOptionsCollection.item(name) method return the item with index 0");
+
+test(function() {
+ var b_opts_length = b_opts.length;
+ b_opts.add(new Option("2", "2"));
+ assert_equals(b_opts[b_opts_length].value, "2");
+}, "HTMLOptionsCollection.add method insert HTMLOptionElement Option element");
+
+test(function() {
+ var b_opts_length = b_opts.length;
+ b_opts.remove(0);
+ assert_equals(b_opts.length, b_opts_length - 1);
+}, "HTMLOptionsCollection.remove method remove Option element by index");
+
+test(function() {
+ var add = document.createElement("p");
+ assert_throws_js(TypeError, function() {b_opts.add(add);});
+}, "Add non-option to collection");
+
+</script>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/radionodelist.html b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/radionodelist.html
new file mode 100644
index 0000000000..fc70d7172c
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/radionodelist.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: the RadioNodeList interface</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/common-dom-interfaces.html#radionodelist">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form >
+ <input type="checkbox" name="rdo" value="0" id="r0" checked>
+ <input type="radio" name="rdo" id="r1">
+ <input type="radio" name="rdo" id="r2" value="2">
+</form>
+<script>
+
+var rdoList;
+
+setup(function () {
+ rdoList = document.forms[0].elements.namedItem("rdo");
+});
+
+//on getting
+test(function () {
+ assert_equals(rdoList.value, "", "The value attribute should be empty.");
+}, "The value attribute should be empty if no element is checked");
+
+test(function () {
+ document.getElementById("r2").checked = true;
+ assert_equals(rdoList.value, "2", "The value attribute should be 2.");
+}, "The RadioNodeList.value must be the first checked radio button's value");
+
+test(function () {
+ document.getElementById("r1").checked = true;
+ assert_equals(rdoList.value, "on", "The value attribute should be on.");
+
+ document.getElementById("r1").value = 1;
+ assert_equals(rdoList.value, "1", "The value attribute should be 1.");
+}, "Check the RadioNodeList.value on getting");
+
+//on setting
+test(function () {
+ assert_equals(rdoList.value, document.getElementById("r1").value,
+ "The value attribute should be equal to the first checked radio input element's value.");
+ assert_false(document.getElementById("r2").checked,
+ "The second radio input element should not be checked.");
+
+ rdoList.value = "2";
+ assert_equals(rdoList.value, document.getElementById("r2").value,
+ "The value attribute should be equal to the second radio input element's value.");
+ assert_true(document.getElementById("r2").checked,
+ "The second radio input element should be checked.");
+
+ //Do nothing if no element's value is equal to new value.
+ rdoList.value = "3";
+ assert_equals(rdoList.value, document.getElementById("r2").value,
+ "The value attribute should be the second radio input element's value.");
+ assert_true(document.getElementById("r2").checked,
+ "The second radio input element should be checked.");
+}, "Check the RadioNodeList.value on setting");
+
+//setting to on, specific case
+test(function () {
+ rdoList.value = "on";
+ assert_equals(rdoList.value, document.getElementById("r2").value,
+ "The value attribute should be the second radio input element's value.");
+ assert_true(document.getElementById("r2").checked,
+ "The second radio input element should be checked.");
+
+ document.getElementById("r1").removeAttribute("value");
+ rdoList.value = "on";
+ assert_equals(rdoList.value, document.getElementById("r1").value,
+ "The value attribute should be the first radio input element's value.");
+ assert_true(document.getElementById("r1").checked,
+ "The first radio input element should be checked.");
+}, "Check the RadioNodeList.value on setting to 'on'");
+
+
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive-ref.html b/testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive-ref.html
new file mode 100644
index 0000000000..6565e0e750
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<p>This square should be black: <font face="Ahem">X</font>
+<p>This square should be black: <font face="Ahem">X</font>
+<p>This square should be blue: <font face="Ahem" color="#0000E0">X</font>
diff --git a/testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive.html b/testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive.html
new file mode 100644
index 0000000000..eee4a9c7c8
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/common-microsyntaxes/colours/parsing-legacy-colour-value-ascii-case-insensitive.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#rules-for-parsing-a-legacy-colour-value">
+<link rel="match" href="parsing-legacy-colour-value-ascii-case-insensitive-ref.html">
+<meta name="assert" content="special legacy color value “transparent†is ASCII case-insensitive">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<p>This square should be black: <font face="Ahem" color="transparent">X</font>
+<p>This square should be black: <font face="Ahem" color="TrAnSpArEnT">X</font>
+<p>This square should be blue: <font face="Ahem" color="tranſparent">X</font>
+<!--
+ Following the rules for parsing a legacy color value should yield a shade of
+ blue, because only the following steps apply, not step 4:
+
+ 1. tranſparent
+ 10. 00a000a0e00
+ 11. 00a000a0e000
+ 12. 00a0 00a0 e000 (length = 4)
+ 15. 00 00 e0
+ 20. #0000E0
+-->
diff --git a/testing/web-platform/tests/html/infrastructure/conformance-requirements/extensibility/foreign.html b/testing/web-platform/tests/html/infrastructure/conformance-requirements/extensibility/foreign.html
new file mode 100644
index 0000000000..eaa133bade
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/conformance-requirements/extensibility/foreign.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en" foo='bar'>
+ <head foo='bar'>
+ <meta charset="utf-8" foo='bar'>
+ <title id='title' foo='bar'>Foreign content</title>
+ <link rel="author" title="Philippe Le Hegaret" href="mailto:plh@w3.org" foo='bar'>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#extensibility" foo='bar'>
+ <script src="/resources/testharness.js" foo='bar'></script>
+ <script src="/resources/testharnessreport.js" foo='bar'></script>
+ </head>
+
+ <body foo='bar'>
+
+ <p class='assert' foo='bar'>User agents must treat elements and attributes that they do not understand as semantically neutral; leaving them in the DOM (for DOM processors), and styling them according to CSS (for CSS processors), but not inferring any meaning from them.</p>
+
+ <foo foo='bar' echo>Foobar</foo>
+
+ <div id="log">Running test...</div>
+
+ <script>
+ var t = async_test("foreign content");
+
+ on_event(window, "load",
+ t.step_func(function() {
+ var nodes = document.getElementsByTagName("*");
+ var cont = true;
+ var last = null;
+ for(var i=0;i<nodes.length && cont; i++) {
+ var as = nodes.item(i).getAttribute("foo");
+ if (!(as === "bar") && (nodes.item(i).getAttribute("id") === "log")) {
+ cont = false;
+ } else {
+ last = nodes.item(i);
+ assert_equals(as, "bar");
+ }
+ }
+
+ assert_equals(last.nodeName, "FOO");
+ assert_equals(last.getAttribute("echo"), "");
+ assert_equals(last.getAttribute("charly"), null);
+ t.done();
+ }));
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/infrastructure/fetching-resources/crossorigin-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/infrastructure/fetching-resources/crossorigin-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..7207d43c60
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/fetching-resources/crossorigin-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#cors-settings-attribute">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="@crossorigin values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<img crossorigin="use-credentials">
+<img crossorigin="UsE-cReDenTiAlS">
+<img crossorigin="uſe-credentialſ">
+<img crossorigin="anonymous">
+<img crossorigin="AnOnYmOuS">
+<img crossorigin="anonymouſ">
+<script>
+const img = document.querySelectorAll("img");
+
+test(() => {
+ assert_equals(img[0].crossOrigin, "use-credentials", "lowercase valid");
+ assert_equals(img[1].crossOrigin, "use-credentials", "mixed case valid");
+ assert_equals(img[2].crossOrigin, "anonymous", "non-ASCII invalid");
+}, "keyword use-credentials");
+
+test(() => {
+ assert_equals(img[3].crossOrigin, "anonymous", "lowercase valid");
+
+ // vacuous: the invalid value default is currently anonymous, so even if the
+ // UA treats this as invalid, the observable behaviour would still be correct
+ assert_equals(img[4].crossOrigin, "anonymous", "mixed case valid");
+
+ // vacuous: the invalid value default is currently anonymous, so even if the
+ // UA treats this as valid, the observable behaviour would still be correct
+ assert_equals(img[5].crossOrigin, "anonymous", "non-ASCII invalid");
+}, "keyword anonymous");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/cross-origin-transfer-resizable-arraybuffer.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/cross-origin-transfer-resizable-arraybuffer.html
new file mode 100644
index 0000000000..2b21a1459d
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/cross-origin-transfer-resizable-arraybuffer.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>postMessage transfer ArrayBuffer cross origin iframe</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='/common/get-host-info.sub.js'></script>
+
+<script>
+
+async_test(t => {
+ const oopif = document.createElement('iframe');
+
+ window.addEventListener('message', t.step_func((e) => {
+ if (e.data === 'started') {
+ const rab = new ArrayBuffer(32, { maxByteLength: 1024 });
+ oopif.contentWindow.postMessage(rab, '*', [rab]);
+ } else {
+ assert_equals(e.data, 'byteLength=32,maxByteLength=1024,resizable=true');
+ t.done();
+ }
+ }));
+
+ window.addEventListener('load', () => {
+ oopif.src = `${get_host_info().HTTP_REMOTE_ORIGIN}/html/infrastructure/safe-passing-of-structured-data/resources/iframe-resizable-arraybuffer-helper.html`;
+ document.body.appendChild(oopif);
+ });
+}, 'postMessaging resizable ArrayBuffer to OOPIF');
+
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/messagechannel.any.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/messagechannel.any.js
new file mode 100644
index 0000000000..6ba17f7a89
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/messagechannel.any.js
@@ -0,0 +1,16 @@
+// META: global=window,worker
+// META: script=/common/sab.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js
+
+runStructuredCloneBatteryOfTests({
+ structuredClone(data, transfer) {
+ return new Promise(resolve => {
+ const channel = new MessageChannel();
+ channel.port2.onmessage = ev => resolve(ev.data.data);
+ channel.port1.postMessage({data, transfer}, transfer);
+ });
+ },
+ hasDocument : self.GLOBAL.isWindow()
+});
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html
new file mode 100644
index 0000000000..c4fd5824a1
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that echos back anything postMessaged to it to its parent</title>
+
+<script>
+"use strict";
+
+window.onmessage = ({ data }) => {
+ parent.postMessage(data, "*");
+};
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-iframe.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js
new file mode 100644
index 0000000000..cbbde8a73c
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js
@@ -0,0 +1,5 @@
+"use strict";
+
+self.onmessage = ({ data }) => {
+ self.postMessage(data);
+};
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/echo-worker.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/iframe-resizable-arraybuffer-helper.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/iframe-resizable-arraybuffer-helper.html
new file mode 100644
index 0000000000..378c953fbe
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/iframe-resizable-arraybuffer-helper.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+
+window.addEventListener('message', (e) => {
+ const buffer = e.data;
+ e.source.postMessage(`byteLength=${buffer.byteLength},maxByteLength=${buffer.maxByteLength},resizable=${buffer.resizable}`, '*');
+});
+
+window.parent.postMessage('started', '*');
+
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/post-parent-type-error.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/post-parent-type-error.html
new file mode 100644
index 0000000000..d6713c4192
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/resources/post-parent-type-error.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Helper that posts its parent a TypeError</title>
+
+<script>
+window.doIt = () => {
+ parent.postMessage(new TypeError("!!"), "*");
+};
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html
new file mode 100644
index 0000000000..bfcc8b61ca
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html
@@ -0,0 +1,132 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+const url = new URL("./", self.location);
+
+function httpWorkerIncrementerTest(name) {
+ return `
+promise_test(t => {
+ const worker = new Worker("${url.href}resources/incrementer-worker.js");
+
+ return testSharingViaIncrementerScript(t, worker, "parent worker", worker, "sub-worker");
+}, "${name}: postMessaging to a dedicated HTTP sub-worker allows them to see each others' modifications");
+`;
+}
+
+function blobWorkerIncrementerTest(name, origin = "null") {
+ return `
+promise_test(t => {
+ const worker = new Worker(URL.createObjectURL(new Blob([\`
+const view = new Uint8Array(new SharedArrayBuffer(1));
+self.onmessage = () => {
+ const succeeded = (view[0] === 1);
+ self.postMessage({ succeeded });
+};
+self.postMessage({ origin: self.origin, view });
+\`], { type: "text/javascript" })));
+
+ return new Promise((resolve, reject) => {
+ /* Initially the sub-worker gives us an object containing an origin and a view on a shared
+ // buffer. We then modify the shared buffer through the buffer and tell the sub-worker to
+ // "continue". The sub-worker verifies the modification and relays whether it succeeded.
+ */
+ worker.onmessage = t.step_func(({ data }) => {
+ if ("succeeded" in data) {
+ assert_true(data.succeeded);
+ resolve();
+ } else {
+ assert_equals(data.origin, "${origin}");
+ assert_equals(data.view[0], 0);
+ data.view[0] = 1;
+ worker.postMessage("continue");
+ }
+ });
+ worker.onmessageerror = reject;
+ });
+}, "${name}: postMessaging to a dedicated blob sub-worker allows them to see each others' modifications");
+`;
+}
+
+function propertyTests(name, crossOriginIsolated) {
+ return `
+test(() => {
+ assert_equals(self.origin, self.location.origin);
+}, "${name}: self.origin");
+
+test(() => {
+ assert_equals(self.crossOriginIsolated, ${crossOriginIsolated});
+}, "${name}: self.crossOriginIsolated");
+
+test(() => {
+ assert_true(self.isSecureContext);
+}, "${name}: self.isSecureContext");
+`;
+}
+
+const workerScript = `
+importScripts("${url.origin}/resources/testharness.js");
+importScripts("${url.href}resources/test-incrementer.js");
+
+${httpWorkerIncrementerTest("blob worker")}
+
+${blobWorkerIncrementerTest("blob worker", self.location.origin)}
+
+${propertyTests("blob worker", true)}
+
+done();
+`;
+
+fetch_tests_from_worker(new Worker(URL.createObjectURL(new Blob([workerScript], { type: "text/javascript" }))));
+
+const frameScript = `
+<!doctype html>
+<script src=${url.origin}/resources/testharness.js><\/script>
+<script src=${url.href}resources/test-incrementer.js><\/script>
+<script>
+${httpWorkerIncrementerTest("blob frame")}
+
+${blobWorkerIncrementerTest("blob frame", self.location.origin)}
+
+${propertyTests("blob frame", true)}
+<\/script>
+`;
+
+const frame = document.body.appendChild(document.createElement("iframe"));
+frame.src = URL.createObjectURL(new Blob([frameScript], { type: "text/html" }));
+frame.style = "display:none";
+fetch_tests_from_window(frame.contentWindow);
+
+const dataWorkerScript = `
+importScripts("${url.origin}/resources/testharness.js?pipe=header(Cross-Origin-Resource-Policy,cross-origin)");
+
+/* Cannot use httpWorkerIncrementerTest() here as the HTTP URL is not same origin. */
+
+${blobWorkerIncrementerTest("data worker")}
+
+${propertyTests("data worker", false)}
+
+done();
+`;
+
+fetch_tests_from_worker(new Worker(`data:,${dataWorkerScript}`));
+
+const dataFrameScript = `
+<!doctype html>
+<script src=${url.origin}/resources/testharness.js?pipe=header(Cross-Origin-Resource-Policy,cross-origin)><\/script>
+<script>
+/* Cannot use httpWorkerIncrementerTest() here as the HTTP URL is not same origin. */
+
+${blobWorkerIncrementerTest("data frame")}
+
+${propertyTests("data frame", true)}
+<\/script>
+`;
+
+const dataFrame = document.body.appendChild(document.createElement("iframe"));
+dataFrame.src = `data:text/html,${dataFrameScript}`;
+dataFrame.style = "display:none";
+fetch_tests_from_window(dataFrame.contentWindow);
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html
new file mode 100644
index 0000000000..8902de49cf
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>SharedArrayBuffer cannot cross agent clusters, BroadcastChannel edition</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+async_test(t => {
+ const channel = new BroadcastChannel("anne was here"),
+ dw = new Worker("resources/broadcastchannel-worker.js"),
+ sw = new SharedWorker("resources/broadcastchannel-sharedworker.js");
+ let startCounter = 0,
+ dwStatus = "unknown",
+ swStatus = "unknown";
+
+ channel.onmessage = t.step_func(({ data }) => {
+ if(data === "hi") {
+ startCounter++;
+ if(startCounter === 2) {
+ const sab = new SharedArrayBuffer();
+ channel.postMessage(sab);
+ } else if(startCounter > 2) {
+ assert_unreached();
+ }
+ } else if(data === "dw-success") {
+ dwStatus = "success";
+ } else if(data === "sw-success") {
+ swStatus = "success";
+ } else {
+ assert_unreached();
+ }
+ if(dwStatus === "success" && swStatus === "success") {
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html
new file mode 100644
index 0000000000..d3e9956368
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Structured cloning of SharedArrayBuffers: BroadcastChannel within the same agent cluster</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#broadcasting-to-other-browsing-contexts">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ const channelName = token();
+ return Promise.all([
+ createIFrame(`resources/broadcastchannel-iframe.html?channel=${channelName}&index=0`),
+ createIFrame(`resources/broadcastchannel-iframe.html?channel=${channelName}&index=1`),
+ createIFrame(`resources/broadcastchannel-iframe.html?channel=${channelName}&index=2`)
+ ]).then(() => {
+ const sab = new SharedArrayBuffer(3);
+ const view = new Uint8Array(sab);
+ const channel = new BroadcastChannel(channelName);
+
+ return new Promise(resolve => {
+ let soFar = 0;
+ channel.onmessage = t.step_func(({ data: { i } }) => {
+ assert_in_array(i, [0, 1, 2], "Any message events must come from expected sources");
+ ++soFar;
+
+ if (soFar === 3) {
+ assert_equals(view[0], 1, "The first iframe must have set view[0] to 1");
+ assert_equals(view[1], 2, "The second iframe must have set view[1] to 2");
+ assert_equals(view[2], 3, "The third iframe must have set view[2] to 3");
+ resolve();
+ }
+ });
+
+ channel.postMessage({ sab });
+ });
+ });
+});
+
+function createIFrame(src) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = src;
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(`iframe with URL ${src} failed to load`);
+ document.body.appendChild(iframe);
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html
new file mode 100644
index 0000000000..869f49043e
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SharedArrayBuffers, when cloned, do not give back the same object</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/test-sab.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+async_test(t => {
+ const testId = token();
+ const sab = new SharedArrayBuffer(1);
+ window.addEventListener("message", t.step_func(({ data }) => {
+ if (data.testId !== testId) {
+ return;
+ }
+
+ assertSABsHaveSameBackingBlock(sab, data.sab);
+
+ t.done();
+ }));
+
+ window.postMessage({ testId, sab }, "*");
+}, "postMessaging to this window does not give back the same SharedArrayBuffer (but does use the same backing block)");
+
+async_test(t => {
+ const testId = token();
+ const sab = new SharedArrayBuffer();
+ const worker = new Worker("../resources/echo-worker.js");
+
+ worker.addEventListener("message", t.step_func(({ data }) => {
+ if (data.testId !== testId) {
+ return;
+ }
+
+ assert_not_equals(data.sab, sab);
+ t.done();
+ }));
+
+ worker.postMessage({ testId, sab });
+}, "postMessaging to a worker and back does not give back the same SharedArrayBuffer");
+
+async_test(t => {
+ const testId = token();
+ const sab = new SharedArrayBuffer();
+ window.addEventListener("message", t.step_func(({ data }) => {
+ if (data.testId !== testId) {
+ return;
+ }
+
+ assert_not_equals(data.sab, sab);
+ t.done();
+ }));
+
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ iframe.contentWindow.postMessage({ testId, sab }, "*");
+ });
+ iframe.src = "../resources/echo-iframe.html";
+ document.body.appendChild(iframe);
+}, "postMessaging to an iframe and back does not give back the same SharedArrayBuffer");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/identity-not-preserved.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html
new file mode 100644
index 0000000000..dd221502b6
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+fetch_tests_from_worker(new SharedWorker("resources/nested-worker-success.js"));
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-sharedworker-success.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html
new file mode 100644
index 0000000000..aeee3705ae
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+fetch_tests_from_worker(new Worker("resources/nested-worker-success.js"));
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js
new file mode 100644
index 0000000000..0db16fd6f7
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js
@@ -0,0 +1,33 @@
+// META: global=window,worker
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ assert_equals(globalThis.SharedArrayBuffer, undefined);
+ assert_false("SharedArrayBuffer" in globalThis);
+}, "SharedArrayBuffer constructor does not exist without COOP+COEP");
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ const channel = new MessageChannel();
+ assert_throws_dom("DataCloneError", () => channel.port1.postMessage(sab));
+}, "SharedArrayBuffer over MessageChannel without COOP+COEP");
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ const channel = new BroadcastChannel("Is mir egal");
+ assert_throws_dom("DataCloneError", () => channel.postMessage(sab));
+}, "SharedArrayBuffer over BroadcastChannel without COOP+COEP");
+
+if (self.GLOBAL.isWindow()) {
+ test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ assert_throws_dom("DataCloneError", () => self.postMessage(sab));
+ }, "SharedArrayBuffer over postMessage() without COOP+COEP");
+}
+
+test(() => {
+ assert_false(self.crossOriginIsolated);
+}, "Bonus: self.crossOriginIsolated");
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html
new file mode 100644
index 0000000000..dfa57fa200
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SharedArrayBuffers cannot be transferred</title>
+<link rel="help" href="https://html.spec.whatwg.org/#structuredclone">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ const sab = new SharedArrayBuffer();
+ assert_throws_dom("DataCloneError", () => window.postMessage(sab, "*", [sab]));
+ assert_throws_dom("DataCloneError", () => window.postMessage("test", "*", [sab]));
+}, "Trying to transfer a SharedArrayBuffer to this window throws");
+
+test(() => {
+ const sab = new SharedArrayBuffer();
+ const worker = new Worker("../resources/echo-worker.js");
+ assert_throws_dom("DataCloneError", () => worker.postMessage(sab, [sab]));
+ assert_throws_dom("DataCloneError", () => worker.postMessage("test", [sab]));
+}, "Trying to transfer a SharedArrayBuffer to a worker throws");
+
+test(() => {
+ const sab = new SharedArrayBuffer();
+ const channel = new MessageChannel();
+ assert_throws_dom("DataCloneError", () => channel.port1.postMessage(sab, [sab]));
+ assert_throws_dom("DataCloneError", () => channel.port1.postMessage("test", [sab]));
+}, "Trying to transfer a SharedArrayBuffer through a MessagePort throws");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html
new file mode 100644
index 0000000000..eec1b2cc8e
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<title>Used as a service worker-controlled iframe</title>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/blank.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html
new file mode 100644
index 0000000000..02b9bcbbe8
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that messes with a given SharedArrayBuffer sent from a BroadcastChannel</title>
+
+<script>
+"use strict";
+const query = new URLSearchParams(location.search);
+const channel = new BroadcastChannel(query.get("channel"));
+const i = Number(query.get("index"));
+
+channel.onmessage = e => {
+ const sab = e.data.sab;
+ if (sab === undefined) {
+ // We only care about "broadcasts" from the window
+ return;
+ }
+
+ const view = new Uint8Array(sab);
+ view[i] = i + 1;
+ channel.postMessage({ i });
+};
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js
new file mode 100644
index 0000000000..310e0e9358
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js
@@ -0,0 +1,7 @@
+const channel = new BroadcastChannel("anne was here");
+channel.onmessageerror = ({ data }) => {
+ if(data === null) {
+ channel.postMessage("sw-success");
+ }
+}
+channel.postMessage("hi");
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js
new file mode 100644
index 0000000000..36369cde50
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js
@@ -0,0 +1,9 @@
+const channel = new BroadcastChannel("anne was here");
+channel.onmessage = ({ data }) => {
+ if(data === "hi" || data === "sw-success") {
+ return;
+ } else if(data instanceof SharedArrayBuffer) {
+ channel.postMessage("dw-success");
+ }
+}
+channel.postMessage("hi");
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-worker.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html
new file mode 100644
index 0000000000..0cdb8b5f59
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that postMessage a SharedArrayBuffer to the parent and also sets document.domain</title>
+
+<script>
+"use strict";
+
+document.domain = "{{host}}";
+parent.postMessage({name: "domain", value: document.domain}, "*");
+parent.postMessage(
+ {name: "crossOriginIsolated", value: self.crossOriginIsolated}, "*");
+parent.postMessage(
+ {name: "hasSharedArrayBuffer", value: Boolean(self.SharedArrayBuffer)}, "*");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html.headers
new file mode 100644
index 0000000000..56d0ac3428
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: same-site
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html
new file mode 100644
index 0000000000..2c33dba79a
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html
@@ -0,0 +1,3 @@
+<script>
+parent.postMessage(new SharedArrayBuffer(10), "*");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html
new file mode 100644
index 0000000000..1666a98458
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+ const channel = new MessageChannel();
+ window.parent.postMessage({ state: "port1", data: channel.port1 }, '*', [channel.port1]);
+ window.onmessage = () => window.parent.postMessage({ state: "port2", data: channel.port2 }, '*', [channel.port2]);
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-complex.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html
new file mode 100644
index 0000000000..c6896762bc
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+ const channel = new MessageChannel();
+ window.parent.postMessage(channel.port2, '*', [channel.port2]);
+ channel.port1.onmessage = e => { alert(e.data); channel.port1.postMessage("message event received") };
+ channel.port1.onmessageerror = () => channel.port1.postMessage("messageerror event received");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-failure.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html
new file mode 100644
index 0000000000..95a610f928
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+ const channel = new MessageChannel();
+ window.parent.postMessage(channel.port2, '*', [channel.port2]);
+ channel.port1.onmessage = e => { channel.port1.postMessage("message event received") };
+ channel.port1.onmessageerror = () => channel.port1.postMessage("messageerror event received");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html.headers
new file mode 100644
index 0000000000..56d0ac3428
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-messagechannel-site-failure.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: same-site
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html
new file mode 100644
index 0000000000..6f3f284ae2
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that messes with a given SharedArrayBuffer via MessageChannel</title>
+<script src="test-incrementer.js"></script>
+
+<script>
+ const channel = new MessageChannel();
+ window.parent.postMessage(channel.port2, '*', [channel.port2]);
+ setupDestinationIncrementer(channel.port1, channel.port1);
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html
new file mode 100644
index 0000000000..6f27ad7d5b
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that messes with a given SharedArrayBuffer</title>
+<script src="test-incrementer.js"></script>
+
+<script>
+"use strict";
+
+setupDestinationIncrementer(self, parent, "*");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html
new file mode 100644
index 0000000000..e583b5c416
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that messes with a given SharedArrayBuffer</title>
+<script src="test-incrementer.js"></script>
+
+<script>
+"use strict";
+
+setupDestinationIncrementer(self, opener, "*");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js
new file mode 100644
index 0000000000..c74fd26d3f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js
@@ -0,0 +1,7 @@
+"use strict";
+importScripts("./test-incrementer.js");
+
+self.onmessage = ({ data }) => {
+ // data will be a MessagePort
+ setupDestinationIncrementer(data, data);
+};
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker-with-channel.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js
new file mode 100644
index 0000000000..5801bd2b97
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js
@@ -0,0 +1,4 @@
+"use strict";
+importScripts("./test-incrementer.js");
+
+setupDestinationIncrementer(self, self);
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-worker.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html
new file mode 100644
index 0000000000..fe93cc0c4b
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Nesting level 1</title>
+
+<iframe src="nested-iframe-2.html"></iframe>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html
new file mode 100644
index 0000000000..fad52ce9de
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Nesting level 2</title>
+
+<iframe src="nested-iframe-3.html"></iframe>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html
new file mode 100644
index 0000000000..7971022b2c
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Nesting level 3</title>
+
+<iframe src="nested-iframe-4-incrementer.html"></iframe>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html
new file mode 100644
index 0000000000..d374515bdc
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that messes with a given SharedArrayBuffer, nested 4 levels deep in iframes</title>
+<script src="test-incrementer.js"></script>
+
+<script>
+"use strict";
+
+setupDestinationIncrementer(self, parent.parent.parent.parent.parent, "*");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js
new file mode 100644
index 0000000000..ffc3708acb
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js
@@ -0,0 +1,14 @@
+importScripts("/resources/testharness.js");
+importScripts("test-incrementer.js");
+
+promise_test(t => {
+ const worker = new Worker("incrementer-worker.js");
+
+ return testSharingViaIncrementerScript(t, worker, "parent worker", worker, "sub-worker");
+}, "postMessaging to a dedicated sub-worker allows them to see each others' modifications");
+
+test(() => {
+ assert_true(self.crossOriginIsolated);
+}, "Bonus: self.crossOriginIsolated");
+
+done();
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js
new file mode 100644
index 0000000000..4e8fba636c
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js
@@ -0,0 +1,33 @@
+"use strict";
+self.importScripts("/resources/testharness.js");
+
+let state = "start in worker";
+
+self.onmessage = e => {
+ if (e.data === "start in window") {
+ assert_equals(state, "start in worker");
+ e.source.postMessage(state);
+ state = "we are expecting a messageerror due to the window sending us a SAB";
+ } else if (e.data === "we are expecting a messageerror due to the worker sending us a SAB") {
+ assert_equals(state, "onmessageerror was received in worker");
+ e.source.postMessage(new SharedArrayBuffer());
+ state = "done in worker";
+ } else {
+ e.source.postMessage(`worker onmessage was reached when in state "${state}" and data ${e.data}`);
+ }
+};
+
+self.onmessageerror = e => {
+ if (state === "we are expecting a messageerror due to the window sending us a SAB") {
+ assert_equals(e.constructor.name, "ExtendableMessageEvent", "type");
+ assert_equals(e.data, null, "data");
+ assert_equals(e.origin, self.origin, "origin");
+ assert_not_equals(e.source, null, "source");
+ assert_equals(e.ports.length, 0, "ports length");
+
+ state = "onmessageerror was received in worker";
+ e.source.postMessage(state);
+ } else {
+ e.source.postMessage(`worker onmessageerror was reached when in state "${state}" and data ${e.data}`);
+ }
+};
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/serviceworker-failure.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js
new file mode 100644
index 0000000000..8472318abd
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js
@@ -0,0 +1,18 @@
+let state = "send-sw-failure"
+onconnect = initialE => {
+ let port = initialE.source;
+ port.postMessage(state)
+ port.onmessage = e => {
+ if(state === "" && e.data === "send-window-failure") {
+ port.postMessage(new SharedArrayBuffer())
+ } else {
+ port.postMessage("failure")
+ }
+ }
+ port.onmessageerror = e => {
+ if(state === "send-sw-failure") {
+ port.postMessage("send-sw-failure-success")
+ state = ""
+ }
+ }
+}
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/sharedworker-failure.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-incrementer.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-incrementer.js
new file mode 100644
index 0000000000..9c3fb813ae
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-incrementer.js
@@ -0,0 +1,80 @@
+"use strict";
+
+self.getViewValue = (view, index) => {
+ if(view instanceof DataView) {
+ return view.getInt8(index);
+ }
+ return view[index];
+};
+
+self.setViewValue = (view, index, value) => {
+ if(view instanceof DataView) {
+ view.setInt8(index, value);
+ } else {
+ view[index] = value;
+ }
+};
+
+self.maybeBigInt = (view, value) => {
+ if (view.constructor.name === "BigInt64Array" || view.constructor.name === "BigUint64Array") {
+ return BigInt(value);
+ }
+ return value;
+};
+
+self.testSharingViaIncrementerScript = (t, whereToListen, whereToListenLabel, whereToSend, whereToSendLabel, origin, type = "Int32Array") => {
+ return new Promise(resolve => {
+ const sab = new SharedArrayBuffer(8);
+ const view = new self[type](sab);
+ setViewValue(view, 0, maybeBigInt(view, 1));
+
+ whereToListen.onmessage = t.step_func(({ data }) => {
+ switch (data.message) {
+ case "initial payload received": {
+ assert_equals(data.value, maybeBigInt(view, 1), `The ${whereToSendLabel} must see the same value in the SharedArrayBuffer`);
+ break;
+ }
+
+ case "changed to 2": {
+ assert_equals(getViewValue(view, 0), maybeBigInt(view, 2), `The ${whereToListenLabel} must see changes made in the ${whereToSendLabel}`);
+
+ setViewValue(view, 0, maybeBigInt(view, 3));
+ whereToSend.postMessage({ message: "changed to 3" }, origin);
+
+ break;
+ }
+
+ case "changed to 3 received": {
+ assert_equals(data.value, maybeBigInt(view, 3), `The ${whereToSendLabel} must see changes made in the ${whereToListenLabel}`);
+ resolve();
+ break;
+ }
+ }
+ });
+
+ whereToSend.postMessage({ message: "initial payload", view }, origin);
+ });
+};
+
+self.setupDestinationIncrementer = (whereToListen, whereToSendBackTo, origin) => {
+ let view;
+ whereToListen.onmessage = ({ data }) => {
+ switch (data.message) {
+ case "initial payload": {
+ view = data.view;
+ whereToSendBackTo.postMessage({ message: "initial payload received", value: getViewValue(view, 0) }, origin);
+
+ setViewValue(view, 0, maybeBigInt(view, 2));
+ whereToSendBackTo.postMessage({ message: "changed to 2" }, origin);
+
+ break;
+ }
+
+ case "changed to 3": {
+ whereToSendBackTo.postMessage({ message: "changed to 3 received", value: getViewValue(view, 0) }, origin);
+
+ break;
+ }
+ }
+ };
+};
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-sab.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-sab.js
new file mode 100644
index 0000000000..6d6efda00d
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/test-sab.js
@@ -0,0 +1,15 @@
+"use strict";
+
+self.assertSABsHaveSameBackingBlock = (originalSAB, clonedSAB) => {
+ const originalView = new Uint8Array(originalSAB);
+ const clonedView = new Uint8Array(clonedSAB);
+
+ assert_not_equals(originalSAB, clonedSAB, "the clone must not be the same object");
+
+ assert_equals(originalView[0], 0, "originalView[0] starts 0");
+ assert_equals(clonedView[0], 0, "clonedView[0] starts 0");
+
+ originalView[0] = 5;
+ assert_equals(originalView[0], 5, "originalView[0] ends up 5");
+ assert_equals(clonedView[0], 5, "clonedView[0] ends up 5");
+};
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html
new file mode 100644
index 0000000000..28859f17cb
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SharedArrayBuffers cloning via history's methods invoking StructuredSerializeForStorage</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserializeforstorage">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-history-pushstate">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-history-replacestate">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+for (const method of ["pushState", "replaceState"]) {
+ test(() => {
+ assert_throws_dom("DataCloneError", () => {
+ history[method](new SharedArrayBuffer(), "dummy title");
+ });
+ }, `history.${method}(): simple case`);
+
+ test(() => {
+ let getter1Called = false;
+ let getter2Called = false;
+ assert_throws_dom("DataCloneError", () => {
+ history[method]([
+ { get x() { getter1Called = true; return 5; } },
+ new SharedArrayBuffer(),
+ { get x() { getter2Called = true; return 5; } }
+ ], "dummy title");
+ });
+
+ assert_true(getter1Called, "The getter before the SAB must have been called");
+ assert_false(getter2Called, "The getter after the SAB must not have been called");
+ }, `history.${method}(): is interleaved correctly`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-history.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-idb.any.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-idb.any.js
new file mode 100644
index 0000000000..e317b150cc
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-idb.any.js
@@ -0,0 +1,44 @@
+// META: script=/IndexedDB/resources/support.js
+"use strict";
+
+async_test(t => {
+ const openReq = createdb(t);
+
+ openReq.onupgradeneeded = e => {
+ const db = e.target.result;
+ const store = db.createObjectStore("store", { keyPath: "key" });
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+
+ assert_throws_dom("DataCloneError", () => {
+ store.put({ key: 1, property: sab });
+ });
+ t.done();
+ };
+}, "SharedArrayBuffer cloning via IndexedDB: basic case");
+
+async_test(t => {
+ const openReq = createdb(t);
+
+ openReq.onupgradeneeded = e => {
+ const db = e.target.result;
+ const store = db.createObjectStore("store", { keyPath: "key" });
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+
+ let getter1Called = false;
+ let getter2Called = false;
+
+ assert_throws_dom("DataCloneError", () => {
+ store.put({ key: 1, property: [
+ { get x() { getter1Called = true; return 5; } },
+ sab,
+ { get x() { getter2Called = true; return 5; } }
+ ]});
+ });
+
+ assert_true(getter1Called, "The getter before the SAB must have been called");
+ assert_false(getter2Called, "The getter after the SAB must not have been called");
+ t.done();
+ };
+}, "SharedArrayBuffer cloning via the IndexedDB: is interleaved correctly");
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-notifications-api.any.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-notifications-api.any.js
new file mode 100644
index 0000000000..4c1c1fdabb
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/serialization-via-notifications-api.any.js
@@ -0,0 +1,28 @@
+"use strict";
+
+test(() => {
+ assert_throws_dom("DataCloneError", () => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ new Notification("Bob: Hi", { data: sab });
+ })
+}, "SharedArrayBuffer cloning via the Notifications API's data member: basic case");
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+
+ let getter1Called = false;
+ let getter2Called = false;
+
+ assert_throws_dom("DataCloneError", () => {
+ new Notification("Bob: Hi", { data: [
+ { get x() { getter1Called = true; return 5; } },
+ sab,
+ { get x() { getter2Called = true; return 5; } }
+ ]});
+ });
+
+ assert_true(getter1Called, "The getter before the SAB must have been called");
+ assert_false(getter2Called, "The getter after the SAB must not have been called");
+}, "SharedArrayBuffer cloning via the Notifications API's data member: is interleaved correctly");
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html
new file mode 100644
index 0000000000..6fa196e094
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Structured cloning of SharedArrayBuffers into same-origin-domain windows</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+"use strict";
+document.domain = "{{host}}";
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "//{{domains[www1]}}:{{location[port]}}/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-domain-failure.sub.html";
+ let domain;
+ let childCrossOriginIsolated;
+ window.onmessage = t.step_func((e) => {
+ if (e.data.name === "domain") {
+ domain = e.data.value;
+ return;
+ }
+ if (e.data.name === "crossOriginIsolated") {
+ childCrossOriginIsolated = e.data.value;
+ return;
+ }
+ if (e.data.name === "hasSharedArrayBuffer") {
+ const hasSharedArrayBuffer = e.data.value;
+
+ // document.domain mutation is no-op because the surrounding agent
+ // cluster's cross-origin isolated is true.
+ assert_equals(domain, "{{domains[www1]}}");
+
+ // crossOriginIsolated is false in the nested frame because the frame is
+ // cross-origin and hence the cross-origin isolated capability is false.
+ // We use assert_equals instead of assert_false here to see if
+ // `childCrossOriginIsolated` is set.
+ assert_equals(childCrossOriginIsolated, false);
+
+ assert_false(hasSharedArrayBuffer);
+ t.done();
+ return;
+ }
+ assert_unreached("Got a message event, expected a messageerror event");
+ });
+ document.body.append(iframe);
+}, "SharedArrayBuffer and a same-origin-domain (but not same-origin) iframe");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-domain-failure.https.sub.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html
new file mode 100644
index 0000000000..203a9f637f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = get_host_info().HTTPS_NOTSAMESITE_ORIGIN + new URL("resources/iframe-failure.html", location).pathname;
+ window.onmessage = t.unreached_func("Got a message event, expected a messageerror event");
+ window.onmessageerror = t.step_func_done();
+ document.body.append(frame);
+}, "SharedArrayBuffer and a cross-site <iframe>");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-failure.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html
new file mode 100644
index 0000000000..acef65cbdf
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Structured cloning of SharedArrayBuffers into windows using MessageChannel</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-incrementer.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+
+<div id="log"></div>
+
+<script>
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ window.onmessage = t.step_func((message) => {
+ // data will be a MessagePort
+ resolve(testSharingViaIncrementerScript(t, message.data, "window", message.data, "iframe"));
+ });
+ iframe.src = "resources/incrementer-iframe-messagechannel.html";
+ document.body.appendChild(iframe);
+ });
+}, `postMessaging to a same-origin iframe via MessageChannel allows them to see each others' modifications`);
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ window.onmessage = t.step_func((message) => {
+ // data will be a MessagePort
+ message.data.postMessage(new SharedArrayBuffer(10));
+ message.data.onmessage = t.step_func(message => {
+ assert_equals(message.data, "messageerror event received");
+ resolve();
+ });
+ });
+ iframe.src = get_host_info().HTTPS_REMOTE_ORIGIN + new URL("resources/iframe-messagechannel-site-failure.html", window.location).pathname;
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to a same-site iframe via MessageChannel should fail");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ window.onmessage = t.step_func(message => {
+ // data will be a MessagePort
+ message.data.postMessage(new SharedArrayBuffer(10));
+ message.data.onmessage = t.step_func(message => {
+ assert_equals(message.data, "messageerror event received");
+ resolve();
+ });
+ });
+ iframe.src = get_host_info().HTTPS_NOTSAMESITE_ORIGIN + new URL("resources/iframe-messagechannel-failure.html", window.location).pathname;
+ document.body.append(iframe);
+ });
+}, "postMessaging to a cross-site iframe via MessageChannel should fail");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ let port = null;
+ window.onmessage = t.step_func(message => {
+ if (message.data.state === "port1") {
+ port = message.data.data;
+ port.postMessage(new SharedArrayBuffer(10));
+ message.source.postMessage("send port2", "*");
+ } else if (message.data.state === "port2") {
+ // Note that onmessage calls start()
+ message.data.data.onmessage = t.step_func(message => {
+ assert_true(message.data instanceof SharedArrayBuffer);
+ assert_equals(message.data.byteLength, 10);
+ resolve();
+ });
+ message.data.data.onmessageerror = t.unreached_func();
+ }
+ });
+ iframe.src = get_host_info().HTTPS_NOTSAMESITE_ORIGIN + new URL("resources/iframe-messagechannel-complex.html", window.location).pathname;
+ document.body.append(iframe);
+ });
+}, "postMessaging with a MessageChannel that's been cross-site should succeed");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-iframe-messagechannel.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html
new file mode 100644
index 0000000000..cd67e5b2c9
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Structured cloning of SharedArrayBuffers using MessageChannel</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ const worker = new Worker("resources/incrementer-worker-with-channel.js");
+ const channel = new MessageChannel();
+ worker.postMessage(channel.port2, [channel.port2]);
+
+ return testSharingViaIncrementerScript(t, channel.port1, "window", channel.port1, "worker");
+}, "postMessaging to a dedicated worker via MessageChannel allows them to see each others' modifications");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-messagechannel-success.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html
new file mode 100644
index 0000000000..373359de85
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SharedArrayBuffer cannot cross agent clusters, service worker edition</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+
+<script>
+"use strict";
+promise_test(t => {
+ const scope = "resources/blank.html";
+ return service_worker_unregister_and_register(t, "resources/serviceworker-failure.js", scope)
+ .then(reg => {
+ t.add_cleanup(() => service_worker_unregister(t, scope));
+ return wait_for_state(t, reg.installing, "activated");
+ })
+ .then(() => with_iframe(scope))
+ .then(iframe => {
+ t.add_cleanup(() => iframe.remove());
+ const sw = iframe.contentWindow.navigator.serviceWorker;
+ let state = "start in window";
+
+ return new Promise(resolve => {
+ sw.onmessage = t.step_func(e => {
+ if (e.data === "start in worker") {
+ assert_equals(state, "start in window");
+ sw.controller.postMessage(new SharedArrayBuffer());
+ state = "we are expecting confirmation of an onmessageerror in the worker";
+ } else if (e.data === "onmessageerror was received in worker") {
+ assert_equals(state, "we are expecting confirmation of an onmessageerror in the worker");
+ state = "we are expecting a messageerror due to the worker sending us a SAB";
+ sw.controller.postMessage(state);
+ } else {
+ assert_unreached("Got an unexpected message from the service worker: " + e.data);
+ }
+ });
+
+ sw.onmessageerror = t.step_func(e => {
+ assert_equals(state, "we are expecting a messageerror due to the worker sending us a SAB");
+
+ assert_equals(e.data, null, "data");
+ assert_equals(e.origin, self.origin, "origin");
+ assert_not_equals(e.source, null, "source");
+ assert_equals(e.ports.length, 0, "ports length");
+
+ state = "done in window";
+ resolve();
+ });
+
+ sw.controller.postMessage(state);
+ });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html
new file mode 100644
index 0000000000..023cb5acde
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>SharedArrayBuffer cannot cross agent clusters, shared worker edition</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+async_test(t => {
+ const sw = new SharedWorker("resources/sharedworker-failure.js")
+ let state = ""
+ sw.port.onmessage = t.step_func(e => {
+ if(e.data === "send-sw-failure") {
+ sw.port.postMessage(new SharedArrayBuffer())
+ } else if(e.data === "send-sw-failure-success") {
+ state = "send-window-failure"
+ sw.port.postMessage(state)
+ } else {
+ assert_unreached()
+ }
+ })
+ sw.port.onmessageerror = t.step_func(e => {
+ if(state === "send-window-failure") {
+ assert_equals(e.data, null, "data")
+ assert_equals(e.origin, "", "origin")
+ assert_equals(e.source, null, "source")
+ assert_equals(e.ports.length, 0, "ports length")
+ t.done()
+ } else {
+ assert_unreached()
+ }
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html
new file mode 100644
index 0000000000..c9b41d0a0d
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Structured cloning of SharedArrayBuffers: simple success cases that don't need dedicated files</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+[
+ "DataView",
+ "Int8Array",
+ "Uint8Array",
+ "Uint8ClampedArray",
+ "Int16Array",
+ "Uint16Array",
+ "Int32Array",
+ "Uint32Array",
+ "BigInt64Array",
+ "BigUint64Array",
+ "Float32Array",
+ "Float64Array"
+].forEach(type => {
+ promise_test(t => {
+ const worker = new Worker("resources/incrementer-worker.js");
+
+ return testSharingViaIncrementerScript(t, worker, "window", worker, "worker", undefined, type);
+ }, "postMessaging to a dedicated worker allows them to see each others' modifications with " + type);
+});
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ resolve(testSharingViaIncrementerScript(t, window, "window", iframe.contentWindow, "iframe", "*"));
+ });
+ iframe.src = "resources/incrementer-iframe.html";
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to a same-origin iframe allows them to see each others' modifications");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ const level1 = iframe.contentWindow;
+ const level2 = level1.frames[0];
+ const level3 = level2.frames[0];
+ const targetWindow = level3.frames[0];
+ resolve(testSharingViaIncrementerScript(t, window, "window", targetWindow, "nested iframe", "*"));
+ });
+ iframe.src = "resources/nested-iframe-1.html";
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to a same-origin deeply-nested iframe allows them to see each others' modifications");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const w = window.open("resources/incrementer-popup.html");
+ w.onload = t.step_func(() => {
+ resolve(testSharingViaIncrementerScript(t, window, "window", w, "popup window", "*").then(() => {
+ w.close();
+ }));
+ });
+ });
+}, "postMessaging to a same-origin opened window allows them to see each others' modifications");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html.headers b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html.headers
new file mode 100644
index 0000000000..63b60e490f
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-simple-success.https.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-extra.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-extra.html
new file mode 100644
index 0000000000..5ff10cbc10
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-extra.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Structured cloning of Error objects: extra tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- Most tests are in the general framework in structuredclone_0.html.
+ This contains specialty tests that don't fit into that framework. -->
+
+<body>
+
+<script>
+"use strict";
+test(t => {
+ const exceptionToThrow = new Error("throw me!");
+
+ const badError = new Error();
+ Object.defineProperty(badError, "name", { get() { throw exceptionToThrow; } });
+
+ const worker = new Worker("./resources/echo-worker.js");
+ t.add_cleanup(() => worker.terminate());
+
+ assert_throws_exactly(exceptionToThrow, () => {
+ worker.postMessage(badError);
+ });
+}, "Throwing name getter fails serialization");
+
+// https://bugs.chromium.org/p/chromium/issues/detail?id=1030086
+// https://github.com/whatwg/html/pull/5150
+async_test(t => {
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.name, "TypeError");
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.onload = () => {
+ if (iframe.contentWindow.location === "about:blank") {
+ return;
+ }
+
+ iframe.contentWindow.doIt();
+ };
+ iframe.src = "resources/post-parent-type-error.html";
+ document.body.append(iframe);
+}, "Errors sent across realms should preserve their type");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js
new file mode 100644
index 0000000000..cbc6a73d51
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-stack-optional.sub.window.js
@@ -0,0 +1,106 @@
+// META: script=/common/utils.js
+
+// .stack properties on errors are unspecified, but are present in most
+// browsers, most of the time. https://github.com/tc39/proposal-error-stacks/ tracks standardizing them.
+// Tests will pass automatically if the .stack property isn't present.
+
+stackTests(() => {
+ return new Error('some message');
+}, 'page-created Error');
+
+stackTests(() => {
+ return new DOMException('InvalidStateError', 'some message');
+}, 'page-created DOMException');
+
+stackTests(() => {
+ try {
+ Object.defineProperty();
+ } catch (e) {
+ return e;
+ }
+}, 'JS-engine-created TypeError');
+
+stackTests(() => {
+ try {
+ HTMLParagraphElement.prototype.align;
+ } catch (e) {
+ return e;
+ }
+}, 'web API-created TypeError');
+
+stackTests(() => {
+ try {
+ document.createElement('');
+ } catch (e) {
+ return e;
+ }
+}, 'web API-created DOMException');
+
+function stackTests(errorFactory, description) {
+ test(t => {
+ const error = errorFactory();
+ const originalStack = error.stack;
+
+ if (!originalStack) {
+ return;
+ }
+
+ const clonedError = structuredClone(error);
+ assert_equals(clonedError.stack, originalStack);
+ }, description + ' (structuredClone())');
+
+ async_test(t => {
+ const error = errorFactory();
+ const originalStack = error.stack;
+
+ if (!originalStack) {
+ t.done();
+ return;
+ }
+
+ const worker = new Worker('resources/echo-worker.js');
+ worker.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.stack, originalStack);
+ });
+
+ worker.postMessage(error);
+ }, description + ' (worker)');
+
+ let iframeTest = (t, url) => {
+ const thisTestId = token();
+
+ const error = errorFactory();
+ const originalStack = error.stack;
+
+ if (!originalStack) {
+ t.done();
+ return;
+ }
+
+ const iframe = document.createElement('iframe');
+ window.addEventListener('message', t.step_func(e => {
+ if (e.data.testId === thisTestId) {
+ assert_equals(e.data.error.stack, originalStack);
+ t.done();
+ }
+ }));
+
+ iframe.onload = t.step_func(() => {
+ iframe.contentWindow.postMessage({ error, testId: thisTestId }, "*");
+ });
+
+ iframe.src = url;
+ document.body.append(iframe);
+ }
+
+ async_test(t => {
+ const crossSiteURL = new URL('resources/echo-iframe.html', location.href);
+ crossSiteURL.hostname = '{{hosts[alt][www1]}}';
+ iframeTest(t, crossSiteURL);
+ }, description + ' (cross-site iframe)');
+
+ async_test(t => {
+ const sameOriginURL = new URL('resources/echo-iframe.html', location.href);
+ iframeTest(t, sameOriginURL);
+ }, description + ' (same-origin iframe)')
+}
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html
new file mode 100644
index 0000000000..c8a6d38393
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html
@@ -0,0 +1,637 @@
+<!doctype html>
+<html>
+ <head>
+ <meta content="text/html; charset=utf-8" http-equiv="content-type" />
+ <title>2.8 Common DOM interfaces - Structured Clone Algorithm </title>
+ <link rel="help" href="http://www.w3.org/TR/html5/common-dom-interfaces.html#safe-passing-of-structured-data" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <iframe></iframe> <!-- used for grabbing an URIError from another realm -->
+
+<script type="text/javascript">
+ var worker;
+ var testCollection;
+ setup(function()
+ {
+ //the worker is used for each test in sequence
+ //worker's callback will be set for each test
+ //worker's internal onmessage echoes the data back to this thread through postMessage
+ worker = new Worker("./resources/echo-worker.js");
+ testCollection = [
+ function() {
+ var t = async_test("Primitive string is cloned");
+ t.id = 0;
+ worker.onmessage = t.step_func(function(e) {assert_equals("primitive string", e.data, "\"primitive string\" === event.data"); t.done(); });
+ t.step(function() { worker.postMessage("primitive string");});
+ },
+ function() {
+ var t = async_test("Primitive integer is cloned");
+ t.id = 1;
+ worker.onmessage = t.step_func(function(e) {assert_equals(2000, e.data, "2000 === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(2000);});
+ },
+ function() {
+ var t = async_test("Primitive floating point is cloned");
+ t.id = 2;
+ worker.onmessage = t.step_func(function(e) {assert_equals(111.456, e.data, "111.456 === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(111.456);});
+ },
+ function() {
+ var t = async_test("Primitive floating point (negative) is cloned");
+ t.id = 3;
+ worker.onmessage = t.step_func(function(e) {assert_equals(-111.456, e.data, "-111.456 === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(-111.456);});
+ },
+ function() {
+ var t = async_test("Primitive number (hex) is cloned");
+ t.id = 4;
+ worker.onmessage = t.step_func(function(e) {assert_equals(0xAB25, e.data, "0xAB25 === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(0xAB25);});
+ },
+ function() {
+ var t = async_test("Primitive number (scientific) is cloned");
+ t.id = 5;
+ worker.onmessage = t.step_func(function(e) {assert_equals(15e2, e.data, "15e2 === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(15e2);});
+ },
+ function() {
+ var t = async_test("Primitive boolean is cloned");
+ t.id = 6;
+ worker.onmessage = t.step_func(function(e) {assert_equals(false, e.data, "false === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(false);});
+ },
+ function() {
+ var t = async_test("Instance of Boolean is cloned");
+ t.id = 7;
+ var obj;
+ t.step(function() {obj = new Boolean(false);});
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "Boolean === event.data.constructor");
+ assert_equals(obj.valueOf(), e.data.valueOf(), "(new Boolean(false)).valueof() === event.data.valueOf()");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },function() {
+ var t = async_test("Instance of Number is cloned");
+ t.id = 8;
+ var obj;
+ t.step(function() {obj = new Number(2000);});
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "Number === event.data.constructor");
+ assert_equals(obj.valueOf(), e.data.valueOf(), "(new Number(2000)).valueof() === event.data.valueOf()");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Instance of String is cloned");
+ t.id = 9;
+ var obj;
+ t.step(function() { obj = new String("String Object");});
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "String === event.data.constructor");
+ assert_equals(obj.valueOf(), e.data.valueOf(), "(new String(\"String Object\")).valueof() === event.data.valueOf()");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Instance of Date is cloned");
+ t.id = 10;
+ var obj;
+ t.step(function() { obj= new Date(2011,1,1);});
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "Date === event.data.constructor");
+ assert_equals(obj.valueOf(), e.data.valueOf(), "(new Date(2011,1,1)).valueof() === event.data.valueOf()");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Instance of RegExp is cloned");
+ t.id = 11;
+ var obj;
+ t.step(function() {obj = new RegExp("w3+c","g","i");});
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "RegExp === event.data.constructor");
+ assert_equals(obj.source, e.data.source, "canon.source === event.data.source");
+ assert_equals(obj.multiline, e.data.multiline, "canon.multiline === event.data.multiline");
+ assert_equals(obj.global, e.data.global, "canon.global === event.data.global");
+ assert_equals(obj.ignoreCase, e.data.ignoreCase, "canon.ignoreCase === event.data.ignoreCase");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Value 'null' is cloned");
+ t.id = 12;
+ worker.onmessage = t.step_func(function(e) {assert_equals(null, e.data, "null === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(null);});
+ },
+ function() {
+ var t = async_test("Value 'undefined' is cloned");
+ t.id = 13;
+ worker.onmessage = t.step_func(function(e) {assert_equals(undefined, e.data, "undefined === event.data"); t.done(); });
+ t.step(function() { worker.postMessage(undefined);});
+ },
+ function() {
+ var t = async_test("Object properties are cloned");
+ t.id = 14;
+ var obj;
+ t.step(function() {
+ obj= {};
+ obj.a = "test";
+ obj.b = 2;
+ obj["child"] = 3;
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_equals(obj.a, e.data.a, "canon.a === event.data.a");
+ assert_equals(obj.b, e.data.b, "canon.b === event.data.b");
+ assert_equals(obj.child, e.data.child, "canon.child === e.data.child");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Prototype chains are not walked.");
+ t.id = 15;
+ function Custom() {
+ this.a = "hello";
+ }
+
+ var obj;
+ t.step(function() {
+ Object.defineProperty(Custom.prototype, "b", { enumerable: true, value: 100 });
+ obj = new Custom();
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_not_equals(obj.constructor, e.data.constructor, "canon.constructor !== event.data.constructor");
+ assert_equals(Object, e.data.constructor, "Object === e.data.constructor");
+ assert_equals(obj.a, e.data.a, "canon.a === e.data.a");
+ assert_equals(undefined, e.data.b, "undefined === e.data.b");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Property descriptors of Objects are not cloned");
+ t.id = 16;
+ var obj;
+ t.step(function() {
+ obj = {};
+ Object.defineProperty(obj, "a", { enumerable: true, writable: false, value: 100 });
+ });
+ worker.onmessage = t.step_func(function(e) {
+ var des = Object.getOwnPropertyDescriptor(e.data, "a");
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_true(des.writable, "Descriptor is writable");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Cycles are preserved in Objects");
+ t.id = 17;
+ var obj;
+ t.step(function() {
+ obj = {};
+ obj.a = obj;
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_equals(e.data, e.data.a, "cycle is preserved");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Identity of duplicates is preserved");
+ t.id = 18;
+ var ref;
+ var obj;
+ t.step(function() {
+ ref = {};
+ ref.called = 0;
+ Object.defineProperty(ref, "child", {get: function(){this.called++;}, enumerable: true});
+
+ obj = {a:ref, b:ref};
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_equals(e.data.b.called, 0, "e.data.b.called === 0");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Property order is preserved");
+ t.id = 19;
+ var obj;
+ t.step(function() {
+ obj = { "a": "hello", "b": "w3c", "c": "and world" };
+ obj["a"] = "named1";
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ var canonNames = Object.getOwnPropertyNames(obj);
+ var testNames = Object.getOwnPropertyNames(e.data);
+ for (var i in canonNames) {
+ assert_equals(canonNames[i], testNames[i], "canonProperty["+i+"] === dataProperty["+i+"]");
+ }
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Enumerable properties of Arrays are cloned");
+ t.id = 20;
+ var obj;
+ t.step(function() {
+ obj = [0,1];
+ obj["a"] = "named1";
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_equals(e.data["a"], "named1", "e.data[\"a\"] === \"named1\"");
+ assert_equals(e.data[0], 0, "e.data[0] === 0");
+ assert_equals(e.data[1], 1, "e.data[1] === 1");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Property descriptors of Arrays are not cloned");
+ t.id = 21;
+ var obj;
+ t.step(function() {
+ obj = [0, 1];
+ Object.defineProperty(obj, "2", { enumerable: true, writable: false, value: 100 });
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_equals(e.data[0], 0, "e.data[0] === 0");
+ assert_equals(e.data[1], 1, "e.data[1] === 1");
+ var des = Object.getOwnPropertyDescriptor(e.data, "2");
+ assert_true(des.writable, "Descriptor is writable");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Cycles are preserved in Arrays");
+ t.id = 22;
+ var obj;
+ t.step(function() {
+ obj = [0,1];
+ obj[2] = obj;
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_equals(e.data[0], 0, "e.data[0] === 0");
+ assert_equals(e.data[1], 1, "e.data[1] === 1");
+ assert_equals(e.data[2], e.data, "e.data[2] === e.data");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+
+ function() {
+ var t = async_test("ImageData object can be cloned");
+ t.id = 23;
+ var obj;
+ t.step(function() {
+ var canvas = document.createElement("canvas");
+ canvas.width = 40;
+ canvas.height = 40;
+ var context = canvas.getContext('2d');
+ obj = context.createImageData(40, 40);
+ assert_true(window.hasOwnProperty("ImageData"), "ImageData constructor must be present");
+ assert_true(obj instanceof ImageData, "ImageData must be returned by .createImageData");
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_not_equals(obj, e.data, "cloned object should be a new instance of ImageData");
+ assert_equals(obj.width, e.data.width, "canon.width === e.data.width");
+ assert_equals(obj.height, e.data.height, "canon.height === e.data.height");
+ assert_array_equals(obj.data, e.data.data, "data arrays are the same");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("ImageData expandos are not cloned");
+ t.id = 24;
+ var obj;
+ t.step(function() {
+ var canvas = document.createElement("canvas");
+ canvas.width = 40;
+ canvas.height = 40;
+ var context = canvas.getContext('2d');
+ obj = context.createImageData(40, 40);
+ assert_true(window.hasOwnProperty("ImageData"), "ImageData constructor must be present");
+ assert_true(obj instanceof ImageData, "ImageData must be returned by .createImageData");
+ obj.foo = "bar";
+ });
+ worker.onmessage = t.step_func(function(e) {
+ assert_equals(obj.constructor, e.data.constructor, "canon.constructor === event.data.constructor");
+ assert_not_equals(obj, e.data, "cloned object should be a new instance of ImageData");
+ assert_equals(obj.width, e.data.width, "canon.width === e.data.width");
+ assert_equals(obj.height, e.data.height, "canon.height === e.data.height");
+ assert_array_equals(obj.data, e.data.data, "data arrays are the same");
+ assert_equals(undefined, e.data.foo, "Expando is lost (undefined === e.data.foo)");
+ t.done();
+ });
+ t.step(function() { worker.postMessage(obj);});
+ },
+ function() {
+ var t = async_test("Window objects cannot be cloned");
+ t.id = 25;
+ worker.onmessage = function() {}; //no op because exception should be thrown.
+ t.step(function() {
+ assert_true(DOMException.hasOwnProperty('DATA_CLONE_ERR'), "DOMException.DATA_CLONE_ERR is present");
+ assert_equals(DOMException.DATA_CLONE_ERR, 25, "DOMException.DATA_CLONE_ERR === 25");
+ assert_throws_dom('DATA_CLONE_ERR', function() {worker.postMessage(window)});
+ });
+ t.done();
+ },
+ function() {
+ var t = async_test("Document objects cannot be cloned");
+ t.id = 26;
+ worker.onmessage = function() {}; //no op because exception should be thrown.
+ t.step(function() {
+ assert_true(DOMException.hasOwnProperty('DATA_CLONE_ERR'), "DOMException.DATA_CLONE_ERR is present");
+ assert_equals(DOMException.DATA_CLONE_ERR, 25, "DOMException.DATA_CLONE_ERR === 25");
+ assert_throws_dom('DATA_CLONE_ERR', function() {worker.postMessage(document)});
+ });
+ t.done();
+ },
+ function() {
+ var t = async_test("Empty Error objects can be cloned");
+ t.id = 27;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), Error.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, Error, "Checking constructor");
+ assert_equals(e.data.name, "Error", "Checking name");
+ assert_false(e.data.hasOwnProperty("message"), "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = Error();
+ assert_false(error.hasOwnProperty("message"), "Checking message on the source realm");
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("Error objects can be cloned");
+ t.id = 28;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), Error.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, Error, "Checking constructor");
+ assert_equals(e.data.name, "Error", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = Error("some message");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("EvalError objects can be cloned");
+ t.id = 29;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), EvalError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, EvalError, "Checking constructor");
+ assert_equals(e.data.name, "EvalError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = EvalError("some message");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("RangeError objects can be cloned");
+ t.id = 30;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), RangeError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, RangeError, "Checking constructor");
+ assert_equals(e.data.name, "RangeError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = RangeError("some message");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("ReferenceError objects can be cloned");
+ t.id = 31;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), ReferenceError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, ReferenceError, "Checking constructor");
+ assert_equals(e.data.name, "ReferenceError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = ReferenceError("some message");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("SyntaxError objects can be cloned");
+ t.id = 32;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), SyntaxError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, SyntaxError, "Checking constructor");
+ assert_equals(e.data.name, "SyntaxError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = SyntaxError("some message");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("TypeError objects can be cloned");
+ t.id = 33;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), TypeError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, TypeError, "Checking constructor");
+ assert_equals(e.data.name, "TypeError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = TypeError("some message");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("URIError objects can be cloned");
+ t.id = 34;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), URIError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, URIError, "Checking constructor");
+ assert_equals(e.data.name, "URIError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = URIError("some message");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("URIError objects from other realms are treated as URIError");
+ t.id = 35;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), URIError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, URIError, "Checking constructor");
+ assert_equals(e.data.name, "URIError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = frames[0].URIError("some message");
+ assert_equals(Object.getPrototypeOf(error), frames[0].URIError.prototype, "Checking prototype before cloning");
+ assert_equals(error.constructor, frames[0].URIError, "Checking constructor before cloning");
+ assert_equals(error.name, "URIError", "Checking name before cloning");
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("Cloning a modified Error");
+ t.id = 36;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), TypeError.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, TypeError, "Checking constructor");
+ assert_equals(e.data.name, "TypeError", "Checking name");
+ assert_equals(e.data.message, "another message", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = URIError("some message");
+ Object.setPrototypeOf(error, SyntaxError.prototype);
+ error.message = {toString: () => "another message" }
+ error.constructor = RangeError;
+ error.name = "TypeError";
+ error.foo = "bar";
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("Error.message: getter is ignored when cloning");
+ t.id = 37;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), Error.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, Error, "Checking constructor");
+ assert_equals(e.data.name, "Error", "Checking name");
+ assert_false(e.data.hasOwnProperty("message"), "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = Error();
+ Object.defineProperty(error, "message", { get: () => "hello" });
+ assert_equals(error.message, "hello", "Checking message on the source realm");
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("Error.message: undefined property is stringified");
+ t.id = 38;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), Error.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, Error, "Checking constructor");
+ assert_equals(e.data.name, "Error", "Checking name");
+ assert_equals(e.data.message, "undefined", "Checking message");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = Error();
+ error.message = undefined;
+ assert_equals(error.message, undefined, "Checking message on the source realm");
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("DOMException objects can be cloned");
+ t.id = 39;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), DOMException.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, DOMException, "Checking constructor");
+ assert_equals(e.data.name, "IndexSizeError", "Checking name");
+ assert_equals(e.data.message, "some message", "Checking message");
+ assert_equals(e.data.code, DOMException.INDEX_SIZE_ERR, "Checking code");
+ assert_equals(e.data.foo, undefined, "Checking custom property");
+ });
+ t.step(function() {
+ const error = new DOMException("some message", "IndexSizeError");
+ worker.postMessage(error);
+ });
+ },
+ function() {
+ var t = async_test("DOMException objects created by the UA can be cloned");
+ t.id = 40;
+ worker.onmessage = t.step_func_done(function(e) {
+ assert_equals(Object.getPrototypeOf(e.data), DOMException.prototype, "Checking prototype");
+ assert_equals(e.data.constructor, DOMException, "Checking constructor");
+ assert_equals(e.data.code, DOMException.DATA_CLONE_ERR, "Checking code");
+ assert_equals(e.data.name, "DataCloneError", "Checking name");
+ });
+ t.step(function() {
+ try {
+ worker.postMessage(window);
+ } catch (error) {
+ worker.postMessage(error);
+ return;
+ }
+ assert_unreached("Window must not be clonable");
+ });
+ },
+ ];
+ }, {explicit_done:true});
+
+ //Callback for result_callback
+ //queues the next test in the array testCollection
+ //serves to make test execution sequential from the async worker callbacks
+ //alternatively, we would have to create a worker for each test
+ function testFinished(test) {
+ if(test.id < testCollection.length - 1) {
+ //queue the function so that stack remains shallow
+ queue(testCollection[test.id+1]);
+ } else {
+ //when the last test has run, explicitly end test suite
+ done();
+ }
+ }
+ function queue(func) {
+ step_timeout(func, 10);
+ }
+
+ add_result_callback(testFinished);
+ //start the first test manually
+ queue(testCollection[0]);
+
+
+
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js
new file mode 100644
index 0000000000..b3ecd86b40
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.js
@@ -0,0 +1,47 @@
+function assert_transfer_error(transferList) {
+ assert_throws_dom("DataCloneError", () => self.postMessage({ get whatever() { throw new Error("You should not have gotten to this point") } }, "*", transferList));
+}
+
+test(() => {
+ [self, self.document, new Image()].forEach(val => {
+ assert_transfer_error([val]);
+ });
+}, "Cannot transfer all objects");
+
+function transfer_tests(name, create) {
+ promise_test(async () => {
+ const transferable = await create();
+ assert_transfer_error([transferable, transferable]);
+ }, `Cannot transfer the same ${name} twice`);
+
+ promise_test(async () => {
+ const transferable = await create();
+ self.postMessage(null, "*", [transferable]);
+ assert_throws_dom("DataCloneError", () => self.postMessage(null, "*", [transferable]));
+ }, `Serialize should make the ${name} detached, so it cannot be transferred again`);
+
+ promise_test(async () => {
+ const transferable = await create(),
+ customError = new Error("hi");
+ self.postMessage(null, "*", [transferable]);
+ assert_throws_exactly(customError, () => self.postMessage({ get whatever() { throw customError } }, "*", [transferable]));
+ }, `Serialize should throw before a detached ${name} is found`);
+
+ promise_test(async () => {
+ const transferable = await create();
+ let seen = false;
+ const message = {
+ get a() {
+ self.postMessage(null, '*', [transferable]);
+ seen = true;
+ }
+ };
+ assert_throws_dom("DataCloneError", () => self.postMessage(message, "*", [transferable]));
+ assert_true(seen);
+ }, `Cannot transfer ${name} detached while the message was serialized`);
+}
+
+transfer_tests("ArrayBuffer", () => new ArrayBuffer(1));
+transfer_tests("MessagePort", () => new MessageChannel().port1);
+transfer_tests("ImageBitmap", () => self.createImageBitmap(document.createElement("canvas")));
+transfer_tests("OffscreenCanvas", () => new OffscreenCanvas(1, 1));
diff --git a/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.js b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.js
new file mode 100644
index 0000000000..2a46d790b8
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.js
@@ -0,0 +1,16 @@
+// META: script=/common/sab.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js
+
+runStructuredCloneBatteryOfTests({
+ structuredClone(data, transfer) {
+ return new Promise(resolve => {
+ window.addEventListener('message', function f(ev) {
+ window.removeEventListener('message', f);
+ resolve(ev.data.data);
+ });
+ window.postMessage({data, transfer}, "/", transfer);
+ });
+ }
+});
diff --git a/testing/web-platform/tests/html/infrastructure/terminology/plugins/sample.txt b/testing/web-platform/tests/html/infrastructure/terminology/plugins/sample.txt
new file mode 100644
index 0000000000..cedecd6d88
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/terminology/plugins/sample.txt
@@ -0,0 +1,3 @@
+This is a sample text/plain document.
+
+This is not an HTML document.
diff --git a/testing/web-platform/tests/html/infrastructure/terminology/plugins/text-plain.html b/testing/web-platform/tests/html/infrastructure/terminology/plugins/text-plain.html
new file mode 100644
index 0000000000..0ed0ce23b8
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/terminology/plugins/text-plain.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title id='title'>Media Types</title>
+ <link rel="author" title="Philippe Le Hegaret" href="mailto:plh@w3.org"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#plugins"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <script>
+ var t = async_test("A user agent must not consider the type text/plain as having a registered plugin.");
+
+ window.onload =
+ t.step_func(function() {
+ assert_equals(document.getElementById("frameContext").contentDocument.body.firstChild.nodeName, "PRE");
+ t.done();
+ });
+ </script>
+
+ <h1>Test of plugin support</h1>
+ <p class='assert'>A user agent must not consider the types text/plain and application/octet-stream as having a registered plugin.</p>
+
+ <iframe id="frameContext" src="sample.txt"></iframe>
+
+
+ <div id="log">Running test...</div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-detached-document.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-detached-document.https.window.js
new file mode 100644
index 0000000000..c51f425b7a
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-detached-document.https.window.js
@@ -0,0 +1,34 @@
+// Verify that an about:blank or about:srcdoc document remembers the baseURI
+// it was created with even after it's detached.
+const runTest = (frame_type) => {
+ async_test((t) => {
+ const frame = document.createElement('iframe');
+
+ if (frame_type == "about:blank")
+ frame.src = "about:blank";
+ else
+ frame.srcdoc = "foo";
+
+ frame.onload = () => {
+ const frame_doc = frame.contentDocument;
+ const initial_base_uri = document.baseURI;
+ assert_equals(initial_base_uri, frame_doc.baseURI);
+
+ const base_element = document.createElement('base');
+ base_element.href = "https://example.com";
+ document.head.appendChild(base_element);
+ assert_equals(initial_base_uri, frame_doc.baseURI);
+
+ frame.remove();
+ assert_equals(initial_base_uri, frame_doc.baseURI);
+ t.done();
+ };
+
+ document.body.appendChild(frame);
+ }, frame_type);
+};
+
+onload = () => {
+ runTest("about:blank");
+ runTest("about:srcdoc");
+};
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-javascript-nav.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-javascript-nav.https.window.js
new file mode 100644
index 0000000000..688eeaf384
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/base-url-javascript-nav.https.window.js
@@ -0,0 +1,22 @@
+// Verify that navigating an about:blank document to a javascript: URL that
+// creates a new document, copies over the base URL from the old document to the
+// new document.
+onload = () => {
+ async_test((t) => {
+ const frame = document.createElement('iframe');
+
+ frame.onload = () => {
+ assert_equals(document.baseURI, frame.contentDocument.baseURI);
+
+ // We'll need to monitor onload again for the javascript: navigation.
+ frame.onload = () => {
+ assert_equals(document.baseURI, frame.contentDocument.baseURI);
+ assert_equals('foo', frame.contentDocument.body.textContent);
+ };
+ frame.src = "javascript:'foo'";
+ t.done();
+ };
+
+ document.body.appendChild(frame);
+ }, "javascript: url nav base url test");
+};
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-uri-synthetic-document.html b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-uri-synthetic-document.html
new file mode 100644
index 0000000000..bed090aac4
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-uri-synthetic-document.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1486750">
+<link rel=help href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#document-base-url">
+<link rel=help href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+test(() => {
+ const doc1 = document.implementation.createHTMLDocument();
+ assert_equals(doc1.baseURI, 'about:blank', 'document.implementation.createHTMLDocument()');
+
+ const doc2 = document.implementation.createDocument('', '');
+ assert_equals(doc2.baseURI, 'about:blank', 'document.implementation.createDocument("", "")');
+
+ const doc3 = new Document();
+ assert_equals(doc3.baseURI, 'about:blank', 'new Document()');
+}, 'Synthetic documents should return about:blank for document.baseURI.');
+
+test(() => {
+ const doc = document.implementation.createHTMLDocument();
+ assert_equals(doc.baseURI, 'about:blank', 'baseURI should be about:blank without a <base>.');
+
+ const base = doc.createElement('base');
+ base.href = '/foo';
+ doc.head.appendChild(base);
+ assert_equals(doc.baseURI, 'about:blank', '<base> with relative URL should not change the about:blank baseURI.');
+
+ base.href = 'http://example.com/';
+ assert_equals(doc.baseURI, 'http://example.com/', '<base> with complete URL should replace the about:blank baseURI.');
+}, 'Synthetic documents should incorporate <base> href URLs correctly.');
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-about-srcdoc-2.https.html b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-about-srcdoc-2.https.html
new file mode 100644
index 0000000000..dab8d2122b
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-about-srcdoc-2.https.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+
+<iframe src='about:blank'></iframe>
+
+<script>
+// If document.open's behavior is modified to remove the url-rewriting behavior,
+// then this test can be deleted (https://github.com/whatwg/html/issues/3989).
+setup({ explicit_done: true });
+
+// This function is called by directly by the child iframe (above), with the
+// child iframe's document. The child iframe's document contains an about:srcdoc iframe.
+window.start = childDocument => {
+ const grandchildDocument =
+ childDocument.getElementById('foo').contentDocument;
+
+ test(() => {
+ assert_equals(childDocument.URL, 'about:blank',
+ 'Child document starting URL');
+ assert_equals(grandchildDocument.URL, 'about:srcdoc',
+ 'Grandchild document starting URL');
+ const originalChildBaseURL = childDocument.baseURI;
+
+ grandchildDocument.open("", "");
+ // Verify that the document.open() trick worked: the grandchild should now
+ // have the same url as the child, and have inherited the child's base url.
+ assert_equals(grandchildDocument.URL, 'about:blank',
+ 'Grandchild document after document.open() trick');
+ assert_equals(grandchildDocument.baseURI, originalChildBaseURL,
+ 'Grandchild base URL must match child base URL');
+
+ // Give child a new base URL.
+ const baseElement = childDocument.createElement('base');
+ baseElement.href = get_host_info().REMOTE_ORIGIN;
+ childDocument.head.append(baseElement);
+
+ // Verify that changing the child's base url succeeded and did not affect
+ // the grandchild's base url.
+ const newChildBaseURL = childDocument.baseURI;
+ assert_equals(grandchildDocument.URL, 'about:blank',
+ 'Grandchild document after child gets new base URL');
+ assert_not_equals(newChildBaseURL, originalChildBaseURL,
+ 'Child base URL must change');
+ assert_equals(grandchildDocument.baseURI, originalChildBaseURL,
+ 'Grandchild base URL must not change');
+ });
+
+ done();
+};
+
+let subframe_doc = document.querySelector('iframe').contentDocument;
+subframe_doc.body.innerHTML = '<iframe srcdoc="foo" id="foo"></iframe>';
+promise_test(async t => {
+ const grandchildIframe = subframe_doc.querySelector('iframe');
+ await new Promise(resolve => {
+ grandchildIframe.onload = resolve;
+ });
+
+ assert_equals(grandchildIframe.contentDocument.URL, 'about:srcdoc');
+
+ let script = subframe_doc.createElement('script');
+ script.innerHTML = 'parent.start(document);';
+ subframe_doc.body.appendChild(script);
+}, "wrapper promise test for timeout.");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-after-nav-about-srcdoc.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-after-nav-about-srcdoc.https.window.js
new file mode 100644
index 0000000000..897647a45e
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-changes-after-nav-about-srcdoc.https.window.js
@@ -0,0 +1,83 @@
+// META: script=/common/get-host-info.sub.js
+//
+// A test to verify that, when a srcdoc frame is created when it's parent
+// has a non-default base URI, and is later restored from history, it has
+// the same baseURI it started with, even if the parent has changed its own
+// baseURI in the meantime.
+// The parent always communicates with the child via postMessage since some
+// of the test cases include the child being sandboxed.
+
+async function sendMessage(frame, msg) {
+ const result = new Promise(r => onmessage = e => r(e.data));
+ frame.postMessage(msg, "*");
+ return await result;
+}
+
+const runTest = (description, sandbox_flags) => {
+ promise_test(async test => {
+ const original_parent_baseURI = document.baseURI;
+ // Create a URL for the child frame to navigate to.
+ const nav_target =
+ (new URL('./resources/send-back-base-url.html', location.href)).href;
+
+ // Change parent to a non-default baseURI.
+ const base_element = document.createElement("base");
+ base_element.href = get_host_info().REMOTE_ORIGIN;
+ document.head.appendChild(base_element);
+ assert_not_equals(document.baseURI, original_parent_baseURI,
+ "parent baseURI failed to change.");
+ const non_default_parent_baseURI = document.baseURI;
+
+ // Create child and load a srcdoc frame.
+ const iframe = document.createElement("iframe");
+ if (sandbox_flags !== null)
+ iframe.sandbox = sandbox_flags;
+ iframe.srcdoc = `
+ <head>
+ <script>
+ addEventListener('message', (event) => {
+ if (event.data == 'report baseURI')
+ event.source.postMessage(document.baseURI, event.origin);
+ if (event.data == 'click link')
+ document.getElementById('link').click();
+ });
+ parent.postMessage('loaded', '*');
+ </scr`+`ipt>
+ </head>
+ <body>
+ <a id='link' href='` + nav_target+ `'>link</a>
+ </body>
+ `;
+
+ const child_loaded = new Promise(r => onmessage = e => r(e.data));
+ document.body.appendChild(iframe);
+ assert_equals(await child_loaded, "loaded");
+
+ // Verify child's baseURI matches parent.
+ const child_base_uri = await sendMessage(frames[0], "report baseURI");
+ assert_equals(child_base_uri, non_default_parent_baseURI);
+
+ // Navigate child frame to non-srcdoc.
+ const child_loaded2 = await sendMessage(frames[0], "click link");
+ assert_equals(child_loaded2, "loaded");
+ const child_base_uri2 = await sendMessage(frames[0], "report baseURI");
+ assert_not_equals(child_base_uri2, non_default_parent_baseURI);
+ assert_not_equals(child_base_uri2, original_parent_baseURI);
+ assert_equals(child_base_uri2, nav_target);
+
+ // Parent resets its baseURI.
+ base_element.remove();
+ assert_equals(document.baseURI, original_parent_baseURI,
+ "parent baseURI failed to reset.");
+
+ // Navigate child back and verify its baseURI didn't change.
+ const child_loaded3 = new Promise(r => onmessage = e => r(e.data));
+ window.history.back();
+ assert_equals(await child_loaded3, "loaded");
+ const child_base_uri3 = await sendMessage(frames[0], "report baseURI");
+ assert_equals(child_base_uri3, non_default_parent_baseURI);
+ }, description);
+}
+
+runTest("non-sandboxed srcdoc - parent changes baseURI",null);
+runTest("sandboxed srcdoc - parent changes baseURI", "allow-scripts");
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-initiated-grand-parent.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-initiated-grand-parent.https.window.js
new file mode 100644
index 0000000000..1983f02c26
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-initiated-grand-parent.https.window.js
@@ -0,0 +1,62 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+
+const testBaseUriAboutBlankFromGrandParent = (description, child_origin) => {
+ promise_test(async test => {
+ // Create a child in an iframe.
+ const child_token = token();
+ const child_url = child_origin +
+ '/common/dispatcher/executor.html' +
+ `?uuid=${child_token}`;
+ const iframe = document.createElement("iframe");
+ iframe.src = child_url;
+ document.body.appendChild(iframe);
+
+ // The child creates a grand child in an iframe.
+ const reply_token = token();
+ send(child_token, `
+ const iframe = document.createElement("iframe");
+ location.hash = "interesting-fragment";
+ iframe.src = "/common/blank.html";
+ iframe.onload = () => {
+ send("${reply_token}", "grand child loaded");
+ };
+ document.body.appendChild(iframe);
+ `);
+ assert_equals(await receive(reply_token), "grand child loaded");
+
+ const child = iframe.contentWindow;
+ const grandchild = child[0];
+
+ // Navigate the grand-child toward about:blank.
+ // Navigation are always asynchronous. It doesn't exist a ways to know the
+ // about:blank document committed. A timer is used instead:
+ grandchild.location = "about:blank";
+ await new Promise(r => test.step_timeout(r, /*ms=*/500));
+
+ // The grandchild baseURI must correspond to its grand parent.
+ //
+ // Note: `child_token` is removed, to get a stable failure, in case the
+ // about:blank's document.baseURI reports the parent's URL instead of its
+ // grand-parent.
+ assert_equals(
+ grandchild.document.baseURI.replace(child_token, "child_token"),
+ self.document.baseURI);
+ }, description);
+}
+
+onload = () => {
+ testBaseUriAboutBlankFromGrandParent(
+ "Check the baseURL of an about:blank document same-origin with its parent",
+ get_host_info().HTTPS_ORIGIN,
+ );
+ testBaseUriAboutBlankFromGrandParent(
+ "Check the baseURL of an about:blank document cross-origin with its parent",
+ get_host_info().HTTPS_REMOTE_ORIGIN,
+ );
+ testBaseUriAboutBlankFromGrandParent(
+ "Check the baseURL of an about:blank document cross-site with its parent",
+ get_host_info().HTTPS_NOTSAMESITE_ORIGIN,
+ );
+}
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.js
new file mode 100644
index 0000000000..a8519b5d1b
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.js
@@ -0,0 +1,40 @@
+// Tests that a popup about:blank window inherits its base url from
+// the initiator, and not the opener.
+const runTest = (description) => {
+ const opener_base_uri = document.baseURI;
+
+ promise_test((test) => {
+ return new Promise(async resolve => {
+ window.popup = window.open();
+ test.add_cleanup(() => popup.close());
+ assert_equals(window.popup.location.href, 'about:blank');
+
+ // Create iframe to be the initiator.
+ const iframe = document.createElement('iframe');
+ iframe.srcdoc = `
+ <head>
+ <base href='https://example.com'>
+ <script>
+ window.top.popup.location.href = 'about:blank';
+ </scr` + `ipt>
+ </head>
+ <body></body>
+ `;
+
+ const popup_navigated = new Promise(r => window.popup.onpagehide = e => r());
+ document.body.append(iframe);
+ await popup_navigated; // This makes sure the old child has unloaded, but
+ // with the timeout below it's really not needed.
+
+ // This is necessary, or else the test times out. The about:blank load
+ // does not fire an onload event we can access.
+ test.step_timeout(resolve, 500);
+ }).then(() => {
+ assert_equals('https://example.com/', window.popup.document.baseURI);
+ });
+ }, description);
+};
+
+onload = () => {
+ runTest("window.open() gets base url from initiator not opener.");
+};
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-open-about-blank.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-open-about-blank.https.window.js
new file mode 100644
index 0000000000..1a60809023
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/document-base-url-window-open-about-blank.https.window.js
@@ -0,0 +1,24 @@
+// Basic test that a popup about:blank window inherits its base url from
+// the initiator (which in this case is also the opener).
+const runTest = (description) => {
+ // In this test the opener and the initiator will be the same.
+ const initiator_base_uri = document.baseURI;
+ test(() => {
+ const popup = window.open();
+
+ // Window.open synchronously loads the initial empty document.
+ assert_equals("about:blank", popup.location.href);
+ assert_equals(initiator_base_uri, popup.document.baseURI);
+
+ // Verify the popup's base url is properly snapshotted, and doesn't change
+ // if the parent's base url changes.
+ const base_element = document.createElement('base');
+ base_element.href = "https://example.com";
+ document.head.appendChild(base_element);
+ assert_equals(initiator_base_uri, popup.document.baseURI);
+ }, description);
+};
+
+onload = () => {
+ runTest("window.open() gets base url from initiator.");
+};
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/matches-about-blank-base-url.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/matches-about-blank-base-url.window.js
new file mode 100644
index 0000000000..b2a0740ddf
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/matches-about-blank-base-url.window.js
@@ -0,0 +1,29 @@
+// Verify that an iframe whose URL "matches about:blank" [1] uses its about base
+// URL and initiator/creator origin.
+//
+// [1]: https://html.spec.whatwg.org/#matches-about:blank
+onload = () => {
+ async_test(t => {
+ const iframe = document.createElement('iframe');
+ // Navigate the iframe away from the initial about:blank Document [1] to
+ // isolate the effects of a "matches about:blank" URL.
+ //
+ // [1]: https://html.spec.whatwg.org/#is-initial-about:blank
+ iframe.src = '/common/blank.html';
+
+ iframe.addEventListener('load', e => {
+ assert_equals(iframe.contentWindow.location.pathname, '/common/blank.html');
+
+ // Navigate the iframe to a URL that "matches about:blank" but isn't exactly
+ // "about:blank".
+ iframe.onload = t.step_func_done(() => {
+ assert_equals(iframe.contentDocument.URL, 'about:blank?query#fragment');
+ assert_equals(iframe.contentWindow.origin, window.origin);
+ assert_equals(iframe.contentDocument.baseURI, document.baseURI);
+ });
+ iframe.src = 'about:blank?query#fragment';
+ }, {once: true});
+
+ document.body.appendChild(iframe);
+ }, "about:blank and about:blank?foo#bar both 'match about:blank'");
+};
diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/resources/send-back-base-url.html b/testing/web-platform/tests/html/infrastructure/urls/base-url/resources/send-back-base-url.html
new file mode 100644
index 0000000000..6fdf81d1db
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/base-url/resources/send-back-base-url.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+ addEventListener('message', (event) => {
+ if (event.data == 'report baseURI')
+ event.source.postMessage(document.baseURI, event.origin);
+ });
+ parent.postMessage('loaded', '*');
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/dynamic-urls.sub.html b/testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/dynamic-urls.sub.html
new file mode 100644
index 0000000000..ce65f392d6
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/dynamic-urls.sub.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTML Test: Dynamic changes to base URLs</title>
+<link rel="author" title="Intel" href="http://www.intel.com/"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dynamic-changes-to-base-urls" />
+<base href="" id="base">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="div" style="display:none"></div>
+<script>
+ var div = document.getElementById("div"),
+ base = document.getElementById("base"),
+ url = document.location.href;
+
+ var testData = [
+ {elements: ["a", "link", "area"], set: "href", get: "href"},
+ {elements: ["q", "blockquote", "ins", "del"], set: "cite", get: "cite"},
+ {elements: ["audio", "input", "img", "embed", "video", "iframe", "script", "source", "track"], set: "src", get: "src"},
+ {elements: ["form"], set: "action", get: "action"},
+ {elements: ["object"], set: "data", get: "data"},
+ {elements: ["button"], set: "formAction", get: "formAction"}
+ ];
+
+ for (var i in testData) {
+ var item = testData[i];
+ for (var j in item.elements) {
+ test(function () {
+ var ele = document.createElement(item.elements[j]);
+
+ ele.setAttribute(item.set, "test.txt");
+ div.appendChild(ele);
+
+ base.setAttribute("href", "");
+ assert_equals(ele[item.get], url.substr(0, url.lastIndexOf("/")) +"/test.txt", "The '" + item.get + "' attribute is incorrect.");
+ base.setAttribute("href", "http://{{domains[www]}}:{{ports[http][0]}}");
+ assert_equals(ele[item.get], "http://{{domains[www]}}:{{ports[http][0]}}/test.txt", "The '" + item.get + "' attribute is incorrect.");
+ }, "The '" + item.set + "' attribute of the '" + item.elements[j] + "' element");
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/historical.sub.xhtml b/testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/historical.sub.xhtml
new file mode 100644
index 0000000000..1be7a7cf2c
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/historical.sub.xhtml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<html id="h" xmlns="http://www.w3.org/1999/xhtml" xml:base="">
+ <head>
+ <title>Historical xml:base must be removed</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/"/>
+ <link rel="help" href="https://github.com/whatwg/html/pull/84/files" />
+ <script src="/resources/testharness.js" id="s1"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="div" style="display:none" xml:base=""></div>
+ <script>
+ <![CDATA[
+ var div = document.getElementById("div"),
+ html = document.getElementById("h"),
+ url = document.location.href;
+
+ var testData = [
+ {elements: ["a", "link", "area"], set: "href", get: "href"},
+ {elements: ["q", "blockquote", "ins", "del"], set: "cite", get: "cite"},
+ {elements: ["audio", "input", "img", "embed", "video", "iframe", "script", "source", "track"], set: "src", get: "src"},
+ {elements: ["form"], set: "action", get: "action"},
+ {elements: ["object"], set: "data", get: "data"},
+ {elements: ["button"], set: "formaction", get: "formAction"}
+ ];
+
+ for (var i in testData) {
+ var item = testData[i];
+ for (var j in item.elements) {
+ test(function () {
+ var ele = document.createElement(item.elements[j]);
+
+ ele.setAttribute(item.set, "test.txt");
+ div.appendChild(ele);
+
+ html.setAttribute("xml:base", "");
+ assert_equals(ele[item.get], url.substr(0, url.lastIndexOf("/")) +"/test.txt", "The '" + item.get + "' attribute is incorrect.");
+ html.setAttribute("xml:base", "http://{{domains[www]}}:{{ports[http][0]}}");
+ assert_not_equals(ele[item.get], "http://{{domains[www]}}:{{ports[http][0]}}/test.txt", "The '" + item.get + "' attribute is incorrect.");
+ }, "The '" + item.set + "' attribute of the '" + item.elements[j] + "' element");
+ }
+ }
+
+ test(function () {
+ var s1 = document.getElementById("s1");
+ var val1 = s1.src;
+ var val2 = div.firstChild.href;
+
+ div.setAttribute("xml:base", "http://{{domains[www2]}}:{{ports[http][0]}}");
+ assert_equals(s1.src, val1, "The outer element must not be effected.");
+ assert_equals(div.firstChild.href, val2, "The inner element must be effected.");
+ assert_not_equals(div.firstChild.href, "http://{{domains[www2]}}:{{ports[http][0]}}/test.txt");
+ }, "Change the base URL must not effect the descendant elements");
+ ]]>
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/attributes.sub.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/attributes.sub.html
new file mode 100644
index 0000000000..d596f37716
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/attributes.sub.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<meta charset={{GET[encoding]}}> <!-- ends up as <meta charset> by default which is windows-1252 -->
+<meta name=variant content="?encoding=windows-1252">
+<meta name=variant content="?encoding=x-cp1251">
+<meta name=variant content="?encoding=utf8">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<link rel=help href=https://html.spec.whatwg.org/multipage/rendering.html#the-page>
+<link rel=help href=https://html.spec.whatwg.org/multipage/rendering.html#tables-2>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes>
+<div id=log></div>
+<script>
+function expected(encoding) {
+ return "?" + {
+ "UTF-8": "%C3%BF",
+ "windows-1251": "%26%23255%3B",
+ "windows-1252": "%FF"
+ }[encoding];
+}
+
+function assert_ends_with(input, endsWith) {
+ assert_true(input.endsWith(endsWith), input + " did not end with " + endsWith);
+}
+
+"body table thead tbody tfoot tr td th".split(" ").forEach(localName => {
+ test(t => {
+ const elm = document.createElement(localName);
+ document.body.appendChild(elm);
+ t.add_cleanup(() => { document.body.removeChild(elm); });
+ elm.setAttribute("background", "?\u00FF");
+ assert_ends_with(getComputedStyle(elm).backgroundImage, expected(document.characterSet) + "\")");
+ }, "getComputedStyle <" + localName + " background>");
+});
+
+function test_url_reflecting(localName, attr, idlAttr) {
+ idlAttr = idlAttr || attr;
+ test(() => {
+ let input = "?\u00FF";
+ const elm = document.createElement(localName);
+ assert_true(idlAttr in elm, idlAttr + " is not supported");
+ elm.setAttribute(attr, input);
+ assert_ends_with(elm[idlAttr], expected(document.characterSet));
+ }, "Getting <" + localName + ">." + idlAttr);
+}
+
+("iframe src, a href, area href, base href, link href, img src, embed src, object data, " +
+ "track src, video src, audio src, input src, form action, input formaction formAction, " +
+ "button formaction formAction, script src").split(", ").forEach(str => {
+ const arr = str.split(" ");
+ test_url_reflecting(arr[0], arr[1], arr[2]);
+});
+
+function test_string_reflecting(localName, attr) {
+ test(() => {
+ let input = "?\u00FF ?\u00FF";
+ const elm = document.createElement(localName);
+ assert_true(attr in elm, attr + " is not supported");
+ elm.setAttribute(attr, input);
+ assert_equals(elm[attr], input);
+ }, "Getting <" + localName + ">." + attr);
+}
+
+"a ping, area ping".split(", ").forEach(str => {
+ const arr = str.split(" ");
+ test_string_reflecting(arr[0], arr[1]);
+});
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/location.sub.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/location.sub.html
new file mode 100644
index 0000000000..1403cf18cd
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/location.sub.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset={{GET[encoding]}}> <!-- ends up as <meta charset> by default which is windows-1252 -->
+<meta name=variant content="?encoding=windows-1252">
+<meta name=variant content="?encoding=x-cp1251">
+<meta name=variant content="?encoding=utf8">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+function expected(encoding) {
+ return "?" + {
+ "UTF-8": "%C3%BF",
+ "windows-1251": "%26%23255%3B",
+ "windows-1252": "%FF"
+ }[encoding];
+}
+
+[
+ [(win, input) => { win.location = input; }, "location [PutForwards]"],
+ [(win, input) => { win.location.assign(input); }, "location.assign()"],
+ [(win, input) => { win.location.replace(input); }, "location.replace()"],
+ [(win, input) => { win.location.href = input; }, "location.href"]
+].forEach(([callback, desc]) => {
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ actualEncoding = document.characterSet
+ callback(frame.contentWindow, "/common/blank.html?\u00FF");
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentWindow.location.search, expected(actualEncoding));
+ });
+ }, desc);
+});
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ actualEncoding = document.characterSet;
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func(() => {
+ frame.contentWindow.location.search = "\u00FF";
+ frame.onload = t.step_func_done(() => {
+ // location.search always uses UTF-8
+ assert_equals(frame.contentWindow.location.search, expected("UTF-8"));
+ });
+ });
+}, "location.search");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/lone-surrogates.sub.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/lone-surrogates.sub.html
new file mode 100644
index 0000000000..e47ab0f310
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/lone-surrogates.sub.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset={{GET[encoding]}}> <!-- ends up as <meta charset> by default which is windows-1252 -->
+<meta name=variant content="?encoding=windows-1252">
+<meta name=variant content="?encoding=utf8">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+function expected(encoding) {
+ return "?" + {
+ // Replacement character (not bogus UTF-8)
+ "UTF-8": "%EF%BF%BD",
+ // Charref for the replacement character (not the lone surrogate)
+ "windows-1252": "%26%2365533%3B",
+ }[encoding];
+}
+
+test(t => {
+ const elm = document.createElement("a");
+ document.body.appendChild(elm);
+ t.add_cleanup(() => elm.remove());
+ elm.setAttribute("href", "?\uD800");
+
+ const shouldEndWith = expected(document.characterSet);
+ assert_true(
+ elm.href.endsWith(shouldEndWith),
+ `${elm.href} did not end with ${shouldEndWith}`
+ );
+}, `Query parsing with lone surrogates in ${document.characterSet}`);
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html
new file mode 100644
index 0000000000..7808434f05
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html
@@ -0,0 +1,162 @@
+<!doctype html>
+<meta charset={{GET[encoding]}}> <!-- ends up as <meta charset> by default which is windows-1252 -->
+<meta name=variant content="?encoding=windows-1252">
+<meta name=variant content="?encoding=x-cp1251">
+<meta name=variant content="?encoding=utf8">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/utils.js"></script>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#following-hyperlinks>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#hyperlink-auditing>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#attr-meta-http-equiv-refresh>
+<div id=log></div>
+<script>
+function expected(encoding) {
+ return {
+ "UTF-8": "%C3%BF",
+ "windows-1251": "%26%23255%3B",
+ "windows-1252": "%FF"
+ }[encoding];
+}
+var encoding = document.characterSet;
+var blank = 'resources/blank.py?encoding=' + encoding;
+var stash_put = 'resources/stash.py?q=\u00FF&action=put&id=';
+var stash_take = 'resources/stash.py?action=take&id=';
+var input_url_html = 'resources/resource.py?q=\u00FF&encoding=' + encoding + '&type=html';
+var expected_current = expected(encoding);
+var expected_utf8 = expected('UTF-8');
+
+function assert_ends_with(input, endsWith) {
+ assert_true(input.endsWith(endsWith), input + " did not end with " + endsWith);
+}
+
+
+function poll_for_stash(test_obj, uuid, expected) {
+ var start = new Date();
+ var poll = test_obj.step_func(function () {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', stash_take + uuid);
+ xhr.onload = test_obj.step_func(function(e) {
+ if (xhr.response == "") {
+ if (new Date() - start > 10000) {
+ // If we set the status to TIMEOUT here we avoid a race between the
+ // page and the test timing out
+ test_obj.force_timeout();
+ }
+ test_obj.step_timeout(poll, 200);
+ } else {
+ assert_equals(xhr.response, expected);
+ test_obj.done();
+ }
+ });
+ xhr.send();
+ })
+ test_obj.step_timeout(poll, 200);
+}
+
+function setup_navigation(elm, iframe, id, test_obj) {
+ iframe.name = id;
+ elm.target = id;
+ elm.setAttribute('href', input_url_html);
+ document.body.appendChild(iframe);
+ document.body.appendChild(elm);
+ test_obj.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ document.body.removeChild(elm);
+ });
+}
+
+// follow hyperlink
+function test_follow_link(tag) {
+ async_test(function() {
+ var elm = document.createElement(tag);
+ var iframe = document.createElement('iframe');
+ setup_navigation(elm, iframe, 'test_follow_link_'+tag, this);
+ iframe.onload = this.step_func_done(function() { // when the page navigated to has loaded
+ assert_equals(iframe.contentDocument.body.textContent, expected_current);
+ });
+ // follow the hyperlink
+ elm.click();
+ // check that navigation succeeded by ...??? XXX
+ }, 'follow hyperlink <'+tag+' href>');
+}
+
+'a, area'.split(', ').forEach(function(str) {
+ test_follow_link(str);
+});
+
+async_test(function() {
+ const iframe = document.createElement('iframe');
+ iframe.name = 'test_dont_follow_link';
+ document.body.appendChild(iframe);
+
+ const link = document.createElement('link');
+ link.target = iframe.name;
+ link.setAttribute('href', input_url_html);
+ document.body.appendChild(link);
+
+ const anchor = document.createElement('a');
+ anchor.target = iframe.name;
+ anchor.setAttribute('href', blank);
+ document.body.appendChild(anchor);
+
+ this.add_cleanup(function() {
+ iframe.remove();
+ link.remove();
+ anchor.remove();
+ });
+
+ iframe.onload = this.step_func_done(() => {
+ assert_equals(
+ iframe.contentDocument.location.pathname,
+ '/html/infrastructure/urls/resolving-urls/query-encoding/resources/blank.py',
+ 'The <a> navigation should occur instead of the <link> navigation.');
+ });
+
+ anchor.click();
+ link.click();
+}, `don't follow hyperlink <link href>`);
+
+// follow hyperlink with ping attribute
+function test_follow_link_ping(tag) {
+ async_test(function() {
+ var uuid = token();
+ var elm = document.createElement(tag);
+ // check if ping is supported
+ assert_true('ping' in elm, 'ping not supported');
+ elm.setAttribute('ping', stash_put + uuid);
+ var iframe = document.createElement('iframe');
+ setup_navigation(elm, iframe, 'test_follow_link_ping_'+tag, this);
+ // follow the hyperlink
+ elm.click();
+ // check that navigation succeeded by ...??? XXX
+ // check that the right URL was requested for the ping
+ poll_for_stash(this, uuid, expected_current);
+ }, 'hyperlink auditing <'+tag+' ping>');
+}
+
+'a, area'.split(', ').forEach(function(str) {
+ test_follow_link_ping(str);
+});
+
+// navigating with meta refresh
+async_test(function() {
+ var iframe = document.createElement('iframe');
+ iframe.src = blank;
+ document.body.appendChild(iframe);
+ this.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+ iframe.onload = this.step_func_done(function() {
+ var doc = iframe.contentDocument;
+ var got = doc.body.textContent;
+ if (got == '') {
+ doc.write('<meta http-equiv=refresh content="0; URL='+input_url_html+'">REFRESH');
+ doc.close();
+ return;
+ }
+ assert_equals(got, expected_current);
+ });
+}, 'meta refresh');
+
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/blank.py b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/blank.py
new file mode 100644
index 0000000000..bbd269d170
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/blank.py
@@ -0,0 +1,2 @@
+def main(request, response):
+ return [(b"Content-Type", b"text/html; charset=%s" % (request.GET[b'encoding']))], u""
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/css-tmpl.py b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/css-tmpl.py
new file mode 100644
index 0000000000..673b2fca32
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/css-tmpl.py
@@ -0,0 +1,7 @@
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ encoding = request.GET[b'encoding']
+ tmpl = request.GET[b'tmpl']
+ sheet = isomorphic_decode(tmpl) % u'\\0000E5'
+ return [(b"Content-Type", b"text/css; charset=%s" % encoding)], sheet.encode(isomorphic_decode(encoding))
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js
new file mode 100644
index 0000000000..74c4c2e997
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js
@@ -0,0 +1,713 @@
+// NOTE: this file needs to be split up rather than expanded. See ../location.sub.html for some
+// extracted tests. Tracked by https://github.com/web-platform-tests/wpt/issues/4934.
+
+/*
+* help:
+* https://html.spec.whatwg.org/multipage/#the-link-element
+* https://html.spec.whatwg.org/multipage/#styling
+* https://html.spec.whatwg.org/multipage/#prepare-a-script
+* https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm
+* https://html.spec.whatwg.org/multipage/#track-url
+* https://html.spec.whatwg.org/multipage/#concept-form-submit
+* https://html.spec.whatwg.org/multipage/#set-the-frozen-base-url
+* https://dom.spec.whatwg.org/#dom-node-baseuri
+* https://html.spec.whatwg.org/multipage/#the-a-element
+* https://html.spec.whatwg.org/multipage/#dom-worker
+* https://html.spec.whatwg.org/multipage/#dom-sharedworker
+* https://html.spec.whatwg.org/multipage/#dom-eventsource
+* https://html.spec.whatwg.org/multipage/#dom-xmldocument-load
+* https://html.spec.whatwg.org/multipage/#dom-open
+* http://url.spec.whatwg.org/#dom-url-search
+* https://www.w3.org/Bugs/Public/show_bug.cgi?id=24148
+* https://xhr.spec.whatwg.org/#the-open()-method
+* https://html.spec.whatwg.org/multipage/#set-up-a-worker-script-settings-object
+* https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-importscripts
+* https://html.spec.whatwg.org/multipage/#parse-a-websocket-url's-components
+* https://html.spec.whatwg.org/multipage/#dom-websocket-url
+* https://www.w3.org/Bugs/Public/show_bug.cgi?id=23968
+* http://dev.w3.org/csswg/cssom/#requirements-on-user-agents-implementing-the-xml-stylesheet-processing-instruction
+* http://url.spec.whatwg.org/#dom-url
+*/
+setup({explicit_done:true});
+onload = function() {
+ var encoding = '{{GET[encoding]}}';
+ var input_url = 'resources/resource.py?q=\u00E5&encoding=' + encoding + '&type=';
+ ('html css js worker sharedworker worker_importScripts sharedworker_importScripts worker_worker worker_sharedworker sharedworker_worker '+
+ 'sharedworker_sharedworker eventstream png svg xmlstylesheet_css video webvtt').split(' ').forEach(function(str) {
+ window['input_url_'+str] = input_url + str;
+ });
+ var blank = 'resources/blank.py?encoding=' + encoding;
+ var stash_put = 'resources/stash.py?q=\u00E5&action=put&id=';
+ var stash_take = 'resources/stash.py?action=take&id=';
+ var expected_obj = {
+ 'utf-8':'%C3%A5',
+ 'utf-16be':'%C3%A5',
+ 'utf-16le':'%C3%A5',
+ 'windows-1252':'%E5',
+ 'windows-1251':'%26%23229%3B'
+ };
+ var expected_current = expected_obj[encoding];
+ var expected_utf8 = expected_obj['utf-8'];
+
+ function msg(expected, got) {
+ return 'expected substring '+expected+' got '+got;
+ }
+
+ function poll_for_stash(test_obj, uuid, expected) {
+ var start = new Date();
+ var poll = test_obj.step_func(function () {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', stash_take + uuid);
+ xhr.onload = test_obj.step_func(function(e) {
+ if (xhr.response == "") {
+ if (new Date() - start > 10000) {
+ // If we set the status to TIMEOUT here we avoid a race between the
+ // page and the test timing out
+ test_obj.force_timeout();
+ }
+ test_obj.step_timeout(poll, 200);
+ } else {
+ assert_equals(xhr.response, expected);
+ test_obj.done();
+ }
+ });
+ xhr.send();
+ })
+ test_obj.step_timeout(poll, 200);
+ }
+
+ // loading html (or actually svg to support <embed>)
+ function test_load_nested_browsing_context(tag, attr, spec_url) {
+ subsetTestByKey('nested-browsing', async_test, function() {
+ var id = 'test_load_nested_browsing_context_'+tag;
+ var elm = document.createElement(tag);
+ elm.setAttribute(attr, input_url_svg);
+ elm.name = id;
+ document.body.appendChild(elm);
+ this.add_cleanup(function() {
+ document.body.removeChild(elm);
+ });
+ elm.onload = this.step_func_done(function() {
+ assert_equals(window[id].document.documentElement.textContent, expected_current);
+ });
+
+ }, 'load nested browsing context <'+tag+' '+attr+'>');
+ }
+
+ spec_url_load_nested_browsing_context = {
+ frame:'https://html.spec.whatwg.org/multipage/#process-the-frame-attributes',
+ iframe:'https://html.spec.whatwg.org/multipage/#process-the-iframe-attributes',
+ object:'https://html.spec.whatwg.org/multipage/#the-object-element',
+ embed:'https://html.spec.whatwg.org/multipage/#the-embed-element-setup-steps'
+ };
+
+ 'frame src, iframe src, object data, embed src'.split(', ').forEach(function(str) {
+ var arr = str.split(' ');
+ test_load_nested_browsing_context(arr[0], arr[1], spec_url_load_nested_browsing_context[arr[0]]);
+ });
+
+ // loading css with <link>
+ subsetTestByKey('loading', async_test, function() {
+ var elm = document.createElement('link');
+ elm.href = input_url_css;
+ elm.rel = 'stylesheet';
+ document.head.appendChild(elm);
+ this.add_cleanup(function() {
+ document.head.removeChild(elm);
+ });
+ elm.onload = this.step_func_done(function() {
+ var got = elm.sheet.href;
+ assert_true(elm.sheet.href.indexOf(expected_current) > -1, 'sheet.href ' + msg(expected_current, got));
+ assert_equals(elm.sheet.cssRules[0].style.content, '"'+expected_current+'"', 'sheet.cssRules[0].style.content');
+ });
+ }, 'loading css <link>');
+
+ // loading js
+ subsetTestByKey('loading-css', async_test, function() {
+ var elm = document.createElement('script');
+ elm.src = input_url_js + '&var=test_load_js_got';
+ document.head.appendChild(elm); // no cleanup
+ elm.onload = this.step_func_done(function() {
+ assert_equals(window.test_load_js_got, expected_current);
+ });
+ }, 'loading js <script>');
+
+ // loading image
+ function test_load_image(tag, attr, spec_url) {
+ subsetTestByKey('loading', async_test, function() {
+ var elm = document.createElement(tag);
+ if (tag == 'input') {
+ elm.type = 'image';
+ }
+ elm.setAttribute(attr, input_url_png);
+ document.body.appendChild(elm);
+ this.add_cleanup(function() {
+ document.body.removeChild(elm);
+ });
+ elm.onload = this.step_func_done(function() {
+ var got = elm.offsetWidth;
+ assert_equals(got, query_to_image_width[expected_current], msg(expected_current, image_width_to_query[got]));
+ });
+ // <video poster> doesn't notify when the image is loaded so we need to poll :-(
+ var interval;
+ var check_video_width = function() {
+ var width = elm.offsetWidth;
+ if (width != 300 && width != 0) {
+ clearInterval(interval);
+ elm.onload();
+ }
+ }
+ if (tag == 'video') {
+ interval = setInterval(check_video_width, 10);
+ }
+ }, 'loading image <'+tag+' '+attr+'>');
+ }
+
+ var query_to_image_width = {
+ '%E5':1,
+ '%26%23229%3B':1,
+ '%C3%A5':2,
+ '%3F':16,
+ 'unknown query':256,
+ 'default intrinsic width':300
+ };
+
+ var image_width_to_query = {};
+ (function() {
+ for (var x in query_to_image_width) {
+ image_width_to_query[query_to_image_width[x]] = x;
+ }
+ })();
+
+ var spec_url_load_image = {
+ img:'https://html.spec.whatwg.org/multipage/#update-the-image-data',
+ embed:'https://html.spec.whatwg.org/multipage/#the-embed-element-setup-steps',
+ object:'https://html.spec.whatwg.org/multipage/#the-object-element',
+ input:'https://html.spec.whatwg.org/multipage/#image-button-state-(type=image)',
+ video:'https://html.spec.whatwg.org/multipage/#poster-frame'
+ };
+
+ 'img src, embed src, object data, input src, video poster'.split(', ').forEach(function(str) {
+ var arr = str.split(' ');
+ test_load_image(arr[0], arr[1], spec_url_load_image[arr[0]]);
+ });
+
+ // XXX test <img srcset> or its successor
+
+ // loading video
+ function test_load_video(tag, use_source_element) {
+ subsetTestByKey('loading', async_test, function() {
+ var elm = document.createElement(tag);
+ var video_ext = '';
+ if (elm.canPlayType('video/webm; codecs="vp9,opus"')) {
+ video_ext = 'webm';
+ } else if (elm.canPlayType('video/ogg; codecs="theora,flac"')) {
+ video_ext = 'ogv';
+ } else if (elm.canPlayType('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) {
+ video_ext = 'mp4';
+ }
+ assert_not_equals(video_ext, '', 'no supported video format');
+ var source;
+ if (use_source_element) {
+ source = document.createElement('source');
+ elm.appendChild(source);
+ } else {
+ source = elm;
+ }
+ source.src = input_url_video + '&ext=' + video_ext;
+ elm.preload = 'auto';
+ elm.load();
+ this.add_cleanup(function() {
+ elm.removeAttribute('src');
+ if (elm.firstChild) {
+ elm.removeChild(elm.firstChild);
+ }
+ elm.load();
+ });
+ elm.onloadedmetadata = this.step_func_done(function() {
+ var got = Math.round(elm.duration);
+ assert_equals(got, query_to_video_duration[expected_current], msg(expected_current, video_duration_to_query[got]));
+ });
+ }, 'loading video <'+tag+'>' + (use_source_element ? '<source>' : ''));
+ }
+
+ var query_to_video_duration = {
+ '%E5':3,
+ '%26%23229%3B':3,
+ '%C3%A5':5,
+ '%3F':30,
+ 'unknown query':300,
+ 'Infinity':Infinity,
+ 'NaN':NaN
+ };
+
+ var video_duration_to_query = {};
+ (function() {
+ for (var x in query_to_video_duration) {
+ video_duration_to_query[query_to_video_duration[x]] = x;
+ }
+ })();
+
+ 'video, audio'.split(', ').forEach(function(str) {
+ test_load_video(str);
+ test_load_video(str, true);
+ });
+
+ // loading webvtt
+ subsetTestByKey('loading', async_test, function() {
+ var video = document.createElement('video');
+ var track = document.createElement('track');
+ video.appendChild(track);
+ track.src = input_url_webvtt;
+ track.track.mode = 'showing';
+ track.onload = this.step_func_done(function() {
+ var got = track.track.cues[0].text;
+ assert_equals(got, expected_current);
+ });
+ }, 'loading webvtt <track>');
+
+ // XXX downloading seems hard to automate
+ // <a href download>
+ // <area href download>
+
+ // submit forms
+ function test_submit_form(tag, attr) {
+ subsetTestByKey('submit', async_test, function(){
+ var elm = document.createElement(tag);
+ elm.setAttribute(attr, input_url_html);
+ var form;
+ var button;
+ if (tag == 'form') {
+ form = elm;
+ button = document.createElement('button');
+ } else {
+ form = document.createElement('form');
+ button = elm;
+ }
+ form.method = 'post';
+ form.appendChild(button);
+ var iframe = document.createElement('iframe');
+ var id = 'test_submit_form_' + tag;
+ iframe.name = id;
+ form.target = id;
+ button.type = 'submit';
+ document.body.appendChild(form);
+ document.body.appendChild(iframe);
+ this.add_cleanup(function() {
+ document.body.removeChild(form);
+ document.body.removeChild(iframe);
+ });
+ button.click();
+ iframe.onload = this.step_func_done(function() {
+ var got = iframe.contentDocument.body.textContent;
+ if (got == '') {
+ return;
+ }
+ assert_equals(got, expected_current);
+ });
+ }, 'submit form <'+tag+' '+attr+'>');
+ }
+
+ 'form action, input formaction, button formaction'.split(', ').forEach(function(str) {
+ var arr = str.split(' ');
+ test_submit_form(arr[0], arr[1]);
+ });
+
+ // <base href>
+ subsetTestByKey('base-href', async_test, function() {
+ var iframe = document.createElement('iframe');
+ iframe.src = blank;
+ document.body.appendChild(iframe);
+ this.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+ iframe.onload = this.step_func_done(function() {
+ var doc = iframe.contentDocument;
+ doc.write('<!doctype html><base href="'+input_url+'"><a href></a>');
+ doc.close();
+ var got_baseURI = doc.baseURI;
+ assert_true(got_baseURI.indexOf(expected_current) > -1, msg(expected_current, got_baseURI), 'doc.baseURI');
+ var got_a_href = doc.links[0].href;
+ assert_true(got_a_href.indexOf(expected_current) > -1, msg(expected_current, got_a_href), 'a.href');
+ });
+ }, '<base href>');
+
+ // XXX drag and drop (<a href> or <img src>) seems hard to automate
+
+ // Worker()
+ subsetTestByKey('workers', async_test, function() {
+ var worker = new Worker(input_url_worker);
+ worker.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_current);
+ });
+ }, 'Worker constructor');
+
+ // SharedWorker()
+ subsetTestByKey('workers', async_test, function() {
+ var worker = new SharedWorker(input_url_sharedworker);
+ worker.port.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_current);
+ });
+ }, 'SharedWorker constructor');
+
+ // EventSource()
+ subsetTestByKey('eventsource', async_test, function() {
+ var source = new EventSource(input_url_eventstream);
+ this.add_cleanup(function() {
+ source.close();
+ });
+ source.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_current);
+ });
+ }, 'EventSource constructor');
+
+ // EventSource#url
+ subsetTestByKey('eventsource', test, function() {
+ var source = new EventSource(input_url_eventstream);
+ source.close();
+ var got = source.url;
+ assert_true(source.url.indexOf(expected_current) > -1, msg(expected_current, got));
+ }, 'EventSource#url');
+
+ // window.open
+ subsetTestByKey('window-open', async_test, function() {
+ var id = 'test_window_open';
+ var iframe = document.createElement('iframe');
+ iframe.name = id;
+ document.body.appendChild(iframe);
+ this.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+ window.open(input_url_html, id);
+ iframe.onload = this.step_func(function() {
+ var got = iframe.contentDocument.body.textContent;
+ if (got != "") {
+ assert_equals(got, expected_current);
+ this.done();
+ }
+ });
+ }, 'window.open()');
+
+ // a.search, area.search
+ function test_hyperlink_search(tag) {
+ subsetTestByKey('hyperlink-search', test, function() {
+ var elm = document.createElement(tag);
+ var input_arr = input_url_html.split('?');
+ elm.href = input_arr[0];
+ elm.search = '?' + input_arr[1];
+ var got_href = elm.getAttribute('href');
+ assert_true(got_href.indexOf(expected_utf8) > -1, 'href content attribute ' + msg(expected_utf8, got_href));
+ var got_search = elm.search;
+ assert_true(got_search.indexOf(expected_utf8) > -1, 'getting .search '+msg(expected_utf8, got_search));
+ }, '<'+tag+'>.search');
+ }
+ 'a, area'.split(', ').forEach(function(str) {
+ test_hyperlink_search(str);
+ });
+
+ // history.pushState
+ // history.replaceState
+ function test_history(prop) {
+ subsetTestByKey('history', async_test, function() {
+ var url = input_url_html.replace('resources/', '');
+ var iframe = document.createElement('iframe');
+ iframe.src = blank;
+ document.body.appendChild(iframe);
+ this.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+ iframe.onload = this.step_func_done(function() {
+ // this should resolve against the iframe's URL
+ // "Parse url, relative to the relevant settings object of history."
+ // https://html.spec.whatwg.org/multipage/nav-history-apis.html#shared-history-push%2Freplace-state-steps
+ iframe.contentWindow.history[prop](null, null, url);
+ var got = iframe.contentWindow.location.href;
+ assert_true(got.indexOf(expected_current) > -1, msg(expected_current, got));
+ assert_not_equals(got.indexOf('/resources/'), -1, 'url was resolved against the test\'s URL');
+ });
+ }, 'history.'+prop);
+ }
+
+ 'pushState, replaceState'.split(', ').forEach(function(str) {
+ test_history(str);
+ });
+
+ // SVG
+ var ns = {svg:'http://www.w3.org/2000/svg', xlink:'http://www.w3.org/1999/xlink'};
+ // a
+ subsetTestByKey('svg', async_test, function() {
+ SVGAElement; // check support
+ var iframe = document.createElement('iframe');
+ var id = 'test_svg_a';
+ iframe.name = id;
+ var svg = document.createElementNS(ns.svg, 'svg');
+ var a = document.createElementNS(ns.svg, 'a');
+ a.setAttributeNS(ns.xlink, 'xlink:href', input_url_html);
+ a.setAttribute('target', id);
+ var span = document.createElement('span');
+ a.appendChild(span);
+ svg.appendChild(a);
+ document.body.appendChild(iframe);
+ document.body.appendChild(svg);
+ this.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ document.body.removeChild(svg);
+ });
+ span.click();
+ iframe.onload = this.step_func_done(function() {
+ var got = iframe.contentDocument.body.textContent;
+ if (got != '') {
+ assert_equals(got, expected_current);
+ }
+ });
+ }, 'SVG <a>');
+
+ // feImage, image, use
+ function test_svg(func, tag) {
+ subsetTestByKey('svg', async_test, function() {
+ var uuid = token();
+ var id = 'test_svg_'+tag;
+ var svg = document.createElementNS(ns.svg, 'svg');
+ var parent = func(svg, id);
+ var elm = document.createElementNS(ns.svg, tag);
+ elm.setAttributeNS(ns.xlink, 'xlink:href', stash_put + uuid + '#foo');
+ parent.appendChild(elm);
+ document.body.appendChild(svg);
+ this.add_cleanup(function() {
+ document.body.removeChild(svg);
+ });
+ poll_for_stash(this, uuid, expected_current);
+ }, 'SVG <' + tag + '>');
+ }
+
+ [[function(svg, id) {
+ SVGFEImageElement; // check support
+ var filter = document.createElementNS(ns.svg, 'filter');
+ filter.setAttribute('id', id);
+ svg.appendChild(filter);
+ var rect = document.createElementNS(ns.svg, 'rect');
+ rect.setAttribute('filter', 'url(#'+id+')');
+ svg.appendChild(rect);
+ return filter;
+ }, 'feImage'],
+ [function(svg, id) { SVGImageElement; return svg; }, 'image'],
+ [function(svg, id) { SVGUseElement; return svg; }, 'use']].forEach(function(arr) {
+ test_svg(arr[0], arr[1]);
+ });
+
+ // UTF-8:
+ // XHR
+ subsetTestByKey('xhr', async_test, function() {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', input_url_html);
+ xhr.onload = this.step_func_done(function() {
+ assert_equals(xhr.response, expected_current);
+ });
+ xhr.send();
+ }, 'XMLHttpRequest#open()');
+
+ // in a worker
+ subsetTestByKey('workers', async_test, function() {
+ var worker = new Worker(input_url_worker_importScripts);
+ worker.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_utf8);
+ });
+ }, 'importScripts() in a dedicated worker');
+
+ subsetTestByKey('workers', async_test, function() {
+ var worker = new Worker(input_url_worker_worker);
+ worker.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_utf8);
+ });
+ }, 'Worker() in a dedicated worker');
+
+ subsetTestByKey('workers', async_test, function() {
+ var worker = new SharedWorker(input_url_sharedworker_importScripts);
+ worker.port.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_utf8);
+ });
+ }, 'importScripts() in a shared worker');
+
+ subsetTestByKey('workers', async_test, function() {
+ var worker = new SharedWorker(input_url_sharedworker_worker);
+ worker.port.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_utf8);
+ });
+ }, 'Worker() in a shared worker');
+
+ // WebSocket()
+ subsetTestByKey('websocket', async_test, function() {
+ var ws = new WebSocket('ws://{{host}}:{{ports[ws][0]}}/echo-query?\u00E5');
+ this.add_cleanup(function() {
+ ws.close();
+ });
+ ws.onmessage = this.step_func_done(function(e) {
+ assert_equals(e.data, expected_utf8);
+ });
+ }, 'WebSocket constructor');
+
+ // WebSocket#url
+ subsetTestByKey('websocket', test, function() {
+ var ws = new WebSocket('ws://{{host}}:{{ports[ws][0]}}/echo-query?\u00E5');
+ ws.close();
+ var got = ws.url;
+ assert_true(ws.url.indexOf(expected_utf8) > -1, msg(expected_utf8, got));
+ }, 'WebSocket#url');
+
+ // CSS
+ function test_css(tmpl, expected_cssom, encoding, use_style_element) {
+ var desc = ['CSS', (use_style_element ? '<style>' : '<link> (' + encoding + ')'), tmpl].join(' ');
+ subsetTestByKey('css', async_test, function(){
+ css_is_supported(tmpl, expected_cssom, this);
+ var uuid = token();
+ var id = 'test_css_' + uuid;
+ var url = 'url(stash.py?q=%s&action=put&id=' + uuid + ')';
+ tmpl = tmpl.replace(/<id>/g, id).replace(/<url>/g, url);
+ var link;
+ if (use_style_element) {
+ link = document.createElement('style');
+ link.textContent = tmpl.replace(/%s/g, '\u00E5').replace(/stash\.py/g, 'resources/stash.py');
+ } else {
+ link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = 'resources/css-tmpl.py?encoding='+encoding+'&tmpl='+encodeURIComponent(tmpl);
+ }
+ var div = document.createElement('div');
+ div.id = id;
+ div.textContent='x';
+ document.head.appendChild(link);
+ document.body.appendChild(div);
+ this.add_cleanup(function() {
+ document.head.removeChild(link);
+ document.body.removeChild(div);
+ });
+ poll_for_stash(this, uuid, expected_utf8);
+ }, desc);
+ }
+
+ // fail fast if the input doesn't parse into the expected cssom
+ function css_is_supported(tmpl, expected_cssom, test_obj) {
+ if (expected_cssom === null) {
+ return;
+ }
+ var style = document.createElement('style');
+ style.textContent = tmpl.replace(/<id>/g, 'x').replace(/<url>/g, 'url(data:,)');
+ document.head.appendChild(style);
+ test_obj.add_cleanup(function() {
+ document.head.removeChild(style);
+ });
+ assert_equals(style.sheet.cssRules.length, expected_cssom.length, 'number of style rules');
+ for (var i = 0; i < expected_cssom.length; ++i) {
+ if (expected_cssom[i] === null) {
+ continue;
+ }
+ assert_equals(style.sheet.cssRules[i].style.length, expected_cssom[i], 'number of declarations in style rule #'+i);
+ }
+ }
+
+ [['#<id> { background-image:<url> }', [1] ],
+ ['#<id> { border-image-source:<url> }', [1] ],
+ ['#<id>::before { content:<url> }', [1] ],
+ ['@font-face { font-family:<id>; src:<url> } #<id> { font-family:<id> }', [null, 1] ],
+ ['#<id> { display:list-item; list-style-image:<url> }', [2] ],
+ ['@import <url>;', null ],
+ // XXX maybe cursor isn't suitable for automation here if browsers delay fetching it
+ ['#<id> { cursor:<url>, pointer }', [1] ]].forEach(function(arr) {
+ var input = arr[0];
+ var expected_cssom = arr[1];
+ var other_encoding = encoding == 'utf-8' ? 'windows-1252' : 'utf-8';
+ test_css(input, expected_cssom, encoding);
+ test_css(input, expected_cssom, other_encoding);
+ test_css(input, expected_cssom, null, true);
+ });
+
+ // XXX maybe test if they become relevant:
+ // binding (obsolete?)
+ // aural: cue-after, cue-before, play-during (not implemented?)
+ // hyphenate-resource (not implemented?)
+ // image() (not implemented?)
+
+ // <?xml-stylesheet?>
+ subsetTestByKey('xml', async_test, function() {
+ var iframe = document.createElement('iframe');
+ iframe.src = input_url_xmlstylesheet_css;
+ document.body.appendChild(iframe);
+ this.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+ iframe.onload = this.step_func_done(function() {
+ assert_equals(iframe.contentDocument.firstChild.sheet.cssRules[0].style.content, '"' + expected_utf8 + '"');
+ });
+ }, '<?xml-stylesheet?> (CSS)');
+
+ // new URL()
+ subsetTestByKey('url', test, function() {
+ var url = new URL('http://example.org/'+input_url);
+ var expected = expected_utf8;
+ assert_true(url.href.indexOf(expected) > -1, 'url.href '+msg(expected, url.href));
+ assert_true(url.search.indexOf(expected) > -1, 'url.search '+msg(expected, url.search));
+ }, 'URL constructor, url');
+
+ subsetTestByKey('url', test, function() {
+ var url = new URL('', 'http://example.org/'+input_url);
+ var expected = expected_utf8;
+ assert_true(url.href.indexOf(expected) > -1, 'url.href '+msg(expected, url.href));
+ assert_true(url.search.indexOf(expected) > -1, 'url.search '+msg(expected, url.search));
+ }, 'URL constructor, base');
+
+ // Test different schemes
+ function test_scheme(url, utf8) {
+ subsetTestByKey('scheme', test, function() {
+ var a = document.createElement('a');
+ a.setAttribute('href', url);
+ var got = a.href;
+ var expected = utf8 ? expected_utf8 : expected_current;
+ assert_true(got.indexOf(expected) != -1, msg(expected, got));
+ }, 'Scheme ' + url.split(':')[0] + ' (getting <a>.href)');
+ }
+
+ var test_scheme_urls = ['ftp://example.invalid/?x=\u00E5',
+ 'file:///?x=\u00E5',
+ 'http://example.invalid/?x=\u00E5',
+ 'https://example.invalid/?x=\u00E5',
+ ];
+
+ var test_scheme_urls_utf8 = ['ws://example.invalid/?x=\u00E5',
+ 'wss://example.invalid/?x=\u00E5',
+ 'gopher://example.invalid/?x=\u00E5',
+ 'mailto:example@invalid?x=\u00E5',
+ 'data:text/plain;charset='+encoding+',?x=\u00E5',
+ 'javascript:"?x=\u00E5"',
+ 'ftps://example.invalid/?x=\u00E5',
+ 'httpbogus://example.invalid/?x=\u00E5',
+ 'bitcoin:foo?x=\u00E5',
+ 'geo:foo?x=\u00E5',
+ 'im:foo?x=\u00E5',
+ 'irc:foo?x=\u00E5',
+ 'ircs:foo?x=\u00E5',
+ 'magnet:foo?x=\u00E5',
+ 'mms:foo?x=\u00E5',
+ 'news:foo?x=\u00E5',
+ 'nntp:foo?x=\u00E5',
+ 'sip:foo?x=\u00E5',
+ 'sms:foo?x=\u00E5',
+ 'smsto:foo?x=\u00E5',
+ 'ssh:foo?x=\u00E5',
+ 'tel:foo?x=\u00E5',
+ 'urn:foo?x=\u00E5',
+ 'webcal:foo?x=\u00E5',
+ 'wtai:foo?x=\u00E5',
+ 'xmpp:foo?x=\u00E5',
+ 'web+http:foo?x=\u00E5',
+ ];
+
+ test_scheme_urls.forEach(function(url) {
+ test_scheme(url);
+ });
+
+ test_scheme_urls_utf8.forEach(function(url) {
+ test_scheme(url, true);
+ });
+
+ done();
+};
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resource.py b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resource.py
new file mode 100644
index 0000000000..54b4bc9b5b
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resource.py
@@ -0,0 +1,103 @@
+import os
+import re
+
+from wptserve.utils import isomorphic_decode, isomorphic_encode
+
+def main(request, response):
+ type = request.GET[b'type']
+ encoding = request.GET[b'encoding']
+ # We want the raw input for 'q'
+ q = re.search(r'q=([^&]+)', request.url_parts.query).groups()[0]
+ if type == b'html':
+ return [(b"Content-Type", b"text/html; charset=utf-8")], isomorphic_encode(q)
+ elif type == b'css':
+ return [(b"Content-Type", b"text/css; charset=utf-8")], b"#test::before { content:'%s' }" % isomorphic_encode(q)
+ elif type == b'js':
+ return [(b"Content-Type", b"text/javascript; charset=utf-8")], b"%s = '%s';" % (request.GET[b'var'], isomorphic_encode(q))
+ elif type == b'worker':
+ return [(b"Content-Type", b"text/javascript")], b"postMessage('%s'); close();" % isomorphic_encode(q)
+ elif type == b'sharedworker':
+ return [(b"Content-Type", b"text/javascript")], b"onconnect = function(e) { e.source.postMessage('%s'); close(); };" % isomorphic_encode(q)
+ elif type == b'worker_importScripts':
+ return ([(b"Content-Type", b"text/javascript; charset=%s" % encoding)], # charset should be ignored for workers
+ b"""try {
+ var x = 'importScripts failed to run';
+ importScripts('?q=\\u00E5&type=js&var=x&encoding=%s');
+ postMessage(x);
+ close();
+ } catch(ex) {
+ postMessage(String(ex));
+ }""" % encoding)
+ elif type == b'worker_worker':
+ return ([(b"Content-Type", b"text/javascript; charset=%s" % encoding)], # charset should be ignored for workers
+ b"""try {
+ var worker = new Worker('?q=\\u00E5&type=worker&encoding=%s');
+ worker.onmessage = function(e) {
+ postMessage(e.data);
+ close();
+ };
+ } catch(ex) {
+ postMessage(String(ex));
+ }""" % encoding)
+ elif type == b'sharedworker_importScripts':
+ return ([(b"Content-Type", b"text/javascript; charset=%s" % encoding)], # charset should be ignored for workers
+ b"""var x = 'importScripts failed to run';
+ onconnect = function(e) {
+ var connect_port = e.source;
+ try {
+ importScripts('?q=\\u00E5&type=js&var=x&encoding=%s');
+ connect_port.postMessage(x);
+ close();
+ } catch(ex) {
+ connect_port.postMessage(String(ex));
+ }
+ };""" % encoding)
+ elif type == b'sharedworker_worker':
+ return ([(b"Content-Type", b"text/javascript; charset=%s" % encoding)], # charset should be ignored for workers
+ b"""onconnect = function(e) {
+ var connect_port = e.source;
+ try {
+ var worker = new Worker('?q=\\u00E5&type=worker&encoding=%s');
+ worker.onmessage = function(e) {
+ connect_port.postMessage(e.data);
+ close();
+ };
+ } catch(ex) {
+ connect_port.postMessage(String(ex));
+ }
+ };""" % encoding)
+ elif type == b'eventstream':
+ return [(b"Content-Type", b"text/event-stream")], b"data: %s\n\n" % isomorphic_encode(q)
+ elif type == b'svg':
+ return [(b"Content-Type", b"image/svg+xml")], b"<svg xmlns='http://www.w3.org/2000/svg'>%s</svg>" % isomorphic_encode(q)
+ elif type == b'xmlstylesheet_css':
+ return ([(b"Content-Type", b"application/xhtml+xml; charset=%s" % encoding)],
+ (u"""<?xml-stylesheet href="?q=&#x00E5;&amp;type=css&amp;encoding=%s"?><html xmlns="http://www.w3.org/1999/xhtml"/>""" % isomorphic_decode(encoding))
+ .encode(isomorphic_decode(encoding)))
+ elif type == b'png':
+ if q == u'%E5' or q == u'%26%23229%3B':
+ image = u'green-1x1.png'
+ elif q == u'%C3%A5':
+ image = u'green-2x2.png'
+ elif q == u'%3F':
+ image = u'green-16x16.png'
+ else:
+ image = u'green-256x256.png'
+ rv = open(os.path.join(request.doc_root, u"images", image), "rb").read()
+ return [(b"Content-Type", b"image/png")], rv
+ elif type == b'video':
+ ext = request.GET[b'ext']
+ if q == u'%E5' or q == u'%26%23229%3B':
+ video = u'A4' # duration: 3
+ elif q == u'%C3%A5':
+ video = u'movie_5' # duration: 5
+ elif q == u'%3F':
+ video = u'green-at-15' # duration: 30
+ else:
+ video = u'movie_300' # duration: 300
+ rv = open(os.path.join(request.doc_root, u"media", u"%s.%s" % (video, isomorphic_decode(ext))), "rb").read()
+ if ext == b'ogv':
+ ext = b'ogg'
+ return [(b"Content-Type", b"video/%s" % ext)], rv
+ elif type == b'webvtt':
+ return [(b"Content-Type", b"text/vtt")], b"WEBVTT\n\n00:00:00.000 --> 00:00:01.000\n%s" % isomorphic_encode(q)
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/stash.py b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/stash.py
new file mode 100644
index 0000000000..a6b589d2ac
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/stash.py
@@ -0,0 +1,15 @@
+import re
+
+def main(request, response):
+ key = request.GET[b'id']
+ action = request.GET[b'action']
+ if action == b'put':
+ # We want the raw input for 'q'
+ q = re.search(r'q=([^&]+)', request.url_parts.query).groups()[0]
+ request.server.stash.put(key, q)
+ return [(b"Content-Type", b"text/html")], u'Put %s' % q
+ else:
+ q = request.server.stash.take(key)
+ if q != None:
+ return [(b"Content-Type", b"text/html")], q
+ return [], u""
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html
new file mode 100644
index 0000000000..f0279ab1c6
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html
Binary files differ
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html
new file mode 100644
index 0000000000..e29385b8a7
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html
Binary files differ
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-8.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-8.html
new file mode 100644
index 0000000000..b2b3b9cb28
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/utf-8.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Resolving URLs, URL character encoding, utf-8</title>
+<meta name="timeout" content="long">
+<meta name="variant" content="?include=nested-browsing">
+<meta name="variant" content="?include=loading">
+<meta name="variant" content="?include=submit">
+<meta name="variant" content="?include=base-href">
+<meta name="variant" content="?include=workers">
+<meta name="variant" content="?include=eventsource">
+<meta name="variant" content="?include=window-open">
+<meta name="variant" content="?include=hyperlink-search">
+<meta name="variant" content="?include=history">
+<meta name="variant" content="?include=svg">
+<meta name="variant" content="?include=xhr">
+<meta name="variant" content="?include=websocket">
+<meta name="variant" content="?include=css">
+<meta name="variant" content="?include=xml">
+<meta name="variant" content="?include=url">
+<meta name="variant" content="?include=scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests-by-key.js"></script>
+<script src="/common/utils.js"></script>
+<div id=log></div>
+<script src="resources/resolve-url.js?encoding=utf-8&pipe=sub"></script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1251.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1251.html
new file mode 100644
index 0000000000..dbf136706e
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1251.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=windows-1251>
+<title>Resolving URLs, URL character encoding, windows-1251</title>
+<meta name="timeout" content="long">
+<meta name="variant" content="?include=nested-browsing">
+<meta name="variant" content="?include=loading">
+<meta name="variant" content="?include=submit">
+<meta name="variant" content="?include=base-href">
+<meta name="variant" content="?include=workers">
+<meta name="variant" content="?include=eventsource">
+<meta name="variant" content="?include=window-open">
+<meta name="variant" content="?include=hyperlink-search">
+<meta name="variant" content="?include=history">
+<meta name="variant" content="?include=svg">
+<meta name="variant" content="?include=xhr">
+<meta name="variant" content="?include=websocket">
+<meta name="variant" content="?include=css">
+<meta name="variant" content="?include=xml">
+<meta name="variant" content="?include=url">
+<meta name="variant" content="?include=scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests-by-key.js"></script>
+<script src="/common/utils.js"></script>
+<div id=log></div>
+<script src="resources/resolve-url.js?encoding=windows-1251&pipe=sub"></script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1252.html b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1252.html
new file mode 100644
index 0000000000..43b7dc9ea4
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/windows-1252.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=windows-1252>
+<title>Resolving URLs, URL character encoding, windows-1252</title>
+<meta name="timeout" content="long">
+<meta name="variant" content="?include=nested-browsing">
+<meta name="variant" content="?include=loading">
+<meta name="variant" content="?include=submit">
+<meta name="variant" content="?include=base-href">
+<meta name="variant" content="?include=workers">
+<meta name="variant" content="?include=eventsource">
+<meta name="variant" content="?include=window-open">
+<meta name="variant" content="?include=hyperlink-search">
+<meta name="variant" content="?include=history">
+<meta name="variant" content="?include=svg">
+<meta name="variant" content="?include=xhr">
+<meta name="variant" content="?include=websocket">
+<meta name="variant" content="?include=css">
+<meta name="variant" content="?include=xml">
+<meta name="variant" content="?include=url">
+<meta name="variant" content="?include=scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/subset-tests-by-key.js"></script>
+<script src="/common/utils.js"></script>
+<div id=log></div>
+<script src="resources/resolve-url.js?encoding=windows-1252&pipe=sub"></script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.js
new file mode 100644
index 0000000000..b8fad36ccf
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.js
@@ -0,0 +1,27 @@
+// META: script=/common/get-host-info.sub.js
+
+// Load about:srcdoc in a sandboxed iframe. Check the document.baseURI is
+// correct.
+const runTest = (description, iframe_sandbox) => {
+ promise_test(async test => {
+ const iframe = document.createElement("iframe");
+ iframe.sandbox = iframe_sandbox;
+ iframe.srcdoc = `
+ <script>
+ parent.postMessage(document.baseURI, '*');
+ </scr`+`ipt>
+ `;
+ const child_base_uri = new Promise(r => onmessage = e => r(e.data));
+ document.body.appendChild(iframe);
+ // [spec]: https://html.spec.whatwg.org/C/#fallback-base-url
+ // Step 1: If document is an iframe srcdoc document, then return the
+ // document base URL of document's browsing context's container
+ // document.
+ assert_equals(await child_base_uri, document.baseURI);
+ }, description);
+}
+
+onload = () => {
+ runTest("allow-same-origin", "allow-scripts allow-same-origin");
+ runTest("disallow-same-origin", "allow-scripts");
+}
diff --git a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-changes-about-srcdoc.https.window.js b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-changes-about-srcdoc.https.window.js
new file mode 100644
index 0000000000..f3eca2e0b5
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url-changes-about-srcdoc.https.window.js
@@ -0,0 +1,47 @@
+// Load about:srcdoc in a iframe. Check the document.baseURI is still
+// correct after the parent changes its base URL..
+const runTest = (description, sandbox_flags) => {
+ promise_test(async test => {
+ // Create child.
+ const iframe = document.createElement("iframe");
+ if (sandbox_flags !== null)
+ iframe.sandbox = sandbox_flags;
+ iframe.srcdoc = `
+ <script>
+ addEventListener('message', (event) => {
+ if (event.data == 'report baseURI')
+ event.source.postMessage(document.baseURI, event.origin);
+ });
+ parent.postMessage('loaded', '*');
+ </scr`+`ipt>
+ `;
+
+ const child_loaded = new Promise(r => onmessage = e => r(e.data));
+ document.body.appendChild(iframe);
+ assert_equals(await child_loaded, "loaded");
+
+ // Verify child's baseURI matches parent.
+ const original_parent_baseURI = document.baseURI;
+ const child_base_uri = new Promise(r => onmessage = e => r(e.data));
+ frames[0].postMessage("report baseURI", "*");
+ assert_equals(await child_base_uri, original_parent_baseURI);
+
+ // Parent changes its baseURI, requests child to report.
+ const base_element = document.createElement("base");
+ base_element.href = "https://foo.com";
+ document.head.appendChild(base_element);
+ assert_not_equals(document.baseURI, original_parent_baseURI,
+ "parent baseURI failed to change.");
+
+ // Verify child's baseURI didn't change.
+ const child_base_uri2 = new Promise(r => onmessage = e => r(e.data));
+ frames[0].postMessage("report baseURI", "*");
+ assert_equals(await child_base_uri2, original_parent_baseURI);
+
+ // Cleanup.
+ base_element.remove();
+ }, description);
+}
+
+runTest("non-sandboxed srcdoc - parent changes baseURI",null);
+runTest("sandboxed srcdoc - parent changes baseURI", "allow-scripts");
diff --git a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url.html b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url.html
new file mode 100644
index 0000000000..ee9d726481
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/document-base-url.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: document base URL</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ iframe { display: none }
+</style>
+<body onload="on_load()">
+ <div id="log"></div>
+ <script>
+ function assert_resolve_url(doc, base) {
+ var img = doc.createElement("img");
+ img.src = "foo";
+ var actual = img.src;
+ var expected = base + "/foo";
+ assert_equals(actual, expected, "img src should resolve correctly");
+ }
+
+ var t1 = async_test("The document base URL of a document containing one or more base elements with href attributes is the frozen base URL of the first base element in the document that has an href attribute, in tree order.");
+
+ function on_load() {
+ t1.step(function () {
+ var base = document.createElement("base");
+ base.setAttribute("href", "/foo/bar");
+ document.head.appendChild(base);
+ t1.add_cleanup(function () {
+ document.head.removeChild(base);
+ });
+
+ assert_resolve_url(document, location.href.replace(location.pathname, "/foo"));
+ assert_equals(document.baseURI, base.href, "The document base URL should be URL of the first base element that has an href attribute.");
+ });
+ t1.done();
+ }
+
+ async_test(function() {
+ var iframe = document.createElement("iframe");
+ iframe.onload = this.step_func_done(function () {
+ assert_resolve_url(iframe.contentDocument, location.href.replace(location.pathname, "/common"));
+ assert_equals(iframe.contentDocument.baseURI, iframe.contentDocument.location.href, "The document base URL should be the document's address.");
+ });
+ iframe.setAttribute("src", "/common/blank.html");
+ document.body.appendChild(iframe);
+ }, "The fallback base URL of a document containing no base element is the document's address.");
+
+ async_test(function () {
+ var iframe = document.createElement("iframe");
+ iframe.onload = this.step_func_done(function () {
+ assert_resolve_url(iframe.contentDocument, location.href.replace("/document-base-url.html", ""));
+ assert_equals(iframe.contentDocument.baseURI, document.baseURI, "The document base URL should be the creator document's base URL.");
+ });
+ iframe.setAttribute("src", "about:blank");
+ document.body.appendChild(iframe);
+ }, "The fallback base URL of a document whose address is about:blank is the document base URL of the creator document.");
+
+ async_test(function () {
+ var iframe = document.createElement("iframe");
+ iframe.onload = this.step_func_done(function () {
+ var doc = iframe.contentDocument;
+ var base = doc.body.appendChild(doc.createElement("base"));
+ base.href = "sub/";
+ assert_resolve_url(doc, location.href.replace("/document-base-url.html", "/sub"));
+ assert_equals(doc.baseURI, document.baseURI.replace("/document-base-url.html", "/sub/"));
+ });
+ iframe.setAttribute("src", "about:blank");
+ document.body.appendChild(iframe);
+ }, "about:blank with a base element.");
+
+ async_test(function () {
+ var iframe = document.createElement("iframe");
+ iframe.onload = this.step_func_done(function () {
+ assert_resolve_url(iframe.contentDocument, location.href.replace("/document-base-url.html", ""));
+ assert_equals(iframe.contentDocument.baseURI, document.baseURI, "The document base URL should be the containing document's base URL.");
+ });
+ iframe.setAttribute("srcdoc", "<p>foobar</p>");
+ document.body.appendChild(iframe);
+ }, "The fallback base URL of an iframe srcdoc document is the document base URL of the document's browsing context's browsing context container's document.");
+
+ async_test(function () {
+ var iframe = document.createElement("iframe");
+ iframe.onload = this.step_func_done(function () {
+ var doc = iframe.contentDocument;
+ assert_resolve_url(doc, location.href.replace("/document-base-url.html", "/sub"));
+ assert_equals(doc.baseURI, document.baseURI.replace("/document-base-url.html", "/sub/"),
+ "The srcdoc document's base URL should be set by the <base> tag.");
+ });
+ iframe.setAttribute("srcdoc", "<base href='sub/'><p>foobar</p>");
+ document.body.appendChild(iframe);
+ }, "The base URL of an iframe srcdoc document with a <base> tag should be set by that tag.");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/multiple-base.sub.html b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/multiple-base.sub.html
new file mode 100644
index 0000000000..49c428c5be
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/multiple-base.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>document base URL: multiple base elements</title>
+<base target="_blank" >
+<base href="http://{{domains[www]}}:{{ports[http][0]}}/">
+<base href="http://{{domains[www1]}}:{{ports[http][0]}}/">
+<base href="http://{{domains[www2]}}:{{ports[http][0]}}/">
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ test(function(){
+ var base = document.querySelectorAll("base");
+ assert_equals(document.baseURI, document.querySelectorAll("base[href]")[0].href);
+ }, "If there are multiple <base> elements, the document base URL is the frozen base URL of the first one that has an href attribute");
+</script>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/nontraditional-about-srcdoc.html b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/nontraditional-about-srcdoc.html
new file mode 100644
index 0000000000..bdb33842a7
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/nontraditional-about-srcdoc.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Non-traditional about:srcdoc documents</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9514">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+
+ const srcdocOpenPromise = new Promise(resolve => {
+ window.srcdocOpenResolve = resolve;
+ });
+
+ iframe.srcdoc = `
+ <body onload="document.open();window.parent.srcdocOpenResolve();"></body>`;
+ document.body.append(iframe);
+
+ await srcdocOpenPromise;
+ assert_equals(iframe.contentDocument.URL, 'about:srcdoc');
+
+ // Calling the `about:srcdoc` Window's `fetch()` like this uses that Window's
+ // environment settings object as the request's client, which is where the
+ // request's referrer comes from, per
+ // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer.
+ //
+ // If this `document.open()`d srcdoc document is considered a proper
+ // `about:srcdoc` document, the referrer will not be `about:srcdoc`, but will
+ // instead come from the parent document.
+ let referrer = await iframe.contentWindow.fetch('resources/echo-referrer-text.py');
+ referrer = await referrer.text();
+ assert_equals(referrer, location.href,
+ 'The request referrer is retrieved from the parent document for ' +
+ 'about:srcdoc documents');
+
+ // Observe that the "about base URL" is retrieved [1] for `document.baseURI`
+ // as well, indicating that the document is treated like a normal srcdoc
+ // document.
+ // [1]: https://html.spec.whatwg.org/#fallback-base-url
+ assert_equals(iframe.contentDocument.baseURI, location.href,
+ 'The about base URL is retrieved as the base URL for srcdoc documents');
+}, 'about:srcdoc with document.open() is treated like a normal about:srcdoc document');
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+
+ const javascriptURLPromise = new Promise(resolve => {
+ window.javascriptURLResolve = resolve;
+ });
+
+ iframe.srcdoc = `
+ <script>
+ location.href = "javascript:'<body onload=window.parent.javascriptURLResolve();>Document contents here</body>'";
+ </scr`+`ipt>`;
+ document.body.append(iframe);
+
+ // This promise will resolve as a result of script running in the *new*
+ // document that gets created by the `javascript:` URL.
+ await javascriptURLPromise;
+ assert_equals(iframe.contentDocument.URL, 'about:srcdoc');
+
+ // See the first assertion in the first test in this file.
+ let referrer = await iframe.contentWindow.fetch('resources/echo-referrer-text.py');
+ referrer = await referrer.text();
+ assert_equals(referrer, location.href,
+ 'The request referrer is retrieved from the parent document for ' +
+ 'about:srcdoc documents');
+
+ // See the second assertion in the first test in this file.
+ assert_equals(iframe.contentDocument.baseURI, location.href,
+ 'The about base URL is retrieved as the base URL for srcdoc documents');
+}, 'about:srcdoc navigated via a `javascript:` URL is treated like a normal about:srcdoc document');
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+
+ const srcdocLoadPromise = new Promise(resolve => {
+ iframe.onload = resolve;
+ });
+ iframe.srcdoc = `Document contents here`;
+ document.body.append(iframe);
+
+ await srcdocLoadPromise;
+ assert_equals(iframe.contentDocument.URL, 'about:srcdoc');
+
+ // Change the document's URL to `about:srcdoc#foo` and ensure that the
+ // observable behavior in that document is consistent with treating it as a
+ // normal `about:srcdoc` document.
+ iframe.contentWindow.history.replaceState(null, '', 'about:srcdoc#foo');
+ assert_equals(iframe.contentDocument.URL, 'about:srcdoc#foo');
+
+ // See the first assertion in the first test in this file.
+ let referrer = await iframe.contentWindow.fetch('resources/echo-referrer-text.py');
+ referrer = await referrer.text();
+ assert_equals(referrer, location.href,
+ 'The request referrer is retrieved from the parent document for ' +
+ 'about:srcdoc documents');
+
+ // See the second assertion in the first test in this file.
+ assert_equals(iframe.contentDocument.baseURI, location.href,
+ 'The about base URL is retrieved as the base URL for srcdoc documents');
+}, 'about:srcdoc with URL changed by history.replaceState() is treated like ' +
+ 'a normal about:srcdoc document');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/infrastructure/urls/terminology-0/resources/echo-referrer-text.py b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/resources/echo-referrer-text.py
new file mode 100644
index 0000000000..8a48d3346a
--- /dev/null
+++ b/testing/web-platform/tests/html/infrastructure/urls/terminology-0/resources/echo-referrer-text.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ response_headers = [(b"Content-Type", b"text/plain")]
+ body = b"%s"% request.headers.get(b"referer", b"")
+ return (200, response_headers, body)
diff --git a/testing/web-platform/tests/html/interaction/focus/chrome-object-tab-focus-bug.html b/testing/web-platform/tests/html/interaction/focus/chrome-object-tab-focus-bug.html
new file mode 100644
index 0000000000..5127ee1cb6
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/chrome-object-tab-focus-bug.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tabbing through object tag</title>
+<link rel="author" title="atotic@chromium.org">
+<link rel="help" href="https://crbug.com/1132895">
+<meta assert="assert" content="Tabbed focus works through OBJECT tags">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>Pressing TAB twice should focus/highlight end checkbox</p>
+
+<input type="checkbox" id="start">start</a>
+<object type='text/html' data='data:text/html,' width='16' height='16'></object>
+<input type="checkbox" id="end" >end</a>
+
+<script>
+
+let t = async_test("focus advances with tab key thorough object element");
+
+let start = document.querySelector("#start");
+let object = document.querySelector("object");
+let end = document.querySelector("#end");
+let tab = "\uE004";
+
+t.step( _ => {
+ document.querySelector("#start").focus();
+ assert_equals(document.activeElement, start, "start got focus");
+ test_driver.send_keys(document.activeElement, tab).then( _ => {
+ t.step( _ => {
+ assert_equals(document.activeElement, object, "object got focus");
+ test_driver.send_keys(document.activeElement, tab).then( _ => {
+ t.step( _ => {
+ assert_equals(document.activeElement, end, "end got focus");
+ t.done();
+ });
+ });
+ });
+ });
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/composed.window.js b/testing/web-platform/tests/html/interaction/focus/composed.window.js
new file mode 100644
index 0000000000..8951afc4e0
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/composed.window.js
@@ -0,0 +1,16 @@
+async_test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ let happened = false;
+ input.onfocus = t.step_func(e => {
+ happened = true;
+ assert_equals(e.type, "focus");
+ assert_true(e.composed);
+ });
+ input.focus();
+ input.onblur = t.step_func_done(e => {
+ assert_true(happened);
+ assert_equals(e.type, "blur");
+ assert_true(e.composed);
+ });
+ input.blur();
+}, "Focus events are composed");
diff --git a/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-has-system-focus.html b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-has-system-focus.html
new file mode 100644
index 0000000000..26f0bbbd09
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-has-system-focus.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - document has system focus</title>
+<link rel="help" href="https://html.spec.whatwg.org/#has-focus-steps">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input id="input">
+<script>
+
+promise_test(async t => {
+ await new Promise(r => window.onload = r);
+ // This test requires the document to have focus as a starting condition.
+ // Whether a newly loaded page receives focus or not, seems to be somewhat
+ // browser-dependent and situation-dependent. For instance, Firefox appears to
+ // focus the page immediately if the page was loaded with the refresh button,
+ // but not if it was loaded from pressing ENTER in the URL bar. To ensure a
+ // reliable starting condition for this test, we give an extra push for focus.
+ if (!document.hasFocus()) {
+ const input = document.getElementById("input");
+ input.focus();
+ await new Promise(r => input.onfocus = r);
+ }
+ assert_true(document.hasFocus(), "Document has focus as starting condition.");
+
+ let gotBlur = false;
+ window.onblur = () => gotBlur = true;
+ const popup = window.open("support/popup.html", "otherwindow", "resizable");
+ assert_not_equals(popup, null, "Test requires popup be opened");
+ t.add_cleanup(() => popup.close());
+ const msg = await new Promise(r => window.onmessage = ({data}) => r(data));
+ assert_equals(msg, "focus = true",
+ "Test requires popups be focused (may require harness flags)");
+ assert_true(gotBlur, "Document received blur event when popup opened");
+ assert_false(document.hasFocus(), "Document lost focus when popup opened");
+
+ const p = new Promise(r => window.onfocus = r);
+ popup.close();
+ await p;
+ assert_true(true, "Document received focus event when popup closed");
+ assert_true(document.hasFocus(), "Document regained focus when popup closed");
+}, "Top-level document receives blur/focus events and loses system focus " +
+ "during opening/closing of a popup");
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-level-apis.html b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-level-apis.html
new file mode 100644
index 0000000000..4a8a9a291a
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/document-level-apis.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - document-level APIs</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document-level-focus-apis">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<input id="test">
+<script>
+
+test(function () {
+ assert_equals(document.activeElement, document.body, "The active element should be the body element.");
+}, "The body element must be the active element if no element is focused");
+
+test(function () {
+ document.getElementById("test").focus();
+ assert_equals(document.activeElement, document.getElementById("test"), "The active element should be the input element.");
+}, "The element must be the active element if it is focused");
+
+function frame_load () {
+ test(function () {
+ document.getElementById("fr").contentDocument.getElementById("ipt").focus();
+ assert_equals(document.activeElement, document.getElementById("fr"), "The active element should be the iframe element.");
+ }, "When a child browsing context is focused, its browsing context container is also focused");
+}
+
+test(function () {
+ var doc = document.implementation.createHTMLDocument("test");
+ assert_false(doc.hasFocus(), "The hasFocus() method should return false.");
+}, "The hasFocus() method must return false if the Document has no browsing context");
+
+</script>
+<iframe id="fr" src="support/test.html" onload="frame_load()"></iframe>
diff --git a/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/popup.html b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/popup.html
new file mode 100644
index 0000000000..3a4419105a
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/popup.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+Popup <input id="input">
+<script>
+window.onload = async () => {
+ try {
+ if (!document.hasFocus()) {
+ const input = document.getElementById("input");
+ input.focus();
+ await new Promise(r => input.onfocus = r);
+ }
+ opener.postMessage(`focus = ${document.hasFocus()}`, "*");
+ } catch(e) {
+ opener.postMessage(`${e.name}: $(e.message)`, "*");
+ }
+};
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/test.html b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/test.html
new file mode 100644
index 0000000000..90d63e51e9
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/document-level-focus-apis/support/test.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - document-level APIs</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<input id="ipt">
diff --git a/testing/web-platform/tests/html/interaction/focus/focus-01.html b/testing/web-platform/tests/html/interaction/focus/focus-01.html
new file mode 100644
index 0000000000..b8faca54d8
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focus-01.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - key events</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#focus">
+<meta assert="flag" content="interact">
+<meta assert="assert" content="Check if the key events received by document are targeted at the element when it is focused">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="log"></div>
+<input id="test">
+<script>
+
+var t1 = async_test("The keydown event must be targeted at the input element"),
+ t2 = async_test("The keypress event must be targeted at the input element"),
+ t3 = async_test("The keyup event must be targeted at the input element"),
+ testEle;
+
+setup(function () {
+ testEle = document.getElementById("test");
+ testEle.focus();
+});
+
+document.onkeydown = t1.step_func_done(function(evt){
+ assert_equals(evt.target, testEle, "The keydown events must be targeted at the input element.");
+});
+
+document.onkeypress = t2.step_func_done(function(evt){
+ assert_equals(evt.target, testEle, "The keypress events must be targeted at the input element.");
+});
+
+document.onkeyup = t3.step_func_done(function(evt){
+ assert_equals(evt.target, testEle, "The keyup events must be targeted at the input element.");
+});
+
+var input_element = document.getElementById("test");
+
+t1.step(function() {
+ test_driver.send_keys(input_element, "a");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/focus-02.html b/testing/web-platform/tests/html/interaction/focus/focus-02.html
new file mode 100644
index 0000000000..2e765ca152
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focus-02.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - key events</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#focus">
+<meta assert="flag" content="interact">
+<meta assert="assert" content="Check if the key events received by document are targeted at the element when no element is focused">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="log"></div>
+<script>
+
+var t1 = async_test("The keydown event must be targeted at the body element"),
+ t2 = async_test("The keypress event must be targeted at the body element"),
+ t3 = async_test("The keyup event must be targeted at the body element");
+
+setup({timeout: 10000});
+
+document.onkeydown = t1.step_func_done(function(evt){
+ assert_equals(evt.target, document.body, "The keydown events must be targeted at the document's body.");
+});
+
+document.onkeypress = t2.step_func_done(function(evt){
+ assert_equals(evt.target, document.body, "The keypress events must be targeted at the document's body.");
+});
+
+document.onkeyup = t3.step_func_done(function(evt){
+ assert_equals(evt.target, document.body, "The keyup events must be targeted at the document's body.");
+});
+
+t1.step(function() {
+ test_driver.send_keys(document.body, "a");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/focus-file-input.html b/testing/web-platform/tests/html/interaction/focus/focus-file-input.html
new file mode 100644
index 0000000000..d3007112ff
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focus-file-input.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: file input can be programatically focused before layout</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#focus">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=505355">
+<link rel="author" title="Mozilla" href="https://mozilla.org/">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input type="file">
+<script>
+test(function() {
+ let input = document.querySelector("input");
+ assert_not_equals(document.activeElement, input);
+ input.focus();
+ assert_equals(document.activeElement, input);
+});
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/focus-input-type-switch.html b/testing/web-platform/tests/html/interaction/focus/focus-input-type-switch.html
new file mode 100644
index 0000000000..eeebe488bf
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focus-input-type-switch.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Inputs remain focusable upon changing type</title>
+<link rel="help" href="https://wicg.github.io/auxclick">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=981248">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<h1>Can still focus on inputs that change types</h1>
+<input type="text" value="123" onfocus="javascript:event.target.type='number'"
+ onblur="javascript:event.target.type='text'">
+<script>
+promise_test(() => {
+ // Click the input to attempt to focus on it
+ const target = document.querySelector("input");
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: target})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send()
+ .then(() => assert_equals(document.activeElement, target,
+ "The element was correctly focused"));
+}, "Can change an input's type during focus handler without breaking focus");
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/focus-keyboard-js.html b/testing/web-platform/tests/html/interaction/focus/focus-keyboard-js.html
new file mode 100644
index 0000000000..8a7242f37e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focus-keyboard-js.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1712724">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<input type=text id=text value=abc>
+<script>
+let text = document.getElementById("text");
+
+document.addEventListener("keyup", function(e) {
+ text.focus();
+});
+
+promise_test(async t => {
+ await test_driver.send_keys(document.body, " ");
+
+ assert_equals(document.activeElement, text, "#text should be focused by our event listener");
+ assert_true(text.matches(":focus"), "#text should match :focus");
+ assert_true(text.matches(":focus-visible"), "#text should match :focus-visible");
+ assert_equals(text.selectionStart, text.selectionEnd, "#text should not be selected");
+ assert_equals(text.value, "abc", "#text should not have changed value");
+});
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/focus-management/focus-event-targets-simple.html b/testing/web-platform/tests/html/interaction/focus/focus-management/focus-event-targets-simple.html
new file mode 100644
index 0000000000..ab7bcfe6d0
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focus-management/focus-event-targets-simple.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Focus events fire at correct targets in correct order in simple case</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/#focus-update-steps">
+ <link rel="help" href="https://html.spec.whatwg.org/#focus-chain">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <input type="text" id="a">
+ <script>
+// Record all the focus event targets in an array.
+// Modulo special cases in the "focus update steps" algorithm,
+// this should be the same as the new focus chain, except in reverse order.
+var newFocusChainReversedNotQuite = [];
+var pushTarget = function (e) {
+ newFocusChainReversedNotQuite.push(e.target);
+};
+// Window is the root node for event dispatch per https://html.spec.whatwg.org/multipage/webappapis.html#events-and-the-window-object
+window.addEventListener('focus', pushTarget, true);// Use event capturing since focus event doesn't bubble
+var input = document.getElementById('a');
+input.focus();
+window.removeEventListener('focus', pushTarget, true);
+test(function() {
+ assert_array_equals(newFocusChainReversedNotQuite, [input], "Exactly 1 focus event should fire and its target should be the input");
+}, "Focus events fire at correct targets in correct order in simple case");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/interaction/focus/focus-management/focus-events.html b/testing/web-platform/tests/html/interaction/focus/focus-management/focus-events.html
new file mode 100644
index 0000000000..5f8ca20a13
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focus-management/focus-events.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Focus management</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#focus-management">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<input type=text id=i1>
+<input type=text id=i2>
+<script>
+ var i1 = document.getElementById('i1'),
+ i2 = document.getElementById('i2'),
+ t1 = async_test("focusing on a focusable element fires a focus event at the element"),
+ t2 = async_test("focusing on a focusable element fires a blur event at the previous focussed element");
+
+ i2.onfocus = t1.step_func_done(function(e){
+ assert_true(e.isTrusted, "focus event is trusted");
+ assert_false(e.bubbles, "focus event doesn't bubble");
+ assert_false(e.cancelable, "focus event is not cancelable");
+ assert_equals(document.activeElement, i2);
+ });
+
+ i1.onblur = t2.step_func_done(function(e){
+ assert_true(e.isTrusted, "blur event is trusted");
+ assert_false(e.bubbles, "blur event doesn't bubble");
+ assert_false(e.cancelable, "blur event is not cancelable");
+ assert_equals(document.activeElement, document.body);
+ });
+
+ i1.focus();
+ i2.focus();
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/focused-element-move-documents-crash.html b/testing/web-platform/tests/html/interaction/focus/focused-element-move-documents-crash.html
new file mode 100644
index 0000000000..ca9ab26edd
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focused-element-move-documents-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1172828">
+
+<!-- No crash should occur if a focused element is moved to another document. -->
+
+<div id=editable contenteditable=>
+
+<script>
+editable.addEventListener('blur', function() {
+ document.execCommand('InsertText', false, '\t');
+});
+editable.focus();
+const secondDoc = document.implementation.createDocument('', null);
+secondDoc.appendChild(editable);
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/descends-into-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/descends-into-extending-focusgroup.html
new file mode 100644
index 0000000000..2590bc3250
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/descends-into-extending-focusgroup.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item within extending focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup=extend>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+ </div>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item3 = document.getElementById("item3");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowUp);
+ assert_equals(document.activeElement, item3);
+
+ await focusAndKeyPress(item4, kArrowLeft);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is a descendant of a subtree, a backward arrow key press should move the focus to that previous item within the subtree.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-focusgroup-root.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-focusgroup-root.html
new file mode 100644
index 0000000000..3dd9cf2da5
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-focusgroup-root.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when initially set on the focusgroup root.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root tabindex=-1 focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var root = document.getElementById("root");
+
+ await focusAndKeyPress(root, kArrowUp);
+ assert_equals(document.activeElement, root);
+
+ await focusAndKeyPress(root, kArrowLeft);
+ assert_equals(document.activeElement, root);
+ }, "When the focus is set on the root of a focusgroup element, an arrow key press shouldn't move the focus at all.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-non-focusgroup-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-non-focusgroup-item.html
new file mode 100644
index 0000000000..0ec7827664
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-non-focusgroup-item.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when initially set a focusable element that isn't a focusgroup item.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ <div>
+ <span id=nonitem1 tabindex=0>nonitem1</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var nonitem1 = document.getElementById("nonitem1");
+
+ await focusAndKeyPress(nonitem1, kArrowUp);
+ assert_equals(document.activeElement, nonitem1);
+
+ await focusAndKeyPress(nonitem1, kArrowLeft);
+ assert_equals(document.activeElement, nonitem1);
+ }, "When the focus is set on a focusable element that isn't a focusgroup item, an arrow key press shouldn't move the focus at all.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item-and-wraps.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item-and-wraps.html
new file mode 100644
index 0000000000..475acdc7f1
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item-and-wraps.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when there is only one item, even though it wraps.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item1, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the only focusgroup item and the focusgroup wraps in the axis of the arrow key pressed, the focus shouldn't move and we shouldn't get stuck in an infinite loop.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item.html
new file mode 100644
index 0000000000..f046769459
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-only-one-item.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when there is only one item.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item1, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the only focusgroup item, the focus shouldn't move and we shouldn't get stuck in an infinite loop.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-outside-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-outside-focusgroup.html
new file mode 100644
index 0000000000..78ee0dadeb
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-outside-focusgroup.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when initially set on an element not included in the focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+<span id=out tabindex=-1>out</span>
+
+<script>
+
+ promise_test(async t => {
+ var out = document.getElementById("out");
+
+ await focusAndKeyPress(out, kArrowUp);
+ assert_equals(document.activeElement, out);
+
+ await focusAndKeyPress(out, kArrowLeft);
+ assert_equals(document.activeElement, out);
+ }, "When the focus is set on an element outside of the focusgroup, an arrow keypress shouldn't move the focus at all.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-wrap-when-not-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-wrap-when-not-supported.html
new file mode 100644
index 0000000000..742daedfef
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-wrap-when-not-supported.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not wrap when 'wrap' not specified.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item1, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the first item of a focusgroup, a backward arrow key press shouldn't move the focus since there aren't any previous item.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/ascends-to-parent-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/ascends-to-parent-focusgroup.html
new file mode 100644
index 0000000000..00ef3db90c
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/ascends-to-parent-focusgroup.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus ascends to parent focusgroup successfully.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=horizontal>
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1>
+ <div>
+ <div focusgroup="extend vertical">
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+ </div>
+ </div>
+ <span id=item5 tabindex=-1>item5</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item2 = document.getElementById("item2");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item3, kArrowLeft);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the first item of an extending focusgroup that doesn't support the axis of the arrow key pressed but the parent focusgroup does, ascend to that focusgroup. This should work whether the extending focusgroup is the child of the other focusgroup or a distant descendant.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-non-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-non-extending-focusgroup.html
new file mode 100644
index 0000000000..b00958719d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-non-extending-focusgroup.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not ascend out of current focusgroup if it does not extend the parent focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<ul focusgroup=horizontal>
+ <li id=item1 tabindex="-1">
+ <ul focusgroup=vertical>
+ <li id=item2 tabindex="-1">item2</li>
+ </ul>
+ </li>
+</ul>
+
+<script>
+
+ promise_test(async t => {
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowLeft);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the first element of a non-extending focusgroup located inside another focusgroup, we should be able to ascend to that other focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-move-when-axis-not-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-move-when-axis-not-supported.html
new file mode 100644
index 0000000000..e671a25844
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-move-when-axis-not-supported.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move to previous focusgroup item when the axis of the arrow key pressed isn't supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=vertical>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowLeft);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the last element of a focusgroup that only supports the orthogonal axis of the arrow key pressed, the focus shouldn't move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-wrap-in-orthogonal-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-wrap-in-orthogonal-axis.html
new file mode 100644
index 0000000000..de09fa813c
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-wrap-in-orthogonal-axis.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not wrap in the arrow key pressed orthogonal axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup="vertical wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the first item of a focusgroup that wraps and supports only the orthogonal axis of the pressed arrow key, a backward arrow key press shouldn't move the focus.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/moves-when-only-current-axis-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/moves-when-only-current-axis-supported.html
new file mode 100644
index 0000000000..12f7934866
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/moves-when-only-current-axis-supported.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous focusgroup item when only the axis of the arrow key pressed is supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=horizontal>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on an item of a focusgroup that only supports the axis of the arrow key pressed the focus should move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html
new file mode 100644
index 0000000000..2fadddac27
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item, skipping the focusgroup that extends in the orthogonal axis (complex case).</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup="extend vertical">
+ <div id=item2 tabindex=-1>
+ <div focusgroup=extend>
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <span id=item5 tabindex=-1>item5</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item5 = document.getElementById("item5");
+
+ await focusAndKeyPress(item5, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is located past an extending focusgroup that only supports the orthogonal axis, a backward arrow key press should move the focus to that previous item without getting stuck in the inner focusgroup that doesn't support the axis. The same should still be true when inside a focusgroup that extends another extending focusgroup that supports only the orthogonal axis within the original focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html
new file mode 100644
index 0000000000..44dace8461
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item, skipping the focusgroup that extends in the orthogonal axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup="extending vertical">
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+ </div>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is a descendant of a subtree, a backward arrow key press should move the focus to that previous item within the subtree. However, if that subtree is an extending focusgroup that supports only the orthogonal axis, it should be skipped.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/wraps-in-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/wraps-in-axis.html
new file mode 100644
index 0000000000..956d3f6406
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/wraps-in-axis.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps in the arrow key pressed axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup="horizontal wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowLeft);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the first item of a focusgroup that wraps and supports only the axis of the pressed arrow key, a backward arrow key press should move the focus to the last item within the focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item-and-skips-focusable-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item-and-skips-focusable-item.html
new file mode 100644
index 0000000000..9bab38a24d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item-and-skips-focusable-item.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item and skips non-focusable elements.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2>item2</span> <!--NOT FOCUSABLE-->
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item3, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item3, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on a focusgroup item, an arrow key press should move the focus to the previous item and skip non-focusable items.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item.html
new file mode 100644
index 0000000000..228a4aad63
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/moves-to-previous-item.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous focusgroup item.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item2, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on a focusgroup item, an arrow key press should move the focus to the previous item.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-empty-wrapping-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-empty-wrapping-focusgroup.html
new file mode 100644
index 0000000000..fb0b66e30e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-empty-wrapping-focusgroup.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item, skipping over an empty extending focusgroup that wraps on itself.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup="extend wrap">
+ <span id=item2>item2</span> <!-- Not focusable -->
+ <span id=item3>item3</span> <!-- Not focusable -->
+ </div>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item4, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is located past an extending focusgroup that wraps but has no item in it, a backward arrow key press should move the focus to that previous item without getting stuck in the inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-non-focusgroup-subtree.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-non-focusgroup-subtree.html
new file mode 100644
index 0000000000..140950202b
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-non-focusgroup-subtree.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item, skipping over a subtree that isn't an extending focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item4, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is located past a non-focusgroup subtree, a backward arrow key press should move the focus to that previous item without getting stuck in the subtree.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup-complex-case.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup-complex-case.html
new file mode 100644
index 0000000000..79731e7b2a
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup-complex-case.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item and skips focusgroup root subtree (complex case).</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup>
+ <div id=item2 tabindex=-1>
+ <div focusgroup=extend>
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <span id=item5 tabindex=-1>item5</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item5 = document.getElementById("item5");
+
+ await focusAndKeyPress(item5, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item5, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is located past an other (non-extending) focusgroup subtree, a backward arrow key press should move the focus to that previous item without getting stuck in the other focusgroup. The same should still be true when inside a focusgroup that extends a root focusgroup within the original focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup.html
new file mode 100644
index 0000000000..c40ce02b5d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item, skipping over a subtree that is a root focusgroup (unrelated to the one we're in).</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+ </div>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item4, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is located past an other (non-extending) focusgroup subtree, a backward arrow key press should move the focus to that previous item without getting stuck in the other focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/ascends-to-parent-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/ascends-to-parent-focusgroup.html
new file mode 100644
index 0000000000..b95f2c527b
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/ascends-to-parent-focusgroup.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus ascends to parent focusgroup successfully.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=vertical>
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1>
+ <div>
+ <div focusgroup="extend horizontal">
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+ </div>
+ </div>
+ <span id=item5 tabindex=-1>item5</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item2 = document.getElementById("item2");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item3, kArrowUp);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the first item of an extending focusgroup that doesn't support the axis of the arrow key pressed but the parent focusgroup does, ascend to that focusgroup. This should work whether the extending focusgroup is the child of the other focusgroup or a distant descendant.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-non-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-non-extending-focusgroup.html
new file mode 100644
index 0000000000..08dc466603
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-non-extending-focusgroup.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not ascend out of current focusgroup if it does not extend the parent focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<ul focusgroup=vertical>
+ <li id=item1 tabindex="-1">
+ <ul focusgroup=horizontal>
+ <li id=item2 tabindex="-1">item2</li>
+ </ul>
+ </li>
+</ul>
+
+<script>
+
+ promise_test(async t => {
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowUp);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the first element of a non-extending focusgroup located inside another focusgroup, we should be able to ascend to that other focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-move-when-axis-not-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-move-when-axis-not-supported.html
new file mode 100644
index 0000000000..0cf6ad3be2
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-move-when-axis-not-supported.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move to previous focusgroup item when the axis of the arrow key pressed isn't supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=horizontal>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowUp);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the last element of a focusgroup that only supports the orthogonal axis of the arrow key pressed, the focus shouldn't move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-wrap-in-orthogonal-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-wrap-in-orthogonal-axis.html
new file mode 100644
index 0000000000..0ec4f02247
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-wrap-in-orthogonal-axis.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not wrap in the arrow key pressed orthogonal axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup="horizontal wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the first item of a focusgroup that wraps and supports only the orthogonal axis of the pressed arrow key, a backward arrow key press shouldn't move the focus.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/moves-when-only-current-axis-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/moves-when-only-current-axis-supported.html
new file mode 100644
index 0000000000..ad46be76b2
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/moves-when-only-current-axis-supported.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous focusgroup item when only the axis of the arrow key pressed is supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=vertical>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowUp);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on an item of a focusgroup that only supports the axis of the arrow key pressed the focus should move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html
new file mode 100644
index 0000000000..86ea79122c
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis-complex-case.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item, skipping the focusgroup that extends in the orthogonal axis (complex case).</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup="extend horizontal">
+ <div id=item2 tabindex=-1>
+ <div focusgroup=extend>
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <span id=item5 tabindex=-1>item5</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item5 = document.getElementById("item5");
+
+ await focusAndKeyPress(item5, kArrowUp);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is located past an extending focusgroup that only supports the orthogonal axis, a backward arrow key press should move the focus to that previous item without getting stuck in the inner focusgroup that doesn't support the axis. The same should still be true when inside a focusgroup that extends another extending focusgroup that supports only the orthogonal axis within the original focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html
new file mode 100644
index 0000000000..960b8604b0
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to previous item, skipping the focusgroup that extends in the orthogonal axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div>
+ <div focusgroup="extending horizontal">
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+ </div>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowUp);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup and the previous item is a descendant of a subtree, a backward arrow key press should move the focus to that previous item within the subtree. However, if that subtree is an extending focusgroup that supports only the orthogonal axis, it should be skipped.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/wraps-in-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/wraps-in-axis.html
new file mode 100644
index 0000000000..fdccee9742
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/wraps-in-axis.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps in the arrow key pressed axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup="vertical wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the first item of a focusgroup that wraps and supports only the axis of the pressed arrow key, a backward arrow key press should move the focus to the last item within the focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-in-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-in-extending-focusgroup.html
new file mode 100644
index 0000000000..6727d93f75
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-in-extending-focusgroup.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps from first to last element when 'wrap' is specified (jumping into extending focusgroup).</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <div focusgroup=extend>
+ <span id=item1 tabindex=0>item1</span>
+ <div focusgroup=extend>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+ </div>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item4);
+
+ await focusAndKeyPress(item2, kArrowUp);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item1, kArrowLeft);
+ assert_equals(document.activeElement, item4);
+
+ await focusAndKeyPress(item2, kArrowLeft);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the first item of an extending focusgroup that inherited its wrapping behavior, it should only wrap if the focused item is also the first item of that parent focusgroup. If it is, then it should wrap within the parent focusgroup, not within the extending focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully-complex-case.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully-complex-case.html
new file mode 100644
index 0000000000..11ebea349d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully-complex-case.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps from first to last focusgroup item, even though there are non items in the way.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <div>
+ <span id=nonitem1>nonitem1</span>
+ <span id=nonitem2>nonitem2</span>
+ </div>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+ <div>
+ <span id=nonitem3>nonitem3</span>
+ <span id=nonitem4>nonitem4</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item3);
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the first item of an extending focusgroup while there are other non-item elements before, we should still be able to wrap to the last item. Also, if the last item has other non-item elements after itself, skipping these non-item elements shouldn't be an issue.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully.html
new file mode 100644
index 0000000000..19df794e19
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/wraps-successfully.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps from first to last element when 'wrap' is specified.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowUp);
+ assert_equals(document.activeElement, item3);
+
+ await focusAndKeyPress(item1, kArrowLeft);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the first item of a focusgroup that wraps, a backward arrow key press should move the focus to the last item within the focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-focusgroup-root.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-focusgroup-root.html
new file mode 100644
index 0000000000..30c70ad3b3
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-focusgroup-root.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when initially set on the focusgroup root.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root tabindex=-1 focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var root = document.getElementById("root");
+
+ await focusAndKeyPress(root, kArrowDown);
+ assert_equals(document.activeElement, root);
+
+ await focusAndKeyPress(root, kArrowRight);
+ assert_equals(document.activeElement, root);
+ }, "When the focus is set on the root of a focusgroup element, an arrow key press shouldn't move the focus at all.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-non-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-non-item.html
new file mode 100644
index 0000000000..3afcd184c1
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-non-item.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when initially set a focusable element that isn't a focusgroup item.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div tabindex=-1 focusgroup>
+ <div>
+ <span id=nonitem1 tabindex=0>nonitem1</span>
+ </div>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var nonitem1 = document.getElementById("nonitem1");
+
+ await focusAndKeyPress(nonitem1, kArrowDown);
+ assert_equals(document.activeElement, nonitem1);
+
+ await focusAndKeyPress(nonitem1, kArrowRight);
+ assert_equals(document.activeElement, nonitem1);
+ }, "When the focus is set on a focusable element that isn't a focusgroup item, an arrow key press shouldn't move the focus at all.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item-and-wraps.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item-and-wraps.html
new file mode 100644
index 0000000000..20cc5eb57d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item-and-wraps.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when there is only one item, even though it wraps.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the only focusgroup item and the focusgroup wraps in the axis of the arrow key pressed, the focus shouldn't move and we shouldn't get stuck in an infinite loop.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item.html
new file mode 100644
index 0000000000..0178494d87
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-only-one-item.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when there is only one item.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the only focusgroup item, the focus shouldn't move and we shouldn't get stuck in an infinite loop.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-outside-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-outside-focusgroup.html
new file mode 100644
index 0000000000..1c35805f80
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-outside-focusgroup.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move when initially set on an element not included in the focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<span id=out tabindex=-1>out</span>
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var out = document.getElementById("out");
+
+ await focusAndKeyPress(out, kArrowDown);
+ assert_equals(document.activeElement, out);
+
+ await focusAndKeyPress(out, kArrowRight);
+ assert_equals(document.activeElement, out);
+ }, "When the focus is set on an element outside of the focusgroup, an arrow keypress shouldn't move the focus at all.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-in-focusgroup-with-no-items.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-in-focusgroup-with-no-items.html
new file mode 100644
index 0000000000..602feed001
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-in-focusgroup-with-no-items.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not wrap in wrapping extending focusgroup that doesn't have focusgroup items.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <div focusgroup="extend wrap">
+ <span id=item2>item2</span> <!--NOT FOCUSABLE-->
+ <span id=item3>item3</span> <!--NOT FOCUSABLE-->
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item4);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item4);
+ }, "This test validates that we don't get stuck in an infinite loop searching for a focusable element in the extending focusgroup that wraps that doesn't contain one. Wrapping should only be allowed in the focusgroup that contains the focusable element we started on or in one of its ancestors.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-when-not-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-when-not-supported.html
new file mode 100644
index 0000000000..550a4ba8a1
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-wrap-when-not-supported.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not wrap when not supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowDown);
+ assert_equals(document.activeElement, item2);
+
+ await focusAndKeyPress(item2, kArrowRight);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the last item of a focusgroup that doesn't support wrapping in the axis of the arrow key pressed, the focus shouldn't move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/descends-in-horizontal-inner-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/descends-in-horizontal-inner-focusgroup.html
new file mode 100644
index 0000000000..3d0d7f0fa5
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/descends-in-horizontal-inner-focusgroup.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus descends from vertical focusgroup into horizontal focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=fg1 focusgroup=vertical>
+ <span id=item1 tabindex=0>item1</span>
+ <div id=fg2 tabindex=-1 focusgroup="extend horizontal">
+ <span id=item2 tabindex=-1>item2</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var fg2 = document.getElementById("fg2");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(fg2, kArrowRight);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the root of an extending focusgroup that supports an orthogonal axis to the outer focusgroup, an arrow key press aligned with the inner focusgroup axis should move the focus within that inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-move-when-axis-not-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-move-when-axis-not-supported.html
new file mode 100644
index 0000000000..ebf7238c54
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-move-when-axis-not-supported.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move to next focusgroup item when the axis of the arrow key pressed isn't supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=vertical>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on an item of a focusgroup that only supports the orthogonal axis to the arrow key pressed, the arrow pressed shouldn't move the focus.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-wrap-even-when-other-axis-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-wrap-even-when-other-axis-supported.html
new file mode 100644
index 0000000000..19535e1df9
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/does-not-wrap-even-when-other-axis-supported.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Does not wrap when the arrow keypress is supported but the focusgroup only wraps in the other axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup="vertical wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1 focusgroup=extend>
+ <!--This focusgroup supports both axes, but only wraps in the vertical one.-->
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowRight);
+ assert_equals(document.activeElement, item4);
+ }, "When the focus is set on the last item of a focusgroup that doesn't support wrapping in the axis of the arrow key pressed but supports wrapping in the orthogonal axis, the focus shouldn't move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/moves-when-only-current-axis-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/moves-when-only-current-axis-supported.html
new file mode 100644
index 0000000000..9ae9892c1d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/moves-when-only-current-axis-supported.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next focusgroup item when only the axis of the arrow key pressed is supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=horizontal>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on an item of a focusgroup that only supports the axis of the arrow key pressed the focus should move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html
new file mode 100644
index 0000000000..05f8268895
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/skips-focusgroup-that-extends-in-orthogonal-axis.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next item, skipping the focusgroup that extends in the orthogonal axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup>
+ <div id=item1 tabindex=0 focusgroup="extend vertical">
+ <span id=item2 tabindex=-1>item2</span>
+ </div>
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on an extending focusgroup element but that focusgroup doesn't support the axis of the arrow key pressed, skip that subtree altogether.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-and-skips-orthogonal-inner-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-and-skips-orthogonal-inner-focusgroup.html
new file mode 100644
index 0000000000..af79ca24c6
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-and-skips-orthogonal-inner-focusgroup.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps and skips the orthogonal inner focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=wrap>
+ <div focusgroup="extend vertical">
+ <span id=item1 tabindex=-1>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ </div>
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=0>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item3 = document.getElementById("item3");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowRight);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the last item of a focusgroup that supports wrapping in the axis of the arrow key pressed and the first item is in an inner focusgroup that doesn't support wrapping in the same axis, the focus moves to the next item out of that inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-appropriate-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-appropriate-focusgroup.html
new file mode 100644
index 0000000000..80290c9918
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-appropriate-focusgroup.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps in the appropriate focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap> <!--Supports horizontal wrapping-->
+ <div focusgroup="extend vertical"> <!--Doesn't support horizontal wrapping-->
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1 focusgroup="extend wrap">
+ <!--Supports wrapping in both axis, but only extend the
+ wrapping behavior of its ancestors in the vertical axis. -->
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item3 = document.getElementById("item3");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowRight);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the last item of an inner focusgroup that supports wrapping while its parent focusgroup doesn't (in the axis of the arrow key pressed), the focus should move to the first item of the inner focusgroup even if there's another focusgroup supporting wrapping in the same axis as the arrow key pressed in the hierarchy.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-inner-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-inner-focusgroup.html
new file mode 100644
index 0000000000..ab33842340
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/horizontal/wraps-in-inner-focusgroup.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps in the inner focusgroup only since the outer focusgroup only wraps in the other axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup="vertical wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1 focusgroup="extend wrap">
+ <!--This focusgroup supports wrapping in both axis, but only extend the wrapping
+ behavior of its parent in the vertical axis. -->
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item3 = document.getElementById("item3");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowRight);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the last item of an inner focusgroup that supports wrapping while its parent focusgroup doesn't (in the axis of the arrow key pressed), the focus should move to the first item of the inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-inside-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-inside-extending-focusgroup.html
new file mode 100644
index 0000000000..bea7b959c5
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-inside-extending-focusgroup.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next item inside an extending focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup>
+ <div id=item1 tabindex=0 focusgroup=extend>
+ <span id=item2>item2</span> <!--NOT FOCUSABLE-->
+ <span id=item3 tabindex=-1>item3</span>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item3);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on a focusgroup item which happens to also be an extending focusgroup, an arrow key press should move the focus to the next item within the extending focusgroup and skip non-focusable items.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-and-skips-non-focusable.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-and-skips-non-focusable.html
new file mode 100644
index 0000000000..a5d6e306ed
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-and-skips-non-focusable.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next item and skips non-focusable elements.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2>item2</span> <!--NOT FOCUSABLE-->
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item3);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on a focusgroup item, an arrow key press should move the focus to the next item and skip non-focusable items.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-outside-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-outside-extending-focusgroup.html
new file mode 100644
index 0000000000..f5a74ced5f
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-outside-extending-focusgroup.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next item outside the extending focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup>
+ <div id=item1 tabindex=0 focusgroup=extend>
+ <span id=item2>item2</span> <!--NOT FOCUSABLE-->
+ <span id=item3>item3</span> <!--NOT FOCUSABLE-->
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item4);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item4);
+ }, "When the focus is set on a focusgroup item which happens to also be an extending focusgroup, an arrow key press should move the focus to the next item within the extending focusgroup and skip non-focusable items. If no valid candidate is found within that extending focusgroup, the next element (in pre-order traversal) should be considered. In this case, |item4| is the valid next candidate.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-within-descendants.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-within-descendants.html
new file mode 100644
index 0000000000..97355ec2da
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item-within-descendants.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next item within its descendants.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup>
+ <div id=item1 tabindex=0>
+ <div>
+ <div focusgroup=extend>
+ <span id=item2 tabindex=-1>item2<span>
+ </div>
+ </div>
+ </div>
+ <span id=item4 tabindex=-1>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item2);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on a focusgroup item that is an ancestor to an extending focusgroup, the focus should move to the next element inside that extending focusgroup even if it's not a direct child.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item.html
new file mode 100644
index 0000000000..747805d7b5
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/moves-to-next-item.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next focusgroup item.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item2);
+
+ await focusAndKeyPress(item1, kArrowRight);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on a focusgroup item, an arrow key press should move the focus to the next item.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/descends-in-vertical-inner-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/descends-in-vertical-inner-focusgroup.html
new file mode 100644
index 0000000000..4f6f8e9459
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/descends-in-vertical-inner-focusgroup.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus descends from horizontal focusgroup into vertical focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=fg1 focusgroup=horizontal>
+ <span id=item1 tabindex=0>item1</span>
+ <div id=fg2 tabindex=-1 focusgroup="extend vertical">
+ <span id=item2 tabindex=-1>item2</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var fg2 = document.getElementById("fg2");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(fg2, kArrowDown);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on the root of an extending focusgroup that supports an orthogonal axis to the outer focusgroup, an arrow key press aligned with the inner focusgroup axis should move the focus within that inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-move-when-axis-not-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-move-when-axis-not-supported.html
new file mode 100644
index 0000000000..794f079418
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-move-when-axis-not-supported.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus does not move to next focusgroup item when the axis of the arrow key pressed isn't supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=horizontal>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on an item of a focusgroup that only supports the orthogonal axis to the arrow key pressed, the arrow pressed shouldn't move the focus.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-wrap-even-when-other-axis-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-wrap-even-when-other-axis-supported.html
new file mode 100644
index 0000000000..f7b697e5c0
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/does-not-wrap-even-when-other-axis-supported.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Does not wrap when the arrow keypress is supported but the focusgroup only wraps in the other axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup="horizontal wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1 focusgroup=extend>
+ <!--This focusgroup supports both axes, but only wraps in the horizontal one.-->
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowDown);
+ assert_equals(document.activeElement, item4);
+ }, "When the focus is set on the last item of a focusgroup that doesn't support wrapping in the axis of the arrow key pressed but supports wrapping in the orthogonal axis, the focus shouldn't move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/moves-when-only-current-axis-supported.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/moves-when-only-current-axis-supported.html
new file mode 100644
index 0000000000..b5b97cca67
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/moves-when-only-current-axis-supported.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next focusgroup item when only the axis of the arrow key pressed is supported.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=vertical>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item2);
+ }, "When the focus is set on an item of a focusgroup that only supports the axis of the arrow key pressed the focus should move.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html
new file mode 100644
index 0000000000..4fe753126b
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/skips-focusgroup-that-extends-in-orthogonal-axis.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus moves to next item, skipping the focusgroup that extends in the orthogonal axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup>
+ <div id=item1 tabindex=0 focusgroup="extend horizontal">
+ <span id=item2 tabindex=-1>item2</span>
+ </div>
+ <span id=item3 tabindex=-1>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on an extending focusgroup element but that focusgroup doesn't support the axis of the arrow key pressed, skip that subtree altogether.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-and-skips-orthogonal-inner-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-and-skips-orthogonal-inner-focusgroup.html
new file mode 100644
index 0000000000..64a4b76ebf
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-and-skips-orthogonal-inner-focusgroup.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps and skips the orthogonal inner focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=wrap>
+ <div focusgroup="extend horizontal">
+ <span id=item1 tabindex=-1>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ </div>
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=0>item4</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item3 = document.getElementById("item3");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowDown);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the last item of a focusgroup that supports wrapping in the axis of the arrow key pressed and the first item is in an inner focusgroup that doesn't support wrapping in the same axis, the focus moves to the next item out of that inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-appropriate-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-appropriate-focusgroup.html
new file mode 100644
index 0000000000..fa9c72b0bd
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-appropriate-focusgroup.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps in the appropriate focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=wrap> <!--Supports vertical wrapping-->
+ <div focusgroup="extend horizontal"> <!--Doesn't support vertical wrapping-->
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1 focusgroup="extend wrap">
+ <!--Supports wrapping in both axis, but only extend the
+ wrapping behavior of its ancestors in the horizontal axis. -->
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item3 = document.getElementById("item3");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowDown);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the last item of an inner focusgroup that supports wrapping while its parent focusgroup doesn't (in the axis of the arrow key pressed), the focus should move to the first item of the inner focusgroup even if there's another focusgroup supporting wrapping in the same axis as the arrow key pressed in the hierarchy.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-inner-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-inner-focusgroup.html
new file mode 100644
index 0000000000..8fd44ba0f1
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/vertical/wraps-in-inner-focusgroup.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps in the inner focusgroup only since the outer focusgroup only wraps in the other axis.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup="horizontal wrap">
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1 focusgroup="extend wrap">
+ <!--This focusgroup supports wrapping in both axis, but only extend the wrapping
+ behavior of its parent in the horizontal axis. -->
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item3 = document.getElementById("item3");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowDown);
+ assert_equals(document.activeElement, item3);
+ }, "When the focus is set on the last item of an inner focusgroup that supports wrapping while its parent focusgroup doesn't (in the axis of the arrow key pressed), the focus should move to the first item of the inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-and-goes-into-inner-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-and-goes-into-inner-focusgroup.html
new file mode 100644
index 0000000000..ea9b1cbec5
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-and-goes-into-inner-focusgroup.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Wraps and goes into inner focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=wrap>
+ <div focusgroup=extend>
+ <span id=item1 tabindex=-1>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+ </div>
+ <span id=item3 tabindex=0>item3</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item3, kArrowDown);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item3, kArrowRight);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup that supports wrapping in the axis of the arrow key pressed and the first item is in an inner focusgroup that supports it too, the focus moves to that item in the inner focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-successfully.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-successfully.html
new file mode 100644
index 0000000000..5ad1cd8fe9
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-successfully.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps successfully from the last item to the first one.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <span id=item2 tabindex=-1>item2</span>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item2 = document.getElementById("item2");
+
+ await focusAndKeyPress(item2, kArrowDown);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item2, kArrowRight);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of a focusgroup that supports wrapping in the axis of the arrow key pressed, the focus should move back to the first item.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-to-parent-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-to-parent-focusgroup.html
new file mode 100644
index 0000000000..afb9744b71
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/wraps-to-parent-focusgroup.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Focus wraps successfully from the last item inside an extending focusgroup to the first item of the parent focusgroup.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div id=root focusgroup=wrap>
+ <span id=item1 tabindex=0>item1</span>
+ <div id=item2 tabindex=-1 focusgroup=extend>
+ <span id=item3 tabindex=-1>item3</span>
+ <span id=item4 tabindex=-1>item4</span>
+ </div>
+</div>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item4 = document.getElementById("item4");
+
+ await focusAndKeyPress(item4, kArrowDown);
+ assert_equals(document.activeElement, item1);
+
+ await focusAndKeyPress(item4, kArrowRight);
+ assert_equals(document.activeElement, item1);
+ }, "When the focus is set on the last item of an inner focusgroup that supports wrapping while its parent focusgroup also does, the focus should move to the first item of the parent focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-flow-only.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-flow-only.html
new file mode 100644
index 0000000000..a634412d53
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-flow-only.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that col-flow allows only vertical flowing.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid col-flow">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r1c3);
+ }, "A right arrow press should not flow the focus horziontally when not supported by the focusgroup.");
+
+ promise_test(async t => {
+ var r1c2 = document.getElementById("r1c2");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r1c2);
+ }, "When on the last row, a down arrow press should move the focus to first row and next column if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowLeft);
+ assert_equals(document.activeElement, r2c1);
+ }, "A left arrow press should not flow the focus horziontally when not supported by the focusgroup.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c3 = document.getElementById("r2c3");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r2c3);
+ }, "When on the first row, an up arrow press should move the focus to last row and previous column if the focusgroup flows horizontally.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-and-row-flow.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-and-row-flow.html
new file mode 100644
index 0000000000..b5f678a706
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-and-row-flow.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that we can col-wrap AND row-flow.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid col-wrap row-flow">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r2c1);
+ }, "When on the last column, a right arrow press should move the focus to first column and next row if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r1c1);
+ }, "When on the last row, a down arrow press should move the focus to first row if the focusgroup wraps vertically.");
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c3);
+ }, "When on the first column, a left arrow press should move the focus to last column and previous row if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r2c1);
+ }, "When on the first row, an up arrow press should move the focus to last row if the focusgroup wraps vertically.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-only.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-only.html
new file mode 100644
index 0000000000..ff19b8778e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/col-wrap-only.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that col-wrap allows only vertical wrapping.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid col-wrap">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r1c3);
+ }, "A right arrow press should not wrap the focus to the first column when only vertical wrap is supported by the focusgroup.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r1c1);
+ }, "When on the last row, a down arrow press should move the focus to first row if the focusgroup wraps vertically.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+
+ await focusAndKeyPress(r1c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c1);
+ }, "A left arrow press should not wrap the focus to the last column when only vertical wrap is supported by the focusgroup.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r2c1);
+ }, "When on the first row, an up arrow press should move the focus to last row if the focusgroup wraps vertically.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/colspan.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/colspan.html
new file mode 100644
index 0000000000..c9daeffaeb
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/colspan.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that we deal correctly with colspans.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1 colspan=2>r1c2</td>
+ <td id=r1c4 tabindex=-1>r1c4</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ <td id=r2c4 tabindex=-1>r2c4</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r1c2");
+ var r1c4 = document.getElementById("r1c4");
+
+ await focusAndKeyPress(r1c1, kArrowRight);
+ assert_equals(document.activeElement, r1c2);
+
+ await focusAndKeyPress(r1c2, kArrowRight);
+ assert_equals(document.activeElement, r1c4);
+ }, "A right arrow press should move the focus to the next column, dealing correctly with colspans.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r1c2");
+ var r1c4 = document.getElementById("r1c4");
+ var r2c1 = document.getElementById("r2c1");
+ var r2c2 = document.getElementById("r2c2");
+ var r2c4 = document.getElementById("r2c4");
+
+ await focusAndKeyPress(r1c1, kArrowDown);
+ assert_equals(document.activeElement, r2c1);
+
+ await focusAndKeyPress(r1c2, kArrowDown);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r1c4, kArrowDown);
+ assert_equals(document.activeElement, r2c4);
+ }, "A down arrow press should move the focus to the right cell on the next row, dealing correctly with colspans.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r1c2");
+ var r1c4 = document.getElementById("r1c4");
+
+ await focusAndKeyPress(r1c4, kArrowLeft);
+ assert_equals(document.activeElement, r1c2);
+
+ await focusAndKeyPress(r1c2, kArrowLeft);
+ assert_equals(document.activeElement, r1c1);
+ }, "A left arrow press should move to the previous column, dealing correctly with the colspans.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r1c2");
+ var r1c4 = document.getElementById("r1c4");
+ var r2c1 = document.getElementById("r2c1");
+ var r2c2 = document.getElementById("r2c2");
+ var r2c3 = document.getElementById("r2c3");
+ var r2c4 = document.getElementById("r2c4");
+
+ await focusAndKeyPress(r2c1, kArrowUp);
+ assert_equals(document.activeElement, r1c1);
+
+ await focusAndKeyPress(r2c2, kArrowUp);
+ assert_equals(document.activeElement, r1c2);
+
+ await focusAndKeyPress(r2c3, kArrowUp);
+ assert_equals(document.activeElement, r1c2);
+
+ await focusAndKeyPress(r2c4, kArrowUp);
+ assert_equals(document.activeElement, r1c4);
+ }, "An up arrow press should move the focus to the right cell on the previous row, dealing correctly with colspans.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table-not-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table-not-focusgroup.html
new file mode 100644
index 0000000000..c0aea689ae
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table-not-focusgroup.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that Focusgroup doesn't work on CSS table when the focusgroup attribute is not present.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div style="display:table">
+ <div style="display:table-row">
+ <div id=r1c1 style="display:table-cell" tabindex=0>r1c1</div>
+ <div id=r1c2 style="display:table-cell" tabindex=-1>r1c2</div>
+ </div>
+ <div style="display:table-row">
+ <div id=r2c1 style="display:table-cell" tabindex=-1>r2c1</div>
+ <div id=r2c2 style="display:table-cell" tabindex=-1>r2c2</div>
+ </div>
+</div>
+
+<script>
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+
+ await focusAndKeyPress(r1c1, kArrowRight);
+ assert_equals(document.activeElement, r1c1);
+ }, "Validates that a CSS table that doesn't have the focusgroup=grid attribute set doesn't allow arrow-keys navigation");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table.html
new file mode 100644
index 0000000000..45e1e2c472
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/css-table.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that Focusgroup works with CSS tables too.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div style="display:table" focusgroup=grid>
+ <div style="display:table-row">
+ <div id=r1c1 style="display:table-cell" tabindex=0>r1c1</div>
+ <div id=r1c2 style="display:table-cell" tabindex=-1>r1c2</div>
+ </div>
+ <div style="display:table-row">
+ <div id=r2c1 style="display:table-cell" tabindex=-1>r2c1</div>
+ <div id=r2c2 style="display:table-cell" tabindex=-1>r2c2</div>
+ </div>
+</div>
+
+<script>
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r1c2");
+ var r2c1 = document.getElementById("r2c1");
+ var r2c2 = document.getElementById("r2c2");
+
+ await focusAndKeyPress(r1c1, kArrowRight);
+ assert_equals(document.activeElement, r1c2);
+
+ await focusAndKeyPress(r1c2, kArrowDown);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r2c2, kArrowLeft);
+ assert_equals(document.activeElement, r2c1);
+
+ await focusAndKeyPress(r2c1, kArrowUp);
+ assert_equals(document.activeElement, r1c1);
+ }, "Tests that grid focusgroups also work on CSS tables (i.e.: 'display: table'). The implementation relies on the layout objects, so the other tests that covers HTML tables don't need to be duplicated to test the same cases with CSS tables.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/does-not-wrap-or-flow.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/does-not-wrap-or-flow.html
new file mode 100644
index 0000000000..050dec3622
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/does-not-wrap-or-flow.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Simple case that validated we don't wrap or flow when these values aren't provided.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup=grid>
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r1c3);
+ }, "A right arrow press should not move the focus when it is at the last column and the focusgroup doesn't wrap or flow.");
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r2c1);
+ }, "A down arrow press should not move the focus when it is at the last row and the focusgroup doesn't wrap or flow.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+
+ await focusAndKeyPress(r1c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c1);
+ }, "A left arrow press should not move the focus when it is at the first column and the focusgroup doesn't wrap or flow.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r1c1);
+ }, "An up arrow press should not move the focus when it is at the first row and the focusgroup doesn't wrap or flow.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/empty-spaces.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/empty-spaces.html
new file mode 100644
index 0000000000..90a09e1a94
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/empty-spaces.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that empty spaces are not troubling our expectations.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid flow">
+ <tr>
+ <td id=r1c1 tabindex=-1 rowspan=2 colspan=2>r1c1</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ <td id=r1c4 tabindex=-1 rowspan=3>r1c4</td>
+ <td id=r1c5 tabindex=-1>r1c5</td>
+ <td id=r1c6 tabindex=-1>r1c6</td>
+ <td id=r1c7 tabindex=-1>r1c7</td>
+ </tr>
+ <tr>
+ <!-- r2c1 and r2c2 starts in the previous row and spans to here. -->
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ <!-- Leave the rest of the row empty -->
+ </tr>
+ <tr>
+ <td id=r3c1 tabindex=-1>r3c1</td>
+ <td id=r3c2 tabindex=-1>r3c2</td>
+ <!-- There will be a cell at r3c4, but it starts in row 1. -->
+ </tr>
+ <tr>
+ <td id=r4c1 tabindex=-1 colspan=5>r4c1</td>
+ <td id=r4c6 tabindex=-1>r4c6</td>
+ <!-- No last cell - leave it empty for the test -->
+ </tr>
+</table>
+
+<script>
+ // The following tests are very corner-case.
+ //
+ // We are creating empty spaces that don't have cells through a weird table
+ // structure. The spaces at the following locations don't have cells (assuming
+ // that the first row/column starts at index 1): r2c5, r2c6, r2c7, r3c3,
+ // r3c5, r3c6, r3c7 and r4c7.
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c4 = document.getElementById("r1c4");
+ var r1c5 = document.getElementById("r1c5");
+ var r2c3 = document.getElementById("r2c3");
+ var r3c2 = document.getElementById("r3c2");
+ var r4c6 = document.getElementById("r4c6");
+
+ await focusAndKeyPress(r3c2, kArrowRight);
+ assert_equals(document.activeElement, r1c4);
+
+ await focusAndKeyPress(r1c4, kArrowRight);
+ assert_equals(document.activeElement, r1c5);
+
+ await focusAndKeyPress(r4c6, kArrowRight);
+ assert_equals(document.activeElement, r1c1);
+
+ await focusAndKeyPress(r2c3, kArrowRight);
+ assert_equals(document.activeElement, r1c4);
+ }, "A right arrow press should move the focus to the next column, dealing correctly with the empty spaces.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c4 = document.getElementById("r1c4");
+ var r1c5 = document.getElementById("r1c5");
+ var r1c6 = document.getElementById("r1c6");
+ var r1c7 = document.getElementById("r1c7");
+ var r2c3 = document.getElementById("r2c3");
+ var r4c1 = document.getElementById("r4c1");
+ var r4c6 = document.getElementById("r4c6");
+
+ await focusAndKeyPress(r2c3, kArrowDown);
+ assert_equals(document.activeElement, r4c1);
+
+ await focusAndKeyPress(r1c5, kArrowDown);
+ assert_equals(document.activeElement, r4c1);
+
+ await focusAndKeyPress(r1c6, kArrowDown);
+ assert_equals(document.activeElement, r4c6);
+
+ // Goes to r1c1 because it flows to the first cell of the first column when
+ // on the last cell of the last column.
+ await focusAndKeyPress(r1c7, kArrowDown);
+ assert_equals(document.activeElement, r1c1);
+
+ await focusAndKeyPress(r4c6, kArrowDown);
+ assert_equals(document.activeElement, r1c7);
+
+ await focusAndKeyPress(r1c4, kArrowDown);
+ assert_equals(document.activeElement, r4c1);
+ }, "A down arrow press should move the focus to the right cell on the next row, dealing correctly with empty spaces.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c3 = document.getElementById("r1c3");
+ var r1c4 = document.getElementById("r1c4");
+ var r3c1 = document.getElementById("r3c1");
+ var r4c1 = document.getElementById("r4c1");
+ var r4c6 = document.getElementById("r4c6");
+
+ await focusAndKeyPress(r1c1, kArrowLeft);
+ assert_equals(document.activeElement, r4c6);
+
+ await focusAndKeyPress(r3c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c4);
+
+ await focusAndKeyPress(r1c4, kArrowLeft);
+ assert_equals(document.activeElement, r1c3);
+
+ await focusAndKeyPress(r4c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c4);
+ }, "A left arrow press should move to the previous column, dealing correctly with the empty spaces.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c6 = document.getElementById("r1c6");
+ var r1c7 = document.getElementById("r1c7");
+ var r3c1 = document.getElementById("r3c1");
+ var r4c1 = document.getElementById("r4c1");
+ var r4c6 = document.getElementById("r4c6");
+
+ await focusAndKeyPress(r1c7, kArrowUp);
+ assert_equals(document.activeElement, r4c6);
+
+ await focusAndKeyPress(r4c6, kArrowUp);
+ assert_equals(document.activeElement, r1c6);
+
+ await focusAndKeyPress(r1c6, kArrowUp);
+ assert_equals(document.activeElement, r4c1);
+
+ await focusAndKeyPress(r4c1, kArrowUp);
+ assert_equals(document.activeElement, r3c1);
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r1c7);
+ }, "An up arrow press should move the focus to the right cell on the previous row, dealing correctly with empty spaces.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/flows-in-both-axes.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/flows-in-both-axes.html
new file mode 100644
index 0000000000..86fc25456b
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/flows-in-both-axes.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that flow allows both horizontal and vertical flowing.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid flow">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r2c1);
+ }, "When on the last column, a right arrow press should move the focus to first column and next row if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r1c2 = document.getElementById("r1c2");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r1c2);
+ }, "When on the last row, a down arrow press should move the focus to first row and next column if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c3);
+ }, "When on the first column, a left arrow press should move the focus to last column and previous row if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c3 = document.getElementById("r2c3");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r2c3);
+ }, "When on the first row, an up arrow press should move the focus to last row and previous column if the focusgroup flows horizontally.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/moves-across-table-sections.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/moves-across-table-sections.html
new file mode 100644
index 0000000000..d7d1b55f45
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/moves-across-table-sections.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Table with one or less row per section (head, 2 bodies and foot).</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid wrap">
+ <thead>
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td id=r3c1 tabindex=-1>r3c1</td>
+ </tr>
+ <tr>
+ <td id=r4c1 tabindex=-1>r4c1</td>
+ </tr>
+ </tbody>
+ <tbody></tbody>
+ <tfoot>
+ <tr>
+ <td id=r5c1 tabindex=-1>r5c1</td>
+ </tr>
+ <tr>
+ <td id=r6c1 tabindex=-1>r6c1</td>
+ </tr>
+ </foot>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r2c1");
+ var r3c2 = document.getElementById("r3c1");
+ var r4c2 = document.getElementById("r4c1");
+ var r5c2 = document.getElementById("r5c1");
+ var r6c2 = document.getElementById("r6c1");
+
+ await focusAndKeyPress(r1c1, kArrowDown);
+ assert_equals(document.activeElement, r2c1);
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r3c1);
+
+ await focusAndKeyPress(r3c1, kArrowDown);
+ assert_equals(document.activeElement, r4c1);
+
+ await focusAndKeyPress(r4c1, kArrowDown);
+ assert_equals(document.activeElement, r5c1);
+
+ await focusAndKeyPress(r5c1, kArrowDown);
+ assert_equals(document.activeElement, r6c1);
+
+ await focusAndKeyPress(r6c1, kArrowDown);
+ assert_equals(document.activeElement, r1c1);
+ }, "A down arrow press should move the focus to the next row even when that row is in another section.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r2c1");
+ var r3c2 = document.getElementById("r3c1");
+ var r4c2 = document.getElementById("r4c1");
+ var r5c2 = document.getElementById("r5c1");
+ var r6c2 = document.getElementById("r6c1");
+
+ await focusAndKeyPress(r6c1, kArrowUp);
+ assert_equals(document.activeElement, r5c1);
+
+ await focusAndKeyPress(r5c1, kArrowUp);
+ assert_equals(document.activeElement, r4c1);
+
+ await focusAndKeyPress(r4c1, kArrowUp);
+ assert_equals(document.activeElement, r3c1);
+
+ await focusAndKeyPress(r3c1, kArrowUp);
+ assert_equals(document.activeElement, r2c1);
+
+ await focusAndKeyPress(r2c1, kArrowUp);
+ assert_equals(document.activeElement, r1c1);
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r6c1);
+ }, "An up arrow press should move the focus to the previous row even when that row is in another section.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/non-table.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/non-table.html
new file mode 100644
index 0000000000..c02de186e6
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/non-table.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that Focusgroup doesn't work when not set on a table element</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<div focusgroup=grid>
+ <span id=nonitem1 tabindex=0>nonitem1</span>
+ <span id=nonitem2 tabindex=-1>nonitem2</span>
+</div>
+
+<script>
+ promise_test(async t => {
+ var nonitem1 = document.getElementById("nonitem1");
+
+ await focusAndKeyPress(nonitem1, kArrowRight);
+ assert_equals(document.activeElement, nonitem1);
+ }, "Validates that focusgroup=grid set on a non table layout doesn't become a grid focusgroup nor a linear one.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/relayout-before-navigation.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/relayout-before-navigation.html
new file mode 100644
index 0000000000..d1fd0ce6eb
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/relayout-before-navigation.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Relayout before navigating in a grid</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+<script>
+ function removeMiddleRow() {
+ document.getElementById("middle-row").remove();
+ }
+</script>
+<table focusgroup="grid">
+ <tr>
+ <td id="item1" tabindex="0" onkeydown="removeMiddleRow()">item1</td>
+ </tr>
+ <tr id="middle-row">
+ <td id="item2" tabindex="-1">item2</td>
+ </tr>
+ <tr>
+ <td id="item3" tabindex="-1">item3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var item1 = document.getElementById("item1");
+ var item3 = document.getElementById("item3");
+
+ await focusAndKeyPress(item1, kArrowDown);
+ assert_equals(document.activeElement, item3);
+ }, "Since |item1| removes the middle row on key press, the grid focusgroup should check for a relayout before navigating to the next row.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-flow-only.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-flow-only.html
new file mode 100644
index 0000000000..138b1b33d5
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-flow-only.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that row-flow allows only horizontal flowing.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid row-flow">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r2c1);
+ }, "When on the last column, a right arrow press should move the focus to first column and next row if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r2c1);
+ }, "A down arrow press should not flow the focus vertically when not supported by the focusgroup.");
+
+ promise_test(async t => {
+ var r1c3 = document.getElementById("r1c3");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c3);
+ }, "When on the first column, a left arrow press should move the focus to last column and previous row if the focusgroup flows horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r1c1);
+ }, "An up arrow press should not flow the focus vertically when not supported by the focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-and-col-flow.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-and-col-flow.html
new file mode 100644
index 0000000000..8ed69e2e3e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-and-col-flow.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that we can row-wrap AND col-flow.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid row-wrap col-flow">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r1c1);
+ }, "When on the last column, a right arrow press should move the focus to first column if the focusgroup wraps horizontally.");
+
+ promise_test(async t => {
+ var r1c2 = document.getElementById("r1c2");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r1c2);
+ }, "When on the last row, a down arrow press should move the focus to first row and next column if the focusgroup flows vertically.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c3);
+ }, "When on the first column, a left arrow press should move the focus to last column if the focusgroup wraps horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c3 = document.getElementById("r2c3");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r2c3);
+ }, "When on the first row, an up arrow press should move the focus to last row and previous column if the focusgroup flows horizontally.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-only.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-only.html
new file mode 100644
index 0000000000..0154ee2125
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/row-wrap-only.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that row-wrap allows only horizontal wrapping.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid row-wrap">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r1c1);
+ }, "When on the last column, a right arrow press should move the focus to first column if the focusgroup wraps horizontally.");
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r2c1);
+ }, "A down arrow press should not wrap the focus to the first row when only horizontal wrap is supported by the focusgroup.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c3);
+ }, "When on the first column, a left arrow press should move the focus to last column if the focusgroup wraps horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r1c1);
+ }, "An up arrow press should not wrap the focus to the last row when only horizontal wrap is supported by the focusgroup.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/rowspan.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/rowspan.html
new file mode 100644
index 0000000000..81ab54ef8e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/rowspan.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that we deal correctly with rowspans.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid">
+ <tr>
+ <td id=r1c1 tabindex=-1>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=0>r2c1</td>
+ <td id=r2c2 tabindex=-1 rowspan=3>r2c2</td>
+ <td id=r2c3 tabindex=-1>r3c3</td>
+ </tr>
+ <tr>
+ <td id=r3c1 tabindex=-1>r3c1</td>
+ <td id=r3c3 tabindex=-1>r3c3</td>
+ </tr>
+ <tr>
+ <td id=r4c1 tabindex=-1>r4c1</td>
+ <td id=r4c3 tabindex=-1>r4c3</td>
+ </tr>
+ <tr>
+ <td id=r5c1 tabindex=-1>r5c1</td>
+ <td id=r5c2 tabindex=-1>r5c2</td>
+ <td id=r5c3 tabindex=-1>r5c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+ var r2c2 = document.getElementById("r2c2");
+ var r2c3 = document.getElementById("r2c3");
+ var r3c1 = document.getElementById("r3c1");
+ var r4c1 = document.getElementById("r4c1");
+ var r5c1 = document.getElementById("r5c1");
+ var r5c2 = document.getElementById("r5c2");
+
+ await focusAndKeyPress(r2c1, kArrowRight);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r2c2, kArrowRight);
+ assert_equals(document.activeElement, r2c3);
+
+ await focusAndKeyPress(r3c1, kArrowRight);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r4c1, kArrowRight);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r5c1, kArrowRight);
+ assert_equals(document.activeElement, r5c2);
+ }, "A right arrow press should move the focus to the next column, dealing correctly with rowspans.");
+
+ promise_test(async t => {
+ var r1c2 = document.getElementById("r1c2");
+ var r1c3 = document.getElementById("r1c3");
+ var r2c2 = document.getElementById("r2c2");
+ var r2c3 = document.getElementById("r2c3");
+ var r3c3 = document.getElementById("r3c3");
+ var r5c2 = document.getElementById("r5c2");
+ var r5c3 = document.getElementById("r5c3");
+
+ await focusAndKeyPress(r1c2, kArrowDown);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r1c3, kArrowDown);
+ assert_equals(document.activeElement, r2c3);
+
+ await focusAndKeyPress(r2c2, kArrowDown);
+ assert_equals(document.activeElement, r5c2);
+
+ await focusAndKeyPress(r2c3, kArrowDown);
+ assert_equals(document.activeElement, r3c3);
+
+ await focusAndKeyPress(r3c3, kArrowDown);
+ assert_equals(document.activeElement, r4c3);
+
+ await focusAndKeyPress(r4c3, kArrowDown);
+ assert_equals(document.activeElement, r5c3);
+ }, "A down arrow press should move the focus to the right cell on the next row, dealing correctly with rowspans.");
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+ var r2c2 = document.getElementById("r2c2");
+ var r2c3 = document.getElementById("r2c3");
+ var r3c3 = document.getElementById("r3c3");
+ var r4c3 = document.getElementById("r4c3");
+ var r5c3 = document.getElementById("r5c3");
+ var r5c2 = document.getElementById("r5c2");
+
+ await focusAndKeyPress(r2c3, kArrowLeft);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r2c2, kArrowLeft);
+ assert_equals(document.activeElement, r2c1);
+
+ await focusAndKeyPress(r3c3, kArrowLeft);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r4c3, kArrowLeft);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r5c3, kArrowLeft);
+ assert_equals(document.activeElement, r5c2);
+ }, "A left arrow press should move to the previous column, dealing correctly with the rowspans.");
+
+ promise_test(async t => {
+ var r1c2 = document.getElementById("r1c2");
+ var r1c3 = document.getElementById("r1c3");
+ var r2c2 = document.getElementById("r2c2");
+ var r2c3 = document.getElementById("r2c3");
+ var r3c3 = document.getElementById("r3c3");
+ var r4c3 = document.getElementById("r4c3");
+ var r5c2 = document.getElementById("r5c2");
+ var r5c3 = document.getElementById("r5c3");
+
+ await focusAndKeyPress(r5c2, kArrowUp);
+ assert_equals(document.activeElement, r2c2);
+
+ await focusAndKeyPress(r5c3, kArrowUp);
+ assert_equals(document.activeElement, r4c3);
+
+ await focusAndKeyPress(r2c2, kArrowUp);
+ assert_equals(document.activeElement, r1c2);
+
+ await focusAndKeyPress(r4c3, kArrowUp);
+ assert_equals(document.activeElement, r3c3);
+
+ await focusAndKeyPress(r3c3, kArrowUp);
+ assert_equals(document.activeElement, r2c3);
+
+ await focusAndKeyPress(r2c3, kArrowUp);
+ assert_equals(document.activeElement, r1c3);
+ }, "An up arrow press should move the focus to the right cell on the previous row, dealing correctly with rowspans.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case-with-non-focusable-cell-in-the-center.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case-with-non-focusable-cell-in-the-center.html
new file mode 100644
index 0000000000..25d2900a9f
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case-with-non-focusable-cell-in-the-center.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Simple case with grid focusgroup, but with the cell R2C2 not focusable.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup=grid>
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+ <tr>
+ <td id=r3c1 tabindex=-1>r3c1</td>
+ <td id=r3c2 tabindex=-1>r3c2</td>
+ <td id=r3c3 tabindex=-1>r3c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+ var r2c3 = document.getElementById("r2c3");
+
+ await focusAndKeyPress(r2c1, kArrowRight);
+ assert_equals(document.activeElement, r2c3);
+ }, "A right arrow press should move the focus to the next column, skipping the non-focusable cell.");
+
+ promise_test(async t => {
+ var r1c2 = document.getElementById("r1c2");
+ var r3c2 = document.getElementById("r3c2");
+
+ await focusAndKeyPress(r1c2, kArrowDown);
+ assert_equals(document.activeElement, r3c2);
+ }, "A down arrow press should move the focus to the next row, skipping the non-focusable cell.");
+
+ promise_test(async t => {
+ var r2c1 = document.getElementById("r2c1");
+ var r2c3 = document.getElementById("r2c3");
+
+ await focusAndKeyPress(r2c3, kArrowLeft);
+ assert_equals(document.activeElement, r2c1);
+ }, "A left arrow press should move to the previous column, skipping the non-focusable cell.");
+
+ promise_test(async t => {
+ var r1c2 = document.getElementById("r1c2");
+ var r3c2 = document.getElementById("r3c2");
+
+ await focusAndKeyPress(r3c2, kArrowUp);
+ assert_equals(document.activeElement, r1c2);
+ }, "An up arrow press should move the focus to the previous row, skipping the non-focusable cell.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case.html
new file mode 100644
index 0000000000..738895ce0b
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/simple-case.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Simple case with grid focusgroup</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup=grid>
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r1c2");
+
+ await focusAndKeyPress(r1c1, kArrowRight);
+ assert_equals(document.activeElement, r1c2);
+ }, "A right arrow press should move the focus to the next column.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r1c1, kArrowDown);
+ assert_equals(document.activeElement, r2c1);
+ }, "A down arrow press should move the focus to the next row.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c2 = document.getElementById("r1c2");
+
+ await focusAndKeyPress(r1c2, kArrowLeft);
+ assert_equals(document.activeElement, r1c1);
+ }, "A left arrow press should move to the previous column.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowUp);
+ assert_equals(document.activeElement, r1c1);
+ }, "An up arrow press should move the focus to the previous row.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/wraps-in-both-axes.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/wraps-in-both-axes.html
new file mode 100644
index 0000000000..809ee53645
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/grid-navigation/wraps-in-both-axes.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focusgroup - Validate that wrap allows both horizontal and vertical wrapping.</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/focusgroup-utils.js"></script>
+
+<table focusgroup="grid wrap">
+ <tr>
+ <td id=r1c1 tabindex=0>r1c1</td>
+ <td id=r1c2 tabindex=-1>r1c2</td>
+ <td id=r1c3 tabindex=-1>r1c3</td>
+ </tr>
+ <tr>
+ <td id=r2c1 tabindex=-1>r2c1</td>
+ <td id=r2c2 tabindex=-1>r2c2</td>
+ <td id=r2c3 tabindex=-1>r2c3</td>
+ </tr>
+</table>
+
+<script>
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c3, kArrowRight);
+ assert_equals(document.activeElement, r1c1);
+ }, "When on the last column, a right arrow press should move the focus to first column if the focusgroup wraps horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r2c1, kArrowDown);
+ assert_equals(document.activeElement, r1c1);
+ }, "When on the last row, a down arrow press should move the focus to first row if the focusgroup wraps vertically.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r1c3 = document.getElementById("r1c3");
+
+ await focusAndKeyPress(r1c1, kArrowLeft);
+ assert_equals(document.activeElement, r1c3);
+ }, "When on the first column, a left arrow press should move the focus to last column if the focusgroup wraps horizontally.");
+
+ promise_test(async t => {
+ var r1c1 = document.getElementById("r1c1");
+ var r2c1 = document.getElementById("r2c1");
+
+ await focusAndKeyPress(r1c1, kArrowUp);
+ assert_equals(document.activeElement, r2c1);
+ }, "When on the first row, an up arrow press should move the focus to last row if the focusgroup wraps vertically.");
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/resources/focusgroup-utils.js b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/resources/focusgroup-utils.js
new file mode 100644
index 0000000000..9a1a14a301
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/resources/focusgroup-utils.js
@@ -0,0 +1,15 @@
+/*
+ Methods for testing the focusgroup feature.
+*/
+
+// https://w3c.github.io/webdriver/#keyboard-actions
+const kArrowLeft = '\uE012';
+const kArrowUp = '\uE013';
+const kArrowRight = '\uE014';
+const kArrowDown = '\uE015';
+
+// Set the focus on target and send the arrow key press event from it.
+function focusAndKeyPress(target, key) {
+ target.focus();
+ return test_driver.send_keys(target, key);
+}
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html b/testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html
new file mode 100644
index 0000000000..2413fe2667
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Focus fixup rule one (no &lt;dialog>s involved)</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focus-fixup-rule">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#attr-fieldset-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div>
+ <button id="button1">Button 1</button>
+ <button id="button2">Button 2</button>
+ <button id="button3">Button 3</button>
+ <fieldset id="fieldset1"><button id="button4">Button 4</button></fieldset>
+ <fieldset id="fieldset2" disabled><legend><button id="button5">Button 5</button></legend></fieldset>
+ <div id="div" tabindex="0">Div</div>
+ <div id="editable" contenteditable=true>editor</div>
+</div>
+
+<script>
+"use strict";
+
+test(() => {
+ const button = document.querySelector("#button1");
+ button.focus();
+
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ button.disabled = true;
+
+ assert_not_equals(document.activeElement, button, "After disabling, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After disabling, the body must be focused");
+
+}, "Disabling the active element (making it inert)");
+
+test(() => {
+ const button = document.querySelector("#button2");
+ button.focus();
+
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ button.hidden = true;
+
+ assert_not_equals(document.activeElement, button, "After hiding, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After hiding, the body must be focused");
+
+}, "Hiding the active element");
+
+test(() => {
+ const button = document.querySelector("#button3");
+ button.focus();
+
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ button.remove();
+
+ assert_not_equals(document.activeElement, button, "After removing, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After removing, the body must be focused");
+
+}, "Removing the active element from the DOM");
+
+test(() => {
+ const fieldset = document.querySelector("#fieldset1");
+ const button = document.querySelector("#button4");
+ button.focus();
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ fieldset.disabled = true;
+
+ assert_not_equals(document.activeElement, button, "After disabling ancestor fieldset, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After disabling ancestor fieldset, the body must be focused");
+}, "Disabling <fieldset> affects its descendants");
+
+test(() => {
+ const fieldset = document.querySelector("#fieldset2");
+ const button = document.querySelector("#button5");
+ button.focus();
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ fieldset.insertBefore(document.createElement("legend"), fieldset.firstChild);
+
+ assert_not_equals(document.activeElement, button, "After changing a legend element, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After changing a legend element, the body must be focused");
+}, "Changing the first legend element in disabled <fieldset>");
+
+test(() => {
+ const div = document.querySelector("#div");
+ div.focus();
+
+ assert_equals(document.activeElement, div, "Sanity check: the div must start focused");
+
+ div.removeAttribute("tabindex");
+
+ assert_not_equals(document.activeElement, div, "After removing tabindex, the div must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After removing tabindex, the body must be focused");
+
+}, "Removing the tabindex attribute from a div");
+
+test(() => {
+ const div = document.querySelector("#editable");
+ div.focus();
+ assert_equals(document.activeElement, div, "Sanity check: the div must start focused");
+
+ div.contentEditable = false;
+
+ assert_not_equals(document.activeElement, div, "After disabling contentEditable, the div must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After disabling contentEditable, the body must be focused");
+}, "Disabling contenteditable");
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html b/testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html
new file mode 100644
index 0000000000..aa7e66fffe
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html
@@ -0,0 +1,65 @@
+<!doctype html>
+
+<title>focus(options) - focusVisible</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+ div {
+ height: 10px;
+ border: 1px solid black;
+ }
+</style>
+
+<button>ABC</button>
+<input>
+<div id="contenteditable" contenteditable></div>
+<div id="tabindex" tabindex=0></div>
+<div id="not_focusable"></div>
+
+<div id="reset_focus" tabindex=0></div>
+
+<script>
+const reset_focus = document.getElementById("reset_focus");
+
+async function check_focus_visible(element, options, { shouldBeVisible, shouldBeFocused }) {
+ // Reset focus by clicking on a div, which should not show focus rings.
+ await test_driver.click(reset_focus);
+
+ assert_equals(document.activeElement, reset_focus, "Reset should be focused");
+ assert_true(reset_focus.matches(":focus"), "Clicking focusable div should match :focus");
+ assert_false(reset_focus.matches(":focus-visible"), "Clicking focusable div shouldn't match :focus-visible");
+
+ element.focus(options);
+
+ assert_equals(document.activeElement, shouldBeFocused ? element : reset_focus, "activeElement");
+ assert_equals(element.matches(":focus"), shouldBeFocused, ":focus");
+ assert_equals(element.matches(":focus-visible"), shouldBeVisible, ":focus-visible");
+}
+
+for (let selector of ["button", "input", "#contenteditable", "#tabindex", "#not_focusable"]) {
+ promise_test(async function() {
+ const takesKeyboardInput = selector == "#contenteditable" || selector == "input";
+ const shouldBeFocused = selector != "#not_focusable";
+
+ const element = document.querySelector(selector);
+
+ await check_focus_visible(element, {}, {
+ shouldBeVisible: takesKeyboardInput,
+ shouldBeFocused,
+ });
+
+ await check_focus_visible(element, { focusVisible: true }, {
+ shouldBeVisible: shouldBeFocused,
+ shouldBeFocused,
+ });
+ await check_focus_visible(element, { focusVisible: false }, {
+ shouldBeVisible: false,
+ shouldBeFocused,
+ });
+ }, "FocusOptions.focusVisible: " + selector);
+}
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html b/testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html
new file mode 100644
index 0000000000..c9209d3cf6
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>legend focusable</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ const t = async_test();
+</script>
+<fieldset>
+ <legend tabindex=0 onfocus="t.step(() => { t.done(); })">
+ legend
+ <input onfocus="t.unreached_func('input in legend was focused')();">
+ </legend>
+ <input onfocus="t.unreached_func('input after legend was focused')();">
+</fieldset>
+<script>
+ document.querySelector('legend').focus();
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/legend.html b/testing/web-platform/tests/html/interaction/focus/processing-model/legend.html
new file mode 100644
index 0000000000..b53839374d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/legend.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ const t = async_test();
+</script>
+<fieldset>
+ <legend onfocus="t.unreached_func('legend was focused')()">
+ legend
+ <input onfocus="t.unreached_func('input in legend was focused')();">
+ </legend>
+ <input onfocus="t.unreached_func('input after legend was focused')();">
+</fieldset>
+<script>
+ document.querySelector('legend').focus();
+ t.step_timeout(() => {
+ t.done();
+ }, 500);
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html
new file mode 100644
index 0000000000..a7ae76f733
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<title>focus(options) - preventScroll on nested scroll elements</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ .scrollBox { width: 400px; height: 300px; border: 1px solid }
+ .bigbox { width: 380px; height: 300px; border: 1px solid }
+ .box { width: 380px; height: 150px; border: 1px solid }
+</style>
+<div class="scrollBox" style="overflow-y: scroll;">
+ <div class="bigbox" id="1" style="overflow-y: scroll;" tabindex=1>
+ <div class="box" id="1_1" tabindex=1>1_1</div>
+ <div class="box" id="1_2" tabindex=1>1_2</div>
+ <div class="box" id="1_3" tabindex=1>1_3</div>
+ </div>
+ <div class="bigbox" id="2" style="overflow-y: scroll;" tabindex=1>
+ <div class="box" id="2_1" tabindex=1>2_1</div>
+ <div class="box" id="2_2" tabindex=1>2_2</div>
+ <div class="box" id="2_3" tabindex=1>2_3</div>
+ </div>
+ <div class="bigbox" id="3" style="overflow-y: scroll;" tabindex=1>
+ <div class="box" id="3_1" tabindex=1>3_1</div>
+ <div class="box" id="3_2" tabindex=1>3_2</div>
+ <div class="box" id="3_3" tabindex=1>3_3</div>
+ </div>
+</div>
+<script>
+promise_test(async function(t) {
+ await new Promise(resolve => window.addEventListener("load", resolve));
+ let div2_2 = document.getElementById("2_2");
+ div2_2.focus({ preventScroll: true });
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+
+ assert_equals(document.activeElement, div2_2, `box 2_2: should have been focused`);
+ assert_equals(div2_2.scrollTop, 0, "box 2_2: should not have scrolled");
+ assert_equals(div2_2.parentNode.scrollTop, 0, "box 2_2: should not have scrolled ancestor");
+
+ // Reset focus
+ let div1_1 = document.getElementById("1_1");
+ div1_1.focus();
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+ assert_equals(document.activeElement, div1_1, `box 1_1: should have been focused`);
+
+ let div2 = document.getElementById("2");
+ div2.focus({ preventScroll: true });
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+
+ assert_equals(document.activeElement, div2, `box 2: should have been focused`);
+ assert_equals(div2.scrollTop, 0, "box 2: should not have scrolled");
+ assert_equals(div2_2.scrollTop, 0, "box 2_2: should not have scrolled");
+ assert_equals(div2.parentNode.scrollTop, 0, "box 2: should not have scrolled ancestor");
+});
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html
new file mode 100644
index 0000000000..446284b186
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>focus(options) - preventScroll on textarea element</title>
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1634153">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div style="height: 200vh"></div>
+<textarea>ABCD</textarea>
+<input value="EFGH">
+<button></button>
+<div style="height: 200vh"></div>
+<script>
+promise_test(async function(t) {
+ await new Promise(resolve => window.addEventListener("load", resolve));
+ let elements = document.querySelectorAll("textarea, input, button");
+ assert_equals(elements.length, 3, `Precondition`);
+ for (let element of elements) {
+ let name = element.nodeName;
+ assert_equals(window.scrollY, 0, `${name}: Precondition`);
+ element.focus({ preventScroll: true });
+ assert_equals(window.scrollY, 0, `${name}: Should not have scrolled`);
+ assert_equals(document.activeElement, element, `${name}: Should have been focused`);
+
+ // Wait a couple event loop turns because the original bug was triggered off
+ // an async event.
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ assert_equals(window.scrollY, 0, `${name}: Should not have scrolled after a couple event loop ticks`);
+ assert_equals(document.activeElement, element, `${name}: Should remain focused`);
+
+ // Also wait for rendering, just out of paranoia.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ assert_equals(window.scrollY, 0, `${name}: Should not have scrolled after rendering`);
+ assert_equals(document.activeElement, element, `${name}: Should remain focused after rendering`);
+ }
+}, "preventScroll: true on a textarea element");
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html
new file mode 100644
index 0000000000..97d341b30e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<title>focus(options) - preventScroll</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#iframe { width: 500px; height: 500px; border: none }
+</style>
+<iframe id=iframe src="support/preventScroll-helper.html"></iframe>
+<script>
+function isEntirelyInView(elm, win) {
+ const inViewHorizontal = (elm.offsetLeft >= win.scrollX) &&
+ ((elm.offsetLeft + elm.clientWidth) <= (win.scrollX + win.innerWidth));
+ const inViewVertical = (elm.offsetTop >= win.scrollY) &&
+ ((elm.offsetTop + elm.clientHeight) <= (win.scrollY + win.innerHeight));
+ return inViewHorizontal && inViewVertical;
+}
+
+setup({explicit_done: true});
+
+function resetState(win) {
+ win.scrollTo(0, 0);
+ win.document.activeElement.blur();
+}
+
+onload = () => {
+ const win = document.getElementById('iframe').contentWindow;
+ const elm = win.document.getElementById('button');
+
+ test(() => {
+ assert_false(isEntirelyInView(elm, win), 'initial state');
+ elm.scrollIntoView();
+ assert_true(isEntirelyInView(elm, win), 'after elm.scrollIntoView()');
+ resetState(win);
+ assert_false(isEntirelyInView(elm, win), 'after resetScrollPosition(win)');
+ }, 'Sanity test');
+
+ test(() => {
+ resetState(win);
+ elm.focus();
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus() without arguments');
+
+ test(() => {
+ resetState(win);
+ elm.focus(undefined);
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus(undefined)');
+
+ test(() => {
+ resetState(win);
+ elm.focus(null);
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus(null)');
+
+ test(() => {
+ resetState(win);
+ elm.focus({});
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus({})');
+
+ test(() => {
+ resetState(win);
+ elm.focus({preventScroll: false});
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus({preventScroll: false})');
+
+ test(() => {
+ resetState(win);
+ elm.focus({preventScroll: true});
+ assert_equals(win.scrollX, 0);
+ assert_equals(win.scrollY, 0);
+ }, 'elm.focus({preventScroll: true})');
+
+ done();
+}
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html b/testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html
new file mode 100644
index 0000000000..43c6d86a57
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>Helper document for preventScroll test</title>
+<style>
+body { padding: 2000px }
+</style>
+<button id=button>X</button>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html b/testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html
new file mode 100644
index 0000000000..c8e252f7da
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<title>programatic focus() scrolls selection into view including ancestors</title>
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1644366">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div style="overflow: auto; height: 100px">
+ <textarea style="overflow: hidden; height: 200px">
+ Some text
+ That is surely more
+ Than 100px tall
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ </textarea>
+</div>
+<script>
+promise_test(async function(t) {
+ await new Promise(resolve => window.addEventListener("load", resolve));
+ let textarea = document.querySelector("textarea");
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length);
+ textarea.focus();
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+
+ assert_not_equals(textarea.parentNode.scrollTop, 0, "Should've scrolled ancestor to show the selection");
+});
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-default-value.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-default-value.html
new file mode 100644
index 0000000000..25e359c2a2
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-default-value.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - default value of tabindex</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#sequential-focus-navigation-and-the-tabindex-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<button id="test1">TEST1</button>
+<div id="test2">TEST2</div>
+<script>
+
+test(function() {
+ assert_equals(document.getElementById("test1").tabIndex, 0, "The value of tabIndex attribute should be 0.");
+}, "The default value of tabIndex attribute must be 0 for elements that are focusable");
+
+test(function() {
+ assert_equals(document.getElementById("test2").tabIndex, -1, "The value of tabIndex attribute should be -1.");
+}, "The default value of tabIndex attribute must be -1 for elements that are not focusable");
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-negative.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-negative.html
new file mode 100644
index 0000000000..859de94517
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-negative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - negative tabindex</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#sequential-focus-navigation-and-the-tabindex-attribute">
+<meta assert="flag" content="interact">
+<meta assert="assert" content="Check if the tabindex attribute controls whether an element is supposed to be focusable">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="log"></div>
+<form id="fm">
+ <input id="test1" tabindex="-1">
+ <input id="test2" tabindex="0">
+</form>
+<script>
+
+var t = async_test("The element with a negative tabindex must not be focused by press 'Tab' key");
+
+setup({timeout: 10000});
+
+document.forms.fm.addEventListener("focus", function (evt) {
+ t.step(function () {
+ var testEle = document.getElementById("test1");
+ assert_equals(testEle.tabIndex, -1, "The tabIndex attribute of the first input element should be -1.");
+ assert_not_equals(evt.target, testEle, "The second input element must be focused.");
+ assert_equals(document.activeElement, document.getElementById("test2"), "The second input element must be activated.");
+ });
+ t.done();
+}, true);
+
+document.addEventListener("keydown", function (evt) {
+ t.step(function () {
+ assert_equals(evt.keyCode, 9, "Please press 'Tab' key.");
+ });
+}, true);
+
+t.step(function () {
+ // TAB = '\ue004'
+ test_driver.send_keys(document.body, "\ue004");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-order.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-order.html
new file mode 100644
index 0000000000..d2ca0a6224
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-order.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<meta assert="flag" content="interact">
+<meta assert="assert" content="Check the sequential focus navigation order">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="log"></div>
+<form id="fm">
+ <button id="btn0">tabindex(omitted)</button>
+ <button id="btn1" tabindex="">tabindex(empty)</button>
+ <button id="btn2" tabindex="a">tabindex(a)</button>
+ <button id="btn3" tabindex="-1">tabindex(-1)</button>
+ <button id="btn4" tabindex="0">tabindex(0)</button>
+ <button id="btn5" tabindex="3">tabindex(3)</button>
+ <button id="btn6" tabindex="2">tabindex(2)</button>
+ <button id="btn7" tabindex="2">tabindex(2)</button>
+ <button id="btn8" tabindex="2">tabindex(2)</button>
+ <button id="btn9" tabindex="1">tabindex(1)</button>
+</form>
+<script>
+
+var i = 0,
+ expectation = ["btn9", "btn6", "btn7", "btn8", "btn5", "btn0", "btn1", "btn2", "btn4"],
+ results = [],
+ t = async_test("Elements with different tabindex must be focused sequentially when pressing 'Tab' keys");
+
+setup(function () {
+ document.body.focus();
+});
+
+
+
+document.forms.fm.addEventListener("focus", function (evt) {
+ results.push(evt.target.id);
+ if (i >= 8) {
+ t.step(function () {
+ assert_array_equals(results, expectation);
+ });
+ t.done();
+ } else {
+ t.step(function () {
+ // TAB = '\ue004'
+ test_driver.send_keys(document.body, "\ue004");
+ });
+ }
+ i++;
+}, true);
+
+document.addEventListener("keydown", function (evt) {
+ t.step(function () {
+ assert_equals(evt.keyCode, 9, "Please press 'Tab' key.");
+ });
+}, true);
+
+t.step(function () {
+ // TAB = '\ue004'
+ test_driver.send_keys(document.body, "\ue004");
+});
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-positive.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-positive.html
new file mode 100644
index 0000000000..1fab1f3adb
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-positive.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - positive tabindex</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#sequential-focus-navigation-and-the-tabindex-attribute">
+<meta assert="flag" content="interact">
+<meta assert="assert" content="Check if the tabindex attribute controls whether an element is supposed to be focusable">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="log"></div>
+<form id="fm">
+ <input id="test" tabindex="1">
+</form>
+<script>
+
+var t = async_test("The element with a positive tabindex must be focused by press 'Tab' key");
+
+setup({timeout: 10000});
+
+document.forms.fm.addEventListener("focus", function (evt) {
+ t.step(function () {
+ var testEle = document.getElementById("test");
+ assert_equals(testEle.tabIndex, 1, "The tabIndex attribute of the input element should be 1.");
+ assert_equals(evt.target, testEle, "The input element must be focused.");
+ assert_equals(document.activeElement, testEle, "The input element must be activated.");
+ });
+ t.done();
+}, true);
+
+document.addEventListener("keydown", function (evt) {
+ t.step(function () {
+ assert_equals(evt.keyCode, 9, "Please press 'Tab' key.");
+ });
+}, true);
+
+t.step(function () {
+ // TAB = '\ue004'
+ test_driver.send_keys(document.body, "\ue004");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-zero.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-zero.html
new file mode 100644
index 0000000000..bda7c84687
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-zero.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<title>HTML Test: focus - zero tabindex</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#sequential-focus-navigation-and-the-tabindex-attribute">
+<meta assert="flag" content="interact">
+<meta assert="assert" content="Check if the tabindex attribute controls whether an element is supposed to be focusable">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<div id="log"></div>
+<form id="fm">
+ <input id="test" tabindex="0">
+</form>
+<script>
+
+var t = async_test("The element with a zero tabindex must be focused by press 'Tab' key");
+
+setup({timeout: 10000});
+
+document.forms.fm.addEventListener("focus", function (evt) {
+ t.step(function () {
+ var testEle = document.getElementById("test");
+ assert_equals(testEle.tabIndex, 0, "The tabIndex attribute of the input element should be 0.");
+ assert_equals(evt.target, testEle, "The input element must be focused.");
+ assert_equals(document.activeElement, testEle, "The input element must be activated.");
+ });
+ t.done();
+}, true);
+
+document.addEventListener("keydown", function (evt) {
+ t.step(function () {
+ assert_equals(evt.keyCode, 9, "Please press 'Tab' key.");
+ });
+}, true);
+
+t.step(function () {
+ // TAB = '\ue004'
+ test_driver.send_keys(document.body, "\ue004");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/resources/frameset-using-page.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/resources/frameset-using-page.html
new file mode 100644
index 0000000000..e3aedea246
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/resources/frameset-using-page.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>We'll grab a frame from this page to test its tabIndex</title>
+<frameset>
+ <frame></frame>
+</frameset>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter-frame.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter-frame.html
new file mode 100644
index 0000000000..27a92f76ab
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter-frame.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: tabIndex getter return value for frames</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- <frame> elements are harder to test than the rest so they get their own file -->
+
+<body>
+
+<script>
+"use strict";
+test(() => {
+ const frame = document.createElement("frame");
+ assert_equals(frame.tabIndex, 0);
+}, "disconnected frame element .tabIndex should return 0 by default");
+
+for (const setValue of [-1, 0, 1]) {
+ test(() => {
+ const frame = document.createElement("frame");
+ frame.setAttribute("tabindex", setValue);
+ assert_equals(frame.tabIndex, setValue);
+ }, `disconnected frame element .tabIndex should return ${setValue} when set to ${setValue}`);
+}
+
+promise_test(async t => {
+ const frame = await getFrame(t);
+ assert_equals(frame.tabIndex, 0);
+}, "connected frame element inside frameset .tabIndex should return 0 by default");
+
+for (const setValue of [-1, 0, 1]) {
+ promise_test(async t => {
+ const frame = await getFrame(t);
+ frame.setAttribute("tabindex", setValue);
+ assert_equals(frame.tabIndex, setValue);
+ }, `connected frame element inside frameset .tabIndex should return ${setValue} when set to ${setValue}`);
+}
+
+
+function getFrame(t) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+
+ iframe.src = "resources/frameset-using-page.html";
+ iframe.onload = () => {
+ resolve(iframe.contentDocument.querySelector("frame"));
+ };
+ iframe.onerror = () => reject(new Error("Could not load frameset page"));
+
+ document.body.append(iframe);
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter.html b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter.html
new file mode 100644
index 0000000000..785e8c91ff
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: tabIndex getter return value</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#scrollable {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+}
+#scrollable-inner {
+ width: 1024px;
+ height: 2048px;
+}
+</style>
+<body>
+<input>
+<input type="hidden">
+<button>button</button>
+<button disabled>button</button>
+<button hidden>button</button>
+<a>a</a>
+<a href="#">a</a>
+<svg><a>svg a</a></svg>
+<svg><a>svg a</a></svg>
+<link id="nohref">
+<textarea></textarea>
+<select><optgroup><option>option</option></optgroup></select>
+<select multiple></select>
+<iframe width="10" height="10"></iframe>
+<embed width="10" height="10"></embed>
+<object width="10" height="10"></object>
+<span></span>
+<div></div>
+<details><summary>summary</summary><summary id="secondsummary">second summary</summary>details</details>
+<div id="hostDelegatesFocus"></div>
+<div id="hostNonDelegatesFocus"></div>
+<div contenteditable="true"></div>
+<div id="scrollable"><div id="scrollable-inner"></div></div>
+<fieldset></fieldset>
+<output></output>
+<slot></slot>
+<script>
+document.getElementById("hostDelegatesFocus").attachShadow({ mode: "open", delegatesFocus: true });
+document.getElementById("hostNonDelegatesFocus").attachShadow({ mode: "open", delegatesFocus: false });
+const defaultList = [
+ ["input", 0],
+ ["input[type='hidden']", 0],
+ ["button", 0],
+ ["button[disabled]", 0],
+ ["button[hidden]", 0],
+ ["a", 0],
+ ["a[href]", 0],
+ ["svg a", 0],
+ ["textarea", 0],
+ ["select", 0],
+ ["select[multiple]", 0],
+ ["option", -1],
+ ["optgroup", -1],
+ ["iframe", 0],
+ ["embed", -1],
+ ["object", 0],
+ ["span", -1],
+ ["div", -1],
+ ["link#nohref", -1],
+ ["link[href]", -1],
+ ["details", -1],
+ ["summary", 0],
+ ["summary#secondsummary", -1],
+ ["#hostDelegatesFocus", -1],
+ ["#hostNonDelegatesFocus", -1],
+ ["div[contenteditable]", -1],
+ ["#scrollable", -1],
+ ["fieldset", -1],
+ ["output", -1],
+ ["slot", -1],
+];
+const tabIndexValue = [-1, 0, 1];
+for (const entry of defaultList) {
+ const element = document.querySelector(entry[0]);
+ test(() => {
+ assert_equals(element.tabIndex, entry[1]);
+ }, entry[0] + ".tabIndex should return " + entry[1] + " by default");
+ for (const setValue of tabIndexValue ) {
+ test(() => {
+ element.setAttribute("tabindex", setValue);
+ assert_equals(element.tabIndex, setValue);
+ }, entry[0] + ".tabIndex should return " + setValue + " when set to " + setValue);
+ }
+}
+</script>
+</body>
+
diff --git a/testing/web-platform/tests/html/interaction/focus/setSequentialFocusStartingPoint.tentative.html b/testing/web-platform/tests/html/interaction/focus/setSequentialFocusStartingPoint.tentative.html
new file mode 100644
index 0000000000..a7c7506b13
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/setSequentialFocusStartingPoint.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/5326">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<button id=b1>b1</button>
+<div id=d1>d1</div>
+<button id=b2>b2</button>
+
+<script>
+const tabKey = '\uE004';
+promise_test(async () => {
+ assert_equals(document.activeElement, document.body,
+ 'Focus should initially be set on the body element.');
+
+ document.setSequentialFocusStartingPoint(d1);
+ assert_equals(document.activeElement, document.body,
+ 'Calling setSequentialFocusStartingPoint should not change the focused element.');
+
+ await test_driver.send_keys(document.activeElement, tabKey);
+ assert_equals(document.activeElement, b2,
+ 'Pressing tab should focus the next button after the sequential focus starting point.');
+}, 'document.setSequentialFocusStartingPoint should set the sequential focus starting point on any element.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/tabindex-focus-flag.html b/testing/web-platform/tests/html/interaction/focus/tabindex-focus-flag.html
new file mode 100644
index 0000000000..93fdb19a59
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/tabindex-focus-flag.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#specially-focusable">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="default-samples">
+<a></a>
+<a href=""></a>
+<button></button>
+<input type="hidden">
+<input type="button">
+<select><option>abc</option></select>
+<textarea></textarea>
+<summary id="summary-out"></summary>
+<details open><summary id="summary-first"></summary><summary id="summary-second"></summary></details>
+<div contenteditable="true"></div>
+<iframe></iframe>
+<svg><a id="svg-a"></a></svg>
+<svg><text id="svg-text"></text></svg>
+<img>
+</div>
+<script>
+setup({ explicit_done: true });
+window.addEventListener("load", runTests);
+
+function runTests() {
+ const defaultList = [
+ ['a', false],
+ ['a[href]', true],
+ ['button', true],
+ ['input[type="hidden"]', false],
+ ['input[type="button"]', true],
+ ['select', true],
+ ['textarea', true],
+ ['#summary-out', false],
+ ['#summary-first', true],
+ ['#summary-second', false],
+ ['[contenteditable]', true],
+ ['iframe', true],
+ ['#svg-a', false],
+ ['#svg-text', false],
+ ['img', false],
+ ];
+ for (entry of defaultList) {
+ test(() => {
+ var element = document.querySelector('#default-samples ' + entry[0]);
+ element.focus();
+ if (entry[1])
+ assert_equals(document.activeElement, element);
+ else
+ assert_not_equals(document.activeElement, element);
+ }, entry[0] + ' should ' + (entry[1] ? '' : 'not ') + 'be focusable by default.');
+ }
+
+ runTests_tabindex0();
+}
+</script>
+
+<div id="tabindex-0">
+<a tabindex="0"></a>
+<svg><a tabindex="0"></a></svg>
+<svg><text tabindex="0"></text></svg>
+<summary tabindex="0" id="summary-out-tabindex0"></summary>
+<details open><summary id="summary-first"></summary><summary tabindex="0" id="summary-second-tabindex0"></summary></details>
+<img tabindex="0">
+</div>
+<script>
+function runTests_tabindex0() {
+ for (element of document.querySelectorAll('#tabindex-0 [tabindex]')) {
+ var elementDesc = element.tagName;
+ if (element.id)
+ elementDesc += '#' + element.id;
+ test(() => {
+ element.focus();
+ assert_equals(document.activeElement, element);
+ }, elementDesc + ' with tabindex=0 should be focusable.');
+ }
+
+ runTests_tabindex_negative();
+}
+</script>
+
+<div id="tabindex-negative">
+<a tabindex="-1"></a>
+<svg><a tabindex="-1"></a></svg>
+<svg><text tabindex="-1"></text></svg>
+<summary tabindex="-1" id="summary-out-tabindex-negative"></summary>
+<details open><summary id="summary-first"></summary><summary tabindex="0" id="summary-second-tabindex-negative"></summary></details>
+<img tabindex="-1">
+</div>
+<script>
+function runTests_tabindex_negative() {
+ for (element of document.querySelectorAll('#tabindex-negative [tabindex]')) {
+ var elementDesc = element.tagName;
+ if (element.id)
+ elementDesc += '#' + element.id;
+ test(() => {
+ element.focus();
+ assert_equals(document.activeElement, element);
+ }, elementDesc + ' with tabindex=-1 should be focusable.');
+ }
+
+ runTests_tabindex_invalid();
+}
+</script>
+
+<div id="tabindex-invalid">
+<a tabindex="invalid"></a>
+<a href="#" tabindex="invalid" id="with-href" data-focusable=true></a>
+<svg><a tabindex="invalid"></a></svg>
+<svg><a href="#" tabindex="invalid" id="with-href" data-focusable=true></a></svg>
+<svg><text tabindex="invalid"></text></svg>
+<div tabindex="invalid"></div>
+<summary tabindex="invalid" id="summary-out-tabindex-invalid"></summary>
+<img tabindex="invalid">
+</div>
+<script>
+function runTests_tabindex_invalid() {
+ for (element of document.querySelectorAll('#tabindex-invalid [tabindex]')) {
+ var focusable = element.dataset && element.dataset.focusable;
+ var elementDesc = element.tagName;
+ if (element.id)
+ elementDesc += '#' + element.id;
+ test(() => {
+ element.focus();
+ focusable ? assert_equals(document.activeElement, element)
+ : assert_not_equals(document.activeElement, element);
+ }, `${elementDesc} with tabindex=invalid should ${focusable ? "be" : "not be"} focusable.`);
+ }
+
+ done();
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-area.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-area.html
new file mode 100644
index 0000000000..e1ff72eb61
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-area.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute">
+<link rel='author' href='mailto:sefeng@mozilla.com'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<map name="mymap">
+ <area id="myarea" shape="circle" coords="100,100,80" href="#" autofocus>
+</map>
+<img style="width:200px; height: 200px"
+ usemap="#mymap"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mP4%2F58BAAT%2FAf9jgNErAAAAAElFTkSuQmCC">
+<script>
+promise_test(async t => {
+ await waitForLoad(window);
+ await waitUntilStableAutofocusState(window);
+ const area = document.querySelector("area");
+ assert_equals(document.activeElement, area);
+}, 'autofocus works on area element');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html
new file mode 100644
index 0000000000..08a0913f42
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute">
+<link rel='author' href='mailto:masonf@chromium.org'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<body>
+<script>
+promise_test(async t => {
+ let w = window.open('/common/blank.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ w.document.body.innerHTML = '<dialog><div tabindex=0 autofocus></dialog><input autofocus>';
+ await waitUntilStableAutofocusState(w);
+ assert_equals(w.document.activeElement.tagName, 'INPUT');
+}, '<dialog> can contain autofocus, without stopping page autofocus content from working');
+
+promise_test(async t => {
+ let w = window.open('/common/blank.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ w.document.body.innerHTML = '<dialog><div tabindex=0 autofocus></dialog><input autofocus>';
+ await waitUntilStableAutofocusState(w);
+ w.document.querySelector('dialog').show();
+ assert_equals(w.document.activeElement.tagName, 'DIV');
+}, '<dialog>-contained autofocus element gets focused when the dialog is shown');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html
new file mode 100644
index 0000000000..a26a44dbfb
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ let doc = document.cloneNode(false);
+ doc.appendChild(doc.createElement('html'))
+ doc.firstChild.innerHTML = '<body><input autofocus/></body>';
+ await waitUntilStableAutofocusState();
+ assert_equals(doc.activeElement, doc.body);
+}, 'Autofocus element in not-fully-active document should not be queued.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html
new file mode 100644
index 0000000000..47e3e3fd0a
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<body>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await waitForLoad(window);
+ await timeOut(t, 1000);
+ let element = document.createElement('input');
+ element.autofocus = true;
+ document.body.appendChild(element);
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, element);
+}, 'Autofocus should work if an element with autofocus is inserted into a ' +
+ 'document which was loaded some time ago.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html
new file mode 100644
index 0000000000..ec9d16d498
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<iframe src="resources/frame-with-autofocus-element.html#"></iframe>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ await waitForLoad(window);
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, document.querySelector('iframe'),
+ 'Autofocus elements in iframes should be focused.');
+
+ let input = document.createElement('input');
+ input.autofocus = true;
+ document.body.appendChild(input);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(document.activeElement, input);
+}, 'Autofocus elements in iframed documents with empty fragments should work.');
+
+promise_test(async () => {
+ let w = window.open('resources/frame-with-autofocus-element.html#');
+ await waitForLoad(w);
+ await waitUntilStableAutofocusState(w);
+ assert_not_equals(w.document.activeElement, w.document.body);
+ w.close();
+}, 'Autofocus elements in top-level browsing context\'s documents with empty fragments should work.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-nonexistent.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-nonexistent.html
new file mode 100644
index 0000000000..3f2dcc4167
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-nonexistent.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<iframe src="resources/frame-with-autofocus-element.html#non-existent"></iframe>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ await waitForLoad(window);
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, document.querySelector('iframe'),
+ 'Autofocus elements in iframes should be focused.');
+
+ let input = document.createElement('input');
+ input.autofocus = true;
+ document.body.appendChild(input);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(document.activeElement, input);
+}, 'Autofocus elements in iframed documents with non-existent fragments should work.');
+
+promise_test(async () => {
+ let w = window.open('resources/frame-with-autofocus-element.html#non-existent');
+ await waitForLoad(w);
+ await waitUntilStableAutofocusState(w);
+ assert_not_equals(w.document.activeElement, w.document.body);
+ w.close();
+}, 'Autofocus elements in top-level browsing context\'s documents with non-existent fragments should work.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html
new file mode 100644
index 0000000000..f2d2aaae60
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<iframe src="resources/frame-with-autofocus-element.html#top"></iframe>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ await waitForLoad(window);
+ const iframe = document.querySelector('iframe');
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, iframe,
+ 'Autofocus elements in iframes should be focused.');
+ const doc = iframe.contentDocument;
+ assert_true(!doc.querySelector(':target'));
+
+ let input = document.createElement('input');
+ input.autofocus = true;
+ document.body.appendChild(input);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(document.activeElement, input);
+}, 'Autofocus elements in iframed documents with "top" fragments should work.');
+
+promise_test(async () => {
+ let w = window.open('resources/frame-with-autofocus-element.html#top');
+ await waitForLoad(w);
+ await waitUntilStableAutofocusState(w);
+ assert_not_equals(w.document.activeElement, w.document.body);
+ w.close();
+}, 'Autofocus elements in top-level browsing context\'s documents with "top" fragments should work.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-valid.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-valid.html
new file mode 100644
index 0000000000..5078f1a0ac
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-valid.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<iframe src="resources/frame-with-anchor.html"></iframe>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ await waitForLoad(window);
+ const iframe = document.querySelector('iframe');
+ iframe.contentWindow.location.hash = 'anchor1';
+ await waitForEvent(iframe.contentWindow, 'hashchange');
+ const doc = iframe.contentDocument;
+ assert_true(!!doc.querySelector(':target'));
+
+ let input = doc.createElement('input');
+ input.autofocus = true;
+ doc.body.appendChild(input);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(doc.activeElement, input);
+ iframe.remove();
+}, 'Autofocus elements in iframed documents with URL fragments should be skipped. (id matches)');
+
+promise_test(async () => {
+ let iframe = await waitForIframeLoad("resources/frame-with-a.html");
+ iframe.contentWindow.location.hash = 'anchor1';
+ await waitForEvent(iframe.contentWindow, 'hashchange');
+ const doc = iframe.contentDocument;
+ assert_true(!!doc.querySelector(':target'));
+
+ let input = doc.createElement('input');
+ input.autofocus = true;
+ doc.body.appendChild(input);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(doc.activeElement, input);
+ iframe.remove();
+}, 'Autofocus elements in iframed documents with URL fragments should be skipped.(a element)');
+
+promise_test(async () => {
+ let w = window.open('resources/frame-with-anchor.html');
+ await waitForLoad(w);
+ w.location.hash = 'anchor1';
+ await waitForEvent(w, 'hashchange');
+ const doc = w.document;
+ assert_true(!!doc.querySelector(':target'));
+
+ let input = doc.createElement('input');
+ input.autofocus = true;
+ doc.body.appendChild(input);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(doc.activeElement, input);
+ w.close();
+}, 'Autofocus elements in top-level browsing context\'s documents with URL fragments should be skipped.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-reconnected.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-reconnected.html
new file mode 100644
index 0000000000..99ee9198d1
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-reconnected.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<input autofocus id="i1">
+<input autofocus id="i2">
+<script>
+"use strict";
+
+promise_test(async () => {
+ const input1 = document.querySelector("#i1");
+ const input2 = document.querySelector("#i2");
+ input1.remove();
+ input2.parentNode.insertBefore(input1, input2);
+
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, input2);
+}, 'The second autofocus element wins if the first autofocus element was ' +
+ 'disconnected and reconnected before flushing the autofocus candidates.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html
new file mode 100644
index 0000000000..f361463401
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The temporally first autofocus in the document wins, even if an element is inserted later that is previous in the document tree</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#autofocusing-a-form-control:-the-autofocus-attribute">
+<link rel="author" title="Domenic Denicola" href="d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<input autofocus>
+
+<script>
+"use strict";
+
+promise_test(async () => {
+ const input1 = document.querySelector("input");
+ const input2 = document.createElement("input");
+ input2.autofocus = true;
+ document.body.prepend(input2);
+
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, input1);
+ assert_not_equals(document.activeElement, input2);
+}, 'The temporally first autofocus in the document wins, even if an element is inserted later that is previous in the document tree.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later.html
new file mode 100644
index 0000000000..1d64b863a1
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first-when-later.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The first autofocus in the document wins, even if elements are inserted later</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#autofocusing-a-form-control:-the-autofocus-attribute">
+<link rel="author" title="Domenic Denicola" href="d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<input autofocus>
+
+<script>
+"use strict";
+
+promise_test(async () => {
+ const input1 = document.querySelector("input");
+ const input2 = document.createElement("input");
+ input2.autofocus = true;
+ document.body.appendChild(input2);
+
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, input1);
+ assert_not_equals(document.activeElement, input2);
+}, 'The first autofocus in the document wins, even if elements are inserted later.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first.html
new file mode 100644
index 0000000000..02ebb79a3e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/first.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The first autofocus in the document wins</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#autofocusing-a-form-control:-the-autofocus-attribute">
+<link rel="author" title="Domenic Denicola" href="d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<input autofocus>
+<input autofocus>
+
+<script>
+"use strict";
+
+promise_test(async () => {
+ const [input1, input2] = document.querySelectorAll("input");
+
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, input1);
+ assert_not_equals(document.activeElement, input2);
+}, 'The first autofocus element in the document should win.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html
new file mode 100644
index 0000000000..327040eeee
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<iframe srcdoc="<input><script>document.querySelector('input').focus();</script>"></iframe>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ await waitForLoad(window);
+ let iframe = document.querySelector('iframe');
+ assert_equals(document.activeElement, iframe, 'Prereq: IFRAME should be focused');
+
+ let input = document.createElement('input');
+ input.autofocus = true;
+ document.body.appendChild(input);
+
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, iframe, 'activeElement should not be changed');
+ assert_not_equals(document.activeElement, input);
+}, 'If topDocument\'s focused area is not topDocument, autofocus is not processed.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-autofocus-on-changing-input-type.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-autofocus-on-changing-input-type.html
new file mode 100644
index 0000000000..79a39ef2f9
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-autofocus-on-changing-input-type.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+<body>
+
+<input id="input1" autofocus>
+<select><option>o1</option></select>
+
+<script>
+"use strict";
+
+// WebKit had a bug that reattaching RenderObject triggered autofocus again.
+// https://bugs.webkit.org/show_bug.cgi?id=68513
+promise_test(async () => {
+ const input1 = document.querySelector('input');
+ const select = document.querySelector('select');
+
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, input1);
+ input1.onblur = () => { input1.type = 'password'; };
+ select.focus();
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, select);
+}, 'Changing input type should not refocus on the element.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html
new file mode 100644
index 0000000000..2cf7428f36
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+</head>
+<body>
+ <h1>Autofocus shouldn't work in cross-origin iframe.</h1>
+ <iframe id="child" width="200" height="100"></iframe>
+
+ <script>
+ let parent_loaded = false;
+ let child_loaded = false;
+
+ async_test(function(t) {
+ function pingChildIfBothFramesLoaded() {
+ if (parent_loaded && child_loaded)
+ frames[0].postMessage("report_focus_state", "*");
+ }
+
+ window.addEventListener("load", t.step_func(event => {
+ parent_loaded = true;
+ pingChildIfBothFramesLoaded();
+ }));
+
+ window.addEventListener("message", t.step_func(event => {
+ if (event.data == "child_loaded") {
+ child_loaded = true;
+ pingChildIfBothFramesLoaded();
+ } else if (event.data == "child_is_focused") {
+ assert_unreached("The iframe shouldn't get focus");
+ } else if (event.data == "child_is_not_focused") {
+ t.done();
+ }
+ }));
+ document.getElementById("child").src =
+ get_host_info().HTTP_REMOTE_ORIGIN + "/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html";
+ }, "Autofocus shouldn't work in cross-origin iframe");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html
new file mode 100644
index 0000000000..991373d336
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<iframe sandbox srcdoc="<input autofocus>"></iframe>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ await waitForLoad(window);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(document.activeElement, document.querySelector('iframe'));
+}, 'If the sandboxed automatic features browsing context flag is set, ' +
+ 'autofocus in the browsing context should not be handled.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/not-on-first-task.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/not-on-first-task.html
new file mode 100644
index 0000000000..ab27342f4f
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/not-on-first-task.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The element is not focused during the initial parsing task</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#autofocusing-a-form-control:-the-autofocus-attribute">
+<link rel="author" title="Domenic Denicola" href="d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<input autofocus>
+<input autofocus>
+
+<script>
+"use strict";
+
+test(() => {
+ const input = document.querySelector("input");
+
+ assert_equals(document.activeElement, document.body);
+ assert_not_equals(document.activeElement, input);
+});
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html
new file mode 100644
index 0000000000..e3b556035d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<textarea autofocus disabled></textarea>
+<select autofocus></select>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ const [textarea, select] = document.querySelectorAll('[autofocus]');
+ textarea.disabled = false;
+
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, textarea);
+ assert_not_equals(document.activeElement, select);
+}, 'If the first autofocus element is not focusable, but becomes focusable before a frame, it should be focused.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html
new file mode 100644
index 0000000000..afd5601a52
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<input id="target" value="This should be unfocused!" autofocus></input>
+
+<script>
+ let got_focus = false;
+ document.getElementById("target").addEventListener("focus", () => {
+ got_focus = true;
+ });
+
+ window.addEventListener("load", () => {
+ parent.postMessage("child_loaded", "*");
+ });
+
+ window.addEventListener("message", event => {
+ if (event.data == "report_focus_state") {
+ let msg = got_focus ? "child_is_focused" : "child_is_not_focused";
+ parent.postMessage(msg, "*");
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html
new file mode 100644
index 0000000000..f60acfc871
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script src="/common/get-host-info.sub.js"></script>
+<iframe id="iframe" width="200" height="100"></iframe>
+
+<script>
+ iframe.src =
+ get_host_info().ORIGIN + "/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html";
+ window.addEventListener("message", event => {
+ if (event.data == "grand_child_loaded") {
+ parent.postMessage("ready", "*");
+ } else if (event.data == "report_focus_state") {
+ frames[0].postMessage("report_focus_state", "*");
+ } else if (event.data == "grand_child_is_focused" ||
+ event.data == "grand_child_is_not_focused") {
+ parent.postMessage(event.data, "*");
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/erase-first.css b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/erase-first.css
new file mode 100644
index 0000000000..bbbcf79939
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/erase-first.css
@@ -0,0 +1,3 @@
+#first {
+ display: none;
+}
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-a.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-a.html
new file mode 100644
index 0000000000..1f31736902
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-a.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+<a name="anchor1"></a>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-anchor.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-anchor.html
new file mode 100644
index 0000000000..b9070159e7
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-anchor.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+<div id="anchor1"></div>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html
new file mode 100644
index 0000000000..985cba4149
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<body>
+<div id="anchor1"></div>
+<input autofocus>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html
new file mode 100644
index 0000000000..88be6e0b04
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<input id="target" value="This should be focused!" autofocus></input>
+
+<script>
+ let got_focus = false;
+ target.addEventListener("focus", () => got_focus = true);
+
+ window.addEventListener("load", () => {
+ parent.postMessage("grand_child_loaded", "*");
+ });
+
+ window.addEventListener("message", event => {
+ if (event.data == "report_focus_state") {
+ let msg = got_focus ? "grand_child_is_focused" : "grand_child_is_not_focused";
+ parent.postMessage(msg, "*");
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/imagemap.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/imagemap.html
new file mode 100644
index 0000000000..fa3d95c81b
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/imagemap.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<body>
+<img src="/media/poster.png" usemap="#map">
+<map name="map"></map>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html
new file mode 100644
index 0000000000..fc6c298a46
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<body>
+<script>
+const input = document.createElement('input');
+input.autofocus = true;
+document.body.appendChild(input);
+input.autofocus = false;
+window.opener.document.body.appendChild(input);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/utils.js b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/utils.js
new file mode 100644
index 0000000000..e928e27540
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/utils.js
@@ -0,0 +1,51 @@
+'use strict';
+
+function waitForEvent(target, type, options) {
+ return new Promise((resolve, reject) => {
+ target.addEventListener(type, resolve, options);
+ });
+}
+
+function waitForAnimationFrame(w) {
+ let targetWindow = w || window;
+ return new Promise((resolve, reject) => {
+ targetWindow.requestAnimationFrame(resolve);
+ });
+}
+
+function waitForEvent(target, type, options) {
+ return new Promise((resolve, reject) => {
+ target.addEventListener(type, resolve, options);
+ });
+}
+
+function waitForLoad(target) {
+ return waitForEvent(target, 'load');
+}
+
+function timeOut(test, ms) {
+ return new Promise((resolve, reject) => {
+ test.step_timeout(resolve, ms);
+ });
+}
+
+// If an element with autofocus is connected to a document and this function
+// is called, the autofocus result is deterministic after returning from the
+// function.
+// Exception: If the document has script-blocking style sheets, this function
+// doesn't work well.
+async function waitUntilStableAutofocusState(w) {
+ let targetWindow = w || window;
+ // Awaiting one animation frame is an easy way to determine autofocus state.
+ await waitForAnimationFrame(targetWindow);
+}
+
+async function waitForIframeLoad(src, w = window) {
+ const iframe = w.document.createElement("iframe");
+ let loadPromise = new Promise(resolve => {
+ iframe.addEventListener("load", () => resolve(iframe));
+ });
+ iframe.src = src;
+ w.document.body.appendChild(iframe);
+ return loadPromise;
+}
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html
new file mode 100644
index 0000000000..1497a7d658
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="assert" content="`autofocus` should not work in the same origin iframe if there is a cross-origin iframe between the parent and the same origin iframe">
+<title>autofocus in the same origin grand child iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/utils.js"></script>
+</head>
+<body>
+ <iframe id="child" width="200" height="100"></iframe>
+ <script>
+ let parent_loaded = false;
+ let grand_child_loaded = false;
+
+ async_test(function(t) {
+ async function pingChildIfBothFramesLoaded() {
+ if (parent_loaded && grand_child_loaded) {
+ await waitUntilStableAutofocusState();
+ frames[0].postMessage("report_focus_state", "*");
+ }
+ }
+
+ window.addEventListener("load", t.step_func(event => {
+ parent_loaded = true;
+ pingChildIfBothFramesLoaded();
+ }));
+
+ window.addEventListener("message", t.step_func(event => {
+ if (event.data == "ready") {
+ grand_child_loaded = true;
+ pingChildIfBothFramesLoaded();
+ } else if (event.data == "grand_child_is_focused") {
+ assert_unreached("The grandchild iframe shouldn't get focus");
+ } else if (event.data == "grand_child_is_not_focused") {
+ t.done();
+ }
+ }));
+ document.getElementById("child").src =
+ get_host_info().HTTP_NOTSAMESITE_ORIGIN + "/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html";
+ }, "Autofocus should not work in the same origin grand child iframe");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html
new file mode 100644
index 0000000000..d392b903f0
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+<script>
+'use strict';
+
+promise_test(async () => {
+ let w = window.open('resources/moving-autofocus-to-parent.html');
+ await waitForLoad(w);
+ await waitUntilStableAutofocusState(w);
+ assert_equals(w.document.activeElement, w.document.body);
+ assert_equals(document.activeElement, document.body);
+ w.close();
+}, 'Autofocus elements queued in another top-level browsing context\'s ' +
+ 'documents should be skipped.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html
new file mode 100644
index 0000000000..008371d8e1
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<textarea autofocus disabled></textarea>
+<select autofocus></select>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ const [textarea, select] = document.querySelectorAll('[autofocus]');
+
+ await waitUntilStableAutofocusState();
+ assert_not_equals(document.activeElement, textarea);
+ assert_equals(document.activeElement, select);
+}, 'Non-focusable autofocus element is skipped.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html
new file mode 100644
index 0000000000..fa5b608d05
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<iframe srcdoc="<input autofocus><script>window.frameElement.remove();</script>"></iframe>
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ let iframe = document.querySelector('iframe');
+ let iframeDocument = iframe.contentDocument;
+ await waitForLoad(window);
+ assert_not_equals(document.activeElement, iframe);
+ assert_equals(iframeDocument.activeElement, iframeDocument.body);
+}, 'Autofocus element in not-fully-active document should be skipped while flusing.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html
new file mode 100644
index 0000000000..fb87eca77a
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+
+<input id="first" autofocus>
+<input id="second" autofocus>
+
+<link rel="stylesheet" href="resources/erase-first.css?pipe=trickle(d1)">
+
+<script>
+'use strict';
+
+promise_test(async () => {
+ await waitForLoad(window);
+ await waitForAnimationFrame();
+ assert_equals(document.activeElement.id, 'second');
+}, 'Script-blocking style sheet should pause flushing autofocus candidates.');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/supported-elements.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/supported-elements.html
new file mode 100644
index 0000000000..29b3f3fb7b
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/supported-elements.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/utils.js"></script>
+<script>
+"use strict";
+
+promise_test(async t => {
+ let w = window.open('/common/blank.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ w.document.body.innerHTML = '<div contenteditable=true autofocus></div>';
+ await waitUntilStableAutofocusState(w);
+ assert_equals(w.document.activeElement.tagName, 'DIV');
+}, 'Contenteditable element should support autofocus');
+
+promise_test(async t => {
+ let w = window.open('/common/blank.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ w.document.body.innerHTML = '<span tabindex=0 autofocus></span>';
+ await waitUntilStableAutofocusState(w);
+ assert_equals(w.document.activeElement.tagName, 'SPAN');
+}, 'Element with tabindex should support autofocus');
+
+promise_test(async t => {
+ let w = window.open('/common/blank.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ let element = w.document.createElementNS('uri1', 'prefix:local');
+ element.setAttribute('autofocus', '');
+ w.document.body.appendChild(element);
+ await waitUntilStableAutofocusState(w);
+ assert_equals(w.document.activeElement.tagName, 'BODY');
+}, 'Non-HTMLElement should not support autofocus');
+
+promise_test(async t => {
+ let w = window.open('/common/blank.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ const host = w.document.createElement('div');
+ host.autofocus = true;
+ const shadow = host.attachShadow({mode:'closed', delegatesFocus:true});
+ shadow.appendChild(w.document.createElement('input'));
+ w.document.body.appendChild(host);
+ await waitUntilStableAutofocusState(w);
+ assert_equals(w.document.activeElement, host);
+ assert_equals(shadow.activeElement.tagName, 'INPUT');
+}, 'Host element with delegatesFocus should support autofocus');
+
+promise_test(async t => {
+ let w = window.open('/common/blank.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ const host = w.document.createElement('div');
+ host.autofocus = true;
+ host.attachShadow({mode:'closed', delegatesFocus:true});
+ w.document.body.appendChild(host);
+ const next = w.document.createElement('input');
+ next.autofocus = true;
+ w.document.body.appendChild(next);
+ await waitUntilStableAutofocusState(w);
+ assert_equals(w.document.activeElement, next);
+}, 'Host element with delegatesFocus including no focusable descendants should be skipped');
+
+promise_test(async t => {
+ let w = window.open('./resources/imagemap.html');
+ await waitForLoad(w);
+ t.add_cleanup(() => { w.close(); });
+ const area = w.document.createElement('area');
+ area.autofocus = true;
+ area.shape = 'rect';
+ area.coords = '1,1,99,99';
+ area.href = '/common/blank.html';
+ w.document.querySelector('map').appendChild(area);
+ await waitUntilStableAutofocusState(w);
+ // According to the specification, DOM anchor for an AREA shape is an IMG
+ // element, but major browsers don't follow it.
+ // See https://github.com/whatwg/html/issues/5054
+ assert_equals(w.document.activeElement, area);
+}, 'Area element should support autofocus');
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html
new file mode 100644
index 0000000000..279f70d490
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/C/#update-the-rendering">
+
+<body>
+<script>
+'use strict';
+
+async_test(t => {
+ t.events = [];
+
+ let w = window.open('/common/blank.html', 'name',
+ 'width=100,height=100,menubar=no,toolbar=no,location=no');
+ t.add_cleanup(() => { w.close(); });
+ w.addEventListener('load', t.step_func(() => {
+ w.focus();
+ let element = w.document.createElement('input');
+ element.autofocus = true;
+ element.style.marginTop = '200px'; // Setting focus causes scrolling.
+ element.addEventListener('focus', t.step_func(() => {
+ t.events.push('autofocus');
+ }));
+
+ w.addEventListener('scroll', t.step_func(() => {
+ t.events.push('scroll');
+ }));
+
+ w.requestAnimationFrame(
+ () => w.requestAnimationFrame(t.step_func_done(() => {
+ t.events.push('animationFrame');
+ assert_array_equals(t.events, ['autofocus', 'scroll', 'animationFrame'], t.events);
+ })));
+
+ w.document.body.appendChild(element);
+ }));
+}, '"Flush autofocus candidates" should be happen before a scroll event and ' +
+ 'animation frame callbacks');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/links/icon/no-error-event.sub.html b/testing/web-platform/tests/html/links/icon/no-error-event.sub.html
new file mode 100644
index 0000000000..128b5d2d3c
--- /dev/null
+++ b/testing/web-platform/tests/html/links/icon/no-error-event.sub.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>&lt;link rel="icon"&gt; doesn't fire an error event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ async_test((t) => {
+ const link = document.createElement("link");
+ link.rel = "icon";
+ link.href = "http://{{hosts[][nonexistent]}}";
+
+ link.addEventListener("error", t.unreached_func());
+ document.head.append(link);
+ t.step_timeout(() => t.done(), 2000);
+ }, document.title);
+</script>
diff --git a/testing/web-platform/tests/html/links/icon/no-load-event.html b/testing/web-platform/tests/html/links/icon/no-load-event.html
new file mode 100644
index 0000000000..f81399a230
--- /dev/null
+++ b/testing/web-platform/tests/html/links/icon/no-load-event.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>&lt;link rel="icon"&gt; doesn't fire a load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ async_test((t) => {
+ const link = document.createElement("link");
+ link.rel = "icon";
+ link.href = "/images/wpt-logo/wpt-logo-darkblue-bg.svg";
+
+ link.addEventListener("load", t.unreached_func());
+ document.head.append(link);
+ t.step_timeout(() => t.done(), 2000);
+ }, document.title);
+</script>
diff --git a/testing/web-platform/tests/html/links/manifest/document-not-attached-manual.html b/testing/web-platform/tests/html/links/manifest/document-not-attached-manual.html
new file mode 100644
index 0000000000..313fb2b96b
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/document-not-attached-manual.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>
+ Manifest attached to document without a browsing context
+</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<script>
+ // Create an orphan document, and make sure it doesn't get used
+ const doc = document.implementation.createHTMLDocument("Orphan document");
+ const link = doc.createElement("link");
+ link.rel = "manifest";
+ link.href = "/appmanifest/name-member/name-member-fail.webmanifest";
+ doc.head.append(link);
+</script>
+<h1>Manifest attached to document that does not have a browsing context</h1>
+<p>
+ To pass, the user agent must not use the manifest in the unattached document.
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/link-relationship/link-rel-manifest.html b/testing/web-platform/tests/html/links/manifest/link-relationship/link-rel-manifest.html
new file mode 100644
index 0000000000..2d9fd78948
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/link-relationship/link-rel-manifest.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>
+ Test that "manifest" is a supported value for the `rel` of a `link`
+</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(() => {
+ const result = document.createElement("link").relList.supports("manifest");
+ assert_true(
+ result,
+ "Expected true if manifest is supported as a link relationship"
+ );
+ }, 'link element supports a rel value of "manifest".');
+</script>
diff --git a/testing/web-platform/tests/html/links/manifest/link-relationship/link-tree-order-manual.html b/testing/web-platform/tests/html/links/manifest/link-relationship/link-tree-order-manual.html
new file mode 100644
index 0000000000..aa9ec9c12d
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/link-relationship/link-tree-order-manual.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>Test that name member is supported</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<link rel="manifesto" href="/appmanifest/name-member/name-member-fail.webmanifest" />
+<link
+ rel="hello manifest another-relationship"
+ href="/appmanifest/name-member/name-member.webmanifest"
+/>
+<link rel="manifest" href="/appmanifest/name-member/name-member-fail.webmanifest" />
+<link rel="hello manifest" href="/appmanifest/name-member/name-member-fail.webmanifest" />
+<p>
+ If when installing the name is "pass" then the test has passed.
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-application-json-manual.html b/testing/web-platform/tests/html/links/manifest/mime-type-application-json-manual.html
new file mode 100644
index 0000000000..5d1a498704
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-application-json-manual.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Test JSON MIME Type support (application/json)</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<link rel="manifest" href="mime-type-application-json.webmanifest" />
+<h1>Test JSON MIME Type support for web manifest</h1>
+<p>
+ To pass, the use agent must treat the manifest valid (name is "pass"). The
+ response's Content-Type metadata is a JSON MIME type "application/json".
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest b/testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest
new file mode 100644
index 0000000000..c51d155a24
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest
@@ -0,0 +1,3 @@
+{
+ "name": "pass"
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest.headers b/testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest.headers
new file mode 100644
index 0000000000..75f8856556
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-application-json.webmanifest.headers
@@ -0,0 +1 @@
+Content-Type: application/json; charset=utf-8 \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json-manual.html b/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json-manual.html
new file mode 100644
index 0000000000..f41c4fdab0
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json-manual.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Test JSON MIME Type support (application/manifest+json)</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<link rel="manifest" href="mime-type-application-manifest+json.webmanifest" />
+<h1>Test JSON MIME Type support for web manifest</h1>
+<p>
+ To pass, the use agent must treat the manifest valid (name is "pass"). The
+ response's Content-Type metadata is a JSON MIME type
+ "application/manifest+json".
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest b/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest
new file mode 100644
index 0000000000..c51d155a24
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest
@@ -0,0 +1,3 @@
+{
+ "name": "pass"
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest.headers b/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest.headers
new file mode 100644
index 0000000000..23f36ea27c
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-application-manifest+json.webmanifest.headers
@@ -0,0 +1 @@
+Content-Type: application/manifest+json; charset=utf-8 \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-invalid-manual.html b/testing/web-platform/tests/html/links/manifest/mime-type-invalid-manual.html
new file mode 100644
index 0000000000..45c2000a45
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-invalid-manual.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Test JSON MIME Type support (application/json)</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<link rel="manifest" href="mime-type-invalid.webmanifest" />
+<h1>Test JSON MIME Type support for web manifest</h1>
+<p>
+ To pass, the use agent must treat the manifest as invalid. The
+ response's Content-Type metadata is an invalid mime type.
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest b/testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest
new file mode 100644
index 0000000000..66051f078a
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest
@@ -0,0 +1,3 @@
+{
+ "name": "fail - invalid MIME type"
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest.headers b/testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest.headers
new file mode 100644
index 0000000000..ac90386634
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-invalid.webmanifest.headers
@@ -0,0 +1 @@
+Content-Type: ׺°â€Ëœ`â€Â°ÂºÃ— INVALID MIME TYPE ׺°â€Ëœ`â€Â°ÂºÃ— \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-none-manual.html b/testing/web-platform/tests/html/links/manifest/mime-type-none-manual.html
new file mode 100644
index 0000000000..8f934155e1
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-none-manual.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Test JSON MIME Type support (application/json)</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<link rel="manifest" href="mime-type-invalid.webmanifest" />
+<h1>Test JSON MIME Type support for web manifest</h1>
+<p>
+ To pass, the use agent must treat the manifest as invalid. The
+ response's does not contain any Content-Type HTTP header.
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest b/testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest
new file mode 100644
index 0000000000..cae1d82a32
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest
@@ -0,0 +1,3 @@
+{
+ "name": "fail - no Content-Type HTTP header"
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest.headers b/testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest.headers
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-none.webmanifest.headers
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-text-json-manual.html b/testing/web-platform/tests/html/links/manifest/mime-type-text-json-manual.html
new file mode 100644
index 0000000000..4597d6ddbd
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-text-json-manual.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Test JSON MIME Type support (text/json)</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<link rel="manifest" href="mime-type-text-json.webmanifest" />
+<h1>Test JSON MIME Type support for web manifest</h1>
+<p>
+ To pass, the use agent must treat the manifest valid (name is "pass"). The
+ response's Content-Type metadata is a JSON MIME type "text/json".
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest b/testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest
new file mode 100644
index 0000000000..c51d155a24
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest
@@ -0,0 +1,3 @@
+{
+ "name": "pass"
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest.headers b/testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest.headers
new file mode 100644
index 0000000000..3f4f83f922
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/mime-type-text-json.webmanifest.headers
@@ -0,0 +1 @@
+Content-Type: text/json; charset=utf-8 \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/no-manifest-from-iframe-manual.html b/testing/web-platform/tests/html/links/manifest/no-manifest-from-iframe-manual.html
new file mode 100644
index 0000000000..7b52e48731
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/no-manifest-from-iframe-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>
+ Don't install manifests that are not top-level from browsing contexts
+</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<h1>Don't install manifests that are not top-level from browsing contexts</h1>
+<p>
+ To pass, the user agent must not use the manifest in iframe. The user agent
+ must behave as if there is no manifest present.
+</p>
+<script>
+ const iframe = document.createElement("iframe");
+ iframe.srcdoc = "<h1>hi</h1>";
+ document.body.append(iframe);
+ iframe.onload = () => {
+ const link = iframe.contentDocument.createElement("link");
+ link.rel = "manifest";
+ link.href = "/appmanifest/name-member/name-member-fail.webmanifest";
+ iframe.contentDocument.head.append(link);
+ };
+</script>
diff --git a/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain-manual.html b/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain-manual.html
new file mode 100644
index 0000000000..fdf5abf099
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain-manual.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Test JSON MIME Type support (text/plain)</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-manifest" />
+<link rel="manifest" href="wrong-mime-type-text-plain.webmanifest.webmanifest" />
+<h1>Test JSON MIME Type support for web manifest</h1>
+<p>
+ To pass, the use agent must treat the manifest as not present. The response's
+ Content-Type metadata "text/plain" is <strong>NOT</strong> a JSON MIME type.
+</p>
diff --git a/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest b/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest
new file mode 100644
index 0000000000..eac6871328
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest
@@ -0,0 +1,3 @@
+{
+ "name": "fail - MIME type is text/plain"
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest.headers b/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest.headers
new file mode 100644
index 0000000000..80fbe8db6c
--- /dev/null
+++ b/testing/web-platform/tests/html/links/manifest/wrong-mime-type-text-plain.webmanifest.headers
@@ -0,0 +1 @@
+Content-Type: text/plain; charset=utf-8 \ No newline at end of file
diff --git a/testing/web-platform/tests/html/links/stylesheet/quirk-origin-check-recursive-import.html b/testing/web-platform/tests/html/links/stylesheet/quirk-origin-check-recursive-import.html
new file mode 100644
index 0000000000..c0053f1f29
--- /dev/null
+++ b/testing/web-platform/tests/html/links/stylesheet/quirk-origin-check-recursive-import.html
@@ -0,0 +1,15 @@
+<!-- quirks -->
+<title>Origin check for stylesheet with non-CSS MIME type quirk: recursive @import</title>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1477672">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1850965">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ single_test: true });
+let errors = 0;
+onload = () => {
+ assert_equals(errors, 1);
+ done();
+};
+</script>
+<link rel="stylesheet" href="data:/,@import url('x/');" onerror="errors++">
diff --git a/testing/web-platform/tests/html/links/stylesheet/quirk-origin-check.html b/testing/web-platform/tests/html/links/stylesheet/quirk-origin-check.html
new file mode 100644
index 0000000000..89adcb428f
--- /dev/null
+++ b/testing/web-platform/tests/html/links/stylesheet/quirk-origin-check.html
@@ -0,0 +1,13 @@
+<!-- quirks -->
+<title>Origin check for stylesheet with non-CSS MIME type quirk</title>
+<link rel="stylesheet" href="data:text/plain,.test { background: red }">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p class=test>There should be no red.</p>
+<script>
+setup({ single_test: true });
+onload = () => {
+ assert_equals(getComputedStyle(document.querySelector('.test')).backgroundColor, 'rgba(0, 0, 0, 0)');
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/META.yml b/testing/web-platform/tests/html/obsolete/META.yml
new file mode 100644
index 0000000000..c1dd8dddf9
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - foolip
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html
new file mode 100644
index 0000000000..37a92bb09f
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>document.all</title>
+<link rel="author" title="Corey Farwell" href="mailto:coreyf@rwell.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-all">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function () {
+ assert_false(Boolean(document.all));
+
+ assert_true(document.all == undefined);
+ assert_true(document.all == null);
+ assert_false(document.all != undefined);
+ assert_false(document.all != null);
+
+ assert_true(document.all !== undefined);
+ assert_true(document.all !== null);
+ assert_false(document.all === undefined);
+ assert_false(document.all === null);
+
+ assert_equals(typeof document.all, "undefined");
+
+ if (document.all) { assert_true(false); }
+
+ if (!document.all) {}
+ else { assert_true(false); }
+}, "'unusual behaviors' of document.all")
+
+test(function() {
+ var all = document.all;
+
+ assert_false(Boolean(all));
+
+ assert_true(all == undefined);
+ assert_true(all == null);
+ assert_false(all != undefined);
+ assert_false(all != null);
+
+ assert_true(all !== undefined);
+ assert_true(all !== null);
+ assert_false(all === undefined);
+ assert_false(all === null);
+
+ assert_equals(typeof all, "undefined");
+
+ if (all) { assert_true(false); }
+
+ if (!all) {}
+ else { assert_true(false); }
+}, "'unusual behaviors' of document.all with assignment")
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html
new file mode 100644
index 0000000000..e6f0c2b573
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>document: fg/bg/link/vlink/alink-color</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-fgcolor">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-body-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function setColorAttributes(doc, color) {
+ doc.fgColor = color;
+ doc.bgColor = color;
+ doc.linkColor = color;
+ doc.vlinkColor = color;
+ doc.alinkColor = color;
+}
+
+function checkColorAttributes(doc, expected) {
+ assert_equals(document.fgColor, expected);
+ assert_equals(document.bgColor, expected);
+ assert_equals(document.linkColor, expected);
+ assert_equals(document.vlinkColor, expected);
+ assert_equals(document.alinkColor, expected);
+}
+
+test(function() {
+ setColorAttributes(document, 'green');
+
+ var body = document.documentElement.removeChild(document.body);
+ this.add_cleanup(function() {
+ // Re-add body and reset color attributes.
+ document.body = body;
+ setColorAttributes(document, '');
+ });
+ // When there is no body element, the color attributes return an
+ // empty string upon getting.
+ checkColorAttributes(document, '');
+}, "getting document color attributes with no body");
+
+test(function() {
+ var body = document.documentElement.removeChild(document.body);
+ this.add_cleanup(function() {
+ document.body = body;
+ });
+
+ // When there is no body element, setting the color attributes has no effect.
+ setColorAttributes(document, 'red');
+ checkColorAttributes(document, '');
+}, "setting document color attributes with no body");
+
+function testBogusRootElement(doc) {
+ doc.replaceChild(doc.createElement('test'), doc.documentElement);
+ var new_body = doc.createElement('body');
+ doc.documentElement.appendChild(new_body);
+
+ setColorAttributes(doc, 'red');
+
+ assert_equals(new_body.attributes.length, 0, 'new_body.attributes.length');
+ checkColorAttributes(doc, '');
+}
+
+function createIframeDoc(markup) {
+ var iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ var doc = iframe.contentDocument;
+ doc.open();
+ doc.write(markup);
+ doc.close();
+ return doc;
+}
+
+test(function() {
+ // Use standards mode for doc
+ var doc = createIframeDoc('<!doctype html>');
+ testBogusRootElement(doc);
+}, "document color attributes when the root element is a test element (iframe)");
+
+test(function() {
+ var doc = document.implementation.createHTMLDocument();
+ testBogusRootElement(doc);
+}, "document color attributes when the root element is a test element (createHTMLDocument)");
+
+test(function() {
+ var doc = createIframeDoc('<!doctype html><frameset text=red link=red vlink=red alink=red bgcolor=red>');
+ assert_equals(doc.body.attributes.length, 5, 'attributes.length on the frameset');
+ checkColorAttributes(doc, '');
+}, "getting document color attributes when document.body is a frameset");
+
+test(function() {
+ var doc = createIframeDoc('<!doctype html><frameset>');
+ setColorAttributes(doc, 'red');
+ assert_equals(doc.body.attributes.length, 0, 'attributes.length on the frameset');
+ checkColorAttributes(doc, '');
+}, "setting document color attributes when document.body is a frameset");
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-02.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-02.html
new file mode 100644
index 0000000000..ebf15e79cd
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-02.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<title>document: fg/bg/link/vlink/alink-color</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-fgcolor">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-body-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.fgColor, document.body.text);
+ assert_equals(document.bgColor, document.body.bgColor);
+ assert_equals(document.linkColor, document.body.link);
+ assert_equals(document.vlinkColor, document.body.vLink);
+ assert_equals(document.alinkColor, document.body.aLink);
+})
+test(function() {
+ document.fgColor = null;
+ assert_equals(document.fgColor, "");
+ assert_equals(document.body.text, "");
+ assert_equals(document.body.getAttribute("text"), "");
+})
+test(function() {
+ document.fgColor = "blue";
+ assert_equals(document.fgColor, "blue");
+ assert_equals(document.body.text, "blue");
+ assert_equals(document.body.getAttribute("text"), "blue");
+})
+test(function() {
+ document.bgColor = "green";
+ assert_equals(document.bgColor, "green");
+ assert_equals(document.body.bgColor, "green");
+ assert_equals(document.body.getAttribute("bgcolor"), "green");
+})
+test(function() {
+ document.linkColor = "red";
+ assert_equals(document.linkColor, "red");
+ assert_equals(document.body.link, "red");
+ assert_equals(document.body.getAttribute("link"), "red");
+})
+test(function() {
+ document.vlinkColor = "yellow";
+ assert_equals(document.vlinkColor, "yellow");
+ assert_equals(document.body.vLink, "yellow");
+ assert_equals(document.body.getAttribute("vlink"), "yellow");
+})
+test(function() {
+ document.alinkColor = "silver";
+ assert_equals(document.alinkColor, "silver");
+ assert_equals(document.body.aLink, "silver");
+ assert_equals(document.body.getAttribute("alink"), "silver");
+})
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-03.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-03.html
new file mode 100644
index 0000000000..629c24627c
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-03.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<title>document: fg/bg/link/vlink/alink-color</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-fgcolor">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-body-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.fgColor, document.body.text);
+ assert_equals(document.bgColor, document.body.bgColor);
+ assert_equals(document.linkColor, document.body.link);
+ assert_equals(document.vlinkColor, document.body.vLink);
+ assert_equals(document.alinkColor, document.body.aLink);
+})
+test(function() {
+ document.body.text = null;
+ assert_equals(document.fgColor, "");
+ assert_equals(document.body.text, "");
+ assert_equals(document.body.getAttribute("text"), "");
+})
+test(function() {
+ document.body.text = "blue";
+ assert_equals(document.fgColor, "blue");
+ assert_equals(document.body.text, "blue");
+ assert_equals(document.body.getAttribute("text"), "blue");
+})
+test(function() {
+ document.body.bgColor = "green";
+ assert_equals(document.bgColor, "green");
+ assert_equals(document.body.bgColor, "green");
+ assert_equals(document.body.getAttribute("bgcolor"), "green");
+})
+test(function() {
+ document.body.link = "red";
+ assert_equals(document.linkColor, "red");
+ assert_equals(document.body.link, "red");
+ assert_equals(document.body.getAttribute("link"), "red");
+})
+test(function() {
+ document.body.vLink = "yellow";
+ assert_equals(document.vlinkColor, "yellow");
+ assert_equals(document.body.vLink, "yellow");
+ assert_equals(document.body.getAttribute("vlink"), "yellow");
+})
+test(function() {
+ document.body.aLink = "silver";
+ assert_equals(document.alinkColor, "silver");
+ assert_equals(document.body.aLink, "silver");
+ assert_equals(document.body.getAttribute("alink"), "silver");
+})
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-04.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-04.html
new file mode 100644
index 0000000000..ca9fc21d85
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-04.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>document: fg/bg/link/vlink/alink-color</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-fgcolor">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-body-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.fgColor, document.body.text);
+ assert_equals(document.bgColor, document.body.bgColor);
+ assert_equals(document.linkColor, document.body.link);
+ assert_equals(document.vlinkColor, document.body.vLink);
+ assert_equals(document.alinkColor, document.body.aLink);
+})
+test(function() {
+ document.body.setAttribute("text", "blue");
+ assert_equals(document.fgColor, "blue");
+ assert_equals(document.body.text, "blue");
+ assert_equals(document.body.getAttribute("text"), "blue");
+})
+test(function() {
+ document.body.setAttribute("bgcolor", "green");
+ assert_equals(document.bgColor, "green");
+ assert_equals(document.body.bgColor, "green");
+ assert_equals(document.body.getAttribute("bgcolor"), "green");
+})
+test(function() {
+ document.body.setAttribute("link", "red");
+ assert_equals(document.linkColor, "red");
+ assert_equals(document.body.link, "red");
+ assert_equals(document.body.getAttribute("link"), "red");
+})
+test(function() {
+ document.body.setAttribute("vlink", "yellow");
+ assert_equals(document.vlinkColor, "yellow");
+ assert_equals(document.body.vLink, "yellow");
+ assert_equals(document.body.getAttribute("vlink"), "yellow");
+})
+test(function() {
+ document.body.setAttribute("alink", "silver");
+ assert_equals(document.alinkColor, "silver");
+ assert_equals(document.body.aLink, "silver");
+ assert_equals(document.body.getAttribute("alink"), "silver");
+})
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/heading-obsolete-attributes-01.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/heading-obsolete-attributes-01.html
new file mode 100644
index 0000000000..8b541f2041
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/heading-obsolete-attributes-01.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>HTMLHeadingElement: obsolete attribute reflecting</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-hx-align">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-DOMString">
+<link rel="help" href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf#page=57">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var el = document.createElement("h7");
+ el.align = "left";
+ assert_equals(el.align, "left");
+ assert_false(el.hasAttribute("align"));
+ assert_equals(el.getAttribute("align"), null);
+}, "IDL attributes for HTMLHeadingElement should not apply to h7.")
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/nothing.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/nothing.html
new file mode 100644
index 0000000000..039495d78d
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/nothing.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Methods that must do nothing: clear(), captureEvents(), and releaseEvents()</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ assert_equals(document.clear(), undefined);
+}, "document.clear");
+
+test(function() {
+ assert_equals(document.captureEvents(), undefined);
+}, "document.captureEvents");
+
+test(function() {
+ assert_equals(document.releaseEvents(), undefined);
+}, "document.releaseEvents");
+
+test(function() {
+ assert_equals(window.captureEvents(), undefined);
+}, "window.captureEvents");
+
+test(function() {
+ assert_equals(window.releaseEvents(), undefined);
+}, "window.releaseEvents");
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/original-id.json b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/original-id.json
new file mode 100644
index 0000000000..601a7c08d1
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/original-id.json
@@ -0,0 +1 @@
+{"original_id":"other-elements,-attributes-and-apis"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/script-IDL-event-htmlfor.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/script-IDL-event-htmlfor.html
new file mode 100644
index 0000000000..1a8c4a2f85
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/script-IDL-event-htmlfor.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>event and htmlFor IDL attributes of HTMLScriptElement</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-script-event">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-script-htmlfor">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var script = document.createElement("script");
+ assert_equals(script.event, "");
+ assert_equals(script.htmlFor, "");
+})
+test(function() {
+ var script = document.createElement("script");
+ script.setAttribute("event", "blah");
+ script.setAttribute("for", "blah");
+ assert_equals(script.event, "blah");
+ assert_equals(script.htmlFor, "blah");
+ assert_equals(script.getAttribute("event"), "blah");
+ assert_equals(script.getAttribute("for"), "blah");
+})
+test(function() {
+ var script = document.createElement("script");
+ script.setAttribute("event", "blah");
+ script.setAttribute("for", "blah");
+ script.event = "foo";
+ script.htmlFor = "foo";
+ assert_equals(script.event, "foo");
+ assert_equals(script.htmlFor, "foo");
+ assert_equals(script.getAttribute("event"), "foo");
+ assert_equals(script.getAttribute("for"), "foo");
+})
+test(function() {
+ var script = document.createElement("script");
+ script.setAttribute("event", "blah");
+ script.setAttribute("for", "blah");
+ script.event = null;
+ script.htmlFor = null;
+ assert_equals(script.event, "null");
+ assert_equals(script.htmlFor, "null");
+ assert_equals(script.getAttribute("event"), "null");
+ assert_equals(script.getAttribute("for"), "null");
+})
+test(function() {
+ var script = document.createElement("script");
+ script.setAttribute("event", "blah");
+ script.setAttribute("for", "blah");
+ script.event = undefined;
+ script.htmlFor = undefined;
+ assert_equals(script.event, "undefined");
+ assert_equals(script.htmlFor, "undefined");
+ assert_equals(script.getAttribute("event"), "undefined");
+ assert_equals(script.getAttribute("for"), "undefined");
+})
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-calc.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-calc.html
new file mode 100644
index 0000000000..51be85323a
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-calc.html
@@ -0,0 +1,3 @@
+<canvas>
+ <marquee style="width: calc(340282366920938463463374607431768211455in - 15%); "/>
+</canvas> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-table.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-table.html
new file mode 100644
index 0000000000..54ad57fcfc
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/crashtests/marquee-with-table.html
@@ -0,0 +1 @@
+<marquee style="width: 0px; display: table; position: absolute;">text</marquee>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-adopt-to-inactive-document-crash.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-adopt-to-inactive-document-crash.html
new file mode 100644
index 0000000000..395f0a2a91
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-adopt-to-inactive-document-crash.html
@@ -0,0 +1,9 @@
+<iframe id="i"></iframe>
+<script>
+var marquee = document.createElement("marquee");
+marquee.start();
+
+var doc = i.contentDocument;
+i.remove();
+doc.adoptNode(marquee);
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-down-manual.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-down-manual.html
new file mode 100644
index 0000000000..a9d4a7f6be
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-down-manual.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-direction-down</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<meta name="assert" content="Check if the marquee direction is from top to bottom">
+<p>Test passes if the text "Test Marquee" moves from top to bottom.</p>
+<marquee direction="down">Test Marquee</marquee>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-left-manual.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-left-manual.html
new file mode 100644
index 0000000000..cf08cca4b7
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-left-manual.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-direction-left</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<meta name="assert" content="Check if the marquee direction is from right to left">
+<p>Test passes if the text "Test Marquee" moves from right to left.</p>
+<marquee direction="left">Test Marquee</marquee>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-right-manual.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-right-manual.html
new file mode 100644
index 0000000000..b42c9454fb
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-right-manual.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-direction-right</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<meta name="assert" content="Check if the marquee direction is from left to right">
+<p>Test passes if the text "Test Marquee" moves from left to right.</p>
+<marquee direction="right">Test Marquee</marquee>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-up-manual.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-up-manual.html
new file mode 100644
index 0000000000..040609fb69
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-up-manual.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-direction-up</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<meta name="assert" content="Check if the marquee direction is from bottom to top">
+<p>Test passes if the text "Test Marquee" moves from bottom to top.</p>
+<marquee direction="up">Test Marquee</marquee>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-events-historical.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-events-historical.html
new file mode 100644
index 0000000000..5780b90f04
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-events-historical.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Marquee events must not be implemented</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<link rel="help" href="https://github.com/whatwg/html/pull/6343">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+<marquee width="1" behavior="alternate">&nbsp;</marquee>
+<marquee width="1">&nbsp;</marquee>
+<marquee width="1" loop="2" behavior="alternate">&nbsp;</marquee>
+<marquee width="1" loop="2">&nbsp;</marquee>
+
+<script>
+test(() => {
+ assert_false("onstart" in HTMLMarqueeElement.prototype, "onstart");
+ assert_false("onfinish" in HTMLMarqueeElement.prototype, "onfinish");
+ assert_false("onbounce" in HTMLMarqueeElement.prototype, "onbounce");
+}, "Event handler IDL attributes must not be implemented");
+
+// Because we use width="1" they will bounce and finish really fast
+async_test(t => {
+ for (const m of document.querySelectorAll("marquee")) {
+ m.addEventListener("start", t.unreached_func(`start: ${m.outerHTML}`));
+ m.addEventListener("finish", t.unreached_func(`finish: ${m.outerHTML}`));
+ m.addEventListener("bounce", t.unreached_func(`bounce: ${m.outerHTML}`));
+ }
+
+ t.step_timeout(() => t.done(), 100);
+}, "No events must be fired, at least during the first 100 ms");
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-loop.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-loop.html
new file mode 100644
index 0000000000..d150a5473b
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-loop.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-loop</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<marquee id="test1" loop="a">Test1</marquee>
+<marquee id="test2" loop="-2">Test2</marquee>
+<marquee id="test3" loop="2">Test3</marquee>
+<script>
+ test(function() {
+ var mq = document.getElementById("test1");
+ assert_equals(mq.loop, -1, "The value of loop should be -1.");
+ }, "marquee_loop_string");
+
+ test(function() {
+ var mq = document.getElementById("test2");
+ assert_equals(mq.loop, -1, "The value of loop should be -1.");
+ }, "marquee_loop_less_than_1");
+
+ test(function() {
+ var mq = document.getElementById("test3");
+ assert_equals(mq.loop, 2, "The value of loop should be 2.");
+ }, "marquee_loop_normal");
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size-ref.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size-ref.html
new file mode 100644
index 0000000000..7a79361fc3
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Test reference</title>
+<div style="width:200px; border: 2px solid purple;">
+ <marquee style="border: 1px solid black; color: transparent;">
+Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt commodi
+ratione iste tempore nemo mollitia exercitationem error cum excepturi sit ab
+eius consectetur quasi possimus facere, iusto est impedit laborum.
+ </marquee>
+</div>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size.html
new file mode 100644
index 0000000000..827583d730
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1692380">
+<title>Marquee min intrinsic size should not cause overflow</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel=match href="marquee-min-intrinsic-size-ref.html">
+<div style="width:200px; border: 2px solid purple;">
+ <div style="display: inline-block;">
+ <marquee style="border: 1px solid black; color: transparent;">
+Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt commodi
+ratione iste tempore nemo mollitia exercitationem error cum excepturi sit ab
+eius consectetur quasi possimus facere, iusto est impedit laborum.
+ </marquee>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount-effect-manual.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount-effect-manual.html
new file mode 100644
index 0000000000..ad7ff0f3b1
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount-effect-manual.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-scrollamount-effect</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<meta name="assert" content="Check the effect of scrollamount attribute">
+<p>Test passes if the text "Test2" moves faster than the text "Test1".</p>
+<marquee id="test1">Test1</marquee>
+<marquee scrollamount="10" id="test2">Test2</marquee>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount.html
new file mode 100644
index 0000000000..808361a99f
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrollamount.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-scrollamount</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<marquee id="test1" scrollamount="aa">Test1</marquee>
+<marquee id="test2" scrollamount="-1">Test2</marquee>
+<marquee id="test3" scrollamount="10">Test3</marquee>
+<script>
+ test(function() {
+ var mq = document.getElementById("test1");
+ assert_equals(mq.scrollAmount, 6, "The value of scrollamount should be 6.");
+ }, "The scrollamount is a string");
+
+ test(function() {
+ var mq = document.getElementById("test2");
+ assert_equals(mq.scrollAmount, 6, "The value of scrollamount should be 6.");
+ }, "The scrollamount is a negative");
+
+ test(function() {
+ var mq = document.getElementById("test3");
+ assert_equals(mq.scrollAmount, 10, "The value of scrollamount should be 10.");
+ }, "The scrollamount is a normal value");
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrolldelay.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrolldelay.html
new file mode 100644
index 0000000000..394158669b
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-scrolldelay.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-scrolldelay</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<marquee id="test1" scrolldelay="aa">Test1</marquee>
+<marquee id="test2" scrolldelay="-1">Test2</marquee>
+<marquee id="test3" scrolldelay="1">Test3</marquee>
+<marquee id="test4" scrolldelay="100">Test4</marquee>
+<script>
+ test(function() {
+ var mq = document.getElementById("test1");
+ assert_equals(mq.scrollDelay, 85, "The delay time should be 85ms.");
+ }, "The scrolldelay attribute is a string");
+
+ test(function() {
+ var mq = document.getElementById("test2");
+ assert_equals(mq.scrollDelay, 85, "The delay time should be 85ms.");
+ }, "The scrolldelay attribute is a negative");
+
+ test(function() {
+ var mq = document.getElementById("test3");
+ assert_equals(mq.scrollDelay, 1,
+ "The delay time should be 1ms (although this doesn't " +
+ "match rendering).");
+ }, "The scrolldelay attribute is less than 60");
+
+ test(function() {
+ var mq = document.getElementById("test4");
+ assert_equals(mq.scrollDelay, 100, "The delay time should be 100ms.");
+ }, "The scrolldelay attribute is greater than 60");
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-start-manual.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-start-manual.html
new file mode 100644
index 0000000000..51b4289b24
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-start-manual.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-start</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<meta name="flags" content="interact">
+<meta name="assert" content="Check the start operation of HTMLMarqueeElement interface">
+<h2>Steps:</h2>
+<ol>
+ <li>Click the 'Start' button to start the marquee element.</li>
+</ol>
+<h2>Expected result:</h2>
+<ul>
+ <li>The text "Test Marquee" start to move when the 'Start' button is clicked.</li>
+</ul>
+<input type="button" id="start" value="Start" />
+<marquee id="test">Test Marquee</marquee>
+<script>
+ document.getElementById("test").stop();
+ document.getElementById("start").addEventListener("click", function(evt) {
+ document.getElementById("test").start();
+ }, false);
+</script>
diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-stop-manual.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-stop-manual.html
new file mode 100644
index 0000000000..57a4e73e4c
--- /dev/null
+++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-stop-manual.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: marquee-stop</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/obsolete.html#the-marquee-element">
+<meta name="flags" content="interact">
+<meta name="assert" content="Check the stop operation of HTMLMarqueeElement interface">
+<h2>Steps:</h2>
+<ol>
+ <li>Click the 'Start' button to start the marquee element.</li>
+</ol>
+<h2>Expected result:</h2>
+<ul>
+ <li>The text "Test Marquee" stop moving when the 'Stop' button is clicked.</li>
+</ul>
+<input type="button" id="stop" value="Stop" />
+<marquee id="test">Test Marquee</marquee>
+<script>
+ document.getElementById("stop").addEventListener("click", function(evt) {
+ document.getElementById("test").stop();
+ }, false);
+</script>
diff --git a/testing/web-platform/tests/html/rendering/bidi-rendering/slot-direction.window.js b/testing/web-platform/tests/html/rendering/bidi-rendering/slot-direction.window.js
new file mode 100644
index 0000000000..673f6c0b0f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bidi-rendering/slot-direction.window.js
@@ -0,0 +1,72 @@
+// https://html.spec.whatwg.org/multipage/rendering.html#bidi-rendering
+// https://github.com/whatwg/html/pull/9796
+// https://github.com/whatwg/html/pull/9880
+
+for (let t of [
+ {
+ description: "<slot> inherits direction from parent",
+ shadow_tree: `
+ <div dir=ltr data-expected="ltr">
+ <slot data-expected="ltr"></slot>
+ </div>
+ `,
+ host_dir: "rtl",
+ },
+ {
+ description: "<slot> inherits CSS direction from parent",
+ shadow_tree: `
+ <div style="direction: ltr" data-expected="ltr">
+ <slot data-expected="ltr"></slot>
+ </div>
+ `,
+ host_dir: "rtl",
+ },
+ {
+ description: "<slot dir=ltr>",
+ shadow_tree: `
+ <slot dir="ltr" data-expected="ltr"></slot>
+ `,
+ host_dir: "rtl",
+ },
+ {
+ description: "<slot dir=rtl>",
+ shadow_tree: `
+ <slot dir="rtl" data-expected="rtl"></slot>
+ `,
+ host_dir: "ltr",
+ },
+ {
+ description: "<slot dir=auto> resolving to LTR",
+ shadow_tree: `
+ <slot dir="ltr" data-expected="ltr"></slot>
+ `,
+ host_dir: "rtl",
+ host_contents: "A",
+ },
+ {
+ description: "<slot dir=auto> resolving to RTL",
+ shadow_tree: `
+ <slot dir="rtl" data-expected="rtl"></slot>
+ `,
+ host_dir: "ltr",
+ host_contents: "\u0627",
+ },
+]) {
+ test(() => {
+ let host = document.createElement("div");
+ document.body.appendChild(host);
+ host.dir = t.host_dir;
+ if ("host_contents" in t) {
+ host.innerHTML = t.host_contents;
+ }
+
+ let root = host.attachShadow({mode: "open"});
+ root.innerHTML = t.shadow_tree;
+
+ for (let e of Array.from(root.querySelectorAll("[data-expected]"))) {
+ assert_equals(getComputedStyle(e).direction, e.getAttribute("data-expected"), `direction of ${e.nodeName}`);
+ }
+
+ host.remove();
+ }, `<slot> element sets CSS direction property: ${t.description}`);
+}
diff --git a/testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001-ref.html b/testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001-ref.html
new file mode 100644
index 0000000000..6675f0a166
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<title>HTML Rendering: slot element has unicode-bidi: isolate</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+
+<div style="unicode-bidi: bidi-override; direction: ltr;">&#x5D1;-&#x5D0;</div>
+
+<div>normal</div>
diff --git a/testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001.html b/testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001.html
new file mode 100644
index 0000000000..bb2e927c08
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bidi-rendering/slot-no-isolate-001.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>HTML Rendering: slot element has unicode-bidi: isolate</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#bidi-rendering">
+<link rel="help" href="https://github.com/whatwg/html/pull/9880">
+<link rel="match" href="slot-no-isolate-001-ref.html">
+
+<div>&#x5D0;-<span id="e">&#x5D1;</span></div>
+
+<div id="v"></div>
+
+<script>
+
+let e = document.getElementById("e");
+let root = e.attachShadow({mode: "open"});
+// use display:inline to override default display:contents
+root.innerHTML = "<slot style='display:inline'>\u05D2</slot>";
+let val = getComputedStyle(root.querySelector("slot")).unicodeBidi;
+document.getElementById("v").innerText = val;
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/bidi-rendering/unicode-bidi-ua-rules.html b/testing/web-platform/tests/html/rendering/bidi-rendering/unicode-bidi-ua-rules.html
new file mode 100644
index 0000000000..78bfb80e6f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bidi-rendering/unicode-bidi-ua-rules.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>unicode-bidi UA stylesheet rules</title>
+<link rel="author" href="masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#bidi-rendering">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<body>
+<script>
+ const elements = ['address', 'blockquote', 'center', 'div', 'figure',
+ 'figcaption', 'footer', 'form', 'header', 'hr', 'legend', 'listing',
+ 'main', 'p', 'plaintext', 'pre', 'summary', 'xmp', 'article', 'aside',
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hgroup', 'nav', 'section', 'search',
+ 'table', 'caption', 'colgroup', 'col', 'thead', 'tbody', 'tfoot', 'tr',
+ 'td', 'th', 'dir', 'dd', 'dl', 'dt', 'menu', 'ol', 'ul', 'li', 'bdi',
+ 'output'];
+ for(let tagname of elements) {
+ test((t) => {
+ const element = document.createElement(tagname);
+ t.add_cleanup(() => element.remove());
+ document.body.appendChild(element);
+ assert_equals(window.getComputedStyle(element, "").unicodeBidi,'isolate');
+ },`UA stylesheet rule for unicode-bidi, for <${tagname}>`);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical-ref.html b/testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical-ref.html
new file mode 100644
index 0000000000..6d728e8136
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<title>Test that button with type="menu" renders the same as button with type="submit"</title>
+<meta charset="utf-8">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://github.com/whatwg/html/pull/2342">
+
+<button type="submit">button</button>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical.html b/testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical.html
new file mode 100644
index 0000000000..b355afeed1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-button-element/button-type-menu-historical.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<title>Test that button with type="menu" renders the same as button with type="submit"</title>
+<meta charset="utf-8">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://github.com/whatwg/html/pull/2342">
+<link rel="match" href="button-type-menu-historical-ref.html">
+
+<button type="menu">button</button>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type-ref.html b/testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type-ref.html
new file mode 100644
index 0000000000..879ca9233f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Unrecognized type should fallback as text type</title>
+<body>
+ <input type="text">
+ <input type="text">
+ <input type="text" disabled>
+ <input type="text" disabled>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type.html b/testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type.html
new file mode 100644
index 0000000000..a5c7a60841
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-input-element-as-a-text-entry-widget/unrecognized-type-should-fallback-as-text-type.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Unrecognized type should fallback as text type</title>
+<link rel="match" href="unrecognized-type-should-fallback-as-text-type-ref.html">
+<body>
+ <input>
+ <input type="unknown">
+ <input disabled>
+ <input type="unknown" disabled>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label-ref.html b/testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label-ref.html
new file mode 100644
index 0000000000..e3f73cb3ed
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label-ref.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Option labels</title>
+<select size=12>
+ <option><!-- No children, no label-->
+ <option><!-- No children, empty label-->
+ <option>label<!-- No children, label-->
+ <option><!-- No children, namespaced label-->
+
+ <option>child<!-- Single child, no label-->
+ <option>child<!-- Single child, empty label-->
+ <option>label<!-- Single child, label-->
+ <option>child<!-- Single child, namespaced label-->
+
+ <option>child node<!-- Two children, no label-->
+ <option>child node<!-- Two children, empty label-->
+ <option>label<!-- Two children, label-->
+ <option>child node<!-- Two children, namespaced label-->
+</select>
+
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label.html b/testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label.html
new file mode 100644
index 0000000000..f98595ed01
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-select-element-0/option-label.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<title>Option labels</title>
+<link rel="match" href="option-label-ref.html">
+<select size=12></select>
+<script>
+var select = document.getElementsByTagName("select")[0], option;
+
+option = document.createElement("option");
+select.appendChild(option);
+
+option = document.createElement("option");
+option.setAttribute("label", "")
+select.appendChild(option);
+
+option = document.createElement("option");
+option.setAttribute("label", "label")
+select.appendChild(option);
+
+option = document.createElement("option");
+option.setAttributeNS("http://www.example.com/", "label", "label")
+select.appendChild(option);
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+select.appendChild(option);
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+option.setAttribute("label", "")
+select.appendChild(option);
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+option.setAttribute("label", "label")
+select.appendChild(option);
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+option.setAttributeNS("http://www.example.com/", "label", "label")
+select.appendChild(option);
+
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+option.appendChild(document.createTextNode(" node "));
+select.appendChild(option);
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+option.appendChild(document.createTextNode(" node "));
+option.setAttribute("label", "")
+select.appendChild(option);
+
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+option.appendChild(document.createTextNode(" node "));
+option.setAttribute("label", "label")
+select.appendChild(option);
+
+option = document.createElement("option");
+option.appendChild(document.createTextNode(" child "));
+option.appendChild(document.createTextNode(" node "));
+option.setAttributeNS("http://www.example.com/", "label", "label")
+select.appendChild(option);
+</script>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-default.html b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-default.html
new file mode 100644
index 0000000000..067d658dd4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-default.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Textarea cols</title>
+<link rel=match href=textarea-ref.html>
+<textarea cols=20></textarea>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-zero.html b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-zero.html
new file mode 100644
index 0000000000..8ecac6bf5a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/cols-zero.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Textarea cols</title>
+<link rel=match href=textarea-ref.html>
+<textarea cols=0></textarea>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-default.html b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-default.html
new file mode 100644
index 0000000000..5bb9f61eee
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-default.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Textarea rows</title>
+<link rel=match href=textarea-ref.html>
+<textarea rows=2></textarea>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-zero.html b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-zero.html
new file mode 100644
index 0000000000..79c8d0ff53
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/rows-zero.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Textarea rows</title>
+<link rel=match href=textarea-ref.html>
+<textarea rows=0></textarea>
diff --git a/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/textarea-ref.html b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/textarea-ref.html
new file mode 100644
index 0000000000..f2982808f6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/bindings/the-textarea-element-0/textarea-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Default textarea</title>
+<textarea></textarea>
diff --git a/testing/web-platform/tests/html/rendering/dimension-attributes.html b/testing/web-platform/tests/html/rendering/dimension-attributes.html
new file mode 100644
index 0000000000..1d94205e0b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/dimension-attributes.html
@@ -0,0 +1,233 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test handling of attributes that map to dimension properties</title>
+<meta name="timeout" content="long">
+<link rel="help"
+ href="https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property">
+<link rel="help"
+ href="https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property-(ignoring-zero)">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<!-- We need a place to put our elements so they're bound to a document and
+ have computed style, but we don't want percentages resolved to lengths,
+ so need to make sure they have no CSS boxes -->
+<div id="container" style="display: none"></div>
+<script>
+ /*
+ * This test tests
+ *
+ * https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property
+ * and
+ * https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property-(ignoring-zero)
+ * for various elements and various values.
+ */
+
+ /*
+ * Array of input/output pairs. The input is the string to use as the
+ * attribute value. The output is the string expected as the computed style
+ * for the relevant CSS property.
+ */
+const valid_values = [
+ // Valid values
+ [ "200", "200px" ],
+ [ "1007", "1007px" ],
+ [ " 00523 ", "523px" ],
+ [ "200.25", "200.25px" ],
+ [ "200.7", "200.7px" ],
+ [ "200.", "200px" ],
+ [ "200in", "200px" ],
+ [ "200.25in", "200.25px" ],
+ [ "200 %", "200px" ],
+ [ "200 abc", "200px" ],
+ [ "200%", "200%" ],
+ [ "200%abc", "200%" ],
+ [ "200.25%", "200.25%" ],
+ [ "200.%", "200%" ],
+ [ "20.25e2", "20.25px" ],
+ [ "20.25E2", "20.25px" ],
+];
+
+ /*
+ * Values that are only valid for the not-ignoring-zero case.
+ */
+const zero_values = [
+ [ "0", "0px" ],
+ [ "0%", "0%" ],
+ [ "0px", "0px" ],
+];
+
+ /*
+ * Array of invalid values. These should lead to the default value of the
+ * relevant CSS property.
+ */
+const invalid_values = [
+ "-0",
+ "-0%",
+ "-200",
+ "-200px",
+ " -200",
+ "+-200",
+ "-+200",
+ "-200%",
+ "+200",
+ " +200in ",
+ " +200.25in ",
+ "+200%",
+ " +200.25% ",
+ " +200.25%abc",
+ "+0",
+ "+0%",
+ ".",
+ ".%",
+ ".x",
+ ".5",
+ ".5%"
+];
+
+const valid_values_with_0 =
+ valid_values.concat(zero_values);
+const invalid_values_with_0 =
+ invalid_values.concat(zero_values.map((v) => v[0]));
+
+function newElem(name) {
+ return () => document.createElement(name);
+}
+
+function newImageInput() {
+ return () => {
+ var elem = newElem("input")();
+ elem.type = "image";
+ return elem;
+ }
+}
+
+function newImgSource() {
+ return () => {
+ var elem = newElem("source")();
+ elem.setAttribute("srcset", "/images/green-100x50.png");
+ return elem;
+ }
+}
+
+/*
+ * Array of tests. Each test consists of the following information:
+ *
+ * 1) An element creation function.
+ * 2) The name of the attribute to set
+ * 3) The name of the CSS property to get.
+ * 4) A boolean indicating whether 0 is a valid value for the dimension
+ * attribute.
+ */
+const tests = [
+ [ newElem("hr"), "width", "width", true ],
+ [ newElem("iframe"), "width", "width", true ],
+ [ newElem("iframe"), "height", "height", true ],
+ [ newImageInput(), "width", "width", true ],
+ [ newImageInput(), "height", "height", true ],
+ [ newElem("marquee"), "width", "width", true ],
+ [ newElem("marquee"), "height", "height", true ],
+ [ newElem("video"), "width", "width", true ],
+ [ newElem("video"), "height", "height", true ],
+ [ newElem("object"), "width", "width", true ],
+ [ newElem("object"), "height", "height", true ],
+ [ newElem("embed"), "width", "width", true ],
+ [ newElem("embed"), "height", "height", true ],
+ [ newElem("img"), "width", "width", true ],
+ [ newElem("img"), "height", "height", true ],
+ [ newElem("td"), "width", "width", false ],
+ [ newElem("td"), "height", "height", false ],
+ [ newElem("table"), "width", "width", false ],
+ [ newElem("table"), "height", "height", true ],
+ [ newElem("tr"), "height", "height", true ],
+ [ newElem("col"), "width", "width", true ],
+ [ newElem("embed"), "hspace", "marginLeft", true ],
+ [ newElem("embed"), "hspace", "marginRight", true ],
+ [ newElem("embed"), "vspace", "marginTop", true ],
+ [ newElem("embed"), "vspace", "marginBottom", true ],
+ [ newElem("img"), "hspace", "marginLeft", true ],
+ [ newElem("img"), "hspace", "marginRight", true ],
+ [ newElem("img"), "vspace", "marginTop", true ],
+ [ newElem("img"), "vspace", "marginBottom", true ],
+ [ newElem("object"), "hspace", "marginLeft", true ],
+ [ newElem("object"), "hspace", "marginRight", true ],
+ [ newElem("object"), "vspace", "marginTop", true ],
+ [ newElem("object"), "vspace", "marginBottom", true ],
+ [ newImageInput(), "hspace", "marginLeft", true ],
+ [ newImageInput(), "hspace", "marginRight", true ],
+ [ newImageInput(), "vspace", "marginTop", true ],
+ [ newImageInput(), "vspace", "marginBottom", true ],
+ [ newElem("marquee"), "hspace", "marginLeft", true ],
+ [ newElem("marquee"), "hspace", "marginRight", true ],
+ [ newElem("marquee"), "vspace", "marginTop", true ],
+ [ newElem("marquee"), "vspace", "marginBottom", true ],
+ // <source width> is mapped to <img> width if both are in <picture>.
+ [ newImgSource(), "width", "width", true, newElem("img"), newElem("picture") ],
+ // <source height> is mapped to <img> height if both are in <picture>.
+ [ newImgSource(), "height", "height", true, newElem("img"), newElem("picture") ],
+];
+
+function style(element) {
+ return element.ownerDocument.defaultView.getComputedStyle(element);
+}
+
+const container = document.getElementById("container");
+
+for (let [ctor, attr, prop, zero_allowed, mappedElemCtor, containerCtor] of tests) {
+ let valid, invalid;
+ if (zero_allowed) {
+ valid = valid_values_with_0;
+ invalid = invalid_values;
+ } else {
+ valid = valid_values;
+ invalid = invalid_values_with_0;
+ }
+
+ let elemContainer = null;
+ if (!!containerCtor) {
+ elemContainer = containerCtor();
+ container.appendChild(elemContainer);
+ } else {
+ elemContainer = container;
+ }
+
+ let runTest = (value, expected) => {
+ let elem = ctor();
+ let mappedElem = !!mappedElemCtor ? mappedElemCtor() : elem;
+ test(function() {
+ this.add_cleanup(() => {
+ elem.remove();
+ if (!!mappedElemCtor) {
+ mappedElem.remove();
+ }
+ });
+ elem.setAttribute(attr, value);
+ assert_equals(elem.getAttribute(attr), value);
+ elemContainer.appendChild(elem);
+ if (!!mappedElemCtor) {
+ elemContainer.appendChild(mappedElem);
+ }
+ assert_equals(style(mappedElem)[prop], expected);
+ }, `<${elem.localName} ${attr}="${value}"> mapping to ` +
+ `<${mappedElem.localName}> ${prop} property`);
+ }
+
+ for (let [value, result] of valid) {
+ runTest(value, result);
+ }
+
+ let default_elem = !!mappedElemCtor ? mappedElemCtor() : ctor();
+ elemContainer.appendChild(default_elem);
+ let defaultVal = style(default_elem)[prop];
+ default_elem.remove();
+ for (let value of invalid) {
+ runTest(value, defaultVal);
+ }
+
+ if (!!containerCtor) {
+ elemContainer.remove();
+ }
+}
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/interactive-media/links-forms-and-navigation/original-id.json b/testing/web-platform/tests/html/rendering/interactive-media/links-forms-and-navigation/original-id.json
new file mode 100644
index 0000000000..07a108785e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/interactive-media/links-forms-and-navigation/original-id.json
@@ -0,0 +1 @@
+{"original_id":"links,-forms,-and-navigation"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog-display.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog-display.html
new file mode 100644
index 0000000000..48b0e33d94
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog-display.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>dialog: display</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ dialog { position: static }
+</style>
+<dialog open id=dialog></dialog>
+<script>
+test(function() {
+ assert_equals(getComputedStyle(document.getElementById('dialog')).display, 'block');
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog.html
new file mode 100644
index 0000000000..3588e03670
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/dialog.html
@@ -0,0 +1,115 @@
+<!doctype html>
+<title>The dialog element</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ #ref-element {
+ padding-top: 1em;
+ background-color: white;
+ color: black;
+ border: solid;
+ }
+</style>
+<!--
+HTML used to have a style rule with `@media screen and (max-width: 540px)`.
+That was removed in https://github.com/whatwg/html/pull/2459
+-->
+<div><iframe style="width: 540px" src="support/dialog-framed.html"></iframe></div>
+<div><iframe style="width: 538px" src="support/dialog-framed.html"></iframe></div>
+<div id=ref-element></div>
+<script>
+setup(() => {
+ const refStyle = getComputedStyle(document.getElementById('ref-element'));
+ window.ref1em = refStyle.paddingTop;
+ window.refWhite = refStyle.backgroundColor;
+ window.refBlack = refStyle.color;
+ window.refMediumBorder = refStyle.borderTopWidth;
+ window.iframeHeight = 150;
+ const bodyTopMargin = 8;
+ window.dialogSize = parseFloat(ref1em) * 2 + parseFloat(refMediumBorder) * 2;
+ window.normalBottomDistance = iframeHeight - bodyTopMargin - dialogSize;
+}, {explicit_done: true});
+
+onload = () => {
+ for (let iframe of document.querySelectorAll('iframe')) {
+ const win = iframe.contentWindow;
+ const styleAttr = iframe.getAttribute('style');
+ const iframeWidth = parseInt(styleAttr.split(' ')[1]);
+ const horizontalDistance = iframeWidth / 2 - dialogSize / 2;
+ const verticalDistance = iframeHeight / 2 - dialogSize / 2;
+ test(() => {
+ const style = win.getComputedStyle(win.dialogClosed);
+ assert_equals(style.position, 'absolute', 'position');
+ assert_equals(style.display, 'none', 'display');
+ assert_equals(style.overflow, 'visible', 'overflow');
+ assert_equals(style.top, 'auto', 'top');
+ assert_equals(style.right, '0px', 'right');
+ assert_equals(style.bottom, 'auto', 'bottom');
+ assert_equals(style.left, '0px', 'left');
+ assert_equals(style.width, 'fit-content', 'width');
+ assert_equals(style.height, 'fit-content', 'height');
+ assert_equals(style.maxWidth, 'none', 'max-width');
+ assert_equals(style.maxHeight, 'none', 'max-height');
+ assert_equals(style.marginTop, 'auto', 'marginTop');
+ assert_equals(style.marginRight, 'auto', 'marginRight');
+ assert_equals(style.marginBottom, 'auto', 'marginBottom');
+ assert_equals(style.marginLeft, 'auto', 'marginLeft');
+ assertCommon(style);
+ }, `Closed dialog in ${styleAttr} iframe`);
+
+ test(() => {
+ const style = win.getComputedStyle(win.dialogOpen);
+ assert_equals(style.position, 'absolute', 'position');
+ assert_equals(style.display, 'block', 'display');
+ assert_equals(style.overflow, 'visible', 'overflow');
+ assert_equals(style.top, '8px', 'top');
+ assert_equals(style.right, '0px', 'right');
+ assert_equals(style.bottom, normalBottomDistance + 'px', 'bottom');
+ assert_equals(style.left, '0px', 'left');
+ assert_equals(style.width, '0px', 'width');
+ assert_equals(style.height, '0px', 'height');
+ assert_equals(style.maxWidth, 'none', 'max-width');
+ assert_equals(style.maxHeight, 'none', 'max-height');
+ assert_equals(style.marginTop, '0px', 'marginTop');
+ assert_equals(style.marginRight, horizontalDistance + 'px', 'marginRight');
+ assert_equals(style.marginBottom, '0px', 'marginBottom');
+ assert_equals(style.marginLeft, horizontalDistance + 'px', 'marginLeft');
+ assertCommon(style);
+ }, `Open dialog in ${styleAttr} iframe`);
+
+ test(() => {
+ const style = win.getComputedStyle(win.dialogModal);
+ assert_equals(style.position, 'fixed', 'position');
+ assert_equals(style.display, 'block', 'display');
+ assert_equals(style.overflow, 'auto', 'overflow');
+ assert_equals(style.top, '0px', 'top');
+ assert_equals(style.right, '0px', 'right');
+ assert_equals(style.bottom, '0px', 'bottom');
+ assert_equals(style.left, '0px', 'left');
+ assert_equals(style.width, '0px', 'width');
+ assert_equals(style.height, '0px', 'height');
+ assert_equals(style.maxWidth, 'calc(100% - 38px)', 'max-width');
+ assert_equals(style.maxHeight, 'calc(100% - 38px)', 'max-height');
+ assert_equals(style.marginTop, verticalDistance + 'px', 'marginTop');
+ assert_equals(style.marginRight, horizontalDistance + 'px', 'marginRight');
+ assert_equals(style.marginBottom, verticalDistance + 'px', 'marginBottom');
+ assert_equals(style.marginLeft, horizontalDistance + 'px', 'marginLeft');
+ assertCommon(style);
+ }, `Modal dialog in ${styleAttr} iframe`);
+ }
+ done();
+};
+
+function assertCommon(style) {
+ assert_equals(style.borderTopStyle, 'solid', 'borderTopStyle');
+ assert_equals(style.borderRightStyle, 'solid', 'borderRightStyle');
+ assert_equals(style.borderBottomStyle, 'solid', 'borderBottomStyle');
+ assert_equals(style.borderLeftStyle, 'solid', 'borderLeftStyle');
+ assert_equals(style.paddingTop, ref1em, 'paddingTop');
+ assert_equals(style.paddingRight, ref1em, 'paddingRight');
+ assert_equals(style.paddingBottom, ref1em, 'paddingBottom');
+ assert_equals(style.paddingLeft, ref1em, 'paddingLeft');
+ assert_equals(style.backgroundColor, refWhite, 'backgroundColor');
+ assert_equals(style.color, refBlack, 'color');
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align-ref.html
new file mode 100644
index 0000000000..da8e4d0dc1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align-ref.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+.test { width: 50px; background-color: yellow; }
+.center { text-align: center; }
+.center .test { margin: 0 auto; }
+.left { text-align: left; }
+.left .test { margin-right: auto; }
+.right { text-align: right; }
+.right .test { margin-left: auto; }
+.rtl { direction: rtl; }
+.ltr { direction: ltr; }
+.left .margin { margin-left: 1em; }
+.right .margin { margin-right: 1em; }
+</style>
+</head>
+<body>
+<!-- Centered tests -->
+<div class=center>
+<div class=test>t ×</div>
+<div class="test rtl">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div class=center>
+<div class="test left">t ×</div>
+<div class="test right">t ×</div>
+</div>
+
+<div class=left>
+<div class=center>
+<div class=test>t ×</div>
+</div>
+</div>
+
+<!-- Left-aligned tests -->
+<div class=left>
+<div class=test>t ×</div>
+<div class="test rtl">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div class="left rtl">
+<div class=test>t ×</div>
+<div class="test ltr">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div class=left>
+<div class="test center">t ×</div>
+<div class="test right">t ×</div>
+</div>
+
+<!-- Right-aligned tests -->
+<div class=right>
+<div class=test>t ×</div>
+<div class="test rtl">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div class="right rtl">
+<div class=test>t ×</div>
+<div class="test ltr">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div class=right>
+<div class="test left">t ×</div>
+<div class="test center">t ×</div>
+</div>
+
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align.html
new file mode 100644
index 0000000000..b96fbaeda1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<link rel="match" href="div-align-ref.html">
+<style>
+.test { width: 50px; background-color: yellow; }
+.rtl { direction: rtl; }
+.ltr { direction: ltr; }
+[align=left] .margin { margin-left: 1em }
+[align=right] .margin { margin-right: 1em }
+</style>
+</head>
+<body>
+<!-- Centered tests -->
+<div align=center>
+<div class=test>t ×</div>
+<div class="test rtl">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div align=center>
+<div class=test align=left>t ×</div>
+<div class=test align=right>t ×</div>
+</div>
+
+<div align=left>
+<div align=center>
+<div class=test>t ×</div>
+</div>
+</div>
+
+<!-- Left-aligned tests -->
+<div align=left>
+<div class=test>t ×</div>
+<div class="test rtl">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div align=left class=rtl>
+<div class=test>t ×</div>
+<div class="test ltr">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div align=left>
+<div class=test align=center>t ×</div>
+<div class=test align=right>t ×</div>
+</div>
+
+<!-- Right-aligned tests -->
+<div align=right>
+<div class=test>t ×</div>
+<div class="test rtl">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div align=right class=rtl>
+<div class=test>t ×</div>
+<div class="test ltr">t ×</div>
+<div class="test margin">t ×</div>
+</div>
+
+<div align=right>
+<div class=test align=left>t ×</div>
+<div class=test align=center>t ×</div>
+</div>
+
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure-ref.html
new file mode 100644
index 0000000000..a87141be11
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure-ref.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The figure element</title>
+<link rel=author title=Ms2ger href=ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-figure-element>
+<style>
+body > div { margin: 1em 40px; }
+</style>
+<div>
+<div>Caption</div>
+Figure
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure.html
new file mode 100644
index 0000000000..943f38c3e0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/figure.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>The figure element</title>
+<link rel="match" href="figure-ref.html">
+<link rel=author title=Ms2ger href=ms2ger@gmail.com>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#the-figure-element>
+<figure>
+<figcaption>Caption</figcaption>
+Figure
+</figure>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html
new file mode 100644
index 0000000000..7f6618bb78
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html
@@ -0,0 +1,20 @@
+<!-- quirks -->
+<title>form margin quirk</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+form { writing-mode: vertical-lr; }
+#ref { margin: 0 1em 0 0; }
+</style>
+<form id=form></form>
+<div id=ref></div>
+<script>
+test(() => {
+ const formStyle = getComputedStyle(document.getElementById('form'));
+ const refStyle = getComputedStyle(document.getElementById('ref'));
+ assert_equals(formStyle.marginTop, refStyle.marginTop, 'marginTop');
+ assert_equals(formStyle.marginRight, refStyle.marginRight, 'marginRight');
+ assert_equals(formStyle.marginBottom, refStyle.marginBottom, 'marginBottom');
+ assert_equals(formStyle.marginLeft, refStyle.marginLeft, 'marginLeft');
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles-iso-8859-8.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles-iso-8859-8.html
new file mode 100644
index 0000000000..343e97f504
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles-iso-8859-8.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<meta charset="ISO-8859-8">
+<title>default styles for the search element, ISO-8859-8</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/rendering/support/test-ua-stylesheet.js"></script>
+<style>
+/* Specify this bogus namespace, so the rules in this stylesheet only apply to the `fakeClone`d elements in #refs, not the HTML elements in #tests. */
+@namespace url(urn:not-html);
+
+address, blockquote, center, dialog, div, figure, figcaption, footer, form,
+header, hr, legend, listing, main, p, plaintext, pre, search, xmp {
+ display: block;
+}
+
+address, blockquote, center, div, figure, figcaption, footer, form, header, hr,
+legend, listing, main, p, plaintext, pre, summary, xmp, article, aside, h1, h2,
+h3, h4, h5, h6, hgroup, nav, section, search, table, caption, colgroup, col,
+thead, tbody, tfoot, tr, td, th, dir, dd, dl, dt, menu, ol, ul, li, bdi, output,
+[dir=ltr i], [dir=rtl i], [dir=auto i], * /* only apply to the bogus namespace */ {
+ unicode-bidi: isolate;
+}
+
+</style>
+
+<div id="log"></div>
+
+<div id="tests">
+ <search></search>
+</div>
+
+<div id="refs"></div>
+
+<script>
+ test(() => {
+ assert_equals(document.characterSet, "ISO-8859-8")
+ }, "Verify document.characterSet is ISO-8859-8");
+
+ const props = [
+ 'display',
+ 'margin-top',
+ 'margin-right',
+ 'margin-bottom',
+ 'margin-left',
+ 'padding-top',
+ 'padding-right',
+ 'padding-bottom',
+ 'padding-left',
+ 'unicode-bidi',
+ ];
+ runUAStyleTests(props);
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles.html
new file mode 100644
index 0000000000..8c8badd170
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/search-styles.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>default styles for the search element</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/rendering/support/test-ua-stylesheet.js"></script>
+<style>
+/* Specify this bogus namespace, so the rules in this stylesheet only apply to the `fakeClone`d elements in #refs, not the HTML elements in #tests. */
+@namespace url(urn:not-html);
+
+address, blockquote, center, dialog, div, figure, figcaption, footer, form,
+header, hr, legend, listing, main, p, plaintext, pre, search, xmp {
+ display: block;
+}
+
+address, blockquote, center, div, figure, figcaption, footer, form, header, hr,
+legend, listing, main, p, plaintext, pre, summary, xmp, article, aside, h1, h2,
+h3, h4, h5, h6, hgroup, nav, section, search, table, caption, colgroup, col,
+thead, tbody, tfoot, tr, td, th, dir, dd, dl, dt, menu, ol, ul, li, bdi, output,
+[dir=ltr i], [dir=rtl i], [dir=auto i] {
+ unicode-bidi: isolate;
+}
+
+</style>
+
+<div id="log"></div>
+
+<div id="tests">
+ <search></search>
+</div>
+
+<div id="refs"></div>
+
+<script>
+ const props = [
+ 'display',
+ 'margin-top',
+ 'margin-right',
+ 'margin-bottom',
+ 'margin-left',
+ 'padding-top',
+ 'padding-right',
+ 'padding-bottom',
+ 'padding-left',
+ 'unicode-bidi',
+ ];
+ runUAStyleTests(props);
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-focusable.tentative.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-focusable.tentative.html
new file mode 100644
index 0000000000..b6d2449e89
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-focusable.tentative.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>CSS Test (Display): <slot> elements should be focusable</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2632">
+<link rel="help" href="https://github.com/whatwg/html/issues/1837">
+<link rel="help" href="https://github.com/whatwg/html/pull/9425">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1366037">
+<!--
+
+ This requirement is not particularly clear from current specs,
+ so this test is tentative. See issues above.
+
+-->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+
+<script>
+
+ function do_test(slot_style, description) {
+ test(
+ function() {
+ let host = document.createElement("div");
+ document.body.appendChild(host);
+ var root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <style>
+ slot { --test-value: slot-not-focused; }
+ slot:focus { --test-value: slot-is-focused; }
+ </style>
+ <slot tabindex="1" style="${slot_style}"></slot>
+ `;
+ let slot = root.querySelector("slot");
+ let cs = getComputedStyle(slot);
+ assert_not_equals(root.activeElement, slot, "precondition");
+ assert_equals(cs.getPropertyValue("--test-value"), "slot-not-focused", "precondition (style)");
+ slot.focus();
+ assert_equals(root.activeElement, slot, "slot is now focused");
+ assert_equals(cs.getPropertyValue("--test-value"), "slot-is-focused", "slot is now focused (style)");
+ document.body.removeChild(host);
+ }, `slot element with ${description} should be focusable`);
+ }
+
+ do_test("display: block", "display: block");
+ do_test("", "default style");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-tabbable.tentative.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-tabbable.tentative.html
new file mode 100644
index 0000000000..22c2324d02
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/slot-element-tabbable.tentative.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>CSS Test (Display): <slot> elements should be tabbable</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2632">
+<link rel="help" href="https://github.com/whatwg/html/issues/1837">
+<link rel="help" href="https://github.com/whatwg/html/pull/9425">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1366037">
+<!--
+
+ This requirement is not particularly clear from current specs,
+ so this test is tentative. See issues above.
+
+-->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+
+<div id="before" tabindex="1" style="height: 10px"></div>
+
+<script>
+
+ function do_test(slot_style, description) {
+ promise_test(
+ async () => {
+ let before = document.getElementById("before");
+ before.focus();
+ let host = document.createElement("div");
+ document.body.appendChild(host);
+ var root = host.attachShadow({mode:"open"});
+ root.innerHTML = `
+ <style>
+ slot { --test-value: slot-not-focused; }
+ slot:focus { --test-value: slot-is-focused; }
+ </style>
+ <slot tabindex="1" style="${slot_style}"></slot>
+ `;
+ let slot = root.querySelector("slot");
+ let cs = getComputedStyle(slot);
+ assert_equals(document.activeElement, before, "precondition");
+ assert_not_equals(root.activeElement, slot, "precondition");
+ assert_equals(cs.getPropertyValue("--test-value"), "slot-not-focused", "precondition (style)");
+ await test_driver.send_keys(before, "\uE004");
+ assert_equals(root.activeElement, slot, "slot is now focused");
+ assert_equals(cs.getPropertyValue("--test-value"), "slot-is-focused", "slot is now focused (style)");
+ document.body.removeChild(host);
+ }, `slot element with ${description} should be focusable`);
+ }
+
+ do_test("display: block", "display: block");
+ do_test("", "default style");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/support/dialog-framed.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/support/dialog-framed.html
new file mode 100644
index 0000000000..f9c414c246
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/support/dialog-framed.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<style>
+ html { color: red }
+</style>
+<dialog id=dialog-closed></dialog>
+<dialog id=dialog-open open></dialog>
+<dialog id=dialog-modal></dialog>
+<script>
+window.dialogClosed = document.getElementById('dialog-closed');
+window.dialogOpen = document.getElementById('dialog-open');
+window.dialogModal = document.getElementById('dialog-modal');
+dialogModal.showModal();
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change-ref.html
new file mode 100644
index 0000000000..478b8db485
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<input type="datetime-local">
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change.html
new file mode 100644
index 0000000000..f8590ee561
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/datetime-dynamic-type-change.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1797139">
+<link rel="match" href="datetime-dynamic-type-change-ref.html">
+<input type="date">
+<script>
+ onload = function() {
+ document.querySelector("input").type = "datetime-local";
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-computed.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-computed.html
new file mode 100644
index 0000000000..1bee40359a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-computed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>used value and computed value of 'line-height' on input elements as text entry widgets</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-input-element-as-a-text-entry-widget">
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om/#computed-stylepropertymapreadonly-objects">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ input { line-height: 1px; }
+</style>
+<p><input type=text value=text>
+<p><input type=tel value=tel>
+<p><input type=search value=search>
+<p><input type=url value=url>
+<p><input type=email value=email>
+<p><input type=password value=password></p>
+<script>
+const inputs = document.querySelectorAll('input');
+for (const input of inputs) {
+ test(() => {
+ const usedLineHeight = getComputedStyle(input).lineHeight;
+ assert_not_equals(usedLineHeight, '1px', 'usedLineHeight');
+ assert_not_equals(usedLineHeight, 'normal', 'usedLineHeight');
+ }, `getComputedStyle(<input type=${input.type}>).lineHeight should return a used value that is no smaller than 'normal' (but should not literally be 'normal')`);
+ test(() => {
+ const computedLineHeight = input.computedStyleMap().get('line-height');
+ assert_equals(computedLineHeight.value, 1, 'computedLineHeight.value');
+ assert_equals(computedLineHeight.unit, 'px', 'computedLineHeight.unit');
+ }, `<input type=${input.type}>.computedStyleMap().get('line-height') should not be affected by the used value clamping`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-ref.html
new file mode 100644
index 0000000000..abf50b8728
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>Reference for 'line-height' smaller than 'normal' on input elements as text entry widgets</title>
+<style>
+ input { font-size: 60px; width: 240px; }
+ .appearance-none { appearance: none; }
+</style>
+<p><input type=text value=text> <input type=text value=text class=appearance-none>
+<p><input type=tel value=tel> <input type=tel value=tel class=appearance-none>
+<p><input type=search value=search> <input type=search value=search class=appearance-none>
+<p><input type=url value=url> <input type=url value=url class=appearance-none>
+<p><input type=email value=email> <input type=email value=email class=appearance-none>
+<p><input type=password value=password> <input type=password value=password class=appearance-none>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height.html
new file mode 100644
index 0000000000..bfcd3665be
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-line-height.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>'line-height' smaller than 'normal' on input elements as text entry widgets</title>
+<link rel="match" href="input-line-height-ref.html">
+<style>
+ input { font-size: 60px; line-height: 40px; width: 240px; }
+ .appearance-none { appearance: none; }
+</style>
+<p><input type=text value=text> <input type=text value=text class=appearance-none>
+<p><input type=tel value=tel> <input type=tel value=tel class=appearance-none>
+<p><input type=search value=search> <input type=search value=search class=appearance-none>
+<p><input type=url value=url> <input type=url value=url class=appearance-none>
+<p><input type=email value=email> <input type=email value=email class=appearance-none>
+<p><input type=password value=password> <input type=password value=password class=appearance-none>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height-ref.html
new file mode 100644
index 0000000000..856146dec4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<input placeholder=Foo>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height.html
new file mode 100644
index 0000000000..7af4e65b09
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/input-placeholder-line-height.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>line-height has no effect on placeholder</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1714631">
+<link rel="match" href="input-placeholder-line-height-ref.html">
+<style>
+ input::placeholder {
+ line-height: 0;
+ }
+</style>
+<input placeholder=Foo>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/placeholder-opacity-default.tentative.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/placeholder-opacity-default.tentative.html
new file mode 100644
index 0000000000..39ad44cfc0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/placeholder-opacity-default.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Placeholder Test: opacity default value</title>
+ <link rel="author" title="Karl Dubost" href="mailto:kdubost@mozilla.com"
+ />
+ <link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#placeholder-pseudo"
+ />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <input
+ id="opacity"
+ placeholder="::placeholder should have default opacity: 1"
+ />
+ <script>
+ test(function () {
+ var target = document.getElementById("opacity");
+ assert_equals(getComputedStyle(target, '::placeholder').opacity, "1");
+ }, "Default opacity value is '1'");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html
new file mode 100644
index 0000000000..db21188ee3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html
@@ -0,0 +1,118 @@
+<!doctype html>
+<title>default style resets</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/rendering/support/test-ua-stylesheet.js"></script>
+<style>
+/* Have some non-initial values on the parent so we can tell the difference whether the UA stylesheet uses 'initial' keyword. */
+#tests, #refs {
+ letter-spacing: 5px;
+ word-spacing: 5px;
+ line-height: 5px;
+ text-transform: uppercase;
+ text-indent: 5px;
+ text-shadow: 0 0 5px transparent;
+ text-align: right;
+}
+</style>
+<style>
+/* Specify this bogus namespace, so the rules in this stylesheet only apply to the `fakeClone`d elements in #refs, not the HTML elements in #tests. */
+@namespace url(urn:not-html);
+
+input, select, button, textarea {
+ letter-spacing: initial;
+ word-spacing: initial;
+ line-height: initial;
+ text-transform: initial;
+ text-indent: initial;
+ text-shadow: initial;
+}
+input, select, textarea {
+ text-align: initial;
+}
+input[type=reset i], input[type=button i], input[type=submit i], button {
+ text-align: center;
+}
+input[type=radio i], input[type=checkbox i], input[type=reset i], input[type=button i],
+input[type=submit i], input[type=color i], input[type=search i], select, button {
+ box-sizing: border-box;
+}
+input, button {
+ display: inline-block;
+}
+input:not([type=image i], [type=range i], [type=checkbox i], [type=radio i]) {
+ overflow: clip;
+ overflow-clip-margin: 0;
+}
+/* in spec prose: */ select, textarea, meter, progress {
+ display: inline-block;
+}
+input[type=hidden i] { display: none !important; }
+marquee {
+ text-align: initial;
+}
+table { display: table; box-sizing: border-box; }
+caption { display: table-caption; }
+colgroup, colgroup[hidden] { display: table-column-group; }
+col, col[hidden] { display: table-column; }
+thead, thead[hidden] { display: table-header-group; }
+tbody, tbody[hidden] { display: table-row-group; }
+tfoot, tfoot[hidden] { display: table-footer-group; }
+tr, tr[hidden] { display: table-row; }
+td, th { display: table-cell; }
+table {
+ text-indent: initial;
+}
+</style>
+
+<div id="tests">
+ <input type="hidden">
+ <input type="text">
+ <input type="search">
+ <input type="tel">
+ <input type="url">
+ <input type="email">
+ <input type="password">
+ <input type="date">
+ <input type="month">
+ <input type="week">
+ <input type="time">
+ <input type="datetime-local">
+ <input type="number">
+ <input type="range">
+ <input type="color">
+ <input type="checkbox">
+ <input type="radio">
+ <input type="file">
+ <input type="submit">
+ <input type="image">
+ <input type="reset">
+ <input type="button">
+ <select><optgroup><option></select>
+ <select multiple></select>
+ <optgroup></optgroup>
+ <option></option>
+ <button></button>
+ <textarea></textarea>
+ <table><tbody><tr><td></table>
+ <marquee></marquee>
+</div>
+
+<div id="refs"></div>
+
+<script>
+ const props = ['letter-spacing',
+ 'word-spacing',
+ 'line-height',
+ 'text-transform',
+ 'text-indent',
+ 'text-shadow',
+ 'text-align',
+ 'display',
+ 'overflow',
+ 'overflow-clip-margin',
+ 'box-sizing'];
+ runUAStyleTests(props);
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-fixedpos-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-fixedpos-crash.html
new file mode 100644
index 0000000000..47046f6c25
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-fixedpos-crash.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel=author href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel=author href="https://mozilla.org" title="Mozilla">
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1741776">
+<style>
+#a {
+ rotate: 1deg 1 0 44;
+ filter: drop-shadow(81px 6px 0px red);
+}
+</style>
+<script>
+window.onload = function() {
+ document.getElementById("b").appendChild(document.getElementById("c"));
+}
+</script>
+<select id="a" multiple="multiple">
+ <option id="b">x</option>
+</select>
+<div id="c" style="position: fixed; top: 0; left: 0;">
+ x
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001-ref.html
new file mode 100644
index 0000000000..c59a6e1d98
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001-ref.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Reference for sizing of select elements, with wide vs. empty option selected</title>
+ <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+ <style>
+ select {
+ color: transparent;
+ margin: 1px;
+ }
+ div.customBorder > select {
+ /* This class is to let us test select elements *without* native theming
+ (for browsers that have both native and non-native controls): */
+ border: 3px solid black;
+ }
+ </style>
+</head>
+<body>
+ <div>
+ <select>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <select>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <select>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <select>
+ <option>some wide option</option>
+ </select>
+ </div>
+
+ <!-- This is the same as above, but now with a custom border on the
+ select elements: -->
+ <div class="customBorder">
+ <select>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <select>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <select>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <select>
+ <option>some wide option</option>
+ </select>
+ </div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001.html
new file mode 100644
index 0000000000..e8db3eae1d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/select-sizing-001.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for sizing of select elements, with wide vs. empty option selected</title>
+ <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#list-box">
+ <link rel="match" href="select-sizing-001-ref.html">
+ <style>
+ select {
+ color: transparent;
+ margin: 1px;
+ }
+ div.customBorder > select {
+ /* This class is to let us test select elements *without* native theming
+ (for browsers that have both native and non-native controls): */
+ border: 3px solid black;
+ }
+ </style>
+</head>
+<body>
+ <div>
+ <!-- Wide thing is 2nd, and not selected: -->
+ <select>
+ <option></option>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <!-- Wide thing is 2nd, and selected: -->
+ <select>
+ <option></option>
+ <option selected>some wide option</option>
+ </select>
+ <br>
+ <!-- Wide thing is 1st, and selected (implicitly): -->
+ <select>
+ <option>some wide option</option>
+ <option></option>
+ </select>
+ <br>
+ <!-- Wide thing is 1st, and not selected: -->
+ <select>
+ <option>some wide option</option>
+ <option selected></option>
+ </select>
+ </div>
+
+ <!-- This is the same as above, but now with a custom border on the
+ select elements: -->
+ <div class="customBorder">
+ <!-- Wide thing is 2nd, and not selected: -->
+ <select>
+ <option></option>
+ <option>some wide option</option>
+ </select>
+ <br>
+ <!-- Wide thing is 2nd, and selected: -->
+ <select>
+ <option></option>
+ <option selected>some wide option</option>
+ </select>
+ <br>
+ <!-- Wide thing is 1st, and selected (implicitly): -->
+ <select>
+ <option>some wide option</option>
+ <option></option>
+ </select>
+ <br>
+ <!-- Wide thing is 1st, and not selected: -->
+ <select>
+ <option>some wide option</option>
+ <option selected></option>
+ </select>
+ </div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform-ref.html
new file mode 100644
index 0000000000..5dc26a78db
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform-ref.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+</head>
+<body>
+ <span>THIS TEXT SHOULD BE UPPER-CASE.</span><br />
+
+ <input type="text" value="this text should be lower-case."><br />
+
+ <select>
+ <option>this text should be lower-case.</option>
+ </select><br />
+ <select multiple>
+ <option>this text should be lower-case.</option>
+ </select><br />
+ <select multiple>
+ <optgroup label="this text should be lower-case.">
+ <option>this text should be lower-case.</option>
+ </optgroup>
+ </select><br />
+
+ <select>
+ <option>THIS TEXT SHOULD BE UPPER-CASE.</option>
+ </select><br />
+ <select multiple>
+ <option>THIS TEXT SHOULD BE UPPER-CASE.</option>
+ </select><br />
+ <select multiple>
+ <optgroup label="THIS TEXT SHOULD BE UPPER-CASE.">
+ <option>THIS TEXT SHOULD BE UPPER-CASE.</option>
+ </optgroup>
+ </select><br />
+
+ <button>this text should be lower-case.</button><br />
+
+ <textarea>this text should be lower-case.</textarea><br />
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform.html
new file mode 100644
index 0000000000..f57f092982
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/text-transform.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <link rel="match" href="text-transform-ref.html">
+</head>
+<body style="text-transform: uppercase;">
+ <span>this text should be upper-case.</span><br />
+
+ <input type="text" value="this text should be lower-case."><br />
+
+ <select>
+ <option>this text should be lower-case.</option>
+ </select><br />
+ <select multiple>
+ <option>this text should be lower-case.</option>
+ </select><br />
+ <select multiple>
+ <optgroup label="this text should be lower-case.">
+ <option>this text should be lower-case.</option>
+ </optgroup>
+ </select><br />
+
+ <select style="text-transform: uppercase;">
+ <option>this text should be upper-case.</option>
+ </select><br />
+ <select multiple style="text-transform: uppercase;">
+ <option>this text should be upper-case.</option>
+ </select><br />
+ <select multiple style="text-transform: uppercase;">
+ <optgroup label="this text should be upper-case.">
+ <option>this text should be upper-case.</option>
+ </optgroup>
+ </select><br />
+
+ <button>this text should be lower-case.</button><br />
+
+ <textarea>this text should be lower-case.</textarea><br />
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display-ref.html
new file mode 100644
index 0000000000..8093efe5e8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display-ref.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+<title>Reference: toggle 'display' test</title>
+
+<div id="tests">
+ <input type="hidden">
+ <input type="text">
+ <input type="text" value="a">
+ <input type="search">
+ <input type="search" value="a">
+ <input type="tel">
+ <input type="tel" value="123456789">
+ <input type="url">
+ <input type="url" value="http://a">
+ <input type="email">
+ <input type="email" value="a">
+ <input type="password">
+ <input type="password" value="a">
+ <input type="date">
+ <input type="month">
+ <input type="week">
+ <input type="time">
+ <input type="datetime-local">
+ <input type="number">
+ <input type="range">
+ <input type="color">
+ <input type="checkbox">
+ <input type="radio">
+ <input type="file">
+ <input type="submit">
+ <input type="image">
+ <input type="reset">
+ <input type="button">
+ <input type="button" value="a">
+ <select><optgroup><option></select>
+ <select><optgroup><option>a</select>
+ <select multiple></select>
+ <select multiple><optgroup>a</optgroup><option>b</option></select>
+ <optgroup></optgroup>
+ <option></option>
+ <button></button>
+ <button>a</button>
+ <textarea></textarea>
+ <textarea>a</textarea>
+</div>
+
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display.html
new file mode 100644
index 0000000000..127679b2ad
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/toggle-display.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>toggle 'display' test</title>
+<meta charset="utf-8">
+<link rel="match" href="toggle-display-ref.html">
+
+<div id="tests">
+ <input type="hidden">
+ <input type="text">
+ <input type="text" value="a">
+ <input type="search">
+ <input type="search" value="a">
+ <input type="tel">
+ <input type="tel" value="123456789">
+ <input type="url">
+ <input type="url" value="http://a">
+ <input type="email">
+ <input type="email" value="a">
+ <input type="password">
+ <input type="password" value="a">
+ <input type="date">
+ <input type="month">
+ <input type="week">
+ <input type="time">
+ <input type="datetime-local">
+ <input type="number">
+ <input type="range">
+ <input type="color">
+ <input type="checkbox">
+ <input type="radio">
+ <input type="file">
+ <input type="submit">
+ <input type="image">
+ <input type="reset">
+ <input type="button">
+ <input type="button" value="a">
+ <select><optgroup><option></select>
+ <select><optgroup><option>a</select>
+ <select multiple></select>
+ <select multiple><optgroup>a</optgroup><option>b</option></select>
+ <optgroup></optgroup>
+ <option></option>
+ <button></button>
+ <button>a</button>
+ <textarea></textarea>
+ <textarea>a</textarea>
+</div>
+
+<script>
+window.onload = () => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ let tests = document.querySelector('#tests');
+ tests.style.display = 'none';
+ requestAnimationFrame(() => {
+ tests.style.display = '';
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ });
+ });
+}
+</script>
+
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/hidden-elements.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/hidden-elements.html
new file mode 100644
index 0000000000..e1bcf508e5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/hidden-elements.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<link rel=help href="https://html.spec.whatwg.org/#hidden-elements">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div hidden></div>
+<embed hidden>
+<embed hidden=until-found>
+<script>
+const kNotHiddenElementLocalNames = [
+ "source", "track",
+];
+
+const kHiddenElementLocalNames = [
+ "area", "base", "basefont", "datalist", "head", "link", "meta", "noembed",
+ "noframes", "param", "rp", "script", "style", "template", "title",
+];
+
+for (let name of kNotHiddenElementLocalNames) {
+ test(function() {
+ let element = document.createElement(name);
+ document.body.appendChild(element);
+ assert_equals(getComputedStyle(element).display, "inline");
+ }, `${name} should not be hidden`);
+}
+
+for (let name of kHiddenElementLocalNames) {
+ test(function() {
+ let element = document.createElement(name);
+ document.body.appendChild(element);
+ assert_equals(getComputedStyle(element).display, "none");
+ }, `${name} should be hidden`);
+}
+
+test(function() {
+ assert_equals(getComputedStyle(document.querySelector("div[hidden]")).display, "none");
+}, `div[hidden] element should be hidden`);
+
+test(function() {
+ const el = document.querySelector("embed[hidden='']");
+ assert_equals(getComputedStyle(el).display, "inline");
+ assert_equals(getComputedStyle(el).width, "0px");
+ assert_equals(getComputedStyle(el).height, "0px");
+}, `embed[hidden=''] element should be inline 0x0`);
+
+test(function() {
+ const el = document.querySelector("embed[hidden='until-found']");
+ assert_equals(getComputedStyle(el).display, "inline");
+ assert_equals(getComputedStyle(el).width, "0px");
+ assert_equals(getComputedStyle(el).height, "0px");
+ assert_equals(getComputedStyle(el).contentVisibility, "visible");
+}, `embed[hidden='until-found'] element should be inline 0x0`);
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/TODO-lists.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/TODO-lists.html
new file mode 100644
index 0000000000..6d79efc384
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/TODO-lists.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<ol><div><li>A</div></ol>
+<ol><div><li>A</div> <li>B</ol>
+<ol><div><li>A</div><div><li>B</div></ol>
+<ol reversed><div><li>A</div> <li>B</ol>
+<ol><div style=display:list-item>A</div><li>B</ol>
+<ol reversed><div style=display:list-item>A</div><li>B</ol>
+<ol reversed>
+ <div><li>Two</li></div>
+ <li>One</li>
+ <li>Zero</li>
+</ol>
+<ol reversed>
+ <li>Three</li>
+ <li style="display: none"></li>
+ <li>Two</li>
+</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type-ref.html
new file mode 100644
index 0000000000..710b39ceca
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test reference</title>
+<dir>
+ <li>A
+</dir>
+<dir>
+ <li>B
+</dir>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type.html
new file mode 100644
index 0000000000..d1f0ae3fe3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/dir-type.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>&lt;dir&gt; doesn't map the type and start attributes to CSS</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1831863">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.com" title="Mozilla">
+<link rel="match" href="dir-type-ref.html">
+<dir type="square">
+ <li>A
+</dir>
+<dir type="decimal" start="5">
+ <li>B
+</dir>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-text-align.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-text-align.html
new file mode 100644
index 0000000000..0fc01f275c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-text-align.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>text-align: match-parent on li</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<ul dir=rtl><li dir=ltr></li></ul>
+<ul dir=ltr><li dir=rtl></li></ul>
+<script>
+ test(() => {
+ const li = document.querySelector('li[dir=ltr]');
+ assert_equals(getComputedStyle(li).textAlign, 'right');
+ }, '<ul dir=rtl><li dir=ltr>');
+
+ test(() => {
+ const li = document.querySelector('li[dir=rtl]');
+ assert_equals(getComputedStyle(li).textAlign, 'left');
+ }, '<ul dir=ltr><li dir=rtl>');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html
new file mode 100644
index 0000000000..0de7ff329c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>li@type: supported types</title>
+<style>
+ .decimal { list-style-type: decimal; }
+ .lower-alpha { list-style-type: lower-alpha; }
+ .upper-alpha { list-style-type: upper-alpha; }
+ .lower-roman { list-style-type: lower-roman; }
+ .upper-roman { list-style-type: upper-roman; }
+ .disc { list-style-type: disc; }
+ .circle { list-style-type: circle; }
+ .square { list-style-type: square; }
+ .none { list-style-type: none; }
+</style>
+<li class="decimal">first item</li>
+<li class="lower-alpha">second item</li>
+<li class="upper-alpha">third item</li>
+<li class="lower-roman">fourth item</li>
+<li class="upper-roman">fifth item</li>
+<li class="disc">sixth item</li>
+<li class="circle">seventh item</li>
+<li class="square">eighth item</li>
+<li class="none">ninth item</li>
+<ol>
+ <li class="decimal">first ordered item</li>
+ <li class="lower-alpha">second ordered item</li>
+ <li class="upper-alpha">third ordered item</li>
+ <li class="lower-roman">fourth ordered item</li>
+ <li class="upper-roman">fifth ordered item</li>
+ <li class="disc">sixth ordered item</li>
+ <li class="circle">seventh ordered item</li>
+ <li class="square">eighth ordered item</li>
+ <li class="none">ninth ordered item</li>
+</ol>
+<ul>
+ <li class="decimal">first unordered item</li>
+ <li class="lower-alpha">second unordered item</li>
+ <li class="upper-alpha">third unordered item</li>
+ <li class="lower-roman">fourth unordered item</li>
+ <li class="upper-roman">fifth unordered item</li>
+ <li class="disc">sixth unordered item</li>
+ <li class="circle">seventh unordered item</li>
+ <li class="square">eighth unordered item</li>
+ <li class="none">ninth unordered item</li>
+</ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-xhtml.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-xhtml.xhtml
new file mode 100644
index 0000000000..7a7640e03f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-xhtml.xhtml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>li@type: supported types</title>
+<link rel="match" href="li-type-supported-ref.html"/>
+</head>
+<body>
+<li type="1">first item</li>
+<li type="a">second item</li>
+<li type="A">third item</li>
+<li type="i">fourth item</li>
+<li type="I">fifth item</li>
+<li type="disc">sixth item</li>
+<li type="circle">seventh item</li>
+<li type="square">eighth item</li>
+<li type="none">ninth item</li>
+<ol>
+ <li type="1">first ordered item</li>
+ <li type="a">second ordered item</li>
+ <li type="A">third ordered item</li>
+ <li type="i">fourth ordered item</li>
+ <li type="I">fifth ordered item</li>
+ <li type="disc">sixth ordered item</li>
+ <li type="circle">seventh ordered item</li>
+ <li type="square">eighth ordered item</li>
+ <li type="none">ninth ordered item</li>
+</ol>
+<ul>
+ <li type="1">first unordered item</li>
+ <li type="a">second unordered item</li>
+ <li type="A">third unordered item</li>
+ <li type="i">fourth unordered item</li>
+ <li type="I">fifth unordered item</li>
+ <li type="disc">sixth unordered item</li>
+ <li type="circle">seventh unordered item</li>
+ <li type="square">eighth unordered item</li>
+ <li type="none">ninth unordered item</li>
+</ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html
new file mode 100644
index 0000000000..ddd9024c11
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>li@type: supported types</title>
+<link rel=match href=li-type-supported-ref.html>
+<li type=1>first item</li>
+<li type=a>second item</li>
+<li type=A>third item</li>
+<li type=i>fourth item</li>
+<li type=I>fifth item</li>
+<li type=disc>sixth item</li>
+<li type=circle>seventh item</li>
+<li type=square>eighth item</li>
+<li type=none>ninth item</li>
+<ol>
+ <li type=1>first ordered item</li>
+ <li type=a>second ordered item</li>
+ <li type=A>third ordered item</li>
+ <li type=i>fourth ordered item</li>
+ <li type=I>fifth ordered item</li>
+ <li type=disc>sixth ordered item</li>
+ <li type=circle>seventh ordered item</li>
+ <li type=square>eighth ordered item</li>
+ <li type=none>ninth ordered item</li>
+</ol>
+<ul>
+ <li type=1>first unordered item</li>
+ <li type=a>second unordered item</li>
+ <li type=A>third unordered item</li>
+ <li type=i>fourth unordered item</li>
+ <li type=I>fifth unordered item</li>
+ <li type=disc>sixth unordered item</li>
+ <li type=circle>seventh unordered item</li>
+ <li type=square>eighth unordered item</li>
+ <li type=none>ninth unordered item</li>
+</ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-alpha.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-alpha.html
new file mode 100644
index 0000000000..81babe7888
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-alpha.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>li@type: unsupported type: lower-alpha</title>
+<link rel=match href=li-type-unsupported-ref.html>
+<li type=lower-alpha>first item</li>
+<li type=LOWER-ALPHA>second item</li>
+<ol>
+ <li type=lower-alpha>first ordered item</li>
+ <li type=LOWER-ALPHA>second ordered item</li>
+</ol>
+<ul>
+ <li type=lower-alpha>first unordered item</li>
+ <li type=LOWER-ALPHA>second unordered item</li>
+</ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-roman.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-roman.html
new file mode 100644
index 0000000000..e01cfdb72d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-lower-roman.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>li@type: unsupported type: lower-roman</title>
+<link rel=match href=li-type-unsupported-ref.html>
+<li type=lower-roman>first item</li>
+<li type=LOWER-ROMAN>second item</li>
+<ol>
+ <li type=lower-roman>first ordered item</li>
+ <li type=LOWER-ROMAN>second ordered item</li>
+</ol>
+<ul>
+ <li type=lower-roman>first unordered item</li>
+ <li type=LOWER-ROMAN>second unordered item</li>
+</ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-ref.html
new file mode 100644
index 0000000000..4fbc5aca97
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-ref.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>li@type: unsupported types</title>
+<li>first item</li>
+<li>second item</li>
+<ol>
+ <li>first ordered item</li>
+ <li>second ordered item</li>
+</ol>
+<ul>
+ <li>first unordered item</li>
+ <li>second unordered item</li>
+</ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-alpha.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-alpha.html
new file mode 100644
index 0000000000..2efb65dbda
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-alpha.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>li@type: unsupported type: upper-alpha</title>
+<link rel=match href=li-type-unsupported-ref.html>
+<li type=upper-alpha>first item</li>
+<li type=UPPER-ALPHA>second item</li>
+<ol>
+ <li type=upper-alpha>first ordered item</li>
+ <li type=UPPER-ALPHA>second ordered item</li>
+</ol>
+<ul>
+ <li type=upper-alpha>first unordered item</li>
+ <li type=UPPER-ALPHA>second unordered item</li>
+</ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-roman.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-roman.html
new file mode 100644
index 0000000000..bd8dafc9c2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-unsupported-upper-roman.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>li@type: unsupported type: upper-roman</title>
+<link rel=match href=li-type-unsupported-ref.html>
+<li type=upper-roman>first item</li>
+<li type=UPPER-ROMAN>second item</li>
+<ol>
+ <li type=upper-roman>first ordered item</li>
+ <li type=UPPER-ROMAN>second ordered item</li>
+</ol>
+<ul>
+ <li type=upper-roman>first unordered item</li>
+ <li type=UPPER-ROMAN>second unordered item</li>
+</ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-presentational-hints-ascii-case-insensitive.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-presentational-hints-ascii-case-insensitive.html
new file mode 100644
index 0000000000..396055f73b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-presentational-hints-ascii-case-insensitive.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#lists:presentational-hints">
+<link rel="help" href="https://drafts.csswg.org/selectors-4/#attribute-case">
+<meta name="assert" content="ul@type + li@type values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<ul type="circle"><li type="disc"></ul>
+<ul type="circle"><li type="DiSc"></ul>
+<ul type="circle"><li type="diſc"></ul>
+<ul type="circle"><li type="square"></ul>
+<ul type="circle"><li type="SqUaRe"></ul>
+<ul type="circle"><li type="Å¿quare"></ul>
+<script>
+const li = document.querySelectorAll("li");
+
+test(() => {
+ assert_equals(getComputedStyle(li[0]).getPropertyValue("list-style-type"),
+ "disc", "lowercase valid");
+ assert_equals(getComputedStyle(li[1]).getPropertyValue("list-style-type"),
+ "disc", "mixed case valid");
+ assert_equals(getComputedStyle(li[2]).getPropertyValue("list-style-type"),
+ "circle", "non-ASCII invalid");
+}, "keyword disc");
+
+test(() => {
+ assert_equals(getComputedStyle(li[3]).getPropertyValue("list-style-type"),
+ "square", "lowercase valid");
+ assert_equals(getComputedStyle(li[4]).getPropertyValue("list-style-type"),
+ "square", "mixed case valid");
+ assert_equals(getComputedStyle(li[5]).getPropertyValue("list-style-type"),
+ "circle", "non-ASCII invalid");
+}, "keyword square");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-styles.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-styles.html
new file mode 100644
index 0000000000..a5f011cecc
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/lists-styles.html
@@ -0,0 +1,228 @@
+<!doctype html>
+<title>default styles and preshints for ol, ul, menu, li, dir, dl, dt, dd</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/rendering/support/test-ua-stylesheet.js"></script>
+<style>
+/* Specify this bogus namespace, so the rules in this stylesheet only apply to the `fakeClone`d elements in #refs, not the HTML elements in #tests. */
+@namespace url(urn:not-html);
+
+dir, dd, dl, dt, menu, ol, ul { display: block; }
+li { display: list-item; text-align: match-parent; }
+
+dir, dl, menu, ol, ul { margin-block-start: 1em; margin-block-end: 1em; }
+
+:is(dir, dl, menu, ol, ul) :is(dir, dl, menu, ol, ul) {
+ margin-block-start: 0; margin-block-end: 0;
+}
+
+dd { margin-inline-start: 40px; }
+dir, menu, ol, ul { padding-inline-start: 40px; }
+
+ol, ul, menu { counter-reset: list-item; }
+ol { list-style-type: decimal; }
+
+dir, menu, ul {
+ list-style-type: disc;
+}
+:is(dir, menu, ol, ul) :is(dir, menu, ul) {
+ list-style-type: circle;
+}
+:is(dir, menu, ol, ul) :is(dir, menu, ol, ul) :is(dir, menu, ul) {
+ list-style-type: square;
+}
+
+/* preshints */
+ol[type="1"], li[type="1"] { list-style-type: decimal; }
+/* use classes due to lack of support for "s" annotation */
+ol[class=type-a], li[class=type-a] { list-style-type: lower-alpha; }
+ol[class=type-A], li[class=type-A] { list-style-type: upper-alpha; }
+ol[class=type-i], li[class=type-i] { list-style-type: lower-roman; }
+ol[class=type-I], li[class=type-I] { list-style-type: upper-roman; }
+ul[type=none i], li[type=none i] { list-style-type: none; }
+ul[type=disc i], li[type=disc i] { list-style-type: disc; }
+ul[type=circle i], li[type=circle i] { list-style-type: circle; }
+ul[type=square i], li[type=square i] { list-style-type: square; }
+
+li[value="10"], li[value="10xyz"], li[value="10e10"] { counter-set: list-item 10; }
+ol[start="10"], ol[start="10xyz"], ol[start="10e10"] { counter-reset: list-item 9; }
+ol[reversed] { counter-reset: reversed(list-item); }
+ol[reversed][start="20"], ol[reversed][start="20xyz"], ol[reversed][start="20e10"] { counter-reset: reversed(list-item) 21; }
+
+/* dir="" */
+[dir=ltr] { direction: ltr; }
+[dir=rtl] { direction: rtl; }
+</style>
+
+<div id="log"></div>
+
+<div id="tests">
+ <li></li>
+ <dir>
+ <li></li>
+ </dir>
+ <dt></dt>
+ <dd></dd>
+ <dl>
+ <dt></dt>
+ <dd></dd>
+ </dl>
+ <menu>
+ <li></li>
+ </menu>
+ <ol>
+ <li></li>
+ </ol>
+ <ul>
+ <li></li>
+ </ul>
+ <dir data-skip><dir></dir></dir>
+ <dir data-skip><menu></menu></dir>
+ <dir data-skip><ul></ul></dir>
+ <menu data-skip><dir></dir></menu>
+ <menu data-skip><menu></menu></menu>
+ <menu data-skip><ul></ul></menu>
+ <ol data-skip><dir></dir></ol>
+ <ol data-skip><menu></menu></ol>
+ <ol data-skip><ul></ul></ol>
+ <ul data-skip><dir></dir></ul>
+ <ul data-skip><menu></menu></ul>
+ <ul data-skip><ul></ul></ul>
+
+ <dir data-skip><dir data-skip><dir></dir></dir></dir>
+ <dir data-skip><dir data-skip><menu></menu></dir></dir>
+ <dir data-skip><dir data-skip><ul></ul></dir></dir>
+ <dir data-skip><menu data-skip><dir></dir></menu></dir>
+ <dir data-skip><menu data-skip><menu></menu></menu></dir>
+ <dir data-skip><menu data-skip><ul></ul></menu></dir>
+ <dir data-skip><ol data-skip><dir></dir></ol></dir>
+ <dir data-skip><ol data-skip><menu></menu></ol></dir>
+ <dir data-skip><ol data-skip><ul></ul></ol></dir>
+ <dir data-skip><ul data-skip><dir></dir></ul></dir>
+ <dir data-skip><ul data-skip><menu></menu></ul></dir>
+ <dir data-skip><ul data-skip><ul></ul></ul></dir>
+
+ <menu data-skip><dir data-skip><dir></dir></dir></menu>
+ <menu data-skip><dir data-skip><menu></menu></dir></menu>
+ <menu data-skip><dir data-skip><ul></ul></dir></menu>
+ <menu data-skip><menu data-skip><dir></dir></menu></menu>
+ <menu data-skip><menu data-skip><menu></menu></menu></menu>
+ <menu data-skip><menu data-skip><ul></ul></menu></menu>
+ <menu data-skip><ol data-skip><dir></dir></ol></menu>
+ <menu data-skip><ol data-skip><menu></menu></ol></menu>
+ <menu data-skip><ol data-skip><ul></ul></ol></menu>
+ <menu data-skip><ul data-skip><dir></dir></ul></menu>
+ <menu data-skip><ul data-skip><menu></menu></ul></menu>
+ <menu data-skip><ul data-skip><ul></ul></ul></menu>
+
+ <ol data-skip><dir data-skip><dir></dir></dir></ol>
+ <ol data-skip><dir data-skip><menu></menu></dir></ol>
+ <ol data-skip><dir data-skip><ul></ul></dir></ol>
+ <ol data-skip><menu data-skip><dir></dir></menu></ol>
+ <ol data-skip><menu data-skip><menu></menu></menu></ol>
+ <ol data-skip><menu data-skip><ul></ul></menu></ol>
+ <ol data-skip><ol data-skip><dir></dir></ol></ol>
+ <ol data-skip><ol data-skip><menu></menu></ol></ol>
+ <ol data-skip><ol data-skip><ul></ul></ol></ol>
+ <ol data-skip><ul data-skip><dir></dir></ul></ol>
+ <ol data-skip><ul data-skip><menu></menu></ul></ol>
+ <ol data-skip><ul data-skip><ul></ul></ul></ol>
+
+ <ul data-skip><dir data-skip><dir></dir></dir></ul>
+ <ul data-skip><dir data-skip><menu></menu></dir></ul>
+ <ul data-skip><dir data-skip><ul></ul></dir></ul>
+ <ul data-skip><menu data-skip><dir></dir></menu></ul>
+ <ul data-skip><menu data-skip><menu></menu></menu></ul>
+ <ul data-skip><menu data-skip><ul></ul></menu></ul>
+ <ul data-skip><ol data-skip><dir></dir></ol></ul>
+ <ul data-skip><ol data-skip><menu></menu></ol></ul>
+ <ul data-skip><ol data-skip><ul></ul></ol></ul>
+ <ul data-skip><ul data-skip><dir></dir></ul></ul>
+ <ul data-skip><ul data-skip><menu></menu></ul></ul>
+ <ul data-skip><ul data-skip><ul></ul></ul></ul>
+
+ <ol type="1"></ol>
+ <ul type="1"></ul>
+ <li type="1"></li>
+ <ol type="a" class="type-a"></ol>
+ <ul type="a" class="type-a"></ul>
+ <li type="a" class="type-a"></li>
+ <ol type="A" class="type-A"></ol>
+ <ul type="A" class="type-A"></ul>
+ <li type="A" class="type-A"></li>
+ <ol type="i" class="type-i"></ol>
+ <ul type="i" class="type-i"></ul>
+ <li type="i" class="type-i"></li>
+ <ol type="I" class="type-I"></ol>
+ <ul type="I" class="type-I"></ul>
+ <li type="I" class="type-I"></li>
+ <ol type="none"></ol>
+ <ul type="none"></ul>
+ <li type="none"></li>
+ <ol type="NONE"></ol>
+ <ul type="NONE"></ul>
+ <li type="NONE"></li>
+ <ol type="disc"></ol>
+ <ul type="disc"></ul>
+ <li type="disc"></li>
+ <ol type="DISC"></ol>
+ <ul type="DISC"></ul>
+ <li type="DISC"></li>
+ <ol type="circle"></ol>
+ <ul type="circle"></ul>
+ <li type="circle"></li>
+ <ol type="CIRCLE"></ol>
+ <ul type="CIRCLE"></ul>
+ <li type="CIRCLE"></li>
+ <ol type="square"></ol>
+ <ul type="square"></ul>
+ <li type="square"></li>
+ <ol type="SQUARE"></ol>
+ <ul type="SQUARE"></ul>
+ <li type="SQUARE"></li>
+
+ <ol>
+ <li value="10"></li>
+ <li value="10xyz"></li>
+ <li value="10e10"></li>
+ <li value="xyz"></li>
+ </ol>
+
+ <ol start="10"><li></li></ol>
+ <ol start="10xyz"><li></li></ol>
+ <ol start="10e10"><li></li></ol>
+ <ol start="xyz"><li></li></ol>
+ <ol reversed><li></li></ol>
+ <ol reversed start="20"><li></li></ol>
+ <ol reversed start="20xyz"><li></li></ol>
+ <ol reversed start="20e10"><li></li></ol>
+ <ol reversed start="xyz"><li></li></ol>
+
+ <ul data-skip dir="rtl"><li dir="ltr"></li></ul>
+ <ul data-skip dir="ltr"><li dir="rtl"></li></ul>
+
+</div>
+
+<div id="refs"></div>
+
+<script>
+ const props = [
+ 'display',
+ 'margin-top',
+ 'margin-right',
+ 'margin-bottom',
+ 'margin-left',
+ 'padding-top',
+ 'padding-right',
+ 'padding-bottom',
+ 'padding-left',
+ 'list-style-type',
+ 'counter-set',
+ 'counter-reset',
+ 'counter-increment',
+ 'text-align',
+ ];
+ runUAStyleTests(props);
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-display-contents-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-display-contents-ref.html
new file mode 100644
index 0000000000..f9fce33313
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-display-contents-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Reference for: display: contents; on &lt;ol ...></title>
+<style>
+ li { margin-left: 40px; list-style-type: decimal; }
+</style>
+<li value="1">The list item marker on this line should be "1."</li>
+<li value="2">The list item marker on this line should be "2."</li>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-display-contents.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-display-contents.html
new file mode 100644
index 0000000000..226570e935
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-display-contents.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>display: contents; on &lt;ol start></title>
+<meta rel=match href=ol-display-contents-ref.html>
+<ol start=5 style="display: contents">
+ <li style="margin-left: 40px">The list item marker on this line should be "1."</li>
+ <li style="margin-left: 40px">The list item marker on this line should be "2."</li>
+</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-reversed-display-contents.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-reversed-display-contents.html
new file mode 100644
index 0000000000..dd6fff0fd8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-start-reversed-display-contents.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>display: contents; on &lt;ol start reversed></title>
+<meta rel=match href=ol-display-contents-ref.html>
+<ol start=5 reversed style="display: contents">
+ <li style="margin-left: 40px">The list item marker on this line should be "1."</li>
+ <li style="margin-left: 40px">The list item marker on this line should be "2."</li>
+</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-ref.html
new file mode 100644
index 0000000000..fb61db3261
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-ref.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: supported types</title>
+<style>
+.decimal {
+ list-style-type: decimal;
+}
+.lower-alpha {
+ list-style-type: lower-alpha;
+}
+.upper-alpha {
+ list-style-type: upper-alpha;
+}
+.lower-roman {
+ list-style-type: lower-roman;
+}
+.upper-roman {
+ list-style-type: upper-roman;
+}
+</style>
+<ol class=decimal><li>1<li>2</ol>
+<ol class=lower-alpha><li>a<li>b</ol>
+<ol class=upper-alpha><li>A<li>B</ol>
+<ol class=lower-roman><li>i<li>ii</ol>
+<ol class=upper-roman><li>I<li>II</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-xhtml.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-xhtml.xhtml
new file mode 100644
index 0000000000..d7b949dab1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported-xhtml.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>ol@type: supported types</title>
+<link rel="match" href="ol-type-supported-ref.html"/>
+</head>
+<body>
+<ol type="1"><li>1</li><li>2</li></ol>
+<ol type="a"><li>a</li><li>b</li></ol>
+<ol type="A"><li>A</li><li>B</li></ol>
+<ol type="i"><li>i</li><li>ii</li></ol>
+<ol type="I"><li>I</li><li>II</li></ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported.html
new file mode 100644
index 0000000000..86ed3be383
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-supported.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: supported types</title>
+<link rel=match href=ol-type-supported-ref.html>
+<ol type=1><li>1<li>2</ol>
+<ol type=a><li>a<li>b</ol>
+<ol type=A><li>A<li>B</ol>
+<ol type=i><li>i<li>ii</ol>
+<ol type=I><li>I<li>II</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-circle.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-circle.html
new file mode 100644
index 0000000000..f3c52e43bd
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-circle.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: circle</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=circle><li>1<li>2</ol>
+<ol type=CIRCLE><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-disc.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-disc.html
new file mode 100644
index 0000000000..a0f41f3b1c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-disc.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: disc</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=disc><li>1<li>2</ol>
+<ol type=DISC><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html
new file mode 100644
index 0000000000..6c1198ef50
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: invalid</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=disk><li>1<li>2</ol>
+<ol type=DISK><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-alpha.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-alpha.html
new file mode 100644
index 0000000000..2fd656100f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-alpha.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: lower-alpha</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=lower-alpha><li>1<li>2</ol>
+<ol type=LOWER-ALPHA><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-roman.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-roman.html
new file mode 100644
index 0000000000..49f5b2888e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-lower-roman.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: lower-roman</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=lower-roman><li>1<li>2</ol>
+<ol type=LOWER-ROMAN><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-none.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-none.html
new file mode 100644
index 0000000000..bf800e1b11
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-none.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: none</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=none><li>1<li>2</ol>
+<ol type=NONE><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-ref.html
new file mode 100644
index 0000000000..530f8ef6c7
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported types</title>
+<ol><li>1<li>2</ol>
+<ol><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-round.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-round.html
new file mode 100644
index 0000000000..10a573687b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-round.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: round</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=round><li>1<li>2</ol>
+<ol type=ROUND><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-square.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-square.html
new file mode 100644
index 0000000000..b3e8937fb3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-square.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: square</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=square><li>1<li>2</ol>
+<ol type=SQUARE><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-alpha.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-alpha.html
new file mode 100644
index 0000000000..6a1ff97dbf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-alpha.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: upper-latin</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=upper-alpha><li>1<li>2</ol>
+<ol type=UPPER-ALPHA><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-roman.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-roman.html
new file mode 100644
index 0000000000..04cf451c67
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ol-type-unsupported-upper-roman.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ol@type: unsupported type: upper-roman</title>
+<link rel=match href=ol-type-unsupported-ref.html>
+<ol type=upper-roman><li>1<li>2</ol>
+<ol type=UPPER-ROMAN><li>1<li>2</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-ref.html
new file mode 100644
index 0000000000..59a0400cc8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-ref.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: supported types</title>
+<style>
+.disc {
+ list-style-type: disc;
+}
+.circle {
+ list-style-type: circle;
+}
+.square {
+ list-style-type: square;
+}
+.none {
+ list-style-type: none;
+}
+</style>
+<ul class="disc"><li>first disc</li><li>second disc</li></ul>
+<ul class="circle"><li>first circle</li><li>second circle</li></ul>
+<ul class="square"><li>first square</li><li>second square</li></ul>
+<ul class="none"><li>first none</li><li>second none</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml
new file mode 100644
index 0000000000..a2e5e0bbf6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>ul@type: supported types</title>
+<link rel="match" href="ul-type-supported-ref.html"/>
+</head>
+<body>
+<ul type="disc"><li>first disc</li><li>second disc</li></ul>
+<ul type="circle"><li>first circle</li><li>second circle</li></ul>
+<ul type="square"><li>first square</li><li>second square</li></ul>
+<ul type="none"><li>first none</li><li>second none</li></ul>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported.html
new file mode 100644
index 0000000000..c2449d7acd
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-supported.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: supported types</title>
+<link rel=match href=ul-type-supported-ref.html>
+<ul type=disc><li>first disc</li><li>second disc</li></ul>
+<ul type=circle><li>first circle</li><li>second circle</li></ul>
+<ul type=square><li>first square</li><li>second square</li></ul>
+<ul type=none><li>first none</li><li>second none</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-decimal.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-decimal.html
new file mode 100644
index 0000000000..0fb0e14abb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-decimal.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: unsupported type: decimal</title>
+<link rel=match href=ul-type-unsupported-ref.html>
+<ul type=decimal><li>first item</li><li>second item</li></ul>
+<ul type=DECIMAL><li>first item</li><li>second item</li></ul>
+<ul type=1><li>first item</li><li>second item</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html
new file mode 100644
index 0000000000..c6ee14eac8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: unsupported type: invalid</title>
+<link rel=match href=ul-type-unsupported-ref.html>
+<ul type=disk><li>first item</li><li>second item</li></ul>
+<ul type=DISK><li>first item</li><li>second item</li></ul>
+<ul type=x><li>first item</li><li>second item</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-alpha.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-alpha.html
new file mode 100644
index 0000000000..f31cc247ca
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-alpha.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: unsupported type: lower-alpha</title>
+<link rel=match href=ul-type-unsupported-ref.html>
+<ul type=lower-alpha><li>first item</li><li>second item</li></ul>
+<ul type=LOWER-ALPHA><li>first item</li><li>second item</li></ul>
+<ul type=a><li>first item</li><li>second item</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-roman.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-roman.html
new file mode 100644
index 0000000000..bd86861c9d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-lower-roman.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: unsupported type: lower-roman</title>
+<link rel=match href=ul-type-unsupported-ref.html>
+<ul type=lower-roman><li>first item</li><li>second item</li></ul>
+<ul type=LOWER-ROMAN><li>first item</li><li>second item</li></ul>
+<ul type=i><li>first item</li><li>second item</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-ref.html
new file mode 100644
index 0000000000..c53fe947f2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: unsupported types</title>
+<ul><li>first item</li><li>second item</li></ul>
+<ul><li>first item</li><li>second item</li></ul>
+<ul><li>first item</li><li>second item</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-alpha.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-alpha.html
new file mode 100644
index 0000000000..3f880f1dcd
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-alpha.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: unsupported type: upper-alpha</title>
+<link rel=match href=ul-type-unsupported-ref.html>
+<ul type=upper-alpha><li>first item</li><li>second item</li></ul>
+<ul type=UPPER-ALPHA><li>first item</li><li>second item</li></ul>
+<ul type=A><li>first item</li><li>second item</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-roman.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-roman.html
new file mode 100644
index 0000000000..d7f1295d63
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/ul-type-unsupported-upper-roman.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>ul@type: unsupported type: upper-roman</title>
+<link rel=match href=ul-type-unsupported-ref.html>
+<ul type=upper-roman><li>first item</li><li>second item</li></ul>
+<ul type=UPPER-ROMAN><li>first item</li><li>second item</li></ul>
+<ul type=I><li>first item</li><li>second item</li></ul>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/compare-computed-style.js b/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/compare-computed-style.js
new file mode 100644
index 0000000000..496bae3a10
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/compare-computed-style.js
@@ -0,0 +1,7 @@
+test(function() {
+ var testStyle = getComputedStyle(document.getElementById('test'));
+ var refStyle = getComputedStyle(document.getElementById('ref'));
+ for (var prop in testStyle) {
+ assert_equals(testStyle[prop], refStyle[prop], prop);
+ }
+});
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html
new file mode 100644
index 0000000000..905d7bc7cb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html
@@ -0,0 +1,7 @@
+<title>multicol default styles (quirks mode)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#multicol">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<multicol id=test></multicol>
+<asdfasdf id=ref></asdfasdf>
+<script src="compare-computed-style.js"></script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-standards-mode.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-standards-mode.html
new file mode 100644
index 0000000000..999cf42c76
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-standards-mode.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<title>multicol default styles (standards mode)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#multicol">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<multicol id=test></multicol>
+<asdfasdf id=ref></asdfasdf>
+<script src="compare-computed-style.js"></script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/br-wbr-content/content-property.tentative.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/br-wbr-content/content-property.tentative.html
new file mode 100644
index 0000000000..d814c0a076
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/br-wbr-content/content-property.tentative.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel=stylesheet href=/fonts/ahem.css>
+<link rel=match href=/css/reference/pass_if_square_96px_black.html>
+<link rel=help href=https://github.com/whatwg/html/issues/2291>
+<link rel=help href=https://drafts.csswg.org/css-content/#content-property>
+<style>
+.test, .ref {
+ font: 16px/1 Ahem;
+ margin: 0;
+}
+.test br {
+ /* This should have no affect. Per css-content, <string> when applied to elements. */
+ content: "";
+}
+</style>
+<p>Test passes if there is a square below.</p>
+<p class=test>xxxxxx<br>xxxxxx<br>xxxxxx</p>
+<p class=ref>xxxxxx<br>xxxxxx<br>xxxxxx</p>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-a.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-a.html
new file mode 100644
index 0000000000..2ac6f44649
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-a.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<link rel=match href=001-ref.html>
+<title>The font element text decoration color quirk, 001, almost standards mode</title>
+<style>[id] > * { color:fuchsia }</style>
+<div>Quirks should not apply:</div>
+<div><u>foo <font style="color:fuchsia">style</font> bar</u></div>
+<div><u>foo <font color="fuchsia">color</font> bar</u></div>
+<div><u>foo <font color="fuchsia" style="color:fuchsia">color and style</font> bar</u></div>
+<div><u>foo <font color="x" style="color:fuchsia">color=x and style</font> bar</u></div>
+<div><u>foo <font color="transparent" style="color:fuchsia">color=transparent and style</font> bar</u></div>
+<div><u>foo <font color="" style="color:fuchsia">color="" and style</font> bar</u></div>
+<div><u>foo <font style="display:block; color:fuchsia">block</font> bar</u></div>
+<div><s>foo <font color="fuchsia">line-through</font> bar</s></div>
+<div><u style="text-decoration:overline">foo <font color="fuchsia">overline</font> bar</u></div>
+<div><u>foo <span style="color:fuchsia">span</span> bar</u></div>
+<div><u id="no-namespace">FAIL (script didn't run)</u></div>
+<script>
+var a = document.getElementById('no-namespace');
+a.textContent = 'foo ';
+var elm = document.createElementNS('', 'font');
+elm.textContent = 'no-namespace font element';
+a.appendChild(elm);
+a.appendChild(document.createTextNode(' bar'));
+</script>
+<div><u id="uppercase">FAIL (script didn't run)</u></div>
+<script>
+var a = document.getElementById('uppercase');
+a.textContent = 'foo ';
+var elm = document.createElementNS('http://www.w3.org/1999/xhtml', 'FONT');
+elm.textContent = 'uppercase FONT element';
+a.appendChild(elm);
+a.appendChild(document.createTextNode(' bar'));
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-q.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-q.html
new file mode 100644
index 0000000000..846cb6e8af
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-q.html
@@ -0,0 +1,32 @@
+<link rel=match href=001-ref.html>
+<title>The font element text decoration color quirk, 001, quirks mode</title>
+<style>[id] > * { color:fuchsia }</style>
+<div>Quirks should not apply:</div>
+<div><u>foo <font style="color:fuchsia">style</font> bar</u></div>
+<div><u>foo <font color="fuchsia">color</font> bar</u></div>
+<div><u>foo <font color="fuchsia" style="color:fuchsia">color and style</font> bar</u></div>
+<div><u>foo <font color="x" style="color:fuchsia">color=x and style</font> bar</u></div>
+<div><u>foo <font color="transparent" style="color:fuchsia">color=transparent and style</font> bar</u></div>
+<div><u>foo <font color="" style="color:fuchsia">color="" and style</font> bar</u></div>
+<div><u>foo <font style="display:block; color:fuchsia">block</font> bar</u></div>
+<div><s>foo <font color="fuchsia">line-through</font> bar</s></div>
+<div><u style="text-decoration:overline">foo <font color="fuchsia">overline</font> bar</u></div>
+<div><u>foo <span style="color:fuchsia">span</span> bar</u></div>
+<div><u id="no-namespace">FAIL (script didn't run)</u></div>
+<script>
+var a = document.getElementById('no-namespace');
+a.textContent = 'foo ';
+var elm = document.createElementNS('', 'font');
+elm.textContent = 'no-namespace font element';
+a.appendChild(elm);
+a.appendChild(document.createTextNode(' bar'));
+</script>
+<div><u id="uppercase">FAIL (script didn't run)</u></div>
+<script>
+var a = document.getElementById('uppercase');
+a.textContent = 'foo ';
+var elm = document.createElementNS('http://www.w3.org/1999/xhtml', 'FONT');
+elm.textContent = 'uppercase FONT element';
+a.appendChild(elm);
+a.appendChild(document.createTextNode(' bar'));
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-ref.html
new file mode 100644
index 0000000000..fb632e693d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-ref.html
@@ -0,0 +1,20 @@
+<title>Reference for The font element text decoration color quirk, 001</title>
+<style>
+span, div > div { color:fuchsia }
+.under { text-decoration:underline }
+.over { text-decoration:overline }
+.strike { text-decoration:line-through }
+</style>
+<div>Quirks should not apply:</div>
+<div class="under">foo <span>style</span> bar</div>
+<div class="under">foo <span>color</span> bar</div>
+<div class="under">foo <span>color and style</span> bar</div>
+<div class="under">foo <span>color=x and style</span> bar</div>
+<div class="under">foo <span>color=transparent and style</span> bar</div>
+<div class="under">foo <span>color="" and style</span> bar</div>
+<div class="under">foo<div>block</div>bar</div>
+<div class="strike">foo <span>line-through</span> bar</div>
+<div class="over">foo <span>overline</span> bar</div>
+<div class="under">foo <span>span</span> bar</div>
+<div class="under">foo <span>no-namespace font element</span> bar</div>
+<div class="under">foo <span>uppercase FONT element</span> bar</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-s.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-s.html
new file mode 100644
index 0000000000..d781fab289
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-s.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<link rel=match href=001-ref.html>
+<title>The font element text decoration color quirk, 001, standards mode</title>
+<style>[id] > * { color:fuchsia }</style>
+<div>Quirks should not apply:</div>
+<div><u>foo <font style="color:fuchsia">style</font> bar</u></div>
+<div><u>foo <font color="fuchsia">color</font> bar</u></div>
+<div><u>foo <font color="fuchsia" style="color:fuchsia">color and style</font> bar</u></div>
+<div><u>foo <font color="x" style="color:fuchsia">color=x and style</font> bar</u></div>
+<div><u>foo <font color="transparent" style="color:fuchsia">color=transparent and style</font> bar</u></div>
+<div><u>foo <font color="" style="color:fuchsia">color="" and style</font> bar</u></div>
+<div><u>foo <font style="display:block; color:fuchsia">block</font> bar</u></div>
+<div><s>foo <font color="fuchsia">line-through</font> bar</s></div>
+<div><u style="text-decoration:overline">foo <font color="fuchsia">overline</font> bar</u></div>
+<div><u>foo <span style="color:fuchsia">span</span> bar</u></div>
+<div><u id="no-namespace">FAIL (script didn't run)</u></div>
+<script>
+var a = document.getElementById('no-namespace');
+a.textContent = 'foo ';
+var elm = document.createElementNS('', 'font');
+elm.textContent = 'no-namespace font element';
+a.appendChild(elm);
+a.appendChild(document.createTextNode(' bar'));
+</script>
+<div><u id="uppercase">FAIL (script didn't run)</u></div>
+<script>
+var a = document.getElementById('uppercase');
+a.textContent = 'foo ';
+var elm = document.createElementNS('http://www.w3.org/1999/xhtml', 'FONT');
+elm.textContent = 'uppercase FONT element';
+a.appendChild(elm);
+a.appendChild(document.createTextNode(' bar'));
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-x.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-x.xhtml
new file mode 100644
index 0000000000..623b346eea
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/001-x.xhtml
@@ -0,0 +1,22 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<link rel="match" href="001-ref.html"/>
+<title>The font element text decoration color quirk, 001, XHTML</title>
+<style>[id] > * { color:fuchsia }</style>
+</head>
+<body>
+<div>Quirks should not apply:</div>
+<div><u>foo <font style="color:fuchsia">style</font> bar</u></div>
+<div><u>foo <font color="fuchsia">color</font> bar</u></div>
+<div><u>foo <font color="fuchsia" style="color:fuchsia">color and style</font> bar</u></div>
+<div><u>foo <font color="x" style="color:fuchsia">color=x and style</font> bar</u></div>
+<div><u>foo <font color="transparent" style="color:fuchsia">color=transparent and style</font> bar</u></div>
+<div><u>foo <font color="" style="color:fuchsia">color="" and style</font> bar</u></div>
+<div><u>foo <font style="display:block; color:fuchsia">block</font> bar</u></div>
+<div><s>foo <font color="fuchsia">line-through</font> bar</s></div>
+<div><u style="text-decoration:overline">foo <font color="fuchsia">overline</font> bar</u></div>
+<div><u>foo <span style="color:fuchsia">span</span> bar</u></div>
+<div><u id="no-namespace">foo <font xmlns="">no-namespace font element</font> bar</u></div>
+<div><u id="uppercase">foo <FONT>uppercase FONT element</FONT> bar</u></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-face.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-face.html
new file mode 100644
index 0000000000..a37da3c45f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-face.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>font face</title>
+<link rel="help" href="https://html.spec.whatwg.org/#the-font-element-text-decoration-color-quirk">
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+const types = ["serif", "sans-serif", "cursive", "fantasy", "monospace"];
+for (let type of types) {
+ test(() => {
+ let elem = document.createElement("font");
+ elem.setAttribute("face", type);
+ document.body.appendChild(elem);
+ let exp_type = window.getComputedStyle(elem, null).getPropertyValue("font-family");
+ assert_equals(exp_type, type);
+ }, `font face attribute ${type} is correct`);
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-size.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-size.html
new file mode 100644
index 0000000000..8e8b9f28eb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/phrasing-content-0/font-element-text-decoration-color/font-size.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>font size</title>
+<link rel="help" href="https://html.spec.whatwg.org/#the-font-element-text-decoration-color-quirk">
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<script>
+
+const modes = ["", "+", "-"];
+const values = ["0", "1", "2", "3", "4", "5", "6", "7", "8"];
+const rels = [
+ ["1", "x-small"],
+ ["2", "small"],
+ ["3", "medium"],
+ ["4", "large"],
+ ["5", "x-large"],
+ ["6", "xx-large"],
+ ["7", "xxx-large"]
+];
+let results = getCSSFontSize(rels);
+
+function cal(n) {
+ //If value is greater than 7, let it be 7
+ //If value is less than 1, let it be 1
+ return n > 7 ? 7 : (n < 1 ? 1 : n);
+}
+
+function getRealInput(mode, value) {
+ switch(mode) {
+ case "+":
+ //If mode is relative-plus, then increment value by 3
+ return cal(3 + parseInt(value));
+ case "-":
+ // If mode is relative-minus, then let value be the result of subtracting value from 3
+ return cal(3 - parseInt(value));
+ default:
+ return cal(parseInt(value));
+ }
+}
+
+function getCSSFontSize(rels) {
+ let results = {};
+ for (let [key, value] of rels) {
+ if (key == "7") {
+ //The 'xxx-large' value is a non-CSS value used here to indicate a font size 50% larger than 'xx-large'.
+ let size = parseInt(results["6"]) * 1.5;
+ results[key] = size.toString();
+ return results;
+ }
+ let elem = document.createElement("span");
+ document.body.appendChild(elem);
+ elem.setAttribute("style", `font-size: ${value}`);
+ let exp_size = window.getComputedStyle(elem, null).getPropertyValue("font-size");
+ results[key] = exp_size.slice(0, -2);
+ }
+ return results;
+}
+
+for (let mode of modes) {
+ for (let value of values) {
+ test(() => {
+ let size = getRealInput(mode, value);
+ let elem = document.createElement("font");
+ elem.setAttribute("size", `${mode}${value}`);
+ document.body.appendChild(elem);
+ let exp_size = window.getComputedStyle(elem, null).getPropertyValue("font-size");
+ assert_equals(exp_size.slice(0, -2), results[size]);
+ }, `font size attribute ${mode}${value} is correct`);
+ }
+}
+
+test(() => {
+ let span_elem = document.createElement("span");
+ document.body.appendChild(span_elem);
+ span_elem.setAttribute("style", "font-size: medium");
+ let span_size = window.getComputedStyle(span_elem, null).getPropertyValue("font-size");
+
+ let font_elem = document.createElement("font");
+ document.body.appendChild(font_elem);
+ let font_size = window.getComputedStyle(font_elem, null).getPropertyValue("font-size");
+ assert_equals(font_size, span_size);
+
+ font_elem.setAttribute("size", "");
+ font_size = window.getComputedStyle(font_elem, null).getPropertyValue("font-size");
+ assert_equals(font_size, span_size);
+
+ font_elem.setAttribute("size", " ");
+ font_size = window.getComputedStyle(font_elem, null).getPropertyValue("font-size");
+ assert_equals(font_size, span_size);
+
+ font_elem.setAttribute("size", " 3");
+ font_size = window.getComputedStyle(font_elem, null).getPropertyValue("font-size");
+ assert_equals(font_size, span_size);
+
+ font_elem.setAttribute("size", "3");
+ font_size = window.getComputedStyle(font_elem, null).getPropertyValue("font-size");
+ assert_equals(font_size, span_size);
+}, "font size default value is 3");
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/sections-and-headings/headings-styles.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/sections-and-headings/headings-styles.html
new file mode 100644
index 0000000000..63e6a83e88
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/sections-and-headings/headings-styles.html
@@ -0,0 +1,152 @@
+<!doctype html>
+<title>default styles for h1..h6, hgroup, article, aside, nav, section</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/rendering/support/test-ua-stylesheet.js"></script>
+<style>
+/* Specify this bogus namespace, so the rules in this stylesheet only apply to the `fakeClone`d elements in #refs, not the HTML elements in #tests. */
+@namespace url(urn:not-html);
+
+article, aside, h1, h2, h3, h4, h5, h6, hgroup, nav, section {
+ display: block;
+}
+
+h1 { margin-block-start: 0.67em; margin-block-end: 0.67em; font-size: 2.00em; font-weight: bold; }
+h2 { margin-block-start: 0.83em; margin-block-end: 0.83em; font-size: 1.50em; font-weight: bold; }
+h3 { margin-block-start: 1.00em; margin-block-end: 1.00em; font-size: 1.17em; font-weight: bold; }
+h4 { margin-block-start: 1.33em; margin-block-end: 1.33em; font-size: 1.00em; font-weight: bold; }
+h5 { margin-block-start: 1.67em; margin-block-end: 1.67em; font-size: 0.83em; font-weight: bold; }
+h6 { margin-block-start: 2.33em; margin-block-end: 2.33em; font-size: 0.67em; font-weight: bold; }
+
+:is(article, aside, nav, section) h1 { margin-block-start: 0.83em; margin-block-end: 0.83em; font-size: 1.50em; }
+:is(article, aside, nav, section) :is(article, aside, nav, section) h1 { margin-block-start: 1.00em; margin-block-end: 1.00em; font-size: 1.17em; }
+:is(article, aside, nav, section) :is(article, aside, nav, section) :is(article, aside, nav, section) h1 { margin-block-start: 1.33em; margin-block-end: 1.33em; font-size: 1.00em; }
+:is(article, aside, nav, section) :is(article, aside, nav, section) :is(article, aside, nav, section) :is(article, aside, nav, section) h1 { margin-block-start: 1.67em; margin-block-end: 1.67em; font-size: 0.83em; }
+:is(article, aside, nav, section) :is(article, aside, nav, section) :is(article, aside, nav, section) :is(article, aside, nav, section) :is(article, aside, nav, section) h1 { margin-block-start: 2.33em; margin-block-end: 2.33em; font-size: 0.67em; }
+
+</style>
+
+<div id="log"></div>
+
+<div id="tests">
+ <h1></h1>
+ <h2></h2>
+ <h3></h3>
+ <h4></h4>
+ <h5></h5>
+ <h6></h6>
+ <hgroup></hgroup>
+ <article></article>
+ <aside></aside>
+ <nav></nav>
+ <section></section>
+ <article data-skip>
+ <h1></h1>
+ <article data-skip>
+ <h1></h1>
+ <article data-skip>
+ <h1></h1>
+ <article data-skip>
+ <h1></h1>
+ <article data-skip>
+ <h1></h1>
+ <hgroup data-skip>
+ <h1></h1>
+ <h2></h2>
+ <h3></h3>
+ <h4></h4>
+ <h5></h5>
+ </hgroup>
+ </article>
+ </article>
+ </article>
+ </article>
+ </article>
+ <aside data-skip>
+ <h1></h1>
+ <aside data-skip>
+ <h1></h1>
+ <aside data-skip>
+ <h1></h1>
+ <aside data-skip>
+ <h1></h1>
+ <aside data-skip>
+ <h1></h1>
+ <hgroup data-skip>
+ <h1></h1>
+ <h2></h2>
+ <h3></h3>
+ <h4></h4>
+ <h5></h5>
+ </hgroup>
+ </aside>
+ </aside>
+ </aside>
+ </aside>
+ </aside>
+ <nav data-skip>
+ <h1></h1>
+ <nav data-skip>
+ <h1></h1>
+ <nav data-skip>
+ <h1></h1>
+ <nav data-skip>
+ <h1></h1>
+ <nav data-skip>
+ <h1></h1>
+ <hgroup data-skip>
+ <h1></h1>
+ <h2></h2>
+ <h3></h3>
+ <h4></h4>
+ <h5></h5>
+ </hgroup>
+ </nav>
+ </nav>
+ </nav>
+ </nav>
+ </nav>
+ <section data-skip>
+ <h1></h1>
+ <section data-skip>
+ <h1></h1>
+ <section data-skip>
+ <h1></h1>
+ <section data-skip>
+ <h1></h1>
+ <section data-skip>
+ <h1></h1>
+ <hgroup data-skip>
+ <h1></h1>
+ <h2></h2>
+ <h3></h3>
+ <h4></h4>
+ <h5></h5>
+ </hgroup>
+ </section>
+ </section>
+ </section>
+ </section>
+ </section>
+</div>
+
+<div id="refs"></div>
+
+<script>
+ const props = [
+ 'display',
+ 'margin-top',
+ 'margin-right',
+ 'margin-bottom',
+ 'margin-left',
+ 'padding-top',
+ 'padding-right',
+ 'padding-bottom',
+ 'padding-left',
+ 'font-size',
+ 'font-weight',
+ ];
+ runUAStyleTests(props);
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign-ref.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign-ref.xhtml
new file mode 100644
index 0000000000..7d21ce1180
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign-ref.xhtml
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_bottom.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_bottom.xhtml
new file mode 100644
index 0000000000..8610f37938
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_bottom.xhtml
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_top.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_top.xhtml
new file mode 100644
index 0000000000..3f02219973
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/colgroup_valign_top.xhtml
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables-xhtml.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables-xhtml.xhtml
new file mode 100644
index 0000000000..6af44139a0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables-xhtml.xhtml
@@ -0,0 +1,23 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>UA style for form in table elements - XHTML</title>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2" />
+ <link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <table><form></form></table>
+ <table><thead><form></form></thead></table>
+ <table><tbody><form></form></tbody></table>
+ <table><tfoot><form></form></tfoot></table>
+ <table><tr><form></form></tr></table>
+ <script>
+ for (const form of document.querySelectorAll("form")) {
+ test(function() {
+ assert_equals(getComputedStyle(form).display, "block");
+ }, `Computed display of form inside ${form.parentNode.nodeName} in xhtml should be 'block'`);
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables.html
new file mode 100644
index 0000000000..611c8305b9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/form-in-tables.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>UA style for form in table elements</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .block { display: block !important }
+</style>
+<div id="display">
+ <table><form></form></table>
+ <table><thead><form></form></thead></table>
+ <table><tbody><form></form></tbody></table>
+ <table><tfoot><form></form></tfoot></table>
+ <table><tr><form></form></tr></table>
+</div>
+<div id="important">
+ <table><form class="block"></form></table>
+ <table><thead><form class="block"></form></thead></table>
+ <table><tbody><form class="block"></form></tbody></table>
+ <table><tfoot><form class="block"></form></tfoot></table>
+ <table><tr><form class="block"></form></tr></table>
+</div>
+<script>
+ for (const form of display.querySelectorAll("form")) {
+ test(function() {
+ assert_equals(getComputedStyle(form).display, "none");
+ }, `Computed display of form inside ${form.parentNode.nodeName} should be 'none'`);
+ }
+ for (const form of important.querySelectorAll("form")) {
+ test(function() {
+ assert_equals(getComputedStyle(form).display, "none");
+ }, `Computed display of form inside ${form.parentNode.nodeName} should be 'none' (!important UA style))`);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/hidden-attr.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/hidden-attr.html
new file mode 100644
index 0000000000..f06c3dc9b4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/hidden-attr.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>UA style for hidden attribute on table elements</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<table hidden></table>
+<table><caption hidden></caption></table>
+<table><colgroup hidden></table>
+<table><col hidden></table>
+<table><thead hidden></table>
+<table><tbody hidden></table>
+<table><tfoot hidden></table>
+<table><tr hidden></table>
+<table><tr><td hidden></table>
+<table><tr><th hidden></table>
+<script>
+const expectedDisplay = {
+ 'table': 'none',
+ 'caption': 'none',
+ 'colgroup': 'table-column-group',
+ 'col': 'table-column',
+ 'thead': 'table-header-group',
+ 'tbody': 'table-row-group',
+ 'tfoot': 'table-footer-group',
+ 'tr': 'table-row',
+ 'td': 'none',
+ 'th': 'none',
+};
+for (const el of document.querySelectorAll("[hidden]")) {
+ test(function() {
+ const style = getComputedStyle(el);
+ assert_equals(style.display, expectedDisplay[el.localName]);
+ if (el instanceof HTMLTableElement ||
+ el instanceof HTMLTableCaptionElement ||
+ el instanceof HTMLTableCellElement) {
+ assert_equals(style.visibility, 'visible');
+ } else {
+ assert_equals(style.visibility, 'collapse');
+ }
+ }, `Computed display and visibility of ${el.localName}`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/aqua-yellow-32x32.png b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/aqua-yellow-32x32.png
new file mode 100644
index 0000000000..42f8a2100b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/aqua-yellow-32x32.png
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/blue-16x20-green-16x20.png b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/blue-16x20-green-16x20.png
new file mode 100644
index 0000000000..9bf59ebdf1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/blue-16x20-green-16x20.png
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/fuchsia-32x32.png b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/fuchsia-32x32.png
new file mode 100644
index 0000000000..7902bc31e0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/fuchsia-32x32.png
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/red-32x32.png b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/red-32x32.png
new file mode 100644
index 0000000000..191e13ea11
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/red-32x32.png
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/yellow-32x32.png b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/yellow-32x32.png
new file mode 100644
index 0000000000..a45f8111b4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/resources/yellow-32x32.png
Binary files differ
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-attribute.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-attribute.html
new file mode 100644
index 0000000000..54acff0350
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-attribute.html
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Table attribute test</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ .div_tbl table {
+ width: 400px;
+ height: 300px;
+ border-spacing: 0px;
+ }
+ .div_tbl td {
+ padding: 0px;
+ }
+ .div_tbl th {
+ padding: 0px;
+ }
+ .div_200 {
+ height: 200px;
+ }
+</style>
+
+<div id="div">
+ <table id="table">
+ <thead id="thead">
+ <tr>
+ <th id="th">Month</th>
+ <th>Savings</th>
+ </tr>
+ </thead>
+ <tbody id="tbody">
+ <tr id="tr">
+ <td>January</td>
+ <td>$60</td>
+ </tr>
+ <tr>
+ <td id="td">February</td>
+ <td>$80</td>
+ </tr>
+ </tbody>
+ <tfoot id="tfoot">
+ <tr>
+ <td>Sum</td>
+ <td>$140</td>
+ </tr>
+ </tfoot>
+ </table>
+</div>
+
+<script>
+
+const ids = ["table", "thead", "tbody", "tfoot", "tr", "td", "th"];
+const alignIds = ["thead", "tbody", "tfoot", "tr", "td", "th"];
+const heightIds = ["tr", "td", "th"];
+const div = document.getElementById("div");
+const table = document.getElementById("table");
+const aligns = [
+ ["center", "center"],
+ ["middle", "center"],
+ ["left", "left"],
+ ["right", "right"],
+ ["justify", "justify"]
+];
+
+function commonTest(id, attr, value, cssProp, expected) {
+ test(t => {
+ let elem = document.getElementById(id);
+ t.add_cleanup(() => {
+ elem.removeAttribute(attr);
+ });
+ elem.setAttribute(attr, value);
+ let css = window.getComputedStyle(elem, null).getPropertyValue(cssProp);
+ assert_equals(css, expected);
+ }, `${id} ${attr} attribute is correct`);
+}
+
+function commonAlignTest(id, attr, value, cssProp, expected) {
+ test(t => {
+ let elem = document.getElementById(id);
+ t.add_cleanup(() => {
+ elem.removeAttribute(attr);
+ });
+ elem.setAttribute(attr, value);
+ let css = window.getComputedStyle(elem, null).getPropertyValue(cssProp);
+ assert_equals(css, expected);
+ }, `table ${id} align attribute ${value} is correct`);
+}
+
+function commonHeightTest(id, attr, value, cssProp, expected, type="", divClass) {
+ test(t => {
+ let elem = document.getElementById(id);
+ t.add_cleanup(() => {
+ elem.removeAttribute(attr);
+ div.classList.remove(divClass);
+ });
+ elem.setAttribute(attr, value);
+ div.classList.add(divClass);
+ let css = window.getComputedStyle(elem, null).getPropertyValue(cssProp);
+ assert_equals(css, expected);
+ }, `${id} ${attr} attribute ${type} is correct`);
+}
+
+// table#bordercolor
+commonTest("table", "bordercolor", "red", "border-color", "rgb(255, 0, 0)");
+// table#cellspacing
+commonTest("table", "cellspacing", "10", "border-spacing", "10px 10px", "10");
+
+// {table, thead, body, tfoot, tr, td, th}#background
+// {table, thead, body, tfoot, tr, td, th}#bgcolor
+const url = new URL('/images/threecolors.png', window.location.href).href;
+for (let id of ids) {
+ commonTest(id, "background", "/images/threecolors.png", "background-image", `url(\"${url}\")`);
+
+ commonTest(id, "bgcolor", "red", "background-color", "rgb(255, 0, 0)");
+}
+
+// {thead, body, tfoot, tr, td, th}#align#{center, middle, left, right, justify}
+for (let id of alignIds) {
+ for (let [value, expected] of aligns) {
+ commonAlignTest(id, "align", value, "text-align", expected);
+ }
+}
+
+// {tr, td, th}#height#pixel
+for (let id of heightIds) {
+ commonHeightTest(id, "height", "60", "height", "60px", "pixel", "div_tbl");
+}
+
+// {tr, td, th}#height#percentage
+let tbl = document.createElement("table");
+tbl.innerHTML = '<tr id="table_tr"><th id="table_th"></th></tr><tr><td id="table_td"></td></tr>';
+div.appendChild(tbl);
+const heightPercIds = ["table_tr", "table_td", "table_th"];
+for (let id of heightPercIds) {
+ commonHeightTest(id, "height", "20%", "height", "60px", "percentage", "div_tbl");
+}
+div.removeChild(tbl);
+
+// table#height#{pixel, percentage}
+commonHeightTest("table", "height", "180", "height", "180px", "pixel", "div_200");
+commonHeightTest("table", "height", "90%", "height", "180px", "90%", "div_200");
+commonHeightTest("table", "height", "110%", "height", "220px", "110%", "div_200");
+
+// table#cellpadding
+test(t => {
+ t.add_cleanup(() => {
+ table.removeAttribute("cellpadding");
+ });
+ table.setAttribute("cellpadding", "10");
+
+ let th = document.getElementById("th");
+ let th_css = window.getComputedStyle(th, null).getPropertyValue("padding");
+ assert_equals(th_css, "10px");
+
+ let td = document.getElementById("td");
+ let td_css = window.getComputedStyle(td, null).getPropertyValue("padding");
+ assert_equals(td_css, "10px");
+}, "table cellpadding attribute is correct");
+
+// th default text-align property is center
+test(t => {
+ let elem = document.getElementById("th");
+ let css = window.getComputedStyle(elem, null).getPropertyValue("text-align");
+ assert_equals(css, "center");
+}, "th default align attribute is center");
+
+// col#width#{pixel, percentage}
+test(t => {
+ let colgroup = document.createElement("colgroup");
+ let col1 = document.createElement("col");
+ let col2 = document.createElement("col");
+ t.add_cleanup(() => {
+ table.removeChild(colgroup);
+ div.classList.remove("div_tbl");
+ });
+ colgroup.appendChild(col1);
+ colgroup.appendChild(col2);
+ table.insertBefore(colgroup, table.firstChild);
+ div.classList.add("div_tbl");
+
+ col1.setAttribute("width", "100");
+ let td = document.getElementById("td");
+ let css = window.getComputedStyle(td, null).getPropertyValue("width");
+ assert_equals(css, "100px");
+
+ col1.setAttribute("width", "50%");
+ css = window.getComputedStyle(td, null).getPropertyValue("width");
+ assert_equals(css, "200px");
+}, "table col width attribute is correct");
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print-ref.html
new file mode 100644
index 0000000000..3aa0abd320
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<table style="background-image: url('resources/aqua-yellow-32x32.png')">
+ <thead style="background-image: url('resources/blue-16x20-green-16x20.png')">
+ <tr>
+ <td>
+ Foo
+ </td>
+ <td style="background-image: url('resources/yellow-32x32.png')">
+ Bar
+ </td>
+ </tr>
+ </thead>
+ <tbody style="background-image: url('resources/red-32x32.png')">
+ <tr>
+ <th style="background-image: url('resources/fuchsia-32x32.png')">
+ Foo
+ </th>
+ <th>
+ Bar
+ </th>
+ </tr>
+ <tr style="background-image: url('resources/fuchsia-32x32.png')">
+ <td>
+ Foo
+ </td>
+ <td style="background-image: url('resources/yellow-32x32.png')">
+ Bar
+ </td>
+ </tr>
+ </tbody>
+ <tfoot style="background-image: url('resources/yellow-32x32.png')">
+ <tr>
+ <td>
+ Baz
+ </td>
+ </tr>
+ </tfoot>
+</table>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html
new file mode 100644
index 0000000000..0cbaca6019
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<link rel="match" href="table-background-print-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="help" href="https://drafts.csswg.org/css-break/">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=816498">
+<body>
+<table background="resources/aqua-yellow-32x32.png">
+ <thead background="resources/blue-16x20-green-16x20.png">
+ <tr>
+ <td>
+ Foo
+ </td>
+ <td background="resources/yellow-32x32.png">
+ Bar
+ </td>
+ </tr>
+ </thead>
+ <tbody background="resources/red-32x32.png">
+ <tr>
+ <th background="resources/fuchsia-32x32.png">
+ Foo
+ </th>
+ <th>
+ Bar
+ </th>
+ </tr>
+ <tr background="resources/fuchsia-32x32.png">
+ <td>
+ Foo
+ </td>
+ <td background="resources/yellow-32x32.png">
+ Bar
+ </td>
+ </tr>
+ </tbody>
+ <tfoot background="resources/yellow-32x32.png">
+ <tr>
+ <td>
+ Baz
+ </td>
+ </tr>
+ </tfoot>
+</table>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1-ref.html
new file mode 100644
index 0000000000..ceac88e9a3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Table borders</title>
+<style>
+table {
+ border-width: 1px;
+ border-style: outset;
+}
+td {
+ border-width: 1px;
+ border-style: inset;
+}
+</style>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1.html
new file mode 100644
index 0000000000..3338813995
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<link rel="match" href="table-border-1-ref.html">
+<title>Table borders</title>
+<table border>
+<tr><td>Test
+</table>
+<table border="">
+<tr><td>Test
+</table>
+<table border=null>
+<tr><td>Test
+</table>
+<table border=undefined>
+<tr><td>Test
+</table>
+<table border=foo>
+<tr><td>Test
+</table>
+<table border=1>
+<tr><td>Test
+</table>
+<table border=1foo>
+<tr><td>Test
+</table>
+<table border=1%>
+<tr><td>Test
+</table>
+<table border=-1>
+<tr><td>Test
+</table>
+<table border=-1foo>
+<tr><td>Test
+</table>
+<table border=-1%>
+<tr><td>Test
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-notref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-notref.html
new file mode 100644
index 0000000000..7558e5271a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-notref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Table borders</title>
+<style>
+table {
+ border-width: 1px;
+ border-style: outset;
+}
+td {
+ border-width: 1px;
+ border-style: inset;
+}
+</style>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-ref.html
new file mode 100644
index 0000000000..36d1e45106
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Table borders</title>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
+<table>
+<tr><td>Test
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2.html
new file mode 100644
index 0000000000..6f4f39b113
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<link rel="match" href="table-border-2-ref.html">
+<title>Table borders</title>
+<table border=0>
+<tr><td>Test
+</table>
+<table border=0foo>
+<tr><td>Test
+</table>
+<table border=0%>
+<tr><td>Test
+</table>
+<table border=+0>
+<tr><td>Test
+</table>
+<table border=+0foo>
+<tr><td>Test
+</table>
+<table border=+0%>
+<tr><td>Test
+</table>
+<table border=-0>
+<tr><td>Test
+</table>
+<table border=-0foo>
+<tr><td>Test
+</table>
+<table border=-0%>
+<tr><td>Test
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3-ref.html
new file mode 100644
index 0000000000..e465fd433c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3-ref.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<head>
+ <title>Reference for default 'border-color' on table (with 'color' set)</title>
+ <meta charset="utf-8">
+ <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+ <style>
+ * {
+ border-color: teal;
+ /* This only affects the elements that we specify 'border-style' on: */
+ border-width: 6px;
+ }
+
+ table {
+ height: 30px;
+ width: 30px;
+ border-spacing: 0;
+
+ /* To test in "rows": */
+ float: left;
+ margin: 1px;
+ }
+ br {
+ clear: both;
+ }
+
+ .dotted {
+ border-style: dotted;
+ }
+ .dashed {
+ border-style: dashed;
+ }
+ .solid {
+ border-style: solid;
+ }
+ .double {
+ border-style: double;
+ }
+ .groove {
+ border-style: groove;
+ }
+ .ridge {
+ border-style: ridge;
+ }
+ .inset {
+ border-style: inset;
+ }
+ .outset {
+ border-style: outset;
+ }
+ </style>
+</head>
+
+<table class="dotted"><td></td></table>
+<table><th class="dotted"></th></table>
+<table><td class="dotted"></td></table>
+<br>
+
+<table class="dashed"><td></td></table>
+<table><th class="dashed"></th></table>
+<table><td class="dashed"></td></table>
+<br>
+
+<table class="solid"><td></td></table>
+<table><th class="solid"></th></table>
+<table><td class="solid"></td></table>
+<br>
+
+<table class="double"><td></td></table>
+<table><th class="double"></th></table>
+<table><td class="double"></td></table>
+<br>
+
+<table class="groove"><td></td></table>
+<table><th class="groove"></th></table>
+<table><td class="groove"></td></table>
+<br>
+
+<table class="ridge"><td></td></table>
+<table><th class="ridge"></th></table>
+<table><td class="ridge"></td></table>
+<br>
+
+<table class="inset"><td></td></table>
+<table><th class="inset"></th></table>
+<table><td class="inset"></td></table>
+<br>
+
+<table class="outset"><td></td></table>
+<table><th class="outset"></th></table>
+<table><td class="outset"></td></table>
+<br>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3q.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3q.html
new file mode 100644
index 0000000000..4b481194dc
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3q.html
@@ -0,0 +1,95 @@
+<!-- Intentionally omitting doctype, to test quirks mode. -->
+<head>
+ <title>Testing default 'border-color' on table (with 'color' set), in quirks mode</title>
+ <meta charset="utf-8">
+ <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+ <link rel="match" href="table-border-3-ref.html">
+ <style>
+ * {
+ /* This sets the used value of 'currentColor', which is what should be
+ used for all border-coloring in this test: */
+ color: teal;
+ /* This only affects the elements that we specify 'border-style' on: */
+ border-width: 6px;
+ }
+
+ table {
+ height: 30px;
+ width: 30px;
+ border-spacing: 0;
+
+ /* To test in "rows": */
+ float: left;
+ margin: 1px;
+ }
+ br {
+ clear: both;
+ }
+
+ .dotted {
+ border-style: dotted;
+ }
+ .dashed {
+ border-style: dashed;
+ }
+ .solid {
+ border-style: solid;
+ }
+ .double {
+ border-style: double;
+ }
+ .groove {
+ border-style: groove;
+ }
+ .ridge {
+ border-style: ridge;
+ }
+ .inset {
+ border-style: inset;
+ }
+ .outset {
+ border-style: outset;
+ }
+ </style>
+</head>
+
+<table class="dotted"><td></td></table>
+<table><th class="dotted"></th></table>
+<table><td class="dotted"></td></table>
+<br>
+
+<table class="dashed"><td></td></table>
+<table><th class="dashed"></th></table>
+<table><td class="dashed"></td></table>
+<br>
+
+<table class="solid"><td></td></table>
+<table><th class="solid"></th></table>
+<table><td class="solid"></td></table>
+<br>
+
+<table class="double"><td></td></table>
+<table><th class="double"></th></table>
+<table><td class="double"></td></table>
+<br>
+
+<table class="groove"><td></td></table>
+<table><th class="groove"></th></table>
+<table><td class="groove"></td></table>
+<br>
+
+<table class="ridge"><td></td></table>
+<table><th class="ridge"></th></table>
+<table><td class="ridge"></td></table>
+<br>
+
+<table class="inset"><td></td></table>
+<table><th class="inset"></th></table>
+<table><td class="inset"></td></table>
+<br>
+
+<table class="outset"><td></td></table>
+<table><th class="outset"></th></table>
+<table><td class="outset"></td></table>
+<br>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3s.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3s.html
new file mode 100644
index 0000000000..c4c019c8eb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-3s.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<head>
+ <title>Testing default 'border-color' on table (with 'color' set), in standards mode</title>
+ <meta charset="utf-8">
+ <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+ <link rel="match" href="table-border-3-ref.html">
+ <style>
+ * {
+ /* This sets the used value of 'currentColor', which is what should be
+ used for all border-coloring in this test: */
+ color: teal;
+ /* This only affects the elements that we specify 'border-style' on: */
+ border-width: 6px;
+ }
+
+ table {
+ height: 30px;
+ width: 30px;
+ border-spacing: 0;
+
+ /* To test in "rows": */
+ float: left;
+ margin: 1px;
+ }
+ br {
+ clear: both;
+ }
+
+ .dotted {
+ border-style: dotted;
+ }
+ .dashed {
+ border-style: dashed;
+ }
+ .solid {
+ border-style: solid;
+ }
+ .double {
+ border-style: double;
+ }
+ .groove {
+ border-style: groove;
+ }
+ .ridge {
+ border-style: ridge;
+ }
+ .inset {
+ border-style: inset;
+ }
+ .outset {
+ border-style: outset;
+ }
+ </style>
+</head>
+
+<table class="dotted"><td></td></table>
+<table><th class="dotted"></th></table>
+<table><td class="dotted"></td></table>
+<br>
+
+<table class="dashed"><td></td></table>
+<table><th class="dashed"></th></table>
+<table><td class="dashed"></td></table>
+<br>
+
+<table class="solid"><td></td></table>
+<table><th class="solid"></th></table>
+<table><td class="solid"></td></table>
+<br>
+
+<table class="double"><td></td></table>
+<table><th class="double"></th></table>
+<table><td class="double"></td></table>
+<br>
+
+<table class="groove"><td></td></table>
+<table><th class="groove"></th></table>
+<table><td class="groove"></td></table>
+<br>
+
+<table class="ridge"><td></td></table>
+<table><th class="ridge"></th></table>
+<table><td class="ridge"></td></table>
+<br>
+
+<table class="inset"><td></td></table>
+<table><th class="inset"></th></table>
+<table><td class="inset"></td></table>
+<br>
+
+<table class="outset"><td></td></table>
+<table><th class="outset"></th></table>
+<table><td class="outset"></td></table>
+<br>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive-ref.html
new file mode 100644
index 0000000000..c8d6a20726
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive-ref.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<style>
+table {
+ display: inline-table;
+ font-family: Ahem;
+}
+table, table * {
+ border-width: 3px;
+}
+</style>
+<p>For every three tables below, the first two should have borders between some cells, but not the third:
+<table rules="rows"><tr><td>X<tr><td>X<tr><td>X</table>
+<table rules="rows"><tr><td>X<tr><td>X<tr><td>X</table>
+<table><tr><td>X<tr><td>X<tr><td>X</table>
+<br><br>
+<table rules="cols"><tr><td>X<td>X<td>X</table>
+<table rules="cols"><tr><td>X<td>X<td>X</table>
+<table><tr><td>X<td>X<td>X</table>
+<br><br>
+<table rules="groups"><colgroup span="2"><colgroup><tr><td>X<td>X<td>X</table>
+<table rules="groups"><colgroup span="2"><colgroup><tr><td>X<td>X<td>X</table>
+<table><colgroup span="2"><colgroup><tr><td>X<td>X<td>X</table>
+<br><br>
+<p>For every three tables below, the first two should have borders on some edges, but not the third:
+<table frame="hsides"><tr><td>X</table>
+<table frame="hsides"><tr><td>X</table>
+<table><tr><td>X</table>
+<br><br>
+<table frame="lhs"><tr><td>X</table>
+<table frame="lhs"><tr><td>X</table>
+<table><tr><td>X</table>
+<br><br>
+<table frame="rhs"><tr><td>X</table>
+<table frame="rhs"><tr><td>X</table>
+<table><tr><td>X</table>
+<br><br>
+<table frame="vsides"><tr><td>X</table>
+<table frame="vsides"><tr><td>X</table>
+<table><tr><td>X</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive.html
new file mode 100644
index 0000000000..45c9da925d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-border-presentational-hints-ascii-case-insensitive.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#tables-2:presentational-hints">
+<link rel="help" href="https://drafts.csswg.org/selectors-4/#attribute-case">
+<link rel="match" href="table-border-presentational-hints-ascii-case-insensitive-ref.html">
+<meta name="assert" content="@rules + @frame values are ASCII case-insensitive">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<style>
+table {
+ display: inline-table;
+ font-family: Ahem;
+}
+table, table * {
+ border-width: 3px;
+}
+</style>
+<p>For every three tables below, the first two should have borders between some cells, but not the third:
+<table rules="rows"><tr><td>X<tr><td>X<tr><td>X</table>
+<table rules="RoWs"><tr><td>X<tr><td>X<tr><td>X</table>
+<table rules="rowſ"><tr><td>X<tr><td>X<tr><td>X</table>
+<br><br>
+<table rules="cols"><tr><td>X<td>X<td>X</table>
+<table rules="CoLs"><tr><td>X<td>X<td>X</table>
+<table rules="colſ"><tr><td>X<td>X<td>X</table>
+<br><br>
+<table rules="groups"><colgroup span="2"><colgroup><tr><td>X<td>X<td>X</table>
+<table rules="GrOuPs"><colgroup span="2"><colgroup><tr><td>X<td>X<td>X</table>
+<table rules="groupſ"><colgroup span="2"><colgroup><tr><td>X<td>X<td>X</table>
+<br><br>
+<p>For every three tables below, the first two should have borders on some edges, but not the third:
+<table frame="hsides"><tr><td>X</table>
+<table frame="HsIdEs"><tr><td>X</table>
+<table frame="hſideſ"><tr><td>X</table>
+<br><br>
+<table frame="lhs"><tr><td>X</table>
+<table frame="LhS"><tr><td>X</table>
+<table frame="lhſ"><tr><td>X</table>
+<br><br>
+<table frame="rhs"><tr><td>X</table>
+<table frame="RhS"><tr><td>X</table>
+<table frame="rhſ"><tr><td>X</table>
+<br><br>
+<table frame="vsides"><tr><td>X</table>
+<table frame="VsIdEs"><tr><td>X</table>
+<table frame="vſideſ"><tr><td>X</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001-ref.html
new file mode 100644
index 0000000000..1b128e6171
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference case for table bordercolor attribute behaving like border-color property</title>
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+<style>
+ table { margin: 5px }
+</style>
+<table>
+ <td>I should not have a border.</td>
+</table>
+<table>
+ <td>I should not have a border.</td>
+</table>
+<table>
+ <td>I should not have a border.</td>
+</table>
+<table>
+ <td>I should not have a border.</td>
+</table>
+<table style="border-color: lime; border-style: solid">
+ <td>I should have a border.</td>
+</table>
+<table style="border-color: lime; border-style: solid">
+ <td>I should have a border.</td>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001.html
new file mode 100644
index 0000000000..014abe3567
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-bordercolor-001.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for table bordercolor attribute behaving like border-color property</title>
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="match" href="table-bordercolor-001-ref.html">
+<meta name="assert" content="bordercolor is treated as a presentation hint, equivalent to setting the border-color property">
+<style>
+ table { margin: 5px }
+</style>
+<table bordercolor="red">
+ <td>I should not have a border.</td>
+</table>
+<table style="border-color: red">
+ <td>I should not have a border.</td>
+</table>
+<table bordercolor="red" style="border-width: 10px">
+ <td>I should not have a border.</td>
+</table>
+<table style="border-color: red; border-width: 10px">
+ <td>I should not have a border.</td>
+</table>
+<table bordercolor="lime" style="border-style: solid">
+ <td>I should have a border.</td>
+</table>
+<table style="border-color: lime; border-style: solid">
+ <td>I should have a border.</td>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width-ref.html
new file mode 100644
index 0000000000..5b2ea91fe5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background: green;"></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width.html
new file mode 100644
index 0000000000..65962ac273
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-nowrap-with-fixed-width.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#tables-2">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=821915">
+<link rel="match" href="table-cell-nowrap-with-fixed-width-ref.html">
+<title>A td element with the nowrap attribute should unconditionally apply white-space:nowrap</title>
+<style>
+table { border-spacing: 0; }
+td { width: 10px; padding: 0; }
+div { display: inline-block; background: green; width: 50px; height: 100px; }
+</style>
+<table>
+ <td nowrap>
+ <div></div><div></div>
+ </td>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-ref.html
new file mode 100644
index 0000000000..b5ba0443f3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-ref.html
@@ -0,0 +1,37 @@
+<style>
+body {
+ margin: 0;
+}
+
+.row {
+ clear: both;
+}
+
+.row div {
+ float: left;
+}
+
+.red {
+ background-color: red;
+}
+</style>
+
+<div class="row">
+ <div class="red" style="width: 200px">a</div>
+ <div style="width: 200px">a</div>
+</div>
+
+<div class="row">
+ <div class="red" style="width: 200px">a</div>
+ <div style="width: 200px">a</div>
+</div>
+
+<div class="row">
+ <div class="red" style="width: 100px">a</div>
+ <div style="width: 300px">a</div>
+</div>
+
+<div class="row">
+ <div class="red" style="width: 100px">a</div>
+ <div style="width: 300px">a</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-s.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-s.html
new file mode 100644
index 0000000000..0fe0e2c25a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width-s.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<link rel="match" href="table-cell-width-ref.html">
+<style>
+body {
+ margin: 0;
+}
+
+table {
+ width: 400px;
+ border-collapse: collapse;
+}
+
+th {
+ font-weight: normal;
+ text-align: left;
+}
+
+td, th {
+ padding: 0;
+}
+
+td:first-child, th:first-child {
+ background-color: red;
+}
+</style>
+
+<!-- width=0 should be treated as 'auto' -->
+<table>
+ <tr>
+ <th width=0>a</th>
+ <th>a</th>
+ </tr>
+</table>
+
+<table>
+ <tr>
+ <td width=0>a</td>
+ <td>a</td>
+ </tr>
+</table>
+
+<!-- test valid width attribute value-->
+<table>
+ <tr>
+ <th width=100>a</th>
+ <th>a</th>
+ </tr>
+</table>
+
+<table>
+ <tr>
+ <td width=100>a</td>
+ <td>a</td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width.html
new file mode 100644
index 0000000000..f66244ab10
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-cell-width.html
@@ -0,0 +1,54 @@
+<link rel="match" href="table-cell-width-ref.html">
+<style>
+body {
+ margin: 0;
+}
+
+table {
+ width: 400px;
+ border-collapse: collapse;
+}
+
+th {
+ font-weight: normal;
+ text-align: left;
+}
+
+td, th {
+ padding: 0;
+}
+
+td:first-child, th:first-child {
+ background-color: red;
+}
+</style>
+
+<!-- width=0 should be treated as 'auto' -->
+<table>
+ <tr>
+ <th width=0>a</th>
+ <th>a</th>
+ </tr>
+</table>
+
+<table>
+ <tr>
+ <td width=0>a</td>
+ <td>a</td>
+ </tr>
+</table>
+
+<!-- test valid width attribute value-->
+<table>
+ <tr>
+ <th width=100>a</th>
+ <th>a</th>
+ </tr>
+</table>
+
+<table>
+ <tr>
+ <td width=100>a</td>
+ <td>a</td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width-ref.html
new file mode 100644
index 0000000000..1eb7c00d21
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<div style="border: 1px solid green; width: 0">Text</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width.html
new file mode 100644
index 0000000000..6358e14a39
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-column-width.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="match" href="table-column-width-ref.html">
+<table style="display: block">
+ <colgroup style="display: block">
+ <col style="border: 1px solid green; display: block" width="0"></col>
+ </colgroup>
+</table>
+<script>
+ document.querySelector("col").append("Text");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction-ref.html
new file mode 100644
index 0000000000..2bbd6c0477
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Table direction</title>
+
+<style>
+ table {
+ border-collapse: collapse;
+ }
+
+ td {
+ border: 2px solid black;
+ width: 20px;
+ height: 20px;
+ }
+
+ td.special {
+ border-left: 5px solid green;
+ border-right: 5px solid blue;
+ }
+</style>
+
+Normal table:
+<table>
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+</table>
+
+<hr>
+
+RTL table:
+<table>
+ <tr>
+ <td class="special"></td>
+ <td></td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction.html
new file mode 100644
index 0000000000..a3de4136f1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-direction.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<link rel="match" href="table-direction-ref.html">
+<title>Table direction</title>
+
+<style>
+ table {
+ border-collapse: collapse;
+ }
+
+ td {
+ border: 2px solid black;
+ width: 20px;
+ height: 20px;
+ }
+
+ td.special {
+ border-left: 5px solid green;
+ border-right: 5px solid blue;
+ }
+</style>
+
+Normal table:
+<table>
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+</table>
+
+<hr>
+
+RTL table:
+<table style="direction: rtl">
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-notref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-notref.html
new file mode 100644
index 0000000000..ef1378185a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-notref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Table layout attribute</title>
+<table border width=100% style=table-layout:fixed>
+<tr><td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<td>aaa
+</table>
+<table border width=100% style=table-layout:fixed>
+<tr><td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<td>aaa
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-ref.html
new file mode 100644
index 0000000000..d76a48c4ab
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Table layout attribute</title>
+<table border width=100%>
+<tr><td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<td>aaa
+</table>
+<table border width=100%>
+<tr><td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<td>aaa
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout.html
new file mode 100644
index 0000000000..7dfacf2279
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-layout.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Table layout attribute</title>
+<link rel="match" href="table-layout-ref.html">
+<meta name="assert"
+ content="The layout attribute on table elements should have no effect.">
+<table border width=100% layout=fixed>
+<tr><td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<td>aaa
+</table>
+<table border width=100% layout=auto>
+<tr><td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<td>aaa
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction-ref.html
new file mode 100644
index 0000000000..dab31636d9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>Table row direction</title>
+
+<style>
+ table {
+ border-collapse: collapse;
+ }
+
+ td {
+ border: 2px solid black;
+ width: 20px;
+ height: 20px;
+ }
+
+ td.special {
+ border-left: 5px solid green;
+ border-right: 5px solid blue;
+ }
+</style>
+
+Normal table with LTR and RTL rows:
+<table>
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+</table>
+
+<hr>
+
+RTL table with LTR and RTL rows:
+<table>
+ <tr>
+ <td class="special"></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td class="special"></td>
+ <td></td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction.html
new file mode 100644
index 0000000000..64ed5a667a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-direction.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<link rel="match" href="table-row-direction-ref.html">
+<title>Table row direction</title>
+
+<style>
+ table {
+ border-collapse: collapse;
+ }
+
+ td {
+ border: 2px solid black;
+ width: 20px;
+ height: 20px;
+ }
+
+ td.special {
+ border-left: 5px solid green;
+ border-right: 5px solid blue;
+ }
+</style>
+
+Normal table with LTR and RTL rows:
+<table>
+ <tr style="direction: ltr">
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ <tr style="direction: rtl">
+ <td></td>
+ <td class="special"></td>
+ </tr>
+</table>
+
+<hr>
+
+RTL table with LTR and RTL rows:
+<table style="direction: rtl">
+ <tr style="direction: ltr">
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ <tr style="direction: rtl">
+ <td></td>
+ <td class="special"></td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction-ref.html
new file mode 100644
index 0000000000..0f3e03f9ba
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>Table row-group direction</title>
+
+<style>
+ table {
+ border-collapse: collapse;
+ }
+
+ td {
+ border: 2px solid black;
+ width: 20px;
+ height: 20px;
+ }
+
+ td.special {
+ border-left: 5px solid green;
+ border-right: 5px solid blue;
+ }
+</style>
+
+Normal table with LTR and RTL row groups:
+<table>
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+</table>
+
+<hr>
+
+RTL table with LTR and RTL row groups:
+<table>
+ <tr>
+ <td class="special"></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td class="special"></td>
+ <td></td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction.html
new file mode 100644
index 0000000000..385672f127
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-direction.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<link rel="match" href="table-row-group-direction-ref.html">
+<title>Table row-group direction</title>
+
+<style>
+ table {
+ border-collapse: collapse;
+ }
+
+ td {
+ border: 2px solid black;
+ width: 20px;
+ height: 20px;
+ }
+
+ td.special {
+ border-left: 5px solid green;
+ border-right: 5px solid blue;
+ }
+</style>
+
+Normal table with LTR and RTL row groups:
+<table>
+ <tbody style="direction: ltr">
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ </tbody>
+ <tbody style="direction: rtl">
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ </tbody>
+</table>
+
+<hr>
+
+RTL table with LTR and RTL row groups:
+<table style="direction: rtl">
+ <tbody style="direction: ltr">
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ </tbody>
+ <tbody style="direction: rtl">
+ <tr>
+ <td></td>
+ <td class="special"></td>
+ </tr>
+ </tbody>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height-ref.html
new file mode 100644
index 0000000000..e5279080f0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height-ref.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<table style="border: 1px solid red">
+ <thead style="display: block; height: 100px">
+ <tr>
+ <td>
+ thead text
+ </td>
+ </tr>
+ </tr>
+</table>
+
+<table style="border: 1px solid red">
+ <tbody style="display: block; height: 100px">
+ <tr>
+ <td>
+ tbody text
+ </td>
+ </tr>
+ </tr>
+</table>
+
+<table style="border: 1px solid red">
+ <tfoot style="display: block; height: 100px">
+ <tr>
+ <td>
+ tfoot text
+ </td>
+ </tr>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height.html
new file mode 100644
index 0000000000..b58311dd72
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-group-height.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<link rel="match" href="table-row-group-height-ref.html">
+<table style="border: 1px solid red">
+ <thead style="display: block" height="100">
+ <tr>
+ <td>
+ thead text
+ </td>
+ </tr>
+ </tr>
+</table>
+
+<table style="border: 1px solid red">
+ <tbody style="display: block" height="100">
+ <tr>
+ <td>
+ tbody text
+ </td>
+ </tr>
+ </tr>
+</table>
+
+<table style="border: 1px solid red">
+ <tfoot style="display: block" height="100">
+ <tr>
+ <td>
+ tfoot text
+ </td>
+ </tr>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height-ref.html
new file mode 100644
index 0000000000..63f882c689
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<div style="border: 1px solid green; height: 0; border-spacing: 2px">
+ <div style="display: table-cell; padding: 1px">Hey</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height.html
new file mode 100644
index 0000000000..8ecc0dd454
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-height.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<link rel="match" href="table-row-height-ref.html">
+<table style="display: block">
+ <tbody style="display: block">
+ <tr style="display: block; border: 1px solid green" height="0">
+ <td>Hey</td>
+ </tr>
+ </tbody>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html
new file mode 100644
index 0000000000..2aa94109ad
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Testing row split</title>
+<style type="text/css">
+@page { size:5in 3in; margin:0.5in; }
+html,body {
+ color:black;
+ background-color:white;
+ font-size:16px;
+ padding:0;
+ margin:0;
+ height:100%;
+}
+div { height:160%; }
+p { height: 50%; margin:0; }
+</style>
+</head>
+<body>
+
+<div>
+<p></p>
+<p>1</p>
+</div>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print.html
new file mode 100644
index 0000000000..3a14558a8b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<link rel="match" href="table-row-pagination-001-print-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="help" href="https://drafts.csswg.org/css-break/">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
+<meta charset="utf-8">
+<title>Testing row split</title>
+<style type="text/css">
+@page { size:5in 3in; margin:0.5in; }
+html,body {
+ color:black;
+ background-color:white;
+ font-size:16px;
+ padding:0;
+ margin:0;
+ height:100%;
+}
+table { height:160%; width:100%; }
+td { height:50%; width:100%; }
+</style>
+</head>
+<body>
+
+<table border="0" cellspacing="0" cellpadding="0">
+<tr><td></td></tr>
+<tr><td valign="top">1</td></tr>
+</table>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print-ref.html
new file mode 100644
index 0000000000..91400fcc1e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+@page { size:5in 3in; margin:0.5in; }
+</style>
+</head>
+<body>
+ <div style="height:3in">Tall div. (Scroll to end.)</div>
+ <div>IS THIS TEXT VISIBLE IN PRINT PREVIEW?</div>
+ <div>[clear:left]</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print.html
new file mode 100644
index 0000000000..bc1c42088f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-002-print.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="match" href="table-row-pagination-002-print-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="help" href="https://drafts.csswg.org/css-break/">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=963441">
+<style>
+@page { size:5in 3in; margin:0.5in; }
+</style>
+</head>
+<body>
+ <div style="float: left">
+ <table cellpadding=0 cellspacing=0>
+ <tr>
+ <td>
+ <div style="height:3in">Tall div. (Scroll to end.)</div>
+ <div>IS THIS TEXT VISIBLE IN PRINT PREVIEW?</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div style="clear: left">[clear:left]</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-ua-stylesheet.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-ua-stylesheet.html
new file mode 100644
index 0000000000..dc3e45f5a4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-ua-stylesheet.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for table element's UA-stylesheet-provided styles</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-css-user-agent-style-sheet-and-presentational-hints">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="refElem"></div>
+<!-- Note: this test puts the table inside of an element with a non-default
+ 'text-indent' and 'border-collapse' values, so that we can verify that
+ the table does indeed use the initial value for these properties, rather
+ than simply inheriting. -->
+<div style="text-indent: 100px; border-collapse: collapse">
+ <table id="tableElem"></table>
+</div>
+
+<script>
+/* These styles come from the default `table` styling here:
+ * https://html.spec.whatwg.org/multipage/rendering.html#tables-2
+ * We can't check for these values directly, because they may be
+ * serialized slightly differently when read from the computed style.
+ * So, for each property here, we apply it to a "reference" div and then
+ * read back the computed value, and we validate that a table element
+ * has that same computed value by default. */
+const defaultTablePropVals = {
+ 'display': 'table',
+ 'box-sizing': 'border-box',
+ 'border-spacing': '2px',
+ 'border-collapse': 'separate',
+ 'text-indent': 'initial',
+};
+
+for (var propName in defaultTablePropVals) {
+ test(function() {
+ refElem.style[propName] = defaultTablePropVals[propName];
+ let expectedComputedVal = getComputedStyle(refElem, "")[propName];
+
+ let actualComputedVal = getComputedStyle(tableElem, "")[propName];
+ assert_equals(actualComputedVal, expectedComputedVal);
+ }, `Computed '${propName}' on table should match html spec`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-valign-baseline-ascii-case-insensitive.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-valign-baseline-ascii-case-insensitive.html
new file mode 100644
index 0000000000..f64bb9aa08
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-valign-baseline-ascii-case-insensitive.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#tables-2:presentational-hints">
+<link rel="help" href="https://drafts.csswg.org/selectors-4/#attribute-case">
+<meta name="assert" content="@valign values on table-related elements are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<table><tr><td valign="baseline">X</table>
+<table><tr><td valign="BaSeLiNe">X</table>
+<table><tr><td valign="baſeline">X</table>
+<script>
+const td = document.querySelectorAll("td");
+
+test(() => {
+ assert_equals(getComputedStyle(td[0]).getPropertyValue("vertical-align"),
+ "baseline", "lowercase valid");
+ assert_equals(getComputedStyle(td[1]).getPropertyValue("vertical-align"),
+ "baseline", "mixed case valid");
+ assert_equals(getComputedStyle(td[2]).getPropertyValue("vertical-align"),
+ "middle", "non-ASCII invalid");
+}, "keyword baseline");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace-s.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace-s.html
new file mode 100644
index 0000000000..9f462b5768
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace-s.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>table vspace hspace (standards mode)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div>x</div>
+<table vspace=25 hspace=25><tr><td>x</table>
+<div>x</div>
+<script>
+test(function() {
+ var style = getComputedStyle(document.querySelector('table'));
+ ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'].forEach(function(m) {
+ assert_equals(style[m], '0px', m);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace.html
new file mode 100644
index 0000000000..c081775b87
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-vspace-hspace.html
@@ -0,0 +1,16 @@
+<!-- quirks -->
+<meta charset=utf-8>
+<title>table vspace hspace (quirks mode)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div>x</div> <!-- prevent margin collapsing quirks -->
+<table vspace=25 hspace=25><tr><td>x</table>
+<div>x</div> <!-- prevent margin collapsing quirks -->
+<script>
+test(function() {
+ var style = getComputedStyle(document.querySelector('table'));
+ ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'].forEach(function(m) {
+ assert_equals(style[m], '0px', m);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent-ref.html
new file mode 100644
index 0000000000..820c360e39
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>Test for capping percentages</title>
+<style>
+div { width:300px; background:yellow; height:50px; }
+table { width:150%; }
+td { background:blue; }
+</style>
+<div>
+ <table cellspacing="0" cellpadding="0" border="0">
+ <tr><td>parent div float=left</td></tr>
+ </table>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent.html
new file mode 100644
index 0000000000..9a5e108505
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-150percent.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>Test for capping percentages</title>
+<link rel="match" href="table-width-150percent-ref.html">
+<style>
+div { width:300px; background:yellow; height:50px; }
+td { background:blue; }
+</style>
+<div>
+ <table width="150%" cellspacing="0" cellpadding="0" border="0">
+ <tr><td>parent div float=left</td></tr>
+ </table>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-ref.html
new file mode 100644
index 0000000000..2b0f9e445c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-ref.html
@@ -0,0 +1,13 @@
+<style>
+p {
+ padding: 0;
+ margin: 0;
+}
+</style>
+
+<p>a b</p>
+
+<hr>
+
+<p>a</p>
+<p>b</p>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-s.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-s.html
new file mode 100644
index 0000000000..5b987e7919
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width-s.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<link rel="match" href="table-width-ref.html">
+
+<style>
+table {
+ border-collapse: collapse;
+}
+
+td {
+ padding: 0;
+}
+</style>
+
+<!-- width=0 should be treated as 'auto' -->
+<table width=0>
+ <tr>
+ <td>
+ a b
+ </td>
+ </tr>
+</table>
+
+<hr>
+
+<table width=1>
+ <tr>
+ <td>
+ a b
+ </td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width.html
new file mode 100644
index 0000000000..59c5ca70d4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-width.html
@@ -0,0 +1,30 @@
+<link rel="match" href="table-width-ref.html">
+
+<style>
+table {
+ border-collapse: collapse;
+}
+
+td {
+ padding: 0;
+}
+</style>
+
+<!-- width=0 should be treated as 'auto' -->
+<table width=0>
+ <tr>
+ <td>
+ a b
+ </td>
+ </tr>
+</table>
+
+<hr>
+
+<table width=1>
+ <tr>
+ <td>
+ a b
+ </td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change-ref.html
new file mode 100644
index 0000000000..2cbcd6a347
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>
+td { width: 100px; height: 100px; background: green; }
+</style>
+<table>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+</table>
+There should be 2 green boxes above.
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change.html
new file mode 100644
index 0000000000..0f37d635a5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/tr-transform-and-will-change.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="match" href="tr-transform-and-will-change-ref.html">
+<style>
+tbody { background: green; }
+td { width: 100px; height: 100px; }
+tr { transform: translateX(5px); will-change: transform; }
+</style>
+<table>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+</table>
+There should be 2 green boxes above.
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border-ref.html
new file mode 100644
index 0000000000..2f313f3395
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+table {
+ border-collapse: collapse;
+}
+td {
+ border: 5px solid black;
+ width: 100px;
+ height: 100px;
+}
+</style>
+Passes if there is a grid containing 2x2 squares.
+<table>
+ <tbody>
+ <tr><td></td><td></td></tr>
+ <tr><td></td><td></td></tr>
+ </tbody>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border.html
new file mode 100644
index 0000000000..5f131e6658
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Test for transformed tbody and tr with collapsed borders</title>
+<link rel="match" href="transformed-tbody-tr-collapsed-border-ref.html">
+<style>
+table {
+ border-collapse: collapse;
+}
+tbody, tr {
+ transform: translateY(0);
+}
+td {
+ border: 5px solid black;
+ width: 100px;
+ height: 100px;
+}
+</style>
+Passes if there is a grid containing 2x2 squares.
+<table>
+ <tbody>
+ <tr><td></td><td></td></tr>
+ <tr><td></td><td></td></tr>
+ </tbody>
+</table>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/META.yml b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/META.yml
new file mode 100644
index 0000000000..f5b533c377
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/META.yml
@@ -0,0 +1,4 @@
+suggested_reviewers:
+ - emilio
+ - mstensho
+ - zcorpan
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend-ref.html
new file mode 100644
index 0000000000..f82faee114
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<style>
+div {
+ display: block;
+ font-size: 32px;
+ background: lime;
+ width: 10em;
+}
+</style>
+
+<div>legend</div>
+<div>legend</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend.html
new file mode 100644
index 0000000000..56d296977c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/absolute-fixed-in-legend.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>Absolute/fixed-positioned boxes in LEGEND should be painted on the fieldset content</title>
+<link rel=match href=absolute-fixed-in-legend-ref.html>
+<style>
+.absolute-container {
+ position: relative;
+ border: none;
+ padding: 0;
+ margin: 0;
+}
+
+.absolute-container .legend-content {
+ display: block;
+ font-size: 32px;
+ position: absolute;
+ left: 0px;
+ background: lime;
+ width: 10em;
+}
+
+.fixed-container {
+ contain: paint;
+ border: none;
+ padding: 0;
+ margin: 0;
+}
+
+.fixed-container .legend-content {
+ display: block;
+ font-size: 32px;
+ position: fixed;
+ left: 0px;
+ background: lime;
+ width: 10em;
+}
+
+.fieldset-content {
+ background: red;
+ font-size: 32px;
+ width: 10em;
+}
+</style>
+
+<fieldset class="absolute-container">
+ <legend><span class="legend-content">legend</span></legend>
+ <div class="fieldset-content">content</div>
+</fieldset>
+
+<fieldset class="fixed-container">
+ <legend><span class="legend-content">legend</span></legend>
+ <div class="fieldset-content">content</div>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/crashtests/fieldset-middleclick.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/crashtests/fieldset-middleclick.html
new file mode 100644
index 0000000000..39acf9eca1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/crashtests/fieldset-middleclick.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html class="test-wait">
+<head>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+#f {
+ overflow-y: scroll;
+ width: 100px;
+ height: 100px;
+}
+</style>
+</head>
+<body>
+<fieldset id="f">
+ <p>test</p>
+ <p>test</p>
+ <p>test</p>
+ <p>test</p>
+ <p>test</p>
+ <p>test</p>
+</fieldset>
+<script>
+onload = async () => {
+ const actions = new test_driver.Actions();
+ const button = {button: actions.ButtonType.MIDDLE};
+ await actions.
+ pointerMove(50, 50).
+ pointerDown(button).
+ pointerUp(button).
+ send();
+ document.documentElement.className = "";
+};
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable-ref.html
new file mode 100644
index 0000000000..7fa15da174
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<div style="all:initial; display:block; overflow:scroll; width:100px; height:100px; background:blue;"></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable.html
new file mode 100644
index 0000000000..c968ed3f13
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/empty-scrollable.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-fieldset-and-legend-elements">
+<link rel="match" href="empty-scrollable-ref.html">
+<fieldset style="all:initial; display:block; overflow:scroll; width:100px; height:100px; background:blue;"></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline-ref.html
new file mode 100644
index 0000000000..ff583435a6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+span {
+ border: solid 2px;
+ padding: 10px;
+ margin: 5px;
+}
+</style>
+<div>
+ text <span style="display: inline-block;">line1<br>line2</span>
+</div>
+<div>
+ text <span style="display: inline-flex;">line1<br>line2</span>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline.html
new file mode 100644
index 0000000000..23f5ad76f3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-baseline.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1305890">
+<link rel="match" href="fieldset-baseline-ref.html">
+<style>
+fieldset {
+ border: solid 2px;
+ padding: 10px;
+ margin: 5px;
+}
+</style>
+<div>
+ text <fieldset style="display: inline-block;">line1<br>line2</fieldset>
+</div>
+<div>
+ text <fieldset style="display: inline-flex;">line1<br>line2</fieldset>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-formatting-context.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-formatting-context.html
new file mode 100644
index 0000000000..c38944ae79
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-formatting-context.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>The fieldset element: block formatting context</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+* {
+ margin: 0;
+ padding: 0;
+}
+fieldset { border: none; }
+.float {
+ float: left;
+ width: 50px;
+ height: 50px;
+ background-color: orange;
+}
+</style>
+
+<div class=float></div>
+<fieldset><div class=float></div></fieldset>
+
+<script>
+test(() => {
+ const fieldset = document.querySelector('fieldset');
+ assert_equals(fieldset.offsetTop, 0, 'fieldset.offsetTop');
+ assert_equals(fieldset.offsetLeft, 50, 'fieldset.offsetLeft');
+ assert_equals(fieldset.clientHeight, 50, 'fieldset.clientHeight');
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-size.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-size.html
new file mode 100644
index 0000000000..e1247637db
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-block-size.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-fieldset-and-legend-elements">
+<!-- A test for the following paragraph:
+For the purpose of calculating the used 'block-size', if the computed
+'block-size' is not 'auto', the space allocated for the rendered legend's
+margin box that spills out past the border, if any, is expected to be
+subtracted from the 'block-size'. If the content box's block-size would be
+negative, then let the content box's block-size be zero instead.
+-->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: 2px solid black;
+}
+legend {
+ height: 102px;
+ background-color: yellow;
+}
+.content {
+ background-color: lime;
+}
+</style>
+<fieldset style="block-size: 200px;">
+<legend>Legend</legend>
+<div class="content" style="height:100%"></div>
+</fieldset>
+
+<fieldset style="block-size: 40px;">
+<legend>Legend</legend>
+<div class="content" style="height:100%"></div>
+</fieldset>
+
+<script>
+test(() => {
+ let cs = document.querySelectorAll('.content');
+ assert_equals(cs[0].offsetHeight, Math.max(202 - 102, 0));
+ assert_equals(cs[1].offsetHeight, Math.max(42 - 102, 0));
+}, 'Test content\'s block-size');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-negative-margin.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-negative-margin.html
new file mode 100644
index 0000000000..563a2aa68d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-negative-margin.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>fieldset border gap with negative margin</title>
+<link rel=match href=no-red-ref.html>
+<style>
+fieldset { border:none; border-left: 100px solid red; margin: 0; padding: 0; height:100px; }
+legend { padding: 0; margin-left: -100px; width: 100px; height: 100px; transform: rotate(45deg); }
+</style>
+<p>There should be no red.</p>
+<fieldset>
+ <legend></legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative-ref.html
new file mode 100644
index 0000000000..95e2347121
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Reference for fieldset border gap</title>
+<style>
+div { position: relative; top: 25px; width: 100px; height: 50px; background: lime; }
+</style>
+<p>There should be no red.</p>
+<div></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative.html
new file mode 100644
index 0000000000..1dbef479a9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-gap-position-relative.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>fieldset border gap</title>
+<link rel=match href=fieldset-border-gap-position-relative-ref.html>
+<style>
+fieldset, legend { margin: 0; padding: 0; }
+fieldset { border: none; border-top: 100px solid red; width: 100px; }
+legend { width: 100px; height: 50px; background: lime; }
+</style>
+<p>There should be no red.</p>
+<fieldset><legend></legend></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-hittest.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-hittest.html
new file mode 100644
index 0000000000..9ab10159d8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-hittest.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>fieldset, border-radius and hit testing</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+fieldset { width: 80px; height: 80px; border-radius: 100px; border: 10px solid; background: lime; }
+</style>
+<fieldset>
+</fieldset>
+<script>
+test(() => {
+ assert_equals(document.elementFromPoint(20, 20), document.body);
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha-ref.html
new file mode 100644
index 0000000000..5cfe13c40d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<style>
+div {
+ background-color: green;
+ height: 110px;
+ position: absolute;
+ width: 150px;
+ top: 70px;
+}
+</style>
+<p>There should be no red.</p>
+<div></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha.html
new file mode 100644
index 0000000000..7a942076fb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-border-radius-with-alpha.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Fieldset with a border-radius and non-opaque border-color</title>
+<link rel="match" href="fieldset-border-radius-with-alpha-ref.html">
+<style>
+fieldset {
+ background-color: green;
+ border: 3px solid rgba(255, 0, 0, 0.9);
+ border-radius: 4px;
+ height: 100px;
+ width: 100px;
+}
+legend {
+ height: 50px;
+ width: 50px;
+}
+div {
+ background-color: green;
+ height: 110px;
+ position: absolute;
+ width: 150px;
+ top: 70px;
+}
+</style>
+<p>There should be no red.</p>
+<fieldset><legend></legend></fieldset>
+<div></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-calculating-min-max-content.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-calculating-min-max-content.html
new file mode 100644
index 0000000000..4a9f261895
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-calculating-min-max-content.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>fieldset calculating min-/max-content</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+fieldset { margin: 0; padding: 0; }
+.min-content { width: 0; }
+legend { padding: 0; }
+fieldset, #ref-max-content, #ref-min-content { float: left; clear: left; border: 1px solid; }
+#log { clear: left; }
+</style>
+<fieldset class=max-content><legend>foo fooo</legend>x x</fieldset>
+<fieldset class=max-content><legend>x x</legend>foo fooo</fieldset>
+<div id=ref-max-content>foo fooo</div>
+
+<fieldset class=min-content><legend>foo fooo</legend>x x</fieldset>
+<fieldset class=min-content><legend>x x</legend>foo fooo</fieldset>
+<div id=ref-min-content>fooo</div>
+
+<script>
+test(() => {
+ const ref = document.querySelector('#ref-max-content');
+ for (const e of document.querySelectorAll('.max-content')) {
+ assert_equals(e.clientWidth, ref.clientWidth);
+ }
+}, 'max-content');
+
+test(() => {
+ const ref = document.querySelector('#ref-min-content');
+ for (const e of document.querySelectorAll('.min-content')) {
+ assert_equals(e.clientWidth, ref.clientWidth);
+ }
+}, 'min-content');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block-ref.html
new file mode 100644
index 0000000000..282c0d4ef1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Reference for fieldset containing block</title>
+<style>
+p { margin: 0; height: 100px }
+.div1 { position: absolute; top: 108px; width: 100px; height: 100px; background: lime; }
+.div2 { position: absolute; top: 158px; width: 200px; height: 100px; background: lime; }
+</style>
+<p>There should be no red.</p>
+<div class="div1"></div>
+<div class="div2"></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block.html
new file mode 100644
index 0000000000..cefb3584ea
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-containing-block.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>fieldset containing block</title>
+<link rel=match href=fieldset-containing-block-ref.html>
+<style>
+p { margin: 0; height: 100px }
+fieldset { position: relative; border: none; padding: 0; margin: 0; }
+legend { padding: 0; width: 100px; height: 50px; background: lime; }
+div { position: absolute; top: 0; width: 100px; height: 50px; background: lime; }
+.behind { height:100px; top: 108px; background: red; }
+
+.fixed-container {
+ filter: invert();
+ overflow: hidden;
+ width: 200px;
+ height: 200px;
+}
+.fixed {
+ position: fixed;
+ width: 400px;
+ height: 100px;
+ background: linear-gradient(to right, #ff00ff 50%, #00ffff 50%);
+}
+.has-fixed {
+ width: 0px;
+ height: 0px;
+}
+</style>
+<p>There should be no red.</p>
+<div class="behind"></div>
+<fieldset><legend></legend><div></div></fieldset>
+
+<fieldset class="fixed-container">
+<legend class="has-fixed"><div style="position:fixed; width:0; height0;"></div></legend>
+<div class="fixed"></div>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-before-legend.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-before-legend.html
new file mode 100644
index 0000000000..5bd1fbc161
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-before-legend.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<title>fieldset content before legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+fieldset {
+ /* Paddings might have fractional values by default, and they can cause
+ rounding differences between the test element and the reference element. */
+ padding: 0;
+}
+</style>
+
+<fieldset id=test>
+ X
+ <legend>legend</legend>
+ Y
+</fieldset>
+<fieldset id=ref>
+ <legend>legend</legend>
+ X Y
+</fieldset>
+
+<fieldset id="test2">
+ P<span id="hidden" style="display:none;">AS</span><legend>legend</legend>S
+</fieldset>
+
+<script>
+ test(() => {
+ const testElm = document.getElementById('test');
+ const refElm = document.getElementById('ref');
+ assert_equals(testElm.clientHeight, refElm.clientHeight);
+ });
+
+ test(() => {
+ const testElm = document.getElementById('test2');
+ testElm.clientHeight;
+ const span = document.getElementById('hidden');
+ span.style.display = 'inline';
+ testElm.clientHeight;
+ }, 'Showing a node just before the rendered legend should not crash');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-percentage-size.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-percentage-size.html
new file mode 100644
index 0000000000..6195d8bb05
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-percentage-size.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1140595">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="height:100px;">
+ <fieldset style="margin:0; padding:0; border:none;">
+ <div id="inner" style="height:59%;"></div>
+ </fieldset>
+</div>
+<script>
+test(() => {
+ let innerDiv = document.querySelector('#inner');
+ assert_equals(innerDiv.clientHeight, 0);
+}, 'A percentage height for an element in an auto-height fieldset should be ignored');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl-ref.html
new file mode 100644
index 0000000000..b7c080bfac
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html dir="rtl">
+<head>
+<meta charset="utf-8">
+<style>
+.control {
+ background: blue;
+ width: 200px;
+ height: 1em;
+}
+.container {
+ border: 2px groove ThreeDFace;
+ margin: 0;
+ padding: 1em;
+}
+</style>
+</head>
+<body>
+<div class="container">
+ <label>Label</label>
+ <div class="control" id="ctrl-d"></div>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl.html
new file mode 100644
index 0000000000..0f12c6f92a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-content-rtl.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html dir="rtl">
+<head>
+<meta charset="utf-8">
+<title>crbug.com/1130174; Non-auto-width block should be right-aligned in an RTL fieldset</title>
+<link rel="match" href="fieldset-content-rtl-ref.html">
+<style>
+.control {
+ background: blue;
+ width: 200px;
+ height: 1em;
+}
+fieldset {
+ border: 2px groove ThreeDFace;
+ margin: 0;
+ padding: 1em;
+}
+</style>
+</head>
+<body>
+<fieldset>
+ <label>Label</label>
+ <div class="control"></div>
+</fieldset>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-crash.html
new file mode 100644
index 0000000000..111dcbe533
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1039241">
+<div style="width: min-content;">
+ <div style="writing-mode:vertical-lr;">
+ <fieldset>
+ <div style="width:10%;"></div>
+ </fieldset>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-default-style.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-default-style.html
new file mode 100644
index 0000000000..9a711383e6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-default-style.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<title>fieldset default style</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#ref {
+ display: block;
+ margin-left: 2px;
+ margin-right: 2px;
+ /* TODO replace above declarations with these when they are widely supported.
+ margin-inline-start: 2px;
+ margin-inline-end: 2px;
+ */
+ border: groove 2px;
+ padding: 0.35em 0.75em 0.625em 0.75em;
+ /* TODO replace above declarations with these when they are widely supported.
+ padding-block-start: 0.35em;
+ padding-inline-end: 0.75em;
+ padding-block-end: 0.625em;
+ padding-inline-start: 0.75em;
+ */
+ min-width: min-content;
+ /* TODO change the above to min-inline-size when it's widely supported. */
+}
+</style>
+<fieldset id=test></fieldset>
+<div id=ref></div>
+<script>
+ const testElm = document.querySelector('#test');
+ const refElm = document.querySelector('#ref');
+ const props = ['display',
+ 'margin-top',
+ 'margin-right',
+ 'margin-bottom',
+ 'margin-left',
+ 'border-top-style',
+ 'border-right-style',
+ 'border-bottom-style',
+ 'border-left-style',
+ 'border-top-width',
+ 'border-right-width',
+ 'border-bottom-width',
+ 'border-left-width',
+ 'padding-top',
+ 'padding-right',
+ 'padding-bottom',
+ 'padding-left',
+ 'min-width',
+ ];
+ const testStyle = getComputedStyle(testElm);
+ const refStyle = getComputedStyle(refElm);
+ props.forEach(prop => {
+ test(() => {
+ assert_equals(testStyle[prop], refStyle[prop]);
+ }, `${prop}`);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-display.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-display.html
new file mode 100644
index 0000000000..a8a553c836
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-display.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>fieldset and CSS display</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ #inline-ref { display: inline-block; }
+</style>
+<fieldset id="block-ref">x</fieldset>
+<fieldset id="inline-ref">x</fieldset>
+<fieldset id="test">x</fieldset>
+<script>
+ const blockWidth = getComputedStyle(document.querySelector('#block-ref')).width;
+ const inlineWidth = getComputedStyle(document.querySelector('#inline-ref')).width;
+ const testElm = document.querySelector('#test');
+ // Please only add canonical values to these lists:
+ const blocks = ['block', 'table', 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row', 'table-cell',
+ 'table-column-group', 'table-column', 'table-caption', 'list-item', 'flow-root', 'run-in', 'block ruby'];
+ const inlines = ['inline', 'inline-block', 'inline-table', 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', 'ruby-text-container'];
+
+ function test_display(val, expectedWidth) {
+ test(() => {
+ testElm.style.removeProperty('display');
+ testElm.style.display = val;
+ const computed = getComputedStyle(testElm);
+ assert_equals(computed.display, val, `display: ${val} is not supported`);
+ assert_equals(computed.width, expectedWidth);
+ }, `fieldset with display: ${val}`);
+ }
+
+ for (const val of blocks) {
+ test_display(val, blockWidth);
+ }
+
+ for (const val of inlines) {
+ test_display(val, inlineWidth);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-div-display-contents.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-div-display-contents.html
new file mode 100644
index 0000000000..5d17b91290
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-div-display-contents.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>fieldset and div with display: contents</title>
+<link rel=fieldset-foo-ref.html>
+<style>
+div { display: contents; }
+</style>
+<p>There should be a normal fieldset below with the legend "Foo".</p>
+<fieldset>
+ <div>
+ <legend>Foo</legend>
+ </div>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline-ref.html
new file mode 100644
index 0000000000..c34096f270
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+baseline
+<fieldset style="display: inline-block;">
+ <div style="position: relative;">
+ line1<br>line2
+ </div>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline.html
new file mode 100644
index 0000000000..88aeed28c1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-baseline.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1307140">
+<link rel="match" href="fieldset-dynamic-baseline-ref.html">
+baseline
+<fieldset style="display: inline-block;">
+ <div style="position: relative;">
+ line1<br>line2<div id="target" style="position: absolute;"></div>
+ </div>
+</fieldset>
+<script>
+document.body.offsetTop;
+document.getElementById('target').style.top = '10px';
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-oof-container-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-oof-container-crash.html
new file mode 100644
index 0000000000..a2b999fa00
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-oof-container-crash.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html class="test-wait">
+<title>No crash after stop being an asbolute container.</title>
+<link rel="help" href="https://crbug.com/1395688">
+<style>
+.c2 {
+ transform: rotate3d(0, 1, 0, 45deg);
+ column-width: 100px;
+}
+.c19 {
+ overflow: auto;
+ padding-left: 65536px;
+ column-count: 3;
+}
+
+q {
+ position: absolute;
+ column-width: 1px;
+}
+
+body {
+ column-count: 3;
+}
+</style>
+<script>
+function animationFrame() {
+ return new Promise(resolve => requestAnimationFrame(resolve));
+}
+
+async function doTest() {
+ document.documentElement.appendChild(document.createElement('body'));
+ await animationFrame();
+ document.body.innerHTML = '<fieldset class=c2><q>q</q></fieldset>';
+ window.scrollBy(28, 71);
+ await animationFrame();
+ document.querySelector('fieldset').setAttribute('class', 'c19');
+ await animationFrame();
+ document.body.remove();
+ await animationFrame();
+ await animationFrame();
+ document.documentElement.classList.remove('test-wait');
+}
+
+doTest();
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo-ref.html
new file mode 100644
index 0000000000..5bdf78db55
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+*::after { content:"after text"; border:3px solid black;}
+*::before { content:"before text"; border:3px solid black; }
+</style>
+</head>
+<body>
+<fieldset><legend>Legend</legend></fielset>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo.html
new file mode 100644
index 0000000000..c5fbf53000
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-dynamic-pseudo.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!--
+Chrome had a crash bug in a case of dynamic addition of pseudo elements.
+crbug.com/1242229
+-->
+<html class="reftest-wait">
+<head>
+<link rel="match" href="fieldset-dynamic-pseudo-ref.html">
+<style>
+*::after { content:"after text"; border:3px solid black;}
+*::before { content:"before text"; border:3px solid black; }
+</style>
+</head>
+<body>
+<fieldset><legend>Legend</legend></fielset>
+<script>
+document.styleSheets[0].disabled = true;
+
+requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.styleSheets[0].disabled = false;
+ document.documentElement.className = '';
+ });
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html
new file mode 100644
index 0000000000..9e1c9ed152
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>fieldset and CSS Flexbox</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#test, #ref, #test-inline, #ref-inline {
+ display: flex;
+ justify-content: space-around;
+ margin: 0;
+ padding: 0;
+ border: none
+}
+#test-inline, #ref-inline { display: inline-flex }
+legend {
+ float: left; /* Makes it not the "rendered legend" */
+ padding: 0;
+}
+</style>
+<fieldset id=test>
+ <legend>1</legend>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</fieldset>
+<hr>
+<div id=ref>
+ <legend>1</legend>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</div>
+<hr>
+<fieldset id=test-inline>
+ <legend>1</legend>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</fieldset>
+<div id=ref-inline>
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</div>
+<script>
+ test(() => {
+ const testElm = document.getElementById('test');
+ const refElm = document.getElementById('ref');
+ assert_equals(getComputedStyle(testElm).height,
+ getComputedStyle(refElm).height, 'height');
+ assert_equals(testElm.querySelector('legend').offsetTop,
+ testElm.querySelector('div').offsetTop, 'offsetTop')
+ }, "Flex");
+
+ test(() => {
+ const testElm = document.getElementById('test-inline');
+ const refElm = document.getElementById('ref-inline');
+ assert_equals(getComputedStyle(testElm).height,
+ getComputedStyle(refElm).height, 'height');
+ assert_equals(testElm.querySelector('legend').offsetTop,
+ testElm.querySelector('div').offsetTop, 'offsetTop')
+
+ }, "Inline flex");
+
+test(() => {
+ const testElm = document.getElementById('test');
+ testElm.style.flexDirection = 'row';
+ const item0 = testElm.querySelectorAll('div')[0];
+ const item1 = testElm.querySelectorAll('div')[1];
+ assert_equals(item0.offsetTop, item1.offsetTop);
+
+ testElm.style.flexDirection = 'column';
+ assert_true(item0.offsetTop < item1.offsetTop);
+}, "Dynamic change of flex-direction");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-foo-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-foo-ref.html
new file mode 100644
index 0000000000..cace814a8c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-foo-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>Reference with a fieldset and legend "Foo"</title>
+<p>There should be a normal fieldset below with the legend "Foo".</p>
+<fieldset>
+ <legend>Foo</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-generated-content.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-generated-content.html
new file mode 100644
index 0000000000..25a36bc42c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-generated-content.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>fieldset generated content</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+body {
+ /* Use Ahem to avoid fractional letter widths. */
+ font: 20px/1 Ahem;
+}
+
+fieldset {
+ display: inline-block;
+ /* Paddings might have fractional values by default, and they can cause
+ rounding differences between the test element and the reference element. */
+ padding: 0;
+}
+
+#test::before, #test::after { content:"X"; }
+</style>
+<fieldset id=test><legend>A</legend>Y</fieldset>
+<fieldset id=ref><legend>A</legend>XYX</fieldset>
+<script>
+test(() => {
+ const testElm = document.querySelector('#test');
+ const refElm = document.querySelector('#ref');
+ assert_equals(testElm.clientWidth, refElm.clientWidth, 'clientWidth');
+ assert_equals(testElm.clientHeight, refElm.clientHeight, 'clientHeight');
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-grid.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-grid.html
new file mode 100644
index 0000000000..bbb71dfa70
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-grid.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<title>fieldset and CSS Grid</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#test, #ref, #test-inline, #ref-inline {
+ display: grid;
+ grid-template-columns: auto 50px auto;
+ grid-template-rows: auto 50px auto;
+ margin: 0;
+ padding: 0;
+ border: none
+}
+#test-inline, #ref-inline { display: inline-grid }
+legend {
+ float: left; /* Makes it not the "rendered legend" */
+ padding: 0;
+}
+</style>
+<fieldset id=test>
+ <legend>1</legend>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</fieldset>
+<hr>
+<div id=ref>
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</div>
+<hr>
+<fieldset id=test-inline>
+ <legend>1</legend>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</fieldset>
+<div id=ref-inline>
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</div>
+<script>
+ test(() => {
+ const testElm = document.getElementById('test');
+ const refElm = document.getElementById('ref');
+ assert_equals(getComputedStyle(testElm).height,
+ getComputedStyle(refElm).height, 'height');
+ assert_equals(testElm.querySelector('legend').offsetTop,
+ testElm.querySelector('div').offsetTop, 'offsetTop')
+ }, "Grid");
+
+ test(() => {
+ const testElm = document.getElementById('test-inline');
+ const refElm = document.getElementById('ref-inline');
+ assert_equals(getComputedStyle(testElm).height,
+ getComputedStyle(refElm).height, 'height');
+ assert_equals(testElm.querySelector('legend').offsetTop,
+ testElm.querySelector('div').offsetTop, 'offsetTop')
+
+ }, "Inline grid");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item-ref.html
new file mode 100644
index 0000000000..05b8ca4770
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Reference for fieldset and dipslay: list-item</title>
+<style>
+ fieldset { margin: 0 40px; }
+</style>
+<p>There should be no bullet points below.</p>
+<fieldset>
+ <legend>X</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item.html
new file mode 100644
index 0000000000..7726947bec
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-list-item.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>fieldset and dipslay: list-item</title>
+<link rel=match href=fieldset-list-item-ref.html>
+<style>
+ fieldset { margin: 0 40px; display: list-item; }
+</style>
+<p>There should be no bullet points below.</p>
+<fieldset>
+ <legend>X</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html
new file mode 100644
index 0000000000..07c9da85b5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<style>
+.fieldset {
+ border: 2px solid gray;
+ margin: 1em;
+ padding: 0;
+ width: 20em;
+}
+
+.f1 {
+ overflow: auto;
+ max-height: 3em;
+}
+.f2 {
+ max-height: 0;
+}
+</style>
+
+<div class="fieldset f1">
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+</div>
+
+<div class="fieldset f1">
+<div>foo</div>
+</div>
+
+<div class="fieldset f2">
+<div>foo</div>
+</div>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size.html
new file mode 100644
index 0000000000..170dedd606
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="http://crbug.com/1151858">
+<link rel="match" href="fieldset-max-block-size-ref.html">
+<style>
+fieldset {
+ border: 2px solid gray;
+ margin: 1em;
+ padding: 0;
+ width: 20em;
+}
+
+.f1 {
+ overflow: auto;
+ max-height: 3em;
+}
+.f2 {
+ max-height: 0;
+}
+</style>
+
+<fieldset class="f1">
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+</fieldset>
+
+<fieldset class="f1">
+<div>foo</div>
+</fieldset>
+
+<fieldset class="f2">
+<div>foo</div>
+</fieldset>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-multicol.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-multicol.html
new file mode 100644
index 0000000000..bdb2c2fd94
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-multicol.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>fieldset multicol</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ #test { margin: 0; padding: 0; border: none }
+ #test, #ref { columns: 5 }
+ p { margin: 0 }
+</style>
+<fieldset id=test>
+ <p>1
+ <p>2
+ <p>3
+ <p>4
+ <p>5
+</fieldset>
+<div id=ref>
+ <p>1
+ <p>2
+ <p>3
+ <p>4
+ <p>5
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(document.getElementById('test')).height,
+ getComputedStyle(document.getElementById('ref')).height);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview.html
new file mode 100644
index 0000000000..c47c7cdd5d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-cssomview.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+fieldset {
+ height: 200px;
+ overflow: scroll;
+ padding: 0;
+}
+
+.content {
+ height: 400px;
+}
+</style>
+
+<fieldset>
+ <legend>Legend</legend>
+ <div class="content"></div>
+</fieldset>
+
+<script>
+test(() => {
+ const fieldset = document.querySelector('fieldset');
+ assert_equals(getComputedStyle(fieldset)['overflow-x'], 'scroll');
+ assert_equals(getComputedStyle(fieldset)['overflow-y'], 'scroll');
+ assert_equals(fieldset.scrollHeight, 400);
+ fieldset.scrollTop = 500;
+ assert_greater_than_equal(fieldset.scrollTop, 200);
+}, 'Test cssom-view API for FIELDSET');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden-ref.html
new file mode 100644
index 0000000000..9fe632f7c2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<title>Reference for fieldset and overflow</title>
+<p>It should say PASS below.</p>
+PASS
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden.html
new file mode 100644
index 0000000000..cacbdbae00
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-hidden.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>fieldset and overflow</title>
+<link rel=match href=fieldset-overflow-hidden-ref.html>
+<style>
+fieldset { margin:0; padding: 0; overflow: hidden; border: none; border-top: 1em solid transparent; }
+legend { padding: 0; }
+</style>
+<p>It should say PASS below.</p>
+<fieldset>
+ <legend>PASS</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-ref.html
new file mode 100644
index 0000000000..42ecb22aef
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow-ref.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<title>Reference for fieldset overflow</title>
+<style>
+.fieldset, .legend {
+ border: 1em solid;
+ background: lime;
+}
+.fieldset {
+ height: 1em;
+ margin-top: 2em;
+}
+.legend {
+ height: 1em;
+ width: 5em;
+ line-height: 1;
+ margin-top: -4em;
+ margin-left: 1em;
+ margin-bottom: 1em;
+}
+
+.fieldset2 {
+ background: lime;
+ padding: 50px;
+ max-height: 50px;
+ overflow: scroll;
+}
+
+.fieldset3 {
+ width: 20em;
+ max-height: 250px;
+ padding: 7px;
+ margin: 0;
+ overflow: auto;
+ box-sizing: border-box;
+ border: 1em solid transparent;
+ border-top: 40px solid transparent;
+}
+</style>
+<p>There should be no red.</p>
+<div class=fieldset>
+</div>
+<div class=legend></div>
+
+<div class=fieldset2>
+ <div style="height:200px; background:blue"></div>
+</div>
+
+<div class="fieldset3">
+ <p>
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+ </p>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow.html
new file mode 100644
index 0000000000..83813f9020
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-overflow.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<title>fieldset overflow</title>
+<link rel=match href=fieldset-overflow-ref.html>
+<style>
+fieldset, legend {
+ border: 1em solid;
+ margin: 0;
+ padding: 0;
+ background: lime
+}
+#f1 {
+ overflow: auto;
+ height: 2em;
+}
+legend {
+ height: 1em;
+ width: 5em;
+}
+div {
+ background: red;
+ height: 2em;
+}
+
+#f2 {
+ border: none;
+ padding: 50px;
+ max-height: 50px;
+ overflow: scroll;
+}
+
+#f3 {
+ width: 20em;
+ max-height: 250px;
+ padding: 7px;
+ overflow: auto;
+ box-sizing: border-box;
+ border-color: transparent;
+ background: transparent;
+}
+
+#f3 legend {
+ height: 40px;
+ border: none;
+ color: transparent;
+ background: transparent;
+}
+</style>
+<p>There should be no red.</p>
+<fieldset id="f1">
+ <legend></legend>
+ <div></div>
+ <div id=last></div>
+</fieldset>
+
+<!-- crbug.com/1247733 -->
+<fieldset id="f2">
+ <div style="height:200px; background:blue"></div>
+</fieldset>
+<script>
+ document.getElementById('last').scrollIntoView();
+</script>
+
+<!-- crbug.com/1282408 -->
+<fieldset id="f3">
+ <legend>Legend</legend>
+ <p>
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+The quick brown fox jumps over the lazy dog.
+ </p>
+</fieldset>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order-ref.html
new file mode 100644
index 0000000000..13b262a804
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order-ref.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Reference for fieldset painting order</title>
+<style>
+div { width: 200px; height: 200px; }
+#a { background: green; }
+#b { background: lime; position: relative; top: -100px; left: 100px; }
+</style>
+<p>There should be no red.</p>
+<div id=a></div>
+<div id=b></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html
new file mode 100644
index 0000000000..7bd2cedb1b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>fieldset painting order</title>
+<link rel=match href=fieldset-painting-order-ref.html>
+<style>
+fieldset, legend { margin: 0; padding: 0; }
+fieldset {
+ border: 100px solid red;
+ width: 0;
+ min-width: 0;
+ height: 0;
+}
+legend { width: 200px; height: 200px; margin-left: -100px; background: green; }
+legend > span { float: right; margin-top: 100px; width: 100px; height: 100px; background: red; }
+fieldset > div { margin-top: -100px; background: lime; width: 200px; height: 200px; }
+</style>
+<p>There should be no red.</p>
+<fieldset><legend><span></span></legend><div></div></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-block-size.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-block-size.html
new file mode 100644
index 0000000000..e98de892d3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-block-size.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<title>fieldset percentage block size</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ fieldset { block-size: 100px; margin: 20px; padding: 0; border: 10px solid; }
+ .rendered-legend { block-size: 80%; background: lime; padding: 0; }
+ .second-legend { block-size: 100%; background: yellow; padding: 0; }
+ fieldset > div { block-size: 100%; background: fuchsia; }
+</style>
+<div style="writing-mode: horizontal-tb">
+ <fieldset>
+ <legend class="rendered-legend">rendered legend</legend>
+ <legend class="second-legend">second legend</legend>
+ <div>div</div>
+ </fieldset>
+</div>
+
+<div style="writing-mode: vertical-lr">
+ <fieldset>
+ <legend class="rendered-legend">rendered legend</legend>
+ <legend class="second-legend">second legend</legend>
+ <div>div</div>
+ </fieldset>
+</div>
+
+<div style="writing-mode: vertical-rl">
+ <fieldset>
+ <legend class="rendered-legend">rendered legend</legend>
+ <legend class="second-legend">second legend</legend>
+ <div>div</div>
+ </fieldset>
+</div>
+
+<table cellspacing="0" cellpadding="0" style="width:100px; height:60px;">
+ <tr>
+ <td>
+ <fieldset style="border:none; padding:0; height:100%; margin:0; margin-top:13px;">
+ <div><div id="elm"></div></div>
+ </fieldset>
+ </td>
+ </tr>
+</table>
+
+<script>
+ for (const div of document.querySelectorAll('div[style]')) {
+ for (const el of div.firstElementChild.children) {
+ test(() => {
+ const expected = el.textContent === 'rendered legend' ? '80px' : '30px';
+ // 30px because: 100px - (max(0, legend-block-size - border-block-start))
+ assert_equals(getComputedStyle(el).blockSize, expected);
+ }, `${el.textContent} (${div.getAttribute('style')})`);
+ }
+ }
+
+// crbug.com/1138204. Though the specification doesn't mention this behavior,
+// there must be no doubt about the expected behavior.
+test(() => {
+ const fieldset = document.querySelector('table fieldset');
+ const initialHeight = fieldset.offsetHeight;
+ document.querySelector('#elm').style.display = 'none';
+ assert_equals(fieldset.offsetHeight, initialHeight);
+}, 'Fieldset with a percentage height should not increase the height on every reflow.');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-padding.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-padding.html
new file mode 100644
index 0000000000..61ad4ed4c9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percentage-padding.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>fieldset percentage padding</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ body { margin: 0; }
+ .outer { width: 500px; position: relative; }
+ fieldset { width: 100px; padding: 10%; margin: 0; border: none; }
+ .overflow { overflow: auto; }
+</style>
+<div class=outer>
+ <fieldset>
+ <div id=no-overflow>x</div>
+ </fieldset>
+</div>
+<div class=outer>
+ <fieldset class=overflow>
+ <div id=with-overflow>x</div>
+ </fieldset>
+</div>
+<script>
+const noOverflow = document.getElementById('no-overflow');
+const withOverflow = document.getElementById('with-overflow');
+for (const div of [noOverflow, withOverflow]) {
+ test(() => {
+ assert_equals(div.offsetLeft, 50, "offsetLeft");
+ assert_equals(div.clientWidth, 100, "clientWidth");
+ assert_equals(div.offsetTop, 50, "offsetTop");
+ }, div.id);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize-ref.html
new file mode 100644
index 0000000000..e77bb69d7f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<style>
+ div {
+ border: 1px solid;
+ padding: 10px;
+ resize: both;
+ overflow: scroll;
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize.html
new file mode 100644
index 0000000000..6a34a7465c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-resize.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>resize property on fieldset</title>
+<link rel="match" href="fieldset-resize-ref.html">
+<style>
+ fieldset {
+ margin: 0;
+ border: 1px solid;
+ padding: 10px;
+ resize: both;
+ overflow: scroll;
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<fieldset></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-shadow-dom.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-shadow-dom.html
new file mode 100644
index 0000000000..3b46eb03c6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-shadow-dom.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>fieldset and shadow DOM</title>
+<link rel=fieldset-foo-ref.html>
+<p>There should be a normal fieldset below with the legend "Foo".</p>
+<template id="my-fieldset">
+ <fieldset><slot name="my-text"></slot></fieldset>
+</template>
+
+<my-fieldset>
+ <legend slot="my-text">Foo</legend>
+</my-fieldset>
+
+<script>
+customElements.define('my-fieldset',
+ class extends HTMLElement {
+ constructor() {
+ super();
+
+ const template = document.getElementById('my-fieldset');
+ const templateContent = template.content;
+
+ this.attachShadow({mode: 'open'}).appendChild(
+ templateContent.cloneNode(true)
+ );
+ }
+ }
+);
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez-ref.html
new file mode 100644
index 0000000000..8200e67194
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Reference for Fieldset and transform: translateZ(0)</title>
+<style>
+fieldset { background: #eee; margin: 0 0 10px; }
+</style>
+<p>It should say PASS below without anything obscuring the text.</p>
+
+<fieldset>
+ <legend>PASS</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez.html
new file mode 100644
index 0000000000..df30468b00
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-transform-translatez.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Fieldset and transform: translateZ(0)</title>
+<link rel=match href=fieldset-transform-translatez-ref.html>
+<style>
+#outer { transform: translateZ(0); }
+fieldset { background: #eee; overflow: hidden; margin: 0 0 10px; }
+#inner { position: relative; }
+</style>
+<p>It should say PASS below without anything obscuring the text.</p>
+<div id=outer>
+ <fieldset>
+ <legend>
+ <div id="inner">PASS</div>
+ </legend>
+ </fieldset>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical-ref.html
new file mode 100644
index 0000000000..29c28ea5f8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Reference for fieldset vertical</title>
+<link rel=stylesheet href=resources/fieldset-vertical.css>
+<p>vertical-lr
+<div style="writing-mode: vertical-lr">
+ <div class=fieldset><div class="legend top">foo bar</div>normal</div>
+ <div class="fieldset rtl"><div class="legend bottom">foo bar</div>dir=rtl</div>
+ <div class="fieldset rtl"><div class="legend top">foo bar</div>dir=rtl align=left</div>
+ <div class="fieldset rtl"><div class="legend center">foo bar</div>dir=rtl align=center</div>
+ <div class="fieldset rtl"><div class="legend bottom">foo bar</div>dir=rtl align=right</div>
+ <div class=fieldset><div class="legend top">foo bar</div>align=left</div>
+ <div class=fieldset><div class="legend center">foo bar</div>align=center</div>
+ <div class=fieldset><div class="legend bottom">foo bar</div>align=right</div>
+</div>
+<hr>
+<p>vertical-rl
+<div style="writing-mode: vertical-rl">
+ <div class=fieldset><div class="legend top">foo bar</div>normal</div>
+ <div class="fieldset rtl"><div class="legend bottom">foo bar</div>dir=rtl</div>
+ <div class="fieldset rtl"><div class="legend top">foo bar</div>dir=rtl align=left</div>
+ <div class="fieldset rtl"><div class="legend center">foo bar</div>dir=rtl align=center</div>
+ <div class="fieldset rtl"><div class="legend bottom">foo bar</div>dir=rtl align=right</div>
+ <div class=fieldset><div class="legend top">foo bar</div>align=left</div>
+ <div class=fieldset><div class="legend center">foo bar</div>align=center</div>
+ <div class=fieldset><div class="legend bottom">foo bar</div>align=right</div></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html
new file mode 100644
index 0000000000..c11b466669
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>fieldset vertical</title>
+<link rel=stylesheet href=resources/fieldset-vertical.css>
+<link rel=match href=fieldset-vertical-ref.html>
+<p>vertical-lr
+<div style="writing-mode: vertical-lr">
+ <fieldset><legend>foo bar</legend>normal</fieldset>
+ <fieldset dir=rtl><legend>foo bar</legend>dir=rtl</fieldset>
+ <fieldset dir=rtl><legend align=left>foo bar</legend>dir=rtl align=left</fieldset>
+ <fieldset dir=rtl><legend align=center>foo bar</legend>dir=rtl align=center</fieldset>
+ <fieldset dir=rtl><legend align=right>foo bar</legend>dir=rtl align=right</fieldset>
+ <fieldset><legend align=left>foo bar</legend>align=left</fieldset>
+ <fieldset><legend align=center>foo bar</legend>align=center</fieldset>
+ <fieldset><legend align=right>foo bar</legend>align=right</fieldset>
+</div>
+<hr>
+<p>vertical-rl
+<div style="writing-mode: vertical-rl">
+ <fieldset><legend>foo bar</legend>normal</fieldset>
+ <fieldset dir=rtl><legend>foo bar</legend>dir=rtl</fieldset>
+ <fieldset dir=rtl><legend align=left>foo bar</legend>dir=rtl align=left</fieldset>
+ <fieldset dir=rtl><legend align=center>foo bar</legend>dir=rtl align=center</fieldset>
+ <fieldset dir=rtl><legend align=right>foo bar</legend>dir=rtl align=right</fieldset>
+ <fieldset><legend align=left>foo bar</legend>align=left</fieldset>
+ <fieldset><legend align=center>foo bar</legend>align=center</fieldset>
+ <fieldset><legend align=right>foo bar</legend>align=right</fieldset>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/flex-legend-float-abspos.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/flex-legend-float-abspos.html
new file mode 100644
index 0000000000..f6eead471c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/flex-legend-float-abspos.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<title>
+ legend and float and position: absolute/fixed when the display type of
+ the fieldset is flex.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ body { margin: 0; }
+ fieldset {
+ border: 10px solid;
+ display: flex;
+ margin: 0;
+ padding: 20px;
+ width: 300px;
+ }
+ legend { height: 10px; }
+ #legend1 { float: left; }
+ #legend2 { float: right; }
+ #legend3 { position: absolute; }
+ #legend4 { position: fixed; }
+</style>
+<fieldset id=fieldset>
+ <div>div</div>
+ <legend id=legend1>legend1</legend>
+ <legend id=legend2>legend2</legend>
+ <legend id=legend3>legend3</legend>
+ <legend id=legend4>legend4</legend>
+ <legend id=legend5>legend5</legend>
+</fieldset>
+<script>
+ const fieldset = document.getElementById('fieldset');
+ const legends = document.getElementsByTagName('legend');
+ const [legend1, legend2, legend3, legend4, legend5] = legends;
+ const expectedTop = 0;
+ const expectedLeft = 10 + 20;
+
+ function assert_rendered_legend(legend) {
+ assert_equals(legend.offsetTop, expectedTop, `${legend.id}.offsetTop`);
+ assert_equals(legend.offsetLeft, expectedLeft, `${legend.id}.offsetLeft`);
+ for (const other of legends) {
+ if (other === legend) {
+ continue;
+ }
+ if (other.offsetTop === expectedTop && other.offsetLeft === expectedLeft) {
+ assert_unreached(`${other.id} should not be the "rendered legend"`);
+ }
+ }
+ }
+
+ test(t => {
+ assert_rendered_legend(legend5);
+ }, 'no dynamic changes');
+
+ test(t => {
+ const legend = document.createElement('legend');
+ t.add_cleanup(() => {
+ legend.remove();
+ });
+ legend.id = 'script-inserted';
+ legend.textContent = 'script-inserted legend';
+ fieldset.insertBefore(legend, legend1);
+ assert_rendered_legend(legend);
+ legend.remove();
+ assert_rendered_legend(legend5);
+ }, 'inserting a new legend and removing it again');
+
+ test(t => {
+ t.add_cleanup(() => {
+ legend1.id = 'legend1';
+ legend2.id = 'legend2';
+ });
+ legend2.id = '';
+ assert_rendered_legend(legend2);
+ legend1.id = '';
+ assert_rendered_legend(legend1);
+ legend1.id = 'legend1';
+ assert_rendered_legend(legend2);
+ legend2.id = 'legend2';
+ assert_rendered_legend(legend5);
+ }, 'dynamic changes to float');
+
+ test(t => {
+ t.add_cleanup(() => {
+ legend3.id = 'legend3';
+ legend4.id = 'legend4';
+ });
+ legend4.id = '';
+ assert_rendered_legend(legend4);
+ legend3.id = '';
+ assert_rendered_legend(legend3);
+ legend3.id = 'legend3';
+ assert_rendered_legend(legend4);
+ legend4.id = 'legend4';
+ assert_rendered_legend(legend5);
+ }, 'dynamic changes to position');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation-ref.html
new file mode 100644
index 0000000000..954bcab573
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<p>There should be a green box below.</p>
+<div style="background: green; width: 100px; height: 100px;"></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation.html
new file mode 100644
index 0000000000..aa51ebac62
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/grid-template-propagation.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="match" href="grid-template-propagation-ref.html">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1222988">
+<link rel="help" href="https://html.spec.whatwg.org/C/#anonymous-fieldset-content-box">
+<style>
+fieldset {
+ display: grid;
+ grid-template: auto / 1fr;
+ grid-template-areas: "a";
+ width: 100px;
+ height: 100px;
+ margin: 0;
+ border: none;
+ padding: 0;
+}
+</style>
+<p>There should be a green box below.</p>
+<fieldset>
+ <div style="background: green; grid-area: a"></div>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/insert-legend-in-multicol-fieldset-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/insert-legend-in-multicol-fieldset-crash.html
new file mode 100644
index 0000000000..aad552dbe2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/insert-legend-in-multicol-fieldset-crash.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1211926">
+<fieldset id="fieldset">
+ <legend id="legend"></legend>
+ <div></div>
+</fieldset>
+<script>
+ document.body.offsetTop;
+ fieldset.style.columns = "2";
+ legend.style.position = "relative";
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-justify-self.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-justify-self.html
new file mode 100644
index 0000000000..29df29d177
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-justify-self.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>legend align to justify-self</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<fieldset><legend>x</legend></fieldset>
+<fieldset><legend align=left>x</legend></fieldset>
+<fieldset><legend align=center>x</legend></fieldset>
+<fieldset><legend align=right>x</legend></fieldset>
+<fieldset><legend align=lEfT>x</legend></fieldset>
+<fieldset><legend align=cEnTeR>x</legend></fieldset>
+<fieldset><legend align=rIgHt>x</legend></fieldset>
+<!-- invalid values -->
+<fieldset><legend align=justify>x</legend></fieldset>
+<fieldset><legend align="left ">x</legend></fieldset>
+<!-- dir -->
+<fieldset><legend dir=ltr>x</legend></fieldset>
+<fieldset><legend dir=rtl>x</legend></fieldset>
+<fieldset dir=rtl><legend dir=ltr>x</legend></fieldset>
+<fieldset dir=rtl><legend dir=rtl>x</legend></fieldset>
+<script>
+for (const fieldset of document.querySelectorAll('fieldset')) {
+ test(() => {
+ const legend = fieldset.firstChild;
+ const align = legend.align.toLowerCase();
+ let expected = 'auto';
+ switch (align) {
+ case 'left': expected = 'left'; break;
+ case 'center': expected = 'center'; break;
+ case 'right': expected = 'right'; break;
+ }
+ assert_equals(getComputedStyle(legend).justifySelf, expected);
+ }, `${fieldset.outerHTML}`)
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-text-align.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-text-align.html
new file mode 100644
index 0000000000..01483bf8ad
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align-text-align.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>legend align does not map to text-align</title>
+<!-- See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1488228 -->
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ legend { width: 13em }
+</style>
+<fieldset><legend>foo bar abcdefghijklmnopqrstuvwxyz</legend></fieldset>
+<fieldset><legend align=left>foo bar abcdefghijklmnopqrstuvwxyz</legend></fieldset>
+<fieldset><legend align=center>foo bar abcdefghijklmnopqrstuvwxyz</legend></fieldset>
+<fieldset><legend align=right>foo bar abcdefghijklmnopqrstuvwxyz</legend></fieldset>
+<fieldset><legend align=justify>foo bar abcdefghijklmnopqrstuvwxyz</legend></fieldset>
+<script>
+function test_align(selectorTest, expectedAlign) {
+ const testElm = document.querySelector(selectorTest);
+ test(() => {
+ assert_equals(getComputedStyle(testElm).textAlign, expectedAlign);
+ }, selectorTest);
+}
+
+test_align('legend', 'start');
+
+for (const val of ['left', 'center', 'right', 'justify']) {
+ test_align(`legend[align=${val}]`, 'start');
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align.html
new file mode 100644
index 0000000000..e774599819
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-align.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<title>legend align</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<fieldset><legend align=left>x</legend></fieldset>
+<fieldset><legend align=center>x</legend></fieldset>
+<fieldset><legend align=right>x</legend></fieldset>
+<fieldset><legend align=justify>x</legend></fieldset>
+<div align=left>
+ <fieldset><legend>x</legend></fieldset>
+</div>
+<div align=center>
+ <fieldset><legend>x</legend></fieldset>
+</div>
+<div align=right>
+ <fieldset><legend>x</legend></fieldset>
+</div>
+<div align=justify>
+ <fieldset><legend>x</legend></fieldset>
+</div>
+<div style="text-align: center">
+ <fieldset><legend>x</legend></fieldset>
+</div>
+<div style="text-align: center" align=center>
+ <fieldset><legend>x</legend></fieldset>
+</div>
+<fieldset><legend style="margin: 0 auto">x</legend></fieldset>
+<fieldset><legend style="margin: 0 0 0 auto">x</legend></fieldset>
+<fieldset dir=rtl><legend>x</legend></fieldset>
+<fieldset dir=rtl><legend style="text-align: left">x</legend></fieldset>
+<script>
+function test_align(selectorTest, selectorRef) {
+ const testElm = document.querySelector(selectorTest);
+ const refElm = document.querySelector(selectorRef);
+ test(() => {
+ assert_equals(testElm.offsetLeft, refElm.offsetLeft, `expected ${selectorRef}`);
+ }, selectorTest);
+}
+
+for (const val of ['left', 'center', 'right', 'justify']) {
+ test_align(`div[align=${val}] legend`, `legend[align=left]`);
+}
+
+test_align(`div[style="text-align: center"] legend`, `legend[align=left]`);
+test_align(`div[style="text-align: center"][align=center] legend`, `legend[align=left]`);
+test_align(`legend[style="margin: 0 auto"]`, `legend[align=center]`);
+test_align(`legend[style="margin: 0 0 0 auto"]`, `legend[align=right]`);
+test_align(`fieldset[dir=rtl] legend`, `legend[align=right]`);
+test_align(`fieldset[dir=rtl] legend[style="text-align: left"]`, `legend[align=right]`);
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins-ref.html
new file mode 100644
index 0000000000..8b1258727f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>legend inline auto margins</title>
+<link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1488301">
+<style>
+body, html { padding:0; margin: 0; }
+div {
+ border: 1px solid black;
+ border-width: 10px 17px 7px 23px;
+ padding: 0;
+ margin: 0;
+ width: 448px;
+ height: 5px;
+ margin-top: 5px;
+ position: relative;
+}
+span {
+ position: absolute;
+ top: -15px;
+ width: 200px;
+ height: 20px;
+ padding: 0;
+ margin: 0;
+ background: grey;
+}
+center { width: 200px; height: 20px; background: red; }
+</style>
+</head>
+<body>
+ <div><span style="right:17px"></span></div>
+ <div><span style="left:31px"></span></div>
+ <div><span style="left:131px"></span></div>
+ <div><span style="right:32px"></span></div>
+ <div><span style="left:46px"></span></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins.html
new file mode 100644
index 0000000000..dd1964ba25
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-auto-margins.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>legend inline auto margins</title>
+<link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1488301">
+<link rel="match" href="legend-auto-margins-ref.html">
+<style>
+body, html { padding:0; margin: 0; }
+fieldset {
+ border: 1px solid black;
+ border-width: 10px 17px 7px 23px;
+ padding: 0 17px 0 31px;
+ margin: 0;
+ width: 400px;
+}
+legend {
+ width: 200px;
+ height: 20px;
+ padding: 0;
+ margin: 0;
+ background: grey;
+}
+</style>
+</head>
+<body>
+ <fieldset><legend style="margin-left:auto"></legend></fieldset>
+ <fieldset><legend style="margin-right:auto"></legend></fieldset>
+ <fieldset><legend style="margin:0 auto"></legend></fieldset>
+ <fieldset><legend style="margin:0 15px 0 auto"></legend></fieldset>
+ <fieldset><legend style="margin:0 auto 0 15px"></legend></fieldset>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-formatting-context.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-formatting-context.html
new file mode 100644
index 0000000000..4e95391797
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-formatting-context.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<title>The legend element: block formatting context</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+/* Set margin and padding for fieldset to 0 to make things simpler */
+fieldset {
+ margin: 0;
+ padding: 0;
+}
+.wrapper {
+ height: 200px;
+ position: relative;
+}
+.float {
+ float: left;
+ width: 50px;
+ height: 50px;
+ background-color: orange;
+}
+</style>
+
+<div class=wrapper>
+ <div class=float></div>
+ <legend id=in-body><div class=float></div></legend>
+ <div class=float></div>
+</div>
+
+<div class=wrapper>
+ <fieldset>
+ <div class=float></div>
+ <legend id=rendered-legend><div class=float></div></legend>
+ <legend id=in-fieldset-second-child><div class=float></div></legend>
+ <div><legend id=in-fieldset-descendant><div class=float></div></legend></div>
+ </fieldset>
+</div>
+
+<script>
+const fieldsetBorderWidth = 2;
+const legendPadding = 2;
+
+test(() => {
+ const legend = document.getElementById('in-body');
+ assert_equals(legend.offsetLeft, 0, 'legend.offsetLeft');
+ assert_equals(legend.offsetTop, 0, 'legend.offsetTop');
+ assert_equals(legend.clientHeight, 0, 'legend.clientHeight');
+ const divAfter = legend.nextElementSibling;
+ assert_equals(divAfter.offsetLeft, 100, 'divAfter.offsetLeft');
+ assert_equals(divAfter.offsetTop, 0, 'divAfter.offsetTop');
+}, 'in-body');
+
+test(() => {
+ const legend = document.getElementById('rendered-legend');
+ assert_equals(legend.offsetLeft, fieldsetBorderWidth, 'legend.offsetLeft');
+ assert_equals(legend.offsetTop, 0, 'legend.offsetTop');
+ assert_equals(legend.clientHeight, 50, 'legend.clientHeight');
+ const divChild = legend.firstChild;
+ assert_equals(divChild.offsetLeft, fieldsetBorderWidth + legendPadding, 'divChild.offsetLeft');
+ assert_equals(divChild.offsetTop, 0, 'divChild.offsetTop');
+}, 'rendered-legend');
+
+test(() => {
+ const legend = document.getElementById('in-fieldset-second-child');
+ assert_equals(legend.offsetLeft, fieldsetBorderWidth, 'legend.offsetLeft');
+ assert_equals(legend.offsetTop, 50, 'legend.offsetTop');
+ assert_equals(legend.clientHeight, 0, 'legend.clientHeight');
+ const divChild = legend.firstChild;
+ assert_equals(divChild.offsetLeft, fieldsetBorderWidth + 50, 'divChild.offsetLeft');
+ assert_equals(divChild.offsetTop, 50, 'divChild.offsetTop');
+}, 'in-fieldset-second-child');
+
+test(() => {
+ const legend = document.getElementById('in-fieldset-descendant');
+ assert_equals(legend.offsetLeft, fieldsetBorderWidth, 'legend.offsetLeft');
+ assert_equals(legend.offsetTop, 50, 'legend.offsetTop');
+ assert_equals(legend.clientHeight, 0, 'legend.clientHeight');
+ const divChild = legend.firstChild;
+ assert_equals(divChild.offsetLeft, fieldsetBorderWidth + 50 + 50, 'divChild.offsetLeft');
+ assert_equals(divChild.offsetTop, 50, 'divChild.offsetTop');
+}, 'in-fieldset-descendant');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2-ref.html
new file mode 100644
index 0000000000..1680cc8884
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2-ref.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Reference for legend block-axis margins</title>
+<style>
+.fieldset {
+ display: block;
+ position: relative;
+ width: 40px;
+ border: 2px solid blue;
+ padding: 4px;
+}
+.legend {
+ display: block;
+ position: absolute;
+ width: 10px;
+ height: 20px;
+ padding: 0;
+ margin: 0;
+ background: grey;
+}
+
+.t2 .fieldset { border-top-width: 12px; }
+.t3 .fieldset { border-top-width: 12px; }
+.t3 .legend { height: 12px; background:white; }
+.legend > x { display:block; position:relative; height:6px; background:grey; margin-top:3px; }
+
+div { border: 1px solid; margin: 0 2px 10px 0; }
+c { display:block; height:10px; background: lightgrey; }
+f { float: left; }
+</style>
+</head>
+<body>
+
+<f>
+<div>
+ <span class="fieldset" style="margin-top:9px"><span class="legend" style="top:-11px"></span><c style="margin-top:9px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset" style="margin-top:19px"><span class="legend" style="top:-11px"></span><c style="margin-top:9px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-12px"></span><c style="margin-top:8px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-20px"></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-20px"></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset" style="margin-top:9px"><span class="legend" style="top:-11px"></span><c style="margin-top:19px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset" style="margin-top:9px"><span class="legend" style="top:-11px"></span><c style="margin-top:29px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-2px; z-index:1"></span><c style="position:relative; z-index:1"></c></span>
+</div>
+</f>
+
+<f class=t2>
+<div>
+ <span class="fieldset" style="margin-top:4px"><span class="legend" style="top:-16px"></span><c style="margin-top:4px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset" style="margin-top:14px"><span class="legend" style="top:-16px"></span><c style="margin-top:4px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-20px"></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-20px"></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-20px"></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset" style="margin-top:4px"><span class="legend" style="top:-16px"></span><c style="margin-top:14px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset" style="margin-top:4px"><span class="legend" style="top:-16px"></span><c style="margin-top:24px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="top:-12px; height:16px"></span><c></c></span>
+</div>
+</f>
+
+<f class=t3>
+<div>
+ <span class="fieldset"><span class="legend" style="margin-top: -16px"><x></x></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset" style="margin-top: 4px"><span class="legend" style="margin-top: -16px"><x style="top:3px"></x></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="margin-top: -16px"><x></x></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="margin-top: -16px"><x></x></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="margin-top: -16px"><x></x></span><c></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="margin-top: -16px"><x style="top:-3px"></x></span><c style="margin-top:4px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="margin-top: -16px"><x style="top:-3px"></x></span><c style="margin-top:14px"></c></span>
+</div>
+
+<div>
+ <span class="fieldset"><span class="legend" style="margin-top: -16px"><x></x></span><c></c></span>
+</div>
+</f>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2.html
new file mode 100644
index 0000000000..9ca9e18adf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>legend block-axis margins</title>
+ <link rel="match" href="legend-block-margins-2-ref.html">
+<style>
+fieldset {
+ width: 40px;
+ border: 2px solid blue;
+ padding: 4px;
+ margin: 0;
+}
+legend {
+ width: 10px;
+ height: 20px;
+ padding: 0;
+ background: grey;
+
+}
+
+.t2 fieldset { border-top-width: 12px; }
+.t3 fieldset { border-top-width: 12px; }
+.t3 legend { height: 6px; }
+
+div { border: 1px solid; margin: 0 2px 10px 0; }
+c { display:block; height:10px; background: lightgrey; }
+f { float: left; }
+</style>
+</head>
+<body>
+
+<f>
+<div>
+ <fieldset><legend></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: 10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -20px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -30px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: 10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: 20px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: -20px"></legend><c></c></fieldset>
+</div>
+</f>
+
+<f class=t2>
+<div>
+ <fieldset><legend></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: 10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -20px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -30px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: 10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: 20px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: -20px"></legend><c></c></fieldset>
+</div>
+</f>
+
+<f class=t3>
+<div>
+ <fieldset><legend></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: 10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -20px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-top: -30px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: 10px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: 20px"></legend><c></c></fieldset>
+</div>
+
+<div>
+ <fieldset><legend style="margin-bottom: -20px"></legend><c></c></fieldset>
+</div>
+</f>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-ref.html
new file mode 100644
index 0000000000..f2701d26f3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-ref.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>Reference for legend block margins</title>
+<style>
+body { margin: 0; }
+.fieldset { margin: 2em 1em 1em 1em; border: 1em solid green; }
+.legend { position: absolute; margin-top: -1em; margin-left: 1em; background: white; height: 1em; }
+.inner { margin: 3em 1em 1em 1em; height: 1em; }
+</style>
+<p>There should be no red.</p>
+<div class=fieldset>
+ <div class=legend>X</div>
+ <div class=inner>Y</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins.html
new file mode 100644
index 0000000000..98cd16c7c1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>legend block margins</title>
+<link rel=match href=legend-block-margins-ref.html>
+<style>
+ body { margin: 0; }
+ fieldset { margin: 1em; border: 1em solid green; padding: 0; background: white; }
+ legend { margin: 1em 1em 2em 1em; height: 1em; padding: 0; }
+ .inner { margin: 1em; height: 1em; }
+ .behind { position: absolute; left: 1em; right: 1em; margin-top: 1em; height: 7em; background: red; z-index: -1; }
+</style>
+<p>There should be no red.</p>
+<div class=behind></div>
+<fieldset>
+ <legend>X</legend>
+ <div class=inner>Y</div>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-position-centering.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-position-centering.html
new file mode 100644
index 0000000000..a4eda6e3ef
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-position-centering.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-fieldset-and-legend-elements">
+<!-- A test for the following paragraph:
+The element is expected to be positioned in the block-flow direction such that
+its border box is centered over the border on the block-start side of the
+fieldset element.
+-->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: 100px solid black;
+}
+legend {
+ height: 0px;
+ border-color: yellow;
+ border-style: solid;
+}
+</style>
+<fieldset>
+<legend style="border-width:50px"></legend>
+<br>
+</fieldset>
+<br>
+
+<fieldset>
+<legend style="border-width:25px 50px"></legend>
+<br>
+</fieldset>
+<br>
+
+<fieldset>
+<legend style="border-width:10px 50px 40px 50px"></legend>
+<br>
+</fieldset>
+<br>
+
+<fieldset>
+<legend style="border-width:40px 50px 10px 50px"></legend>
+<br>
+</fieldset>
+
+<script>
+function legendBlockOffset(fieldset) {
+ let legend = fieldset.querySelector('legend');
+ return legend.getBoundingClientRect().y - fieldset.getBoundingClientRect().y;
+}
+
+test(() => {
+ let fieldsets = document.querySelectorAll('fieldset');
+ assert_equals(legendBlockOffset(fieldsets[0]), 0);
+ assert_equals(legendBlockOffset(fieldsets[1]), 25);
+ assert_equals(legendBlockOffset(fieldsets[2]), 25);
+ assert_equals(legendBlockOffset(fieldsets[3]), 25);
+}, 'Legend\'s border-box should be centere on the fieldset border');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering-ref.html
new file mode 100644
index 0000000000..e6eff47e53
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Reference for Rendering of display: none legend</title>
+<style>
+ div { border: 2em solid lime; width: 0; }
+</style>
+<p>There should be a green box below.</p>
+<div></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering.html
new file mode 100644
index 0000000000..abf3c45df7
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none-rendering.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Rendering of display: none legend</title>
+<link rel=match href=legend-display-none-rendering-ref.html>
+<style>
+ fieldset { border: 2em solid lime; width: 0; margin: 0; padding: 0; }
+ legend { display: none; background: red; }
+</style>
+<p>There should be a green box below.</p>
+<fieldset>
+ <legend>FAIL</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none.html
new file mode 100644
index 0000000000..689454ac49
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-none.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>legend display: none</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ legend { display: none; }
+</style>
+<fieldset>
+ <legend>Foo</legend>
+</fieldset>
+<script>
+ test(() => {
+ const display = getComputedStyle(document.querySelector('legend')).display;
+ assert_equals(display, 'none');
+ });
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering-ref.html
new file mode 100644
index 0000000000..189b195f61
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering-ref.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Reference for rendered legend and CSS display rendering</title>
+<style>
+body { margin: 0; }
+div { width: 600px; box-sizing: border-box; background: #ddd; border: 1px solid; }
+.padding { padding-left: 5em; }
+.margin { margin-left: 5em; }
+.cell { display: inline-block; width: 50%; }
+.ruby { display: block; }
+</style>
+<div><span class=cell>table</span><span class=cell>table</span></div>
+<div class=padding>table-row-group</div>
+<div class=padding>table-header-group</div>
+<div class=padding>table-footer-group</div>
+<div class=padding>table-row</div>
+<div class=margin>table-cell</div>
+<div class=padding>table-column-group</div>
+<div class=padding>table-column</div>
+<div>table-caption</div>
+<div>flow</div>
+<div>flow-root</div>
+<div>run-in</div>
+<div>inline</div>
+<div>inline-block</div>
+<div><span class=cell>inline-table</span><span class=cell>inline-table</span></div>
+<div><span class=ruby>ruby</span><span class=ruby>ruby</span></div>
+<div>ruby-base</div>
+<div>ruby-text</div>
+<div>ruby-base-container</div>
+<div>ruby-text-container</div>
+<div><span class=cell>grid</span><span class=cell>grid</span></div>
+<div><span class=cell>inline-grid</span><span class=cell>inline-grid</span></div>
+<div><span class=cell>flex</span><span class=cell>flex</span></div>
+<div><span class=cell>inline-flex</span><span class=cell>inline-flex</span></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering.html
new file mode 100644
index 0000000000..ba6610503a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display-rendering.html
@@ -0,0 +1,119 @@
+<!doctype html>
+<title>rendered legend and CSS display rendering</title>
+<link rel=match href=legend-display-rendering-ref.html>
+<style>
+body { margin: 0; }
+fieldset { margin: 0; padding: 0; border: none; }
+legend { width: 600px; box-sizing: border-box; padding: 0; background: #ddd; border: 1px solid; }
+[style="display: table"] span,
+[style="display: inline-table"] span { display: table-cell; }
+[style="display: table-row-group"],
+[style="display: table-header-group"],
+[style="display: table-footer-group"],
+[style="display: table-row"],
+[style="display: table-column-group"],
+[style="display: table-column"] { padding-left: 5em; /* would be ignored if not blockified */ }
+[style="display: table-cell"] { margin-left: 5em; /* would be ignored if not blockified */ }
+.rb { display: ruby-base; }
+.rt { display: ruby-text; }
+[style="display: grid"],
+[style="display: inline-grid"] { grid-template-columns: auto auto; }
+[style="display: flex"] span,
+[style="display: inline-flex"] span { display: block; flex-grow: 1 }
+</style>
+<fieldset>
+ <legend style="display: table"><span>table</span><span>table</span></legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-row-group">table-row-group</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-header-group">table-header-group</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-footer-group">table-footer-group</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-row">table-row</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-cell">table-cell</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-column-group">table-column-group</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-column">table-column</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: table-caption">table-caption</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: flow">flow</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: flow-root">flow-root</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: run-in">run-in</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: inline">inline</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: inline-block">inline-block</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: inline-table"><span>inline-table</span><span>inline-table</span></legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: ruby"><span class=rb>ruby</span><span class=rt>ruby</span></legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: ruby-base">ruby-base</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: ruby-text">ruby-text</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: ruby-base-container">ruby-base-container</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: ruby-text-container">ruby-text-container</legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: grid"><span>grid</span><span>grid</span></legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: inline-grid"><span>inline-grid</span><span>inline-grid</span></legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: flex"><span>flex</span><span>flex</span></legend>
+</fieldset>
+
+<fieldset>
+ <legend style="display: inline-flex"><span>inline-flex</span><span>inline-flex</span></legend>
+</fieldset>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html
new file mode 100644
index 0000000000..b6c57a67ba
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>rendered legend and CSS display</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+legend { width:initial; }
+</style>
+<fieldset><legend id="ref">x</legend></fieldset>
+<fieldset><legend id="test">x</legend></fieldset>
+<script>
+ const refElm = document.querySelector('#ref');
+ const refStyle = getComputedStyle(refElm);
+ const testElm = document.querySelector('#test');
+ const values = ['block', 'table', 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row', 'table-cell',
+ 'table-column-group', 'table-column', 'table-caption', 'list-item', 'flow', 'flow-root','run-in','inline',
+ 'inline-block', 'inline-table', 'block ruby', 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', 'ruby-text-container',
+ 'grid', 'inline-grid', 'flex', 'inline-flex'];
+ const extraStyle = ['', 'overflow:hidden', 'columns:1', 'overflow:hidden;columns:1'];
+
+ for (const style of extraStyle) {
+ for (const val of values) {
+ test(() => {
+ testElm.style.removeProperty('display');
+ testElm.style = style;
+ testElm.style.display = val;
+ const computed = getComputedStyle(testElm);
+ // Note that computed value is different from the used value.
+ // E.g., if ruby is not supported, the following assertion will
+ // fail as the computed value of display will be block.
+ // If ruby is supported, computed.display will return "ruby",
+ // but the used value is supposed to be "block ruby".
+ // Also, 'flow' is serialized as 'block' for legacy reasons.
+ let expected = val == 'flow' ? 'block' : val;
+ assert_equals(computed.display, expected, `display: ${val} is not supported`);
+ assert_equals(computed.width, refStyle.width, 'width');
+ assert_equals(testElm.offsetLeft, refElm.offsetLeft, 'offsetLeft');
+ }, `rendered legend with display: ${val}` + (style == '' ? '' : "; " + style));
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-dynamic-update.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-dynamic-update.html
new file mode 100644
index 0000000000..5dc68244fe
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-dynamic-update.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>legend and dynamic update</title>
+<link rel=fieldset-foo-ref.html>
+<p>There should be a normal fieldset below with the legend "Foo".</p>
+<fieldset>
+ <legend>F</legend>
+</fieldset>
+<script>
+ const legend = document.querySelector('legend');
+ // force layout
+ legend.offsetTop;
+ requestAnimationFrame(() => {
+ legend.textContent += "oo";
+ requestAnimationFrame(() => {
+ document.documentElement.removeAttribute('class');
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-abspos.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-abspos.html
new file mode 100644
index 0000000000..7979e1d03e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-abspos.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<title>legend and float and position: absolute/fixed</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ body { margin: 0; }
+ fieldset { margin: 0; padding: 20px; border: 10px solid; width: 300px; }
+ legend { height: 10px; }
+ #legend1 { float: left; }
+ #legend2 { float: right; }
+ #legend3 { position: absolute; }
+ #legend4 { position: fixed; }
+</style>
+<fieldset id=fieldset>
+ <div>div</div>
+ <legend id=legend1>legend1</legend>
+ <legend id=legend2>legend2</legend>
+ <legend id=legend3>legend3</legend>
+ <legend id=legend4>legend4</legend>
+ <legend id=legend5>legend5</legend>
+</fieldset>
+<script>
+ const fieldset = document.getElementById('fieldset');
+ const legends = document.getElementsByTagName('legend');
+ const [legend1, legend2, legend3, legend4, legend5] = legends;
+ const expectedTop = 0;
+ const expectedLeft = 10 + 20;
+
+ function assert_rendered_legend(legend) {
+ assert_equals(legend.offsetTop, expectedTop, `${legend.id}.offsetTop`);
+ assert_equals(legend.offsetLeft, expectedLeft, `${legend.id}.offsetLeft`);
+ for (const other of legends) {
+ if (other === legend) {
+ continue;
+ }
+ if (other.offsetTop === expectedTop && other.offsetLeft === expectedLeft) {
+ assert_unreached(`${other.id} should not be the "rendered legend"`);
+ }
+ }
+ }
+
+ test(t => {
+ assert_rendered_legend(legend5);
+ }, 'no dynamic changes');
+
+ test(t => {
+ const legend = document.createElement('legend');
+ t.add_cleanup(() => {
+ legend.remove();
+ });
+ legend.id = 'script-inserted';
+ legend.textContent = 'script-inserted legend';
+ fieldset.insertBefore(legend, legend1);
+ assert_rendered_legend(legend);
+ legend.remove();
+ assert_rendered_legend(legend5);
+ }, 'inserting a new legend and removing it again');
+
+ test(t => {
+ t.add_cleanup(() => {
+ legend1.id = 'legend1';
+ legend2.id = 'legend2';
+ });
+ legend2.id = '';
+ assert_rendered_legend(legend2);
+ legend1.id = '';
+ assert_rendered_legend(legend1);
+ legend1.id = 'legend1';
+ assert_rendered_legend(legend2);
+ legend2.id = 'legend2';
+ assert_rendered_legend(legend5);
+ }, 'dynamic changes to float');
+
+ test(t => {
+ t.add_cleanup(() => {
+ legend3.id = 'legend3';
+ legend4.id = 'legend4';
+ });
+ legend4.id = '';
+ assert_rendered_legend(legend4);
+ legend3.id = '';
+ assert_rendered_legend(legend3);
+ legend3.id = 'legend3';
+ assert_rendered_legend(legend4);
+ legend4.id = 'legend4';
+ assert_rendered_legend(legend5);
+ }, 'dynamic changes to position');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-ref.html
new file mode 100644
index 0000000000..c51bca231e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Reference for floated legend should not disappear</title>
+<style>
+ div { width: 100px; height: 100px; background: lime; }
+</style>
+<p>There should be no red.</p>
+<div></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float.html
new file mode 100644
index 0000000000..f70e952ed0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-float.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>floated legend should not disappear</title>
+<link rel=match href=legend-float-ref.html>
+<style>
+ fieldset { margin: 0; padding: 0; border: none; width: 100px; height: 50px; background: red; }
+ legend { width: 100px; height: 50px; background: lime; padding: 0; }
+ .left { float: left; }
+ .right { float: right; }
+</style>
+<p>There should be no red.</p>
+<fieldset><legend class=left></legend></fieldset>
+<fieldset><legend class=right></legend></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html
new file mode 100644
index 0000000000..edd2600d4a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>legend and flexbox, grid & multicol</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+legend { width: 200px; background: silver }
+#flex { display: flex; }
+#inline-flex { display: inline-flex; }
+#grid { display: grid; }
+#inline-grid { display: inline-grid; }
+#grid, #inline-grid { grid-template-columns: auto auto }
+#multicol { columns: 2; }
+</style>
+<fieldset><legend id=ref>12</legend></fieldset>
+<fieldset><legend id=flex><div>1</div><div>2</div></legend></fieldset>
+<fieldset><legend id=inline-flex><div>1</div><div>2</div></legend></fieldset>
+<fieldset><legend id=grid><div>1</div><div>2</div></legend></fieldset>
+<fieldset><legend id=inline-grid><div>1</div><div>2</div></legend></fieldset>
+<fieldset><legend id=multicol><div>1</div><div>2</div></legend></fieldset>
+<script>
+ const ref = document.getElementById('ref');
+ for (const id of ["flex", "inline-flex", "grid", "inline-grid", "multicol"]) {
+ test(() => {
+ const elm = document.getElementById(id);
+ assert_equals(elm.offsetHeight, ref.offsetHeight, 'offsetHeight');
+ if (id !== "multicol") {
+ assert_equals(getComputedStyle(elm).display, id, 'display');
+ }
+ }, id);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-hover.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-hover.html
new file mode 100644
index 0000000000..170e3cc874
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-hover.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+legend:hover {
+ background: lime;
+}
+</style>
+<h1>Header</h1>
+<fieldset>
+ <legend>Legend</legend>
+</fieldset>
+<script>
+// https://crbug.com/1127743
+promise_test(async () => {
+ await test_driver.click(document.querySelector('legend'));
+ assert_not_equals(document.querySelector('legend:hover'), null);
+}, 'The rendered LEGEND should work well for :hover.');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot-ref.html
new file mode 100644
index 0000000000..ee64e81762
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<fieldset>
+ <legend>Is rendered legend</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot.html
new file mode 100644
index 0000000000..c4ab5a3ea0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-in-slot.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="match" href="legend-in-slot-ref.html">
+
+<div id="host">
+ <legend id="legend">Was rendered legend</legend>
+ <legend>Is rendered legend</legend>
+</div>
+<script>
+let root = document.querySelector('#host').attachShadow({mode:"open"});
+root.innerHTML = `
+<fieldset>
+ <slot></slot>
+</fieldset>`;
+document.body.offsetTop;
+document.querySelector('#legend').remove();
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-inline-position-with-fieldset-padding.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-inline-position-with-fieldset-padding.html
new file mode 100644
index 0000000000..0b2624859e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-inline-position-with-fieldset-padding.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-fieldset-and-legend-elements">
+<!-- A test for the following paragraphs:
+The element's box is expected to be constrained in the inline direction by
+the inline content size of the fieldset as if it had used its computed
+inline padding.
+Example:
+For example, if the fieldset has a specified padding of 50px, then the
+rendered legend will be positioned 50px in from the fieldset's border. The
+padding will further apply to the anonymous fieldset content box instead
+of the fieldset element itself.
+-->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+fieldset {
+ width: 400px;
+ margin: 0;
+ padding: 0 50px;
+ border: 2px solid black;
+}
+legend {
+ width: 100%;
+ padding: 0;
+ background-color: yellow;
+}
+.content {
+ background-color: lime;
+}
+</style>
+<fieldset>
+<legend>Legend</legend>
+</fieldset>
+
+<script>
+test(() => {
+ let fieldset = document.querySelector('fieldset');
+ let legend = document.querySelector('legend');
+ assert_equals(legend.offsetLeft - fieldset.offsetLeft, 52);
+ assert_equals(legend.offsetWidth, 400);
+}, 'Test legend\'s inline-size in a fieldset with inline paddings');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering-ref.html
new file mode 100644
index 0000000000..f7b4754e09
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference for legend and display: list-item numbering</title>
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+
+<style>
+ol { margin: 0; padding: 0; border: none; }
+ol > * { margin: 0 40px; padding: 0; }
+</style>
+
+<ol>
+ <li value="2">B</li>
+ <li value="1">A</li>
+ <li value="3">C</li>
+</ol>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering.html
new file mode 100644
index 0000000000..d7d904b8c7
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-numbering.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Legend and display: list-item numbering</title>
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel=match href="legend-list-item-numbering-ref.html">
+
+<style>
+fieldset { margin: 0; padding: 0; border: none; list-style-type: decimal; }
+fieldset > * { margin: 0 40px; padding: 0; display: list-item; }
+</style>
+
+<fieldset>
+ <div>A</div>
+ <legend>B</legend>
+ <div>C</div>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-ref.html
new file mode 100644
index 0000000000..ee76e93b64
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Reference for legend and dipslay: list-item</title>
+<style>
+ div { margin: 0 40px; display: list-item; }
+</style>
+<p>There should be a bullet point below.</p>
+<div>X</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item.html
new file mode 100644
index 0000000000..e967643572
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-list-item.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>legend and dipslay: list-item</title>
+<link rel=match href=legend-list-item-ref.html>
+<style>
+ fieldset { margin: 0; padding: 0; border: none; }
+ legend { margin: 0 40px; padding: 0; display: list-item; }
+</style>
+<p>There should be a bullet point below.</p>
+<fieldset>
+ <legend>X</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-margin-inline.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-margin-inline.html
new file mode 100644
index 0000000000..8daf78db99
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-margin-inline.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<title>legend and margin (inline direction)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ fieldset { margin: 0 0 10px 0; padding: 20px; border: 10px solid; width: 500px; }
+ legend { height: 10px; width: 200px; padding: 0; }
+
+ #legend-center { margin-left: auto; margin-right: auto; }
+ #legend-right { margin-left: auto; }
+ #legend-10 { margin-left: 10px; }
+</style>
+<fieldset>
+ <legend id=legend-left>left</legend>
+</fieldset>
+<fieldset>
+ <legend id=legend-center>center</legend>
+</fieldset>
+<fieldset>
+ <legend id=legend-right>right</legend>
+</fieldset>
+<fieldset>
+ <legend id=legend-10>10px</legend>
+</fieldset>
+
+<script>
+ const legends = document.getElementsByTagName('legend');
+ const [legendLeft, legendCenter, legendRight, legend10] = legends;
+
+ const expectedLeft = 8 + 10 + 20;
+ const expectedCenter = expectedLeft + (500 / 2) - (200 / 2);
+ const expectedRight = expectedLeft + 500 - 200;
+ const expected10 = expectedLeft + 10;
+
+ test(() => {
+ assert_equals(legendLeft.offsetLeft, expectedLeft);
+ }, 'left');
+
+ test(() => {
+ assert_equals(legendCenter.offsetLeft, expectedCenter);
+ }, 'center');
+
+ test(() => {
+ assert_equals(legendRight.offsetLeft, expectedRight);
+ }, 'right');
+
+ test(() => {
+ assert_equals(legend10.offsetLeft, expected10);
+ }, '10px');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order-ref.html
new file mode 100644
index 0000000000..94f4991f22
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>Reference for fieldset painting order</title>
+
+<p>There should be a green square below, and no red.</p>
+<div style="width:100px; height:100px; background:green;"></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order.html
new file mode 100644
index 0000000000..ac0bd43037
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-painting-order.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>Legend painting order</title>
+<link rel=match href=legend-painting-order-ref.html>
+
+<p>There should be a green square below, and no red.</p>
+<div style="float:left; width:0px; height:0px;">
+ <div style="width:100px; height:100px; background:red;"></div>
+</div>
+<fieldset style="margin:0; border:none; padding:0;">
+ <legend style="padding:0;">
+ <div style="float:left; width:100px; height:100px; background:green;"></div>
+ </legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2-ref.html
new file mode 100644
index 0000000000..da2dd3e1c6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Reference for legend position: relative</title>
+<style>
+#fieldset2 {
+ background: lime;
+ border: 2px solid lime;
+ width: 200px;
+ padding: 0;
+ margin: 0;
+}
+#legend2 {
+ background: #00ffff;
+}
+</style>
+<p>"Legend" should be shown.</p>
+<fieldset id="fieldset2"><legend id="legend2">Legend</legend></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2.html
new file mode 100644
index 0000000000..3fbdbd5c20
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-2.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>legend position: relative</title>
+<link ref=help href="http://crbug.com/1151295">
+<link rel=match href=legend-position-relative-2-ref.html>
+<style>
+#fieldset2 {
+ background: lime;
+ border: 2px solid lime;
+ width: 200px;
+ padding: 0;
+ margin: 0;
+ overflow: hidden;
+}
+#legend2 {
+ position: relative;
+ overflow: hidden;
+ background: #00ffff;
+}
+</style>
+<p>"Legend" should be shown.</p>
+<fieldset id="fieldset2"><legend id="legend2">Legend</legend></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-ref.html
new file mode 100644
index 0000000000..fd6c11a005
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Reference for legend position: relative</title>
+<style>
+div { display: inline-block; background: lime; }
+.a { width: 100px; height: 200px; }
+.b { width: 100px; height: 100px; }
+.c { width: 200px; height: 200px; }
+</style>
+<p>There should be no red.</p>
+<div class=a></div><div class=b></div><div class=c></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative.html
new file mode 100644
index 0000000000..9938361261
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-position-relative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>legend position: relative</title>
+<link rel=match href=legend-position-relative-ref.html>
+<style>
+fieldset { border: 100px solid lime; width: 200px; padding: 0; margin: 0 }
+legend { position: relative; left: 100px; width: 100px; height: 100px; padding: 0 }
+.behind { position: absolute; left: 208px; width: 100px; height: 100px; background: red; z-index: -1 }
+</style>
+<p>There should be no red.</p>
+<div class=behind></div>
+<fieldset><legend></legend></fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sans-fieldset-display.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sans-fieldset-display.html
new file mode 100644
index 0000000000..5f27ca5c29
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sans-fieldset-display.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<title>legend sans fieldset and CSS display</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ * { margin: 0; padding: 0; }
+ .table, table { display: table; width: 200px; border-collapse: collapse; }
+ .tbody { display: table-row-group; }
+ .tr { display: table-row; }
+ .td, td { display: table-cell; }
+ .col { display: table-column; }
+ .caption { display: table-caption; }
+ .li { display: list-item; }
+ .inline { display: inline; }
+ .inline-block { display: inline-block; }
+ .inline-table { display: inline-table; }
+ .ruby { display: ruby; }
+ .rt { display: ruby-text; }
+ rt { font-size: inherit; }
+</style>
+<legend class=table>
+ <legend class=caption>caption</legend>
+ <legend class=col></legend><legend class=col></legend>
+ <legend class=tbody>
+ <legend class=tr>
+ <legend class=td>td</legend><legend class=td>td</legend>
+ </legend>
+ </legend>
+</legend>
+<table>
+ <caption>caption</caption>
+ <col><col>
+ <tbody>
+ <tr>
+ <td>td<td>td
+</table>
+<ul>
+ <legend class=li>li</legend>
+ <li>li</li>
+</ul>
+<p>foo <legend class=inline>inline</legend> <span>inline</span>
+<p>foo <legend class=inline-block>inline-block</legend> <span class=inline-block>inline-block</span>
+<p><legend class=ruby>ruby<legend class=rt>rt</legend></legend> <ruby>ruby<rt>rt</ruby>
+<script>
+ function test_display(testSelector, refSelector) {
+ test(() => {
+ const testElm = document.querySelector(testSelector);
+ const refElm = document.querySelector(refSelector);
+ const testStyle = getComputedStyle(testElm);
+ const refStyle = getComputedStyle(refElm);
+ assert_equals(testStyle.display, refStyle.display, testSelector + ' display');
+ assert_equals(testStyle.width, refStyle.width, testSelector + ' width');
+ assert_equals(testStyle.height, refStyle.height, testSelector + ' height');
+ }, testSelector);
+ }
+
+ test_display('.table', 'table');
+ test_display('.caption', 'caption');
+ test_display('.col', 'col');
+ test_display('.tbody', 'tbody');
+ test_display('.tr', 'tr');
+ test_display('.td', 'td');
+ test_display('.li', 'li');
+ test_display('.inline', 'span');
+ test_display('.inline-block', 'span.inline-block');
+ test_display('.ruby', 'ruby');
+ test_display('.rt', 'rt');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sticky-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sticky-crash.html
new file mode 100644
index 0000000000..71debf7e44
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sticky-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="help" href="http://crbug.com/1197946">
+<style>
+legend {
+ position: sticky;
+ left: -1em;
+}
+fieldset {
+ position: sticky;
+ left: -1em;
+ overflow-y: auto;
+}
+</style>
+<fieldset>
+<legend>Legend</legend>
+</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall-ref.html
new file mode 100644
index 0000000000..004ce42129
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Reference for tall legend</title>
+<style>
+body, p { margin: 0; }
+.legend { position: absolute; height: 160px; margin-left: 20px; inline-size: fit-content; background: lime }
+.fieldset {
+ position: absolute;
+ z-index: -1;
+ margin-top: calc((/* half legend height */ 160px / 2) - (/* half top border */ 20px / 2));
+ background: green;
+ height: 40px;
+ left: 0;
+ right: 0;
+}
+.hello { margin-top: 160px; margin-left: 20px; }
+</style>
+<p>There should be no red.</p>
+<div class=legend>X</div>
+<div class=fieldset></div>
+<div class=hello>HELLO</div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall.html
new file mode 100644
index 0000000000..7b9495946e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-tall.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>tall legend</title>
+<link rel=match href=legend-tall-ref.html>
+<style>
+body, p { margin: 0; }
+fieldset { height: 30px; margin: 0; border: 20px solid green; padding: 0px; background: red; }
+legend { height: 160px; background: lime; padding: 0; }
+#behind {
+ position: absolute;
+ z-index: -1;
+ margin-top: calc((/* half legend height */ 160px / 2) - (/* half top border */ 20px / 2));
+ background: red;
+ height: 40px;
+ left: 0;
+ right: 0;
+}
+</style>
+<p>There should be no red.</p>
+<div id=behind></div>
+<fieldset><legend>X</legend>HELLO</fieldset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend.html
new file mode 100644
index 0000000000..1cda91f32b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<title>The legend element</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#ref {
+ display: block;
+ unicode-bidi: isolate;
+ padding-left: 2px;
+ padding-right: 2px;
+ /* TODO: uncomment this when these properties are widely supported
+ padding-inline-start: 2px; padding-inline-end: 2px;
+ */
+}
+</style>
+
+<legend id=in-body></legend>
+<fieldset>
+ <legend id=rendered-legend></legend>
+ <legend id=in-fieldset-second-child></legend>
+ <div><legend id=in-fieldset-descendant></legend></div>
+</fieldset>
+<div id=ref></div>
+
+<script>
+setup(() => {
+ self.legends = [].slice.call(document.querySelectorAll('legend'));
+ self.refStyle = getComputedStyle(document.getElementById('ref'));
+ self.props = ['display',
+ 'unicodeBidi',
+ 'marginTop',
+ 'marginRight',
+ 'marginBottom',
+ 'marginLeft',
+ 'paddingTop',
+ 'paddingRight',
+ 'paddingBottom',
+ 'paddingLeft',
+ 'overflow',
+ // Extra tests
+ 'height',
+ 'box-sizing',
+ ];
+});
+legends.forEach(legend => {
+ const testStyle = getComputedStyle(legend);
+ props.forEach(prop => {
+ test(() => {
+ assert_equals(testStyle[prop], refStyle[prop]);
+ }, `${legend.id}: ${prop}`);
+ });
+
+ // Test width separately since it differs outside fieldset vs. in fieldset vs. rendered legend
+ test(() => {
+ if (legend.id === 'rendered-legend') {
+ assert_equals(testStyle.width, '0px');
+ } else {
+ assert_not_equals(testStyle.width, '0px');
+ }
+ }, `${legend.id}: width`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/min-inline-size.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/min-inline-size.html
new file mode 100644
index 0000000000..92c3302970
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/min-inline-size.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>fieldset min-inline-size</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ fieldset { width: 0; height: 0 }
+ fieldset > div { width: 100px; height: 100px }
+ #vertical-lr { writing-mode: vertical-lr }
+ #vertical-rl { writing-mode: vertical-rl }
+ .override { min-inline-size: 5px }
+</style>
+<fieldset id=horizontal-tb><div></div></fieldset>
+<fieldset id=vertical-lr><div></div></fieldset>
+<fieldset id=vertical-rl><div></div></fieldset>
+<script>
+ for (const className of ['', 'override']) {
+ const expected = className === '' ? '100px' : '5px';
+ test(() => {
+ const fieldset = document.getElementById('horizontal-tb');
+ fieldset.className = className;
+ assert_equals(getComputedStyle(fieldset).width, expected, 'width');
+ assert_equals(getComputedStyle(fieldset).height, '0px', 'height');
+ }, `horizontal-tb ${className}`);
+
+ test(() => {
+ const fieldset = document.getElementById('vertical-lr');
+ fieldset.className = className;
+ assert_equals(getComputedStyle(fieldset).width, '0px', 'width');
+ assert_equals(getComputedStyle(fieldset).height, expected, 'height');
+ }, `vertical-lr ${className}`);
+
+ test(() => {
+ const fieldset = document.getElementById('vertical-rl');
+ fieldset.className = className;
+ assert_equals(getComputedStyle(fieldset).width, '0px', 'width');
+ assert_equals(getComputedStyle(fieldset).height, expected, 'height');
+ }, `vertical-rl ${className}`);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/multicol-legend-becomes-floated-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/multicol-legend-becomes-floated-crash.html
new file mode 100644
index 0000000000..0e7f87232a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/multicol-legend-becomes-floated-crash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1122722">
+<fieldset id="fieldset">
+ <legend id="legend"></legend>
+</fieldset>
+<script>
+ document.body.offsetTop;
+ fieldset.style.columns = "2";
+ legend.style.cssFloat = "left";
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/no-red-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/no-red-ref.html
new file mode 100644
index 0000000000..32b7d46d6f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/no-red-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Reference there should be no red</title>
+<p>There should be no red.</p>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/resources/fieldset-vertical.css b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/resources/fieldset-vertical.css
new file mode 100644
index 0000000000..b358d925a7
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/resources/fieldset-vertical.css
@@ -0,0 +1,18 @@
+body > div { display: inline-block }
+fieldset, .fieldset { padding: 0; height:10em; width:2em; border:1em groove; margin: 0em; line-height:1 }
+legend, .legend { padding: 0; width: 1em }
+.legend {
+ background: white; /* overlap the border to emulate the border not being painted */
+ display: table; /* shrink-wrap */
+}
+
+[style="writing-mode: vertical-lr"] .legend {
+ margin-left: -1em;
+}
+[style="writing-mode: vertical-rl"] .legend {
+ margin-right: -1em;
+}
+.top { margin-bottom: auto }
+.center { margin-top: auto; margin-bottom: auto }
+.bottom { margin-top: auto }
+.rtl { direction: rtl }
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/second-legend-becomes-rendered-legend-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/second-legend-becomes-rendered-legend-crash.html
new file mode 100644
index 0000000000..6e36b3bb5a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/second-legend-becomes-rendered-legend-crash.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1119400">
+<fieldset>
+ <legend id="legend1"></legend>
+ <legend id="legend2"><div id="child" style="float:left;"></div></legend>
+</fieldset>
+<script>
+ requestAnimationFrame(()=> {
+ requestAnimationFrame(()=> {
+ legend1.style.display = "none";
+ document.body.offsetTop;
+
+ child.style.width = "22px";
+ document.body.offsetTop;
+
+ child.style.display = "none";
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-crash.html
new file mode 100644
index 0000000000..a6e8fa7a82
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-crash.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="help" href="http://crbug.com/1146872">
+<body>
+<fieldset><span><span></span></span></fieldset>
+<div id="host"><span></span></div>
+<script>
+const host = document.querySelector('#host');
+const shadowRoot = host.attachShadow({mode: 'closed'});
+const fieldset = shadowRoot.appendChild(document.createElement('fieldset'));
+fieldset.setAttribute('style', 'overflow: scroll');
+fieldset.innerHTML = '<slot></slot>';
+</script>
+<style>
+*:not(fieldset, div) {
+ position: sticky;
+ bottom: 72pc;
+}
+fieldset {
+ overflow: visible scroll;
+}
+</style>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-ref.html
new file mode 100644
index 0000000000..95e3c05cd8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<body>
+<style>
+body {
+ margin: 0;
+}
+
+.fieldset div {
+ height:1000px;
+}
+
+span {
+ background: lime;
+ display: block;
+ height: 40px;
+ position: absolute;
+ top: 4px;
+ left: 0px;
+ width: 40px;
+}
+
+.fieldset {
+ border: none;
+ height: 400px;
+ margin: 0;
+ overflow: scroll;
+ padding: 0;
+}
+</style>
+<div class="fieldset"><div><span></span></div></div>
+<script>
+document.querySelector('.fieldset').scrollTop = 1000;
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content.html
new file mode 100644
index 0000000000..f60d9ec528
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/sticky-content.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="help" href="http://crbug.com/1146925">
+<link rel="match" href="sticky-content-ref.html">
+<body>
+<style>
+body {
+ margin: 0;
+}
+
+fieldset div {
+ height:1000px;
+}
+
+span {
+ background: lime;
+ display: block;
+ height: 40px;
+ position: sticky;
+ top: 4px;
+ width: 40px;
+}
+
+fieldset {
+ border: none;
+ height: 400px;
+ margin: 0;
+ overflow: scroll;
+ padding: 0;
+}
+</style>
+<fieldset><div><span></span></div></fieldset>
+<script>
+document.querySelector('fieldset').scrollTop = 1000;
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/different-writing-modes.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/different-writing-modes.html
new file mode 100644
index 0000000000..c697d4dbbf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/different-writing-modes.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel="match" href="reference/different-writing-modes-ref.html">
+<style>
+frame {
+ writing-mode: vertical-rl;
+}
+</style>
+<frameset cols="50%,*" rows="50%,*">
+ <frame src="resources/green.html"></frame>
+ <frame src="resources/green.html"></frame>
+ <frame></frame>
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/exceed-then-not-exceed.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/exceed-then-not-exceed.html
new file mode 100644
index 0000000000..85a5b98ae5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/exceed-then-not-exceed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async () => {
+ await new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve, {once:true}));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ // #fs1, #container, and #child were laid out.
+
+ // Move #child.
+ // It makes #container dirty, and it exceeds from #fs1's 1x1 grid.
+ document.querySelector('#fs1').insertBefore(
+ document.querySelector('#child'), document.querySelector('#container'));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ // Removing #child makes #container visible again.
+ document.querySelector('#child').remove();
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+}, 'No crash when a dirty FRAMESET exceeds from the grid then fits in it again');
+</script>
+</head>
+<frameset id="fs1">
+<frameset id="container">
+<frameset id="child"></frameset>
+</frameset>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/frameset-visibility-hidden.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/frameset-visibility-hidden.html
new file mode 100644
index 0000000000..b6c454b03f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/frameset-visibility-hidden.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets">
+<link rel="match" href="reference/empty-ref.html">
+<frameset cols="100%" style="visibility: hidden">
+ <frame src="resources/red.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-abssize.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-abssize.html
new file mode 100644
index 0000000000..ba55da8686
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-abssize.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1116832">
+<link rel="match" href="reference/green-ref.html">
+<frameset cols="4294967227%,*" frameborder="0">
+ <frame src="resources/green.html">
+ <frame src="resources/red.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-percentage.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-percentage.html
new file mode 100644
index 0000000000..ba55da8686
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-percentage.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1116832">
+<link rel="match" href="reference/green-ref.html">
+<frameset cols="4294967227%,*" frameborder="0">
+ <frame src="resources/green.html">
+ <frame src="resources/red.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-relsize.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-relsize.html
new file mode 100644
index 0000000000..f056aa7e30
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-cols-relsize.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1116832">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1353277">
+<script>
+window.onload = () => {
+ test(() => {
+ const frameSet = document.querySelector('frameset');
+ const frames = document.querySelectorAll('frame');
+ assert_less_than(frames[0].offsetWidth, frameSet.offsetWidth);
+ assert_greater_than(frames[0].offsetWidth, frames[1].offsetWidth);
+ assert_greater_than_equal(frames[1].offsetWidth, 0);
+ }, 'A large relative value should not produce weird sizes.');
+};
+</script>
+<frameset cols="4294967227*,*" frameborder="0">
+ <frame src="resources/green.html">
+ <frame src="resources/red.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-abssize.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-abssize.html
new file mode 100644
index 0000000000..7cd86b9455
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-abssize.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1116832">
+<link rel="match" href="reference/green-ref.html">
+<frameset rows="4294967227%,*" frameborder="0">
+ <frame src="resources/green.html">
+ <frame src="resources/red.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-percentage.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-percentage.html
new file mode 100644
index 0000000000..7cd86b9455
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-percentage.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1116832">
+<link rel="match" href="reference/green-ref.html">
+<frameset rows="4294967227%,*" frameborder="0">
+ <frame src="resources/green.html">
+ <frame src="resources/red.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-relsize.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-relsize.html
new file mode 100644
index 0000000000..c33cd44adf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/large-rows-relsize.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1116832">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1353277">
+<script>
+window.onload = () => {
+ test(() => {
+ const frameSet = document.querySelector('frameset');
+ const frames = document.querySelectorAll('frame');
+ assert_less_than(frames[0].offsetHeight, frameSet.offsetHeight);
+ assert_greater_than(frames[0].offsetHeight, frames[1].offsetHeight);
+ assert_greater_than_equal(frames[1].offsetHeight, 0);
+ }, 'A large relative value should not produce weird sizes.');
+};
+</script>
+<frameset rows="4294967227*,*" frameborder="0">
+ <frame src="resources/green.html">
+ <frame src="resources/red.html">
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/multicol-table-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/multicol-table-crash.html
new file mode 100644
index 0000000000..adfb76e15a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/multicol-table-crash.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel="help" href="http://crbug.com/1383361">
+<body style="columns:2">
+<div id="parent" style="display:table-caption"></div>
+<script>
+const caption = document.querySelector('#parent');
+const frameset = caption.appendChild(document.createElement('frameset'));
+frameset.setAttribute('rows', '100%');
+frameset.setAttribute('cols', '100%');
+frameset.innerHTML = '<frame srcdoc=""></frame><frame srcdoc=""></frame>';
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/different-writing-modes-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/different-writing-modes-ref.html
new file mode 100644
index 0000000000..9d5e5977f0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/different-writing-modes-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<frameset cols="50%,*" rows="50%,*">
+ <frame src="../resources/green.html"></frame>
+ <frame src="../resources/green.html"></frame>
+ <frame></frame>
+</frameset>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/empty-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/empty-ref.html
new file mode 100644
index 0000000000..0e76edd65b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/empty-ref.html
@@ -0,0 +1 @@
+<!DOCTYPE html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/green-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/green-ref.html
new file mode 100644
index 0000000000..62208d72c9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/reference/green-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+ body { background: green; }
+</style>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/green.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/green.html
new file mode 100644
index 0000000000..62208d72c9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/green.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+ body { background: green; }
+</style>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/red.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/red.html
new file mode 100644
index 0000000000..b5e7f79617
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-frameset-and-frame-elements/resources/red.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+ body { background: red; }
+</style>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align-ref.html
new file mode 100644
index 0000000000..9e4283e208
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align-ref.html
@@ -0,0 +1,31 @@
+
+<!doctype html>
+<meta charset=utf-8>
+<style>
+.hr {
+ color: gray;
+ border-style: inset;
+ border-width: 1px;
+ margin: 0.5em auto;
+ width: 100px;
+}
+
+.left {
+ margin-left: 0;
+}
+
+.right {
+ margin-right: 0;
+}
+</style>
+<div class='hr'></div>
+<div class='hr left'></div>
+<div class='hr'></div>
+<div class='hr right'></div>
+<div class='hr'></div>
+
+<div class='hr'></div>
+<div class='hr left'></div>
+<div class='hr'></div>
+<div class='hr right'></div>
+<div class='hr'></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align.html
new file mode 100644
index 0000000000..1657f2458d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/align.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="match" href="align-ref.html">
+<style>
+hr {
+ width: 100px;
+}
+</style>
+
+<hr align=>
+<hr align=left>
+<hr align=center>
+<hr align=right>
+<hr align=foobar>
+
+<script>
+// Test the IDL attribute
+const values = ['', 'left', 'center', 'right', 'foobar'];
+values.forEach(value => {
+ const hr = document.createElement('hr');
+ hr.align = value;
+ document.body.appendChild(hr);
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color-ref.html
new file mode 100644
index 0000000000..5cd35c83ad
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color-ref.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset=utf-8>
+<style>
+.hr {
+ color: gray;
+ border-style: inset;
+ border-width: 1px;
+ margin: 0.5em auto;
+}
+
+.green {
+ color: green;
+}
+
+.no-inset {
+ border-style: solid;
+}
+</style>
+<div class='hr'></div>
+<div class='hr no-inset'></div>
+<div class='hr no-inset'></div>
+<div class='hr green no-inset'></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color.html
new file mode 100644
index 0000000000..750f77e5fb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/color.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel=match href="color-ref.html">
+<hr>
+<hr color="">
+<hr color=transparent>
+<hr color=green>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/hr.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/hr.html
new file mode 100644
index 0000000000..e852216494
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/hr.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<title>The hr element</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#ref {
+ display: block;
+ unicode-bidi: isolate;
+ color: gray;
+ border-style: inset;
+ border-width: 1px;
+ margin: 0.5em auto;
+ /* TODO: uncomment this when these properties are widely supported
+ margin-block-start: 0.5em;
+ margin-inline-end: auto;
+ margin-block-end: 0.5em;
+ margin-inline-start: auto;
+ */
+ overflow: hidden;
+}
+</style>
+
+<hr id=test>
+<div id=ref></div>
+
+<script>
+setup(() => {
+ self.testStyle = getComputedStyle(document.getElementById('test'));
+ self.refStyle = getComputedStyle(document.getElementById('ref'));
+});
+['display',
+ 'unicodeBidi',
+ 'color',
+ 'borderTopStyle',
+ 'borderRightStyle',
+ 'borderBottomStyle',
+ 'borderLeftStyle',
+ 'borderTopWidth',
+ 'borderRightWidth',
+ 'borderBottomWidth',
+ 'borderLeftWidth',
+ 'marginTop',
+ 'marginRight',
+ 'marginBottom',
+ 'marginLeft',
+ 'overflow',
+ // Extra tests
+ 'height',
+ 'box-sizing',
+].forEach(prop => {
+ test(() => {
+ assert_equals(testStyle[prop], refStyle[prop]);
+ }, prop);
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/setting-overflow-visible.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/setting-overflow-visible.html
new file mode 100644
index 0000000000..11e3d63e34
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/setting-overflow-visible.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<title>The hr element: setting 'overflow: visible'</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+/* Use 0 margin for hr instead of default 0.5em to make things simpler */
+hr {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+.wrapper {
+ height: 150px;
+ position: relative;
+}
+#test-visible {
+ overflow: visible;
+}
+.float, hr::before {
+ content: "";
+ float: left;
+ width: 50px;
+ height: 50px;
+ background-color: orange;
+}
+.clear {
+ clear: left;
+}
+</style>
+
+<div class=wrapper>
+ <div class=float></div>
+ <hr id=test-control>
+ <div class=float></div>
+</div>
+
+<div class=wrapper>
+ <div class=float></div>
+ <hr id=test-visible>
+ <div class=float></div>
+</div>
+
+<script>
+
+test(() => {
+ const hr = document.getElementById('test-control');
+ assert_equals(hr.offsetLeft, 50, 'hr.offsetLeft');
+ assert_equals(hr.offsetTop, 0, 'hr.offsetTop');
+ assert_equals(hr.clientHeight, 50, 'hr.clientHeight');
+ const divAfter = hr.nextElementSibling;
+ assert_equals(divAfter.offsetLeft, 0, 'divAfter.offsetLeft');
+ assert_equals(divAfter.offsetTop, 50 + 1 + 1 /* hr border */, 'divAfter.offsetTop');
+}, 'control');
+
+test(() => {
+ const hr = document.getElementById('test-visible');
+ assert_equals(hr.offsetLeft, 0, 'hr.offsetLeft');
+ assert_equals(hr.offsetTop, 0, 'hr.offsetTop');
+ assert_equals(hr.clientHeight, 0, 'hr.clientHeight');
+ const divAfter = hr.nextElementSibling;
+ assert_equals(divAfter.offsetLeft, 50 + 50, 'divAfter.offsetLeft');
+ assert_equals(divAfter.offsetTop, 1 + 1 /* hr border */, 'divAfter.offsetTop');
+}, 'overflow: visible');
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width-ref.html
new file mode 100644
index 0000000000..71e7651c1a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width-ref.html
@@ -0,0 +1,19 @@
+<style>
+.hr {
+ color: gray;
+ border-style: inset;
+ border-width: 1px;
+ margin: 0.5em auto;
+}
+</style>
+<div class=hr></div>
+<div class=hr style="width: 50%"></div>
+<div class=hr style="width: 100px"></div>
+<div class=hr style="width: 100px"></div>
+<div class=hr style="width: 100px"></div>
+<div class=hr style="width: 100.99px"></div>
+<div class=hr style="width: 0%"></div>
+<div class=hr style="width: 0%"></div>
+<div class=hr></div>
+<div class=hr></div>
+<div class=hr></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width.html
new file mode 100644
index 0000000000..a436d2ae25
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-hr-element-0/width.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<link rel="match" href="width-ref.html">
+<hr>
+<hr width='50%'>
+<hr width='100'>
+<hr width='100foo'>
+<hr width=' 100 '>
+<hr width='100.99'>
+<hr width='0'>
+<hr width='00'>
+<hr width='+0'>
+<hr width='+00'>
+<hr width='++0'>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1-ref.html
new file mode 100644
index 0000000000..3d9d45ec94
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<iframe src="data:text/html,<!doctype html><body style='margin-left: 100px; margin-right: 100px;'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1a.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1a.html
new file mode 100644
index 0000000000..ba891d5021
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1a.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginwidth attribute has the right effect in standards mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe src="data:text/html,<!doctype html><body marginwidth='100'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1b.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1b.html
new file mode 100644
index 0000000000..431f74be29
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1b.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginwidth attribute has the right effect in quirks mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe src="data:text/html,<body marginwidth='100'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1c.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1c.html
new file mode 100644
index 0000000000..84eb4cfe82
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1c.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's leftmargin/rightmargin attributes have the right effect in standards mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe src="data:text/html,<!doctype html><body leftmargin='100' rightmargin='100'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1d.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1d.html
new file mode 100644
index 0000000000..938d4c2c0b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1d.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's leftmargin/rightmargin attributes have the right effect in quirks mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe src="data:text/html,<body leftmargin='100' rightmargin='100'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1e.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1e.html
new file mode 100644
index 0000000000..8cc463fb15
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1e.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that iframe's marginwidth attribute has the right effect in standards mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="100" src="data:text/html,<!doctype html><body>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1f.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1f.html
new file mode 100644
index 0000000000..2f123b392c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1f.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that iframe's marginwidth attribute has the right effect in quirks mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="100" src="data:text/html,<body>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1g.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1g.html
new file mode 100644
index 0000000000..052455390c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1g.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's style margin takes precedence over everything else in standards mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="20" src="data:text/html,<!doctype html><body marginwidth='30' leftmargin='40' rightmargin='50' style='margin-left: 100px; margin-right: 100px'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1h.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1h.html
new file mode 100644
index 0000000000..6778945494
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1h.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's style margin takes precedence over everything else in quirks mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="20" src="data:text/html,<body marginwidth='30' leftmargin='40' rightmargin='50' style='margin-left: 100px; margin-right: 100px'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1i.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1i.html
new file mode 100644
index 0000000000..95e21e3626
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1i.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginwidth attribute takes precedence over other body margin presentational attributes in standards mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="20" src="data:text/html,<!doctype html><body marginwidth='100' leftmargin='40' rightmargin='50'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1j.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1j.html
new file mode 100644
index 0000000000..4f945853d8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1j.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginwidth attribute takes precedence over other body margin presentational attributes in quirks mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="20" src="data:text/html,<body marginwidth='100' leftmargin='40' rightmargin='50'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1k.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1k.html
new file mode 100644
index 0000000000..c05cd24e2d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1k.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's leftmargin/rightmargin attributes take precedence over iframe marginwidth in standards mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="20" src="data:text/html,<!doctype html><body leftmargin='100' rightmargin='100'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1l.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1l.html
new file mode 100644
index 0000000000..f53d2c5a1d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-1l.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's leftmargin/rightmargin attributes take precedence over iframe marginwidth in quirks mode</title>
+<link rel=match href="body-margin-1-ref.html">
+<iframe marginwidth="20" src="data:text/html,<body leftmargin='100' rightmargin='100'>100px left/right margins, default top/bottom margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2-ref.html
new file mode 100644
index 0000000000..1bda1a8928
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<iframe src="data:text/html,<!doctype html><body style='margin-top: 100px; margin-bottom: 100px;'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2a.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2a.html
new file mode 100644
index 0000000000..9091ab2c78
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2a.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginheight attribute has the right effect in standards mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe src="data:text/html,<!doctype html><body marginheight='100'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2b.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2b.html
new file mode 100644
index 0000000000..dd7d74b635
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2b.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginheight attribute has the right effect in quirks mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe src="data:text/html,<body marginheight='100'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2c.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2c.html
new file mode 100644
index 0000000000..f72e4b6c71
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2c.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's topmargin/bottommargin attributes have the right effect in standards mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe src="data:text/html,<!doctype html><body topmargin='100' bottommargin='100'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2d.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2d.html
new file mode 100644
index 0000000000..40799b2277
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2d.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's topmargin/bottommargin attributes have the right effect in quirks mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe src="data:text/html,<body topmargin='100' bottommargin='100'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2e.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2e.html
new file mode 100644
index 0000000000..bab012bcf0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2e.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that iframe's marginheight attribute has the right effect in standards mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="100" src="data:text/html,<!doctype html><body>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2f.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2f.html
new file mode 100644
index 0000000000..103cde96d9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2f.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that iframe's marginheight attribute has the right effect in quirks mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="100" src="data:text/html,<body>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2g.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2g.html
new file mode 100644
index 0000000000..b87acacefe
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2g.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's style margin takes precedence over everything else in standards mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="20" src="data:text/html,<!doctype html><body marginheight='30' topmargin='40' bottommargin='50' style='margin-top: 100px; margin-bottom: 100px'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2h.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2h.html
new file mode 100644
index 0000000000..53f5267f71
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2h.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's style margin takes precedence over everything else in quirks mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="20" src="data:text/html,<body marginheight='30' topmargin='40' bottommargin='50' style='margin-top: 100px; margin-bottom: 100px'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2i.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2i.html
new file mode 100644
index 0000000000..642972ea33
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2i.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginheight attribute takes precedence over other body margin presentational attributes in standards mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="20" src="data:text/html,<!doctype html><body marginheight='100' topmargin='40' bottommargin='50'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2j.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2j.html
new file mode 100644
index 0000000000..f1b1689f1f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2j.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's marginheight attribute takes precedence over other body margin presentational attributes in quirks mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="20" src="data:text/html,<body marginheight='100' topmargin='40' bottommargin='50'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2k.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2k.html
new file mode 100644
index 0000000000..d11febeb5d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2k.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's topmargin/bottommargin attributes take precedence over iframe marginheight in standards mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="20" src="data:text/html,<!doctype html><body topmargin='100' bottommargin='100'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2l.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2l.html
new file mode 100644
index 0000000000..2f947b984f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body-margin-2l.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that body's topmargin/bottommargin attributes take precedence over iframe marginheight in quirks mode</title>
+<link rel=match href="body-margin-2-ref.html">
+<iframe marginheight="20" src="data:text/html,<body topmargin='100' bottommargin='100'>100px top/bottom margins, default left/right margins</body>"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_link.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_link.xhtml
new file mode 100644
index 0000000000..b93435de8a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_link.xhtml
@@ -0,0 +1,16 @@
+<html xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+<title>body - LINK=yellow</title>
+</head>
+<body link="yellow">
+<p> Test for <b> link="yellow" </b> on body </p>
+
+This <a href="test-body.xhtml">LINK</a> should be displayed in <b>yellow</b><i> if it has not been clicked before </i><br/>
+<p>Once clicked, the link will take default color of visited link.<br /></p>
+<p>To run this test again in browsers, delete your browsing history and navigate to this page.<br /></p>
+
+<p>
+<i>Note - This test checks for User Agent requirement as per HTML5 spec NOT the author requirement</i>
+</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff-ref.html
new file mode 100644
index 0000000000..c907a119f8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[body - TEXT=00ffff] Reference file</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<style>
+ body {
+ color: blue;
+ }
+</style>
+<body>
+ <p>This document should have text color 'Blue' using the RGB Hexadecimal color value of "0000ff". </p>
+ <p>This test passes if the color of text above matches the image below.</p>
+ <p><img src="/images/blue.png"/></p>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff.xhtml
new file mode 100644
index 0000000000..42b8aa3b6b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/body_text_00ffff.xhtml
@@ -0,0 +1,12 @@
+<html xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+<title>body - TEXT=00ffff</title>
+<link rel="match" href="body_text_00ffff-ref.html"/>
+<meta name="assert" content="Test checks that User Agent requirement as per HTML5 spec NOT the author requirement."/>
+</head>
+<body text="0000ff">
+<p>This document should have text color 'Blue' using the RGB Hexadecimal color value of "0000ff". </p>
+<p>This test passes if the color of text above matches the image below.</p>
+<p><img src="/images/blue.png" /></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/crashtests/body-huge-attr-value-crash.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/crashtests/body-huge-attr-value-crash.html
new file mode 100644
index 0000000000..b7aea03bf3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/crashtests/body-huge-attr-value-crash.html
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xlink="http://www.w3.org/1999/xlink">
+<meta charset="utf-8">
+<link rel="author" href="mailto:0xdevssh@gmail.com">
+<link rel="help" href="https://crbug.com/1238543">
+<meta name="assert" content="The renderer should not crash.">
+ <body topmargin="62099815446794541154677680790275022478020296315411546776807902750224780254115467768079027502247802349339973475906291022478020296313493399734759062916145353182409398397817693775043304346030250203150386848140862367251147544337900224780202963134933997347590629161453531824093983978176937750433043460302502031503868481408623672511475443379061453531824093983978176937750433043460302502031503868481408623672511475443379012223934551240400478945189095326288308770771506289350685029953926171692408537507190372257890049958209064274944598761777623561353561543911257846386780780067311874929239209275379981860757">
+
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-body-margin-attributes.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-body-margin-attributes.html
new file mode 100644
index 0000000000..e1f4fb5154
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-body-margin-attributes.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>iframe and body margin attributes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body marginwidth=20 marginheight=20 topmargin=10 rightmargin=10 bottommargin=10 leftmargin=10>
+<iframe data-desc="iframe marginwidth vs child body leftmargin" src="support/body-topmargin-leftmargin.html" marginwidth=10 marginheight=10></iframe>
+<iframe data-desc="iframe marginwidth vs child body marginwidth" src="support/body-marginwidth-marginheight.html" marginwidth=10 marginheight=10></iframe>
+<script>
+setup({explicit_done: true});
+
+onload = () => {
+ test(() => {
+ const style = getComputedStyle(document.body);
+ assert_style_props(style);
+ }, 'body marginwidth vs body leftmargin');
+
+ [].forEach.call(document.querySelectorAll('iframe'), iframe => {
+ test(() => {
+ const win = iframe.contentWindow;
+ const style = win.getComputedStyle(win.document.body);
+ assert_style_props(style);
+ }, iframe.dataset.desc);
+ });
+ done();
+}
+
+function assert_style_props(style) {
+ for (let prop of ['marginTop', 'marginRight', 'marginBottom', 'marginLeft']) {
+ assert_equals(style[prop], '20px', prop);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-marginwidth-marginheight.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-marginwidth-marginheight.html
new file mode 100644
index 0000000000..b09cafbbe4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-marginwidth-marginheight.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>iframe marginwidth and marginheight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe src="/common/blank.html" marginwidth=0 marginheight=0></iframe>
+<script>
+setup({ single_test: true });
+onload = () => {
+ assert_equals(window[0].document.body.attributes.length, 0, "Number of attributes on the child document's body");
+ done();
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html
new file mode 100644
index 0000000000..424e039e3f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>iframe: changing the scrolling attribute</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+
+
+<style>
+ iframe {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<p>These two iframes should *both* render with scrollbars:</p>
+<iframe src="support/big-page.html" scrolling="unknown"></iframe>
+<iframe src="support/big-page.html" scrolling="unknown"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html
new file mode 100644
index 0000000000..2ead59af39
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>The scrolling attribute</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+
+
+<style>
+ iframe {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<p>This page tests the behavior of the <tt>scrolling</tt> attribute on
+<tt>&lt;iframe&gt;</tt> elements which contain a page large enough to need to
+be scrolled.</p>
+
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+<iframe src="support/big-page.html" scrolling="no"></iframe>
+<iframe src="support/big-page.html" scrolling="no"></iframe>
+<iframe src="support/big-page.html" scrolling="no"></iframe>
+<iframe src="support/big-page.html" scrolling="no"></iframe>
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html
new file mode 100644
index 0000000000..997745ba48
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>The scrolling attribute</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-page">
+<link rel="match" href="iframe-scrolling-attribute-values-ref.html">
+<meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-4">
+
+<style>
+ iframe {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<p>This page tests the behavior of the <tt>scrolling</tt> attribute on
+<tt>&lt;iframe&gt;</tt> elements which contain a page large enough to need to
+be scrolled.</p>
+
+<iframe src="support/big-page.html"></iframe>
+<iframe src="support/big-page.html" scrolling></iframe>
+<iframe src="support/big-page.html" scrolling=""></iframe>
+<iframe src="support/big-page.html" scrolling="auto"></iframe>
+<iframe src="support/big-page.html" scrolling="yes"></iframe>
+<iframe src="support/big-page.html" scrolling="no"></iframe>
+<iframe src="support/big-page.html" scrolling="noscroll"></iframe>
+<iframe src="support/big-page.html" scrolling="off"></iframe>
+<iframe src="support/big-page.html" scrolling="NoScRoLl"></iframe>
+<iframe src="support/big-page.html" scrolling="bogus"></iframe>
+<iframe src="support/big-page.html" scrolling="1234"></iframe>
+
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html
new file mode 100644
index 0000000000..d04e2dd56f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>iframe: changing the scrolling attribute</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-page">
+<link rel="match" href="iframe-scrolling-attribute-ref.html">
+
+<style>
+ iframe {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<p>These two iframes should *both* render with scrollbars:</p>
+<iframe src="support/big-page.html" scrolling="unknown"></iframe>
+<iframe src="support/big-page.html" scrolling="unknown"></iframe>
+
+<script>
+ var iframe = document.getElementsByTagName("iframe")[1];
+ // Setting scrolling=no and then back to scrolling=unknown
+ // should result in a final value of auto.
+ iframe.setAttribute("scrolling", "no");
+ iframe.setAttribute("scrolling", "unknown");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/big-page.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/big-page.html
new file mode 100644
index 0000000000..65f2a65bcf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/big-page.html
@@ -0,0 +1,2 @@
+<p>Scroll me</p>
+<div style="width: 400px; height: 400px; background-color: blue"></div>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-marginwidth-marginheight.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-marginwidth-marginheight.html
new file mode 100644
index 0000000000..5d825e3455
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-marginwidth-marginheight.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<body marginwidth=20 marginheight=20>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-topmargin-leftmargin.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-topmargin-leftmargin.html
new file mode 100644
index 0000000000..7ba5e53330
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/support/body-topmargin-leftmargin.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<body topmargin=20 rightmargin=20 bottommargin=20 leftmargin=20>
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/test-body.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/test-body.xhtml
new file mode 100644
index 0000000000..cd733b17ba
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-page/test-body.xhtml
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title> This is a test page</title>
+</head>
+<body>
+<p> To rerun this test, delete history and go <a href="body_link.xhtml">back </a> to previous test.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/rendering/pixel-length-attributes.html b/testing/web-platform/tests/html/rendering/pixel-length-attributes.html
new file mode 100644
index 0000000000..888e6d0a3f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/pixel-length-attributes.html
@@ -0,0 +1,173 @@
+<!doctype html>
+<meta charset=utf-8>
+<!-- Creating iframes is slow in browsers -->
+<meta name=timeout content=long>
+<title>Test handling of attributes that map to pixel length properties</title>
+<link rel="help"
+ href="https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-pixel-length-property">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+ <div id="container" style="display: none">
+ <img id="defaultImg">
+ <object id="defaultObject"></object>
+ <input type="image" id="defaultInput"></input>
+ </div>
+<script>
+ /*
+ * This test tests
+ * https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-pixel-length-property
+ * for various elements and various values.
+ */
+
+ /*
+ * Array of input/output pairs. The input is the string to use as the
+ * attribute value. The output is the string expected as the computed style
+ * for the relevant CSS property.
+ */
+const valid_values = [
+ [ "200", "200px" ],
+ [ "1007", "1007px" ],
+ [ " 00523 ", "523px" ],
+ [ "200.", "200px" ],
+ [ "200.25", "200px" ],
+ [ "200.7", "200px" ],
+ [ "0", "0px" ],
+ [ "-0", "0px" ],
+ [ "+0", "0px" ],
+ [ "+200", "200px" ],
+ [ "200in", "200px" ],
+ [ "200.25in", "200px" ],
+ [ " +200in ", "200px" ],
+ [ "200%", "200px" ],
+ [ "200.%", "200px" ],
+ [ "200.25%", "200px" ],
+];
+
+ /*
+ * Array of invalid values. These should lead to the default value of the
+ * relevant CSS property.
+ */
+const invalid_values = [
+ "-200",
+ "-200px",
+ " -200",
+ "+-200",
+ "-+200",
+];
+
+/*
+ * Array of tests. Each test consists of the following information:
+ *
+ * 1) A function to call to create the element to test. This returns a
+ * 3-element array: The element to set the attribute on, the element to
+ * compute style on, and a cleanup function to call when done.
+ * 2) The name of the attribute to set.
+ * 3) The name of the computed style property to get.
+ * 4) An element that can be used to determine the fallback style value for
+ * invalid values.
+ */
+const tests = [
+ [ createIframe, "marginwidth", "marginLeft", document.body ],
+ [ createIframe, "marginwidth", "marginRight", document.body ],
+ [ createIframe, "marginheight", "marginTop", document.body ],
+ [ createIframe, "marginheight", "marginBottom", document.body ],
+ [ createFrame, "marginwidth", "marginLeft", document.body ],
+ [ createFrame, "marginwidth", "marginRight", document.body ],
+ [ createFrame, "marginheight", "marginTop", document.body ],
+ [ createFrame, "marginheight", "marginBottom", document.body ],
+ [ createBody, "marginwidth", "marginLeft", document.body ],
+ [ createBody, "marginwidth", "marginRight", document.body ],
+ [ createBody, "leftmargin", "marginLeft", document.body ],
+ [ createBody, "rightmargin", "marginRight", document.body ],
+ [ createBody, "marginheight", "marginTop", document.body ],
+ [ createBody, "marginheight", "marginBottom", document.body ],
+ [ createBody, "topmargin", "marginTop", document.body ],
+ [ createBody, "bottommargin", "marginBottom", document.body ],
+ [ newElem("img"), "border", "borderTopWidth", defaultImg ],
+ [ newElem("img"), "border", "borderRightWidth", defaultImg ],
+ [ newElem("img"), "border", "borderBottomWidth", defaultImg ],
+ [ newElem("img"), "border", "borderLeftWidth", defaultImg ],
+ [ newElem("object"), "border", "borderTopWidth", defaultObject ],
+ [ newElem("object"), "border", "borderRightWidth", defaultObject ],
+ [ newElem("object"), "border", "borderBottomWidth", defaultObject ],
+ [ newElem("object"), "border", "borderLeftWidth", defaultObject ],
+ [ newImageInput, "border", "borderTopWidth", defaultInput ],
+ [ newImageInput, "border", "borderRightWidth", defaultInput ],
+ [ newImageInput, "border", "borderBottomWidth", defaultInput ],
+ [ newImageInput, "border", "borderLeftWidth", defaultInput ],
+];
+
+function newElem(name) {
+ return () => {
+ var elem = document.createElement(name);
+ document.getElementById("container").appendChild(elem);
+ return [ elem, elem, () => elem.remove() ];
+ }
+}
+
+function newImageInput() {
+ var elem = document.createElement("input");
+ elem.type = "image";
+ document.getElementById("container").appendChild(elem);
+ return [ elem, elem, () => elem.remove() ];
+}
+
+function createIframe() {
+ let ifr = document.createElement("iframe");
+ document.body.appendChild(ifr);
+ return [ ifr, ifr.contentDocument.body, () => ifr.remove() ];
+ }
+
+function createBody() {
+ let ifr = document.createElement("iframe");
+ document.body.appendChild(ifr);
+ return [ ifr.contentDocument.body, ifr.contentDocument.body, () => ifr.remove() ];
+}
+
+function createFrame() {
+ // We need to create a separate iframe to put our frameset into.
+ let ifr = document.createElement("iframe");
+ document.body.appendChild(ifr);
+ let doc = ifr.contentDocument;
+ let root = doc.documentElement;
+ while (root.firstChild) {
+ root.firstChild.remove();
+ }
+ let frameset = doc.createElement("frameset");
+ frameset.setAttribute("rows", "*");
+ frameset.setAttribute("cols", "*");
+ let frame = doc.createElement("frame");
+ frameset.appendChild(frame);
+ root.appendChild(frameset);
+ return [ frame, frame.contentDocument.body, () => ifr.remove() ];
+}
+
+function style(element) {
+ return element.ownerDocument.defaultView.getComputedStyle(element);
+}
+
+for (let [setup, attr, prop, default_elem] of tests) {
+ for (let [value, result] of valid_values) {
+ let [ attr_elem, prop_elem, cleanup ] = setup();
+ test(function() {
+ this.add_cleanup(cleanup);
+ attr_elem.setAttribute(attr, value);
+ assert_equals(attr_elem.getAttribute(attr), value);
+ assert_equals(style(prop_elem)[prop], result);
+ }, `<${attr_elem.localName} ${attr}="${value}"> mapping to ${prop}`);
+ }
+
+ let defaultVal = style(default_elem)[prop];
+ for (let value of invalid_values) {
+ let [ attr_elem, prop_elem, cleanup ] = setup();
+ test(function() {
+ this.add_cleanup(cleanup);
+ attr_elem.setAttribute(attr, value);
+ assert_equals(attr_elem.getAttribute(attr), value);
+ assert_equals(style(prop_elem)[prop], defaultVal);
+ }, `<${attr_elem.localName} ${attr}="${value}"> mapping to ${prop}`);
+ }
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/align.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/align.html
new file mode 100644
index 0000000000..b5f3ec1aa6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/align.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>align attribute mapping on replaced elements</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<img id="replaced" src="/images/green.png">
+<something id="non-replaced"></something>
+<script>
+const kMapping = {
+ "left": ["float", "left"],
+ "right": ["float", "right"],
+
+ "top": ["vertical-align", "top"],
+
+ // This one requires a magic value (`-moz-middle-with-baseline` on Gecko,
+ // `-webkit-baseline-middle` on WebKit-based browsers).
+ "middle": ["vertical-align", undefined],
+ // These are inconsistent across browsers. WebKit maps it to "middle", Gecko
+ // to the aforementioned value.
+ "center": ["vertical-align", undefined],
+
+ "baseline": ["vertical-align", "baseline"],
+ "bottom": ["vertical-align", "baseline"], // *shrug*
+
+ "texttop": ["vertical-align", "text-top"],
+ "absmiddle": ["vertical-align", "middle"],
+ "abscenter": ["vertical-align", "middle"],
+ "absbottom": ["vertical-align", "bottom"],
+};
+
+const kInitialValues = {
+ "vertical-align": "baseline",
+ "float": "none",
+};
+
+let replaced = document.getElementById("replaced");
+let nonReplaced = document.getElementById("non-replaced");
+let t = async_test("align attribute mapping");
+onload = t.step_func_done(function() {
+ for (const attributeValue in kMapping) {
+ for (const element of [replaced, nonReplaced]) {
+ test(function() {
+ element.setAttribute("align", attributeValue);
+ let [property, expected] = kMapping[attributeValue];
+ let actual = getComputedStyle(element).getPropertyValue(property);
+ if (element == nonReplaced) {
+ assert_equals(actual, kInitialValues[property], "align shouldn't map to non-replaced elements")
+ } else {
+ if (expected) {
+ assert_equals(actual, expected, `align=${attributeValue} should map to ${property}: ${expected}`);
+ } else {
+ assert_equals(property, "vertical-align");
+ assert_not_equals(actual, "baseline", `align=${attributeValue} should map a vertical-align value`);
+ }
+ }
+ }, `align=${attributeValue} on ${element == replaced ? "replaced" : "non-replaced"} elements`);
+ }
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html
new file mode 100644
index 0000000000..dbe4c7d9f3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<title>Canvas width and height attributes are used as the surface size, and also to infer aspect ratio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/aspect-ratio.js"></script>
+<style>
+ canvas {
+ width: 100%;
+ max-width: 100px;
+ height: auto;
+ }
+</style>
+<body>
+<canvas id="contained" width="250" height="100" style="contain: size;"></canvas>
+<script>
+function assert_ratio(img, expected) {
+ let epsilon = 0.001;
+ assert_approx_equals(parseInt(getComputedStyle(img).width, 10) / parseInt(getComputedStyle(img).height, 10), expected, epsilon);
+}
+
+function test_computed_style(width, height, expected) {
+ test_computed_style_aspect_ratio("canvas", {width: width, height: height}, expected);
+}
+
+test(function() {
+ canvas = document.getElementById("contained");
+ assert_ratio(canvas, 2.5);
+}, "Canvas width and height attributes are used as the surface size with contain:size");
+
+// Create and append a new canvas and immediately check the ratio.
+test(function() {
+ var canvas = document.createElement("canvas");
+ canvas.setAttribute("width", "250");
+ canvas.setAttribute("height", "100");
+ document.body.appendChild(canvas);
+ // Canvases always use the aspect ratio from their surface size.
+ assert_ratio(canvas, 2.5);
+}, "Canvas width and height attributes are used as the surface size");
+
+test_computed_style("10", "20", "auto 10 / 20");
+test_computed_style("0", "1", "auto 0 / 1");
+test_computed_style("1", "0", "auto 1 / 0");
+test_computed_style("0", "0", "auto 0 / 0");
+test_computed_style("0.5", "1.5", "auto 0 / 1");
+test_computed_style("10%", "20", "auto 10 / 20");
+test_computed_style(null, null, "auto");
+test_computed_style("10", null, "auto");
+test_computed_style(null, "20", "auto");
+test_computed_style("xx", "20", "auto");
+test_computed_style("20", "xx", "auto");
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html
new file mode 100644
index 0000000000..42be8ce7a8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/content-aspect-ratio.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>div with content style's width and height attributes are not used to infer aspect-ratio</title>
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=201641#c22">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ video {
+ width: 100%;
+ max-width: 100px;
+ height: auto;
+ }
+</style>
+<body>
+<script>
+// Create and append a div with content style and immediately check the height.
+let t = test(function() {
+ var div = document.createElement("div");
+ div.setAttribute("style", "content: url('/images/blue.png')");
+ div.setAttribute("width", "250");
+ div.setAttribute("height", "100");
+ document.body.appendChild(div);
+ assert_equals(getComputedStyle(div).height, "0px");
+}, "div with content style's width and height attributes are not used to infer aspect-ratio");
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/embedded-and-images-presentational-hints-ascii-case-insensitive.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/embedded-and-images-presentational-hints-ascii-case-insensitive.html
new file mode 100644
index 0000000000..8fafb0c7f0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/embedded-and-images-presentational-hints-ascii-case-insensitive.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attributes-for-embedded-content-and-images:presentational-hints">
+<link rel="help" href="https://drafts.csswg.org/selectors-4/#attribute-case">
+<meta name="assert" content="@align values on embedded content and images are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<img src="fuchsia.png" align="absbottom">
+<img src="fuchsia.png" align="AbSbOtToM">
+<img src="fuchsia.png" align="abſbottom">
+<img src="fuchsia.png" align="abscenter">
+<img src="fuchsia.png" align="AbScEnTeR">
+<img src="fuchsia.png" align="abſcenter">
+<img src="fuchsia.png" align="absmiddle">
+<img src="fuchsia.png" align="AbSmIdDlE">
+<img src="fuchsia.png" align="abſmiddle">
+<script>
+const img = document.querySelectorAll("img");
+
+test(() => {
+ assert_equals(getComputedStyle(img[0]).getPropertyValue("vertical-align"),
+ "bottom", "lowercase valid");
+ assert_equals(getComputedStyle(img[1]).getPropertyValue("vertical-align"),
+ "bottom", "mixed case valid");
+ assert_equals(getComputedStyle(img[2]).getPropertyValue("vertical-align"),
+ "baseline", "non-ASCII invalid");
+}, "keyword absbottom");
+
+test(() => {
+ assert_equals(getComputedStyle(img[3]).getPropertyValue("vertical-align"),
+ "middle", "lowercase valid");
+ assert_equals(getComputedStyle(img[4]).getPropertyValue("vertical-align"),
+ "middle", "mixed case valid");
+ assert_equals(getComputedStyle(img[5]).getPropertyValue("vertical-align"),
+ "baseline", "non-ASCII invalid");
+}, "keyword abscenter");
+
+test(() => {
+ assert_equals(getComputedStyle(img[6]).getPropertyValue("vertical-align"),
+ "middle", "lowercase valid");
+ assert_equals(getComputedStyle(img[7]).getPropertyValue("vertical-align"),
+ "middle", "mixed case valid");
+ assert_equals(getComputedStyle(img[8]).getPropertyValue("vertical-align"),
+ "baseline", "non-ASCII invalid");
+}, "keyword absmiddle");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html
new file mode 100644
index 0000000000..b057967e7e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>Crash test: img alt rendering in combination with style attribute selector</title>
+<link rel="help" href="https://crbug.com/1057210">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ img { display: block; width: 100px; }
+ [style] + * {}
+</style>
+<img id="img" alt="alternative text">
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(img).width, "100px");
+ }, "Should not crash.");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.html
new file mode 100644
index 0000000000..1833efb804
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio-lazy.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>Image width and height attributes are used to infer aspect-ratio for lazy-loaded images</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ img {
+ width: 100%;
+ max-width: 100px;
+ height: auto;
+ }
+</style>
+<div style="height: 600vh"></div>
+<img src="/images/green.png" loading="lazy" width=100 height=100>
+<script>
+let t = async_test("Image width and height attributes are used to infer aspect-ratio for lazy-loaded images");
+
+function assert_ratio(img, expected) {
+ let epsilon = 0.001;
+ assert_approx_equals(parseFloat(getComputedStyle(img).width, 10) / parseFloat(getComputedStyle(img).height, 10), expected, epsilon);
+}
+
+t.step(function() {
+ let img = document.querySelector("img");
+ // The initial aspect ratio is given by the width/height attributes:
+ // https://html.spec.whatwg.org/#map-to-the-aspect-ratio-property-(using-dimension-rules)
+ assert_ratio(img, 1.0);
+ img.addEventListener("load", t.step_func_done(function() {
+ // Now the element "represents an image":
+ // https://html.spec.whatwg.org/multipage/rendering.html#images-3
+ // 2.0 is the original aspect ratio of green.png
+ assert_ratio(img, 2.0);
+ }));
+ window.scrollTo(0, img.getBoundingClientRect().top);
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html
new file mode 100644
index 0000000000..4dee3cf7ad
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html
@@ -0,0 +1,120 @@
+<!doctype html>
+<title>Image width and height attributes are used to infer aspect-ratio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/aspect-ratio.js"></script>
+<style>
+ img {
+ width: 100%;
+ max-width: 100px;
+ height: auto;
+ }
+</style>
+<img src="/images/green.png">
+<img src="/images/green.png" width=100 height=125>
+<img src="" width=100 height=125>
+<img src="error.png" width=100 height=125>
+<img src="error.png">
+<img src="error.png" alt="Alt text" width=100 height=500>
+<script>
+let guard = async_test("Image width and height attributes are used to infer aspect-ratio");
+let cookie = Math.random();
+function assert_ratio(img, expected, description) {
+ let epsilon = 0.001;
+ assert_approx_equals(parseFloat(getComputedStyle(img).width, 10) / parseFloat(getComputedStyle(img).height, 10),
+ expected, epsilon, description);
+}
+
+function test_computed_style(width, height, expected) {
+ test_computed_style_aspect_ratio("img", {width: width, height: height}, expected);
+ test_computed_style_aspect_ratio("input", {type: "image", width: width, height: height}, expected);
+ // input type=submit should not do this mapping.
+ test_computed_style_aspect_ratio("input", {type: "submit", width: width, height: height}, "auto");
+}
+
+// Create and append a new image and immediately check the ratio.
+// We append a random query to the URL(s) to avoid matching something in the 'list
+// of available images' (step 6 of the algorithm below) and thus have the actual
+// load run in a microtask.
+// https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
+test(function() {
+ // This img will be tested again after loaded. In order to locate it correctly, body should append it first.
+ var img = new Image();
+ img.width = 250;
+ img.height = 100;
+ img.src = "/images/blue.png?" + cookie;
+ document.body.appendChild(img);
+ assert_ratio(img, 2.5);
+}, "Create, append and test immediately: <img> with attributes width=250, height=100");
+
+test(function () {
+ img = new Image();
+ img.setAttribute("width", "0.8");
+ img.setAttribute("height", "0.2");
+ img.src = "/images/blue.png?" + (cookie + 1);
+ document.body.appendChild(img);
+ assert_ratio(img, 4);
+}, "Create, append and test immediately: <img> with attributes width=0.8, height=0.2");
+
+test(function () {
+ img = new Image();
+ img.setAttribute("width", "50%");
+ img.setAttribute("height", "25%");
+ img.src = "/images/blue.png?" + (cookie + 2);
+ document.body.appendChild(img);
+ // Percentages should be ignored.
+ assert_equals(getComputedStyle(img).height, "0px");
+}, "Create, append and test immediately: <img> with attributes width=50% height=25%");
+
+test(function () {
+ img = new Image();
+ img.setAttribute("width", "50pp");
+ img.setAttribute("height", "25xx");
+ img.src = "/images/blue.png?" + (cookie + 3);
+ document.body.appendChild(img);
+ assert_ratio(img, 2);
+}, "Create, append and test immediately: <img> with invalid trailing attributes width=50pp height=25xx");
+
+test_computed_style("10", "20", "auto 10 / 20");
+test_computed_style("0", "1", "auto 0 / 1");
+test_computed_style("1", "0", "auto 1 / 0");
+test_computed_style("0", "0", "auto 0 / 0");
+test_computed_style("0.5", "1.5", "auto 0.5 / 1.5");
+test_computed_style(null, null, "auto");
+test_computed_style("10", null, "auto");
+test_computed_style(null, "20", "auto");
+test_computed_style("xx", "20", "auto");
+test_computed_style("10%", "20", "auto");
+
+onload = function() {
+ let images = document.querySelectorAll("img");
+ // Tests for images finished loading.
+ test(function() {
+ assert_ratio(images[0], 2.0, "2.0 is the original aspect ratio of green.png");
+ }, "Loaded images test: <img> without width height attributes");
+
+ test(function() {
+ assert_ratio(images[1], 2.0, "Loaded image's aspect ratio, at least by default, overrides width / height ratio.");
+ }, "Loaded images test: <img> with width and height attributes, but conflicting to the original aspect ratio");
+
+ test(function () {
+ assert_ratio(images[2], 100 / 125, "aspect-ratio should override intrinsic size of images that don't have any src.");
+ }, "Loaded images test: <img> with width, height and empty src attributes");
+
+ test(function () {
+ assert_ratio(images[3], 100 / 125, "aspect-ratio should affect the size of error images.");
+ }, "Loaded images test: Error image with width and height attributes");
+
+ test(function () {
+ assert_not_equals(images[5].offsetHeight, 500, "Images with alt text should be inline and ignore the aspect ratio");
+ // Though aspect-ratio is ignored, its value does not change.
+ assert_equals(getComputedStyle(images[5]).aspectRatio, "auto 100 / 500");
+ }, "Loaded images test: Error image with width, height and alt attributes");
+
+ test(function () {
+ assert_ratio(images[6], 133 / 106, "The original aspect ratio of blue.png");
+ }, "Loaded images test: <img> with width and height attributes, but not equal to the original aspect ratio");
+
+ guard.done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim-ref.html
new file mode 100644
index 0000000000..b1adb68307
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>img width/height - reference</title>
+<style>
+p { width: 50px; height: 50px; }
+</style>
+<p><img src=/images/green.png>
+<p><img src=/images/green.png style="width: 10px">
+<p><img src=/images/green.png style="height: 10px">
+<p><img src=/images/green.png style="width: 10%">
+<p><img src=/images/green.png style="height: 10%">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim.html
new file mode 100644
index 0000000000..2d636c9417
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-dim.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>img width/height</title>
+<link rel=match href=img-dim-ref.html>
+<style>
+p { width: 50px; height: 50px; }
+</style>
+<p><img src=/images/green.png>
+<p><img src=/images/green.png width=10>
+<p><img src=/images/green.png height=10>
+<p><img src=/images/green.png width=10%>
+<p><img src=/images/green.png height=10%>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-empty-alt-replaced.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-empty-alt-replaced.html
new file mode 100644
index 0000000000..3cc06d6c85
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-empty-alt-replaced.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>Images with an empty alt attribute have an intrinsic size of zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ img {
+ width: 50px;
+ height: auto;
+ }
+</style>
+<img src="broken">
+<img src="broken" alt="non-empty">
+<img src="broken" alt="">
+<script>
+const t = async_test("Images with an empty alt attribute have an intrinsic size of zero");
+onload = t.step_func_done(function() {
+ for (const img of document.querySelectorAll("img")) {
+ const alt = img.getAttribute("alt");
+ const shouldTakeUpSpace = alt == null || alt.length > 0;
+ (shouldTakeUpSpace ? assert_not_equals : assert_equals)(img.offsetHeight, 0, img.outerHTML);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-no-alt-replaced.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-no-alt-replaced.html
new file mode 100644
index 0000000000..5f3503ae3b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-no-alt-replaced.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>Images without alt attribute or with an empty alt attribute render as replaced elements regardless of src</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="mailto:yuzhehan@chromium.org" title="Yu Han">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1196668">
+<link rel="help" href="https://crbug.com/753868">
+<link ref="help" href="https://html.spec.whatwg.org/multipage/rendering.html#images-3">
+<style>
+ img {
+ width: 100px;
+ height: 150px;
+ }
+</style>
+<img>
+<img src="broken">
+<img alt="">
+<img alt>
+<img src="broken" alt="">
+<script>
+const t = async_test("Images without alt attribute or with an empty alt attribute render as replaced elements regardless of src");
+onload = t.step_func_done(function() {
+ for (const img of document.querySelectorAll("img")) {
+ assert_equals(img.offsetWidth, 100, `width: ${img.outerHTML}`);
+ assert_equals(img.offsetHeight, 150, `height: ${img.outerHTML}`);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-replaced-box-while-loading.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-replaced-box-while-loading.html
new file mode 100644
index 0000000000..48bbd36db9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-replaced-box-while-loading.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>Images don't render as a non-replaced inline while loading, even when there's no concrete size specified</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1472637">
+<style>
+ img {
+ min-width: 1000px;
+ }
+</style>
+<img alt="T">
+<script>
+// Do an async test off the onload handler to avoid waiting for the load even for too long unnecessarily.
+let t = async_test("Loading images should get a replaced box, even without specified size");
+onload = t.step_func(function() {
+ const image = document.querySelector("img");
+ // Use the trickle pipe to have 100 seconds until the image actually loads,
+ // that should be enough to run the test.
+ image.src = "../../../../../images/blue.png?pipe=trickle(d100)";
+ t.step_timeout(t.step_func_done(function() {
+ assert_equals(
+ image.offsetWidth,
+ 1000,
+ );
+ }), 0);
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-title-only-w-sizing.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-title-only-w-sizing.html
new file mode 100644
index 0000000000..c290d9d6b1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-title-only-w-sizing.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Images with only title should be treated as a replaced element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="author" href="mailto:yuzhehan@chromium.org" title="Yu Han">
+<link rel="help" href="https://crbug.com/958250">
+<link ref="help" href="https://html.spec.whatwg.org/multipage/rendering.html#images-3">
+<style>
+ .title-only {
+ width: 100px;
+ height: 150px;
+ }
+</style>
+<img class="title-only" title="title">
+<img width="100" height="150px" title="title">
+<script>
+async_test(t => {
+ onload = t.step_func_done(function() {
+ for (const img of document.querySelectorAll("img")) {
+ assert_equals(img.offsetWidth, 100, `width: ${img.outerHTML}`);
+ assert_equals(img.offsetHeight, 150, `height: ${img.outerHTML}`);
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border-ref.xhtml b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border-ref.xhtml
new file mode 100644
index 0000000000..0050c542cd
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border-ref.xhtml
@@ -0,0 +1,9 @@
+<html xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+<title>IMG - Border in CSS</title>
+</head>
+<body>
+<p><img src="../../../../../images/blue.png"/></p>
+<p><img src="../../../../../images/blue.png" style="border-width:50px; border-style:solid;"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border_percent.xhtml b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border_percent.xhtml
new file mode 100644
index 0000000000..da74fb32b9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img_border_percent.xhtml
@@ -0,0 +1,10 @@
+<html xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+<title>IMG - Border= value in percent</title>
+<link rel="match" href="img_border-ref.xhtml"/>
+</head>
+<body>
+<p><img src="../../../../../images/blue.png" border="0%"/></p>
+<p><img src="../../../../../images/blue.png" border="50%"/></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-1.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-1.html
new file mode 100644
index 0000000000..6e32206d91
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-1.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<link rel="match" href="input-align-right-ref.html">
+<input type="image" align="right">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-2.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-2.html
new file mode 100644
index 0000000000..d58848aa53
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-2.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<link rel="match" href="input-align-right-ref.html">
+<input align="right" type="image">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-ref.html
new file mode 100644
index 0000000000..55f06ef96b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-align-right-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<input type="image" style="float: right">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-content-crash.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-content-crash.html
new file mode 100644
index 0000000000..84ef2ab153
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-content-crash.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>Crash test: asynchronously applying image content to image input</title>
+<link rel="author" href="mailto:yuzhehan@chromium.org" title="Yu Han">
+<link rel="help" href="https://crbug.com/1096002">
+<style>
+ .content { content: url(data:text/plain,aaa); }
+</style>
+<input id="input" type="image" class=content>
+<script>
+ onload = ()=> {
+ document.body.offsetTop;
+ input.setAttribute('class', '');
+ document.body.offsetTop;
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt-ref.html
new file mode 100644
index 0000000000..b3fdc14ef1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Input image type fallback content should respect display property.</title>
+<meta name="author" title="Yu Han" href="mailto:yuzhehan@chromium.org">
+<style>
+ div {
+ border:1px dashed blue;
+ line-height: 1em;
+ height: 100px;
+ width: 150px;
+ }
+ input {
+ font: 1em monospace;
+ line-height: 1em;
+ }
+</style>
+<div>
+ <input alt="This is a long ALT text which takes up few lines to display. And additional text to be inlined." type="image">
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt.html
new file mode 100644
index 0000000000..e05ad84191
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-image-inline-alt.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Input image type fallback content should respect display property.</title>
+<meta name="author" title="Yu Han" href="mailto:yuzhehan@chromium.org">
+<link rel="match" href="input-image-inline-alt-ref.html">
+<link ref="help" href="https://html.spec.whatwg.org/multipage/rendering.html#images-3:represents-5">
+<style>
+ div {
+ border:1px dashed blue;
+ font: 1em monospace;
+ line-height: 1em;
+ height: 100px;
+ width: 150px;
+ }
+ input {
+ display: inline;
+ font: 1em monospace;
+ line-height: 1em;
+ }
+</style>
+<div>
+ <input alt="This is a long ALT text which takes up few lines to display." type="image">
+ And additional text to be inlined.
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1-ref.html
new file mode 100644
index 0000000000..7768379e91
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<input align="right">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1.html
new file mode 100644
index 0000000000..00747a5fbf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/input-type-change-from-image-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel=match href="input-type-change-from-image-1-ref.html">
+<input type="image" align="right">
+<script>
+ onload = function() {
+ var i = document.querySelector("input");
+ window.rect = i.getBoundingClientRect();
+ i.type = "text";
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned-ref.html
new file mode 100644
index 0000000000..61dff1e460
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<style>
+input {
+ color: black;
+ text-align: right;
+}
+</style>
+<input type=number value=0>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned.html
new file mode 100644
index 0000000000..c1c695ffca
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/number-placeholder-right-aligned.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1698043">
+<link rel="match" href="number-placeholder-right-aligned-ref.html">
+<style>
+input {
+ color: black;
+ text-align: right;
+}
+input::placeholder {
+ color: inherit;
+ opacity: 1;
+}
+</style>
+<input type=number placeholder=0>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border-ref.xhtml b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border-ref.xhtml
new file mode 100644
index 0000000000..6eaaa0ba14
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border-ref.xhtml
@@ -0,0 +1,8 @@
+<html xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+<title>OBJECT - border in CSS</title>
+</head>
+<body>
+<p><object data="../../../../images/blue.png" type="image/png" style="border-width:50px; border-style:solid;"></object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_perc.xhtml b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_perc.xhtml
new file mode 100644
index 0000000000..3663e9ce61
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_perc.xhtml
@@ -0,0 +1,9 @@
+<html xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+<title>OBJECT - border=value in %</title>
+<link rel="match" href="object_border-ref.xhtml"/>
+</head>
+<body>
+<p><object data="../../../../images/blue.png" type="image/png" border="50%"></object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_pixel.xhtml b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_pixel.xhtml
new file mode 100644
index 0000000000..55f7f0a3bf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/object_border_pixel.xhtml
@@ -0,0 +1,9 @@
+<html xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+<title>OBJECT - border=pixel</title>
+<link rel="match" href="object_border-ref.xhtml"/>
+</head>
+<body>
+<p><object data="../../../../images/blue.png" type="image/png" border="50"></object></p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/picture-aspect-ratio.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/picture-aspect-ratio.html
new file mode 100644
index 0000000000..939d7895c3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/picture-aspect-ratio.html
@@ -0,0 +1,242 @@
+<!doctype html>
+<title>Image width and height attributes are used to infer aspect-ratio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ img:not([nowidth]) {
+ width: 100%;
+ max-width: 100px;
+ height: auto;
+ }
+</style>
+<picture>
+ <source srcset="/images/green-100x50.png"></source>
+ <img>
+</picture>
+
+<picture>
+ <source srcset="/images/green-100x50.png" width="100" height="100"></source>
+ <img>
+</picture>
+
+<picture>
+ <source srcset="/images/green-100x50.png" width="100" height="100"></source>
+ <img width="50" height="100">
+</picture>
+
+<picture>
+ <source srcset="/images/green-100x50.png" width="100" height="100" id="twosource-s1"></source>
+ <source srcset="/images/green-100x50.png" width="300" height="150"></source>
+ <img id="twosource-img">
+</picture>
+
+<div style="width: 100px;">
+ <picture>
+ <source srcset="/images/green-100x50.png" width="100%" height="50%" id="percent-src"></source>
+ <img style="contain:size;" id="percent-img" nowidth="true">
+ </picture>
+</div>
+
+<picture>
+ <source srcset="/images/green-100x50.png" width="100abc" height="50abc" id="trailing-src"></source>
+ <img style="contain:size;" id="trailing-img" nowidth="true">
+</picture>
+
+<script>
+let guard = async_test("source width and height attributes are used to infer aspect-ratio in <picture>");
+function assert_ratio(img, expected, description) {
+ let epsilon = 0.001;
+ assert_approx_equals(parseFloat(getComputedStyle(img).width, 10) / parseFloat(getComputedStyle(img).height, 10),
+ expected, epsilon, description);
+}
+
+function createPicture(width, height) {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ if (width !== undefined)
+ source.setAttribute("width", width);
+ if (height !== undefined)
+ source.setAttribute("height", height);
+ source.setAttribute("srcset", "/images/green.png");
+ picture.appendChild(source);
+ var img = document.createElement("img");
+ picture.appendChild(img);
+ document.body.appendChild(picture);
+ return img;
+}
+
+function assert_cs(img, val) {
+ assert_equals(getComputedStyle(img).aspectRatio, val);
+}
+
+// Create and append a new image and immediately check the ratio.
+// This is not racy because the spec requires the user agent to queue a task:
+// https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
+test(function() {
+ var img = createPicture(100, 100);
+ assert_ratio(img, 1.0);
+ assert_cs(img, "auto 100 / 100");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "100px");
+ assert_equals(getComputedStyle(img).height, "100px");
+ var source = img.previousSibling;
+ assert_equals(getComputedStyle(source).width, "auto");
+ assert_equals(getComputedStyle(source).height, "auto");
+}, "Computed style for width/height/aspect-ratio");
+
+test(function() {
+ img = createPicture(200, 100);
+ img.setAttribute("width", 250);
+ img.setAttribute("height", 50);
+ assert_ratio(img, 2.0);
+ assert_cs(img, "auto 200 / 100");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "200px");
+ assert_equals(getComputedStyle(img).height, "100px");
+ source = img.previousSibling;
+ assert_equals(getComputedStyle(source).width, "auto");
+ assert_equals(getComputedStyle(source).height, "auto");
+}, "Source width/height should take precedence over img attributes.");
+
+test(function() {
+ img.parentNode.removeChild(img.previousSibling);
+ assert_cs(img, "auto 250 / 50");
+ img.src = "/images/green.png";
+ assert_ratio(img, 5.0);
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "250px");
+ assert_equals(getComputedStyle(img).height, "50px");
+}, "Make sure style gets invalidated correctly when the source gets removed.");
+
+test(function() {
+ img = createPicture(100, undefined);
+ img.setAttribute("width", 200);
+ img.setAttribute("height", 100);
+ assert_cs(img, "auto");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "100px");
+ assert_equals(getComputedStyle(img).height, "auto");
+}, "If the <source> has only one of width/height, we don't get an aspect ratio, even if the <img> has both.");
+
+test(function() {
+ img = createPicture(undefined, undefined);
+ img.setAttribute("width", 200);
+ img.setAttribute("height", 100);
+ assert_cs(img, "auto 200 / 100");
+}, "If we don't have width/height on the source, we fall back to width/height on the <img>.");
+
+test(function() {
+ img = createPicture(100, undefined);
+ img.parentNode.style.display = "none";
+ img.setAttribute("width", "200");
+ img.setAttribute("height", "300");
+ img.setAttribute("nowidth", "true");
+ assert_cs(img, "auto");
+ assert_equals(getComputedStyle(img).width, "100px");
+ assert_equals(getComputedStyle(img).height, "auto");
+}, "If we only have one width attribute, we should get width mapped but no aspect ratio, even if <img> has attributes.");
+
+test(function() {
+ img = createPicture(undefined, 100);
+ img.parentNode.style.display = "none";
+ img.setAttribute("width", "200");
+ img.setAttribute("height", "300");
+ img.setAttribute("nowidth", "true");
+ assert_cs(img, "auto");
+ assert_equals(getComputedStyle(img).width, "auto");
+ assert_equals(getComputedStyle(img).height, "100px");
+}, "If we only have height attribute, we should get height mapped but no aspect ratio, even if <img> has attributes.");
+
+test(function() {
+ img = createPicture(100, 100);
+ assert_cs(img, "auto 100 / 100");
+ img.previousSibling.setAttribute("height", "300");
+ assert_cs(img, "auto 100 / 300");
+ img.previousSibling.setAttribute("width", "10");
+ assert_cs(img, "auto 10 / 300");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "10px");
+ assert_equals(getComputedStyle(img).height, "300px");
+}, "Dynamically changing width/height should change computed style");
+
+test(function() {
+ img = document.getElementById("twosource-img");
+ assert_cs(img, "auto 100 / 100");
+ source = document.getElementById("twosource-s1");
+ source.setAttribute("type", "x-foo/x-bar");
+ // We should now match the second source
+ assert_cs(img, "auto 300 / 150");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "300px");
+ assert_equals(getComputedStyle(img).height, "150px");
+}, "Changing which <source> matches should change computed style");
+
+test(function() {
+ img = document.getElementById("percent-img");
+ assert_equals(img.offsetWidth, 100);
+ assert_equals(img.offsetHeight, 0);
+ assert_cs(img, "auto");
+ source = document.getElementById("percent-src");
+ assert_equals(source.width, 100);
+ assert_equals(source.height, 50);
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "100%");
+ assert_equals(getComputedStyle(img).height, "50%");
+}, "Percentages on source should be ignored for aspect-ratio but used for width/height.");
+
+test(function() {
+ img = document.getElementById("trailing-img");
+ assert_equals(img.offsetWidth, 100);
+ assert_equals(img.offsetHeight, 50);
+ assert_cs(img, "auto 100 / 50");
+ source = document.getElementById("trailing-src");
+ assert_equals(source.width, 100);
+ assert_equals(source.height, 50);
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "100px");
+ assert_equals(getComputedStyle(img).height, "50px");
+}, "Trailing garbage should be ignored but not make the attribute invalid");
+
+onload = function() {
+ let images = document.querySelectorAll("img");
+ test(function() {
+ var img = images[0];
+ assert_ratio(img, 2.0, "2.0 is the original aspect ratio of green-100x50.png");
+ assert_cs(img, "auto");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "auto");
+ assert_equals(getComputedStyle(img).height, "auto");
+ }, "Loaded picture test: Both <source> and <img> are without width and height attributes");
+
+ test(function () {
+ img = images[1];
+ assert_ratio(img, 2.0, "Loaded image's aspect ratio, at least by default, overrides width / height ratio.");
+ assert_cs(img, "auto 100 / 100");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "100px");
+ assert_equals(getComputedStyle(img).height, "100px");
+ }, "Loaded picture test: <source> with width and height attributes, <img> without width and height attributes");
+
+ test(function () {
+ img = images[2];
+ assert_ratio(img, 2.0, "Loaded image's aspect ratio, at least by default, overrides width / height ratio (2).");
+ assert_cs(img, "auto 100 / 100");
+ img.style.display = "none";
+ img.setAttribute("nowidth", "true");
+ assert_equals(getComputedStyle(img).width, "100px");
+ assert_equals(getComputedStyle(img).height, "100px");
+ }, "Loaded picture test: Both <source> and <img> are with width and height attributes");
+
+ guard.done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/resources/aspect-ratio.js b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/resources/aspect-ratio.js
new file mode 100644
index 0000000000..c6826f271a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/resources/aspect-ratio.js
@@ -0,0 +1,14 @@
+function test_computed_style_aspect_ratio(tag, attributes, expected) {
+ test(function() {
+ var elem = document.createElement(tag);
+ for (name in attributes) {
+ let val = attributes[name];
+ if (val !== null)
+ elem.setAttribute(name, val);
+ }
+ document.body.appendChild(elem);
+ let aspectRatio = getComputedStyle(elem).aspectRatio;
+ assert_equals(aspectRatio, expected);
+ elem.remove();
+ }, `Computed style test: ${tag} with ${JSON.stringify(attributes)}`);
+}
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-aspect-ratio.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-aspect-ratio.html
new file mode 100644
index 0000000000..119523d250
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-aspect-ratio.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<title>Video width and height attributes are used to infer aspect-ratio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script src="resources/aspect-ratio.js"></script>
+<style>
+ video {
+ width: 100%;
+ max-width: 100px;
+ height: auto;
+ }
+</style>
+<body>
+<video width="250" height="100" id="contained" style="contain: size;"></video>
+<script>
+function assert_ratio(img, expected) {
+ let epsilon = 0.001;
+ assert_approx_equals(parseInt(getComputedStyle(img).width, 10) / parseInt(getComputedStyle(img).height, 10), expected, epsilon);
+}
+
+function test_computed_style(width, height, expected) {
+ test_computed_style_aspect_ratio("video", {width: width, height: height}, expected);
+}
+
+promise_test(async function() {
+ {
+ let video = document.getElementById("contained");
+ video.src = getVideoURI('/media/2x2-green');
+ assert_ratio(video, 2.5, "contain:size aspect ratio");
+ }
+
+ // Create and append a new video and immediately check the ratio.
+ // This is not racy because the spec requires the user agent to queue a task:
+ // https://html.spec.whatwg.org/multipage/media.html#concept-media-load-algorithm
+ {
+ let video = document.createElement("video");
+ video.setAttribute("width", "250");
+ video.setAttribute("height", "100");
+ video.src = getVideoURI('/media/2x2-green');
+ document.body.appendChild(video);
+ assert_ratio(video, 2.5, "aspect ratio for regular video before load");
+ await new Promise(r => video.addEventListener("loadeddata", r, { once: true }));
+ // When loaded this video is square.
+ assert_ratio(video, 1, "aspect ratio for regular video after load");
+ }
+
+ // Same but with auto width.
+ {
+ let video = document.createElement("video");
+ video.setAttribute("width", "250");
+ video.setAttribute("height", "100");
+ video.style.width = "auto";
+ video.src = getVideoURI('/media/2x2-green');
+ document.body.appendChild(video);
+ assert_ratio(video, 2.5, "aspect ratio for regular video with width: auto before load");
+ await new Promise(r => video.addEventListener("loadeddata", r, { once: true }));
+ assert_ratio(video, 1, "aspect ratio for regular video with width: auto after load");
+ }
+
+ test_computed_style("10", "20", "auto 10 / 20");
+ test_computed_style("0.5", "1.5", "auto 0.5 / 1.5");
+ test_computed_style("0", "1", "auto 0 / 1");
+ test_computed_style("1", "0", "auto 1 / 0");
+ test_computed_style("0", "0", "auto 0 / 0");
+ test_computed_style(null, null, "auto");
+ test_computed_style("10", null, "auto");
+ test_computed_style(null, "20", "auto");
+ test_computed_style("xx", "20", "auto");
+ test_computed_style("10%", "20", "auto");
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html
new file mode 100644
index 0000000000..74989be521
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>video element intrinsic width/height</title>
+ <link rel="author" title="Sammy Gill" href="sammy.gill@apple.com" />
+ <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7524" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#replaced-elements" />
+ <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <video title="no width/height attributes"
+ data-expected-width="300" data-expected-height="150"></video>
+ <video title="only width attribute"
+ data-expected-width="100" data-expected-height="50"
+ width="100"></video>
+ <video title="only height attribute"
+ data-expected-width="200" data-expected-height="100"
+ height="100"></video>
+ <video title="both width/height attributes"
+ data-expected-width="100" data-expected-height="100"
+ width="100" height="100"></video>
+ <!-- A width:height ratio other than 2:1 and overriding the specified style must be used to
+ verify that width/height does not influence intrinsic ratio -->
+ <video title="both width/height attributes and style"
+ data-expected-width="300" data-expected-height="300"
+ width="100" height="100" style="width: auto; height: auto"></video>
+ <script>
+ Array.prototype.forEach.call(document.querySelectorAll('video'), function(video)
+ {
+ test(function()
+ {
+ assert_equals(video.clientWidth, parseInt(video.dataset.expectedWidth), "width");
+ assert_equals(video.clientHeight, parseInt(video.dataset.expectedHeight), "height");
+ }, video.title);
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-001.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-001.html
new file mode 100644
index 0000000000..eeb4368aaa
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-001.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>HTML audio with controls</title>
+<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules">
+<link rel="mismatch" href="/common/blank.html">
+
+<audio controls></audio>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-002.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-002.html
new file mode 100644
index 0000000000..678ba7281e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-controls-002.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>HTML audio with controls via Web APIs</title>
+<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules">
+<link rel="mismatch" href="/common/blank.html">
+
+<audio id="target"></audio>
+
+<script>
+ document.body.offsetTop;
+ document.getElementById("target").setAttribute("controls", "");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-without-controls.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-without-controls.html
new file mode 100644
index 0000000000..8cc134d6d6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/audio-without-controls.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>HTML audio without controls</title>
+<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules">
+<link rel="match" href="../../../../css/reference/ref-filled-green-100px-square.xht" />
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="width: 100px; height: 100px; background: green;">
+ <audio style="display: block; width: 100px; height: 100px; background: red;"></audio>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback-ref.html
new file mode 100644
index 0000000000..9077591f46
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Canvas fallback content</title>
+<p>The word "FAIL" should not be visible below this line.
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback.html
new file mode 100644
index 0000000000..01df0c547a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-fallback.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Canvas fallback content</title>
+<link rel=match href=canvas-fallback-ref.html>
+<style>
+canvas {
+ height: 2em; /* avoid causing scrollbar for 600x600 viewport */
+}
+
+#canvas2 {
+ display: inline;
+}
+
+#canvas3 {
+ display: block;
+}
+
+#canvas4 {
+ display: table;
+}
+</style>
+<p>The word "FAIL" should not be visible below this line.
+<p><canvas id=canvas1>FAIL</canvas>
+<p><canvas id=canvas2>FAIL</canvas>
+<p><canvas id=canvas3>FAIL</canvas>
+<p><canvas id=canvas4>FAIL</canvas>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit-ref.html
new file mode 100644
index 0000000000..3133ac86f3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<div style="width: 200px; height: 100px; background: black; border: 100px solid blue; padding-left: 100px">
+ <div style="width: 100px; height: 100px; background: green"></div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit.html
new file mode 100644
index 0000000000..b20db66eb9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Verifies canvas with object-fit and border correctly updates</title>
+<link rel="match" href="canvas-update-with-border-object-fit-ref.html">
+<html class="reftest-wait">
+ <div style="width: 300px; height: 100px; background: black; border: 100px solid blue">
+ <canvas id="target" width="1000" height="1000"
+ style="object-fit: contain; object-position: center; width: 100%; height: 100%">
+ </canvas>
+ </div>
+</html>
+<script>
+var ctx = target.getContext("2d");
+ctx.fillStyle = "red";
+ctx.fillRect(0, 0, target.width, target.height);
+
+var x=0, y=0, step=500;
+ctx.fillStyle = "green";
+function drawRect() {
+ ctx.fillRect(x, y, step, step);
+ x += step;
+ if (x >= target.width) {
+ x = 0;
+ y += step;
+ }
+ if (y >= target.height)
+ document.documentElement.classList.remove("reftest-wait");
+ else
+ requestAnimationFrame(drawRect);
+}
+drawRect();
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale.html
new file mode 100644
index 0000000000..cdc4647534
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verify that canvases are scaled up to their computed size</title>
+<link rel="match" href="canvas_scale_ref.html">
+<style>
+canvas {
+ width: 20px;
+ height: 20px;
+}
+div {
+ line-height: 0;
+}
+</style>
+<div><canvas width="16" height="16" data-color="#FF00FF"></canvas><canvas width="16" height="16" data-color="#00FF00"></canvas></div>
+<div><canvas width="16" height="16" data-color="#0000FF"></canvas><canvas width="16" height="16" data-color="#FF00FF"></canvas></div>
+<script>
+var canvases = document.getElementsByTagName('canvas');
+for (var i = 0; i < canvases.length; i++) {
+ var ctx = canvases[i].getContext('2d');
+ ctx.fillStyle = canvases[i].getAttribute('data-color');
+ ctx.fillRect(0, 0, 16, 16);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale_ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale_ref.html
new file mode 100644
index 0000000000..2d1756f856
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_scale_ref.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset="utf-8">
+<style>
+span {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+}
+div {
+ line-height: 0;
+}
+</style>
+<div><span style="background-color: #FF00FF"></span><span style="background-color: #00FF00"></span></div>
+<div><span style="background-color: #0000FF"></span><span style="background-color: #FF00FF"></span></div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_a.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_a.html
new file mode 100644
index 0000000000..b7398f8d59
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_a.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="match" href="canvas_without_context_ref.html">
+<style>
+ div {
+ background-color: green;
+ width: 20px;
+ height: 20px;
+ }
+</style>
+</head>
+<body>
+<div><canvas width="20" height="20"></canvas></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_ref.html
new file mode 100644
index 0000000000..ae0c9c8c8e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas_without_context_ref.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<style>
+ div {
+ background-color: green;
+ width: 20px;
+ height: 20px;
+ }
+</style>
+</head>
+<body>
+<div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/audio-controls-intrinsic-size.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/audio-controls-intrinsic-size.html
new file mode 100644
index 0000000000..6cbbcd02f5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/audio-controls-intrinsic-size.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Audio intrinsic size doesn't depend on its max size</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1683979">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div style="display: inline-block">
+ <audio controls style="max-width: 99%" id="test"></audio>
+</div>
+<script>
+let audio = document.getElementById("test");
+
+function computeSize() {
+ return audio.getBoundingClientRect().width;
+}
+
+let size = computeSize();
+async_test(function(t) {
+ requestAnimationFrame(t.step_func(function() {
+ assert_equals(computeSize(), size, "Shouldn't have changed size");
+ requestAnimationFrame(t.step_func_done(function() {
+ assert_equals(computeSize(), size, "Shouldn't have changed size");
+ }));
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed-ref.html
new file mode 100644
index 0000000000..96500cf0bd
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<embed style="display:block;" src="data:text/html,PASS">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html
new file mode 100644
index 0000000000..521a816337
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1240261">
+<link rel="match" href="change-src-while-not-displayed-ref.html">
+<embed id="embed" style="display:block;" src="data:text/html,FAIL">
+<script>
+ onload = function() {
+ document.body.offsetTop;
+ embed.style.display = "none";
+ document.body.offsetTop;
+ embed.src = "data:text/html,PASS";
+ document.body.offsetTop;
+ embed.style.display = "block";
+ requestAnimationFrame(()=> {
+ requestAnimationFrame(()=> {
+ document.documentElement.classList.remove('reftest-wait');
+ });
+ });
+ }
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub-ref.html
new file mode 100644
index 0000000000..2645ed459f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<div style="height: 100px"></div>
+<iframe id=myframe src="/images/green.png"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub.html
new file mode 100644
index 0000000000..e39e2bc764
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Rendering of cross-domain iframe element in multicol</title>
+<link rel="match" href="cross-domain-iframe-in-multicol.sub-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules">
+<meta name="assert" content="Checks that cross-domain iframe in multicol is correctly rendered">
+<div style="columns: 2; height: 300px">
+ <div style="height: 100px"></div>
+ <iframe id=myframe src="http://{{domains[www1]}}:{{ports[http][0]}}/images/green.png"></iframe>
+ <div style="height: 100px"></div>
+</div>
+<script>
+ myframe.onload = () => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove('reftest-wait');
+ });
+ });
+ }
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub-ref.html
new file mode 100644
index 0000000000..a3579eee74
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Test reference</title>
+<iframe src="/images/green.png"></iframe>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub.html
new file mode 100644
index 0000000000..8d9b8cd5b4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Rendering of iframe element with src attribute from another domain</title>
+<link rel="match" href="cross-domain-iframe.sub-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules">
+<meta name="assert" content="Checks that iframe content is correctly rendered even if it is retrieved from a different domain.">
+<iframe id=myframe src="http://{{domains[www1]}}:{{ports[http][0]}}/images/green.png"></iframe>
+<script>
+ myframe.onload = () => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove('reftest-wait');
+ });
+ });
+ }
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration-ref.html
new file mode 100644
index 0000000000..9481e80ac8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Test reference</title>
+<span style="text-decoration:underline">This text should be underlined.</span>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration.html
new file mode 100644
index 0000000000..89657ef8a2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/object-fallback-text-decoration.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Rendering of object element fallback with text-decoration</title>
+<link rel="match" href="object-fallback-text-decoration-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules">
+<meta name="assert" content="Checks that text-decoration applies to rendered object fallback.">
+<style>
+ object { text-decoration: underline; }
+</style>
+<object>This text should be underlined.</object>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/resources/tall.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/resources/tall.html
new file mode 100644
index 0000000000..3de84d0b3f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/resources/tall.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body style="background: blue">
+ <div style="position: fixed; left: 0; top: 0; width: 100%; height: 100px; background: yellow"></div>
+ <div style="position: fixed; left: 0; bottom: 0; width: 100%; height: 100px; background: green"></div>
+ <div style="height: 2000px"></div>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub-ref.html
new file mode 100644
index 0000000000..01c1e2f86f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html class="reftest-wait">
+<iframe id=myframe style="width: 300px; height: 1000px" src="resources/tall.html"></iframe>
+<div style="height: 2000px"></div>
+<script>
+ window.scrollTo(0, 700);
+ myframe.onload = () => document.documentElement.classList.remove('reftest-wait');
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub.html
new file mode 100644
index 0000000000..865be7c883
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Rendering of tall cross-domain iframe element in a scrolled window</title>
+<link rel="match" href="tall-cross-domain-iframe-in-scrolled.sub-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules">
+<meta name="assert" content="Checks that tall cross-domain iframe in a scrolled window is correctly rendered">
+<iframe id=myframe style="width: 300px; height: 1000px"
+ src="http://{{domains[www1]}}:{{ports[http][0]}}/html/rendering/replaced-elements/embedded-content/resources/tall.html"></iframe>
+<div style="height: 2000px"></div>
+<script>
+ window.scrollTo(0, 700);
+ myframe.onload = () => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove('reftest-wait');
+ });
+ });
+ }
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode-ref.html
new file mode 100644
index 0000000000..e65bf6cbe8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<div>
+ <video controls></video>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode.html b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode.html
new file mode 100644
index 0000000000..acd2588112
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/embedded-content/video-controls-vertical-writing-mode.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Video controls rendering in vertical-lr</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#embedded-content-rendering-rules" />
+<link rel="match" href="video-controls-vertical-writing-mode-ref.html" />
+<div style="writing-mode:vertical-lr">
+ <video controls></video>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp-ref.html
new file mode 100644
index 0000000000..f37d8a3ec9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>Test reference</title>
+<style>img { border: solid; }</style>
+It should say PASS below:<br>
+<img alt="PASS">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp.html
new file mode 100644
index 0000000000..2416e5dfd0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/blocked-by-csp.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>Images behave the same when blocked by CSP as when failing to load/broken</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1664156">
+<link rel="match" href="blocked-by-csp-ref.html">
+<meta http-equiv=content-security-policy content="img-src 'none'">
+<style>img { border: solid; }</style>
+It should say PASS below:<br>
+<img src=image alt="PASS">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/img-sizes-auto.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/img-sizes-auto.html
new file mode 100644
index 0000000000..cbdf769596
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/img-sizes-auto.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<title>default styles for &lt;img sizes=auto></title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/rendering/support/test-ua-stylesheet.js"></script>
+<style>
+/* Specify this bogus namespace, so the rules in this stylesheet only apply to the `fakeClone`d elements in #refs, not the HTML elements in #tests. */
+@namespace url(urn:not-html);
+
+img:is([sizes="auto" i], [sizes^="auto," i]) {
+ contain: size !important;
+ contain-intrinsic-size: 300px 150px;
+}
+</style>
+
+<style>
+/* Test !important */
+img.test-important { contain: style; contain-intrinsic-size: 30px 15px; }
+</style>
+
+<div id="log"></div>
+
+<div id="tests">
+ <img sizes=auto>
+ <img sizes=auto width=10 height=20>
+ <img sizes=AuTo>
+ <img sizes=auto,xyz>
+ <img sizes=AuTo,xyz>
+
+ <!-- UA style should not apply: -->
+ <img>
+ <img sizes>
+ <img sizes=xyz,auto>
+ <picture data-skip>
+ <source data-skip sizes=auto>
+ <img>
+ </picture>
+
+ <!-- Test !important -->
+ <img sizes=auto class=test-important>
+</div>
+
+<div id="refs"></div>
+
+<script>
+ const props = [
+ 'contain',
+ 'contain-intrinsic-size'
+ ];
+ runUAStyleTests(props);
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content-ref.html
new file mode 100644
index 0000000000..37af13329e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Input type=image with CSS content.</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+You should see a red dot.<br>
+<input type="image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content.html
new file mode 100644
index 0000000000..5376e8033f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/input-image-content.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Input type=image with CSS content.</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="match" href="input-image-content-ref.html">
+
+You should see a red dot.<br>
+<style>
+ input {
+ content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==);
+ }
+</style>
+
+<input type="image">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print-ref.html
new file mode 100644
index 0000000000..6b2f0bb606
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Test reference</title>
+<img width=100 height=50 src="/images/black-rectangle.png">
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print.html
new file mode 100644
index 0000000000..fd5c2c5754
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/revoked-blob-print.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="test-wait">
+<title>Printing an image with src="revoked-blob"</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1665343">
+<link rel="match" href="revoked-blob-print-ref.html">
+<img width=100 height=50>
+<script>
+ let canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 50;
+ let ctx = canvas.getContext("2d");
+ ctx.fillRect(0, 0, 100, 50);
+ canvas.toBlob(function(blob) {
+ let img = document.querySelector("img");
+ let url = URL.createObjectURL(blob);
+ img.onload = function() {
+ URL.revokeObjectURL(url);
+ document.documentElement.className = "";
+ };
+ img.src = url;
+ });
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/space-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/space-ref.html
new file mode 100644
index 0000000000..0cf272e162
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/space-ref.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>img hspace/vspace - reference</title>
+<style>
+span { background: blue; }
+</style>
+<div style=width:400px;>
+<p><span><img src=/images/green.png></span>
+<p><span><img src=/images/green.png style="margin: 0 10px"></span>
+<p><span><img src=/images/green.png style="margin: 10px 0"></span>
+<p><span><img src=/images/green.png style="margin: 0 10%"></span>
+<p><span><img src=/images/green.png style="margin: 10% 0"></span>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/images/space.html b/testing/web-platform/tests/html/rendering/replaced-elements/images/space.html
new file mode 100644
index 0000000000..fee115dfce
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/images/space.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>img hspace/vspace</title>
+<link rel=match href=space-ref.html>
+<style>
+span { background: blue; }
+</style>
+<div style=width:400px;>
+<p><span><img src=/images/green.png></span>
+<p><span><img src=/images/green.png hspace=10></span>
+<p><span><img src=/images/green.png vspace=10></span>
+<p><span><img src=/images/green.png hspace=10%></span>
+<p><span><img src=/images/green.png vspace=10%></span>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/resources/svg-sizing.js b/testing/web-platform/tests/html/rendering/replaced-elements/resources/svg-sizing.js
new file mode 100644
index 0000000000..c212c6b283
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/resources/svg-sizing.js
@@ -0,0 +1,418 @@
+// Simple implementation of SVG sizing
+
+setup({explicit_done: true});
+
+var SVGSizing = (function() {
+ function parseLength(l) {
+ var match = /^([-+]?[0-9]+|[-+]?[0-9]*\.[0-9]+)(px|%)?$/.exec(l);
+ if (!match)
+ return null;
+ return new Length(Number(match[1]), match[2] ? match[2] : "px");
+ }
+
+ function parseViewBox(input) {
+ if (!input)
+ return null;
+
+ var arr = input.split(' ');
+ return arr.map(function(a) { return parseInt(a); });
+ }
+
+ // Only px and % are used
+ function convertToPx(input, percentRef) {
+ if (input == null)
+ return null;
+ var length = parseLength(input);
+ if (length.amount == 0)
+ return 0;
+ if (!length.unit)
+ length.unit = "px";
+ if (length.unit == "%" && percentRef === undefined)
+ return null;
+ return length.amount * { px: 1,
+ "%": percentRef/100}[length.unit];
+ }
+
+ function Length(amount, unit) {
+ this.amount = amount;
+ this.unit = unit;
+ }
+
+ function describe(data) {
+ function dumpObject(obj) {
+ var r = "";
+ for (var property in obj) {
+ if (obj.hasOwnProperty(property)) {
+ var value = obj[property];
+ if (typeof value == 'string')
+ value = "'" + value + "'";
+ else if (value == null)
+ value = "null";
+ else if (typeof value == 'object')
+ {
+ if (value instanceof Array)
+ value = "[" + value + "]";
+ else
+ value = "{" + dumpObject(value) + "}";
+ }
+
+ if (value != "null")
+ r += property + ": " + value + ", ";
+ }
+ }
+ return r;
+ }
+ var result = dumpObject(data);
+ if (result == "")
+ return "(initial values)";
+ return result;
+ }
+
+ function mapPresentationalHintLength(testData, cssProperty, attr) {
+ if (attr) {
+ var l = parseLength(attr);
+ if (l)
+ testData.style[cssProperty] = l.amount + l.unit;
+ }
+ }
+
+ function computedWidthIsAuto(testData) {
+ return !testData.style["width"] || testData.style["width"] == 'auto';
+ }
+
+ function computedHeightIsAuto(testData) {
+ return !testData.style["height"] || testData.style["height"] == 'auto' ||
+ (parseLength(testData.style["height"]).unit == '%' &&
+ containerComputedHeightIsAuto(testData));
+ }
+
+ function containerComputedWidthIsAuto(testData) {
+ return !testData.config.containerWidthStyle ||
+ testData.config.containerWidthStyle == 'auto';
+ }
+
+ function containerComputedHeightIsAuto(testData) {
+ return !testData.config.containerHeightStyle ||
+ testData.config.containerHeightStyle == 'auto';
+ }
+
+ function intrinsicInformation(testData) {
+ if (testData.config.placeholder == 'iframe')
+ return {};
+
+ var w = convertToPx(testData.config.svgWidthAttr) || 0;
+ var h = convertToPx(testData.config.svgHeightAttr) || 0;
+ var r = 0;
+ if (w && h) {
+ r = w / h;
+ } else {
+ var vb = parseViewBox(testData.config.svgViewBoxAttr);
+ if (vb) {
+ r = vb[2] / vb[3];
+ }
+ if (r) {
+ if (!w && h)
+ w = h * r;
+ else if (!h && w)
+ h = w / r;
+ }
+ }
+ return { width: w, height: h, ratio: r };
+ };
+
+ function contentAttributeForPlaceholder(testData) {
+ if (testData.config.placeholder == 'object')
+ return "data";
+ else
+ return "src";
+ }
+
+ function TestData(config) {
+ this.config = config;
+ this.name = describe(config);
+ this.style = {};
+ if (config.placeholder) {
+ mapPresentationalHintLength(this, "width", config.placeholderWidthAttr);
+ mapPresentationalHintLength(this, "height", config.placeholderHeightAttr);
+ } else {
+ if (config.svgWidthStyle)
+ this.style["width"] = config.svgWidthStyle;
+ else
+ mapPresentationalHintLength(this, "width", config.svgWidthAttr);
+
+ if (config.svgHeightStyle)
+ this.style["height"] = config.svgHeightStyle;
+ else
+ mapPresentationalHintLength(this, "height", config.svgHeightAttr);
+ }
+ }
+
+ TestData.prototype.computeInlineReplacedSize = function(outerWidth, outerHeight) {
+ var intrinsic = intrinsicInformation(this);
+ var self = this;
+
+ // http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height
+ function calculateUsedHeight() {
+ if (computedHeightIsAuto(self)) {
+ if (computedWidthIsAuto(self) && intrinsic.height)
+ return intrinsic.height;
+ if (intrinsic.ratio)
+ return calculateUsedWidth() / intrinsic.ratio;
+ if (intrinsic.height)
+ return intrinsic.height;
+ return 150;
+ }
+
+ return convertToPx(self.style["height"],
+ convertToPx(self.config.containerHeightStyle,
+ outerHeight));
+ }
+
+ // http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width
+ function calculateUsedWidth() {
+ if (computedWidthIsAuto(self)) {
+ if (computedHeightIsAuto(self) && intrinsic.width)
+ return intrinsic.width;
+ if (!computedHeightIsAuto(self) && intrinsic.ratio)
+ return calculateUsedHeight() * intrinsic.ratio;
+ if (computedHeightIsAuto(self) && intrinsic.ratio) {
+ if (containerComputedWidthIsAuto(self)) {
+ // Note: While this is actually undefined in CSS
+ // 2.1, use the suggested value by examining the
+ // ancestor widths.
+ return outerWidth;
+ } else {
+ return convertToPx(self.config.containerWidthStyle,
+ outerWidth);
+ }
+ }
+ if (intrinsic.width)
+ return intrinsic.width;
+ return 300;
+ }
+
+ if (containerComputedWidthIsAuto(self))
+ return convertToPx(self.style["width"], outerWidth);
+ else
+ return convertToPx(self.style["width"],
+ convertToPx(self.config.containerWidthStyle,
+ outerWidth));
+ }
+ return { width: calculateUsedWidth(),
+ height: calculateUsedHeight() };
+ };
+
+ TestData.prototype.buildContainer = function (placeholder, options) {
+ options = options || {};
+
+ var container = document.createElement("div");
+
+ container.id = "container";
+ if (this.config.containerWidthStyle)
+ container.style.width = this.config.containerWidthStyle;
+
+ if (this.config.containerHeightStyle)
+ container.style.height = this.config.containerHeightStyle;
+
+ if (options.pretty)
+ container.appendChild(document.createTextNode("\n\t\t"));
+ container.appendChild(placeholder);
+ if (options.pretty)
+ container.appendChild(document.createTextNode("\n\t"));
+
+ return container;
+ };
+
+ TestData.prototype.buildSVGOrPlaceholder = function (options) {
+ options = options || {};
+ var self = this;
+
+ if (this.config.placeholder) {
+ var generateSVGURI = function(testData, encoder) {
+ var res = '<svg xmlns="http://www.w3.org/2000/svg"';
+ function addAttr(attr, prop) {
+ if (testData.config[prop])
+ res += ' ' + attr + '="' + testData.config[prop] + '"';
+ }
+ addAttr("width", "svgWidthAttr");
+ addAttr("height", "svgHeightAttr");
+ addAttr("viewBox", "svgViewBoxAttr");
+ res += '></svg>';
+ return 'data:image/svg+xml' + encoder(res);
+ };
+ var placeholder = document.createElement(this.config.placeholder);
+ if (options.pretty) {
+ placeholder.appendChild(document.createTextNode("\n\t\t\t"));
+ placeholder.appendChild(
+ document.createComment(
+ generateSVGURI(this, function(x) { return "," + x; })));
+ placeholder.appendChild(document.createTextNode("\n\t\t"));
+ }
+ placeholder.setAttribute("id", "test");
+ if (this.config.placeholderWidthAttr)
+ placeholder.setAttribute("width", this.config.placeholderWidthAttr);
+ if (this.config.placeholderHeightAttr)
+ placeholder.setAttribute("height", this.config.placeholderHeightAttr);
+ placeholder.setAttribute(contentAttributeForPlaceholder(this),
+ generateSVGURI(this, function(x) {
+ return ";base64," + btoa(x);
+ }));
+ return placeholder;
+ } else {
+ var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svgElement.setAttribute("id", "test");
+ if (self.config.svgWidthStyle)
+ svgElement.style.width = self.config.svgWidthStyle;
+ if (self.config.svgHeightStyle)
+ svgElement.style.height = self.config.svgHeightStyle;
+ if (self.config.svgWidthAttr)
+ svgElement.setAttribute("width", self.config.svgWidthAttr);
+ if (self.config.svgHeightAttr)
+ svgElement.setAttribute("height", self.config.svgHeightAttr);
+ if (self.config.svgViewBoxAttr)
+ svgElement.setAttribute("viewBox", self.config.svgViewBoxAttr);
+ return svgElement;
+ }
+ };
+
+ TestData.prototype.buildDemo = function (expectedRect, id) {
+ // Non-essential debugging tool
+ var self = this;
+
+ function buildDemoSerialization() {
+ var outerWidth = 800;
+ var outerHeight = 600;
+
+ var options = { pretty: true };
+ var container =
+ self.buildContainer(self.buildSVGOrPlaceholder(options), options);
+
+ var root = document.createElement("html");
+ var style = document.createElement("style");
+
+ style.textContent = "\n" +
+ "\tbody { margin: 0; font-family: sans-serif }\n" +
+ "\tiframe { border: none }\n" +
+ "\t#expected {\n" +
+ "\t\twidth: " + (expectedRect.width) + "px; height: "
+ + (expectedRect.height) + "px;\n" +
+ "\t\tborder: 10px solid lime; position: absolute;\n" +
+ "\t\tbackground-color: red }\n" +
+ "\t#testContainer { position: absolute;\n" +
+ "\t\ttop: 10px; left: 10px; width: " + outerWidth + "px;\n" +
+ "\t\theight: " + outerHeight + "px }\n" +
+ "\t#test { background-color: green }\n" +
+ "\t.result { position: absolute; top: 0; right: 0;\n" +
+ "\t\tbackground-color: hsla(0,0%, 0%, 0.85); border-radius: 0.5em;\n" +
+ "\t\tpadding: 0.5em; border: 0.25em solid black }\n" +
+ "\t.pass { color: lime }\n" +
+ "\t.fail { color: red }\n";
+
+ root.appendChild(document.createTextNode("\n"));
+ root.appendChild(style);
+ root.appendChild(document.createTextNode("\n"));
+
+ var script = document.createElement("script");
+ script.textContent = "\n" +
+ "onload = function() {\n" +
+ "\tvar svgRect =\n" +
+ "\t\tdocument.querySelector('#test').getBoundingClientRect();\n" +
+ "\tpassed = (svgRect.width == " + expectedRect.width + " && " +
+ "svgRect.height == " + expectedRect.height + ");\n" +
+ "\tdocument.body.insertAdjacentHTML('beforeEnd',\n" +
+ "\t\t'<span class=\"result '+ (passed ? 'pass' : 'fail') " +
+ "+ '\">' + (passed ? 'Pass' : 'Fail') + '</span>');\n" +
+ "};\n";
+
+ root.appendChild(script);
+ root.appendChild(document.createTextNode("\n"));
+
+ var expectedElement = document.createElement("div");
+ expectedElement.id = "expected";
+ root.appendChild(expectedElement);
+ root.appendChild(document.createTextNode("\n"));
+
+ var testContainer = document.createElement("div");
+ testContainer.id = "testContainer";
+ testContainer.appendChild(document.createTextNode("\n\t"));
+ testContainer.appendChild(container);
+ testContainer.appendChild(document.createTextNode("\n"));
+ root.appendChild(testContainer);
+ root.appendChild(document.createTextNode("\n"));
+
+ return "<!DOCTYPE html>\n" + root.outerHTML;
+ }
+
+ function pad(n, width, z) {
+ z = z || '0';
+ n = n + '';
+ return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
+ }
+
+ function heightToDescription(height) {
+ if (!height || height == "auto")
+ return "auto";
+ if (parseLength(height).unit == '%')
+ return "percentage";
+ return "fixed";
+ }
+
+ var demoRoot = document.querySelector('#demo');
+ if (demoRoot) {
+ var demo = buildDemoSerialization();
+ var iframe = document.createElement('iframe');
+ iframe.style.width = (Math.max(900, expectedRect.width)) + "px";
+ iframe.style.height = (Math.max(400, expectedRect.height)) + "px";
+ iframe.src = "data:text/html;charset=utf-8," + encodeURIComponent(demo);
+ demoRoot.appendChild(iframe);
+ demoRoot.insertAdjacentHTML(
+ 'beforeEnd',
+ '<p><a href="data:application/octet-stream;charset=utf-8;base64,' +
+ btoa(demo) + '" download="svg-in-' + this.config.placeholder + "-" +
+ heightToDescription(this.config.placeholderHeightAttr) + "-" + pad(id, 3) +
+ '.html">Download</a></p>');
+ }
+ };
+
+ return {
+ TestData: TestData,
+ doCombinationTest: function(values, func, testSingleId) {
+ function computeConfig(id) {
+ id--;
+ var multiplier = 1;
+ var config = {};
+ for (var i=0; i<values.length; i++) {
+ // Compute offset into current array
+ var ii = (Math.floor(id / multiplier)) % values[i][1].length;
+ // Set corresponding value
+ config[values[i][0]] = values[i][1][ii];
+ // Compute new multiplier
+ multiplier *= values[i][1].length;
+ }
+ if (id >= multiplier)
+ return null;
+ return config;
+ }
+
+ function cont(id) {
+ var config = computeConfig(id);
+ if (config && (!testSingleId || testSingleId == id)) {
+ var next = function() {func(config, id, cont)};
+ // Make sure we don't blow the stack, without too much slowness
+ if (id % 20 === 0) {
+ step_timeout(next, 0);
+ } else {
+ next();
+ }
+ } else {
+ done();
+ }
+ };
+
+ if (testSingleId)
+ cont(testSingleId);
+ else
+ cont(1);
+ }
+ };
+})();
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-embedded-sizing.js b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-embedded-sizing.js
new file mode 100644
index 0000000000..a502d2c747
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-embedded-sizing.js
@@ -0,0 +1,96 @@
+// global async_test, assert_equals
+//
+// This test generates a couple of scenarios (each a
+// SVGSizing.TestData) for sizing inline <svg> and uses a simple
+// JavaScript sizing implementation for comparison.
+//
+// The tests loops through different combinations of:
+//
+// * width and height on <object>, <iframe> (input dependent)
+//
+// * width and height on <svg>
+//
+// * viewBox on <svg> (gives intrinsic ratio)
+//
+// * width and height on containing block of <object>
+//
+// All these contribute to the final size of the SVG in some way.
+//
+// The test focuses on the size of the CSS box generated by the SVG.
+// The SVG is always empty by itself so no actual SVG are tested.
+// Little focus is put on variations within an attribute that doesn't
+// affect the sizing behavior.
+//
+// To debug a specific test, append ?<test-id> to the URL. An <iframe>
+// is generated with equivalent test and the source code of the test
+// can be downloaded.
+
+var debugHint = function(id) { return "(append ?"+id+" to debug) "; };
+var testSingleId;
+if (window.location.search) {
+ testSingleId = parseInt(window.location.search.substring(1));
+ debugHint = function(id) { return ""; };
+}
+
+function testPlaceholderWithHeight(placeholder,
+ placeholderHeightAttr) {
+ var testContainer = document.querySelector('#testContainer');
+ var outerWidth = testContainer.getBoundingClientRect().width;
+ var outerHeight = testContainer.getBoundingClientRect().height;
+
+ SVGSizing.doCombinationTest(
+ [["placeholder", [ placeholder ]],
+ ["containerWidthStyle", [null, "400px"]],
+ ["containerHeightStyle", [null, "400px"]],
+ ["placeholderWidthAttr", [null, "100", "50%"]],
+ ["placeholderHeightAttr", [placeholderHeightAttr]],
+ ["svgViewBoxAttr", [ null, "0 0 100 200" ]],
+ ["svgWidthAttr", [ null, "200", "25%" ]],
+ ["svgHeightAttr", [ null, "200", "25%" ]]],
+ function (config, id, cont) {
+ var testData = new SVGSizing.TestData(config);
+ var t = async_test(testData.name);
+ var expectedRect =
+ testData.computeInlineReplacedSize(outerWidth, outerHeight);
+ var placeholder = testData.buildSVGOrPlaceholder();
+ var container =
+ testData.buildContainer(placeholder);
+
+ var checkSize = function() {
+ var placeholderRect =
+ placeholder.getBoundingClientRect();
+
+ try {
+ assert_equals(placeholderRect.width,
+ expectedRect.width,
+ debugHint(id) + "Wrong width");
+ assert_equals(placeholderRect.height,
+ expectedRect.height,
+ debugHint(id) + "Wrong height");
+ } finally {
+ testContainer.removeChild(container);
+ if (testSingleId)
+ document.body.removeChild(testContainer);
+ cont(id+1);
+ }
+ t.done();
+ };
+
+ if (!config.placeholder) {
+ testContainer.appendChild(container);
+ test(checkSize, testData.name);
+ } else {
+ t.step(function() {
+ placeholder.addEventListener('load', function() {
+ // step_timeout is a work-around to let engines
+ // finish layout of child browsing contexts even
+ // after the load event
+ step_timeout(t.step_func(checkSize), 0);
+ });
+ testContainer.appendChild(container);
+ });
+ }
+ if (testSingleId == id)
+ testData.buildDemo(expectedRect, id);
+ }, testSingleId);
+}
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-auto.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-auto.html
new file mode 100644
index 0000000000..4c3fefe88f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-auto.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;iframe></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("iframe", null)</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-fixed.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-fixed.html
new file mode 100644
index 0000000000..ae3328c6a8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-fixed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;iframe></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("iframe", '100px')</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-percentage.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-percentage.html
new file mode 100644
index 0000000000..da00c0680b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-iframe-percentage.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;iframe></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("iframe", '100%')</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-auto.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-auto.html
new file mode 100644
index 0000000000..cef3530676
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-auto.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;img></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("img", null)</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-fixed.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-fixed.html
new file mode 100644
index 0000000000..e8ad0dc935
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-fixed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;img></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("img", '100px')</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-percentage.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-percentage.html
new file mode 100644
index 0000000000..7bd5d90317
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-percentage.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;img></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("img", '100%')</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-auto.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-auto.html
new file mode 100644
index 0000000000..7d79d2a428
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-auto.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;object></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("object", null)</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-fixed.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-fixed.html
new file mode 100644
index 0000000000..75f7636258
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-fixed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;object></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("object", '100px')</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-percentage.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-percentage.html
new file mode 100644
index 0000000000..8f82836e1b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-object-percentage.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- This file is generated by gen-svgsizing-tests.py -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;object></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("object", '100%')</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html b/testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html
new file mode 100644
index 0000000000..2b3cf65366
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>SVG sizing: inline</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px;
+ }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-inline.js"></script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.js b/testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.js
new file mode 100644
index 0000000000..9b7fca0502
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.js
@@ -0,0 +1,79 @@
+// global async_test, assert_equals
+//
+// This test generates a couple of scenarios (each a
+// SVGSizing.TestData) for sizing inline <svg> and uses a simple
+// JavaScript sizing implementation for comparison.
+//
+// The tests loops through different combinations of:
+//
+// * width and height attributes and style on <svg>
+//
+// * viewBox on <svg> (gives intrinsic ratio)
+//
+// * width and height on containing block of <svg>
+//
+// All these may contribute to the final size of the SVG. The test
+// focuses on the size of the CSS box generated by the SVG. Little
+// focus is put on variations within an attribute that doesn't affect
+// the final size.
+//
+// To debug a specific test append ?<test-id> to the URL. An <iframe>
+// is generated with equivalent test and the source of the test is
+// added to a <pre> element.
+
+var debugHint = function(id) { return "(append ?"+id+" to debug) "; };
+var testSingleId;
+if (window.location.search) {
+ testSingleId = window.location.search.substring(1);
+ debugHint = function(id) { return ""; };
+}
+
+var testContainer = document.querySelector('#testContainer');
+var testContainerWidth = testContainer.getBoundingClientRect().width;
+var testContainerHeight = testContainer.getBoundingClientRect().height;
+
+SVGSizing.doCombinationTest(
+ [["placeholder", [ null ]],
+ ["containerWidthStyle", [null, "400px"]],
+ ["containerHeightStyle", [null, "400px"]],
+ ["svgViewBoxAttr", [ null, "0 0 100 200" ]],
+ ["svgWidthStyle", [ null, "100px", "50%" ]],
+ ["svgHeightStyle", [ null, "100px", "50%" ]],
+ ["svgWidthAttr", [ null, "200", "25%" ]],
+ ["svgHeightAttr", [ null, "200", "25%" ]]],
+ function(config, id, cont) {
+ var testData = new SVGSizing.TestData(config);
+
+ var expectedRect =
+ testData.computeInlineReplacedSize(testContainerWidth,
+ testContainerHeight);
+ var svgElement = testData.buildSVGOrPlaceholder();
+ var container =
+ testData.buildContainer(svgElement);
+
+ var checkSize = function() {
+ var svgRect =
+ svgElement.getBoundingClientRect();
+
+ try {
+ assert_equals(svgRect.width,
+ expectedRect.width,
+ debugHint(id) + "Wrong width");
+ assert_equals(svgRect.height,
+ expectedRect.height,
+ debugHint(id) + "Wrong height");
+ } finally {
+ testContainer.removeChild(container);
+ if (testSingleId)
+ document.body.removeChild(testContainer);
+ cont(id+1);
+ }
+ };
+
+ testContainer.appendChild(container);
+ test(checkSize, testData.name);
+
+ if (testSingleId == id) {
+ testData.buildDemo(expectedRect, id);
+ }
+ }, testSingleId);
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br-ref.html
new file mode 100644
index 0000000000..954840f389
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>option element with br child</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#concept-option-label">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-option-text">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-select-element-2">
+
+<p>This test passes if the option element displays three options:</p>
+
+<pre>a
+b
+ab</pre>
+
+<p>Importantly the third option must not be split across two lines.</p>
+
+<select multiple>
+ <option>a</option>
+ <option>b</option>
+ <option>ab</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br.html
new file mode 100644
index 0000000000..3b8d992cc2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/option-with-br.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>option element with br child</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#concept-option-label">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-option-text">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-select-element-2">
+
+<link rel="match" href="option-with-br-ref.html">
+
+<p>This test passes if the option element displays three options:</p>
+
+<pre>a
+b
+ab</pre>
+
+<p>Importantly the third option must not be split across two lines.</p>
+
+<select multiple>
+ <option>a</option>
+ <option>b</option>
+ <option id="manipulate-me"></option>
+</select>
+
+<script>
+"use strict";
+const option = document.querySelector("#manipulate-me");
+
+option.appendChild(document.createTextNode("a"));
+option.appendChild(document.createElement("br"));
+option.appendChild(document.createTextNode("b"));
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos-ref.html
new file mode 100644
index 0000000000..3cb496ea1b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos-ref.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Test reference</title>
+<style>
+.abspos {
+ position: absolute;
+ background-color: green;
+ height: 300px;
+ width: 300px;
+}
+</style>
+<div class="abspos"></div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos.html
new file mode 100644
index 0000000000..ed290250da
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-option-element/select-multiple-covered-by-abspos.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1770532">
+<link rel="author" href="mailto:dholbert@mozilla.com" title="Daniel Holbert">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="match" href="select-multiple-covered-by-abspos-ref.html">
+<title>Combobox selects are not stacking contexts by default</title>
+<style>
+.abspos {
+ position: absolute;
+ background-color: green;
+ height: 300px;
+ width: 300px;
+}
+</style>
+<div class="abspos"></div>
+<select multiple>
+ <option>This text shouldn't be visible.</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref-2.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref-2.html
new file mode 100644
index 0000000000..385c2a75d4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref-2.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1571764">
+<style>
+div {
+ -webkit-appearance: none;
+ appearance: none;
+
+ background: black;
+ color: black;
+
+ line-height: 100px;
+ width: 100px;
+
+ border: 0;
+ padding: 0;
+
+ display: inline-block;
+}
+</style>
+<div>A</div>
+<div>A</div>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref.html
new file mode 100644
index 0000000000..3834281dd8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001-ref.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1571764">
+<link rel="match" href="select-1-block-size-001-ref-2.html">
+<style>
+button {
+ -webkit-appearance: none;
+ appearance: none;
+
+ background: black;
+ color: black;
+
+ line-height: 100px;
+ width: 100px;
+
+ border: 0;
+ border-radius: 0;
+ padding: 0;
+}
+</style>
+<button>A</button>
+<button>A</button>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001.html
new file mode 100644
index 0000000000..5dc0fc15dc
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-001.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Select block size when line-height is specified</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1571764">
+<link rel="match" href="select-1-block-size-001-ref.html">
+<style>
+select {
+ -webkit-appearance: none;
+ appearance: none;
+
+ background: black;
+ color: black;
+
+ line-height: 100px;
+ width: 100px;
+
+ border: 0;
+ border-radius: 0;
+ padding: 0;
+}
+</style>
+<select></select>
+<select><option>A</option></select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-ref.html
new file mode 100644
index 0000000000..3e437494c0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Reference: Combobox block-size test</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+ <style>
+html,body {
+ color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+select { -webkit-appearance: none; }
+
+.big { font-size: 48pt; min-height: 40pt; }
+.lh { line-height: 48pt; min-height: 40pt; }
+
+.mask { position:fixed; left:20px; right:0; top:0; bottom:0; background: black; }
+ </style>
+</head>
+<body>
+
+<!-- mask off differences on the right side -->
+<div class="mask"></div>
+
+<select><optgroup label="label"><option>option</option></select><br>
+<select class="big"><optgroup label="label"><option>option</option></select><br>
+<select class="lh"><optgroup label="label"><option>option</option></select><br>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size.html
new file mode 100644
index 0000000000..4aecc596ce
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-block-size.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Test: Combobox block-size test</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+ <link rel="match" href="select-1-block-size-ref.html">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1499578">
+ <style>
+html,body {
+ color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+select { -webkit-appearance: none; }
+
+optgroup { font-size: 32pt; }
+option { font-size: 24pt; }
+.big { font-size: 48pt; }
+.lh { line-height: 48pt; }
+
+.mask { position:fixed; left:20px; right:0; top:0; bottom:0; background: black; }
+ </style>
+</head>
+<body>
+
+<!-- mask off differences on the right side -->
+<div class="mask"></div>
+
+<select><optgroup label="label"><option>option</option></select><br>
+<select class="big"><optgroup label="label"><option>option</option></select><br>
+<select class="lh"><optgroup label="label"><option>option</option></select><br>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height-ref.html
new file mode 100644
index 0000000000..26e5f33282
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Reference: Combobox ignores CSS 'line-height'</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+ <style type="text/css">
+html,body {
+ color:black; background-color:white; font:16px/1 monospace;
+}
+
+ </style>
+</head>
+<body>
+
+<select><option>aaaaaaaaaa<option>bbbbbbbbbb</select>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height.html
new file mode 100644
index 0000000000..605a988e25
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-1-line-height.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Test: Combobox ignores CSS 'line-height'</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+ <link rel="match" href="select-1-line-height-ref.html">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1501908">
+ <style type="text/css">
+html,body {
+ color:black; background-color:white; font:16px/1 monospace;
+}
+
+select { line-height:100px; }
+
+ </style>
+</head>
+<body>
+
+<select><option>aaaaaaaaaa<option>bbbbbbbbbb</select>
+
+<script>
+document.body.offsetHeight;
+var cv = window.getComputedStyle(document.querySelector('select')).lineHeight;
+if (cv != "normal" && parseInt(cv) > 50) {
+ document.body.appendChild(document.createTextNode(
+ "FAIL: got computed line-height '" + cv + "', " +
+ "expected 'normal' or a length <= 50px"));
+}</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty-ref.html
new file mode 100644
index 0000000000..31ba23a5cf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Reference: empty SELECT</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+ <style>
+
+.none { display: none; }
+
+ </style>
+</head>
+<body>
+
+<table border="1" cellpadding="10">
+<tr>
+<td><select size="4"><option class="none"></select>
+<td><select size="4" style="-webkit-appearance: none"><option class="none">option</select>
+<td><select size="4" style="-webkit-appearance: none; border: 1px solid black"><option class="none">option</select>
+<td><select size="4" style="border: 1px solid black"><option class="none">option</select>
+</table>
+
+<table border="1" cellpadding="10">
+<tr>
+<td><select size="1"><option class="none"></select>
+<td><select size="1" style="-webkit-appearance: none"><option class="none"></select>
+<td><select size="1" style="-webkit-appearance: none; border: 1px solid black"><option class="none"></select>
+<td><select size="1" style="border: 1px solid black"><option class="none"></select>
+</table>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty.html
new file mode 100644
index 0000000000..6568a6de34
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-empty.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Test: empty SELECT</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+ <link rel="match" href="select-empty-ref.html">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1499230">
+</head>
+<body>
+
+<table border="1" cellpadding="10">
+<tr>
+<td><select size="4"></select>
+<td><select size="4" style="-webkit-appearance: none"></select>
+<td><select size="4" style="-webkit-appearance: none; border: 1px solid black"></select>
+<td><select size="4" style="border: 1px solid black"></select>
+</table>
+
+<table border="1" cellpadding="10">
+<tr>
+<td><select size="1"></select>
+<td><select size="1" style="-webkit-appearance: none"></select>
+<td><select size="1" style="-webkit-appearance: none; border: 1px solid black"></select>
+<td><select size="1" style="border: 1px solid black"></select>
+</table>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size-ref.html
new file mode 100644
index 0000000000..8b1b422176
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<select>
+ <option>ABC</option>
+</select>
+<select>
+ <option>ABC</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size.html
new file mode 100644
index 0000000000..7f36708973
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-option-font-size.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Select should be as wide as needed to fit its options regardless of option styles</title>
+<link rel=match href=select-intrinsic-option-font-size-ref.html>
+<select>
+ <option style="font-size: 5px">ABC</option>
+</select>
+<select>
+ <option style="font-size: 50px">ABC</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform-ref.html
new file mode 100644
index 0000000000..18e272ba10
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<select>
+ <option>ABCDEFGHIJK</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform.html
new file mode 100644
index 0000000000..1026e29977
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-intrinsic-text-transform.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>text-transform in option doesn't affect combobox rendering</title>
+<link rel=match href=select-intrinsic-text-transform-ref.html>
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1283930">
+<link rel=author href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<select>
+ <option style="text-transform: lowercase">ABCDEFGHIJK</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment-ref.html
new file mode 100644
index 0000000000..cb66ddaaaf
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>Reference: Move option from select[multiple] into DocumentFragment and back</title>
+<p>You should see the word PASS below.</p>
+<select multiple><option>PASS</option></select>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html
new file mode 100644
index 0000000000..844f05639c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>Test: Move option from select[multiple] into DocumentFragment and back</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="select-multiple-re-add-option-via-document-fragment-ref.html">
+<p>You should see the word PASS below.</p>
+<select multiple id="sel"><option id="opt">PASS</option></select>
+<script>
+ document.body.offsetTop;
+ let rm = opt;
+ document.createDocumentFragment().appendChild(rm);
+ sel.appendChild(rm);
+</script>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001-ref.html
new file mode 100644
index 0000000000..2f479dd72a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference Case</title>
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ block-size: 8em;
+ inline-size: 10em;
+
+ padding-block-end: 0;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+<textarea>X</textarea>
+<textarea class="rtl">X</textarea>
+<br>
+<textarea class="vlr">X</textarea>
+<textarea class="vrl">X</textarea>
+<textarea class="slr">X</textarea>
+<textarea class="srl">X</textarea>
+<br>
+<textarea class="vlr rtl">X</textarea>
+<textarea class="vrl rtl">X</textarea>
+<textarea class="slr rtl">X</textarea>
+<textarea class="srl rtl">X</textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001.tentative.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001.tentative.html
new file mode 100644
index 0000000000..d99ca1956f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bend-overlaps-content-001.tentative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test: padding-block-end on a textarea creates space that content can render into</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-textarea-element-2">
+<link rel="match" href="textarea-padding-bend-overlaps-content-001-ref.html">
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ /* Zero out the content-box, leaving the padding area as the only
+ place where the contents might get rendered. */
+ block-size: 0;
+ inline-size: 10em;
+
+ padding-block-end: 8em;
+
+ /* The textarea's padding-block-end may create overflow in the block axis,
+ and hence generate a scrollbar in the testcase that's not present in the
+ reference case (and might be hard to properly mock up there). We avoid
+ this problem by suppressing scrollbars using "overflow:hidden": */
+ overflow: hidden;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+
+<textarea>X</textarea>
+<textarea class="rtl">X</textarea>
+<br>
+<textarea class="vlr">X</textarea>
+<textarea class="vrl">X</textarea>
+<textarea class="slr">X</textarea>
+<textarea class="srl">X</textarea>
+<br>
+<textarea class="vlr rtl">X</textarea>
+<textarea class="vrl rtl">X</textarea>
+<textarea class="slr rtl">X</textarea>
+<textarea class="srl rtl">X</textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001-ref.html
new file mode 100644
index 0000000000..5144f18660
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference Case</title>
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ block-size: 8em;
+ inline-size: 10em;
+
+ padding-block-start: 0;
+ padding-block-end: 0;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+
+<textarea></textarea>
+<textarea class="rtl"></textarea>
+<br>
+<textarea class="vlr"></textarea>
+<textarea class="vrl"></textarea>
+<textarea class="slr"></textarea>
+<textarea class="srl"></textarea>
+<br>
+<textarea class="vlr rtl"></textarea>
+<textarea class="vrl rtl"></textarea>
+<textarea class="slr rtl"></textarea>
+<textarea class="srl rtl"></textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001.tentative.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001.tentative.html
new file mode 100644
index 0000000000..f4643af132
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-bstart-moves-content-001.tentative.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test: padding-block-start on a textarea moves the textarea content over, potentially out of the textarea's scrollport</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-textarea-element-2">
+<link rel="match" href="textarea-padding-bstart-moves-content-001-ref.html">
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ /* Zero out the content-box, leaving the padding area as the only
+ place where the contents might get rendered. */
+ block-size: 0;
+ inline-size: 10em;
+
+ padding-block-start: 8em;
+ padding-block-end: 0;
+
+ /* We expect the textarea's content to overflow in the block direction,
+ which makes the textarea blank, aside from any scrollbars that might get
+ created by this overflow. We use overflow:hidden here to suppress these
+ scrollbars, so that the reference case can just use a trivial empty
+ textarea (without needing to worry about mocking up scrollbars of
+ precisely the right size). */
+ overflow: hidden;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+
+<textarea>X</textarea>
+<textarea class="rtl">X</textarea>
+<br>
+<textarea class="vlr">X</textarea>
+<textarea class="vrl">X</textarea>
+<textarea class="slr">X</textarea>
+<textarea class="srl">X</textarea>
+<br>
+<textarea class="vlr rtl">X</textarea>
+<textarea class="vrl rtl">X</textarea>
+<textarea class="slr rtl">X</textarea>
+<textarea class="srl rtl">X</textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001-ref.html
new file mode 100644
index 0000000000..94473c0873
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference Case</title>
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ inline-size: 8em;
+ block-size: 10em;
+
+ padding-inline-end: 0;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+<textarea>X</textarea>
+<textarea class="rtl">X</textarea>
+<br>
+<textarea class="vlr">X</textarea>
+<textarea class="vrl">X</textarea>
+<textarea class="slr">X</textarea>
+<textarea class="srl">X</textarea>
+<br>
+<textarea class="vlr rtl">X</textarea>
+<textarea class="vrl rtl">X</textarea>
+<textarea class="slr rtl">X</textarea>
+<textarea class="srl rtl">X</textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001.tentative.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001.tentative.html
new file mode 100644
index 0000000000..80cbbe3c1e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-iend-overlaps-content-001.tentative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test: padding-inline-end on a textarea creates space that content can render into</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-textarea-element-2">
+<link rel="match" href="textarea-padding-iend-overlaps-content-001-ref.html">
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ /* Zero out the content-box, leaving the padding area as the only
+ place where the contents might get rendered. */
+ inline-size: 0;
+ block-size: 10em;
+
+ padding-inline-end: 8em;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+
+<textarea>X</textarea>
+<textarea class="rtl">X</textarea>
+<br>
+<textarea class="vlr">X</textarea>
+<textarea class="vrl">X</textarea>
+<textarea class="slr">X</textarea>
+<textarea class="srl">X</textarea>
+<br>
+<textarea class="vlr rtl">X</textarea>
+<textarea class="vrl rtl">X</textarea>
+<textarea class="slr rtl">X</textarea>
+<textarea class="srl rtl">X</textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001-ref.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001-ref.html
new file mode 100644
index 0000000000..eb88858b87
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference Case</title>
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ inline-size: 8em;
+ block-size: 10em;
+
+ padding-inline-start: 0;
+ padding-inline-end: 0;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+
+<textarea></textarea>
+<textarea class="rtl"></textarea>
+<br>
+<textarea class="vlr"></textarea>
+<textarea class="vrl"></textarea>
+<textarea class="slr"></textarea>
+<textarea class="srl"></textarea>
+<br>
+<textarea class="vlr rtl"></textarea>
+<textarea class="vrl rtl"></textarea>
+<textarea class="slr rtl"></textarea>
+<textarea class="srl rtl"></textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001.tentative.html b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001.tentative.html
new file mode 100644
index 0000000000..f5abf84c12
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/the-textarea-element/textarea-padding-istart-moves-content-001.tentative.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test: padding-inline-start on a textarea moves the textarea content over, potentially out of the textarea's scrollport</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-textarea-element-2">
+<link rel="match" href="textarea-padding-istart-moves-content-001-ref.html">
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+textarea {
+ font: 10px/1 Ahem;
+
+ /* Zero out the content-box, leaving the padding area as the only
+ place where the contents might get rendered. */
+ inline-size: 0;
+ block-size: 10em;
+
+ padding-inline-start: 8em;
+ padding-inline-end: 0;
+
+ /* We expect the textarea's content to overflow in the inline direction,
+ which makes the textarea blank, aside from any scrollbars that might get
+ created by this overflow. We use overflow:hidden here to suppress these
+ scrollbars, so that the reference case can just use a trivial empty
+ textarea (without needing to worry about mocking up scrollbars of
+ precisely the right size). */
+ overflow: hidden;
+}
+.rtl { direction: rtl; }
+.vlr { writing-mode: vertical-lr; }
+.vrl { writing-mode: vertical-rl; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+</style>
+
+<textarea>X</textarea>
+<textarea class="rtl">X</textarea>
+<br>
+<textarea class="vlr">X</textarea>
+<textarea class="vrl">X</textarea>
+<textarea class="slr">X</textarea>
+<textarea class="srl">X</textarea>
+<br>
+<textarea class="vlr rtl">X</textarea>
+<textarea class="vrl rtl">X</textarea>
+<textarea class="slr rtl">X</textarea>
+<textarea class="srl rtl">X</textarea>
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/tools/gen-svgsizing-tests.py b/testing/web-platform/tests/html/rendering/replaced-elements/tools/gen-svgsizing-tests.py
new file mode 100644
index 0000000000..5ba69f8ab5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/replaced-elements/tools/gen-svgsizing-tests.py
@@ -0,0 +1,55 @@
+from string import Template
+import os
+import sys
+
+template = Template("""<!DOCTYPE html>
+<!-- This file is generated by $generator -->
+<html>
+ <head>
+ <title>SVG sizing: &lt;$placeholder></title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/svg-sizing.js"></script>
+ <style>
+ #testContainer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 800px;
+ height: 600px
+ }
+ iframe { border: 0 }
+ </style>
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width">
+ <link rel="help" href="http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#replaced-elements">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-dim-width">
+ <link rel="help" href="http://www.w3.org/TR/SVG/coords.html#ViewportSpace">
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="testContainer"></div>
+ <div id="demo"></div>
+ <script src="svg-embedded-sizing.js"></script>
+ <script>testPlaceholderWithHeight("$placeholder", $placeholderHeightAttr)</script>
+ </body>
+</html>
+""")
+
+placeholders = [ "object", "iframe", "img" ]
+placeholderHeightAttrs = [ "null", "'100px'", "'100%'" ]
+placeholderHeightAttrsDescriptions = [ "auto", "fixed", "percentage" ]
+
+try:
+ os.makedirs("../svg-embedded-sizing")
+except OSError:
+ pass
+
+for placeholder in placeholders:
+ for i, placeholderHeightAttr in enumerate(placeholderHeightAttrs):
+ testContent = template.substitute(placeholder=placeholder, placeholderHeightAttr=placeholderHeightAttr, generator=sys.argv[0])
+ filename = "../svg-embedded-sizing/svg-in-%s-%s.html" % (placeholder, placeholderHeightAttrsDescriptions[i])
+ f = open(filename, "w")
+ f.write(testContent)
+ f.close()
diff --git a/testing/web-platform/tests/html/rendering/support/test-ua-stylesheet.js b/testing/web-platform/tests/html/rendering/support/test-ua-stylesheet.js
new file mode 100644
index 0000000000..e23c9a6720
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/support/test-ua-stylesheet.js
@@ -0,0 +1,74 @@
+function runUAStyleTests(props) {
+ const refs = document.getElementById('refs');
+ for (const el of document.querySelectorAll('#tests > *')) {
+ const clone = fakeClone(el);
+ refs.append(clone);
+ }
+ const testsContainer = document.getElementById('tests');
+ const testEls = document.querySelectorAll('#tests *');
+ const refEls = document.querySelectorAll('#refs *');
+ for (let i = 0; i < testEls.length; ++i) {
+ const testEl = testEls[i];
+ if (testEl.hasAttribute('data-skip')) {
+ continue;
+ }
+ const refEl = refEls[i];
+ const testStyle = getComputedStyle(testEl);
+ const refStyle = getComputedStyle(refEl);
+ for (const prop of props) {
+ // Don't test display for some elements.
+ // TODO(zcorpan): https://github.com/whatwg/html/issues/4093
+ // TODO(zcorpan): https://github.com/whatwg/html/issues/5063
+ if (prop === 'display' &&
+ (testEl.localName === 'optgroup' ||
+ testEl.localName === 'option' ||
+ testEl.localName === 'marquee')
+ ) {
+ continue;
+ }
+ if (prop === 'overflow' && testEl.localName === 'select') {
+ // TODO: https://github.com/whatwg/html/issues/10031
+ continue;
+ }
+ test(() => {
+ assert_equals(testStyle.getPropertyValue(prop), refStyle.getPropertyValue(prop));
+ }, `${testNameContext(testEl)} - ${prop}`);
+ }
+ }
+
+ function fakeClone(el) {
+ const clone = document.createElementNS('urn:not-html', el.localName);
+ for (const att of el.attributes) {
+ clone.setAttributeNS(att.namespaceURI, att.name, att.value);
+ }
+ // deep clone
+ for (const child of el.children) {
+ clone.append(fakeClone(child));
+ }
+ return clone;
+ }
+
+ function testNameContext(el) {
+ const outerHTML = el.outerHTML;
+ const startTags = outerHTML.substring(0, outerHTML.indexOf('</')) || outerHTML;
+
+ let ancestors = [];
+ let current = el.parentNode;
+ while (current != testsContainer) {
+ ancestors.unshift(`<${current.localName}${contextAttrs(current.attributes)}>`);
+ current = current.parentNode;
+ }
+ return startTags + (ancestors.length ? ` (in ${ancestors.join('')})` : '');
+ }
+
+ function contextAttrs(attributes) {
+ let rv = "";
+ for (let i = 0; i < attributes.length; ++i) {
+ if (attributes[i].name === 'data-skip') {
+ continue;
+ }
+ rv += ` ${attributes[i].name}="${attributes[i].value}"`;
+ }
+ return rv;
+ }
+}
diff --git a/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change-ref.html b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change-ref.html
new file mode 100644
index 0000000000..43f0c6dd20
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<body bgcolor="green">
+ Passes if the background is green.
+</body>
diff --git a/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change.html b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change.html
new file mode 100644
index 0000000000..d0b2396a40
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/body-bgcolor-attribute-change.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/obsolete.html#attr-body-bgcolor">
+<link rel="match" href="body-bgcolor-attribute-change-ref.html">
+<body bgcolor="yellow">
+ Passes if the background is green.
+</body>
+<script>
+ document.body.offsetTop;
+ document.body.setAttribute("bgcolor", "green");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/mouse-cursor-imagemap.html b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/mouse-cursor-imagemap.html
new file mode 100644
index 0000000000..78f69a2895
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/mouse-cursor-imagemap.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<img usemap="#map" src="data:image/gif;base64,R0lGODlhAQABAIAAAOTm7AAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" width="1000" height="1000" style="border: 1px solid black;">
+<map name="map">
+ <area id="clickable-area" shape="rect" coords="0,0,500,500" href="#" role="img">
+ <area id="nonclickable-area" shape="rect" coords="500,500,1000,1000" role="img"><!-- No href attribute.-->
+</map>
+
+<p>An unclickable (non-link) area should not show the link cursor when hovered.</p>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ let clickable = window.getComputedStyle(document.getElementById('clickable-area'));
+ let nonclickable = window.getComputedStyle(document.getElementById('nonclickable-area'));
+ assert_equals(clickable.getPropertyValue('cursor'), 'pointer');
+ assert_not_equals(nonclickable.getPropertyValue('cursor'), 'pointer');
+}, 'Only clickable areas should show the link cursor.');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links-wrapped-in-svg.historical.html b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links-wrapped-in-svg.historical.html
new file mode 100644
index 0000000000..da6ffe15bb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links-wrapped-in-svg.historical.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>link with rel="help" cursor tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<link rel="help" href="https://html.spec.whatwg.org/#phrasing-content-3">
+<link rel="help" href="https://github.com/w3c/svgwg/issues/468">
+
+<div id="log"></div>
+
+<svg>
+<a href="/common/blank.html?unvisited" rel="help" id="unvisited">unvisited</a>
+<a href="/common/blank.html?willbevisited" rel="help" id="willbevisited">will be visited</a>
+</svg>
+
+<script>
+"use strict";
+
+
+test(() => {
+ const el = document.querySelector("#unvisited");
+ const style = window.getComputedStyle(el);
+
+ assert_equals(style.cursor, "pointer");
+},"Unvisited help links must have pointer cursor, not help cursor");
+
+
+// This test is kind of dubious. Browsers don't allow you to distinguish visited and unvisited links
+// from script, for privacy reasons. So we can't really be sure that loading the iframe would make
+// the link count as visited. Manually running this test turns the link purple in some browsers,
+// but leaves it blue in others. Even then it's not clear whether it turned purple before or after
+// the onload; this test assumes that once the iframe onload fires, it counts as visited, which
+// may not be justified even in the purple-turning browsers.
+//
+// Still, the test doesn't really hurt. At worst it's redundant with the above.
+//
+// If someone comes up with a better way of testing this (i.e. something that truly guarantees that
+// the link will count as "visited" for UA stylesheet purposes), then please submit a PR.
+async_test(t => {
+ const el = document.querySelector("#willbevisited");
+
+ const iframe = document.createElement("iframe");
+ iframe.src = el.href;
+ iframe.onload = t.step_func_done(() => {
+ const style = window.getComputedStyle(el);
+ assert_equals(style.cursor, "pointer");
+ });
+
+ document.body.appendChild(iframe);
+}, "Visited help links must have pointer cursor, not help cursor");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links.historical.html b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links.historical.html
new file mode 100644
index 0000000000..82aa08d215
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links.historical.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>link with rel="help" cursor tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<link rel="help" href="https://html.spec.whatwg.org/#phrasing-content-3">
+<link rel="help" href="https://github.com/whatwg/html/pull/3902">
+
+<div id="log"></div>
+
+<a href="/common/blank.html?unvisited" rel="help" id="unvisited">unvisited</a>
+<a href="/common/blank.html?willbevisited" rel="help" id="willbevisited">will be visited</a>
+
+<script>
+"use strict";
+
+
+test(() => {
+ const el = document.querySelector("#unvisited");
+ const style = window.getComputedStyle(el);
+
+ assert_equals(style.cursor, "pointer");
+},"Unvisited help links must have pointer cursor, not help cursor");
+
+
+// This test is kind of dubious. Browsers don't allow you to distinguish visited and unvisited links
+// from script, for privacy reasons. So we can't really be sure that loading the iframe would make
+// the link count as visited. Manually running this test turns the link purple in some browsers,
+// but leaves it blue in others. Even then it's not clear whether it turned purple before or after
+// the onload; this test assumes that once the iframe onload fires, it counts as visited, which
+// may not be justified even in the purple-turning browsers.
+//
+// Still, the test doesn't really hurt. At worst it's redundant with the above.
+//
+// If someone comes up with a better way of testing this (i.e. something that truly guarantees that
+// the link will count as "visited" for UA stylesheet purposes), then please submit a PR.
+async_test(t => {
+ const el = document.querySelector("#willbevisited");
+
+ const iframe = document.createElement("iframe");
+ iframe.src = el.href;
+ iframe.onload = t.step_func_done(() => {
+ const style = window.getComputedStyle(el);
+ assert_equals(style.cursor, "pointer");
+ });
+
+ document.body.appendChild(iframe);
+}, "Visited help links must have pointer cursor, not help cursor");
+</script>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/auto-expand-details-text-fragment.html b/testing/web-platform/tests/html/rendering/the-details-element/auto-expand-details-text-fragment.html
new file mode 100644
index 0000000000..321d82c02d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/auto-expand-details-text-fragment.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>beforematch fired on ScrollToTextFragment</title>
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script src="/common/utils.js"></script>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<script>
+promise_test(t => new Promise((resolve, reject) => {
+ const key = token();
+ test_driver.bless('Open a scroll to text fragment URL', () => {
+ window.open(
+ `resources/auto-expand-details-text-fragment.html?key=${key}#:~:text=foo`,
+ '_blank',
+ 'noopener');
+ });
+ fetchResults(key, resolve, reject);
+}).then(results => {
+ assert_true(results.detailsHasOpenAttribute,
+ 'The matching closed details element should be open.');
+ assert_true(results.pageYOffsetAfterRaf > 0,
+ 'The page should be scrolled down to the match.');
+}), 'Verifies that the beforematch event is fired on the matching element of a ScrollToTextFragment navigation.');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-after.html b/testing/web-platform/tests/html/rendering/the-details-element/details-after.html
new file mode 100644
index 0000000000..b4bf050466
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-after.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="help" title="https://github.com/whatwg/html/pull/3686">
+<link rel="help" title="https://html.spec.whatwg.org/multipage/#the-details-element">
+<link rel="mismatch" href="single-summary.html">
+<title>CSS Test: details ::after pseudo-element</title>
+<style>
+ details::after { content: "::after" }
+</style>
+<details>
+ <summary>This is the main summary</summary>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-autofocus-crash.html b/testing/web-platform/tests/html/rendering/the-details-element/details-autofocus-crash.html
new file mode 100644
index 0000000000..70a220feac
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-autofocus-crash.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://crbug.com/1500582">
+<meta name="assert" content="The renderer should not crash.">
+<html>
+<head>
+<style>
+:not(g) { justify-content: right; line-clamp: none; glyph-orientation-vertical: 0deg; flood-color: rgb(22,11,39); mso-style-parent: style61; column-count: auto; nav-left: htmlvar00001 root; grid-row-end: 35 last; white-space-trim: discard-before discard-after discard-inner; row-gap: normal; cellspacing: 33; line-grid: match-parent; caret-shape: auto; outline: solid red 0px; -webkit-columns: 21; font-family: Courier; mso-displayed-thousand-separator: '\,'; animation-timeline: scroll( x ); shape-image-threshold: 0; stroke-origin: fill-box }
+
+.class1 { background-position-x: x-end 43; ruby-overhang: auto; -webkit-writing-mode: vertical-rl; flex-direction: initial; fill-size: 0px 92; voice-balance: center; -webkit-direction: rtl; inset-block-end: auto; -webkit-align-items: flex-start; text-decoration-style: dashed; order: inherit; border-top-style: dotted; border-bottom-style: dashed; box-shadow: <hwb()> 4 0 2px 0px; view-timeline-axis: block, block; border-inline-start-style: dotted; line-height-step: 1px; content-visibility: hidden; -webkit-flex-flow: ""; text-group-align: right }
+
+
+.class1, #htmlvar00003 { max-zoom: auto; cy: 39; margin-block-end: 96%; max-width: 1vmax; overscroll-behavior-y: auto; -webkit-mask-position: 24px 1px; overflow-y: scroll; -webkit-filter: drop-shadow(1px 1px 1px green); text-emphasis-color: rgb(156,115,92); box-reflect: right; mask-mode: alpha, luminance; voice-balance: left; wrap-flow: both; font-size: math; background-repeat: space space; -webkit-margin-top-collapse: collapse; border-block-style: groove solid; scrollbar-width: auto; padding: 0px 0px 0px 1px; -webkit-column-break-before: always }
+</style>
+<script>
+
+</script>
+</head>
+<body>
+
+<details id="htmlvar00003" class="class1" class="class7" open="true" class="class1" illuminance="0.3882950034014543" autofocus="autofocus" basefrequency="0.0833728589179481" optimum="0.10248315813775544" rel="noreferrer">
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-before.html b/testing/web-platform/tests/html/rendering/the-details-element/details-before.html
new file mode 100644
index 0000000000..3dd95e311a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-before.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="help" title="https://github.com/whatwg/html/pull/3686">
+<link rel="help" title="https://html.spec.whatwg.org/multipage/#the-details-element">
+<link rel="mismatch" href="single-summary.html">
+<title>CSS Test: details ::before pseudo-element</title>
+<style>
+ details::before { content: "::before" }
+</style>
+<details>
+ <summary>This is the main summary</summary>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-blockification.html b/testing/web-platform/tests/html/rendering/the-details-element/details-blockification.html
new file mode 100644
index 0000000000..960074854d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-blockification.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: details children blockification</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<meta name="assert" content="Ensure blockification of <details> children">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<div id="example1">
+ <details style="display: grid" open>
+ <summary style="display: inline">foo</summary>
+ <div style="display: inline">bar</span>
+ </details>
+</div>
+
+<div id="example2" style="display: grid">
+ <details style="display: contents" open>
+ <summary style="display: inline">foo</summary>
+ <div style="display: inline">bar</span>
+ </details>
+</div>
+
+<script>
+ function checkDetails(details) {
+ assert_equals(getComputedStyle(details.querySelector('summary')).display, "block");
+ assert_equals(getComputedStyle(details.querySelector('div')).display, "block");
+ }
+ test(() => {
+ checkDetails(document.querySelector('#example1'));
+ checkDetails(document.querySelector('#example2'));
+ assert_equals(getComputedStyle(document.querySelector('#example2>details')).display, "contents");
+ }, "Summary and content should have display:block computed value");
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored-ref.html
new file mode 100644
index 0000000000..6ebed6075d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Grogan" href="dgrogan@chromium.org">
+From <a href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">html.spec.whatwg.org</a>:
+
+<blockquote>
+The details element is expected to render as a block box. The element's shadow
+tree is expected to take the element's first summary element child, if any, and
+place it in a first block box container, and then take the element's remaining
+descendants, if any, and place them in a second block box container.
+</blockquote>
+
+&lt;details display:flex> should be ignored. Otherwise details would render as
+something other than a block box.
+<hr>
+
+<details open>
+ <summary>This is the summary.</summary>
+ <div>thing 1</div>
+ thing 2
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored.html b/testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored.html
new file mode 100644
index 0000000000..445b4e483d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-display-property-is-ignored.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Grogan" href="dgrogan@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<link rel="match" href="details-display-property-is-ignored-ref.html">
+<link rel="bookmark" href="https://bugs.chromium.org/p/chromium/issues/detail?id=635282" />
+<meta name="assert" content="The display property is ignored on details elements and is instead always rendered as a block box." />
+
+From <a href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">html.spec.whatwg.org</a>:
+
+<blockquote>
+The details element is expected to render as a block box. The element's shadow
+tree is expected to take the element's first summary element child, if any, and
+place it in a first block box container, and then take the element's remaining
+descendants, if any, and place them in a second block box container.
+</blockquote>
+
+&lt;details display:flex> should be ignored. Otherwise details would render as
+something other than a block box.
+<hr>
+
+<details open style="display:flex;">
+ <summary>This is the summary.</summary>
+ <div>thing 1</div>
+ thing 2
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref.html
new file mode 100644
index 0000000000..925ee19f77
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<title>Details and summary and display property (subgrid)</title>
+<link rel="match" href="details-display-type-001-ref2.html">
+<style>
+
+.container {
+ display: grid;
+ grid: 30px 75px 45px / 80px 125px 90px;
+}
+
+.before {
+ background: aqua;
+ grid-area: 1 / 3;
+}
+
+.after {
+ background: yellow;
+ grid-area: 3 / 1;
+}
+
+.details {
+ display: grid;
+ grid-template: subgrid / subgrid;
+ grid-area: 2 / 2 / 4 / 4;
+}
+
+.summary {
+ background: fuchsia;
+ grid-area: 1 / 1;
+}
+
+.contents {
+ background: silver;
+ grid-area: 2 / 2;
+}
+
+</style>
+
+<div class="container">
+ <div class="before">before</div>
+ <div class="details">
+ <div class="summary">summary</div>
+ <div class="contents">contents</div>
+ </div>
+ <div class="after">after</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref2.html b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref2.html
new file mode 100644
index 0000000000..4349df38d6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001-ref2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<title>Details and summary and display property (subgrid)</title>
+<style>
+
+.container {
+ display: grid;
+ grid: 30px 75px 45px / 80px 125px 90px;
+}
+
+.before {
+ background: aqua;
+ grid-area: 1 / 3;
+}
+
+.after {
+ background: yellow;
+ grid-area: 3 / 1;
+}
+
+.summary {
+ background: fuchsia;
+ grid-area: 2 / 2;
+}
+
+.contents {
+ background: silver;
+ grid-area: 3 / 3;
+}
+
+</style>
+
+<div class="container">
+ <div class="before">before</div>
+ <div class="summary">summary</div>
+ <div class="contents">contents</div>
+ <div class="after">after</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001.tentative.html b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001.tentative.html
new file mode 100644
index 0000000000..8164135518
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-001.tentative.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<title>Details and summary and display property (subgrid)</title>
+<link rel="match" href="details-display-type-001-ref.html">
+<link rel="help" href="https://github.com/dbaron/details-styling">
+<link rel="help" href="https://crbug.com/1469418">
+<style>
+
+.container {
+ display: grid;
+ grid: 30px 75px 45px / 80px 125px 90px;
+}
+
+.container::before {
+ display: block;
+ content: "before";
+ background: aqua;
+ grid-area: 1 / 3;
+}
+
+.container::after {
+ display: block;
+ content: "after";
+ background: yellow;
+ grid-area: 3 / 1;
+}
+
+details {
+ display: grid;
+ grid-template: subgrid / subgrid;
+ grid-area: 2 / 2 / 4 / 4;
+}
+
+summary {
+ display: block;
+ background: fuchsia;
+ grid-area: 1 / 1;
+}
+
+.contents {
+ background: silver;
+ grid-area: 2 / 2;
+}
+
+</style>
+
+<div class="container">
+ <details open>
+ <summary>summary</summary>
+ <div class="contents">contents</div>
+ </details>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002-ref.html
new file mode 100644
index 0000000000..297634b521
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002-ref.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<style>
+ div {
+ margin-top: 3em;
+ }
+</style>
+<details open>
+ <summary>The summary.</summary>
+ <div>Some details.</div>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002.tentative.html b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002.tentative.html
new file mode 100644
index 0000000000..716221f7c5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-display-type-002.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Details display property (flex)</title>
+<link rel="match" href="details-display-type-002-ref.html">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1856374">
+<link rel="help" href="https://github.com/dbaron/details-styling">
+<style>
+ details {
+ display: flex;
+ flex-direction: column;
+ gap: 3em;
+ }
+</style>
+<details open>
+ <summary>The summary.</summary>
+ Some details.
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-1-print.html b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-1-print.html
new file mode 100644
index 0000000000..cb7fc466d0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-1-print.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <link rel="match" href="details-two-pages-print-ref.html">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+ <style>
+ summary {
+ /* Hide the triangle for comparing with div in reftest. */
+ list-style-type: none;
+ }
+ </style>
+ <body>
+ <details open>
+ <summary style="page-break-after: always;">Summary</summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-2-print.html b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-2-print.html
new file mode 100644
index 0000000000..2f3d12bbe3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-after-2-print.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <link rel="match" href="details-two-pages-print-ref.html">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+ <style>
+ summary {
+ /* Hide the triangle for comparing with div in reftest. */
+ list-style-type: none;
+ }
+ </style>
+ <body>
+ <details open>
+ <summary>Summary<div style="page-break-before: always;"></div></summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-1-print.html b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-1-print.html
new file mode 100644
index 0000000000..5abf4c39ae
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-1-print.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <link rel="match" href="details-two-pages-print-ref.html">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+ <style>
+ summary {
+ /* Hide the triangle for comparing with div in reftest. */
+ list-style-type: none;
+ }
+ </style>
+ <body>
+ <details open>
+ <summary>Summary</summary>
+ <p style="page-break-before: always;">This is the details.</p>
+ </details>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-2-print.html b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-2-print.html
new file mode 100644
index 0000000000..2f3d12bbe3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-page-break-before-2-print.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <link rel="match" href="details-two-pages-print-ref.html">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+ <style>
+ summary {
+ /* Hide the triangle for comparing with div in reftest. */
+ list-style-type: none;
+ }
+ </style>
+ <body>
+ <details open>
+ <summary>Summary<div style="page-break-before: always;"></div></summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001-ref.html
new file mode 100644
index 0000000000..e00e6c52ff
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<title>Details pseudo-elements</title>
+<!-- This test is (at time of writing) _EXTREMELY_ tentative. -->
+<style>
+
+div { background: aqua }
+
+</style>
+
+<details open>
+ <summary>The summary</summary>
+ <div>The details!</div>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001.tentative.html b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001.tentative.html
new file mode 100644
index 0000000000..1a9f7d6a02
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-001.tentative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<title>Details pseudo-elements</title>
+<!-- This test is (at time of writing) _EXTREMELY_ tentative. -->
+<link rel="match" href="details-pseudo-elements-001-ref.html">
+<link rel="help" href="https://github.com/dbaron/details-styling">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1469418">
+<style>
+
+details::details-content {
+ background: aqua;
+ display: block; /* override display: contents for slot */
+}
+
+</style>
+
+<details open>
+ <summary>The summary</summary>
+ <div>The details!</div>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002-ref.html
new file mode 100644
index 0000000000..baf242291b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<title>Details pseudo-elements</title>
+<!-- This test is (at time of writing) _EXTREMELY_ tentative. -->
+<style>
+
+summary { list-style-type: disclosure-closed; }
+div { background: aqua }
+
+</style>
+
+<details open>
+ <summary>The summary</summary>
+ <div>The details!</div>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002.tentative.html b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002.tentative.html
new file mode 100644
index 0000000000..e149395665
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-pseudo-elements-002.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<title>Details pseudo-elements</title>
+<!-- This test is (at time of writing) _EXTREMELY_ tentative. -->
+<link rel="match" href="details-pseudo-elements-002-ref.html">
+<link rel="help" href="https://github.com/dbaron/details-styling">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1469418">
+<meta name="assert" content="Checks that the content-visibility property on the pseudo-element overrides the style attribute inside the shadow dom.">
+<style>
+
+details::details-content {
+ background: aqua;
+ display: block; /* override display: contents for slot */
+ content-visibility: visible;
+}
+
+</style>
+
+<details>
+ <summary>The summary</summary>
+ <div>The details!</div>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-revert-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/details-revert-ref.html
new file mode 100644
index 0000000000..dc46b15901
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-revert-ref.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test Reference</title>
+<style>
+ summary {
+ display: list-item;
+ counter-increment: list-item 0;
+ list-style: disclosure-closed inside;
+ }
+ details[open] > summary {
+ list-style-type: disclosure-open;
+ }
+</style>
+<details>
+ <summary>Example</summary>
+</details>
+<details open>
+ <summary>Example</summary>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-revert.html b/testing/web-platform/tests/html/rendering/the-details-element/details-revert.html
new file mode 100644
index 0000000000..760cc8281e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-revert.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#the-details-and-summary-elements">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1804925">
+<link rel="match" href="details-revert-ref.html">
+<style>
+ summary {
+ display: revert;
+ counter-increment: revert;
+ list-style: revert;
+ }
+</style>
+<details>
+ <summary>Example</summary>
+</details>
+<details open>
+ <summary>Example</summary>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/details-two-pages-print-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/details-two-pages-print-ref.html
new file mode 100644
index 0000000000..309a138696
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/details-two-pages-print-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <div>
+ <div style="page-break-after: always;">Summary</div>
+ <p>This is the details.</p>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/empty-crash.html b/testing/web-platform/tests/html/rendering/the-details-element/empty-crash.html
new file mode 100644
index 0000000000..d409eff4a8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/empty-crash.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1068227">
+<style>
+body { display: flex; }
+details, summary { display: inherit; }
+</style>
+<details><summary>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/resources/auto-expand-details-text-fragment.html b/testing/web-platform/tests/html/rendering/the-details-element/resources/auto-expand-details-text-fragment.html
new file mode 100644
index 0000000000..b95459fe5c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/resources/auto-expand-details-text-fragment.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<!-- This test is navigated to with the fragment #:~:text=foo -->
+
+<body>
+ <div style="height: 4000px;">spacer</div>
+ <details>
+ <div>foo</div>
+ </details>
+ <script>
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ const results = {};
+ // This should be true. The details element should be opened by
+ // ScrollToTextFragment because it has matching text.
+ results.detailsHasOpenAttribute = document.querySelector('details').hasAttribute('open');
+ // This should be greater than zero. The page should be scrolled down
+ // to the matching target.
+ results.pageYOffsetAfterRaf = window.pageYOffset;
+
+ params = new URLSearchParams(window.location.search);
+ stashResultsThenClose(params.get('key'), results);
+ });
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/single-summary.html b/testing/web-platform/tests/html/rendering/the-details-element/single-summary.html
new file mode 100644
index 0000000000..1f09e7e75f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/single-summary.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<title>CSS Test Reference</title>
+<details>
+ <summary>This is the main summary</summary>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex-ref.html
new file mode 100644
index 0000000000..083dba8795
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex-ref.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<style>
+.flex-container {
+ background: #333;
+ border: 0px;
+ display: flex;
+ margin: 0px;
+ padding: 0px;
+}
+
+.flex-container.flex-direction-row {
+ flex-direction : row;
+}
+
+.flex-container.flex-direction-row-reverse {
+ flex-direction : row-reverse;
+}
+
+.flex-container.flex-direction-column {
+ flex-direction : column;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.justify-content-center {
+ justify-content: center;
+}
+
+.flex-container.justify-content-space-around {
+ justify-content: space-around;
+}
+
+.flex-container.justify-content-space-between {
+ justify-content: space-between;
+}
+
+.flex-item {
+ width:50px;
+ height:50px;
+ margin:20px;
+ background: #eee;
+ line-height: 50px;
+ text-align: center;
+}
+</style>
+
+<summary>
+ <div>these fieldsshouldn't bestacked vertically</div>
+</summary>
+
+<h1>flex-direction: row</h1>
+<div class="flex-container flex-direction-row">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>flex-direction: row-reverse</h1>
+<div class="flex-container flex-direction-row-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>flex-direction: column</h1>
+<div class="flex-container flex-direction-column">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>flex-direction: column-reverse</h1>
+<div class="flex-container flex-direction-column-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>justify-content: center</h1>
+<div class="flex-container justify-content-center">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>justify-content: space-around</h1>
+<div class="flex-container justify-content-space-around">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>justify-content: space-between</h1>
+<div class="flex-container justify-content-space-between">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex.html
new file mode 100644
index 0000000000..c495516cf3
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-flex.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: summary with 'display: flex'</title>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<link rel="match" href="summary-display-flex-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<meta name="assert" content="Checks that styling a <summary> with 'display: flex' makes it a flex container.">
+<style>
+.flex-container {
+ background: #333;
+ border: 0px;
+ display: flex;
+ margin: 0px;
+ padding: 0px;
+}
+
+.flex-container.flex-direction-row {
+ flex-direction : row;
+}
+
+.flex-container.flex-direction-row-reverse {
+ flex-direction : row-reverse;
+}
+
+.flex-container.flex-direction-column {
+ flex-direction : column;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.justify-content-center {
+ justify-content: center;
+}
+
+.flex-container.justify-content-space-around {
+ justify-content: space-around;
+}
+
+.flex-container.justify-content-space-between {
+ justify-content: space-between;
+}
+
+.flex-item {
+ width:50px;
+ height:50px;
+ margin:20px;
+ background: #eee;
+ line-height: 50px;
+ text-align: center;
+}
+</style>
+
+<summary style="display: flex;">
+ <div>these fields</div>
+ <div>shouldn't be</div>
+ <div>stacked vertically</div>
+</summary>
+
+<h1>flex-direction: row</h1>
+<summary class="flex-container flex-direction-row">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>flex-direction: row-reverse</h1>
+<summary class="flex-container flex-direction-row-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>flex-direction: column</h1>
+<summary class="flex-container flex-direction-column">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>flex-direction: column-reverse</h1>
+<summary class="flex-container flex-direction-column-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>justify-content: center</h1>
+<summary class="flex-container justify-content-center">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>justify-content: space-around</h1>
+<summary class="flex-container justify-content-space-around">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>justify-content: space-between</h1>
+<summary class="flex-container justify-content-space-between">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid-ref.html
new file mode 100644
index 0000000000..a7c4c4c014
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid-ref.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<style>
+.grid-container {
+ display: grid;
+ grid-template-columns: 200px 10px 0.3fr 10px 0.7fr;
+ grid-template-rows: auto 20px auto;
+}
+
+.grid-element {
+ background-color: #444;
+ color: #fff;
+ padding: 20px;
+ font-size: 2em;
+}
+
+.element-a {
+ grid-column-start: 1;
+ grid-column-end: ;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #6F9;
+}
+
+.element-b {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #69F;
+}
+
+.element-c {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #F69;
+}
+
+.element-d {
+ grid-column-start: 1;
+ grid-column-end: 2;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #9F6;
+}
+
+.element-e {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #96F;
+}
+
+.element-f {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #F96;
+}
+</style>
+<div class="grid-container">
+ <div class="grid-element element-a">A</div>
+ <div class="grid-element element-b">B</div>
+ <div class="grid-element element-c">C</div>
+ <div class="grid-element element-d">D</div>
+ <div class="grid-element element-e">E</div>
+ <div class="grid-element element-f">F</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid.html
new file mode 100644
index 0000000000..934b4ff595
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-grid.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: summary with 'display: grid'</title>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<link rel="match" href="summary-display-grid-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<meta name="assert" content="Checks that styling a <summary> with 'display: grid' makes it a grid container.">
+<style>
+.grid-container {
+ display: grid;
+ grid-template-columns: 200px 10px 0.3fr 10px 0.7fr;
+ grid-template-rows: auto 20px auto;
+}
+
+.grid-element {
+ background-color: #444;
+ color: #fff;
+ padding: 20px;
+ font-size: 2em;
+}
+
+.element-a {
+ grid-column-start: 1;
+ grid-column-end: ;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #6F9;
+}
+
+.element-b {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #69F;
+}
+
+.element-c {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #F69;
+}
+
+.element-d {
+ grid-column-start: 1;
+ grid-column-end: 2;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #9F6;
+}
+
+.element-e {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #96F;
+}
+
+.element-f {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #F96;
+}
+</style>
+<summary class="grid-container">
+ <div class="grid-element element-a">A</div>
+ <div class="grid-element element-b">B</div>
+ <div class="grid-element element-c">C</div>
+ <div class="grid-element element-d">D</div>
+ <div class="grid-element element-e">E</div>
+ <div class="grid-element element-f">F</div>
+</summary>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex-ref.html
new file mode 100644
index 0000000000..25a9b315f4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex-ref.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<style>
+.flex-container {
+ background: #333;
+ border: 0px;
+ display: inline-flex;
+ margin: 0px;
+ padding: 0px;
+}
+
+.flex-container.flex-direction-row {
+ flex-direction : row;
+}
+
+.flex-container.flex-direction-row-reverse {
+ flex-direction : row-reverse;
+}
+
+.flex-container.flex-direction-column {
+ flex-direction : column;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.justify-content-center {
+ justify-content: center;
+}
+
+.flex-container.justify-content-space-around {
+ justify-content: space-around;
+}
+
+.flex-container.justify-content-space-between {
+ justify-content: space-between;
+}
+
+.flex-item {
+ width:50px;
+ height:50px;
+ margin:20px;
+ background: #eee;
+ line-height: 50px;
+ text-align: center;
+}
+</style>
+
+<summary>
+ <div>these fieldsshouldn't bestacked vertically</div>
+</summary>
+
+<h1>flex-direction: row</h1>
+<div class="flex-container flex-direction-row">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>flex-direction: row-reverse</h1>
+<div class="flex-container flex-direction-row-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>flex-direction: column</h1>
+<div class="flex-container flex-direction-column">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>flex-direction: column-reverse</h1>
+<div class="flex-container flex-direction-column-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>justify-content: center</h1>
+<div class="flex-container justify-content-center">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>justify-content: space-around</h1>
+<div class="flex-container justify-content-space-around">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
+
+<h1>justify-content: space-between</h1>
+<div class="flex-container justify-content-space-between">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex.html
new file mode 100644
index 0000000000..2c935e42b8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-flex.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: summary with 'display: inline-flex'</title>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<link rel="match" href="summary-display-inline-flex-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<meta name="assert" content="Checks that styling a <summary> with 'display: inline-flex' makes it a flex container.">
+<style>
+.flex-container {
+ background: #333;
+ border: 0px;
+ display: inline-flex;
+ margin: 0px;
+ padding: 0px;
+}
+
+.flex-container.flex-direction-row {
+ flex-direction : row;
+}
+
+.flex-container.flex-direction-row-reverse {
+ flex-direction : row-reverse;
+}
+
+.flex-container.flex-direction-column {
+ flex-direction : column;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.flex-direction-column-reverse {
+ flex-direction : column-reverse;
+}
+
+.flex-container.justify-content-center {
+ justify-content: center;
+}
+
+.flex-container.justify-content-space-around {
+ justify-content: space-around;
+}
+
+.flex-container.justify-content-space-between {
+ justify-content: space-between;
+}
+
+.flex-item {
+ width:50px;
+ height:50px;
+ margin:20px;
+ background: #eee;
+ line-height: 50px;
+ text-align: center;
+}
+</style>
+
+<summary style="display: inline-flex;">
+ <div>these fields</div>
+ <div>shouldn't be</div>
+ <div>stacked vertically</div>
+</summary>
+
+<h1>flex-direction: row</h1>
+<summary class="flex-container flex-direction-row">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>flex-direction: row-reverse</h1>
+<summary class="flex-container flex-direction-row-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>flex-direction: column</h1>
+<summary class="flex-container flex-direction-column">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>flex-direction: column-reverse</h1>
+<summary class="flex-container flex-direction-column-reverse">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>justify-content: center</h1>
+<summary class="flex-container justify-content-center">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>justify-content: space-around</h1>
+<summary class="flex-container justify-content-space-around">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
+
+<h1>justify-content: space-between</h1>
+<summary class="flex-container justify-content-space-between">
+ <div class="flex-item">1</div>
+ <div class="flex-item">2</div>
+ <div class="flex-item">3</div>
+</summary>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid-ref.html
new file mode 100644
index 0000000000..f6a8b04bec
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid-ref.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<style>
+.grid-container {
+ display: inline-grid;
+ grid-template-columns: 200px 10px 0.3fr 10px 0.7fr;
+ grid-template-rows: auto 20px auto;
+}
+
+.grid-element {
+ background-color: #444;
+ color: #fff;
+ padding: 20px;
+ font-size: 2em;
+}
+
+.element-a {
+ grid-column-start: 1;
+ grid-column-end: ;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #6F9;
+}
+
+.element-b {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #69F;
+}
+
+.element-c {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #F69;
+}
+
+.element-d {
+ grid-column-start: 1;
+ grid-column-end: 2;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #9F6;
+}
+
+.element-e {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #96F;
+}
+
+.element-f {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #F96;
+}
+</style>
+<div class="grid-container">
+ <div class="grid-element element-a">A</div>
+ <div class="grid-element element-b">B</div>
+ <div class="grid-element element-c">C</div>
+ <div class="grid-element element-d">D</div>
+ <div class="grid-element element-e">E</div>
+ <div class="grid-element element-f">F</div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid.html
new file mode 100644
index 0000000000..3578f050e2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-inline-grid.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: summary with 'display: inline-grid'</title>
+<link rel="author" title="Xing Xu" href="mailto:xing.xu@intel.com">
+<link rel="match" href="summary-display-inline-grid-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<meta name="assert" content="Checks that styling a <summary> with 'display: inline-grid' makes it a grid container.">
+<style>
+.grid-container {
+ display: inline-grid;
+ grid-template-columns: 200px 10px 0.3fr 10px 0.7fr;
+ grid-template-rows: auto 20px auto;
+}
+
+.grid-element {
+ background-color: #444;
+ color: #fff;
+ padding: 20px;
+ font-size: 2em;
+}
+
+.element-a {
+ grid-column-start: 1;
+ grid-column-end: ;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #6F9;
+}
+
+.element-b {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #69F;
+}
+
+.element-c {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 1;
+ grid-row-end: 2;
+ background: #F69;
+}
+
+.element-d {
+ grid-column-start: 1;
+ grid-column-end: 2;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #9F6;
+}
+
+.element-e {
+ grid-column-start: 3;
+ grid-column-end: 4;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #96F;
+}
+
+.element-f {
+ grid-column-start: 5;
+ grid-column-end: 6;
+ grid-row-start: 3;
+ grid-row-end: 4;
+ background: #F96;
+}
+</style>
+<summary class="grid-container">
+ <div class="grid-element element-a">A</div>
+ <div class="grid-element element-b">B</div>
+ <div class="grid-element element-c">C</div>
+ <div class="grid-element element-d">D</div>
+ <div class="grid-element element-e">E</div>
+ <div class="grid-element element-f">F</div>
+</summary>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001-ref.html
new file mode 100644
index 0000000000..31e98d3fdc
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference: summary with 'display: list-item'</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<style>
+details {
+ margin-left: 50px;
+}
+.inside {
+ list-style-position: inside;
+}
+</style>
+<details>
+ <summary>summary</summary>
+ content
+</details>
+<details>
+ <summary class="inside">summary</summary>
+ content
+</details>
+<details open>
+ <summary>summary</summary>
+ content
+</details>
+<details open>
+ <summary class="inside">summary</summary>
+ content
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001.html
new file mode 100644
index 0000000000..8b94f10f27
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-001.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: summary with 'display: list-item'</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="match" href="summary-display-list-item-001-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<meta name="assert" content="Checks that styling a <summary> with 'display: list-item' has no effect since it should already be a list item by default.">
+<style>
+summary {
+ display: list-item;
+}
+details {
+ margin-left: 50px;
+}
+.inside {
+ list-style-position: inside;
+}
+</style>
+<details>
+ <summary>summary</summary>
+ content
+</details>
+<details>
+ <summary class="inside">summary</summary>
+ content
+</details>
+<details open>
+ <summary>summary</summary>
+ content
+</details>
+<details open>
+ <summary class="inside">summary</summary>
+ content
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002-ref.html
new file mode 100644
index 0000000000..317e1cfe7d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: summary with 'display: list-item' and flex children</title>
+<link rel="author" title="Cameron McCormack" href="mailto:">
+<link rel="author" title="Sergio Villar Senin" href="mailto:svillar@igalia.com">
+<div style="width: 100px;">
+ <div style="display: list-item; list-style-type: none;">
+ <div style="display: flex; justify-content: space-between;">
+ <div>AAA</div>
+ <div>BBB</div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002.html
new file mode 100644
index 0000000000..ee32997009
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-display-list-item-002.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: summary with 'display: list-item' and flex children</title>
+<link rel="author" title="Cameron McCormack" href="mailto:heycam@apple.com">
+<link rel="author" title="Sergio Villar Senin" href="mailto:svillar@igalia.com">
+<link rel="match" href="summary-display-list-item-002-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<style>
+summary {
+ display: list-item;
+ list-style-type: none;
+}
+/* WebKit does not support list-style-type:none yet */
+summary::-webkit-details-marker { display: none; }
+</style>
+<details style="width: 100px;">
+ <summary>
+ <div style="display: flex; justify-content: space-between;">
+ <div>AAA</div>
+ <div>BBB</div>
+ </div>
+ </summary>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol-ref.html
new file mode 100644
index 0000000000..bb6f79b627
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+
+<ol>
+ <details><summary>summary</summary></details>
+ <li value="1">1</li>
+ <li value="2">2 <details><summary>summary</summary></details></li>
+ <li value="3">3</li>
+ <details><summary>summary</summary></details>
+ <li value="4">4</li>
+</ol>
+
+<ol>
+ <summary>summary</summary>
+ <li value="1">1</li>
+ <li value="2">2 <summary>summary</summary></li>
+ <li value="3">3</li>
+ <summary>summary</summary>
+ <li value="4">4</li>
+</ol>
+
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol.html
new file mode 100644
index 0000000000..0f380bf058
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-in-ol.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Check if SUMMARY has 'counter-increment: list-item 0'</title>
+<link rel="match" href="summary-in-ol-ref.html">
+
+<ol>
+ <details><summary>summary</summary></details>
+ <li>1</li>
+ <li>2 <details><summary>summary</summary></details></li>
+ <li>3</li>
+ <details><summary>summary</summary></details>
+ <li>4</li>
+</ol>
+
+<ol>
+ <summary>summary</summary>
+ <li>1</li>
+ <li>2 <summary>summary</summary></li>
+ <li>3</li>
+ <summary>summary</summary>
+ <li>4</li>
+</ol>
+
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration-ref.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration-ref.html
new file mode 100644
index 0000000000..ffdcfd25fb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration-ref.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Test reference</title>
+<style>
+div {
+ display: list-item;
+ list-style-type: disclosure-closed;
+ list-style-position: inside;
+ text-decoration: underline;
+}
+</style>
+<div>This text should be underlined.</div>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration.html b/testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration.html
new file mode 100644
index 0000000000..80bbd44c46
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/summary-text-decoration.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Rendering of summary element with text-decoration</title>
+<link rel="match" href="summary-text-decoration-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
+<meta name="assert" content="Checks that text-decoration applies to rendered summary element.">
+<style>
+ summary { text-decoration: underline; }
+</style>
+<details>
+ <summary>This text should be underlined.</summary>
+</details>
diff --git a/testing/web-platform/tests/html/rendering/the-details-element/two-summaries-removal-crash.html b/testing/web-platform/tests/html/rendering/the-details-element/two-summaries-removal-crash.html
new file mode 100644
index 0000000000..164d3f16aa
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/the-details-element/two-summaries-removal-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://crbug.com/1216804">
+<meta name="assert" content="The renderer should not crash.">
+
+<details id="details" open>
+ <summary></summary>
+ <summary>
+ <details></details>
+ </summary>
+</details>
+
+<script>
+details.appendChild(document.createElement("frame"));
+details.innerText="";
+</script>
diff --git a/testing/web-platform/tests/html/rendering/unmapped-attributes.html b/testing/web-platform/tests/html/rendering/unmapped-attributes.html
new file mode 100644
index 0000000000..5824f836f0
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/unmapped-attributes.html
@@ -0,0 +1,95 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test handling of attributes that should not be mapped into style, but
+ incorrectly were in some browsers</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<div id="container" style="display: none"></div>
+<iframe></iframe>
+<script>
+ /*
+ * We wand to test both quirks and standards mode. We can use the fact that
+ * our document is in standards mode and the about:blank iframe we have is in
+ * quirks mode.
+ */
+test(() => {
+ assert_equals(document.compatMode, "CSS1Compat")
+}, "We should be in standards mode");
+const container = document.getElementById("container");
+
+const frameDoc = document.querySelector("iframe").contentDocument;
+test(() => {
+ assert_equals(frameDoc.compatMode, "BackCompat")
+}, "Subframe should be in quirks mode");
+const frameContainer = frameDoc.createElement("div");
+frameContainer.style.display = "none";
+frameDoc.body.appendChild(frameContainer);
+
+function newElem(name) {
+ return (parent) =>
+ parent.appendChild(parent.ownerDocument.createElement(name));
+}
+
+ /*
+ * Array of tests. Each test consists of the following information:
+ *
+ * 1) An element creation function, which takes a parent element as an
+ * argument.
+ * 2) The name of the attribute to set
+ * 3) The name of the CSS property to get.
+ */
+const tests = [
+ [ newElem("table"), "hspace", "marginLeft" ],
+ [ newElem("table"), "hspace", "marginRight" ],
+ [ newElem("table"), "vspace", "marginTop" ],
+ [ newElem("table"), "vspace", "marginBottom" ],
+ [ newElem("embed"), "border", "borderTopWidth" ],
+ [ newElem("embed"), "border", "borderRightWidth" ],
+ [ newElem("embed"), "border", "borderBottomWidth" ],
+ [ newElem("embed"), "border", "borderLeftWidth" ],
+ [ newElem("iframe"), "border", "borderTopWidth" ],
+ [ newElem("iframe"), "border", "borderRightWidth" ],
+ [ newElem("iframe"), "border", "borderBottomWidth" ],
+ [ newElem("iframe"), "border", "borderLeftWidth" ],
+ [ newElem("iframe"), "hspace", "marginLeft" ],
+ [ newElem("iframe"), "hspace", "marginRight" ],
+ [ newElem("iframe"), "vspace", "marginTop" ],
+ [ newElem("iframe"), "vspace", "marginBottom" ],
+ [ newElem("marquee"), "border", "borderTopWidth" ],
+ [ newElem("marquee"), "border", "borderRightWidth" ],
+ [ newElem("marquee"), "border", "borderBottomWidth" ],
+ [ newElem("marquee"), "border", "borderLeftWidth" ],
+ // Non-image input
+ [ newElem("input"), "border", "borderTopWidth" ],
+ [ newElem("input"), "border", "borderRightWidth" ],
+ [ newElem("input"), "border", "borderBottomWidth" ],
+ [ newElem("input"), "border", "borderLeftWidth" ],
+ [ newElem("input"), "width", "width" ],
+ [ newElem("input"), "height", "height" ],
+ [ newElem("input"), "hspace", "marginLeft" ],
+ [ newElem("input"), "hspace", "marginRight" ],
+ [ newElem("input"), "vspace", "marginTop" ],
+ [ newElem("input"), "vspace", "marginBottom" ],
+];
+
+function style(element) {
+ return element.ownerDocument.defaultView.getComputedStyle(element);
+}
+
+for (let [ctor, attr, prop] of tests) {
+ for (let parent of [container, frameContainer]) {
+ let elem = ctor(parent);
+ test(function() {
+ let default_elem = ctor(parent);
+ this.add_cleanup(() => {
+ elem.remove();
+ default_elem.remove();
+ });
+ elem.setAttribute(attr, "200");
+ assert_equals(elem.getAttribute(attr), "200");
+ assert_equals(style(elem)[prop], style(default_elem)[prop]);
+ }, `<${elem.localName} ${attr}> should not be mapped to style ${prop} in ${parent.ownerDocument.compatMode} mode`);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-001.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-001.html
new file mode 100644
index 0000000000..7d7474e0a4
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-001.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>compute the kind of widget: author origin and animation origin</title>
+<link rel="match" href="appearance-transition-001-ref.html">
+<style>
+ input {
+ background-color: rgb(0, 0, 0);
+ }
+ .bg200 {
+ animation: 1e10s steps(2, start) animate-bg;
+ }
+ @keyframes animate-bg {
+ to {
+ background-color: rgb(0, 200, 0);
+ }
+ }
+</style>
+<input value="text" id=input>
+<script>
+ document.documentElement.offsetTop;
+ input.classList.toggle('bg200');
+</script>
+<p>PASS if the input field has a dark green background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002-ref.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002-ref.html
new file mode 100644
index 0000000000..fab70234d2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<input value="text">
+<p>PASS if the input field does not have a red background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002.html
new file mode 100644
index 0000000000..acca93eae7
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-animation-002.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>compute the kind of widget: animation origin only</title>
+<link rel="match" href="appearance-animation-002-ref.html">
+<style>
+ .bg200 {
+ animation: 1e10s steps(2, start) animate-bg;
+ }
+ @keyframes animate-bg {
+ to {
+ background-color: rgb(255, 0, 0);
+ }
+ }
+</style>
+<input value="text" id=input>
+<script>
+ document.documentElement.offsetTop;
+ input.classList.toggle('bg200');
+</script>
+<p>PASS if the input field does not have a red background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001-ref.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001-ref.html
new file mode 100644
index 0000000000..69726c6cd2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+ input { background-color: rgb(0, 100, 0); }
+</style>
+<input value="text">
+<p>PASS if the input field has a dark green background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001.html
new file mode 100644
index 0000000000..ec51cf9be5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-001.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>compute the kind of widget: author origin and transition origin</title>
+<link rel="help" href="https://crbug.com/1086732">
+<link rel="match" href="appearance-transition-001-ref.html">
+<style>
+ input {
+ background-color: rgb(0, 0, 0);
+ transition: background-color 1e10s steps(2, start);
+ }
+
+ .bg200 {
+ background-color: rgb(0, 200, 0);
+ }
+</style>
+<input value="text" id=input>
+<script>
+ document.documentElement.offsetTop;
+ input.classList.toggle('bg200');
+</script>
+<p>PASS if the input field has a dark green background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002-ref.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002-ref.html
new file mode 100644
index 0000000000..febd6c55aa
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<style>
+ input { background-color: rgb(128, 228, 128); }
+</style>
+<input value="text">
+<p>PASS if the input field has a light green background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002.html
new file mode 100644
index 0000000000..b6c2a3dcad
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-002.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>compute the kind of widget: transition origin without author origin style before the transition</title>
+<link rel="match" href="appearance-transition-002-ref.html">
+<style>
+ input {
+ transition: background-color 1e10s steps(2, start);
+ }
+
+ .bg200 {
+ background-color: rgb(0, 200, 0);
+ }
+</style>
+<input value="text" id=input>
+<script>
+ document.documentElement.offsetTop;
+ input.classList.toggle('bg200');
+</script>
+<p>PASS if the input field has a light green background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-003.html b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-003.html
new file mode 100644
index 0000000000..109617b7b7
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/appearance-transition-003.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>compute the kind of widget: transition origin without author origin style after the transition</title>
+<link rel="match" href="appearance-animation-002-ref.html">
+<style>
+ input {
+ transition: background-color 1e10s steps(2, start);
+ }
+
+ .bg200 {
+ background-color: rgb(255, 0, 0);
+ }
+</style>
+<input value="text" id=input class=bg200>
+<script>
+ document.documentElement.offsetTop;
+ input.classList.toggle('bg200');
+</script>
+<p>PASS if the input field does not have a red background</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/appearance/default-styles.html b/testing/web-platform/tests/html/rendering/widgets/appearance/default-styles.html
new file mode 100644
index 0000000000..8869808696
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/appearance/default-styles.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<title>HTML: default style for 'appearance'</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// namespaces
+const htmlns = 'http://www.w3.org/1999/xhtml';
+const svgns = 'http://www.w3.org/2000/svg';
+
+// auto
+testAppearance(htmlns, 'input', null, 'auto');
+testAppearance(htmlns, 'input', {type: 'text'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'TEXT'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'search'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'tel'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'url'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'email'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'password'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'date'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'month'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'week'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'time'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'datetime-local'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'number'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'range'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'color'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'checkbox'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'radio'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'submit'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'reset'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'button'}, 'auto');
+testAppearance(htmlns, 'input', {type: 'unknowntype'}, 'auto');
+testAppearance(htmlns, 'select', null, 'auto');
+testAppearance(htmlns, 'select', {multiple: ''}, 'auto');
+testAppearance(htmlns, 'select', {size: '2'}, 'auto');
+testAppearance(htmlns, 'button', null, 'auto');
+testAppearance(htmlns, 'textarea', null, 'auto');
+testAppearance(htmlns, 'meter', null, 'auto');
+testAppearance(htmlns, 'progress', null, 'auto');
+
+// none
+testAppearance(htmlns, 'input', {type: 'hidden'}, 'none');
+testAppearance(htmlns, 'input', {type: 'HIDDEN'}, 'none');
+testAppearance(htmlns, 'input', {type: 'file'}, 'none');
+testAppearance(htmlns, 'input', {type: 'image'}, 'none');
+testAppearance(htmlns, 'div', null, 'none');
+testAppearance(htmlns, 'details', null, 'none');
+testAppearance(htmlns, 'summary', null, 'none');
+testAppearance(htmlns, 'video', null, 'none');
+testAppearance(htmlns, 'video', {controls: ''}, 'none');
+testAppearance(htmlns, 'menuitem', null, 'none');
+testAppearance(htmlns, 'marquee', null, 'none');
+testAppearance(htmlns, 'keygen', null, 'none');
+testAppearance(null, 'input', null, 'none');
+testAppearance(svgns, 'input', null, 'none');
+
+test(t => {
+ assertAppearance(document.documentElement, 'none');
+}, 'The html element');
+
+test(t => {
+ assertAppearance(document.body, 'none');
+}, 'The body element');
+
+
+function testAppearance(ns, tag, attributes, expected) {
+ test(t => {
+ const elm = document.createElementNS(ns, tag);
+ for (const att in attributes) {
+ elm.setAttribute(att, attributes[att]);
+ }
+ document.body.appendChild(elm);
+ t.add_cleanup(() => elm.remove());
+ assertAppearance(elm, expected);
+ }, formatTestName(ns, tag, attributes));
+}
+
+function assertAppearance(elm, expected) {
+ const computedStyle = getComputedStyle(elm);
+ assert_equals(computedStyle.getPropertyValue('-webkit-appearance'), expected, '-webkit-appearance');
+ assert_equals(computedStyle.getPropertyValue('appearance'), expected, 'appearance (no prefix)');
+}
+
+function formatTestName(ns, tag, attributes) {
+ let s = `<${tag}`;
+ for (const att in attributes) {
+ s += ` ${att}="${attributes[att]}"`;
+ }
+ s += '>';
+ if (ns !== htmlns) {
+ s += ` (namespace: ${ns})`;
+ }
+ return s;
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/baseline-alignment-and-overflow.tentative.html b/testing/web-platform/tests/html/rendering/widgets/baseline-alignment-and-overflow.tentative.html
new file mode 100644
index 0000000000..45ac762bd1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/baseline-alignment-and-overflow.tentative.html
@@ -0,0 +1,237 @@
+<!DOCTYPE html>
+<title>HTML: widgets' baseline alignment and interaction with 'overflow'</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!--
+This test tests where the baseline is for different widgets.
+This isn't yet specified, see https://github.com/whatwg/html/issues/5065
+
+Where the baseline is affects where *other* things on the same linebox are positioned. So this test
+makes assertions about the offsetTop for a previous sibling <span>, and compares it to a reference
+that has equivalent setup but using a styled <span> instead of an actual widget (where possible) so
+we can control where its baseline will be (assuming a correct CSS implementation).
+
+CSS has the following behavior regarding baselines (this is non-normative, also read the spec):
+
+* for a normal inline containing text, the baseline is the same as the text's baseline.
+* for replaced elements (like images), the baseline is at the bottom margin-box edge.
+* for an inline-block, it depends on its 'overflow':
+ - 'visible': the baseline is the same as the baseline for the last line box in the inline-block.
+ - otherwise: the baseline is like replaced elements.
+
+The baselines for different widgets, as implemented in Firefox, are as follows:
+
+<input type=text>
+<input type=search>
+<input type=tel>
+<input type=email>
+<input type=password>
+<input type=date>
+<input type=month>
+<input type=week>
+<input type=time>
+<input type=datetime-local>
+<input type=number>
+<input type=submit>
+<input type=reset>
+<input type=button>
+ Like inline-block but 'overflow' is forced to 'visible' and text inside is vertically centered.
+
+<input type=checkbox> with 'appearance: auto'
+<input type=radio> with 'appearance: auto'
+ At the bottom of the content-box edge (and whether there's a border depends
+ on the platform).
+
+ This is not usually how CSS works so we fake it with a negative margin-bottom.
+
+<input type=color>
+ At the content-box edge.
+
+<input type=file>
+ Like inline-block but 'overflow' is forced to to 'visible' and it contains a button; the button
+ can affect where the baseline is.
+
+<input type=image> showing alt text, with 'overflow: visible'
+ Like inline-block.
+
+<input type=image> showing an image
+<input type=image> showing alt text, with 'overflow' something other than 'visible'
+<input type=range>
+<input type=checkbox> with 'appearance: none'
+<input type=radio> with 'appearance: none'
+ Like replaced elements.
+
+-->
+<style>
+.test {
+ border-collapse: collapse;
+ font-size: 10px;
+}
+.test td {
+ border: none;
+ padding: 0;
+ margin:0;
+ outline: 1px solid silver;
+}
+
+.test td > input,
+.ref td > .fake-input-text,
+.ref td > .inline-block,
+.ref td > img,
+.ref td > button {
+ font: inherit;
+ height: 60px;
+ width: 60px;
+ padding: 10px;
+ box-sizing: border-box;
+ margin: 10px 0;
+ /* Note: a border is not specified because that would imply 'appearance: none' for some widgets */
+}
+
+.ref button img {
+ height: 100%;
+ width: 100%;
+ display: block;
+}
+
+/* Use inline-grid instead of inline-block here to more easily center the text inside */
+.ref .fake-input-text {
+ display: inline-grid;
+ border: 2px solid; /* 2px matches UA default style */
+ align-items: center;
+}
+.ref .inline-block {
+ display: inline-block;
+}
+
+.ref-file-input-like button {
+ font-size: unset;
+}
+
+[style*="appearance: none;"] {
+ -webkit-appearance: none; /* TODO(zcorpan) remove this when unprefixed appearance is supported */
+}
+</style>
+<div id="log"></div>
+<h2>refs</h2>
+<!--
+The first span's offsetTop is what we want to compare with the corresponding test's span's offsetTop.
+The sibling element is there to control where the baseline for the line box will be.
+-->
+<table class="test ref">
+ <tr class="ref-text-input-like"><td><span>ref-text-input-like</span> <span class=fake-input-text>x</span>
+ <tr class="ref-checkbox-input-appearance-auto-like"><td><span>ref-checkbox-input-appearance-auto-like</span> <img class=auto-checkbox src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAIAAAD3UuoiAAAAGklEQVQoz2Nk%2BP%2BfgRqAiYFKYNSgUYOGp0EA%2BQMCFrJdTgsAAAAASUVORK5CYII%3D">
+ <tr class="ref-color-input-like"><td><span>ref-color-input-like</span> <button><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAIAAAD3UuoiAAAAGklEQVQoz2Nk%2BP%2BfgRqAiYFKYNSgUYOGp0EA%2BQMCFrJdTgsAAAAASUVORK5CYII%3D"></button>
+ <tr class="ref-file-input-like"><td><span>ref-file-input-like</span> <span class=inline-block><button>x</button></span>
+ <tr class="ref-image-input-showing-alt-overflow-visible-like"><td><span>ref-image-input-showing-alt-overflow-visible-like</span> <span class=inline-block>x</span>
+ <tr class="ref-image-input-showing-image-like"><td><span>ref-image-input-showing-image-like</span> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAIAAAD3UuoiAAAAGklEQVQoz2Nk%2BP%2BfgRqAiYFKYNSgUYOGp0EA%2BQMCFrJdTgsAAAAASUVORK5CYII%3D">
+</table>
+<h2>template table</h2>
+<!--
+Each row in this table will be cloned into #test-table for each combination of styles to test.
+-->
+<table id="template-table">
+ <tr><td><span>text</span> <input type=text value=x></td></tr>
+ <tr><td><span>search</span> <input type=search value=x></td></tr>
+ <tr><td><span>tel</span> <input type=tel value=x></td></tr>
+ <tr><td><span>url</span> <input type=url value="data:,x"></td></tr>
+ <tr><td><span>email</span> <input type=email value=x></td></tr>
+ <tr><td><span>password</span> <input type=password value=x></td></tr>
+ <tr><td><span>date</span> <input type=date value="2020-01-01"></td></tr>
+ <tr><td><span>month</span> <input type=month value="2020-01"></td></tr>
+ <tr><td><span>week</span> <input type=week value="2020-W01"></td></tr>
+ <tr><td><span>time</span> <input type=time value="00:00"></td></tr>
+ <tr><td><span>datetime-local</span> <input type=datetime-local value="2020-01-01T00:00"></td></tr>
+ <tr><td><span>number</span> <input type=number value=0></td></tr>
+ <tr><td><span>range</span> <input type=range></td></tr>
+ <tr><td><span>color</span> <input type=color value=#000000></td></tr>
+ <tr><td><span>checkbox</span> <input type=checkbox></td></tr>
+ <tr><td><span>radio</span> <input type=radio></td></tr>
+ <tr><td><span>file</span> <input type=file></td></tr>
+ <tr><td><span>submit</span> <input type=submit value=x></td></tr>
+ <tr><td><span>image</span> <input type=image src="data:,broken" alt="x"></td></tr>
+ <tr><td><span>image-with-src</span> <input type=image src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAIAAAD3UuoiAAAAGklEQVQoz2Nk%2BP%2BfgRqAiYFKYNSgUYOGp0EA%2BQMCFrJdTgsAAAAASUVORK5CYII%3D" alt="x"></td></tr>
+ <tr><td><span>reset</span> <input type=reset value=x></td></tr>
+ <tr><td><span>button</span> <input type=button value=x></td></tr>
+</table>
+<h2>tests</h2>
+<!--
+This table gets populated by the script.
+-->
+<table class="test" id="test-table"><tbody></tbody></table>
+<script>
+ "use strict";
+
+ promise_setup(async () => {
+ const templateTable = document.querySelector('#template-table');
+ const testTBody = document.querySelector('#test-table tbody');
+
+ {
+ const checkboxBorder = getComputedStyle(document.querySelector("input[type=checkbox]")).borderTopWidth;
+ const checkboxLike = document.querySelector(".auto-checkbox");
+ checkboxLike.style.border = checkboxBorder + " solid";
+ checkboxLike.style.marginBottom = "-" + checkboxBorder;
+ }
+
+ const templateRows = templateTable.querySelectorAll('tr');
+ for (const templateRow of templateRows) {
+ for (const appearanceValue of ["auto", "none"]) {
+ for (const overflowValue of ['visible', 'hidden', 'scroll']) {
+ const clonedRow = templateRow.cloneNode(true);
+ clonedRow.querySelector('input').setAttribute('style', `overflow: ${overflowValue}; appearance: ${appearanceValue};`);
+ testTBody.append(clonedRow);
+ }
+ }
+ }
+
+ // wait for images to load
+ await new Promise(resolve => window.onload = e => resolve());
+ for (const img of document.images) {
+ assert_true(img.complete); // either error state or loaded
+ }
+
+ // get layout info from refs
+ const refTextInputLikeOffsetTop = document.querySelector('.ref-text-input-like span').offsetTop;
+ const refCheckboxInputAppearanceAutoLikeOffsetTop = document.querySelector('.ref-checkbox-input-appearance-auto-like span').offsetTop;
+ const refColorInputLikeOffsetTop = document.querySelector('.ref-color-input-like span').offsetTop;
+ const refFileInputLikeOffsetTop = document.querySelector('.ref-file-input-like span').offsetTop;
+ const refImageInputShowingAltOverflowVisibleLikeOffsetTop = document.querySelector('.ref-image-input-showing-alt-overflow-visible-like span').offsetTop;
+ const refImageInputShowingImageLikeOffsetTop = document.querySelector('.ref-image-input-showing-image-like span').offsetTop;
+
+ function expectedOffsetTop(input) {
+ // TODO(zcorpan) https://github.com/whatwg/html/issues/5065
+ // for now this is intended to match Firefox
+ const style = input.getAttribute('style');
+ const src = input.getAttribute('src');
+ switch (input.type) {
+ case 'file':
+ return refFileInputLikeOffsetTop;
+ case 'range':
+ return refImageInputShowingImageLikeOffsetTop;
+ case 'color':
+ return refColorInputLikeOffsetTop;
+ case 'checkbox':
+ case 'radio':
+ return (style.includes('appearance: none;')) ? refImageInputShowingImageLikeOffsetTop : refCheckboxInputAppearanceAutoLikeOffsetTop;
+ case 'image':
+ return (src === 'data:,broken' && style.includes('overflow: visible;')) ? refImageInputShowingAltOverflowVisibleLikeOffsetTop : refImageInputShowingImageLikeOffsetTop;
+ default:
+ return refTextInputLikeOffsetTop;
+ }
+ }
+
+ function testName(markup) {
+ return markup.replace(/data:image\/png[^"]+/, 'data:(png)');
+ }
+
+ for (const row of testTBody.children) {
+ const input = row.firstChild.lastElementChild;
+ // This is not using test() because promise_setup() only allows promise_test().
+ promise_test(async () => {
+ assert_equals(input.type, input.getAttribute('type'), 'input type should be supported')
+ const offsetTopActual = row.firstChild.firstChild.offsetTop;
+ assert_equals(offsetTopActual, expectedOffsetTop(input), '<span>.offsetTop');
+ }, testName(input.outerHTML));
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box-ref.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box-ref.html
new file mode 100644
index 0000000000..243f0add0d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box-ref.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Reference for Anonymous button content box</title>
+<style>
+body { margin: 0 }
+.button { width: 124px; height: 124px; background: papayawhip; border: 2px solid; display: table-cell; vertical-align: middle; text-align: center; box-sizing: border-box; }
+.top { vertical-align: top }
+.left { text-align: left }
+.float { display: block; float: left; }
+body > div { clear: left; }
+.button > div { width: 50px; height: 50px; border: solid }
+.button > .tall { height: 150px }
+.button > .wide { width: 150px; margin-top: 32px }
+</style>
+<div>
+ <div class=button>input</div>
+ <div class="button">input grid</div>
+ <div class="button">input flex</div>
+ <div class=button>button</div>
+</div>
+<div>
+ <div class=button style="text-align: left">button left</div>
+ <div class=button><div>div</div></div>
+ <div class="button top">grid</div>
+ <div class="button top"><div>grid</div></div>
+</div>
+<div>
+ <div class="button top left float">flex</div>
+ <div class="button top float"><div>flex</div></div>
+ <div class="button float"><div class=tall>tall</div></div>
+ <div class="button float"><div class=wide>wide</div></div>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box.html
new file mode 100644
index 0000000000..07c7b8e366
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/anonymous-button-content-box.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Anonymous button content box</title>
+<link rel=match href=anonymous-button-content-box-ref.html>
+<style>
+body { margin: 0 }
+button, input { width: 124px; height: 124px; background: papayawhip; border: 2px solid; margin: 0; padding: 0; float: left; font: inherit }
+body > div { clear: left; }
+button > div { width: 50px; height: 50px; border: solid }
+.grid { display: grid }
+.flex { display: flex }
+.tall { height: 150px }
+.wide { width: 150px }
+</style>
+<div>
+ <input type=button value=input>
+ <input type=button value="input grid" class=grid>
+ <input type=button value="input flex" class=flex>
+ <button>button</button>
+</div>
+<div>
+ <button style="text-align: left">button left</button>
+ <button><div>div</div></button>
+ <button class=grid>grid</button>
+ <button class=grid><div>grid</div></button>
+</div>
+<div>
+ <button class=flex>flex</button>
+ <button class=flex><div>flex</div></button>
+ <button><div class=tall>tall</div></button>
+ <button><div class=wide>wide</div></button>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/computed-style.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/computed-style.html
new file mode 100644
index 0000000000..d18e5d365d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/computed-style.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<title>computed style on buttons</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div class="tests">
+ <input type="reset">
+ <input type="button">
+ <input type="submit">
+ <input type="color">
+ <input type="image">
+ <button></button>
+</div>
+<script>
+// "behave as" doesn't change computed value.
+const displayValues = ['inline', 'block', 'list-item', 'inline-block', 'table', 'inline-table', 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row', 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none', 'contents', 'flow', 'flow-root', 'flex', 'grid', 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', 'ruby-text-container', 'inline-flex', 'inline-grid'];
+for (const val of displayValues) {
+ [].slice.call(document.querySelectorAll('.tests > *')).forEach(el => {
+ el.style.display = ''
+ el.style.display = val;
+ const attrs = el.type ? ` type=${el.type}` : '';
+ const tag = `<${el.localName}${attrs}>`;
+ test(() => {
+ assert_not_equals(el.style.display, '', `display: ${val} is not supported`)
+ let expectedVal = val;
+ if (el instanceof HTMLInputElement && val === 'contents') {
+ expectedVal = 'none'; // https://drafts.csswg.org/css-display/#unbox-html
+ }
+ if (val == 'flow') {
+ // Use the more backwards-compatible form, `block` is better than `flow`
+ // https://drafts.csswg.org/cssom/#serializing-css-values
+ expectedVal = 'block';
+ }
+ assert_equals(getComputedStyle(el).display, expectedVal);
+ }, `computed display of ${tag} with display: ${val}`);
+ });
+}
+
+for (let input of document.querySelectorAll("input")) {
+ test(() => {
+ if (input.type == "image") {
+ assert_equals(getComputedStyle(input).overflow, "visible", "Should not be clip by default");
+ return;
+ }
+ assert_equals(getComputedStyle(input).overflow, "clip", "Should be clip by default");
+ assert_equals(getComputedStyle(input).overflowClipMargin, "0px", "Should use 0 margin");
+ input.style.overflow = "visible";
+ input.style.overflowClipMargin = "10px";
+ assert_equals(getComputedStyle(input).overflow, "clip", "Clip be !important");
+ assert_equals(getComputedStyle(input).overflowClipMargin, "0px", "Clip margin should be !important too");
+ }, `<input type=${input.type}> overflow/overflow-clip-margin`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/display-other.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/display-other.html
new file mode 100644
index 0000000000..6ed3f5894a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/display-other.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<title>button with other display values</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+body { margin: 0 }
+.float { width: 100px; height: 100px; float: left; background: blue; margin: 10px }
+</style>
+<div class=float></div>
+<button id=button style="display: block;"><div class=float></div></button><span id=after>after</span>
+<script>
+// These should all behave as flow-root.
+const displayValues = ['run-in', 'flow', 'flow-root', 'table', 'list-item',
+ 'table-row-group', 'table-header-group', 'table-footer-group',
+ 'table-row', 'table-cell', 'table-column-group', 'table-column',
+ 'table-caption', 'ruby-base', 'ruby-text', 'ruby-base-container',
+ 'ruby-text-container'];
+const button = document.getElementById('button');
+const after = document.getElementById('after');
+function getValues() {
+ return {
+ buttonLeft: button.offsetLeft,
+ buttonTop: button.offsetTop,
+ buttonWidth: button.clientWidth,
+ buttonHeight: button.clientHeight,
+ afterLeft: after.offsetLeft,
+ afterTop: after.offsetTop,
+ };
+}
+const expected = getValues();
+test(t => {
+ assert_equals(expected.buttonLeft, 120, 'buttonLeft');
+ assert_equals(expected.buttonTop, 0, 'buttonTop');
+ assert_greater_than_equal(expected.buttonWidth, 120, 'buttonWidth');
+ assert_greater_than_equal(expected.buttonHeight, 120, 'buttonHeight');
+ assert_equals(expected.afterLeft, 0, 'afterLeft');
+ assert_greater_than_equal(expected.afterTop, 120, 'afterTop');
+}, 'display: block');
+for (const val of displayValues) {
+ test(t => {
+ t.add_cleanup(() => {
+ button.style.display = 'block';
+ });
+ assert_true(CSS.supports(`display: ${val}`), `display: ${val} is not supported`);
+ button.style.display = val;
+ const values = getValues();
+ for (const prop in values) {
+ assert_equals(values[prop], expected[prop], prop);
+ }
+ }, `display: ${val}`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/flex.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/flex.html
new file mode 100644
index 0000000000..7f159fc232
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/flex.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<title>button with flex/inline-flex</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#inline-flex { display: inline-flex }
+#flex { display: flex }
+#ref > div { display: flex }
+
+#flexstart {
+ border: none;
+ padding: 0;
+ display: flex;
+ align-items: flex-start;
+ height: 3em;
+}
+
+#stretch {
+ border: none;
+ padding: 0;
+ display: flex;
+ align-items: stretch;
+ height: 3em;
+}
+</style>
+<button id=inline-flex><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=flex><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=ref><div><div>1</div><div>2</div><div>3</div><div>4</div></div></button>
+
+<div><button id="flexstart"><span id="flexstart-item">abc</span></button></div>
+
+<div><button id="stretch"><span id="stretch-item">abc</span></button></div>
+<script>
+const ref = document.getElementById('ref');
+const expectedWidth = ref.clientWidth;
+const expectedHeight = ref.clientHeight;
+for (const elm of [document.getElementById('inline-flex'),
+ document.getElementById('flex')]) {
+ test(() => {
+ // check that flex is supported
+ const flexValue = elm.id;
+ assert_equals(getComputedStyle(elm).display, flexValue, `${flexValue} is not supported`);
+ const width = elm.clientWidth;
+ const height = elm.clientHeight;
+ assert_equals(width, expectedWidth, 'clientWidth');
+ assert_equals(height, expectedHeight, 'clientHeight');
+ }, elm.id);
+}
+
+// crbug.com/700029
+test(() => {
+ assert_equals(document.getElementById('flexstart').offsetTop,
+ document.getElementById('flexstart-item').offsetTop);
+}, 'align-items:flex-start should work');
+
+// crbug.com/1004163
+test(() => {
+ assert_equals(document.getElementById('stretch').offsetHeight,
+ document.getElementById('stretch-item').offsetHeight);
+}, 'align-items:stretch should work');
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/grid.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/grid.html
new file mode 100644
index 0000000000..7c2a467099
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/grid.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>button with grid/inline-grid</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#inline-grid { display: inline-grid }
+#grid { display: grid }
+#ref > div { display: grid }
+#inline-grid, #grid, #ref > div { grid-template: auto auto / auto auto }
+</style>
+<button id=inline-grid><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=grid><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=ref><div><div>1</div><div>2</div><div>3</div><div>4</div></div></button>
+<script>
+const ref = document.getElementById('ref');
+const expectedWidth = ref.clientWidth;
+const expectedHeight = ref.clientHeight;
+for (const elm of [document.getElementById('inline-grid'),
+ document.getElementById('grid')]) {
+ test(() => {
+ // check that grid is supported
+ const gridValue = elm.id;
+ assert_equals(getComputedStyle(elm).display, gridValue, `${gridValue} is not supported`);
+ const width = elm.clientWidth;
+ const height = elm.clientHeight;
+ assert_equals(width, expectedWidth, 'clientWidth');
+ assert_equals(height, expectedHeight, 'clientHeight');
+ }, elm.id);
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level-ref.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level-ref.html
new file mode 100644
index 0000000000..3784cc30db
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level-ref.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Reference for button with inline-level display</title>
+<style>
+button { font: inherit }
+</style>
+<p>There should be three buttons below containing "1" and "2" on separate lines, and "a" and "b" before and after on the same baseline as the "2".</p>
+<p>a<button>1<br>2</button>b</p>
+<p>a<button>1<br>2</button>b</p>
+<p>a<button>1<br>2</button>b</p>
+<p>a<button>1<br>2</button>b</p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level.html
new file mode 100644
index 0000000000..e23aba731c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/inline-level.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>button with inline-level display</title>
+<link rel=match href=inline-level-ref.html>
+<style>
+button, input { font: inherit }
+.inline { display: inline }
+.inline-block { display: inline-block }
+.inline-table { display: inline-table }
+</style>
+<p>There should be three buttons below containing "1" and "2" on separate lines, and "a" and "b" before and after on the same baseline as the "2".</p>
+<p>a<button class=inline>1<br><span class=check-baseline>2</span></button><span class=ref-baseline>b</span></p>
+<p>a<button class=inline-block>1<br><span class=check-baseline>2</span></button><span class=ref-baseline>b</span></p>
+<p>a<button class=inline-table>1<br><span class=check-baseline>2</span></button><span class=ref-baseline>b</span></p>
+<p>a<input type=button class=inline-table value="1&#10;2">b</p>
+<script>
+const spans = document.querySelectorAll('.check-baseline');
+for (const span of [].slice.call(spans)) {
+ const baseline = span.offsetTop + span.offsetHeight;
+ const refSpan = span.parentNode.nextSibling;
+ const refBaseline = refSpan.offsetTop + refSpan.offsetHeight;
+ if (baseline !== refBaseline) {
+ refSpan.textContent += " (wrong baseline)";
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip-ref.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip-ref.html
new file mode 100644
index 0000000000..8f956d36f5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<style>
+ button {
+ width: 10ch;
+ overflow: clip;
+ text-overflow: ellipsis;
+ }
+</style>
+<button>aaaaaaaaaaaaaaaaaaaaaa</button>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip.html
new file mode 100644
index 0000000000..db1ad35cce
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-clip.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>button input is clipped by default, and text-overflow: ellipsis works</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/9976">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1800077">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="match" href="input-type-button-clip-ref.html">
+<style>
+ input {
+ width: 10ch;
+ text-overflow: ellipsis;
+ }
+</style>
+<input type="button" value="aaaaaaaaaaaaaaaaaaaaaa">
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2-mismatch.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2-mismatch.html
new file mode 100644
index 0000000000..19930b947b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2-mismatch.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<input type=button value="1">
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2.html
new file mode 100644
index 0000000000..6e21c9e98e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=922011">
+<link rel=mismatch href="input-type-button-newline-2-mismatch.html">
+
+<input type=button value="1&#10;2">
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-mismatch.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-mismatch.html
new file mode 100644
index 0000000000..3d6495de66
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline-mismatch.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<input type=button value="12">
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline.html
new file mode 100644
index 0000000000..139893ed42
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/input-type-button-newline.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=922011">
+<link rel=mismatch href="input-type-button-newline-mismatch.html">
+
+<input type=button value="1&#10;2">
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration-ref.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration-ref.html
new file mode 100644
index 0000000000..f33a011a20
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Reference for propagating text-decoration into buttons</title>
+<style>
+button { font: inherit }
+</style>
+<p>The text in the following buttons should be underlined.</p>
+<p><button><u>foo</u></button><button><u>foo</u></button></p>
+<p>The text in the following buttons should NOT be underlined.</p>
+<p><button>foo</button><button>foo</button></p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration.html
new file mode 100644
index 0000000000..83608402c1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/propagate-text-decoration.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>propagating text-decoration into buttons</title>
+<link rel=match href=propagate-text-decoration-ref.html>
+<meta name=fuzzy content="maxDifference=0-40;totalPixels=0-50">
+<style>
+button, input { font: inherit }
+.inline > u > * { display: inline }
+.inline-block > u > * { display: inline-block }
+</style>
+<p>The text in the following buttons should be underlined.</p>
+<p class=inline><u><button>foo</button><input type=button value=foo></u></p>
+<p>The text in the following buttons should NOT be underlined.</p>
+<p class=inline-block><u><button>foo</button><input type=button value=foo></u></p>
diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/shrink-wrap.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/shrink-wrap.html
new file mode 100644
index 0000000000..6d61102608
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/shrink-wrap.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<title>shrink-wrap button</title>
+<link rel=help href=https://html.spec.whatwg.org/multipage/rendering.html#button-layout>
+<link rel=help href=https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<!--
+ minimum preferred width 100px
+ preferred width 250px
+ available width vary
+-->
+<style>
+div.available-width { border: thin dotted red; margin: 1em 0; padding: 1em 0 }
+button { border: none; margin: 0; padding: 0; background: papayawhip; }
+button > span.minimum-preferred-width { width: 100px }
+button > span { width: 50px; display: inline-block; outline: thin dotted blue }
+</style>
+<div class="available-width" style="width: 50px">
+ <button data-expected="100"><span class="minimum-preferred-width">x</span><span>x</span><span>x</span><span>x</span></button>
+</div>
+
+<div class="available-width" style="width: 200px">
+ <button data-expected="200"><span class="minimum-preferred-width">x</span><span>x</span><span>x</span><span>x</span></button>
+</div>
+
+<div class="available-width" style="width: 300px">
+ <button data-expected="250"><span class="minimum-preferred-width">x</span><span>x</span><span>x</span><span>x</span></button>
+</div>
+
+<script>
+const styles = ['display: block', 'display: inline', 'display: inline-block',
+ 'display: list-item', 'display: table', 'display: table-row',
+ 'display: table-cell', 'float: left'];
+for (const style of styles) {
+ for (const button of [].slice.call(document.querySelectorAll('button'))) {
+ test(() => {
+ button.setAttribute('style', style);
+ assert_equals(button.clientWidth, parseInt(button.dataset.expected, 10));
+ }, `${style} - available ${button.parentNode.getAttribute('style')}`);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/field-sizing-input-number.html b/testing/web-platform/tests/html/rendering/widgets/field-sizing-input-number.html
new file mode 100644
index 0000000000..859d35197b
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/field-sizing-input-number.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#field-sizing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.disable-default {
+ field-sizing: content;
+}
+
+.small-placeholder {
+ font-size: 32px;
+}
+.small-placeholder::placeholder {
+ font-size: 16px;
+}
+
+.large-placeholder {
+ font-size: 20px;
+}
+.large-placeholder::placeholder {
+ font-size: 40px;
+}
+</style>
+<body>
+<div id="container"></div>
+<script>
+const DISABLE = 'class="disable-default"';
+const LONG_VALUE = '12345678901234567890.123';
+const LONG_TEXT = 'The quick brown fox jumps over the lazy dog.';
+
+function addElements(source) {
+ const container = document.querySelector('#container');
+ container.innerHTML = source + source;
+ container.lastElementChild.classList.add('disable-default');
+ return {
+ fixed: container.firstElementChild,
+ content: container.lastElementChild
+ };
+}
+
+function addTwoElements(source1, source2) {
+ const container = document.querySelector('#container');
+ container.innerHTML = source1 + source2;
+ return {
+ e1: container.firstElementChild,
+ e2: container.lastElementChild
+ };
+}
+
+['number'].forEach(type => {
+ test(() => {
+ let pair = addElements(`<input type=${type}>`);
+ // A text <input> has approximately 20ch width by default.
+ // A text <input> with field-sizing:content must be shorter than the default.
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addTwoElements(`<input type=${type} ${DISABLE}>`,
+ `<input type=${type} ${DISABLE} value="123">`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+ }, `${type}: Empty value`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} value="${LONG_VALUE}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ pair = addElements(`<input type=${type} placeholder="${LONG_TEXT}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ }, `${type}: Auto width and auto height with a long text`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} style="width:20ch; height:2lh">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch; height:2lh" value="${LONG_VALUE}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch; height:2lh" placeholder="${LONG_TEXT}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+ }, `${type}: Explicit width and heigth`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} style="width:20ch">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch" value="${LONG_VALUE}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch" placeholder="${LONG_TEXT}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+ }, `${type}: Explicit width and auto height`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} style="height:2lh">`);
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="height:2lh" value="${LONG_VALUE}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="height:2lh" placeholder="${LONG_TEXT}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+ }, `${type}: Explicit height and auto width`);
+
+ test(() => {
+ let pair = addTwoElements(
+ `<input type=${type} class="disable-default small-placeholder">`,
+ `<input type=${type} class="disable-default small-placeholder" placeholder="foo bar">`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+ }, `${type}: Text caret is taller than the placeholder`);
+
+ test(() => {
+ let pair = addTwoElements(
+ `<input type=${type} class="disable-default large-placeholder">`,
+ `<input type=${type} class="disable-default large-placeholder" placeholder="foo bar">`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_less_than(pair.e1.offsetHeight, pair.e2.offsetHeight);
+
+ pair = addTwoElements(
+ `<input type=${type} class="disable-default" style="font-size:40px; padding:16px">`,
+ `<input type=${type} class="disable-default large-placeholder"
+ placeholder="foo bar" style="font-size:40px; padding:16px">`);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+ assert_equals(pair.e1.offsetTop, pair.e2.offsetTop);
+ }, `${type}: Text caret is shorter than the placeholder`);
+
+ test(() => {
+ const container = document.querySelector('#container');
+ container.innerHTML = `<input type=${type}>`;
+ const element = container.firstChild;
+ const w = element.offsetWidth;
+ const h = element.offsetHeight;
+ element.classList.add('disable-default');
+ assert_less_than(element.offsetWidth, w);
+ assert_equals(element.offsetHeight, h);
+ }, `${type}: Update field-sizing property dynamically`);
+
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/field-sizing-input-text.html b/testing/web-platform/tests/html/rendering/widgets/field-sizing-input-text.html
new file mode 100644
index 0000000000..f421ad602c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/field-sizing-input-text.html
@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#field-sizing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.disable-default {
+ field-sizing: content;
+}
+
+.small-placeholder {
+ font-size: 32px;
+}
+.small-placeholder::placeholder {
+ font-size: 16px;
+}
+
+.large-placeholder {
+ font-size: 20px;
+}
+.large-placeholder::placeholder {
+ font-size: 40px;
+}
+</style>
+<body>
+<div id="container"></div>
+<script>
+const DISABLE = 'class="disable-default"';
+const LONG_TEXT = 'The quick brown fox jumps over the lazy dog.';
+
+function addElements(source) {
+ const container = document.querySelector('#container');
+ container.innerHTML = source + source;
+ container.lastElementChild.classList.add('disable-default');
+ return {
+ fixed: container.firstElementChild,
+ content: container.lastElementChild
+ };
+}
+
+function addTwoElements(source1, source2) {
+ const container = document.querySelector('#container');
+ container.innerHTML = source1 + source2;
+ return {
+ e1: container.firstElementChild,
+ e2: container.lastElementChild
+ };
+}
+
+['text', 'search', 'tel', 'url', 'email', 'password'].forEach(type => {
+ test(() => {
+ let pair = addElements(`<input type=${type}>`);
+ // A text <input> has approximately 20ch width by default.
+ // A text <input> with field-sizing:content must be shorter than the default.
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addTwoElements(`<input type=${type} ${DISABLE}>`,
+ `<input type=${type} ${DISABLE} value="foo">`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+ }, `${type}: Empty value`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} size="10">`);
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+ }, `${type}: Empty value with a size atttribute`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} value="${LONG_TEXT}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ pair = addElements(`<input type=${type} placeholder="${LONG_TEXT}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ }, `${type}: Auto width and auto height with a long text`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} style="width:20ch; height:2lh">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch; height:2lh" value="${LONG_TEXT}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch; height:2lh" placeholder="${LONG_TEXT}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+ }, `${type}: Explicit width and heigth`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} style="width:20ch">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch" value="${LONG_TEXT}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="width:20ch" placeholder="${LONG_TEXT}">`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+ }, `${type}: Explicit width and auto height`);
+
+ test(() => {
+ let pair = addElements(`<input type=${type} style="height:2lh">`);
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="height:2lh" value="${LONG_TEXT}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<input type=${type} style="height:2lh" placeholder="${LONG_TEXT}">`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+ }, `${type}: Explicit height and auto width`);
+
+ test(() => {
+ let pair = addTwoElements(
+ `<input type=${type} class="disable-default small-placeholder">`,
+ `<input type=${type} class="disable-default small-placeholder" placeholder="foo bar">`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+ }, `${type}: Text caret is taller than the placeholder`);
+
+ test(() => {
+ let pair = addTwoElements(
+ `<input type=${type} class="disable-default large-placeholder">`,
+ `<input type=${type} class="disable-default large-placeholder" placeholder="foo bar">`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_less_than(pair.e1.offsetHeight, pair.e2.offsetHeight);
+
+ pair = addTwoElements(
+ `<input type=${type} class="disable-default" style="font-size:40px; padding:16px">`,
+ `<input type=${type} class="disable-default large-placeholder"
+ placeholder="foo bar" style="font-size:40px; padding:16px">`);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+ assert_equals(pair.e1.offsetTop, pair.e2.offsetTop);
+ }, `${type}: Text caret is shorter than the placeholder`);
+
+ test(() => {
+ const container = document.querySelector('#container');
+ container.innerHTML = `<input type=${type}>`;
+ const element = container.firstChild;
+ const w = element.offsetWidth;
+ const h = element.offsetHeight;
+ element.classList.add('disable-default');
+ assert_less_than(element.offsetWidth, w);
+ assert_equals(element.offsetHeight, h);
+ }, `${type}: Update field-sizing property dynamically`);
+
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/field-sizing-select.html b/testing/web-platform/tests/html/rendering/widgets/field-sizing-select.html
new file mode 100644
index 0000000000..d55898105e
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/field-sizing-select.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#field-sizing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<style>
+.disable-default {
+ field-sizing: content;
+}
+</style>
+<div id="container"></div>
+<script>
+const container = document.querySelector('#container');
+const DISABLE = 'class="disable-default"';
+
+// Tests for drop-down box ====================================================
+
+test(() => {
+ const s = '<select>><option>1<option>quick brown<option>fox</select>';
+ container.innerHTML = s + s;
+ container.lastElementChild.style.fieldSizing = 'content';
+ const widthForContent1 = container.lastElementChild.offsetWidth;
+ assert_greater_than(container.firstElementChild.offsetWidth,
+ widthForContent1);
+ container.lastElementChild.selectedIndex = 1;
+ const widthForContentQuickBrown = container.lastElementChild.offsetWidth;
+ assert_greater_than(widthForContentQuickBrown, widthForContent1);
+}, 'dropdown: The width should depend on the selected OPTION');
+
+test(() => {
+ container.innerHTML = '<select><option>foo<option>quick brown fox</select>';
+ const select = container.firstElementChild;
+ const initialWidth = select.offsetWidth;
+ select.style.fieldSizing = 'content';
+ assert_less_than(select.offsetWidth, initialWidth);
+ select.style.fieldSizing = 'fixed';
+ assert_equals(select.offsetWidth, initialWidth);
+}, 'dropdown: Change the field-sizing value dynamically');
+
+// Tests for list box =========================================================
+
+// Some paltforms don't support list box rendering.
+container.innerHTML = '<select></select><select multiple></select>';
+if (container.firstElementChild.offsetHeight != container.lastElementChild.offsetHeight) {
+
+ test(() => {
+ container.innerHTML = `<select multiple><option>fox</select>` +
+ `<select multiple ${DISABLE}><option>fox</select>`;
+ const former = container.firstElementChild;
+ const latter = container.lastElementChild;
+ const widthForOneItem = latter.offsetWidth;
+ const heightForOneItem = latter.offsetHeight;
+ assert_equals(former.offsetWidth, widthForOneItem);
+ assert_greater_than(former.offsetHeight, heightForOneItem);
+
+ latter.add(new Option('quick brown'));
+ assert_greater_than(latter.offsetWidth, widthForOneItem);
+ assert_greater_than(latter.offsetHeight, heightForOneItem);
+ }, 'listbox: The size depend on the content');
+
+ test(() => {
+ container.innerHTML = `<select size="4"></select><select size="4" ${DISABLE}></select>`;
+ const former = container.firstElementChild;
+ const latter = container.lastElementChild;
+ const widthForZeroItem = latter.offsetWidth;
+ const heightForZeroItem = latter.offsetHeight;
+ assert_equals(former.offsetWidth, widthForZeroItem);
+ assert_greater_than(former.offsetHeight, heightForZeroItem);
+
+ latter.add(new Option('quick brown'));
+ assert_greater_than(latter.offsetWidth, widthForZeroItem);
+ assert_greater_than(latter.offsetHeight, heightForZeroItem);
+ }, 'listbox: The size attribute value is ignored');
+
+ test(() => {
+ container.innerHTML = '<select multiple><option>foo<option>quick brown fox</select>';
+ const select = container.firstElementChild;
+ const initialHeight = select.offsetHeight;
+ select.style.fieldSizing = 'content';
+ assert_less_than(select.offsetHeight, initialHeight);
+ select.style.fieldSizing = 'fixed';
+ assert_equals(select.offsetHeight, initialHeight);
+ }, 'listbox: Change the field-sizing value dynamically');
+
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/field-sizing-textarea.html b/testing/web-platform/tests/html/rendering/widgets/field-sizing-textarea.html
new file mode 100644
index 0000000000..f8d90dc659
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/field-sizing-textarea.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#field-sizing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.disable-default {
+ field-sizing: content;
+}
+
+.small-placeholder {
+ font-size: 32px;
+}
+.small-placeholder::placeholder {
+ font-size: 16px;
+}
+
+.large-placeholder {
+ font-size: 20px;
+}
+.large-placeholder::placeholder {
+ font-size: 40px;
+}
+</style>
+<body>
+<div id="container"></div>
+<script>
+const DISABLE = 'class="disable-default"';
+const LONG_TEXT = 'The quick brown fox jumps over the lazy dog.';
+
+function addElements(source) {
+ const container = document.querySelector('#container');
+ container.innerHTML = source + source;
+ container.lastElementChild.classList.add('disable-default');
+ return {
+ fixed: container.firstElementChild,
+ content: container.lastElementChild
+ };
+}
+
+function addTwoElements(source1, source2) {
+ const container = document.querySelector('#container');
+ container.innerHTML = source1 + source2;
+ return {
+ e1: container.firstElementChild,
+ e2: container.lastElementChild
+ };
+}
+
+test(() => {
+ let pair = addElements('<textarea></textarea>');
+ // Historically a <textarea> has approximately 20ch x 2lh size by default.
+ // A <textarea> with field-sizing:content must be samller than the default.
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_less_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addTwoElements(`<textarea ${DISABLE}></textarea>`, `<textarea ${DISABLE}>foo</textarea>`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+}, 'Empty value');
+
+test(() => {
+ let pair = addElements('<textarea cols="10" rows="3"></textarea>');
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_less_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
+}, 'Empty value with cols/rows');
+
+test(() => {
+ let pair = addElements(`<textarea>${LONG_TEXT}</textarea>`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ pair = addElements(`<textarea placeholder="${LONG_TEXT}"></textarea>`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+}, 'Auto width and auto height with a long text');
+
+test(() => {
+ let pair = addElements('<textarea style="width:20ch; height:2lh"></textarea>');
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<textarea style="width:20ch; height:2lh">${LONG_TEXT}</textarea>`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<textarea style="width:20ch; height:2lh" placeholder="${LONG_TEXT}"></textarea>`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+}, 'Explicit width and heigth');
+
+test(() => {
+ let pair = addElements('<textarea style="width:20ch"></textarea>');
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_less_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<textarea style="width:20ch">${LONG_TEXT}</textarea>`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_greater_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<textarea style="width:20ch" placeholder="${LONG_TEXT}"></textarea>`);
+ assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_greater_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
+}, 'Explicit width and auto height');
+
+test(() => {
+ let pair = addElements('<textarea style="height:2lh"></textarea>');
+ assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<textarea style="height:2lh">${LONG_TEXT}</textarea>`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+
+ pair = addElements(`<textarea style="height:2lh" placeholder="${LONG_TEXT}"></textarea>`);
+ assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
+ assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
+}, 'Explicit height and auto width');
+
+test(() => {
+ let pair = addTwoElements(`<textarea class="disable-default small-placeholder"></textarea>`,
+ `<textarea class="disable-default small-placeholder" placeholder="foo"></textarea>`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
+ assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
+}, 'Text caret is taller than the placeholder');
+
+test(() => {
+ let pair = addTwoElements(`<textarea class="disable-default large-placeholder"></textarea>`,
+ `<textarea class="disable-default large-placeholder" placeholder="foo"></textarea>`);
+ assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth, 'width');
+ assert_less_than(pair.e1.offsetHeight, pair.e2.offsetHeight, 'height');
+}, 'Text caret is shorter than the placeholder');
+
+test(() => {
+ const container = document.querySelector('#container');
+ container.innerHTML = '<textarea></textarea>';
+ const element = container.firstChild;
+ const w = element.offsetWidth;
+ const h = element.offsetHeight;
+ element.classList.add('disable-default');
+ assert_less_than(element.offsetWidth, w);
+ assert_less_than(element.offsetHeight, h);
+}, 'Update field-sizing property dynamically');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked-notref.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked-notref.html
new file mode 100644
index 0000000000..8caced027a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked-notref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<input type=checkbox disabled>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked.html
new file mode 100644
index 0000000000..f72fdf62fd
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-disabled-checked.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>checkbox with disabled and checked attributes renders differently than unchecked</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1735077">
+<link rel=mismatch href="input-checkbox-disabled-checked-notref.html">
+<input type=checkbox disabled checked>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-no-centering.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-no-centering.html
new file mode 100644
index 0000000000..7dba149c75
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-no-centering.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1441341">
+<link rel="match" href="input-no-centering-ref.html">
+<style>
+input {
+ appearance: none;
+ margin: 0;
+ width: 200px;
+ height: 200px;
+}
+input:before {
+ content: "";
+ display: block;
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+</style>
+<input type="checkbox">
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate-ref.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate-ref.html
new file mode 100644
index 0000000000..cb9a76aa21
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<input type=checkbox switch>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate.tentative.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate.tentative.html
new file mode 100644
index 0000000000..bd4137c1b5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-indeterminate.tentative.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Checkbox with switch attribute set does not render differently when the indeterminate attribute is set</title>
+<link rel=match href="input-checkbox-switch-indeterminate-ref.html">
+<input id="input" type=checkbox switch>
+<script>
+ input.indeterminate = true;
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-notref.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-notref.html
new file mode 100644
index 0000000000..75fff5d94f
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-notref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<input type=checkbox>
+<input type=checkbox>
+<input type=checkbox switch>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-ref.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-ref.html
new file mode 100644
index 0000000000..94aa1b7ecd
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<input type=checkbox switch>
+<input type=checkbox switch>
+<input type=checkbox>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl-notref.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl-notref.html
new file mode 100644
index 0000000000..02d57baf43
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl-notref.html
@@ -0,0 +1 @@
+<input type=checkbox switch>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl.tentative.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl.tentative.html
new file mode 100644
index 0000000000..3e0c5d44c9
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch-rtl.tentative.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<link rel=mismatch href=input-checkbox-switch-rtl-notref.html>
+<input type=checkbox switch dir=rtl>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.html b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.html
new file mode 100644
index 0000000000..315728d539
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Checkbox with switch attribute set renders differently than a checkbox without switch attribute</title>
+<link rel=match href="input-checkbox-switch-ref.html">
+<link rel=mismatch href="input-checkbox-switch-notref.html">
+<input type=checkbox switch>
+<input id='input2' type=checkbox>
+<input id='input3' type=checkbox switch>
+<script>
+ input2.setAttribute('switch','');
+ input3.removeAttribute('switch');
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.window.js b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.window.js
new file mode 100644
index 0000000000..84198eda10
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-checkbox-switch.tentative.window.js
@@ -0,0 +1,34 @@
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ assert_equals(getComputedStyle(input).appearance, "auto");
+}, "Default appearance value");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.style.display = "none"
+ assert_equals(getComputedStyle(input).display, "none");
+}, "Default appearance value: display:none");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.style.appearance = "none";
+ assert_equals(getComputedStyle(input).appearance, "none");
+}, "appearance:none should work");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.style.appearance = "none";
+ assert_equals(getComputedStyle(input).display, "inline");
+}, "appearance:none should work: display gets its initial value");
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height-ref.html b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height-ref.html
new file mode 100644
index 0000000000..249076a5b7
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height-ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test Reference</title>
+<div>
+ abc <input type=date style="height: 40px"> def
+</div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height.html b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height.html
new file mode 100644
index 0000000000..86b552ab80
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-min-height.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>date input baseline should account for min-height</title>
+<link rel="match" href="input-date-baseline-min-height-ref.html">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1825709">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<div>
+ abc <input type=date style="min-height: 40px"> def
+</div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-print.html b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-print.html
new file mode 100644
index 0000000000..dda06c69fb
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-print.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>date input baseline shouldn't be insane when printing</title>
+<link rel="match" href="input-date-baseline-ref.html">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1667510">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+ div {
+ border: 1px solid black;
+ line-height: 0;
+ }
+
+ input {
+ height: 20px;
+ font: 20px/1 Ahem;
+ box-sizing: border-box;
+ padding: 1px; /* Needed to trigger the bug */
+ border: 0;
+ visibility: hidden;
+ -webkit-appearance: none;
+ appearance: none;
+ }
+</style>
+<div>
+ <input type="date">
+</div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-ref.html b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-ref.html
new file mode 100644
index 0000000000..dcef656bad
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Test reference</title>
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+ div {
+ border: 1px solid black;
+ line-height: 0;
+ }
+ span {
+ font: 20px/1 Ahem;
+ visibility: hidden;
+ }
+</style>
+<div>
+ <span>A</span>
+</div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-baseline.html b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline.html
new file mode 100644
index 0000000000..0d8f46c064
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-baseline.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>date input baseline shouldn't be insane</title>
+<link rel="match" href="input-date-baseline-ref.html">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1667510">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+ div {
+ border: 1px solid black;
+ line-height: 0;
+ }
+
+ input {
+ height: 20px;
+ font: 20px/1 Ahem;
+ box-sizing: border-box;
+ padding: 1px; /* needed to trigger the bug */
+ border: 0;
+ visibility: hidden;
+ -webkit-appearance: none;
+ appearance: none;
+ }
+</style>
+<div>
+ <input type="date">
+</div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-content-size-ref.html b/testing/web-platform/tests/html/rendering/widgets/input-date-content-size-ref.html
new file mode 100644
index 0000000000..18019c56b1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-content-size-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Test reference</title>
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<body>
+ <input type="date">
+ <br>
+ <input type="date">
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-content-size.html b/testing/web-platform/tests/html/rendering/widgets/input-date-content-size.html
new file mode 100644
index 0000000000..d026771f3c
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-content-size.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Test: the date field's min-content and max-content sizes should be the same as its automatic size</title>
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1695578">
+<link rel="match" href="input-date-content-size-ref.html">
+<body>
+ <input type="date" style="width: min-content">
+ <br>
+ <input type="date" style="width: max-content">
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-date-no-resize-on-hover.html b/testing/web-platform/tests/html/rendering/widgets/input-date-no-resize-on-hover.html
new file mode 100644
index 0000000000..74952ca239
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-date-no-resize-on-hover.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Date input should not resize on hover when using web fonts</title>
+<link rel="help" href="https://crbug.com/1167555">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input id="target" type="date" style="font-family: custom-font">
+
+<script>
+function mouseMoveToTarget(target) {
+ return new test_driver.Actions().pointerMove(0, 0, {origin: target}).send();
+}
+
+promise_test(async () => {
+ // Update layout before font loads
+ document.body.offsetWidth;
+
+ const font_sheet = document.createElement('style');
+ font_sheet.textContent = '@font-face { font-family: custom-font; src: url(/fonts/Revalia.woff) }';
+ document.body.appendChild(font_sheet);
+
+ await document.fonts.ready;
+
+ const target = document.getElementById('target');
+ const width_before_hover = target.offsetWidth;
+ await mouseMoveToTarget(target);
+ const width_after_hover = target.offsetWidth;
+ assert_equals(width_before_hover, width_after_hover);
+});
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-no-centering-ref.html b/testing/web-platform/tests/html/rendering/widgets/input-no-centering-ref.html
new file mode 100644
index 0000000000..5b2ea91fe5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-no-centering-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background: green;"></div>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked-notref.html b/testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked-notref.html
new file mode 100644
index 0000000000..987e03cd92
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked-notref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<input type=radio disabled>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked.html b/testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked.html
new file mode 100644
index 0000000000..3ac2a37199
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-radio-disabled-checked.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<title>radio with disabled and checked attributes renders differently than unchecked</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1735077">
+<link rel=mismatch href="input-radio-disabled-checked-notref.html">
+<input type=radio disabled checked>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-radio-no-centering.html b/testing/web-platform/tests/html/rendering/widgets/input-radio-no-centering.html
new file mode 100644
index 0000000000..6a714f8460
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-radio-no-centering.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1441341">
+<link rel="match" href="input-no-centering-ref.html">
+<style>
+input {
+ appearance: none;
+ margin: 0;
+ width: 200px;
+ height: 200px;
+}
+input:before {
+ content: "";
+ display: block;
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+</style>
+<input type="radio">
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-text-size.html b/testing/web-platform/tests/html/rendering/widgets/input-text-size.html
new file mode 100644
index 0000000000..fb3008df08
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-text-size.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-input-element-as-a-text-entry-widget">
+<title>Test `size` attribute behaivor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<input id="missing">
+<input id="invalid" size="-1">
+<input id="size0" size="0">
+<input id="size1" size="1">
+<input id="size10" size="10">
+<input id="size20" size="20">
+<input id="size21" size="21">
+<input id="computed" style="border:none; padding:0;" size="19">
+<input id="computedNone" style="display:none" size="17">
+
+<script>
+test(() => {
+ assert_equals(missing.offsetWidth, size20.offsetWidth);
+}, 'A misssing attribute is equivalent to size=20');
+
+test(() => {
+ assert_equals(invalid.offsetWidth, size20.offsetWidth, 'size="-1"');
+ assert_equals(size0.offsetWidth, size20.offsetWidth, 'size="0"');
+}, 'An invalid attribute value is equivalent to size=20');
+
+test(() => {
+ assert_less_than(size1.offsetWidth, size10.offsetWidth, '1 < 10');
+ assert_less_than(size10.offsetWidth, size20.offsetWidth, '10 < 20');
+ assert_less_than(size20.offsetWidth, size21.offsetWidth, '20 < 21');
+}, 'The width depends on a size attribute');
+
+test(() => {
+ const computedString = getComputedStyle(computed).width;
+ assert_equals(computed.offsetWidth,
+ parseInt(computedString.substring(0, computedString.length - 2)));
+}, 'Size attribute value affects layout-dependent computed style');
+
+test(() => {
+ const computedString = getComputedStyle(computedNone).width;
+ assert_equals(computedString, 'auto');
+}, 'Size attribute value is not a presentational hint');
+</script>
+
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-time-content-size-ref.html b/testing/web-platform/tests/html/rendering/widgets/input-time-content-size-ref.html
new file mode 100644
index 0000000000..938d2659a8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-time-content-size-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Test reference</title>
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<body>
+ <input type="time">
+ <br>
+ <input type="time">
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/input-time-content-size.html b/testing/web-platform/tests/html/rendering/widgets/input-time-content-size.html
new file mode 100644
index 0000000000..4a378f6923
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/input-time-content-size.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Test: the time field's min-content and max-content sizes should be the same as its automatic size</title>
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1695578">
+<link rel="match" href="input-time-content-size-ref.html">
+<body>
+ <input type="time" style="width: min-content">
+ <br>
+ <input type="time" style="width: max-content">
+</body>
diff --git a/testing/web-platform/tests/html/rendering/widgets/select-wrap-no-spill.optional.html b/testing/web-platform/tests/html/rendering/widgets/select-wrap-no-spill.optional.html
new file mode 100644
index 0000000000..84aa5602ac
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/select-wrap-no-spill.optional.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<table>
+<tr><td id="target">
+<select style="width:80px; white-space:pre-wrap;">
+<option>ab option with a very long text that does not fit but should not spill</option>
+</select>
+<tr><td id="reference"><select style="width:80px;">
+<option>ab option with a very long text that does not fit but should not spill</option>
+</select>
+</table>
+<script>
+// crbug.com/924929
+test(() => {
+ assert_equals(document.querySelector('#target').offsetHeight,
+ document.querySelector('#reference').offsetHeight);
+}, 'Selected OPTION label with white-space:pre-wrap should not spill out.');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/rendering/widgets/textarea-cols-rows.html b/testing/web-platform/tests/html/rendering/widgets/textarea-cols-rows.html
new file mode 100644
index 0000000000..6ad24a2eb8
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/textarea-cols-rows.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-textarea-element-2">
+<title>Test `cols` `rows` attributes behaivor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<textarea id="missing"></textarea>
+<textarea id="invalid" cols="-1" rows="-1"></textarea>
+<textarea id="computed" style="border:none; padding:0;" cols="19" rows="5"></textarea>
+<textarea id="computedNone" style="display:none" cols="17" rows="7"></textarea>
+
+<textarea id="cols0" cols="0"></textarea>
+<textarea id="cols1" cols="1"></textarea>
+<textarea id="cols10" cols="10"></textarea>
+<textarea id="cols20" cols="20"></textarea>
+<textarea id="cols21" cols="21"></textarea>
+
+<textarea id="rows0" rows="0"></textarea>
+<textarea id="rows1" rows="1"></textarea>
+<textarea id="rows2" rows="2"></textarea>
+<textarea id="rows3" rows="3"></textarea>
+
+<script>
+test(() => {
+ assert_equals(missing.offsetWidth, cols20.offsetWidth);
+ assert_equals(missing.offsetHeight, rows2.offsetHeight);
+}, 'A misssing attribute is equivalent to cols=20 rows=2');
+
+test(() => {
+ assert_equals(invalid.offsetWidth, cols20.offsetWidth);
+ assert_equals(invalid.offsetHeight, rows2.offsetHeight);
+ assert_equals(cols0.offsetWidth, cols20.offsetWidth);
+ assert_equals(rows0.offsetHeight, rows2.offsetHeight);
+}, 'An invalid attribute value is equivalent to cols=20 rows=2');
+
+test(() => {
+ assert_less_than(cols1.offsetWidth, cols10.offsetWidth, '1 < 10');
+ assert_less_than(cols10.offsetWidth, cols20.offsetWidth, '10 < 20');
+ assert_less_than(cols20.offsetWidth, cols21.offsetWidth, '20 < 21');
+}, 'The width depends on a cols attribute');
+
+test(() => {
+ assert_less_than(rows1.offsetHeight, rows2.offsetHeight, '1 < 2');
+ assert_less_than(rows2.offsetHeight, rows3.offsetHeight, '2 < 3');
+}, 'The height depends on a rows attribute');
+
+test(() => {
+ const computedWidth = getComputedStyle(computed).width;
+ assert_equals(computed.offsetWidth,
+ parseInt(computedWidth.substring(0, computedWidth.length - 2)));
+
+ const computedHeight = getComputedStyle(computed).height;
+ assert_equals(computed.offsetHeight,
+ parseInt(computedHeight.substring(0, computedHeight.length - 2)));
+}, 'Cols/rows attribute values affect layout-dependent computed style');
+
+test(() => {
+ const computedNoneStyle = getComputedStyle(computedNone);
+ assert_equals(computedNoneStyle.width, 'auto');
+ assert_equals(computedNoneStyle.height, 'auto');
+}, 'Cols/rows attribute values are not presentational hints');
+</script>
+
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label-quirks.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label-quirks.html
new file mode 100644
index 0000000000..2c3c8093e2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label-quirks.html
@@ -0,0 +1,20 @@
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT -- Adding a label (quirks)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="option-label-ref.html">
+<meta name="assert" content="An option element is expected to be rendered by displaying the element's label.">
+
+<select>
+ <option>Element Text</option>
+</select>
+<br/>
+<select size="4">
+ <option>Element Text</option>
+</select>
+<script>
+let options = document.querySelectorAll("option");
+options[0].getBoundingClientRect(); // force layout.
+for (let option of options) {
+ option.setAttribute("label", "Label Text");
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label.html
new file mode 100644
index 0000000000..5110164c31
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-add-label.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT -- Adding a label</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="option-label-ref.html">
+<meta name="assert" content="An option element is expected to be rendered by displaying the element's label.">
+
+<select>
+ <option>Element Text</option>
+</select>
+<br/>
+<select size="4">
+ <option>Element Text</option>
+</select>
+<script>
+let options = document.querySelectorAll("option");
+options[0].getBoundingClientRect(); // force layout.
+for (let option of options) {
+ option.setAttribute("label", "Label Text");
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling-ref.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling-ref.html
new file mode 100644
index 0000000000..92504a47b5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Stylability of select>option with :checked pseudo</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+<style>
+ option {
+ color: black;
+ }
+ #option2 {
+ background-color: red;
+ }
+ #option1 {
+ background: green;
+ }
+</style>
+
+<select id=select size=3 multiple>
+ <option id=option1>Option #1 (should be green)</option>
+ <option id=option2>Option #2 (should be red)</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling.html
new file mode 100644
index 0000000000..216b03a88a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-checked-styling.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Stylability of select>option with :checked pseudo</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#the-select-element">
+<link rel="match" href="option-checked-styling-ref.html">
+
+<style>
+ option {
+ color: black;
+ background-color: red;
+ }
+ option:checked {
+ background: green;
+ }
+</style>
+
+<select id=select size=3 multiple>
+ <option id=option1 selected>Option #1 (should be green)</option>
+ <option id=option2>Option #2 (should be red)</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label-to-empty-string.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label-to-empty-string.html
new file mode 100644
index 0000000000..c8aa4d20b2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label-to-empty-string.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT -- Adding a label</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="option-label-ref.html">
+<meta name="assert" content="An option element is expected to be rendered by displaying the element's label.">
+
+<select>
+ <option label="Element Text">Label Text</option>
+</select>
+<br/>
+<select size="4">
+ <option label="Element Text">Label Text</option>
+</select>
+<script>
+let options = document.querySelectorAll("option");
+options[0].getBoundingClientRect(); // force layout.
+for (let option of options) {
+ option.setAttribute("label", "");
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label.html
new file mode 100644
index 0000000000..a34c41d299
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-empty-label.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT -- Empty label uses Element text</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="option-label-ref.html">
+<meta name="assert" content="An option element is expected to be rendered by displaying the element's label.">
+
+<select>
+ <option label>Label Text</option>
+</select>
+<br/>
+<select size="4">
+ <option label>Label Text</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-and-text.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-and-text.html
new file mode 100644
index 0000000000..152bfdcb6a
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-and-text.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT - Prefers label over element text</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="option-label-ref.html">
+<meta name="assert" content="An option element is expected to be rendered by displaying the element's label.">
+
+<select>
+ <option label="Label Text">Element Text</option>
+</select>
+<br/>
+<select size="4">
+ <option label="Label Text">Element Text</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-ref.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-ref.html
new file mode 100644
index 0000000000..3dd07b8dc2
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-label-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT (reference)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+
+<select>
+ <option>Label Text</option>
+</select>
+<br/>
+<select size="4">
+ <option>Label Text</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-only-label.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-only-label.html
new file mode 100644
index 0000000000..5e3c06cd66
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-only-label.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT -- Only a label</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="option-label-ref.html">
+<meta name="assert" content="An option element is expected to be rendered by displaying the element's label.">
+
+<select>
+ <option label="Label Text"></option>
+</select>
+<br/>
+<select size="4">
+ <option label="Label Text"></option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-rm-label.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-rm-label.html
new file mode 100644
index 0000000000..a5272cf6f1
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/option-rm-label.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OPTION's label attribute in SELECT -- Removing the label</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-select-element-2">
+<link rel="match" href="option-label-ref.html">
+<meta name="assert" content="An option element is expected to be rendered by displaying the element's label.">
+
+<select>
+ <option label="Bad Label Text">Label Text</option>
+</select>
+<br/>
+<select size="4">
+ <option label="Bad Label Text">Label Text</option>
+</select>
+<script>
+let options = document.querySelectorAll("option");
+options[0].getBoundingClientRect(); // force layout.
+for (let option of options) {
+ option.removeAttribute("label");
+}
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-as-listbox-default-styles.tentative.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-as-listbox-default-styles.tentative.html
new file mode 100644
index 0000000000..3e9b001f99
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-as-listbox-default-styles.tentative.html
@@ -0,0 +1,113 @@
+<!doctype html>
+<title>default styles for select as a listbox</title>
+<meta name="viewport" content="width=device-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/rendering/support/test-ua-stylesheet.js"></script>
+<style>
+/* Specify this bogus namespace, so the rules in this stylesheet only apply to the `fakeClone`d elements in #refs, not the HTML elements in #tests. */
+@namespace url(urn:not-html);
+
+select {
+ letter-spacing: initial;
+ word-spacing: initial;
+ line-height: initial;
+ text-transform: initial;
+ text-indent: initial;
+ text-shadow: initial;
+ appearance: auto;
+}
+
+select {
+ text-align: initial;
+}
+
+select {
+ box-sizing: border-box;
+}
+
+/* When the element renders as a list box, it is a devolvable widget expected to render as an 'inline-block' box whose 'height' is the height necessary to contain as many rows for items as given by the element's display size, or four rows if the attribute is absent, and whose 'width' is the width of the select's labels plus the width of a scrollbar. */
+select {
+ display: inline-block;
+}
+
+</style>
+
+<style>
+/* non-initial styles on parent to test 'initial' in UA stylesheet */
+#tests, #refs {
+ letter-spacing: 1px;
+ word-spacing: 1px;
+ line-height: 20px;
+ text-transform: lowercase;
+ text-indent: 1px;
+ text-shadow: 0px 0px;
+ text-align: justify;
+}
+</style>
+
+<div id="log"></div>
+
+<div id="tests">
+
+ <select multiple><option>1</option><optgroup label=2><option>3</select>
+
+</div>
+
+<div id="refs"></div>
+
+<script>
+ const props = [
+ 'display',
+ 'margin-top',
+ 'margin-right',
+ 'margin-bottom',
+ 'margin-left',
+ 'padding-top',
+ 'padding-right',
+ 'padding-bottom',
+ 'padding-left',
+ 'letter-spacing',
+ 'word-spacing',
+ 'text-transform',
+ 'text-indent',
+ 'text-shadow',
+ 'appearance',
+ 'box-sizing',
+ 'border-top-width',
+ 'border-right-width',
+ 'border-bottom-width',
+ 'border-left-width',
+ 'border-top-style',
+ 'border-right-style',
+ 'border-bottom-style',
+ 'border-left-style',
+ 'border-top-color',
+ 'border-right-color',
+ 'border-bottom-color',
+ 'border-left-color',
+ 'align-items',
+ 'white-space',
+ 'color',
+ 'background-color',
+ 'cursor',
+ 'font-style',
+ 'font-weight',
+ 'font-size',
+ 'font-family',
+ 'writing-mode',
+ 'scrollbar-width',
+ 'overflow',
+ 'vertical-align',
+ 'user-select',
+ 'page-break-inside',
+ 'overflow-clip-box',
+ 'font-variant-ligatures',
+ 'font-variant-caps',
+ 'font-variant-numeric',
+ 'font-variant-east-asian',
+ 'text-rendering',
+ ];
+ runUAStyleTests(props);
+
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation-ref.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation-ref.html
new file mode 100644
index 0000000000..7a3dd1d50d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Select rendering invalidation</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+<style>
+ select {
+ color: lime;
+ }
+</style>
+
+<select id=select>
+ <option>The down arrow should be green</option>
+ <option>value B</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation.html
new file mode 100644
index 0000000000..199db0b7aa
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-invalidation.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Select rendering invalidation</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#the-select-element">
+<link rel="match" href="select-invalidation-ref.html">
+
+<style>
+ select {
+ color: red;
+ }
+</style>
+
+<select id=select>
+ <option>The down arrow should be green</option>
+ <option>value B</option>
+</select>
+
+<script>
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ select.style.color = "lime";
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ });
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-001.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-001.html
new file mode 100644
index 0000000000..ad4055415d
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-001.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>select size=1 renders the same as plain select</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1643279">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="match" href="select-size-ref.html">
+<select size="1">
+ <option value ="1">1</option>
+ <option value ="2">2</option>
+ <option value ="3">3</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-002.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-002.html
new file mode 100644
index 0000000000..0838e7a3c6
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-002.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>select size=0 renders the same as plain select</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1643279">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="match" href="select-size-ref.html">
+<select size="0">
+ <option value ="1">1</option>
+ <option value ="2">2</option>
+ <option value ="3">3</option>
+</select>
diff --git a/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-ref.html b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-ref.html
new file mode 100644
index 0000000000..fc3b3be6e5
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/widgets/the-select-element/select-size-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Test reference.</title>
+<select>
+ <option value ="1">1</option>
+ <option value ="2">2</option>
+ <option value ="3">3</option>
+</select>
diff --git a/testing/web-platform/tests/html/resources/common.js b/testing/web-platform/tests/html/resources/common.js
new file mode 100644
index 0000000000..4f9ec54dde
--- /dev/null
+++ b/testing/web-platform/tests/html/resources/common.js
@@ -0,0 +1,201 @@
+"use strict";
+
+const HTML5_ELEMENTS = [
+ 'a', 'abbr', 'address', 'area', 'article', 'aside',
+ 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote',
+ 'body', 'br', 'button', 'canvas', 'caption', 'cite',
+ 'code', 'col', 'colgroup', 'data', 'datalist', 'dd',
+ 'del', 'details', 'dfn', 'dialog', 'div', 'dl',
+ 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure',
+ 'footer', 'form', 'h1', 'h2', 'h3', 'h4',
+ 'h5', 'h6', 'head', 'header', 'hr', 'html',
+ 'i', 'iframe', 'img', 'input', 'ins', 'kbd',
+ 'label', 'legend', 'li', 'link', 'main', 'map',
+ 'mark', 'menu', 'meta', 'meter', 'nav', 'noscript',
+ 'object', 'ol', 'optgroup', 'option', 'output', 'p',
+ 'param', 'pre', 'progress', 'q', 'rp', 'rt',
+ 'ruby', 's', 'samp', 'script', 'section', 'select',
+ 'slot', 'small', 'source', 'span', 'strong', 'style',
+ 'sub', 'sup', 'summary', 'table', 'tbody', 'td',
+ 'template', 'textarea', 'tfoot', 'th', 'thead', 'time',
+ 'title', 'tr', 'track', 'u', 'ul', 'var',
+ 'video', 'wbr'
+];
+
+// only void (without end tag) HTML5 elements
+var HTML5_VOID_ELEMENTS = [
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta',
+ 'param', 'source', 'track', 'wbr'
+];
+
+// https://html.spec.whatwg.org/multipage/multipage/forms.html#form-associated-element
+var HTML5_FORM_ASSOCIATED_ELEMENTS = [ 'button', 'fieldset', 'input',
+ 'object', 'output', 'select', 'textarea' ];
+
+const HTML5_SHADOW_ALLOWED_ELEMENTS = [
+ 'article', 'aside', 'blockquote', 'body', 'div', 'footer', 'h1', 'h2', 'h3',
+ 'h4', 'h5', 'h6', 'header', 'main', 'nav', 'p', 'section', 'span'
+];
+
+const HTML5_SHADOW_DISALLOWED_ELEMENTS =
+ HTML5_ELEMENTS.filter(el => !HTML5_SHADOW_ALLOWED_ELEMENTS.includes(el));
+
+// These are *deprecated/removed* HTML5 element names.
+const HTML5_DEPRECATED_ELEMENTS = [
+ 'acronym', 'applet', 'basefont', 'bgsound', 'big', 'blink',
+ 'center', 'command', 'content', 'dir', 'font', 'frame',
+ 'frameset', 'hgroup', 'image', 'isindex', 'keygen', 'marquee',
+ 'menuitem', 'nobr', 'noembed', 'noframes', 'plaintext', 'rb',
+ 'rtc', 'shadow', 'spacer', 'strike', 'tt', 'xmp'
+];
+
+function newDocument() {
+ var d = document.implementation.createDocument();
+ return d;
+}
+
+function newHTMLDocument() {
+ var d = document.implementation.createHTMLDocument('Test Document');
+ return d;
+}
+
+function newXHTMLDocument() {
+ var doctype = document.implementation.createDocumentType('html',
+ '-//W3C//DTD XHTML 1.0 Transitional//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd');
+
+ var d = document.implementation.createDocument(
+ 'http://www.w3.org/1999/xhtml', 'html', doctype);
+ return d;
+}
+
+function newIFrame(context, src) {
+ if (typeof (context) === 'undefined'
+ || typeof (context.iframes) !== 'object') {
+ assert_unreached('Illegal context object in newIFrame');
+ }
+
+ var iframe = document.createElement('iframe');
+
+ if (typeof (src) != 'undefined') {
+ iframe.src = src;
+ }
+ document.body.appendChild(iframe);
+ context.iframes.push(iframe);
+
+ assert_true(typeof (iframe.contentWindow) != 'undefined'
+ && typeof (iframe.contentWindow.document) != 'undefined'
+ && iframe.contentWindow.document != document,
+ 'Failed to create new rendered document');
+ return iframe;
+}
+
+function newRenderedHTMLDocument(context) {
+ var frame = newIFrame(context);
+ var d = frame.contentWindow.document;
+ return d;
+}
+
+function newContext() {
+ return {
+ iframes : []
+ };
+}
+
+function cleanContext(context) {
+ context.iframes.forEach(function(e) {
+ e.parentNode.removeChild(e);
+ });
+}
+
+// run given test function in context
+// the context is cleaned up after test completes.
+function inContext(f) {
+ return function() {
+ var context = newContext();
+ try {
+ f(context);
+ } finally {
+ cleanContext(context);
+ }
+ };
+}
+
+// new context and iframe are created and url (if supplied) is asigned to
+// iframe.src
+// function f is bound to the iframe onload event or executed directly after
+// iframe creation
+// the context is passed to function as argument
+function testInIFrame(url, f, testName, testProps) {
+ if (url) {
+ var t = async_test(testName);
+ t.step(function() {
+ var context = newContext();
+ var iframe = newIFrame(context, url);
+ iframe.onload = t.step_func(function() {
+ try {
+ f(context);
+ t.done();
+ } finally {
+ cleanContext(context);
+ }
+ });
+ });
+ } else {
+ test(inContext(function(context) {
+ newRenderedHTMLDocument(context);
+ f(context);
+ }), testName);
+ }
+}
+
+function assert_nodelist_contents_equal_noorder(actual, expected, message) {
+ assert_equals(actual.length, expected.length, message);
+ var used = [];
+ for ( var i = 0; i < expected.length; i++) {
+ used.push(false);
+ }
+ for (i = 0; i < expected.length; i++) {
+ var found = false;
+ for ( var j = 0; j < actual.length; j++) {
+ if (used[j] == false && expected[i] == actual[j]) {
+ used[j] = true;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ assert_unreached(message + ". Fail reason: element not found: "
+ + expected[i]);
+ }
+ }
+}
+
+function isVoidElement(elementName) {
+ return HTML5_VOID_ELEMENTS.indexOf(elementName) >= 0;
+}
+
+function checkTemplateContent(d, obj, html, id, nodeName) {
+
+ obj.innerHTML = '<template id="tmpl">' + html + '</template>';
+
+ var t = d.querySelector('#tmpl');
+
+ if (id != null) {
+ assert_equals(t.content.childNodes.length, 1, 'Element ' + nodeName
+ + ' should present among template nodes');
+ assert_equals(t.content.firstChild.id, id, 'Wrong element ID');
+ }
+ if (nodeName != null) {
+ assert_equals(t.content.firstChild.nodeName, nodeName.toUpperCase(),
+ 'Wrong node name');
+ }
+}
+
+function checkBodyTemplateContent(d, html, id, nodeName) {
+ checkTemplateContent(d, d.body, html, id, nodeName);
+}
+
+function checkHeadTemplateContent(d, html, id, nodeName) {
+ checkTemplateContent(d, d.head, html, id, nodeName);
+}
diff --git a/testing/web-platform/tests/html/scripting/the-script-element/attr-script-fetchpriority.html b/testing/web-platform/tests/html/scripting/the-script-element/attr-script-fetchpriority.html
new file mode 100644
index 0000000000..738c9eab74
--- /dev/null
+++ b/testing/web-platform/tests/html/scripting/the-script-element/attr-script-fetchpriority.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Fetch Priority - Script element</title>
+<meta name="author" title="Patrick Meenan" href="mailto:patmeenan@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script id=script1 src="resources/script.js" fetchpriority=high></script>
+<script id=script2 src="resources/script.js" fetchpriority=low></script>
+<script id=script3 src="resources/script.js" fetchpriority=auto></script>
+<script id=script4 src="resources/script.js" fetchpriority=xyz></script>
+<script id=script5 src="resources/script.js"></script>
+
+<script>
+ test(() => {
+ assert_equals(script1.fetchPriority, "high", "high fetchPriority is a valid IDL value on the script element");
+ assert_equals(script2.fetchPriority, "low", "low fetchPriority is a valid IDL value on the script element");
+ assert_equals(script3.fetchPriority, "auto", "auto fetchPriority is a valid IDL value on the script element");
+ assert_equals(script4.fetchPriority, "auto", "invalid fetchPriority reflects as 'auto' IDL attribute on the script element");
+ assert_equals(script5.fetchPriority, "auto", "missing fetchPriority reflects as 'auto' IDL attribute on the script element");
+ }, "fetchpriority attribute on <script> elements should reflect valid IDL values");
+
+ test(() => {
+ const script = document.createElement("script");
+ assert_equals(script.fetchPriority, "auto");
+ }, "default fetchpriority attribute on <script> elements should be 'auto'");
+</script>
diff --git a/testing/web-platform/tests/html/scripting/the-script-element/resources/script.js b/testing/web-platform/tests/html/scripting/the-script-element/resources/script.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/html/scripting/the-script-element/resources/script.js
diff --git a/testing/web-platform/tests/html/select/options-length-too-large.html b/testing/web-platform/tests/html/select/options-length-too-large.html
new file mode 100644
index 0000000000..7ce33ffad4
--- /dev/null
+++ b/testing/web-platform/tests/html/select/options-length-too-large.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>select options.length too large</title>
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <select id="test">
+ <option value="1"></option>
+ <option value="2"></option>
+ <option value="3"></option>
+ </select>
+
+ <script>
+ var mySelect = document.getElementById("test");
+
+ test(function() {
+ mySelect.options.length = -1;
+ assert_equals(mySelect.options.length, 3, "Length of <select> should remain unchanged");
+ });
+
+ test(function() {
+ mySelect.options.length = 100001;
+ assert_equals(mySelect.options.length, 3, "Length of <select> should remain unchanged");
+ });
+
+ test(function() {
+ mySelect.options.length = Number.MAX_SAFE_INTEGER;
+ assert_equals(mySelect.options.length, 3, "Length of <select> should remain unchanged");
+ });
+
+ test(function() {
+ mySelect.options.length = 100000;
+ assert_equals(mySelect.options.length, 100000, "Length of <select> should be 100,000");
+ });
+
+ test(function() {
+ mySelect.appendChild(new Option());
+ mySelect.appendChild(new Option());
+ assert_equals(mySelect.options.length, 100002, "Manual expansion still works");
+ mySelect.options.length = 100001;
+ assert_equals(mySelect.options.length, 100001, "Truncation works if over the limit");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/disabled-checkbox-click.html b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-checkbox-click.html
new file mode 100644
index 0000000000..805770c854
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-checkbox-click.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<link rel="author" title="Aditya Keerthi" href="https://github.com/pxlcoder">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute">
+<title>Test disabled checkbox does not change state when clicked</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input type="checkbox" disabled>
+<script>
+const input = document.querySelector("input");
+
+promise_test(async function() {
+ assert_false(input.checked);
+
+ await new test_driver.Actions()
+ .pointerMove(0, 0, { origin: input })
+ .pointerDown()
+ .pointerUp()
+ .send();
+
+ assert_false(input.checked);
+}, `Disabled checkbox does not change state when clicked`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch-additional.tentative.html b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch-additional.tentative.html
new file mode 100644
index 0000000000..8ef4e5ccc7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch-additional.tentative.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/2368">
+<link rel=help href="https://github.com/whatwg/html/issues/5886">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<!-- This test should be merged with disabled-event-dispatch.tentative.html after interop2023 is over. -->
+
+<div id=targetparent>
+ <button disabled>
+ hello world
+ <span style="border: 1px solid black">child</span>
+ </button>
+ <my-control disabled>
+ hello world
+ <span style="border: 1px solid black">child</span>
+ </my-control>
+</div>
+
+<script>
+customElements.define('my-control', class extends HTMLElement {
+ static get formAssociated() { return true; }
+});
+
+['dblclick', 'auxclick'].forEach(eventName => {
+ [true, false].forEach(clickChildElement => {
+ for (const target of targetparent.children) {
+ promise_test(async () => {
+ let parentReceivedEvent = false;
+ targetparent.addEventListener(eventName, () => parentReceivedEvent = true);
+
+ let targetReceivedEvent = false;
+ target.addEventListener(eventName, () => targetReceivedEvent = true);
+
+ let childReceivedEvent = false;
+ let targetchild = target.firstElementChild;
+ targetchild.addEventListener(eventName, () => childReceivedEvent = true);
+
+ const elementToClick = clickChildElement ? targetchild : target;
+ if (eventName === 'dblclick') {
+ await (new test_driver.Actions()
+ .pointerMove(1, 1, {origin: elementToClick})
+ .pointerDown()
+ .pointerUp()
+ .pointerDown()
+ .pointerUp())
+ .send();
+ } else if (eventName === 'auxclick') {
+ const actions = new test_driver.Actions();
+ await actions
+ .pointerMove(1, 1, {origin: elementToClick})
+ .pointerDown({button: actions.ButtonType.MIDDLE})
+ .pointerUp({button: actions.ButtonType.MIDDLE})
+ .send();
+ }
+
+
+ const shouldReceiveEvents = eventName.startsWith('pointer') || eventName === 'auxclick';
+ assert_equals(parentReceivedEvent, shouldReceiveEvents,
+ `parent element received ${eventName} events`);
+ assert_equals(targetReceivedEvent, shouldReceiveEvents,
+ `target element received ${eventName} events`);
+ assert_equals(childReceivedEvent, clickChildElement,
+ `child element received ${eventName} events`);
+ }, `Testing ${eventName} events when clicking ${clickChildElement ? 'child of ' : ''}disabled ${target.localName}.`);
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch.tentative.html b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch.tentative.html
new file mode 100644
index 0000000000..e2b8846fc3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch.tentative.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/2368">
+<link rel=help href="https://github.com/whatwg/html/issues/5886">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<div id=targetparent>
+ <button disabled>
+ hello world
+ <span style="border: 1px solid black">child</span>
+ </button>
+ <my-control disabled>
+ hello world
+ <span style="border: 1px solid black">child</span>
+ </my-control>
+</div>
+
+<script>
+customElements.define('my-control', class extends HTMLElement {
+ static get formAssociated() { return true; }
+});
+
+['mousedown', 'mouseup', 'pointerdown', 'pointerup', 'click'].forEach(eventName => {
+ [true, false].forEach(clickChildElement => {
+ for (const target of targetparent.children) {
+ promise_test(async () => {
+ let parentReceivedEvent = false;
+ targetparent.addEventListener(eventName, () => parentReceivedEvent = true);
+
+ let targetReceivedEvent = false;
+ target.addEventListener(eventName, () => targetReceivedEvent = true);
+
+ let childReceivedEvent = false;
+ let targetchild = target.firstElementChild;
+ targetchild.addEventListener(eventName, () => childReceivedEvent = true);
+
+ await test_driver.click(clickChildElement ? targetchild : target);
+
+ const parentShouldReceiveEvents = eventName.startsWith('pointer');
+ assert_equals(parentReceivedEvent, parentShouldReceiveEvents,
+ `parent element received ${eventName} events`);
+
+ const targetShouldReceiveEvents = eventName.startsWith('pointer');
+ assert_equals(targetReceivedEvent, targetShouldReceiveEvents,
+ `target element received ${eventName} events`);
+ assert_equals(childReceivedEvent, clickChildElement,
+ `child element received ${eventName} events`);
+ }, `Testing ${eventName} events when clicking ${clickChildElement ? 'child of ' : ''}disabled ${target.localName}.`);
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/disabledElement.html b/testing/web-platform/tests/html/semantics/disabled-elements/disabledElement.html
new file mode 100644
index 0000000000..03f57424d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/disabled-elements/disabledElement.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Disabled elements</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#disabled-elements">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<button disabled>button</button>
+<input disabled>
+<select disabled>
+ <optgroup label="options" disabled>
+ <option value="option1" disabled>option1
+ <option value="option2">option2
+</select>
+<textarea disabled>textarea</textarea>
+<fieldset disabled>
+ <input type=radio name=c value=0 checked>
+ <input type=radio name=c value=1>
+</fieldset>
+<a href="http://www.w3.org/" disabled>w3</a>
+<span tabindex=0 disabled>foobar</span>
+
+<script>
+ test(function(){
+ assert_equals(document.activeElement, document.body);
+ }, "The body element must be the active element if no element is focused");
+
+ ["button", "input", "select", "optgroup", "option", "textarea", "input[type=radio]"].forEach(function(el) {
+ test(function() {
+ var element = document.querySelector(el);
+ element.focus();
+ assert_equals(document.activeElement, document.body, "activeElement after focus on a disabled <" + el + "> remains unchanged");
+ }, "A disabled <" + el + "> should not be focusable");
+ });
+
+ ["a", "span"].forEach(function(el) {
+ test(function() {
+ var element = document.querySelector(el);
+ element.focus();
+ assert_equals(document.activeElement, element, "focus on a <" + el + "> with a disabled attribute should make it the activeElement");
+ }, "A disabled <" + el + "> should be focusable");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled-keyboard.tentative.html b/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled-keyboard.tentative.html
new file mode 100644
index 0000000000..3aacf21f1d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled-keyboard.tentative.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>KeyboardEvent propagation on disabled form elements</title>
+<link rel="author" href="mailto:avandolder@mozilla.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script>
+ class CustomControl extends HTMLElement {
+ static get formAssociated() {return true;}
+
+ constructor() {
+ super();
+ this.internals = this.attachInternals();
+
+ this.attachShadow({mode: "open", delegatesFocus: true});
+ this.shadowRoot.append(
+ document.querySelector("template").content.cloneNode(true)
+ );
+ }
+
+ get target() {
+ return this.shadowRoot.getElementById("target");
+ }
+ }
+
+ customElements.define("custom-control", CustomControl)
+</script>
+
+<template>
+ <div tabindex="0" id="target">
+ <slot></slot>
+ </div>
+</template>
+
+<div tabindex="0" id="reset"></div>
+
+<form id="form">
+ <input> <!-- Sanity check with non-disabled control -->
+ <select disabled></select>
+ <textarea disabled></textarea>
+ <input disabled type="button">
+ <input disabled type="checkbox">
+ <input disabled type="color" value="#000000">
+ <input disabled type="date">
+ <input disabled type="datetime-local">
+ <input disabled type="email">
+ <input disabled type="file">
+ <input disabled type="image">
+ <input disabled type="month">
+ <input disabled type="number">
+ <input disabled type="password">
+ <input disabled type="radio">
+ <input disabled type="range" value="0">
+ <input disabled type="reset">
+ <input disabled type="search">
+ <input disabled type="submit">
+ <input disabled type="tel">
+ <input disabled type="text">
+ <input disabled type="time">
+ <input disabled type="url">
+ <input disabled type="week">
+
+ <fieldset disabled><span tabindex="0">Span</span></fieldset>
+ <button disabled><span tabindex="0">Span</span></button>
+ <custom-control disabled>Text</custom-control>
+</form>
+
+<script>
+ const keyEvents = ["keydown", "keyup"];
+
+ function setupTest(t, element, observingElement) {
+ const observedEvents = [];
+ const controller = new AbortController();
+ const {signal} = controller;
+ const listenerFn = t.step_func(event => {
+ observedEvents.push(event.type);
+ });
+ for (const event of keyEvents) {
+ observingElement.addEventListener(event, listenerFn, {signal});
+ }
+ t.add_cleanup(() => controller.abort());
+
+ const target = element;
+ return {target, observedEvents};
+ }
+
+ function fire_trusted_key_events(target) {
+ const actions = new test_driver.Actions();
+ return actions.keyDown("a").keyUp("a").send();
+ }
+
+ function fire_untrusted_key_events(target) {
+ target.dispatchEvent(new KeyboardEvent("keydown", {bubbles: true}));
+ target.dispatchEvent(new KeyboardEvent("keyup", {bubbles: true}));
+ }
+
+ const observingElement = document.getElementById("form");
+ const reset = document.getElementById("reset");
+
+ for (const element of observingElement.children) {
+ promise_test(async t => {
+ const {observedEvents} = setupTest(t, element, observingElement);
+
+ const target = element.firstElementChild ?? element.target ?? element;
+ await t.step_func(fire_untrusted_key_events)(target);
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+
+ assert_array_equals(observedEvents, keyEvents, "Observed events");
+ }, `Untrusted key events on ${element.outerHTML}, observed from <${observingElement.localName}>`);
+
+ // Only test elements with children for trusted key events.
+ if (!element.firstElementChild && !element.target) {
+ continue;
+ }
+
+ promise_test(async t => {
+ const {observedEvents} = setupTest(t, element, observingElement);
+
+ const target = element.firstElementChild ?? element.target;
+
+ reset.focus();
+ assert_not_equals(document.activeElement, target, "Reset current focus");
+ target.focus();
+ assert_equals(document.activeElement, element.firstElementChild ?? element, "Focus the target element");
+
+ await t.step_func(fire_trusted_key_events)(target);
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+
+ assert_array_equals(observedEvents, keyEvents, "Observed events");
+ }, `Trusted key events on ${element.outerHTML}, observed from <${observingElement.localName}>`);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled.tentative.html b/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled.tentative.html
new file mode 100644
index 0000000000..e3dcd43a17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled.tentative.html
@@ -0,0 +1,264 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<meta name="timeout" content="long">
+<title>Event propagation on disabled form elements</title>
+<link rel="author" href="mailto:krosylight@mozilla.com">
+<link rel="help" href="https://github.com/whatwg/html/issues/2368">
+<link rel="help" href="https://github.com/whatwg/html/issues/5886">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<div id="cases">
+ <input> <!-- Sanity check with non-disabled control -->
+ <select disabled></select>
+ <select disabled>
+ <!-- <option> can't be clicked as it doesn't have its own painting area -->
+ <option>foo</option>
+ </select>
+ <fieldset disabled>Text</fieldset>
+ <fieldset disabled><span class="target">Span</span></fieldset>
+ <button disabled>Text</button>
+ <button disabled><span class="target">Span</span></button>
+ <textarea disabled></textarea>
+ <input disabled type="button">
+ <input disabled type="checkbox">
+ <input disabled type="color" value="#000000">
+ <input disabled type="date">
+ <input disabled type="datetime-local">
+ <input disabled type="email">
+ <input disabled type="file">
+ <input disabled type="image">
+ <input disabled type="month">
+ <input disabled type="number">
+ <input disabled type="password">
+ <input disabled type="radio">
+ <!-- Native click will click the bar -->
+ <input disabled type="range" value="0">
+ <!-- Native click will click the slider -->
+ <input disabled type="range" value="50">
+ <input disabled type="reset">
+ <input disabled type="search">
+ <input disabled type="submit">
+ <input disabled type="tel">
+ <input disabled type="text">
+ <input disabled type="time">
+ <input disabled type="url">
+ <input disabled type="week">
+ <my-control disabled>Text</my-control>
+</div>
+
+<script>
+ customElements.define('my-control', class extends HTMLElement {
+ static get formAssociated() { return true; }
+ get disabled() { return this.hasAttribute("disabled"); }
+ });
+
+ /**
+ * @param {Element} element
+ */
+ function getEventFiringTarget(element) {
+ return element.querySelector(".target") || element;
+ }
+
+ const allEvents = ["pointermove", "mousemove", "pointerdown", "mousedown", "pointerup", "mouseup", "click"];
+
+ /**
+ * @param {*} t
+ * @param {Element} element
+ * @param {Element} observingElement
+ */
+ function setupTest(t, element, observingElement) {
+ /** @type {{type: string, composedPath: Node[]}[]} */
+ const observedEvents = [];
+ const controller = new AbortController();
+ const { signal } = controller;
+ const listenerFn = t.step_func(event => {
+ observedEvents.push({
+ type: event.type,
+ target: event.target,
+ isTrusted: event.isTrusted,
+ composedPath: event.composedPath().map(n => n.constructor.name),
+ });
+ });
+ for (const event of allEvents) {
+ observingElement.addEventListener(event, listenerFn, { signal });
+ }
+ t.add_cleanup(() => controller.abort());
+
+ const target = getEventFiringTarget(element);
+ return { target, observedEvents };
+ }
+
+ /**
+ * @param {Element} element
+ * @returns {boolean}
+ */
+ function isFormControl(element) {
+ if (["button", "input", "select", "textarea"].includes(element.localName)) {
+ return true;
+ }
+ return element.constructor.formAssociated;
+ }
+
+ function isDisabledFormControl(element) {
+ return isFormControl(element) && element.disabled;
+ }
+
+ /**
+ * @param {Element} target
+ * @param {*} observedEvent
+ */
+ function shouldNotBubble(target, observedEvent) {
+ return (
+ isDisabledFormControl(target) &&
+ observedEvent.isTrusted &&
+ ["mousedown", "mouseup", "click"].includes(observedEvent.type)
+ );
+ }
+
+ /**
+ * @param {Event} event
+ */
+ function getExpectedComposedPath(event) {
+ let target = event.target;
+ const result = [];
+ while (target) {
+ if (shouldNotBubble(target, event)) {
+ return result;
+ }
+ result.push(target.constructor.name);
+ target = target.parentNode;
+ }
+ result.push("Window");
+ return result;
+ }
+
+ /**
+ * @param {object} options
+ * @param {Element & { disabled: boolean }} options.element
+ * @param {Element} options.observingElement
+ * @param {string[]} options.expectedEvents
+ * @param {(target: Element) => (Promise<void> | void)} options.clickerFn
+ * @param {string} options.title
+ */
+ function promise_event_test({ element, observingElement, expectedEvents, nonDisabledExpectedEvents, clickerFn, title }) {
+ promise_test(async t => {
+ const { target, observedEvents } = setupTest(t, element, observingElement);
+
+ await t.step_func(clickerFn)(target);
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+
+ const expected = isDisabledFormControl(element) ? expectedEvents : nonDisabledExpectedEvents;
+ assert_array_equals(observedEvents.map(e => e.type), expected, "Observed events");
+
+ for (const observed of observedEvents) {
+ assert_equals(observed.target, target, `${observed.type}.target`)
+ assert_array_equals(
+ observed.composedPath,
+ getExpectedComposedPath(observed),
+ `${observed.type}.composedPath`
+ );
+ }
+
+ }, `${title} on ${element.outerHTML}, observed from <${observingElement.localName}>`);
+ }
+
+ /**
+ * @param {object} options
+ * @param {Element & { disabled: boolean }} options.element
+ * @param {string[]} options.expectedEvents
+ * @param {(target: Element) => (Promise<void> | void)} options.clickerFn
+ * @param {string} options.title
+ */
+ function promise_event_test_hierarchy({ element, expectedEvents, nonDisabledExpectedEvents, clickerFn, title }) {
+ const targets = [element, document.body];
+ if (element.querySelector(".target")) {
+ targets.unshift(element.querySelector(".target"));
+ }
+ for (const observingElement of targets) {
+ promise_event_test({ element, observingElement, expectedEvents, nonDisabledExpectedEvents, clickerFn, title });
+ }
+ }
+
+ function trusted_click(target) {
+ // To workaround type=file clicking issue
+ // https://github.com/w3c/webdriver/issues/1666
+ return new test_driver.Actions()
+ .pointerMove(0, 0, { origin: target })
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }
+
+ const mouseEvents = ["mousemove", "mousedown", "mouseup", "click"];
+ const pointerEvents = ["pointermove", "pointerdown", "pointerup"];
+
+ // Events except mousedown/up/click
+ const allowedEvents = ["pointermove", "mousemove", "pointerdown", "pointerup"];
+
+ const elements = document.getElementById("cases").children;
+ for (const element of elements) {
+ // Observe on a child element of the control, if exists
+ const target = element.querySelector(".target");
+ if (target) {
+ promise_event_test({
+ element,
+ observingElement: target,
+ expectedEvents: allEvents,
+ nonDisabledExpectedEvents: allEvents,
+ clickerFn: trusted_click,
+ title: "Trusted click"
+ });
+ }
+
+ // Observe on the control itself
+ promise_event_test({
+ element,
+ observingElement: element,
+ expectedEvents: allowedEvents,
+ nonDisabledExpectedEvents: allEvents,
+ clickerFn: trusted_click,
+ title: "Trusted click"
+ });
+
+ // Observe on document.body
+ promise_event_test({
+ element,
+ observingElement: document.body,
+ expectedEvents: allowedEvents,
+ nonDisabledExpectedEvents: allEvents,
+ clickerFn: trusted_click,
+ title: "Trusted click"
+ });
+
+ const eventFirePair = [
+ [MouseEvent, mouseEvents],
+ [PointerEvent, pointerEvents]
+ ];
+
+ for (const [eventInterface, events] of eventFirePair) {
+ promise_event_test_hierarchy({
+ element,
+ expectedEvents: events,
+ nonDisabledExpectedEvents: events,
+ clickerFn: target => {
+ for (const event of events) {
+ target.dispatchEvent(new eventInterface(event, { bubbles: true }))
+ }
+ },
+ title: `Dispatch new ${eventInterface.name}()`
+ })
+ }
+
+ promise_event_test_hierarchy({
+ element,
+ expectedEvents: getEventFiringTarget(element) === element ? [] : ["click"],
+ nonDisabledExpectedEvents: ["click"],
+ clickerFn: target => target.click(),
+ title: `click()`
+ })
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/fieldset-event-propagation.tentative.html b/testing/web-platform/tests/html/semantics/disabled-elements/fieldset-event-propagation.tentative.html
new file mode 100644
index 0000000000..6d1a39c1de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/disabled-elements/fieldset-event-propagation.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled">
+<link rel=help href="https://github.com/whatwg/html/issues/5886#issuecomment-1460425364">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<div id=target1parent>
+ <fieldset disabled id=target1fieldset>
+ <div id=target1child>hello world</div>
+ </fieldset>
+</div>
+
+<div id=target2parent>
+ <fieldset disabled id=target2fieldset>hello world</fieldset>
+</div>
+
+<script>
+ const clickers = {
+ "native click": target => test_driver.click(target),
+ "click()": target => target.click(),
+ };
+
+ for (const [clickerName, clicker] of Object.entries(clickers)) {
+ promise_test(async () => {
+ let target1parentClicked = false;
+ let target1childClicked = false;
+ let target1fieldsetClicked = false;
+ target1parent.onclick = () => target1parentClicked = true;
+ target1child.onclick = () => target1childClicked = true;
+ target1fieldset.onclick = () => target1fieldsetClicked = true;
+
+ await clicker(target1child);
+
+ assert_true(target1parentClicked, 'The parent of the fieldset should receive a click event.');
+ assert_true(target1childClicked, 'The child of the fieldset should receive a click event.');
+ assert_true(target1fieldsetClicked, 'The fieldset element should receive a click event.');
+ }, `Disabled fieldset elements should not prevent click event propagation from ${clickerName}`);
+
+ promise_test(async () => {
+ let target2parentClicked = false;
+ let target2fieldsetClicked = false;
+ target2parent.onclick = () => target2parentClicked = true;
+ target2fieldset.onclick = () => target2fieldsetClicked = true;
+
+ await clicker(target2fieldset);
+
+ assert_true(target2parentClicked, 'The parent of the fieldset should receive a click event.');
+ assert_true(target2fieldsetClicked, 'The fieldset element should receive a click event.');
+ }, `Disabled fieldset elements should not block click events from ${clickerName}.`);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/conditionally-block-rendering-on-link-media-attr.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/conditionally-block-rendering-on-link-media-attr.html
new file mode 100644
index 0000000000..d21df46d30
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/conditionally-block-rendering-on-link-media-attr.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="support/utils.js"></script>
+
+<link rel=stylesheet href=stylesheet.py>
+<link rel=stylesheet media="screen and (max-width:10px)" href=stylesheet.py?stylesNotMatchingEnvironment&delay=2>
+<h1>Dominic Farolino</h1>
+<script>
+ test(() => {
+ const h1 = document.querySelector('h1');
+ const computedColor = getComputedStyle(h1).color;
+ const expectedColor = "rgb(128, 0, 128)";
+
+ assert_equals(computedColor, expectedColor);
+ assert_true(styleExists("h1 { color: purple; }")); // first style sheet
+ assert_false(styleExists("h1 { color: brown; }")); // second style sheet (should not be loaded yet)
+ }, "Only the style sheet loaded via a link element whose media attribute matches the environment should block following script execution");
+
+ const secondStylesheetTest = async_test("Both style sheets loaded via the link elements should be registered as style sheets for the document after 2 seconds");
+ secondStylesheetTest.step_timeout(() => {
+ assert_true(styleExists("h1 { color: purple; }")); // first style sheet
+ assert_true(styleExists("h1 { color: brown; }")); // second style sheet (loaded now!)
+ secondStylesheetTest.done();
+ }, 3000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-link-stylesheet-does-not-block-script.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-link-stylesheet-does-not-block-script.html
new file mode 100644
index 0000000000..bcc98050ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-link-stylesheet-does-not-block-script.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Script-created render-blocking link stylesheet is not script-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<script>
+const link = document.createElement('link');
+link.rel = 'stylesheet';
+link.href = 'stylesheet.py?delay=1';
+link.blocking = 'render';
+document.head.appendChild(link);
+</script>
+<h1>Some text</h1>
+<script>
+test(() => {
+ assert_false(styleExists("h1 { color: purple; }"),
+ 'stylesheet should still be pending');
+ const h1 = document.querySelector('h1');
+ assert_equals(getComputedStyle(h1).color, 'rgb(0, 0, 0)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-style-element-does-not-block-script.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-style-element-does-not-block-script.html
new file mode 100644
index 0000000000..9a8c4b466b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/dynamic-render-blocking-style-element-does-not-block-script.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Script-created render-blocking style element is not script-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<script>
+const style = document.createElement('style');
+const sheet = document.createTextNode('@import url(stylesheet.py?delay=1);');
+style.appendChild(sheet);
+style.blocking = 'render';
+document.head.appendChild(style);
+</script>
+<h1>Some text</h1>
+<script>
+test(() => {
+ assert_false(styleExists("h1 { color: purple; }"),
+ 'stylesheet should still be pending');
+ const h1 = document.querySelector('h1');
+ assert_equals(getComputedStyle(h1).color, 'rgb(0, 0, 0)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/link-stylesheet-with-non-match-media-does-not-block-render.tentative.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/link-stylesheet-with-non-match-media-does-not-block-render.tentative.html
new file mode 100644
index 0000000000..7a881bcc3d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/link-stylesheet-with-non-match-media-does-not-block-render.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>
+ Delayed Stylesheet imported using link tag should not block rendering
+ or JS execution when media doesn't match.
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<link rel="stylesheet" href="support/link-style.css?pipe=trickle(d1)"
+ media="print" onload="this.media='all'">
+<h1>
+ This text is black in color till stylesheet is fetched.
+</h1>
+<script>
+test(() => {
+ assert_false(styleExists("h1 { color: purple; }"),
+ 'Stylesheet should still be pending due to delay');
+ const h1 = document.querySelector('h1');
+ assert_equals(getComputedStyle(h1).color, 'rgb(0, 0, 0)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-link-stylesheet-does-not-block-script.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-link-stylesheet-does-not-block-script.html
new file mode 100644
index 0000000000..2c27bd32f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-link-stylesheet-does-not-block-script.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Script-created link stylesheet is not script-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<script>
+const link = document.createElement('link');
+link.rel = 'stylesheet';
+link.href = 'stylesheet.py?delay=1';
+document.head.appendChild(link);
+</script>
+<h1>Some text</h1>
+<script>
+test(() => {
+ assert_false(styleExists("h1 { color: purple; }"),
+ 'stylesheet should still be pending');
+ const h1 = document.querySelector('h1');
+ assert_equals(getComputedStyle(h1).color, 'rgb(0, 0, 0)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-style-element-does-not-block-script.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-style-element-does-not-block-script.html
new file mode 100644
index 0000000000..f04c3f668f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/script-created-style-element-does-not-block-script.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Script-created style element is not script-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<script>
+const style = document.createElement('style');
+const sheet = document.createTextNode('@import url(stylesheet.py?delay=1);');
+style.appendChild(sheet);
+document.head.appendChild(style);
+</script>
+<h1>Some text</h1>
+<script>
+test(() => {
+ assert_false(styleExists("h1 { color: purple; }"),
+ 'stylesheet should still be pending');
+ const h1 = document.querySelector('h1');
+ assert_equals(getComputedStyle(h1).color, 'rgb(0, 0, 0)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-match-block-script.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-match-block-script.html
new file mode 100644
index 0000000000..17adfc1728
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-match-block-script.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Style element is script-blocking when media matches</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<style>
+@import url('stylesheet.py?delay=1');
+</style>
+<h1>Some text</h1>
+<script>
+test(() => {
+ assert_true(styleExists("h1 { color: purple; }"),
+ 'script should be blocked until the stylesheet is loaded');
+ const h1 = document.querySelector('h1');
+ assert_equals(getComputedStyle(h1).color, 'rgb(128, 0, 128)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-not-match-does-not-block-script.html b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-not-match-does-not-block-script.html
new file mode 100644
index 0000000000..c05b6ed945
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/style-element-media-not-match-does-not-block-script.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Style element is not script-blocking when media doesn't match</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<style media="print">
+@import url('stylesheet.py?delay=1');
+</style>
+<h1>Some text</h1>
+<script>
+test(() => {
+ assert_false(styleExists("h1 { color: purple; }"),
+ 'stylesheet should still be pending');
+ const h1 = document.querySelector('h1');
+ assert_equals(getComputedStyle(h1).color, 'rgb(0, 0, 0)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/stylesheet.py b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/stylesheet.py
new file mode 100644
index 0000000000..d5ae5b9cca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/stylesheet.py
@@ -0,0 +1,10 @@
+from time import sleep
+def main(request, response):
+ if b"delay" in request.GET:
+ delay = int(request.GET[b"delay"])
+ sleep(delay)
+
+ if b"stylesNotMatchingEnvironment" in request.GET:
+ return u'h1 {color: brown;}'
+ else:
+ return u'h1 {color: purple;}'
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/link-style.css b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/link-style.css
new file mode 100644
index 0000000000..1024df8792
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/link-style.css
@@ -0,0 +1,3 @@
+h1 {
+ color: purple;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/utils.js b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/utils.js
new file mode 100644
index 0000000000..02d3a095cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/interactions-of-styling-and-scripting/support/utils.js
@@ -0,0 +1,20 @@
+function styleExistsInSheet(styleText, sheet) {
+ for (let rule of sheet.cssRules) {
+ if (styleText == rule.cssText)
+ return true;
+ if (rule instanceof CSSImportRule) {
+ if (rule.styleSheet && styleExistsInSheet(styleText, rule.styleSheet))
+ return true;
+ }
+ }
+ return false;
+}
+
+function styleExists(styleText) {
+ for (let sheet of document.styleSheets) {
+ if (styleExistsInSheet(styleText, sheet))
+ return true;
+ }
+ return false;
+}
+
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/styling/LinkStyle.html b/testing/web-platform/tests/html/semantics/document-metadata/styling/LinkStyle.html
new file mode 100644
index 0000000000..d1bb433520
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/styling/LinkStyle.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: Styling</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#styling">
+ <link id="style1" rel="text" title="Intel" href="./support/unmatch.css">
+ <link id="style2" rel="alternate stylesheet" type="text/css" title="" href="./support/emptytitle.css">
+ <link id="style3" rel="alternate stylesheet" type="text/css" href="./support/notitle.css">
+ <link id="style5" rel="stylesheet" type="text/css" href="./support/normal.css">
+ <link id="style6" rel="alternate stylesheet" type="text/css" href="./support/normal.css" title="./support/alternate.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style id="style4" type="text/html">
+ #test {
+ height: 100px;
+ width: 100px;
+ }
+ </style>
+ <style id="style7" type="text/css" media="all" title="./support/alternate.css">
+ #test {
+ background-color: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="test" style="display:none">STYLING TEST</div>
+
+ <script>
+ /**
+ * Browsers may incorrectly issue requests for these resources and defer
+ * definition of the `sheet` attribute until after loading is complete.
+ * In such cases, synchronous assertions regarding the absence of
+ * attributes will spuriously pass.
+ *
+ * In order to account for this incorrect behavior (exhibited at the time
+ * of this writing most notably by the Chromium browser), defer the
+ * assertions until the "load" event has been triggered.
+ */
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func(function() {
+ var style = null,
+ i;
+ for (i = 1; i < 5; i++) {
+ style = document.getElementById("style" + i);
+ assert_equals(style.sheet, null, "The sheet attribute of style" + i + " should be null.");
+ assert_false(style.disabled, "The disabled attribute of style" + i + " should be false.");
+ }
+ t.done();
+ }));
+ }, "The LinkStyle interface's sheet attribute must return null; the disabled attribute must be false");
+
+ test(function() {
+ var style = document.createElement("style"),
+ link = document.createElement("link");
+ assert_equals(style.sheet, null, "The sheet attribute of the style element not in a document should be null.");
+ assert_equals(link.sheet, null, "The sheet attribute of the link element not in a document should be null.");
+ }, "The LinkStyle interface's sheet attribute must return null if the corresponding element is not in a Document");
+
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func(function() {
+ var style = null,
+ i;
+ for (i = 5; i < 8; i++) {
+ style = document.getElementById("style" + i);
+ assert_true(style.sheet instanceof StyleSheet, "The sheet attribute of style" + i + " should be a StyleSheet object.");
+ assert_equals(style.disabled, style.sheet.disabled, "The disabled attribute of style" + i + " should equal to the same attribute of StyleSheet.");
+ }
+ t.done();
+ }));
+ }, "The LinkStyle interface's sheet attribute must return StyleSheet object; the disabled attribute must be same as the StyleSheet's disabled attribute");
+
+ test(function() {
+ assert_equals(document.getElementById("style2").title, "", "The title attribute of style2 is incorrect.");
+ assert_equals(document.getElementById("style5").title, "", "The title attribute of style5 is incorrect.");
+ assert_equals(document.getElementById("style6").title, "./support/alternate.css", "The title attribute of style6 is incorrect.");
+ assert_equals(document.getElementById("style7").title, "./support/alternate.css", "The title attribute of style7 is incorrect.");
+ }, "The title must be the same as the value of the element's title content attribute");
+
+ test(function() {
+ assert_equals(document.getElementById("style5").media, "", "The media attribute of style5 is incorrect.");
+ assert_equals(document.getElementById("style7").media, "all", "The media attribute of style7 is incorrect.");
+ }, "The media must be the same as the value of the element's media content attribute, or the empty string if it is omitted");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/styling/support/alternate.css b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/alternate.css
new file mode 100644
index 0000000000..b8deb07b0a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/alternate.css
@@ -0,0 +1,7 @@
+#test {
+ color: yellow;
+ background-color: blue;
+ width: 100px;
+ height: 50px;
+ font-size: .5em;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/styling/support/emptytitle.css b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/emptytitle.css
new file mode 100644
index 0000000000..e62fe701b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/emptytitle.css
@@ -0,0 +1,4 @@
+#test {
+ width: 100px;
+ height: 100px;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/styling/support/normal.css b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/normal.css
new file mode 100644
index 0000000000..a803c22112
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/normal.css
@@ -0,0 +1,5 @@
+#test {
+ width: 100px;
+ height: 50px;
+ font-size: 10px;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/styling/support/notitle.css b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/notitle.css
new file mode 100644
index 0000000000..e62fe701b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/notitle.css
@@ -0,0 +1,4 @@
+#test {
+ width: 100px;
+ height: 100px;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/styling/support/unmatch.css b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/unmatch.css
new file mode 100644
index 0000000000..e62fe701b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/styling/support/unmatch.css
@@ -0,0 +1,4 @@
+#test {
+ width: 100px;
+ height: 100px;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-data.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-data.html
new file mode 100644
index 0000000000..4905dd84ca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-data.html
@@ -0,0 +1,32 @@
+<!-- Please update base-javascript.html together with this -->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;base> and data: URLs</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<base href="data:/,test">
+<base href="https://example.com/">
+<div id=log></div>
+<script>
+test(() => {
+ const link = document.createElement("a");
+ link.href = "blah";
+ assert_equals(link.href, new URL("blah", document.URL).href);
+}, "First <base> has a data: URL so fallback is used");
+
+test(() => {
+ document.querySelector("base").remove();
+ const link = document.createElement("a");
+ link.href = "blah";
+ assert_equals(link.href, new URL("blah", "https://example.com/").href);
+}, "First <base> is removed so second is used");
+
+test(() => {
+ const base = document.createElement("base");
+ base.href = "data:/,more-test";
+ document.head.prepend(base);
+ const link = document.createElement("a");
+ link.href = "blah";
+ assert_equals(link.href, new URL("blah", document.URL).href);
+}, "Dynamically inserted first <base> has a data: URL so fallback is used");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-javascript.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-javascript.html
new file mode 100644
index 0000000000..65d9c84fcf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base-javascript.html
@@ -0,0 +1,32 @@
+<!-- Please update base-data.html together with this -->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;base> and javascript: URLs</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<base href="javascript:/,test">
+<base href="https://example.com/">
+<div id=log></div>
+<script>
+test(() => {
+ const link = document.createElement("a");
+ link.href = "blah";
+ assert_equals(link.href, new URL("blah", document.URL).href);
+}, "First <base> has a javascript: URL so fallback is used");
+
+test(() => {
+ document.querySelector("base").remove();
+ const link = document.createElement("a");
+ link.href = "blah";
+ assert_equals(link.href, new URL("blah", "https://example.com/").href);
+}, "First <base> is removed so second is used");
+
+test(() => {
+ const base = document.createElement("base");
+ base.href = "javascript:/,more-test";
+ document.head.prepend(base);
+ const link = document.createElement("a");
+ link.href = "blah";
+ assert_equals(link.href, new URL("blah", document.URL).href);
+}, "Dynamically inserted first <base> has a javascript: URL so fallback is used");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_about_blank.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_about_blank.html
new file mode 100644
index 0000000000..54c4794549
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_about_blank.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>base element in about:blank document should resolve against its fallback base URI</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<script>
+var t = async_test();
+addEventListener("load", t.step_func_done(function() {
+ var doc = frames[0].document;
+ var b = doc.createElement("base");
+ b.setAttribute("href", "test");
+ var newBaseValue = location.href.replace(/\/[^/]*$/, "/") + "test";
+ assert_equals(b.href, newBaseValue);
+ assert_equals(doc.baseURI, location.href);
+ doc.head.appendChild(b);
+ assert_equals(doc.baseURI, newBaseValue);
+}));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_empty.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_empty.html
new file mode 100644
index 0000000000..7737556a1a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_empty.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: base_href_empty</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-base-element">
+<base id="base" href="" target="_blank">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<img id="test" src="/images/blue-100x100.png" style="display:none">
+
+<script>
+ var testElement,
+ baseElement;
+
+ setup(function() {
+ testElement = document.getElementById("test");
+ baseElement = document.getElementById("base");
+ });
+
+ test(function() {
+ assert_equals(baseElement.href, document.location.href, "The href of base element is incorrect.");
+ }, "The value of the href attribute must be the document's address if it is empty");
+
+ test(function() {
+ var exp = testElement.src.substring(0, testElement.src.lastIndexOf("/images/blue-100x100.png") + 1);
+ assert_true(baseElement.href.indexOf(exp) != -1, "The src of img element is incorrect.");
+ }, "The src attribute of the img element must relative to document's address");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_invalid.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_invalid.html
new file mode 100644
index 0000000000..6d12d29e8a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_invalid.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>base element with unparseable href should have .href getter return attr value</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(function() {
+ var b = document.createElement("base");
+ b.setAttribute("href", "//test:test");
+ assert_equals(b.href, "//test:test");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_specified.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_specified.html
new file mode 100644
index 0000000000..83e71387a0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_specified.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: base_href_specified</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-base-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<base id="base">
+<div id="log"></div>
+<img id="test" src="test.ico" style="display:none">
+
+<script>
+ var testElement;
+ var baseElement;
+
+ var otherOrigin = get_host_info().HTTP_REMOTE_ORIGIN;
+
+ setup(function() {
+ testElement = document.getElementById("test");
+ baseElement = document.getElementById("base");
+
+ baseElement.setAttribute("href", otherOrigin);
+ });
+
+ test(function() {
+ assert_equals(baseElement.href, otherOrigin + "/", "The href attribute of the base element is incorrect.");
+ }, "The href attribute of the base element is specified");
+
+ test(function() {
+ assert_equals(testElement.src, otherOrigin + "/test.ico", "The src attribute of the img element is incorrect.");
+ }, "The src attribute of the img element must relative to the href attribute of the base element");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_unspecified.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_unspecified.html
new file mode 100644
index 0000000000..cf883f7239
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_href_unspecified.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: base_href_unspecified</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-base-element">
+<base id="base" target="_blank">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+<img id="test" src="/images/blue-100x100.png" style="display:none">
+
+<script>
+ var testElement,
+ baseElement;
+
+ setup(function () {
+ testElement = document.getElementById("test");
+ baseElement = document.getElementById("base");
+ });
+
+ test(function() {
+ assert_equals(baseElement.href, document.location.href, "Return the document base URL if the base element has no href content attribute.");
+ }, "The value of the href attribute must be the document's address if it is unspecified");
+
+ test(function() {
+ var exp = testElement.src.substring(0, testElement.src.lastIndexOf("/images/blue-100x100.png") + 1);
+ assert_true(baseElement.href.indexOf(exp) != -1, "The src attribute of the img element is incorrect.");
+ }, "The src attribute of the img element must relative to document's address");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_multiple.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_multiple.html
new file mode 100644
index 0000000000..4b7c0d213c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_multiple.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: base_multiple</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-base-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <div id="log"></div>
+ <iframe id="test1" src="example.html" style="width:0;height:0" frameborder="0"></iframe>
+ <iframe id="test2" src="example.html" name="targetWin" style="width:0;height:0" frameborder="0"></iframe>
+ <script>
+ async_test(function() {
+ window.onload = this.step_func(function() {
+ var fr1 = document.getElementById("test1");
+ fr1.addEventListener("load", this.unreached_func("loaded in the wrong iframe"));
+
+ var fr2 = document.getElementById("test2");
+ fr2.addEventListener("load", this.step_func_done(function () {
+ var doc2 = fr2.contentDocument;
+ assert_not_equals(doc2.location.href.indexOf("example2.html"), -1, "The target attribute does not impact the a element.");
+ assert_equals(doc2.getElementById("d1").innerHTML, "PASS", "The opend page should be the example2.html.");
+ }), true);
+
+ fr1.contentDocument.getElementById("a1").click();
+ });
+ }, "The attributes of the a element must be affected by the first base element");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_srcdoc.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_srcdoc.html
new file mode 100644
index 0000000000..eea1efe51d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_srcdoc.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>base element in srcdoc document should resolve against its fallback base URI</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe srcdoc=""></iframe>
+<script>
+var t = async_test();
+addEventListener("load", t.step_func_done(function() {
+ var doc = frames[0].document;
+ var b = doc.createElement("base");
+ b.setAttribute("href", "test");
+ var newBaseValue = location.href.replace(/\/[^/]*$/, "/") + "test";
+ assert_equals(b.href, newBaseValue);
+ assert_equals(doc.baseURI, location.href);
+ doc.head.appendChild(b);
+ assert_equals(doc.baseURI, newBaseValue);
+}));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_iframe_src_navigation.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_iframe_src_navigation.html
new file mode 100644
index 0000000000..b432698f21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_iframe_src_navigation.html
@@ -0,0 +1,10 @@
+<base id="base" target="_blank">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="about:blank"></iframe>
+<script>
+async_test(function(t) {
+ window.onmessage = () => t.done();
+ i.src = "data:text/html,This should navigate the iframe<script>top.postMessage('done', '*');</sc" + "ript>";
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_location_assignment.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_location_assignment.html
new file mode 100644
index 0000000000..2914f1f77f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_location_assignment.html
@@ -0,0 +1,10 @@
+<base id="base" target="_blank">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="about:blank"></iframe>
+<script>
+async_test(function(t) {
+ window.onmessage = () => t.done();
+ i.contentWindow.location = "data:text/html,This should navigate the iframe<script>top.postMessage('done', '*');</sc" + "ript>";
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example.html
new file mode 100644
index 0000000000..49dc772f91
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Example</title>
+<base target="targetWin" href="">
+<base target="_self" href="http://www.example.com/">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<a id="a1" href="example2.html" target="">click me</a>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example2.html b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example2.html
new file mode 100644
index 0000000000..0e57cb9c5c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-base-element/example2.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Example</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<div id="d1">PASS</div>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all
new file mode 100644
index 0000000000..60f1eab971
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all
@@ -0,0 +1,3 @@
+body {
+ color: red;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all.headers b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all.headers
new file mode 100644
index 0000000000..74e07a14e7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/all.headers
@@ -0,0 +1 @@
+Content-Type: text/css
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/document-without-browsing-context.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/document-without-browsing-context.html
new file mode 100644
index 0000000000..127b253f59
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/document-without-browsing-context.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Documents without browsing contexts should not load stylesheets</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<body>
+<script>
+ function count(id, t) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'stylesheet.py?count=1&id=' + id);
+ xhr.onload = t.step_func_done(function() {
+ assert_equals(xhr.responseText, "1");
+ });
+ xhr.onerror = t.unreached_func();
+ xhr.send();
+ }
+
+ async_test(function(t) {
+ var id = token();
+ var doc = (new DOMParser()).parseFromString('<link rel="stylesheet" href="stylesheet.py?id=' + id + '"></link>', 'text/html');
+ var link = doc.querySelector('link');
+ document.head.appendChild(link);
+ t.step_timeout(function() { count(id, t) }, 500);
+ }, 'Create a document, adopt the node');
+
+ async_test(function(t) {
+ var id = token();
+ var d = document.createElement('div');
+ document.body.appendChild(d);
+ d.innerHTML = '<link rel="stylesheet" href="stylesheet.py?id=' + id + '"></link>';
+ t.step_timeout(function() { count(id, t) }, 500);
+ }, 'Create a stylesheet in innerHTML document');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-error-fired-before-scripting-unblocked.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-error-fired-before-scripting-unblocked.html
new file mode 100644
index 0000000000..188e4ba5ab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-error-fired-before-scripting-unblocked.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var saw_link_onerror = false;
+var t = async_test("Check if the stylesheet's error event is fired before the " +
+ "pending parsing-blocking script is unblocked");
+</script>
+<link href="nonexistent.css" rel="stylesheet" id="style_test"
+ onload="t.unreached_func('Sheet should fail to load')"
+ onerror="t.step(function() { saw_link_onerror = true; })">
+<script>
+ t.step(function() {
+ assert_true(saw_link_onerror, "The pending parsing-blocking script should " +
+ "only run after the last element that " +
+ "contributes a script-blocking style " +
+ "sheet's error event is fired if the sheet " +
+ "fails to load.");
+ });
+ t.done();
+</script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.html
new file mode 100644
index 0000000000..e4f617d458
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="resources/link-load-error-events.sub.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.https.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.https.html
new file mode 100644
index 0000000000..e4f617d458
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-error-events.https.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="resources/link-load-error-events.sub.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-event.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-event.html
new file mode 100644
index 0000000000..e95fff7988
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-event.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Josh Matthews" href="mailto:josh@joshmatthews.net">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-link-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var saw_link_onload = false;
+var t = async_test("Check if the stylesheet's load event blocks the document load event");
+window.addEventListener('load', t.step_func_done(function() {
+ assert_true(saw_link_onload);
+}));
+</script>
+<link href="style.css?pipe=trickle(d3)" rel="stylesheet" id="style_test"
+ onload="t.step(function() { saw_link_onload = true; })"
+ onerror="t.unreached_func('Sheet should load OK')">
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-fired-before-scripting-unblocked.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-fired-before-scripting-unblocked.html
new file mode 100644
index 0000000000..a809cc44b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-load-fired-before-scripting-unblocked.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var saw_link_onload = false;
+var t = async_test("Check if the stylesheet's load event is fired before the " +
+ "pending parsing-blocking script is unblocked");
+</script>
+<link href="style.css?pipe=trickle(d3)" rel="stylesheet" id="style_test"
+ onload="t.step(function() { saw_link_onload = true; })"
+ onerror="t.unreached_func('Sheet should load OK')">
+<script>
+ t.step(function() {
+ assert_true(saw_link_onload, "The pending parsing-blocking script should " +
+ "only run after the last element that " +
+ "contributes a script-blocking style " +
+ "sheet's load event is fired.");
+ });
+ t.done();
+</script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-error-events.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-error-events.html
new file mode 100644
index 0000000000..9d112e88d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-error-events.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-link-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link id=style_link rel=stylesheet>
+<script>
+ async_test(t => {
+ const link = document.querySelector('#style_link');
+ link.onload = t.unreached_func('Sheet should fail to load');
+ link.onerror = t.step_func(() => {
+ link.onerror = t.step_func_done(() => {});
+ link.href = 'nonexistent.css?second';
+ });
+
+ link.href = 'nonexistent.css?first';
+ }, "Check if the <link>'s error event fires for each stylesheet it fails to load");
+
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-load-events.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-load-events.html
new file mode 100644
index 0000000000..b5550bb382
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-multiple-load-events.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-link-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link id=style_link rel=stylesheet>
+<script>
+ async_test(t => {
+ const link = document.querySelector('#style_link');
+ link.onerror = t.unreached_func('Sheet should load successfully');
+ link.onload = t.step_func(() => {
+ link.onload = t.step_func_done(() => {});
+ link.href = 'style.css?second';
+ });
+
+ link.href = 'style.css?first';
+
+ }, "Check if the <link>'s load event fires for each stylesheet it loads");
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive-notref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive-notref.html
new file mode 100644
index 0000000000..04e3a7cb02
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive-notref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>link element rel is ASCII case-insensitive (mismatch reference)</title>
+<link rel="stylesheet" href="stylesheet.css">
+<p>Test passes if background is not red.</p>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive.html
new file mode 100644
index 0000000000..5ee55f7d2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute-ascii-case-insensitive.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>link element rel is ASCII case-insensitive</title>
+<link rel="help" href="https://html.spec.whatwg.org/#the-link-element">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-rel">
+<link rel="help" href="https://html.spec.whatwg.org/#linkTypes">
+<meta name="assert" content="link element's rel attribute is ASCII case-insensitive.">
+<link rel="mismatch" href="link-rel-attribute-ascii-case-insensitive-notref.html">
+
+<!-- Load sheet with a red background (rel attribute value is case-sensitive
+ equal to "stylesheet") -->
+<link rel="stylesheet" href="stylesheet.css">
+
+<!-- Load sheet with white background (rel attribute value is ASCII
+ case-insensitive equal to "stylesheet") -->
+<link rel="StyLeShEeT" href="style.css">
+
+<!-- Do not load sheet with a red background (rel attribute value is
+ case-insensitive equal to "stylesheet") -->
+<link rel="ſtyleſheet" href="stylesheet.css">
+
+<p>Test passes if background is not red.</p>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute.html
new file mode 100644
index 0000000000..14d06227ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rel-attribute.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<script src = "/resources/testharness.js"></script>
+<script src = "/resources/testharnessreport.js"></script>
+
+<link id="light-link" rel="stylesheet" href="resources/link-rel-attribute.css">
+<div id="light-div" class="green">I"m green when light DOM link is on</div>
+
+<div id="host">
+ I"m green when Shadow DOM link is on
+ <template id="shadow-dom">
+ <link id="shadow-link" rel="stylesheet" href="resources/link-rel-attribute.css">
+ <div id="shadow-div" class="green">
+ <slot></slot>
+ </div>
+ </template>
+</div>
+
+<script>
+
+function testLinkRelModification(testDiv, testLink) {
+ assert_equals(getComputedStyle(testDiv).color, "rgb(0, 128, 0)");
+ testLink.setAttribute("rel", "no-stylesheet");
+ assert_equals(getComputedStyle(testDiv).color, "rgb(0, 0, 0)");
+ testLink.setAttribute("rel", "stylesheet");
+ assert_equals(getComputedStyle(testDiv).color, "rgb(0, 128, 0)");
+ testLink.removeAttribute("rel");
+ assert_equals(getComputedStyle(testDiv).color, "rgb(0, 0, 0)");
+}
+
+test (() => {
+ testLinkRelModification(document.querySelector("#light-div"),
+ document.querySelector("#light-link"));
+}, "Removing stylesheet from link rel attribute should remove the stylesheet for light DOM");
+
+test (() => {
+ var host = document.querySelector("#host");
+ var shadow = host.attachShadow({ mode: "open" });
+ var tmpl = document.querySelector("template#shadow-dom");
+ var clone = document.importNode(tmpl.content, true);
+ shadow.appendChild(clone);
+ testLinkRelModification(shadow.querySelector("#shadow-div"),
+ shadow.querySelector("#shadow-link"));
+}, "Removing stylesheet from link rel attribute should remove the stylesheet for shadow DOM");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rellist.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rellist.html
new file mode 100644
index 0000000000..8647426755
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-rellist.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>link.relList: non-string contains</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-link-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#domtokenlist">
+<link rel="help" href="https://webidl.spec.whatwg.org/#ecmascript-binding">
+<link rel="help" href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf#page=57">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link id="link" rel="undefined null 0 NaN Infinity">
+<div id="log"></div>
+<script>
+test(function() {
+ var list = document.getElementById("link").relList;
+ assert_equals(list.contains(undefined), true); //"undefined"
+ assert_equals(list.contains(null), true); //"null"
+ assert_equals(list.contains(-0), true); //"0"
+ assert_equals(list.contains(+0), true); //"0"
+ assert_equals(list.contains(NaN), true); //"NaN"
+ assert_equals(list.contains(+Infinity), true); //"Infinity"
+ assert_equals(list.contains(-Infinity), false); //"-Infinity"
+ assert_equals(list.supports("stylesheet"), true);
+ assert_equals(list.supports("nosuchrelvalueever"), false);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-01.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-01.html
new file mode 100644
index 0000000000..575324d761
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-01.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>link: error events</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-link-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=/common/get-host-info.sub.js></script>
+<div id="log"></div>
+<div id="test">
+<script>
+var t404 = async_test("Should get an error event for a 404 error.")
+t404.step(function() {
+ var elt = document.createElement("link");
+ elt.onerror = t404.step_func(function() {
+ assert_true(true, "Got error event for 404 error.")
+ t404.step_timeout(function() { t404.done() }, 0);
+ })
+ elt.onload = t404.unreached_func("load event should not be fired");
+ elt.rel = "stylesheet";
+ elt.href = "nonexistent_stylesheet.css";
+ document.getElementsByTagName("head")[0].appendChild(elt);
+})
+
+var tUnsupported = async_test("Should get an error event for an unsupported URL.")
+tUnsupported.step(function() {
+ var elt = document.createElement("link");
+ elt.onerror = tUnsupported.step_func(function() {
+ assert_true(true, "Got error event for unsupported URL.")
+ tUnsupported.step_timeout(function() { tUnsupported.done() }, 0);
+ })
+ elt.onload = tUnsupported.unreached_func("load event should not be fired");
+ elt.rel = "stylesheet";
+ elt.href = "nonexistent:stylesheet.css";
+ document.getElementsByTagName("head")[0].appendChild(elt);
+});
+</script>
+<script src=resources/link-style-error.js></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-limited-quirks.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-limited-quirks.html
new file mode 100644
index 0000000000..d3c520ba75
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-limited-quirks.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//" "">
+<title>link: error events in limited quirks mode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=/common/get-host-info.sub.js></script>
+<div id="log"></div>
+<script src=resources/link-style-error.js></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-quirks.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-quirks.html
new file mode 100644
index 0000000000..ae2efa415e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-style-error-quirks.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML PUBLIC "-//Sun Microsystems Corp.//DTD HotJava Strict HTML//" "">
+<title>link: error events in quirks mode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=/common/get-host-info.sub.js></script>
+<div id="log"></div>
+<script src=resources/link-style-error.js></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute-ref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute-ref.html
new file mode 100644
index 0000000000..f32472105d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<p>You should see a green rectangle below</p>
+<div style="width:100px;height:100px;background-color:green"></div>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute.html
new file mode 100644
index 0000000000..80acb9f3dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/link-type-attribute.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel=match href=link-type-attribute-ref.html>
+<link rel="stylesheet" type="application/javascript" href="data:text/css,div { background-color: red !important; }">
+<link rel="stylesheet" type="ABCtext/css" href="data:text/css,div { background-color: red !important; }">
+<link rel="stylesheet" type="text/cssDEF" href="data:text/css,div { background-color: red !important; }">
+<link rel="stylesheet" type="text/invalid" href="data:text/css,div { background-color: red !important; }">
+<link rel="stylesheet" type="invalid" href="data:text/css,div { background-color: red !important; }">
+<p>You should see a green rectangle below</p>
+<div style="width:100px;height:100px;background-color:green"></div>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/bad.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/bad.css
new file mode 100644
index 0000000000..4e1fe36165
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/bad.css
@@ -0,0 +1,4 @@
+p {
+ background-color: red;
+ color: black;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/css.py b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/css.py
new file mode 100644
index 0000000000..1a11c1d5b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/css.py
@@ -0,0 +1,7 @@
+def main(request, response):
+ response.add_required_headers = False
+ if b"content_type" in request.GET:
+ response.writer.write_header(b"Content-Type", request.GET.first(b"content_type"))
+ if b"nosniff" in request.GET:
+ response.writer.write_header(b"x-content-type-options", b"nosniff")
+ response.writer.write_content(u"body { background:red }")
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/empty-href.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/empty-href.css
new file mode 100644
index 0000000000..60f1eab971
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/empty-href.css
@@ -0,0 +1,3 @@
+body {
+ color: red;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/good.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/good.css
new file mode 100644
index 0000000000..1da5e2b8cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/good.css
@@ -0,0 +1,3 @@
+p {
+ color: green;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-load-error-events.sub.js b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-load-error-events.sub.js
new file mode 100644
index 0000000000..33c8709579
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-load-error-events.sub.js
@@ -0,0 +1,192 @@
+/**
+ * This is the guts of the load/error event tests for <link rel="stylesheet">.
+ *
+ * We have a list of tests each of which is an object containing: href value,
+ * expected load success boolean, test description. Href values are set up in
+ * such a way that we guarantee that all stylesheet URLs are unique. This
+ * avoids issues around caching of sheets based on URL.
+ */
+
+// Our URLs are random, so we don't use them in error messages by
+// default, but enable doing it if someone wants to debug things.
+const DEBUG_URLS = false;
+
+var isHttps = location.protocol == "https:";
+
+var tests = [
+ // Basic tests
+ {
+ href: existingSheet(),
+ success: true,
+ description: "Basic load of stylesheet",
+ },
+ {
+ href: nonexistentSheet(),
+ success: false,
+ description: "Attempted load of nonexistent stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("${existingSheet()}")`,
+ success: true,
+ description: "Import of stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("${nonexistentSheet()}")`,
+ success: false,
+ description: "Import of nonexistent stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("data:text/css,@import url('${existingSheet()}')")`,
+ success: true,
+ description: "Import of import of stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("data:text/css,@import url('${nonexistentSheet()}')")`,
+ success: false,
+ description: "Import of import of nonexistent stylesheet",
+ },
+
+ // Non-CSS-response tests.
+ {
+ href: makeUnique(""),
+ success: false,
+ description: "Load of non-CSS stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("${makeUnique("")}")`,
+ success: false,
+ description: "Import of non-CSS stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("data:text/css,@import url('${makeUnique("")}')")`,
+ success: false,
+ description: "Import of import of non-CSS stylesheet",
+ },
+
+ // http:// tests, to test what happens with mixed content blocking.
+ {
+ href: httpSheet(),
+ success: !isHttps,
+ description: "Load of http:// stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("${httpSheet()}")`,
+ success: !isHttps,
+ description: "Import of http:// stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("data:text/css,@import url('${httpSheet()}')")`,
+ success: !isHttps,
+ description: "Import of import of http:// stylesheet",
+ },
+
+ // https:// tests just as a control
+ {
+ href: httpsSheet(),
+ success: true,
+ description: "Load of https:// stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("${httpsSheet()}")`,
+ success: true,
+ description: "Import of https:// stylesheet",
+ },
+ {
+ href: `data:text/css,@import url("data:text/css,@import url('${httpsSheet()}')")`,
+ success: true,
+ description: "Import of import of https:// stylesheet",
+ },
+
+ // Tests with multiple imports some of which are slow and some are fast.
+ {
+ href: `data:text/css,@import url("${slowResponse(existingSheet())}"); @import url("${nonexistentSheet()}");`,
+ success: false,
+ description: "Slow successful import, fast failing import",
+ },
+ {
+ href: `data:text/css,@import url("${existingSheet()}"); @import url("${slowResponse(nonexistentSheet())}");`,
+ success: false,
+ description: "Fast successful import, slow failing import",
+ }
+];
+
+// Note: Here we really do need to use "let" at least for the href,
+// because we lazily evaluate it in the unreached cases.
+for (var test of tests) {
+ let {href, success, description} = test;
+ var t = async_test(description);
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ hrefString = DEBUG_URLS ? `: ${href}` : "";
+ if (success) {
+ link.onload = t.step_func_done(() => {});
+ link.onerror = t.step_func_done(() => assert_unreached(`error fired when load expected${hrefString}`) );
+ } else {
+ link.onerror = t.step_func_done(() => {});
+ link.onload = t.step_func_done(() => assert_unreached(`load fired when error expected${hrefString}`) );
+ }
+ link.href = href;
+ document.head.appendChild(link);
+}
+
+/* Utility function */
+function makeUnique(url) {
+ // Make sure we copy here, even if the thing coming in is a URL, so we don't
+ // mutate our caller's data.
+ url = new URL(url, location.href);
+ // We want to generate a unique URI to avoid the various caches browsers have
+ // for stylesheets. We don't want to just use a counter, because that would
+ // not be robust to the test being reloaded or othewise run multiple times
+ // without a browser restart. We don't want to use timstamps, because those
+ // are not likely to be unique across calls to this function, especially given
+ // the degraded timer resolution browsers have due to Spectre.
+ //
+ // So just fall back on Math.random() and assume it can't duplicate values.
+ url.searchParams.append("r", Math.random());
+ return url;
+}
+
+function existingSheet() {
+ return makeUnique("resources/good.css");
+}
+
+/**
+ * Function the add values to the "pipe" search param. See
+ * http://wptserve.readthedocs.io/en/latest/pipes.html for why one would do
+ * this. Because this param uses a weird '|'-separated syntax instead of just
+ * using multiple params with the same name, we need some manual code to munge
+ * the value properly.
+ */
+function addPipe(url, pipeVal) {
+ url = new URL(url, location.href);
+ var params = url.searchParams;
+ var oldVal = params.get("pipe");
+ if (oldVal) {
+ params.set("pipe", oldVal + "|" + pipeVal);
+ } else {
+ params.set("pipe", pipeVal);
+ }
+ return url;
+}
+
+function nonexistentSheet() {
+ return addPipe(existingSheet(), "status(404)");
+}
+
+function httpSheet() {
+ var url = existingSheet();
+ url.protocol = "http";
+ url.port = {{ports[http][0]}};
+ return url;
+}
+
+function httpsSheet() {
+ var url = existingSheet();
+ url.protocol = "https";
+ url.port = {{ports[https][0]}};
+ return url;
+}
+
+function slowResponse(url) {
+ return addPipe(url, "trickle(d1)");
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-rel-attribute.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-rel-attribute.css
new file mode 100644
index 0000000000..fa95e11ba9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-rel-attribute.css
@@ -0,0 +1,3 @@
+.green {
+ color: green;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-style-error.js b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-style-error.js
new file mode 100644
index 0000000000..d1fa5ac2d6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/link-style-error.js
@@ -0,0 +1,47 @@
+["<link>", "@import"].forEach(linkType => {
+ [
+ ["same-origin", "resources/css.py"],
+ ["cross-origin", get_host_info().HTTP_REMOTE_ORIGIN + "/html/semantics/document-metadata/the-link-element/resources/css.py"]
+ ].forEach(originType => {
+ ["no Content-Type", "wrong Content-Type", "broken Content-Type"].forEach(contentType => {
+ ["no nosniff", "nosniff"].forEach(nosniff => {
+ async_test(t => {
+ const l = document.createElement("link");
+ t.add_cleanup(() => l.remove());
+ if (nosniff === "nosniff" || contentType === "wrong Content-Type" && (document.compatMode === "CSS1Compat" || originType[0] === "cross-origin")) {
+ l.onerror = t.step_func_done();
+ l.onload = t.unreached_func("error event should have fired");
+ } else {
+ l.onload = t.step_func_done();
+ l.onerror = t.unreached_func("load event should have fired");
+ }
+ l.rel = "stylesheet";
+ let query = [];
+ if (contentType === "broken Content-Type") {
+ query.push("content_type=oops");
+ } else if (contentType === "wrong Content-Type") {
+ query.push("content_type=text/plain")
+ }
+ if (nosniff === "nosniff") {
+ query.push("nosniff");
+ }
+ let stringQuery = "";
+ query.forEach(val => {
+ if (stringQuery === "") {
+ stringQuery += "?" + val;
+ } else {
+ stringQuery += "&" + val;
+ }
+ });
+ const link = new URL(originType[1] + stringQuery, location).href;
+ if (linkType === "<link>") {
+ l.href = link;
+ } else {
+ l.href = "data:text/css,@import url(" + link + ");";
+ }
+ document.head.appendChild(l);
+ }, "Stylesheet loading using " + linkType + " with " + contentType + ", " + originType[0] + ", and " + nosniff);
+ });
+ });
+ });
+});
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/neutral.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/neutral.css
new file mode 100644
index 0000000000..796c55c42f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/neutral.css
@@ -0,0 +1,3 @@
+body {
+ background-color: gray;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/stylesheet.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/stylesheet.css
new file mode 100644
index 0000000000..e1b2552ffe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/resources/stylesheet.css
@@ -0,0 +1,3 @@
+body {
+ background-color: green;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/style.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/style.css
new file mode 100644
index 0000000000..d48115e565
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/style.css
@@ -0,0 +1,3 @@
+body {
+ background-color: white;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href-ref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href-ref.html
new file mode 100644
index 0000000000..9ae6e36655
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset="utf-8">
+<style>
+ p {
+ color: green;
+ }
+</style>
+<p>This text should be green on a white background
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href.html
new file mode 100644
index 0000000000..6a3f18de98
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-change-href.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Obtaining a new stylesheet removes styles from the previous stylesheet.</title>
+<link rel=match href=stylesheet-change-href-ref.html>
+<script>
+ function changeHref() {
+ var elem = document.getElementById('stylesheet');
+ elem.href = 'resources/good.css';
+ elem.onload = null;
+ }
+</script>
+<link id=stylesheet rel=stylesheet href="resources/bad.css" onload="changeHref()">
+<p>This text should be green on a white background
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href-ref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href-ref.html
new file mode 100644
index 0000000000..63b75d0ae2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test</title>
+<style>
+body {
+ color: green;
+}
+</style>
+<p>This text should be green.
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href.html
new file mode 100644
index 0000000000..16b14efacc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-empty-href.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test</title>
+<link rel=match href=stylesheet-empty-href-ref.html>
+<style>
+body {
+ color: green;
+}
+</style>
+<base href=resources/empty-href.css>
+<link rel=stylesheet href>
+<p>This text should be green.
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media-ref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media-ref.html
new file mode 100644
index 0000000000..63b75d0ae2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test</title>
+<style>
+body {
+ color: green;
+}
+</style>
+<p>This text should be green.
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media.html
new file mode 100644
index 0000000000..9a72924cf4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-media.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test</title>
+<link rel=match href=stylesheet-media-ref.html>
+<style>
+body {
+ color: green;
+}
+</style>
+<link rel=stylesheet id=link>
+<script>
+// This tests for a bug in Servo, where it would treat the media attribute as
+// if it was the href attribute.
+var link = document.getElementById("link");
+link.setAttribute("media", "all")
+</script>
+<p>This text should be green.
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-not-removed-until-next-stylesheet-loads.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-not-removed-until-next-stylesheet-loads.html
new file mode 100644
index 0000000000..ab8ee727f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-not-removed-until-next-stylesheet-loads.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link href="style.css" rel="stylesheet" id="style_test">
+<script>
+ test(function() {
+ assert_true(document.styleSheets.length === 1 &&
+ document.styleSheets[0].href.includes("style.css"),
+ "The style sheet 'style.css' must be available to scripts");
+
+ style_test.href = "resources/neutral.css?pipe=trickle(d1)";
+
+ assert_true(document.styleSheets.length === 1 &&
+ document.styleSheets[0].href.includes("style.css"),
+ "The style sheet 'style.css' must remain accessible to " +
+ "scripts until its replacement has finished loading");
+ }, "Check that a style sheet loaded by a <link> is available until its successor is loaded");
+</script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base-ref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base-ref.html
new file mode 100644
index 0000000000..83f0d06772
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Stylesheet Without Base Tag</title>
+ <style>
+ body { background-color: green; }
+ </style>
+</head>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base.html b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base.html
new file mode 100644
index 0000000000..a9f2a8bce0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet-with-base.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Stylesheet With Base Tag</title>
+ <link rel="match" href="stylesheet-with-base-ref.html">
+ <base href="resources/">
+ <link rel="stylesheet" href="stylesheet.css">
+</head>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.css b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.css
new file mode 100644
index 0000000000..e8f24f94a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.css
@@ -0,0 +1,3 @@
+body {
+ background-color: red;
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.py b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.py
new file mode 100644
index 0000000000..1a4dec2724
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-link-element/stylesheet.py
@@ -0,0 +1,9 @@
+def main(request, response):
+ try:
+ count = int(request.server.stash.take(request.GET[b"id"]))
+ except:
+ count = 0
+ if b"count" in request.GET:
+ return str(count)
+ request.server.stash.put(request.GET[b"id"], str(count + 1))
+ return u'body { color: red }'
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-attribute-changes.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-attribute-changes.html
new file mode 100644
index 0000000000..6f877ee416
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-attribute-changes.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>Meta color-scheme - attribute changes</title>
+<meta id="meta" name="color-scheme" content="dark">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<script>
+ assert_root_color_scheme("dark", "Meta color-scheme initially 'dark'.");
+
+ meta.removeAttribute("name");
+ assert_root_color_scheme("light", "Removed name attribute from meta color-scheme.");
+
+ meta.setAttribute("name", "color-scheme");
+ assert_root_color_scheme("dark", "Set meta name to color-scheme.");
+
+ meta.setAttribute("content", "");
+ assert_root_color_scheme("light", "Set content attribute of meta color-scheme to empty string.");
+
+ meta.setAttribute("content", ",,invalid");
+ assert_root_color_scheme("light", "Set content attribute of meta color-scheme to an invalid value.");
+
+ meta.setAttribute("content", "light");
+ assert_root_color_scheme("light", "Set content attribute of meta color-scheme to 'light'.");
+
+ meta.setAttribute("content", "dark");
+ assert_root_color_scheme("dark", "Set content attribute of meta color-scheme to 'dark'.");
+
+ meta.removeAttribute("content");
+ assert_root_color_scheme("light", "Removed the content attribute of meta color-scheme.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-empty-content-value.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-empty-content-value.html
new file mode 100644
index 0000000000..8a3cf18af8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-empty-content-value.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Meta color-scheme - empty content value</title>
+<meta name="color-scheme" content="">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<script>
+ assert_root_color_scheme("light", "Meta color-scheme with empty content attribute has no effect.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-first-valid-applies.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-first-valid-applies.html
new file mode 100644
index 0000000000..095d0f360d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-first-valid-applies.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Multiple color-scheme meta tags - first valid applies</title>
+<meta name="color-scheme">
+<meta name="color-scheme" content>
+<meta name="color-scheme" content="">
+<meta name="color-scheme" content="light,dark">
+<!-- This is first with a valid content value -->
+<meta name="color-scheme" content="dark">
+<meta name="color-scheme" content="light">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<script>
+ assert_root_color_scheme("dark", "Tree order decides which meta color-scheme applies.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-insert.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-insert.html
new file mode 100644
index 0000000000..463c318105
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-insert.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Insert color-scheme meta tags</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<script>
+ function createMeta(content) {
+ const meta = document.createElement("meta");
+ meta.setAttribute("name", "color-scheme");
+ meta.setAttribute("content", content);
+ return meta;
+ }
+
+ assert_root_color_scheme("light", "Initial color-scheme");
+
+ document.head.appendChild(createMeta("dark"));
+ assert_root_color_scheme("dark", "Inserted meta color-scheme applies");
+
+ document.head.insertBefore(createMeta("light"), document.head.lastChild);
+ assert_root_color_scheme("light", "Inserted meta color-scheme before existing in head applies");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-no-content-value.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-no-content-value.html
new file mode 100644
index 0000000000..0d22e44b26
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-no-content-value.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Meta color-scheme - no content value</title>
+<meta name="color-scheme">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<script>
+ assert_root_color_scheme("light", "Meta color-scheme without content attribute has no effect.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-normal-descendant-change.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-normal-descendant-change.html
new file mode 100644
index 0000000000..136f4c371b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-normal-descendant-change.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Change color-scheme meta tag affecting normal descendant</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta id="meta" name="color-scheme" content="dark">
+<div style="color-scheme: dark; color: CanvasText" id="dark">
+ <div style="color-scheme: normal; color: CanvasText" id="normal"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(dark).color, getComputedStyle(normal).color);
+ }, "Normal initially dark");
+
+ meta.content = "light";
+
+ test(() => {
+ assert_not_equals(getComputedStyle(dark).color, getComputedStyle(normal).color);
+ }, "Normal should change to light from page color schemes");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove-head.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove-head.html
new file mode 100644
index 0000000000..587e2fa596
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove-head.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Remove head with meta color-scheme</title>
+<meta name="color-scheme" content="dark">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<body></body>
+<script>
+ assert_root_color_scheme("dark", "Meta color-scheme applies.");
+ document.head.remove();
+ assert_root_color_scheme("light", "Initial value after removing head including meta color-scheme.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove.html
new file mode 100644
index 0000000000..a89a520791
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Remove color-scheme meta tag</title>
+<meta id="dark" name="color-scheme" content="dark">
+<meta id="light" name="color-scheme" content="light">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<script>
+ assert_root_color_scheme("dark", "First meta applies.");
+ dark.remove();
+ assert_root_color_scheme("light", "Second meta applies after first one is removed.");
+ light.remove();
+ assert_root_color_scheme("light", "Initial color-scheme with both meta elements removed.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-body.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-body.html
new file mode 100644
index 0000000000..19f8d53994
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-body.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>Meta color-scheme in body should apply</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<body>
+ <meta name="color-scheme" content="dark">
+</body>
+<script>
+ assert_root_color_scheme("dark", "Meta color-scheme in body should apply.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-head.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-head.html
new file mode 100644
index 0000000000..b9fd2c4384
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-head.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Single meta color-scheme in head</title>
+<meta name="color-scheme" content="dark">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<script>
+ assert_root_color_scheme("dark", "Meta color-scheme in head applies.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-shadow-tree.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-shadow-tree.html
new file mode 100644
index 0000000000..7ccafc8419
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-single-value-in-shadow-tree.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Meta color-scheme in shadow-tree should not apply</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<script>
+ const host = document.createElement("div");
+ host.id = "host";
+ document.head.appendChild(host);
+ const root = host.attachShadow({mode:"open"});
+ const meta = document.createElement("meta");
+ meta.setAttribute("name", "color-scheme");
+ meta.setAttribute("content", "dark");
+ root.appendChild(meta);
+
+ assert_root_color_scheme("light", "Meta color-scheme in shadow tree does not apply.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/support/compute-root-color-scheme.js b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/support/compute-root-color-scheme.js
new file mode 100644
index 0000000000..74cbf895ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/color-scheme/support/compute-root-color-scheme.js
@@ -0,0 +1,28 @@
+'use strict';
+
+function assert_root_color_scheme(expected_used_scheme, description) {
+ function get_used_root_color_scheme() {
+ let light = get_system_color("only light", "CanvasText");
+ let dark = get_system_color("only dark", "CanvasText");
+ assert_not_equals(light, dark, "CanvasText system color should be different with light and dark color schemes");
+ let root = getComputedStyle(document.documentElement).color;
+ assert_in_array(root, [light, dark], "Root color scheme should be either light or dark, or the text needs to be extended for newer color-schemes");
+ return root == light ? "light" : "dark";
+ }
+
+ function get_system_color(scheme, color) {
+ let div = document.createElement("div");
+ div.style.color = color;
+ div.style.colorScheme = scheme;
+
+ document.documentElement.appendChild(div);
+ let computed = getComputedStyle(div).color;
+ div.remove();
+ return computed;
+ }
+
+ test(() => {
+ assert_equals(get_used_root_color_scheme(), expected_used_scheme);
+ assert_equals(getComputedStyle(document.documentElement).colorScheme, "normal", "Root element's color-scheme should be 'normal'");
+ }, description);
+}
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-1.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-1.html
new file mode 100644
index 0000000000..ac82e3396d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-1.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>Setting both http-equiv and name attributes on a meta element</title>
+<meta http-equiv=content-language name=color-scheme content=dark>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="color-scheme/support/compute-root-color-scheme.js"></script>
+<!--
+ NOTE: This test assumes that the browser's default color-scheme is "light",
+ see https://github.com/web-platform-tests/wpt/pull/31268 for reasoning
+-->
+<script>
+ // This creates a test()
+ assert_root_color_scheme("dark", "<meta> set the color-scheme to dark");
+
+ // We can't test content-language against :lang(), because CSS Selectors 4
+ // references BCP 47 syntax and RFC4647 "Matching of Language Tags", but
+ // "dark" is not a well-formed BCP 47 tag and therefore cannot be matched.
+ // Therefore, the test that content-language gets set is split off to a
+ // separate testcase using a well-formed lang tag as the content.
+ // test(() => {
+ // assert_equals(document.querySelector(":root:lang(dark)"), document.documentElement);
+ // }, "<meta> set the content-language to dark");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-2.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-2.html
new file mode 100644
index 0000000000..b73013a341
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/http-equiv-and-name-2.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>Setting both http-equiv and name attributes on a meta element</title>
+<meta http-equiv=content-language name=color-scheme content=de-DE>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ // We don't attempt to test the color-scheme here because "de-DE" is not a valid
+ // value for it.
+
+ test(() => {
+ assert_equals(document.querySelector(":root:lang(de-DE)"), document.documentElement);
+ }, "<meta> set the content-language to de-DE");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html
new file mode 100644
index 0000000000..196f6d0409
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-refresh">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ single_test: true });
+
+const sourceIFrame = document.createElement("iframe");
+sourceIFrame.setAttribute("sandbox", "allow-same-origin");
+
+const destIFrame = document.createElement("iframe");
+
+let sourceLoadCount = 0;
+let destLoadCount = 0;
+
+sourceIFrame.onload = () => {
+ ++sourceLoadCount;
+
+ if (sourceLoadCount === 2) {
+ assert_unreached("The iframe from which the meta came from must not refresh");
+ }
+
+ maybeStartTest();
+};
+
+destIFrame.onload = () => {
+ ++destLoadCount;
+
+ if (destLoadCount === 2) {
+ // destIFrame doesn't have the sandboxed automatic features browsing context
+ // flag sets, thus navigated.
+ assert_equals(destIFrame.contentDocument.body.textContent.trim(), "foo");
+ done();
+ }
+
+ maybeStartTest();
+};
+
+function maybeStartTest() {
+ if (sourceLoadCount === 1 && destLoadCount === 1) {
+ const meta = sourceIFrame.contentDocument.querySelector("meta");
+ destIFrame.contentDocument.body.appendChild(meta);
+ }
+}
+
+sourceIFrame.src = "support/refresh.sub.html?input=" + encodeURIComponent("1; url=foo");
+destIFrame.src = "support/ufoo";
+
+document.body.appendChild(sourceIFrame);
+document.body.appendChild(destIFrame);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html
new file mode 100644
index 0000000000..cc7eb5e5e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-refresh">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ single_test: true });
+
+const sourceIFrame = document.createElement("iframe");
+
+const destIFrame = document.createElement("iframe");
+destIFrame.setAttribute("sandbox", "allow-same-origin");
+
+let sourceLoadCount = 0;
+let destLoadCount = 0;
+
+sourceIFrame.onload = () => {
+ ++sourceLoadCount;
+
+ if (sourceLoadCount === 2) {
+ assert_equals(sourceIFrame.contentDocument.body.textContent.trim(), "foo");
+ done();
+ }
+
+ maybeStartTest();
+};
+
+destIFrame.onload = () => {
+ ++destLoadCount;
+
+ if (destLoadCount === 2) {
+ assert_unreached("The iframe into which the meta was moved must not refresh");
+ }
+
+ maybeStartTest();
+};
+
+function maybeStartTest() {
+ if (sourceLoadCount === 1 && destLoadCount === 1) {
+ const meta = sourceIFrame.contentDocument.querySelector("meta");
+ destIFrame.contentDocument.body.appendChild(meta);
+ }
+}
+
+sourceIFrame.src = "support/refresh.sub.html?input=" + encodeURIComponent("1; url=foo");
+destIFrame.src = "support/ufoo";
+
+document.body.appendChild(sourceIFrame);
+document.body.appendChild(destIFrame);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/dynamic-append.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/dynamic-append.html
new file mode 100644
index 0000000000..4d2fa78940
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/dynamic-append.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Meta refresh applies even when dynamically appended</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#pragma-directives">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ single_test: true });
+
+const iframe = document.createElement("iframe");
+let loadCount = 0;
+
+iframe.onload = () => {
+ ++loadCount;
+ const iDocument = iframe.contentDocument;
+
+ if (loadCount === 1) {
+ iDocument.body.innerHTML = `<meta http-equiv="refresh" content="1; url=foo">`;
+ } else if (loadCount === 2) {
+ assert_equals(iDocument.body.textContent.trim(), "foo");
+ done();
+ }
+};
+
+iframe.src = "support/ufoo";
+document.body.appendChild(iframe);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/not-in-shadow-tree.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/not-in-shadow-tree.html
new file mode 100644
index 0000000000..2a9f301fff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/not-in-shadow-tree.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Meta refresh only applies while in the document tree, not in a shadow tree</title>
+<meta name="timeout" content="long" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#pragma-directives">
+
+<div id="log"></div>
+<script>
+"use strict";
+setup({ single_test: true });
+
+const iframe = document.createElement("iframe");
+iframe.src = "support/ufoo";
+
+let loadCount = 0;
+
+iframe.onload = () => {
+ ++loadCount;
+ const iDocument = iframe.contentDocument;
+
+ if (loadCount === 1) {
+ const div = iDocument.createElement("div");
+ assert_true('attachShadow' in div, 'attachShadow support');
+ const shadowRoot = div.attachShadow({ mode: "open" });
+ shadowRoot.innerHTML = `<meta http-equiv="refresh" content="1; url=foo">`;
+ iDocument.body.appendChild(div);
+
+ // Want to make sure no refreshes happen
+ step_timeout(done, 3000);
+ } else {
+ assert_unreached("Got more than 1 load event");
+ }
+};
+
+document.body.appendChild(iframe);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html
new file mode 100644
index 0000000000..73ac4bcc00
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html
@@ -0,0 +1,147 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="variant" content="?1-10">
+<meta name="variant" content="?11-20">
+<meta name="variant" content="?21-30">
+<meta name="variant" content="?31-40">
+<meta name="variant" content="?41-50">
+<meta name="variant" content="?51-60">
+<meta name="variant" content="?61-70">
+<meta name="variant" content="?71-80">
+<meta name="variant" content="?81-90">
+<meta name="variant" content="?91-100">
+<meta name="variant" content="?101-110">
+<meta name="variant" content="?111-120">
+<meta name="variant" content="?121-130">
+<meta name="variant" content="?131-last">
+<title>Parsing of meta refresh</title>
+<meta name="timeout" content="long">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/subset-tests.js></script>
+<style>
+iframe { display:none }
+</style>
+<body>
+<script>
+
+// failure to parse is []
+// success to parse is [time, url] where url is unresolved
+
+var tests_arr = [
+ {input: '', expected: []},
+ {input: '1', expected: [1, '__filename__']},
+ {input: '1 ', expected: [1, '__filename__']},
+ {input: '1\t', expected: [1, '__filename__']},
+ {input: '1\r', expected: [1, '__filename__']},
+ {input: '1\n', expected: [1, '__filename__']},
+ {input: '1\f', expected: [1, '__filename__']},
+ {input: '1;', expected: [1, '__filename__']},
+ {input: '1,', expected: [1, '__filename__']},
+ {input: '1; url=foo', expected: [1, 'foo']},
+ {input: '1, url=foo', expected: [1, 'foo']},
+ {input: '1 url=foo', expected: [1, 'foo']},
+ {input: '1;\turl=foo', expected: [1, 'foo']},
+ {input: '1,\turl=foo', expected: [1, 'foo']},
+ {input: '1\turl=foo', expected: [1, 'foo']},
+ {input: '1;\rurl=foo', expected: [1, 'foo']},
+ {input: '1,\rurl=foo', expected: [1, 'foo']},
+ {input: '1\rurl=foo', expected: [1, 'foo']},
+ {input: '1;\nurl=foo', expected: [1, 'foo']},
+ {input: '1,\nurl=foo', expected: [1, 'foo']},
+ {input: '1\nurl=foo', expected: [1, 'foo']},
+ {input: '1;\furl=foo', expected: [1, 'foo']},
+ {input: '1,\furl=foo', expected: [1, 'foo']},
+ {input: '1\furl=foo', expected: [1, 'foo']},
+ {input: '1url=foo', expected: []},
+ {input: '1x;url=foo', expected: []},
+ {input: '1 x;url=foo', expected: [1, 'x;url=foo']},
+ {input: '1;;url=foo', expected: [1, ';url=foo']},
+ {input: ' 1 ; url = foo', expected: [1, 'foo']},
+ {input: ' 1 , url = foo', expected: [1, 'foo']},
+ {input: ' 1 ; foo', expected: [1, 'foo']},
+ {input: ' 1 , foo', expected: [1, 'foo']},
+ {input: ' 1 url = foo', expected: [1, 'foo']},
+ {input: '1; url=foo ', expected: [1, 'foo']},
+ {input: '1; url=f\to\no', expected: [1, 'foo']},
+ {input: '1; url="foo"bar', expected: [1, 'foo']},
+ {input: '1; url=\'foo\'bar', expected: [1, 'foo']},
+ {input: '1; url="foo\'bar', expected: [1, 'foo\'bar']},
+ {input: '1; url foo', expected: [1, 'url foo']},
+ {input: '1; urlfoo', expected: [1, 'urlfoo']},
+ {input: '1; urfoo', expected: [1, 'urfoo']},
+ {input: '1; ufoo', expected: [1, 'ufoo']},
+ {input: '1; "foo"bar', expected: [1, 'foo']},
+ {input: '; foo', expected: []},
+ {input: ';foo', expected: []},
+ {input: ', foo', expected: []},
+ {input: ',foo', expected: []},
+ {input: 'foo', expected: []},
+ {input: '+1; url=foo', expected: []},
+ {input: '-1; url=foo', expected: []},
+ {input: '+0; url=foo', expected: []},
+ {input: '-0; url=foo', expected: []},
+ {input: '0; url=foo', expected: [0, 'foo']},
+ {input: '+1; foo', expected: []},
+ {input: '-1; foo', expected: []},
+ {input: '+0; foo', expected: []},
+ {input: '-0; foo', expected: []},
+ {input: '0; foo', expected: [0, 'foo']},
+ {input: '+1', expected: []},
+ {input: '-1', expected: []},
+ {input: '+0', expected: []},
+ {input: '-0', expected: []},
+ {input: '0', expected: [0, '__filename__']},
+ {input: '1.9; url=foo', expected: [1, 'foo']},
+ {input: '1.9..5.; url=foo', expected: [1, 'foo']},
+ {input: '.9; url=foo', expected: [0, 'foo']},
+ {input: '0.9; url=foo', expected: [0, 'foo']},
+ {input: '0...9; url=foo', expected: [0, 'foo']},
+ {input: '0...; url=foo', expected: [0, 'foo']},
+ {input: '1e0; url=foo', expected: []},
+ {input: '1e1; url=foo', expected: []},
+ {input: '10e-1; url=foo', expected: []},
+ {input: '-0.1; url=foo', expected: []},
+];
+
+tests_arr.forEach(function(test_obj) {
+ ["<meta>", "Refresh header"].forEach(type => {
+ if(type === "Refresh header" && test_obj.input.match("[\n\r\f]")) { // See https://github.com/web-platform-tests/wpt/issues/8372 for why \f as well
+ return;
+ }
+ const filename = type === "<meta>" ? "refresh.sub.html" : "refresh.py";
+ subsetTest(async_test, function(t) {
+ var iframe = document.createElement('iframe');
+ t.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+ iframe.src = "support/" + filename + "?input=" + encodeURIComponent(test_obj.input);
+ document.body.appendChild(iframe);
+ var loadCount = 0;
+ iframe.onload = t.step_func(function() {
+ loadCount++;
+ var got = iframe.contentDocument.body.textContent.trim();
+ if (test_obj.expected.length === 0) {
+ assert_equals(got, filename);
+ if (loadCount === 1) {
+ t.step_timeout(function() {
+ t.done();
+ }, 3000); // want to make sure it doesn't redirect when it shouldn't
+ } else {
+ assert_unreached('Got > 1 load events');
+ }
+ } else {
+ if (loadCount === 2) {
+ if(test_obj.expected[1] === "__filename__") {
+ assert_equals(got, filename);
+ } else {
+ assert_equals(got, test_obj.expected[1]);
+ }
+ t.done();
+ }
+ }
+ });
+ }, type + ": " + format_value(test_obj.input));
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/remove-from-document.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/remove-from-document.html
new file mode 100644
index 0000000000..1e608a3456
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/remove-from-document.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A meta must refresh the original document even if it was removed.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-refresh">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ single_test: true });
+
+const sourceIFrame = document.createElement("iframe");
+let sourceLoadCount = 0;
+
+sourceIFrame.onload = () => {
+ ++sourceLoadCount;
+
+ if (sourceLoadCount === 2) {
+ assert_equals(sourceIFrame.contentDocument.body.textContent.trim(), "foo");
+ done();
+ }
+
+ maybeStartTest();
+};
+
+function maybeStartTest() {
+ if (sourceLoadCount === 1) {
+ sourceIFrame.contentDocument.querySelector("meta").remove();
+ }
+}
+
+sourceIFrame.src = "support/refresh.sub.html?input=" + encodeURIComponent("1; url=foo");
+
+document.body.appendChild(sourceIFrame);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/;url=foo b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/;url=foo
new file mode 100644
index 0000000000..622ff110d3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/;url=foo
@@ -0,0 +1 @@
+;url=foo
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/__dir__.headers b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/__dir__.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/__dir__.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo
new file mode 100644
index 0000000000..257cc5642c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo
@@ -0,0 +1 @@
+foo
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo'bar b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo'bar
new file mode 100644
index 0000000000..80e7410879
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/foo'bar
@@ -0,0 +1 @@
+foo'bar
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.py b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.py
new file mode 100644
index 0000000000..797c7b9412
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ response.headers.set(b"Content-Type", b"text/html")
+ response.headers.set(b"Refresh", request.GET.first(b"input"))
+ response.content = u"<!doctype html>refresh.py\n"
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.sub.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.sub.html
new file mode 100644
index 0000000000..bc97f29c62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/refresh.sub.html
@@ -0,0 +1 @@
+<!doctype html><meta http-equiv=refresh content="{{GET[input]}}">refresh.sub.html
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/ufoo b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/ufoo
new file mode 100644
index 0000000000..8fff3cf4fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/ufoo
@@ -0,0 +1 @@
+ufoo
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urfoo b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urfoo
new file mode 100644
index 0000000000..7d7373f4b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urfoo
@@ -0,0 +1 @@
+urfoo
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/url foo b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/url foo
new file mode 100644
index 0000000000..a1e6a92290
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/url foo
@@ -0,0 +1 @@
+url foo
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urlfoo b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urlfoo
new file mode 100644
index 0000000000..3e67b2f7ca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/urlfoo
@@ -0,0 +1 @@
+urlfoo
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/x;url=foo b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/x;url=foo
new file mode 100644
index 0000000000..f10371aa7b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/support/x;url=foo
@@ -0,0 +1 @@
+x;url=foo
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-lower.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-lower.html
new file mode 100644
index 0000000000..026e61c2ca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-lower.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta http-equiv="content-security-policy" content="script-src 'self'">
+<script>inline = true;</script>
+<script src="http-equiv-enumerated-ascii-case-insensitive-message.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-message.js b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-message.js
new file mode 100644
index 0000000000..1dc218a0a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-message.js
@@ -0,0 +1 @@
+parent.postMessage(null, "*");
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-mixed.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-mixed.html
new file mode 100644
index 0000000000..b4c547d342
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-mixed.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta http-equiv="CoNtEnT-sEcUrItY-pOlIcY" content="script-src 'self'">
+<script>inline = true;</script>
+<script src="http-equiv-enumerated-ascii-case-insensitive-message.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-other.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-other.html
new file mode 100644
index 0000000000..5c89a5e8bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive-other.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta http-equiv="content-Å¿ecurity-policy" content="script-src 'self'">
+<script>inline = true;</script>
+<script src="http-equiv-enumerated-ascii-case-insensitive-message.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..6d19be4149
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/http-equiv-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-meta-http-equiv">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="meta@http-equiv values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function() {
+ let loaded = 0;
+
+ // we use a message rather than the iframe’s load event to avoid dealing with
+ // spurious load events that some browsers dispatch on the initial about:blank
+ addEventListener("message", this.step_func(event => {
+ if (++loaded == 3) {
+ const iframe = document.querySelectorAll("iframe");
+
+ assert_equals(iframe[0].contentWindow.inline,
+ undefined, "lowercase valid");
+ assert_equals(iframe[1].contentWindow.inline,
+ undefined, "mixed case valid");
+ assert_equals(iframe[2].contentWindow.inline,
+ true, "non-ASCII invalid");
+
+ this.done();
+ }
+ }));
+}, "keyword content-security-policy");
+</script>
+<iframe src="http-equiv-enumerated-ascii-case-insensitive-lower.html"></iframe>
+<iframe src="http-equiv-enumerated-ascii-case-insensitive-mixed.html"></iframe>
+<iframe src="http-equiv-enumerated-ascii-case-insensitive-other.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/the-lang-attribute-012.html b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/the-lang-attribute-012.html
new file mode 100644
index 0000000000..af872d6e3a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/the-lang-attribute-012.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html >
+<head>
+<meta charset="utf-8"/>
+ <meta http-equiv="Content-Language" content="ko,zh,ja" >
+<title>Multiple languages in Content-Language meta element</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#pragma-directives'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='dom'>
+<style type='text/css'>
+ #colonlangcontroltest { color: red; font-weight: bold; width: 400px; }
+ #colonlangcontroltest:lang(xx) { display:none; }
+.test div { width: 50px; }
+
+#box:lang(ko) { width: 100px; }
+#box:lang(zh) { width: 100px; }
+#box:lang(ja) { width: 100px; }
+
+ /* styling for debugging related notes */
+ .notes span:lang(ko) { background-color: #0000FF; color: white; padding: 0 5px; }
+ .notes span:lang(zh) { background-color: #0000FF; color: white; padding: 0 5px; }
+ .notes span:lang(ja) { background-color: #0000FF; color: white; padding: 0 5px; }
+
+</style>
+</head>
+<body>
+
+
+
+<div class="test"><div id="box">&#xA0;</div></div>
+<p lang='xx' id='colonlangcontroltest'>This test failed because it relies on :lang for results, but :lang is not supported by this browser.</p>
+
+
+<!--Notes:
+
+This test uses :lang to detect whether the language has been set. If :lang is not supported, a message will appear and the test will fail.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('colonlangcontroltest').offsetWidth, 0)
+assert_equals(document.getElementById('box').offsetWidth, 50);
+}, "The UA will not recognize a language declaration in the Content-Language meta element when more than one language is declared.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/historical.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/historical.html
new file mode 100644
index 0000000000..d475f5b3c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/historical.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Historical style element features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function t(property) {
+ test(function() {
+ assert_false(property in document.createElement('style'));
+ }, 'style.' + property + ' should not be supported');
+}
+// added in https://github.com/whatwg/html/commit/29cf39d2163cfc85b67409f4e10390619ffb2b40
+// removed in https://github.com/whatwg/html/commit/c2a3b2a2e3db49c14b486a5e99acf7d10cfe8443
+t('scoped');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment-ref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment-ref.html
new file mode 100644
index 0000000000..999383c769
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>[style] Reference file</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<style>
+ h4 {
+ color: green;
+ }
+</style>
+<body>
+ <p>
+ This page tests that Style written inside HTML comment is not applied
+ </p>
+ This test passes if the text below is <b>Green. NOT Red.</b>
+ <h4>
+ This is some text.
+ </h4>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment.xhtml b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment.xhtml
new file mode 100644
index 0000000000..839548f01c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/html_style_in_comment.xhtml
@@ -0,0 +1,18 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<link rel="match" href="html_style_in_comment-ref.html"/>
+<style type="text/css">
+h4 {color: green}
+<!--
+h4 {color: red}
+-->
+</style>
+</head>
+<body>
+<p> This page tests that Style written inside HTML comment is not applied</p>
+This test passes if the text below is <b>Green. NOT Red.</b>
+<h4>
+This is some text.
+</h4>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/mutations.window.js b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/mutations.window.js
new file mode 100644
index 0000000000..1c93b40394
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/mutations.window.js
@@ -0,0 +1,48 @@
+test(t => {
+ const style = document.body.appendChild(document.createElement("style"));
+ const sheet = style.sheet;
+ t.add_cleanup(() => style.remove());
+ assert_not_equals(sheet, null);
+ style.appendChild(new Comment());
+ assert_not_equals(sheet, style.sheet);
+}, "Mutating the style element: inserting a Comment node");
+
+test(t => {
+ const style = document.body.appendChild(document.createElement("style"));
+ t.add_cleanup(() => style.remove());
+ const comment = style.appendChild(new Comment());
+ const sheet = style.sheet;
+ comment.appendData("x");
+ assert_not_equals(sheet, style.sheet);
+}, "Mutating the style element: mutating a Comment node");
+
+test(t => {
+ const style = document.body.appendChild(document.createElement("style"));
+ t.add_cleanup(() => style.remove());
+ const text1 = style.appendChild(new Text("1"));
+ const text2 = style.appendChild(new Text("2"));
+ assert_equals(style.textContent, "12");
+ assert_equals(style.childNodes.length, 2);
+ const sheet = style.sheet;
+ style.normalize();
+ assert_equals(style.childNodes.length, 1);
+ assert_not_equals(sheet, style.sheet);
+}, "Mutating the style element: using normalize()");
+
+test(t => {
+ const style = document.body.appendChild(document.createElement("style"));
+ t.add_cleanup(() => style.remove());
+ const comment = style.appendChild(new Comment());
+ const sheet = style.sheet;
+ comment.remove();
+ assert_not_equals(sheet, style.sheet);
+}, "Mutating the style element: removing a Comment node");
+
+test(t => {
+ const style = document.body.appendChild(document.createElement("style"));
+ const sheet = style.sheet;
+ t.add_cleanup(() => style.remove());
+ assert_not_equals(sheet, null);
+ style.appendChild(new DocumentFragment());
+ assert_equals(sheet, style.sheet);
+}, "Mutating the style element: inserting an empty DocumentFragment node");
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-error-01.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-error-01.html
new file mode 100644
index 0000000000..0bdef0e175
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-error-01.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>style: error events</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<script>
+//var t404 = async_test("Should get an error event for a 404 error.")
+//t404.step(function() {
+// var elt = document.createElement("style");
+// elt.onerror = t404.step_func(function() {
+// assert_true(true, "Got error event for 404 error.")
+// t404.done()
+// })
+// elt.appendChild(
+// document.createTextNode('@import 404 error;'));
+// document.getElementsByTagName("head")[0].appendChild(elt);
+//})
+var tText = async_test("Should get an error event for a text/plain response.")
+tText.step(function() {
+ var elt = document.createElement("style");
+ elt.onerror = tText.step_func(function() {
+ assert_true(true, "Got error event for 404 error.")
+ tText.done()
+ })
+ elt.appendChild(
+ document.createTextNode('@import "support/css-red.txt";'));
+ document.getElementsByTagName("head")[0].appendChild(elt);
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-load-after-mutate.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-load-after-mutate.html
new file mode 100644
index 0000000000..901c5c1ac2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style-load-after-mutate.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>The 'load' event on the style element should still fire after mutation</title>
+<link rel="help" href="https://crbug.com/1323319">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async () => {
+ const style = document.createElement('style');
+ document.head.appendChild(style);
+ style.appendChild(document.createTextNode('@import url(/support/css-red.txt);'));
+ style.appendChild(document.createTextNode('body {color: green; }'));
+
+ // The 'load' event should fire.
+ await new Promise(resolve => style.onload = resolve);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_disabled.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_disabled.html
new file mode 100644
index 0000000000..1a88bf1305
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_disabled.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: The style should not be applied if it is disabled</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ #test {
+ width: 100px;
+ }
+ </style>
+ <style id="style">
+ #test {
+ width: 50px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="test"></div>
+ <script>
+ test(function() {
+ var testElement = document.getElementById("test");
+ var style = document.getElementById("style");
+ var width1, width2;
+
+ width1 = window.getComputedStyle(testElement)["width"];
+ assert_equals(width1, "50px", "The style should be applied.");
+
+ style.disabled = true;
+ width2 = window.getComputedStyle(testElement)["width"];
+ assert_equals(width2, "100px", "The style should not be applied.");
+ }, "The style is not applied when it is disabled");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_events.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_events.html
new file mode 100644
index 0000000000..5e07e50882
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_events.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: The style events</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var tLoad = async_test("If the style is loaded successfully, the 'load' event must be fired");
+ var tError = async_test("If the style is loaded unsuccessfully, the 'error' event must be fired");
+
+ function onstyleload(e) {
+ tLoad.done();
+ }
+
+ function onstyleerror(e) {
+ tError.done();
+ }
+ </script>
+ <style onload="onstyleload()">
+ #test {
+ height: 100px;
+ width: 100px;
+ }
+ </style>
+ <style onerror="onstyleerror()">
+ @import url(nonexistent.css);
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="test"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_async.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_async.html
new file mode 100644
index 0000000000..ef8ac89c46
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_async.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Style load event should be async</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test("style load should be async");
+ var sync = true;
+ function check() {
+ assert_false(sync);
+ t.done();
+ }
+</script>
+<style onload="t.step(check)">
+</style>
+<script>
+ sync = false
+</script>
+
+<body>
+ <div id="log"></div>
+ <div id="test"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_event.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_event.html
new file mode 100644
index 0000000000..d852661791
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_load_event.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>HTML Test: The style load event should fire when textContent is changed</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#update-a-style-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ var loadCount = 0;
+ function load() { loadCount++; }
+</script>
+
+<style id=target onload="load()">
+ .box { color:red; }
+</style>
+<div class='box'>Box</div>
+
+<script>
+window.onload = () => {
+ const target = document.getElementById('target');
+ promise_test(async t => {
+ assert_equals(loadCount,1,"Style element should have loaded once by now");
+ target.textContent = `.box { color: green; }`;
+ await new Promise(resolve => target.addEventListener('load', resolve));
+ assert_equals(loadCount,2,"Style element should fire the load event when textContent changes");
+ },"style load event should fire when textContent changed");
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media.html
new file mode 100644
index 0000000000..04bcbc53ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: The style information must be applied to the environment specified by the media attribute</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-style-media">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ #test {
+ width: 100px;
+ }
+ </style>
+ <style id="style">
+ #test {
+ width: 50px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="test"></div>
+ <script>
+ test(function() {
+ var testElement = document.getElementById("test");
+ var style = document.getElementById("style");
+ var width1, width2;
+
+ width1 = window.getComputedStyle(testElement)["width"];
+ assert_equals(width1, "50px", "The style should be applied.");
+
+ style.media = "print";
+ width2 = window.getComputedStyle(testElement)["width"];
+ assert_equals(width2, "100px", "The style should not be applied.");
+ }, "The style information must be applied to the environment specified by the media attribute");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media_change.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media_change.html
new file mode 100644
index 0000000000..8b7e8440d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_media_change.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Dynamically changing HTMLStyleElement.media should change the rendering accordingly</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-element">
+ <style>
+ span {
+ color: red;
+ }
+ </style>
+ <style id="text-style" media="none">
+ span {
+ color: green;
+ }
+ </style>
+ <style id="body-style" media="aural">
+ body {
+ color: green;
+ }
+ </style>
+ </head>
+ <body>
+ <span>text</span>
+ <script>
+ test(function() {
+ var element = document.querySelector("span");
+ assert_equals(getComputedStyle(element).color, "rgb(255, 0, 0)");
+ document.getElementById("text-style").media = 'all';
+ assert_equals(getComputedStyle(element).color, "rgb(0, 128, 0)");
+ }, "change media value dynamically");
+
+ test(function() {
+ var style = document.getElementById("body-style");
+ assert_not_equals(getComputedStyle(document.querySelector("body")).color, "rgb(0, 128, 0)");
+ style.removeAttribute("media");
+ assert_equals(getComputedStyle(document.querySelector("body")).color, "rgb(0, 128, 0)");
+ }, "removing media attribute");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_non_matching_media.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_non_matching_media.html
new file mode 100644
index 0000000000..74d3554b13
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_non_matching_media.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: Non-matching media type should have stylesheet</title>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style media="unknown">
+ body { color: green }
+ </style>
+ </head>
+ <body>
+ <script>
+ test(function() {
+ assert_equals(document.styleSheets.length, 1);
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_change.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_change.html
new file mode 100644
index 0000000000..a19b3c86d7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_change.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Dynamically changing HTMLStyleElement.type should change the rendering accordingly</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-style-element">
+ <style type="no/mime">
+ body { color: green }
+ </style>
+ </head>
+ <body>
+ Text content.
+ <script>
+ var style = document.querySelector("style");
+ test(function() {
+ assert_equals(document.styleSheets.length, 0);
+ }, "Check initial styleSheets length type");
+
+ test(function() {
+ assert_not_equals(getComputedStyle(document.querySelector("body")).color, "rgb(0, 128, 0)");
+ assert_equals(document.styleSheets.length, 0);
+ style.type = "text/css";
+ assert_equals(getComputedStyle(document.querySelector("body")).color, "rgb(0, 128, 0)");
+ assert_equals(document.styleSheets.length, 1);
+ }, "Change type from invalid type to valid type");
+
+ test(function() {
+ assert_equals(getComputedStyle(document.querySelector("body")).color, "rgb(0, 128, 0)");
+ assert_equals(document.styleSheets.length, 1);
+ style.type = "no/mime";
+ assert_not_equals(getComputedStyle(document.querySelector("body")).color, "rgb(0, 128, 0)");
+ assert_equals(document.styleSheets.length, 0);
+ }, "Change type from valid type to invalid type");
+
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_html.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_html.html
new file mode 100644
index 0000000000..cc48868bd7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_html.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;style> type="" edge cases</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#update-a-style-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+#test1 { color: rgb(0, 128, 0); }
+</style>
+
+<style type="">
+#test2 { color: rgb(0, 128, 0); }
+</style>
+
+<style type="TEXT/CsS">
+#test3 { color: rgb(0, 128, 0); }
+</style>
+
+<style type=" text/css ">
+#test4 { color: rgb(0, 128, 0); }
+</style>
+
+<style type="text/css; charset=utf-8">
+#test5 { color: rgb(0, 128, 0); }
+</style>
+
+<body>
+
+<div id="test1"></div>
+<div id="test2"></div>
+<div id="test3"></div>
+<div id="test4"></div>
+<div id="test5"></div>
+
+<script>
+"use strict";
+
+test(() => {
+ assertApplied("test1");
+}, "With no type attribute, the style should apply");
+
+test(() => {
+ assertApplied("test2");
+}, "With an empty type attribute, the style should apply");
+
+test(() => {
+ assertApplied("test3");
+}, "With a mixed-case type attribute, the style should apply");
+
+test(() => {
+ assertNotApplied("test4");
+}, "With a whitespace-surrounded type attribute, the style should not apply");
+
+test(() => {
+ assertNotApplied("test5");
+}, "With a charset parameter in the type attribute, the style should not apply");
+
+function getColor(id) {
+ return window.getComputedStyle(document.getElementById(id)).color;
+}
+
+function assertApplied(id) {
+ assert_equals(getColor(id), "rgb(0, 128, 0)");
+}
+
+function assertNotApplied(id) {
+ assert_not_equals(getColor(id), "rgb(0, 128, 0)");
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_svg.svg b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_svg.svg
new file mode 100644
index 0000000000..6b0d1e874e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/style_type_svg.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:h="http://www.w3.org/1999/xhtml"
+ width="800px" height="8000px">
+ <title>&lt;style&gt; type="" edge cases</title>
+ <metadata>
+ <h:link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#update-a-style-block"/>
+ </metadata>
+ <h:script src="/resources/testharness.js"/>
+ <h:script src="/resources/testharnessreport.js"/>
+
+ <style>
+ #test1 { color: rgb(0, 128, 0); }
+ </style>
+
+ <style type="">
+ #test2 { color: rgb(0, 128, 0); }
+ </style>
+
+ <style type="TEXT/CsS">
+ #test3 { color: rgb(0, 128, 0); }
+ </style>
+
+ <style type=" text/css ">
+ #test4 { color: rgb(0, 128, 0); }
+ </style>
+
+ <style type="text/css; charset=utf-8">
+ #test5 { color: rgb(0, 128, 0); }
+ </style>
+
+ <h:body>
+ <h:div id="test1"/>
+ <h:div id="test2"/>
+ <h:div id="test3"/>
+ <h:div id="test4"/>
+ <h:div id="test5"/>
+
+ <h:script><![CDATA[
+ "use strict";
+
+ test(() => {
+ assertApplied("test1");
+ }, "With no type attribute, the style should apply");
+
+ test(() => {
+ assertApplied("test2");
+ }, "With an empty type attribute, the style should apply");
+
+ test(() => {
+ assertApplied("test3");
+ }, "With a mixed-case type attribute, the style should apply");
+
+ test(() => {
+ assertNotApplied("test4");
+ }, "With a whitespace-surrounded type attribute, the style should not apply");
+
+ test(() => {
+ assertNotApplied("test5");
+ }, "With a charset parameter in the type attribute, the style should not apply");
+
+ function getColor(id) {
+ return window.getComputedStyle(document.getElementById(id)).color;
+ }
+
+ function assertApplied(id) {
+ assert_equals(getColor(id), "rgb(0, 128, 0)");
+ }
+
+ function assertNotApplied(id) {
+ assert_not_equals(getColor(id), "rgb(0, 128, 0)");
+ }
+ ]]></h:script>
+ </h:body>
+</svg>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/support/css-red.txt b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/support/css-red.txt
new file mode 100644
index 0000000000..9ef04cbd12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/support/css-red.txt
@@ -0,0 +1 @@
+html { color: red; }
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive-ref.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive-ref.html
new file mode 100644
index 0000000000..5ac2432547
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+p:after { font-weight: bold; }
+p:after { content: "PASS"; color: green; }
+</style>
+<p>text/css treated as CSS?
+<p>TeXt/CsS treated as CSS?
+<p>text/cſs ignored?
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive.html b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive.html
new file mode 100644
index 0000000000..3c5cd152d6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-style-element/update-style-block-ascii-case-insensitive.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#update-a-style-block">
+<link rel="match" href="update-style-block-ascii-case-insensitive-ref.html">
+<meta name="assert" content="style@type values are ASCII case-insensitive">
+<style>
+p:after { font-weight: bold; }
+p:after { content: "FAIL"; color: red; }
+#c:after { content: "PASS"; color: green; }
+</style>
+<style type="text/css">#a:after { content: "PASS"; color: green; }</style>
+<style type="TeXt/CsS">#b:after { content: "PASS"; color: green; }</style>
+<style type="text/cſs">#c:after { content: "FAIL"; color: red; }</style>
+<p id="a">text/css treated as CSS?
+<p id="b">TeXt/CsS treated as CSS?
+<p id="c">text/cſs ignored?
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-01.html b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-01.html
new file mode 100644
index 0000000000..7f25400ea8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-01.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>title.text with comment and element children.</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-title-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+try {
+ var title = document.getElementsByTagName("title")[0];
+ while (title.childNodes.length)
+ title.removeChild(title.childNodes[0]);
+ title.appendChild(document.createComment("COMMENT"));
+ title.appendChild(document.createTextNode("TEXT"));
+ title.appendChild(document.createElement("a"))
+ .appendChild(document.createTextNode("ELEMENT"))
+} catch (e) {
+}
+</script>
+<script>
+test(function() {
+ assert_equals(title.text, "TEXT");
+ assert_equals(title.textContent, "TEXTELEMENT");
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-02.xhtml b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-02.xhtml
new file mode 100644
index 0000000000..068b105046
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-02.xhtml
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>title.text with comment and element children.</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-title-text"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+try {
+ var title = document.getElementsByTagName("title")[0];
+ while (title.childNodes.length)
+ title.removeChild(title.childNodes[0]);
+ title.appendChild(document.createComment("COMMENT"));
+ title.appendChild(document.createTextNode("TEXT"));
+ title.appendChild(document.createElement("a"))
+ .appendChild(document.createTextNode("ELEMENT"))
+} catch (e) {
+}
+</script>
+<script>
+test(function() {
+ assert_equals(title.text, "TEXT");
+ assert_equals(title.textContent, "TEXTELEMENT");
+})
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-03.html b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-03.html
new file mode 100644
index 0000000000..1c119a825c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-03.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title> title.text and space normalization </title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-title-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByTagName("title")[0].text,
+ " title.text and space normalization ");
+ assert_equals(document.getElementsByTagName("title")[0].textContent,
+ " title.text and space normalization ");
+ assert_equals(document.getElementsByTagName("title")[0].firstChild.nodeValue,
+ " title.text and space normalization ");
+}, "title.text and space normalization (markup)");
+[
+ "one space", "two spaces",
+ "one\ttab", "two\t\ttabs",
+ "one\nnewline", "two\n\nnewlines",
+ "one\fform feed", "two\f\fform feeds",
+ "one\rcarriage return", "two\r\rcarriage returns"
+].forEach(function(str) {
+ test(function() {
+ document.title = str;
+ var title = document.getElementsByTagName("title")[0];
+ assert_equals(title.text, str);
+ assert_equals(title.textContent, str);
+ assert_equals(title.firstChild.nodeValue, str);
+ }, "title.text and space normalization: " + format_value(str))
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-04.xhtml b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-04.xhtml
new file mode 100644
index 0000000000..de382ab4d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/document-metadata/the-title-element/title.text-04.xhtml
@@ -0,0 +1,37 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title> title.text and space normalization </title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-title-text"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_equals(document.getElementsByTagName("title")[0].text,
+ " title.text and space normalization ");
+ assert_equals(document.getElementsByTagName("title")[0].textContent,
+ " title.text and space normalization ");
+ assert_equals(document.getElementsByTagName("title")[0].firstChild.nodeValue,
+ " title.text and space normalization ");
+}, "title.text and space normalization (markup)");
+[
+ "one space", "two spaces",
+ "one\ttab", "two\t\ttabs",
+ "one\nnewline", "two\n\nnewlines",
+ "one\fform feed", "two\f\fform feeds",
+ "one\rcarriage return", "two\r\rcarriage returns"
+].forEach(function(str) {
+ test(function() {
+ document.title = str;
+ var title = document.getElementsByTagName("title")[0];
+ assert_equals(title.text, str);
+ assert_equals(title.textContent, str);
+ assert_equals(title.firstChild.nodeValue, str);
+ }, "title.text and space normalization: " + format_value(str))
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/edits/the-del-element/del_effect.html b/testing/web-platform/tests/html/semantics/edits/the-del-element/del_effect.html
new file mode 100644
index 0000000000..14297e5293
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/edits/the-del-element/del_effect.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset=UTF-8>
+<title>HTML Test: Text in the del element should be 'line-through'</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-del-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p><del>crossed-off text</del></p>
+<div id="log"></div>
+
+<script>
+ test(function() {
+ var element = document.getElementsByTagName('del')[0],
+ textDecoration = getComputedStyle(element).textDecorationLine ||
+ getComputedStyle(element).textDecoration;
+ assert_equals(textDecoration, 'line-through');
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/edits/the-ins-element/ins_effect.html b/testing/web-platform/tests/html/semantics/edits/the-ins-element/ins_effect.html
new file mode 100644
index 0000000000..6e1b344596
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/edits/the-ins-element/ins_effect.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset=UTF-8>
+<title>HTML Test: Text in the ins element should be 'underline'</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ins-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p><ins>underlined text</ins></p>
+<div id="log"></div>
+
+<script>
+ test(function() {
+ var element = document.getElementsByTagName('ins')[0],
+ textDecoration = getComputedStyle(element).textDecorationLine ||
+ getComputedStyle(element).textDecoration;
+ assert_equals(textDecoration, 'underline');
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/META.yml b/testing/web-platform/tests/html/semantics/embedded-content/META.yml
new file mode 100644
index 0000000000..c1dd8dddf9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - foolip
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-html.html b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-html.html
new file mode 100644
index 0000000000..0808538337
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-html.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="help" href="http://crbug.com/1325192">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/test-only-api.js"></script>
+
+<script type="module">
+import * as common from "./resources/common.js";
+common.runBfcacheTestForEmbeds({"type": "text/html", "src": "/resources/blank.html"});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-img.html b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-img.html
new file mode 100644
index 0000000000..7e9d713c0a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-img.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="help" href="http://crbug.com/1325192">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/test-only-api.js"></script>
+
+<script type="module">
+import * as common from "./resources/common.js";
+common.runBfcacheTestForEmbeds({'type': 'image/png', 'src': '/images/blue.png'});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-js.html b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-js.html
new file mode 100644
index 0000000000..c3b027563d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-js.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<link rel="help" href="http://crbug.com/1325192">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/test-only-api.js"></script>
+
+<script type="module">
+import * as common from "./resources/common.js";
+common.runBfcacheTestForEmbeds(
+ {'type': 'application/javascript', 'src': '/resources/test-only-api.js'});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-mp4.html b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-mp4.html
new file mode 100644
index 0000000000..fde560e5be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-mp4.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="help" href="http://crbug.com/1325192">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/test-only-api.js"></script>
+
+<script type="module">
+import * as common from "./resources/common.js";
+common.runBfcacheTestForEmbeds({'src': '/media/white.mp4'});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-not-found.html b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-not-found.html
new file mode 100644
index 0000000000..0b56b5eadc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-not-found.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="help" href="http://crbug.com/1325192">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/test-only-api.js"></script>
+
+<script type="module">
+import * as common from "./resources/common.js";
+common.runBfcacheTestForEmbeds({'type': 'image/png', 'src': '/404.png'});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-type-only.html b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-type-only.html
new file mode 100644
index 0000000000..90c9d3311c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/embedded-type-only.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="help" href="http://crbug.com/1325192">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/test-only-api.js"></script>
+
+<script type="module">
+import * as common from "./resources/common.js";
+common.runBfcacheTestForEmbeds({'type': 'text/html'});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/bfcache/resources/common.js b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/resources/common.js
new file mode 100644
index 0000000000..5bb9642a83
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/bfcache/resources/common.js
@@ -0,0 +1,46 @@
+'use strict';
+
+async function loadBfCacheTestHelperResources() {
+ await loadScript('/common/utils.js');
+ await loadScript('/common/dispatcher/dispatcher.js');
+ await loadScript(
+ '/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js');
+}
+await loadBfCacheTestHelperResources();
+
+// Runs BFCache tests for embed elements, specifically <embed> and <object>.
+// 1. Attaches the target element to first page.
+// 2. Navigates away, then back via bfcache if this case is supported by the
+// browser.
+// @param {Object} testCase - The target element's attributes to test with.
+export function runBfcacheTestForEmbeds(testCase) {
+ assert_implements(runBfcacheTest, '`runBfcacheTest()` is unavailable.');
+ assert_implements(originSameOrigin, '`originSameOrigin` is unavailable.');
+
+ const tags = [
+ {'name': 'embed', 'srcAttr': 'src'},
+ {'name': 'object', 'srcAttr': 'data'},
+ ];
+ for (const tag of tags) {
+ runBfcacheTest(
+ {
+ targetOrigin: originSameOrigin,
+ shouldBeCached: true,
+ funcBeforeNavigation: (tag, attrs) => {
+ let e = document.createElement(tag.name);
+ // Only sets defined attributes to match the intended test behavior
+ // like embedded-type-only.html test.
+ if ('type' in attrs) {
+ e.type = attrs.type;
+ }
+ if ('src' in attrs) {
+ e[tag.srcAttr] = attrs.src;
+ }
+ document.body.append(e);
+ },
+ argsBeforeNavigation: [tag, testCase]
+ },
+ `Page with <${tag.name} ` +
+ `type=${testCase.type} ${tag.srcAttr}=${testCase.src}>`);
+ }
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html b/testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html
new file mode 100644
index 0000000000..1fb71431f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html
@@ -0,0 +1,254 @@
+<!DOCTYPE {{GET[doctype]}}>
+<!-- This file should be polyglot -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Test data for hash name reference</title>
+ <style>
+ body { margin: 0 }
+ img, object { height: 1px; display:block }
+ </style>
+ </head>
+ <body>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="no-hash-name"/>
+ <object data="/images/threecolors.png" usemap="no-hash-name"></object>
+ <map name="no-hash-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-no-hash-name"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="no-hash-id"/>
+ <object data="/images/threecolors.png" usemap="no-hash-id"></object>
+ <map id="no-hash-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-no-hash-id"/>
+ </map>
+</div>
+
+<div data-expect="area-hash-name">
+ <img src="/images/threecolors.png" usemap="#hash-name"/>
+ <object data="/images/threecolors.png" usemap="#hash-name"></object>
+ <map name="hash-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-name"/>
+ </map>
+</div>
+
+<div data-expect="area-hash-id">
+ <img src="/images/threecolors.png" usemap="#hash-id"/>
+ <object data="/images/threecolors.png" usemap="#hash-id"></object>
+ <map id="hash-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-id"/>
+ </map>
+</div>
+
+<div data-expect="area-non-map-with-this-name">
+ <img src="/images/threecolors.png" usemap="#non-map-with-this-name" name="non-map-with-this-name"/>
+ <object data="/images/threecolors.png" usemap="#non-map-with-this-name"></object>
+ <map name="non-map-with-this-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-non-map-with-this-name"/>
+ </map>
+</div>
+
+<div data-expect="area-non-map-with-this-id">
+ <img src="/images/threecolors.png" usemap="#non-map-with-this-id" id="non-map-with-this-id"/>
+ <object data="/images/threecolors.png" usemap="#non-map-with-this-id"></object>
+ <map id="non-map-with-this-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-non-map-with-this-id"/>
+ </map>
+</div>
+
+<div data-expect="area-two-maps-with-this-name-1">
+ <img src="/images/threecolors.png" usemap="#two-maps-with-this-name"/>
+ <object data="/images/threecolors.png" usemap="#two-maps-with-this-name"></object>
+ <map name="two-maps-with-this-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-name-1"/>
+ </map>
+ <map name="two-maps-with-this-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-name-2"/>
+ </map>
+</div>
+
+<div data-expect="area-two-maps-with-this-id-1">
+ <img src="/images/threecolors.png" usemap="#two-maps-with-this-id"/>
+ <object data="/images/threecolors.png" usemap="#two-maps-with-this-id"></object>
+ <map id="two-maps-with-this-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-id-1"/>
+ </map>
+ <map id="two-maps-with-this-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-id-2"/>
+ </map>
+</div>
+
+<div data-expect="area-two-maps-with-this-name-or-id-1">
+ <img src="/images/threecolors.png" usemap="#two-maps-with-this-name-or-id"/>
+ <object data="/images/threecolors.png" usemap="#two-maps-with-this-name-or-id"></object>
+ <map name="two-maps-with-this-name-or-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-name-or-id-1"/>
+ </map>
+ <map id="two-maps-with-this-name-or-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-name-or-id-2"/>
+ </map>
+</div>
+
+<div data-expect="area-two-maps-with-this-id-or-name-1">
+ <img src="/images/threecolors.png" usemap="#two-maps-with-this-id-or-name"/>
+ <object data="/images/threecolors.png" usemap="#two-maps-with-this-id-or-name"></object>
+ <map id="two-maps-with-this-id-or-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-id-or-name-1"/>
+ </map>
+ <map name="two-maps-with-this-id-or-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-two-maps-with-this-id-or-name-2"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="hash-last#"/>
+ <object data="/images/threecolors.png" usemap="hash-last#"></object>
+ <map name="hash-last" id="hash-last">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-last-no-hash-in-map-name-and-id"/>
+ </map>
+ <map name="hash-last#" id="hash-last#">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-last-with-hash-in-map-name-and-id"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap=""/>
+ <object data="/images/threecolors.png" usemap=""></object>
+ <map name="" id="">
+ <area shape="rect" coords="0,0,99,50" href="#area-empty-usemap-empty-map-name-and-id"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="#"/>
+ <object data="/images/threecolors.png" usemap="#"></object>
+ <map name="" id="">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-usemap-empty-name-and-id"/>
+ </map>
+</div>
+
+<div data-expect="area-hash-space-usemap-space-map-name">
+ <img src="/images/threecolors.png" usemap="# "/>
+ <object data="/images/threecolors.png" usemap="# "></object>
+ <map name=" ">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-space-usemap-space-map-name"/>
+ </map>
+</div>
+
+<div data-expect="area-hash-LF-usemap-LF-map-id">
+ <img src="/images/threecolors.png" usemap="#&#x0A;"/>
+ <object data="/images/threecolors.png" usemap="#&#x0A;"></object>
+ <map id="&#x0A;">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-LF-usemap-LF-map-id"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="#percent-escape-name-%41"/>
+ <object data="/images/threecolors.png" usemap="#percent-escape-name-%41"></object>
+ <map name="percent-escape-name-A">
+ <area shape="rect" coords="0,0,99,50" href="#area-percent-escape-name-A"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="#percent-escape-id-%41"/>
+ <object data="/images/threecolors.png" usemap="#percent-escape-id-%41"></object>
+ <map id="percent-escape-id-A">
+ <area shape="rect" coords="0,0,99,50" href="#area-percent-escape-id-A"/>
+ </map>
+</div>
+
+<div data-expect="area-percent-escape-name-B">
+ <img src="/images/threecolors.png" usemap="#percent-escape-name-%42"/>
+ <object data="/images/threecolors.png" usemap="#percent-escape-name-%42"></object>
+ <map name="percent-escape-name-%42">
+ <area shape="rect" coords="0,0,99,50" href="#area-percent-escape-name-B"/>
+ </map>
+</div>
+
+<div data-expect="area-percent-escape-id-B">
+ <img src="/images/threecolors.png" usemap="#percent-escape-id-%42"/>
+ <object data="/images/threecolors.png" usemap="#percent-escape-id-%42"></object>
+ <map id="percent-escape-id-%42">
+ <area shape="rect" coords="0,0,99,50" href="#area-percent-escape-id-B"/>
+ </map>
+</div>
+
+<div data-expect="area-hash-space-name">
+ <img src="/images/threecolors.png" usemap="# hash-space-name"/>
+ <object data="/images/threecolors.png" usemap="# hash-space-name"></object>
+ <map name=" hash-space-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-space-name"/>
+ </map>
+</div>
+
+<div data-expect="area-hash-space-id">
+ <img src="/images/threecolors.png" usemap="# hash-space-id"/>
+ <object data="/images/threecolors.png" usemap="# hash-space-id"></object>
+ <map id=" hash-space-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-hash-space-id"/>
+ </map>
+</div>
+
+<div data-expect="area-space-before-hash-name">
+ <img src="/images/threecolors.png" usemap=" #space-before-hash-name"/>
+ <object data="/images/threecolors.png" usemap=" #space-before-hash-name"></object>
+ <map name="space-before-hash-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-space-before-hash-name"/>
+ </map>
+</div>
+
+<div data-expect="area-space-before-hash-id">
+ <img src="/images/threecolors.png" usemap=" #space-before-hash-id"/>
+ <object data="/images/threecolors.png" usemap=" #space-before-hash-id"></object>
+ <map id="space-before-hash-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-space-before-hash-id"/>
+ </map>
+</div>
+
+<div data-expect="area-garbage-before-hash-name">
+ <img src="/images/threecolors.png" usemap="http://example.org/#garbage-before-hash-name"/>
+ <object data="/images/threecolors.png" usemap="http://example.org/#garbage-before-hash-name"></object>
+ <map name="garbage-before-hash-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-garbage-before-hash-name"/>
+ </map>
+</div>
+
+<div data-expect="area-garbage-before-hash-id">
+ <img src="/images/threecolors.png" usemap="http://example.org/#garbage-before-hash-id"/>
+ <object data="/images/threecolors.png" usemap="http://example.org/#garbage-before-hash-id"></object>
+ <map id="garbage-before-hash-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-garbage-before-hash-id"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="#no-such-map"/>
+ <object data="/images/threecolors.png" usemap="#no-such-map"></object>
+ <map>
+ <area shape="rect" coords="0,0,99,50" href="#area-no-such-map"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="#different-CASE-name"/>
+ <object data="/images/threecolors.png" usemap="#different-CASE-name"></object>
+ <map name="different-case-name">
+ <area shape="rect" coords="0,0,99,50" href="#area-different-case-name"/>
+ </map>
+</div>
+
+<div data-expect="no match">
+ <img src="/images/threecolors.png" usemap="#different-CASE-id"/>
+ <object data="/images/threecolors.png" usemap="#different-CASE-id"></object>
+ <map id="different-case-id">
+ <area shape="rect" coords="0,0,99,50" href="#area-different-case-id"/>
+ </map>
+</div>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html b/testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html
new file mode 100644
index 0000000000..b00f8fe2ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>parsing a hash-name reference for img and object</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ body { margin-top: 0 }
+ iframe { height: 600px; width:50px; border-top: none }
+</style>
+
+<div id="log"></div>
+
+<iframe data-name="HTML (standards)" src="hash-name-reference-test-data.html?pipe=sub&amp;doctype=html"></iframe>
+<iframe data-name="HTML (quirks)" src="hash-name-reference-test-data.html?pipe=sub&amp;doctype=quirks"></iframe>
+<iframe data-name="XHTML" src="hash-name-reference-test-data.html?pipe=sub|header(Content-Type, application/xhtml%2Bxml)&amp;doctype=html"></iframe>
+
+<script>
+setup({explicit_done: true});
+
+onload = function() {
+ var iframes = document.querySelectorAll('iframe');
+ iframes.forEach(function(iframe) {
+ var iframeName = iframe.getAttribute('data-name');
+ var doc = iframe.contentDocument;
+ var divs = doc.querySelectorAll('div[data-expect]');
+ var div, img, object;
+ for (var i = 0; i < divs.length; ++i) {
+ div = divs[i];
+ img = div.querySelector('img');
+ object = div.querySelector('object');
+ [img, object].forEach(function(elm) {
+ test(function(t) {
+ var expected = div.getAttribute('data-expect');
+ var expected_elm = (expected === 'no match' || elm === object) ? elm : div.querySelector('area[href="#' + expected + '"]');
+ var got_elm = doc.elementFromPoint(elm.offsetLeft, elm.offsetTop);
+ assert_not_equals(expected_elm, null, 'sanity check (data-expect value wrong?)');
+ assert_not_equals(got_elm, null, 'sanity check (too many tests to fit in viewport?)');
+ assert_equals(got_elm, expected_elm);
+ }, iframeName + ' ' + elm.tagName + ' usemap=' + format_value(elm.useMap));
+ });
+ }
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_controls_present-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_controls_present-manual.html
new file mode 100644
index 0000000000..38faa4d00a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_controls_present-manual.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Audio Test: audio_controls_present.html</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-controls" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the controls attribute is present in the audio element that expecting the user agent exposes a controller user interface" />
+ </head>
+ <body>
+ <p>Test passes if a controller user interface appears below and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <audio id="m" controls>The user agent doesn't support media element.</audio>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_base.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_base.html
new file mode 100644
index 0000000000..418e1b19c3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_base.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Audio Test: audio_loop_base</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-loop" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if audio.loop is set to true that expecting the seeking event is fired more than once" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <audio id="m" controls>The user agent doesn't support media element.</audio>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ var name = document.getElementsByName("assert")[0].content;
+ var t = async_test(name);
+
+ var looped = false;
+
+ function startTest() {
+ if (looped) {
+ t.step(function() {
+ assert_true(true, "looped");
+ });
+ t.done();
+ media.pause();
+ }
+
+ looped = true;
+ }
+
+ media.addEventListener("error", t.unreached_func());
+ media.addEventListener("seeking", startTest, false);
+ media.loop = true;
+ media.src = getAudioURI("/media/sound_0") + "?" + new Date() + Math.random();
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_seek_to_eos.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_seek_to_eos.html
new file mode 100644
index 0000000000..01a2d4bea9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_loop_seek_to_eos.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Seeking to the end of looping audio</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<audio id="a" controls loop></audio>
+<script type="text/javascript">
+
+promise_test(async t => {
+ const a = document.getElementById("a");
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ await a.play();
+
+ // Seek to the end of audio (EOS). However, as audio is looping, it should
+ // keep playing after seeking.
+ a.currentTime = a.duration;
+ await new Promise(r => a.onseeked = r);
+ await new Promise(r => a.ontimeupdate = r);
+ assert_false(a.paused);
+} , "seeking to the end of looping audio");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_overriding_volume-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_overriding_volume-manual.html
new file mode 100644
index 0000000000..cc1892ce89
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_overriding_volume-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Audio Test: audio_muted_overriding_volume</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-muted" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the muted attribute is present in the audio element with volume is set to loudest that expecting the user hears no sound" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the audio is playing without sound output and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <audio id="m" controls muted>The user agent doesn't support media element.</audio>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ media.volume = 1.0;
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_present-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_present-manual.html
new file mode 100644
index 0000000000..16d6f07eed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_muted_present-manual.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Audio Test: audio_muted_present</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-muted" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the muted attribute is present in the audio element that expecting the user hears no sound" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the audio is playing without sound output and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <audio id="m" controls muted>The user agent doesn't support media element.</audio>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_check.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_check.html
new file mode 100644
index 0000000000..b467c702a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_check.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Audio Test: audio_volume_check</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-volume" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check that audio.volume returns the value of the muted content attribute" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <audio id="m">The user agent doesn't support media element.</audio>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ var VOLUME = {
+ 'SILENT' : 0.0,
+ 'NORMAL' : 0.5,
+ 'LOUDEST' : 1.0,
+ 'LOWER' : -1.1,
+ 'UPPER' : 1.1,
+ };
+
+ test(function() {
+ assert_false(media.volume < VOLUME.SILENT || media.volume > VOLUME.LOUDEST, "media.volume outside the range 0.0 to 1.0 inclusive");
+ }, "Check if the intial value of the audio.volume is in the range 0.0 to 1.0 inclusive");
+
+ function volume_setting(vol, name)
+ {
+ if (vol < VOLUME.SILENT || vol > VOLUME.LOUDEST) {
+ try {
+ media.volume = vol;
+ test(function() {
+ assert_true(false, "media.volume setting exception");
+ }, name);
+ } catch(e) {
+ test(function() {
+ // 1 should be e.IndexSizeError or e.INDEX_SIZE_ERR in previous spec
+ assert_equals(e.code, 1, "media.volume setting exception");
+ }, name);
+ }
+ } else {
+ media.volume = vol;
+ test(function() {
+ assert_equals(media.volume, vol, "media.volume new value");
+ }, name);
+ }
+ }
+
+ volume_setting(VOLUME.NORMAL, "Check if audio.volume is able to set to new value in the range 0.0 to 1.0");
+ volume_setting(VOLUME.SILENT, "Check if media.volume is able to set to new value 0.0 as silent");
+ volume_setting(VOLUME.LOUDEST, "Check if media.volume is able to set to new value 1.0 as loudest");
+ volume_setting(VOLUME.LOWER, "Check if media.volume is set to new value less than 0.0 that expecting an IndexSizeError exception is to be thrown");
+ volume_setting(VOLUME.UPPER, "Check if audio.volume is set to new value greater than 1.0 that expecting an IndexSizeError exception is to be thrown");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_loudest-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_loudest-manual.html
new file mode 100644
index 0000000000..a623e8f5c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_loudest-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Audio Test: audio_volume_loudest</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-volume" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the volume attribute is set to 1.0 as loudest in the audio element that expecting the user hears sound loudly" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the audio is playing with sound heard and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <audio id="m" controls>The user agent doesn't support media element.</audio>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ media.volume = 1.0;
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_silent-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_silent-manual.html
new file mode 100644
index 0000000000..257bd46289
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/audio_volume_silent-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Audio Test: audio_volume_silent</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-volume" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the volume attribute is set to 0.0 as silent in the audio element that expecting the user hears no sound" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the audio is playing without sound heard and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <audio id="m" controls volume=0.0>The user agent doesn't support media element.</audio>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ media.volume = 0.0;
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
new file mode 100644
index 0000000000..6f11f8995b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<body>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <script src=/resources/testdriver.js></script>
+ <script src=/resources/testdriver-vendor.js></script>
+ <script src=/common/media.js></script>
+ <script src=/feature-policy/resources/featurepolicy.js></script>
+ <script src=/feature-policy/resources/autoplay.js></script>
+ <script>
+ 'use strict';
+ const relative_path = '/feature-policy/resources/feature-policy-autoplay.html';
+ const base_src = '/feature-policy/resources/redirect-on-load.html#';
+ const same_origin_src = base_src + relative_path;
+ const cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' +
+ relative_path;
+ const header = 'Feature-Policy allow="autoplay"';
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability(
+ 'autoplay', t, same_origin_src,
+ expect_feature_available_default, 'autoplay');
+ });
+ }, header + ' allows same-origin navigation in an iframe.');
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability(
+ 'autoplay', t, cross_origin_src,
+ expect_feature_unavailable_default, 'autoplay');
+ });
+ }, header + ' disallows cross-origin navigation in an iframe.');
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html
new file mode 100644
index 0000000000..59b33d7c4d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy-attribute.https.sub.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<body>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <script src=/resources/testdriver.js></script>
+ <script src=/resources/testdriver-vendor.js></script>
+ <script src=/common/media.js></script>
+ <script src=/feature-policy/resources/featurepolicy.js></script>
+ <script src=/feature-policy/resources/autoplay.js></script>
+ <script>
+ 'use strict';
+ const same_origin_src = '/feature-policy/resources/feature-policy-autoplay.html';
+ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+ same_origin_src;
+ const feature_name = 'Feature policy "autoplay"';
+ const header = 'allow="autoplay" attribute';
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability(
+ 'autoplay', t, same_origin_src,
+ expect_feature_available_default, 'autoplay');
+ });
+ }, feature_name + ' can be enabled in same-origin iframe using ' + header);
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability(
+ 'autoplay', t, cross_origin_src,
+ expect_feature_available_default, 'autoplay');
+ });
+ }, feature_name + ' can be enabled in cross-origin iframe using ' + header);
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html
new file mode 100644
index 0000000000..63479c0cb6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<body>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <script src=/resources/testdriver.js></script>
+ <script src=/resources/testdriver-vendor.js></script>
+ <script src=/common/media.js></script>
+ <script src=/feature-policy/resources/featurepolicy.js></script>
+ <script src=/feature-policy/resources/autoplay.js></script>
+ <script>
+ 'use strict';
+ const same_origin_src = '/feature-policy/resources/feature-policy-autoplay.html';
+ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+ same_origin_src;
+ const header = 'Feature-Policy header: autoplay *';
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ isAutoplayAllowed().then(t.step_func_done((result) => {
+ assert_true(result);
+ }));
+ });
+ }, header + ' allows the top-level document.');
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability('autoplay', t, same_origin_src,
+ expect_feature_available_default);
+ });
+ }, header + ' allows same-origin iframes.');
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability('autoplay', t, cross_origin_src,
+ expect_feature_available_default);
+ });
+ }, header + ' allows cross-origin iframes.');
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.headers b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000000..08461fadc2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: autoplay *
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html
new file mode 100644
index 0000000000..763073e437
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<body>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <script src=/resources/testdriver.js></script>
+ <script src=/resources/testdriver-vendor.js></script>
+ <script src=/common/media.js></script>
+ <script src=/feature-policy/resources/featurepolicy.js></script>
+ <script src=/feature-policy/resources/autoplay.js></script>
+ <script>
+ 'use strict';
+ const same_origin_src = '/feature-policy/resources/feature-policy-autoplay.html';
+ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+ same_origin_src;
+ const header = 'Default "autoplay" feature policy ["self"]';
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ isAutoplayAllowed().then(t.step_func_done((result) => {
+ assert_true(result);
+ }));
+ });
+ }, header + ' allows the top-level document.');
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability('autoplay', t, same_origin_src,
+ expect_feature_available_default);
+ });
+ }, header + ' allows same-origin iframes.');
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability('autoplay', t, cross_origin_src,
+ expect_feature_unavailable_default,);
+ });
+ }, header + ' disallows cross-origin iframes.');
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html
new file mode 100644
index 0000000000..3dd3afbf77
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<body>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <script src=/resources/testdriver.js></script>
+ <script src=/resources/testdriver-vendor.js></script>
+ <script src=/common/media.js></script>
+ <script src=/feature-policy/resources/featurepolicy.js></script>
+ <script src=/feature-policy/resources/autoplay.js></script>
+ <script>
+ 'use strict';
+ const same_origin_src = '/feature-policy/resources/feature-policy-autoplay.html';
+ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+ same_origin_src;
+ const header = 'Feature-Policy header: autoplay "none"';
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ isAutoplayAllowed().then(t.step_func_done((result) => {
+ assert_true(result);
+ }));
+ });
+ }, header + ' has no effect on the top level document.');
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability('autoplay', t, same_origin_src,
+ expect_feature_unavailable_default);
+ });
+ }, header + ' disallows same-origin iframes.');
+
+ async_test(t => {
+ simulateGesture(t, () => {
+ test_feature_availability('autoplay', t, cross_origin_src,
+ expect_feature_unavailable_default,);
+ });
+ }, header + ' disallows cross-origin iframes.');
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.headers b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000000..69ce436270
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: autoplay 'none'
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-supported-by-feature-policy.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-supported-by-feature-policy.html
new file mode 100644
index 0000000000..af4de6bf89
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that autoplay is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/infrastructure.html#policy-controlled-features">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ assert_in_array('autoplay', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise autoplay.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-with-broken-track.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-with-broken-track.html
new file mode 100644
index 0000000000..f687edf198
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/autoplay-with-broken-track.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/media.html#text-track-model">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<script>
+// Media elements have a "list of pending text tracks" which should be populated
+// with text tracks with readyState "loading". When the text track src is
+// invalid or points to a non-existent resource, it shouldn't be possible to
+// block the media element's readyState indefinitely.
+function t(trackSrc) {
+ const track = document.createElement('track');
+ track.src = trackSrc;
+ track.default = true;
+ async_test(t => {
+ const video = document.createElement('video');
+ video.autoplay = true;
+ video.controls = true; // for visual inspection, not part of test
+ video.src = getVideoURI('/media/movie_5');
+ video.appendChild(track);
+ document.body.appendChild(video);
+ // The playing event isn't used because it's fired in Safari even when the
+ // playback doesn't actually start.
+ video.ontimeupdate = t.step_func(() => {
+ if (video.currentTime > 0)
+ t.done();
+ });
+ }, `<video autoplay> with ${track.outerHTML} child`);
+}
+t("invalid://url");
+t("404");
+t("");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/controlsList.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/controlsList.tentative.html
new file mode 100644
index 0000000000..11144839ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/controlsList.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Test controlsList attribute</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/6715">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const allowedValues = [
+ "nodownload",
+ "nofullscreen",
+ "noplaybackrate",
+ "noremoteplayback",
+];
+
+function testControlsList(tagName) {
+ const element = document.createElement(tagName);
+
+ // Test that supports() is returning true for allowed values.
+ for (const value of allowedValues) {
+ assert_true(
+ element.controlsList.supports(value),
+ `tag = ${element.tagName}, value = ${value} must be supported`
+ );
+ }
+}
+
+["audio", "video"].forEach((tagName) => {
+ test(
+ () => testControlsList(tagName),
+ `Test controlsList allowed values for <${tagName}>`
+ );
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/error-codes/error.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/error-codes/error.html
new file mode 100644
index 0000000000..42d86e49b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/error-codes/error.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>error</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<script>
+function error_test(tagName, src) {
+ test(function() {
+ assert_equals(document.createElement(tagName).error, null);
+ }, tagName + '.error initial value');
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = src;
+ e.onerror = t.unreached_func();
+ e.onloadeddata = t.step_func(function() {
+ assert_equals(e.error, null);
+ t.done();
+ });
+ }, tagName + '.error after successful load');
+
+ // TODO: MEDIA_ERR_ABORTED, MEDIA_ERR_NETWORK, MEDIA_ERR_DECODE
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = '';
+ e.onerror = t.step_func(function() {
+ assert_true(e.error instanceof MediaError);
+ assert_equals(e.error.code, 4);
+ assert_equals(e.error.code, e.error.MEDIA_ERR_SRC_NOT_SUPPORTED);
+ assert_equals(typeof e.error.message, 'string', 'error.message type');
+ t.done();
+ });
+ }, tagName + '.error after setting src to the empty string');
+}
+
+error_test('audio', getAudioURI('/media/sound_5'));
+error_test('video', getVideoURI('/media/movie_5'));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay.html
new file mode 100644
index 0000000000..e5c632bc17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - canplay</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger canplay event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplay", t.step_func(function() {
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - canplay");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger canplay event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplay", t.step_func(function() {
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - canplay");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay_noautoplay.html
new file mode 100644
index 0000000000..b43f8d052a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplay_noautoplay.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - canplay</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function () {
+ var t = async_test("setting src attribute on non-autoplay audio should trigger canplay event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplay", t.step_func_done(), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - canplay");
+
+test(function () {
+ var t = async_test("setting src attribute on non-autoplay video should trigger canplay event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplay", t.step_func_done(), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - canplay");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough.html
new file mode 100644
index 0000000000..b0895a97c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - canplaythrough</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger canplaythrough event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplaythrough", t.step_func(function() {
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - canplaythrough");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger canplaythrough event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplaythrough", t.step_func(function() {
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - canplaythrough");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough_noautoplay.html
new file mode 100644
index 0000000000..195b464f01
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_canplaythrough_noautoplay.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - canplaythrough</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay audio should trigger canplaythrough event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplaythrough", t.step_func_done(), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - canplaythrough");
+
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay video should trigger canplaythrough event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplaythrough", t.step_func_done(), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - canplaythrough");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata.html
new file mode 100644
index 0000000000..f502c595e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadeddata</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger loadeddata event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadeddata", t.step_func(function() {
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadeddata");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger loadeddata event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadeddata", t.step_func(function() {
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - loadeddata");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata_noautoplay.html
new file mode 100644
index 0000000000..08b2f2f86e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadeddata_noautoplay.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadeddata</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay audio should trigger loadeddata event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadeddata", t.step_func_done(), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadeddata");
+
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay video should trigger loadeddata event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadeddata", t.step_func_done(), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - loadeddata");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata.html
new file mode 100644
index 0000000000..5a0731e811
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadedmetadata</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger loadedmetadata event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadedmetadata", t.step_func(function() {
+ t.done();
+ a.pause();
+ }));
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadedmetadata");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger loadedmetadata event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadedmetadata", t.step_func(function() {
+ t.done();
+ v.pause();
+ }));
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - loadedmetadata");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata_noautoplay.html
new file mode 100644
index 0000000000..b460317f8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadedmetadata_noautoplay.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadedmetadata</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay audio should trigger loadedmetadata event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadedmetadata", t.step_func_done(), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadedmetadata");
+
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay video should trigger loadedmetadata event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadedmetadata", t.step_func_done(), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events, loadedmetadata");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart.html
new file mode 100644
index 0000000000..192821a961
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadstart</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger loadstart event");
+ var a = document.getElementById("a");
+ a.addEventListener("loadstart", function() {
+ t.done();
+ a.pause();
+ }, false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadstart");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger loadstart event");
+ var v = document.getElementById("v");
+ v.addEventListener("loadstart", function() {
+ t.done();
+ v.pause();
+ }, false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - loadstart");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart_noautoplay.html
new file mode 100644
index 0000000000..10af32afcb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_loadstart_noautoplay.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadstart</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay audio should trigger loadstart event");
+ var a = document.getElementById("a");
+ a.addEventListener("loadstart", function() {
+ t.done();
+ }, false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadstart");
+
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay video should trigger loadstart event");
+ var v = document.getElementById("v");
+ v.addEventListener("loadstart", function() {
+ t.done();
+ }, false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - loadstart");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_canplaythrough.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_canplaythrough.html
new file mode 100644
index 0000000000..e1bae90ed0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_canplaythrough.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - canplay, then canplaythrough</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger canplay then canplaythrough event");
+ var a = document.getElementById("a");
+ var found_canplay = false;
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplay", t.step_func(function() {
+ found_canplay = true;
+ }));
+ a.addEventListener("canplaythrough", t.step_func(function() {
+ assert_true(found_canplay);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - canplay, then canplaythrough");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger canplay then canplaythrough event");
+ var v = document.getElementById("v");
+ var found_canplay = false;
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplay", t.step_func(function() {
+ found_canplay = true;
+ }));
+ v.addEventListener("canplaythrough", t.step_func(function() {
+ assert_true(found_canplay);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - canplay, then canplaythrough");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_playing.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_playing.html
new file mode 100644
index 0000000000..3571e5151c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_canplay_playing.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - canplay, then playing</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger canplay then playing event");
+ var a = document.getElementById("a");
+ var found_canplay = false;
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplay", t.step_func(function() {
+ found_canplay = true;
+ }));
+ a.addEventListener("playing", t.step_func(function() {
+ assert_true(found_canplay);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - canplay, then playing");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger canplay then playing event");
+ var v = document.getElementById("v");
+ var found_canplay = false;
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplay", t.step_func(function() {
+ found_canplay = true;
+ }));
+ v.addEventListener("playing", t.step_func(function() {
+ assert_true(found_canplay);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - canplay, then playing");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadedmetadata_loadeddata.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadedmetadata_loadeddata.html
new file mode 100644
index 0000000000..71aeca50c1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadedmetadata_loadeddata.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadedmetadata, then loadeddata</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger loadedmetadata then loadeddata event");
+ var a = document.getElementById("a");
+ var found_loadedmetadata = false;
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadedmetadata", t.step_func(function() {
+ found_loadedmetadata = true;
+ }));
+ a.addEventListener("loadeddata", t.step_func(function() {
+ assert_true(found_loadedmetadata);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadedmetadata, then loadeddata");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger loadedmetadata then loadeddata event");
+ var v = document.getElementById("v");
+ var found_loadedmetadata = false;
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadedmetadata", t.step_func(function() {
+ found_loadedmetadata = true;
+ }));
+ v.addEventListener("loadeddata", t.step_func(function() {
+ assert_true(found_loadedmetadata);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - loadedmetadata, then loadeddata");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadstart_progress.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadstart_progress.html
new file mode 100644
index 0000000000..c6e1dbe07a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_order_loadstart_progress.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - loadstart, then progress</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger loadstart then progress event");
+ var a = document.getElementById("a");
+ var found_loadstart = false;
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadstart", t.step_func(function() {
+ found_loadstart = true;
+ }));
+ a.addEventListener("progress", t.step_func(function() {
+ assert_true(found_loadstart);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - loadstart, then progress");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger loadstart then progress event");
+ var v = document.getElementById("v");
+ var found_loadstart = false;
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadstart", t.step_func(function() {
+ found_loadstart = true;
+ }));
+ v.addEventListener("progress", t.step_func(function() {
+ assert_true(found_loadstart);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - loadstart, then progress");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause.html
new file mode 100644
index 0000000000..841e124d5b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - pause</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("calling pause() on autoplay audio should trigger pause event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("pause", t.step_func_done(), false);
+ a.addEventListener("play", t.step_func(function() {
+ a.pause(); // pause right after play
+ }));
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - pause");
+
+test(function() {
+ var t = async_test("calling pause() on autoplay video should trigger pause event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("pause", t.step_func_done(), false);
+ v.addEventListener("play", t.step_func(function() {
+ v.pause(); // pause right after play
+ }));
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - pause");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause_noautoplay.html
new file mode 100644
index 0000000000..c6d9d5920b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_pause_noautoplay.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - pause</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+promise_test(function(t) {
+ var async_t = async_test("calling play() then pause() on non-autoplay audio should trigger pause event");
+ var a = document.getElementById("a");
+ a.addEventListener("pause", function() {
+ async_t.done();
+ }, false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ var play_promise = a.play();
+ a.pause();
+ return promise_rejects_dom(t, "AbortError", play_promise, "pause() should reject all pending play Promises");
+}, "audio events - pause");
+
+promise_test(function(t) {
+ var async_t = async_test("calling play() then pause() on non-autoplay video should trigger pause event");
+ var v = document.getElementById("v");
+ v.addEventListener("pause", function() {
+ async_t.done();
+ }, false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ var play_promise = v.play()
+ v.pause();
+ return promise_rejects_dom(t, "AbortError", play_promise, "pause() should reject all pending play Promises");
+}, "video events - pause");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play.html
new file mode 100644
index 0000000000..f96c35113b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - play</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger play event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("play", t.step_func(function() {
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - play");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger play event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("play", t.step_func(function() {
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - play");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play_noautoplay.html
new file mode 100644
index 0000000000..0dff37c800
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_play_noautoplay.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - play</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+promise_test(function(t) {
+ var async_t = async_test("calling play() on audio should trigger play event");
+ var a = document.getElementById("a");
+ a.addEventListener("play", async_t.step_func(function() {
+ a.pause();
+ async_t.done();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ return promise_rejects_dom(t, "AbortError", a.play(), "pause() should reject all pending play Promises");
+}, "audio events - play");
+
+promise_test(function(t) {
+ var async_t = async_test("calling play() on video should trigger play event");
+ var v = document.getElementById("v");
+ v.addEventListener("play", async_t.step_func(function() {
+ v.pause();
+ async_t.done();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ return promise_rejects_dom(t, "AbortError", v.play(), "pause() should reject all pending play Promises");
+}, "video events - play");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing.html
new file mode 100644
index 0000000000..18204c457a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - playing</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger playing event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("playing", t.step_func(function() {
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - playing");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger playing event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("playing", t.step_func(function() {
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - playing");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing_noautoplay.html
new file mode 100644
index 0000000000..e9714d7ec5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_playing_noautoplay.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - playing</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("calling play() on audio should trigger playing event");
+ var a = document.getElementById("a");
+ a.addEventListener("playing", function() {
+ t.done();
+ a.pause();
+ });
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ a.play();
+}, "audio events - playing");
+
+test(function() {
+ var t = async_test("calling play() on video should trigger playing event");
+ var v = document.getElementById("v");
+ v.addEventListener("playing", function() {
+ t.done();
+ v.pause();
+ });
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ v.play();
+}, "video events - playing");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress.html
new file mode 100644
index 0000000000..ae4496c99f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - progress</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on autoplay audio should trigger progress event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("progress", t.step_func(function() {
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - progress");
+
+test(function() {
+ var t = async_test("setting src attribute on autoplay video should trigger progress event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("progress", t.step_func(function() {
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - progress");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress_noautoplay.html
new file mode 100644
index 0000000000..8b32448b9f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_progress_noautoplay.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - progress</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay audio should trigger progress event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("progress", t.step_func_done(), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - progress");
+
+test(function() {
+ var t = async_test("setting src attribute on non-autoplay video should trigger progress event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("progress", t.step_func_done(), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - progress");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate.html
new file mode 100644
index 0000000000..0909c864e3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - timeupdate</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+var ta = async_test("setting src attribute on a sufficiently long autoplay audio should trigger timeupdate event");
+var a = document.getElementById("a");
+a.addEventListener("timeupdate", function() {
+ ta.done();
+ a.pause();
+}, false);
+a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+
+var tv = async_test("setting src attribute on a sufficiently long autoplay video should trigger timeupdate event");
+var v = document.getElementById("v");
+v.addEventListener("timeupdate", function() {
+ tv.done();
+ v.pause();
+}, false);
+v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate_noautoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate_noautoplay.html
new file mode 100644
index 0000000000..2738a3b4ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_timeupdate_noautoplay.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - timeupdate</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("calling play() on a sufficiently long audio should trigger timeupdate event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("timeupdate", t.step_func(function() {
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ a.play();
+}, "audio events - timeupdate");
+
+test(function() {
+ var t = async_test("calling play() on a sufficiently long video should trigger timeupdate event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("timeupdate", t.step_func(function() {
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ v.play();
+}, "video events - timeupdate");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_volumechange.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_volumechange.html
new file mode 100644
index 0000000000..3481947e87
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/event_volumechange.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<title>volumechange event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function volumechange_test(tagName) {
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ assert_equals(e.volume, 1);
+ e.volume = 0.5;
+ assert_equals(e.volume, 0.5);
+ e.onvolumechange = t.step_func(function() {
+ assert_equals(e.volume, 0.5);
+ e.volume = 1;
+ assert_equals(e.volume, 1);
+ e.onvolumechange = t.step_func(function() {
+ assert_equals(e.volume, 1);
+ t.done();
+ });
+ });
+ }, "setting " + tagName + ".volume fires volumechange");
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ assert_false(e.muted);
+ e.muted = true;
+ assert_true(e.muted);
+ e.onvolumechange = t.step_func(function() {
+ assert_true(e.muted);
+ e.muted = false;
+ assert_false(e.muted);
+ e.onvolumechange = t.step_func(function() {
+ assert_false(e.muted);
+ t.done();
+ });
+ });
+ }, "setting " + tagName + ".muted fires volumechange");
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.volume = e.volume;
+ e.muted = e.muted;
+ e.onvolumechange = t.step_func(function() {
+ assert_unreached();
+ });
+ var e2 = document.createElement(tagName);
+ e2.muted = !e2.muted;
+ e2.onvolumechange = t.step_func(function() {
+ t.done();
+ });
+ }, "setting " + tagName + ".volume/muted to the same value does not fire volumechange");
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.muted = !e.muted;
+ e.volume = 1 - e.volume;
+ e.muted = !e.muted;
+ e.volume = 1 - e.volume;
+ var volumechange_count = 0;
+ e.onvolumechange = t.step_func(function() {
+ volumechange_count++;
+ if (volumechange_count == 4) {
+ t.done();
+ }
+ });
+ }, "setting " + tagName + ".volume/muted repeatedly fires volumechange repeatedly");
+}
+
+volumechange_test("audio");
+volumechange_test("video");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/historical.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/historical.html
new file mode 100644
index 0000000000..d7395632eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/historical.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<title>Historical media element features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function t(property, tagName) {
+ var tagNames = tagName ? [tagName] : ['audio', 'video'];
+ tagNames.forEach(function(tagName) {
+ test(function() {
+ assert_false(property in document.createElement(tagName));
+ }, tagName + '.' + property + ' should not be supported');
+ });
+}
+
+t('bufferingRate'); // added in r678, removed in r2872.
+t('start'); // added in r692, removed in r2401.
+t('end'); // added in r692, removed in r2401.
+t('loopStart'); // added in r692, removed in r2401.
+t('loopEnd'); // added in r692, removed in r2401.
+t('loopCount'); // added in r692, replaced with playCount in r1105.
+t('currentLoop'); // added in r692, removed in r2401.
+t('addCuePoint'); // added in r721, replaced with addCueRange in r1106.
+t('removeCuePoint'); // added in r721, replaced with removeCueRanges in r1106.
+t('playCount'); // added in r1105, removed in r2401.
+t('addCueRange'); // added in r1106, removed in r5070.
+t('removeCueRanges'); // added in r1106, removed in r5070.
+t('pixelratio', 'source'); // added in r1629, removed in r2493.
+t('bufferedBytes'); // added in r1630, removed in r2405.
+t('totalBytes'); // added in r1630, removed in r2405.
+t('bufferingThrottled'); // added in r1632, removed in r2872.
+t('autobuffer'); // added in r2855, replaced with preload in r4811.
+t('startTime'); // added in r3035, replaced with initialTime in r5310.
+t('startOffsetTime'); // added in r5310, replaced with startDate in r7045.
+t('initialTime'); // added in r5310, removed in r7046.
+t('audio', 'video'); // added in r5636, replaced with muted in r5991.
+t('startDate'); // added in r7045, replaced with getStartDate() in r8113.
+t('mozSrcObject'); // never in the spec
+t('mozPreservesPitch'); // prefixed version should be removed.
+t('webkitPreservesPitch'); // prefixed version should be removed.
+
+// TextTrackCue constructor: added in r5723, removed in r7742.
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new TextTrackCue(0, 0, '');
+ });
+}, 'TextTrackCue constructor should not be supported');
+
+// added in https://github.com/whatwg/html/commit/66c5b32240c202c74f475872e7ea2cd163777b4a
+// removed in https://github.com/whatwg/html/commit/634698e70ea4586d58c989fa7d2cbfcad20d33e6
+t('mediaGroup');
+t('controller');
+test(function() {
+ assert_false('MediaController' in window);
+}, 'MediaController constructor should not be supported');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/addTextTrack.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/addTextTrack.html
new file mode 100644
index 0000000000..0e1a48f78a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/addTextTrack.html
@@ -0,0 +1,116 @@
+<!doctype html>
+<title>HTMLMediaElement.addTextTrack</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+var video = document.createElement('video');
+test(function(){
+ assert_throws_js(TypeError, function(){
+ video.addTextTrack('foo');
+ });
+ assert_throws_js(TypeError, function(){
+ video.addTextTrack(undefined);
+ });
+ assert_throws_js(TypeError, function(){
+ video.addTextTrack(null);
+ });
+}, document.title + ' bogus first arg');
+
+test(function(){
+ assert_throws_js(TypeError, function(){
+ video.addTextTrack('SUBTITLES');
+ });
+}, document.title + ' uppercase first arg');
+
+test(function(){
+ var t = video.addTextTrack('subtitles');
+ assert_equals(t.kind, 'subtitles');
+ assert_equals(t.label, '');
+ assert_equals(t.language, '');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' subtitles first arg');
+
+test(function(){
+ var t = video.addTextTrack('captions');
+ assert_equals(t.kind, 'captions');
+ assert_equals(t.label, '');
+ assert_equals(t.language, '');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' captions first arg');
+
+test(function(){
+ var t = video.addTextTrack('descriptions');
+ assert_equals(t.kind, 'descriptions');
+ assert_equals(t.label, '');
+ assert_equals(t.language, '');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' descriptions first arg');
+
+test(function(){
+ var t = video.addTextTrack('chapters');
+ assert_equals(t.kind, 'chapters');
+ assert_equals(t.label, '');
+ assert_equals(t.language, '');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' chapters first arg');
+
+test(function(){
+ var t = video.addTextTrack('metadata');
+ assert_equals(t.kind, 'metadata');
+ assert_equals(t.label, '');
+ assert_equals(t.language, '');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' metadata first arg');
+
+test(function(){
+ var t = video.addTextTrack('subtitles', undefined, undefined);
+ assert_equals(t.kind, 'subtitles');
+ assert_equals(t.label, '');
+ assert_equals(t.language, '');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' undefined second and third arg');
+
+test(function(){
+ var t = video.addTextTrack('subtitles', null, null);
+ assert_equals(t.kind, 'subtitles');
+ assert_equals(t.label, 'null');
+ assert_equals(t.language, 'null');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' null second and third arg');
+
+test(function(){
+ var t = video.addTextTrack('subtitles', 'foo', 'bar');
+ assert_equals(t.kind, 'subtitles');
+ assert_equals(t.label, 'foo');
+ assert_equals(t.language, 'bar');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' foo and bar second and third arg');
+
+test(function(){
+ var t = video.addTextTrack('subtitles', 'foo');
+ assert_equals(t.kind, 'subtitles');
+ assert_equals(t.label, 'foo');
+ assert_equals(t.language, '');
+ assert_equals(t.mode, 'hidden');
+ assert_true(t.cues instanceof TextTrackCueList);
+ assert_equals(t.cues.length, 0);
+}, document.title + ' foo second arg, third arg omitted');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/crossOrigin.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/crossOrigin.html
new file mode 100644
index 0000000000..e29f2b0fbc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/crossOrigin.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<title>HTMLMediaElement.crossOrigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var video = document.createElement('video');
+ assert_true('crossOrigin' in video);
+});
+test(function(){
+ var video = document.createElement('video');
+ assert_equals(video.crossOrigin, null);
+}, document.title+', content attribute missing');
+test(function(){
+ var video = document.createElement('video');
+ video.setAttribute('crossorigin', 'foo');
+ assert_equals(video.crossOrigin, 'anonymous');
+}, document.title+', content attribute invalid value');
+test(function(){
+ var video = document.createElement('video');
+ video.setAttribute('crossorigin', '');
+ assert_equals(video.crossOrigin, 'anonymous');
+}, document.title+', content attribute empty string');
+test(function(){
+ var video = document.createElement('video');
+ video.setAttribute('crossorigin', 'ANONYMOUS');
+ assert_equals(video.crossOrigin, 'anonymous');
+}, document.title+', content attribute uppercase ANONYMOUS');
+test(function(){
+ var video = document.createElement('video');
+ video.setAttribute('crossorigin', 'use-credentials');
+ assert_equals(video.crossOrigin, 'use-credentials');
+}, document.title+', content attribute use-credentials');
+test(function(){
+ var video = document.createElement('video');
+ video.crossOrigin = '';
+ assert_equals(video.getAttribute('crossorigin'), '');
+}, document.title+', setting to empty string');
+test(function(){
+ var video = document.createElement('video');
+ video.crossOrigin = null;
+ assert_false(video.hasAttribute('crossorigin'));
+}, document.title+', setting to null');
+test(function(){
+ var video = document.createElement('video');
+ video.crossOrigin = 'foo';
+ assert_equals(video.getAttribute('crossorigin'), 'foo');
+}, document.title+', setting to invalid value');
+test(function(){
+ var video = document.createElement('video');
+ video.crossOrigin = 'ANONYMOUS';
+ assert_equals(video.getAttribute('crossorigin'), 'ANONYMOUS');
+}, document.title+', setting to uppercase ANONYMOUS');
+test(function(){
+ var video = document.createElement('video');
+ video.crossOrigin = 'use-credentials';
+ assert_equals(video.getAttribute('crossorigin'), 'use-credentials');
+}, document.title+', setting to use-credentials');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/textTracks.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/textTracks.html
new file mode 100644
index 0000000000..0f183b7e15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLMediaElement/textTracks.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>HTMLMediaElement.textTracks</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+var video = document.createElement('video');
+test(function(){
+ assert_equals(video.textTracks, video.textTracks);
+ assert_equals(video.textTracks.length, 0);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/default.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/default.html
new file mode 100644
index 0000000000..05fd0f7f7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/default.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<title>HTMLTrackElement.default</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track['default'], false);
+ assert_equals(track.getAttribute('default'), null);
+}, document.title + ' missing value');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('default', '');
+ assert_equals(track['default'], true);
+ assert_equals(track.getAttribute('default'), '');
+}, document.title + ' empty string content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track['default'] = '';
+ assert_equals(track['default'], false);
+ assert_equals(track.getAttribute('default'), null);
+}, document.title + ' empty string IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('default', 'foo');
+ assert_equals(track['default'], true);
+ assert_equals(track.getAttribute('default'), 'foo');
+}, document.title + ' foo in content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track['default'] = 'foo';
+ assert_equals(track['default'], true);
+ assert_equals(track.getAttribute('default'), '');
+}, document.title + ' foo in IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track['default'] = true;
+ assert_equals(track['default'], true);
+ assert_equals(track.getAttribute('default'), '');
+}, document.title + ' true in IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('default', '');
+ track['default'] = false;
+ assert_equals(track['default'], false);
+ assert_equals(track.getAttribute('default'), null);
+}, document.title + ' false in IDL attribute');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/kind.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/kind.html
new file mode 100644
index 0000000000..78c3bff51a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/kind.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<title>HTMLTrackElement.kind</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track.kind, 'subtitles');
+ assert_equals(track.getAttribute('kind'), null);
+}, document.title + ' missing value');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'invalid');
+ assert_equals(track.kind, 'metadata');
+ assert_equals(track.getAttribute('kind'), 'invalid');
+}, document.title + ' invalid value in content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'CAPTIONS');
+ assert_equals(track.kind, 'captions');
+ assert_equals(track.getAttribute('kind'), 'CAPTIONS');
+}, document.title + ' content attribute uppercase');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'CAPT\u0130ONS');
+ assert_equals(track.kind, 'metadata');
+ assert_equals(track.getAttribute('kind'), 'CAPT\u0130ONS');
+}, document.title + ' content attribute with uppercase turkish I (with dot)');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'capt\u0131ons');
+ assert_equals(track.kind, 'metadata');
+ assert_equals(track.getAttribute('kind'), 'capt\u0131ons');
+}, document.title + ' content attribute with lowercase turkish i (dotless)');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'subtitles');
+ assert_equals(track.kind, 'subtitles');
+ assert_equals(track.getAttribute('kind'), 'subtitles');
+}, document.title + ' content attribute "subtitles"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'captions');
+ assert_equals(track.kind, 'captions');
+ assert_equals(track.getAttribute('kind'), 'captions');
+}, document.title + ' content attribute "captions"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'descriptions');
+ assert_equals(track.kind, 'descriptions');
+ assert_equals(track.getAttribute('kind'), 'descriptions');
+}, document.title + ' content attribute "descriptions"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'chapters');
+ assert_equals(track.kind, 'chapters');
+ assert_equals(track.getAttribute('kind'), 'chapters');
+}, document.title + ' content attribute "chapters"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'metadata');
+ assert_equals(track.kind, 'metadata');
+ assert_equals(track.getAttribute('kind'), 'metadata');
+}, document.title + ' content attribute "metadata"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'captions\u0000');
+ assert_equals(track.kind, 'metadata');
+ assert_equals(track.getAttribute('kind'), 'captions\u0000');
+}, document.title + ' content attribute "captions\\u0000"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'subtitles';
+ assert_equals(track.getAttribute('kind'), 'subtitles');
+ assert_equals(track.kind, 'subtitles');
+}, document.title + ' setting IDL attribute to "subtitles"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'captions';
+ assert_equals(track.getAttribute('kind'), 'captions');
+ assert_equals(track.kind, 'captions');
+}, document.title + ' setting IDL attribute to "captions"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'descriptions';
+ assert_equals(track.getAttribute('kind'), 'descriptions');
+ assert_equals(track.kind, 'descriptions');
+}, document.title + ' setting IDL attribute to "descriptions"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'chapters';
+ assert_equals(track.getAttribute('kind'), 'chapters');
+ assert_equals(track.kind, 'chapters');
+}, document.title + ' setting IDL attribute to "chapters"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'metadata';
+ assert_equals(track.getAttribute('kind'), 'metadata');
+ assert_equals(track.kind, 'metadata');
+}, document.title + ' setting IDL attribute to "metadata"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'CAPTIONS';
+ assert_equals(track.getAttribute('kind'), 'CAPTIONS');
+ assert_equals(track.kind, 'captions');
+}, document.title + ' setting IDL attribute to "CAPTIONS"');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'CAPT\u0130ONS';
+ assert_equals(track.getAttribute('kind'), 'CAPT\u0130ONS');
+ assert_equals(track.kind, 'metadata');
+}, document.title + ' setting IDL attribute with uppercase turkish I (with dot)');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'capt\u0131ons';
+ assert_equals(track.getAttribute('kind'), 'capt\u0131ons');
+ assert_equals(track.kind, 'metadata');
+}, document.title + ' setting IDL attribute with lowercase turkish I (dotless)');
+
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'captions\u0000';
+ assert_equals(track.getAttribute('kind'), 'captions\u0000');
+ assert_equals(track.kind, 'metadata');
+}, document.title + ' setting IDL attribute with \\u0000');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/label.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/label.html
new file mode 100644
index 0000000000..b2360315cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/label.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<title>HTMLTrackElement.label</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track.label, '');
+ assert_equals(track.getAttribute('label'), null);
+}, document.title + ' missing value');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('label', '');
+ assert_equals(track.label, '');
+ assert_equals(track.getAttribute('label'), '');
+}, document.title + ' empty string content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.label = '';
+ assert_equals(track.label, '');
+ assert_equals(track.getAttribute('label'), '');
+}, document.title + ' empty string IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('label', 'foo');
+ assert_equals(track.label, 'foo');
+ assert_equals(track.getAttribute('label'), 'foo');
+}, document.title + ' lowercase content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('label', 'FOO');
+ assert_equals(track.label, 'FOO');
+ assert_equals(track.getAttribute('label'), 'FOO');
+}, document.title + ' uppercase content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('label', '\u0000');
+ assert_equals(track.label, '\u0000');
+ assert_equals(track.getAttribute('label'), '\u0000');
+}, document.title + '\\u0000 in content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.label = 'foo';
+ assert_equals(track.label, 'foo');
+ assert_equals(track.getAttribute('label'), 'foo');
+}, document.title + ' lowercase IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.label = 'FOO';
+ assert_equals(track.label, 'FOO');
+ assert_equals(track.getAttribute('label'), 'FOO');
+}, document.title + ' uppercase IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('label', ' foo \n');
+ assert_equals(track.label, ' foo \n');
+ assert_equals(track.getAttribute('label'), ' foo \n');
+}, document.title + ' whitespace in content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.label = ' foo \n';
+ assert_equals(track.label, ' foo \n');
+ assert_equals(track.getAttribute('label'), ' foo \n');
+}, document.title + ' whitespace in IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.label = '\u0000';
+ assert_equals(track.label, '\u0000');
+ assert_equals(track.getAttribute('label'), '\u0000');
+}, document.title + ' \\u0000 in IDL attribute');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/readyState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/readyState.html
new file mode 100644
index 0000000000..cde21e694e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/readyState.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>HTMLTrackElement.readyState</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track.readyState, 0);
+}, document.title + ' default value');
+
+test(function(){
+ assert_equals(HTMLTrackElement.NONE, 0);
+ assert_equals(HTMLTrackElement.LOADING, 1);
+ assert_equals(HTMLTrackElement.LOADED, 2);
+ assert_equals(HTMLTrackElement.ERROR, 3);
+}, document.title + ' values');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/src.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/src.html
new file mode 100644
index 0000000000..4089913cbd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/src.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<title>HTMLTrackElement.src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track.src, '');
+ assert_equals(track.getAttribute('src'), null);
+}, document.title + ' missing value');
+
+function resolve(url) {
+ var link = document.createElement('a');
+ link.setAttribute('href', url);
+ return link.href;
+}
+
+var tests = [
+ {input:'', expectedIDL:resolve(''), desc:'empty string'},
+ {input:'http://foo bar', expectedIDL:'http://foo bar', desc:'unresolvable value'},
+ {input:'test', expectedIDL:resolve('test'), desc:'resolvable value'},
+ // Leading and trailing C0 controls and space is stripped per url spec.
+ {input:'\u0000', expectedIDL:resolve(''), desc:'\\u0000'},
+ {input:'foo\u0000bar', expectedIDL:resolve('foo%00bar'), desc:'foo\\u0000bar'},
+];
+
+tests.forEach(function(t) {
+ test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('src', t.input);
+ assert_equals(track.src, t.expectedIDL);
+ assert_equals(track.getAttribute('src'), t.input);
+ }, [document.title, t.desc, 'in content attribute'].join(' '));
+
+ test(function(){
+ var track = document.createElement('track');
+ track.src = t.input;
+ assert_equals(track.src, t.expectedIDL);
+ assert_equals(track.getAttribute('src'), t.input);
+ }, [document.title, 'assigning', t.desc, 'to IDL attribute'].join(' '));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/srclang.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/srclang.html
new file mode 100644
index 0000000000..b5071e0c36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/srclang.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<title>HTMLTrackElement.srclang</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track.srclang, '');
+ assert_equals(track.getAttribute('srclang'), null);
+}, document.title + ' missing value');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('srclang', '');
+ assert_equals(track.srclang, '');
+ assert_equals(track.getAttribute('srclang'), '');
+}, document.title + ' empty string content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.srclang = '';
+ assert_equals(track.srclang, '');
+ assert_equals(track.getAttribute('srclang'), '');
+}, document.title + ' empty string IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('srclang', 'foo');
+ assert_equals(track.srclang, 'foo');
+ assert_equals(track.getAttribute('srclang'), 'foo');
+}, document.title + ' lowercase content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('srclang', 'FOO');
+ assert_equals(track.srclang, 'FOO');
+ assert_equals(track.getAttribute('srclang'), 'FOO');
+}, document.title + ' uppercase content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('srclang', '\u0000');
+ assert_equals(track.srclang, '\u0000');
+ assert_equals(track.getAttribute('srclang'), '\u0000');
+}, document.title + ' \\u0000 content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.srclang = 'foo';
+ assert_equals(track.srclang, 'foo');
+ assert_equals(track.getAttribute('srclang'), 'foo');
+}, document.title + ' lowercase IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.srclang = 'FOO';
+ assert_equals(track.srclang, 'FOO');
+ assert_equals(track.getAttribute('srclang'), 'FOO');
+}, document.title + ' uppercase IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('srclang', ' foo \n');
+ assert_equals(track.srclang, ' foo \n');
+ assert_equals(track.getAttribute('srclang'), ' foo \n');
+}, document.title + ' whitespace in content attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.srclang = ' foo \n';
+ assert_equals(track.srclang, ' foo \n');
+ assert_equals(track.getAttribute('srclang'), ' foo \n');
+}, document.title + ' whitespace in IDL attribute');
+
+test(function(){
+ var track = document.createElement('track');
+ track.srclang = '\u0000';
+ assert_equals(track.srclang, '\u0000');
+ assert_equals(track.getAttribute('srclang'), '\u0000');
+}, document.title + ' \\u0000 in IDL attribute');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/track.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/track.html
new file mode 100644
index 0000000000..1de0a88046
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/HTMLElement/HTMLTrackElement/track.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>HTMLTrackElement.track</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track.track, track.track, 'same object should be returned');
+ assert_true(track.track instanceof TextTrack, 'returned object should be a TextTrack');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/activeCues.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/activeCues.html
new file mode 100644
index 0000000000..7a57826f30
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/activeCues.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<title>TextTrack.activeCues</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/media.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ window.track = document.createElement('track');
+ track['default'] = true;
+ video.appendChild(track);
+ window.t2 = track.track;
+ t2.mode = 'showing';
+ window.t1_cues = t1.activeCues;
+ window.t2_cues = t2.activeCues;
+ document.body.appendChild(video);
+ if (!t1)
+ throw new Error('t1 was undefined')
+});
+function smoke_test() {
+ assert_true('HTMLTrackElement' in window, 'track not supported');
+}
+
+test(function(){
+ smoke_test();
+ assert_equals(t1.activeCues, t1_cues, 't1.activeCues should return same object');
+ assert_equals(t2.activeCues, t2_cues, 't2.activeCues should return same object');
+ assert_not_equals(t1.activeCues, t2.activeCues, 't1.activeCues and t2.activeCues should be different objects');
+ assert_not_equals(t1.activeCues, null, 't1.activeCues should not be null');
+ assert_not_equals(t2.activeCues, null, 't2.activeCues should not be null');
+ assert_equals(t1.activeCues.length, 0, 't1.activeCues should have length 0');
+ assert_equals(t2.activeCues.length, 0, 't2.activeCues should have length 0');
+}, document.title+', empty list');
+test(function(){
+ smoke_test();
+ var c = new VTTCue(0, 1, "text");
+ t1.addCue(c);
+ assert_equals(t1.activeCues, t1_cues, "t1.activeCues should return same object");
+ assert_equals(t1.activeCues.length, 0, "t1.activeCues.length");
+ var c2 = new VTTCue(1, 2, "text2");
+ t1.addCue(c2);
+ assert_equals(t1.activeCues, t1_cues, "t1.activeCues should return the same object after adding a second cue");
+ assert_equals(t1.activeCues.length, 0, "t1.activeCues.length after adding a second cue");
+}, document.title+', after addCue()');
+test(function(){
+ smoke_test();
+ t1.mode = 'showing';
+ assert_equals(t1.activeCues, t1_cues, "t1.activeCues should return the same object after setting mode to showing");
+ t1.mode = 'hidden';
+ assert_equals(t1.activeCues, t1_cues, "t1.activeCues should return the same object after setting mode to hidden");
+ t1.mode = 'disabled';
+ assert_equals(t1.activeCues, null, "t1.activeCues should be null when mode is disabled");
+ assert_equals(t1_cues.length, 0, "t1_cues should still be intact after setting mode to disabled");
+}, document.title+', different modes');
+
+// ok now let's load in a video
+var test1 = async_test(document.title+', video loading');
+var test2 = async_test(document.title+', video playing');
+var test3 = async_test(document.title+', adding cue during playback');
+test1.step(smoke_test);
+test2.step(smoke_test);
+test3.step(smoke_test);
+test1.step(function(){
+ t1.mode = 'showing';
+ video.onloadeddata = test1.step_func(function(e) {
+ video.onplaying = test2.step_func(function(e) {
+ try {
+ assert_equals(t1.activeCues, t1_cues, "t1.activeCues should return the same object after playing a video");
+ assert_equals(t1.activeCues.length, 1, "t1.activeCues.length after the video has started playing");
+ } catch(ex) {
+ test2.step(function() { throw ex; });
+ test3.step(function() { assert_unreached(); });
+ return;
+ }
+ test3.step(function(){
+ var c3 = new VTTCue(0, 2, "text3");
+ t1.addCue(c3);
+ assert_equals(t1.activeCues.length, 2, "t1.activeCues.length should be changed immediately");
+ test3.done();
+ });
+ test2.done();
+ });
+ try {
+ assert_equals(t1.activeCues, t1_cues, "t1.activeCues should return the same object after loading a video");
+ assert_equals(t2.activeCues, t2_cues, "t2.activeCues should return the same object after loading a video");
+ assert_equals(t1.activeCues.length, 0, "t1.activeCues.length before the video has started playing");
+ assert_equals(t2.activeCues.length, 0, "t1.activeCues.length before the video has started playing");
+ } catch(ex) {
+ test1.step(function() { throw ex; });
+ test2.step(function() { assert_unreached(); });
+ test3.step(function() { assert_unreached(); });
+ return;
+ }
+ video.play();
+ test1.done();
+ });
+ video.src = getVideoURI("/media/movie_5");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html
new file mode 100644
index 0000000000..622ec4abfd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/addCue.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<title>TextTrack.addCue()</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ document.body.appendChild(video);
+});
+test(function() {
+ var t1 = video.addTextTrack('subtitles');
+ var t2 = video.addTextTrack('subtitles');
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ t2.addCue(c1);
+ assert_equals(c1.track, t2);
+}, document.title+', adding a cue to two different tracks');
+test(function() {
+ var t1 = video.addTextTrack('subtitles');
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ assert_equals(c1.track, t1);
+ t1.addCue(c1);
+ assert_equals(c1.track, t1);
+}, document.title+', adding a cue to a track twice');
+test(function() {
+ var t1 = video.addTextTrack('subtitles');
+ var t2 = video.addTextTrack('subtitles');
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ assert_equals(c1.track, t1);
+ t1.removeCue(c1);
+ assert_equals(c1.track, null);
+ t2.addCue(c1);
+ assert_equals(c1.track, t2);
+}, document.title+', adding a removed cue to a different track');
+test(function() {
+ var t1 = video.addTextTrack('subtitles');
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ assert_equals(t1.cues.length, 1, 't1.cues.length after first addition');
+ t1.removeCue(c1);
+ assert_equals(t1.cues.length, 0, 't1.cues.length after removal');
+ t1.addCue(c1);
+ assert_equals(t1.cues.length, 1, 't1.cues.length after second addition');
+}, document.title+', adding an associated but removed cue to the same track');
+
+var t = async_test(document.title+', adding a cue associated with a track element to other track');
+t.step(function(){
+ var t1 = video.addTextTrack('subtitles');
+ var track = document.createElement('track');
+ track.onload = t.step_func(function(){
+ var cue = track.track.cues[0];
+ track.track.removeCue(cue);
+ t1.addCue(cue);
+ assert_equals(cue.track, t1);
+ t.done();
+ });
+ track.onerror = t.step_func(function() {
+ assert_unreached('got error event');
+ });
+ track.src= 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:01.000\ntest\n');
+ track.kind = 'subtitles';
+ track.track.mode = 'hidden';
+ video.appendChild(track);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/constants.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/constants.html
new file mode 100644
index 0000000000..3c8046cdc4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/constants.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>TextTrack constants</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+});
+test(function(){
+ assert_equals(t1.DISABLED, undefined, "t1.DISABLED");
+ assert_equals(t1.HIDDEN, undefined, "t1.HIDDEN");
+ assert_equals(t1.SHOWING, undefined, "t1.SHOWING");
+ assert_equals(TextTrack.prototype.DISABLED, undefined, "TextTrack.prototype.DISABLED");
+ assert_equals(TextTrack.prototype.HIDDEN, undefined, "TextTrack.prototype.HIDDEN");
+ assert_equals(TextTrack.prototype.SHOWING, undefined, "TextTrack.prototype.SHOWING");
+ assert_equals(TextTrack.DISABLED, undefined, "TextTrack.DISABLED");
+ assert_equals(TextTrack.HIDDEN, undefined, "TextTrack.HIDDEN");
+ assert_equals(TextTrack.SHOWING, undefined, "TextTrack.SHOWING");
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/cues.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/cues.html
new file mode 100644
index 0000000000..4b7808c963
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/cues.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<title>TextTrack.cues</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var video = document.createElement('video');
+ var t1 = video.addTextTrack('subtitles');
+ assert_equals(t1.cues, t1.cues, 't1.cues should return same object');
+ assert_not_equals(t1.cues, null, 't1.cues should not be null');
+ assert_true(t1.cues instanceof TextTrackCueList, 't1.cues instanceof TextTrackCueList');
+ assert_equals(t1.cues.length, 0, 't1.cues.length');
+}, document.title+', empty list');
+
+function addCue(texttrack, start, end, text, id) {
+ var c = new VTTCue(start, end, text);
+ c.id = id;
+ texttrack.addCue(c);
+ return c;
+}
+
+test(function(){
+ var video = document.createElement('video');
+ var t1 = video.addTextTrack('subtitles');
+ var t1_cues = t1.cues;
+ var c = addCue(t1, 0, 1, 'text', 'id');
+ assert_equals(t1.cues, t1_cues, "t1.cues should return same object");
+ assert_equals(t1.cues.length, 1, "t1.cues.length");
+ var c2 = addCue(t1, 1, 2, 'text2', 'id2');
+ assert_equals(t1.cues, t1_cues, "t1.cues should return the same object after adding a second cue");
+ assert_equals(t1.cues.length, 2, "t1.cues.length after adding a second cue");
+ assert_equals(t1.cues[0].id, "id");
+ assert_equals(t1.cues[1].id, "id2");
+}, document.title+', after addCue()');
+
+test(function(){
+ var video = document.createElement('video');
+ var t1 = video.addTextTrack('subtitles');
+ var t1_cues = t1.cues;
+ var c = addCue(t1, 0, 1, 'text', 'id');
+ var c2 = addCue(t1, 1, 2, 'text2', 'id2');
+ t1.mode = 'showing';
+ assert_equals(t1.cues, t1_cues, "t1.cues should return the same object after setting mode to 'showing'");
+ t1.mode = 'hidden';
+ assert_equals(t1.cues, t1_cues, "t1.cues should return the same object after setting mode to 'hidden'");
+ t1.mode = 'disabled';
+ assert_equals(t1.cues, null, "t1.cues should be null when mode is 'disabled'");
+ assert_equals(t1_cues.length, 2, "t1_cues should still be intact after setting mode to 'disabled'");
+ assert_equals(t1_cues[0].id, "id", "t1_cues first cue should still be intact after setting mode to 'disabled'");
+ assert_equals(t1_cues[1].id, "id2", "t1_cues second cue should still be intact after setting mode to 'disabled'");
+ t1.mode = 'hidden';
+ assert_equals(t1.cues, t1_cues, "t1.cues should return the same object after setting mode to 'disabled' and then 'hidden'");
+ t1.mode = 'disabled';
+ assert_equals(t1.cues, null, "t1.cues should be null when mode is set to 'disabled' again");
+ assert_equals(t1_cues.length, 2, "t1_cues should still be intact after setting mode to 'disabled' again");
+ assert_equals(t1_cues[0].id, "id", "t1_cues first cue should still be intact after setting mode to 'disabled' again");
+ assert_equals(t1_cues[1].id, "id2", "t1_cues second cue should still be intact after setting mode to 'disabled' again");
+ t1.mode = 'showing';
+ assert_equals(t1.cues, t1_cues, "t1.cues should return the same object after setting mode to 'disabled' and then 'showing'");
+}, document.title+', different modes');
+
+test(function(){
+ var video = document.createElement('video');
+ var t1 = video.addTextTrack('subtitles');
+ var t1_cues = t1.cues;
+ var c = addCue(t1, 0, 1, 'text', 'id');
+ var c2 = addCue(t1, 1, 2, 'text2', 'id2');
+ t1.mode = 'showing';
+ t1.cues[1].startTime = 0; // this should change the text track cue order
+ assert_equals(t1.cues[0].id, 'id2');
+ assert_equals(t1.cues[1].id, 'id');
+ t1.cues[0].startTime = 0.5; // this should change it back
+ assert_equals(t1.cues[0].id, 'id');
+ assert_equals(t1.cues[1].id, 'id2');
+}, document.title+', changing order');
+
+async_test(function(){
+ var video = document.createElement('video');
+ var t1 = video.addTextTrack('subtitles');
+ var t1_cues = t1.cues;
+ t1.mode = 'hidden';
+ var track = document.createElement('track');
+ track['default'] = true;
+ video.appendChild(track); // queues a task to "honor user preferences...", media element event task source
+ var t2 = track.track;
+ assert_equals(t2.cues, null, 't2.cues should be null');
+ // We need to wait until the "honor user preferences..." steps have run so we invoke play()
+ // which queues an event with the same task source.
+ video.onplay = this.step_func(function(){
+ assert_equals(t2.cues, t2.cues, 't2.cues should return same object');
+ assert_not_equals(t1.cues, t2.cues, 't1.cues and t2.cues should be different objects');
+ assert_not_equals(t2.cues, null, 't2.cues should not be null');
+ assert_true(t2.cues instanceof TextTrackCueList, 't2.cues instanceof TextTrackCueList');
+ assert_equals(t2.cues.length, 0, 't2.cues should have length 0');
+ this.done();
+ });
+ video.play(); // queues a task to fire 'play', media element event task source
+}, document.title+', default attribute');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/kind.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/kind.html
new file mode 100644
index 0000000000..d5dbc8342c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/kind.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>TextTrack.kind</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var video = document.createElement('video');
+ var t1 = video.addTextTrack('subtitles');
+ var t2 = video.addTextTrack('captions');
+ var t3 = video.addTextTrack('descriptions');
+ var t4 = video.addTextTrack('chapters');
+ var t5 = video.addTextTrack('metadata');
+ assert_equals(t1.kind, 'subtitles');
+ assert_equals(t2.kind, 'captions');
+ assert_equals(t3.kind, 'descriptions');
+ assert_equals(t4.kind, 'chapters');
+ assert_equals(t5.kind, 'metadata');
+}, document.title+', addTextTrack');
+test(function(){
+ var track = document.createElement('track');
+ track.setAttribute('kind', 'CAPTIONS');
+ var t = track.track;
+ assert_equals(t.kind, 'captions');
+}, document.title+', track element');
+test(function(){
+ var track = document.createElement('track');
+ track.kind = 'captions\u0000';
+ assert_equals(track.track.kind, 'metadata');
+}, document.title+', \\u0000');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/label.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/label.html
new file mode 100644
index 0000000000..c60e85c21a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/label.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>TextTrack.label</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles', 'foo');
+ window.track = document.createElement('track');
+ track.setAttribute('label', 'bar');
+ video.appendChild(track);
+ window.t2 = track.track;
+});
+test(function(){
+ assert_equals(t1.label, 'foo');
+ assert_equals(t2.label, 'bar');
+ track.label = 'baz';
+ assert_equals(t2.label, 'baz');
+ track.removeAttribute('label');
+ assert_equals(t2.label, '');
+});
+test(function(){
+ track.label = '\u0000a';
+ assert_equals(t2.label, '\u0000a');
+ track.setAttribute('label', '\u0000b', 'IDL attribute');
+ assert_equals(t2.label, '\u0000b', 'content attribute');
+}, document.title+', \\u0000');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/language.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/language.html
new file mode 100644
index 0000000000..eda3653de0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/language.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>TextTrack.language</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles', 'foo', 'foo');
+ window.track = document.createElement('track');
+ track.setAttribute('srclang', 'bar');
+ video.appendChild(track);
+ window.t2 = track.track;
+});
+test(function(){
+ assert_equals(t1.language, 'foo');
+ assert_equals(t2.language, 'bar');
+ track.srclang = 'baz';
+ assert_equals(t2.language, 'baz');
+ track.removeAttribute('srclang');
+ assert_equals(t2.language, '');
+});
+test(function(){
+ track.srclang = '\u0000a';
+ assert_equals(t2.language, '\u0000a', 'IDL attribute');
+ track.setAttribute('srclang', '\u0000b');
+ assert_equals(t2.language, '\u0000b', 'content attribute');
+}, document.title+', \\u0000');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/mode.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/mode.html
new file mode 100644
index 0000000000..9f94156706
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/mode.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<title>TextTrack.mode</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var track = document.createElement('track');
+ assert_equals(track.track.mode, 'disabled', 'initial');
+ track.track.mode = 1;
+ assert_equals(track.track.mode, 'disabled', '1');
+ track.track.mode = '';
+ assert_equals(track.track.mode, 'disabled', '""');
+ track.track.mode = null;
+ assert_equals(track.track.mode, 'disabled', 'null');
+ track.track.mode = undefined;
+ assert_equals(track.track.mode, 'disabled', 'undefined');
+ track.track.mode = 'showing';
+ assert_equals(track.track.mode, 'showing', 'showing (correct value)');
+ track.track.mode = 'DISABLED';
+ assert_equals(track.track.mode, 'showing', '"DISABLED"');
+ track.track.mode = 'd\u0130sabled'; // dotted uppercase i
+ assert_equals(track.track.mode, 'showing', '"d\u0130sabled" (dotted uppercase i)');
+ track.track.mode = 'd\u0131sabled'; // dotless lowercase i
+ assert_equals(track.track.mode, 'showing', '"d\u0131sabled" (dotless lowercase i)');
+ track.track.mode = 'disabled ';
+ assert_equals(track.track.mode, 'showing', '"disabled "');
+ track.track.mode = ' disabled';
+ assert_equals(track.track.mode, 'showing', '" disabled"');
+ track.track.mode = {};
+ assert_equals(track.track.mode, 'showing', '{}');
+ track.track.mode = 'HIDDEN';
+ assert_equals(track.track.mode, 'showing', '"HIDDEN"');
+ track.track.mode = 'h\u0130dden'; // dotted uppercase i
+ assert_equals(track.track.mode, 'showing', '"h\u0130dden" (dotted uppercase i)');
+ track.track.mode = 'h\u0131dden'; // dotless lowercase i
+ assert_equals(track.track.mode, 'showing', '"h\u0131dden" (dotless lowercase i)');
+}, document.title+', wrong value');
+test(function() {
+ var track = document.createElement('track');
+ assert_equals(track.track.mode, 'disabled', 'initial');
+ track.track.mode = 'disabled'; // no-op
+ assert_equals(track.track.mode, 'disabled', 'disabled (1)');
+ track.track.mode = 'hidden';
+ assert_equals(track.track.mode, 'hidden', 'hidden (1)');
+ track.track.mode = 'hidden'; // no-op
+ assert_equals(track.track.mode, 'hidden', 'hidden (2)');
+ track.track.mode = 'showing';
+ assert_equals(track.track.mode, 'showing', 'showing (1)');
+ track.track.mode = 'showing'; // no-op
+ assert_equals(track.track.mode, 'showing', 'showing (2)');
+ track.track.mode = {toString:function() { return 'disabled'; }};
+ assert_equals(track.track.mode, 'disabled', '{toString:...}');
+}, document.title+', correct value');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/oncuechange.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/oncuechange.html
new file mode 100644
index 0000000000..16c76f9484
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/oncuechange.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>TextTrack.oncuechange</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ window.ev = new Event('cuechange');
+ window.ran = false;
+ window.cb = function() { ran = true; };
+});
+test(function(){
+ assert_equals(t1.oncuechange, null);
+ t1.oncuechange = cb;
+ t1.dispatchEvent(ev);
+ assert_true(ran);
+ t1.oncuechange = null;
+ ran = false;
+ t1.dispatchEvent(ev);
+ assert_false(ran);
+});
+test(function(){
+ t1.addEventListener('cuechange', cb, false);
+ t1.dispatchEvent(ev);
+ assert_true(ran);
+ t1.removeEventListener('cuechange', cb, false);
+ ran = false;
+ t1.dispatchEvent(ev);
+ assert_false(ran);
+}, 'TextTrack.addEventListener/removeEventListener');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/removeCue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/removeCue.html
new file mode 100644
index 0000000000..09043458cc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrack/removeCue.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<title>TextTrack.removeCue()</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ document.body.appendChild(video);
+});
+test(function() {
+ var t1 = video.addTextTrack('subtitles');
+ var t2 = video.addTextTrack('subtitles');
+ var c1 = new VTTCue(0, 1, 'text1');
+ assert_throws_dom("NOT_FOUND_ERR", function() {
+ t1.removeCue(c1);
+ }, 'standalone');
+ t1.addCue(c1);
+ assert_throws_dom("NOT_FOUND_ERR", function() {
+ t2.removeCue(c1);
+ }, 'listed in t1, remove from t2');
+ t1.removeCue(c1);
+ assert_throws_dom("NOT_FOUND_ERR", function() {
+ t1.removeCue(c1);
+ }, 'standalone, remove from t1');
+ assert_throws_dom("NOT_FOUND_ERR", function() {
+ t2.removeCue(c1);
+ }, 'standalone, remove from t2');
+}, document.title+', two elementless tracks');
+var t = async_test(document.title+', cue from track element');
+t.step(function(){
+ var t1 = video.addTextTrack('subtitles');
+ var track = document.createElement('track');
+ track.onload = t.step_func(function(){
+ var cue = track.track.cues[0];
+ assert_throws_dom('NOT_FOUND_ERR', function() { t1.removeCue(cue); }, 'listed in track.track, remove from t1');
+ track.track.removeCue(cue);
+ assert_throws_dom('NOT_FOUND_ERR', function() { track.track.removeCue(cue); }, 'standalone, remove from track.track');
+ assert_throws_dom('NOT_FOUND_ERR', function() { t1.removeCue(cue); }, 'standalone, remove from t1');
+ t.done();
+ });
+ track.onerror = t.step_func(function() {
+ assert_unreached('got error event');
+ });
+ track.src= 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:01.000\ntest\n');
+ track.kind = 'subtitles';
+ track.track.mode = 'hidden';
+ video.appendChild(track);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html
new file mode 100644
index 0000000000..8ee9adb1c0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/constructor.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>TextTrackCue constructor</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ test(function()
+ {
+ assert_not_equals(TextTrackCue, VTTCue);
+ }, "TextTrackCue and VTTCue are separate interfaces");
+ test(function()
+ {
+ assert_throws_js(TypeError, function()
+ {
+ new TextTrackCue(0, 0, "");
+ });
+ }, "TextTrackCue constructor should not be supported");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/endTime.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/endTime.html
new file mode 100644
index 0000000000..18b14bdfa9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/endTime.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>TextTrackCue.endTime</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+});
+test(function(){
+ var c1 = new VTTCue(-2, -1, 'text1');
+ assert_equals(c1.endTime, -1);
+ c1.endTime = c1.endTime;
+ assert_equals(c1.endTime, -1);
+ assert_throws_js(TypeError, function(){ c1.endTime = NaN; });
+ c1.endTime = +Infinity;
+ assert_equals(c1.endTime, +Infinity);
+ assert_throws_js(TypeError, function(){ c1.endTime = -Infinity; });
+}, document.title+', script-created cue');
+
+var t_parsed = async_test(document.title+', parsed cue');
+t_parsed.step(function(){
+ var t = document.createElement('track');
+ t.onload = this.step_func(function(){
+ var c = t.track.cues;
+ assert_equals(c[0].endTime, 0.001);
+ assert_equals(c[1].endTime, 3600.001);
+ this.done();
+ });
+ t.onerror = this.step_func(function() {
+ assert_unreached('got error event');
+ });
+ t.src = 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:00.001\ntest'+
+ '\n\nfoobar\n01:00:00.000 --> 01:00:00.001\ntest');
+ t.track.mode = 'showing';
+ video.appendChild(t);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/id.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/id.html
new file mode 100644
index 0000000000..a88f94766f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/id.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>TextTrackCue.id</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+});
+test(function(){
+ var c1 = new VTTCue(0, 1, 'text1');
+ c1.id = 'id1\r\n\u0000';
+ assert_equals(c1.id, 'id1\r\n\u0000');
+ c1.id = c1.id;
+ assert_equals(c1.id, 'id1\r\n\u0000');
+ c1.id = null;
+ assert_equals(c1.id, 'null');
+}, document.title+', script-created cue');
+
+var t_parsed = async_test(document.title+', parsed cue');
+t_parsed.step(function(){
+ var t = document.createElement('track');
+ t.onload = this.step_func(function(){
+ var c = t.track.cues;
+ assert_equals(c[0].id, '');
+ assert_equals(c[1].id, 'foobar');
+ this.done();
+ });
+ t.onerror = this.step_func(function() {
+ assert_unreached('got error event');
+ });
+ t.src = 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:00.001\ntest'+
+ '\n\nfoobar\n00:00:00.000 --> 00:00:00.001\ntest');
+ t.track.mode = 'showing';
+ video.appendChild(t);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onenter.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onenter.html
new file mode 100644
index 0000000000..17deed0530
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onenter.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>TextTrackCue.onenter</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.c1 = new VTTCue(0, 1, 'text1');
+ window.ev = new Event('enter');
+ window.ran = false;
+ window.cb = function() { ran = true; };
+});
+test(function(){
+ assert_equals(c1.onenter, null, 'initial value');
+ c1.onenter = undefined;
+ assert_equals(c1.onenter, null, 'assigning undefined');
+ c1.onenter = cb;
+ assert_equals(c1.onenter, cb, 'assigning onenter');
+ c1.dispatchEvent(ev);
+ assert_true(ran, 'dispatching event');
+ c1.onenter = null;
+ assert_equals(c1.onenter, null, 'assigning null');
+ ran = false;
+ c1.dispatchEvent(ev);
+ assert_false(ran, 'dispatching event after nulling onenter');
+});
+test(function(){
+ c1.addEventListener('enter', cb, false);
+ c1.dispatchEvent(ev);
+ assert_true(ran);
+ c1.removeEventListener('enter', cb, false);
+ ran = false;
+ c1.dispatchEvent(ev);
+ assert_false(ran);
+}, 'TextTrackCue.addEventListener/removeEventListener');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onexit.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onexit.html
new file mode 100644
index 0000000000..815377e4d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/onexit.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>TextTrackCue.onexit</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.c1 = new VTTCue(0, 1, 'text1');
+ window.ev = new Event('exit');
+ window.ran = false;
+ window.cb = function() { ran = true; };
+});
+test(function(){
+ assert_equals(c1.onexit, null, 'initial value');
+ c1.onexit = undefined;
+ assert_equals(c1.onexit, null, 'assigning undefined');
+ c1.onexit = cb;
+ assert_equals(c1.onexit, cb, 'assigning onexit');
+ c1.dispatchEvent(ev);
+ assert_true(ran, 'dispatching event');
+ c1.onexit = null;
+ assert_equals(c1.onexit, null, 'assigning null');
+ ran = false;
+ c1.dispatchEvent(ev);
+ assert_false(ran, 'dispatching event after nulling onexit');
+});
+test(function(){
+ c1.addEventListener('exit', cb, false);
+ c1.dispatchEvent(ev);
+ assert_true(ran);
+ c1.removeEventListener('exit', cb, false);
+ ran = false;
+ c1.dispatchEvent(ev);
+ assert_false(ran);
+}, 'TextTrackCue.addEventListener/removeEventListener');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/pauseOnExit.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/pauseOnExit.html
new file mode 100644
index 0000000000..31ea4c63b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/pauseOnExit.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>TextTrackCue.pauseOnExit</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+});
+test(function(){
+ var c1 = new VTTCue(0, 1, 'text1');
+ assert_equals(c1.pauseOnExit, false);
+ c1.pauseOnExit = null;
+ assert_equals(c1.pauseOnExit, false);
+ c1.pauseOnExit = 'foo';
+ assert_equals(c1.pauseOnExit, true);
+}, document.title+', script-created cue');
+
+var t_parsed = async_test(document.title+', parsed cue');
+t_parsed.step(function(){
+ var t = document.createElement('track');
+ t.onload = this.step_func(function(){
+ var c1 = t.track.cues[0];
+ assert_equals(c1.pauseOnExit, false);
+ c1.pauseOnExit = null;
+ assert_equals(c1.pauseOnExit, false);
+ c1.pauseOnExit = 'foo';
+ assert_equals(c1.pauseOnExit, true);
+ this.done();
+ });
+ t.onerror = this.step_func(function() {
+ assert_unreached('got error event');
+ });
+ t.src = 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:00.001\ntest');
+ t.track.mode = 'showing';
+ video.appendChild(t);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/startTime.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/startTime.html
new file mode 100644
index 0000000000..7fba1df415
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/startTime.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>TextTrackCue.startTime</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+});
+test(function(){
+ var c1 = new VTTCue(-1, 1, 'text1');
+ assert_equals(c1.startTime, -1);
+ c1.startTime = c1.startTime;
+ assert_equals(c1.startTime, -1);
+ assert_throws_js(TypeError, function(){ c1.startTime = NaN; });
+ assert_throws_js(TypeError, function(){ c1.startTime = +Infinity; });
+ assert_throws_js(TypeError, function(){ c1.startTime = -Infinity; });
+}, document.title+', script-created cue');
+
+var t_parsed = async_test(document.title+', parsed cue');
+t_parsed.step(function(){
+ var t = document.createElement('track');
+ t.onload = this.step_func(function(){
+ var c = t.track.cues;
+ assert_equals(c[0].startTime, 0);
+ assert_equals(c[1].startTime, 3600);
+ this.done();
+ });
+ t.onerror = this.step_func(function() {
+ assert_unreached('got error event');
+ });
+ t.src = 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:00.001\ntest'+
+ '\n\nfoobar\n01:00:00.000 --> 01:00:00.001\ntest');
+ t.track.mode = 'showing';
+ video.appendChild(t);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/track.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/track.html
new file mode 100644
index 0000000000..219e3e703b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/track.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>TextTrackCue.track</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+});
+test(function(){
+ var c1 = new VTTCue(0, 1, 'text1');
+ assert_equals(c1.track, null);
+ t1.addCue(c1);
+ assert_equals(c1.track, t1);
+ t1.removeCue(c1);
+ assert_equals(c1.track, null);
+}, document.title+', script-created cue');
+
+var t_parsed = async_test(document.title+', parsed cue');
+t_parsed.step(function(){
+ var t = document.createElement('track');
+ t.onload = this.step_func(function(){
+ var c = t.track.cues[0];
+ assert_equals(c.track, t.track);
+ t.track.removeCue(c);
+ assert_equals(c.track, null);
+ this.done();
+ });
+ t.onerror = this.step_func(function() {
+ assert_unreached('got error event');
+ });
+ t.src = 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:00.001\ntest');
+ t.track.mode = 'showing';
+ video.appendChild(t);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getCueById.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getCueById.html
new file mode 100644
index 0000000000..8184189b05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getCueById.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<title>TextTrackCueList.getCueById</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var video = document.createElement('video');
+ var t = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+ var cues = t.cues;
+ var c = new VTTCue(0, 1, 'text1');
+ t.addCue(c);
+ assert_equals(cues.getCueById(""), null, '""');
+ assert_equals(cues.getCueById(null), null, 'null');
+ assert_equals(cues.getCueById(undefined), null, 'undefined');
+}, document.title+ ', no id');
+test(function(){
+ var video = document.createElement('video');
+ var t = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+ var cues = t.cues;
+ var c = new VTTCue(0, 1, 'text1');
+ c.id = 'foo';
+ t.addCue(c);
+ assert_equals(cues.getCueById(""), null, '""');
+ assert_equals(cues.getCueById("foo"), c, '"foo"');
+ assert_equals(cues.getCueById({toString:function(){return "foo"}}), c, 'object');
+}, document.title+ ', id foo');
+test(function(){
+ var video = document.createElement('video');
+ var t = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+ var cues = t.cues;
+ var c = new VTTCue(0, 1, 'text1');
+ c.id = '1';
+ t.addCue(c);
+ assert_equals(cues.getCueById(""), null, '""');
+ assert_equals(cues.getCueById("1"), c, '"1"');
+ assert_equals(cues.getCueById(1), c, '1');
+}, document.title+ ', no 1');
+test(function(){
+ var video = document.createElement('video');
+ var t = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+ var cues = t.cues;
+ var c = new VTTCue(0, 1, 'text1');
+ c.id = 'a\u0000b';
+ t.addCue(c);
+ assert_equals(cues.getCueById("a\u0000b"), c, '"a\\u0000b"');
+ assert_equals(cues.getCueById("a"), null, '"a"');
+}, document.title+ ', id a\\u0000b');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getter.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getter.html
new file mode 100644
index 0000000000..8056d24543
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/getter.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>TextTrackCueList getter</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+});
+test(function(){
+ var cues = t1.cues;
+ assert_equals(cues[0], undefined, 'cues[0] before');
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ assert_equals(cues[0], c1, 'cues[0]');
+ assert_equals(cues[1], undefined, 'cues[1]');
+ assert_equals(cues[-1], undefined, 'cues[-1]');
+ t1.removeCue(c1);
+ assert_equals(cues[0], undefined, 'cues[0] after');
+});
+test(function(){
+ var cues = t1.cues;
+ assert_equals(cues[0], undefined);
+ cues[0] = 'foo';
+ assert_equals(cues[0], undefined);
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ assert_equals(cues[0], c1);
+ cues[0] = 'foo';
+ assert_equals(cues[0], c1);
+ t1.removeCue(c1);
+}, document.title+', no indexed set/create');
+test(function(){
+ 'use strict';
+ var cues = t1.cues;
+ assert_equals(cues[0], undefined);
+ assert_throws_js(TypeError, function() { cues[0] = 'foo'; });
+ assert_equals(cues[0], undefined);
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ assert_equals(cues[0], c1);
+ assert_throws_js(TypeError, function() { cues[0] = 'foo'; });
+ assert_equals(cues[0], c1);
+ t1.removeCue(c1);
+}, document.title+', no indexed set/create (strict)');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/length.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/length.html
new file mode 100644
index 0000000000..91e6e7ff99
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackCueList/length.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>TextTrackCueList.length</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ window.t1 = video.addTextTrack('subtitles');
+ document.body.appendChild(video);
+});
+test(function(){
+ var cues = t1.cues;
+ assert_equals(cues.length, 0);
+ var c1 = new VTTCue(0, 1, 'text1');
+ t1.addCue(c1);
+ assert_equals(cues.length, 1);
+ t1.removeCue(c1);
+ assert_equals(cues.length, 0);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getTrackById.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getTrackById.html
new file mode 100644
index 0000000000..b701dd5e73
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getTrackById.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>TextTrackList.getTrackById</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var video = document.createElement('video');
+ var track1 = video.addTextTrack('subtitles');
+ var track2 = video.addTextTrack('subtitles');
+ assert_equals(track1.id, '');
+ assert_equals(track2.id, '');
+ assert_equals(video.textTracks.getTrackById(''), track1);
+ assert_equals(video.textTracks.getTrackById('fake-id'), null);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter.html
new file mode 100644
index 0000000000..9baa459419
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>TextTrackList getter</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ video.addTextTrack('subtitles', 'b');
+ window.track = document.createElement('track');
+ track.label = 'a';
+ video.appendChild(track);
+ video.addTextTrack('subtitles', 'c');
+});
+test(function(){
+ assert_equals(video.textTracks[0].label, 'a');
+ assert_equals(video.textTracks[1].label, 'b');
+ assert_equals(video.textTracks[2].label, 'c');
+});
+test(function(){
+ var track_before = video.textTracks[0];
+ video.textTracks[0] = 'foo';
+ assert_equals(video.textTracks[0], track_before);
+}, document.title+', no indexed set/create');
+test(function(){
+ 'use strict';
+ var track_before = video.textTracks[0];
+ assert_throws_js(TypeError, function(){ video.textTracks[0] = 'foo'; });
+ assert_equals(video.textTracks[0], track_before);
+}, document.title+', no indexed set/create (strict)');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/length.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/length.html
new file mode 100644
index 0000000000..7a24130d10
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/length.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>TextTrackList.length</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.video = document.createElement('video');
+ video.addTextTrack('subtitles');
+ window.track = document.createElement('track');
+ video.appendChild(track);
+ video.addTextTrack('subtitles');
+});
+test(function(){
+ assert_equals(video.textTracks.length, 3);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onaddtrack.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onaddtrack.html
new file mode 100644
index 0000000000..114ca89046
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onaddtrack.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>TextTrackList.onaddtrack</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.tracks = document.createElement('video').textTracks;
+ window.ev = new Event('addtrack');
+ window.ran = false;
+ window.cb = function() { ran = true; };
+});
+test(function(){
+ assert_equals(tracks.onaddtrack, null);
+ tracks.onaddtrack = cb;
+ assert_equals(tracks.onaddtrack, cb);
+ tracks.dispatchEvent(ev);
+ assert_true(ran);
+ tracks.onaddtrack = null;
+ ran = false;
+ tracks.dispatchEvent(ev);
+ assert_false(ran);
+});
+test(function(){
+ tracks.addEventListener('addtrack', cb, false);
+ tracks.dispatchEvent(ev);
+ assert_true(ran);
+ tracks.removeEventListener('addtrack', cb, false);
+ ran = false;
+ tracks.dispatchEvent(ev);
+ assert_false(ran);
+}, 'TextTrackList.addEventListener/removeEventListener');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onremovetrack.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onremovetrack.html
new file mode 100644
index 0000000000..b8da16ce2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/onremovetrack.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>TextTrackList.onremovetrack</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+setup(function(){
+ window.tracks = document.createElement('video').textTracks;
+ window.ev = new Event('removetrack');
+ window.ran = false;
+ window.cb = function() { ran = true; };
+});
+test(function(){
+ assert_equals(tracks.onremovetrack, null);
+ tracks.onremovetrack = cb;
+ assert_equals(tracks.onremovetrack, cb);
+ tracks.dispatchEvent(ev);
+ assert_true(ran);
+ tracks.onremovetrack = null;
+ ran = false;
+ tracks.dispatchEvent(ev);
+ assert_false(ran);
+});
+test(function(){
+ tracks.addEventListener('removetrack', cb, false);
+ tracks.dispatchEvent(ev);
+ assert_true(ran);
+ tracks.removeEventListener('removetrack', cb, false);
+ ran = false;
+ tracks.dispatchEvent(ev);
+ assert_false(ran);
+}, 'TextTrackList.addEventListener/removeEventListener');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/constructor.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/constructor.html
new file mode 100644
index 0000000000..cb5b89711f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/constructor.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>TrackEvent constructor</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ var ev = new TrackEvent('foo');
+ assert_true(ev instanceof TrackEvent, 'ev instanceof TrackEvent');
+ assert_true(ev instanceof Event, 'ev instanceof Event');
+ assert_equals(ev.track, null, 'ev.track');
+ ev.track = {};
+ assert_equals(ev.track, null, 'ev.track after assignment');
+}, document.title+', one arg');
+test(function(){
+ var video = document.createElement('video');
+ var testTrack = video.addTextTrack('subtitles', 'foo', 'foo');
+ var ev = new TrackEvent('foo', {track: testTrack});
+ assert_true(ev instanceof TrackEvent, 'ev instanceof TrackEvent');
+ assert_true(ev instanceof Event, 'ev instanceof Event');
+ assert_equals(ev.track, testTrack, 'ev.track');
+ ev.track = {};
+ assert_equals(ev.track, testTrack, 'ev.track after assignment');
+}, document.title+', two args');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/createEvent.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/createEvent.html
new file mode 100644
index 0000000000..1d7eb540c3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/interfaces/TrackEvent/createEvent.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>TrackEvent created with createEvent</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function(){
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17268
+ assert_throws_dom('NOT_SUPPORTED_ERR', function() {
+ var ev = document.createEvent('TrackEvent');
+ });
+ var ev = new TrackEvent('foo');
+ assert_false('initTrackEvent' in ev, 'initTrackEvent');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/autoplay-overrides-preload.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/autoplay-overrides-preload.html
new file mode 100644
index 0000000000..332184d55c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/autoplay-overrides-preload.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<title>autoplay overrides preload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id=log></div>
+<script>
+['none', 'metadata'].forEach(function(preload) {
+ ['first', 'last'].forEach(function(order) {
+ async_test(function(t) {
+ var a = document.createElement('audio');
+ a.src = getAudioURI('/media/sound_5');
+ if (order == 'first') {
+ a.autoplay = true;
+ a.preload = preload;
+ } else {
+ a.preload = preload;
+ a.autoplay = true;
+ }
+ a.addEventListener('error', t.unreached_func());
+ a.addEventListener('playing', t.step_func(function() {
+ assert_equals(a.readyState, a.HAVE_ENOUGH_DATA);
+ assert_false(a.paused);
+ t.done();
+ }));
+ }, 'autoplay (set ' + order + ') overrides preload "' + preload + '"');
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html
new file mode 100644
index 0000000000..d163c0e5b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-events-networkState.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<title>load() fires abort/emptied events when networkState is not NETWORK_EMPTY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id=log></div>
+<script>
+// Load media resource
+// https://html.spec.whatwg.org/multipage/media.html#loading-the-media-resource
+function load_test(t, v) {
+ assert_not_equals(v.networkState, v.NETWORK_EMPTY);
+
+ var expected_events = [];
+ if (v.networkState == v.NETWORK_LOADING || v.networkState == v.NETWORK_IDLE) {
+ expected_events.push('abort');
+ }
+
+ if (v.networkState != v.NETWORK_EMPTY) {
+ expected_events.push('emptied');
+ }
+
+ if (v.currentTime != 0.0) {
+ expected_events.push('timeupdate');
+ }
+
+ var actual_events = [];
+ v.onabort = v.onemptied = v.ontimeupdate = t.step_func(function(e) {
+ actual_events.push(e.type);
+ });
+
+ v.onloadstart = t.step_func(function() {
+ assert_array_equals(actual_events, expected_events);
+ t.done();
+ });
+
+ v.load();
+
+ assert_array_equals(actual_events, [], 'events should be fired in queued tasks');
+}
+
+async_test(function(t) {
+ var v = document.createElement('video');
+ // suspend is fired optionally "if the user agent intends to not attempt to
+ // fetch the resource" or "once the entire media resource has been fetched"
+ v.preload = 'none';
+ v.src = getAudioURI('/media/sound_5');
+ v.onerror = t.unreached_func();
+ v.onsuspend = t.step_func(function() {
+ v.onsuspend = null;
+ assert_equals(v.networkState, v.NETWORK_IDLE);
+ load_test(t, v);
+ });
+}, 'NETWORK_IDLE');
+
+// Test if media element receives `emptied` before `timeupdate`
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = getAudioURI('/media/sound_5');
+ v.onerror = t.unreached_func();
+ v.onloadeddata = t.step_func(function() {
+ v.onloadeddata = null;
+ assert_not_equals(v.networkState, v.NETWORK_EMPTY);
+ // Modify current time to ensure that loading would trigger `timeupdate` by
+ // resetting the current time.
+ v.currentTime = 1.0;
+ load_test(t, v);
+ });
+}, 'NETWORK_DISPATCH_EMPTIED_BEFORE_TIMEUPDATE');
+
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = 'resources/delayed-broken-video.py';
+ v.onerror = t.unreached_func();
+ v.onloadstart = t.step_func(function() {
+ v.onloadstart = null;
+ assert_equals(v.networkState, v.NETWORK_LOADING);
+ load_test(t, v);
+ });
+}, 'NETWORK_LOADING');
+
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = 'data:,';
+ v.onerror = t.step_func(function() {
+ v.onerror = null;
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE);
+ load_test(t, v);
+ });
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE);
+}, 'NETWORK_NO_SOURCE');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-removes-queued-error-event.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-removes-queued-error-event.html
new file mode 100644
index 0000000000..54d5c28dad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/load-removes-queued-error-event.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>load() removes queued error event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+// The loadstart and error event firing tasks are queued in the synchronous
+// section of the resource selection algorithm, so no tasks can come between
+// them. Calling load() in the loadstart event handler removes the queued error
+// event task at very latest opportunity, failing any implementation that fires
+// the events in the same task.
+
+async_test(function(t) {
+ var v = document.createElement('video');
+ var events = [];
+ v.onloadstart = v.onerror = t.step_func(function(e) {
+ events.push(e.type);
+ if (events.length == 1) {
+ v.load();
+ } else if (events.length == 3) {
+ assert_array_equals(events, ['loadstart', 'loadstart', 'error']);
+ t.done();
+ }
+ });
+ v.src = '';
+}, 'video error event');
+
+async_test(function(t) {
+ var v = document.createElement('video');
+ var s = document.createElement('source');
+ var events = [];
+ v.onloadstart = s.onerror = t.step_func(function(e) {
+ events.push(e.type);
+ if (events.length == 1) {
+ v.load();
+ } else if (events.length == 3) {
+ assert_array_equals(events, ['loadstart', 'loadstart', 'error']);
+ t.done();
+ }
+ });
+ v.onerror = t.step_func(function() { assert_unreached(); });
+ v.appendChild(s);
+}, 'source error event');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html
new file mode 100644
index 0000000000..39c9887505
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-insert-before.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>inserting another source before the candidate</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+function createSource(src) {
+ var source = document.createElement('source');
+ source.src = src;
+ return source;
+}
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ v.addEventListener('loadstart', t.step_func(function() {
+ assert_equals(v.currentSrc.substr(v.currentSrc.lastIndexOf('#')), '#a');
+ t.done();
+ }), false);
+ v.appendChild(createSource('#a')); // invokes resource selection
+});
+</script>
+<!-- now resource selection algorithm will continue its sync section (the </script> tag below provides a stable state) -->
+<!-- #a is candidate -->
+<!-- pointer is between #a and the end of the list -->
+<script>
+t.step(function() {
+ v.insertBefore(createSource('#b'), v.firstChild); // pointer is unchanged, #a is still candidate
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-moved.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-moved.html
new file mode 100644
index 0000000000..f59452e0d8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-moved.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>moving the candidate source</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var s;
+var t = async_test(function(t) {
+ var v = document.createElement('video');
+ s = document.createElement('source');
+ s.src = 'resources/delayed-broken-video.py';
+ s.onerror = t.step_func(function() { t.done(); });
+ v.appendChild(s); // invokes resource selection
+ onload = t.step_func(function() { assert_unreached(); });
+});
+</script>
+<script>
+t.step(function() {
+ document.body.appendChild(s);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-addEventListener.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-addEventListener.html
new file mode 100644
index 0000000000..0c1e6f0ad8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-addEventListener.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>removing the candidate source, addEventListener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+function createSource(src) {
+ var source = document.createElement('source');
+ source.src = src;
+ return source;
+}
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ v.appendChild(createSource('resources/delayed-broken-video.py')); // invokes resource selection
+ v.firstChild.addEventListener('error', t.step_func(function() { t.done(); }), false);
+});
+</script>
+<!-- now resource selection algorithm will continue its sync section (the </script> tag below provides a stable state) -->
+<!-- the <source> is candidate -->
+<!-- pointer is between the <source> and the end of the list -->
+<script>
+t.step(function() {
+ v.removeChild(v.firstChild); // tests that we fire 'error' on it despite being removed
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-no-listener.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-no-listener.html
new file mode 100644
index 0000000000..f384eb3121
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-no-listener.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>removing the candidate source, no listener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+function createSource(src) {
+ var source = document.createElement('source');
+ source.src = src;
+ return source;
+}
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ v.appendChild(createSource('resources/delayed-broken-video.py')); // invokes resource selection
+});
+</script>
+<!-- now resource selection algorithm will continue its sync section (the </script> tag below provides a stable state) -->
+<!-- the <source> is candidate -->
+<!-- pointer is between the <source> and the end of the list -->
+<script>
+t.step(function() {
+ v.removeChild(v.firstChild); // just tests that we don't crash
+ onload = t.step_func(function() { t.done(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-onerror.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-onerror.html
new file mode 100644
index 0000000000..c295c85bfc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-candidate-remove-onerror.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>removing the candidate source, onerror</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+function createSource(src) {
+ var source = document.createElement('source');
+ source.src = src;
+ return source;
+}
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ v.appendChild(createSource('resources/delayed-broken-video.py')); // invokes resource selection
+ v.firstChild.onerror = t.step_func(function() { t.done(); });
+});
+</script>
+<!-- now resource selection algorithm will continue its sync section (the </script> tag below provides a stable state) -->
+<!-- the <source> is candidate -->
+<!-- pointer is between the <source> and the end of the list -->
+<script>
+t.step(function() {
+ v.removeChild(v.firstChild); // tests that we fire 'error' on it despite being removed
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-currentSrc.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-currentSrc.html
new file mode 100644
index 0000000000..61902161ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-currentSrc.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<title>currentSrc should not be reset when changing source</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<audio src="/media/sine440.mp3"></audio>
+<script>
+let v;
+let t = async_test("Test currentSrc behaviour in various playback scenarios");
+v = document.querySelector('audio');
+function queueTaskAndStep(f) {
+ step_timeout(function() {
+ t.step(f);
+ }, 0);
+}
+
+function next() {
+ let testcase = tests.shift();
+ if (!testcase) {
+ t.done();
+ return;
+ }
+ step_timeout(testcase, 0);
+}
+
+let tests = [
+ function() {
+ v.src = "/media/sound_0.mp3";
+ queueTaskAndStep(function() {
+ assert_true(v.currentSrc.indexOf("sound_0.mp3") != -1, "currentSrc must be equal to the source after load if present");
+ next();
+ });
+ },
+ function() {
+ v.src = URL.createObjectURL(new MediaSource());
+ queueTaskAndStep(function() {
+ assert_not_equals(v.currentSrc, "", "currentSrc must not be equal to the empty string after load if playing a MediaSource from the src attribute");
+ next();
+ });
+ },
+ function() {
+ fetch('/media/sound_0.mp3')
+ .then(function(response) {
+ return response.arrayBuffer();
+ }).then((b) => {
+ v.src = URL.createObjectURL(new Blob([new Uint8Array(b)], ["audio/mpeg"]));
+ queueTaskAndStep(function() {
+ assert_not_equals(v.currentSrc, "", "currentSrc must be not equal to the empty string after load if playing a Blob from the src attribute");
+ next();
+ });
+ });
+ },
+ function() {
+ v.src = "/media/sound_0.mp3";
+ // Source should be ignored when there is an `src`
+ let sourceNode = document.createElement("source");
+ sourceNode.setAttribute("src", "/media/sine440.mp3");
+ sourceNode.setAttribute("type", "audio/mpeg");
+ v.appendChild(sourceNode);
+ queueTaskAndStep(function() {
+ assert_true(v.currentSrc.indexOf("sine440.mp3") == -1, "The src attribute takes precedence over any source child element when both are preset");
+ next();
+ })
+ },
+ function() {
+ // But taken into account when there is no `src` attribute;
+ v.src = "";
+ v.removeAttribute("src");
+ queueTaskAndStep(function() {
+ assert_true(v.currentSrc.indexOf("sine440.mp3") != -1, "The child source element is the current source when no src attribute is present");
+ next();
+ });
+ },
+ function() {
+ v.firstChild.remove();
+ v.src = "https://test:test/";
+ queueTaskAndStep(function() {
+ assert_true(v.currentSrc.indexOf("sine440.mp3") != -1, "Not reset when a new load errors");
+ next();
+ });
+ },
+ function() {
+ v.srcObject = new MediaStream();
+ queueTaskAndStep(function() {
+ assert_equals(v.currentSrc, "", "When playing a MediaStream, currentSrc should also be reset to an empty string");
+ next();
+ });
+ }
+];
+
+next();
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor-no-src.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor-no-src.html
new file mode 100644
index 0000000000..cb2a579597
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor-no-src.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>NOT invoking resource selection with new Audio() sans src</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var a = new Audio();
+ assert_equals(a.networkState, a.NETWORK_EMPTY);
+ a.onloadstart = t.step_func(function() { assert_unreached(); });
+ window.onload = t.step_func(function() { t.done(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor.html
new file mode 100644
index 0000000000..662129756f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-audio-constructor.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>invoking resource selection with new Audio(src)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var a = new Audio('');
+ a.onloadstart = t.step_func(function() { t.done(); });
+ window.onload = t.step_func(function() { assert_unreached(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-in-sync-event.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-in-sync-event.html
new file mode 100644
index 0000000000..1635598efd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-in-sync-event.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>await a stable state and sync event handlers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.querySelector('video');
+ var a = document.createElement('a');
+ a.onclick = t.step_func(function() {
+ v.setAttribute('src', '#'); // invokes media load which invokes resource selection
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState in onclick handler');
+ });
+ a.click(); // sync fires click, so sets src
+ // now we should still await a stable state because the script hasn't
+ // finished, the event handler has just returned
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after click()');
+ v.removeAttribute('src');
+});
+t.step(function() {
+ // now the sync section of resource selection should have run and should
+ // have found no src="" or <source> thus networkState being set to NETWORK_EMPTY.
+ // if the sync section was run when onclick returned, then networkState
+ // would be either NETWORK_LOADING or NETWORK_NO_SOURCE.
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after src removed');
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-fragment-into-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-fragment-into-document.html
new file mode 100644
index 0000000000..5d4c32f670
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-fragment-into-document.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>NOT invoking resource selection by inserting document fragment into a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild(v);
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after fragment.appendChild(v)');
+ document.body.appendChild(fragment);
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after document.body.appendChild(fragment)');
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState in separate script');
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-document.html
new file mode 100644
index 0000000000..2f9ec978a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-document.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>NOT invoking resource selection by inserting into a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function(t) {
+ var v = document.createElement('video');
+ document.body.appendChild(v);
+ assert_equals(v.networkState, v.NETWORK_EMPTY);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-iframe.html
new file mode 100644
index 0000000000..45d133d878
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-into-iframe.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>NOT invoking resource selection by inserting into other document with src set</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe hidden></iframe>
+<script>
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = 'data:,';
+ v.onerror = t.step_func(function() {
+ assert_equals(v.readyState, v.HAVE_NOTHING);
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE);
+ var iframe = document.querySelector('iframe');
+ iframe.contentDocument.body.appendChild(v);
+ v.onloadstart = t.step_func(function() { assert_unreached(); });
+ // wait for an event after the above
+ var v2 = document.createElement('video');
+ v2.src = 'data:,';
+ v2.onloadstart = t.step_func(function() { t.done(); });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-parent-into-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-parent-into-document.html
new file mode 100644
index 0000000000..6da34344fd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-parent-into-document.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>NOT invoking resource selection by inserting parent into a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var v = document.createElement('video');
+ var div = document.createElement('div');
+ div.appendChild(v);
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after div.appendChild(v)');
+ document.body.appendChild(div);
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after document.body.appendChild(div)');
+ window.onload = t.step_func(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState in window.onload');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-div.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-div.html
new file mode 100644
index 0000000000..b79bea52f1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-div.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>NOT invoking resource selection by inserting &lt;source> in &lt;div> in &lt;video></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video><div></div></video>
+<script>
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.onloadstart = t.step_func(function() { assert_unreached(); });
+ v.firstChild.appendChild(document.createElement('source'));
+ window.onload = t.step_func(function() { t.done(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-namespace.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-namespace.html
new file mode 100644
index 0000000000..b73f229ecc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-in-namespace.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>NOT invoking resource selection by inserting &lt;source> in the wrong namespace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.onloadstart = t.step_func(function() { assert_unreached(); });
+ v.appendChild(document.createElementNS('bogus','source'));
+ window.onload = t.step_func(function() { t.done(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-networkState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-networkState.html
new file mode 100644
index 0000000000..5ef6e4cb3b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-networkState.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>NOT invoking resource selection by inserting &lt;source> when networkState is not NETWORK_EMPTY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var loadstartCount = 0;
+var s1ErrorCount = 0;
+var s2ErrorCount = 0;
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ v.onloadstart = function() { loadstartCount++; };
+ var s1 = document.createElement('source');
+ s1.src = 'resources/delayed-broken-video.py';
+ s1.onerror = function() { s1ErrorCount++; };
+ v.appendChild(s1); // invokes resource selection
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState in first script');
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(v.networkState, v.NETWORK_LOADING, 'networkState in second script');
+ assert_equals(s1ErrorCount, 0, 's1ErrorCount in second script');
+ var s2 = document.createElement('source');
+ s2.onerror = t.step_func(function() {
+ s2ErrorCount++;
+ assert_equals(s1ErrorCount, 1, 's1ErrorCount in s2.onerror');
+ });
+ v.appendChild(s2);
+ onload = t.step_func(function() {
+ assert_equals(s2ErrorCount, 1, 's2ErrorCount in window.onload');
+ assert_equals(loadstartCount, 1, 'loadstartCount in window.onload'); // reliable if https://www.w3.org/Bugs/Public/show_bug.cgi?id=24353 is fixed
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState in window.onload'); // See Waiting step
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-not-in-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-not-in-document.html
new file mode 100644
index 0000000000..2007b2e8b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-not-in-document.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>invoking resource selection by inserting &lt;source> in video not in a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.onloadstart = t.step_func(function() { t.done(); });
+ v.appendChild(document.createElement('source'));
+ window.onload = t.step_func(function() { assert_unreached(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source.html
new file mode 100644
index 0000000000..969daad623
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>invoking resource selection by inserting &lt;source></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.onloadstart = t.step_func(function() { t.done(); });
+ v.appendChild(document.createElement('source'));
+ window.onload = t.step_func(function() { assert_unreached(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-load.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-load.html
new file mode 100644
index 0000000000..909c72cd15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-load.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>invoking resource selection with load()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after creating v');
+ v.load();
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after v.load()');
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState in separate script');
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause-networkState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause-networkState.html
new file mode 100644
index 0000000000..18561a2649
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause-networkState.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>NOT invoking resource selection with pause() when networkState is not NETWORK_EMPTY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.querySelector('video');
+ v.src = 'data:,';
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after setting src');
+ var errorCount = 0;
+ v.onerror = t.step_func(function() {
+ errorCount++;
+ if (errorCount == 1) {
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState in onerror');
+ v.pause(); // should not invoke RSA. if it does, error will be fired again.
+ } else {
+ assert_unreached();
+ }
+ });
+ onload = t.step_func(function() {
+ assert_equals(errorCount, 1, 'errorCount');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause.html
new file mode 100644
index 0000000000..4f1bca74dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>invoking resource selection with pause()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after creating v');
+ v.pause();
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after v.pause()');
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState in separate script');
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-play.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-play.html
new file mode 100644
index 0000000000..64a440080c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-play.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>invoking resource selection with play()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after creating v');
+ v.play();
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after v.play()');
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState in separate script');
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document-networkState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document-networkState.html
new file mode 100644
index 0000000000..1eed276b20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document-networkState.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>NOT invoking resource selection with implicit pause() when networkState is not NETWORK_EMPTY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.querySelector('video');
+ v.src = 'data:,';
+ document.body.appendChild(v);
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after setting src');
+ var errorCount = 0;
+ v.onerror = t.step_func(function() {
+ errorCount++;
+ if (errorCount == 1) {
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState in onerror');
+ document.body.removeChild(v); // invokes pause() which should not invoke RSA. if it does, error will be fired again.
+ } else {
+ assert_unreached();
+ }
+ });
+ onload = t.step_func(function() {
+ assert_equals(errorCount, 1, 'errorCount');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document.html
new file mode 100644
index 0000000000..65d0f73114
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-from-document.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>NOT invoking resource selection by removing from document with NETWORK_EMPTY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+test(function() {
+ v = document.createElement('video');
+ document.body.appendChild(v);
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after appending v to document');
+ v.parentNode.removeChild(v); // search for "When a media element is removed from a Document,"
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after removing v');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-src.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-src.html
new file mode 100644
index 0000000000..6302ffeacf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-remove-src.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>NOT invoking media load or resource selection when removing the src attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ v.setAttribute('src', ''); // invokes media load
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after setting src');
+ var s = document.createElement('source');
+ s.onerror = this.step_func(function() { assert_unreached(); });
+ v.appendChild(s); // src is present so nothing happens here
+ onload = this.step_func(function() { t.done(); });
+});
+</script>
+<script>
+t.step(function() {
+ v.removeAttribute('src'); // nothing should happen
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-in-namespace.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-in-namespace.html
new file mode 100644
index 0000000000..438db124d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-in-namespace.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>NOT invoking load by setting src in the wrong namespace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.onloadstart = t.step_func(function() { assert_unreached(); });
+ v.setAttributeNS('bogus','src', '');
+ window.onload = t.step_func(function() { t.done(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-networkState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-networkState.html
new file mode 100644
index 0000000000..ed86dbe0c5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-networkState.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>invoking load by setting src when networkState is not NETWORK_EMPTY</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function(t) {
+ var v = document.createElement('video');
+ v.play().catch(() => {}); // invokes resource selection and sets .paused to false
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState');
+ assert_false(v.paused, 'paused');
+ v.setAttribute('src', ''); // invokes media load which sets .paused to true
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after setting src');
+ assert_true(v.paused, 'paused after setting src');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-not-in-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-not-in-document.html
new file mode 100644
index 0000000000..f6c4f2406a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src-not-in-document.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>invoking load by setting src on video not in a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.onloadstart = t.step_func(function() { t.done(); });
+ v.setAttribute('src','');
+ window.onload = t.step_func(function() { assert_unreached(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src.html
new file mode 100644
index 0000000000..e04b1b0580
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-set-src.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>invoking load by setting src</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.onloadstart = t.step_func(function() { t.done(); });
+ v.setAttribute('src', '');
+ window.onload = t.step_func(function() { assert_unreached(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-control.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-control.html
new file mode 100644
index 0000000000..dad5e5fd00
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-control.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>pointer updates (control test)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var a = 0;
+var b = 0;
+var c = 0;
+</script>
+<video
+ ><source onerror=a++
+ ><source onerror=b++ src='resources/delayed-broken-video.py'
+ ><source onerror=c++
+ ></video
+>
+<script>
+async_test(function(t) {
+ window.onload = t.step_func(function() {
+ assert_equals(a, 1, 'error events on a');
+ assert_equals(b, 1, 'error events on b');
+ assert_equals(c, 1, 'error events on c');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-br.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-br.html
new file mode 100644
index 0000000000..3ee141e306
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-br.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>pointer updates (adding br elements)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var a = 0;
+var b = 0;
+var c = 0;
+</script>
+<video
+ ><source onerror=a++
+ ><source onerror=b++ src='resources/delayed-broken-video.py'
+ ><source onerror=c++
+ ></video
+>
+<script>
+async_test(function(t) {
+ var video = document.querySelector('video');
+ // add br elements
+ var br = document.createElement('br');
+ video.insertBefore(br, video.querySelector('[onerror="a++"]'));
+ video.insertBefore(br.cloneNode(false), video.querySelector('[onerror="b++"]'));
+ video.insertBefore(br.cloneNode(false), video.querySelector('[onerror="c++"]'));
+ video.appendChild(br.cloneNode(false));
+ window.onload = t.step_func(function() {
+ assert_equals(a, 1, 'error events on a');
+ assert_equals(b, 1, 'error events on b');
+ assert_equals(c, 1, 'error events on c');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-source.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-source.html
new file mode 100644
index 0000000000..2d32e6fca0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-source.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>pointer updates (adding source elements)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var a = 0;
+var b = 0;
+var c = 0;
+var x1 = 0;
+var x2 = 0;
+var x3 = 0;
+var x4 = 0;
+</script>
+<video
+ ><source onerror=a++
+ ><source onerror=b++ src='resources/delayed-broken-video.py'
+ ><source onerror=c++
+ ></video
+>
+<script>
+async_test(function(t) {
+ var video = document.querySelector('video');
+ // add source elements
+ var source1 = document.createElement('source'); source1.onerror = function() { x1++; };
+ var source2 = document.createElement('source'); source2.onerror = function() { x2++; };
+ var source3 = document.createElement('source'); source3.onerror = function() { x3++; };
+ var source4 = document.createElement('source'); source4.onerror = function() { x4++; };
+ video.insertBefore(source1, video.querySelector('[onerror="a++"]'));
+ video.insertBefore(source2, video.querySelector('[onerror="b++"]'));
+ video.insertBefore(source3, video.querySelector('[onerror="c++"]'));
+ video.appendChild(source4);
+ window.onload = t.step_func(function() {
+ assert_equals(a, 1, 'error events on a');
+ assert_equals(b, 1, 'error events on b');
+ assert_equals(c, 1, 'error events on c');
+ assert_equals(x1, 0, 'error events on x1');
+ assert_equals(x2, 0, 'error events on x2');
+ assert_equals(x3, 1, 'error events on x3');
+ assert_equals(x4, 1, 'error events on x4');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-text.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-text.html
new file mode 100644
index 0000000000..15a4e4be06
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-insert-text.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>pointer updates (adding text nodes)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var a = 0;
+var b = 0;
+var c = 0;
+</script>
+<video
+ ><source onerror=a++
+ ><source onerror=b++ src='resources/delayed-broken-video.py'
+ ><source onerror=c++
+ ></video
+>
+<script>
+async_test(function(t) {
+ var video = document.querySelector('video');
+ // add text nodes
+ var text = document.createTextNode('x');
+ video.insertBefore(text, video.querySelector('[onerror="a++"]'));
+ video.insertBefore(text.cloneNode(false), video.querySelector('[onerror="b++"]'));
+ video.insertBefore(text.cloneNode(false), video.querySelector('[onerror="c++"]'));
+ video.appendChild(text.cloneNode(false));
+ window.onload = t.step_func(function() {
+ assert_equals(a, 1, 'error events on a');
+ assert_equals(b, 1, 'error events on b');
+ assert_equals(c, 1, 'error events on c');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source-after.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source-after.html
new file mode 100644
index 0000000000..0d1c940375
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source-after.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<title>pointer updates (removing source element after pointer)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var a = 0;
+var b = 0;
+var c = 0;
+var x1 = 0;
+var x2 = 0;
+var x3 = 0;
+var x4 = 0;
+</script>
+<video
+ ><source onerror=a++
+ ><source onerror=b++ src='resources/delayed-broken-video.py'
+ ><source onerror=x1++
+ ><source onerror=x2++
+ ><source onerror=x3++
+ ><source onerror=x4++
+ ><source onerror=c++
+ ></video
+>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.querySelector('video');
+ v.removeChild(document.querySelector('[onerror="x1++"]'));
+ window.onload = t.step_func(function() {
+ assert_equals(a, 1, 'error events on a');
+ assert_equals(b, 1, 'error events on b');
+ assert_equals(c, 1, 'error events on c');
+ assert_equals(x1, 0, 'error events on x1');
+ assert_equals(x2, 0, 'error events on x2');
+ assert_equals(x3, 0, 'error events on x3');
+ assert_equals(x4, 0, 'error events on x4');
+ t.done();
+ });
+});
+</script>
+<script>
+t.step(function() {
+ v.removeChild(document.querySelector('[onerror="x2++"]'));
+});
+</script>
+<script>
+t.step(function() {
+ v.removeChild(document.querySelector('[onerror="x3++"]'));
+});
+</script>
+<script>
+t.step(function() {
+ v.removeChild(document.querySelector('[onerror="x4++"]'));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source.html
new file mode 100644
index 0000000000..191f9b5e21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<title>pointer updates (removing source elements)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var a = 0;
+var b = 0;
+var c = 0;
+var x1 = 0;
+var x2 = 0;
+var x3 = 0;
+var x4 = 0;
+</script>
+<video
+ ><source onerror=x1++
+ ><source onerror=a++
+ ><source onerror=x2++
+ ><source onerror=b++ src='resources/delayed-broken-video.py'
+ ><source onerror=x3++
+ ><source onerror=c++
+ ><source onerror=x4++
+ ></video
+>
+<script>
+async_test(function(t) {
+ var video = document.querySelector('video');
+ // remove the xn elements
+ [].forEach.call(document.querySelectorAll('[onerror^="x"]'), function(elm) {
+ video.removeChild(elm);
+ });
+ window.onload = t.step_func(function() {
+ assert_equals(a, 1, 'error events on a');
+ assert_equals(b, 1, 'error events on b');
+ assert_equals(c, 1, 'error events on c');
+ assert_equals(x1, 1, 'error events on x1');
+ assert_equals(x2, 1, 'error events on x2');
+ assert_equals(x3, 0, 'error events on x3');
+ assert_equals(x4, 0, 'error events on x4');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-text.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-text.html
new file mode 100644
index 0000000000..f0fe5da909
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-text.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>pointer updates (removing text nodes)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var a = 0;
+var b = 0;
+var c = 0;
+</script>
+<video
+ >x<source onerror=a++
+ >x<source onerror=b++ src='resources/delayed-broken-video.py'
+ >x<source onerror=c++
+ >x</video
+>
+<script>
+async_test(function(t) {
+ var video = document.querySelector('video');
+ // remove the text nodes
+ [].forEach.call(video.childNodes, function(node) {
+ if (node.nodeType == node.TEXT_NODE) {
+ video.removeChild(node);
+ }
+ });
+ window.onload = t.step_func(function() {
+ assert_equals(a, 1, 'error events on a');
+ assert_equals(b, 1, 'error events on b');
+ assert_equals(c, 1, 'error events on c');
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-source.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-source.html
new file mode 100644
index 0000000000..fbeead0191
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-source.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Changes to networkState when inserting and removing a &lt;source></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState when creating the element');
+ v.appendChild(document.createElement('source')); // runs resource selection algorithm
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState when inserting a source element');
+ v.removeChild(v.firstChild);
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after removing the source element');
+});
+</script>
+<!-- now resource selection will continue its sync section (the </script> tag below provides a stable state) -->
+<!-- will find neither src nor source, so sets networkState to NETWORK_EMPTY -->
+<script>
+t.step(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY, 'networkState after letting the sync section of resource selection run');
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-src.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-src.html
new file mode 100644
index 0000000000..4d78871823
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-remove-src.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>invoking resource selection by setting src; await stable state</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var v;
+var t = async_test(function(t) {
+ v = document.createElement('video');
+ v.onloadstart = t.step_func(function() { assert_unreached(); });
+ v.setAttribute('src', ''); // runs resource selection algorithm, but it will wait running the sync section until this script has finished
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE);
+ v.removeAttribute('src'); // will make resource selection algorithm revert to NETWORK_EMPTY and abort (in the sync section)
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE);
+ window.onload = t.step_func(function() { t.done(); });
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-resumes-onload.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-resumes-onload.html
new file mode 100644
index 0000000000..b166763d14
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-resumes-onload.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>resource selection should not delay the load event indefinitely</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video></video>
+<script>
+async_test(function(t) {
+ const v = document.querySelector('video');
+ v.onloadstart = t.unreached_func("loadstart event should not be fired when the resource selection algorithm cannot determine mode");
+ const s = document.createElement('source');
+ v.appendChild(s); // this will trigger resource selection
+ v.removeChild(s); // force an early return in resource selection algorithm
+ window.onload = t.step_func_done(function() {
+ assert_equals(v.networkState, v.NETWORK_EMPTY);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media-env-change.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media-env-change.html
new file mode 100644
index 0000000000..67f2c8300d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media-env-change.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>the &lt;source> media attribute: no reaction to environment change</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe src="resources/media-min-width.html" width="300"></iframe>
+<script>
+// promises for the iframed test to resolve
+let beforeEnvChange = new Promise((resolve, reject) => {
+ window[0].resolveBeforeEnvChange = resolve;
+});
+let afterEnvChange = new Promise((resolve, reject) => {
+ window[0].resolveAfterEnvChange = resolve;
+});
+let afterLoadCalled = new Promise((resolve, reject) => {
+ window[0].resolveAfterLoadCalled = resolve;
+});
+const t = promise_test(async () => {
+ [beforeEnvChange, afterEnvChange, afterLoadCalled] = await Promise.all([ beforeEnvChange, afterEnvChange, afterLoadCalled ]);
+ assert_equals(beforeEnvChange, '#a', 'beforeEnvChange');
+ assert_equals(afterEnvChange, '#a', 'afterEnvChange');
+ assert_equals(afterLoadCalled, '#b', 'afterLoadCalled');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media.html
new file mode 100644
index 0000000000..df5f47add2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-source-media.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>the &lt;source> media attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<video><source src="resources/delayed-broken-video.py" media="not all"></video>
+<script>
+test(function() {
+ var v = document.querySelector('video');
+ var s = document.querySelector('source');
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE);
+ assert_equals(v.currentSrc, '');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/delayed-broken-video.py b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/delayed-broken-video.py
new file mode 100644
index 0000000000..4eae3261f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/delayed-broken-video.py
@@ -0,0 +1,5 @@
+import time
+
+def main(request, response):
+ time.sleep(0.1)
+ return [(b"Content-Type", b"text/plain")], u"FAIL"
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/media-min-width.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/media-min-width.html
new file mode 100644
index 0000000000..8a4ad500cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resources/media-min-width.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<video width="200"></video>
+<script>
+function createSource(src, media) {
+ var source = document.createElement('source');
+ source.src = src;
+ if (media) {
+ source.media = media;
+ }
+ return source;
+}
+const rAF = () => new Promise(resolve => requestAnimationFrame(resolve));
+const hash = str => str.substr(str.lastIndexOf('#'));
+(async () => {
+ const v = document.querySelector('video');
+ v.getBoundingClientRect(); // force layout flush. ensure viewport dimensions are up-to-date
+ v.append(createSource('/media-source/mp4/test.mp4#a', '(min-width: 200px)'));
+ v.append(createSource('/media-source/mp4/test.mp4#b'));
+ await rAF();
+ await rAF();
+ window.resolveBeforeEnvChange(hash(v.currentSrc));
+ window.frameElement.width = '150';
+ await rAF();
+ await rAF();
+ window.resolveAfterEnvChange(hash(v.currentSrc));
+ v.load()
+ await rAF();
+ await rAF();
+ window.resolveAfterLoadCalled(hash(v.currentSrc));
+})();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-beforeunload-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-beforeunload-manual.html
new file mode 100644
index 0000000000..61ed225fa1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-beforeunload-manual.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>stable state in beforeunload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<button>click this button and cancel navigation</button>
+<a href="data:text/plain,FAIL: did not cancel navigation"></a>
+<script>
+async_test(function(t) {
+ window.onbeforeunload = t.step_func(function(event) {
+ var message = "foo bar";
+ event.returnValue = message;
+ return message;
+ });
+ var button = document.querySelector('button');
+ var link = document.querySelector('a');
+ button.onclick = t.step_func(function() {
+ v = document.createElement('video');
+ v.src = 'data:,';
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState before dialog');
+ assert_equals(v.currentSrc, '', 'currentSrc before dialog');
+ link.click();
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after dialog');
+ assert_equals(v.currentSrc, '', 'currentSrc after dialog');
+ t.done();
+ window.onbeforeonload = null;
+ button.remove();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-dialogs-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-dialogs-manual.html
new file mode 100644
index 0000000000..267dde913c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-dialogs-manual.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>stable state in dialogs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+['alert', 'confirm', 'prompt'].forEach(function(dialog) {
+ test(function() {
+ v = document.createElement('video');
+ v.src = 'data:,';
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState before dialog');
+ assert_equals(v.currentSrc, '', 'currentSrc before dialog');
+ window[dialog]('dismiss this dialog');
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after dialog');
+ assert_equals(v.currentSrc, '', 'currentSrc after dialog');
+ }, 'stable state in ' + dialog + '()');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-print-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-print-manual.html
new file mode 100644
index 0000000000..1261a00793
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/stable-state-print-manual.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>stable state in print()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<button>click this button and dismiss the print dialog</button>
+<script>
+async_test(function(t) {
+ var button = document.querySelector('button');
+ button.onclick = t.step_func(function() {
+ v = document.createElement('video');
+ v.src = 'data:,';
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState before dialog');
+ assert_equals(v.currentSrc, '', 'currentSrc before dialog');
+ print();
+ assert_equals(v.networkState, v.NETWORK_NO_SOURCE, 'networkState after dialog');
+ assert_equals(v.currentSrc, '', 'currentSrc after dialog');
+ t.done();
+ button.remove();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/location-of-the-media-resource/currentSrc.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/location-of-the-media-resource/currentSrc.html
new file mode 100644
index 0000000000..be4d09f739
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/location-of-the-media-resource/currentSrc.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<title>currentSrc</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+['audio', 'video'].forEach(function(tagName) {
+ test(function() {
+ assert_equals(document.createElement(tagName).currentSrc, '');
+ }, tagName + '.currentSrc initial value');
+
+ ['', '.', ' ', 'data:,'].forEach(function(src) {
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = src;
+ assert_equals(e.currentSrc, '');
+ e.addEventListener('loadstart', function () {
+ t.step_timeout(function () {
+ if (src == '') {
+ assert_equals(e.currentSrc, '');
+ } else {
+ assert_equals(e.currentSrc, e.src);
+ }
+ t.done();
+ }, 0);
+ })
+ }, tagName + '.currentSrc after setting src attribute "' + src + '"');
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ var s = document.createElement('source');
+ s.src = src;
+ e.appendChild(s);
+ assert_equals(e.currentSrc, '');
+ e.addEventListener('loadstart', function() {
+ t.step_timeout(function () {
+ if (src == '') {
+ assert_equals(e.currentSrc, '');
+ } else {
+ assert_equals(e.currentSrc, s.src);
+ }
+ t.done();
+ }, 0);
+ });
+ }, tagName + '.currentSrc after adding source element with src attribute "' + src + '"');
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/media_fragment_seek.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/media_fragment_seek.html
new file mode 100644
index 0000000000..2a0106ce16
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/media_fragment_seek.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Video should seek to time specified in media fragment syntax</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<video id="video"></video>
+<script>
+async_test(function () {
+ let video = document.getElementById("video");
+ video.src = getVideoURI('/media/movie_5') + "#t=4,7";
+ video.load();
+ this.step_timeout(function () {
+ assert_equals(video.currentTime, 4.0);
+
+ video.src = getVideoURI('/media/movie_5') + "#t=%6Ept:3";
+ video.load();
+ this.step_timeout(function () {
+ assert_true(video.src.endsWith("t=%6Ept:3"));
+ assert_equals(video.currentTime, 3.0);
+
+ video.src = getVideoURI('/media/movie_5') + "#t=00:00:01.00";
+ video.load();
+ this.step_timeout(function () {
+ assert_true(video.src.endsWith("t=00:00:01.00"));
+ assert_equals(video.currentTime, 1.0);
+
+ video.src = getVideoURI('/media/movie_5') + "#u=12&t=3";
+ video.load();
+ this.step_timeout(function () {
+ assert_true(video.src.endsWith("#u=12&t=3"));
+ assert_equals(video.currentTime, 3.0);
+
+ video.src = getVideoURI('/media/movie_5') + "#t=npt%3A3";
+ video.load();
+ this.step_timeout(function () {
+ assert_true(video.src.endsWith("t=npt%3A3"));
+ assert_equals(video.currentTime, 3.0);
+ this.done();
+ }, 1000);
+ }, 1000);
+ }, 1000);
+ }, 1000);
+ }, 1000);
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html
new file mode 100644
index 0000000000..56edf25aa8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html
@@ -0,0 +1,123 @@
+<!doctype html>
+<title>canPlayType</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<audio id="audio"></audio>
+<video id="video"></video>
+<div id="log"></div>
+<script>
+let VIDEO_ELEM = document.getElementById('video');
+let AUDIO_ELEM = document.getElementById('audio');
+
+function t(type, expected) {
+ assert_equals(canPlayType(type), expected, type);
+}
+
+function mime(type, codecs) {
+ if (codecs.length) {
+ return type + '; codecs="' + codecs.join(', ') + '"';
+ }
+ return type;
+}
+
+test(function() {
+ assert_equals(mime('video/webm', []), 'video/webm');
+ assert_equals(mime('video/webm', ['vp8']), 'video/webm; codecs="vp8"');
+ assert_equals(mime('video/webm', ['vp8', 'vorbis']), 'video/webm; codecs="vp8, vorbis"');
+}, 'utility code');
+
+function canPlayType(type) {
+ let audioCanPlay = AUDIO_ELEM.canPlayType(type);
+ let videoCanPlay = VIDEO_ELEM.canPlayType(type);
+ assert_equals(audioCanPlay, videoCanPlay,
+ 'audio.canPlayType() and video.canPlayType() agree');
+ assert_in_array(audioCanPlay, ['', 'maybe', 'probably'],
+ 'return value is one of "", "maybe" and "probably"');
+ return audioCanPlay;
+}
+
+test(function() {
+ t('application/octet-stream', '');
+ t('application/octet-stream; codecs="vorbis"', '');
+ t('application/octet-stream; codecs="vp8, vorbis"', '');
+ t('application/octet-stream; codecs="mp4a.40.2"', '');
+ t('application/octet-stream; codecs="theora, vorbis"', '');
+ t('application/octet-stream; codecs="avc1.42E01E, mp4a.40.2"', '');
+}, 'application/octet-stream not supported');
+
+test(function() {
+ t('application/marks-fantasmagorical-format', '');
+ t('video/x-new-fictional-format', '');
+ t('video/x-new-fictional-format;codecs="kittens,bunnies"', '');
+}, 'fictional formats and codecs not supported');
+
+function type_codecs_test(type, audioCodecs, videoCodecs) {
+ var typeSupported = false;
+ var codecSupported = false;
+
+ // Test 'type' without codecs.
+ // Spec: Generally, a user agent should never return "probably" for a type
+ // that allows the codecs parameter if that parameter is not present.
+ test(function() {
+ t(type, 'maybe');
+ t(type + ';', 'maybe');
+ t(type + ';codecs', 'maybe');
+ t(type + ';codecs=', 'maybe');
+ typeSupported = true;
+ }, type + ' (optional)');
+
+ function test_codec(codec) {
+ var typeWithCodec = mime(type, [codec]);
+ test(function() {
+ t(typeWithCodec, 'probably');
+ codecSupported = true;
+ }, typeWithCodec + ' (optional)');
+ }
+
+ // Test each audio and video codec separately.
+ audioCodecs.forEach(test_codec);
+ videoCodecs.forEach(test_codec);
+
+ // Test different pairings and orderings of audio+video codecs.
+ if (audioCodecs.length > 0 && videoCodecs.length > 0) {
+ test(function() {
+ audioCodecs.forEach(function(ac) {
+ videoCodecs.forEach(function(vc) {
+ var canPlayBoth = canPlayType(mime(type, [ac, vc]));
+ if (canPlayBoth) {
+ t(mime(type, [ac]), canPlayBoth);
+ t(mime(type, [vc]), canPlayBoth);
+ }
+ });
+ });
+ }, type + ' codecs subset');
+
+ test(function() {
+ audioCodecs.forEach(function(ac) {
+ videoCodecs.forEach(function(vc) {
+ assert_equals(canPlayType(mime(type, [ac, vc])),
+ canPlayType(mime(type, [vc, ac])));
+ });
+ });
+ }, type + ' codecs order');
+ }
+
+ test(function() {
+ t(mime(type, ['bogus']), '');
+ }, type + ' with bogus codec');
+
+ test(function() {
+ // At least one known codec must be supported if the container format is.
+ assert_equals(typeSupported, codecSupported);
+ }, type + ' with and without codecs');
+}
+
+type_codecs_test('audio/mp4', ['mp4a.40.2'], []);
+type_codecs_test('audio/ogg', ['opus', 'vorbis'], []);
+type_codecs_test('audio/wav', ['1'], []);
+type_codecs_test('audio/webm', ['opus', 'vorbis'], []);
+type_codecs_test('video/3gpp', ['samr'], ['mp4v.20.8']);
+type_codecs_test('video/mp4', ['mp4a.40.2'], ['avc1.42E01E', 'avc1.4D401E', 'avc1.58A01E', 'avc1.64001E', 'mp4v.20.8', 'mp4v.20.240']);
+type_codecs_test('video/ogg', ['opus', 'vorbis'], ['theora']);
+type_codecs_test('video/webm', ['opus', 'vorbis'], ['vp8', 'vp8.0', 'vp9', 'vp9.0']);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_loadstart.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_loadstart.html
new file mode 100644
index 0000000000..8570bfd111
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_loadstart.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video}.networkState - NETWORK_LOADING</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#dom-media-networkstate">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+var ta = async_test("audioElement.networkState should be NETWORK_LOADING during loadstart event");
+var a = document.getElementById("a");
+a.addEventListener("loadstart", function() {
+ ta.step(function() {
+ assert_equals(a.networkState,
+ a.NETWORK_LOADING);
+ });
+ ta.done();
+ a.pause();
+}, false);
+a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+
+var tv = async_test("videoElement.networkState should be NETWORK_LOADING during loadstart event");
+var v = document.getElementById("v");
+v.addEventListener("loadstart", function() {
+ tv.step(function() {
+ assert_equals(v.networkState,
+ v.NETWORK_LOADING);
+ });
+ tv.done();
+ v.pause();
+}, false);
+v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_progress.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_progress.html
new file mode 100644
index 0000000000..db9df23cb6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_during_progress.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video}.networkState - NETWORK_LOADING</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#dom-media-networkstate">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var ta = async_test("audioElement.networkState should be NETWORK_LOADING during progress event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", ta.unreached_func());
+ a.addEventListener("progress", ta.step_func(function() {
+ assert_equals(a.networkState, a.NETWORK_LOADING);
+ ta.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - networkState during progress");
+
+test(function() {
+ var tv = async_test("videoElement.networkState should be NETWORK_LOADING during progress event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", tv.unreached_func());
+ v.addEventListener("progress", tv.step_func(function() {
+ assert_equals(v.networkState, v.NETWORK_LOADING);
+ tv.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - networkState during progress");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_initial.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_initial.html
new file mode 100644
index 0000000000..0a203e6542
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/networkState_initial.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video}.networkState - default state</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#dom-media-networkstate">spec reference</a></p>
+ <audio id="a">
+ </audio>
+ <video id="v">
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var a = document.getElementById("a");
+ assert_equals(
+ a.networkState,
+ a.NETWORK_EMPTY,
+ "audioElement.networkState should be NETWORK_EMPTY to begin with");
+}, "audio.networkState - default state");
+
+test(function() {
+ var v = document.getElementById("v");
+ assert_equals(
+ v.networkState,
+ v.NETWORK_EMPTY,
+ "videoElement.networkState should be NETWORK_EMPTY to begin with");
+}, "video.networkState - default state");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/currentTime.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/currentTime.html
new file mode 100644
index 0000000000..e9b6589941
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/currentTime.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>currentTime</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var v = document.createElement('video');
+ assert_equals(v.currentTime, 0);
+}, 'currentTime initial value');
+
+test(function() {
+ var v = document.createElement('video');
+ assert_equals(v.readyState, v.HAVE_NOTHING);
+ v.currentTime = Number.MAX_VALUE;
+ assert_equals(v.currentTime, Number.MAX_VALUE);
+ assert_false(v.seeking);
+}, 'setting currentTime when readyState is HAVE_NOTHING');
+
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = getVideoURI('/media/movie_5');
+ v.onloadedmetadata = t.step_func(function() {
+ assert_greater_than(v.readyState, v.HAVE_NOTHING);
+ assert_false(v.seeking);
+ v.currentTime = 1;
+ assert_true(v.seeking);
+ t.done();
+ });
+}, 'setting currentTime when readyState is greater than HAVE_NOTHING');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/duration.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/duration.html
new file mode 100644
index 0000000000..0ac26eddb9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/offsets-into-the-media-resource/duration.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>duration</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var v = document.createElement('video');
+ assert_true(isNaN(v.duration));
+}, 'duration initial value');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_false_during_play.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_false_during_play.html
new file mode 100644
index 0000000000..946deecf43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_false_during_play.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - paused property</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("audio.paused should be false during play event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("play", t.step_func(function() {
+ assert_false(a.paused);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - paused property");
+
+test(function() {
+ var t = async_test("video.paused should be false during play event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("play", t.step_func(function() {
+ assert_false(v.paused);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - paused property");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_true_during_pause.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_true_during_pause.html
new file mode 100644
index 0000000000..817615c5cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/paused_true_during_pause.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - paused property</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" controls>
+ </audio>
+ <video id="v" controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("audio.paused should be true during pause event");
+ var a = document.getElementById("a");
+ a.addEventListener("pause", function() {
+ t.step(function() {
+ assert_true(a.paused);
+ });
+
+ t.done();
+ }, false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+ a.play().catch(() => {});
+ a.pause();
+}, "audio events - paused property");
+
+test(function() {
+ var t = async_test("video.paused should be true during pause event");
+ var v = document.getElementById("v");
+ v.addEventListener("pause", function() {
+ t.step(function() {
+ assert_true(v.paused);
+ });
+
+ t.done();
+ }, false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ v.play().catch(() => {});
+ v.pause();
+}, "video events - paused property");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/pitch-detector.js b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/pitch-detector.js
new file mode 100644
index 0000000000..78f22ccd85
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/pitch-detector.js
@@ -0,0 +1,58 @@
+// This should be removed when the webaudio/historical.html tests are passing.
+// Tracking bug: https://bugs.webkit.org/show_bug.cgi?id=204719
+window.AudioContext = window.AudioContext || window.webkitAudioContext;
+
+var FFT_SIZE = 2048;
+
+var audioContext;
+var sourceNode;
+
+function getPitchDetector(media) {
+ if(!audioContext) {
+ audioContext = new AudioContext();
+ sourceNode = audioContext.createMediaElementSource(media);
+ }
+
+ var analyser = audioContext.createAnalyser();
+ analyser.fftSize = FFT_SIZE;
+
+ sourceNode.connect(analyser);
+ analyser.connect(audioContext.destination);
+
+ return {
+ ensureStart() { return audioContext.resume(); },
+ detect() { return getPitch(analyser); },
+ cleanup() {
+ sourceNode.disconnect();
+ analyser.disconnect();
+ },
+ };
+}
+
+function getPitch(analyser) {
+ // Returns the frequency value for the nth FFT bin.
+ var binConverter = (bin) =>
+ (audioContext.sampleRate/2)*((bin)/(analyser.frequencyBinCount-1));
+
+ var buf = new Uint8Array(analyser.frequencyBinCount);
+ analyser.getByteFrequencyData(buf);
+ return findDominantFrequency(buf, binConverter);
+}
+
+// Returns the dominant frequency, +/- a certain margin.
+function findDominantFrequency(buf, binConverter) {
+ var max = 0;
+ var bin = 0;
+
+ for (var i=0;i<buf.length;i++) {
+ if(buf[i] > max) {
+ max = buf[i];
+ bin = i;
+ }
+ }
+
+ // The spread of frequencies within bins is constant and corresponds to
+ // (1/(FFT_SIZE-1))th of the sample rate. Use the value of bin #1 as a
+ // shorthand for that value.
+ return { value:binConverter(bin), margin:binConverter(1) };
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html
new file mode 100644
index 0000000000..d099a8a0f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>play() with loop set to true after playback ended</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<video></video>
+<script>
+// Seek towards end of video (for faster testing).
+// Play video to end with "loop" set to false.
+// Once ended, set "loop" to true. Call play.
+// Verify that "seeked" event fires, seeking back to the beginning.
+// Pause video and end test.
+// Chromium bug: https://crbug.com/364442
+// Spec issue: https://github.com/whatwg/html/issues/4487
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ video.onloadedmetadata = t.step_func(function() {
+ // Video is initially paused and "loop" unset.
+ assert_true(video.paused, "paused initially ");
+ assert_false(video.loop, "loop initially");
+ // Seek to just before the end of the video and play.
+ video.currentTime = video.duration - 0.5;
+ video.onended = t.step_func(function() {
+ // Verify played to end and stopped.
+ assert_true(video.ended, "ended at ended event");
+ assert_true(video.paused, "paused at ended event");
+ assert_equals(video.currentTime, video.duration, "currentTime at ended event");
+
+ // With playback ended, set "loop" attribute. This will cause ended == false.
+ // looping video cannot be "ended", only paused.
+ assert_false(video.loop, "loop at ended event");
+ video.loop = true;
+ assert_true(video.loop, "loop after seek");
+ assert_false(video.ended, "ended after seek");
+ assert_true(video.paused, "paused after seek");
+
+ video.onseeked = t.step_func_done(function() {
+ // Observed seek. Verify current time decreased and still playing.
+ assert_true(video.loop, "loop at seeked event")
+ assert_false(video.paused, "paused at seeked event");
+ assert_false(video.ended, "ended at seeked event");
+ assert_less_than(video.currentTime, video.duration, "currentTime at seeked event");
+ // Pausing now that test is over to prevent additional unwanted looping.
+ video.pause();
+ });
+
+ // Play video with "loop" set. Expect seek back to start.
+ video.play();
+ });
+
+ video.play();
+ });
+
+ video.src = getVideoURI("/media/movie_5");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document.html
new file mode 100644
index 0000000000..77b4a288d8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>paused state when moving to other document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<video hidden></video>
+<iframe hidden></iframe>
+<script>
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.src = getVideoURI('/media/movie_300');
+ v.play();
+ v.onplaying = t.step_func(function() {
+ assert_false(v.paused, 'paused after playing');
+ document.querySelector('iframe').contentDocument.body.appendChild(v);
+ assert_false(v.paused, 'paused after moving');
+ t.step_timeout(function() {
+ assert_false(v.paused, 'paused after stable state')
+ t.done();
+ }, 0);
+ });
+ v.onpause = t.step_func(function() { assert_unreached(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document.html
new file mode 100644
index 0000000000..911aa7b5c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>paused state when moving within a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<video hidden></video>
+<div id="elsewhere"></div>
+<script>
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.src = getVideoURI('/media/movie_300');
+ v.play();
+ v.onplaying = t.step_func(function() {
+ assert_false(v.paused, 'paused after playing');
+ document.getElementById('elsewhere').appendChild(v);
+ assert_false(v.paused, 'paused after moving');
+ t.step_timeout(function() {
+ assert_false(v.paused, 'paused after stable state')
+ t.done();
+ }, 0);
+ });
+ v.onpause = t.step_func(function() { assert_unreached(); });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-different-load.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-different-load.html
new file mode 100644
index 0000000000..4802665cdd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-different-load.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>paused state when removing from a document</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1583052">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<div>
+ <video hidden></video>
+</div>
+<script>
+function afterStableState(func) {
+ var a = new Audio();
+ a.volume = 0;
+ a.addEventListener('volumechange', func);
+}
+
+async_test(function(t) {
+ var v = document.querySelector('video');
+
+ // Much like pause-remove-from-document.html, modulo this call.
+ document.body.appendChild(v);
+
+ v.src = getVideoURI('/media/movie_300');
+ v.play();
+ v.onplaying = t.step_func(function() {
+ assert_false(v.paused, 'paused after playing');
+ v.parentNode.removeChild(v);
+ assert_false(v.paused, 'paused after removing');
+ afterStableState(t.step_func(function() {
+ assert_true(v.paused, 'paused after stable state');
+ v.onpause = t.step_func(function() {
+ assert_true(v.paused, 'paused in pause event');
+ // re-insert and verify that it stays paused
+ document.body.appendChild(v);
+ t.step_timeout(function() {
+ assert_true(v.paused, 'paused after re-inserting');
+ t.done();
+ }, 0);
+ });
+ }));
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-networkState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-networkState.html
new file mode 100644
index 0000000000..5140ea5611
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-networkState.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>paused state when removing from a document when networkState is NETWORK_EMPTY</title>
+<meta name="timeout" content="long" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<video hidden></video>
+<script>
+// Negative test for the specified behavior prior to HTML r8447.
+promise_test(async function(t) {
+ var v = document.querySelector('video');
+ var watcher = new EventWatcher(t, v, [ 'pause' ]);
+ var p = v.play();
+
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ assert_equals(v.networkState, v.NETWORK_EMPTY,
+ 'networkState after stable state');
+ assert_false(v.paused, 'paused after stable state');
+ v.parentNode.removeChild(v);
+ assert_false(v.paused, 'paused after removing');
+
+ await watcher.wait_for('pause');
+
+ await promise_rejects_dom(t, 'AbortError', p, 'We expect promise being rejected');
+ assert_true(v.paused, 'paused after removing and stable state');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document.html
new file mode 100644
index 0000000000..5425844037
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>paused state when removing from a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<video hidden></video>
+<script>
+function afterStableState(func) {
+ var a = new Audio();
+ a.volume = 0;
+ a.addEventListener('volumechange', func);
+}
+
+async_test(function(t) {
+ var v = document.querySelector('video');
+ v.src = getVideoURI('/media/movie_300');
+ v.play();
+ v.onplaying = t.step_func(function() {
+ assert_false(v.paused, 'paused after playing');
+ v.parentNode.removeChild(v);
+ assert_false(v.paused, 'paused after removing');
+ afterStableState(t.step_func(function() {
+ assert_true(v.paused, 'paused after stable state');
+ v.onpause = t.step_func(function() {
+ assert_true(v.paused, 'paused in pause event');
+ // re-insert and verify that it stays paused
+ document.body.appendChild(v);
+ t.step_timeout(function() {
+ assert_true(v.paused, 'paused after re-inserting');
+ t.done();
+ }, 0);
+ });
+ }));
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/play-in-detached-document.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/play-in-detached-document.html
new file mode 100644
index 0000000000..8e9a7843b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/play-in-detached-document.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>play() in detached document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<script>
+// Negative test for failure to play in a detached document.
+async_test(function(t)
+{
+ var doc = document.implementation.createHTMLDocument("");
+ var v = doc.createElement("video");
+ doc.body.appendChild(v);
+ v.src = getVideoURI("/media/movie_5");
+ v.play().catch(() => {});
+
+ v.addEventListener("timeupdate", t.step_func(function() {
+ assert_false(v.paused);
+ if (v.currentTime > 0) {
+ t.done();
+ }
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/playbackRate.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/playbackRate.html
new file mode 100644
index 0000000000..d8e14b5fc1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/playbackRate.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<title>playbackRate</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var v = document.createElement('video');
+ assert_equals(v.playbackRate, 1);
+}, 'playbackRate initial value');
+
+function testPlaybackRateHelper(t, newPlaybackRate) {
+ var v = document.createElement('video');
+ var initialRate = v.playbackRate;
+
+ v.addEventListener('ratechange', t.step_func_done(function() {
+ assert_equals(v.playbackRate, newPlaybackRate);
+ }));
+
+ try {
+ v.playbackRate = newPlaybackRate;
+ } catch(e) {
+ assert_equals(e.name, 'NotSupportedError');
+ assert_equals(v.playbackRate, initialRate);
+ t.done();
+ }
+}
+
+async_test(function(t) {
+ testPlaybackRateHelper(this, 3);
+}, "playbackRate set to small positive value");
+
+async_test(function(t) {
+ testPlaybackRateHelper(this, 100);
+}, "playbackRate set to large positive value");
+
+async_test(function(t) {
+ testPlaybackRateHelper(this, -3);
+}, "playbackRate set to small negative value");
+
+async_test(function(t) {
+ testPlaybackRateHelper(this, -100);
+}, "playbackRate set to large negative value");
+
+async_test(function(t) {
+ testPlaybackRateHelper(this, 0);
+}, "playbackRate set to 0");
+
+async_test(function(t) {
+ testPlaybackRateHelper(this, -1);
+}, "playbackRate set to -1");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/preload_reflects_none_autoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/preload_reflects_none_autoplay.html
new file mode 100644
index 0000000000..2670b0dd81
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/preload_reflects_none_autoplay.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video}.preload - reflection test</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#dom-media-preload">spec reference</a></p>
+ <audio id="audio" autoplay preload="none">
+ </audio>
+ <video id="video" autoplay preload="none">
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ assert_equals(
+ document.getElementById("audio").preload,
+ "none",
+ "audioElement.preload reflects 'none' value even if autoplay attribute is present");
+}, "audio.preload - reflection test");
+
+test(function() {
+ assert_equals(
+ document.getElementById("video").preload,
+ "none",
+ "videoElement.preload reflects 'none' value even if autoplay attribute is present");
+}, "video.preload - reflection test");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/preserves-pitch.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/preserves-pitch.html
new file mode 100644
index 0000000000..ba76f51d47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/preserves-pitch.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<title>Test preservesPitch.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="pitch-detector.js"></script>
+<script>
+
+// Remove when media-elements/historical.html's preservePitch prefix tests are are passing.
+function getPreservesPitch(audio) {
+ if ("preservesPitch" in HTMLAudioElement.prototype) {
+ return audio.preservesPitch;
+ }
+ if ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
+ return audio.webkitPreservesPitch;
+ }
+ return undefined;
+}
+
+// Remove when media-elements/historical.html's preservePitch prefix tests are are passing.
+function setPreservesPitch(audio, value) {
+ if ("preservesPitch" in HTMLAudioElement.prototype) {
+ audio.preservesPitch = value;
+ } else if ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
+ audio.webkitPreservesPitch = value;
+ }
+}
+
+test(function(t) {
+ assert_true("preservesPitch" in HTMLAudioElement.prototype);
+}, "Test that preservesPitch is present and unprefixed.");
+
+test(function(t) {
+ let defaultAudio = document.createElement('audio');
+ assert_true(getPreservesPitch(defaultAudio));
+
+ setPreservesPitch(defaultAudio, false);
+ assert_false(getPreservesPitch(defaultAudio));
+}, "Test that preservesPitch is on by default");
+
+
+var audio;
+
+function addTestCleanups(t, detector) {
+ t.add_cleanup(() => {
+ audio.pause();
+ audio.currentTime = 0;
+ });
+ t.add_cleanup(() => detector.cleanup());
+}
+
+function testPreservesPitch(preservesPitch, playbackRate, expectedPitch, description) {
+ promise_test(async t => {
+ let detector = getPitchDetector(audio);
+ addTestCleanups(t, detector);
+
+ audio.playbackRate = playbackRate;
+ setPreservesPitch(audio, preservesPitch);
+
+ function waitUntil(time) {
+ return new Promise((resolve) => {
+ audio.ontimeupdate = () => {
+ if (audio.currentTime >= time) {
+ resolve();
+ }
+ };
+ });
+ }
+
+ // Wait until we have played some audio. Otherwise, the detector
+ // might return a pitch of 0Hz.
+ audio.play();
+ await waitUntil(0.5);
+
+ var pitch = detector.detect();
+
+ // 25Hz is larger than the margin we get from 48kHz and 44.1kHz
+ // audio being analyzed by a FFT of size 2048. If we get something
+ // different, there is an error within the test's calculations (or
+ // we might be dealing a larger sample rate).
+ assert_less_than(pitch.margin, 25,
+ "Test error: the margin should be reasonably small.")
+
+ // Allow for a 15% margin of error in the pitch detector, to reduce test
+ // flakiness. Since our tests speed up and slow down by a factor of 2,
+ // this should be plenty of leeway, without causing false negatives.
+ assert_approx_equals(pitch.value, expectedPitch, expectedPitch*0.15,
+ "The actual pitch should be close to the expected pitch.");
+
+ }, description);
+}
+
+var REFERENCE_PITCH = 440;
+
+promise_test(async t => {
+ // Create the audio element only once, in order to lower the chances of
+ // tests timing out.
+ audio = document.createElement('audio');
+
+ // This file contains 5 seconds of a 440hz sine wave.
+ audio.src = "/media/sine440.mp3";
+
+ let detector = getPitchDetector(audio);
+ addTestCleanups(t, detector);
+
+ // The first time we run the test, we need to interact with the
+ // AudioContext and Audio element via user gestures.
+ await test_driver.bless("Play audio element", () => {
+ return Promise.all([audio.play(), detector.ensureStart()]);
+ });
+}, "Setup Audio element and AudioContext")
+
+testPreservesPitch(true, 1.0, REFERENCE_PITCH,
+ "The default playbackRate should not affect pitch")
+
+testPreservesPitch(false, 1.0, REFERENCE_PITCH,
+ "The default playbackRate should not affect pitch, even with preservesPitch=false")
+
+testPreservesPitch(true, 2.0, REFERENCE_PITCH,
+ "Speed-ups should not change the pitch when preservesPitch=true")
+
+testPreservesPitch(true, 0.5, REFERENCE_PITCH,
+ "Slow-downs should not change the pitch when preservesPitch=true")
+
+testPreservesPitch(false, 2.0, REFERENCE_PITCH*2.0,
+ "Speed-ups should change the pitch when preservesPitch=false")
+
+testPreservesPitch(false, 0.5, REFERENCE_PITCH*0.5,
+ "Slow-downs should change the pitch when preservesPitch=false")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html
new file mode 100644
index 0000000000..16c6e29be9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>autoplay hidden</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/media.html#ready-states"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<script>
+
+// https://html.spec.whatwg.org/multipage/media.html#ready-states:eligible-for-autoplay-2
+
+promise_test(async t => {
+ let video = document.createElement("video");
+ video.src = getVideoURI("/media/movie_5");
+ video.autoplay = true;
+ // In Safari, Chrome and Firefox, the video needs to be muted in order to be
+ // paused when hidden. They decided to do this in order to save resources when
+ // a video goes out of view and isn't expected to make any sound.
+ video.muted = true;
+ video.loop = true;
+ let watcher = new EventWatcher(t, video, ["playing", "pause"]);
+ document.body.appendChild(video);
+
+ await watcher.wait_for("playing");
+ assert_false(video.paused, "paused when video is display");
+ video.hidden = true;
+
+ await watcher.wait_for("pause");
+ assert_true(video.paused, "paused when video is hidden");
+ video.hidden = false;
+
+ await watcher.wait_for("playing");
+ assert_false(video.paused, "paused when video is display");
+}, "Allow delaying autoplay until video elements become visible");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-with-slow-text-tracks.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-with-slow-text-tracks.html
new file mode 100644
index 0000000000..930d9cbd5b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay-with-slow-text-tracks.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<title>autoplay with slow text tracks</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<script>
+setup({ single_test: true });
+// https://html.spec.whatwg.org/#ready-states says:
+//
+// HAVE_FUTURE_DATA: "the text tracks are ready".
+// HAVE_ENOUGH_DATA: All the conditions described for the HAVE_FUTURE_DATA state are met, ...
+//
+// When the ready state of a media element whose networkState is not NETWORK_EMPTY changes,
+// the user agent must follow the steps given below:
+// If the new ready state is HAVE_ENOUGH_DATA
+// (autoplay)
+//
+// So if the text tracks are not yet ready, we can't autoplay.
+
+var started = 0;
+var numOfTests = 5;
+
+function createTest() {
+ var video = document.createElement('video');
+ video.src = getVideoURI('/media/movie_5');
+ video.autoplay = true;
+ video.muted = true;
+ video.controls = true;
+ video.onplaying = function() {
+ started++;
+ assert_equals(track.track.cues.length, 1);
+ if (started === numOfTests) {
+ done();
+ }
+ };
+ var track = document.createElement('track');
+ track.src = '/media/foo.vtt?pipe=trickle(d2)';
+ track.default = true;
+ video.appendChild(track);
+ document.body.appendChild(video);
+}
+for (var i = 0; i < numOfTests; ++i) {
+ createTest();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay.html
new file mode 100644
index 0000000000..b60b58a421
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/ready-states/autoplay.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<title>autoplay</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
+<script>
+function autoplay_test(tagName, src) {
+ function expect_events(t, e, expected_events) {
+ var actual_events = [];
+ var callback = t.step_func(function(ev) {
+ actual_events.push(ev.type);
+ assert_array_equals(actual_events,
+ expected_events.slice(0, actual_events.length));
+ if (expected_events.length == actual_events.length) {
+ t.done();
+ }
+ });
+ ['canplay', 'canplaythrough',
+ 'pause', 'play', 'playing', 'error'].forEach(function(type) {
+ e.addEventListener(type, callback);
+ });
+ }
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = src;
+ e.autoplay = true;
+ expect_events(t, e, ['canplay', 'play', 'playing', 'canplaythrough']);
+ }, tagName + '.autoplay');
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = src;
+ e.autoplay = true;
+ e.pause(); // sets the autoplaying flag to false
+ e.load(); // sets the autoplaying flag to true
+ expect_events(t, e, ['canplay', 'play', 'playing', 'canplaythrough']);
+ }, tagName + '.autoplay and load()');
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = src;
+ e.autoplay = true;
+ e.play(); // sets the autoplaying flag to false
+ // play() also sets the paused attribute to false; there is no way for the
+ // autoplaying flag to be true when the paused attribute is false.
+ assert_equals(e.paused, false);
+ expect_events(t, e, ['play', 'canplay', 'playing', 'canplaythrough']);
+ }, tagName + '.autoplay and play()');
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = src;
+ e.autoplay = true;
+ e.pause(); // sets the autoplaying flag to false
+ expect_events(t, e, ['canplay', 'canplaythrough']);
+ }, tagName + '.autoplay and pause()');
+
+ async_test(function(t) {
+ var e = document.createElement(tagName);
+ e.src = src;
+ e.autoplay = true;
+ document.body.appendChild(e);
+ document.body.removeChild(e);
+ // in stable state, internal pause steps sets the autoplaying flag to false
+ expect_events(t, e, ['canplay', 'canplaythrough']);
+ }, tagName + '.autoplay and internal pause steps');
+}
+
+autoplay_test('audio', getAudioURI('/media/sound_5'));
+autoplay_test('video', getVideoURI('/media/movie_5'));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplay.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplay.html
new file mode 100644
index 0000000000..358a87fe21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplay.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - readyState property during canplay</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("audio.readyState should be >= HAVE_FUTURE_DATA during canplay event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplay", t.step_func(function() {
+ assert_greater_than_equal(a.readyState, a.HAVE_FUTURE_DATA);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - readyState property during canplay");
+
+test(function() {
+ var t = async_test("video.readyState should be >= HAVE_FUTURE_DATA during canplay event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplay", t.step_func(function() {
+ assert_greater_than_equal(v.readyState, v.HAVE_FUTURE_DATA);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - readyState property during canplay");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplaythrough.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplaythrough.html
new file mode 100644
index 0000000000..2721d18633
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_canplaythrough.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - readyState property during canplaythrough</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("audio.readyState should be HAVE_ENOUGH_DATA during canplaythrough event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("canplaythrough", t.step_func(function() {
+ assert_equals(a.readyState, a.HAVE_ENOUGH_DATA);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - readyState property during canplaythrough");
+
+test(function() {
+ var t = async_test("video.readyState should be HAVE_ENOUGH_DATA during canplaythrough event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("canplaythrough", t.step_func(function() {
+ assert_equals(v.readyState, v.HAVE_ENOUGH_DATA);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - readyState property during canplaythrough");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadeddata.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadeddata.html
new file mode 100644
index 0000000000..f237b1fbd3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadeddata.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - readyState property during loadeddata</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("audio.readyState should be >= HAVE_CURRENT_DATA during loadeddata event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadeddata", t.step_func(function() {
+ assert_greater_than_equal(a.readyState, a.HAVE_CURRENT_DATA);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - readyState property during loadeddata");
+
+test(function() {
+ var t = async_test("video.readyState should be >= HAVE_CURRENT_DATA during loadeddata event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadeddata", t.step_func(function() {
+ assert_greater_than_equal(v.readyState, v.HAVE_CURRENT_DATA);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - readyState property during loadeddata");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadedmetadata.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadedmetadata.html
new file mode 100644
index 0000000000..73f33f0b98
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_loadedmetadata.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - readyState property during loadedmetadata</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("audio.readyState should be >= HAVE_METADATA during loadedmetadata event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("loadedmetadata", t.step_func(function() {
+ assert_greater_than_equal(a.readyState, a.HAVE_METADATA);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - readyState property during loadedmetadata");
+
+test(function() {
+ var t = async_test("video.readyState should be >= HAVE_METADATA during loadedmetadata event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("loadedmetadata", t.step_func(function() {
+ assert_greater_than_equal(v.readyState, v.HAVE_METADATA);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - readyState property during loadedmetadata");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_playing.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_playing.html
new file mode 100644
index 0000000000..663bad701b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_during_playing.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video} events - readyState property during playing</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#mediaevents">spec reference</a></p>
+ <audio id="a" autoplay controls>
+ </audio>
+ <video id="v" autoplay controls>
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var t = async_test("audio.readyState should be >= HAVE_FUTURE_DATA during playing event");
+ var a = document.getElementById("a");
+ a.addEventListener("error", t.unreached_func());
+ a.addEventListener("playing", t.step_func(function() {
+ assert_greater_than_equal(a.readyState, a.HAVE_FUTURE_DATA);
+ t.done();
+ a.pause();
+ }), false);
+ a.src = getAudioURI("/media/sound_5") + "?" + new Date() + Math.random();
+}, "audio events - readyState property during playing");
+
+test(function() {
+ var t = async_test("video.readyState should be >= HAVE_FUTURE_DATA during playing event");
+ var v = document.getElementById("v");
+ v.addEventListener("error", t.unreached_func());
+ v.addEventListener("playing", t.step_func(function() {
+ assert_greater_than_equal(v.readyState, v.HAVE_FUTURE_DATA);
+ t.done();
+ v.pause();
+ }), false);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+}, "video events - readyState property during playing");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_initial.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_initial.html
new file mode 100644
index 0000000000..e9c112bd24
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/readyState_initial.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video}.readyState - default state</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#dom-media-networkstate">spec reference</a></p>
+ <audio id="a">
+ </audio>
+ <video id="v">
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ var a = document.getElementById("a");
+ assert_equals(
+ a.readyState,
+ a.HAVE_NOTHING,
+ "audioElement.readyState should be HAVE_NOTHING to begin with");
+}, "audio.readyState - default state");
+
+test(function() {
+ var v = document.getElementById("v");
+ assert_equals(
+ v.readyState,
+ v.HAVE_NOTHING,
+ "videoElement.readyState should be HAVE_NOTHING to begin with");
+}, "video.readyState - default state");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-currentTime.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-currentTime.html
new file mode 100644
index 0000000000..82b27bf87d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-currentTime.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>seek to currentTime</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = getVideoURI('/media/movie_5');
+ v.onloadedmetadata = t.step_func(function() {
+ assert_greater_than(v.readyState, v.HAVE_NOTHING, 'readyState');
+ assert_greater_than(v.seekable.length, 0, 'seekable ranges');
+ assert_false(v.seeking, 'seeking before setting currentTime');
+ v.currentTime = v.currentTime;
+ assert_true(v.seeking, 'seeking after setting currentTime');
+ var events = [];
+ v.onseeking = v.ontimeupdate = v.onseeked = t.step_func(function(e) {
+ events.push(e.type);
+ // v.seeking can be true or false in the seeking event, see
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=24774
+ if (e.type != 'seeking') {
+ assert_equals(v.seeking, false, 'seeking in ' + e.type + ' event');
+ }
+ if (e.type == 'seeked') {
+ assert_array_equals(events, ['seeking', 'timeupdate', 'seeked'],
+ 'fired events');
+ t.done();
+ }
+ });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-max-value.htm b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-max-value.htm
new file mode 100644
index 0000000000..a31f6c07ab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-max-value.htm
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>seek to Number.MAX_VALUE</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = getVideoURI('/media/movie_5');
+ v.onloadedmetadata = t.step_func(function() {
+ assert_equals(v.seekable.length, 1);
+ v.currentTime = Number.MAX_VALUE;
+ assert_true(v.seeking, 'seeking after setting');
+ assert_equals(v.currentTime, v.seekable.end(0), 'currentTime after setting');
+ v.onseeked = t.step_func(function(e) {
+ assert_false(v.seeking, 'seeking in seeked event');
+ assert_equals(v.currentTime, v.seekable.end(0), 'currentTime in seeked event');
+ t.done();
+ });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-negative-time.htm b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-negative-time.htm
new file mode 100644
index 0000000000..56a99028de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/seeking/seek-to-negative-time.htm
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>seek to negative time</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var v = document.createElement('video');
+ v.src = getVideoURI('/media/movie_5');
+ v.onloadedmetadata = t.step_func(function() {
+ assert_equals(v.seekable.start(0), 0, 'earliest seekable time');
+ v.currentTime = -1;
+ assert_true(v.seeking, 'seeking after setting');
+ assert_equals(v.currentTime, 0, 'currentTime after setting');
+ v.onseeked = t.step_func(function(e) {
+ assert_false(v.seeking, 'seeking in seeked event');
+ assert_equals(v.currentTime, 0, 'currentTime in seeked event');
+ t.done();
+ });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_object_blob.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_object_blob.html
new file mode 100644
index 0000000000..ae2bb76b26
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_object_blob.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTMLMediaElement.srcObject blob</title>
+<script src='/common/media.js'></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<video></video>
+<script>
+ const video = document.querySelector("video");
+ promise_test(async () => {
+ const blob = await fetch(getVideoURI('/media/movie_5'))
+ .then(r => r.blob());
+ try {
+ video.srcObject = blob;
+ } catch (error) {
+ assert_unreached(error);
+ }
+ const done = new Promise(res => video.addEventListener('ended', res));
+ test_driver.bless('initiate media playback', function () {
+ video.play();
+ });
+ return done;
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_reflects_attribute_not_source_elements.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_reflects_attribute_not_source_elements.html
new file mode 100644
index 0000000000..3dd43cc3f5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/src_reflects_attribute_not_source_elements.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+ <head>
+ <title>{audio,video}.src - reflection test</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p><a href="https://html.spec.whatwg.org/multipage/#dom-media-src">spec reference</a></p>
+ <audio id="audio" src="foo">
+ <source src="barbaz" />
+ </audio>
+ <video id="video" src="foo">
+ <source src="barbaz" />
+ </video>
+ <div id="log"></div>
+ <script>
+test(function() {
+ assert_equals(
+ document.getElementById("audio").src.indexOf("barbaz"),
+ -1,
+ "audioElement.src should reflect src attribute, not source child elements");
+}, "audio.src - reflection test");
+
+test(function() {
+ assert_equals(
+ document.getElementById("video").src.indexOf("barbaz"),
+ -1,
+ "videoElement.src should reflect src attribute, not source child elements");
+}, "video.src - reflection test");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cloneNode.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cloneNode.html
new file mode 100644
index 0000000000..9e0f0bf900
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cloneNode.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<title>track element cloneNode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var elm = document.createElement('track');
+ assert_equals(elm.readyState, elm.NONE, 'elm.readyState after element creation');
+ var clone = elm.cloneNode(true);
+ assert_equals(clone.readyState, clone.NONE, 'clone.readyState after element creation');
+ assert_not_equals(clone.track, elm.track, 'clone.track and elm.track');
+}, document.title+', not loaded');
+
+async_test(function(t) {
+ var elm = document.createElement('track');
+ var video = document.createElement('video');
+ video.appendChild(elm);
+ elm.track.mode = 'showing';
+ assert_equals(elm.readyState, elm.NONE, 'elm.readyState after appening to video setting mode');
+ elm.src = 'resources/track.vtt?pipe=trickle(d1)';
+ assert_equals(elm.readyState, elm.NONE, 'elm.readyState after setting src');
+ t.step_timeout(function() {
+ assert_equals(elm.readyState, elm.LOADING, 'elm.readyState in setTimeout');
+ var clone = elm.cloneNode(true);
+ assert_equals(clone.readyState, clone.NONE, 'clone.readyState after element creation');
+ video.appendChild(clone);
+ clone.track.mode = 'showing';
+ assert_equals(clone.readyState, clone.NONE, 'clone.readyState after appending to video and setting mode');
+ assert_not_equals(clone.track, elm.track, 'clone.track and elm.track');
+ t.done();
+ }, 0);
+}, document.title+', loading');
+
+async_test(function(t) {
+ var elm = document.createElement('track');
+ var video = document.createElement('video');
+ video.appendChild(elm);
+ elm.track.mode = 'showing';
+ elm.src = 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\n00:00:00.000 --> 00:00:01.000\nfoo');
+ assert_equals(elm.readyState, elm.NONE, 'elm.readyState after setting src');
+ elm.onload = this.step_func(function() {
+ assert_equals(elm.readyState, elm.LOADED, 'elm.readyState');
+ assert_equals(elm.track.cues.length, 1, 'elm.track.cues.length');
+ assert_equals(elm.track.cues[0].startTime, 0, 'elm.track.cues[0].startTime');
+ assert_equals(elm.track.cues[0].endTime, 1, 'elm.track.cues[0].endTime');
+ assert_equals(elm.track.cues[0].text, 'foo', 'elm.track.cues[0].text');
+ var clone = elm.cloneNode(true);
+ assert_equals(clone.readyState, clone.NONE, 'clone.readyState after element creation');
+ video.appendChild(clone);
+ clone.track.mode = 'showing';
+ assert_equals(clone.readyState, clone.NONE, 'clone.readyState after appending to video and setting mode');
+ assert_not_equals(clone.track, elm.track, 'clone.track and elm.track');
+ clone.onload = this.step_func(function(){
+ assert_equals(clone.readyState, clone.LOADED, 'clone.readyState');
+ assert_not_equals(clone.track, elm.track, 'clone.track and elm.track');
+ assert_not_equals(clone.track.cues, elm.track.cues, 'clone.track.cues and elm.track.cues');
+ assert_equals(clone.track.cues.length, 1, 'clone.track.cues.length');
+ assert_not_equals(clone.track.cues[0], elm.track.cues[0], 'cues[0]');
+ assert_equals(clone.track.cues[0].startTime, 0, 'clone.track.cues[0].startTime');
+ assert_equals(clone.track.cues[0].endTime, 1, 'clone.track.cues[0].endTime');
+ assert_equals(clone.track.cues[0].text, 'foo', 'clone.track.cues[0].text');
+ this.done();
+ });
+ clone.onerror = this.step_func(function() { assert_unreached('clone got error'); });
+ });
+ elm.onerror = this.step_func(function() { assert_unreached('elm got error'); });
+}, document.title+', loaded');
+
+async_test(function(t) {
+ var elm = document.createElement('track');
+ var video = document.createElement('video');
+ video.appendChild(elm);
+ elm.track.mode = 'showing';
+ elm.onerror = t.step_func(function() {
+ assert_equals(elm.readyState, elm.ERROR, 'elm.readyState in onerror');
+ var clone = elm.cloneNode(true);
+ assert_equals(clone.readyState, clone.NONE, 'clone.readyState after element creation');
+ video.appendChild(clone);
+ clone.track.mode = 'showing';
+ assert_equals(clone.readyState, clone.NONE, 'clone.readyState after appending to video and setting mode');
+ assert_not_equals(clone.track, elm.track, 'clone.track and elm.track');
+ clone.onerror = t.step_func_done();
+ });
+ elm.src = 'javascript:"network error"';
+}, document.title+', failed to load');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/003.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/003.html
new file mode 100644
index 0000000000..4236df29b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/003.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: No CORS, same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/004.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/004.html
new file mode 100644
index 0000000000..4f86d011a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/004.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: No CORS, same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/005.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/005.html
new file mode 100644
index 0000000000..e6a693400c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/005.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/006.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/006.html
new file mode 100644
index 0000000000..351b97d677
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/006.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/007.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/007.html
new file mode 100644
index 0000000000..4ccc6b66ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/007.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/008.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/008.html
new file mode 100644
index 0000000000..0444a83085
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/008.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/009.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/009.html
new file mode 100644
index 0000000000..dd62232755
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/009.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: No CORS, not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/010.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/010.html
new file mode 100644
index 0000000000..d75d6f4d6d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/010.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/011.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/011.html
new file mode 100644
index 0000000000..6d0fae6de7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/011.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/012.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/012.html
new file mode 100644
index 0000000000..110497b494
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/012.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/013.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/013.html
new file mode 100644
index 0000000000..d2a9ddb193
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/013.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/014.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/014.html
new file mode 100644
index 0000000000..a1d6a8b295
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/014.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: No CORS, same-origin, no headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/015.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/015.html
new file mode 100644
index 0000000000..2850a24e17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/015.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: No CORS, same-origin, with headers, redirects to same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/016.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/016.html
new file mode 100644
index 0000000000..5cd5a85d43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/016.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/017.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/017.html
new file mode 100644
index 0000000000..0ec5bc3291
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/017.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers, redirects to same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/018.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/018.html
new file mode 100644
index 0000000000..f639d043a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/018.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, no headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/019.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/019.html
new file mode 100644
index 0000000000..45e1291c92
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/019.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, with headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/020.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/020.html
new file mode 100644
index 0000000000..e1153b6813
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/020.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, no headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'no'}]}; // redirect not followed
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/021.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/021.html
new file mode 100644
index 0000000000..ec2e9d8bb4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/021.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, with headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'no'}, {cors:'null', cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/022.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/022.html
new file mode 100644
index 0000000000..e8fb0c3d43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/022.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, with headers, redirects to same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:origin, cookie:'no'}, {cors:'null', cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/023.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/023.html
new file mode 100644
index 0000000000..ac9bb35465
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/023.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, no headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'yes'}]}; // redirect not followed
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/024.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/024.html
new file mode 100644
index 0000000000..302340022d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/024.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, with headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'yes'}, {cors:'null', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/025.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/025.html
new file mode 100644
index 0000000000..5cbe8528e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/025.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, with headers, redirects to same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:origin, cookie:'yes'}, {cors:'null', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/026.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/026.html
new file mode 100644
index 0000000000..c8386ffff3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/026.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: No CORS, same-origin, with headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}]}; // redirect not followed
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/027.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/027.html
new file mode 100644
index 0000000000..5fe4760e66
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/027.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers, redirects to not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/028.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/028.html
new file mode 100644
index 0000000000..6019d37b63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/028.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, with headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/029.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/029.html
new file mode 100644
index 0000000000..7fa85456de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/029.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, no headers, redirects to not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/030.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/030.html
new file mode 100644
index 0000000000..f7abf3b1ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/030.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, with headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/031.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/031.html
new file mode 100644
index 0000000000..d709d0bc42
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/031.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, no headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'no'}]}; // redirect not followed
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/032.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/032.html
new file mode 100644
index 0000000000..62b1008a41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/032.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, with headers, redirects to not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'no'}, {cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/033.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/033.html
new file mode 100644
index 0000000000..215cae2419
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/033.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, not same-origin, with headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:origin, cookie:'no'}, {cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/034.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/034.html
new file mode 100644
index 0000000000..bebb43ba8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/034.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, no headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'yes'}]}; // redirect not followed
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/035.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/035.html
new file mode 100644
index 0000000000..a17fb7dfc1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/035.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, with headers, redirects to not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:origin, cookie:'yes'}, {cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/036.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/036.html
new file mode 100644
index 0000000000..52411177ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/036.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, not same-origin, with headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:origin, cookie:'yes'}, {cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/037.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/037.html
new file mode 100644
index 0000000000..675b913a13
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/037.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers, redirects to not same-origin, no headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'no'}]}; // second redirect not followed
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/038.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/038.html
new file mode 100644
index 0000000000..a29b2bdead
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/038.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, with headers, redirects to not same-origin, with headers, redirects to same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'no'}, {cors:'null', cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/039.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/039.html
new file mode 100644
index 0000000000..fcd4871ddb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/039.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers, redirects to not same-origin, with headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'no'}, {cors:'null', cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/040.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/040.html
new file mode 100644
index 0000000000..3c819684c4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/040.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, no headers, redirects to not same-origin, no headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'yes'}]}; // second redirect not followed
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/041.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/041.html
new file mode 100644
index 0000000000..f0f81953fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/041.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, with headers, redirects to not same-origin, with headers, redirects to same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'yes'}, {cors:'null', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/042.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/042.html
new file mode 100644
index 0000000000..c1ffa5f1ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/042.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, no headers, redirects to not same-origin, with headers, redirects to same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:origin, cookie:'yes'}, {cors:'null', cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/043.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/043.html
new file mode 100644
index 0000000000..09072a9895
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/043.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}, {cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/044.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/044.html
new file mode 100644
index 0000000000..0d4a9fefbd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/044.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Anonymous, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}, {cors:origin, cookie:'no'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/045.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/045.html
new file mode 100644
index 0000000000..7151364f9c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/045.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, no headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'error', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}, {cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/046.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/046.html
new file mode 100644
index 0000000000..e286462814
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/046.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>track CORS: Use Credentials, same-origin, no headers, redirects to same-origin, no headers, redirects to not same-origin, with headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script src=/common/utils.js></script>
+<script src=support/common.js?pipe=sub></script>
+<script>
+var expected = {event:'load', requests:[{cors:'no', cookie:'yes'}, {cors:'no', cookie:'yes'}, {cors:origin, cookie:'yes'}]};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/common.js b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/common.js
new file mode 100644
index 0000000000..e30c627149
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/common.js
@@ -0,0 +1,144 @@
+setup(function(){
+ window.id = token();
+ var p = document.createElement('p');
+ p.innerHTML = 'Test id: <samp>'+id+'</samp>';
+ document.body.appendChild(p);
+ window.actual = {event:null, requests:[]};
+ window.errors = [];
+ window.origin = location.protocol+'//'+location.host;
+ window.escapedOrigin = encodeURIComponent(origin);
+ window.sameOriginURL = "http://{{domains[]}}:{{ports[http][0]}}" + location.pathname.replace(/\/[^\/]+$/, '/');
+ window.otherOriginURL = "http://{{domains[www1]}}:{{ports[http][0]}}" + location.pathname.replace(/\/[^\/]+$/, '/');
+}, {timeout:10000, explicit_done:true});
+
+onload = function() {
+ (async_test()).step(function() {
+ // fail early if track isn't supported
+ assert_true('HTMLTrackElement' in window, 'track not supported');
+ window.corsMode = document.title.match(/^track CORS: (No CORS|Anonymous|Use Credentials)/)[1];
+ var requests_tmp = document.title.substr(('track CORS: '+corsMode+', ').length).split(/, redirects to /g);
+ window.requests = [];
+ requests_tmp.forEach(function(r) {
+ var parts = r.split(', ');
+ requests.push({sameOrigin:parts[0] == 'same-origin', withHeaders:parts[1] == 'with headers'});
+ });
+ if (document.title.indexOf('not same-origin') > -1) {
+ window.hasCrossDomainCookie = true;
+ this.step(setCrossDomainCookie);
+ } else {
+ window.hasCrossDomainCookie = false;
+ this.step(loadTrack);
+ }
+ });
+ done();
+};
+
+function setCrossDomainCookie() {
+ var iframe = document.createElement('iframe');
+ iframe.onload = this.step_func(loadTrack);
+ iframe.src = otherOriginURL + 'support/set-cookie.html#'+id;
+ document.body.appendChild(iframe);
+}
+
+function loadTrack() {
+ var video = document.createElement('video');
+ window.track = document.createElement('track');
+ if (corsMode == 'Anonymous')
+ video.setAttribute('crossorigin', 'anonymous');
+ else if (corsMode == 'Use Credentials')
+ video.setAttribute('crossorigin', 'use-credentials');
+ // else No CORS, omit the crossorigin attribute
+ video.appendChild(track);
+ document.body.appendChild(video);
+ track.track.mode = 'showing';
+ document.cookie = id+'=yes;path=/;max-age=10';
+ var url = '';
+ var r;
+ while (r = requests.pop()) {
+ url = (r.sameOrigin ? sameOriginURL : otherOriginURL) +
+ 'support/cors-tester.py?id=' + id +
+ (r.withHeaders ? '&origin=' + escapedOrigin : '') +
+ (url === '' ? '' : '&redirect=' + encodeURIComponent(url));
+ }
+ track.src = url;
+ track.onerror = track.onload = this.step_func(function(e) {
+ actual.event = e.type;
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'support/cors-tester.py?read=true&id=' + id, true);
+ xhr.onload = this.step_func(function() {
+ if (xhr.status == 200) {
+ var lines = xhr.responseText.split('\n');
+ lines.forEach(function(line) {
+ var chunks = line.split(' | ');
+ var current = {};
+ actual.requests.push(current);
+ chunks.forEach(function(chunk) {
+ var nameval = chunk.split(' = ');
+ var name = nameval[0];
+ var value = nameval[1];
+ current[name] = value;
+ });
+ });
+ } else if (xhr.status == 404) {
+ //No stash was found
+ } else {
+ errors.push('got unexpected xhr status: '+xhr.status);
+ }
+ this.step(removeCookies);
+ });
+ xhr.onerror = this.step_func(function() {
+ errors.push('got xhr error');
+ this.step(removeCookies);
+ });
+ xhr.send();
+ });
+}
+
+function removeCookies() {
+ document.cookie = id+'=;path=/;max-age=0';
+ var nextStep = checkData;
+ if (hasCrossDomainCookie) {
+ var iframe = document.createElement('iframe');
+ iframe.onload = this.step_func(nextStep);
+ iframe.src = otherOriginURL + 'support/cors-tester.py?delete-cookie&id=' + id;
+ document.body.appendChild(iframe);
+ } else {
+ this.step(nextStep);
+ }
+}
+
+function removeLog() {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'support/cors-tester.py?cleanup&id='+id, true);
+ xhr.onload = this.step_func(function() {
+ assert_equals(xhr.responseText, 'OK', 'failed to clean up log: '+id);
+ this.step(checkData);
+ });
+ xhr.onerror = this.step_func(function() {
+ assert_unreached('failed to clean up log: '+id);
+ });
+ xhr.send();
+}
+
+function checkData() {
+ assert_equals(errors.length, 0, errors);
+ try {
+ if (actual.event == 'load' && expected.event == 'error')
+ assert_unreached('Security problem: got load event but expected error event');
+ assert_object_equals(actual, expected);
+ } catch(ex) {
+ var style = document.createElement('style');
+ style.textContent = '.json-diffs td { vertical-align:top } .json-diffs pre { margin:0 }';
+ document.head.appendChild(style);
+ var table = document.createElement('table');
+ table.border = "";
+ table.className = 'json-diffs';
+ table.innerHTML = '<tr><th>Actual<th>Expected<tr><td><pre></pre><td><pre></pre>';
+ table.getElementsByTagName('pre')[0].textContent = JSON.stringify(actual, null, 2);
+ table.getElementsByTagName('pre')[1].textContent = JSON.stringify(expected, null, 2);
+ document.body.insertBefore(table, document.getElementById('log'));
+ throw ex;
+ }
+ assert_equals(track.track.cues.length, expected.event == 'load' ? 1 : 0, 'track.track.cues.length');
+ this.done();
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/cors-tester.py b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/cors-tester.py
new file mode 100644
index 0000000000..ad1cce1922
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/cors-tester.py
@@ -0,0 +1,50 @@
+from wptserve.handlers import HTTPException
+
+def main(request, response):
+ if request.method != u"GET":
+ raise HTTPException(400, message=u"Method was not GET")
+
+ if not b"id" in request.GET:
+ raise HTTPException(400, message=u"No id")
+
+ id = request.GET[b'id']
+ if b"read" in request.GET:
+ data = request.server.stash.take(id)
+ if data is None:
+ response.set_error(404, u"Tried to read data not yet set")
+ return
+ return [(b"Content-Type", b"text/plain")], data
+
+ elif b"cleanup" in request.GET:
+ request.server.stash.take(id)
+ return b"OK"
+
+ elif b"delete-cookie" in request.GET:
+ response.delete_cookie(id)
+ return [(b"Content-Type", b"text/plain")], b"OK"
+
+ if b"origin" in request.GET:
+ response.headers.set(b'Access-Control-Allow-Origin', request.GET[b'origin'])
+ response.headers.set(b'Access-Control-Allow-Credentials', b'true')
+
+ cors = request.headers.get(b"origin", b"no")
+
+ cookie = request.cookies.first(id, None)
+ cookie_value = cookie.value if cookie is not None else b"no"
+
+ line = b'cors = ' + cors + b' | cookie = ' + cookie_value
+
+ data = request.server.stash.take(id)
+ if data is not None:
+ line = data + b"\n" + line
+
+ request.server.stash.put(id, line)
+
+ if b"redirect" in request.GET:
+ response.status = 302
+ response.headers.set(b'Location', request.GET[b'redirect'])
+ else:
+ return b"""WEBVTT
+
+00:00:00.000 --> 00:00:10.000
+Test"""
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/remove-cookie.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/remove-cookie.html
new file mode 100644
index 0000000000..00430e3f0e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/remove-cookie.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>Remove cookie from location.hash</title>
+<script>
+if (location.hash)
+ document.cookie = decodeURIComponent(location.hash.substr(1))+'=yes;path=/;max-age=0';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/set-cookie.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/set-cookie.html
new file mode 100644
index 0000000000..cc1c926386
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/cors/support/set-cookie.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>Set cookie from location.hash</title>
+<script>
+if (location.hash)
+ document.cookie = decodeURIComponent(location.hash.substr(1))+'=yes;path=/;max-age=15';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/crashtests/track-element-src-aborted-load-onerror-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/crashtests/track-element-src-aborted-load-onerror-crash.html
new file mode 100644
index 0000000000..9db5ef0748
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/crashtests/track-element-src-aborted-load-onerror-crash.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>HTMLTrackElement 'src' attribute changed, load pending, 'error' handler mutates</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/media.html#start-the-track-processing-model">
+<link rel="help" href="https://crbug.com/1374341">
+<video></video>
+<script>
+ const video = document.querySelector('video');
+ video.style.visibility = 'collapse';
+ video.setAttribute('crossorigin', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
+ const track = document.createElement('track');
+ track.src = 'x';
+ track.track.mode = 'hidden';
+ video.appendChild(track);
+ track.onerror = () => {
+ for (let i = 0; i < 10; ++i)
+ video.setAttribute('foo' + i, 'bar');
+ };
+ setTimeout(() => {
+ track.src = 'y';
+ }, 0);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/no-cuechange-before-play.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/no-cuechange-before-play.html
new file mode 100644
index 0000000000..cd53914ecd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/no-cuechange-before-play.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Ensure that the 'cuechange' event is not fired before video playback has begun.</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(function(t) {
+ let video = document.createElement('video');
+ video.src = getVideoURI('/media/movie_5');
+ video.preload = 'auto';
+
+ // Create a track element. The 'cuechange' event should not be fired.
+ let track = document.createElement('track');
+ track.oncuechange = t.unreached_func('The \`cuechange\` event should not be fired');
+
+ let videoWatcher = new EventWatcher(t, video, 'canplaythrough');
+ let trackWatcher = new EventWatcher(t, track, ['cuechange', 'load'])
+
+ track.src = 'resources/captions-fast.vtt';
+ track.kind = 'captions';
+ track.default = true;
+ track.track.mode = 'showing';
+ video.appendChild(track);
+
+ return Promise.all([videoWatcher.wait_for('canplaythrough'), trackWatcher.wait_for('load')]);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning-bad.vtt
new file mode 100644
index 0000000000..ff4c3fb5cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning-bad.vtt
@@ -0,0 +1,20 @@
+WEBVTT
+Either one or both of positioning and alignment values are invalid.
+
+1
+00:00:00.000 --> 00:00:30.500 position:10% align: start
+Bear is Coming!!!!!
+Positioning on the left bottom, middle aligned,
+because the alignment is mistyped.
+
+2
+00:00:31.000 --> 00:00:45.500 position:200% align:middle
+I said Bear is coming!!!!
+Positioning on the bottom middle, middle aligned,
+because the positioning is off.
+
+3
+00:01:01.000 --> 00:02:00.500 position:-80% align:ends
+I said Bear is coming now!!!!
+Positioning on the bottom middle, middle aligned,
+because both the alignment and positioning don't apply.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning.vtt
new file mode 100644
index 0000000000..a6e6af2ef9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-positioning.vtt
@@ -0,0 +1,20 @@
+WEBVTT
+Cues should position at different horizontal positions with different alignments.
+
+1
+00:00:00.000 --> 00:00:30.500 position:10% align:start
+Bear is Coming!!!!!
+Positioning on the left bottom, start aligned, and
+first character rendering position is at 10% of width.
+
+2
+00:00:31.000 --> 00:00:45.500 position:20% align:middle
+I said Bear is coming!!!!
+Positioning on the bottom left, middle aligned, and
+middle character rendering position of each line is at 20% of width.
+
+3
+00:01:01.000 --> 00:02:00.500 align:end position:80%
+I said Bear is coming now!!!!
+Positioning on the bottom right, end aligned, and
+last character rendering position of each line is at 80% of width.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position-bad.vtt
new file mode 100644
index 0000000000..b196f13a20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position-bad.vtt
@@ -0,0 +1,21 @@
+WEBVTT
+One or more of line/text positioning and alignment values are invalid (settings are ignored).
+
+1
+00:00:00.000 --> 00:00:30.500 position: 0% align: start line: 0%
+Bear is Coming!!!!!
+None of the cue settings will be applied, just the default.
+
+2
+00:00:31.000 --> 00:00:01.500 position:0% align:end line:-30%
+I said Bear is coming!!!!
+The line position setting is ignored.
+No text is visible though because it's off-screen at position
+0 and the last character is at position 0%.
+
+3
+00:01:01.000 --> 00:01:30.000 line:-3 align:middler position:60%
+I said Bear is coming now!!!!
+Positioning on line 3 from the viewport bottom, middle aligned,
+with middle character of cue at 60% width.
+The alignment is ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position.vtt
new file mode 100644
index 0000000000..dd3a6debb8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/align-text-line-position.vtt
@@ -0,0 +1,28 @@
+WEBVTT
+Cues with valid alignment, line and text position settings.
+
+1
+00:00:00.000 --> 00:00:15.000 position:10% align:start line:0%
+Bear is Coming!!!!!
+Positioning on the top of the viewport at 10% horizontally,
+start aligned.
+
+00:00:15.500 --> 00:00:30.500 line:0 align:start
+Bear is Coming!!!!!
+This is line 0, middle aligned, first character at 50% width.
+
+2
+00:00:31.000 --> 00:00:45.500 position:80% line:80%
+I said Bear is coming!!!!
+Middle aligned, middle of cue's character is at 80% width and 80% height.
+
+00:00:46.000 --> 00:01:00.500 line:5 align:end position:30%
+I said Bear is coming!!!!
+This is line 6 from the top of the video viewport,
+end aligned with last character at 30% of viewport width.
+
+3
+00:01:01.000 --> 00:01:30.000 line:-3 align:middle position:60%
+I said Bear is coming now!!!!
+Positioning on line 3 from the viewport bottom, middle aligned,
+with middle character of cue at 60% width.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-bad.vtt
new file mode 100644
index 0000000000..5beb376f45
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-bad.vtt
@@ -0,0 +1,22 @@
+WEBVTT
+Cue alignment may only be start, middle, or end. These are all misspelled and so will default to middle.
+
+1
+00:00:00.000 --> 00:00:30.500 align:starta
+Bear is Coming!!!!!
+Erroneous alignment value -> middle.
+
+2
+00:00:31.000 --> 00:01:00.500 align:-start
+I said Bear is coming!!!!
+Erroneous alignment value --> middle.
+
+3
+00:01:01.000 --> 00:02:00.500 align: end
+I said Bear is coming now!!!!
+Erroneous alignment value with surplus whitespace --> middle.
+
+4
+00:02:01.000 --> 100:20:00.500 align:piugjk
+I said Bear is coming now!!!!
+Erroneous alignment value -> middle.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-ltr.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-ltr.vtt
new file mode 100644
index 0000000000..673b29ac85
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment-ltr.vtt
@@ -0,0 +1,22 @@
+WEBVTT
+Cue alignment may be start, middle, or end (default is middle).
+
+1
+00:00:00.000 --> 00:00:30.500 align:start
+الدب قادم!!!!!
+بدء محاذاته.
+
+2
+00:00:31.000 --> 00:01:00.500 align:middle
+قلت الدب قادم!!
+محاذاة الوسط.
+
+3
+00:01:01.000 --> 00:02:00.500 align:end
+قلت الدب قادم الآن!!
+محاذاة الغاية.
+
+4
+00:02:01.000 --> 100:20:00.500
+قلت الدب قادم الآن!!
+الاÙتراضية هي محاذاة الوسط. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment.vtt
new file mode 100644
index 0000000000..ad7792f772
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/alignment.vtt
@@ -0,0 +1,22 @@
+WEBVTT
+Cue alignment may be start, middle, or end (default is middle).
+
+1
+00:00:00.000 --> 00:00:30.500 align:start
+Bear is Coming!!!!!
+Start align.
+
+2
+00:00:31.000 --> 00:01:00.500 align:middle
+I said Bear is coming!!!!
+Middle align.
+
+3
+00:01:01.000 --> 00:02:00.500 align:end
+I said Bear is coming now!!!!
+End align.
+
+4
+00:02:01.000 --> 100:20:00.500
+I said Bear is coming now!!!!
+Default is middle alignment. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/bom.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/bom.vtt
new file mode 100644
index 0000000000..0c8de32bcb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/bom.vtt
@@ -0,0 +1,10 @@
+WEBVTT FILE
+A BOM character at the start of a file should be ignored.
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:20:00.500
+I said Bear is coming!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-fast.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-fast.vtt
new file mode 100644
index 0000000000..7fe5b1241a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-fast.vtt
@@ -0,0 +1,13 @@
+WEBVTT
+
+1
+00:00:00.000 --> 00:00:00.300
+Lorem
+
+2
+00:00:00.300 --> 00:00:01.300
+ipsum
+
+3
+00:00:01.800 --> 00:00:02.800
+dolor
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-gaps.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-gaps.vtt
new file mode 100644
index 0000000000..44c74665c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-gaps.vtt
@@ -0,0 +1,18 @@
+WEBVTT
+
+1
+00:00:01.000 --> 00:00:02.000
+Lorem ipsum dolor sit amet,
+
+2
+00:00:03.000 --> 00:00:04.000
+consectetuer adipiscing elit,
+
+3
+00:00:05.000 --> 00:00:06.000
+sed diam nonummy nibh euismod tincidunt
+
+4
+00:00:07.000 --> 00:00:08.000
+ut laoreet dolore magna aliquam erat volutpat.
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-html.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-html.vtt
new file mode 100644
index 0000000000..0730f8bc40
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions-html.vtt
@@ -0,0 +1,18 @@
+WEBVTT
+
+1
+00:00:00.000 --> 00:00:01.000
+Lorem <b>ipsum</b> <u>dolor</u> <i.sit>sit</i> amet,
+
+2
+00:00:03.000 --> 00:00:04.000
+consectetuer adipiscing elit,
+
+3
+00:00:05.000 --> 00:00:06.000
+sed diam nonummy nibh euismod tincidunt
+
+4
+00:00:07.000 --> 00:00:08.000
+ut laoreet dolore magna aliquam erat volutpat.
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions.vtt
new file mode 100644
index 0000000000..787c430868
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/captions.vtt
@@ -0,0 +1,18 @@
+WEBVTT
+
+1
+00:00:00.000 --> 00:00:01.000
+Lorem
+
+2
+00:00:01.000 --> 00:00:02.000
+ipsum
+
+3
+00:00:02.000 --> 00:00:03.000
+dolor
+
+4
+00:00:03.000 --> 00:00:04.000
+sit
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class-bad.vtt
new file mode 100644
index 0000000000..650ea2c496
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class-bad.vtt
@@ -0,0 +1,17 @@
+WEBVTT
+Invalid <c> class markup.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+<c .black>Bear is Coming!!!!!</c>
+The space signified an annotation start.
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+<c.red&large>I said Bear is coming!!!!</c>
+Probably should only allow characters that CSS allows in class names.
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+I said <c.9red.upper+case>Bear is coming now</c>!!!!
+Probably should only allow characters that CSS allows in class names.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class.vtt
new file mode 100644
index 0000000000..ea3ef623f5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/class.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+Cue text fragment with <c> class markup is mapped to HTML <span> element with CSS classes.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+<c.black>Bear is Coming!!!!!</c>
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+<c.green>I said Bear is coming!!!!</c>
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+I said <c.red.uppercase>Bear is coming now</c>!!!!
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id-error.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id-error.vtt
new file mode 100644
index 0000000000..2b5db0c1da
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id-error.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+Cue identifiers cannot contain the string "-->".
+
+-->random_id
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+another random identifier-->
+00:00:31.000 --> 00:01:00.500
+I said Bear is coming!!!!
+
+identifier-->too
+00:01:01.000 --> 00:20:00.500
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id.vtt
new file mode 100644
index 0000000000..3902118620
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-id.vtt
@@ -0,0 +1,18 @@
+WEBVTT
+Random text is accepted for cue identifiers.
+
+random_id
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+another random identifier
+00:00:31.000 --> 00:01:00.500
+I said Bear is coming!!!!
+
+identifier--too
+00:01:01.000 --> 00:02:00.500
+I said Bear is coming now!!!!
+
+identifier--too
+00:02:01.000 --> 00:03:00.500
+Duplicate identifier \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id-error.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id-error.vtt
new file mode 100644
index 0000000000..111bae6344
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id-error.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+Cue identifiers cannot contain "-->". Whole cue is ignored.
+
+-->
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+-->
+00:00:31.000 --> 00:01:00.500
+I said Bear is coming!!!!
+
+-->
+00:01:01.000 --> 00:20:00.500
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id.vtt
new file mode 100644
index 0000000000..0d52a70ee4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-no-id.vtt
@@ -0,0 +1,11 @@
+WEBVTT
+Cues don't have to have identifiers.
+
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+00:00:31.000 --> 00:01:00.500
+I said Bear is coming!!!!
+
+00:01:01.000 --> 00:20:00.500
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-cuetext.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-cuetext.vtt
new file mode 100644
index 0000000000..88f56cceca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-cuetext.vtt
@@ -0,0 +1,6 @@
+WEBVTT
+
+00:00.000 --> 00:01.000
+Valid cue 1
+00:02.000 --> 00:03.000
+Valid cue 2
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-header.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-header.vtt
new file mode 100644
index 0000000000..205955e3e4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-header.vtt
@@ -0,0 +1,6 @@
+WEBVTT
+00:00.000 --> 00:01.000
+Valid cue 1
+
+00:02.000 --> 00:03.000
+Valid cue 2
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-note.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-note.vtt
new file mode 100644
index 0000000000..56defcc48b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-recovery-note.vtt
@@ -0,0 +1,9 @@
+WEBVTT
+
+00:00.000 --> 00:01.000
+Valid cue 1
+
+NOTE about something
+NOTE or something else - maybe an identifier
+00:02.000 --> 00:03.000
+Valid cue 2
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align-bad.vtt
new file mode 100644
index 0000000000..5e4a61a5e4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align-bad.vtt
@@ -0,0 +1,18 @@
+WEBVTT
+Either size or alignment are invalid.
+
+1
+00:00:00.000 --> 00:00:30.500 size:100% align:@start
+Bear is Coming!!!!!
+Box for the cue is 100% of the video viewport width, alignment is ignored.
+
+2
+00:00:31.000 --> 00:01:00.500 size:-10% align:end
+I said Bear is coming!!!!
+Box for the cue is as big as the text, no line wrapping,
+(except if viewport is too small) and end aligned.
+
+3
+00:01:01.000 --> 00:02:00.500 size:110% align:@end
+I said Bear is coming now!!!!
+Both cue size and alignment are ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align.vtt
new file mode 100644
index 0000000000..6d36536539
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-align.vtt
@@ -0,0 +1,19 @@
+WEBVTT
+Valid cue size with alignment settings.
+
+1
+00:00:00.000 --> 00:00:30.500 size:100% align:start
+Bear is Coming!!!!!
+Box for the cue is 100% of the video viewport width
+and because of the start align, all text is left aligned on the video viewport.
+
+2
+00:00:31.000 --> 00:01:00.500 size:10% align:end
+I said Bear is coming!!!!
+Box for the cue is 10% of the video viewport width, which will mean that automatic line wrapping will happen
+and the text is aligned to the end.
+
+3
+00:01:01.000 --> 00:02:00.500 size:0% align:middle
+I said Bear is coming now!!!!
+Cue text box size of 0 is acceptable, even if not visible.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-bad.vtt
new file mode 100644
index 0000000000..700600d7a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size-bad.vtt
@@ -0,0 +1,17 @@
+WEBVTT
+Invalid cue sizes (all settings are ignored).
+
+1
+00:00:00.000 --> 00:00:30.500 size: 50%
+Bear is Coming!!!!!
+Cue size setting doesn't parse and is ignored.
+
+2
+00:00:31.000 --> 00:01:00.500 size:-10%
+I said Bear is coming!!!!
+Negative cue size setting is not acceptable and is ignored.
+
+3
+00:01:01.000 --> 00:02:00.500 size:4000%
+I said Bear is coming now!!!!
+Cue size beyond 100% is not acceptable and is ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size.vtt
new file mode 100644
index 0000000000..017d59a18b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cue-size.vtt
@@ -0,0 +1,19 @@
+WEBVTT
+Valid cue size values.
+
+1
+00:00:00.000 --> 00:00:30.500 size:100%
+Bear is Coming!!!!!
+Box for the cue is 100% of the video viewport width,
+exemplified through background color,
+even if the text needs less.
+
+2
+00:00:31.000 --> 00:01:00.500 size:10%
+I said Bear is coming!!!!
+Box for the cue is 10% of the video viewport width, which will mean that automatic line wrapping will happen.
+
+3
+00:01:01.000 --> 00:02:00.500 size:0%
+I said Bear is coming now!!!!
+Cue text box size of 0 is acceptable, even if not visible.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-chrono-order.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-chrono-order.vtt
new file mode 100644
index 0000000000..fd6d484f88
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-chrono-order.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+Cues that have overlapping time ranges.
+
+1
+00:00:01.000 --> 00:00:02.000
+Bear is Coming!!!!!
+
+2
+00:00:02.500 --> 00:00:03.500
+I said Bear is coming!!!!
+
+3
+00:00:04.000 --> 00:00:05.000
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-no-separation.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-no-separation.vtt
new file mode 100644
index 0000000000..9062c67ede
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-no-separation.vtt
@@ -0,0 +1,11 @@
+WEBVTT
+Cues must be separated by at least one blank line, otherwise treated like one big cue.
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+2
+00:00:31.000 --> 00:01:00.500
+I said Bear is coming!!!!
+00:01:01.000 --> 100:20:00.500
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-overlapping.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-overlapping.vtt
new file mode 100644
index 0000000000..3f035d331f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues-overlapping.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+Cues that have overlapping time ranges.
+
+1
+00:00:01.000 --> 00:00:06.000
+Bear is Coming!!!!!
+
+2
+00:00:01.500 --> 00:00:05.000
+I said Bear is coming!!!!
+
+3
+00:00:02.000 --> 00:00:05.000
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues.vtt
new file mode 100644
index 0000000000..125ed66785
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/cues.vtt
@@ -0,0 +1,17 @@
+WEBVTT
+Cues may be separated by one or more blank lines.
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+
+2
+00:00:31.000 --> 00:01:00.500
+I said Bear is coming!!!!
+
+
+
+3
+00:01:01.000 --> 100:20:00.500
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/default-styles.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/default-styles.vtt
new file mode 100644
index 0000000000..d890ca3f71
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/default-styles.vtt
@@ -0,0 +1,19 @@
+WEBVTT
+
+COMMENT-->
+this is a comment, that will parse as part of the header;
+the STYLE and DEFAULTS below are parsed as invalid cues
+
+STYLE-->
+::cue(.narration) { color: blue; }
+
+DEFAULTS -->
+line:-1 align:middle size:50%
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:20:00.500
+I said Bear is coming!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/degenerate-cues.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/degenerate-cues.vtt
new file mode 100644
index 0000000000..c04390420f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/degenerate-cues.vtt
@@ -0,0 +1,5 @@
+WEBVTT
+
+00:00.000 --> 00:01.000
+00:02.000 --> 00:03.000
+00:04.000 --> 00:05.000
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/empty-cue.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/empty-cue.vtt
new file mode 100644
index 0000000000..dbfde34b69
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/empty-cue.vtt
@@ -0,0 +1,11 @@
+WEBVTT
+Empty cues should not be discarded.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities-wrong.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities-wrong.vtt
new file mode 100644
index 0000000000..f45fee4793
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities-wrong.vtt
@@ -0,0 +1,15 @@
+WEBVTT
+Invalid use of < and > characters.
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+This cue has a less than < character.
+It turns everything from there on into an annotation
+for an empty tag and ends only at the next &gt; or &amp; character.
+
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+This cue has a greater than > character.
+Since it's not related to a &lt; character,
+it's just interpreted as text.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities.vtt
new file mode 100644
index 0000000000..a8817954a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/entities.vtt
@@ -0,0 +1,30 @@
+WEBVTT
+Cue content with escape characters for &, <, >, LRM, RLM and non-breaking space.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+This cue has an ampersand &amp; character.
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+This cue has a less than &lt; character.
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+This cue has a greater than &gt; character.
+
+4
+00:02:01.000 --> 00:02:30.500 align:start position:20%
+This cue has a Left-to-Right Mark &lrm;.
+
+5
+00:02:31.000 --> 00:03:00.500 align:start position:20%
+This cue has a Right-to-Left Mark &rlm;.
+
+6
+00:03:01.000 --> 00:03:30.500 align:start position:20%
+This cue has a non-breaking space &nbsp;.
+
+7
+00:03:31.000 --> 00:04:00.500
+This & is parsed to the same as &amp;.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/interspersed-non-cue.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/interspersed-non-cue.vtt
new file mode 100644
index 0000000000..c825ab32e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/interspersed-non-cue.vtt
@@ -0,0 +1,9 @@
+WEBVTT
+
+00:00.000 --> 00:01.000
+First
+
+Stray Id or other non-cue content
+
+00:02.000 --> 00:03.000
+Second
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/iso2022jp3.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/iso2022jp3.vtt
new file mode 100644
index 0000000000..10a1624386
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/iso2022jp3.vtt
@@ -0,0 +1,10 @@
+WEBVTT FILE
+Different encodings (iconv) should not be recognized as WebVTT a file.
+
+1
+00:00:00.000 --> 00:00:30.500
+$B7J5$H=CG(B
+
+2
+00:00:31.000 --> 00:20:00.500
+$BEENOITB-(B
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/large-timestamp.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/large-timestamp.vtt
new file mode 100644
index 0000000000..e6c18ce3bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/large-timestamp.vtt
@@ -0,0 +1,5 @@
+WEBVTT
+
+1
+1234567:00:00.000 --> 1234567890:00:00.000
+A very long cue.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position-bad.vtt
new file mode 100644
index 0000000000..3d52175729
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position-bad.vtt
@@ -0,0 +1,30 @@
+WEBVTT
+Invalid positioning values (all settings are ignored).
+
+1
+00:00:00.000 --> 00:00:15.000 line:-0%
+Bear is Coming!!!!!
+Negative percentages are not allowed.
+Line position is ignored.
+
+2
+00:00:31.000 --> 00:00:45.500 line:+50%
+I said Bear is coming!!!!
+Non-numbers are not allowed.
+Line position is ignored.
+
+00:00:46.000 --> 00:01:00.500 line:+5
+I said Bear is coming!!!!
+Plus sign is not allowed.
+Line position is ignored.
+
+3
+00:01:01.000 --> 00:01:30.000 line:10%0%
+I said Bear is coming now!!!!
+Doesn't parse into a percentage.
+Line position is ignored.
+
+00:01:31.000 --> 00:02:00.500 line:-10l
+I said Bear is coming now!!!!
+Doesn't parse into a number.
+Line position is ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position.vtt
new file mode 100644
index 0000000000..82f7e2a523
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/line-position.vtt
@@ -0,0 +1,37 @@
+WEBVTT
+Cues with valid vertical line positioning values.
+
+1
+00:00:00.000 --> 00:00:15.000 line:0%
+Bear is Coming!!!!!
+Positioning on the top of the viewport, in the middle.
+
+00:00:15.500 --> 00:00:30.500 line:0
+Bear is Coming!!!!!
+This is line 0.
+Positioning on the top of the viewport, in the middle.
+
+2
+00:00:31.000 --> 00:00:45.500 line:50%
+I said Bear is coming!!!!
+Positioning on the center of the video.
+
+
+00:00:46.000 --> 00:01:00.500 line:5
+I said Bear is coming!!!!
+This is line 6 from the top of the video viewport.
+
+3
+00:01:01.000 --> 00:01:30.000 line:100%
+I said Bear is coming now!!!!
+Positioning on the bottom middle.
+
+00:01:31.000 --> 00:02:00.500 line:-1
+I said Bear is coming now!!!!
+This is the first line at the bottom of the video viewport.
+Positioning on the bottom middle. Only 1 line shows.
+
+00:02:01.000 --> 00:02:30.000 line:500
+I said Bear is coming now!!!!
+This is legal,
+even though the line will likely not be within the video viewport.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup-bad.vtt
new file mode 100644
index 0000000000..4ff7add2d7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup-bad.vtt
@@ -0,0 +1,22 @@
+WEBVTT
+Cue text has invalid markup of <b>, <i>, <u>, <rt> and <ruby>. Has a bad effect on the remainder of the cue.
+
+1
+00:00:00.000 --> 00:00:15.000 align:start position:20%
+The following bear starts bold but end is broken:
+<b>Bear</ b> is Coming!!!!!
+
+00:00:15.500 --> 00:00:30.500 align:start position:20%
+The following bear is not in italics but the markup is removed:
+< i>Bear</i> is Coming!!!!!
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+The following bear is not underlined and markup is removed:
+I said < u >Bear</u> is coming!!!!
+
+3
+00:01:01.000 --> 00:01:30.000 align:start position:20%
+The following bear is not ruby annotated and markup is removed:
+I said <ru by>Bear<rt>bear with me</rt></ruby> is coming!!!!
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup.vtt
new file mode 100644
index 0000000000..252a599b5f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/markup.vtt
@@ -0,0 +1,22 @@
+WEBVTT
+Cues with <b>, <i>, <u>, <rt> and <ruby> tags (all valid).
+
+1
+00:00:00.000 --> 00:00:15.000 align:start position:20%
+The following bear is bold:
+<b>Bear</b> is Coming!!!!!
+
+00:00:15.500 --> 00:00:30.500 align:start position:20%
+The following bear is in italics and has a class of "larger":
+<i.larger>Bear</i> is Coming!!!!!
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+The following bear is underlined even though the element has a blank:
+I said <u >Bear</u> is coming!!!!
+
+3
+00:01:01.000 --> 00:01:30.000 align:start position:20%
+The following bear is ruby annotated:
+I said <ruby>Bear<rt>bear with me</rt></ruby> is coming!!!!
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata-area.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata-area.vtt
new file mode 100644
index 0000000000..255298aeb0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata-area.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+This is where metadata would go and these lines should be skipped.
+author = silviapf@google.com
+COMMENT-->
+this is a comment, that will parse as part of the header;
+the STYLE and DEFAULTS below are parsed as invalid cues
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:20:00.500
+I said Bear is coming!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata.vtt
new file mode 100644
index 0000000000..03d8cf4a1c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/metadata.vtt
@@ -0,0 +1,38 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:01.000
+Lorem ipsum dolor sit amet,
+
+00:00:02.000 --> 00:00:03.000
+consectetuer adipiscing elit,
+
+00:00:04.000 --> 00:00:05.000
+sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+00:00:06.000 --> 00:00:07.000
+Ut wisi enim ad minim veniam,
+
+00:00:08.000 --> 00:00:09.000
+quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+
+00:00:10.000 --> 00:00:11.000
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat,
+
+00:00:12.000 --> 00:00:13.000
+vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
+
+00:00:14.000 --> 00:00:15.000
+dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+
+00:00:16.000 --> 00:00:17.000
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id
+
+00:00:18.000 --> 00:00:19.000
+quod mazim placerat facer possim assum.
+
+00:00:20.000 --> 00:00:21.000
+Typi non habent claritatem insitam;
+
+00:00:22.000 --> 00:00:23.000
+est usus legentis in iis qui facit eorum claritatem.
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/missed-cues.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/missed-cues.vtt
new file mode 100644
index 0000000000..36e8366e90
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/missed-cues.vtt
@@ -0,0 +1,31 @@
+WEBVTT
+Events should be triggered for missed (skipped) cues during normal playback.
+
+1
+00:00:00.000 --> 00:00:01.500 align:start position:20%
+Bear is Coming!!!!!
+And what kind of a bear it is - just have look.
+
+2
+00:00:02.000 --> 00:00:02.500 align:start position:20%
+I said Bear is coming!!!!
+
+3
+00:00:05.500 --> 00:00:05.501 align:start position:20%
+I said Bear is coming now!!!!
+
+4
+00:00:05.700 --> 00:00:05.701 align:start position:20%
+This is the second missed cue in the test.
+
+5
+00:00:05.800 --> 00:00:05.800 align:start position:20%
+Third missed cue - zero-length cue.
+
+6
+00:00:05.850 --> 00:00:05.851 align:start position:20%
+Fourth missed cue.
+
+7
+00:00:05.950 --> 00:00:01.100
+Negative length cue. Should be treated correctly.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-newline-at-eof.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-newline-at-eof.vtt
new file mode 100644
index 0000000000..49e4e9051a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-newline-at-eof.vtt
@@ -0,0 +1,6 @@
+WEBVTT
+A file with no line terminator at the end should be fine (last cue should be recognized).
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-timings.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-timings.vtt
new file mode 100644
index 0000000000..4cb85b6df2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-timings.vtt
@@ -0,0 +1,13 @@
+WEBVTT
+Cues without timings are ignored.
+
+1
+00:00:00.000
+Bear is Coming!!!!!
+
+2
+00h:00m:31s.000ms
+I said Bear is coming!!!!
+
+3
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-webvtt.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-webvtt.vtt
new file mode 100644
index 0000000000..12053b2703
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/no-webvtt.vtt
@@ -0,0 +1,10 @@
+AWEBVTT FILE
+A file with wrong file header should not be recognized as a webvtt file.
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:20:00.500
+I said Bear is coming!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-bad.vtt
new file mode 100644
index 0000000000..58ca6792be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-bad.vtt
@@ -0,0 +1,39 @@
+WEBVTT
+Invalid horizontal positioning values (all settings are ignored).
+
+1
+00:00:00.000 --> 00:00:15.500 position:-5%
+Bear is Coming!!!!!
+This would be off screen -> ignored.
+
+00:00:16.000 --> 00:00:30.500 position:150%
+Bear is Coming!!!!!
+This would be off screen -> ignored.
+
+2
+00:00:31.000 --> 00:00:45.500 position:50
+I said Bear is coming!!!!
+Missing percent sign -> ignored.
+
+2
+00:00:46.000 --> 00:01:00.500 position:50a%
+I said Bear is coming!!!!
+Surplus character between number and percent sign -> ignored.
+
+3
+00:01:01.000 --> 00:01:30.500 position:100%-fj
+I said Bear is coming now!!!!
+Surplus characters after percent sign -> ignored.
+
+
+00:01:31.000 --> 00:02:00.500 position:100asdf
+I said Bear is coming now!!!!
+Surplus characters and no percent sign -> ignored.
+
+00:02:01.000 --> 00:02:02.000 position:e50%
+I said Bear is coming now!!!!
+Surplus characters at beginning of size string -> ignored.
+
+00:02:02.100 --> 00:02:02.500 position:5g0%
+I said Bear is coming now!!!!
+Surplus characters in middle of size string -> ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-ltr.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-ltr.vtt
new file mode 100644
index 0000000000..b23a7446b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning-ltr.vtt
@@ -0,0 +1,21 @@
+WEBVTT
+Valid horizontal positioning values.
+
+1
+00:00:00.000 --> 00:00:30.500 position:0%
+الدب قادم!!!!!
+تحديد المواقع ÙÙŠ أسÙÙ„ اليمين.
+
+2
+00:00:31.000 --> 00:00:45.500 position:50%
+قلت الدب قادم!!
+تحديد المواقع ÙÙŠ منتص٠القاع.
+
+00:00:46.000 --> 00:01:00.500
+قلت الدب قادم!!
+المواقع الاÙتراضية على منتص٠أسÙÙ„ تزال قائمة.
+
+3
+00:01:01.000 --> 00:02:00.500 position:100%
+قلت الدب قادم الآن!!
+غادر لتحديد المواقع ÙÙŠ القاع.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning.vtt
new file mode 100644
index 0000000000..ccf6024da0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/positioning.vtt
@@ -0,0 +1,21 @@
+WEBVTT
+Valid horizontal positioning values.
+
+1
+00:00:00.000 --> 00:00:30.500 position:0%
+Bear is Coming!!!!!
+Positioning on the left bottom.
+
+2
+00:00:31.000 --> 00:00:45.500 position:50%
+I said Bear is coming!!!!
+Positioning on the bottom middle.
+
+00:00:46.000 --> 00:01:00.500
+I said Bear is coming!!!!
+Default positioning on the bottom middle still.
+
+3
+00:01:01.000 --> 00:02:00.500 position:100%
+I said Bear is coming now!!!!
+Positioning on the bottom right.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings-bad-separation.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings-bad-separation.vtt
new file mode 100644
index 0000000000..cbfe6ea6e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings-bad-separation.vtt
@@ -0,0 +1,20 @@
+WEBVTT
+Cues settings may only be separated by spaces or tabs, but illegal characters
+between settings are ignored.
+
+1
+00:00:00.000 --> 00:00:30.500 - line:43% position:10% -
+Bear is Coming!!!!! Bad separator ignored.
+
+2
+00:00:31.000 --> 00:01:00.500 --> position:50% Vertical:lr align:end
+I said Bear is coming!!!! Bad separator and setting ignored.
+
+3
+00:01:01.000 --> 00:02:00.500 <align:end> <position:90%>
+I said Bear is coming now!!!! Bad setting markup. Not ignored because the settings are
+not delimited by spaces or tabs.
+
+4
+00:02:01.000 --> 100:20:00.500 / vertical:lr | position:90%
+I said Bear is coming now!!!! Bad separator ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings.vtt
new file mode 100644
index 0000000000..dd6b02296a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/settings.vtt
@@ -0,0 +1,18 @@
+WEBVTT
+Cue settings may be separated by spaces or tabs.
+
+1
+00:00:00.000 --> 00:00:30.500 line:100% align:start
+Bear is Coming!!!!! One blank.
+
+2
+00:00:31.000 --> 00:01:00.500 position:40% vertical:rl line:15%
+I said Bear is coming!!!! Several blanks.
+
+3
+00:01:01.000 --> 00:02:00.500 align:middle position:10%
+I said Bear is coming now!!!! Tab separator.
+
+4
+00:02:01.000 --> 100:20:00.500 line:95% vertical:lr align:end
+I said Bear is coming now!!!! Tab separators. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/simple-captions.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/simple-captions.vtt
new file mode 100644
index 0000000000..9815b111da
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/simple-captions.vtt
@@ -0,0 +1,17 @@
+WEBVTT
+
+0
+00:00:04.000 --> 00:00:04.500
+First cue
+
+1
+00:00:04.500 --> 00:00:05.000
+Lorem
+
+2
+00:00:05.000 --> 00:00:05.500
+ipsum
+
+3
+00:00:05.500 --> 00:00:05.501
+Missed cue with pause-on-exit
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/sorted-dispatch.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/sorted-dispatch.vtt
new file mode 100644
index 0000000000..438ea6abf9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/sorted-dispatch.vtt
@@ -0,0 +1,34 @@
+WEBVTT
+Enter and exit events should be dispatched in a sorted order according to their times.
+
+0
+00:00:04.000 --> 00:00:04.500
+Missed cue that should not be considered because of seeking.
+
+1
+00:00:05.100 --> 00:00:05.800 align:start position:20%
+Bear is Coming!!!!!
+
+2
+00:00:05.100 --> 00:00:05.101
+Missed cue 1
+
+3
+00:00:05.100 --> 00:00:05.301
+And what kind of a bear it is - just have look.
+
+4
+00:00:05.100 --> 00:00:05.101
+Missed Cue 2
+
+5
+00:00:05.300 --> 00:00:05.800 align:start position:20%
+I said Bear is coming!!!!
+
+6
+00:00:05.990 --> 00:00:05.993 align:start position:20%
+I said Bear is coming now!!!!
+
+7
+00:00:05.994 --> 00:00:05.998 align:start position:20%
+Bear is already here
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp-bad.vtt
new file mode 100644
index 0000000000..4479cdb722
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp-bad.vtt
@@ -0,0 +1,17 @@
+WEBVTT
+Invalid <timestamp> markup.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+This <00:00:05.000>cue <00:00:10.000>is <00:00:12.000>painted <00:00:08.000>on.
+But since the last two timestamps are out of order, they are ignored.
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+I <00:00:20.000>said <00:00:22.000>Bear <00:00:24.000>is <00:00:26.000>coming!!!!
+All of these timestamps are before the start of the cue, so get ignored.
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+I <00:02:05.000>said <00:02:10.000>Bear <00:02:15.000>is <00:02:20.000>coming <00:02:25.000>now!!!!
+All of these timestamps are after the end of the cue, so get ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp.vtt
new file mode 100644
index 0000000000..17d464bfed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timestamp.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+Paint-on text in cues with <timestamp> markup.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+This <00:00:05.000>cue <00:00:10.000>is <00:00:15.000>painted <00:00:20.000>on.
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+I <00:00:35.000>said <00:00:40.000>Bear <00:00:45.000>is <00:00:50.000>coming!!!!
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+I <00:01:05.000>said <00:01:10.000>Bear <00:01:15.000>is <00:01:20.000>coming <00:01:25.000>now!!!!
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour-error.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour-error.vtt
new file mode 100644
index 0000000000..c33f8a96c3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour-error.vtt
@@ -0,0 +1,22 @@
+WEBVTT
+These timings all have errors and all cues should be ignored.
+
+1
+00:00.00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:01:00:500
+I said Bear is coming!!!!
+
+3
+00:01:01.000 --> 00:120:00.500
+I said Bear is coming now!!!!
+
+4
+00:02:01.000 - 00:03:00.500
+I said Bear is coming now!!!!
+
+5
+00h:03m:01s.000ms --> 00h:03m:00s.500ms
+I said Bear is coming now!!!!
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour.vtt
new file mode 100644
index 0000000000..b708b83338
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-hour.vtt
@@ -0,0 +1,14 @@
+WEBVTT
+Timings can optionally contain an hour.
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:01:00.500
+I said Bear is coming!!!!
+
+3
+00:01:01.000 --> 100:20:00.500
+I said Bear is coming now!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour-errors.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour-errors.vtt
new file mode 100644
index 0000000000..e4bf27d4e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour-errors.vtt
@@ -0,0 +1,22 @@
+WEBVTT
+These timings all have errors and all cues should be ignored.
+
+1
+00.00.000 --> 00:30.500
+Bear is Coming!!!!!
+
+2
+00:31.000 --> 01:00:500
+I said Bear is coming!!!!
+
+3
+01:01.000 --> 120:00.500
+I said Bear is coming now!!!!
+
+4
+01:01.000 - 02:00.500
+I said Bear is coming now!!!!
+
+5
+02:01.000 --> 03m:00.500
+I said Bear is coming now!!!!
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour.vtt
new file mode 100644
index 0000000000..745c34ff9f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-no-hour.vtt
@@ -0,0 +1,18 @@
+WEBVTT
+The hour of a timestamp is optional.
+
+1
+00:00.000 --> 00:30.500
+Bear is Coming!!!!!
+
+2
+00:31.000 --> 01:00.500
+I said Bear is coming!!!!
+
+3
+01:01.000 --> 02:00.500
+I said Bear is coming now!!!!
+
+4
+02:01.000 --> 03:00.500
+tab separators \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-whitespace.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-whitespace.vtt
new file mode 100644
index 0000000000..9d9ac9a38a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/timings-whitespace.vtt
@@ -0,0 +1,51 @@
+WEBVTT
+Whitespace (U+0020, U+0009, U+000C) surrounding cue-timings separator ("-->") is optional
+
+1
+00:00:00.100 -->00:00:01.500
+Single U+0020 SPACE left of cue-timings separator
+
+2
+00:00:00.100--> 00:00:01.500
+Single U+0020 SPACE right of cue-timings separator
+
+3
+00:00:00.100 -->00:00:01.500
+Single U+0009 TAB left of cue-timings separator
+
+4
+00:00:00.100--> 00:00:01.500
+Single U+0009 TAB right of cue-timings separator
+
+5
+00:00:00.100 -->00:00:01.500
+Single U+000C FORM FEED left of cue-timings separator
+
+6
+00:00:00.100--> 00:00:01.500
+Single U+000C FORM FEED right of cue-timings separator
+
+7
+00:00:00.100 -->00:00:01.500
+Several U+0020 SPACE left of cue-timings separator
+
+8
+00:00:00.100--> 00:00:01.500
+Several U+0020 SPACE right of cue-timings separator
+
+9
+00:00:00.100 -->00:00:01.500
+Several U+0009 TAB left of cue-timings separator
+
+10
+00:00:00.100--> 00:00:01.500
+Several U+0009 TAB right of cue-timings separator
+
+11
+00:00:00.100 -->00:00:01.500
+Several U+000C FORM FEED left of cue-timings separator
+
+12
+00:00:00.100--> 00:00:01.500
+Several U+000C FORM FEED right of cue-timings separator
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.de.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.de.vtt
new file mode 100644
index 0000000000..9eaf3d31e4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.de.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:01.000
+German
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.en.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.en.vtt
new file mode 100644
index 0000000000..4241f35b56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.en.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:01.000
+English
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.fr.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.fr.vtt
new file mode 100644
index 0000000000..5523224e0d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.fr.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:01.000
+french
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.vtt
new file mode 100644
index 0000000000..c916c0983b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/track.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:01.000
+test
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/unsupported-markup.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/unsupported-markup.vtt
new file mode 100644
index 0000000000..b4ea7ea09b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/unsupported-markup.vtt
@@ -0,0 +1,23 @@
+WEBVTT
+Any HTML markup that is not supported should be ignored.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+<h1>Bear is Coming!!!!!</h1>
+<p>And what kind of a bear it is - just have <a href="webpage.html">look</a>.</p>
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+<ul>
+ <li>I said Bear is coming!!!!</li>
+ <li>I said Bear is still coming!!!!</li>
+</ul>
+
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+<ol>
+ <li>I said Bear is coming now!!!!</li>
+ <li><img src="bear.png" alt="mighty bear"></li>
+ <li><video src="bear_ad.webm" controls></video></li>
+</ol> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/utf8.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/utf8.vtt
new file mode 100644
index 0000000000..8dd8f27948
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/utf8.vtt
@@ -0,0 +1,10 @@
+WEBVTT
+UTF-8 encoded characters should be recognized.
+
+1
+00:00:00.000 --> 00:00:30.500
+景気判断
+
+2
+00:00:31.000 --> 00:20:00.500
+電力ä¸è¶³ \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-bad.vtt
new file mode 100644
index 0000000000..8e7b3b738d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-bad.vtt
@@ -0,0 +1,17 @@
+WEBVTT
+Invalid vertical direction settings (all settings are ignored).
+
+1
+00:00:00.000 --> 00:00:30.500 vertical:#vertical
+Bear is Coming!!!!!
+Normal rendering - direction setting is ignored.
+
+2
+00:00:31.000 --> 00:01:00.500 vertical:verticallr
+I said Bear is coming!!!!
+Normal rendering - direction setting is ignored.
+
+3
+00:01:01.000 --> 00:02:00.500 vertical:vertical-rl
+I said Bear is coming now!!!!
+Normal rendering - direction setting is ignored.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-ltr.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-ltr.vtt
new file mode 100644
index 0000000000..74838369d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign-ltr.vtt
@@ -0,0 +1,20 @@
+WEBVTT
+Valid vertical direction settings.
+
+1
+00:00:00.000 --> 00:00:30.500 vertical:rl
+الدب قادم!!!!!
+يجعل على الجانب الأيمن من المعاينة الÙيديو والمتوسطة الانحياز ØŒ
+أسÙÙ„ إلى أعلى، وتزايد اليسار.
+
+2
+00:00:31.000 --> 00:01:00.500 vertical:lr
+قلت الدب قادم!!
+يجعل على الجانب الأيسر من المعاينة الÙيديو والمتوسطة الانحياز ØŒ
+أسÙÙ„ إلى أعلى، وتنامي اليمين.
+
+3
+00:01:01.000 --> 00:02:00.500 vertical:rl align:start position:0%
+قلت الدب قادم الآن!!
+يجعل على الجانب الأيمن من المعاينة الÙيديو ØŒ على حد سواء أسÙÙ„ محاذاة
+لمربع جديلة والنص داخل النص ØŒ من أسÙÙ„ إلى أعلى، وتزايد اليسار.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign.vtt
new file mode 100644
index 0000000000..f757a365e3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/valign.vtt
@@ -0,0 +1,20 @@
+WEBVTT
+Valid vertical direction settings.
+
+1
+00:00:00.000 --> 00:00:30.500 vertical:rl
+Bear is Coming!!!!!
+Renders on the right side of the video viewport, middle aligned,
+top to bottom, growing left.
+
+2
+00:00:31.000 --> 00:01:00.500 vertical:lr
+I said Bear is coming!!!!
+Renders on the left side of the video viewport, middle aligned,
+top to bottom, growing right.
+
+3
+00:01:01.000 --> 00:02:00.500 vertical:rl align:start position:0%
+I said Bear is coming now!!!!
+Renders on the right side of the video viewport, top aligned both
+for the cue box and the text within, text from top to bottom, growing left.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice-bad.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice-bad.vtt
new file mode 100644
index 0000000000..12ffdeb82e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice-bad.vtt
@@ -0,0 +1,17 @@
+WEBVTT
+Invalid <v> voice markup.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+< v Speaker>Bear is Coming!!!!!</v>
+This is two annotations for an empty tag.
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+<v&Doe Hunter>I said Bear is coming!!!!</v>
+This does not parse as a voice tag.
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+I said <v-Speaker>Bear is coming now</v>!!!!
+This does not parse as a voice tag.
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice.vtt
new file mode 100644
index 0000000000..d6cfc6887f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/voice.vtt
@@ -0,0 +1,15 @@
+WEBVTT
+Cue text fragment with <v> voice markup mapped to HTML <q> element with @title for annotation.
+
+1
+00:00:00.000 --> 00:00:30.500 align:start position:20%
+<v.blue Speaker>Bear is Coming!!!!!</v>
+Text span with a class and an annotation.
+
+2
+00:00:31.000 --> 00:01:00.500 align:start position:20%
+<v Doe Hunter>I said Bear is coming!!!!</v>
+
+3
+00:01:01.000 --> 00:02:00.500 align:start position:20%
+I said <v.blue Speaker>Bear is coming now</v>!!!!
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/vp8-vorbis-webvtt.webm b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/vp8-vorbis-webvtt.webm
new file mode 100644
index 0000000000..c626f86e33
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/vp8-vorbis-webvtt.webm
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-file.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-file.vtt
new file mode 100644
index 0000000000..0c1a5fb158
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-file.vtt
@@ -0,0 +1,9 @@
+WEBVTT FILE
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:20:00.500
+I said Bear is coming!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-rubbish.vtt b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-rubbish.vtt
new file mode 100644
index 0000000000..dacc215409
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/resources/webvtt-rubbish.vtt
@@ -0,0 +1,10 @@
+WEBVTT asdfasdfauhio
+Rubbish after the WEBVTT header should be ignored.
+
+1
+00:00:00.000 --> 00:00:30.500
+Bear is Coming!!!!!
+
+2
+00:00:31.000 --> 00:20:00.500
+I said Bear is coming!!!! \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-clear-cues.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-clear-cues.html
new file mode 100644
index 0000000000..3ba8c9db88
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-clear-cues.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<title>track element changing "track URL" and clearing cues</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+(async_test(document.title+', set mode, add cue, set src')).step(function(){
+ var track = document.createElement('track');
+ var c = new VTTCue(0, 1, 'foo');
+ c.id = 'id';
+ track.track.addCue(c);
+ assert_equals(track.track.cues, null, 'cues before setting src or mode');
+ track.track.mode = 'showing';
+ assert_equals(track.track.cues.length, 1, 'cues after setting mode');
+ var cues = track.track.cues;
+ track.src = 'data:,a';
+ assert_equals(track.track.cues.length, 0, 'cues.length after setting src');
+ assert_equals(track.track.cues, cues, 'track.track.cues sameness after setting src');
+ assert_equals(c.id, 'id', 'liveness of removed cue');
+ this.done();
+});
+
+(async_test(document.title+', set mode, set src, add cue, change src')).step(function(){
+ var track = document.createElement('track');
+ track.track.mode = 'showing';
+ track.src = 'data:,a';
+ var c = new VTTCue(0, 1, 'foo');
+ c.id = 'id';
+ track.track.addCue(c);
+ assert_equals(track.track.cues.length, 1, 'cues.length before changing src');
+ var cues = track.track.cues;
+ track.src = 'data:,b';
+ assert_equals(track.track.cues.length, 0, 'cues.length after changing src');
+ assert_equals(track.track.cues, cues, 'track.track.cues sameness after changing src');
+ assert_equals(c.id, 'id', 'liveness of removed cue');
+ this.done();
+});
+
+(async_test(document.title+', set mode, add cue, change mode to disabled, set src')).step(function(){
+ var track = document.createElement('track');
+ track.track.mode = 'showing';
+ var c = new VTTCue(0, 1, 'foo');
+ c.id = 'id';
+ track.track.addCue(c);
+ var cues = track.track.cues;
+ track.track.mode = 'disabled';
+ track.src = 'data:,a';
+ assert_equals(cues.length, 0, 'cues.length after changing src');
+ assert_equals(c.id, 'id', 'liveness of removed cue');
+ this.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-empty-string.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-empty-string.html
new file mode 100644
index 0000000000..27c76b6be4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/src-empty-string.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Setting HTMLTrackElement.src to the empty string fires 'error' and sets readyState to ERROR</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/media.html#sourcing-out-of-band-text-tracks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video></video>
+<script>
+async_test(t => {
+ let track = document.createElement("track");
+ track.src = '';
+ track.default = true;
+ track.onerror = t.step_func_done(() => {
+ assert_equals(track.readyState, HTMLTrackElement.ERROR);
+ });
+ track.onload = t.unreached_func('fired load');
+
+ assert_equals(track.readyState, HTMLTrackElement.NONE);
+
+ document.querySelector('video').appendChild(track);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html
new file mode 100644
index 0000000000..a7c08a2e3e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>Ensure that no text track cues are active after the video is unloaded</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var eventCount = 0;
+
+ function eventCallback() {
+ eventCount++;
+ if (eventCount == 3) {
+ assert_equals(trackElement.track.activeCues.length, 1);
+ video.src = '';
+ }
+ }
+
+ var video = document.createElement('video');
+ video.src = getVideoURI('/media/movie_5');
+ // uanset media element's `show-poster` flag in order to run `time marches on`
+ // when we add new cues into media element's cues list.
+ video.play();
+ var trackElement = document.createElement('track');
+
+ trackElement.onload = t.step_func(eventCallback);
+ trackElement.oncuechange = t.step_func(eventCallback);
+ video.oncanplaythrough = t.step_func(eventCallback);
+
+ video.onerror = t.step_func_done(function(event) {
+ assert_equals(event.target, video);
+ assert_not_equals(video.error, null);
+ assert_equals(video.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+ assert_equals(video.networkState, HTMLMediaElement.NETWORK_NO_SOURCE);
+ assert_equals(trackElement.track.activeCues.length, 0);
+ });
+
+ trackElement.src = 'resources/captions-fast.vtt';
+ trackElement.kind = 'captions';
+ trackElement.default = true;
+ video.appendChild(trackElement);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-remove-cue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-remove-cue.html
new file mode 100644
index 0000000000..e738964001
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-remove-cue.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<title>TextTrack's addCue and removeCue</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var video = document.createElement("video");
+ var trackElement = document.createElement("track");
+
+ trackElement.onload = t.step_func_done(function() {
+ var cues = trackElement.track.cues;
+ // Test cues loaded from the file.
+ assert_equals(cues.length, 4);
+ assert_equals(cues.getCueById("1").startTime, 0);
+ assert_equals(cues[1].startTime, 31);
+ assert_equals(cues[2].startTime, 61);
+ assert_equals(cues.getCueById("4").startTime, 121);
+ assert_equals(cues.getCueById("junk"), null);
+
+ // Create a new cue, check values.
+ var textCue = new VTTCue(33, 3.4, "Sausage?");
+ assert_equals(textCue.track, null);
+ assert_equals(textCue.id, "");
+ assert_equals(textCue.startTime, 33);
+ assert_equals(textCue.endTime, 3.4);
+ assert_equals(textCue.pauseOnExit, false);
+ assert_equals(textCue.vertical, "");
+ assert_equals(textCue.snapToLines, true);
+ assert_equals(textCue.line, "auto");
+ assert_equals(textCue.position, "auto");
+ assert_equals(textCue.size, 100);
+ assert_equals(textCue.align, "center");
+
+ // Remove the unadded track, make sure it throws correctly.
+ assert_throws_dom("NotFoundError", function() { trackElement.track.removeCue(textCue); });
+
+ // Add the new cue to a track, make sure it is inserted correctly.
+ trackElement.track.addCue(textCue);
+ assert_equals(textCue.track, trackElement.track);
+ assert_equals(cues[1].startTime, 31);
+ assert_equals(cues[2].startTime, 33);
+ assert_equals(cues[3].startTime, 61);
+
+ // create a new cue and add it to a track created with
+ // video.addTextTrack, make sure it is inserted correctly.
+ var newTrack = video.addTextTrack("subtitles", "French subtitles", "fr");
+ newTrack.mode = "showing";
+ var newCue = new VTTCue(0, 1, "Test!");
+ newTrack.addCue(newCue);
+ assert_equals(newCue, newTrack.cues[0])
+ assert_equals(newCue.track, newTrack);
+ assert_equals(newCue.id, "");
+ assert_equals(newCue.startTime, 0);
+ assert_equals(newCue.endTime, 1);
+ assert_equals(newCue.pauseOnExit, false);
+ assert_equals(newCue.vertical, "");
+ assert_equals(newCue.snapToLines, true);
+ assert_equals(newCue.line, "auto");
+ assert_equals(newCue.position, "auto");
+ assert_equals(newCue.size, 100);
+ assert_equals(newCue.align, "center");
+
+ trackElement.track.removeCue(textCue);
+ assert_equals(textCue.track, null);
+ assert_equals(cues[1].startTime, 31);
+ assert_equals(cues[2].startTime, 61);
+
+ // Remove a cue added from the WebVTT file.
+ textCue = cues[2];
+ trackElement.track.removeCue(textCue);
+ assert_equals(textCue.track, null);
+ assert_equals(cues[1].startTime, 31);
+ assert_equals(cues[2].startTime, 121);
+
+ // Try to remove the cue again.
+ assert_throws_dom("NotFoundError", function() { trackElement.track.removeCue(textCue); });
+
+ // Add a cue before all the existing cues.
+ trackElement.track.addCue(new VTTCue(0, 31, "I am first"));
+ assert_equals(cues[0].startTime, 0);
+ assert_equals(cues[0].endTime, 31);
+ assert_equals(cues[1].startTime, 0);
+ assert_equals(cues[1].endTime, 30.5);
+ assert_equals(cues[2].startTime, 31);
+ });
+
+ trackElement.src = "resources/settings.vtt";
+ trackElement.kind = "captions";
+ trackElement.default = true;
+ video.appendChild(trackElement);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html
new file mode 100644
index 0000000000..c924c92da9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>'addtrack' event is fired when a TextTrack is created</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var video = document.createElement('video');
+
+ var trackElement = document.createElement('track');
+ video.appendChild(trackElement);
+ var tracks = [];
+ tracks.push(trackElement.track);
+
+ // Register the 'addtrack' listener after creating the element
+ // to make sure the event is dispatched asynchronously.
+ video.textTracks.onaddtrack = t.step_func(function(event) {
+ assert_equals(event.target, video.textTracks);
+ assert_true(event instanceof TrackEvent, 'instanceof');
+ assert_equals(event.track, tracks[video.textTracks.length - 1]);
+
+ if (video.textTracks.length == 1) {
+ tracks.push(video.addTextTrack('captions', 'Caption Track', 'en'));
+ assert_equals(video.textTracks.length, 2);
+ } else {
+ t.done();
+ }
+ });
+
+ trackElement.src = 'resources/webvtt-file.vtt';
+ trackElement.track.mode = 'hidden';
+ assert_equals(video.textTracks.length, 1);
+ assert_equals(trackElement.readyState, HTMLTrackElement.NONE);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-addtrack-kind.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-addtrack-kind.html
new file mode 100644
index 0000000000..d058bf2987
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-addtrack-kind.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>addTextTrack() only accepts known "kind" values</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ var trackCount = 0;
+
+ function addTrack(type) {
+ video.addTextTrack(type);
+ assert_equals(video.textTracks.length, ++trackCount);
+ }
+
+ var video = document.createElement("video");
+ assert_equals(video.textTracks.length, 0);
+ assert_throws_js(TypeError, function() { video.addTextTrack("kaptions"); });
+ assert_equals(video.textTracks.length, 0);
+
+ addTrack("subtitles");
+ addTrack("captions");
+ addTrack("descriptions");
+ addTrack("chapters");
+ addTrack("metadata");
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-api-texttracks.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-api-texttracks.html
new file mode 100644
index 0000000000..b2840d235a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-api-texttracks.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Track element - text tracks API test</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#text-track-api">
+<link rel="author" title="Hyunjin Cho">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<h1>Track element and API Test</h1>
+<div style="display:none;">
+ <video id="tracktest" src="/media/movie_300.mp4">
+ <track kind="subtitles" src="resources/track.en.vtt" srclang="en" label="English">
+ <track kind="captions" src="resources/track.en.vtt" srclang="en" label="English with Captions">
+ <track id="french" kind="subtitles" src="resources/track.fr.vtt" srclang="fr" label="Francais">
+ <track kind="subtitles" src="resources/track.de.vtt" srclang="de" label="Deutsch">
+ </video>
+</div>
+<div id="log"></div>
+<script>
+test(function() {
+ var t1 = document.getElementById('tracktest').textTracks;
+ assert_not_equals(t1, undefined, "textTracks member should not be undefined");
+}, "Check the track elements");
+test(function() {
+ var t2 = document.getElementById('tracktest').textTracks.getTrackById("french");
+ assert_not_equals(t2, undefined, "textTracks member should not be undefined");
+}, "Check getTrackById method");
+test(function() {
+ var t3 = document.getElementById('tracktest').textTracks.length;
+ assert_equals(t3, 4, "textTracks List should be 4");
+}, "Count track list");
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-change-event.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-change-event.html
new file mode 100644
index 0000000000..7a17dee2a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-change-event.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>A 'change' event is fired when a TextTrack's mode changes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var video = document.createElement('video');
+ var track = video.addTextTrack('subtitles', 'test', 'en');
+
+ // addTextTrack() defaults to "hidden", so settings
+ // mode to "showing" should trigger a "change" event.
+ track.mode = 'showing';
+ assert_equals(video.textTracks.length, 1);
+
+ video.textTracks.onchange = t.step_func_done(function(event) {
+ assert_equals(event.target, video.textTracks);
+ assert_true(event instanceof Event, 'instanceof');
+ assert_false(event.hasOwnProperty('track'), 'unexpected property found: "track"');
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-css-cue-pseudo-class.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-css-cue-pseudo-class.html
new file mode 100644
index 0000000000..d18f8b55cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-css-cue-pseudo-class.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+:cue { color: red; }
+:cue(i) { color: red; }
+</style>
+<script>
+test(function() {
+ assert_equals(document.styleSheets[0].cssRules.length, 0);
+}, ":cue pseudo-class is not supported and dropped during parsing");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-empty.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-empty.html
new file mode 100644
index 0000000000..59f8fc6c7b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-empty.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Invoke getCueAsHTML() on an empty cue</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ var emptyCue = new VTTCue(0, 0, "");
+ var fragment = emptyCue.getCueAsHTML();
+
+ // The getCueAsHTML() method should return a document fragment.
+ assert_true(fragment instanceof DocumentFragment);
+
+ // The document fragment should have one child, an empty Text node.
+ assert_equals(fragment.childNodes.length, 1);
+ assert_equals(fragment.childNodes[0].constructor.name, Text.name);
+ assert_equals(fragment.childNodes[0].length, 0);
+ assert_equals(fragment.childNodes[0].data, "");
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html
new file mode 100644
index 0000000000..3b4c3542a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Add a track and change its mode through JS</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <source src="/media/test.mp4" type="video/mp4">
+ <source src="/media/test.ogv" type="video/ogg">
+</video>
+<script>
+test(function() {
+ var video = document.querySelector('video');
+ var track = video.addTextTrack('captions', 'English', 'en');
+ track.addCue(new VTTCue(0.0, 10.0, 'wow wow'));
+ track.mode = 'showing';
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable-fragment.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable-fragment.html
new file mode 100644
index 0000000000..713e781996
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable-fragment.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<title>Cue fragment is mutable</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+p, div { display: none; }
+</style>
+<video>
+ <track src="resources/captions-html.vtt" kind="captions" default>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var testTrack = document.querySelector("track");
+
+ video.oncanplaythrough = t.step_func(testMutability);
+ testTrack.onload = t.step_func(testMutability);
+
+ var fragment;
+ var eventCount = 0;
+ function testMutability() {
+ eventCount++;
+ if (eventCount != 2)
+ return;
+
+ var testCue = testTrack.track.cues[0];
+
+ // Test initial cue contents.
+ assert_equals(testCue.text, "Lorem <b>ipsum</b> <u>dolor</u> <i.sit>sit</i> amet,");
+
+ // Cue getCueAsHTML() should return a correct fragment.
+ createExpectedFragment(document.createDocumentFragment());
+ assert_true(fragment.isEqualNode(testCue.getCueAsHTML()));
+
+ // Appending getCuesAsHTML() twice to the DOM should be succesful.
+ document.getElementsByTagName("div")[0].appendChild(testCue.getCueAsHTML());
+ document.getElementsByTagName("div")[1].appendChild(testCue.getCueAsHTML());
+
+ createExpectedFragment(document.createElement("div"));
+ assert_true(fragment.isEqualNode(document.getElementsByTagName("div")[0]));
+ assert_true(fragment.isEqualNode(document.getElementsByTagName("div")[1]));
+
+ // The fragment returned by getCuesAsHTML() should be independently mutable.
+ document.getElementsByTagName("div")[0].firstChild.textContent = "Different text ";
+ assert_false(fragment.isEqualNode(document.getElementsByTagName("div")[0]));
+ assert_true(fragment.isEqualNode(document.getElementsByTagName("div")[1]));
+
+ // Calling twice getCueAsHTML() should not return the same fragment.
+ assert_not_equals(testCue.getCueAsHTML(), testCue.getCueAsHTML());
+
+ t.done();
+ }
+
+ function createExpectedFragment(rootNode) {
+ fragment = rootNode;
+ fragment.appendChild(document.createTextNode("Lorem "));
+
+ var bold = document.createElement("b");
+ bold.appendChild(document.createTextNode("ipsum"));
+ fragment.appendChild(bold);
+
+ fragment.appendChild(document.createTextNode(" "));
+
+ var underline = document.createElement("u");
+ underline.appendChild(document.createTextNode("dolor"));
+ fragment.appendChild(underline);
+
+ fragment.appendChild(document.createTextNode(" "));
+
+ var italics = document.createElement("i");
+ italics.className = "sit";
+ italics.appendChild(document.createTextNode("sit"));
+ fragment.appendChild(italics);
+
+ fragment.appendChild(document.createTextNode(" amet,"));
+ }
+
+ video.src = getVideoURI("/media/counting");
+ });
+ </script>
+</video>
+<p>Fragment 1</p>
+<div></div>
+<p>Fragment 2</p>
+<div></div> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable.html
new file mode 100644
index 0000000000..26a6b84f8a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-mutable.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<title>Modifying attributes of a VTTCue</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track id="captions" src="resources/captions.vtt" kind="captions" default>
+ <script>
+ async_test(function(t) {
+ var track = document.querySelector("track");
+
+ track.onload = t.step_func_done(function() {
+ var cues = track.track.cues;
+
+ // Test initial values.
+ textCue = cues.getCueById("1");
+
+ assert_equals(textCue.startTime, 0);
+ assert_equals(textCue.endTime, 1.0);
+ assert_equals(textCue.pauseOnExit, false);
+ assert_equals(textCue.vertical, "");
+ assert_equals(textCue.snapToLines, true);
+ assert_equals(textCue.line, "auto");
+ assert_equals(textCue.position, "auto");
+ assert_equals(textCue.size, 100);
+ assert_equals(textCue.align, "center");
+
+ // Modify cue values.
+ textCue.startTime = 1.1;
+ assert_equals(textCue.startTime, 1.1);
+
+ textCue.endTime = 3.9;
+ assert_equals(textCue.endTime, 3.9);
+
+ textCue.pauseOnExit = true;
+ assert_equals(textCue.pauseOnExit, true);
+
+ // http://dev.w3.org/html5/webvtt/#dfn-dom-vttcue-vertical
+ // On setting, the text track cue writing direction must be
+ // set to the value given in the first cell of the row in
+ // the table above whose second cell is a case-sensitive
+ // match for the new value.
+ textCue.vertical = "RL";
+ assert_equals(textCue.vertical, "");
+ textCue.vertical = "rl";
+ assert_equals(textCue.vertical, "rl");
+
+ textCue.snapToLines = false;
+ assert_equals(textCue.snapToLines, false);
+
+ // http://dev.w3.org/html5/webvtt/#dfn-vttcue-line
+ // On setting, the text track cue line position must be set
+ // to the new value; if the new value is the string "auto",
+ // then it must be interpreted as the special value auto.
+ assert_equals(textCue.line, "auto");
+ assert_throws_js(TypeError, function() { textCue.line = "gazonk"; });
+ assert_equals(textCue.line, "auto");
+ textCue.line = 42;
+ assert_equals(textCue.line, 42);
+ textCue.line = -2;
+ assert_equals(textCue.line, -2);
+ textCue.line = 102;
+ assert_equals(textCue.line, 102);
+ textCue.snapToLines = true;
+ textCue.line = -2;
+ assert_equals(textCue.line, -2);
+ textCue.line = 102;
+ assert_equals(textCue.line, 102);
+
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-line
+ // On setting, if the new value is negative or greater than 100,
+ // then throw an IndexSizeError exception.
+ // Otherwise, set the text track cue text position to the new value.
+ assert_throws_dom("IndexSizeError", function() { textCue.position = -200; });
+ assert_throws_dom("IndexSizeError", function() { textCue.position = 110; });
+ textCue.position = 11;
+ assert_equals(textCue.position, 11);
+
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-size
+ // On setting, if the new value is negative or greater than 100,
+ // then throw an IndexSizeError exception.
+ // Otherwise, set the text track cue size to the new value.
+ assert_throws_dom("IndexSizeError", function() { textCue.size = -200 });
+ assert_throws_dom("IndexSizeError", function() { textCue.size = 110 });
+ textCue.size = 57;
+ assert_equals(textCue.size, 57);
+
+ // http://dev.w3.org/html5/webvtt/#dfn-dom-vttcue-align
+ // On setting, the text track cue text alignment must be
+ // set to the value given in the first cell of the row
+ // in the table above whose second cell is a case-sensitive
+ // match for the new value.
+ textCue.align = "End";
+ assert_equals(textCue.align, "center");
+ textCue.align = "end";
+ assert_equals(textCue.align, "end");
+ });
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-duration.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-duration.html
new file mode 100644
index 0000000000..e2f78900a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-duration.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Enter, Exit events for a cue with negative duration</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var track = video.addTextTrack("subtitles");
+
+ // Add a cue with negative duration.
+ var cue = new VTTCue(1, -10, "Sausage?");
+ track.addCue(cue);
+ assert_equals(track.cues.length, 1);
+
+ // Verify that enter and exit events are fired.
+ var enterEvent = false;
+ cue.onenter = t.step_func(function() {
+ enterEvent = true;
+ });
+ cue.onexit = t.step_func_done(function() {
+ assert_true(enterEvent);
+ });
+
+ video.src = getVideoURI("/media/test");
+ video.play();
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp-events.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp-events.html
new file mode 100644
index 0000000000..ebd7877f78
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp-events.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Enter, Exit events for cues with negative timestamps</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var track = video.addTextTrack("subtitles");
+
+ // Add cue with negative startTime.
+ var cue = new VTTCue(-10, 1, "Sausage?");
+ track.addCue(cue);
+ assert_equals(track.cues.length, 1);
+ cue.onenter = t.step_func(function() {
+ cue.onexit = t.step_func_done();
+ });
+
+ // Add cue with negative startTime and negative endTime.
+ // This cue should never be active.
+ var missedCue = new VTTCue(-110, -3.4, "Pepperoni?");
+ track.addCue(missedCue);
+ assert_equals(track.cues.length, 2);
+ missedCue.onenter = t.unreached_func();
+ missedCue.onexit = t.unreached_func();
+
+ video.src = getVideoURI("/media/test");
+ video.play();
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp.html
new file mode 100644
index 0000000000..5dc54ed25b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>Negative timestamps</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/settings.vtt" default>
+ <script>
+ async_test(function(t) {
+ var testTrack = document.querySelector("track");
+
+ testTrack.onload = t.step_func_done(function() {
+ var cues = testTrack.track.cues;
+ assert_equals(testTrack.track.cues.length, 4);
+ // Add cue with negative startTime.
+ var cue = new VTTCue(-3439332606, 3.4, "Sausage?");
+ testTrack.track.addCue(cue);
+ assert_equals(cues.length, 5);
+
+ // Add cue with negative startTime and negative endTime.
+ cue = new VTTCue(-110, -3.4, "Pepperoni?");
+ testTrack.track.addCue(cue);
+ assert_equals(cues.length, 6);
+
+ // Set startTime and endTime to negative values.
+ var testCue = cues[2];
+ assert_equals(testCue.startTime, 0);
+ testCue.startTime = -5;
+ assert_equals(testCue.startTime, -5);
+ assert_equals(testCue.endTime, 30.5);
+ testCue.endTime = -3439332606;
+ assert_equals(testCue.endTime, -3439332606);
+
+ // Check negative cues ordering.
+ testCue = cues[3];
+ assert_equals(testCue.startTime, 31);
+ testCue.startTime = -200;
+ // Verify that this cue is moved to 2nd position.
+ assert_equals(cues[1].startTime, -200);
+ });
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-order.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-order.html
new file mode 100644
index 0000000000..58e11ebe70
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-order.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<title>Text track cue order</title>
+<link rel="help" href="https://html.spec.whatwg.org/#text-track-cue-order">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function concat_cuetext(cues) {
+ return Array.prototype.reduce.call(cues, function(acc, value) {
+ return acc + value.text;
+ }, "");
+}
+
+setup(function() {
+ window.video = document.createElement('video');
+});
+
+test(function() {
+ let track = video.addTextTrack('subtitles');
+ track.addCue(new VTTCue(8, 9, '1'));
+ track.addCue(new VTTCue(4, 5, '2'));
+ track.addCue(new VTTCue(2, 3, '3'));
+ assert_equals(concat_cuetext(track.cues), '321');
+}, document.title + ', decreasing start times.');
+
+test(function() {
+ let track = video.addTextTrack('subtitles');
+ track.addCue(new VTTCue(2, 9, '1'));
+ track.addCue(new VTTCue(2, 3, '2'));
+ track.addCue(new VTTCue(2, 5, '3'));
+ assert_equals(concat_cuetext(track.cues), '132');
+}, document.title + ', equal start times varying end times.');
+
+test(function() {
+ let track = video.addTextTrack('subtitles');
+ track.addCue(new VTTCue(2, 3, '1'));
+ track.addCue(new VTTCue(2, 3, '2'));
+ track.addCue(new VTTCue(2, 3, '3'));
+ assert_equals(concat_cuetext(track.cues), '123');
+}, document.title + ', equal start and end times.');
+
+test(function() {
+ let track = video.addTextTrack('subtitles');
+ track.addCue(new VTTCue(2, 5, '1'));
+ track.addCue(new VTTCue(2, 5, '2'));
+ track.addCue(new VTTCue(2, 5, '3'));
+ assert_equals(concat_cuetext(track.cues), '123', 'initial order');
+
+ let cue = track.cues[0];
+ track.removeCue(cue);
+ assert_equals(concat_cuetext(track.cues), '23', '"1" removed');
+
+ track.addCue(cue);
+ assert_equals(concat_cuetext(track.cues), '231', '"1" reinserted');
+}, document.title + ', after re-insertion.');
+
+test(function() {
+ let track = video.addTextTrack('subtitles');
+ track.addCue(new VTTCue(2, 5, '1'));
+ track.addCue(new VTTCue(2, 5, '2'));
+ track.addCue(new VTTCue(2, 5, '3'));
+ assert_equals(concat_cuetext(track.cues), '123', 'initial order');
+
+ track.cues[0].startTime = 4;
+ assert_equals(concat_cuetext(track.cues), '231', '"1" moved last');
+
+ track.cues[2].startTime = 2;
+ assert_equals(concat_cuetext(track.cues), '123', '"1" moved first');
+}, document.title + ', equal start and end times with startTime mutations.');
+
+test(function() {
+ let track = video.addTextTrack('subtitles');
+ track.addCue(new VTTCue(2, 5, '1'));
+ track.addCue(new VTTCue(2, 5, '2'));
+ track.addCue(new VTTCue(2, 5, '3'));
+ assert_equals(concat_cuetext(track.cues), '123', 'initial order');
+
+ track.cues[2].endTime = 9;
+ assert_equals(concat_cuetext(track.cues), '312', '"3" moved first');
+
+ track.cues[1].endTime = 3;
+ assert_equals(concat_cuetext(track.cues), '321', '"1" moved last');
+}, document.title + ', equal start and end times with endTime mutations.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added-ref.html
new file mode 100644
index 0000000000..bd43c462dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<title>Text track cue layout after controls are added (reference)</title>
+<style>
+::cue {
+ font-size: 50px;
+}
+
+/* Video width should be large enough to display all of the media controls. */
+video {
+ border: 1px solid gray;
+ width: 500px;
+}
+</style>
+<video controls onloadeddata="this.onloadeddata = null; takeScreenshot();">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script>
+// Add a single cue at line -2, where it would be if there were controls visible
+// at the bottom. (This assumes that those controls are less than 50px high.)
+// cue at line -1.
+var video = document.querySelector("video");
+var track = video.addTextTrack("captions");
+var cue = new VTTCue(0, 1, "text");
+cue.line = -2;
+track.addCue(cue);
+track.mode = "showing";
+</script>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added.html
new file mode 100644
index 0000000000..23c27e418e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-added.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<link rel="match" href="track-cue-rendering-after-controls-added-ref.html">
+<title>Text track cue layout after controls are added</title>
+<style>
+::cue {
+ font-size: 50px;
+}
+</style>
+<!-- Width should be large enough to display all of the media controls. -->
+<video style="border:1px solid gray; width: 500px;">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script>
+// Add a cue that will overlap with the video controls.
+var video = document.querySelector("video");
+var track = video.addTextTrack("captions");
+track.addCue(new VTTCue(0, 1, "text"));
+track.mode = "showing";
+
+video.onloadeddata = function() {
+ // Double nesting of requestAnimationFrame to
+ // make sure cue layout and paint happens.
+ window.requestAnimationFrame(function() {
+ window.requestAnimationFrame(function() {
+ video.controls = true;
+ // Wait for the relayout before screenshot.
+ window.requestAnimationFrame(function() {
+ takeScreenshot();
+ });
+ });
+ });
+};
+</script>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed-ref.html
new file mode 100644
index 0000000000..96afaef346
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<title>Text track cue layout after controls are removed (reference)</title>
+<style>
+::cue {
+ font-size: 50px;
+}
+
+video {
+ border: 1px solid gray;
+}
+</style>
+<video onloadeddata="this.onloadeddata = null; takeScreenshot();">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script>
+// Add a single cue at line -2, where it would be if there were controls visible
+// at the bottom. (This assumes that those controls are less than 50px high.)
+// cue at line -1.
+var video = document.querySelector("video");
+var track = video.addTextTrack("captions");
+var cue = new VTTCue(0, 1, "text");
+cue.line = -2;
+track.addCue(cue);
+track.mode = "showing";
+</script>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed.html
new file mode 100644
index 0000000000..76019c9b41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<link rel="match" href="track-cue-rendering-after-controls-removed-ref.html">
+<title>Text track cue layout after controls are removed</title>
+<style>
+::cue {
+ font-size: 50px;
+}
+</style>
+<video controls style="border:1px solid gray">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script>
+// Add a cue that will overlap with the video controls.
+var video = document.querySelector("video");
+var track = video.addTextTrack("captions");
+track.addCue(new VTTCue(0, 1, "text"));
+track.mode = "showing";
+
+video.onloadeddata = function() {
+ // Double nesting of requestAnimationFrame to
+ // make sure cue layout and paint happens.
+ window.requestAnimationFrame(function() {
+ window.requestAnimationFrame(function() {
+ // Remove the controls. The cue should not move.
+ video.controls = false;
+ takeScreenshot();
+ });
+ });
+};
+</script>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html
new file mode 100644
index 0000000000..427189f6fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Empty cues</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var video = document.createElement("video");
+ video.src = getVideoURI("/media/test");
+ video.addTextTrack("captions", "regular captions track", "en");
+ video.textTracks[0].addCue(new VTTCue(0, 4, ""));
+
+ video.onplaying = t.step_func_done();
+ video.play();
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit-ref.html
new file mode 100644
index 0000000000..8354041eb2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<style>
+.container {
+ position: relative;
+ display: inline-block;
+ width: 320px;
+ height: 240px;
+}
+.cue {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ overflow: hidden;
+}
+.cue > span {
+ font-family: sans-serif;
+ background: green;
+ color: green;
+ font-size: 120px;
+ padding: 2px;
+}
+</style>
+<div class="container">
+ <video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+ </video>
+ <div class="cue"><span>PAS</span></div>
+</div>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit.html
new file mode 100644
index 0000000000..d3dcee1037
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<link rel="match" href="track-cue-rendering-line-doesnt-fit-ref.html">
+<script>
+function addCue(track, cueData) {
+ var cue = new VTTCue(0, 10, 'XXX');
+ for (var prop in cueData)
+ cue[prop] = cueData[prop];
+ track.addCue(cue);
+}
+</script>
+<style>
+video::cue {
+ font-size: 120px;
+ color: green;
+ background-color: green;
+}
+</style>
+<video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+ <script>
+ var video = document.querySelector("video");
+ var track = video.addTextTrack('subtitles');
+ addCue(track, { line: 0, align: 'start', text: 'PAS' });
+ // This cue will not fit, and will not be displayed.
+ addCue(track, { line: 1, align: 'start', text: 'FAI' });
+ track.mode = 'showing';
+ </script>
+</video>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video-ref.html
new file mode 100644
index 0000000000..39461350b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<style>
+.container {
+ transform: translate(1px, 0px);
+ position: relative;
+ display: inline-block;
+ width: 320px;
+ height: 240px;
+}
+.cue {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ overflow: hidden;
+ text-align: start;
+}
+.cue > span {
+ font-family: sans-serif;
+ background: green;
+ color: green;
+ font-size: 50px;
+ padding: 2px;
+}
+</style>
+<div class="container">
+ <video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+ </video>
+ <div class="cue"><span>XXX</span></div>
+</div>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video.html
new file mode 100644
index 0000000000..69ca92e845
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/common/reftest-wait.js"></script>
+<link rel="match" href="track-cue-rendering-transformed-video-ref.html">
+<style>
+video {
+ transform: translate(1px, 0px);
+}
+video::cue {
+ font-size: 50px;
+ color: green;
+ background-color: green;
+}
+</style>
+<video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+ <script>
+ var video = document.querySelector('video');
+ var track = video.addTextTrack('subtitles');
+ var cue = new VTTCue(0, 10, 'XXX');
+ cue.align = 'start';
+ cue.line = 0;
+ track.addCue(cue);
+ track.mode = 'showing';
+ </script>
+</video>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange-dynamically-created-track-element.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange-dynamically-created-track-element.html
new file mode 100644
index 0000000000..f990bc8c72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange-dynamically-created-track-element.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>'cuechange' event on dynamically created track element</title>
+<meta name="timeout" content="long">
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+<script>
+/**
+ * 'cuechange' event should be correctly dispatched on the dynamically created
+ * track element.
+ */
+promise_test(function(t) {
+ const video = document.querySelector("video");
+ const track = document.createElement("track");
+ track.src = "resources/cues-chrono-order.vtt";
+ track.track.mode = "hidden";
+ video.appendChild(track);
+
+ const cueChangedPromise = new Promise(r => track.oncuechange = r);
+ video.src = getVideoURI("/media/test");
+ // 'TimeMarchesOn' algorithm will be run after calling 'play()', from which
+ // the 'cuechange' event would be dispatched.
+ video.play();
+ return cueChangedPromise;
+});
+</script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange.html
new file mode 100644
index 0000000000..2593401771
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>TextTrack's cues are indexed and updated in order during video playback</title>
+<meta name="timeout" content="long">
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/cues-chrono-order.vtt" kind="captions" default>
+ <script>
+ // Use the cuechange event on TextTrack.
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var testTrack = document.querySelector("track");
+
+ video.src = getVideoURI("/media/test");
+ video.oncanplaythrough = t.step_func(attemptTests);
+
+ function attemptTests() {
+ assert_equals(testTrack.track.cues.length, 3);
+ testTrack.oncuechange = t.step_func(cueChangedFromTrackElement);
+ video.play();
+ }
+
+ var currentCueIndex;
+ var cueChangeCount = 0;
+ function cueChangedFromTrackElement() {
+ currentCueIndex = Math.floor(cueChangeCount / 2);
+ currentCue = event.target.track.cues[currentCueIndex];
+ if (cueChangeCount % 2 == 0) {
+ // Cue entered.
+ assert_equals(currentCue, testTrack.track.activeCues[0]);
+ assert_equals(currentCue.id, (currentCueIndex + 1).toString());
+ }
+
+ ++cueChangeCount;
+ if (cueChangeCount == testTrack.track.cues.length * 2)
+ t.done();
+ }
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-exit.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-exit.html
new file mode 100644
index 0000000000..2d49c21178
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-exit.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>TextTrack's cues are indexed and updated in order during video playback</title>
+<meta name="timeout" content="long">
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/cues-chrono-order.vtt" kind="captions" default>
+ <script>
+ // Use the enter and exit events on TextTrackCue.
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var testTrack = document.querySelector("track");
+
+ video.src = getVideoURI("/media/test");
+
+ video.oncanplaythrough = t.step_func(attemptTests);
+
+ function attemptTests() {
+ assert_equals(testTrack.track.cues.length, 3);
+ for (var i = 0; i < testTrack.track.cues.length; i++) {
+ testTrack.track.cues[i].onenter = t.step_func(cueEntered);
+ testTrack.track.cues[i].onexit = t.step_func(cueExited);
+ }
+ video.play();
+ }
+
+ var cueCount = 0;
+ function cueEntered(event) {
+ var currentCue = event.target;
+
+ // This cue is the currently active cue.
+ assert_equals(currentCue, testTrack.track.activeCues[0]);
+ assert_equals(currentCue.id, (cueCount + 1).toString());
+ }
+
+ function cueExited() {
+ ++cueCount;
+ if (cueCount == testTrack.track.cues.length)
+ t.done();
+ }
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-seeking.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-seeking.html
new file mode 100644
index 0000000000..ae56e16205
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-seeking.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>TextTrack's cue onenter handler called when seeked onto</title>
+<meta name="timeout" content="long">
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/cues-chrono-order.vtt" kind="captions" default>
+ <script>
+ // Check that the onenter event is called for the right cue when seeking on the video element.
+ // Based on the spec step 4 [1], after a seek happens, the missed cues should be empty,
+ // so any cues before the target time should not receive enter event.
+ // [1] https://html.spec.whatwg.org/multipage/media.html#time-marches-on
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var testTrack = document.querySelector("track");
+
+ video.src = getVideoURI("/media/test");
+
+ video.oncanplaythrough = t.step_func(attemptTests);
+
+ function attemptTests() {
+ assert_equals(testTrack.track.cues.length, 3);
+ const targetTime = 4.0000000004;
+
+ for (let cue of testTrack.track.cues) {
+ if (cue.endTime > targetTime) {
+ cue.onenter = t.step_func(_=>t.done());
+ } else {
+ cue.onenter = t.unreached_func("onenter called for wrong cue");
+ }
+ }
+
+ video.currentTime = targetTime;
+ }
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-missed.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-missed.html
new file mode 100644
index 0000000000..2acae212d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-missed.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>Events are triggered for missed (skipped) cues during normal playback</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/missed-cues.vtt" default>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var testTrack = document.querySelector("track");
+
+ video.src = getVideoURI("/media/test");
+
+ video.onended = t.step_func_done();
+
+ video.oncanplaythrough = t.step_func(function() {
+ video.oncanplaythrough = null;
+ video.currentTime = 5.00;
+ runTests();
+ });
+
+ testTrack.onload = t.step_func(runTests);
+
+ var cueCount;
+ var eventCount = 0;
+ function runTests() {
+ eventCount++;
+
+ if(eventCount != 2)
+ return;
+
+ assert_equals(testTrack.track.cues.length, 7);
+
+ for (cueCount = 2; cueCount < testTrack.track.cues.length; cueCount++) {
+ var cue = testTrack.track.cues[cueCount];
+
+ cue.onenter = t.step_func(cueEnteredOrExited);
+ cue.onexit = t.step_func(cueEnteredOrExited);
+ }
+
+ // Test events for missed cues, which are cues with ids
+ // from 3 to 7 in the file resources/missed-cues.vtt.
+ cueCount = 3;
+ video.play();
+ }
+
+ function cueEnteredOrExited(event) {
+ var currentCue = event.target;
+ assert_equals(testTrack.track.cues.getCueById(cueCount).text, currentCue.text);
+ assert_equals(currentCue.id, cueCount.toString());
+
+ if (event.type == "exit")
+ cueCount++;
+ }
+
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-pause-on-exit.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-pause-on-exit.html
new file mode 100644
index 0000000000..eaf7e2a1d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-pause-on-exit.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Video is paused after cues having pause-on-exit flag are processed</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/simple-captions.vtt" default>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var track = document.querySelector("track");
+ track.onload = t.step_func(function() {
+ assert_equals(track.track.cues.length, 4);
+ for (var i = 0; i < track.track.cues.length; ++i) {
+ var cue = track.track.cues[i];
+ if (i % 2 == 0) {
+ cue.pauseOnExit = true;
+ cue.onexit = t.step_func(function(event) {
+ assert_true(video.paused);
+
+ video.play();
+
+ if (event.target.id == 2)
+ t.done();
+ });
+ }
+ }
+ video.src = getVideoURI("/media/test");
+ video.currentTime = 4.00;
+ video.play();
+ assert_false(video.paused);
+ });
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-seeking.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-seeking.html
new file mode 100644
index 0000000000..99cd2d550e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-seeking.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>TextTrack's activeCues are indexed and updated during video playback</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/cues-overlapping.vtt" kind="subtitles" default>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var track = document.querySelector("track");
+ track.onload = t.step_func(function() {
+ assert_equals(track.track.cues.length, 3);
+ video.src = getVideoURI("/media/test");
+ video.currentTime = 0.5;
+ });
+
+ var seekedCount = 0;
+ video.onseeked = t.step_func(function() {
+ ++seekedCount;
+
+ assert_equals(video.currentTime, seekedCount * 0.5);
+ assert_equals(track.track.activeCues.length, seekedCount - 1);
+ video.currentTime = (seekedCount + 1) * 0.5;
+
+ if (seekedCount == 4)
+ t.done();
+ });
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-sorted-before-dispatch.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-sorted-before-dispatch.html
new file mode 100644
index 0000000000..edc202f435
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cues-sorted-before-dispatch.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>All events are triggered in chronological order</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/sorted-dispatch.vtt" default>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ video.src = getVideoURI("/media/test");
+ var track = document.querySelector("track");
+
+ track.onload = t.step_func(function() {
+ var cues = track.track.cues;
+ assert_equals(cues.length, 8);
+
+ for (var i = 0; i < cues.length; ++i) {
+ cues[i].onenter = t.step_func(cueEnteredOrExited);
+ cues[i].onexit = t.step_func(cueEnteredOrExited);
+ }
+
+ video.play();
+ });
+
+ var cueTimings = [];
+ function cueEnteredOrExited(event) {
+ var currentCue = event.target;
+
+ if (event.type == "exit")
+ cueTimings.push(currentCue.endTime);
+ else
+ cueTimings.push(currentCue.startTime);
+ }
+
+ video.onended = t.step_func_done(function() {
+ assert_equals(cueTimings.length, 14);
+ var time = 0;
+ for (var i = 0; i < cueTimings.length; ++i) {
+ assert_less_than_equal(time, cueTimings[i], "cueTimings[" + i + "]");
+ time = cueTimings[i];
+ }
+ });
+
+ video.currentTime = 5;
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-data-url.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-data-url.html
new file mode 100644
index 0000000000..26ff90d56d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-data-url.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>track element data: URL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+[null, "anonymous", "use-credentials"].forEach(function(crossOriginValue) {
+ async_test(function() {
+ var video = document.createElement('video');
+ if (crossOriginValue !== null) {
+ video.setAttribute('crossorigin', crossOriginValue);
+ }
+ document.body.appendChild(video);
+ var t = document.createElement('track');
+ t.onload = this.step_func_done(function() {
+ assert_equals(t.track.cues.length, 1);
+ assert_equals(t.track.cues[0].startTime, 1);
+ assert_equals(t.track.cues[0].endTime, 2);
+ assert_equals(t.track.cues[0].id, 'x');
+ assert_equals(t.track.cues[0].text, 'test');
+ });
+ t.onerror = this.step_func(function() {
+ assert_unreached('got error event');
+ });
+ t.src = 'data:text/vtt,'+encodeURIComponent('WEBVTT\n\nx\n00:00:01.000 --> 00:00:02.000\ntest\n\n');
+ t.track.mode = 'showing';
+ video.appendChild(t);
+ }, document.title + ' ' + (crossOriginValue ? crossOriginValue : 'No CORS'));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-default-attribute.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-default-attribute.html
new file mode 100644
index 0000000000..3e8c547fc3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-default-attribute.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>A track with the "default" attribute loads automatically</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track kind="captions" src="resources/default-styles.vtt">
+ <track kind="captions" src="resources/metadata-area.vtt">
+ <track kind="captions" src="resources/webvtt-file.vtt" id="default" default>
+ <script>
+ async_test(function(t) {
+ var timer = null;
+ var tracks = document.querySelectorAll("track");
+ for (var track of tracks) {
+ track.onload = t.step_func(function() {
+ assert_equals(event.target.readyState, HTMLTrackElement.LOADED);
+ assert_equals(event.target.id, "default");
+ assert_true(event.target.default);
+ // End the test after a brief pause so we allow other tracks to load if they will.
+ if (timer)
+ clearTimeout(timer);
+ timer = t.step_timeout(t.step_func_done(), 200);
+ });
+ }
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-delete-during-setup.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-delete-during-setup.html
new file mode 100644
index 0000000000..ce9f73335a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-delete-during-setup.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Track deletion during setup</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/metadata.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+ var track = document.querySelector("track");
+ t.step_timeout(function() {
+ video.parentNode.removeChild(video);
+ }, 61);
+
+ track.onload = t.step_func(function() {
+ var track2 = document.createElement("track");
+ video.appendChild(track2);
+ t.step_timeout(t.step_func_done(), 100);
+ });
+
+ assert_equals(track.readyState, HTMLTrackElement.NONE);
+ assert_equals(track.track.mode, "disabled");
+ track.track.mode = "hidden";
+
+ video.src = getVideoURI("/media/test");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled-addcue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled-addcue.html
new file mode 100644
index 0000000000..038e6f6ba7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled-addcue.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<title>Adding cues to a disabled text track</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var cueDuration = 0.1;
+ var video = document.createElement("video");
+ var track = video.addTextTrack("subtitles");
+ track.mode = "disabled";
+
+ for (var i = 0; i < 10; ++i) {
+ var start = i * cueDuration;
+ var end = start + cueDuration;
+ track.addCue(new VTTCue(start, end, "Test Cue " + i));
+ }
+
+ // Waiting for 2 cue durations to elapse.
+ video.ontimeupdate = t.step_func(function(event) {
+ if (event.target.currentTime < (2 * cueDuration))
+ return;
+
+ // End test after at least 2 cueDurations to make sure the test
+ // would have gone through the period where the first 2 cues would
+ // have been rendered if the track was not disabled.
+ t.done();
+ });
+
+ video.src = getVideoURI("/media/test");
+ video.play();
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled.html
new file mode 100644
index 0000000000..d517b9d12c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-disabled.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Disabling a track</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track kind="subtitles" src="resources/captions.vtt"/>
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+ video.textTracks[0].mode = "disabled";
+
+ // Waiting for the duration of the first cue to elapse.
+ video.ontimeupdate = t.step_func(function (event) {
+ if (event.target.currentTime < 1)
+ return;
+
+ // End test after the duration of the first cue to make sure
+ // the test would have gone through the period where this cue
+ // would have been rendered if the track was not disabled.
+ t.done();
+ });
+
+ video.src = getVideoURI("/media/test");
+ video.play();
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-dom-change.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-dom-change.html
new file mode 100644
index 0000000000..ff447f33f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-dom-change.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Simple DOM mutations with track element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ var video = document.createElement("video");
+ var testTrack = document.createElement("track");
+
+ // Append the track element to the video element.
+ video.appendChild(testTrack);
+
+ // Set the mode of the text track to "showing".
+ testTrack.track.mode = "showing";
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-aborted-load.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-aborted-load.html
new file mode 100644
index 0000000000..234e087313
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-aborted-load.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>HTMLTrackElement 'src' attribute changed, load pending</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/media.html#start-the-track-processing-model">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video></video>
+<script>
+async_test(t => {
+ const track = document.createElement('track');
+ track.onload = t.unreached_func('first source should not load');
+ track.onerror = t.step_func_done();
+ track.src = 'resources/settings.vtt?pipe=trickle(d3600)';
+ track.track.mode = 'hidden';
+ document.querySelector('video').appendChild(track);
+ t.step_timeout(() => {
+ track.src = 'resources/entities.vtt';
+ }, 0);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html
new file mode 100644
index 0000000000..dd97d0522d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<title>HTMLTrackElement 'src' attribute mutations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/settings.vtt" default>
+ <script>
+ async_test(function(t) {
+ var cues = null;
+ var testTrack = document.querySelector("track");
+ var stage = 0;
+ var timer = null;
+ function step_onLoad() {
+ switch (stage) {
+ case 0:
+ cues = testTrack.track.cues;
+ assert_equals(testTrack.readyState, HTMLTrackElement.LOADED, "readyState after first loading of the track");
+ assert_equals(cues.length, 4, "Number of cues after first loading of the track");
+ ++stage;
+ testTrack.src = "resources/non-existing-file.vtt"; // this should fail
+ break;
+ case 1:
+ case 3:
+ case 4:
+ assert_unreached("'error' event did not fire, stage = " + stage);
+ break;
+ case 2:
+ assert_equals(testTrack.readyState, HTMLTrackElement.LOADED, "readyState after loading of the second track");
+ assert_equals(cues.length, 4, "Number of cues after loading of the second track");
+ assert_equals(cues[cues.length-1].text, 'I said Bear is coming now!!!! Tab separators.', "Last cue content check");
+ ++stage;
+ testTrack.src = ""; // this should fail
+ assert_equals(cues.length, 0, "cues list is reset immediately after 'src' mutation with the new URL");
+ // This should raise onError event. If no, we'll know about this after some time.
+ timer = t.step_timeout(t.unreached_func("'error' event is not fired when an empty URL is set"), 100);
+ break;
+ default:
+ assert_unreached("unexpected stage number = " + stage);
+ break;
+ }
+ }
+
+ function step_onError() {
+ switch (stage) {
+ case 0:
+ case 2:
+ assert_unreached("'error' event fired, stage = " + stage);
+ break;
+ case 1:
+ assert_equals(cues, testTrack.track.cues, ".cues object are the same after 'src' attr mutation");
+ assert_equals(cues.length, 0, "Number of cues after trying to load non-existing url");
+ assert_equals(testTrack.readyState, HTMLTrackElement.ERROR, "readyState after trying to load non-existing url");
+ ++stage;
+ testTrack.src = "resources/settings.vtt";
+ break;
+ case 3:
+ clearTimeout(timer);
+ assert_equals(testTrack.readyState, HTMLTrackElement.ERROR, "readyState after setting an empty URL");
+ assert_equals(cues, testTrack.track.cues, ".cues object are the same after 'src' attr mutation");
+ assert_equals(cues.length, 0, "Number of cues with an empty URL set");
+ ++stage;
+ testTrack.src = "resources/settings.vtt";
+ // error should happen when we remove `src` during loading, so we have to wait a task because loading starts asynchronously.
+ t.step_timeout(() => {
+ testTrack.removeAttribute('src');
+ // This should raise onError event, so we'll wait for it for some time
+ timer = t.step_timeout(t.unreached_func("'error' event is not fired when an empty URL is set"), 100);
+ }, 0);
+ break;
+ case 4:
+ clearTimeout(timer);
+ assert_equals(testTrack.readyState, HTMLTrackElement.ERROR, "readyState after removing 'src' attr");
+ assert_equals(cues.length, 0, "Number of cues after removing 'src' attr");
+ t.done();
+ break;
+ default:
+ assert_unreached("unexpected stage number = " + stage);
+ break;
+ }
+ }
+
+ testTrack.onload = t.step_func(step_onLoad);
+ testTrack.onerror = t.step_func(step_onError);
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html
new file mode 100644
index 0000000000..f3c78668b4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>HTMLTrackElement 'src' attribute mutations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/settings.vtt" default>
+ <script>
+ async_test(function(t) {
+ var cues = null;
+ var testTrack = document.querySelector("track");
+ var stage = 0;
+ function step_onLoad() {
+ switch (stage) {
+ case 0:
+ cues = testTrack.track.cues;
+ assert_equals(testTrack.readyState, HTMLTrackElement.LOADED, "readyState after first loading of the track");
+ assert_equals(cues.length, 4, "Number of cues after first loading of the track");
+ assert_equals(cues[cues.length-1].text, 'I said Bear is coming now!!!! Tab separators.', "Last cue content check");
+ ++stage;
+ testTrack.src = "resources/entities.vtt";
+ assert_equals(cues.length, 0, "cues list is reset immediately after 'src' mutation with the new URL");
+ break;
+ case 1:
+ assert_equals(testTrack.readyState, HTMLTrackElement.LOADED), "readyState after loading of the second track";
+ assert_equals(cues, testTrack.track.cues, ".cues object are the same after 'src' attr mutation");
+ assert_equals(cues.length, 7, "Number of cues after loading of the second track");
+ assert_equals(cues[cues.length-1].text, 'This & is parsed to the same as &amp;.', "Last cue content check");
+ ++stage;
+ testTrack.src = "resources/settings.vtt";
+ break;
+ case 2:
+ assert_equals(testTrack.readyState, HTMLTrackElement.LOADED, "readyState after after loading of the first track again");
+ assert_equals(cues[cues.length-1].text, 'I said Bear is coming now!!!! Tab separators.', "Last cue content check");
+ assert_equals(cues, testTrack.track.cues, ".cues object are the same after 'src' attr mutation");
+ assert_equals(cues.length, 4, "Number of cues after loading of the first track");
+ ++stage;
+ testTrack.src = "resources/settings.vtt";
+ // This should not raise onLoad or onError event, so we'll wait for it for some time
+ t.step_timeout(t.step_func_done(function() {
+ assert_equals(testTrack.readyState, HTMLTrackElement.LOADED, "readyState after changing 'src' to the same value");
+ assert_equals(cues, testTrack.track.cues, ".cues object are the same after 'src' attr mutation");
+ assert_equals(cues.length, 4, "Number of cues after changing 'src' to the same value");
+ }, 100));
+ break;
+ case 3:
+ assert_unreached("'load' event should not fire, stage = " + stage);
+ break;
+ }
+ }
+
+ testTrack.onload = t.step_func(step_onLoad);
+ testTrack.onerror = t.unreached_func("'error' event should not fire");
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-helpers.js b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-helpers.js
new file mode 100644
index 0000000000..09c85dd7bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-helpers.js
@@ -0,0 +1,83 @@
+function enableAllTextTracks(textTracks) {
+ for (var i = 0; i < textTracks.length; i++) {
+ var track = textTracks[i];
+ if (track.mode == "disabled")
+ track.mode = "hidden";
+ }
+}
+
+function assert_cues_equal(cues, expected) {
+ assert_equals(cues.length, expected.length);
+ for (var i = 0; i < cues.length; i++) {
+ assert_equals(cues[i].id, expected[i].id);
+ assert_equals(cues[i].startTime, expected[i].startTime);
+ assert_equals(cues[i].endTime, expected[i].endTime);
+ assert_equals(cues[i].text, expected[i].text);
+ }
+}
+
+function assert_cues_match(cues, expected) {
+ assert_equals(cues.length, expected.length);
+ for (var i = 0; i < cues.length; i++) {
+ var cue = cues[i];
+ var expectedItem = expected[i];
+ for (var property of Object.getOwnPropertyNames(expectedItem))
+ assert_equals(cue[property], expectedItem[property]);
+ }
+}
+
+function assert_cues_html_content(cues, expected) {
+ assert_equals(cues.length, expected.length);
+ for (var i = 0; i < cues.length; i++) {
+ var expectedItem = expected[i];
+ var property = Object.getOwnPropertyNames(expectedItem)[0];
+ var propertyValue = expectedItem[property];
+ assert_equals(propertyValue(cues[i]), expectedItem.expected);
+ }
+}
+
+function check_cues_from_track(src, func) {
+ async_test(function(t) {
+ var video = document.createElement("video");
+ var trackElement = document.createElement("track");
+ trackElement.src = src;
+ trackElement.default = true;
+ video.appendChild(trackElement);
+
+ trackElement.onload = t.step_func_done(function() {
+ func(trackElement.track);
+ });
+ }, "Check cues from " + src);
+}
+
+function assert_cue_fragment(cue, children) {
+ var fragment = createFragment(children);
+ assert_true(fragment.isEqualNode(cue.getCueAsHTML()));
+}
+
+function assert_cue_fragment_as_textcontent(cue, children) {
+ var fragment = createFragment(children);
+ assert_equals(cue.getCueAsHTML().textContent, fragment.textContent);
+}
+
+function createFragment(children) {
+ var fragment = document.createDocumentFragment();
+ cloneChildrenToFragment(fragment, children);
+ return fragment;
+}
+
+function cloneChildrenToFragment(root, children) {
+ for (var child of children) {
+ var childElement;
+ if (child.type == "text") {
+ childElement = document.createTextNode(child.value);
+ } else {
+ childElement = document.createElement(child.type);
+ var styles = child.style || {};
+ for (var attr of Object.getOwnPropertyNames(styles))
+ childElement[attr] = styles[attr];
+ cloneChildrenToFragment(childElement, child.value);
+ }
+ root.appendChild(childElement);
+ }
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-id.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-id.html
new file mode 100644
index 0000000000..f0223fda64
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-id.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>TextTrack "id" attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track id="LoremIpsum" src="resources/captions-fast.vtt" default>
+ <script>
+ test(function() {
+ var video = document.querySelector("video");
+ var track = document.querySelector("track");
+ var textTrack = track.track;
+
+ // Test default attribute value.
+ assert_equals(textTrack.id, "LoremIpsum");
+ assert_equals(video.textTracks[0].id, "LoremIpsum");
+
+ // Make sure we can look up tracks by id.
+ assert_equals(video.textTracks.getTrackById("LoremIpsum"), textTrack);
+
+ // Test that it's readonly.
+ textTrack.id = "newvalue";
+ assert_equals(textTrack.id, "LoremIpsum");
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-insert-after-load.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-insert-after-load.html
new file mode 100644
index 0000000000..28b4f82688
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-insert-after-load.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Inserting a track element immediately after video load</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ var video = document.createElement('video');
+ video.src = getVideoURI('/media/test');
+ video.load();
+ video.appendChild(document.createElement('track'));
+ video.onloadedmetadata = t.step_func_done();
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-large-timestamp.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-large-timestamp.html
new file mode 100644
index 0000000000..bae1852cf8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-large-timestamp.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Very large timestamp is parsed correctly</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/large-timestamp.vtt" default>
+ <script>
+ async_test(function(t) {
+ var testTrack = document.querySelector("track");
+ testTrack.onload = t.step_func_done(function() {
+ assert_equals(testTrack.track.cues.length, 1);
+ var cue = testTrack.track.cues[0];
+ assert_equals(parseInt(cue.id), 1);
+ assert_equals(cue.startTime / 3600, 1234567);
+ assert_equals(cue.endTime / 3600, 1234567890);
+ });
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-error-readyState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-error-readyState.html
new file mode 100644
index 0000000000..8e232bff53
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-error-readyState.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Error event on HTMLTrackElement and ERROR readyState on TextTrack</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="junk" default>
+ <script>
+ async_test(function(t) {
+ var track = document.querySelector("track");
+ track.onerror = t.step_func_done(function() {
+ assert_equals(track.readyState, HTMLTrackElement.ERROR);
+ });
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-element-readyState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-element-readyState.html
new file mode 100644
index 0000000000..62a68f6543
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-element-readyState.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Load event on HTMLTrackElement and LOADED readyState on TextTrack when src is set on the element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/webvtt-file.vtt" default>
+ <script>
+ async_test(function(t) {
+ var track = document.querySelector("track");
+ track.onload = t.step_func_done(function() {
+ assert_equals(track.readyState, HTMLTrackElement.LOADED);
+ });
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-src-readyState.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-src-readyState.html
new file mode 100644
index 0000000000..e569eeb96f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-load-from-src-readyState.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Load event on HTMLTrackElement and LOADED readyState on TextTrack when src is set from JavaScript</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track>
+</video>
+<script>
+async_test(function(t) {
+ var track = document.querySelector("track");
+ assert_equals(track.readyState, HTMLTrackElement.NONE);
+
+ track.onload = t.step_func_done(function() {
+ assert_equals(track.readyState, HTMLTrackElement.LOADED);
+ });
+
+ track.src = "resources/webvtt-file.vtt";
+ track.track.mode = "hidden";
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-disabled.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-disabled.html
new file mode 100644
index 0000000000..6b46bf4e34
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-disabled.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Cues are properly removed from the active cue list when their track changes mode to disabled</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/captions-gaps.vtt" kind="captions" default >
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var testTrack = document.querySelector("track");
+
+ video.src = getVideoURI("/media/counting");
+ video.oncanplaythrough = t.step_func(startTest);
+ video.onseeked = t.step_func_done(seeked);
+
+ function startTest() {
+ // Set the mode of the text track to "showing".
+ testTrack.track.mode = "showing";
+ // Seek to a time with a caption.
+ video.currentTime = 1.5;
+ }
+
+ function seeked() {
+ // Set the mode of the text track to "hidden", then to "showing" again.
+ testTrack.track.mode = "hidden";
+ testTrack.track.mode = "showing";
+
+ // Set the mode of the text track to "disabled".
+ testTrack.track.mode = "disabled";
+ }
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html
new file mode 100644
index 0000000000..3ec47a39e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<title>A track appended after the initial track configuration does not change other tracks</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track kind="metadata" src="resources/metadata.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector('video');
+
+ var track1 = document.querySelectorAll('track')[0];
+ assert_equals(track1.readyState, HTMLTrackElement.NONE);
+ assert_equals(track1.track.mode, 'disabled');
+
+ video.src = getVideoURI('/media/test');
+ video.oncanplaythrough = t.step_func(canplaythrough);
+ track1.onload = t.step_func(metadataTrackLoaded);
+
+ function canplaythrough() {
+ // check initial metadata track state.
+ assert_equals(track1.readyState, HTMLTrackElement.NONE);
+ assert_equals(track1.track.mode, 'disabled');
+ assert_equals(track1.track.cues, null);
+ track1.track.mode = 'hidden';
+ }
+
+ function metadataTrackLoaded() {
+ // check metadata track state.
+ assert_equals(track1.readyState, HTMLTrackElement.LOADED);
+ assert_equals(track1.track.mode, 'hidden');
+ assert_equals(track1.track.cues.length, 12);
+ assert_equals(track1.track.cues[11].startTime, 22);
+
+ // Add a caption track, and explicitly enable it.
+ track2 = document.createElement('track');
+ track2.setAttribute('kind', 'captions');
+ track2.setAttribute('default', 'default');
+ track2.setAttribute('src', 'resources/webvtt-file.vtt');
+ track2.track.mode = 'showing';
+ track2.onload = t.step_func(captionsTrackLoaded);
+ video.appendChild(track2);
+ }
+
+ function captionsTrackLoaded() {
+ // Check that metadata track state has not changed.
+ assert_equals(track1.readyState, HTMLTrackElement.LOADED);
+ assert_equals(track1.track.mode, 'hidden');
+ // and that the caption track state is correct.
+ assert_equals(track2.readyState, HTMLTrackElement.LOADED);
+ assert_equals(track2.track.mode, 'showing');
+
+ video.textTracks.onaddtrack = t.step_func_done(trackAdded);
+ // add a subtitle track with video.addTextTrack().
+ track3 = video.addTextTrack('subtitles', 'Subtitle Track', 'en');
+ track3.mode = 'showing';
+ }
+
+ function trackAdded(event) {
+ // Check that metadata track state has not changed.
+ assert_equals(track1.readyState, HTMLTrackElement.LOADED);
+ assert_equals(track1.track.mode, 'hidden');
+ // and that the caption track state has not changed.
+ assert_equals(track2.readyState, HTMLTrackElement.LOADED);
+ assert_equals(track2.track.mode, 'showing');
+ // and that the subtitle track state is correct.
+ assert_equals(event.target, video.textTracks);
+ assert_true(event instanceof window.TrackEvent);
+ assert_equals(event.track, video.textTracks[video.textTracks.length - 1]);
+ assert_equals(track3.mode, 'showing');
+ }
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-triggers-loading.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-triggers-loading.html
new file mode 100644
index 0000000000..2e29d70469
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode-triggers-loading.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>A "metadata" track does not load automatically, but it does load when the mode is changed</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track kind="metadata" src="resources/metadata.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ // Check initial metadata track state.
+ var track = document.querySelectorAll("track")[0];
+ assert_equals(track.readyState, HTMLTrackElement.NONE);
+ assert_equals(video.textTracks[0].mode, "disabled");
+
+ video.src = getVideoURI("/media/test");
+ video.oncanplaythrough = t.step_func(canplaythrough);
+ track.onload = t.step_func_done(trackLoaded);
+
+ function trackLoaded() {
+ assert_equals(track.readyState, HTMLTrackElement.LOADED);
+ assert_equals(track.track.mode, "hidden");
+ assert_equals(video.textTracks[0].cues.length, 12);
+ assert_equals(video.textTracks[0].cues[11].startTime, 22);
+ }
+
+ function canplaythrough() {
+ assert_equals(track.readyState, HTMLTrackElement.NONE);
+ assert_equals(video.textTracks[0].mode, "disabled");
+ assert_equals(video.textTracks[0].cues, null);
+ // Change metadata track mode so it loads.
+ video.textTracks[0].mode = "hidden";
+ }
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html
new file mode 100644
index 0000000000..206ac9968f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>TextTrack mode attribute</title>
+<meta name="timeout" content="long">
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/captions-fast.vtt" default>
+ <script>
+ async_test(function(t) {
+ var video = document.querySelector("video");
+ var track = document.querySelector("track");
+ if (track.readyState != HTMLTrackElement.LOADED) {
+ assert_not_equals(track.readyState, HTMLTrackElement.ERROR,
+ "track failed to load resource.");
+ track.onload = t.step_func(trackLoaded);
+ } else {
+ trackLoaded();
+ }
+
+ var cueCount = 0;
+ var textTrack;
+ function trackLoaded() {
+ textTrack = track.track;
+ // Test default attribute value.
+ assert_equals(textTrack.mode, "showing");
+ assert_equals(video.textTracks[0].mode, "showing");
+ // Set to bogus value, should return default.
+ var value = "bogus";
+ textTrack.mode = value;
+ assert_equals(textTrack.mode, "showing");
+ assert_equals(video.textTracks[0].mode, "showing");
+
+ // Set to numeric value (no longer supported), should return default.
+ textTrack.mode = 2;
+ assert_equals(textTrack.mode, "showing");
+ assert_equals(video.textTracks[0].mode, "showing");
+
+ // Set to known values.
+ setModeAndCheck("disabled");
+
+ video.src = getVideoURI("/media/test");
+ video.play();
+
+ // Wait for end of first cue (no events should fire while track is disabled).
+ video.ontimeupdate = () => {
+ if (video.currentTime > 0.4) {
+ testHiddenAndShowing();
+ video.ontimeupdate = null;
+ }
+ }
+ }
+
+ track.oncuechange = t.step_func(function(event) {
+ cueCount++;
+ // As the 'enter' and the 'exit' event would be fired for the second
+ // and the third cue, so there would be 4 times 'oncuechange' event.
+ if (cueCount == 4)
+ t.done();
+ });
+
+ function setModeAndCheck(value) {
+ textTrack.mode = value;
+ assert_equals(textTrack.mode, value);
+ assert_equals(video.textTracks[0].mode, value);
+ if (value == "disabled")
+ assert_equals(textTrack.cues, null);
+ }
+
+ function testHiddenAndShowing() {
+ setModeAndCheck("hidden");
+ setModeAndCheck("showing");
+ }
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-node-add-remove.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-node-add-remove.html
new file mode 100644
index 0000000000..2708879424
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-node-add-remove.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>Add and remove track node</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+test(function() {
+ var video = document.createElement('video');
+ var tracka = document.createElement('track');
+ video.appendChild(tracka);
+ var trackb = document.createElement('track');
+ video.appendChild(trackb);
+
+ // Adding tracks outside the DOM tree.
+ assert_array_equals(video.textTracks, [tracka.track, trackb.track]);
+
+ // Inserting the parent video element into the document.
+ document.body.appendChild(video);
+ assert_array_equals(video.textTracks, [tracka.track, trackb.track]);
+
+ // Inserting and removing another track in the document.
+ var trackc = document.createElement('track');
+ video.appendChild(trackc);
+ assert_array_equals(video.textTracks, [tracka.track, trackb.track, trackc.track]);
+
+ trackb.parentNode.removeChild(trackb);
+ assert_array_equals(video.textTracks, [tracka.track, trackc.track]);
+
+ // Removing the video from the document.
+ document.body.removeChild(video);
+ assert_array_equals(video.textTracks, [tracka.track, trackc.track]);
+
+ tracka.parentNode.removeChild(tracka);
+ assert_array_equals(video.textTracks, [trackc.track]);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-active-cue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-active-cue.html
new file mode 100644
index 0000000000..176e0065c5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-active-cue.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Removing an active cue</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video></video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+ video.src = getVideoURI("/media/test");
+
+ // Add a text track to the video element.
+ video.addTextTrack("captions", "regular captions track", "en");
+
+ // Add a cue to the track with enter event listener.
+ var cue = new VTTCue(0, 4, "Random");
+ cue.onenter = t.step_func_done(removeActiveCue);
+
+ var track = video.textTracks[0];
+ track.addCue(cue);
+
+ function removeActiveCue() {
+ assert_equals(track.activeCues.length, 1);
+
+ // Remove the cue while it is active.
+ track.removeCue(track.activeCues[0]);
+
+ // No crash. PASS.
+ }
+
+ // Play the video and remove cue when it becomes active.
+ video.play();
+ track.mode = "showing";
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-by-setting-innerHTML.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-by-setting-innerHTML.html
new file mode 100644
index 0000000000..95929bc83f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-by-setting-innerHTML.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Removing a track by setting video.innerHTML doesn't crash</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track default src="resources/captions-gaps.vtt">
+ <script>
+ // https://bugs.webkit.org/show_bug.cgi?id=100981
+ async_test(function(t) {
+ var firstSeek = true;
+ var video = document.querySelector('video');
+ video.onseeked = t.step_func(function() {
+ if (!firstSeek) {
+ t.done();
+ return;
+ }
+
+ // Remove the text track
+ video.innerHTML = '';
+
+ // Seek again to force a repaint.
+ video.currentTime = 7.9;
+ firstSeek = false;
+ });
+
+ video.currentTime = 0.5;
+ video.src = getVideoURI('/media/counting');
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-insert-ready-state.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-insert-ready-state.html
new file mode 100644
index 0000000000..91375f579e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-insert-ready-state.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>Attaching a media element again to the document, having a child track that failed loading doesn't block video from playing</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/no-webvtt.vtt" kind="captions" default>
+</video>
+<script>
+ async_test(function(t) {
+ var video = document.querySelector('video');
+ video.src = getVideoURI('/media/test');
+ video.oncanplaythrough = t.step_func(canplaythrough);
+
+ function canplaythrough() {
+ video.oncanplaythrough = null;
+ var track = document.querySelector('track');
+
+ // Track should have error as ready state.
+ assert_equals(track.readyState, HTMLTrackElement.ERROR);
+
+ // Remove the video element from body.
+ document.body.removeChild(video);
+
+ // Reset the video src attribute to re-trigger resource selection for tracks.
+ video.src = getVideoURI('/media/test');
+
+ // Append the video element back to the body.
+ document.body.appendChild(video);
+
+ assert_equals(track.readyState, HTMLTrackElement.ERROR);
+
+ video.onplaying = t.step_func_done();
+ video.play();
+ // The video should start playing.
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-quickly.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-quickly.html
new file mode 100644
index 0000000000..4be040c5f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-quickly.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Removing a track element before it has been processed doesn't crash</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="video_container"></div>
+<script>
+var mediaFile = getVideoURI("/media/test");
+document.getElementById("video_container").innerHTML = "<video src='" + mediaFile + "' controls ><track kind='captions' src='resources/simple-captions.vtt' default ></video>";
+test(function() {
+// https://bugs.webkit.org/show_bug.cgi?id=85095
+// Test passes if it doesn't crash.
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track-inband.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track-inband.html
new file mode 100644
index 0000000000..7dcfe68318
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track-inband.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <script src="/common/media.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ async_test(function(test)
+ {
+ var video = document.createElement("video");
+
+ // Create an out-of-band text track by adding a track element.
+ var trackElement = document.createElement('track');
+
+ trackElement.addEventListener("error", test.step_func(function()
+ {
+ assert_unreached("'error' event on track element should not fire.")
+ }));
+
+ video.appendChild(trackElement);
+ trackElement.src = 'resources/webvtt-file.vtt';
+ trackElement.track.mode = 'hidden';
+
+ assert_equals(video.textTracks.length, 1);
+ var outOfBandTrack = video.textTracks[0];
+
+ // Load a media file with an inband text track.
+ var inbandTrack = null;
+ var url = "resources/vp8-vorbis-webvtt.webm"
+
+ var firstAddTrackHandler = test.step_func(function()
+ {
+ assert_equals(event.target, video.textTracks);
+ assert_equals(event instanceof window.TrackEvent, true);
+ if (event.track == outOfBandTrack) {
+ return;
+ }
+
+ assert_equals(inbandTrack, null);
+ assert_equals(video.textTracks.length, 2);
+ assert_equals(event.track, video.textTracks[1]);
+ inbandTrack = event.track;
+
+ video.textTracks.removeEventListener("addtrack", firstAddTrackHandler);
+
+ // Clear .src to force the inband track to get destroyed.
+ video.src = "";
+
+ // Verify that the inband track was removed.
+ assert_not_equals(inbandTrack, null);
+ assert_equals(video.textTracks.length, 1);
+ assert_equals(video.textTracks[0], outOfBandTrack);
+
+ // Load the URL again to trigger another 'addtrack' event to make sure
+ // no 'removetrack' event was queued.
+ video.src = url;
+ video.textTracks.addEventListener("addtrack", test.step_func(function()
+ {
+ assert_equals(video.textTracks.length, 2);
+ test.done();
+ }));
+ });
+ video.textTracks.addEventListener("addtrack", firstAddTrackHandler);
+
+ video.textTracks.addEventListener("removetrack", test.step_func(function()
+ {
+ assert_unreached("'removetrack' event should not fire.")
+ }));
+
+ video.src = url;
+ }, "Tests that the 'removetrack' event is NOT fired for inband TextTrack on a failed load.");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html
new file mode 100644
index 0000000000..d5695cd302
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <script src="/common/media.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ async_test(function(test)
+ {
+ var video = document.createElement("video");
+ var track;
+
+ function trackRemoved()
+ {
+ assert_equals(event.target, video.textTracks);
+ assert_equals(event instanceof window.TrackEvent, true);
+ assert_equals(event.track, track);
+ test.done();
+ }
+
+ var trackElement = document.createElement('track');
+ video.appendChild(trackElement);
+
+ trackElement.src = 'resources/webvtt-file.vtt';
+ trackElement.track.mode = 'hidden';
+
+ assert_equals(video.textTracks.length, 1);
+
+ track = video.textTracks[0];
+ video.removeChild(trackElement);
+ video.textTracks.addEventListener("removetrack", test.step_func(trackRemoved));
+ }, "Tests that the 'removetrack' event is fired when an out-of-band TextTrack is removed.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-metadata.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-metadata.html
new file mode 100644
index 0000000000..c4d88a35f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-metadata.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<title>Multiple 'metadata' tracks with 'default'</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track kind="metadata" src="resources/default-styles.vtt" id="t1">
+ <track kind="metadata" src="resources/class.vtt" default id="t2hidden">
+ <track kind="metadata" src="resources/metadata-area.vtt" id="t3">
+ <track kind="metadata" src="resources/webvtt-file.vtt" default id="t4hidden">
+</video>
+<script>
+async_test(function() {
+ var video = document.querySelector('video');
+ video.onloadstart = this.step_func_done(function() {
+ assert_equals(video.textTracks.length, 4);
+ for (var track of video.textTracks) {
+ assert_equals(track.kind, 'metadata');
+
+ var trackElement = document.getElementById(track.id);
+ if (track.id.indexOf('hidden') != -1) {
+ assert_true(trackElement.default);
+ assert_equals(track.mode, 'hidden');
+ } else {
+ assert_false(trackElement.default);
+ assert_equals(track.mode, 'disabled');
+ }
+ }
+ });
+
+ video.src = getVideoURI("/media/test");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html
new file mode 100644
index 0000000000..522d067adf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>HTMLTrackElement Text Track Selection Task Order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+/**
+ * This test is used to ensure that we queue 'honor user preferences for automatic
+ * text track selection' as a macro task, not a micro task. In this test, we
+ * trigger a media event before queuing a text track selection task, and check
+ * the text track's mode to know whether the text track selection runs after the
+ * task for media event.
+ */
+async_test(function(t) {
+ let video = document.createElement("video");
+ video.play();
+ video.onplay = t.step_func(startedPlay);
+
+ // When we create a text track element, it queue a task to run automatic
+ // text track selection later.
+ let track = document.createElement("track");
+ track.default = true;
+ video.appendChild(track);
+ assert_equals(track.track.mode, "disabled", "Text track's mode is disabled by default.");
+
+ function startedPlay() {
+ assert_equals(track.track.mode, "disabled", "Text track selection hasn't started yet.");
+ track.onerror = t.step_func_done(trackError);
+ }
+
+ function trackError() {
+ assert_equals(track.track.mode, "showing", "Text track selection modified track's mode.");
+ t.done();
+ }
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list.html
new file mode 100644
index 0000000000..73241ce0d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>TextTrackCueList functionality: length, operator[], and getCueById()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/settings.vtt" kind="captions" default>
+ <script>
+ async_test(function(t) {
+ var testTrack = document.querySelector("track");
+
+ testTrack.onload = t.step_func_done(function() {
+ var cues = testTrack.track.cues;
+
+ // Testing TextTrackCueList length.
+ assert_equals(cues.length, 4);
+
+ // Testing TextTrackCueList [] operator.
+ assert_equals(cues[0].id, "1");
+ assert_equals(cues[3].id, "4");
+ assert_equals(cues[4], undefined);
+
+ // Testing TextTrackCueList getCueById().
+ assert_equals(cues.getCueById("1").startTime, 0);
+ assert_equals(cues.getCueById("4").startTime, 121);
+ assert_equals(cues.getCueById("junk"), null);
+ });
+ });
+ </script>
+</video>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-texttracks.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-texttracks.html
new file mode 100644
index 0000000000..4d006fcefb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-texttracks.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>TextTracks in a TextTrackList are kept in the correct order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track kind="captions" src="resources/webvtt-file.vtt">
+</video>
+<script>
+test(function() {
+ var video = document.querySelector("video");
+
+ // Add a track with video.addTextTrack().
+ video.addTextTrack("descriptions", "Descriptions Track", "en");
+
+ // Add a track element with DOM API.
+ var trackElement = document.createElement("track");
+ trackElement.setAttribute("kind", "chapters");
+ video.appendChild(trackElement);
+
+ // Verify track order.
+ assert_equals(video.textTracks.length, 3);
+ assert_equals(video.textTracks[0].kind, "captions");
+ assert_equals(video.textTracks[1].kind, "chapters");
+ assert_equals(video.textTracks[2].kind, "descriptions");
+
+ // Verify the default parameters of the text track object
+ // returned by addTextTrack().
+ assert_equals(video.textTracks[2].mode, "hidden");
+ assert_not_equals(video.textTracks[2].cues, null);
+ assert_equals(video.textTracks[2].cues.length, 0);
+
+ // Add another track element, it should insert
+ // before the addTextTrack() track.
+ trackElement = document.createElement("track");
+ trackElement.setAttribute("kind", "metadata");
+ video.appendChild(trackElement);
+
+ assert_equals(video.textTracks.length, 4);
+ assert_equals(video.textTracks[0].kind, "captions");
+ assert_equals(video.textTracks[1].kind, "chapters");
+ assert_equals(video.textTracks[2].kind, "metadata");
+ assert_equals(video.textTracks[3].kind, "descriptions");
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-positioning.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-positioning.html
new file mode 100644
index 0000000000..07ebfd622b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-positioning.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>Cue text position and alignment from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/align-positioning.vtt">
+ <track src="resources/align-positioning-bad.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ assert_equals(trackElements.length, video.textTracks.length);
+ for (var i = 0; i < trackElements.length; i++)
+ trackElements[i].onload = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 2)
+ return;
+
+ testTrack(0);
+ testTrackError(1);
+ t.done();
+ }
+
+ function testTrack(index) {
+ var expected = [
+ { position : 10, align : "start" },
+ { position : 20, align : "center" },
+ { position : 80, align : "end" }
+ ];
+
+ assert_cues_match(video.textTracks[index].cues, expected);
+ }
+
+ function testTrackError(index) {
+ var expected = [
+ { position : 10, align : "center" },
+ { position : "auto", align : "center" },
+ { position : "auto", align : "center" }
+ ];
+
+ assert_cues_match(video.textTracks[index].cues, expected);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-text-line-position.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-text-line-position.html
new file mode 100644
index 0000000000..deb389916a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-align-text-line-position.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>Cue alignment, line and text position from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/align-text-line-position.vtt">
+ <track src="resources/align-text-line-position-bad.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ assert_equals(trackElements.length, video.textTracks.length);
+ for (var i = 0; i < trackElements.length; i++)
+ trackElements[i].onload = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 2)
+ return;
+
+ testTrack(0);
+ testTrackError(1);
+ t.done();
+ }
+
+ function testTrack(index) {
+ var expected = [
+ { align : "start", position : 10, line : 0, snapToLines : false },
+ { align : "start", position : "auto", line : 0, snapToLines : true },
+ { align : "center", position : 80, line : 80, snapToLines : false },
+ { align : "end", position : 30, line : 5, snapToLines : true },
+ { align : "center", position : 60, line : -3, snapToLines : true }
+ ];
+
+ assert_cues_match(video.textTracks[index].cues, expected);
+ }
+
+ function testTrackError(index) {
+ var expected = [
+ { align : "center", position : "auto", line : "auto", snapToLines : true },
+ { align : "end", position : 0, line : "auto", snapToLines : true },
+ { align : "center", position : 60, line : -3, snapToLines : true }
+ ];
+
+ assert_cues_match(video.textTracks[index].cues, expected);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-alignment.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-alignment.html
new file mode 100644
index 0000000000..e8f47e876a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-alignment.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Cue alignment from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/alignment.vtt", testTrack);
+check_cues_from_track("resources/alignment-ltr.vtt", testTrack);
+
+check_cues_from_track("resources/alignment-bad.vtt", function(track) {
+ var expected = [
+ { align: "center" },
+ { align: "center" },
+ { align: "center" },
+ { align: "center" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+function testTrack(track) {
+ var expected = [
+ { align: "start" },
+ { align: "center" },
+ { align: "end" },
+ { align: "center" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-blank-lines.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-blank-lines.html
new file mode 100644
index 0000000000..114aebc38c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-blank-lines.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Cues are affected neither by multiple newlines \n, \r, and \r\n nor by the absence of a seperating line</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/cues.vtt", function(track) {
+ var expected = [
+ { id: "1", startTime: 0, endTime: 30.5, text: "Bear is Coming!!!!!" },
+ { id: "2", startTime: 31, endTime: 60.5, text: "I said Bear is coming!!!!" },
+ { id: "3", startTime: 61, endTime: 361200.5, text: "I said Bear is coming now!!!!" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+check_cues_from_track("resources/cues-no-separation.vtt", function(track) {
+ var expected = [
+ { id: "1", startTime: 0, endTime: 30.5, text: "Bear is Coming!!!!!\n2" },
+ { id: "", startTime: 31, endTime: 60.5, text: "I said Bear is coming!!!!" },
+ { id: "", startTime: 61, endTime: 361200.5, text: "I said Bear is coming now!!!!" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-bom.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-bom.html
new file mode 100644
index 0000000000..c138f96af5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-bom.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Parser properly ignores a UTF-8 BOM character at the beginning of a file and all other cues are properly parsed</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/bom.vtt" default>
+ <script>
+ async_test(function(t) {
+ var track = document.querySelector("track");
+
+ track.onload = t.step_func_done(function() {
+ var expected = [
+ {
+ id : "1",
+ startTime : 0,
+ endTime : 30.5,
+ text : "Bear is Coming!!!!!"
+ },
+ {
+ id : "2",
+ startTime : 31,
+ endTime : 1200.5,
+ text : "I said Bear is coming!!!!"
+ }
+ ];
+
+ var cues = track.track.cues;
+ assert_equals(cues.length, 2);
+ assert_cues_equal(cues, expected);
+ });
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-class-markup.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-class-markup.html
new file mode 100644
index 0000000000..ecc5a57497
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-class-markup.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<title>Tests cues with class markup &lt;c&gt;.</title>
+<meta name="timeout" content="long">
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/class.vtt", function(track) {
+ assert_equals(track.cues.length, 3);
+
+ var children = [
+ { type: "span", style: { className: "black" },
+ value: [ { type: "text", value: "Bear is Coming!!!!!" } ] }
+ ];
+ assert_cue_fragment(track.cues[0], children);
+
+ children = [
+ { type: "span", style: { className: "green" },
+ value: [ { type: "text", value: "I said Bear is coming!!!!" } ] }
+ ];
+ assert_cue_fragment(track.cues[1], children);
+
+ children = [
+ { type: "text", value: "I said " },
+ { type: "span", style: { className: "red uppercase" },
+ value: [ { type: "text", value: "Bear is coming now" } ] },
+ { type: "text", value: "!!!!" }
+ ];
+ assert_cue_fragment(track.cues[2], children);
+});
+
+check_cues_from_track("resources/class-bad.vtt", function(track) {
+ assert_equals(track.cues.length, 3);
+
+ var children = [
+ { type: "span", value: [ { type: "text", value: "Bear is Coming!!!!!" } ] },
+ { type: "text", value: "\nThe space signified an annotation start." }
+ ];
+ assert_cue_fragment(track.cues[0], children);
+
+ children = [
+ { type: "span", style: { className: "red&large" },
+ value: [ { type: "text", value: "I said Bear is coming!!!!" } ] },
+ { type: "text", value: "\nProbably should only allow characters that CSS allows in class names." }
+ ];
+ assert_cue_fragment(track.cues[1], children);
+
+ children = [
+ { type: "text", value: "I said " },
+ { type: "span", style: { className: "9red upper+case" },
+ value: [ { type: "text", value: "Bear is coming now" } ] },
+ { type: "text", value: "!!!!\nProbably should only allow characters that CSS allows in class names." }
+ ];
+ assert_cue_fragment(track.cues[2], children);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-identifiers.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-identifiers.html
new file mode 100644
index 0000000000..02b0a15187
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-identifiers.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Any text other than "-->" is recognized as optional cue identifier</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/cue-id.vtt", function(track) {
+ var expected = [
+ { id: "random_id", startTime: 0, endTime: 30.5, text: "Bear is Coming!!!!!" },
+ { id: "another random identifier", startTime: 31, endTime: 60.5, text: "I said Bear is coming!!!!" },
+ { id: "identifier--too", startTime: 61, endTime: 120.5, text: "I said Bear is coming now!!!!" },
+ { id: "identifier--too", startTime: 121, endTime: 180.5, text: "Duplicate identifier" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+check_cues_from_track("resources/cue-id-error.vtt", function(track) {
+ var expected = [
+ { id: "", startTime: 0, endTime: 30.5, text: "Bear is Coming!!!!!" },
+ { id: "", startTime: 31, endTime: 60.5, text: "I said Bear is coming!!!!" },
+ { id: "", startTime: 61, endTime: 1200.5, text: "I said Bear is coming now!!!!" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-no-id.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-no-id.html
new file mode 100644
index 0000000000..b2f4b77083
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-no-id.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Empty cue identifiers, but having "-->" leads to discarded cue</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/cue-no-id.vtt", testTrack);
+check_cues_from_track("resources/cue-no-id-error.vtt", testTrack);
+
+function testTrack(track) {
+ var expected = [
+ { id: "", startTime: 0, endTime: 30.5, text: "Bear is Coming!!!!!" },
+ { id: "", startTime: 31, endTime: 60.5, text: "I said Bear is coming!!!!" },
+ { id: "", startTime: 61, endTime: 1200.5, text: "I said Bear is coming now!!!!" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-recovery.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-recovery.html
new file mode 100644
index 0000000000..6a104916b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-recovery.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>A cue is recovered when a line with a "-->" is encountered without blank line separator</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/cue-recovery-header.vtt", testTrack);
+check_cues_from_track("resources/cue-recovery-note.vtt", testTrack);
+check_cues_from_track("resources/cue-recovery-cuetext.vtt", testTrack);
+
+function testTrack(track) {
+ var expected = [
+ { startTime: 0, endTime: 1, text: "Valid cue 1" },
+ { startTime: 2, endTime: 3, text: "Valid cue 2" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size-align.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size-align.html
new file mode 100644
index 0000000000..a1243a95e7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size-align.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Cue size and alignment from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/cue-size-align.vtt", function(track) {
+ var expected = [
+ { size: 100, align: "start" },
+ { size: 10, align: "end" },
+ { size: 0, align: "center" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+check_cues_from_track("resources/cue-size-align-bad.vtt", function(track) {
+ var expected = [
+ { size: 100, align: "center" },
+ { size: 100, align: "end" },
+ { size: 100, align: "center" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size.html
new file mode 100644
index 0000000000..d8e03edce7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-cue-size.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Cue size from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/cue-size.vtt", function(track) {
+ var expected = [
+ { size: 100 },
+ { size: 10 },
+ { size: 0 }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+check_cues_from_track("resources/cue-size-bad.vtt", function(track) {
+ var expected = [
+ { size: 100 },
+ { size: 100 },
+ { size: 100 }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-degenerate-cues.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-degenerate-cues.html
new file mode 100644
index 0000000000..8d2569993c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-degenerate-cues.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Degenerate cues without separating blank lines</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/degenerate-cues.vtt", function(track) {
+ var expected = [
+ { startTime: 0, endTime: 1, text: "" },
+ { startTime: 2, endTime: 3, text: "" },
+ { startTime: 4, endTime: 5, text: "" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-empty-cue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-empty-cue.html
new file mode 100644
index 0000000000..e1f5570250
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-empty-cue.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Empty cues should not be discarded</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/empty-cue.vtt", function(track) {
+ assert_equals(track.cues.length, 3);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-entities.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-entities.html
new file mode 100644
index 0000000000..a5295795ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-entities.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>Entities in the cue text</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var getCueAsHTMLContent = function(cue) {
+ return cue.getCueAsHTML().textContent;
+};
+
+check_cues_from_track("resources/entities.vtt", function(track) {
+ var expected = [
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has an ampersand & character." },
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has a less than < character." },
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has a greater than > character." },
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has a Left-to-Right Mark \u200e." },
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has a Right-to-Left Mark \u200f." },
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has a non-breaking space \u00a0." },
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This & is parsed to the same as &." }
+ ];
+
+ assert_cues_html_content(track.cues, expected);
+});
+
+check_cues_from_track("resources/entities-wrong.vtt", function(track) {
+ var expected = [
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has a less than ", },
+ { innerHTML: getCueAsHTMLContent,
+ expected: "This cue has a greater than > character.\nSince it's not related to a < character,\nit's just interpreted as text.", }
+ ];
+
+ assert_cues_html_content(track.cues, expected);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-header-comment.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-header-comment.html
new file mode 100644
index 0000000000..f9b35576f3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-header-comment.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>Optional comment area under the "WEBVTT" file header is properly ignored and also, default settings and styling are currently ignored (treated as faulty cues)</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/default-styles.vtt">
+ <track src="resources/metadata-area.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ for (var i = 0; i < video.textTracks.length; i++)
+ trackElements[i].onload = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 2)
+ return;
+
+ testTrack(0);
+ testTrack(1);
+ t.done();
+ }
+
+ function testTrack(index) {
+ var expected = [
+ {
+ id : "1",
+ startTime : 0,
+ endTime : 30.5,
+ text : "Bear is Coming!!!!!"
+ },
+ {
+ id : "2",
+ startTime : 31,
+ endTime : 1200.5,
+ text : "I said Bear is coming!!!!"
+ }
+ ];
+
+ assert_cues_equal(video.textTracks[index].cues, expected);
+ }
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-interspersed-non-cue.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-interspersed-non-cue.html
new file mode 100644
index 0000000000..2287cc2830
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-interspersed-non-cue.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>An empty line after an identifier line discards the current cue and restarts the cue loop</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/interspersed-non-cue.vtt", function(track) {
+ var expected = [
+ { text: "First" },
+ { text: "Second" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-line-position.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-line-position.html
new file mode 100644
index 0000000000..bea4acb917
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-line-position.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>Cue line position from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/line-position.vtt">
+ <track src="resources/line-position-bad.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ assert_equals(trackElements.length, video.textTracks.length);
+ for (var i = 0; i < trackElements.length; i++)
+ trackElements[i].onload = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 2)
+ return;
+
+ testTrack(0);
+ testTrackError(1);
+ t.done();
+ }
+
+ function testTrack(index) {
+ var expected = [
+ { line : 0, snapToLines : false },
+ { line : 0, snapToLines : true },
+ { line : 50, snapToLines : false },
+ { line : 5, snapToLines : true },
+ { line : 100, snapToLines : false },
+ { line : -1, snapToLines : true },
+ { line : 500, snapToLines : true }
+ ];
+
+ assert_cues_match(video.textTracks[index].cues, expected);
+ }
+
+ function testTrackError(index) {
+ var expected = [
+ { line : "auto", snapToLines : true },
+ { line : "auto", snapToLines : true },
+ { line : "auto", snapToLines : true },
+ { line : "auto", snapToLines : true },
+ { line : "auto", snapToLines : true }
+ ];
+
+ assert_cues_match(video.textTracks[index].cues, expected);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-magic-header.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-magic-header.html
new file mode 100644
index 0000000000..ff4637a8a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-magic-header.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>Magic file header "WEBVTT" leads to the file properly recognized as a WebVTT file</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/webvtt-file.vtt">
+ <track src="resources/webvtt-rubbish.vtt">
+ <track src="resources/no-webvtt.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ trackElements[0].onload = t.step_func(trackLoaded);
+ trackElements[1].onload = t.step_func(trackLoaded);
+ trackElements[2].onerror = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 3)
+ return;
+
+ testTrack(0);
+ testTrack(1);
+ testTrackError(2);
+ t.done();
+ }
+
+ function testTrack(index) {
+ var expected = [
+ {
+ id : "1",
+ startTime : 0,
+ endTime : 30.5,
+ text : "Bear is Coming!!!!!"
+ },
+ {
+ id : "2",
+ startTime : 31,
+ endTime : 1200.5,
+ text : "I said Bear is coming!!!!"
+ }
+ ];
+
+ assert_cues_equal(video.textTracks[index].cues, expected);
+ }
+
+ function testTrackError(index) {
+ assert_cues_equal(video.textTracks[index].cues, []);
+ }
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-markup.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-markup.html
new file mode 100644
index 0000000000..ceb05dd450
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-markup.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<title>Cues with &lt;b&gt;, &lt;i&gt;, &lt;u&gt;, &lt;rt&gt; and &lt;ruby&gt; tags</title>
+<meta name="timeout" content="long">
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/markup.vtt", function(track) {
+ assert_equals(track.cues.length, 4);
+
+ var children = [
+ { type: "text", value: "The following bear is bold:\n" },
+ { type: "b", value: [ { type: "text", value: "Bear" } ] },
+ { type: "text", value: " is Coming!!!!!" }
+ ];
+ assert_cue_fragment(track.cues[0], children);
+
+ children = [
+ { type: "text", value: "The following bear is in italics and has a class of \"larger\":\n" },
+ { type: "i", value: [ { type: "text", value: "Bear" } ] },
+ { type: "text", value: " is Coming!!!!!" }
+ ];
+
+ var fragment = createFragment(children);
+ fragment.querySelector("i").className = "larger";
+ assert_true(fragment.isEqualNode(track.cues[1].getCueAsHTML()));
+
+ children = [
+ { type: "text", value: "The following bear is underlined even though the element has a blank:\nI said " },
+ { type: "u", value: [ { type: "text", value: "Bear" } ] },
+ { type: "text", value: " is coming!!!!" }
+ ];
+ assert_cue_fragment(track.cues[2], children);
+
+ children = [
+ { type: "text", value: "The following bear is ruby annotated:\nI said " },
+ {
+ type: "ruby",
+ value: [
+ { type: "text", value: "Bear" },
+ {
+ type: "rt",
+ value: [ { type: "text", value: "bear with me" } ]
+ }
+ ]
+ },
+ { type: "text", value: " is coming!!!!" }
+ ];
+ assert_cue_fragment(track.cues[3], children);
+});
+
+check_cues_from_track("resources/markup-bad.vtt", function(track) {
+ assert_equals(track.cues.length, 4);
+
+ var children = [
+ { type: "text", value: "The following bear starts bold but end is broken:\n" },
+ {
+ type: "b",
+ value:
+ [
+ { type: "text", value: "Bear" },
+ { type: "text", value: " is Coming!!!!!" }
+ ]
+ }
+ ];
+ assert_cue_fragment(track.cues[0], children);
+
+ children = [
+ { type: "text", value: "The following bear is not in italics but the markup is removed:\n" },
+ { type: "text", value: "Bear" },
+ { type: "text", value: " is Coming!!!!!" }
+ ];
+ assert_cue_fragment(track.cues[1], children);
+
+ children = [
+ { type: "text", value: "The following bear is not underlined and markup is removed:\nI said " },
+ { type: "text", value : "Bear" },
+ { type: "text", value : " is coming!!!!" }
+ ];
+ assert_cue_fragment(track.cues[2], children);
+
+ children = [
+ { type: "text", value: "The following bear is not ruby annotated and markup is removed:\nI said " },
+ { type: "text", value: "Bear" },
+ { type: "text", value: "bear with me" },
+ { type: "text", value: " is coming!!!!" }
+ ];
+ assert_cue_fragment(track.cues[3], children);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-newlines.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-newlines.html
new file mode 100644
index 0000000000..4da7e6b1b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-newlines.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>A cue with no newline at eof is parsed properly</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/no-newline-at-eof.vtt" default>
+ <script>
+ async_test(function(t) {
+ var track = document.querySelector("track");
+
+ track.onload = t.step_func_done(function() {
+ var expected = [
+ {
+ id : "1",
+ startTime : 0,
+ endTime : 30.5,
+ text : "Bear is Coming!!!!!"
+ }
+ ];
+
+ assert_cues_equal(track.track.cues, expected);
+ });
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-no-timings.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-no-timings.html
new file mode 100644
index 0000000000..a39a2c37aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-no-timings.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Cue without timings are ignored</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/no-timings.vtt" default>
+ <script>
+ async_test(function(t) {
+ var track = document.querySelector("track");
+
+ track.onload = t.step_func_done(function() {
+ assert_cues_equal(track.track.cues, []);
+ });
+ });
+ </script>
+</video> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines-ref.html
new file mode 100644
index 0000000000..137a9334f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Reference test for track-webvtt-non-snap-to-lines.html</title>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/media.js"></script>
+<style>
+.container {
+ position: relative;
+ display: inline-block;
+}
+.cue {
+ position: absolute;
+ top: 30px;
+ left: 0px;
+ font-family: sans-serif;
+ background: green;
+ color: rgba(255, 255, 255, 1);
+ font-size: 7.5px;
+}
+</style>
+<div class="container">
+ <video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+ <script>
+ document.currentScript.parentNode.src = getVideoURI("/media/test");
+ </script>
+ </video>
+ <span class="cue">Bear is Coming!!!!!</span>
+</div>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines.html
new file mode 100644
index 0000000000..ec350ff44d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Position is not adjusted for non snap-to-lines cues</title>
+<link rel="match" href="track-webvtt-non-snap-to-lines-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/media.js"></script>
+<style>
+::cue {
+ background: green;
+}
+</style>
+<video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();"></video>
+<script>
+var video = document.querySelector("video");
+var track = video.addTextTrack("captions");
+var cue = new VTTCue(0, 1, "Bear is Coming!!!!!");
+cue.snapToLines = false;
+cue.line = 20;
+cue.align = "left";
+track.addCue(cue);
+track.mode = "showing";
+video.src = getVideoURI("/media/test");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-positioning.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-positioning.html
new file mode 100644
index 0000000000..d14a5768d3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-positioning.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Cue text position from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/positioning.vtt", testTrack);
+check_cues_from_track("resources/positioning-ltr.vtt", testTrack);
+
+check_cues_from_track("resources/positioning-bad.vtt", function(track) {
+ var expected = [
+ { position: "auto" },
+ { position: "auto" },
+ { position: "auto" },
+ { position: "auto" },
+ { position: "auto" },
+ { position: "auto" },
+ { position: "auto" },
+ { position: "auto" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+function testTrack(track) {
+ var expected = [
+ { position: 0 },
+ { position: 50 },
+ { position: "auto" },
+ { position: 100 }
+ ];
+
+ assert_cues_match(track.cues, expected);
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-settings.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-settings.html
new file mode 100644
index 0000000000..9ad98ffa1a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-settings.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>WebVTT settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/settings.vtt", function(track) {
+ var expected = [
+ { line: 100, position: "auto", align: "start", vertical: "" },
+ { line: 15, position: 40, align: "center", vertical: "rl" },
+ { line: "auto", position: 10, align: "center", vertical: "" },
+ { line: 95, position: "auto", align: "end", vertical: "lr" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+check_cues_from_track("resources/settings-bad-separation.vtt", function(track) {
+ var expected = [
+ { line: 43, position: 10, align: "center", vertical: "" },
+ { line: "auto", position: 50, align: "end", vertical: "" },
+ { line: "auto", position: "auto", align: "center", vertical: "" },
+ { line: "auto", position: 90, align: "center", vertical: "lr" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timestamp.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timestamp.html
new file mode 100644
index 0000000000..e311f121f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timestamp.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>Cues with &lt;timestamps&gt; tags</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/timestamp.vtt", function(track) {
+ assert_equals(track.cues.length, 3);
+
+ // TODO(srirama.m): Timestamps are handled as ProcessingInstructions,
+ // but because ProcessingInstructions are used in XML and not HTML,
+ // they are ignored here. This should later be tested with oncuechange events.
+
+ var children = [ { type: "text", value: "This cue is painted on." } ];
+ assert_cue_fragment_as_textcontent(track.cues[0], children);
+
+ children = [ { type: "text", value: "I said Bear is coming!!!!" } ];
+ assert_cue_fragment_as_textcontent(track.cues[1], children);
+
+ children = [ { type: "text", value: "I said Bear is coming now!!!!" } ];
+ assert_cue_fragment_as_textcontent(track.cues[2], children);
+});
+
+check_cues_from_track("resources/timestamp-bad.vtt", function(track) {
+ assert_equals(track.cues.length, 3);
+
+ var children = [ { type: "text", value: "This cue is painted on.\nBut since the last two timestamps are out of order, they are ignored." } ];
+ assert_cue_fragment_as_textcontent(track.cues[0], children);
+
+ children = [ { type: "text", value: "I said Bear is coming!!!!\nAll of these timestamps are before the start of the cue, so get ignored." } ];
+ assert_cue_fragment_as_textcontent(track.cues[1], children);
+
+ children = [ { type: "text", value: "I said Bear is coming now!!!!\nAll of these timestamps are after the end of the cue, so get ignored." } ];
+ assert_cue_fragment_as_textcontent(track.cues[2], children);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-hour.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-hour.html
new file mode 100644
index 0000000000..c03e182c79
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-hour.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Cue timings and various syntax errors in timings, with hours</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/timings-hour.vtt">
+ <track src="resources/timings-hour-error.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ for (var i = 0; i < video.textTracks.length; i++)
+ trackElements[i].onload = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 2)
+ return;
+
+ testTrack0();
+ testTrack1();
+ t.done();
+ }
+
+ function testTrack0() {
+ var expected = [
+ {
+ id : "1",
+ startTime : 0,
+ endTime : 30.5,
+ text : "Bear is Coming!!!!!"
+ },
+ {
+ id : "2",
+ startTime : 31,
+ endTime : 60.5,
+ text : "I said Bear is coming!!!!"
+ },
+ {
+ id : "3",
+ startTime : 61,
+ endTime : 361200.5,
+ text : "I said Bear is coming now!!!!"
+ }
+ ];
+
+ assert_cues_equal(video.textTracks[0].cues, expected);
+ }
+
+ function testTrack1() {
+ // Test that all the cues are ignored.
+ assert_cues_equal(video.textTracks[1].cues, []);
+ }
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-no-hours.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-no-hours.html
new file mode 100644
index 0000000000..e81ae03cc2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-no-hours.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<title>Cue timings and various syntax errors in timings, without hours</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/timings-no-hour.vtt">
+ <track src="resources/timings-no-hour-errors.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ for (var i = 0; i < video.textTracks.length; i++)
+ trackElements[i].onload = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 2)
+ return;
+
+ testTrack0();
+ testTrack1();
+ t.done();
+ }
+
+ function testTrack0() {
+ var expected = [
+ {
+ id : "1",
+ startTime : 0,
+ endTime : 30.5,
+ text : "Bear is Coming!!!!!"
+ },
+ {
+ id : "2",
+ startTime : 31,
+ endTime : 60.5,
+ text : "I said Bear is coming!!!!"
+ },
+ {
+ id : "3",
+ startTime : 61,
+ endTime : 120.5,
+ text : "I said Bear is coming now!!!!"
+ },
+ {
+ id : "4",
+ startTime : 121,
+ endTime : 180.5,
+ text : "tab separators"
+ }
+ ];
+
+ assert_cues_equal(video.textTracks[0].cues, expected);
+ }
+
+ function testTrack1() {
+ // Test that all the cues are ignored.
+ assert_cues_equal(video.textTracks[1].cues, []);
+ }
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-whitespace.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-whitespace.html
new file mode 100644
index 0000000000..db1346d23a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-timings-whitespace.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>"Skip whitespace" step around cue-timings separator</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/timings-whitespace.vtt", function(track) {
+ var expected = [
+ { id: "1", startTime: 0.1, endTime: 1.5, text: "Single U+0020 SPACE left of cue-timings separator" },
+ { id: "2", startTime: 0.1, endTime: 1.5, text: "Single U+0020 SPACE right of cue-timings separator" },
+ { id: "3", startTime: 0.1, endTime: 1.5, text: "Single U+0009 TAB left of cue-timings separator" },
+ { id: "4", startTime: 0.1, endTime: 1.5, text: "Single U+0009 TAB right of cue-timings separator" },
+ { id: "5", startTime: 0.1, endTime: 1.5, text: "Single U+000C FORM FEED left of cue-timings separator" },
+ { id: "6", startTime: 0.1, endTime: 1.5, text: "Single U+000C FORM FEED right of cue-timings separator" },
+ { id: "7", startTime: 0.1, endTime: 1.5, text: "Several U+0020 SPACE left of cue-timings separator" },
+ { id: "8", startTime: 0.1, endTime: 1.5, text: "Several U+0020 SPACE right of cue-timings separator" },
+ { id: "9", startTime: 0.1, endTime: 1.5, text: "Several U+0009 TAB left of cue-timings separator" },
+ { id: "10", startTime: 0.1, endTime: 1.5, text: "Several U+0009 TAB right of cue-timings separator" },
+ { id: "11", startTime: 0.1, endTime: 1.5, text: "Several U+000C FORM FEED left of cue-timings separator" },
+ { id: "12", startTime: 0.1, endTime: 1.5, text: "Several U+000C FORM FEED right of cue-timings separator" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end-ref.html
new file mode 100644
index 0000000000..1c8f751c2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT two-cue layout after the first cue has ended (reference)</title>
+<script src="/common/reftest-wait.js"></script>
+<video style="border:1px solid gray">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script>
+// Add a single cue at line -2, where it would be if there was a first
+// cue at line -1.
+var video = document.querySelector("video");
+var track = video.addTextTrack("captions");
+var cue = new VTTCue(0, 3, "cue 2");
+cue.line = -2;
+track.addCue(cue);
+track.mode = "showing";
+video.play();
+video.onplaying = function() {
+ video.onplaying=null;
+ video.pause();
+ takeScreenshot();
+};
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end.html
new file mode 100644
index 0000000000..df816ffe2b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT two-cue layout after the first cue has ended</title>
+<link rel="match" href="track-webvtt-two-cue-layout-after-first-end-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<video style="border:1px solid gray">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+</video>
+<script>
+// Add two cues, where the first cue ends before the second.
+var video = document.querySelector("video");
+var track = video.addTextTrack("captions");
+let cue1 = new VTTCue(-1, 1, "cue 1");
+track.addCue(cue1);
+// As video's duration is 10s, it ensures that this cue would always be displayed.
+track.addCue(new VTTCue(0, 10, "cue 2"));
+track.mode = "showing";
+video.play();
+cue1.onexit = () => {
+ cue1.onexit = null;
+ video.pause();
+ takeScreenshot();
+};
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-unsupported-markup.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-unsupported-markup.html
new file mode 100644
index 0000000000..ed3107f89b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-unsupported-markup.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Unsupported markup is properly ignored</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var getCueAsHTMLContent = function(cue) {
+ return cue.getCueAsHTML().textContent;
+};
+
+check_cues_from_track("resources/unsupported-markup.vtt", function(track) {
+ var expected = [
+ {
+ innerHTML: getCueAsHTMLContent,
+ expected: "Bear is Coming!!!!!\nAnd what kind of a bear it is - just have look."
+ },
+ {
+ innerHTML: getCueAsHTMLContent,
+ expected: "\n I said Bear is coming!!!!\n I said Bear is still coming!!!!\n",
+ },
+ {
+ innerHTML: getCueAsHTMLContent,
+ expected: "\n I said Bear is coming now!!!!\n \n \n",
+ }
+ ];
+
+ assert_cues_html_content(track.cues, expected);
+
+ var expected_text = [
+ { text: "<h1>Bear is Coming!!!!!</h1>\n<p>And what kind of a bear it is - just have <a href=\"webpage.html\">look</a>.</p>" },
+ { text: "<ul>\n <li>I said Bear is coming!!!!</li>\n <li>I said Bear is still coming!!!!</li>\n</ul>" },
+ { text: "<ol>\n <li>I said Bear is coming now!!!!</li>\n <li><img src=\"bear.png\" alt=\"mighty bear\"></li>\n <li><video src=\"bear_ad.webm\" controls></video></li>\n</ol>" }
+ ];
+
+ assert_cues_match(track.cues, expected_text);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-utf8.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-utf8.html
new file mode 100644
index 0000000000..eb44c85ba8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-utf8.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>UTF-8 encoded characters are recognized properly and different encodings (iconv) are not recognized as a WebVTT file</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video>
+ <track src="resources/utf8.vtt">
+ <track src="resources/iso2022jp3.vtt">
+</video>
+<script>
+async_test(function(t) {
+ var video = document.querySelector("video");
+
+ var trackElements = document.querySelectorAll("track");
+ for (var i = 0; i < video.textTracks.length; i++)
+ trackElements[i].onload = t.step_func(trackLoaded);
+
+ enableAllTextTracks(video.textTracks);
+
+ var numberOfTracksLoaded = 0;
+ function trackLoaded() {
+ numberOfTracksLoaded++;
+ if (numberOfTracksLoaded != 2)
+ return;
+
+ testTrack0();
+ testTrack1();
+ t.done();
+ }
+
+ function testTrack0() {
+ var expected = [
+ {
+ id : "1",
+ startTime : 0,
+ endTime : 30.5,
+ text : "景気判断"
+ },
+ {
+ id : "2",
+ startTime : 31,
+ endTime : 1200.5,
+ text : "電力ä¸è¶³"
+ }
+ ];
+
+ var cues = video.textTracks[0].cues;
+ assert_equals(cues.length, 2);
+ assert_cues_equal(cues, expected);
+ }
+
+ function testTrack1() {
+ assert_equals(video.textTracks[1].cues.length, 2);
+ }
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-valign.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-valign.html
new file mode 100644
index 0000000000..ace0760740
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-valign.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Cue vertical alignment (direction) from settings</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/valign.vtt", testTrack);
+check_cues_from_track("resources/valign-ltr.vtt", testTrack);
+check_cues_from_track("resources/valign-bad.vtt", function(track) {
+ var expected = [
+ { vertical: "" },
+ { vertical: "" },
+ { vertical: "" }
+ ];
+
+ assert_cues_match(track.cues, expected);
+});
+
+function testTrack(track) {
+ var expected = [
+ { vertical: "rl", align: "center", position: "auto" },
+ { vertical: "lr", align: "center", position: "auto" },
+ { vertical: "rl", align: "start", position: 0 }
+ ];
+
+ assert_cues_match(track.cues, expected);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-voice.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-voice.html
new file mode 100644
index 0000000000..5df8b4057a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-voice.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>Cues with voice markup &lt;v&gt;</title>
+<script src="track-helpers.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+check_cues_from_track("resources/voice.vtt", function(track) {
+ assert_equals(track.cues.length, 3);
+
+ var children = [
+ { type: "span", style: { className: "blue", title: "Speaker" },
+ value: [ { type: "text", value: "Bear is Coming!!!!!" } ] },
+ { type: "text", value: "\nText span with a class and an annotation." }
+ ];
+ assert_cue_fragment(track.cues[0], children);
+
+ children = [
+ { type: "span", style: { title: "Doe Hunter" },
+ value: [ { type: "text", value: "I said Bear is coming!!!!" } ] }
+ ];
+ assert_cue_fragment(track.cues[1], children);
+
+ children = [
+ { type: "text", value: "I said " },
+ { type: "span", style: { className: "blue", title: "Speaker" },
+ value: [ { type: "text", value: "Bear is coming now" } ] },
+ { type: "text", value: "!!!!" }
+ ];
+ assert_cue_fragment(track.cues[2], children);
+});
+
+check_cues_from_track("resources/voice-bad.vtt", function(track) {
+ assert_equals(track.cues.length, 3);
+
+ var children = [
+ { type: "text", value: "Bear is Coming!!!!!" },
+ { type: "text", value: "\nThis is two annotations for an empty tag." }
+ ];
+ assert_cue_fragment(track.cues[0], children);
+
+ children = [
+ { type: "text", value: "I said Bear is coming!!!!" },
+ { type: "text", value: "\nThis does not parse as a voice tag." }
+ ];
+ assert_cue_fragment(track.cues[1], children);
+
+ children = [
+ { type: "text", value: "I said " },
+ { type: "text", value: "Bear is coming now" },
+ { type: "text", value: "!!!!\nThis does not parse as a voice tag." }
+ ];
+ assert_cue_fragment(track.cues[2], children);
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/vtt-cue-float-precision.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/vtt-cue-float-precision.html
new file mode 100644
index 0000000000..9cb5824279
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/vtt-cue-float-precision.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Float precision of VTTCue attributes line, position and size</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var cue = new VTTCue(0, 1, 'text');
+
+ // Assign a value which is exactly representable as double but not float.
+ var doubleValue = 1.000000000000004;
+ cue.line = doubleValue;
+ assert_equals(cue.line, doubleValue);
+ cue.position = doubleValue;
+ assert_equals(cue.position, doubleValue);
+ cue.size = doubleValue;
+ assert_equals(cue.size, doubleValue);
+
+ // Assign a value which is exactly representable as float but is non-integral.
+ var floatValue = 1.5;
+ cue.line = floatValue;
+ assert_equals(cue.line, floatValue);
+ cue.position = floatValue;
+ assert_equals(cue.position, floatValue);
+ cue.size = floatValue;
+ assert_equals(cue.size, floatValue);
+}, document.title+', stored as floats');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/user-interface/muted.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/user-interface/muted.html
new file mode 100644
index 0000000000..eb6d2ac688
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/user-interface/muted.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<title>muted</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<style>video { display: none; }</style>
+<div id=log></div>
+
+<script>
+function test_setting(e, muted, hasAttribute) {
+ assert_equals(e.muted, muted);
+ assert_equals(e.hasAttribute('muted'), hasAttribute);
+
+ e.muted = !e.muted;
+ assert_equals(e.muted, !muted);
+ assert_equals(e.hasAttribute('muted'), hasAttribute);
+
+ e.muted = !e.muted;
+ assert_equals(e.muted, muted);
+ assert_equals(e.hasAttribute('muted'), hasAttribute);
+}
+</script>
+
+<!-- These tests are inside <audio>/<video> so that the steps for updating the
+ muted IDL attribute cannot be delayed until the end tag is parsed. -->
+
+<audio id=a1>
+<script>
+var a1 = document.getElementById('a1');
+
+test(function() {
+ assert_false(a1.muted);
+}, 'getting audio.muted (parser-created)');
+
+test(function() {
+ test_setting(a1, false, false);
+}, 'setting audio.muted (parser-created)');
+</script>
+</audio>
+
+<audio id=a2 muted>
+<script>
+var a2 = document.getElementById('a2');
+
+test(function() {
+ assert_true(a2.muted);
+}, 'getting audio.muted with muted="" (parser-created)');
+
+test(function() {
+ test_setting(a2, true, true);
+}, 'setting audio.muted with muted="" (parser-created)');
+</script>
+</audio>
+
+<video id=v1>
+<script>
+var v1 = document.getElementById('v1');
+
+test(function() {
+ assert_false(v1.muted);
+}, 'getting video.muted (parser-created)');
+
+test(function() {
+ test_setting(v1, false, false);
+}, 'setting video.muted (parser-created)');
+</script>
+</video>
+
+<video id=v2 muted>
+<script>
+var v2 = document.getElementById('v2');
+
+test(function() {
+ assert_true(v2.muted);
+}, 'getting video.muted with muted="" (parser-created)');
+
+test(function() {
+ test_setting(v2, true, true);
+}, 'setting video.muted with muted="" (parser-created)');
+</script>
+</video>
+
+<!-- Negative test to ensure that the load algorithm does not update the
+ muted IDL attribute to match the content attribute. -->
+
+<video id=v3 muted></video>
+<script>
+async_test(function(t) {
+ var v = document.getElementById('v3');
+ assert_true(v.muted);
+ v.muted = false;
+ v.src = 'data:,'; // invokes load()
+ v.addEventListener('error', t.step_func(function() {
+ assert_false(v.muted);
+ t.done();
+ }));
+}, 'getting video.muted with muted="" after load (parser-created)');
+</script>
+
+<script>
+['audio', 'video'].forEach(function(tagName) {
+ test(function() {
+ var m = document.createElement(tagName);
+ assert_false(m.muted);
+ }, 'getting ' + tagName + '.muted (script-created)');
+
+ test(function() {
+ var m = document.createElement(tagName);
+ test_setting(m, false, false);
+ }, 'setting ' + tagName + '.muted (script-created)');
+
+ test(function() {
+ var m = document.createElement(tagName);
+ m.setAttribute('muted', '');
+ assert_false(m.muted);
+ }, 'getting ' + tagName + '.muted with muted="" (script-created)');
+
+ test(function() {
+ var m = document.createElement(tagName);
+ m.setAttribute('muted', '');
+ test_setting(m, false, true);
+ }, 'setting ' + tagName + '.muted with muted="" (script-created)');
+
+ // Spec bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=25153
+ /*
+ test(function() {
+ var m = document.createElement(tagName);
+ m.setAttribute('muted', '');
+ m = m.cloneNode(false);
+ assert_true(m.hasAttribute('muted'));
+ assert_false(m.muted);
+ }, 'getting ' + tagName + '.muted with muted="" (cloneNode-created)');
+ */
+
+ test(function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<' + tagName + ' muted>';
+ m = div.firstChild;
+ assert_true(m.hasAttribute('muted'));
+ assert_true(m.muted);
+ }, 'getting ' + tagName + '.muted with muted="" (innerHTML-created)');
+
+ test(function() {
+ var id = tagName;
+ assert_equals(document.getElementById(id), null);
+ document.write('<' + tagName + ' id=' + id + ' muted>');
+ m = document.getElementById(id);
+ assert_true(m.hasAttribute('muted'));
+ assert_true(m.muted);
+ }, 'getting ' + tagName + '.muted with muted="" (document.write-created)');
+
+ test(function() {
+ var m = document.createElement(tagName);
+ m.setAttribute('muted', '');
+
+ var c = m.cloneNode(true);
+ assert_true(c.muted);
+ }, 'cloning ' + tagName + ' propagates muted (script-created)');
+
+ test(function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<' + tagName + ' muted>';
+ m = div.firstChild;
+
+ var c = m.cloneNode(true);
+ assert_true(c.muted);
+ }, 'cloning ' + tagName + ' propagates muted (innerHTML-created)');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_controls_present-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_controls_present-manual.html
new file mode 100644
index 0000000000..8e44951d7a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_controls_present-manual.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Video Test: video_controls_present.html</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-controls" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the controls attribute is present in the video element that expecting the user agent exposes a controller user interface" />
+ </head>
+ <body>
+ <p>Test passes if a controller user interface appears below and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <video id="m" controls>The user agent doesn't support media element.</video>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_loop_base.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_loop_base.html
new file mode 100644
index 0000000000..9b5d69b31a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_loop_base.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Video Test: video_loop_base</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-loop" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if video.loop is set to true that expecting the seeking event is fired more than once" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <video id="m" controls>The user agent doesn't support media element.</video>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ var name = document.getElementsByName("assert")[0].content;
+ var t = async_test(name);
+ var looped = false;
+
+ function startTest() {
+ if (looped) {
+ t.step(function() {
+ assert_true(true, "looped");
+ });
+ t.done();
+ media.pause();
+ }
+
+ looped = true;
+ }
+
+ media.addEventListener("seeking", startTest, false);
+ media.loop = true;
+ media.src = getVideoURI("/media/2x2-green") + "?" + new Date() + Math.random();
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_overriding_volume-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_overriding_volume-manual.html
new file mode 100644
index 0000000000..6d770666cc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_overriding_volume-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Video Test: video_muted_overriding_volume</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-muted" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the muted attribute is present in the video element with volume is set to loudest that expecting the user hears no sound" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the video is playing without sound output and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <video id="m" controls muted>The user agent doesn't support media element.</video>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ media.volume = 1.0;
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_present-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_present-manual.html
new file mode 100644
index 0000000000..bc80827775
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_muted_present-manual.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Video Test: video_muted_present</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-muted" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the muted attribute is present in the video element that expecting the user hears no sound" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the video is playing without sound output and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <video id="m" controls muted>The user agent doesn't support media element.</video>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_check.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_check.html
new file mode 100644
index 0000000000..1a45358a76
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_check.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Video Test: video_volume_check</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-volume" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check that video.volume returns the value of the muted content attribute" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <video id="m">The user agent doesn't support media element.</video>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ var VOLUME = {
+ 'SILENT' : 0.0,
+ 'NORMAL' : 0.5,
+ 'LOUDEST' : 1.0,
+ 'LOWER' : -1.1,
+ 'UPPER' : 1.1,
+ };
+
+ test(function() {
+ assert_false(media.volume < VOLUME.SILENT || media.volume > VOLUME.LOUDEST, "media.volume outside the range 0.0 to 1.0 inclusive");
+ }, "Check if the intial value of the video.volume is in the range 0.0 to 1.0 inclusive");
+
+ function volume_setting(vol, name)
+ {
+ if (vol < VOLUME.SILENT || vol > VOLUME.LOUDEST) {
+ try {
+ media.volume = vol;
+ test(function() {
+ assert_true(false, "media.volume setting exception");
+ }, name);
+ } catch(e) {
+ test(function() {
+ // 1 should be e.IndexSizeError or e.INDEX_SIZE_ERR in previous spec
+ assert_equals(e.code, 1, "media.volume setting exception");
+ }, name);
+ }
+ } else {
+ media.volume = vol;
+ test(function() {
+ assert_equals(media.volume, vol, "media.volume new value");
+ }, name);
+ }
+ }
+
+ volume_setting(VOLUME.NORMAL, "Check if video.volume is able to set to new value in the range 0.0 to 1.0");
+ volume_setting(VOLUME.SILENT, "Check if media.volume is able to set to new value 0.0 as silent");
+ volume_setting(VOLUME.LOUDEST, "Check if media.volume is able to set to new value 1.0 as loudest");
+ volume_setting(VOLUME.LOWER, "Check if media.volume is set to new value less than 0.0 that expecting an IndexSizeError exception is to be thrown");
+ volume_setting(VOLUME.UPPER, "Check if video.volume is set to new value greater than 1.0 that expecting an IndexSizeError exception is to be thrown");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_loudest-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_loudest-manual.html
new file mode 100644
index 0000000000..7475781201
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_loudest-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Video Test: video_volume_loudest</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-volume" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the volume attribute is set to 1.0 as loudest in the video element that expecting the user hears sound loudly" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the video is playing with sound heard and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <video id="m" controls>The user agent doesn't support media element.</video>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ media.volume = 1.0;
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_silent-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_silent-manual.html
new file mode 100644
index 0000000000..1768dd4d4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/video_volume_silent-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Video Test: video_volume_silent</title>
+ <link rel="author" title="Intel" href="http://www.intel.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-volume" />
+ <meta name="flags" content="" />
+ <meta name="assert" content="Check if the volume attribute is set to 0.0 as silent in the video element that expecting the user hears no sound" />
+ <script src="/common/media.js"></script>
+ </head>
+ <body>
+ <p>Test passes if the video is playing without sound heard and the text 'The user agent doesn't support media element.' does not appear anywhere on this page</p>
+ <video id="m" controls volume=0.0>The user agent doesn't support media element.</video>
+ <script type="text/javascript">
+ var media = document.getElementById("m");
+ media.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ media.volume = 0.0;
+ media.play();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/volume_nonfinite.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/volume_nonfinite.html
new file mode 100644
index 0000000000..fce50c2e20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/volume_nonfinite.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Setting HTMLMediaElement.volume to non-finite numbers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+["audio", "video"].forEach(function(aElement) {
+ [NaN, Infinity, -Infinity].forEach(function(aValue) {
+ test(function() {
+ var el = document.createElement(aElement);
+ assert_throws_js(TypeError, function() {
+ el.volume = aValue;
+ });
+ }, "Setting " + aElement + ".volume to " + String(aValue) + " should throw a TypeError");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/resources/common.js b/testing/web-platform/tests/html/semantics/embedded-content/resources/common.js
new file mode 100644
index 0000000000..06f18b3e04
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/resources/common.js
@@ -0,0 +1,45 @@
+// Helper to access the element, its associated loading promise, and also to
+// resolve the promise.
+class ElementLoadPromise {
+ constructor(element_id) {
+ this.element_id = element_id;
+ this.promise = new Promise((resolve, reject) => {
+ this.resolve = resolve
+ this.reject = reject
+ });
+ }
+ element() {
+ return document.getElementById(this.element_id);
+ }
+}
+
+// Returns if the image is complete and the lazily loaded image matches the expected image.
+function is_image_fully_loaded(image, expected_image) {
+ if (!image.complete || !expected_image.complete) {
+ return false;
+ }
+
+ if (image.width != expected_image.width ||
+ image.height != expected_image.height) {
+ return false;
+ }
+
+ let canvas = document.createElement('canvas');
+ canvas.width = image.width;
+ canvas.height = image.height;
+ let canvasContext = canvas.getContext("2d");
+ canvasContext.save();
+ canvasContext.drawImage(image, 0, 0);
+ let data = canvasContext.getImageData(0, 0, canvas.width, canvas.height).data;
+
+ canvasContext.restore();
+ canvasContext.drawImage(expected_image, 0, 0);
+ let expected_data = canvasContext.getImageData(0, 0, canvas.width, canvas.height).data;
+
+ for (var i = 0; i < data.length; i++) {
+ if (data[i] != expected_data[i]) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html b/testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html
new file mode 100644
index 0000000000..a941511642
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>I should not be embeddable because of X-Frame-Options</title>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html.headers b/testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html.headers
new file mode 100644
index 0000000000..fa717cc748
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/resources/not-embeddable.html.headers
@@ -0,0 +1 @@
+X-Frame-Options: deny
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/resources/should-load.html b/testing/web-platform/tests/html/semantics/embedded-content/resources/should-load.html
new file mode 100644
index 0000000000..a9a178ce51
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/resources/should-load.html
@@ -0,0 +1,3 @@
+<script>
+ parent.loadedCount++;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/resources/should-not-load.html b/testing/web-platform/tests/html/semantics/embedded-content/resources/should-not-load.html
new file mode 100644
index 0000000000..6281b2da55
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/resources/should-not-load.html
@@ -0,0 +1,5 @@
+<script>
+ parent.nestingTest.step(function() {
+ parent.assert_unreached(window.frameElement.getAttribute("test-description"));
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-coords.html b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-coords.html
new file mode 100644
index 0000000000..9ec6f3e427
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-coords.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLAreaElement coords parsing</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ body { margin: 0 }
+</style>
+<img src=/images/threecolors.png usemap=#x id=img width=300 height=300>
+<map name=x><area id=area></map>
+<script src=support/hit-test.js></script>
+<script>
+tests = [
+ {desc: 'COMMA', shape: 'rect', coords: "2,2,10,10", hit: hitRect},
+ {desc: 'SEMICOLON', shape: 'rect', coords: "2;2;10;10", hit: hitRect},
+ {desc: 'SPACE', shape: 'rect', coords: "2 2 10 10", hit: hitRect},
+ {desc: 'TAB', shape: 'rect', coords: "2\t2\t10\t10", hit: hitRect},
+ {desc: 'FORM FEED', shape: 'rect', coords: "2\f2\f10\f10", hit: hitRect},
+ {desc: 'LINE FEED', shape: 'rect', coords: "2\n2\n10\n10", hit: hitRect},
+ {desc: 'CARRIGAGE RETURN', shape: 'rect', coords: "2\r2\r10\r10", hit: hitRect},
+ {desc: 'LINE TABULATION', shape: 'rect', coords: "2\u000b2\u000b10\u000b10", hit: hitNone},
+ {desc: 'LINE NEXT', shape: 'rect', coords: "2\u00852\u008510\u008510", hit: hitNone},
+ {desc: 'EN QUAD', shape: 'rect', coords: "2\u20002\u200010\u200010", hit: hitNone},
+ {desc: 'abc between numbers', shape: 'rect', coords: "2a2b20c20,2,10,10", hit: hitRect},
+ {desc: 'COLON between numbers', shape: 'rect', coords: "2:2:20:20,2,10,10", hit: hitRect},
+ {desc: 'U+0000 between numbers', shape: 'rect', coords: "2\u00002\u000020\u000020,2,10,10", hit: hitRect},
+ {desc: 'leading COMMA', shape: 'rect', coords: ",2,2,10,10", hit: hitRect},
+ {desc: 'leading SPACE', shape: 'rect', coords: " 2,2,10,10", hit: hitRect},
+ {desc: 'leading SEMICOLON', shape: 'rect', coords: ";2,2,10,10", hit: hitRect},
+ {desc: 'trailing COMMA', shape: 'rect', coords: "2,2,10,", hit: hitNone},
+ {desc: 'trailing SPACE', shape: 'rect', coords: "2,2,10 ", hit: hitNone},
+ {desc: 'trailing SEMICOLON', shape: 'rect', coords: "2,2,10;", hit: hitNone},
+ {desc: 'PERCENT', shape: 'rect', coords: "2%,2%,10%,10%", hit: hitRect},
+ {desc: 'CSS units', shape: 'rect', coords: "2in,2in,10cm,10cm", hit: hitRect},
+ {desc: 'float', shape: 'rect', coords: "1.4,1.4,10,10", hit: hitRect},
+ {desc: 'number starting with PERIOD', shape: 'rect', coords: ".4,.4,10,10", hit: [[area, 1, 1], [img, 0, 0]]},
+ {desc: 'sci-not', shape: 'rect', coords: "2,2,1e1,1e1", hit: hitRect},
+ {desc: 'leading/trailing garbage', shape: 'rect', coords: "='2,2,10,10' ", hit: hitRect},
+ {desc: 'non-ascii garbage', shape: 'rect', coords: "“2,2,10,10\"", hit: hitRect},
+ {desc: 'URL garbage with number', shape: 'rect', coords: "2,2,10ls/spain/holidays/regions/10/Canary+Islands/Canary+Islands.html", hit: hitNone},
+ {desc: 'consecutive COMMAs', shape: 'rect', coords: "2,,10,10", hit: hitNone},
+ {desc: 'consecutive SPACEs', shape: 'rect', coords: "2 10,10", hit: hitNone},
+ {desc: 'consecutive SEMICOLONs', shape: 'rect', coords: "2;;10,10", hit: hitNone},
+ {desc: 'several consecutive separators', shape: 'rect', coords: ",,2;,;2,;,10 \t\r\n10;;", hit: hitRect},
+ {desc: 'one too many numbers, trailing COMMA', shape: 'poly', coords: "100,100,120,100,100,120,300,", hit: hitPoly},
+];
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-download-click.html b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-download-click.html
new file mode 100644
index 0000000000..8100ada9d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-download-click.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Clicking on an &lt;area> element with a download attribute must not throw an exception</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-area-element:activation-behaviour">
+<link rel="help" href="https://github.com/whatwg/html/issues/2116">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+"use strict";
+async_test(t => {
+ const frame = document.createElement("iframe");
+
+ frame.addEventListener("load", t.step_func(function () {
+ frame.contentWindow.addEventListener(
+ "beforeunload", t.unreached_func("Navigated instead of downloading"));
+ const string = "test";
+ const blob = new Blob([string], { type: "text/html" });
+
+ const link = frame.contentDocument.querySelector("#blob-url");
+ link.href = URL.createObjectURL(blob);
+
+ link.click();
+
+ t.step_timeout(() => t.done(), 1000);
+ }));
+ frame.src = "resources/area-download-click.html";
+ document.body.appendChild(frame);
+}, "Clicking on an <area> element with a download attribute must not throw an exception");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-processing.html b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-processing.html
new file mode 100644
index 0000000000..d1c3a83dd8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-processing.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLAreaElement processing</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ body { margin: 0 }
+</style>
+<img src=/images/threecolors.png usemap=#x id=img width=300 height=300>
+<map name=x><area id=area></map>
+<script src=support/hit-test.js></script>
+<script>
+var tests = [
+ {desc: 'too few numbers', shape: 'rect', coords: "2,2,10", hit: hitNone},
+ {desc: 'negative', shape: 'rect', coords: "-10,-10,10,10", hit: [[area, 1, 1], [img, 299, 299]]},
+ {desc: 'empty string', shape: 'rect', coords: "", hit: hitNone},
+ {desc: 'omitted coords', shape: 'rect', coords: null, hit: hitNone},
+ {desc: 'first > third', shape: 'rect', coords: "10,2,2,10", hit: hitRect},
+ {desc: 'second > fourth', shape: 'rect', coords: "2,10,10,2", hit: hitRect},
+ {desc: 'first > third, second > fourth', shape: 'rect', coords: "10,10,2,2", hit: hitRect},
+
+ {desc: 'negative', shape: 'default', coords: "-10,-10,-10,-10", hit: hitAll},
+
+ {desc: 'too few numbers', shape: 'circle', coords: "20,40", hit: hitNone},
+ {desc: 'negative radius', shape: 'circle', coords: "20,40,-10", hit: hitNone},
+ {desc: 'zero radius', shape: 'circle', coords: "20,40,0", hit: hitNone},
+
+ {desc: 'too few numbers', shape: 'poly', coords: "100,100,120,100,100", hit: hitNone},
+ {desc: 'one too many numbers', shape: 'poly', coords: "100,100,120,100,100,120,300", hit: hitPoly},
+ {desc: 'even-odd rule', shape: 'poly', coords: "100,100,200,100,100,200,150,50,200,200", hit: hitStar},
+];
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-shape.html b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-shape.html
new file mode 100644
index 0000000000..1ad0690f9e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-shape.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLAreaElement shape</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ body { margin: 0 }
+</style>
+<img src=/images/threecolors.png usemap=#x id=img width=300 height=300>
+<map name=x><area id=area></map>
+<script src=support/hit-test.js></script>
+<script>
+var tests = [
+ {desc: 'missing value default', shape: null, coords: "2,2,10,10", hit: hitRect},
+ {desc: 'missing value default', shape: null, coords: "20,40,10", hit: hitNone},
+ {desc: 'missing value default', shape: null, coords: null, hit: hitNone},
+ {desc: 'invalid value default', shape: 'foobar invalid', coords: "2,2,10,10", hit: hitRect},
+ {desc: 'invalid value default', shape: '', coords: "2,2,10,10", hit: hitRect},
+
+ {desc: 'empty string', shape: 'default', coords: "", hit: hitAll},
+ {desc: 'omitted coords', shape: 'DEFAULT', coords: null, hit: hitAll},
+
+ {desc: 'simple', shape: 'rect', coords: "2,2,10,10", hit: hitRect},
+ {desc: 'simple', shape: 'rectangle', coords: "2,2,10,10", hit: hitRect},
+
+ {desc: 'simple', shape: 'circle', coords: "20,40,10", hit: hitCircle},
+ {desc: 'simple', shape: 'circ', coords: "20,40,10", hit: hitCircle},
+ {desc: 'simple', shape: 'CIRCLE', coords: "20,40,10", hit: hitCircle},
+ {desc: 'simple', shape: 'CIRC', coords: "20,40,10", hit: hitCircle},
+ {desc: 'LATIN CAPITAL LETTER I WITH DOT ABOVE', shape: 'C\u0130RCLE', coords: "20,40,10", hit: hitNone},
+ {desc: 'LATIN SMALL LETTER DOTLESS I', shape: 'c\u0131rcle', coords: "20,40,10", hit: hitNone},
+
+ {desc: 'simple', shape: 'poly', coords: "100,100,120,100,100,120", hit: hitPoly},
+ {desc: 'simple', shape: 'polygon', coords: "100,100,120,100,100,120", hit: hitPoly},
+];
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-stringifier.html b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-stringifier.html
new file mode 100644
index 0000000000..3a661893d7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/area-stringifier.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>HTMLAreaElement stringifier</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-stringifier">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/stringifiers.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ test_stringifier_attribute(document.createElement("area"), "href", false);
+ var area = document.createElement("area");
+ area.setAttribute("href", "foo");
+ test_stringifier_attribute(area, "href", false);
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/resources/area-download-click.html b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/resources/area-download-click.html
new file mode 100644
index 0000000000..c0679f8233
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/resources/area-download-click.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<img src="/images/threecolors.png" usemap="#x" id="img" width="300" height="300">
+<map name="x">
+ <area id="blob-url" download="foo.html" coords="0,0,300,300">
+</map>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/support/hit-test.js b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/support/hit-test.js
new file mode 100644
index 0000000000..82a98f1c35
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-area-element/support/hit-test.js
@@ -0,0 +1,42 @@
+setup({explicit_done: true});
+
+var img = document.getElementById('img');
+var area = document.getElementById('area');
+
+var hitRect = [[area, 3, 3], [area, 9, 9], [img, 1, 3], [img, 3, 1], [img, 11, 9], [img, 9, 11], [img, 21, 41], [img, 101, 101]];
+var hitNone = [[img, 3, 3], [img, 9, 9], [img, 1, 3], [img, 3, 1], [img, 11, 9], [img, 9, 11], [img, 21, 41], [img, 101, 101]];
+var hitAll = [[area, 1, 1], [area, 1, 299], [area, 299, 1], [area, 299, 299], [area, 21, 41], [area, 101, 101]];
+var hitCircle = [[area, 11, 40], [area, 29, 40], [area, 20, 31], [area, 20, 49], [img, 12, 32], [img, 28, 48], [img, 101, 101]];
+var hitPoly = [[area, 101, 101], [area, 119, 101], [area, 101, 119], [img, 118, 118], [img, 3, 3], [img, 21, 41]];
+var hitStar = [[area, 101, 101], [area, 199, 101], [area, 150, 51], [img, 150, 125], [img, 3, 3], [img, 21, 41]];
+
+var tests;
+// The test file should have `tests` defined as follows:
+// tests = [
+// {desc: string, shape: string?, coords: string?, hit: [[element, x, y], ...]},
+// ...
+// ];
+
+onload = function() {
+ tests.forEach(function(t) {
+ test(function(t_obj) {
+ if (t.shape === null) {
+ area.removeAttribute('shape');
+ } else {
+ area.shape = t.shape;
+ }
+ if (area.coords === null) {
+ area.removeAttribute('coords');
+ } else {
+ area.coords = t.coords;
+ }
+ t.hit.forEach(function(arr) {
+ var expected = arr[0];
+ var x = arr[1];
+ var y = arr[2];
+ assert_equals(document.elementFromPoint(x, y), expected, 'elementFromPoint('+x+', '+y+')');
+ });
+ }, t.desc + ': ' + format_value(t.coords) + ' (' + t.shape + ')');
+ });
+ done();
+};
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-appendChild-to-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-appendChild-to-inactive-document-crash.html
new file mode 100644
index 0000000000..33d52ca899
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-appendChild-to-inactive-document-crash.html
@@ -0,0 +1,6 @@
+<iframe id=i></iframe>
+<script>
+var doc = i.contentDocument.cloneNode();
+i.remove();
+doc.appendChild(document.createElement("audio"));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-play-in-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-play-in-inactive-document-crash.html
new file mode 100644
index 0000000000..ade40797b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio-play-in-inactive-document-crash.html
@@ -0,0 +1,8 @@
+<audio id="a"></audio>
+<iframe id="i"></iframe>
+<script>
+var a = document.getElementById("a");
+i.contentDocument.documentElement.appendChild(a);
+i.remove();
+a.play();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_001.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_001.htm
new file mode 100644
index 0000000000..f455c68241
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_001.htm
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Media Elements: Content inside the 'audio' element is not shown to the user (image).</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#audio" />
+ <link rel="match" href="audio_content-ref.htm" />
+ <meta name="assert" content="Content inside the 'audio' element is not shown to the user (image)." />
+</head>
+<body>
+<p>Test passes if there is no red.</p>
+<div id='testcontent'>
+<audio><img src="../../../../images/fail.gif" /></audio>
+
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_002.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_002.htm
new file mode 100644
index 0000000000..23b3ea188a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_002.htm
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Media Elements: Content inside the 'audio' element is not shown to the user.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#audio" />
+ <link rel="match" href="audio_content-ref.htm" />
+ <meta name="assert" content="Content inside the 'audio' element is not shown to the user." />
+</head>
+<body>
+<p>Test passes if there is no red.</p>
+<div id='testcontent'>
+<audio><span style="color: red;">FAIL</span></audio>
+
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_constructor.html b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_constructor.html
new file mode 100644
index 0000000000..c5b5b80ac1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_constructor.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Audio constructor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var throwingObject = {
+ toString: function() { throw Error() },
+ valueOf: function() { throw Error() }
+ };
+ var tests = [
+ [function() { return new Audio() }, null, "No arguments"],
+ [function() { return new Audio("") }, "", "Empty string argument"],
+ [function() { return new Audio("src") }, "src", "Non-empty string argument"],
+ [function() { return new Audio(null) }, "null", "Null argument"],
+ [function() { return new Audio(undefined) }, null, "Undefined argument"],
+ [function() { return new Audio("", throwingObject) }, "", "Extra argument"],
+ ];
+ tests.forEach(function(t) {
+ var fn = t[0], expectedSrc = t[1], description = t[2];
+ test(function() {
+ var element = fn();
+ assert_equals(element.localName, "audio");
+ assert_equals(element.tagName, "AUDIO");
+ assert_equals(element.namespaceURI, "http://www.w3.org/1999/xhtml");
+ assert_equals(element.nodeType, Node.ELEMENT_NODE);
+ assert_equals(element.getAttribute("preload"), "auto");
+ assert_equals(element.getAttribute("src"), expectedSrc);
+ assert_equals(element.ownerDocument, document);
+ }, description);
+ });
+});
+
+test(function() {
+ var audio = new Audio();
+ assert_equals(Object.getPrototypeOf(audio), HTMLAudioElement.prototype);
+}, "Prototype of object created with named constructor");
+
+test(function() {
+ assert_throws_js(TypeError, function() {
+ Audio();
+ });
+}, "Calling Audio should throw");
+test(function() {
+ assert_throws_js(TypeError, function() {
+ HTMLAudioElement();
+ });
+}, "Calling HTMLAudioElement should throw");
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new HTMLAudioElement();
+ });
+}, "Constructing HTMLAudioElement should throw");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_content-ref.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_content-ref.htm
new file mode 100644
index 0000000000..ef5964496d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-audio-element/audio_content-ref.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Media Elements: Content inside the 'audio' element is not shown to the user.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+</head>
+<body>
+<p>Test passes if there is no red.</p>
+<div id='testcontent'>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/2d-getcontext-options.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/2d-getcontext-options.html
new file mode 100644
index 0000000000..5d35d4108c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/2d-getcontext-options.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>Options conversion for getContext("2d")</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ const expected = [
+ "alpha",
+ "colorSpace",
+ "colorSpace toString",
+ "desynchronized",
+ "willReadFrequently",
+ ];
+ var actual = [];
+ const options = {
+ get alpha() {
+ actual.push("alpha");
+ return true;
+ },
+ get willReadFrequently() {
+ actual.push("willReadFrequently");
+ return false;
+ },
+ get desynchronized() {
+ actual.push("desynchronized");
+ return false;
+ },
+ get colorSpace() {
+ actual.push("colorSpace");
+ return {
+ toString() {
+ actual.push("colorSpace toString");
+ return "srgb";
+ }
+ };
+ },
+ };
+
+ const canvas = document.createElement("canvas");
+ const context = canvas.getContext('2d', options);
+ assert_not_equals(context, null, "context");
+ assert_array_equals(actual, expected, "order of operations (creation)");
+ actual = [];
+ assert_equals(canvas.getContext('2d', options), context, "cached context");
+ assert_array_equals(actual, expected, "order of operations (caching)");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-001.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-001.html
new file mode 100644
index 0000000000..327c9f49d6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-001.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Canvas descendants focusability</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area">
+<meta name="assert" content="Checks that elements being used as relevant canvas
+ fallback content can be focusable even if not rendered.">
+<div id="log"></div>
+<canvas>
+ <button data-focusable="true"></button>
+ <section data-focusable="false">
+ <div data-focusable="false"></div>
+ <span data-focusable="false"></span>
+ <a data-focusable="false"></a>
+ </section>
+ <section tabindex="-1" data-focusable="true">
+ <div tabindex="-1" data-focusable="true"></div>
+ <span tabindex="-1" data-focusable="true"></span>
+ <a href="#" data-focusable="true"></a>
+ </section>
+</canvas>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+for (let element of document.querySelectorAll("[data-focusable]")) {
+ let title = element.cloneNode(false).outerHTML.toLowerCase();
+ title = title.slice(0, title.lastIndexOf("<"));
+ test(function() {
+ assert_true(document.activeElement !== element, "Not initially focused");
+ element.focus();
+ if (JSON.parse(element.dataset.focusable)) {
+ assert_true(document.activeElement === element, "Should be focused");
+ } else {
+ assert_true(document.activeElement !== element, "Shouldn't be focused");
+ }
+ }, title);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-002.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-002.html
new file mode 100644
index 0000000000..aa607365d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-002.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Canvas descendants focusability</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area">
+<meta name="assert" content="Checks that descendants of a non-rendered canvas
+ aren't relevant canvas fallback content, so they aren't focusable.">
+<div id="log"></div>
+<canvas hidden>
+ <button data-focusable="false"></button>
+ <section tabindex="-1" data-focusable="false">
+ <div tabindex="-1" data-focusable="false"></div>
+ <span tabindex="-1" data-focusable="false"></span>
+ <a href="#" data-focusable="false"></a>
+ </section>
+</canvas>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup(() => {
+ const canvas = document.querySelector("canvas");
+ assert_equals(canvas.getClientRects().length, 0, "Canvas not rendered");
+});
+for (let element of document.querySelectorAll("[data-focusable]")) {
+ let title = element.cloneNode(false).outerHTML.toLowerCase();
+ title = title.slice(0, title.lastIndexOf("<"));
+ test(function() {
+ assert_equals(element.getClientRects().length, 0, "Not rendered");
+ assert_true(document.activeElement !== element, "Not initially focused");
+ element.focus();
+ if (JSON.parse(element.dataset.focusable)) {
+ assert_true(document.activeElement === element, "Should be focused");
+ } else {
+ assert_true(document.activeElement !== element, "Shouldn't be focused");
+ }
+ }, title);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-003.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-003.tentative.html
new file mode 100644
index 0000000000..cd42d1e6b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-003.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Canvas descendants focusability</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area">
+<link rel="help" href="https://github.com/whatwg/html/issues/7534">
+<meta name="assert" content="Checks that elements being used as relevant canvas
+ fallback content can't be focusable if they are not rendered because of an
+ explicit 'display: none' style, but can if they are not rendered because of
+ a 'display: contents' style.">
+<div id="log"></div>
+<canvas>
+ <button hidden data-focusable="false"></button>
+ <section hidden tabindex="-1" data-focusable="false">
+ <div tabindex="-1" data-focusable="false"></div>
+ <span tabindex="-1" data-focusable="false"></span>
+ <a href="#" data-focusable="false"></a>
+ </section>
+ <button style="display: contents" data-focusable="true"></button>
+ <section style="display: contents" tabindex="-1" data-focusable="true">
+ <div style="display: contents" tabindex="-1" data-focusable="true"></div>
+ <span style="display: contents" tabindex="-1" data-focusable="true"></span>
+ <a style="display: contents" href="#" data-focusable="true"></a>
+ </section>
+</canvas>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup(() => {
+ const canvas = document.querySelector("canvas");
+ assert_greater_than(canvas.getClientRects().length, 0, "Canvas is rendered");
+});
+for (let element of document.querySelectorAll("[data-focusable]")) {
+ let title = element.cloneNode(false).outerHTML.toLowerCase();
+ title = title.slice(0, title.lastIndexOf("<"));
+ test(function() {
+ assert_equals(element.getClientRects().length, 0, "Not rendered");
+ assert_true(document.activeElement !== element, "Not initially focused");
+ element.focus();
+ if (JSON.parse(element.dataset.focusable)) {
+ assert_true(document.activeElement === element, "Should be focused");
+ } else {
+ assert_true(document.activeElement !== element, "Shouldn't be focused");
+ }
+ }, title);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-004.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-004.tentative.html
new file mode 100644
index 0000000000..5d8dfcd2f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-004.tentative.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Canvas descendants focusability</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area">
+<link rel="help" href="https://github.com/whatwg/html/issues/7534">
+<meta name="assert" content="Checks that elements being used as relevant canvas
+ fallback content can't be focusable if they are not in the flat tree.">
+<div id="log"></div>
+<canvas>
+ <section id="shadow-host">
+ <button data-focusable="false"></button>
+ <section tabindex="-1" data-focusable="false">
+ <div tabindex="-1" data-focusable="false"></div>
+ <span tabindex="-1" data-focusable="false"></span>
+ <a href="#" data-focusable="false"></a>
+ </section>
+ </section>
+</canvas>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup(() => {
+ const canvas = document.querySelector("canvas");
+ assert_greater_than(canvas.getClientRects().length, 0, "Canvas is rendered");
+ const shadowHost = document.getElementById("shadow-host");
+ const shadowRoot = shadowHost.attachShadow({ mode: "open" });
+ const slot = document.createElement("slot");
+ slot.name = "slot";
+ shadowRoot.appendChild(slot);
+});
+for (let element of document.querySelectorAll("[data-focusable]")) {
+ let title = element.cloneNode(false).outerHTML.toLowerCase();
+ title = title.slice(0, title.lastIndexOf("<"));
+ test(function() {
+ assert_equals(element.getClientRects().length, 0, "Not rendered");
+ assert_true(document.activeElement !== element, "Not initially focused");
+ element.focus();
+ if (JSON.parse(element.dataset.focusable)) {
+ assert_true(document.activeElement === element, "Should be focused");
+ } else {
+ assert_true(document.activeElement !== element, "Shouldn't be focused");
+ }
+ }, title);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-005.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-005.html
new file mode 100644
index 0000000000..f3bee6b06b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/canvas-descendants-focusability-005.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Canvas descendants focusability</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area">
+<meta name="assert" content="Checks that descendants of a canvas that represents
+ fallback content are not focusable if not rendered, as usual.">
+<div id="log"></div>
+<!-- Use a sandboxed iframe to disable scripting and make the canvas
+ represent its fallback content instead of embedded content. -->
+<iframe sandbox="allow-same-origin" allow="focus-without-user-activation *"
+ srcdoc='
+ <button data-focusable="true" a></button>
+ <canvas>
+ <button data-focusable="true"></button>
+ <section tabindex="-1" data-focusable="true">
+ <div tabindex="-1" data-focusable="true"></div>
+ <span tabindex="-1" data-focusable="true"></span>
+ <a href="#" data-focusable="true"></a>
+ </section>
+ <button hidden data-focusable="false"></button>
+ <section tabindex="-1" hidden data-focusable="false">
+ <div tabindex="-1" data-focusable="false"></div>
+ <span tabindex="-1" data-focusable="false"></span>
+ <a href="#" data-focusable="false"></a>
+ </section>
+ </canvas>
+ '></iframe>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true });
+setup(async () => {
+ const iframe = document.querySelector("iframe");
+ await new Promise(resolve => {
+ const win = iframe.contentWindow;
+ if (win.location.href === "about:blank" ||
+ win.document.readyState !== "complete") {
+ iframe.addEventListener("load", resolve, {once: true});
+ } else {
+ resolve();
+ }
+ });
+ const doc = iframe.contentDocument;
+ for (let element of doc.querySelectorAll("[data-focusable]")) {
+ let title = element.cloneNode(false).outerHTML.toLowerCase();
+ title = title.slice(0, title.lastIndexOf("<"));
+ test(function() {
+ assert_true(doc.activeElement !== element, "Not initially focused");
+ element.focus();
+ if (JSON.parse(element.dataset.focusable)) {
+ assert_true(doc.activeElement === element, "Should be focused");
+ } else {
+ assert_true(doc.activeElement !== element, "Shouldn't be focused");
+ }
+ }, title);
+ }
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.arguments.missing.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.arguments.missing.html
new file mode 100644
index 0000000000..eb4d69aed0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.arguments.missing.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: context.arguments.missing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>context.arguments.missing</h1>
+<p class="desc"></p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("");
+_addTest(function(canvas, ctx) {
+
+assert_throws_js(TypeError, function() { canvas.getContext(); });
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.casesensitive.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.casesensitive.html
new file mode 100644
index 0000000000..8753185449
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.casesensitive.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: context.casesensitive</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>context.casesensitive</h1>
+<p class="desc">Context name "2D" is unrecognised; matching is case sensitive</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Context name \"2D\" is unrecognised; matching is case sensitive");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.getContext('2D'), null, "canvas.getContext('2D')", "null");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.emptystring.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.emptystring.html
new file mode 100644
index 0000000000..1f27225882
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.emptystring.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: context.emptystring</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>context.emptystring</h1>
+<p class="desc">getContext with empty string returns null</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getContext with empty string returns null");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.getContext(""), null, "canvas.getContext(\"\")", "null");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badname.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badname.html
new file mode 100644
index 0000000000..55d503036b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badname.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: context.unrecognised.badname</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>context.unrecognised.badname</h1>
+<p class="desc">getContext with unrecognised context name returns null</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("getContext with unrecognised context name returns null");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.getContext('This is not an implemented context in any real browser'), null, "canvas.getContext('This is not an implemented context in any real browser')", "null");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badsuffix.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badsuffix.html
new file mode 100644
index 0000000000..ea0a14aaed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badsuffix.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: context.unrecognised.badsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>context.unrecognised.badsuffix</h1>
+<p class="desc">Context name "2d" plus a suffix is unrecognised</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Context name \"2d\" plus a suffix is unrecognised");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.getContext("2d#"), null, "canvas.getContext(\"2d#\")", "null");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.nullsuffix.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.nullsuffix.html
new file mode 100644
index 0000000000..ea8db36a89
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.nullsuffix.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: context.unrecognised.nullsuffix</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>context.unrecognised.nullsuffix</h1>
+<p class="desc">Context name "2d" plus a "\0" suffix is unrecognised</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Context name \"2d\" plus a \"\\0\" suffix is unrecognised");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.getContext("2d\0"), null, "canvas.getContext(\"2d\\0\")", "null");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.unicode.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.unicode.html
new file mode 100644
index 0000000000..727ea3584f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/context.unrecognised.unicode.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: context.unrecognised.unicode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>context.unrecognised.unicode</h1>
+<p class="desc">Context name which kind of looks like "2d" is unrecognised</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Context name which kind of looks like \"2d\" is unrecognised");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.getContext("2\uFF44"), null, "canvas.getContext(\"2\\uFF44\")", "null"); // Fullwidth Latin Small Letter D
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.basic.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.basic.html
new file mode 100644
index 0000000000..5aaf49adf7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.basic.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: fallback.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>fallback.basic</h1>
+<p class="desc">Fallback content is inserted into the DOM</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Fallback content is inserted into the DOM");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.childNodes.length, 1, "canvas.childNodes.length", "1");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.multiple.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.multiple.html
new file mode 100644
index 0000000000..9585b06a4a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.multiple.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: fallback.multiple</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>fallback.multiple</h1>
+<p class="desc">Fallback content with multiple elements</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL</p><p class="fallback">FAIL</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Fallback content with multiple elements");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.childNodes.length, 2, "canvas.childNodes.length", "2");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.nested.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.nested.html
new file mode 100644
index 0000000000..14b19cd104
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/fallback.nested.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: fallback.nested</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>fallback.nested</h1>
+<p class="desc">Fallback content containing another canvas (mostly testing parsers)</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><canvas><p class="fallback">FAIL (fallback content)</p></canvas><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Fallback content containing another canvas (mostly testing parsers)");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.childNodes.length, 2, "canvas.childNodes.length", "2");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/historical.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/historical.html
new file mode 100644
index 0000000000..33044ffb1b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/historical.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Historical canvas features</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+var canvas, context, path2d;
+setup(function() {
+ canvas = document.createElement("canvas");
+ context = canvas.getContext('2d');
+ path2d = new Path2D();
+});
+function t(member, obj) {
+ var name = obj === canvas ? "Canvas" : String(obj).match(/\[object (\S+)\]/)[1];
+ test(function() {
+ assert_false(member in obj);
+ }, name + " support for " + member);
+}
+// added in https://github.com/whatwg/html/commit/0ecbf0e010df16d9c6d11eef6b2c58419158c4da
+// renamed in https://github.com/whatwg/html/commit/2542a12cb25ee93534cbed1f31b5e1bc05fcdd0e
+t("supportsContext", canvas);
+
+// removed in https://github.com/whatwg/html/commit/2cfb8e3f03d3166842d2ad0f661459d26e2a40eb
+t("probablySupportsContext", canvas);
+
+// removed in https://github.com/whatwg/html/commit/ef72f55da4acdf266174225c6ca8bf2a650d0219
+t("width", context);
+t("height", context);
+
+// removed in https://github.com/whatwg/html/commit/740634d0f30a3b76e9da166ac2fa8835fcc073ab
+t("setContext", canvas);
+t("transferControlToProxy", canvas);
+t("CanvasProxy", window);
+t("commit", canvas);
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new CanvasRenderingContext2D();
+ }, 'no arguments');
+ assert_throws_js(TypeError, function() {
+ new CanvasRenderingContext2D(1, 1);
+ }, 'with arguments');
+}, "CanvasRenderingContext2D constructors");
+
+// removed in https://github.com/whatwg/html/commit/e1d04f49a38e2254a783c28987457a95a47d9511
+t("addPathByStrokingPath", path2d);
+t("addText", path2d);
+t("addPathByStrokingText", path2d);
+
+// renamed in https://github.com/whatwg/html/commit/fcb0756dd94d96df9b8355741d82fcd5ca0a6154
+test(function() {
+ var canvas = document.createElement('canvas');
+ var context = canvas.getContext('bitmaprenderer');
+ if (context) {
+ assert_false('transferImageBitmap' in context);
+ }
+}, 'ImageBitmapRenderingContext support for transferImageBitmap');
+
+// renamed in https://github.com/whatwg/html/commit/3aec2a7e04a3402201afd29c224b57fa54497517
+t('Path', window);
+
+// removed in https://github.com/whatwg/html/commit/d5759b0435091e4858c9bff90319cbe5b040eda2
+t('toDataURLHD', canvas);
+t('toBlobHD', canvas);
+t('createImageDataHD', context);
+t('getImageDataHD', context);
+t('putImageDataHD', context);
+test(function() {
+ if ('ImageData' in window) {
+ assert_false('resolution' in new ImageData(1, 1));
+ }
+}, 'ImageData support for resolution');
+
+// dropped/renamed in https://github.com/whatwg/html/commit/ff07c6d630fb986f6c4f64b2fb87387b4f89647d
+t('drawSystemFocusRing', context);
+t('drawCustomFocusRing', context);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/imagedata.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/imagedata.html
new file mode 100644
index 0000000000..e124f8ff6e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/imagedata.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>ImageData Tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ new ImageData(0, 1);
+ });
+}, "ImageData(w, h), width cannot be 0");
+
+test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ new ImageData(1, 0);
+ });
+}, "ImageData(w, h), height cannot be 0");
+
+test(function() {
+ var imageData = new ImageData(2, 3);
+ assert_equals(imageData.width, 2);
+ assert_equals(imageData.height, 3);
+ assert_equals(imageData.data.length, 24);
+ assert_true(imageData.data instanceof Uint8ClampedArray);
+}, "ImageData(w, h), exposed attributes check");
+
+test(function() {
+ assert_throws_dom("InvalidStateError", function() {
+ new ImageData(new Uint8ClampedArray(3), 1);
+ });
+}, "ImageData(buffer, w), the buffer size must be a multiple of 4");
+
+test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ new ImageData(new Uint8ClampedArray(16), 3);
+ });
+}, "ImageData(buffer, w), buffer size must be a multiple of the image width");
+
+test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ new ImageData(new Uint8ClampedArray(16), 4, 3);
+ });
+}, "ImageData(buffer, w, h), buffer.length == 4 * w * h must be true");
+
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new ImageData(new Int8Array(1), 1);
+ });
+}, "ImageData(buffer, w, opt h), Uint8ClampedArray argument type check");
+
+test(function() {
+ var imageData = new ImageData(new Uint8ClampedArray(24), 2);
+ assert_equals(imageData.width, 2);
+ assert_equals(imageData.height, 3);
+ assert_equals(imageData.data.length, 24);
+ assert_true(imageData.data instanceof Uint8ClampedArray);
+}, "ImageData(buffer, w, opt h), exposed attributes check");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.html
new file mode 100644
index 0000000000..166732a57b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.colour</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.colour</h1>
+<p class="desc">Initial state is transparent black</p>
+
+<p class="notes">Output should be transparent black (not transparent anything-else), but manual
+verification can only confirm that it's transparent - it's not possible to make
+the actual blackness visible.
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="initial.colour.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Initial state is transparent black");
+_addTest(function(canvas, ctx) {
+
+_assertPixel(canvas, 20,20, 0,0,0,0);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.colour.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.clip.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.clip.html
new file mode 100644
index 0000000000..ebf52bfa76
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.clip.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.reset.clip</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.reset.clip</h1>
+<p class="desc">Resetting the canvas state resets the current clip region</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state resets the current clip region");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 100;
+ctx.rect(0, 0, 1, 1);
+ctx.clip();
+canvas.width = 100;
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 20,20, 0,255,0,255);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.html
new file mode 100644
index 0000000000..d55dd250c0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.reset.different</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.reset.different</h1>
+<p class="desc">Changing size resets canvas to transparent black</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="initial.reset.different.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Changing size resets canvas to transparent black");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 50, 50);
+_assertPixel(canvas, 20,20, 255,0,0,255);
+canvas.width = 50;
+_assertPixel(canvas, 20,20, 0,0,0,0);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.png
new file mode 100644
index 0000000000..d83fdd55b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.different.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.gradient.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.gradient.html
new file mode 100644
index 0000000000..31b56ec8e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.gradient.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.reset.gradient</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.reset.gradient</h1>
+<p class="desc">Resetting the canvas state does not invalidate any existing gradients</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state does not invalidate any existing gradients");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 50;
+var g = ctx.createLinearGradient(0, 0, 100, 0);
+g.addColorStop(0, '#0f0');
+g.addColorStop(1, '#0f0');
+canvas.width = 100;
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+ctx.fillStyle = g;
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 0,255,0,255);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.html
new file mode 100644
index 0000000000..3525377d2d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.reset.path</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.reset.path</h1>
+<p class="desc">Resetting the canvas state resets the current path</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="initial.reset.path.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state resets the current path");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 100;
+ctx.rect(0, 0, 100, 50);
+canvas.width = 100;
+ctx.fillStyle = '#f00';
+ctx.fill();
+_assertPixel(canvas, 20,20, 0,0,0,0);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.path.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.pattern.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.pattern.html
new file mode 100644
index 0000000000..28f8306d96
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.pattern.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.reset.pattern</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.reset.pattern</h1>
+<p class="desc">Resetting the canvas state does not invalidate any existing patterns</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state does not invalidate any existing patterns");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 30;
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 30, 50);
+var p = ctx.createPattern(canvas, 'repeat-x');
+canvas.width = 100;
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+ctx.fillStyle = p;
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 0,255,0,255);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.html
new file mode 100644
index 0000000000..1a0872ba2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.reset.same</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.reset.same</h1>
+<p class="desc">Setting size (not changing the value) resets canvas to transparent black</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="initial.reset.same.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting size (not changing the value) resets canvas to transparent black");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 100;
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 50, 50);
+_assertPixel(canvas, 20,20, 255,0,0,255);
+canvas.width = 100;
+_assertPixel(canvas, 20,20, 0,0,0,0);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.same.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.transform.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.transform.html
new file mode 100644
index 0000000000..36284ba498
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/initial.reset.transform.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: initial.reset.transform</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>initial.reset.transform</h1>
+<p class="desc">Resetting the canvas state resets the current transformation matrix</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state resets the current transformation matrix");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 100;
+ctx.scale(0.1, 0.1);
+canvas.width = 100;
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 20,20, 0,255,0,255);
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.dataURI.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.dataURI.html
new file mode 100644
index 0000000000..93b560e82c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.dataURI.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.dataURI</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.dataURI</h1>
+<p class="desc">data: URIs do not count as different-origin, and do not taint the canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("data: URIs do not count as different-origin, and do not taint the canvas");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 50);
+var data = canvas.toDataURL();
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+var img = new Image();
+deferTest();
+img.onload = t.step_func_done(function ()
+{
+ ctx.drawImage(img, 0, 0);
+ canvas.toDataURL(); // should be permitted
+ _assertPixel(canvas, 50,25, 0,255,0,255);
+});
+img.src = data;
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.cross.html
new file mode 100644
index 0000000000..3a32cf2c16
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.cross.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.drawImage.canvas.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.drawImage.canvas.cross</h1>
+<p class="desc">drawImage of unclean canvas makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage of unclean canvas makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0);
+ctx.drawImage(canvas2, 0, 0);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.redirect.html
new file mode 100644
index 0000000000..5545205837
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.redirect.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.drawImage.canvas.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.drawImage.canvas.redirect</h1>
+<p class="desc">drawImage of unclean canvas makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage of unclean canvas makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0);
+ctx.drawImage(canvas2, 0, 0);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.cross.html
new file mode 100644
index 0000000000..b58177edc5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.cross.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.drawImage.image.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.drawImage.image.cross</h1>
+<p class="desc">drawImage of different-origin image makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage of different-origin image makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+ctx.drawImage(document.getElementById('yellow.png'), 0, 0);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.redirect.html
new file mode 100644
index 0000000000..4661554f9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.redirect.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.drawImage.image.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.drawImage.image.redirect</h1>
+<p class="desc">drawImage of different-origin image makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("drawImage of different-origin image makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+ctx.drawImage(document.getElementById('yellow.png'), 0, 0);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.cross.html
new file mode 100644
index 0000000000..35f0c70723
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.cross.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.canvas.fillStyle.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.canvas.fillStyle.cross</h1>
+<p class="desc">Setting fillStyle to a pattern of an unclean canvas makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting fillStyle to a pattern of an unclean canvas makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0);
+var p = ctx.createPattern(canvas2, 'repeat');
+ctx.fillStyle = p;
+ctx.fillStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.redirect.html
new file mode 100644
index 0000000000..d17be93233
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.redirect.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.canvas.fillStyle.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.canvas.fillStyle.redirect</h1>
+<p class="desc">Setting fillStyle to a pattern of an unclean canvas makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting fillStyle to a pattern of an unclean canvas makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0);
+var p = ctx.createPattern(canvas2, 'repeat');
+ctx.fillStyle = p;
+ctx.fillStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.cross.html
new file mode 100644
index 0000000000..828becef8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.cross.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.canvas.strokeStyle.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.canvas.strokeStyle.cross</h1>
+<p class="desc">Setting strokeStyle to a pattern of an unclean canvas makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting strokeStyle to a pattern of an unclean canvas makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0);
+var p = ctx.createPattern(canvas2, 'repeat');
+ctx.strokeStyle = p;
+ctx.strokeStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.redirect.html
new file mode 100644
index 0000000000..c6e7a64c15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.redirect.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.canvas.strokeStyle.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.canvas.strokeStyle.redirect</h1>
+<p class="desc">Setting strokeStyle to a pattern of an unclean canvas makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting strokeStyle to a pattern of an unclean canvas makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0);
+var p = ctx.createPattern(canvas2, 'repeat');
+ctx.strokeStyle = p;
+ctx.strokeStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.cross.html
new file mode 100644
index 0000000000..1ae1c4928b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.cross.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.canvas.timing.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.canvas.timing.cross</h1>
+<p class="desc">Pattern safety depends on whether the source was origin-clean, not on whether it still is clean</p>
+
+<p class="notes">Disagrees with spec on "is" vs "was"
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Pattern safety depends on whether the source was origin-clean, not on whether it still is clean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.fillStyle = '#0f0';
+ctx2.fillRect(0, 0, 100, 50);
+var p = ctx.createPattern(canvas2, 'repeat');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0); // make canvas2 origin-unclean
+ctx.fillStyle = p;
+ctx.fillRect(0, 0, 100, 50);
+canvas.toDataURL();
+ctx.getImageData(0, 0, 1, 1);
+_assert(true, "true"); // okay if there was no exception
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.redirect.html
new file mode 100644
index 0000000000..f48366cd54
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.timing.redirect.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.canvas.timing.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.canvas.timing.redirect</h1>
+<p class="desc">Pattern safety depends on whether the source was origin-clean, not on whether it still is clean</p>
+
+<p class="notes">Disagrees with spec on "is" vs "was"
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Pattern safety depends on whether the source was origin-clean, not on whether it still is clean");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+ctx2.fillStyle = '#0f0';
+ctx2.fillRect(0, 0, 100, 50);
+var p = ctx.createPattern(canvas2, 'repeat');
+ctx2.drawImage(document.getElementById('yellow.png'), 0, 0); // make canvas2 origin-unclean
+ctx.fillStyle = p;
+ctx.fillRect(0, 0, 100, 50);
+canvas.toDataURL();
+ctx.getImageData(0, 0, 1, 1);
+_assert(true, "true"); // okay if there was no exception
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.cross.html
new file mode 100644
index 0000000000..e0d3d10556
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.cross.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.create.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.create.cross</h1>
+<p class="desc">Creating an unclean pattern does not make the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Creating an unclean pattern does not make the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat');
+canvas.toDataURL();
+ctx.getImageData(0, 0, 1, 1);
+_assert(true, "true"); // okay if there was no exception
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.redirect.html
new file mode 100644
index 0000000000..3fb7cf98b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.create.redirect.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.create.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.create.redirect</h1>
+<p class="desc">Creating an unclean pattern does not make the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Creating an unclean pattern does not make the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat');
+canvas.toDataURL();
+ctx.getImageData(0, 0, 1, 1);
+_assert(true, "true"); // okay if there was no exception
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.cross.html
new file mode 100644
index 0000000000..2dd14a87a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.cross.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.cross.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.cross.cross</h1>
+<p class="desc">Using an unclean pattern makes the target canvas origin-unclean, not the pattern canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Using an unclean pattern makes the target canvas origin-unclean, not the pattern canvas");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+var p = ctx2.createPattern(document.getElementById('yellow.png'), 'repeat');
+ctx.fillStyle = p;
+ctx.fillRect(0, 0, 100, 50);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+canvas2.toDataURL();
+ctx2.getImageData(0, 0, 1, 1);
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.redirect.html
new file mode 100644
index 0000000000..8d69ea0afc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.redirect.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.cross.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.cross.redirect</h1>
+<p class="desc">Using an unclean pattern makes the target canvas origin-unclean, not the pattern canvas</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Using an unclean pattern makes the target canvas origin-unclean, not the pattern canvas");
+_addTest(function(canvas, ctx) {
+
+var canvas2 = document.createElement('canvas');
+canvas2.width = 100;
+canvas2.height = 50;
+var ctx2 = canvas2.getContext('2d');
+var p = ctx2.createPattern(document.getElementById('yellow.png'), 'repeat');
+ctx.fillStyle = p;
+ctx.fillRect(0, 0, 100, 50);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+canvas2.toDataURL();
+ctx2.getImageData(0, 0, 1, 1);
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html
new file mode 100644
index 0000000000..a1053c04a4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by tools/gentest.py. -->
+<title>Canvas test: security.pattern.canvas.fillStyle.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/media.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<body>
+<p class="desc">Setting fillStyle to a pattern of an unclean canvas makes the canvas origin-unclean</p>
+
+<script>
+
+forEachCanvasSource(get_host_info().HTTP_REMOTE_ORIGIN,
+ get_host_info().HTTP_ORIGIN,
+ (name, factory) => {
+ promise_test(_ => {
+ return factory().then(source => {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ const pattern = ctx.createPattern(source, 'repeat');
+ ctx.fillStyle = pattern;
+ ctx.fillStyle = 'red';
+ assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+ assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+ });
+ }, `${name}: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean`);
+});
+
+forEachCanvasSource(get_host_info().HTTP_REMOTE_ORIGIN,
+ get_host_info().HTTP_ORIGIN,
+ (name, factory) => {
+ promise_test(_ => {
+ return factory().then(source => {
+ const pattern = new OffscreenCanvas(10, 10)
+ .getContext('2d')
+ .createPattern(source, 'repeat');
+
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ ctx.fillStyle = pattern;
+ ctx.fillStyle = 'red';
+ assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+ assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+ });
+ }, `${name}: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean`);
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.cross.html
new file mode 100644
index 0000000000..1d7dc1d84f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.cross.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.image.fillStyle.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.image.fillStyle.cross</h1>
+<p class="desc">Setting fillStyle to a pattern of a different-origin image makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting fillStyle to a pattern of a different-origin image makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat');
+ctx.fillStyle = p;
+ctx.fillStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.redirect.html
new file mode 100644
index 0000000000..3af917705b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.redirect.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.image.fillStyle.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.image.fillStyle.redirect</h1>
+<p class="desc">Setting fillStyle to a pattern of a different-origin image makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting fillStyle to a pattern of a different-origin image makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat');
+ctx.fillStyle = p;
+ctx.fillStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.cross.html
new file mode 100644
index 0000000000..e35535af43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.cross.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.image.strokeStyle.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.image.strokeStyle.cross</h1>
+<p class="desc">Setting strokeStyle to a pattern of a different-origin image makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting strokeStyle to a pattern of a different-origin image makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat');
+ctx.strokeStyle = p;
+ctx.strokeStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.redirect.html
new file mode 100644
index 0000000000..09df15d24f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.redirect.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.pattern.image.strokeStyle.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.pattern.image.strokeStyle.redirect</h1>
+<p class="desc">Setting strokeStyle to a pattern of a different-origin image makes the canvas origin-unclean</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting strokeStyle to a pattern of a different-origin image makes the canvas origin-unclean");
+_addTest(function(canvas, ctx) {
+
+var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat');
+ctx.strokeStyle = p;
+ctx.strokeStyle = 'red';
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+assert_throws_dom("SECURITY_ERR", function() { ctx.getImageData(0, 0, 1, 1); });
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.cross.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.cross.html
new file mode 100644
index 0000000000..f823bbd8ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.cross.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.reset.cross</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.reset.cross</h1>
+<p class="desc">Resetting the canvas state resets the origin-clean flag</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state resets the origin-clean flag");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 50;
+ctx.drawImage(document.getElementById('yellow.png'), 0, 0);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+canvas.width = 100;
+canvas.toDataURL();
+ctx.getImageData(0, 0, 1, 1);
+_assert(true, "true"); // okay if there was no exception
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.redirect.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.redirect.html
new file mode 100644
index 0000000000..af881c5fdc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/security.reset.redirect.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: security.reset.redirect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>security.reset.redirect</h1>
+<p class="desc">Resetting the canvas state resets the origin-clean flag</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Resetting the canvas state resets the origin-clean flag");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 50;
+ctx.drawImage(document.getElementById('yellow.png'), 0, 0);
+assert_throws_dom("SECURITY_ERR", function() { canvas.toDataURL(); });
+canvas.width = 100;
+canvas.toDataURL();
+ctx.getImageData(0, 0, 1, 1);
+_assert(true, "true"); // okay if there was no exception
+
+
+});
+</script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="data:text/javascript,addCrossOriginRedirectYellowImage()"></script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.html
new file mode 100644
index 0000000000..ecf35285a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.default</h1>
+<p class="desc">Default width/height when attributes are missing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" ><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="size.attributes.default.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Default width/height when attributes are missing");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.width, 300, "canvas.width", "300");
+_assertSame(canvas.height, 150, "canvas.height", "150");
+_assert(!canvas.hasAttribute('width'), "!canvas.hasAttribute('width')");
+_assert(!canvas.hasAttribute('height'), "!canvas.hasAttribute('height')");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.png
new file mode 100644
index 0000000000..a72d047556
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.default.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.get.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.get.png
new file mode 100644
index 0000000000..47830c83ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.get.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.html
new file mode 100644
index 0000000000..1594a1c5e5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.idl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.idl</h1>
+<p class="desc">Getting/setting width/height IDL attributes</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Getting/setting width/height IDL attributes");
+_addTest(function(canvas, ctx) {
+
+canvas.width = "100";
+canvas.height = "100";
+_assertSame(canvas.width, 100, "canvas.width", "100");
+_assertSame(canvas.height, 100, "canvas.height", "100");
+
+canvas.width = "+1.5e2";
+canvas.height = "0x96";
+_assertSame(canvas.width, 150, "canvas.width", "150");
+_assertSame(canvas.height, 150, "canvas.height", "150");
+
+canvas.width = 200 - Math.pow(2, 32);
+canvas.height = 200 - Math.pow(2, 32);
+_assertSame(canvas.width, 200, "canvas.width", "200");
+_assertSame(canvas.height, 200, "canvas.height", "200");
+
+canvas.width = 301.999;
+canvas.height = 301.001;
+_assertSame(canvas.width, 301, "canvas.width", "301");
+_assertSame(canvas.height, 301, "canvas.height", "301");
+
+canvas.width = "400x";
+canvas.height = "foo";
+_assertSame(canvas.width, 0, "canvas.width", "0");
+_assertSame(canvas.height, 0, "canvas.height", "0");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.set.zero.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.set.zero.html
new file mode 100644
index 0000000000..c09d5cb278
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.idl.set.zero.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.idl.set.zero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.idl.set.zero</h1>
+<p class="desc">Setting width/height IDL attributes to 0</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting width/height IDL attributes to 0");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 0;
+canvas.height = 0;
+_assertSame(canvas.width, 0, "canvas.width", "0");
+_assertSame(canvas.height, 0, "canvas.height", "0");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.html
new file mode 100644
index 0000000000..a25c4b784a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.reflect.setcontent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.reflect.setcontent</h1>
+<p class="desc">Setting content attributes updates IDL and content attributes</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="size.attributes.reflect.setcontent.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting content attributes updates IDL and content attributes");
+_addTest(function(canvas, ctx) {
+
+canvas.setAttribute('width', '120');
+canvas.setAttribute('height', '60');
+_assertSame(canvas.getAttribute('width'), '120', "canvas.getAttribute('width')", "'120'");
+_assertSame(canvas.getAttribute('height'), '60', "canvas.getAttribute('height')", "'60'");
+_assertSame(canvas.width, 120, "canvas.width", "120");
+_assertSame(canvas.height, 60, "canvas.height", "60");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.png
new file mode 100644
index 0000000000..47830c83ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setcontent.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.html
new file mode 100644
index 0000000000..e228276da7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.reflect.setidl</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.reflect.setidl</h1>
+<p class="desc">Setting IDL attributes updates IDL and content attributes</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="size.attributes.reflect.setidl.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting IDL attributes updates IDL and content attributes");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 120;
+canvas.height = 60;
+_assertSame(canvas.getAttribute('width'), '120', "canvas.getAttribute('width')", "'120'");
+_assertSame(canvas.getAttribute('height'), '60', "canvas.getAttribute('height')", "'60'");
+_assertSame(canvas.width, 120, "canvas.width", "120");
+_assertSame(canvas.height, 60, "canvas.height", "60");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.png
new file mode 100644
index 0000000000..47830c83ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidl.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidlzero.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidlzero.html
new file mode 100644
index 0000000000..65df3f9f94
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.reflect.setidlzero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.reflect.setidlzero</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.reflect.setidlzero</h1>
+<p class="desc">Setting IDL attributes to 0 updates IDL and content attributes</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("Setting IDL attributes to 0 updates IDL and content attributes");
+_addTest(function(canvas, ctx) {
+
+canvas.width = 0;
+canvas.height = 0;
+_assertSame(canvas.getAttribute('width'), '0', "canvas.getAttribute('width')", "'0'");
+_assertSame(canvas.getAttribute('height'), '0', "canvas.getAttribute('height')", "'0'");
+_assertSame(canvas.width, 0, "canvas.width", "0");
+_assertSame(canvas.height, 0, "canvas.height", "0");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.html
new file mode 100644
index 0000000000..c96cba7b17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.removed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.removed</h1>
+<p class="desc">Removing content attributes reverts to default size</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="120" height="60"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="size.attributes.removed.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Removing content attributes reverts to default size");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.width, 120, "canvas.width", "120");
+canvas.removeAttribute('width');
+_assertSame(canvas.width, 300, "canvas.width", "300");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.png
new file mode 100644
index 0000000000..1ebf30d8aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.removed.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.set.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.set.png
new file mode 100644
index 0000000000..47830c83ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.set.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.html
new file mode 100644
index 0000000000..aeb5c7ecb2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: size.attributes.style</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>size.attributes.style</h1>
+<p class="desc">Canvas size is independent of CSS resizing</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="50" height="30" style="width: 100px; height: 50px"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="size.attributes.style.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("Canvas size is independent of CSS resizing");
+_addTest(function(canvas, ctx) {
+
+_assertSame(canvas.width, 50, "canvas.width", "50");
+_assertSame(canvas.height, 30, "canvas.height", "30");
+
+
+});
+</script>
+
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.png
new file mode 100644
index 0000000000..eeedd0ff05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/size.attributes.style.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html
new file mode 100644
index 0000000000..393170baad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>toBlob() reports the exception from its callback in the callback's global object</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe srcdoc="<canvas></canvas>"></iframe>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+setup({ allow_uncaught_exception: true });
+
+const onerrorCalls = [];
+window.onerror = () => { onerrorCalls.push("top"); };
+frames[0].onerror = () => { onerrorCalls.push("frame0"); };
+frames[1].onerror = () => { onerrorCalls.push("frame1"); };
+frames[2].onerror = () => { onerrorCalls.push("frame2"); };
+
+async_test(t => {
+ window.onload = t.step_func(() => {
+ const canvas = frames[0].document.querySelector("canvas");
+ canvas.toBlob(new frames[1].Function(`throw new parent.frames[2].Error("PASS");`));
+
+ t.step_timeout(() => {
+ assert_array_equals(onerrorCalls, ["frame1"]);
+ t.done();
+ }, 25);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.jpeg.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.jpeg.html
new file mode 100644
index 0000000000..1a95d4a6dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.jpeg.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Canvas test: toBlob.jpeg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<canvas id="c"></canvas>
+<script>
+async_test(function() {
+ on_event(window, "load", this.step_func(function() {
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+ canvas.toBlob(this.step_func_done(function(data) {
+ assert_equals(data.type, "image/jpeg");
+ }), 'image/jpeg');
+ }));
+}, "toBlob with image/jpeg returns a JPEG Blob");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.null.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.null.html
new file mode 100644
index 0000000000..11368a169c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.null.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Canvas test: toBlob.null</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toBlob.null</h1>
+<p class="desc">toBlob with zero dimension returns a null Blob</p>
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="0"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+async_test(function() {
+ on_event(window, "load", this.step_func(function() {
+ var toBlobCalled = false;
+ c.toBlob(this.step_func(function(blob) {
+ toBlobCalled = true;
+ _assertSame(blob, null, "blob", "null");
+ c.width = 0;
+ c.height = 100;
+ c.toBlob(this.step_func_done(function(blob) {
+ _assertSame(blob, null, "blob", "null");
+ }), 'image/jpeg');
+ }), 'image/jpeg');
+ assert_false(toBlobCalled, "toBlob callback shouldn't be called synchronously");
+ }));
+}, "toBlob with zero dimension returns a null Blob");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.png.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.png.html
new file mode 100644
index 0000000000..1533bfdb6c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toBlob.png.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Canvas test: toBlob.png</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<canvas id="c"></canvas>
+<script>
+async_test(function() {
+ on_event(window, "load", this.step_func(function() {
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+ canvas.toBlob(this.step_func_done(function(data) {
+ assert_equals(data.type, "image/png");
+ }), 'image/png');
+ }));
+}, "toBlob with image/png returns a PNG Blob");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.1.html
new file mode 100644
index 0000000000..8ed134c6b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.arguments.1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.arguments.1</h1>
+<p class="desc">toDataURL ignores extra arguments</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL ignores extra arguments");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL('image/png', 'another argument that should not raise an exception');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.2.html
new file mode 100644
index 0000000000..5226c215f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.arguments.2</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.arguments.2</h1>
+<p class="desc">toDataURL ignores extra arguments</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL ignores extra arguments");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL('image/png', 'another argument that should not raise an exception', 'and another');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.3.html
new file mode 100644
index 0000000000..23b3e33ed8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.arguments.3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.arguments.3</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.arguments.3</h1>
+<p class="desc">toDataURL ignores extra arguments</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL ignores extra arguments");
+_addTest(function(canvas, ctx) {
+
+// More arguments that should not raise exceptions
+var data = canvas.toDataURL('image/png', null, null, null);
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.bogustype.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.bogustype.html
new file mode 100644
index 0000000000..9b2414fc0b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.bogustype.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.bogustype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.bogustype</h1>
+<p class="desc">toDataURL with a syntactically invalid type returns a PNG</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with a syntactically invalid type returns a PNG");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL('bogus');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.default.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.default.html
new file mode 100644
index 0000000000..8bae384373
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.default.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.default</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.default</h1>
+<p class="desc">toDataURL with no arguments returns a PNG</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with no arguments returns a PNG");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL();
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.html
new file mode 100644
index 0000000000..daf278351d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.jpeg.alpha</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.jpeg.alpha</h1>
+<p class="desc">toDataURL with JPEG composites onto black</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="toDataURL.jpeg.alpha.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with JPEG composites onto black");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = 'rgba(128, 255, 128, 0.5)';
+ctx.fillRect(0, 0, 100, 50);
+ctx.globalCompositeOperation = 'destination-over'; // should be ignored by toDataURL
+var data = canvas.toDataURL('image/jpeg');
+ctx.globalCompositeOperation = 'source-over';
+if (!data.match(/^data:image\/jpeg[;,]/)) {
+ _assert(true, "true");
+} else {
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ var img = new Image();
+ deferTest();
+ img.onload = t.step_func_done(function ()
+ {
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 50,25, 63,127,63,255, 8);
+ });
+ img.src = data;
+}
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.png
new file mode 100644
index 0000000000..551871295c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.alpha.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.html
new file mode 100644
index 0000000000..750487bdea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.jpeg.primarycolours</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.jpeg.primarycolours</h1>
+<p class="desc">toDataURL with JPEG handles simple colours correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="toDataURL.jpeg.primarycolours.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with JPEG handles simple colours correctly");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#ff0';
+ctx.fillRect(0, 0, 25, 40);
+ctx.fillStyle = '#0ff';
+ctx.fillRect(25, 0, 50, 40);
+ctx.fillStyle = '#00f';
+ctx.fillRect(75, 0, 25, 40);
+ctx.fillStyle = '#fff';
+ctx.fillRect(0, 40, 100, 10);
+var data = canvas.toDataURL('image/jpeg'); // it is okay if this returns a PNG instead
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+var img = new Image();
+deferTest();
+img.onload = t.step_func_done(function ()
+{
+ ctx.drawImage(img, 0, 0);
+ _assertPixelApprox(canvas, 12,20, 255,255,0,255, 8);
+ _assertPixelApprox(canvas, 50,20, 0,255,255,255, 8);
+ _assertPixelApprox(canvas, 87,20, 0,0,255,255, 8);
+ _assertPixelApprox(canvas, 50,45, 255,255,255,255, 8);
+});
+img.src = data;
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.png
new file mode 100644
index 0000000000..cfd1369007
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.html
new file mode 100644
index 0000000000..dc5d814244
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.jpeg.quality.basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.jpeg.quality.basic</h1>
+<p class="desc">toDataURL with JPEG uses the quality parameter</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="toDataURL.jpeg.quality.basic.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with JPEG uses the quality parameter");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#00f';
+ctx.fillRect(0, 0, 100, 50);
+ctx.fillStyle = '#0ff';
+ctx.fillRect(0, 3, 100, 1);
+// Check for JPEG support first
+var data = canvas.toDataURL('image/jpeg');
+if (!data.match(/^data:image\/jpeg[;,]/)) {
+ _assert(true, "true");
+} else {
+ var data_hi = canvas.toDataURL('image/jpeg', 0.99);
+ var data_lo = canvas.toDataURL('image/jpeg', 0.01);
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(0, 0, 100, 50);
+ deferTest();
+ var img_hi = new Image();
+ img_hi.onload = function ()
+ {
+ var img_lo = new Image();
+ img_lo.onload = t.step_func_done(function ()
+ {
+ ctx.drawImage(img_hi, 0, 0, 50, 50, 0, 0, 50, 50);
+ ctx.drawImage(img_lo, 0, 0, 50, 50, 50, 0, 50, 50);
+ _assert(data_hi.length > data_lo.length, "data_hi.length > data_lo.length");
+ _assertPixelApprox(canvas, 25,25, 0,0,255,255, 8);
+ _assertPixelApprox(canvas, 75,25, 0,0,255,255, 32);
+ });
+ img_lo.src = data_lo;
+ };
+ img_hi.src = data_hi;
+}
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.png
new file mode 100644
index 0000000000..2f8a0bc790
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.basic.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.notnumber.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.notnumber.html
new file mode 100644
index 0000000000..aa8066e67f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.notnumber.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.jpeg.quality.notnumber</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.jpeg.quality.notnumber</h1>
+<p class="desc">toDataURL with JPEG handles non-numeric quality parameters</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with JPEG handles non-numeric quality parameters");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#00f';
+ctx.fillRect(0, 0, 100, 50);
+ctx.fillStyle = '#0ff';
+ctx.fillRect(0, 3, 100, 1);
+// Check for JPEG support first
+var data = canvas.toDataURL('image/jpeg');
+if (!data.match(/^data:image\/jpeg[;,]/)) {
+ _assert(true, "true");
+} else {
+ _assertSame(canvas.toDataURL('image/jpeg', 'bogus'), data, "canvas.toDataURL('image/jpeg', 'bogus')", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', {}), data, "canvas.toDataURL('image/jpeg', {})", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', null), data, "canvas.toDataURL('image/jpeg', null)", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', undefined), data, "canvas.toDataURL('image/jpeg', undefined)", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', true), data, "canvas.toDataURL('image/jpeg', true)", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', '0.01'), data, "canvas.toDataURL('image/jpeg', '0.01')", "data");
+}
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.outsiderange.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.outsiderange.html
new file mode 100644
index 0000000000..9e40fb887b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.quality.outsiderange.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.jpeg.quality.outsiderange</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.jpeg.quality.outsiderange</h1>
+<p class="desc">toDataURL with JPEG handles out-of-range quality parameters</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with JPEG handles out-of-range quality parameters");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#00f';
+ctx.fillRect(0, 0, 100, 50);
+ctx.fillStyle = '#0ff';
+ctx.fillRect(0, 3, 100, 1);
+// Check for JPEG support first
+var data = canvas.toDataURL('image/jpeg');
+if (!data.match(/^data:image\/jpeg[;,]/)) {
+ _assert(true, "true");
+} else {
+ _assertSame(canvas.toDataURL('image/jpeg', 10), data, "canvas.toDataURL('image/jpeg', 10)", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', -10), data, "canvas.toDataURL('image/jpeg', -10)", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', 1.01), data, "canvas.toDataURL('image/jpeg', 1.01)", "data");
+ _assertSame(canvas.toDataURL('image/jpeg', -0.01), data, "canvas.toDataURL('image/jpeg', -0.01)", "data");
+
+ _assert(canvas.toDataURL('image/jpeg', 1).length >= canvas.toDataURL('image/jpeg', 0.9).length, "canvas.toDataURL('image/jpeg', 1).length >= canvas.toDataURL('image/jpeg', 0.9).length");
+ _assert(canvas.toDataURL('image/jpeg', 0).length <= canvas.toDataURL('image/jpeg', 0.1).length, "canvas.toDataURL('image/jpeg', 0).length <= canvas.toDataURL('image/jpeg', 0.1).length");
+}
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpg.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpg.html
new file mode 100644
index 0000000000..e59793db70
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.jpg.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.jpg</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.jpg</h1>
+<p class="desc">toDataURL with image/jpg is invalid type hence returns a PNG</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with image/jpg is invalid type hence returns a PNG");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL('image/jpg');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.ascii.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.ascii.html
new file mode 100644
index 0000000000..858035cc64
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.ascii.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.lowercase.ascii</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.lowercase.ascii</h1>
+<p class="desc">toDataURL type is case-insensitive</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL type is case-insensitive");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL('ImAgE/PnG');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+// If JPEG is supported at all, it must be supported case-insensitively
+data = canvas.toDataURL('image/jpeg');
+if (data.match(/^data:image\/jpeg[;,]/)) {
+ data = canvas.toDataURL('ImAgE/JpEg');
+ assert_regexp_match(data, /^data:image\/jpeg[;,]/);
+}
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.unicode.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.unicode.html
new file mode 100644
index 0000000000..123f966ee2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.lowercase.unicode.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.lowercase.unicode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.lowercase.unicode</h1>
+<p class="desc">toDataURL type is ASCII-case-insensitive</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL type is ASCII-case-insensitive");
+_addTest(function(canvas, ctx) {
+
+// Use LATIN CAPITAL LETTER I WITH DOT ABOVE (Unicode lowercase is "i")
+var data = canvas.toDataURL('\u0130mage/png');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+var data = canvas.toDataURL('\u0130mage/jpeg');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.nocontext.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.nocontext.html
new file mode 100644
index 0000000000..704dc74fb9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.nocontext.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.nocontext</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.nocontext</h1>
+<p class="desc">toDataURL works before any context has been got</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL works before any context has been got");
+_addTest(function(canvas, ctx) {
+
+var no_context_data = canvas.toDataURL();
+var ctx = canvas.getContext('2d');
+ctx.rect(0, 0, 100, 50);
+ctx.fillStyle = "rgba(0, 0, 0, 0)";
+ctx.fill();
+var data = canvas.toDataURL();
+assert_equals(no_context_data, data);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.html
new file mode 100644
index 0000000000..dadea7c5b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.png.complexcolours</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.png.complexcolours</h1>
+<p class="desc">toDataURL with PNG handles non-primary and non-solid colours correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="toDataURL.png.complexcolours.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with PNG handles non-primary and non-solid colours correctly");
+_addTest(function(canvas, ctx) {
+
+// (These values are chosen to survive relatively alright through being premultiplied)
+ctx.fillStyle = 'rgba(1, 3, 254, 1)';
+ctx.fillRect(0, 0, 25, 25);
+ctx.fillStyle = 'rgba(8, 252, 248, 0.75)';
+ctx.fillRect(25, 0, 25, 25);
+ctx.fillStyle = 'rgba(6, 10, 250, 0.502)';
+ctx.fillRect(50, 0, 25, 25);
+ctx.fillStyle = 'rgba(12, 16, 244, 0.25)';
+ctx.fillRect(75, 0, 25, 25);
+var img = new Image();
+deferTest();
+img.onload = t.step_func_done(function ()
+{
+ ctx.drawImage(img, 0, 25);
+ // (The alpha values do not really survive float->int conversion, so just
+ // do approximate comparisons)
+ _assertPixel(canvas, 12,40, 1,3,254,255);
+ _assertPixelApprox(canvas, 37,40, 8,252,248,191, 2);
+ _assertPixelApprox(canvas, 62,40, 6,10,250,127, 4);
+ _assertPixelApprox(canvas, 87,40, 12,16,244,63, 8);
+});
+img.src = canvas.toDataURL();
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.png
new file mode 100644
index 0000000000..b5f9c118aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.html
new file mode 100644
index 0000000000..26c92a45a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.png</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.png</h1>
+<p class="desc">toDataURL with image/png returns a PNG</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with image/png returns a PNG");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL('image/png');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.html
new file mode 100644
index 0000000000..a13850d54e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.png.primarycolours</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.png.primarycolours</h1>
+<p class="desc">toDataURL with PNG handles simple colours correctly</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="toDataURL.png.primarycolours.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with PNG handles simple colours correctly");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = '#ff0';
+ctx.fillRect(0, 0, 25, 40);
+ctx.fillStyle = '#0ff';
+ctx.fillRect(25, 0, 50, 40);
+ctx.fillStyle = '#00f';
+ctx.fillRect(75, 0, 25, 40);
+ctx.fillStyle = '#fff';
+ctx.fillRect(0, 40, 100, 10);
+var data = canvas.toDataURL();
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+var img = new Image();
+deferTest();
+img.onload = t.step_func_done(function ()
+{
+ ctx.drawImage(img, 0, 0);
+ _assertPixel(canvas, 12,20, 255,255,0,255);
+ _assertPixel(canvas, 50,20, 0,255,255,255);
+ _assertPixel(canvas, 87,20, 0,0,255,255);
+ _assertPixel(canvas, 50,45, 255,255,255,255);
+});
+img.src = data;
+
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.png b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.png
new file mode 100644
index 0000000000..cfd1369007
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.unrecognised.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.unrecognised.html
new file mode 100644
index 0000000000..835a898027
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.unrecognised.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.unrecognised</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.unrecognised</h1>
+<p class="desc">toDataURL with an unhandled type returns a PNG</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL with an unhandled type returns a PNG");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL('image/example');
+assert_regexp_match(data, /^data:image\/png[;,]/);
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zeroheight.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zeroheight.html
new file mode 100644
index 0000000000..9b09beb6d6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zeroheight.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.zeroheight</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.zeroheight</h1>
+<p class="desc">toDataURL on zero-size canvas returns 'data:,'</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" height="0"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL on zero-size canvas returns 'data:,'");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL();
+_assertSame(data, 'data:,', "data", "'data:,'");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerosize.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerosize.html
new file mode 100644
index 0000000000..4468104268
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerosize.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.zerosize</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.zerosize</h1>
+<p class="desc">toDataURL on zero-size canvas returns 'data:,'</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="0" height="0"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL on zero-size canvas returns 'data:,'");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL();
+_assertSame(data, 'data:,', "data", "'data:,'");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerowidth.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerowidth.html
new file mode 100644
index 0000000000..9ab3524352
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/toDataURL.zerowidth.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: toDataURL.zerowidth</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>toDataURL.zerowidth</h1>
+<p class="desc">toDataURL on zero-size canvas returns 'data:,'</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="0"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("toDataURL on zero-size canvas returns 'data:,'");
+_addTest(function(canvas, ctx) {
+
+var data = canvas.toDataURL();
+_assertSame(data, 'data:,', "data", "'data:,'");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.delete.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.delete.html
new file mode 100644
index 0000000000..7fd54b30d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.delete.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: type.delete</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>type.delete</h1>
+<p class="desc">window.HTMLCanvasElement interface object is [[Configurable]]</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("window.HTMLCanvasElement interface object is [[Configurable]]");
+_addTest(function(canvas, ctx) {
+
+_assertSame(delete window.HTMLCanvasElement, true, "delete window.HTMLCanvasElement", "true");
+_assertSame(window.HTMLCanvasElement, undefined, "window.HTMLCanvasElement", "undefined");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.exists.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.exists.html
new file mode 100644
index 0000000000..26f59a1614
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.exists.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: type.exists</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>type.exists</h1>
+<p class="desc">HTMLCanvasElement is a property of window</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("HTMLCanvasElement is a property of window");
+_addTest(function(canvas, ctx) {
+
+_assert(window.HTMLCanvasElement, "window.HTMLCanvasElement");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.extend.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.extend.html
new file mode 100644
index 0000000000..e17209f455
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.extend.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: type.extend</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>type.extend</h1>
+<p class="desc">HTMLCanvasElement methods can be added, and the new methods used by canvases</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("HTMLCanvasElement methods can be added, and the new methods used by canvases");
+_addTest(function(canvas, ctx) {
+
+window.HTMLCanvasElement.prototype.getZero = function () { return 0; };
+_assertSame(canvas.getZero(), 0, "canvas.getZero()", "0");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.name.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.name.html
new file mode 100644
index 0000000000..fdf1d1d398
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.name.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: type.name</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>type.name</h1>
+<p class="desc">HTMLCanvasElement type and toString</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("HTMLCanvasElement type and toString");
+_addTest(function(canvas, ctx) {
+
+_assertSame(Object.prototype.toString.call(canvas), '[object HTMLCanvasElement]', "Object.prototype.toString.call(canvas)", "'[object HTMLCanvasElement]'");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.prototype.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.prototype.html
new file mode 100644
index 0000000000..f47f755388
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.prototype.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: type.prototype</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>type.prototype</h1>
+<p class="desc">window.HTMLCanvasElement has prototype, which is { ReadOnly, DontDelete }. prototype has getContext, which is not</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("window.HTMLCanvasElement has prototype, which is { ReadOnly, DontDelete }. prototype has getContext, which is not");
+_addTest(function(canvas, ctx) {
+
+_assert(window.HTMLCanvasElement.prototype, "window.HTMLCanvasElement.prototype");
+_assert(window.HTMLCanvasElement.prototype.getContext, "window.HTMLCanvasElement.prototype.getContext");
+window.HTMLCanvasElement.prototype = null;
+_assert(window.HTMLCanvasElement.prototype, "window.HTMLCanvasElement.prototype");
+delete window.HTMLCanvasElement.prototype;
+_assert(window.HTMLCanvasElement.prototype, "window.HTMLCanvasElement.prototype");
+window.HTMLCanvasElement.prototype.getContext = 1;
+_assertSame(window.HTMLCanvasElement.prototype.getContext, 1, "window.HTMLCanvasElement.prototype.getContext", "1");
+delete window.HTMLCanvasElement.prototype.getContext;
+_assertSame(window.HTMLCanvasElement.prototype.getContext, undefined, "window.HTMLCanvasElement.prototype.getContext", "undefined");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.replace.html b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.replace.html
new file mode 100644
index 0000000000..e67fe7c4a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-canvas-element/type.replace.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: type.replace</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>type.replace</h1>
+<p class="desc">HTMLCanvasElement methods can be replaced, and the replacement methods used by canvases</p>
+
+<p class="notes">Defined in "Web IDL" (draft)
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+
+<ul id="d"></ul>
+<script>
+var t = async_test("HTMLCanvasElement methods can be replaced, and the replacement methods used by canvases");
+_addTest(function(canvas, ctx) {
+
+window.HTMLCanvasElement.prototype.getContext = function (name) { return 0; };
+_assertSame(canvas.getContext('2d'), 0, "canvas.getContext('2d')", "0");
+
+
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/document-getters-return-null-for-cross-origin.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/document-getters-return-null-for-cross-origin.html
new file mode 100644
index 0000000000..89dc5d6d95
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/document-getters-return-null-for-cross-origin.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that getSVGDocument() returns null for a cross-origin document.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<embed src='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect height="100" width="100"/></svg>'></embed>
+<script>
+const embed = document.querySelector('embed');
+var t = async_test('HTMLEmbedElement.getSVGDocument() for cross-origin document');
+window.addEventListener(
+ 'load', t.step_func_done(() => { assert_equals(embed.getSVGDocument(), null); }));
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-change-src.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-change-src.html
new file mode 100644
index 0000000000..4152d51b0c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-change-src.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element">
+<link rel="help" href="http://crbug.com/1035330">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+promise_test(async () => {
+ const embed = document.createElement('embed');
+ let loadPromise = new Promise(resolve => embed.onload = resolve);
+ embed.src = '/media/white.mp4';
+ document.body.appendChild(embed);
+
+ await loadPromise;
+
+ loadPromise = new Promise(resolve => embed.onload = resolve);
+ embed.src = '/media/white.webm';
+
+ await loadPromise;
+}, 'Verifies that embed elements reload with new content when the src attribute is changed.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-dimension.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-dimension.html
new file mode 100644
index 0000000000..d65e39c750
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-dimension.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: dimension</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-embed-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<embed src="/images/blue.png" height="100" width="100" id="test">
+<script>
+ test(function () {
+ var height = getComputedStyle(document.getElementById("test"))["height"];
+ assert_equals(height, "100px", "The height of the embed element should be 100px.");
+ }, "Check the actual length of the embed element's height");
+
+ test(function () {
+ var width = getComputedStyle(document.getElementById("test"))["width"];
+ assert_equals(width, "100px", "The width of the embed element should be 100px.");
+ }, "Check the actual length of the embed element's width");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-focus.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-focus.html
new file mode 100644
index 0000000000..26a77918e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-focus.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="Ensure document finishes load when focus is attempted before the embed is finished loading">
+
+<style>
+.hidden { content-visibility: hidden; }
+</style>
+<body>
+ <script>
+ async_test(t => {
+ window.onload = () => t.done();
+ }, "ensure onload happens");
+ </script>
+ <div class=hidden>
+ <embed id=target src="embed-iframe.html">
+ </div>
+ <script>
+ // Ensure we process style in the hidden object, which normally delays
+ // load until the embed object is finished loading.
+ target.focus();
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-gbcr.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-gbcr.html
new file mode 100644
index 0000000000..074e63bf2b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-gbcr.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="Ensure document finishes load when getBoundingClientRect is attempted before the embed is finished loading">
+
+<style>
+.hidden { content-visibility: hidden; }
+</style>
+<body>
+ <script>
+ async_test(t => {
+ window.onload = () => t.done();
+ }, "ensure onload happens");
+ </script>
+ <div class=hidden>
+ <embed id=target src="embed-iframe.html">
+ </div>
+ <script>
+ // Ensure we process style and layout in the hidden object.
+ target.getBoundingClientRect();
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html
new file mode 100644
index 0000000000..3d44678cf1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="Check if the embed element represents a document when a text/html resource source is used">
+<body>
+ <script type="application/javascript">
+ window.childLoaded = false;
+ async_test(function() {
+ addEventListener("load", this.step_func_done(function() {
+ assert_true(window.childLoaded);
+ }));
+ }, "Test document type embedding");
+ </script>
+ <embed src="embed-iframe.html">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute-ref.html
new file mode 100644
index 0000000000..4b3d0feceb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>The hidden global presentation attribute within the embed element</title>
+
+This embed should be visible (green box):
+<embed src='../../../../images/green-256x256.png' type='image/png'></embed>
+
+These should not be visible (no red):
+
+<style>
+ embed {
+ display:block;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute.html
new file mode 100644
index 0000000000..18d3897df9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-hidden-attribute.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>The hidden global presentation attribute within the embed element</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=match href="embed-hidden-attribute-ref.html">
+
+This embed should be visible (green box):
+<embed src='../../../../images/green-256x256.png' type='image/png'></embed>
+
+These should not be visible (no red):
+<embed hidden src='../../../../images/fail.gif' type='image/png'></embed>
+<embed hidden="" src='../../../../images/fail.gif' type='image/png'></embed>
+<embed hidden="hidden" src='../../../../images/fail.gif' type='image/png'></embed>
+<embed hidden="true" src='../../../../images/fail.gif' type='image/png'></embed>
+<embed hidden="yes" src='../../../../images/fail.gif' type='image/png'></embed>
+
+<style>
+embed {
+ display:block;
+}
+</style>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-iframe.html
new file mode 100644
index 0000000000..f9b1bfdb57
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-iframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<body>
+ <script type="application/javascript">
+ parent.childLoaded = true;
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-ignored-in-media-element.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-ignored-in-media-element.html
new file mode 100644
index 0000000000..941b0c0b77
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-ignored-in-media-element.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="Check if the embed element is ignored when used inside a media element">
+<script type="application/javascript">
+ var nestingTest = async_test("Test embed being ignored inside media element");
+ onload = nestingTest.step_func_done(function() {
+ assert_true(true, "We got to a load event without loading things we should not load");
+ });
+</script>
+<body>
+ <video>
+ <embed type="text/html" src="../resources/should-not-load.html"
+ test-description="<embed> in <video>">
+ </video>
+ <audio>
+ <embed type="text/html" src="../resources/should-not-load.html"
+ test-description="<embed> in <audio>">
+ </audio>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-2.html
new file mode 100644
index 0000000000..d904a7e1a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-2.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title></title>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <script>
+ var loadedCount = 0;
+ var nestingTest = async_test("Test <embed> nesting inside <object>");
+ onload = nestingTest.step_func_done(function() {
+ assert_equals(loadedCount, 12, "Should have loaded all should-load elements");
+ });
+ </script>
+ <style>
+ object, embed { display: none }
+ </style>
+ </head>
+ <body>
+ <object data="../resources/should-load.html" style="width: 100px; height: 100px">
+ <embed type="text/html" src="../resources/should-not-load.html"
+ test-description="<embed> inside <object>">
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <embed type="text/html" src="../resources/should-load.html" />
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <div></div>
+ <embed type="text/html" src="../resources/should-load.html" />
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <div>
+ <embed type="text/html" src="../resources/should-load.html" />
+ </div>
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <embed type="text/html" src="../resources/should-load.html" />
+ <embed type="text/html" src="../resources/should-load.html" />
+ <object data="../resources/should-load.html">
+ <embed type="text/html" src="../resources/should-not-load.html"
+ test-description="<embed> inside loaded <object> inside non-loaded <object>">
+ </object>
+ <object data="data:application/x-does-not-exist,test">
+ <embed type="text/html" src="../resources/should-load.html" />
+ </object>
+ </object>
+ <div>
+ <object data="../resources/should-load.html" style="width: 100px; height: 100px"></object>
+ <embed type="text/html" src="../resources/should-load.html" />
+ </div>
+ <div>
+ <embed type="text/html" src="../resources/should-load.html" />
+ <object data="../resources/should-load.html" style="width: 100px; height: 100px"></object>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-subdocument.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-subdocument.html
new file mode 100644
index 0000000000..a2c2a93992
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback-subdocument.html
@@ -0,0 +1,4 @@
+<script>
+ var varName = location.search.substr(1);
+ parent[varName] = true;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback.html
new file mode 100644
index 0000000000..52fa01b91a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-in-object-fallback.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Ensure that embed elements inside object elements load when the objects
+ fall back but not otherwise</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var child1Loaded = false;
+ var child2Loaded = false;
+ var child3Loaded = false;
+ var parent3Loaded = false;
+</script>
+<object>
+ <embed src="embed-in-object-fallback-subdocument.html?child1Loaded">
+</object>
+<object>
+ <embed id="two" src="embed-in-object-fallback-subdocument.html?child2Loaded">
+ <!-- Something that forces the embed to be in the tree before the <object>
+ is done parsing -->
+ <script>
+ test(function() {
+ assert_equals(document.getElementById("two").localName,
+ "embed");
+ }, "We have the right embed element");
+ </script>
+</object>
+<object data="embed-in-object-fallback-subdocument.html?parent3Loaded">
+ <embed src="embed-in-object-fallback-subdocument.html?child3Loaded">
+</object>
+<script>
+ var t = async_test("Check that the right things loaded");
+ onload = t.step_func_done(function() {
+ assert_true(child1Loaded, "child 1 should load");
+ assert_true(child2Loaded, "child 2 should load");
+ assert_false(child3Loaded, "child 3 should not load");
+ assert_true(parent3Loaded, "parent 3 should load");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-named-attribute-detached-context-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-named-attribute-detached-context-crash.html
new file mode 100644
index 0000000000..7445b49f0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-named-attribute-detached-context-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<body>
+<iframe id="i"></iframe>
+<script>
+let e = i.contentDocument.createElement("embed");
+i.contentDocument.body.appendChild(e);
+i.remove();
+
+// These should not crash.
+e.tryToGetAnAttribute;
+e.tryToSetAnAttribute = "foo";
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-network-error.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-network-error.sub.html
new file mode 100644
index 0000000000..bae995c101
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-network-error.sub.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Network errors with embed elements</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const embed = document.createElement("embed");
+ embed.src = "//{{hosts[][nonexistent]}}/";
+ embed.onload = () => t.done();
+ embed.onerror = t.unreached_func("error event must not fire");
+ document.body.append(embed);
+}, "new embed: nonexistent host");
+
+async_test(t => {
+ const embed = document.createElement("embed");
+ embed.src = "../resources/not-embeddable.html";
+ embed.onload = () => t.done();
+ embed.onerror = t.unreached_func("error event must not fire");
+ document.body.append(embed);
+}, "new embed: X-Frame-Options prevents embedding");
+
+async_test(t => {
+ const embed = document.createElement("embed");
+ embed.src = "/common/blank.html";
+ embed.name = "existingEmbed1";
+ embed.onload = t.step_func(() => {
+ embed.onload = () => t.done();
+ embed.onerror = t.unreached_func("error event must not fire 2");
+
+ frames.existingEmbed1.location.href = "//{{hosts[][nonexistent]}}/";
+ });
+ embed.onerror = t.unreached_func("error event must not fire 1");
+ document.body.append(embed);
+}, "navigating an existing embed: nonexistent host");
+
+async_test(t => {
+ const embed = document.createElement("embed");
+ embed.src = "/common/blank.html";
+ embed.name = "existingEmbed2";
+ embed.onload = t.step_func(() => {
+ embed.onload = () => t.done();
+ embed.onerror = t.unreached_func("error event must not fire 2");
+
+ frames.existingEmbed2.location.href = "../resources/not-embeddable.html";
+ });
+ embed.onerror = t.unreached_func("error event must not fire 1");
+ document.body.append(embed);
+}, "navigating an existing embed: X-Frame-Options prevents embedding");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-01.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-01.html
new file mode 100644
index 0000000000..e66bd4a906
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-01.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element without src and type attributes represents nothing</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-embed-element">
+<link rel="match" href="embed-represent-nothing-ref.html">
+<meta name="assert" content="Check if the embed element without src and type attributes represents nothing">
+<style>
+ embed {
+ background-color: red;
+ height: 100px;
+ width: 100px;
+ }
+</style>
+<body>
+ <p>Test passes if there is <strong>no red</strong>.</p>
+ <embed>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-02.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-02.html
new file mode 100644
index 0000000000..65cd672387
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-02.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents nothing when its type and src attributs are removed</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-embed-element">
+<link rel="match" href="embed-represent-nothing-ref.html">
+<meta name="assert" content="Check if the embed element represents nothing when its src and type attributes are removed">
+<style>
+ embed {
+ background-color: red;
+ height: 100px;
+ width: 100px;
+ }
+</style>
+<body>
+ <p>Test passes if there is <strong>no red</strong>.</p>
+ <embed id="embed" src="/images/red-16x16.png" type="image/png">
+ <script>
+ document.getElementById("embed").removeAttribute("src");
+ document.getElementById("embed").removeAttribute("type");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-03.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-03.html
new file mode 100644
index 0000000000..a16f3794ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-03.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents nothing when it has a media ancestor</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-embed-element">
+<link rel="match" href="embed-represent-nothing-ref.html">
+<meta name="assert" content="Check if the embed element represents nothing when it has a media ancestor">
+<style>
+ embed {
+ background-color: red;
+ height: 100px;
+ width: 100px;
+ }
+</style>
+<body>
+ <p>Test passes if there is <strong>no red</strong>.</p>
+ <video>
+ <embed src="/images/red-16x16.png">
+ </video>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-04.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-04.html
new file mode 100644
index 0000000000..7cc1b668a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-04.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents nothing when it has an object ancestor</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-embed-element">
+<link rel="match" href="embed-represent-nothing-ref.html">
+<meta name="assert" content="Check if the embed element represents nothing when it has a object ancestor that is not showing its fallback content">
+<style>
+ embed {
+ background-color: red;
+ height: 100px;
+ width: 100px;
+ }
+</style>
+<body>
+ <p>Test passes if there is <strong>no red</strong>.</p>
+ <object type="application/x-shockwave-flash">
+ <embed src="/images/red-16x16.png">
+ </object>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-ref.html
new file mode 100644
index 0000000000..91d680debf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Embed Reftest Reference</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<body>
+ <p>Test passes if there is <strong>no red</strong>.</p>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-svg-navigation-resets-size.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-svg-navigation-resets-size.html
new file mode 100644
index 0000000000..237c9c3646
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-svg-navigation-resets-size.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Intrinsic sizing of SVG embedded via &lt;embed&gt; should disappear on navigation</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<embed id="element" name="embed" src='data:image/svg+xml,
+ <svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ viewBox="0 0 1000 1000"
+ width="400"
+ height="400"
+ font-size="100">
+ </svg>'></embed>
+<script>
+promise_test(async () => {
+ await new Promise(resolve => {
+ onload = resolve;
+ });
+
+ assert_equals(element.scrollWidth, 400, "The width should be determined by the embedded SVG");
+ assert_equals(element.scrollHeight, 400, "The height should be determined by the embedded SVG");
+
+ await new Promise(resolve => {
+ element.onload = resolve;
+ element.src = "about:blank";
+ });
+
+ assert_equals(element.scrollWidth, 300, "The width should be the default");
+ assert_equals(element.scrollHeight, 150, "The height should be the default");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/historical.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/historical.html
new file mode 100644
index 0000000000..9df7280bb6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/historical.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>Historical embed element features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<embed id=embed>
+<script>
+test(function() {
+ var elm = document.getElementById('embed');
+ assert_equals(typeof elm, 'object', 'typeof');
+ assert_throws_js(TypeError, function() {
+ elm();
+ });
+}, 'embed legacycaller should not be supported');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-frame-element/document-getters-return-null-for-cross-origin.html b/testing/web-platform/tests/html/semantics/embedded-content/the-frame-element/document-getters-return-null-for-cross-origin.html
new file mode 100644
index 0000000000..2628e91009
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-frame-element/document-getters-return-null-for-cross-origin.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that contentDocument returns null for a cross-origin document.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test('HTMLFrameElement.contentDocument for cross-origin document');
+window.addEventListener(
+ 'load', t.step_func_done(() => { assert_equals(document.querySelector('frame').contentDocument, null); }));
+</script>
+<frameset>
+<frame src='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect height="100" width="100"/></svg>'></frame>
+</frameset>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_child.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_child.html
new file mode 100644
index 0000000000..738ceee529
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_child.html
@@ -0,0 +1,14 @@
+<body>
+ Child.
+ <iframe id="grandchild" src="change_grandchild.html"></iframe>
+</body>
+<script>
+ var timer = window.setInterval(poll, 100);
+ function poll() {
+ if (document.body.getAttribute("data-contains-grandchild")) {
+ var grandchild = document.getElementById("grandchild");
+ window.frameElement.parentNode.appendChild(grandchild);
+ window.clearTimeout(timer);
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_grandchild.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_grandchild.html
new file mode 100644
index 0000000000..885622c2b4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_grandchild.html
@@ -0,0 +1,4 @@
+<body>Grandchild.</body>
+<script>
+ window.frameElement.parentNode.setAttribute("data-contains-grandchild", true);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_parentage.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_parentage.html
new file mode 100644
index 0000000000..1d62ccc480
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/change_parentage.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Change the frame heriarchy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <iframe src="change_child.html"></iframe>
+</body>
+<script>
+ async_test(function(t) {
+ var timer = window.setInterval(t.step_func(poll), 100);
+ function poll() {
+ // We wait for the grandchild's script to set the custom attribtue.
+ // Note that if this test passes, the grandchild's script must have been run twice,
+ // once to trigger the move from the child to here, and once to pass this test.
+ if (document.body.getAttribute("data-contains-grandchild")) {
+ window.clearTimeout(timer);
+ t.done();
+ }
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/content_document_changes_only_after_load_matures.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/content_document_changes_only_after_load_matures.html
new file mode 100644
index 0000000000..b657f26158
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/content_document_changes_only_after_load_matures.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Iframe's contentDocument should only change after its pending load has matured.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body></body>
+<script>
+async_test(function(t) {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ var checkedDuringParse = false;
+ iframe.onload = t.step_func_done(function() {
+ testContentDocument();
+ assert_true(checkedDuringParse);
+ });
+
+ let url = "support/iframe-that-checks-contentDocument.html";
+ window.testContentDocument = t.step_func(function() {
+ assert_true(iframe.contentDocument.location.toString().includes(url));
+ checkedDuringParse = true;
+ });
+
+ assert_equals(iframe.contentDocument.location.toString(), "about:blank");
+ iframe.src = url + "?pipe=trickle(d2)";
+ // The location of the contentDocument should not change until the new document has matured.
+ assert_equals(iframe.contentDocument.location.toString(), "about:blank");
+}, "contentDocument should only change after a load matures.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom-part-2.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom-part-2.window.js
new file mode 100644
index 0000000000..c88158d267
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom-part-2.window.js
@@ -0,0 +1,59 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ frame.src = "support/document-with-embedded-svg.html";
+ const elements = {
+ "embed": ["getSVGDocument"],
+ "frame": ["contentDocument"],
+ "iframe": ["getSVGDocument", "contentDocument"],
+ "object": ["getSVGDocument", "contentDocument"]
+ };
+ function element_to_document(element, api) {
+ return api === "getSVGDocument" ? element[api]() : element[api];
+ }
+ function assert_apis(instance, assertNull = false) {
+ const name = instance.localName;
+ let priorPossibleDocument = null;
+ elements[name].forEach(api => {
+ const assertReason = `${name}.${api}`;
+ const possibleDocument = element_to_document(instance, api);
+ if (assertNull) {
+ assert_equals(possibleDocument, null, assertReason);
+ return;
+ } else {
+ assert_not_equals(possibleDocument, null, assertReason);
+
+ // This needs standardizing still
+ // assert_class_string(possibleDocument, "XMLDocument");
+ }
+
+ // Ensure getSVGDocument() and contentDocument if both available return the same
+ if (priorPossibleDocument === null) {
+ priorPossibleDocument = possibleDocument;
+ } else {
+ assert_equals(priorPossibleDocument, possibleDocument);
+ }
+ });
+ }
+ frame.onload = t.step_func_done(() => {
+ const instances = Object.keys(elements).map(element => frame.contentDocument.querySelector(element));
+ // Everything is same origin and same origin-domain, no sweat
+ instances.forEach(instance => assert_apis(instance));
+ // Make the SVG cross origin-domain (its container and the current settings object are not
+ // affected)
+ instances.forEach(instance => {
+ const svgDocument = element_to_document(instance, elements[instance.localName][0]);
+ svgDocument.domain = svgDocument.domain;
+ });
+ instances.forEach(instance => assert_apis(instance, true));
+ const svgContainer = frame.contentDocument;
+ // Make the current settings object same origin-domain with the SVG and cross origin-domain with
+ // SVG's container (SVG's container is not affected)
+ document.domain = document.domain;
+ assert_equals(frame.contentDocument, null);
+ instances.forEach(instance => assert_apis(instance, true));
+ // Make everything same origin-domain once more
+ svgContainer.domain = svgContainer.domain;
+ instances.forEach(instance => assert_apis(instance));
+ });
+ document.body.appendChild(frame);
+}, "Test embed/frame/iframe/object nested document APIs for same origin-domain and cross origin-domain embedder document");
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom.window.js
new file mode 100644
index 0000000000..a1e592d8f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom.window.js
@@ -0,0 +1,37 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ frame.src = "support/document-with-embedded-svg.html";
+ const elements = {
+ "embed": ["getSVGDocument"],
+ "frame": ["contentDocument"],
+ "iframe": ["getSVGDocument", "contentDocument"],
+ "object": ["getSVGDocument", "contentDocument"]
+ };
+ function assert_apis(instance) {
+ const name = instance.localName;
+ let priorPossibleDocument = null;
+ elements[name].forEach(api => {
+ const possibleDocument = api == "getSVGDocument" ? instance[api]() : instance[api];
+ assert_not_equals(possibleDocument, null, `${name}.${api}`);
+ // This needs standardizing still
+ // assert_class_string(possibleDocument, "XMLDocument");
+
+ // Ensure getSVGDocument() and contentDocument if both available return the same
+ if (priorPossibleDocument === null) {
+ priorPossibleDocument = possibleDocument;
+ } else {
+ assert_equals(priorPossibleDocument, possibleDocument);
+ }
+ });
+ }
+ frame.onload = t.step_func_done(() => {
+ const instances = Object.keys(elements).map(element => frame.contentDocument.querySelector(element));
+ // Everything is same origin and same origin-domain, no sweat
+ instances.forEach(instance => assert_apis(instance));
+ // Make the current settings object cross origin-domain (SVG and its container are not affected)
+ document.domain = document.domain;
+ assert_equals(frame.contentDocument, null);
+ instances.forEach(instance => assert_apis(instance));
+ });
+ document.body.appendChild(frame);
+}, "Test embed/frame/iframe/object nested document APIs for same origin-domain and cross origin-domain current settings object");
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_child.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_child.html
new file mode 100644
index 0000000000..8b44fe805f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_child.html
@@ -0,0 +1,12 @@
+<script src="iframe_harness.js"></script>
+<body>
+ <iframe src="cross_origin_grandchild.html"></iframe>
+</body>
+<script>
+ send_test_results({
+ "id": '79a52de8-4222-427e-92db-caec28e75f8e',
+ "parent": window.parent !== window,
+ "grandparent": window.parent.parent === window.parent,
+ "top": window.top === window.parent,
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_grandchild.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_grandchild.html
new file mode 100644
index 0000000000..1eff64af10
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_grandchild.html
@@ -0,0 +1,11 @@
+<script src="iframe_harness.js"></script>
+<body>
+</body>
+<script>
+ send_test_results({
+ "id": '6c8da65d-2c5e-44ef-bb0b-b8b9849aab19',
+ "parent": window.parent !== window,
+ "grandparent": window.parent.parent !== window.parent,
+ "top": window.top === window.parent.parent,
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.sub.html
new file mode 100644
index 0000000000..128892d7ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.sub.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check the frame heriarchy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="iframe_harness.js"></script>
+<body>
+ <iframe src="http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/embedded-content/the-iframe-element/cross_origin_child.html"></iframe>
+</body>
+<script>
+ get_test_results('bffa23ee-b45a-4e9a-9405-87ab437d5cfa');
+ get_test_results('79a52de8-4222-427e-92db-caec28e75f8e');
+ get_test_results('6c8da65d-2c5e-44ef-bb0b-b8b9849aab19');
+ send_test_results({
+ "id": 'bffa23ee-b45a-4e9a-9405-87ab437d5cfa',
+ "parent": window.parent === window,
+ "top": window.top === window,
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/document-getters-return-null-for-cross-origin.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/document-getters-return-null-for-cross-origin.html
new file mode 100644
index 0000000000..e3dc0b0e4e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/document-getters-return-null-for-cross-origin.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that contentDocument/getSVGDocument() return null for a cross-origin document.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<iframe src='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect height="100" width="100"/></svg>'></iframe>
+<script>
+const iframe = document.querySelector('iframe');
+var t1 = async_test('HTMLIFrameElement.contentDocument for cross-origin document');
+window.addEventListener(
+ 'load', t1.step_func_done(() => { assert_equals(iframe.contentDocument, null); }));
+var t2 = async_test('HTMLIFrameElement.getSVGDocument() for cross-origin document');
+window.addEventListener(
+ 'load', t2.step_func_done(() => { assert_equals(iframe.getSVGDocument(), null); }));
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/historical.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/historical.html
new file mode 100644
index 0000000000..0bc8b857b4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/historical.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Historical iframe element features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function t(property) {
+ test(function() {
+ assert_false(property in document.createElement('iframe'));
+ }, 'iframe.' + property + ' should not be supported');
+}
+
+// added in https://github.com/whatwg/html/commit/f6490f17f577fa3478791b29ad8c2b586418001f
+// removed in https://github.com/whatwg/html/commit/1490eba4dba5ab476f0981443a86c01acae01311
+t('seamless');
+
+// Added by https://github.com/whatwg/html/pull/2133
+// Removed by https://github.com/whatwg/html/pull/5915
+t('allowPaymentRequest');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/hittest-detached-iframe-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/hittest-detached-iframe-crash.html
new file mode 100644
index 0000000000..70fe8c1126
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/hittest-detached-iframe-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+function test() {
+ document.body.offsetHeight;
+ document.body.attachShadow({mode: 'closed'});
+ iframe.contentDocument.caretRangeFromPoint(0, 0);
+}
+</script>
+<iframe id="iframe" onload="test()"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allow.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allow.html
new file mode 100644
index 0000000000..9e67314923
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allow.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check processing of allow attribute in nested browsing context</title>
+<link rel="author" title="Google" href="https://www.google.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-allow">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#initialise-the-document-object">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#fullscreen-enabled-flag">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+<script>
+ // This returns a data URL (cross-origin with the containing document) which
+ // advances a counter, and reports the counter value together with the
+ // document's fullscreenEnabled state, every time it receives a postMessage.
+ // Fullscreen itself is not important for this test, but the flag is a useful
+ // indicator of whether a policy-controlled-feature is allowed or denied.
+ function getSourceForCrossOriginPage(initial_count) {
+ var page_contents = "<html><body><script>var count="+initial_count+";window.addEventListener('message',function(){parent.postMessage({'count':count++,'fullscreenEnabled':document.fullscreenEnabled},'*');});</scr"+"ipt></body></html>";
+ return "data:text/html;base64,"+btoa(page_contents);
+ }
+
+ async_test(function(t) {
+ var iframe = document.createElement("iframe");
+ iframe.src = getSourceForCrossOriginPage(0);
+
+ iframe.addEventListener('load', function() {
+ // Request the fullscreenEnabled state whenever the frame loads
+ iframe.contentWindow.postMessage(true,"*");
+ });
+
+ window.addEventListener('message', this.step_func(function(msg) {
+ if (msg.data.count == 0) {
+ assert_false(msg.data.fullscreenEnabled, "Document inside cross-origin iframe without allow attribute should not have feature enabled");
+ iframe.setAttribute("allow", "fullscreen");
+ iframe.contentWindow.postMessage(true,"*"); // Request state again
+ } else if (msg.data.count == 1) {
+ assert_false(msg.data.fullscreenEnabled, "Feature should be denied when correct allow attribute is added, before reload");
+ iframe.src = getSourceForCrossOriginPage(2); // Reload the frame
+ } else if (msg.data.count == 2) {
+ assert_true(msg.data.fullscreenEnabled, "Feature should be allowed when correct allow attribute is added, after reload");
+ iframe.removeAttribute("allow");
+ iframe.contentWindow.postMessage(true,"*"); // Request state again
+ } else if (msg.data.count == 3) {
+ assert_true(msg.data.fullscreenEnabled, "Feature should be allowed when allow attribute is removed, before reload");
+ iframe.src = getSourceForCrossOriginPage(4); // Reload the frame
+ } else if (msg.data.count == 4) {
+ assert_false(msg.data.fullscreenEnabled, "Feature should be denied when allow attribute is removed, after reload");
+ iframe.setAttribute("allow", "payment"); // Set allow to an unrelated feature
+ iframe.src = getSourceForCrossOriginPage(5); // Reload the frame
+ } else if (msg.data.count == 5) {
+ assert_false(msg.data.fullscreenEnabled, "Feature should be denied with incorrect allow attribute");
+ iframe.setAttribute("allow", "payment;fullscreen"); // Include fullscreen again
+ iframe.src = getSourceForCrossOriginPage(6); // Reload the frame
+ } else if (msg.data.count == 6) {
+ assert_true(msg.data.fullscreenEnabled, "Feature should be allowed with complex allow attribute");
+ t.done();
+ } else {
+ assert_unreached();
+ }
+ }));
+
+ document.body.appendChild(iframe);
+ }, "iframe-cross-origin-allow");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html
new file mode 100644
index 0000000000..9fc285bf3e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html
@@ -0,0 +1,95 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check how allowfullscreen affects fullscreen enabled flag</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#initialise-the-document-object">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#fullscreen-enabled-flag">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+<script>
+ // This returns a data URL (cross-origin with the containing document) which
+ // advances a counter, and reports the counter value together with the
+ // document's fullscreenEnabled state, every time it receives a postMessage.
+ function getSourceForCrossOriginPage(initial_count) {
+ var page_contents = "<html><body><script>var count="+initial_count+";window.addEventListener('message',function(){parent.postMessage({'count':count++,'fullscreenEnabled':document.fullscreenEnabled},'*');});</scr"+"ipt></body></html>";
+ return "data:text/html;base64,"+btoa(page_contents);
+ }
+
+ async_test(function(t) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "support/blank.htm";
+ var eventWatcher = new EventWatcher(t, iframe, "load");
+ document.body.appendChild(iframe);
+ t.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+
+ assert_true(document.fullscreenEnabled, "Top level document has fullscreen enabled flag set");
+ eventWatcher.wait_for("load").then(t.step_func_done(function() {
+ assert_true(iframe.contentDocument.fullscreenEnabled, "Document inside same-origin iframe without allowfullscreen attribute should still have fullscreen enabled flag set");
+ }));
+ }, "iframe-same-origin-allowfullscreen");
+
+ async_test(function(t) {
+ var iframe = document.createElement("iframe");
+ iframe.src = getSourceForCrossOriginPage(0);
+
+ iframe.addEventListener('load', function() {
+ // Request the fullscreenEnabled state whenever the frame loads
+ iframe.contentWindow.postMessage(true,"*");
+ });
+
+ window.addEventListener('message', this.step_func(function(msg) {
+ if (msg.data.count == 0) {
+ assert_false(msg.data.fullscreenEnabled, "Document inside cross-origin iframe without allowfullscreen attribute should not have fullscreen enabled flag set");
+ iframe.setAttribute("allowfullscreen", "");
+ iframe.contentWindow.postMessage(true,"*"); // Request state again
+ } else if (msg.data.count == 1) {
+ assert_false(msg.data.fullscreenEnabled, "Fullscreen should be denied when allowfullscreen attribute is added, before reload");
+ iframe.src = getSourceForCrossOriginPage(2); // Reload the frame
+ } else if (msg.data.count == 2) {
+ assert_true(msg.data.fullscreenEnabled, "Fullscreen should be allowed when allowfullscreen attribute is added, after reload");
+ iframe.removeAttribute("allowfullscreen");
+ iframe.contentWindow.postMessage(true,"*"); // Request state again
+ } else if (msg.data.count == 3) {
+ assert_true(msg.data.fullscreenEnabled, "Fullscreen should be allowed when allowfullscreen attribute is removed, before reload");
+ iframe.src = getSourceForCrossOriginPage(4); // Reload the frame
+ } else if (msg.data.count == 4) {
+ assert_false(msg.data.fullscreenEnabled, "Fullscreen should be denied when allowfullscreen attribute is removed, after reload");
+ t.done();
+ }
+ }));
+
+ document.body.appendChild(iframe);
+ t.add_cleanup(function() {
+ document.body.removeChild(iframe);
+ });
+ }, "iframe-cross-origin-allowfullscreen");
+
+ /* Fullscreen enabled flag with about:blank */
+
+ function test_allowfullscreen_noload(setup_iframe, check) {
+ var iframe = document.createElement("iframe");
+ setup_iframe(iframe);
+ document.body.appendChild(iframe);
+ check(iframe.contentDocument);
+ document.body.removeChild(iframe);
+ }
+
+ test(function() {
+ test_allowfullscreen_noload(function() {}, function(doc) {
+ assert_true(doc.fullscreenEnabled, "Fullscreen should still be enabled in same-origin document without allowfullscreen attribute");
+ });
+ }, "iframe-noload-noallowfullscreen");
+
+ test(function() {
+ test_allowfullscreen_noload(function(iframe) {
+ iframe.setAttribute("allowfullscreen", true);
+ }, function(doc) {
+ assert_true(doc.fullscreenEnabled, "Fullscreen should be enabled with allowfullscreen attribute");
+ });
+ }, "iframe-noload-allowfullscreen");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-append-to-child-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-append-to-child-document.html
new file mode 100644
index 0000000000..ac8bd5e053
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-append-to-child-document.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Append iframe element to its own child document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id=x></iframe>
+<script>
+test(function() {
+ var iframe = document.getElementById('x');
+ var childWindow = iframe.contentWindow;
+ assert_equals(childWindow.parent, window);
+ childWindow.document.body.appendChild(iframe);
+ assert_equals(childWindow.parent, null);
+ assert_equals(iframe.contentWindow, null);
+ assert_equals(childWindow.document.body.firstChild, iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-display-none-with-object.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-display-none-with-object.html
new file mode 100644
index 0000000000..ee73953217
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-display-none-with-object.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that iframe with object triggers load event in owner document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+ async_test(t => {
+ window.onload = t.step_func_done();
+ const obj = document.createElement("iframe");
+ obj.style.display = "none";
+ obj.src = "support/iframe-with-object.html";
+ document.body.appendChild(obj);
+ }, "Load event triggered on window");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-document-move-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-document-move-crash.html
new file mode 100644
index 0000000000..43da8bf50c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-document-move-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+This test passes if it does not crash.
+<script>
+ var doc1 = document.documentElement;
+ let iframe1 = document.createElement("iframe");
+ doc1.appendChild(iframe1);
+ separateDoc = document.implementation.createDocument("", null);
+ iframe1.addEventListener("DOMFocusOut", function () { separateDoc.adoptNode(iframe1); });
+ iframe1.focus();
+ iframe1 = document.createElement("iframe");
+ doc1.appendChild(iframe1);
+ iframe1.focus();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-first-load-canceled-second-load-blank.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-first-load-canceled-second-load-blank.html
new file mode 100644
index 0000000000..bed3b6477f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-first-load-canceled-second-load-blank.html
@@ -0,0 +1,38 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+// Check about:blank navigation is asynchronous, even if the initial navigation
+// is canceled.
+promise_test(async test => {
+ // Wait for document.body to exist, before appending the <iframe>.
+ await new Promise(resolve => window.onload = resolve);
+
+ // The initial navigation in this new iframe will result in a 204 No Content.
+ // As a result the navigation will be canceled. The <iframe> will stay on the
+ // initial empty document.
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html?pipe=status(204)"
+ document.body.appendChild(iframe);
+
+ // The navigation in the iframe will be canceled. There are no good ways to
+ // detect when it will happen. Anyway, any additional navigation must be
+ // asynchronous. To test what happens when there are no pending navigation,
+ // waiting one second should be enough, most of the time.
+ await new Promise(resolve => test.step_timeout(resolve, 1000));
+
+ let load_event_fired = false;
+ const load_event_promise = new Promise(resolve => {
+ iframe.onload = () => {
+ load_event_fired = true;
+ resolve();
+ };
+ });
+ iframe.src = "about:blank";
+ assert_equals(load_event_fired, false,
+ "about:blank must not commit synchronously");
+ await load_event_promise;
+}, "about:blank navigation is asynchronous, even if the initial one is " +
+ "canceled.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated-ref.html
new file mode 100644
index 0000000000..0484c397cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <iframe src="resources/hello-world.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated.html
new file mode 100644
index 0000000000..818ec0362e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-initially-empty-is-updated.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Iframe that doesn't load can be updated and rendered.</title>
+<meta charset="utf-8">
+<link rel="match" href="iframe-initially-empty-is-updated-ref.html"/>
+<html>
+ <body>
+ <iframe src="resources/empty.html"></iframe>
+ <script>
+ window[0].document.body.appendChild(document.createElement('div'))
+ .appendChild(document.createTextNode('Hello world!'));
+ window[0].document.body.firstChild.style = 'color: green';
+ window.stop();
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-load-event.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-load-event.html
new file mode 100644
index 0000000000..5ffe53c308
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-load-event.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test some sanity behavior around iframe load/error events</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<script>
+async_test(function(t) {
+ var obj = document.createElement("iframe");
+ obj.onload = t.step_func_done(function(e){
+ assert_not_equals(obj.contentWindow, null, "The iframe element should represent a nested browsing context.")
+ assert_equals(Object.getPrototypeOf(e).constructor, Event, "The load event should use the Event interface.");
+ assert_true(e.isTrusted, "The load event should be a trusted event.");
+ assert_false(e.cancelable, "The load event should not be a cancelable event.");
+ assert_false(e.bubbles, "The load event should not be a bubble event.");
+ assert_equals(e.target, obj, "The load event target should be the corresponding object element.");
+ });
+
+ obj.onerror = t.unreached_func("The error event should not be fired.");
+
+ var url = URL.createObjectURL(new Blob([""], { type: "text/html" }));
+
+ obj.src = url;
+ document.body.appendChild(obj);
+}, "load event of blob URL");
+
+async_test(function(t) {
+ var obj = document.createElement("iframe");
+ obj.onload = t.step_func_done(function(e){
+ assert_not_equals(obj.contentWindow, null, "The object element should represent a nested browsing context.")
+ assert_equals(Object.getPrototypeOf(e).constructor, Event, "The load event should use the Event interface.");
+ assert_true(e.isTrusted, "The load event should be a trusted event.");
+ assert_false(e.cancelable, "The load event should not be a cancelable event.");
+ assert_false(e.bubbles, "The load event should not be a bubble event.");
+ assert_equals(e.target, obj, "The load event target should be the corresponding object element.");
+ });
+
+ obj.onerror = t.unreached_func("The error event should not be fired.");
+
+ document.body.appendChild(obj);
+}, "load event of initial about:blank");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-eager.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-eager.html
new file mode 100644
index 0000000000..8acd99d23d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-eager.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<head>
+ <title>Iframes with loading='eager' load immediately regardless of their
+ position with respect to the viewport.</title>
+ <link rel="author" title="Scott Little" href="mailto:sclittle@chromium.org">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test("Test that iframes with loading='eager' load " +
+ "immediately regardless of their position with " +
+ "respect to the viewport.");
+
+ let has_in_viewport_loaded = false;
+ const in_viewport_iframe_onload = t.step_func(() => {
+ assert_false(has_in_viewport_loaded,
+ "The in_viewport element should load only once.");
+ has_in_viewport_loaded = true;
+ });
+
+ let has_below_viewport_loaded = false;
+ const below_viewport_iframe_onload = t.step_func(() => {
+ assert_false(has_below_viewport_loaded,
+ "The below_viewport element should load only once.");
+ has_below_viewport_loaded = true;
+ });
+
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_true(has_in_viewport_loaded,
+ "The in_viewport element should have loaded before " +
+ "window.load().");
+ assert_true(has_below_viewport_loaded,
+ "The below_viewport element should have loaded before " +
+ "window.load().");
+ }));
+</script>
+
+<body>
+ <iframe id="in_viewport" src="resources/subframe.html?in-viewport"
+ loading="eager" width="200px" height="100px"
+ onload="in_viewport_iframe_onload();"></iframe>
+ <div style="height:1000vh;"></div>
+
+ <!-- The below_viewport element loads very slowly in order to ensure that the
+ window load event is blocked on it. -->
+ <iframe id="below_viewport"
+ src="resources/subframe.html?below-viewport&pipe=trickle(d1)"
+ loading="eager" width="200px" height="100px"
+ onload="below_viewport_iframe_onload();"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-2.html
new file mode 100644
index 0000000000..8782f3d315
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-2.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred loading=lazy iframes load relative to the document's base URL
+ at parse-time</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="author" title="Raj T" href="mailto:rajendrant@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<script>
+ const below_viewport_iframe = new ElementLoadPromise("below-viewport");
+
+ let has_window_loaded = false;
+
+ async_test(t => {
+ // Change the document's base URL to a bogus one, and scroll the
+ // below-viewport iframe into view. When it loads, it should load relative
+ // to the old base URL computed at parse-time.
+ window.addEventListener("load", t.step_func(() => {
+ window.history.pushState(2, document.title,
+ '/invalid-url-where-no-subresources-exist/')
+ has_window_loaded = true;
+ below_viewport_iframe.element().scrollIntoView();
+ }));
+
+ below_viewport_iframe.promise.then(t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "Below-viewport loading=lazy iframes do not block the " +
+ "window load event");
+ assert_true(below_viewport_iframe.element()
+ .contentDocument.body.innerHTML.includes("<p>Subframe</p>"));
+ }));
+
+ }, "When a loading=lazy iframe is loaded, it loads relative to the " +
+ "document's base URL computed at parse-time.");
+</script>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <script>
+ // Change the document's base URL so that the iframe request parses relative
+ // to it when it sets up the request at parse-time.
+ window.history.pushState(1, document.title, 'resources/')
+ </script>
+ <iframe id="below-viewport" src="subframe.html" loading="lazy" width="200px"
+ height="100px" onload="below_viewport_iframe.resolve()"</iframe>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-3.html
new file mode 100644
index 0000000000..611221d801
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url-3.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred iframes with loading='lazy' changed to eager later
+ use the document's base URL computed at parse-time</title>
+ <link rel="author" title="Oliver Medhurst" href="mailto:omedhurst@mozilla.com">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+ <base href='/html/semantics/embedded-content/the-iframe-element/resources/'>
+</head>
+
+<script>
+ const below_viewport_iframe = new ElementLoadPromise("below-viewport");
+
+ let has_window_loaded = false;
+
+ async_test(t => {
+ // Change the base URL and scroll down to load the deferred iframe.
+ window.addEventListener("load", t.step_func(() => {
+ const base = document.querySelector('base');
+ base.href = '/invalid-url-where-no-subresources-exist/';
+ has_window_loaded = true;
+ below_viewport_iframe.element().loading = 'eager';
+ }));
+
+ below_viewport_iframe.promise.then(
+ t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "Below-viewport loading=lazy iframes do not block the " +
+ "window load event");
+ assert_true(below_viewport_iframe.element().contentDocument.body.
+ innerHTML.includes("<p>Subframe</p>"),
+ "The loading=lazy iframe's content is accessible upon loading");
+ }));
+
+ }, "When a loading=lazy iframe is changed to eager later before loading, it loads relative to the " +
+ "document's base URL computed at parse-time.");
+</script>
+
+<body>
+ <div style="height:1000vh"></div>
+ <iframe id="below-viewport" src="subframe.html" loading="lazy"
+ width="200px" height="100px" onload="below_viewport_iframe.resolve()">
+ </iframe>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url.html
new file mode 100644
index 0000000000..ec9f73400d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-base-url.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred iframes with loading='lazy' use the original
+ base URL specified at parse-time</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+ <base href='/html/semantics/embedded-content/the-iframe-element/resources/'>
+</head>
+
+<script>
+ const below_viewport_iframe = new ElementLoadPromise("below-viewport");
+
+ let has_window_loaded = false;
+
+ async_test(t => {
+ // Change the base URL and scroll down to load the deferred iframe.
+ window.addEventListener("load", t.step_func(() => {
+ const base = document.querySelector('base');
+ base.href = '/invalid-url-where-no-subresources-exist/';
+ has_window_loaded = true;
+ below_viewport_iframe.element().scrollIntoView();
+ }));
+
+ below_viewport_iframe.promise.then(
+ t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "Below-viewport loading=lazy iframes do not block the " +
+ "window load event");
+ assert_true(below_viewport_iframe.element().contentDocument.body.
+ innerHTML.includes("<p>Subframe</p>"),
+ "The loading=lazy iframe's content is accessible upon loading");
+ }));
+
+ }, "When a loading=lazy iframe is loaded, it loads relative to the " +
+ "document's base URL computed at parse-time.");
+</script>
+
+<body>
+ <div style="height:1000vh"></div>
+ <iframe id="below-viewport" src="subframe.html" loading="lazy"
+ width="200px" height="100px" onload="below_viewport_iframe.resolve()">
+ </iframe>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-script-disabled-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-script-disabled-iframe.html
new file mode 100644
index 0000000000..4f191cd784
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-script-disabled-iframe.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<head>
+<title>Iframes with loading='lazy' in script disabled iframe are not handled
+ as 'lazy'</title>
+<link rel="help" href="https://github.com/scott-little/lazyload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+
+<div style="height:1000vh;"></div>
+<iframe id="iframe" sandbox="allow-same-origin"
+ src="resources/iframe-loading-lazy-in-viewport.html">
+</iframe>
+<script>
+promise_test(async t => {
+ await new Promise(resolve => iframe.addEventListener("load", resolve));
+
+ const inner_iframe = iframe.contentDocument.querySelector("iframe");
+
+ assert_equals(inner_iframe.contentDocument.body.textContent.trim(), 'Subframe',
+ "lazy-load iframe shouldn't be honored in script disabled iframe");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-far.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-far.html
new file mode 100644
index 0000000000..eeb05b7b98
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-far.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 10000vh;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes do not load when far from viewport."
+ );
+
+ function iframe_onload() {
+ t.unreached_func(
+ "Lazy-loading iframe far from viewport should not load."
+ )();
+ }
+
+ t.step_timeout(() => {
+ t.done();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal-far.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal-far.html
new file mode 100644
index 0000000000..f058b46fbd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal-far.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ display: flex;
+ }
+
+ #spacer {
+ width: 10000vw;
+ height: 130px;
+ flex-shrink: 0;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ flex-shrink: 0;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes do not load when far from viewport."
+ );
+
+ function img_onload() {
+ t.unreached_func(
+ "Lazy-loading iframe far from viewport should not load."
+ )();
+ }
+
+ t.step_timeout(() => {
+ t.done();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal.html
new file mode 100644
index 0000000000..80ba0829a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-horizontal.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ display: flex;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ flex-shrink: 0;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ flex-shrink: 0;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes load when near viewport."
+ );
+
+ function iframe_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for iframe to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-2.html
new file mode 100644
index 0000000000..d080831175
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-2.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller2>
+ <div id="spacer"></div>
+ <div id="scroller">
+ <iframe>
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes load when near viewport."
+ );
+
+ function iframe_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for iframe to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-3.html
new file mode 100644
index 0000000000..aa02bb4151
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-3.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ #scroller3 {
+ width: 120px;
+ height: 120px;
+ overflow: scroll;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller3>
+ <div id=scroller2>
+ <div id="scroller">
+ <div id="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+ </div>
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes load when near viewport."
+ );
+
+ function iframe_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for iframe to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-4.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-4.html
new file mode 100644
index 0000000000..e15d891fb5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-4.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ .spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller2>
+ <div class="spacer"></div>
+ <div id="scroller">
+ <div class="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes load when near viewport."
+ );
+
+ function iframe_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for iframe to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-5.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-5.html
new file mode 100644
index 0000000000..b36265024d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested-5.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ display: flex;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ .spacer {
+ width: 130px;
+ height: 130px;
+ flex-shrink: 0;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ flex-shrink: 0;
+ }
+</style>
+
+<div id=scroller2>
+ <div class="spacer"></div>
+ <div id="scroller">
+ <div class="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function iframe_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for iframe to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested.html
new file mode 100644
index 0000000000..50b5e7ee0a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller-nested.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller2>
+ <div id="scroller">
+ <div id="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes load when near viewport."
+ );
+
+ function iframe_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for iframe to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller.html
new file mode 100644
index 0000000000..631710e740
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-scroller.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #spacer {
+ width: 100px;
+ height: 100px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <iframe
+ id="target"
+ src="resources/subframe.html"
+ loading="lazy"
+ onload="iframe_onload();"
+ ></iframe>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded iframes load when near viewport."
+ );
+
+ function iframe_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for iframe to load."
+ )();
+ }, 2000);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-001.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-001.html
new file mode 100644
index 0000000000..408e5309e5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-001.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <link rel="match" href="iframe-loading-lazy-in-viewport-ref.html">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1860041">
+</head>
+<body>
+ <iframe loading="lazy" src="data:text/html,PASS" onload="document.documentElement.className = ''"></iframe>
+ <script>
+ document.querySelector("iframe").getBoundingClientRect();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-ref.html
new file mode 100644
index 0000000000..067bfa1920
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-viewport-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe src="data:text/html,PASS"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-load-event.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-load-event.html
new file mode 100644
index 0000000000..bf98bf7585
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-load-event.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<head>
+ <title>In-viewport loading=lazy iframes do not block the window load event</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<body>
+ <!-- This image blocks the window load event for 1 second -->
+ <img src="/common/square.png?pipe=trickle(d1)">
+
+ <!-- These iframes must load because they intersect the viewport, but they must
+ not block the window load event, because they are loading=lazy -->
+ <iframe id="visible" loading="lazy"
+ src="resources/subframe.html?visible&pipe=trickle(d3)"></iframe>
+ <iframe id="visibility_hidden" style="visibility:hidden;" loading="lazy"
+ src="resources/subframe.html?visibility_hidden&pipe=trickle(d3)"></iframe>
+</body>
+
+<script>
+ const visible_iframe = document.querySelector('#visible');
+ const hidden_iframe = document.querySelector('#visibility_hidden');
+
+ const visible_iframe_t =
+ async_test('In-viewport loading=lazy iframe does not block the load event');
+
+ const hidden_iframe_t =
+ async_test('In-viewport loading=lazy visibility:hidden iframe does not ' +
+ 'block the load event');
+
+ let has_window_loaded = false;
+ window.addEventListener("load", () => {
+ has_window_loaded = true;
+ });
+
+ visible_iframe.onload = visible_iframe_t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "The visible iframe should not block the load event");
+ });
+
+ hidden_iframe.onload = hidden_iframe_t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "The hidden iframe should not block the load event");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-queued-navigations.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-queued-navigations.html
new file mode 100644
index 0000000000..0569881ea5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-queued-navigations.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<head>
+ <title>Multiple queued lazy load navigations do not crash the page</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test('Multiple queued lazy load navigations do not crash ' +
+ 'the page');
+
+ let has_below_viewport_loaded = false;
+
+ window.addEventListener("load", t.step_func(() => {
+ assert_false(has_below_viewport_loaded,
+ "The below_viewport element should not have loaded before " +
+ "window.load().");
+
+ // Queue another lazy load navigation on the iframe. This should not result
+ // in multiple internal intersection observers being created for the iframe
+ // element, but instead should result in only one intersection observer
+ // associated with the iframe element, and the resulting navigation should
+ // be for the latest `src` attribute mutation.
+ const target = document.querySelector('#below_viewport');
+ target.src = 'resources/subframe.html?new-src';
+ target.scrollIntoView();
+ }));
+
+ const below_viewport_iframe_onload = t.step_func_done(() => {
+ const target = document.querySelector('#below_viewport');
+ // We check both of these to ensure that the `src` attribute and actual
+ // navigated resource do not get out-of-sync when navigating to lazy loaded
+ // resources.
+ assert_true(
+ target.src.includes('new-src'),
+ "The iframe's src should be updated to reflect the latest `src` " +
+ "mutation");
+ assert_true(
+ target.contentDocument.location.href.includes('new-src'),
+ 'The iframe should be navigated to the resource provided by the latest ' +
+ '`src` mutation');
+ });
+</script>
+
+<body>
+ <div style="height:3000vh;"></div>
+ <iframe id="below_viewport" src="resources/subframe.html?old-src"
+ loading="lazy" width="200px" height="100px"
+ onload="below_viewport_iframe_onload();"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-times.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-times.html
new file mode 100644
index 0000000000..89e41e61bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-multiple-times.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<head>
+ <title>Iframes with loading='lazy' can be lazy loaded multiple times</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <!-- This is used to represent the top of the viewport, so we can scroll the
+ below-viewport iframe out-of-view later in the test -->
+ <div id="top_div"></div>
+ <div style="height:1000vh;"></div>
+ <iframe id="below_viewport" loading="lazy" src="resources/unload-reporter.html?first"></iframe>
+
+<script>
+ const t = async_test();
+ const iframe = document.querySelector('#below_viewport');
+ const top_div = document.querySelector('#top_div');
+
+ let has_window_load_fired = false;
+ let iframe_being_unloaded = false;
+
+ // This should be triggered first.
+ window.addEventListener('load', t.step_func(() => {
+ has_window_load_fired = true;
+ // Scroll the loading=lazy below-viewport iframe into view, so that it loads.
+ iframe.scrollIntoView();
+ }));
+
+ window.addEventListener('message', t.step_func(msg => {
+ if (msg.data === 'unloading')
+ iframe_being_unloaded = true;
+ }));
+
+ iframe.onload = t.step_func(() => {
+ assert_true(has_window_load_fired,
+ "The loading=lazy below-viewport iframe should not block the " +
+ "window load event");
+ changeIframeSourceAndScrollToTop();
+ });
+
+ function changeIframeSourceAndScrollToTop() {
+ top_div.scrollIntoView();
+
+ // Lazily load a "different" iframe.
+ iframe.src = 'resources/unload-reporter.html?second';
+ iframe.onload =
+ t.unreached_func("The loading=lazy below-viewport iframe should lazily " +
+ "load its second resource, and not load it eagerly " +
+ "when the `src` attribute is changed");
+
+ // In 1s, scroll the iframe *back* into view, and record that it loads
+ // successfully.
+ t.step_timeout(() => {
+ assert_false(iframe_being_unloaded,
+ "The iframe's old resource is not eagerly unloaded");
+
+ iframe.onload = t.step_func_done(() => {
+ assert_true(iframe_being_unloaded,
+ "The iframe's old resources was unloaded correctly");
+ });
+
+ iframe.scrollIntoView();
+ }, 1000);
+ }
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-referrerpolicy-change.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-referrerpolicy-change.sub.html
new file mode 100644
index 0000000000..68734d5708
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-referrerpolicy-change.sub.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred loading=lazy iframes are fetched with the parse-time
+ `referrerpolicy` attribute</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="author" title="Raj T" href="mailto:rajendrant@chromium.org">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<script>
+ const below_viewport_iframe = new ElementLoadPromise("below_viewport_iframe");
+
+ async_test(function(t) {
+ // Change the referrer-policy and scroll down to load the deferred elements.
+ window.addEventListener("load", t.step_func(() => {
+ below_viewport_iframe.element().referrerPolicy = "no-referrer";
+ below_viewport_iframe.element().scrollIntoView();
+ }));
+
+ below_viewport_iframe.promise.then(
+ t.step_func_done(function() {
+ // The `Referer` header should be the full URL, as specified by the
+ // iframe's `referrerpolicy` attribute at parse-time, and not the origin
+ // (as specified in meta referrer tag) or null (as overridden
+ // referrerpolicy=no-referrer after load deferral).
+ assert_true(below_viewport_iframe.element().contentDocument.body.innerHTML
+ .includes("Referer: http://{{location[host]}}{{location[path]}}"),
+ "The iframe's `Referer` should be the referrer's full URL");
+ }));
+ }, "Test that when a deferred iframe is fetched, it uses the " +
+ "`referrerpolicy` specified at parse-time");
+</script>
+
+<body>
+ <meta name="referrer" content="origin">
+ <div style="height:1000vh;"></div>
+ <iframe id="below_viewport_iframe" src="/xhr/resources/echo-headers.py"
+ loading="lazy" width="200px" height="100px"
+ referrerpolicy="unsafe-url"
+ onload="below_viewport_iframe.resolve();">
+ </iframe>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-to-eager.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-to-eager.html
new file mode 100644
index 0000000000..371601a8c3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-to-eager.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<head>
+ <title>Below-viewport iframes with loading='lazy' load when set to
+ loading='eager' or the `loading` attribute is removed</title>
+ <link rel="author" title="Dom Farolino" href="mailto:domfarolino@gmail.com">
+ <link rel="help" href="https://github.com/whatwg/html/pull/5579">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test("Below-viewport iframes with loading='lazy' load when " +
+ "set to loading='eager' or the `loading` attribute is " +
+ "removed");
+
+ const iframe_1_onload = t.unreached_func("#iframe_1 should not load before " +
+ "the window load event");
+ const iframe_2_onload = t.unreached_func("#iframe_2 should not load before " +
+ "the window load event");
+
+ window.addEventListener("load", t.step_func(() => {
+ const iframe_1 = document.querySelector('#iframe_1');
+ const iframe_2 = document.querySelector('#iframe_2');
+
+ const iframe_1_promise = new Promise((resolve, reject) => {
+ iframe_1.onerror = reject;
+ iframe_1.onload = resolve;
+ });
+
+ const iframe_2_promise = new Promise((resolve, reject) => {
+ iframe_2.onerror = reject;
+ iframe_2.onload = resolve;
+ });
+
+ Promise.all([iframe_1_promise, iframe_2_promise])
+ .then(t.step_func_done())
+ .catch(t.unreached_func("The iframes should load successfully"));
+
+ // Kick off the requests.
+ iframe_1.loading = 'eager';
+ iframe_2.removeAttribute('loading'); // unset the attribute, putting it in
+ // the default (eager) state.
+ }));
+
+</script>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <iframe id="iframe_1"
+ src="resources/subframe.html?1"
+ loading="lazy" onload="iframe_1_onload();"></iframe>
+ <iframe id="iframe_2"
+ src="resources/subframe.html?2"
+ loading="lazy" onload="iframe_2_onload();"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html
new file mode 100644
index 0000000000..336703ebc4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<head>
+ <title>Iframes with loading='lazy' load when in the viewport</title>
+ <link rel="author" title="Scott Little" href="mailto:sclittle@chromium.org">
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="help" href="https://github.com/whatwg/html/pull/5579">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t_in_viewport =
+ async_test('In-viewport iframes load eagerly');
+ const t_below_viewport =
+ async_test('Below-viewport iframes load lazily');
+ const t_below_viewport_srcdoc =
+ async_test('Below-viewport srcdoc iframes load lazily');
+ const t_below_viewport_data_url =
+ async_test('Below-viewport data: url iframes load lazily');
+ const t_below_viewport_blob_url =
+ async_test('Below-viewport blob url iframes load lazily');
+
+
+ document.addEventListener('DOMContentLoaded', e => {
+ const blob_url_iframe = document.querySelector('#below_viewport_blob_url');
+ const blob = new Blob(['<p>Blob subframe</p>'], {type: 'text/html'});
+ const url = URL.createObjectURL(blob);
+ blob_url_iframe.src = url;
+ });
+
+ let has_window_loaded = false;
+
+ const in_viewport_iframe_onload = t_in_viewport.step_func_done(() => {
+ assert_false(has_window_loaded,
+ "The in_viewport iframe should not block the load event");
+ });
+
+ window.addEventListener("load", () => {
+ has_window_loaded = true;
+ document.getElementById("below_viewport_srcdoc").scrollIntoView();
+ });
+
+ const below_viewport_iframe_onload = t_below_viewport.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "The window load event should have fired before " +
+ "the below-viewport iframe loads");
+ });
+
+ // Onload handlers for below-viewport srcdoc iframe.
+ // Must make this accessible to the srcdoc iframe's body.
+ window.below_viewport_srcdoc_iframe_subresource_onload = t_below_viewport_srcdoc.step_func(() => {
+ assert_true(has_window_loaded,
+ "The window load event should have fired before " +
+ "the below-viewport srcdoc iframe's subresource loads");
+ });
+
+ const below_viewport_srcdoc_iframe_onload = t_below_viewport_srcdoc.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "The window load event should have fired before " +
+ "the below-viewport srcdoc iframe loads");
+ });
+
+ // Onload handler for below-viewport data url iframe.
+ const below_viewport_data_url_iframe_onload = t_below_viewport_data_url.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "The window load event should have fired before " +
+ "the below-viewport data url iframe loads");
+ });
+
+ // Onload handler for below-viewport blob url iframe.
+ const below_viewport_blob_url_iframe_onload = t_below_viewport_blob_url.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "The window load event should have fired before " +
+ "the below-viewport blob url iframe loads");
+ });
+
+</script>
+
+<body>
+ <iframe id="in_viewport" src="resources/subframe.html?in-viewport"
+ loading="lazy" width="200px" height="100px"
+ onload="in_viewport_iframe_onload();"></iframe>
+
+ <div style="height:2000vh;"></div>
+ <iframe id="below_viewport" src="resources/subframe.html?below-viewport"
+ loading="lazy" width="200px" height="100px"
+ onload="below_viewport_iframe_onload();"></iframe>
+ <iframe id="below_viewport_srcdoc"
+ srcdoc="<body><img src='/common/square.png?below-viewport'
+ onload='parent.below_viewport_srcdoc_iframe_subresource_onload();'></body>"
+ loading="lazy" width="200px" height="100px"
+ onload="below_viewport_srcdoc_iframe_onload();"></iframe>
+ <iframe id="below_viewport_data_url"
+ src="data:text/html,<p>Subframe</p>"
+ loading="lazy" width="200px" height="100px"
+ onload="below_viewport_data_url_iframe_onload();"></iframe>
+ <!-- This iframe has its `src` set to a blob URL dynamically above -->
+ <iframe id="below_viewport_blob_url"
+ loading="lazy" width="200px" height="100px"
+ onload="below_viewport_blob_url_iframe_onload();"></iframe>
+
+
+
+ <!-- This async script loads very slowly in order to ensure that, if the
+ below_viewport* elements have started loading, it has a chance to finish
+ loading before window load event fires, so that the test will dependably
+ fail in that case instead of potentially passing depending on how long
+ different resource fetches take. -->
+ <script async src="/common/slow.py"></script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes-ref.html
new file mode 100644
index 0000000000..3758ea2cab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>iframe with scrolling attr equals yes</title>
+<link rel="author" title="Jinfeng Ma" href="mailto:majinfeng1@xiaomi.org">
+
+<p>Test passes if you can see the scrollbars of the iframe displayed below.</p>
+<iframe src="support/iframe-which-content-height-equals-400px.html" scrolling="yes" width="200px" height="100px">
+</iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes.html
new file mode 100644
index 0000000000..9d85aa543d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>modify iframe scrolling attr to yes</title>
+<link rel="author" title="Jinfeng Ma" href="mailto:majinfeng1@xiaomi.org">
+<link rel="help" href="https://www.w3.org/TR/html401/present/frames.html#adef-scrolling">
+<link rel="match" href="iframe-modify-scrolling-attr-to-yes-ref.html">
+
+<p>Test passes if you can see the scrollbars of the iframe displayed below.</p>
+<iframe src="support/iframe-which-content-height-equals-400px.html" scrolling="no" width="200px" height="100px">
+</iframe>
+
+<script>
+ let iframe = document.querySelector("iframe");
+ iframe.onload = function () {
+ iframe.scrolling = 'yes';
+ };
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-network-error.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-network-error.sub.html
new file mode 100644
index 0000000000..4dd3012af4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-network-error.sub.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Network errors with iframe elements</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "//{{hosts[][nonexistent]}}/";
+ iframe.onload = () => t.done();
+ iframe.onerror = t.unreached_func("error event must not fire");
+ document.body.append(iframe);
+}, "new iframe: nonexistent host");
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "../resources/not-embeddable.html";
+ iframe.onload = () => t.done();
+ iframe.onerror = t.unreached_func("error event must not fire");
+ document.body.append(iframe);
+}, "new iframe: X-Frame-Options prevents embedding");
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ iframe.name = "existingIframe1";
+ iframe.onload = t.step_func(() => {
+ iframe.onload = () => t.done();
+ iframe.onerror = t.unreached_func("error event must not fire 2");
+
+ frames.existingIframe1.location.href = "//{{hosts[][nonexistent]}}/";
+ });
+ iframe.onerror = t.unreached_func("error event must not fire 1");
+ document.body.append(iframe);
+}, "navigating an existing iframe: nonexistent host");
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ iframe.name = "existingIframe2";
+ iframe.onload = t.step_func(() => {
+ iframe.onload = () => t.done();
+ iframe.onerror = t.unreached_func("error event must not fire 2");
+
+ frames.existingIframe2.location.href = "../resources/not-embeddable.html";
+ });
+ iframe.onerror = t.unreached_func("error event must not fire 1");
+ document.body.append(iframe);
+}, "navigating an existing iframe: X-Frame-Options prevents embedding");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-nosrc.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-nosrc.html
new file mode 100644
index 0000000000..57189a0b88
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-nosrc.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Check processing of iframe without src and srcdoc attribute</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+<iframe></iframe>
+<script>
+ let iframe = document.querySelector("iframe");
+
+ async_test(t => {
+ let originDoc = iframe.contentDocument;
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_equals(iframe.contentDocument, originDoc, "contentDocument shouldn't be changed");
+ }));
+ }, "iframe.contentDocument should not be changed");
+
+ async_test(t => {
+ iframe.addEventListener("load", t.unreached_func());
+ window.addEventListener("load", () => t.done());
+ }, "load event of iframe should not be fired after processing the element");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-synchronously-discard.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-synchronously-discard.html
new file mode 100644
index 0000000000..c339525ebd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-synchronously-discard.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>IFrame discards are processed synchronously</title>
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ async_test(function(t) {
+ var child = document.createElement("iframe");
+ child.src = "support/blank.htm?1";
+ child.onload = t.step_func(function () {
+ var childWindow = child.contentWindow;
+ var grandchild = childWindow.document.createElement("iframe");
+ grandchild.src = "blank.htm?2";
+ grandchild.onload = t.step_func(function () {
+ var grandchildWindow = grandchild.contentWindow;
+ assert_equals(child.contentWindow, childWindow, "child window");
+ assert_equals(childWindow.parent, window, "child parentage");
+ assert_equals(grandchild.contentWindow, grandchildWindow, "grandchild window");
+ assert_equals(grandchildWindow.parent, childWindow, "grandchild parentage");
+ document.body.removeChild(child);
+ assert_equals(child.contentWindow, null, "child should be discarded");
+ assert_equals(childWindow.parent, null, "child window should be discarded");
+ assert_equals(grandchild.contentWindow, null, "grandchild should be discarded");
+ assert_equals(grandchildWindow.parent, null, "grandchild window should be discarded");
+ t.done();
+ });
+ childWindow.document.body.appendChild(grandchild);
+ });
+ document.body.appendChild(child);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base-ref.html
new file mode 100644
index 0000000000..21f11d1953
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+<head>
+ <title>iframe Without Base Tag</title>
+</head>
+<body>
+ <iframe src="support/blank.htm">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html
new file mode 100644
index 0000000000..4637a41a47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+<head>
+ <title>iframe With Base Tag</title>
+ <link rel="match" href="iframe-with-base-ref.html">
+ <base href="support/">
+</head>
+<body>
+ <iframe src="blank.htm">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_harness.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_harness.js
new file mode 100644
index 0000000000..2b43c54e2f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_harness.js
@@ -0,0 +1,27 @@
+function get_test_results(id) {
+ async_test(function(test) {
+ test.step_timeout(loop, 100);
+ function loop() {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'stash.py?id=' + id);
+ xhr.onload = test.step_func(function() {
+ assert_equals(xhr.status, 200);
+ if (xhr.responseText) {
+ assert_equals(xhr.responseText, "OK");
+ test.done();
+ } else {
+ test.step_timeout(loop, 100);
+ }
+ });
+ xhr.send();
+ }
+ });
+}
+
+function send_test_results(results) {
+ var ok = true;
+ for (result in results) { ok = ok && results[result]; }
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', 'stash.py?id=' + results.id);
+ xhr.send(ok ? "OK" : "FAIL: " + JSON.stringify(results));
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_javascript_url_01.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_javascript_url_01.htm
new file mode 100644
index 0000000000..fd65f93298
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_javascript_url_01.htm
@@ -0,0 +1,61 @@
+<!DOCTYPE html><html><head><title>javascript: URL creating a document in an about:blank iframe</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#process-the-iframe-attributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log">FAILED (test did not run)</div>
+
+<iframe src="about:blank" name="ifr1"></iframe>
+<iframe src="" name="ifr2"></iframe>
+<iframe src="./" name="ifr3"></iframe>
+
+<script>
+var test = async_test();
+var results = {};
+var expected = {
+ ifr1:{url:"about:blank", sameDom: true},
+ ifr2:{url:"about:blank", sameDom: true},
+ ifr3:{url: location.href.replace(/\/[^\/]*$/, '/'), sameDom: true },
+ ifr4:{url:"about:blank", sameDom: true},
+ ifr5:{url:"about:blank", sameDom: true}
+}
+
+var js_url = 'javascript:"<html><script>var sameDom = false; try{var cn = top.document.body.className;sameDom = true;}catch(e){}; parent.postMessage( {url: document.URL, name: name, sameDom: sameDom}, \'*\')<\/script><body><p>JS-generated document</p></body></<html>";'
+window.addEventListener('message', function(e){
+ var ifr = e.data.name;
+ results[ifr] = e.data;
+ test.step(function(){
+ assert_equals(results[ifr].url, expected[ifr].url);
+ assert_equals(results[ifr].sameDom, expected[ifr].sameDom);
+ }, 'Testing URL and details of IFRAME ' + ifr);
+ if(Object.keys(results).length === Object.keys(expected).length){
+ test.done();
+ }
+}, false);
+
+var ifr = document.createElement('iframe');
+ifr.name = 'ifr4';
+document.body.appendChild(ifr);
+
+window.onload = function () {
+ for (var i = 0, frame, frames = document.getElementsByTagName('iframe'); frame = frames[i]; i++) {
+ try{
+ frame.src = js_url;
+ }catch(e){
+ results[frame.name] = 'Exception on setting!';
+ }
+ };
+
+ // An iframe with an initial src of a javascript: URL should also have a
+ // document URL of about:blank.
+ var ifr = document.createElement('iframe');
+ ifr.name = 'ifr5';
+ ifr.src = js_url;
+ document.body.appendChild(ifr);
+}
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_navigate_ancestor-1.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_navigate_ancestor-1.sub.html
new file mode 100644
index 0000000000..5e3b7682cc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_navigate_ancestor-1.sub.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ async_test(t => {
+ window.addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data, "can navigate");
+ }));
+ }, "A => B => B: B should be able to navigate B.");
+</script>
+<iframe src="https://{{hosts[][www]}}:{{ports[https][0]}}/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-its-child.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_remove_src.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_remove_src.html
new file mode 100644
index 0000000000..f0ff9ff508
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_remove_src.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that removing the src attribute of an iframe loads about:blank
+ instead of whatever was loaded previously.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <script>
+ var iframe;
+ var t = async_test();
+ t.step(setupFrame);
+
+ function setupFrame() {
+ iframe = document.createElement("iframe");
+ iframe.src = URL.createObjectURL(new Blob(["text"], { type: "text/html" }));
+ iframe.onload = t.step_func(blobLoaded);
+ document.body.appendChild(iframe);
+ }
+
+ var removalRunning = false;
+ function blobLoaded() {
+ assert_equals(iframe.contentDocument.location.protocol, "blob:",
+ "Should have loaded the blob");
+ assert_equals(iframe.contentDocument.documentElement.textContent, "text",
+ "Should have loaded the blob text");
+ iframe.onload = t.step_func_done(aboutBlankLoaded);
+ removalRunning = true;
+ iframe.removeAttribute("src");
+ removalRunning = false;
+ }
+
+ function aboutBlankLoaded() {
+ assert_false(removalRunning, "Should not have loaded about:blank sync");
+ assert_equals(iframe.contentDocument.location.href, "about:blank",
+ "Should have loaded about:blank");
+ assert_equals(iframe.contentDocument.documentElement.textContent, "",
+ "Should have loaded the about:blank text");
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_script.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_script.html
new file mode 100644
index 0000000000..3e65d91984
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_script.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: iframe_sandbox_allow_scripts</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+<script>
+ // Set up all our script stuff before the iframe starts loading, so we don't
+ // miss any messages from it.
+ var step1 = false;
+ var t = async_test("iframe_sandbox_allow_scripts");
+
+ setup({timeout:1000});
+ window.addEventListener("message", callback1, false);
+
+ function run() {
+ window.removeEventListener("message", callback1, false);
+ document.getElementById("testIframe").sandbox = "allow-scripts";
+ document.getElementById("testIframe").contentWindow.location.reload();
+ window.addEventListener("message", callback2, false);
+ }
+
+ function callback1(e) {
+ step1= !step1;
+ }
+
+ function callback2(e) {
+ t.step(function () {
+ assert_false(step1, "[allow-scripts] is not set.");
+ assert_equals(e.data, "Script executed", "[allow-scripts] is set.");
+ });
+ t.done();
+ }
+
+ // Make sure the iframe loads before we mess with it.
+ window.addEventListener("load", function() {
+ // The load event might fire before a message from the child comes in...
+ // Wait a bit to see if that message does come in.
+ setTimeout(run, 500);
+ });
+</script>
+<iframe id="testIframe" src="support/sandbox_allow_script.html" sandbox="allow-same-origin" style="display:none"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-1.html
new file mode 100644
index 0000000000..b033ec44d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Check that sandboxed iframe can perform navigation on the top frame
+ when allow-top-navigation is set</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ // We are the main test page. Open a popup, so that we can
+ // can experiment navigation of the top frame.
+ async_test(t => {
+ window.addEventListener("message", t.step_func_done(e => {
+ assert_equals(e.data, "can navigate");
+ e.source.close();
+ }));
+ let sandbox = "allow-top-navigation allow-scripts";
+ window.open("support/load-into-the-iframe.html?sandbox=" + sandbox);
+ }, "Frames with `allow-top-navigation` should be able to navigate the top frame.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-2.html
new file mode 100644
index 0000000000..bd7d92c0dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Check that sandboxed iframe cannot perform navigation on the top
+ frame when allow-top-navigation is not set</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ // We are the main test page. Open a popup, so that we can
+ // can experiment navigation of the top frame.
+ async_test(t => {
+ window.addEventListener("message", t.step_func_done(e => {
+ assert_equals(e.data, "cannot navigate");
+ e.source.close();
+ }));
+ window.open('support/load-into-the-iframe.html');
+ }, "Frames without `allow-top-navigation` should not be able to navigate the top frame.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-3.html
new file mode 100644
index 0000000000..c3e5dc1fd6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-3.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Check that sandboxed iframe can perform navigation on the top frame
+ when allow-top-navigation is set (even when
+ allow-top-navigation-by-user-activation is set)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ // We are the main test page. Open a popup, so that we can
+ // can experiment navigation of the top frame.
+ async_test(t => {
+ window.addEventListener("message", t.step_func_done(e => {
+ assert_equals(e.data, "can navigate");
+ e.source.close();
+ }));
+ // Specifying both allow-top-navigation and
+ // allow-top-navigation-by-user-activation is a document conformance
+ // error: allow-top-navigation-by-user-activation will have no effect.
+ let sandbox = "allow-top-navigation allow-top-navigation-by-user-activation allow-scripts";
+ window.open("support/load-into-the-iframe.html?sandbox=" + sandbox);
+ }, "Frames with `allow-top-navigation` should be able to navigate the top frame even when `allow-top-navigation-by-user-activation` is set.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation-manual.html
new file mode 100644
index 0000000000..0fa9de7c12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation-manual.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+ <style>
+ iframe { width: 400px; height: 300px;}
+ </style>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ setup({explicit_timeout: true});
+
+ async_test(function(t) {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, "BLOCKED", "The message should say 'BLOCKED'.");
+ }));
+ }, "The sandboxed iframe should post a message saying the top navigation was blocked when no user gesture.");
+ </script>
+</head>
+<body>
+ <p>This tests that an iframe in sandbox with 'allow-top-navigation-by-user-activation'
+ can navigate the top level page, if it is trigged by a user gesture.</p>
+ <p>Click on the button in the iframe and it should navigate the top page.</p>
+ <iframe id="i" sandbox="allow-scripts allow-top-navigation-by-user-activation" src="support/iframe-that-performs-top-navigation.html"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation_without_user_gesture.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation_without_user_gesture.html
new file mode 100644
index 0000000000..042851bbb4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation_without_user_gesture.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, "PASS", "The message should say 'PASS' instead of 'FAIL'");
+ }));
+}, "The sandboxed iframe should post a message saying the test was in the state of 'PASS'.");
+</script>
+</head>
+<body>
+ <p>This tests that an iframe in sandbox with 'allow-top-navigation-by-user-activation'
+ cannot navigate its top level page, if it is not trigged by a user gesture.</p>
+ <iframe sandbox='allow-top-navigation-by-user-activation allow-scripts' src="support/iframe-that-performs-top-navigation-without-user-gesture-failed.html"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_allow_downloads.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_allow_downloads.tentative.html
new file mode 100644
index 0000000000..26b8b393ab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_allow_downloads.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;a download&gt; triggered download in sandbox is allowed by allow-downloads.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="support/iframe_sandbox_download_helper.js"></script>
+<body>
+<script>
+"use strict";
+
+const attributes_list = [
+ '',
+ 'target="_blank"',
+ 'target="_blank" rel="noopener"',
+];
+
+const download_flags = [false, true];
+
+attributes_list.forEach(attributes =>
+ download_flags.forEach(download_flag =>
+ async_test(t => {
+ const download_token = token();
+ let iframe = document.createElement("iframe");
+ iframe.srcdoc = `<a ${attributes}>Download</a>`;
+ iframe.sandbox = "allow-same-origin allow-popups allow-downloads";
+ iframe.addEventListener('load', t.step_func(function () {
+ if (attributes !== '' || download_flag) {
+ // Specifiying `download` or a `target` should not trigger a
+ // navigation in this iframe.
+ iframe.contentWindow.addEventListener(
+ "unload", t.unreached_func("Unexpected navigation."));
+ }
+ let anchor = iframe.contentDocument.getElementsByTagName('a')[0];
+ anchor.href = "support/download_stash.py?token=" + download_token;
+ if (download_flag) anchor.download = null;
+ anchor.click();
+ AssertDownloadSuccess(t, download_token, DownloadVerifyDelay());
+ }), { once: true });
+
+ document.body.appendChild(iframe);
+ }, `<a ${attributes} ${download_flag ? "download" : ""}> triggered ` +
+ `download in sandbox is allowed by allow-downloads.`)));
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_block_downloads.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_block_downloads.tentative.html
new file mode 100644
index 0000000000..df46932319
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_anchor_download_block_downloads.tentative.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;a download&gt; triggered download in sandbox is blocked.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="support/iframe_sandbox_download_helper.js"></script>
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const download_token = token();
+ var iframe = document.createElement("iframe");
+ iframe.srcdoc = "<a>Download</a>";
+ iframe.sandbox = "allow-same-origin";
+ iframe.onload = t.step_func(function () {
+ iframe.contentWindow.addEventListener(
+ "unload", t.unreached_func("Unexpected navigation."));
+ let anchor = iframe.contentDocument.getElementsByTagName('a')[0];
+ anchor.href = `support/download_stash.py?token=${download_token}` +
+ `&finish-delay=${StreamDownloadFinishDelay()}`;
+ anchor.download = null;
+ anchor.click();
+ AssertDownloadFailure(t, download_token, StreamDownloadFinishDelay() +
+ DownloadVerifyDelay());
+ });
+
+ document.body.appendChild(iframe);
+}, "<a download> triggered download in sandbox is blocked.");
+
+async_test(t => {
+ const download_token = token();
+ let iframe = document.createElement("iframe");
+ iframe.srcdoc = '<a>Download</a>';
+ iframe.sandbox = "allow-same-origin";
+ iframe.onload = t.step_func(function () {
+ iframe.contentWindow.addEventListener(
+ "unload", t.unreached_func("Unexpected navigation."));
+ let anchor = iframe.contentDocument.getElementsByTagName('a')[0];
+ anchor.href = `support/download_stash.py?token=${download_token}`;
+ anchor.download = null;
+ anchor.click();
+ AssertDownloadFailure(t, download_token, DownloadVerifyDelay());
+ });
+
+ document.body.appendChild(iframe);
+}, '<a download> triggered download in sandbox is blocked ' +
+ 'before a request is made.');
+
+['', 'target="_blank" ', 'target="_blank" rel="noopener" '].forEach(
+ attributes => async_test(t => {
+ const download_token = token();
+ let iframe = document.createElement("iframe");
+ iframe.srcdoc = `<a ${attributes}>Download</a>`;
+ iframe.sandbox = "allow-same-origin allow-popups";
+ iframe.onload = t.step_func(function () {
+ iframe.contentWindow.addEventListener(
+ "unload", t.unreached_func("Unexpected navigation."));
+ let anchor = iframe.contentDocument.getElementsByTagName('a')[0];
+ anchor.href = `support/download_stash.py?token=${download_token}` +
+ `&finish-delay=${StreamDownloadFinishDelay()}`;
+ anchor.click();
+ AssertDownloadFailure(t, download_token, StreamDownloadFinishDelay() +
+ DownloadVerifyDelay());
+ });
+
+ document.body.appendChild(iframe);
+ }, `<a ${attributes}> triggered download in sandbox is blocked.`));
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-1.html
new file mode 100644
index 0000000000..ce171bfb8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>iframe sandbox without allow_modals (alert)</title>
+<link rel="author" title="Igalia" href="https://www.igalia.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe sandbox="allow-scripts"></iframe>
+<script src="support/iframe_sandbox_block_modals.js"></script>
+<script>
+ runTest("alert", undefined);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-2.html
new file mode 100644
index 0000000000..fbd4d23d01
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-2.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>iframe sandbox without allow_modals (confirm)</title>
+<link rel="author" title="Igalia" href="https://www.igalia.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe sandbox="allow-scripts"></iframe>
+<script src="support/iframe_sandbox_block_modals.js"></script>
+<script>
+ runTest("confirm", false);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-3.html
new file mode 100644
index 0000000000..5712301180
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-3.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>iframe sandbox without allow_modals (prompt)</title>
+<link rel="author" title="Igalia" href="https://www.igalia.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe sandbox="allow-scripts"></iframe>
+<script src="support/iframe_sandbox_block_modals.js"></script>
+<script>
+ runTest("prompt", null);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-4.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-4.html
new file mode 100644
index 0000000000..f750e345ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_block_modals-4.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>iframe sandbox without allow_modals (print)</title>
+<link rel="author" title="Igalia" href="https://www.igalia.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe sandbox="allow-scripts"></iframe>
+<script src="support/iframe_sandbox_block_modals.js"></script>
+<script>
+ runTest("print", undefined);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-1.html
new file mode 100644
index 0000000000..4cf48184c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-1.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that sandboxed iframe can not navigate their ancestors</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.data, "can not navigate", "Should have the right message");
+ });
+</script>
+<iframe sandbox="allow-scripts" src="support/iframe-tried-to-be-navigated-by-its-child.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-2.html
new file mode 100644
index 0000000000..159491c73c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-2.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that unsandboxed iframe can navigate their ancestors</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.data, "can navigate", "Should have the right message");
+ });
+</script>
+<iframe src="support/iframe-tried-to-be-navigated-by-its-child.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_descendants.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_descendants.html
new file mode 100644
index 0000000000..0934adfa82
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_descendants.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that sandboxed iframe can navigate their descendants</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.data, "can navigate", "Should have the right message");
+ });
+</script>
+<iframe sandbox="allow-scripts" src="support/iframe-trying-to-navigate-its-child.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back-2.html
new file mode 100644
index 0000000000..7a94f1ce4a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back-2.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that sandboxed iframe can navigate their self</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func((e) => {
+ if (e.data == 'pushstatebackdone') t.done();
+ });
+
+ function doNavigation() {
+ frames[0].postMessage('pushstateback', '*');
+ }
+</script>
+<iframe id="child_frame" sandbox="allow-scripts" src="support/iframe-tried-to-be-navigated-by-history.html" onload="doNavigation();"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back.html
new file mode 100644
index 0000000000..7026edf8f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_back.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that sandboxed iframe can not navigate their ancestors</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test();
+ onpopstate = t.unreached_func('no pop state');
+
+ function doNavigation() {
+ history.pushState( {state: "one past"}, 'page 2', '');
+ frames[0].postMessage('back', '*');
+ t.step_timeout(() => {
+ t.done();
+ }, 1000);
+ }
+</script>
+<iframe id="child_frame" sandbox="allow-scripts" src="support/iframe-tried-to-be-navigated-by-history.html" onload="doNavigation();"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_forward.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_forward.html
new file mode 100644
index 0000000000..e9d1def099
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_history_go_forward.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that sandboxed iframe can not navigate their ancestors</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test();
+ var pop_state_count = 0;
+ onpopstate = t.step_func((e) => {
+ pop_state_count++;
+ if (pop_state_count == 1) {
+ // Should not generate a pop state
+ frames[0].postMessage('forward', '*');
+ t.step_timeout(() => {
+ t.done();
+ }, 1000);
+ } else if (pop_state_count > 1) {
+ assert_unreached('no pop state');
+ }
+ });
+
+ function doNavigation() {
+ history.pushState( {state: "one past"}, 'page 2', '');
+ // Should generate a pop state
+ history.back();
+ }
+</script>
+<iframe id="child_frame" sandbox="allow-scripts" src="support/iframe-tried-to-be-navigated-by-history.html" onload="doNavigation();"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_itself.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_itself.html
new file mode 100644
index 0000000000..12c4e0ca50
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_itself.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that sandboxed iframe can navigate itself</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.data, "can navigate", "Should have the right message");
+ });
+</script>
+<iframe sandbox="allow-scripts" src="support/iframe-trying-to-navigate-itself.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html
new file mode 100644
index 0000000000..19704b38a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check that sandboxed iframe can not navigate other frame's popup</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+
+// This HTML file is loaded 3 times.
+// (1) As the initial test file (mode = '').
+// (2) In the popup window (mode = 'popup').
+// (3) In the sandboxed iframe (mode = 'iframe').
+// Note: The sandboxed iframe (3) tries to navigate the popup window (2) to
+// a new mode=iframenavigated URL. But this must be blocked because (3) is not
+// the 'one permitted sandboxed navigator'.
+// https://html.spec.whatwg.org/multipage/origin.html#one-permitted-sandboxed-navigator
+(() => {
+ const mode = '{{GET[mode]}}';
+ if (mode == 'popup') {
+ // (2): Loaded in the popup window.
+ return;
+ }
+ if (mode == 'iframe') {
+ // (3): Loaded in the sandboxed iframe.
+ try {
+ // Attempts to navigate the popup window (2).
+ parent.document.popupWin.location = location.href + 'navigated';
+ } catch (e) {
+ parent.postMessage('cannot navigate');
+ }
+ return;
+ }
+ if (mode == 'iframenavigated') {
+ // This URL page must not be loaded.
+ opener.postMessage('can navigate');
+ return;
+ }
+
+ // (1): Loaded as the initial test file.
+ promise_test(async t => {
+ // Opens a popup window to load the page (2).
+ document.popupWin = window.open(location.href + '?mode=popup', '_blank');
+ t.add_cleanup(() => document.popupWin.close());
+ await new Promise(resolve => {
+ document.popupWin.addEventListener('load', resolve);
+ });
+
+ // Adds an iframe to load the page (3).
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => iframe.remove());
+ iframe.sandbox = 'allow-popups allow-same-origin allow-scripts';
+ iframe.src = location.href + '?mode=iframe';
+ const message_promise = new Promise(resolve => {
+ window.addEventListener('message', (e) => { resolve(e.data); });
+ });
+ document.body.appendChild(iframe);
+
+ const result = await message_promise;
+ assert_equals(result, 'cannot navigate');
+ }, "Sandboxed iframe can not navigate other frame's popup");
+
+})();
+</script>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_allow_downloads.sub.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_allow_downloads.sub.tentative.html
new file mode 100644
index 0000000000..6b3b3104ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_allow_downloads.sub.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation resulted download in sandbox is allowed by allow-downloads.</title>
+<meta name="timeout" content="long" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/resources/testharness.js"></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="support/iframe_sandbox_download_helper.js"></script>
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const token = "{{$id:uuid()}}";
+ var iframe = document.createElement("iframe");
+ iframe.srcdoc = "<a>Download</a>";
+ iframe.sandbox = "allow-same-origin allow-downloads";
+ iframe.onload = t.step_func(function () {
+ iframe.contentWindow.addEventListener(
+ "unload", t.unreached_func("Unexpected navigation."));
+ var anchor = iframe.contentDocument.getElementsByTagName('a')[0];
+ // Set |finish-delay| to let the server stream a response over a period
+ // of time, so it's able to catch potential download cancellation by
+ // detecting a socket close.
+ anchor.href = "support/download_stash.py?token=" + token + "&finish-delay=" + StreamDownloadFinishDelay();
+ anchor.click();
+ AssertDownloadSuccess(t, token, StreamDownloadFinishDelay() + DownloadVerifyDelay());
+ });
+
+ document.body.appendChild(iframe);
+}, "Navigation resulted download in sandbox is allowed by allow-downloads.");
+
+async_test(t => {
+ const token = "{{$id:uuid()}}";
+ var iframe = document.createElement("iframe");
+
+ const folder = location.origin+"/html/semantics/embedded-content/the-iframe-element/";
+ const href = `${folder}support/download_stash.py?token=${token}&finish-delay=${StreamDownloadFinishDelay() }`;
+ const objectDoc =`<a href="${href}">download</a>
+ <${"script"}> document.querySelector("a").click(); </${"script"}>`;
+
+ iframe.srcdoc = `<object data='data:text/html,${objectDoc}'></object>`;
+ iframe.sandbox = "allow-same-origin allow-scripts allow-downloads";
+ iframe.addEventListener("load",()=>{
+ AssertDownloadSuccess(t, token, StreamDownloadFinishDelay() + DownloadVerifyDelay());
+ })
+ document.body.appendChild(iframe);
+}, "Navigation resulted download in sandbox from <object> is allowed by allow-downloads.");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_block_downloads.sub.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_block_downloads.sub.tentative.html
new file mode 100644
index 0000000000..87d1d722d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigation_download_block_downloads.sub.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation resulted download in sandbox is blocked.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/resources/testharness.js"></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="support/iframe_sandbox_download_helper.js"></script>
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const token = "{{$id:uuid()}}";
+ var iframe = document.createElement("iframe");
+ iframe.srcdoc = "<a>Download</a>";
+ iframe.sandbox = "allow-same-origin";
+ iframe.onload = t.step_func(function () {
+ iframe.contentWindow.addEventListener(
+ "unload", t.unreached_func("Unexpected navigation."));
+ var anchor = iframe.contentDocument.getElementsByTagName('a')[0];
+ // Set |finish-delay| to let the server stream a response over a period
+ // of time, so it's able to catch potential download cancellation by
+ // detecting a socket close.
+ anchor.href = "support/download_stash.py?token=" + token + "&finish-delay=" + StreamDownloadFinishDelay();
+ anchor.click();
+ AssertDownloadFailure(t, token, StreamDownloadFinishDelay() + DownloadVerifyDelay());
+ });
+
+ document.body.appendChild(iframe);
+}, "Navigation resulted download in sandbox is blocked.");
+
+
+async_test(t => {
+ const token = "{{$id:uuid()}}";
+ var iframe = document.createElement("iframe");
+
+ const folder = location.origin+"/html/semantics/embedded-content/the-iframe-element/";
+ const href = `${folder}support/download_stash.py?token=${token}&finish-delay=${StreamDownloadFinishDelay() }`;
+ const objectDoc =`<a href="${href}">download</a>
+ <${"script"}> document.querySelector("a").click(); </${"script"}>`;
+
+ iframe.srcdoc = `<object data='data:text/html,${objectDoc}'></object>`;
+ iframe.sandbox = "allow-same-origin allow-scripts";
+ iframe.addEventListener("load",()=>{
+ AssertDownloadFailure(t, token, StreamDownloadFinishDelay() + DownloadVerifyDelay());
+ })
+ document.body.appendChild(iframe);
+}, "Navigation resulted download in sandbox from <object> is blocked.");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html
new file mode 100644
index 0000000000..342d422036
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check that popups from a sandboxed iframe escape the sandbox if
+ allow-popups-to-escape-sandbox is used</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox">
+</iframe>
+<script>
+ var t = async_test();
+ var ourOrigin;
+ onmessage = t.step_func(function(e) {
+ assert_equals(e.data, "hello", "This is our origin getter message");
+ ourOrigin = e.origin;
+
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.origin, "null", "It came from a sandboxed iframe");
+ assert_equals(e.data.data, undefined, "Should have the right message");
+ assert_equals(e.data.origin, ourOrigin, "Should have escaped the sandbox");
+ });
+
+ document.querySelector("iframe").src = "iframe_sandbox_popups_helper-1.html";
+ });
+ postMessage("hello", "*");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html
new file mode 100644
index 0000000000..40ffbb1e02
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check that popups from a sandboxed iframe escape the sandbox if
+ allow-popups-to-escape-sandbox is used</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox">
+</iframe>
+<script>
+ var t = async_test();
+ var ourOrigin;
+ onmessage = t.step_func(function(e) {
+ assert_equals(e.data, "hello", "This is our origin getter message");
+ ourOrigin = e.origin;
+
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.origin, "null", "It came from a sandboxed iframe");
+ assert_equals(e.data.data, undefined, "Should have the right message");
+ assert_equals(e.data.origin, ourOrigin, "Should have escaped the sandbox");
+ });
+
+ var iframe = document.querySelector("iframe");
+ iframe.onload = function() {
+ frames[0].postMessage("start", "*");
+ }
+ iframe.src = "iframe_sandbox_popups_helper-2.html";
+ });
+ addEventListener("load", function() {
+ postMessage("hello", "*");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html
new file mode 100644
index 0000000000..2d35fd5fc1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check that popups from a sandboxed iframe escape the sandbox if
+ allow-popups-to-escape-sandbox is used</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox">
+</iframe>
+<script>
+ var t = async_test();
+ var ourOrigin;
+ onmessage = t.step_func(function(e) {
+ assert_equals(e.data, "hello", "This is our origin getter message");
+ ourOrigin = e.origin;
+
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.origin, "null", "It came from a sandboxed iframe");
+ assert_equals(e.data.data, undefined, "Should have the right message");
+ assert_equals(e.data.origin, ourOrigin, "Should have escaped the sandbox");
+ });
+
+ document.querySelector("iframe").src = "iframe_sandbox_popups_helper-3.html";
+ });
+ postMessage("hello", "*");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-1.html
new file mode 100644
index 0000000000..6b120f15d0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script>
+ if (opener) {
+ // We're the popup. Send back our state. What we really want to send is
+ // our origin, but that will come automatically.
+ opener.postMessage(undefined, "*");
+ self.close();
+ } else {
+ // We're the child. Start listening for messages and open ourselves as the
+ // popup.
+ onmessage = function (e) {
+ parent.postMessage({ data: e.data, origin: e.origin }, "*");
+ };
+ popupWin = window.open(location.href);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-2.html
new file mode 100644
index 0000000000..ea28cf5375
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<body>
+<script>
+ if (opener) {
+ // We're the popup. Send back our state. What we really want to send is
+ // our origin, but that will come automatically.
+ opener.postMessage(undefined, "*");
+ self.close();
+ } else {
+ // We're the child. Start listening for messages from our parent and open
+ // ourselves as the popup when we get the "start" message.
+ onmessage = function (e) {
+ if (e.data == "start") {
+ // Now listen for messages from the thing we plan to open.
+ onmessage = function(e) {
+ parent.postMessage({ data: e.data, origin: e.origin }, "*");
+ }
+
+ var a = document.createElement("a");
+ a.href = location.href;
+ a.target = "_blank";
+ a.rel = "opener";
+ document.body.appendChild(a);
+ a.click();
+ }
+ };
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-3.html
new file mode 100644
index 0000000000..ef3e59037f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_helper-3.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script>
+ if (opener) {
+ // We're the popup. Send back our state. What we really want to send is
+ // our origin, but that will come automatically.
+ opener.postMessage(undefined, "*");
+ self.close();
+ } else {
+ // We're the child. Start listening for messages and open ourselves as the
+ // popup.
+ onmessage = function (e) {
+ parent.postMessage({ data: e.data, origin: e.origin }, "*");
+ };
+ var popupWin = window.open();
+ popupWin.location.href = location.href;
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html
new file mode 100644
index 0000000000..3dee96d67a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check that popups from a sandboxed iframe do not escape the sandbox</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.origin, "null", "It came from a sandboxed iframe");
+ assert_equals(e.data.data, undefined, "Should have the right message");
+ assert_equals(e.data.origin, "null", "Should not have escaped the sandbox");
+ });
+</script>
+<iframe sandbox="allow-scripts allow-popups"
+ src="iframe_sandbox_popups_helper-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html
new file mode 100644
index 0000000000..27046db744
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check that popups from a sandboxed iframe do not escape the sandbox</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.origin, "null", "It came from a sandboxed iframe");
+ assert_equals(e.data.data, undefined, "Should have the right message");
+ assert_equals(e.data.origin, "null", "Should not have escaped the sandbox");
+ });
+ addEventListener("load", function() {
+ frames[0].postMessage("start", "*");
+ });
+</script>
+<iframe sandbox="allow-scripts allow-popups"
+ src="iframe_sandbox_popups_helper-2.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html
new file mode 100644
index 0000000000..556387e14a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check that popups from a sandboxed iframe do not escape the sandbox</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var t = async_test();
+ onmessage = t.step_func_done(function(e) {
+ assert_equals(e.origin, "null", "It came from a sandboxed iframe");
+ assert_equals(e.data.data, undefined, "Should have the right message");
+ assert_equals(e.data.origin, "null", "Should not have escaped the sandbox");
+ });
+</script>
+<iframe sandbox="allow-scripts allow-popups"
+ src="iframe_sandbox_popups_helper-3.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_allow_downloads.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_allow_downloads.tentative.html
new file mode 100644
index 0000000000..158fc4f947
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_allow_downloads.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Downloads triggered by window.open from sandbox are blocked.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="support/iframe_sandbox_download_helper.js"></script>
+<body>
+<script>
+"use strict";
+
+['', '"_blank"', '"_blank", "noopener"'].forEach(options =>
+ async_test(t => {
+ const download_token = token();
+ let iframe = document.createElement("iframe");
+ const download_link = `support/download_stash.py?token=${download_token}` +
+ `&finish-delay=${StreamDownloadFinishDelay()}`;
+ iframe.srcdoc = `<script>window.open("${download_link}", ${options})</scr` +
+ `ipt>`;
+ iframe.sandbox = "allow-same-origin allow-popups allow-scripts " +
+ "allow-downloads";
+ AssertDownloadSuccess(t, download_token, DownloadVerifyDelay());
+ document.body.appendChild(iframe);
+ }, `window.open(download, ${options}) triggering download in ` +
+ 'sandbox is allowed by allow-downloads.'));
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_block_downloads.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_block_downloads.tentative.html
new file mode 100644
index 0000000000..20423e202f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_block_downloads.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Downloads triggered by window.open from sandbox are blocked.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-iframe-element">
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="support/iframe_sandbox_download_helper.js"></script>
+<body>
+<script>
+"use strict";
+
+['', '"_blank"', '"_blank", "noopener"'].forEach(options =>
+ async_test(t => {
+ const download_token = token();
+ let iframe = document.createElement("iframe");
+ const download_link = `support/download_stash.py?token=${download_token}` +
+ `&finish-delay=${StreamDownloadFinishDelay()}`;
+ iframe.srcdoc = `<script>window.open("${download_link}", ${options})</scr` +
+ `ipt>`;
+ iframe.sandbox = "allow-same-origin allow-popups allow-scripts";
+ AssertDownloadFailure(t, download_token, StreamDownloadFinishDelay() +
+ DownloadVerifyDelay());
+ document.body.appendChild(iframe);
+ }, `window.open(download, ${options}) triggering download in ` +
+ 'sandbox is blocked.'));
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_01.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_01.html
new file mode 100644
index 0000000000..900d8cd022
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_01.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>moving modified IFRAME in document (original page about:blank, DOM modification)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#iframe-load-event-steps">
+<iframe src="about:blank"></iframe>
+<div id="target"></div>
+<script>
+setup({ single_test: true });
+onload = function() {
+ var ifr = document.getElementsByTagName('iframe')[0];
+ ifr.contentDocument.body.appendChild(ifr.contentDocument.createElement('p')).textContent = 'Modified document';
+ setTimeout(function() {
+ ifr.onload = function() {
+ assert_equals(ifr.contentDocument.body.textContent.indexOf('Modified'), -1);
+ done();
+ };
+ document.getElementById('target').appendChild(ifr);
+ }, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_02.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_02.html
new file mode 100644
index 0000000000..d82dafe3ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_02.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>moving modified IFRAME in document (original page about:blank, document.write modification)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#iframe-load-event-steps">
+<iframe src="about:blank"></iframe>
+<div id="target"></div>
+<script>
+setup({ single_test: true });
+onload = function() {
+ var ifr = document.getElementsByTagName('iframe')[0];
+ ifr.contentDocument.open();
+ ifr.contentDocument.write('Modified document');
+ ifr.contentDocument.close();
+ setTimeout(function() {
+ ifr.onload = function() {
+ assert_equals(ifr.contentDocument.body.textContent.indexOf('Modified'), -1);
+ done();
+ };
+ document.getElementById('target').appendChild(ifr);
+ }, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html
new file mode 100644
index 0000000000..5db77ad890
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>moving modified IFRAME in document (original page from server, DOM modification)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#iframe-load-event-steps">
+<iframe src="support/blank.htm"></iframe>
+<div id="target"></div>
+<script>
+setup({ single_test: true });
+onload = function() {
+ var ifr = document.getElementsByTagName('iframe')[0];
+ ifr.contentDocument.body.appendChild(ifr.contentDocument.createElement('p')).textContent = 'Modified document';
+ setTimeout(function() {
+ ifr.onload = function() {
+ assert_equals(ifr.contentDocument.body.textContent.indexOf('Modified'), -1);
+ done();
+ };
+ document.getElementById('target').appendChild(ifr);
+ }, 100);
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_04.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_04.html
new file mode 100644
index 0000000000..bd076948ab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_04.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>moving modified IFRAME in document (original page from server, document.write modification)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#iframe-load-event-steps">
+<iframe src="support/blank.htm"></iframe>
+<div id="target"></div>
+<script>
+setup({ single_test: true });
+onload = function(){
+ var ifr = document.getElementsByTagName('iframe')[0];
+ ifr.contentDocument.open();
+ ifr.contentDocument.write('Modified document');
+ ifr.contentDocument.close();
+ setTimeout(function() {
+ ifr.onload = function () {
+ assert_equals(ifr.contentDocument.body.textContent.indexOf('Modified'), -1);
+ done();
+ };
+ document.getElementById('target').appendChild(ifr);
+ }, 100);
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/multiple-iframes-with-allow-scripts-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/multiple-iframes-with-allow-scripts-crash.html
new file mode 100644
index 0000000000..572f119be8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/multiple-iframes-with-allow-scripts-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <style>
+ iframe {
+ margin: 10px;
+ float: left;
+ width: 300px;
+ height: 300px;
+ border: none;
+ }
+ </style>
+ <iframe sandbox="allow-scripts" src="resources/hello-world.html"> </iframe>
+ <iframe sandbox="allow-scripts" src="resources/hello-world.html"> </iframe>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/empty.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/empty.html
new file mode 100644
index 0000000000..763b0739be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/empty.html
@@ -0,0 +1 @@
+<!DOCTYPE html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/hello-world.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/hello-world.html
new file mode 100644
index 0000000000..0d1111b48c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/hello-world.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ * { color: 'green'; }
+ </style>
+ </head>
+ <body>
+ <div><span style="color: green">Hello world!</span></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/iframe-loading-lazy-in-viewport.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/iframe-loading-lazy-in-viewport.html
new file mode 100644
index 0000000000..1efd2bd056
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/iframe-loading-lazy-in-viewport.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe id="iframe" loading="lazy" src="subframe.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/post-origin-to-opener.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/post-origin-to-opener.html
new file mode 100644
index 0000000000..bb625942f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/post-origin-to-opener.html
@@ -0,0 +1,3 @@
+<script>
+ opener.postMessage({origin: window.origin}, "*");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/sandbox-top-navigation-helper.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/sandbox-top-navigation-helper.js
new file mode 100644
index 0000000000..413f392dfc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/sandbox-top-navigation-helper.js
@@ -0,0 +1,76 @@
+// To use this file, use the following imports:
+// // META: script=/common/dispatcher/dispatcher.js
+// // META: script=/common/get-host-info.sub.js
+// // META: script=/common/utils.js
+// // META: script=/resources/testdriver.js
+// // META: script=/resources/testdriver-vendor.js
+// // META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// // META: script=./resources/sandbox-top-navigation-helper.js
+
+// Helper file that provides various functions to test top-level navigation
+// with various frame and sandbox flag configurations.
+
+async function createNestedIframe(parent, origin, frame_sandbox, header_sandbox)
+{
+ let headers = [];
+ if (header_sandbox) {
+ headers.push([
+ "Content-Security-Policy",
+ "sandbox allow-scripts " + header_sandbox
+ ]);
+ }
+ let iframe_attributes = {};
+ if (frame_sandbox) {
+ iframe_attributes.sandbox = "allow-scripts " + frame_sandbox;
+ }
+ return parent.addIframe({
+ origin: origin,
+ scripts: [
+ '/resources/testdriver.js',
+ '/resources/testdriver-driver.js',
+ '/resources/testdriver-vendor.js'
+ ],
+ headers: headers,
+ }, iframe_attributes);
+}
+
+async function attemptTopNavigation(iframe, should_succeed) {
+ let did_succeed;
+ try {
+ await iframe.executeScript(() => {
+ window.top.location.href = "https://google.com";
+ });
+ did_succeed = true;
+ } catch (e) {
+ did_succeed = false;
+ }
+
+ assert_equals(did_succeed, should_succeed,
+ should_succeed ?
+ "The navigation should succeed." :
+ "The navigation should fail.");
+}
+
+async function setupTest() {
+ const rcHelper = new RemoteContextHelper();
+ return rcHelper.addWindow(/*config=*/ null, /*options=*/ {});
+}
+
+async function activate(iframe) {
+ return iframe.executeScript(async () => {
+ let b = document.createElement("button");
+ document.body.appendChild(b);
+
+ // Since test_driver.bless() does not play nicely with the remote context
+ // helper, this is a workaround to trigger user activation in the iframe.
+ // This adds a button to the iframe and then simulates hitting the 'tab' key
+ // twice. Once to focus on the button, and once to trigger user activation
+ // in the iframe (user activation is given to the frame that has focus when
+ // the tab key is pressed, not the frame that ends up getting focus). Note
+ // that this will result in both the parent and this frame getting user
+ // activation. Note that this currently only works for iframes nested 1
+ // level deep.
+ test_driver.set_test_context(window.top);
+ return test_driver.send_keys(document.body, "\uE004\uE004");
+ });
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/subframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/subframe.html
new file mode 100644
index 0000000000..07cb999afa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/subframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+ <p>Subframe</p>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/unload-reporter.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/unload-reporter.html
new file mode 100644
index 0000000000..18599b2a6e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/resources/unload-reporter.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<h1>I'll report to my parent when I'm unloaded</h1>
+
+<script>
+ window.onbeforeunload = e => {
+ parent.postMessage('unloading', '*');
+ };
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_child.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_child.html
new file mode 100644
index 0000000000..a36e231fa2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_child.html
@@ -0,0 +1,12 @@
+<script src="iframe_harness.js"></script>
+<body>
+ <iframe src="same_origin_grandchild.html"></iframe>
+</body>
+<script>
+ send_test_results({
+ "id": '08782f28-e313-47ae-8cd7-419f3e194b0a',
+ "parent": window.parent !== window,
+ "grandparent": window.parent.parent === window.parent,
+ "top": window.top === window.parent,
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_grandchild.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_grandchild.html
new file mode 100644
index 0000000000..e7a2293b76
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_grandchild.html
@@ -0,0 +1,11 @@
+<script src="iframe_harness.js"></script>
+<body>
+</body>
+<script>
+ send_test_results({
+ "id": '66de8d44-7da7-47c7-9a52-41cba4f22bfe',
+ "parent": window.parent !== window,
+ "grandparent": window.parent.parent !== window.parent,
+ "top": window.top === window.parent.parent,
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_parentage.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_parentage.html
new file mode 100644
index 0000000000..a163eb8eec
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/same_origin_parentage.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Check the frame heriarchy</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="iframe_harness.js"></script>
+<body>
+ <iframe src="same_origin_child.html"></iframe>
+</body>
+<script>
+ get_test_results('17381dae-9c3e-4661-9f2b-28eb07a5f2fc');
+ get_test_results('08782f28-e313-47ae-8cd7-419f3e194b0a');
+ get_test_results('66de8d44-7da7-47c7-9a52-41cba4f22bfe');
+ send_test_results({
+ "id": '17381dae-9c3e-4661-9f2b-28eb07a5f2fc',
+ "parent": window.parent === window,
+ "top": window.top === window,
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-ascii-case-insensitive.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-ascii-case-insensitive.html
new file mode 100644
index 0000000000..fd77bfa42f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-ascii-case-insensitive.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>iframe 'sandbox' ASCII case insensitive</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-iframe-sandbox">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+async_test(function(t) {
+ let iframe = document.createElement('iframe');
+ iframe.setAttribute('sandbox', 'allow-same-or\u0130gin');
+ iframe.setAttribute('hidden', '');
+
+ assert_true(iframe.sandbox.supports('allow-same-origin'), 'supports the allow-same-origin token');
+
+ iframe.src = 'support/blank.htm';
+ iframe.onload = t.step_func_done(function() {
+ try {
+ assert_equals(iframe.contentDocument, null, 'child document not reachable');
+ } catch (e) {
+ // The assert_equals throwing is a pass.
+ }
+ });
+ document.body.appendChild(iframe);
+}, document.title + ', allow-same-or\u0130gin');
+
+async_test(function(t) {
+ let iframe = document.createElement('iframe');
+ iframe.setAttribute('sandbox', 'allow-\u017Fcripts');
+ iframe.setAttribute('hidden', '');
+
+ assert_true(iframe.sandbox.supports('allow-scripts'), 'supports the allow-scripts token');
+
+ window.onmessage = t.unreached_func('no scripts should run in the iframe');
+ iframe.src = 'support/sandbox_allow_script.html';
+ iframe.onload = t.step_func(function() {
+ t.step_timeout(t.step_func_done(), 100);
+ });
+ document.body.appendChild(iframe);
+}, document.title + ', allow-\u017Fcripts');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed-frame.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed-frame.html
new file mode 100644
index 0000000000..0f35f28709
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed-frame.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+ // Sandbox flags are inherited from a document toward every frame it creates,
+ // which then is inherited to every new document created in this frame.
+ //
+ // Using the flag 'allow-popups-to-escape-sandbox' inhibits this inheritance
+ // mechanism when the new frame is a popup.
+ //
+ // Sandbox flags are not inherited from the initiator/creator when loading a
+ // local scheme document unlike CSP (tested in
+ // ./sandbox-inherit-to-blank-document-unsandboxed.html)
+ //
+ // This tests in particular the initial empty document and the first
+ // about:blank navigation and verifies that no sandbox is applied on the
+ // popups.
+ promise_test(async test => {
+ const msg = await new Promise(r => window.addEventListener("message", r));
+ assert_false(msg.data.access_initial_navigation_to_about_blank_throws,
+ "Failed to access initial about:blank popup, it is probably sandboxed"
+ );
+ assert_false(msg.data.access_first_navigation_to_about_blank_throws,
+ "Failed to access navigation to about:blank, it is probably sandboxed"
+ );
+ assert_false(msg.data.access_after_delay_initial_navigation_to_about_blank_throws,
+ "Failed to access navigation to about:blank, it is probably sandboxed"
+ );
+ assert_false(msg.data.access_after_delay_first_navigation_to_about_blank_throws,
+ "Failed to access navigation to about:blank, it is probably sandboxed"
+ );
+ }, "Popup do not inherit sandbox, because of " +
+ "'allow-popups-to-escape-sandbox'. The document isn't sandboxed.")
+
+</script>
+<iframe
+ sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox"
+ srcdoc="
+ <script>
+ let access_initial_navigation_to_about_blank_throws = false;
+ let access_first_navigation_to_about_blank_throws = false;
+ let access_after_delay_initial_navigation_to_about_blank_throws = false;
+ let access_after_delay_first_navigation_to_about_blank_throws = false;
+ const initial_about_blank_window =
+ window.open('/common/blank.html?pipe=status(204)');
+ try {
+ initial_about_blank_window.origin;
+ } catch(e) {
+ access_initial_navigation_to_about_blank_throws = true;
+ }
+ const renavigated_about_blank_window = window.open('about:blank');
+ try {
+ renavigated_about_blank_window.origin;
+ } catch(e) {
+ access_first_navigation_to_about_blank_throws = true;
+ }
+ setTimeout(() => {
+ try {
+ initial_about_blank_window.origin;
+ } catch(e) {
+ access_after_delay_initial_navigation_to_about_blank_throws = true;
+ }
+ try {
+ renavigated_about_blank_window.origin;
+ } catch(e) {
+ access_after_delay_first_navigation_to_about_blank_throws = true;
+ }
+ top.postMessage({
+ 'access_initial_navigation_to_about_blank_throws':
+ access_initial_navigation_to_about_blank_throws,
+ 'access_first_navigation_to_about_blank_throws':
+ access_first_navigation_to_about_blank_throws,
+ 'access_after_delay_initial_navigation_to_about_blank_throws':
+ access_after_delay_initial_navigation_to_about_blank_throws,
+ 'access_after_delay_first_navigation_to_about_blank_throws':
+ access_after_delay_first_navigation_to_about_blank_throws
+ }, '*');
+ }, 500);
+ </script>"
+>
+</iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html
new file mode 100644
index 0000000000..2c6f0bd6a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html
@@ -0,0 +1,100 @@
+<!--
+Content-Security-Policy: sandbox allow-scripts
+ allow-popups
+ allow-popups-to-escape-sandbox
+-->
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+
+<script>
+
+// Sandbox flags are inherited from a document toward every frame it creates,
+// which then is inherited to every new document created in this frame.
+
+// Using the flag 'allow-popups-to-escape-sandbox' inhibits this inheritance
+// mechanism when the new frame is a popup.
+//
+// Sandbox flags can also be set via CSP. CSP are inherited from a document
+// toward every other documents its creates that are loading with a local scheme.
+// In particular, this includes:
+// - The initial empty document
+// - The first about:blank navigation. See (note)
+// - Any about:blank navigation.
+//
+// Both mechanism are at play here.
+//
+// Note: As of 2021, Chrome handles the very first navigation to about:blank in
+// a frame synchronously instead of asynchronously. This is the only navigation
+// behaving this way. As a result, inheritance of sandbox is different and needs
+// to be tested separately.
+// See also:
+// https://docs.google.com/document/d/1KY0DCaoKjUPbOX28N9KWvBjbnAfQEIRTaLbZUq9EkK8
+
+test(test => {
+ assert_equals(window.origin, 'null');
+}, "Document is sandboxed via its CSP.");
+
+promise_test(async test => {
+ // The navigation will be canceled (204 no content). As a result, the
+ // document in the popup must still be the initial empty document.
+ const w = window.open("/common/blank.html?pipe=status(204)");
+
+ // The initial empty document is sandboxed, because it inherited CSP from
+ // its opener. However this is impossible to verify. There are cross-origin
+ // access restrictions and an about:blank document can't do much on its own.
+ // We try to identify that the document is sandboxed by accessing a
+ // cross-origin restricted API.
+ assert_throws_dom(
+ "SecurityError", () => { w.origin },
+ "Access before timeout throws");
+
+ // Test after a 500ms timeout, delay after which we expect asynchronous
+ // navigations to be canceled.
+ await new Promise(r => setTimeout(r, 500) );
+
+ // The about:blank must still be sandboxed.
+ assert_throws_dom(
+ "SecurityError", () => { w.origin },
+ "Access after timeout throws");
+}, "The initial empty document inherit sandbox via CSP.");
+
+// Regression test for https://crbug.com/1190065
+promise_test(async test => {
+ const w = window.open("about:blank");
+
+ // The about:blank document is sandboxed, because it inherited CSP from its
+ // opener. However this is impossible to verify. There are cross-origin
+ // access restrictions and an about:blank document can't do much on its own.
+ // We try to identify that the document is sandboxed by accessing a
+ // cross-origin restricted API.
+ assert_throws_dom(
+ "SecurityError", () => { w.origin },
+ "Access before timeout throws");
+
+ // Test after a 500ms timeout, delay after which we expect asynchronous
+ // about:blank navigation to be completed.
+ await new Promise(r => setTimeout(r, 500) );
+
+ // The about:blank must still be sandboxed.
+ assert_throws_dom(
+ "SecurityError", () => { w.origin },
+ "Access after timeout throws");
+}, "The synchronous re-navigation to about:blank inherits sandbox via CSP");
+
+async_test(test => {
+ window.addEventListener("message", test.step_func_done(e => {
+ assert_equals(e.data.origin, (new URL(location)).origin,
+ "popup is not sandboxed");
+ }));
+ window.open("./resources/post-origin-to-opener.html");
+}, "Popup do not inherit sandbox, because of 'allow-popups-to-escape-sandbox'" +
+ " the document doesn't inherit CSP. The document isn't sandboxed")
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html.headers b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html.headers
new file mode 100644
index 0000000000..9850d21f3c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-inherit-to-blank-document-unsandboxed.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts allow-popups allow-popups-to-escape-sandbox
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-toggle-in-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-toggle-in-inactive-document-crash.html
new file mode 100644
index 0000000000..654542f6a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-toggle-in-inactive-document-crash.html
@@ -0,0 +1,9 @@
+<body>
+<iframe id="i"></iframe>
+<script>
+var saved_i = i;
+var saved_i_doc = i.contentDocument;
+i.remove();
+saved_i_doc.adoptNode(saved_i);
+saved_i.sandbox.toggle("1");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-special-cases.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-special-cases.tentative.sub.window.js
new file mode 100644
index 0000000000..8681411dd7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-special-cases.tentative.sub.window.js
@@ -0,0 +1,47 @@
+// META: title=Top-level navigation tests with cross origin & user activated child frames
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/sandbox-top-navigation-helper.js
+
+'use strict';
+
+/* ------------------------- USER ACTIVATION TESTS ------------------------- */
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "allow-top-navigation-by-user-activation", "");
+ await activate(iframe_1);
+
+ await attemptTopNavigation(iframe_1, true);
+}, "Allow top with user activation + user activation");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "allow-top-navigation-by-user-activation", "");
+
+ await attemptTopNavigation(iframe_1, false);
+}, "allow-top-navigation-by-user-activation set but no sticky activation");
+
+/* ---------------------- CROSS ORIGIN (A -> B) TESTS ---------------------- */
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_REMOTE_ORIGIN", "allow-top-navigation", "");
+
+ await attemptTopNavigation(iframe_1, true);
+}, "A cross-origin frame with frame sandbox flags can navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_REMOTE_ORIGIN", "", "allow-top-navigation");
+
+ await attemptTopNavigation(iframe_1, false);
+}, "A cross-origin frame with delivered sandbox flags can not navigate top");
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.js
new file mode 100644
index 0000000000..53faa99a40
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.js
@@ -0,0 +1,56 @@
+// META: title=Top-level navigation tests with child frames
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/sandbox-top-navigation-helper.js
+
+'use strict';
+
+/* ----------------------- SAME ORIGIN (A -> A) TESTS ----------------------- */
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "", "allow-top-navigation allow-same-origin");
+
+ await attemptTopNavigation(iframe_1, true);
+}, "A same-origin frame with delivered sandbox flags can navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "allow-top-navigation allow-same-origin", "");
+
+ await attemptTopNavigation(iframe_1, true);
+}, "A same-origin frame with frame sandbox flags can navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "", "");
+
+ await attemptTopNavigation(iframe_1, true);
+}, "A same-origin unsandboxed frame can navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "",
+ "allow-top-navigation allow-top-navigation-by-user-activation allow-same-origin");
+
+ await attemptTopNavigation(iframe_1, true);
+}, "A frame with both top navigation delivered sandbox flags uses the less \
+ restrictive one");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN",
+ "allow-top-navigation allow-top-navigation-by-user-activation", "");
+
+ await attemptTopNavigation(iframe_1, true);
+}, "A frame with both top navigation frame sandbox flags uses the less \
+ restrictive one");
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-escalate-privileges.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-escalate-privileges.tentative.sub.window.js
new file mode 100644
index 0000000000..a5cda9b0b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-escalate-privileges.tentative.sub.window.js
@@ -0,0 +1,63 @@
+// META: title=Top-level navigation tests with frames that try to give themselves top-nav permission
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/sandbox-top-navigation-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_REMOTE_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_REMOTE_ORIGIN", "allow-top-navigation", "");
+
+ await attemptTopNavigation(iframe_2, false);
+}, "A cross origin unsandboxed frame can't escalate privileges in a child \
+ frame");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_REMOTE_ORIGIN", "allow-top-navigation", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "OTHER_ORIGIN", "", "");
+
+ await attemptTopNavigation(iframe_2, true);
+}, "An unsandboxed grandchild inherits its parents ability to navigate top.");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_ORIGIN", "allow-top-navigation", "");
+
+ await attemptTopNavigation(iframe_2, true);
+}, "A same-origin grandchild with frame allow-top can navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_ORIGIN", "", "allow-top-navigation");
+
+ await attemptTopNavigation(iframe_2, false);
+}, "A sandboxed same-origin grandchild without allow-same-origin can't \
+ escalate its own top-nav privileges");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_ORIGIN", "", "allow-same-origin allow-top-navigation");
+
+ await attemptTopNavigation(iframe_2, true);
+}, "A sandboxed same-origin grandchild with allow-same-origin can \
+ give itself top-nav privileges");
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.js
new file mode 100644
index 0000000000..a07148f802
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.js
@@ -0,0 +1,50 @@
+// META: title=Top-level navigation tests with grandchild frames
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=./resources/sandbox-top-navigation-helper.js
+
+'use strict';
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_ORIGIN", "allow-scripts", "");
+
+ await attemptTopNavigation(iframe_2, false);
+}, "A fully sandboxed same-origin grandchild can't navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_ORIGIN", "", "");
+
+ await attemptTopNavigation(iframe_2, true);
+}, "An unsandboxed same-origin grandchild can navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_REMOTE_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_ORIGIN", "", "");
+
+ await attemptTopNavigation(iframe_2, true);
+}, "A same-origin grandchild in a cross-origin parent can navigate top");
+
+promise_test(async t => {
+ const main = await setupTest();
+ const iframe_1 = await createNestedIframe(main,
+ "HTTP_REMOTE_ORIGIN", "", "");
+ const iframe_2 = await createNestedIframe(iframe_1,
+ "HTTP_ORIGIN", "allow-top-navigation allow-same-origin", "");
+
+ await attemptTopNavigation(iframe_2, true);
+}, "A same-origin sandboxed grandchild in a cross-origin parent can navigate top"); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_001.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_001.htm
new file mode 100644
index 0000000000..97af61163a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_001.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow script execution inside iframe with sandbox attribute when sandbox="allow-scripts".</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-scripts-browsing-context-flag" />
+ <meta name="assert" content="Allow script execution inside iframe with sandbox attribute when sandbox='allow-scripts'." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Allow script execution inside iframe with sandbox attribute when sandbox='allow-scripts'.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "script ran");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 8000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <iframe src="support/iframe_sandbox_001.htm" sandbox="allow-scripts" style="display: none"></iframe>
+ <div id=log></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_002.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_002.htm
new file mode 100644
index 0000000000..4291674275
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_002.htm
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow autoplay for HTML5 Video inside iframe with sandbox attribute if sandbox='allow-scripts'.</title>
+ <meta name=timeout content=long>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script>
+ async_test(function (t) {
+ var callback = t.step_func_done(function(event) {
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "play event fired");
+ });
+
+ window.addEventListener("message", callback, false);
+ }, "Allow autoplay for HTML5 Video inside iframe with sandbox attribute if sandbox='allow-scripts'.");
+ </script>
+ <iframe src="support/iframe_sandbox_002.htm" sandbox="allow-scripts" style="display: none"></iframe>
+ <div id=log></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_003-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_003-manual.htm
new file mode 100644
index 0000000000..6363900bed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_003-manual.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block autofocus on form control inside iframe with sandbox attribute.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-automatic-features-browsing-context-flag" />
+ <meta name="assert" content="Block autofocus on form control inside iframe with sandbox attribute." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Block autofocus on form controls inside iframe with sandbox attribute.</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>Test passes if caret (text cursor) is not on the textbox in the below iframe.</td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <pre>iframe with sandbox</pre>
+ <iframe src="support/iframe_sandbox_003.htm" sandbox style="height: 100px; width: 400px;"></iframe>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_004.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_004.htm
new file mode 100644
index 0000000000..08b32f0158
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_004.htm
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Sandbox: Block plugins inside iframe with sandbox attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ https://github.com/whatwg/html/issues/3958
+ https://github.com/whatwg/html/pull/6946
+-->
+
+<iframe sandbox="allow-same-origin" src="support/iframe_sandbox_004.htm" height="400" width ="600"></iframe>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ test(() => {
+ const object = document.querySelector("iframe").contentWindow.document.querySelector("object");
+ const rect = object.getBoundingClientRect();
+ assert_less_than(rect.width, 300);
+ assert_less_than(rect.height, 300);
+ }, "Fallback content is always displayed for sandboxed PDFs");
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_005.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_005.htm
new file mode 100644
index 0000000000..7b1e60e7e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_005.htm
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block script execution inside iframe with sandbox attribute.</title>
+ <meta name="timeout" content="long">
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-scripts-browsing-context-flag" />
+ <meta name="assert" content="Block script execution inside iframe with sandbox attribute." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Block script execution inside iframe with sandbox attribute.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_true(!event);
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <iframe src="support/iframe_sandbox_001.htm" sandbox style="display: none"></iframe>
+ <div id=log></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_006-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_006-manual.htm
new file mode 100644
index 0000000000..1935f439f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_006-manual.htm
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow form submission inside sandbox iframe when sandbox='allow-forms'</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-forms-browsing-context-flag" />
+ <meta name="assert" content="Allow form submission inside sandbox iframe when sandbox='allow-forms'." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Allow form submission inside iframe with sandbox attribute if sandbox='allow-forms'.</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>
+ <div>Steps:</div>
+ <div>1. Click button "Submit Form".</div>
+ <br />
+ <div>Test passes if there is no red on the page and if the word "PASS" appears in the below iframe after following the above steps.</div>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <pre>iframe with sandbox="allow-forms"</pre>
+ <iframe src="support/iframe_sandbox_006.htm" sandbox="allow-forms" style="height: 100px; width: 300px;"></iframe>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_007-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_007-manual.htm
new file mode 100644
index 0000000000..dfed9a6f0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_007-manual.htm
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block form submission inside sandbox iframe</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-forms-browsing-context-flag" />
+ <meta name="assert" content="Block form submission inside sandbox iframe." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Block form submission inside iframe with sandbox attribute.</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>
+ <div>Steps:</div>
+ <div>1. Click button "Submit Form".</div>
+ <br />
+ <div>Test passes if there is no red on the page and there is no navigation in the below iframe after following the above steps.</div>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <pre>iframe with sandbox="allow-scripts allow-same-origin allow-top-navigation"</pre>
+ <iframe src="support/iframe_sandbox_007.htm" sandbox="allow-scripts allow-same-origin allow-top-navigation" style="height: 100px; width: 300px;"></iframe>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_008-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_008-manual.htm
new file mode 100644
index 0000000000..9e479a7899
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_008-manual.htm
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow sandboxed iframe content to navigate the sandboxed browsing context itself.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-navigation-browsing-context-flag" />
+ <meta name="assert" content="Allow sandboxed iframe content to navigate the sandboxed browsing context itself." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Allow sandboxed iframe content to navigate the sandboxed browsing context itself.</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>
+ <div>Steps:</div>
+ <div>1. Click link "Click here to perform self navigation".</div>
+ <br />
+ <div>Test passes if there is no red on the page and the word "PASS" appears in the below iframe after following the above steps.</div>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <pre>iframe with sandbox=""</pre>
+ <iframe id="iframe1" name="iframe1" src="support/iframe_sandbox_008.htm" sandbox="" style="height: 100px; width: 350px;"></iframe>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_010-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_010-manual.htm
new file mode 100644
index 0000000000..41802be775
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_010-manual.htm
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block window.open() API inside iframe with sandbox attribute.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-navigation-browsing-context-flag" />
+ <meta name="assert" content="Block window.open() API inside iframe with sandbox attribute." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Block window.open() API inside iframe with sandbox attribute.</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>
+ <div>Steps:</div>
+ <div>1. Click button "Click here to call window.open() API".</div>
+ <br />
+ <div>Test passes if there is no red on the page and no new window opens. The user agent may offer the user the option of allowing a new window to open.</div>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <pre>iframe with sandbox="allow-scripts allow-same-origin allow-forms allow-top-navigation"</pre>
+ <iframe src="support/iframe_sandbox_010.htm" sandbox="allow-scripts allow-same-origin allow-forms allow-top-navigation" style="height: 100px; width: 450px;"></iframe>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_011.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_011.htm
new file mode 100644
index 0000000000..ce3ee1a7d0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_011.htm
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: iframe sandbox attribute value support DOMTokenList interface.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#the-iframe-element" />
+ <meta name="assert" content="iframe sandbox attribute value support DOMTokenList interface." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id=log></div>
+ <iframe id="iframe1" src="about:blank" sandbox="allow-scripts allow-same-origin allow-forms" style="display : none"></iframe>
+ <script type="text/javascript">
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ assert_equals(iframeEle.sandbox.length, 3)
+ }, "DOMTokenList length")
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ assert_equals(iframeEle.sandbox.item(1), "allow-same-origin")
+ }, "DOMTokenList item(index)")
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ assert_true(iframeEle.sandbox.contains("allow-forms"))
+ }, "DOMTokenList contains(DomString)")
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ iframeEle.sandbox.add("ALLOW-SANDBOX");
+ assert_true(iframeEle.sandbox.contains("ALLOW-SANDBOX"))
+ }, "DOMTokenList add(DomString)")
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ iframeEle.sandbox.remove("ALLOW-SANDBOX");
+ assert_false(iframeEle.sandbox.contains("ALLOW-SANDBOX"))
+ }, "DOMTokenList remove(DomString)")
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ iframeEle.sandbox.remove("ALLOW-SANDBOX");
+ assert_true(
+ iframeEle.sandbox.toggle("allow-top-navigation") && iframeEle.sandbox.contains("allow-top-navigation") &&
+ !iframeEle.sandbox.toggle("allow-top-navigation") && !iframeEle.sandbox.contains("allow-top-navigation")
+ )
+ }, "DOMTokenList toggle(DomString) - Returns true if token is now present (it was added); returns false if it is not (it was removed).")
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ assert_equals(iframeEle.sandbox.value, iframeEle.sandbox.toString())
+ }, "DOMTokenList sandbox.toString()")
+
+ test(function() {
+ var iframeEle = document.getElementById("iframe1");
+ iframeEle.sandbox.remove("ALLOW-SANDBOX");
+ assert_true(iframeEle.sandbox.contains("allow-scripts") != iframeEle.sandbox.contains("Allow-SCRIPTS"))
+ }, "DOMTokenList case sensitivity")
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_012.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_012.htm
new file mode 100644
index 0000000000..7642162d5a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_012.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox=" Allow-Scripts Allow-Same-Origin "></iframe>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_013.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_013.htm
new file mode 100644
index 0000000000..c434989706
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_013.htm
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox="
+ allow-scripts
+ allow-same-origin
+ "></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_014.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_014.htm
new file mode 100644
index 0000000000..d979c91977
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_014.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox=" allow-scripts allow-same-origin "></iframe>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_015.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_015.htm
new file mode 100644
index 0000000000..6fd5405c73
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_015.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox="&#32ALLOW-SCRIPTS&#32allow-same-origin&#32"></iframe>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_016.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_016.htm
new file mode 100644
index 0000000000..4c5f043d2b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_016.htm
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function() {
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox="&#13ALLOW-SCRIPTS&#13allow-same-origin&#13"></iframe>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_017.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_017.htm
new file mode 100644
index 0000000000..f46f22e15b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_017.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox="&#12ALLOW-SCRIPTS&#12allow-same-origin&#12"></iframe>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_018.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_018.htm
new file mode 100644
index 0000000000..b5e5927ce4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_018.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox="&#10ALLOW-SCRIPTS&#10allow-same-origin&#10"></iframe>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_019.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_019.htm
new file mode 100644
index 0000000000..a503531f81
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_019.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: value of sandbox attribute must be an unordered set of unique space-separated tokens.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="value of sandbox attribute must be an unordered set of unique space-separated tokens." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("value of sandbox attribute must be an unordered set of unique space-separated tokens.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+
+ <iframe style="display:none" src="support/iframe_sandbox_012.htm" sandbox="&#9ALLOW-SCRIPTS&#9allow-same-origin&#9"></iframe>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_020-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_020-manual.htm
new file mode 100644
index 0000000000..ed46b3b298
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_020-manual.htm
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Nested iframes cannot have less sandbox restrictions than their most restrictive ancestor iframe.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="Nested iframes cannot have less sandbox restrictions than their most restrictive ancestor iframe." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Nested iframes cannot have less sandbox restrictions than their most restrictive ancestor iframe.</pre>
+ <div>This test is to verify script is blocked inside nested iframes if the top-most sandbox iframe has no 'allow-scripts' token.</div>
+ <br />
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>Test passes if there is no red on the page.</td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <div style="font-weight:bold">Top-most iframe with sandbox=""</div>
+ <iframe id="iframe1" name="iframe1" src="support/iframe_sandbox_020.htm" sandbox="" style="height: 330px; width: 400px;"></iframe>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_021-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_021-manual.htm
new file mode 100644
index 0000000000..0d149d3974
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_021-manual.htm
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Nested iframes cannot have less sandbox restrictions than their most restrictive ancestor iframe.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#attr-iframe-sandbox" />
+ <meta name="assert" content="Nested iframes cannot have less sandbox restrictions than their most restrictive ancestor iframe." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Nested iframes cannot have less sandbox restrictions than their most restrictive ancestor iframe.</pre>
+ <div>This test is to verify script is allowed inside nested iframes if any of the conditions below are true</div>
+ <div>1. both parent sandbox and child sandbox have 'allow-scripts' token.</div>
+ <div>2. parent sandbox has 'allow-scripts' token and nested child iframe has no sandbox attribute.</div>
+ <div>3. parent iframe has no sandbox attribute and child iframe has sandbox='allow-scripts' token.</div>
+ <div>4. both parent and child iframes have no sandbox attribute.</div>
+ <br />
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>Test passes if there is no red on the page.</td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <div style="float: left; border: 1px solid; padding: 5px;">
+ <div style="font-weight: bold">Top-most iframe with sandbox="allow-scripts"</div>
+ <iframe id="iframe1" src="support/iframe_sandbox_021.htm" sandbox="allow-scripts" style="height: 330px; width: 400px;"></iframe>
+ </div>
+ <div style="float: left; border: 1px solid; padding: 5px; margin-left: 20px;">
+ <div style="font-weight: bold">Top-most iframe without sandbox attribute</div>
+ <iframe id="iframe2" src="support/iframe_sandbox_021.htm" style="height: 330px; width: 400px;"></iframe>
+ </div>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_022-manual.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_022-manual.htm
new file mode 100644
index 0000000000..ebfca06a61
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_022-manual.htm
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: allow sandbox iframe to navigate their top-level browsing context if sandbox="allow-top-navigation".</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-scripts-browsing-context-flag" />
+ <meta name="assert" content="Allow sandbox iframe to navigate their top-level browsing context if sandbox='allow-top-navigation'." />
+ <script src="support/sandbox_helper.js" type="text/javascript"></script>
+</head>
+<body>
+ <pre>Description: Allow sandbox iframe to navigate its top-level browsing context if sandbox='allow-top-navigation'.</pre>
+ <table id='testtable' border='1'>
+ <tr>
+ <td>Test Result</td>
+ <td>Test Assertion</td>
+ </tr>
+ <tr>
+ <td id='test_0_result'>Manual</td>
+ <td id='test_0_assertion'>
+ <div>Steps:</div>
+ <div>1. Click link "Open the link in top window".</div>
+ <br />
+ <div>Test passes if there is no red on the page and no top-level navigation after following the above steps.</div>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <div id="testframe">
+ <pre>iframe with sandbox="allow-top-navigation"</pre>
+ <iframe src="support/iframe_sandbox_022.htm" sandbox="allow-top-navigation" style="height: 100px; width: 450px;"></iframe>
+ </div>
+ <script type="text/javascript">
+ DisableTestForNonSupportingBrowsers();
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_023.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_023.htm
new file mode 100644
index 0000000000..78cf35d2bb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_023.htm
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow sandbox iframe to access other content from the same origin if sandbox="allow-same-origin".</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content=" Allow sandbox iframe to access other content from the same origin if sandbox='allow-same-origin'." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Allow sandbox iframe to access other content from the same origin if sandbox='allow-same-origin'");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "window.parent.document");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <iframe src="support/iframe_sandbox_023.htm" sandbox="allow-scripts allow-same-origin" style="display:none"></iframe>
+ <div id=log></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_024.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_024.htm
new file mode 100644
index 0000000000..530162e5b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_024.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: document.cookie access is allowed inside iframe with sandbox="allow-same-origin".</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="document.cookie access is allowed inside iframe with sandbox='allow-same-origin'." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("document.cookie access is allowed inside iframe with sandbox='allow-same-origin'.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+ <iframe src="support/iframe_sandbox_024.htm" sandbox="allow-scripts allow-same-origin" style="display:none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_025.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_025.htm
new file mode 100644
index 0000000000..96783062bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_025.htm
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow parent content to access sandbox child iframe content when sandbox='allow-same-origin</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Allow parent content to access sandbox child iframe content when sandbox='allow-same-origin'" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Allow parent content to access sandbox child iframe content when sandbox='allow-same-origin'");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(document.getElementById('sandboxIframe').contentDocument.title, "Page with a message");
+ });
+ t.done();
+ }
+ </script>
+ <div id=log></div>
+
+ <iframe id='sandboxIframe' src="support/standalone-iframe-content.htm" sandbox="allow-same-origin" onload="callback()" style="display : none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_026.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_026.htm
new file mode 100644
index 0000000000..694a43dad3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_026.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow localStorage and sessionStorage access inside iframe with sandbox='allow-same-origin allow-scripts'.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Allow localStorage and sessionStorage access inside iframe with sandbox='allow-same-origin allow-scripts'." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Allow localStorage and sessionStorage access inside iframe with sandbox='allow-same-origin allow-scripts'");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "access to window.localStorage and window.sessionStorage");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+ <iframe src="support/iframe_sandbox_026.htm" sandbox="allow-scripts allow-same-origin" style="display : none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_027.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_027.htm
new file mode 100644
index 0000000000..aa28505c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_027.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Allow XMLHttpRequest inside iframe with the sandbox attribute if sandbox='allow-same-origin'.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Allow XMLHttpRequest in an iframe with the sandbox attribute if sandbox='allow-same-origin'." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Allow XMLHttpRequest in an iframe with the sandbox attribute if sandbox='allow-same-origin'.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "access to window.XMLHttpRequest");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+ <iframe src="support/iframe_sandbox_027.htm" sandbox="allow-scripts allow-same-origin" style="display : none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_028.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_028.htm
new file mode 100644
index 0000000000..76afcdbe3c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_028.htm
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block sandbox iframe from accessing other content from the same origin.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Block sandbox iframe from accessing other content from the same origin." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Block sandbox iframe from accessing other content from the same origin.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "!window.parent.document");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <iframe src="support/iframe_sandbox_028.htm" sandbox="allow-scripts" style="display:none"></iframe>
+ <div id=log></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_029.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_029.htm
new file mode 100644
index 0000000000..d458e04cd3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_029.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block document.cookie inside iframe with the sandbox attribute.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Block document.cookie inside iframe with the sandbox attribute." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Block document.cookie inside iframe with the sandbox attribute.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "cookies are not R/W");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+ <iframe src="support/iframe_sandbox_029.htm" sandbox="allow-scripts" style="display:none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_030.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_030.htm
new file mode 100644
index 0000000000..d6bb6cc7ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_030.htm
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block parent content to access sandbox child iframe content when sandbox attribute exists</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Block parent content to access sandbox child iframe content when sandbox attribute exists" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Block parent content to access sandbox child iframe content when sandbox attribute exists");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ try { document.getElementById('sandboxIframe').contentDocument.title; assert_true(false);}
+ catch(e) {assert_true(true);}
+ });
+ t.done();
+ }
+ </script>
+ <div id=log></div>
+
+ <iframe id='sandboxIframe' src="support/standalone-iframe-content.htm" sandbox onload="callback()" style="display : none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_031.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_031.htm
new file mode 100644
index 0000000000..3c65d416c5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_031.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block localStorage and sessionStorage inside iframe with the sandbox attribute.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Block localStorage and sessionStorage inside iframe with the sandbox attribute." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Block localStorage and sessionStorage inside iframe with the sandbox attribute.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "no access to window.localStorage and window.sessionStorage");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+ <iframe src="support/iframe_sandbox_031.htm" sandbox="allow-scripts" style="display : none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_032.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_032.htm
new file mode 100644
index 0000000000..4e6293949a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox_032.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Sandbox: Block XMLHttpRequest in an iframe with the sandbox attribute.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="http://dev.w3.org/html5/spec/Overview.html#sandboxed-origin-browsing-context-flag" />
+ <meta name="assert" content="Block XMLHttpRequest inside sandbox iframe." />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script type="text/javascript">
+
+ var t = async_test("Block XMLHttpRequest in an iframe with the sandbox attribute.");
+
+ function callback(event)
+ {
+ t.step(function(){
+ assert_true('sandbox' in document.createElement('iframe'));
+ assert_equals(event.data, "no access to window.XMLHttpRequest");
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(callback, 4000);
+ window.addEventListener("message", callback, false);
+ </script>
+ <div id=log></div>
+ <iframe src="support/iframe_sandbox_032.htm" sandbox="allow-scripts" style="display : none"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor.html
new file mode 100644
index 0000000000..2f77dfe164
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor.html
@@ -0,0 +1,138 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Navigation should not occur when `src` matches the location of a anscestor browsing context</title>
+<script>
+// Avoid recursion in non-conforming browsers
+if (parent !== window && parent.title == window.title) {
+ window.stop();
+}
+</script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+/**
+ * This test uses the `beforeunload` event to detect navigation. Because that
+ * event is fired synchronously in response to "process the iframe attributes",
+ * a second "control" iframe may be used to verify cases where navigation
+ * should *not* occur. `Promise.race` ensures that tests complete as soon as
+ * possible.
+ *
+ * Although the specification dictates that the `beforeunload` event must be
+ * emitted synchronously during navigation, a number of user agents do not
+ * adhere to this requirement. WPT includes a dedicated test for synchronous
+ * emission of the event [1]. This test is authored to support non-standard
+ * behavior in order to avoid spuriously passing in those UAs.
+ *
+ * [1] https://github.com/web-platform-tests/wpt/pull/12343
+ */
+'use strict';
+
+function when(target, eventName) {
+ return new Promise(function(resolve, reject) {
+ target.addEventListener(eventName, function() {
+ resolve();
+ }, { once: true });
+ target.addEventListener('error', function() {
+ reject(new Error('Error while waiting for ' + eventName));
+ }, { once: true });
+ });
+}
+
+function init(doc, t) {
+ var iframes = [doc.createElement('iframe'), doc.createElement('iframe')];
+
+ iframes.forEach(function(iframe) {
+ iframe.src = '/common/blank.html';
+ doc.body.appendChild(iframe);
+
+ t.add_cleanup(function() {
+ iframe.parentNode.removeChild(iframe);
+ });
+ });
+
+ return Promise.all([when(iframes[0], 'load'), when(iframes[1], 'load')])
+ .then(function() { return iframes; });
+}
+
+// This test verifies that navigation does occur; it is intended to validate
+// the utility functions.
+promise_test(function(t) {
+ return init(document, t)
+ .then(function(iframes) {
+ var subjectNavigation = when(iframes[0].contentWindow, 'beforeunload');
+ var controlNavigation = when(iframes[1].contentWindow, 'beforeunload');
+
+ iframes[0].src = '/common/blank.html?2';
+ iframes[1].src = '/common/blank.html?3';
+
+ return Promise.all([subjectNavigation, controlNavigation]);
+ });
+}, 'different path name');
+
+promise_test(function(t) {
+ return init(document, t)
+ .then(function(iframes) {
+ var subjectNavigation = when(iframes[0].contentWindow, 'beforeunload');
+ var controlNavigation = when(iframes[1].contentWindow, 'beforeunload');
+
+ iframes[0].src = location.href;
+ iframes[1].src = '/common/blank.html?4';
+
+ return Promise.race([
+ subjectNavigation.then(function() {
+ assert_unreached('Should not navigate');
+ }),
+ controlNavigation
+ ]);
+ });
+}, 'same path name, no document fragment');
+
+promise_test(function(t) {
+ return init(document, t)
+ .then(function(iframes) {
+ var subjectNavigation = when(iframes[0].contentWindow, 'beforeunload');
+ var controlNavigation = when(iframes[1].contentWindow, 'beforeunload');
+
+ iframes[0].src = location.href + '#something-else';
+ iframes[1].src = '/common/blank.html?5';
+
+ return Promise.race([
+ subjectNavigation.then(function() {
+ assert_unreached('Should not navigate');
+ }),
+ controlNavigation
+ ]);
+ });
+}, 'same path name, different document fragment');
+
+promise_test(function(t) {
+ var intermediate = document.createElement('iframe');
+
+ document.body.appendChild(intermediate);
+
+ t.add_cleanup(function() {
+ intermediate.parentNode.removeChild(intermediate);
+ });
+ intermediate.contentDocument.open();
+ intermediate.contentDocument.write('<body></body>');
+ intermediate.contentDocument.close();
+
+ return init(intermediate.contentDocument, t)
+ .then(function(iframes) {
+ var subjectNavigation = when(iframes[0].contentWindow, 'beforeunload');
+ var controlNavigation = when(iframes[1].contentWindow, 'beforeunload');
+
+ iframes[0].src = location.href;
+ iframes[1].src = '/common/blank.html?6';
+
+ return Promise.race([
+ subjectNavigation.then(function() {
+ assert_unreached('Should not navigate');
+ }),
+ controlNavigation
+ ]);
+ });
+}, 'same path name, no document fragement (intermediary browsing context)');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-anchor.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-anchor.html
new file mode 100644
index 0000000000..cf26c28f08
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-anchor.html
@@ -0,0 +1,17 @@
+<title>Verify srcdoc content loads when src is about:srcdoc#foo.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id=myframe srcdoc='srcdoc_text' src='about:srcdoc#foo'></iframe>
+
+<script>
+ async_test(function(t) {
+ // Verify that the srcdoc content is loaded before we start.
+ window.onload = t.step_func_done(() => {
+ assert_true(typeof myframe.contentDocument !== 'undefined',
+ 'iframe has contentDocument');
+ assert_equals(myframe.contentDocument.body.innerText, 'srcdoc_text',
+ 'iframe contains srcdoc content');
+ }, '');
+ }, 'Verify srcdoc content loads when src is about:srcdoc#foo.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-attribute-reset.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-attribute-reset.html
new file mode 100644
index 0000000000..452a984afb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-attribute-reset.html
@@ -0,0 +1,33 @@
+<title>Verify that clearing srcdoc resets the iframe's content.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes">
+<link rel="author" title="James MacLean" href="mailto:wjmaclean@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id=myframe srcdoc='srcdoc_text'></iframe>
+<script>
+ 'use strict';
+
+ async_test(function(t) {
+ window.onload = () => {
+ // Verify that the srcdoc content is loaded before we start.
+ t.step(() => {
+ assert_true(typeof myframe.contentDocument !== 'undefined',
+ 'iframe has contentDocument');
+ assert_equals(myframe.contentDocument.body.innerText, 'srcdoc_text',
+ 'iframe contains srcdoc content');
+ });
+
+ myframe.onload = t.step_func_done(function() {
+ assert_true(typeof myframe.contentDocument !== 'undefined',
+ 'iframe has contentDocument');
+ assert_equals(myframe.contentDocument.body.innerText, '',
+ 'iframe content is empty');
+ });
+
+ // Don't remove srcdoc until the initial load has completed, and the
+ // frame's onload handler is in place.
+ myframe.removeAttribute('srcdoc');
+ };
+ }, 'Verify that the frame reloads with empty body after we remove srcdoc.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_change_hash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_change_hash.html
new file mode 100644
index 0000000000..fe72333d1a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_change_hash.html
@@ -0,0 +1,68 @@
+<title>same-document navigation inside an srcdoc iframe using location.hash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+ promise_test(async () => {
+ // Wait until 'document' is available.
+ await new Promise(resolve => window.addEventListener('load', resolve));
+
+ // Create an iframe, wait until is is loaded.
+ let iframe = document.createElement('iframe');
+ await new Promise(resolve => {
+ iframe.srcdoc = "srcdoc document";
+ iframe.onload = resolve;
+ document.body.appendChild(iframe);
+ });
+
+ assert_equals(iframe.contentDocument.body.innerText, "srcdoc document");
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc");
+
+ function iframeHashChanged() {
+ return new Promise(resolve => {
+ iframe.contentWindow.onhashchange = resolve;
+ })
+ }
+
+ // 1) hash = "1".
+ {
+ let hash_changed = iframeHashChanged();
+ await test_driver.bless("hash = '1'", () => {
+ iframe.contentWindow.location.hash = "1";
+ });
+ await hash_changed;
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#1");
+ }
+
+ // 2) hash = "2".
+ {
+ let hash_changed = iframeHashChanged();
+ await test_driver.bless("hash = '2'", () => {
+ iframe.contentWindow.location.hash = "2";
+ });
+ await hash_changed;
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#2");
+ }
+
+ // 3) history.back().
+ {
+ let hash_changed = iframeHashChanged();
+ await test_driver.bless("history.back()", () => {
+ history.back();
+ });
+ await hash_changed;
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#1");
+ }
+
+ // 4) history.forward().
+ {
+ let hash_changed = iframeHashChanged();
+ await test_driver.bless("history.forward()", () => {
+ history.forward();
+ });
+ await hash_changed;
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#2");
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_process_attributes.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_process_attributes.html
new file mode 100644
index 0000000000..0bd9f9b229
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc_process_attributes.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Whenever `srcdoc` attribute is set, changed, or removed, the UA must process the &lt;iframe> attributes</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes-2">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+function createIFrameWithBlobSrc() {
+ var iframe = document.createElement("iframe");
+ iframe.src = URL.createObjectURL(new Blob(["src"], {type: "text/html"}));
+ return iframe;
+}
+
+async_test(function(t) {
+ var iframe = createIFrameWithBlobSrc();
+ var isAdded = false;
+ iframe.onload = t.step_func(function() {
+ assert_equals(iframe.contentDocument.location.protocol, "blob:");
+ assert_equals(iframe.contentDocument.body.textContent, "src");
+
+ iframe.onload = t.step_func_done(function() {
+ assert_true(isAdded);
+ assert_equals(iframe.contentDocument.location.href, "about:srcdoc");
+ assert_equals(iframe.contentDocument.body.textContent, "srcdoc");
+ });
+
+ iframe.setAttribute("srcdoc", "srcdoc");
+ isAdded = true;
+ });
+
+ document.body.appendChild(iframe);
+}, "Adding `srcdoc` attribute triggers attributes processing");
+
+async_test(function(t) {
+ var iframe = createIFrameWithBlobSrc();
+ var isChanged = false;
+ iframe.srcdoc = "old";
+ iframe.onload = t.step_func(function() {
+ assert_equals(iframe.contentDocument.location.href, "about:srcdoc");
+ assert_equals(iframe.contentDocument.body.textContent, "old");
+
+ iframe.onload = t.step_func_done(function() {
+ assert_true(isChanged);
+ assert_equals(iframe.contentDocument.location.href, "about:srcdoc");
+ assert_equals(iframe.contentDocument.body.textContent, "new");
+ });
+
+ iframe.srcdoc = "new";
+ isChanged = true;
+ });
+
+ document.body.appendChild(iframe);
+}, "Setting `srcdoc` (via property) triggers attributes processing");
+
+async_test(function(t) {
+ var iframe = createIFrameWithBlobSrc();
+ var isRemoved = false;
+ iframe.srcdoc = "srcdoc";
+ iframe.onload = t.step_func(function() {
+ assert_equals(iframe.contentDocument.location.href, "about:srcdoc");
+ assert_equals(iframe.contentDocument.body.textContent, "srcdoc");
+
+ iframe.onload = t.step_func_done(function() {
+ assert_true(isRemoved);
+ assert_equals(iframe.contentDocument.location.protocol, "blob:");
+ assert_equals(iframe.contentDocument.body.textContent, "src");
+ });
+
+ iframe.removeAttribute("srcdoc");
+ isRemoved = true;
+ });
+
+ document.body.appendChild(iframe);
+}, "Removing `srcdoc` attribute triggers attributes processing");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/stash.py b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/stash.py
new file mode 100644
index 0000000000..2404380881
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/stash.py
@@ -0,0 +1,5 @@
+def main(request, response):
+ if request.method == u'POST':
+ request.server.stash.put(request.GET[b"id"], request.body)
+ return u''
+ return request.server.stash.take(request.GET[b"id"])
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/blank.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/blank.htm
new file mode 100644
index 0000000000..18ecdcb795
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/blank.htm
@@ -0,0 +1 @@
+<html></html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/document-with-embedded-svg.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/document-with-embedded-svg.html
new file mode 100644
index 0000000000..a81d13d9a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/document-with-embedded-svg.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<body>
+<script>
+["embed", "frame", "iframe", "object"].forEach(name => {
+ const frame = document.body.appendChild(document.createElement(name));
+ const attr = name !== "object" ? "src" : "data";
+ frame[attr] = "svg.svg";
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/download_stash.py b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/download_stash.py
new file mode 100644
index 0000000000..11366a9c3f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/download_stash.py
@@ -0,0 +1,32 @@
+import time
+
+def main(request, response):
+ token = request.GET[b"token"]
+ response.status = 200
+ response.headers.append(b"Content-Type", b"text/html")
+
+ # Make sure to disable sniffing because it would read enough bytes to finish
+ # the load, before even giving the client application a chance to cancel it.
+ response.headers.append(b"X-Content-Type-Options", b"nosniff")
+
+ if b"verify-token" in request.GET:
+ if request.server.stash.take(token):
+ return u'TOKEN_SET'
+ return u'TOKEN_NOT_SET'
+
+ if b"finish-delay" not in request.GET:
+ # <a download>
+ request.server.stash.put(token, True)
+ return
+
+ # navigation to download
+ response.headers.append(b"Content-Disposition", b"attachment")
+ response.write_status_headers()
+ finish_delay = float(request.GET[b"finish-delay"]) / 1E3
+ count = 10
+ single_delay = finish_delay / count
+ for i in range(count): # pylint: disable=unused-variable
+ time.sleep(single_delay)
+ if not response.writer.write_content(b"\n"):
+ return
+ request.server.stash.put(token, True)
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-checks-contentDocument.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-checks-contentDocument.html
new file mode 100644
index 0000000000..bc35a977e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-checks-contentDocument.html
@@ -0,0 +1,3 @@
+<script>
+ parent.testContentDocument();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-opens-modals.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-opens-modals.html
new file mode 100644
index 0000000000..50f56c6278
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-opens-modals.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script>
+function openModal(name) {
+ switch (name) {
+ case "alert":
+ return alert("MESSAGE");
+ break;
+ case "confirm":
+ return confirm("MESSAGE?");
+ break;
+ case "prompt":
+ return prompt("MESSAGE:", "DEFAULT VALUE");
+ break;
+ case "print":
+ return print();
+ break;
+ }
+}
+
+onmessage = function(e) {
+ parent.postMessage(openModal(e.data), "*");
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-on-popup.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-on-popup.html
new file mode 100644
index 0000000000..9b9eae8a72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-on-popup.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+window.onload = function() {
+ try {
+ top.location = "iframe-that-send-message-to-the-opener.html";
+ } catch(e) {
+ top.postMessage("cannot navigate", "*");
+ }
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-without-user-gesture-failed.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-without-user-gesture-failed.html
new file mode 100644
index 0000000000..0436d56df9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation-without-user-gesture-failed.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+The top navigation from this iframe should be blocked. This text should appear.
+<script>
+window.onload = function()
+{
+ try {
+ top.location = "navigation-changed-iframe.html";
+ top.postMessage("FAIL", "*");
+ } catch(e) {
+ top.postMessage("PASS", "*");
+ }
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation.html
new file mode 100644
index 0000000000..c855ca3bab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-performs-top-navigation.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+ <script>
+ function performTest()
+ {
+ try {
+ top.location = "navigation-changed-iframe.html";
+ } catch(e) {
+ top.postMessage("BLOCKED", "*");
+ }
+ }
+ </script>
+</head>
+<body onload="performTest();">
+ <p>This doc tried to navigate the top page when loaded, which should fail since it's not trigged by user activation while in a sandboxed frame with 'allow-top-navigtaion-by-user-activation'. <br> <br>
+ Click the button below, the top navigation should succeed with a new page saying "PASSED: Navigation succeeded." in browsers supporting the 'allow-top-navigtaion-by-user-activation' iframe@sandbox keyword (eg., Chrome v58+); Otherwise, the top navigation should fail.
+ </p>
+ <button id="b" onclick="performTest();">Navigate the top page</button>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-send-message-to-the-opener.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-send-message-to-the-opener.html
new file mode 100644
index 0000000000..0dc3b1122a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-send-message-to-the-opener.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+opener.postMessage('can navigate', '*');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-tries-to-navigate-parent-and-sends-result-to-grandparent.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-tries-to-navigate-parent-and-sends-result-to-grandparent.html
new file mode 100644
index 0000000000..4b8930de42
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-that-tries-to-navigate-parent-and-sends-result-to-grandparent.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<p>This is a frame that tries to navigate its parent.</p>
+<script>
+window.onload = function() {
+ try {
+ parent.location.href = "data:text/html,\u003c!DOCTYPE html\u003e\u003cp\u003eIf this message appears, then this frame has been navigated by its child.\u003c/p\u003e\u003cscript\u003eparent.postMessage('can navigate', '*');\u003c/script\u003e";
+ } catch(e) {
+ parent.parent.postMessage("can not navigate", "*");
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-history.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-history.html
new file mode 100644
index 0000000000..c4ba8011f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-history.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<p>This is a frame that tries to navigate via history API.</p>
+<script>
+window.onmessage = (e) => {
+ if (e.data == 'back') {
+ history.back();
+ } else if (e.data == 'forward') {
+ history.forward();
+ } else if (e.data = 'pushstateback') {
+ onpopstate = (e) => {
+ parent.postMessage('pushstatebackdone', '*');
+ };
+
+ history.pushState({someState: 'blah'}, '');
+ history.back();
+ }
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-its-child.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-its-child.html
new file mode 100644
index 0000000000..50edc878ad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-tried-to-be-navigated-by-its-child.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<p>If this message appears, then this frame has not been navigated by its child.</p>
+<iframe src="iframe-that-tries-to-navigate-parent-and-sends-result-to-grandparent.html">
+</iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-its-child.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-its-child.html
new file mode 100644
index 0000000000..9ac754c418
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-its-child.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<iframe src="data:text/html,If this message appears, then this frame has not been navigated by its parent."></iframe>
+<script>
+window.onload = function() {
+ try {
+ document.querySelector("iframe").contentWindow.location.href = "data:text/html,\u003c!DOCTYPE html\u003e\u003cp\u003eIf this message appears, then this frame has been navigated by its parent.\u003c/p\u003e\u003cscript\u003eparent.parent.postMessage('can navigate', '*');\u003c/script\u003e";
+ } catch(e) {
+ parent.postMessage("can not navigate", "*");
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-itself.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-itself.html
new file mode 100644
index 0000000000..6755d295aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-itself.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<p>If this message appears, then this frame has not been navigated.</p>
+<script>
+window.onload = function() {
+ try {
+ location.href = "data:text/html,\u003c!DOCTYPE html\u003e\u003cp\u003eIf this message appears, then this frame has been navigated.\u003c/p\u003e\u003cscript\u003eparent.postMessage('can navigate', '*');\u003c/script\u003e";
+ } catch(e) {
+ parent.postMessage("can not navigate", "*");
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-which-content-height-equals-400px.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-which-content-height-equals-400px.html
new file mode 100644
index 0000000000..bb3dbd3118
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-which-content-height-equals-400px.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body style="width: 200px; height: 400px">
+iframe content
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-with-object.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-with-object.html
new file mode 100644
index 0000000000..c983a9d36d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe-with-object.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Display:none iframe with object tag</title>
+<script>
+ // If this document is loaded as a display:none iframe, forcing an update here
+ // means we do not need a style or layout update after the object is inserted
+ // below because we are not being rendered.
+ document.documentElement.offsetTop;
+</script>
+<object data="data:text/html,frame"></object>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_001.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_001.htm
new file mode 100644
index 0000000000..051ca5ecd7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_001.htm
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with script</title>
+</head>
+<body>
+ <script type="text/javascript">
+ parent.window.postMessage("script ran", "*");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_002.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_002.htm
new file mode 100644
index 0000000000..e637847714
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_002.htm
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 video with autoplay attribute.</title>
+ <script src="/common/media.js"></script>
+</head>
+<body>
+ <script>
+ function do_play(event) {
+ parent.window.postMessage("play event fired", "*");
+ }
+
+ document.write(
+ "<video id='video0' src='" + getVideoURI("/media/green-at-15") + "'" +
+ " autoplay onplay='do_play(event);'>"
+ );
+ </script>
+ Your browser does not support HTML5 video.
+ </video>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_003.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_003.htm
new file mode 100644
index 0000000000..621ece79af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_003.htm
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>autofocus on form control</title>
+</head>
+<body>
+ <div>Below form control has autofocus attribute set.</div><br />
+ <form action="">
+ <span>Textbox: </span><input autofocus="autofocus" type="text" name="movie" />
+ </form>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_004.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_004.htm
new file mode 100644
index 0000000000..661afb0943
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_004.htm
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>object tag</title>
+</head>
+<body>
+ <object type="application/pdf" width="600" height="600" data="sandbox.pdf">
+ Fallback content
+ </object>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_006.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_006.htm
new file mode 100644
index 0000000000..42542ae147
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_006.htm
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Form submission</title>
+</head>
+<body>
+ <form id="form1" action="standalone-pass.htm">
+ <span>Name: </span><input type="text" name="name" value="browser" /><br />
+ <input id="submitButton" type="submit" value="Submit Form" />
+ </form>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_007.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_007.htm
new file mode 100644
index 0000000000..fc01557c75
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_007.htm
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Form submission</title>
+</head>
+<body>
+ <form id="form1" action="standalone-fail.htm">
+ <span>Name: </span><input type="text" name="name" value="browser" /><br />
+ <input id="submitButton" type="submit" value="Submit Form" />
+ </form>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_008.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_008.htm
new file mode 100644
index 0000000000..ebd8279675
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_008.htm
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with hyperlink and target set to self</title>
+</head>
+<body>
+ <a id="hyperlink" href="standalone-pass.htm" target="_self">Click here to perform self navigation</a>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_010.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_010.htm
new file mode 100644
index 0000000000..27fc4209f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_010.htm
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with window.open()</title>
+</head>
+<body>
+ <button type="button" onclick="javascript:window.open('standalone-fail.htm')">Click here to call window.open() API</button>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_012.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_012.htm
new file mode 100644
index 0000000000..b1e8f92fb4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_012.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with access to document.cookie</title>
+</head>
+<body>
+ <script type="text/javascript">
+ cookie = document.cookie;
+ document.cookie = "name=browser";
+ parent.window.postMessage("cookies are R/W", "*");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020.htm
new file mode 100644
index 0000000000..fd0d6bb6fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020.htm
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with iframes</title>
+</head>
+<body>
+ <table cellpadding="5" cellspacing="10">
+ <tr>
+ <td>
+ <span>child iframe with sandbox="allow-scripts" attribute</span><br />
+ <iframe id="Iframe1" src="iframe_sandbox_020a.htm" sandbox="allow-scripts" style="height: 50px; width: 250px;"></iframe>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span>child iframe with sandbox="" attribute</span><br />
+ <iframe id="Iframe2" src="iframe_sandbox_020a.htm" sandbox="" style="height: 50px; width: 250px;"></iframe>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span>child iframe without sandbox attribute</span><br />
+ <iframe id="Iframe3" src="iframe_sandbox_020a.htm" style="height: 50px; width: 250px;"></iframe>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020a.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020a.htm
new file mode 100644
index 0000000000..ccfa4eae2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_020a.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with script</title>
+</head>
+<body>
+ <div>Script Execution: <span id="scriptExecute" style="Color: Green">Blocked</span></div>
+ <script type="text/javascript">
+ document.getElementById("scriptExecute").innerHTML = "Not Blocked";
+ document.getElementById("scriptExecute").style.color = "Red";
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021.htm
new file mode 100644
index 0000000000..63f5892456
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021.htm
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with iframes</title>
+</head>
+<body>
+ <table cellpadding="5" cellspacing="10">
+ <tr>
+ <td>
+ <span>child iframe with sandbox="allow-scripts" attribute</span><br />
+ <iframe id="Iframe1" src="iframe_sandbox_021a.htm" sandbox="allow-scripts" style="height: 50px; width: 250px;"></iframe>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span>child iframe with sandbox="" attribute</span><br />
+ <iframe id="Iframe2" src="iframe_sandbox_020a.htm" sandbox="" style="height: 50px; width: 250px;"></iframe>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <span>child iframe without sandbox attribute</span><br />
+ <iframe id="Iframe3" src="iframe_sandbox_021a.htm" style="height: 50px; width: 250px;"></iframe>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021a.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021a.htm
new file mode 100644
index 0000000000..a42520d7e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_021a.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with script</title>
+</head>
+<body>
+ <div>Script Execution: <span id="scriptExecute" style="Color: Red">Blocked</span></div>
+ <script type="text/javascript">
+ document.getElementById("scriptExecute").innerHTML = "Allowed";
+ document.getElementById("scriptExecute").style.color = "Green";
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_022.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_022.htm
new file mode 100644
index 0000000000..87082e51dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_022.htm
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>target=_top</title>
+</head>
+<body>
+ <div>hyperlink with target=_top</div>
+ <br />
+ <a href="standalone-pass.htm" target="_top">Open the link in top window</a>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_023.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_023.htm
new file mode 100644
index 0000000000..a65db539bb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_023.htm
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head><title>Access parent dom</title>
+</head>
+<body>
+ <script type="text/javascript">
+ if (window.parent.document)
+ {
+ parent.window.postMessage("window.parent.document", "*");
+ }else{
+ parent.window.postMessage("!window.parent.document", "*");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_024.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_024.htm
new file mode 100644
index 0000000000..1b0996e589
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_024.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head><title>Page with access to document.cookie</title>
+</head>
+<body>
+ <div>Cookie Read: <span id="readCookie"></span></div>
+ <script type="text/javascript">
+ cookie = document.cookie;
+ document.cookie = "name=browser";
+ parent.window.postMessage("cookies are R/W", "*");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_026.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_026.htm
new file mode 100644
index 0000000000..7171cf7721
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_026.htm
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head><title>Page with access to localStorage and sessionStorage</title>
+</head>
+<body>
+ <script type="text/javascript">
+ if (window.localStorage && window.sessionStorage) {
+ parent.window.postMessage("access to window.localStorage and window.sessionStorage", "*");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_027.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_027.htm
new file mode 100644
index 0000000000..c1a48cdef9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_027.htm
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head><title>XMLHttpRequest</title>
+</head>
+<body>
+ <script type="text/javascript">
+ xhrRequest = new XMLHttpRequest();
+
+ xhrRequest.onreadystatechange = function () {
+ if (xhrRequest.readyState == 4 && xhrRequest.status == 200) {
+ //xhr successful
+ parent.window.postMessage("access to window.XMLHttpRequest", "*");
+ }
+ }
+
+ xhrRequest.open("GET", "standalone-pass.htm", true);
+ xhrRequest.send();
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_028.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_028.htm
new file mode 100644
index 0000000000..d7ca761441
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_028.htm
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head><title>Access parent dom</title>
+</head>
+<body>
+ <script type="text/javascript">
+ try
+ {
+ if (window.parent.document)
+ {
+ parent.window.postMessage("window.parent.document", "*");
+ }
+ }
+ catch(e)
+ {
+ parent.window.postMessage("!window.parent.document", "*");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_029.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_029.htm
new file mode 100644
index 0000000000..5d5c720bd8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_029.htm
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head><title>Page with access to document.cookie</title>
+</head>
+<body>
+ <div>Cookie Read: <span id="readCookie"></span></div>
+ <script type="text/javascript">
+ try
+ {
+ cookie = document.cookie;
+ document.cookie = "name=browser";
+ parent.window.postMessage("cookies are R/W", "*");
+ }catch(e)
+ {
+ parent.window.postMessage("cookies are not R/W", "*");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_031.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_031.htm
new file mode 100644
index 0000000000..fb987dac38
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_031.htm
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head><title>Page with access to localStorage and sessionStorage</title>
+</head>
+<body>
+ <script type="text/javascript">
+ try
+ {
+ if (window.localStorage && window.sessionStorage) {
+ parent.window.postMessage("access to window.localStorage and window.sessionStorage", "*");
+ }
+ }
+ catch(e)
+ {
+ parent.window.postMessage("no access to window.localStorage and window.sessionStorage", "*");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_032.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_032.htm
new file mode 100644
index 0000000000..6059b7df43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_032.htm
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head><title>XMLHttpRequest</title>
+</head>
+<body>
+ <script type="text/javascript">
+
+ try
+ {
+ xhrRequest = new XMLHttpRequest();
+
+ xhrRequest.onreadystatechange = function () {
+ if (xhrRequest.readyState == 4 && xhrRequest.status == 200) {
+ //xhr successful
+ parent.window.postMessage("access to window.XMLHttpRequest", "*");
+ }
+ }
+
+ xhrRequest.open("GET", "standalone-pass.htm", true);
+ xhrRequest.send();
+
+ }catch(e){}
+
+ parent.window.postMessage("no access to window.XMLHttpRequest", "*");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_block_modals.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_block_modals.js
new file mode 100644
index 0000000000..67733d8101
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_block_modals.js
@@ -0,0 +1,18 @@
+function runTest(modalName, expectedValue) {
+ let timeOutForFailingToOpenModal = 500;
+ let startTime;
+ async_test(t => {
+ let iframe = document.querySelector("iframe");
+ iframe.onload = t.step_func(() => {
+ window.addEventListener("message", t.step_func_done(e => {
+ // This tests work by checking the call to open the modal diaglog will return immediately (or at least within timeOutForFailingToOpenModal).
+ // If the modal dialog is not blocked, then it will wait for user input and the test will time out.
+ assert_less_than(new Date().getTime() - startTime, timeOutForFailingToOpenModal, "Call to open modal dialog did not return immediately");
+ assert_equals(e.data, expectedValue, "Call to open modal dialog did not return expected value");
+ }));
+ startTime = new Date().getTime();
+ iframe.contentWindow.postMessage(modalName, "*");
+ });
+ iframe.src = "support/iframe-that-opens-modals.html";
+ }, "Frames without `allow-modals` should not be able to open modal dialogs");
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_download_helper.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_download_helper.js
new file mode 100644
index 0000000000..7090e7662c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_download_helper.js
@@ -0,0 +1,37 @@
+function StreamDownloadFinishDelay() {
+ return 1000;
+}
+
+function DownloadVerifyDelay() {
+ return 1000;
+}
+
+function VerifyDownload(test_obj, token, timeout, expect_download) {
+ var verify_token = test_obj.step_func(function () {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'support/download_stash.py?verify-token&token=' + token);
+ xhr.onload = test_obj.step_func(function(e) {
+ if (expect_download) {
+ if (xhr.response != "TOKEN_SET") {
+ // Always retry, and rely on the test timeout to conclude that
+ // download didn't happen and to fail the test.
+ test_obj.step_timeout(verify_token, DownloadVerifyDelay());
+ return;
+ }
+ } else {
+ assert_equals(xhr.response, "TOKEN_NOT_SET", "Expect no download to happen, but got one.");
+ }
+ test_obj.done();
+ });
+ xhr.send();
+ });
+ test_obj.step_timeout(verify_token, timeout);
+}
+
+function AssertDownloadSuccess(test_obj, token, timeout) {
+ VerifyDownload(test_obj, token, timeout, true);
+}
+
+function AssertDownloadFailure(test_obj, token, timeout) {
+ VerifyDownload(test_obj, token, timeout, false);
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/load-into-the-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/load-into-the-iframe.html
new file mode 100644
index 0000000000..9e08eb587a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/load-into-the-iframe.html
@@ -0,0 +1,24 @@
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <iframe sandbox="allow-scripts"></iframe>
+ <script>
+ let frame = document.querySelector("iframe");
+ let sandbox = new URL(location.href).searchParams.get("sandbox");
+ if (sandbox) {
+ frame.sandbox = sandbox;
+ }
+ // We're the popup (i.e. a top frame). Load into the iframe the page
+ // trying to modifying the top frame and transmit the result to our
+ // opener.
+ onmessage = function(e) {
+ opener.postMessage(e.data, "*")
+ }
+ frame.src = "iframe-that-performs-top-navigation-on-popup.html";
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/navigation-changed-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/navigation-changed-iframe.html
new file mode 100644
index 0000000000..abe0e78dfe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/navigation-changed-iframe.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+ <script>
+ function fireSentinel()
+ {
+ document.getElementsByTagName('h4')[0].innerHTML = document.domain;
+ }
+ </script>
+</head>
+<body onload="fireSentinel();">
+ <h4>DOMAIN</h4>
+ <p>PASSED: Navigation succeeded.</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdf b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdf
new file mode 100644
index 0000000000..0e16bc8d93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdf
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdf.headers b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdf.headers
new file mode 100644
index 0000000000..5a8e57e482
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox.pdf.headers
@@ -0,0 +1 @@
+Content-Type: application/pdf
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_allow_script.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_allow_script.html
new file mode 100644
index 0000000000..a2d5b73eda
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_allow_script.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: sandbox_allow_scripts</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<div id="test">Before change</div>
+<script>
+ parent.window.postMessage("Script executed", "*");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_helper.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_helper.js
new file mode 100644
index 0000000000..26aa67faf4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/sandbox_helper.js
@@ -0,0 +1,14 @@
+function IsSandboxSupported() {
+ if ('sandbox' in document.createElement('iframe')) {
+ return true;
+ }
+ return false;
+}
+
+function DisableTestForNonSupportingBrowsers() {
+ //check if sandbox is supported by the browser
+ if (!IsSandboxSupported()) {
+ document.getElementById('testframe').innerHTML = "FAIL: Your browser does not support the sandbox attribute on the iframe element.";
+ document.getElementById('testframe').style.color = "Red";
+ }
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-fail.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-fail.htm
new file mode 100644
index 0000000000..29ef4d5abb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-fail.htm
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with FAIL message</title>
+</head>
+<body>
+ <div style="color: Red">FAIL!!!</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-iframe-content.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-iframe-content.htm
new file mode 100644
index 0000000000..b26f7fda75
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-iframe-content.htm
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with a message</title>
+</head>
+<body>
+ <div>Hello World.</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-pass.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-pass.htm
new file mode 100644
index 0000000000..9d1b2530fe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/standalone-pass.htm
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page with PASS message</title>
+</head>
+<body>
+ <div style="color: Green">PASS!!!</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/svg.svg b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/svg.svg
new file mode 100644
index 0000000000..1570afcadc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/support/svg.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg"><rect height="100" width="100"/></svg>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/3.jpg b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/3.jpg
new file mode 100644
index 0000000000..d30ac2ac36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/3.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.html
new file mode 100644
index 0000000000..73b937f67f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>404 response with actual image data should be rendered and load event is fired</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<img id="img">
+
+<script>
+ async_test(t => {
+ var img = document.getElementById("img");
+ img.onload = t.step_func_done(e => {
+ assert_equals(e.type, "load", "image.onload() called");
+ });
+ img.onerror = t.unreached_func("image.onerror() was not supposed to be called");
+ img.src = "404-response-with-actual-image-data.py";
+ }, "404 response with actual image data should be rendered and load event is fired");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.py b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.py
new file mode 100644
index 0000000000..083aa90b41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/404-response-with-actual-image-data.py
@@ -0,0 +1,4 @@
+from base64 import decodebytes
+
+def main(req, res):
+ return 404, [(b'Content-Type', b'image/png')], decodebytes(b"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAhSURBVDhPY3wro/KfgQLABKXJBqMGjBoAAqMGDLwBDAwAEsoCTFWunmQAAAAASUVORK5CYII=")
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/Image-constructor.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/Image-constructor.html
new file mode 100644
index 0000000000..3cfef5f4f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/Image-constructor.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<title>DOM Image constructor Test</title>
+<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element" />
+<meta name="assert" content="Tests the Image constructor for the img-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+
+<div id="log"></div>
+<script>
+ test(function() {
+ var img = new Image();
+ assert_true(img != undefined);
+ }, "Image constructor works");
+
+ test(function() {
+ assert_equals(Image.prototype, HTMLImageElement.prototype);
+ }, "Image and HTMLImageElement share a prototype");
+
+ test(function() {
+ assert_true((new Image()).localName === "img");
+ }, "Image localName is img");
+
+ test(function() {
+ assert_true((new Image()).namespaceURI === "http://www.w3.org/1999/xhtml");
+ }, "Image namespace URI is correct");
+
+ test(function() {
+ assert_equals(Image.name, "Image", "Image name should be Image (not HTMLImageElement)");
+ assert_equals(Object.getPrototypeOf(Image), Function.prototype, "Image's prototype is Function.prototype");
+ assert_equals(Image.prototype, HTMLImageElement.prototype, "Image.prototype is same as HTMLImageElement.prototype");
+ assert_equals(Object.getPrototypeOf(new Image()), HTMLImageElement.prototype, "new Image()'s prototype is HTMLImageElement.prototype ");
+ assert_equals(Object.getPrototypeOf(Image.prototype), HTMLElement.prototype, "Image.prototype's prototype is HTMLElement.prototype");
+
+ const desc = Object.getOwnPropertyDescriptor(Image, "prototype");
+ assert_false(desc.configurable, "Image.prototype is not configurable");
+ assert_false(desc.enumerable, "Image.prototype is not enumerable");
+ assert_false(desc.writable, "Image.prototype is not writable");
+ }, "NamedConstructor creates the correct object structure.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adopt-from-image-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adopt-from-image-document.html
new file mode 100644
index 0000000000..30729fc81c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adopt-from-image-document.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Adopt img from image document</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+<link rel="match" href="document-base-url-ref.html">
+<!-- Counteract any style added by the image document -->
+<style>img { width: initial; height: initial; }</style>
+<iframe></iframe>
+<script>
+ var iframe = document.querySelector('iframe');
+ iframe.onload = function() {
+ let img = iframe.contentDocument.body.firstChild;
+ document.body.appendChild(img);
+ iframe.remove();
+ };
+ iframe.src = 'resources/cat.jpg';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adoption.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adoption.html
new file mode 100644
index 0000000000..15e02bcf51
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/adoption.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Adopting an image updates the image data</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+
+<!-- tests -->
+
+<div id="adoptTest1"></div>
+<picture id="adoptTest2">
+<source srcset="/images/green-2x2.png">
+</picture>
+
+<script>
+function resolve(url) {
+ if (url === "") {
+ return url;
+ }
+ var a = document.createElement('a');
+ a.href = url;
+ return a.href;
+}
+
+function t(desc, data, expect) {
+ async_test(function(t) {
+ var d = (new DOMParser()).parseFromString(data, 'text/html');
+ var i = d.querySelector('img');
+ i.onerror = this.unreached_func('got unexpected error event');
+ i.onload = this.step_func_done(function() {
+ assert_equals(i.currentSrc, resolve(expect));
+ });
+ var n = d.querySelector('[adopt-node]');
+ document.adoptNode(n);
+ }, desc);
+}
+
+onload = function() {
+
+ t('img (src only)',
+ '<img src="/images/green-1x1.png" adopt-node>',
+ '/images/green-1x1.png');
+
+ t('img (src only), parent is picture',
+ '<picture adopt-node><img src="/images/green-1x1.png"></picture>',
+ '/images/green-1x1.png');
+
+ t('img (src only), previous sibling is source',
+ '<picture adopt-node><source srcset="/images/green-1x1.png"><img src="/images/green-2x2.png"></picture>',
+ '/images/green-1x1.png');
+
+ t('img (srcset 1 cand)',
+ '<img srcset="/images/green-1x1.png" adopt-node>',
+ '/images/green-1x1.png');
+
+ t('img (srcset 1 cand), parent is picture',
+ '<picture adopt-node><img srcset="/images/green-1x1.png"></picture>',
+ '/images/green-1x1.png');
+
+ t('img (srcset 1 cand), previous sibling is source',
+ '<picture adopt-node><source srcset="/images/green-1x1.png"><img srcset="/images/green-2x2.png"></picture>',
+ '/images/green-1x1.png');
+
+ async_test(function(t) {
+ var d = (new DOMParser()).parseFromString('<template><img src="/images/green-1x1.png"></template>', 'text/html');
+ var i = d.querySelector('template').content.querySelector('img').cloneNode(1);
+ i.onerror = this.unreached_func('got unexpected error event');
+ i.onload = this.step_func_done(function() {
+ assert_equals(i.currentSrc, resolve('/images/green-1x1.png'));
+ });
+
+ document.getElementById('adoptTest1').appendChild(i);
+ }, 'adopt a cloned img in template');
+
+ async_test(function(t) {
+ var preload = new Image();
+ preload.src = '/images/green-1x1.png?' + Math.random();
+ preload.onload = t.step_func(function() {
+ var d = (new DOMParser()).parseFromString('<img src="' + preload.src + '">', 'text/html');
+ var i = d.querySelector('img');
+ i.onerror = this.unreached_func('got unexpected error event');
+ i.onload = this.step_func_done(function() {
+ assert_equals(i.currentSrc, resolve("/images/green-2x2.png"));
+ });
+
+ var p = document.getElementById('adoptTest2');
+ p.appendChild(i);
+ });
+ }, 'adoption is from appendChild');
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/already-loaded-image-sync-width.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/already-loaded-image-sync-width.html
new file mode 100644
index 0000000000..4a63bd7a7a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/already-loaded-image-sync-width.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Image dimensions are available synchronously after changing src to an already-loaded image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1797798">
+<img id="existing">
+<script>
+ let src = "/images/green.png";
+ let existing = document.getElementById("existing");
+ async_test(function(t) {
+ let tmp = document.createElement("img");
+ tmp.src = src;
+ tmp.onload = t.step_func_done(function() {
+ existing.src = src;
+ assert_equals(existing.width, 100);
+ assert_equals(existing.height, 50);
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-onload.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-onload.html
new file mode 100644
index 0000000000..5fc5cb8b61
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-onload.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+<title>Ensure images from available images list can be drawn to a canvas</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-list-of-available-images">
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ async_test(function(t) {
+ var i = new Image();
+ i.onerror = t.unreached_func();
+ i.onload = t.step_func(function() {
+ var i2 = new Image();
+ // Potentially start multiple image loading tasks by performing several
+ // relevant mutations. This could lead to an invalid state later in an
+ // erroneous implementation.
+ i2.crossOrigin = true;
+ // Start an image loading task that is expected to short-circuit since
+ // the requested image is present in the list of available images.
+ i2.src = "3.jpg";
+ i2.onerror = t.unreached_func();
+ // Ensure the loaded image is in a state that is usable by a 2d canvas.
+ i2.onload = t.step_func_done(function() {
+ var c = document.createElement('canvas');
+ var ctx = c.getContext('2d');
+ ctx.drawImage(i2, 0, 0);
+ });
+ });
+ // Request an image which should be added to the list of available images.
+ i.src = "3.jpg";
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-ref.html
new file mode 100644
index 0000000000..8061abae50
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<img src="3.jpg">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images.html
new file mode 100644
index 0000000000..779ff97868
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/available-images.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Ensure images from available images list are rendered</title>
+<meta charset="utf-8">
+<link rel="match" href="available-images-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+<div id="log"></div>
+<script>
+ var i = new Image();
+ i.onload = function() {
+ var i2 = new Image();
+ i2.src = "3.jpg";
+ document.body.appendChild(i2);
+ document.documentElement.classList.remove("reftest-wait");
+ };
+ i.src = "3.jpg";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/below-viewport-image-loading-lazy-load-event.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/below-viewport-image-loading-lazy-load-event.html
new file mode 100644
index 0000000000..b1dee3a3ca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/below-viewport-image-loading-lazy-load-event.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<head>
+ <title>Below-viewport loading=lazy images do not block the window load event
+ when scrolled into viewport</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<body>
+ <!-- When this image loads, we will scroll the below-viewport loading=lazy
+ images into the viewport. This happens before the window load event is
+ fired -->
+ <img id="scroll_trigger"
+ src="resources/image.png?scroll-trigger&pipe=trickle(d1)"
+ onload="scroll_trigger_img.resolve();" onerror="scroll_trigger_img.reject();">
+ <!-- This image blocks the window load event for 2 seconds -->
+ <img src="resources/image.png?window-load-blocking&pipe=trickle(d2)">
+
+ <div style="height:1000vh"></div>
+ <!-- These images must load because they intersect the viewport, but they must
+ not block the window load event, because they are loading=lazy -->
+ <img id="visible"
+ src="resources/image.png?visible&pipe=trickle(d3)" loading="lazy"
+ onload="visible_img.resolve();" onerror="visible_img.reject();">
+ <img id="visibility_hidden" style="visibility:hidden;"
+ src="resources/image.png?visibility_hidden&pipe=trickle(d3)" loading="lazy"
+ onload="visibility_hidden_img.resolve();" onerror="visibility_hidden_img.reject();">
+</body>
+
+<script>
+ const scroll_trigger_img = new ElementLoadPromise("visible");
+ const visible_img = new ElementLoadPromise("visible");
+ const visibility_hidden_img = new ElementLoadPromise("visibility_hidden");
+
+ async_test(t => {
+ let has_window_loaded = false;
+
+ scroll_trigger_img.promise
+ .then(t.step_func(() => {
+ assert_false(has_window_loaded,
+ "The scroll_trigger image should load before the window " +
+ "load event fires");
+ visibility_hidden_img.element().scrollIntoView();
+ }))
+ .catch(t.unreached_func("The scroll_trigger image should load"));
+
+ window.addEventListener("load", t.step_func(() => {
+ has_window_loaded = true;
+ }));
+
+ Promise.all([visible_img.promise, visibility_hidden_img.promise])
+ .then(t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "The window load event should fire before the " +
+ "below-viewport loading=lazy images load");
+ assert_true(visible_img.element().complete,
+ "The below-viewport loading=lazy visible image is complete");
+ assert_true(visibility_hidden_img.element().complete,
+ "The below-viewport loading=lazy visibility:hidden image is complete");
+ }))
+ .catch(t.unreached_func("The images should load successfully"));
+
+ }, "Below-viewport loading=lazy images do not block the window load event when " +
+ "scrolled into viewport");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/brokenimg.jpg b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/brokenimg.jpg
new file mode 100644
index 0000000000..ccff177ae9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/brokenimg.jpg
@@ -0,0 +1,4 @@
+<!DOCTYPE HTML>
+<html>
+
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/basic.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/basic.html
new file mode 100644
index 0000000000..f7d47b3640
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/basic.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>img current pixel density basic</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<img src="/images/green-256x256.png" data-expect="256">
+<img srcset="/images/green-256x256.png 1x" data-expect="256">
+<img srcset="/images/green-256x256.png 1.6x" data-expect="160">
+<img srcset="/images/green-256x256.png 2x" data-expect="128">
+<img srcset="/images/green-256x256.png 10000x" data-expect="0">
+<img srcset="/images/green-256x256.png 9e99999999999999999999999x" data-expect="0">
+<img srcset="/images/green-256x256.png 256w" sizes="256px" data-expect="256">
+<img srcset="/images/green-256x256.png 512w" sizes="256px" data-expect="128">
+<img srcset="/images/green-256x256.png 256w" sizes="512px" data-expect="512">
+<img srcset="/images/green-256x256.png 256w" sizes="1px" data-expect="1">
+<img srcset="/images/green-256x256.png 256w" sizes="0px" data-expect="0">
+<!-- SVG -->
+<img srcset="data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='-1%20-1%202%202'%20width='20'%20height='20'><circle%20r='1'/></svg> 2x" data-expect="10">
+<img srcset="data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='-1%20-1%202%202'%20width='20'><circle%20r='1'/></svg> 2x" data-expect="10">
+<img srcset="data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='-1%20-1%202%202'%20height='20'><circle%20r='1'/></svg> 2x" data-expect="10">
+<script>
+setup({explicit_done:true});
+onload = function() {
+ [].forEach.call(document.images, function(img) {
+ test(function() {
+ var expected = parseFloat(img.dataset.expect);
+ assert_equals(img.width, expected, 'width');
+ assert_equals(img.height, expected, 'height');
+ assert_equals(img.clientWidth, expected, 'clientWidth');
+ assert_equals(img.clientHeight, expected, 'clientHeight');
+ assert_equals(img.naturalWidth, expected, 'naturalWidth');
+ assert_equals(img.naturalHeight, expected, 'naturalHeight');
+ }, img.outerHTML);
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/error.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/error.html
new file mode 100644
index 0000000000..5e328b5e2d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/current-pixel-density/error.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>img current pixel density error</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<img id=ref src="404" alt="testing">
+<img srcset="404" alt="testing">
+<img srcset="404 0.5x" alt="testing">
+<img srcset="404 2x" alt="testing">
+<img srcset="404 100w" alt="testing">
+<img srcset="404 100w" sizes="500px" alt="testing">
+<picture><img src="404 100w" sizes="500px" alt="testing"></picture>
+<script>
+setup({explicit_done:true});
+onload = function() {
+ var ref = document.getElementById("ref");
+ var expected_width = ref.width;
+ var expected_height = ref.height;
+ [].forEach.call(document.images, function(img) {
+ test(function() {
+ assert_not_equals(expected_width, 0, 'expected_width');
+ assert_not_equals(expected_height, 0, 'expected_height');
+ assert_equals(img.width, expected_width, 'width');
+ assert_equals(img.height, expected_height, 'height');
+ assert_equals(img.naturalWidth, 0, 'naturalWidth');
+ assert_equals(img.naturalHeight, 0, 'naturalHeight');
+ }, img.outerHTML);
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/currentSrc-blob-cache.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/currentSrc-blob-cache.html
new file mode 100644
index 0000000000..a5e108dcd6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/currentSrc-blob-cache.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>currentSrc is right even if underlying image is a shared blob</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1625786">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<img id="first">
+<img id="second">
+<script>
+promise_test(async t => {
+ let canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+ let ctx = canvas.getContext("2d");
+ ctx.fillStyle = "green";
+ ctx.rect(0, 0, 100, 100);
+ ctx.fill();
+
+ let blob = await new Promise(resolve => canvas.toBlob(resolve));
+
+ let first = document.querySelector("#first");
+ let second = document.querySelector("#second");
+
+ let firstLoad = new Promise(resolve => {
+ first.addEventListener("load", resolve, { once: true });
+ });
+
+ let secondLoad = new Promise(resolve => {
+ second.addEventListener("load", resolve, { once: true });
+ });
+
+ let uri1 = URL.createObjectURL(blob);
+ let uri2 = URL.createObjectURL(blob);
+ first.src = uri1;
+ second.src = uri2;
+
+ await firstLoad;
+ await secondLoad;
+
+ assert_equals(first.src, first.currentSrc);
+ assert_equals(second.src, second.currentSrc);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/data-url.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/data-url.html
new file mode 100644
index 0000000000..808b5c884c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/data-url.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>data URL image</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+setup({ single_test: true });
+
+var c = document.createElement("canvas"),
+ con = c.getContext("2d"),
+ img = document.createElement("img")
+img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAA+UlEQVR4nO3RoRHAQBDEsOu/6YR+B2sgIO4Z3919pMwDMCRtHoAhafMADEmbB2BI2jwAQ9LmARiSNg/AkLR5AIakzQMwJG0egCFp8wAMSZsHYEjaPABD0uYBGJI2D8CQtHkAhqTNAzAkbR6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPwJC0eQCGpM0DMCRtHoAhafMADEmbB2BI2jwAQ9LmARiSNg/AkLR5AIakzQMwJG0egCFp8wAMSZsHYEjaPABD0uYBGJI2D8CQtHkAhqTNAzAkbR6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPwJC0eQCGpM0DMCRtHsDjB5K06yueJFXJAAAAAElFTkSuQmCC"
+img.onload = () => {
+ con.drawImage(img, 0, 0)
+ var data = con.getImageData(0, 0, 10, 10) // should not throw as data URLs are same-origin
+ for(var i = 0; i < data.data.length; i++) {
+ var expected = ((i+1) % 4 == 0) ? 255 : 0
+ assert_equals(data.data[i], expected)
+ }
+ c.toDataURL() // shouldn't throw either
+ done()
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-iframe.html
new file mode 100644
index 0000000000..ed14a007a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-iframe.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>HTMLImageElement.prototype.decode(), iframe tests.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="frame_loaded" srcdoc="iframe"></iframe>
+<iframe id="frame_notloaded" srcdoc="iframe"></iframe>
+<iframe id="frame_notloaded2" srcdoc="iframe"></iframe>
+
+<script>
+"use strict";
+
+promise_test(function() {
+ return new Promise(function(resolve, reject) {
+ var frame = document.getElementById("frame_loaded");
+ var img = frame.contentDocument.createElement("img");
+ img.src = "/images/green.png";
+ img.onload = function() {
+ // At this point the frame which created the img is removed, so decode() should fail.
+ frame.parentNode.removeChild(frame);
+ img.decode().then(function() {
+ assert_unreached("Unexpected success");
+ }, function() {
+ resolve();
+ });
+ };
+ });
+}, document.title + " Decode from removed iframe fails (loaded img)");
+
+promise_test(function(t) {
+ var frame = document.getElementById("frame_notloaded");
+ var img = frame.contentDocument.createElement("img");
+ img.src = "/images/green.png";
+ frame.parentNode.removeChild(frame);
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Decode from removed iframe fails (img not loaded)");
+
+promise_test(function(t) {
+ var frame = document.getElementById("frame_notloaded2");
+ var img = frame.contentDocument.createElement("img");
+ img.src = "/images/green.png";
+ // First request a promise, then remove the iframe.
+ var promise = img.decode();
+ frame.parentNode.removeChild(frame);
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Decode from iframe, later removed, fails (img not loaded)");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-image-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-image-document.html
new file mode 100644
index 0000000000..e54ae223a0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-image-document.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>HTMLImageElement.prototype.decode(), image document tests.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="frame_imgdoc" src="about:blank"></iframe>
+<script>
+"use strict";
+
+promise_test(function() {
+ return new Promise(function(resolve) {
+ var frame = document.getElementById("frame_imgdoc");
+ // Load an image in the iframe and then replace that.
+ frame.src = "/images/red.png";
+ frame.onload = function() {
+ let img = frame.contentDocument.body.firstElementChild;
+ img.src = "/images/green.png";
+ img.decode().then(function() {
+ resolve();
+ });
+ };
+ });
+}, document.title + " Decode from iframe with image document, succeeds (img not loaded)");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes-svg.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes-svg.tentative.html
new file mode 100644
index 0000000000..1bc53a1f18
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes-svg.tentative.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>SVGImageElement.prototype.decode(), href mutation tests.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+// src tests
+// -------------------
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.png");
+ var promise = img.decode();
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.svg");
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " xlink:href changes fail decode.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "/images/green.png");
+ var promise = img.decode();
+ img.setAttribute('href', "/images/green.svg");
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " href changes fail decode.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.png");
+ var first_promise = img.decode();
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.svg");
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ second_promise
+ ]);
+}, document.title + " xlink:href changes fail decode; following good decode succeeds.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "/images/green.png");
+ var first_promise = img.decode();
+ img.setAttribute('href', "/images/green.svg");
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ second_promise
+ ]);
+}, document.title + " href changes fail decode; following good decode succeeds.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.png");
+ var first_promise = img.decode();
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/non/existent/path.png");
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ promise_rejects_dom(t, "EncodingError", second_promise)
+ ]);
+}, document.title + " xlink:href changes fail decode; following bad decode fails.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "/images/green.png");
+ var first_promise = img.decode();
+ img.setAttribute('href', "/non/existent/path.png");
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ promise_rejects_dom(t, "EncodingError", second_promise)
+ ]);
+}, document.title + " href changes fail decode; following bad decode fails.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes.html
new file mode 100644
index 0000000000..4b878c1bae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>HTMLImageElement.prototype.decode(), src/srcset mutation tests.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+// src tests
+// -------------------
+promise_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ var promise = img.decode();
+ img.src = "/images/green.svg";
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " src changes fail decode.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ var first_promise = img.decode();
+ img.src = "/images/blue.png";
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ second_promise
+ ]);
+}, document.title + " src changes fail decode; following good png decode succeeds.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ var first_promise = img.decode();
+ img.src = "/images/green.svg";
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ second_promise
+ ]);
+}, document.title + " src changes fail decode; following good svg decode succeeds.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ var first_promise = img.decode();
+ img.src = "/non/existent/path.png";
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ promise_rejects_dom(t, "EncodingError", second_promise)
+ ]);
+}, document.title + " src changes fail decode; following bad decode fails.");
+
+promise_test(function(t) {
+ return new Promise(function(resolve, reject) {
+ var img = new Image();
+ // We wait for an onload, since the "Updating the image data" spec states
+ // that if a new microtask is scheduled, the old one is canceled so
+ // without the onload, the first decode request would be requested when the
+ // img.src is empty. With an onload, we ensure that the img.src is set and
+ // the image exists before issuing the first decode, then we verify that the
+ // src change to the same value does not prevent that request from
+ // succeeding.
+ img.onload = t.step_func(function() {
+ img.onload = null;
+
+ var first_promise = img.decode();
+ img.src = "/images/green.png";
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ resolve(Promise.all([first_promise, second_promise]));
+ });
+ img.src = "/images/green.png";
+ });
+}, document.title + " src changes to the same path succeed.");
+
+// srcset tests
+// -------------------
+promise_test(function(t) {
+ var img = new Image();
+ img.srcset = "/images/green.png 100w";
+ var promise = img.decode();
+ img.srcset = "/images/green.svg 100w";
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " srcset changes fail decode.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.srcset = "/images/green.png 100w";
+ var first_promise = img.decode();
+ img.srcset = "/images/green.svg 100w";
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ second_promise
+ ]);
+}, document.title + " srcset changes fail decode; following good decode succeeds.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.srcset = "/images/green.png 100w";
+ var first_promise = img.decode();
+ img.srcset = "/non/existent/path.png 100w";
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ promise_rejects_dom(t, "EncodingError", first_promise),
+ promise_rejects_dom(t, "EncodingError", second_promise)
+ ]);
+}, document.title + " srcset changes fail decode; following bad decode fails.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-picture.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-picture.html
new file mode 100644
index 0000000000..2f4d5e7c41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-picture.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>HTMLImageElement.prototype.decode(), picture tests.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<picture>
+<source srcset="/images/green.png">
+<source srcset="/images/blue.png">
+<img id="testimg">
+</picture>
+
+<script>
+"use strict";
+
+promise_test(function() {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ var img = document.createElement("img");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ source.srcset = "/images/green.png";
+
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG source decodes with undefined.");
+
+promise_test(function() {
+ var img = document.getElementById("testimg");
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with multiple sources decodes with undefined.");
+
+promise_test(function() {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ var img = document.createElement("img");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ source.srcset = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIA" +
+ "AAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QUSEioKsy" +
+ "AgywAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAW" +
+ "SURBVAjXY9y3bx8DAwPL58+fGRgYACktBRltLfebAAAAAElFTkSuQmCC";
+
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG data URL source decodes with undefined.");
+
+promise_test(function() {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ var img = document.createElement("img");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ source.srcset = "/images/green.svg";
+
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with SVG source decodes with undefined.");
+
+promise_test(function(t) {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ var img = document.createElement("img");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ source.srcset = "/non/existent/path.png";
+
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Non-existent source fails decode.");
+
+promise_test(function(t) {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ var img = document.createElement("img");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ source.srcset = "data:image/png;base64,iVBO00PDR0BADBEEF00KGg";
+
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Corrupt image in src fails decode.");
+
+promise_test(function(t) {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ var img = document.createElement("img");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Image without srcset fails decode.");
+
+promise_test(function() {
+ var picture = document.createElement("picture");
+ var source = document.createElement("source");
+ var img = document.createElement("img");
+
+ picture.appendChild(source);
+ picture.appendChild(img);
+
+ source.srcset = "/images/green.png";
+
+ var first_promise = img.decode();
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ first_promise,
+ second_promise
+ ]);
+}, document.title + " Multiple decodes for images with src succeed.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-svg.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-svg.tentative.html
new file mode 100644
index 0000000000..047470f1e3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-svg.tentative.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>SVGImageElement.prototype.decode(), basic tests.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+// src tests
+// -------------------
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.png");
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG xlink:href decodes with undefined.");
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "/images/green.png");
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG href decodes with undefined.");
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAA" +
+ "D91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QUSEioKsyAgyw" +
+ "AAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAWSURBVA" +
+ "jXY9y3bx8DAwPL58+fGRgYACktBRltLfebAAAAAElFTkSuQmCC");
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG data URL xlink:href decodes with undefined.");
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href',
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAA" +
+ "D91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QUSEioKsyAgyw" +
+ "AAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAWSURBVA" +
+ "jXY9y3bx8DAwPL58+fGRgYACktBRltLfebAAAAAElFTkSuQmCC");
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG data URL href decodes with undefined.");
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.svg");
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with SVG xlink:href decodes with undefined.");
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "/images/green.svg");
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with SVG href decodes with undefined.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/non/existent/path.png");
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Non-existent xlink:href fails decode.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "/non/existent/path.png");
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Non-existent href fails decode.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "data:image/png;base64,iVBO00PDR0BADBEEF00KGg");
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Corrupt image in xlink:href fails decode.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "data:image/png;base64,iVBO00PDR0BADBEEF00KGg");
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Corrupt image in href fails decode.");
+
+promise_test(function(t) {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Image without xlink:href or href fails decode.");
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.png");
+ var first_promise = img.decode();
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ first_promise,
+ second_promise
+ ]);
+}, document.title + " Multiple decodes with a xlink:href succeed.");
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttribute('href', "/images/green.png");
+ var first_promise = img.decode();
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ first_promise,
+ second_promise
+ ]);
+}, document.title + " Multiple decodes with a href succeed.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach-svg.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach-svg.tentative.html
new file mode 100644
index 0000000000..0fc49e6036
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach-svg.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>SVGImageElement.prototype.decode(), attach to DOM before promise resolves.</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<svg></svg>
+<script>
+"use strict";
+
+promise_test(function() {
+ var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+ img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "/images/green.png");
+ const promise = img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+ // Don't wait for the promise to resolve before attaching the image.
+ // The promise should still resolve successfully.
+ document.querySelector('svg').appendChild(img);
+ return promise;
+}, document.title);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach.html
new file mode 100644
index 0000000000..be680da619
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode-with-quick-attach.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>HTMLImageElement.prototype.decode(), attach to DOM before promise resolves.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body></body>
+
+<script>
+"use strict";
+
+promise_test(function() {
+ const img = new Image();
+ img.src = "/images/green.png";
+ const promise = img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+ // Don't wait for the promise to resolve before attaching the image.
+ // The promise should still resolve successfully.
+ document.body.appendChild(img);
+ return promise;
+}, document.title);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode.html
new file mode 100644
index 0000000000..fac61a1446
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/decode/image-decode.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>HTMLImageElement.prototype.decode(), basic tests.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+// src tests
+// -------------------
+promise_test(function() {
+ var img = new Image();
+ img.src = "/images/green.png";
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG src decodes with undefined.");
+
+promise_test(function() {
+ var img = new Image();
+ img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAA" +
+ "D91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QUSEioKsyAgyw" +
+ "AAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAWSURBVA" +
+ "jXY9y3bx8DAwPL58+fGRgYACktBRltLfebAAAAAElFTkSuQmCC";
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG data URL src decodes with undefined.");
+
+promise_test(function() {
+ var img = new Image();
+ img.src = "/images/green.svg";
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with SVG src decodes with undefined.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.src = "/non/existent/path.png";
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Non-existent src fails decode.");
+
+promise_test(function(t) {
+ var inactive_doc = document.implementation.createHTMLDocument();
+ var img = inactive_doc.createElement("img");
+ img.src = "/images/green.png";
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Inactive document fails decode.");
+
+promise_test(function(t) {
+ var inactive_doc = document.implementation.createHTMLDocument();
+ var img = document.createElement("img");
+ img.src = "/images/green.png";
+ var promise = img.decode();
+ inactive_doc.body.appendChild(img);
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Adopted active image into inactive document fails decode.");
+
+promise_test(function() {
+ var inactive_doc = document.implementation.createHTMLDocument();
+ var img = inactive_doc.createElement("img");
+ img.src = "/images/green.png";
+ document.body.appendChild(img);
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Adopted inactive image into active document succeeds.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.src = "data:image/png;base64,iVBO00PDR0BADBEEF00KGg";
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Corrupt image in src fails decode.");
+
+promise_test(function(t) {
+ var img = new Image();
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Image without src/srcset fails decode.");
+
+promise_test(function() {
+ var img = new Image();
+ img.src = "/images/green.png";
+ var first_promise = img.decode();
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ first_promise,
+ second_promise
+ ]);
+}, document.title + " Multiple decodes for images with src succeed.");
+
+// srcset tests
+// -------------------
+promise_test(function() {
+ var img = new Image();
+ img.srcset = "/images/green.png 100w";
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with PNG srcset decodes with undefined.");
+
+promise_test(function() {
+ var img = new Image();
+ img.srcset = "/images/green.svg 100w";
+ return img.decode().then(function(arg) {
+ assert_equals(arg, undefined);
+ });
+}, document.title + " Image with SVG srcset decodes with undefined.");
+
+promise_test(function(t) {
+ var img = new Image();
+ img.srcset = "/non/existent/path.png 100w";
+ var promise = img.decode();
+ return promise_rejects_dom(t, "EncodingError", promise);
+}, document.title + " Non-existent srcset fails decode.");
+
+promise_test(function() {
+ var img = new Image();
+ img.srcset = "/images/green.png 100w";
+ var first_promise = img.decode();
+ var second_promise = img.decode();
+ assert_not_equals(first_promise, second_promise);
+ return Promise.all([
+ first_promise,
+ second_promise
+ ]);
+}, document.title + " Multiple decodes for images with srcset succeed.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-detached.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-detached.html
new file mode 100644
index 0000000000..5c68de29e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-detached.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Detached image blocks load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var img_loaded = false;
+
+var img = new Image();
+img.onload = function() {
+ img_loaded = true;
+};
+img.src = "/images/blue.png?pipe=trickle(d2)";
+
+test(function() {
+ assert_false(img_loaded);
+}, "setting img.src is async");
+
+async_test(function(t) {
+ document.addEventListener("DOMContentLoaded", t.step_func_done(function() {
+ assert_false(img_loaded);
+ }));
+}, "DOMContentLoaded doesn't wait for images");
+
+async_test(function(t) {
+ window.addEventListener("load", t.step_func_done(function() {
+ assert_true(img_loaded);
+ }));
+}, "load waits for images");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-until-move-to-empty-source.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-until-move-to-empty-source.html
new file mode 100644
index 0000000000..7b61606c47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event-until-move-to-empty-source.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Inline image element blocks load until source is changed to empty source</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<img src="/images/blue.png?pipe=trickle(d100)">
+<script>
+
+async_test(t => {
+ const image = document.querySelector("img");
+
+ assert_false(image.complete, "The image is loading initially");
+
+ // Complete the test as soon as we obtained the window "load" event,
+ // which should happen as soon as the image stops loading by moving
+ // to an empty source.
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_true(image.complete, "The image is no longer loading once the window 'load' event is dispatched");
+ }));
+
+ // Stop loading the image.
+ image.src = "";
+}, "Image element delays window's load event until the image changes to empty source");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event.html
new file mode 100644
index 0000000000..ac0cf29d3f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/delay-load-event.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Inline image element blocks load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var img_loaded = false;
+</script>
+<img src="/images/blue.png?pipe=trickle(d2)" onload="img_loaded = true;">
+<script>
+test(function() {
+ assert_false(img_loaded);
+}, "script execution doesn't wait for the image to load");
+
+async_test(function(t) {
+ document.addEventListener("DOMContentLoaded", t.step_func_done(function() {
+ assert_false(img_loaded);
+ }));
+}, "DOMContentLoaded doesn't wait for images");
+
+async_test(function(t) {
+ window.addEventListener("load", t.step_func_done(function() {
+ assert_true(img_loaded);
+ }));
+}, "Image element delays window's load event");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/disconnected-image-loading-lazy.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/disconnected-image-loading-lazy.html
new file mode 100644
index 0000000000..fe6d79fe2a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/disconnected-image-loading-lazy.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+async_test(t => {
+ x = new Image();
+ x.loading = "auto";
+ x.src = "resources/image.png?auto";
+ x.onload = t.step_func_done();
+ t.step_timeout(t.unreached_func("Disconnected loading=auto image loads " +
+ "successfully, and doesn't timeout"), 2000);
+}, "loading=auto for disconnected image");
+
+async_test(t => {
+ x = new Image();
+ x.loading = "eager";
+ x.src = "resources/image.png?eager";
+ x.onload = t.step_func_done();
+ t.step_timeout(t.unreached_func("Disconnected loading=eager image loads " +
+ "successfully, and doesn't timeout"), 2000);
+}, "loading=eager for disconnected image");
+
+async_test(t => {
+ x = new Image();
+ x.loading = "lazy";
+ x.src = "resources/image.png?lazy";
+ x.onload = t.unreached_func("Disconnected loading=lazy image loads lazily.");
+ t.step_timeout(t.step_func_done(), 2000);
+}, "loading=lazy for disconnected image");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html
new file mode 100644
index 0000000000..ea63114d57
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Document base URL adopted img test</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element" />
+<link rel="match" href="document-base-url-ref.html">
+<base href="resources/" />
+<iframe></iframe>
+<script>
+ var iframe = document.querySelector('iframe');
+ var i = iframe.contentDocument.createElement('img');
+ i.src = "cat.jpg";
+ document.body.appendChild(i);
+ iframe.remove();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url-ref.html
new file mode 100644
index 0000000000..6e55b21ff0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Document base URL img test</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element" />
+<img src="resources/cat.jpg" alt="cat">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url.html
new file mode 100644
index 0000000000..074209cc04
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-base-url.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Document base URL img test</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element" />
+<link rel="match" href="document-base-url-ref.html">
+<base href="resources/" />
+<img src="cat.jpg" alt="cat">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-destroyed-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-destroyed-crash.html
new file mode 100644
index 0000000000..da43099f71
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/document-destroyed-crash.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>&lt;img> loading in destroyed document</title>
+<iframe></iframe>
+<script>
+onload = function() {
+ const img = new Image();
+ img.onload = function() {
+ const iframe = document.querySelector('iframe');
+ iframe.contentDocument.createElement('div').innerHTML =
+ `<picture>
+ <source srcset="nonexistent.png">
+ <img src="data:image/gif;base64,R0lGODlhCgAKAIAAAP/MAAAAACH5BAAAAAAALAAAAAAKAAoAAAIIhI+py+0PYysAOw==">
+ </picture>`;
+ iframe.remove();
+ };
+ img.src = 'data:image/gif;base64,R0lGODlhCgAKAIAAAP/MAAAAACH5BAAAAAAALAAAAAAKAAoAAAIIhI+py+0PYysAOw==';
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/iframed.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/iframed.sub.html
new file mode 100644
index 0000000000..0f7ab9ae27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/iframed.sub.html
@@ -0,0 +1,78 @@
+<!doctype html>
+
+<img
+data-desc="img (no src)"
+data-narrow=""
+data-wide=""
+data-no-change>
+
+<img src=""
+data-desc="img (empty src)"
+data-narrow=""
+data-wide=""
+data-no-change>
+
+<img src="/images/broken.png?30-{{GET[id]}}"
+data-desc="img (src only) broken image"
+data-narrow="/images/broken.png?30-{{GET[id]}}"
+data-wide="/images/broken.png?30-{{GET[id]}}"
+data-no-change>
+
+<img src="/images/green-1x1.png?40-{{GET[id]}}"
+data-desc="img (src only) valid image"
+data-narrow="/images/green-1x1.png?40-{{GET[id]}}"
+data-wide="/images/green-1x1.png?40-{{GET[id]}}"
+data-no-change>
+
+<img srcset="/images/broken.png?50-{{GET[id]}}"
+data-desc="img (srcset 1 cand) broken image"
+data-narrow="/images/broken.png?50-{{GET[id]}}"
+data-wide="/images/broken.png?50-{{GET[id]}}"
+data-no-change>
+
+<img srcset="/images/green-1x1.png?60-{{GET[id]}}"
+data-desc="img (srcset 1 cand) valid image"
+data-narrow="/images/green-1x1.png?60-{{GET[id]}}"
+data-wide="/images/green-1x1.png?60-{{GET[id]}}"
+data-no-change>
+
+<picture>
+<source media="(max-width:500px)" srcset="/images/broken.png?70-{{GET[id]}}">
+<img src="/images/broken.png?71-{{GET[id]}}"
+data-desc="picture: source (max-width:500px) broken image, img broken image"
+data-narrow="/images/broken.png?70-{{GET[id]}}"
+data-wide="/images/broken.png?71-{{GET[id]}}">
+</picture>
+
+<picture>
+<source media="(max-width:500px)" srcset="/images/broken.png?80-{{GET[id]}}">
+<img src="/images/green-2x2.png?81-{{GET[id]}}"
+data-desc="picture: source (max-width:500px) broken image, img valid image"
+data-narrow="/images/broken.png?80-{{GET[id]}}"
+data-wide="/images/green-2x2.png?81-{{GET[id]}}">
+</picture>
+
+<picture>
+<source media="(max-width:500px)" srcset="/images/green-1x1.png?90-{{GET[id]}}">
+<img src="/images/broken.png?91-{{GET[id]}}"
+data-desc="picture: source (max-width:500px) valid image, img broken image"
+data-narrow="/images/green-1x1.png?90-{{GET[id]}}"
+data-wide="/images/broken.png?91-{{GET[id]}}">
+</picture>
+
+<picture>
+<source media="(max-width:500px)" srcset="/images/green-1x1.png?100-{{GET[id]}}">
+<img src="/images/green-2x2.png?101-{{GET[id]}}"
+data-desc="picture: source (max-width:500px) valid image, img valid image"
+data-narrow="/images/green-1x1.png?100-{{GET[id]}}"
+data-wide="/images/green-2x2.png?101-{{GET[id]}}">
+</picture>
+
+<picture>
+<source media="(max-width:500px)" srcset="/images/green-1x1.png?110-{{GET[id]}}">
+<img src="/images/green-1x1.png?110-{{GET[id]}}"
+data-desc="picture: same URL in source (max-width:500px) and img"
+data-narrow="/images/green-1x1.png?110-{{GET[id]}}"
+data-wide="/images/green-1x1.png?110-{{GET[id]}}"
+data-no-change>
+</picture>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html
new file mode 100644
index 0000000000..f6ae65708c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<title>img viewport change</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<style>
+.narrow { width:50px }
+.wide { width:1000px }
+</style>
+<div id=log></div>
+<script>
+setup({explicit_done:true});
+
+function resolve(url) {
+ if (url === "") {
+ return url;
+ }
+ var a = document.createElement('a');
+ a.href = url;
+ return a.href;
+}
+
+function insertIframe(className) {
+ var iframe = document.createElement('iframe');
+ iframe.className = className;
+ iframe.src = 'iframed.sub.html?id=' + token();
+ document.body.appendChild(iframe);
+}
+insertIframe('narrow');
+insertIframe('wide');
+
+var start_date = new Date();
+
+onload = function() {
+ var load_time = new Date() - start_date;
+ var iframes = document.getElementsByTagName('iframe');
+ [].forEach.call(iframes, function(iframe) {
+ [].forEach.call(iframe.contentDocument.images, function(img) {
+ var expected = {wide:resolve(img.dataset.wide), narrow:resolve(img.dataset.narrow)};
+ var current = iframe.className;
+ var next = current === 'wide' ? 'narrow' : 'wide';
+ var expect_change = expected[next].indexOf('broken.png') === -1 && !('noChange' in img.dataset);
+
+ test(function() {
+ assert_equals(img.currentSrc, expected[current]);
+ }, img.dataset.desc + ', onload, ' + current);
+
+ async_test(function() {
+ img.onload = this.unreached_func('Got unexpected load event');
+ img.onerror = this.unreached_func('Got unexpected error event');
+ if (expect_change) {
+ img.onload = this.step_func_done(function() {
+ assert_equals(img.currentSrc, expected[next]);
+ });
+ } else {
+ setTimeout(this.step_func_done(), 500 + load_time);
+ }
+ }, img.dataset.desc + ', resize to ' + next);
+ });
+ iframe.classList.toggle('wide');
+ iframe.classList.toggle('narrow');
+ });
+ done();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/historical-progress-event.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/historical-progress-event.window.js
new file mode 100644
index 0000000000..7c4e121b7c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/historical-progress-event.window.js
@@ -0,0 +1,16 @@
+async_test(t => {
+ const img = new Image();
+ t.add_cleanup(() => img.remove());
+ img.onloadstart = img.onprogress = img.onloadend = t.unreached_func("progress event fired");
+ img.onload = t.step_func_done(e => {
+ assert_true(e instanceof Event);
+ assert_false(e instanceof ProgressEvent);
+ });
+ img.src = "/images/rrgg-256x256.png";
+ document.body.append(img);
+}, "<img> does not support ProgressEvent or loadstart/progress/loadend");
+
+test(t => {
+ assert_equals(document.body.onloadend, undefined);
+ assert_equals(window.onloadend, undefined);
+}, "onloadend is not exposed");
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-1.jpg b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-1.jpg
new file mode 100644
index 0000000000..2fb0255609
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-1.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-base-url.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-base-url.html
new file mode 100644
index 0000000000..932cd92b41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-base-url.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Image load parses URL after microtask runs</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+<script>
+// See https://github.com/whatwg/html/issues/7383 and
+// https://chromium-review.googlesource.com/c/chromium/src/+/3311225.
+// This test asserts two things:
+// 1.) That Document base URL modifications that take place in between an
+// image loading microtask being scheduled and executed are reflected in
+// the final image request
+// 2.) That subsequent changes to a Document's base URL before an image is
+// inserted into the DOM do not lead to the image being refetched when it
+// is inserted asynchronously later. This is because image insertion is
+// not a relevant mutation
+// (https://html.spec.whatwg.org/#relevant-mutations).
+promise_test(async t => {
+ const image = new Image();
+ image.src = 'green.png';
+
+ // Dynamically insert a <base> tag that should influence the above image
+ // request because the above code triggers a microtask to continue fetching
+ // the image, which will run while we await `loadPromise` below.
+ const base = document.createElement('base');
+ base.setAttribute('href', 'resources/');
+ document.head.append(base);
+
+ const loadPromise = new Promise((resolve, reject) => {
+ image.addEventListener('load', e => {
+ resolve();
+ }, {once: true});
+
+ image.addEventListener('error', e => {
+ reject('The image must load');
+ }, {once: true});
+ });
+
+ // The image should load successfully, since its request was influenced by the
+ // <base> element which points the request to the right directory.
+ await loadPromise;
+
+ // Now manipulate the <base> element to point to a bogus directory.
+ base.setAttribute('href', 'bogus/');
+ document.body.append(image);
+
+ const timeoutPromise = new Promise(resolve => t.step_timeout(resolve, 1500));
+ const imageErrorPromise = new Promise((resolve, reject) => {
+ image.addEventListener('load', e => {
+ reject('The image should not be refetched upon insertion and load, ' +
+ 'because (1) insertion is not a relevant mutation, and (2) the ' +
+ 'new relative URL should not resolve to a real resource');
+ }, {once: true});
+
+ image.addEventListener('error', e => {
+ reject('The image should not be refetched upon insertion, because ' +
+ 'insertion is not a relevant mutation');
+ }, {once: true});
+ });
+
+ await Promise.race([timeoutPromise, imageErrorPromise]);
+}, "An image should not be refetched upon insertion asynchronously after its " +
+ "Document's base URL changes");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change-ref.html
new file mode 100644
index 0000000000..ea80d8b545
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<style>
+#change {
+ height:75px;
+ width:75px;
+}
+</style>
+<img id="change" src="/images/green-16x16.png"></img>
+<img src="/images/green-16x16.png"></img>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change.html
new file mode 100644
index 0000000000..d3e7ee4171
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-change.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Composited images correctly re-raster when the image and bounds change</title>
+<meta charset="utf-8">
+<meta name=fuzzy content="maxDifference=0-150;totalPixels=0-296">
+<link rel="match" href="image-compositing-change-ref.html"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+<style>
+#change {
+ will-change:transform;
+ height:426px; width:426px;
+}
+</style>
+<img id="change" src="image.png"></img>
+<img id="original" src="../../../../images/green-16x16.png"></img>
+<script>
+window.onload = () => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ let image = document.querySelector('#change');
+ image.style.width = image.style.height = "75px";
+ image.src = original.src;
+
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ });
+ });
+}
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change-ref.html
new file mode 100644
index 0000000000..852a47687e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
+<style>
+html { overflow: hidden; }
+#change {
+ will-change:transform;
+ width:200vw;
+ height:200vh;
+ position:absolute;
+ top: 0px;
+ left: 0px;
+}
+</style>
+<img id="change" src="image.png"></img>
+<div id="placeholder" style="position:relative">div</div>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change.html
new file mode 100644
index 0000000000..515f88e3f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Composited images correctly display under large scale transform changes</title>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
+<link rel="match" href="image-compositing-large-scale-change-ref.html"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+<style>
+html { overflow: hidden; }
+#change {
+ will-change:transform;
+ width:1000px;
+ height:1000px;
+ position:absolute;
+ top: calc(50% - 5px);
+ left: calc(50% - 5px);
+}
+</style>
+<img id="change" src="image.png"></img>
+<div id="placeholder" style="position:relative"></div>
+<script>
+window.onload = () => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ let image = document.querySelector('#change');
+ image.style.transform = 'scale(20)';
+ placeholder.innerText = "div";
+
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ });
+ });
+}
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-eager.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-eager.html
new file mode 100644
index 0000000000..54e169f867
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-eager.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<head>
+ <title>Images with loading='eager' load immediately regardless of their
+ position with respect to the viewport</title>
+ <link rel="author" title="Scott Little" href="mailto:sclittle@chromium.org">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test("Test that images with loading='eager' load " +
+ "immediately regardless of their position with " +
+ "respect to the viewport.");
+
+ let has_in_viewport_loaded = false;
+ const in_viewport_img_onload = t.step_func(() => {
+ assert_false(has_in_viewport_loaded,
+ "The in_viewport element should load only once.");
+ has_in_viewport_loaded = true;
+ });
+
+ let has_below_viewport_loaded = false;
+ const below_viewport_img_onload = t.step_func(() => {
+ assert_false(has_below_viewport_loaded,
+ "The below_viewport element should load only once.");
+ has_below_viewport_loaded = true;
+ });
+
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_true(has_in_viewport_loaded,
+ "The in_viewport element should have loaded before window.load().");
+ assert_true(has_below_viewport_loaded,
+ "The below_viewport element should have loaded before window.load().");
+ }));
+
+</script>
+
+<body>
+ <img id="in_viewport" src="resources/image.png?in-viewport" loading="eager" onload="in_viewport_img_onload();">
+ <div style="height:10000px;"></div>
+ <!-- The below_viewport element loads very slowly in order to ensure that the
+ window load event is blocked on it. -->
+ <img id="below_viewport"
+ src="resources/image.png?below-viewport&pipe=trickle(d2)"
+ loading="eager" onload="below_viewport_img_onload();">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-available.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-available.html
new file mode 100644
index 0000000000..1e58c43c86
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-available.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>The list of available images gets checked before deciding to make a load lazy</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#update-the-image-data">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#will-lazy-load-image-steps">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1709577">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<img src="/images/green-256x256.png">
+<div style="height:1000vh;"></div>
+<script>
+promise_test(async t => {
+ await new Promise(resolve => {
+ window.addEventListener("load", resolve);
+ });
+ let nonLazy = document.querySelector("img");
+ assert_equals(nonLazy.width, 256);
+ assert_equals(nonLazy.height, 256);
+
+ let lazy = document.createElement("img");
+ lazy.loading = "lazy";
+ lazy.src = nonLazy.src;
+ document.body.appendChild(lazy);
+
+ await new Promise(resolve => setTimeout(resolve));
+
+ assert_equals(lazy.width, 256, "The list of available images should be checked before delaying the image load");
+ assert_equals(lazy.height, 256, "The list of available images should be checked before delaying the image load");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url-2.html
new file mode 100644
index 0000000000..e3a4a5f96e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url-2.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred loading=lazy images load relative to the document's base URL
+ at parse-time</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="author" title="Raj T" href="mailto:rajendrant@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<script>
+ const below_viewport_img = new ElementLoadPromise("below-viewport");
+
+ let has_window_loaded = false;
+
+ async_test(t => {
+ // Change the document's base URL to a bogus one, and scroll the
+ // below-viewport img into view. When it loads, it should load relative
+ // to the old base URL computed at parse-time.
+ window.addEventListener("load", t.step_func(() => {
+ window.history.pushState(2, document.title,
+ '/invalid-url-where-no-subresources-exist/')
+ has_window_loaded = true;
+ below_viewport_img.element().scrollIntoView();
+ }));
+
+ below_viewport_img.promise.then(t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "Below-viewport loading=lazy images do not block the " +
+ "window load event");
+ }));
+
+ below_viewport_img.promise.catch(
+ t.unreached_func("The image request should not load relative to the " +
+ "current (incorrect) base URL.")
+ );
+ }, "When a loading=lazy image is loaded, it loads relative to the " +
+ "document's base URL computed at parse-time.");
+</script>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <script>
+ // Change the document's base URL so that the img request parses relative
+ // to it when it sets up the request at parse-time.
+ window.history.pushState(1, document.title, 'resources/')
+ </script>
+ <img id="below-viewport" src="image.png?base-url-2" loading="lazy"
+ onload="below_viewport_img.resolve()"
+ onerror="below_viewport_img.reject()">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url.html
new file mode 100644
index 0000000000..01ce961d0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-base-url.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred images with loading='lazy' use the original
+ base URL specified at parse-time</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+ <base href='/html/semantics/embedded-content/the-img-element/resources/'>
+</head>
+
+<script>
+ const below_viewport_img = new ElementLoadPromise("below-viewport");
+
+ let has_window_loaded = false;
+
+ async_test(t => {
+ // At this point, the below-viewport image's request has been set-up, and
+ // its URL is the URL that was parsed relative to the document's base URL
+ // at this time. Any changes to the document's base URL from this point
+ // forward should not impact the image when we scroll it in-view. This is
+ // because the next step in the #updating-the-img-data algorithm is to to
+ // fetch the request that has already been set up. Now we'll change the
+ // document's base URL, and scroll the image in-view.
+ window.addEventListener("load", t.step_func(() => {
+ const base = document.querySelector('base');
+ base.href = '/invalid-url-where-no-subresources-exist/';
+ has_window_loaded = true;
+ below_viewport_img.element().scrollIntoView();
+ }));
+
+ below_viewport_img.promise.then(t.step_func_done(() => {
+ assert_true(has_window_loaded,
+ "Below-viewport loading=lazy images do not block the " +
+ "window load event");
+ }));
+
+ below_viewport_img.promise.catch(
+ t.unreached_func("The image request should not load relative to the " +
+ "current (incorrect) base URL.")
+ );
+ }, "When a loading=lazy image is loaded, it loads relative to the " +
+ "document's base URL computed at parse-time.");
+</script>
+
+<body>
+ <div style="height:1000vh"></div>
+ <img id="below-viewport" src="image.png?base-url" loading="lazy"
+ onload="below_viewport_img.resolve()"
+ onerror="below_viewport_img.reject()">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-below-viewport-dynamic.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-below-viewport-dynamic.html
new file mode 100644
index 0000000000..78f18f0c23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-below-viewport-dynamic.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<head>
+ <title>Below viewport images with loading='lazy' and changed to
+ loading='eager' load and do not block the window load event</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test("Test that below viewport images with loading='lazy' " +
+ "and changed to loading='eager' load and do not block " +
+ "the window load event.");
+
+ let has_below_viewport_loaded = false;
+ let has_window_loaded = false;
+
+ window.addEventListener("load", t.step_func(function() {
+ assert_false(has_window_loaded,
+ "The window load event should only fire once.");
+ has_window_loaded = true;
+ }));
+
+ const below_viewport_img_onload = t.step_func_done(function() {
+ assert_false(has_below_viewport_loaded,
+ "The in_viewport element should load only once.");
+ assert_true(has_window_loaded,
+ "The window load event should have fired before " +
+ "below_viewport loaded.");
+ has_below_viewport_loaded = true;
+ });
+</script>
+
+<body>
+ <div style="height:10000px;"></div>
+ <img id="below_viewport" src="resources/image.png?below-viewport-dynamic&pipe=trickle(d2)"
+ loading="lazy" onload="below_viewport_img_onload();">
+ <script>
+ assert_false(has_window_loaded,
+ "The window load event should not fire before " +
+ "changing below_viewport to loading='eager'.");
+ document.getElementById("below_viewport").loading = 'eager';
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path-ref.html
new file mode 100644
index 0000000000..05a60034ad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<img src="resources/image.png">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html
new file mode 100644
index 0000000000..55f134a701
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+<link rel="help" href="https://crbug.com/1308299">
+<link rel="match" href="image-loading-lazy-clip-path-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<img id=target loading="lazy"
+ src="resources/image.png"
+ style="vertical-align: middle; clip-path: polygon(0 0, 110% 0, 110% 110%, 0 110%, 0 0)">
+<script>
+ target.onload = takeScreenshot;
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-crossorigin-change.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-crossorigin-change.sub.html
new file mode 100644
index 0000000000..84efc7b0d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-crossorigin-change.sub.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred images with loading='lazy' use the latest crossorigin attribute</title>
+ <link rel="author" title="Raj T" href="mailto:rajendrant@chromium.org">
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<script>
+ const img = new ElementLoadPromise("cross-origin");
+
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func(() => {
+ // At this point the image's #updating-the-image-data algorithm has been
+ // invoked, and the image request has been deferred. The deferred
+ // cross-origin image request was created with the `no-cors` request mode,
+ // which would succeed to load the cross-origin image.
+ // While the request is deferred, we'll set the `crossorigin` attribute to a
+ // value that would cause the image request to fail. Since `crossorigin`
+ // mutations trigger another #updating-the-image-data invocation (replacing
+ // the first one), when we scroll the image into view, the image should be
+ // fetched with the latest `crossorigin` attribute value, and fail to load.
+ img.element().crossOrigin = 'anonymous';
+ img.element().scrollIntoView();
+ }));
+
+ img.promise
+ .then(t.unreached_func("The image should not load."))
+ .catch(t.step_func(() => { img.element().onload = t.step_func_done(); img.element().src = 'resources/image.png'; }));
+ }, "Test that when deferred image is loaded, it uses the latest crossorigin attribute.");
+</script>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <img id="cross-origin" loading="lazy"
+ src='http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/embedded-content/the-img-element/resources/image.png'
+ onload="img.resolve();" onerror="img.reject();">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https-ref.html
new file mode 100644
index 0000000000..05a60034ad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<img src="resources/image.png">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https.html
new file mode 100644
index 0000000000..809068dd05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-data-url-to-https.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>Lazy loaded Images with data url placeholders can be overwritten by a src change</title>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#update-the-image-data">
+ <link rel="match" href="image-loading-lazy-data-url-to-https-ref.html">
+ <script src="/common/reftest-wait.js"></script>
+</head>
+
+<body>
+ <img id="image" loading="lazy" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 872 490' width='872' height='490' style='background: green' %3E%3C/svg%3E">
+
+<script>
+ const image = document.querySelector('#image');
+
+ window.onload = function() {
+ // trigger intersection observer through forced layout.
+ image.offsetWidth;
+ image.setAttribute("src", 'resources/image.png');
+ setTimeout(() => { takeScreenshot(); }, 100);
+ };
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-different-crossorigin.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-different-crossorigin.html
new file mode 100644
index 0000000000..1ea46ffcc5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-different-crossorigin.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+<title>Lazyload images cannot load immediately from the list of available images if their tuple doesn't match other images in that list</title>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="author" title="Przemyslaw Gorszkowski" href="mailto:pgorszkowski@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- A `loading=lazy` image will be placed below this div so that it is below
+ the viewport -->
+<div id="img-container"></div>
+<div style="height: 1000vh;"></div>
+<div id="below-viewport-img-container"></div>
+
+<script>
+const image_path = 'resources/image.png?image-lazy-loading-lazy-different-crossorigin-' + Math.random();
+
+promise_test(async t => {
+ await new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = resolve;
+ img.onerror = e => { reject(new Error("The img should not fail to load")) };
+ document.querySelector('#img-container').append(img);
+ img.src = image_path;
+ });
+
+
+ // At this point, the image fetched eagerly above exists in the "list of
+ // available images". As per the spec's #updating-the-image-data algorithm [1]
+ // step 6, the "list of avalable images" is consulted before we take any
+ // lazyload-specific action. This means that lazyload images can load eagerly
+ // if they target a resource in the list of available images (includes cors
+ // settings attribute matching [2]). The image from "img-container" does not
+ // have "crossorigin" attribute and the lazyload image has crossorigin="anonymous".
+ // This means that lazyload image cannot be loaded eagerly from the list of available images.
+ // [1]: https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
+ // [2]: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attribute
+ const lazyload_image_promise = new Promise((resolve, reject) => {
+ const img = new Image();
+ img.loading = 'lazy';
+ img.crossOrigin = "anonymous";
+ img.onload = e => {
+ reject("The img should not be loaded from the list of available images because of different 'crossorigin'") };
+ img.onerror = e => { reject("The img should not fail to load") };
+
+ document.querySelector('#below-viewport-img-container').append(img);
+ img.src = image_path;
+ });
+ const timeout_promise = new Promise((resolve, reject) => {
+ t.step_timeout(() => {
+ resolve("The `loading=lazy` image should not load immediately from " +
+ "the list of available images because of different 'crossorigin'");
+ }, 2000);
+ });
+
+ // The `timeout_promise` should resolve first because lazyload image is not
+ // able to eagerly use resource from the "list of available images" if there
+ // is a difference in 'crossorigin'.
+ await Promise.race([lazyload_image_promise, timeout_promise]);
+}, "Lazyload images cannot load immediately from the list of available images if their tuple doesn't match other images in that list");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-empty-src.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-empty-src.html
new file mode 100644
index 0000000000..2a0aefea1d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-empty-src.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<title>Lazy loaded Images handle correctly when setting src to empty</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#update-the-image-data">
+<div id=log></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<img id="image" loading="lazy" src="resources/image.png">
+
+<script>
+ const image = document.querySelector('#image');
+
+async_test(function(t) {
+ image.onerror = t.step_func(function(e) {
+ assert_equals(e.type, "error", "null image source check failed");
+ image.onload = t.step_func(function() {
+ t.done();
+ });
+ image.src = "resources/image.png";
+ });
+ image.src = "";
+}, "lazy loaded image and empty src");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-001.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-001.sub.html
new file mode 100644
index 0000000000..eed3644650
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-001.sub.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<head>
+<title>A below-viewport loading=lazy image in a cross origin iframe loads only
+ when scrolled into viewport</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+</head>
+
+<iframe id="iframe" width="500px" height="500px"></iframe>
+
+<script>
+promise_test(t => {
+ iframe.src =
+ get_host_info().HTTP_NOTSAMESITE_ORIGIN +
+ new URL("resources/", self.location).pathname +
+ "image-loading-lazy-below-viewport.html";
+
+ // Wait for the frame to report that its window load event fired.
+ return new Promise(resolve => {
+ window.addEventListener("message",
+ event => resolve(event.data), {once: true});
+ }).then(iframe_message => {
+ assert_equals(iframe_message, "window_loaded",
+ "The loading=lazy image should not block the iframe's load " +
+ "event");
+
+ // Tell the iframe to scroll the image element into view.
+ frames[0].postMessage("scroll", "*");
+
+ return new Promise(resolve => {
+ window.addEventListener("message", event => resolve(event.data));
+ });
+ }).then(iframe_message => {
+ assert_equals(iframe_message, "image_loaded",
+ "The below-viewport loading=lazy image should load only " +
+ "once scrolled into the viewport");
+
+ }); // new Promise();
+}); // promise_test.
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-002.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-002.sub.html
new file mode 100644
index 0000000000..85060d2193
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-iframe-002.sub.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<head>
+<title>A loading=lazy image in a below-viewport cross-origin iframe loads only
+ when the cross-origin iframe is scrolled into view</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+</head>
+
+<div style="height:1000vh;"></div>
+<iframe id="iframe" width="500px" height="500px"></iframe>
+
+<script>
+promise_test(t => {
+ iframe.src =
+ get_host_info().HTTP_NOTSAMESITE_ORIGIN +
+ new URL("resources/", self.location).pathname +
+ "image-loading-lazy-in-viewport.html";
+
+ // Wait for the frame to report that its window load event fired.
+ return new Promise(resolve => {
+ window.addEventListener("message",
+ event => resolve(event.data), {once: true});
+ }).then(iframe_message => {
+ assert_equals(iframe_message, "window_loaded",
+ "The loading=lazy image should not block the iframe's load " +
+ "event");
+
+ // Scroll the iframe into view, which also puts the image into view.
+ iframe.scrollIntoView();
+
+ return new Promise(resolve => {
+ window.addEventListener("message", event => resolve(event.data));
+ });
+ }).then(iframe_message => {
+ assert_equals(iframe_message, "image_loaded",
+ "The below-viewport loading=lazy image should load only " +
+ "once scrolled into the viewport");
+
+ }); // new Promise().
+
+}); // promise_test().
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-script-disabled-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-script-disabled-iframe.html
new file mode 100644
index 0000000000..fbcadd86c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-script-disabled-iframe.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<head>
+<title>Images with loading='lazy' in script disabled iframe are not handled
+ as 'lazy'</title>
+<link rel="help" href="https://github.com/scott-little/lazyload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+
+<div style="height:1000vh;"></div>
+<iframe id="iframe" sandbox="allow-same-origin"
+ src="resources/image-loading-lazy-in-viewport.html">
+</iframe>
+<script>
+promise_test(async t => {
+ await new Promise(resolve => iframe.addEventListener("load", resolve));
+
+ const image = iframe.contentDocument.querySelector("img");
+
+ assert_true(image.complete,
+ "lazy-load image shouldn't be honored in script disabled iframe");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-far.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-far.html
new file mode 100644
index 0000000000..065fa939bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-far.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 10000vh;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images do not load when far from viewport."
+ );
+
+ function img_onload() {
+ t.unreached_func(
+ "Lazy-loading image far from viewport should not load."
+ )();
+ }
+
+ t.step_timeout(() => {
+ t.done();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal-far.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal-far.html
new file mode 100644
index 0000000000..20274310f1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal-far.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ display: flex;
+ }
+
+ #spacer {
+ width: 10000vw;
+ height: 130px;
+ flex-shrink: 0;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ flex-shrink: 0;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images do not load when far from viewport."
+ );
+
+ function img_onload() {
+ t.unreached_func(
+ "Lazy-loading image far from viewport should not load."
+ )();
+ }
+
+ t.step_timeout(() => {
+ t.done();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal.html
new file mode 100644
index 0000000000..124dbaddf8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-horizontal.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ display: flex;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ flex-shrink: 0;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ flex-shrink: 0;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function img_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for image to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-2.html
new file mode 100644
index 0000000000..5e25f45275
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-2.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller2>
+ <div id="spacer"></div>
+ <div id="scroller">
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function img_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for image to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-3.html
new file mode 100644
index 0000000000..0d6d95826b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-3.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ #scroller3 {
+ width: 120px;
+ height: 120px;
+ overflow: scroll;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller3>
+ <div id=scroller2>
+ <div id="scroller">
+ <div id="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+ </div>
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function img_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for image to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-4.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-4.html
new file mode 100644
index 0000000000..312e90bd49
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-4.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ .spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller2>
+ <div class="spacer"></div>
+ <div id="scroller">
+ <div class="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function img_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for image to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-5.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-5.html
new file mode 100644
index 0000000000..77bb3004d3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested-5.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ display: flex;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ .spacer {
+ width: 130px;
+ height: 130px;
+ flex-shrink: 0;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ flex-shrink: 0;
+ }
+</style>
+
+<div id=scroller2>
+ <div class="spacer"></div>
+ <div id="scroller">
+ <div class="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function img_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for image to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested.html
new file mode 100644
index 0000000000..cc3e4d5e4b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller-nested.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #scroller2 {
+ width: 110px;
+ height: 110px;
+ overflow: scroll;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id=scroller2>
+ <div id="scroller">
+ <div id="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+ </div>
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function img_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for image to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller.html
new file mode 100644
index 0000000000..17f0e7d17a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-scroller.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-root-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ #scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ background-color: gray;
+ }
+
+ #spacer {
+ width: 130px;
+ height: 130px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <img
+ id="target"
+ src="resources/green.png"
+ loading="lazy"
+ onload="img_onload();"
+ >
+</div>
+
+<script>
+ const t = async_test(
+ "Test that lazy-loaded images load when near viewport."
+ );
+
+ function img_onload() {
+ t.done();
+ }
+
+ t.step_timeout(() => {
+ t.unreached_func(
+ "Timed out while waiting for image to load."
+ )();
+ }, 2000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-viewport-dynamic.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-viewport-dynamic.html
new file mode 100644
index 0000000000..39dd5dc1e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-viewport-dynamic.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<head>
+ <title>In viewport images with loading='lazy' and changed to loading='eager'
+ do not block the window load event</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test("Test that in viewport images with loading='lazy' and " +
+ "changed to loading='eager' do not block the window " +
+ "load event.");
+
+ let has_in_viewport_loaded = false;
+ let has_window_loaded = false;
+
+ const in_viewport_img_onload = t.step_func_done(function() {
+ assert_false(has_in_viewport_loaded,
+ "The in_viewport element should load only once.");
+ assert_true(has_window_loaded,
+ "The window load event should fire before in_viewport image loads.");
+ has_in_viewport_loaded = true;
+ });
+
+ window.addEventListener("load", t.step_func(function() {
+ assert_false(has_window_loaded,
+ "The window load event should only fire once.");
+ has_window_loaded = true;
+ }));
+</script>
+
+<body>
+ <img id="in_viewport" src="resources/image.png?in-viewport-dynamic&pipe=trickle(d2)"
+ loading="lazy" onload="in_viewport_img_onload();">
+ <script>
+ document.getElementById("in_viewport").loading = 'eager';
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-document.html
new file mode 100644
index 0000000000..ff7e83105c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-document.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<title>Moving loading='lazy' image into another top level document</title>
+<link rel="help" href="https://github.com/scott-little/lazyload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+
+<div style="height:1000vh;"></div>
+<img loading="lazy"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAG0lEQVR42mP8z0A%2BYKJA76jmUc2jmkc1U0EzACKcASfOgGoMAAAAAElFTkSuQmCC">
+<script>
+promise_test(async t => {
+ let image_loaded = false;
+ const img = document.querySelector("img");
+ img.addEventListener("load", () => { image_loaded = true; });
+
+ await new Promise(resolve => window.addEventListener("load", resolve));
+
+ assert_false(image_loaded,
+ "lazy-load image shouldn't be loaded yet");
+
+ const anotherWin = window.open("resources/newwindow.html");
+
+ await new Promise(resolve => anotherWin.addEventListener("load", resolve));
+
+ anotherWin.document.body.appendChild(img);
+
+ assert_false(image_loaded,
+ "lazy-load image shouldn't be loaded yet");
+
+ img.scrollIntoView();
+
+ await new Promise(resolve => img.addEventListener("load", resolve));
+ assert_true(img.complete,
+ "Now the lazy-load image should be loaded");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-into-script-disabled-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-into-script-disabled-iframe.html
new file mode 100644
index 0000000000..79bfa24378
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-move-into-script-disabled-iframe.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<head>
+<title>A loading='lazy' image starts loading when the element is moved into
+ an iframe where script is disabled</title>
+<link rel="help" href="https://github.com/scott-little/lazyload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+
+<div style="height:1000vh;"></div>
+<iframe id="iframe" src="resources/image-loading-lazy-in-viewport.html">
+</iframe>
+<iframe id="sandboxediframe" sandbox="allow-same-origin">
+</iframe>
+<script>
+promise_test(async t => {
+ await new Promise(resolve => window.addEventListener('load', resolve));
+
+ const image = iframe.contentDocument.querySelector("img");
+
+ assert_false(image.complete, "lazy-load image shouldn't be loaded");
+
+ sandboxediframe.contentDocument.body.appendChild(image);
+ await new Promise(resolve => image.addEventListener("load", resolve));
+
+ assert_true(image.complete,
+ "lazy-load image shouldn't be honored in script disabled iframe");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multicol.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multicol.html
new file mode 100644
index 0000000000..20d52d4dfa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multicol.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<head>
+ <title>Images with loading='lazy' load when in the viewport</title>
+ <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test("Test that images with loading='lazy' under multicol load once they enter the viewport.");
+
+ let has_in_viewport_loaded = false;
+ let has_window_loaded = false;
+
+ const in_viewport_img_onload = t.step_func(function() {
+ assert_false(has_in_viewport_loaded, "The in_viewport element should load only once.");
+ has_in_viewport_loaded = true;
+ });
+
+ window.addEventListener("load", t.step_func_done(function() {
+ assert_true(has_in_viewport_loaded, "The in_viewport element should have loaded before window.load().");
+ assert_false(has_window_loaded, "The window load event should only fire once.");
+ has_window_loaded = true;
+ }));
+
+</script>
+
+<div class=texty style="column-count: 2; height: 300px">
+ <div style="border: 1px solid black">
+ <h2 style="column-span: all"></h2>
+ <img loading="lazy" src="resources/image.png?loading-lazy-multicol-first" width="160" height="120"
+ onload="in_viewport_img_onload()">
+ </div>
+</div>
+
+ <!--
+ This async script loads very slowly in order to ensure that, if the
+ below_viewport element has started loading, it has a chance to finish
+ loading before window load event fires, so that the test will dependably fail
+ in that case instead of potentially passing depending on how long different
+ resource fetches take.
+ -->
+ <script async src="/common/slow.py"></script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multiple-times.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multiple-times.html
new file mode 100644
index 0000000000..2d67150560
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-multiple-times.html
@@ -0,0 +1,60 @@
+<head>
+ <title>Images with loading='lazy' can be lazy loaded multiple times</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <!-- This is used to represent the top of the viewport, so we can scroll the
+ below-viewport image out-of-view later in the test -->
+ <div id="top_div"></div>
+ <div style="height:1000vh;"></div>
+ <img id="below_viewport" loading="lazy" src="resources/image.png?image-loading-lazy-multiple-times-first">
+
+<script>
+ const t = async_test("Images with loading='lazy' can be lazy loaded multiple times");
+ const image = document.querySelector('#below_viewport');
+ const top_div = document.querySelector('#top_div');
+
+ let has_window_load_fired = false;
+
+ // This should be triggered first.
+ window.addEventListener('load', t.step_func(() => {
+ has_window_load_fired = true;
+ // Scroll the loading=lazy below-viewport image into view, so that it loads.
+ image.scrollIntoView();
+ }));
+
+ image.onload = t.step_func(() => {
+ assert_true(has_window_load_fired,
+ "The loading=lazy below-viewport image should not block the " +
+ "window load event");
+ changeImageSourceAndScrollToTop();
+ });
+
+ function changeImageSourceAndScrollToTop() {
+ top_div.scrollIntoView();
+
+ // Allow some time for scroll back to top, since we don't
+ // want the image to still be in the viewport and trigger a
+ // load due to the scroll being slow.
+ t.step_timeout(() => {
+ // Lazily load a "different" image.
+ image.src = 'resources/image.png?image-loading-lazy-multiple-times-second';
+ image.onload =
+ t.unreached_func("The loading=lazy below-viewport image should lazily " +
+ "load its second image, and not load it eagerly when " +
+ "the `src` attribute is changed");
+
+ // In 1s, scroll the image *back* into view, and record that it loads
+ // successfully.
+ t.step_timeout(() => {
+ image.onload = t.step_func_done();
+ image.scrollIntoView();
+ }, 1000);
+ }, 500);
+ }
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-negative-margin.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-negative-margin.html
new file mode 100644
index 0000000000..875160b4ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-negative-margin.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<head>
+ <title>Images with loading='lazy' defers images in a hidden area as a result
+ of negative margins</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <script>
+ window.negative_margin_test =
+ async_test("A loading=lazy image that is pulled into an `overflow: hidden` " +
+ "area by a negative margin will not load because " +
+ "IntersectionObserver sees it as non-intersecting");
+
+ // If the `negative_margin` image in the DOM loads, the test should fail
+ // immediately.
+ window.negative_margin_onload =
+ negative_margin_test.step_func_done(
+ negative_margin_test.unreached_func("The image with a negative margin " +
+ "should never load"));
+ </script>
+
+ <div style="width: 200px; height: 200px; overflow: hidden;">
+ <img id="negative_margin" width="5px"; style="margin-left: -10000vw;"
+ loading="lazy" src="resources/image.png?loading-lazy-negative-margin"
+ onload="window.negative_margin_onload()">
+ </div>
+
+ <script>
+ const intersection_observer_promise = new Promise(resolve => {
+ function io_callback(entries) {
+ assert_equals(entries.length, 1);
+ resolve(entries[0].isIntersecting);
+ }
+
+ const options = {
+ root: document,
+ rootMargin: '0px',
+ threshold: 1.0,
+ }
+
+ const observer = new IntersectionObserver(io_callback, options);
+ observer.observe(document.querySelector('#negative_margin'));
+ });
+
+ const timeout_promise = new Promise(resolve => {
+ window.negative_margin_test.step_timeout(resolve, 500);
+ });
+
+ Promise.all([intersection_observer_promise, timeout_promise]).then(
+ window.negative_margin_test.step_func_done(values => {
+ assert_equals(values.length, 2);
+ assert_equals(values[0], false, "The IntersectionObserver sees that " +
+ "the image does not intersect the " +
+ "viewport");
+ }));
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-referrerpolicy-change.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-referrerpolicy-change.sub.html
new file mode 100644
index 0000000000..110c36cca7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-referrerpolicy-change.sub.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<head>
+ <title>Deferred loading=lazy images are fetched with the latest
+ `referrerpolicy` attribute</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="author" title="Raj T" href="mailto:rajendrant@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<script>
+ const below_viewport_img = new ElementLoadPromise("below_viewport_img");
+
+ async_test(function(t) {
+ // At this point the image's #updating-the-image-data algorithm has been
+ // invoked, and the image request has been deferred. The deferred request
+ // was created with the default referrer policy, which will result in a
+ // `Referer` header being sent when the request is finally made. The request
+ // is also for an image that the server will send a broken response for if
+ // the request has a `Referer` header.
+ // While the request is deferred, we'll set the `referrerpolicy` attribute
+ // to `no-referrer`, which would cause the image request to succeed. Since
+ // `referrerpolicy` mutations trigger another #updating-the-image-data
+ // invocation (replacing the first one), when we scroll the image into view,
+ // the image should be fetched with no `Referer` header, and succeed.
+ window.addEventListener("load", t.step_func(() => {
+ below_viewport_img.element().referrerPolicy = "no-referrer";
+ below_viewport_img.element().scrollIntoView();
+ }));
+
+ below_viewport_img.promise
+ .then(t.step_func_done())
+ .catch(t.unreached_func("The image request should successfully load"))
+ }, "Test that when a deferred image is loaded, it uses the latest referrerpolicy");
+</script>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <img id="below_viewport_img"
+ src="resources/referrer-checker-img.py?expected_referrer="
+ loading="lazy" referrerpolicy="unsafe-url"
+ onload="below_viewport_img.resolve();"
+ onerror="below_viewport_img.reject();">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-relevant-mutations.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-relevant-mutations.html
new file mode 100644
index 0000000000..3a2662451e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-relevant-mutations.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<head>
+ <title>Relevant mutations on deferred loading=lazy images should not trigger
+ a request</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ let below_viewport_1_loaded = false,
+ below_viewport_2_loaded = false,
+ below_viewport_3_loaded = false
+
+ // For general lazy loading behavior.
+ promise_test(() => {
+ // When the page loads, start the rest of the tests.
+ return new Promise(resolve => {
+ window.addEventListener("load", e => {
+ const kAssertion = 'image should never load';
+ assert_false(below_viewport_1_loaded, `below-viewport-1 ${kAssertion}`);
+ assert_false(below_viewport_2_loaded, `below-viewport-2 ${kAssertion}`);
+ assert_false(below_viewport_3_loaded, `below-viewport-3 ${kAssertion}`);
+ resolve();
+ });
+ });
+ }, "Images are lazyloaded");
+
+ // For `referrerPolicy` attribute mutations.
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ const below_viewport_1 = document.querySelector('img#below-viewport-1');
+ below_viewport_1.onload = reject;
+ below_viewport_1.onerror = reject;
+ t.step_timeout(resolve, 1000);
+
+ below_viewport_1.referrerPolicy = 'no-referrer';
+ });
+ }, "Image referrerPolicy mutation does not cause deferred loading=lazy " +
+ "images to be fetched");
+
+ // For `crossOrigin` attribute mutations.
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ const below_viewport_2 = document.querySelector('img#below-viewport-2');
+ below_viewport_2.onload = reject;
+ below_viewport_2.onerror = reject;
+ t.step_timeout(resolve, 1000);
+
+ below_viewport_2.crossOrigin = 'anonymous';
+ });
+ }, "Image crossOrigin mutation does not cause deferred loading=lazy " +
+ "images to be fetched");
+
+ // For `src` attribute mutations.
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ const below_viewport_3 = document.querySelector('img#below-viewport-3');
+ below_viewport_3.onload = reject;
+ below_viewport_3.onerror = reject;
+ t.step_timeout(resolve, 1000);
+
+ below_viewport_3.src = "resources/image.png?relevant-mutations-change";
+ });
+ }, "Image src mutation does not cause deferred loading=lazy " +
+ "images to be fetched");
+</script>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <img id="below-viewport-1" src="resources/image.png?relevant-mutations-1" loading="lazy"
+ onload="below_viewport_1_loaded = true"
+ onerror="below_viewport_1_loaded = true">
+
+ <img id="below-viewport-2" src="resources/image.png?relevant-mutations-2" loading="lazy"
+ onload="below_viewport_2_loaded = true"
+ onerror="below_viewport_2_loaded = true">
+
+ <img id="below-viewport-3" src="resources/image.png?relevant-mutations-3" loading="lazy"
+ onload="below_viewport_3_loaded = true"
+ onerror="below_viewport_3_loaded = true">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio-ref.html
new file mode 100644
index 0000000000..6de01a9b3b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio-ref.html
@@ -0,0 +1,2 @@
+<!doctype HTML>
+<span style="display: inline-block; width: 100px; height: 50px; border: 1px solid black">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html
new file mode 100644
index 0000000000..662ada6909
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <link rel="match" href="image-loading-lazy-slow-aspect-ratio-ref.html">
+ <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+ <script src="/common/reftest-wait.js"></script>
+ <img id=target loading="lazy"
+ width="200" height="100" style="width: 100px; height: auto; border: 1px solid black">
+<script>
+ let loaded = false;
+ target.onload = () => {
+ if (loaded) return;
+ loaded = true;
+ target.src = "";
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ target.src = "resources/image.png?slow-aspect-ratio&pipe=trickle(d2)";
+ takeScreenshot();
+ }));
+ };
+ target.src = "resources/image.png?slow-aspect-ratio";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-ref.html
new file mode 100644
index 0000000000..20fbb9b50e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<span style="display: inline-block; width: 330px; height: 254px; border: 1px solid black"></span>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html
new file mode 100644
index 0000000000..fac2c2e8f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <link rel="match" href="image-loading-lazy-slow-ref.html">
+ <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+
+ <script src="/common/reftest-wait.js"></script>
+ <img id=target loading="lazy"
+ width="330" height="254" style="border: 1px solid black">
+<script>
+ let loaded = false;
+ target.onload = () => {
+ if (loaded) return;
+ loaded = true;
+ target.src = "";
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ target.src = "resources/image.png?loading-lazy-slow&pipe=trickle(d2)";
+ takeScreenshot();
+ }));
+ };
+ target.src = "resources/image.png?loading-lazy-slow";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-srcset.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-srcset.html
new file mode 100644
index 0000000000..953d4af4ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-srcset.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<title>loading='lazy' image with srcset</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#update-the-image-data">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#will-lazy-load-image-steps">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<div style="height:1000vh;"></div>
+<img srcset="resources/image.png?loading-lazy-srcset" loading="lazy">
+<img loading="lazy" srcset="resources/image.png?loading-lazy-srcset">
+<script>
+promise_test(async t => {
+ let loaded_images = 0;
+ const imgs = document.querySelectorAll("img");
+ imgs.forEach(img => {
+ img.addEventListener("load", () => { loaded_images++; }, { once: true });
+ });
+
+ await new Promise(resolve => window.addEventListener("load", resolve));
+
+ assert_equals(loaded_images, 0,
+ "lazy-load images with srcset shouldn't be loaded yet");
+
+ const promises = [
+ new Promise(resolve => imgs[0].addEventListener("load", resolve)),
+ new Promise(resolve => imgs[1].addEventListener("load", resolve)),
+ ];
+
+ imgs[1].scrollIntoView();
+ await Promise.all(promises);
+
+ imgs.forEach(img => {
+ assert_true(img.complete,
+ "Now the lazy-load image with srcset should be loaded");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-subframe-detached-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-subframe-detached-crash.html
new file mode 100644
index 0000000000..86a290d50d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-subframe-detached-crash.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html class="test-wait">
+<title>Crash when detaching a frame during a lazy-load operation</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1619858">
+<iframe srcdoc=""></iframe>
+<script>
+onload = function() {
+ let frame = document.querySelector("iframe");
+ frame.contentDocument.body.innerHTML = `
+ <div style="height: 300vh"></div>
+ <img loading="lazy" src="/images/blue96x96.png" width=96 height=96>
+ `;
+ let img = frame.contentDocument.querySelector("img");
+ new IntersectionObserver(() => {
+ frame.remove();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.className = "";
+ });
+ });
+ }).observe(img);
+ frame.contentWindow.scrollTo(0, img.getBoundingClientRect().top);
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-to-eager.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-to-eager.html
new file mode 100644
index 0000000000..6246063981
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-to-eager.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<head>
+ <title>Below-viewport images with loading='lazy' load when set to
+ loading='eager' or the `loading` attribute is removed</title>
+ <link rel="author" title="Dom Farolino" href="mailto:domfarolino@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const t = async_test("Below-viewport images with loading='lazy' load when " +
+ "set to loading='eager' or the `loading` attribute is " +
+ "removed");
+
+ const img_1_onload = t.unreached_func("#img_1 should not load before the " +
+ "window load event");
+ const img_2_onload = t.unreached_func("#img_2 should not load before the " +
+ "window load event");
+
+ window.addEventListener("load", t.step_func(() => {
+ const img_1 = document.querySelector('#img_1');
+ const img_2 = document.querySelector('#img_2');
+
+ const img_1_promise = new Promise((resolve, reject) => {
+ img_1.onerror = reject;
+ img_1.onload = resolve;
+ });
+
+ const img_2_promise = new Promise((resolve, reject) => {
+ img_2.onerror = reject;
+ img_2.onload = resolve;
+ });
+
+ Promise.all([img_1_promise, img_2_promise])
+ .then(t.step_func_done())
+ .catch(t.unreached_func("The images should load successfully"));
+
+ // Kick off the requests.
+ img_1.loading = 'eager';
+ img_2.removeAttribute('loading'); // unset the attribute, putting it in
+ // the default (eager) state.
+ }));
+
+</script>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <img id="img_1"
+ src="resources/image.png?lazy-to-eager-1"
+ loading="lazy" onload="img_1_onload();">
+ <img id="img_2"
+ src="resources/image.png?lazy-to-eager-2"
+ loading="lazy" onload="img_2_onload();">
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-use-list-of-available-images.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-use-list-of-available-images.html
new file mode 100644
index 0000000000..f2a8a3542f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-use-list-of-available-images.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+<title>Lazyload images can load immediately from the list of available images</title>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- A `loading=lazy` image will be placed below this div so that it is below
+ the viewport -->
+<div style="height: 1000vh;"></div>
+<div id="below-viewport-img-container"></div>
+
+<script>
+const image_path = location.origin + '/html/semantics/embedded-content/the-img-element/resources/image.png?image-loading-lazy-use-list-of-available-images-' + Math.random();
+
+promise_test(async t => {
+ const eager_image_promise = new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = resolve;
+ img.onerror = e => { reject(new Error("The img should not fail to load")) };
+ img.src = image_path;
+ });
+
+ await eager_image_promise;
+
+ // At this point, the image fetched eagerly above exists in the "list of
+ // available images". As per the spec's #updating-the-image-data algorithm [1]
+ // step 6, the "list of avalable images" is consulted before we take any
+ // lazyload-specific action. This means that lazyload images can load eagerly
+ // if they target a resource in the list of available images, which the image
+ // below is doing.
+ //
+ // Note that if https://github.com/whatwg/html/issues/7005 resolves in favor
+ // of allowing in-flight image requests to be placed in the list of available
+ // images, as opposed to just complete images, this would allow lazyload
+ // images (in addition to non-lazyload ones) to coalesce with these in-flight
+ // entries in the list of available images too. In that case we'd need to test
+ // for this here.
+ // [1]: https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
+ const lazyload_image_promise = new Promise((resolve, reject) => {
+ const img = new Image();
+ img.loading = 'lazy';
+ img.onload = resolve;
+ img.onerror = e => { reject("The img should not fail to load") };
+
+ document.querySelector('#below-viewport-img-container').append(img);
+ img.src = image_path;
+ });
+ const timeout_promise = new Promise((resolve, reject) => {
+ t.step_timeout(() => {
+ reject(new Error("The `loading=lazy` image should load immediately from " +
+ "the list of available images, beating this timeout " +
+ "promise."));
+ }, 1000);
+ });
+
+ // The `lazyload_image_promise` should resolve first because lazyload images
+ // are able to eagerly use resources in the "list of available images".
+ await Promise.race([lazyload_image_promise, timeout_promise]);
+}, 'Lazyload images can load immediately from the list of available images');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-zero-intersection-area.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-zero-intersection-area.html
new file mode 100644
index 0000000000..9962ce7837
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy-zero-intersection-area.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Image with zero intersection area is lazy-loaded</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+<link rel="help" href="https://html.spec.whatwg.org/#lazy-load-intersection-observer">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1785186">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div style="height: 0; overflow: hidden;">
+ <img style="display: block" id=target loading="lazy" width="100" height="100">
+</div>
+<script>
+ async_test(function(t) {
+ target.addEventListener("load", t.step_func_done(function() {}));
+ target.src = "resources/image.png?zero-intersection-area";
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy.html
new file mode 100644
index 0000000000..88f6549d96
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-lazy.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<head>
+ <title>Images with loading='lazy' load only when in the viewport</title>
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <link rel="author" title="Scott Little" href="mailto:sclittle@chromium.org">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<script>
+ const in_viewport_test =
+ async_test("In-viewport loading=lazy images load immediately but do not " +
+ "block the window load event");
+ const below_viewport_test =
+ async_test("Below-viewport loading=lazy images only load when in the " +
+ "viewport and do not block the window load event");
+ const below_viewport_data_url_test =
+ async_test("Below-viewport data:url images only load when in the " +
+ "viewport and do not block the window load event");
+ const below_viewport_blob_url_test =
+ async_test("Below-viewport blob URL images only load when in the " +
+ "viewport and do not block the window load event");
+
+ document.addEventListener('DOMContentLoaded', e => {
+ const img = document.querySelector('#below_viewport_blob_url');
+
+ // Blob URL helper.
+ // Source: https://bl.ocks.org/nolanlawson/0eac306e4dac2114c752.
+ function fixBinary(bin) {
+ const length = bin.length;
+ const buf = new ArrayBuffer(length);
+ const arr = new Uint8Array(buf);
+ for (var i = 0; i < length; i++) {
+ arr[i] = bin.charCodeAt(i);
+ }
+
+ return buf;
+ }
+
+ const base64 =
+ "R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA";
+ const binary = fixBinary(atob(base64));
+ const blob = new Blob([binary], {type: 'image/png'});
+ const url = URL.createObjectURL(blob);
+ img.src = url;
+ }) // DOMContentLoaded.
+
+ let has_window_load_fired = false;
+ let has_in_viewport_loaded = false;
+
+ window.onload = e => {
+ has_window_load_fired = true;
+ }
+
+ // Helper assertion messages for the below tests.
+ const kScrollAssertion = "images only load when scrolled into view";
+ const kWindowLoadAssertion = "images do not block the load event";
+
+ const in_viewport_img_onload = in_viewport_test.step_func_done(() => {
+ has_in_viewport_loaded = true;
+ document.querySelector('#bottom').scrollIntoView();
+ assert_true(has_window_load_fired,
+ "In-viewport loading=lazy images do not block the window " +
+ "load event");
+ });
+
+ const below_viewport_img_onload = below_viewport_test.step_func_done(() => {
+ assert_true(has_in_viewport_loaded,
+ "Below-viewport loading=lazy images only load once loaded " +
+ "into the viewport");
+ assert_true(has_window_load_fired,
+ "Below-viewport loading=lazy images should not block the " +
+ "window load event");
+ });
+
+ const below_viewport_data_url_img_onload = below_viewport_data_url_test.step_func_done(() => {
+ assert_true(has_in_viewport_loaded,
+ "Below-viewport loading=lazy data: url images " +
+ kScrollAssertion);
+ assert_true(has_window_load_fired,
+ "Below-viewport loading=lazy data: url images " +
+ kWindowLoadAssertion);
+ });
+
+ const below_viewport_blob_url_img_onload = below_viewport_blob_url_test.step_func_done(() => {
+ assert_true(has_in_viewport_loaded,
+ "Below-viewport loading=lazy blob url images " +
+ kScrollAssertion);
+ assert_true(has_window_load_fired,
+ "Below-viewport loading=lazy blob url images " +
+ kWindowLoadAssertion);
+ });
+</script>
+
+<body>
+ <!-- |in_viewport| takes 2 seconds to load, so that in browsers that don't
+ support lazy loading, |below_viewport| finishes before |in_viewport|, and
+ the test will dependably fail without relying on a timeout. -->
+ <img id="in_viewport" loading="lazy" src="resources/image.png?image-loading-lazy-first&pipe=trickle(d2)"
+ onload="in_viewport_img_onload()">
+ <div style="height:1000vh;"></div>
+ <img id="below_viewport" loading="lazy" src="resources/image.png?image-loading-lazy-second"
+ onload="below_viewport_img_onload()">
+ <img id="below_viewport_data_url" loading="lazy"
+ src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA"
+ onload="below_viewport_data_url_img_onload()">
+ <!-- This image has its `src` set to a blob URL dynamically above -->
+ <img id="below_viewport_blob_url" loading="lazy"
+ onload="below_viewport_blob_url_img_onload()">
+ <div id="bottom"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip-ref.html
new file mode 100644
index 0000000000..f841dba31b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<style>
+* {
+ margin: 0;
+}
+</style>
+<html class="reftest-wait" style="overflow: hidden">
+ <head>
+ <title>Images with loading='lazy' load under subpixel-offset clips</title>
+ <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/#lazy-loading-attributes">
+ </head>
+ <div style="height: 44.5px"></div>
+ <div style="position: relative; font-size: 0; background: lightblue">
+ <img id=target loading="lazy" data-sizes="auto">
+ </div>
+</html>
+<script src="/common/reftest-wait.js"></script>
+<script>
+ target.onload = takeScreenshot;
+ target.src = "resources/image.png";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip.html
new file mode 100644
index 0000000000..594d9bebe4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<style>
+* {
+ margin: 0;
+}
+</style>
+<html class="reftest-wait" style="overflow: hidden">
+ <head>
+ <title>Images with loading='lazy' load under subpixel-offset clips</title>
+ <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/#lazy-loading-attributes">
+ <link rel="match" href="image-loading-subpixel-clip-ref.html">
+ </head>
+ <div style="height: 44.5px"></div>
+ <div style="overflow: hidden">
+ <div style="position: relative; font-size: 0; background: lightblue">
+ <img id=target loading="lazy" data-sizes="auto">
+ </div>
+ </div>
+</html>
+<script src="/common/reftest-wait.js"></script>
+<script>
+ target.onload = takeScreenshot;
+ target.src = "resources/image.png";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print-ref.html
new file mode 100644
index 0000000000..160f9f50a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<style>
+ body { margin: 0 }
+ img {
+ margin: 16px; /* to account for iframe + body margin in the test */
+ }
+</style>
+<img src="/images/green-100x50.png" alt="FAIL">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print.html
new file mode 100644
index 0000000000..4ad922ba00
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image-srcdoc-relative-uri-print.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1710822">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="match" href="image-srcdoc-relative-uri-print-ref.html">
+<iframe frameborder=0 srcdoc="<img src=/images/green-100x50.png>">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image.png b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image.png
new file mode 100644
index 0000000000..d26878c9f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/image.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-created-in-active-document-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-created-in-active-document-crash.html
new file mode 100644
index 0000000000..852375bff3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-created-in-active-document-crash.html
@@ -0,0 +1,6 @@
+<iframe id="i"></iframe>
+<script>
+var doc = i.contentDocument;
+i.remove();
+doc.createElement("img");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-picture-ancestor.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-picture-ancestor.html
new file mode 100644
index 0000000000..3518cab54d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-picture-ancestor.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>img should only look at a parent picture element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<picture>
+ <source media="not all" srcset="data:,a">
+ <source media="all" srcset="data:,b">
+ <img src="data:,c">
+ <picture>
+ <source media="not all" srcset="data:,e">
+ <source media="all" srcset="data:,f">
+ <img src="data:,g">
+ </picture>
+</picture>
+<script>
+const picture1 = document.querySelector("picture");
+const picture2 = document.querySelector("picture > picture");
+const img1 = document.querySelector("picture > img");
+const img2 = document.querySelector("picture > picture > img");
+
+const div = document.createElement("div");
+
+const imgInsideDiv = document.createElement("img");
+imgInsideDiv.src = "data:,d";
+div.append(imgInsideDiv);
+
+test(function() {
+ assert_equals(img1.currentSrc, "data:,b");
+}, "currentSrc of img in normally parented picture is correct");
+
+test(function() {
+ assert_equals(img2.currentSrc, "data:,f");
+}, "currentSrc of img in nested picture element is correct");
+
+async_test(function(t) {
+ picture1.append(div);
+ queueMicrotask(t.step_func(function() {
+ assert_equals(imgInsideDiv.currentSrc, "data:,d");
+ t.done();
+ }));
+}, "currentSrc of img with picture ancestor but non-picture parent is correct");
+
+async_test(function(t) {
+ picture2.remove();
+ queueMicrotask(t.step_func(function() {
+ assert_equals(img2.currentSrc, "data:,f");
+ t.done();
+ }));
+}, "currentSrc of img in nested picture element remains correct when the inner picture is removed from the document");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-src-in-synthetic-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-src-in-synthetic-document.html
new file mode 100644
index 0000000000..24c5567fb8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-src-in-synthetic-document.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9855">
+<link rel=help href="https://html.spec.whatwg.org/#reflecting-content-attributes-in-idl-attributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+test(() => {
+ const doc = document.implementation.createHTMLDocument('');
+ const img = doc.createElement('img');
+ img.setAttribute('src', '/test');
+ doc.body.appendChild(img);
+ assert_equals(img.src, '/test');
+}, 'HTMLImageElement.src should return the string from the attribute in about:blank documents.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size-ref.html
new file mode 100644
index 0000000000..56176c4b71
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<style>
+img {
+ width: 200px;
+ height: 100px;
+}
+</style>
+<img src="image.png">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size.html
new file mode 100644
index 0000000000..bdeae0ac2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img-with-containment-and-size.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Ensure images with containment and size are rendered properly</title>
+<meta charset="utf-8">
+<link rel="match" href="img-with-containment-and-size-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
+<style>
+img {
+ contain: paint;
+ width: 200px;
+ height: 100px;
+ will-change: transform;
+}
+</style>
+<script>
+onload = () => {
+ var i = new Image();
+ i.onload = function() {
+ document.body.appendChild(i);
+ document.documentElement.classList.remove("reftest-wait");
+ };
+ i.src = "image.png";
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img.complete.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img.complete.html
new file mode 100644
index 0000000000..d8d5a84eb7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/img.complete.html
@@ -0,0 +1,200 @@
+<!DOCTYPE HTML>
+<title>DOM img complete Test</title>
+<meta charset=UTF-8>
+<link rel="author" title="Anselm Hannemann" href="http://anselm-hannemann.com/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<img id="imgTestTag">
+<img src="" id="imgTestTag2">
+<img id="imgTestTag3" style="width: 80px; height:auto;">
+<img id="imgTestTag4">
+<img id="imgTestTag5">
+<div id="image-container"></div>
+
+<script>
+ var imageInstance = document.createElement('img');
+ imageInstance.style.display = 'none';
+
+ document.body.appendChild(imageInstance);
+</script>
+
+<div id="log"></div>
+<script>
+ test(function() {
+ assert_true(document.getElementById("imgTestTag").complete);
+ }, "img src and srcset omitted");
+
+ test(function() {
+ var img = document.createElement("img");
+ assert_true(img.complete);
+ }, "img src and srcset omitted on newly-created image");
+
+ test(function() {
+ var cont = document.getElementById("image-container");
+ this.add_cleanup(() => { cont.innerHTML = "" });
+ var img = document.createElement("img");
+ cont.appendChild(img);
+ assert_true(img.complete);
+ }, "img src and srcset omitted on newly-created-and-inserted image");
+
+ test(function() {
+ var cont = document.getElementById("image-container");
+ this.add_cleanup(() => { cont.innerHTML = "" });
+ cont.innerHTML = "<img>";
+ assert_true(cont.querySelector("img").complete);
+ }, "img src and srcset omitted on newly-created-via-innerHTML image");
+
+ test(function() {
+ assert_true(document.getElementById("imgTestTag2").complete);
+ }, "img src empty and srcset omitted");
+
+ test(function() {
+ var img = document.createElement("img");
+ img.setAttribute("src", "");
+ assert_true(img.complete);
+ }, "img src empty and srcset omitted on newly-created image");
+
+ test(function() {
+ var cont = document.getElementById("image-container");
+ this.add_cleanup(() => { cont.innerHTML = "" });
+ var img = document.createElement("img");
+ img.setAttribute("src", "");
+ cont.appendChild(img);
+ assert_true(img.complete);
+ }, "img src empty and srcset omitted on newly-created-and-inserted image");
+
+ test(function() {
+ var cont = document.getElementById("image-container");
+ this.add_cleanup(() => { cont.innerHTML = "" });
+ cont.innerHTML = "<img src=''>";
+ assert_true(cont.querySelector("img").complete);
+ }, "img src empty and srcset omitted on newly-created-via-innerHTML image");
+
+ test(function() {
+ var img = document.createElement("img");
+ img.src = location.href;
+ assert_false(img.complete, "Should have a load going");
+ img.removeAttribute("src");
+ assert_true(img.complete);
+ }, "img src and srcset omitted on image after it started a load");
+
+ // test if set to true after img is completely available
+ async_test(t => {
+ var loaded = false;
+ const img = document.getElementById("imgTestTag3");
+ img.onload = t.step_func_done(function(){
+ assert_false(loaded);
+ loaded = true;
+ assert_true(img.complete);
+ var currentSrc = img.currentSrc;
+ var expectedUrl = new URL("3.jpg", window.location);
+ assert_equals(new URL(currentSrc).pathname, expectedUrl.pathname);
+ }, "Only one onload, despite setting the src twice");
+
+ img.src = 'test' + Math.random();
+ //test if img.complete is set to false if src is changed
+ assert_false(img.complete, "src changed, should be set to false")
+ //change src again, should make only one request as per 'await stable state'
+ img.src = '3.jpg?nocache=' + Math.random();
+ }, "async src complete test");
+
+ async_test(t => {
+ var loaded = false;
+ const img = document.getElementById("imgTestTag5")
+ img.onload = t.step_func_done(function(){
+ assert_false(loaded);
+ loaded = true;
+ assert_true(img.complete);
+ }, "Only one onload, despite setting the srcset twice");
+ //Test if src, srcset is omitted
+ assert_true(img.complete)
+ img.srcset = "/images/green-256x256.png 1x";
+ //test if img.complete is set to false if srcset is present
+ assert_false(img.complete, "srcset present, should be set to false");
+ //change src again, should make only one request as per 'await stable state'
+ img.srcset="/images/green-256x256.png 1.6x"
+ }, "async srcset complete test");
+
+ // https://html.spec.whatwg.org/multipage/multipage/embedded-content-1.html#update-the-image-data
+ // says to "await a stable state" before fetching so we use a separate <script>
+ imageInstance.src = 'image-1.jpg?pipe=trickle(d1)&nocache=' + Math.random(); // make sure the image isn't in cache
+</script>
+<script>
+ // test: The final task that is queued by the networking task source once the resource has been fetched has been queued, but has not yet been run, and the img element is not in the broken state
+ test(function() {
+ assert_false(imageInstance.complete, "imageInstance.complete should be false");
+ var startTime = Date.now();
+ while (true) {
+ if (Date.now() - startTime > 2000) {
+ assert_false(imageInstance.complete, "imageInstance.complete should remain false");
+ break;
+ }
+ if (imageInstance.complete === true) {
+ assert_unreached(".complete should not change within a task");
+ }
+ }
+ },
+ 'IDL attribute complete cannot "randomly" change during a task');
+
+ // test if broken img does not pass
+ async_test(t => {
+ const img = document.getElementById("imgTestTag4");
+
+ img.src = 'brokenimg.jpg';
+
+ //test if img.complete is set to false if src is changed
+ assert_false(img.complete, "src changed to broken img, should be set to false");
+
+ img.onload = img.onerror = t.step_func_done(function(event){
+ assert_equals(event.type, "error");
+ assert_true(img.complete);
+ });
+ }, "async src broken test");
+
+ async_test(t => {
+ var img = document.createElement("img");
+ assert_true(img.complete);
+ img.src = `3.jpg?nocache=${Math.random()}`;
+ assert_false(img.complete);
+ img.onload = t.step_func_done(() => {
+ assert_true(img.complete);
+ img.removeAttribute("src");
+ assert_true(img.complete, "Should be complete, since we removed the src");
+ });
+ }, "async src removal test");
+
+ async_test(t => {
+ var img = document.createElement("img");
+ assert_true(img.complete);
+ img.srcset = `3.jpg?nocache=${Math.random()} 1x`;
+ assert_false(img.complete);
+ img.onload = t.step_func_done(() => {
+ assert_true(img.complete);
+ img.removeAttribute("srcset");
+ assert_true(img.complete, "Should be complete, since we removed the srcset");
+ });
+ }, "async srcset removal test");
+
+ async_test(t => {
+ var preload = document.createElement("img");
+ var url = `3.jpg?nocache=${Math.random()}`;
+ preload.src = url;
+ preload.onload = t.step_func_done(function() {
+ var img = document.createElement("img");
+ assert_true(img.complete);
+ img.src = url;
+ assert_true(img.complete, "Should be complete because we should hit the available image cache");
+ });
+ }, "async src available image lookup test");
+
+ async_test(t => {
+ var img = document.createElement("img");
+ img.src = `3.jpg?nocache=${Math.random()}`;
+ img.onload = t.step_func_done(function() {
+ assert_true(img.complete);
+ img.src = `3.jpg?nocache=${Math.random()}`;
+ assert_false(img.complete, "Should not be complete because we have started a new load");
+ });
+ }, "async pending request test");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invalid-src.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invalid-src.html
new file mode 100644
index 0000000000..37ea8ce754
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invalid-src.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Loading a non-parsing URL as an image should silently fail; triggering appropriate events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<img id=brokenurl />
+<img id=emptysrc />
+<script>
+async_test(function(t) {
+ var img = document.getElementById("brokenurl");
+ img.src = "http://[";
+
+ // The errors should be queued in the event loop, so they should only trigger
+ // after this block of code finishes, not during the img.src setter itself
+ img.addEventListener('error', t.step_func(function() {
+ t.step_timeout(t.step_func_done(), 0);
+ }));
+}, 'src="http://["');
+
+async_test(function(t) {
+ var img = document.getElementById("emptysrc");
+ img.src = "";
+
+ // Setting src to empty string triggers only error event.
+ // The errors should be queued in the event loop, so they should only trigger
+ // after this block of code finishes, not during the img.src setter itself
+ img.addEventListener('error', t.step_func(function() {
+ t.step_timeout(t.step_func_done(), 0);
+ }));
+}, 'src=""');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invisible-image.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invisible-image.html
new file mode 100644
index 0000000000..35ebbaa11a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/invisible-image.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<head>
+ <title>Test that below-viewport invisible images that are not marked
+ loading=lazy still load, and block the window load event</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="common.js"></script>
+</head>
+
+<body>
+ <div style="height:1000vh;"></div>
+ <img id="visibility_hidden" style="visibility:hidden;" src='resources/image.png?1'>
+ <img id="visibility_hidden_explicit_eager" style="visibility:hidden;" src='resources/image.png?2'
+ loading="eager">
+
+ <img id="display_none" style="display:none;" src='resources/image.png?3'>
+ <img id="display_none_explicit_eager" style="display:none;" src='resources/image.png?4'
+ loading="eager">
+
+ <img id="attribute_hidden" hidden src='resources/image.png?5'>
+ <img id="attribute_hidden_explicit_eager" hidden src='resources/image.png?6'
+ loading="eager">
+
+ <img id="js_display_none" src='resources/image.png?7'>
+ <img id="js_display_none_explicit_eager" src='resources/image.png?8'
+ loading="eager">
+ <script>
+ document.getElementById("js_display_none").style = 'display:none;';
+
+ const visibility_hidden_element = document.getElementById("visibility_hidden");
+ const visibility_hidden_element_explicit_eager =
+ document.getElementById("visibility_hidden_explicit_eager");
+
+ const display_none_element = document.getElementById("display_none");
+ const display_none_element_explicit_eager =
+ document.getElementById("display_none_explicit_eager");
+
+ const attribute_hidden_element = document.getElementById("attribute_hidden");
+ const attribute_hidden_element_explicit_eager =
+ document.getElementById("attribute_hidden_explicit_eager");
+
+ const js_display_none_element = document.getElementById("js_display_none");
+ const js_display_none_element_explicit_eager =
+ document.getElementById("js_display_none_explicit_eager");
+
+ let have_images_loaded = false;
+
+ async_test(t => {
+ let image_fully_loaded_promise = (element) => {
+ return new Promise(resolve => {
+ element.addEventListener("load", t.step_func(resolve));
+ });
+ }
+
+ Promise.all([
+ image_fully_loaded_promise(visibility_hidden_element),
+ image_fully_loaded_promise(visibility_hidden_element_explicit_eager),
+ image_fully_loaded_promise(display_none_element),
+ image_fully_loaded_promise(display_none_element_explicit_eager),
+ image_fully_loaded_promise(attribute_hidden_element),
+ image_fully_loaded_promise(attribute_hidden_element_explicit_eager),
+ image_fully_loaded_promise(js_display_none_element),
+ image_fully_loaded_promise(js_display_none_element_explicit_eager)
+ ]).then(t.step_func(() => {
+ have_images_loaded = true;
+ })).catch(t.unreached_func("All images should load correctly"));
+
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_true(have_images_loaded,
+ "The images should block the window load event.");
+ }));
+
+ }, "Test that below-viewport invisible images that are not marked " +
+ "loading=lazy still load, and block the window load event");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-after.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-after.html
new file mode 100644
index 0000000000..bb4c5991c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-after.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+ <head>
+ <title>img ismap attribute coordinate origin</title>
+ <style>
+ #bg { background-color: lightgray; position: relative; }
+ #target { position: absolute; width: 48px; height: 48px; border: 2px dashed green; pointer-events: none; }
+ .after { top: 246px; left: 246px; }
+ img { margin: 50px; border: 50px solid white; padding: 50px; }
+ </style>
+ </head>
+ <body>
+ <div id="bg">
+ <div id="target" class="after"></div>
+ <a href="/somewhere/">
+ <img src="/images/blue96x96.png" ismap>
+ </a>
+ </div>
+ <h1>Click inside the dashed rectangle</h1>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-before.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-before.html
new file mode 100644
index 0000000000..8349b62783
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-before.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+ <head>
+ <title>img ismap attribute coordinate origin</title>
+ <style>
+ #bg { background-color: lightgray; position: relative; }
+ #target { position: absolute; width: 96px; height: 96px; border: 2px dashed green; pointer-events: none; }
+ .before { top: 50px; left: 50px; }
+ img { margin: 50px; border: 50px solid white; padding: 50px; }
+ </style>
+ </head>
+ <body>
+ <div id="bg">
+ <div id="target" class="before"></div>
+ <a href="/somewhere/">
+ <img src="/images/blue96x96.png" ismap>
+ </a>
+ </div>
+ <h1>Click inside the dashed rectangle</h1>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-inside.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-inside.html
new file mode 100644
index 0000000000..fdecee9ace
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-iframe-inside.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+ <head>
+ <title>img ismap attribute coordinate origin</title>
+ <style>
+ #bg { background-color: lightgray; position: relative; }
+ #target { position: absolute; width: 96px; height: 96px; border: 2px dashed green; pointer-events: none; }
+ .in { top: 148px; left: 148px; }
+ img { margin: 50px; border: 50px solid white; padding: 50px; }
+ </style>
+ </head>
+ <body>
+ <div id="bg">
+ <div id="target" class="in"></div>
+ <a href="/common/blank.html">
+ <img src="/images/blue96x96.png" ismap>
+ </a>
+ </div>
+ <h1>Click inside the dashed rectangle</h1>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-manual.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-manual.html
new file mode 100644
index 0000000000..4d77e677e4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/ismap/img-ismap-coordinates-manual.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<html>
+ <head>
+ <title>img ismap attribute coordinate origin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ iframe { width: 500px; height: 500px; }
+ </style>
+ </head>
+ <body>
+ <iframe></iframe>
+ <div id="log"></div>
+ <script type="text/javascript">
+ tests = [
+ {
+ file: "img-ismap-coordinates-iframe-inside.html",
+ },
+ {
+ test: async_test("Coordinates within the content box of an image map have origin of the context box"),
+ resultMinXY: 0,
+ resultMaxXY: 96,
+ },
+ {
+ file: "img-ismap-coordinates-iframe-before.html",
+ },
+ {
+ test: async_test("Coordinates within the margin/padding (top-left) of the image map are clamped to zero"),
+ resultMinXY: 0,
+ resultMaxXY: 0,
+ },
+ {
+ file: "img-ismap-coordinates-iframe-after.html",
+ },
+ {
+ test: async_test("Coordinates within the margin/padding (bottom-right) of the image map have origin in the content box"),
+ resultMinXY: 97,
+ resultMaxXY: 146,
+ }
+ ];
+ testIndex = 0;
+
+ var iframe = document.querySelector('iframe');
+ iframe.onload = function testInit() {
+ if (testIndex % 2 == 0) {
+ testIndex++;
+ return;
+ }
+ // User clicked on a results...
+ var url = iframe.contentWindow.location.toString();
+ var test = tests[testIndex].test;
+ var minXY = tests[testIndex].resultMinXY;
+ var maxXY = tests[testIndex].resultMaxXY;
+ testIndex++;
+ if (testIndex < tests.length)
+ iframe.src = tests[testIndex].file; // Advance the test...
+ // Validate the last test's results...
+ test.step(function () {
+ var i = url.indexOf("?");
+ assert_not_equals(i, -1);
+ var coordsStr = url.substr(i+1);
+ var i = coordsStr.indexOf(',');
+ assert_not_equals(i, -1);
+ var x = parseFloat(coordsStr.substring(0, i));
+ var y = parseFloat(coordsStr.substring(i+1));
+ assert_greater_than_equal(x, minXY);
+ assert_less_than_equal(x, maxXY);
+ assert_greater_than_equal(y, minXY);
+ assert_less_than_equal(y, maxXY);
+ test.done();
+ });
+ if (testIndex >= tests.length)
+ iframe.style.display = "none";
+ }
+ iframe.src = tests[0].file;
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-does-not-coalesce-in-flight-requests.sub.tentative.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-does-not-coalesce-in-flight-requests.sub.tentative.html
new file mode 100644
index 0000000000..52e91bc087
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-does-not-coalesce-in-flight-requests.sub.tentative.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+<title>List of available images does not coalesce in-flight requests</title>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-list-of-available-images">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+const uuid = "{{uuid()}}";
+const path = location.origin + '/html/semantics/embedded-content/the-img-element/resources/image-and-stash.py';
+
+promise_test(async t => {
+ let first_image_promise = new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = resolve;
+ img.onerror = e => { reject(new Error("The img should not fail to load")) };
+ img.src = path + `?increment=${uuid}&pipe=trickle(d1)`;
+ });
+
+ // As of right now, the spec's #updating-the-image-data step 6 [1] does not
+ // place images into the "list of available images" until they are completely
+ // downloaded and the `load` event is fired. The spec also explicitly states
+ // that coalescing in-flight image requests is not what the list of available
+ // images is for. This test asserts this behavior, though since no browsers
+ // follow this behavior (they all allow coalescing in-flight requests for the
+ // same image resource) we've started discussion about this in
+ // https://github.com/whatwg/html/issues/7005. If that issue resolves in
+ // letting in-flight requests into the list of available images, then we
+ // should changes the expectations of this test.
+ //
+ // [1]: https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
+ let second_image_promise = new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = resolve;
+ img.onerror = e => { reject("The img should not fail to load") };
+ img.src = path + `?increment=${uuid}&pipe=trickle(d1)`;
+ });
+
+ await Promise.all([first_image_promise, second_image_promise]);
+ const response = await fetch(path + `?read=${uuid}`);
+ const request_count = await response.text();
+
+ assert_equals(request_count, "2", "The server should have seen exactly two " +
+ "requests, since the second image request " +
+ "above did not coalesce with the first " +
+ "in-flight one");
+}, 'list of available images does not coalesce in-flight requests');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-matching.https.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-matching.https.html
new file mode 100644
index 0000000000..4843d21915
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/list-of-available-images-matching.https.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<html>
+<title>List of available images tuple-matching logic</title>
+<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-list-of-available-images">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+
+<script>
+const path = location.origin + '/html/semantics/embedded-content/the-img-element/';
+const image_url = path + 'image-1.jpg';
+
+function syncWait(ms) {
+ const start = Date.now();
+ while (Date.now() - start < ms);
+}
+
+let sawNoCorsRequest = false;
+
+navigator.serviceWorker.onmessage = ({data}) => {
+ if (data.url === image_url && data.mode === 'no-cors') {
+ sawNoCorsRequest = true;
+ }
+};
+
+promise_test(t => {
+ return service_worker_unregister_and_register(t, 'resources/sw.js', path)
+ .then(r => {
+ return wait_for_state(t, r.installing, 'activated');
+ });
+}, 'registering service worker');
+
+promise_test(async t => {
+ const img = new Image();
+
+ function load_img_promise() {
+ return new Promise((resolve, reject) => {
+ img.onload = resolve;
+ img.onerror = e => { reject("The img should not fail to load") };
+
+ img.src = image_url;
+ // If there is not a matching image in the list of available images, the
+ // actual fetch operation is queued as a microtask, so we will see a
+ // request with mode 'cors' due to setting the crossorigin attribute
+ // below.
+ syncWait(500);
+ img.crossOrigin = 'anonymous';
+ });
+ };
+
+ await load_img_promise();
+ assert_false(sawNoCorsRequest, "The image is not fetched with mode: no-cors");
+ await new Promise(resolve => {
+ img.onload = img.onerror = resolve;
+ img.src = '';
+ img.crossOrigin = null;
+ });
+ await load_img_promise();
+ assert_false(sawNoCorsRequest, "The image is not fetched with mode: no-cors");
+
+}, 'list of available images tuple-matching logic');
+
+promise_test(t => {
+ return service_worker_unregister(t, path);
+}, 'unregistering service worker');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/move-element-and-scroll.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/move-element-and-scroll.html
new file mode 100644
index 0000000000..3c95fae5bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/move-element-and-scroll.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<head>
+ <title>Images with loading='lazy' load being moved to another document
+ and then scrolled to</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="common.js"></script>
+</head>
+
+<body>
+ <div id="tall_div" style="height:1000vh"></div>
+ <div id="below_viewport_div"></div>
+ <img id="below_viewport" src='resources/image.png?below_viewport' loading="lazy">
+
+ <script>
+ const tall_div = document.getElementById("tall_div");
+ const below_viewport_element = document.getElementById("below_viewport");
+ const below_viewport_div = document.getElementById("below_viewport_div");
+
+ async_test(function(t) {
+ below_viewport_element.onload =
+ t.unreached_func("The below viewport image should not load");
+ t.step_timeout(t.step_func_done(), 1000);
+ const iframe = document.createElement('iframe');
+ iframe.setAttribute("style", "display:none");
+ iframe.srcdoc = "<body></body>";
+ iframe.onload = () => {
+ const adopted_img = iframe.contentDocument.adoptNode(below_viewport_element);
+ iframe.contentDocument.body.appendChild(adopted_img);
+ below_viewport_div.scrollIntoView();
+ };
+ document.body.insertBefore(iframe, tall_div);
+ }, "Test that <img> below viewport is not loaded when moved to another " +
+ "document and then scrolled to");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/natural-size-orientation.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/natural-size-orientation.html
new file mode 100644
index 0000000000..662dc0804f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/natural-size-orientation.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>naturalWidth and naturalHeight on HTMLImageElement reflect orientation metadata</title>
+<link rel="author" title="Cameron McCormack" href="mailto:cam@mcc.id.au">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.ignore-orientation { image-orientation: none; }
+</style>
+<body>
+<script>
+async_test(function(t) {
+ let img = document.createElement("img");
+ img.src = "/images/green-100x50.png";
+ img.onload = t.step_func_done(function() {
+ assert_equals(img.naturalWidth, 100);
+ assert_equals(img.naturalHeight, 50);
+ img.remove();
+ });
+ document.body.append(img);
+}, "naturalWidth and naturalHeight return correct values for an image without orientation metadata");
+
+async_test(function(t) {
+ let img = document.createElement("img");
+ img.src = "/images/arrow-oriented-upright.jpg";
+ img.onload = t.step_func_done(function() {
+ assert_equals(img.naturalWidth, 144);
+ assert_equals(img.naturalHeight, 240);
+ img.remove();
+ });
+ document.body.append(img);
+}, "naturalWidth and naturalHeight return re-oriented values for an image with orientation metadata");
+
+async_test(function(t) {
+ let img = document.createElement("img");
+ img.src = "/images/arrow-oriented-upright.jpg";
+ img.className = "ignore-orientation";
+ img.onload = t.step_func_done(function() {
+ assert_equals(img.naturalWidth, 144);
+ assert_equals(img.naturalHeight, 240);
+ img.remove();
+ });
+ document.body.append(img);
+}, "naturalWidth and naturalHeight return re-oriented values for an image with orientation metadata even with image-orientation:none");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/non-active-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/non-active-document.html
new file mode 100644
index 0000000000..6072138cb3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/non-active-document.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>img in non-active document should not perform loads</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+
+<!-- Per load the image so that any loads in this test would be cached. -->
+<img src=/images/green-1x1.png>
+
+<!-- tests -->
+<template>
+<img>
+</template>
+
+<script>
+
+onload = function() {
+ async_test(function(t) {
+ var p = new DOMParser();
+ var d = p.parseFromString('<img>', 'text/html');
+ var i = d.querySelector('img');
+ i.onerror = t.unreached_func('got unexpected error event');
+ i.onload = t.unreached_func('got unexpected load event');
+ i.src = '/images/green-1x1.png';
+ // delay to ensure there is no load/error event fired.
+ t.step_timeout(t.step_func_done(), 0);
+ }, "DOMParser");
+
+ async_test(function(t) {
+ var d = document.implementation.createHTMLDocument('');
+ d.body.innerHTML = '<img>';
+ var i = d.querySelector('img');
+ i.onerror = this.unreached_func('got unexpected error event');
+ i.onload = this.unreached_func('got unexpected load event');
+ i.src = '/images/green-1x1.png';
+ // delay to ensure there is no load/error event fired.
+ t.step_timeout(t.step_func_done(), 0);
+ }, "createHTMLDocument");
+
+ async_test(function(t) {
+ var template = document.querySelector('template');
+ var i = template.content.querySelector('img');
+ i.onerror = this.unreached_func('got unexpected error event');
+ i.onload = this.unreached_func('got unexpected load event');
+ i.src = '/images/green-1x1.png';
+ // delay to ensure there is no load/error event fired.
+ t.step_timeout(t.step_func_done(), 0);
+ }, "<template>");
+};
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/nonexistent-image.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/nonexistent-image.html
new file mode 100644
index 0000000000..f58569ede0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/nonexistent-image.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Loading an nonexisting image should fail; triggering appropriate events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<img>
+
+<script>
+ async_test(function(t) {
+ var img = document.querySelector("img");
+ img.onload = this.step_func_done(function() {
+ assert_unreached("image.onload() was not supposed to be called");
+ });
+ img.onerror = this.step_func_done(function(e) {
+ assert_equals(e.type, "error", "image.onerror() called");
+ t.done();
+ });
+ img.src = "404";
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-below-viewport-image-loading-lazy.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-below-viewport-image-loading-lazy.html
new file mode 100644
index 0000000000..401771565a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-below-viewport-image-loading-lazy.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<head>
+ <title>Below-viewport loading=lazy not-rendered images should never load,
+ even when scrolled into view</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<body>
+ <!-- These images must not attempt to load when scrolled into the
+ viewport -->
+ <img id="display_none" style="display:none;" src="resources/image.png?not-rendered-2" loading="lazy"
+ onload="display_none_img.resolve();" onerror="display_none_img.reject();">
+ <img id="attribute_hidden" hidden src="resources/image.png?not-rendered-3" loading="lazy"
+ onload="attribute_hidden_img.resolve();" onerror="attribute_hidden_img.reject();">
+ <img id="js_display_none" src="resources/image.png?not-rendered-4" loading="lazy"
+ onload="js_display_none_img.resolve();" onerror="js_display_none_img.reject();">
+ <script>
+ document.getElementById("js_display_none").style = 'display:none;';
+ </script>
+
+ <!-- Later in the test we'll scroll to this div, instead of the above images,
+ since due to them not being rendered, they cannot be scrolled to -->
+ <div id="rendered_div"></div>
+</body>
+
+<script>
+ const display_none_img = new ElementLoadPromise("display_none");
+ const attribute_hidden_img = new ElementLoadPromise("attribute_hidden");
+ const js_display_none_img = new ElementLoadPromise("js_display_none");
+ const rendered_div_element = document.querySelector('#rendered_div');
+
+ async_test(t => {
+ window.addEventListener("load", t.step_func(() => {
+ rendered_div.scrollIntoView();
+ }));
+
+ const unreached_not_rendered_img_func =
+ t.unreached_func("The not-rendered below-viewport loading=lazy images " +
+ "should not attempt to load.");
+
+ display_none_img.promise
+ .then(unreached_not_rendered_img_func)
+ .catch(unreached_not_rendered_img_func);
+
+ attribute_hidden_img.promise
+ .then(unreached_not_rendered_img_func)
+ .catch(unreached_not_rendered_img_func);
+
+ js_display_none_img.promise
+ .then(unreached_not_rendered_img_func)
+ .catch(unreached_not_rendered_img_func);
+
+ // If none of the above images load after being scrolled to within the below
+ // timeout, the test passes.
+ t.step_timeout(t.done, 2000);
+ }, "Below-viewport loading=lazy not-rendered images should never load, " +
+ "even when scrolled into view");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-dimension-getter.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-dimension-getter.html
new file mode 100644
index 0000000000..4d929fd8b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-dimension-getter.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Image intrinsic dimensions are returned if the image isn't rendered</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-img-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="container" style="display: none">
+</div>
+<script>
+async_test(function(t) {
+ var img = document.createElement('img');
+ img.onload = t.step_func_done(function() {
+ assert_equals(img.width, 389, "intrinsic width should've been returned")
+ assert_equals(img.height, 590, "intrinsic height should've been returned")
+ document.getElementById('container').appendChild(img);
+ assert_equals(img.width, 389, "intrinsic width should've been returned");
+ assert_equals(img.height, 590, "intrinsic height should've been returned");
+ });
+ img.src = "image-1.jpg";
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-image-loading-lazy.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-image-loading-lazy.html
new file mode 100644
index 0000000000..25aaedb2b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/not-rendered-image-loading-lazy.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<head>
+ <title>In-viewport loading=lazy not-rendered images should never load</title>
+ <link rel="author" title="Rob Buis" href="mailto:rbuis@igalia.com">
+ <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<body>
+ <!-- These images must not attempt to load -->
+ <img id="display_none" style="display:none;" src="resources/image.png?not-rendered-image-loading-lazy-2" loading="lazy"
+ onload="display_none_img.resolve();" onerror="display_none_img.reject();">
+ <img id="attribute_hidden" hidden src="resources/image.png?not-rendered-image-loading-lazy-3" loading="lazy"
+ onload="attribute_hidden_img.resolve();" onerror="attribute_hidden_img.reject();">
+ <img id="js_display_none" src="resources/image.png?not-rendered-image-loading-lazy-4" loading="lazy"
+ onload="js_display_none_img.resolve();" onerror="js_display_none_img.reject();">
+ <script>
+ document.getElementById("js_display_none").style = 'display:none;';
+ </script>
+</body>
+
+<script>
+ const display_none_img = new ElementLoadPromise("display_none");
+ const attribute_hidden_img = new ElementLoadPromise("attribute_hidden");
+ const js_display_none_img = new ElementLoadPromise("js_display_none");
+
+ async_test(t => {
+ const unreached_not_rendered_img_func =
+ t.unreached_func("The not-rendered in-viewport loading=lazy images " +
+ "should not attempt to load.");
+
+ display_none_img.promise
+ .then(unreached_not_rendered_img_func)
+ .catch(unreached_not_rendered_img_func);
+
+ attribute_hidden_img.promise
+ .then(unreached_not_rendered_img_func)
+ .catch(unreached_not_rendered_img_func);
+
+ js_display_none_img.promise
+ .then(unreached_not_rendered_img_func)
+ .catch(unreached_not_rendered_img_func);
+
+ t.step_timeout(t.done, 2000);
+ }, "In-viewport loading=lazy not-rendered images should never load");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/null-image-source.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/null-image-source.html
new file mode 100644
index 0000000000..8999276503
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/null-image-source.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Null image source check for src, srcset and picture parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<img id="src_id" src="">
+<img id="srcset_id" srcset="">
+<picture><img id="parent_picture_id"></picture>
+<script>
+async_test(function(t) {
+ img = document.getElementById('src_id');
+ img.onerror = t.step_func(function(e) {
+ assert_equals(e.type, "error", "null image source check failed");
+ t.done();
+ });
+}, "img with empty src");
+
+async_test(function(t) {
+ img = document.getElementById('srcset_id');
+ img.onerror = t.unreached_func("empty srcset fires an error");
+ t.step_timeout(function() { t.done(); }, 2000);
+}, "img with empty srcset");
+
+async_test(function(t) {
+ img = document.getElementById('parent_picture_id');
+ img.onerror = t.unreached_func("null img with picture parent fires an error");
+ t.step_timeout(function() { t.done(); }, 2000);
+}, "img with picture parent");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/picture-loading-lazy.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/picture-loading-lazy.html
new file mode 100644
index 0000000000..08c01616bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/picture-loading-lazy.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<head>
+ <title>Images with loading='lazy' in picture elements load when near the viewport</title>
+ <link rel="author" title="Raj T" href="mailto:rajendrant@chromium.org">
+ <link rel="help" href="https://github.com/scott-little/lazyload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/common.js"></script>
+</head>
+
+<script>
+const in_viewport_img = new ElementLoadPromise("in_viewport_img");
+const lazy_attribute_img = new ElementLoadPromise("lazy_attribute_img");
+const eager_attribute_img = new ElementLoadPromise("eager_attribute_img");
+
+const document_load_promise = new Promise(resolve => {
+ window.addEventListener("load", resolve);
+});
+
+async_test(function(t) {
+ document_load_promise.then(t.step_func_done(function() {
+ assert_false(lazy_attribute_img.element().complete);
+ lazy_attribute_img.element().scrollIntoView();
+ }));
+}, "Test that the loading=lazy <picture> element below viewport was deferred, on document load.");
+
+async_test(function(t) {
+ in_viewport_img.promise.then(t.step_func_done());
+}, "Test that in viewport <picture> element was loaded");
+
+async_test(function(t) {
+ eager_attribute_img.promise.then(t.step_func_done());
+}, "Test that eager <picture> element was loaded");
+
+async_test(function(t) {
+ lazy_attribute_img.promise.then(t.step_func_done());
+}, "Test that deferred <picture> element was loaded-in as well, after scrolled down");
+
+</script>
+
+<body>
+<picture>
+ <source sizes='50vw' srcset='resources/image.png?in_viewport_img'>
+ <img id='in_viewport_img' src='img-not-loaded.png' loading="lazy" onload="in_viewport_img.resolve();">
+</picture>
+<div style="height:10000px;"></div>
+<picture>
+ <source sizes='50vw' srcset='resources/image.png?lazy_attribute_img'>
+ <img id='lazy_attribute_img' src='img-not-loaded.png' loading="lazy" onload="lazy_attribute_img.resolve();">
+</picture>
+<picture>
+ <source sizes='50vw' srcset='resources/image.png?eager_attribute_img'>
+ <img id='eager_attribute_img' src='img-not-loaded.png' loading="eager" onload="eager_attribute_img.resolve();">
+</picture>
+
+<!--
+ This async script loads very slowly in order to ensure that, if the
+ below_viewport image has started loading, it has a chance to finish
+ loading before window.load() happens, so that the test will dependably fail
+ in that case instead of potentially passing depending on how long different
+ resource fetches take.
+-->
+<script async src="/common/slow.py"></script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations-lazy.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations-lazy.html
new file mode 100644
index 0000000000..d3784671b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations-lazy.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<title>img relevant mutations, lazy-loaded</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/relevant-mutations.js"></script>
+<div id=log></div>
+
+<img src="/images/green-2x2.png"> <!-- block the window load event -->
+
+<!-- should invoke update the image data, but omit events for layout changes -->
+<!-- but also see https://github.com/whatwg/html/issues/8492 -->
+
+<img srcset="/images/green-2x2.png 2w" sizes="auto" width="2" loading="lazy" data-desc="width attribute changes">
+
+<img srcset="/images/green-2x2.png 2w" sizes="auto" style="width: 2px" loading="lazy" data-desc="width property changes">
+
+<div style="width: 2px">
+ <img srcset="/images/green-2x2.png 2w" sizes="auto" style="width: 100%" loading="lazy" data-desc="percentage width, CB width changes">
+</div>
+
+<img srcset="/images/green-2x2.png 2w" sizes="auto" style="height: 2px; aspect-ratio: 2 / 2" loading="lazy" data-desc="height property changes (with aspect-ratio)">
+
+<img srcset="/images/green-2x2.png 2w" sizes="auto" style="width: 2px" loading="lazy" data-desc="being rendered changes">
+
+<!-- should not invoke update the image data -->
+
+<img srcset="/images/green-2x2.png 2w" sizes="auto" style="width: 2px" loading="lazy" data-desc="loading attribute state changes">
+
+<img srcset="/images/green-2x2.png 2w" sizes="auto" style="width: 2px" loading="lazy" data-desc="display property changes to inline-block">
+
+<img srcset="/images/green-2x2.png 2w" sizes="auto" style="width: 2px" loading="lazy" data-desc="loading attribute changes to LAZY">
+
+<script>
+const rAF = () => new Promise(resolve => requestAnimationFrame(resolve));
+
+onload = async function() {
+
+ await rAF();
+ await rAF();
+
+ // lazy-loaded images should have fired their first 'load' event at this point.
+
+ t('width attribute changes', function(img) {
+ img.width = '4';
+ }, 'load');
+
+ t('width property changes', function(img) {
+ img.style.width = '4px';
+ }, 'timeout');
+
+ t('percentage width, CB width changes', function(img) {
+ img.parentNode.style.width = '4px';
+ }, 'timeout');
+
+ t('height property changes (with aspect-ratio)', function(img) {
+ img.style.height = '4px';
+ }, 'timeout');
+
+ t('loading attribute state changes', function(img) {
+ img.loading = 'eager';
+ }, 'timeout');
+
+ t('being rendered changes', function(img) {
+ img.style.display = 'none';
+ }, 'timeout');
+
+ t('display property changes to inline-block', function(img) {
+ img.style.display = 'inline-block';
+ }, 'timeout');
+
+ t('loading attribute changes to LAZY', function(img) {
+ img.setAttribute('loading', 'LAZY');
+ }, 'timeout');
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations.html
new file mode 100644
index 0000000000..24e15543cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/relevant-mutations.html
@@ -0,0 +1,628 @@
+<!doctype html>
+<title>img relevant mutations</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/relevant-mutations.js"></script>
+<div id=log></div>
+
+<!-- should invoke update the image data -->
+
+<img data-desc="src set">
+<img src="/images/green-2x2.png" data-desc="src changed">
+<img src="/images/green-2x2.png" data-desc="src removed">
+
+<img data-desc="srcset set">
+<img srcset="/images/green-2x2.png" data-desc="srcset changed">
+<img srcset="/images/green-2x2.png" data-desc="srcset removed">
+
+<img data-desc="sizes set">
+<img sizes="" data-desc="sizes changed">
+<img sizes="" data-desc="sizes removed">
+
+<img src="/images/green-2x2.png" data-desc="src set to same value">
+
+<img data-desc="crossorigin absent to empty, src absent">
+<img data-desc="crossorigin absent to anonymous, src absent">
+<img data-desc="crossorigin absent to use-credentials, src absent">
+<img crossorigin data-desc="crossorigin empty to absent, src absent">
+<img crossorigin data-desc="crossorigin empty to use-credentials, src absent">
+<img crossorigin=anonymous data-desc="crossorigin anonymous to absent, src absent">
+<img crossorigin=anonymous data-desc="crossorigin anonymous to use-credentials, src absent">
+<img crossorigin=use-credentials data-desc="crossorigin use-credentials to absent, src absent">
+<img crossorigin=use-credentials data-desc="crossorigin use-credentials to empty, src absent">
+<img crossorigin=use-credentials data-desc="crossorigin use-credentials to anonymous, src absent">
+<img crossorigin=use-credentials data-desc="crossorigin use-credentials to invalid, src absent">
+
+<img src="/images/green-2x2.png" data-desc="crossorigin absent to empty, src already set">
+<img src="/images/green-2x2.png" data-desc="crossorigin absent to anonymous, src already set">
+<img src="/images/green-2x2.png" data-desc="crossorigin absent to use-credentials, src already set">
+<img src="/images/green-2x2.png" crossorigin data-desc="crossorigin empty to absent, src already set">
+<img src="/images/green-2x2.png" crossorigin data-desc="crossorigin empty to use-credentials, src already set">
+<img src="/images/green-2x2.png" crossorigin=anonymous data-desc="crossorigin anonymous to absent, src already set">
+<img src="/images/green-2x2.png" crossorigin=anonymous data-desc="crossorigin anonymous to use-credentials, src already set">
+<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin use-credentials to absent, src already set">
+<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin use-credentials to empty, src already set">
+<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin use-credentials to anonymous, src already set">
+<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin use-credentials to invalid, src already set">
+
+<img data-desc="referrerpolicy absent to no-referrer-when-downgrade, src absent">
+<img data-desc="referrerpolicy absent to no-referrer, src absent">
+<img referrerpolicy data-desc="referrerpolicy empty to no-referrer-when-downgrade, src absent">
+<img referrerpolicy data-desc="referrerpolicy empty to no-referrer, src absent">
+<img referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to absent, src absent">
+<img referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to empty, src absent">
+<img referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to no-referrer, src absent">
+<img referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to invalid, src absent">
+<img referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to absent, src absent">
+<img referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to empty, src absent">
+<img referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to no-referrer-when-downgrade, src absent">
+<img referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to invalid, src absent">
+
+<img src="/images/green-2x2.png" data-desc="referrerpolicy absent to no-referrer-when-downgrade, src already set">
+<img src="/images/green-2x2.png" data-desc="referrerpolicy absent to no-referrer, src already set">
+<img src="/images/green-2x2.png" referrerpolicy data-desc="referrerpolicy empty to no-referrer-when-downgrade, src already set">
+<img src="/images/green-2x2.png" referrerpolicy data-desc="referrerpolicy empty to no-referrer, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to absent, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to empty, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to no-referrer, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer-when-downgrade data-desc="referrerpolicy no-referrer-when-downgrade to invalid, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to absent, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to empty, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to no-referrer-when-downgrade, src already set">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer data-desc="referrerpolicy no-referrer to invalid, src already set">
+
+<img src="/images/green-2x2.png" data-desc="inserted into picture"><picture></picture>
+
+<picture><img src="/images/green-2x2.png" data-desc="removed from picture"></picture>
+
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, previous source inserted"></picture>
+
+<picture><source><img src="/images/green-2x2.png" data-desc="parent is picture, previous source removed"></picture>
+
+<picture><source><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has srcset set"></picture>
+<picture><source srcset=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has srcset changed"></picture>
+<picture><source srcset=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has srcset removed"></picture>
+
+<picture><source><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has sizes set"></picture>
+<picture><source sizes=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has sizes changed"></picture>
+<picture><source sizes=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has sizes removed"></picture>
+
+<picture><source><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has media set"></picture>
+<picture><source media=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has media changed"></picture>
+<picture><source media=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has media removed"></picture>
+
+<picture><source><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has type set"></picture>
+<picture><source type=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has type changed"></picture>
+<picture><source type=""><img src="/images/green-2x2.png" data-desc="parent is picture, previous source has type removed"></picture>
+
+<img srcset="/images/green-2x2.png" data-desc="srcset is set to same value">
+<img srcset="/images/green-2x2.png" sizes data-desc="sizes is set to same value">
+
+<img src="/images/green-2x2.png" data-desc="crossorigin state not changed: absent, removeAttribute">
+<img src="/images/green-2x2.png" crossorigin data-desc="crossorigin state not changed: empty to anonymous">
+<img src="/images/green-2x2.png" crossorigin=anonymous data-desc="crossorigin state not changed: anonymous to foobar">
+<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin state not changed: use-credentials to USE-CREDENTIALS">
+
+<img src="/images/green-2x2.png" data-desc="referrerpolicy state not changed: absent, removeAttribute">
+<img src="/images/green-2x2.png" referrerpolicy data-desc="referrerpolicy state not changed: empty to empty">
+<img src="/images/green-2x2.png" referrerpolicy data-desc="referrerpolicy state not changed: empty to invalid">
+<img src="/images/green-2x2.png" data-desc="referrerpolicy state not changed: absent to invalid">
+<img src="/images/green-2x2.png" referrerpolicy=no-referrer data-desc="referrerpolicy state not changed: no-referrer to NO-REFERRER">
+<img src="/images/green-2x2.png" referrerpolicy=foobar data-desc="referrerpolicy state not changed: invalid to other-invalid">
+
+<img src="/images/green-2x2.png" data-desc="inserted into picture ancestor"><picture><span></span></picture>
+<picture><span><img src="/images/green-2x2.png" data-desc="removed from picture ancestor"></span></picture>
+
+<picture><span><img src="/images/green-2x2.png" data-desc="ancestor picture has a source inserted"></span></picture>
+<picture><source><span><img src="/images/green-2x2.png" data-desc="ancestor picture has a source removed"></span></picture>
+
+<picture><span><img src="/images/green-2x2.png" data-desc="ancestor picture; previous sibling source inserted"></span></picture>
+<picture><span><source><img src="/images/green-2x2.png" data-desc="ancestor picture; previous sibling source removed"></span></picture>
+
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following sibling source inserted"></picture>
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following sibling source removed"><source></picture>
+
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following sibling source has srcset set"><source></picture>
+
+<img src="/images/green-2x2.png" data-desc="media on img set">
+<img src="/images/green-2x2.png" data-desc="type on img set">
+<img src="/images/green-2x2.png" data-desc="class on img set">
+<img src="/images/green-2x2.png" data-desc="alt on img set">
+
+<picture><source><img src="/images/green-2x2.png" data-desc="src on previous sibling source set"></picture>
+<picture><source><img src="/images/green-2x2.png" data-desc="class on previous sibling source set"></picture>
+
+<img src="/images/green-2x2.png" data-desc="inserted/removed children of img">
+
+<picture><img src="/images/green-2x2.png" data-desc="picture is inserted; img has src"></picture><span></span>
+<picture><img srcset="/images/green-2x2.png" data-desc="picture is inserted; img has srcset"></picture><span></span>
+<picture><source srcset="/images/green-2x2.png"><img src="/images/green-2x2.png" data-desc="picture is inserted; img has previous sibling source"></picture><span></span>
+<picture><img src="/images/green-2x2.png" data-desc="picture is inserted; img has following sibling source"><source srcset="/images/green-2x2.png"></picture><span></span>
+
+<picture><img src="/images/green-2x2.png" data-desc="picture is removed; img has src"></picture>
+<picture><img srcset="/images/green-2x2.png" data-desc="picture is removed; img has srcset"></picture>
+<picture><source srcset="/images/green-2x2.png"><img src="/images/green-2x2.png" data-desc="picture is removed; img has previous sibling source"></picture>
+<picture><img src="/images/green-2x2.png" data-desc="picture is removed; img has following sibling source"><source srcset="/images/green-2x2.png"></picture>
+
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following img inserted"></picture>
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following img removed"><img></picture>
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following img has src set"><img></picture>
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following img has srcset set"><img></picture>
+<picture><img src="/images/green-2x2.png" data-desc="parent is picture, following img has sizes set"><img></picture>
+
+
+<script>
+onload = function() {
+
+ t('src set', function(img) {
+ img.src = '/images/green-2x2.png';
+ }, 'load');
+
+ t('src changed', function(img) {
+ img.src = '/images/green-2x2.png ';
+ }, 'load');
+
+ t('src removed', function(img) {
+ img.removeAttribute('src');
+ }, 'timeout');
+
+ t('srcset set', function(img) {
+ img.srcset = '/images/green-2x2.png';
+ }, 'load');
+
+ t('srcset changed', function(img) {
+ img.srcset = '/images/green-2x2.png ';
+ }, 'load');
+
+ t('srcset removed', function(img) {
+ img.removeAttribute('srcset');
+ }, 'timeout');
+
+ t('sizes set', function(img) {
+ img.sizes = '';
+ }, 'timeout');
+
+ t('sizes changed', function(img) {
+ img.sizes = ' ';
+ }, 'timeout');
+
+ t('sizes removed', function(img) {
+ img.removeAttribute('sizes');
+ }, 'timeout');
+
+ t('src set to same value', function(img) {
+ img.src = '/images/green-2x2.png';
+ }, 'load');
+
+ // When src is absent, changing the crossorigin attribute state MUST NOT
+ // generate events.
+
+ t('crossorigin absent to empty, src absent', function(img) {
+ img.crossOrigin = '';
+ }, 'timeout');
+
+ t('crossorigin absent to anonymous, src absent', function(img) {
+ img.crossOrigin = 'anonymous';
+ }, 'timeout');
+
+ t('crossorigin absent to use-credentials, src absent', function(img) {
+ img.crossOrigin = 'use-credentials';
+ }, 'timeout');
+
+ t('crossorigin empty to absent, src absent', function(img) {
+ img.removeAttribute('crossorigin');
+ }, 'timeout');
+
+ t('crossorigin empty to use-credentials, src absent', function(img) {
+ img.crossOrigin = 'use-credentials';
+ }, 'timeout');
+
+ t('crossorigin anonymous to absent, src absent', function(img) {
+ img.removeAttribute('crossorigin');
+ }, 'timeout');
+
+ t('crossorigin anonymous to use-credentials, src absent', function(img) {
+ img.crossOrigin = 'use-credentials';
+ }, 'timeout');
+
+ t('crossorigin use-credentials to absent, src absent', function(img) {
+ img.removeAttribute('crossorigin');
+ }, 'timeout');
+
+ t('crossorigin use-credentials to empty, src absent', function(img) {
+ img.crossOrigin = '';
+ }, 'timeout');
+
+ t('crossorigin use-credentials to anonymous, src absent', function(img) {
+ img.crossOrigin = 'anonymous';
+ }, 'timeout');
+
+ t('crossorigin use-credentials to invalid, src absent', function(img) {
+ img.crossOrigin = 'foobar';
+ }, 'timeout');
+
+ // When src is set, changing the crossorigin attribute state MUST generate
+ // events.
+
+ t('crossorigin absent to empty, src already set', function(img) {
+ img.crossOrigin = '';
+ }, 'load');
+
+ t('crossorigin absent to anonymous, src already set', function(img) {
+ img.crossOrigin = 'anonymous';
+ }, 'load');
+
+ t('crossorigin absent to use-credentials, src already set', function(img) {
+ img.crossOrigin = 'use-credentials';
+ }, 'load');
+
+ t('crossorigin empty to absent, src already set', function(img) {
+ img.removeAttribute('crossorigin');
+ }, 'load');
+
+ t('crossorigin empty to use-credentials, src already set', function(img) {
+ img.crossOrigin = 'use-credentials';
+ }, 'load');
+
+ t('crossorigin anonymous to absent, src already set', function(img) {
+ img.removeAttribute('crossorigin');
+ }, 'load');
+
+ t('crossorigin anonymous to use-credentials, src already set', function(img) {
+ img.crossOrigin = 'use-credentials';
+ }, 'load');
+
+ t('crossorigin use-credentials to absent, src already set', function(img) {
+ img.removeAttribute('crossorigin');
+ }, 'load');
+
+ t('crossorigin use-credentials to empty, src already set', function(img) {
+ img.crossOrigin = '';
+ }, 'load');
+
+ t('crossorigin use-credentials to anonymous, src already set', function(img) {
+ img.crossOrigin = 'anonymous';
+ }, 'load');
+
+ t('crossorigin use-credentials to invalid, src already set', function(img) {
+ img.crossOrigin = 'foobar';
+ }, 'load');
+
+ // When src is absent, changing the referrerpolicy attribute state MUST NOT
+ // generate events.
+
+ t('referrerpolicy absent to no-referrer-when-downgrade, src absent', function(img) {
+ img.referrerPolicy = 'no-referrer-when-downgrade';
+ }, 'timeout');
+
+ t('referrerpolicy absent to no-referrer, src absent', function(img) {
+ img.referrerPolicy = 'no-referrer';
+ }, 'timeout');
+
+ t('referrerpolicy empty to no-referrer-when-downgrade, src absent', function(img) {
+ img.referrerPolicy = 'no-referrer-when-downgrade';
+ }, 'timeout');
+
+ t('referrerpolicy empty to no-referrer, src absent', function(img) {
+ img.referrerPolicy = 'no-referrer';
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer-when-downgrade to absent, src absent', function(img) {
+ img.removeAttribute('referrerpolicy');
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer-when-downgrade to empty, src absent', function(img) {
+ img.referrerPolicy = '';
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer-when-downgrade to no-referrer, src absent', function(img) {
+ img.referrerPolicy = 'no-referrer';
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer-when-downgrade to invalid, src absent', function(img) {
+ img.referrerPolicy = 'foobar';
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer to absent, src absent', function(img) {
+ img.removeAttribute('referrerpolicy');
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer to empty, src absent', function(img) {
+ img.referrerPolicy = '';
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer to no-referrer-when-downgrade, src absent', function(img) {
+ img.referrerPolicy = 'no-referrer-when-downgrade';
+ }, 'timeout');
+
+ t('referrerpolicy no-referrer to invalid, src absent', function(img) {
+ img.removeAttribute('referrerpolicy');
+ }, 'timeout');
+
+ // When src is set, changing the referrerpolicy attribute state MUST generate
+ // events.
+
+ t('referrerpolicy absent to no-referrer-when-downgrade, src already set', function(img) {
+ img.referrerPolicy = 'no-referrer-when-downgrade';
+ }, 'load');
+
+ t('referrerpolicy absent to no-referrer, src already set', function(img) {
+ img.referrerPolicy = 'no-referrer';
+ }, 'load');
+
+ t('referrerpolicy empty to no-referrer-when-downgrade, src already set', function(img) {
+ img.referrerPolicy = 'no-referrer-when-downgrade';
+ }, 'load');
+
+ t('referrerpolicy empty to no-referrer, src already set', function(img) {
+ img.referrerPolicy = 'no-referrer';
+ }, 'load');
+
+ t('referrerpolicy no-referrer-when-downgrade to absent, src already set', function(img) {
+ img.removeAttribute('referrerpolicy');
+ }, 'load');
+
+ t('referrerpolicy no-referrer-when-downgrade to empty, src already set', function(img) {
+ img.referrerPolicy = '';
+ }, 'load');
+
+ t('referrerpolicy no-referrer-when-downgrade to no-referrer, src already set', function(img) {
+ img.referrerPolicy = 'no-referrer';
+ }, 'load');
+
+ t('referrerpolicy no-referrer-when-downgrade to invalid, src already set', function(img) {
+ img.referrerPolicy = 'foobar';
+ }, 'load');
+
+ t('referrerpolicy no-referrer to absent, src already set', function(img) {
+ img.removeAttribute('referrerpolicy');
+ }, 'load');
+
+ t('referrerpolicy no-referrer to empty, src already set', function(img) {
+ img.referrerPolicy = '';
+ }, 'load');
+
+ t('referrerpolicy no-referrer to no-referrer-when-downgrade, src already set', function(img) {
+ img.referrerPolicy = 'no-referrer-when-downgrade';
+ }, 'load');
+
+ t('referrerpolicy no-referrer to invalid, src already set', function(img) {
+ img.removeAttribute('referrerpolicy');
+ }, 'load');
+
+
+ t('inserted into picture', function(img) {
+ img.nextSibling.appendChild(img);
+ }, 'load');
+
+ t('removed from picture', function(img) {
+ img.parentNode.removeChild(img);
+ }, 'load');
+
+ t('parent is picture, previous source inserted', function(img) {
+ img.parentNode.insertBefore(document.createElement('source'), img);
+ }, 'load');
+
+ t('parent is picture, previous source removed', function(img) {
+ img.parentNode.removeChild(img.previousSibling);
+ }, 'load');
+
+ t('parent is picture, previous source has srcset set', function(img) {
+ img.previousSibling.srcset = '';
+ }, 'load');
+
+ t('parent is picture, previous source has srcset changed', function(img) {
+ img.previousSibling.srcset = ' ';
+ }, 'load');
+
+ t('parent is picture, previous source has srcset removed', function(img) {
+ img.previousSibling.removeAttribute('srcset');
+ }, 'load');
+
+ t('parent is picture, previous source has sizes set', function(img) {
+ img.previousSibling.sizes = '';
+ }, 'load');
+
+ t('parent is picture, previous source has sizes changed', function(img) {
+ img.previousSibling.sizes = ' ';
+ }, 'load');
+
+ t('parent is picture, previous source has sizes removed', function(img) {
+ img.previousSibling.removeAttribute('sizes');
+ }, 'load');
+
+ t('parent is picture, previous source has media set', function(img) {
+ img.previousSibling.media = '';
+ }, 'load');
+
+ t('parent is picture, previous source has media changed', function(img) {
+ img.previousSibling.media = ' ';
+ }, 'load');
+
+ t('parent is picture, previous source has media removed', function(img) {
+ img.previousSibling.removeAttribute('media');
+ }, 'load');
+
+ t('parent is picture, previous source has type set', function(img) {
+ img.previousSibling.type = '';
+ }, 'load');
+
+ t('parent is picture, previous source has type changed', function(img) {
+ img.previousSibling.type = ' ';
+ }, 'load');
+
+ t('parent is picture, previous source has type removed', function(img) {
+ img.previousSibling.removeAttribute('type');
+ }, 'load');
+
+ t('srcset is set to same value', function(img) {
+ img.srcset = "/images/green-2x2.png";
+ }, 'load');
+
+ t('sizes is set to same value', function(img) {
+ img.sizes = '';
+ }, 'load');
+
+ t('crossorigin state not changed: absent, removeAttribute', function(img) {
+ img.removeAttribute('crossorigin');
+ }, 'timeout');
+
+ t('crossorigin state not changed: empty to anonymous', function(img) {
+ img.crossOrigin = 'anonymous';
+ }, 'timeout');
+
+ t('crossorigin state not changed: anonymous to foobar', function(img) {
+ img.crossOrigin = 'foobar';
+ }, 'timeout');
+
+ t('crossorigin state not changed: use-credentials to USE-CREDENTIALS', function(img) {
+ img.crossOrigin = 'USE-CREDENTIALS';
+ }, 'timeout');
+
+ t('referrerpolicy state not changed: absent, removeAttribute', function(img) {
+ img.removeAttribute('referrerpolicy');
+ }, 'timeout');
+
+ t('referrerpolicy state not changed: empty to empty', function(img) {
+ img.referrerPolicy = '';
+ }, 'timeout');
+
+ t('referrerpolicy state not changed: empty to invalid', function(img) {
+ img.referrerPolicy = 'foobar';
+ }, 'timeout');
+
+ t('referrerpolicy state not changed: absent to invalid', function(img) {
+ img.referrerPolicy = 'foobar';
+ }, 'timeout');
+
+ t('referrerpolicy state not changed: no-referrer to NO-REFERRER', function(img) {
+ img.referrerPolicy = 'NO-REFERRER';
+ }, 'timeout');
+
+ t('referrerpolicy state not changed: invalid to other-invalid', function(img) {
+ img.referrerPolicy = 'foobar2';
+ }, 'timeout');
+
+ t('inserted into picture ancestor', function(img) {
+ img.nextSibling.firstChild.appendChild(img);
+ }, 'timeout');
+
+ t('removed from picture ancestor', function(img) {
+ img.parentNode.removeChild(img);
+ }, 'timeout');
+
+ t('ancestor picture has a source inserted', function(img) {
+ img.parentNode.parentNode.insertBefore(document.createElement('source'), img.parentNode);
+ }, 'timeout');
+
+ t('ancestor picture has a source removed', function(img) {
+ img.parentNode.parentNode.removeChild(img.parentNode.previousSibling);
+ }, 'timeout');
+
+ t('ancestor picture; previous sibling source inserted', function(img) {
+ img.parentNode.insertBefore(document.createElement('source'), img);
+ }, 'timeout');
+
+ t('ancestor picture; previous sibling source removed', function(img) {
+ img.parentNode.removeChild(img.previousSibling);
+ }, 'timeout');
+
+ t('parent is picture, following sibling source inserted', function(img) {
+ img.parentNode.appendChild(document.createElement('source'));
+ }, 'timeout');
+
+ t('parent is picture, following sibling source removed', function(img) {
+ img.parentNode.removeChild(img.nextSibling);
+ }, 'timeout');
+
+ t('parent is picture, following sibling source has srcset set', function(img) {
+ img.nextSibling.srcset = '';
+ }, 'timeout');
+
+ t('media on img set', function(img) {
+ img.setAttribute('media', '');
+ }, 'timeout');
+
+ t('type on img set', function(img) {
+ img.setAttribute('type', '');
+ }, 'timeout');
+
+ t('class on img set', function(img) {
+ img.className = '';
+ }, 'timeout');
+
+ t('alt on img set', function(img) {
+ img.alt = '';
+ }, 'timeout');
+
+ t('src on previous sibling source set', function(img) {
+ img.previousSibling.setAttribute('src', '');
+ }, 'timeout');
+
+ t('class on previous sibling source set', function(img) {
+ img.previousSibling.className = '';
+ }, 'timeout');
+
+ t('inserted/removed children of img', function(img) {
+ img.appendChild(document.createElement('source'));
+ setTimeout(this.step_func(function() {
+ img.removeChild(img.firstChild);
+ }), 0);
+ }, 'timeout');
+
+ t('picture is inserted; img has src', function(img) {
+ img.parentNode.nextSibling.appendChild(img.parentNode);
+ }, 'timeout');
+
+ t('picture is inserted; img has srcset', function(img) {
+ img.parentNode.nextSibling.appendChild(img.parentNode);
+ }, 'timeout');
+
+ t('picture is inserted; img has previous sibling source', function(img) {
+ img.parentNode.nextSibling.appendChild(img.parentNode);
+ }, 'timeout');
+
+ t('picture is inserted; img has following sibling source', function(img) {
+ img.parentNode.nextSibling.appendChild(img.parentNode);
+ }, 'timeout');
+
+ t('picture is removed; img has src', function(img) {
+ img.parentNode.parentNode.removeChild(img.parentNode);
+ }, 'timeout');
+
+ t('picture is removed; img has srcset', function(img) {
+ img.parentNode.parentNode.removeChild(img.parentNode);
+ }, 'timeout');
+
+ t('picture is removed; img has previous sibling source', function(img) {
+ img.parentNode.parentNode.removeChild(img.parentNode);
+ }, 'timeout');
+
+ t('picture is removed; img has following sibling source', function(img) {
+ img.parentNode.parentNode.removeChild(img.parentNode);
+ }, 'timeout');
+
+ t('parent is picture, following img inserted', function(img) {
+ img.parentNode.appendChild(document.createElement('img'));
+ }, 'timeout');
+
+ t('parent is picture, following img removed', function(img) {
+ img.parentNode.removeChild(img.nextSibling);
+ }, 'timeout');
+
+ t('parent is picture, following img has src set', function(img) {
+ img.nextSibling.src = '';
+ }, 'timeout');
+
+ t('parent is picture, following img has srcset set', function(img) {
+ img.nextSibling.srcset = '';
+ }, 'timeout');
+
+ t('parent is picture, following img has sizes set', function(img) {
+ img.nextSibling.sizes = '';
+ }, 'timeout');
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/remove-element-and-scroll.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/remove-element-and-scroll.html
new file mode 100644
index 0000000000..8e7fa1bfbf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/remove-element-and-scroll.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+ <title>Images with loading='lazy' load being removed and then scrolled to</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="common.js"></script>
+</head>
+
+<body>
+ <img id="in_viewport" src='resources/image.png?in_viewport&pipe=trickle(d1)'>
+ <div style="height:1000vh"></div>
+ <div id="below_viewport_div"></div>
+ <img id="below_viewport" src='resources/image.png?below_viewport' loading="lazy">
+
+ <script>
+ const in_viewport_element = document.getElementById("in_viewport");
+ const below_viewport_element = document.getElementById("below_viewport");
+ const below_viewport_div = document.getElementById("below_viewport_div");
+
+ async_test(t => {
+ below_viewport_element.onload = t.unreached_func("Removed loading=lazy image " +
+ "should not load when its old position is scrolled to.");
+ below_viewport_element.remove();
+
+ in_viewport_element.onload = () => {
+ below_viewport_div.scrollIntoView();
+ t.step_timeout(t.step_func_done(), 2000);
+ };
+ }, "Test that <img> below viewport is not loaded when removed from the " +
+ "document and then scrolled to");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/blue-10.png b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/blue-10.png
new file mode 100644
index 0000000000..62949e08d8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/blue-10.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/cat.jpg b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/cat.jpg
new file mode 100644
index 0000000000..a4f14f54d6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/cat.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/green.png b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/green.png
new file mode 100644
index 0000000000..25b76c3c6f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/green.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-and-stash.py b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-and-stash.py
new file mode 100644
index 0000000000..d16a3e591d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-and-stash.py
@@ -0,0 +1,44 @@
+# This is a simple implementation of a server-side stash that supports two
+# operations:
+# - increment:
+# Increments a value in the stash keyed by a given uuid, and returns an
+# image file
+# - read:
+# Returns the value in the stash keyed by a given uuid or 0 otherwise.
+# This is a read-only operation, and does not remove the value from the
+# stash as-is the default WPT stash behavior:
+# https://web-platform-tests.org/tools/wptserve/docs/stash.html.
+
+import os
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ if b"increment" in request.GET:
+ uuid = request.GET[b"increment"]
+
+ # First, increment the stash value keyed by `uuid`, and write it back to the
+ # stash. Writing it back to the stash is necessary since `take()` actually
+ # removes the value whereas we want to increment it.
+ stash_value = request.server.stash.take(uuid)
+ if stash_value is None:
+ stash_value = 0
+ request.server.stash.put(uuid, stash_value + 1)
+
+ # Return a basic image.
+ response_headers = [(b"Content-Type", b"image/png")]
+ image_path = os.path.join(os.path.dirname(isomorphic_decode(__file__)), u"image.png")
+ return (200, response_headers, open(image_path, mode='rb').read())
+
+ elif b"read" in request.GET:
+ uuid = request.GET[b"read"]
+ stash_value = request.server.stash.take(uuid)
+
+ if stash_value is None:
+ stash_value = 0
+ # Write the stash value keyed by `uuid` back to the stash. This is necessary
+ # because `take()` actually removes it, but we want a read-only read.
+ request.server.stash.put(uuid, stash_value);
+ return (200, [], str(stash_value))
+
+ return (404 , [], "Not found")
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-below-viewport.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-below-viewport.html
new file mode 100644
index 0000000000..f25bd6f4d0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-below-viewport.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<div style="height:1000vh;"></div>
+
+<img id="img" loading="lazy" src="image.png?lazy-below-viewport">
+
+<script>
+ const img = document.querySelector('#img');
+
+ img.addEventListener("load", () => {
+ parent.postMessage("image_loaded", "*");
+ });
+
+ window.addEventListener("load", () => {
+ parent.postMessage("window_loaded", "*");
+ });
+
+ window.addEventListener("message", event => {
+ if (event.data == "scroll") {
+ img.scrollIntoView();
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-in-viewport.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-in-viewport.html
new file mode 100644
index 0000000000..bafdacc883
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image-loading-lazy-in-viewport.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+
+<!-- This frame is used by image-loading-lazy-in-cross-origin-iframe-002.sub.html -->
+
+<img id="img" loading="lazy">
+
+<script>
+ const img = document.querySelector('#img');
+
+ // Prevent the list of available images check from loading the image non-lazily.
+ img.src = "image.png?image-loading-lazy-in-viewport-" + Math.random() + "-" + Date.now();
+
+ img.addEventListener("load", () => {
+ parent.postMessage("image_loaded", "*");
+ });
+
+ window.addEventListener("load", () => {
+ parent.postMessage("window_loaded", "*");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image.png b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image.png
new file mode 100644
index 0000000000..b712825093
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/image.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/newwindow.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/newwindow.html
new file mode 100644
index 0000000000..735b8f6213
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/newwindow.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="height:1000vh;"></div>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/red.png b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/red.png
new file mode 100644
index 0000000000..57bf3ddc52
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/red.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/referrer-checker-img.py b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/referrer-checker-img.py
new file mode 100644
index 0000000000..bb2071cb97
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/referrer-checker-img.py
@@ -0,0 +1,14 @@
+import os
+
+from wptserve.utils import isomorphic_decode
+
+# Returns a valid image response when request's |referrer| matches
+# |expected_referrer|.
+def main(request, response):
+ referrer = request.headers.get(b"referer", b"")
+ expected_referrer = request.GET.first(b"expected_referrer", b"")
+ response_headers = [(b"Content-Type", b"image/png")]
+ if referrer == expected_referrer:
+ image_path = os.path.join(os.path.dirname(isomorphic_decode(__file__)), u"image.png")
+ return (200, response_headers, open(image_path, mode='rb').read())
+ return (404, response_headers, u"Not found")
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js
new file mode 100644
index 0000000000..8bd079f790
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js
@@ -0,0 +1,20 @@
+addEventListener('install', (event) => {
+ skipWaiting();
+});
+
+addEventListener('activate', (event) => {
+ event.waitUntil(clients.claim());
+});
+
+async function broadcast(msg) {
+ const allClients = await clients.matchAll();
+ for (const client of allClients) {
+ client.postMessage(msg);
+ }
+}
+
+addEventListener('fetch', (event) => {
+ event.waitUntil(
+ broadcast({ url: event.request.url, mode: event.request.mode })
+ )
+});
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js.headers b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js.headers
new file mode 100644
index 0000000000..3c534471c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/resources/sw.js.headers
@@ -0,0 +1 @@
+Service-Worker-Allowed: /html/semantics/embedded-content/the-img-element/
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print-ref.html
new file mode 100644
index 0000000000..7189a57642
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+ <div style="width: 200px; height: 200px; background-color: green;"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print.html
new file mode 100644
index 0000000000..60b061f14a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/responsive-image-select-print.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Test print result of responsive image</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#srcset-attribute">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1803094">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="match" href="responsive-image-select-print-ref.html">
+<body>
+ <picture>
+ <source width="200" srcset="./resources/red.png 1w, ./resources/green.png 200w">
+ <img>
+ </picture>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/scrolling-below-viewport-image-lazy-loading-in-iframe.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/scrolling-below-viewport-image-lazy-loading-in-iframe.html
new file mode 100644
index 0000000000..3c26323426
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/scrolling-below-viewport-image-lazy-loading-in-iframe.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Scrolling a lazy loaded image into view in an iframe</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<iframe onload="async_test(this.contentWindow.run)" srcdoc="
+<!DOCTYPE html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='../resources/common.js'></script>
+
+<h1>Scroll down...</h1>
+<p>Scroll down...</p>
+<p>Scroll down...</p>
+<img id='below_iframe_viewport_img' src='resources/image.png' loading='lazy'
+ onload='below_iframe_viewport_img.resolve();' onerror='below_iframe_viewport_img.reject();'>
+
+<script>
+ const scroll_trigger_img = new ElementLoadPromise('visible');
+ const below_iframe_viewport_img = new ElementLoadPromise('below_iframe_viewport_img');
+
+ function run(t) {
+ below_iframe_viewport_img.element().scrollIntoView();
+ below_iframe_viewport_img.promise
+ .then(t.step_func(() => {
+ assert_not_equals(below_iframe_viewport_img.element().width, 0, 'width should be greater than zero after scrolling');
+ assert_not_equals(below_iframe_viewport_img.element().height, 0, 'height should be greater than zero after scrolling');
+ t.done();
+ }))
+ .catch(t.unreached_func('The below_iframe_viewport image should load'));
+ };
+</script>
+"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/implicit-sizes-ignores-width.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/implicit-sizes-ignores-width.html
new file mode 100644
index 0000000000..db61db351e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/implicit-sizes-ignores-width.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Implicit sizes ignores width</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+img {width: auto;}
+</style>
+<img srcset="../srcset/resources/image.png 100w" sizes="400px" id="sizes">
+<img srcset="../srcset/resources/image.png 100w" width="400" id="width">
+<script>
+setup({explicit_done:true});
+onload = () => {
+ test(() => {
+ assert_equals(document.getElementById("sizes").width, 400);
+ assert_equals(document.getElementById("width").width, window.innerWidth);
+ done();
+ });
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-display-none.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-display-none.html
new file mode 100644
index 0000000000..6aa77ebf85
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-display-none.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>img parse a sizes attribute (display:none)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe data-desc="display:none" style="width:1000px; height:1000px" src="support/sizes-iframed.sub.html?doctype=doctype%20html&style=display:none"></iframe>
+<script src="support/parse-a-sizes-attribute.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-quirks-mode.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-quirks-mode.html
new file mode 100644
index 0000000000..2150192d29
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-quirks-mode.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>img parse a sizes attribute (quirks mode)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe data-desc="quirks mode" style="width:1000px; height:1000px" src="support/sizes-iframed.sub.html?doctype=----&style="></iframe>
+<script src="support/parse-a-sizes-attribute.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-standards-mode.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-standards-mode.html
new file mode 100644
index 0000000000..6e70c88396
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-standards-mode.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>img parse a sizes attribute (standards mode)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe data-desc="standards mode" style="width:1000px; height:1000px" src="support/sizes-iframed.sub.html?doctype=doctype%20html&style="></iframe>
+<script src="support/parse-a-sizes-attribute.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-width-1000px.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-width-1000px.html
new file mode 100644
index 0000000000..ab3f69e058
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute-width-1000px.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>img parse a sizes attribute (width:1000px)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe data-desc="width:1000px" style="width:1000px; height:1000px" src="support/sizes-iframed.sub.html?doctype=doctype%20html&style=width:1000px%3B%20height:16px"></iframe>
+<script src="support/parse-a-sizes-attribute.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/reference/sizes-auto-rendering-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/reference/sizes-auto-rendering-ref.html
new file mode 100644
index 0000000000..221930fddb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/reference/sizes-auto-rendering-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>Auto sizes rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
+<img
+ src="/images/green.png"
+ width="33"
+ height="13"
+>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-2.html
new file mode 100644
index 0000000000..75d1884e34
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-2.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Auto sizes rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
+<link rel="match" href="reference/sizes-auto-rendering-ref.html">
+<script src="/common/rendering-utils.js"></script>
+<script src="/common/reftest-wait.js"></script>
+<img
+ id="testImg"
+ loading="lazy"
+ sizes="auto"
+ width="33"
+ height="13"
+>
+<script>
+ function imageLoaded() {
+ waitForAtLeastOneFrame().then(takeScreenshot);
+ }
+
+ testImg.addEventListener('load', imageLoaded);
+ testImg.setAttribute('srcset', `
+ /images/red.png 10w,
+ /images/green.png 100w
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-3.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-3.html
new file mode 100644
index 0000000000..71ed90ebbe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-3.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Auto sizes rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
+<link rel="match" href="reference/sizes-auto-rendering-ref.html">
+<script src="/common/rendering-utils.js"></script>
+<script src="/common/reftest-wait.js"></script>
+<img
+ id="testImg"
+ loading="lazy"
+ sizes="auto"
+ width="33"
+ height="13"
+>
+<script>
+ function imageLoaded() {
+ waitForAtLeastOneFrame().then(takeScreenshot);
+ }
+
+ testImg.addEventListener('load', imageLoaded);
+ testImg.setAttribute('srcset', `
+ /images/green.png 100w,
+ /images/red.png 1000w
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-dynamic.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-dynamic.html
new file mode 100644
index 0000000000..901c6f2e09
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering-dynamic.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Auto sizes dynamic rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
+<link rel="match" href="reference/sizes-auto-rendering-ref.html">
+<script src="/common/rendering-utils.js"></script>
+<script src="/common/reftest-wait.js"></script>
+<img
+ id="testImg"
+ loading="lazy"
+ sizes="auto"
+ width="1"
+ height="13"
+>
+<script>
+ function secondImageLoaded() {
+ waitForAtLeastOneFrame().then(takeScreenshot);
+ }
+
+ function firstImageLoaded() {
+ const expected = 'red.png';
+ if (!testImg.currentSrc.endsWith('red.png')) {
+ const fail_msg = `FAIL: currentSrc is ${testImg.currentSrc}, expected ${expected}.`;
+ document.body.textContent = fail_msg;
+ takeScreenshot();
+ }
+
+ testImg.addEventListener('load', secondImageLoaded);
+ testImg.style.width = '33px';
+ }
+
+ testImg.addEventListener('load', firstImageLoaded, {once: true});
+ testImg.setAttribute('srcset', `
+ /images/fail.gif 1000w,
+ /images/green.png 100w,
+ /images/red.png 10w
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering.html
new file mode 100644
index 0000000000..e972f69c20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto-rendering.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Auto sizes rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#sizes-attributes">
+<link rel="match" href="reference/sizes-auto-rendering-ref.html">
+<script src="/common/rendering-utils.js"></script>
+<script src="/common/reftest-wait.js"></script>
+<img
+ id="testImg"
+ loading="lazy"
+ sizes="auto"
+ width="33"
+ height="13"
+>
+<script>
+ function imageLoaded() {
+ waitForAtLeastOneFrame().then(takeScreenshot);
+ }
+
+ testImg.addEventListener('load', imageLoaded);
+ testImg.setAttribute('srcset', `
+ /images/fail.gif 10w,
+ /images/green.png 100w,
+ /images/red.png 1000w
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto.html
new file mode 100644
index 0000000000..6c1a741f2b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-auto.html
@@ -0,0 +1,153 @@
+<!doctype html>
+<title>img parse a sizes attribute: sizes=auto</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#parse-a-sizes-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+img { height: 10px; } /* Keep all images in the viewport, so lazy images load */
+#narrow-div { width: 10px; }
+#wide-div { width: 500px; }
+</style>
+<div id=log></div>
+<script src="support/parse-a-sizes-attribute.js"></script>
+<img srcset='/images/green-1x1.png?ref1 50w, /images/green-16x16.png?ref1 51w' sizes='1px' id=ref1>
+<img srcset='/images/green-1x1.png?ref2 50w, /images/green-16x16.png?ref2 51w' sizes='100vw' id=ref2>
+<div id='narrow-div'></div>
+<div id='wide-div'></div>
+<script>
+"use strict";
+
+// https://github.com/web-platform-tests/rfcs/pull/75
+let async_promise_test = (promise, description) => {
+ async_test(test => {
+ promise(test)
+ .then(() => {test.done();})
+ .catch(test.step_func(error => { throw error; }));
+ }, description);
+};
+
+function check(imgOrPicture) {
+ let img = imgOrPicture;
+ let source;
+ if (imgOrPicture.localName === 'picture') {
+ source = imgOrPicture.firstChild;
+ img = imgOrPicture.lastChild;
+ }
+ const ref = document.getElementById(img.dataset.ref);
+ async_promise_test(async (t) => {
+ let expect = ref.currentSrc;
+ if (expect) {
+ expect = expect.split('?')[0];
+ }
+ if (expect === '' || expect === null || expect === undefined) {
+ assert_unreached('ref currentSrc was ' + format_value(expect));
+ }
+ await new Promise((resolve, reject) => {
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ t.step(() => {
+ let got = img.currentSrc;
+ assert_greater_than(got.indexOf('?'), -1, 'expected a "?" in currentSrc');
+ got = got.split('?')[0];
+ assert_equals(got, expect);
+ })
+ }, imgOrPicture.outerHTML);
+}
+
+const tests = [
+ // Smoke tests
+ {sizes: '1px', 'data-ref': 'ref1'},
+ {sizes: '', 'data-ref': 'ref2'},
+ // Actual tests
+ {loading: 'lazy', sizes: 'auto', width: '10', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'AUTO', width: '10', 'data-ref': 'ref1'},
+ {loading: 'lazy', width: '10', 'data-ref': 'ref2'}, // no `sizes: 'auto'` -> 100vw
+ {loading: 'lazy', style: 'width: 10px', 'data-ref': 'ref2'}, // no `sizes: 'auto'` -> 100vw
+ {loading: 'lazy', style: 'max-width: 10px', 'data-ref': 'ref2'}, // no `sizes: 'auto'` -> 100vw
+ {loading: 'lazy', style: 'width: 100%; max-width: 10px', 'data-ref': 'ref2'}, // no `sizes: 'auto'` -> 100vw
+ {loading: 'lazy', sizes: 'auto', width: '500', 'data-ref': 'ref2'},
+ {loading: 'lazy', sizes: 'auto', width: '10', style: 'visibility: hidden', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', width: '10', style: 'display: inline', hidden: '', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', width: '0', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'width: 0px', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', 'data-ref': 'ref2'}, // no width -> UA default of 300px
+ {loading: 'lazy', sizes: 'auto, 100vw', width: '10', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: '100vw, auto', width: '10', 'data-ref': 'ref2'},
+ {loading: 'eager', sizes: 'auto', width: '10', 'data-ref': 'ref2'},
+ {loading: 'lazy', sizes: 'auto', width: '100%', parent: 'narrow-div', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', width: '100%', parent: 'wide-div', 'data-ref': 'ref2'},
+ {loading: 'lazy', sizes: 'auto', style: 'height: 10px; aspect-ratio: 10 / 10', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'height: 10px; aspect-ratio: 500 / 10', 'data-ref': 'ref2'},
+ {loading: 'lazy', sizes: 'auto', style: 'min-height: 10px; aspect-ratio: 10 / 10', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'min-height: 10px; aspect-ratio: 500 / 10', 'data-ref': 'ref2'},
+ {loading: 'lazy', sizes: 'auto', style: 'inline-size: 10px', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'min-inline-size: 10px', 'data-ref': 'ref2'}, // no width -> UA default of 300px
+ {loading: 'lazy', sizes: 'auto', style: 'block-size: 10px; aspect-ratio: 10 / 10', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'min-block-size: 10px; aspect-ratio: 10 / 10', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'block-size: 10px; writing-mode: vertical-rl', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'min-block-size: 10px; writing-mode: vertical-rl', 'data-ref': 'ref2'}, // no width -> UA default of 300px
+ {loading: 'lazy', sizes: 'auto', style: 'inline-size: 10px; aspect-ratio: 10/10; writing-mode: vertical-rl', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'min-inline-size: 10px; aspect-ratio: 10/10; writing-mode: vertical-rl', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: '--my-width: 10px; width: var(--my-width)', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'width: calc(5px + 5px)', 'data-ref': 'ref1'},
+ {loading: 'lazy', sizes: 'auto', style: 'position: absolute; left: 50%; right: 49%', 'data-ref': 'ref2'}, // replaced elements don't get the width resolved from 'left'/'right' per https://drafts.csswg.org/css2/#abs-replaced-width
+];
+
+function test_img(obj, i) {
+ const img = document.createElement('img');
+ let parent = document.body;
+ for (const attr in obj) {
+ if (attr === 'parent') {
+ parent = document.getElementById(obj.parent);
+ } else {
+ img.setAttribute(attr, obj[attr]);
+ }
+ }
+ img.srcset = `/images/green-1x1.png?img${i} 50w, /images/green-16x16.png?img${i} 51w`
+ parent.appendChild(img);
+ check(img);
+}
+
+function test_picture(obj, i) {
+ const picture = document.createElement('picture');
+ const source = document.createElement('source');
+ const img = document.createElement('img');
+ let parent = document.body;
+ for (const attr in obj) {
+ switch (attr) {
+ case 'parent':
+ parent = document.getElementById(obj.parent);
+ break;
+ case 'sizes':
+ // Authors have to specify sizes="auto" on the img to use auto-sizes.
+ if(obj[attr].toLowerCase().startsWith("auto")) {
+ img.setAttribute(attr, obj[attr]);
+ break;
+ }
+ case 'type':
+ case 'media':
+ source.setAttribute(attr, obj[attr]);
+ break;
+ default:
+ img.setAttribute(attr, obj[attr]);
+ break;
+ }
+ }
+ source.srcset = `/images/green-1x1.png?picture${i} 50w, /images/green-16x16.png?picture${i} 51w`;
+ picture.appendChild(source);
+ picture.appendChild(img);
+ parent.appendChild(picture);
+ check(picture);
+}
+
+onload = () => {
+ let i = 0;
+ for (const obj of tests) {
+ i++;
+ test_img(obj, i);
+ test_picture(obj, i);
+ }
+ done();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001-ref.html
new file mode 100644
index 0000000000..68466ae94d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>Test reference</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<iframe width="500" height="500" srcdoc='<!doctype html><img alt="FAIL" srcset="/images/green-256x256.png 100w" style="max-width: 100%" sizes="10px">'></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001.html
new file mode 100644
index 0000000000..51f8145bf9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-001.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Image intrinsic size specified via sizes attribute reacts properly to media changes</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="match" href="sizes-dynamic-001-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/#sizes-attributes">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1149357">
+<script>
+function frameLoaded(frame) {
+ frame.width = "500";
+ let img = frame.contentDocument.querySelector('img');
+
+ // Trigger the viewport resize, which will trigger the image load task.
+ img.offsetWidth;
+
+ // Wait for the image load task to run.
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+}
+</script>
+<iframe onload="frameLoaded(this)" width="200" height="500" srcdoc='<!doctype html><img srcset="/images/green-256x256.png 100w" style="max-width: 100%" sizes="(min-width: 400px) 10px, 20px">'></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-002.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-002.html
new file mode 100644
index 0000000000..6c64b3da39
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/sizes-dynamic-002.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Image intrinsic size specified via sizes attribute reacts properly to media changes in Shadow DOM</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="match" href="sizes-dynamic-001-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/#sizes-attributes">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1149357">
+<script>
+function frameLoaded(frame) {
+ let doc = frame.contentDocument;
+ let shadow = doc.getElementById("host").attachShadow({ mode: "open" });
+
+ let img = doc.createElement("img");
+ img.srcset = "/images/green-256x256.png 100w";
+ img.style.maxWidth = "100%";
+ img.setAttribute("sizes", "(min-width: 400px) 10px, 20px");
+
+ img.onload = function() {
+ img.offsetWidth; // Flush layout.
+
+ frame.width = "500";
+
+ // Trigger the viewport resize, which will trigger the image load task.
+ img.offsetWidth;
+
+ // Wait for the image load task to run.
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+ };
+
+ shadow.appendChild(img);
+}
+</script>
+<iframe onload="frameLoaded(this)" width="200" height="500" srcdoc='<!doctype html><div id="host"></div>'></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/parse-a-sizes-attribute.js b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/parse-a-sizes-attribute.js
new file mode 100644
index 0000000000..62ad00a468
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/parse-a-sizes-attribute.js
@@ -0,0 +1,29 @@
+setup({explicit_done:true});
+
+function check(p, iframe) {
+ var current = p.firstElementChild;
+ var ref_sizes = current.getAttribute('sizes');
+ var expect = current.currentSrc;
+ if (expect) {
+ expect = expect.split('?')[0];
+ }
+ while (current = current.nextElementSibling) {
+ test(function() {
+ if (expect === '' || expect === null || expect === undefined) {
+ assert_unreached('ref currentSrc was ' + format_value(expect));
+ }
+ var got = current.currentSrc;
+ assert_greater_than(got.indexOf('?'), -1, 'expected a "?" in currentSrc');
+ got = got.split('?')[0];
+ assert_equals(got, expect);
+ }, current.outerHTML + ' ref sizes=' + format_value(ref_sizes) + ' (' + iframe.getAttribute('data-desc') + ')');
+ }
+}
+
+onload = function() {
+ var iframe = document.querySelector('iframe');
+ [].forEach.call(iframe.contentDocument.querySelectorAll('p'), function(p) {
+ check(p, iframe);
+ });
+ done();
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/sizes-iframed.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/sizes-iframed.sub.html
new file mode 100644
index 0000000000..1f80ad2f91
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/sizes/support/sizes-iframed.sub.html
@@ -0,0 +1,186 @@
+<!{{GET[doctype]}}>
+<style> img { {{GET[style]}} } </style>
+<!-- First <img> in a <p> is the reference. The following <img>s should be equivalent -->
+<!-- default is 100vw, not 300px -->
+<p>
+<img srcset='/images/green-1x1.png?a1 300w, /images/green-16x16.png?a1 301w' sizes='100vw'>
+<img srcset='/images/green-1x1.png?a2 300w, /images/green-16x16.png?a2 301w'>
+<p>
+<img srcset='/images/green-1x1.png?b1 450w, /images/green-16x16.png?b1 451w' sizes='100vw'>
+<img srcset='/images/green-1x1.png?b2 450w, /images/green-16x16.png?b2 451w'>
+<p>
+<img srcset='/images/green-1x1.png?c1 600w, /images/green-16x16.png?c1 601w' sizes='100vw'>
+<img srcset='/images/green-1x1.png?c2 600w, /images/green-16x16.png?c2 601w'>
+<p>
+<img srcset='/images/green-1x1.png?d1 900w, /images/green-16x16.png?d1 901w' sizes='100vw'>
+<img srcset='/images/green-1x1.png?d2 900w, /images/green-16x16.png?d2 901w'>
+
+<p>
+<img srcset='/images/green-1x1.png?e1 50w, /images/green-16x16.png?e1 51w' sizes='1px'>
+<img srcset='/images/green-1x1.png?e2 50w, /images/green-16x16.png?e2 51w' sizes='0'>
+<img srcset='/images/green-1x1.png?e3 50w, /images/green-16x16.png?e3 51w' sizes='-0'>
+<img srcset='/images/green-1x1.png?e4 50w, /images/green-16x16.png?e4 51w' sizes='+0'>
+<img srcset='/images/green-1x1.png?e5 50w, /images/green-16x16.png?e5 51w' sizes='+1px'>
+<img srcset='/images/green-1x1.png?e6 50w, /images/green-16x16.png?e6 51w' sizes='.1px'>
+<img srcset='/images/green-1x1.png?e7 50w, /images/green-16x16.png?e7 51w' sizes='0.1em'>
+<img srcset='/images/green-1x1.png?e8 50w, /images/green-16x16.png?e8 51w' sizes='0.1ex'>
+<img srcset='/images/green-1x1.png?e9 50w, /images/green-16x16.png?e9 51w' sizes='0.1ch'>
+<img srcset='/images/green-1x1.png?e10 50w, /images/green-16x16.png?e10 51w' sizes='0.1rem'>
+<img srcset='/images/green-1x1.png?e11 50w, /images/green-16x16.png?e11 51w' sizes='0.1vw'>
+<img srcset='/images/green-1x1.png?e12 50w, /images/green-16x16.png?e12 51w' sizes='0.1vh'>
+<img srcset='/images/green-1x1.png?e13 50w, /images/green-16x16.png?e13 51w' sizes='0.1vmin'>
+<img srcset='/images/green-1x1.png?e14 50w, /images/green-16x16.png?e14 51w' sizes='0.1vmax'>
+<img srcset='/images/green-1x1.png?e15 50w, /images/green-16x16.png?e15 51w' sizes='0.1cm'>
+<img srcset='/images/green-1x1.png?e16 50w, /images/green-16x16.png?e16 51w' sizes='1mm'>
+<img srcset='/images/green-1x1.png?e17 50w, /images/green-16x16.png?e17 51w' sizes='1q'>
+<img srcset='/images/green-1x1.png?e18 50w, /images/green-16x16.png?e18 51w' sizes='0.01in'>
+<img srcset='/images/green-1x1.png?e19 50w, /images/green-16x16.png?e19 51w' sizes='0.1pc'>
+<img srcset='/images/green-1x1.png?e20 50w, /images/green-16x16.png?e20 51w' sizes='0.1pt'>
+<img srcset='/images/green-1x1.png?e21 50w, /images/green-16x16.png?e21 51w' sizes='/* */1px/* */'>
+<img srcset='/images/green-1x1.png?e22 50w, /images/green-16x16.png?e22 51w' sizes=' /**/ /**/ 1px /**/ /**/ '>
+<img srcset='/images/green-1x1.png?e23 50w, /images/green-16x16.png?e23 51w' sizes='(),1px'>
+<img srcset='/images/green-1x1.png?e24 50w, /images/green-16x16.png?e24 51w' sizes='x(),1px'>
+<img srcset='/images/green-1x1.png?e25 50w, /images/green-16x16.png?e25 51w' sizes='{},1px'>
+<img srcset='/images/green-1x1.png?e26 50w, /images/green-16x16.png?e26 51w' sizes='[],1px'>
+<img srcset='/images/green-1x1.png?e27 50w, /images/green-16x16.png?e27 51w' sizes='1px,('>
+<img srcset='/images/green-1x1.png?e28 50w, /images/green-16x16.png?e28 51w' sizes='1px,x('>
+<img srcset='/images/green-1x1.png?e29 50w, /images/green-16x16.png?e29 51w' sizes='1px,{'>
+<img srcset='/images/green-1x1.png?e30 50w, /images/green-16x16.png?e30 51w' sizes='1px,['>
+<img srcset='/images/green-1x1.png?e31 50w, /images/green-16x16.png?e31 51w' sizes='\(,1px'>
+<img srcset='/images/green-1x1.png?e32 50w, /images/green-16x16.png?e32 51w' sizes='x\(,1px'>
+<img srcset='/images/green-1x1.png?e33 50w, /images/green-16x16.png?e33 51w' sizes='\{,1px'>
+<img srcset='/images/green-1x1.png?e34 50w, /images/green-16x16.png?e34 51w' sizes='\[,1px'>
+<img srcset='/images/green-1x1.png?e35 50w, /images/green-16x16.png?e35 51w' sizes='1\p\x'>
+<img srcset='/images/green-1x1.png?e36 50w, /images/green-16x16.png?e36 51w' sizes='calc(1px)'>
+<img srcset='/images/green-1x1.png?e36a 50w, /images/green-16x16.png?e36a 51w' sizes='min(1px, 100px)'>
+<img srcset='/images/green-1x1.png?e36b 50w, /images/green-16x16.png?e36b 51w' sizes='min(-100px, 1px)'>
+<img srcset='/images/green-1x1.png?e37 50w, /images/green-16x16.png?e37 51w' sizes='(min-width:0) calc(1px)'>
+<img srcset='/images/green-1x1.png?e37a 50w, /images/green-16x16.png?e37a 51w' sizes='(min-width:0) min(1px, 100px)'>
+<img srcset='/images/green-1x1.png?e37b 50w, /images/green-16x16.png?e37b 51w' sizes='(min-width:0) max(-100px, 1px)'>
+<img srcset='/images/green-1x1.png?e38 50w, /images/green-16x16.png?e38 51w' sizes='(min-width:calc(0)) 1px'>
+<img srcset='/images/green-1x1.png?e39 50w, /images/green-16x16.png?e39 51w' sizes='(min-width:0) 1px, 100vw'>
+<img srcset='/images/green-1x1.png?e40 50w, /images/green-16x16.png?e40 51w' sizes='(min-width:0) 1px, (min-width:0) 100vw, 100vw'>
+<img srcset='/images/green-1x1.png?e41 50w, /images/green-16x16.png?e41 51w' sizes='(min-width:0) 1px'>
+<img srcset='/images/green-1x1.png?e42 50w, /images/green-16x16.png?e42 51w' sizes='not (min-width:0) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e43 50w, /images/green-16x16.png?e43 51w' sizes='(min-width:unknown-mf-value) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e44 50w, /images/green-16x16.png?e44 51w' sizes='not (min-width:unknown-mf-value) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e45 50w, /images/green-16x16.png?e45 51w' sizes='(min-width:-1px) 1px, 100vw'>
+<img srcset='/images/green-1x1.png?e46 50w, /images/green-16x16.png?e46 51w' sizes='not (min-width:-1px) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e47 50w, /images/green-16x16.png?e47 51w' sizes='(unknown-mf-name) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e48 50w, /images/green-16x16.png?e48 51w' sizes='not (unknown-mf-name) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e49 50w, /images/green-16x16.png?e49 51w' sizes='(unknown "general-enclosed") 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e50 50w, /images/green-16x16.png?e50 51w' sizes='not (unknown "general-enclosed") 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e51 50w, /images/green-16x16.png?e51 51w' sizes='unknown-general-enclosed(foo) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e52 50w, /images/green-16x16.png?e52 51w' sizes='not unknown-general-enclosed(foo) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e53 50w, /images/green-16x16.png?e53 51w' sizes='print 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e54 50w, /images/green-16x16.png?e54 51w' sizes='not print 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e55 50w, /images/green-16x16.png?e55 51w' sizes='unknown-media-type 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e56 50w, /images/green-16x16.png?e56 51w' sizes='not unknown-media-type 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e57 50w, /images/green-16x16.png?e57 51w' sizes='(min-width:0) or (min-width:0) 1px'>
+<img srcset='/images/green-1x1.png?e58 50w, /images/green-16x16.png?e58 51w' sizes='(min-width:0) or (unknown-mf-name) 1px'>
+<img srcset='/images/green-1x1.png?e59 50w, /images/green-16x16.png?e59 51w' sizes='(min-width:0) or (min-width:unknown-mf-value) 1px'>
+<img srcset='/images/green-1x1.png?e60 50w, /images/green-16x16.png?e60 51w' sizes='(min-width:0) or (min-width:-1px) 1px'>
+<img srcset='/images/green-1x1.png?e61 50w, /images/green-16x16.png?e61 51w' sizes='(min-width:0) or (unknown "general-enclosed") 1px'>
+<img srcset='/images/green-1x1.png?e62 50w, /images/green-16x16.png?e62 51w' sizes='(min-width:0) or unknown-general-enclosed(foo) 1px'>
+<img srcset='/images/green-1x1.png?e63 50w, /images/green-16x16.png?e63 51w' sizes='(min-width:0) or (]) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e64 50w, /images/green-16x16.png?e64 51w' sizes='(min-width:0) or unknown-media-type 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e65 50w, /images/green-16x16.png?e65 51w' sizes='(123) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e66 50w, /images/green-16x16.png?e66 51w' sizes='not (123) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e67 50w, /images/green-16x16.png?e67 51w' sizes='(!) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e68 50w, /images/green-16x16.png?e68 51w' sizes='not (!) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e69 50w, /images/green-16x16.png?e69 51w' sizes='! 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e70 50w, /images/green-16x16.png?e70 51w' sizes='not ! 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e71 50w, /images/green-16x16.png?e71 51w' sizes='(]) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e72 50w, /images/green-16x16.png?e72 51w' sizes='not (]) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e73 50w, /images/green-16x16.png?e73 51w' sizes='] 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e74 50w, /images/green-16x16.png?e74 51w' sizes='not ] 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e75 50w, /images/green-16x16.png?e75 51w' sizes='(}) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e76 50w, /images/green-16x16.png?e76 51w' sizes='not (}) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e77 50w, /images/green-16x16.png?e77 51w' sizes='} 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e78 50w, /images/green-16x16.png?e78 51w' sizes='not } 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e79 50w, /images/green-16x16.png?e79 51w' sizes=') 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e80 50w, /images/green-16x16.png?e80 51w' sizes='not ) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e81 50w, /images/green-16x16.png?e81 51w' sizes='(;) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e82 50w, /images/green-16x16.png?e82 51w' sizes='not (;) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e83 50w, /images/green-16x16.png?e83 51w' sizes='(.) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e84 50w, /images/green-16x16.png?e84 51w' sizes='not (.) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e85 50w, /images/green-16x16.png?e85 51w' sizes='; 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e86 50w, /images/green-16x16.png?e86 51w' sizes='not ; 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e87 50w, /images/green-16x16.png?e87 51w' sizes=', 1px'>
+<img srcset='/images/green-1x1.png?e88 50w, /images/green-16x16.png?e88 51w' sizes='1px,'>
+<img srcset='/images/green-1x1.png?e89 50w, /images/green-16x16.png?e89 51w' sizes='(min-width:0) 1px,'>
+<img srcset='/images/green-1x1.png?e90 50w, /images/green-16x16.png?e90 51w' sizes='-0e-0px'>
+<img srcset='/images/green-1x1.png?e91 50w, /images/green-16x16.png?e91 51w' sizes='+0.11e+01px'>
+<img srcset='/images/green-1x1.png?e92 50w, /images/green-16x16.png?e92 51w' sizes='0.2e1px'>
+<img srcset='/images/green-1x1.png?e93 50w, /images/green-16x16.png?e93 51w' sizes='0.3E1px'>
+<img srcset='/images/green-1x1.png?e94 50w, /images/green-16x16.png?e94 51w' sizes='.4E1px'>
+<img srcset='/images/green-1x1.png?e95 50w, /images/green-16x16.png?e95 51w' sizes='all 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e96 50w, /images/green-16x16.png?e96 51w' sizes='all and (min-width:0) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e97 50w, /images/green-16x16.png?e97 51w' sizes='min-width:0 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e98 50w, /images/green-16x16.png?e98 51w' sizes='1px, 100vw'>
+<img srcset='/images/green-1x1.png?e99 50w, /images/green-16x16.png?e99 51w' sizes='1px, (min-width:0) 100vw'>
+<img srcset='/images/green-1x1.png?e100 50w, /images/green-16x16.png?e100 51w' sizes='1px, foo bar'>
+<img srcset='/images/green-1x1.png?e101 50w, /images/green-16x16.png?e101 51w' sizes='(min-width:0) 1px, foo bar'>
+<img srcset='/images/green-1x1.png?e102 50w, /images/green-16x16.png?e102 51w' sizes='("grammar does not match") 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e103 50w, /images/green-16x16.png?e103 51w' sizes='not ("grammar does not match") 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e104 50w, /images/green-16x16.png?e104 51w' sizes='(unknown-general-enclosed !) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e105 50w, /images/green-16x16.png?e105 51w' sizes='not (unknown-general-enclosed !) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e106 50w, /images/green-16x16.png?e106 51w' sizes='(min-width:0) or (unknown-general-enclosed !) 1px'>
+<img srcset='/images/green-1x1.png?e107 50w, /images/green-16x16.png?e107 51w' sizes='not ((min-width:0) or (unknown "general-enclosed")) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e108 50w, /images/green-16x16.png?e108 51w' sizes='(max-width:0) or (unknown-general-enclosed !) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?e109 50w, /images/green-16x16.png?e109 51w' sizes='not ((max-width:0) or (unknown "general-enclosed")) 100vw, 1px'>
+<img srcset='/images/green-1x1.png?f48 50w, /images/green-16x16.png?f48 51w' sizes='calc(1px'>
+<img srcset='/images/green-1x1.png?f48a 50w, /images/green-16x16.png?f48a 51w' sizes='min(1px, 200vw'>
+<img srcset='/images/green-1x1.png?f48b 50w, /images/green-16x16.png?f48b 51w' sizes='max(-200vw, 1px'>
+<img srcset='/images/green-1x1.png?f49 50w, /images/green-16x16.png?f49 51w' sizes='(min-width:0) calc(1px'>
+<img srcset='/images/green-1x1.png?f49a 50w, /images/green-16x16.png?f49a 51w' sizes='(min-width:0) min(1px, 200vw'>
+<img srcset='/images/green-1x1.png?f49b 50w, /images/green-16x16.png?f49b 51w' sizes='(min-width:0) max(-200vw, 1px'>
+
+<p>
+<img srcset='/images/green-1x1.png?f1 50w, /images/green-16x16.png?f1 51w' sizes='100vw'>
+<img srcset='/images/green-1x1.png?f2 50w, /images/green-16x16.png?f2 51w' sizes=''>
+<img srcset='/images/green-1x1.png?f3 50w, /images/green-16x16.png?f3 51w' sizes=','>
+<img srcset='/images/green-1x1.png?f4 50w, /images/green-16x16.png?f4 51w' sizes='-1px'>
+<img srcset='/images/green-1x1.png?f5 50w, /images/green-16x16.png?f5 51w' sizes='1'>
+<img srcset='/images/green-1x1.png?f6 50w, /images/green-16x16.png?f6 51w' sizes='0.1%'>
+<img srcset='/images/green-1x1.png?f7 50w, /images/green-16x16.png?f7 51w' sizes='0.1deg'>
+<img srcset='/images/green-1x1.png?f8 50w, /images/green-16x16.png?f8 51w' sizes='0.1grad'>
+<img srcset='/images/green-1x1.png?f9 50w, /images/green-16x16.png?f9 51w' sizes='0.1rad'>
+<img srcset='/images/green-1x1.png?f10 50w, /images/green-16x16.png?f10 51w' sizes='0.1turn'>
+<img srcset='/images/green-1x1.png?f11 50w, /images/green-16x16.png?f11 51w' sizes='0.1s'>
+<img srcset='/images/green-1x1.png?f12 50w, /images/green-16x16.png?f12 51w' sizes='0.1ms'>
+<img srcset='/images/green-1x1.png?f13 50w, /images/green-16x16.png?f13 51w' sizes='0.1Hz'>
+<img srcset='/images/green-1x1.png?f14 50w, /images/green-16x16.png?f14 51w' sizes='0.1kHz'>
+<img srcset='/images/green-1x1.png?f15 50w, /images/green-16x16.png?f15 51w' sizes='0.1dpi'>
+<img srcset='/images/green-1x1.png?f16 50w, /images/green-16x16.png?f16 51w' sizes='0.1dpcm'>
+<img srcset='/images/green-1x1.png?f17 50w, /images/green-16x16.png?f17 51w' sizes='0.1dppx'>
+<img srcset='/images/green-1x1.png?f17a 50w, /images/green-16x16.png?f17a 51w' sizes='0.1x'>
+<img srcset='/images/green-1x1.png?f18 50w, /images/green-16x16.png?f18 51w' data-foo='1px' sizes='attr(data-foo, length, 1px)'>
+<img srcset='/images/green-1x1.png?f19 50w, /images/green-16x16.png?f19 51w' data-foo='1' sizes='attr(data-foo, px, 1px)'>
+<img srcset='/images/green-1x1.png?f20 50w, /images/green-16x16.png?f20 51w' sizes='toggle(1px)'>
+<img srcset='/images/green-1x1.png?f21 50w, /images/green-16x16.png?f21 51w' sizes='inherit'>
+<img srcset='/images/green-1x1.png?f23 50w, /images/green-16x16.png?f23 51w' sizes='initial'>
+<img srcset='/images/green-1x1.png?f24 50w, /images/green-16x16.png?f24 51w' sizes='unset'>
+<img srcset='/images/green-1x1.png?f25 50w, /images/green-16x16.png?f25 51w' sizes='default'>
+<img srcset='/images/green-1x1.png?f26 50w, /images/green-16x16.png?f26 51w' sizes='1/* */px'>
+<img srcset='/images/green-1x1.png?f27 50w, /images/green-16x16.png?f27 51w' sizes='1p/* */x'>
+<img srcset='/images/green-1x1.png?f28 50w, /images/green-16x16.png?f28 51w' sizes='-/**/0'>
+<img srcset='/images/green-1x1.png?f29 50w, /images/green-16x16.png?f29 51w' sizes='((),1px'>
+<img srcset='/images/green-1x1.png?f30 50w, /images/green-16x16.png?f30 51w' sizes='x(x(),1px'>
+<img srcset='/images/green-1x1.png?f31 50w, /images/green-16x16.png?f31 51w' sizes='{{},1px'>
+<img srcset='/images/green-1x1.png?f32 50w, /images/green-16x16.png?f32 51w' sizes='[[],1px'>
+<img srcset='/images/green-1x1.png?f33 50w, /images/green-16x16.png?f33 51w' sizes='1px !important'>
+<img srcset='/images/green-1x1.png?f34 50w, /images/green-16x16.png?f34 51w' sizes='\1px'>
+<img srcset='/images/green-1x1.png?f35 50w, /images/green-16x16.png?f35 51w' sizes='all 1px'>
+<img srcset='/images/green-1x1.png?f36 50w, /images/green-16x16.png?f36 51w' sizes='all and (min-width:0) 1px'>
+<img srcset='/images/green-1x1.png?f37 50w, /images/green-16x16.png?f37 51w' sizes='min-width:0 1px'>
+<img srcset='/images/green-1x1.png?f38 50w, /images/green-16x16.png?f38 51w' sizes='100vw, 1px'>
+<img srcset='/images/green-1x1.png?f39 50w, /images/green-16x16.png?f39 51w' sizes='100vw, (min-width:0) 1px'>
+<img srcset='/images/green-1x1.png?f40 50w, /images/green-16x16.png?f40 51w' sizes='foo bar'>
+<img srcset='/images/green-1x1.png?f41 50w, /images/green-16x16.png?f41 51w' sizes='foo-bar'>
+<img srcset='/images/green-1x1.png?f42 50w, /images/green-16x16.png?f42 51w' sizes='(min-width:0) 1px foo bar'>
+<img srcset='/images/green-1x1.png?f43 50w, /images/green-16x16.png?f43 51w' sizes='(min-width:0) 0.1%'>
+<img srcset='/images/green-1x1.png?f44 50w, /images/green-16x16.png?f44 51w' sizes='(min-width:0) 1'>
+<img srcset='/images/green-1x1.png?f45 50w, /images/green-16x16.png?f45 51w' sizes='-1e0px'>
+<img srcset='/images/green-1x1.png?f46 50w, /images/green-16x16.png?f46 51w' sizes='1e1.5px'>
+<img srcset='/images/green-1x1.png?f47 50w, /images/green-16x16.png?f47 51w' style='--foo: 1px' sizes='var(--foo)'>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/source-media-outside-doc.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/source-media-outside-doc.html
new file mode 100644
index 0000000000..5997e14e4b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/source-media-outside-doc.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>Image source selection using media queries is performed for img elements outside the document</title>
+<link rel="help" href="https://html.spec.whatwg.org/#reacting-to-environment-changes">
+<link rel="help" href="https://html.spec.whatwg.org/#reacting-to-dom-mutations">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe width="350" height="100" onload="async_test(this.contentWindow.run)" srcdoc="
+<!DOCTYPE html>
+<script>
+const { assert_equals } = parent;
+const iframe = parent.document.querySelector('iframe');
+
+function run(t) {
+ const picture = document.createElement('picture');
+
+ const source1 = document.createElement('source');
+ source1.setAttribute('media', '(min-width: 300px)');
+ source1.setAttribute('srcset', 'data:,a');
+ picture.append(source1);
+
+ const source2 = document.createElement('source');
+ source2.setAttribute('media', '(min-width: 200px)');
+ source2.setAttribute('srcset', 'data:,b');
+ picture.append(source2);
+
+ const img = document.createElement('img');
+ img.src = 'data:,c';
+ picture.append(img);
+
+ queueMicrotask(t.step_func(function() {
+ assert_equals(img.currentSrc, 'data:,a', 'Initial currentSrc value');
+ matchMedia(source1.media).addEventListener(
+ 'change',
+ function() {
+ queueMicrotask(t.step_func(function() {
+ assert_equals(img.currentSrc, 'data:,b', 'After MQ change');
+ img.remove();
+ queueMicrotask(t.step_func(function() {
+ assert_equals(img.currentSrc, 'data:,c', 'After removing img');
+ t.done();
+ }));
+ }));
+ },
+ { once: true }
+ );
+ iframe.width = 250;
+ }));
+}
+</script>
+"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/avoid-reload-on-resize.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/avoid-reload-on-resize.html
new file mode 100644
index 0000000000..52366dcaa7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/avoid-reload-on-resize.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Avoid srcset image reloads when viewport resizes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({single_test:true});
+const image_was_loaded = () => {
+ const iframe = document.getElementById("iframe");
+ // Resize the iframe
+ iframe.width="400";
+ // Wait 500 ms
+ step_timeout(() => {
+ // Check that the iframe only loaded a single resource
+ const entries = iframe.contentWindow.performance.getEntriesByType("resource");
+ assert_equals(entries.length, 1);
+ done();
+ }, 500);
+}
+</script>
+<iframe id=iframe width="401" src="resources/resized.html" onload="image_was_loaded()"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/common.js b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/common.js
new file mode 100644
index 0000000000..d4d2c7534c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/common.js
@@ -0,0 +1,25 @@
+setup({explicit_done:true});
+
+function check(img) {
+ var name = format_value(img.getAttribute('srcset'));
+ if (img.hasAttribute('sizes')) {
+ name += ' sizes=' + format_value(img.getAttribute('sizes'));
+ }
+ if (img.hasAttribute('data-desc')) {
+ name += ' (' + img.getAttribute('data-desc') + ')';
+ }
+ test(function() {
+ var expect = img.dataset.expect;
+ if ('resolve' in img.dataset) {
+ var a = document.createElement('a');
+ a.href = expect;
+ expect = a.href;
+ }
+ assert_equals(img.currentSrc, expect);
+ }, name);
+}
+
+onload = function() {
+ [].forEach.call(document.images, check);
+ done();
+};
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html
new file mode 100644
index 0000000000..ce1e4cebe5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/parse-a-srcset-attribute.html
@@ -0,0 +1,245 @@
+<!doctype html>
+<title>img parse a srcset attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=common.js></script>
+<div id=log></div>
+<!-- splitting loop -->
+<img srcset='' data-expect=''>
+<img srcset=',' data-expect=''>
+<img srcset=',,,' data-expect=''>
+<img srcset=' data:,a 1x ' data-expect='data:,a'>
+<img srcset='&#x9;&#x9;data:,a&#x9;&#x9;1x&#x9;&#x9;' data-expect='data:,a'>
+<img srcset='&#xA;&#xA;data:,a&#xA;&#xA;1x&#xA;&#xA;' data-expect='data:,a'>
+<img srcset='&#xB;&#xB;data:,a&#xB;&#xB;1x&#xB;&#xB;' data-expect='&#xB;&#xB;data:,a&#xB;&#xB;1x&#xB;&#xB;' data-resolve>
+<img srcset='&#xC;&#xC;data:,a&#xC;&#xC;1x&#xC;&#xC;' data-expect='data:,a'>
+<img srcset='&#xD;&#xD;data:,a&#xD;&#xD;1x&#xD;&#xD;' data-expect='data:,a'>
+<img srcset='&#xE;&#xE;data:,a&#xE;&#xE;1x&#xE;&#xE;' data-expect='&#xE;&#xE;data:,a&#xE;&#xE;1x&#xE;&#xE;' data-resolve>
+<img srcset='&#xF;&#xF;data:,a&#xF;&#xF;1x&#xF;&#xF;' data-expect='&#xF;&#xF;data:,a&#xF;&#xF;1x&#xF;&#xF;' data-resolve>
+<img srcset='&#x10;&#x10;data:,a&#x10;&#x10;1x&#x10;&#x10;' data-expect='&#x10;&#x10;data:,a&#x10;&#x10;1x&#x10;&#x10;' data-resolve>
+<img srcset='data:,a' data-expect='data:,a'>
+<img srcset='data:,a ' data-expect='data:,a'>
+<img srcset='data:,a ,' data-expect='data:,a'>
+<img srcset='data:,a,' data-expect='data:,a'>
+<img srcset='data:,a, ' data-expect='data:,a'>
+<img srcset='data:,a,,,' data-expect='data:,a'>
+<img srcset='data:,a,, , ' data-expect='data:,a'>
+<img srcset=' data:,a' data-expect='data:,a'>
+<img srcset=',,,data:,a' data-expect='data:,a'>
+<img srcset=' , ,,data:,a' data-expect='data:,a'>
+<img srcset='&nbsp;data:,a' data-expect='&nbsp;data:,a' data-resolve>
+<img srcset='data:,a&nbsp;' data-expect='data:,a&nbsp;' data-resolve>
+<!-- descriptor tokenizer -->
+<img srcset='data:,a 1x' data-expect='data:,a'>
+<img srcset='data:,a 1x ' data-expect='data:,a'>
+<img srcset='data:,a 1x,' data-expect='data:,a'>
+<img srcset='data:,a ( , data:,b 1x, ), data:,c' data-expect='data:,c'>
+<img srcset='data:,a ((( , data:,b 1x, ), data:,c' data-expect='data:,c'>
+<img srcset='data:,a [ , data:,b 1x, ], data:,c' data-expect='data:,b'>
+<img srcset='data:,a { , data:,b 1x, }, data:,c' data-expect='data:,b'>
+<img srcset='data:,a " , data:,b 1x, ", data:,c' data-expect='data:,b'>
+<img srcset='data:,a \,data:;\,b, data:,c' data-expect='data:;\,b'>
+<img srcset='data:,a, data:,b (' data-expect='data:,a'>
+<img srcset='data:,a, data:,b ( ' data-expect='data:,a'>
+<img srcset='data:,a, data:,b (,' data-expect='data:,a'>
+<img srcset='data:,a, data:,b (x' data-expect='data:,a'>
+<img srcset='data:,a, data:,b ()' data-expect='data:,a'>
+<img srcset='data:,a (, data:,b' data-expect=''>
+<img srcset='data:,a /*, data:,b, data:,c */' data-expect='data:,b'>
+<img srcset='data:,a //, data:,b' data-expect='data:,b'>
+<!-- descriptor parser -->
+<img srcset='data:,a foo' data-expect=''>
+<img srcset='data:,a foo foo' data-expect=''>
+<img srcset='data:,a foo 1x' data-expect=''>
+<img srcset='data:,a foo 1x foo' data-expect=''>
+<img srcset='data:,a foo 1w' data-expect=''>
+<img srcset='data:,a foo 1w foo' data-expect=''>
+<img srcset='data:,a 1x 1x' data-expect=''>
+<img srcset='data:,a 1w 1w' data-expect=''>
+<img srcset='data:,a 1w 1x' data-expect=''>
+<img srcset='data:,a 1x 1w' data-expect=''>
+<img srcset='data:,a 1w 1h' data-expect='data:,a'><!-- should fail for x-only impl -->
+<img srcset='data:,a 1h 1w' data-expect='data:,a'><!-- should fail for x-only impl -->
+<img srcset='data:,a 1h 1h' data-expect=''>
+<img srcset='data:,a 1h 1x' data-expect=''>
+<img srcset='data:,a 1h 1w 1x' data-expect=''>
+<img srcset='data:,a 1x 1w 1h' data-expect=''>
+<img srcset='data:,a 1w' data-expect='data:,a'><!-- should fail for x-only impl -->
+<img srcset='data:,a 1h' data-expect=''>
+<img srcset='data:,a 1h foo' data-expect=''>
+<img srcset='data:,a foo 1h' data-expect=''>
+<img srcset='data:,a 0w' data-expect=''>
+<img srcset='data:,a -1w' data-expect=''>
+<img srcset='data:,a 1w -1w' data-expect=''>
+<img srcset='data:,a 1.0w' data-expect=''>
+<img srcset='data:,a 1w 1.0w' data-expect=''>
+<img srcset='data:,a 1e0w' data-expect=''>
+<img srcset='data:,a 1w 1e0w' data-expect=''>
+<img srcset='data:,a 1www' data-expect=''>
+<img srcset='data:,a 1w 1www' data-expect=''>
+<img srcset='data:,a +1w' data-expect=''>
+<img srcset='data:,a 1w +1w' data-expect=''>
+<img srcset='data:,a 1W' data-expect=''>
+<img srcset='data:,a 1w 1W' data-expect=''>
+<img srcset='data:,a Infinityw' data-expect=''>
+<img srcset='data:,a 1w Infinityw' data-expect=''>
+<img srcset='data:,a NaNw' data-expect=''>
+<img srcset='data:,a 1w NaNw' data-expect=''>
+<img srcset='data:,a 0x1w' data-expect=''>
+<img srcset='data:,a 0X1w' data-expect=''>
+<img srcset='data:,a 1&#x1;w' data-expect='' data-desc='trailing U+0001'>
+<img srcset='data:,a 1&nbsp;w' data-expect='' data-desc='trailing U+00A0'>
+<img srcset='data:,a 1&#x1680;w' data-expect='' data-desc='trailing U+1680'>
+<img srcset='data:,a 1&#x2000;w' data-expect='' data-desc='trailing U+2000'>
+<img srcset='data:,a 1&#x2001;w' data-expect='' data-desc='trailing U+2001'>
+<img srcset='data:,a 1&#x2002;w' data-expect='' data-desc='trailing U+2002'>
+<img srcset='data:,a 1&#x2003;w' data-expect='' data-desc='trailing U+2003'>
+<img srcset='data:,a 1&#x2004;w' data-expect='' data-desc='trailing U+2004'>
+<img srcset='data:,a 1&#x2005;w' data-expect='' data-desc='trailing U+2005'>
+<img srcset='data:,a 1&#x2006;w' data-expect='' data-desc='trailing U+2006'>
+<img srcset='data:,a 1&#x2007;w' data-expect='' data-desc='trailing U+2007'>
+<img srcset='data:,a 1&#x2008;w' data-expect='' data-desc='trailing U+2008'>
+<img srcset='data:,a 1&#x2009;w' data-expect='' data-desc='trailing U+2009'>
+<img srcset='data:,a 1&#x200A;w' data-expect='' data-desc='trailing U+200A'>
+<img srcset='data:,a 1&#x200C;w' data-expect='' data-desc='trailing U+200C'>
+<img srcset='data:,a 1&#x200D;w' data-expect='' data-desc='trailing U+200D'>
+<img srcset='data:,a 1&#x202F;w' data-expect='' data-desc='trailing U+202F'>
+<img srcset='data:,a 1&#x205F;w' data-expect='' data-desc='trailing U+205F'>
+<img srcset='data:,a 1&#x3000;w' data-expect='' data-desc='trailing U+3000'>
+<img srcset='data:,a 1&#xFEFF;w' data-expect='' data-desc='trailing U+FEFF'>
+<img srcset='data:,a &#x1;1w' data-expect='' data-desc='leading U+0001'>
+<img srcset='data:,a &nbsp;1w' data-expect='' data-desc='leading U+00A0'>
+<img srcset='data:,a &#x1680;1w' data-expect='' data-desc='leading U+1680'>
+<img srcset='data:,a &#x2000;1w' data-expect='' data-desc='leading U+2000'>
+<img srcset='data:,a &#x2001;1w' data-expect='' data-desc='leading U+2001'>
+<img srcset='data:,a &#x2002;1w' data-expect='' data-desc='leading U+2002'>
+<img srcset='data:,a &#x2003;1w' data-expect='' data-desc='leading U+2003'>
+<img srcset='data:,a &#x2004;1w' data-expect='' data-desc='leading U+2004'>
+<img srcset='data:,a &#x2005;1w' data-expect='' data-desc='leading U+2005'>
+<img srcset='data:,a &#x2006;1w' data-expect='' data-desc='leading U+2006'>
+<img srcset='data:,a &#x2007;1w' data-expect='' data-desc='leading U+2007'>
+<img srcset='data:,a &#x2008;1w' data-expect='' data-desc='leading U+2008'>
+<img srcset='data:,a &#x2009;1w' data-expect='' data-desc='leading U+2009'>
+<img srcset='data:,a &#x200A;1w' data-expect='' data-desc='leading U+200A'>
+<img srcset='data:,a &#x200C;1w' data-expect='' data-desc='leading U+200C'>
+<img srcset='data:,a &#x200D;1w' data-expect='' data-desc='leading U+200D'>
+<img srcset='data:,a &#x202F;1w' data-expect='' data-desc='leading U+202F'>
+<img srcset='data:,a &#x205F;1w' data-expect='' data-desc='leading U+205F'>
+<img srcset='data:,a &#x3000;1w' data-expect='' data-desc='leading U+3000'>
+<img srcset='data:,a &#xFEFF;1w' data-expect='' data-desc='leading U+FEFF'>
+<img srcset='data:,a 0x' data-expect='data:,a'>
+<img srcset='data:,a -0x' data-expect='data:,a'>
+<img srcset='data:,a 1x -0x' data-expect=''>
+<img srcset='data:,a -1x' data-expect=''>
+<img srcset='data:,a 1x -1x' data-expect=''>
+<img srcset='data:,a 1e0x' data-expect='data:,a'>
+<img srcset='data:,a 1E0x' data-expect='data:,a'>
+<img srcset='data:,a 1e-1x' data-expect='data:,a'>
+<img srcset='data:,a 1.5e1x' data-expect='data:,a'>
+<img srcset='data:,a -x' data-expect=''>
+<img srcset='data:,a .x' data-expect=''>
+<img srcset='data:,a -.x' data-expect=''>
+<img srcset='data:,a 1.x' data-expect=''>
+<img srcset='data:,a .5x' data-expect='data:,a'>
+<img srcset='data:,a .5e1x' data-expect='data:,a'>
+<img srcset='data:,a 1x 1.5e1x' data-expect=''>
+<img srcset='data:,a 1x 1e1.5x' data-expect=''>
+<img srcset='data:,a 1.0x' data-expect='data:,a'>
+<img srcset='data:,a 1x 1.0x' data-expect=''>
+<img srcset='data:,a +1x' data-expect=''>
+<img srcset='data:,a 1X' data-expect=''>
+<img srcset='data:,a Infinityx' data-expect=''>
+<img srcset='data:,a NaNx' data-expect=''>
+<img srcset='data:,a 0x1x' data-expect=''>
+<img srcset='data:,a 0X1x' data-expect=''>
+<img srcset='data:,a 1&#x1;x' data-expect='' data-desc='trailing U+0001'>
+<img srcset='data:,a 1&nbsp;x' data-expect='' data-desc='trailing U+00A0'>
+<img srcset='data:,a 1&#x1680;x' data-expect='' data-desc='trailing U+1680'>
+<img srcset='data:,a 1&#x2000;x' data-expect='' data-desc='trailing U+2000'>
+<img srcset='data:,a 1&#x2001;x' data-expect='' data-desc='trailing U+2001'>
+<img srcset='data:,a 1&#x2002;x' data-expect='' data-desc='trailing U+2002'>
+<img srcset='data:,a 1&#x2003;x' data-expect='' data-desc='trailing U+2003'>
+<img srcset='data:,a 1&#x2004;x' data-expect='' data-desc='trailing U+2004'>
+<img srcset='data:,a 1&#x2005;x' data-expect='' data-desc='trailing U+2005'>
+<img srcset='data:,a 1&#x2006;x' data-expect='' data-desc='trailing U+2006'>
+<img srcset='data:,a 1&#x2007;x' data-expect='' data-desc='trailing U+2007'>
+<img srcset='data:,a 1&#x2008;x' data-expect='' data-desc='trailing U+2008'>
+<img srcset='data:,a 1&#x2009;x' data-expect='' data-desc='trailing U+2009'>
+<img srcset='data:,a 1&#x200A;x' data-expect='' data-desc='trailing U+200A'>
+<img srcset='data:,a 1&#x200C;x' data-expect='' data-desc='trailing U+200C'>
+<img srcset='data:,a 1&#x200D;x' data-expect='' data-desc='trailing U+200D'>
+<img srcset='data:,a 1&#x202F;x' data-expect='' data-desc='trailing U+202F'>
+<img srcset='data:,a 1&#x205F;x' data-expect='' data-desc='trailing U+205F'>
+<img srcset='data:,a 1&#x3000;x' data-expect='' data-desc='trailing U+3000'>
+<img srcset='data:,a 1&#xFEFF;x' data-expect='' data-desc='trailing U+FEFF'>
+<img srcset='data:,a &#x1;1x' data-expect='' data-desc='leading U+0001'>
+<img srcset='data:,a &nbsp;1x' data-expect='' data-desc='leading U+00A0'>
+<img srcset='data:,a &#x1680;1x' data-expect='' data-desc='leading U+1680'>
+<img srcset='data:,a &#x2000;1x' data-expect='' data-desc='leading U+2000'>
+<img srcset='data:,a &#x2001;1x' data-expect='' data-desc='leading U+2001'>
+<img srcset='data:,a &#x2002;1x' data-expect='' data-desc='leading U+2002'>
+<img srcset='data:,a &#x2003;1x' data-expect='' data-desc='leading U+2003'>
+<img srcset='data:,a &#x2004;1x' data-expect='' data-desc='leading U+2004'>
+<img srcset='data:,a &#x2005;1x' data-expect='' data-desc='leading U+2005'>
+<img srcset='data:,a &#x2006;1x' data-expect='' data-desc='leading U+2006'>
+<img srcset='data:,a &#x2007;1x' data-expect='' data-desc='leading U+2007'>
+<img srcset='data:,a &#x2008;1x' data-expect='' data-desc='leading U+2008'>
+<img srcset='data:,a &#x2009;1x' data-expect='' data-desc='leading U+2009'>
+<img srcset='data:,a &#x200A;1x' data-expect='' data-desc='leading U+200A'>
+<img srcset='data:,a &#x200C;1x' data-expect='' data-desc='leading U+200C'>
+<img srcset='data:,a &#x200D;1x' data-expect='' data-desc='leading U+200D'>
+<img srcset='data:,a &#x202F;1x' data-expect='' data-desc='leading U+202F'>
+<img srcset='data:,a &#x205F;1x' data-expect='' data-desc='leading U+205F'>
+<img srcset='data:,a &#x3000;1x' data-expect='' data-desc='leading U+3000'>
+<img srcset='data:,a &#xFEFF;1x' data-expect='' data-desc='leading U+FEFF'>
+<img srcset='data:,a 1w 0h' data-expect=''>
+<img srcset='data:,a 1w -1h' data-expect=''>
+<img srcset='data:,a 1w 1.0h' data-expect=''>
+<img srcset='data:,a 1w 1e0h' data-expect=''>
+<img srcset='data:,a 1w 1hhh' data-expect=''>
+<img srcset='data:,a 1w +1h' data-expect=''>
+<img srcset='data:,a 1w 1H' data-expect=''>
+<img srcset='data:,a 1w Infinityh' data-expect=''>
+<img srcset='data:,a 1w NaNh' data-expect=''>
+<img srcset='data:,a 0x1h' data-expect=''>
+<img srcset='data:,a 0X1h' data-expect=''>
+<img srcset='data:,a 1w 1&#x1;h' data-expect='' data-desc='trailing U+0001'>
+<img srcset='data:,a 1w 1&nbsp;h' data-expect='' data-desc='trailing U+00A0'>
+<img srcset='data:,a 1w 1&#x1680;h' data-expect='' data-desc='trailing U+1680'>
+<img srcset='data:,a 1w 1&#x2000;h' data-expect='' data-desc='trailing U+2000'>
+<img srcset='data:,a 1w 1&#x2001;h' data-expect='' data-desc='trailing U+2001'>
+<img srcset='data:,a 1w 1&#x2002;h' data-expect='' data-desc='trailing U+2002'>
+<img srcset='data:,a 1w 1&#x2003;h' data-expect='' data-desc='trailing U+2003'>
+<img srcset='data:,a 1w 1&#x2004;h' data-expect='' data-desc='trailing U+2004'>
+<img srcset='data:,a 1w 1&#x2005;h' data-expect='' data-desc='trailing U+2005'>
+<img srcset='data:,a 1w 1&#x2006;h' data-expect='' data-desc='trailing U+2006'>
+<img srcset='data:,a 1w 1&#x2007;h' data-expect='' data-desc='trailing U+2007'>
+<img srcset='data:,a 1w 1&#x2008;h' data-expect='' data-desc='trailing U+2008'>
+<img srcset='data:,a 1w 1&#x2009;h' data-expect='' data-desc='trailing U+2009'>
+<img srcset='data:,a 1w 1&#x200A;h' data-expect='' data-desc='trailing U+200A'>
+<img srcset='data:,a 1w 1&#x200C;h' data-expect='' data-desc='trailing U+200C'>
+<img srcset='data:,a 1w 1&#x200D;h' data-expect='' data-desc='trailing U+200D'>
+<img srcset='data:,a 1w 1&#x202F;h' data-expect='' data-desc='trailing U+202F'>
+<img srcset='data:,a 1w 1&#x205F;h' data-expect='' data-desc='trailing U+205F'>
+<img srcset='data:,a 1w 1&#x3000;h' data-expect='' data-desc='trailing U+3000'>
+<img srcset='data:,a 1w 1&#xFEFF;h' data-expect='' data-desc='trailing U+FEFF'>
+<img srcset='data:,a 1w &#x1;1h' data-expect='' data-desc='leading U+0001'>
+<img srcset='data:,a 1w &nbsp;1h' data-expect='' data-desc='leading U+00A0'>
+<img srcset='data:,a 1w &#x1680;1h' data-expect='' data-desc='leading U+1680'>
+<img srcset='data:,a 1w &#x2000;1h' data-expect='' data-desc='leading U+2000'>
+<img srcset='data:,a 1w &#x2001;1h' data-expect='' data-desc='leading U+2001'>
+<img srcset='data:,a 1w &#x2002;1h' data-expect='' data-desc='leading U+2002'>
+<img srcset='data:,a 1w &#x2003;1h' data-expect='' data-desc='leading U+2003'>
+<img srcset='data:,a 1w &#x2004;1h' data-expect='' data-desc='leading U+2004'>
+<img srcset='data:,a 1w &#x2005;1h' data-expect='' data-desc='leading U+2005'>
+<img srcset='data:,a 1w &#x2006;1h' data-expect='' data-desc='leading U+2006'>
+<img srcset='data:,a 1w &#x2007;1h' data-expect='' data-desc='leading U+2007'>
+<img srcset='data:,a 1w &#x2008;1h' data-expect='' data-desc='leading U+2008'>
+<img srcset='data:,a 1w &#x2009;1h' data-expect='' data-desc='leading U+2009'>
+<img srcset='data:,a 1w &#x200A;1h' data-expect='' data-desc='leading U+200A'>
+<img srcset='data:,a 1w &#x200C;1h' data-expect='' data-desc='leading U+200C'>
+<img srcset='data:,a 1w &#x200D;1h' data-expect='' data-desc='leading U+200D'>
+<img srcset='data:,a 1w &#x202F;1h' data-expect='' data-desc='leading U+202F'>
+<img srcset='data:,a 1w &#x205F;1h' data-expect='' data-desc='leading U+205F'>
+<img srcset='data:,a 1w &#x3000;1h' data-expect='' data-desc='leading U+3000'>
+<img srcset='data:,a 1w &#xFEFF;1h' data-expect='' data-desc='leading U+FEFF'>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.png b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.png
new file mode 100644
index 0000000000..d26878c9f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.png
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.png.headers b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.png.headers
new file mode 100644
index 0000000000..edaec7ad15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/image.png.headers
@@ -0,0 +1,3 @@
+Cache-Control: no-store
+
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/resized.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/resized.html
new file mode 100644
index 0000000000..6fb6847a66
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/resources/resized.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<img srcset="image.png?400 400w, image.png?800 800w, image.png?1600 1600w" sizes="50vw">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/select-an-image-source.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/select-an-image-source.html
new file mode 100644
index 0000000000..292395d3ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/select-an-image-source.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>img select an image source</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=common.js></script>
+<div id=log></div>
+<!-- dup entries -->
+<img srcset='data:,a 1x, data:,b 1x' data-expect='data:,a'>
+<img srcset='data:,a , data:,b 1x' data-expect='data:,a'>
+<img srcset='data:,a 1x, data:,b' data-expect='data:,a'>
+<img srcset='data:,a 1w, data:,b 1w' data-expect='data:,a'>
+<img srcset='data:,a 1w 1h, data:,b 1w' data-expect='data:,a'>
+<img srcset='data:,a 1w, data:,b 1w 1h' data-expect='data:,a'>
+<img srcset='data:,a 1w 1h, data:,b 1w 2h' data-expect='data:,a'>
+<img srcset='data:,a 1w 2h, data:,b 1w 1h' data-expect='data:,a'>
+<img srcset='data:,a , data:,b' data-expect='data:,a'>
+<img srcset='data:,a 1w, data:,b 1x' sizes='1px' data-expect='data:,a'>
+<img srcset='data:,a 1x, data:,b 1w' sizes='1px' data-expect='data:,a'>
+<img srcset='data:,a 1w, data:,b 2x' sizes='0.5px' data-expect='data:,a'>
+<img srcset='data:,a 2x, data:,b 1w' sizes='0.5px' data-expect='data:,a'>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html
new file mode 100644
index 0000000000..2cc74e2b8f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>source element in picture handles dynamic media change correctly.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1523627">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<picture id="pic">
+ <source srcset="data:,a">
+</picture>
+<script>
+let t = async_test("Dynamic media change is handled correctly");
+
+let pic = document.getElementById("pic");
+// Something that will never match.
+pic.querySelector("source").setAttribute("media", "not all");
+
+let img = document.createElement("img");
+img.src = "data:,b";
+pic.appendChild(img);
+
+onload = t.step_func_done(function() {
+ assert_equals(img.currentSrc, "data:,b");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/external-sheet.svg b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/external-sheet.svg
new file mode 100644
index 0000000000..fd2eda7164
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/external-sheet.svg
@@ -0,0 +1,4 @@
+<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
+ <style xmlns="http://www.w3.org/1999/xhtml">:root { background-color: green }</style>
+ <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" type="text/css" href="red-bg.css" />
+</svg>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/red-bg.css b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/red-bg.css
new file mode 100644
index 0000000000..da9af10628
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/red-bg.css
@@ -0,0 +1,2 @@
+:root { background: red }
+
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/relevant-mutations.js b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/relevant-mutations.js
new file mode 100644
index 0000000000..7105b03708
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/support/relevant-mutations.js
@@ -0,0 +1,15 @@
+setup({explicit_done:true});
+
+function t(desc, func, expect) {
+ async_test(function() {
+ var img = document.querySelector('[data-desc="' + desc + '"]');
+ img.onload = img.onerror = this.unreached_func('update the image data was run');
+ if (expect == 'timeout') {
+ setTimeout(this.step_func_done(), 1000);
+ } else {
+ img['on' + expect] = this.step_func_done();
+ setTimeout(this.unreached_func('update the image data didn\'t run'), 1000);
+ }
+ func.call(this, img);
+ }, desc);
+}
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet-ref.html
new file mode 100644
index 0000000000..fdab582933
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>Test reference</title>
+<p>You should see a green square below.</p>
+<div style="background:green;width:100px;height:100px"></div>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet.html
new file mode 100644
index 0000000000..a09dd7cc54
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/svg-img-with-external-stylesheet.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>An img element with an svg src should not load external resources from the svg file.</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element">
+<link rel="match" href="svg-img-with-external-stylesheet-ref.html">
+<p>You should see a green square below.</p>
+<img width="100" height="100" src="support/external-sheet.svg">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-media.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-media.html
new file mode 100644
index 0000000000..dd679ef571
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-media.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>img update media</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ var t = async_test('set media after src updates selected image');
+
+ var img;
+
+ onload = t.step_func(function() {
+ img = document.querySelector('img');
+ img.addEventListener('load', t.step_func_done(onImgLoad));
+
+ var source = document.querySelector('source[data-media]');
+ source.setAttribute('media', source.getAttribute('data-media'));
+ });
+
+ function onImgLoad() {
+ img.removeEventListener('load', onImgLoad);
+
+ assert_true(img.currentSrc.indexOf(img.getAttribute('data-expect')) > -1);
+ }
+
+</script>
+
+<div id="log"></div>
+<picture>
+ <source srcset="/images/fail.gif" data-media="(max-width: 1px)" />
+ <source srcset="/images/smiley.png" />
+ <img data-expect="/images/smiley.png">
+</picture> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-src-complete.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-src-complete.html
new file mode 100644
index 0000000000..de3926a296
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-src-complete.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Changing the img src should retain the 'complete' property</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p id="display"><img src="image.png"></p>
+<script>
+ setup({ single_test: true });
+
+ function check() {
+ var img = document.querySelector("img");
+ assert_true(img.complete, "By onload, image should have loaded");
+ img.src = `image.png?${Math.random()}`;
+ assert_false(img.complete, "Now that we're loading we should no longer be complete");
+ img.onload = function () {
+ assert_true(img.complete, "The new thing should have loaded.");
+ done();
+ }
+ }
+
+ onload = function () {
+ check();
+ };
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/current-request-microtask.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/current-request-microtask.html
new file mode 100644
index 0000000000..125b37eadb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/current-request-microtask.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>An img's current request should be updated in a microtask after selecting an image source</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+
+<script>
+async_test(function(t) {
+ const picture = document.createElement("picture");
+
+ const nonMatchingSource = document.createElement("source");
+ nonMatchingSource.media = "not all";
+ nonMatchingSource.srcset = "data:,a";
+ picture.append(nonMatchingSource);
+
+ const matchingSource = document.createElement("source");
+ matchingSource.media = "all";
+ matchingSource.srcset = "data:,b";
+ picture.append(matchingSource);
+
+ const img = document.createElement("img");
+ img.src = "data:,c";
+
+ assert_equals(img.currentSrc, "", "after assigning to img.src but before the corresponding microtask is run");
+
+ queueMicrotask(t.step_func(function() {
+ assert_equals(img.currentSrc, "data:,c", "after assigning to img.src and after corresponding microtask is run");
+
+ picture.append(img);
+ assert_equals(img.currentSrc, "data:,c", "after appending img to picture but before the corresponding microtask is run");
+
+ queueMicrotask(t.step_func(function() {
+ assert_equals(img.currentSrc, "data:,b", "after appending img to picture and after the corresponding microtask is run");
+ t.done();
+ }));
+ }));
+}, "currentSrc is updated only after the microtask that updates the current request is run");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/fail-to-resolve.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/fail-to-resolve.html
new file mode 100644
index 0000000000..959ceaa979
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-image-data/fail-to-resolve.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>img update the image data: fail to resolve URL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+
+<img src="//[">
+<img srcset="//[">
+<img srcset="//[" src="/images/red.png">
+<img srcset="//[, /images/red.png">
+
+<script>
+setup({explicit_done: true});
+
+var expected = '//[';
+
+onload = function() {
+ [].forEach.call(document.images, function(img) {
+ test(function() {
+ assert_equals(img.currentSrc, expected);
+ }, img.outerHTML);
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-source-set.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-source-set.html
new file mode 100644
index 0000000000..063667baa9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/update-the-source-set.html
@@ -0,0 +1,140 @@
+<!doctype html>
+<title>img update the source set</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({explicit_done:true});
+
+function check(p) {
+ var img = p.querySelector('[data-expect]');
+ test(function() {
+ var expect = img.dataset.expect;
+ if ('resolve' in img.dataset) {
+ var a = document.createElement('a');
+ a.href = expect;
+ expect = a.href;
+ }
+ assert_equals(img.currentSrc, expect);
+ }, p.innerHTML);
+}
+
+onload = function() {
+ [].forEach.call(document.querySelectorAll('div:not([id])'), check);
+ done();
+};
+
+</script>
+<div id=log></div>
+<div><img data-expect=''></div>
+<div><img src data-expect=''></div>
+<div><img src='data:,a' data-expect='data:,a'></div>
+<div><img srcset src='data:,a' data-expect='data:,a'></div>
+<div><img srcset='data:,b' src='data:,a' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b' data-expect='data:,b'><!-- srcset after src --></div>
+<div><img src='data:,a' srcset='data:,b 1x' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 1.0x' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 1e0x' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 10000w' sizes='1px' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 10000w, data:,c 10000x' sizes='1px' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 10000x, data:,c 10000w' sizes='1px' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 1w' sizes='10000px' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 1w, data:,c 0.0001x' sizes='10000px' data-expect='data:,b'></div>
+<div><img src='data:,a' srcset='data:,b 0.0001x, data:,c 1w' sizes='10000px' data-expect='data:,b'></div>
+<div><img srcset='data:,a' data-expect='data:,a'></div>
+
+<!-- child is not a <source> -->
+
+<div><picture>foo<img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><!--foo--><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><br><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><p></p><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><video><source srcset='data:,b'></video><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><span><source srcset='data:,b'></span><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><svg><source srcset='data:,b'/></svg><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><svg/><source srcset='data:,b'/><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><svg><font/><source srcset='data:,b'/></svg><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><svg><!--<font face> tag breaks out of svg--><font face></font><source srcset='data:,b'/></svg><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><img src='data:,a'><img src='data:,b' data-expect='data:,b'></picture></div>
+
+<!-- <source> has no srcset -->
+
+<div><picture><source><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source src='data:,b'><img src='data:,a' data-expect='data:,a'></picture></div>
+
+<!-- <source srcset> has zero candidates -->
+
+<div><picture><source srcset><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset=', ,'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b 1x 1x'><img src='data:,a' data-expect='data:,a'></picture></div>
+
+<!-- <source media> -->
+
+<div><picture><source srcset='data:,b' media><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' media='all'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' media='all and (min-width:0)'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' media='all and !'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' media='all and (!)'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' media='not all'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' media='not all and (min-width:0)'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' media='not all and (max-width:0)'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' media='not all and !'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' media='not all and (!)'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' media='all, !'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' media=','><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' media=', all'><img src='data:,a' data-expect='data:,b'></picture></div>
+
+<!-- <source type> assume support for gif, png, jpg, svg, ico -->
+
+<div><picture><source srcset='data:,b' type><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type=' '><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type=' image/gif'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif '><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif;'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif;encodings'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif;encodings='><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif;encodings=foobar'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/png'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/jpeg'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/svg+xml'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='image/x-icon'><img src='data:,a' data-expect='data:,b'></picture></div>
+<div><picture><source srcset='data:,b' type='text/xml'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='text/html'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='text/plain'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='text/css'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='video/mp4'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='video/ogg'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='video/webm'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='unknown/unknown'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='application/octet-stream'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='application/x-shockwave-flash'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='image\gif'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='gif'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='.gif'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='*'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='*/*'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='image/*'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type=','><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif, image/png'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='image/gif image/png'><img src='data:,a' data-expect='data:,a'></picture></div>
+<div><picture><source srcset='data:,b' type='image/foobarbaz'><img src='data:,a' data-expect='data:,a'></picture></div>
+
+<!-- trailing garbage -->
+
+<div><picture><img src='data:,a' data-expect='data:,a'>foo</picture></div>
+<div><picture><img src='data:,a' data-expect='data:,a'><br></picture></div>
+<div><picture><img src='data:,a' data-expect='data:,a'><!--foo--></picture></div>
+<div><picture><img src='data:,a' data-expect='data:,a'><img src='data:,b'></picture></div>
+<div><picture><img data-expect=''><img src='data:,b'></picture></div>
+<div><picture><img src='data:,a' data-expect='data:,a'><source srcset='data:,b'></picture></div>
+<div><picture><img data-expect=''><source srcset='data:,b'></picture></div>
+
+<!-- parent not picture -->
+
+<div><picture><span><source srcset='data:,b'><img data-expect=''></span></picture></div>
+<div><picture><span><source srcset='data:,b'><img src='data:,a' data-expect='data:,a'></span></picture></div>
+<div><picture><source srcset='data:,b'><span><img src='data:,a' data-expect='data:,a'></span></picture></div>
+
+<!-- no src -->
+
+<div><picture><source srcset='data:,b'><img data-expect='data:,b'></picture></div>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/usemap-casing.html b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/usemap-casing.html
new file mode 100644
index 0000000000..c28f667ff3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-img-element/usemap-casing.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>img usemap case-sensitive</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-a-hash-name-reference">
+<!-- See also: https://github.com/whatwg/html/issues/1666 -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<img src="/images/threecolors.png" usemap="#sanityCheck" width="100" height="100">
+<map name="sanityCheck"><area shape="rect" coords="0,0,100,100"></map>
+
+<img src="/images/threecolors.png" usemap="#sImPlE" width="100" height="100">
+<map name="simple"><area shape="rect" coords="0,0,100,100"></map>
+<map name="SIMPLE"><area shape="rect" coords="0,0,100,100"></map>
+
+<img src="/images/threecolors.png" usemap="#paSSfield-killroyß" width="100" height="100">
+<map name="passfield-killroyß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="PASSFIELD-KILLROYß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="paſſfield-killroyß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="passfield-&#x212a;illroyß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="paßfield-killroyß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="paẞfield-killroyß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="passfield-killroyẞ"><area shape="rect" coords="0,0,100,100"></map>
+<map name="passï¬eld-killroyß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="passfıeld-killroyß"><area shape="rect" coords="0,0,100,100"></map>
+<map name="passfİeld-killroyß"><area shape="rect" coords="0,0,100,100"></map>
+
+<img src="/images/threecolors.png" usemap="#глупый" width="100" height="100">
+<map name="глупы&#x438;&#x306;"><area shape="rect" coords="0,0,100,100"></map>
+<map name="ГЛУПЫЙ"><area shape="rect" coords="0,0,100,100"></map>
+<map name="ГЛУПЫ&#x418;&#x306;"><area shape="rect" coords="0,0,100,100"></map>
+
+<img src="/images/threecolors.png" usemap="#åωk" width="100" height="100">
+<map name="ÅΩK"><area shape="rect" coords="0,0,100,100"></map>
+<map name="&#x212b;ωk"><area shape="rect" coords="0,0,100,100"></map>
+<map name="Ã¥&#x2126;k"><area shape="rect" coords="0,0,100,100"></map>
+<map name="åω&#x212a;"><area shape="rect" coords="0,0,100,100"></map>
+
+<img src="/images/threecolors.png" usemap="#blah1" width="100" height="100">
+<map name="blah&#x2460;"><area shape="rect" coords="0,0,100,100"></map>
+<map name="bl&#x24b6;h1"><area shape="rect" coords="0,0,100,100"></map>
+<map name="bl&#x24d0;h1"><area shape="rect" coords="0,0,100,100"></map>
+
+<img src="/images/threecolors.png" usemap="#t&Eacute;dz5アパートFi" width="100" height="100">
+<map name="T&Eacute;DZ5アパートFi"><area shape="rect" coords="0,0,100,100"></map>
+<map name="T&eacute;&#x01F1;&#x2075;アパートFi"><area shape="rect" coords="0,0,100,100"></map>
+<map name="t&Eacute;dz5&#x3100;Fi"><area shape="rect" coords="0,0,100,100"></map>
+<map name="t&Eacute;dz5&#x30A2;&#x30CF;&#x309A;&#x30FC;&#x30C8;Fi"><area shape="rect" coords="0,0,100,100"></map>
+<map name="T&Eacute;DZâµã‚¢ãƒ‘ートFi"><area shape="rect" coords="0,0,100,100"></map>
+<map name="T&Eacute;DZ5アパートï¬"><area shape="rect" coords="0,0,100,100"></map>
+
+<img src="/images/threecolors.png" usemap="#ΣΣ" width="100" height="100">
+<map name="σς"><area shape="rect" coords="0,0,100,100"></map>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+onload = () => {
+ test(() => {
+ const image = document.querySelector(`img[usemap="#sanityCheck"]`);
+ const imageRect = image.getBoundingClientRect();
+ const x = imageRect.left + imageRect.width / 2;
+ const y = imageRect.top + imageRect.height / 2;
+ const element = document.elementFromPoint(x, y);
+ const area = document.querySelector(`map[name="sanityCheck"] > area`);
+
+ assert_equals(element, area);
+ }, `Image with usemap of #sanityCheck should match the area with map named sanityCheck`);
+
+ const images = Array.from(document.querySelectorAll(`img:not([usemap="#sanityCheck"])`));
+
+ for (let image of images) {
+ test(() => {
+ const imageRect = image.getBoundingClientRect();
+ const x = imageRect.left + imageRect.width / 2;
+ const y = imageRect.top + imageRect.height / 2;
+ const element = document.elementFromPoint(x, y);
+
+ const name = element.parentElement.getAttribute("name");
+ const messageSuffix = name ? `; used <map> with name "${name}"` : "";
+
+ assert_equals(element, image, "The element retrieved must be the image, not an area" + messageSuffix);
+ }, `Image with usemap of ${image.useMap} should not match any of the areas`);
+ }
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/block-object-with-ruby-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/block-object-with-ruby-crash.html
new file mode 100644
index 0000000000..481a7408e4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/block-object-with-ruby-crash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=966363">
+<object style="display:block;">
+ <ruby></ruby>
+</object>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {}, "no crash");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/document-getters-return-null-for-cross-origin.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/document-getters-return-null-for-cross-origin.html
new file mode 100644
index 0000000000..3d1077538e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/document-getters-return-null-for-cross-origin.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that contentDocument/getSVGDocument() return null for a cross-origin document.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<object data='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect height="100" width="100"/></svg>'></object>
+<script>
+const object = document.querySelector('object');
+var t1 = async_test('HTMLObjectElement.contentDocument for cross-origin document');
+window.addEventListener(
+ 'load', t1.step_func_done(() => { assert_equals(object.contentDocument, null); }));
+var t2 = async_test('HTMLObjectElement.getSVGDocument() for cross-origin document');
+window.addEventListener(
+ 'load', t2.step_func_done(() => { assert_equals(object.getSVGDocument(), null); }));
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/historical.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/historical.html
new file mode 100644
index 0000000000..c7a577a9d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/historical.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>Historical object element features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<object id=object></object>
+<script>
+test(function() {
+ var elm = document.getElementById('object');
+ assert_equals(typeof elm, 'object', 'typeof');
+ assert_throws_js(TypeError, function() {
+ elm();
+ });
+}, 'object legacycaller should not be supported');
+
+test(() => {
+ const obj = document.createElement("object");
+ assert_false("typeMustMatch" in obj);
+}, "object's typeMustMatch IDL attribute should not be supported");
+
+async_test(t => {
+ const obj = document.createElement("object");
+ t.add_cleanup(() => obj.remove());
+ obj.setAttribute("data", "/common/blank.html");
+ obj.setAttribute("type", "text/plain");
+ obj.setAttribute("typemustmatch", "");
+ obj.onload = t.step_func_done(() => {
+ assert_not_equals(obj.contentDocument, null, "/common/blank.html should be loaded");
+ });
+ obj.onerror = t.unreached_func();
+ document.body.appendChild(obj);
+}, "object's typemustmatch content attribute should not be supported");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-attributes.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-attributes.html
new file mode 100644
index 0000000000..c630d8055c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-attributes.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: object - attributes</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body onload="on_load()">
+<div id="log"></div>
+<form>
+ <object id="obj1" data="/common/blank.html" name="o" height="50" width="100"></object>
+ <object id="obj2" name="p" type="image/png"></object>
+ <object id="obj3" data="missing.html" name="o3" height="50" width="100"></object>
+</form>
+<script>
+ var obj1;
+ var obj2;
+ var obj3;
+ var t1 = async_test("object.contentWindow");
+ var t2 = async_test("object.contentWindow.name");
+ var t3 = async_test("object.width");
+ var t4 = async_test("object.height");
+
+ setup(function() {
+ obj1 = document.getElementById("obj1");
+ obj2 = document.getElementById("obj2");
+ obj3 = document.getElementById("obj3");
+ });
+
+ function on_load () {
+ t1.step(function() {
+ assert_not_equals(obj1.contentWindow, null, "The contentWindow of the object element should not be null.");
+ assert_equals(obj2.contentWindow, null, "The contentWindow of the object element should be null when it type attribute starts with 'image/'.");
+ assert_equals(obj3.contentWindow, null, "The contentWindow of the object element should be null as it is showing fallback content.");
+ });
+ t1.done()
+
+ t2.step(function() {
+ assert_equals(obj1.contentWindow.name, "o", "The contentWindow's name of the object element should be 'o'.");
+ obj1.setAttribute("name", "o1");
+ assert_equals(obj1.name, "o1", "The name of the object element should be 'o1'.");
+ assert_equals(obj1.contentWindow.name, "o", "The contentWindow's name of the object element should still be 'o'.");
+ obj1.removeAttribute("name");
+ assert_equals(obj1.name, "", "The name of the object element should be empty string.");
+ assert_equals(obj1.contentWindow.name, "o", "The contentWindow's name of the object element should still be 'o'.");
+ });
+ t2.done()
+
+ t3.step(function() {
+ assert_equals(getComputedStyle(obj1, null)["width"], "100px", "The width should be 100px.");
+ });
+ t3.done();
+
+ t4.step(function() {
+ assert_equals(getComputedStyle(obj1, null)["height"], "50px", "The height should be 50px.");
+ });
+ t4.done();
+ }
+</script>
+
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-construct-in-document-with-null-browsing-context-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-construct-in-document-with-null-browsing-context-crash.html
new file mode 100644
index 0000000000..7248368656
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-construct-in-document-with-null-browsing-context-crash.html
@@ -0,0 +1,11 @@
+<title>HTMLObjectElement: construct in a document with a null browsing context</title>
+<link rel="author" title="Nate Chapin" href="mailto:japhet@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-object-element">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1083437">
+<meta name="assert" content="Constructing an HTMLObjectElement in a document with a null browsing context should not crash"/>
+<iframe id="i"></iframe>
+<script>
+var doc = i.contentDocument;
+i.remove();
+doc.createElement('object');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-events.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-events.html
new file mode 100644
index 0000000000..38f92c3d35
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-events.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: object-events</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+async_test(function(t) {
+ var obj = document.createElement("object");
+ obj.onerror = t.step_func_done(function(e){
+ assert_equals(Object.getPrototypeOf(e).constructor, Event, "The error event should use the Event interface.");
+ assert_true(e.isTrusted, "The error event should be a trusted event.");
+ assert_false(e.cancelable, "The error event should not be a cancelable event.");
+ assert_false(e.bubbles, "The error event should not be a bubble event.");
+ assert_equals(e.target, obj, "The error event target should be the corresponding object element.");
+ });
+
+ obj.onload = t.step_func_done(function(e){
+ assert_unreached("The load event should not be fired.");
+ });
+
+ obj.data = "file:\\http://nonexistent.html";
+ document.body.appendChild(obj);
+}, "error event (using 'file:' protocol)");
+
+async_test(function(t) {
+ var obj = document.createElement("object");
+ obj.onerror = t.step_func_done(function(e){
+ assert_equals(e.target, obj,
+ "The error event should be fired on our element");
+ });
+ obj.onload = t.step_func_done(function(e){
+ assert_unreached("The load event should not be fired.");
+ });
+
+ obj.data = "http://test:test";
+ document.body.appendChild(obj);
+}, "error event (using 'http:' protocol)");
+
+
+async_test(function(t) {
+ var obj = document.createElement("object");
+ obj.onload = t.step_func_done(function(e){
+ assert_equals(Object.getPrototypeOf(e).constructor, Event, "The load event should use the Event interface.");
+ assert_true(e.isTrusted, "The load event should be a trusted event.");
+ assert_false(e.cancelable, "The load event should not be a cancelable event.");
+ assert_false(e.bubbles, "The load event should not be a bubble event.");
+ assert_equals(e.target, obj, "The load event target should be the corresponding object element.");
+ });
+
+ obj.onerror = t.step_func_done(function(e){
+ assert_unreached("The error event should not be fired.");
+ });
+
+ obj.data = "/images/blue.png";
+ document.body.appendChild(obj);
+}, "load event");
+
+async_test(function(t) {
+ var obj = document.createElement("object");
+ obj.onload = t.step_func_done(function(e){
+ assert_true(obj.contentWindow instanceof obj.contentWindow.Window, "The object element should represent a nested browsing context.")
+ assert_equals(Object.getPrototypeOf(e).constructor, Event, "The load event should use the Event interface.");
+ assert_true(e.isTrusted, "The load event should be a trusted event.");
+ assert_false(e.cancelable, "The load event should not be a cancelable event.");
+ assert_false(e.bubbles, "The load event should not be a bubble event.");
+ assert_equals(e.target, obj, "The load event target should be the corresponding object element.");
+ });
+
+ obj.onerror = t.step_func_done(function(e){
+ assert_unreached("The error event should not be fired.");
+ });
+
+ obj.data = "about:blank";
+ document.body.appendChild(obj);
+}, "load event of about:blank");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-fallback-failed-cross-origin-navigation.sub.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-fallback-failed-cross-origin-navigation.sub.html
new file mode 100644
index 0000000000..09061e0349
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-fallback-failed-cross-origin-navigation.sub.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that &lt;object&gt; renders its own fallback.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+ const URIS = [
+ // The host exists but the resource is unavailable.
+ "http://{{hosts[alt][www]}}:{{ports[http][0]}}/foo.html",
+ // The destination does not even exist and the navigation fails.
+ "http://{{hosts[alt][nonexistent]}}:{{ports[http][0]}}/foo.html",
+ ];
+
+ // Create an <object> with some fallback content.
+ function create_object_with_fallback(url, t) {
+ var object = document.createElement("object");
+ var fallback = document.createElement("button");
+ fallback.textContent = "FALLBACK CONTENT";
+ object.appendChild(fallback);
+ object.data = url;
+ object.type = "text/html";
+ let promise = new Promise(resolve => {
+ object.addEventListener("load", t.unreached_func("Should never reach the load event"), {once: true});
+ object.addEventListener("error", () => resolve(object), {once: true});
+ });
+ document.body.appendChild(object);
+ t.add_cleanup(() => object.remove());
+ return promise;
+ }
+
+ function area(el) {
+ let bounds = el.getBoundingClientRect();
+ return bounds.width * bounds.height;
+ }
+
+ for (let uri of URIS) {
+ promise_test(async(t) => {
+ let object = await create_object_with_fallback(uri, t);
+
+ // XXX In Chrome this is needed, fallback doesn't seem to be ready after
+ // the error event, which seems weird/odd.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ assert_true(area(object.firstChild) > 0, "Should be showing fallback");
+
+ // Per https://html.spec.whatwg.org/#the-object-element:
+ //
+ // The object element can represent an external resource, which,
+ // depending on the type of the resource, will either be treated as
+ // image, as a child browsing context, or as an external resource to
+ // be processed by a plugin.
+ //
+ // [...]
+ //
+ // If the load failed (e.g. there was an HTTP 404 error, there was a
+ // DNS error), fire an event named error at the element, then jump to
+ // the step below labeled fallback.
+ //
+ // (And that happens before "Determine the resource type" which is what
+ // sets the nested browsing context).
+ //
+ // So the expected window.length is 0.
+ assert_equals(window.length, 0);
+ }, `Verify fallback content for failed cross-origin navigations is shown correctly: ${uri}`);
+ }
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-handler.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-handler.html
new file mode 100644
index 0000000000..a24554e0cc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-handler.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: object - handler</title>
+<link rel="author" title="Intel" href="http://www.intel.com" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<object id="test" name="obj" data="test0.html" type="text/html"></object>
+<script>
+
+var t1 = async_test("The nested browsing context must be navigated to the resource specified by the data attribute.");
+var t2 = async_test("The object.data must not be updated if the browsing context gets further navigated.");
+
+function callback(data) {
+ if (data == "test0") {
+ t1.step(function() {
+ var testEle = document.getElementById("test");
+ assert_true(testEle.contentDocument.location.href.indexOf("test0.html") != -1, "The nested browsing context should be navigated to test0.html.");
+ window["obj"].history.replaceState({state:"ok"}, "mytitle ", "object-fallback.html");
+ assert_not_equals(testEle.contentDocument.location.href.indexOf("object-fallback.html"), -1, "The nested browsing context should be replacement enabled.");
+ });
+ t1.done();
+ } else if (data == "test1") {
+ t2.step(function() {
+ var testEle = document.getElementById("test");
+ assert_true(testEle.contentDocument.location.href.indexOf("test1.html") != -1, "The browsing context should be navigated to test1.html.");
+ assert_true(testEle.data.indexOf("test0.html") != -1, "The value of attribute data should not be updated.");
+ });
+ t2.done();
+ }
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-ignored-in-media-element.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-ignored-in-media-element.html
new file mode 100644
index 0000000000..2bf84c2946
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-ignored-in-media-element.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTML Test: The embed element represents a document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="Check if the object element is ignored when used inside a media element">
+<script type="application/javascript">
+ var nestingTest = async_test("Test <object> being ignored inside media element");
+ onload = nestingTest.step_func_done(function() {
+ assert_true(true, "We got to a load event without loading things we should not load");
+ });
+</script>
+<body>
+ <video>
+ <object type="text/html" data="../resources/should-not-load.html"
+ test-description="<object> in <video>"></object>
+ </video>
+ <audio>
+ <object type="text/html" data="../resources/should-not-load.html"
+ test-description="<object> in <audio>"></object>
+ </audio>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-display-none-load-event.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-display-none-load-event.html
new file mode 100644
index 0000000000..c8369365af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-display-none-load-event.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html style="display:none">
+<meta charset=utf-8>
+<title>Test that an object in a display:none subtree does not block the load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ async_test(t => {
+ window.onload = t.step_func_done();
+ document.documentElement.offsetTop;
+ }, "Load event triggered on window");
+</script>
+<object data="data:text/html,"></object>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-object-fallback-2.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-object-fallback-2.html
new file mode 100644
index 0000000000..47cf801693
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-in-object-fallback-2.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title></title>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <script>
+ var loadedCount = 0;
+ var nestingTest = async_test("Test <object> nesting inside <object>");
+ onload = nestingTest.step_func_done(function() {
+ assert_equals(loadedCount, 12, "Should have loaded all should-load elements");
+ });
+ </script>
+ <style>
+ object { display: none }
+ </style>
+ </head>
+ <body>
+ <object data="../resources/should-load.html" style="width: 100px; height: 100px">
+ <object type="text/html" data="../resources/should-not-load.html"
+ test-description="<object> inside <object>"></object>
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <object type="text/html" data="../resources/should-load.html"></object>
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <div></div>
+ <object type="text/html" data="../resources/should-load.html"></object>
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <div>
+ <object type="text/html" data="../resources/should-load.html"></object>
+ </div>
+ </object>
+ <object style="width: 100px; height: 100px" data="data:application/x-does-not-exist,test">
+ <object type="text/html" data="../resources/should-load.html"></object>
+ <object type="text/html" data="../resources/should-load.html"></object>
+ <object data="../resources/should-load.html">
+ <object type="text/html" data="../resources/should-not-load.html"
+ test-description="<object> inside loaded <object> inside non-loaded <object>"></object>
+ </object>
+ <object data="data:application/x-does-not-exist,test">
+ <object type="text/html" data="../resources/should-load.html"></object>
+ </object>
+ </object>
+ <div>
+ <object data="../resources/should-load.html" style="width: 100px; height: 100px"></object>
+ <object type="text/html" data="../resources/should-load.html"></object>
+ </div>
+ <div>
+ <object type="text/html" data="../resources/should-load.html"></object>
+ <object data="../resources/should-load.html" style="width: 100px; height: 100px"></object>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url-ref.html
new file mode 100644
index 0000000000..7eb9256b0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url-ref.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>object element containing param element specifying a URL</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+<style>
+ div {
+ width:300px;
+ height:80px;
+ border:1px solid black;
+ margin: 5px;
+ overflow: hidden;
+ }
+</style>
+<body>
+<script>
+const smallPdf = 'JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyA5IFRmKFRlc3QpJyBFVAplbmRzdHJlYW0KZW5kb2JqCjQgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCA1IDAgUgovQ29udGVudHMgOSAwIFIKPj4KZW5kb2JqCjUgMCBvYmoKPDwKL0tpZHMgWzQgMCBSIF0KL0NvdW50IDEKL1R5cGUgL1BhZ2VzCi9NZWRpYUJveCBbIDAgMCA5OSA5IF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiUlRU9G';
+const dataUrl = `data:application/pdf;base64,${smallPdf}`;
+
+function addOne(html) {
+ const wrapper = document.createElement('div');
+ wrapper.innerHTML = html;
+ const objectElement = wrapper.querySelector('object');
+ document.body.appendChild(wrapper);
+}
+
+// This should be one <object> that loads a PDF, and the rest that don't.
+addOne(`<object data=${dataUrl}></object>`);
+addOne(`<object></object>`);
+addOne(`<object></object>`);
+addOne(`<object></object>`);
+addOne(`<object></object>`);
+addOne(`<object></object>`);
+
+// Not a great way to tell when any <object> that might load has loaded.
+setTimeout(() => document.documentElement.classList.remove("reftest-wait"),2000);
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url.html
new file mode 100644
index 0000000000..5f1e54c4d9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-param-url.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>object element containing param element specifying a URL</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://github.com/whatwg/html/pull/7816">
+<link rel=match href="object-param-url-ref.html">
+
+<style>
+ div {
+ width:300px;
+ height:80px;
+ border:1px solid black;
+ margin: 5px;
+ overflow: hidden;
+ }
+</style>
+<body>
+<script>
+const smallPdf = 'JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyA5IFRmKFRlc3QpJyBFVAplbmRzdHJlYW0KZW5kb2JqCjQgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCA1IDAgUgovQ29udGVudHMgOSAwIFIKPj4KZW5kb2JqCjUgMCBvYmoKPDwKL0tpZHMgWzQgMCBSIF0KL0NvdW50IDEKL1R5cGUgL1BhZ2VzCi9NZWRpYUJveCBbIDAgMCA5OSA5IF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiUlRU9G';
+const dataUrl = `data:application/pdf;base64,${smallPdf}`;
+
+function addOne(html) {
+ const wrapper = document.createElement('div');
+ wrapper.innerHTML = html;
+ const objectElement = wrapper.querySelector('object');
+ document.body.appendChild(wrapper);
+}
+
+// This should be one <object> that loads a PDF, and the rest that don't.
+addOne(`<object data=${dataUrl}></object>`);
+addOne(`<object><param name=src value=${dataUrl}></object>`);
+addOne(`<object><param name=data value=${dataUrl}></object>`);
+addOne(`<object><param name=code value=${dataUrl}></object>`);
+addOne(`<object><param name=movie value=${dataUrl}></object>`);
+addOne(`<object><param name=url value=${dataUrl}></object>`);
+
+// Not a great way to tell when any <object> that might load has loaded.
+setTimeout(() => document.documentElement.classList.remove("reftest-wait"),2000);
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-remove-param-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-remove-param-crash.html
new file mode 100644
index 0000000000..e1e2ebb4e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-remove-param-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>HTML Test: object - crash removing a param after changing its style</title>
+<link rel="help" href="https://crbug.com/1195633">
+<object type="text/html">
+ <param id="param"></param>
+</object>
+<script>
+ getComputedStyle(param).color;
+ param.style.color = "red";
+ param.remove();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-setcustomvalidity.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-setcustomvalidity.html
new file mode 100644
index 0000000000..44574ffd11
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/object-setcustomvalidity.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<title>object setCustomValidity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<object id='object_test'></object>
+
+<script>
+
+test(() => {
+ let elem = document.getElementById("object_test");
+ assert_false(elem.validity.customError);
+ elem.setCustomValidity("custom error");
+ assert_true(elem.validity.customError);
+}, "object setCustomValidity is correct")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test0.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test0.html
new file mode 100644
index 0000000000..17df71daa2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test0.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script>
+
+parent.callback("test0");
+document.location.href = "test1.html";
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test1.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test1.html
new file mode 100644
index 0000000000..cf2423275e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/test1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script>
+
+parent.callback("test1");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html
new file mode 100644
index 0000000000..114a472fb6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>object usemap case-sensitive</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-a-hash-name-reference">
+<!-- See also: https://github.com/whatwg/html/issues/1666 -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<object data="/images/threecolors.png" usemap="#sanityCheck" width="300" height="300"></object>
+<map name="sanityCheck"><area shape="rect" coords="0,0,300,300"></map>
+
+<object data="/images/threecolors.png" usemap="#sImPlE" width="300" height="300"></object>
+<map name="simple"><area shape="rect" coords="0,0,300,300"></map>
+<map name="SIMPLE"><area shape="rect" coords="0,0,300,300"></map>
+
+<object data="/images/threecolors.png" usemap="#paSSfield-killroyß" width="300" height="300"></object>
+<map name="passfield-killroyß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="PASSFIELD-KILLROYß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="paſſfield-killroyß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="passfield-&#x212a;illroyß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="paßfield-killroyß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="paẞfield-killroyß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="passfield-killroyẞ"><area shape="rect" coords="0,0,300,300"></map>
+<map name="passï¬eld-killroyß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="passfıeld-killroyß"><area shape="rect" coords="0,0,300,300"></map>
+<map name="passfİeld-killroyß"><area shape="rect" coords="0,0,300,300"></map>
+
+<object data="/images/threecolors.png" usemap="#глупый" width="300" height="300"></object>
+<map name="глупы&#x438;&#x306;"><area shape="rect" coords="0,0,300,300"></map>
+<map name="ГЛУПЫЙ"><area shape="rect" coords="0,0,300,300"></map>
+<map name="ГЛУПЫ&#x418;&#x306;"><area shape="rect" coords="0,0,300,300"></map>
+
+<object data="/images/threecolors.png" usemap="#åωk" width="300" height="300"></object>
+<map name="ÅΩK"><area shape="rect" coords="0,0,300,300"></map>
+<map name="&#x212b;ωk"><area shape="rect" coords="0,0,300,300"></map>
+<map name="Ã¥&#x2126;k"><area shape="rect" coords="0,0,300,300"></map>
+<map name="åω&#x212a;"><area shape="rect" coords="0,0,300,300"></map>
+
+<object data="/images/threecolors.png" usemap="#blah1" width="300" height="300"></object>
+<map name="blah&#x2460;"><area shape="rect" coords="0,0,300,300"></map>
+<map name="bl&#x24b6;h1"><area shape="rect" coords="0,0,300,300"></map>
+<map name="bl&#x24d0;h1"><area shape="rect" coords="0,0,300,300"></map>
+
+<object data="/images/threecolors.png" usemap="#t&Eacute;dz5アパートFi" width="300" height="300"></object>
+<map name="T&Eacute;DZ5アパートFi"><area shape="rect" coords="0,0,300,300"></map>
+<map name="T&eacute;&#x01F1;&#x2075;アパートFi"><area shape="rect" coords="0,0,300,300"></map>
+<map name="t&Eacute;dz5&#x3300;Fi"><area shape="rect" coords="0,0,300,300"></map>
+<map name="t&Eacute;dz5&#x30A2;&#x30CF;&#x309A;&#x30FC;&#x30C8;Fi"><area shape="rect" coords="0,0,300,300"></map>
+<map name="T&Eacute;DZâµã‚¢ãƒ‘ートFi"><area shape="rect" coords="0,0,300,300"></map>
+<map name="T&Eacute;DZ5アパートï¬"><area shape="rect" coords="0,0,300,300"></map>
+
+<object data="/images/threecolors.png" usemap="#ΣΣ" width="300" height="300"></object>
+<map name="σς"><area shape="rect" coords="0,0,300,300"></map>
+
+<script>
+"use strict";
+setup({ explicit_done: true });
+
+onload = () => {
+ const objects = Array.from(document.querySelectorAll(`object`));
+
+ for (let object of objects) {
+ test(() => {
+ const objectRect = object.getBoundingClientRect();
+ const x = objectRect.left + objectRect.width / 2;
+ const y = objectRect.top + objectRect.height / 2;
+ const element = document.elementFromPoint(x, y);
+
+ const name = element.parentElement.getAttribute("name");
+ const messageSuffix = name ? `; used <map> with name "${name}"` : "";
+
+ assert_equals(element, object, "The element retrieved must be the object, not an area" + messageSuffix);
+ }, `Object with usemap of ${object.useMap} should not match any of the areas (it does not support usemap)`);
+ }
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm
new file mode 100644
index 0000000000..f9426a529c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+<head>
+<title>video element - intrinsic sizes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+</head>
+<body>
+<p><a href="https://html.spec.whatwg.org/multipage/#the-video-element">spec reference</a></p>
+<video id="v1"></video>
+<video id="v2" width="400"></video>
+<video id="v3" height="100"></video>
+<video id="v4"></video>
+<video id="v5" poster="/media/poster.png"></video>
+<div id="log"></div>
+<script>
+test(function() {
+ var s = getComputedStyle(document.getElementById("v1"));
+ assert_equals(s.width, "300px");
+ assert_equals(s.height, "150px");
+}, "default object size is 300x150");
+
+test(function() {
+ var s = getComputedStyle(document.getElementById("v2"));
+ assert_equals(s.width, "400px");
+ assert_equals(s.height, "200px");
+}, "default height is half the width");
+
+test(function() {
+ var s = getComputedStyle(document.getElementById("v3"));
+ assert_equals(s.width, "200px");
+ assert_equals(s.height, "100px");
+}, "default width is twice the height");
+
+async_test(function(t) {
+ var v = document.getElementById("v4");
+ var s = getComputedStyle(v);
+ v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+ v.onerror = t.unreached_func();
+ v.onloadedmetadata = t.step_func(function() {
+ assert_equals(s.width, '320px');
+ assert_equals(s.height, '240px');
+ v.removeAttribute("src");
+ v.load();
+ // Dimensions should be updated only on next layout.
+ requestAnimationFrame(t.step_func_done(function() {
+ assert_equals(s.width, "300px");
+ assert_equals(s.height, "150px");
+ }));
+ });
+}, "default object size after src is removed");
+
+async_test(function(t) {
+ var v = document.getElementById("v5");
+ var s = getComputedStyle(v);
+ v.onerror = t.unreached_func();
+ onload = t.step_func(function() {
+ assert_equals(s.width, '102px');
+ assert_equals(s.height, '77px');
+ v.removeAttribute("poster");
+ // Dimensions should be updated only on next layout.
+ requestAnimationFrame(t.step_func_done(function() {
+ assert_equals(s.width, "300px");
+ assert_equals(s.height, "150px");
+ }));
+ });
+}, "default object size after poster is removed");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/resize-during-playback.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/resize-during-playback.html
new file mode 100644
index 0000000000..e1f35768bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/resize-during-playback.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+<head>
+<title>video element resizing during playback</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/media.html#concept-video-intrinsic-width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+for (const format of ['mp4', 'webm']) {
+ promise_test(async (t) => {
+ const video = document.createElement('video');
+ assert_implements_optional(video.canPlayType(`video/${format}`), `${format} supported`);
+
+ const eventWatcher = new EventWatcher(t, video, ['resize', 'playing', 'error', 'ended']);
+
+ // Load the video and wait for initial resize event.
+ video.muted = true;
+ video.preload = 'auto';
+ video.onerror = t.unreached_func("error during playback");
+ video.src = `/media/400x300-red-resize-200x150-green.${format}`;
+ document.body.appendChild(video);
+
+ await eventWatcher.wait_for(['resize']);
+ assert_equals(video.videoWidth, 400, 'width after first resize event');
+ assert_equals(video.videoHeight, 300, 'height after first resize event');
+
+ // Now play and wait for a second resize event.
+ const playPromise = video.play();
+ if (playPromise) {
+ playPromise.catch(t.unreached_func("play rejected"));
+ }
+ await eventWatcher.wait_for(['playing', 'resize']);
+ assert_equals(video.videoWidth, 200, 'width after second resize event');
+ assert_equals(video.videoHeight, 150, 'height after second resize event');
+ }, `${format} video`);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-import-to-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-import-to-inactive-document-crash.html
new file mode 100644
index 0000000000..1e14388efd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-import-to-inactive-document-crash.html
@@ -0,0 +1,7 @@
+<iframe id="i"></iframe>
+<video id="v"></video>
+<script>
+var doc = i.contentDocument;
+i.remove();
+doc.importNode(v);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto-ref.html
new file mode 100644
index 0000000000..66b42e4025
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+ <title>Verifies that video poster is shown even if video element has 'preload="auto"' attribute</title>
+ <video preload="none" poster="/media/poster.png" src="/media/video.webm" width="100" height="100"></video>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto.html
new file mode 100644
index 0000000000..95e32bb77c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-poster-shown-preload-auto.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <title>Verifies that video poster is shown even if video element has 'preload="auto"' attribute</title>
+ <link rel="match" href="video-poster-shown-preload-auto-ref.html">
+ <video preload="auto" poster="/media/poster.png" src="/media/video.webm" width="100" height="100"></video>
+ <script>
+ const video = document.querySelector("video");
+ video.oncanplaythrough = () => document.documentElement.classList.remove("reftest-wait");
+ </script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-tabindex.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-tabindex.html
new file mode 100644
index 0000000000..3044874789
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video-tabindex.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>tabindex on video elements</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#video">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<video></video>
+</div>
+<script>
+var t = async_test("Attributes shouldn't magically appear");
+on_event(window, "load", t.step_func(function() {
+ var el = document.getElementById("test").getElementsByTagName("video")[0];
+ assert_equals(el.hasAttribute("tabindex"), false);
+ t.done()
+}))
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content-ref.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content-ref.htm
new file mode 100644
index 0000000000..c02abb1236
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content-ref.htm
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>HTML5 Media Elements: Content inside the 'video' element is not shown to the user.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ </head>
+ <body>
+ <div id='testcontent'>
+ </div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_image.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_image.htm
new file mode 100644
index 0000000000..0808d894aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_image.htm
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>HTML5 Media Elements: Content inside the 'video' element is not shown to the user.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#video" />
+ <link rel="match" href="video_content-ref.htm" />
+ <meta name="assert" content="Content inside the 'video' element is not shown to the user (image)." />
+ </head>
+ <body>
+ <div id='testcontent'>
+ <video><img src="../../../../images/fail.gif" /></video>
+ </div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_text.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_text.htm
new file mode 100644
index 0000000000..639fb73f8f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_content_text.htm
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>HTML5 Media Elements: Content inside the 'video' element is not shown to the user.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#video" />
+ <link rel="match" href="video_content-ref.htm" />
+ <meta name="assert" content="Content inside the 'video' element is not shown to the user." />
+ </head>
+ <body>
+ <div id='testcontent'>
+ <video><p style="color: red;">FAIL</p></video>
+ </div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_crash_empty_src.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_crash_empty_src.html
new file mode 100644
index 0000000000..3aecb4e1eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_crash_empty_src.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Media Elements: An empty src should not crash the player.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Alicia Boya García" href="mailto:aboya@igalia.com"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+ function makeCrashTest(src) {
+ async_test((test) => {
+ const video = document.createElement("video");
+ video.src = src;
+ video.controls = true;
+ video.addEventListener("error", () => {
+ document.body.removeChild(video);
+ test.done();
+ });
+ document.body.appendChild(video);
+ }, `src="${src}" does not crash.`);
+ }
+
+ makeCrashTest("about:blank");
+ makeCrashTest("");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster-ref.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster-ref.htm
new file mode 100644
index 0000000000..78c03626e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster-ref.htm
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>Reference for poster tests</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<img src="/media/poster.png">
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_absolute.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_absolute.htm
new file mode 100644
index 0000000000..bec2b0fba7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_absolute.htm
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>The 'HTMLVideoElement' interface supports setting 'poster' to an absolute URL</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-video-poster">
+<link rel="match" href="video_dynamic_poster-ref.htm">
+<meta name="assert" content="The 'HTMLVideoElement' interface supports setting 'poster' to an absolute URL">
+<video id="video0">Your browser does not support video.</video>
+<script>
+var testElem = document.getElementById("video0");
+testElem.poster = "/media/poster.png";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_relative.htm b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_relative.htm
new file mode 100644
index 0000000000..4faca61c40
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_dynamic_poster_relative.htm
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>The 'HTMLVideoElement' interface supports setting 'poster' to a relative URL</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-video-poster">
+<link rel="match" href="video_dynamic_poster-ref.htm">
+<meta name="assert" content="The 'HTMLVideoElement' interface supports setting 'poster' to a relative URL">
+<video id="video0">Your browser does not support video.</video>
+<script>
+var testElem = document.getElementById("video0");
+testElem.poster = "../../../../media/poster.png";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused-ref.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused-ref.html
new file mode 100644
index 0000000000..8556aabf23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>Video elements should initially be paused</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-paused">
+<script src="/common/media.js"></script>
+<p>The following video element should be paused. (All clocks at zero).</p>
+<img src='/images/movie_300_frame_0.png'>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused.html
new file mode 100644
index 0000000000..b2725b04aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_initially_paused.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>Video elements should initially be paused</title>
+<link rel="match" href="video_initially_paused-ref.html">
+<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-media-paused">
+<script src="/common/media.js"></script>
+<style>
+div#video {
+ padding: 6px 3px;
+}
+</style>
+<p>The following video element should be paused. (All clocks at zero).</p>
+<div id=video>
+<script>
+document.write(
+ "<video src='"+ getVideoURI('/media/movie_300') + "' >" +
+ "Your browser does not support the video element." +
+ "<\/video>");
+</script>
+</div>
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_size_preserved_after_ended.html b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_size_preserved_after_ended.html
new file mode 100644
index 0000000000..9b2783c930
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/video_size_preserved_after_ended.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>HTML5 Media Elements: The size of the video shouldn't be lost after an 'ended' event.</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Alicia Boya García" href="mailto:aboya@igalia.com"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<video id="video">
+ <source src="/media/test-1s.mp4" type="video/mp4">
+ <source src="/media/test-1s.webm" type="video/webm">
+</video>
+<script>
+ promise_test(async (test) => {
+ const eventWatcher = new EventWatcher(test, video, ["loadedmetadata", "ended"]);
+ await eventWatcher.wait_for("loadedmetadata");
+ assert_equals(video.videoWidth, 320, "width when the video is loaded");
+ assert_equals(video.videoHeight, 240, "height when the video is loaded");
+ video.play();
+ await eventWatcher.wait_for(["ended"]);
+ assert_equals(video.videoWidth, 320, "width after playback");
+ assert_equals(video.videoHeight, 240, "height after playback");
+ if (video.videoTracks)
+ assert_equals(video.videoTracks.length, 1);
+ }, "Video dimensions are preserved at the end of the video.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/META.yml b/testing/web-platform/tests/html/semantics/forms/META.yml
new file mode 100644
index 0000000000..ce84e4ae4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - tkent-google
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-ltr.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-ltr.html
new file mode 100644
index 0000000000..7bb7aae3b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-ltr.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Submitting element directionality: the dirname attribute</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#submitting-element-directionality:-the-dirname-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/dirname.js"></script>
+<div id="log"></div>
+<form action="resources/dirname-iframe.html" method=get target="iframe">
+ <p><label>User: <input type=text name="user" dirname="user.dir" required></label></p>
+ <p><label>Comment: <textarea name="comment" dirname="comment.dir" required></textarea></label></p>
+ <p><button type=submit>Post Comment</button></p>
+</form>
+<iframe name="iframe"></iframe>
+<script>
+ document.querySelector("input").value = "foobar";
+ document.querySelector("textarea").value = "foobar";
+ document.querySelector("button").click();
+
+ var t_inp = async_test("submit input element directionality");
+ onIframeLoadedDone(t_inp, function(params) {
+ assert_equals(params.get("user.dir"), "ltr");
+ });
+
+ var t_ta = async_test("submit textarea element directionality");
+ onIframeLoadedDone(t_ta, function(params) {
+ assert_equals(params.get("comment.dir"), "ltr");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-only-if-applies.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-only-if-applies.html
new file mode 100644
index 0000000000..8af6826fa1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-only-if-applies.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title>Submitting element directionality: the dirname attribute</title>
+ <link rel="author" title="Vincent Hilla" href="mailto:vhilla@mozilla.com">
+ <link rel=help href="https://html.spec.whatwg.org/multipage/#submitting-element-directionality:-the-dirname-attribute">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="resources/dirname.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <form action="resources/dirname-iframe.html" method=get target="iframe">
+ <textarea name="textarea" dirname="textarea.dir"></textarea>
+ <p><input id="btn-submit" type=submit name=submit dirname=submit.dir value=Submit></p>
+ </form>
+ <iframe name="iframe"></iframe>
+
+ <script>
+ const types_applies = [
+ "hidden", "text", "search", "tel", "url", "email", "password", "submit"
+ ];
+ const types_not = [
+ "date", "month", "week", "time", "datetime-local", "number", "range", "color", "checkbox",
+ "radio", "file", "image", "reset", "button"
+ ];
+ const types = [...types_applies, ...types_not];
+ let form = document.querySelector("form");
+ for (const type of types) {
+ if (type === "submit") {
+ continue;
+ }
+ let p = document.createElement("p");
+ let lbl = document.createElement("label");
+ let txt = document.createTextNode(type + ": ");
+ let inp = document.createElement("input");
+ inp.type = type;
+ inp.name = type;
+ inp.dirName = type + ".dir";
+ inp.id = "testelement." + type
+ lbl.appendChild(txt);
+ lbl.appendChild(inp);
+ p.appendChild(lbl);
+ form.appendChild(p);
+ }
+ // Avoid continue in https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set:attr-fe-dirname
+ document.getElementById("testelement.checkbox").checked = true;
+ document.getElementById("testelement.radio").checked = true;
+
+ function assertInputSubmission(data) {
+ for (const type of types_applies) {
+ assert_equals(data.get(type + ".dir"), "ltr", "Submit ltr for input type=" + type);
+ }
+ for (const type of types_not) {
+ assert_false(data.has(type + ".dir"), "Do not submit dir for input type=" + type);
+ }
+ }
+
+ const submitter = document.getElementById("btn-submit");
+ const data = new FormData(form, submitter);
+ test(function() {
+ assertInputSubmission(data);
+ }, "Submit input element directionality to FormData, if dirname applies.");
+ test(function() {
+ assert_equals(data.get("textarea.dir"), "ltr", "Submit ltr for textarea");
+ }, "Submit textarea element directionality to FormData.");
+
+ submitter.click();
+ const t_inp = async_test("Submit input element directionality, if dirname applies.");
+ onIframeLoadedDone(t_inp, function(params) {
+ assertInputSubmission(params);
+ });
+ const t_ta = async_test("Submit textarea element directionality.");
+ onIframeLoadedDone(t_ta, function(params) {
+ assert_equals(params.get("textarea.dir"), "ltr", "Submit ltr for textarea");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-auto.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-auto.html
new file mode 100644
index 0000000000..02aeb79176
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-auto.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Submitting element directionality: the dirname attribute</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#submitting-element-directionality:-the-dirname-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/dirname.js"></script>
+<div id="log"></div>
+<form action="resources/dirname-iframe.html" method=get target="iframe">
+ <p><label>User: <input type=text name="user" dir="auto" dirname="user.dir" required/></label></p>
+ <p><label>Comment: <textarea name="comment" dir="auto" dirname="comment.dir" required></textarea></label></p>
+ <p><button type=submit>Post Comment</button></p>
+</form>
+<iframe name="iframe"></iframe>
+<script>
+ var rtlValue = "مرحبا";
+ document.querySelector("input").value = rtlValue;
+ document.querySelector("textarea").value = rtlValue;
+ document.querySelector("button").click();
+
+ var t_inp = async_test("submit input element directionality");
+ onIframeLoadedDone(t_inp, function(params) {
+ assert_equals(params.get("user.dir"), "rtl");
+ });
+
+ var t_ta = async_test("submit textarea element directionality");
+ onIframeLoadedDone(t_ta, function(params) {
+ assert_equals(params.get("comment.dir"), "rtl");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-inherited.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-inherited.html
new file mode 100644
index 0000000000..0c5331f0ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-inherited.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Submitting element directionality: the dirname attribute</title>
+<link rel="author" title="Kolupaev Dmitry" href="mailto:dmitry.klpv@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#submitting-element-directionality:-the-dirname-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/dirname.js"></script>
+<div id="log"></div>
+<div dir="rtl">
+ <form action="resources/dirname-iframe.html" method=get target="iframe">
+ <p><label>User: <input type=text name="user" dirname="user.dir" required/></label></p>
+ <p><label>Comment: <textarea name="comment" dirname="comment.dir" required></textarea></label></p>
+ <p><button type=submit>Post Comment</button></p>
+ </form>
+</div>
+<iframe name="iframe"></iframe>
+<script>
+ document.querySelector("input").value = "foobar";
+ document.querySelector("textarea").value = "foobar";
+ document.querySelector("button").click();
+
+ var t_inp = async_test("submit input element directionality");
+ onIframeLoadedDone(t_inp, function(params) {
+ assert_equals(params.get("user.dir"), "rtl");
+ });
+
+ var t_ta = async_test("submit textarea element directionality");
+ onIframeLoadedDone(t_ta, function(params) {
+ assert_equals(params.get("comment.dir"), "rtl");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-manual.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-manual.html
new file mode 100644
index 0000000000..c5f683101d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-manual.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Submitting element directionality: the dirname attribute (rtl)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#submitting-element-directionality:-the-dirname-attribute">
+<form action="dirname-rtl-manual.html" method=get>
+ <p><label>User: <input type=text name="user" dirname="user.dir" required></label></p>
+ <p><label>Comment: <textarea name="comment" dirname="comment.dir" required></textarea></label></p>
+ <p><button type=submit>Post Comment</button></p>
+</form>
+<p>Switch to a right-to-left writing direction, enter a text in the input and textarea, and submit the form.</p>
+<p>Test passes if the word "PASS" appears below</p>
+<script>
+ function getParameterByName(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+ results = regex.exec(location.search);
+ return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+ }
+
+ var userDir = getParameterByName("user.dir");
+ var commentDir = getParameterByName("comment.dir");
+ if (commentDir && userDir) {
+ var p = document.createElement("p");
+ var success = (commentDir == "rtl" && userDir == "rtl")
+ p.textContent = success ? "PASS" : "FAIL";
+ document.body.appendChild(p);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/disabled-elements-01.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/disabled-elements-01.html
new file mode 100644
index 0000000000..14443e4099
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/disabled-elements-01.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<title>HTMLFormElement: the disabled attribute</title>
+<link rel="author" title="Eric Casler" href="mailto:ericorange@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#enabling-and-disabling-form-controls:-the-disabled-attribute">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="log"></div>
+<div id="root"></div>
+<script>
+// Elements tested for in this file
+var types = ["button", "input", "select", "textarea"];
+// no tests for: optgroup, option, fieldset
+
+var root = document.getElementById("root");
+for (var element_type = 0; element_type < types.length; element_type++) {
+ test(function() {
+ root.innerHTML = "<"+types[element_type]+" + id='elem'></"+types[element_type]+">";
+
+ var elem = document.getElementById("elem");
+ assert_false(elem.disabled);
+ },
+ "Test ["+types[element_type]+"]: default behaviour is NOT disabled");
+
+ test(function() {
+ var formats = ["disabled",
+ "disabled=disabled", "disabled='disabled'",
+ "disabled='true'", "disabled=true",
+ "disabled='false'", "disabled=false"];
+
+ for (var f = 0; f < formats.length; f++) {
+ root.innerHTML = "<"+types[element_type]+" id='elem' " + formats[f] + "></"+types[element_type]+">";
+
+ var elem = document.getElementById("elem");
+ assert_true(elem.disabled);
+ }
+ },
+ "Test ["+types[element_type]+"]: verify disabled acts as boolean attribute");
+
+ test(function() {
+ root.innerHTML = "<"+types[element_type]+" id='elem'></"+types[element_type]+"><input id='other' value='no event dispatched'></input>";
+ var elem = document.getElementById("elem"),
+ other = document.getElementById("other");
+
+ assert_equals(other.value, "no event dispatched");
+
+ elem.disabled = true;
+ assert_true(elem.disabled);
+
+ elem.onclick = function () {
+ // change value of other element, to avoid *.value returning "" for disabled elements
+ document.getElementById("other").value = "event dispatched";
+ };
+
+ // Check if dispatched event executes
+ var evObj = document.createEvent('Events');
+ evObj.initEvent("click", true, false);
+ elem.dispatchEvent(evObj);
+ assert_equals(other.value, "event dispatched");
+ },
+ "Test ["+types[element_type]+"]: synthetic click events should be dispatched");
+
+ test(function() {
+ root.innerHTML = "<"+types[element_type]+" id='elem'></"+types[element_type]+"><input id='other' value='no event dispatched'></input>";
+ var elem = document.getElementById("elem"),
+ other = document.getElementById("other");
+
+ assert_equals(other.value, "no event dispatched");
+
+ elem.disabled = true;
+ assert_true(elem.disabled);
+
+ elem.onclick = function () {
+ // change value of other element, to avoid *.value returning "" for disabled elements
+ document.getElementById("other").value = "event dispatched";
+ };
+
+ // Check that click() on a disabled element doesn't dispatch a click event.
+ elem.click();
+ assert_equals(other.value, "no event dispatched");
+ },
+ "Test ["+types[element_type]+"]: click() should not dispatch a click event");
+}
+root.innerHTML = "";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html
new file mode 100644
index 0000000000..4510807c2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: formAction_document_address</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-fs-formaction">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-document's-address">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-button-element">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+ <meta name="assert" content="On getting the formAction IDL attribute, when the content attribute is missing or its value is the empty string, the document's address must be returned instead.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+
+ <div id="missing" style="display:none">
+ <button type="submit">Submit</button>
+ <input type="submit">
+ </div>
+
+ <div id="empty_string" style="display:none">
+ <button type="submit" formaction="">Submit</button>
+ <input type="submit" formaction="">
+ </div>
+
+ <div id="no_assigned_value" style="display:none">
+ <button type="submit" formaction>Submit</button>
+ <input type="submit" formaction>
+ </div>
+
+ <script>
+ // formaction content attribute is missing
+ test(function() {
+ var formAction = document.querySelector('#missing button').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if button.formAction is the document's address when formaction content attribute is missing");
+
+ test(function() {
+ var formAction = document.querySelector('#missing input').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if input.formAction is the document's address when formaction content attribute is missing");
+
+ // formaction content attribute value is empty string
+ test(function() {
+ var formAction = document.querySelector('#empty_string button').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if button.formAction is the document's address when formaction content attribute value is empty string");
+
+ test(function() {
+ var formAction = document.querySelector('#empty_string input').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if input.formAction is the document's address when formaction content attribute value is empty string");
+
+ // formaction content attribute value is not assigned, just for comparison with empty string above
+ test(function() {
+ var formAction = document.querySelector('#no_assigned_value button').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if button.formAction is the document's address when formaction content attribute value is not assigned");
+
+ test(function() {
+ var formAction = document.querySelector('#no_assigned_value input').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if input.formAction is the document's address when formaction content attribute value is not assigned");
+
+ var newUrl = location.href.replace(/\/[^\/]*$/,'\/dummy.html');
+ history.pushState('','','dummy.html');
+
+ test(function() {
+ assert_equals(document.location.href, newUrl);
+
+ var formAction = document.querySelector('#missing button').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if button.formAction is the document's new address when formaction content attribute is missing and pushState has been used");
+
+ test(function() {
+ assert_equals(document.location.href, newUrl);
+
+ var formAction = document.querySelector('#missing input').formAction;
+ var address = document.location.href;
+ assert_equals(formAction, address);
+ }, "Check if input.formAction is the document's new address when formaction content attribute is missing and pushState has been used");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formaction.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formaction.html
new file mode 100644
index 0000000000..82798eaa84
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/formaction.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html><head>
+ <title>formaction on button element</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <meta content="formaction on button element" name="description">
+ <link href="https://html.spec.whatwg.org/multipage/#dom-fs-formaction" rel="help">
+</head>
+ <body>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <div id="log"></div>
+ <button formaction="http://www.example.com/" style="display: none" type="submit">Submit</button>
+ <input formaction="http://www.example.com/" style="display: none" type="submit" value="submit">
+ <input style="display: none" type="submit" value="submit">
+ <input formaction="" style="display: none" type="submit" value="submit">
+
+ <script type="text/javascript">
+ function relativeToAbsolute(relativeURL) {
+ var a = document.createElement('a');
+ a.href = relativeURL;
+ return a.href;
+ }
+ test(function() {assert_equals(document.getElementsByTagName("button")[0].formAction, "http://www.example.com/")}, "formAction on button support");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].formAction, "http://www.example.com/")}, "formAction on input support");
+
+ var testElem = document.getElementsByTagName("input")[0];
+ testElem.formAction = "http://www.example.com/page2.html";
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].formAction, "http://www.example.com/page2.html")}, "formaction absolute URL value on input reflects correct value after being updated by the DOM");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].getAttribute("formaction"), "http://www.example.com/page2.html")}, "formAction absolute URL value is correct using getAttribute");
+
+ var testElem = document.getElementsByTagName("input")[0];
+ testElem.formAction = "../page3.html";
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].formAction, relativeToAbsolute('../page3.html'))}, "formAction relative URL value on input reflects correct value after being updated by the DOM");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].getAttribute("formaction"), "../page3.html")}, "formAction relative URL value is correct using getAttribute");
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].formAction, document.URL)}, "On getting, when formaction is missing, the document's address must be returned");
+ test(function() {assert_equals(document.getElementsByTagName("input")[2].formAction, document.URL)}, "On getting, when formaction value is the empty string, the document's address must be returned");
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname-iframe.html b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname-iframe.html
new file mode 100644
index 0000000000..b5ed7e3d9a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Submitting element directionality: the dirname attribute support</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
diff --git a/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname.js b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname.js
new file mode 100644
index 0000000000..f0e97bc301
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/attributes-common-to-form-controls/resources/dirname.js
@@ -0,0 +1,12 @@
+function onIframeLoadedDone(t, cb, selector="iframe") {
+ const iframe = document.querySelector(selector);
+ iframe.addEventListener("load", function() {
+ // The initial about:blank load event can be fired before the form navigation occurs.
+ // See https://github.com/whatwg/html/issues/490 for more information.
+ if(iframe.contentWindow.location.href == "about:blank") { return; }
+
+ const params = new URLSearchParams(iframe.contentWindow.location.search);
+ t.step(() => cb(params))
+ t.done();
+ });
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/beforeinput.tentative.html b/testing/web-platform/tests/html/semantics/forms/beforeinput.tentative.html
new file mode 100644
index 0000000000..7aa51a2523
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/beforeinput.tentative.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test the onbeforeinput attribute</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<div id="container"></div>
+<script>
+ const container = document.getElementById("container");
+ const events = new Map();
+
+ function handleEvent(event) {
+ if (!events.has(event.target)) {
+ events.set(event.target, []);
+ }
+ events.get(event.target).push(event);
+ }
+
+ let onInputFired = null;
+
+ const onBeforeInput = handleEvent;
+ const onInput = (event) => {
+ handleEvent(event);
+ onInputFired()
+ }
+
+ let elems = [];
+ for (let type of ["text", "search", "tel", "url", "email", "password", "number"]) {
+ elems.push(`<input type=${type} onbeforeinput="onBeforeInput(event)" oninput="onInput(event)">
+<input type=${type}>
+`);
+ }
+ elems.push(`<textarea onbeforeinput="onBeforeInput(event)" oninput="onInput(event)"></textarea>
+<textarea></textarea>`)
+ container.innerHTML = elems.join("");
+
+for (const element of container.children) {
+ promise_test(async t => {
+ if (!element.hasAttribute("onbeforeinput")) {
+ element.onbeforeinput = e => onBeforeInput(e);
+ element.oninput = e => onInput(e);
+ };
+
+ let afterOnInput = new Promise(resolve => {onInputFired = resolve});
+ await test_driver.send_keys(element, "1"); // has to be a number so <input type=number> works
+ // Ensure we're in the post-update state
+ await afterOnInput;
+
+ assert_true(events.has(element), "Got events for element");
+ let elementEvents = events.get(element);
+
+ assert_array_equals(elementEvents.map(event => event.type), ["beforeinput", "input"], "Got expected events");
+ }, `${element.outerHTML}`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-checkValidity.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-checkValidity.html
new file mode 100644
index 0000000000..2e790c75d8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-checkValidity.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.checkValidity()</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-cva-checkvalidity">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "password"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {maxLength: "4", value: "abcdef"}, expected: true, name: "[target] not suffering from being too long", dirty: true},
+ {conditions: {pattern: "[A-Z]", value: "abc"}, expected: false, name: "[target] suffering from a pattern mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["url"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {maxLength: "20", value: "http://www.example.com"}, expected: true, name: "[target] suffering from being too long", dirty: true},
+ {conditions: {pattern: "http://www.example.com", value: "http://www.example.net"}, expected: false, name: "[target] suffering from a pattern mismatch"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] suffering from a type mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["email"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {maxLength: "10", value: "test@example.com"}, expected: true, name: "[target] not suffering from being too long", dirty: true},
+ {conditions: {pattern: "test@example.com", value: "test@example.net"}, expected: false, name: "[target] suffering from a pattern mismatch"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] suffering from a type mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-01-01T12:00:00", value: "2001-01-01T12:00:00"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-01-01T12:00:00", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 60 * 1000, value: "2001-01-01T12:03:00"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["date"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-01-01", value: "2001-01-01"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-01-01", value: "2000-01-01"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 1 * 86400000, value: "2001-01-03"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["month"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-01", value: "2001-01"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-01", value: "2000-01"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 3 * 1 * 1, value: "2001-03"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["week"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-W01", value: "2001-W01"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-W01", value: "2000-W01"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 1 * 604800000, value: "2001-W03"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["time"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "12:00:00", value: "13:00:00"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "12:00:00", value: "11:00:00"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 60 * 1000, value: "12:03:00"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["number"],
+ testData: [
+ {conditions: {max: "5", value: "6"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "5", value: "4"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 1 * 1, value: "3"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["checkbox", "radio"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, checked: false, name: "test1"}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["file"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, files: null}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "select",
+ types: [],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "checkValidity");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-reportValidity.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-reportValidity.html
new file mode 100644
index 0000000000..c68e21c9d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-reportValidity.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.reportValidity()</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-cva-reportvalidity">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "password"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {maxLength: "4", value: "abcdef"}, expected: true, name: "[target] not suffering from being too long", dirty: true},
+ {conditions: {pattern: "[A-Z]", value: "abc"}, expected: false, name: "[target] suffering from a pattern mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["url"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {maxLength: "20", value: "http://www.example.com"}, expected: true, name: "[target] not suffering from being too long", dirty: true},
+ {conditions: {pattern: "http://www.example.com", value: "http://www.example.net"}, expected: false, name: "[target] suffering from a pattern mismatch"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] suffering from a type mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["email"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {maxLength: "10", value: "test@example.com"}, expected: true, name: "[target] not suffering from being too long", dirty: true},
+ {conditions: {pattern: "test@example.com", value: "test@example.net"}, expected: false, name: "[target] suffering from a pattern mismatch"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] suffering from a type mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-01-01T12:00:00", value: "2001-01-01T12:00:00"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-01-01T12:00:00", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 60 * 1000, value: "2001-01-01T12:03:00"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["date"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-01-01", value: "2001-01-01"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-01-01", value: "2000-01-01"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 1 * 86400000, value: "2001-01-03"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["month"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-01", value: "2001-01"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-01", value: "2000-01"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 3 * 1 * 1, value: "2001-03"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["week"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "2000-W01", value: "2001-W01"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "2001-W01", value: "2000-W01"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 1 * 604800000, value: "2001-W03"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["time"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {max: "12:00:00", value: "13:00:00"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "12:00:00", value: "11:00:00"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 60 * 1000, value: "12:03:00"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["number"],
+ testData: [
+ {conditions: {max: "5", value: "6"}, expected: false, name: "[target] suffering from an overflow"},
+ {conditions: {min: "5", value: "4"}, expected: false, name: "[target] suffering from an underflow"},
+ {conditions: {step: 2 * 1 * 1, value: "3"}, expected: false, name: "[target] suffering from a step mismatch"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["checkbox", "radio"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, checked: false, name: "test1"}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["file"],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, files: null}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "select",
+ types: [],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [
+ {conditions: {}, expected: true, name: "[target] no constraint"},
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] suffering from being missing"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "reportValidity");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validate.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validate.html
new file mode 100644
index 0000000000..e32fd90330
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validate.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Constraint validation</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#constraint-validation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form id="fm1" style="display:none">
+ <fieldset id="test0">
+ <input type="text" required value="" id="test1">
+ </fieldset>
+ <input type="email" value="abc" id="test2">
+ <button id="test3">TEST</button>
+ <select id="test4"></select>
+ <textarea id="test5"></textarea>
+ <output id="test6"></output>
+</form>
+<form id="fm2" style="display:none">
+ <fieldset>
+ <input type="text" required value="abc">
+ </fieldset>
+ <input type="email" value="test@example.com">
+ <button>TEST</button>
+ <select></select>
+ <textarea></textarea>
+ <output></output>
+</form>
+<form id="fm3" style="display:none">
+ <fieldset id="fs">
+ <legend><input type="text" id="inp1"></legend>
+ <input type="text" required value="" id="inp2">
+ </fieldset>
+</form>
+
+<script>
+ var cancelable = true,
+ times1 = 0,
+ times2 = 0,
+ invalidList1 = [],
+ invalidList2 = [],
+ test1,
+ test2,
+ fm1,
+ fm2,
+ fm3;
+
+ setup(function () {
+ fm1 = document.getElementById("fm1");
+ fm2 = document.getElementById("fm2");
+ fm3 = document.getElementById("fm3");
+ test1 = document.getElementById("test1");
+ test2 = document.getElementById("test2");
+ for (var index = 0; index < fm1.elements.length; index++) {
+ var ele = fm1.elements.item(index);
+ ele.addEventListener("invalid", function (e) {
+ times1++;
+ invalidList1.push(e.target);
+ if (!e.cancelable)
+ cancelable = e.cancelable;
+ }, false);
+ }
+
+ for (var index = 0; index < fm2.elements.length; index++) {
+ var ele = fm2.elements.item(index);
+ ele.addEventListener("invalid", function (e) {
+ times2++;
+ invalidList2.push(ele);
+ }, false);
+ }
+ });
+
+ test(function(){
+ assert_false(fm1.checkValidity(), "The checkValidity method should be false.");
+ }, "If there is any invalid submittable element whose form owner is the form, the form.checkValidity must be false");
+
+ test(function(){
+ assert_true("reportValidity" in fm1, "The reportValidity method is not supported");
+ assert_false(fm1.reportValidity(), "The reportValidity method should be false.");
+ }, "If there is any invalid submittable element whose form owner is the form, the form.reportValidity must be false");
+
+ test(function(){
+ assert_true(fm2.checkValidity(), "The checkValidity method should be true.");
+ }, "If all of the submittable elements whose form owner is the form are valid, the form.checkValidity must be true");
+
+ test(function(){
+ assert_true("reportValidity" in fm2, "The reportValidity method is not supported.");
+ assert_true(fm2.reportValidity(), "The reportValidity method should be true.");
+ }, "If all of the submittable elements whose form owner is the form are valid, the form.reportValidity must be true");
+
+ test(function(){
+ assert_false(fm3.checkValidity(), "The checkValidity method should be false.");
+ document.getElementById("fs").disabled = true;
+ assert_true(fm3.checkValidity(), "The checkValidity method should be true.");
+
+ document.getElementById("inp1").value = "aaa";
+ document.getElementById("inp1").type = "url";
+ assert_false(fm3.checkValidity(), "The checkValidity method should be false.");
+ }, "Check the checkValidity method of the form element when it has a fieldset child");
+
+ test(function(){
+ // Restore the condition to default which was modified during the previous test.
+ document.getElementById("fs").disabled = false;
+ document.getElementById("inp1").value = "";
+ document.getElementById("inp1").type = "text";
+
+ assert_true("reportValidity" in fm3, "The reportValidity method is not supported.");
+ assert_false(fm3.reportValidity(), "The reportValidity method should be false.");
+ document.getElementById("fs").disabled = true;
+ assert_true(fm3.reportValidity(), "The reportValidity method should be true.");
+
+ document.getElementById("inp1").value = "aaa";
+ document.getElementById("inp1").type = "url";
+ assert_false(fm3.reportValidity(), "The reportValidity method should be false.");
+ }, "Check the reportValidity method of the form element when it has a fieldset child");
+
+ test(function () {
+ assert_equals(times1, 4, "The invalid event will be fired if the checkValidity or reportValidity method are called.");
+ assert_array_equals(invalidList1, [test1, test2, test1, test2], "The invalid event must be fired at the invalid control");
+ assert_true(cancelable, "The invalid event is cancelable.");
+ }, "The invalid event must be fired at the invalid controls");
+
+ test(function () {
+ assert_equals(times2, 0, "The invalid event should not be fired, times should be 0.");
+ assert_array_equals(invalidList2, [], "The invalid event should not be fired, invalid list should be empty");
+ }, "The invalid event must not be fired at the invalid controls");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-badInput.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-badInput.html
new file mode 100644
index 0000000000..8f6153b923
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-badInput.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.badInput</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/association-of-controls-and-forms.html#suffering-from-bad-input">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/the-button-element.html#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+var testElements = [
+ {
+ tag: "input",
+ types: ["email"],
+ testData: [
+ {conditions: {multiple: false, value: ""}, expected: false, name: "[target] The multiple attribute is false and the value attribute is empty"},
+ {conditions: {multiple: false, value: "test1@example.com"}, expected: false, name: "[target] The multiple attribute is false and the value attribute is a valid e-mail address"},
+ {conditions: {multiple: true, value: "test1@example.com,test2@eample.com"}, expected: false, name: "[target] The multiple attribute is true and the value contains valid e-mail addresses"},
+ {conditions: {multiple: true, value: "test,1@example.com"}, expected: false, name: "[target] The multiple attribute is true and the value attribute contains a ','"}
+ //TODO, the value contains characters that cannot be converted to punycode.
+ //Can not find a character that cannot be converted to punycode.
+ ]
+ },
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {value: ""}, expected: false, name: "[target] The value attribute is empty"},
+ {conditions: {value: "2000-01-01T12:00:00"}, expected: false, name: "[target] The value attribute is a valid date and time string"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] The value attribute cannot convert to a valid normalized forced-UTC global date and time string"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["color"],
+ testData: [
+ {conditions: {value: ""}, expected: false, name: "[target] The value attribute is empty"},
+ {conditions: {value: "#000000"}, expected: false, name: "[target] The value attribute is a valid sample color"},
+ {conditions: {value: "#FFFFFF"}, expected: false, name: "[target] The value attribute is not a valid lowercase sample color"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] The value attribute cannot convert to a valid sample color"}
+ ]
+ },
+ ];
+ validator.run_test (testElements, "badInput");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-customError.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-customError.html
new file mode 100644
index 0000000000..2ae6240ace
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-customError.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.customError</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-customerror">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+var testElements = [
+ {
+ tag: "input",
+ types: [],
+ testData: [
+ {conditions: {message: "My custom error"}, expected: true, name: "[target] The validity.customError must be true if the custom validity error message is not empty"},
+ {conditions: {message: ""}, expected: false, name: "[target] The validity.customError must be false if the custom validity error message is empty"}
+ ]
+ },
+ {
+ tag: "button",
+ types: [],
+ testData: [
+ {conditions: {message: "My custom error"}, expected: true, name: "[target] The validity.customError must be true if the custom validity error message is not empty"},
+ {conditions: {message: ""}, expected: false, name: "[target] The validity.customError must be false if the custom validity error message is empty"}
+ ]
+ },
+ {
+ tag: "select",
+ types: [],
+ testData: [
+ {conditions: {message: "My custom error"}, expected: true, name: "[target] The validity.customError must be true if the custom validity error message is not empty"},
+ {conditions: {message: ""}, expected: false, name: "[target] The validity.customError must be false if the custom validity error message is empty"}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [
+ {conditions: {message: "My custom error"}, expected: true, name: "[target] The validity.customError must be true if the custom validity error message is not empty"},
+ {conditions: {message: ""}, expected: false, name: "[target] The validity.customError must be false if the custom validity error message is empty"}
+ ]
+ }
+ ]
+
+ validator.run_test(testElements, "customError");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html
new file mode 100644
index 0000000000..286001b965
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.patternMismatch</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-patternmismatch">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "url", "email", "password"],
+ testData: [
+ {conditions: {pattern: null, value: "abc"}, expected: false, name: "[target] The pattern attribute is not set"},
+ {conditions: {pattern: "[A-Z]+", value: ""}, expected: false, name: "[target] The value attibute is empty string"},
+ {conditions: {pattern: "[A-Z]{1}", value: "A"}, expected: false, name: "[target] The value attribute matches the pattern attribute"},
+ {conditions: {pattern: "[A-Z]+", value: "\x41\x42\x43"}, expected: false, name: "[target] The value(ABC) in unicode attribute matches the pattern attribute"},
+ {conditions: {pattern: "[a-z]{3,}", value: "ABCD"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute"},
+ {conditions: {pattern: "[A-Z]+", value: "ABC123"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute even when a subset matches"},
+ {conditions: {pattern: "(abc", value: "de"}, expected: false, name: "[target] Invalid regular expression gets ignored"},
+ {conditions: {pattern: "[(]", value: "x"}, expected: false, name: "[target] Invalid `v` regular expression gets ignored"},
+ {conditions: {pattern: "a)(b", value: "de"}, expected: false, name: "[target] The pattern attribute tries to escape a group"},
+ {conditions: {pattern: "a\\u{10FFFF}", value: "a\u{10FFFF}"}, expected: false, name: "[target] The pattern attribute uses Unicode features"},
+ {conditions: {pattern: "\\u1234\\cx[5-\\[]{2}", value: "\u1234\x18[6"}, expected: false, name: "[target] The value attribute matches JavaScript-specific regular expression"},
+ {conditions: {pattern: "\\u1234\\cx[5-\\[]{2}", value: "\u1234\x18[4"}, expected: true, name: "[target] The value attribute mismatches JavaScript-specific regular expression"},
+ ]
+ },
+ {
+ tag: "input",
+ types: ["email"],
+ testData: [
+ {conditions: {multiple: true, pattern: null, value: "abc,abc"}, expected: false, name: "[target] The pattern attribute is not set, if multiple is present"},
+ {conditions: {multiple: true, pattern: "[A-Z]+", value: ""}, expected: false, name: "[target] The value attibute is empty string, if multiple is present"},
+ {conditions: {multiple: true, pattern: "[A-Z]{1}", value: "A,A"}, expected: false, name: "[target] The value attribute matches the pattern attribute, if multiple is present"},
+ {conditions: {multiple: true, pattern: "[A-Z]+", value: "\x41\x42\x43,\x41\x42\x43"}, expected: false, name: "[target] The value(ABC) in unicode attribute matches the pattern attribute, if multiple is present"},
+ {conditions: {multiple: true, pattern: "[a-z]{3,}", value: "abcd,ABCD"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute, if multiple is present"},
+ {conditions: {multiple: true, pattern: "[A-Z]+", value: "ABCD,ABC123"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute even when a subset matches, if multiple is present"},
+ {conditions: {multiple: true, pattern: "(abc", value: "de,de"}, expected: false, name: "[target] Invalid regular expression gets ignored, if multiple is present"},
+ {conditions: {multiple: true, pattern: "[(]", value: "x"}, expected: false, name: "[target] Invalid `v` regular expression gets ignored, if multiple is present"},
+ {conditions: {multiple: true, pattern: "a)(b", value: "de,de"}, expected: false, name: "[target] The pattern attribute tries to escape a group, if multiple is present"},
+ {conditions: {multiple: true, pattern: "a\\u{10FFFF}", value: "a\u{10FFFF},a\u{10FFFF}"}, expected: false, name: "[target] The pattern attribute uses Unicode features, if multiple is present"},
+ {conditions: {multiple: true, pattern: "\\u1234\\cx[5-\\[]{2}", value: "\u1234\x18[6,\u1234\x18[Z"}, expected: false, name: "[target] The value attribute matches JavaScript-specific regular expression, if multiple is present"},
+ {conditions: {multiple: true, pattern: "\\u1234\\cx[5-\\[]{2}", value: "\u1234\x18[4,\u1234\x18[6"}, expected: true, name: "[target] The value attribute mismatches JavaScript-specific regular expression, if multiple is present"},
+ {conditions: {multiple: true, pattern: "a,", value: "a,"}, expected: true, name: "[target] Commas should be stripped from regex input, if multiple is present"},
+ ]
+ }
+ ];
+
+ validator.run_test (testElements, "patternMismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow-weekmonth.html
new file mode 100644
index 0000000000..5a1478829f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow-weekmonth.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.rangeOverflow</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeoverflow">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["month"],
+ testData: [
+ {conditions: {max: "", value: "2000-01"}, expected: false, name: "[target] The max attribute is not set"},
+ {conditions: {max: "2000-01", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {max: "2000/01", value: "2001-02"}, expected: false, name: "[target] The max attribute is an invalid month string"},
+ {conditions: {max: "2000-01", value: "2000-1"}, expected: false, name: "[target] The value attribute is an invalid month string"},
+ {conditions: {max: "987-01", value: "988-01"}, expected: false, name: "[target] The value is an invalid month string(year is three digits)"},
+ {conditions: {max: "2000-01", value: "2000-13"}, expected: false, name: "[target] The value is an invalid month string(month is greater than 12)"},
+ {conditions: {max: "2000-12", value: "2000-01"}, expected: false, name: "[target] The max attribute is greater than value attribute"},
+ {conditions: {max: "2000-01", value: "2000-12"}, expected: true, name: "[target] The value attribute is greater than max attribute"},
+ {conditions: {max: "9999-01", value: "10000-01"}, expected: true, name: "[target] The value attribute is greater than max attribute(Year is 10000 should be valid)"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["week"],
+ testData: [
+ {conditions: {max: "", value: "2000-W01"}, expected: false, name: "[target] The max attribute is not set"},
+ {conditions: {max: "2000-W01", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {max: "2000/W01", value: "2001-W02"}, expected: false, name: "[target] The max attribute is an invalid week string"},
+ {conditions: {max: "2000-W01", value: "2000-W2"}, expected: false, name: "[target] The value attribute is an invalid week string"},
+ {conditions: {max: "2000-W01", value: "2000-w02"}, expected: false, name: "[target] The value attribute is an invalid week string(w is in lowercase)"},
+ {conditions: {max: "987-W01", value: "988-W01"}, expected: false, name: "[target] The value is an invalid week string(year is three digits)"},
+ {conditions: {max: "2000-W01", value: "2000-W57"}, expected: false, name: "[target] The value is an invalid week string(week is too greater)"},
+ {conditions: {max: "2000-W12", value: "2000-W01"}, expected: false, name: "[target] The max attribute is greater than value attribute"},
+ {conditions: {max: "2000-W01", value: "2000-W12"}, expected: true, name: "[target] The value attribute is greater than max attribute"},
+ {conditions: {max: "9999-W01", value: "10000-W01"}, expected: true, name: "[target] The value attribute is greater than max attribute(Year is 10000 should be valid)"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "rangeOverflow");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html
new file mode 100644
index 0000000000..98847e70ff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.rangeOverflow</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeoverflow">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {max: "", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] The max attribute is not set"},
+ {conditions: {max: "2000-01-01T12:00:00", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {max: "2000-01-01 12:00:00", value: "2001-01-01T12:00:00"}, expected: false, name: "[target] The max attribute is an invalid local date time string"},
+ {conditions: {max: "2000-01-01T12:00:00", value: "2000-01-01T11:00:00"}, expected: false, name: "[target] The max attribute is greater than the value attribute"},
+ {conditions: {max: "2000-01-01T23:59:59", value: "2001-01-01T24:00:00"}, expected: false, name: "[target] The value is an invalid local date time string(hour is greater than 23)"},
+ {conditions: {max: "1970-01-01T12:00", value: "80-01-01T12:00"}, expected: false, name: "[target] The value if an invalid local date time string(year is two digits)"},
+ {conditions: {max: "2000-01-01T12:00:00", value: "2001-01-01T13:00:00"}, expected: true, name: "[target] The value is greater than max"},
+ {conditions: {max: "2000-01-01T12:00:00.1", value: "2000-01-01T12:00:00.2"}, expected: true, name: "[target] The value is greater than max(with millisecond in 1 digit)"},
+ {conditions: {max: "2000-01-01T12:00:00.01", value: "2000-01-01T12:00:00.02"}, expected: true, name: "[target] The value is greater than max(with millisecond in 2 digits)"},
+ {conditions: {max: "2000-01-01T12:00:00.001", value: "2000-01-01T12:00:00.002"}, expected: true, name: "[target] The value is greater than max(with millisecond in 3 digits)"},
+ {conditions: {max: "2000-01-01T12:00:00", value: "10000-01-01T12:00:00"}, expected: true, name: "[target] The value is greater than max(Year is 10000 should be valid)"},
+ ]
+ },
+ {
+ tag: "input",
+ types: ["date"],
+ testData: [
+ {conditions: {max: "", value: "2000-01-01"}, expected: false, name: "[target] The max attribute is not set"},
+ {conditions: {max: "2000-01-01", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {max: "2000/01/01", value: "2002-01-01"}, expected: false, name: "[target] The max attribute is an invalid date"},
+ {conditions: {max: "2000-01-01", value: "2000-2-2"}, expected: false, name: "[target] The value attribute is an invalid date"},
+ {conditions: {max: "987-01-01", value: "988-01-01"}, expected: false, name: "[target] The value is an invalid date(year is three digits)"},
+ {conditions: {max: "2000-01-01", value: "2000-13-01"}, expected: false, name: "[target] The value is an invalid date(month is greater than 12)"},
+ {conditions: {max: "2000-01-01", value: "2000-02-30"}, expected: false, name: "[target] The value is an invalid date(date is greater than 29 for Feb)"},
+ {conditions: {max: "2000-12-01", value: "2000-01-01"}, expected: false, name: "[target] The max attribute is greater than value attribute"},
+ {conditions: {max: "2000-12-01", value: "2001-01-01"}, expected: true, name: "[target] The value attribute is greater than max attribute"},
+ {conditions: {max: "9999-01-01", value: "10000-01-01"}, expected: true, name: "[target] The value attribute is greater than max attribute(Year is 10000 should be valid)"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["time"],
+ testData: [
+ {conditions: {max: "", value: "12:00:00"}, expected: false, name: "[target] The max attribute is not set"},
+ {conditions: {max: "12:00:00", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {max: "12.00.00", value: "12:00:01"}, expected: false, name: "[target] The max attribute is an invalid time string"},
+ {conditions: {max: "12:00:00", value: "12.00.01"}, expected: false, name: "[target] The value attribute is an invalid time string"},
+ {conditions: {max: "23:59:59", value: "24:00:00"}, expected: false, name: "[target] The value attribute is an invalid time string(hour is greater than 23)"},
+ {conditions: {max: "23:59:59", value: "23:60:00"}, expected: false, name: "[target] The value attribute is an invalid time string(minute is greater than 59)"},
+ {conditions: {max: "23:59:59", value: "23:59:60"}, expected: false, name: "[target] The value attribute is an invalid time string(second is greater than 59)"},
+ {conditions: {max: "13:00:00", value: "12:00:00"}, expected: false, name: "[target] The max attribute is greater than value attribute"},
+ {conditions: {max: "12:00:00", value: "13"}, expected: false, name: "[target] The time missing second and minute parts is invalid"},
+ {conditions: {max: "12:00:00", value: "12:00:02"}, expected: true, name: "[target] The value attribute is greater than max attribute"},
+ {conditions: {max: "12:00:00.1", value: "12:00:00.2"}, expected: true, name: "[target] The value is greater than max(with millisecond in 1 digit)"},
+ {conditions: {max: "12:00:00.01", value: "12:00:00.02"}, expected: true, name: "[target] The value is greater than max(with millisecond in 2 digit)"},
+ {conditions: {max: "12:00:00.001", value: "12:00:00.002"}, expected: true, name: "[target] The value is greater than max(with millisecond in 3 digit)"},
+ {conditions: {max: "12:00:00", value: "12:01"}, expected: true, name: "[target] The time missing second part is valid"},
+ {conditions: {max: "12:00:00", min: "14:00:00", value: "12:00:00"}, expected: false, name: "[target] The time is max for reversed range"},
+ {conditions: {max: "12:00:00", min: "14:00:00", value: "13:00:00"}, expected: true, name: "[target] The time is outside the accepted range for reversed range"},
+ {conditions: {max: "12:00:00", min: "14:00:00", value: "14:00:00"}, expected: false, name: "[target] The time is min for reversed range"},
+ {conditions: {max: "12:00:00", min: "14:00:00", value: "15:00:00"}, expected: false, name: "[target] The time is inside the accepted range for reversed range"},
+ ]
+ },
+ {
+ tag: "input",
+ types: ["number"],
+ testData: [
+ {conditions: {max: "", value: "10"}, expected: false, name: "[target] The max attribute is not set"},
+ {conditions: {max: "5", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {max: "5", value: "4"}, expected: false, name: "[target] The max is greater than value(integer)"},
+ {conditions: {max: "-5.5", value: "-5.6"}, expected: false, name: "[target] The max is greater than value(floating number)"},
+ {conditions: {max: "-0", value: "0"}, expected: false, name: "[target] The max equals to value"},
+ {conditions: {max: "5", value: "1abc"}, expected: false, name: "[target] The value is not a number"},
+ {conditions: {max: "5", value: "6"}, expected: true, name: "[target] The value is greater than max(integer)"},
+ {conditions: {max: "-5.5", value: "-5.4"}, expected: true, name: "[target] The value is greater than max(floating number)"},
+ {conditions: {max: "-1", value: "-.8"}, expected: true, name: "[target] The value is greater than max(special floating number)"},
+ {conditions: {max: "-5e-1", value: "5e+2"}, expected: true, name: "[target] The value is greater than max(scientific notation)"},
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "rangeOverflow");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow-weekmonth.html
new file mode 100644
index 0000000000..9ddf2565c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow-weekmonth.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.rangeUnderflow</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeunderflow">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["month"],
+ testData: [
+ {conditions: {min: "", value: "2000-01"}, expected: false, name: "[target] The min attribute is not set"},
+ {conditions: {min: "2000-01", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {min: "2001/01", value: "2000-02"}, expected: false, name: "[target] The min attribute is an invalid month string"},
+ {conditions: {min: "2000-02", value: "2000-1"}, expected: false, name: "[target] The value attribute is an invalid month string"},
+ {conditions: {min: "988-01", value: "987-01"}, expected: false, name: "[target] The value is an invalid month string(year is three digits)"},
+ {conditions: {min: "2001-01", value: "2000-13"}, expected: false, name: "[target] The value is an invalid month string(month is less than 12)"},
+ {conditions: {min: "2000-01", value: "2000-12"}, expected: false, name: "[target] The min attribute is less than value attribute"},
+ {conditions: {min: "2001-01", value: "2000-12"}, expected: true, name: "[target] The value attribute is less than min attribute"},
+ {conditions: {min: "10000-01", value: "2000-01"}, expected: true, name: "[target] The value attribute is less than min attribute(Year is 10000 should be valid)"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["week"],
+ testData: [
+ {conditions: {min: "", value: "2000-W01"}, expected: false, name: "[target] The min attribute is not set"},
+ {conditions: {min: "2000-W01", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {min: "2001/W02", value: "2000-W01"}, expected: false, name: "[target] The min attribute is an invalid week string"},
+ {conditions: {min: "2001-W02", value: "2000-W1"}, expected: false, name: "[target] The value attribute is an invalid week string"},
+ {conditions: {min: "2001-W02", value: "2000-w01"}, expected: false, name: "[target] The value attribute is an invalid week string(w is in lowercase)"},
+ {conditions: {min: "988-W01", value: "987-W01"}, expected: false, name: "[target] The value is an invalid week string(year is three digits)"},
+ {conditions: {min: "2001-W01", value: "2000-W57"}, expected: false, name: "[target] The value is an invalid week string(week is too greater)"},
+ {conditions: {min: "2000-W01", value: "2000-W12"}, expected: false, name: "[target] The min attribute is less than value attribute"},
+ {conditions: {min: "2000-W12", value: "2000-W01"}, expected: true, name: "[target] The value attribute is less than min attribute"},
+ {conditions: {min: "10000-W01", value: "2000-W01"}, expected: true, name: "[target] The value attribute is less than min attribute(Year is 10000 should be valid)"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "rangeUnderflow");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html
new file mode 100644
index 0000000000..c97af1b671
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.rangeUnderflow</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeunderflow">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {min: "", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] The min attribute is not set"},
+ {conditions: {min: "2000-01-01T12:00:00", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {min: "2001-01-01 12:00:00", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] The min attribute is an invalid local date time string"},
+ {conditions: {min: "2000-01-01T11:00:00", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] The min attribute is less than the value attribute"},
+ {conditions: {min: "2001-01-01T23:59:59", value: "2000-01-01T24:00:00"}, expected: false, name: "[target] The value is an invalid local date time string(hour is greater than 23)"},
+ {conditions: {min: "1980-01-01T12:00", value: "79-01-01T12:00"}, expected: false, name: "[target] The value is an invalid local date time string(year is two digits)"},
+ {conditions: {min: "2000-01-01T13:00:00", value: "2000-01-01T12:00:00"}, expected: true, name: "[target] The value is less than min"},
+ {conditions: {min: "2000-01-01T12:00:00.2", value: "2000-01-01T12:00:00.1"}, expected: true, name: "[target] The value is less than min(with millisecond in 1 digit)"},
+ {conditions: {min: "2000-01-01T12:00:00.02", value: "2000-01-01T12:00:00.01"}, expected: true, name: "[target] The value is less than min(with millisecond in 2 digits)"},
+ {conditions: {min: "2000-01-01T12:00:00.002", value: "2000-01-01T12:00:00.001"}, expected: true, name: "[target] The value is less than min(with millisecond in 3 digits)"},
+ {conditions: {min: "10000-01-01T12:00:00", value: "2000-01-01T12:00:00"}, expected: true, name: "[target] The value is less than min(Year is 10000 should be valid)"},
+ {conditions: {max: "8593-01-01T02:09", value: "8592-01-01T02:09"}, expected: false, name: "[target] The value is greater than max"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["date"],
+ testData: [
+ {conditions: {min: "", value: "2000-01-01"}, expected: false, name: "[target] The min attribute is not set"},
+ {conditions: {min: "2000-01-01", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {min: "2001/01/01", value: "2000-01-01"}, expected: false, name: "[target] The min attribute is an invalid date"},
+ {conditions: {min: "2000-02-02", value: "2000-1-1"}, expected: false, name: "[target] The value attribute is an invalid date"},
+ {conditions: {min: "988-01-01", value: "987-01-01"}, expected: false, name: "[target] The value is an invalid date(year is three digits)"},
+ {conditions: {min: "2001-01-01", value: "2000-13-01"}, expected: false, name: "[target] The value is an invalid date(month is less than 12)"},
+ {conditions: {min: "2001-01-01", value: "2000-02-30"}, expected: false, name: "[target] The value is an invalid date(date is less than 29 for Feb)"},
+ {conditions: {min: "2000-01-01", value: "2000-12-01"}, expected: false, name: "[target] The min attribute is less than value attribute"},
+ {conditions: {min: "2000-12-01", value: "2000-01-01"}, expected: true, name: "[target] The value attribute is less than min attribute"},
+ {conditions: {min: "10000-01-01", value: "9999-01-01"}, expected: true, name: "[target] The value attribute is less than min attribute(Year is 10000 should be valid)"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["time"],
+ testData: [
+ {conditions: {min: "", value: "12:00:00"}, expected: false, name: "[target] The min attribute is not set"},
+ {conditions: {min: "12:00:00", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {min: "12.00.01", value: "12:00:00"}, expected: false, name: "[target] The min attribute is an invalid time string"},
+ {conditions: {min: "12:00:01", value: "12.00.00"}, expected: false, name: "[target] The value attribute is an invalid time string"},
+ {conditions: {min: "12:00:00", value: "13:00:00"}, expected: false, name: "[target] The min attribute is less than value attribute"},
+ {conditions: {min: "13:00:00", value: "12"}, expected: false, name: "[target] The time missing second and minute parts is invalid"},
+ {conditions: {min: "12:00:02", value: "12:00:00"}, expected: true, name: "[target] The value attribute is less than min attribute"},
+ {conditions: {min: "12:00:00.2", value: "12:00:00.1"}, expected: true, name: "[target] The value is less than min(with millisecond in 1 digit)"},
+ {conditions: {min: "12:00:00.02", value: "12:00:00.01"}, expected: true, name: "[target] The value is less than min(with millisecond in 2 digit)"},
+ {conditions: {min: "12:00:00.002", value: "12:00:00.001"}, expected: true, name: "[target] The value is less than min(with millisecond in 3 digit)"},
+ {conditions: {min: "12:00:00", value: "11:59"}, expected: true, name: "[target] The time missing second part is valid"},
+ {conditions: {min: "14:00:00", max: "12:00:00", value: "12:00:00"}, expected: false, name: "[target] The time is max for reversed range"},
+ {conditions: {min: "14:00:00", max: "12:00:00", value: "13:00:00"}, expected: true, name: "[target] The time is outside the accepted range for reversed range"},
+ {conditions: {min: "14:00:00", max: "12:00:00", value: "14:00:00"}, expected: false, name: "[target] The time is min for reversed range"},
+ {conditions: {min: "14:00:00", max: "12:00:00", value: "15:00:00"}, expected: false, name: "[target] The time is inside the accepted range for reversed range"},
+ ]
+ },
+ {
+ tag: "input",
+ types: ["number"],
+ testData: [
+ {conditions: {min: "", value: "10"}, expected: false, name: "[target] The min attribute is not set"},
+ {conditions: {min: "5", value: ""}, expected: false, name: "[target] Value is empty string"},
+ {conditions: {min: "4", value: "5"}, expected: false, name: "[target] The min is less than value(integer)"},
+ {conditions: {min: "-5.6", value: "-5.5"}, expected: false, name: "[target] The min is less than value(floating number)"},
+ {conditions: {min: "0", value: "-0"}, expected: false, name: "[target] The min equals to value"},
+ {conditions: {min: "5", value: "6abc"}, expected: false, name: "[target] The value is not a number"},
+ {conditions: {min: "6", value: "5"}, expected: true, name: "[target] The value is less than min(integer)"},
+ {conditions: {min: "-5.4", value: "-5.5"}, expected: true, name: "[target] The value is less than min(floating number)"},
+ {conditions: {min: "1", value: "-.8"}, expected: true, name: "[target] The value is less than min(special floating number)"},
+ {conditions: {min: "5e+2", value: "-5e-1"}, expected: true, name: "[target] The value is less than min(scientific notation)"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "rangeUnderflow");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-stepMismatch.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-stepMismatch.html
new file mode 100644
index 0000000000..4d4b4328f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-stepMismatch.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.stepMismatch</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-stepmismatch">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ //set step = 2 * default step * factor
+ var testElements = [
+ {
+ tag: "input",
+ types: ["date"],
+ testData: [
+ {conditions: {step: "", value: "2000-01-01"}, expected: false, name: "[target] The step attribute is not set"},
+ {conditions: {step: 2, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
+ {conditions: {step: 2, value: "1970-01-03"}, expected: false, name: "[target] The value must match the step"},
+ {conditions: {step: 2, value: "1970-01-02"}, expected: true, name: "[target] The value must mismatch the step"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["month"],
+ testData: [
+ {conditions: {step: "", value: "2000-01"}, expected: false, name: "[target] The step attribute is not set"},
+ {conditions: {step: 2, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
+ {conditions: {step: 2, value: "1970-03"}, expected: false, name: "[target] The value must match the step"},
+ {conditions: {step: 2, value: "1970-04"}, expected: true, name: "[target] The value must mismatch the step"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["week"],
+ testData: [
+ {conditions: {step: "", value: "1970-W01"}, expected: false, name: "[target] The step attribute is not set"},
+ {conditions: {step: 2, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
+ {conditions: {step: 2, value: "1970-W03"}, expected: false, name: "[target] The value must match the step"},
+ {conditions: {step: 2, value: "1970-W04"}, expected: true, name: "[target] The value must mismatch the step"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["time"],
+ testData: [
+ {conditions: {step: "", value: "12:00:00"}, expected: false, name: "[target] The step attribute is not set"},
+ {conditions: {step: 2 * 60, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
+ {conditions: {step: 2 * 60, value: "12:02:00"}, expected: false, name: "[target] The value must match the step"},
+ {conditions: {step: 2 * 60, value: "12:03:00"}, expected: true, name: "[target] The value must mismatch the step"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {step: "", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] The step attribute is not set"},
+ {conditions: {step: 2 * 60, value: ""}, expected: false, name: "[target] The value attibute is empty string"},
+ {conditions: {step: 2 * 60, value: "1970-01-01T12:02:00"}, expected: false, name: "[target] The value must match the step"},
+ {conditions: {step: 2 * 60, value: "1970-01-01T12:03:00"}, expected: true, name: "[target] The value must mismatch the step"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["number"],
+ testData: [
+ {conditions: {step: "", value: "1"}, expected: false, name: "[target] The step attribute is not set"},
+ {conditions: {step: "", value: "-.8"}, expected: true, name: "[target] The step attribute is not set and the value attribute is a floating number"},
+ {conditions: {step: 2 * 1 * 1, value: ""}, expected: false, name: "[target] The value attribute is empty string"},
+ {conditions: {step: 2 * 1 * 1, value: "2"}, expected: false, name: "[target] The value must match the step"},
+ {conditions: {step: 2 * 1 * 1, value: "3"}, expected: true, name: "[target] The value must mismatch the step"},
+ {conditions: {step: 0.003, value: "3.6"}, expected: false, name: "[target] No step mismatch when step is a floating number and value is its integral multiple"},
+ {conditions: {step: 1e-12, value: "-12345678.9"}, expected: false, name: "[target] No step mismatch when step is a floating number in exponent format and value is its integral multiple"},
+ {conditions: {step: 3e-15, value: "17"}, expected: true, name: "[target] Step mismatch when step is a very small floating number and value is not its integral multiple"},
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "stepMismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html
new file mode 100644
index 0000000000..aa787d471d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.tooLong</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-toolong">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "url", "email", "password"],
+ testData: [ // Non-dirty value
+ {conditions: {maxLength: "", value: "abc"}, expected: false, name: "[target] Non-dirty value - maxlength is not set"},
+ {conditions: {maxLength: "4", value: ""}, expected: false, name: "[target] Non-dirty value - value is empty string"},
+ {conditions: {maxLength: "4", value: "abc"}, expected: false, name: "[target] Non-dirty value - length of value is less than maxlength"},
+ {conditions: {maxLength: "4", value: "abcd"}, expected: false, name: "[target] Non-dirty value - length of value equals to maxlength"},
+ {conditions: {maxLength: "4", value: "abcde"}, expected: false, name: "[target] Non-dirty value - length of value is greater than maxlength"},
+ //Dirty value
+ {conditions: {maxLength: "4", value: "abc"}, expected: false, name: "[target] Dirty value - value is less than maxlength", dirty: true},
+ {conditions: {maxLength: "4", value: "\u0041\u0041\u0041"}, expected: false, name: "[target] Dirty value - length of value(AAA) in unicode is less than maxlength", dirty: true},
+ {conditions: {maxLength: "4", value: "abcd"}, expected: false, name: "[target] Dirty value - value equals to maxlength", dirty: true},
+ // False due to lack of required interactive editing by the user
+ {conditions: {maxLength: "4", value: "abcde"}, expected: false, name: "[target] Dirty value - length of value is greater than maxlength", dirty: true}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [
+ {conditions: {maxLength: "", value: "abc"}, expected: false, name: "[target] Non-dirty value - maxlength is not set"},
+ {conditions: {maxLength: "4", value: ""}, expected: false, name: "[target] Non-dirty value - value is empty string"},
+ {conditions: {maxLength: "4", value: "abc"}, expected: false, name: "[target] Non-dirty value - length of value is less than maxlength"},
+ {conditions: {maxLength: "4", value: "abcd"}, expected: false, name: "[target] Non-dirty value - length of value equals to maxlength"},
+ {conditions: {maxLength: "4", value: "abcde"}, expected: false, name: "[target] Non-dirty value - length of value is greater than maxlength"},
+ //Dirty value
+ {conditions: {maxLength: "4", value: "abc"}, expected: false, name: "[target] Dirty value - value is less than maxlength", dirty: true},
+ {conditions: {maxLength: "4", value: "\u000D\u000A"}, expected: false, name: "[target] Dirty value - length of value(LF, CRLF) in unicode is less than maxlength", dirty: true},
+ {conditions: {maxLength: "4", value: "abcd"}, expected: false, name: "[target] Dirty value - length of value equals to maxlength", dirty: true},
+ // False due to lack of required interactive editing by the user
+ {conditions: {maxLength: "4", value: "abcde"}, expected: false, name: "[target] Dirty value - length of value is greater than maxlength", dirty: true}
+ ]
+ }
+ ];
+
+ validator.run_test (testElements, "tooLong");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooShort.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooShort.html
new file mode 100644
index 0000000000..b6c0e43992
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooShort.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.tooShort</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#suffering-from-being-too-short">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "url", "email", "password"],
+ testData: [
+ // Non-dirty value
+ {conditions: {minLength: "", value: "abc"}, expected: false, name: "[target] Non-dirty value - minLength is not set"},
+ {conditions: {minLength: "4", value: ""}, expected: false, name: "[target] Non-dirty value - value is empty string"},
+ {conditions: {minLength: "4", value: "abcde"}, expected: false, name: "[target] Non-dirty value - length of value is greater than minLength"},
+ {conditions: {minLength: "4", value: "abcd"}, expected: false, name: "[target] Non-dirty value - length of value equals to minLength"},
+ {conditions: {minLength: "4", value: "abc"}, expected: false, name: "[target] Non-dirty value - length of value is less than minLength"},
+ //Dirty value
+ {conditions: {minLength: "4", value: "abcde"}, expected: false, name: "[target] Dirty value - value is greater than minLength", dirty: true},
+ {conditions: {minLength: "4", value: "\u0041\u0041\u0041\u0041\u0041"}, expected: false, name: "[target] Dirty value - length of value(AAAAA) in unicode is greater than minLength", dirty: true},
+ {conditions: {minLength: "4", value: "abcd"}, expected: false, name: "[target] Dirty value - value equals to minLength", dirty: true},
+ // False due to lack of required interactive editing by the user
+ {conditions: {minLength: "4", value: "abc"}, expected: false, name: "[target] Dirty value - length of value is less than minLength", dirty: true}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [
+ // Non-dirty value
+ {conditions: {minLength: "", value: "abc"}, expected: false, name: "[target] Non-dirty value - minLength is no set"},
+ {conditions: {minLength: "4", value: ""}, expected: false, name: "[target] Non-dirty value - value is empty string"},
+ {conditions: {minLength: "4", value: "abcde"}, expected: false, name: "[target] Non-dirty value - length of value is greater than minLength"},
+ {conditions: {minLength: "4", value: "abcd"}, expected: false, name: "[target] Non-dirty value - length of value equals to minLength"},
+ {conditions: {minLength: "4", value: "abc"}, expected: false, name: "[target] Non-dirty value - length of length of value is greater than minLength"},
+ //Dirty value
+ {conditions: {minLength: "4", value: "abcde"}, expected: false, name: "[target] Dirty value - value is less than minLength", dirty: true},
+ {conditions: {minLength: "4", value: "\u000D\u000A\u000D\u000A\u000D\u000A"}, expected: false, name: "[target] Dirty value - length of value(LF, CRLF) in unicode is less than minLength", dirty: true},
+ {conditions: {minLength: "4", value: "abcd"}, expected: false, name: "[target] Dirty value - length of value equals to minLength", dirty: true},
+ // False due to lack of required interactive editing by the user
+ {conditions: {minLength: "4", value: "abc"}, expected: false, name: "[target] Dirty value - length of value is greater than minLength", dirty: true}
+ ]
+ }
+ ];
+
+ validator.run_test (testElements, "tooShort");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html
new file mode 100644
index 0000000000..40444277cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-typeMismatch.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.typeMismatch</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-typemismatch">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["email"],
+ testData: [
+ // multiple is false
+ {conditions: {multiple: false, value: ""}, expected: false, name: "[target] The value is empty"},
+ {conditions: {multiple: false, value: "test@example.com"}, expected: false, name: "[target] The value is a valid email address"},
+ {conditions: {multiple: false, value: "\u000A\u000D\u0020\u0009 test@example.com \u000A\u000D\u0020\u0009"}, expected: false, name: "[target] The value is a valid email address with some white spaces."},
+ {conditions: {multiple: false, value: "abc"}, expected: true, name: "[target] The value is not an email address"},
+ {conditions: {multiple: false, value: "test1@example.com,test2@example.com"}, expected: true, name: "[target] The value contains multiple email addresses"},
+ //multiple is true
+ {conditions: {multiple: true, value: "test1@example.com,test2@example.com"}, expected: false, name: "[target] The value is valid email addresses"},
+ {conditions: {multiple: true, value: "test1@example.com;test2@example.com"}, expected: true, name: "[target] The value contains invalid separator"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["url"],
+ testData: [
+ {conditions: {multiple: false, value: ""}, expected: false, name: "[target] The value is empty"},
+ {conditions: {multiple: false, value: "http://www.example.com"}, expected: false, name: "[target] The value is a valid url"},
+ {conditions: {multiple: false, value: "\u000A\u000D\u0020\u0009 http://www.example.com \u000A\u000D\u0020\u0009 "}, expected: false, name: "[target] The value is a valid url with some white spaces."},
+ {conditions: {multiple: false, value: "abc"}, expected: true, name: "[target] The value is not an url"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "typeMismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid-weekmonth.html
new file mode 100644
index 0000000000..3f47d391de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid-weekmonth.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.valid</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-valid">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["month"],
+ testData: [
+ {conditions: {max: "2000-01", value: "2001-01"}, expected: false, name: "[target] validity.valid must be false if validity.rangeOverflow is true"},
+ {conditions: {min: "2001-01", value: "2000-01"}, expected: false, name: "[target] validity.valid must be false if validity.rangeUnderflow is true"},
+ // Step checks that "months since Jan 1970" is evenly divisible by `step`
+ {conditions: {step: 3, value: "2001-02"}, expected: false, name: "[target] validity.valid must be false if validity.stepMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["week"],
+ testData: [
+ {conditions: {max: "2000-W01", value: "2001-W01"}, expected: false, name: "[target] validity.valid must be false if validity.rangeOverflow is true"},
+ {conditions: {min: "2001-W01", value: "2000-W01"}, expected: false, name: "[target] validity.valid must be false if validity.rangeUnderflow is true"},
+ {conditions: {step: 2 * 1 * 604800000, value: "2001-W03"}, expected: false, name: "[target] validity.valid must be false if validity.stepMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "isValid");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid.html
new file mode 100644
index 0000000000..009b7a8e5d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valid.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.valid</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-valid">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "password"],
+ testData: [
+ {conditions: {pattern: "[A-Z]", value: "abc"}, expected: false, name: "[target] validity.valid must be false if validity.patternMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["url"],
+ testData: [
+ {conditions: {pattern: "http://www.example.com", value: "http://www.example.net"}, expected: false, name: "[target] validity.valid must be false if validity.patternMismatch is true"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] validity.valid must be false if validity.typeMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["email"],
+ testData: [
+ {conditions: {pattern: "test@example.com", value: "test@example.net"}, expected: false, name: "[target] validity.valid must be false if validity.patternMismatch is true"},
+ {conditions: {value: "abc"}, expected: false, name: "[target] validity.valid must be false if validity.typeMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {max: "2000-01-01T12:00:00", value: "2001-01-01T12:00:00"}, expected: false, name: "[target] validity.valid must be false if validity.rangeOverflow is true"},
+ {conditions: {min: "2001-01-01T12:00:00", value: "2000-01-01T12:00:00"}, expected: false, name: "[target] validity.valid must be false if validity.rangeUnderflow is true"},
+ {conditions: {step: 2 * 60 * 1000, value: "2001-01-01T12:03:00"}, expected: false, name: "[target] validity.valid must be false if validity.stepMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["date"],
+ testData: [
+ {conditions: {max: "2000-01-01", value: "2001-01-01"}, expected: false, name: "[target] validity.valid must be false if validity.rangeOverflow is true"},
+ {conditions: {min: "2001-01-01", value: "2000-01-01"}, expected: false, name: "[target] validity.valid must be false if validity.rangeUnderflow is true"},
+ {conditions: {step: 2 * 1 * 86400000, value: "2000-01-03"}, expected: false, name: "[target] validity.valid must be false if validity.stepMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["time"],
+ testData: [
+ {conditions: {max: "12:00:00", value: "13:00:00"}, expected: false, name: "[target] validity.valid must be false if validity.rangeOverflow is true"},
+ {conditions: {min: "12:00:00", value: "11:00:00"}, expected: false, name: "[target] validity.valid must be false if validity.rangeUnderflow is true"},
+ {conditions: {step: 2 * 60 * 1000, value: "12:03:00"}, expected: false, name: "[target] validity.valid must be false if validity.stepMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["number"],
+ testData: [
+ {conditions: {max: "5", value: "6"}, expected: false, name: "[target] validity.valid must be false if validity.rangeOverflow is true"},
+ {conditions: {min: "5", value: "4"}, expected: false, name: "[target] validity.valid must be false if validity.rangeUnderflow is true"},
+ {conditions: {step: 2 * 1 * 1, value: "3"}, expected: false, name: "[target] validity.valid must be false if validity.stepMismatch is true"},
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["checkbox", "radio"],
+ testData: [
+ {conditions: {required: true, checked: false, name: "test1"}, expected: false, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["file"],
+ testData: [
+ {conditions: {required: true, files: null}, expected: false, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "select",
+ types: [],
+ testData: [
+ {conditions: {required: true, value: ""}, expected: false, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [
+ {conditions: {required: true, value: ""}, expected: false, expectedImmutable: true, name: "[target] validity.valid must be false if validity.valueMissing is true"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "isValid");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-weekmonth.html
new file mode 100644
index 0000000000..2078c2ec13
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing-weekmonth.html
@@ -0,0 +1,55 @@
+
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.valueMissing</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-valuemissing">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<form>
+ <input id="messagetest" type="checkbox" required="" disabled="">
+</form>
+
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["month"],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "2000-12"}, expected: false, name: "[target] Valid month string(2000-12)"},
+ {conditions: {required: true, value: "9999-01"}, expected: false, name: "[target] Valid month string(9999-01)"},
+ {conditions: {required: true, value: 1234567}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a number(1234567)"},
+ {conditions: {required: true, value: new Date()}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a Date object"},
+ {conditions: {required: true, value: "2000-99"}, expected: true, expectedImmutable: false, name: "[target] Invalid month string(2000-99)"},
+ {conditions: {required: true, value: "37-01"}, expected: true, expectedImmutable: false, name: "[target] Invalid month string(37-01)"},
+ {conditions: {required: true, value: "2000/01"}, expected: true, expectedImmutable: false, name: "[target] Invalid month string(2000/01)"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value attribute is empty string"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["week"],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "2000-W12"}, expected: false, name: "[target] Valid week string(2000-W12)"},
+ {conditions: {required: true, value: "9999-W01"}, expected: false, name: "[target] Valid week string(9999-W01)"},
+ {conditions: {required: true, value: 1234567}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a number(1234567)"},
+ {conditions: {required: true, value: new Date()}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a Date object"},
+ {conditions: {required: true, value: "2000-W99"}, expected: true, expectedImmutable: false, name: "[target] Invalid week string(2000-W99)"},
+ {conditions: {required: true, value: "2000-W00"}, expected: true, expectedImmutable: false, name: "[target] invalid week string(2000-W00)"},
+ {conditions: {required: true, value: "2000-w01"}, expected: true, expectedImmutable: false, name: "[target] invalid week string(2000-w01)"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value attribute is empty string"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "valueMissing");
+
+ test(() => {
+ assert_equals(document.getElementById("messagetest").validationMessage, '');
+ }, 'validationMessage should return empty string when willValidate is false and valueMissing is true');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html
new file mode 100644
index 0000000000..c586c6debe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.validity.valueMissing</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-validitystate-valuemissing">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<form>
+ <input id="messagetest" type="checkbox" required="" disabled="">
+</form>
+
+<script>
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "url", "email", "password"],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "abc"}, expected: false, name: "[target] The value is not empty and required is true"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value is empty and required is true"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["datetime-local"],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "2000-12-10T12:00:00"}, expected: false, name: "[target] Valid local date and time string(2000-12-10T12:00:00)"},
+ {conditions: {required: true, value: "2000-12-10 12:00"}, expected: false, name: "[target] Valid local date and time string(2000-12-10 12:00)"},
+ {conditions: {required: true, value: "1979-10-14T12:00:00.001"}, expected: false, name: "[target] Valid local date and time string(1979-10-14T12:00:00.001)"},
+ {conditions: {required: true, value: 1234567}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a number(1234567)"},
+ {conditions: {required: true, value: new Date()}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a Date object"},
+ {conditions: {required: true, value: "1979-10-99 99:99"}, expected: true, expectedImmutable: false, name: "[target] Invalid local date and time string(1979-10-99 99:99)"},
+ {conditions: {required: true, value: "1979-10-14 12:00:00"}, expected: false, name: "[target] Valid local date and time string(1979-10-14 12:00:00)"},
+ {conditions: {required: true, value: "2001-12-21 12:00"}, expected: true, expectedImmutable: false, name: "[target] Invalid local date and time string(2001-12-21 12:00)-two white space"},
+ {conditions: {required: true, value: "abc"}, expected: true, expectedImmutable: false, name: "[target] the value attribute is a string(abc)"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value attribute is empty string"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["date"],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "2000-12-10"}, expected: false, name: "[target] Valid date string(2000-12-10)"},
+ {conditions: {required: true, value: "9999-01-01"}, expected: false, name: "[target] Valid date string(9999-01-01)"},
+ {conditions: {required: true, value: 1234567}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a number(1234567)"},
+ {conditions: {required: true, value: new Date()}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a Date object"},
+ {conditions: {required: true, value: "9999-99-99"}, expected: true, expectedImmutable: false, name: "[target] Invalid date string(9999-99-99)"},
+ {conditions: {required: true, value: "37/01/01"}, expected: true, expectedImmutable: false, name: "[target] Invalid date string(37-01-01)"},
+ {conditions: {required: true, value: "2000/01/01"}, expected: true, expectedImmutable: false, name: "[target] Invalid date string(2000/01/01)"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value attribute is empty string"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["time"],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "12:00:00"}, expected: false, name: "[target] Validtime string(12:00:00)"},
+ {conditions: {required: true, value: "12:00"}, expected: false, name: "[target] Validtime string(12:00)"},
+ {conditions: {required: true, value: "12:00:00.001"}, expected: false, name: "[target] Valid time string(12:00:60.001)"},
+ {conditions: {required: true, value: "12:00:00.01"}, expected: false, name: "[target] Valid time string(12:00:60.01)"},
+ {conditions: {required: true, value: "12:00:00.1"}, expected: false, name: "[target] Valid time string(12:00:60.1)"},
+ {conditions: {required: true, value: 1234567}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a number(1234567)"},
+ {conditions: {required: true, value: new Date()}, expected: true, expectedImmutable: false, name: "[target] The value attribute is a time object"},
+ {conditions: {required: true, value: "25:00:00"}, expected: true, expectedImmutable: false, name: "[target] Invalid time string(25:00:00)"},
+ {conditions: {required: true, value: "12:60:00"}, expected: true, expectedImmutable: false, name: "[target] Invalid time string(12:60:00)"},
+ {conditions: {required: true, value: "12:00:60"}, expected: true, expectedImmutable: false, name: "[target] Invalid time string(12:00:60)"},
+ {conditions: {required: true, value: "12:00:00:001"}, expected: true, expectedImmutable: false, name: "[target] Invalid time string(12:00:00:001)"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value attribute is empty string"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["number"],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "123"}, expected: false, name: "[target] Value is an integer with a leading symbol '+'"},
+ {conditions: {required: true, value: "-123.45"}, expected: false, name: "[target] Value is a number with a '-' symbol"},
+ {conditions: {required: true, value: "123.01e-10"}, expected: false, name: "[target] Value is a number in scientific notation form(e is in lowercase)"},
+ {conditions: {required: true, value: "123.01E+10"}, expected: false, name: "[target] Value is a number in scientific notation form(E is in uppercase)"},
+ {conditions: {required: true, value: "-0"}, expected: false, name: "[target] Value is -0"},
+ {conditions: {required: true, value: " 123 "}, expected: true, expectedImmutable: false, name: "[target] Value is a number with some white spaces"},
+ {conditions: {required: true, value: Math.pow(2, 1024)}, expected: true, expectedImmutable: false, name: "[target] Value is Math.pow(2, 1024)"},
+ {conditions: {required: true, value: Math.pow(-2, 1024)}, expected: true, expectedImmutable: false, name: "[target] Value is Math.pow(-2, 1024)"},
+ {conditions: {required: true, value: "abc"}, expected: true, expectedImmutable: false, name: "[target] Value is a string that cannot be converted to a number"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value attribute is empty string"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["checkbox"],
+ testData: [
+ {conditions: {required: false, checked: false, name: "test1"}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, checked: true, name: "test2"}, expected: false, name: "[target] The checked attribute is true"},
+ {conditions: {required: true, checked: false, name: "test3"}, expected: true, name: "[target] The checked attribute is false"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["radio"],
+ testData: [
+ {conditions: {required: false, checked: false, name: "test4"}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, checked: true, name: "test5"}, expected: false, name: "[target] The checked attribute is true"},
+ {conditions: {required: true, checked: false, name: "test6"}, expected: true, name: "[target] The checked attribute is false"},
+ {conditions: {required: true, checked: false, name: ""}, expected: false, name: "[target] The checked attribute is false and the name attribute is empty"}
+ ]
+ },
+ {
+ tag: "input",
+ types: ["file"],
+ testData: [
+ {conditions: {required: false, files: null}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, files: null}, expected: true, name: "[target] The Files attribute is null"}
+ //ToDo: Add a case to test the files is not null
+ ]
+ },
+ {
+ tag: "select",
+ types: [],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: 1}, expected: false, name: "[target] Selected the option with value equals to 1"},
+ {conditions: {required: true, value: ""}, expected: true, name: "[target] Selected the option with value equals to empty"}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [
+ {conditions: {required: false, value: ""}, expected: false, name: "[target] The required attribute is not set"},
+ {conditions: {required: true, value: "abc"}, expected: false, name: "[target] The value is not empty"},
+ {conditions: {required: true, value: ""}, expected: true, expectedImmutable: false, name: "[target] The value is empty"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "valueMissing");
+
+ test(() => {
+ assert_equals(document.getElementById("messagetest").validationMessage, '');
+ }, 'validationMessage should return empty string when willValidate is false and valueMissing is true');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate-datalist.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate-datalist.html
new file mode 100644
index 0000000000..b6b14c6304
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate-datalist.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.willValidate</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-cva-willvalidate">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<datalist></datalist>
+<script>
+ var dl = document.querySelector("datalist");
+ function runTest(element, name) {
+ test(function () {
+ assert_true(element.willValidate, "The willValidate attribute should be true initially.");
+
+ dl.appendChild(element);
+ assert_false(element.willValidate, "The willValidate attribute should be false if element has datalist parent.");
+
+ element.remove();
+ assert_true(element.willValidate, "The willValidate attribute should be true if element has no parent.");
+
+ let div = document.createElement("div");
+ div.appendChild(element);
+ let foo = document.createElementNS('some-random-namespace', 'foo');
+ foo.appendChild(div);
+ dl.appendChild(foo);
+ assert_false(element.willValidate, "The willValidate attribute should be false if element has datalist ancestor.");
+
+ foo.remove();
+ assert_true(element.willValidate, "The willValidate attribute should be true if element has no datalist ancestor.");
+ }, name);
+ }
+
+ var testElements = [
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "url", "email", "password", "datetime-local", "date", "month", "week", "time", "color", "file", "submit"]
+ },
+ {
+ tag: "button",
+ types: ["submit"],
+ },
+ {
+ tag: "select",
+ types: [],
+ },
+ {
+ tag: "textarea",
+ types: [],
+ }
+ ].forEach(function(testData) {
+ if (testData.types.length > 0) {
+ testData.types.forEach(function(type) {
+ let ele = document.createElement(testData.tag);
+ try {
+ ele.type = type;
+ } catch (e) {
+ //Do nothing, avoid the runtime error breaking the test
+ }
+ runTest(ele, `Test ${testData.tag} element with ${type} type`);
+ });
+ } else {
+ runTest(document.createElement(testData.tag), `Test ${testData.tag} element`);
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html
new file mode 100644
index 0000000000..c1abf76b65
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The constraint validation API Test: element.willValidate</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-cva-willvalidate">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-constraint-validation-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/validator.js"></script>
+<div id="log"></div>
+<script>
+ var testElements = [
+ //input in hidden, button and reset status must be barred from the constraint validation
+ {
+ tag: "input",
+ types: ["hidden", "button", "reset"],
+ testData: [{conditions: {}, expected: false, name: "[target] Must be barred from the constraint validation"}]
+ },
+ //button in button and reset status must be barred from the constraint validation
+ {
+ tag: "button",
+ types: ["button", "reset"],
+ testData: [{conditions: {}, expected: false, name: "[target] Must be barred from the constraint validation"}]
+ },
+ // FIELDSET and OUTPUT elements are not "submittable elements" and therefore never validate.
+ {
+ tag: "fieldset",
+ types: [],
+ testData: [{conditions: {}, expected: false, name: "[target] The willValidate attribute must be false since FIELDSET is not a submittable element"}]
+ },
+ {
+ tag: "output",
+ types: [],
+ testData: [{conditions: {}, expected: false, name: "[target] The willValidate attribute must be false since OUTPUT is not a submittable element"}]
+ },
+ //OBJECT, KEYGEN, elements must be barred from the constraint validation
+ {
+ tag: "object",
+ types: [],
+ testData: [{conditions: {}, expected: false, name: "[target] Must be barred from the constraint validation"}]
+ },
+ //If an element is disabled, it is barred from constraint validation.
+ //The willValidate attribute must be true if an element is mutable
+ //If the readonly attribute is specified on an INPUT element, the element is barred from constraint validation.
+ {
+ tag: "input",
+ types: ["text", "search", "tel", "url", "email", "password", "datetime-local", "date", "month", "week", "time"],
+ testData: [
+ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is disabled"},
+ {conditions: {disabled: false, readOnly: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"},
+ {conditions: {readOnly: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is readonly"},
+ {conditions: {disabled: false, readOnly: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"},
+ ]
+ },
+ //In the following cases, the readonly attribute does not apply, however we should still bar the element from constraint validation.
+ {
+ tag: "input",
+ types: ["color", "file", "submit"],
+ testData: [
+ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is disabled"},
+ {conditions: {disabled: false, readOnly: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"},
+ {conditions: {readOnly: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is readonly"},
+ {conditions: {disabled: false, readOnly: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"},
+ ]
+ },
+ {
+ tag: "button",
+ types: ["submit"],
+ testData: [
+ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation"},
+ {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"},
+ {conditions: {disabled: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"}
+ ]
+ },
+ {
+ tag: "select",
+ types: [],
+ testData: [
+ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation"},
+ {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"},
+ {conditions: {disabled: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"}
+ ]
+ },
+ {
+ tag: "textarea",
+ types: [],
+ testData: [,
+ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation"},
+ {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"},
+ {conditions: {disabled: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"}
+ ]
+ }
+ ];
+
+ validator.run_test(testElements, "willValidate");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/infinite_backtracking.tentative.html b/testing/web-platform/tests/html/semantics/forms/constraints/infinite_backtracking.tentative.html
new file mode 100644
index 0000000000..52f6e316b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/infinite_backtracking.tentative.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The infinite pattern validation test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input type=text id=badinput value="12345678901234567890123456789123456789z" pattern="(\d+)*$">
+<script>
+ test(function(){
+ var elements = document.querySelectorAll(":invalid");
+ assert_array_equals(elements, [document.getElementById('badinput')]);
+ }, "Infinite backtracking pattern terminates");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/input-maxlength-emoji.html b/testing/web-platform/tests/html/semantics/forms/constraints/input-maxlength-emoji.html
new file mode 100644
index 0000000000..068abad936
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/input-maxlength-emoji.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests pasting an emoji in a text field with a maxlength attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=252900">
+<input id="target" type="text" maxlength="10" />
+<script>
+test(function() {
+ let target = document.getElementById("target");
+ target.focus();
+ document.execCommand("InsertHTML", false, "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦");
+ assert_equals(target.value, "👨â€ðŸ‘©â€ðŸ‘§â€");
+}, "Emoji gets truncated due to maxlength attribute");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/input-number-validity-dynamic-value-no-change.html b/testing/web-platform/tests/html/semantics/forms/constraints/input-number-validity-dynamic-value-no-change.html
new file mode 100644
index 0000000000..2100fb7fb6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/input-number-validity-dynamic-value-no-change.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Number input step dynamic value attribute change</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1621273">
+<input type="number" value="9999" step="1">
+<script>
+test(function() {
+ let input = document.querySelector("input");
+ input.value = "113.90";
+ assert_true(input.matches(":invalid"), "Input should be invalid because step base is @value");
+ assert_false(input.validity.valid, "Input should be invalid because step base is @value");
+ input.setAttribute("value", "113.90");
+ assert_true(input.matches(":valid"), "Input should be valid because step base is @value");
+ assert_true(input.validity.valid, "Input should be valid because step base is @value");
+}, "number input number validation is updated correctly after value attribute change which doesn't change input value");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/input-pattern-dynamic-value.html b/testing/web-platform/tests/html/semantics/forms/constraints/input-pattern-dynamic-value.html
new file mode 100644
index 0000000000..58e566c738
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/input-pattern-dynamic-value.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Pattern dynamic value attribute change</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1636495">
+<input pattern="a" value="a">
+<script>
+test(function() {
+ let i = document.querySelector("input");
+ assert_false(i.matches(":invalid"));
+ i.pattern = "b";
+ assert_true(i.matches(":invalid"));
+ i.pattern = "(";
+ assert_false(i.matches(":invalid"));
+}, "input validation is updated after pattern attribute change");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/inputwillvalidate.html b/testing/web-platform/tests/html/semantics/forms/constraints/inputwillvalidate.html
new file mode 100644
index 0000000000..909fd889bb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/inputwillvalidate.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+ <title>willValidate property on the input element</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <meta content="willValidate property on the input element" name="description">
+ <link href="https://html.spec.whatwg.org/multipage/#dom-cva-willvalidate" rel="help">
+ </head>
+ <body>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <div id="log"></div>
+
+ <form action="http://www.example.com/" style="display : none">
+ <input required="" type="text">
+ <input disabled="" type="text">
+ </form>
+
+ <script type="text/javascript">
+
+ test(function() {assert_true(document.getElementsByTagName("input")[0].willValidate)}, "willValidate property returns true when required attribute exists");
+ test(function() {assert_false(document.getElementsByTagName("input")[1].willValidate)}, "willValidate property returns false when disabled attribute exists");
+
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/number-input-lang-validationMessage-crash.html b/testing/web-platform/tests/html/semantics/forms/constraints/number-input-lang-validationMessage-crash.html
new file mode 100644
index 0000000000..7affb1d65b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/number-input-lang-validationMessage-crash.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>Should not assert or crash</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1649569">
+<script>
+window.onload = () => {
+ var a = document.getElementById("a")
+ var c = document.createElement("m")
+ a.valueAsNumber = 0.165
+ c.insertBefore(b, c.childNodes[0])
+ a.validationMessage
+}
+</script>
+<pre lang="nb">
+<form id="b">
+<input id="a" type="number">
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/radio-valueMissing.html b/testing/web-platform/tests/html/semantics/forms/constraints/radio-valueMissing.html
new file mode 100644
index 0000000000..a02b6b9645
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/radio-valueMissing.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>valueMissing property on the input[type=radio] element</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <meta content="valueMissing property on the input[type=radio] element" name="description">
+ <link href="https://html.spec.whatwg.org/multipage/#dom-validitystate-valuemissing" rel="help">
+ <link href="https://html.spec.whatwg.org/multipage/#radio-button-state-(type%3Dradio)%3Asuffering-from-being-missing" rel="help">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+
+ <form style="display: none">
+ <input type="radio" name="group1" id="radio1">
+ <input type="radio" name="group1" id="radio2">
+
+ <input type="radio" name="group2" required id="radio3">
+ <input type="radio" name="group2" id="radio4">
+
+ <input type="radio" name="group3" required checked id="radio5">
+ <input type="radio" name="group3" id="radio6">
+
+ <input type="radio" name="group4" required id="radio7">
+ <input type="radio" name="group4" checked id="radio8">
+ <input type="radio" name="group4" id="radio9">
+
+ <input type="radio" name="group5" required disabled id="radio10">
+ <input type="radio" name="group5" id="radio11">
+
+ <input type="radio" name="group6" required checked disabled id="radio12">
+ <input type="radio" name="group6" id="radio13">
+ </form>
+
+ <script type="text/javascript">
+ var cases = [
+ {
+ name: "The required attribute is not set",
+ group: ["radio1", "radio2"],
+ expected: false
+ },
+ {
+ name: "One of the radios is required, but none checked",
+ group: ["radio3", "radio4"],
+ expected: true
+ },
+ {
+ name: "One of the radios is required and checked",
+ group: ["radio5", "radio6"],
+ expected: false
+ },
+ {
+ name: "One of the radios is required and another one is checked",
+ group: ["radio7", "radio8", "radio9"],
+ expected: false
+ },
+ {
+ name: "One of the radios is required and disabled, but none checked",
+ group: ["radio10", "radio11"],
+ expected: true
+ },
+ {
+ name: "One of the radios is required, checked and disabled",
+ group: ["radio12", "radio13"],
+ expected: false
+ }
+ ];
+
+ for (var info of cases) {
+ test(function () {
+ for (var id of info.group) {
+ var radio = document.getElementById(id);
+
+ assert_class_string(radio.validity, "ValidityState",
+ "HTMLInputElement.validity must be a ValidityState instance");
+
+ if (info.expected) {
+ assert_true(radio.validity.valueMissing,
+ "The " + id + ".validity.valueMissing should be true");
+ } else {
+ assert_false(radio.validity.valueMissing,
+ "The " + id + ".validity.valueMissing should be false");
+ }
+ }
+ }, info.name);
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/reportValidity-crash.html b/testing/web-platform/tests/html/semantics/forms/constraints/reportValidity-crash.html
new file mode 100644
index 0000000000..d6bab924ad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/reportValidity-crash.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<script>
+Object.prototype.__defineGetter__('then', prom);
+var prom_count = 0;
+function prom() {
+prom_count++;
+if (prom_count > 2) return;
+var v14 = x37.animate({},100);
+v14.reverse();
+v14.ready;
+v14.currentTime = 0;
+x57.reportValidity();
+}
+function f0() {
+var v38 = x37.animate({},300);
+v38.ready;
+x57.prepend(x78);
+}
+function f1() {
+var x57 = document.getElementById("x57");
+x57.disabled = false;
+}
+</script>
+</head>
+
+<body>
+<fieldset id="x37">
+<canvas onfocusin="f0()" >
+<input id="x78" autofocus="" onfocusout="f1()" >
+</canvas>
+<select id="x57" disabled="" required=""></select>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js b/testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js
new file mode 100644
index 0000000000..aa43b3a2f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js
@@ -0,0 +1,481 @@
+var validator = {
+
+ test_tooLong: function(ctl, data) {
+ var self = this;
+ test(function() {
+ self.pre_check(ctl, 'tooLong');
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.tooLong,
+ 'The validity.tooLong should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.tooLong,
+ 'The validity.tooLong should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_tooShort: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "tooShort");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.tooShort,
+ 'The validity.tooShort should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.tooShort,
+ 'The validity.tooShort should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_patternMismatch: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "patternMismatch");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.patternMismatch,
+ 'The validity.patternMismatch should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.patternMismatch,
+ 'The validity.patternMismatch should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_valueMissing: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "valueMissing");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.valueMissing,
+ 'The validity.valueMissing should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.valueMissing,
+ 'The validity.valueMissing should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_typeMismatch: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "typeMismatch");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.typeMismatch,
+ 'The validity.typeMismatch should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.typeMismatch,
+ 'The validity.typeMismatch should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_rangeOverflow: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "rangeOverflow");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.rangeOverflow,
+ 'The validity.rangeOverflow should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.rangeOverflow,
+ 'The validity.rangeOverflow should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_rangeUnderflow: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "rangeUnderflow");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.rangeUnderflow,
+ 'The validity.rangeUnderflow should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.rangeUnderflow,
+ 'The validity.rangeUnderflow should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_stepMismatch: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "stepMismatch");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.stepMismatch,
+ 'The validity.stepMismatch should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.stepMismatch,
+ 'The validity.stepMismatch should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_badInput: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "badInput");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.badInput,
+ 'The validity.badInput should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.badInput,
+ 'The validity.badInput should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_customError: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "customError");
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected) {
+ assert_true(
+ ctl.validity.customError,
+ 'The validity.customError attribute should be true' + condStr);
+ // validationMessage returns the empty string if ctl is barred from
+ // constraint validation, which happens if ctl is disabled or readOnly.
+ if (ctl.disabled || ctl.readOnly) {
+ assert_equals(
+ ctl.validationMessage, '',
+ 'The validationMessage attribute must be empty' + condStr);
+ } else {
+ assert_equals(
+ ctl.validationMessage, data.conditions.message,
+ 'The validationMessage attribute should be \'' +
+ data.conditions.message + '\'' + condStr);
+ }
+ } else {
+ assert_false(
+ ctl.validity.customError,
+ 'The validity.customError attribute should be false' + condStr);
+ assert_equals(
+ ctl.validationMessage, '',
+ 'The validationMessage attribute must be empty' + condStr);
+ }
+ });
+ }, data.name);
+ },
+
+ test_isValid: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.iterate_over(ctl, data).forEach(function(val) {
+ const {ctl, expected, condStr} = val;
+ if (expected)
+ assert_true(
+ ctl.validity.valid,
+ 'The validity.valid should be true' + condStr);
+ else
+ assert_false(
+ ctl.validity.valid,
+ 'The validity.valid should be false' + condStr);
+ });
+ }, data.name);
+ },
+
+ test_willValidate: function(ctl, data) {
+ var self = this;
+ test(function () {
+ self.pre_check(ctl, "willValidate");
+ self.set_conditions(ctl, data.conditions);
+ if (data.ancestor) {
+ var dl = document.createElement("datalist");
+ dl.appendChild(ctl);
+ }
+
+ if (data.expected)
+ assert_true(ctl.willValidate, "The willValidate attribute should be true.");
+ else
+ assert_false(ctl.willValidate, "The willValidate attribute should be false.");
+ }, data.name);
+ },
+
+ test_checkValidity: function(ctl, data) {
+ var self = this;
+ test(function () {
+ var eventFired = false;
+ self.pre_check(ctl, "checkValidity");
+ self.set_conditions(ctl, data.conditions);
+ if (data.dirty)
+ self.set_dirty(ctl);
+
+ on_event(ctl, "invalid", function(e){
+ assert_equals(e.type, "invalid", "The invalid event should be fired.");
+ eventFired = true;
+ });
+
+ if (data.expected) {
+ assert_true(ctl.checkValidity(), "The checkValidity method should be true.");
+ assert_false(eventFired, "The invalid event should not be fired.");
+ } else {
+ assert_false(ctl.checkValidity(), "The checkValidity method should be false.");
+ assert_true(eventFired, "The invalid event should be fired.");
+ }
+ }, data.name);
+
+ test(function () {
+ var fm = document.createElement("form");
+ var ctl2 = ctl.cloneNode(true);
+
+ self.pre_check(ctl, "checkValidity");
+ self.set_conditions(ctl2, data.conditions);
+ fm.appendChild(ctl2);
+ document.body.appendChild(fm);
+ if (data.dirty)
+ self.set_dirty(ctl2);
+
+ var result = fm.checkValidity();
+ document.body.removeChild(fm);
+
+ if (data.expected)
+ assert_true(result, "The checkValidity method of the element's form owner should return true.");
+ else
+ assert_false(result, "The checkValidity method of the element's form owner should return false.");
+ }, data.name + " (in a form)");
+ },
+
+ test_reportValidity: function(ctl, data) {
+ var self = this;
+ test(function () {
+ var eventFired = false;
+
+ self.pre_check(ctl, "reportValidity");
+ self.set_conditions(ctl, data.conditions);
+ if (data.dirty)
+ self.set_dirty(ctl);
+
+ on_event(ctl, "invalid", function(e){
+ assert_equals(e.type, "invalid", "The invalid event should be fired.");
+ eventFired = true;
+ });
+
+ if (data.expected) {
+ assert_true(ctl.reportValidity(), "The reportValidity method should be true.");
+ assert_false(eventFired, "The invalid event should not be fired.");
+ } else {
+ assert_false(ctl.reportValidity(), "The reportValidity method should be false.");
+ assert_true(eventFired, "The invalid event should be fired.");
+ }
+ }, data.name);
+
+ test(function () {
+ var fm = document.createElement("form");
+ var ctl2 = ctl.cloneNode(true);
+
+ self.pre_check(ctl, "reportValidity");
+ self.set_conditions(ctl2, data.conditions);
+ fm.appendChild(ctl2);
+ document.body.appendChild(fm);
+ if (data.dirty)
+ self.set_dirty(ctl2);
+
+ var result = fm.reportValidity();
+ document.body.removeChild(fm);
+
+ if (data.expected)
+ assert_true(result, "The reportValidity method of the element's form owner should return true.");
+ else
+ assert_false(result, "The reportValidity method of the element's form owner should return false.");
+ }, data.name + " (in a form)");
+ },
+
+ set_conditions: function(ctl, obj) {
+ [
+ "checked",
+ "disabled",
+ "max",
+ "maxlength",
+ "min",
+ "minlength",
+ "multiple",
+ "pattern",
+ "readonly",
+ "required",
+ "selected",
+ "step",
+ "value"
+ ].forEach(function(item) {
+ ctl.removeAttribute(item);
+ });
+ for (var attr in obj) {
+ if (attr === "message")
+ ctl.setCustomValidity(obj[attr]);
+ else if (attr === "checked" || obj[attr] || obj[attr] === "")
+ ctl[attr] = obj[attr];
+ }
+ },
+
+ set_dirty: function(ctl) {
+ ctl.focus();
+ var old_value = ctl.value;
+ ctl.value = "a";
+ ctl.value = old_value;
+ },
+
+ pre_check: function(ctl, item) {
+ switch (item) {
+ case "willValidate":
+ assert_true(item in ctl, "The " + item + " attribute doesn't exist.");
+ break;
+ case "checkValidity":
+ case "reportValidity":
+ assert_true(item in ctl, "The " + item + " method doesn't exist.");
+ break;
+ case "tooLong":
+ case "tooShort":
+ case "patternMismatch":
+ case "typeMismatch":
+ case "stepMismatch":
+ case "rangeOverflow":
+ case "rangeUnderflow":
+ case "valueMissing":
+ case "badInput":
+ case "valid":
+ assert_true("validity" in ctl, "The validity attribute doesn't exist.");
+ assert_true(item in ctl.validity, "The " + item + " attribute doesn't exist.");
+ break;
+ case "customError":
+ assert_true("validity" in ctl, "The validity attribute doesn't exist.");
+ assert_true("setCustomValidity" in ctl, "The validity attribute doesn't exist.");
+ assert_true("validationMessage" in ctl, "The validity attribute doesn't exist.");
+ assert_true(item in ctl.validity, "The " + item + " attribute doesn't exist.");
+ break;
+ }
+ },
+
+ iterate_over: function(ctl, data) {
+ // Iterate over normal, disabled, readonly, and both (if applicable).
+ var ctlNormal = ctl.cloneNode(true);
+ this.set_conditions(ctlNormal, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlNormal);
+
+ var ctlDisabled = ctl.cloneNode(true);
+ this.set_conditions(ctlDisabled, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlDisabled);
+ ctlDisabled.disabled = true;
+
+ var expectedImmutable =
+ data.expectedImmutable !== undefined ? data.expectedImmutable : data.expected;
+
+ var variants = [
+ {ctl: ctlNormal, expected: data.expected, condStr: '.'},
+ {ctl: ctlDisabled, expected: expectedImmutable, condStr: ', when control is disabled.'},
+ ];
+
+ if ('readOnly' in ctl) {
+ var ctlReadonly = ctl.cloneNode(true);
+ this.set_conditions(ctlReadonly, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlReadonly);
+ ctlReadonly.readOnly = true;
+
+ var ctlBoth = ctl.cloneNode(true);
+ this.set_conditions(ctlBoth, data.conditions);
+ if (data.dirty)
+ this.set_dirty(ctlBoth);
+ ctlBoth.disabled = true;
+ ctlBoth.readOnly = true;
+
+ variants.push({
+ ctl: ctlReadonly,
+ expected: expectedImmutable,
+ condStr: ', when control is readonly.'
+ });
+
+ variants.push({
+ ctl: ctlBoth,
+ expected: expectedImmutable,
+ condStr: ', when control is disabled & readonly.'
+ });
+ }
+
+ return variants;
+ },
+
+ run_test: function(testee, method) {
+ var testMethod = "test_" + method;
+ if (typeof this[testMethod] !== "function") {
+ return false;
+ }
+
+ var ele = null,
+ prefix = "";
+
+ for (var i = 0; i < testee.length; i++) {
+ if (testee[i].types.length > 0) {
+ for (var typ in testee[i].types) {
+ ele = document.createElement(testee[i].tag);
+ document.body.appendChild(ele);
+ try {
+ ele.type = testee[i].types[typ];
+ } catch (e) {
+ //Do nothing, avoid the runtime error breaking the test
+ }
+
+ prefix = "[" + testee[i].tag.toUpperCase() + " in " + testee[i].types[typ].toUpperCase() + " status] ";
+
+ for (var j = 0; j < testee[i].testData.length; j++) {
+ testee[i].testData[j].name = testee[i].testData[j].name.replace(/\[.*\]\s/g, prefix);
+ this[testMethod](ele, testee[i].testData[j]);
+ }
+ }
+ } else {
+ ele = document.createElement(testee[i].tag);
+ document.body.appendChild(ele);
+ prefix = "[" + testee[i].tag + "] ";
+
+ if (testElements[i].tag === "select") {
+ ele.add(new Option('test1', '')); // Placeholder
+ ele.add(new Option("test2", 1));
+ }
+
+ for (var item in testee[i].testData) {
+ testee[i].testData[item].name = testee[i].testData[item].name.replace("[target]", prefix);
+ this[testMethod](ele, testee[i].testData[item]);
+ }
+ }
+ }
+ }
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html
new file mode 100644
index 0000000000..008089f39a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="email"], ValidityState.tooLong and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#limiting-user-input-length:-the-maxlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still exceeds the input's maxlength should suffer from being too long.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Delete one character from the following text input:</p>
+ <input type="email" value="jane.doe@example.com" maxlength="5" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooLong, "tooLong must be true since the user just changed the input's value and the value exceeds the maxlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html
new file mode 100644
index 0000000000..353d9466dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="password"], ValidityState.tooLong and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#limiting-user-input-length:-the-maxlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still exceeds the input's maxlength should suffer from being too long.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Delete one character from the following text input:</p>
+ <input type="password" value="swordfish" maxlength="5" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooLong, "tooLong must be true since the user just changed the input's value and the value exceeds the maxlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html
new file mode 100644
index 0000000000..73be3b6d83
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="search"], ValidityState.tooLong and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#limiting-user-input-length:-the-maxlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still exceeds the input's maxlength should suffer from being too long.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Delete one character from the following text input:</p>
+ <input type="search" value="abcdefghi" maxlength="5" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooLong, "tooLong must be true since the user just changed the input's value and the value exceeds the maxlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html
new file mode 100644
index 0000000000..bf7682af3e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="tel"], ValidityState.tooLong and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#limiting-user-input-length:-the-maxlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still exceeds the input's maxlength should suffer from being too long.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Delete one character from the following text input:</p>
+ <input type="tel" value="123-456-7890" maxlength="7" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooLong, "tooLong must be true since the user just changed the input's value and the value exceeds the maxlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html
new file mode 100644
index 0000000000..2eea2b7245
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="text"], ValidityState.tooLong and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#limiting-user-input-length:-the-maxlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still exceeds the input's maxlength should suffer from being too long.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Delete one character from the following text input:</p>
+ <input type="text" value="0123456789" maxlength="5" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooLong, "tooLong must be true since the user just changed the input's value and the value exceeds the maxlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html
new file mode 100644
index 0000000000..17039a71a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="url"], ValidityState.tooLong and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#limiting-user-input-length:-the-maxlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still exceeds the input's maxlength should suffer from being too long.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Delete one character from the following text input:</p>
+ <input type="url" value="http://example.com/foo" maxlength="12" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooLong, "tooLong must be true since the user just changed the input's value and the value exceeds the maxlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html
new file mode 100644
index 0000000000..2212a1ca90
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>textarea, ValidityState.tooLong and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#limiting-user-input-length:-the-maxlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, a textarea whose value was edited by the user but still exceeds the textarea's maxlength should suffer from being too long.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Delete one character from the following text area:</p>
+ <textarea maxlength="5" autocomplete="off" id="testinput">0123456789</textarea>
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLTextAreaElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooLong, "tooLong must be true since the user just changed the input's value and the value exceeds the maxlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-email-add-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-email-add-manual.html
new file mode 100644
index 0000000000..366f028e77
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-email-add-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="email"], ValidityState.tooShort and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#setting-minimum-input-length-requirements:-the-minlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still falls below the input's minlength should suffer from being too short.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Type one additional character into the following text input:</p>
+ <input type="email" value="jane.doe@example.com" minlength="25" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooShort, "tooShort must be true since the user just changed the input's value and the value falls below the minlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-password-add-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-password-add-manual.html
new file mode 100644
index 0000000000..cdfd05152b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-password-add-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="password"], ValidityState.tooShort and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#setting-minimum-input-length-requirements:-the-minlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still falls below the input's minlength should suffer from being too short.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Type one additional character into the following text input:</p>
+ <input type="password" value="swordfish" minlength="15" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooShort, "tooShort must be true since the user just changed the input's value and the value falls below the minlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-search-add-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-search-add-manual.html
new file mode 100644
index 0000000000..86af374c37
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-search-add-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="search"], ValidityState.tooShort and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#setting-minimum-input-length-requirements:-the-minlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still falls below the input's minlength should suffer from being too short.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Type one additional character into the following text input:</p>
+ <input type="search" value="abcdefghi" minlength="15" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooShort, "tooShort must be true since the user just changed the input's value and the value falls below the minlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-tel-add-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-tel-add-manual.html
new file mode 100644
index 0000000000..08573f892a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-tel-add-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="tel"], ValidityState.tooShort and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#setting-minimum-input-length-requirements:-the-minlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still falls below the input's minlength should suffer from being too short.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Type one additional character into the following text input:</p>
+ <input type="tel" value="123-456-7890" minlength="20" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooShort, "tooShort must be true since the user just changed the input's value and the value falls below the minlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-text-add-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-text-add-manual.html
new file mode 100644
index 0000000000..129ed93533
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-text-add-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="text"], ValidityState.tooShort and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#setting-minimum-input-length-requirements:-the-minlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still falls below the input's minlength should suffer from being too short.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Type one additional character into the following text input:</p>
+ <input type="text" value="1234" minlength="10" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooShort, "tooShort must be true since the user just changed the input's value and the value falls below the minlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-url-add-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-url-add-manual.html
new file mode 100644
index 0000000000..418c75b8f3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-input-url-add-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>input[type="url"], ValidityState.tooShort and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#setting-minimum-input-length-requirements:-the-minlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, an input whose value was edited by the user but still falls below the input's minlength should suffer from being too short.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Type one additional character into the following text input:</p>
+ <input type="url" value="http://example.com" minlength="25" autocomplete="off" id="testinput">
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLInputElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooShort, "tooShort must be true since the user just changed the input's value and the value falls below the minlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-textarea-add-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-textarea-add-manual.html
new file mode 100644
index 0000000000..73005977df
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooShort-textarea-add-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>textarea, ValidityState.tooShort and user editing</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#setting-minimum-input-length-requirements:-the-minlength-attribute">
+ <meta name="flags" content="interact">
+ <meta name="assert" content="Per the 'Constraint validation' definition in the referenced section, a textarea whose value was edited by the user but still falls below the textarea's minlength should suffer from being too short.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>Type one additional character into the following text area:</p>
+ <textarea minlength="15" autocomplete="off" id="testinput">123456789</textarea>
+
+ <div id="log"></div>
+ <script>
+var input = document.getElementById('testinput');
+setup({explicit_timeout: true, explicit_done: true});
+on_event(input, "input", function () {
+ test(function() {
+ assert_class_string(input.validity, 'ValidityState', 'HTMLTextAreaElement.validity must be a ValidityState instance');
+ assert_true(input.validity.tooShort, "tooShort must be true since the user just changed the input's value and the value falls below the minlength");
+ });
+ done();
+});
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/association.window.js b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/association.window.js
new file mode 100644
index 0000000000..4d84e7d3f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/association.window.js
@@ -0,0 +1,7 @@
+test(() => {
+ const form = document.createElement("form"),
+ input = document.createElement("input");
+
+ form.appendChild(input);
+ assert_equals(input.form, form);
+}, "Ensure input and form get associated when not in a document");
diff --git a/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form.html b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form.html
new file mode 100644
index 0000000000..518de35863
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLInputElement#form</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form id="form">
+<p><button id="button">button</button>
+<p><fieldset id="fieldset">fieldset</fieldset>
+<p><input id="input">
+<p><object id="object">object</object>
+<p><output id="output">output</output>
+<p><select id="select"><option>select</option></select>
+<p><textarea id="textarea">textarea</textarea>
+
+<!-- label is special: label.form is an alias for label.control.form -->
+<p><label id="label">label</label>
+<p><label id="label-form" form="form">label-form</label>
+<p><label id="label-form-form2" form="form2">label-form-form2</label>
+<p><label id="label-with-control">label-with-control <input></label>
+<p><label id="label-for" for="control-for-label">label-for</label> <input id="control-for-label">
+<p>
+ <input id="input-with-form-attr-in-form" form="form2">
+ <label id="label-for-control-form-in-form" for="input-with-form-attr-in-form">label-for-control-form-in-form</label>
+</p>
+</form>
+<form id="form2"></form>
+<p>
+ <input id="input-with-form-attr" form="form2">
+ <label id="label-for-control-form" for="input-with-form-attr">label-for-control-form</label>
+</p>
+<!-- misnested tags where form-association is set by the HTML parser -->
+<table>
+ <form id="form3"><!-- self-closes but sets the form element pointer -->
+ <tr>
+ <td><label id="label-in-table">label-in-table</label>
+ <td><label id="label-in-table-with-control">label-in-table <input></label>
+ <td><label id="label-in-table-for" for="input-in-table">label-in-table-for</label>
+ <td><input id="input-in-table"><!-- is associated with form3 -->
+ </tr>
+ </form>
+</table>
+<script>
+var form;
+setup(function() {
+ form = document.getElementById("form");
+ form2 = document.getElementById("form2");
+ form3 = document.getElementById("form3");
+ if (!form || !form2 || !form3) {
+ throw new TypeError("Didn't find all forms");
+ }
+});
+
+var listedElements = [
+ "button",
+ "fieldset",
+ "input",
+ "object",
+ "output",
+ "select",
+ "textarea",
+];
+
+listedElements.forEach(function(localName) {
+ test(function() {
+ var control = document.getElementById(localName);
+ assert_equals(control.form, form);
+ }, localName + ".form");
+});
+
+// label
+function testLabel(id, expected) {
+ test(function() {
+ var label = document.getElementById(id);
+ assert_equals(label.control && label.control.form, expected, 'Sanity check: label.control.form');
+ assert_equals(label.form, expected, 'label.form');
+ }, id + ".form");
+}
+
+testLabel("label", null);
+testLabel("label-form", null);
+testLabel("label-form-form2", null);
+testLabel("label-with-control", form);
+testLabel("label-for", form);
+testLabel("label-for-control-form-in-form", form2);
+testLabel("label-for-control-form", form2);
+testLabel("label-in-table", null);
+testLabel("label-in-table-with-control", form3);
+testLabel("label-in-table-for", form3);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_attribute.html b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_attribute.html
new file mode 100644
index 0000000000..dde3250dbf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_attribute.html
@@ -0,0 +1,233 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div>
+ <form id="f1"></form>
+ <form id="f2">
+ <input id="i1" />
+ <input id="i2" form="f1" />
+ <input id="i3" />
+ </form>
+ <script>
+ test(function() {
+ var i1 = document.getElementById("i1");
+ var i2 = document.getElementById("i2");
+ var i3 = document.getElementById("i3");
+ var f1 = document.getElementById("f1");
+ var f2 = document.getElementById("f2");
+
+ assert_equals(i1.form, f2,
+ "i1 must be associated with f2 by the parser");
+ assert_equals(i2.form, f1,
+ "i2 is not associated with f2 by the parser " +
+ "since it has the form attribute set to f1");
+
+ f1.appendChild(i1);
+ i3.setAttribute("form", "f1");
+
+ assert_equals(i1.form, f1,
+ "i1's form owner must be reset when parent changes");
+ assert_equals(i3.form, f1,
+ "i3's form owner must be reset when the form" +
+ "attribute is set");
+
+ assert_equals(i2.form, f1);
+ }, "Tests for parser inserted controls");
+ </script>
+ </div>
+
+ <div id="placeholder">
+ </div>
+
+ <script>
+ var reassociateableElements = [
+ "button",
+ "fieldset",
+ "input",
+ "object",
+ "output",
+ "select",
+ "textarea",
+ ];
+
+ var form1 = null;
+ var form2 = null;
+ var placeholder = document.getElementById("placeholder");
+
+ reassociateableElements.forEach(function(localName) {
+ function testControl(test_, desc) {
+ test(function() {
+ var control = document.createElement(localName);
+
+ while(placeholder.firstChild)
+ placeholder.removeChild(placeholder.firstChild);
+
+ form1 = document.createElement("form");
+ form2 = document.createElement("form");
+ form1.id = "form1";
+ form2.id = "form2";
+ placeholder.appendChild(form1);
+ placeholder.appendChild(form2);
+
+ test_.call(control);
+ }, "[" + localName.toUpperCase() + "] " + desc);
+ }
+
+ testControl(function() {
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+ }, "Basic form association - control with no form attribute is associated with ancestor");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.id = "form-one";
+ assert_equals(this.form, null);
+ }, "Form owner is reset to null when control's form attribute is set to an ID " +
+ "that does not exist in the document");
+
+ testControl(function() {
+ this.setAttribute("form", "");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control whose form attribute is an empty string has no form owner");
+
+ testControl(function() {
+ form1.id = "";
+ this.setAttribute("form", "");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control whose form attribute is an empty string has no form owner " +
+ "even when form with empty attribute is present");
+
+ testControl(function() {
+ form1.id = "FORM1";
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control's form attribute must be a case sensitive match for the form's id");
+
+ testControl(function() {
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ this.setAttribute("form", "form2");
+ assert_equals(this.form, form2);
+ }, "Setting the form attribute of a control to the id of a non-ancestor form works");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ this.removeAttribute("form");
+ assert_equals(this.form, form2);
+ }, "Removing form id from a control resets the form owner to ancestor");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ placeholder.removeChild(form1);
+
+ assert_equals(this.form, null);
+ }, "Removing the form owner of a control with form attribute resets " +
+ "the form owner to null");
+
+ testControl(function() {
+ var form3 = document.createElement("form");
+ form3.id = "form3";
+ placeholder.appendChild(form3);
+ form3.appendChild(this);
+ assert_equals(this.form, form3);
+
+ this.setAttribute("form", "form2");
+ assert_equals(this.form, form2);
+
+ this.setAttribute("form", "form1");
+ assert_equals(this.form, form1);
+ }, "Changing form attibute of control resets form owner to correct form");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ var form3 = document.createElement("form");
+ form3.id = "form3";
+
+ placeholder.appendChild(form3);
+ placeholder.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+ }, "Moving a control with form attribute within the document " +
+ "does not change the form owner");
+
+ testControl(function() {
+ form1.id = "form-one";
+ this.setAttribute("form", "form1");
+ form2.appendChild(this);
+ assert_equals(this.form, null);
+
+ form1.id = "form1";
+ assert_equals(this.form, form1);
+ }, "When the id of a non-ancestor form changes from not being a match for the " +
+ "form attribute to being a match, the control's form owner is reset");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form2.id = "form1";
+ form1.parentNode.insertBefore(form2, form1);
+ assert_equals(this.form, form2);
+
+ form2.parentNode.removeChild(form2);
+ assert_equals(this.form, form1);
+
+ form1.parentNode.appendChild(form2);
+ assert_equals(this.form, form1);
+ }, "When form element with same ID as the control's form attribute is inserted " +
+ "earlier in tree order, the form owner is changed to the inserted form");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ var span = document.createElement("span");
+ span.id = "form1";
+ form1.parentNode.insertBefore(span, form1);
+ assert_equals(this.form, null);
+
+ form1.parentNode.appendChild(span);
+ assert_equals(this.form, form1);
+ }, "When non-form element with same ID as the control's form attribute is " +
+ "inserted earlier in tree order, the control does not have a form owner");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.parentNode.removeChild(form1);
+ assert_equals(this.form, form1);
+ }, "A control that is not in the document but has the form attribute set " +
+ "is associated with the nearest ancestor form if one exists");
+
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table.html b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table.html
new file mode 100644
index 0000000000..1aa75c27b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="root">
+ <form id='form1'></form>
+ <table id='table1'>
+ <form id='form2'>
+ <tr><td><input id='input1'></td></tr>
+ <tr><td><input id='input2' form='form1'></td></tr>
+ </table>
+ <form id="form3">
+ <input id="input3" />
+ </form>
+ </div>
+
+ <script>
+ test(function() {
+ var input1 = document.getElementById('input1');
+ var input2 = document.getElementById('input2');
+ var input3 = document.getElementById('input3');
+ var form1 = document.getElementById('form1');
+ var form2 = document.getElementById('form2');
+ var form3 = document.getElementById('form3');
+
+ var root = document.getElementById('root');
+
+ assert_equals(input1.form, form2,
+ "input1's form owner must be form2 as per the parsing rules");
+ assert_equals(input2.form, form1,
+ "input2's form owner must be the form with id 'form1'");
+ assert_equals(input3.form, form2,
+ "input3's form owner must be form2 as per the parsing rules");
+
+ root.parentNode.removeChild(root);
+
+ assert_equals(input1.form, form2,
+ "input1's form owner must not have changed since they are both in the same subtree");
+ assert_equals(input2.form, null,
+ "input2 must not have a form owner since it has the form attribute set");
+ assert_equals(input3.form, form2,
+ "input3's form owner must not have changed since they are both in the same subtree");
+
+ }, "Form element and form controls nested inside a table are correctly handled");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_2.html b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_2.html
new file mode 100644
index 0000000000..d9aee12b5f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_2.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div>
+ <form id='form1'></form>
+ <table id='table1'>
+ <form id='form2'>
+ <script>
+ var t = document.getElementById('table1');
+ var f = document.getElementById('form2');
+ t.removeChild(f);
+ </script>
+ <tr><td><input id='input1'></td></tr>
+ <tr><td><input id='input2' form='form1'></td></tr>
+ </table>
+ <form id="form3">
+ <input id="input3" />
+ </form>
+ </div>
+
+ <script>
+ test(function() {
+ var form1 = document.getElementById('form1');
+
+ assert_equals(document.getElementById('input1').form, null,
+ "input1's form owner must be null since form2 is not in the" +
+ "same home subtree");
+
+ assert_equals(document.getElementById('input2').form, form1,
+ "input2's form owner must be the form with id 'form1'");
+
+ assert_equals(document.getElementById('input3').form, null,
+ "input3's form owner must be null instead of form2 (as per parsing rules)" +
+ "since form2 is not in the same home subtree");
+
+ }, "Controls nested in tables are not associated with form element inside the " +
+ "table if the form had been removed by script before the controls were " +
+ "inserted by the parser");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_3.html b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_3.html
new file mode 100644
index 0000000000..db70b34b1a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-control-infrastructure/form_owner_and_table_3.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<table><form><tr><td><input></table>
+<div id=2></div>
+<script>
+test(() => {
+ const input = document.querySelector("input"),
+ form = document.querySelector("form");
+ assert_equals(input.form, form);
+ document.getElementById("2").appendChild(form.parentNode);
+ assert_equals(input.form, form);
+ document.getElementById("2").appendChild(input);
+ assert_equals(input.form, null);
+}, "parser inserted flag is not reset by insertions with the owner form, but reset by by removal from the owner form");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/FormDataEvent.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/FormDataEvent.window.js
new file mode 100644
index 0000000000..4890fd8623
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/FormDataEvent.window.js
@@ -0,0 +1,21 @@
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#the-formdataevent-interface
+
+test(() => {
+ let fd = new FormData();
+ assert_throws_js(TypeError, () => { FormDataEvent('', {formData:fd}) }, "Calling FormDataEvent constructor without 'new' must throw");
+ assert_throws_js(TypeError, () => { new FormDataEvent() }, '0 arguments');
+ assert_throws_js(TypeError, () => { new FormDataEvent('foo') }, '1 argument');
+ assert_throws_js(TypeError, () => { new FormDataEvent(fd, fd) }, '2 invalid arguments');
+ assert_throws_js(TypeError, () => { new FormDataEvent('foo', null) }, 'Null dictionary');
+ assert_throws_js(TypeError, () => { new FormDataEvent('foo', undefined) }, 'Undefined dictionary');
+ assert_throws_js(TypeError, () => { new FormDataEvent('foo', { formData: null }) }, 'Null formData');
+ assert_throws_js(TypeError, () => { new FormDataEvent('foo', { formData: undefined }) }, 'Undefined formData');
+ assert_throws_js(TypeError, () => { new FormDataEvent('foo', { formData: 'bar' }) }, 'Wrong type of formData');
+}, 'Failing FormDataEvent constructor');
+
+test(() => {
+ let fd = new FormData();
+ let event = new FormDataEvent('bar', { formData: fd, bubbles: true });
+ assert_equals(event.formData, fd);
+ assert_true(event.bubbles);
+}, 'Successful FormDataEvent constructor');
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/SubmitEvent.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/SubmitEvent.window.js
new file mode 100644
index 0000000000..3821815515
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/SubmitEvent.window.js
@@ -0,0 +1,41 @@
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#the-submitevent-interface
+
+test(() => {
+ assert_throws_js(TypeError, () => SubmitEvent(""), "Calling SubmitEvent constructor without 'new' must throw");
+ assert_throws_js(TypeError, () => { new SubmitEvent() }, '0 arguments');
+ assert_throws_js(TypeError, () => { new SubmitEvent('foo', { submitter: 'bar' }) }, 'Wrong type of submitter');
+}, 'Failing SubmitEvent constructor');
+
+test(() => {
+ let button = document.createElement('button');
+ let event = new SubmitEvent('bar', { submitter: button, bubbles: true });
+ assert_equals(event.submitter, button);
+ assert_true(event.bubbles);
+}, 'Successful SubmitEvent constructor');
+
+test(() => {
+ let event1 = new SubmitEvent('bar', {submitter: null});
+ assert_equals(event1.submitter, null);
+ let event2 = new SubmitEvent('baz', {submitter: undefined});
+ assert_equals(event2.submitter, null);
+}, 'Successful SubmitEvent constructor; null/undefined submitter');
+
+test(() => {
+ let event1 = new SubmitEvent('bar', null);
+ assert_equals(event1.submitter, null);
+ let event2 = new SubmitEvent('baz', undefined);
+ assert_equals(event2.submitter, null);
+}, 'Successful SubmitEvent constructor; null/undefined dictionary');
+
+test(() => {
+ let event1 = new SubmitEvent('bar', {});
+ assert_equals(event1.submitter, null);
+ let button = document.createElement('button');
+ let event2 = new SubmitEvent("bax", button);
+ assert_equals(event2.submitter, null);
+}, 'Successful SubmitEvent constructor; empty dictionary');
+
+test(() => {
+ let event = new SubmitEvent('bar');
+ assert_equals(event.submitter, null);
+}, 'Successful SubmitEvent constructor; missing dictionary');
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/constructing-form-data-set.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/constructing-form-data-set.html
new file mode 100644
index 0000000000..c27270ea30
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/constructing-form-data-set.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set">
+<link ref="help" href="https://xhr.spec.whatwg.org/#dom-formdata">
+<link rel="help" href="https://fetch.spec.whatwg.org/#concept-bodyinit-extract">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/targetted-form.js"></script>
+
+<iframe name="frame1" id="frame1"></iframe>
+<form accept-charset="iso-8859-1" target="frame1" action="/common/blank.html" id="form1">
+ <input type="hidden" name="_charset_">
+</form>
+
+<iframe name="frame2" id="frame2"></iframe>
+<form target="frame2" action="/common/blank.html" id="form2">
+ <input type="text" name="foo">
+ <button type="close" name="close" value="true">close</button>
+ <button type="button" name="button" value="true">button</button>
+ <button type="reset" name="reset" value="true">reset</button>
+ <button type="submit" name="submit" value="true">submit</button>
+</form>
+
+<script>
+
+const form1 = document.getElementById('form1'),
+ form2 = document.getElementById('form2'),
+ frame1 = document.getElementById('frame1'),
+ frame2 = document.getElementById('frame2');
+
+test(() => {
+ const formData = new FormData(form1);
+ assert_equals(formData.get('_charset_'), 'UTF-8');
+}, 'FormData constructor always produces UTF-8 _charset_ value.');
+
+async_test(t => {
+ frame1.onload = t.step_func(() => {
+ if (frame1.contentWindow.location.href == "about:blank") { return; }
+ assert_not_equals(frame1.contentDocument.URL.indexOf('_charset_=windows-1252'), -1,"should see _charset_=windows-1252 in "+frame1.contentDocument.URL);
+ t.done();
+ });
+ form1.submit();
+},'_charset_ control sets the expected encoding name.');
+
+async_test(t => {
+ frame2.onload = t.step_func_done(() => {
+ assert_equals(frame2.contentDocument.URL.split("?")[1], 'foo=&submit=true');
+ });
+ form2.submit.click();
+}, 'The button cannot be setted if it is not a submitter.');
+
+test(() => {
+ let didCallHandler = false;
+ let wasBubbles = false;
+ let wasCancelable = true;
+ let form = populateForm();
+ document.addEventListener('formdata', e => {
+ didCallHandler = true;
+ wasBubbles = e.bubbles;
+ wasCancelable = e.cancelable;
+ });
+ new FormData(form);
+ assert_true(didCallHandler);
+ assert_true(wasBubbles);
+ assert_false(wasCancelable);
+}, '"formdata" event bubbles, and is not cancelable.');
+
+test(() => {
+ let didCallHandler = false;
+ let form = populateForm();
+ let orphanRoot = document.createElement('div');
+ orphanRoot.appendChild(form);
+ orphanRoot.addEventListener('formdata', e => {
+ didCallHandler = true;
+ });
+ new FormData(form);
+ assert_true(didCallHandler);
+}, '"formdata" event bubbles in an orphan tree.');
+
+for (const enctype of ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]) {
+ test((t) => {
+ let form = populateForm('<input name=file type=file><input name=empty type=file>');
+ form.enctype = enctype;
+
+ const file = new File([], "filename");
+ const dataTransfer = new DataTransfer();
+ dataTransfer.items.add(file);
+ form.querySelector('input[name=file]').files = dataTransfer.files;
+
+ form.addEventListener('formdata', t.step_func(e => {
+ assert_true(e.formData.has('file'));
+ assert_equals(e.formData.get('file'), file);
+ assert_true(e.formData.has('empty'));
+ assert_true(e.formData.get('empty') instanceof File);
+ }));
+ form.submit();
+ }, `Files in a ${enctype} form show up as File objects in the "formData" IDL attribute`);
+}
+
+test(() => {
+ let listener1ok = false;
+ let listeern2ok = false;
+ let form = populateForm('<input name=n1 value=v1>');
+ form.addEventListener('formdata', e => {
+ listener1ok = e.formData.get('n1') == 'v1';
+ e.formData.append('h1', 'vh1');
+ e.formData.append('h2', 'vh2');
+ });
+ form.addEventListener('formdata', e => {
+ if (e.formData.get('h1') == 'vh1' && e.formData.get('h2') == 'vh2')
+ listener2ok = true;
+ });
+ form.submit();
+ assert_true(listener1ok);
+ assert_true(listener2ok);
+}, '"formData" IDL attribute should have entries for form-associated elements' +
+ ' in the first event handler, and the second handler can read entries ' +
+ 'set by the first handler.');
+
+let t1 = async_test('Entries added to "formData" IDL attribute should be submitted.');
+t1.step(() => {
+ let form = populateForm('<input name=n1 value=v1>');
+ form.addEventListener('formdata', e => {
+ e.formData.append('h1', 'vh1');
+ });
+ let iframe = form.previousSibling;
+ iframe.onload = t1.step_func(() => {
+ // The initial about:blank load event can be fired before the form navigation occurs.
+ // See https://github.com/whatwg/html/issues/490 for more information.
+ if (iframe.contentWindow.location.href == "about:blank") { return; }
+ assert_true(iframe.contentWindow.location.search.indexOf('n1=v1&h1=vh1') != -1);
+ t1.done();
+ });
+ form.submit();
+});
+
+test(() => {
+ const form = populateForm('');
+ form.addEventListener('formdata', e => {
+ e.formData.append('a\nb', 'c\rd');
+ });
+ const formData = new FormData(form);
+ const [name, value] = [...formData][0];
+ assert_equals(name, 'a\nb');
+ assert_equals(value, 'c\rd');
+}, 'Entries added to the "formdata" IDL attribute shouldn\'t be newline normalized in the resulting FormData');
+
+test(() => {
+ let form = populateForm('<input name=n1 value=v1><button type=submit name=n2 value=v2></button>');
+ let formDataInEvent = null;
+ let submitter = form.querySelector('button[type=submit]');
+ form.addEventListener('submit', e => {
+ e.preventDefault();
+ formDataInEvent = new FormData(e.target);
+ });
+
+ submitter.click();
+ assert_equals(formDataInEvent.get('n1'), 'v1');
+ assert_false(formDataInEvent.has('n2'));
+}, 'The constructed FormData object should not contain an entry for the submit button that was used to submit the form.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/enctypes-helper.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/enctypes-helper.js
new file mode 100644
index 0000000000..0f0d68163d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/enctypes-helper.js
@@ -0,0 +1,187 @@
+// This file exposes the `formSubmissionTemplate` function, which can be used
+// to create tests for the form submission encoding of various enctypes:
+//
+// const urlencodedTest = formSubmissionTemplate(
+// "application/x-www-form-urlencoded",
+// (expected, _actualFormBody) => expected
+// );
+//
+// urlencodedTest({
+// name: "a",
+// value: "b",
+// expected: "a=b",
+// formEncoding: "UTF-8", // optional
+// description: "Simple urlencoded test"
+// });
+//
+// The above call to `urlencodedTest` tests the urlencoded form submission for a
+// form whose entry list contains a single entry with name "a" and value "b",
+// and it checks that the form payload matches the `expected` property after
+// isomorphic-encoding.
+//
+// Since per the spec no normalization of the form entries should happen before
+// the actual form encoding, each call to `urlencodedTest` will in fact add two
+// tests: one submitting the entry as a form control (marked "normal form"), and
+// one adding the entry through the `formdata` event (marked "formdata event").
+// Both cases are compared against the same expected value.
+//
+// Since multipart/form-data boundary strings can't be predicted ahead of time,
+// the second parameter of `formSubmissionTemplate` allows transforming the
+// expected value passed to each test. The second argument of that callback
+// is the actual form body (isomorphic-decoded). When this callback is used, the
+// `expected` property doesn't need to be a string.
+
+(() => {
+ // Using echo-content-escaped.py rather than
+ // /fetch/api/resources/echo-content.py to work around WebKit not
+ // percent-encoding \x00, which causes the response to be detected as
+ // a binary file and served as a download.
+ const ACTION_URL = "/FileAPI/file/resources/echo-content-escaped.py";
+
+ const IFRAME_NAME = "formtargetframe";
+
+ // Undoes the escapes from echo-content-escaped.py
+ function unescape(str) {
+ return str
+ .replace(/\r\n?|\n/g, "\r\n")
+ .replace(
+ /\\x[0-9A-Fa-f]{2}/g,
+ (escape) => String.fromCodePoint(parseInt(escape.substring(2), 16)),
+ )
+ .replace(/\\\\/g, "\\");
+ }
+
+ // Tests the form submission of an entry list containing a single entry.
+ //
+ // `expectedBuilder` is a function that takes in the actual form body
+ // (necessary to get the multipart/form-data payload) and returns the form
+ // body that should be expected.
+ //
+ // If `testFormData` is false, the form entry will be submitted in for
+ // controls. If it is true, it will submitted by modifying the entry list
+ // during the `formdata` event.
+ async function formSubmissionTest({
+ name,
+ value,
+ expectedBuilder,
+ enctype,
+ formEncoding,
+ testFormData = false,
+ testCase,
+ }) {
+ if (document.readyState !== "complete") {
+ await new Promise((resolve) => addEventListener("load", resolve));
+ }
+
+ const formTargetFrame = Object.assign(document.createElement("iframe"), {
+ name: IFRAME_NAME,
+ });
+ document.body.append(formTargetFrame);
+ testCase.add_cleanup(() => {
+ document.body.removeChild(formTargetFrame);
+ });
+
+ const form = Object.assign(document.createElement("form"), {
+ acceptCharset: formEncoding,
+ action: ACTION_URL,
+ method: "POST",
+ enctype,
+ target: IFRAME_NAME,
+ });
+ document.body.append(form);
+ testCase.add_cleanup(() => {
+ document.body.removeChild(form);
+ });
+
+ if (!testFormData) {
+ const input = document.createElement("input");
+ input.name = name;
+ if (value instanceof File) {
+ input.type = "file";
+ const dataTransfer = new DataTransfer();
+ dataTransfer.items.add(value);
+ input.files = dataTransfer.files;
+ } else {
+ input.type = "hidden";
+ input.value = value;
+ }
+ form.append(input);
+ } else {
+ form.addEventListener("formdata", (evt) => {
+ evt.formData.append(name, value);
+ });
+ }
+
+ await new Promise((resolve) => {
+ form.submit();
+ formTargetFrame.onload = resolve;
+ });
+
+ const serialized = unescape(
+ formTargetFrame.contentDocument.body.textContent,
+ );
+ const expected = expectedBuilder(serialized);
+ assert_equals(serialized, expected);
+ }
+
+ // This function returns a function to add individual form tests corresponding
+ // to some enctype.
+ // `expectedBuilder` is an optional callback that takes two parameters:
+ // `expected` (the `expected` property passed to a test) and `actualFormBody`
+ // (the actual form body submitted by the browser, isomorphic-decoded). It
+ // must return the correct form body that should have been submitted,
+ // isomorphic-encoded. This is necessary in order to account for the
+ // multipart/form-data boundary.
+ //
+ // The returned function takes an object with the following properties:
+ // - `name`, the form entry's name. Must be a string.
+ // - `value`, the form entry's value, either a string or a `File` object.
+ // - `expected`, the expected form body. Usually a string, but it can be
+ // anything depending on `expectedBuilder`.
+ // - `formEncoding` (optional), the character encoding used for submitting the
+ // form.
+ // - `description`, used as part of the testharness test's description.
+ window.formSubmissionTemplate = (
+ enctype,
+ expectedBuilder = (expected) => expected
+ ) => {
+ function form({
+ name,
+ value,
+ expected,
+ formEncoding = "utf-8",
+ description,
+ }) {
+ const commonParams = {
+ name,
+ value,
+ expectedBuilder: expectedBuilder.bind(null, expected),
+ enctype,
+ formEncoding,
+ };
+
+ // Normal form
+ promise_test(
+ (testCase) =>
+ formSubmissionTest({
+ ...commonParams,
+ testCase,
+ }),
+ `${enctype}: ${description} (normal form)`,
+ );
+
+ // formdata event
+ promise_test(
+ (testCase) =>
+ formSubmissionTest({
+ ...commonParams,
+ testFormData: true,
+ testCase,
+ }),
+ `${enctype}: ${description} (formdata event)`,
+ );
+ }
+
+ return form;
+ };
+})();
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-empty-file.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-empty-file.window.js
new file mode 100644
index 0000000000..7df128515c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-empty-file.window.js
@@ -0,0 +1,99 @@
+test(t => {
+ const form = document.body.appendChild(document.createElement("form")),
+ input = form.appendChild(document.createElement("input"));
+ input.type = "file";
+ input.name = "hi";
+ t.add_cleanup(() => {
+ document.body.removeChild(form);
+ });
+ const fd = new FormData(form),
+ value = fd.get(input.name);
+ assert_true(value instanceof File, "value is a File");
+ assert_equals(value.name, "", "name");
+ assert_equals(value.type, "application/octet-stream", "type");
+ assert_equals(value.size, 0, "expected value to be an empty File");
+}, "Empty <input type=file> is still added to the form's entry list");
+
+async_test((t) => {
+ const form = document.body.appendChild(document.createElement("form")),
+ input = form.appendChild(document.createElement("input")),
+ target = document.createElement("iframe");
+ target.name = "target1";
+ document.body.appendChild(target);
+ form.method = "POST";
+ form.action = "/fetch/api/resources/echo-content.py";
+ form.enctype = "application/x-www-form-urlencoded";
+ form.target = target.name;
+ input.type = "file";
+ input.name = "hi";
+ t.add_cleanup(() => {
+ document.body.removeChild(form);
+ document.body.removeChild(target);
+ });
+
+ target.addEventListener("load", t.step_func_done(() => {
+ assert_equals(target.contentDocument.body.textContent, "hi=");
+ }));
+ form.submit();
+}, "Empty <input type=file> shows up in the urlencoded serialization");
+
+async_test((t) => {
+ const form = document.body.appendChild(document.createElement("form")),
+ input = form.appendChild(document.createElement("input")),
+ target = document.createElement("iframe");
+ target.name = "target2";
+ document.body.appendChild(target);
+ form.method = "POST";
+ form.action = "/fetch/api/resources/echo-content.py";
+ form.enctype = "multipart/form-data";
+ form.target = target.name;
+ input.type = "file";
+ input.name = "hi";
+ t.add_cleanup(() => {
+ document.body.removeChild(form);
+ document.body.removeChild(target);
+ });
+
+ target.addEventListener("load", t.step_func_done(() => {
+ // We use \n rather than \r\n because newlines get normalized as a result
+ // of HTML parsing.
+ const found = target.contentDocument.body.textContent;
+ const boundary = found.split("\n")[0];
+ const expected = [
+ boundary,
+ 'Content-Disposition: form-data; name="hi"; filename=""',
+ "Content-Type: application/octet-stream",
+ "",
+ "",
+ boundary + "--",
+ "",
+ ].join("\n");
+ assert_equals(found, expected);
+ }));
+ form.submit();
+}, "Empty <input type=file> shows up in the multipart/form-data serialization");
+
+async_test((t) => {
+ const form = document.body.appendChild(document.createElement("form")),
+ input = form.appendChild(document.createElement("input")),
+ target = document.createElement("iframe");
+ target.name = "target3";
+ document.body.appendChild(target);
+ form.method = "POST";
+ form.action = "/fetch/api/resources/echo-content.py";
+ form.enctype = "text/plain";
+ form.target = target.name;
+ input.type = "file";
+ input.name = "hi";
+ t.add_cleanup(() => {
+ document.body.removeChild(form);
+ document.body.removeChild(target);
+ });
+
+ target.addEventListener("load", t.step_func_done(() => {
+ // The actual result is "hi=\r\n"; the newline gets normalized as a side
+ // effect of the HTML parsing.
+ assert_equals(target.contentDocument.body.textContent, "hi=\n");
+ }));
+ form.submit();
+}, "Empty <input type=file> shows up in the text/plain serialization");
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv-form.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv-form.html
new file mode 100644
index 0000000000..ce87abd957
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv-form.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This is the form that will be submitted</title>
+
+<form action="form-echo.py" method="post" enctype="text/plain">
+ <input id="input1" type="text">
+ <select id="input2">
+ <option selected>option
+ </select>
+ <input id="input3" type="radio" checked>
+ <input id="input4" type="checkbox" checked>
+</form>
+
+<script>
+"use strict";
+
+const form = document.querySelector("form");
+
+for (let el of Array.from(form.querySelectorAll("input"))) { // Firefox/Edge support
+ el.name = el.id + "\uDC01";
+ el.value = el.id + "\uDC01";
+}
+
+const select = document.querySelector("select");
+select.name = select.id + "\uDC01";
+select.firstElementChild.value = select.id + "\uDC01";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv.html
new file mode 100644
index 0000000000..4ac26092ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-data-set-usv.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Submitting a form data set that contains unpaired surrogates must convert to Unicode scalar values</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#constructing-form-data-set">
+<link rel="help" href="https://github.com/whatwg/html/issues/1490">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="testframe" src="form-data-set-usv-form.html"></iframe>
+<iframe id="testframe2" src="form-data-set-usv-form.html"></iframe>
+
+<script>
+"use strict";
+
+async_test(t => {
+ window.addEventListener("load", t.step_func(() => {
+ const iframe = document.querySelector("#testframe");
+ const form = iframe.contentWindow.document.querySelector("form");
+
+ iframe.onload = t.step_func_done(() => {
+ const result = iframe.contentWindow.document.body.textContent;
+
+ assert_equals(result,
+ "69 6e 70 75 74 31 ef bf bd 3d 69 6e 70 75 74 31 ef bf bd " + // input1\uFFFD=input1\uFFFD
+ "0d 0a " + // \r\n
+ "69 6e 70 75 74 32 ef bf bd 3d 69 6e 70 75 74 32 ef bf bd " + // input2\uFFFD=input2\uFFFD
+ "0d 0a " + // \r\n
+ "69 6e 70 75 74 33 ef bf bd 3d 69 6e 70 75 74 33 ef bf bd " + // input3\uFFFD=input3\uFFFD
+ "0d 0a " + // \r\n
+ "69 6e 70 75 74 34 ef bf bd 3d 69 6e 70 75 74 34 ef bf bd " + // input4\uFFFD=input4\uFFFD
+ "0d 0a" // \r\n
+ );
+
+ // ef bf bd is the UTF-8 encoding of U+FFFD
+ });
+
+ form.submit();
+ }));
+}, 'Strings from form controls should be converted to Unicode scalar values in form submission');
+
+async_test(t => {
+ window.addEventListener("load", t.step_func_done(() => {
+ const iframe = document.querySelector("#testframe2");
+ const formData = new FormData(iframe.contentWindow.document.querySelector("form"));
+ assert_equals(formData.get("input1\uFFFD"), "input1\uFFFD");
+ assert_equals(formData.get("input2\uFFFD"), "input2\uFFFD");
+ assert_equals(formData.get("input3\uFFFD"), "input3\uFFFD");
+ assert_equals(formData.get("input4\uFFFD"), "input4\uFFFD");
+ }));
+}, 'Strings from form controls should be converted to Unicode scalar values in FormData');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-2.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-2.html
new file mode 100644
index 0000000000..f7939e0fa3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-2.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- The onclick submit() should *not* get superseded in this case by the
+ default action submit(), because onclick here calls preventDefault().
+ -->
+
+
+
+
+<label for=frame1 style="display:block">This frame should stay blank</label>
+<iframe name=frame1 id=frame1></iframe>
+<label for=frame2 style="display:block">This frame should navigate (to 404)</label>
+<iframe name=frame2 id=frame2></iframe>
+<form id="form1" target="frame2" action="nonexistent.html">
+ <input type=hidden name=navigated value=1>
+ <input id=submitbutton type=submit>
+</form>
+
+<script>
+let frame1 = document.getElementById('frame1');
+let frame2 = document.getElementById('frame2');
+let form1 = document.getElementById('form1');
+let submitbutton = document.getElementById('submitbutton');
+
+async_test(t => {
+ window.addEventListener('load', () => {
+ frame1.addEventListener('load', t.step_func_done(() => {
+ assert_unreached("Frame1 should not get navigated by this test.");
+ }));
+ frame2.addEventListener('load', t.step_func_done(() => {
+ let params = (new URL(frame2.contentWindow.location)).searchParams;
+ let wasNavigated = !!params.get("navigated");
+ assert_true(wasNavigated);
+ }));
+ form1.addEventListener('click', t.step_func(() => {
+ form1.submit();
+ form1.target='frame1';
+ event.preventDefault(); // Prevent default here
+ }));
+ submitbutton.click();
+ });
+}, 'preventDefault should allow onclick submit() to succeed');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-3.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-3.html
new file mode 100644
index 0000000000..fbb6a42577
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-3.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- <button> should have the same double-submit protection that
+ <input type=submit> has.
+ -->
+
+
+
+
+<label for=frame1 style="display:block">This frame should stay blank</label>
+<iframe name=frame1 id=frame1></iframe>
+<label for=frame2 style="display:block">This frame should navigate (to 404)</label>
+<iframe name=frame2 id=frame2></iframe>
+<form id="form1" target="frame1" action="nonexistent.html">
+ <input type=hidden name=navigated value=1>
+ <button id=submitbutton>submit</button>
+</form>
+
+<script>
+let frame1 = document.getElementById('frame1');
+let frame2 = document.getElementById('frame2');
+let form1 = document.getElementById('form1');
+let submitbutton = document.getElementById('submitbutton');
+
+async_test(t => {
+ window.addEventListener('load', () => {
+ frame1.addEventListener('load', t.step_func_done(() => {
+ assert_unreached("Frame1 should not get navigated by this test.");
+ }));
+ frame2.addEventListener('load', t.step_func_done(() => {
+ let params = (new URL(frame2.contentWindow.location)).searchParams;
+ let wasNavigated = !!params.get("navigated");
+ assert_true(wasNavigated)
+ }));
+ form1.addEventListener('click', t.step_func(() => {
+ form1.submit();
+ form1.target='frame2';
+
+ }));
+ submitbutton.click();
+ });
+}, '<button> should have the same double-submit protection as <input type=submit>');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-default-action.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-default-action.html
new file mode 100644
index 0000000000..a14cfe7afa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-default-action.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/targetted-form.js"></script>
+<!--
+ The submit() in event handler should get superseded by the default action
+ submit(), which isn't preventDefaulted. This is per the Form Submission
+ Algorithm [1], step 24, which says that new planned navigations replace old
+ planned navigations.
+ [1] https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm
+-->
+<body>
+<script>
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><input type=submit>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("input[type=submit]");
+ submitter.addEventListener('click', () => {
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v4");
+}, 'default submit action should supersede input onclick submit()');
+
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><button>submit</button>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("button");
+ submitter.addEventListener('click', (e) => {
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v4");
+}, 'default submit action should supersede button onclick submit()');
+
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><input type=submit>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("input[type=submit]");
+ form.addEventListener('click', () => {
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v4");
+}, 'default submit action should supersede form onclick submit()');
+
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><input type=submit>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("input[type=submit]");
+ form.addEventListener('submit', () => {
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v4");
+}, 'default submit action should supersede form onsubmit submit()');
+
+promise_test(async (t) => {
+ let form = populateForm('<input name=n1 value=v1><input type=submit>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("input[type=submit]");
+ form.addEventListener('click', () => {
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ form.addEventListener('submit', () => {
+ input.value = 'v5';
+ form.submit();
+ input.value = 'v6';
+ form.submit();
+ input.value = 'v7';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v7");
+}, 'default submit action should supersede form onclick/onsubmit submit()');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-multiple-targets.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-multiple-targets.html
new file mode 100644
index 0000000000..2b5a589b53
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-multiple-targets.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+
+<!-- The expected behavior of this test is not explicitly specified. -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id=myform name=myform action="/formaction.html"></form>
+<iframe id=frame1 name=target1></iframe>
+<iframe id=frame2 name=target2></iframe>
+<iframe id=frame3 name=target3></iframe>
+
+<script>
+
+promise_test(async () => {
+ const frame1LoadPromise = new Promise(resolve => frame1.onload = resolve);
+ const frame2LoadPromise = new Promise(resolve => frame2.onload = resolve);
+ const frame3LoadPromise = new Promise(resolve => frame3.onload = resolve);
+
+ myform.target = 'target1';
+ myform.submit();
+ myform.target = 'target2';
+ myform.submit();
+ myform.target = 'target3';
+ myform.submit();
+
+ await Promise.all([frame1LoadPromise, frame2LoadPromise, frame3LoadPromise]);
+
+ assert_equals(frame1.contentDocument.location.pathname, '/formaction.html');
+ assert_equals(frame2.contentDocument.location.pathname, '/formaction.html');
+ assert_equals(frame3.contentDocument.location.pathname, '/formaction.html');
+
+}, 'Verifies that one form used to target multiple frames in succession navigates all of them.');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault-click.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault-click.html
new file mode 100644
index 0000000000..68dc9c10a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault-click.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/targetted-form.js"></script>
+<!--
+ The submit() in event handler should *not* get superseded in this case by the
+ default action submit(), because event handler here calls preventDefault().
+-->
+<body>
+<script>
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><input type=submit>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("input[type=submit]");
+ submitter.addEventListener('click', (e) => {
+ e.preventDefault();
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v3");
+}, 'PreventDefaulting input onclick should allow submit() to succeed');
+
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><button>submit</button>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("button");
+ submitter.addEventListener('click', (e) => {
+ e.preventDefault();
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v3");
+}, 'PreventDefaulting button onclick should allow submit() to succeed');
+
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><input type=submit>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("input[type=submit]");
+ form.addEventListener('click', (e) => {
+ e.preventDefault();
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v3");
+}, 'PreventDefaulting form onclick should allow submit() to succeed');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault.html
new file mode 100644
index 0000000000..b63b78916c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-preventdefault.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/targetted-form.js"></script>
+<!--
+ The submit() in event handler should *not* get superseded in this case by the
+ default action submit(), because event handler here calls preventDefault().
+-->
+<body>
+<script>
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1><input type=submit>');
+ let iframe = form.previousSibling;
+ let input = form.querySelector("input[name=n1]");
+ let submitter = form.querySelector("input[type=submit]");
+ form.addEventListener('click', () => {
+ input.value = 'v2';
+ form.submit();
+ input.value = 'v3';
+ form.submit();
+ input.value = 'v4';
+ });
+ form.addEventListener('submit', (e) => {
+ e.preventDefault();
+ input.value = 'v5';
+ form.submit();
+ input.value = 'v6';
+ form.submit();
+ input.value = 'v7';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v6");
+}, 'PreventDefaulting form onsubmit should allow submit() to succeed');
+
+promise_test(async () => {
+ let form = populateForm('<input type=submit><input name=n1 value=v1>');
+ let iframe = form.previousSibling;
+ let input = form['n1'];
+ let submitter = form.querySelector('input[type=submit]');
+ form.addEventListener('submit', e => {
+ e.preventDefault();
+ input.value = 'v2';
+ form.submit();
+
+ input.value = 'v3';
+ form.remove();
+ form.submit();
+ document.body.insertBefore(form, iframe.nextSibling);
+ input.value = 'v4';
+ });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, "n1"), "v2");
+}, 'PreventDefaulting form onsubmit should allow submit() to succeed and the second submit() which is invalid should not supersede first one');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-requestsubmit.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-requestsubmit.html
new file mode 100644
index 0000000000..b4028784ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-requestsubmit.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help"
+ href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/targetted-form.js"></script>
+
+<!-- The onclick requestSubmit() should get superseded by the default
+ action submit, which isn't preventDefaulted by onclick here.
+ This is per the Form Submission Algorithm [1], which
+ says that new planned navigations replace old planned navigations.
+ [1] https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#planned-navigation
+ -->
+
+<body>
+ <script>
+ function runTest({ submitterType, preventDefaultSubmitButton, preventDefaultRequestSubmit, passSubmitter, testName }) {
+ if (preventDefaultRequestSubmit && preventDefaultSubmitButton) {
+ // In this case, no submit action will take place.
+ return;
+ }
+
+ promise_test(async () => {
+ const form = populateForm(`<input name=n1 value=v1><${submitterType} type=submit name=n2 value=v2>${submitterType == 'button' ? '</button>' : ''}`);
+ const input = form.elements[0];
+ const submitter = form.elements[1];
+ submitter.addEventListener('click', e => {
+ form.addEventListener('submit', e => {
+ submitter.value = 'v3';
+ if (preventDefaultRequestSubmit) {
+ e.preventDefault();
+ }
+ }, { once: true });
+
+ form.requestSubmit(passSubmitter ? submitter : null);
+ input.value = 'v2';
+
+ form.addEventListener('submit', e => {
+ submitter.value = 'v4';
+ if (preventDefaultSubmitButton) {
+ e.preventDefault();
+ }
+ }, { once: true });
+ });
+
+ let formDataInEvent;
+ form.addEventListener('formdata', e => {
+ formDataInEvent = e.formData;
+ });
+
+ submitter.click();
+ assert_equals(formDataInEvent.get('n1'), preventDefaultSubmitButton ? 'v1' : 'v2');
+ if (preventDefaultSubmitButton && !passSubmitter) {
+ assert_false(formDataInEvent.has('n2'));
+ } else {
+ assert_equals(formDataInEvent.get('n2'),
+ preventDefaultSubmitButton && passSubmitter ? 'v3' : 'v4')
+ }
+
+ let iframe = form.previousSibling;
+ await loadPromise(iframe);
+ assert_equals(getParamValue(iframe, 'n1'), preventDefaultSubmitButton ? 'v1' : 'v2');
+ if (preventDefaultSubmitButton && !passSubmitter) {
+ assert_equals(getParamValue(iframe, 'n2'), null);
+ } else {
+ assert_equals(getParamValue(iframe, 'n2'),
+ preventDefaultSubmitButton && passSubmitter ? 'v3' : 'v4');
+ }
+ }, testName);
+ }
+
+ function runTest2({ submitterType, callRequestSubmit, callSubmit, preventDefault, passSubmitter, testName }) {
+ if (!callSubmit && preventDefault) {
+ // Without callSubmit, preventDefault will cause the form to not get
+ // submitted.
+ return;
+ }
+
+ promise_test(async () => {
+ const form = populateForm(`<input name=n1 value=v1><${submitterType} type=submit name=n2 value=v3>${submitterType == 'button' ? '</button>' : ''}`);
+ const input = form.elements[0];
+ const submitter = form.elements[1];
+
+ form.addEventListener('submit', e => {
+ if (callRequestSubmit) {
+ form.requestSubmit(passSubmitter ? submitter : null);
+ input.value = 'v2';
+ }
+ if (callSubmit) {
+ form.submit();
+ }
+ if (preventDefault) {
+ e.preventDefault();
+ }
+ });
+
+ form.requestSubmit(passSubmitter ? submitter : null);
+ let iframe = form.previousSibling;
+ await loadPromise(iframe);
+
+ assert_equals(getParamValue(iframe, 'n1'), callRequestSubmit ? 'v2' : 'v1');
+ if (callSubmit || !passSubmitter) {
+ assert_equals(getParamValue(iframe, 'n2'), null);
+ } else {
+ assert_equals(getParamValue(iframe, 'n2'), 'v3')
+ }
+ }, testName);
+ }
+
+ function callWithArgs(test, argsLeft, args) {
+ if (argsLeft.length == 0) {
+ args.testName = 'test ' + test.name + ' with ' + Object.entries(args).map(([key, value]) => `${key}: ${value}`).join(', ');
+ test(args);
+ return;
+ }
+
+ let [name, values] = argsLeft[0];
+ for (let value of values) {
+ callWithArgs(test, argsLeft.slice(1), { ...args, [name]: value })
+ }
+ }
+
+ let args = {
+ submitterType: ['input', 'button'],
+ preventDefaultRequestSubmit: [true, false],
+ preventDefaultSubmitButton: [true, false],
+ passSubmitter: [true, false],
+ };
+ callWithArgs(runTest, Object.entries(args), {});
+
+ args = {
+ submitterType: ['input', 'button'],
+ callRequestSubmit: [true, false],
+ callSubmit: [true, false],
+ preventDefault: [true, false],
+ passSubmitter: [true, false],
+ };
+ callWithArgs(runTest2, Object.entries(args), {});
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html
new file mode 100644
index 0000000000..00a46bfd43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+
+<!-- The onclick submit() should get superseded by the default
+ action submit(), which isn't preventDefaulted by onclick here.
+ This is per the Form Submission Algorithm [1], step 22.3, which
+ says that new planned navigations replace old planned navigations.
+ [1] https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm
+ -->
+
+<label for=frame1 style="display:block">This frame should stay blank</label>
+<iframe name=frame1 id=frame1></iframe>
+<label for=frame2 style="display:block">This frame should navigate (to 404)</label>
+<iframe name=frame2 id=frame2></iframe>
+<form id="form1" target="frame1" action="nonexistent.html">
+ <input type=hidden name=navigated value=1>
+ <input id=submitbutton type=submit>
+</form>
+
+<script>
+promise_test(async () => {
+ function getLoadPromise(frame) {
+ return new Promise(resolve => {
+ frame.addEventListener('load', resolve);
+ });
+ }
+
+ const frame1 = document.getElementById('frame1');
+ const frame2 = document.getElementById('frame2');
+ await getLoadPromise(window);
+
+ const frame1LoadPromise = getLoadPromise(frame1);
+ let frame2LoadPromise = getLoadPromise(frame2);
+ const subframeUrl = get_host_info().REMOTE_ORIGIN;
+ frame1.src = subframeUrl;
+ frame2.src = subframeUrl;
+ await frame1LoadPromise;
+ await frame2LoadPromise;
+ assert_false(!!frame1.contentDocument, 'frame1 should have a different origin.');
+ assert_false(!!frame2.contentDocument, 'frame2 should have a different origin.');
+
+ window.frame1Navigated = false;
+ frame1.addEventListener('load', () => {
+ window.frame1Navigated = true;
+ });
+ frame2LoadPromise = getLoadPromise(frame2);
+
+ form1.addEventListener('click', () => {
+ form1.submit();
+ form1.target = 'frame2';
+ });
+ submitbutton.click();
+ await frame2LoadPromise;
+
+ assert_false(window.frame1Navigated, 'frame1 should not be navigated by the form submission.');
+}, 'default submit action should supersede onclick submit() for cross-origin iframes');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit.html
new file mode 100644
index 0000000000..b6ea0cefab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-double-submit.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- The onclick submit() should get superseded by the default
+ action submit(), which isn't preventDefaulted by onclick here.
+ This is per the Form Submission Algorithm [1], step 22.3, which
+ says that new planned navigations replace old planned navigations.
+ [1] https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm
+ -->
+
+<label for=frame1 style="display:block">This frame should stay blank</label>
+<iframe name=frame1 id=frame1></iframe>
+<label for=frame2 style="display:block">This frame should navigate (to 404)</label>
+<iframe name=frame2 id=frame2></iframe>
+<form id="form1" target="frame1" action="nonexistent.html">
+ <input type=hidden name=navigated value=1>
+ <input id=submitbutton type=submit>
+</form>
+
+<script>
+let frame1 = document.getElementById('frame1');
+let frame2 = document.getElementById('frame2');
+let form1 = document.getElementById('form1');
+let submitbutton = document.getElementById('submitbutton');
+
+async_test(t => {
+ window.addEventListener('load', () => {
+ frame1.addEventListener('load', t.step_func_done(() => {
+ assert_unreached("Frame1 should not get navigated by this test.");
+ }));
+ frame2.addEventListener('load', t.step_func_done(() => {
+ let params = (new URL(frame2.contentWindow.location)).searchParams;
+ let wasNavigated = !!params.get("navigated");
+ assert_true(wasNavigated)
+ }));
+ form1.addEventListener('click', t.step_func(() => {
+ form1.submit();
+ form1.target='frame2';
+
+ }));
+ submitbutton.click();
+ });
+}, 'default submit action should supersede onclick submit()');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-echo.py b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-echo.py
new file mode 100644
index 0000000000..72f1f51ce5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-echo.py
@@ -0,0 +1,7 @@
+def main(request, response):
+ bytes = bytearray(request.raw_input.read())
+ bytes_string = b" ".join(b"%02x" % b for b in bytes)
+ return (
+ [(b"Content-Type", b"text/plain")],
+ bytes_string
+ )
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submission-algorithm.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submission-algorithm.html
new file mode 100644
index 0000000000..0f0fd4ede0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submission-algorithm.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/targetted-form.js"></script>
+<body>
+<script>
+test(() => {
+ let form = populateForm('<input name=n10 value=v10>');
+ let counter = 0;
+ form.addEventListener('formdata', e => {
+ ++counter;
+ form.submit();
+ });
+ form.submit();
+ assert_equals(counter, 1);
+ new FormData(form);
+ assert_equals(counter, 2);
+}, 'If constructing entry list flag of form is true, then return');
+
+test(() => {
+ let form = populateForm('<input><input type=submit>');
+ let submitter1 = form.querySelector('input[type=submit]');
+ let valid = form.elements[0];
+ let counter = 0;
+ valid.oninvalid = () => {
+ ++counter;
+ };
+ form.onsubmit = () => {
+ valid.required = true;
+ submitter1.dispatchEvent(new MouseEvent("click"));
+ };
+ submitter1.dispatchEvent(new MouseEvent("click"));
+ assert_equals(counter, 0);
+}, "If firing submission events flag of form is true, then return");
+
+test(() => {
+ let form = populateForm('<input required><input type=submit><button type=submit></button>');
+ let submitter1 = form.querySelector('input[type=submit]');
+ let submitter2 = form.querySelector('button[type=submit]');
+ let invalid = form.querySelector('[required]');
+ let counter = 0;
+ invalid.oninvalid = () => {
+ ++counter;
+ // Needs to click different one because click() has reentrancy protection.
+ submitter2.click();
+ };
+ submitter1.click();
+ assert_equals(counter, 1);
+}, "If form's firing submission events is true, then return; 'invalid' event");
+
+promise_test(async () => {
+ let form = populateForm('<input type=submit name=n value=i><button type=submit name=n value=b>');
+ let submitter1 = form.querySelector('input[type=submit]');
+ let submitter2 = form.querySelector('button[type=submit]');
+ let iframe = form.previousSibling;
+ form.onsubmit = () => {
+ // Needs to click different one because click() has reentrancy protection.
+ submitter2.click();
+ };
+ submitter1.click();
+ // We actually submit the form in order to check which 'click()' submits it.
+ await loadPromise(iframe);
+ assert_not_equals(iframe.contentWindow.location.search.indexOf('n=i'), -1);
+}, "If form's firing submission events is true, then return; 'submit' event");
+
+promise_test(async () => {
+ let form = populateForm('<button type=submit></button><input name=n1 value=submit type=submit>');
+ let iframe = form.previousSibling;
+ let submitter = form.querySelector('input[type=submit]');
+ let event;
+ form.addEventListener('submit', e => { event = e; });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+ assert_equals(event.submitter, submitter);
+ assert_true(event instanceof SubmitEvent);
+}, 'firing an event named submit; clicking a submit button');
+
+promise_test(async () => {
+ let form = populateForm('<input type=image name=n1>');
+ let iframe = form.previousSibling;
+ let submitter = form.querySelector('input[type=image]');
+ let event;
+ form.addEventListener('submit', e => { event = e; });
+ submitter.click();
+ await loadPromise(iframe);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+ assert_equals(event.submitter, submitter);
+ assert_true(event instanceof SubmitEvent);
+}, 'firing an event named submit; clicking an image button');
+
+promise_test(async () => {
+ let form = populateForm('');
+ let iframe = form.previousSibling;
+ let event;
+ form.addEventListener('submit', e => { event = e; });
+ form.requestSubmit();
+ await loadPromise(iframe);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+ assert_equals(event.submitter, null);
+ assert_true(event instanceof SubmitEvent);
+}, 'firing an event named submit; form.requestSubmit()');
+
+promise_test(async () => {
+ let form = populateForm('');
+ let iframe = form.previousSibling;
+ let event;
+ form.addEventListener('submit', e => { event = e; });
+ form.requestSubmit(null);
+ await loadPromise(iframe);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+ assert_equals(event.submitter, null);
+ assert_true(event instanceof SubmitEvent);
+}, 'firing an event named submit; form.requestSubmit(null)');
+
+promise_test(async () => {
+ let form = populateForm('<input type=submit><button type=submit></button>');
+ let iframe = form.previousSibling;
+ let submitter = form.querySelector('button');
+ let event;
+ form.addEventListener('submit', e => { event = e; });
+ form.requestSubmit(submitter);
+ await loadPromise(iframe);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+ assert_equals(event.submitter, submitter);
+ assert_true(event instanceof SubmitEvent);
+}, 'firing an event named submit; form.requestSubmit(submitter)');
+
+promise_test(async () => {
+ let form = populateForm('<input name=n1 value=v1>');
+ form.onformdata = (e) => { e.target.remove(); };
+ let wasLoaded = false;
+ let iframe = form.previousSibling;
+ // Request to load '/common/dummy.xhtml', and immediately submit the form to
+ // the same frame. If the form submission is aborted, the first request
+ // will be completed.
+ iframe.addEventListener("load", () => {
+ // This may be complicated by loads of the initial about:blank;
+ // we need to ignore them and only look at a load that isn't about:blank.
+ if (iframe.contentWindow.location == "about:blank") { return; }
+ wasLoaded = true;
+ });
+ iframe.src = '/common/dummy.xhtml';
+ assert_false(wasLoaded, 'Make sure the first loading is ongoing.');
+ form.submit();
+ await loadPromise(iframe);
+ assert_true(iframe.contentWindow.location.search.indexOf('n1=v1') == -1);
+}, 'Cannot navigate (after constructing the entry list)');
+
+promise_test(async () => {
+ let form = populateForm('<input type=submit>');
+ let iframe = form.previousSibling;
+ let event;
+ form.submit();
+ await loadPromise(iframe);
+ assert_true(iframe.contentWindow.location.href.includes("?"));
+}, 'Submission URL should always have a non-null query part');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submit-iframe-then-location-navigate.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submit-iframe-then-location-navigate.html
new file mode 100644
index 0000000000..ad2943e2bb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/form-submit-iframe-then-location-navigate.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This behavior is not explicitly specified. -->
+
+<iframe id=myframe name=framename></iframe>
+<form id=myform target=framename action="resources/form.html"></form>
+
+<script>
+async_test(t => {
+ myframe.onload = t.step_func_done(() => {
+ assert_equals(
+ myframe.contentDocument.location.pathname,
+ '/html/semantics/forms/form-submission-0/resources/location.html');
+ });
+ myform.submit();
+ myframe.contentDocument.location = 'resources/location.html';
+}, 'Verifies that location navigations take precedence when following form submissions.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/getactionurl.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/getactionurl.html
new file mode 100644
index 0000000000..2e21828ae6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/getactionurl.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="resources/getactionurl-iframe.html"></iframe>
+<script>
+async_test(t => {
+ window.onmessage = t.step_func_done(event => assert_equals(event.data, 'PASS'));
+}, `Verifies that a form element's target can be a data url.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/historical.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/historical.window.js
new file mode 100644
index 0000000000..fcc47d90f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/historical.window.js
@@ -0,0 +1,19 @@
+// META: script=./resources/targetted-form.js
+
+test(t => {
+ const form = populateForm('<input required><input type=submit>');
+ t.add_cleanup(() => {
+ form.previousElementSibling.remove();
+ form.remove();
+ });
+ const submitter = form.querySelector('input[type=submit]');
+ let invalid = form.querySelector('[required]');
+ let targets = [];
+ const listener = e => targets.push(e.target.localName);
+ form.addEventListener("invalid", t.step_func(listener));
+ form.oninvalid = t.step_func(listener);
+ invalid.addEventListener("invalid", t.step_func(listener));
+ invalid.oninvalid = t.step_func(listener);
+ submitter.click();
+ assert_array_equals(targets, ["input", "input"]);
+}, "invalid event is only supported for form controls");
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/implicit-submission.optional.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/implicit-submission.optional.html
new file mode 100644
index 0000000000..379a4396ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/implicit-submission.optional.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/C/#implicit-submission">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="./resources/targetted-form.js"></script>
+<body>
+<script>
+// This test file is "optional" because triggering implicit submission by
+// "Enter" key is not standardized.
+
+const ENTER = '\uE007';
+
+promise_test(async () => {
+ let form = populateForm('<input name=text value=abc><input name=submitButton type=submit>');
+ let event;
+ form.text.focus();
+ form.addEventListener('submit', e => { event = e; });
+ await test_driver.send_keys(form.text, ENTER);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+ assert_equals(event.submitter, form.submitButton);
+ assert_true(event instanceof SubmitEvent);
+}, 'Submit event with a submit button');
+
+promise_test(async () => {
+ let form = populateForm('<input name=text value=abc>');
+ let event;
+ form.text.focus();
+ form.addEventListener('submit', e => { event = e; });
+ await test_driver.send_keys(form.text, ENTER);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+ assert_equals(event.submitter, null);
+ assert_true(event instanceof SubmitEvent);
+}, 'Submit event with no submit button');
+
+promise_test(async (test) => {
+ let form = populateForm('<input name=text value=abc><input name=submitButton type=submit disabled>');
+ form.text.focus();
+ form.addEventListener('submit', test.unreached_func('submit event should not be dispatched'));
+ await test_driver.send_keys(form.text, ENTER);
+}, 'Submit event with disabled submit button');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html
new file mode 100644
index 0000000000..f476308b7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This behavior is not explicitly specified. -->
+
+<iframe id=frameid name=framename></iframe>
+<form id=formid target=framename action="resources/form.html"></form>
+
+<script>
+async_test(t => {
+ frameid.src = 'resources/jsurl-form-submit-iframe.html';
+
+ frameid.onload = t.step_func(() => {
+ assert_equals(
+ frameid.contentDocument.location.pathname,
+ '/html/semantics/forms/form-submission-0/resources/jsurl-form-submit-iframe.html');
+
+ frameid.onload = t.step_func_done(() => {
+ assert_equals(
+ frameid.contentDocument.location.pathname,
+ '/html/semantics/forms/form-submission-0/resources/form.html');
+ });
+
+ frameid.contentDocument.getElementById('anchorid').click();
+ });
+
+}, `Verifies that form submissions scheduled inside javascript: urls take precedence over the javascript: url's return value.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-navigation-then-form-submit.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-navigation-then-form-submit.html
new file mode 100644
index 0000000000..93a4ea6004
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/jsurl-navigation-then-form-submit.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+
+<!-- The expected behavior of this test is not explicitly specified. -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ const iframe = document.createElement('iframe');
+ iframe.name = 'myframe';
+
+ iframe.onload = t.step_func_done(() => {
+ assert_equals(iframe.contentDocument.location.pathname, '/formaction.html');
+ });
+
+ const form = document.createElement('form');
+ form.target = iframe.name;
+ form.action = '/formaction.html';
+ document.body.appendChild(form);
+
+ iframe.src = 'javascript:false';
+ document.body.appendChild(iframe);
+ form.submit();
+ });
+}, 'Verifies that form submissions cancel javascript navigations to prevent duplicate load events.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/multipart-formdata.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/multipart-formdata.window.js
new file mode 100644
index 0000000000..ca69c7dac7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/multipart-formdata.window.js
@@ -0,0 +1,357 @@
+// META: script=enctypes-helper.js
+
+// Form submissions in multipart/form-data are also tested in
+// /FileAPI/file/send-file*
+
+// The `expected` property of objects passed to `formTest` must be an object
+// with `name`, `value` and optionally `filename` properties, which represent
+// the corresponding data in a multipart/form-data part.
+const formTest = formSubmissionTemplate(
+ "multipart/form-data",
+ ({ name, filename, value }, serialized) => {
+ let headers;
+ if (filename === undefined) {
+ headers = [`Content-Disposition: form-data; name="${name}"`];
+ } else {
+ headers = [
+ `Content-Disposition: form-data; name="${name}"; filename="${filename}"`,
+ "Content-Type: text/plain",
+ ];
+ }
+
+ const boundary = serialized.split("\r\n")[0];
+
+ return [
+ boundary,
+ ...headers,
+ "",
+ value,
+ boundary + "--",
+ "",
+ ].join("\r\n");
+ },
+);
+
+formTest({
+ name: "basic",
+ value: "test",
+ expected: {
+ name: "basic",
+ value: "test",
+ },
+ description: "Basic test",
+});
+
+formTest({
+ name: "basic",
+ value: new File([], "file-test.txt", { type: "text/plain" }),
+ expected: {
+ name: "basic",
+ filename: "file-test.txt",
+ value: "",
+ },
+ description: "Basic File test",
+});
+
+formTest({
+ name: "a\0b",
+ value: "c",
+ expected: {
+ name: "a\0b",
+ value: "c",
+ },
+ description: "0x00 in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\0c",
+ expected: {
+ name: "a",
+ value: "b\0c",
+ },
+ description: "0x00 in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\0c", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b\0c",
+ value: "",
+ },
+ description: "0x00 in filename",
+});
+
+formTest({
+ name: "a\nb",
+ value: "c",
+ expected: {
+ name: "a%0D%0Ab",
+ value: "c",
+ },
+ description: "\\n in name",
+});
+
+formTest({
+ name: "a\rb",
+ value: "c",
+ expected: {
+ name: "a%0D%0Ab",
+ value: "c",
+ },
+ description: "\\r in name",
+});
+
+formTest({
+ name: "a\r\nb",
+ value: "c",
+ expected: {
+ name: "a%0D%0Ab",
+ value: "c",
+ },
+ description: "\\r\\n in name",
+});
+
+formTest({
+ name: "a\n\rb",
+ value: "c",
+ expected: {
+ name: "a%0D%0A%0D%0Ab",
+ value: "c",
+ },
+ description: "\\n\\r in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\nc",
+ expected: {
+ name: "a",
+ value: "b\r\nc",
+ },
+ description: "\\n in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\rc",
+ expected: {
+ name: "a",
+ value: "b\r\nc",
+ },
+ description: "\\r in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\r\nc",
+ expected: {
+ name: "a",
+ value: "b\r\nc",
+ },
+ description: "\\r\\n in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\n\rc",
+ expected: {
+ name: "a",
+ value: "b\r\n\r\nc",
+ },
+ description: "\\n\\r in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\nc", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b%0Ac",
+ value: "",
+ },
+ description: "\\n in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\rc", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b%0Dc",
+ value: "",
+ },
+ description: "\\r in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\r\nc", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b%0D%0Ac",
+ value: "",
+ },
+ description: "\\r\\n in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\n\rc", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b%0A%0Dc",
+ value: "",
+ },
+ description: "\\n\\r in filename",
+});
+
+formTest({
+ name: 'a"b',
+ value: "c",
+ expected: {
+ name: "a%22b",
+ value: "c",
+ },
+ description: "double quote in name",
+});
+
+formTest({
+ name: "a",
+ value: 'b"c',
+ expected: {
+ name: "a",
+ value: 'b"c',
+ },
+ description: "double quote in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], 'b"c', { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b%22c",
+ value: "",
+ },
+ description: "double quote in filename",
+});
+
+formTest({
+ name: "a'b",
+ value: "c",
+ expected: {
+ name: "a'b",
+ value: "c",
+ },
+ description: "single quote in name",
+});
+
+formTest({
+ name: "a",
+ value: "b'c",
+ expected: {
+ name: "a",
+ value: "b'c",
+ },
+ description: "single quote in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b'c", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b'c",
+ value: "",
+ },
+ description: "single quote in filename",
+});
+
+formTest({
+ name: "a\\b",
+ value: "c",
+ expected: {
+ name: "a\\b",
+ value: "c",
+ },
+ description: "backslash in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\\c",
+ expected: {
+ name: "a",
+ value: "b\\c",
+ },
+ description: "backslash in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\\c", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "b\\c",
+ value: "",
+ },
+ description: "backslash in filename",
+});
+
+formTest({
+ name: "áb",
+ value: "ç",
+ expected: {
+ name: "\xC3\xA1b",
+ value: "\xC3\xA7",
+ },
+ description: "non-ASCII in name and value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "É™.txt", { type: "text/plain" }),
+ expected: {
+ name: "a",
+ filename: "\xC9\x99.txt",
+ value: "",
+ },
+ description: "non-ASCII in filename",
+});
+
+formTest({
+ name: "aəb",
+ value: "c\uFFFDd",
+ formEncoding: "windows-1252",
+ expected: {
+ name: "a&#601;b",
+ value: "c&#65533;d",
+ },
+ description: "characters not in encoding in name and value",
+});
+
+formTest({
+ name: "á",
+ value: new File([], "💩", { type: "text/plain" }),
+ formEncoding: "windows-1252",
+ expected: {
+ name: "\xE1",
+ filename: "&#128169;",
+ value: "",
+ },
+ description: "character not in encoding in filename",
+});
+
+formTest({
+ name: "\uD800",
+ value: "\uD800",
+ formEncoding: "windows-1252",
+ expected: {
+ name: "&#65533;",
+ value: "&#65533;"
+ },
+ description: "lone surrogate in name and value",
+});
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/newline-normalization.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/newline-normalization.html
new file mode 100644
index 0000000000..2c83c5a1e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/newline-normalization.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>
+ Constructing the entry list shouldn't perform newline normalization
+ </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ function createForm(testCase, name, value) {
+ const form = document.createElement("form");
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = name;
+ input.value = value;
+ form.appendChild(input);
+ document.body.appendChild(form);
+ testCase.add_cleanup(() => {
+ document.body.removeChild(form);
+ });
+ return form;
+ }
+
+ function createFormWithFile(testCase, name, filename) {
+ const form = document.createElement("form");
+ const input = document.createElement("input");
+ input.type = "file";
+ input.name = name;
+ const dataTransfer = new DataTransfer();
+ dataTransfer.items.add(new File([], filename, { type: "text/plain" }));
+ input.files = dataTransfer.files;
+ form.appendChild(input);
+ document.body.appendChild(form);
+ testCase.add_cleanup(() => {
+ document.body.removeChild(form);
+ });
+ return form;
+ }
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a", "b\nc"));
+ assert_equals(formData.get("a"), "b\nc");
+ }, document.title + ": \\n in the value");
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a", "b\rc"));
+ assert_equals(formData.get("a"), "b\rc");
+ }, document.title + ": \\r in the value");
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a", "b\r\nc"));
+ assert_equals(formData.get("a"), "b\r\nc");
+ }, document.title + ": \\r\\n in the value");
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a", "b\n\rc"));
+ assert_equals(formData.get("a"), "b\n\rc");
+ }, document.title + ": \\n\\r in the value");
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a\nb", "c"));
+ assert_equals([...formData][0][0], "a\nb");
+ }, document.title + ": \\n in the name");
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a\rb", "c"));
+ assert_equals([...formData][0][0], "a\rb");
+ }, document.title + ": \\r in the name");
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a\r\nb", "c"));
+ assert_equals([...formData][0][0], "a\r\nb");
+ }, document.title + ": \\r\\n in the name");
+
+ test((testCase) => {
+ const formData = new FormData(createForm(testCase, "a\n\rb", "c"));
+ assert_equals([...formData][0][0], "a\n\rb");
+ }, document.title + ": \\n\\r in the name");
+
+ test((testCase) => {
+ const formData = new FormData(
+ createFormWithFile(testCase, "a", "b\nc")
+ );
+ assert_equals(formData.get("a").name, "b\nc");
+ }, document.title + ": \\n in the filename");
+
+ test((testCase) => {
+ const formData = new FormData(
+ createFormWithFile(testCase, "a", "b\rc")
+ );
+ assert_equals(formData.get("a").name, "b\rc");
+ }, document.title + ": \\r in the filename");
+
+ test((testCase) => {
+ const formData = new FormData(
+ createFormWithFile(testCase, "a", "b\r\nc")
+ );
+ assert_equals(formData.get("a").name, "b\r\nc");
+ }, document.title + ": \\r\\n in the filename");
+
+ test((testCase) => {
+ const formData = new FormData(
+ createFormWithFile(testCase, "a", "b\n\rc")
+ );
+ assert_equals(formData.get("a").name, "b\n\rc");
+ }, document.title + ": \\n\\r in the filename");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html
new file mode 100644
index 0000000000..6b50bf599b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html
@@ -0,0 +1,15 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<iframe id="i" src="about:blank"></iframe>
+<script>
+async_test(t => {
+ var form = i.contentDocument.createElement('form');
+ form.action = '/common/blank.html';
+ i.contentDocument.body.appendChild(form);
+ i.onload = t.step_func_done(() => {});
+ form.submit();
+ new Document().prepend(form);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/request-submit-activation.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/request-submit-activation.html
new file mode 100644
index 0000000000..0d1e54daf3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/request-submit-activation.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/targetted-form.js"></script>
+<body>
+<script>
+promise_test(async () => {
+ let form = populateForm('<input type=submit name=n1 value=v1><button type=submit name=n2 value=v2></button>');
+ let submitter = form.querySelector('button');
+ let iframe = form.previousSibling;
+ let event;
+ form.requestSubmit(submitter);
+ await loadPromise(iframe);
+ assert_true(iframe.contentWindow.location.search.indexOf('n1=v1') == -1, "n1=v1");
+ assert_true(iframe.contentWindow.location.search.indexOf('n2=v2') > 0), "n2=v2";
+}, 'Test activation of submitter for requestSubmit');
+
+promise_test(async () => {
+ let form = populateForm('<input type=submit name=n1 value=v1><button type=submit name=n2 value=v2></button>');
+ let submitter = form.querySelector('input');
+ let iframe = form.previousSibling;
+ let event;
+ form.requestSubmit(submitter);
+ await loadPromise(iframe);
+ assert_true(iframe.contentWindow.location.search.indexOf('n1=v1') > 0, "n1=v1");
+ assert_true(iframe.contentWindow.location.search.indexOf('n2=v2') == -1), "n2=v2";
+}, 'Test activation of submitter for requestSubmit 2');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/file-submission.py b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/file-submission.py
new file mode 100644
index 0000000000..89cd182add
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/file-submission.py
@@ -0,0 +1,10 @@
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/html")]
+ testinput = request.POST.first(b"testinput")
+ value = isomorphic_decode(testinput.value)
+ body = u"<script>parent.postMessage(" + json.dumps(value) + u", '*');</script>"
+ return headers, body
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form-submission.py b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form-submission.py
new file mode 100644
index 0000000000..f0c2d4cf61
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form-submission.py
@@ -0,0 +1,12 @@
+def main(request, response):
+ if request.headers.get(b'Content-Type') == b'application/x-www-form-urlencoded':
+ result = request.body == b'foo=bara'
+ elif request.headers.get(b'Content-Type') == b'text/plain':
+ result = request.body == b'qux=baz\r\n'
+ else:
+ result = request.POST.first(b'foo') == b'bar'
+
+ result = result and request.url_parts.query == u'query=1'
+
+ return ([(b"Content-Type", b"text/plain")],
+ b"OK" if result else b"FAIL")
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form.html
new file mode 100644
index 0000000000..8b16672d6b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/form.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+ form.html
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/getactionurl-iframe.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/getactionurl-iframe.html
new file mode 100644
index 0000000000..116371a995
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/getactionurl-iframe.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<body>
+<script>
+ const form = document.createElement('form');
+ document.body.appendChild(form);
+ form.action = `data:text/html,
+ <!DOCTYPE html>
+ <body>
+ <script>
+ window.top.postMessage('PASS', '*');
+ <\/script>
+ `;
+ form.submit();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/jsurl-form-submit-iframe.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/jsurl-form-submit-iframe.html
new file mode 100644
index 0000000000..00a1eefd0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/jsurl-form-submit-iframe.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<form id=formid action="form.html"></form>
+<a id=anchorid href="javascript:jsurl()">anchor</a>
+
+<script>
+function jsurl() {
+ formid.submit();
+ return 'jsurl return value';
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/location.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/location.html
new file mode 100644
index 0000000000..6724189eff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/location.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+ location.html
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/targetted-form.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/targetted-form.js
new file mode 100644
index 0000000000..52482c859f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/resources/targetted-form.js
@@ -0,0 +1,38 @@
+let frameCounter = 0;
+
+function populateForm(optionalContentHtml) {
+ if (!optionalContentHtml)
+ optionalContentHtml = '';
+ const frameName = "form-test-target-" + frameCounter++;
+ document.body.insertAdjacentHTML(
+ 'afterbegin',
+ `<iframe name="${frameName}"></iframe>` +
+ `<form action="/common/blank.html" target="` +
+ `${frameName}">${optionalContentHtml}</form>`);
+ return document.getElementsByName(frameName)[0].nextSibling;
+}
+
+function submitPromise(form, iframe) {
+ return new Promise((resolve, reject) => {
+ iframe.onload = () => resolve(iframe.contentWindow.location.search);
+ iframe.onerror = () => reject(new Error('iframe onerror fired'));
+ form.submit();
+ });
+}
+
+function loadPromise(iframe) {
+ return new Promise((resolve, reject) => {
+ iframe.onload = function() {
+ // The initial about:blank load event can be fired before the form navigation occurs.
+ // See https://github.com/whatwg/html/issues/490 for more information.
+ if (iframe.contentWindow.location == "about:blank") { return; }
+ resolve();
+ };
+ iframe.onerror = () => reject(new Error('iframe onerror fired'));
+ });
+}
+
+function getParamValue(iframe, paramName) {
+ let params = (new URL(iframe.contentWindow.location)).searchParams;
+ return params.get(paramName);
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/submission-checks.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/submission-checks.window.js
new file mode 100644
index 0000000000..e242ce830a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/submission-checks.window.js
@@ -0,0 +1,62 @@
+async_test(t => {
+ const frame = document.createElement("frame"),
+ form = document.createElement("form");
+ t.add_cleanup(() => frame.remove());
+ form.action = "/common/blank.html";
+ form.target = "doesnotmattertwobits";
+ frame.name = "doesnotmattertwobits";
+ document.body.appendChild(frame);
+ frame.onload = t.step_func(() => {
+ if(frame.contentWindow.location.href === "about:blank")
+ return;
+ assert_unreached();
+ });
+ form.submit();
+ t.step_timeout(() => {
+ assert_equals(frame.contentWindow.location.href, "about:blank");
+ t.done();
+ }, 500);
+}, "<form> not connected to a document cannot navigate");
+
+async_test(t => {
+ const frame = document.createElement("frame"),
+ form = document.createElement("form");
+ t.add_cleanup(() => frame.remove());
+ form.action = "/common/blank.html";
+ form.target = "doesnotmattertwoqbits";
+ form.onsubmit = t.step_func(() => form.remove());
+ frame.name = "doesnotmattertwoqbits";
+ document.body.appendChild(frame);
+ document.body.appendChild(form);
+ frame.onload = t.step_func(() => {
+ if(frame.contentWindow.location.href === "about:blank")
+ return;
+ assert_unreached();
+ });
+ const submit = form.appendChild(document.createElement("input"));
+ submit.type = "submit"
+ submit.click();
+ t.step_timeout(() => {
+ assert_equals(frame.contentWindow.location.href, "about:blank");
+ t.done();
+ }, 500);
+}, "<form> not connected to a document after submit event cannot navigate");
+
+async_test(t => {
+ const frame = document.createElement("frame"),
+ form = document.createElement("form");
+ t.add_cleanup(() => frame.remove());
+ form.action = "/";
+ document.body.appendChild(frame);
+ frame.contentDocument.body.appendChild(form);
+ frame.onload = t.step_func(() => {
+ if(frame.contentWindow.location.href === "about:blank")
+ return;
+ form.submit();
+ t.step_timeout(() => {
+ assert_equals(frame.contentWindow.location.pathname, "/common/blank.html");
+ t.done();
+ }, 500)
+ });
+ frame.src = "/common/blank.html";
+}, "<form> in a navigated document cannot navigate");
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-entity-body.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-entity-body.html
new file mode 100644
index 0000000000..be9c5f0696
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-entity-body.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var simple_tests = [
+ {
+ name: "form submission from form should navigate to url with x-www-form-urlencoded",
+ input: "<input name=foo value=bara>",
+ enctype: "application/x-www-form-urlencoded",
+ submitelement: "",
+ submitaction: function(doc) { doc.getElementById("testform").submit(); }
+ },
+ {
+ name: "form submission from form should navigate to url with multipart/form-data",
+ input: "<textarea name=foo>bar</textarea>",
+ enctype: "multipart/form-data",
+ submitelement: "",
+ submitaction: function(doc) { doc.getElementById("testform").submit(); }
+ },
+ {
+ name: "form submission from form should navigate to url with text/plain",
+ input: "<textarea name=qux>baz</textarea>",
+ enctype: "text/plain",
+ submitelement: "",
+ submitaction: function(doc) { doc.getElementById("testform").submit(); }
+ },
+ {
+ name: "form submission from button should navigate to url with x-www-form-urlencoded",
+ input: "<input name=foo value=bara>",
+ enctype: "application/x-www-form-urlencoded",
+ submitelement: "<button id=buttonsubmit type=\"submit\">Submit</button>",
+ submitaction: function(doc) { doc.getElementById("buttonsubmit").click(); }
+ },
+ {
+ name: "form submission from button should navigate to url with multipart/form-data",
+ input: "<textarea name=foo>bar</textarea>",
+ enctype: "multipart/form-data",
+ submitelement: "<button id=buttonsubmit type=\"submit\">Submit</button>",
+ submitaction: function(doc) { doc.getElementById("buttonsubmit").click(); }
+ },
+ {
+ name: "form submission from button should navigate to url with text/plain",
+ input: "<textarea name=qux>baz</textarea>",
+ enctype: "text/plain",
+ submitelement: "<button id=buttonsubmit type=\"submit\">Submit</button>",
+ submitaction: function(doc) { doc.getElementById("buttonsubmit").click(); }
+ },
+ {
+ name: "form submission from input should navigate to url with x-www-form-urlencoded",
+ input: "<input name=foo value=bara>",
+ enctype: "application/x-www-form-urlencoded",
+ submitelement: "<input id=inputsubmit type=\"submit\">Submit</input>",
+ submitaction: function(doc) { doc.getElementById("inputsubmit").click(); }
+ },
+ {
+ name: "form submission from input should navigate to url with multipart/form-data",
+ input: "<textarea name=foo>bar</textarea>",
+ enctype: "multipart/form-data",
+ submitelement: "<input id=inputsubmit type=\"submit\">Submit</input>",
+ submitaction: function(doc) { doc.getElementById("inputsubmit").click(); }
+ },
+ {
+ name: "form submission from input should navigate to url with text/plain",
+ input: "<input name=qux value=baz>",
+ enctype: "text/plain",
+ submitelement: "<input id=inputsubmit type=\"submit\">Submit</input>",
+ submitaction: function(doc) { doc.getElementById("inputsubmit").click(); }
+ },
+ {
+ name: "form submission from submit input should contain submit button value",
+ input: "<button type=submit name=notclicked value=nope>not clicked</button>",
+ enctype: "application/x-www-form-urlencoded",
+ submitelement: "<button id=inputsubmit type=\"submit\" name=foo value=bara>Submit</button>",
+ submitaction: function(doc) { doc.getElementById("inputsubmit").click(); }
+ }
+,
+ {
+ name: "form submission from submit button should contain submit button value",
+ input: "<input type=submit name=notclicked value=nope/>",
+ enctype: "application/x-www-form-urlencoded",
+ submitelement: "<input id=inputsubmit type=\"submit\" name=foo value=bara >",
+ submitaction: function(doc) { doc.getElementById("inputsubmit").click(); }
+ }
+];
+simple_tests.forEach(function(test_obj) {
+ test_obj.test = async_test(test_obj.name);
+});
+function run_simple_test() {
+ if (simple_tests.length == 0) {
+ return;
+ }
+ var test_obj = simple_tests.pop();
+ var t = test_obj.test;
+ var testframe = document.getElementById("testframe");
+ var testdocument = testframe.contentWindow.document;
+ testdocument.body.innerHTML =
+ "<form id=testform method=post action=\"/html/semantics/forms/form-submission-0/resources/form-submission.py?query=1\" enctype=\"" + test_obj.enctype + "\">" +
+ test_obj.input +
+ test_obj.submitelement +
+ "</form>";
+ testframe.onload = function() {
+ t.step(function (){
+ var response = testframe.contentDocument.documentElement.textContent;
+ assert_equals(response, "OK");
+ });
+ t.done();
+ run_simple_test();
+ };
+ test_obj.submitaction(testdocument);
+}
+</script>
+<iframe id=testframe src="/common/blank.html" onload="run_simple_test();"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-file.sub.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-file.sub.html
new file mode 100644
index 0000000000..aab60ba949
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/submit-file.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<iframe id=testframe name=testframe></iframe>
+<form id=testform method=post action="//{{domains[www1]}}:{{location[port]}}/html/semantics/forms/form-submission-0/resources/file-submission.py" target=testframe enctype="multipart/form-data">
+<input name=testinput id=testinput type=file>
+</form>
+<script>
+async_test(t => {
+ const dataTransfer = new DataTransfer();
+ dataTransfer.items.add(new File(["foobar"], "name"));
+ assert_equals(1, dataTransfer.files.length);
+
+ testinput.files = dataTransfer.files;
+ testform.submit();
+
+ onmessage = t.step_func(e => {
+ if (e.source !== testframe) return;
+ assert_equals(e.data, "foobar");
+ t.done();
+ });
+}, 'Posting a File');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/text-plain.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/text-plain.window.js
new file mode 100644
index 0000000000..9ff77aed12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/text-plain.window.js
@@ -0,0 +1,223 @@
+// META: script=enctypes-helper.js
+
+const formTest = formSubmissionTemplate("text/plain");
+
+formTest({
+ name: "basic",
+ value: "test",
+ expected: "basic=test\r\n",
+ description: "Basic test",
+});
+
+formTest({
+ name: "basic",
+ value: new File([], "file-test.txt"),
+ expected: "basic=file-test.txt\r\n",
+ description: "Basic File test",
+});
+
+formTest({
+ name: "a\0b",
+ value: "c",
+ expected: "a\0b=c\r\n",
+ description: "0x00 in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\0c",
+ expected: "a=b\0c\r\n",
+ description: "0x00 in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\0c"),
+ expected: "a=b\0c\r\n",
+ description: "0x00 in filename",
+});
+
+formTest({
+ name: "a\nb",
+ value: "c",
+ expected: "a\r\nb=c\r\n",
+ description: "\\n in name",
+});
+
+formTest({
+ name: "a\rb",
+ value: "c",
+ expected: "a\r\nb=c\r\n",
+ description: "\\r in name",
+});
+
+formTest({
+ name: "a\r\nb",
+ value: "c",
+ expected: "a\r\nb=c\r\n",
+ description: "\\r\\n in name",
+});
+
+formTest({
+ name: "a\n\rb",
+ value: "c",
+ expected: "a\r\n\r\nb=c\r\n",
+ description: "\\n\\r in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\nc",
+ expected: "a=b\r\nc\r\n",
+ description: "\\n in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\rc",
+ expected: "a=b\r\nc\r\n",
+ description: "\\r in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\r\nc",
+ expected: "a=b\r\nc\r\n",
+ description: "\\r\\n in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\n\rc",
+ expected: "a=b\r\n\r\nc\r\n",
+ description: "\\n\\r in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\nc"),
+ expected: "a=b\r\nc\r\n",
+ description: "\\n in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\rc"),
+ expected: "a=b\r\nc\r\n",
+ description: "\\r in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\r\nc"),
+ expected: "a=b\r\nc\r\n",
+ description: "\\r\\n in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\n\rc"),
+ expected: "a=b\r\n\r\nc\r\n",
+ description: "\\n\\r in filename",
+});
+
+formTest({
+ name: 'a"b',
+ value: "c",
+ expected: 'a"b=c\r\n',
+ description: "double quote in name",
+});
+
+formTest({
+ name: "a",
+ value: 'b"c',
+ expected: 'a=b"c\r\n',
+ description: "double quote in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], 'b"c'),
+ expected: 'a=b"c\r\n',
+ description: "double quote in filename",
+});
+
+formTest({
+ name: "a'b",
+ value: "c",
+ expected: "a'b=c\r\n",
+ description: "single quote in name",
+});
+
+formTest({
+ name: "a",
+ value: "b'c",
+ expected: "a=b'c\r\n",
+ description: "single quote in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b'c"),
+ expected: "a=b'c\r\n",
+ description: "single quote in filename",
+});
+
+formTest({
+ name: "a\\b",
+ value: "c",
+ expected: "a\\b=c\r\n",
+ description: "backslash in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\\c",
+ expected: "a=b\\c\r\n",
+ description: "backslash in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\\c"),
+ expected: "a=b\\c\r\n",
+ description: "backslash in filename",
+});
+
+formTest({
+ name: "áb",
+ value: "ç",
+ expected: "\xC3\xA1b=\xC3\xA7\r\n",
+ description: "non-ASCII in name and value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "É™.txt"),
+ expected: "a=\xC9\x99.txt\r\n",
+ description: "non-ASCII in filename",
+});
+
+formTest({
+ name: "aəb",
+ value: "c\uFFFDd",
+ formEncoding: "windows-1252",
+ expected: "a&#601;b=c&#65533;d\r\n",
+ description: "characters not in encoding in name and value",
+});
+
+formTest({
+ name: "á",
+ value: new File([], "💩"),
+ formEncoding: "windows-1252",
+ expected: "\xE1=&#128169;\r\n",
+ description: "character not in encoding in filename",
+});
+
+formTest({
+ name: "\uD800",
+ value: "\uD800",
+ formEncoding: "windows-1252",
+ expected: "&#65533;=&#65533;\r\n",
+ description: "lone surrogate in name and value",
+});
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/url-encoded.html b/testing/web-platform/tests/html/semantics/forms/form-submission-0/url-encoded.html
new file mode 100644
index 0000000000..d05364387e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/url-encoded.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id=testframe src="/common/blank.html"></iframe>
+<script>
+var simple_tests = [
+ {
+ name: "text.simple",
+ input: "<input name=foo value=bara>",
+ output: "foo=bara"
+ },
+ {
+ name: "textarea.simple",
+ input: "<textarea name=foo>bar</textarea>",
+ output: "foo=bar"
+ },
+ {
+ name: "nokeygen.simple",
+ input: "<input name=foo value=barb><keygen>",
+ output: "foo=barb"
+ }
+];
+simple_tests.forEach(function(test_obj) {
+ test_obj.test = async_test(test_obj.name);
+});
+function run_simple_test() {
+ if (simple_tests.length == 0) {
+ return;
+ }
+ test_obj = simple_tests.pop();
+ var t = test_obj.test;
+ var testframe = document.getElementById("testframe");
+ var testdocument = testframe.contentWindow.document;
+ testdocument.body.innerHTML =
+ "<form id=testform action=\"/common/blank.html\">" +
+ test_obj.input +
+ "</form>";
+ testframe.onload = function() {
+ t.step(function (){
+ var get_url = testframe.contentWindow.location.toString();
+ var encoded = get_url.substr(get_url.indexOf("?") + 1);
+ assert_equals(encoded, test_obj.output);
+ });
+ t.done();
+ run_simple_test();
+ };
+ testdocument.getElementById("testform").submit();
+}
+document.getElementById("testframe").onload = run_simple_test;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-0/urlencoded2.window.js b/testing/web-platform/tests/html/semantics/forms/form-submission-0/urlencoded2.window.js
new file mode 100644
index 0000000000..7818f68619
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-0/urlencoded2.window.js
@@ -0,0 +1,223 @@
+// META: script=enctypes-helper.js
+
+const formTest = formSubmissionTemplate("application/x-www-form-urlencoded");
+
+formTest({
+ name: "basic",
+ value: "test",
+ expected: "basic=test",
+ description: "Basic test",
+});
+
+formTest({
+ name: "basic",
+ value: new File([], "file-test.txt"),
+ expected: "basic=file-test.txt",
+ description: "Basic File test",
+});
+
+formTest({
+ name: "a\0b",
+ value: "c",
+ expected: "a%00b=c",
+ description: "0x00 in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\0c",
+ expected: "a=b%00c",
+ description: "0x00 in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\0c"),
+ expected: "a=b%00c",
+ description: "0x00 in filename",
+});
+
+formTest({
+ name: "a\nb",
+ value: "c",
+ expected: "a%0D%0Ab=c",
+ description: "\\n in name",
+});
+
+formTest({
+ name: "a\rb",
+ value: "c",
+ expected: "a%0D%0Ab=c",
+ description: "\\r in name",
+});
+
+formTest({
+ name: "a\r\nb",
+ value: "c",
+ expected: "a%0D%0Ab=c",
+ description: "\\r\\n in name",
+});
+
+formTest({
+ name: "a\n\rb",
+ value: "c",
+ expected: "a%0D%0A%0D%0Ab=c",
+ description: "\\n\\r in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\nc",
+ expected: "a=b%0D%0Ac",
+ description: "\\n in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\rc",
+ expected: "a=b%0D%0Ac",
+ description: "\\r in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\r\nc",
+ expected: "a=b%0D%0Ac",
+ description: "\\r\\n in value",
+});
+
+formTest({
+ name: "a",
+ value: "b\n\rc",
+ expected: "a=b%0D%0A%0D%0Ac",
+ description: "\\n\\r in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\nc"),
+ expected: "a=b%0D%0Ac",
+ description: "\\n in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\rc"),
+ expected: "a=b%0D%0Ac",
+ description: "\\r in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\r\nc"),
+ expected: "a=b%0D%0Ac",
+ description: "\\r\\n in filename",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\n\rc"),
+ expected: "a=b%0D%0A%0D%0Ac",
+ description: "\\n\\r in filename",
+});
+
+formTest({
+ name: 'a"b',
+ value: "c",
+ expected: "a%22b=c",
+ description: "double quote in name",
+});
+
+formTest({
+ name: "a",
+ value: 'b"c',
+ expected: "a=b%22c",
+ description: "double quote in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], 'b"c'),
+ expected: "a=b%22c",
+ description: "double quote in filename",
+});
+
+formTest({
+ name: "a'b",
+ value: "c",
+ expected: "a%27b=c",
+ description: "single quote in name",
+});
+
+formTest({
+ name: "a",
+ value: "b'c",
+ expected: "a=b%27c",
+ description: "single quote in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b'c"),
+ expected: "a=b%27c",
+ description: "single quote in filename",
+});
+
+formTest({
+ name: "a\\b",
+ value: "c",
+ expected: "a%5Cb=c",
+ description: "backslash in name",
+});
+
+formTest({
+ name: "a",
+ value: "b\\c",
+ expected: "a=b%5Cc",
+ description: "backslash in value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "b\\c"),
+ expected: "a=b%5Cc",
+ description: "backslash in filename",
+});
+
+formTest({
+ name: "áb",
+ value: "ç",
+ expected: "%C3%A1b=%C3%A7",
+ description: "non-ASCII in name and value",
+});
+
+formTest({
+ name: "a",
+ value: new File([], "É™.txt"),
+ expected: "a=%C9%99.txt",
+ description: "non-ASCII in filename",
+});
+
+formTest({
+ name: "aəb",
+ value: "c\uFFFDd",
+ formEncoding: "windows-1252",
+ expected: "a%26%23601%3Bb=c%26%2365533%3Bd",
+ description: "characters not in encoding in name and value",
+});
+
+formTest({
+ name: "á",
+ value: new File([], "💩"),
+ formEncoding: "windows-1252",
+ expected: "%E1=%26%23128169%3B",
+ description: "character not in encoding in filename",
+});
+
+formTest({
+ name: "\uD800",
+ value: "\uD800",
+ formEncoding: "windows-1252",
+ expected: "%26%2365533%3B=%26%2365533%3B",
+ description: "lone surrogate in name and value",
+});
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe-helper.py b/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe-helper.py
new file mode 100644
index 0000000000..bbc3b71e94
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe-helper.py
@@ -0,0 +1,3 @@
+def main(request, response):
+ return ([(b"Content-Type", b"text/plain")],
+ b"OK")
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe.html b/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe.html
new file mode 100644
index 0000000000..f37bc33f6f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-iframe.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Form targetted at iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+ window.addEventListener("load", t.step_func(function() {
+ var frame = document.createElement("iframe");
+ frame.name = "frame";
+ document.documentElement.appendChild(frame);
+ var form = document.createElement("form");
+ form.target = "frame";
+ form.action = "form-target-iframe-helper.py";
+ form.method = "POST";
+ var input = document.createElement("input");
+ input.name = "n";
+ form.appendChild(input);
+ document.documentElement.appendChild(form);
+ form.submit();
+ frame.addEventListener("load", t.step_func(function() {
+ if (frame.contentWindow.location.href.includes("form-target-iframe-helper.py")) {
+ assert_equals(frame.contentWindow.document.body.textContent, "OK");
+ t.done();
+ }
+ }));
+ }));
+}, "Form targetted at iframe");
+</script>
+<body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-request-header.html b/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-request-header.html
new file mode 100644
index 0000000000..a5cd68078d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/form-target-request-header.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Form request header test</title>
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.addEventListener("load", function() {
+ let form = document.createElement("form");
+ form.action = "resources/form-target-request-header-helper.py";
+ form.method = "post";
+ form.target = "_blank";
+
+ const channelName = token();
+ const channel = new BroadcastChannel(channelName);
+ channel.onmessage = t.step_func_done(e => {
+ assert_equals(e.data, "OK");
+ });
+
+ let url_input = document.createElement("input");
+ url_input.type = "hidden";
+ url_input.name = "channelname";
+ url_input.value = channelName;
+
+ form.appendChild(url_input);
+ document.body.appendChild(form);
+ form.submit();
+ });
+}, 'Verify the content-type exist during a form submission toward "_blank"');
+</script>
+<body>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-base-target.html b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-base-target.html
new file mode 100644
index 0000000000..222be95d2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-base-target.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>&lt;form rel> with &lt;base target></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=resources/reltester.js></script>
+<base target=_blank>
+<div id=log></div>
+<form action=resources/endpoint.html><input type=hidden name=channelname></form>
+<script>
+const submitter = document.querySelector("form"),
+ channelInput = document.querySelector("input");
+relTester(submitter, channelInput, "<base target>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-button-target.html b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-button-target.html
new file mode 100644
index 0000000000..76fa868590
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-button-target.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>&lt;form rel> with &lt;button formtarget></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=resources/reltester.js></script>
+<div id=log></div>
+<form action=resources/endpoint.html><input type=hidden name=channelname><button type=submit formtarget=_blank></form>
+<script>
+const submitter = document.querySelector("button"),
+ channelInput = document.querySelector("input");
+relTester(submitter, channelInput, "<button formtarget>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-form-target.html b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-form-target.html
new file mode 100644
index 0000000000..58611f41a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-form-target.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>&lt;form rel target></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=resources/reltester.js></script>
+<div id=log></div>
+<form action=resources/endpoint.html target=_blank><input type=hidden name=channelname></form>
+<script>
+const submitter = document.querySelector("form"),
+ channelInput = document.querySelector("input");
+relTester(submitter, channelInput, "<form target>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-input-target.html b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-input-target.html
new file mode 100644
index 0000000000..b80e0240ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/rel-input-target.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>&lt;form rel> with &lt;input formtarget></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=resources/reltester.js></script>
+<base target=_blank>
+<div id=log></div>
+<form action=resources/endpoint.html><input type=hidden name=channelname><input type=submit formtarget=_blank></form>
+<script>
+const submitter = document.querySelector("input[type=submit]"),
+ channelInput = document.querySelector("input");
+relTester(submitter, channelInput, "<input formtarget>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/endpoint.html b/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/endpoint.html
new file mode 100644
index 0000000000..be9e794292
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/endpoint.html
@@ -0,0 +1,11 @@
+<script>
+ const channelName = new URL(location).searchParams.get("channelname"),
+ channel = new BroadcastChannel(channelName);
+ channel.postMessage({ haveOpener: window.opener !== null,
+ referrer: document.referrer });
+ // Because messages are not delivered synchronously and because closing a
+ // browsing context prompts the eventual clearing of all task sources, this
+ // document should not be closed until the opener document has confirmed
+ // receipt.
+ channel.onmessage = () => window.close();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/form-target-request-header-helper.py b/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/form-target-request-header-helper.py
new file mode 100644
index 0000000000..80770167a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/form-target-request-header-helper.py
@@ -0,0 +1,14 @@
+body_template="""
+<script>
+const channel = new BroadcastChannel('{}');
+channel.postMessage('{}', '*');
+window.close();
+</script>
+"""
+def main(request, response):
+ has_content_type = bool(request.headers.get(b'Content-Type'))
+ result = u"OK" if has_content_type else u"FAIL"
+ channel_name = request.body.decode('utf-8').split("=")[1];
+ body = body_template.format(channel_name, result);
+ headers = [(b"Content-Type", b"text/html")]
+ return headers, body
diff --git a/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/reltester.js b/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/reltester.js
new file mode 100644
index 0000000000..8ca9ddbc27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/form-submission-target/resources/reltester.js
@@ -0,0 +1,82 @@
+function formUsesTargetBlank(submitter) {
+ if (submitter.formTarget && submitter.formTarget === "_blank") {
+ return true;
+ }
+ if (submitter.form && submitter.form.target === "_blank") {
+ return true;
+ }
+ if (submitter.target && submitter.target === "_blank") {
+ return true;
+ }
+ if (submitter.getRootNode().querySelector("base").target === "_blank") {
+ return true;
+ }
+ return false;
+}
+
+function relTester(submitter, channelInput, title) {
+ [
+ {
+ rel: "",
+ exposed: "all"
+ },
+ {
+ rel: "noopener",
+ exposed: "noopener"
+ },
+ {
+ rel: "noreferrer",
+ exposed: "noreferrer"
+ },
+ {
+ rel: "opener",
+ exposed: "all"
+ },
+ {
+ rel: "noopener noreferrer",
+ exposed: "noreferrer"
+ },
+ {
+ rel: "noreferrer opener",
+ exposed: "noreferrer"
+ },
+ {
+ rel: "opener noopener",
+ exposed: "noopener"
+ }
+ ].forEach(relTest => {
+ // Use promise_test to submit only after one test concluded
+ promise_test(t => {
+ return new Promise(resolve => {
+ const channelName = Date.now() + relTest.rel,
+ channel = new BroadcastChannel(channelName);
+ let form = submitter;
+ if (submitter.localName !== "form") {
+ form = submitter.form;
+ }
+ form.rel = relTest.rel;
+ channelInput.value = channelName;
+ if (submitter.localName !== "form") {
+ submitter.click();
+ } else {
+ submitter.submit();
+ }
+ channel.onmessage = t.step_func(e => {
+ if (relTest.exposed === "all" || relTest.exposed === "noopener") {
+ assert_equals(e.data.referrer, window.location.href, "referrer");
+ } else {
+ assert_equals(e.data.referrer, "", "referrer");
+ }
+ // When rel is not explicitly given, account for target=_blank defaulting to noopener
+ if (relTest.exposed === "all" && !(relTest.rel === "" && formUsesTargetBlank(submitter))) {
+ assert_true(e.data.haveOpener, "opener");
+ } else {
+ assert_false(e.data.haveOpener, "opener");
+ }
+ resolve();
+ });
+ t.add_cleanup(() => channel.postMessage(null));
+ });
+ }, `<form rel="${relTest.rel}"> with ${title}`);
+ });
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/historical-search-event.html b/testing/web-platform/tests/html/semantics/forms/historical-search-event.html
new file mode 100644
index 0000000000..b7a089a68e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/historical-search-event.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>search event should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<input id=input type=search incremental>
+<script>
+promise_test(async t => {
+ const input = document.getElementById('input');
+ const eventWatcher = new EventWatcher(t, input, ['search', 'keypress']);
+ await Promise.all([
+ test_driver.send_keys(input, 'x'),
+ eventWatcher.wait_for(['keypress'])
+ ]);
+ // During this timeout, the search event will fire, if it's supported,
+ // which fails the test since the event watcher isn't expecting it.
+ await new Promise(resolve => t.step_timeout(resolve, 1000));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/historical.html b/testing/web-platform/tests/html/semantics/forms/historical.html
new file mode 100644
index 0000000000..277e124161
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/historical.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<title>Historical forms features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<form id=form hidden>
+ <label id=label></label>
+ <input id=input>
+ <button id=button></button>
+ <select id=select>
+ <optgroup id=optgroup>
+ <option id=option>
+ </select>
+ <datalist id=datalist></datalist>
+ <textarea id=textarea></textarea>
+ <progress id=progress></progress>
+ <meter id=meter></meter>
+ <fieldset id=fieldset>
+ <legend id=legend></legend>
+ </fieldset>
+</form>
+
+<form hidden action="isindex-support.txt" target=isindex_iframe id=isindex_form>
+ <input name=isindex value=x>
+ <iframe name=isindex_iframe id=isindex_iframe></iframe>
+</form>
+<script>
+var tags = ['form', 'label', 'input', 'button', 'select', 'datalist',
+'optgroup', 'option', 'textarea', 'progress', 'meter', 'fieldset', 'legend'];
+
+function t(property, tagName) {
+ var tagNames = tagName ? [tagName] : tags;
+ tagNames.forEach(function(tagName) {
+ test(function() {
+ assert_false(property in document.getElementById(tagName));
+ }, tagName + '.' + property + ' should not be supported');
+ });
+}
+
+function inputType(type) {
+ test(function() {
+ var input = document.createElement('input');
+ input.type = type;
+ assert_equals(input.type, 'text');
+ }, '<input type=' + type + '> should not be supported');
+}
+
+// <input type=range multiple>
+// added in https://github.com/whatwg/html/commit/1efac390abb3f95df61f2d2ac6c0feb47349d97b
+// removed in https://github.com/whatwg/html/commit/b598d4f873fb8c27d4b23b033837108edfbc3d75
+t('valueLow', 'input');
+t('valueHigh', 'input');
+
+// requestAutoComplete()
+// added in https://github.com/whatwg/html/commit/321659e4db11228857632487ab72b6959db1ba86
+// removed in https://github.com/whatwg/html/commit/6a257aae619f85390eee20b47767f34887450fcd
+t('requestAutocomplete', 'form');
+t('onautocomplete', 'form');
+t('onautocompleteerror', 'form');
+
+// <input type=datetime>
+// added in WF2
+// removed in https://github.com/whatwg/html/commit/80ba4fa24e5d3d81a10aa1bbd8a2f72f4bcc3f7c
+inputType('datetime');
+
+// <progress form>, <meter form>
+// removed in https://github.com/whatwg/html/commit/3814376a311837ddfac229d9a631cd10adf53157
+t('form', 'progress');
+t('form', 'meter');
+
+// form.item(), form.namedItem()
+// removed in https://github.com/whatwg/html/commit/da87ab9009d5aeca95a602e718439e35b00d0731
+t('item', 'form');
+t('namedItem', 'form');
+
+// Never specified but implemented in WebKit/Chromium
+test(() => {
+ assert_false("onsearch" in window);
+}, "window.onsearch should not be supported");
+
+test(() => {
+ assert_false("onsearch" in document);
+}, "document.onsearch should not be supported");
+
+t('onsearch', 'input');
+t('incremental', 'input');
+
+// <input name=isindex>
+// removed in https://github.com/whatwg/html/commit/5c44abc734eb483f9a7ec79da5844d2fe63d9c3b
+async_test(function() {
+ var iframe = document.getElementById('isindex_iframe');
+ iframe.onload = this.step_func_done(function() {
+ assert_regexp_match(iframe.contentWindow.location.href, /\?isindex=x$/);
+ });
+ document.getElementById('isindex_form').submit();
+}, '<input name=isindex> should not be supported');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/input-change-event-properties.html b/testing/web-platform/tests/html/semantics/forms/input-change-event-properties.html
new file mode 100644
index 0000000000..f3c6f7d462
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/input-change-event-properties.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test the properties of input and change events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<fieldset id="clickable">
+ <input type="checkbox">
+ <input type="radio">
+</fieldset>
+
+<fieldset id="typeable">
+ <input type="text">
+ <input type="search">
+ <input type="tel">
+ <input type="url">
+ <input type="email">
+ <input type="password">
+ <input type="number">
+ <textarea></textarea>
+</fieldset>
+
+<select>
+ <option>1</option>
+ <option>2</option>
+</select>
+
+<!-- Not going to test the more complicated input types like date or color for now... -->
+
+<button id="click-me-to-unfocus-other-things">Clickable</button>
+
+<script>
+"use strict";
+const clickMeToUnfocusOtherThings = document.querySelector("#click-me-to-unfocus-other-things");
+
+for (const el of document.querySelector("#clickable").children) {
+ test(() => {
+ let inputEvent, changeEvent;
+ el.oninput = e => inputEvent = e;
+ el.onchange = e => changeEvent = e;
+
+ el.click();
+
+ assert_event(inputEvent, { bubbles: true, cancelable: false, composed: true });
+ assert_event(changeEvent, { bubbles: true, cancelable: false, composed: false });
+ }, `${el.outerHTML} click()`);
+}
+
+for (const el of document.querySelector("#typeable").children) {
+ promise_test(async () => {
+ let inputEvent, changeEvent;
+ el.oninput = e => inputEvent = e;
+ el.onchange = e => changeEvent = e;
+
+ await test_driver.send_keys(el, "1"); // has to be a number so <input type=number> works
+ await test_driver.click(clickMeToUnfocusOtherThings);
+
+ assert_event(inputEvent, { bubbles: true, cancelable: false, composed: true });
+ assert_event(changeEvent, { bubbles: true, cancelable: false, composed: false });
+ }, `${el.outerHTML} typing`);
+}
+
+promise_test(async () => {
+ const el = document.querySelector("select");
+
+ let inputEvent, changeEvent;
+ el.oninput = e => inputEvent = e;
+ el.onchange = e => changeEvent = e;
+
+ // TODO: this doesn't seem to work in Safari/on Macs. Or maybe Safari is legitimately failing.
+ // Someone with a Mac should investigate.
+ await test_driver.send_keys(el, "\uE015"); // down arrow key
+ await test_driver.click(clickMeToUnfocusOtherThings);
+
+ assert_event(inputEvent, { bubbles: true, cancelable: false, composed: true });
+ assert_event(changeEvent, { bubbles: true, cancelable: false, composed: false });
+}, `<select> pressing down arrow`);
+
+function assert_event(e, { bubbles, cancelable, composed }) {
+ assert_equals(e.bubbles, bubbles, `${e.type} bubbles`);
+ assert_equals(e.cancelable, cancelable, `${e.type} cancelable`);
+ assert_equals(e.composed, composed, `${e.type} composed`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-event.html b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-event.html
new file mode 100644
index 0000000000..f7d98f7216
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-event.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Test aspects of the reset event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ async_test((t) => {
+ var form = document.createElement("form")
+ form.onreset = t.step_func_done((e) => {
+ assert_true(e.bubbles)
+ assert_true(e.cancelable)
+ assert_true(e.isTrusted)
+ assert_equals(e.target, form)
+ })
+ form.reset()
+ assert_unreached()
+ })
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-2.html b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-2.html
new file mode 100644
index 0000000000..6ce0040c4a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-2.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Resetting a form integration test</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#concept-form-reset">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+
+ const form = document.createElement("form");
+ const text = document.createElement("input");
+ text.type = "text";
+ const checkbox = document.createElement("input");
+ checkbox.type = "checkbox";
+ const select = document.createElement("select");
+ select.multiple = true;
+ const option = document.createElement("option");
+ option.value = "option";
+ const textarea = document.createElement("textarea");
+
+ form.appendChild(text);
+ form.appendChild(checkbox);
+ form.appendChild(textarea);
+ form.appendChild(select);
+ select.appendChild(option);
+
+ text.defaultValue = "text default";
+ checkbox.defaultChecked = true;
+ option.defaultSelected = true;
+ textarea.defaultValue = "textarea default";
+
+ text.value = "text new value";
+ checkbox.checked = false;
+ option.selected = false;
+ textarea.value = "textarea new value";
+
+ form.reset();
+
+ assert_equals(text.value, "text default", "input should reset value to default");
+ assert_equals(checkbox.checked, true, "input should reset checkedness to default");
+ assert_equals(option.selected, true, "second option should reset selectedness to default");
+ assert_equals(select.selectedIndex, 0, "second option should reset selectedness to default");
+ assert_equals(textarea.value, "textarea default", "textarea should reset api value to default");
+
+ text.defaultValue = "text new default";
+ checkbox.defaultChecked = false;
+ option.defaultSelected = false;
+ textarea.defaultValue = "textarea new default";
+
+ assert_equals(text.value, "text new default", "input should reset dirty value to false");
+ assert_equals(checkbox.checked, false, "input should reset dirty checkedness to false");
+ assert_equals(option.selected, false, "option should reset dirtyness to false");
+ assert_equals(select.selectedIndex, -1, "option should reset dirtyness to false");
+ assert_equals(textarea.value, "textarea new default", "textarea should reset dirty value to false");
+
+}, "integration test on reset for a created-from-script form");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-event-realm.html b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-event-realm.html
new file mode 100644
index 0000000000..6c125c46d0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form-event-realm.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>reset() event firing realm</title>
+<link rel="help" href="https://html.spec.whatwg.org/#resetting-a-form">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-event-fire">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="support/reset-form-event-realm.html"></iframe>
+<iframe></iframe>
+
+<script>
+"use strict";
+
+async_test(t => {
+ window.onload = t.step_func_done(() => {
+ const frame0Form = frames[0].document.forms[0];
+ const frame1Body = frames[1].document.body;
+
+ frame1Body.appendChild(frame0Form);
+
+ let resetCalled = false;
+ frame0Form.onreset = t.step_func(ev => {
+ resetCalled = true;
+
+ const functionConstructorInEvRealm = ev.constructor.constructor;
+ const functionConstructorInFormRealm = frame0Form.constructor.constructor;
+
+ assert_equals(functionConstructorInEvRealm, functionConstructorInFormRealm,
+ "the event must be created in the realm of the target");
+ });
+
+ frame0Form.reset();
+ assert_true(resetCalled, "The reset event handler must have been called");
+ });
+}, "reset()'s event must be fired in the Realm of the target")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form.html b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form.html
new file mode 100644
index 0000000000..c7ac3e085b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/reset-form.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Resetting a form</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#concept-form-reset">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#category-reset">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form name="fm1" style="display:none">
+ <input value="abc" id="ipt1" />
+ <input id="ipt2" />
+ <input type="radio" id="rd1" checked="checked" />
+ <input type="radio" id="rd2"/>
+ <input type="checkbox" id="cb1" checked="checked" />
+ <input type="checkbox" id="cb2" />
+ <textarea id="ta">abc</textarea>
+ <output id="opt">5</output>
+ <select id="slt1">
+ <option value="1">ITEM1</option>
+ <option value="2">ITEM2</option>
+ </select>
+ <select id="slt2">
+ <option value="1">ITEM1</option>
+ <option value="2" selected>ITEM2</option>
+ </select>
+ <select id="slt3" multiple>
+ <option value="1">ITEM1</option>
+ <option value="2" selected>ITEM2</option>
+ <option value="3" selected>ITEM3</option>
+ </select>
+ <button id="rst1" type="reset">Reset1</button>
+ <input id="rst2" type="reset" value="Reset2" />
+</form>
+<script>
+
+runTest(function() {
+ document.forms.fm1.reset();
+}, "by calling the reset() method");
+
+runTest(function() {
+ document.getElementById("rst1").click();
+}, "by clicking the button in reset status");
+
+runTest(function() {
+ document.getElementById("rst2").click();
+}, "by clicking the input in reset status");
+
+function setPreconditions (arg) {
+ document.getElementById("ipt1").value = "123";
+ document.getElementById("ipt2").value = "123";
+ document.getElementById("rd1").checked = false;
+ document.getElementById("rd2").checked = true;
+ document.getElementById("cb1").checked = false;
+ document.getElementById("cb2").checked = true;
+ document.getElementById("ta").value = "123";
+ document.getElementById("opt").textContent = "abc";
+ document.getElementById("slt1").value = "2";
+ document.getElementById("slt2").value = "1";
+ document.getElementById("slt3").options[0].selected = true;
+ document.getElementById("slt3").options[1].selected = false;
+ document.getElementById("slt3").options[2].selected = false;
+
+ assert_equals(document.getElementById("ipt1").value, "123", "Precondition 1");
+ assert_equals(document.getElementById("ipt2").value, "123", "Precondition 2");
+ assert_false(document.getElementById("rd1").checked, "Precondition 3");
+ assert_true(document.getElementById("rd2").checked, "Precondition 4");
+ assert_false(document.getElementById("cb1").checked, "Precondition 5");
+ assert_true(document.getElementById("cb2").checked, "Precondition 6");
+ assert_equals(document.getElementById("ta").value, "123", "Precondition 17");
+ assert_equals(document.getElementById("opt").textContent, "abc", "Precondition 8");
+ assert_true(document.getElementById("slt1").options[1].selected, "Precondition 9");
+ assert_true(document.getElementById("slt2").options[0].selected, "Precondition 10");
+ assert_true(document.getElementById("slt3").options[0].selected, "Precondition 11");
+ assert_false(document.getElementById("slt3").options[1].selected, "Precondition 12");
+ assert_false(document.getElementById("slt3").options[2].selected, "Precondition 13");
+}
+
+function runTest(reset, description) {
+ test(function() {
+ setPreconditions("Setting preconditions for resetting " + description);
+ reset();
+ assert_equals(document.getElementById("ipt1").value, "abc", "The value of the input element in text status should be 'abc'.");
+ assert_equals(document.getElementById("ipt2").value, "", "The value of the input element in text status should be empty string.");
+ assert_true(document.getElementById("rd1").checked, "The checked attribute of the input element in radio should be true.");
+ assert_false(document.getElementById("rd2").checked, "The checked attribute of the input element in radio should be false.");
+ assert_true(document.getElementById("cb1").checked, "The checked attribute of the input element in checkbox should be true.");
+ assert_false(document.getElementById("cb2").checked, "The checked attribute of the input element in checkbox should be false.");
+ }, "Resetting <input> " + description);
+
+ test(function() {
+ setPreconditions("Setting preconditions for resetting " + description);
+ reset();
+ assert_equals(document.getElementById("ta").value, document.getElementById("ta").textContent, "The value attribute of the textarea element should be reset.");
+ assert_equals(document.getElementById("ta").value, "abc", "The value attribute of the textarea element should be 'abc'.");
+ }, "Resetting <textarea> " + description);
+
+ test(function() {
+ setPreconditions("Setting preconditions for resetting " + description);
+ reset();
+ assert_equals(document.getElementById("opt").textContent, document.getElementById("opt").defaultValue, "The textContent of the output element should be reset.");
+ assert_equals(document.getElementById("opt").textContent, "abc", "The textContent of the output element should be 'abc'.");
+ }, "Resetting <output> " + description);
+
+ test(function() {
+ setPreconditions("Setting preconditions for resetting " + description);
+ reset();
+ assert_true(document.getElementById("slt1").options[0].selected, "The first option in the select element should be selected.");
+ assert_false(document.getElementById("slt1").options[1].selected, "The second option in the select element should not be selected.");
+ assert_false(document.getElementById("slt2").options[0].selected, "The first option in the select element should not be selected.");
+ assert_true(document.getElementById("slt2").options[1].selected, "The second option in the select element should be selected.");
+ assert_false(document.getElementById("slt3").options[0].selected, "The first option in the select element with multiple attribute should not be selected.");
+ assert_true(document.getElementById("slt3").options[1].selected, "The second option in the select element with multiple attribute should be selected.");
+ assert_true(document.getElementById("slt3").options[2].selected, "The third option in the select element with multiple attribute should be selected.");
+ }, "Resetting <select> " + description);
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/resetting-a-form/support/reset-form-event-realm.html b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/support/reset-form-event-realm.html
new file mode 100644
index 0000000000..f55c651b54
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/resetting-a-form/support/reset-form-event-realm.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+
+<form></form>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/defaultSelection.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/defaultSelection.html
new file mode 100644
index 0000000000..be965bf5cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/defaultSelection.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<textarea>foo</textarea>
+<input type="text" value="foo"></input>
+<script>
+
+for (let el of [document.querySelector("textarea"), document.querySelector("input")]) {
+ test(function() {
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ }, `Default selectionStart and selectionEnd for ${el}`);
+
+ test(function() {
+ el.value="foo";
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ }, `selectionStart and selectionEnd do not change when same value set again for ${el}`);
+
+ test(function() {
+ el.value="Foo";
+ assert_equals(el.selectionStart, 3);
+ assert_equals(el.selectionEnd, 3);
+ }, `selectionStart and selectionEnd change when value changed to upper case for ${el}`);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/original-id.json b/testing/web-platform/tests/html/semantics/forms/textfieldselection/original-id.json
new file mode 100644
index 0000000000..d9fe435856
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/original-id.json
@@ -0,0 +1 @@
+{"original_id":"textFieldSelection"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/select-event.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/select-event.html
new file mode 100644
index 0000000000..d1b46a22d8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/select-event.html
@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>text field selection: select()</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#textFieldSelection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<textarea>foobar</textarea>
+<input type="text" value="foobar">
+<input type="search" value="foobar">
+<input type="tel" value="1234">
+<input type="url" value="https://example.com/">
+<input type="password" value="hunter2">
+
+<script>
+"use strict";
+
+const els = [document.querySelector("textarea"), ...document.querySelectorAll("input")];
+
+const actions = [
+ {
+ label: "select()",
+ action: el => el.select()
+ },
+ {
+ label: "selectionStart",
+ action: el => el.selectionStart = 1
+ },
+ {
+ label: "selectionEnd",
+ action: el => el.selectionEnd = el.value.length - 1
+ },
+ {
+ label: "selectionDirection",
+ action: el => el.selectionDirection = "backward"
+ },
+ {
+ label: "setSelectionRange()",
+ action: el => el.setSelectionRange(1, el.value.length - 1) // changes direction implicitly to none/forward
+ },
+ {
+ label: "setRangeText()",
+ action: el => el.setRangeText("newmiddle", el.selectionStart, el.selectionEnd, "select")
+ },
+ {
+ label: "selectionStart out of range",
+ action: el => el.selectionStart = 1000
+ },
+ {
+ label: "selectionEnd out of range",
+ action: el => el.selectionEnd = 1000
+ },
+ {
+ label: "setSelectionRange out of range",
+ action: el => el.setSelectionRange(1000, 2000)
+ }
+];
+
+function waitForEvents() {
+ // Engines differ in when these events are sent (see:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1785615) so wait for both a
+ // frame to be rendered, and a timeout.
+ return new Promise(resolve => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ setTimeout(() => {
+ resolve();
+ });
+ });
+ });
+ });
+}
+
+function initialize(el) {
+ el.setRangeText("foobar", 0, el.value.length, "start");
+ // Make sure to flush async dispatches
+ return waitForEvents();
+}
+
+els.forEach((el) => {
+ const elLabel = el.localName === "textarea" ? "textarea" : "input type " + el.type;
+
+ actions.forEach((action) => {
+ // promise_test instead of async_test is important because these need to happen in sequence (to test that events
+ // fire if and only if the selection changes).
+ promise_test(async t => {
+ await initialize(el);
+
+ const watcher = new EventWatcher(t, el, "select");
+
+ const promise = watcher.wait_for("select").then(e => {
+ assert_true(e.isTrusted, "isTrusted must be true");
+ assert_true(e.bubbles, "bubbles must be true");
+ assert_false(e.cancelable, "cancelable must be false");
+ });
+
+ action.action(el);
+
+ return promise;
+ }, `${elLabel}: ${action.label}`);
+
+ promise_test(async t => {
+ el.onselect = t.unreached_func("the select event must not fire the second time");
+
+ action.action(el);
+
+ await waitForEvents();
+ el.onselect = null;
+ }, `${elLabel}: ${action.label} a second time (must not fire select)`);
+
+ promise_test(async t => {
+ const element = el.cloneNode(true);
+ let fired = false;
+ element.addEventListener('select', () => fired = true, { once: true });
+
+ action.action(element);
+
+ await waitForEvents();
+ assert_true(fired, "event didn't fire");
+
+ }, `${elLabel}: ${action.label} disconnected node`);
+
+ // Intentionally still using promise_test, as assert_unreachable does not
+ // make the test fail inside a listener while t.unreached_func() does.
+ promise_test(async t => {
+ const element = el.cloneNode(true);
+ let fired = false;
+ element.addEventListener('select', () => fired = true, { once: true });
+
+ action.action(element);
+
+ assert_false(fired, "the select event must not fire synchronously");
+ await waitForEvents();
+ assert_true(fired, "event didn't fire");
+ }, `${elLabel}: ${action.label} event queue`);
+
+ promise_test(async t => {
+ const element = el.cloneNode(true);
+ let selectCount = 0;
+ element.addEventListener('select', () => ++selectCount);
+ assert_equals(element.selectionEnd, 0);
+
+ action.action(element);
+ action.action(element);
+
+ await waitForEvents();
+ assert_equals(selectCount, 1, "the select event must not fire twice");
+ }, `${elLabel}: ${action.label} twice in disconnected node (must fire select only once)`);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-after-content-change.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-after-content-change.html
new file mode 100644
index 0000000000..60390085c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-after-content-change.html
@@ -0,0 +1,144 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Selection indices after content change</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<input id="i1" type="text" value="hello">
+<textarea id="t1">hello</textarea>
+
+<script>
+"use strict";
+
+// This helper ensures that when the selection direction is reset, it always is reset to the same value consistently
+// (which must be one of either "none" or "forward"). This helps catch bugs like one observed in Chrome, where textareas
+// reset to "none" but inputs reset to "forward".
+let observedResetSelectionDirection;
+function assertSelectionDirectionIsReset(element) {
+ if (!observedResetSelectionDirection) {
+ assert_in_array(element.selectionDirection, ["none", "forward"],
+ "selectionDirection must be set to either none or forward");
+ observedResetSelectionDirection = element.selectionDirection;
+ } else {
+ assert_equals(element.selectionDirection, observedResetSelectionDirection,
+ `selectionDirection must be reset to ${observedResetSelectionDirection} (which was previously observed to be ` +
+ `the value after resetting the selection direction)`);
+ }
+}
+
+runInputTest("input out of document", () => {
+ const input = document.createElement("input");
+ input.value = "hello";
+ return input;
+});
+
+runInputTest("input in document", () => {
+ const input = document.querySelector("#i1");
+ input.value = "hello";
+ return input;
+});
+
+runInputTest("input in document, with focus", () => {
+ const input = document.querySelector("#i1");
+ input.value = "hello";
+ input.focus();
+ return input;
+});
+
+runTextareaTest("textarea out of document", () => {
+ const textarea = document.createElement("textarea");
+ textarea.value = "hello";
+ return textarea;
+});
+
+runTextareaTest("textarea in document", () => {
+ const textarea = document.querySelector("#t1");
+ textarea.value = "hello";
+ return textarea;
+});
+
+runTextareaTest("textarea in document, with focus", () => {
+ const textarea = document.querySelector("#t1");
+ textarea.value = "hello";
+ textarea.focus();
+ return textarea;
+});
+
+function runTest(descriptor, elementFactory) {
+ test(() => {
+ const element = elementFactory();
+ element.setSelectionRange(1, 3, "backward");
+
+ assert_equals(element.selectionStart, 1, "Sanity check: selectionStart was set correctly");
+ assert_equals(element.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
+ assert_equals(element.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");
+
+ element.value = "hello";
+
+ assert_equals(element.selectionStart, 1, "selectionStart must not change");
+ assert_equals(element.selectionEnd, 3, "selectionEnd must not change");
+ assert_equals(element.selectionDirection, "backward", "selectionDirection must not change");
+ }, `${descriptor}: selection must not change when setting the same value`);
+
+ test(() => {
+ const element = elementFactory();
+ element.setSelectionRange(1, 3, "backward");
+
+ assert_equals(element.selectionStart, 1, "Sanity check: selectionStart was set correctly");
+ assert_equals(element.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
+ assert_equals(element.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");
+
+ element.value = "hello2";
+
+ assert_equals(element.selectionStart, element.value.length, "selectionStart must be reset to the end");
+ assert_equals(element.selectionEnd, element.value.length, "selectionEnd must be reset to the end");
+ assertSelectionDirectionIsReset(element);
+ }, `${descriptor}: selection must change when setting a different value`);
+}
+
+function runInputTest(descriptor, elementFactory) {
+ runTest(descriptor, elementFactory);
+
+ test(() => {
+ const input = elementFactory();
+ input.setSelectionRange(1, 3, "backward");
+
+ assert_equals(input.selectionStart, 1, "Sanity check: selectionStart was set correctly");
+ assert_equals(input.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
+ assert_equals(input.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");
+
+ input.value = "he\nllo";
+
+ assert_equals(input.selectionStart, 1, "selectionStart must not change");
+ assert_equals(input.selectionEnd, 3, "selectionEnd must not change");
+ assert_equals(input.selectionDirection, "backward", "selectionDirection must not change");
+ }, `${descriptor}: selection must not change when setting a value that becomes the same after the value ` +
+ `sanitization algorithm`);
+}
+
+function runTextareaTest(descriptor, elementFactory) {
+ runTest(descriptor, elementFactory);
+
+ test(() => {
+ const textarea = elementFactory();
+ textarea.value = "hell\no";
+ textarea.setSelectionRange(1, 3, "backward");
+
+ assert_equals(textarea.selectionStart, 1, "Sanity check: selectionStart was set correctly");
+ assert_equals(textarea.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
+ assert_equals(textarea.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");
+
+ textarea.value = "hell\r\no";
+
+ assert_equals(textarea.selectionStart, 1, "selectionStart must not change when setting to CRLF");
+ assert_equals(textarea.selectionEnd, 3, "selectionEnd must not change when setting to CRLF");
+ assert_equals(textarea.selectionDirection, "backward", "selectionDirection must not change when setting to CRLF");
+
+ textarea.value = "hell\ro";
+
+ assert_equals(textarea.selectionStart, 1, "selectionStart must not change when setting to CR");
+ assert_equals(textarea.selectionEnd, 3, "selectionEnd must not change when setting to CR");
+ assert_equals(textarea.selectionDirection, "backward", "selectionDirection must not change when setting to CR");
+ }, `${descriptor}: selection must not change when setting the same normalized value`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application-textarea.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application-textarea.html
new file mode 100644
index 0000000000..48c6313f32
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application-textarea.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>text field selection (textarea)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#textFieldSelection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ test(function() {
+ var el = document.createElement("textarea");
+ assert_equals(el.selectionStart, 0, "initial selectionStart");
+ assert_equals(el.selectionEnd, 0, "initial selectionEnd");
+ // The initial selection direction must be "none" if the platform supports that
+ // direction, or "forward" otherwise.
+ assert_in_array(el.selectionDirection, ["none", "forward"]);
+
+ const initialDirection = el.selectionDirection;
+ el.selectionDirection = "none";
+ assert_equals(el.selectionDirection, initialDirection);
+
+ el.value = "foo";
+ el.selectionStart = 1;
+ el.selectionEnd = 1;
+ el.selectionDirection = "forward";
+ assert_equals(el.selectionStart, 1, "updated selectionStart");
+ assert_equals(el.selectionEnd, 1, "updated selectionEnd");
+ assert_equals(el.selectionDirection, "forward", "updated selectionDirection");
+
+ el.setRangeText("foobar");
+ el.setSelectionRange(0, 1);
+ assert_equals(el.selectionStart, 0, "final selectionStart");
+ assert_equals(el.selectionEnd, 1, "final selectionEnd");
+ assert_in_array(el.selectionDirection, ["none", "forward"]);
+
+ const finalDirection = el.selectionDirection;
+ el.finalDirection = "forward";
+ assert_equals(el.selectionDirection, finalDirection);
+ }, "text field selection for the input textarea");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application.html
new file mode 100644
index 0000000000..063836cd23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-not-application.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name=variant content="?default">
+<meta name=variant content="?week,month">
+<title>text field selection</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#textFieldSelection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var nonApplicableTypes = ["hidden", "email", "datetime-local", "date", "time", "number", "range", "color", "checkbox", "radio", "file", "submit", "image", "reset", "button"];
+ var applicableTypes = ["text", "search", "tel", "url", "password", "aninvalidtype", null];
+
+ if (location.search !== "?default") {
+ // For non default case, use the parameters passed in through query string
+ // instead of nonApplicableTypes.
+ nonApplicableTypes = location.search.substr(1).split(',');
+ }
+
+ nonApplicableTypes.forEach(function(type){
+ var el = document.createElement("input");
+ el.type = type;
+
+ test(() => {
+ assert_equals(el.selectionStart, null);
+ }, `selectionStart on an input[type=${type}] returns null`);
+
+ test(() => {
+ assert_equals(el.selectionEnd, null);
+ }, `selectionEnd on an input[type=${type}] returns null`);
+
+ test(() => {
+ assert_equals(el.selectionDirection, null);
+ }, `selectionDirection on an input[type=${type}] returns null`);
+
+ test(() => {
+ assert_throws_dom("InvalidStateError", function(){
+ el.selectionStart = 0;
+ });
+ }, `assigning selectionStart on an input[type=${type}] throws InvalidStateError`);
+
+ test(() => {
+ assert_throws_dom("InvalidStateError", function(){
+ el.selectionEnd = 0;
+ });
+ }, `assigning selectionEnd on an input[type=${type}] throws InvalidStateError`);
+
+ test(() => {
+ assert_throws_dom("InvalidStateError", function(){
+ el.selectionDirection = 'none';
+ });
+ }, `assigning selectionDirection on an input[type=${type}] throws InvalidStateError`);
+
+ test(() => {
+ assert_throws_dom("InvalidStateError", function(){
+ el.setRangeText("foobar");
+ });
+ }, `setRangeText on an input[type=${type}] throws InvalidStateError`);
+
+ test(() => {
+ assert_throws_dom("InvalidStateError", function(){
+ el.setSelectionRange(0, 1);
+ });
+ }, `setSelectionRange on an input[type=${type}] throws InvalidStateError`);
+ });
+
+ applicableTypes.forEach(function(type) {
+ const el = document.createElement("input");
+ if (type) {
+ el.type = type;
+ }
+ const initialDirection = el.selectionDirection;
+
+ test(() => {
+ assert_equals(el.selectionStart, 0);
+ }, `selectionStart on an input[type=${type}] returns a value`);
+
+ test(() => {
+ assert_equals(el.selectionEnd, 0);
+ }, `selectionEnd on an input[type=${type}] returns a value`);
+
+ test(() => {
+ assert_in_array(el.selectionDirection, ["forward", "none"]);
+ }, `selectionDirection on an input[type=${type}] returns a value`);
+
+ test(() => {
+ el.selectionDirection = "none";
+ assert_equals(el.selectionDirection, initialDirection);
+ }, `assigning selectionDirection "none" on an input[type=${type}] should give the initial direction`);
+
+ test(() => {
+ el.selectionStart = 1;
+ }, `assigning selectionStart on an input[type=${type}] doesn't throw an exception`);
+
+ test(() => {
+ el.selectionEnd = 1;
+ }, `assigning selectionEnd on an input[type=${type}] doesn't throw an exception`);
+
+ test(() => {
+ el.selectionDirection = "forward";
+ }, `assigning selectionDirection on an input[type=${type}] doesn't throw an exception`);
+
+ test(() => {
+ el.setRangeText("foobar");
+ }, `setRangeText on an input[type=${type}] doesn't throw an exception`);
+
+ test(() => {
+ el.setSelectionRange(0, 1);
+ }, `setSelectionRange on an input[type=${type}] doesn't throw an exception`);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end-extra.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end-extra.html
new file mode 100644
index 0000000000..c8ba83bb98
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end-extra.html
@@ -0,0 +1,153 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<form id="form"><input id="form-input" type="text" value="abc" /></form>
+<script>
+ // * Should we test setting the dirty flag in any way that isn't
+ // setting the value?
+ // * How to simulate users typing?
+
+ test(function() {
+ var el = document.createElement("textarea");
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ el.defaultValue = "123";
+ assert_equals(el.value.length, 3);
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ }, "Setting defaultValue in a textarea should NOT move the cursor to the end");
+
+ test(function() {
+ var el = document.createElement("textarea");
+ el.value = "abcdef";
+ assert_equals(el.selectionStart, 6);
+ assert_equals(el.selectionEnd, 6);
+ el.defaultValue = "123";
+ assert_equals(el.value.length, 6);
+ assert_equals(el.selectionStart, 6);
+ assert_equals(el.selectionEnd, 6);
+ }, "Setting defaultValue in a textarea with a value should NOT make any difference");
+
+ test(function() {
+ var el = document.createElement("textarea");
+ el.appendChild(document.createTextNode("abcdef"));
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ el.textContent = "abcdef123456";
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ }, "Setting textContent in a textarea should NOT move selection{Start,End} to the end");
+
+ test(function() {
+ var el = document.createElement("textarea");
+ el.appendChild(document.createTextNode("abcdef"));
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ el.appendChild(document.createTextNode("123456"));
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ }, "Adding children to a textarea should NOT move selection{Start,End} to the end");
+
+ test(function() {
+ var el = document.createElement("textarea");
+ el.appendChild(document.createTextNode("abcdef"));
+ el.appendChild(document.createTextNode("123"));
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+
+ el.removeChild(el.firstChild);
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ }, "Removing children from a textarea should NOT update selection{Start,End}");
+
+ test(function() {
+ var el = document.createElement("textarea");
+ el.textContent = "abcdef\nwhatevs";
+ el.selectionStart = 3;
+ el.selectionEnd = 5;
+
+ el.firstChild.data = "abcdef\r\nwhatevs";
+ assert_equals(el.selectionStart, 3);
+ assert_equals(el.selectionEnd, 5);
+ }, "Setting the same value (with different newlines) in a textarea should NOT update selection{Start,End}");
+
+ test(function() {
+ var el = document.createElement("textarea");
+ el.textContent = "foobar";
+ el.selectionStart = 3;
+ el.selectionEnd = 5;
+ el.firstChild.remove();
+ assert_equals(el.selectionStart, 0, 'selectionStart after node removal');
+ assert_equals(el.selectionEnd, 0, 'selectionEnd after node removal');
+ el.appendChild(document.createTextNode("foobar"));
+ assert_equals(el.selectionStart, 0, 'selectionStart after appendChild');
+ assert_equals(el.selectionEnd, 0, 'selectionEnd after appendChild');
+
+ el.selectionStart = 3;
+ el.selectionEnd = 5;
+ el.textContent = "foobar2"; // This removes the child node first.
+ assert_equals(el.selectionStart, 0, 'selectionStart after textContent setter');
+ assert_equals(el.selectionEnd, 0, 'selectionEnd after textContent setter');
+
+ el.selectionStart = 3;
+ el.selectionEnd = 5;
+ el.defaultValue = "foobar"; // Same as textContent setter.
+ assert_equals(el.selectionStart, 0, 'selectionStart after defaultValue setter');
+ assert_equals(el.selectionEnd, 0, 'selectionEnd after defaultValue setter');
+
+ }, "Removing child nodes in non-dirty textarea should make selection{Start,End} 0");
+
+ test(function() {
+ var el = document.createElement("textarea");
+ el.defaultValue = "123";
+ assert_equals(el.value.length, 3);
+ el.selectionStart = 3;
+ el.selectionEnd = 3;
+ el.value = "12";
+ assert_equals(el.value.length, 2);
+ assert_equals(el.selectionStart, 2);
+ assert_equals(el.selectionEnd, 2);
+ }, "Setting value to a shorter string than defaultValue should correct the cursor position");
+
+ test(function() {
+ var el = document.createElement("input");
+ el.type = "text";
+ el.value = "http://example.com ";
+ assert_equals(el.selectionStart, 21);
+ assert_equals(el.selectionEnd, 21);
+ el.type = "url";
+ assert_equals(el.selectionStart, 18);
+ assert_equals(el.selectionEnd, 18);
+ }, "Shortening value by turning the input type into 'url' should correct selection{Start,End}");
+
+ test(function() {
+ var el = document.createElement("input");
+ el.type = "text";
+ el.value = "#123456xx";
+ assert_equals(el.selectionStart, 9);
+ assert_equals(el.selectionEnd, 9);
+ el.type = "color";
+ el.type = "text";
+ // https://html.spec.whatwg.org/C/input.html#the-input-element:attr-input-type-15
+ // 9. If previouslySelectable is false and nowSelectable is true, set the
+ // element's text entry cursor position to the beginning of the text
+ // control, ...
+ assert_equals(el.selectionStart, 0);
+ assert_equals(el.selectionEnd, 0);
+ }, "Shortening value by turning the input type into 'color' and back to 'text' should correct selection{Start,End}");
+
+ test(function() {
+ var form = document.getElementById("form");
+ var el = document.getElementById("form-input");
+
+ el.value = "abcde";
+ assert_equals(el.value.length, 5);
+ form.reset();
+ assert_equals(el.value.length, 3);
+ assert_equals(el.selectionStart, 3);
+ assert_equals(el.selectionEnd, 3);
+ }, "Resetting a value to a shorter string than defaultValue should correct the cursor position");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end.html
new file mode 100644
index 0000000000..768bce4129
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-start-end.html
@@ -0,0 +1,206 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+ function createInputElement(value, append, suffix) {
+ var el = document.createElement("input");
+ el.type = "text";
+ el.value = value;
+ el.id = "input" + (append ? "-appended" : "-not-appended") + (suffix ? suffix : "");
+ if (append) {
+ document.body.appendChild(el);
+ }
+ return el;
+ };
+
+ function createTextareaElement(value, append, suffix) {
+ var el = document.createElement("textarea");
+ el.value = value;
+
+ el.id = "textarea" + (append ? "-appended" : "-not-appended") + (suffix ? suffix : "");
+ if (append) {
+ document.body.appendChild(el);
+ }
+ return el;
+ };
+
+ function createPrefocusedInputElement(value, append) {
+ var el = createInputElement(value, append, "-prefocused");
+ el.focus();
+ el.blur();
+ return el;
+ }
+
+ function createPrefocusedTextareaElement(value, append) {
+ var el = createTextareaElement(value, append, "-prefocused");
+ el.focus();
+ el.blur();
+ return el;
+ }
+
+ function createClonedTextareaWithNoDirtyValueFlag(text) {
+ const textarea = document.createElement("textarea");
+ textarea.textContent = text;
+ return textarea.cloneNode(true);
+ }
+
+ function createTestElements(value) {
+ return [ createInputElement(value, true),
+ createInputElement(value, false),
+ createPrefocusedInputElement(value, true),
+ createPrefocusedInputElement(value, false),
+ createTextareaElement(value, true),
+ createTextareaElement(value, false),
+ createPrefocusedTextareaElement(value, true),
+ createPrefocusedTextareaElement(value, false),
+ createClonedTextareaWithNoDirtyValueFlag(value)
+ ];
+ }
+
+ var testValue = "abcdefghij";
+
+ test(function() {
+ assert_equals(testValue.length, 10);
+ }, "Sanity check for testValue length; if this fails, variou absolute offsets in the test below need to be adjusted to be less than testValue.length");
+
+ for (let prop of ["selectionStart", "selectionEnd"]) {
+ for (let el of createTestElements(testValue)) {
+ if (el.defaultValue !== el.value) {
+ test(function() {
+ assert_equals(el[prop], testValue.length);
+ }, `Initial .value set on ${el.id} should set ${prop} to end of value`);
+ }
+ }
+ }
+
+ test(function() {
+ for (let el of createTestElements(testValue)) {
+ var t = async_test(`onselect should fire when selectionStart is changed on ${el.id}`);
+ el.onselect = t.step_func_done(function(e) {
+ assert_equals(e.type, "select");
+ el.remove();
+ });
+ el.selectionStart = 2;
+ }
+ }, "onselect should fire when selectionStart is changed");
+
+ test(function() {
+ for (let el of createTestElements(testValue)) {
+ var t = async_test(`onselect should fire when selectionEnd is changed on ${el.id}`);
+ el.onselect = t.step_func_done(function(e) {
+ assert_equals(e.type, "select");
+ el.remove();
+ });
+ el.selectionEnd = 2;
+ }
+ }, "onselect should fire when selectionEnd is changed");
+
+ test(function() {
+ for (let el of createTestElements(testValue)) {
+ el.selectionStart = 0;
+ el.selectionEnd = 5;
+ el.selectionStart = 8;
+ assert_equals(el.selectionStart, 8, `selectionStart on ${el.id}`);
+ assert_equals(el.selectionEnd, 8, `selectionEnd on ${el.id}`);
+ el.remove();
+ }
+ }, "Setting selectionStart to a value larger than selectionEnd should increase selectionEnd");
+
+ test(function() {
+ for (let el of createTestElements(testValue)) {
+ el.selectionStart = 8;
+ el.selectionEnd = 5;
+ assert_equals(el.selectionStart, 5, `selectionStart on ${el.id}`);
+ assert_equals(el.selectionEnd, 5, `selectionEnd on ${el.id}`);
+ el.remove();
+ }
+ }, "Setting selectionEnd to a value smaller than selectionStart should decrease selectionStart");
+
+ test(function() {
+ for (let el of createTestElements(testValue)) {
+ el.selectionStart = 0;
+ assert_equals(el.selectionStart, 0, `We just set it on ${el.id}`);
+ el.selectionStart = -1;
+ assert_equals(el.selectionStart, testValue.length,
+ `selectionStart setter on ${el.id} should convert -1 to 2^32-1`);
+ el.selectionStart = Math.pow(2, 32);
+ assert_equals(el.selectionStart, 0,
+ `selectionStart setter on ${el.id} should convert 2^32 to 0`);
+ el.selectionStart = Math.pow(2, 32) - 1;
+ assert_equals(el.selectionStart, testValue.length,
+ `selectionStart setter on ${el.id} should leave 2^32-1 as-is`);
+ el.remove();
+ }
+ }, "selectionStart edge-case values");
+
+ test(function() {
+ for (let el of createTestElements(testValue)) {
+ el.selectionEnd = 0;
+ assert_equals(el.selectionEnd, 0, `We just set it on ${el.id}`);
+ el.selectionEnd = -1;
+ assert_equals(el.selectionEnd, testValue.length,
+ `selectionEnd setter on ${el.id} should convert -1 to 2^32-1`);
+ el.selectionEnd = Math.pow(2, 32);
+ assert_equals(el.selectionEnd, 0,
+ `selectionEnd setter on ${el.id} should convert 2^32 to 0`);
+ el.selectionEnd = Math.pow(2, 32) - 1;
+ assert_equals(el.selectionEnd, testValue.length,
+ `selectionEnd setter on ${el.id} should leave 2^32-1 as-is`);
+ el.remove();
+ }
+ }, "selectionEnd edge-case values");
+
+ test(() => {
+ for (const el of createTestElements(testValue)) {
+ el.selectionStart = 200;
+ assert_equals(el.selectionStart, testValue.length);
+ el.remove();
+ }
+ }, "selectionStart should be clamped by the current value length");
+
+ test(() => {
+ for (const el of createTestElements(testValue)) {
+ el.selectionStart = 300;
+ assert_equals(el.selectionEnd, testValue.length);
+ el.remove();
+ }
+ }, "selectionEnd should be clamped by the current value length");
+
+ test(() => {
+ for (const el of createTestElements(testValue)) {
+ el.setSelectionRange(200, 300);
+ assert_equals(el.selectionStart, testValue.length);
+ assert_equals(el.selectionEnd, testValue.length);
+ el.remove();
+ }
+ }, "setSelectionRange should be clamped by the current value length");
+
+ test(() => {
+ for (let el of createTestElements(testValue)) {
+ const start = 1;
+ const end = testValue.length - 1;
+
+ el.setSelectionRange(start, end);
+
+ assert_equals(el.selectionStart, start, `selectionStart on ${el.id}`);
+ assert_equals(el.selectionEnd, end, `selectionEnd on ${el.id}`);
+
+ el.selectionDirection = "backward";
+
+ assert_equals(el.selectionStart, start,
+ `selectionStart on ${el.id} after setting selectionDirection to "backward"`);
+ assert_equals(el.selectionEnd, end,
+ `selectionEnd on ${el.id} after setting selectionDirection to "backward"`);
+
+ el.selectionDirection = "forward";
+
+ assert_equals(el.selectionStart, start,
+ `selectionStart on ${el.id} after setting selectionDirection to "forward"`);
+ assert_equals(el.selectionEnd, end,
+ `selectionEnd on ${el.id} after setting selectionDirection to "forward"`);
+ }
+ }, "selectionStart and selectionEnd should remain the same when selectionDirection is changed");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-value-interactions.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-value-interactions.html
new file mode 100644
index 0000000000..0fd8a5b182
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection-value-interactions.html
@@ -0,0 +1,127 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<div id=target></div>
+<script>
+ var target = document.getElementById("target");
+ var sometext = "something";
+ var shorttext = "abc";
+ var elemData = [
+ {
+ desc: "textarea not in body",
+ factory: () => document.createElement("textarea"),
+ },
+ {
+ desc: "input not in body",
+ factory: () => document.createElement("input"),
+ },
+ {
+ desc: "textarea in body",
+ factory: () => document.body.appendChild(document.createElement("textarea")),
+ },
+ {
+ desc: "input in body",
+ factory: () => document.body.appendChild(document.createElement("input")),
+ },
+ {
+ desc: "textarea in body with parsed default value",
+ factory: () => {
+ target.innerHTML = "<textarea>abcdefghij</textarea>"
+ return target.querySelector("textarea");
+ },
+ },
+ {
+ desc: "input in body with parsed default value",
+ factory: () => {
+ target.innerHTML = "<input value='abcdefghij'>"
+ return target.querySelector("input");
+ },
+ },
+ {
+ desc: "focused textarea",
+ factory: () => {
+ var t = document.body.appendChild(document.createElement("textarea"));
+ t.focus();
+ return t;
+ },
+ },
+ {
+ desc: "focused input",
+ factory: () => {
+ var i = document.body.appendChild(document.createElement("input"));
+ i.focus();
+ return i;
+ },
+ },
+ {
+ desc: "focused then blurred textarea",
+ factory: () => {
+ var t = document.body.appendChild(document.createElement("textarea"));
+ t.focus();
+ t.blur();
+ return t;
+ },
+ },
+ {
+ desc: "focused then blurred input",
+ factory: () => {
+ var i = document.body.appendChild(document.createElement("input"));
+ i.focus();
+ i.blur()
+ return i;
+ },
+ },
+ ];
+
+for (var data of elemData) {
+ test(function() {
+ var el = data.factory();
+ this.add_cleanup(() => el.remove());
+ el.defaultValue = sometext;
+ assert_true(sometext.length > 8,
+ "sometext too short, test won't work right");
+ el.selectionStart = 4;
+ el.selectionEnd = 6;
+ el.setRangeText("xyz");
+ el.defaultValue = "set range text";
+ assert_equals(el.value, sometext.slice(0, 4) + "xyz" + sometext.slice(6),
+ "Calling setRangeText should set the value dirty flag");
+ }, `value dirty flag behavior after setRangeText on ${data.desc}`);
+}
+
+for (var tag of ['input', 'textarea']) {
+ test(function() {
+ var el = document.createElement(tag);
+ document.body.appendChild(el);
+ this.add_cleanup(() => el.remove());
+
+ for (let val of ["", "foo", "foobar"]) {
+ el.value = val;
+ assert_equals(el.selectionStart, val.length,
+ "element.selectionStart should be value.length");
+ assert_equals(el.selectionEnd, val.length,
+ "element.selectionEnd should be value.length");
+ }
+ }, `selection is collapsed to the end after changing values on ${tag}`);
+
+ test(function() {
+ var el = document.createElement(tag);
+ document.body.appendChild(el);
+ this.add_cleanup(() => el.remove());
+
+ el.value = "foobar"
+ el.selectionStart = 2
+ el.selectionEnd = 4
+ el.value = "foobar"
+
+ assert_equals(el.selectionStart, 2,
+ "element.selectionStart should be unchanged");
+ assert_equals(el.selectionEnd, 4,
+ "element.selectionEnd should be unchanged");
+ }, `selection is not collapsed to the end when value is set to its existing value on ${tag}`);
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection.html
new file mode 100644
index 0000000000..04ab7298e3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/selection.html
@@ -0,0 +1,206 @@
+<!DOCTYPE HTML>
+<title>test if select() API returns correct attributes</title>
+<meta charset="UTF-8">
+<meta name="timeout" content="long">
+<link rel="author" title="Koji Tashiro" href="mailto:koji.tashiro@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/multipage/association-of-controls-and-forms.html#textFieldSelection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script>
+ var body = document.getElementsByTagName("body").item(0);
+ var dirs = ['forward', 'backward', 'none'];
+ var sampleText = "0123456789";
+
+ var createInputElement = function(value, append = true) {
+ var el = document.createElement("input");
+ el.type = "text";
+ el.value = value;
+ el.id = "input" + (append ? "-appended" : "-not-appended");
+ if (append) {
+ body.appendChild(el);
+ }
+ return el;
+ };
+
+ var createTextareaElement = function(value, append = true) {
+ var el = document.createElement("textarea");
+ el.value = value;
+ el.id = "textarea" + (append ? "-appended" : "-not-appended");
+ if (append) {
+ body.appendChild(el);
+ }
+ return el;
+ };
+
+
+ test(function() {
+ var text = 'a';
+ for (var i=0; i<255; i++) {
+ var el = createInputElement(text);
+ el.select();
+ var selectionText = el.value.substring(el.selectionStart, el.selectionEnd);
+ assert_equals(selectionText, text, "Selection text mismatched");
+ el.parentNode.removeChild(el);
+ text += 'a';
+ }
+ }, "test if selection text is correct for input");
+
+
+ test(function() {
+ var text = 'a';
+ for (var i=0; i<255; i++) {
+ var el = createTextareaElement(text);
+ el.select();
+ var selectionText = el.value.substring(el.selectionStart, el.selectionEnd);
+ assert_equals(selectionText, text, "Selection text mismatched");
+ el.parentNode.removeChild(el);
+ text += 'a';
+ }
+ }, "test if selection text is correct for textarea");
+
+
+ test(function() {
+ var text = 'ã‚';
+ for (var i=0; i<255; i++) {
+ var el = createInputElement(text);
+ el.select();
+ var selectionText = el.value.substring(el.selectionStart, el.selectionEnd);
+ assert_equals(selectionText, text, "Selection text mismatched");
+ el.parentNode.removeChild(el);
+ text += 'ã‚';
+ }
+ }, "test if non-ascii selection text is correct for input");
+
+
+ test(function() {
+ var text = 'ã‚';
+ for (var i=0; i<255; i++) {
+ var el = createTextareaElement(text);
+ el.select();
+ var selectionText = el.value.substring(el.selectionStart, el.selectionEnd);
+ assert_equals(selectionText, text, "Selection text mismatched");
+ el.parentNode.removeChild(el);
+ text += 'ã‚';
+ }
+ }, "test if non-ascii selection text is correct for textarea");
+
+
+ for (var append of [true, false]) {
+ test(function() {
+ var el = createInputElement(sampleText, append);
+ // If there is no selection, then it must return the offset(in logical order)
+ // to the character that immediately follows the text entry cursor.
+ assert_equals(el.selectionStart, el.value.length,
+ "SelectionStart offset without selection in " + el.id);
+ el.select();
+ assert_equals(el.selectionStart, 0, "SelectionStart offset");
+ el.remove();
+ }, "test SelectionStart offset for input that is " +
+ (append ? "appended" : " not appended"));
+ }
+
+ for (var append of [true, false]) {
+ test(function() {
+ var el = createTextareaElement(sampleText, append);
+ // If there is no selection, then it must return the offset(in logical order)
+ // to the character that immediately follows the text entry cursor.
+ assert_equals(el.selectionStart, el.value.length,
+ "SelectionStart offset without selection in " + el.id);
+ el.select();
+ assert_equals(el.selectionStart, 0, "SelectionStart offset");
+ el.remove();
+ }, "test SelectionStart offset for textarea that is " +
+ (append ? "appended" : " not appended"));
+ }
+
+ for (var append of [true, false]) {
+ test(function() {
+ var el = createInputElement(sampleText, append);
+ // If there is no selection, then it must return the offset(in logical order)
+ // to the character that immediately follows the text entry cursor.
+ assert_equals(el.selectionEnd, el.value.length,
+ "SelectionEnd offset without selection in " + el.id);
+ el.select();
+ assert_equals(el.selectionEnd, el.value.length, "SelectionEnd offset");
+ el.remove();
+ }, "test SelectionEnd offset for input that is " +
+ (append ? "appended" : " not appended"));
+ }
+
+
+ for (var append of [true, false]) {
+ test(function() {
+ var el = createTextareaElement(sampleText, append);
+ // If there is no selection, then it must return the offset(in logical order)
+ // to the character that immediately follows the text entry cursor.
+ assert_equals(el.selectionEnd, el.value.length,
+ "SelectionEnd offset without selection in " + el.id);
+ el.select();
+ assert_equals(el.selectionEnd, el.value.length, "SelectionEnd offset");
+ el.remove();
+ }, "test SelectionEnd offset for textarea that is " +
+ (append ? "appended" : " not appended"));
+ }
+
+ test(function() {
+ var el = createInputElement(sampleText);
+ assert_in_array(el.selectionDirection, dirs, "SelectionDirection");
+ el.select();
+ assert_in_array(el.selectionDirection, dirs, "SelectionDirection");
+ el.parentNode.removeChild(el);
+ }, "test SelectionDirection for input");
+
+
+ test(function() {
+ var el = createTextareaElement(sampleText);
+ assert_in_array(el.selectionDirection, dirs, "SelectionDirection");
+ el.select();
+ assert_in_array(el.selectionDirection, dirs, "SelectionDirection");
+ el.parentNode.removeChild(el);
+ }, "test SelectionDirection for textarea");
+
+ promise_test(async () => {
+ // cause a layout overflow
+ const el = createInputElement(sampleText.repeat(100));
+ el.selectionEnd = 0;
+ await new Promise(requestAnimationFrame);
+ assert_equals(el.scrollLeft, 0);
+
+ el.select();
+ await new Promise(requestAnimationFrame);
+ assert_equals(el.scrollLeft, 0);
+ el.remove();
+ }, `test scrollLeft for input`);
+
+ promise_test(async () => {
+ // cause a layout overflow
+ const el = createInputElement(sampleText.repeat(100));
+ el.scrollLeft = 33;
+
+ el.select();
+ await new Promise(requestAnimationFrame);
+ assert_equals(el.scrollLeft, 33);
+ el.remove();
+ }, `test scrollLeft preservation for input`);
+
+ for (const localName of ["input", "textarea"]) {
+ promise_test(async () => {
+ const container = document.createElement("div");
+ container.style.height = "100px";
+ container.style.overflow = "scroll";
+ const element = document.createElement(localName);
+ element.style.marginTop = "120px";
+ container.append(element);
+ document.body.append(container);
+
+ element.select();
+ await new Promise(requestAnimationFrame);
+ assert_equals(container.scrollTop, 0);
+
+ container.remove();
+ }, `test container.scrollTop for ${localName}`);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/setSelectionRange.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/setSelectionRange.html
new file mode 100644
index 0000000000..bdf52a77f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/setSelectionRange.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<textarea>
+
+</textarea>
+<script>
+test(function() {
+ let textarea = document.querySelector('textarea');
+ assert_equals(textarea.selectionStart, 0);
+ assert_equals(textarea.selectionEnd, 0);
+ textarea.setSelectionRange(0, 1);
+ assert_equals(textarea.selectionStart, 0);
+ assert_equals(textarea.selectionEnd, 1);
+}, "setSelectionRange on line boundaries");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/textarea-selection-while-parsing.xhtml b/testing/web-platform/tests/html/semantics/forms/textfieldselection/textarea-selection-while-parsing.xhtml
new file mode 100644
index 0000000000..57326bb4a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/textarea-selection-while-parsing.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<textarea>
+a
+<script>document.querySelector('textarea').value = 'ggg';</script>
+b
+</textarea>
+<script>
+test(() => {
+ let ta = document.querySelector('textarea');
+ assert_equals(ta.selectionStart, 3);
+ assert_equals(ta.selectionEnd, 3);
+}, 'Value setter while parsing textarea children should move ' +
+ 'selection{Start,End} to the end');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setRangeText.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setRangeText.html
new file mode 100644
index 0000000000..b435fe7881
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setRangeText.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>text field selection: setRangeText</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#textFieldSelection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #display_none {display:none;}
+</style>
+<div id="log"></div>
+<input type=text id=text value="foobar">
+<input type=search id=search value="foobar">
+<input type=tel id=tel value="foobar">
+<input type=url id=url value="foobar">
+<input type=password id=password value="foobar">
+<input id=display_none value="foobar">
+<textarea id=textarea>foobar</textarea>
+<script>
+ var input = document.createElement("input");
+ input.id = "input_not_in_doc";
+ input.value = "foobar";
+
+ var elements = [
+ document.getElementById("text"),
+ document.getElementById("search"),
+ document.getElementById("tel"),
+ document.getElementById("url"),
+ document.getElementById("password"),
+ document.getElementById("display_none"),
+ document.getElementById("textarea"),
+ input,
+ ]
+
+ function untilEvent(element, eventName) {
+ return new Promise((resolve) => {
+ element.addEventListener(eventName, resolve, { once: true });
+ });
+ }
+
+ elements.forEach(function(element) {
+ test(function() {
+ element.value = "foobar";
+ element.selectionStart = 0;
+ element.selectionEnd = 3;
+ assert_equals(element.selectionStart, 0);
+ assert_equals(element.selectionEnd, 3);
+ element.setRangeText("foobar2");
+ assert_equals(element.value, "foobar2bar");
+ assert_equals(element.selectionStart, 0);
+ assert_equals(element.selectionEnd, 7);
+ element.setRangeText("foobar3", 7, 10);
+ assert_equals(element.value, "foobar2foobar3");
+ }, element.id + " setRangeText with only one argument replaces the value between selectionStart and selectionEnd, otherwise replaces the value between 2nd and 3rd arguments");
+
+ test(function(){
+ element.value = "foobar";
+ element.selectionStart = 0;
+ element.selectionEnd = 0;
+
+ element.setRangeText("foobar2", 0, 3); // no 4th arg, default "preserve"
+ assert_equals(element.value, "foobar2bar");
+ assert_equals(element.selectionStart, 0);
+ assert_equals(element.selectionEnd, 0);
+ }, element.id + " selectionMode missing");
+
+ test(function(){
+ element.value = "foobar"
+ element.setRangeText("foo", 3, 6, "select");
+ assert_equals(element.value, "foofoo");
+ assert_equals(element.selectionStart, 3);
+ assert_equals(element.selectionEnd, 6);
+ }, element.id + " selectionMode 'select'");
+
+ test(function(){
+ element.value = "foobar"
+ element.setRangeText("foo", 3, 6, "start");
+ assert_equals(element.value, "foofoo");
+ assert_equals(element.selectionStart, 3);
+ assert_equals(element.selectionEnd, 3);
+ }, element.id + " selectionMode 'start'");
+
+ test(function(){
+ element.value = "foobar"
+ element.setRangeText("foobar", 3, 6, "end");
+ assert_equals(element.value, "foofoobar");
+ assert_equals(element.selectionStart, 9);
+ assert_equals(element.selectionEnd, 9);
+ }, element.id + " selectionMode 'end'");
+
+ test(function(){
+ element.value = "foobar"
+ element.selectionStart = 0;
+ element.selectionEnd = 5;
+ assert_equals(element.selectionStart, 0);
+ element.setRangeText("", 3, 6, "preserve");
+ assert_equals(element.value, "foo");
+ assert_equals(element.selectionStart, 0);
+ assert_equals(element.selectionEnd, 3);
+ }, element.id + " selectionMode 'preserve'");
+
+ test(function(){
+ assert_throws_dom("INDEX_SIZE_ERR", function() {
+ element.setRangeText("barfoo", 2, 1);
+ });
+ }, element.id + " setRangeText with 3rd argument greater than 2nd argument throws an IndexSizeError exception");
+
+ test(function(){
+ assert_throws_js(TypeError, function() {
+ element.setRangeText();
+ });
+ }, element.id + " setRangeText without argument throws a type error");
+
+ promise_test(async (t) => {
+ // At this point there are already "select" events queued up on
+ // "element". Give them time to fire; otherwise we can get spurious
+ // passes.
+ //
+ // This is unfortunately racy in that we might _still_ get spurious
+ // passes. I'm not sure how best to handle that.
+ t.step_timeout(function() {
+ var q = false;
+ element.onselect = t.step_func_done(function(e) {
+ assert_true(q, "event should be queued");
+ assert_true(e.isTrusted, "event is trusted");
+ assert_true(e.bubbles, "event bubbles");
+ assert_false(e.cancelable, "event is not cancelable");
+ });
+ element.setRangeText("foobar2", 0, 6);
+ q = true;
+ }, 10);
+ }, element.id + " setRangeText fires a select event");
+
+ promise_test(async () => {
+ element.value = "XXXXXXXXXXXXXXXXXXX";
+ const { length } = element.value;
+ element.setSelectionRange(0, length);
+ await untilEvent(element, "select");
+ element.setRangeText("foo", 2, 2);
+ await untilEvent(element, "select");
+ assert_equals(element.selectionStart, 0, ".selectionStart");
+ assert_equals(element.selectionEnd, length + 3, ".selectionEnd");
+ }, element.id + " setRangeText fires a select event when fully selected");
+
+ promise_test(async () => {
+ element.value = "XXXXXXXXXXXXXXXXXXX";
+ element.select();
+ await untilEvent(element, "select");
+ element.setRangeText("foo", 2, 2);
+ await untilEvent(element, "select");
+ assert_equals(element.selectionStart, 0, ".selectionStart");
+ assert_equals(element.selectionEnd, element.value.length, ".selectionEnd");
+ }, element.id + " setRangeText fires a select event after select()");
+ })
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html b/testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html
new file mode 100644
index 0000000000..3aba6b7adb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html
@@ -0,0 +1,304 @@
+<!DOCTYPE HTML>
+<title>Test of text field setSelectionRange</title>
+<link rel="author" title="Takeharu.Oshida" href="mailto:georgeosddev@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-textarea/input-setselectionrange">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="hide" style="display: block">
+ <input id="a" type="text" value="abcde">
+ <textarea id="b">abcde</textarea>
+</div>
+<script>
+var expected_direction_none;
+setup(function() {
+ var input = document.createElement("input");
+ input.setSelectionRange(0, 1, "none");
+ var direction = input.selectionDirection;
+ if (direction !== "none" && direction !== "forward") {
+ throw new Error("Unexpected direction");
+ }
+ expected_direction_none = direction;
+});
+
+test(function() {
+ var input = document.getElementById("a");
+ test(function() {
+ assert_equals(typeof(input.setSelectionRange), "function", "element must have 'setSelectionRange' function");
+ },"input typeof(input.setSelectionRange)'");
+
+ test(function() {
+ assert_equals(input.setSelectionRange(0,1,"forward"),undefined,"setSelectionRange is void functuon");
+ },"input setSelectionRange return void");
+
+ test(function() {
+ input.setSelectionRange(0,1)
+ assert_equals(input.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange(0,1)');
+
+ test(function() {
+ input.setSelectionRange(0,input.value.length+1)
+ assert_equals(input.selectionEnd, input.value.length, "Arguments greater than the length of the value of the text field must be treated as pointing at the end of the text field");
+ },'input setSelectionRange(0,input.value.length+1)');
+
+ test(function() {
+ input.setSelectionRange(input.value.length+1,input.value.length+1)
+ assert_equals(input.selectionStart, input.value.length, "Arguments (start) greater than the length of the value of the text field must be treated as pointing at the end of the text field");
+ assert_equals(input.selectionEnd, input.value.length, "Arguments (end) greater than the length of the value of the text field must be treated as pointing at the end of the text field");
+ },'input setSelectionRange(input.value.length+1,input.value.length+1)');
+
+ test(function() {
+ input.setSelectionRange(input.value.length+1,1)
+ assert_equals(input.selectionStart, 1, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange(input.value.length+1,1)');
+
+ test(function() {
+ input.setSelectionRange(2,2)
+ assert_equals(input.selectionStart, 2, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ assert_equals(input.selectionEnd, 2, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ },'input setSelectionRange(2,2)');
+
+ test(function() {
+ input.setSelectionRange(2,1)
+ assert_equals(input.selectionStart, 1, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ assert_equals(input.selectionEnd, 1, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ },'input setSelectionRange(2,1)');
+
+ test(function() {
+ input.setSelectionRange(0,1,"backward")
+ assert_equals(input.selectionDirection, "backward", 'The direction of the selection must be set to backward if direction is a case-sensitive match for the string "backward"');
+ },'input direction of setSelectionRange(0,1,"backward")');
+
+ test(function() {
+ input.setSelectionRange(0,1,"forward")
+ assert_equals(input.selectionDirection, "forward", 'The direction of the selection must be set to forward if direction is a case-sensitive match for the string "forward"');
+ },'input direction of setSelectionRange(0,1,"forward")');
+
+ test(function() {
+ input.setSelectionRange(0,1,"none")
+ assert_equals(input.selectionDirection, expected_direction_none);
+ },'input direction of setSelectionRange(0,1,"none")');
+
+ test(function() {
+ input.setSelectionRange(0,1,"hoge")
+ assert_equals(input.selectionDirection, expected_direction_none);
+ },'input direction of setSelectionRange(0,1,"hoge")');
+
+ test(function() {
+ input.setSelectionRange(0,1,"BACKWARD")
+ assert_equals(input.selectionDirection, expected_direction_none);
+ },'input direction of setSelectionRange(0,1,"BACKWARD")');
+
+ test(function() {
+ input.setSelectionRange(0,1)
+ assert_equals(input.selectionDirection, expected_direction_none);
+ },'input direction of setSelectionRange(0,1)');
+
+ test(function() {
+ input.setSelectionRange(1,-1);
+ assert_equals(input.selectionStart, 1, "element.selectionStart should be 1");
+ assert_equals(input.selectionEnd, input.value.length, "ECMAScript conversion to unsigned long");
+ },'input setSelectionRange(1,-1)');
+
+ test(function() {
+ input.setSelectionRange(-1,1);
+ assert_equals(input.selectionStart, 1, "ECMAScript conversion to unsigned long + if end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange(-1,1)');
+
+ test(function() {
+ input.setSelectionRange("string",1);
+ assert_equals(input.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange("string",1)');
+
+ test(function() {
+ input.setSelectionRange(true,1);
+ assert_equals(input.selectionStart, 1, "element.selectionStart should be 1");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange(true,1)');
+
+ test(function() {
+ input.setSelectionRange([],1);
+ assert_equals(input.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange([],1)');
+
+ test(function() {
+ input.setSelectionRange({},1);
+ assert_equals(input.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange({},1)');
+
+ test(function() {
+ input.setSelectionRange(NaN,1);
+ assert_equals(input.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange(NaN,1)');
+
+ test(function() {
+ input.setSelectionRange(null,1);
+ assert_equals(input.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange(null,1)');
+
+ test(function() {
+ input.setSelectionRange(undefined,1);
+ assert_equals(input.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(input.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'input setSelectionRange(undefined,1)');
+
+ test(function() {
+ input.setSelectionRange(Math.pow(2,32) - 2, Math.pow(2,32) - 1);
+ assert_equals(input.selectionStart, input.value.length,
+ "element.selectionStart should be value.length");
+ assert_equals(input.selectionEnd, input.value.length,
+ "element.selectionEnd should be value.length");
+ }, 'input setSelectionRange(Math.pow(2,32) - 2, Math.pow(2,32) - 1)');
+
+ test(function() {
+ input.setSelectionRange(Math.pow(2,31), Math.pow(2,32) - 1);
+ assert_equals(input.selectionStart, input.value.length,
+ "element.selectionStart should be value.length");
+ assert_equals(input.selectionEnd, input.value.length,
+ "element.selectionEnd should be value.length");
+ }, 'input setSelectionRange(Math.pow(2,31), Math.pow(2,32) - 1)');
+},"test of input.setSelectionRange");
+
+async_test(function() {
+ var q = false;
+ var input = document.getElementById("a");
+ input.addEventListener("select", this.step_func_done(function(e) {
+ assert_true(q, "event should be queued");
+ assert_true(e.isTrusted, "event is trusted");
+ assert_true(e.bubbles, "event bubbles");
+ assert_false(e.cancelable, "event is not cancelable");
+ }));
+ input.setSelectionRange(0, 1);
+ q = true;
+}, "input setSelectionRange fires a select event");
+
+test(function() {
+ var textarea = document.getElementById("b");
+ test(function() {
+ assert_equals(typeof(textarea.setSelectionRange), "function", "element must have 'setSelectionRange' function");
+ },"textarea typeof(input.setSelectionRange)'");
+
+ test(function() {
+ assert_equals(textarea.setSelectionRange(0,1,"forward"),undefined,"setSelectionRange is void functuon");
+ },"textarea setSelectionRange return void");
+
+ test(function() {
+ textarea.setSelectionRange(0,1)
+ assert_equals(textarea.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionEnd should be 1");
+ },'textarea setSelectionRange(0,1)');
+
+ test(function() {
+ textarea.setSelectionRange(0,textarea.value.length+1)
+ assert_equals(textarea.selectionEnd, textarea.value.length, "Arguments greater than the length of the value of the text field must be treated as pointing at the end of the text field");
+ },'textarea setSelectionRange(0,textarea.value.length+1)');
+
+ test(function() {
+ textarea.setSelectionRange(2,2)
+ assert_equals(textarea.selectionStart, 2, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ assert_equals(textarea.selectionEnd, 2, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ },'textarea setSelectionRange(2,2)');
+
+ test(function() {
+ textarea.setSelectionRange(2,1)
+ assert_equals(textarea.selectionStart, 1, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ assert_equals(textarea.selectionEnd, 1, "If end is less than or equal to start then the start of the selection and the end of the selection must both be placed immediately before the character with offset end");
+ },'textarea setSelectionRange(2,1)');
+
+ test(function() {
+ textarea.setSelectionRange(0,1,"backward")
+ assert_equals(textarea.selectionDirection, "backward", 'The direction of the selection must be set to backward if direction is a case-sensitive match for the string "backward"');
+ },'textarea direction of setSelectionRange(0,1,"backward")');
+
+ test(function() {
+ textarea.setSelectionRange(0,1,"forward")
+ assert_equals(textarea.selectionDirection, "forward", 'The direction of the selection must be set to forward if direction is a case-sensitive match for the string "forward"');
+ },'textarea direction of setSelectionRange(0,1,"forward")');
+
+ test(function() {
+ textarea.setSelectionRange(0,1,"none")
+ assert_equals(textarea.selectionDirection, expected_direction_none);
+ },'textarea direction of setSelectionRange(0,1,"none")');
+
+ test(function() {
+ textarea.setSelectionRange(0,1,"hoge")
+ assert_equals(textarea.selectionDirection, expected_direction_none);
+ },'textarea direction of setSelectionRange(0,1,"hoge")');
+
+ test(function() {
+ textarea.setSelectionRange(0,1,"BACKWARD")
+ assert_equals(textarea.selectionDirection, expected_direction_none);
+ },'textarea direction of setSelectionRange(0,1,"BACKWARD")');
+
+ test(function() {
+ textarea.setSelectionRange(0,1)
+ assert_equals(textarea.selectionDirection, expected_direction_none);
+ },'textarea direction of setSelectionRange(0,1)');
+
+ test(function() {
+ textarea.setSelectionRange("string",1);
+ assert_equals(textarea.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1");
+ },'textarea setSelectionRange("string",1)');
+
+ test(function() {
+ textarea.setSelectionRange(true,1);
+ assert_equals(textarea.selectionStart, 1, "element.selectionStart should be 1");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1");
+ },'textarea setSelectionRange(true,1)');
+
+ test(function() {
+ textarea.setSelectionRange([],1);
+ assert_equals(textarea.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1");
+ },'textarea setSelectionRange([],1)');
+
+ test(function() {
+ textarea.setSelectionRange({},1);
+ assert_equals(textarea.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1");
+ },'textarea setSelectionRange({},1)');
+
+ test(function() {
+ textarea.setSelectionRange(NaN,1);
+ assert_equals(textarea.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1");
+ },'textarea setSelectionRange(NaN,1)');
+
+ test(function() {
+ textarea.setSelectionRange(null,1);
+ assert_equals(textarea.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1");
+ },'textarea setSelectionRange(null,1)');
+
+ test(function() {
+ textarea.setSelectionRange(undefined,1);
+ assert_equals(textarea.selectionStart, 0, "element.selectionStart should be 0");
+ assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1");
+ },'textarea setSelectionRange(undefined,1)');
+
+ test(function() {
+ textarea.setSelectionRange(Math.pow(2,32) - 2, Math.pow(2,32) - 1);
+ assert_equals(textarea.selectionStart, textarea.value.length,
+ "element.selectionStart should be value.length");
+ assert_equals(textarea.selectionEnd, textarea.value.length,
+ "element.selectionEnd should be value.length");
+ }, 'textarea setSelectionRange(Math.pow(2,32) - 2, Math.pow(2,32) - 1)');
+
+ test(function() {
+ textarea.setSelectionRange(Math.pow(2,31), Math.pow(2,32) - 1);
+ assert_equals(textarea.selectionStart, textarea.value.length,
+ "element.selectionStart should be value.length");
+ assert_equals(textarea.selectionEnd, textarea.value.length,
+ "element.selectionEnd should be value.length");
+ }, 'textarea setSelectionRange(Math.pow(2,31), Math.pow(2,32) - 1)');
+},"test of textarea.setSelectionRange");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/active-onblur.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/active-onblur.html
new file mode 100644
index 0000000000..b9e1b9705e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/active-onblur.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="http://crbug.com/945854">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!-- This behavior is not explicitly specified. -->
+
+<button id=b1>button one</button>
+<button id=b2>button two</button>
+
+<script>
+promise_test(async () => {
+ b1.focus();
+
+ // Hold spacebar down
+ await (new test_driver.Actions()).keyDown('\uE00D').send();
+ assert_equals(document.querySelector(':active'), b1,
+ 'Buttons should be :active while the spacebar is pressed down.');
+
+ // Press tab
+ await (new test_driver.Actions()).keyDown('\uE004').keyUp('\uE004').send();
+ assert_equals(document.querySelector(':active'), null,
+ 'Buttons should not be :active after tab is used to change focus.');
+
+ // Release spacebar
+ await (new test_driver.Actions()).keyUp('\uE00D').send();
+}, 'Buttons should clear :active when the user tabs away from them while holding spacebar.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-frame.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-frame.html
new file mode 100644
index 0000000000..37619d7912
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-frame.html
@@ -0,0 +1,3 @@
+<form action="about:blank">
+ <button id="submit">Submit</button>
+</form>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-keyup-prevented.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-keyup-prevented.html
new file mode 100644
index 0000000000..29ffbbdd99
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate-keyup-prevented.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Button activation submits on keyup, but not if keydown is defaultPrevented</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1481400">
+<link rel=author href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel=author href="https://mozilla.org" title="Mozilla">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<button>The button</button>
+<script>
+let button = document.querySelector("button");
+promise_test(async t => {
+ button.focus();
+ assert_equals(document.activeElement, button, "Button should be focused");
+ let clickPromise = new Promise(resolve => {
+ button.addEventListener("click", resolve, { once: true });
+ });
+
+ await test_driver.send_keys(button, " ");
+
+ await clickPromise;
+
+ assert_true(true, "Button should have activated");
+
+ document.addEventListener("keydown", t.step_func(function(e) {
+ e.preventDefault();
+ }));
+
+ button.addEventListener("click", t.unreached_func("button got incorrectly activated"));
+
+ await test_driver.send_keys(button, " ");
+
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ assert_true(true, "Button should not have activated");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate.html
new file mode 100644
index 0000000000..43fe96d398
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-activate.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe src="button-activate-frame.html" onload="runTest()"></iframe>
+<script>
+var t = async_test("button activation behaviour submits form");
+function runTest() {
+ var iframe = document.querySelector('iframe');
+ iframe.onload = t.step_func(function() {
+ t.done();
+ });
+ var doc = iframe.contentDocument;
+ doc.querySelector('button').click();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-checkvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-checkvalidity.html
new file mode 100644
index 0000000000..55d3091a0a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-checkvalidity.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>button_checkValidity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><button id='button_id'>button</button></p>
+ </form>
+ <script>
+
+ var button = document.getElementById("button_id");
+
+ try
+ {
+ var ret = button.checkValidity();
+
+ test(function() {
+ assert_equals(ret, true, "calling of checkValidity method is failed.");
+ });
+ }
+ catch (e) {
+ test(function() {
+ assert_unreached("autofocus attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-click-submits.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-click-submits.html
new file mode 100644
index 0000000000..86b92402e4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-click-submits.html
@@ -0,0 +1,232 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Clicking a button should submit the form</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-button-type-submit-state">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ form.appendChild(button);
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_equals(ev.target, form);
+ }));
+
+ button.click();
+
+}, "clicking a button with .click() should trigger a submit (form connected)");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ form.appendChild(button);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_unreached("Form should not be submitted");
+ }));
+
+ button.click();
+ t.step_timeout(() => t.done(), 500);
+
+}, "clicking a button with .click() should not trigger a submit (form disconnected)");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ form.appendChild(button);
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_equals(ev.target, form);
+ }));
+
+ const e = new MouseEvent("click");
+ button.dispatchEvent(e);
+
+}, "clicking a button by dispatching an event should trigger a submit (form connected)");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ form.appendChild(button);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_unreached("Form should not be submitted");
+ }));
+
+ const e = new MouseEvent("click");
+ button.dispatchEvent(e);
+ t.step_timeout(() => t.done(), 500);
+
+}, "clicking a button by dispatching an event should not trigger a submit (form disconnected)");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ form.appendChild(button);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_unreached("Form should not be submitted");
+ }));
+
+ button.addEventListener("click", t.step_func(ev => {
+ ev.preventDefault();
+ t.step_timeout(() => t.done(), 500);
+ }));
+ button.click();
+
+}, "clicking a button that cancels the event should not trigger a submit");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ button.setAttribute("disabled", "");
+ form.appendChild(button);
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_unreached("Form should not be submitted");
+ }));
+
+ button.click();
+ t.step_timeout(() => t.done(), 500);
+
+}, "clicking a disabled button (via disabled attribute) should not trigger submit");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ form.innerHTML = `<fieldset disabled><button>hello</button></fieldset>`;
+ const button = form.querySelector("button");
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_unreached("Form should not be submitted");
+ }));
+
+ button.click();
+ t.step_timeout(() => t.done(), 500);
+
+}, "clicking a disabled button (via ancestor fieldset) should not trigger submit");
+
+test(t => {
+
+ const form = document.createElement("form");
+ form.innerHTML = `<fieldset disabled><legend><button>hello</button></legend></fieldset>`;
+ const button = form.querySelector("button");
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_equals(ev.target, form);
+ }));
+
+ button.click();
+
+}, "clicking a button inside a disabled fieldset's legend *should* trigger submit");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ const span = document.createElement("span");
+ button.appendChild(span);
+ form.appendChild(button);
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_equals(ev.target, form);
+ }));
+
+ span.click();
+
+}, "clicking the child of a button with .click() should trigger a submit");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ const span = document.createElement("span");
+ button.appendChild(span);
+ form.appendChild(button);
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_equals(ev.target, form);
+ }));
+
+ const e = new MouseEvent("click", { bubbles: true });
+ span.dispatchEvent(e);
+
+}, "clicking the child of a button by dispatching a bubbling event should trigger a submit");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ const span = document.createElement("span");
+ button.appendChild(span);
+ form.appendChild(button);
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_unreached("Form should not be submitted");
+ }));
+
+ span.addEventListener("click", t.step_func(ev => {
+ t.step_timeout(() => t.done(), 500);
+ }));
+
+ const e = new MouseEvent("click", { bubbles: false });
+ span.dispatchEvent(e);
+
+}, "clicking the child of a button by dispatching a non-bubbling event should not trigger submit");
+
+async_test(t => {
+
+ const form = document.createElement("form");
+ const button = document.createElement("button");
+ button.disabled = true;
+ const span = document.createElement("span");
+ button.appendChild(span);
+ form.appendChild(button);
+ document.body.appendChild(form);
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ assert_unreached("Form should not be submitted");
+ }));
+
+ span.addEventListener("click", t.step_func(ev => {
+ assert_true(true, "span was clicked");
+ t.step_timeout(() => t.done(), 500);
+ }));
+
+ span.click();
+
+}, "clicking the child of a disabled button with .click() should not trigger submit");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html
new file mode 100644
index 0000000000..be7806e1ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: Button - events</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-button-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form name="fm1" style="display:none">
+ <button id="btn">BUTTON</button>
+ <button id="menu_btn" type="menu" menu="menu">MENU BUTTON</button>
+</form>
+<script>
+
+var btn = document.getElementById("btn"),
+ menu_btn = document.getElementById("menu_btn"),
+ t1 = async_test("The submit event must be fired when click a button in submit status"),
+ t2 = async_test("The reset event must be fired when click a button in reset status");
+ t3 = async_test("type=button shouldn't trigger submit or reset events");
+ t4 = async_test("Switching from type=button to type=submit should submit the form");
+ t5 = async_test("Switching from type=button to type=reset should reset the form");
+ t6 = async_test("Innermost button should submit its form");
+ t7 = async_test("Innermost button should reset its form");
+ t8 = async_test("Anchor inside a button should be prevent button activation");
+ t9 = async_test("input type=submit inside a button should be prevent button activation");
+
+document.forms.fm1.onsubmit = t1.step_func(function (evt) {
+ evt.preventDefault();
+ assert_true(evt.isTrusted, "The isTrusted attribute of the submit event should be true.");
+ assert_true(evt.bubbles, "The bubbles attribute of the submit event should be true.");
+ assert_true(evt.cancelable, "The cancelable attribute of the submit event should be true.");
+ assert_true(evt instanceof Event, "The submit event is an instance of Event interface.");
+ t1.done();
+});
+
+document.forms.fm1.onreset = t2.step_func(function (evt) {
+ assert_true(evt.isTrusted, "The isTrusted attribute of the reset event should be true.");
+ assert_true(evt.bubbles, "The bubbles attribute of the reset event should be true.");
+ assert_true(evt.cancelable, "The cancelable attribute of the reset event should be true.");
+ assert_true(evt instanceof Event, "The reset event is an instance of Event interface.");
+ t2.done();
+});
+
+t1.step(function () {
+ btn.type = "submit";
+ assert_equals(btn.type, "submit", "The button type should be 'submit'.");
+ btn.click();
+});
+
+t2.step(function () {
+ btn.type = "reset";
+ assert_equals(btn.type, "reset", "The button type should be 'reset'.");
+ btn.click();
+});
+
+t3.step(function () {
+ btn.type = "button";
+ assert_equals(btn.type, "button", "The button type should be 'button'.");
+ document.forms.fm1.onsubmit = t3.step_func(function (evt) {
+ assert_unreached("type=button shouldn't trigger submission.");
+ });
+ document.forms.fm1.onreset = t3.step_func(function (evt) {
+ assert_unreached("type=button shouldn't reset the form.");
+ });
+ btn.click();
+ t3.done();
+});
+
+t4.step(function () {
+ btn.type = "button";
+ btn.onclick = function() { btn.type = "submit"; }
+ document.forms.fm1.onsubmit = t4.step_func(function (evt) {
+ evt.preventDefault();
+ assert_equals(btn.type, "submit", "The button type should be 'submit'.");
+ t4.done();
+ });
+ btn.click();
+});
+
+t5.step(function () {
+ btn.type = "button";
+ btn.onclick = function() { btn.type = "reset"; }
+ document.forms.fm1.onreset = t5.step_func(function (evt) {
+ evt.preventDefault();
+ assert_equals(btn.type, "reset", "The button type should be 'reset'.");
+ t5.done();
+ });
+ btn.click();
+});
+
+t6.step(function () {
+ btn.type = "submit";
+ btn.innerHTML = "";
+ var fm2 = document.createElement("form");
+ var btn2 = document.createElement("button");
+ btn2.type = "submit";
+ fm2.appendChild(btn2);
+ btn.appendChild(fm2);
+ assert_true(document.forms.fm1.contains(fm2), "Should have nested forms");
+
+ function submitListener(evt) {
+ evt.preventDefault();
+ assert_equals(evt.target, fm2, "Innermost form should have got the submit event");
+ };
+ window.addEventListener("submit", submitListener, true);
+ btn2.click();
+ window.removeEventListener("submit", submitListener, true);
+ t6.done();
+});
+
+t7.step(function () {
+ btn.type = "reset";
+ btn.innerHTML = "";
+ var fm2 = document.createElement("form");
+ var btn2 = document.createElement("button");
+ btn2.type = "reset";
+ fm2.appendChild(btn2);
+ btn.appendChild(fm2);
+ assert_true(document.forms.fm1.contains(fm2), "Should have nested forms");
+
+ function resetListener(evt) {
+ evt.currentTarget.removeEventListener(evt.type, resetListener, true);
+ evt.preventDefault();
+ assert_equals(evt.target, fm2, "Innermost form should have got the reset event");
+ t7.done();
+ };
+ window.addEventListener("reset", resetListener, true);
+ btn2.click();
+});
+
+t8.step(function () {
+ btn.type = "submit";
+ btn.innerHTML = "";
+ var a = document.createElement("a");
+ a.href = "#";
+ btn.appendChild(a);
+ document.forms.fm1.onsubmit = t8.step_func(function (evt) {
+ assert_unreached("type=button shouldn't trigger submission.");
+ });
+
+ a.click();
+ t8.done();
+});
+
+t9.step(function () {
+ btn.type = "submit";
+ btn.innerHTML = "";
+ var fm2 = document.createElement("form");
+ var btn2 = document.createElement("input");
+ btn2.type = "submit";
+ fm2.appendChild(btn2);
+ btn.appendChild(fm2);
+ assert_true(document.forms.fm1.contains(fm2), "Should have nested forms");
+
+ function submitListener(evt) {
+ evt.preventDefault();
+ assert_equals(evt.target, fm2, "Innermost form should have got the submit event");
+ };
+
+ window.addEventListener("submit", submitListener, true);
+ btn2.click();
+ window.removeEventListener("submit", submitListener, true);
+ t9.done();
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-labels.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-labels.html
new file mode 100644
index 0000000000..b06c71f95d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-labels.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>button_labels</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><label>Full name:<label>(name)<button id='button_id1'>button1</button><small>Format: First Last</small></label></label></p>
+ <p><label>Age: <button id='button_id2'>button2</button></label></p>
+ </form>
+ <script>
+ test(function() {
+ var button1 = document.getElementById("button_id1");
+ var button2 = document.getElementById("button_id2");
+
+ assert_true(button1.labels instanceof NodeList, "button1.labels is NodeList");
+ assert_equals(button1.labels.length, 2, "button1.labels.length");
+
+ assert_true(button2.labels instanceof NodeList, "button2.labels is NodeList");
+ assert_equals(button2.labels.length, 1, "button2.labels.length");
+ });
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-menu-historical.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-menu-historical.html
new file mode 100644
index 0000000000..fe68be0fd0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-menu-historical.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<title>Test that nobody implemented the now-removed menu type and attribute on button</title>
+<meta charset="utf-8">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://github.com/whatwg/html/pull/2342">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button id="b" type="menu" menu="m">button</button>
+<menu id="m"></menu>
+
+<script>
+"use strict";
+
+const button = document.querySelector("button");
+
+test(() => {
+ assert_false('menu' in button, 'The menu property must not exist on the button');
+ assert_equals(button.menu, undefined, 'The value of the menu property on the button must be undefined');
+}, 'button.menu, the potentially-reflecting IDL attribute, does not exist');
+
+test(() => {
+ assert_equals(button.type, 'submit', 'The type property must reflect as its invalid value default of submit');
+}, 'button.type reflects properly');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-setcustomvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-setcustomvalidity.html
new file mode 100644
index 0000000000..1747bd727a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-setcustomvalidity.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<title>button setCustomValidity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button id='button_test'></button>
+
+<script>
+
+test(() => {
+ let elem = document.getElementById("button_test");
+ assert_false(elem.validity.customError);
+ elem.setCustomValidity("custom error");
+ assert_true(elem.validity.customError);
+}, "button setCustomValidity is correct")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-children.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-children.html
new file mode 100644
index 0000000000..9cec2bd875
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-children.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe name=frame1 id=frame1></iframe>
+<form id=form1 target=frame1 action="does_not_exist.html">
+ <div id=parentdiv>
+ <button id=submitbutton type=submit>
+ <div id=buttonchilddiv>
+ button child div text
+ </div>
+ </button>
+ </div>
+</form>
+
+<script>
+async_test(t => {
+ window.addEventListener('load', () => {
+ const frame1 = document.getElementById('frame1');
+ frame1.addEventListener('load', t.step_func_done(() => {}));
+
+ const submitButton = document.getElementById('submitbutton');
+ submitButton.addEventListener('click', event => {
+ event.preventDefault();
+ const form = document.getElementById('form1');
+ form.submit();
+ });
+
+ const buttonChildDiv = document.getElementById('buttonchilddiv');
+ buttonChildDiv.click();
+ });
+}, 'This test will pass if a form navigation successfully occurs when clicking a child element of a <button type=submit> element with a onclick event handler which prevents the default form submission and manually calls form.submit() instead.');
+
+async_test(t => {
+ window.addEventListener('load', () => {
+ const frame1 = document.getElementById('frame1');
+ frame1.addEventListener('load', t.step_func_done(() => {}));
+
+ const submitButton = document.getElementById('submitbutton');
+ submitButton.addEventListener('click', event => {
+ const form = document.getElementById('form1');
+ form.submit();
+ });
+
+ const parentDiv = document.getElementById("parentdiv");
+ parentDiv.addEventListener("click", event => {
+ // event was already handled for the button
+ // but it's activation behavior won't have run yet and we prevent that now
+ event.preventDefault();
+ })
+
+ submitButton.click();
+ });
+}, "clicking a submit button, which calls form.submit and has a parent calling e.prevenDefault() should still submit the form");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children-jssubmit.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children-jssubmit.html
new file mode 100644
index 0000000000..26ce16dd2d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children-jssubmit.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe name=frame1 id=frame1></iframe>
+<form id=form1 target=frame1 action="does_not_exist.html">
+ <button id=submitbutton type=submit>
+ <span id=outerchild>
+ <span id=innerchild>submit</span>
+ </span>
+ </button>
+</form>
+
+<script>
+async_test(t => {
+ window.addEventListener('load', () => {
+ const frame1 = document.getElementById('frame1');
+ frame1.addEventListener('load', t.step_func_done(() => {}));
+
+ const submitButton = document.getElementById('submitbutton');
+ submitButton.addEventListener('click', event => {
+ document.getElementById('outerchild').remove();
+ document.getElementById('form1').submit();
+ });
+
+ document.getElementById('innerchild').click();
+ });
+}, 'This test will pass if a form navigation successfully occurs when clicking a child element of a <button type=submit> element with a onclick event handler which removes the button\'s child and then calls form.submit().');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children.html
new file mode 100644
index 0000000000..1dc259564c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-children.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe name=frame1 id=frame1></iframe>
+<form id=form1 target=frame1 action="does_not_exist.html">
+ <button id=submitbutton type=submit>
+ <span id=outerchild>
+ <span id=innerchild>submit</span>
+ </span>
+ </button>
+</form>
+
+<script>
+async_test(t => {
+ window.addEventListener('load', () => {
+ const frame1 = document.getElementById('frame1');
+ frame1.addEventListener('load', t.step_func_done(() => {}));
+
+ const submitButton = document.getElementById('submitbutton');
+ submitButton.addEventListener('click', event => {
+ document.getElementById('outerchild').remove();
+ });
+
+ document.getElementById('innerchild').click();
+ });
+}, 'This test will pass if a form navigation successfully occurs when clicking a child element of a <button type=submit> element with a onclick event handler which removes the button\'s child.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-jssubmit.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-jssubmit.html
new file mode 100644
index 0000000000..6e71d958d6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-submit-remove-jssubmit.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe name="frame" id="frame"></iframe>
+<form id="form" target="frame" action="does_not_exist.html">
+ <input id="input" name="name" value="foo">
+ <button id="submitbutton" type="submit">Submit</button>
+</form>
+
+<script>
+async_test(t => {
+ window.addEventListener('load', () => {
+ const frame = document.getElementById('frame');
+ frame.addEventListener('load', t.step_func_done(() => {
+ const expected = (new URL("does_not_exist.html?name=bar", location.href)).href;
+ assert_equals(frame.contentWindow.location.href, expected);
+ }));
+
+ const form = document.getElementById('form');
+ const input = document.getElementById('input');
+ const submitButton = document.getElementById('submitbutton');
+ submitButton.addEventListener('click', event => {
+ submitButton.remove();
+ form.submit();
+ input.value = "bar";
+ form.submit();
+ input.value = "baz";
+ });
+
+ submitButton.click();
+ });
+}, 'This test will pass if a form navigation successfully occurs when clicking a <button type=submit> element with a onclick event handler which removes the button and then calls form.submit().');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-type-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-type-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..30a2c24a80
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-type-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-button-type">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="button@type values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<button type="reset">
+<button type="ReSeT">
+<button type="reſet">
+<button type="submit">
+<button type="SuBmIt">
+<button type="Å¿ubmit">
+<script>
+const button = document.querySelectorAll("button");
+
+test(() => {
+ assert_equals(button[0].type, "reset", "lowercase valid");
+ assert_equals(button[1].type, "reset", "mixed case valid");
+ assert_equals(button[2].type, "submit", "non-ASCII invalid");
+}, "keyword reset");
+
+test(() => {
+ assert_equals(button[3].type, "submit", "lowercase valid");
+
+ // vacuous: the invalid value default is currently submit, so even if the UA
+ // treats this as invalid, the observable behaviour would still be correct
+ assert_equals(button[4].type, "submit", "mixed case valid");
+
+ // vacuous: the invalid value default is currently submit, so even if the UA
+ // treats this as valid, the observable behaviour would still be correct
+ assert_equals(button[5].type, "submit", "non-ASCII invalid");
+}, "keyword submit");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-type.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-type.html
new file mode 100644
index 0000000000..6cfd6687c7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-type.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLButtonElement.prototype.type</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-button-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+
+ const button = document.createElement("button");
+ assert_equals(button.type, "submit");
+
+}, "a button's type should be submit by default");
+
+test(() => {
+
+ const button = document.createElement("button");
+
+ for (const type of ["reset", "button", "submit"]) {
+ button.type = type;
+ assert_equals(button.type, type);
+
+ button.type = type.toUpperCase();
+ assert_equals(button.type, type);
+ }
+
+ button.type = "reset";
+ button.type = "asdfgdsafd";
+ assert_equals(button.type, "submit");
+
+ button.type = "reset";
+ button.type = "";
+ assert_equals(button.type, "submit");
+
+}, "a button's type should stay within the range of valid values");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-untrusted-key-event.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-untrusted-key-event.html
new file mode 100644
index 0000000000..dd34d31233
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-untrusted-key-event.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Forms</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<form id="input_form">
+ <button name="submitButton" type="submit">Submit</button>
+ <button name="resetButton" type="reset">Reset</button>
+ <button name="buttonButton" type="button">Button</button>
+</form>
+<script type="module">
+const form = document.querySelector("form");
+form.addEventListener("submit", (e) => {
+ e.preventDefault();
+ assert_true(false, 'form should not be submitted');
+});
+
+for (const button of document.querySelectorAll("button")) {
+ button.addEventListener("click", function(e) {
+ assert_true(false, `${button.type} button should not be clicked`);
+ });
+}
+
+// Create and append button elements
+for (const button of document.querySelectorAll("button")) {
+ test(() => {
+ // keyCode: Enter
+ button.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ button.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ button.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ button.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: " ",
+ })
+ );
+ }, `Dispatching untrusted keypress events to ${button.type} button should not cause click event`);
+
+ test(() => {
+ // keyCode: Enter
+ button.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 13,
+ })
+ );
+ button.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ button.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: "Enter",
+ })
+ );
+ button.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ button.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 32,
+ })
+ );
+ button.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ button.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: " ",
+ })
+ );
+ button.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: " ",
+ })
+ );
+ }, `Dispatching untrusted keyup/keydown events to ${button.type} button should not cause click event`);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validation.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validation.html
new file mode 100644
index 0000000000..f3b57fd296
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validation.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>button element validation</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-button-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<button id=btn1>button</button>
+<button id=btn2 type=submit>button</button>
+<button id=btn3 type=reset>button</button>
+<button id=btn4 type=button>button</button>
+<button id=btn5 type=menu>button</button>
+<button id=btn6 type=foobar>button</button>
+<script>
+ function willValid(element, expectedType, willValidate, desc) {
+ test(function(){
+ assert_equals(element.type, expectedType);
+ assert_equals(element.willValidate, willValidate);
+ }, desc);
+ }
+
+ willValid(document.getElementById('btn1'), "submit", true, "missing type attribute");
+ willValid(document.getElementById('btn2'), "submit", true, "submit type attribute");
+ willValid(document.getElementById('btn3'), "reset", false, "reset type attribute");
+ willValid(document.getElementById('btn4'), "button", false, "button type attribute");
+ willValid(document.getElementById('btn5'), "submit", true, "historical menu type attribute");
+ willValid(document.getElementById('btn6'), "submit", true, "invalid type attribute");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validationmessage.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validationmessage.html
new file mode 100644
index 0000000000..a2572ed7be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validationmessage.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>button_validationMessage</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><button id='button_id'>button</button></p>
+ </form>
+ <script>
+
+ var button = document.getElementById("button_id");
+
+ if (typeof(button.validationMessage) == "string") {
+ test(function() {
+ assert_equals(button.validationMessage, "", "validationMessage attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("validationMessage attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validity.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validity.html
new file mode 100644
index 0000000000..acc02d92bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-validity.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>button_validity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><button id='button_id'>button</button></p>
+ </form>
+ <script>
+
+ var button = document.getElementById("button_id");
+
+ if (typeof(button.validity) == "object") {
+ test(function() {
+ assert_equals(button.validity.valueMissing, false, "validity attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("validity attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate-readonly-attribute.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate-readonly-attribute.html
new file mode 100644
index 0000000000..c4a05a4401
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate-readonly-attribute.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Button element with "readonly" attribute shouldn't be barred from constraint validation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button id="implicitSubmitButton" readonly>1</button>
+<button id="explicitSubmitButton" readonly type="submit">2</button>
+<script>
+ test(() => {
+ assert_true(implicitSubmitButton.willValidate);
+ assert_true(explicitSubmitButton.willValidate);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate.html
new file mode 100644
index 0000000000..e91c5e3843
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-willvalidate.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>button_willValidate</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><button id='button_id'>button</button></p>
+ </form>
+ <script>
+
+ var button = document.getElementById("button_id");
+
+ if (typeof(button.willValidate) == "boolean") {
+ test(function() {
+ assert_equals(button.willValidate, true, "willValidate attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("willValidate attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-datalist-element/datalistoptions.html b/testing/web-platform/tests/html/semantics/forms/the-datalist-element/datalistoptions.html
new file mode 100644
index 0000000000..245d43cec4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-datalist-element/datalistoptions.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Datalist element options</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-datalist-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<label>
+ Number:
+ <input list=numbers>
+</label>
+<datalist id=numbers>
+ <label> Select number:
+ <select id=num>
+ <option label="zero" value="0">
+ <option label="one" value="1">
+ <option label="two">2
+ <option label="three" disabled>3
+ <option>
+ </select>
+ </label>
+</datalist>
+<script>
+ test(function(){
+ var datalist = document.getElementById('numbers'),
+ labels = [],
+ values = [];
+ assert_equals(datalist.options.length, 5);
+
+ for (var i = 0, len = datalist.options.length; i < len; i++) {
+ assert_equals(datalist.options[i], datalist.options.item(i));
+ labels.push(datalist.options[i].label);
+ values.push(datalist.options[i].value);
+ }
+ assert_array_equals(labels, ["zero", "one", "two", "three", ""]);
+ assert_array_equals(values, ["0", "1", "2", "3", ""]);
+ }, "options label/value");
+
+ test(function(){
+ assert_false(document.getElementById('num').willValidate);
+ }, "If an element has a datalist element ancestor, it is barred from constraint validation");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-datalist-element/remove-datalist-crash.html b/testing/web-platform/tests/html/semantics/forms/the-datalist-element/remove-datalist-crash.html
new file mode 100644
index 0000000000..b40d5feebe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-datalist-element/remove-datalist-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Remove datalist element crash</title>
+<link rel="help" href="https://crbug.com/1199861">
+<datalist id="datalist"><option>foo</option></datalist>
+<input id="input">
+<input list="datalist">
+<script>
+ document.body.offsetTop;
+ input.appendChild(datalist);
+ datalist.remove();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html
new file mode 100644
index 0000000000..9e6e52d7b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/HTMLFieldSetElement.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: HTMLFieldSetElement interface</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-fieldset-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form name="fm1" style="display:none">
+ <fieldset id="fs_outer">
+ <legend><input type="checkbox" name="cb"></legend>
+ <input type=text name="txt" id="ctl1">
+ <button id="ctl2" name="btn">BUTTON</button>
+ <fieldset id="fs_inner">
+ <input type="text" name="txt_inner">
+ <progress name="pg" value="0.5"></progress>
+ </fieldset>
+ </fieldset>
+</form>
+<script>
+
+var fm1,
+ fs_outer,
+ children_outer;
+
+setup(function () {
+ fm1 = document.forms.fm1;
+ fs_outer = document.getElementById("fs_outer");
+ children_outer = fs_outer.elements;
+});
+
+test(function () {
+ assert_equals(fs_outer.type, "fieldset", "The value of type attribute is incorrect.");
+}, "The type attribute must return 'fieldset'");
+
+test(function () {
+ assert_equals(fs_outer.form, fm1, "The fieldset should have a form owner.");
+}, "The form attribute must return the fieldset's form owner");
+
+test(function () {
+ assert_equals(children_outer.constructor, HTMLCollection,
+ "The elements attribute should be an HTMLCollection object");
+}, "The elements must return an HTMLCollection object");
+
+test(function () {
+ var fs_inner = document.getElementById("fs_inner");
+ var children_inner = fs_inner.elements;
+ assert_array_equals(children_inner, [fm1.txt_inner],
+ "The items in the collection must be children of the inner fieldset element.");
+ assert_array_equals(children_outer, [fm1.cb, fm1.txt, fm1.btn, fm1.fs_inner, fm1.txt_inner],
+ "The items in the collection must be children of the outer fieldset element.");
+}, "The controls must root at the fieldset element");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/README.md b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/README.md
new file mode 100644
index 0000000000..b238a023dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/README.md
@@ -0,0 +1,12 @@
+Fieldset accessibility tests
+============================
+
+These tests are intended to test the accessibility of the fieldset and legend elements.
+
+To run these tests, open the browser's developer tools and navigate to the Accessibility pane (may
+need to activate it in Settings), or use an OS-level accessibility inspector, and verify that the
+accessible name/role matches the expected accessible name/role.
+
+The following issue discusses ways to automate these tests:
+
+https://github.com/web-platform-tests/wpt/issues/12791
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/aria-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/aria-manual.html
new file mode 100644
index 0000000000..c61d62769a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/aria-manual.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>fieldset accessibility test: ARIA</title>
+<div id=fieldset role=group aria-labelledby=legend>
+ <div id=legend>Foo</div>
+ <input>
+</div>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/baseline-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/baseline-manual.html
new file mode 100644
index 0000000000..2ee1ab20e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/baseline-manual.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>fieldset accessibility test: baseline</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-appearance-none-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-appearance-none-manual.html
new file mode 100644
index 0000000000..dbc4edc2a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-appearance-none-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset -webkit-appearance: none</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ fieldset { -webkit-appearance: none; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-contents-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-contents-manual.html
new file mode 100644
index 0000000000..943a030337
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-contents-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset display: contents</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ fieldset { display: contents; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-none-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-none-manual.html
new file mode 100644
index 0000000000..b45576036a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-display-none-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset display: none</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ fieldset { display: none; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected no accessible node for id=fieldset.
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-div-display-contents-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-div-display-contents-manual.html
new file mode 100644
index 0000000000..2eb01f2a71
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-div-display-contents-manual.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset div display: contents</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ div { display: contents; }
+</style>
+<fieldset id=fieldset>
+ <div>
+ <legend>Foo</legend>
+ <input>
+ </div>
+</fieldset>
+<p>Expected accessible name for id=fieldset: ""
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-none-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-none-manual.html
new file mode 100644
index 0000000000..4638a2b8d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-none-manual.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset role=none</title>
+<link rel=help href=http://w3c.github.io/aria/#none>
+<fieldset id=fieldset role=none>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected no accessible node for id=fieldset.
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-presentation-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-presentation-manual.html
new file mode 100644
index 0000000000..e1360d29c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-role-presentation-manual.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset role=presentation</title>
+<link rel=help href=http://w3c.github.io/aria/#presentation>
+<fieldset id=fieldset role=presentation>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected no accessible node for id=fieldset.
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-collapse-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-collapse-manual.html
new file mode 100644
index 0000000000..a3dd273bbe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-collapse-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset visibility: collapse</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ fieldset { visibility: collapse; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected no accessible node for id=fieldset.
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-hidden-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-hidden-manual.html
new file mode 100644
index 0000000000..894f00af52
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/fieldset-visibility-hidden-manual.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>fieldset accessibility test: fieldset visibility: hidden</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ fieldset { visibility: hidden; }
+ legend, input { visibility: visible; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected no accessible node for id=fieldset.
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/flexbox-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/flexbox-manual.html
new file mode 100644
index 0000000000..2d3d2a929c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/flexbox-manual.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>fieldset accessibility test: flexbox</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ fieldset { display: flex; }
+ legend { float: left; flex: auto; }
+ input { display: block; flex: auto; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/grid-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/grid-manual.html
new file mode 100644
index 0000000000..9d966d0113
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/grid-manual.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>fieldset accessibility test: grid</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ fieldset { display: grid; grid-template-columns: auto auto; }
+ legend { float: left; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-abspos-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-abspos-manual.html
new file mode 100644
index 0000000000..019e63fcd3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-abspos-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: position: absolute legend</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend { position: absolute; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-display-none-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-display-none-manual.html
new file mode 100644
index 0000000000..bc5d9fb7f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-display-none-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: legend child display: none</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend > span { display: none; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo<span>Bar</span></legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-visibility-hidden-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-visibility-hidden-manual.html
new file mode 100644
index 0000000000..01ceb9ec65
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-child-visibility-hidden-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: legend visibility: hidden</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend > span { visibility: hidden; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo<span>Bar</span></legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-contents-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-contents-manual.html
new file mode 100644
index 0000000000..f9fd1a31b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-contents-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: legend display: contents</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend { display: contents; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-none-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-none-manual.html
new file mode 100644
index 0000000000..14060b99f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-display-none-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: legend display: none</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend { display: none; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: ""
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-float-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-float-manual.html
new file mode 100644
index 0000000000..40f2c4ac23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-float-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: floating legend</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend { float: left; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-role-group-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-role-group-manual.html
new file mode 100644
index 0000000000..e15ff4d810
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-role-group-manual.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>fieldset accessibility test: legend role=group aria-labelledby=fieldset</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<fieldset id=fieldset>
+ <legend role=group aria-labelledby=fieldset>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: ""
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-collapse-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-collapse-manual.html
new file mode 100644
index 0000000000..c44bb1e888
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-collapse-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: legend visibility: collapse</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend { visibility: collapse; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: ""
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-hidden-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-hidden-manual.html
new file mode 100644
index 0000000000..f989712565
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/legend-visibility-hidden-manual.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>fieldset accessibility test: legend visibility: hidden</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<style>
+ legend { visibility: hidden; }
+</style>
+<fieldset id=fieldset>
+ <legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: ""
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/multiple-legends-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/multiple-legends-manual.html
new file mode 100644
index 0000000000..5d25317ad0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/multiple-legends-manual.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>fieldset accessibility test: multiple legends</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<fieldset id=fieldset>
+ <div></div>
+ <legend>Foo</legend>
+ <legend>Bar</legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/role-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/role-manual.html
new file mode 100644
index 0000000000..d09c203b6b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/role-manual.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>fieldset accessibility test: role</title>
+<fieldset id=fieldset>
+ <legend id=legend>Foo</legend>
+ <input>
+</fieldset>
+<p>Expected accessible role for id=fieldset: "group"
+<p>Expected accessible role for id=legend: No corresponding role
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/shadow-dom-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/shadow-dom-manual.html
new file mode 100644
index 0000000000..bb93d07644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/shadow-dom-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>fieldset accessibility test: shadow DOM</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<template id="my-fieldset">
+ <fieldset id=fieldset>
+ <slot name="my-text"></slot>
+ <input>
+ </fieldset>
+</template>
+
+<my-fieldset>
+ <legend slot="my-text">Foo</legend>
+</my-fieldset>
+
+<p>Expected accessible name for id=fieldset: ""
+
+<script>
+customElements.define('my-fieldset',
+ class extends HTMLElement {
+ constructor() {
+ super();
+
+ const template = document.getElementById('my-fieldset');
+ const templateContent = template.content;
+
+ this.attachShadow({mode: 'open'}).appendChild(
+ templateContent.cloneNode(true)
+ );
+ }
+ }
+);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/title-attribute-and-empty-legend-manual.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/title-attribute-and-empty-legend-manual.html
new file mode 100644
index 0000000000..0169a513a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/accessibility/title-attribute-and-empty-legend-manual.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>fieldset accessibility test: title attribute and empty legend</title>
+<link rel=help href=https://w3c.github.io/html-aam/#fieldset-element-accessible-name-computation>
+<fieldset id=fieldset title="Foo">
+ <legend></legend>
+ <input>
+</fieldset>
+<p>Expected accessible name for id=fieldset: "Foo"
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-001.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-001.html
new file mode 100644
index 0000000000..02137ab97e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-001.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Fieldset disabled</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-fieldset-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form>
+ <fieldset id=fs disabled>
+ <legend>
+ <input type=checkbox id=clubc_l1>
+ <input type=radio id=clubr_l1>
+ <input type=text id=clubt_l1>
+ </legend>
+ <legend><input type=checkbox id=club_l2></legend>
+ <p><label>Name on card: <input id=clubname required></label></p>
+ <p><label>Card number: <input id=clubnum required pattern="[-0-9]+"></label></p>
+ </fieldset>
+ <fieldset id=fs2 disabled>
+ <p><legend><input type=checkbox id=club2></legend></p>
+ <p><label>Name on card: <input id=clubname2 required></label></p>
+ <p><label>Card number: <input id=clubnum2 required pattern="[-0-9]+"></label></p>
+ </fieldset>
+ <fieldset id=fs3 disabled>
+ <fieldset>
+ <legend><input type=checkbox id=club3></legend>
+ </fieldset>
+ <p><label>Name on card: <input id=clubname3 required></label></p>
+ <p><label>Card number: <input id=clubnum3 required pattern="[-0-9]+"></label></p>
+ </fieldset>
+ <fieldset id=fs4 disabled>
+ <legend>
+ <fieldset id=fs4-1><input type=checkbox id=club4></fieldset>
+ </legend>
+ <p><label>Name on card: <input id=clubname4 required></label></p>
+ <p><label>Card number: <input id=clubnum4 required pattern="[-0-9]+"></label></p>
+ </fieldset>
+</form>
+<script>
+ test(function () {
+ assert_true(document.getElementById('fs').disabled, "The fieldset is disabled");
+ assert_false(document.getElementById('clubname').willValidate, "fieldset is disabled so is input 'clubname'");
+ assert_false(document.getElementById('clubnum').willValidate, "fieldset is disabled so is input 'clubnum'");
+ assert_true(document.getElementById('clubc_l1').willValidate, "input 'clubc_l1' is descendant of the first legend child of the fieldset. It should not be disabled");
+ assert_true(document.getElementById('clubr_l1').willValidate, "input 'clubr_l1' is descendant of the first legend child of the fieldset. It should not be disabled");
+ assert_true(document.getElementById('clubt_l1').willValidate, "input 'clubt_l1' is descendant of the first legend child of the fieldset. It should not be disabled");
+ assert_false(document.getElementById('club_l2').willValidate, "input 'club_l2' is a descendant of the second legend child of the fieldset. It should be disabled");
+ }, "The disabled attribute, when specified, causes all the form control descendants of the fieldset element, excluding those that are descendants of the fieldset element's first legend element child, if any, to be disabled.");
+
+ test(function () {
+ assert_true(document.getElementById('fs2').disabled, "The fieldset is disabled");
+ assert_false(document.getElementById('clubname2').willValidate, "fieldset is disabled so is input 'clubname2'");
+ assert_false(document.getElementById('clubnum2').willValidate, "fieldset is disabled so is input 'clubnum2'");
+ assert_false(document.getElementById('club2').willValidate, "the first legend is not a child of the disabled fieldset: input 'club2' is disabled");
+ }, "The first 'legend' element is not a child of the disabled fieldset: Its descendants should be disabled.");
+
+ test(function () {
+ assert_true(document.getElementById('fs3').disabled, "The fieldset is disabled");
+ assert_false(document.getElementById('clubname3').willValidate, "fieldset is disabled so is input 'clubname3'");
+ assert_false(document.getElementById('clubnum3').willValidate, "fieldset is disabled so is input 'clubnum3'");
+ assert_false(document.getElementById('club3').willValidate, "the first legend is not a child of the disabled fieldset: input 'club3' is disabled");
+ }, "The <legend> element is not a child of the disabled fieldset: Its descendants should be disabled.");
+
+ test(function () {
+ assert_true(document.getElementById('fs4').disabled, "The fieldset is disabled");
+ assert_false(document.getElementById('clubname4').willValidate, "fieldset is disabled so is input 'clubname4'");
+ assert_false(document.getElementById('clubnum4').willValidate, "fieldset is disabled so is input 'clubnum4'");
+ assert_true(document.getElementById('club4').willValidate, "the first legend a child of the disabled fieldset: input 'club4' is disabled");
+ }, "The <legend> element is child of the disabled fieldset: Its descendants should be disabled.");
+
+ test(function () {
+ let fs41 = document.querySelector('#fs4-1');
+ fs41.disabled = true;
+ assert_true(fs41.disabled, "The fieldset in a legend is disabled");
+ assert_false(document.getElementById('club4').willValidate, "In a disabled fieldset in the first legend child of another disabled fieldset: input 'club4' is disabled");
+ }, "A <fieldset> element is in the <legend> element of another disabled <fieldset>: Its descendants should be disabled.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-002.xhtml b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-002.xhtml
new file mode 100644
index 0000000000..896d737df4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-002.xhtml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>File input descendants of disabled fieldsets</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-fieldset-disabled" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <form>
+ <fieldset id="fs" disabled="disabled">
+ <input id="myfile" type="file" />
+ </fieldset>
+ </form>
+ <script>
+ test(function () {
+ assert_true(document.getElementById('fs').disabled, "disabled fieldset should be disabled");
+ assert_false(document.getElementById('myfile').willValidate, "form control descendant of disabled fieldset that is not also a descendant of a legend should be disabled");
+ }, "A file input without a disabled attribute that is a descendant of a disabled fieldset should be disabled (modulo legend-related complications that don't apply here)");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-003.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-003.html
new file mode 100644
index 0000000000..de01eb57fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/disabled-003.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Disable nested fieldsets with focused element</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-fieldset-element">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1427047">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=container1>
+ <fieldset id=target1>
+ <legend>foo</legend>
+ <fieldset>
+ <legend>bar</legend>
+ <input id=input1>
+ </fieldset>
+ </fieldset>
+</div>
+<script>
+test(() => {
+ input1.focus();
+ target1.disabled = true;
+ assert_not_equals(document.activeElement, input1);
+}, 'Disable light-nested fieldsets should not crash');
+</script>
+
+<div id=container2></div>
+<script>
+test(() => {
+ let n = 100;
+ let markup = '<fieldset><legend>foo</legend>'.repeat(n) +
+ '<input id=input2>' + '</fieldset>'.repeat(n);
+ container2.innerHTML = markup;
+ input2.focus();
+ container2.firstChild.disabled = true;
+ assert_not_equals(document.activeElement, input2);
+}, 'Disable deep-nested fieldsets should not hang');
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-checkvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-checkvalidity.html
new file mode 100644
index 0000000000..eeeca1853b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-checkvalidity.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>FieldSet_checkValidity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <fieldset id="input_field">
+ </fieldset>
+ </form>
+ <script>
+
+ var field = document.getElementById("input_field");
+
+ try
+ {
+ var ret = field.checkValidity();
+
+ test(function() {
+ assert_equals(ret, true, "calling of checkValidity method is failed.");
+ });
+ }
+ catch (e) {
+ test(function() {
+ assert_unreached("Error is raised.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-intrinsic-size.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-intrinsic-size.html
new file mode 100644
index 0000000000..5ff0d7db41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-intrinsic-size.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Fieldset with intrinsic size</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-fieldset-element">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#intrinsic">
+<meta name="assert" content="A fieldset with an intrinsic size should be as big as required by the contents.">
+<style>
+fieldset {
+ height: min-content;
+ padding: 7px;
+ border: 3px solid cyan;
+}
+fieldset > div {
+ border: 3px solid orange;
+}
+.auto {
+ height: auto;
+}
+.min-content {
+ height: min-content;
+}
+.max-content {
+ height: max-content;
+}
+.content-box {
+ box-sizing: content-box;
+}
+.border-box {
+ box-sizing: border-box;
+}
+</style>
+
+<div id="log"></div>
+
+<fieldset class="auto content-box">
+ <legend>Legend</legend>
+ <div>Contents</div>
+</fieldset>
+<fieldset class="auto border-box">
+ <legend>Legend</legend>
+ <div>Contents</div>
+</fieldset>
+<fieldset class="min-content content-box">
+ <legend>Legend</legend>
+ <div>Contents</div>
+</fieldset>
+<fieldset class="min-content border-box">
+ <legend>Legend</legend>
+ <div>Contents</div>
+</fieldset>
+<fieldset class="max-content content-box">
+ <legend>Legend</legend>
+ <div>Contents</div>
+</fieldset>
+<fieldset class="max-content border-box">
+ <legend>Legend</legend>
+ <div>Contents</div>
+</fieldset>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+for (let fieldset of document.querySelectorAll("fieldset")) {
+ test(function() {
+ const fieldsetRect = fieldset.getBoundingClientRect();
+ const contentsRect = fieldset.querySelector("div").getBoundingClientRect();
+ const fieldsetOuterEnd = fieldsetRect.y + fieldsetRect.height;
+ const fieldsetInnerEnd = fieldsetOuterEnd - 10;
+ const contentsOuterEnd = contentsRect.y + contentsRect.height;
+ assert_equals(fieldsetInnerEnd, contentsOuterEnd);
+ }, fieldset.className);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-setcustomvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-setcustomvalidity.html
new file mode 100644
index 0000000000..64aa374f19
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-setcustomvalidity.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<title>fieldset setCustomValidity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<fieldset id='fieldset_test'></fieldset>
+
+<script>
+
+test(() => {
+ let elem = document.getElementById("fieldset_test");
+ assert_false(elem.validity.customError);
+ elem.setCustomValidity("custom error");
+ assert_true(elem.validity.customError);
+}, "fieldset setCustomValidity is correct")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validationmessage.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validationmessage.html
new file mode 100644
index 0000000000..14dda76a13
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validationmessage.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>FieldSet_validationMessage</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <fieldset id="input_field">
+ </fieldset>
+ </form>
+ <script>
+
+ var field = document.getElementById("input_field");
+
+ if (typeof(field.validationMessage) == "string") {
+ test(function() {
+ assert_equals(field.validationMessage, "", "validationMessage attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("validationMessage attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validity.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validity.html
new file mode 100644
index 0000000000..7fd2d85656
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-validity.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>FieldSet_validity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <fieldset id="input_field">
+ </fieldset>
+ </form>
+ <script>
+
+ var field = document.getElementById("input_field");
+
+ if (typeof(field.validity) == "object") {
+ test(function() {
+ assert_equals(field.validity.valueMissing, false, "validity attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("validity attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-willvalidate.html b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-willvalidate.html
new file mode 100644
index 0000000000..357c9c16fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-fieldset-element/fieldset-willvalidate.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>FieldSet_willValidate</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <fieldset id="input_field">
+ </fieldset>
+ </form>
+ <script>
+
+ var field = document.getElementById("input_field");
+
+ if (typeof(field.willValidate) == "boolean") {
+ test(function() {
+ assert_equals(field.willValidate, false, "willValidate attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("willValidate attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html
new file mode 100644
index 0000000000..8a3543fcbc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html
@@ -0,0 +1,6 @@
+<iframe id="i"></iframe>
+<script>
+var form = i.contentDocument.createElement("form");
+i.remove();
+form.action = "GET";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html
new file mode 100644
index 0000000000..67828a3077
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form.action with a base URL</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<base href="/common/blank.html">
+
+<form id="form1" action="a.html"></form>
+<form id="form2" action=""></form>
+<form id="form3"></form>
+
+<script>
+"use strict";
+
+test(() => {
+
+ assert_equals(document.querySelector("#form1").action, (new URL("a.html", document.baseURI)).href,
+ "action should equal the correct absolute URL");
+
+}, "An action URL should be resolved relative to the document's base URL (not the document's URL)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form2").action, document.URL);
+
+}, "An empty-string action content attribute should cause the IDL attribute to return the document's URL (not the document's base URL)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form3").action, document.URL);
+
+}, "A missing action content attribute should cause the IDL attribute to return the document's URL (not the document's base URL)");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html
new file mode 100644
index 0000000000..c92fd0f0cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form.action</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form1" action="a.html"></form>
+<form id="form2" action=""></form>
+<form id="form3"></form>
+
+<script>
+"use strict";
+
+test(() => {
+
+ assert_equals(document.querySelector("#form1").action, (new URL("a.html", document.baseURI)).href,
+ "action should equal the correct absolute URL");
+
+}, "An action URL should be resolved relative to the document's base URL (= the document's URL in this case)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form2").action, document.URL);
+
+}, "An empty-string action content attribute should cause the IDL attribute to return the document's URL (= the document's base URL in this case)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form3").action, document.URL);
+
+}, "A missing action content attribute should cause the IDL attribute to return the document's URL (= the document's base URL in this case)");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html
new file mode 100644
index 0000000000..baee5500de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form action="" attribute effect on submission</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+// promise_test instead of async_test because all tests use window.success, and so can't run at the same time.
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/target/form-action-url-target.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action-and-base.sub.html?action=form-action-url-target.html";
+ document.body.appendChild(iframe);
+ });
+}, "An action URL should be resolved relative to the document's base URL (not document URL)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-with-action-and-base.sub.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action-and-base.sub.html?action=";
+ document.body.appendChild(iframe);
+ });
+}, "An empty-string action should submit the form to its containing document's URL (not its base URL)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-no-action-with-base.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-no-action-with-base.html";
+ document.body.appendChild(iframe);
+ });
+}, "A missing action should submit the form to its containing document's URL (not its base URL)");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html
new file mode 100644
index 0000000000..54ca7b5ff5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form action="" attribute effect on submission</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+// promise_test instead of async_test because all tests use window.success, and so can't run at the same time.
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/target/form-action-url-target.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action.sub.html?action=target/form-action-url-target.html";
+ document.body.appendChild(iframe);
+ });
+}, "An action URL should be resolved relative to the document's base URL (= document's URL in this case)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-with-action.sub.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action.sub.html?action=";
+ document.body.appendChild(iframe);
+ });
+}, "An empty-string action should submit the form to the document's URL (= document's base URL in this case)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-no-action.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-no-action.html";
+ document.body.appendChild(iframe);
+ });
+}, "A missing action should submit the form to the document's URL (= document's base URL in this case)");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html
new file mode 100644
index 0000000000..14717c5e6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>Form_action</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action="http://www.google.com/"
+ id="input_form">
+ <p><input type=hidden name="custname"></p>
+ <p><input type=hidden name="custtel"></p>
+ <p><input type=hidden name="custemail"></p>
+
+ </form>
+ <script>
+
+ var form = document.getElementById("input_form");
+
+ if (typeof(form.action) == "string") {
+ test(function() {
+ assert_equals(form.action, "http://www.google.com/", "action attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("action attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html
new file mode 100644
index 0000000000..fcd93ce2ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>form autocomplete attribute</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-form-element">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#attr-fe-autocomplete">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form name="missing_attribute">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<form name="autocomplete_on" autocomplete="on">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<form name="autocomplete_off" autocomplete="off">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<form name="autocomplete_invalid" autocomplete="foobar">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<script>
+ function autocompletetest(form, expectedValues, desc) {
+ test(function(){
+ assert_equals(form.autocomplete, expectedValues[0]);
+ assert_equals(form.elements[0].autocomplete, expectedValues[1]);
+ assert_equals(form.elements[1].autocomplete, expectedValues[2]);
+ assert_equals(form.elements[2].autocomplete, expectedValues[3]);
+ assert_equals(form.elements[3].autocomplete, expectedValues[4]);
+ }, desc);
+ }
+
+ autocompletetest(document.forms.missing_attribute, ["on", "", "on", "off", ""], "form autocomplete attribute missing");
+ autocompletetest(document.forms.autocomplete_on, ["on", "", "on", "off", ""], "form autocomplete attribute on");
+ autocompletetest(document.forms.autocomplete_off, ["off", "", "on", "off", ""], "form autocomplete attribute off");
+ autocompletetest(document.forms.autocomplete_invalid, ["on", "", "on", "off", ""], "form autocomplete attribute invalid");
+
+ var keywords = [ "on", "off", "name", "honorific-prefix", "given-name", "additional-name", "family-name", "honorific-suffix", "nickname", "username", "new-password", "current-password", "one-time-code", "organization-title", "organization", "street-address", "address-line1", "address-line2", "address-line3", "address-level4", "address-level3", "address-level2", "address-level1", "country", "country-name", "postal-code", "cc-name", "cc-given-name", "cc-additional-name", "cc-family-name", "cc-number", "cc-exp", "cc-exp-month", "cc-exp-year", "cc-csc", "cc-type", "transaction-currency", "transaction-amount", "language", "bday", "bday-day", "bday-month", "bday-year", "sex", "url", "photo", "tel", "tel-country-code", "tel-national", "tel-area-code", "tel-local", "tel-local-prefix", "tel-local-suffix", "tel-extension", "email", "impp", "webauthn" ];
+
+ keywords.forEach(function(keyword) {
+ test(function(){
+ var input = document.createElement("input");
+ // Include whitespace to test splitting tokens on whitespace.
+ // Convert to uppercase to ensure that the tokens are normalized to lowercase.
+ input.setAttribute("autocomplete", " " + keyword.toUpperCase() + "\t");
+ assert_equals(input.autocomplete, keyword);
+ }, keyword + " is an allowed autocomplete field name");
+ });
+
+
+test(() => {
+ const select = document.createElement("select");
+ select.setAttribute("autocomplete", " \n");
+ assert_equals(select.autocomplete, "");
+}, "Test whitespace-only attribute value");
+
+test(() => {
+ const select = document.createElement("select");
+
+ select.setAttribute("autocomplete", "foo off");
+ assert_equals(select.autocomplete, "");
+
+ // Normal category; max=3
+ select.setAttribute("autocomplete", "foo section-foo billing name");
+ assert_equals(select.autocomplete, "");
+
+ // Contact category; max=4
+ select.setAttribute("autocomplete", "foo section-bar billing work tel");
+ assert_equals(select.autocomplete, "");
+
+ // Credential category; max=5
+ select.setAttribute("autocomplete", "foo section-bar billing work tel webauthn");
+ assert_equals(select.autocomplete, "");
+}, "Test maximum number of tokens");
+
+test(() => {
+ const textarea = document.createElement("textarea");
+
+ textarea.setAttribute("autocomplete", "call-sign");
+ assert_equals(textarea.autocomplete, "");
+}, "Unknown field");
+
+test(() => {
+ const hidden = document.createElement("input");
+ hidden.type = "hidden";
+ hidden.setAttribute("autocomplete", "on");
+ assert_equals(hidden.autocomplete, "");
+ hidden.setAttribute("autocomplete", "off");
+ assert_equals(hidden.autocomplete, "");
+}, "Test 'wearing the autofill anchor mantle' with off/on");
+
+test(() => {
+ const textarea = document.createElement("textarea");
+
+ textarea.setAttribute("autocomplete", " HOME\ntel");
+ assert_equals(textarea.autocomplete, "home tel");
+
+ textarea.setAttribute("autocomplete", "shipping country");
+ assert_equals(textarea.autocomplete, "shipping country");
+
+ textarea.setAttribute("autocomplete", "billing work email");
+ assert_equals(textarea.autocomplete, "billing work email");
+
+ textarea.setAttribute("autocomplete", " section-FOO bday");
+ assert_equals(textarea.autocomplete, "section-foo bday");
+}, "Serialize combinations of section, mode, contact, and field");
+
+test(() => {
+ const textarea = document.createElement("textarea");
+
+ textarea.setAttribute("autocomplete", "\tusername webauthn");
+ assert_equals(textarea.autocomplete, "username webauthn");
+
+ textarea.setAttribute("autocomplete", " section-LOGIN shipping work tel webauthn ");
+ assert_equals(textarea.autocomplete, "section-login shipping work tel webauthn");
+}, "Serialize combinations of section, mode, contact, field, and credential");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html
new file mode 100644
index 0000000000..941ab94d45
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>Form_checkValidity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type=hidden name="custname"></p>
+ <p><input type=hidden name="custtel"></p>
+ <p><input type=hidden name="custemail"></p>
+
+ </form>
+ <script>
+
+ var form = document.getElementById("input_form");
+
+ try
+ {
+ var ret = form.checkValidity();
+
+ test(function() {
+ assert_equals(ret, true, "calling of checkValidity method is failed.");
+ });
+ }
+ catch (e) {
+ test(function() {
+ assert_unreached("Error is raised.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html
new file mode 100644
index 0000000000..693560188a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html
@@ -0,0 +1,192 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form.elements must contain all listed elements with the form owner</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<!--
+ Elements with data-in are expected to be in the form.elements collection.
+ The choice of other elements besides "listed elements" (i.e. img, label, meter, progress) was
+ because those are ones that appear in form-associated or labelable element categories.
+-->
+
+<button data-in form="form" id="before-button1"></button>
+<fieldset data-in form="form" id="before-fieldset1"></fieldset>
+<object data-in form="form" id="before-object1"></object>
+<output data-in form="form" id="before-output1"></output>
+<select data-in form="form" id="before-select1">
+ <option form="form" id="before-option1">x</option>
+</select>
+<textarea data-in form="form" id="before-textarea1"></textarea>
+
+<input data-in form="form" id="before-input1">
+<input data-in type="hidden" form="form" id="before-input2">
+<input data-in type="search" form="form" id="before-input3">
+<input data-in type="tel" form="form" id="before-input4">
+<input data-in type="url" form="form" id="before-input5">
+<input data-in type="email" form="form" id="before-input6">
+<input data-in type="password" form="form" id="before-input7">
+<input data-in type="date" form="form" id="before-input8">
+<input data-in type="month" form="form" id="before-input9">
+<input data-in type="week" form="form" id="before-input10">
+<input data-in type="time" form="form" id="before-input11">
+<input data-in type="datetime-local" form="form" id="before-input12">
+<input data-in type="number" form="form" id="before-input13">
+<input data-in type="range" form="form" id="before-input14">
+<input data-in type="color" form="form" id="before-input15">
+<input data-in type="checkbox" form="form" id="before-input16">
+<input data-in type="radio" form="form" id="before-input17">
+<input data-in type="file" form="form" id="before-input18">
+<input data-in type="submit" form="form" id="before-input19">
+<input data-in type="reset" form="form" id="before-input20">
+<input data-in type="button" form="form" id="before-input21">
+
+<img form="form" id="before-img1">
+<label form="form" id="before-label1"></label>
+<meter form="form" id="before-meter1"></meter>
+<progress form="form" id="before-progress1"></progress>
+
+<form id="form">
+ <button data-in id="button1"></button>
+ <fieldset data-in id="fieldset1"></fieldset>
+ <object data-in id="object1"></object>
+ <output data-in id="output1"></output>
+ <select data-in id="select1">
+ <option id="option1">x</option>
+ </select>
+ <textarea data-in id="textarea1"></textarea>
+
+ <input data-in id="input1">
+ <input data-in type="hidden" id="input2">
+ <input data-in type="search" id="input3">
+ <input data-in type="tel" id="input4">
+ <input data-in type="url" id="input5">
+ <input data-in type="email" id="input6">
+ <input data-in type="password" id="input7">
+ <input data-in type="date" id="input8">
+ <input data-in type="month" id="input9">
+ <input data-in type="week" id="input10">
+ <input data-in type="time" id="input11">
+ <input data-in type="datetime-local" id="input12">
+ <input data-in type="number" id="input13">
+ <input data-in type="range" id="input14">
+ <input data-in type="color" id="input15">
+ <input data-in type="checkbox" id="input16">
+ <input data-in type="radio" id="input17">
+ <input data-in type="file" id="input18">
+ <input data-in type="submit" id="input19">
+ <input data-in type="reset" id="input20">
+ <input data-in type="button" id="input21">
+
+ <img id="img1">
+ <label id="label1"></label>
+ <meter id="meter1"></meter>
+ <progress id="progress1"></progress>
+</form>
+
+<button data-in form="form" id="after-button1"></button>
+<fieldset data-in form="form" id="after-fieldset1"></fieldset>
+<object data-in form="form" id="after-object1"></object>
+<output data-in form="form" id="after-output1"></output>
+<select data-in form="form" id="after-select1">
+ <option form="form" id="after-option1">x</option>
+</select>
+<textarea data-in form="form" id="after-textarea1"></textarea>
+
+<input data-in form="form" id="after-input1">
+<input data-in type="hidden" form="form" id="after-input2">
+<input data-in type="search" form="form" id="after-input3">
+<input data-in type="tel" form="form" id="after-input4">
+<input data-in type="url" form="form" id="after-input5">
+<input data-in type="email" form="form" id="after-input6">
+<input data-in type="password" form="form" id="after-input7">
+<input data-in type="date" form="form" id="after-input8">
+<input data-in type="month" form="form" id="after-input9">
+<input data-in type="week" form="form" id="after-input10">
+<input data-in type="time" form="form" id="after-input11">
+<input data-in type="datetime-local" form="form" id="after-input12">
+<input data-in type="number" form="form" id="after-input13">
+<input data-in type="range" form="form" id="after-input14">
+<input data-in type="color" form="form" id="after-input15">
+<input data-in type="checkbox" form="form" id="after-input16">
+<input data-in type="radio" form="form" id="after-input17">
+<input data-in type="file" form="form" id="after-input18">
+<input data-in type="submit" form="form" id="after-input19">
+<input data-in type="reset" form="form" id="after-input20">
+<input data-in type="button" form="form" id="after-input21">
+
+<img form="form" id="after-img1">
+<label form="form" id="after-label1"></label>
+<meter form="form" id="after-meter1"></meter>
+<progress form="form" id="after-progress1"></progress>
+
+<button id="after-unassociated-button1"></button>
+<fieldset id="after-unassociated-fieldset1"></fieldset>
+<object id="after-unassociated-object1"></object>
+<output id="after-unassociated-output1"></output>
+<select id="after-unassociated-select1">
+ <option id="after-unassociated-option1">x</option>
+</select>
+<textarea id="after-unassociated-textarea1"></textarea>
+
+<input id="after-unassociated-input1">
+<input type="hidden" id="after-unassociated-input2">
+<input type="search" id="after-unassociated-input3">
+<input type="tel" id="after-unassociated-input4">
+<input type="url" id="after-unassociated-input5">
+<input type="email" id="after-unassociated-input6">
+<input type="password" id="after-unassociated-input7">
+<input type="date" id="after-unassociated-input8">
+<input type="month" id="after-unassociated-input9">
+<input type="week" id="after-unassociated-input10">
+<input type="time" id="after-unassociated-input11">
+<input type="datetime-local" id="after-unassociated-input12">
+<input type="number" id="after-unassociated-input13">
+<input type="range" id="after-unassociated-input14">
+<input type="color" id="after-unassociated-input15">
+<input type="checkbox" id="after-unassociated-input16">
+<input type="radio" id="after-unassociated-input17">
+<input type="file" id="after-unassociated-input18">
+<input type="submit" id="after-unassociated-input19">
+<input type="reset" id="after-unassociated-input20">
+<input type="button" id="after-unassociated-input21">
+
+<img id="after-unassociated-img1">
+<label id="after-unassociated-label1"></label>
+<meter id="after-unassociated-meter1"></meter>
+<progress id="after-unassociated-progress1"></progress>
+
+<form id="form2">
+ <span id="shadow-1"></span>
+</form>
+<span id="shadow-2"></span>
+
+<script>
+"use strict";
+test(() => {
+ const elements = document.querySelector("#form").elements;
+ const ids = Array.from(elements).map(el => el.id);
+
+ const allCorrectIDs = Array.from(document.querySelectorAll("[data-in]")).map(el => el.id);
+
+ assert_array_equals(ids, allCorrectIDs);
+});
+
+test(() => {
+ const shadowRoot1 = document.querySelector("#shadow-1").attachShadow({mode: "open"});
+ const input1 = document.createElement("input");
+ shadowRoot1.appendChild(input1);
+
+ const shadowRoot2 = document.querySelector("#shadow-2").attachShadow({mode: "open"});
+ const input2 = document.createElement("input");
+ input2.setAttribute("form", "form2");
+ shadowRoot2.appendChild(input2);
+
+ assert_equals(document.querySelector("#form2").elements.length, 0);
+ assert_equals(input1.form, null);
+ assert_equals(input2.form, null);
+}, "form.elements only includes elements from the same shadow tree");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html
new file mode 100644
index 0000000000..c8b4a6c71e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>form.elements: interfaces</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#htmlformcontrolscollection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var form = document.createElement("form");
+ ["HTMLFormControlsCollection", "HTMLCollection"].forEach(function(i) {
+ test(function() {
+ assert_true(i in window, "Interface should exist")
+ assert_true(form.elements instanceof window[i],
+ "elements should implement the interface")
+ }, "Testing interface " + i)
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html
new file mode 100644
index 0000000000..7921627265
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>form.elements: matches</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id="form">
+<input type="image">
+</form>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementById("form").elements.length, 0);
+}, "input type=image should not be present in the form.elements collection")
+test(function() {
+ var form = document.getElementById("form");
+ var i = document.createElement("input");
+ i.name = "2";
+ form.appendChild(i);
+ var j = document.createElement("input");
+ j.name = "03";
+ form.appendChild(j);
+ assert_equals(form.elements[-1], undefined, '[-1]');
+ assert_equals(form.elements["-1"], undefined, '["-1"]');
+ assert_equals(form.elements[0], i, '[0]');
+ assert_equals(form.elements["0"], i, '["0"]');
+ assert_equals(form.elements[1], j, '[1]');
+ assert_equals(form.elements["1"], j, '["1"]');
+ assert_equals(form.elements[2], undefined, '[2]');
+ assert_equals(form.elements["2"], undefined, '["2"]');
+ assert_equals(form.elements[03], undefined, '[03]');
+ assert_equals(form.elements["03"], j, '["03"]');
+ assert_equals(form.elements.item(-1), null, 'item(-1)');
+ assert_equals(form.elements.item(0), i, 'item(0)');
+ assert_equals(form.elements.item(1), j, 'item(1)');
+ assert_equals(form.elements.item(2), null, 'item(2)');
+ assert_equals(form.elements.namedItem("2"), i, 'namedItem("2")');
+ assert_equals(form.elements.namedItem("03"), j, 'namedItem("03")');
+ assert_equals(form.elements.namedItem("3"), null, 'namedItem("3")');
+ assert_array_equals(form.elements, [i, j]);
+ form.removeChild(i);
+ form.removeChild(j);
+}, "form.elements should include elements whose name starts with a number");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html
new file mode 100644
index 0000000000..0b5aeb8ef5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>form.elements: namedItem</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id=form>
+<input name=b id=i1>
+<input name=b id=i2>
+</form>
+</div>
+<script>
+test(function() {
+ assert_true("RadioNodeList" in window, "RadioNodeList should exist");
+}, "RadioNodeList should exist")
+test(function() {
+ var nl = document.forms.form.elements["b"];
+ assert_true(nl instanceof NodeList, "Should get a NodeList");
+ if ("RadioNodeList" in window) {
+ assert_true(nl instanceof RadioNodeList, "Should get a RadioNodeList");
+ }
+ assert_array_equals(nl,
+ [document.getElementById("i1"),
+ document.getElementById("i2")]);
+
+ var el = nl[0];
+ el.parentNode.removeChild(el);
+ assert_true(nl instanceof NodeList, "Should get a NodeList");
+ if ("RadioNodeList" in window) {
+ assert_true(nl instanceof RadioNodeList, "Should get a RadioNodeList");
+ }
+ assert_array_equals(nl, [document.getElementById("i2")]);
+ assert_equals(document.forms.form.elements["b"], document.getElementById("i2"));
+}, "elements collection should return elements or RadioNodeLists")
+test(function() {
+ var fs = document.forms.form.appendChild(document.createElement("fieldset"));
+ fs.name = "fs";
+ assert_equals(document.forms.form.elements.fs, fs);
+ fs.parentNode.removeChild(fs);
+}, "elements collection should include fieldsets")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html
new file mode 100644
index 0000000000..c25e554de1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>form.elements: parsing</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#parsing-main-intr">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id=form>
+<table>
+<tr>
+<td><input type="radio" name="radio1" id="r1" value=1></td>
+<td><input type="radio" name="radio2" id="r2" value=2></td>
+<input type="radio" name="radio0" id="r0" value=0>
+</tr>
+</table>
+</form>
+</div>
+<script>
+test(function() {
+ var form = document.getElementById("form");
+ assert_array_equals(form.elements,
+ [document.getElementById("r0"),
+ document.getElementById("r1"),
+ document.getElementById("r2")]);
+}, "form.elements should work correctly in the face of table syntax errors")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html
new file mode 100644
index 0000000000..1c1aa5d3dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Testing [SameObject] on the 'elements' attribute on the 'form' element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<form>
+ <input>
+</form>
+
+<script>
+test(function() {
+ var form = document.querySelector('form');
+ var elements = form.elements;
+ assert_equals(elements, form.elements);
+ form.appendChild(document.createElement('input'));
+ assert_equals(elements, form.elements);
+}, "[SameObject] should apply to 'elements' attr on <form>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html
new file mode 100644
index 0000000000..a108ce8a93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>form.elements: indexed access reflects DOM order, not flat tree</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form id="target">
+ <div id="host">
+ <template shadowrootmode="open">
+ <slot name="first"></slot>
+ <slot name="second"></slot>
+ </template>
+ <input id="first" slot="second">
+ <input id="second" slot="first">
+ </div>
+</form>
+<script>
+test(function() {
+ let target = document.getElementById("target");
+ let host = document.getElementById("host");
+ assert_true(!!host.shadowRoot, "Should have a shadow tree");
+ assert_equals(target.elements[0], first, "form.elements reflects DOM order, not flat tree order");
+ assert_equals(target.elements[1], second);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html
new file mode 100644
index 0000000000..5ea96d3d1b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>form.elements: indexed</title>
+<link rel="author" title="Ivan.Yang" href="mailto:jsyangwenjie@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id=form>
+<input type="radio" name="radio1" id="r1" value=1>
+<input type="radio" name="radio2" id="r2" value=2>
+</form>
+</div>
+<script>
+test(function() {
+ var form = document.getElementById("form");
+ assert_equals(form[0], document.getElementById("r1"));
+ assert_equals(form[1], document.getElementById("r2"));
+ assert_equals(form[2], undefined);
+ assert_equals(form[-1], undefined);
+}, "form.elements should be accessed correctly by index")
+
+test(function(){
+ var form = document.getElementById("form");
+ var old_item = form[0];
+ var old_desc = Object.getOwnPropertyDescriptor(form, 0);
+ assert_equals(old_desc.value, old_item);
+ assert_true(old_desc.enumerable);
+ assert_true(old_desc.configurable);
+ assert_false(old_desc.writable);
+
+ Object.prototype[0] = 5;
+ this.add_cleanup(function () { delete Object.prototype[0]; });
+ assert_equals(form[0], old_item);
+
+ delete form[0];
+ assert_equals(form[0], old_item);
+
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ delete form[0];
+ });
+ assert_equals(form[0], old_item);
+}, 'Trying to delete an indexed property name should never work');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html
new file mode 100644
index 0000000000..3326809fc6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>Form_length</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type=hidden name="custname"></p>
+ <p><input type=hidden name="custtel"></p>
+ <p><input type=hidden name="custemail"></p>
+
+ </form>
+ <script>
+
+ var form = document.getElementById("input_form");
+ var len = form.length;
+
+ test(function() {
+ assert_equals(len, 3, "length attribute is not correct.");
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html
new file mode 100644
index 0000000000..7b7d573615
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html
@@ -0,0 +1,418 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Form named getter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<!-- XXX Nothing tests id attributes yet. -->
+<!-- XXX We also need tests for moving inputs and forms in the DOM. -->
+<form>
+<input type=button name=button>
+<input type=radio name=radio value=x>
+<input type=radio name=radio value=y>
+<input type=radio name=radio value=z>
+</form>
+
+<form>
+<button name=l1></button>
+<fieldset name=l2></fieldset>
+<input type=hidden name=l3>
+<input type=text name=l4>
+<input type=search name=l5>
+<input type=tel name=l6>
+<input type=url name=l7>
+<input type=email name=l8>
+<input type=password name=l9>
+<input type=datetime name=l10>
+<input type=date name=l11>
+<input type=month name=l12>
+<input type=week name=l13>
+<input type=time name=l14>
+<input type=datetime-local name=l15>
+<input type=number name=l16>
+<input type=range name=l17>
+<input type=color name=l18>
+<input type=checkbox name=l19>
+<input type=radio name=l20>
+<input type=file name=l21>
+<input type=submit name=l22>
+<input type=image name=l23>
+<input type=reset name=l24>
+<input type=button name=l25>
+<input type=foo name=l26>
+<input name=l27>
+<object name=l28></object>
+<output name=l29></output>
+<select name=l30></select>
+<textarea name=l31></textarea>
+</form>
+
+<form>
+<!-- EventTarget -->
+<input type=radio name=addEventListener>
+<input type=radio name=removeEventListener>
+<input type=radio name=dispatchEvent>
+
+<!-- Node -->
+<input type=radio name=nodeType>
+<input type=radio name=nodeName>
+<input type=radio name=ownerDocument>
+
+<!-- Element -->
+<input type=radio name=namespaceURI>
+<input type=radio name=prefix>
+<input type=radio name=localName>
+
+<!-- HTMLElement -->
+<input type=radio name=title>
+<input type=radio name=lang>
+<input type=radio name=dir>
+
+<!-- HTMLFormElement -->
+<input type=radio name=acceptCharset>
+<input type=radio name=action>
+<input type=radio name=autocomplete>
+<input type=radio name=enctype>
+<input type=radio name=encoding>
+<input type=radio name=method>
+<input type=radio name=name>
+<input type=radio name=noValidate>
+<input type=radio name=target>
+<input type=radio name=elements>
+<input type=radio name=length>
+<input type=radio name=submit>
+<input type=radio name=reset>
+<input type=radio name=checkValidity>
+</form>
+
+<img name=x>
+<form></form><!-- no child nodes -->
+<img name=y>
+<form><!-- a child node --></form>
+<img name=z>
+
+<input form=a name=b>
+<form id=a></form>
+<input form=c name=d>
+<input form=c name=d>
+<form id=c></form>
+<script>
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.item, undefined)
+ assert_false("item" in form)
+}, "Forms should not have an item method")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.namedItem, undefined)
+ assert_false("namedItem" in form)
+}, "Forms should not have a namedItem method")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ var button = document.getElementsByTagName("input")[0]
+ assert_equals(button.type, "button")
+ assert_equals(form.button, button)
+ var desc = Object.getOwnPropertyDescriptor(form, "button");
+ assert_equals(desc.value, button);
+ assert_false(desc.writable);
+ assert_true(desc.configurable);
+ assert_false(desc.enumerable);
+ assert_equals(form.button.length, undefined)
+}, "Name for a single element should work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.radio.item(-1), null)
+ assert_array_equals([0, 1, 2].map(function(i) {
+ return form.radio.item(i).value
+ }), ["x", "y", "z"])
+ assert_equals(form.radio.item(3), null)
+}, "Calling item() on the NodeList returned from the named getter should work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.radio.length, 3)
+ assert_equals(form.radio[-1], undefined)
+ assert_array_equals([0, 1, 2].map(function(i) {
+ return form.radio[i].value
+ }), ["x", "y", "z"])
+ assert_equals(form.radio[3], undefined)
+}, "Indexed getter on the NodeList returned from the named getter should work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ var indices = [-1, 0, 1, 2, 3]
+ indices.forEach(function(i) {
+ assert_throws_js(TypeError, function() {
+ form.radio(i)
+ })
+ })
+}, "Invoking a legacycaller on the NodeList returned from the named getter " +
+ "should not work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1]
+ for (var i = 1; i <= 31; ++i) {
+ if (i == 23) {
+ // input type=image
+ assert_equals(form["l" + i], undefined)
+ } else {
+ assert_equals(form["l" + i], form.children[i - 1])
+ }
+ }
+}, "All listed elements except input type=image should be present in the form")
+
+test(function() {
+ var names = [
+ // EventTarget
+ "addEventListener", "removeEventListener", "dispatchEvent",
+ // Node
+ "nodeType", "nodeName", "ownerDocument",
+ // Element
+ "namespaceURI", "prefix", "localName",
+ // HTMLElement
+ "title", "lang", "dir",
+ // HTMLFormElement
+ "acceptCharset", "action", "autocomplete", "enctype", "encoding", "method",
+ "name", "noValidate", "target", "elements", "length", "submit", "reset",
+ "checkValidity"
+ ]
+ var form = document.getElementsByTagName("form")[2]
+ names.forEach(function(name, i) {
+ assert_equals(form[name], form.children[i])
+ })
+}, "Named elements should override builtins")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[3]
+ assert_equals(form.x, undefined, "x should not be associated with the form")
+ assert_equals(form.y, undefined, "y should not be associated with the form")
+ assert_equals(form.z, undefined, "z should not be associated with the form")
+ assert_equals(form[0], undefined, "The form should not have supported property indices")
+ assert_equals(form.length, 0)
+}, "Named items outside the form should not be returned (no children)")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[4]
+ assert_equals(form.x, undefined, "x should not be associated with the form")
+ assert_equals(form.y, undefined, "y should not be associated with the form")
+ assert_equals(form.z, undefined, "z should not be associated with the form")
+ assert_equals(form[0], undefined, "The form should not have supported property indices")
+ assert_equals(form.length, 0)
+}, "Named items outside the form should not be returned (one child)")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[5]
+ assert_equals(form.id, "a")
+
+ var input = document.getElementsByName("b")[0]
+ assert_equals(input.localName, "input")
+ assert_equals(input.getAttribute("form"), "a")
+
+ assert_equals(form.b, input);
+}, "The form attribute should be taken into account for named getters (single element)")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[6]
+ assert_equals(form.id, "c")
+
+ var input1 = document.getElementsByName("d")[0]
+ assert_equals(input1.localName, "input")
+ assert_equals(input1.getAttribute("form"), "c")
+
+ var input2 = document.getElementsByName("d")[1]
+ assert_equals(input2.localName, "input")
+ assert_equals(input2.getAttribute("form"), "c")
+
+ var desc = Object.getOwnPropertyDescriptor(form, "d");
+ assert_equals(desc.value, form.d);
+ assert_false(desc.writable);
+ assert_true(desc.configurable);
+ assert_false(desc.enumerable);
+
+ assert_true(form.d instanceof NodeList, "form.d should be a NodeList")
+ assert_array_equals(form.d, [input1, input2])
+}, "The form attribute should be taken into account for named getters (multiple elements)")
+
+test(function() {
+ var f = document.body.appendChild(document.createElement("form"))
+ f.id = "f"
+ var g = f.appendChild(document.createElement("form"))
+ g.id = "g"
+ var input = g.appendChild(document.createElement("input"))
+ input.name = "x"
+ assert_equals(f.x, undefined)
+ assert_equals(g.x, input)
+}, "Input should only be a named property on the innermost form that contains it")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+ var old_item = form["l1"];
+ var old_desc = Object.getOwnPropertyDescriptor(form, "l1");
+ assert_equals(old_desc.value, old_item);
+ assert_false(old_desc.enumerable);
+ assert_true(old_desc.configurable);
+ assert_false(old_desc.writable);
+
+ form["l1"] = 5;
+ assert_equals(form["l1"], old_item);
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ form["l1"] = 5;
+ });
+ assert_throws_js(TypeError, function() {
+ Object.defineProperty(form, "l1", { value: 5 });
+ });
+
+ delete form["l1"];
+ assert_equals(form["l1"], old_item);
+
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ delete form["l1"];
+ });
+ assert_equals(form["l1"], old_item);
+
+}, 'Trying to set an expando that would shadow an already-existing named property');
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+ var old_item = form["new-name"];
+ var old_desc = Object.getOwnPropertyDescriptor(form, "new-name");
+ assert_equals(old_item, undefined);
+ assert_equals(old_desc, undefined);
+
+ form["new-name"] = 5;
+ assert_equals(form["new-name"], 5);
+
+ var input = document.createElement("input");
+ this.add_cleanup(function () {input.remove();});
+ input.name = "new-name";
+ form.appendChild(input);
+
+ assert_equals(form["new-name"], 5);
+
+ delete form["new-name"];
+ assert_equals(form["new-name"], input);
+}, 'Trying to set an expando that shadows a named property that gets added later');
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+ var old_item = form["new-name2"];
+ var old_desc = Object.getOwnPropertyDescriptor(form, "new-name2");
+ assert_equals(old_item, undefined);
+ assert_equals(old_desc, undefined);
+
+ Object.defineProperty(form, "new-name2", { configurable: false, writable:
+ false, value: 5 });
+ assert_equals(form["new-name2"], 5);
+
+ var input = document.createElement("input");
+ this.add_cleanup(function () {input.remove();});
+ input.name = "new-name2";
+ form.appendChild(input);
+
+ assert_equals(form["new-name2"], 5);
+
+ delete form["new-name2"];
+ assert_equals(form["new-name2"], 5);
+
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ delete form["new-name2"];
+ });
+ assert_equals(form["new-name2"], 5);
+}, 'Trying to set a non-configurable expando that shadows a named property that gets added later');
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+
+ var i1 = document.createElement("input");
+ i1.name = "past-name1";
+ i1.id = "past-id1"
+
+ assert_equals(form["past-name1"], undefined);
+ assert_equals(form["past-id1"], undefined);
+ form.appendChild(i1);
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["past-id1"], i1);
+
+ i1.name = "twiddled-name1";
+ i1.id = "twiddled-id1";
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["twiddled-name1"], i1);
+ assert_equals(form["past-id1"], i1);
+ assert_equals(form["twiddled-id1"], i1);
+
+ i1.name = "twiddled-name2";
+ i1.id = "twiddled-id2";
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["twiddled-name1"], i1);
+ assert_equals(form["twiddled-name2"], i1);
+ assert_equals(form["past-id1"], i1);
+ assert_equals(form["twiddled-id1"], i1);
+ assert_equals(form["twiddled-id2"], i1);
+
+ i1.removeAttribute("id");
+ i1.removeAttribute("name");
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["twiddled-name1"], i1);
+ assert_equals(form["twiddled-name2"], i1);
+ assert_equals(form["past-id1"], i1);
+ assert_equals(form["twiddled-id1"], i1);
+ assert_equals(form["twiddled-id2"], i1);
+
+ i1.remove();
+ assert_equals(form["past-name1"], undefined);
+ assert_equals(form["twiddled-name1"], undefined);
+ assert_equals(form["twiddled-name2"], undefined);
+ assert_equals(form["past-id1"], undefined);
+ assert_equals(form["twiddled-id1"], undefined);
+ assert_equals(form["twiddled-id2"], undefined);
+
+ var i2 = document.createElement("input");
+ i2.name = "past-name2";
+ i2.id = "past-id2";
+
+ assert_equals(form["past-name2"], undefined);
+ assert_equals(form["past-id2"], undefined);
+ form.appendChild(i2);
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["past-id2"], i2);
+
+ i2.name = "twiddled-name3";
+ i2.id = "twiddled-id3";
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["twiddled-name3"], i2);
+ assert_equals(form["past-id2"], i2);
+ assert_equals(form["twiddled-id3"], i2);
+
+ i2.name = "twiddled-name4";
+ i2.id = "twiddled-id4";
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["twiddled-name3"], i2);
+ assert_equals(form["twiddled-name4"], i2);
+ assert_equals(form["past-id2"], i2);
+ assert_equals(form["twiddled-id3"], i2);
+ assert_equals(form["twiddled-id4"], i2);
+
+ i2.removeAttribute("id");
+ i2.removeAttribute("name");
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["twiddled-name3"], i2);
+ assert_equals(form["twiddled-name4"], i2);
+ assert_equals(form["past-id2"], i2);
+ assert_equals(form["twiddled-id3"], i2);
+ assert_equals(form["twiddled-id4"], i2);
+
+ i2.setAttribute("form", "c");
+ assert_equals(form["past-name2"], undefined);
+ assert_equals(form["twiddled-name3"], undefined);
+ assert_equals(form["twiddled-name4"], undefined);
+ assert_equals(form["past-id2"], undefined);
+ assert_equals(form["twiddled-id3"], undefined);
+ assert_equals(form["twiddled-id4"], undefined);
+}, "Past names map should work correctly");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html
new file mode 100644
index 0000000000..cbc46cc7d3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html
@@ -0,0 +1,215 @@
+<!DOCTYPE html>
+<title>form.requestSubmit() tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe name="iframe" src="about:blank"></iframe>
+
+<script>
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', '<form>' +
+ '<input type="reset">' +
+ '<input type="text">' +
+ '<button type="reset"></button>' +
+ '<button type="button"></button>' +
+ '</form>');
+ let form = document.querySelector('form');
+ assert_throws_js(TypeError, () => {
+ form.requestSubmit(document.body);
+ });
+ for (let control of form.elements) {
+ assert_throws_js(TypeError, () => { form.requestSubmit(control); });
+ }
+}, 'Passing an element which is not a submit button should throw');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', `<form>
+ <input form="" type="submit">
+ <button form="form2" type="submit"></button>
+ </form>
+ <form id="form2"></form>`);
+ let form = document.querySelector('form');
+ let submitButton = document.createElement('button');
+ submitButton.type = 'submit';
+ assert_throws_dom('NotFoundError', () => {
+ form.requestSubmit(submitButton);
+ });
+
+ let buttons = form.querySelectorAll('input, button');
+ assert_equals(buttons.length, 2);
+ for (let control of buttons) {
+ assert_throws_dom('NotFoundError', () => { form.requestSubmit(control) },
+ control.outerHTML);
+ }
+}, 'Passing a submit button not owned by the context object should throw');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', `<input type=submit form="form1">
+ <form id="form1" target="_blank">
+ <button type="submit"></button>
+ <button></button>
+ <button type="invalid"></button>
+ <input type="submit">
+ <input type="image">
+ </form>`);
+ let form = document.querySelector('form');
+ let didDispatchSubmit = false;
+ form.addEventListener('submit', event => { event.preventDefault(); didDispatchSubmit = true; });
+
+ assert_equals(form.elements.length, 5);
+ for (let control of form.elements) {
+ didDispatchSubmit = false;
+ form.requestSubmit(control);
+ assert_true(didDispatchSubmit, `${control.outerHTML} should submit the form`);
+ }
+ // <input type=image> is not in form.elements.
+ let control = form.querySelector('[type=image]');
+ didDispatchSubmit = false;
+ form.requestSubmit(control);
+ assert_true(didDispatchSubmit, `${control.outerHTML} should submit the form`);
+}, 'requestSubmit() should accept button[type=submit], input[type=submit], and input[type=image]');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', '<form><input required></form>');
+ let form = document.querySelector('form');
+ let invalidControl = form.querySelector('input:invalid');
+ let didDispatchInvalid = false;
+ invalidControl.addEventListener('invalid', e => { didDispatchInvalid = true; });
+
+ form.requestSubmit();
+ assert_true(didDispatchInvalid);
+}, 'requestSubmit() should trigger interactive form validation');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin',
+ '<form><input type=submit></form>');
+ let form = document.querySelector('form');
+ let submitButton = form.elements[0];
+ let submitCounter = 0;
+ form.addEventListener('submit', e => {
+ ++submitCounter;
+ form.requestSubmit();
+ e.preventDefault();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(submitCounter, 1, 'requestSubmit() + requestSubmit()');
+
+ submitCounter = 0;
+ form.addEventListener('submit', e => {
+ ++submitCounter;
+ submitButton.click();
+ e.preventDefault();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(submitCounter, 1, 'requestSubmit() + click()');
+
+ submitCounter = 0;
+ form.addEventListener('submit', e => {
+ ++submitCounter;
+ form.requestSubmit();
+ e.preventDefault();
+ }, {once: true});
+ submitButton.click();
+ assert_equals(submitCounter, 1, 'click() + requestSubmit()');
+}, 'requestSubmit() doesn\'t run form submission reentrantly');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin',
+ '<form><input type=submit><input required></form>');
+ let form = document.querySelector('form');
+ let submitButton = form.elements[0];
+ let invalidControl = form.elements[1];
+ let invalidCounter = 0;
+ invalidControl.addEventListener('invalid', e => {
+ ++invalidCounter;
+ if (invalidCounter < 10)
+ form.requestSubmit();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(invalidCounter, 1, 'requestSubmit() + requestSubmit()');
+
+ invalidCounter = 0;
+ invalidControl.addEventListener('invalid', e => {
+ ++invalidCounter;
+ if (invalidCounter < 10)
+ submitButton.click();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(invalidCounter, 1, 'requestSubmit() + click()');
+
+ invalidCounter = 0;
+ invalidControl.addEventListener('invalid', e => {
+ ++invalidCounter;
+ if (invalidCounter < 10)
+ form.requestSubmit();
+ }, {once: true});
+ submitButton.click();
+ assert_equals(invalidCounter, 1, 'click() + requestSubmit()');
+}, 'requestSubmit() doesn\'t run interactive validation reentrantly');
+
+test(() => {
+ let form = document.createElement('form');
+ let submitCounter = 0;
+ form.addEventListener('submit', e => { ++submitCounter; e.preventDefault(); });
+ form.requestSubmit();
+ assert_equals(submitCounter, 0);
+}, 'requestSubmit() for a disconnected form should not submit the form');
+
+async_test(t => {
+ window.addEventListener('load', t.step_func(() => {
+ document.body.insertAdjacentHTML('afterbegin', `
+<form action="/common/blank.html">
+<input required>
+<input type=submit formnovalidate formtarget=iframe name=s value=v>
+</form>`);
+ let form = document.body.querySelector('form');
+ let iframe = document.body.querySelector('iframe');
+ assert_true(form.matches(':invalid'), 'The form is invalid.');
+ // The form should be submitted though it is invalid.
+ iframe.addEventListener('load', t.step_func_done(() => {
+ assert_not_equals(iframe.contentWindow.location.search.indexOf('s=v'), -1);
+ }));
+ form.requestSubmit(form.querySelector('[type=submit]'));
+ }));
+}, 'The value of the submitter should be appended, and form* ' +
+ 'attributes of the submitter should be handled.');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', `<form>
+ <input name="n1" value="v1">
+ <button type="submit" name="n2" value="v2"></button>
+ </form>
+ <form id="form2"></form>`);
+ let form = document.querySelector('form');
+ let formDataInEvent = null;
+ let submitter = form.querySelector('button[type=submit]');
+ form.addEventListener('submit', e => {
+ e.preventDefault();
+ formDataInEvent = new FormData(e.target);
+ });
+
+ form.requestSubmit(submitter);
+ assert_equals(formDataInEvent.get('n1'), 'v1');
+ assert_false(formDataInEvent.has('n2'));
+}, 'The constructed FormData object should not contain an entry for the submit button that was used to submit the form.');
+
+async_test(t => {
+ document.body.insertAdjacentHTML('afterbegin', `<form>
+ <button type="submit" name="n1" value="v1" disabled=""></button>
+ </form>`);
+ let form = document.querySelector('form');
+ let formDataInEvent = null;
+ let submitter = form.querySelector('button[type=submit]');
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ formDataInEvent = new FormData(ev.target);
+ assert_false(formDataInEvent.has('n1'));
+ assert_equals(ev.target, form);
+ }));
+
+ form.requestSubmit(submitter);
+
+}, "Using requestSubmit on a disabled button (via disabled attribute) should trigger submit but not be visible in FormData");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html
new file mode 100644
index 0000000000..b3599a45e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<base href="target/"></base>
+
+<form>
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html
new file mode 100644
index 0000000000..c0c2ad0330
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html
@@ -0,0 +1,18 @@
+<!doctype html>
+
+<form>
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html
new file mode 100644
index 0000000000..edb101bece
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<base href="target/"></base>
+
+<form action="{{GET[action]}}">
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html
new file mode 100644
index 0000000000..97e800a561
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html
@@ -0,0 +1,18 @@
+<!doctype html>
+
+<form action="{{GET[action]}}">
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html
new file mode 100644
index 0000000000..d509f21924
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+"use strict";
+window.parent.success(window.location.href);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-active-contenteditable.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-active-contenteditable.html
new file mode 100644
index 0000000000..88a9a35cc5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-active-contenteditable.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="http://crbug.com/1007941">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!-- This behavior is not explicitly specified. -->
+
+<a id=anchorid href="nonexistant">anchor</a>
+
+<script>
+anchorid.addEventListener('mousedown', () => {
+ anchorid.contentEditable = true;
+});
+
+promise_test(async () => {
+ await test_driver.click(anchorid);
+ assert_equals(document.querySelector(':active'), null);
+}, 'Anchor elements should not stay :active when contentEditable is enabled.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-contenteditable-navigate.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-contenteditable-navigate.html
new file mode 100644
index 0000000000..e958f10df8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/anchor-contenteditable-navigate.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!-- This behavior is not explicitly specified. -->
+
+<a id=anchorid href="javascript:window.anchornavigated = true;">anchor</a>
+
+<script>
+promise_test(async () => {
+ window.anchornavigated = false;
+
+ anchorid.contentEditable = true;
+ await test_driver.click(anchorid);
+
+ assert_false(window.anchornavigated, "Anchor's javascript: url was run.");
+
+}, 'Anchor elements should not be able to navigate if they have contentEditable.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-dynamic.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-dynamic.html
new file mode 100644
index 0000000000..68ea51185c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-dynamic.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<link rel="match" href="auto-direction-ref.html">
+<body>
+<script>
+const dirInput = document.createElement('input')
+dirInput.setAttribute('dir', 'auto')
+dirInput.setAttribute('value', 'شنينسنمس')
+document.body.appendChild(dirInput)
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-ref.html
new file mode 100644
index 0000000000..675ba50914
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/auto-direction-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<input dir="auto" value="شنينسنمس">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/button.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/button.html
new file mode 100644
index 0000000000..3c826a9754
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/button.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>input type button</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#button-state-(type=button)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form id=f1>
+ <input type=button id=b1 name=b1>
+</form>
+<form>
+ <input id=i1 value="foo">
+ <input type=button id=b2 name=b2>
+</form>
+<form>
+ <input type=radio id=i2 checked name=b3>
+ <input type=button id=b3 name=b3>
+</form>
+<form>
+ <input type=checkbox id=i3>
+ <input type=button id=b4 name=b4>
+</form>
+
+<script>
+ var t = async_test("clicking on button should not submit a form"),
+ b1 = document.getElementById('b1'),
+ b2 = document.getElementById('b2'),
+ b3 = document.getElementById('b3'),
+ b4 = document.getElementById('b4'),
+ i1 = document.getElementById('i1'),
+ i2 = document.getElementById('i2'),
+ i3 = document.getElementById('i3');
+
+ test(function(){
+ assert_false(b1.willValidate);
+ }, "the element is barred from constraint validation");
+
+ document.getElementById('f1').onsubmit = t.step_func(function(e) {
+ e.preventDefault();
+ assert_unreached("form has been submitted");
+ });
+
+ t.step(function() {
+ b1.click();
+ });
+ t.done();
+
+ test(function(){
+ i1.value = "bar";
+ b2.click();
+ assert_equals(i1.value, "bar");
+ }, "clicking on button should not reset other form fields");
+
+ test(function(){
+ assert_true(i2.checked);
+ b3.click();
+ assert_true(i2.checked);
+ }, "clicking on button should not unchecked radio buttons");
+
+ test(function(){
+ assert_false(i3.indeterminate);
+ b4.click();
+ assert_false(i3.indeterminate);
+ }, "clicking on button should not change its indeterminate IDL attribute");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/change-to-empty-value.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/change-to-empty-value.html
new file mode 100644
index 0000000000..fd1eeb4584
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/change-to-empty-value.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Change event when clearing an input</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1881457">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<input type="text" value="abc">
+<script>
+promise_test(async function() {
+ let input = document.querySelector("input");
+ let changeFired = false;
+ input.addEventListener("change", () => {
+ changeFired = true;
+ }, { once: true });
+ input.focus();
+ assert_equals(document.activeElement, input, "Should focus input");
+ assert_false(changeFired, "Shouldn't have fired change event after focus");
+ input.select();
+ const kBackspaceKey = "\uE003";
+ await test_driver.send_keys(input, kBackspaceKey)
+ assert_false(changeFired, "Shouldn't have fired change event after select");
+ input.blur();
+ assert_true(changeFired, "Should've have fired change event after blur");
+ assert_equals(input.value, "", "Should've have cleared the value");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur-with-click.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur-with-click.html
new file mode 100644
index 0000000000..2d5d008622
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur-with-click.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+* {
+ font-size: 20px;
+}
+</style>
+</head>
+<body>
+
+<!-- This behavior is not explicitly specified. -->
+
+<input type=checkbox id=cb1 checked> <label for=cb1>ghi</label>
+<input type=radio id=r1 checked> <label for=r1>jkl</label>
+<label id=lc>abc <input type=checkbox id=cb2 checked></label>
+<label id=lr>def <input type=radio id=r2 checked></label>
+
+<script>
+promise_test(async () => {
+ await new Promise(resolve => {
+ addEventListener("load", resolve, { once: true });
+ });
+}, "Wait for load");
+
+const tabKey = "\uE004";
+promise_test(async t => {
+ const checkbox = document.querySelector("input[type=checkbox]");
+ // pointerdown on the checkbox
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, { origin: checkbox })
+ .pointerDown())
+ .send();
+ t.add_cleanup(async () => {
+ // Release the pointer
+ await (new test_driver.Actions().pointerUp()).send();
+ });
+ assert_equals(document.querySelector("input:active"), checkbox,
+ "Checkboxes should be :active while it is pressed");
+
+ // Press tab
+ await (new test_driver.Actions().keyDown(tabKey).keyUp(tabKey)).send();
+ assert_equals(document.querySelector(":active"), null,
+ "Checkboxes should not be :active after tab is used to change focus.");
+}, "Checkboxes should clear :active when the user tabs away from them while pressing it with a pointing device");
+
+promise_test(async t => {
+ const radio = document.querySelector("input[type=radio]");
+ // pointerdown on the radio
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, { origin: radio })
+ .pointerDown())
+ .send();
+ t.add_cleanup(async () => {
+ // Release the pointer
+ await (new test_driver.Actions().pointerUp()).send();
+ });
+ assert_equals(document.querySelector("input:active"), radio,
+ "Radios should be :active while it is pressed");
+
+ // Press tab
+ await (new test_driver.Actions().keyDown(tabKey).keyUp(tabKey)).send();
+ assert_equals(document.querySelector(":active"), null,
+ "Radios should not be :active after tab is used to change focus.");
+}, "Radios should clear :active when the user tabs away from them while pressing it with a pointing device");
+
+promise_test(async t => {
+ const checkbox = document.querySelector("label > input[type=checkbox]");
+ const label = checkbox.parentElement;
+ // pointerdown on the label
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, { origin: label })
+ .pointerDown())
+ .send();
+ t.add_cleanup(async () => {
+ // Release the pointer
+ await (new test_driver.Actions().pointerUp()).send();
+ });
+ assert_equals(document.querySelector("input:active"), checkbox,
+ "Checkboxes should be :active while the label is pressed");
+
+ // Press tab
+ await (new test_driver.Actions().keyDown(tabKey).keyUp(tabKey)).send();
+ assert_equals(document.querySelector(":active"), null,
+ "Checkboxes should not be :active after tab is used to change focus.");
+}, "Checkboxes should clear :active when the user tabs away from them while pressing the parent label with a pointing device");
+
+promise_test(async t => {
+ const radio = document.querySelector("label > input[type=radio]");
+ const label = radio.parentElement;
+ const radioRect = radio.getBoundingClientRect();
+ const labelRect = label.getBoundingClientRect();
+ // pointerdown on the label
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, { origin: label })
+ .pointerDown())
+ .send();
+ t.add_cleanup(async () => {
+ // Release the pointer
+ await (new test_driver.Actions().pointerUp()).send();
+ });
+ assert_equals(document.querySelector("input:active"), radio,
+ "Radios should be :active while the label is pressed");
+
+ // Press tab
+ await (new test_driver.Actions().keyDown(tabKey).keyUp(tabKey)).send();
+ assert_equals(document.querySelector(":active"), null,
+ "Radios should not be :active after tab is used to change focus.");
+}, "Radios should clear :active when the user tabs away from them while pressing the parent label with a pointing device");
+
+promise_test(async t => {
+ const label = document.querySelector("label[for=cb1]");
+ // pointerdown on the label
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, { origin: label })
+ .pointerDown())
+ .send();
+ t.add_cleanup(async () => {
+ // Release the pointer
+ await (new test_driver.Actions().pointerUp()).send();
+ });
+ assert_equals(document.querySelector("input:active"), label.control,
+ "Checkboxes should be :active while the label is pressed");
+
+ // Press tab
+ await (new test_driver.Actions().keyDown(tabKey).keyUp(tabKey)).send();
+ assert_equals(document.querySelector(":active"), null,
+ "Checkboxes should not be :active after tab is used to change focus.");
+}, "Checkboxes should clear :active when the user tabs away from them while pressing the following label with a pointing device");
+
+promise_test(async t => {
+ const label = document.querySelector("label[for=r1]");
+ // pointerdown on the label
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, { origin: label })
+ .pointerDown())
+ .send();
+ t.add_cleanup(async () => {
+ // Release the pointer
+ await (new test_driver.Actions().pointerUp()).send();
+ });
+ assert_equals(document.querySelector("input:active"), label.control,
+ "Radios should be :active while the label is pressed");
+
+ // Press tab
+ await (new test_driver.Actions().keyDown(tabKey).keyUp(tabKey)).send();
+ assert_equals(document.querySelector(":active"), null,
+ "Radios should not be :active after tab is used to change focus.");
+}, "Radios should clear :active when the user tabs away from them while pressing the following label with a pointing device");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur.html
new file mode 100644
index 0000000000..cc88996fe7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-onblur.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="http://crbug.com/1157510">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!-- This behavior is not explicitly specified. -->
+
+<input type=checkbox id=checkbox checked>
+<input type=radio id=radio checked>
+
+<script>
+promise_test(async t => {
+ checkbox.focus();
+
+ // Hold spacebar down
+ await (new test_driver.Actions()).keyDown('\uE00D').send();
+ t.add_cleanup(async () => {
+ // Release spacebar
+ await (new test_driver.Actions()).keyUp('\uE00D').send();
+ });
+ assert_equals(document.querySelector('input:active'), checkbox,
+ 'Checkboxes should be :active while the spacebar is pressed down.');
+
+ // Press tab
+ await (new test_driver.Actions()).keyDown('\uE004').keyUp('\uE004').send();
+ assert_equals(document.querySelector(':active'), null,
+ 'Checkboxes should not be :active after tab is used to change focus.');
+}, 'Checkboxes should clear :active when the user tabs away from them while holding spacebar.');
+
+promise_test(async t => {
+ radio.focus();
+
+ // Hold spacebar down
+ await (new test_driver.Actions()).keyDown('\uE00D').send();
+ t.add_cleanup(async () => {
+ // Release spacebar
+ await (new test_driver.Actions()).keyUp('\uE00D').send();
+ });
+ assert_equals(document.querySelector('input:active'), radio,
+ 'Radios should be :active while the spacebar is pressed down.');
+
+ // Press tab
+ await (new test_driver.Actions()).keyDown('\uE004').keyUp('\uE004').send();
+ assert_equals(document.querySelector(':active'), null,
+ 'Radios should not be :active after tab is used to change focus.');
+}, 'Radios should clear :active when the user tabs away from them while holding spacebar.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-being-disabled.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-being-disabled.html
new file mode 100644
index 0000000000..5f725b85f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-being-disabled.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Tests active state of checkbox/radio when pressing space key but it's disabled by a keydown event listener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+<input type="checkbox">
+<input type="radio">
+<script>
+const spaceKey = "\uE00D";
+
+function disableTarget(event) {
+ event.target.disabled = true;
+}
+
+// If a `keydown` event listener disables the event target, default event
+// handler in browser shouldn't activate the disabled element. Otherwise,
+// the browser loses a chance to inactivate the disabled element because
+// it won't get keyup events until it's enabled again.
+
+promise_test(async t => {
+ const checkbox = document.querySelector("input[type=checkbox]");
+ checkbox.focus();
+ checkbox.addEventListener("keydown", disableTarget);
+ await (new test_driver.Actions()).keyDown(spaceKey).send();
+ let released = false;
+ t.add_cleanup(async () => {
+ if (!released) {
+ await (new test_driver.Actions()).keyUp(spaceKey).send();
+ }
+ checkbox.removeEventListener("keydown", disableTarget);
+ checkbox.remove();
+ });
+ test(() => {
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The checkbox shouldn't be activated"
+ );
+ }, "Space key press shouldn't activate the disabled checkbox");
+
+ await (new test_driver.Actions()).keyUp(spaceKey).send();
+ released = true;
+
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The disabled checkbox should be inactivated even if activated accidentally"
+ );
+}, "Space key shouldn't active the checkbox when it's disabled by a keydown event listener");
+
+promise_test(async t => {
+ const radio = document.querySelector("input[type=radio]");
+ radio.focus();
+ radio.addEventListener("keydown", disableTarget);
+ await (new test_driver.Actions()).keyDown(spaceKey).send();
+ let released = false;
+ t.add_cleanup(async () => {
+ if (!released) {
+ await (new test_driver.Actions()).keyUp(spaceKey).send();
+ }
+ radio.removeEventListener("keydown", disableTarget);
+ radio.disabled = false;
+ });
+ test(() => {
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The radio shouldn't be activated"
+ );
+ }, "Space key press shouldn't activate the disabled radio");
+
+ await (new test_driver.Actions()).keyUp(spaceKey).send();
+ released = true;
+
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The disabled radio should be inactivated even if it's accidentally activated"
+ );
+}, "Space key shouldn't active the radio when it's disabled by a keydown event listener");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-prevented-default.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-prevented-default.html
new file mode 100644
index 0000000000..877cd70e29
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-prevented-default.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Tests active state of checkbox/radio when pressing space key but its default prevented</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+<input type="checkbox">
+<input type="radio">
+<script>
+const spaceKey = "\uE00D";
+
+function preventDefault(event) {
+ event.preventDefault();
+}
+
+promise_test(async t => {
+ const checkbox = document.querySelector("input[type=checkbox]");
+ checkbox.focus();
+ checkbox.addEventListener("keydown", preventDefault);
+ await (new test_driver.Actions()).keyDown(spaceKey).send();
+ t.add_cleanup(async () => {
+ await (new test_driver.Actions()).keyUp(spaceKey).send();
+ checkbox.removeEventListener("keydown", preventDefault);
+ });
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The checkbox shouldn't be activated"
+ );
+}, "Space key shouldn't active the checkbox when its default is prevented");
+
+promise_test(async t => {
+ const radio = document.querySelector("input[type=radio]");
+ radio.focus();
+ radio.addEventListener("keydown", preventDefault);
+ await (new test_driver.Actions()).keyDown(spaceKey).send();
+ t.add_cleanup(async () => {
+ await (new test_driver.Actions()).keyUp(spaceKey).send();
+ radio.removeEventListener("keydown", preventDefault);
+ });
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The radio shouldn't be activated"
+ );
+}, "Space key shouldn't active the radio when its default is prevented");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-untrusted-event.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-untrusted-event.html
new file mode 100644
index 0000000000..190757d8d3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkable-active-space-key-untrusted-event.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Tests active state of checkbox/radio when pressing space key emulated with untrusted key events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<input type="checkbox">
+<input type="radio">
+<script>
+function sendSpaceKeyEvent(eventType, target) {
+ const eventData = { keyCode: 32, which: 32, key: " ", code: "Space"};
+ const spaceKeyEvent = new KeyboardEvent(eventType, eventData);
+ target.dispatchEvent(spaceKeyEvent);
+}
+
+test(t => {
+ const checkbox = document.querySelector("input[type=checkbox]");
+ checkbox.focus();
+ sendSpaceKeyEvent("keydown", checkbox);
+ t.add_cleanup(() => {
+ sendSpaceKeyEvent("keyup", checkbox);
+ });
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The checkbox shouldn't be activated"
+ );
+}, "Space key shouldn't active the checkbox when space key press is emulated by untrusted events");
+
+test(t => {
+ const radio = document.querySelector("input[type=radio]");
+ radio.focus();
+ sendSpaceKeyEvent("keydown", radio);
+ t.add_cleanup(() => {
+ sendSpaceKeyEvent("keyup", radio);
+ });
+ assert_equals(
+ document.querySelector("input:active"),
+ null,
+ "The radio shouldn't be activated"
+ );
+}, "Space key shouldn't active the radio when space key press is emulated by untrusted events");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox-click-events.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox-click-events.html
new file mode 100644
index 0000000000..5051fdd4e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox-click-events.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Checkbox click events</title>
+<link rel="author" title="jeffcarp" href="mailto:gcarpenterv@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/#checkbox-state-(type=checkbox)">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+test(() => {
+
+ const input = document.createElement("input");
+ input.type = "checkbox";
+
+ const values = [];
+
+ input.addEventListener("click", e => {
+ values.push(input.checked);
+ e.preventDefault();
+ values.push(input.checked);
+ });
+
+ input.click();
+
+ values.push(input.checked);
+ assert_array_equals(values, [true, true, false]);
+
+}, "clicking and preventDefaulting a checkbox causes the checkbox to be checked during the click handler but reverted");
+
+test(() => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ document.body.appendChild(input);
+ const events = [];
+
+ input.addEventListener("change", () => {
+ events.push("change");
+ });
+ input.addEventListener("click", () => {
+ events.push("click");
+ });
+ input.addEventListener("input", () => {
+ events.push("input");
+ });
+
+ assert_false(input.checked);
+
+ input.click();
+
+ assert_true(input.checked);
+ assert_array_equals(events, ["click", "input", "change"]);
+
+}, "a checkbox input emits click, input, change events in order after synthetic click");
+
+test(() => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ document.body.appendChild(input);
+ const events = [];
+
+ input.addEventListener("change", () => {
+ events.push("change");
+ });
+ input.addEventListener("click", () => {
+ events.push("click");
+ });
+ input.addEventListener("input", () => {
+ events.push("input");
+ });
+
+ assert_false(input.checked);
+
+ const event = new MouseEvent("click", { bubbles: true, cancelable: true });
+ input.dispatchEvent(event);
+
+ assert_true(input.checked);
+ assert_array_equals(events, ["click", "input", "change"]);
+
+}, "a checkbox input emits click, input, change events in order after dispatching click event");
+
+test(() => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ document.body.appendChild(input);
+ const events = [];
+
+ input.addEventListener("change", () => {
+ events.push("change");
+ });
+ input.addEventListener("click", e => {
+ e.preventDefault();
+ events.push("click");
+ });
+ input.addEventListener("input", () => {
+ events.push("input");
+ });
+
+ assert_false(input.checked);
+
+ input.click();
+
+ assert_false(input.checked);
+ assert_array_equals(events, ["click"]);
+}, "checkbox input respects cancel behavior on synthetic clicks");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html
new file mode 100644
index 0000000000..c48083d685
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>input type checkbox</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox)">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#run-synthetic-click-activation-steps">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<input type=checkbox id=checkbox1>
+<input type=checkbox id=checkbox2 disabled>
+<input type=checkbox id=checkbox3>
+<input type=checkbox id=checkbox4 checked>
+<input type=checkbox id=checkbox5>
+<input type=checkbox id=checkbox6>
+<script>
+ var checkbox1 = document.getElementById('checkbox1'),
+ checkbox2 = document.getElementById('checkbox2'),
+ checkbox3 = document.getElementById('checkbox3'),
+ checkbox4 = document.getElementById('checkbox4'),
+ checkbox5 = document.getElementById('checkbox5'),
+ checkbox6 = document.getElementById('checkbox6'),
+ c1_click_fired = false,
+ c1_input_fired = false,
+ c1_change_fired = false,
+ t1 = async_test("click on mutable checkbox fires a click event, then an input event, then a change event"),
+ t2 = async_test("click on non-mutable checkbox doesn't fire the input or change event"),
+ t3 = async_test("pre-activation steps on unchecked checkbox"),
+ t4 = async_test("pre-activation steps on checked checkbox"),
+ t5 = async_test("canceled activation steps on unchecked checkbox"),
+ t6 = async_test("canceled activation steps on unchecked checkbox (indeterminate=true in onclick)");
+
+ checkbox1.onclick = t1.step_func(function(e) {
+ c1_click_fired = true;
+ assert_false(c1_input_fired, "click event should fire before input event");
+ assert_false(c1_change_fired, "click event should fire before change event");
+ assert_false(e.isTrusted, "click()-initiated click event should not be trusted");
+ });
+ checkbox1.oninput = t1.step_func(function(e) {
+ c1_input_fired = true;
+ assert_true(c1_click_fired, "input event should fire after click event");
+ assert_false(c1_change_fired, "input event should fire before change event");
+ assert_true(e.bubbles, "event should bubble");
+ assert_true(e.isTrusted, "click()-initiated event should be trusted");
+ assert_false(e.cancelable, "event should not be cancelable");
+ assert_true(checkbox1.checked, "checkbox is checked");
+ assert_false(checkbox1.indeterminate, "checkbox is not indeterminate");
+ });
+
+ checkbox1.onchange = t1.step_func(function(e) {
+ c1_change_fired = true;
+ assert_true(c1_click_fired, "change event should fire after click event");
+ assert_true(c1_input_fired, "change event should fire after input event");
+ assert_true(e.bubbles, "event should bubble")
+ assert_true(e.isTrusted, "click()-initiated event should be trusted");
+ assert_false(e.cancelable, "event should not be cancelable");
+ assert_true(checkbox1.checked, "checkbox is checked");
+ assert_false(checkbox1.indeterminate, "checkbox is not indeterminate");
+ });
+
+ checkbox2.oninput= t2.step_func(function(e) {
+ assert_unreached("event input fired");
+ });
+
+ checkbox2.onchange = t2.step_func(function(e) {
+ assert_unreached("event change fired");
+ });
+
+ t1.step(function() {
+ checkbox1.click();
+ assert_true(c1_input_fired);
+ assert_true(c1_change_fired);
+ t1.done();
+ });
+
+ t2.step(function() {
+ checkbox2.click();
+ t2.done();
+ });
+
+ t3.step(function() {
+ checkbox3.indeterminate = true;
+ checkbox3.click();
+ assert_true(checkbox3.checked);
+ assert_false(checkbox3.indeterminate);
+ t3.done();
+ });
+
+ t4.step(function() {
+ checkbox4.indeterminate = true;
+ checkbox4.click();
+ assert_false(checkbox4.checked);
+ assert_false(checkbox4.indeterminate);
+ t4.done();
+ });
+
+ checkbox5.onclick = t5.step_func(function(e) {
+ e.preventDefault();
+ /*
+ The prevention of the click doesn't have an effect until after all the
+ click event handlers have been run.
+ */
+ assert_true(checkbox5.checked);
+ assert_false(checkbox5.indeterminate);
+ t5.step_timeout(function() {
+ /*
+ The click event has finished being dispatched, so the checkedness and
+ determinateness have been toggled back by now because the event
+ was preventDefault-ed.
+ */
+ assert_false(checkbox5.checked);
+ assert_false(checkbox5.indeterminate);
+ t5.done();
+ }, 0);
+ });
+
+ t5.step(function(){
+ assert_false(checkbox5.checked);
+ assert_false(checkbox5.indeterminate);
+ checkbox5.click();
+ });
+
+ checkbox6.onclick = t6.step_func(function(e) {
+ checkbox6.indeterminate = true;
+ e.preventDefault();
+ /*
+ The prevention of the click doesn't have an effect until after all the
+ click event handlers have been run.
+ */
+ assert_true(checkbox6.checked);
+ assert_true(checkbox6.indeterminate);
+ t6.step_timeout(function() {
+ /*
+ The click event has finished being dispatched, so the checkedness and
+ determinateness have been toggled back by now because the event
+ was preventDefault-ed.
+ */
+ assert_false(checkbox6.checked);
+ assert_false(checkbox6.indeterminate);
+ t6.done();
+ }, 0);
+ });
+
+ t6.step(function(){
+ assert_false(checkbox6.checked);
+ assert_false(checkbox6.indeterminate);
+ checkbox6.click();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checked.xhtml b/testing/web-platform/tests/html/semantics/forms/the-input-element/checked.xhtml
new file mode 100644
index 0000000000..70aeb51097
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checked.xhtml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>input@checked is immediately reflected to 'checked' IDL attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<input style="display: none" type="checkbox" checked="">
+<script>
+test(function(){
+assert_true(document.querySelector('input').checked, 'Examining "checked" IDL attribute value:')
+});
+</script>
+</input>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/clone.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/clone.html
new file mode 100644
index 0000000000..0f7e053baa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/clone.html
@@ -0,0 +1,150 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test input value retention upon clone</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>form {display: none;} </style>
+<form>
+<p><input type=checkbox> This checkbox is initially unchecked.</p>
+<p><input type=checkbox checked="checked"> This checkbox is initially checked.</p>
+<p><input type=radio name=radio> This radiobutton is initially unchecked.</p>
+<p><input type=radio checked="checked" name=radio> This radiobutton is initially checked.</p>
+<p><input type=hidden value="DEFAULT
+DEFAULT"> This hidden field has the initial value "DEFAULT\nDEFAULT".</p>
+<p><input type=text value=DEFAULT> This text field has the initial value "DEFAULT".</p>
+<p><input type=search value=DEFAULT> This search field has the initial value "DEFAULT".</p>
+<p><input type=tel value=DEFAULT> This phone number field has the initial value "DEFAULT".</p>
+<p><input type=url value=https://default.invalid/> This URL field has the initial value "https://default.invalid/".</p>
+<p><input type=email value=default@default.invalid> This email field has the initial value "default@default.invalid".</p>
+<p><input type=password value=DEFAULT> This password field has the initial value "DEFAULT".</p>
+<p><input type=date value=2015-01-01> This date field has the initial value "2015-01-01".</p>
+<p><input type=month value=2015-01> This month field has the initial value "2015-01".</p>
+<p><input type=week value=2015-W01> This week field has the initial value "2015-W01".</p>
+<p><input type=time value=12:00> This time field has the initial value "12:00".</p>
+<p><input type=datetime-local value=2015-01-01T12:00> This datetime (local) field has the initial value "2015-01-01T12:00".</p>
+<p><input type=number value=1> This number field has the initial value "1".</p>
+<p><input type=range value=1> This range control has the initial value "1".</p>
+<p><input type=color value=#ff0000> This color picker has the initial value "#FF0000".</p>
+<p><input type="button" value="Clone" onclick="clone();"></p>
+</form>
+<script>
+setup(function() {
+ let form = document.getElementsByTagName("form")[0];
+ let inputs = form.getElementsByTagName("input");
+ inputs[0].checked = true;
+ inputs[1].checked = false;
+ inputs[2].checked = true;
+ inputs[4].value = "CHANGED\nCHANGED";
+ inputs[5].value = "CHANGED";
+ inputs[6].value = "CHANGED";
+ inputs[7].value = "CHANGED";
+ inputs[8].value = "https://changed.invalid/";
+ inputs[9].value = "changed@changed.invalid";
+ inputs[10].value = "CHANGED";
+ inputs[11].value = "2016-01-01";
+ inputs[12].value = "2016-01";
+ inputs[13].value = "2016-W01";
+ inputs[14].value = "12:30";
+ inputs[15].value = "2016-01-01T12:30";
+ inputs[16].value = "2";
+ inputs[17].value = "2";
+ inputs[18].value = "#00ff00";
+ let clone = form.cloneNode(true);
+ document.body.appendChild(clone);
+});
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_true(inputs[0].checked, "Should have retained checked state");
+}, "Checkbox must retain checked state.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_false(inputs[1].checked, "Should have retained unchecked state");
+}, "Checkbox must retain unchecked state.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_true(inputs[2].checked, "Should have retained checked state");
+}, "Radiobutton must retain checked state.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_false(inputs[3].checked, "Should have retained unchecked state");
+}, "Radiobutton must retain unchecked state.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[4].value, "CHANGED\nCHANGED", "Should have retained the changed value.");
+}, "Hidden field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[5].value, "CHANGED", "Should have retained the changed value.");
+}, "Text field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[6].value, "CHANGED", "Should have retained the changed value.");
+}, "Search field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[7].value, "CHANGED", "Should have retained the changed value.");
+}, "Phone number field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[8].value, "https://changed.invalid/", "Should have retained the changed value.");
+}, "URL field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[9].value, "changed@changed.invalid", "Should have retained the changed value.");
+}, "Email field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[10].value, "CHANGED", "Should have retained the changed value.");
+}, "Password field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[11].value, "2016-01-01", "Should have retained the changed value.");
+}, "Date field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[12].value, "2016-01", "Should have retained the changed value.");
+}, "Month field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[13].value, "2016-W01", "Should have retained the changed value.");
+}, "Week field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[14].value, "12:30", "Should have retained the changed value.");
+}, "Time field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[15].value, "2016-01-01T12:30", "Should have retained the changed value.");
+}, "Datetime (local) field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[16].value, "2", "Should have retained the changed value.");
+}, "Number field must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[17].value, "2", "Should have retained the changed value.");
+}, "Range control must retain changed value.");
+test(function() {
+ let clone = document.getElementsByTagName("form")[1];
+ let inputs = clone.getElementsByTagName("input");
+ assert_equals(inputs[18].value, "#00ff00", "Should have retained the changed value.");
+}, "Color picker must retain changed value.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/cloning-steps.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/cloning-steps.html
new file mode 100644
index 0000000000..fe468509e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/cloning-steps.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cloning of input elements</title>
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-node-clonenode">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-node-clone">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-node-clone-ext">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-node-clone-ext">
+<link rel="author" title="Matthew Phillips" href="mailto:matthew@matthewphillips.info">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script type=module>
+import inputTypes from "./input-types.js";
+
+test(function() {
+ var input = document.createElement("input");
+ input.value = "foo bar";
+
+ var copy = input.cloneNode();
+ assert_equals(copy.value, "foo bar");
+}, "input element's value should be cloned");
+
+test(function() {
+ var input = document.createElement("input");
+ input.value = "foo bar";
+
+ var copy = input.cloneNode();
+ copy.setAttribute("value", "something else");
+
+ assert_equals(copy.value, "foo bar");
+}, "input element's dirty value flag should be cloned, so setAttribute doesn't affect the cloned input's value");
+
+for (const inputType of inputTypes) {
+ test(function() {
+ var input = document.createElement("input");
+ input.setAttribute("type", inputType);
+ input.indeterminate = true;
+
+ var copy = input.cloneNode();
+ assert_equals(copy.indeterminate, true);
+ }, `input[type=${inputType}] element's indeterminateness should be cloned`);
+
+ test(function() {
+ var input = document.createElement("input");
+ input.setAttribute("type", inputType);
+ input.checked = true;
+
+ var copy = input.cloneNode();
+ assert_equals(copy.checked, true);
+ }, `input[type=${inputType}] element's checkedness should be cloned`);
+
+ test(function() {
+ var input = document.createElement("input");
+ input.setAttribute("type", inputType);
+ input.checked = false;
+
+ var copy = input.cloneNode();
+ copy.setAttribute("checked", "checked");
+
+ assert_equals(copy.checked, false);
+ }, `input[type=${inputType}] element's dirty checkedness should be cloned, so setAttribute doesn't affect the cloned input's checkedness`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/color.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/color.html
new file mode 100644
index 0000000000..6164815f66
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/color.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Form input type=color</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/multipage/common-microsyntaxes.html#colors">
+<link rel=help href="https://html.spec.whatwg.org/multipage/multipage/states-of-the-type-attribute.html#color-state-(type=color)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var colors = [
+ {value: "", expected: "#000000", testname: "Empty value should return #000000"},
+ {expected: "#000000", testname: "Missing value should return #000000"},
+ {value: "#ffffff", expected: "#ffffff", testname: "Valid simple color: should return #ffffff"},
+ {value: "#FFFFFF", expected: "#ffffff", testname: "Valid simple color (containing LATIN CAPITAL LETTERS): should return #ffffff (converted to ASCII lowercase)"},
+ {value: "#0F0F0F", expected: "#0f0f0f", testname: "Zero-padding"},
+ {value: "#fff", expected: "#000000", testname: "Invalid simple color: not 7 characters long"},
+ {value: "fffffff", expected: "#000000", testname: "Invalid simple color: no starting # sign"},
+ {value: "#gggggg", expected: "#000000", testname: "Invalid simple color: non ASCII hex digits"},
+ {value: "foobar", expected: "#000000", testname: "Invalid simple color: foobar"},
+ {value: "#ffffff\u0000", expected: "#000000", testname: "Invalid color: trailing Null (U+0000)"},
+ {value: "#ffffff;", expected: "#000000", testname: "Invalid color: trailing ;"},
+ {value: " #ffffff", expected: "#000000", testname: "Invalid color: leading space"},
+ {value: "#ffffff ", expected: "#000000", testname: "Invalid color: trailing space"},
+ {value: " #ffffff ", expected: "#000000", testname: "Invalid color: leading+trailing spaces"},
+ {value: "crimson", expected: "#000000", testname: "Invalid color: keyword crimson"},
+ {value: "bisque", expected: "#000000", testname: "Invalid color: keyword bisque"},
+ {value: "currentColor", expected: "#000000", testname: "Invalid color: keyword currentColor"},
+ {value: "transparent", expected: "#000000", testname: "Invalid color: keyword transparent"},
+ {value: "ActiveBorder", expected: "#000000", testname: "Invalid color: keyword ActiveBorder"},
+ {value: "inherit", expected: "#000000", testname: "Invalid color: keyword inherit"},
+ {value: "rgb(1,1,1)", expected: "#000000", testname: "Invalid color: rgb(1,1,1)"},
+ {value: "rgb(1,1,1,1)", expected: "#000000", testname: "Invalid color: rgb(1,1,1,1)"},
+ {value: "#FFFFF\u1F4A9", expected: "#000000", testname: "Invalid color: PILE OF POO (U+1F4A9)"}
+ ];
+ for (var i = 0; i < colors.length; i++) {
+ var w = colors[i];
+ test(function() {
+ var input = document.createElement("input");
+ input.type = "color";
+ input.value = w.value;
+ assert_equals(input.value, w.expected);
+ }, w.testname);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/date.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/date.html
new file mode 100644
index 0000000000..9b95b86b16
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/date.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Inputs Date</title>
+ <link rel="author" title="Morishita Hiromitsu" href="mailto:hero@asterisk-works.jp">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#date-state-(type=date)">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dates-and-times">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Inputs Date</h1>
+ <div style="display: none">
+ <input id="valid" type="date" value="2011-11-01" min="2011-01-01" max="2011-12-31" />
+ <input id="too_small_value" type="date" value="1999-01-31" min="2011-01-01" max="2011-12-31"/>
+ <input id="too_large_value" type="date" value="2099-01-31" min="2011-01-01" max="2011-12-31"/>
+ <input id="invalid_min" type="date" value="2011-01-01" min="1999-1" max="2011-12-31"/>
+ <input id="invalid_max" type="date" value="2011-01-01" min="2011-01-01" max="2011-13-162-777"/>
+ <input id="min_larger_than_max" type="date" value="2011-01-01" min="2099-01-01" max="2011-12-31"/>
+ <input id="invalid_value" type="date" value="invalid-date" min="2011-01-01" max="2011-12-31"/>
+ </div>
+
+ <div id="log"></div>
+
+ <script type="text/javascript">
+ test(function() {
+ assert_equals(document.getElementById("valid").type, "date")
+ }, "date type support on input element");
+
+ test(function() {
+ assert_equals(document.getElementById("valid").value, "2011-11-01");
+ assert_equals(document.getElementById("too_small_value").value, "1999-01-31");
+ assert_equals(document.getElementById("too_large_value").value, "2099-01-31");
+ }, "The value attribute, if specified and not empty, must have a value that is a valid date string.");
+
+ test(function() {
+ assert_equals(document.getElementById("valid").min, "2011-01-01");
+ assert_equals(document.getElementById("invalid_min").min, "1999-1");
+ }, "The min attribute must be reflected verbatim by the min property.");
+
+ test(function() {
+ assert_equals(document.getElementById("valid").max, "2011-12-31");
+ assert_equals(document.getElementById("min_larger_than_max").max, "2011-12-31");
+ assert_equals(document.getElementById("invalid_max").max, "2011-13-162-777");
+ }, "The max attribute must be reflected verbatim by the max property.");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_value").value, "");
+ }, "User agents must not allow the user to set the value to a non-empty string that is not a valid date string.");
+ test(function() {
+ var numDays = [
+ // the number of days in month month of year year is: 31 if month is 1, 3, 5, 7, 8, 10, or 12;
+ {value: "2014-01-31", expected: "2014-01-31", testname: "January has 31 days"},
+ {value: "2014-01-32", expected: "", testname: "January has 31 days"},
+ {value: "2014-03-31", expected: "2014-03-31", testname: "March has 31 days"},
+ {value: "2014-03-32", expected: "", testname: "March has 31 days"},
+ {value: "2014-05-31", expected: "2014-05-31", testname: "May has 31 days"},
+ {value: "2014-05-32", expected: "", testname: "May has 31 days"},
+ {value: "2014-07-31", expected: "2014-07-31", testname: "July has 31 days"},
+ {value: "2014-07-32", expected: "", testname: "July has 31 days"},
+ {value: "2014-08-31", expected: "2014-08-31", testname: "August has 31 days"},
+ {value: "2014-08-32", expected: "", testname: "August has 31 days"},
+ {value: "2014-10-31", expected: "2014-10-31", testname: "October has 31 days"},
+ {value: "2014-10-32", expected: "", testname: "October has 31 days"},
+ {value: "2014-12-31", expected: "2014-12-31", testname: "December has 31 days"},
+ {value: "2014-12-32", expected: "", testname: "December has 31 days"},
+ // the number of days in month month of year year is: 30 if month is 4, 6, 9, or 11;
+ {value: "2014-04-30", expected: "2014-04-30", testname: "April has 30 days"},
+ {value: "2014-04-31", expected: "", testname: "April has 30 days"},
+ {value: "2014-06-30", expected: "2014-06-30", testname: "June has 30 days"},
+ {value: "2014-06-31", expected: "", testname: "June has 30 days"},
+ {value: "2014-09-30", expected: "2014-09-30", testname: "September has 30 days"},
+ {value: "2014-09-31", expected: "", testname: "September has 30 days"},
+ {value: "2014-11-30", expected: "2014-11-30", testname: "November has 30 days"},
+ {value: "2014-11-31", expected: "", testname: "November has 30 days"},
+ // leap years
+ {value: "2014-02-28", expected: "2014-02-28", testname: "2014 is not a leap year: February has 28 days"},
+ {value: "2014-02-29", expected: "", testname: "2014 is not a leap year: February has 28 days: value should be empty"},
+ {value: "2016-02-29", expected: "2016-02-29", testname: "2016 is a leap year: February has 29 days"}
+ ];
+ for (var i = 0; i < numDays.length; i++) {
+ var input = document.createElement("input");
+ input.type = "date";
+ input.value = numDays[i].value;
+ assert_equals(input.value, numDays[i].expected, numDays[i].testname);
+ }
+ }, "Number of days");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local-trailing-zeros.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local-trailing-zeros.html
new file mode 100644
index 0000000000..0fb031d510
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local-trailing-zeros.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel=help href="https://html.spec.whatwg.org/multipage/multipage/common-microsyntaxes.html#local-dates-and-times">
+<link rel=help href="https://html.spec.whatwg.org/multipage/multipage/states-of-the-type-attribute.html#local-date-and-time-state-(type=datetime-local)">
+
+<input id=input type=datetime-local value="2022-04-19T12:34:56.010">
+
+<script>
+test(() => {
+ assert_equals(input.value, '2022-04-19T12:34:56.01');
+}, 'Verifies that trailing zeros in the milliseconds portion of the date strings are removed.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local.html
new file mode 100644
index 0000000000..2fe24d7e8a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-local.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Form input type=datetime-local</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/multipage/common-microsyntaxes.html#local-dates-and-times">
+<link rel=help href="https://html.spec.whatwg.org/multipage/multipage/states-of-the-type-attribute.html#local-date-and-time-state-(type=datetime-local)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var datetimeLocal = [
+ {value: "", expected: "", testname: "empty value"},
+ {value: "2014-01-01T11:11:11.111", expected: "2014-01-01T11:11:11.111", testname: "datetime-local input value set to 2014-01-01T11:11:11.111 without min/max"},
+ {value: "2014-01-01 11:11:11.111", expected: "2014-01-01T11:11:11.111", testname: "datetime-local input value set to 2014-01-01 11:11:11.111 without min/max"},
+ {value: "2014-01-01 11:11", expected: "2014-01-01T11:11", testname: "datetime-local input value set to 2014-01-01 11:11 without min/max"},
+ {value: "2014-01-01 00:00:00.000", expected: "2014-01-01T00:00", testname: "datetime-local input value set to 2014-01-01 00:00:00.000 without min/max"},
+ {value: "2014-01-0 11:11", expected: "", testname: "datetime-local input value set to 2014-01-0 11:11 without min/max"},
+ {value: "2014-01-01 11:1", expected: "", testname: "datetime-local input value set to 2014-01-01 11:1 without min/max"},
+ {value: "2014-01-01 11:1d1", expected: "", testname: "invalid datetime-local input value 1"},
+ {value: "2014-01-01H11:11", expected: "", testname: "invalid datetime-local input value 2"},
+ {value: "2014-01-01 11:11:", expected: "", testname: "invalid datetime-local input value 3"},
+ {value: "2014-01-01 11-11", expected: "", testname: "invalid datetime-local input value 4"},
+ {value: "2014-01-01 11:11:123", expected: "", testname: "invalid datetime-local input value 5"},
+ {value: "2014-01-01 11:11:12.1234", expected: "", testname: "invalid datetime-local input value 6"},
+ {value: "2014-01-01 11:12", attributes: { min: "2014-01-01 11:11" }, expected: "2014-01-01T11:12", testname: "Value >= min attribute"},
+ {value: "2014-01-01 11:10", attributes: { min: "2014-01-01 11:11" }, expected: "2014-01-01T11:10", testname: "Value < min attribute"},
+ {value: "2014-01-01 11:10", attributes: { max: "2014-01-01 11:11" }, expected: "2014-01-01T11:10", testname: "Value <= max attribute"},
+ {value: "2014-01-01 11:12", attributes: { max: "2014-01-01 11:11" }, expected: "2014-01-01T11:12", testname: "Value > max attribute"}
+ ];
+ for (var i = 0; i < datetimeLocal.length; i++) {
+ var w = datetimeLocal[i];
+ test(function() {
+ var input = document.createElement("input");
+ input.type = "datetime-local";
+ input.value = w.value;
+ for(var attr in w.attributes) {
+ input[attr] = w.attributes[attr];
+ }
+ assert_equals(input.value, w.expected);
+ }, w.testname);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-weekmonth.html
new file mode 100644
index 0000000000..4a7b66ddd1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime-weekmonth.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Date and Time Inputs</title>
+ <meta name=viewport content="width=device-width, maximum-scale=1.0, user-scalable=no" />
+ <link rel="author" title="Fabrice Clari" href="mailto:f.clari@inno-group.com">
+ <link rel="author" title="Dimitri Bocquet" href="mailto:Dimitri.Bocquet@mosquito-fp7.eu">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-type">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-value">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-min">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-max">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-step">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-stepup">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-stepdown">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <h1>Date and Time Inputs</h1>
+ <div style="display: none">
+ <input type="month" value="2011-01" min="2011-01" max="2011-12" step="2" />
+ <input type="week" value="2011-W40" min="2011-W20" max="2011-W50" step="2" />
+ </div>
+
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].type, "month")}, "month type support on input element");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].value, "2011-01")}, "[month] The value must be a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].min, "2011-01")}, "[month] The min attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].max, "2011-12")}, "[month] The max attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].step, "2")}, "[month] The step attribute must be expressed in seconds");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[0].stepUp) == "function")}, "[month] stepUp method support on input 'month' element");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[0].stepDown) == "function")}, "[month] stepDown method support on input 'month' element");
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].type, "week")}, "week type support on input element");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].value, "2011-W40")}, "[week] The value must be a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].min, "2011-W20")}, "[week] The min attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].max, "2011-W50")}, "[week] The max attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].step, "2")}, "[week] The step attribute must be expressed in seconds");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[1].stepUp) == "function")}, "[week] stepUp method support on input 'week' element");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[1].stepDown) == "function")}, "[week] stepDown method support on input 'week' element");
+
+ </script>
+
+ </body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime.html
new file mode 100644
index 0000000000..e762060ea7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/datetime.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Date and Time Inputs</title>
+ <meta name=viewport content="width=device-width, maximum-scale=1.0, user-scalable=no" />
+ <link rel="author" title="Fabrice Clari" href="mailto:f.clari@inno-group.com">
+ <link rel="author" title="Dimitri Bocquet" href="mailto:Dimitri.Bocquet@mosquito-fp7.eu">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-type">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-value">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-min">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-max">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-step">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-stepup">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-stepdown">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+
+ <h1>Date and Time Inputs</h1>
+ <div style="display: none">
+ <input type="date" value="2011-12-01" min="2011-12-01" max="2011-12-31" step="5" />
+ <input type="time" value= "12:00" min="11:30" max="14:00" step="600" />
+ <input type="datetime-local" value="2011-12-01T12:00" min="2011-12-01T12:00" max="2011-12-31T22:00" step="7200" />
+ </div>
+
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].type, "date")}, "date type support on input element");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].value, "2011-12-01")}, "[date] The value must be a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].min, "2011-12-01")}, "[date] The min attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].max, "2011-12-31")}, "[date] The max attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].step, "5")}, "[date] The step attribute must be expressed in seconds");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[0].stepUp) == "function")}, "[date] stepUp method support on input 'date' element");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[0].stepDown) == "function")}, "[date] stepDown method support on input 'date' element");
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].type, "time")}, "[time] time type support on input element");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].value, "12:00")}, "[time] The value must be a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].min, "11:30")}, "[time] The min attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].max, "14:00")}, "[time] The max attribute must have a value that is a valid global date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[1].step, "600")}, "[time] The step attribute must be expressed in seconds");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[1].stepUp) == "function")}, "[time] stepUp method support on input 'time' element");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[1].stepDown) == "function")}, "[time] stepDown method support on input 'time' element");
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[2].type, "datetime-local")}, "datetime-local type support on input element");
+ test(function() {assert_equals(document.getElementsByTagName("input")[2].value, "2011-12-01T12:00")}, "[datetime-local] The must be a valid local date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[2].min, "2011-12-01T12:00")}, "[datetime-local] The min attribute must have a value that is a valid local date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[2].max, "2011-12-31T22:00")}, "[datetime-local] The max attribute must have a value that is a valid local date and time string");
+ test(function() {assert_equals(document.getElementsByTagName("input")[2].step, "7200")}, "[datetime-local] The step attribute must be expressed in seconds");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[2].stepUp) == "function")}, "[datetime-local] stepUp method support on input 'datetime-local' element");
+ test(function() {assert_true(typeof(document.getElementsByTagName("input")[2].stepDown) == "function")}, "[datetime-local] stepDown method support on input 'datetime-local' element");
+
+ </script>
+
+ </body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/defaultValue-clobbering.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/defaultValue-clobbering.html
new file mode 100644
index 0000000000..41ff967c19
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/defaultValue-clobbering.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<meta name="assert" content="Assigning to defaultValue does not modify text a user has already typed in.">
+
+<!-- This behavior is not explicitly specified. -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<div>
+ email with leading whitespace: <input id=emailinput type=email>
+</div>
+<div>
+ number with trailing incomplete exponent: <input id=numberinput type=number>
+</div>
+
+<script>
+promise_test(async () => {
+ await test_driver.send_keys(emailinput, ' user');
+ assert_false(emailinput.validity.valid, '" user" should not be a valid value for type=email.');
+
+ emailinput.defaultValue = emailinput.value;
+ assert_false(emailinput.validity.valid, 'Assigning to defaultValue should not affect input.validity.');
+}, 'Visible value and validity should not be affected when assigning to the defaultValue property for type=email.');
+
+promise_test(async () => {
+ await test_driver.send_keys(numberinput, '123e');
+ assert_false(numberinput.validity.valid, '"123e" should not be a valid value for type=number.');
+
+ numberinput.defaultValue = numberinput.value;
+ assert_false(numberinput.validity.valid, 'Assigning to defaultValue should not affect input.validity.');
+}, 'Visible value and validity should not be affected when assigning to the defaultValue property for type=number.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/disabled-click-picker-manual.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/disabled-click-picker-manual.html
new file mode 100644
index 0000000000..b77f981e6c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/disabled-click-picker-manual.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Disabled input elements must not open pickers</title>
+<p>
+ Click the Open buttons below. If clicking them does not open any pickers, then consider this as a passing test.<br>
+ (This is manual because we don't have an event to check whether the picker is opened or not.)
+</p>
+<input disabled type="color" id="color"><button>Open</button><br>
+<input disabled type="file" id="file"><button>Open</button>
+<script>
+ for (const button of document.getElementsByTagName("button")) {
+ button.onclick = () => {
+ const input = button.previousElementSibling;
+ input.dispatchEvent(new MouseEvent("click"));
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/email-set-value.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/email-set-value.html
new file mode 100644
index 0000000000..95245fb824
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/email-set-value.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Input Email setValue</title>
+<link rel="author" href="mailto:atotic@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#e-mail-state-(type=email)">
+<link rel="help" href="https://crbug.com/423785">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input type="email">
+
+<script>
+promise_test(async () => {
+ let input = document.querySelector("input");
+ let unsanitized = ' foo@bar ';
+ let sanitized = unsanitized.trim();
+ await test_driver.send_keys(input, unsanitized);
+ input.select();
+ assert_equals(input.value, sanitized, "value is sanitized");
+ assert_equals(window.getSelection().toString(), unsanitized,
+ "visible value is unsanitized");
+ input.value = sanitized;
+ input.select();
+ assert_equals(window.getSelection().toString(), sanitized,
+ "visible value is sanitized after setValue(sanitized)");
+},
+"setValue(sanitizedValue) is reflected in visible text field content");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/email.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/email.html
new file mode 100644
index 0000000000..c187d89bad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/email.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Input Email</title>
+<link rel="author" title="Kazuki Kanamori" href="mailto:yogurito@gmail.com">
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#e-mail-state-(type=email)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input type="email" id="single_email" value="user@example.com"/>
+<input type="email" id="multiple_email" value="user1@example.com, user2@test.com" multiple/>
+<div id="log"></div>
+
+<script type="text/javascript">
+ var single = document.getElementById('single_email'),
+ mult = document.getElementById('multiple_email');
+
+ test(function(){
+ assert_false(single.multiple);
+ }, "single_email doesn't have the multiple attribute");
+
+ test(function(){
+ single.value = 'user2@example.com\u000A';
+ assert_equals(single.value, 'user2@example.com');
+ single.value = 'user3@example.com\u000D';
+ assert_equals(single.value, 'user3@example.com');
+ }, 'value should be sanitized: strip line breaks');
+
+ test(function(){
+ single.value = 'user4@example.com';
+ assert_true(single.validity.valid);
+ single.value = 'example.com';
+ assert_false(single.validity.valid);
+ }, 'Email address validity');
+
+ test(function(){
+ single.setAttribute('multiple', true);
+ single.value = ' user@example.com , user2@example.com ';
+ assert_equals(single.value, 'user@example.com,user2@example.com');
+ single.removeAttribute('multiple');
+ assert_equals(single.value, 'user@example.com,user2@example.com');
+ }, 'When the multiple attribute is removed, the user agent must run the value sanitization algorithm');
+
+ test(function(){
+ assert_true(mult.multiple);
+ }, "multiple_email has the multiple attribute");
+
+ test(function(){
+ mult.value = ' user1@example.com , user2@test.com, user3@test.com ';
+ assert_equals(mult.value, 'user1@example.com,user2@test.com,user3@test.com');
+ }, "run the value sanitization algorithm after setting a new value");
+
+ test(function(){
+ mult.value = 'user1@example.com,user2@test.com,user3@test.com';
+ assert_true(mult.validity.valid);
+
+ mult.value = 'u,ser1@example.com,user2@test.com,user3@test.com';
+ assert_false(mult.validity.valid);
+ }, "valid value is a set of valid email addresses separated by a single ','");
+
+ test(function(){
+ mult.removeAttribute('multiple');
+ mult.value = 'user1@example.com , user2@example.com';
+ assert_equals(mult.value, 'user1@example.com , user2@example.com');
+ mult.setAttribute('multiple', true);
+ assert_equals(mult.value, 'user1@example.com,user2@example.com');
+ }, 'When the multiple attribute is set, the user agent must run the value sanitization algorithm');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/event-select-manual.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/event-select-manual.html
new file mode 100644
index 0000000000..ed0b21e9f3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/event-select-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLInputElement Test: select event</title>
+<link rel="author" title="Intel" href="www.intel.com/">
+<meta name="flags" content="interact">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-select">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="testForm" name="testForm">
+ <input id="testInput" type="text" value="0123456789"/>
+</form>
+
+<h2>Description</h2>
+<p>
+ This test validates that select characters in input element should fired select event.
+</p>
+
+<h2>Test steps:</h2>
+<ol>
+ <li>
+ Select any numeric characters in the input flag below
+ </li>
+</ol>
+
+<script>
+
+let input = document.getElementById("testInput");
+
+setup({explicit_done : true, explicit_timeout : true});
+
+on_event(input, "select", evt => {
+ test(() => {
+ assert_greater_than(input.value.substring(input.selectionStart, input.selectionEnd).length, 0, "Check if the select event captured when text selected");
+ });
+ done();
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html
new file mode 100644
index 0000000000..9e2d47c423
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>input type file</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#file-upload-state-(type=file)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<p>Manual test: clicking on the input should open a prompt allowing you to select a file.</p>
+<input type=file id=file>
+<script>
+ setup({explicit_timeout:true});
+
+ var input = document.getElementById('file'),
+ t1 = async_test("selecting files should fire the input event at the input element"),
+ t2 = async_test("selecting files should fire the change event at the input element");
+
+ document.getElementById('file').oninput = t1.step_func_done(function(e) {
+ assert_true(e.bubbles, "input event bubbles");
+ assert_true(e.isTrusted, "input event should be trusted");
+ assert_false(e.cancelable, "input event should not be cancelable");
+ })
+ document.getElementById('file').onchange = t2.step_func_done(function(e) {
+ assert_true(e.bubbles, "change event bubbles");
+ assert_true(e.isTrusted, "change event should be trusted");
+ assert_false(e.cancelable, "change event should not be cancelable");
+ assert_true(input.files instanceof FileList);
+ assert_equals(input.value, "C:\\fakepath\\" + input.files[0].name);
+ })
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/files.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/files.html
new file mode 100644
index 0000000000..43ebd71906
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/files.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLInputElement#files</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+var types = [
+ "hidden",
+ "text",
+ "search",
+ "tel",
+ "url",
+ "email",
+ "password",
+ "date",
+ "month",
+ "week",
+ "time",
+ "datetime-local",
+ "number",
+ "range",
+ "color",
+ "checkbox",
+ "radio",
+ "submit",
+ "image",
+ "reset",
+ "button",
+];
+
+types.forEach(function(type) {
+ test(function() {
+ const input = document.createElement("input"),
+ input2 = document.createElement("input");
+ input.type = type;
+ input2.type = "file";
+ assert_equals(input.files, null, "files should be null");
+
+ input.files = input2.files;
+ assert_equals(input.files, null, "files should remain null as it cannot be set when it does not apply");
+ }, "files for input type=" + type);
+});
+
+test(function() {
+ var input = document.createElement("input");
+ input.type = "file";
+ assert_not_equals(input.files, null);
+ assert_true(input.files instanceof FileList, "files should be a FileList");
+ var files = input.files;
+ assert_equals(input.files, files, "files should return the same object");
+}, "files for input type=file");
+
+test(() => {
+ const i1 = document.createElement("input"),
+ i2 = document.createElement("input");
+ i1.type = "file";
+ i2.type = "file";
+
+ const files = i2.files;
+ i1.files = i2.files;
+ assert_equals(i1.files, files, "FileList should not be copied");
+ assert_equals(i2.files, files, "FileList can be shared across input elements");
+
+ i1.files = null;
+ assert_equals(i1.files, files, "files cannot be set to null");
+
+ assert_throws_js(TypeError, () => i1.files = [], "files cannot be set to an array");
+ assert_throws_js(TypeError, () => i1.files = [new File([], "x")], "files cannot be set to an array (even when it contains File objects)");
+}, "setting <input type=file>.files");
+
+test(() => {
+ const i = document.createElement("input");
+ i.type = "file";
+
+ let dt = new DataTransfer();
+
+ const files = dt.files;
+ i.files = files;
+ assert_equals(i.files, files, "FileList should not be copied");
+ assert_equals(dt.files, files, "FileList can be shared across input / DataTransfer");
+}, "setting <input type=file>.files from DataTransfer");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change-on-blur.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change-on-blur.html
new file mode 100644
index 0000000000..23292d3a83
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change-on-blur.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Input type switch on blur event should clean up properly</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script type=module>
+import inputTypes from "./input-types.js";
+
+function tick() {
+ return new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+}
+
+function test_from_to(fromType, toType, capture) {
+ if (fromType == toType) {
+ return;
+ }
+ promise_test(async function(t) {
+ const input = document.createElement("input");
+ input.type = fromType;
+ document.body.appendChild(input);
+ input.focus();
+ assert_equals(document.activeElement, input, `${fromType} input should be focused`);
+ function onFocus() {
+ t.assert_unreached("shouldn't be getting spurious focus events");
+ }
+ function onBlur() {
+ input.type = toType;
+ }
+ input.addEventListener("focus", onFocus);
+ input.addEventListener("blur", onBlur, capture);
+ await tick();
+
+ assert_equals(document.activeElement, input, `${fromType} input should still be focused after tick`);
+ assert_true(input.matches(":focus"), `${fromType} input should match :focus`);
+ assert_true(input.matches(":focus-visible"), `${fromType} input should match :focus-visible`);
+
+ input.blur();
+
+ assert_equals(document.activeElement, document.body, `${fromType} input should not remain focused after blur`);
+ assert_false(input.matches(":focus"), `${fromType} input should not match :focus`);
+ assert_false(input.matches(":focus-visible"), `${fromType} input should not match :focus-visible`);
+ assert_equals(input.type, toType, `${fromType} input should have changed to ${toType}`);
+ input.removeEventListener("focus", onFocus);
+ input.removeEventListener("blur", onBlur);
+ }, `${fromType} -> ${toType} ${capture}`);
+}
+
+for (let type of inputTypes) {
+ if (type == "hidden") {
+ continue; // hidden inputs are not focusable
+ }
+ for (let capture of [true, false]) {
+ test_from_to(type, "text", capture);
+ test_from_to("text", type, capture);
+ }
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change.html
new file mode 100644
index 0000000000..982cda6c92
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/focus-dynamic-type-change.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Input type switch on focused input shouldn't blur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script type=module>
+import inputTypes from "./input-types.js";
+
+function tick() {
+ return new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+}
+
+function test_from_to(fromType, toType) {
+ if (fromType == toType) {
+ return;
+ }
+ promise_test(async function(t) {
+ const input = document.createElement("input");
+ input.type = fromType;
+ document.body.appendChild(input);
+ input.focus();
+ assert_equals(document.activeElement, input, `${fromType} input should be focused`);
+ function onFocus() {
+ t.assert_unreached("shouldn't be getting spurious focus events");
+ }
+ function onBlur() {
+ t.assert_unreached("shouldn't be getting spurious blur events");
+ }
+ input.addEventListener("focus", onFocus);
+ input.addEventListener("blur", onBlur);
+ input.type = toType;
+ assert_equals(document.activeElement, input, `${fromType} input should be focused after change to ${toType}`);
+ assert_true(input.matches(":focus"), `${fromType} input should still match :focus`);
+ assert_true(input.matches(":focus-visible"), `${fromType} input should still match :focus-visible`);
+ await tick();
+ assert_equals(document.activeElement, input, `${fromType} input should still be focused after change to ${toType}`);
+ assert_true(input.matches(":focus"), `${fromType} input should still match :focus`);
+ assert_true(input.matches(":focus-visible"), `${fromType} input should still match :focus-visible`);
+ input.removeEventListener("focus", onFocus);
+ input.removeEventListener("blur", onBlur);
+ }, `${fromType} -> ${toType}`);
+}
+
+for (let type of inputTypes) {
+ if (type == "hidden") {
+ continue; // hidden inputs are not focusable
+ }
+ test_from_to(type, "text");
+ test_from_to("text", type);
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive-child.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive-child.html
new file mode 100644
index 0000000000..92c9981a11
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive-child.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script>
+parent.postMessage(location.href, "*");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive.html
new file mode 100644
index 0000000000..537500c91f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden-charset-case-sensitive.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#hidden-state-(type=hidden):attr-fe-name-charset">
+<meta name="assert" content="special input@name value “_charset_†is case-sensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form target="child" method="GET" action="hidden-charset-case-sensitive-child.html">
+ <input type="hidden" name="_charset_">
+ <input type="hidden" name="_CHARSET_">
+ <input type="hidden" name="_ChArSeT_">
+ <input type="hidden" name="_charſet_">
+</form>
+<iframe name="child"></iframe>
+<script>
+// #attr-fe-name-charset only affects form submission, so we need to do that
+async_test(function() {
+ // we use a message rather than the iframe’s load event to avoid dealing with
+ // spurious load events that some browsers dispatch on the initial about:blank
+ addEventListener("message", this.step_func_done(event => {
+ const params = new URL(event.data).searchParams;
+
+ assert_equals(params.get("_charset_"), "UTF-8", "lowercase valid");
+ assert_equals(params.get("_CHARSET_"), "UTF-8", "uppercase valid");
+ assert_equals(params.get("_ChArSeT_"), "UTF-8", "mixed case invalid");
+ assert_equals(params.get("_charſet_"), "", "non-ASCII invalid");
+ }));
+
+ document.querySelector("form").submit();
+}, "keyword _charset_");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden.html
new file mode 100644
index 0000000000..9274b5cddb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/hidden.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Hidden input element</title>
+ <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#hidden-state-(type=hidden)">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Hidden input element</h1>
+ <div style="display: none">
+
+ <input id="hidden" type="hidden" />
+ <input id="hidden_with_value" type="hidden" value="foo" />
+
+ </div>
+ <div id="log"></div>
+ <script type="text/javascript">
+
+ test(
+ function() {
+ assert_equals(document.getElementById("hidden").value, "");
+ assert_equals(document.getElementById("hidden_with_value").value, "foo");
+ }, "Value returns the current value for hidden");
+
+ test(
+ function() {
+ document.getElementById("hidden").value = "A";
+ assert_equals(document.getElementById("hidden").value, "A");
+ document.getElementById("hidden").value = "B";
+ assert_equals(document.getElementById("hidden").value, "B");
+ }, "Setting value changes the current value for hidden");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("hidden").files, null);
+ }, "files attribute must return null for hidden");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("hidden").valueAsDate, null);
+ }, "valueAsDate attribute must return null for hidden");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("hidden").valueAsNumber, NaN);
+ }, "valueAsNumber attribute must return NaN for hidden");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("hidden").list, null);
+ }, "list attribute must return null for hidden");
+
+ test(
+ function() {
+ var el = document.getElementById("hidden");
+ assert_throws_dom("InvalidStateError", function() { el.stepDown(); }, "");
+ }, "stepDown does not apply for hidden");
+
+ test(
+ function() {
+ var el = document.getElementById("hidden");
+ assert_throws_dom("InvalidStateError", function() { el.stepUp(); }, "");
+ }, "stepUp does not apply for hidden");
+
+ test(function(){
+ var el = document.getElementById("hidden");
+ assert_false(el.willValidate);
+ }, "input type=hidden is barred from constraint validation");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/image-click-form-data.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/image-click-form-data.html
new file mode 100644
index 0000000000..87b77e4805
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/image-click-form-data.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Check form-data for image submit button with non-empty 'value' attribute</title>
+<link rel="author" title="Shanmuga Pandi" href="mailto:shanmuga.m@samsung.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+// promise_test instead of async_test because this test use window.success, and so can't run at the same time.
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/image-submit-click.html?name.x=0&name.y=0", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/image-submit-click.html";
+ document.body.appendChild(iframe);
+ });
+}, "Image submit button should not add extra form data if 'value' attribute is present with non-empty value");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/image01-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/image01-ref.html
new file mode 100644
index 0000000000..62c141d960
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/image01-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>input type image reference file</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<img src="/media/poster.png"/>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/image01.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/image01.html
new file mode 100644
index 0000000000..e9028dceec
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/image01.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>input type image</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#image-button-state-(type=image)">
+<link rel="match" href="image01-ref.html">
+<input type=image id=image src="/media/poster.png">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-checkvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-checkvalidity.html
new file mode 100644
index 0000000000..b336204fcc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-checkvalidity.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_checkValidity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type='hidden' id='input_text'></p>
+ </form>
+ <script>
+
+ var input = document.getElementById("input_text");
+
+ try
+ {
+ var ret = input.checkValidity();
+
+ test(function() {
+ assert_equals(ret, true, "calling of checkValidity method is failed.");
+ });
+ }
+ catch (e) {
+ test(function() {
+ assert_unreached("Error is raised.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-disabled-fieldset-dynamic.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-disabled-fieldset-dynamic.html
new file mode 100644
index 0000000000..eefcd972bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-disabled-fieldset-dynamic.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1861027">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-actions.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<fieldset id="fieldset" disabled>
+ <input id="target">
+</fieldset>
+<script>
+const target = document.getElementById("target");
+const fieldset = document.getElementById("fieldset");
+promise_test(async function() {
+ await new Promise(r => window.addEventListener("load", r, { once: true }));
+ assert_true(target.matches(":disabled"), "Fieldset disables the input");
+ assert_true(target.matches(":read-only"), "Disabled implies read-only");
+
+ // Try to focus, it shouldn't be focusable.
+ target.focus();
+
+ assert_not_equals(document.activeElement, target, "Should not be focusable");
+
+ fieldset.removeAttribute("disabled");
+
+ assert_false(target.matches(":disabled"), "Should go back to writable");
+ assert_false(target.matches(":read-only"), "No longer read-only");
+
+ // Should be focusable now.
+ target.focus();
+
+ assert_equals(document.activeElement, target, "Should not be focusable");
+
+ await test_driver.send_keys(target, "A");
+ assert_equals(target.value, "A", "Typing should work");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-form-detach-style-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-form-detach-style-crash.html
new file mode 100644
index 0000000000..5472563763
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-form-detach-style-crash.html
@@ -0,0 +1,17 @@
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1800543">
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+ b.setCustomValidity("x")
+ a.reportValidity()
+ c.appendChild(a)
+ document.adoptNode(d)
+ document.documentElement.style.display = 'none'
+})
+</script>
+<form id="a">
+<textarea id="b">a</textarea>
+</form>
+<dl id="d">
+<canvas id="c"></canvas>
+</dl>
+<input form="a">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-height.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-height.html
new file mode 100644
index 0000000000..dea4f41765
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-height.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_height</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <p><input type='image' id='input_text'></p>
+ </form>
+
+ <script>
+
+ var input_text = document.getElementById("input_text");
+ input_text.height = 30;
+
+ if (typeof(input_text.height) == "number") {
+ test(function() {
+ assert_equals(input_text.height, 30, "formTarget attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("height attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-importNode-to-detached-document-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-importNode-to-detached-document-crash.html
new file mode 100644
index 0000000000..5e0cff4fa3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-importNode-to-detached-document-crash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<body>
+<p>This test passes if it does not crash.</p>
+<input id="input" type="image" src="data:image/gif;base64,">
+<iframe id="iframe"></iframe>
+<script>
+let i_doc = iframe.contentDocument;
+iframe.remove();
+i_doc.importNode(input);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-labels.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-labels.html
new file mode 100644
index 0000000000..77f4d8b31a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-labels.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_labels</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><label>Full name:<label>(name)<input name=fn id='input_text1'> <small>Format: First Last</small></label></label></p>
+ <p><label>Age: <input name=age type=number min=0 id='input_text2'></label></p>
+ <p><label>Post code: <input name=pc> <small>Format: AB12 3CD</small></label></p>
+ </form>
+ <script>
+
+ var input1 = document.getElementById("input_text1");
+ var input2 = document.getElementById("input_text2");
+
+ if (typeof(input1.labels) == "object") {
+ if (input1.labels.length == 2 && input2.labels.length == 1) {
+ test(function() {
+ assert_true(true, "labels attribute is correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("labels attribute is not correct.");
+ });
+ }
+ } else {
+ test(function() {
+ assert_unreached("labels attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-list.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-list.html
new file mode 100644
index 0000000000..006a8fbd8f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-list.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>input list attribute</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_list</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <datalist id="thelist">
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </datalist>
+
+ <p id="non_datalist_first">
+ <datalist id="non_datalist_first">
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </datalist>
+
+ <datalist id="datalist_first">
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </datalist>
+ <p id="datalist_first">
+
+ <p><input list="thelist" id='input_with_list'></p>
+ <p><input id='input_without_list'></p>
+ <p><input list="input_with_list" id='input_with_nondatalist_list'></p>
+ <p><input list="not_an_id" id='input_with_missing_list'></p>
+ <p><input list="non_datalist_first" id='input_with_non_datalist_first'></p>
+ <p><input list="datalist_first" id='input_with_datalist_first'></p>
+ </form>
+
+ <script>
+ test(function() {
+ assert_equals(document.getElementById("input_with_list").list, document.getElementById("thelist"));
+ }, "getting .list of input must return the datalist with that id");
+ test(function() {
+ assert_equals(document.getElementById("input_without_list").list, null);
+ }, "getting .list of input must return null if it has no list attribute");
+ test(function() {
+ assert_equals(document.getElementById("input_with_nondatalist_list").list, null);
+ }, "getting .list of input must return null if the list attribute is a non-datalist's id");
+ test(function() {
+ assert_equals(document.getElementById("input_with_missing_list").list, null);
+ }, "getting .list of input must return null if the list attribute is no element's id");
+ test(function() {
+ assert_equals(document.getElementById("input_with_non_datalist_first").list, null);
+ }, "getting .list of input must return null if the list attribute is used in a non-datalist earlier than a datalist");
+ test(function() {
+ assert_equals(document.getElementById("input_with_datalist_first").list, document.querySelector("datalist#datalist_first"));
+ }, "getting .list of input must return the datalist with that id even if a later non-datalist also has the id");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html
new file mode 100644
index 0000000000..d94bee1029
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<html>
+ <head>
+ <title>HTMLInputElement leading zeroes in seconds/millis</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h3>input times and datetimes with leading zeroes in seconds/millis</h3>
+ <!-- This test ensures that seconds and milliseconds are being
+ output with the appropriate field widths when sanitizing
+ datetime-locals and times, e.g. that we don't see "12:30:1".
+ The spec is not specific about how much precision to use
+ in a sanitized time string, but an invalid string would
+ fail at .valueAsNumber -->
+ <hr>
+ <div id="log"></div>
+
+ <input id="inp">
+ <script>
+ var inp=document.getElementById("inp");
+ var cases = [
+ ["datetime-local", "2000-01-01T12:30:01", 946729801000],
+ ["datetime-local", "2000-01-01T12:30:00.5", 946729800500],
+ ["datetime-local", "2000-01-01T12:30:00.04", 946729800040],
+ ["datetime-local", "2000-01-01T12:30:00.003", 946729800003],
+
+ ["time", "12:30:01", 45001000],
+ ["time", "12:30:00.5", 45000500],
+ ["time", "12:30:00.04", 45000040],
+ ["time", "12:30:00.003", 45000003],
+ ];
+
+ for (var i in cases) {
+ var c = cases[i];
+ test(function() {
+ inp.setAttribute("type", c[0]);
+ inp.value = c[1];
+ assert_equals(inp.valueAsNumber, c[2]);
+ },"Expected valueAsNumber=" +c[2] + " from " + c[1]);
+ if (c[0] == "datetime-local") {
+ test(function() {
+ inp.setAttribute("type", c[0]);
+ inp.value = c[1];
+ assert_in_array(inp.value, [c[1], c[1].replace("T", " ")]);
+ },"Expected digits unchanged in round-trip of " + c[1])
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-setcustomvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-setcustomvalidity.html
new file mode 100644
index 0000000000..accb24d8f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-setcustomvalidity.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<title>input setCustomValidity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<input id='input_test'>
+
+<script>
+
+test(() => {
+ let elem = document.getElementById("input_test");
+ assert_false(elem.validity.customError);
+ elem.setCustomValidity("custom error");
+ assert_true(elem.validity.customError);
+}, "input setCustomValidity is correct")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown-weekmonth.html
new file mode 100644
index 0000000000..c50f67fce5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown-weekmonth.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<title>Forms</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h3>input_stepDown</h3>
+<input type="month" id="month_input" min="2011-02" step="1" value="2010-02">
+<input type="week" id="week_input" min="2011-W02" step="1" value="2010-W02">
+
+<script>
+ function testStepDownOverflow(id, value, type) {
+ test(function() {
+ var input = document.getElementById(id);
+ input.stepDown();
+ assert_equals(input.value, value, "value shouldn't change.");
+ }, "Calling stepDown() on input - " + type + " - where value < min should not modify value.");
+ }
+
+ testStepDownOverflow("month_input", "2010-02", "month");
+ testStepDownOverflow("week_input", "2010-W02", "week");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown.html
new file mode 100644
index 0000000000..3cd246f015
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepdown.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<title>Forms</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h3>input_stepDown</h3>
+<input type='number' id='input_number'>
+<input type="number" id="number_input" min="300" step="1" value="200">
+<input type="date" id="date_input" min="2011-02-10" step="1" value="2010-02-10">
+<input type="datetime-local" id="dtl_input" min="2011-02-10T20:13" step="1" value="2010-02-10T20:13">
+<input type="time" id="time_input" min="21:13" step="60" value="20:13">
+
+<script>
+ var input_number = document.getElementById("input_number");
+ input_number.max = "30";
+ input_number.step = "3";
+ input_number.value = "30";
+ input_number.stepDown(5);
+
+ if (typeof(input_number.stepDown) == "function") {
+ test(function() {
+ assert_equals(input_number.value, "15", "call of stepDown method is failed.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("stepDown attribute is not exist.");
+ });
+ }
+
+ function testStepDownOverflow(id, value, type) {
+ test(function() {
+ var input = document.getElementById(id);
+ input.stepDown();
+ assert_equals(input.value, value, "value shouldn't change.");
+ }, "Calling stepDown() on input - " + type + " - where value < min should not modify value.");
+ }
+
+ testStepDownOverflow("number_input", "200", "number");
+ testStepDownOverflow("date_input", "2010-02-10", "date");
+ testStepDownOverflow("dtl_input", "2010-02-10T20:13", "datetime-local");
+ testStepDownOverflow("time_input", "20:13", "time");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup-weekmonth.html
new file mode 100644
index 0000000000..09316b0854
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup-weekmonth.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<title>Forms</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h3>input_stepUp</h3>
+<input type="month" id="month_input" max="2009-02" step="1" value="2010-02">
+<input type="week" id="week_input" max="2009-W02" step="1" value="2010-W02">
+
+<script>
+ function testStepUpOverflow(id, value, type) {
+ test(function() {
+ var input = document.getElementById(id);
+ input.stepUp();
+ assert_equals(input.value, value, "value shouldn't change.");
+ }, "Calling stepUp() on input -" + type + "- where value > max should not modify value.");
+ }
+
+ testStepUpOverflow("month_input", "2010-02", "month");
+ testStepUpOverflow("week_input", "2010-W02", "week");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup.html
new file mode 100644
index 0000000000..f6f97eecbe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-stepup.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<title>Forms</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h3>input_stepUp</h3>
+<input type='number' id='input_number'> <br/>
+<input type="number" id="number_input" max="100" step="1" value="200">
+<input type="date" id="date_input" max="2009-02-10" step="1" value="2010-02-10">
+<input type="datetime-local" id="dtl_input" max="2009-02-10T20:13" step="1" value="2010-02-10T20:13">
+<input type="time" id="time_input" max="19:13" step="60" value="20:13">
+
+<script>
+
+ var input_number = document.getElementById("input_number");
+ input_number.max = "30";
+ input_number.step = "3";
+ input_number.value = "0";
+ input_number.stepUp(5);
+
+ if (typeof(input_number.stepUp) == "function") {
+ test(function() {
+ assert_equals(input_number.value, "15", "call of stepUp method is failed.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("stepUp attribute is not exist.");
+ });
+ }
+
+ function testStepUpOverflow(id, value, type) {
+ test(function() {
+ var input = document.getElementById(id);
+ input.stepUp();
+ assert_equals(input.value, value, "value shouldn't change.");
+ }, "Calling stepUp() on input -" + type + "- where value > max should not modify value.");
+ }
+
+ testStepUpOverflow("number_input", "200", "number");
+ testStepUpOverflow("date_input", "2010-02-10", "date");
+ testStepUpOverflow("dtl_input", "2010-02-10T20:13", "datetime-local");
+ testStepUpOverflow("time_input", "20:13", "time");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-submit-remove-jssubmit.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-submit-remove-jssubmit.html
new file mode 100644
index 0000000000..f992ff9ed5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-submit-remove-jssubmit.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe name="frame" id="frame"></iframe>
+<form id="form" target="frame" action="does_not_exist.html">
+ <input id="input" name="name" value="foo">
+ <input id="submitbutton" type="submit"></input>
+</form>
+
+<script>
+async_test(t => {
+ window.addEventListener('load', () => {
+ const frame = document.getElementById('frame');
+ frame.addEventListener('load', t.step_func_done(() => {
+ const expected = (new URL("does_not_exist.html?name=bar", location.href)).href;
+ assert_equals(frame.contentWindow.location.href, expected);
+ }));
+
+ const form = document.getElementById('form');
+ const input = document.getElementById('input');
+ const submitButton = document.getElementById('submitbutton');
+ submitButton.addEventListener('click', event => {
+ submitButton.remove();
+ form.submit();
+ input.value = "bar";
+ form.submit();
+ input.value = "baz";
+ });
+
+ submitButton.click();
+ });
+}, 'This test will pass if a form navigation successfully occurs when clicking a <input type=submit> element with a onclick event handler which removes the input and then calls form.submit().');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-button.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-button.html
new file mode 100644
index 0000000000..0f269355a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-button.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<head>
+<title>input type button</title>
+<link rel="author" title="Takeharu.Oshida" href="mailto:georgeosddev@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#button-state-(type=button)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="hide" style="display">
+ <input type="button"/>
+ <input type="button" value="BUTTON"/>
+ <form action="/" method="get" onsubmit="isSubmitted = true;return false;">
+ <input type="button" value="mutable"/>
+ </form>
+ <form action="/" method="get" onsubmit="isSubmitted = true;return false;">
+ <input type="button" value="immutable" disabled/>
+ </form>
+</div>
+<script>
+var isSubmitted = false;
+var buttons = document.getElementsByTagName("input");
+
+test(function() {
+ assert_equals(buttons[0].click(), undefined, "The input element represents a button with no default behavior");
+},"default behavior");
+
+test(function() {
+ assert_equals(buttons[0].value, "", "It must be the empty string");
+},"empty value attribute");
+
+test(function() {
+ document.getElementById("hide").style.display = "block";
+ assert_not_equals(buttons[0].offsetWidth, buttons[1].offsetWidth, "If the element has a value attribute, the button's label must be the value of that attribute");
+ document.getElementById("hide").style.display = "none";
+},"label value");
+
+test(function() {
+ isSubmitted = false;
+ buttons[2].click();
+ assert_equals(isSubmitted, false, "If the element is mutable, the element's activation behavior is to do nothing.");
+},"mutable element's activation behavior is to do nothing.");
+
+test(function() {
+ isSubmitted = false;
+ buttons[3].click()
+ assert_equals(isSubmitted, false, "If the element is immutable, the element has no activation behavior.");
+},"immutable element has no activation behavior.");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-empty-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-empty-crash.html
new file mode 100644
index 0000000000..6e44250ccb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-empty-crash.html
@@ -0,0 +1,8 @@
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+ a.type = "foo"
+ document.execCommand("insertHorizontalRule", false)
+ a.type = "text"
+})
+</script>
+<input id="a" type="color">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-value.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-value.html
new file mode 100644
index 0000000000..74aeef7cd5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-change-value.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Input type switch from / to color</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://html.spec.whatwg.org/#input-type-change">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1833477">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+function runTest(focus) {
+ let input = document.createElement("input");
+ input.type = "color";
+ document.body.appendChild(input);
+ if (focus) {
+ input.focus();
+ }
+ assert_equals(input.value, "#000000", "Invalid color should return a non-empty sanitized value");
+ input.type = "text";
+ assert_equals(input.value, "", "Value dirty flag should remain false");
+ input.type = "color";
+ input.value = "#ffffff";
+ assert_equals(input.value, "#ffffff", "Valid color is returned");
+ input.type = "text";
+ assert_equals(input.value, "#ffffff", "Value dirty flag should remain true");
+ if (focus) {
+ assert_equals(document.activeElement, input, "Focus is preserved");
+ }
+}
+test(() => runTest(false), "Without focus");
+test(() => runTest(true), "With focus");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox-switch.tentative.window.js b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox-switch.tentative.window.js
new file mode 100644
index 0000000000..6128a62a0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox-switch.tentative.window.js
@@ -0,0 +1,19 @@
+test(t => {
+ const input = document.createElement("input");
+ input.switch = true;
+
+ assert_true(input.hasAttribute("switch"));
+ assert_equals(input.getAttribute("switch"), "");
+ assert_equals(input.type, "text");
+}, "switch IDL attribute, setter");
+
+test(t => {
+ const container = document.createElement("div");
+ container.innerHTML = "<input type=checkbox switch>";
+ const input = container.firstChild;
+
+ assert_true(input.hasAttribute("switch"));
+ assert_equals(input.getAttribute("switch"), "");
+ assert_equals(input.type, "checkbox");
+ assert_true(input.switch);
+}, "switch IDL attribute, getter");
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox.html
new file mode 100644
index 0000000000..7dd2f26b12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-checkbox.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<head>
+<title>input type checkbox</title>
+<link rel="author" title="Gary Gao" href="mailto:angrytoast@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div style="display:none;">
+ <input id="checkbox_default" type="checkbox" width="20" />
+
+ <input id="checkbox_checked" type="checkbox" checked />
+
+ <input id="checkbox_indeterminate" type="checkbox" />
+
+ <input id="checkbox_default_value" type="checkbox" />
+</div>
+
+<div id="log"></div>
+
+<script>
+ var checkbox_default = document.getElementById('checkbox_default'),
+ checkbox_checked = document.getElementById('checkbox_checked'),
+ checkbox_indeterminate = document.getElementById('checkbox_indeterminate'),
+ checkbox_default_value = document.getElementById('checkbox_default_value');
+
+ test(function() {
+ assert_false(checkbox_default.checked);
+ }, "default checkbox has no checkedness state");
+
+ test(function() {
+ assert_true(checkbox_checked.checked);
+ }, "checkbox with initial state set to checked has checkedness state");
+
+ test(function() {
+ checkbox_default.checked = 'chicken'
+ assert_true(checkbox_default.checked);
+ }, "changing the checked attribute to a string sets the checkedness state");
+
+ test(function() {
+ assert_false(checkbox_indeterminate.indeterminate);
+ }, "a checkbox has an indeterminate state set to false onload");
+
+ test(function() {
+ checkbox_indeterminate.indeterminate = true,
+ assert_true(checkbox_indeterminate.indeterminate);
+ }, "on setting, a checkbox's indeterminate state must be set to the new value and returns the last value it was set to");
+
+ test(function() {
+ assert_equals(checkbox_default_value.value, 'on');
+ }, "default/on: on getting, if the element has a value attribute, it must return that attribute's value; otherwise, it must return the string 'on'");
+
+ test(function() {
+ checkbox_default_value.value = 'chicken'
+ assert_equals(checkbox_default_value.value, 'chicken');
+ }, "on getting, if the element has a value attribute, it must return that attribute's value");
+</script>
+
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-number-rtl-invalid-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-number-rtl-invalid-crash.html
new file mode 100644
index 0000000000..d749d1faad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-type-number-rtl-invalid-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1835437">
+<script>
+window.onload = () => {
+ a.stepDown(251)
+ document.execCommand("delete", false, null)
+}
+</script>
+<form lang="ar-SA">
+<input id="a" type="number" autofocus dir="auto">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-types.js b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-types.js
new file mode 100644
index 0000000000..4456751052
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-types.js
@@ -0,0 +1,24 @@
+export default [
+ "button",
+ "checkbox",
+ "color",
+ "date",
+ "datetime-local",
+ "email",
+ "file",
+ "hidden",
+ "image",
+ "month",
+ "number",
+ "password",
+ "radio",
+ "range",
+ "reset",
+ "search",
+ "submit",
+ "tel",
+ "text",
+ "time",
+ "url",
+ "week",
+];
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-untrusted-key-event.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-untrusted-key-event.html
new file mode 100644
index 0000000000..eb96b6fd95
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-untrusted-key-event.html
@@ -0,0 +1,225 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Forms</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<form id="input_form">
+ <fieldset>
+ <input type="radio" name="radio" value="1">
+ <input type="radio" name="radio" value="2">
+ </fieldset>
+</form>
+<script type="module">
+import inputTypes from "./input-types.js";
+
+const form = document.querySelector("form");
+form.addEventListener("submit", (e) => {
+ e.preventDefault();
+ assert_true(false, 'form should not be submitted');
+});
+
+const radioButton = document.querySelector("input[type=radio]");
+radioButton.addEventListener("click", function(e) {
+ assert_true(false, `input radio should not be clicked`);
+});
+radioButton.addEventListener("focus", function(e) {
+ assert_true(false, `input radio should not be focused on`);
+});
+radioButton.addEventListener("change", function(e) {
+ assert_true(false, `input radio should not be changed`);
+});
+radioButton.addEventListener("input", function(e) {
+ assert_true(false, `input radio should not have been inputted`);
+});
+
+// Create and append input elements
+for (const inputType of inputTypes) {
+ if (inputType == "radio") {
+ continue;
+ }
+
+ let input = document.createElement("input");
+ input.type = inputType;
+ form.appendChild(input);
+
+ input.addEventListener("click", function(e) {
+ assert_true(false, `input ${inputType} should not be clicked`);
+ });
+ input.addEventListener("focus", function(e) {
+ assert_true(false, `input ${inputType} should not be focused on`);
+ });
+ input.addEventListener("change", function(e) {
+ assert_true(false, `input ${inputType} should not be changed`);
+ });
+ input.addEventListener("input", function(e) {
+ assert_true(false, `input ${inputType} should not have been inputted`);
+ });
+}
+
+// Start tests
+for (const inputType of inputTypes) {
+ let input = document.querySelector(`input[type=${inputType}]`);
+
+ test(() => {
+ // keyCode: Enter
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: " ",
+ })
+ );
+
+ // keyCode: Tab
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 9,
+ })
+ );
+
+ // key: Tab
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: "Tab",
+ })
+ );
+
+ // keyCode: ArrowUp
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 38,
+ })
+ );
+
+ // key: ArrowUp
+ input.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: "ArrowUp",
+ })
+ );
+ }, `Dispatching untrusted keypress events to input ${inputType} should not cause submission, click, change, input, or focus events`);
+
+ test(() => {
+ // keyCode: Enter
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 13,
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: "Enter",
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 32,
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: " ",
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: " ",
+ })
+ );
+
+ // keyCode: Tab
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 9,
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 9,
+ })
+ );
+
+ // key: Tab
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: "Tab",
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: "Tab",
+ })
+ );
+
+ // keyCode: ArrowUp
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 38,
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 38,
+ })
+ );
+
+ // key: ArrowUp
+ input.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: "ArrowUp",
+ })
+ );
+ input.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: "ArrowUp",
+ })
+ );
+ }, `Dispatching untrusted keyup/keydown events to input ${inputType} should not cause submission, click, change, input, or focus events`);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-validationmessage.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-validationmessage.html
new file mode 100644
index 0000000000..775c06f06e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-validationmessage.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_validationMessage</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type='hidden' id='input_text'></p>
+ </form>
+ <script>
+
+ var input = document.getElementById("input_text");
+
+ if (typeof(input.validationMessage) == "string") {
+ test(function() {
+ assert_equals(input.validationMessage, "", "validationMessage attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("validationMessage attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-validity.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-validity.html
new file mode 100644
index 0000000000..719144d511
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-validity.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_validity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type='hidden' id='input_text'></p>
+ </form>
+ <script>
+
+ var input = document.getElementById("input_text");
+
+ if (typeof(input.validity) == "object") {
+ test(function() {
+ assert_equals(input.validity.valueMissing, false, "validity attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("validity attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-value-invalidstateerr.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-value-invalidstateerr.html
new file mode 100644
index 0000000000..78e6624e7c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-value-invalidstateerr.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_value_INVALID_STATE_ERR</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <p><input type='file' id='input_file'></p>
+ </form>
+
+ <script>
+
+ var input_file = document.getElementById("input_file");
+ try {
+ input_file.value = "val";
+ test(function() {
+ assert_unreached("INVALID_STATE_ERR error is not raised.");
+ });
+ } catch (e) {
+ test(function() {
+ assert_equals(e.code, e["INVALID_STATE_ERR"], "INVALID_STATE_ERR error is not raised.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-invalidstateerr.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-invalidstateerr.html
new file mode 100644
index 0000000000..bd49a15fc8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-invalidstateerr.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_valueAsDate_INVALID_STATE_ERR</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <p><input type='checkbox' id='input_checkbox'></p>
+ </form>
+
+ <script>
+ var input_checkbox = document.getElementById("input_checkbox");
+ try {
+ input_checkbox.valueAsDate = new Date('2011-11-01');
+ test(function() {
+ assert_reached("INVALID_STATE_ERR error is not raised.");
+ });
+ }
+ catch (e) {
+ test(function() {
+ assert_equals(e.code, e["INVALID_STATE_ERR"], "INVALID_STATE_ERR error is not raised.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-stepping.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-stepping.html
new file mode 100644
index 0000000000..0985611031
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate-stepping.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>valueAsDate stepping</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_valueAsDate_stepping</h3>
+ <!-- This test verifies that valueAsDate reads and writes Date values,
+ that those values step by the correct default step, and that the values
+ represent the correct times.
+ -->
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <p><input type='date' id='input_date'></p>
+ <p><input type='time' id='input_time'></p>
+ <p><input type='week' id='input_week'></p>
+ <p><input type='month' id='input_month'></p>
+ </form>
+
+ <script>
+ function test_stepping(inputType, stringValue, steppedString, baseMillis, stepAmount) {
+ test(function() {
+ // put date in, constructed from a UTC timestamp so the test doesn't
+ // vary by local timezone
+ input = document.getElementById("input_" + inputType);
+ input.valueAsDate = new Date(baseMillis)
+
+ // get string out (using startsWith here to allow for optional
+ // seconds and milliseconds)
+ var sanitizedStr = input.value;
+ assert_true(sanitizedStr.startsWith(stringValue),
+ "The input value [" + sanitizedStr + "] must resemble [" + stringValue + "]");
+
+ // get date out
+ var sanitized = input.valueAsDate;
+ assert_equals(sanitized.getTime(), baseMillis, "The input valueAsDate must represent the same time as the original Date.")
+
+ // step up, get new date out
+ input.stepUp()
+ var steppedDate = input.valueAsDate;
+ assert_equals(steppedDate.getTime(), baseMillis + stepAmount, "Stepping must be by the correct amount")
+
+ // get new string out
+ var steppedStrOut = input.value;
+ assert_true(steppedStrOut.startsWith(steppedString),
+ "The changed input value [" + steppedStrOut + "] must resemble ["+steppedString+"]");
+
+ // step back down, get first date out again
+ input.stepDown()
+ var backDown = input.valueAsDate;
+ assert_equals(backDown.getTime(), baseMillis, "Stepping back down must return the date to its original value");
+
+ }, inputType + " should step correctly");
+ }
+
+ var millis_per_day = 24 * 60 * 60 * 1000;
+
+ // jan 1 midnight, step 1 day to jan 2
+ test_stepping("date", "1970-01-01", "1970-01-02", 0, millis_per_day);
+
+ // jan 1 midnight, step 1 minute to 00:01:00
+ test_stepping("time", "00:00", "00:01", 0, 60 * 1000);
+
+ // jan 1 midnight, step 31 days to feb 1
+ test_stepping("month", "1970-01", "1970-02", 0, 31 * millis_per_day);
+
+ // monday jan 5 1970 midnight, step 7 days to jan 12
+ // (this has to start on a monday for stepping up and down to return)
+ test_stepping("week", "1970-W02", "1970-W03", 4 * millis_per_day, 7 * millis_per_day);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate.html
new file mode 100644
index 0000000000..894983add2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasdate.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<html>
+ <head>
+ <title>HTMLInputElement valueAsDate</title>
+ <link rel="author" title="pmdartus" href="mailto:dartus.pierremarie@gmail.com">
+ <link rel=help href="https://html.spec.whatwg.org/#dom-input-valueasdate">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h3>input_valueAsDate</h3>
+ <hr>
+ <div id="log"></div>
+
+ <input id="input_date" type="date" />
+ <input id="input_month" type="month" />
+ <input id="input_week" type="week" />
+ <input id="input_time" type="time" />
+
+ <script>
+ "use strict";
+
+ function testValueAsDateGetter(type, element, cases) {
+ for (const [actualValue, expectedValueAsDate] of cases) {
+ test(
+ () => {
+ element.value = actualValue;
+
+ const actualValueAsDate = element.valueAsDate;
+ if (actualValueAsDate instanceof Date) {
+ assert_equals(
+ actualValueAsDate.getTime(),
+ expectedValueAsDate.getTime(),
+ `valueAsDate returns an invalid date (actual: ${actualValueAsDate.toISOString()}, ` +
+ `expected: ${expectedValueAsDate.toISOString()})`
+ );
+ } else {
+ assert_equals(actualValueAsDate, expectedValueAsDate);
+ }
+ },
+ `valueAsDate getter on type ${type} (with value: ${JSON.stringify(actualValue)})`
+ );
+ }
+ }
+
+ function testValueAsDateSetter(type, element, cases) {
+ for (const [valueDateStr, expectedValue] of cases) {
+ test(() => {
+ element.valueAsDate = new Date(valueDateStr);
+ assert_equals(element.value, expectedValue);
+ }, `valueAsDate setter on type ${type} (new Date(${JSON.stringify(valueDateStr)}))`);
+ }
+ }
+
+ const dateInput = document.getElementById("input_date");
+ testValueAsDateGetter("date", dateInput, [
+ ["", null],
+ ["0000-12-10", null],
+ ["2019-00-12", null],
+ ["2019-12-00", null],
+ ["2019-13-10", null],
+ ["2019-02-29", null],
+ ["2019-12-10", new Date("2019-12-10T00:00:00.000Z")],
+ ["2016-02-29", new Date("2016-02-29T00:00:00.000Z")] // Leap year
+ ]);
+ testValueAsDateSetter("date", dateInput, [
+ ["2019-12-10T00:00:00.000Z", "2019-12-10"],
+ ["2016-02-29T00:00:00.000Z", "2016-02-29"] // Leap year
+ ]);
+
+ const monthInput = document.getElementById("input_month");
+ testValueAsDateGetter("month", monthInput, [
+ ["", null],
+ ["0000-12", null],
+ ["2019-00", null],
+ ["2019-12", new Date("2019-12-01T00:00:00.000Z")]
+ ]);
+ testValueAsDateSetter("month", monthInput, [["2019-12-01T00:00:00.000Z", "2019-12"]]);
+
+ const weekInput = document.getElementById("input_week");
+ testValueAsDateGetter("week", weekInput, [
+ ["", null],
+ ["0000-W50", null],
+ ["2019-W00", null],
+ ["2019-W60", null],
+ ["2019-W50", new Date("2019-12-09T00:00:00.000Z")]
+ ]);
+ testValueAsDateSetter("week", weekInput, [["2019-12-09T00:00:00.000Z", "2019-W50"]]);
+
+ const timeInput = document.getElementById("input_time");
+ testValueAsDateGetter("time", timeInput, [
+ ["", null],
+ ["24:00", null],
+ ["00:60", null],
+ ["00:00", new Date("1970-01-01T00:00:00.000Z")],
+ ["12:00", new Date("1970-01-01T12:00:00.000Z")],
+ ["23:59", new Date("1970-01-01T23:59:00.000Z")]
+ ]);
+ testValueAsDateSetter("time", timeInput, [
+ ["1970-01-01T00:00:00.000Z", "00:00"],
+ ["1970-01-01T12:00:00.000Z", "12:00"],
+ ["1970-01-01T23:59:00.000Z", "23:59"]
+ ]);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-invalidstateerr.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-invalidstateerr.html
new file mode 100644
index 0000000000..a3187ff3fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-invalidstateerr.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_valueAsNumber_INVALID_STATE_ERR</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <p><input type='checkbox' id='input_checkbox'></p>
+ </form>
+
+ <script>
+
+ var input_checkbox = document.getElementById("input_checkbox");
+ try {
+ input_checkbox.valueAsNumber = 5;
+ }
+ catch (e) {
+ test(function() {
+ assert_equals(e.code, e["INVALID_STATE_ERR"], "INVALID_STATE_ERR error is not raised.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-stepping.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-stepping.html
new file mode 100644
index 0000000000..c93c25b23f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber-stepping.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>valueAsNumber stepping</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_valueAsNumber_stepping</h3>
+ <!-- This test verifies that valueAsNumber reads and writes number values,
+ that those values step by the correct default step, and that the values
+ represent the correct milliseconds/months.
+ -->
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <p><input type='date' id='input_date'></p>
+ <p><input type='time' id='input_time'></p>
+ <p><input type='week' id='input_week'></p>
+ <p><input type='month' id='input_month'></p>
+ <p><input type='datetime-local' id='input_datetime-local'></p>
+ <p><input type='range' id='input_range'></p>
+ <p><input type='number' id='input_number'></p>
+ </form>
+
+ <script>
+ function test_stepping(inputType, stringValue, steppedString, baseNumber, stepAmount) {
+ test(function() {
+ // put number in
+ input = document.getElementById("input_" + inputType);
+ input.valueAsNumber = baseNumber
+
+ // get string out
+ // startsWith is here to allow for optional seconds and milliseconds.
+ // the replace("T", " ") fallback is for https://github.com/web-platform-tests/wpt/issues/20994
+ var sanitizedStr = input.value;
+ assert_true(sanitizedStr.startsWith(stringValue) || sanitizedStr.startsWith(stringValue.replace("T", " ")),
+ "The input value [" + sanitizedStr + "] must resemble [" + stringValue + "]");
+
+ // get number out
+ var sanitized = input.valueAsNumber;
+ assert_equals(sanitized, baseNumber, "The input valueAsNumber must equal the original number.")
+
+ // step up, get new date out
+ input.stepUp()
+ var steppedNumber = input.valueAsNumber;
+ assert_equals(steppedNumber, baseNumber + stepAmount, "Stepping must be by the correct amount")
+
+ // get new string out
+ var steppedStrOut = input.value;
+ assert_true(steppedStrOut.startsWith(steppedString) || steppedStrOut.startsWith(steppedString.replace("T", " ")),
+ "The changed input value [" + steppedStrOut + "] must resemble [" + steppedString + "]");
+
+ // step back down, get first date out again
+ input.stepDown()
+ var backDown = input.valueAsNumber;
+ assert_equals(backDown, baseNumber, "Stepping back down must return the number to its original value");
+
+ }, inputType + " should step correctly");
+ }
+
+ var millis_per_day = 24 * 60 * 60 * 1000;
+
+ // jan 1 midnight, step 1 day to jan 2
+ test_stepping("date", "1970-01-01", "1970-01-02", 0, millis_per_day);
+
+ // jan 1 midnight, step 1 minute to 00:01:00
+ test_stepping("time", "00:00", "00:01", 0, 60 * 1000);
+
+ // jan 1 midnight, step 1 month (not counting by milliseconds) to feb 1
+ test_stepping("month", "1970-01", "1970-02", 0, 1);
+
+ // monday jan 5 1970 midnight, step 7 days to jan 12
+ // (this has to start on a monday for stepping up and down to return)
+ test_stepping("week", "1970-W02", "1970-W03", 4 * millis_per_day, 7 * millis_per_day);
+
+ // jan 1 midnight, step 1 minute to 00:01:00
+ test_stepping("datetime-local", "1970-01-01T00:00", "1970-01-01T00:01", 0, 60 * 1000);
+
+ // numbers, for which the default step is 1
+ test_stepping("range", "22", "23", 22, 1);
+ test_stepping("number", "24", "25", 24, 1);
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber.html
new file mode 100644
index 0000000000..1af75eafa3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-valueasnumber.html
@@ -0,0 +1,151 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<html>
+ <head>
+ <title>HTMLInputElement valueAsNumber</title>
+ <link rel="author" title="pmdartus" href="mailto:dartus.pierremarie@gmail.com">
+ <link rel=help href="https://html.spec.whatwg.org/#dom-input-valueasnumber">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h3>input_valueAsNumber</h3>
+ <hr>
+ <div id="log"></div>
+
+ <input id="input_date" type="date" />
+ <input id="input_month" type="month" />
+ <input id="input_week" type="week" />
+ <input id="input_time" type="time" />
+ <input id="input_datetime-local" type="datetime-local" />
+ <input id="input_number" type="number" />
+ <input id="input_range" type="range" min="0" max="100" />
+
+ <script>
+ "use strict";
+
+ function testValueAsNumberGetter(type, element, cases) {
+ for (const [value, expectedValueAsNumber] of cases) {
+ test(
+ () => {
+ element.value = value;
+ assert_equals(element.valueAsNumber, expectedValueAsNumber);
+ },
+ `valueAsNumber getter on type ${type} (actual value: ${value}, ` +
+ `expected valueAsNumber: ${expectedValueAsNumber})`
+ );
+ }
+ }
+
+ function testValueAsNumberSetter(type, element, cases) {
+ for (const [valueAsNumber, expectedValue] of cases) {
+ test(
+ () => {
+ element.valueAsNumber = valueAsNumber;
+ assert_equals(element.value, expectedValue);
+ },
+ `valueAsNumber setter on type ${type} (actual valueAsNumber: ${valueAsNumber}, ` +
+ `expected value: ${expectedValue})`
+ );
+ }
+ }
+
+ const dateInput = document.getElementById("input_date");
+ testValueAsNumberGetter("date", dateInput, [
+ ["", NaN],
+ ["0000-12-10", NaN],
+ ["2019-00-12", NaN],
+ ["2019-12-00", NaN],
+ ["2019-13-10", NaN],
+ ["2019-02-29", NaN],
+ ["2019-12-10", 1575936000000],
+ ["2016-02-29", 1456704000000] // Leap year
+ ]);
+ testValueAsNumberSetter("date", dateInput, [
+ [0, "1970-01-01"],
+ [1575936000000, "2019-12-10"],
+ [1456704000000, "2016-02-29"] // Leap year
+ ]);
+
+ const monthInput = document.getElementById("input_month");
+ testValueAsNumberGetter("month", monthInput, [
+ ["", NaN],
+ ["0000-12", NaN],
+ ["2019-00", NaN],
+ ["2019-12", 599]
+ ]);
+ testValueAsNumberSetter("month", monthInput, [[599, "2019-12"]]);
+
+ const weekInput = document.getElementById("input_week");
+ testValueAsNumberGetter("week", weekInput, [
+ ["", NaN],
+ ["0000-W50", NaN],
+ ["2019-W00", NaN],
+ ["2019-W60", NaN],
+ ["2019-W50", 1575849600000]
+ ]);
+ testValueAsNumberSetter("week", weekInput, [
+ [0, "1970-W01"],
+ [1575849600000, "2019-W50"]
+ ]);
+
+ const timeInput = document.getElementById("input_time");
+ testValueAsNumberGetter("time", timeInput, [
+ ["", NaN],
+ ["24:00", NaN],
+ ["00:60", NaN],
+ ["00:00", 0],
+ ["12:00", 12 * 3600 * 1000],
+ ["23:59", ((23 * 3600) + (59 * 60)) * 1000]
+ ]);
+ testValueAsNumberSetter("time", timeInput, [
+ [0, "00:00"],
+ [12 * 3600 * 1000, "12:00"],
+ [((23 * 3600) + (59 * 60)) * 1000, "23:59"]
+ ]);
+
+ const dateTimeLocalInput = document.getElementById("input_datetime-local");
+ testValueAsNumberGetter("datetime-local", dateTimeLocalInput, [
+ ["", NaN],
+ ["2019-12-10T00:00", 1575936000000],
+ ["2019-12-10T12:00", 1575979200000]
+ ]);
+ testValueAsNumberSetter("datetime-local", dateTimeLocalInput, [
+ [1575936000000, "2019-12-10T00:00"],
+ [1575979200000, "2019-12-10T12:00"]
+ ]);
+
+ const numberInput = document.getElementById("input_number");
+ testValueAsNumberGetter("number", numberInput, [
+ ["", NaN],
+ ["123", 123],
+ ["123.456", 123.456],
+ ["1e3", 1000],
+ ["1e", NaN],
+ ["-123", -123]
+ ]);
+ testValueAsNumberSetter("number", numberInput, [
+ [123, "123"],
+ [123.456, "123.456"],
+ [1e3, "1000"],
+ [-123, "-123"]
+ ]);
+
+ const rangeInput = document.getElementById("input_range");
+ testValueAsNumberGetter("range", rangeInput, [
+ ["", 50],
+ ["0", 0],
+ ["50", 50],
+ ["100", 100],
+ ["-10", 0], // Realign to the min
+ ["110", 100] // Realign to the max
+ ]);
+ testValueAsNumberSetter("range", rangeInput, [
+ [0, "0"],
+ [50, "50"],
+ [100, "100"]
+ ]);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-whitespace.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-whitespace.html
new file mode 100644
index 0000000000..8c3c20e877
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-whitespace.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+ <head>
+ <title>Chrome whitespace bug</title>
+ <link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
+ <link rel="help" href="https://crbug.com/1309014">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <style>
+ [data-foo] { color: red; }
+ div input { color: inherit; }
+ </style>
+ </head>
+ <body>
+ <div id="container" data-foo="foo"><input id="input1"></input></div>
+ <script>
+ async_test(t => {
+ let container = document.getElementById('container');
+ let input = document.getElementById('input1');
+ input.onkeypress = function(e) {
+ container.removeAttribute('data-foo');
+ input.style.display = 'block';
+ };
+ test_driver.send_keys(input, "a b")
+ .then(t.step_func(() => {
+ assert_equals(input.value, "a b");
+ t.done();
+ }));
+ }, "whitespace should not be eaten after parent style recalculation");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-width.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-width.html
new file mode 100644
index 0000000000..5278ff77e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-width.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_width</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ name="input_form">
+ <p><input type='image' id='input_text'></p>
+ </form>
+
+ <script>
+
+ var input_text = document.getElementById("input_text");
+ input_text.width = 30;
+
+ if (typeof(input_text.width) == "number") {
+ test(function() {
+ assert_equals(input_text.width, 30, "width attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("width attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-willvalidate.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-willvalidate.html
new file mode 100644
index 0000000000..e4bcf2e11e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-willvalidate.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>input_willValidate</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type='hidden' id='input_text'></p>
+ </form>
+ <script>
+
+ var input = document.getElementById("input_text");
+
+ if (typeof(input.willValidate) == "boolean") {
+ test(function() {
+ assert_equals(input.willValidate, false, "willValidate attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("willValidate attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/invalid-datalist-options-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/invalid-datalist-options-crash.html
new file mode 100644
index 0000000000..7cdd55196c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/invalid-datalist-options-crash.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+<input max="0" list="ticks" type="range">
+<datalist id="ticks">
+ <option value="0"></option>
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/large-step-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/large-step-crash.html
new file mode 100644
index 0000000000..6c7d577546
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/large-step-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1310229">
+<script>
+const input = document.createElement('input');
+input.type = 'range';
+input.max = "06146014076123948948236985915694585937453938739248525313667193356954648912174625325457686181245605159230507050382951965923880139416566171456307667108838599671206701390275757535304375074544995161254818024615";
+input.step = "55244276720723476767813103100759083382064508394993167470137";
+input.stepUp();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-manual.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-manual.html
new file mode 100644
index 0000000000..fdf6c26441
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-manual.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title>input max length</title>
+ <link rel="author" title="Sam Gibson" href="mailto:sam@ifdown.net">
+ <link rel=help href="https://html.spec.whatwg.org/multipage/forms.html#the-maxlength-and-minlength-attributes">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <div id="log"></div>
+ <p>Type a letter anywhere into the input field (do not select any text, or otherwise manipulate the input)</p>
+ <input type=text maxlength=4 id=only-four value="inpu"></input>
+
+ <script>
+ var input;
+ setup(function() {
+ input = document.getElementById('only-four');
+ }, {explicit_done: true, explicit_timeout: true});
+
+
+ on_event(input, 'keyup', function(event) {
+ if ((event.keyCode >= 65 && event.keyCode <= 90) ||
+ (event.keyCode >= 97 && event.keyCode <= 122)) {
+ test(function() {
+ assert_equals(input.value, "inpu");
+ }, 'input content should limit to maxlength')
+
+ done();
+ }
+ });
+ </script>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-number.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-number.html
new file mode 100644
index 0000000000..1e1d9f694c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength-number.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>input type=number maxlength</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input type="number" maxlength="1">
+
+<script>
+ async_test(t => {
+ let elem = document.getElementsByTagName("input")[0];
+ test_driver.send_keys(elem, "1234")
+ .then(t.step_func(() => {
+ assert_equals(elem.value, "1234");
+ t.done();
+ }));
+ }, "maxlength doesn't apply to input type=number");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength.html
new file mode 100644
index 0000000000..da5d18d00a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/maxlength.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>input max length</title>
+ <link rel="author" title="Sam Gibson" href="mailto:sam@ifdown.net">
+ <link rel=help href="https://html.spec.whatwg.org/multipage/forms.html#the-maxlength-and-minlength-attributes">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Text input element</h1>
+
+ <div style="display: none">
+ <input id="none" />
+ <input id="negative" type="-5" />
+ <input id="non-numeric" type="not-a-number" />
+ <input id="assign-negative" />
+ <input id="assign-non-numeric" />
+ </div>
+
+ <div id="log"></div>
+
+ <script type="text/javascript">
+ test(
+ function() {
+ assert_equals(document.getElementById("none").maxLength, -1);
+ }, "Unset maxlength is -1");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("negative").maxLength, -1);
+ }, "Negative maxlength is always -1");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("non-numeric").maxLength, -1);
+ }, "Non-numeric maxlength is -1");
+
+ test(
+ function() {
+ assert_throws_dom("INDEX_SIZE_ERR", function() {
+ document.getElementById("assign-negative").maxLength = -5;
+ });
+ }, "Assigning negative integer throws IndexSizeError");
+
+ test(
+ function() {
+ document.getElementById("assign-non-numeric").maxLength = "not-a-number";
+ assert_equals(document.getElementById("assign-non-numeric").maxLength, 0);
+ }, "Assigning non-numeric to maxlength sets maxlength to 0");
+ </script>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/minlength.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/minlength.html
new file mode 100644
index 0000000000..6748e30eaf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/minlength.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>input min length</title>
+ <link rel="author" title="Taryn Hill" href="mailto:Phrohdoh@gmail.com">
+ <link rel=help href="https://html.spec.whatwg.org/multipage/forms.html#the-minlength-and-minlength-attributes">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Text input element</h1>
+
+ <div style="display: none">
+ <input id="none" />
+ <input id="negative" minlength=-5 />
+ <input id="non-numeric" minlength="not-a-number" />
+ <input id="assign-negative" />
+ <input id="assign-non-numeric" />
+ </div>
+
+ <div id="log"></div>
+
+ <script type="text/javascript">
+ test(
+ function() {
+ assert_equals(document.getElementById("none").minLength, -1);
+ }, "Unset minlength is -1");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("negative").minLength, -1);
+ }, "Negative minlength is always -1");
+
+ test(
+ function() {
+ assert_equals(document.getElementById("non-numeric").minLength, -1);
+ }, "Non-numeric minlength is -1");
+
+ test(
+ function() {
+ assert_throws_dom("INDEX_SIZE_ERR", function() {
+ document.getElementById("assign-negative").minLength = -5;
+ });
+ }, "Assigning negative integer throws IndexSizeError");
+
+ test(
+ function() {
+ document.getElementById("assign-non-numeric").minLength = "not-a-number";
+ assert_equals(document.getElementById("assign-non-numeric").minLength, 0);
+ }, "Assigning non-numeric to minlength sets minlength to 0");
+ </script>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/month.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/month.html
new file mode 100644
index 0000000000..99be9bca67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/month.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Inputs Month</title>
+ <link rel="author" title="Morishita Hiromitsu" href="mailto:hero@asterisk-works.jp">
+ <link rel="author" title="kaseijin" href="mailto:pcmkas@gmail.com">
+ <link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#months">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#month-state-(type=month)">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Inputs Month</h1>
+ <div style="display: none">
+ <input id="valid_value_1" type="month" value="20133-12" />
+ <input id="valid_value_2" type="month" value="2013-12" />
+ <input id="valid_value_3" type="month" value="0003-01" />
+ <input id="valid" type="month" value="2011-11" min="2011-01" max="2011-12" />
+ <input id="invalid_value" type="month" value="invalid-month" min="2011-01" max="2011-12"/>
+ <input id="value_can_be_empty_string" type="month" value="2013-06" />
+ <input id="invalid_value_with_two_digits_year" type="month" value="13-06" />
+ <input id="invalid_value_is_set" type="month" />
+ <input id="step_attribute_is_invalid_value" type="month" value="2013-06" step="invalid_step_value" />
+ <input id="invalid_month_too_high" type="month" value="2013-13" />
+ <input id="invalid_month_too_low" type="month" value="2013-00" />
+ <input id="invalid_year_all_zero" type="month" value="0000-10" />
+ <input id="invalid_month_with_one_number" type="month" value="2013-1" />
+ <input id="invalid_month_non_numerical" type="month" value="2013-abc" />
+ <input id="invalid_date_additional_tuples" type="month" value="2013-11-1-1" />
+ </div>
+
+ <div id="log"></div>
+
+ <script>
+ test(function() {
+ assert_equals(document.getElementById("valid_value_1").value, "20133-12")
+ }, "year can be more than four digits");
+
+ test(function() {
+ assert_equals(document.getElementById("valid_value_2").value, "2013-12")
+ }, "valid value test");
+
+ test(function() {
+ assert_equals(document.getElementById("valid_value_3").value, "0003-01")
+ }, "year can contain prefixes of zero, as long as there are at least four digits");
+
+ test(function() {
+ assert_equals(document.getElementById("valid").type, "month")
+ }, "month type support on input element");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_value").value, "")
+ }, "User agents must not allow the user to set the value to a non-empty string that is not a valid month string.");
+
+ test(function() {
+ document.getElementById("value_can_be_empty_string").value = "";
+ assert_equals(document.getElementById("value_can_be_empty_string").value, "")
+ }, "Month value can be empty string.");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_value_with_two_digits_year").value, "")
+ }, "When value attribute has two digits year value, the value,which is invalid, must return empty string.");
+
+ test(function() {
+ document.getElementById("invalid_value_is_set").value = "invalid value";
+ assert_equals(document.getElementById("invalid_value_is_set").value, "")
+ }, "When value is set with invalid value, the value must return empty string.");
+
+ test(function() {
+ document.getElementById("step_attribute_is_invalid_value").stepUp();
+ assert_equals(document.getElementById("step_attribute_is_invalid_value").value, "2013-07")
+ }, "When step attribute is given invalid value, it must ignore the invalid value and use defaul value instead.");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_month_too_high").value, "");
+ }, "Month should be <= 13: If the value of the element is not a valid month string, then set it to the empty string instead.");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_month_too_low").value, "");
+ }, "Month should be > 0: If the value of the element is not a valid month string, then set it to the empty string instead.>");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_year_all_zero").value, "");
+ }, "Year should be > 0: If the value of the element is not a valid year string, then set it to the empty string instead.>");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_month_with_one_number").value, "");
+ }, "Month should be two digits: If the value of the element is not a valid month string, then set it to the empty string instead.>");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_month_non_numerical").value, "");
+ }, "Month should be two digits not characters: If the value of the element is not a valid month string, then set it to the empty string instead.>");
+
+ test(function() {
+ assert_equals(document.getElementById("invalid_date_additional_tuples").value, "");
+ }, "Value should be two parts: If the value of the element is not a valid month string, then set it to the empty string instead.>");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-cr.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-cr.html
new file mode 100644
index 0000000000..06f07cbbd8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-cr.html
@@ -0,0 +1 @@
+<!doctype html> <html class="reftest-wait"> <meta charset="utf-8"> <title>input multiline placeholder (CR)</title> <link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#the-placeholder-attribute"> <meta name="assert" content="input element's placeholder strips newlines (CR)"> <link rel="match" href="multiline-placeholder-ref.html"> <input placeholder="this is a multiline placeholder"> <input placeholder="this is&#xd;a multiline&#xd;&#xd;placeholder"> <input id="dynamic"> <script> document.querySelector("#dynamic") .setAttribute("placeholder", "this is\ra multiline\r\rplaceholder"); document.documentElement.classList.remove("reftest-wait"); </script> </html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-crlf.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-crlf.html
new file mode 100644
index 0000000000..b4336e24d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-crlf.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>input multiline placeholder (CRLF)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#the-placeholder-attribute">
+<meta name="assert" content="input element's placeholder strips newlines (CRLF)">
+<link rel="match" href="multiline-placeholder-ref.html">
+<input placeholder="this is
+a multiline
+
+placeholder">
+<input placeholder="this is&#xd;&#xa;a multiline&#xd;&#xa;&#xd;&#xa;placeholder">
+<input id="dynamic">
+<script>
+ document.querySelector("#dynamic")
+ .setAttribute("placeholder", "this is\r\na multiline\r\n\r\nplaceholder");
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-ref.html
new file mode 100644
index 0000000000..2812f86e1e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset=utf-8>
+<input placeholder="this isa multilineplaceholder">
+<input placeholder="this isa multilineplaceholder">
+<input placeholder="this isa multilineplaceholder">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder.html
new file mode 100644
index 0000000000..4d2ec43c3f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/multiline-placeholder.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>input multiline placeholder</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#the-placeholder-attribute">
+<meta name="assert" content="input element's placeholder strips newlines">
+<link rel="match" href="multiline-placeholder-ref.html">
+<input placeholder="this is
+a multiline
+
+placeholder">
+<input placeholder="this is&#xa;a multiline&#xa;&#xa;placeholder">
+<input id="dynamic">
+<script>
+ document.querySelector("#dynamic")
+ .setAttribute("placeholder", "this is\na multiline\n\nplaceholder");
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/number-constraint-validation.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/number-constraint-validation.html
new file mode 100644
index 0000000000..959726bb50
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/number-constraint-validation.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Form input type=number constraint validation</title>
+<link rel="author" title="Adam Vandolder" href="mailto:avandolder@mozilla.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#number-state-(type=number)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="log"></div>
+<input type="number">
+<script>
+ const input = document.querySelector("input");
+ const invalidInputNumber = "1.e";
+ const invalidSetNumber = "1.";
+
+ promise_test(async () => {
+ await test_driver.click(input);
+ await test_driver.send_keys(input, invalidInputNumber);
+ assert_equals(input.value.length, 0);
+ assert_true(input.validity.badInput);
+ }, "Unparsable number user input triggers sanitization and causes badInput to be set.");
+
+ promise_test(async () => {
+ input.value = invalidInputNumber;
+ assert_equals(input.value.length, 0);
+ assert_false(input.validity.badInput);
+ }, "Setting .value to an unparsable number triggers sanitization but doesn't set badInput.");
+
+ promise_test(async () => {
+ await test_driver.click(input);
+ await test_driver.send_keys(input, invalidSetNumber);
+ assert_equals(input.value, "1");
+ assert_false(input.validity.badInput);
+ }, "Users inputting a parsable but invalid floating point number doesn't trigger sanitization and doesn't set badInput.");
+
+ promise_test(async () => {
+ input.value = invalidSetNumber;
+ assert_equals(input.value.length, 0);
+ assert_false(input.validity.badInput);
+ }, "Setting .value to a parsable but invalid floating point number triggers sanitization but doesn't set badInput.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/number-disabled.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/number-disabled.html
new file mode 100644
index 0000000000..11cb82fdda
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/number-disabled.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>disabled works properly for number inputs</title>
+<link rel="help" href="https://html.spec.whatwg.org/#enabling-and-disabling-form-controls:-the-disabled-attribute">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1461706">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input type="number" disabled>
+<input type="number" disabled style="-moz-appearance: textfield; -webkit-appearance: textfield">
+<script>
+ test(function() {
+ for (const element of Array.from(document.querySelectorAll('input'))) {
+ element.focus();
+ assert_true(element.disabled);
+ assert_equals(document.activeElement, document.body);
+ }
+ }, "disabled works on number input regardless of appearance");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/number.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/number.html
new file mode 100644
index 0000000000..7d93f20898
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/number.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Form input type=number</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#number-state-(type=number)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var numbers = [
+ {value: "", expected: "", testname: "empty value"},
+ {value: "11", expected: "11", testname: "value = 11"},
+ {value: "11.12", expected: "11.12", testname: "value = 11.12"},
+ {value: "-11111", expected: "-11111", testname: "value = -11111"},
+ {value: "-11111.123", expected: "-11111.123", testname: "value = -11111.123"},
+ {value: "1e2", expected: "1e2", testname: "value = 1e2"},
+ {value: "1E2", expected: "1E2", testname: "value = 1E2"},
+ {value: "1e+2", expected: "1e+2", testname: "value = 1e+2"},
+ {value: "1e-2", expected: "1e-2", testname: "value = 1e-2"},
+ {value: "1d+2", expected: "", testname: "value is not a valid floating-point number: 1d+2"},
+ {value: "foobar", expected: "", testname: "value not a valid floating-point number: random string"},
+ {value: "11", attributes: { min: "10" }, expected: "11", testname: "Value >= min attribute"},
+ {value: "9", attributes: { min: "10" }, expected: "9", testname: "Value < min attribute"},
+ {value: "19", attributes: { max: "20" }, expected: "19", testname: "Value <= max attribute"},
+ {value: "21", attributes: { max: "20" }, expected: "21", testname: "Value > max attribute"},
+ {value: ".1", expected: ".1", testname: "value with a leading '.'"},
+ {value: "1.", expected: "", testname: "value ending with '.'"},
+ {value: "-0", expected: "-0", testname: "value = -0"},
+ {value: "Infinity", expected: "", testname: " value = Infinity"},
+ {value: "-Infinity", expected: "", testname: "value = -Infinity"},
+ {value: "NaN", expected: "", testname: "value = NaN"},
+ {value: "9007199254740993", expected: "9007199254740993", testname: "value = 2^53+1"},
+ {value: "2e308", expected: "", testname: "value >= Number.MAX_VALUE"},
+ {value: "1e", expected: "", testname: "value = 1e"},
+ {value: "+1", expected: "", testname: "value = +1"},
+ {value: "+", expected: "", testname: "value = '+'"},
+ {value: "-", expected: "", testname: "value = '-'"},
+ {value: "\t1", expected: "", testname: "value with a leading tab"},
+ {value: "\n1", expected: "", testname: "value with a leading newline"},
+ {value: "\f1", expected: "", testname: "value with a leading form feed"},
+ {value: "\r1", expected: "", testname: "value with a leading carriage return"},
+ {value: " 1", expected: "", testname: "value with a leading space"},
+ {value: "1trailing junk", expected: "", testname: "value = 1trailing junk"}
+ ];
+ for (var i = 0; i < numbers.length; i++) {
+ var w = numbers[i];
+ test(function() {
+ var input = document.createElement("input");
+ input.type = "number";
+ input.value = w.value;
+ for(var attr in w.attributes) {
+ input[attr] = w.attributes[attr];
+ }
+ assert_equals(input.value, w.expected);
+ }, w.testname);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/password-delete-space.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/password-delete-space.html
new file mode 100644
index 0000000000..78b3c966b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/password-delete-space.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Backspace with trailing white space in password field</title>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1400844">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#password-state-%28type=password%29">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input id="target" type="password" value=" ">
+
+<script>
+promise_test(async () => {
+ target.focus();
+ target.selectionStart = 2;
+ await test_driver.send_keys(target, '\uE003');
+ assert_equals(target.value, " ");
+}, "Backspace with trailing white space in password field");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/password.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/password.html
new file mode 100644
index 0000000000..aac54aa1c7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/password.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Password input element</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#password-state-%28type=password%29">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div style="display: none">
+<input id="password" type="password" />
+<input id=password2 type=password value="password">
+<input id="password_with_value" type="password" value="foobar" />
+</div>
+<script type="text/javascript">
+ setup(function() {
+ window.password = document.getElementById("password");
+ });
+
+ test(function() {
+ assert_equals(password.value, "");
+ assert_equals(document.getElementById("password_with_value").value, "foobar");
+ }, "Value returns the current value for password");
+
+ test(function() {
+ password.value = "A";
+ assert_equals(password.value, "A");
+ assert_equals(password.getAttribute("value"), null);
+ password.value = "B";
+ assert_equals(password.value, "B");
+ assert_equals(password.getAttribute("value"), null);
+ }, "Setting value changes the current value for password, but not the value content attribute");
+
+ test(function() {
+ // Any LF (\n) must be stripped.
+ password.value = "\nAB";
+ assert_equals(password.value, "AB");
+ password.value = "A\nB";
+ assert_equals(password.value, "AB");
+ password.value = "AB\n";
+ assert_equals(password.value, "AB");
+
+ // Any CR (\r) must be stripped.
+ password.value = "\rAB";
+ assert_equals(password.value, "AB");
+ password.value = "A\rB";
+ assert_equals(password.value, "AB");
+ password.value = "AB\r";
+ assert_equals(password.value, "AB");
+
+ // Any combinations of LF CR must be stripped.
+ password.value = "\r\nAB";
+ assert_equals(password.value, "AB");
+ password.value = "A\r\nB";
+ assert_equals(password.value, "AB");
+ password.value = "AB\r\n";
+ assert_equals(password.value, "AB");
+ password.value = "\r\nA\n\rB\r\n";
+ assert_equals(password.value, "AB");
+ }, "Value sanitization algorithm should strip line breaks for password");
+
+ var pass = document.getElementById('password2');
+
+ test(function(){
+ assert_equals(pass.value, "password");
+ pass.value = " pass word ";
+ assert_equals(pass.value, " pass word ");
+ }, "sanitization algorithm doesn't strip leading and trailing whitespaces");
+
+ test(function(){
+ pass.value = "pass\u000Aword";
+ assert_equals(pass.value, "password");
+ pass.value = "\u000Apassword\u000A";
+ assert_equals(pass.value, "password");
+ pass.value = "pass\u000Dword";
+ assert_equals(pass.value, "password");
+ pass.value = "\u000Dpassword\u000D";
+ assert_equals(pass.value, "password");
+ }, "sanitization algorithm strips line breaks");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/pattern_attribute.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/pattern_attribute.html
new file mode 100644
index 0000000000..93cbd2caec
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/pattern_attribute.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>pattern attribute</title>
+<meta name=viewport content="width=device-width">
+<link rel="author" title="Fabrice Clari" href="mailto:f.clari@inno-group.com">
+<link rel="author" title="Dimitri Bocquet" href="mailto:Dimitri.Bocquet@mosquito-fp7.eu">
+<link rel="author" title="Mathias Bynens" href="https://mathiasbynens.be/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-input-pattern">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h1><code>pattern</code> attribute</h1>
+<div style="display: none">
+ <input pattern="[a-z]{3}" value="abcd" id="basic">
+
+ <input pattern="a.b" value="a&#x1D306;b" id="unicode-code-points">
+ <input pattern="\p{ASCII_Hex_Digit}+" value="c0ff33" id="unicode-property">
+
+ <input pattern="\p{RGI_Emoji}+" value="&#x1F618;&#x1F48B;" id="unicode-property-of-strings">
+ <input pattern="[\p{ASCII_Hex_Digit}--[Ff]]" value="0123456789abcdefABCDEF" id="set-difference">
+ <input pattern="[_\q{a|bc|def}]" value="q" id="string-literal">
+
+ <div class="breaking-changes-from-u-to-v">
+ <!-- Unescaped special characters in character classes. -->
+ <input pattern="[(]" value="foo">
+ <input pattern="[)]" value="foo">
+ <input pattern="[[]" value="foo">
+ <input pattern="[{]" value="foo">
+ <input pattern="[}]" value="foo">
+ <input pattern="[/]" value="foo">
+ <input pattern="[-]" value="foo">
+ <input pattern="[|]" value="foo">
+ <!-- Double punctuators in character classes. -->
+ <input pattern="[&&]" value="foo">
+ <input pattern="[!!]" value="foo">
+ <input pattern="[##]" value="foo">
+ <input pattern="[$$]" value="foo">
+ <input pattern="[%%]" value="foo">
+ <input pattern="[**]" value="foo">
+ <input pattern="[++]" value="foo">
+ <input pattern="[,,]" value="foo">
+ <input pattern="[..]" value="foo">
+ <input pattern="[::]" value="foo">
+ <input pattern="[;;]" value="foo">
+ <input pattern="[<<]" value="foo">
+ <input pattern="[==]" value="foo">
+ <input pattern="[>>]" value="foo">
+ <input pattern="[??]" value="foo">
+ <input pattern="[@@]" value="foo">
+ <input pattern="[``]" value="foo">
+ <input pattern="[~~]" value="foo">
+ <input pattern="[_^^]" value="foo">
+ </div>
+</div>
+<div id="log"></div>
+<script>
+ test(() => {
+ const input = document.querySelector("#basic");
+
+ assert_idl_attribute(input, "pattern");
+ assert_equals(input.pattern, "[a-z]{3}");
+
+ assert_inherits(input, "validity");
+ assert_false(input.validity.valid);
+ assert_true(input.validity.patternMismatch);
+
+ assert_true(input.matches(":invalid"));
+ }, "basic <input pattern> support");
+
+ test(() => {
+ const input = document.querySelector("#unicode-code-points");
+ assert_true(input.validity.valid);
+ assert_true(input.matches(":valid"));
+ assert_false(input.validity.patternMismatch);
+ }, "<input pattern> is Unicode code point-aware");
+
+ test(() => {
+ const input = document.querySelector("#unicode-property");
+ assert_true(input.validity.valid);
+ assert_true(input.matches(":valid"));
+ assert_false(input.validity.patternMismatch);
+ }, "<input pattern> supports Unicode property escape syntax");
+
+ test(() => {
+ const input = document.querySelector("#unicode-property-of-strings");
+ assert_true(input.validity.valid);
+ assert_true(input.matches(":valid"));
+ assert_false(input.validity.patternMismatch);
+ }, "<input pattern> supports Unicode property escape syntax for properties of strings");
+
+ test(() => {
+ const input = document.querySelector("#set-difference");
+ assert_false(input.validity.valid);
+ assert_false(input.matches(":valid"));
+ assert_true(input.validity.patternMismatch);
+ }, "<input pattern> supports set difference syntax");
+
+ test(() => {
+ const input = document.querySelector("#string-literal");
+ assert_false(input.validity.valid);
+ assert_true(input.validity.patternMismatch);
+ assert_true(input.matches(":invalid"));
+ }, "<input pattern> supports string literal syntax");
+
+ test(() => {
+ const inputs = document.querySelectorAll(".breaking-changes-from-u-to-v input");
+ // These examples are all written such that they’re all `:invalid`
+ // when using `u`, but would become `:valid` when using `v` because
+ // the pattern would error, in turn resulting in
+ // `validity.valid === true`.
+ for (const input of inputs) {
+ const html = input.outerHTML;
+ assert_true(input.validity.valid, `${html} should be valid`);
+ assert_true(input.matches(":valid"), `${html} should match \`:valid\``);
+ assert_false(input.validity.patternMismatch, `${html} should not trigger a pattern mismatch`);
+ }
+ }, "<input pattern> enables the RegExp v flag");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update-ref.html
new file mode 100644
index 0000000000..38da019539
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<input value="content">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update.html
new file mode 100644
index 0000000000..af0c0793ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/placeholder-update.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<link rel="match" href="placeholder-update-ref.html">
+<body>
+<div id="app"></div>
+<script>
+const rootElement = document.getElementById("app");
+const input = document.createElement("input");
+input.placeholder = "placeholder";
+input.value = "content";
+rootElement.appendChild(input);
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html
new file mode 100644
index 0000000000..60feb2b0ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html
@@ -0,0 +1,168 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta charset="utf-8">
+ <title>Test for validity of disconnected radio buttons</title>
+</head>
+
+<body>
+ <input type="radio" name="group" id="radio1" required />
+ <input type="radio" name="group" id="radio2" checked />
+ <form>
+ <input type="radio" name="group" id="radio3" required />
+ <input type="radio" name="group" id="radio4" checked />
+ </form>
+
+ <script>
+ test(() => {
+ const radio1 = document.getElementById("radio1");
+ const radio2 = document.getElementById("radio2");
+ assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
+
+ radio2.remove();
+ assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
+
+ radio1.checked = true;
+ radio2.required = true;
+ assert_false(radio2.validity.valueMissing, "Element should not suffer from value missing");
+
+ const radio3 = document.getElementById("radio3");
+ const radio4 = document.getElementById("radio4");
+ assert_false(radio3.validity.valueMissing, "Element should not suffer from value missing");
+
+ radio4.remove();
+ assert_true(radio3.validity.valueMissing, "Element should suffer from value missing");
+
+ radio3.checked = true;
+ assert_true(radio4.checked, "Element should remain checked");
+ assert_false(radio3.validity.valueMissing, "Element should not suffer from value missing");
+
+ document.querySelector("form").appendChild(radio2);
+ assert_false(radio3.checked, "Element should be unchecked");
+ assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
+ }, "Removed elements are moved into separate radio groups.");
+
+ test(() => {
+ const container = document.createElement("div");
+ const radio = document.createElement("input");
+ radio.type = "radio";
+ radio.name = "group";
+ radio.id = "radio5";
+ radio.required = true;
+ container.appendChild(radio);
+ assert_true(radio.validity.valueMissing, "Element should suffer from value missing");
+
+ const outerContainer = document.createElement("div");
+ const outerRadio = document.createElement("input");
+ outerRadio.type = "radio";
+ outerRadio.name = "group";
+ outerRadio.id = "radio6";
+ outerRadio.checked = true;
+ outerContainer.appendChild(outerRadio);
+ outerContainer.appendChild(container);
+ assert_false(radio.validity.valueMissing, "Element should not suffer from value missing");
+
+ container.remove();
+ assert_true(radio.validity.valueMissing, "Element should suffer from value missing");
+ }, "Disconnected radio buttons should be contained by their tree root.");
+
+ test(() => {
+ const radioParent = document.createElement("input");
+ radioParent.type = "radio";
+ radioParent.name = "group";
+ radioParent.id = "radio-parent";
+ radioParent.required = true;
+ assert_true(radioParent.validity.valueMissing, "Element should suffer from value missing");
+
+ const radioChild = document.createElement("input");
+ radioChild.type = "radio";
+ radioChild.name = "group";
+ radioChild.id = "radio-child";
+ radioChild.checked = true;
+ assert_false(radioChild.validity.valueMissing, "Element should not suffer from value missing");
+
+ radioParent.appendChild(radioChild);
+ assert_false(radioChild.validity.valueMissing, "Element should not suffer from value missing");
+ assert_false(radioParent.validity.valueMissing, "Element should not suffer from value missing");
+
+ radioParent.checked = true;
+ assert_false(radioChild.checked, "Element should no longer be checked");
+ }, "Disconnected radio buttons can serve as radio group containers.");
+
+ test(() => {
+ const shadowHost = document.createElement("div");
+ const root = shadowHost.attachShadow({mode: "open"});
+ const container = document.createElement("div");
+ container.appendChild(shadowHost);
+
+ const radio1 = document.createElement("input");
+ radio1.type = "radio";
+ radio1.name = "group";
+ radio1.required = true;
+ const radio2 = document.createElement("input");
+ radio2.type = "radio";
+ radio2.name = "group";
+ radio2.checked = true;
+ const radio3 = document.createElement("input");
+ radio3.type = "radio";
+ radio3.name = "group";
+ radio3.required = true;
+
+ root.appendChild(radio1);
+ container.appendChild(radio3);
+ assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
+ assert_true(radio3.validity.valueMissing, "Element should suffer from value missing");
+
+ root.appendChild(radio2);
+ assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
+ assert_true(radio3.validity.valueMissing, "Element should suffer from value missing");
+ }, "Shadow roots in disconnected trees can serve as radio group containers.");
+
+ test(() => {
+ const svgRoot = document.createElementNS("http://www.w3.org/2000/svg", "g")
+ const htmlContainer = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
+ svgRoot.appendChild(htmlContainer);
+
+ const radio1 = document.createElement("input");
+ radio1.type = "radio";
+ radio1.name = "group";
+ radio1.required = true;
+ const radio2 = document.createElement("input");
+ radio2.type = "radio";
+ radio2.name = "group";
+ radio2.checked = true;
+
+ htmlContainer.appendChild(radio1);
+ assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
+ htmlContainer.appendChild(radio2);
+ assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
+ radio1.checked = true;
+ assert_false(radio2.checked, "Element should no longer be checked");
+ }, "Non-HTML elements in disconnected trees can serve as radio group containers.");
+
+ test(() => {
+ const fragment = document.createDocumentFragment();
+
+ const radio1 = document.createElement("input");
+ radio1.type = "radio";
+ radio1.name = "group";
+ radio1.required = true;
+ const radio2 = document.createElement("input");
+ radio2.type = "radio";
+ radio2.name = "group";
+ radio2.checked = true;
+
+ fragment.appendChild(radio1);
+ assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
+ fragment.appendChild(radio2);
+ assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
+ radio1.checked = true;
+ assert_false(radio2.checked, "Element should no longer be checked");
+ }, "Disconnected document fragments can serve as radio group containers.");
+ </script>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-double-activate-pseudo.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-double-activate-pseudo.html
new file mode 100644
index 0000000000..287dc7d58e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-double-activate-pseudo.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!-- This behavior is not explicitly specified. -->
+
+<input type=radio id=radioinput>
+
+<script>
+ promise_test(async () => {
+ await test_driver.send_keys(radioinput, ' ');
+ await test_driver.send_keys(radioinput, ' ');
+ assert_equals(document.querySelector(':active'), null,
+ `If the radio doesn't have the :active pseudo selector, nothing else should either.`);
+ }, `<input type=radio> shouldn't have the :active pseudo element after pressing the spacebar twice.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-groupname-case.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-groupname-case.html
new file mode 100644
index 0000000000..3c54aca3e7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-groupname-case.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>radio group name case-sensitive</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#radio-button-group">
+<!-- See also: https://github.com/whatwg/html/issues/1666 -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<input id=r1 type="radio" name="sImPlE">
+<input id=r2 type="radio" name="simple">
+<input id=r3 type="radio" name="SIMPLE">
+
+<input id=r4 type="radio" name="paSSfield-killroyß">
+<input id=r5 type="radio" name="passfield-killroyß">
+<input id=r6 type="radio" name="PASSFIELD-KILLROYß">
+<input id=r7 type="radio" name="paſſfield-killroyß">
+<input id=r8 type="radio" name="passfield-&#x212a;illroyß">
+<input id=r9 type="radio" name="paßfield-killroyß">
+<input id=r10 type="radio" name="paẞfield-killroyß">
+<input id=r11 type="radio" name="passfield-killroyẞ">
+<input id=r12 type="radio" name="passï¬eld-killroyß">
+<input id=r13 type="radio" name="passfıeld-killroyß">
+<input id=r14 type="radio" name="passfİeld-killroyß">
+
+<input id=r15 type="radio" name="глупый">
+<input id=r16 type="radio" name="глупы&#x438;&#x306;">
+<input id=r17 type="radio" name="ГЛУПЫЙ">
+<input id=r18 type="radio" name="ГЛУПЫ&#x418;&#x306;">
+
+<input id=r19 type="radio" name="åωk">
+<input id=r20 type="radio" name="ÅΩK">
+<input id=r21 type="radio" name="&#x212b;ωk">
+<input id=r22 type="radio" name="Ã¥&#x2126;k">
+<input id=r23 type="radio" name="åω&#x212a;">
+
+<input id=r24 type="radio" name="blah1">
+<input id=r25 type="radio" name="blah&#x2460;">
+<input id=r26 type="radio" name="bl&#x24b6;h1">
+<input id=r27 type="radio" name="bl&#x24d0;h1">
+
+<input id=r28 type="radio" name="t&Eacute;dz5アパートFi">
+<input id=r29 type="radio" name="T&Eacute;DZ5アパートFi">
+<input id=r30 type="radio" name="T&eacute;&#x01F1;&#x2075;アパートFi">
+<input id=r31 type="radio" name="t&Eacute;dz5&#x3300;Fi">
+<input id=r32 type="radio" name="t&Eacute;dz5&#x30A2;&#x30CF;&#x309A;&#x30FC;&#x30C8;Fi">
+<input id=r34 type="radio" name="T&Eacute;DZâµã‚¢ãƒ‘ートFi">
+<input id=r35 type="radio" name="T&Eacute;DZ5アパートï¬">
+
+<input id=r36 type="radio" name="ΣΣ">
+<input id=r37 type="radio" name="σς">
+
+<script>
+"use strict";
+const notGroups = {
+ "sImPlE": ["r1" ,"r2", "r3"],
+ "paSSfield-killroyß": ["r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14"],
+ "глупый": ["r15", "r16", "r17", "r18"],
+ "åωk": ["r19", "r20", "r21", "r22", "r23"],
+ "blah1": ["r24", "r25", "r26", "r27"],
+ "tÉdz5アパートFi": ["r28", "r29", "r30", "r31", "r32", "r34", "r35"],
+ "ΣΣ": ["r36", "r37"]
+};
+
+for (let notGroupLabel of Object.keys(notGroups)) {
+ test(() => {
+ const ids = notGroups[notGroupLabel];
+ const radios = ids.map(id => document.getElementById(id));
+
+ for (let radio of radios) {
+ radio.checked = true;
+ }
+
+ for (let radio of radios) {
+ assert_true(radio.checked, `${radio.name} must be checked`);
+ }
+ }, `Among names like ${notGroupLabel}, everything must be checkable at the same time`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-input-cancel.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-input-cancel.html
new file mode 100644
index 0000000000..fc2796b041
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-input-cancel.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<title>Radio input cancel behavior reverts state</title>
+<link rel="author" title="jeffcarp" href="mailto:gcarpenterv@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/#radio-button-state-(type=radio)">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+test(() => {
+ const input = document.createElement("input");
+ input.type = "radio";
+ document.body.appendChild(input);
+ const events = [];
+
+ input.addEventListener("change", () => {
+ events.push("change");
+ });
+ input.addEventListener("click", e => {
+ // cancel click event
+ e.preventDefault();
+ events.push("click");
+ });
+ input.addEventListener("input", () => {
+ events.push("input");
+ });
+
+ assert_false(input.checked);
+
+ input.click();
+
+ assert_false(input.checked);
+
+ // only click event called
+ assert_array_equals(events, ["click"]);
+
+}, "radio input cancel behavior reverts state");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-keyboard-navigation-order.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-keyboard-navigation-order.html
new file mode 100644
index 0000000000..d019ca982c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-keyboard-navigation-order.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Radio button group keyboard navigation order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+<form id="inside">
+ <input type="radio" name="inside" id="inside1"/>
+ <input type="radio" name="inside" id="inside2"/>
+ <input type="radio" name="inside" id="inside3"/>
+</form>
+<form id="before"></form>
+<input type="radio" form="before" name="before" id="before1"/>
+<input type="radio" form="before" name="before" id="before2"/>
+<input type="radio" form="before" name="before" id="before3"/>
+<input type="radio" form="after" name="after" id="after1"/>
+<input type="radio" form="after" name="after" id="after2"/>
+<input type="radio" form="after" name="after" id="after3"/>
+<form id="after"></form>
+<input type="radio" name="mix" id="mix1"/>
+<form id="mix"><input type="radio" name="mix" id="mix2"/></form>
+<input type="radio" name="mix" id="mix3"/>
+<input type="radio" name="doc" id="doc1"/>
+<input type="radio" name="doc" id="doc2"/>
+<input type="radio" name="doc" id="doc3"/>
+<script>
+async function pressRight() {
+ return new test_driver.Actions()
+ .keyDown("\uE014")
+ .keyUp("\uE014")
+ .send();
+}
+
+promise_test(async () => {
+ for (const groupName of ["inside", "before", "after", "mix", "doc"]) {
+ const firstInGroup = document.querySelector(`input[name="${groupName}"]`);
+ const newInput = document.createElement("input");
+ newInput.id = groupName + "New";
+ newInput.type = "radio";
+ if (groupName != "doc") {
+ newInput.setAttribute("form", groupName);
+ }
+ newInput.name = groupName;
+ firstInGroup.after(newInput);
+ }
+
+ for (const formId of ["inside", "before", "after", "mix"]) {
+ document.forms[formId].elements[0].focus();
+ for (const radio of document.forms[formId].elements) {
+ assert_equals(radio, document.activeElement, `Navigated to next radio button in form '${formId}'`);
+ await pressRight();
+ }
+ }
+
+ const radios = document.querySelectorAll("input[name='doc']");
+ radios[0].focus();
+ for (const radio of radios) {
+ assert_equals(radio, document.activeElement, `Navigated to next radio button on document`);
+ await pressRight();
+ }
+}, "Radio button keyboard navigation should proceed in tree-order.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-morphed.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-morphed.html
new file mode 100644
index 0000000000..b7b8658948
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-morphed.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Morphed radio input</title>
+<link rel="author" title="Kagami Sascha Rosylight" href="mailto:krosylight@mozilla.com">
+<link rel="help" href="https://html.spec.whatwg.org/#radio-button-state-(type=radio)">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input id="radio" type="radio" name="name_7" checked>
+<input id="text" name="name_7" checked>
+<script>
+ "use strict";
+
+ test(() => {
+ text.type = 'radio';
+ assert_false(radio.checked);
+ }, "Setting type attribute must unset checkedness of other elements");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-multiple-selected.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-multiple-selected.html
new file mode 100644
index 0000000000..83d42032a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio-multiple-selected.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Multiple required input radio elements</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<form id='testForm'>
+ <input type=radio name=foo required checked>
+ <input type=radio name=foo required>
+ <input type=submit>
+</form>
+<script>
+ test(function() {
+ assert_true(document.getElementById('testForm').reportValidity());
+ }, "Form should be valid since one of the radio elements is checked");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html
new file mode 100644
index 0000000000..7f183f8367
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html
@@ -0,0 +1,351 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>input type radio</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<input type=radio name=group1 id=radio1>
+<input type=radio name=group1 id=radio2>
+
+<input type=radio name=groüp2 id=radio3>
+<input type=radio name=groüp2 id=radio4>
+
+<input type=radio id=radio5>
+<input type=radio id=radio6 disabled>
+
+<input type=radio name="group5" id=radio71 checked>
+<input type=radio name="group5" id=radio72>
+
+<input type=radio name=group3 id=radio8 checked>
+<input type=radio name=group3 id=radio9>
+<input type=radio name=group4 id=radio10>
+<input type=radio name=group4 id=radio11 checked>
+
+<form id="testform"></form>
+<input type=radio form=testform name=group6 id=radio12 checked>
+<input type=radio form=testform name=group6 id=radio13>
+<input type=radio form=testform name=group6 id=radio14>
+
+<script>
+ var radio1 = document.getElementById('radio1'),
+ radio2 = document.getElementById('radio2'),
+ radio3 = document.getElementById('radio3'),
+ radio4 = document.getElementById('radio4'),
+ radio5 = document.getElementById('radio5'),
+ radio6 = document.getElementById('radio6'),
+ radio71 = document.getElementById('radio71'),
+ radio72 = document.getElementById('radio72'),
+ radio8 = document.getElementById('radio8'),
+ radio9 = document.getElementById('radio9'),
+ radio10 = document.getElementById('radio10'),
+ radio11 = document.getElementById('radio11'),
+ radio12 = document.getElementById('radio12'),
+ radio13 = document.getElementById('radio13'),
+ radio14 = document.getElementById('radio14'),
+ testform = document.getElementById('testform'),
+ t1 = async_test("click on mutable radio fires click event, then input event, then change event"),
+ t3 = async_test("click on non-mutable radio doesn't fire the input event"),
+ t4 = async_test("click on non-mutable radio doesn't fire the change event"),
+ t5 = async_test("canceled activation steps on unchecked radio"),
+ input_fired = false,
+ change_fired = false;
+
+ test(function(){
+ assert_false(radio1.checked);
+ assert_false(radio2.checked);
+ radio1.checked = true;
+ assert_true(radio1.checked);
+ assert_false(radio2.checked);
+ radio2.checked = true;
+ assert_false(radio1.checked);
+ assert_true(radio2.checked);
+ }, "only one control of a radio button group can have its checkedness set to true");
+
+ test(function(){
+ assert_false(radio3.checked);
+ assert_false(radio4.checked);
+ radio3.checked = true;
+ assert_true(radio3.checked);
+ assert_false(radio4.checked);
+ radio4.checked = true;
+ assert_false(radio3.checked);
+ assert_true(radio4.checked);
+ }, "radio inputs with non-ASCII name attributes belong to the same radio button group");
+
+ test(function(){
+ assert_true(radio8.checked);
+ assert_false(radio9.checked);
+ assert_false(radio10.checked);
+ assert_true(radio11.checked);
+ radio9.name="group4";
+ radio9.checked = true;
+ assert_true(radio8.checked);
+ assert_true(radio9.checked);
+ assert_false(radio10.checked);
+ assert_false(radio11.checked);
+ }, "changing the name of a radio input element and setting its checkedness to true makes all the other elements' checkedness in the same radio button group be set to false");
+
+ test(function(){
+ radio12.remove();
+ assert_true(radio12.checked);
+ assert_false(radio13.checked);
+ assert_false(radio14.checked);
+ radio13.checked = true;
+ assert_true(radio13.checked);
+ assert_false(radio14.checked);
+ radio13.removeAttribute("form");
+ radio14.removeAttribute("form");
+ assert_true(radio13.checked);
+ assert_false(radio14.checked);
+ radio14.checked = true;
+ assert_false(radio13.checked);
+ assert_true(radio14.checked);
+ radio13.setAttribute("form", "testform");
+ radio14.setAttribute("form", "testform");
+ radio13.checked = true;
+ assert_true(radio13.checked);
+ assert_false(radio14.checked);
+ testform.remove();
+ assert_true(radio13.checked);
+ assert_false(radio14.checked);
+ }, "moving radio input element out of or into a form should still work as expected");
+
+ radio5.onclick = t1.step_func(function(e) {
+ click_fired = true;
+ assert_false(input_fired, "click event should fire before input event");
+ assert_false(change_fired, "click event should fire before change event");
+ assert_false(e.isTrusted, "click()-initiated click event shouldn't be trusted");
+ });
+
+ radio5.oninput = t1.step_func(function(e) {
+ input_fired = true;
+ assert_true(click_fired, "input event should fire after click event");
+ assert_false(change_fired, "input event should fire before change event");
+ assert_true(e.bubbles, "input event should bubble")
+ assert_true(e.isTrusted, "input event should be trusted");
+ assert_false(e.cancelable, "input event should not be cancelable");
+ });
+
+ radio5.onchange = t1.step_func(function(e) {
+ change_fired = true;
+ assert_true(click_fired, "change event should fire after click event");
+ assert_true(input_fired, "change event should fire after input event");
+ assert_true(e.bubbles, "change event should bubble")
+ assert_true(e.isTrusted, "change event should be trusted");
+ assert_false(e.cancelable, "change event should not be cancelable");
+ });
+
+ radio6.oninput= t3.step_func_done(function(e) {
+ assert_unreached("event input fired");
+ });
+
+ radio6.onchange = t4.step_func_done(function(e) {
+ assert_unreached("event change fired");
+ });
+
+ t1.step(function() {
+ radio5.click();
+ assert_true(input_fired);
+ t1.done();
+ });
+
+ t3.step(function(){
+ radio6.click();
+ t3.done();
+ t4.done();
+ });
+
+ radio72.onclick = t5.step_func_done(function(e){
+ assert_false(radio71.checked, "click on radio should uncheck other radio in same group");
+ assert_true(radio72.checked, "click on radio should check that radio");
+ e.preventDefault();
+ // The cancelation of the click doesn't have an effect until after all the click event handlers have been run.
+ assert_false(radio71.checked, "radio remains unchecked immediately after click event on other radio in same group is canceled");
+ assert_true(radio72.checked, "clicked radio remains checked immediately after click event is canceled");
+ });
+
+ t5.step(function(){
+ assert_true(radio71.checked, "initially checked radio should be checked");
+ assert_false(radio72.checked, "other radios in same group as initially-checked radio should be unchecked");
+ radio72.click();
+ // Now that the click event has been fully dispatched, its cancelation has taken effect.
+ assert_true(radio71.checked, "canceled click event on radio should leave the previously-checked radio checked");
+ assert_false(radio72.checked, "canceled click event on previously-unchecked radio should leave that radio unchecked");
+ });
+
+ test(() => {
+ const container = document.createElement('div');
+ container.innerHTML =
+ '<input type=radio name=n1><span><input type=radio name=n1 checked></span>' +
+ '<form><input type=radio name=n1 checked></form>';
+ const radios = container.querySelectorAll('input');
+ assert_false(radios[0].checked, 'Sanity check: The first radio should be unchecked');
+ assert_true(radios[1].checked, 'Sanity check: The second radio should be checked');
+ assert_true(radios[2].checked, 'Sanity check: The third radio should be checked');
+
+ radios[0].checked = true;
+ assert_true(radios[0].checked, 'The first radio should be checked after setting checked');
+ assert_false(radios[1].checked, 'The second radio should be unchecked after setting checked');
+ assert_true(radios[2].checked, 'The third radio should be checked after setting checked');
+
+ radios[1].required = true;
+ assert_false(radios[0].validity.valueMissing, 'The first radio should be valid');
+ assert_false(radios[1].validity.valueMissing, 'The second radio should be valid');
+ assert_false(radios[2].validity.valueMissing, 'The third radio should be valid');
+
+ radios[0].remove();
+ assert_false(radios[0].validity.valueMissing, 'The first radio should be valid because of no required');
+ assert_true(radios[1].validity.valueMissing, 'The second radio should be invalid***');
+ assert_false(radios[2].validity.valueMissing, 'The third radio should be valid');
+
+ radios[0].required = true;
+ radios[0].checked = false;
+ assert_true(radios[0].validity.valueMissing, 'The first radio should be invalid because of required');
+ }, 'Radio buttons in an orphan tree should make a group');
+
+ test(() => {
+ const container = document.createElement('div');
+ container.innerHTML =
+ '<form>' +
+ '<input type=radio name=group1 id=radio1 checked>' +
+ '<input type=radio name=group1 id=radio2>' +
+ '</form>' +
+ '<form>' +
+ '<input type=radio name=group1 id=radio3 checked>' +
+ '<input type=radio name=group1 id=radio4>' +
+ '</form>' +
+ '<input type=radio name=group1 id=radio5 checked>' +
+ '<input type=radio name=group1 id=radio6>';
+ const radio1 = container.querySelector('#radio1');
+ const radio2 = container.querySelector('#radio2');
+ const radio3 = container.querySelector('#radio3');
+ const radio4 = container.querySelector('#radio4');
+ const radio5 = container.querySelector('#radio5');
+ const radio6 = container.querySelector('#radio6');
+
+ // initial conditions
+ assert_true(radio1.checked, 'radio1 should be checked');
+ assert_false(radio2.checked, 'radio2 should be unchecked');
+ assert_true(radio3.checked, 'radio3 should be checked');
+ assert_false(radio4.checked, 'radio4 should be unchecked');
+ assert_true(radio5.checked, 'radio5 should be checked');
+ assert_false(radio6.checked, 'radio6 should be unchecked');
+
+ radio2.checked = true;
+ assert_false(radio1.checked, 'radio1 should be unchecked');
+ assert_true(radio2.checked, 'radio2 should be checked');
+ assert_true(radio3.checked, 'radio3 should remain checked');
+ assert_true(radio5.checked, 'radio5 should remain checked');
+
+ radio4.checked = true;
+ assert_false(radio1.checked, 'radio1 should remain unchecked');
+ assert_true(radio2.checked, 'radio2 should remain checked');
+ assert_false(radio3.checked, 'radio3 should be unchecked');
+ assert_true(radio4.checked, 'radio4 should be checked');
+ assert_true(radio5.checked, 'radio5 should remain checked');
+
+ radio6.checked = true;
+ assert_false(radio1.checked, 'radio1 should remain unchecked');
+ assert_true(radio2.checked, 'radio2 should remain checked');
+ assert_false(radio3.checked, 'radio3 should remain unchecked');
+ assert_true(radio4.checked, 'radio4 should remain checked');
+ assert_false(radio5.checked, 'radio5 should be unchecked');
+ assert_true(radio6.checked, 'radio6 should be checked');
+ }, "Radio buttons in different groups (because they have different form owners or no form owner) do not affect each other's checkedness");
+
+ test(() => {
+ const container = document.createElement('div');
+ container.innerHTML =
+ '<form>' +
+ '<input type=radio name=group1 id=radio1 checked>' +
+ '<input type=radio name=group1 id=radio2>' +
+ '<input type=radio name=group1 id=radio3>' +
+ '<input type=radio name=group1 id=radio4>' +
+ '</form>';
+ const radio1 = container.querySelector('#radio1');
+ const radio2 = container.querySelector('#radio2');
+ const radio3 = container.querySelector('#radio3');
+ const radio4 = container.querySelector('#radio4');
+
+ // initial conditions
+ assert_true(radio1.checked, 'radio1 should be checked');
+ assert_false(radio2.checked, 'radio2 should be unchecked');
+ assert_false(radio3.checked, 'radio3 should be unchecked');
+ assert_false(radio4.checked, 'radio4 should be unchecked');
+
+ radio3.remove();
+ radio4.remove();
+ radio3.checked = true;
+ radio4.checked = true;
+ assert_true(radio1.checked, 'radio1 should remain checked');
+ assert_false(radio2.checked, 'radio2 should remain unchecked');
+ assert_true(radio3.checked, 'radio3 should be checked');
+ assert_true(radio4.checked, 'radio4 should be checked');
+ }, "Radio buttons in different groups (because they are not in the same tree) do not affect each other's checkedness");
+
+ test(() => {
+ const container = document.createElement('div');
+ container.innerHTML =
+ '<form>' +
+ '<input type=radio name=group1 id=radio1 checked>' +
+ '<input type=radio name=group1 id=radio2>' +
+ '<input type=radio name=group2 id=radio3 checked>' +
+ '<input type=radio name=group2 id=radio4>' +
+ '<input type=radio name="" id=radio5 checked>' +
+ '<input type=radio name="" id=radio6>' +
+ '<input type=radio id=radio7 checked>' +
+ '<input type=radio id=radio8>' +
+ '</form>';
+ const radio1 = container.querySelector('#radio1');
+ const radio2 = container.querySelector('#radio2');
+ const radio3 = container.querySelector('#radio3');
+ const radio4 = container.querySelector('#radio4');
+ const radio5 = container.querySelector('#radio5');
+ const radio6 = container.querySelector('#radio6');
+ const radio7 = container.querySelector('#radio7');
+ const radio8 = container.querySelector('#radio8');
+
+ // initial conditions
+ assert_true(radio1.checked, 'radio1 should be checked');
+ assert_false(radio2.checked, 'radio2 should be unchecked');
+ assert_true(radio3.checked, 'radio3 should be checked');
+ assert_false(radio4.checked, 'radio4 should be unchecked');
+ assert_true(radio5.checked, 'radio5 should be checked');
+ assert_false(radio6.checked, 'radio6 should be unchecked');
+ assert_true(radio7.checked, 'radio7 should be checked');
+ assert_false(radio8.checked, 'radio8 should be unchecked');
+
+ radio2.checked = true;
+ assert_false(radio1.checked, 'radio1 should be unchecked');
+ assert_true(radio2.checked, 'radio2 should be checked');
+ assert_true(radio3.checked, 'radio3 should remain checked');
+ assert_false(radio4.checked, 'radio4 should remain unchecked');
+ assert_true(radio5.checked, 'radio5 should remain checked');
+ assert_false(radio6.checked, 'radio6 should remain unchecked');
+ assert_true(radio7.checked, 'radio7 should remain checked');
+ assert_false(radio8.checked, 'radio8 should remain unchecked');
+
+ radio6.checked = true;
+ assert_false(radio1.checked, 'radio1 should remain unchecked');
+ assert_true(radio2.checked, 'radio2 should remain checked');
+ assert_true(radio3.checked, 'radio3 should remain checked');
+ assert_false(radio4.checked, 'radio4 should remain unchecked');
+ assert_true(radio5.checked, 'radio5 should remain checked');
+ assert_true(radio6.checked, 'radio6 should be checked');
+ assert_true(radio7.checked, 'radio7 should remain checked');
+
+ radio8.checked = true;
+ assert_false(radio1.checked, 'radio1 should remain unchecked');
+ assert_true(radio2.checked, 'radio2 should remain checked');
+ assert_true(radio3.checked, 'radio3 should remain checked');
+ assert_false(radio4.checked, 'radio4 should remain unchecked');
+ assert_true(radio5.checked, 'radio5 should remain checked');
+ assert_true(radio6.checked, 'radio6 should remain checked');
+ assert_true(radio7.checked, 'radio7 should remain checked');
+ assert_true(radio8.checked, 'radio8 should be checked');
+
+ }, "Radio buttons in different groups (because they have different name attribute values, or no name attribute) do not affect each other's checkedness");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-2.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-2.html
new file mode 100644
index 0000000000..3277dfc07f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>range input Tests</title>
+<link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<input type="range" id="r00" min="0" max="100" step="20" value="40" style="display:none">
+<input type="range" id="r01" min="0" max="1" step=".1" value=".2" style="display:none">
+<input type="range" id="r02" style="display:none">
+<input type="range" id="r03" style="display:none">
+<input type="range" id="r04" style="display:none">
+
+<script>
+test(function rangeElementTest0() {
+ document.getElementById('r00').value = "";
+ assert_equals(document.getElementById('r00').type, "range");
+ assert_equals(document.getElementById('r00').value, "60");
+}, "range input value set to ''");
+
+test(function rangeElementTest1() {
+ document.getElementById('r01').value = .6;
+ assert_equals(document.getElementById('r01').type, "range");
+ assert_equals(document.getElementById('r01').value, "0.6");
+}, "range input value set to an integer");
+
+test(function rangeElementTest2() {
+ assert_equals(document.getElementById('r02').type, "range");
+ assert_equals(document.getElementById('r02').value, "50");
+}, "range input value equals 50");
+
+test(function rangeElementTest3() {
+ document.getElementById('r03').value = 200;
+ assert_equals(document.getElementById('r03').type, "range");
+ assert_equals(document.getElementById('r03').value, "100");
+}, "range input value equals 100");
+
+test(function rangeElementTest4() {
+ document.getElementById('r04').value = 2.1;
+ assert_equals(document.getElementById('r04').type, "range");
+ assert_equals(document.getElementById('r04').value, "2");
+}, "range input value equals 2");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size-ref.html
new file mode 100644
index 0000000000..48beaea3f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size-ref.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Reference: type=range intrinsic size</title>
+ <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1512066">
+ <style>
+html,body {
+ color:black; background-color:white; font:16px/1 monospace;
+}
+
+.flex {
+ display: inline-flex;
+ width: 0;
+ border: 1px solid;
+ justify-items:start;
+}
+.flex2 {
+ display: inline-flex;
+ border: 1px solid;
+ justify-items:start;
+}
+.grid {
+ display: inline-grid;
+ grid: auto / 0;
+ border: 1px solid;
+ justify-items:start;
+}
+.grid2 {
+ display: inline-grid;
+ border: 1px solid;
+ justify-items:start;
+}
+.ib {
+ display: inline-block;
+ width: 0;
+ border: 1px solid;
+ justify-items:start;
+}
+
+input {
+ width: max-content;
+ min-width: 0;
+}
+input.min {
+ min-width: min-content;
+}
+input.mbp0 {
+ margin-left: 0;
+ margin-right: 0;
+ padding: 0;
+ border: 0;
+}
+ </style>
+</head>
+<body>
+
+<div class="flex"><input type="range" class="min"></div><br>
+<div class="flex"><input type="range" style="width:0"></div><br>
+<div class="flex"><input type="range" class="min"></div><br>
+<div class="flex"><input type="range" class="min"></div><br>
+<div class="flex"><input type="range" class="min"></div><br>
+<br>
+
+<div class="flex2"><input type="range"></div>
+<div class="flex2" style="width:3px"><input type="range" style="width:3px" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
+<div class="flex2"><input type="range"></div>
+<div class="flex2"><input type="range"></div>
+<div class="flex2"><input type="range"></div>
+<div class="flex2"><input type="range"></div>
+<br>
+
+<div class="grid"><input type="range" style="width:0"></div><br>
+<div class="grid"><input type="range" style="width:0"></div><br>
+<div class="grid" style="justify-items:start"><input type="range"></div><br>
+
+<div class="grid2"><input type="range"></div>
+<div class="grid2"><input type="range" style="min-width:0"></div>
+<div class="grid2" style="width:3px"><input type="range" style="width:3px" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
+<div class="grid2" style="justify-items:start"><input type="range"></div>
+
+<br>
+
+<div class="ib"><input type="range"></div><br>
+<div class="ib"><input type="range"></div><br>
+
+<input type="range">
+<input type="range"
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size.html
new file mode 100644
index 0000000000..ce37faf89b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>Test: type=range intrinsic size</title>
+ <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1512066">
+ <link rel="match" href="range-intrinsic-size-ref.html">
+ <style>
+html,body {
+ color:black; background-color:white; font:16px/1 monospace;
+}
+
+.flex {
+ display: inline-flex;
+ width: 0;
+ border: 1px solid;
+}
+.flex2 {
+ display: inline-flex;
+ border: 1px solid;
+}
+.grid {
+ display: inline-grid;
+ grid: auto / 0;
+ border: 1px solid;
+}
+.grid2 {
+ display: inline-grid;
+ border: 1px solid;
+}
+.ib {
+ display: inline-block;
+ width: 0;
+ border: 1px solid;
+}
+input.mbp0 {
+ margin-left: 0;
+ margin-right: 0;
+ padding: 0;
+ border: 0;
+}
+ </style>
+</head>
+<body>
+
+<div class="flex"><input type="range"></div><br>
+<div class="flex"><input type="range" style="min-width:0"></div><br>
+<div class="flex" style="justify-items:start"><input type="range"></div><br>
+<div class="flex" style="-webkit-box-pack: start"><input type="range"></div><br>
+<div class="flex" style="-webkit-box-pack: start; justify-content: flex-start;"><input type="range"></div><br>
+<br>
+
+<div class="flex2"><input type="range"></div>
+<div class="flex2" style="width:3px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="flex2"><input type="range" style="min-width:0"></div>
+<div class="flex2" style="justify-items:start"><input type="range"></div>
+<div class="flex2" style="-webkit-box-pack: start"><input type="range"></div>
+<div class="flex2" style="-webkit-box-pack: start; justify-content: flex-start;"><input type="range"></div>
+<br>
+
+<div class="grid"><input type="range"></div><br>
+<div class="grid"><input type="range" style="min-width:0"></div><br>
+<div class="grid" style="justify-items:start"><input type="range"></div><br>
+
+<div class="grid2"><input type="range"></div>
+<div class="grid2"><input type="range" style="min-width:0"></div>
+<div class="grid2" style="width:3px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="grid2" style="width:30px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="grid2" style="grid:auto/30px"><input type="range" class="mbp0"></div>
+<div class="grid2" style="justify-items:start"><input type="range"></div>
+
+<br>
+
+<div class="ib"><input type="range"></div><br>
+<div class="ib"><input type="range" style="min-width:0"></div><br>
+
+<input type="range" style="width:min-content;">
+<input type="range" style="width:max-content;">
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-added-repaint.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-added-repaint.html
new file mode 100644
index 0000000000..344f36527b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-added-repaint.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>The range is repainted if a list ID is added</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1805105">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<script src=/common/reftest-wait.js></script>
+<input type=range step=3 value=1 min=-5 max=5>
+<datalist id=tickmarks>
+ <option value=4>
+ <option value=-2>
+</datalist>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ document.querySelector("input[type=range]").setAttribute("list", "tickmarks");
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-change-repaint.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-change-repaint.html
new file mode 100644
index 0000000000..b5bee03b3b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-change-repaint.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>The range is repainted if its list attribute changes</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1805105">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<script src=/common/reftest-wait.js></script>
+<input type=range step=3 value=1 min=-5 max=5 list=firstlist>
+<datalist id=firstlist>
+ <option value=1></option>
+ <option value=-5></option>
+</datalist>
+<datalist id=secondlist>
+ <option value=-2>
+ <option value=4>
+</datalist>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ const range = document.querySelector("input[type=range]");
+ range.setAttribute("list", "secondlist");
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-duplicate-id-repaint.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-duplicate-id-repaint.html
new file mode 100644
index 0000000000..0a2a90b500
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-duplicate-id-repaint.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>The range is repainted if the ID identifies a different list</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1805105">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<script src=/common/reftest-wait.js></script>
+<input type=range step=3 value=1 min=-5 max=5 list=firstlist>
+<datalist id=firstlist>
+ <option value=1></option>
+ <option value=-5></option>
+</datalist>
+<datalist id=secondlist>
+ <option value=4>
+ <option value=-2>
+</datalist>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ const firstList = document.querySelector("datalist#firstlist");
+ const secondList = document.querySelector("datalist#secondlist");
+ secondList.id = "firstlist";
+ firstList.parentNode.insertBefore(secondList, firstList);
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-nonexistent.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-nonexistent.html
new file mode 100644
index 0000000000..72fb19b9c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-list-nonexistent.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>The range is repainted if the ID first identifies no list, then a list takes on that ID</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1805105">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<script src=/common/reftest-wait.js></script>
+<input type=range step=3 value=1 min=-5 max=5 list=nonexistentlist>
+<datalist>
+ <option value=4>
+ <option value=-2>
+</datalist>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ const dataListWithIDOfNonExistentList = document.querySelector("datalist");
+ dataListWithIDOfNonExistentList.id = "nonexistentlist";
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-add-repaint.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-add-repaint.html
new file mode 100644
index 0000000000..31631b0c59
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-add-repaint.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>The range is repainted if an option is added to the range's list</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1805105">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<script src=/common/reftest-wait.js></script>
+<input type=range step=3 value=1 min=-5 max=5 list=tickmarks>
+<datalist id=tickmarks>
+ <option value=4></option>
+</datalist>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ const dataList = document.querySelector("datalist");
+ const toAdd = document.createElement("option");
+ toAdd.value = -2;
+ dataList.appendChild(toAdd);
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-remove-repaint.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-remove-repaint.html
new file mode 100644
index 0000000000..672e371bfb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-remove-repaint.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>The range is repainted if an option is removed from the range's list</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1805105">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<script src=/common/reftest-wait.js></script>
+<input type=range step=3 value=1 min=-5 max=5 list=tickmarks>
+<datalist id=tickmarks>
+ <option value=-2></option>
+ <option value=1 id=to-remove></option>
+ <option value=4></option>
+</datalist>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ document.querySelector("option#to-remove").remove();
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-value-change-repaint.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-value-change-repaint.html
new file mode 100644
index 0000000000..7dcace4e47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-option-value-change-repaint.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>The range is repainted if the value of an option in the range's list changes</title>
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1805105">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<script src=/common/reftest-wait.js></script>
+<input type=range step=3 value=1 min=-5 max=5 list=tickmarks>
+<datalist id=tickmarks>
+ <option value=-2></option>
+ <option value=1 id=to-change></option>
+</datalist>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ const toChange = document.querySelector("option#to-change");
+ toChange.value = 4;
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-restore-oninput-onchange-event.https.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-restore-oninput-onchange-event.https.html
new file mode 100644
index 0000000000..f73c5e6f63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-restore-oninput-onchange-event.https.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/7283">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1266468">
+
+<link rel=author href="mailto:gulukesh@gmail.com">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1131234">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+function runTest(type, testValue) {
+ promise_test(async () => {
+ const w = window.open(`resources/${type}-restore-events.html`);
+ // Unfortunately, navigating |w| doesn't fire load events in this parent
+ // window, so we have to make the child window manually tell this parent
+ // window when it has loaded.
+ await new Promise(resolve => window.loadResolver = resolve);
+ // We can't navigate the child window until after a setTimeout.
+ await new Promise(resolve => step_timeout(resolve, 0));
+
+ assert_not_equals(
+ w.document.querySelector('input').value,
+ testValue,
+ `Test shouldn't start with the new value already in the input.`);
+ w.document.querySelector('input').value = testValue;
+
+ w.location.href = 'resources/loadresolver.html';
+ await new Promise(resolve => window.loadResolver = resolve);
+
+ w.history.back();
+ await new Promise(resolve => window.loadResolver = resolve);
+ // The value doesn't get restored until after a setTimeout.
+ await new Promise(resolve => step_timeout(resolve, 0));
+
+ assert_equals(w.document.querySelector('input').value, testValue,
+ 'The input should have its value restored.');
+
+ assert_false(w.seeninput || false,
+ 'The input event should not have been fired after restoration.');
+ assert_false(w.seenchange || false,
+ 'The change event should not have been fired after restoration.');
+
+ w.close();
+ }, `Verifies that form restoration does not fire input or change events for <input type=${type}>.`);
+}
+
+runTest('range', '8');
+runTest('text', 'foo');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value-ref.html
new file mode 100644
index 0000000000..71a44cdf2f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>range input element setAttribute value appearance</title>
+
+<p>Test passes if the range element below visually has its slider at 2/10 from the left</p>
+
+<input type=range min=0 max=10 value=2></input>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value.html
new file mode 100644
index 0000000000..3a03a5b6fe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-setattribute-value.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#the-input-element">
+<link rel="match" href="range-setattribute-value-ref.html">
+<title>range input element setAttribute value appearance</title>
+
+<p>Test passes if the range element below visually has its slider at 2/10 from the left</p>
+
+<script>
+window.onload = () => {
+
+ const input = document.createElement('input');
+ input.type = 'range';
+ input.min = 0;
+ input.max = 10;
+ document.body.appendChild(input);
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ input.setAttribute('value', 2);
+ document.documentElement.classList.remove('reftest-wait');
+ });
+ });
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01-notref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01-notref.html
new file mode 100644
index 0000000000..58192bec8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01-notref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>LTR range input with datalist reference</title>
+<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01.html
new file mode 100644
index 0000000000..225422dc4d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-01.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>LTR range input with datalist</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=841942">
+<link rel="author" href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel="mismatch" href="range-tick-marks-01-notref.html">
+<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers">
+<datalist id="powers">
+ <option value="0">
+ <option value="-30">
+ <option value="30">
+ <option value="50">
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02-ref.html
new file mode 100644
index 0000000000..3d5b323470
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02-ref.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>RTL range input with datalist reference</title>
+<style>
+ input[type=range] {
+ transform: scaleX(-1);
+ }
+</style>
+<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers">
+<datalist id="powers">
+ <option value="0">
+ <option value="-30">
+ <option value="30">
+ <option value="50">
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02.html
new file mode 100644
index 0000000000..453e650b2f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-02.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>RTL range input with datalist</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#attr-dir-rtl">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=841942">
+<link rel="author" href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel="match" href="range-tick-marks-02-ref.html">
+<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers" dir="rtl">
+<datalist id="powers">
+ <option value="0">
+ <option value="-30">
+ <option value="30">
+ <option value="50">
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03-notref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03-notref.html
new file mode 100644
index 0000000000..5c15233a31
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03-notref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>max and min attributes applied to range input with datalist reference</title>
+<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers">
+<datalist id="powers">
+ <option value="0">
+ <option value="-30">
+ <option value="30">
+ <option value="50">
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03.html
new file mode 100644
index 0000000000..e067013bdd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-03.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>max and min attributes applied to range input with datalist</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#the-min-and-max-attributes">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=841942">
+<link rel="author" href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel="mismatch" href="range-tick-marks-03-notref.html">
+<input type="range" min="-40" max="40" value="0" step="10" name="power" list="powers">
+<datalist id="powers">
+ <option value="0">
+ <option value="-30">
+ <option value="30">
+ <option value="50">
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04-ref.html
new file mode 100644
index 0000000000..a6afaff7ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>no range tick marks for disabled datalist elements reference</title>
+<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers">
+<datalist id="powers">
+ <option value="-30">
+ <option value="50">
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04.html
new file mode 100644
index 0000000000..6fb0e930a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-04.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>no range tick marks for disabled datalist elements</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#htmldatalistelement">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-disabled">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=841942">
+<link rel="author" href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel="match" href="range-tick-marks-04-ref.html">
+<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers">
+<datalist id="powers">
+ <option value="0" disabled>
+ <option value="-30">
+ <option value="30" disabled>
+ <option value="50">
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05-ref.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05-ref.html
new file mode 100644
index 0000000000..f144af3880
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>no range tick marks for range tick marks that are step mismatches reference</title>
+<input type=range step=3 value=1 min=-5 max=5 list=degrees>
+<datalist id=degrees>
+ <option value=-2>
+ <option value=4>
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05.html
new file mode 100644
index 0000000000..a65c7b7946
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-tick-marks-05.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>no range tick marks for range tick marks that are step mismatches</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)">
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1803303">
+<link rel=author href="mailto:zach@zrhoffman.net" title="Zach Hoffman">
+<link rel=match href=range-tick-marks-05-ref.html>
+<input type=range step=3 value=1 min=-5 max=5 list=degrees>
+<datalist id=degrees>
+ <option value=-4>
+ <option value=-2>
+ <option value=0>
+ <option value=2>
+ <option value=4>
+</datalist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/range.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/range.html
new file mode 100644
index 0000000000..27cc6abe9c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range.html
@@ -0,0 +1,245 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <title>Input Range</title>
+ <meta name=viewport content="width=device-width, maximum-scale=1.0, user-scalable=no" />
+ <link rel="author" title="Fabrice Clari" href="mailto:f.clari@inno-group.com">
+ <link rel="author" title="Dimitri Bocquet" href="mailto:Dimitri.Bocquet@mosquito-fp7.eu">
+ <link rel="author" title="Tomoyuki SHIMIZU" href="mailto:tomoyuki.labs@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-type">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-min">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-max">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#range-state-(type=range)">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-stepup">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-stepdown">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#best-representation-of-the-number-as-a-floating-point-number">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+
+
+ <h1>Input Range</h1>
+ <div style="display:none">
+ <input type="range" id="range_basic" min=0 max=5 />
+ <input type="range" id="value_smaller_than_min" min=0 max=5 value=-10 />
+ <input type="range" id="value_larger_than_max" min=0 max=5 value=7 />
+ <input type="range" id="empty_attributes" />
+ <input type="range" id="value_not_specified" min=2 max=6 />
+ <input type="range" id="control_step_mismatch" min=0 max=7 step=2 />
+ <input type="range" id="max_smaller_than_min" min=2 max=-3 />
+ <input type="range" id="default_step_scale_factor_1" min=5 max=12.6 value=6.7 />
+ <input type="range" id="default_step_scale_factor_2" min=5.3 max=12 value=6.7 />
+ <input type="range" id="float_step_scale_factor" min=5.3 max=12 step=0.5 value=6.7 />
+ <input type="range" id="stepup" min=3 max=14 value=6 step=3 />
+ <input type="range" id="stepdown" min=3 max=11 value=9 step=3 />
+ <input type="range" id="stepup_beyond_max" min=3 max=14 value=9 step=3 />
+ <input type="range" id="stepdown_beyond_min" min=3 max=11 value=6 step=3 />
+ <input type="range" id="illegal_min_and_max" min="ab" max="f" />
+ <input type="range" id="illegal_value_and_step" min=0 max=5 value="ppp" step="xyz" />
+ <input type="range" id="should_skip_whitespace" value=" 123"/>
+ <input type="range" id="exponent_value1" value=""/>
+ <input type="range" id="exponent_value2" value=""/>
+ </div>
+
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+
+ test(
+ function() {
+ assert_equals(document.getElementById('range_basic').type, "range");
+ },
+ "range type support on input element"
+ );
+
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById('range_basic')).overflow, "visible");
+ }, "range overflow styles");
+
+ test(
+ function() {
+ assert_equals(document.getElementById('range_basic').min, "0")
+ },
+ "min attribute support on input element"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('range_basic').max, "5")
+ },
+ "max attribute support on input element"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('illegal_min_and_max').min, "ab")
+ },
+ "Illegal value of min attribute"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('illegal_min_and_max').max, "f")
+ },
+ "Illegal value of max attribute"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('illegal_value_and_step').value, "3")
+ },
+ "Converting an illegal string to the default value"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('illegal_value_and_step').step, "xyz")
+ },
+ "Illegal value of step attribute"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('value_smaller_than_min').value, "0")
+ },
+ "the value is set to min when a smaller value than min attribute is given"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('value_larger_than_max').value, "5")
+ },
+ "the value is set to max when a larger value than max attribute is given"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('empty_attributes').min, "")
+ },
+ "default value of min attribute in input type=range"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('empty_attributes').max, "")
+ },
+ "default value of max attribute in input type=range"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('value_not_specified').value, "4")
+ },
+ "default value when min and max attributes are given (= min plus half the difference between min and max)"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('control_step_mismatch').value, "4")
+ },
+ "default value with step control when both min and max attributes are given"
+ );
+
+ // Chrome would result in different value out of the range between min and max. Why?
+ test(
+ function() {
+ assert_equals(document.getElementById('max_smaller_than_min').value, "2")
+ },
+ "default value when both min and max attributes are given, while min > max"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('default_step_scale_factor_1').value, "7")
+ },
+ "The default step scale factor is 1, unless min attribute has non-integer value"
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('default_step_scale_factor_2').value, "6.3")
+ },
+ "Step scale factor behavior when min attribute has integer value but max attribute is non-integer "
+ );
+
+ test(
+ function() {
+ assert_equals(document.getElementById('float_step_scale_factor').value, "6.8")
+ },
+ "Solving the step mismatch"
+ );
+
+ // Firefox Nightly (24.0a1) would result in the possible maximum value in this range... (i.e. 12)
+ test(
+ function() {
+ var e = document.getElementById('stepup');
+ e.stepUp();
+ assert_equals(e.value, "9")
+ },
+ "Performing stepUp()"
+ );
+
+ // Firefox Nightly (24.0a1) would result in the possible minimum value in this range... (i.e. 3)
+ test(
+ function() {
+ var e = document.getElementById('stepdown');
+ e.stepDown();
+ assert_equals(e.value, "6")
+ },
+ "Performing stepDown()"
+ );
+
+ // Chrome and Opera would throw DOM Exception 11 (InvalidStateError)
+ // Firefox Nightly gives the correct result
+ test(
+ function() {
+ var e = document.getElementById('stepup_beyond_max');
+ e.stepUp(2);
+ assert_equals(e.value, "12")
+ },
+ "Performing stepUp() beyond the value of the max attribute"
+ );
+
+ // Chrome and Opera would throw DOM Exception 11 (InvalidStateError)
+ // Firefox Nightly gives the correct result
+ test(
+ function() {
+ var e = document.getElementById('stepdown_beyond_min');
+ e.stepDown(2);
+ assert_equals(e.value, "3")
+ }, "Performing stepDown() beyond the value of the min attribute"
+ );
+
+ test(
+ function() {
+ var e = document.getElementById('should_skip_whitespace');
+ assert_equals(e.value, "50")
+ }, "Input should be reset to the default value when value attribute has whitespace"
+ );
+
+ test(
+ function() {
+ var e = document.getElementById('exponent_value1');
+ e.value = 1e2;
+ assert_equals(e.value, "100")
+ }, "Multiply value by ten raised to the exponentth power with `e`"
+ );
+
+ test(
+ function() {
+ var e = document.getElementById('exponent_value2');
+ e.value = 1E2;
+ assert_equals(e.value, "100")
+ }, "Multiply value by ten raised to the exponentth power with `E`"
+ );
+
+ </script>
+
+ </body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/required_attribute.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/required_attribute.html
new file mode 100644
index 0000000000..63488e9f4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/required_attribute.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <title>Required Attribute</title>
+ <meta name=viewport content="width=device-width, maximum-scale=1.0, user-scalable=no" />
+ <link rel="author" title="Fabrice Clari" href="mailto:f.clari@inno-group.com">
+ <link rel="author" title="Dimitri Bocquet" href="mailto:Dimitri.Bocquet@mosquito-fp7.eu">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-input-required">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+
+
+ <h1>Required Attribute</h1>
+ <div style="display: none">
+ <input type="text" required="required" />
+ </div>
+
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].getAttribute("required"), "required")}, "required attribute support on input element");
+
+ </script>
+
+ </body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/reset.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/reset.html
new file mode 100644
index 0000000000..9a97995426
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/reset.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>input type reset</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#reset-button-state-(type=reset)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form>
+ <input type=text id=input1 value="foobar">
+ <input type=text id=input2>
+ <input type=reset id=r1>
+</form>
+
+<input type=text id=input3 value="barfoo">
+
+<table>
+ <form>
+ <tr>
+ <td>
+ <input type=text id=input4 value="foobar">
+ <input type=reset id=r2>
+ </td>
+ </tr>
+ </form>
+</table>
+
+<div>
+ <form>
+ <input type=text id=input5 value="foobar">
+ </div>
+ <input type=reset id=r3>
+</form>
+
+<div>
+ <form>
+ <input type=reset id=r4>
+ </div>
+ <input type=text id=input6 value="foobar">
+</form>
+
+<form id=form5>
+ <input type=reset id=r5>
+</form>
+<input form=form5 type=text id=input7 value="foobar">
+
+<form id=form6>
+ <input type=text id=input8 value="foobar">
+</form>
+<input type=reset form=form6 id=r6>
+
+<script>
+ var input1 = document.getElementById('input1'),
+ input2 = document.getElementById('input2'),
+ input3 = document.getElementById('input3'),
+ input7 = document.getElementById('input7'),
+ input8 = document.getElementById('input8'),
+ r1 = document.getElementById('r1');
+
+ test(function(){
+ assert_equals(input1.value, "foobar");
+ assert_equals(input2.value, "");
+ assert_equals(input3.value, "barfoo");
+ input1.value = "foobar1";
+ input2.value = "notempty";
+ input3.value = "barfoo1";
+ assert_equals(input1.value, "foobar1");
+ assert_equals(input2.value, "notempty");
+ assert_equals(input3.value, "barfoo1");
+ r1.click();
+ assert_equals(input1.value, "foobar");
+ assert_equals(input2.value, "");
+ assert_equals(input3.value, "barfoo1");
+ }, "reset button only resets the form owner");
+
+ test(function(){
+ assert_false(r1.willValidate);
+ }, "the element is barred from constraint validation");
+
+ test(function(){
+ assert_equals(input1.value, "foobar");
+ assert_equals(input2.value, "");
+ assert_equals(input3.value, "barfoo1");
+ r1.disabled = true;
+ r1.click();
+ assert_equals(input1.value, "foobar");
+ assert_equals(input2.value, "");
+ assert_equals(input3.value, "barfoo1");
+ }, "clicking on a disabled reset does nothing");
+
+ function testReset(inputId, buttonId) {
+ var inp = document.getElementById(inputId);
+ assert_equals(inp.value, "foobar");
+ inp.value = "barfoo";
+ assert_equals(inp.value, "barfoo");
+ document.getElementById(buttonId).click();
+ assert_equals(inp.value, "foobar");
+ }
+
+ test(function(){
+ testReset("input4", "r2");
+ testReset("input5", "r3");
+ testReset("input6", "r4");
+ }, "reset button resets controls associated with their form using the form element pointer");
+
+ test(function(){
+ testReset("input7", "r5");
+ }, "reset button resets controls associated with a form using the form attribute");
+
+ test(function(){
+ testReset("input8", "r6");
+ }, "reset button associated with a form using the form attribute resets all the form's controls");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/image-submit-click.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/image-submit-click.html
new file mode 100644
index 0000000000..8461a03d7a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/image-submit-click.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<form>
+ <input type="image" name="name" value="value">
+</form>
+
+<script>
+"use strict";
+if (window.location.search.startsWith("?name.x")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const input = document.querySelector("input");
+ input.click();
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/loadresolver.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/loadresolver.html
new file mode 100644
index 0000000000..14379dd922
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/loadresolver.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+ window.onload = () => {
+ window.opener.loadResolver();
+ };
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html
new file mode 100644
index 0000000000..27044c3537
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ window.onload = () => {
+ const loadResolver = window.opener.loadResolver;
+ if (loadResolver)
+ loadResolver();
+ };
+</script>
+<input type=range min=0 max=10 value=5>
+<script>
+ const input = document.querySelector('input');
+ input.onchange = () => window.seenchange = true;
+ input.oninput = () => window.seeninput = true;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html.headers b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html.headers
new file mode 100644
index 0000000000..4030ea1d3d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/range-restore-events.html.headers
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/show-picker-child-iframe.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/show-picker-child-iframe.html
new file mode 100644
index 0000000000..07b72f02cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/show-picker-child-iframe.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Test showPicker() in an iframe</title>
+<script type=module>
+import inputTypes from "./../input-types.js";
+
+const urlParams = new URLSearchParams(location.search);
+const documentDomain = urlParams.get('documentDomain');
+if (documentDomain) {
+ document.domain = documentDomain;
+}
+
+let securityErrors = [];
+for (const inputType of inputTypes) {
+ const input = document.createElement("input");
+ input.setAttribute("type", inputType);
+
+ try {
+ input.showPicker();
+ } catch (error) {
+ if (error instanceof DOMException && error.name == 'SecurityError') {
+ securityErrors.push(inputType);
+ }
+ }
+}
+parent.postMessage(securityErrors.join(','), "*");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html
new file mode 100644
index 0000000000..aa57bdebcc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ window.onload = () => {
+ const loadResolver = window.opener.loadResolver;
+ if (loadResolver)
+ loadResolver();
+ };
+</script>
+<input type=text value=initialValue>
+<script>
+ const input = document.querySelector('input');
+ input.onchange = () => window.seenchange = true;
+ input.oninput = () => window.seeninput = true;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html.headers b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html.headers
new file mode 100644
index 0000000000..4030ea1d3d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/resources/text-restore-events.html.headers
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/search_input.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/search_input.html
new file mode 100644
index 0000000000..7b63cd43e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/search_input.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <title>Search Input</title>
+ <meta name=viewport content="width=device-width, maximum-scale=1.0, user-scalable=no" />
+ <link rel="author" title="Fabrice Clari" href="mailto:f.clari@inno-group.com">
+ <link rel="author" title="Dimitri Bocquet" href="mailto:Dimitri.Bocquet@mosquito-fp7.eu">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-type">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-input-placeholder">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+
+
+ <h1>Search Input</h1>
+ <input type="search" style="display:none" placeholder="Search..." />
+
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+
+
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].type, "search")}, "search type support on input element");
+ test(function() {assert_equals(document.getElementsByTagName("input")[0].placeholder, "Search...")}, "placeholder attribute support on input element");
+
+ </script>
+
+ </body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/selection-pointer.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/selection-pointer.html
new file mode 100644
index 0000000000..7f06ae24a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/selection-pointer.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Selecting texts across input element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<link rel="stylesheet" href="/fonts/ahem.css" />
+
+<style>
+ .test {
+ font: 16px/1 Ahem;
+ padding-bottom: 16px;
+ }
+</style>
+<div class="test">
+ <span id="foo">foo</span><br>
+ <input id="input"><br>
+ <span id="bar">bar</span>
+</div>
+<script type="module">
+import inputTypes from "./input-types.js";
+
+const selection = getSelection();
+const inputVisibleTypes = inputTypes.filter(t => t !== "hidden");
+
+for (const inputType of inputVisibleTypes) {
+ promise_test(async () => {
+ input.type = inputType;
+ selection.collapse(foo);
+ await new test_driver.Actions()
+ .pointerMove(0, 0, {origin: foo})
+ .pointerDown()
+ .pointerMove(0, 0, {origin: input})
+ .pointerMove(0, 0, {origin: bar})
+ .pointerUp()
+ .send();
+ const nRanges = selection.rangeCount;
+ assert_true(nRanges > 0);
+ const expectedStart = foo.childNodes[0];
+ const expectedEnd = bar.childNodes[0];
+ if (nRanges === 1) {
+ assert_equals(selection.anchorNode, expectedStart, "anchorNode");
+ assert_equals(selection.focusNode, expectedEnd, "focusNode");
+ } else {
+ // In case multiple ranges are supported, make sure the set of ranges
+ // spans the full selection, across the input.
+ const ranges = [...Array(nRanges).keys()].map(n => selection.getRangeAt(n));
+ assert_true(ranges.some(r => r.startContainer === expectedStart),"startContainer");
+ assert_true(ranges.some(r => r.endContainer === expectedEnd),"endContainer");
+ }
+ }, `Selecting texts across <input type=${inputType}> should not cancel selection`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/selection-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/selection-weekmonth.html
new file mode 100644
index 0000000000..c0d36dded9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/selection-weekmonth.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<title>Input element programmatic selection support</title>
+<link rel="author" title="yaycmyk" href="mailto:evan@yaycmyk.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-select">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+/* all textual, non-hidden inputs support .select() */
+test(function() {
+ var valid = [
+ "month",
+ "week",
+ ];
+
+ valid.forEach(function(type) {
+ test(function() {
+ var input = document.createElement("input");
+ var a;
+
+ input.type = type;
+ assert_equals(input.type, type, "the given input type is not supported");
+
+ input.select();
+
+ }, "input type " + type + " should support the select() method");
+ });
+});
+
+/* only certain input types are allowed to have a variable-length selection */
+test(function() {
+ var invalid = [
+ "month",
+ "week",
+ ];
+
+ invalid.forEach(function(type) {
+ test(function() {
+ var input = document.createElement("input");
+
+ input.type = type;
+ assert_equals(input.type, type, "the given input type is not supported");
+
+ assert_equals(input.selectionStart, null, 'getting input.selectionStart');
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.selectionStart = 0; });
+ assert_equals(input.selectionEnd, null, 'getting input.selectionEnd');
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.selectionEnd = 0; });
+ assert_equals(input.selectionDirection, null, 'getting input.selectionDirection');
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.selectionDirection = "none"; });
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.setSelectionRange(0, 0); });
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.setRangeText('', 0, 0); });
+
+ }, "input type " + type + " should not support variable-length selections");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/selection.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/selection.html
new file mode 100644
index 0000000000..1f5c8378ff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/selection.html
@@ -0,0 +1,140 @@
+<!DOCTYPE HTML>
+<title>Input element programmatic selection support</title>
+<link rel="author" title="yaycmyk" href="mailto:evan@yaycmyk.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-select">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+/* all textual, non-hidden inputs support .select() */
+test(function() {
+ var valid = [
+ "text",
+ "search",
+ "url",
+ "tel",
+ "email",
+ "password",
+ "date",
+ "time",
+ "datetime-local",
+ "number",
+ "color",
+ "file",
+ ];
+
+ var invalid = [
+ "hidden",
+ "range",
+ "checkbox",
+ "radio",
+ "submit",
+ "image",
+ "reset",
+ "button"
+ ];
+
+ valid.forEach(function(type) {
+ test(function() {
+ var input = document.createElement("input");
+ var a;
+
+ input.type = type;
+ assert_equals(input.type, type, "the given input type is not supported");
+
+ input.select();
+
+ }, "input type " + type + " should support the select() method");
+ });
+
+ invalid.forEach(function(type) {
+ test(function() {
+ var input = document.createElement("input");
+
+ input.type = type;
+ assert_equals(input.type, type, "the given input type is not supported");
+
+ var selectionStartBefore = input.selectionStart;
+ var selectionEndBefore = input.selectionEnd;
+ var selectionDirectionBefore = input.selectionDirection;
+
+ // Does not throw; see https://github.com/whatwg/html/issues/2275
+ input.select();
+
+ assert_equals(input.selectionStart, selectionStartBefore, "selectionStart must not change");
+ assert_equals(input.selectionEnd, selectionEndBefore, "selectionEnd must not change");
+ assert_equals(input.selectionDirection, selectionDirectionBefore, "selectionDirection must not change");
+
+ }, "input type " + type + " should do nothing when the select() method is called (but, not throw)");
+ });
+});
+
+/* only certain input types are allowed to have a variable-length selection */
+test(function() {
+ var valid = [
+ "text",
+ "search",
+ "url",
+ "tel",
+ "password"
+ ];
+
+ var invalid = [
+ "hidden",
+ "email",
+ "date",
+ "time",
+ "datetime-local",
+ "number",
+ "range",
+ "color",
+ "checkbox",
+ "radio",
+ "file",
+ "submit",
+ "image",
+ "reset",
+ "button"
+ ];
+
+ valid.forEach(function(type) {
+ test(function() {
+ var input = document.createElement("input");
+ var a;
+
+ input.type = type;
+ assert_equals(input.type, type, "the given input type is not supported");
+
+ a = input.selectionStart;
+ input.selectionStart = 0;
+ a = input.selectionEnd;
+ input.selectionEnd = 0;
+ a = input.selectionDirection;
+ input.selectionDirection = "none";
+ input.setSelectionRange(0, 0);
+ input.setRangeText('', 0, 0);
+
+ }, "input type " + type + " should support all selection attributes and methods");
+ });
+
+ invalid.forEach(function(type) {
+ test(function() {
+ var input = document.createElement("input");
+
+ input.type = type;
+ assert_equals(input.type, type, "the given input type is not supported");
+
+ assert_equals(input.selectionStart, null, 'getting input.selectionStart');
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.selectionStart = 0; });
+ assert_equals(input.selectionEnd, null, 'getting input.selectionEnd');
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.selectionEnd = 0; });
+ assert_equals(input.selectionDirection, null, 'getting input.selectionDirection');
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.selectionDirection = "none"; });
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.setSelectionRange(0, 0); });
+ assert_throws_dom("INVALID_STATE_ERR", function() { input.setRangeText('', 0, 0); });
+
+ }, "input type " + type + " should not support variable-length selections");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-cross-origin-iframe.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-cross-origin-iframe.html
new file mode 100644
index 0000000000..c8197cc180
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-cross-origin-iframe.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<title>Test showPicker() called from cross-origin iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<iframe id="iframe1"></iframe>
+<iframe id="iframe2"></iframe>
+<iframe id="iframe3"></iframe>
+<iframe id="iframe4"></iframe>
+</body>
+<script>
+function waitForSecurityErrors() {
+ return new Promise((resolve) => {
+ window.addEventListener("message", (event) => resolve(event.data), {
+ once: true,
+ });
+ });
+}
+
+promise_test(async (t) => {
+ iframe1.src =
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html";
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "",
+ "In same-origin iframes, showPicker() does not throw a SecurityError."
+ );
+});
+
+promise_test(async (t) => {
+ iframe2.src =
+ get_host_info().HTTP_NOTSAMESITE_ORIGIN +
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html";
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "button,checkbox,date,datetime-local,email,hidden,image,month,number,password,radio,range,reset,search,submit,tel,text,time,url,week",
+ "In cross-origin iframes, showPicker() throws a SecurityError except on file and color."
+ );
+});
+
+promise_test(async (t) => {
+ iframe3.src =
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html?documentDomain=" + get_host_info().ORIGINAL_HOST;
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "",
+ "In same-origin but cross-origin-domain iframes, showPicker() does not throw a SecurityError."
+ );
+});
+
+promise_test(async (t) => {
+ document.domain = get_host_info().ORIGINAL_HOST;
+ iframe4.src =
+ get_host_info().HTTP_REMOTE_ORIGIN +
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html?documentDomain=" + get_host_info().ORIGINAL_HOST;
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "button,checkbox,date,datetime-local,email,hidden,image,month,number,password,radio,range,reset,search,submit,tel,text,time,url,week",
+ "In cross-origin but same-origin-domain iframes, showPicker() throws a SecurityError except on file and color."
+ );
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-disabled-readonly.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-disabled-readonly.html
new file mode 100644
index 0000000000..8fdffc158f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-disabled-readonly.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>Test showPicker() disabled/readonly requirement</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body></body>
+<script type=module>
+import inputTypes from "./input-types.js";
+
+for (const inputType of inputTypes) {
+ test(() => {
+ const input = document.createElement("input");
+ input.setAttribute("type", inputType);
+ input.setAttribute("disabled", "");
+
+ assert_throws_dom('InvalidStateError', () => { input.showPicker(); });
+ }, `input[type=${inputType}] showPicker() throws when disabled`);
+}
+
+const noReadonlySupport = ['button', 'checkbox', 'color', 'file',
+'hidden', 'image', 'radio', 'range', 'reset', 'submit'];
+for (const inputType of inputTypes) {
+ if (!noReadonlySupport.includes(inputType)) {
+ test(() => {
+ const input = document.createElement("input");
+ input.setAttribute("type", inputType);
+ input.setAttribute("readonly", "");
+
+ assert_throws_dom('InvalidStateError', () => { input.showPicker(); });
+ }, `input[type=${inputType}] showPicker() throws when readonly`);
+ } else {
+ test(() => {
+ const input = document.createElement("input");
+ input.setAttribute("type", inputType);
+ input.setAttribute("readonly", "");
+
+ // Missing user gesture activation throws.
+ assert_throws_dom('NotAllowedError', () => { input.showPicker(); });
+ }, `input[type=${inputType}] showPicker() doesn't throw when readonly`);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-user-gesture.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-user-gesture.html
new file mode 100644
index 0000000000..6b94b4f0f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/show-picker-user-gesture.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Test showPicker() user gesture requirement</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body></body>
+<script type=module>
+import inputTypes from "./input-types.js";
+
+for (const inputType of inputTypes) {
+ test(() => {
+ const input = document.createElement("input");
+ input.setAttribute("type", inputType);
+
+ assert_throws_dom('NotAllowedError', () => { input.showPicker(); });
+ }, `input[type=${inputType}] showPicker() requires a user gesture`);
+}
+
+for (const inputType of inputTypes) {
+ promise_test(async t => {
+ const input = document.createElement("input");
+ input.setAttribute("type", inputType);
+
+ await test_driver.bless('show picker');
+ input.showPicker();
+ input.blur();
+ }, `input[type=${inputType}] showPicker() does not throw when user activation is active`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/telephone.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/telephone.html
new file mode 100644
index 0000000000..974cbaf88b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/telephone.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Input tel</title>
+ <link rel="author" title="Kazuki Kanamori" href="mailto:yogurito@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#telephone-state-(type=tel)">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>Input tel</h1>
+ <input type="tel" id="novalue" />
+ <input type="tel" id="value_with_LF" value="0&#x000A;1" />
+ <input type="tel" id="value_with_CR" value="0&#x000D;1" />
+ <input type="tel" id="value_with_CRLF" value="0&#x000A;&#x000D;1" />
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+ var element = document.getElementById('novalue');
+ test(function(){
+ assert_equals(element.type, 'tel');
+ }, 'tel type supported on input element');
+ test(function(){
+ element.value = '0\u000A1';
+ assert_equals(element.value, '01');
+ }, 'User agents must not allow users to insert "LF" (U+000A)');
+ test(function(){
+ element.value = '0\u000D1';
+ assert_equals(element.value, '01');
+ }, 'User agents must not allow users to insert "CR" (U+000D)');
+
+ element = document.getElementById('value_with_LF');
+ test(function(){
+ assert_equals(element.value, '01');
+ }, 'The value attribute, if specified, must have a value that contains no "LF" (U+000A)');
+
+ element = document.getElementById('value_with_CR');
+ test(function(){
+ assert_equals(element.value, '01');
+ }, 'The value attribute, if specified, must have a value that contains no "CR" (U+000D)');
+
+ test(function(){
+ element = document.getElementById('novalue');
+ element.value = '0\u000D\u000A1';
+ assert_equals(element.value, '01');
+
+ element = document.getElementById('value_with_CRLF');
+ assert_equals(element.value, '01');
+ }, 'The value sanitization algorithm is as follows: Strip line breaks from the value');
+
+ element = document.getElementById('novalue');
+ test(function(){
+ element.value = '+811234';
+ assert_equals(element.value, '+811234');
+ }, 'Element can accept the phone number with plus sign(country code)');
+ test(function(){
+ element.value = '1234#5678';
+ assert_equals(element.value, '1234#5678');
+ }, 'Element can accept the phone number with hash mark(extension number)');
+ test(function(){
+ element.value = '123-456-789';
+ assert_equals(element.value, '123-456-789');
+ }, 'Element can accept the phone number with hyphen');
+ test(function(){
+ element.value = '123.456.789';
+ assert_equals(element.value, '123.456.789');
+ }, 'Element can accept the phone number with dots');
+ test(function(){
+ element.value = '1 23 4';
+ assert_equals(element.value, '1 23 4');
+ }, 'Element can accept the phone number with whitespace');
+ test(function(){
+ element.value = ' 1234 ';
+ assert_equals(element.value, ' 1234 ');
+ }, 'Element can accept the phone number with leading & following whitespaces');
+ test(function(){
+ element.value = '(03)12345678';
+ assert_equals(element.value, '(03)12345678');
+ }, 'Element can accept the phone number with parentheses(area code)');
+ </script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/text.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/text.html
new file mode 100644
index 0000000000..f30f9d39fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/text.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Text input element</title>
+ <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#text-(type=text)-state-and-search-state-(type=search)">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Text input element</h1>
+ <div style="display: none">
+
+ <input id="text" type="text" />
+ <input id="text_with_value" type="text" value="foo" />
+
+ <input id="search" type="search" />
+ <input id="search_with_value" type="search" value="foo" />
+
+ </div>
+ <div id="log"></div>
+ <script type="text/javascript">
+ var types = [ 'text', 'search' ];
+
+ for (var i = 0; i < types.length; ++i) {
+ test(
+ function() {
+ assert_equals(document.getElementById(types[i]).value, "");
+ assert_equals(document.getElementById(types[i] + "_with_value").value, "foo");
+ }, "Value returns the current value for " + types[i]);
+
+ test(
+ function() {
+ document.getElementById(types[i]).value = "A";
+ assert_equals(document.getElementById(types[i]).value, "A");
+ document.getElementById(types[i]).value = "B";
+ }, "Setting value changes the current value for " + types[i]);
+
+ test(
+ function() {
+ // Any LF (\n) must be stripped.
+ document.getElementById(types[i]).value = "\nAB";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ document.getElementById(types[i]).value = "A\nB";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ document.getElementById(types[i]).value = "AB\n";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+
+ // Any CR (\r) must be stripped.
+ document.getElementById(types[i]).value = "\rAB";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ document.getElementById(types[i]).value = "A\rB";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ document.getElementById(types[i]).value = "AB\r";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+
+ // Any combinations of LF CR must be stripped.
+ document.getElementById(types[i]).value = "\r\nAB";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ document.getElementById(types[i]).value = "A\r\nB";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ document.getElementById(types[i]).value = "AB\r\n";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ document.getElementById(types[i]).value = "\r\nA\n\rB\r\n";
+ assert_equals(document.getElementById(types[i]).value, "AB");
+ }, "Value sanitization algorithm should strip line breaks for " + types[i]);
+
+ test(
+ function() {
+ assert_equals(document.getElementById(types[i]).files, null);
+ }, "files attribute must return null for " + types[i]);
+
+ test(
+ function() {
+ assert_equals(document.getElementById(types[i]).valueAsDate, null);
+ }, "valueAsDate attribute must return null for " + types[i]);
+
+ test(
+ function() {
+ assert_equals(document.getElementById(types[i]).valueAsNumber, NaN);
+ }, "valueAsNumber attribute must return NaN for " + types[i]);
+
+ test(
+ function() {
+ assert_equals(document.getElementById("text").list, null);
+ }, "list attribute must return null for " + types[i]);
+
+ test(
+ function() {
+ var el = document.getElementById(types[i]);
+ assert_throws_dom("InvalidStateError", function() { el.stepDown(); }, "");
+ }, "stepDown does not apply for " + types[i]);
+
+ test(
+ function() {
+ var el = document.getElementById(types[i]);
+ assert_throws_dom("InvalidStateError", function() { el.stepUp(); }, "");
+ }, "stepUp does not apply for " + types[i]);
+ }
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/time-2.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/time-2.html
new file mode 100644
index 0000000000..0ffec33bf5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/time-2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Form input type=time</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#times">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#time-state-(type=time)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var times = [
+ {value: "", expected: "", testname: "empty value"},
+ {value: "00:00", expected: "00:00", testname: "Valid value: value should be 00:00"},
+ {value: "00:00:00", expected: "00:00:00", testname: "Valid value: value should be 00:00:00"},
+ {value: "00:00:00.0", expected: "00:00:00.0", testname: "Valid value: value should be 00:00:00.0"},
+ {value: "00:00:00.00", expected: "00:00:00.00", testname: "Valid value: value should be 00:00:00.00"},
+ {value: "00:00:00.000", expected: "00:00:00.000", testname: "Valid value: value should be 00:00:00.000"},
+ {value: "00:00:00.0000", expected: "", testname: "Invalid value: fraction should have one, two or three ASCII digits. Value should be empty"},
+ {value: "0:00:00.000", expected: "", testname: "Invalid value: hour should have two ASCII digits. Value should be empty"},
+ {value: "00:0:00.000", expected: "", testname: "Invalid value: minutes should have two ASCII digits. Value should be empty"},
+ {value: "00:00:0.000", expected: "", testname: "Invalid value: seconds should have two ASCII digits. Value should be empty"},
+ {value: "24:00:00.000", expected: "", testname: "Invalid value: hour > 23. Value should be empty"},
+ {value: "00:60:00.000", expected: "", testname: "Invalid value: minute > 59. Value should be empty"},
+ {value: "00:00:60.000", expected: "", testname: "Invalid value: second > 59. Value should be empty"},
+ {value: "12:00:00.001", attributes: { min: "12:00:00.000" }, expected: "12:00:00.001", testname: "Value >= min attribute"},
+ {value: "12:00:00.000", attributes: { min: "12:00:00.001" }, expected: "12:00:00.000", testname: "Value < min attribute"},
+ {value: "12:00:00.000", attributes: { max: "12:00:00.001" }, expected: "12:00:00.000", testname: "Value <= max attribute"},
+ {value: "12:00:00.001", attributes: { max: "12:00:00.000" }, expected: "12:00:00.001", testname: "Value > max attribute"}
+ ];
+ for (var i = 0; i < times.length; i++) {
+ var w = times[i];
+ test(function() {
+ var input = document.createElement("input");
+ input.type = "time";
+ input.value = w.value;
+ for(var attr in w.attributes) {
+ input[attr] = w.attributes[attr];
+ }
+ assert_equals(input.value, w.expected);
+ }, w.testname);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/time-datalist-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/time-datalist-crash.html
new file mode 100644
index 0000000000..2964032e35
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/time-datalist-crash.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="newParent"></div>
+<datalist id="suggestions">
+ <option>12:00</option>
+ <input type="time" list="suggestions">
+</datalist>
+<script>
+ test(() => {
+ document.body.offsetTop;
+ newParent.appendChild(suggestions);
+ }, "Moving a datalist enclosing an input type=time using that list should not crash.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/time-focus-dynamic-value-change.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/time-focus-dynamic-value-change.html
new file mode 100644
index 0000000000..95ccb1ff69
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/time-focus-dynamic-value-change.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>input type=date and input type=datetime handle focus state correctly</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time)">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1450219">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input type="time">
+<input type="text">
+<script>
+let t = async_test("Time input handles focus correctly when value changes");
+window.onload = t.step_func_done(function() {
+ let time = document.querySelector("input[type=time]");
+ let text = document.querySelector("input[type=text]");
+ time.focus();
+ assert_true(time.matches(":focus"));
+ assert_equals(document.activeElement, time);
+ time.value = "08:10:10";
+ assert_true(time.matches(":focus"));
+ assert_equals(document.activeElement, time);
+ time.value = "08:10";
+ assert_true(time.matches(":focus"));
+ assert_equals(document.activeElement, time);
+ text.focus();
+ assert_true(text.matches(":focus"));
+ assert_false(time.matches(":focus"));
+ assert_equals(document.activeElement, text);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/time.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/time.html
new file mode 100644
index 0000000000..ec815d4cb3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/time.html
@@ -0,0 +1,357 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <title>Input Time</title>
+ <meta name=viewport content="width=device-width, maximum-scale=1.0, user-scalable=no" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <h1>Input Time</h1>
+ <div style="display:none;">
+ <input type="time "id="chkDefaultValue" />
+ <input type="time" id="chkStep" />
+ <input type="time" id="chkSetValueTest" />
+ <input type="time" id="chkSupportAttribute" min="01:01:01.001" max="12:12:12.012" step="600" />
+ </div>
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+
+/* check default value */
+test(function(){ assert_equals(document.getElementById("chkDefaultValue").value, "");
+}, "time element of default time value");
+test(function(){assert_equals(document.getElementById('chkStep').step, "");
+}, "step attribute on default value check");
+test(function(){assert_equals(document.getElementById('chkDefaultValue').max, "");
+}, "max attribute on default value check")
+test(function(){assert_equals(document.getElementById('chkDefaultValue').max, "");
+}, "min attribute on default value check")
+
+/* simple attribute test*/
+test(function(){assert_equals(document.getElementById("chkSupportAttribute").type,"time");}
+ , "type attribute support on input element");
+test(function(){assert_equals(document.getElementById('chkSupportAttribute').min, "01:01:01.001")}
+ , "max attribute support on input element");
+test(function(){assert_equals(document.getElementById('chkSupportAttribute').max, "12:12:12.012")}
+ , "min attribute support on input element");
+test(function(){assert_equals(document.getElementById("chkSupportAttribute").step, "600")}
+ , "step attribute support on input element");
+
+/* check step up and down */
+var _StepTest = document.getElementById("chkStep");
+test(function(){ assert_true(typeof(_StepTest.stepUp) ==="function" ) } , "stepUp function support on input Element");
+test(function(){ assert_true(typeof(_StepTest.stepDown) ==="function" ) } , "stepDown function support on input Element");
+
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "12:01",
+ "12:01:00",
+ "12:01:00.0",
+ "12:01:00.00",
+ "12:01:00.000"],
+ "a valid time string representing 1 minute after noon");
+} , "stepUp step value empty on default step value ");
+
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "11:59",
+ "11:59:00",
+ "11:59:00.0",
+ "11:59:00.00",
+ "11:59:00.000"],
+ "a valid time string representing 1 minute before noon");
+}, "stepDown step value empty default step value");
+
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "-600";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "12:01",
+ "12:01:00",
+ "12:01:00.0",
+ "12:01:00.00",
+ "12:01:00.000"],
+ "a valid time string representing 1 minute after noon");
+},"stepUp on step value minus");
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "-600";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "11:59",
+ "11:59:00",
+ "11:59:00.0",
+ "11:59:00.00",
+ "11:59:00.000"],
+ "a valid time string representing 1 minute before noon");
+},"stepDown on step value minus");
+
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "0";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "12:01",
+ "12:01:00",
+ "12:01:00.0",
+ "12:01:00.00",
+ "12:01:00.000"],
+ "a valid time string representing 1 minute after noon");
+} , "stepUp on step value zero ");
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "0";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "11:59",
+ "11:59:00",
+ "11:59:00.0",
+ "11:59:00.00",
+ "11:59:00.000"],
+ "a valid time string representing 1 minute before noon");
+} , "stepDown on step value zero ");
+
+test(function(){
+ _StepTest.value = "00:00";
+ _StepTest.step = "86399";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "23:59:59",
+ "23:59:59.0",
+ "23:59:59.00",
+ "23:59:59.000"],
+ "a valid time string representing 1 second before midnight");
+} , "stepUp on step value 24 hour");
+test(function(){
+ _StepTest.value = "23:59:59";
+ _StepTest.step = "86399";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "00:00",
+ "00:00:00",
+ "00:00:00.0",
+ "00:00:00.00",
+ "00:00:00.000"],
+ "a valid time string representing midnight");
+} , "stepDown on step value 24 hour ");
+
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "3600";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "13:00",
+ "13:00:00",
+ "13:00:00.0",
+ "13:00:00.00",
+ "13:00:00.000"],
+ "a valid time string representing 1pm");
+} , "stepUp on step value hour ");
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "3600";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "11:00",
+ "11:00:00",
+ "11:00:00.0",
+ "11:00:00.00",
+ "11:00:00.000"],
+ "a valid time string representing 11am");
+} , "stepDown on step value hour ");
+
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "1";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "12:00:01",
+ "12:00:01.0",
+ "12:00:01.00",
+ "12:00:01.000"],
+ "a valid time string representing 1 second after noon");
+} , "stepUp on step value second ");
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "1";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "11:59:59",
+ "11:59:59.0",
+ "11:59:59.00",
+ "11:59:59.000"],
+ "a valid time string representing 1 second before noon");
+} , "stepDown on step value second ");
+
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "0.001";
+ _StepTest.stepUp();
+ assert_equals(_StepTest.value, "12:00:00.001");
+} , "stepUp on step value with fractional seconds");
+test(function(){
+ _StepTest.value = "12:00";
+ _StepTest.step = "0.001";
+ _StepTest.stepDown();
+ assert_equals(_StepTest.value, "11:59:59.999");
+} , "stepDown on step value with fractional seconds");
+
+test(function(){
+ _StepTest.value = "13:00:00";
+ _StepTest.step = "1";
+ _StepTest.stepUp(2);
+ assert_in_array(
+ _StepTest.value,
+ [
+ "13:00:02",
+ "13:00:02.0",
+ "13:00:02.00",
+ "13:00:02.000"],
+ "a valid time string representing 2 seconds after 1pm");
+}, "stepUp argument 2 times");
+test(function(){
+ _StepTest.value = "13:00:00";
+ _StepTest.step = "1";
+ _StepTest.stepDown(2);
+ assert_in_array(
+ _StepTest.value,
+ [
+ "12:59:58",
+ "12:59:58.0",
+ "12:59:58.00",
+ "12:59:58.000"],
+ "a valid time string representing 2 seconds before 1pm");
+}, "stepDown argument 2 times");
+
+test(function(){
+ _StepTest.max = "15:00";
+ this.add_cleanup(function() { _StepTest.max = ""; });
+ _StepTest.value = "15:00";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "15:00",
+ "15:00:00",
+ "15:00:00.0",
+ "15:00:00.00",
+ "15:00:00.000"],
+ "a valid time string representing 3pm");
+} , "stepUp stop because it exceeds the maximum value");
+test(function(){
+ _StepTest.min = "13:00";
+ this.add_cleanup(function() { _StepTest.min = ""; });
+ _StepTest.value = "13:00";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "13:00",
+ "13:00:00",
+ "13:00:00.0",
+ "13:00:00.00",
+ "13:00:00.000"],
+ "a valid time string representing 1pm");
+} , "stepDown stop so lower than the minimum value");
+
+test(function(){
+ // Set min value to ensure that 15:01 - base is a multiple of 2 min (i.e., a
+ // valid value).
+ _StepTest.min = "14:01";
+ _StepTest.max = "15:01";
+ this.add_cleanup(function() { _StepTest.min = _StepTest.max = ""; });
+ _StepTest.value = "15:00";
+ _StepTest.step = "120";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "15:01",
+ "15:01:00",
+ "15:01:00.0",
+ "15:01:00.00",
+ "15:01:00.000"],
+ "a valid time string representing 1 minute after 3pm");
+} , "stop at border on stepUp");
+test(function(){
+ _StepTest.min = "12:59";
+ this.add_cleanup(function() { _StepTest.min = ""; });
+ _StepTest.value = "13:00";
+ _StepTest.step = "120";
+ _StepTest.stepDown();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "12:59",
+ "12:59:00",
+ "12:59:00.0",
+ "12:59:00.00",
+ "12:59:00.000"],
+ "a valid time string representing 1 minute before 2pm");
+} , "stop at border on stepDown");
+
+test(function(){
+ _StepTest.value = "";
+ _StepTest.step = "60";
+ _StepTest.stepUp();
+ assert_in_array(
+ _StepTest.value,
+ [
+ "00:01",
+ "00:01:00",
+ "00:01:00.0",
+ "00:01:00.00",
+ "00:01:00.000"],
+ "a valid time string representing 1 minute after midnight");
+} , " empty value of stepUp");
+
+
+/* set value test */
+test(function(){
+ var _time = document.getElementById("chkSetValueTest");
+ _time.value = "12:00:00.000";
+ assert_equals(_time.value, "12:00:00.000");
+ _time.value = "hh:mi:ss.sss";
+ assert_equals(_time.value, "");
+}, "set value on not time format value");
+
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-file-to-text-crash.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-file-to-text-crash.html
new file mode 100644
index 0000000000..5fb5000a26
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-file-to-text-crash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#input-type-change">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<input id="myInput" type="file">
+<script>
+ test(() => {
+ myInput.offsetTop;
+ myInput.type = "text";
+ }, "Changing type from file to text should not crash.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state-weekmonth.html
new file mode 100644
index 0000000000..cab8e3a625
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state-weekmonth.html
@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Input element's type attribute changes state</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-input-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+ const INITIAL_VALUE = " foo\rbar ";
+
+ // Sanitize algorithm implementations only for values used in this test.
+ function sanitizeText(value) {
+ switch (value) {
+ case INITIAL_VALUE: return " foobar ";
+ case " foobar ": return value;
+ case "foobar": return value;
+ case "50": return value;
+ case "#000000": return value;
+ case "": return value;
+ default: throw new DOMException(`Internal Error: Should add support of "${value}"`, "NotSupportedError");
+ }
+ }
+ function sanitizeEmailOrUrl(value) {
+ switch (value) {
+ case INITIAL_VALUE: return "foobar";
+ case " foobar ": return "foobar";
+ case "foobar": return value;
+ case "50": return value;
+ case "#000000": return value;
+ case "": return value;
+ default: throw new DOMException(`Internal Error: Should add support of "${value}"`, "NotSupportedError");
+ }
+ }
+ function sanitizeTemporal(value) {
+ // We have no test cases using valid temporal values.
+ return "";
+ }
+ function sanitizeNumber(value) {
+ switch (value) {
+ case "50": return value;
+ default:
+ // We have no test cases using valid numbers other than "50".
+ return "";
+ }
+ }
+ function sanitizeRange(value) {
+ // We have no test cases using valid numbers other than "50".
+ return "50";
+ }
+ function sanitizeColor(value) {
+ // We have no test cases using valid colors other than "#000000".
+ return "#000000";
+ }
+ function browserSupportsInputTypeOf(inputType) {
+ var inputTest = document.createElement("input");
+ inputTest.type = inputType;
+ return (inputTest.type === inputType);
+ }
+
+
+ var types = [
+ { type: "hidden" },
+ { type: "text", sanitizer: sanitizeText },
+ { type: "search", sanitizer: sanitizeText },
+ { type: "tel", sanitizer: sanitizeText },
+ { type: "url", sanitizer: sanitizeEmailOrUrl },
+ { type: "email", sanitizer: sanitizeEmailOrUrl },
+ { type: "password", sanitizer: sanitizeText },
+ { type: "datetime-local", sanitizer: sanitizeTemporal },
+ { type: "date", sanitizer: sanitizeTemporal },
+ { type: "month", sanitizer: sanitizeTemporal },
+ { type: "week", sanitizer: sanitizeTemporal },
+ { type: "time", sanitizer: sanitizeTemporal },
+ { type: "number", sanitizer: sanitizeNumber },
+ { type: "range", sanitizer: sanitizeRange },
+ { type: "color", sanitizer: sanitizeColor },
+ { type: "checkbox", defaultValue: "on" },
+ { type: "radio", defaultValue: "on" },
+ { type: "file" },
+ { type: "submit" },
+ { type: "image" },
+ { type: "reset" },
+ { type: "button" }
+ ];
+
+ const selectionStart = 2;
+ const selectionEnd = 5;
+ const selectionDirection = "backward";
+
+ // Obtain selectionDirection after setting it to "none".
+ // Some platforms don't support "none" direction, and "forward" is returned
+ // in such platforms.
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#set-the-selection-direction
+ function testNoneDirection() {
+ const input = document.createElement("input");
+ input.selectionDirection = "none";
+ return input.selectionDirection;
+ }
+ const noneDirectionResult = testNoneDirection();
+
+ for (var i = 0; i < types.length; i++) {
+ for (var j = 0; j < types.length; j++) {
+ const monthOrWeek = types[i].type === 'month' || types[i].type === 'week' || types[j].type === 'month' || types[j].type === 'week';
+ if ((types[i] != types[j]) && monthOrWeek) {
+ test(function() {
+ assert_implements(browserSupportsInputTypeOf(types[i].type), "Support for input type " + types[i].type + " is required for this test.");
+ assert_implements(browserSupportsInputTypeOf(types[j].type), "Support for input type " + types[j].type + " is required for this test.");
+ var input = document.createElement("input");
+ var expected = INITIAL_VALUE;
+ input.type = types[i].type;
+ if (types[i].type === "file") {
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ input.value = expected;
+ });
+ assert_equals(input.value, "");
+ } else if (types[j].type === "file") {
+ input.value = expected;
+ input.type = types[j].type; // change state
+ assert_equals(input.value, "");
+ } else {
+ input.value = expected;
+ expected = input.value;
+
+ const previouslySelectable = (input.selectionStart !== null);
+
+ if (previouslySelectable) {
+ input.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
+ }
+
+ input.type = types[j].type; // change state
+
+ var preSanitizeValue = expected;
+ // type[j] sanitization
+ if (types[j].sanitizer) {
+ expected = types[j].sanitizer(expected);
+ }
+
+ // type[j] defaultValue
+ if (expected === "" && types[j].defaultValue) {
+ expected = types[j].defaultValue;
+ }
+
+ assert_equals(input.value, expected, "input.value should be '" + expected + "' after change of state");
+
+ const nowSelectable = (input.selectionStart !== null);
+
+ if (nowSelectable) {
+ if (previouslySelectable) {
+ // Value might change after sanitization. The following checks are only valid when the value stays the same.
+ if (preSanitizeValue === expected) {
+ assert_equals(input.selectionStart, selectionStart, "selectionStart should be unchanged");
+ assert_equals(input.selectionEnd, selectionEnd, "selectionEnd should be unchanged");
+ assert_equals(input.selectionDirection, selectionDirection, "selectionDirection should be unchanged");
+ }
+ } else {
+ assert_equals(input.selectionStart, 0, "selectionStart should be 0");
+ assert_equals(input.selectionEnd, 0, "selectionEnd should be 0");
+ assert_equals(input.selectionDirection, noneDirectionResult,
+ `selectionDirection should be '{noneDirectionResult}'`);
+ }
+ }
+ }
+ }, "change state from " + types[i].type + " to " + types[j].type);
+ }
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state.html
new file mode 100644
index 0000000000..5fb4cc9b56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/type-change-state.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Input element's type attribute changes state</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-input-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+ const INITIAL_VALUE = " foo\rbar ";
+
+ // Sanitize algorithm implementations only for values used in this test.
+ function sanitizeText(value) {
+ switch (value) {
+ case INITIAL_VALUE: return " foobar ";
+ case " foobar ": return value;
+ case "foobar": return value;
+ case "50": return value;
+ case "#000000": return value;
+ case "": return value;
+ default: throw new DOMException(`Internal Error: Should add support of "${value}"`, "NotSupportedError");
+ }
+ }
+ function sanitizeEmailOrUrl(value) {
+ switch (value) {
+ case INITIAL_VALUE: return "foobar";
+ case " foobar ": return "foobar";
+ case "foobar": return value;
+ case "50": return value;
+ case "#000000": return value;
+ case "": return value;
+ default: throw new DOMException(`Internal Error: Should add support of "${value}"`, "NotSupportedError");
+ }
+ }
+ function sanitizeTemporal(value) {
+ // We have no test cases using valid temporal values.
+ return "";
+ }
+ function sanitizeNumber(value) {
+ switch (value) {
+ case "50": return value;
+ default:
+ // We have no test cases using valid numbers other than "50".
+ return "";
+ }
+ }
+ function sanitizeRange(value) {
+ // We have no test cases using valid numbers other than "50".
+ return "50";
+ }
+ function sanitizeColor(value) {
+ // We have no test cases using valid colors other than "#000000".
+ return "#000000";
+ }
+ function browserSupportsInputTypeOf(inputType) {
+ var inputTest = document.createElement("input");
+ inputTest.type = inputType;
+ return (inputTest.type === inputType);
+ }
+
+
+ var types = [
+ { type: "hidden" },
+ { type: "text", sanitizer: sanitizeText },
+ { type: "search", sanitizer: sanitizeText },
+ { type: "tel", sanitizer: sanitizeText },
+ { type: "url", sanitizer: sanitizeEmailOrUrl },
+ { type: "email", sanitizer: sanitizeEmailOrUrl },
+ { type: "password", sanitizer: sanitizeText },
+ { type: "datetime-local", sanitizer: sanitizeTemporal },
+ { type: "date", sanitizer: sanitizeTemporal },
+ { type: "time", sanitizer: sanitizeTemporal },
+ { type: "number", sanitizer: sanitizeNumber },
+ { type: "range", sanitizer: sanitizeRange },
+ { type: "color", sanitizer: sanitizeColor },
+ { type: "checkbox", defaultValue: "on" },
+ { type: "radio", defaultValue: "on" },
+ { type: "file" },
+ { type: "submit" },
+ { type: "image" },
+ { type: "reset" },
+ { type: "button" }
+ ];
+
+ const selectionStart = 2;
+ const selectionEnd = 5;
+ const selectionDirection = "backward";
+
+ // Obtain selectionDirection after setting it to "none".
+ // Some platforms don't support "none" direction, and "forward" is returned
+ // in such platforms.
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#set-the-selection-direction
+ function testNoneDirection() {
+ const input = document.createElement("input");
+ input.selectionDirection = "none";
+ return input.selectionDirection;
+ }
+ const noneDirectionResult = testNoneDirection();
+
+ for (var i = 0; i < types.length; i++) {
+ for (var j = 0; j < types.length; j++) {
+ if (types[i] != types[j]) {
+ test(function() {
+ assert_implements(browserSupportsInputTypeOf(types[i].type), "Support for input type " + types[i].type + " is required for this test.");
+ assert_implements(browserSupportsInputTypeOf(types[j].type), "Support for input type " + types[j].type + " is required for this test.");
+ var input = document.createElement("input");
+ var expected = INITIAL_VALUE;
+ input.type = types[i].type;
+ if (types[i].type === "file") {
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ input.value = expected;
+ });
+ assert_equals(input.value, "");
+ } else if (types[j].type === "file") {
+ input.value = expected;
+ input.type = types[j].type; // change state
+ assert_equals(input.value, "");
+ } else {
+ input.value = expected;
+ expected = input.value;
+
+ const previouslySelectable = (input.selectionStart !== null);
+
+ if (previouslySelectable) {
+ input.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
+ }
+
+ input.type = types[j].type; // change state
+
+ var preSanitizeValue = expected;
+ // type[j] sanitization
+ if (types[j].sanitizer) {
+ expected = types[j].sanitizer(expected);
+ }
+
+ // type[j] defaultValue
+ if (expected === "" && types[j].defaultValue) {
+ expected = types[j].defaultValue;
+ }
+
+ assert_equals(input.value, expected, "input.value should be '" + expected + "' after change of state");
+
+ const nowSelectable = (input.selectionStart !== null);
+
+ if (nowSelectable) {
+ if (previouslySelectable) {
+ // Value might change after sanitization. The following checks are only valid when the value stays the same.
+ if (preSanitizeValue === expected) {
+ assert_equals(input.selectionStart, selectionStart, "selectionStart should be unchanged");
+ assert_equals(input.selectionEnd, selectionEnd, "selectionEnd should be unchanged");
+ assert_equals(input.selectionDirection, selectionDirection, "selectionDirection should be unchanged");
+ }
+ } else {
+ assert_equals(input.selectionStart, 0, "selectionStart should be 0");
+ assert_equals(input.selectionEnd, 0, "selectionEnd should be 0");
+ assert_equals(input.selectionDirection, noneDirectionResult,
+ `selectionDirection should be '{noneDirectionResult}'`);
+ }
+ }
+ }
+ }, "change state from " + types[i].type + " to " + types[j].type);
+ }
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/url.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/url.html
new file mode 100644
index 0000000000..aafa0ced9d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/url.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Input url</title>
+ <link rel="author" title="Hyeonseok Shin" href="mailto:hyeonseok@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#url-state-%28type=url%29">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>Input url</h1>
+ <div style="display: none">
+ <input type="url" id="type_support" />
+ <input type="url" id="set_value_LF" />
+ <input type="url" id="set_value_CR" />
+ <input type="url" id="set_value_CRLF" />
+ <input type="url" id="value_with_CRLF" value="a&#x000D;&#x000A;a" />
+ <input type="url" id="value_with_leading_trailing_white_space" value=" aa " />
+ <input type="url" id="value_with_leading_trailing_inner_white_space" value=" a a " />
+ </div>
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+ test(function(){
+ var element = document.getElementById('type_support');
+ assert_equals(element.type, 'url');
+ }, 'url type supported on input element');
+
+ test(function(){
+ var element = document.getElementById('set_value_LF');
+ element.value = 'a\u000Aa';
+ assert_equals(element.value, 'aa');
+
+ element = document.getElementById('set_value_CR');
+ element.value = 'a\u000Da';
+ assert_equals(element.value, 'aa');
+
+ element = document.getElementById('set_value_CRLF');
+ element.value = 'a\u000D\u000Aa';
+ assert_equals(element.value, 'aa');
+ }, 'The value must not be set with "LF" (U+000A) or "CR" (U+000D)');
+
+ test(function(){
+ var element = document.getElementById('value_with_CRLF');
+ assert_equals(element.value, 'aa');
+ }, 'The value sanitization algorithm is as follows: Strip line breaks from the value');
+
+ test(function(){
+ var element = document.getElementById('value_with_leading_trailing_white_space');
+ assert_equals(element.value, 'aa');
+
+ element = document.getElementById('value_with_leading_trailing_inner_white_space');
+ assert_equals(element.value, 'a a');
+ }, 'The value sanitization algorithm is as follows: Strip leading and trailing whitespace from the value.');
+ </script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode-weekmonth.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode-weekmonth.html
new file mode 100644
index 0000000000..c4a241016b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode-weekmonth.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Input element value mode</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+// MODE DEFAULT
+test(function () {
+ var input = document.createElement("input");
+ input.type = "month";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type month without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "month";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type month with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "week";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type week without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "week";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type week with value attribute");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode.html
new file mode 100644
index 0000000000..37f3a7bce9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/valueMode.html
@@ -0,0 +1,304 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Input element value mode</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+// MODE DEFAULT
+test(function () {
+ var input = document.createElement("input");
+ input.type = "hidden";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type hidden without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "hidden";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type hidden with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "submit";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type submit without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "submit";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type submit with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "image";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type image without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "image";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type image with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "reset";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type reset without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "reset";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type reset with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "button";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type button without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "button";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type button with value attribute");
+
+// MODE DEFAULT/ON
+test(function () {
+ var input = document.createElement("input");
+ input.type = "checkbox";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type checkbox without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "checkbox";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type checkbox with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "radio";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type radio without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "radio";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\r\r\n\n\0");
+}, "value IDL attribute of input type radio with value attribute");
+
+// MODE VALUE
+test(function () {
+ var input = document.createElement("input");
+ input.type = "text";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type text without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "text";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type text with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "search";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type search without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "search";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type search with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "tel";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type tel without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "tel";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type tel with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "url";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type url without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "url";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type url with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "email";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type email without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "email";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type email with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "password";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type password without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "password";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "foo\0");
+}, "value IDL attribute of input type password with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "datetime-local";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type datetime-local without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "datetime-local";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type datetime-local with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "date";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type date without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "date";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type date with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "time";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type time without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "time";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type time with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "number";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type number without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "number";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "");
+}, "value IDL attribute of input type number with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "range";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "50");
+}, "value IDL attribute of input type range without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "range";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "50");
+}, "value IDL attribute of input type range with value attribute");
+
+test(function () {
+ var input = document.createElement("input");
+ input.type = "color";
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "#000000");
+}, "value IDL attribute of input type color without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "color";
+ input.setAttribute("value", "bar");
+ input.value = "foo\r\r\n\n\0";
+ assert_equals(input.value, "#000000");
+}, "value IDL attribute of input type color with value attribute");
+
+// MODE FILENAME
+test(function () {
+ var input = document.createElement("input");
+ input.type = "file";
+
+ for (const emptyValue of ["", null]) {
+ input.value = emptyValue;
+ assert_equals(input.value, "", `input.value is empty after assigning ${emptyValue}`);
+ }
+
+ for (const invalidValue of ["foo", 10, undefined]) {
+ assert_throws_dom("InvalidStateError", () => {
+ input.value = invalidValue;
+ });
+ assert_equals(input.value, "", `input.value is empty after assigning ${invalidValue}`);
+ }
+}, "value IDL attribute of input type file without value attribute");
+test(function() {
+ var input = document.createElement("input");
+ input.type = "file";
+ input.setAttribute("value", "bar");
+ assert_equals(input.value, "", "input.value is empty even with a value attribute");
+
+ input.value = "";
+ assert_equals(input.getAttribute("value"), "bar", "Setting input.value does not change the value attribute");
+}, "value IDL attribute of input type file with value attribute");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/week.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/week.html
new file mode 100644
index 0000000000..925acfdaf8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/week.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Form input type=week</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#weeks">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#week-state-(type=week)">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ var weeks = [
+ {value: "", expected: "", testname: "empty value"},
+ {value: "2014-W52", expected: "2014-W52", testname: "Valid value: Value should be 2014-W52"},
+ {value: "2014-W53", expected: "", testname: "2014 has 52 weeks: Value should be empty"},
+ {value: "2015-W53", expected: "2015-W53", testname: "2015 has 53 weeks: Value should be 2015-W53"},
+ {value: "2014", expected: "", testname: "Invalid value: year only"},
+ {value: "2014W", expected: "", testname: "Invalid value: no week number"},
+ {value: "2014W52", expected: "", testname: "Invalid value: no '-' (U+002D)"},
+ {value: "-W52", expected: "", testname: "Invalid value: yearless week"},
+ {value: "2017-w52", expected: "", testname: "Invalid value: should be capital letter 'W'"},
+ {value: "2017-W52-", expected: "", testname: "Invalid value: incorrect with '-' at the end"},
+ {value: "2017-W52-12", expected: "", testname: "Invalid value: value should be two parts"},
+ {value: "W52", expected: "", testname: "Invalid value: yearless week and no '-' (U+002D)"},
+ {value: "2014-W03", attributes: { min: "2014-W02" }, expected: "2014-W03", testname: "Value >= min attribute"},
+ {value: "2014-W01", attributes: { min: "2014-W02" }, expected: "2014-W01", testname: "Value < min attribute"},
+ {value: "2014-W10", attributes: { max: "2014-W11" }, expected: "2014-W10", testname: "Value <= max attribute"},
+ {value: "2014-W12", attributes: { max: "2014-W11" }, expected: "2014-W12", testname: "Value > max attribute"}
+ ];
+ for (var i = 0; i < weeks.length; i++) {
+ var w = weeks[i];
+ test(function() {
+ var input = document.createElement("input");
+ input.type = "week";
+ input.value = w.value;
+ for(var attr in w.attributes) {
+ input[attr] = w.attributes[attr];
+ }
+ assert_equals(input.value, w.expected);
+ }, w.testname);
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-interactive-content.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-interactive-content.html
new file mode 100644
index 0000000000..300d09cdda
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-interactive-content.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Label event handling when a descendant interactive content is clicked</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<label id=label></label>
+<template id=interactive-content>
+ <a href="about:blank" onclick="event.preventDefault()"></a>
+ <audio controls></audio>
+ <button></button>
+ <details></details>
+ <embed>
+ <iframe></iframe>
+ <img usemap="">
+ <input>
+ <label>label</label>
+ <select></select>
+ <textarea></textarea>
+ <video controls></video>
+</template>
+
+<script>
+"use strict";
+
+const interactiveContent = document.getElementById("interactive-content");
+const interactiveElements = Array.from(interactiveContent.content.children);
+const label = document.getElementById("label");
+
+for (const srcInteractiveElement of interactiveElements) {
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const interactiveElement = srcInteractiveElement.cloneNode();
+ label.appendChild(interactiveElement);
+
+ let clicked = 0;
+ interactiveElement.addEventListener("click", () => {
+ clicked++;
+ });
+ interactiveElement.click();
+ assert_equals(clicked, 1, "clicking interactive content");
+
+ clicked = 0;
+ const span = document.createElement("span");
+ interactiveElement.appendChild(span);
+ span.click();
+ assert_equals(clicked, 1, "clicking descendant of interactive content");
+ }, `interactive content ${srcInteractiveElement.outerHTML} as first child of <label>`);
+
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const interactiveElement = srcInteractiveElement.cloneNode();
+ const div = document.createElement("div");
+ div.appendChild(interactiveElement);
+ label.appendChild(div);
+
+ let clicked = 0;
+ interactiveElement.addEventListener("click", () => {
+ clicked++;
+ });
+ interactiveElement.click();
+ assert_equals(clicked, 1, "clicking nested interactive content");
+
+ clicked = 0;
+ const span = document.createElement("span");
+ interactiveElement.appendChild(span);
+ span.click();
+ assert_equals(clicked, 1, "clicking descendant of nested interactive content");
+ }, `interactive content ${srcInteractiveElement.outerHTML} deeply nested under <label>`);
+
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const button = document.createElement("button");
+ label.appendChild(button);
+
+ const interactiveElement = srcInteractiveElement.cloneNode();
+ label.appendChild(interactiveElement);
+
+ let buttonClicked = 0;
+ button.addEventListener("click", () => {
+ buttonClicked++;
+ });
+
+ let clicked = 0;
+ interactiveElement.addEventListener("click", () => {
+ clicked++;
+ });
+ interactiveElement.click();
+ assert_equals(clicked, 1, "clicking nested interactive content");
+ assert_equals(buttonClicked, 0, "clicking nested interactive content should not click button");
+
+ clicked = 0;
+ const span = document.createElement("span");
+ interactiveElement.appendChild(span);
+ span.click();
+ assert_equals(clicked, 1, "clicking descendant of nested interactive content");
+ assert_equals(buttonClicked, 0, "clicking descendant of nested interactive content should not click button");
+ }, `interactive content ${srcInteractiveElement.outerHTML} as second child under <label>`);
+}
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-labelable-content.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-labelable-content.html
new file mode 100644
index 0000000000..5563ef1e3c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-labelable-content.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Label event handling when a descendant labelable but not interactive element is clicked</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<label id=label></label>
+<template id=labelable-not-interactive-content>
+ <meter></meter>
+ <output></output>
+ <progress></progress>
+</template>
+
+<script>
+"use strict";
+
+const template = document.getElementById("labelable-not-interactive-content");
+const labelableNotInteractiveElements = Array.from(template.content.children);
+const label = document.getElementById("label");
+
+// This part may be subject to platform-dependent operations in the spec, so we
+// only check for obvious errors. (Clicking once should register at least one
+// click, but less than 30 clicks.) See
+// https://github.com/whatwg/html/issues/5415 for possibly tightening this up.
+function checkClickCount(clicked, description) {
+ assert_greater_than(clicked, 0, description);
+ assert_less_than(clicked, 30, description);
+}
+
+for (const srcElement of labelableNotInteractiveElements) {
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const element = srcElement.cloneNode();
+ label.appendChild(element);
+
+ let clicked = 0;
+ element.addEventListener("click", () => {
+ clicked++;
+ });
+ element.click();
+ checkClickCount(clicked, "clicking labelable content");
+
+ clicked = 0;
+ const span = document.createElement("span");
+ element.appendChild(span);
+ span.click();
+ checkClickCount(clicked, "clicking descendant of labelable content");
+ }, `labelable element ${srcElement.outerHTML} as first child of <label>`);
+
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const element = srcElement.cloneNode();
+ const div = document.createElement("div");
+ div.appendChild(element);
+ label.appendChild(div);
+
+ let clicked = 0;
+ element.addEventListener("click", () => {
+ clicked++;
+ });
+ element.click();
+ checkClickCount(clicked, "clicking nested labelable content");
+
+ clicked = 0;
+ const span = document.createElement("span");
+ element.appendChild(span);
+ span.click();
+ checkClickCount(clicked, "clicking descendant of nested labelable content");
+ }, `labelable element ${srcElement.outerHTML} deeply nested under <label>`);
+
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const button = document.createElement("button");
+ label.appendChild(button);
+
+ const element = srcElement.cloneNode();
+ label.appendChild(element);
+
+ let buttonClicked = 0;
+ button.addEventListener("click", () => {
+ buttonClicked++;
+ });
+
+ let clicked = 0;
+ element.addEventListener("click", () => {
+ clicked++;
+ });
+ element.click();
+ assert_equals(clicked, 1, "clicking nested labelable content");
+ assert_equals(buttonClicked, 1, "clicking nested labelable content should click button");
+
+ buttonClicked = 0;
+ clicked = 0;
+ const span = document.createElement("span");
+ element.appendChild(span);
+ span.click();
+ assert_equals(clicked, 1, "clicking descendant of nested labelable content");
+ assert_equals(buttonClicked, 1, "clicking descendant of nested labelable content should not click button");
+ }, `labelable element ${srcElement.outerHTML} as second child under <label>`);
+}
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-unlabelable-content.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-unlabelable-content.html
new file mode 100644
index 0000000000..285cd8c041
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/clicking-noninteractive-unlabelable-content.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Label event handling when a descendant noninteractive and unlabelable content is clicked</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<label id=label></label>
+<template id=noninteractive-unlabelable-content>
+ <div></div>
+ <svg></svg>
+
+ <!-- These are "almost interactive": they could become interactive with the
+ addition/removal of a non-tabindex attribute. -->
+ <a></a>
+ <audio></audio>
+ <img>
+ <input type=hidden>
+ <video></video>
+
+ <!-- These are considered interactive content for the purpose of <label> in a
+ previous version of the HTML Standard, but no longer. -->
+ <a tabindex=""></a>
+ <audio tabindex=""></audio>
+ <div tabindex=""></div>
+ <img tabindex="">
+ <input type=hidden tabindex="">
+ <object></object>
+ <object tabindex=""></object>
+ <object usemap=""></object>
+ <video tabindex=""></video>
+</template>
+
+<script>
+"use strict";
+
+const template = document.getElementById("noninteractive-unlabelable-content");
+{
+ const details = document.createElementNS("http://www.w3.org/2000/svg", "details");
+ template.content.appendChild(details);
+}
+
+const elements = Array.from(template.content.children);
+const label = document.getElementById("label");
+
+for (const srcElement of elements) {
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const element = srcElement.cloneNode();
+ label.appendChild(element);
+
+ let clicked = 0;
+ element.addEventListener("click", () => {
+ clicked++;
+ });
+ element.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ assert_equals(clicked, 1, "clicking interactive content");
+
+ clicked = 0;
+ const span = document.createElement("span");
+ element.appendChild(span);
+ span.click();
+ assert_equals(clicked, 1, "clicking descendant of interactive content");
+ }, `noninteractive unlabelable content ${srcElement.outerHTML} as first child of <label>`);
+
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const element = srcElement.cloneNode();
+ const div = document.createElement("div");
+ div.appendChild(element);
+ label.appendChild(div);
+
+ let clicked = 0;
+ element.addEventListener("click", () => {
+ clicked++;
+ });
+ element.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ assert_equals(clicked, 1, "clicking nested interactive content");
+
+ clicked = 0;
+ const span = document.createElement("span");
+ element.appendChild(span);
+ span.click();
+ assert_equals(clicked, 1, "clicking descendant of nested interactive content");
+ }, `noninteractive unlabelable content ${srcElement.outerHTML} deeply nested under <label>`);
+
+ test(t => {
+ t.add_cleanup(() => {
+ label.innerHTML = "";
+ });
+
+ const button = document.createElement("button");
+ label.appendChild(button);
+
+ const element = srcElement.cloneNode();
+ label.appendChild(element);
+
+ let buttonClicked = 0;
+ button.addEventListener("click", () => {
+ buttonClicked++;
+ });
+
+ let clicked = 0;
+ element.addEventListener("click", () => {
+ clicked++;
+ });
+ element.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ assert_equals(clicked, 1, "clicking noninteractive unlabelable content");
+ assert_equals(buttonClicked, 1, "clicking noninteractive unlabelable content should click button");
+
+ buttonClicked = 0;
+ clicked = 0;
+ const span = document.createElement("span");
+ element.appendChild(span);
+ span.click();
+ assert_equals(clicked, 1, "clicking descendant of nested noninteractive unlabelable content");
+ assert_equals(
+ buttonClicked, 1,
+ "clicking descendant of nested noninteractive unlabelable content should click button"
+ );
+ }, `noninteractive unlabelable content ${srcElement.outerHTML} as second child under <label>`);
+}
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/forward-focus-to-associated-element.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/forward-focus-to-associated-element.html
new file mode 100644
index 0000000000..86e3f652af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/forward-focus-to-associated-element.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<title>label element focus forwarding via "for" attribute or nested labelable element</title>
+<link rel="author" title="yaycmyk" href="mailto:evan@yaycmyk.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-label-element:the-label-element-10">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form id="test">
+ <input id="input-a" type="checkbox">
+ <label id="label-a" for="input-a">a</label>
+
+ <label id="label-b">
+ <input id="input-b" type="checkbox" /> b
+ </label>
+
+ <label id="label-c" tabindex="0">
+ <input id="input-c" type="checkbox" /> c
+ </label>
+
+ <label id="label-d" tabindex="-1">
+ <input id="input-d" type="checkbox" /> d
+ </label>
+
+ <label id="label-e" tabindex="">
+ <input id="input-e" type="checkbox" /> e
+ </label>
+
+ <input id="input-f" type="checkbox">
+ <label id="label-f" for="input-f" tabindex="0" style="display:none">f</label>
+</form>
+<script>
+ "use strict";
+
+ async_test(t => {
+ const label = document.getElementById("label-a");
+ const input = document.getElementById("input-a");
+
+ input.addEventListener("focus", t.step_func_done());
+ label.addEventListener("focus", t.unreached_func("Label should not receive focus"));
+
+ label.focus();
+
+ }, "focusing a label with for attribute should forward focus to the associated element");
+
+ async_test(t => {
+ const label = document.getElementById("label-b");
+ const input = document.getElementById("input-b");
+
+ input.addEventListener("focus", t.step_func_done());
+ label.addEventListener("focus", t.unreached_func("Label should not receive focus"));
+
+ label.focus();
+
+ }, "focusing a label without for attribute should fowrad focus to the first labelable child");
+
+ async_test(t => {
+ const label = document.getElementById("label-c");
+ const input = document.getElementById("input-c");
+
+ input.addEventListener("focus", t.unreached_func("Input should not receive focus"));
+ label.addEventListener("focus", t.step_func_done());
+
+ label.focus();
+
+ }, "focusing a label with tabindex should not forward focus to the labelable element");
+
+ async_test(t => {
+ const label = document.getElementById("label-d");
+ const input = document.getElementById("input-d");
+
+ input.addEventListener("focus", t.unreached_func("Input should not receive focus"));
+ label.addEventListener("focus", t.step_func_done());
+
+ label.focus();
+
+ }, "focusing a label with negative tabindex should not forward focus to the labelable element");
+
+ async_test(t => {
+ const label = document.getElementById("label-e");
+ const input = document.getElementById("input-e");
+
+ label.addEventListener("focus", t.unreached_func("Label should not receive focus"));
+ input.addEventListener("focus", t.step_func_done());
+
+ label.focus();
+
+ }, "focusing a label with empty tabindex should forward focus to the labelable element");
+
+ async_test(t => {
+ const label = document.getElementById("label-f");
+ const input = document.getElementById("input-f");
+
+ label.addEventListener("focus", t.unreached_func("Label should not receive focus"));
+ input.addEventListener("focus", t.step_func_done());
+
+ label.focus();
+
+ }, "focusing a hidden label with tabindex should forward focus to the labelable element");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/iframe-label-attributes.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/iframe-label-attributes.html
new file mode 100644
index 0000000000..3f08a29094
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/iframe-label-attributes.html
@@ -0,0 +1,8 @@
+<html>
+ <body>
+ <label>
+ <div id="div1"></div>
+ </label>
+ <label for="test13"></label>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.sub.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.sub.html
new file mode 100644
index 0000000000..3c8591c7ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.sub.html
@@ -0,0 +1,339 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: The label element</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form id="fm" style="display:none">
+ <label id="lbl0" for="test0"></label>
+ <b id="test0"></b>
+
+ <input id="test1"></input>
+
+ <label id="lbl1">
+ <a id="test2"></a>
+ <div><input id="test3"></div>
+ <input id="test4">
+ </label>
+
+ <label id="lbl2" for="testx">
+ <input id="test5">
+ </label>
+
+ <label id="lbl3" for="test6">
+ <b id="test6"></b>
+ <input id="test6" class="class1">
+ </label>
+
+ <label id="lbl4" for="">
+ <input id="" class="class2">
+ </label>
+
+ <label id="lbl5" for="test7"></label>
+ <input id="test7">
+
+ <label id="lbl7">
+ <label id="lbl8">
+ <div id="div1">
+ <input id="test8">
+ </div>
+ </label>
+ </label>
+ <div id="div2"></div>
+
+ <label id="lbl9">
+ <label id="lbl10" for="test10">
+ <div id="div3">
+ <input id="test9">
+ </div>
+ </label>
+ </label>
+ <div id="div4"><input id="test10"></div>
+
+ <label id="lbl11">
+ <object id="obj">
+ <input id="test11">
+ <input id="test12">
+ </object>
+ </label>
+ <label id="lbl12" for="test12"><div id="div5"></div></label>
+
+ <label id="lbl13">
+ <p id="p1">
+ <input id="test13">
+ </p>
+ </label>
+
+ <div id="div6">
+ <div id="div7">
+ <label id="lbl14">
+ <label id="lbl15" for="test15">
+ <input id="test14">
+ </label>
+ </label>
+ </div>
+ </div>
+ <input id="test15">
+</form>
+
+<label id="lbl6" for="test7"></label>
+<div id="content" style="display: none">
+<script>
+
+ //control attribute
+ test(function () {
+ assert_not_equals(document.getElementById("lbl0").control, document.getElementById("test0"),
+ "An element that's not a labelable element can't be a label element's labeled control.");
+ assert_equals(document.getElementById("lbl0").control, null,
+ "A label element whose 'for' attribute doesn't reference any labelable element shouldn't have any labeled control.");
+ }, "A label element with a 'for' attribute should only be associated with a labelable element.");
+
+ test(function () {
+ var label = document.createElement("label");
+ label.htmlFor = "test1";
+ assert_not_equals(label.control, document.getElementById("test1"),
+ "A label element not in a document should not label an element in a document.");
+ document.body.appendChild(label);
+ assert_equals(label.control, document.getElementById("test1"));
+ label.remove();
+ }, "A label element not in a document can not label any element in the document.");
+
+ test(function () {
+ var labels = document.getElementById("test3").labels;
+ assert_equals(document.getElementById("lbl1").control, document.getElementById("test3"),
+ "The first labelable descendant of a label element should be its labeled control.");
+
+ var input = document.createElement("input");
+ document.getElementById("lbl1").insertBefore(input, document.getElementById("test2"));
+ assert_equals(document.getElementById("lbl1").control, input,
+ "The first labelable descendant of a label element in tree order should be its labeled control.");
+ assert_equals(input.labels.length, 1,
+ "The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
+ assert_equals(labels.length, 0,
+ "The number of labels should be 0 if it's not the first labelable descendant of a label element.");
+ input.remove();
+ }, "The labeled control for a label element that has no 'for' attribute is the first labelable element which is a descendant of that label element.");
+
+ test(function () {
+ assert_equals(document.getElementById("lbl2").control, null,
+ "The label's 'control' property should return null if its 'for' attribute points to an inexistent element.");
+ }, "The 'for' attribute points to an inexistent id.");
+
+ test(function () {
+ assert_equals(document.getElementById("lbl3").control, null, "The label should have no control associated.");
+ assert_equals(document.querySelector(".class1").labels.length, 0);
+ }, "A non-control follows by a control with same ID.");
+
+ test(function () {
+ assert_equals(document.getElementById("lbl4").control, null,
+ "A label element with an empty 'for' attribute should not associate with anything.");
+ }, "The 'for' attribute is an empty string.");
+
+ //labels attribute
+ test(function () {
+ var labels = document.getElementById("test7").labels;
+ assert_true(labels instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels.length, 2,
+ "The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
+ assert_array_equals(labels, [document.getElementById("lbl5"), document.getElementById("lbl6")],
+ "The labels for a form control should be returned in tree order.");
+
+ var newLabel = document.createElement("label");
+ newLabel.htmlFor = "test7";
+ document.getElementById("fm").insertBefore(newLabel, document.getElementById("lbl0"));
+ assert_array_equals(document.getElementById("test7").labels, [newLabel, document.getElementById("lbl5"), document.getElementById("lbl6")],
+ "The labels for a form control should be returned in tree order.");
+ newLabel.remove();
+ }, "A form control has multiple labels.");
+
+ test(function () {
+ var labels = document.getElementById("test8").labels;
+ assert_true(labels instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels.length, 2,
+ "The form control has two ancestors with no explicit associated label, and is the first labelable descendant.");
+ assert_array_equals(labels, [document.getElementById("lbl7"), document.getElementById("lbl8")],
+ "The labels for a form control should be returned in tree order.");
+
+ document.getElementById('div2').insertBefore(document.getElementById('div1'), document.getElementById('div2').firstChild);
+ assert_equals(labels.length, 0,
+ "The number of labels should be 0 after the labelable element is moved to outside of nested associated labels.");
+ }, "A labelable element is moved to outside of nested associated labels.");
+
+ test(function () {
+ var labels1 = document.getElementById("test9").labels;
+ var labels2 = document.getElementById("test10").labels;
+ assert_true(labels1 instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_true(labels2 instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels1.length, 1,
+ "The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
+ assert_equals(labels2.length, 1,
+ "The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
+ assert_array_equals(labels1, [document.getElementById("lbl9")],
+ "The labels for a form control should be returned in tree order.");
+ assert_array_equals(labels2, [document.getElementById("lbl10")],
+ "The labels for a form control should be returned in tree order.");
+ document.getElementById('div3').insertBefore(document.getElementById('div4'), document.getElementById('div3').firstChild);
+ assert_equals(labels1.length, 0,
+ "The number of labels should be 0 if it's not the first labelable descendant of a label element.");
+ assert_equals(labels2.length, 2,
+ "The form control has an ancestor with an explicit associated label, and is the first labelable descendant.");
+ }, "A labelable element is moved to inside of nested associated labels.");
+
+ test(function () {
+ var labels1 = document.getElementById("test11").labels;
+ var labels2 = document.getElementById("test12").labels;
+ assert_true(labels1 instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_true(labels2 instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels1.length, 1,
+ "The form control has an ancestor with no explicit associated label, and it is the first labelable descendant.");
+ assert_equals(labels2.length, 1,
+ "The number of labels should be 1 since there is a label with a 'for' attribute associated with this labelable element.");
+ assert_array_equals(labels1, [document.getElementById("lbl11")],
+ "The labels for a form control should be returned in tree order.");
+ assert_array_equals(labels2, [document.getElementById("lbl12")],
+ "The labels for a form control should be returned in tree order.");
+ document.getElementById('div5').appendChild(document.getElementById('obj'));
+ assert_equals(labels1.length, 0,
+ "The number of labels should be 0 after the labelable element is moved to outside of associated label.");
+ assert_equals(labels2.length, 1,
+ "The number of labels should be 1 after the labelable element is moved to outside of associated label.");
+ }, "A labelable element which is a descendant of non-labelable element is moved to outside of associated label.");
+
+ async_test(function () {
+ var labels = document.getElementById("test13").labels;
+ assert_true(labels instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels.length, 1,
+ "The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
+ assert_array_equals(labels, [document.getElementById("lbl13")],
+ "The labels for a form control should be returned in tree order.");
+ let iframe = document.createElement('iframe');
+
+ iframe.onload = this.step_func_done(() => {
+ iframe.contentWindow.document.getElementById("div1").appendChild(document.getElementById("p1"));
+ assert_equals(labels.length, 2,
+ "The number of labels should be 2 after the labelable element is moved to iframe.");
+ });
+
+ iframe.setAttribute('src', 'http://{{domains[]}}:{{ports[http][0]}}/html/semantics/forms/the-label-element/iframe-label-attributes.html');
+ document.body.appendChild(iframe);
+ }, "A labelable element is moved to iframe.");
+
+ test(function () {
+ var test14 = document.getElementById("test14");
+ var labels1 = test14.labels;
+ var labels2 = document.getElementById("test15").labels;
+ assert_true(labels1 instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels1.length, 1,
+ "The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
+ assert_equals(labels2.length, 1,
+ "The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
+ assert_array_equals(labels1, [document.getElementById("lbl14")],
+ "The labels for a form control should be returned in tree order.");
+ assert_array_equals(labels2, [document.getElementById("lbl15")],
+ "The labels for a form control should be returned in tree order.");
+
+ document.getElementById('div6').removeChild(document.getElementById('div7'));
+ assert_equals(labels1.length, 1,
+ "The number of labels should be 1 after the labelable element is removed but label element is still in the same tree.");
+ assert_equals(labels2.length, 0,
+ "The number of labels should be 0 since there is no label with a 'for' attribute associated with this labelable element.");
+ test14.remove();
+ assert_equals(labels1.length, 0,
+ "The number of labels should be 0 after the labelable element is removed.");
+ }, "A div element which contains labelable element is removed.");
+
+ test(function () {
+ // <label><input id="test16"><label for="test16"></label></label>
+ var label1 = document.createElement('label');
+ label1.innerHTML = "<input id='test16'>";
+ var label2 = document.createElement('label');
+ label2.htmlFor = "test16";
+ label1.appendChild(label2);
+
+ var input = label1.firstChild;
+ var labels = input.labels;
+
+ assert_equals(labels.length, 2,
+ "The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
+ assert_true(labels instanceof NodeList,
+ "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(label1.control, input, "The first labelable descendant of a label element should be its labeled control.");
+ assert_equals(label2.control, input, "The labeled cotrol should be associated with the control whose ID is equal to the value of the 'for' attribute.");
+ }, "A labelable element not in a document can label element in the same tree.");
+
+ test(function () {
+ var root1 = document.getElementById('content').attachShadow({mode: 'open'});
+ assert_true(root1 instanceof DocumentFragment,
+ "ShadowRoot should be an instance of DocumentFragment.");
+ // <label><input id="shadow1"/></label><div id="div1"></div>
+ var label1 = document.createElement('label');
+ var input1 = document.createElement('input');
+ input1.setAttribute("id", "shadow1");
+ label1.appendChild(input1);
+ root1.appendChild(label1);
+
+ var div1 = document.createElement('div');
+ label1.appendChild(div1);
+ // <label for="shadow2"></label><input id="shadow2"/>
+ var root2 = div1.attachShadow({mode: 'open'});
+
+ assert_true(root2 instanceof DocumentFragment,
+ "ShadowRoot should be an instance of DocumentFragment.");
+ var label2 = document.createElement('label');
+ label2.setAttribute("for", "shadow2");
+
+ var input2 = document.createElement('input');
+ input2.setAttribute("id", "shadow2");
+ root2.appendChild(label2);
+ root2.appendChild(input2);
+
+ assert_equals(root1.getElementById("shadow1").labels.length, 1,
+ "The form control has an ancestor with no explicit associated label, and it is the first labelable descendant.");
+ assert_equals(root2.getElementById("shadow2").labels.length, 1,
+ "The number of labels should be 1 since there is a label with a 'for' attribute associated with this labelable element.");
+ }, "A labelable element inside the shadow DOM.");
+
+ test(function () {
+ var labels = document.getElementById("test3").labels;
+ assert_true(labels instanceof NodeList, "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels.length, 1, "The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
+ }, "A form control has an implicit label.");
+
+ test(function () {
+ var labels = document.getElementById("test4").labels;
+ assert_true(labels instanceof NodeList, "A form control's 'labels' property should be an instance of a NodeList.");
+ assert_equals(labels.length, 0, "The form control has an ancestor with no explicit associated label, but is *not* the first labelable descendant");
+ }, "A form control has no label 1.");
+
+ test(function () {
+ assert_equals(document.getElementById("test5").labels.length, 0,
+ "The number of labels should be 0 if the form control has an ancestor label element that the for attribute points to another control.");
+ assert_equals(document.getElementById("lbl2").control, null,
+ "The labeled cotrol should be associated with the control whose ID is equal to the value of the 'for' attribute.");
+ }, "A form control has no label 2.");
+
+ // form attribute
+ test(function () {
+ assert_equals(document.getElementById("lbl0").form, null,
+ "The 'form' property for a label should return null if label.control is null.");
+ }, "A label in a form without a control");
+
+ test(function () {
+ assert_equals(document.getElementById("lbl6").form, document.getElementById("fm"),
+ "The 'form' property for a label should return label.control.form.");
+ }, "A label outside a form with a control inside the form");
+
+ // htmlFor attribute
+ test(function () {
+ assert_equals(document.getElementById("lbl2").htmlFor, "testx");
+ }, "A label's htmlFor attribute must reflect the for content attribute");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/label-inside-anchor.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-inside-anchor.html
new file mode 100644
index 0000000000..316441c5f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-inside-anchor.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>label element: clicking on label containing inline element placed inside &lt;a&gt; </title>
+<link rel="author" title="Yu Han" href="mailto:yuzhehan@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-label-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<a href="javascript:void(0)" target="_blank">
+ <label for="peas"><span id="text">peas?</span></label>
+ <input type="checkbox" name="peas" id="peas">
+</a>
+<script>
+ const text = document.getElementById('text'),
+ peas_cb = document.getElementById('peas');
+
+ t1 = async_test("click on inline element inside a label that's placed inside a anchor should trigger default label behavior");
+
+ peas_cb.onchange = t1.step_func_done(function(e) {
+ assert_true(peas_cb.checked, "checkbox is checked");
+ });
+
+ t1.step(function() {
+ text.click();
+ });
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/labelable-elements.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/labelable-elements.html
new file mode 100644
index 0000000000..7943aa2be3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/labelable-elements.html
@@ -0,0 +1,174 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: labelable elements</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form style="display:none">
+ <output id="testoutput"></output>
+ <label id="lbl0" for="testoutput"></label>
+ <progress id="testprogress"></progress>
+ <label id="lbl1" for="testprogress"></label>
+ <select id="testselect"></select>
+ <label id="lbl2" for="testselect"></label>
+ <textarea id="testarea"></textarea>
+ <label id="lbl3" for="testarea"></label>
+ <button id="testButton"></button>
+ <label id="lbl4" for="testButton"></label>
+ <input type="hidden" id="testHidden">
+ <label id="lbl5" for="testHidden"></label>
+ <input type="radio" id="testRadio">
+ <label id="lbl6" for="testRadio"></label>
+ <keygen id="testkeygen">
+ <label id="lbl7" for="testkeygen"></label>
+ <meter id="testmeter"></meter>
+ <label id="lbl8" for="testmeter"></label>
+
+ <fieldset id="testfieldset"></fieldset>
+ <label id="lbl9" for="testfieldset"></label>
+ <label id="testlabel"></label>
+ <label id="lbl10" for="testlabel"></label>
+ <object id="testobject"></object>
+ <label id="lbl11" for="testobject"></label>
+ <img id="testimg">
+ <label id="lbl12" for="testimg"></label>
+</form>
+
+<script>
+function testLabelsAttr(formElementId, labelElementId) {
+ var elem = document.getElementById(formElementId);
+ if (labelElementId) {
+ assert_equals(elem.labels.length, 1);
+ assert_equals(elem.labels[0].id, labelElementId);
+ } else {
+ assert_equals(elem.labels.length, 0);
+ }
+}
+
+test(function() {
+ assert_equals(document.getElementById("lbl0").control.id, "testoutput", "An output element should be labelable.");
+}, "Check if the output element is a labelable element");
+
+test(function() {
+ testLabelsAttr("testoutput", "lbl0");
+}, "Check if the output element can access 'labels'");
+
+test(function() {
+ assert_equals(document.getElementById("lbl1").control.id, "testprogress", "A progress element should be labelable.");
+}, "Check if the progress element is a labelable element");
+
+test(function() {
+ testLabelsAttr("testprogress", "lbl1");
+}, "Check if the progress element can access 'labels'");
+
+test(function() {
+ assert_equals(document.getElementById("lbl2").control.id, "testselect", "A select element should be labelable.");
+}, "Check if the select element is a labelable element");
+
+test(function() {
+ testLabelsAttr("testselect", "lbl2");
+}, "Check if the select element can access 'labels'");
+
+test(function() {
+ assert_equals(document.getElementById("lbl3").control.id, "testarea", "A textarea element should be labelable.");
+}, "Check if the textarea element is a labelable form-element");
+
+test(function() {
+ testLabelsAttr("testarea", "lbl3");
+}, "Check if the textarea element can access 'labels'");
+
+test(function() {
+ assert_equals(document.getElementById("lbl4").control.id, "testButton", "A button element should be labelable.");
+}, "Check if the button element is a labelable element");
+
+test(function() {
+ testLabelsAttr("testButton", "lbl4");
+}, "Check if the button element can access 'labels'");
+
+test(function() {
+ assert_equals(document.getElementById("lbl5").control, null, "An input element in hidden state should not be labelable.");
+}, "Check if the hidden input element is not a labelable element.");
+
+test(function() {
+ var hiddenInput = document.getElementById("testHidden");
+ assert_equals(hiddenInput.labels, null, "input[type=hidden] must have null .labels");
+
+ this.add_cleanup(function () {
+ hiddenInput.type = "hidden";
+ });
+
+ hiddenInput.type = "text";
+ testLabelsAttr("testHidden", "lbl5");
+ var labels = hiddenInput.labels;
+
+ hiddenInput.type = "hidden";
+ assert_equals(labels.length, 0, "Retained .labels NodeList should be empty after input type changed to hidden");
+ assert_equals(hiddenInput.labels, null, ".labels NodeList should be null after input type changed to hidden");
+
+ hiddenInput.type = "checkbox";
+ assert_equals(labels, hiddenInput.labels, ".labels property must return the [SameObject] after input type is toggled back from 'hidden'");
+ assert_equals(hiddenInput.labels.length, 1, ".labels NodeList should contain the input after the input type is changed from 'hidden' to 'checkbox'");
+}, "Check if the hidden input element has null 'labels'");
+
+test(function() {
+ assert_equals(document.getElementById("lbl6").control.id, "testRadio", "An input element in radio state should be labelable.");
+}, "Check if the input element in radio state is a labelable element");
+
+test(function() {
+ testLabelsAttr("testRadio", "lbl6");
+}, "Check if the input element in radio state can access 'labels'");
+
+test(function() {
+ assert_not_equals(document.getElementById("lbl7").control, document.getElementById("testkeygen"));
+ assert_equals(document.getElementById("lbl7").control, null, "A keygen element should not be labelable.");
+}, "Check if the keygen element is not a labelable element");
+
+test(function() {
+ assert_equals(document.getElementById("testkeygen").labels, undefined);
+}, "Check if the keygen element can access 'labels'");
+
+test(function() {
+ assert_equals(document.getElementById("lbl8").control.id, "testmeter", "A meter element should be labelable.");
+}, "Check if the meter element is a labelable element");
+
+test(function() {
+ testLabelsAttr("testmeter", "lbl8");
+}, "Check if the meter element can access 'labels'");
+
+test(function() {
+ assert_not_equals(document.getElementById("lbl9").control, document.getElementById("testfieldset"));
+ assert_equals(document.getElementById("lbl9").control, null, "A fieldset element should not be labelable.");
+}, "Check if the fieldset element is not a labelable element");
+
+test(function() {
+ assert_equals(document.getElementById("testfieldset").labels, undefined);
+}, "Check if the fieldset element can access 'labels'");
+
+test(function() {
+ assert_not_equals(document.getElementById("lbl9").control, document.getElementById("testlabel"));
+ assert_equals(document.getElementById("lbl10").control, null, "A label element should not be labelable.");
+}, "Check if the label element is not a labelable element");
+
+test(function() {
+ assert_equals(document.getElementById("testlabel").labels, undefined);
+}, "Check if the label element can access 'labels'");
+
+test(function() {
+ assert_not_equals(document.getElementById("lbl9").control, document.getElementById("testobject"));
+ assert_equals(document.getElementById("lbl11").control, null, "An object element should not be labelable.");
+}, "Check if the object element is not a labelable element");
+
+test(function() {
+ assert_equals(document.getElementById("testobject").labels, undefined);
+}, "Check if the object element can access 'labels'");
+
+test(function() {
+ assert_not_equals(document.getElementById("lbl9").control, document.getElementById("testimg"));
+ assert_equals(document.getElementById("lbl12").control, null, "An img element should not be labelable.");
+}, "Check if the img element is not a labelable element");
+
+test(function() {
+ assert_equals(document.getElementById("lbl9").labels, undefined);
+}, "Check if the img element can access 'labels'");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-click-to-associated-element.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-click-to-associated-element.html
new file mode 100644
index 0000000000..fbfeda8a51
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-click-to-associated-element.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<title>label element click proxying via "for" attribute or nested labelable element</title>
+<link rel="author" title="yaycmyk" href="mailto:evan@yaycmyk.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-label-element:the-label-element-10">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form id="test">
+ <input id="foo" type="checkbox" />
+ <label id="foo-label" for="foo">foo</label>
+
+ <label id="bar-label">
+ <input id="bar" type="checkbox" /> bar
+ <input id="baz" type="checkbox" /> baz
+ </label>
+
+ <input id="baz" type="checkbox" />
+ <label id="baz-label" for="baz">baz</label>
+</form>
+<script>
+ "use strict";
+
+ async_test(t => {
+ const label = document.getElementById("foo-label");
+ const input = document.getElementById("foo");
+
+ input.addEventListener("click", t.step_func_done());
+
+ label.click();
+
+ }, "label with for attribute should proxy click events to the associated element");
+
+ async_test(t => {
+ const label = document.getElementById("bar-label");
+ const input = document.getElementById("bar");
+
+ input.addEventListener("click", t.step_func_done());
+
+ label.click();
+
+ }, "label without for attribute should proxy click events to the first labelable child");
+
+ async_test(t => {
+
+ const label = document.getElementById("baz-label");
+ const input = document.getElementById("baz");
+
+ input.addEventListener("click", t.unreached_func("Input should not receive click"));
+ label.addEventListener("click", t.step_func(ev => {
+ ev.preventDefault();
+ t.step_timeout(() => t.done(), 500);
+ }));
+
+ label.click();
+
+ }, "clicking a label that prevents the event's default should not proxy click events");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-modifier-click-to-associated-element.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-modifier-click-to-associated-element.tentative.html
new file mode 100644
index 0000000000..fa50c08025
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/proxy-modifier-click-to-associated-element.tentative.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<title>clicks on label element with modifier keys should be proxied to its associated control</title>
+<link rel="author" title="Mu-An Chiou" href="mailto:hi@muan.co">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-label-element:the-label-element-10">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id="log"></div>
+<div style="user-select: none;">
+ <label id="click-label" for="click">foo</label><input id="click" type="checkbox" />
+ <label id="shift-label" for="shift">foo</label><input id="shift" type="checkbox" />
+ <label id="alt-label" for="alt">foo</label><input id="alt" type="checkbox" />
+ <label id="meta-label" for="meta">foo</label><input id="meta" type="checkbox" />
+</div>
+<script>
+ "use strict";
+
+ function clickWithModifier(label, key) {
+ new test_driver.Actions()
+ .keyDown(key)
+ .pointerMove(0, 0, { origin: label })
+ .pointerDown()
+ .pointerUp()
+ .addTick()
+ .keyUp(key)
+ .send()
+ }
+
+ async_test(t => {
+ const label = document.getElementById("click-label");
+ const input = document.getElementById("click");
+
+ input.addEventListener("click", t.step_func_done());
+ new test_driver.click(label)
+
+ }, "label with for attribute should proxy click events to the associated element on click");
+
+ async_test(t => {
+ const label = document.getElementById("shift-label");
+ const input = document.getElementById("shift");
+
+ input.addEventListener("click", t.step_func_done());
+ clickWithModifier(label, "\uE008"); // ShiftLeft
+
+ }, "label with for attribute should proxy click events to the associated element on shift click");
+
+ async_test(t => {
+ const label = document.getElementById("alt-label");
+ const input = document.getElementById("alt");
+
+ input.addEventListener("click", t.step_func_done());
+ clickWithModifier(label, "\uE00A"); // AltLeft
+
+ }, "label with for attribute should proxy click events to the associated element on alt click");
+
+ async_test(t => {
+ const label = document.getElementById("meta-label");
+ const input = document.getElementById("meta");
+
+ input.addEventListener("click", t.step_func_done());
+ clickWithModifier(label, "\uE03D"); // OSLeft
+
+ }, "label with for attribute should proxy click events to the associated element on meta click");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-legend-element/HTMLLegendElement.html b/testing/web-platform/tests/html/semantics/forms/the-legend-element/HTMLLegendElement.html
new file mode 100644
index 0000000000..8600e5437a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-legend-element/HTMLLegendElement.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: HTMLLegendElement</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" title="4.10.17 The legend element" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-button-element.html#the-legend-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div style="display:none">
+ <form>
+ <legend id="lgd1">test</legend>
+ </form>
+ <form id="fm">
+ <fieldset id="fs">
+ <legend id="lgd2">test</legend>
+ </fieldset>
+ </form>
+</div>
+<script>
+ test(function() {
+ assert_equals(document.getElementById("lgd1").form, null,
+ "The legend.form return null if it has no fieldset parent.");
+ }, "The legend.form return null when it has no fieldset parent");
+
+ test(function() {
+ assert_equals(document.getElementById("lgd2").form, document.getElementById("fs").form,
+ "The legend.form should be same as fieldset.form.");
+ assert_equals(document.getElementById("lgd2").form, document.getElementById("fm"),
+ "The legend.form should be the correct form.");
+ }, "The legend.form must be same value as fieldset.form");
+
+ test(function() {
+ assert_true(document.getElementById("lgd1") instanceof HTMLLegendElement, "legend should be a HTMLLegendElement");
+ assert_readonly(document.getElementById("lgd1"), "form", "The form is not readonly");
+ }, "Interface HTMLLegendElement");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-legend-element/legend-form.html b/testing/web-platform/tests/html/semantics/forms/the-legend-element/legend-form.html
new file mode 100644
index 0000000000..b127164aed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-legend-element/legend-form.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLLegendElement Test: form</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<div style="display:none">
+ <form id="testform">
+ <legend id="testlegend">radio</legend>
+ </form>
+</div>
+
+<div style="display:none">
+ <form id="testformWithFieldSet">
+ <fieldset>
+ <legend id="legendWithFieldSet">radio</legend>
+ </fieldset>
+ </form>
+</div>
+<script>
+test(function () {
+ var legendEle = document.getElementById("legendWithFieldSet");
+ assert_not_equals(legendEle.form, null);
+ assert_equals(legendEle.form, document.getElementById("testformWithFieldSet"));
+}, "Check if legend.form returns its parent when it's inside a fieldset");
+test(function () {
+ var legendEle = document.getElementById("testlegend");
+ assert_equals(legendEle.form, null);
+}, "Check if legend.form return null when legend has no fieldset element as its parent");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html b/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html
new file mode 100644
index 0000000000..f253945968
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Test Reference</title>
+<meter max="1.0" value="0.5">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering.html b/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering.html
new file mode 100644
index 0000000000..ca83fc9565
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter-min-rendering.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>min is accounted for when rendering a &lt;meter&gt; element</title>
+<link rel=author href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel=author href="https://mozilla.org" title="Mozilla">
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1746758">
+<link rel=match href="meter-min-rendering-ref.html">
+
+<meter min="1.0" max="2.0" value="1.5">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter.html b/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter.html
new file mode 100644
index 0000000000..c7c260c957
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-meter-element/meter.html
@@ -0,0 +1,250 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>The meter element</title>
+ <link rel="author" title="Tomoyuki SHIMIZU" href="mailto:tomoyuki.labs@gmail.com">
+ <link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-meter-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Meter Element</h1>
+ <div id="log"></div>
+ <div style="display: none;">
+ <meter id="meter_illegal_value" value="abc"></meter>
+ <meter id="meter_without_min" value="-10"></meter>
+ <meter id="meter_without_max" value="10"></meter>
+ <meter id="meter_min_without_max_1" value="10" min="-3.1"></meter>
+ <meter id="meter_min_without_max_2" value="210" min="12.1"></meter>
+ <meter id="meter_max_without_min_1" value="-10" max="-5342.55"></meter>
+ <meter id="meter_max_without_min_2" value="210" max="-9.9"></meter>
+ <meter id="meter_illegal_min" value="-2" min="hugfe"></meter>
+ <meter id="meter_illegal_max" value="2.4" max="min"></meter>
+ <meter id="meter_illegal_low_with_min" value="-20" min="-10.3" low="ahuge"></meter>
+ <meter id="meter_illegal_high_with_max" value="2.4" high="old" max="1.5"></meter>
+ <meter id="meter_smaller_than_min" value="-10" min="4.5"></meter>
+ <meter id="meter_larger_than_max" value="2345.53" max="52.02"></meter>
+ <meter id="meter_default_low_and_high_1" value="40" min="-12.3" max="3.4"></meter>
+ <meter id="meter_default_low_and_high_2" value="23"></meter>
+ <meter id="meter_low_smaller_than_min" value="-4" min="12.3" low="34"></meter>
+ <meter id="meter_low_larger_than_max" value="-1" min="-50" low="-5" max="-34.5"></meter>
+ <meter id="meter_high_smaller_than_min" value="-4" min="12.3" high="34"></meter>
+ <meter id="meter_high_larger_than_max" value="-1" min="-50" high="-5" max="-34.5"></meter>
+ <meter id="meter_high_smaller_than_low" value="-9" min="-20" low="-10.3" high="-15.2" max="-2"></meter>
+ <meter id="meter_low_without_min" value="-1" low="-5"></meter>
+ <meter id="meter_high_without_max" value="50000" high="4"></meter>
+ <meter id="meter_optimum_smaller_than_min" value="-8" optimum="-4"></meter>
+ <meter id="meter_optimum_larger_than_max" value="324" optimum="4.6"></meter>
+ <meter id="meter_default_optimum" value="10" min="-132.35" max="33.423"></meter>
+ </div>
+ <script>
+ var meters = [
+ {value: 0, expectedValue: 0, expectedMin: 0, expectedMax: 1.0, expectedLow: 0, expectedHigh: 1.0, expectedOptimum: 0.5, testname: "Default values"},
+ {value: 3, expectedValue: 3, min: -10.1, expectedMin: -10.1, max: 10.1, expectedMax: 10.1, low: -9.1, expectedLow: -9.1, high: 9.1, expectedHigh: 9.1, optimum: 3, expectedOptimum: 3, testname: "Setting values to min, max, low, high and optimum"},
+ {value: 0, expectedValue: 0, min: 0, expectedMin: 0, max: -1.0, expectedMax: 0, expectedLow: 0, expectedHigh: 0, expectedOptimum: 0, testname: "max < min"},
+ {value: 0, expectedValue: 10, min: 10, expectedMin: 10, max: 20, expectedMax: 20, expectedLow: 10, expectedHigh: 20, expectedOptimum: 15, testname: "value < min"},
+ {value: 30, expectedValue: 20, min: 10, expectedMin: 10, max: 20, expectedMax: 20, expectedLow: 10, expectedHigh: 20, expectedOptimum: 15, testname: "value > max"},
+ {value: 15, expectedValue: 15, min: 10, expectedMin: 10, max: 20, expectedMax: 20, low: 5, expectedLow: 10, expectedHigh: 20, expectedOptimum: 15, testname: "low < min"},
+ {value: 15, expectedValue: 15, min: 10, expectedMin: 10, max: 20, expectedMax: 20, low: 25, expectedLow: 20, expectedHigh: 20, expectedOptimum: 15, testname: "low > max"},
+ {value: 15, expectedValue: 15, min: 10, expectedMin: 10, max: 20, expectedMax: 20, low: 12, expectedLow: 12, high: 10, expectedHigh: 12, expectedOptimum: 15, testname: "high < low"},
+ {value: 15, expectedValue: 15, min: 10, expectedMin: 10, max: 20, expectedMax: 20, low: 10, expectedLow: 10, high: 22, expectedHigh: 20, expectedOptimum: 15, testname: "high > max"},
+ {value: 15, expectedValue: 15, min: 10, expectedMin: 10, max: 20, expectedMax: 20, expectedLow: 10, expectedHigh: 20, optimum: 9, expectedOptimum: 10, testname: "optimum < min"},
+ {value: 15, expectedValue: 15, min: 10, expectedMin: 10, max: 20, expectedMax: 20, expectedLow: 10, expectedHigh: 20, optimum: 21, expectedOptimum: 20, testname: "optimum > max"}
+ ];
+ for (var i = 0; i < meters.length; i++) {
+ var m = meters[i];
+ test(function() {
+ var meter = document.createElement("meter");
+ meter.value = m.value;
+ if (m.min) meter.min= m.min;
+ if (m.max) meter.max = m.max;
+ if (m.low) meter.low = m.low;
+ if (m.high) meter.high = m.high;
+ if (m.optimum) meter.optimum = m.optimum;
+ assert_equals(meter.value, m.expectedValue, "meter value");
+ assert_equals(meter.min, m.expectedMin, "min value");
+ assert_equals(meter.max, m.expectedMax, "max value");
+ assert_equals(meter.low, m.expectedLow, "low value");
+ assert_equals(meter.high, m.expectedHigh, "high value");
+ assert_equals(meter.optimum, m.expectedOptimum, "optimum value");
+ }, m.testname);
+ }
+ test(function() {
+ var meter = document.createElement("meter");
+ assert_throws_js(TypeError, function() { meter.value = "foobar"; }, "value attribute");
+ assert_throws_js(TypeError, function() { meter.min = "foobar"; }, "min attribute");
+ assert_throws_js(TypeError, function() { meter.max = "foobar"; }, "max attribute");
+ assert_throws_js(TypeError, function() { meter.low = "foobar"; }, "low attribute");
+ assert_throws_js(TypeError, function() { meter.high = "foobar"; }, "high attribute");
+ assert_throws_js(TypeError, function() { meter.optimum = "foobar"; }, "optimum attribute");
+ }, "Invalid floating-point number values");
+
+ </script>
+ <script type="text/javascript">
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_value').value, 0);
+ }, "value must be 0 when a string is given");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_without_min').min, 0);
+ }, "default value of min is 0");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_without_min').value, 0);
+ }, "If min is not specified and value is smaller than the default value of min (i.e. 0), the actual value must be 0");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_without_max').max, 1.0);
+ }, "default value of max is 1.0");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_without_max').value, 1.0);
+ }, "If max is not specified and value is larger than the default value of max (i.e. 1.0), the actual value must be 1.0");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_min_without_max_1').max, 1.0);
+ }, "If a value smaller than 1.0 is given to min and max is not specified, max must be the same value as its default value (i.e. 1.0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_min_without_max_1').value, 1.0);
+ }, "If a value smaller than 1.0 is given to min, max is not specified, and value is larger than the default value of max (i.e. 1.0), the actual value must be 1.0");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_min_without_max_2').max, 12.1);
+ }, "If a value larger than or equal to 1.0 is given to min and max is not specified, max must be the same value as min");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_min_without_max_2').value, 12.1);
+ }, "If a value larger than or equal to 1.0 is given to min and max is not specified, the actual value must be the same value as min");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_max_without_min_1').min, 0);
+ }, "If a value smaller than 0 is given to max and min is not specified, min must be be the same value as its default value (i.e. 0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_max_without_min_1').max, 0);
+ }, "If a value smaller than 0 is given to max and min is not specified, max must be be the same value as the default value of min (i.e. 0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_max_without_min_1').value, 0);
+ }, "If a value smaller than 0 is given to max and min is not specified, the actual value must be be the same value as the default value of min (i.e. 0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_max_without_min_2').max, 0);
+ }, "If a value larger than or equal to 0 is given to max and min is not specified, max must be the same value as the default value of min (i.e. 0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_max_without_min_2').min, 0);
+ }, "If a value larger than or equal to 0 is given to max and min is not specified, min must be the same value as its default value (i.e. 0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_max_without_min_2').value, 0);
+ }, "If a value larger than or equal to 0 is given to max and min is not specified, the actual value must be the same value as the default value of min (i.e. 0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_min').min, 0);
+ }, "min must be 0 when a string is given");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_min').value, 0);
+ }, "If a string is given to min and value is smaller than the default value of min (i.e. 0), the actual value must be 0");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_max').max, 1.0);
+ }, "max must be 1.0 when a string is given");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_max').value, 1.0);
+ }, "If a string is given to max and value is larger than the default value of min (i.e. 1.0), the actual value must be 1.0");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_low_with_min').low, -10.3);
+ }, "giving a string to low must not affect the actual value");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_high_with_max').high, 1.5);
+ }, "high must equal max when a string is given to high");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_illegal_high_with_max').value, 1.5);
+ }, "giving a string to high must not affect the actual value");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_smaller_than_min').value, 4.5);
+ }, "value must not be smaller than min");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_larger_than_max').value, 52.02);
+ }, "value must not be larger than max");
+
+ test(function() {
+ var e = document.getElementById('meter_default_low_and_high_1');
+ assert_array_equals([e.low,e.high], [-12.3,3.4]);
+ }, "default low and high values equal min and max, respectively");
+
+ test(function() {
+ var e = document.getElementById('meter_default_low_and_high_2');
+ assert_array_equals([e.low,e.high], [0,1.0]);
+ }, "default low and high values equal 0 and 1.0 respectively, if both low and high are not specified");
+
+ test(function() {
+ var e = document.getElementById('meter_low_smaller_than_min');
+ assert_array_equals([e.low,e.min,e.value], [12.3,12.3,12.3]);
+ }, "low must not be smaller than min");
+
+ test(function() {
+ var e = document.getElementById('meter_low_larger_than_max');
+ assert_array_equals([e.low,e.max,e.value], [-34.5,-34.5,-34.5]);
+ }, "low must not be larger than max");
+
+ test(function() {
+ var e = document.getElementById('meter_high_smaller_than_min');
+ assert_array_equals([e.high,e.min,e.value], [12.3,12.3,12.3]);
+ }, "high must not be smaller than min");
+
+ test(function() {
+ var e = document.getElementById('meter_high_larger_than_max');
+ assert_array_equals([e.high,e.max,e.value], [-34.5,-34.5,-34.5]);
+ }, "high must not be larger than max");
+
+ test(function() {
+ var e = document.getElementById('meter_low_without_min');
+ assert_array_equals([e.low,e.min,e.value], [0,0,0]);
+ }, "If min is not specified, low must not be smaller than default value of min (i.e. 0)");
+
+ test(function() {
+ var e = document.getElementById('meter_high_smaller_than_low');
+ assert_array_equals([e.low,e.high,e.value], [-10.3,-10.3,-9]);
+ }, "If a value smaller than low is given to high, it must be set to the same value as low");
+
+ test(function() {
+ var e = document.getElementById('meter_high_without_max');
+ assert_array_equals([e.high,e.value], [1.0,1.0]);
+ }, "If max is not specified, high must not be larger than default value of max (i.e. 1.0)");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_optimum_smaller_than_min').optimum, 0);
+ }, "optimum smaller than min");
+
+ test(function() {
+ var e = document.getElementById('meter_optimum_smaller_than_min');
+ assert_array_equals([e.min,e.value], [0,0]);
+ }, "optimum (smaller than min) must not affect min and the actual value");
+
+ test(function() {
+ assert_equals(document.getElementById('meter_optimum_larger_than_max').optimum, 1.0);
+ }, "optimum smaller than max");
+
+ test(function() {
+ var e = document.getElementById('meter_optimum_larger_than_max');
+ assert_array_equals([e.max,e.value], [1.0,1.0]);
+ }, "optimum (larger than max) must not affect max and the actual value");
+
+ test(function() {
+ var e = document.getElementById('meter_default_optimum');
+ assert_equals(e.optimum, (e.max + e.min) / 2);
+ }, "default optimum value is the midpoint between min and max");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-disabled-manual.html b/testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-disabled-manual.html
new file mode 100644
index 0000000000..ca8c6cda80
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-disabled-manual.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLOptGroupElement Test: disabled</title>
+<meta name="flags" content="interact">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+
+<form>
+ <select>
+ <optgroup label="8.01" disabled>
+ <option value="8.01.1">Lecture 01: Powers of Ten</option>
+ <option value="8.01.2">Lecture 02: 1D Kinematics</option>
+ <option value="8.01.3">Lecture 03: Vectors</option>
+ </optgroup>
+ <optgroup label="8.02">
+ <option value="8.02.1">Lecture 01: What holds our world together?</option>
+ <option value="8.02.2">Lecture 02: Electric Field</option>
+ <option value="8.02.3">Lecture 03: Electric Flux</option>
+ </optgroup>
+ </select>
+</form>
+
+<h2>Description</h2>
+<p>
+ This test validates that an optgroup element is disabled if its disabled attribute is present.
+</p>
+
+<h2>Test steps:</h2>
+<ol>
+ <li>
+ Click the select flag to select section '8.01'
+ </li>
+</ol>
+
+<h2>Result:</h2>
+<p>Click the select flag and try to select section 8.01, test passes if the section 8.01 is disable to be selected</p>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-removal.window.js b/testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-removal.window.js
new file mode 100644
index 0000000000..4e4c2df77e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-optgroup-element/optgroup-removal.window.js
@@ -0,0 +1,7 @@
+test(() => {
+ const select = document.createElement("select");
+ select.innerHTML = "<optgroup><option>1<optgroup><option>2";
+ assert_equals(select.value, "1");
+ select.querySelector("optgroup").remove();
+ assert_equals(select.value, "2");
+}, "<select> needs to be updated when <optgroup> is removed");
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering-ref.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering-ref.html
new file mode 100644
index 0000000000..453bb70822
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<select>
+<option>foo</option>
+</select>
+
+<select multiple>
+<option>bar</option>
+</select>
+
+</bod>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering.html
new file mode 100644
index 0000000000..1108c45e11
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/dynamic-content-change-rendering.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Invalidation test on resetting &lt;select></title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#concept-option-label">
+<link rel="help" href="http://crbug.com/1090806">
+<link rel="match" href="dynamic-content-change-rendering-ref.html">
+<meta name=fuzzy content="maxDifference=0-3;totalPixels=20">
+<body>
+
+<select id="dropdown">
+<option></option>
+</select>
+
+<select id="listbox" multiple>
+<option></option>
+</select>
+
+<script>
+const selects = document.querySelectorAll('select');
+
+const span0 = document.createElement('span');
+selects[0].options[0].appendChild(span0);
+
+const span1 = document.createElement('span');
+selects[1].options[0].appendChild(span1);
+
+document.documentElement.addEventListener('TestRendered', e => {
+ span0.textContent = 'foo';
+ span1.textContent = 'bar';
+ e.target.removeAttribute('class');
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-disabled-manual.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-disabled-manual.html
new file mode 100644
index 0000000000..25dfcc87a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-disabled-manual.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLOptionElement Test: disabled</title>
+<meta name="flags" content="interact">
+<link rel="author" title="Intel" href="http://www.intel.com/">
+
+<div>
+ <select>
+ <option id="testOption1" text="Option1" >Option1</option>
+ <option id="testOption2" disabled >Option2</option>
+ <option id="testOption3" >Option3</option>
+ </select>
+</div>
+
+<h2>Description</h2>
+<p>
+ This test validates that an option element is disabled if its disabled attribute is present.
+</p>
+
+<h2>Test steps:</h2>
+<ol>
+ <li>
+ Click the select flag to select 'Option2'
+ </li>
+</ol>
+
+<h2>Result:</h2>
+<p>Test passes if not able to select 'Option2'</p>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-element-constructor.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-element-constructor.html
new file mode 100644
index 0000000000..05bcb3024a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-element-constructor.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Option element constructor</title>
+<link rel="author" title="Alex Pearson" href="mailto:alex@alexpear.com">
+<link rel="help" href="https://html.spec.whatwg.org/#the-option-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="parent">
+ <div id="child" tabindex="0"></div>
+</div>
+
+<body>
+<script>
+ "use strict";
+
+ test(() => {
+ const option = new Option();
+
+ assert_true(option instanceof HTMLOptionElement);
+
+ assert_false(option.hasChildNodes());
+ assert_false(option.hasAttribute("value"));
+ assert_false(option.hasAttribute("selected"));
+ assert_false(option.selected);
+
+ assert_equals(option.textContent, "");
+ assert_equals(option.value, "");
+ }, "Option constructor with no arguments");
+
+ test(() => {
+ const option = new Option(false, false);
+
+ assert_true(option instanceof HTMLOptionElement);
+
+ assert_true(option.hasChildNodes());
+ assert_equals(option.childNodes.length, 1);
+ assert_equals(option.childNodes[0].nodeType, Node.TEXT_NODE);
+ assert_equals(option.childNodes[0].data, "false");
+ assert_equals(option.getAttribute("value"), "false");
+ assert_false(option.hasAttribute("selected"));
+ assert_false(option.selected);
+
+ assert_equals(option.textContent, "false");
+ assert_equals(option.value, "false");
+ }, "Option constructor with falsy arguments");
+
+ test(() => {
+ const option = new Option("text", "value");
+
+ assert_true(option.hasChildNodes());
+ assert_equals(option.childNodes.length, 1);
+ assert_equals(option.childNodes[0].nodeType, Node.TEXT_NODE);
+ assert_equals(option.childNodes[0].data, "text");
+ assert_equals(option.getAttribute("value"), "value");
+ assert_false(option.hasAttribute("selected"));
+ assert_false(option.selected);
+
+ assert_equals(option.textContent, "text");
+ assert_equals(option.value, "value");
+ }, "Option constructor creates HTMLOptionElement with specified text and value");
+
+ test(() => {
+ const notSelected = new Option("text", "value", false);
+ const selected = new Option("text", "value", true);
+
+ assert_false(notSelected.hasAttribute("selected"));
+ assert_equals(notSelected.getAttribute("selected"), null);
+ assert_false(notSelected.selected);
+
+ assert_equals(selected.getAttribute("selected"), "");
+ assert_false(selected.selected);
+ }, "Option constructor handles selectedness correctly when specified with defaultSelected only");
+
+ test(() => {
+ const notSelected = new Option("text", "value", true, false);
+ const selected = new Option("text", "value", false, true);
+
+ assert_equals(notSelected.selected, false);
+ assert_equals(selected.selected, true);
+ }, "Option constructor handles selectedness correctly, even when incongruous with defaultSelected");
+
+ test(() => {
+ const option = new Option(undefined, undefined);
+
+ assert_false(option.hasChildNodes());
+ assert_false(option.hasAttribute("value"));
+
+ assert_equals(option.textContent, "");
+ assert_equals(option.value, "");
+ }, "Option constructor treats undefined text and value correctly");
+
+ test(() => {
+ const option = new Option("", "");
+
+ assert_false(option.hasChildNodes());
+ assert_true(option.hasAttribute("value"));
+
+ assert_equals(option.textContent, "");
+ assert_equals(option.value, "");
+ }, "Option constructor treats empty text and value correctly");
+
+ test(() => {
+ const option = new Option("text", "value", 0, "");
+
+ assert_false(option.hasAttribute("selected"));
+ assert_false(option.selected);
+ }, "Option constructor treats falsy selected and defaultSelected correctly");
+
+ test(() => {
+ const option = new Option("text", "value", {}, 1);
+
+ assert_true(option.hasAttribute("selected"));
+ assert_true(option.selected);
+ }, "Option constructor treats truthy selected and defaultSelected correctly");
+
+ test(() => {
+ const option = new Option("text", "value", false, true);
+
+ assert_false(option.hasAttribute("selected"));
+ assert_true(option.selected);
+
+ option.setAttribute("selected", "");
+ assert_true(option.selected);
+
+ option.removeAttribute("selected");
+ assert_false(option.selected);
+ }, "Option constructor does not set dirtiness (so, manipulating the selected content attribute still updates the " +
+ "selected IDL attribute)");
+
+ test(function() {
+ var option = new Option();
+ assert_equals(Object.getPrototypeOf(option), HTMLOptionElement.prototype);
+ }, "Prototype of object created with named constructor");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-form.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-form.html
new file mode 100644
index 0000000000..1a68b5c1ca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-form.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.form</title>
+<link rel=author title="Sergey Alexandrov" href="mailto:splavgm@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-option-form">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form id="form">
+ <select id="select">
+ <optgroup id="optgroup"></optgroup>
+ </select>
+</form>
+<div id=log></div>
+
+<script>
+test(function () {
+ var form = document.getElementById("form");
+ var select = document.getElementById("select");
+ var optgroup = document.getElementById("optgroup");
+
+ var o1 = document.createElement("option");
+ assert_equals(o1.form, null);
+
+ select.appendChild(o1);
+ assert_equals(o1.form, select.form);
+
+ var o2 = document.createElement("option");
+ select.appendChild(o2);
+ assert_equals(o2.form, select.form);
+
+}, "form");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-index.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-index.html
new file mode 100644
index 0000000000..719c608a63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-index.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<title>HTMLOptionElement.prototype.index</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-option-index">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<select>
+<option id="option0">hello</option>
+<option id="option1">hello</option>
+</select>
+
+
+<datalist>
+<option id="dl-option0">hello</option>
+<option id="dl-option1">hello</option>
+</datalist>
+
+<option id="doc-option0">hello</option>
+<option id="doc-option1">hello</option>
+
+<script>
+"use strict";
+
+test(() => {
+
+ assert_equals(document.querySelector("#option0").index, 0);
+ assert_equals(document.querySelector("#option1").index, 1);
+
+}, "option index should work inside the document");
+
+test(() => {
+
+ assert_equals(document.querySelector("#dl-option0").index, 0);
+ assert_equals(document.querySelector("#dl-option1").index, 0);
+
+}, "option index should always be 0 for options in datalists");
+
+test(() => {
+
+ assert_equals(document.querySelector("#doc-option0").index, 0);
+ assert_equals(document.querySelector("#doc-option1").index, 0);
+
+}, "option index should always be 0 for options with no container");
+
+test(() => {
+
+ assert_equals(document.createElement("option").index, 0);
+ assert_equals(document.createElement("option").index, 0);
+
+}, "option index should always be 0 for options not even in the document");
+
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-label-value.js b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-label-value.js
new file mode 100644
index 0000000000..5c453f1733
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-label-value.js
@@ -0,0 +1,82 @@
+function test_option(member) {
+ test(function() {
+ var option = document.createElement("option");
+ assert_equals(option[member], "");
+ }, "No children, no " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.setAttribute(member, "")
+ assert_equals(option[member], "");
+ }, "No children, empty " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.setAttribute(member, member)
+ assert_equals(option[member], member);
+ }, "No children, " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.setAttributeNS("http://www.example.com/", member, member)
+ assert_equals(option[member], "");
+ }, "No children, namespaced " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ assert_equals(option[member], "child");
+ }, "Single child, no " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ option.setAttribute(member, "")
+ assert_equals(option[member], "");
+ }, "Single child, empty " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ option.setAttribute(member, member)
+ assert_equals(option[member], member);
+ }, "Single child, " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ option.setAttributeNS("http://www.example.com/", member, member)
+ assert_equals(option[member], "child");
+ }, "Single child, namespaced " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ option.appendChild(document.createTextNode(" node "));
+ assert_equals(option[member], "child node");
+ }, "Two children, no " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ option.appendChild(document.createTextNode(" node "));
+ option.setAttribute(member, "")
+ assert_equals(option[member], "");
+ }, "Two children, empty " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ option.appendChild(document.createTextNode(" node "));
+ option.setAttribute(member, member)
+ assert_equals(option[member], member);
+ }, "Two children, " + member);
+
+ test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" child "));
+ option.appendChild(document.createTextNode(" node "));
+ option.setAttributeNS("http://www.example.com/", member, member)
+ assert_equals(option[member], "child node");
+ }, "Two children, namespaced " + member);
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-label.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-label.html
new file mode 100644
index 0000000000..f931b96220
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-label.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.label</title>
+<link rel=author title=Ms2ger href="mailto:Ms2ger@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-option-label">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=option-label-value.js></script>
+<div id=log></div>
+<script>
+test_option("label")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-selected.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-selected.html
new file mode 100644
index 0000000000..e18e90b853
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-selected.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.selected</title>
+<link rel=author title="Corey Farwell" href="mailto:coreyf@rwell.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-option-selected">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+
+<script>
+test(function () {
+ var elem = document.createElement("option");
+ assert_equals(elem.selected, false);
+
+ elem.setAttribute("selected", "");
+ assert_equals(elem.selected, true);
+
+ elem.removeAttribute("selected");
+ assert_equals(elem.selected, false);
+
+ elem.defaultSelected = true
+ assert_equals(elem.selected, true);
+
+ elem.defaultSelected = false;
+ assert_equals(elem.selected, false);
+}, "not dirty");
+
+test(function () {
+ testDirty(true);
+}, "dirty, selected");
+
+test(function () {
+ testDirty(false);
+}, "dirty, not selected");
+
+function testDirty(isSelected) {
+ var elem = document.createElement("option");
+
+ elem.selected = isSelected; // After this assignment, dirtiness=true
+ assertDirty(elem, isSelected);
+
+ elem.selected = !isSelected; // Change the value, still dirty
+ assertDirty(elem, !isSelected);
+};
+
+function assertDirty(elem, expect) {
+ assert_equals(elem.selected, expect);
+
+ elem.setAttribute("selected", "");
+ assert_equals(elem.selected, expect);
+
+ elem.removeAttribute("selected");
+ assert_equals(elem.selected, expect);
+
+ elem.defaultSelected = true;
+ assert_equals(elem.selected, expect);
+
+ elem.defaultSelected = false;
+ assert_equals(elem.selected, expect);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-backslash.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-backslash.html
new file mode 100644
index 0000000000..34bd0d368b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-backslash.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=EUC-JP>
+<title>Test for the backslash sign in option.text</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<select id=test><option>\</option></select>
+<script>
+test(function() {
+ var select = document.getElementById("test");
+ var option = select.firstChild;
+ assert_equals(option.text, "\\");
+ assert_equals(option.textContent, "\\");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-label.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-label.html
new file mode 100644
index 0000000000..9259aecf30
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-label.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.text</title>
+<link rel=author title=Ms2ger href="mailto:Ms2ger@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-option-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var option = document.createElement("option");
+ option.setAttribute("label", "label");
+ option.textContent = "text";
+ assert_equals(option.text, "text");
+}, "Option with non-empty label.");
+
+test(function() {
+ var option = document.createElement("option");
+ option.setAttribute("label", "");
+ option.textContent = "text";
+ assert_equals(option.text, "text");
+}, "Option with empty label.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-recurse.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-recurse.html
new file mode 100644
index 0000000000..cf854f5260
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-recurse.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.text</title>
+<link rel=author title=Ms2ger href="mailto:Ms2ger@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-option-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createElement("font"))
+ .appendChild(document.createTextNode(" font "));
+ assert_equals(option.text, "font");
+}, "option.text should recurse");
+
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElement("script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before after");
+}, "option.text should not recurse into HTML script elements");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before after");
+}, "option.text should not recurse into SVG script elements");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before script after");
+}, "option.text should recurse into MathML script elements");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElementNS(null, "script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before script after");
+}, "option.text should recurse into null script elements");
+test(function() {
+ var option = document.createElement("option");
+ var span = option.appendChild(document.createElement("span"));
+ span.appendChild(document.createTextNode(" Some "));
+ span.appendChild(document.createElement("script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" Text "));
+ assert_equals(option.text, "Some Text");
+}, "option.text should work if a child of the option ends with a script");
+
+test(function() {
+ var script = document.createElement("script");
+ var option = script.appendChild(document.createElement("option"));
+ option.appendChild(document.createTextNode("text"));
+ assert_equals(option.text, "text");
+}, "option.text should work if the option is in an HTML script element");
+test(function() {
+ var script = document.createElementNS("http://www.w3.org/2000/svg", "script");
+ var option = script.appendChild(document.createElement("option"));
+ option.appendChild(document.createTextNode("text"));
+ assert_equals(option.text, "text");
+}, "option.text should work if the option is in an SVG script element");
+test(function() {
+ var script = document.createElementNS("http://www.w3.org/1998/Math/MathML", "script");
+ var option = script.appendChild(document.createElement("option"));
+ option.appendChild(document.createTextNode("text"));
+ assert_equals(option.text, "text");
+}, "option.text should work if the option is in a MathML script element");
+
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode("te"));
+ option.appendChild(document.createComment("comment"));
+ option.appendChild(document.createTextNode("xt"));
+ assert_equals(option.text, "text");
+}, "option.text should ignore comment children");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode("te"));
+ option.appendChild(document.createProcessingInstruction("target", "data"));
+ option.appendChild(document.createTextNode("xt"));
+ assert_equals(option.text, "text");
+}, "option.text should ignore PI children");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-setter.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-setter.html
new file mode 100644
index 0000000000..1aa44ed7f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-setter.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form>
+ <select>
+ <option value='foo'>bar</option>
+ </select>
+</form>
+<script>
+test(() => {
+ var option = document.querySelector('option');
+ var textChild = option.firstChild;
+ assert_equals(textChild.nodeValue, "bar", "Verify that text child node's value equals the option value.");
+ assert_true(textChild.isConnected, 'Verify that text child node is in the document.');
+ option.text = "baz";
+ assert_equals(textChild.nodeValue, "bar", 'Verify that the text child node does not have an updated value.');
+ assert_false(textChild.isConnected, 'Verify that the text child node is not in the document.');
+ assert_not_equals(textChild, option.firstChild, 'Verify that text child node was replaced by a different text child node.');
+ assert_equals(option.firstChild.nodeValue, "baz", 'Verify that the new text child node does equal the updated value.');
+ assert_equals(option.text, "baz", 'Verify that option text getter returns the updated value.');
+ option.text = "";
+ assert_equals(option.firstChild, null, 'Verify that after setting to empty string there are no child text nodes.');
+}, 'Verify that using HTMLOptionElement.text setter does not update the existing text child node.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-spaces.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-spaces.html
new file mode 100644
index 0000000000..2c712655a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-text-spaces.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.text</title>
+<link rel=author title=Ms2ger href="mailto:Ms2ger@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-option-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var spaces = ["\u0020", "\u0009", "\u000A", "\u000C", "\u000D"];
+ spaces.forEach(function(space) {
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = space + "text";
+ assert_equals(option.text, "text");
+ }, "option.text should strip leading space characters (" +
+ format_value(space) + ")");
+ });
+ spaces.forEach(function(space) {
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = "text" + space;
+ assert_equals(option.text, "text");
+ }, "option.text should strip trailing space characters (" +
+ format_value(space) + ")");
+ });
+ spaces.forEach(function(space) {
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = space + "text" + space;
+ assert_equals(option.text, "text");
+ }, "option.text should strip leading and trailing space characters (" +
+ format_value(space) + ")");
+ });
+ spaces.forEach(function(space) {
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = "before" + space + "after";
+ assert_equals(option.text, "before after");
+ }, "option.text should replace single internal space characters (" +
+ format_value(space) + ")");
+ });
+ spaces.forEach(function(space1) {
+ spaces.forEach(function(space2) {
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = "before" + space1 + space2 + "after";
+ assert_equals(option.text, "before after");
+ }, "option.text should replace multiple internal space characters (" +
+ format_value(space1) + ", " + format_value(space2) + ")");
+ });
+ });
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = "\u00a0text";
+ assert_equals(option.text, "\u00a0text");
+ }, "option.text should leave leading NBSP alone.");
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = "text\u00a0";
+ assert_equals(option.text, "text\u00a0");
+ }, "option.text should leave trailing NBSP alone.");
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = "before\u00a0after";
+ assert_equals(option.text, "before\u00a0after");
+ }, "option.text should leave a single internal NBSP alone.");
+ test(function() {
+ var option = document.createElement("option");
+ option.textContent = "before\u00a0\u00a0after";
+ assert_equals(option.text, "before\u00a0\u00a0after");
+ }, "option.text should leave two internal NBSPs alone.");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-option-element/option-value.html b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-value.html
new file mode 100644
index 0000000000..cccdc37487
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-option-element/option-value.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.value</title>
+<link rel=author title=Ms2ger href="mailto:Ms2ger@gmail.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-option-label">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=option-label-value.js></script>
+<div id=log></div>
+<script>
+test_option("value")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-output-element/mutations.window.js b/testing/web-platform/tests/html/semantics/forms/the-output-element/mutations.window.js
new file mode 100644
index 0000000000..9b1c6bd70f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-output-element/mutations.window.js
@@ -0,0 +1,36 @@
+function assert_equal_values(output, value, message) {
+ assert_equals(output.value, value, `.value ${message}`);
+ assert_equals(output.defaultValue, value, `.defaultValue ${message}`);
+}
+function assert_values(output, value, defaultValue, message) {
+ assert_equals(output.value, value, `.value ${message}`);
+ assert_equals(output.defaultValue, defaultValue, `.defaultValue ${message}`);
+}
+
+test(() => {
+ const output = document.createElement("output"),
+ child = output.appendChild(document.createElement("span"));
+ assert_equal_values(output, "", "start");
+ child.textContent = "x";
+ assert_equal_values(output, "x", "after setting textContent");
+ output.value = "some";
+ assert_values(output, "some", "x", "after setting value");
+ output.textContent = "y";
+ assert_values(output, "y", "x", "after setting textContent again");
+}, "Descendant mutations and output.value and .defaultValue");
+
+test(() => {
+ const form = document.createElement("form"),
+ output = form.appendChild(document.createElement("output"));
+ output.textContent = "value";
+ assert_equal_values(output, "value", "after setting textContent");
+ output.value = "heya";
+ assert_values(output, "heya", "value", "after setting value");
+ form.reset();
+ assert_equal_values(output, "value", "after form.reset()");
+
+ output.innerHTML = "<div>something</div>";
+ assert_equal_values(output, "something", "after setting innerHTML");
+ form.reset();
+ assert_equal_values(output, "something", "after form.reset() again");
+}, "output and output.form.reset()");
diff --git a/testing/web-platform/tests/html/semantics/forms/the-output-element/output-setcustomvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-output-element/output-setcustomvalidity.html
new file mode 100644
index 0000000000..1166eeb610
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-output-element/output-setcustomvalidity.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<title>output setCustomValidity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<output id='output_test'></output>
+
+<script>
+
+test(() => {
+ let elem = document.getElementById("output_test");
+ assert_false(elem.validity.customError);
+ elem.setCustomValidity("custom error");
+ assert_true(elem.validity.customError);
+}, "output setCustomValidity is correct")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-output-element/output-validity.html b/testing/web-platform/tests/html/semantics/forms/the-output-element/output-validity.html
new file mode 100644
index 0000000000..c59a055ecf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-output-element/output-validity.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<title>:valid and :invalid pseudo-class on output element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<output id='output_test'></output>
+
+<script>
+
+test(() => {
+ let output = document.getElementById("output_test");
+ assert_false(output.matches(":valid"), "should not match :valid pseudo-class");
+ assert_false(output.matches(":invalid"), "should not match :invalid pseudo-class");
+
+ output.setCustomValidity("custom error");
+ assert_equals(output.validationMessage, "", "should not have a validation message");
+ assert_true(output.validity.customError, "should have a custom error");
+ assert_false(output.validity.valid, "should not be valid with a custom error");
+ assert_false(output.matches(":valid"), "should still not match :valid pseudo-class");
+ assert_false(output.matches(":invalid"), "should still not match :invalid pseudo-class");
+}, ":valid and :invalid pseudo-class on output element")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-output-element/output.html b/testing/web-platform/tests/html/semantics/forms/the-output-element/output.html
new file mode 100644
index 0000000000..7ae00ec7e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-output-element/output.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>The output element</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-output-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<output id=output></output>
+<output id="defaultValueOutput">abc</output>
+<script>
+ var output = document.getElementById("output");
+ var defaultValueOutput = document.getElementById("defaultValueOutput");
+
+ test(function(){
+ assert_equals(output.type, "output", "type must return the string 'output'");
+ assert_equals(output.textContent, "", "textContent is empty");
+ assert_equals(output.value, "", "value should be empty");
+ assert_equals(output.defaultValue, "", "defaultValue should be empty");
+
+ output.textContent="5";
+ assert_equals(output.value, "5", "textContent is set to 5: value is updated");
+ assert_equals(output.textContent, "5", "textContent is set to 5");
+ assert_equals(output.defaultValue, "5", "textContent is set to 5: defaultValue is updated");
+
+ output.defaultValue="10"; // value mode flag is in "default" mode. Setting defaultValue should set textContent as well
+ assert_equals(output.value, "10", "defaultValue is set to 10: value is updated");
+ assert_equals(output.textContent, "10", "defaultValue is set to 10: textContent is updated");
+ assert_equals(output.defaultValue, "10", "defaultValue is set to 10");
+
+ output.value="20"; // set the value mode flag to "value": default value remains unchanged
+ assert_equals(output.value, "20", "value is set to 20");
+ assert_equals(output.textContent, "20", "value is set to 20: textContent is updated");
+ assert_equals(output.defaultValue, "10", "value is set to 20: defaultValue remains unchanged");
+
+ output.defaultValue="15"; // value mode flag is in "value" mode. textContent remains unchanged when setting defaultValue
+ assert_equals(output.value, "20", "defaultValue is set to 15: value remains unchanged");
+ assert_equals(output.textContent, "20", "defaultValue is set to 15: textContent remains unchanged");
+ assert_equals(output.defaultValue, "15", "defaultValue is set to 15");
+
+ assert_equals(defaultValueOutput.type, "output", "type must return the string 'output'");
+ assert_equals(defaultValueOutput.textContent, "abc", "textContent should be 'abc'");
+ assert_equals(defaultValueOutput.value, "abc", "value should be 'abc'");
+ assert_equals(defaultValueOutput.defaultValue, "abc", "defaultValue should be 'abc'");
+ }, "output value and defaultValue");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress-2.html b/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress-2.html
new file mode 100644
index 0000000000..ebc4750627
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress-2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Progress Element Tests</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-progress-element" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <progress id="p00" style="display: none"></progress>
+ <progress id="p01" max="5.5" value=".5" style="display: none"></progress>
+ <script>
+ test(function () {
+ assert_equals(document.getElementById('p00').position, -1);
+ }, "progress position equals -1");
+
+ test(function () {
+ assert_equals(document.getElementById('p00').value, 0);
+ }, "progress value equals 0");
+
+ test(function () {
+ assert_equals(document.getElementById('p01').value, .5);
+ }, "progress value equals .5");
+
+ test(function () {
+ document.getElementById('p00').value = document.getElementById('p00').value;
+ assert_equals(document.getElementById('p00').position, 0);
+ }, "progress position equals 0");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.html b/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.html
new file mode 100644
index 0000000000..00d63c372f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>The progress element</title>
+
+ <link rel="author" title="dan smith" href="mailto:XX1011@gmail.com">
+ <link rel="author" title="Tomoyuki SHIMIZU" href="mailto:tomoyuki.labs@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-progress-element">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+
+ <progress id="indeterminate"></progress>
+ <progress id="removevalue" value="0.5"></progress>
+ <progress id="quarter" value="1" max="4"></progress>
+ <progress id="largerthanmax" value="2"></progress>
+ <progress id="invalidmax" value="1" max="a"></progress>
+ <progress id="negativemax" value="1" max="-1"></progress>
+ <progress id="invalidvalue" value="a"></progress>
+ <progress id="negativevalue" value="-1"></progress>
+
+ <script>
+
+ test(function() {
+ assert_equals(indeterminate.position, -1);
+ }, "Indeterminate progress bar should have position -1");
+
+ test(function() {
+ removevalue.removeAttribute('value');
+ assert_equals(removevalue.position, -1);
+ }, "Revoming the value attribute makes an intermediate progress bar, which should have position -1");
+
+ test(function() {
+ assert_equals(quarter.position, quarter.value / quarter.max);
+ }, "Determinate progress bar should have fractional position");
+
+ test(function() {
+ assert_equals(indeterminate.value, 0);
+ }, "Indeterminate progress bar should have value 0");
+
+ test(function() {
+ assert_equals(largerthanmax.value, 1);
+ }, "Value must equal max if the parsed value is larger than max");
+
+ test(function() {
+ assert_equals(indeterminate.max, 1);
+ }, "Max must be 1 by default");
+
+ test(function() {
+ assert_equals(largerthanmax.max, 1);
+ }, "Max must be 1 by default, even if value is specified");
+
+ test(function() {
+ assert_equals(invalidmax.max, 1);
+ }, "Max must be 1 if max value is invalid");
+
+ test(function() {
+ assert_equals(negativemax.max, 1);
+ }, "Max must be 1 if the parsed max value is less than or equal to zero");
+
+ test(function() {
+ assert_equals(invalidvalue.value, 0);
+ }, "Value must be 0 if value is invalid");
+
+ test(function() {
+ assert_equals(negativevalue.value, 0);
+ }, "Value must be 0 if the parsed value is less than or equal to zero");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.window.js b/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.window.js
new file mode 100644
index 0000000000..37526053de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-progress-element/progress.window.js
@@ -0,0 +1,18 @@
+test(() => {
+ const pr = document.createElement("progress");
+ assert_equals(pr.value, 0);
+ assert_equals(pr.position, -1);
+ pr.value = 2;
+ assert_equals(pr.value, 1);
+ assert_equals(pr.position, 1);
+}, "If value > max, then current value = max");
+
+test(() => {
+ const pr = document.createElement("progress");
+ pr.value = 2;
+ assert_equals(pr.value, 1);
+ assert_equals(pr.position, 1);
+ pr.max = 4;
+ assert_equals(pr.value, 2);
+ assert_equals(pr.position, 0.5);
+}, "If value < max, then current value = value");
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-add.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-add.html
new file mode 100644
index 0000000000..cf4306e271
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-add.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title id='title'>HTMLOptionsCollection</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<select id="selly">
+ <option id="id1" name="name1">1</option>
+ <option id="id2" name="name2">2</option>
+ <option id="id3" name="name3">3</option>
+ <option id="id4" name="name4">4</option>
+ <optgroup id="og1">
+ <option name="nameonly">n1</option>
+ <option id="id5">5</option>
+ </optgroup>
+ <optgroup id="og2">
+ <option name="nameonly">n2</option>
+ <option id="id6">6</option>
+ </optgroup>
+
+</select>
+
+<script>
+var selly;
+setup(function() {
+ selly = document.getElementById('selly');
+});
+
+test(function () {
+ var option = document.getElementById('id1');
+ var optgroup = document.getElementById('og1');
+ selly.options.add(option, option);
+ selly.options.add(optgroup, optgroup);
+ assert_equals(selly.children.length, 6);
+ assert_equals(selly.length, 8);
+}, "if before and node are the same element nothing should be done");
+
+test(function () {
+ var o1 = document.createElement("option");
+ o1.value = "a";
+ var o2 = document.createElement("option");
+ o2.value = "b";
+ var o3 = document.createElement("option");
+ o3.value = "c";
+ var optgroup = document.getElementById('og1');
+ selly.options.add(o1, null);
+ selly.options.add(o2, optgroup);
+ selly.options.add(o3, 0);
+
+ var elarray = [];
+ for (var i = 0; i < selly.length; i++) {
+ elarray.push(selly[i].value);
+ }
+ assert_array_equals(elarray, ["c", "1", "2", "3", "4", "b", "n1", "5", "n2", "6", "a"]);
+}, "add method should add option elements correctly");
+
+test(function () {
+ var og1 = document.createElement("optgroup");
+ var o1 = document.createElement("option");
+ o1.value = "a";
+ o1.appendChild(og1);
+ var og2 = document.createElement("optgroup");
+ var o2 = document.createElement("option");
+ o2.value = "b";
+ o2.appendChild(og2);
+ var og3 = document.createElement("optgroup");
+ var o3 = document.createElement("option");
+ o3.value = "c";
+ o3.appendChild(og3);
+
+ var optgroup = document.getElementById('og1');
+ selly.options.add(og1, null);
+ selly.options.add(og2, optgroup);
+ selly.options.add(og3, 0);
+
+ var elarray = [];
+ for (var i = 0; i < selly.length; i++) {
+ elarray.push(selly[i].value);
+ }
+ assert_array_equals(elarray, ["c", "1", "2", "3", "4", "b", "n1", "5", "n2", "6", "a"]);
+}, "add method should add option groups correctly");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-namedItem.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-namedItem.html
new file mode 100644
index 0000000000..c5c8510a47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection-namedItem.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title id='title'>HTMLOptionsCollection</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<select id="selly">
+ <option id="id1" name="name1">1</option>
+ <option id="id2" name="name2">2</option>
+ <option id="id3" name="name3">3</option>
+ <option id="id4" name="name4">4</option>
+ <option name="nameonly">nameonly</option>
+ <option id="id3">duplicate ID</option>
+ <option name="name4">duplicate name</option>
+ <option id="mixed1">mixed ID</option>
+ <option name="mixed1">mixed name</option>
+</select>
+
+<script>
+var selly;
+setup(function() {
+ selly = document.getElementById('selly');
+});
+
+test(function () {
+ assert_equals(selly.namedItem('nameonly')["value"], "nameonly");
+}, "if only one item has a *name* or id value matching the parameter, return that object and stop");
+
+test(function () {
+ assert_equals(selly.namedItem('id2')["value"], "2");
+}, "if only one item has a name or *id* value matching the parameter, return that object and stop");
+
+test(function () {
+ assert_equals(selly.namedItem('thisdoesnotexist'), null);
+}, "if no item has a name or id value matching the parameter, return null and stop");
+
+test(function () {
+ assert_equals(selly.namedItem('id3')["value"], "3");
+}, "if multiple items have a name or *id* value matching the parameter, return the first object and stop");
+
+test(function () {
+ assert_equals(selly.namedItem('name4')["value"], "4");
+}, "if multiple items have a *name* or id value matching the parameter, return the first object and stop");
+
+test(function () {
+ assert_equals(selly.namedItem('mixed1')["value"], "mixed ID");
+}, "if multiple items have a *name* or *id* value matching the parameter, return the first object and stop");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection.html
new file mode 100644
index 0000000000..737e9be876
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/common-HTMLOptionsCollection.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title id='title'>HTMLOptionsCollection</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<select id="selly">
+ <option>1</option>
+ <option>2</option>
+ <option>3</option>
+ <option>4</option>
+</select>
+
+<script>
+var selly;
+setup(function() {
+ selly = document.getElementById('selly');
+});
+
+test(function () {
+ assert_equals(selly.length, 4);
+}, "On getting, the length attribute must return the number of nodes represented by the collection.");
+
+test(function () {
+ selly.length = 7;
+ assert_equals(selly.length, 7,
+ "Number of nodes in collection should have changed");
+ assert_equals(selly.children.length, 7,
+ "Number of children should have changed");
+ for (var i = 4; i < 7; ++i) {
+ var child = selly.children[i];
+ assert_equals(child.localName, "option",
+ "new child should be an option");
+ assert_equals(child.namespaceURI, "http://www.w3.org/1999/xhtml",
+ "new child should be an HTML element");
+ assert_equals(child.attributes.length, 0,
+ "new child should not have attributes");
+ assert_equals(child.childNodes.length, 0,
+ "new child should not have child nodes");
+ }
+}, "Changing the length adds new nodes; The number of new nodes = new length minus old length");
+
+test(function () {
+ var elarray = [];
+ for (var i = 0; i < selly.length; i++) {
+ elarray.push(selly[i].value);
+ }
+ assert_array_equals(elarray, ["1", "2", "3", "4", "", "", ""]);
+}, "New nodes have no value");
+
+test(function () {
+ selly.length = 7;
+ assert_equals(selly.length, 7,
+ "Number of nodes in collection should not have changed");
+ assert_equals(selly.children.length, 7,
+ "Number of children should not have changed");
+}, "Setting a length equal to existing length changes nothing");
+
+test(function () {
+ selly.length = 4;
+ assert_equals(selly[6], undefined,
+ "previously set node is now undefined");
+ assert_equals(selly.length, 4,
+ "Number of nodes in collection is correctly changed");
+ assert_equals(selly.children.length, 4,
+ "Number of children should have changed");
+}, "Setting a length lower than the old length trims nodes from the end");
+
+test(function () {
+ var opts = selly.options;
+ opts[3] = null;
+ assert_equals(selly[3], undefined,
+ "previously set node is now undefined");
+ assert_equals(selly.length, 3,
+ "Number of nodes in collection is correctly changed");
+ assert_equals(selly.children.length, 3,
+ "Number of children should have changed");
+}, "Setting element to null by index removed the element");
+
+test(function () {
+ var opts = selly.options;
+ var new_option = document.createElement("option");
+ var replace_option = new_option.cloneNode(true);
+ new_option.value = "-1";
+ replace_option.value = "a";
+ opts[5] = new_option;
+ opts[0] = replace_option;
+
+ var elarray = [];
+ for (var i = 0; i < selly.length; i++) {
+ elarray.push(selly[i].value);
+ }
+ assert_array_equals(elarray, ["a", "2", "3", "", "", "-1"]);
+
+}, "Setting element by index should correctly append and replace elements");
+
+test(function () {
+ var selection = document.createElementNS("http://www.w3.org/1999/xhtml", "foo:select");
+
+ selection.length = 5;
+ assert_equals(selection.length, 5,
+ "Number of nodes in collection should have changed");
+ for (var i = 0; i < 5; ++i) {
+ var child = selection.children[i];
+ assert_equals(child.localName, "option",
+ "new child should be an option");
+ assert_equals(child.namespaceURI, "http://www.w3.org/1999/xhtml",
+ "new child should be an HTML element");
+ assert_equals(child.prefix, null,
+ "new child should not copy select's prefix");
+ }
+
+}, "Changing the length adds new nodes; The new nodes will not copy select's prefix");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/inserted-or-removed.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/inserted-or-removed.html
new file mode 100644
index 0000000000..0db2bf0e77
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/inserted-or-removed.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-select-element:nodes-are-inserted">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<select id="by-parser">
+<option selected>First</option>
+<option selected>Second</option>
+</select>
+
+<select id="by-parser-optgroup">
+<optgroup>
+<option selected>First</option>
+<option selected>Second</option>
+</optgroup>
+</select>
+
+<select id="by-dom"></select>
+
+<select id="by-innerHTML"></select>
+
+<script>
+test(() => {
+ const target = document.querySelector("#by-parser");
+ assert_equals(target.selectedOptions[0].textContent, 'Second');
+
+ const target2 = document.querySelector("#by-parser-optgroup");
+ assert_equals(target2.selectedOptions[0].textContent, 'Second');
+}, 'The last selected OPTION should win; Inserted by parser');
+
+test(() => {
+ const target = document.querySelector("#by-dom");
+ const option1 = document.createElement('option');
+ option1.defaultSelected = true;
+ option1.textContent = 'First';
+ const option2 = document.createElement('option');
+ option2.defaultSelected = true;
+ option2.textContent = 'Second';
+ target.appendChild(option1);
+ target.appendChild(option2);
+ assert_equals(target.selectedOptions[0].textContent, 'Second');
+
+ target.innerHTML = '';
+ const optgroup = document.createElement('optgroup');
+ const option3 = document.createElement('option');
+ option3.defaultSelected = true;
+ option3.textContent = 'First';
+ const option4 = document.createElement('option');
+ option4.defaultSelected = true;
+ option4.textContent = 'Second';
+ optgroup.appendChild(option3);
+ optgroup.appendChild(option4);
+ target.appendChild(optgroup);
+ assert_equals(target.selectedOptions[0].textContent, 'Second');
+}, 'The last selected OPTION should win; Inserted by DOM API');
+
+test(() => {
+ const target = document.querySelector("#by-dom");
+ target.innerHTML = '';
+ const inner = `<option value="one" selected>First</option>
+ <option value="two" selected>Second</option>`;
+
+ // Emulate what jQuery 1.x/2.x does in append(inner).
+ const fragment = document.createDocumentFragment();
+ const div = document.createElement('div');
+ div.innerHTML = '<select multiple>' + inner + '</select>';
+ while (div.firstChild.firstChild)
+ fragment.appendChild(div.firstChild.firstChild);
+ target.appendChild(fragment);
+ assert_equals(target.selectedOptions[0].textContent, 'Second');
+}, 'The last selected OPTION should win; Inserted by jQuery append()');
+
+test(() => {
+ const target = document.querySelector("#by-innerHTML");
+ target.innerHTML = '<option selected>First</option>' +
+ '<option selected>Second</option>';
+ assert_equals(target.selectedOptions[0].textContent, 'Second');
+
+ target.innerHTML = '<option selected>First</option>' +
+ '<optgroup><option selected>Second</option>' +
+ '<option selected>Third</option></optgroup>' +
+ '<option selected>Fourth</option>';
+ assert_equals(target.selectedOptions[0].textContent, 'Fourth');
+}, 'The last selected OPTION should win; Inserted by innerHTML');
+
+test (() => {
+ for (let insert_location = 0; insert_location < 3; ++insert_location) {
+ const target = document.querySelector('#by-innerHTML');
+ target.innerHTML = '<option>A</option>' +
+ '<option selected>C</option>' +
+ '<option>D</option>';
+ const refNode = target.querySelectorAll('option')[insert_location];
+
+ const opt = document.createElement('option');
+ opt.selected = true;
+ opt.textContent = 'B';
+ target.insertBefore(opt, refNode);
+ assert_equals(target.selectedOptions[0].textContent, 'B');
+ }
+}, 'If an OPTION says it is selected, it should be selected after it is inserted.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering-ref.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering-ref.html
new file mode 100644
index 0000000000..acf192d1d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<body>
+
+<form>
+<select>
+<option>Default</option>
+<option>Another</option>
+</select>
+
+<select>
+<option>Another</option>
+<option selected>Default</option>
+</select>
+
+<select multiple>
+<option>option 1</option>
+<option>option 2</option>
+</select>
+</form>
+
+</body>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering.html
new file mode 100644
index 0000000000..67da173ff2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/reset-algorithm-rendering.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Invalidation test on resetting &lt;select></title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-select-element:concept-form-reset-control">
+<link rel="help" href="http://crbug.com/1087031">
+<link rel="match" href="reset-algorithm-rendering-ref.html">
+<body>
+
+<form>
+<select>
+<option>Default</option>
+<option>Another</option>
+</select>
+
+<select>
+<option>Another</option>
+<option selected>Default</option>
+</select>
+
+<select multiple>
+<option>option 1</option>
+<option>option 2</option>
+</select>
+</form>
+
+<script>
+const selects = document.querySelectorAll('select');
+selects[0].selectedIndex = 1;
+selects[1].selectedIndex = 0;
+selects[2].options[1].selected = true;
+
+document.documentElement.addEventListener('TestRendered', e => {
+ document.querySelector('form').reset();
+ e.target.removeAttribute('class');
+});
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/show-picker-child-iframe.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/show-picker-child-iframe.html
new file mode 100644
index 0000000000..bba3989824
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/show-picker-child-iframe.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Test showPicker() in an iframe</title>
+<script type=module>
+ const urlParams = new URLSearchParams(location.search);
+ const documentDomain = urlParams.get('documentDomain');
+ if (documentDomain) {
+ document.domain = documentDomain;
+ }
+
+ let securityErrors = [];
+ const select = document.createElement("select");
+ try {
+ select.showPicker();
+ } catch (error) {
+ if (error instanceof DOMException && error.name == 'SecurityError') {
+ securityErrors.push("select");
+ }
+ }
+ parent.postMessage(securityErrors.join(','), "*");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/stylable-select-styles.css b/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/stylable-select-styles.css
new file mode 100644
index 0000000000..a7e9a87cdf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/stylable-select-styles.css
@@ -0,0 +1,18 @@
+.stylable-select-datalist {
+ box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+ box-sizing: border-box;
+ overflow: auto;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 0.25em;
+ padding: 0.25em 0;
+ background-color: Field;
+ margin: 0;
+ inset: auto;
+ min-inline-size: anchor-size(self-inline);
+ min-block-size: 1lh;
+ inset-block-start: anchor(self-end);
+ inset-inline-start: anchor(self-start);
+
+ font-family: Arial;
+ font-size: 13.3333px;
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-optgroup.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-optgroup.html
new file mode 100644
index 0000000000..fab5332f26
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-optgroup.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1477785">
+<link rel=help href="https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-add">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<select>
+ <option id=opt1>opt1</option>
+ <optgroup label=group1>
+ <option id=opt2>opt2</option>
+ </optgroup>
+</select>
+
+<script>
+test(() => {
+ const select = document.querySelector('select');
+ const optgroup = document.querySelector('optgroup');
+ const newOption = document.createElement('option');
+ newOption.textContent = 'new option';
+
+ select.options.add(newOption, 1);
+ assert_equals(select.options.length, 3);
+ assert_equals(select.options[0], opt1, 'First item should be opt1.');
+ assert_equals(select.options[1], newOption, 'Second item should be newOption.');
+ assert_equals(select.options[2], opt2, 'Third item should be opt2.');
+ assert_equals(newOption.parentNode, optgroup, 'The new option should be inside the optgroup.');
+}, 'select.add() with an index should work when the target is inside an optgroup.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-option-crash.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-option-crash.html
new file mode 100644
index 0000000000..78e9e7de53
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add-option-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1178128">
+
+<script>
+function iframeloadhandler() {
+ selectid[5] = optionid;
+}
+</script>
+<option id="optionid" selected>
+ <select id="selectid">
+ <select>
+ <iframe onload="iframeloadhandler()">
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add.html
new file mode 100644
index 0000000000..910be348ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-add.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLSelectElement Test: add()</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-add-dev">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form style="display:none">
+ <option id="testoption">
+ <select id="testselect1">
+ </select>
+ <select id="testselect2">
+ <option>TEST</option>
+ </select>
+ </option>
+</form>
+
+<script>
+
+test(() => {
+ let testselect1 = document.getElementById("testselect1");
+ let opt1 = new Option("Marry","1");
+ testselect1.add(opt1);
+ assert_equals(testselect1.options[0].value, "1");
+}, "test that HTMLSelectElement.add method can add option element");
+
+test(() => {
+ let testselect2 = document.getElementById("testselect2");
+ let opt2 = document.getElementById("testoption");
+ assert_throws_dom("HierarchyRequestError", () => {
+ testselect2.add(opt2);
+ });
+}, "test that HierarchyRequestError exception must be thrown when element is an ancestor of the element into which it is to be inserted");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-ask-for-reset.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-ask-for-reset.html
new file mode 100644
index 0000000000..822114fb26
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-ask-for-reset.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLSelectElement ask for reset</title>
+<link rel="author" title="Dongie Agnir" href="dongie.agnir@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var select = makeSelect(5);
+
+ select.children[4].selected = true;
+ unselectedExcept(select, 4);
+
+ select.children[4].remove();
+ unselectedExcept(select, 0); // remove selected node, should default to first
+
+ select.children[3].selected = true;
+
+ select.children[0].remove();
+ unselectedExcept(select, 2); // last node still selected
+
+ select.size = 2;
+ select.children[2].remove();
+
+ unselectedExcept(select, null);
+}, "ask for reset on node remove, non multiple.");
+
+test(function() {
+ var select = makeSelect(3);
+ select.children[1].selected = true;
+
+ // insert selected option, should remain selected
+ var opt4 = document.createElement("option");
+ opt4.selected = true;
+ select.appendChild(opt4);
+ unselectedExcept(select, 3);
+
+ // insert unselected, 3 should remain selected
+ var opt5 = document.createElement("option");
+ select.appendChild(opt5);
+ unselectedExcept(select, 3);
+}, "ask for reset on node insert, non multiple.");
+
+test(function() {
+ var select = makeSelect(3);
+
+ var options = select.children;
+
+ // select options from first to last
+ for (var i = 0; i < options.length; ++i) {
+ options[i].selected = true;
+ unselectedExcept(select, i);
+ }
+
+ // select options from last to first
+ for (var i = options.length - 1; i >= 0; --i) {
+ options[i].selected = true;
+ unselectedExcept(select, i);
+ }
+
+ options[2].selected = true;
+ options[2].selected = false; // none selected
+ unselectedExcept(select, 0);
+
+ // disable first so option at index 1 is first eligible
+ options[0].disabled = true;
+ options[2].selected = true;
+ options[2].selected = false; // none selected
+ unselectedExcept(select, 1);
+
+ select.size = 2;
+ options[1].selected = false;
+ unselectedExcept(select, null); // size > 1 so should not default to any
+}, "change selectedness of option, non multiple.");
+
+
+function unselectedExcept(sel, opt) {
+ for (var i = 0; i < sel.children.length; ++i) {
+ if (i != opt) {
+ assert_false(sel.children[i].selected, "option should not be selected.");
+ }
+ if (opt != null) {
+ assert_true(sel.children[opt].selected, "option should be selected.");
+ }
+ }
+}
+
+function makeSelect(n) {
+ var sel = document.createElement("select");
+ for (var i = 0; i < n; ++i) {
+ opt = document.createElement("option");
+ sel.appendChild(opt);
+ }
+ return sel;
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-ref.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-ref.html
new file mode 100644
index 0000000000..46bbd0ccd0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel=stylesheet href="resources/stylable-select-styles.css">
+
+<button popovertarget=popover id=button>button</button>
+<div id=popover popover=auto anchor=button class=stylable-select-datalist>
+ <option>one</option>
+ <option>two</option>
+</div>
+
+<script>
+document.getElementById('popover').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist.tentative.html
new file mode 100644
index 0000000000..b74957feed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist.tentative.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9799">
+<link rel=match href="select-child-button-and-datalist-ref.html">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<select>
+ <button type=popover>button</button>
+ <datalist>
+ <option>one</option>
+ <option>two</option>
+ </datalist>
+</select>
+
+<script>
+document.querySelector('button').click();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-in-table-crash.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-in-table-crash.html
new file mode 100644
index 0000000000..d1f1cee217
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-in-table-crash.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1519397">
+<table><select>A0A0AA<<datalist><title></title><table><table><td>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-multiple.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-multiple.html
new file mode 100644
index 0000000000..e348064151
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-multiple.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLSelectElement ask for reset</title>
+<link rel="author" title="Sebastian Mayr" href="wpt@smayr.name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<select multiple id="initial-selected">
+ <option selected>Test 1</option>
+ <option selected>Test 2</option>
+</select>
+<select multiple id="scripted-select">
+ <option selected>Test 1</option>
+ <option>Test 2</option>
+</select>
+<div id=log></div>
+<script>
+"use strict";
+
+test(() => {
+
+ const select = document.getElementById("initial-selected");
+ assert_true(select.options[0].selected, "first option should be selected.");
+ assert_true(select.options[1].selected, "second option should be selected.");
+
+}, "multiple selected options exist, both set from markup");
+
+test(() => {
+
+ const select = document.getElementById("initial-selected");
+ select.options[1].selected = true;
+
+ assert_true(select.options[0].selected, "first option should be selected.");
+ assert_true(select.options[1].selected, "second option should be selected.");
+
+}, "multiple selected options exist, one set from script");
+
+// crbug.com/1245443
+test(() => {
+ let select = document.createElement("select");
+ select.length = 4;
+ let o1 = select.options.item(1);
+ select.multiple = true;
+ select.selectedIndex = 2;
+ o1.selected = true;
+ select.multiple = false;
+ assert_equals(select.selectedOptions.length, 1);
+}, "Removing multiple attribute reduces the number of selected OPTIONs to 1");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-named-getter.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-named-getter.html
new file mode 100644
index 0000000000..da43da9d92
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-named-getter.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Absence of a named getter on HTMLSelectElement</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<select id=select>
+ <option id=op1>A</option>
+ <option name=op2>B</option>
+ <option id=op3 name=op4>C</option>
+ <option id=>D</option>
+ <option name=>D</option>
+</select>
+<script>
+test(function() {
+ var select = document.getElementById("select");
+ assert_equals(select.op1, undefined);
+ assert_false("op1" in select);
+ assert_equals(select.namedItem("op1"), select.children[0]);
+}, "Option with id")
+
+test(function() {
+ var select = document.getElementById("select");
+ assert_equals(select.op2, undefined);
+ assert_false("op2" in select);
+ assert_equals(select.namedItem("op2"), select.children[1]);
+}, "Option with name")
+
+test(function() {
+ var select = document.getElementById("select");
+ assert_equals(select.op3, undefined);
+ assert_false("op3" in select);
+ assert_equals(select.namedItem("op3"), select.children[2]);
+
+ assert_equals(select.op4, undefined);
+ assert_false("op4" in select);
+ assert_equals(select.namedItem("op4"), select.children[2]);
+}, "Option with name and id")
+
+test(function() {
+ var select = document.getElementById("select");
+ assert_equals(select[""], undefined);
+ assert_false("" in select);
+ assert_equals(select.namedItem(""), null);
+}, "Empty string name");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-parsing.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-parsing.tentative.html
new file mode 100644
index 0000000000..31133446d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-parsing.tentative.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9799">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+
+<select id=s1>
+ <div>div 1</div>
+ <button>button</button>
+ <div>div 2</div>
+ <datalist>
+ <option>option</option>
+ </datalist>
+ <div>div 3</div>
+</select>
+
+<select id=s2>
+ <button>button
+</select>
+
+<select id=s3>
+ <datalist>datalist
+</select>
+
+<select id=s4>
+ <button>
+ <select></select>
+ </button>
+</select>
+
+<select id=s5>
+ <button>
+ <div>
+ <select>
+</select>
+
+<select id=s6>
+<button>
+<button></button>
+</button>
+<datalist>
+<datalist></datalist>
+</datalist>
+</select>
+
+<div id=afterlast>
+ keep this div after the last test case
+</div>
+
+<script>
+test(() => {
+ // The document.body check here and in the other tests is to make sure that a
+ // previous test case didn't leave the HTML parser open on another element.
+ assert_equals(document.getElementById('s1').parentNode, document.body);
+ assert_equals(document.getElementById('s1').innerHTML, `
+ div 1
+ <button>button</button>
+ div 2
+ <datalist>
+ <option>option</option>
+ </datalist>
+ div 3
+`);
+}, '<button>s and <datalist>s should be allowed in <select>.');
+
+test(() => {
+ assert_equals(document.getElementById('s2').parentNode, document.body);
+ assert_equals(document.getElementById('s2').innerHTML, `
+ <button>button
+</button>`);
+}, '</select> should close <button>.');
+
+test(() => {
+ assert_equals(document.getElementById('s3').parentNode, document.body);
+ assert_equals(document.getElementById('s3').innerHTML, `
+ <datalist>datalist
+</datalist>`);
+}, '</select> should close <datalist>.');
+
+test(() => {
+ assert_equals(document.getElementById('s4').parentNode, document.body);
+ assert_equals(document.getElementById('s4').innerHTML, `
+ <button>
+ </button>`);
+}, '<select> in <button> in <select> should remove inner <select>.');
+
+test(() => {
+ assert_equals(document.getElementById('s5').parentNode, document.body);
+ assert_equals(document.getElementById('s5').innerHTML, `
+ <button>
+ <div>
+ </div></button>`);
+}, '<select> in <select><button><div> should remove inner <select>.');
+
+test(() => {
+ assert_equals(document.getElementById('s6').parentNode, document.body);
+ assert_equals(document.getElementById('s6').innerHTML, `
+<button>
+</button>
+
+<datalist>
+</datalist>
+
+`);
+}, 'Nested <button>s or <datalist>s in <select> should be dropped.');
+
+test(() => {
+ assert_equals(document.getElementById('afterlast').parentNode, document.body);
+}, 'The last test should not leave any tags open after parsing.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-remove.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-remove.html
new file mode 100644
index 0000000000..cf2128bd15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-remove.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLSelectElement.remove</title>
+<link rel="author" title="Ms2ger" href="Ms2ger@gmail.com">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-childnode-remove">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-select-remove">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-htmloptionscollection-remove">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function testRemove(getter, desc) {
+ test(function() {
+ var div = document.createElement("div");
+ var select = document.createElement("select");
+ div.appendChild(select);
+ assert_equals(select.parentNode, div);
+
+ var options = [];
+ for (var i = 0; i < 3; ++i) {
+ var option = document.createElement("option");
+ option.textContent = String(i);
+ select.appendChild(option);
+ options.push(option);
+ }
+
+ getter(select).remove(-1);
+ assert_array_equals(select.options, options, "After remove(-1)");
+ assert_equals(select.parentNode, div);
+
+ getter(select).remove(3);
+ assert_array_equals(select.options, options, "After remove(3)");
+ assert_equals(select.parentNode, div);
+
+ getter(select).remove(0);
+ assert_array_equals(select.options, [options[1], options[2]], "After remove(0)");
+ assert_equals(select.parentNode, div);
+ }, desc)
+}
+testRemove(function(select) { return select; }, "select.remove(n) should work");
+testRemove(function(select) { return select.options; }, "select.options.remove(n) should work");
+test(function() {
+ var div = document.createElement("div");
+ var select = document.createElement("select");
+ div.appendChild(select);
+ assert_equals(select.parentNode, div);
+ assert_equals(div.firstChild, select);
+
+ select.remove();
+ assert_equals(select.parentNode, null);
+ assert_equals(div.firstChild, null);
+}, "remove() should work on select elements.")
+test(function() {
+ var div = document.createElement("div");
+ var select = document.createElement("select");
+ div.appendChild(select);
+ assert_equals(select.parentNode, div);
+ assert_equals(div.firstChild, select);
+
+ Element.prototype.remove.call(select);
+ assert_equals(select.parentNode, null);
+ assert_equals(div.firstChild, null);
+}, "Element#remove() should work on select elements.")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-selectedOptions.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-selectedOptions.html
new file mode 100644
index 0000000000..bd5984a6b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-selectedOptions.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>HTMLSelectElement.selectedOptions</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-select-selectedoptions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<select id="select-none-selected">
+ <option>One</option>
+ <option>Two</option>
+ <option>Three</option>
+</select>
+
+<select id="select-one-selected">
+ <option>One</option>
+ <option selected>Two</option>
+ <option>Three</option>
+</select>
+
+<select multiple id="multiple-select-none-selected">
+ <option>One</option>
+ <option>Two</option>
+ <option>Three</option>
+</select>
+
+<select multiple id="multiple-select-two-selected">
+ <option>One</option>
+ <option selected>Two</option>
+ <option selected>Three</option>
+</select>
+
+<select id="select-named-selected">
+ <option>One</option>
+ <option>Two</option>
+ <option id="named-option" selected>Three</option>
+</select>
+
+<select id="invalid-select">
+ <option selected>One</option>
+ <option selected>Two</option>
+ <option>Three</option>
+</select>
+
+<select id="select-same-object">
+ <option>One</option>
+ <option selected>Two</option>
+ <option>Three</option>
+</select>
+
+<select multiple id="select-same-object-change">
+ <option selected>One</option>
+ <option selected>Two</option>
+ <option selected>Three</option>
+</select>
+
+<script>
+"use strict";
+
+test(() => {
+ const select = document.getElementById("select-none-selected");
+
+ assert_array_equals(select.selectedOptions, [select.children[0]]);
+ assert_equals(select.selectedOptions.length, 1);
+
+}, ".selectedOptions with no selected option");
+
+test(() => {
+ const select = document.getElementById("select-one-selected");
+
+ assert_array_equals(select.selectedOptions, [select.children[1]]);
+ assert_equals(select.selectedOptions.length, 1);
+}, ".selectedOptions with one selected option");
+
+test(() => {
+ const select = document.getElementById("multiple-select-none-selected");
+
+ assert_equals(select.selectedOptions.item(0), null);
+ assert_equals(select.selectedOptions.length, 0);
+}, ".selectedOptions using the 'multiple' attribute with no selected options");
+
+test(() => {
+ const select = document.getElementById("multiple-select-two-selected");
+
+ assert_equals(select.selectedOptions.item(0), select.children[1]);
+ assert_equals(select.selectedOptions.item(1), select.children[2]);
+ assert_equals(select.selectedOptions.length, 2);
+}, ".selectedOptions using the 'multiple' attribute with two selected options");
+
+// "A select element whose multiple attribute is not specified must not have
+// more than one descendant option element with its selected attribute set."
+// - https://html.spec.whatwg.org/multipage/forms.html#the-option-element:the-select-element-6
+
+// "If two or more option elements in the select element's list of options
+// have their selectedness set to true, set the selectedness of all but
+// the last option element with its selectedness set to true in the list of
+// options in tree order to false."
+// - https://html.spec.whatwg.org/multipage/forms.html#the-select-element:the-option-element-21
+test(() => {
+ const select = document.getElementById("invalid-select");
+
+ assert_array_equals(select.selectedOptions, [select.children[1]]);
+ assert_equals(select.selectedOptions.length, 1);
+
+}, ".selectedOptions without the 'multiple' attribute but " +
+ "more than one selected option should return the last one");
+
+test(() => {
+ const select = document.getElementById("select-named-selected");
+
+ assert_equals(select.selectedOptions.constructor, HTMLCollection);
+ assert_equals(select.selectedOptions.namedItem("named-option"), select.children[2]);
+}, ".selectedOptions should return `HTMLCollection` instance");
+
+test(() => {
+ const select = document.getElementById("select-same-object");
+ const selectAgain = document.getElementById("select-same-object");
+
+ assert_equals(select.selectedOptions, selectAgain.selectedOptions);
+
+}, ".selectedOptions should always return the same value - [SameObject]");
+
+test(() => {
+ const select = document.getElementById("select-same-object-change");
+ const before = select.selectedOptions;
+ assert_equals(before.length, 3);
+
+ select.selectedOptions[1].selected = false;
+
+ const after = select.selectedOptions;
+
+ assert_equals(before, after);
+ assert_equals(before.length, 2);
+ assert_equals(after.length, 2);
+
+}, ".selectedOptions should return the same object after selection changes - [SameObject]");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-setcustomvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-setcustomvalidity.html
new file mode 100644
index 0000000000..15308c1a8f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-setcustomvalidity.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<title>select setCustomValidity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<select id='select_test'></select>
+
+<script>
+
+test(() => {
+ let elem = document.getElementById("select_test");
+ assert_false(elem.validity.customError);
+ elem.setCustomValidity("custom error");
+ assert_true(elem.validity.customError);
+}, "select setCustomValidity is correct")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-validity.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-validity.html
new file mode 100644
index 0000000000..9f044879d9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-validity.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLSelectElement.checkValidity</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-select-element:attr-select-required-4">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+
+test(function() {
+ var select = document.createElement('select');
+ assert_true(select.willValidate, "A select element is a submittable element that is a candidate for constraint validation.");
+ var placeholder = document.createElement('option');
+ select.appendChild(placeholder);
+ assert_true(select.checkValidity(), "Always valid when the select isn't a required value.");
+ select.required = true;
+ assert_true(placeholder.selected, "If display size is 1, multiple is absent and no options have selectedness true, the first option is selected.");
+ assert_equals(select.value, "", "The placeholder's value should be the select's value right now");
+ assert_false(select.checkValidity(), "A selected placeholder option should invalidate the select.");
+ var emptyOption = document.createElement('option');
+ select.appendChild(emptyOption);
+ emptyOption.selected = true;
+ assert_equals(select.value, "", "The empty value should be set.");
+ assert_true(select.checkValidity(), "An empty non-placeholder option should be a valid choice.");
+ var filledOption = document.createElement('option');
+ filledOption.value = "test";
+ select.appendChild(filledOption);
+ filledOption.selected = true;
+ assert_equals(select.value, "test", "The non-empty value should be set.");
+ assert_true(select.checkValidity(), "A non-empty non-placeholder option should be a valid choice.");
+ select.removeChild(placeholder);
+ select.appendChild(emptyOption); // move emptyOption to second place
+ emptyOption.selected = true;
+ assert_equals(select.value, "", "The empty value should be set.");
+ assert_true(select.checkValidity(), "Only the first option can be seen as a placeholder.");
+ placeholder.disabled = true;
+ select.insertBefore(placeholder, filledOption);
+ placeholder.selected = true;
+ assert_equals(select.value, "", "A disabled first placeholder option should result in an empty value.");
+ assert_false(select.checkValidity(), "A disabled first placeholder option should invalidate the select.");
+}, "Placeholder label options within a select");
+
+test(function() {
+ var select = document.createElement('select');
+ select.required = true;
+ var optgroup = document.createElement('optgroup');
+ var emptyOption = document.createElement('option');
+ optgroup.appendChild(emptyOption);
+ select.appendChild(optgroup);
+ emptyOption.selected = true;
+ assert_equals(select.value, "", "The empty value should be set.");
+ assert_true(select.checkValidity(), "The first option is not considered a placeholder if it is located within an optgroup.");
+ var otherEmptyOption = document.createElement('option');
+ otherEmptyOption.value = "";
+ select.appendChild(otherEmptyOption);
+ otherEmptyOption.selected = true;
+ assert_equals(select.value, "", "The empty value should be set.");
+ assert_true(select.checkValidity(), "The empty option should be accepted as it is not the first option in the tree ordered list.");
+}, "Placeholder label-like options within optgroup");
+
+test(function() {
+ var select = document.createElement('select');
+ select.required = true;
+ select.size = 2;
+ var emptyOption = document.createElement('option');
+ select.appendChild(emptyOption);
+ assert_false(emptyOption.selected, "Display size is not 1, so the first option should not be selected.");
+ assert_false(select.checkValidity(), "If no options are selected the select must be seen as invalid.");
+ emptyOption.selected = true;
+ assert_true(select.checkValidity(), "If one option is selected, the select should be considered valid.");
+ var otherEmptyOption = document.createElement('option');
+ otherEmptyOption.value = "";
+ select.appendChild(otherEmptyOption);
+ otherEmptyOption.selected = true;
+ assert_false(emptyOption.selected, "Whenever an option has its selectiveness set to true, the other options must be set to false.");
+ otherEmptyOption.selected = false;
+ assert_false(otherEmptyOption.selected, "It should be possible to set the selectiveness to false with a display size more than one.");
+ assert_false(select.checkValidity(), "If no options are selected the select must be seen as invalid.");
+}, "Validation on selects with display size set as more than one");
+
+test(function() {
+ var select = document.createElement('select');
+ select.required = true;
+ select.multiple = true;
+ var emptyOption = document.createElement('option');
+ select.appendChild(emptyOption);
+ assert_false(select.checkValidity(), "If no options are selected the select must be seen as invalid.");
+ emptyOption.selected = true;
+ assert_true(select.checkValidity(), "If one option is selected, the select should be considered valid.");
+ var optgroup = document.createElement('optgroup');
+ optgroup.appendChild(emptyOption); // Move option to optgroup
+ select.appendChild(optgroup);
+ assert_true(select.checkValidity(), "If one option within an optgroup or not is selected, the select should be considered valid.");
+}, "Validation on selects with multiple set");
+
+test(function() {
+ var select = document.createElement('select');
+ select.required = true;
+ var option = document.createElement('option');
+ option.value = 'test';
+ option.disabled = true;
+ option.selected = true;
+ select.appendChild(option);
+ assert_true(select.checkValidity(), "When a required select has an option that is selected and disabled, the select should be considered valid.");
+}, "Validation on selects with non-empty disabled option");
+
+test(function() {
+ var select = document.createElement('select');
+ select.required = true;
+ var placeholder = document.createElement('option');
+ select.appendChild(placeholder);
+ var nonPlaceholder = document.createElement('option');
+ nonPlaceholder.textContent = "non-placeholder-option";
+ select.appendChild(nonPlaceholder);
+
+ assert_false(select.checkValidity(), "If the placeholder label option is selected, required select element shouldn't be valid.");
+ placeholder.remove();
+ assert_true(select.checkValidity(), "If the placeholder label option is removed, required select element should become valid.");
+ select.prepend(placeholder);
+ assert_false(select.checkValidity(), "If the placeholder label option is selected, required select element shouldn't be valid.");
+
+}, "Remove and add back the placeholder label option");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-value.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-value.html
new file mode 100644
index 0000000000..d8d5263e3e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-value.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTMLSelectElement.value</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-select-value">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+
+<select id=sel1>
+ <option value=0></option>
+ <option selected value=1></option>
+</select>
+
+<select id=sel2>
+ <optgroup>
+ <option value=0></option>
+ </optgroup>
+ <optgroup></optgroup>
+ <optgroup>
+ <option></option>
+ <option value=1></option>
+ <option selected value=2></option>
+ </optgroup>
+</select>
+
+<select id=sel3>
+ <option selected value=1></option>
+</select>
+
+<select id=sel4></select>
+
+<script>
+test(function() {
+ var select = document.getElementById('sel1');
+ assert_equals(select.value, '1');
+}, 'options');
+
+test(function() {
+ var select = document.getElementById('sel2');
+ assert_equals(select.value, '2');
+}, 'optgroups');
+
+test(function() {
+ var select = document.getElementById('sel3');
+ var option = select.options[0];
+ var div = document.createElement('div');
+ select.appendChild(div);
+ div.appendChild(option);
+ assert_equals(select.value, '');
+}, 'option is child of div');
+
+test(function() {
+ var select = document.getElementById('sel4');
+ assert_equals(select.value, '');
+}, 'no options');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-willvalidate-readonly-attribute.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-willvalidate-readonly-attribute.html
new file mode 100644
index 0000000000..d3f4ce43cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-willvalidate-readonly-attribute.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Select element with "readonly" attribute shouldn't be barred from constraint validation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<select id="singleSelect" readonly>
+ <option>1
+ <option>2
+</select>
+
+<select id="multiSelect" readonly multiple>
+ <option>a
+ <option>b
+ <option>c
+ <option>d
+</select>
+
+<script>
+ test(() => {
+ assert_true(singleSelect.willValidate);
+ assert_true(multiSelect.willValidate);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/selected-index.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/selected-index.html
new file mode 100644
index 0000000000..7f7fd9a1a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/selected-index.html
@@ -0,0 +1,143 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLSelectElement selectedIndex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+
+<form id=form>
+ <select id=empty></select>
+
+ <select id=default>
+ <option></option>
+ <option></option>
+ <option></option>
+ <option></option>
+ <option></option>
+ </select>
+
+ <select id=disabled>
+ <option disabled></option>
+ <option></option>
+ </select>
+
+ <select id=selected>
+ <option></option>
+ <option selected></option>
+ </select>
+
+ <select id=display-none>
+ <option style="display:none"></option>
+ <option></option>
+ </select>
+
+ <select id=minus-one>
+ <option value=1>1</option>
+ <option value=2>2</option>
+ </select>
+</form>
+
+<script>
+function assertSelectedIndex(select, value) {
+ assert_equals(select.selectedIndex, value);
+ assert_equals(select.options.selectedIndex, value);
+}
+
+function assertSelectValue(select, value) {
+ assert_equals(select.value, value);
+}
+
+test(function () {
+ var select = document.getElementById('empty');
+ assertSelectedIndex(select, -1);
+}, "get empty");
+
+test(function () {
+ var select = document.getElementById('default');
+ assertSelectedIndex(select, 0);
+}, "get default");
+
+test(function () {
+ var select = document.getElementById('disabled');
+ assertSelectedIndex(select, 1);
+}, "get disabled");
+
+test(function () {
+ var select = document.getElementById('selected');
+ assertSelectedIndex(select, 1);
+}, "get unselected");
+
+test(function () {
+ var select = document.getElementById('empty');
+ select.selectedIndex = 1;
+ assertSelectedIndex(select, -1);
+}, "set empty (HTMLSelectElement)");
+
+test(function () {
+ var select = document.getElementById('empty');
+ select.options.selectedIndex = 1;
+ assertSelectedIndex(select, -1);
+}, "set empty (HTMLOptionsCollection)");
+
+test(function () {
+ var select = document.getElementById('default');
+ assertSelectedIndex(select, 0);
+ select.selectedIndex = 2;
+ assertSelectedIndex(select, 2);
+ this.add_cleanup(() => { select.selectedIndex = 0; });
+}, "set (HTMLSelectElement)");
+
+test(function () {
+ var select = document.getElementById('default');
+ assertSelectedIndex(select, 0);
+ select.options.selectedIndex = 2;
+ assertSelectedIndex(select, 2);
+ this.add_cleanup(() => { select.selectedIndex = 0; });
+}, "set (HTMLOptionsCollection)");
+
+test(function () {
+ var select = document.getElementById('selected');
+ var form = document.getElementById('form');
+ assertSelectedIndex(select, 1);
+ select.selectedIndex = 0;
+ assertSelectedIndex(select, 0);
+ form.reset();
+ assertSelectedIndex(select, 1);
+}, "set and reset (HTMLSelectElement)");
+
+test(function () {
+ var select = document.getElementById('selected');
+ var form = document.getElementById('form');
+ assertSelectedIndex(select, 1);
+ select.options.selectedIndex = 0;
+ assertSelectedIndex(select, 0);
+ form.reset();
+ assertSelectedIndex(select, 1);
+}, "set and reset (HTMLOptionsCollection)");
+
+test(function () {
+ var select = document.getElementById('display-none');
+ assertSelectedIndex(select, 0);
+}, "get display:none");
+
+test(function () {
+ var select = document.getElementById('display-none');
+ select.offsetTop; // force rendering
+ assertSelectedIndex(select, 0);
+ select.options[1].selected = true;
+ assertSelectedIndex(select, 1);
+ select.options[1].selected = false;
+ assertSelectedIndex(select, 0);
+}, "reset to display:none");
+
+test(function() {
+ var select = document.getElementById("minus-one");
+ assertSelectedIndex(select, 0);
+
+ select.selectedIndex = -1;
+
+ assertSelectedIndex(select, -1);
+ assertSelectValue(select, "");
+
+}, "set selectedIndex=-1");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-cv-hidden.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-cv-hidden.html
new file mode 100644
index 0000000000..8990734f93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-cv-hidden.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Test showPicker() being rendered requirement with content-visibility</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div style="content-visibility: hidden">
+ <select id="select">
+ <option>Item 1</option>
+ </select>
+</div>
+<script>
+promise_test(async t => {
+ await test_driver.bless('show picker');
+ assert_throws_dom('NotSupportedError', () => { select.showPicker(); });
+
+ // Test that dynamically changing to actually being rendered works.
+ await test_driver.bless('show picker');
+ select.parentElement.style.contentVisibility = 'visible';
+ select.showPicker();
+ select.blur();
+}, 'select showPicker() throws when content-visibility hidden');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-rendered.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-rendered.html
new file mode 100644
index 0000000000..e7be4aea52
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-being-rendered.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Test showPicker() being rendered requirement</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<select id="select" style="display: none;">
+ <option>Item 1</option>
+</select>
+<script>
+promise_test(async t => {
+ await test_driver.bless('show picker');
+ assert_throws_dom('NotSupportedError', () => { select.showPicker(); });
+
+ // Test that dynamically changing to actually being rendered works.
+ await test_driver.bless('show picker');
+ select.style.display = 'inline-block';
+ select.showPicker();
+ select.blur();
+}, 'select showPicker() throws when not being rendered');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-cross-origin-iframe.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-cross-origin-iframe.tentative.html
new file mode 100644
index 0000000000..3f710b39c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-cross-origin-iframe.tentative.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<title>Test showPicker() called from cross-origin iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<iframe id="iframe1"></iframe>
+<iframe id="iframe2"></iframe>
+<iframe id="iframe3"></iframe>
+<iframe id="iframe4"></iframe>
+</body>
+<script>
+ function waitForSecurityErrors() {
+ return new Promise((resolve) => {
+ window.addEventListener("message", (event) => resolve(event.data), {
+ once: true,
+ });
+ });
+ }
+
+ promise_test(async (t) => {
+ iframe1.src =
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html";
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "",
+ "In same-origin iframes, showPicker() does not throw a SecurityError."
+ );
+ });
+
+ promise_test(async (t) => {
+ iframe2.src =
+ get_host_info().HTTP_NOTSAMESITE_ORIGIN +
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html";
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "select",
+ "In cross-origin iframes, showPicker() throws a SecurityError."
+ );
+ });
+
+ promise_test(async (t) => {
+ iframe3.src =
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html?documentDomain=" + get_host_info().ORIGINAL_HOST;
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "",
+ "In same-origin but cross-origin-domain iframes, showPicker() does not throw a SecurityError."
+ );
+ });
+
+ promise_test(async (t) => {
+ document.domain = get_host_info().ORIGINAL_HOST;
+ iframe4.src =
+ get_host_info().HTTP_REMOTE_ORIGIN +
+ new URL("resources/", self.location).pathname +
+ "show-picker-child-iframe.html?documentDomain=" + get_host_info().ORIGINAL_HOST;
+
+ // Wait for the iframe to report security errors when calling showPicker().
+ const securityErrors = await waitForSecurityErrors();
+ assert_equals(
+ securityErrors,
+ "select",
+ "In cross-origin but same-origin-domain iframes, showPicker() throws a SecurityError."
+ );
+ });
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-disabled.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-disabled.tentative.html
new file mode 100644
index 0000000000..f20bc2cf61
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-disabled.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Test showPicker() disabled requirement</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<select id="select" disabled>
+ <option>Item 1</option>
+</select>
+<script>
+ test(() => {
+ assert_throws_dom('InvalidStateError', () => { select.showPicker(); });
+ }, 'select showPicker() throws when disabled');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-multiple.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-multiple.tentative.html
new file mode 100644
index 0000000000..c38e98ee4e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-multiple.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Test showPicker() on multiple selects</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<select id="select" multiple>
+ <option>Item 1</option>
+</select>
+<script>
+promise_test(async t => {
+ await test_driver.bless('show picker');
+ select.showPicker();
+ select.blur();
+}, `select showPicker() does not throw when called on a <select multiple>`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-size.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-size.tentative.html
new file mode 100644
index 0000000000..b9b10c42a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-size.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Test showPicker() on sized selects</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<select id="select" size="4">
+ <option>Item 1</option>
+</select>
+<script>
+promise_test(async t => {
+ await test_driver.bless('show picker');
+ select.showPicker();
+ select.blur();
+}, `select showPicker() does not throw when called on a <select size="4">`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-user-gesture.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-user-gesture.tentative.html
new file mode 100644
index 0000000000..e917fe2a61
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/show-picker-user-gesture.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Test showPicker() user gesture requirement</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body></body>
+<script type=module>
+ test(() => {
+ const select = document.createElement("select");
+
+ assert_throws_dom('NotAllowedError', () => { select.showPicker(); });
+ }, `select showPicker() requires a user gesture`);
+
+ promise_test(async t => {
+ const select = document.createElement("select");
+ document.body.append(select)
+
+ await test_driver.bless('show picker');
+ select.showPicker();
+ select.blur();
+ }, `select showPicker() does not throw when user activation is active`);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html
new file mode 100644
index 0000000000..fd7f2b7bca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<button>button</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html
new file mode 100644
index 0000000000..3ab7044366
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="button-type-selectlist-appearance-ref.html">
+
+<button type=selectlist>button</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html
new file mode 100644
index 0000000000..7cf2aff515
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: ask-for-reset</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form name="fm1" id="form1">
+ <selectlist id="selectlist1">
+ <option>one</option>
+ <option>two</option>
+ </selectlist>
+
+ <selectlist id="selectlist2">
+ <option>one</option>
+ <option selected>two</option>
+ </selectlist>
+
+ <selectlist id="selectlist3">
+ <option>one</option>
+ <option selected>two</option>
+ <option selected>three</option>
+ </selectlist>
+</form>
+
+<script>
+function createSelectList(numberOfOptions) {
+ let selectList = document.createElement("selectlist");
+ for (let i = 0; i < numberOfOptions; i++) {
+ let option = document.createElement("option");
+ option.value = i;
+ selectList.appendChild(option);
+ }
+ return selectList;
+}
+
+function checkSelection(selectList, selectedOptionIndex, msg) {
+ for (let i = 0; i < selectList.children.length; i++) {
+ if (i != selectedOptionIndex) {
+ assert_false(selectList.children[i].selected);
+ }
+ }
+ assert_true(selectList.children[selectedOptionIndex].selected, msg);
+ assert_equals(selectList.value, selectList.children[selectedOptionIndex].value);
+}
+
+test(() => {
+ let selectList = createSelectList(5);
+
+ selectList.children[4].selected = true;
+ checkSelection(selectList, 4);
+
+ selectList.children[4].remove();
+ checkSelection(selectList, 0, "After removing the selected option, selection should default to first option.");
+
+ selectList.children[3].selected = true;
+ checkSelection(selectList, 3);
+ selectList.children[0].remove();
+ checkSelection(selectList, 2, "Removing non-selected option should have no effect.");
+}, "ask-for-reset when removing option");
+
+test(() => {
+ let selectList = createSelectList(3);
+ selectList.children[1].selected = true;
+
+ let newOption = document.createElement("option");
+ newOption.selected = true;
+ selectList.appendChild(newOption);
+ checkSelection(selectList, 3, "Inserting a selected option should update selection.");
+
+ let newOption2 = document.createElement("option");
+ newOption2.selected = true;
+ selectList.prepend(newOption2);
+ checkSelection(selectList, 0, "Inserting a selected option should update selection, even though it's not last in tree order.");
+
+ let newOption3 = document.createElement("option");
+ selectList.appendChild(newOption3);
+ checkSelection(selectList, 0, "Inserting a non-selected option should have no effect.");
+}, "ask-for-reset when inserting option");
+
+test(() => {
+ let selectList = createSelectList(3);
+ let options = selectList.children;
+
+ // select options from first to last
+ for (let i = 0; i < options.length; i++) {
+ options[i].selected = true;
+ checkSelection(selectList, i);
+ }
+
+ // select options from last to first
+ for (let i = options.length - 1; i >= 0; i--) {
+ options[i].selected = true;
+ checkSelection(selectList, i);
+ }
+
+ options[2].selected = true;
+ checkSelection(selectList, 2);
+ options[2].selected = false;
+ checkSelection(selectList, 0, "First non-disabled option should be selected.");
+
+ options[0].disabled = true;
+ options[2].selected = true;
+ checkSelection(selectList, 2);
+ options[2].selected = false;
+ checkSelection(selectList, 1, "First non-disabled option should be selected.");
+}, "ask-for-reset when changing selectedness of option");
+
+test(() => {
+ let selectList1 = document.getElementById("selectlist1");
+ let selectList2 = document.getElementById("selectlist2");
+ let selectList3 = document.getElementById("selectlist3");
+
+ document.getElementById("form1").reset();
+
+ assert_equals(selectList1.value, "one", "First non-disabled option should be selected.");
+ assert_equals(selectList2.value, "two", "The selected option should be selected.");
+ assert_equals(selectList3.value, "three", "Last selected option should be selected.")
+}, "ask-for-reset for form");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html
new file mode 100644
index 0000000000..d3e6febd78
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1408838">
+<link rel=help href="https://github.com/openui/open-ui/issues/639">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id=defaultbutton-defaultlistbox>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<selectlist id=custombutton-defaultlistbox>
+ <button type=selectlist>custom button</button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<selectlist id=defaultbutton-customlistbox>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<selectlist id=custombutton-customlistbox>
+ <button type=selectlist>custom button</button>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<script>
+document.querySelectorAll('selectlist').forEach(selectlist => {
+ promise_test(async () => {
+ assert_false(selectlist.matches(':open'),
+ 'The listbox should not be showing at the start of the test.');
+
+ await test_driver.click(selectlist);
+ assert_true(selectlist.matches(':open'),
+ 'The listbox should be showing after clicking the button.');
+
+ await test_driver.click(selectlist);
+ assert_false(selectlist.matches(':open'),
+ 'The listbox should be closed after clicking the button.');
+ }, `${selectlist.id}: Clicking the selectlist's button should toggle the listbox.`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html
new file mode 100644
index 0000000000..37d4b1b471
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<button>hello world</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html
new file mode 100644
index 0000000000..50ad339b05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-button-type-appearance-ref.html">
+
+<selectlist>
+ <button type=selectlist>hello world</button>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html
new file mode 100644
index 0000000000..cc7eb62a63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist>
+ <button type=selectlist id=b1>first button</button>
+ <button type=selectlist id=b2>second button</button>
+ <button id=b3>third button</button>
+ <option>option</option>
+</selectlist>
+
+<script>
+const ESC = '\uE00C'
+
+promise_test(async () => {
+ const selectlist = document.querySelector('selectlist');
+ const b1 = document.getElementById('b1');
+ const b2 = document.getElementById('b2');
+ const b3 = document.getElementById('b3');
+
+ assert_false(selectlist.open, 'The selectlist should start closed.');
+ await test_driver.click(b1);
+ assert_true(selectlist.open, 'The selectlist should get opened when the button is clicked.');
+
+ await test_driver.send_keys(selectlist, ESC);
+ assert_false(selectlist.open, 'Pressing escape should close the selectlist.');
+
+ await test_driver.click(b2);
+ assert_true(selectlist.open, 'The selectlist should get opened when a second type=selectlist button is clicked.');
+ await test_driver.send_keys(selectlist, ESC);
+ assert_false(selectlist.open, 'Pressing escape should close the selectlist.');
+
+ await test_driver.click(b3);
+ assert_false(selectlist.open, 'Clicking a button witout type=selectlist should not open the listbox.');
+
+ b1.removeAttribute('type');
+ await test_driver.click(b1);
+ assert_false(selectlist.open, 'If the button is not type=selectlist, it should not open the selectlist.');
+}, '<button type=selectlist> should open the parent selectlist when clicked.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html
new file mode 100644
index 0000000000..faa96e1f27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<style>
+.container {
+ display: inline-flex;
+ font-family: sans-serif;
+ font-size: 0.875em;
+}
+</style>
+<div class=container>
+ <div>first child</div>
+ <div>second child</div>
+</div>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html
new file mode 100644
index 0000000000..d8688d5957
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-default-button-slot-ref.html">
+<meta rel=assert title="Child nodes of selectlist should be slotted into the button slot by default">
+
+<selectlist>
+ <div>first child</div>
+ <div>second child</div>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html
new file mode 100644
index 0000000000..f557804d9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script>
+focused_element_id = null;
+
+function OnFocus(event) {
+ focused_element_id = event.target.id;
+}
+</script>
+
+<selectlist id="selectlist" onfocus="OnFocus(event)" disabled>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+promise_test(async () => {
+ const selectlist = document.getElementById("selectlist");
+ selectlist.focus();
+ assert_equals(focused_element_id, null);
+}, "Check that disabled <selectlist> cannot be focused");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html
new file mode 100644
index 0000000000..a88a3b1f7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html
@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<title>HTMLSelectListElement Test: events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id="selectList0">
+ <div slot="button" behavior="button">
+ <span behavior="selected-value"></span>
+ <button id="selectList0-button">selectList0-button</button>
+ </div>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectList1">
+ <option>one</option>
+ <option>
+ two
+ <button id="selectList1-button">selectList1-button</button>
+ </option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectList2">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectList3">
+ <option>same</option>
+ <option>same</option>
+</selectlist>
+
+<selectlist id="selectList4">
+ <option>one</option>
+ <option id="selectList4-option2">two</option>
+</selectlist>
+
+<selectlist id="selectList5WithTabIndex" tabindex="1">
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<input id="input6"/>
+<selectlist id="selectList7">
+ <button slot="button" behavior="button" id="selectList7-button">
+ selectList7-button
+ </button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<script>
+
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList0");
+ const selectListButton = document.getElementById("selectList0-button");
+ assert_false(selectList.open);
+ const selectListButtonPromise = new Promise(async resolve => {
+ selectListButton.addEventListener("click", (e) => {
+ assert_false(selectList.open, "Listbox shouldn't have opened yet");
+ // PreventDefaulting the event here should prevent UA controller code
+ // on the button part from opening the listbox.
+ e.preventDefault();
+ resolve();
+ });
+ });
+
+ const selectListPromise = new Promise(async resolve => {
+ selectList.addEventListener("click", (e) => {
+ assert_true(e.defaultPrevented, "Event should have been defaultPrevented by selectListButton click handler");
+ assert_false(selectList.open, "Listbox shouldn't have opened, because click event was defaultPrevented.");
+ resolve();
+ });
+ });
+
+ await clickOn(selectListButton);
+ return Promise.all([selectListButtonPromise, selectListPromise]);
+ }, "Button controller code should not run if the click event is preventDefaulted.");
+
+ // See https://w3c.github.io/webdriver/#keyboard-actions
+ const KEY_CODE_MAP = {
+ 'Tab': '\uE004',
+ 'Enter': '\uE007',
+ 'Space': '\uE00D',
+ 'ArrowUp': '\uE013',
+ 'ArrowDown': '\uE015',
+ };
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList1");
+ const selectListButton = document.getElementById("selectList1-button");
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ const selectListButtonPromise = new Promise(async resolve => {
+ selectListButton.addEventListener("click", (e) => {
+ assert_true(selectList.open, "Listbox shouldn't have closed yet");
+ // PreventDefaulting the event here should prevent UA controller code
+ // on the listbox part from selecting the option and closing the listbox.
+ e.preventDefault();
+ resolve();
+ });
+ });
+
+ const selectListPromise = new Promise(async resolve => {
+ selectList.addEventListener("click", (e) => {
+ assert_true(e.defaultPrevented, "Event should have been defaultPrevented by selectListButton click handler");
+ assert_true(selectList.open, "Listbox shouldn't have closed, because keydown event was defaultPrevented.");
+ assert_equals(selectList.value, "one", "<selectlist> shouldn't have changed value, because keydown event was defaultPrevented.");
+ resolve();
+ });
+ });
+
+ await clickOn(selectListButton);
+ return Promise.all([selectListButtonPromise, selectListPromise]);
+ }, "Listbox controller code should not run if the click event is preventDefaulted.");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList2");
+ const events = [];
+
+ selectList.addEventListener("input", (e) => {
+ assert_true(e.composed, "input event should be composed.");
+ events.push('input');
+ });
+ selectList.addEventListener("change", (e) => {
+ assert_false(e.composed, "change event should not be composed.");
+ events.push('change');
+ });
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open);
+ assert_equals(selectList.value, "one");
+ assert_array_equals(events, [], "input and change shouldn't fire if value wansn't changed.");
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(selectList.value, "one", "value shouldn't change when user switches options with arrow key.");
+ assert_array_equals(events, ['input'], "input event should fire when user switches options with arrow key.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_equals(selectList.value, "two");
+ assert_array_equals(events, ['input', 'input', 'change'], "input and change should fire after pressing enter.");
+ }, "<selectlist> should fire input and change events when new option is selected.");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList3");
+ const events = [];
+
+ selectList.addEventListener("input", (e) => {
+ assert_true(e.composed, "input event should be composed.");
+ events.push('input');
+ });
+ selectList.addEventListener("change", (e) => {
+ assert_false(e.composed, "change event should not be composed.");
+ events.push('change');
+ });
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_array_equals(events, ['input'], "input event should have fired after ArrowDown.");
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_array_equals(events, ['input', 'input', 'change'], "input and change should fire after pressing Enter.");
+ }, "<selectlist> should fire input and change events even when new selected option has the same value as the old.");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList4");
+ const selectListOption2 = document.getElementById("selectList4-option2");
+ let input_event_count = 0;
+ let change_event_count = 0;
+
+ selectList.addEventListener("input", (e) => {
+ assert_true(e.composed, "input event should be composed");
+ assert_equals(input_event_count, 0, "input event should not fire twice");
+ assert_equals(change_event_count, 0, "input event should not fire before change");
+ input_event_count++;
+ });
+
+ selectList.addEventListener("change", (e) => {
+ assert_false(e.composed, "change event should not be composed");
+ assert_equals(input_event_count, 1, "change event should fire after input");
+ assert_equals(change_event_count, 0, "change event should not fire twice");
+ change_event_count++;
+ });
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await clickOn(selectListOption2);
+ assert_equals(input_event_count, 1, "input event shouldn't fire when selected option didn't change");
+ assert_equals(change_event_count, 1, "change event shouldn't fire when selected option didn't change");
+ }, "<selectlist> should fire input and change events when option in listbox is clicked");
+
+ promise_test(async() => {
+ const selectList = document.getElementById("selectList2");
+ await test_driver.send_keys(selectList, " ");
+ assert_true(selectList.open, "<Space> should open selectlist");
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "<Enter> should close selectlist");
+ }, "Check that <Space> opens <selectlist>.");
+
+ promise_test(async() => {
+ const selectList = document.getElementById("selectList5WithTabIndex");
+ await test_driver.send_keys(selectList, " ");
+ assert_true(selectList.open, "<Space> should open selectlist");
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "<Enter> should close selectlist");
+ }, "Check that <Space> opens <selectlist> when <selectlist> specifies tabindex");
+
+ promise_test(async() => {
+ const input6 = document.getElementById("input6");
+ const selectList = document.getElementById("selectList7");
+ const selectListButton = document.getElementById("selectList7-button")
+
+ var keydown_count = 0;
+ selectListButton.addEventListener("keydown", (e) => {
+ keydown_count++;
+ });
+
+ // Focus selectlist via Tab traversal because focus() does not work when selectlist
+ // has custom slot.
+ // TODO(http://crbug.com/1440573) Fix this.
+ await test_driver.send_keys(input6, KEY_CODE_MAP.Tab);
+
+ await test_driver.send_keys(selectList, "a");
+ assert_equals(keydown_count, 1, "button in shadowroot should have observed keydown");
+}, "Test that <selectlist> button slot receives key events.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html
new file mode 100644
index 0000000000..46d932c4b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('option');
+ document.body.appendChild(selectlist);
+ const button = selectlist.querySelector('.fake-selectlist-internal-selectlist-button');
+ button.style.width = "400px";
+ button.style.height = "50px";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html
new file mode 100644
index 0000000000..d9e52a9fff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- Tests that selectlist respects explicit size -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-explicit-size-ref.tentative.html">
+
+<style>
+selectlist {
+ width:400px;
+ height:50px;
+}
+</style>
+<selectlist>
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html
new file mode 100644
index 0000000000..46d6dbdb9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('option');
+ document.body.appendChild(selectlist);
+ selectlist.style.fontSize = "48px";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html
new file mode 100644
index 0000000000..9c30e71bda
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<!-- Tests that selectlist respects explicit size -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-font-size-ref.tentative.html">
+
+<style>
+selectlist {
+ font-size:48px;
+}
+</style>
+<selectlist>
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html
new file mode 100644
index 0000000000..c1872b9303
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html
@@ -0,0 +1,231 @@
+<!-- This test is tentative until the <selectlist> gets adopted by standards. When that happens,
+ this test can be folded back into these tests:
+ html/semantics/forms/form-control-infrastructure/form_attribute.html
+ html/semantics/forms/form-control-infrastructure/form.html -->
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div>
+ <form id="f1"></form>
+ <form id="f2">
+ <input id="i1" />
+ <input id="i2" form="f1" />
+ <input id="i3" />
+ </form>
+ <script>
+ test(function() {
+ var i1 = document.getElementById("i1");
+ var i2 = document.getElementById("i2");
+ var i3 = document.getElementById("i3");
+ var f1 = document.getElementById("f1");
+ var f2 = document.getElementById("f2");
+
+ assert_equals(i1.form, f2,
+ "i1 must be associated with f2 by the parser");
+ assert_equals(i2.form, f1,
+ "i2 is not associated with f2 by the parser " +
+ "since it has the form attribute set to f1");
+
+ f1.appendChild(i1);
+ i3.setAttribute("form", "f1");
+
+ assert_equals(i1.form, f1,
+ "i1's form owner must be reset when parent changes");
+ assert_equals(i3.form, f1,
+ "i3's form owner must be reset when the form" +
+ "attribute is set");
+
+ assert_equals(i2.form, f1);
+ }, "Tests for parser inserted controls");
+ </script>
+ </div>
+
+ <div id="placeholder">
+ </div>
+
+ <script>
+ var reassociateableElements = [
+ "selectlist",
+ ];
+
+ var form1 = null;
+ var form2 = null;
+ var placeholder = document.getElementById("placeholder");
+
+ reassociateableElements.forEach(function(localName) {
+ function testControl(test_, desc) {
+ test(function() {
+ var control = document.createElement(localName);
+
+ while(placeholder.firstChild)
+ placeholder.removeChild(placeholder.firstChild);
+
+ form1 = document.createElement("form");
+ form2 = document.createElement("form");
+ form1.id = "form1";
+ form2.id = "form2";
+ placeholder.appendChild(form1);
+ placeholder.appendChild(form2);
+
+ test_.call(control);
+ }, "[" + localName.toUpperCase() + "] " + desc);
+ }
+
+ testControl(function() {
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+ }, "Basic form association - control with no form attribute is associated with ancestor");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.id = "form-one";
+ assert_equals(this.form, null);
+ }, "Form owner is reset to null when control's form attribute is set to an ID " +
+ "that does not exist in the document");
+
+ testControl(function() {
+ this.setAttribute("form", "");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control whose form attribute is an empty string has no form owner");
+
+ testControl(function() {
+ form1.id = "";
+ this.setAttribute("form", "");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control whose form attribute is an empty string has no form owner " +
+ "even when form with empty attribute is present");
+
+ testControl(function() {
+ form1.id = "FORM1";
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control's form attribute must be a case sensitive match for the form's id");
+
+ testControl(function() {
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ this.setAttribute("form", "form2");
+ assert_equals(this.form, form2);
+ }, "Setting the form attribute of a control to the id of a non-ancestor form works");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ this.removeAttribute("form");
+ assert_equals(this.form, form2);
+ }, "Removing form id from a control resets the form owner to ancestor");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ placeholder.removeChild(form1);
+
+ assert_equals(this.form, null);
+ }, "Removing the form owner of a control with form attribute resets " +
+ "the form owner to null");
+
+ testControl(function() {
+ var form3 = document.createElement("form");
+ form3.id = "form3";
+ placeholder.appendChild(form3);
+ form3.appendChild(this);
+ assert_equals(this.form, form3);
+
+ this.setAttribute("form", "form2");
+ assert_equals(this.form, form2);
+
+ this.setAttribute("form", "form1");
+ assert_equals(this.form, form1);
+ }, "Changing form attibute of control resets form owner to correct form");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ var form3 = document.createElement("form");
+ form3.id = "form3";
+
+ placeholder.appendChild(form3);
+ placeholder.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+ }, "Moving a control with form attribute within the document " +
+ "does not change the form owner");
+
+ testControl(function() {
+ form1.id = "form-one";
+ this.setAttribute("form", "form1");
+ form2.appendChild(this);
+ assert_equals(this.form, null);
+
+ form1.id = "form1";
+ assert_equals(this.form, form1);
+ }, "When the id of a non-ancestor form changes from not being a match for the " +
+ "form attribute to being a match, the control's form owner is reset");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form2.id = "form1";
+ form1.parentNode.insertBefore(form2, form1);
+ assert_equals(this.form, form2);
+
+ form2.parentNode.removeChild(form2);
+ assert_equals(this.form, form1);
+
+ form1.parentNode.appendChild(form2);
+ assert_equals(this.form, form1);
+ }, "When form element with same ID as the control's form attribute is inserted " +
+ "earlier in tree order, the form owner is changed to the inserted form");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ var span = document.createElement("span");
+ span.id = "form1";
+ form1.parentNode.insertBefore(span, form1);
+ assert_equals(this.form, null);
+
+ form1.parentNode.appendChild(span);
+ assert_equals(this.form, form1);
+ }, "When non-form element with same ID as the control's form attribute is " +
+ "inserted earlier in tree order, the control does not have a form owner");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.parentNode.removeChild(form1);
+ assert_equals(this.form, form1);
+ }, "A control that is not in the document but has the form attribute set " +
+ "is associated with the nearest ancestor form if one exists");
+
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html
new file mode 100644
index 0000000000..07e5be6ef4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: form.elements</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<form id="form0">
+<selectlist id="selectlist0">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+<form>
+
+<script>
+promise_test(async t => {
+ // TODO: Move test to /the-form-element/form-elements-filter.html once
+ // <selectlist> becomes part of the HTML spec.
+ const formElements = document.querySelector("#form0").elements;
+ assert_equals(formElements.length, 1);
+ assert_equals(formElements[0].id, "selectlist0");
+}, "Check that <selectlist> is exposed in form.elements");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html
new file mode 100644
index 0000000000..f98494a950
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: form state restore</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<input id="emptyOnFirstVisit">
+<form action="support/back.html" id="form0">
+<selectlist id="selectlist0">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+</form>
+
+<script>
+async_test(t => {
+ window.onload = () => t.step_timeout(() => {
+ let state = document.getElementById('emptyOnFirstVisit');
+ let selectList = document.getElementById("selectlist0");
+
+ if (!state.value) {
+ // First visit.
+ t.step_timeout(() => {
+ state.value = 'visited';
+ assert_equals(selectList.value, "one");
+ selectList.value = "two";
+ // The form is submitted in a timeout to make sure that a new back/forward list item is created.
+ document.getElementById('form0').submit();
+ }, 0);
+ } else {
+ // Went back to this page again, and the form state should be restored.
+ assert_equals(selectList.value, "two");
+ t.done();
+ }
+ }, 1);
+}, "Test restoring state after form submission");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html
new file mode 100644
index 0000000000..4b5e497028
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: form submission</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form0">
+ <selectlist name="s0" id="selectlist0">
+ <option selected>one</option>
+ <option>two</option>
+ <option>three</option>
+ </selectlist>
+</form>
+
+<form id="form1">
+ <input type="text" name="i1" value="test">
+ <selectlist id="selectlist1">
+ <option selected>one</option>
+ <option>two</option>
+ <option>three</option>
+ </selectlist>
+</form>
+
+<script>
+
+test(() => {
+ const form0 = document.getElementById("form0");
+ const selectList0 = document.getElementById("selectlist0");
+ assert_equals(selectList0.value, "one");
+
+ const formData = new FormData(form0);
+ let entries = 0;
+ for (let entry of formData.entries()) {
+ assert_equals(entry[0], "s0");
+ assert_equals(entry[1], "one");
+ entries++;
+ }
+ assert_equals(entries, 1);
+}, "Test that HTMLSelectList.value is used for form submission");
+
+test(() => {
+ const form1 = document.getElementById("form1");
+ const selectList1 = document.getElementById("selectlist1");
+ assert_equals(selectList1.value, "one");
+
+ const formData = new FormData(form1);
+ let entries = 0;
+ for (let entry of formData.entries()) {
+ assert_equals(entry[0], "i1");
+ assert_equals(entry[1], "test");
+ entries++;
+ }
+ assert_equals(entries, 1);
+}, "Test that HTMLSelectList.value is not used for form submission without name attribute");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html
new file mode 100644
index 0000000000..a70ab7e6a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1422275">
+<link rel=help href="https://github.com/openui/open-ui/issues/433#issuecomment-1452461404">
+<link rel=help href="https://github.com/openui/open-ui/issues/386#issuecomment-1452469497">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<form></form>
+
+<div id=notform>
+ <selectlist id=defaultbutton>
+ <option class=one>one</option>
+ <option class=two>two</option>
+ <option class=three>three</option>
+ </selectlist>
+
+ <selectlist id=custombutton>
+ <button type=selectlist>custom button</button>
+ <option class=one>one</option>
+ <option class=two>two</option>
+ <option class=three>three</option>
+ </selectlist>
+</div>
+
+<script>
+const Enter = '\uE007';
+const Escape = '\uE00C';
+const ArrowLeft = '\uE012';
+const ArrowUp = '\uE013';
+const ArrowRight = '\uE014';
+const ArrowDown = '\uE015';
+const Space = ' ';
+const form = document.querySelector('form');
+const notform = document.getElementById('notform');
+
+for (const id of ['defaultbutton', 'custombutton']) {
+ const selectlist = document.getElementById(id);
+
+ async function closeListbox() {
+ await test_driver.click(selectlist);
+ }
+
+ function addCloseCleanup(t) {
+ t.add_cleanup(async () => {
+ if (selectlist.matches(':open')) {
+ await closeListbox();
+ }
+ if (selectlist.matches(':open')) {
+ throw new Error('selectlist failed to close!');
+ }
+ selectlist.value = 'one';
+ });
+ }
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+ // TODO(http://crbug.com/1350299): When focus for custom buttons is fixed,
+ // then we shouldn't need to explicitly focus the custom button like this
+ // anymore.
+ const customButton = selectlist.querySelector('button');
+ if (customButton) {
+ customButton.focus();
+ } else {
+ selectlist.focus();
+ }
+ assert_false(selectlist.matches(':open'),
+ 'The selectlist should initially be closed.');
+ await test_driver.send_keys(selectlist, Space);
+ assert_true(selectlist.matches(':open'),
+ 'The selectlist should be open after pressing space.');
+ }, `${id}: When the listbox is closed, spacebar should open the listbox.`);
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+ selectlist.value = 'two';
+ selectlist.focus();
+ assert_false(selectlist.matches(':open'),
+ 'The selectlist should initially be closed.');
+
+ await test_driver.send_keys(selectlist, ArrowLeft);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow left should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow left should not change the selected value.');
+ await closeListbox();
+
+ await test_driver.send_keys(selectlist, ArrowUp);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow up should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow up should not change the selected value.');
+ await closeListbox();
+
+ await test_driver.send_keys(selectlist, ArrowRight);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow right should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow right should not change the selected value.');
+ await closeListbox();
+
+ await test_driver.send_keys(selectlist, ArrowDown);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow down should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow down should not change the selected value.');
+ }, `${id}: When the listbox is closed, all arrow keys should open the listbox.`);
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+
+ // TODO(http://crbug.com/1350299): When focus for custom buttons is fixed,
+ // then we shouldn't need to explicitly use the custom button like this
+ // anymore.
+ const customButton = selectlist.querySelector('button');
+ if (customButton) {
+ await test_driver.send_keys(customButton, Enter);
+ } else {
+ await test_driver.send_keys(selectlist, Enter);
+ }
+ assert_false(selectlist.matches(':open'),
+ 'Enter should not open the listbox when outside a form.');
+
+ form.appendChild(selectlist);
+ let formWasSubmitted = false;
+ form.addEventListener('submit', event => {
+ event.preventDefault();
+ formWasSubmitted = true;
+ }, {once: true});
+ if (customButton) {
+ await test_driver.send_keys(customButton, Enter);
+ } else {
+ await test_driver.send_keys(selectlist, Enter);
+ }
+ assert_true(formWasSubmitted,
+ 'Enter should submit the form when the listbox is closed.');
+ assert_false(selectlist.matches(':open'),
+ 'Enter should not open the listbox when it is in a form.');
+ }, `${id}: When the listbox is closed, the enter key should submit the form or do nothing.`);
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+ const optionOne = selectlist.querySelector('.one');
+ const optionTwo = selectlist.querySelector('.two');
+ const optionThree = selectlist.querySelector('.three');
+
+ selectlist.value = 'two';
+ await test_driver.click(selectlist);
+ assert_true(selectlist.matches(':open'),
+ 'The selectlist should open when clicked.');
+ assert_equals(document.activeElement, optionTwo,
+ 'The selected option should receive initial focus.');
+
+ await test_driver.send_keys(document.activeElement, ArrowDown);
+ assert_equals(document.activeElement, optionThree,
+ 'The next option should receive focus when the down arrow key is pressed.');
+ assert_equals(selectlist.value, 'two',
+ 'The selectlists value should not change when focusing another option.');
+
+ await test_driver.send_keys(document.activeElement, ArrowUp);
+ assert_equals(document.activeElement, optionTwo,
+ 'The previous option should receive focus when the up arrow key is pressed.');
+ assert_equals(selectlist.value, 'two',
+ 'The selectlists value should not change when focusing another option.');
+
+ await test_driver.send_keys(document.activeElement, ArrowUp);
+ assert_equals(document.activeElement, optionOne,
+ 'The first option should be selected.');
+ assert_equals(selectlist.value, 'two',
+ 'The selectlists value should not change when focusing another option.');
+
+ await test_driver.send_keys(document.activeElement, Enter);
+ assert_false(selectlist.matches(':open'),
+ 'The listbox should be closed after pressing enter.');
+ assert_equals(selectlist.value, 'one',
+ 'The selectlists value should change after pressing enter on a different option.');
+ }, `${id}: When the listbox is open, the enter key should commit the selected option.`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html
new file mode 100644
index 0000000000..5d1c65e941
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html
@@ -0,0 +1,142 @@
+<!doctype html>
+<title>HTMLSelectListElement Test: keyboard accessibility</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+ <selectlist id="selectList0">
+ <button id="selectList0-button0" type=selectlist>button</button>
+ <option class=one>one</option>
+ <option class=two>two</option>
+ <option class=three>three</option>
+ </selectlist>
+
+ <selectlist id="selectList1">
+ <option id="selectList1-child0">one</option>
+ </selectlist>
+
+ <selectlist id="selectList2" disabled>
+ <button id="selectList2-button0" type=selectlist>button</button>
+ <option disabled>one</option>
+ <option>two</option>
+ <option>three</option>
+ </selectlist>
+
+ <selectlist id="selectList3">
+ <button id="selectList3-button0" type=selectlist>button</button>
+ <option class=one>one</option>
+ <option disabled>two</option>
+ <option class=three>three</option>
+ </selectlist>
+<script>
+// See https://w3c.github.io/webdriver/#keyboard-actions
+const KEY_CODE_MAP = {
+ 'Enter': '\uE007',
+ 'Space': '\uE00D',
+ 'ArrowUp': '\uE013',
+ 'ArrowDown': '\uE015'
+};
+
+function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+promise_test(async t => {
+ const selectList = document.querySelector("#selectList0");
+ const button = document.querySelector("#selectList0-button0");
+ assert_false(selectList.open, "selectlist should not be initially open");
+
+ await test_driver.send_keys(button, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "Enter key shouldn't open selectlist");
+ await test_driver.send_keys(button, KEY_CODE_MAP.Space);
+ assert_true(selectList.open, "Space key should open selectlist");
+ assert_equals(selectList.value, "one");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList.querySelector('.two'),
+ "Down arrow should focus the next option.");
+ assert_equals(selectList.value, "one", "Down arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList.querySelector('.three'),
+ "Down arrow should focus the next option.");
+ assert_equals(selectList.value, "one", "Down arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList.querySelector('.three'),
+ "Down arrow should do nothing if already at the last option.");
+ assert_equals(selectList.value, "one", "Down arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList.querySelector('.two'),
+ "Up arrow should focus the previous option.");
+ assert_equals(selectList.value, "one", "Up arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList.querySelector('.one'),
+ "Up arrow should focus the previous option.");
+ assert_equals(selectList.value, "one", "Up arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList.querySelector('.one'),
+ "Up arrow should do nothing if already at the first option.");
+ assert_equals(selectList.value, "one", "Up arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "Enter key should close selectlist");
+
+ await test_driver.send_keys(button, KEY_CODE_MAP.Space);
+ assert_true(selectList.open, "Space key should open selectlist");
+
+ // This behavior is suspicious (since Space key can open the selectlist),
+ // but it maches <select>. See https://github.com/openui/open-ui/issues/386
+ await test_driver.send_keys(selectList, " ");
+ assert_true(selectList.open, "Space key should *not* close selectlist");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "Enter key should close selectlist");
+}, "Validate Enter, Up/Down Arrow, and Space keyboard accessibility support for <selectlist>");
+
+promise_test(async t => {
+ const selectListOption = document.getElementById("selectList1-child0");
+ const event = document.createEvent("Event");
+ event.initEvent("keydown");
+ selectListOption.dispatchEvent(event);
+}, "Firing a synthetic event at a selectlist's option doesn't crash");
+
+promise_test(async t => {
+ const selectList2 = document.querySelector("#selectList2");
+ const selectList2Button = document.querySelector("#selectList2-button0");
+ assert_false(selectList2.open, "selectlist should not be initially open");
+
+ await test_driver.send_keys(selectList2Button, KEY_CODE_MAP.Enter);
+ assert_false(selectList2.open, "Enter key should not open a disabled selectlist");
+ await clickOn(selectList2);
+ assert_false(selectList2.open, "Click should not open a disabled selectlist");
+ assert_equals(selectList2.value, "one");
+
+ const selectList3 = document.querySelector("#selectList3");
+ const selectList3Button = document.querySelector("#selectList3-button0");
+ assert_false(selectList3.open, "selectlist should not be initially open");
+
+ await test_driver.send_keys(selectList3Button, KEY_CODE_MAP.Enter);
+ assert_false(selectList3.open, "Enter key shouldn't open selectlist");
+
+ await test_driver.send_keys(selectList3Button, KEY_CODE_MAP.Space);
+ assert_true(selectList3.open, "Space key should open selectlist");
+ assert_equals(selectList3.value, "one");
+
+ await test_driver.send_keys(selectList3, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList3.querySelector('.three'),
+ "Down arrow should go to next non-disabled option");
+
+ await test_driver.send_keys(selectList3, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList3.querySelector('.one'),
+ "Up arrow should go to the previous non-disabled option");
+}, "Validate Enter, Up/Down Arrow keyboard accessibility support for disabled <selectlist>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html
new file mode 100644
index 0000000000..819da49fdd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: labels</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<label id="label1" for="selectlist1">Label 1</label>
+<selectlist id="selectlist1">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+ <option>four</option>
+</selectlist>
+<label id="label2" for="selectlist1">Label 2</label>
+<label id="label3" for="invalid-selectlist1">Label 3</label>
+
+<script>
+
+test(() => {
+ let selectList = document.getElementById("selectlist1");
+
+ assert_true(selectList.labels instanceof NodeList);
+ assert_equals(selectList.labels.length, 2);
+ assert_equals(selectList.labels[0], document.getElementById("label1"));
+ assert_equals(selectList.labels[1], document.getElementById("label2"));
+}, "Validate selectlist.labels");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html
new file mode 100644
index 0000000000..5533a97f60
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+const selectlist = createFakeSelectlist('button', /*includeListbox=*/true);
+document.body.appendChild(selectlist);
+
+selectlist.querySelector('button').remove();
+selectlist.insertAdjacentHTML('afterbegin', `<button>button</button>`);
+
+const listbox = selectlist.querySelector('.fake-selectlist-listbox');
+listbox.innerHTML = `
+ <option id=optone tabindex=0>one</option>
+ <option>two</option>
+`;
+document.getElementById('optone').focus();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html
new file mode 100644
index 0000000000..14223633b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-listbox-element-ref.html">
+
+<selectlist>
+ <button type=selectlist>button</button>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<script>
+document.querySelector('button').click();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html
new file mode 100644
index 0000000000..1a9e81d817
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html class="test-wait">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1400522">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+
+<selectlist id="selectList" style="position: absolute;">
+ <div slot="button">
+ <button id="b1" behavior="button"></button>
+ </div>
+ <selectlist>
+ <div slot="button">
+ <button id="b2" behavior="button">x</button>
+ </div>
+ <div id="listbox" popover slot="listbox" behavior="listbox">
+ <option>y</option>
+ </div>
+ </selectlist>
+</selectlist>
+
+<script type="module">
+ const raf = () => new Promise(resolve => requestAnimationFrame(resolve));
+
+ document.querySelector('#b1').click();
+ document.querySelector('#b2').click();
+
+ document.querySelector('#selectList').style.top = '-25px';
+
+ await raf();
+ await raf();
+
+ document.querySelector('#selectList').style.top = '0px';
+
+ document.documentElement.classList.remove('test-wait');
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html
new file mode 100644
index 0000000000..36bae1a82b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: many options</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+ #selectList0 {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ }
+
+ #selectList0-popover {
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+ box-sizing: border-box;
+ overflow: auto;
+ padding: 4px;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox" id="selectList0-popover">
+ <option>bottom left</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+<br>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Popover = document.getElementById("selectList0-popover");
+
+ await clickOn(selectList0);
+ assert_equals(Math.round(selectList0.getBoundingClientRect().bottom), Math.round(selectList0Popover.getBoundingClientRect().top));
+ assert_equals(Math.round(selectList0.getBoundingClientRect().left), Math.round(selectList0Popover.getBoundingClientRect().left));
+ assert_equals(window.innerHeight, Math.round(selectList0Popover.getBoundingClientRect().bottom));
+ }, "The popover should be bottom left positioned");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html
new file mode 100644
index 0000000000..b1df8a6581
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+div {
+ width:300px;
+ display:flex;
+ justify-content:flex-end;
+}
+selectlist {
+ width:100px;
+}
+selectlist::part(button) {
+ border-style:none;
+ background-color:rgba(0,0,0,0)
+}
+</style>
+<div>
+<selectlist>
+ <option></option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html
new file mode 100644
index 0000000000..c2540acb36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- Tests that <selectlist> marker is end-aligned -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-marker-end-aligned-ref.tentative.html">
+
+<style>
+div {
+ width:300px;
+ display:flex;
+ justify-content:flex-end;
+}
+selectlist {
+ width:200px;
+}
+selectlist::part(button) {
+ border-style:none;
+ background-color:rgba(0,0,0,0)
+}
+</style>
+<div>
+<selectlist>
+ <option></option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html
new file mode 100644
index 0000000000..1a1302b96d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+ selectlist.querySelector('.fake-selectlist-internal-selectlist-button-icon')
+ .style.backgroundColor = 'red';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html
new file mode 100644
index 0000000000..dedabc9bab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-marker-part-ref.html">
+
+<style>
+selectlist::part(marker) {
+ background-color: red;
+}
+</style>
+<selectlist>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html
new file mode 100644
index 0000000000..e910df4c5a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+
+ const oldMarker = selectlist.querySelector('.fake-selectlist-internal-selectlist-button-icon');
+ const newMarker = document.createElement('div');
+ newMarker.textContent = 'marker';
+
+ replaceChildElement(newMarker, oldMarker);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html
new file mode 100644
index 0000000000..de43a02c33
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-marker-slot-ref.html">
+
+<selectlist>
+ <div slot=marker>marker</div>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html
new file mode 100644
index 0000000000..dda53db5bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<style>
+selectlist {
+ width:100px;
+}
+</style>
+<div>
+<selectlist>
+ <option>&nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html
new file mode 100644
index 0000000000..345c205983
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- Tests that the marker is visible when the <selectlist> contains more text than it can show -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-marker-visible-overflow-ref.tentative.html">
+
+<style>
+selectlist {
+ width:100px;
+}
+</style>
+<div>
+<selectlist>
+ <option>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html
new file mode 100644
index 0000000000..c29413f180
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: nested selects</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox">
+ <selectlist id="nested0">
+ <option id="child1">one</option>
+ <option id="child2">two</option>
+ </selectlist>
+ <option id="child3">three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList1">
+ <div popover slot="listbox" behavior="listbox">
+ <select>
+ <option>one</option>
+ <option>two</option>
+ </select>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList2">
+ <div slot="button">
+ <selectlist id="nested2">
+ <button type=selectlist id="selectList2-button0">button0</button>
+ <option id="nested2-option1">one</option>
+ </selectlist>
+ <button type=selectlist id="selectList2-button1">button1</button>
+ </div>
+ <option>two</option>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const nested0 = document.getElementById("nested0");
+ const child2 = document.getElementById("child2");
+ assert_equals(selectList0.value, "three", "Options nested in another <selectlist> should not get controller code from outer <selectlist>");
+ await clickOn(selectList0);
+ assert_true(selectList0.open);
+ assert_false(nested0.open);
+
+ await clickOn(nested0);
+ assert_true(nested0.open);
+
+ await clickOn(child2);
+ assert_false(nested0.open);
+ assert_equals(nested0.value, "two");
+ assert_true(selectList0.open, "click on option in inner <selectlist> should not close outer <selectlist>");
+ assert_equals(selectList0.value, "three", "click on option in inner <selectlist> should not change value of outer <selectlist>");
+ }, "A <selectlist> shouldn't apply controller code to parts nested in a <selectlist> child");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ assert_equals(selectList0.value, "three");
+ }, "A <selectlist> shouldn't apply controller code to parts nested in a <select> child");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const nested2 = document.getElementById("nested2");
+ const button0 = document.getElementById("selectList2-button0");
+ const button1 = document.getElementById("selectList2-button1");
+ const nested2Option1 = document.getElementById("nested2-option1");
+ assert_false(selectList2.open);
+ assert_false(nested2.open);
+
+ await clickOn(button0);
+ assert_false(selectList2.open, "Clicking the button of a nested <selectlist> should not open the outer <selectlist>");
+ assert_true(nested2.open, "Clicking the button of a nested <selectlist> should open the outer <selectlist>");
+
+ await clickOn(nested2Option1);
+ assert_false(nested2.open);
+
+ await clickOn(button1);
+ assert_true(selectList2.open);
+ assert_false(nested2.open);
+ }, "A nested button part in a nested <selectlist> shouldn't get controller code even if it comes first in document order");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html
new file mode 100644
index 0000000000..126bbc5dd7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<link rel="stylesheet" href="/fonts/ahem.css">
+
+<selectlist id="selectList0">
+ <option>option with image displayed</option>
+</selectlist>
+<div id=fakelistbox>
+ option with image displayed
+ <img src="/images/green-256x256.png">
+</div>
+
+<style>
+ html,selectlist {
+ font-family: Ahem;
+ }
+ #fakelistbox {
+ /* Per spec: */
+ display: block;
+ position: fixed;
+ top: 30px;
+ left: 0;
+ font-size: 0.765625em /* 0.875 * 0.875 */;
+ /* Per settings in test file: */
+ width: fit-content;
+ height: fit-content;
+ background: light-dark(white, black);
+ color: light-dark(black, white);
+ border: 1px solid rgba(0, 0, 0, 1);
+ border-radius: 0px;
+ box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+ box-sizing: border-box;
+ overflow: auto;
+ padding: 4px;
+ }
+
+ selectlist {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ height: 30px;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html
new file mode 100644
index 0000000000..2d51002fb2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>HTMLSelectListElement Test: option arbitrary content displayed</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<link rel=match href="selectlist-option-arbitrary-content-displayed-ref.tentative.html">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+ html,selectlist {
+ font-family: Ahem;
+ }
+ selectlist {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ height: 30px;
+ }
+
+ [popover] {
+ width: fit-content;
+ height: fit-content;
+ background: white;
+ color: black;
+ border: 1px solid rgba(0, 0, 0, 1);
+ border-radius: 0px;
+ box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+ box-sizing: border-box;
+ overflow: auto;
+ padding: 4px;
+ }
+
+ option {
+ background-color: white !important;
+ padding: 0px;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox">
+ <option>
+ option with image displayed
+ <img src="/images/green-256x256.png">
+ </option>
+ </div>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ async function test() {
+ const selectList0 = document.getElementById("selectList0");
+
+ await clickOn(selectList0);
+ document.documentElement.classList.remove('reftest-wait');
+ }
+
+ test();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html
new file mode 100644
index 0000000000..fa44198fff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+
+<option>
+ option with image not displayed
+</option>
+
+<selectlist>
+</selectlist>
+
+<option>
+ option with image not displayed
+</option> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html
new file mode 100644
index 0000000000..e7cacdba27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: option arbitrary content not displayed</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<link rel=match href="selectlist-option-arbitrary-content-not-displayed-ref.tentative.html">
+
+<option>
+ option with image not displayed
+ <img src="/images/green-256x256.png">
+</option>
+
+<selectlist id="selectList0">
+ <option id="selectList0-option">
+ option with image not displayed
+ <img src="/images/green-256x256.png">
+ </option>
+</selectlist>
+
+<script>
+ const selectList0Option = document.getElementById("selectList0-option");
+
+ // removing an option from <selectlist> should revert back to its original display behavior
+ selectList0Option.remove();
+ document.body.append(selectList0Option);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html
new file mode 100644
index 0000000000..993ef007e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: option facusable</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id="selectlist0">
+ <option>one</option>
+ <option id="selectlist0-option2">two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+// See https://w3c.github.io/webdriver/#keyboard-actions
+const KEY_CODE_MAP = {
+ 'Enter': '\uE007',
+ 'Space': '\uE00D',
+ 'ArrowUp': '\uE013',
+ 'ArrowDown': '\uE015'
+};
+
+function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+}
+
+promise_test(async t => {
+ const selectList = document.querySelector("#selectlist0");
+ assert_false(selectList.open, "selectlist should not be initially open");
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ assert_equals(selectList.value, "one");
+
+ const option2 = document.querySelector('#selectlist0-option2');
+ option2.focus();
+ assert_equals(document.activeElement, option2);
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_equals(selectList.value, "two");
+}, "Validate <option> is focusable when is a descendant of <selectlist>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html
new file mode 100644
index 0000000000..1ab1d54722
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<selectlist>
+ <option>textcontent</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html
new file mode 100644
index 0000000000..c719ee0e07
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-option-label-rendering-ref.html">
+<link rel=assert title="option elements should not render their label attributes when used in selectlist.">
+
+<selectlist>
+ <option label=label>textcontent</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html
new file mode 100644
index 0000000000..df87060359
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+selectlist {
+ width:30px;
+ height:20px;
+}
+selectlist::part(button) {
+ background-color:blue;
+}
+</style>
+<selectlist>
+ <option>&nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html
new file mode 100644
index 0000000000..93c1b948a0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- Tests that selectlist button part text is truncated by the button. -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-overflow-x-ref.tentative.html">
+<style>
+selectlist {
+ width:30px;
+ height:20px;
+}
+selectlist::part(button) {
+ background-color:blue;
+}
+</style>
+<selectlist>
+ <option>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html
new file mode 100644
index 0000000000..104ae841f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html
@@ -0,0 +1,495 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: part structure</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox">
+ <option id="selectList0-child1">one</option>
+ <option id="selectList0-child2">two</option>
+ <div behavior="option" id="selectList0-child3">three</div>
+ </div>
+ <option id="selectList0-child4">four</option>
+</selectlist>
+
+<selectlist id="selectList1">
+ <listbox id="selectList1-popover">
+ <button type=selectlist id="selectList1-button">
+ Custom button
+ </button>
+ <option>one</option>
+ <option id="selectList1-child2">two</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList2">
+ <button type=selectlist id="selectList2-button">
+ Custom button
+ <listbox id="selectList2-popover">
+ Custom listbox
+ </listbox>
+ </button>
+ <option>three</option>
+ <div>
+ This is some text.
+ <option id="selectList2-child4">four</option>
+ More text.
+ </div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div slot="button" id="selectList3-button-slot">
+ <button type=selectlist id="selectList3-button0">button0</button>
+ </div>
+ <option>one</option>
+</selectlist>
+
+<selectlist id="selectList4">
+ <button type=selectlist id="selectList4-button0">button0</button>
+ <div slot="listbox" id="selectList4-listbox-slot">
+ <div popover behavior="listbox" id="selectList4-listbox0">
+ <option>one</option>
+ <option id="selectList4-option2">two</option>
+ </div>
+ </div>
+</selectlist>
+
+<selectlist id="selectList5">
+ <div slot="button" id="selectList5-button-slot">
+ <button type=selectlist id="selectList5-button0">button0</button>
+ <div behavior="selected-value" id="selectList5-selectedValue0"></div>
+ </div>
+ <option>one</option>
+ <option id="selectList5-option0">two</option>
+</selectlist>
+
+<!-- No associated JS test -- just don't crash when parsing! -->
+<selectlist id="selectList6">
+ <div slot="button"></div>
+ <div popover slot="listbox" behavior="listbox"></div>
+</selectlist>
+
+<!-- No associated JS test -- just don't crash when parsing! -->
+<selectlist id="selectList7">
+ <div slot="listbox"></div>
+ <button type=selectlist></button>
+</selectlist>
+
+<!-- No associated JS test -- just don't crash when parsing! -->
+<selectlist id="selectList8">
+ <div slot="listbox"></div>
+ <option>one</option>
+</selectlist>
+
+<selectlist id="selectList9">
+ <div slot="listbox" id="selectList9-listbox-slot">
+ <div popover behavior="listbox" id="selectList9-originalListbox">
+ <option>one</option>
+ <option id="selectList9-option2">two</option>
+ </div>
+ </div>
+</selectlist>
+
+<selectlist id="selectList11">
+ <div popover slot="listbox" behavior="listbox">
+ <option>one</option>
+ </div>
+ <div slot="button" behavior="listbox" id="selectList11-button">Test</div>
+</selectlist>
+
+<selectlist id="selectList12">
+ <button type=selectlist id="selectList12-button0">button0</button>
+ <listbox id="selectList12-listbox">
+ <option id="selectList12-option1">one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList13">
+ <div slot="button" id="selectList12-button-slot">
+ <div id="selectList13-removeContent-button">
+ <button type=selectlist id="selectList13-button0">button0</button>
+ <button type=selectlist id="selectList13-button1">button1</button>
+ </div>
+ <button type=selectlist id="selectList13-button2">button2</button>
+ </div>
+ <div slot="listbox" id="selectList13-listbox-slot">
+ <div id="selectList13-removeContent-listbox">
+ <div popover behavior="listbox" id="selectList13-originalListbox">
+ <option id="selectList13-option1">one</option>
+ <option id="selectList13-option2">two</option>
+ </div>
+ </div>
+ <div popover behavior="listbox" id="selectList13-newListbox">
+ <option>three</option>
+ <option id="selectList13-option4">four</option>
+ </div>
+ </div>
+</selectlist>
+
+<selectlist id="selectList14">
+ <button type=selectlist id="selectList14-button0">button0</button>
+ <option>one</option>
+ <option id="selectList14-option2">two</option>
+</selectlist>
+
+<selectlist id="selectList15">
+ <div slot="button" id="selectList15-div0"></div>
+ <option>one</option>
+</selectlist>
+
+<selectlist id="selectList16">
+ <div slot="button">
+ <div id="selectList16-div0">
+ <button type=selectlist id="selectList16-button0">button</button>
+ </div>
+ </div>
+ <option>one</option>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Child1 = document.getElementById("selectList0-child1");
+ const selectList0Child2 = document.getElementById("selectList0-child2");
+ const selectList0Child3 = document.getElementById("selectList0-child3");
+ assert_equals(selectList0.value, "one");
+ await clickOn(selectList0);
+ await clickOn(selectList0Child2);
+ assert_equals(selectList0.value, "two");
+
+ await clickOn(selectList0);
+ await clickOn(selectList0Child3);
+ assert_equals(selectList0.value, "two", "Clicking a non-HTMLOptionElement labeled as an option should do nothing");
+
+ await clickOn(selectList0Child1);
+ assert_equals(selectList0.value, "one");
+ assert_false(selectList0.open);
+ }, "HTMLOptionElements (and not other element types) should receive option controller code");
+
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Child4 = document.getElementById("selectList0-child4");
+
+ assert_equals(selectList0.value, "one");
+ await clickOn(selectList0);
+ assert_true(selectList0.open);
+ selectList0Child4.click();
+ assert_equals(selectList0.value, "one", "Clicking an option outside of the popover should not change the value");
+ }, "To receive option part controller code, an option must be a descendant of the listbox part in a flat tree traversal");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const selectList1Popover = document.getElementById("selectList1-popover");
+ const selectList1Button = document.getElementById("selectList1-button");
+ const selectList1Child2 = document.getElementById("selectList1-child2");
+ assert_false(selectList1Popover.matches(':popover-open'));
+ selectList1Button.click();
+ assert_false(selectList1Popover.matches(':popover-open'), "Clicking a button part that is a descendant of the listbox part should have no effect");
+
+ assert_equals(selectList1.value, "one");
+ await clickOn(selectList1);
+ assert_true(selectList1Popover.matches(':popover-open'));
+ await clickOn(selectList1Child2);
+ assert_equals(selectList1.value, "two", "Clicking an <option> should change the value");
+ }, "To receive button part controller code, an element labeled as a button must not be a descendant of the listbox part in a flat tree traversal");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const selectList2Popover = document.getElementById("selectList2-popover");
+ const selectList2Button = document.getElementById("selectList2-button");
+ const selectList2Child2 = document.getElementById("selectList2-child2");
+ const selectList2Child4 = document.getElementById("selectList2-child4");
+
+ assert_false(selectList2Popover.matches(':popover-open'));
+ await clickOn(selectList2Button);
+ assert_false(selectList2Popover.matches(':popover-open'), "Clicking a button part should not show an invalid listbox part");
+
+ assert_equals(selectList2.value, "three");
+ await clickOn(selectList2Child4);
+ assert_equals(selectList2.value, "four", "Clicking an <option> that is a descendant of a valid listbox part should update the value");
+ }, "To receive listbox part controller code, an element labeled as a listbox must not be a descendant of the button part in a flat tree traversal");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3ButtonSlot = document.getElementById("selectList3-button-slot");
+ const selectList3Button0 = document.getElementById("selectList3-button0");
+
+ assert_false(selectList3.open);
+
+ let button1 = document.createElement("div");
+ button1.innerText = "button1";
+ button1.setAttribute("behavior", "button");
+ selectList3ButtonSlot.appendChild(button1);
+
+ await clickOn(button1);
+ assert_false(selectList3.open, "A button part should only get controller code if it's first in document order, even if added dynamically");
+
+ await clickOn(selectList3Button0);
+ assert_true(selectList3.open, "A button part should get controller code if it's first in document order");
+ }, "Button controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
+
+ promise_test(async () => {
+ const selectList4 = document.getElementById("selectList4");
+ const selectList4Button0 = document.getElementById("selectList4-button0");
+ const selectList4ListboxSlot = document.getElementById("selectList4-listbox-slot");
+ const selectList4Option2 = document.getElementById("selectList4-option2");
+
+ assert_false(selectList4.open);
+
+ let listbox2 = document.createElement("div");
+ listbox2.innerHTML = `
+ <option>three</option>
+ <option id="selectList4-option4">four</option>
+ `;
+ listbox2.setAttribute("behavior", "listbox");
+ selectList4ListboxSlot.appendChild(listbox2);
+
+ await clickOn(selectList4Button0);
+ assert_true(selectList4.open);
+
+ const selectList4Option4 = document.getElementById("selectList4-option4");
+ await clickOn(selectList4Option4);
+ assert_equals(selectList3.value, "one", "An option in a listbox should not get controller code if its listbox isn't first in document order, even if added dynamically");
+
+ await clickOn(selectList4Button0);
+ assert_true(selectList4.open);
+
+ await clickOn(selectList4Option2);
+ assert_equals(selectList4.value, "two", "An option in a listbox should get controller code if its listbox is first in document order, even if another listbox was added dynamically");
+}, "Listbox controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
+
+promise_test(async () => {
+ const selectList5 = document.getElementById("selectList5");
+ const selectList5ButtonSlot = document.getElementById("selectList5-button-slot");
+ const selectList5Button0 = document.getElementById("selectList5-button0");
+ const selectList5SelectedValue0 = document.getElementById("selectList5-selectedValue0");
+
+ assert_false(selectList3.open);
+ assert_equals(selectList5SelectedValue0.innerText, "one");
+
+ let selectedValue1 = document.createElement("div");
+ selectList5ButtonSlot.appendChild(selectedValue1);
+
+ await clickOn(selectList5Button0);
+ assert_true(selectList5.open);
+
+ await clickOn(document.getElementById("selectList5-option0"));
+ assert_false(selectList5.open);
+ assert_equals(selectList5SelectedValue0.innerText, "two", "first selected-value part in flat tree order should get controller code");
+ assert_equals(selectedValue1.innerText, "", "Dynamically inserted selected-value part shouldn't get controller code if it's not first in flat tree order");
+ }, "selected-value controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList9");
+ const originalListbox = document.getElementById("selectList9-originalListbox");
+ const option2 = document.getElementById("selectList9-option2");
+ assert_equals(selectList.value, "one", "Initial value should be the first option");
+
+ let newListbox = document.createElement("div");
+ newListbox.setAttribute("popover", "auto");
+ newListbox.setAttribute("behavior", "listbox");
+ let newOption = document.createElement("option");
+ newOption.innerText = "three";
+ newListbox.appendChild(newOption);
+ let newOption2 = document.createElement("option");
+ newOption2.innerText = "four";
+ newListbox.appendChild(newOption2);
+ originalListbox.parentElement.insertBefore(newListbox, originalListbox);
+
+ await clickOn(selectList);
+ assert_true(selectList.open, "Menu should open when clicked");
+
+ option2.click(); // clickOn doesn't work because the old options are not displayed
+ assert_equals(selectList.value, "three", "Elements in second popover should no longer be option parts");
+ assert_true(selectList.open, "Clicking non-part options shouldn't close the popover");
+
+ await clickOn(newOption2);
+ assert_false(selectList.open);
+ assert_equals(selectList.value, "four", "New options should get controller code after listbox switch");
+ }, "Ensure that option controller code is updated when listbox changes");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList11");
+ const selectList11Button= document.getElementById("selectList11-button");
+
+ await clickOn(selectList11Button);
+ assert_false(selectList.open, "Controller code not applied due to part attribute not being button");
+ selectList11Button.remove();
+
+ await clickOn(selectList);
+ assert_true(selectList.open, "Default button part should be used");
+ }, "Ensure that controller code is applied when slot and part attributes are different");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList12");
+ const originalListbox = document.getElementById("selectList12-listbox");
+ assert_equals(selectList.value, "one", "Initial value should be the first option");
+
+ const selectListButton0 = document.getElementById("selectList12-button0");
+ const selectListOption1 = document.getElementById("selectList12-option1");
+
+ assert_false(selectList.open);
+ let button1 = document.createElement("button");
+ button1.innerText = "button1";
+ button1.setAttribute("type", "selectlist");
+ selectList.insertBefore(button1, selectListButton0);
+ button1.click();
+ assert_true(selectList.open, "Controller code should be applied to the new first button in document order");
+ await clickOn(selectListOption1);
+ assert_false(selectList.open, "listbox should close after clicking option1.");
+ selectListButton0.click();
+ assert_true(selectList.open, "listbox should open after clicking button0.");
+ selectListButton0.click();
+ assert_false(selectList.open, "listbox should close after clicking button0 again.");
+
+ let button2 = document.createElement("button");
+ button2.innerText = "button2";
+ selectList.insertBefore(button2, button1);
+ button2.click();
+ assert_false(selectList.open, "Controller code should not be applied to button2 since it doesn't have type=selectlist.");
+ button2.setAttribute("type", "selectlist");
+ button2.click();
+ assert_true(selectList.open, "Controller code should be applied to the new button part");
+ await clickOn(selectListOption1);
+ assert_false(selectList.open);
+
+ let newListbox = document.createElement("listbox");
+ let newOption = document.createElement("option");
+ newOption.innerText = "three";
+ newListbox.appendChild(newOption);
+ let newOption2 = document.createElement("option");
+ newOption2.innerText = "four";
+ newListbox.appendChild(newOption2);
+ originalListbox.parentElement.insertBefore(newListbox, originalListbox);
+ assert_equals(selectList.value, "three", "New value should be the first option");
+
+ newListbox.innerHTML = "<option>five</option><option>six</option>";
+ assert_equals(selectList.value, "five", "New value should be the first option");
+
+ selectList.innerHTML = "<option>seven</option><option id='selectList12-option2'>eight</option>";
+ assert_equals(selectList.value, "seven", "New value should be the first option");
+ const selectListOption2 = document.getElementById("selectList12-option2");
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await clickOn(selectListOption2);
+ assert_equals(selectList.value, "eight", "Controller code should be applied to new options");
+
+ selectListOption2.slot = "button";
+ assert_equals(selectList.value, "seven", "Previous selected option should become invalid");
+ }, "Ensure that controller code is synchronously applied");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList13");
+ assert_equals(selectList.value, "one");
+
+ const selectListButton0 = document.getElementById("selectList13-button0");
+ const selectListButton1 = document.getElementById("selectList13-button1");
+ selectListButton1.click();
+ assert_false(selectList.open);
+ selectListButton0.click();
+ assert_true(selectList.open, "First button should receive controller code");
+ await clickOn(document.getElementById("selectList13-option2"));
+ assert_equals(selectList.value, "two");
+ let divButtonToRemove = document.getElementById("selectList13-removeContent-button");
+ divButtonToRemove.innerHTML = "";
+ selectListButton0.click();
+ assert_false(selectList.open, "The first button is invalid");
+ const selectListButton2 = document.getElementById("selectList13-button2");
+ selectListButton2.click();
+ assert_true(selectList.open, "The button part should be updated")
+ await clickOn(document.getElementById("selectList13-option1"));
+ assert_equals(selectList.value, "one");
+
+ const selectListOption4 = document.getElementById("selectList13-option4");
+ selectListOption4.click();
+ assert_equals(selectList.value, "one");
+ let divListboxToRemove = document.getElementById("selectList13-removeContent-listbox");
+ divListboxToRemove.innerHTML = "";
+ assert_equals(selectList.value, "three", "The listbox part should be updated");
+ selectListOption4.click();
+ assert_equals(selectList.value, "four", "Controller code should be applied to the new options");
+
+ let selectListNewListbox = document.getElementById("selectList13-newListbox");
+ selectListNewListbox.innerHTML = "";
+ assert_equals(selectList.value, "");
+ selectListOption4.click();
+ assert_equals(selectList.value, "");
+ }, "Controller code should be updated when nested parts are removed");
+
+ promise_test(async () => {
+ let selectList = document.getElementById("selectList14");
+ assert_equals(selectList.value, "one");
+ const selectListButton0 = document.getElementById("selectList14-button0");
+ const selectListOption2 = document.getElementById("selectList14-option2");
+
+ selectListButton0.click();
+ assert_true(selectList.open);
+ await clickOn(selectListOption2);
+ assert_equals(selectList.value, "two");
+
+ document.body.removeChild(selectList);
+ selectList.removeChild(selectListOption2);
+ assert_equals(selectList.value, "one");
+ let newOption = document.createElement("option");
+ newOption.innerText = "three";
+ selectList.appendChild(newOption);
+ newOption.click();
+ assert_equals(selectList.value, "three", "New option should receive controller code");
+
+ let doc = document.implementation.createHTMLDocument('');
+ let selectList1 = doc.createElement('selectlist');
+ let firstOption = doc.createElement('option');
+ firstOption.innerText = 'one';
+ let secondOption = doc.createElement('option');
+ secondOption.innerText = 'two';
+ selectList1.appendChild(firstOption);
+ selectList1.appendChild(secondOption);
+ assert_equals(selectList1.value, "one");
+ secondOption.click();
+ assert_equals(selectList1.value, "two");
+ document.body.appendChild(selectList1);
+ selectList1.removeChild(secondOption);
+ assert_equals(selectList1.value, "one");
+ }, "Moving a selectlist between documents should keep controller code active");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList15");
+ const selectListButtonContainer = document.getElementById("selectList15-div0");
+
+ const outerDiv = document.createElement("div");
+ const button = document.createElement("input");
+ button.type = button.value = "button";
+ button.setAttribute("behavior", "button");
+ outerDiv.appendChild(button);
+ selectListButtonContainer.appendChild(outerDiv);
+
+ await clickOn(selectList);
+ assert_true(selectList.open, "New button should receive controller code");
+ }, "New parts should be detected even when in the subtree of an inserted node");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList16");
+ const selectListButtonContainer = document.getElementById("selectList16-div0");
+ const selectListButton = document.getElementById("selectList16-button0");
+
+ selectListButtonContainer.remove();
+
+ selectListButton.click();
+ assert_false(selectList.open, "Removed button should no longer have controller code");
+ }, "Part removals should be detected even when in the subtree of a removed node");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html
new file mode 100644
index 0000000000..692eed7caa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: popover position with zoom</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+ #selectList0 {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ zoom: 2;
+ }
+
+ #selectList1 {
+ position: absolute;
+ bottom: 0px;
+ left: 0px;
+ zoom: 1.5;
+ }
+
+ #selectList1-popover {
+ zoom: 2;
+ }
+
+ #selectList2 {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ zoom: 3;
+ }
+
+ #selectList3 {
+ position: absolute;
+ bottom: 0px;
+ right: 0px;
+ zoom: 4;
+ }
+
+ #selectList3-popover {
+ zoom: 1.5;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <button type=selectlist id="selectList0-button">Custom bottom left</button>
+ <listbox id="selectList0-popover">
+ <option>bottom left</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+<br>
+
+<selectlist id="selectList1">
+ <button type=selectlist id="selectList1-button">Custom top left</button>
+ <listbox id="selectList1-popover">
+ <option>top left</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList2">
+ <button type=selectlist id="selectList2-button">Custom bottom right</button>
+ <listbox id="selectList2-popover">
+ <option>bottom right</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList3">
+ <button type=selectlist id="selectList3-button">Custom top right</button>
+ <listbox id="selectList3-popover">
+ <option>top right</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Popover = document.getElementById("selectList0-popover");
+ const selectList0Button = document.getElementById("selectList0-button");
+
+ await clickOn(selectList0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().bottom - selectList0Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().left - selectList0Popover.getBoundingClientRect().left)), 0);
+ }, "The popover should be bottom left positioned");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const selectList1Popover = document.getElementById("selectList1-popover");
+ const selectList1Button = document.getElementById("selectList1-button");
+
+ selectList1Button.click();
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().top - selectList1Popover.getBoundingClientRect().bottom * 2)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().left - selectList1Popover.getBoundingClientRect().left * 2)), 0);
+ }, "The popover should be top left positioned");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const selectList2Popover = document.getElementById("selectList2-popover");
+ const selectList2Button = document.getElementById("selectList2-button");
+
+ selectList2Button.click();
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().bottom - selectList2Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().right - selectList2Popover.getBoundingClientRect().right)), 0);
+ }, "The popover should be bottom right positioned");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3Popover = document.getElementById("selectList3-popover");
+ const selectList3Button = document.getElementById("selectList3-button");
+
+ selectList3Button.click();
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().top - selectList3Popover.getBoundingClientRect().bottom * 1.5)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().right - selectList3Popover.getBoundingClientRect().right * 1.5)), 0);
+ }, "The popover should be top right positioned");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html
new file mode 100644
index 0000000000..a19e2b0d28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: popover position</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+ #selectList0 {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ }
+
+ #selectList1 {
+ position: absolute;
+ bottom: 0px;
+ left: 0px;
+ }
+
+ #selectList2 {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ }
+
+ #selectList3 {
+ position: absolute;
+ bottom: 0px;
+ right: 0px;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox" id="selectList0-popover">
+ <option>bottom left</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+<br>
+
+<selectlist id="selectList1">
+ <div popover slot="listbox" behavior="listbox" id="selectList1-popover">
+ <option>top left</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList2">
+ <div popover slot="listbox" behavior="listbox" id="selectList2-popover">
+ <option>bottom right</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div popover slot="listbox" behavior="listbox" id="selectList3-popover">
+ <option>top right</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Popover = document.getElementById("selectList0-popover");
+
+ await clickOn(selectList0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().bottom - selectList0Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().left - selectList0Popover.getBoundingClientRect().left)), 0);
+ }, "The popover should be bottom left positioned");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const selectList1Popover = document.getElementById("selectList1-popover");
+
+ await clickOn(selectList1);
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().top - selectList1Popover.getBoundingClientRect().bottom)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().left - selectList1Popover.getBoundingClientRect().left)), 0);
+ }, "The popover should be top left positioned");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const selectList2Popover = document.getElementById("selectList2-popover");
+
+ await clickOn(selectList2);
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().bottom - selectList2Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().right - selectList2Popover.getBoundingClientRect().right)), 0);
+ }, "The popover should be bottom right positioned");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3Popover = document.getElementById("selectList3-popover");
+
+ await clickOn(selectList3);
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().top - selectList3Popover.getBoundingClientRect().bottom)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().right - selectList3Popover.getBoundingClientRect().right)), 0);
+ }, "The popover should be top right positioned");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html
new file mode 100644
index 0000000000..a26d026649
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<title>HTMLSelectListElement Test: popover</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id="selectList0">
+ <option>one</option>
+ <option id="selectList0-child2">two</option>
+ <option>three</option>
+ <option>four</option>
+</selectlist>
+
+<selectlist id="selectList1">
+ <button type=selectlist class=button>
+ Custom button
+ </button>
+ <listbox>
+ <option>one</option>
+ <option class="child2">two</option>
+ <option class="child3">three</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList2">
+ <!-- Swap out the listbox part without providing a replacement -->
+ <div slot="listbox"></div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div slot="listbox">
+ <div popover behavior="listbox" id="selectList3-listbox">
+ <option>one</option>
+ </div>
+ </div>
+</selectlist>
+<script>
+
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Child2 = document.getElementById("selectList0-child2");
+ assert_equals(selectList0.value, "one");
+ assert_equals(selectList0.open, false);
+ await clickOn(selectList0);
+ assert_equals(selectList0.open, true);
+ await clickOn(selectList0Child2);
+ assert_equals(selectList0.value, "two");
+ assert_equals(selectList0.open, false);
+
+ await clickOn(selectList0);
+ assert_equals(selectList0.open, true);
+ await clickOn(selectList0Child2);
+ assert_equals(selectList0.open, false);
+ }, "Opening the popover and clicking an option should change the selectlist's value");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const button = selectList1.querySelector(".button");
+ const child2 = selectList1.querySelector(".child2");
+ const child3 = selectList1.querySelector(".child3");
+ assert_equals(selectList1.value, "one");
+ assert_equals(selectList1.open, false);
+ await clickOn(button);
+ assert_equals(selectList1.open, true);
+ await clickOn(child2);
+ assert_equals(selectList1.value, "two", "Clicking an <option> should change the value");
+ assert_equals(selectList1.open, false);
+
+ await clickOn(button);
+ assert_equals(selectList1.open, true);
+ await clickOn(child3);
+ assert_equals(selectList1.value, "three", "Clicking a <div part='option'> should change the value");
+ assert_equals(selectList1.open, false);
+ }, "With custom button and popover: opening the popover and clicking an option should change the selectlist's value");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ await clickOn(selectList2);
+ assert_equals(selectList2.value, "");
+ assert_equals(selectList2.open, false);
+ }, "Clicking a popover with no listbox part does nothing");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3Listbox = document.getElementById("selectList3-listbox");
+ selectList3Listbox.remove();
+
+ await clickOn(selectList3);
+ assert_equals(selectList3.value, "");
+ assert_equals(selectList3.open, false);
+ }, "Clicking a popover with a listbox that was removed does nothing");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html
new file mode 100644
index 0000000000..bda5842a37
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="http://crbug.com/1429839">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id=selectlist>
+ <option id=optone>one</option>
+ <option id=opttwo>two</option>
+</selectlist>
+<style>
+selectlist {
+ background-color: rgb(0, 0, 255);
+}
+selectlist:closed {
+ background-color: rgb(0, 255, 0);
+}
+selectlist:open {
+ background-color: rgb(255, 0, 0);
+}
+</style>
+<button id=button>hello world</button>
+
+<script>
+promise_test(async () => {
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 255, 0)',
+ 'The selectlist should match :closed at the start of the test.');
+
+ await test_driver.click(selectlist);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The selectlist should match :open when opened.');
+
+ await test_driver.click(opttwo);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 255, 0)',
+ 'The selectlist should match :closed after clicking an option.');
+
+ await test_driver.click(selectlist);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The selectlist should match :open when reopened.');
+
+ await test_driver.click(button);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 255, 0)',
+ 'The selectlist should match :closed after light dismiss.');
+}, 'selectlist should not match :open when light dismissed.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html
new file mode 100644
index 0000000000..1d5a082c03
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/547">
+<link rel=help href="https://drafts.csswg.org/selectors/#open-state">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id=myselectlist>
+ <button type=selectlist id=custombutton>button</button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<script>
+test(() => {
+ assert_false(myselectlist.matches(':open'),
+ 'Selectlist should not match :open while it is closed.');
+ assert_true(myselectlist.matches(':closed'),
+ 'Selectlist should match :closed while it is closed.');
+
+ custombutton.click();
+
+ assert_true(myselectlist.matches(':open'),
+ 'Selectlist should match :open while it is open.');
+ assert_false(myselectlist.matches(':closed'),
+ 'Selectlist should not match :closed while it is open.');
+}, 'Selectlist should support :open and :closed pseudo selectors.');
+</script>
+
+<selectlist id=selectlistinvalidation>
+ <button type=selectlist>button</button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+<style>
+selectlist:closed {
+ background-color: red;
+}
+selectlist:open {
+ background-color: green;
+}
+</style>
+
+<script>
+test(() => {
+ const selectlist = document.getElementById('selectlistinvalidation');
+ const button = selectlist.querySelector('button');
+ const option = selectlist.querySelector('option');
+
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The style rules from :closed should apply when the selectlist is closed.');
+
+ button.click();
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 128, 0)',
+ 'The style rules from :open should apply when the selectlist is open.');
+
+ option.click();
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The style rules from :closed should apply when the selectlist is opened and closed again.');
+}, 'Selectlist :open and :closed should invalidate correctly.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html
new file mode 100644
index 0000000000..ef4408915b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: required attribute</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ selectlist:required {
+ border: 3px dashed rgb(255, 0, 0);
+ }
+
+ selectlist:optional {
+ border: 1px solid rgb(128, 128, 128);
+ }
+</style>
+
+<selectlist id="selectlist0" required>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectlist1">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectlist2">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+function checkRequired(style) {
+ assert_equals(style.borderWidth, '3px');
+ assert_equals(style.borderStyle, 'dashed');
+ assert_equals(style.borderColor, 'rgb(255, 0, 0)');
+}
+
+function checkOptional(style) {
+ assert_equals(style.borderWidth, '1px');
+ assert_equals(style.borderStyle, 'solid');
+ assert_equals(style.borderColor, 'rgb(128, 128, 128)');
+}
+
+test(() => {
+ const selectList0 = document.getElementById("selectlist0");
+ const selectList1 = document.getElementById("selectlist1");
+ const selectList2 = document.getElementById("selectlist2");
+
+ checkRequired(window.getComputedStyle(selectList0));
+ checkOptional(window.getComputedStyle(selectList1));
+ checkOptional(window.getComputedStyle(selectList2));
+ selectList2.required = true;
+ checkRequired(window.getComputedStyle(selectList2));
+}, "Test required attribute");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html
new file mode 100644
index 0000000000..7c71ea079c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('option');
+ document.body.appendChild(selectlist);
+ const button = selectlist.querySelector('.fake-selectlist-internal-selectlist-button');
+ button.style.direction = "rtl";
+ button.style.width = "300px";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html
new file mode 100644
index 0000000000..24f7959632
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- Tests selectlist text alignment in rtl -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-rtl-ref.tentative.html">
+
+<style>
+selectlist {
+ direction:rtl;
+ width:300px;
+}
+</style>
+<selectlist>
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html
new file mode 100644
index 0000000000..7d76f1c817
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+ selectlist.querySelector('.fake-selectlist-selected-value')
+ .style.color = 'blue';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html
new file mode 100644
index 0000000000..a43e43d267
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-selected-value-behavior-ref.html">
+
+<selectlist>
+ <div style="color:blue" slot=selected-value behavior=selected-value></div>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html
new file mode 100644
index 0000000000..3be168beba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+ selectlist.querySelector('.fake-selectlist-selected-value')
+ .style.backgroundColor = 'red';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html
new file mode 100644
index 0000000000..8fc05480d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-selected-value-part-ref.html">
+
+<style>
+selectlist::part(selected-value) {
+ background-color: red;
+}
+</style>
+<selectlist>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html
new file mode 100644
index 0000000000..ecb8c886a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+
+ const oldSelectedValue = selectlist.querySelector('.fake-selectlist-selected-value');
+ const newSelectedValue = document.createElement('div');
+ newSelectedValue.textContent = 'new selected value';
+
+ replaceChildElement(newSelectedValue, oldSelectedValue);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html
new file mode 100644
index 0000000000..c69a962c29
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-selected-value-slot-ref.html">
+
+<selectlist>
+ <div slot=selected-value>new selected value</div>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html
new file mode 100644
index 0000000000..1c0be58d67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<button>
+ <span style="color:red">red</span> one
+</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html
new file mode 100644
index 0000000000..5d273c21bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/571">
+<link rel=match href="selectlist-selectedoption-element-cloning-ref.html">
+
+<selectlist>
+ <button type=selectlist>
+ <selectedoption></selectedoption>
+ </button>
+ <option><span style="color:red">red</span> one</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html
new file mode 100644
index 0000000000..593c4a6f21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist>
+ <div>Foo <selectedoption></selectedoption> Bar</div>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<script>
+test(() => {
+ const selectlist = document.querySelector('selectlist');
+ const selectedoption = document.querySelector('selectedoption');
+ const div = document.querySelector('div');
+
+ assert_equals(selectlist.value, 'one', 'one should be selected initially.');
+ assert_equals(selectedoption.textContent, 'one', "selectedoption's initial text content should be one.");
+ assert_equals(div.textContent, 'Foo one Bar', 'Outer textContent should include default selectedoption.');
+
+ selectlist.value = 'two';
+ assert_equals(selectlist.value, 'two', "assigning two into selectlists's value should work.");
+ assert_equals(selectedoption.textContent, 'two', "selectedoption's text content should be updated with the new value.");
+ assert_equals(div.textContent, 'Foo two Bar', 'Outer textContent should include new selectedoption.');
+}, "<selectedoption>'s text contents should be replaced with its ancestor <selectlist>'s selected value.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html
new file mode 100644
index 0000000000..3b7d9d3d9a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input id="input1">
+<selectlist id="selectlist">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+<input id="input3">
+
+<script>
+promise_test(async () => {
+ const TAB_KEY = "\uE004";
+
+ const input1 = document.getElementById("input1");
+ const selectlist = document.getElementById("selectlist");
+
+ input1.focus();
+ assert_equals(document.activeElement.id, "input1", "input1 should be active");
+
+ await test_driver.send_keys(input1, TAB_KEY);
+ assert_equals(document.activeElement.id, "selectlist", "selectlist should be active");
+
+ await test_driver.send_keys(selectlist, TAB_KEY);
+ assert_equals(document.activeElement.id, "input3", "input3 should be active");
+}, "Check that <selectlist> occupies just one slot in tab navigation.");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html
new file mode 100644
index 0000000000..c93efe1118
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<input id="input1">
+<selectlist id="selectlist" tabindex="2">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+<input id="input3" tabindex="1">
+
+<script>
+promise_test(async () => {
+ const TAB_KEY = "\uE004";
+
+ const input1 = document.getElementById("input1");
+ const input3 = document.getElementById("input3");
+
+ input1.focus();
+ assert_equals(document.activeElement.id, "input1", "input1 should be active");
+
+ await test_driver.send_keys(input1, TAB_KEY);
+ assert_equals(document.activeElement.id, "input3", "input3 should be active");
+
+ await test_driver.send_keys(input3, TAB_KEY);
+ assert_equals(document.activeElement.id, "selectlist", "selectlist should be active");
+}, "Check that tabindex applies to <selectlist>");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html
new file mode 100644
index 0000000000..ab28fbede6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>
+.container {
+ display: inline-flex;
+ font-family: sans-serif;
+ font-size: 0.875em;
+}
+</style>
+<div class=container>text node</div>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html
new file mode 100644
index 0000000000..ceefeca608
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-text-only-ref.html">
+
+<selectlist>
+ text node
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html
new file mode 100644
index 0000000000..078e8a4d83
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/687">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id=useragent>
+ <option id=useragentoptionone>one</option>
+ <option id=useragentoptiontwo>two</option>
+</selectlist>
+
+<selectlist id=custom>
+ <div id=custombutton slot=button behavior=button>button</div>
+ <div id=customlistbox popover=auto slot=listbox behavior=listbox>listbox</div>
+</selectlist>
+
+<selectlist id=customwithselection style="user-select:auto">
+ <div id=custombuttonwithselection slot=button behavior=button>button</div>
+ <div id=customlistboxwithselection popover=auto slot=listbox behavior=listbox>listbox</div>
+</selectlist>
+
+<selectlist id=customwithmixedselection>
+ <div id=custombuttonwithmixedselection slot=button behavior=button style="user-select:auto">button</div>
+ <div id=customlistboxwithmixedselection popover=auto slot=listbox behavior=listbox style="user-select:auto">listbox</div>
+</selectlist>
+
+<script>
+test(() => {
+ assert_equals(getComputedStyle(useragent).userSelect, 'none',
+ 'The selectlist should have user-select:none.');
+ assert_equals(getComputedStyle(useragentoptionone).userSelect, 'none',
+ 'The first option should have user-select:none.');
+ assert_equals(getComputedStyle(useragentoptiontwo).userSelect, 'none',
+ 'The second option should have user-select:none.');
+}, 'Option elements should have user-select:none without slotting buttons or listboxes.');
+
+test(() => {
+ assert_equals(getComputedStyle(custom).userSelect, 'none',
+ 'The selectlist should have user-select:none.');
+ assert_equals(getComputedStyle(custombutton).userSelect, 'none',
+ 'The custom button should have user-select:none.');
+ assert_equals(getComputedStyle(customlistbox).userSelect, 'none',
+ 'The custom listbox should have user-select:none.');
+}, 'Slotted in buttons and listboxes should have user-select:none.');
+
+test(() => {
+ assert_equals(getComputedStyle(customwithselection).userSelect, 'auto',
+ 'The selectlist should have user-select:auto.');
+ assert_equals(getComputedStyle(custombuttonwithselection).userSelect, 'auto',
+ 'The custom button should have user-select:auto.');
+ assert_equals(getComputedStyle(customlistboxwithselection).userSelect, 'auto',
+ 'The custom listbox should have user-select:auto.');
+}, 'Setting user-select:auto on selectlists should re-enable selection.');
+
+test(() => {
+ assert_equals(getComputedStyle(customwithmixedselection).userSelect, 'none',
+ 'The selectlist should have user-select:none.');
+ assert_equals(getComputedStyle(custombuttonwithmixedselection).userSelect, 'auto',
+ 'The custom button should have user-select:auto.');
+ assert_equals(getComputedStyle(customlistboxwithmixedselection).userSelect, 'auto',
+ 'The custom listbox should have user-select:auto.');
+}, 'Children of selectlist should be able to opt-in to user-select.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html
new file mode 100644
index 0000000000..2b2033f668
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: validity</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id="selectlist1" required>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+ <option>four</option>
+</selectlist>
+
+<form>
+ <selectlist id="selectlist2" required>
+ </selectlist>
+</form>
+
+<script>
+
+test(() => {
+ let selectList = document.createElement('selectlist');
+ assert_true(selectList.willValidate, "A selectlist element is a submittable element that is a candidate for constraint validation.");
+ let option = document.createElement('option');
+ selectList.appendChild(option);
+ assert_true(selectList.checkValidity(), "Always valid when the selectlist isn't a required value.");
+
+ selectList.required = true;
+ assert_equals(selectList.value, "");
+ assert_false(selectList.checkValidity(), "A selected placeholder option should invalidate the selectlist.");
+
+ let emptyOption = document.createElement('option');
+ selectList.appendChild(emptyOption);
+ assert_false(selectList.checkValidity(), "A selected placeholder option should invalidate the selectlist even if there are multiple options.");
+ emptyOption.selected = true;
+ assert_true(selectList.checkValidity(), "An empty non-placeholder option should be a valid choice.");
+
+ let filledOption = document.createElement('option');
+ filledOption.value = "test";
+ selectList.appendChild(filledOption);
+ filledOption.selected = true;
+ assert_equals(selectList.value, "test", "The non-empty value should be set.");
+ assert_true(selectList.checkValidity(), "A non-empty non-placeholder option should be a valid choice.");
+
+ selectList.removeChild(option);
+ selectList.appendChild(emptyOption);
+ emptyOption.selected = true;
+ assert_equals(selectList.value, "", "The empty value should be set.");
+ assert_true(selectList.checkValidity(), "Only the first option can be seen as a placeholder.");
+
+ selectList.removeChild(filledOption);
+ assert_false(selectList.checkValidity(), "A selected placeholder option should invalidate the selectlist.");
+
+ emptyOption.value = "test2";
+ assert_equals(selectList.value, "test2");
+ assert_true(selectList.checkValidity(), "A non-empty option value should be a valid choice.");
+
+ emptyOption.removeAttribute("value");
+ assert_equals(selectList.value, "");
+ assert_false(selectList.checkValidity());
+ emptyOption.innerText = "test";
+ assert_equals(selectList.value, "test");
+ assert_true(selectList.checkValidity(), "A non-empty option should be a valid choice.");
+
+ const selectList1 = document.getElementById('selectlist1');
+ assert_equals(selectList1.value, "one");
+ assert_true(selectList1.checkValidity(), "A selectlist with non-empty placeholder option should be valid.");
+}, "Validation for placeholder option");
+
+test(() => {
+ const selectList2 = document.getElementById('selectlist2');
+ assert_equals(selectList2.value, "");
+ assert_false(selectList2.checkValidity());
+ let form = document.querySelector('form');
+ let invalidControl = form.querySelector('selectlist:invalid');
+ assert_equals(selectList2, invalidControl);
+ let didDispatchInvalid = false;
+ invalidControl.addEventListener('invalid', e => { didDispatchInvalid = true; });
+ let didDispatchSubmit = false;
+ form.addEventListener('submit', event => { event.preventDefault(); didDispatchSubmit = true; });
+
+ form.requestSubmit();
+ assert_true(didDispatchInvalid);
+ assert_false(didDispatchSubmit);
+}, "Check form not submitted for invalid selectlist");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html
new file mode 100644
index 0000000000..243067937c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/664">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id=selectlist>
+ <option id=optone>innertext one</option>
+ <option id=opttwo value=valueattribute>innertext two</option>
+</selectlist>
+
+<script>
+test(() => {
+ assert_equals(selectlist.value, 'innertext one',
+ 'The first option should be selected initially.');
+ selectlist.value = 'valueattribute';
+ assert_equals(selectlist.value, 'valueattribute',
+ 'Assigning value should look at the options value, not innertext');
+}, 'selectlist.value should reflect option.value');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html
new file mode 100644
index 0000000000..20e72ac1dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html
@@ -0,0 +1,224 @@
+<!DOCTYPE html>
+<title>HTMLSelectListElement Test: value and selectedOption</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id="selectList0"></selectlist>
+
+<selectlist id="selectList1">
+ <option>one</option>
+ <option>two</option>
+ <div>I'm a div with no part attr</div>
+ <option id="selectList1-option3">three</option>
+ <option>four</option>
+</selectlist>
+
+<selectlist id="selectList2">
+ <div behavior="option">one</div>
+ <div behavior="option">two</div>
+ <div>I'm a div with no part attr</div>
+ <div behavior="option">three</div>
+ <div behavior="option">four</div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div>I'm a div with no part attr</div>
+ <option id="selectList3-child1">one</option>
+ <option id="selectList3-child2">two</option>
+ <option id="selectList3-child3">three</option>
+</selectlist>
+
+<selectlist id="selectList4">
+ <div slot="button" behavior="button">
+ <div behavior="selected-value" id="selectList4-custom-selected-value">Default custom selected-value text</div>
+ </div>
+ <option id="selectList4-option1">one</option>
+ <option id="selectList4-option2">two</option>
+</selectlist>
+
+<selectlist id="selectList5">
+ <div slot="button" behavior="button">
+ <div behavior="selected-value" id="selectList5-custom-selected-value">Default custom selected-value text</div>
+ </div>
+ <div popover slot="listbox" behavior="listbox">
+ <option id="selectList5-option1">one</option>
+ <option id="selectList5-option2">two</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList6">
+ <option id="selectList6-option1">one</option>
+ <option id="selectList6-option2" selected>two</option>
+ <option id="selectList6-option3">three</option>
+</selectlist>
+
+<selectlist id="selectList7">
+ <option id="selectList7-option1">one</option>
+ <option id="selectList7-option2" selected value="test">two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+
+test(() => {
+ const selectList0 = document.getElementById("selectList0");
+ assert_equals(selectList0.value, "");
+ assert_equals(selectList0.selectedOption, null);
+ selectList0.value = "something";
+ assert_equals(selectList0.value, "", "If there is no matching option, selectlist should be cleared");
+ assert_equals(selectList0.selectedOption, null);
+}, "Test that HTMLSelectList with no options has empty string for value and null for selectedOption");
+
+test(() => {
+ const selectList1 = document.getElementById("selectList1");
+ assert_equals(selectList1.value, "one", "value should start with the text of the first option part");
+
+ selectList1.value = "three";
+ assert_equals(selectList1.value, "three", "value can be set to the text of an option part");
+ assert_equals(selectList1.selectedOption, document.getElementById("selectList1-option3"));
+
+ selectList1.value = "I'm a div with no part attr";
+ assert_equals(selectList1.value, "", "If there is no matching option selectlist should be cleared");
+ assert_equals(selectList1.selectedOption, null);
+}, "Test value and selectedOption with HTMLOptionElement element option parts");
+
+test(() => {
+ const selectList1 = document.getElementById("selectList1");
+ selectList1.value = "one";
+ assert_equals(selectList1.value, "one");
+
+ selectList1.value = null;
+ assert_equals(selectList1.value, "");
+ assert_equals(selectList1.selectedOption, null);
+}, "Test value and selectedOption when value is null");
+
+test(() => {
+ const selectList1 = document.getElementById("selectList1");
+ selectList1.value = "one";
+ assert_equals(selectList1.value, "one");
+
+ selectList1.value = undefined;
+ assert_equals(selectList1.value, "");
+ assert_equals(selectList1.selectedOption, null);
+}, "Test value and selectedOption when value is undefined");
+
+test(() => {
+ const selectList2 = document.getElementById("selectList2");
+ assert_equals(selectList2.value, "", "Non-HTMLOptionElements shouldn't be treated as option parts");
+ assert_equals(selectList2.selectedOption, null);
+
+ selectList2.value = "three";
+ assert_equals(selectList2.value, "", "value can't be set when there are no option parts'");
+ assert_equals(selectList2.selectedOption, null);
+}, "Test value with non-HTMLOptionElement elements labeled as parts");
+
+test(() => {
+ const selectList3 = document.getElementById("selectList3");
+ assert_equals(selectList3.value, "one", "value should start with the text of the first option part");
+ assert_equals(selectList3.selectedOption, document.getElementById("selectList3-child1"));
+
+ document.getElementById("selectList3-child3").remove();
+ assert_equals(selectList3.value, "one", "Removing a non-selected option should not change the value");
+ assert_equals(selectList3.selectedOption, document.getElementById("selectList3-child1"));
+
+ document.getElementById("selectList3-child1").remove();
+ assert_equals(selectList3.value, "two", "When the selected option is removed, the new first option should become selected");
+ assert_equals(selectList3.selectedOption, document.getElementById("selectList3-child2"));
+
+ document.getElementById("selectList3-child2").remove();
+ assert_equals(selectList3.value, "", "When all options are removed, value should be the empty string");
+ assert_equals(selectList3.selectedOption, null);
+}, "Test that value and selectedOption are updated when options are removed");
+
+test(() => {
+ const selectList4 = document.getElementById("selectList4");
+ let customSelectedValuePart = document.getElementById("selectList4-custom-selected-value");
+ assert_equals(selectList4.value, "one", "value should start with the text of the first option part");
+ assert_equals(selectList4.selectedOption, document.getElementById("selectList4-option1"));
+ assert_equals(customSelectedValuePart.innerText, "one", "Custom selected value part should be set to initial value of selectlist");
+
+ selectList4.value = "two";
+ assert_equals(customSelectedValuePart.innerText, "two", "Custom selected value part should be updated when value of selectlist changes");
+ assert_equals(selectList4.selectedOption, document.getElementById("selectList4-option2"));
+}, "Test that slotted-in selected-value part is updated to value of selectlist");
+
+test(() => {
+ const selectList5 = document.getElementById("selectList5");
+ let customSelectedValuePart = document.getElementById("selectList5-custom-selected-value");
+ assert_equals(selectList5.value, "one", "value should start with the text of the first option part");
+ assert_equals(selectList5.selectedOption, document.getElementById("selectList5-option1"));
+ assert_equals(customSelectedValuePart.innerText, "one", "Custom selected value part should be set to initial value of selectlist");
+
+ selectList5.value = "two";
+ assert_equals(customSelectedValuePart.innerText, "two", "Custom selected value part should be updated when value of selectlist changes");
+ assert_equals(selectList5.selectedOption, document.getElementById("selectList5-option2"));
+}, "Test that option parts in a slotted-in listbox are reflected in the value property");
+
+test(() => {
+ let selectList = document.createElement('selectlist');
+ assert_equals(selectList.value, "");
+ let option = document.createElement('option');
+ option.innerText = "one";
+ selectList.appendChild(option);
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, option);
+
+ let newOption = document.createElement('option');
+ newOption.innerText = 'two';
+ selectList.appendChild(newOption);
+ selectList.value = "two";
+ assert_equals(selectList.value, "two");
+ assert_equals(selectList.selectedOption, newOption);
+
+ option.click();
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, option);
+}, "Test that value and selectedOption are correctly updated");
+
+test(() => {
+ const selectList = document.getElementById("selectList6");
+ let selectListOption1 = document.getElementById("selectList6-option1");
+
+ assert_equals(selectList.value, "two");
+ assert_equals(selectList.selectedOption, document.getElementById("selectList6-option2"));
+ assert_false(selectListOption1.selected);
+ selectListOption1.selected = true;
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, selectListOption1);
+
+ let newOption = document.createElement("option");
+ newOption.innerText = "four";
+ newOption.selected = true;
+ selectList.appendChild(newOption);
+ assert_equals(selectList.value, "four");
+ assert_equals(selectList.selectedOption, newOption);
+ assert_false(selectListOption1.selected);
+
+ selectList.value = "three";
+ assert_equals(selectList.selectedOption, document.getElementById("selectList6-option3"));
+ assert_false(newOption.selected);
+}, "Test that HTMLOption.selected updates selectlist.value and selectlist.selectedOption");
+
+test(() => {
+ const selectList = document.getElementById("selectList7");
+ let selectListOption1 = document.getElementById("selectList7-option1");
+
+ assert_equals(selectList.value, "test");
+ assert_equals(selectList.selectedOption, document.getElementById("selectList7-option2"));
+ assert_false(selectListOption1.selected);
+ selectListOption1.selected = true;
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, selectListOption1);
+
+ selectListOption1.value = "new test";
+ assert_equals(selectList.value, "new test");
+ assert_equals(selectList.selectedOption, selectListOption1);
+ selectListOption1.removeAttribute("value");
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, selectListOption1);
+ selectListOption1.value = "";
+ assert_equals(selectList.value, "");
+ assert_equals(selectList.selectedOption, selectListOption1);
+}, "Test that HTMLOption.value updates selectlist.value");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html
new file mode 100644
index 0000000000..9973696ddd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/600">
+<link rel=mismatch href="selectlist-writingmode-tb-ref.html">
+
+<selectlist style="writing-mode: vertical-lr">
+ <option>hello</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html
new file mode 100644
index 0000000000..dc74203e69
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/600">
+<link rel=mismatch href="selectlist-writingmode-tb-ref.html">
+
+<selectlist style="writing-mode: vertical-rl">
+ <option>hello</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html
new file mode 100644
index 0000000000..db922f5f9f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<selectlist style="writing-mode: horizontal-tb">
+ <option>hello</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html
new file mode 100644
index 0000000000..9cc5a1d603
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script>history.back()</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js
new file mode 100644
index 0000000000..77b3f74921
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js
@@ -0,0 +1,112 @@
+function replaceChildElement(newChild, oldChild) {
+ oldChild.parentElement.replaceChild(newChild, oldChild);
+}
+
+function createFakeSelectlist(selectedValueText, includeListbox) {
+ const selectlist = document.createElement('div');
+ selectlist.classList.add('fake-selectlist');
+ selectlist.innerHTML = `
+ <button class="fake-selectlist-internal-selectlist-button">
+ <div class="fake-selectlist-selected-value"></div>
+ <div class="fake-selectlist-internal-selectlist-button-icon"></div>
+ </button>
+ `;
+ if (includeListbox) {
+ const listbox = document.createElement('div');
+ listbox.classList.add('fake-selectlist-listbox');
+ listbox.anchorElement = selectlist;
+ selectlist.appendChild(listbox);
+ }
+ selectlist.appendChild(createFakeSelectlistStyles());
+
+ const selectedvalue = selectlist.querySelector('.fake-selectlist-selected-value');
+ if (selectedValueText) {
+ selectedvalue.textContent = selectedValueText;
+ }
+
+ return selectlist;
+}
+
+function createFakeSelectlistStyles() {
+ const style = document.createElement('style');
+ style.textContent = `
+ .fake-selectlist {
+ display: inline-flex;
+ font-family: sans-serif;
+ font-size: 0.875em;
+ user-select: none;
+ }
+
+ .fake-selectlist-internal-selectlist-button {
+ color: fieldtext;
+ background-color: field;
+ appearance: none;
+ cursor: default;
+ font-size: inherit;
+ text-align: inherit;
+ display: inline-flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ align-items: center;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ padding: 0.25em;
+ border-width: 1px;
+ border-style: solid;
+ border-color: buttonborder;
+ border-image: initial;
+ border-radius: 0.25em;
+ }
+
+ .fake-selectlist-selected-value {
+ color: FieldText;
+ flex-grow:1;
+ }
+
+ .fake-selectlist-internal-selectlist-button-icon {
+ background-image: url(support/selectlist_button_icon.svg);
+ background-origin: content-box;
+ background-repeat: no-repeat;
+ background-size: contain;
+ height: 1.0em;
+ margin-inline-start: 4px;
+ opacity: 1;
+ outline: none;
+ padding-bottom: 2px;
+ padding-inline-start: 3px;
+ padding-inline-end: 3px;
+ padding-top: 2px;
+ width: 1.2em;
+ }
+
+ .fake-selectlist-listbox {
+ font-family: sans-serif;
+ box-shadow: rgba(0, 0, 0, 0.13) 0px 12.8px 28.8px, rgba(0, 0, 0, 0.11) 0px 0px 9.2px;
+ box-sizing: border-box;
+ background-color: canvas;
+ min-inline-size: anchor-size(self-inline);
+ min-block-size: 1lh;
+ position: fixed;
+ width: fit-content;
+ height: fit-content;
+ color: canvastext;
+ overflow: auto;
+ border-width: initial;
+ border-style: solid;
+ border-color: initial;
+ border-image: initial;
+ border-radius: 0.25em;
+ padding: 0.25em;
+ margin: 0px;
+ inset: auto;
+
+ top: anchor(bottom);
+ }
+
+ .fake-selectlist option {
+ font-size: 0.875em;
+ padding: 0.25em;
+ }
+ `;
+ return style;
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg
new file mode 100644
index 0000000000..1a6c0193e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="14" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">\
+ <path d="M4 6 L10 12 L 16 6" stroke="WindowText" stroke-width="3" stroke-linejoin="round"/>\
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html
new file mode 100644
index 0000000000..1706ed34cc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/599">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1359089">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<selectlist id=defaultlistbox>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<selectlist id=customlistbox>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<script>
+const tabKey = '\uE004';
+
+document.querySelectorAll('selectlist').forEach(selectlist => {
+ promise_test(async () => {
+ selectlist.focus();
+ await test_driver.send_keys(selectlist, ' ');
+ assert_true(selectlist.open, 'Listbox should be open after pressing space.');
+
+ selectlist.addEventListener('keydown', event => {
+ if (event.key === 'Tab') {
+ event.preventDefault();
+ }
+ }, {once: true});
+ await test_driver.send_keys(document.activeElement, tabKey);
+ assert_true(selectlist.open, 'Listbox should stay open when the tab keydown is preventDefaulted.');
+
+ await test_driver.send_keys(document.activeElement, tabKey);
+ assert_false(selectlist.open, 'Listbox should close after pressing the tab key.');
+ }, `${selectlist.id}: Pressing tab should close the listbox.`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/change-to-empty-value.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/change-to-empty-value.html
new file mode 100644
index 0000000000..b3aa7b416d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/change-to-empty-value.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Change event when clearing a textarea</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1881457">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<textarea>abc</textarea>
+<script>
+promise_test(async function() {
+ let textarea = document.querySelector("textarea");
+ let changeFired = false;
+ textarea.addEventListener("change", () => {
+ changeFired = true;
+ }, { once: true });
+
+ textarea.focus();
+ assert_equals(document.activeElement, textarea, "Should focus textarea");
+ assert_false(changeFired, "Shouldn't have fired change event after focus");
+ textarea.select();
+ const kBackspaceKey = "\uE003";
+ await test_driver.send_keys(textarea, kBackspaceKey)
+ assert_false(changeFired, "Shouldn't have fired change event after select");
+ textarea.blur();
+ assert_true(changeFired, "Should've have fired change event after blur");
+ assert_equals(textarea.value, "", "Should've have cleared the value");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/cloning-steps.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/cloning-steps.html
new file mode 100644
index 0000000000..7a85bd26a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/cloning-steps.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Cloning of textarea elements</title>
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-node-clonenode">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-node-clone">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-node-clone-ext">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-node-clone-ext">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(function() {
+ var textarea = document.createElement("textarea");
+ textarea.value = "foo bar";
+
+ var copy = textarea.cloneNode();
+ assert_equals(copy.value, "foo bar");
+}, "textarea element's value should be cloned");
+
+test(function() {
+ var textarea = document.createElement("textarea");
+ textarea.value = "foo bar";
+
+ var copy = textarea.cloneNode();
+ copy.setAttribute("value", "something else");
+
+ assert_equals(copy.value, "foo bar");
+}, "textarea element's dirty value flag should be cloned, so setAttribute doesn't affect the cloned textarea's value");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-cr.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-cr.html
new file mode 100644
index 0000000000..08d0982ba5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-cr.html
@@ -0,0 +1 @@
+<!doctype html> <html class="reftest-wait"> <meta charset="utf-8"> <title>textarea multiline placeholder (CR)</title> <link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#attr-textarea-placeholder"> <meta name="assert" content="textarea element's placeholder preserves newlines (CR)"> <link rel="match" href="multiline-placeholder-ref.html"> <link rel="stylesheet" href="support/placeholder.css"> <textarea rows="5" placeholder="this is a multiline placeholder"></textarea> <textarea rows="5" placeholder="this is&#xd;a multiline&#xd;&#xd;placeholder"></textarea> <textarea rows="5" id="dynamic"></textarea> <script> document.querySelector("#dynamic") .setAttribute("placeholder", "this is\ra multiline\r\rplaceholder"); document.documentElement.classList.remove("reftest-wait"); </script> </html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-crlf.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-crlf.html
new file mode 100644
index 0000000000..b82a203076
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-crlf.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>textarea multiline placeholder (CRLF)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#attr-textarea-placeholder">
+<meta name="assert" content="textarea element's placeholder preserves newlines (CRLF)">
+<link rel="match" href="multiline-placeholder-ref.html">
+<link rel="stylesheet" href="support/placeholder.css">
+<textarea rows="5" placeholder="this is
+a multiline
+
+placeholder"></textarea>
+<textarea rows="5" placeholder="this is&#xd;&#xa;a multiline&#xd;&#xa;&#xd;&#xa;placeholder"></textarea>
+<textarea rows="5" id="dynamic"></textarea>
+<script>
+ document.querySelector("#dynamic")
+ .setAttribute("placeholder", "this is\r\na multiline\r\n\r\nplaceholder");
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-ref.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-ref.html
new file mode 100644
index 0000000000..0234ed64c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder-ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="stylesheet" href="support/placeholder.css">
+<textarea rows="5" class="placeholder">this is
+a multiline
+
+placeholder</textarea>
+<textarea rows="5" class="placeholder">this is
+a multiline
+
+placeholder</textarea>
+<textarea rows="5" class="placeholder">this is
+a multiline
+
+placeholder</textarea>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder.html
new file mode 100644
index 0000000000..4e835a6f56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/multiline-placeholder.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>textarea multiline placeholder</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#attr-textarea-placeholder">
+<meta name="assert" content="textarea element's placeholder preserves newlines">
+<link rel="match" href="multiline-placeholder-ref.html">
+<link rel="stylesheet" href="support/placeholder.css">
+<textarea rows="5" placeholder="this is
+a multiline
+
+placeholder"></textarea>
+<textarea rows="5" placeholder="this is&#xa;a multiline&#xa;&#xa;placeholder"></textarea>
+<textarea rows="5" id="dynamic"></textarea>
+<script>
+ document.querySelector("#dynamic")
+ .setAttribute("placeholder", "this is\na multiline\n\nplaceholder");
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
+
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space-notref.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space-notref.html
new file mode 100644
index 0000000000..375bdef874
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space-notref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<style>
+ textarea {
+ max-width: 100px;
+ }
+</style>
+<textarea placeholder="This is a really long string that needs to be truncated"></textarea>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space.tentative.html
new file mode 100644
index 0000000000..7af55643fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/placeholder-white-space.tentative.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Textarea placeholder honors textarea's text-overflow</title>
+<link rel=author href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel=author href="https://mozilla.com" title="Mozilla">
+<link rel=mismatch href="placeholder-white-space-notref.html">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/6669">
+<style>
+ textarea {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: 100px;
+ }
+</style>
+<textarea placeholder="This is a really long string that needs to be truncated"></textarea>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/support/placeholder.css b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/support/placeholder.css
new file mode 100644
index 0000000000..9aaed05c86
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/support/placeholder.css
@@ -0,0 +1,6 @@
+textarea.placeholder,
+textarea::placeholder {
+ /* revert browser styling of the placeholder */
+ color: GrayText; /* blink/webkit use colour */
+ opacity: 1.0; /* gecko uses opacity */
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-maxlength.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-maxlength.html
new file mode 100644
index 0000000000..3ea6739518
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-maxlength.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>textarea maxlength</title>
+ <link rel="author" title="tigercosmos" href="mailto:phy.tiger@gmail.com">
+ <link rel=help href="https://html.spec.whatwg.org/multipage/#attr-textarea-maxlength">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+
+ <textarea id="none"></textarea>
+ <textarea id="negative" maxlength="-5"></textarea>
+ <textarea id="non-numeric" maxlength="not-a-number"></textarea>
+ <textarea id="assign-negative"></textarea>
+ <textarea id="assign-non-numeric"></textarea>
+
+ <script>
+ test(
+ function () {
+ assert_equals(document.getElementById("none").maxLength, -1);
+ }, "Unset maxlength is -1");
+
+ test(
+ function () {
+ assert_equals(document.getElementById("negative").maxLength, -1);
+ }, "Negative maxlength is always -1");
+
+ test(
+ function () {
+ assert_equals(document.getElementById("non-numeric").maxLength, -1);
+ }, "Non-numeric maxlength is -1");
+
+ test(
+ function () {
+ assert_throws_dom("INDEX_SIZE_ERR", function () {
+ document.getElementById("assign-negative").maxLength = -5;
+ });
+ }, "Assigning negative integer throws IndexSizeError");
+
+ test(
+ function () {
+ document.getElementById("assign-non-numeric").maxLength = "not-a-number";
+ assert_equals(document.getElementById("assign-non-numeric").maxLength, 0);
+ }, "Assigning non-numeric to maxlength sets maxlength to 0");
+ </script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-minlength.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-minlength.html
new file mode 100644
index 0000000000..2d40901b40
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-minlength.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>textarea minlength</title>
+ <link rel="author" title="tigercosmos" href="mailto:phy.tiger@gmail.com">
+ <link rel=help href="https://html.spec.whatwg.org/multipage/#attr-textarea-minlength">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+
+ <textarea id="none"></textarea>
+ <textarea id="negative" minlength=-5></textarea>
+ <textarea id="non-numeric" minlength="not-a-number"></textarea>
+ <textarea id="assign-negative"></textarea>
+ <textarea id="assign-non-numeric"></textarea>
+
+ <script>
+ test(
+ function () {
+ assert_equals(document.getElementById("none").minLength, -1);
+ }, "Unset minlength is -1");
+
+ test(
+ function () {
+ assert_equals(document.getElementById("negative").minLength, -1);
+ }, "Negative minlength is always -1");
+
+ test(
+ function () {
+ assert_equals(document.getElementById("non-numeric").minLength, -1);
+ }, "Non-numeric minlength is -1");
+
+ test(
+ function () {
+ assert_throws_dom("INDEX_SIZE_ERR", function () {
+ document.getElementById("assign-negative").minLength = -5;
+ });
+ }, "Assigning negative integer throws IndexSizeError");
+
+ test(
+ function () {
+ document.getElementById("assign-non-numeric").minLength = "not-a-number";
+ assert_equals(document.getElementById("assign-non-numeric").minLength, 0);
+ }, "Assigning non-numeric to minlength sets minlength to 0");
+ </script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi-ref.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi-ref.html
new file mode 100644
index 0000000000..d69195b4f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML Test reference: newline in &lt;textarea&gt; separates bidi paragraphs</title>
+ <link rel="author" title="Amir E. Aharoni" href="mailto:amir.aharoni@mail.huji.ac.il"/>
+ <link rel="author" title="Eyal Sela" href="mailto:eyal@post.isoc.org.il"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-textarea-element"/>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the rightmost character in the first line below is a full stop and to the left of it is a Hebrew letter.</p></div>
+ <div class="test">
+ <textarea cols="70" rows="3">
+A Hebrew letter and a full stop: &#x05d0;.&lrm;
+&#x05d0; this line begins with a Hebrew letter.
+ </textarea>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi.html
new file mode 100644
index 0000000000..ce1ff944c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-newline-bidi.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: newline in &lt;textarea&gt; separates bidi paragraphs</title>
+ <link rel="match" href="textarea-newline-bidi-ref.html" />
+ <link rel="author" title="Amir E. Aharoni" href="mailto:amir.aharoni@mail.huji.ac.il"/>
+ <link rel="author" title="Eyal Sela" href="mailto:eyal@post.isoc.org.il"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-textarea-element"/>
+ <meta name="assert"
+ content="A newline in a textarea element, and in its raw value, should separate paragraphs for the purposes of the Unicode bidirectional algorithm."/>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the rightmost character in the first line below is a full stop and to the left of it is a Hebrew letter.</p></div>
+ <div class="test">
+ <textarea cols="70" rows="3">
+A Hebrew letter and a full stop: &#x05d0;.
+&#x05d0; this line begins with a Hebrew letter.
+ </textarea>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-lineheight.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-lineheight.html
new file mode 100644
index 0000000000..e7df07c97a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-lineheight.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>textarea placeholder line-height</title>
+ <link rel="author" title="Daniel Libby" href="mailto:dlibby@microsoft.com">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ textarea {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ }
+ </style>
+</head>
+
+<body>
+ <textarea rows=1 placeholder=foo style="border:0"></textarea>
+ <script>
+ let textarea = document.querySelector('textarea');
+ const lineHeight = 19.5;
+ textarea.style.lineHeight = lineHeight + "px";
+ test(
+ function () {
+ assert_equals(textarea.getBoundingClientRect().height, lineHeight);
+ }, "Bounding rect height for textarea must be the same as line-height");
+
+ test(
+ function () {
+ assert_equals(getComputedStyle(textarea).lineHeight, lineHeight + "px");
+ }, "ComputedStyle line-height for textarea must be the same as set value");
+ </script>
+</body>
+
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-manual.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-manual.html
new file mode 100644
index 0000000000..d59a259415
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-placeholder-manual.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: textarea - placeholder attribute</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-textarea-placeholder">
+<meta name="flags" content="interact">
+<body>
+ <p>
+ Test passes if there is a "Placeholder Text" in the text area,
+ and if the "Placeholder Text" disappears after type in any character.
+ </p>
+ <textarea placeholder="Placeholder Text"></textarea>
+</body>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-event-manual.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-event-manual.html
new file mode 100644
index 0000000000..f1679e2809
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-event-manual.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLTextAreaElement Test: select event</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<meta name="flags" content="interact">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p>Select any numberic characters in the text area below</p>
+
+<form id="testForm" name="testForm">
+ <textarea id="testtextarea">0123456789</textarea>
+</form>
+
+<script>
+
+var textarea = document.getElementById("testtextarea");
+
+setup({explicit_done : true});
+setup({explicit_timeout : true});
+
+on_event(textarea, "select", function(evt) {
+ test(function() {
+ assert_greater_than(textarea.value.substring(textarea.selectionStart, textarea.selectionEnd).length, 0, "Check if select event captured when text selected");
+ });
+ done();
+});
+
+</script>
+
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-manual.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-manual.html
new file mode 100644
index 0000000000..4e98ba5093
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-select-manual.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLTextAreaElement Test: select()</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<meta name="flags" content="interact">
+
+<p>Test passes if content of the input area is selected</p>
+
+<textarea id="test_obj">1234567</textarea>
+<script>
+var textarea = document.querySelector("#test_obj");
+textarea.select();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-setcustomvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-setcustomvalidity.html
new file mode 100644
index 0000000000..922a1e73e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-setcustomvalidity.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<title>textarea setCustomValidity</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<textarea id='textarea_test'></textarea>
+
+<script>
+
+test(() => {
+ let elem = document.getElementById("textarea_test");
+ assert_false(elem.validity.customError);
+ elem.setCustomValidity("custom error");
+ assert_true(elem.validity.customError);
+}, "textarea setCustomValidity is correct")
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-crash.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-crash.html
new file mode 100644
index 0000000000..9d66a4ae05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-crash.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script>
+addEventListener("load", () => {
+ const textarea = document.querySelector("textarea");
+ const ul = document.createElement('ul');
+
+ const textNodeInTextarea = document.createTextNode("");
+ textarea.appendChild(textNodeInTextarea);
+ document.documentElement.getBoundingClientRect();
+
+ textarea.appendChild(ul);
+ const range = document.createRange();
+ range.selectNode(ul);
+
+ textNodeInTextarea.data = "ab";
+ textNodeInTextarea.splitText(1);
+});
+</script>
+</head>
+<body><textarea></textarea></body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-simple-crash.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-simple-crash.html
new file mode 100644
index 0000000000..d3c1e7ce94
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-splittext-with-range-simple-crash.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script>
+document.addEventListener("DOMContentLoaded", () => {
+ const textarea = document.querySelector("textarea");
+ getSelection().selectAllChildren(textarea);
+ textarea.firstChild.splitText(0);
+});
+</script>
+</head>
+<body><textarea>abcd</textarea></body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-textLength.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-textLength.html
new file mode 100644
index 0000000000..d249278960
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-textLength.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<title>The textLengh IDL attribute</title>
+<meta content="charset=utf-16">
+<link rel="author" title="tigercosmos" href="mailto:phy.tiger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-textarea-textlength">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<textarea id="textarea"></textarea>
+<script>
+ var textarea = document.getElementById("textarea");
+
+ test(function () {
+ textarea.value= "Hello, World!";
+ assert_equals(textarea.textLength, 13);
+
+ textarea.value = "\u4f60\u597d\uff0c\u4e16\u754c\uff01"; //你好,世界!
+ assert_equals(textarea.textLength, 6);
+ }, "Textarea's 'testLength' should work for utf-16.");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-type.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-type.html
new file mode 100644
index 0000000000..ac80f93656
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-type.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<title>The type IDL attribute</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-textarea-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="test">
+<textarea></textarea>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementById("test")
+ .getElementsByTagName("textarea")[0].type,
+ "textarea");
+}, "Textarea's type attribute should return 'textarea'");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-clone.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-clone.html
new file mode 100644
index 0000000000..23d90e714b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-clone.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTML Test: &lt;textarea&gt; validity state is correct after a clone</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-textarea-element">
+<link rel="help" href="https://bugzil.la/1472169">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ let form = document.createElement("form");
+ let textarea = document.createElement("textarea");
+ textarea.required = true;
+
+ textarea.appendChild(document.createTextNode("A"));
+ form.appendChild(textarea);
+
+ assert_true(textarea.validity.valid);
+
+ let formClone = form.cloneNode(true);
+ assert_equals(
+ formClone.querySelector('textarea').validity.valid,
+ textarea.validity.valid,
+ "Validity state should be preserved after a clone"
+ );
+}, "<textarea> validity state should be preserved after a clone");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-valueMissing-inside-datalist.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-valueMissing-inside-datalist.html
new file mode 100644
index 0000000000..f0ce0028ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/textarea-validity-valueMissing-inside-datalist.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>textarea element with "required" attribute and empty value is considered "suffering from being missing" even if inside datalist element</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:suffering-from-being-missing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<datalist>
+ <textarea required></textarea>
+</datalist>
+
+<script>
+test(() => {
+ const textarea = document.querySelector("textarea");
+
+ assert_true(textarea.validity.valueMissing);
+ assert_false(textarea.validity.valid);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent-xhtml.xhtml b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent-xhtml.xhtml
new file mode 100644
index 0000000000..9462e94935
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent-xhtml.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>textarea element value/defaultValue/textContent functionality</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-textarea-value"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+"use strict";
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.appendChild(document.createCDATASection("foo bar baz"));
+ assert_equals(textarea.defaultValue, "foo bar baz", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo bar baz",
+ "changing the child text content should change the raw value, and subsequently the api value");
+
+}, "defaultValue and value include CDATASection Text nodes");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html
new file mode 100644
index 0000000000..a1a405fdbe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html
@@ -0,0 +1,181 @@
+<!DOCTYPE HTML>
+<title>textarea element value/defaultValue/textContent functionality</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-textarea-value">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ assert_equals(textarea.defaultValue, "", "defaultValue is empty string when it has no content");
+ assert_equals(textarea.value, "", "value is empty string when it has no content");
+
+}, "defaultValue and value are the empty string by default");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.textContent = "foo bar";
+ assert_equals(textarea.defaultValue, "foo bar", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo bar",
+ "changing the textContent should change the raw value, and subsequently the api value");
+
+}, "defaultValue and value are affected by setting textContent");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.textContent = "some text";
+ textarea.firstChild.nodeValue = "foo bar";
+ assert_equals(textarea.defaultValue, "foo bar", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo bar",
+ "changing the textContent should change the raw value, and subsequently the api value");
+
+}, "defaultValue and value are affected by setting nodeValue on a child text node");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.textContent = "some text";
+ textarea.firstChild.data = "foo bar";
+ assert_equals(textarea.defaultValue, "foo bar", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo bar",
+ "changing the textContent should change the raw value, and subsequently the api value");
+
+}, "defaultValue and value are affected by setting data on a child text node");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.textContent = "foo bar";
+ textarea.appendChild(document.createTextNode(" baz"));
+ assert_equals(textarea.defaultValue, "foo bar baz", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo bar baz",
+ "changing the textContent should change the raw value, and subsequently the api value");
+
+}, "defaultValue and value are affected by textContent in combination with appending a text node");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+ textarea.textContent = "foo bar";
+
+ const frag = document.createDocumentFragment();
+ frag.appendChild(document.createTextNode(" baz"));
+ const el = document.createElement("span");
+ el.appendChild(document.createTextNode("qux?"));
+ frag.appendChild(el);
+ frag.appendChild(document.createTextNode(" fizz"));
+ textarea.appendChild(frag);
+
+ textarea.appendChild(document.createTextNode(" whee"));
+ assert_equals(textarea.defaultValue, "foo bar baz fizz whee", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo bar baz fizz whee",
+ "changing the textContent should change the raw value, and subsequently the api value");
+
+}, "defaultValue and value are affected by textContent in combination with appending a DocumentFragment");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+ textarea.appendChild(document.createTextNode("foo bar"));
+
+ const child = document.createElement("span");
+ child.textContent = "baz";
+ textarea.appendChild(child);
+
+ assert_equals(textarea.textContent, "foo barbaz", "the textContent should have *all* the text content");
+ assert_equals(textarea.defaultValue, "foo bar", "the defaultValue should reflect the child text content");
+ assert_equals(textarea.value, "foo bar",
+ "changing the child text content should change the raw value, and subsequently the api value");
+
+}, "defaultValue and value reflect child text content, not textContent");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+ textarea.appendChild(document.createTextNode("foo bar"));
+
+ const child = document.createElement("span");
+ child.textContent = "baz";
+ textarea.appendChild(child);
+
+ textarea.defaultValue = "foo";
+
+ assert_equals(textarea.childNodes.length, 1, "Only one child node should exist");
+ assert_equals(textarea.defaultValue, "foo", "the defaultValue should be the new text");
+ assert_equals(textarea.value, "foo", "the api value should be the new text");
+ assert_equals(textarea.textContent, "foo", "the textContent should be the new text");
+
+}, "Setting defaultValue wipes out any children, including elements (just like setting textContent)");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.textContent = "foo\r\nbar\rbaz\nqux";
+ assert_equals(textarea.defaultValue, "foo\r\nbar\rbaz\nqux", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo\nbar\nbaz\nqux", "The value property should normalize CRLF and CR to LF");
+
+}, "defaultValue and value treat CRLF differently");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.appendChild(document.createTextNode("foo\r"));
+ textarea.appendChild(document.createTextNode("\nbar\rbaz\nqux"));
+ assert_equals(textarea.defaultValue, "foo\r\nbar\rbaz\nqux", "the defaultValue should reflect the textContent");
+ assert_equals(textarea.value, "foo\nbar\nbaz\nqux", "The value property should normalize CRLF and CR to LF");
+
+}, "value normalizes CRLF even spread over multiple text nodes");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.textContent = "foo";
+ textarea.value = "baz";
+ assert_equals(textarea.defaultValue, "foo", "setting the value property should not affect the defaultValue");
+ assert_equals(textarea.textContent, "foo", "setting the value property should not affect the textContent");
+ assert_equals(textarea.value, "baz",
+ "on setting, the value property must set the element's raw & api value to the new value");
+
+ textarea.value = "foo\r\nbar\rbaz\nqux";
+ assert_equals(textarea.value, "foo\nbar\nbaz\nqux", "The API value should normalize CRLF and CR to LF");
+
+ textarea.value = null;
+ assert_equals(textarea.value, "", "setting the value property to null should result in an empty string");
+
+}, "tests for the value setter");
+
+test(() => {
+
+ const textarea = document.createElement("textarea");
+
+ textarea.defaultValue = "foo\0";
+ assert_equals(textarea.defaultValue, "foo\0", "defaultValue after setting defaultValue");
+ assert_equals(textarea.textContent, "foo\0", "textContent after setting defaultValue");
+ assert_equals(textarea.value, "foo\0", "value after setting defaultValue");
+
+ textarea.textContent = "bar\0";
+ assert_equals(textarea.defaultValue, "bar\0", "defaultValue after setting textContent");
+ assert_equals(textarea.textContent, "bar\0", "textContent after setting textContent");
+ assert_equals(textarea.value, "bar\0", "value after setting textContent");
+
+ textarea.value = "baz\0";
+ assert_equals(textarea.defaultValue, "bar\0", "defaultValue after setting value");
+ assert_equals(textarea.textContent, "bar\0", "textContent after setting value");
+ assert_equals(textarea.value, "baz\0", "value after setting value");
+
+}, "tests for U+0000 NULL");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive-child.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive-child.html
new file mode 100644
index 0000000000..92c9981a11
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive-child.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script>
+parent.postMessage(location.href, "*");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive.html
new file mode 100644
index 0000000000..9bb16bb681
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-enumerated-ascii-case-insensitive.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-textarea-wrap">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="textarea@wrap values are ASCII case-insensitive">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form target="child" method="GET" action="wrap-enumerated-ascii-case-insensitive-child.html">
+ <textarea name="a" wrap="hard" cols="7">hello world</textarea>
+ <textarea name="b" wrap="HaRd" cols="7">hello world</textarea>
+ <textarea name="c" wrap="soft" cols="7">hello world</textarea>
+ <textarea name="d" wrap="SoFt" cols="7">hello world</textarea>
+ <textarea name="e" wrap="Å¿oft" cols="7">hello world</textarea>
+</form>
+<iframe name="child"></iframe>
+<script>
+// #dom-textarea-wrap reflects the content attribute, but it isn’t a nullable
+// DOMString, nor is it #limited-to-only-known-values, so we can’t just take
+// the shortcut of asserting the IDL attribute like most other attributes
+async_test(function() {
+ // we use a message rather than the iframe’s load event to avoid dealing with
+ // spurious load events that some browsers dispatch on the initial about:blank
+ addEventListener("message", this.step_func_done(event => {
+ const params = new URL(event.data).searchParams;
+
+ // #textarea-wrapping-transformation says that a UA-defined algorithm wraps
+ // values by inserting CRLF pairs, so "hello \r\nworld" and "hello w\r\norld"
+ // are two of many valid outcomes for cols=7
+ assert_true(params.get("a").includes("\r\n"), "lowercase “hard†valid");
+ assert_true(params.get("b").includes("\r\n"), "mixed case “hard†valid");
+ assert_false(params.get("c").includes("\r\n"), "lowercase “soft†valid");
+
+ // vacuous: the invalid value default is currently soft, so even if the UA
+ // treats this as invalid, the observable behaviour would still be correct
+ assert_false(params.get("d").includes("\r\n"), "mixed case “soft†valid");
+
+ // vacuous: the invalid value default is currently soft, so even if the UA
+ // treats this as valid, the observable behaviour would still be correct
+ assert_false(params.get("e").includes("\r\n"), "non-ASCII “soft†invalid");
+ }));
+
+ // we submit the form in GET mode to observe the values [#concept-fe-value] of
+ // the textareas, because IDL gives us the API value [#concept-fe-api-value],
+ // which isn’t subject to hard wrapping [#textarea-wrapping-transformation]
+ document.querySelector("form").submit();
+}, "keywords");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1-ref.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1-ref.html
new file mode 100644
index 0000000000..98a7f8a3af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<title>Dynamic manipulation of textarea.wrap</title>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#dom-textarea-wrap>
+<link rel=author title=Ms2ger href=mailto:ms2ger@gmail.com>
+<textarea wrap=soft cols=20>01234567890 01234567890 01234567890</textarea>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1a.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1a.html
new file mode 100644
index 0000000000..b3baa79d7a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1a.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Dynamic manipulation of textarea.wrap</title>
+<link rel=match href=wrap-reflect-1-ref.html>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#dom-textarea-wrap>
+<link rel=author title=Ms2ger href=mailto:ms2ger@gmail.com>
+<textarea wrap=off cols=20>01234567890 01234567890 01234567890</textarea>
+<script>
+document.getElementsByTagName("textarea")[0].wrap = "soft";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1b.html b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1b.html
new file mode 100644
index 0000000000..b0a9b460f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrap-reflect-1b.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Dynamic manipulation of textarea.wrap</title>
+<link rel=match href=wrap-reflect-1-ref.html>
+<link rel=help href=https://html.spec.whatwg.org/multipage/#dom-textarea-wrap>
+<link rel=author title=Ms2ger href=mailto:ms2ger@gmail.com>
+<textarea wrap=off cols=20>01234567890 01234567890 01234567890</textarea>
+<script>
+document.getElementsByTagName("textarea")[0].setAttribute("wrap", "soft");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrapping-transformation.window.js b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrapping-transformation.window.js
new file mode 100644
index 0000000000..c5c28a4854
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-textarea-element/wrapping-transformation.window.js
@@ -0,0 +1,58 @@
+test((t) => {
+ const form = document.createElement("form");
+ const textarea = document.createElement("textarea");
+ textarea.name = "linebreakTest";
+ textarea.textContent = "a\nb\rc\r\nd\n\re";
+ form.appendChild(textarea);
+ document.body.appendChild(form);
+ t.add_cleanup(() => {
+ document.body.removeChild(form);
+ });
+
+ assert_equals(textarea.textContent, "a\nb\rc\r\nd\n\re");
+ assert_equals(textarea.value, "a\nb\nc\nd\n\ne");
+
+ const formData = new FormData(form);
+ assert_equals(
+ formData.get("linebreakTest"),
+ "a\nb\nc\nd\n\ne",
+ );
+}, "Textarea wrapping transformation: Newlines should be normalized to LF.");
+
+test((t) => {
+ const form = document.createElement("form");
+ const textarea = document.createElement("textarea");
+ textarea.name = "wrapTest";
+ textarea.cols = 10;
+ textarea.wrap = "hard";
+ textarea.textContent =
+ "Some text that is too long for the specified character width.";
+ form.appendChild(textarea);
+ document.body.appendChild(form);
+ t.add_cleanup(() => {
+ document.body.removeChild(form);
+ });
+
+ assert_true(
+ !textarea.textContent.includes("\n") &&
+ !textarea.textContent.includes("\r"),
+ "textContent shouldn't contain any newlines",
+ );
+ assert_true(
+ !textarea.textContent.includes("\n") &&
+ !textarea.textContent.includes("\r"),
+ "The API value shouldn't be line wrapped.",
+ );
+
+ const formData = new FormData(form);
+ const formDataValue = formData.get("wrapTest");
+
+ assert_true(
+ !formDataValue.includes("\r"),
+ "The wrapping done on the value must be LF, not CRLF.",
+ );
+ assert_true(
+ formDataValue.includes("\n"),
+ "The value must be wrapped.",
+ );
+}, "Textarea wrapping transformation: Wrapping happens with LF newlines.");
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/META.yml b/testing/web-platform/tests/html/semantics/grouping-content/META.yml
new file mode 100644
index 0000000000..0f3cf59653
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - domenic
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-dd-element/grouping-dd.html b/testing/web-platform/tests/html/semantics/grouping-content/the-dd-element/grouping-dd.html
new file mode 100644
index 0000000000..022e555bd2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-dd-element/grouping-dd.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the dd element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dd-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("dd");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLElement.prototype, "HTMLElement.prototype should be used for dd");
+ }, "The prototype for dd is HTMLElement.prototype");
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the dd element.</p>
+
+ <div id="log"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-div-element/grouping-div.html b/testing/web-platform/tests/html/semantics/grouping-content/the-div-element/grouping-div.html
new file mode 100644
index 0000000000..ffde6eb53f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-div-element/grouping-div.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>The div element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-div-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("div");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLDivElement.prototype, "HTMLDivElement.prototype should be used for div");
+ }, "The prototype for div is HTMLDivElement.prototype");
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the div element.</p>
+
+ <div id="log"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-dl-element/grouping-dl.html b/testing/web-platform/tests/html/semantics/grouping-content/the-dl-element/grouping-dl.html
new file mode 100644
index 0000000000..2394d6a929
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-dl-element/grouping-dl.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the dl element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dl-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("dl");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLDListElement.prototype, "HTMLDListElement.prototype should be used for dl");
+ }, "The prototype for dl is HTMLDListElement.prototype");
+
+ // Not checking: effects of markup on defining groups and the name-pair values within those groups
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the dl element.</p>
+
+ <div id="log"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-dt-element/grouping-dt.html b/testing/web-platform/tests/html/semantics/grouping-content/the-dt-element/grouping-dt.html
new file mode 100644
index 0000000000..1dbb4384dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-dt-element/grouping-dt.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the dl element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dt-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("dt");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLElement.prototype, "HTMLElement.prototype should be used for dt");
+ }, "The prototype for dt is HTMLElement.prototype");
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the dt element.</p>
+
+ <div id="log"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-figcaption-element/grouping-figcaption.html b/testing/web-platform/tests/html/semantics/grouping-content/the-figcaption-element/grouping-figcaption.html
new file mode 100644
index 0000000000..68e7a516b4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-figcaption-element/grouping-figcaption.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the figcaption element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-figcaption-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("figcaption");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLElement.prototype, "HTMLElement.prototype should be used for figcaption");
+ }, "The prototype for figcaption is HTMLElement.prototype");
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the figcaption element.</p>
+
+ <div id="log"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-figure-element/grouping-figure.html b/testing/web-platform/tests/html/semantics/grouping-content/the-figure-element/grouping-figure.html
new file mode 100644
index 0000000000..31c156ce38
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-figure-element/grouping-figure.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the figure element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-figure-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("figure");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLElement.prototype, "HTMLElement.prototype should be used for figure");
+ }, "The prototype for figure is HTMLElement.prototype");
+
+ // Not checking: Sectioning root
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the figure element.</p>
+
+ <div id="log"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-hr-element/grouping-hr.html b/testing/web-platform/tests/html/semantics/grouping-content/the-hr-element/grouping-hr.html
new file mode 100644
index 0000000000..eeadd97d44
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-hr-element/grouping-hr.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the hr element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-hr-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("hr");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLHRElement.prototype, "HTMLHRElement.prototype should be used for hr");
+ }, "The prototype for hr is HTMLHRElement.prototype");
+
+ // Not checking: "The hr element does not affect the document's outline."
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the hr element.</p>
+
+ <div id="log"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-novalue-manual.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-novalue-manual.html
new file mode 100644
index 0000000000..346ed56629
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-novalue-manual.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Body Element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-body-element">
+ <meta name="flags" content="interact" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>Validation of li characteristic requiring manual testing</h1>
+
+ <p>The spec states: "If the parent element is an ol element, then the li element has an ordinal value."</p>
+ <p>This manual test is needed to verify that NON-ol element parents do NOT result in an ordinal value.</p>
+ <p>It needs to be manual because the ordinal value assigned to each list element by the user agent is NOT available programmatically. Values which are set either via markup or IDL are available programmatically, but not the calculated values for all the other list items.</p>
+ <p>And, as we cannot be sure how a mistakenly assigned value would be rendered, this test cannot be a reftest.</p>
+ <p>So, please use the buttons to answer the following questions:</p>
+
+ <table>
+ <thead>
+ <tr>
+ <th>HTML Markup</th>
+ <th>Do you see any sort of sequencing information?</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <menu>
+ <li>Menu Item</li>
+ <li>Menu Item</li>
+ </menu>
+ </td>
+ <td>
+ <input type="button" id="btnMenuYes" value="Yes" />
+ <input type="button" id="btnMenuNo" value="No" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <menu type="toolbar">
+ <li>
+ <menu label="ToolbarLabel">
+ <li><a>Toolbar Menu Item</a></li>
+ <li><a>Toolbar Menu Item</a></li>
+ </menu>
+ </li>
+ <li>
+ <menu label="ToolbarLabel">
+ <li><a>Toolbar Menu Item</a></li>
+ <li><a>Toolbar Menu Item</a></li>
+ </menu>
+ </li>
+ </menu>
+ </td>
+ <td>
+ <input type="button" id="btnToolbarYes" value="Yes" />
+ <input type="button" id="btnToolbarNo" value="No" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <ul>
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ul>
+ </td>
+ <td>
+ <input type="button" id="btnULYes" value="Yes" />
+ <input type="button" id="btnULNo" value="No" />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div id="log"></div>
+
+ <script>
+ "use strict";
+
+ var testMenu = {}, testToolbar = {}, testUL = {};
+
+ // need to be able to wait for user to push button
+ setup(function () {
+ btnMenuYes.disabled = false;
+ btnMenuNo.disabled = false;
+ btnToolbarYes.disabled = false;
+ btnToolbarNo.disabled = false;
+ btnULYes.disabled = false;
+ btnULNo.disabled = false;
+ },
+ { explicit_timeout: true }
+ );
+
+ // register async tests
+ testMenu = async_test("Check that menu element does not result in values for list items.");
+ testToolbar = async_test("Check that toolbar type menu element does not result in values for list items.");
+ testUL = async_test("Check that unordered list element does not result in values for list items.");
+
+ // run async tests after buttons are clicked - MENU test
+ document.getElementById("btnMenuNo").onclick = testMenu.step_func(function (event) {
+ assert_true(true, "No sequencing applied to list elements inside menu.");
+ testMenu.done();
+ btnMenuYes.disabled = true;
+ btnMenuNo.disabled = true;
+ });
+ document.getElementById("btnMenuYes").onclick = testMenu.step_func(function (event) {
+ assert_true(false, "No sequencing applied to list elements inside menu.");
+ testMenu.done();
+ btnMenuYes.disabled = true;
+ btnMenuNo.disabled = true;
+ });
+
+ // run async tests after buttons are clicked -TOOLBAR test
+ document.getElementById("btnToolbarNo").onclick = testToolbar.step_func(function (event) {
+ assert_true(true, "No sequencing applied to list elements inside toolbar menu.");
+ testToolbar.done();
+ btnToolbarYes.disabled = true;
+ btnToolbarNo.disabled = true;
+ });
+ document.getElementById("btnToolbarYes").onclick = testToolbar.step_func(function (event) {
+ assert_true(false, "No sequencing applied to list elements inside toolbar menu.");
+ testToolbar.done();
+ btnToolbarYes.disabled = true;
+ btnToolbarNo.disabled = true;
+ });
+
+ // run async tests after buttons are clicked - UL test
+ document.getElementById("btnULNo").onclick = testUL.step_func(function (event) {
+ assert_true(true, "No sequencing applied to list elements inside UL.");
+ testUL.done();
+ btnULYes.disabled = true;
+ btnULNo.disabled = true;
+ });
+ document.getElementById("btnULYes").onclick = testUL.step_func(function (event) {
+ assert_true(false, "No sequencing applied to list elements inside UL.");
+ testUL.done();
+ btnULYes.disabled = true;
+ btnULNo.disabled = true;
+ });
+
+
+ </script>
+
+</body>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html
new file mode 100644
index 0000000000..23b6cdabe8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>li element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-li-element">
+ <meta name="assert" content="The value attribute has no effect when applied to a li element whose parent is a non-ol element." />
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the li element.</p>
+
+ <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent and not marked as display: list-item.</p>
+ <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically. Only explicitly-set values are then available programatically.</p>
+ <p>This reftest passes if you see NO sequencing information on any of the items below.</p>
+
+ <p>Unordered List</p>
+ <ul>
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ul>
+
+ <menu>
+ <li>Menu Item</li>
+ <li>Menu Item</li>
+ </menu>
+
+ <menu type="toolbar">
+ <li>
+ <menu label="ToolbarLabel">
+ <li><a>Toolbar Menu Item</a></li>
+ <li><a>Toolbar Menu Item</a></li>
+ </menu>
+ </li>
+ <li>
+ <menu label="ToolbarLabel">
+ <li><a>Toolbar Menu Item</a></li>
+ <li><a>Toolbar Menu Item</a></li>
+ </menu>
+ </li>
+ </menu>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html
new file mode 100644
index 0000000000..86200edadf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>li element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-li-element">
+ <link rel="match" href="grouping-li-reftest-001-ref.html" />
+ <meta name="assert" content="The value attribute has no effect when applied to a li element whose parent is a non-ol element." />
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the li element.</p>
+
+ <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent and not marked as display: list-item.</p>
+ <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically. Only explicitly-set values are then available programatically.</p>
+ <p>This reftest passes if you see NO sequencing information on any of the items below.</p>
+
+ <p>Unordered List</p>
+ <ul>
+ <li value="2">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ul>
+
+ <menu>
+ <li>Menu Item</li>
+ <li value="3">Menu Item</li>
+ </menu>
+
+ <menu type="toolbar">
+ <li value="4">
+ <menu label="ToolbarLabel">
+ <li value="5"><a>Toolbar Menu Item</a></li>
+ <li><a>Toolbar Menu Item</a></li>
+ </menu>
+ </li>
+ <li value="6">
+ <menu label="ToolbarLabel">
+ <li value="10"><a>Toolbar Menu Item</a></li>
+ <li><a>Toolbar Menu Item</a></li>
+ </menu>
+ </li>
+ </menu>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html
new file mode 100644
index 0000000000..1e453e1afb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>li element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-li-element">
+ <meta name="assert" content="li elements with ol parents have ordinal values." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span span p {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ </style>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the li element.</p>
+
+ <p>This reftest verifies that the value attribute has an effect when applied to a list item with an ol parent.</p>
+ <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically. Only explicitly-set values are then available programatically.</p>
+ <p>This reftest passes if you see the numbers 1. 2. 3. below the words "Ordered List"</p>
+
+ <span>
+ <p>Ordered List</p>
+ <span>
+ <p>1.</p>
+ <p>2.</p>
+ <p>3.</p>
+ </span>
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html
new file mode 100644
index 0000000000..3c0a464255
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>li element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-li-element">
+ <link rel="match" href="grouping-li-reftest-002-ref.html" />
+ <meta name="assert" content="li elements with ol parents have ordinal values." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span li {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ span ol {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;
+ list-style-position: inside; list-style-type: decimal; }
+ </style>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the li element.</p>
+
+ <p>This reftest verifies that the value attribute has an effect when applied to a list item with an ol parent.</p>
+ <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically. Only explicitly-set values are then available programatically.</p>
+ <p>This reftest passes if you see the numbers 1. 2. 3. below the words "Ordered List"</p>
+
+ <span>
+ <p>Ordered List</p>
+ <ol>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+ </span>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003-ref.html
new file mode 100644
index 0000000000..dbe4c05bad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: dynamic update test for LI @value</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+</head>
+<body onload="document.getElementById('item').removeAttribute('value');">
+<ol><li id="item" value="3"></li></ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003.html
new file mode 100644
index 0000000000..bad3819604
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: dynamic update test for LI @value</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element">
+ <link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-set">
+ <link rel="match" href="grouping-li-reftest-003-ref.html">
+</head>
+<body onload="document.getElementById('item').removeAttribute('value');">
+<noscript>Test not run - javascript required.</noscript>
+<ol><li id="item" value="3"></li></ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004-ref.html
new file mode 100644
index 0000000000..2d9b19e043
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: implied scope</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+ <style>
+ body { margin-left: 40px }
+ li { list-style-type: decimal }
+ </style>
+</head>
+<body>
+<li start="1"></li>
+<li start="2"></li>
+<li start="3"></li>
+<li start="4"></li>
+<div><li></li></div>
+<div><div><li></li></div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004.html
new file mode 100644
index 0000000000..2a7647fc2a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-004.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: implied scope</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element">
+ <link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-reset">
+ <link rel="match" href="grouping-li-reftest-004-ref.html">
+ <style>
+ body { margin-left: 40px }
+ li { list-style-type: decimal }
+ </style>
+</head>
+<body>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<div><li></li></div>
+<div><div><li></li></div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005-ref.html
new file mode 100644
index 0000000000..6ec02e5433
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: explicit scope</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+</head>
+<body>
+<ol>
+<li start="1"></li>
+<li start="2"></li>
+<li start="3"></li>
+<li start="4"></li>
+<div><li start="5"></li></div>
+<div><li start="6"></li></div>
+<div><div><li start="7"></li></div></div>
+</ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005.html
new file mode 100644
index 0000000000..2fa4e5dbe3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-005.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: explicit scope</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element">
+ <link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-reset">
+ <link rel="match" href="grouping-li-reftest-005-ref.html">
+</head>
+<body>
+<ol>
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<div><li></li></div>
+<div><li></li></div>
+<div><div><li></li></div></div>
+</ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-006.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-006.html
new file mode 100644
index 0000000000..d6a260bbc0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-006.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: implied scope</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element">
+ <link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-reset">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1745506">
+ <link rel="match" href="grouping-li-reftest-004-ref.html">
+ <style>
+ body { margin-left: 40px }
+ ol { margin: 0; padding: 0; }
+ li { list-style-type: decimal }
+ </style>
+</head>
+<body>
+<div><ol start=99></ol></div> <!-- this scope shouldn't affect the LIs below -->
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<div><li></li></div>
+<div><div><li></li></div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html
new file mode 100644
index 0000000000..8eda7057da
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+ <meta charset="utf-8">
+ <title>HTML LI element: implied scope</title>
+ <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element">
+ <link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-reset">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1745506">
+ <link rel="match" href="grouping-li-reftest-004-ref.html">
+ <style>
+ body { margin-left: 40px }
+ ol { margin: 0; padding: 0 }
+ li { list-style-type: decimal }
+ </style>
+</head>
+<body>
+<div><ol start=99></ol></div> <!-- this scope shouldn't affect the LIs below -->
+<ol style="counter-reset: item"> <!-- this counter shouldn't affect the LIs below -->
+<li></li>
+<li></li>
+<li></li>
+<li></li>
+<div><li></li></div>
+<div><div><li></li></div></div>
+</ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html
new file mode 100644
index 0000000000..fb142f84b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>display: list-item on non-&lt;li> elements</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+ .list-item {
+ display: list-item;
+ list-style-type: decimal;
+ }
+
+ .list-item[hidden] {
+ display: none;
+ }
+</style>
+
+<p>This test matches if both lists display similar to the following:</p>
+
+<pre>1. A
+2. B
+ D
+3. E</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <span>D</span>
+ <li value="3">E</li>
+</ol>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <span>D</span>
+ <li value="3">E</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html
new file mode 100644
index 0000000000..ce63cf7c7c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>display: list-item on non-&lt;li> elements</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-display-list-item-ref.html">
+
+<style>
+ .list-item {
+ display: list-item;
+ list-style-type: decimal;
+ }
+
+ .list-item[hidden] {
+ display: none;
+ }
+</style>
+
+<p>This test matches if both lists display similar to the following:</p>
+
+<pre>1. A
+2. B
+ D
+3. E</pre>
+
+<hr>
+
+<ul>
+ <span class="list-item">A</span>
+ <span class="list-item">B</span>
+ <span class="list-item" hidden>C</span>
+ <span>D</span>
+ <span class="list-item">E</span>
+</ul>
+
+<ol>
+ <div class="list-item">A</div>
+ <div class="list-item">B</div>
+ <div class="list-item" hidden>C</div>
+ <div>D</div>
+ <div class="list-item">E</div>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html
new file mode 100644
index 0000000000..2b1ea76656
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor menu if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="3">C</li>
+ <li value="4">D</li>
+ <li value="5">E</li>
+ <ol>
+ <li value="1">F</li>
+ <li value="2">G</li>
+ </ol>
+ <li value="6">H</li>
+ <ol>
+ <li value="1">I</li>
+ <li value="2">
+ J
+ <ol>
+ <li value="1">K</li>
+ <li value="2">L</li>
+ </ol>
+ </li>
+ </ol>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html
new file mode 100644
index 0000000000..86afa5d206
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor menu if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-menu-ref.html">
+
+<style>
+ li {
+ list-style-type: decimal;
+ }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<menu>
+ <li>A</li>
+ <li>B</li>
+ <div>
+ <li>C</li>
+ <span>
+ <li>D</li>
+ <li>E</li>
+ </span>
+ <menu>
+ <li>F</li>
+ <li>G</li>
+ </menu>
+ </div>
+ <li>H</li>
+ <menu>
+ <li>I</li>
+ <li>
+ J
+ <menu>
+ <li>K</li>
+ <li>L</li>
+ </menu>
+ </li>
+ </menu>
+</menu>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html
new file mode 100644
index 0000000000..b72768aefc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul or ul (but not dir) if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+ li {
+ list-style-type: decimal;
+ }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 3. K
+ 4. L</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="3">C</li>
+ <li value="4">D</li>
+ <li value="5">E</li>
+ <ol>
+ <li value="1">F</li>
+ <li value="2">G</li>
+ </ol>
+ <li value="6">H</li>
+ <ol>
+ <li value="1">I</li>
+ <li value="2">
+ J
+ <dir>
+ <li value="3">K</li>
+ <li value="4">L</li>
+ </dir>
+ </li>
+ </ol>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html
new file mode 100644
index 0000000000..82e39f995e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul or ul (but not dir) if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-mixed-ref.html">
+
+<style>
+ li {
+ list-style-type: decimal;
+ }
+
+ .list-item {
+ display: list-item;
+ list-style-type: decimal;
+ }
+
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 3. K
+ 4. L</pre>
+
+<hr>
+
+<ul>
+ <li>A</li>
+ <li>B</li>
+ <div>
+ <li>C</li>
+ <span>
+ <li>D</li>
+ <li>E</li>
+ </span>
+ <ol>
+ <li>F</li>
+ <span class="list-item">G</span>
+ </ol>
+ </div>
+ <li>H</li>
+ <ol>
+ <li>I</li>
+ <li>
+ J
+ <dir>
+ <li>K</li>
+ <li>L</li>
+ </dir>
+ </li>
+ </ol>
+</ul>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html
new file mode 100644
index 0000000000..fad00fa3bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The dir element is not treated specially when calculating list owners</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+ li {
+ list-style-type: decimal;
+ }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 6. F
+ 7. G
+8. H
+ 9. I
+ 10. J
+ 11. K
+ 12. L</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="3">C</li>
+ <li value="4">D</li>
+ <li value="5">E</li>
+ <dir>
+ <li value="6">F</li>
+ <li value="7">G</li>
+ </dir>
+ <li value="8">H</li>
+ <dir>
+ <li value="9">I</li>
+ <li value="10">
+ J
+ <dir>
+ <li value="11">K</li>
+ <li value="12">L</li>
+ </dir>
+ </li>
+ </dir>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html
new file mode 100644
index 0000000000..747d90738b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The dir element is not treated specially when calculating list owners</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-not-dir-ref.html">
+
+<style>
+ li {
+ list-style-type: decimal;
+ }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 6. F
+ 7. G
+8. H
+ 9. I
+ 10. J
+ 11. K
+ 12. L</pre>
+
+<hr>
+
+<ol>
+ <li>A</li>
+ <li>B</li>
+ <div>
+ <li>C</li>
+ <span>
+ <li>D</li>
+ <li>E</li>
+ </span>
+ <dir>
+ <li>F</li>
+ <li>G</li>
+ </dir>
+ </div>
+ <li>H</li>
+ <dir>
+ <li>I</li>
+ <li>
+ J
+ <dir>
+ <li>K</li>
+ <li>L</li>
+ </dir>
+ </li>
+ </dir>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html
new file mode 100644
index 0000000000..96cc9c3600
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor ol if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="3">C</li>
+ <li value="4">D</li>
+ <li value="5">E</li>
+ <ol>
+ <li value="1">F</li>
+ <li value="2">G</li>
+ </ol>
+ <li value="6">H</li>
+ <ol>
+ <li value="1">I</li>
+ <li value="2">
+ J
+ <ol>
+ <li value="1">K</li>
+ <li value="2">L</li>
+ </ol>
+ </li>
+ </ol>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html
new file mode 100644
index 0000000000..fcb93cfbb5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor ol if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-ol-ref.html">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<ol>
+ <li>A</li>
+ <li>B</li>
+ <div>
+ <li>C</li>
+ <span>
+ <li>D</li>
+ <li>E</li>
+ </span>
+ <ol>
+ <li>F</li>
+ <li>G</li>
+ </ol>
+ </div>
+ <li>H</li>
+ <ol>
+ <li>I</li>
+ <li>
+ J
+ <ol>
+ <li>K</li>
+ <li>L</li>
+ </ol>
+ </li>
+ </ol>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html
new file mode 100644
index 0000000000..03a0570ba4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be the parent if there is no ancestor ul/ol/menu</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+ li {
+ list-style-type: decimal;
+ list-style-position: inside;
+ }
+
+ ol {
+ padding: 50px;
+ margin: 0;
+ }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+1. C
+1. D
+ 1. E
+3. F</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="1">C</li>
+ <li value="1">D</li>
+ <blockquote>
+ <li value="1">E</li>
+ </blockquote>
+ <li value="3">F</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html
new file mode 100644
index 0000000000..0345add996
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be the parent if there is no ancestor ul/ol/menu</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-parent-ref.html">
+
+<style>
+ li {
+ list-style-type: decimal;
+ list-style-position: inside;
+ }
+
+ .container {
+ padding: 50px;
+ }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+1. C
+1. D
+ 1. E
+3. F</pre>
+
+<hr>
+
+<div class="container">
+ <li>A</li>
+ <li>B</li>
+ <div>
+ <li>C</li>
+ <span>
+ <li>D</li>
+ </span>
+ </div>
+ <blockquote>
+ <li>E</li>
+ </blockquote>
+ <li>F</li>
+</div>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html
new file mode 100644
index 0000000000..e758f52be9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner calculation skips elements that do not generate layout boxes</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+6. F
+7. G
+8. H
+9. I
+10. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="3">C</li>
+ <li value="4">D</li>
+ <li value="5">E</li>
+ <li value="6">F</li>
+ <li value="7">G</li>
+ <li value="8">H</li>
+ <li value="9">I</li>
+ <li value="10">
+ J
+ <ol>
+ <li value="1">K</li>
+ <li value="2">L</li>
+ </ol>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html
new file mode 100644
index 0000000000..defdcb7000
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner calculation skips elements that do not generate layout boxes</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-skip-no-boxes-ref.html">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+6. F
+7. G
+8. H
+9. I
+10. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<ol>
+ <li>A</li>
+ <li>B</li>
+ <div>
+ <li>C</li>
+ <span>
+ <li>D</li>
+ <li>E</li>
+ </span>
+ <ol style="display: contents;">
+ <li>F</li>
+ <li>G</li>
+ </ol>
+ </div>
+ <li>H</li>
+ <ol style="display: contents;">
+ <li>I</li>
+ <li>
+ J
+ <ol>
+ <li>K</li>
+ <li>L</li>
+ </ol>
+ </li>
+ </ol>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html
new file mode 100644
index 0000000000..22ee9f437f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="3">C</li>
+ <li value="4">D</li>
+ <li value="5">E</li>
+ <ol>
+ <li value="1">F</li>
+ <li value="2">G</li>
+ </ol>
+ <li value="6">H</li>
+ <ol>
+ <li value="1">I</li>
+ <li value="2">
+ J
+ <ol>
+ <li value="1">K</li>
+ <li value="2">L</li>
+ </ol>
+ </li>
+ </ol>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html
new file mode 100644
index 0000000000..8f1c7e3766
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-ul-ref.html">
+
+<style>
+ li {
+ list-style-type: decimal;
+ }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+ 1. F
+ 2. G
+6. H
+ 1. I
+ 2. J
+ 1. K
+ 2. L</pre>
+
+<hr>
+
+<ul>
+ <li>A</li>
+ <li>B</li>
+ <div>
+ <li>C</li>
+ <span>
+ <li>D</li>
+ <li>E</li>
+ </span>
+ <ul>
+ <li>F</li>
+ <li>G</li>
+ </ul>
+ </div>
+ <li>H</li>
+ <ul>
+ <li>I</li>
+ <li>
+ J
+ <ul>
+ <li>K</li>
+ <li>L</li>
+ </ul>
+ </li>
+ </ul>
+</ul>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html
new file mode 100644
index 0000000000..9a018cfaf3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>List items that are not being rendered do not participate in numbering</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. D
+4. E
+5. G</pre>
+
+<hr>
+
+<ol>
+ <li value="1">A</li>
+ <li value="2">B</li>
+ <li value="3">D</li>
+ <li value="4">E</li>
+ <li value="5">G</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html
new file mode 100644
index 0000000000..da476c4bff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>List items that are not being rendered do not participate in numbering</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-not-being-rendered-ref.html">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. D
+4. E
+5. G</pre>
+
+<hr>
+
+<ol>
+ <li>A</li>
+ <li>B</li>
+ <li hidden>C</li>
+ <li>D</li>
+ <div>
+ <li>E</li>
+ <li hidden>F</li>
+ <li>G</li>
+ </div>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li.html b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li.html
new file mode 100644
index 0000000000..fa342b3e28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li.html
@@ -0,0 +1,193 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>li element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-li-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the li element.</p>
+
+ <div id="log"></div>
+
+ <span>
+ <menu id="listmenu">
+ <li>Command</li>
+ <li value="3">Command</li>
+ </menu>
+
+ <menu type="toolbar" id="toolbarmenu">
+ <li>
+ <menu label="File">
+ <button type="button">New...</button>
+ <button type="button">Open...</button>
+ </menu>
+ </li>
+ <li value="10">
+ <menu label="Help">
+ <li value = "2"><a href="help.html">Help Me</a></li>
+ <li><a href="about.html">About</a></li>
+ </menu>
+ </li>
+ </menu>
+
+ <p>Unordered List</p>
+ <ul id="unordered">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ul>
+ </span>
+
+ <p>Ordered List</p>
+ <ol id="basic">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="start2">
+ <li value="2">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="negative">
+ <li value="-10">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="posFloatDown">
+ <li value="4.03">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="negFloatDown">
+ <li value="-4.03">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="posFloatUp">
+ <li value="4.9">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="negFloatUp">
+ <li value="-4.9">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="exponent">
+ <li value="7e2">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="decimal">
+ <li value=".5">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="letter">
+ <li value="A">list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <script>
+ "use strict";
+
+ // "The [value] attribute has no default value" so, per https://html.spec.whatwg.org/multipage/#reflect,
+ // the default when unspecified is 0
+ test(function() {
+ var testList = document.querySelectorAll("#unordered li, #basic li");
+ for (var i = 0; i < testList.length; i++) {
+ assert_equals(testList[i].value, 0, "Default (unspecified) value of value is 0.");
+ }
+ }, "Default (unspecified) value of value is 0.");
+
+ // "If the value attribute is present, user agents must parse it as an integer, in order to determine the attribute's value.
+ // If the attribute's value cannot be converted to a number, the attribute must be treated as if it was absent."
+ // Per https://html.spec.whatwg.org/multipage/#collect-a-sequence-of-characters,
+ // an integer is parsed by collecting as many digits as possible and then aborting at the first
+ // non-digit character after the first digit (otherwise, with no beginning digit, it's just an error)
+ // and: "The value IDL attribute must reflect the value of the value content attribute."
+
+ // start2's first element has value of 2
+ test(function() {
+ var testLI = document.getElementById("start2").children[0];
+ assert_equals(testLI.value, 2, "value of 2 -> value of 2");
+ }, ".value property reflects content attribute - and both parse value of '2' correctly.");
+
+ // negative's first element has value of -10
+ test(function() {
+ var testLI = document.getElementById("negative").children[0];
+ assert_equals(testLI.value, -10, "value of -10 -> value of -10");
+ }, "IDL and content attribute parse value of '-10' correctly.");
+
+ // posFloatDown's first element has value of 4.03 which should return 4
+ test(function() {
+ var testLI = document.getElementById("posFloatDown").children[0];
+ assert_equals(testLI.value, 4, "value of 4.03 -> 4");
+ }, "IDL and content attribute parse value of '4.03' correctly.");
+
+ // negFloatDown's first element has value of -4.03 which should return -4
+ test(function() {
+ var testLI = document.getElementById("negFloatDown").children[0];
+ assert_equals(testLI.value, -4, "value of -4.03 -> -4");
+ }, "IDL and content attribute parse value of '-4.03' correctly.");
+
+ // posFloatUp's first element has value of 4.9 which should return 4
+ test(function() {
+ var testLI = document.getElementById("posFloatUp").children[0];
+ assert_equals(testLI.value, 4, "value of 4.9 -> 4");
+ }, "IDL and content attribute parse value of '4.9' correctly.");
+
+ // negFloatUp's first element has value of -4.9 which should return -4
+ test(function() {
+ var testLI = document.getElementById("negFloatUp").children[0];
+ assert_equals(testLI.value, -4, "value of -4.9 -> -4");
+ }, "IDL and content attribute parse value of '-4.9' correctly.");
+
+ // exponent's first element has value of 7e2 which should return 7
+ test(function() {
+ var testLI = document.getElementById("exponent").children[0];
+ assert_equals(testLI.value, 7, "value of 7e2 -> 7");
+ }, "IDL and content attribute parse value of '7e2' correctly.");
+
+ // decimal's first element has value of .5 which should return 0
+ test(function() {
+ var testLI = document.getElementById("decimal").children[0];
+ assert_equals(testLI.value, 0, "value of .5 -> 0 (default)");
+ }, "IDL and content attribute parse value of '.5' correctly.");
+
+ // letter's first element has value of A which should return 0
+ test(function() {
+ var testLI = document.getElementById("letter").children[0];
+ assert_equals(testLI.value, 0, "value of A -> 0 (default)");
+ }, "IDL and content attribute parse value of 'A' correctly.");
+
+ // SHOULD I TEST MORE NON-ASCII-DIGIT ENTRIES?
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html
new file mode 100644
index 0000000000..8da32887f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="OL's reversed attribute creates a descending list." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span span p {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ </style>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the ol element.</p>
+
+ <p>These reftests are necessary because the values of the ol's li children as calculated by the user agent are NOT available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if you see an ascending list followed by two descending lists.</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>Ordered List</p>
+ <span>
+ <p>1.</p>
+ <p>2.</p>
+ <p>3.</p>
+ </span>
+
+ <p>Ordered List - reversed via content attribute</p>
+ <span>
+ <p>3.</p>
+ <p>2.</p>
+ <p>1.</p>
+ </span>
+
+ <p>Ordered List - reversed via IDL</p>
+ <span>
+ <p>3.</p>
+ <p>2.</p>
+ <p>1.</p>
+ </span>
+
+ </span>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html
new file mode 100644
index 0000000000..7c502e3835
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <link rel="match" href="grouping-ol-rev-reftest-001-ref.html" />
+ <meta name="assert" content="OL's reversed attribute creates a descending list." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span li {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ span ol {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;
+ list-style-position: inside; list-style-type: decimal; }
+ </style>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the ol element.</p>
+
+ <p>These reftests are necessary because the values of the ol's li children as calculated by the user agent are NOT available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if you see an ascending list followed by two descending lists.</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>Ordered List</p>
+ <ol>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>Ordered List - reversed via content attribute</p>
+ <ol reversed="reversed">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>Ordered List - reversed via IDL</p>
+ <ol id="reverse_me">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ </span>
+
+ <script>
+ document.getElementById("reverse_me").reversed = true;
+ </script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html
new file mode 100644
index 0000000000..0f651183a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="Sequences produced by calculated values for LI elements within OL match spec's expectations. (part one)" />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span span p {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>2, 3, 4 (ol has start attribute of 2)</p>
+ <span>
+ <p>2.</p>
+ <p>3.</p>
+ <p>4.</p>
+ </span>
+
+ <p>-9, -8, -7 (ol has start attribute of -9)</p>
+ <span>
+ <p>-9.</p>
+ <p>-8.</p>
+ <p>-7.</p>
+ </span>
+
+ <p>1000, 1001, 1002 (list's start attribute of 1000 provided by JavaScript)</p>
+ <span>
+ <p>1000.</p>
+ <p>1001.</p>
+ <p>1002.</p>
+ </span>
+
+ <p>2, 1, 9 (each list item has a specified value attribute, list has a start attribute of 1000)</p>
+ <span>
+ <p>2.</p>
+ <p>1.</p>
+ <p>9.</p>
+ </span>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html
new file mode 100644
index 0000000000..b6de3dbb4b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <link rel="match" href="grouping-ol-start-reftest-001-ref.html" />
+ <meta name="assert" content="Sequences produced by calculated values for LI elements within OL match spec's expectations (part one)." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span li {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ span ol {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;
+ list-style-position: inside; list-style-type: decimal; }
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>2, 3, 4 (ol has start attribute of 2)</p>
+ <ol start="2">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>-9, -8, -7 (ol has start attribute of -9)</p>
+ <ol start="-9">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>1000, 1001, 1002 (list's start attribute of 1000 provided by JavaScript)</p>
+ <ol id="start_me">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>2, 1, 9 (each list item has a specified value attribute, list has a start attribute of 1000)</p>
+ <ol istart="1000">
+ <li value="2"></li>
+ <li value="1"></li>
+ <li value="9"></li>
+ </ol>
+
+ </span>
+
+ <script>
+ document.getElementById("start_me").start = 1000;
+ </script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html
new file mode 100644
index 0000000000..2dbdf4aa74
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="Sequences produced by calculated values for LI elements within OL match spec's expectations. (part two)" />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span span p {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>4, 5, 6 (ol has start attribute of 2 which is overridden by first list item's value attribute of 4)</p>
+ <span>
+ <p>4.</p>
+ <p>5.</p>
+ <p>6.</p>
+ </span>
+
+ <p>4, 5, 6 (ol has start attribute of -10 which is overridden by first list item's value attribute of 4)</p>
+ <span>
+ <p>4.</p>
+ <p>5.</p>
+ <p>6.</p>
+ </span>
+
+ <p>1, 5, 6 (2nd list item has value attribute of 5)</p>
+ <span>
+ <p>1.</p>
+ <p>5.</p>
+ <p>6.</p>
+ </span>
+
+ <p>-1, -5, -4 (list has a start attribute of -1, and 2nd list item has value attribute of -5)</p>
+ <span>
+ <p>-1.</p>
+ <p>-5.</p>
+ <p>-4.</p>
+ </span>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html
new file mode 100644
index 0000000000..9b21c79496
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <link rel="match" href="grouping-ol-start-reftest-002-ref.html" />
+ <meta name="assert" content="Sequences produced by calculated values for LI elements within OL match spec's expectations. (part two)" />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span li {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ span ol {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;
+ list-style-position: inside; list-style-type: decimal; }
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>4, 5, 6 (ol has start attribute of 2 which is overridden by first list item's value attribute of 4)</p>
+ <ol start="2">
+ <li value="4"></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>4, 5, 6 (ol has start attribute of -10 which is overridden by first list item's value attribute of 4)</p>
+ <ol start="-10">
+ <li value="4"></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>1, 5, 6 (2nd list item has value attribute of 5)</p>
+ <ol>
+ <li></li>
+ <li value="5"></li>
+ <li></li>
+ </ol>
+
+ <p>-1, -5, -4 (list has a start attribute of -1, and 2nd list item has value attribute of -5)</p>
+ <ol start="-1">
+ <li></li>
+ <li value="-5"></li>
+ <li></li>
+ </ol>
+
+ </span>
+
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html
new file mode 100644
index 0000000000..391859efc6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="OL's type attribute defaults to decimal state." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span span p {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+ <p>1, 2, 3 (default value for unspecified type attribute is 'decimal')</p>
+ <span>
+ <p>1.</p>
+ <p>2.</p>
+ <p>3.</p>
+ </span>
+
+ <p>1, 2, 3 (default value for type attribute not listed in spec table is 'decimal' (type = "!"))</p>
+ <span>
+ <p>1.</p>
+ <p>2.</p>
+ <p>3.</p>
+ </span>
+
+ <p>1, 2, 3 (default value for type attribute not listed in spec table is 'decimal' (type = "2"))</p>
+ <span>
+ <p>1.</p>
+ <p>2.</p>
+ <p>3.</p>
+ </span>
+
+ <p>1, 2, 3 (default value for type attribute not listed in spec table is 'decimal' (type = "b"))</p>
+ <span>
+ <p>1.</p>
+ <p>2.</p>
+ <p>3.</p>
+ </span>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html
new file mode 100644
index 0000000000..b7dc22d779
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <link rel="match" href="grouping-ol-type-reftest-001-ref.html" />
+ <meta name="assert" content="OL's type attribute defaults to decimal state." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span li {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ span ol {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace; list-style-position: inside; }
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>1, 2, 3 (default value for unspecified type attribute is 'decimal')</p>
+ <ol>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>1, 2, 3 (default value for type attribute not listed in spec table is 'decimal' (type = "!"))</p>
+ <ol type="!">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>1, 2, 3 (default value for type attribute not listed in spec table is 'decimal' (type = "2"))</p>
+ <ol type="2">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>1, 2, 3 (default value for type attribute not listed in spec table is 'decimal' (type = "b"))</p>
+ <ol type="b">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html
new file mode 100644
index 0000000000..8d8012e8ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="List items are rendered consistently with the state of the type attribute." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span span p {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+ <p>1, 2, 3 (type attribute of "1" results in decimal type)</p>
+ <span>
+ <p>1.</p>
+ <p>2.</p>
+ <p>3.</p>
+ </span>
+
+ <p>aa, ab, ac (type attribute of "a" results in lower case latin alphabet, start = 27)</p>
+ <span>
+ <p>aa.</p>
+ <p>ab.</p>
+ <p>ac.</p>
+ </span>
+
+ <p>AA, AB, AC (type attribute of "A" results in upper case latin alphabet, start = 27)</p>
+ <span>
+ <p>AA.</p>
+ <p>AB.</p>
+ <p>AC.</p>
+ </span>
+
+ <p>i, v, c (type attribute of "i" results in lower case roman alphabet, list values = 1, 5, 100)</p>
+ <span>
+ <p>i.</p>
+ <p>v.</p>
+ <p>c.</p>
+ </span>
+
+ <p>I, V, C (type attribute of "I" results in upper case roman alphabet, list values = 1, 5, 100)</p>
+ <span>
+ <p>I.</p>
+ <p>V.</p>
+ <p>C.</p>
+ </span>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html
new file mode 100644
index 0000000000..db55fd4bc4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <link rel="match" href="grouping-ol-type-reftest-002-ref.html" />
+ <meta name="assert" content="List items are rendered consistently with the state of the type attribute." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span li {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ span ol {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace; list-style-position: inside;}
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>1, 2, 3 (type attribute of "1" results in decimal type)</p>
+ <ol type="1">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>aa, ab, ac (type attribute of "a" results in lower case latin alphabet, start = 27)</p>
+ <ol type="a" start="27">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>AA, AB, AC (type attribute of "A" results in upper case latin alphabet, start = 27)</p>
+ <ol type="A" start="27">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>i, v, c (type attribute of "i" results in lower case roman alphabet, list values = 1, 5, 100)</p>
+ <ol type="i">
+ <li value="1"></li>
+ <li value="5"></li>
+ <li value="100"></li>
+ </ol>
+
+ <p>I, V, C (type attribute of "I" results in upper case roman alphabet, list values = 1, 5, 100)</p>
+ <ol type="I">
+ <li value="1"></li>
+ <li value="5"></li>
+ <li value="100"></li>
+ </ol>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html
new file mode 100644
index 0000000000..6c836cf367
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="OL's type attribute defaults to decimal state." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span span p {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+ <p>-3, -2, -1 (type is "a", start is -3)</p>
+ <span>
+ <p>-3.</p>
+ <p>-2.</p>
+ <p>-1.</p>
+ </span>
+
+ <p>0, a (type is "a", start is 0)</p>
+ <span>
+ <p>0.</p>
+ <p>a.</p>
+ </span>
+
+ <p>-3, -2, -1 (type is "A", start is -3)</p>
+ <span>
+ <p>-3.</p>
+ <p>-2.</p>
+ <p>-1.</p>
+ </span>
+
+ <p>0, A (type is "A", start is 0)</p>
+ <span>
+ <p>0.</p>
+ <p>A.</p>
+ </span>
+
+ <p>-3, -2, -1 (type is "i", start is -3)</p>
+ <span>
+ <p>-3.</p>
+ <p>-2.</p>
+ <p>-1.</p>
+ </span>
+
+ <p>0, i (type is "i", start is 0)</p>
+ <span>
+ <p>0.</p>
+ <p>i.</p>
+ </span>
+
+ <p>-3, -2, -1 (type is "I", start is -3)</p>
+ <span>
+ <p>-3.</p>
+ <p>-2.</p>
+ <p>-1.</p>
+ </span>
+
+ <p>0, I (type is "I", start is 0)</p>
+ <span>
+ <p>0.</p>
+ <p>I.</p>
+ </span>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html
new file mode 100644
index 0000000000..654287298f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <link rel="match" href="grouping-ol-type-reftest-003-ref.html" />
+ <meta name="assert" content="OL's type attribute defaults to decimal state." />
+ <style type="text/css">
+ span p {display:list-item; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0;}
+ span li {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; padding-top: 0; padding-bottom: 0; font-family: monospace;}
+ span ol {margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 5em; padding-top: 0; padding-bottom: 0; font-family: monospace; list-style-position: inside;}
+ </style>
+</head>
+<body>
+ <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
+
+ <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
+ <p>(Note: each list item has no content; only the sequencing should appear.)</p>
+
+ <span>
+
+ <p>-3, -2, -1 (type is "a", start is -3)</p>
+ <ol type="a" start="-3">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>0, a (type is "a", start is 0)</p>
+ <ol type="a" start="0">
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>-3, -2, -1 (type is "A", start is -3)</p>
+ <ol type="A" start="-3">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>0, A (type is "A", start is 0)</p>
+ <ol type="A" start="0">
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>-3, -2, -1 (type is "i", start is -3)</p>
+ <ol type="i" start="-3">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>0, i (type is "i", start is 0)</p>
+ <ol type="i" start="0">
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>-3, -2, -1 (type is "I", start is -3)</p>
+ <ol type="I" start="-3">
+ <li></li>
+ <li></li>
+ <li></li>
+ </ol>
+
+ <p>0, I (type is "I", start is 0)</p>
+ <ol type="I" start="0">
+ <li></li>
+ <li></li>
+ </ol>
+
+ </span>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol.html
new file mode 100644
index 0000000000..80fa734a92
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/grouping-ol.html
@@ -0,0 +1,299 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the ol element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the ol element.</p>
+
+ <div id="log"></div>
+
+ <p>Ordered List</p>
+ <ol id="basic">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="allAtts" reversed start="3" type="A">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="justRev" reversed>
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="basicRevGoodName" reversed="reversed">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="basicRevNameWithSpace" reversed=" reversed ">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="basicRevEmpty" reversed="" start="A">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="basicRevTrue" reversed="true">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="basicRevFalse" reversed="false">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="start2" start="2">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="negative" start="-10">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="posFloatDown" start="4.03">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="negFloatDown" start="-4.03">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="posFloatUp" start="4.9">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="negFloatUp" start="-4.9">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="exponent" start="7e2">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="decimal" start=".5">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="letter" start="A">
+ <li>list item</li>
+ <li>list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="middle50">
+ <li>list item</li>
+ <li value="50">list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="middleneg50">
+ <li>list item</li>
+ <li value="-50">list item</li>
+ <li>list item</li>
+ </ol>
+
+ <p>Ordered List</p>
+ <ol id="lots" reversed="reversed">
+ <li value="10">list item</li>
+ <li value="20">list item</li>
+<a></a><abbr></abbr><address></address><area></area><article></article><aside></aside><audio></audio><b></b><base></base><bdi></bdi><bdo></bdo><blockquote></blockquote><body></body><br></br><button></button><canvas></canvas><caption></caption><cite></cite><code></code><col></col><colgroup></colgroup><command></command><datalist></datalist><dd></dd><del></del><details></details><dfn></dfn><dialog></dialog><div></div><dl></dl><dt></dt><em></em><embed></embed><fieldset></fieldset><figcaption></figcaption><figure></figure><footer></footer><form></form><h1></h1><h2></h2><h3></h3><h4></h4><h5></h5><h6></h6><head></head><header></header><hgroup></hgroup><hr></hr><html></html><i></i><iframe></iframe><img></img><input></input><ins></ins><kbd></kbd><keygen></keygen><label></label><legend></legend><link></link><map></map><mark></mark><menu></menu><meta></meta><meter></meter><nav></nav><noscript></noscript><object></object><ol><li></li><li></li></ol><optgroup></optgroup><option></option><output></output><p></p><param></param><pre></pre><progress></progress><q></q><rp></rp><rt></rt><ruby></ruby><s></s><samp></samp><script></script><section></section><select></select><small></small><source></source><span></span><strong></strong><style></style><sub></sub><summary></summary><sup></sup><table></table><tbody></tbody><td></td><textarea></textarea><tfoot></tfoot><th></th><thead></thead><time></time><title></title><tr></tr><track></track><u></u><ul><li></li><li></li></ul><var></var><video></video><wbr></wbr>
+ <li value="-99">list item</li>
+ </ol>
+
+ <script>
+ "use strict";
+
+ var testList;
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ testList = document.getElementById("basic");
+ assert_equals(Object.getPrototypeOf(testList), HTMLOListElement.prototype, "HTMLOListElement.prototype should be used for OL");
+ }, "The prototype for OL is HTMLOListElement.prototype");
+
+ // check that "own" properties reversed, start, and type are present
+ test(function () {
+ testList = document.getElementById("basic");
+ assert_idl_attribute(testList, "reversed");
+ }, "'reversed' property should be defined on OL.");
+
+ test(function () {
+ testList = document.getElementById("basic");
+ assert_idl_attribute(testList, "start");
+ }, "'start' property should be defined on OL.");
+
+ test(function () {
+ testList = document.getElementById("basic");
+ assert_idl_attribute(testList, "type");
+ }, "'type' property should be defined on OL.");
+
+ // "The reversed, start, and type IDL attributes must reflect the respective content attributes of the same name."
+ test(function () {
+ testList = document.getElementById("allAtts");
+ assert_true(testList.reversed);
+ }, "OL's 'reversed' IDL property reflects content attribute.");
+
+ test(function () {
+ testList = document.getElementById("allAtts");
+ assert_equals(testList.start, 3);
+ }, "OL's 'start' IDL property reflects content attribute.");
+
+ test(function () {
+ testList = document.getElementById("allAtts");
+ assert_equals(testList.type, "A");
+ }, "OL's 'type' IDL property reflects content attribute.");
+
+
+ // "The reversed attribute is a boolean attribute."
+
+ // check lists for which reversed value should be false
+ test(function() {
+ assert_false(document.getElementById("basic").reversed, "IDL 'reversed' attribute value false when content attribute absent");
+ }, "IDL 'reversed' attribute value false when content attribute absent");
+
+ // check lists for which reversed value should be true
+ test(function() {
+ assert_true(document.getElementById("justRev").reversed);
+ assert_true(document.getElementById("basicRevGoodName").reversed);
+ assert_true(document.getElementById("basicRevNameWithSpace").reversed);
+ assert_true(document.getElementById("basicRevEmpty").reversed);
+ assert_true(document.getElementById("basicRevTrue").reversed);
+ assert_true(document.getElementById("basicRevFalse").reversed);
+ }, "IDL 'reversed' attribute value true when content attribute exists");
+
+ // check that IDL property works to change reversed value
+ test(function() {
+ document.getElementById("justRev").reversed = false;
+ assert_false(document.getElementById("justRev").reversed, "Changing IDL 'reversed' property changes list's reversed property.");
+ }, "Changing IDL 'reversed' property changes list's reversed property.");
+
+
+ // If the start attribute is present, user agents must parse it as an integer, in order to determine the attribute's value.
+ // The default value, used if the attribute is missing or
+ // if the value cannot be converted to a number according to the referenced algorithm,
+ // is 1 if the element has no reversed attribute, and
+ // is the number of child li elements otherwise."
+ // "The start IDL attribute has the same default as its content attribute."
+
+ test(function() {
+ assert_equals(document.getElementById("basic").start, 1);
+ }, "Default start value for non-reversed list should be 1");
+
+ test(function() {
+ assert_equals(document.getElementById("decimal").start, 1);
+ }, "IDL and content attribute parse start of '.5' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("letter").start, 1);
+ }, "IDL and content attribute parse start of 'A' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("basicRevGoodName").start, 1);
+ }, "Default start value (if none provided) for reversed list = 1.");
+
+ test(function() {
+ assert_equals(document.getElementById("basicRevEmpty").start, 1);
+ }, "Default start value (if failed to parse) for reversed list = 1.");
+
+ test(function() {
+ assert_equals(document.getElementById("lots").start, 1);
+ }, "Default start value for reversed list = 1 (even with tons of other child elements).");
+
+ test(function() {
+ var myList = document.getElementById("basicRevGoodName"), myLI = document.createElement("li");
+ myList.appendChild(myLI);
+ assert_equals(document.getElementById("basicRevGoodName").start, 1);
+ }, "Adding child element to reversed list does not change start value");
+
+ test(function() {
+ var myList = document.getElementById("basicRevTrue");
+ myList.removeChild(myList.children[0]);
+ assert_equals(document.getElementById("basicRevTrue").start, 1);
+ }, "Deleting child element from reversed list does not change start value");
+
+ test(function() {
+ assert_equals(document.getElementById("start2").start, 2);
+ }, "IDL and content attribute parse start of '2' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("negative").start, -10);
+ }, "IDL and content attribute parse start of '-10' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("posFloatDown").start, 4);
+ }, "IDL and content attribute parse start of '4.03' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("negFloatDown").start, -4);
+ }, "IDL and content attribute parse start of '-4.03' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("posFloatUp").start, 4);
+ }, "IDL and content attribute parse start of '4.9' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("negFloatUp").start, -4);
+ }, "IDL and content attribute parse start of '-4.9' correctly.");
+
+ test(function() {
+ assert_equals(document.getElementById("exponent").start, 7);
+ }, "IDL and content attribute parse start of '7e2' correctly.");
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-1.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-1.html
new file mode 100644
index 0000000000..7f2a00c703
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>ol.start - reflection test</title>
+ <link rel="author" title="Shiki Okasaka" href="http://shiki.esrille.com/">
+ <link rel="author" title="Esrille Inc." href="http://www.esrille.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="This test checks that the start IDL attribute reflects the respective content attribute of the same name.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol id="ol">
+ <li>One</li>
+ <li>Two</li>
+ <li>Three</li>
+ </ol>
+ <div id="log"></div>
+ <script>
+test(function() {
+ assert_equals(document.getElementById('ol').start, 1);
+})
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-2.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-2.html
new file mode 100644
index 0000000000..c7c9aeab48
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/ol.start-reflection-2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>ol.start - reflection test</title>
+ <link rel="author" title="Shiki Okasaka" href="http://shiki.esrille.com/">
+ <link rel="author" title="Esrille Inc." href="http://www.esrille.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ol-element">
+ <meta name="assert" content="This test checks that the start IDL attribute reflects the respective content attribute of the same name.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <ol id='ol' reversed>
+ <li>Three</li>
+ <li>Two</li>
+ <li>One</li>
+ </ol>
+ <div id='log'></div>
+ <script>
+test(function() {
+ assert_equals(document.getElementById('ol').start, 1);
+})
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1-ref.html
new file mode 100644
index 0000000000..f8cac3c702
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<ol>
+ <li value="3">Three</li>
+ <li value="2">Two</li>
+ <li value="1">One</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1a.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1a.html
new file mode 100644
index 0000000000..202315b1c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1a.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>`reversed` should reverse the numbering correctly</title>
+<link rel=match href="reversed-1-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/#attr-ol-reversed">
+<ol reversed>
+ <li>Three</li>
+ <li>Two</li>
+ <li>One</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1b.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1b.html
new file mode 100644
index 0000000000..4d6202943d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Dynamically setting `reversed` should update the numbering</title>
+<link rel=match href="reversed-1-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/#attr-ol-reversed">
+<ol id="x">
+ <li>Three</li>
+ <li>Two</li>
+ <li>One</li>
+</ol>
+<script>
+ var l = document.getElementById("x");
+ var w = l.offsetWidth;
+ l.setAttribute("reversed", "");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1c.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1c.html
new file mode 100644
index 0000000000..6fad13053f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1c.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Reversed numbering should update on dynamic addition of child nodes</title>
+<link rel=match href="reversed-1-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/#attr-ol-reversed">
+<ol id="x" reversed>
+ <li>Three</li>
+ <li>Two</li>
+</ol>
+<script>
+ var l = document.getElementById("x");
+ var w = l.offsetWidth;
+ var li = document.createElement("li");
+ li.textContent = "One"
+ l.appendChild(li);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1d.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1d.html
new file mode 100644
index 0000000000..a256b6a428
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1d.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Reverse numbering should not be affected by nested div</title>
+<link rel=match href="reversed-1-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/#attr-ol-reversed">
+<ol reversed>
+ <li>Three</li>
+ <div>
+ <li>Two</li>
+ <li>One</li>
+ </div>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1e.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1e.html
new file mode 100644
index 0000000000..48a2799942
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-1e.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Reverse numbering should not count display:none elements</title>
+<link rel=match href="reversed-1-ref.html">
+<link rel=help href="https://html.spec.whatwg.org/#attr-ol-reversed">
+<ol reversed>
+ <li>Three</li>
+ <li style="display:none">Three</li>
+ <li>Two</li>
+ <li>One</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2-ref.html
new file mode 100644
index 0000000000..4f3ece2be4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<ol>
+ <li value="5">Five</li>
+ <li value="4">Four</li>
+ <li value="3">Three</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2.html
new file mode 100644
index 0000000000..0d4948153c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ol-element/reversed-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<link rel="match" href="reversed-2-ref.html">
+<ol reversed start="5">
+ <li>Five</li>
+ <li>Four</li>
+ <li>Three</li>
+</ol>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-p-element/grouping-p.html b/testing/web-platform/tests/html/semantics/grouping-content/the-p-element/grouping-p.html
new file mode 100644
index 0000000000..5f15aca31f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-p-element/grouping-p.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the p element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-p-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("p");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLParagraphElement.prototype, "HTMLParagraphElement.prototype should be used for p");
+ }, "The prototype for p is HTMLParagraphElement.prototype");
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the p element.</p>
+
+ <div id="log"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001-ref.html
new file mode 100644
index 0000000000..75aa91b302
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>pre element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-pre-element">
+ <meta name="assert" content="Newlines within pre elements separate paragraphs for the purposes of BIDI." />
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the pre element.</p>
+
+ <p>The spec states:</p>
+ <blockquote>"A newline in a pre element should separate paragraphs for the purposes of the Unicode bidirectional algorithm. This requirement may be implemented indirectly through the style layer. For example, an HTML+CSS user agent could implement these requirements by implementing the CSS 'unicode-bidi' property."</blockquote>
+
+ <p>This reftest passes if below you see "ABC ABC" repeated on two separate lines below (4 ABCs total):</p>
+
+ <pre>ABC ABC
+ABC ABC</pre>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001.html b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001.html
new file mode 100644
index 0000000000..29e582edd5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre-reftest-001.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>pre element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-pre-element">
+ <link rel="match" href="grouping-pre-reftest-001-ref.html" />
+ <meta name="assert" content="Newlines within pre elements separate paragraphs for the purposes of BIDI." />
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test continues to validate the pre element.</p>
+
+ <p>The spec states:</p>
+ <blockquote>"A newline in a pre element should separate paragraphs for the purposes of the Unicode bidirectional algorithm. This requirement may be implemented indirectly through the style layer. For example, an HTML+CSS user agent could implement these requirements by implementing the CSS 'unicode-bidi' property."</blockquote>
+
+ <p>This reftest passes if below you see "ABC ABC" repeated on two separate lines below (4 ABCs total):</p>
+
+ <pre>&#x202E CBA CBA
+ABC ABC</pre>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre.html b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre.html
new file mode 100644
index 0000000000..07fc631b91
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/grouping-pre.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the pre element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-pre-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the pre element.</p>
+
+ <div id="log"></div>
+
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("pre");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLPreElement.prototype, "HTMLPreElement.prototype should be used for pre");
+ }, "The prototype for pre is HTMLPreElement.prototype");
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi-ref.html b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi-ref.html
new file mode 100644
index 0000000000..0f302b5d4b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML Test reference: newline in &lt;pre&gt; separates bidi paragraphs</title>
+ <link rel="author" title="Amir E. Aharoni" href="mailto:amir.aharoni@mail.huji.ac.il"/>
+ <link rel="author" title="Eyal Sela" href="mailto:eyal@post.isoc.org.il"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-pre-element"/>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the rightmost character in the first line below is a full stop and to the left of it is a Hebrew letter.</p></div>
+ <div class="test">
+ <pre>
+A Hebrew letter and a full stop: &#x05d0;.&lrm;
+&#x05d0; this line begins with a Hebrew letter.
+ </pre>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi.html b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi.html
new file mode 100644
index 0000000000..23d442f52c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-pre-element/pre-newline-bidi.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: newline in pre separates bidi paragraphs</title>
+ <link rel="match" href="pre-newline-bidi-ref.html" />
+ <link rel="author" title="Amir E. Aharoni" href="mailto:amir.aharoni@mail.huji.ac.il"/>
+ <link rel="author" title="Eyal Sela" href="mailto:eyal@post.isoc.org.il"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-pre-element"/>
+ <meta name="assert"
+ content="A newline in a pre element should separate paragraphs for the purposes of the Unicode bidirectional algorithm."/>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the rightmost character in the first line below is a full stop and to the left of it is a Hebrew letter.</p></div>
+ <div class="test">
+ <pre>
+A Hebrew letter and a full stop: &#x05d0;.
+&#x05d0; this line begins with a Hebrew letter.
+ </pre>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/grouping-content/the-ul-element/grouping-ul.html b/testing/web-platform/tests/html/semantics/grouping-content/the-ul-element/grouping-ul.html
new file mode 100644
index 0000000000..6e62343f6b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-ul-element/grouping-ul.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>the ul element</title>
+ <link rel="author" title="dzenana" href="mailto:dzenana.trenutak@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-ul-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ "use strict";
+
+ // check that prototype matches spec's DOM interface
+ test(function () {
+ var testElement = document.createElement("ul");
+ assert_equals(Object.getPrototypeOf(testElement), HTMLUListElement.prototype, "HTMLUListElement.prototype should be used for ul");
+ }, "The prototype for ul is HTMLUListElement.prototype");
+
+ </script>
+</head>
+<body>
+ <h1>Description</h1>
+ <p>This test validates the ul element.</p>
+
+ <div id="log"></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/META.yml b/testing/web-platform/tests/html/semantics/interactive-elements/META.yml
new file mode 100644
index 0000000000..c1dd8dddf9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - foolip
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js b/testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js
new file mode 100644
index 0000000000..f08761be8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js
@@ -0,0 +1,36 @@
+setup({explicit_done: true, explicit_timeout: true});
+
+const NOTRUN = 3;
+let status = NOTRUN;
+function notrun() {
+ return status === NOTRUN;
+}
+add_completion_callback(tests => {
+ status = tests[0].status;
+});
+
+function pass() {
+ // Wait a couple of frames in case fail() is also called.
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ if (notrun()) {
+ test(() => {});
+ done();
+ }
+ });
+ });
+}
+
+function fail(msg) {
+ if (notrun()) {
+ test(() => { assert_unreached(msg); });
+ done();
+ }
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ const accessKeyElement = document.querySelector('[accesskey]');
+ if (accessKeyElement.accessKeyLabel) {
+ document.querySelector('kbd').textContent = accessKeyElement.accessKeyLabel;
+ }
+});
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html
new file mode 100644
index 0000000000..521b4bb975
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>First input after the legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>legend</legend>
+ <input onfocus="pass()">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html
new file mode 100644
index 0000000000..1c40cc7b81
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>First input before the legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <input onfocus="pass()">
+ <legend accesskey=a>legend
+ <input onfocus="fail('input in legend was focused')">
+ </legend>
+ <input onfocus="fail('input after legend was focused')">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html
new file mode 100644
index 0000000000..abd3a3b2df
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>First input inside the legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>legend
+ <input onfocus="pass()">
+ </legend>
+ <input onfocus="fail('input after legend was focused')">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html
new file mode 100644
index 0000000000..e2880a77bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Focusable legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend tabindex=0 onclick="fail('unexpected click event on legend')"
+ onfocus="fail('legend was focused')" accesskey=a>
+ legend
+ <input onfocus="pass()">
+ </legend>
+ <input onfocus="fail('input after legend was focused')">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html
new file mode 100644
index 0000000000..49dcaaf7d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Focusable legend sibling</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>first legend</legend>
+ <legend tabindex=0 onfocus="fail('sibling legend was focused')">second legend</legend>
+</fieldset>
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html
new file mode 100644
index 0000000000..dc6af48323
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Input outside fieldset</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>legend</legend>
+</fieldset>
+<input onfocus="fail('input outside fieldset was focused')">
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html
new file mode 100644
index 0000000000..8a7b20565f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Label sibling</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<input id=x onfocus="fail('input associated with the label was focused')">
+<fieldset>
+ <legend accesskey=a>legend</legend>
+ <label for=x onclick="fail('label received a click event')">label</label>
+</fieldset>
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html
new file mode 100644
index 0000000000..e7abb71454
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>No fieldset parent</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<legend accesskey=a>
+ legend
+ <input onfocus="fail('input in legend was focused')">
+</legend>
+<input onfocus="fail('input after legend was focused')">
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html b/testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html
new file mode 100644
index 0000000000..f723d3a92a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>menu element removed properties</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-menu-element">
+<link rel="help" href="https://github.com/whatwg/html/pull/2742">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<menu type="context" label="label">
+ <menuitem>Text</menuitem>
+ <menuitem type="checkbox" checked>Checked</menuitem>
+ <menuitem disabled>Disabled</menuitem>
+ <menuitem default>Default</menuitem>
+</menu>
+
+<script>
+"use strict";
+
+const menu = document.querySelector("menu");
+const menuitem = document.querySelector("menuitem");
+
+test(() => {
+ assert_false("HTMLMenuItemElement" in window, "the HTMLMenuItemElement interface must not exist");
+ assert_equals(menuitem.constructor, HTMLUnknownElement, "A <menuitem> must be HTMLUnknownElement");
+
+ for (const prop of ["type", "label", "icon", "disabled", "checked", "radiogroup", "default"]) {
+ assert_false(prop in menuitem, `menuitem.${prop} must not be present`);
+ }
+}, "HTMLMenuItemElement must not be not present");
+
+test(() => {
+ const potentialBadLocations = [
+ window,
+ document,
+ HTMLElement.prototype,
+ SVGElement.prototype,
+ Document.prototype,
+ HTMLDocument.prototype,
+ Element.prototype
+ ];
+ for (const location of potentialBadLocations) {
+ assert_false("onshow" in location,
+ `${location.constructor.name} must not have a property "onshow"`);
+ }
+}, `onshow must not be present on the GlobalEventHandlers locations`);
+
+test(() => {
+ assert_false("RelatedEvent" in window);
+}, "RelatedEvent must not be present");
+
+test(() => {
+ assert_false("contextMenu" in HTMLElement.prototype,
+ "HTMLElement's prototype must not have a property \"contextMenu\"");
+ assert_false("contextMenu" in document.createElement("div"),
+ "A div must not have a property \"contextMenu\"");
+}, "el.contextMenu must not be present");
+
+test(() => {
+ assert_false("type" in menu);
+
+ menu.type = "toolbar";
+ assert_equals(menu.getAttribute("type"), "context");
+}, "menu.type must not exist or reflect the content attribute");
+
+test(() => {
+ assert_false("label" in menu);
+
+ menu.label = "new label";
+ assert_equals(menu.getAttribute("label"), "label");
+}, "menu.label must not exist or reflect the content attribute");
+
+test(() => {
+ assert_array_equals(document.querySelectorAll("menuitem:enabled"), []);
+}, ":enabled must not match menuitems");
+
+test(() => {
+ assert_array_equals(document.querySelectorAll("menuitem:disabled"), []);
+}, ":disabled must not match menuitems");
+
+test(() => {
+ assert_array_equals(document.querySelectorAll("menuitem:checked"), []);
+}, ":checked must not match menuitems");
+
+test(() => {
+ try {
+ assert_array_equals(document.querySelectorAll("menuitem:default"), []);
+ } catch (e) {
+ // Not everyone has implemented :default as of the time of this writing.
+ if (e.name !== "SyntaxError") {
+ throw e;
+ }
+ }
+}, ":default must not match menuitems");
+
+test(() => {
+ assert_equals(getComputedStyle(menu).display, "block");
+}, "The user-agent stylesheet must leave type=\"context\" menus as block display like other menus");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html
new file mode 100644
index 0000000000..0ecd30dda3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html
@@ -0,0 +1,28 @@
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1255406">
+<script type="text/javascript">
+var nodes = Array();
+var text = Array();
+ {
+ nodes[9] = document.createElement('textarea');
+ nodes[11] = document.createElement('legend');
+ nodes[44] = document.createElement('details');
+ document.documentElement.appendChild(nodes[44]);
+ nodes[68] = document.createElement('fieldset');
+ nodes[81] = document.createElement('option');
+
+
+
+ nodes[85] = document.createElement('img');
+ text[42] = document.createTextNode('744879385');
+ nodes[44].appendChild(text[42]);
+ nodes[68].appendChild(nodes[11]);
+ nodes[44].appendChild(nodes[68]);
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ try { nodes[85].appendChild(nodes[68]); } catch(e) {}
+ });
+ });
+ nodes[44].appendChild(nodes[9]);
+ requestAnimationFrame(() => { document.execCommand("SelectAll", false, ""); });
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html
new file mode 100644
index 0000000000..d3d04f07a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://github.com/whatwg/html/pull/6466">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="height:2000px">spacer</div>
+
+<details id=details>
+ <div id=target>target</div>
+</details>
+
+<script>
+async_test(t => {
+ assert_false(details.hasAttribute('open'),
+ `The <details> should be closed at the start of the test.`);
+ assert_equals(window.pageYOffset, 0,
+ `The page should be scrolled to the top at the start of the test.`);
+
+ window.location.hash = '#target';
+
+ requestAnimationFrame(t.step_func_done(() => {
+ assert_true(details.hasAttribute('open'),
+ `<details> should be opened by navigating to an element inside it.`);
+ assert_not_equals(window.pageYOffset, 0,
+ `The page should be scrolled down to the <details> element.`);
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html
new file mode 100644
index 0000000000..d24b4634cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1264704">
+
+<script>
+function runTest() {
+ details2.appendChild(child);
+ document.caretRangeFromPoint();
+}
+</script>
+
+<body onload=runTest()>
+
+<details style="writing-mode: vertical-rl">
+ <div id=child>foo</div>
+</details>
+
+<details id=details2 open=true ontoggle="window.find()">
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html
new file mode 100644
index 0000000000..1936cdb67d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/6466">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<details id=details>
+ <div style="width:100px; height:100px; background-color:red" id=innerdiv></div>
+</details>
+
+<script>
+test(() => {
+ assert_not_equals(innerdiv.getBoundingClientRect().x, 0, 'x before open');
+ assert_not_equals(innerdiv.getBoundingClientRect().y, 0, 'y before open');
+ assert_not_equals(innerdiv.getBoundingClientRect().width, 0, 'width before open');
+ assert_not_equals(innerdiv.getBoundingClientRect().height, 0, 'height before open');
+ details.open = true;
+ assert_not_equals(innerdiv.getBoundingClientRect().x, 0, 'x after open');
+ assert_not_equals(innerdiv.getBoundingClientRect().y, 0, 'y after open');
+ assert_not_equals(innerdiv.getBoundingClientRect().width, 0, 'width after open');
+ assert_not_equals(innerdiv.getBoundingClientRect().height, 0, 'height after open');
+ details.open = false;
+ assert_not_equals(innerdiv.getBoundingClientRect().x, 0, 'x after close');
+ assert_not_equals(innerdiv.getBoundingClientRect().y, 0, 'y after close');
+ assert_not_equals(innerdiv.getBoundingClientRect().width, 0, 'width after close');
+ assert_not_equals(innerdiv.getBoundingClientRect().height, 0, 'height after close');
+}, `Verifies the layout results of elements inside a closed <details> based on the usage of content-visibility:hidden.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html
new file mode 100644
index 0000000000..14f2be232f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<details>
+ <summary>new summary</summary>
+ details
+</details>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html
new file mode 100644
index 0000000000..1b0062e43a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel=match href="details-add-summary-ref.html">
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/6466">
+
+<!-- This test makes sure that new <summary> elements get rendered correctly
+ when added to a <details> element. I ran into it when adding
+ content-visibility:hidden to the second slot of <details>. -->
+
+<script>
+onload = () => {
+ const newsummary = document.createElement('summary');
+ newsummary.textContent = 'new summary';
+ document.getElementById('detailsid').insertBefore(newsummary,
+ document.getElementById('oldsummary'));
+
+ document.documentElement.classList.remove('reftest-wait');
+};
+</script>
+
+<details id=detailsid>
+ <summary id=oldsummary>old summary</summary>
+ details
+</details>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html
new file mode 100644
index 0000000000..393e464c4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1334983">
+
+<canvas>
+ <details>
+ <card card>
+
+<script>
+async function trigger() {
+ document.querySelector("canvas").style.setProperty("container-type", "size");
+ document.querySelector("canvas").style.setProperty("column-span", "all");
+ document.querySelector("card").setAttribute("contenteditable", "true");
+}
+onload = requestAnimationFrame(() => requestAnimationFrame(trigger));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html
new file mode 100644
index 0000000000..dc8686b216
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1264507">
+
+<script>
+window.onload = () => {
+ window.getSelection().selectAllChildren(document.body);
+ document.querySelector('object').remove();
+ document.execCommand('FindString',false,0);
+};
+</script>
+
+<details>
+ <object id='id6'></object>
+</details>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html
new file mode 100644
index 0000000000..a5534e24d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Details activation with space bar</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1726454">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+:root {
+ scroll-behavior: instant;
+}
+.spacer {
+ height: 200vh;
+}
+</style>
+<details>
+ <summary>Activate me with the <kbd>Space</kbd> key</summary>
+ <p>Summary</p>
+</details>
+<div class="spacer"></div>
+<script>
+function tick() {
+ return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+}
+
+promise_test(async t => {
+ const details = document.querySelector("details");
+ const summary = details.querySelector("summary");
+
+ summary.focus();
+ assert_equals(document.activeElement, summary, "Summary should be focusable");
+ assert_false(details.open, "Details should be closed");
+
+ const oldScrollY = window.scrollY;
+ assert_equals(oldScrollY, 0, "Should be at top");
+
+ window.addEventListener("scroll", t.unreached_func("Unexpected scroll event"));
+
+ await test_driver.send_keys(summary, " ");
+
+ assert_true(details.open, "Space bar on summary should open details");
+ assert_equals(window.scrollY, oldScrollY, "Scroll position shouldn't change");
+
+ await tick();
+
+ assert_equals(window.scrollY, oldScrollY, "Scroll position shouldn't change");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html
new file mode 100644
index 0000000000..5ed14c53af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML details element API</title>
+ <style>#one, #two { visibility: hidden; }</style>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+
+ <!-- Used in parsing tests -->
+ <div id='one'><details></details><details></details></div>
+ <div id='two'><p><details></details></div>
+
+ <script type="text/javascript">
+
+function makeDetails () {
+ return document.createElement('details');
+}
+
+
+// <details>
+test(function () {
+ var times = document.getElementById('one').getElementsByTagName('details');
+ assert_equals( times.length, 2 );
+}, 'HTML parsing should locate 2 details elements in this document');
+
+test(function () {
+ assert_equals( document.getElementById('two').getElementsByTagName('p')[0].innerHTML, '' );
+}, 'HTML parsing should close an unclosed <p> before <details>');
+
+test(function () {
+ assert_true( !!window.HTMLDetailsElement );
+}, 'HTMLDetailsElement should be exposed for prototyping');
+
+test(function () {
+ assert_true( makeDetails() instanceof window.HTMLDetailsElement);
+}, 'a dynamically created details element should be instanceof HTMLDetailsElement');
+
+test(function () {
+ assert_true( document.getElementById('one').getElementsByTagName('details')[0] instanceof window.HTMLDetailsElement);
+}, 'a details element from the parser should be instanceof HTMLDetailsElement');
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html
new file mode 100644
index 0000000000..80812cccb5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=969619">
+<details open="open" style="display:table;"><rt></rt></details>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> { }, "No crash");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html
new file mode 100644
index 0000000000..35ddca1fa6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html
@@ -0,0 +1,31 @@
+<!doctype HTML>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://crbug.com/1276488">
+
+<style>
+.first + .second {}
+</style>
+
+<script>
+const details = document.createElement('details');
+const ol = document.createElement('ol');
+const text = document.createTextNode('abcdefghijklmnopqrstuvxyz');
+
+function step3() {
+ ol.setAttribute('class', 'first');
+}
+
+function step2() {
+ details.appendChild(text);
+ requestAnimationFrame(step3);
+}
+
+function runTest() {
+ document.documentElement.appendChild(details);
+ details.appendChild(ol);
+
+ requestAnimationFrame(step2);
+}
+
+onload = runTest;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/name-attribute.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/name-attribute.html
new file mode 100644
index 0000000000..2685546e9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/name-attribute.html
@@ -0,0 +1,469 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>Test for the name attribute creating exclusive accordions from details elements</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-details-element">
+<link rel="help" href="https://open-ui.org/components/accordion.explainer">
+<link rel="help" href="https://github.com/openui/open-ui/issues/725">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1444057">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="container">
+</div>
+
+<script>
+
+function assert_element_states(elements, expectations, description) {
+ assert_array_equals(elements.map(e => Number(e.open)), expectations, description);
+}
+
+let container = document.getElementById("container");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a">
+ <summary>1</summary>
+ This is the first item.
+ </details>
+
+ <details name="a">
+ <summary>2</summary>
+ This is the second item.
+ </details>
+ `;
+ let first = container.firstElementChild;
+ let second = first.nextElementSibling;
+ assert_false(first.open);
+ assert_false(second.open);
+ first.open = true;
+ assert_true(first.open);
+ assert_false(second.open);
+ second.open = true;
+ assert_false(first.open);
+ assert_true(second.open);
+ second.open = true;
+ assert_false(first.open);
+ assert_true(second.open);
+ second.open = false;
+ assert_false(first.open);
+ assert_false(second.open);
+}, "basic handling of mutually exclusive details");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" open>
+ <summary>1</summary>
+ This is the first item.
+ </details>
+
+ <details name="a">
+ <summary>2</summary>
+ This is the second item.
+ </details>
+
+ <details name="a" open>
+ <summary>3</summary>
+ This is the third item.
+ </details>
+ `;
+ let first = container.firstElementChild;
+ let second = first.nextElementSibling;
+ let third = second.nextElementSibling;
+ function assert_states(expected_first, expected_second, expected_third, description) {
+ assert_array_equals([first.open, second.open, third.open], [expected_first, expected_second, expected_third], description);
+ }
+
+ assert_states(true, false, false, "initial states from open attribute");
+ first.open = true;
+ assert_states(true, false, false, "non-mutation doesn't change state");
+ second.open = true;
+ assert_states(false, true, false, "mutation closes multiple open elements");
+ third.setAttribute("open", "");
+ assert_states(false, false, true, "setAttribute closes other open element");
+}, "more complex handling of mutually exclusive details");
+
+promise_test(async t => {
+ let details_elements_string = `
+ <details name="a"></details>
+ <details name="a" open></details>
+ <details name="b"></details>
+ <details name="b"></details>
+ `;
+ container.innerHTML = `
+ ${details_elements_string}
+ <div id="shadow_host"></div>
+ `;
+ let shadow_root = document.getElementById("shadow_host").attachShadow({ mode: "open" });
+ shadow_root.innerHTML = details_elements_string;
+ let elements = Array.from(container.querySelectorAll("details")).concat(Array.from(shadow_root.querySelectorAll("details")));
+
+ assert_element_states(elements, [0, 1, 0, 0, 0, 1, 0, 0], "initial states from open attribute");
+ elements[4].open = true;
+ assert_element_states(elements, [0, 1, 0, 0, 1, 0, 0, 0], "after mutation in shadow tree");
+ for (let i = 0; i < 8; ++i) {
+ elements[i].open = true;
+ }
+ assert_element_states(elements, [0, 1, 0, 1, 0, 1, 0, 1], "after setting all elements open");
+ elements[0].open = true;
+ assert_element_states(elements, [1, 0, 0, 1, 0, 1, 0, 1], "after final mutation");
+}, "mutually exclusive details across multiple names and multiple tree scopes");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ <details name="a" id="e3" open></details>
+ `;
+ let e2 = document.createElement("details");
+ e2.id = "e2";
+ e2.name = "a";
+ e2.open = true;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ e2,
+ document.getElementById("e3") ];
+ container.insertBefore(e2, elements[3]);
+
+ let mutation_event_received_ids = [];
+ let mutation_listener = event => {
+ assert_equals(event.type, "DOMSubtreeModified");
+ assert_equals(event.target.nodeType, Node.ELEMENT_NODE);
+ let element = event.target;
+ assert_equals(element.localName, "details");
+ mutation_event_received_ids.push(element.id);
+ };
+ let toggle_event_received_ids = [];
+ let toggle_event_promises = [];
+ for (let element of elements) {
+ element.addEventListener("DOMSubtreeModified", mutation_listener);
+ toggle_event_promises.push(new Promise((resolve, reject) => {
+ element.addEventListener("toggle", event => {
+ assert_equals(event.type, "toggle");
+ assert_equals(event.target, element);
+ toggle_event_received_ids.push(element.id);
+ resolve(undefined);
+ });
+ }));
+ }
+ assert_array_equals(mutation_event_received_ids, []);
+ assert_element_states(elements, [1, 0, 0, 0], "states before mutation");
+ elements[1].open = true;
+ if (mutation_event_received_ids.length == 0) {
+ // ok if mutation events are not supported
+ } else {
+ assert_array_equals(mutation_event_received_ids, ["e1"],
+ "mutation events received only for open attribute mutation and not for closing other element");
+ }
+ assert_element_states(elements, [0, 1, 0, 0], "states after mutation");
+ assert_array_equals(toggle_event_received_ids, [], "toggle events received before awaiting promises");
+ await Promise.all(toggle_event_promises);
+ assert_array_equals(toggle_event_received_ids, ["e3", "e2", "e1", "e0"], "toggle events received after awaiting promises, including toggle events from parser insertion");
+}, "mutation event and toggle event order");
+
+// This function is used to guard tests that test behavior that is
+// relevant only because of Mutation Events. If mutation events (for
+// attribute addition/removal) are removed from the web, the tests using
+// this function can be removed.
+function mutation_events_for_attribute_removal_supported() {
+ if (!("MutationEvent" in window)) {
+ return false;
+ }
+ container.innerHTML = `<div id="event-removal-test"></div>`;
+ let element = container.firstChild;
+ let event_fired = false;
+ element.addEventListener("DOMSubtreeModified", event => event_fired = true);
+ element.removeAttribute("id");
+ return event_fired;
+}
+
+promise_test(async t => {
+ if (!mutation_events_for_attribute_removal_supported()) {
+ return;
+ }
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ <details name="a" id="e2" open></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ document.getElementById("e2") ];
+
+ let received_ids = [];
+ let listener = event => {
+ received_ids.push(event.target.id);
+ let i = 0;
+ for (let element of elements) {
+ element.setAttribute("name", `b${i++}`);
+ }
+ };
+ for (let element of elements) {
+ element.addEventListener("DOMSubtreeModified", listener);
+ }
+ assert_array_equals(received_ids, []);
+ assert_element_states(elements, [1, 0, 0], "states before mutation");
+ elements[1].open = true;
+ assert_array_equals(received_ids, ["e1"],
+ "mutation events received only for open attribute mutation and not for closing other element");
+ assert_element_states(elements, [0, 1, 0], "states after mutation");
+}, "interaction of open attribute changes with mutation events");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details></details>
+ <details></details>
+ <details name></details>
+ <details name></details>
+ <details name=""></details>
+ <details name=""></details>
+ `;
+ let elements = Array.from(container.querySelectorAll("details"));
+
+ assert_element_states(elements, [0, 0, 0, 0, 0, 0], "initial states from open attribute");
+ for (let i = 0; i < 6; ++i) {
+ elements[i].open = true;
+ }
+ assert_element_states(elements, [1, 1, 1, 1, 1, 1], "after setting all elements open");
+}, "empty and missing name attributes do not create groups");
+
+const connected_scenarios = {
+ "connected": {
+ "create": data => container,
+ "cleanup": data => {},
+ },
+ "disconnected": {
+ "create": data => document.createElement("div"),
+ "cleanup": data => {},
+ },
+ "shadow": {
+ "create": data => {
+ let e = document.createElement("div");
+ container.appendChild(e);
+ data.wrapper = e;
+ let shadowRoot = e.attachShadow({ mode: "open" });
+ let d = document.createElement("div");
+ shadowRoot.appendChild(d);
+ return d;
+ },
+ "cleanup": data => { data.wrapper.remove(); },
+ },
+ "shadow-in-disconnected": {
+ "create": data => {
+ let e = document.createElement("div");
+ let shadowRoot = e.attachShadow({ mode: "open" });
+ let d = document.createElement("div");
+ shadowRoot.appendChild(d);
+ return d;
+ },
+ "cleanup": data => {},
+ },
+ "template-in-disconnected": {
+ "create": data => {
+ let e = document.createElement("div");
+ e.innerHTML = `
+ <template>
+ <div></div>
+ </template>
+ `;
+ return e.firstElementChild.content.firstElementChild;
+ },
+ "cleanup": data => {},
+ },
+ "connected-in-xhr-response": {
+ "create": data => new Promise((resolve, reject) => {
+ let xhr = new XMLHttpRequest();
+ xhr.open("GET", "support/empty-html-document.html");
+ xhr.responseType = "document";
+ xhr.send();
+ xhr.addEventListener("load", event => { resolve(xhr.response.body); });
+ let reject_with_type =
+ event => { reject(`${event.type} event received`); }
+ xhr.addEventListener("error", reject_with_type);
+ xhr.addEventListener("abort", reject_with_type);
+ }),
+ "cleanup": data => {},
+ },
+ "connected-in-implementation-create-document": {
+ "create": data => {
+ let doc = document.implementation.createHTMLDocument("impl-created");
+ return doc.body;
+ },
+ "cleanup": data => {},
+ },
+ "connected-in-template": {
+ "create": data => {
+ container.innerHTML = `
+ <template>
+ <div></div>
+ </template>
+ `;
+ return container.firstElementChild.content.firstElementChild;
+ },
+ "cleanup": data => { container.innerHTML = ""; },
+ },
+};
+
+for (const [scenario, scenario_callbacks] of Object.entries(connected_scenarios)) {
+ promise_test(async t => {
+ let data = {};
+ let container = await scenario_callbacks.create(data);
+ t.add_cleanup(async () => await scenario_callbacks.cleanup(data));
+ assert_true(container instanceof HTMLDivElement ||
+ container instanceof HTMLBodyElement,
+ "error in test setup");
+
+ container.innerHTML = `
+ <details name="scenariotest" open></details>
+ <details name="scenariotest"></details>
+ `;
+
+ let elements = Array.from(container.querySelectorAll("details[name='scenariotest']"));
+ assert_element_states(elements, [1, 0], "state before toggle");
+ elements[1].open = true;
+ assert_element_states(elements, [0, 1], "state after toggle enforces exclusivity");
+ }, `exclusivity enforcement with attachment scenario ${scenario}`);
+}
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ <details name="b" id="e2" open></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ document.getElementById("e2") ];
+
+ let mutation_received_ids = [];
+ let listener = event => {
+ mutation_received_ids.push(event.target.id);
+ };
+ for (let element of elements) {
+ element.addEventListener("DOMSubtreeModified", listener);
+ }
+
+ assert_element_states(elements, [1, 0, 1], "states before first mutation");
+ assert_array_equals(mutation_received_ids, [], "mutation events received before first mutation");
+ elements[2].name = "a";
+ assert_element_states(elements, [1, 0, 0], "states after first mutation");
+ if (mutation_received_ids.length != 0) {
+ // OK to not support mutation events, or to send DOMSubtreeModified
+ // only for attribute addition/removal (open) but not for attribute
+ // change (name)
+ assert_array_equals(mutation_received_ids, ["e2"], "mutation events received after first mutation");
+ }
+ elements[0].name = "c";
+ elements[2].open = true;
+ assert_element_states(elements, [1, 0, 1], "states before second mutation");
+ if (mutation_received_ids.length != 0) { // OK to not support mutation events
+ if (mutation_received_ids.length == 1) {
+ // OK to receive DOMSubtreeModified for attribute addition/removal
+ // (open) but not for attribute change (name)
+ assert_array_equals(mutation_received_ids, ["e2"], "mutation events received before second mutation");
+ } else {
+ assert_array_equals(mutation_received_ids, ["e2", "e0", "e2"], "mutation events received before second mutation");
+ }
+ }
+ elements[0].name = "a";
+ assert_element_states(elements, [0, 0, 1], "states after second mutation");
+ if (mutation_received_ids.length != 0) { // OK to not support mutation events
+ if (mutation_received_ids.length == 1) {
+ // OK to receive DOMSubtreeModified for attribute addition/removal
+ // (open) but not for attribute change (name)
+ assert_array_equals(mutation_received_ids, ["e2"], "mutation events received before second mutation");
+ } else {
+ assert_array_equals(mutation_received_ids, ["e2", "e0", "e2", "e0"], "mutation events received after second mutation");
+ }
+ }
+}, "handling of name attribute changes");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1" open></details>
+ <details open name="a" id="e2"></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ document.getElementById("e2") ];
+
+ assert_element_states(elements, [1, 0, 0], "states after insertion by parser");
+}, "closing as a result of parsing doesn't depend on attribute order");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1") ];
+
+ assert_element_states(elements, [1, 0], "states before first mutation");
+
+ let make_details = () => {
+ let e = document.createElement("details");
+ e.setAttribute("name", "a");
+ return e;
+ };
+
+ let watch_e0 = new EventWatcher(t, elements[0], ['toggle']);
+ let watch_e1 = new EventWatcher(t, elements[1], ['toggle']);
+
+ let expect_opening = async (watcher) => {
+ await watcher.wait_for(['toggle'], {record: 'all'}).then((events) => {
+ assert_equals(events[0].oldState, "closed");
+ assert_equals(events[0].newState, "open");
+ });
+ };
+
+ let expect_closing = async (watcher) => {
+ await watcher.wait_for(['toggle'], {record: 'all'}).then((events) => {
+ assert_equals(events[0].oldState, "open");
+ assert_equals(events[0].newState, "closed");
+ });
+ };
+
+ let track_mutations = (element) => {
+ let result = { count: 0 };
+ let listener = event => {
+ ++result.count;
+ };
+ element.addEventListener("DOMSubtreeModified", listener);
+ return result;
+ }
+
+ await expect_opening(watch_e0);
+
+ // Test appending an open element in the group.
+ let new1 = make_details();
+ let mutations1 = track_mutations(new1);
+ let watch_new1 = new EventWatcher(t, new1, ['toggle']);
+ new1.open = true;
+ assert_in_array(mutations1.count, [0, 1], "mutation events count before inserting new1");
+ await expect_opening(watch_new1);
+ container.appendChild(new1);
+ await expect_closing(watch_new1);
+ assert_in_array(mutations1.count, [0, 1], "mutation events count after inserting new1");
+
+ // Test appending a closed element in the group.
+ let new2 = make_details();
+ let mutations2 = track_mutations(new2);
+ let watch_new2 = new EventWatcher(t, new2, ['toggle']);
+ container.appendChild(new2);
+ assert_equals(mutations2.count, 0, "mutation events count after inserting new2");
+
+ // Test inserting an open element at the start of the group.
+ let new3 = make_details();
+ let mutations3 = track_mutations(new3);
+ new3.open = true; // this time do this before creating the EventWatcher
+ let watch_new3 = new EventWatcher(t, new3, ['toggle']);
+ assert_in_array(mutations3.count, [0, 1], "mutation events count before inserting new3");
+ await expect_opening(watch_new3);
+ container.insertBefore(new3, elements[0]);
+ await expect_closing(watch_new3);
+ assert_in_array(mutations3.count, [0, 1], "mutation events count after inserting new3");
+}, "handling of insertion of elements into group");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html
new file mode 100644
index 0000000000..f3e821a950
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html
@@ -0,0 +1,26 @@
+<!doctype HTML>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://crbug.com/1270206">
+
+<script type="text/javascript">
+function eventHandler1() {
+ document.getElementById('target').insertAdjacentText("afterEnd", "");
+ document.getElementById('target').focus();
+ document.getElementById('target').hidden = "true";
+}
+function operate() {
+ document.addEventListener('DOMNodeInsertedIntoDocument', eventHandler1, true);
+}
+function exec_event() {
+ event = new Event('DOMNodeInsertedIntoDocument')
+ document.dispatchEvent(event)
+}
+function go(){
+ operate();
+ exec_event();
+}
+</script>
+<body onload="go();" contentEditable="true">
+<details onselectstart='eventHandler2();'>
+<dfn id='target' class=onload='eventHandler1();'>
+<details id= onmsgesturehold='eventHandler2();'>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html
new file mode 100644
index 0000000000..c8d8ae4ed7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html
@@ -0,0 +1,17 @@
+<!doctype HTML>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://crbug.com/1273395">
+
+<dialog id="parentElement">
+ <details id="childElement" open="true" ontoggle="toggleHandler()">
+ <dialog id="grandchildElement">
+ </dialog>
+ </details>
+</dialog>
+<script>
+function toggleHandler() {
+ grandchildElement.showModal();
+ parentElement.showModal();
+ childElement.open = false;
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/support/empty-html-document.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/support/empty-html-document.html
new file mode 100644
index 0000000000..56415b8476
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/support/empty-html-document.html
@@ -0,0 +1,2 @@
+<!DOCTYPE HTML>
+<title>Empty HTML Document</title>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html
new file mode 100644
index 0000000000..c918f8eb62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html
@@ -0,0 +1,183 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>The details element</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-details-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<details id=details1>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details2>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details3 style="display:none;">
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details4>
+</details>
+<details id=details6>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details7>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details8>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<script>
+ window.details9TogglePromise = new Promise(resolve => {
+ window.details9TogglePromiseResolver = resolve;
+ });
+</script>
+<details id=details9 ontoggle="window.details9TogglePromiseResolver()" open>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details10>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<script>
+ var t1 = async_test("Adding open to 'details' should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'"),
+ t2 = async_test("Adding open to 'details' and then removing open from that 'details' should fire only one toggle event at the 'details' element, with 'oldState: closed' and 'newState: closed'"),
+ t3 = async_test("Adding open to 'details' (display:none) should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'"),
+ t4 = async_test("Adding open to 'details' (no children) should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'"),
+ t6 = async_test("Adding open to 'details' and then removing open from that 'details' and then again adding open to that 'details' should fire only one toggle event at the 'details' element, with 'oldState: closed' and 'newState: closed'"),
+ t7 = async_test("Adding open to 'details' using setAttribute('open', '') should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'"),
+ t8 = async_test("Adding open to 'details' and then calling removeAttribute('open') should fire only one toggle event at the 'details' element, with 'oldState: closed' and 'newState: closed'"),
+ t9 = async_test("Setting open=true on an opened 'details' element should not fire a toggle event at the 'details' element"),
+ t10 = async_test("Setting open=false on a closed 'details' element should not fire a toggle event at the 'details' element"),
+
+ details1 = document.getElementById('details1'),
+ details2 = document.getElementById('details2'),
+ details3 = document.getElementById('details3'),
+ details4 = document.getElementById('details4'),
+ details6 = document.getElementById('details6'),
+ details7 = document.getElementById('details7'),
+ details8 = document.getElementById('details8'),
+ details9 = document.getElementById('details9'),
+ details10 = document.getElementById('details10'),
+ loop=false;
+
+ function testEvent(evt) {
+ assert_true(evt.isTrusted, "event is trusted");
+ assert_false(evt.bubbles, "event doesn't bubble");
+ assert_false(evt.cancelable, "event is not cancelable");
+ assert_equals(Object.getPrototypeOf(evt), ToggleEvent.prototype, "Prototype of toggle event is ToggleEvent.prototype");
+ }
+
+ details1.ontoggle = t1.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details1.open);
+ testEvent(evt)
+ });
+ details1.open = true; // opens details1
+
+ details2.ontoggle = t2.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "closed");
+ assert_false(details2.open);
+ testEvent(evt);
+ });
+ details2.open = true;
+ details2.open = false; // closes details2
+
+ details3.ontoggle = t3.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details3.open);
+ testEvent(evt);
+ });
+ details3.open = true; // opens details3
+
+ details4.ontoggle = t4.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details4.open);
+ testEvent(evt);
+ });
+ details4.open = true; // opens details4
+
+ async_test(function(t) {
+ var details5 = document.createElement("details");
+ details5.ontoggle = t.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details5.open);
+ testEvent(evt);
+ })
+ details5.open = true;
+ }, "Adding open to 'details' (not in the document) should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'");
+
+ details6.open = true;
+ details6.open = false;
+ details6.ontoggle = t6.step_func(function(evt) {
+ if (loop) {
+ assert_unreached("toggle event fired twice");
+ } else {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "closed");
+ loop = true;
+ }
+ });
+ t6.step_timeout(function() {
+ assert_true(loop);
+ t6.done();
+ }, 0);
+
+ details7.ontoggle = t7.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details7.open);
+ testEvent(evt)
+ });
+ details7.setAttribute('open', ''); // opens details7
+
+ details8.ontoggle = t8.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "closed");
+ assert_false(details8.open);
+ testEvent(evt)
+ });
+ details8.open = true;
+ details8.removeAttribute('open'); // closes details8
+
+ window.details9TogglePromise.then(t9.step_func(() => {
+ // The toggle event should be fired once when declaring details9 with open
+ // attribute.
+ details9.ontoggle = t9.step_func(() => {
+ assert_unreached("toggle event fired twice on opened details element");
+ });
+ // setting open=true on details9 should not fire another event since it is
+ // already open.
+ details9.open = true;
+ t9.step_timeout(() => {
+ assert_true(details9.open);
+ t9.done();
+ });
+ }));
+
+ details10.ontoggle = t10.step_func_done(function(evt) {
+ assert_unreached("toggle event fired on closed details element");
+ });
+ details10.open = false; // closes details10
+ t10.step_timeout(function() {
+ assert_false(details10.open);
+ t10.done();
+ }, 0);
+
+ async_test(function(t) {
+ new DOMParser().parseFromString("<details open>", "text/html").querySelector("details").ontoggle = t.step_func_done(function(e) {
+ assert_true(e.target.open);
+ });
+ }, "Setting open from the parser fires a toggle event");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml
new file mode 100644
index 0000000000..f270236cff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: dialog
+ files: "**"
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html
new file mode 100644
index 0000000000..77ed29ce56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="viewport" content="user-scalable=no">
+<title>Tests layout of absolutely positioned modal dialogs.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+/* Remove body margin and dialog styles for easier positioning expected values */
+body {
+ height: 10000px;
+ margin: 0;
+}
+
+dialog {
+ border: 0;
+ padding: 0;
+ max-width: 100%;
+ max-height: 100%;
+}
+
+#absolute-div {
+ position: absolute;
+ top: 800px;
+ height: 50px;
+ width: 90%;
+}
+
+#relative-div {
+ position: relative;
+ top: 20px;
+ height: 30px;
+}
+</style>
+</head>
+<dialog >It is my dialog.</dialog>
+<div id="absolute-div">
+ <div id="relative-div"></div>
+</div>
+<script>
+"use strict";
+
+function checkVerticallyCentered(dialog) {
+ var centeredTop = (document.documentElement.clientHeight - dialog.offsetHeight) / 2;
+ // Using approx equals because getBoundingClientRect() and centeredTop
+ // are calculated by different parts of the engine. Due to the loss
+ // of precision, the numbers might not equal exactly.
+ assert_approx_equals(dialog.getBoundingClientRect().top, centeredTop, 1);
+}
+
+function reset() {
+ document.body.style.width = "auto";
+ dialog.style.top = null;
+ dialog.style.height = null;
+ if (dialog.open)
+ dialog.close();
+ dialog.remove();
+ document.body.appendChild(dialog);
+ window.scroll(0, 500);
+}
+
+var dialog = document.querySelector('dialog');
+var absoluteContainer = document.querySelector('#absolute-div');
+var relativeContainer = document.querySelector('#relative-div');
+reset();
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "showModal() should center in the viewport");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ dialog.close();
+ window.scroll(0, 2 * window.scrollY);
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "Dialog should be recentered if showModal() is called after close()");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ var expectedTop = dialog.getBoundingClientRect().top;
+ window.scroll(0, window.scrollY * 2);
+
+ // Trigger relayout
+ document.body.offsetHeight;
+
+ window.scroll(0, window.scrollY / 2);
+ assert_equals(dialog.getBoundingClientRect().top, expectedTop);
+}, "Dialog should not recenter on relayout.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.style.height = '20000px';
+ dialog.showModal();
+ assert_equals(dialog.getBoundingClientRect().top, 0);
+}, "A tall dialog should be positioned at the top of the viewport.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ document.body.style.width = '4000px';
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+
+}, "The dialog should be centered regardless of the presence of a horizontal scrollbar.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.remove();
+ absoluteContainer.appendChild(dialog);
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+ dialog.close();
+
+ dialog.remove();
+ relativeContainer.appendChild(dialog);
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "Centering should work when dialog is inside positioned containers.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ var expectedTop = dialog.getBoundingClientRect().top;
+ relativeContainer.style.display = 'none';
+ relativeContainer.style.display = 'block';
+ assert_equals(dialog.getBoundingClientRect().top, expectedTop);
+}, "A centered dialog's position should survive becoming display:none temporarily.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ // Remove and reinsert so that the document position isn't changed by the second remove and reinsert
+ dialog.remove();
+ relativeContainer.appendChild(dialog);
+
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+ dialog.remove();
+
+ relativeContainer.appendChild(dialog);
+ assert_equals(relativeContainer.getBoundingClientRect().top, dialog.getBoundingClientRect().top);
+}, "Dialog should not still be centered when removed, and re-added to the document.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ dialog.style.top = '0px';
+ var expectedTop = dialog.getBoundingClientRect().top;
+ dialog.close();
+ dialog.showModal();
+ assert_equals(dialog.getBoundingClientRect().top, expectedTop);
+}, "Dialog's specified position should survive after close() and showModal().");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ dialog.removeAttribute('open');
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "Dialog should be recentered if showModal() is called after removing 'open'.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html
new file mode 100644
index 0000000000..70d84c21ff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.backdrop {
+ position: absolute;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+</head>
+<body>
+Test ::backdrop used in descendant selectors. The test passes if there are two green boxes and no red.
+<div class="backdrop" style="top: 100px; left: 100px"></div>
+<div class="backdrop" style="top: 100px; left: 300px"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html
new file mode 100644
index 0000000000..73706548ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="backdrop-descendant-selector-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ visibility: hidden;
+}
+
+::backdrop {
+ visibility: visible;
+ position: absolute;
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+
+/* This shouldn't be matched, dialog is not the parent of ::backdrop.
+ * It is given high specificity so we actually test something.
+ */
+#dialog-parent > #dialog > ::backdrop,
+#dialog-parent > #dialog ::backdrop {
+ background: red;
+}
+
+#dialog-parent > ::backdrop {
+ top: 100px;
+ left: 100px;
+ background: green;
+}
+
+#backdrop-ancestor ::backdrop {
+ top: 100px;
+ left: 300px;
+ background: green;
+}
+</style>
+</head>
+<body>
+Test ::backdrop used in descendant selectors. The test passes if there are two green boxes and no red.
+
+<div id="dialog-parent">
+ <dialog id="dialog"></dialog>
+</div>
+<div id="backdrop-ancestor">
+ <p><span><dialog></dialog></span></p>
+</div>
+<script>
+var dialogs = document.querySelectorAll('dialog');
+for (var i = 0; i < dialogs.length; ++i)
+ dialogs[i].showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html
new file mode 100644
index 0000000000..c49a11d416
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<title>Reference: Test that adding display: none; dynamically on ::backdrop makes it disappear</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<p>Test passes if there is no red.</p>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html
new file mode 100644
index 0000000000..bcf100b368
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<title>Test that adding display: none; dynamically on ::backdrop makes it disappear</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="backdrop-dynamic-display-none-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<p>Test passes if there is no red.</p>
+<dialog></dialog>
+<style>
+dialog { visibility: hidden; }
+::backdrop { background-color: red; }
+.hidden-backdrop::backdrop {
+ display: none;
+}
+</style>
+<script>
+dialog = document.querySelector("dialog");
+dialog.showModal();
+requestAnimationFrame(() => {
+ dialog.classList.add("hidden-backdrop");
+});
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html
new file mode 100644
index 0000000000..01cb93d2ab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background-color: green;
+}
+</style>
+</head>
+<body>
+Test dynamic changes to ::backdrop style. The test passes if there is a green box below.
+<div class="backdrop"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html
new file mode 100644
index 0000000000..6c609a769c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="backdrop-dynamic-style-change-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ visibility: hidden;
+}
+
+::backdrop {
+ visibility: visible;
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background-color: red;
+}
+
+.green::backdrop {
+ background-color: green;
+}
+</style>
+</head>
+<body>
+Test dynamic changes to ::backdrop style. The test passes if there is a green box below.
+<dialog></dialog>
+<script>
+dialog = document.querySelector('dialog');
+dialog.showModal();
+dialog.classList.add('green');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html
new file mode 100644
index 0000000000..4857557bf8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+#backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+<body>
+Test that position 'static' or 'relative' for ::backdrop computes to 'absolute'.
+The test passes if there is a single green box.
+<div id="backdrop"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html
new file mode 100644
index 0000000000..06f790979e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<link rel="match" href="backdrop-in-flow-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ visibility: hidden;
+}
+
+dialog::backdrop {
+ visibility: visible;
+ height: 100px;
+ width: 50px;
+}
+
+#left::backdrop {
+ position: static;
+ top: 100px;
+ left: 100px;
+ background: green;
+}
+
+#right::backdrop {
+ position: relative;
+ background: green;
+ top: 100px;
+ left: 150px;
+}
+</style>
+<body>
+Test that position 'static' or 'relative' for ::backdrop computes to 'absolute'.
+The test passes if there is a single green box.
+<dialog id="left"></dialog>
+<dialog id="right"></dialog>
+</div>
+<script>
+document.querySelector('#left').showModal();
+document.querySelector('#right').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits-ref.html
new file mode 100644
index 0000000000..d9f4cb2c84
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+#backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+<body>
+Test that ::backdrop inherits from its originating element. The test passes if
+there is a green box and no red.
+<div id="backdrop"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits.html
new file mode 100644
index 0000000000..458320f019
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<link rel="match" href="backdrop-inherits-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-position-4/#backdrop">
+<style>
+dialog {
+ --backdrop-bg: green;
+ visibility: hidden;
+}
+
+dialog::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ visibility: visible;
+ background-color: var(--backdrop-bg);
+}
+</style>
+Test that ::backdrop inherits from its originating element. The test passes if
+there is a green box and no red.
+<dialog></dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html
new file mode 100644
index 0000000000..5d515000ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<title>Test that ::backdrop receives events for the associated element</title>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<body>
+<style>
+/* ::backdrop takes up whole screen, actual <dialog> is hidden */
+dialog {
+ visibility: hidden;
+ pointer-events: none;
+}
+
+dialog::backdrop {
+ visibility: visible;
+ pointer-events: initial;
+ background-color: red;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+
+dialog.clicked::backdrop {
+ background-color: green;
+}
+</style>
+<dialog></dialog>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+setup({ single_test: true });
+
+const dialog = document.querySelector("dialog");
+dialog.showModal();
+dialog.addEventListener("click", () => {
+ // Change style for debugging purposes, done() actually makes the test pass
+ dialog.className = "clicked";
+ done();
+});
+new test_driver.Actions()
+ .pointerMove(0, 0, {origin: "viewport"})
+ .pointerDown()
+ .pointerUp()
+ .send();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html
new file mode 100644
index 0000000000..d3f82de181
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<style>
+div {
+ position: absolute;
+}
+
+#bottom-backdrop {
+ top: 100px;
+ left: 100px;
+ height: 300px;
+ width: 300px;
+ background-color: rgb(0, 50, 0);
+}
+
+#bottom {
+ top: 125px;
+ left: 125px;
+ height: 250px;
+ width: 250px;
+ background-color: rgb(0, 90, 0);
+}
+
+#middle-backdrop {
+ top: 150px;
+ left: 150px;
+ height: 200px;
+ width: 200px;
+ background-color: rgb(0, 130, 0);
+}
+
+#middle {
+ top: 175px;
+ left: 175px;
+ height: 150px;
+ width: 150px;
+ background-color: rgb(0, 170, 0);
+}
+
+#top-backdrop {
+ top: 200px;
+ left: 200px;
+ height: 100px;
+ width: 100px;
+ background-color: rgb(0, 210, 0);
+}
+
+#top {
+ top: 225px;
+ left: 225px;
+ height: 50px;
+ width: 50px;
+ background-color: rgb(0, 255, 0);
+}
+</style>
+<body>
+Test for dialog::backdrop stacking order. The test passes if there are 6
+boxes enclosed in each other, becoming increasingly smaller and brighter
+green.
+<div id="bottom-backdrop"></div>
+<div id="bottom"></div>
+<div id="middle-backdrop"></div>
+<div id="middle"></div>
+<div id="top-backdrop"></div>
+<div id="top"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html
new file mode 100644
index 0000000000..897f54a53f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<link rel="match" href="backdrop-stacking-order-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ padding: 0px;
+ border: none;
+ margin: 0px;
+ outline: none;
+}
+
+#bottom::backdrop {
+ top: 100px;
+ left: 100px;
+ height: 300px;
+ width: 300px;
+ background-color: rgb(0, 50, 0);
+ z-index: 100; /* z-index has no effect. */
+}
+
+#bottom {
+ top: 125px;
+ left: 125px;
+ height: 250px;
+ width: 250px;
+ background-color: rgb(0, 90, 0);
+}
+
+#middle::backdrop {
+ top: 150px;
+ left: 150px;
+ height: 200px;
+ width: 200px;
+ background-color: rgb(0, 130, 0);
+ z-index: -100; /* z-index has no effect. */
+}
+
+#middle {
+ top: 175px;
+ left: 175px;
+ height: 150px;
+ width: 150px;
+ background-color: rgb(0, 170, 0);
+}
+
+#top::backdrop {
+ top: 200px;
+ left: 200px;
+ height: 100px;
+ width: 100px;
+ background-color: rgb(0, 210, 0);
+ z-index: 0; /* z-index has no effect. */
+}
+
+#top {
+ top: 225px;
+ left: 225px;
+ height: 50px;
+ width: 50px;
+ background-color: rgb(0, 255, 0);
+ z-index: -1000; /* z-index has no effect. */
+}
+</style>
+<body>
+Test for dialog::backdrop stacking order. The test passes if there are 6
+boxes enclosed in each other, becoming increasingly smaller and brighter
+green.
+<dialog id="top"></dialog>
+<dialog id="middle"></dialog>
+<dialog id="bottom"></dialog>
+<script>
+var topDialog = document.getElementById('top');
+var middleDialog = document.getElementById('middle');
+var bottomDialog = document.getElementById('bottom');
+topDialog.showModal();
+bottomDialog.showModal();
+topDialog.close(); // Just to shuffle the top layer order around a little.
+middleDialog.showModal();
+topDialog.showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html
new file mode 100644
index 0000000000..6ffd72296d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element centered frame</title>
+<style>
+ html {
+ writing-mode: {{GET[html-writing-mode]}}
+ }
+
+ #container {
+ writing-mode: {{GET[container-writing-mode]}}
+ }
+
+ dialog {
+ writing-mode: {{GET[dialog-writing-mode]}};
+ border: none;
+ padding: 0;
+ max-width: initial;
+ max-height: initial;
+ width: {{GET[dialog-width]}};
+ height: {{GET[dialog-height]}};
+ }
+</style>
+
+<div id="container">
+ <dialog>X</dialog>
+</div>
+
+<script>
+"use strict";
+document.querySelector("dialog").showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html
new file mode 100644
index 0000000000..2dc6ce3edf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element: centered alignment</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/#flow-content-3">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<script>
+"use strict";
+
+// Be sure to sync with centering-iframe.html
+const dialogWidth = 20;
+const dialogHeight = 10;
+
+testDialogCentering("horizontal-tb", "", "", "tall viewport", 40, 100);
+testDialogCentering("horizontal-tb", "", "", "wide viewport", 100, 40);
+testDialogCentering("horizontal-tb", "", "", "square viewport", 100, 100);
+testDialogCentering("horizontal-tb", "", "", "dialog and viewport match", dialogWidth, dialogHeight);
+
+testDialogCentering("vertical-rl", "", "", "tall viewport", 40, 100);
+testDialogCentering("vertical-lr", "", "", "tall viewport", 40, 100);
+
+testDialogCentering("vertical-rl", "", "horizontal-tb", "tall viewport", 40, 100);
+testDialogCentering("vertical-lr", "", "horizontal-tb", "tall viewport", 40, 100);
+
+testDialogCentering("horizontal-tb", "vertical-rl", "", "tall viewport", 40, 100);
+testDialogCentering("vertical-rl", "horizontal-tb", "", "tall viewport", 40, 100);
+
+testDialogCentering("horizontal-tb", "vertical-rl", "horizontal-tb", "tall viewport", 40, 100);
+testDialogCentering("vertical-rl", "horizontal-tb", "vertical-rl", "tall viewport", 40, 100);
+
+function testDialogCentering(writingMode, containerWritingMode, dialogWritingMode, label, iframeWidth, iframeHeight) {
+ const dialogSizesToTest = [["", ""], [dialogWidth.toString()+'px', dialogHeight.toString()+'px']];
+ for (const dialogSizes of dialogSizesToTest) {
+ const isDefaultSize = dialogSizes[0] == "";
+ // This test doesn't make sense if the dialog sizes are default
+ if (isDefaultSize && label == "dialog and viewport match") {
+ continue;
+ }
+
+ async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = `centering-iframe.sub.html?html-writing-mode=${writingMode}&container-writing-mode=${containerWritingMode}&dialog-writing-mode=${dialogWritingMode}&dialog-width=${dialogSizes[0]}&dialog-height=${dialogSizes[1]}`;
+ iframe.width = iframeWidth;
+ iframe.height = iframeHeight;
+ iframe.onload = t.step_func_done(() => {
+ const dialog = iframe.contentDocument.querySelector("dialog");
+ const dialogRect = dialog.getBoundingClientRect();
+
+ const expectedLeftOffset = iframeWidth / 2 - dialogRect.width / 2;
+ const expectedTopOffset = Math.max(iframeHeight / 2 - dialogRect.height / 2, 0);
+
+ if (isDefaultSize) {
+ assert_approx_equals(dialogRect.left, expectedLeftOffset, 1/60);
+ assert_approx_equals(dialogRect.top, expectedTopOffset, 1/60);
+ } else {
+ assert_equals(dialogRect.left, expectedLeftOffset);
+ assert_equals(dialogRect.top, expectedTopOffset);
+ }
+ });
+ document.body.appendChild(iframe);
+ }, writingMode + (containerWritingMode ? ` (container ${containerWritingMode})` : "") +
+ (dialogWritingMode ? ` (dialog ${dialogWritingMode})` : "") + `: ${label}` + `, default-sizes: ${isDefaultSize}`);
+
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html
new file mode 100644
index 0000000000..bc787202cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/8199">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog autofocus id=autofocusdialog data-description="dialog element with autofocus should get initial focus." class=target>
+ <button>focusable button</button>
+ <button autofocus>autofocusable button</button>
+</dialog>
+
+<dialog id=keyboardfocusdialog data-description="Only keyboard-focusable elements should get dialog initial focus.">
+ <button tabindex="-1">mouse focusable button</button>
+ <button class=target>keyboard focusable button</button>
+</dialog>
+
+<dialog id=autofocuswithoutkeyboarddialog data-description="Autofocus takes precedence over keyboard-focusable requirement.">
+ <button>keyboard focusable button</button>
+ <button tabindex="-1" autofocus class=target>mouse focusable autofocus button</button>
+</dialog>
+
+<dialog id=subtree data-description="Only keyboard-focusable elements should get dialog initial focus including in subtrees.">
+ <div>
+ <button tabindex="-1">mouse focusable button</button>
+ <button class=target>keyboard focusable button</button>
+ </div>
+</dialog>
+
+<dialog id=nestedbuttons data-description="Only keyboard-focusable elements should get dialog initial focus including in nested buttons.">
+ <button tabindex="-1">
+ <span>mouse focusable button</span>
+ <button tabindex="-1">nested mouse focusable button</button>
+ </button>
+ <button class=target>keyboard focusable button</button>
+</dialog>
+
+<script>
+document.querySelectorAll('dialog').forEach(dialog => {
+ test(t => {
+ let target = dialog.querySelector('.target');
+ if (dialog.classList.contains('target')) {
+ target = dialog;
+ }
+ t.add_cleanup(() => {
+ if (dialog.open)
+ dialog.close();
+ });
+
+ dialog.showModal();
+ assert_equals(document.activeElement, target,
+ 'showModal: the target element did not receive initial focus.');
+ dialog.close();
+
+ dialog.show();
+ assert_equals(document.activeElement, target,
+ 'show: the target element did not receive initial focus.');
+ dialog.close();
+ }, dialog.dataset.description);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html
new file mode 100644
index 0000000000..9d9856962d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=110952">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+#div {
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+</style>
+<div id=div></div>
+<dialog id="dialog"></dialog>
+<dialog></dialog>
+
+<script>
+promise_test(async () => {
+ const dialog = document.getElementById('dialog');
+ dialog.showModal();
+ dialog.close();
+
+ const div = document.getElementById('div');
+ div.addEventListener('click', function(event) {
+ div.firedOn = true;
+ div.style.backgroundColor = 'green';
+ });
+
+ var absoluteTop = 0;
+ var absoluteLeft = 0;
+ for (var parentNode = div; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = absoluteLeft + div.offsetWidth / 2;
+ const y = absoluteTop + div.offsetHeight / 2;
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ assert_true(div.firedOn, 'div should have gotten a click event.');
+}, 'Ensure that closed dialogs do not block mouse events. To test manually, click the red box. The test succeeds if the red box turns green.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html
new file mode 100644
index 0000000000..5a6e6b21fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for dialog element colors</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+:root { background-color: Canvas; color: CanvasText; }
+#light { color-scheme: light }
+#dark { color-scheme: dark }
+</style>
+<dialog id="dialog" open>
+ This is a dialog
+</dialog>
+<dialog id="light" open>
+ This is a dialog
+</dialog>
+<dialog id="dark" open>
+ This is a dialog
+</dialog>
+<script>
+test(function() {
+ let dialog = document.getElementById("dialog");
+ let cs = getComputedStyle(dialog);
+ let rootCs = getComputedStyle(document.documentElement);
+ assert_equals(cs.color, rootCs.color, "Dialog color should match CanvasText");
+ assert_equals(cs.backgroundColor, rootCs.backgroundColor, "Dialog background should match Canvas");
+}, "<dialog> color and background match default")
+
+test(function() {
+ let lightCs = getComputedStyle(document.getElementById("light"));
+ let darkCs = getComputedStyle(document.getElementById("dark"));
+ assert_not_equals(lightCs.color, darkCs.color, "Dialog color should react to color-scheme");
+ assert_not_equals(lightCs.backgroundColor, darkCs.backgroundColor, "Dialog background should react to color-scheme");
+}, "<dialog> colors react to dark mode")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html
new file mode 100644
index 0000000000..c8c1ab2826
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1206122">
+
+<body onload=dlg.show()>
+<dialog id="dlg">
+ <audio></audio>
+ <video></video>
+</dialog>
+
+This test passes if it does not crash.
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html
new file mode 100644
index 0000000000..894efd59dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/interaction/focus/the-autofocus-attribute/resources/utils.js"></script>
+<body>
+<dialog>
+<input>
+<input autofocus>
+</dialog>
+<script>
+// https://github.com/whatwg/html/issues/4788
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.show();
+ assert_equals(document.activeElement, dialog.querySelector('[autofocus]'),
+ 'dialog.show() should set focus on a descendant element with an ' +
+ 'autofocus attribute.');
+ document.activeElement.blur();
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, document.body,
+ 'Non-dialog autofocus processing should be skipped.');
+}, 'An autofocus element in a dialog element should not try to get focus twice.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html
new file mode 100644
index 0000000000..ff9ebd7d28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/common.js"></script>
+<script>
+promise_test(() => {
+ return waitUntilLoadedAndAutofocused().then(() => {
+ assert_equals(document.activeElement, document.getElementById("outer-button"));
+
+ var focusCount = 0;
+ var dlg = document.getElementById("dlg");
+ var input1 = document.getElementById("input1");
+ var input2 = document.getElementById("input2");
+ input2.onfocus = function() { focusCount += 1 };
+
+ var expectedFocusCount = 3;
+ for (i = 0; i < expectedFocusCount; i++) {
+ dlg.show();
+ assert_equals(document.activeElement, input2);
+ input1.focus();
+ assert_equals(document.activeElement,input1);
+ dlg.close();
+ }
+
+ assert_equals(focusCount.toString(), expectedFocusCount.toString());
+ });
+}, "autofocus is run every time a dialog is opened");
+</script>
+</head>
+<body>
+<button id="outer-button" autofocus></button>
+<dialog id="dlg">
+ <!-- Unfocusable elements with [autofocus] should be ignored. -->
+ <input autofocus disabled>
+ <textarea autofocus hidden></textarea>
+ <input id="input1"></input>
+ <input id="input2" autofocus></input>
+</dialog>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html
new file mode 100644
index 0000000000..149a53eacf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/common.js"></script>
+<script>
+promise_test(() => {
+ return waitUntilLoadedAndAutofocused().then(() => {
+ assert_equals(document.activeElement, document.getElementById("outer-button"));
+
+ var dialog = document.getElementById('dialog');
+ dialog.showModal();
+
+ autofocusButton = document.getElementById('autofocus-button');
+ assert_equals(document.activeElement, autofocusButton);
+
+ anotherButton = document.getElementById('another-button');
+ anotherButton.focus();
+ assert_equals(document.activeElement, anotherButton);
+
+ // Test that recreating layout does not give focus back to a previously autofocused element.
+ autofocusButton.style.display = 'none';
+ document.body.offsetHeight;
+ autofocusButton.style.display = 'block';
+ document.body.offsetHeight;
+ assert_equals(document.activeElement, anotherButton);
+
+ // Test that reinserting does not give focus back to a previously autofocused element.
+ var parentNode = autofocusButton.parentNode;
+ parentNode.removeChild(autofocusButton);
+ document.body.offsetHeight;
+ parentNode.appendChild(autofocusButton);
+ document.body.offsetHeight;
+ assert_equals(document.activeElement, anotherButton);
+
+ dialog.close();
+ // Test that dialog focusing steps run when a dialog is reopened.
+ dialog.showModal();
+ assert_equals(document.activeElement, autofocusButton);
+ dialog.close();
+ });
+}, "autofocus when a modal dialog is opened");
+</script>
+</head>
+<body>
+<button id="outer-button" autofocus></button>
+<dialog id="dialog">
+ <button></button>
+ <!-- Unfocusable elements with [autofocus] should be ignored. -->
+ <input autofocus disabled>
+ <textarea autofocus hidden></textarea>
+ <dialog>
+ <button autofocus></button>
+ </dialog>
+ <div>
+ <span>
+ <button id="autofocus-button" autofocus></button>
+ </span>
+ </div>
+ <button id="another-button" autofocus></button>
+</dialog>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html
new file mode 100644
index 0000000000..d583c4cd23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test cancel event is fired when the dialog is closed by user close requests</title>
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="/close-watcher/resources/helpers.js"></script>
+
+<dialog>
+ <p>Hello World</p>
+</dialog>
+
+<script type="module">
+setup({ single_test: true });
+
+const dialog = document.querySelector("dialog");
+const events = [];
+
+dialog.addEventListener("cancel", event => {
+ assert_true(event.cancelable, "cancel event should be cancelable");
+ assert_array_equals(events, []);
+
+ events.push("addEventListener cancel");
+});
+
+assert_equals(dialog.oncancel, null);
+dialog.oncancel = () => {
+ assert_array_equals(events, ["addEventListener cancel"]);
+
+ events.push("oncancel");
+};
+
+dialog.addEventListener("close", () => {
+ assert_array_equals(events, ["addEventListener cancel", "oncancel"]);
+
+ events.push("addEventListener close");
+});
+
+assert_equals(dialog.onclose, null);
+dialog.onclose = () => {
+ assert_array_equals(events, ["addEventListener cancel", "oncancel", "addEventListener close"]);
+
+ done();
+};
+
+dialog.showModal();
+await blessTopLayer(dialog);
+await sendCloseRequest();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html
new file mode 100644
index 0000000000..4daffc09a4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cancel event with preventDefault on cancel event for dialog element</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/common/top-layer.js"></script>
+ <script src="/close-watcher/resources/helpers.js"></script>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test cancel event with preventDefault on cancel event for dialog element</p>
+<dialog>
+ <p>Hello World</p>
+</dialog>
+<script type=module>
+ setup({ single_test: true });
+
+ var hasCancelEventFired = false;
+
+ const dialog = document.querySelector("dialog");
+
+ const verify = () => {
+ assert_true(hasCancelEventFired, "cancel is fired");
+ done();
+ };
+
+ dialog.addEventListener("cancel", function(event) {
+ hasCancelEventFired = true;
+ event.preventDefault();
+ step_timeout(function() {
+ verify();
+ }, 0)
+ });
+
+ dialog.addEventListener("close", function() {
+ assert_true(false, "close event should not be fired");
+ });
+
+ dialog.showModal();
+
+ await blessTopLayer(dialog);
+ await sendCloseRequest();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html
new file mode 100644
index 0000000000..153d434317
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test dialog modal is closed by escape key with input focused</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test dialog modal is closed by escape key with input focused</p>
+<dialog id="dialog">
+ <p>Hello World</p>
+</dialog>
+
+<dialog id="dialogWithAutofocus">
+ <input autofocus/>
+</dialog>
+
+<script>
+ setup({ single_test: true });
+
+ const triggerEscKey = () => {
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+ };
+
+ /* Make sure we still cancel the dialog even if the input element is focused */
+ function runTestCancelWhenInputFocused() {
+ const dialog = document.getElementById("dialogWithAutofocus");
+ const input = document.querySelector("input");
+
+ dialog.addEventListener("close", function() {
+ assert_false(dialog.open, "dialog with input autofocused is closed");
+ done();
+ });
+ dialog.showModal();
+ assert_true(input == document.activeElement, "input element should be focused");
+
+ triggerEscKey();
+ }
+
+ const dialog = document.getElementById("dialog");
+
+ dialog.addEventListener("close", function() {
+ assert_false(dialog.open, "dialog closed");
+ step_timeout(function() {
+ runTestCancelWhenInputFocused();
+ }, 0);
+ });
+
+ dialog.showModal();
+ triggerEscKey();
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html
new file mode 100644
index 0000000000..178d5a2711
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test dialog modal is closed by escape key with select focused</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test dialog modal is closed by escape key with select focused</p>
+<dialog id="dialog">
+ <select>
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </select>
+</dialog>
+
+<script>
+ setup({ single_test: true });
+
+ const dialog = document.getElementById("dialog");
+ const select = document.querySelector("select");
+
+ dialog.addEventListener("close", function() {
+ assert_false(dialog.open, "dialog with select is closed");
+ done();
+ });
+ dialog.showModal();
+ assert_true(select == document.activeElement, "select element should be focused");
+
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html
new file mode 100644
index 0000000000..e368bde6fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=253357">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="/close-watcher/resources/helpers.js"></script>
+
+<!--
+To test manually, hit Escape once to see the topmost dialog turn green
+then once again to close it. Repeat for the remaining dialog.
+-->
+
+<style>
+#bottom {
+ top: 100px;
+ left: 100px;
+ height: 300px;
+ width: 300px;
+ margin: 0;
+ background: cyan;
+}
+
+#top {
+ top: 150px;
+ left: 150px;
+ height: 200px;
+ width: 200px;
+ margin: 0;
+ background: yellow;
+}
+</style>
+
+<dialog id="bottom">
+ <span></span>
+ <div>You can't Escape when this textbox has focus: <input id="swallow-input" type="text"></div>
+ <div>You can Escape even if this textbox has focus: <input id="normal-input" type="text"></div>
+</dialog>
+<dialog id="top">
+ <span></span>
+</dialog>
+
+<script>
+function handleCancel(event) {
+ this.style.background = 'green';
+ this.querySelector('span').textContent = 'I blocked the cancel! Try again to close me.';
+ event.preventDefault();
+ this.removeEventListener('cancel', handleCancel);
+}
+
+promise_test(async () => {
+ bottomDialog = document.getElementById('bottom');
+ bottomDialog.addEventListener('cancel', handleCancel);
+
+ topDialog = document.getElementById('top');
+ topDialog.addEventListener('cancel', handleCancel);
+
+ normalInput = document.getElementById('normal-input');
+ swallowInput = document.getElementById('swallow-input');
+ swallowInput.addEventListener('keydown', function(event) {
+ event.preventDefault();
+ });
+
+ bottomDialog.showModal();
+ await blessTopLayer(bottomDialog);
+ topDialog.showModal();
+
+ await blessTopLayer(topDialog);
+ await sendEscKey();
+ assert_true(topDialog.open, 'Top dialog event listener should prevent closing.');
+ assert_true(bottomDialog.open, 'Top dialog event listener should prevent closing.');
+
+ await blessTopLayer(topDialog);
+ await sendEscKey();
+ assert_false(topDialog.open, 'Top dialog should close.');
+ assert_true(bottomDialog.open, 'Top dialog should close.');
+
+ swallowInput.focus();
+ await sendEscKey();
+ await sendEscKey();
+ await sendEscKey();
+ assert_false(topDialog.open, 'Input should swallow Escape mechanism.');
+ assert_true(bottomDialog.open, 'Input should swallow Escape mechanism.');
+
+ normalInput.focus();
+ await sendEscKey();
+ assert_false(topDialog.open, 'Bottom dialog event listener should prevent closing.');
+ assert_true(bottomDialog.open, 'Bottom dialog event listener should prevent closing.');
+
+ await sendEscKey();
+ assert_false(topDialog.open, 'Bottom dialog should close.');
+ assert_false(bottomDialog.open, 'Bottom dialog should close.');
+
+ await sendEscKey();
+ assert_false(topDialog.open, 'Pressing Escape now should do nothing.');
+ assert_false(bottomDialog.open, 'Pressing Escape now should do nothing.');
+
+ bottomDialog.remove();
+ topDialog.remove();
+}, 'Modal dialogs should close when the escape key is pressed.');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html
new file mode 100644
index 0000000000..0f8d40aa2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<title>dialog element: close()</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<dialog id="d1" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<script>
+ var d1 = document.getElementById('d1'),
+ t = async_test("close() fires a close event"),
+ was_queued = false;
+
+ d1.onclose = t.step_func_done(function(e) {
+ assert_true(was_queued, "close event should be queued");
+ assert_true(e.isTrusted, "close event is trusted");
+ assert_false(e.bubbles, "close event doesn't bubble");
+ assert_false(e.cancelable, "close event is not cancelable");
+ });
+
+ t.step(function() {
+ d1.close();
+ was_queued = true;
+ })
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html
new file mode 100644
index 0000000000..b7903ed461
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=276785">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog></dialog>
+
+<script>
+async_test(t => {
+ document.addEventListener('close', t.step_func_done(() => {
+ t.assert_unreached(`The 'close' event unexpectedly bubbled.`);
+ }));
+
+ closedCount = 0;
+ dialog = document.querySelector('dialog');
+ dialog.addEventListener('close', function(event) {
+ const selfDialog = this;
+ t.step(() => {
+ closedCount++;
+ assert_equals(selfDialog, dialog);
+ assert_false(dialog.open);
+ assert_false(event.cancelable);
+ event.preventDefault();
+
+ if (closedCount == 1) {
+ dialog.show();
+ dialog.close();
+ assert_equals(closedCount, 1, `dialog's close event handler shouldn't be called synchronously.`);
+ } else if (closedCount == 2) {
+ t.done();
+ }
+ });
+ });
+
+ dialog.show();
+ dialog.close();
+
+ // Verify that preventDefault() didn't cancel closing.
+ assert_false(dialog.open);
+
+ // dialog's close event handler shouldn't be called synchronously.
+ assert_equals(closedCount, 0);
+}, "Test that dialog receives a close event upon closing.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html
new file mode 100644
index 0000000000..5c2e70f87a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/5802">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<button>button</button>
+<dialog>hello world</dialog>
+
+<script>
+const dialog = document.querySelector('dialog');
+const button = document.querySelector('button');
+
+promise_test(async t => {
+ dialog.showModal();
+
+ let closeFired = false;
+ let cancelFired = false;
+ dialog.addEventListener('close', () => closeFired = true);
+ dialog.addEventListener('cancel', () => cancelFired = true);
+
+ dialog.removeAttribute('open');
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ await new Promise(requestAnimationFrame);
+
+ assert_false(dialog.matches(':modal'),
+ 'The dialog should not match :modal after closing.');
+ assert_false(cancelFired,
+ 'The cancel event should not fire when removing the open attribute.');
+ assert_true(closeFired,
+ 'The close event should be fired when removing the open attribute.');
+
+ let buttonFiredClick = false;
+ button.addEventListener('click', () => buttonFiredClick = true);
+ await test_driver.click(button);
+ assert_true(buttonFiredClick,
+ 'The page should not be inert or blocked after removing the open attribute.');
+}, 'Removing the open attribute from an open modal dialog should run the closing algorithm.');
+
+promise_test(async t => {
+ dialog.show();
+
+ let closeFired = false;
+ let cancelFired = false;
+ dialog.addEventListener('close', () => closeFired = true);
+ dialog.addEventListener('cancel', () => cancelFired = true);
+
+ dialog.removeAttribute('open');
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ await new Promise(requestAnimationFrame);
+
+ assert_false(cancelFired,
+ 'The cancel event should not fire when removing the open attribute.');
+ assert_true(closeFired,
+ 'The close event should be fired when removing the open attribute.');
+}, 'Removing the open attribute from an open non-modal dialog should fire a close event.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html
new file mode 100644
index 0000000000..9029612b24
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element: close()</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<dialog id="d1">
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d2" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d3" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d4" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d5" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<script>
+ var d1 = document.getElementById('d1'),
+ d2 = document.getElementById('d2'),
+ d3 = document.getElementById('d3'),
+ d4 = document.getElementById('d4'),
+ d5 = document.getElementById('d5'),
+ t = async_test("close() fires a close event"),
+ was_queued = false;
+
+ test(function(){
+ d1.close("closedialog");
+ assert_equals(d1.returnValue, "");
+ }, "close() on a <dialog> that doesn't have an open attribute aborts the steps");
+
+ test(function(){
+ assert_true(d2.open);
+ assert_equals(d2.returnValue, "");
+ d2.close("closedialog");
+ assert_false(d2.hasAttribute("open"));
+ assert_equals(d2.returnValue, "closedialog");
+ }, "close() removes the open attribute and set the returnValue to the first argument");
+
+ test(function(){
+ assert_true(d3.open);
+ assert_equals(d3.returnValue, "");
+ d3.returnValue = "foobar";
+ d3.close();
+ assert_false(d3.hasAttribute("open"));
+ assert_equals(d3.returnValue, "foobar");
+ }, "close() without argument removes the open attribute and there's no returnValue");
+
+ d4.onclose = t.step_func_done(function(e) {
+ assert_true(was_queued, "close event should be queued");
+ assert_true(e.isTrusted, "close event is trusted");
+ assert_false(e.bubbles, "close event doesn't bubble");
+ assert_false(e.cancelable, "close event is not cancelable");
+ });
+
+ t.step(function() {
+ d4.close();
+ was_queued = true;
+ })
+
+ test(function(){
+ Object.defineProperty(HTMLDialogElement.prototype, 'returnValue', { set: function(v) { assert_unreached('JS-defined setter returnValue on the prototype was invoked'); }, configurable:true });
+ Object.defineProperty(d5, 'returnValue', { set: function(v) { assert_unreached('JS-defined setter returnValue on the instance was invoked'); }, configurable:true });
+ d5.close('foo');
+ }, "close() should set the returnValue IDL attribute but not the JS property");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html
new file mode 100644
index 0000000000..87a130c6f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<dialog></dialog>
+<script>
+test(function() {
+ dialog = document.querySelector('dialog')
+ assert_true(dialog instanceof HTMLDialogElement);
+}, "The DIALOG element should be recognized");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-previous-outside.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-previous-outside.html
new file mode 100644
index 0000000000..43a17676ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-previous-outside.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/8904">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button id=b1>button 1</button>
+<button id=b2>button 2</button>
+<div id=host>
+ <template shadowrootmode=open>
+ <button>button in shadowroot outside dialog</button>
+ </template>
+</div>
+<dialog id=mydialog>
+ <button id=b3>button in dialog</button>
+ <div id=dialoghost>
+ <template shadowrootmode=open>
+ <button>button in shadowroot in dialog</button>
+ </template>
+ </div>
+</dialog>
+
+<div id=host2>
+ <template shadowrootmode=open>
+ <dialog>
+ <slot></slot>
+ </dialog>
+ </template>
+ <button id=host2button>button</button>
+</div>
+
+<dialog id=mydialog2>hello world</dialog>
+
+<script>
+test(() => {
+ b1.focus();
+ mydialog.show();
+ b2.focus();
+ mydialog.close();
+ assert_equals(document.activeElement, b2);
+}, 'Focus should not be restored if the currently focused element is not inside the dialog.');
+
+test(() => {
+ const shadowbutton = host.shadowRoot.querySelector('button');
+ b2.focus();
+ mydialog.show();
+ shadowbutton.focus();
+ mydialog.close();
+ assert_equals(document.activeElement, host, 'document.activeElement should point at the shadow host.');
+ assert_equals(host.shadowRoot.activeElement, shadowbutton, 'The button in the shadowroot should remain focused.');
+}, 'Focus restore should not occur when the focused element is in a shadowroot outside of the dialog.');
+
+test(() => {
+ const shadowbutton = dialoghost.shadowRoot.querySelector('button');
+ b2.focus();
+ mydialog.show();
+ shadowbutton.focus();
+ mydialog.close();
+ assert_equals(document.activeElement, b2);
+}, 'Focus restore should occur when the focused element is in a shadowroot inside the dialog.');
+
+test(() => {
+ const dialog = host2.shadowRoot.querySelector('dialog');
+ b2.focus();
+ dialog.show();
+ host2button.focus();
+ dialog.close();
+ assert_equals(document.activeElement, b2);
+}, 'Focus restore should occur when the focused element is slotted into a dialog.');
+
+test(() => {
+ b1.focus();
+ const dialog = document.getElementById('mydialog2');
+ dialog.showModal();
+ dialog.blur();
+ assert_equals(document.activeElement, document.body,
+ 'Focus should return to the body when calling dialog.blur().');
+ dialog.close();
+ assert_equals(document.activeElement, b1,
+ 'Focus should be restored to the previously focused element.');
+}, 'Focus restore should always occur for modal dialogs.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html
new file mode 100644
index 0000000000..2cd63eb796
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>dialog focusing delegation: with two nested shadow trees</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<dialog>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus>Focusable</button>
+ <button tabindex="-1">Focusable</button>
+ </template>
+ <button tabindex="-1">Focusable</button>
+ </template>
+ <button tabindex="-1">Focusable</button>
+</dialog>
+
+<script>
+function turnIntoShadowTree(template) {
+ for (const subTemplate of template.content.querySelectorAll(".turn-into-shadow-tree")) {
+ turnIntoShadowTree(subTemplate);
+ }
+
+ const div = document.createElement("div");
+ div.attachShadow({ mode: "open", delegatesFocus: template.classList.contains("delegates-focus") });
+ div.shadowRoot.append(template.content);
+ template.replaceWith(div);
+}
+
+for (const template of document.querySelectorAll(".turn-into-shadow-tree")) {
+ turnIntoShadowTree(template);
+}
+
+for (const method of ["show", "showModal"]) {
+ test(t => {
+ const dialog = document.querySelector("dialog");
+ dialog[method]();
+ t.add_cleanup(() => dialog.close());
+
+ const shadowHostOuter = dialog.querySelector("div");
+ assert_equals(document.activeElement, shadowHostOuter, "document.activeElement");
+
+ const shadowHostInner = shadowHostOuter.shadowRoot.querySelector("div");
+ assert_equals(shadowHostOuter.shadowRoot.activeElement, shadowHostInner, "shadowHostOuter.shadowRoot.activeElement");
+
+ const button = shadowHostInner.shadowRoot.querySelector("[autofocus]");
+ assert_equals(shadowHostInner.shadowRoot.activeElement, button, "shadowHostInner.shadowRoot.activeElement");
+ }, `${method}()`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html
new file mode 100644
index 0000000000..7e57685425
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html
@@ -0,0 +1,274 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>dialog focus delegation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<!--
+ We focus this one between each test, to ensure that for non-modal dialogs,
+ if there is no focus delegate, it stays focused (instead of causing focus to reset to the body).
+-->
+<button id="focus-between-tests">Focus between tests</button>
+
+<dialog data-description="No autofocus, no delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="No autofocus, yes delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, yes delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, yes delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button>Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus before, no delegatesFocus">
+ <button autofocus class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus before, yes delegatesFocus">
+ <button autofocus class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus after, no delegatesFocus">
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button autofocus class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus after, yes delegatesFocus">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button autofocus class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, yes delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree delegates-focus autofocus">
+ <button disabled>Non-focusable</button>
+ <button class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, yes delegatesFocus, sibling before">
+ <button>Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus autofocus">
+ <button disabled>Non-focusable</button>
+ <button class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, yes delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree delegates-focus autofocus">
+ <button disabled>Non-focusable</button>
+ <button class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button>Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, no delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree autofocus">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, no delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree autofocus">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, no delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree autofocus">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button>Focusable</button>
+ <button autofocus class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button>Focusable</button>
+ <button autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button>Focusable</button>
+ <button autofocus class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button>Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ <button autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ <button autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ <button autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Two shadow trees, both delegatesFocus, first tree doesn't have autofocus element, second does">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button autofocus>Focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, slotted target">
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ <slot></slot>
+ <button>Focusable</button>
+ </template>
+ <button class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Shadowroot on child, no autofocus, no delegatesFocus">
+ <div>
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ </template>
+ </div>
+ <button class="focus-me">Focusable</button>
+</dialog>
+
+<script>
+for (const template of document.querySelectorAll(".turn-into-shadow-tree")) {
+ const div = document.createElement("div");
+ div.attachShadow({ mode: "open", delegatesFocus: template.classList.contains("delegates-focus") });
+
+ if (template.classList.contains("autofocus")) {
+ div.setAttribute("autofocus", true);
+ }
+ div.shadowRoot.append(template.content);
+ template.replaceWith(div);
+}
+
+const focusBetweenTests = document.querySelector("#focus-between-tests");
+
+for (const dialog of document.querySelectorAll("dialog")) {
+ for (const method of ["show", "showModal"]) {
+ test(t => {
+ focusBetweenTests.focus();
+
+ dialog[method]();
+ t.add_cleanup(() => dialog.close());
+
+ const expectedFocusOutsideShadowTree = dialog.querySelector(".focus-me");
+ if (expectedFocusOutsideShadowTree) {
+ assert_equals(document.activeElement, expectedFocusOutsideShadowTree);
+ } else {
+ const shadowHost = dialog.querySelector("div");
+ const expectedFocusInsideShadowTree = shadowHost.shadowRoot.querySelector(".focus-me");
+ if (expectedFocusInsideShadowTree) {
+ assert_equals(document.activeElement, shadowHost);
+ assert_equals(shadowHost.shadowRoot.activeElement, expectedFocusInsideShadowTree);
+ } else {
+ // There is no focus delegate. The dialog element should be focused.
+ assert_equals(document.activeElement, dialog);
+ }
+ }
+ }, `${method}: ${dialog.dataset.description}`);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html
new file mode 100644
index 0000000000..1e00086609
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>dialog element: focusability</title>
+<link rel=help href="https://github.com/whatwg/html/pull/8199">
+<link rel=author href="mailto:masonf@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<button id="before">before</button>
+<dialog id="dialog1" open>
+ <button id="within1">button</button>
+</dialog>
+<button id="after1">after 1</button>
+<dialog tabindex=0 id="dialog2" open>
+ <button id="within2">button</button>
+</dialog>
+<button id="after2">after 2</button>
+<dialog tabindex="-1" id="dialog3" open>
+ <button id="within3">button</button>
+</dialog>
+<button id="after3">after 3</button>
+<dialog contenteditable="true" id="dialog4" open>
+ <button id="within4">button</button>
+</dialog>
+<button id="after4">after 4</button>
+
+<style>
+ #dialog1 { top: 25px; }
+ #dialog2 { top: 100px; }
+ #dialog3 { top: 175px; }
+ #dialog4 { top: 250px; }
+</style>
+
+<script>
+ function navigateForward() {
+ const TAB = '\ue004';
+ return test_driver.send_keys(document.body, TAB);
+ }
+ async function assert_focus_order(elements) {
+ assert_true(elements.length >= 2);
+ elements[0].focus();
+ for(let i=0;i<elements.length;++i) {
+ assert_equals(document.activeElement,elements[i],`Focus order mismatch at step ${i+1}/${elements.length}`);
+ await navigateForward();
+ }
+ }
+
+ async_test((t) => {
+ window.onload = async () => {
+ await assert_focus_order([before,within1,after1,dialog2,within2,after2,
+ within3,after3,dialog4,within4,after4]);
+ t.done();
+ };
+ }, "The dialog element itself should not be keyboard focusable.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html
new file mode 100644
index 0000000000..bf621b640b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test focusing steps when dialog is disconnected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<input>
+<script>
+test(function() {
+ const outerInput = document.querySelector("input");
+ outerInput.focus();
+ assert_equals(document.activeElement, outerInput,
+ "Focus should be on element we just focused");
+
+ const dialog = document.createElement("dialog");
+ assert_false(dialog.open, "Dialog should initially be closed");
+ assert_false(dialog.hasAttribute('open'), "Dialog should initially be closed");
+
+ const innerInput = document.createElement("input");
+ innerInput.autofocus = true;
+ dialog.append(innerInput);
+
+ dialog.show();
+ this.add_cleanup(() => { dialog.close(); });
+ assert_true(dialog.open, "Disconnected dialog can still be open");
+
+
+ assert_equals(document.activeElement, outerInput, "Focusing steps should not change focus");
+}, "dialog.show(): focusing steps should not change focus on disconnected <dialog>");
+
+test(function() {
+ assert_throws_dom("InvalidStateError", () => {
+ document.createElement("dialog").showModal();
+ });
+}, "dialog.showModal() should throw on disconnected <dialog>");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html
new file mode 100644
index 0000000000..003c456179
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test focusing steps when dialog is inert</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<input id="outer-input">
+<dialog>
+ <input autofocus>
+</dialog>
+<script>
+function test_focusing_steps_with_inert_dialog(test, isModal) {
+ const outerInput = document.querySelector("#outer-input");
+ outerInput.focus();
+ assert_equals(document.activeElement, outerInput,
+ "Focus should be on element we just focused");
+
+ const dialog = document.querySelector("dialog");
+ assert_false(dialog.open, "Dialog should initially be closed");
+
+ dialog.inert = true;
+ test.add_cleanup(() => { dialog.inert = false; });
+
+ if (isModal) {
+ dialog.showModal();
+ test.add_cleanup(() => { dialog.close(); });
+ assert_equals(document.activeElement, document.body,
+ "dialog.showModal(): focusing steps should apply focus fixup rule when dialog is inert");
+ } else {
+ dialog.show();
+ test.add_cleanup(() => { dialog.close(); });
+ assert_equals(document.activeElement, outerInput,
+ "dialog.show(): focusing steps should not change focus when dialog is inert");
+ }
+}
+
+test(function() {
+ test_focusing_steps_with_inert_dialog(this, false);
+}, "dialog.show(): focusing steps should not change focus when dialog is inert");
+
+test(function() {
+ test_focusing_steps_with_inert_dialog(this, true);
+}, "dialog.showModal(): focusing steps should apply focus fixup rule when dialog is inert");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html
new file mode 100644
index 0000000000..4d1e792c1c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/interaction/focus/the-autofocus-attribute/resources/utils.js"></script>
+<body>
+<dialog></dialog>
+<script>
+// https://github.com/whatwg/html/issues/4788
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.show();
+ dialog.close();
+ const input = document.createElement('input');
+ input.autofocus = true;
+ document.body.insertBefore(input, dialog);
+ await waitUntilStableAutofocusState();
+ assert_not_equals(document.activeElement, input,
+ 'Non-dialog autofocus processing should be skipped.');
+}, 'After showing a dialog, non-dialog autofocus processing won\'t work.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission-unusual.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission-unusual.html
new file mode 100644
index 0000000000..ae0de29a89
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission-unusual.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Test dialog form submission, unusual cases</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=log></div>
+
+<dialog>
+ <form method=dialog action="https://test:test/" target=doesnotmatter rel=noopener>
+ <input type=submit formaction="https://test:test/" id=submit-1>
+ <input type=submit id=submit-2>
+ </form>
+</dialog>
+
+<script>
+test(() => {
+ const dialog = document.querySelector("dialog");
+ dialog.showModal();
+ assert_true(dialog.open);
+
+ document.getElementById("submit-1").click();
+ assert_false(dialog.open);
+}, "A form's action and rel=noopener are ignored during submission");
+
+test(() => {
+ const dialog = document.querySelector("dialog");
+ dialog.showModal();
+ assert_true(dialog.open);
+
+ document.getElementById("submit-2").click();
+ assert_false(dialog.open);
+}, "A form's action and rel=noopener are ignored during submission, part 2");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html
new file mode 100644
index 0000000000..5934485087
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<meta charset=urf-8>
+<meta name=viewport content="width=device-width,initial-scale=1">
+<title>Test dialog form submission</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<dialog id="favDialog">
+ <form id="dialogForm" method="dialog">
+ <button id="confirmBtn" value="default">Confirm</button>
+ <input id="confirmImgBtn" src="./resources/submit.jpg" width="41"
+ height="41" type="image" alt="Hello">
+ </form>
+ <form method="post">
+ <input id="confirmImgBtn2" src="./resources/submit.jpg" width="41"
+ formmethod="dialog" height="41" type="image" alt="Hello">
+ </form>
+</dialog>
+<script>
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.click();
+
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_equals(dialog.returnValue, "default", "Return the default value");
+}, 'click the form submission button should close the dialog');
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.value = "sushi";
+ button.click();
+
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_equals(dialog.returnValue, "sushi", "Return the updated value");
+}, 'form submission should return correct value');
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.removeAttribute("value");
+ button.click();
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_not_equals(dialog.returnValue, undefined, "returnValue should not be set");
+}, "no returnValue when there's no result.");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('input');
+ let expectedReturnValue = "";
+ button.addEventListener('click', function(event) {
+ expectedReturnValue = event.offsetX + "," + event.offsetY;
+ });
+ await test_driver.click(button);
+
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_not_equals(dialog.returnValue, "", "returnValue shouldn't be empty string");
+ assert_equals(dialog.returnValue, expectedReturnValue, "returnValue should be the offsets of the click");
+}, "input image button should return the coordinates");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+ const button = document.getElementById('confirmImgBtn2');
+ await test_driver.click(button);
+ assert_false(dialog.open, "dialog should be closed now");
+}, "formmethod attribute should use dialog form submission");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.returnValue = "";
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.value = "sushi";
+
+ const dialogForm = document.getElementById('dialogForm');
+ dialogForm.onsubmit = function() {
+ dialog.close();
+ }
+
+ button.click();
+ assert_false(dialog.open, "dialog should be closed now");
+ // If the submission request got processed, the returnValue should change
+ // to "sushi" because that's the value of the submitter
+ assert_equals(dialog.returnValue, "", "dialog's returnValue remains the same");
+}, "closing the dialog while submitting should stop the submission");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.returnValue = undefined;
+ dialog.showModal();
+
+ let submitEvent = false;
+ const dialogForm = document.getElementById('dialogForm');
+ dialogForm.onsubmit = function() {
+ submitEvent = true;
+ assert_false(dialog.open, "dialog should be closed");
+ assert_equals(dialog.returnValue, "", "dialog's returnValue remains the same");
+ };
+
+ const button = document.querySelector('button');
+ button.value = "sushi";
+ button.onclick = function() {
+ dialogForm.submit();
+ assert_false(dialog.open, "dialog should be closed now");
+ // The returnValue should be "" because there is no submitter
+ assert_equals(dialog.returnValue, "", "returnValue shouldn be empty string");
+ };
+
+ button.click();
+ assert_true(submitEvent, "Should have submit event");
+ assert_false(dialog.open, "dialog should be closed");
+ assert_equals(dialog.returnValue, "", "dialog's returnValue remains the same");
+}, "calling form.submit() in click handler of submit button should start the submission synchronously");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html
new file mode 100644
index 0000000000..864420b9d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ body { margin: 0 }
+ dialog {
+ width: 100%;
+ height: 100%;
+ max-width: 100%;
+ max-height: 100%;
+ box-sizing: border-box;
+ padding: 0;
+ }
+ dialog::backdrop {
+ display: none;
+ }
+</style>
+<dialog id=dialog>Something</dialog>
+<script>
+test(function() {
+ let dialog = document.getElementById("dialog");
+ dialog.showModal();
+ assert_equals(
+ document.elementFromPoint(10, 10),
+ dialog,
+ "Dialog is hittable by default",
+ );
+ dialog.inert = true;
+ assert_not_equals(
+ document.elementFromPoint(10, 10),
+ dialog,
+ "Dialog becomes inert dynamically",
+ );
+ dialog.close();
+ dialog.showModal();
+ assert_not_equals(
+ document.elementFromPoint(10, 10),
+ dialog,
+ "Dialog remains inert after open",
+ );
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html
new file mode 100644
index 0000000000..4a50b13c87
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cancel event with preventDefault on keydown event for dialog element</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test cancel event with preventDefault on keydown event for dialog element</p>
+<dialog>
+ <p>Hello World</p>
+</dialog>
+<script>
+ setup({ single_test: true });
+
+ var hasCancelEventFired = false;
+
+ const dialog = document.querySelector("dialog");
+
+ const verify = () => {
+ assert_false(hasCancelEventFired, "cancel should not be fired");
+ assert_true(hasKeydownEventFired, "document level keydown event should be fired");
+ done();
+ };
+
+ dialog.addEventListener("cancel", function(event) {
+ hasCancelEventFired = true;
+ });
+
+ document.addEventListener("keydown", function(event) {
+ hasKeydownEventFired = true;
+ event.preventDefault();
+ step_timeout(function() {
+ verify();
+ }, 0);
+ });
+ dialog.showModal();
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-no-throw-requested-state.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-no-throw-requested-state.html
new file mode 100644
index 0000000000..c86cbe84a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-no-throw-requested-state.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9142">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog>hello</dialog>
+
+<script>
+test(() => {
+ const dialog = document.querySelector('dialog');
+
+ // calling close() on a dialog that is already closed should not throw.
+ dialog.close();
+
+ dialog.show();
+ // calling show() on a dialog that is already showing non-modal should not throw.
+ dialog.show();
+ assert_throws_dom('InvalidStateError', () => dialog.showModal(),
+ 'Calling showModal() on a dialog that is already showing non-modal should throw.');
+ dialog.close();
+
+ dialog.showModal();
+ assert_throws_dom('InvalidStateError', () => dialog.show(),
+ 'Calling show() on a dialog that is already showing modal should throw.');
+ // calling showModal() on a dialog that is already showing modal should not throw.
+ dialog.showModal();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html
new file mode 100644
index 0000000000..fe3fab8ebb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ const dialog = document.createElement("dialog");
+ dialog.show();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html
new file mode 100644
index 0000000000..79120d07eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=90931">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog id=mydialog>It's my dialog.</dialog>
+
+<script>
+test(() => {
+ const dialog = document.getElementById('mydialog');
+ let computedStyle = window.getComputedStyle(dialog, null);
+ assert_equals(computedStyle.getPropertyValue('display'), 'none');
+
+ dialog.show();
+ computedStyle = window.getComputedStyle(dialog, null);
+ assert_equals(computedStyle.getPropertyValue('display'), 'block');
+
+ dialog.close();
+ computedStyle = window.getComputedStyle(dialog, null);
+
+ assert_equals(computedStyle.getPropertyValue('display'), 'none');
+ dialog.close();
+}, "Tests that dialog is visible after show() is called and not visible after close() is called.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html
new file mode 100644
index 0000000000..e1f4c6ab82
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>dialog element: open</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/forms.html#dom-dialog-open">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<dialog id="d1">
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d2" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<script>
+ var d1 = document.getElementById('d1');
+ var d2 = document.getElementById('d2');
+
+ test(function(){
+ assert_false(d1.open);
+ assert_true(d2.open);
+ }, "On getting, the IDL open attribute must return true if the content open attribute is set, and false if it is absent.");
+
+ test(function(){
+ d1.open = true;
+ assert_true(d1.hasAttribute("open"));
+ d2.open = false;
+ assert_false(d2.hasAttribute("open"));
+ }, "On setting, the content open attribute must be removed if the IDL open attribute is set to false, and must be present if the IDL open attribute is set to true.");
+
+ async_test(function(t){
+ d2.open = true;
+ assert_true(d2.hasAttribute("open"));
+ d2.onclose = t.unreached_func("close event should not be fired when just setting the open attribute");
+ d2.open = false;
+ assert_false(d2.hasAttribute("open"));
+
+ // close event is async, give it a chance to be fired
+ t.step_timeout(function() {
+ t.done();
+ }, 0);
+ }, "On setting it to false, the close event should not be fired");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay-re-add-during-transition.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay-re-add-during-transition.html
new file mode 100644
index 0000000000..30d104a973
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay-re-add-during-transition.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>dialog: close and re-add modal dialog during overlay transition</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel="help" href="https://drafts.csswg.org/css-position-4/#overlay">
+<link rel="match" href="pass-dialog-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<dialog id="dialog1">PASS</dialog>
+<dialog id="dialog2">FAIL</dialog>
+<style>
+ dialog::backdrop { background-color: black; }
+ #dialog1 {
+ transition-property: overlay, display;
+ transition-duration: 100s;
+ }
+</style>
+<script>
+ const dialog1 = document.getElementById("dialog1");
+ const dialog2 = document.getElementById("dialog2");
+
+ dialog1.showModal();
+ dialog2.showModal();
+ dialog1.close();
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ // dialog1 no longer "in top layer" even if rendered in top-layer, should
+ // be added as last top layer element.
+ dialog1.showModal();
+ takeScreenshot();
+ })
+ );
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html
new file mode 100644
index 0000000000..9cd2426b8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>dialog: overlay</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel="help" href="https://drafts.csswg.org/css-position-4/#overlay">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<dialog id="dialog"></dialog>
+<script>
+ const dialog = document.getElementById("dialog");
+
+ test(() => {
+ assert_equals(getComputedStyle(dialog).overlay, "none",
+ "Computed overlay");
+ assert_equals(getComputedStyle(dialog, "::backdrop").overlay, "none",
+ "Computed overlay for ::backdrop");
+ }, "dialog computed overlay initially 'none'");
+
+ test(() => {
+ dialog.showModal();
+
+ assert_equals(getComputedStyle(dialog).overlay, "auto",
+ "Computed overlay on open dialog");
+ // ::backdrop pseudo element is always rendered in the top layer when its
+ // originating element is. It does not get its overlay property changed,
+ // though.
+ assert_equals(getComputedStyle(dialog, "::backdrop").overlay, "none",
+ "Computed overlay for ::backdrop");
+
+ dialog.close();
+
+ assert_equals(getComputedStyle(dialog).overlay, "none",
+ "Computed overlay on closed dialog");
+ assert_equals(getComputedStyle(dialog, "::backdrop").overlay, "none",
+ "Computed overlay for ::backdrop");
+ }, "Opening and closing a modal dialog changes computed overlay to 'auto' and back to 'none'");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html
new file mode 100644
index 0000000000..2a80de65a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<dialog></dialog>
+<script>
+test(function() {
+ dialog = document.querySelector('dialog');
+ assert_equals(dialog.returnValue, '');
+
+ dialog.returnValue = 'Setting value directly';
+ assert_equals(dialog.returnValue, 'Setting value directly');
+
+ dialog.returnValue = null;
+ assert_equals(dialog.returnValue, 'null');
+
+ dialog.returnValue = '';
+ assert_equals(dialog.returnValue, '');
+
+ dialog.returnValue = 7;
+ assert_equals(dialog.returnValue, '7');
+
+ dialog.show();
+ dialog.close('Return value set from close()');
+ assert_equals(dialog.returnValue, 'Return value set from close()');
+
+ dialog.show();
+ dialog.close('');
+ assert_equals(dialog.returnValue, '');
+
+ dialog.show();
+ dialog.close(null);
+ assert_equals(dialog.returnValue, 'null');
+
+ dialog.returnValue = 'Should not change because no argument to close()';
+ dialog.show();
+ dialog.close();
+ assert_equals(dialog.returnValue, 'Should not change because no argument to close()');
+
+ dialog.returnValue = 'Should not change because of undefined argument to close()';
+ dialog.show();
+ dialog.close(undefined);
+ assert_equals(dialog.returnValue, 'Should not change because of undefined argument to close()');
+
+ dialog.returnValue = 'Should not change because of no-op close()';
+ dialog.close('blah');
+ assert_equals(dialog.returnValue, 'Should not change because of no-op close()');
+}, "Tests dialog.returnValue is settable and returns the last value set.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html
new file mode 100644
index 0000000000..54c2edab6b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<iframe id="frame"></iframe>
+<script>
+ async_test(function(t) {
+ onload = t.step_func(() => {
+ const host = document.createElement("div");
+ frame.appendChild(host);
+ frame.contentDocument.body.innerHTML = "<dialog></dialog>";
+ document.body.offsetTop;
+ const root = host.attachShadow({mode: 'open'});
+ root.innerHTML = "<content>";
+ const dialog = frame.contentDocument.querySelector("dialog");
+ dialog.showModal();
+ t.done();
+ });
+ }, "Dialog.showModal() called when we have a dirty shadow distribution should not crash.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html
new file mode 100644
index 0000000000..c2350c3042
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>dialog element: removing from document after showModal()</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-showmodal">
+<link rel=help href="https://fullscreen.spec.whatwg.org/#removing-steps">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<dialog></dialog>
+<script>
+async_test(t => {
+ const dialog = document.querySelector('dialog')
+ dialog.showModal()
+ assert_true(dialog.open)
+ // The dialog element is now in top layer. Removing it should synchronously
+ // remove it from top layer, but should leave it in a strange limbo state.
+ dialog.addEventListener('close', t.unreached_func('close event'))
+ dialog.remove()
+ assert_true(dialog.open)
+ // if an event was queued, it would fire before this timeout
+ step_timeout(t.step_func_done(() => {
+ assert_true(dialog.open)
+ // pass if no close event was fired
+ }))
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html
new file mode 100644
index 0000000000..47612e759e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html
@@ -0,0 +1,188 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element: showModal()</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<button id="b0">OK</button>
+<dialog id="d1">
+ <p>foobar</p>
+ <button id="b1">OK</button>
+</dialog>
+<dialog id="d2" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d3">
+ <p>foobar</p>
+ <button id="b3">OK</button>
+</dialog>
+<dialog id="d4">
+ <p>foobar</p>
+ <button id="b4">OK</button>
+</dialog>
+<dialog id="d5">
+ <p>foobar</p>
+ <button id="b5">OK</button>
+</dialog>
+<dialog id="d6"></dialog>
+<dialog id="d7">
+ <input id="i71" value="foobar">
+ <input id="i72" value="foobar">
+ <button id="b7">OK</button>
+</dialog>
+<dialog id="d8">
+ <input id="i81" value="foobar">
+ <input id="i82" value="foobar" autofocus>
+ <button id="b8">OK</button>
+</dialog>
+<dialog id="d9"></dialog>
+<dialog id="d10"></dialog>
+<dialog id="d11"></dialog>
+<script>
+ var d1 = document.getElementById('d1'),
+ d2 = document.getElementById('d2'),
+ d3 = document.getElementById('d3'),
+ d4 = document.getElementById('d4'),
+ d5 = document.getElementById('d5'),
+ d6 = document.getElementById('d6'),
+ d7 = document.getElementById('d7'),
+ d8 = document.getElementById('d8'),
+ d9 = document.getElementById('d9'),
+ d10 = document.getElementById('d10'),
+ d11 = document.getElementById('d11'),
+ b0 = document.getElementById('b0'),
+ b1 = document.getElementById('b1'),
+ b3 = document.getElementById('b3'),
+ b4 = document.getElementById('b4'),
+ b5 = document.getElementById('b5');
+
+ test(function(){
+ assert_false(d1.open);
+ assert_false(d1.hasAttribute("open"));
+ assert_equals(getComputedStyle(d1).display, "none");
+ d1.showModal();
+ this.add_cleanup(function() { d1.close(); });
+ assert_true(d1.open);
+ assert_equals(d1.getAttribute("open"), "");
+ assert_equals(getComputedStyle(d1).display, "block");
+ assert_equals(document.activeElement, b1);
+ });
+
+ test(function(){
+ this.add_cleanup(function() { d2.close(); });
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ d2.showModal();
+ });
+ }, "showModal() on a <dialog> that already has an open attribute throws an InvalidStateError exception");
+
+ test(function(){
+ d9.showModal();
+ this.add_cleanup(function() { d9.close(); });
+ assert_true(d9.open);
+ d9.removeAttribute("open");
+ assert_false(d9.open);
+ d9.showModal();
+ assert_true(d9.open);
+ }, "showModal() on a <dialog> after initial showModal() and removing the open attribute");
+
+ test(function(){
+ var d = document.createElement("dialog");
+ this.add_cleanup(function() { d.close(); });
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ d.showModal();
+ });
+ }, "showModal() on a <dialog> not in a Document throws an InvalidStateError exception");
+
+ test(function(){
+ assert_false(d3.open);
+ assert_false(d4.open);
+ assert_false(d5.open);
+ d3.showModal();
+ this.add_cleanup(function() { d3.close(); });
+ d4.showModal();
+ this.add_cleanup(function() { d4.close(); });
+ d5.showModal();
+ this.add_cleanup(function() { d5.close(); });
+ assert_true(d3.open);
+ assert_true(d4.open);
+ assert_true(d5.open);
+ }, "when opening multiple dialogs, only the newest one is non-inert");
+
+ test(function(){
+ assert_false(d6.open);
+ d6.showModal();
+ this.add_cleanup(function() { d6.close(); });
+ assert_true(d6.open);
+ assert_equals(document.activeElement, d6);
+ }, "opening dialog without focusable children");
+
+ test(function(){
+ assert_false(d7.open);
+ d7.showModal();
+ this.add_cleanup(function() { d7.close(); });
+ assert_true(d7.open);
+ assert_equals(document.activeElement, document.getElementById("i71"));
+ }, "opening dialog with multiple focusable children");
+
+ test(function(){
+ assert_false(d8.open);
+ d8.showModal();
+ this.add_cleanup(function() { d8.close(); });
+ assert_true(d8.open);
+ assert_equals(document.activeElement, document.getElementById("i82"));
+ }, "opening dialog with multiple focusable children, one having the autofocus attribute");
+
+ test(function(){
+ assert_false(d10.open);
+ assert_false(d11.open);
+ d10.showModal();
+ this.add_cleanup(function() { d10.close(); });
+ d11.showModal();
+ this.add_cleanup(function() { d11.close(); });
+ var rect10 = d10.getBoundingClientRect();
+ var rect11 = d11.getBoundingClientRect();
+
+ // The two <dialog>s are both in top layer, with the same position/size.
+ assert_equals(rect10.left, rect11.left);
+ assert_equals(rect10.top, rect11.top);
+ assert_equals(rect10.width, rect11.width);
+ assert_equals(rect10.height, rect11.height);
+
+ var pointX = rect10.left + rect10.width / 2,
+ pointY = rect10.top + rect10.height / 2;
+ function topElement() {
+ return document.elementFromPoint(pointX, pointY);
+ }
+
+ // d11 was most recently openened, and thus on top.
+ assert_equals(topElement(), d11);
+
+ // Removing the open attribute and running through the showModal() algorithm
+ // again should promote d10 to the top.
+ d10.removeAttribute("open");
+ assert_equals(topElement(), d11);
+ d10.showModal();
+ assert_equals(topElement(), d10);
+
+ // Closing d11 with close() should cause d10 to be the topmost element.
+ d11.close();
+ assert_equals(topElement(), d10);
+ }, "when opening multiple dialogs, the most recently opened is rendered on top");
+
+ test(function() {
+ assert_false(d11.open);
+ d11.parentNode.removeChild(d11);
+ assert_throws_dom("INVALID_STATE_ERR", () => d11.showModal());
+
+ const doc = document.implementation.createHTMLDocument();
+ doc.body.appendChild(d11);
+ this.add_cleanup(() => document.body.append(d11));
+ assert_false(d11.open);
+ d11.showModal();
+ assert_true(d11.open);
+ this.add_cleanup(() => d11.close());
+ }, "Although the document is not attached to any pages, showModal() should execute as normal.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html
new file mode 100644
index 0000000000..4b31dc7062
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<body>
+Test that ::backdrop is not shown for non-open or non-modal dialogs.
+The test passes if there is no red shown.
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html
new file mode 100644
index 0000000000..fec4ba8587
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<link rel="match" href="dialogs-with-no-backdrop-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<style>
+dialog::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+
+#display-none-backdrop::backdrop {
+ display: none;
+}
+</style>
+<body>
+Test that ::backdrop is not shown for non-open or non-modal dialogs.
+The test passes if there is no red shown.
+<dialog id="never-opened-dialog"></dialog>
+<dialog id="display-none-dialog" style="display: none"></dialog>
+<dialog id="non-modal-dialog" style="visibility: hidden"></dialog>
+<dialog id="display-none-backdrop" style="visibility: hidden"></dialog>
+<dialog id="closed-dialog"></dialog>
+<dialog id="removed-dialog"></dialog>
+<script>
+document.getElementById('display-none-dialog').showModal();
+document.getElementById('non-modal-dialog').show();
+document.getElementById('display-none-backdrop').showModal();
+
+var closedDialog = document.getElementById('closed-dialog');
+closedDialog.showModal();
+closedDialog.close();
+
+var removedDialog = document.getElementById('removed-dialog');
+removedDialog.showModal();
+removedDialog.parentNode.removeChild(removedDialog);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html
new file mode 100644
index 0000000000..7e6112b3ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<style>
+dialog {
+ outline: none;
+}
+#non-modal {
+ position: static;
+}
+</style>
+<p>Test that a non-top layer element doesn't share style with a top layer
+element. The test passes if you see two boxes.</p>
+<dialog id="non-modal" open></dialog>
+<dialog id="modal"></dialog>
+<script>
+document.querySelector('#modal').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html
new file mode 100644
index 0000000000..e4f4ce50b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<link rel="match" href="dont-share-style-to-top-layer-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ position: static;
+}
+#modal {
+ outline: none;
+}
+</style>
+<p>Test that a non-top layer element doesn't share style with a top layer
+element. The test passes if you see two boxes.</p>
+<dialog open></dialog>
+<dialog id="modal"></dialog>
+<script>
+document.querySelector('#modal').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html
new file mode 100644
index 0000000000..c0b64e68bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.green {
+ color: green;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/106538">106538</a>: Top layer fails for inline elements</p>
+<p>This tests that position 'static' no longer computes to 'absolute' for an
+element that has been removed from the top layer. The test passes if you see
+a single line of text.</p>
+<span class="green">This is the span.</span>
+<span class="green">This is the dialog following it.</span>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html
new file mode 100644
index 0000000000..0dead33163
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="element-removed-from-top-layer-has-original-position-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+.green {
+ color: green;
+}
+
+#right-dialog {
+ display: inline;
+ position: static;
+ border: none;
+ padding: 0;
+ margin: 0;
+ outline: none;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/106538">106538</a>: Top layer fails for inline elements</p>
+<p>This tests that position 'static' no longer computes to 'absolute' for an
+element that has been removed from the top layer. The test passes if you see
+a single line of text.</p>
+<span class="green">This is the span.</span>
+<dialog class="green" id="right-dialog">This is the dialog following it.</dialog>
+<script>
+var dialog = document.getElementById('right-dialog');
+dialog.showModal();
+dialog.close();
+dialog.show();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html
new file mode 100644
index 0000000000..5ee64fc1d9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+#dialog {
+ outline: none;
+}
+</style>
+</head>
+<body>
+<div style="contain: layout">
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+</div>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html
new file mode 100644
index 0000000000..d973c0876d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+</head>
+<style>
+</style>
+<body>
+<div class="pseudodialog" style="position: fixed">
+Dialog should be centered.
+</div>
+<div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html
new file mode 100644
index 0000000000..2bc294be2f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+#dialog {
+ outline: none;
+}
+</style>
+</head>
+<body>
+<svg>
+ <foreignObject>
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+ </foreignObject>
+</svg>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.tentative.html
new file mode 100644
index 0000000000..527d508252
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+#dialog {
+ outline: none;
+}
+</style>
+</head>
+<body>
+<div style="scale: 1">
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+</div>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.tentative.html
new file mode 100644
index 0000000000..e9db7321cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+#dialog {
+ outline: none;
+}
+</style>
+</head>
+<body>
+<div style="will-change: transform">
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+</div>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html
new file mode 100644
index 0000000000..93baf65cf6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Test focus is moved to the previously focused element when dialog is closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<input />
+<dialog>
+ <button id="button1">This is a button1</button>
+ <button id="button2">This is a button2</button>
+ <button id="button3">This is a button3</button>
+</dialog>
+<script>
+
+// Test focus is moved to the previously focused element
+function test_move_to_previously_focused(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+ dialog.close();
+
+ assert_equals(document.activeElement, input);
+}
+
+// Test focus is moved to the previously focused element with some complex dialog usage
+async function test_move_to_previously_focused_with_complex_dialog_usage(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ const button1 = document.getElementById("button1");
+ const button2 = document.getElementById("button2");
+ const button3 = document.getElementById("button3");
+
+ await test_driver.click(button1);
+ await test_driver.click(button2);
+ await test_driver.click(button3);
+
+ dialog.close();
+
+ assert_equals(document.activeElement, input);
+}
+
+// Test focus is moved to the previously focused element even if that element moved in between
+function test_element_move_in_between_show_close(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+
+ assert_equals(input.nextElementSibling, dialog, "Element is in correct position");
+
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ document.body.appendChild(input);
+ assert_not_equals(input.nextElementSibling, dialog, "Element should have moved");
+
+ dialog.close();
+ assert_equals(document.activeElement, input, "Focus should be restored to previously focused input");
+
+ // Clean up
+ document.body.insertBefore(input, dialog);
+}
+
+// Test focus is moved to the previously focused element even if that element moved to shadow root in between
+function test_element_move_to_shadow_root_in_between_show_close(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+
+ assert_equals(input.nextElementSibling, dialog, "Element is in correct position");
+
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ const shadowHost = document.createElement("div");
+ const shadowRoot = shadowHost.attachShadow({mode: "open"});
+ shadowRoot.appendChild(input);
+ document.body.appendChild(shadowHost);
+
+ assert_not_equals(input.nextElementSibling, dialog, "Element should have moved");
+
+ dialog.close();
+ assert_equals(shadowRoot.activeElement, input, "Focus should be restored to previously focused input");
+ assert_equals(document.activeElement, shadowHost, "document.activeElement should be previously focused input's shadow DOM host");
+
+ // Clean up
+ document.body.insertBefore(input, dialog);
+ shadowHost.remove();
+}
+
+// Test focus is moved to <body> if the previously focused
+// element can't be focused
+function test_move_to_body_if_fails(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+ dialog.close();
+ input.remove();
+ assert_equals(document.activeElement, document.body);
+
+ // Clean up
+ document.body.insertBefore(input, dialog);
+}
+
+// Test focus is moved to shadow host if the previously
+// focused element is a shadow node.
+function test_move_to_shadow_host(showModal) {
+ const shadowHost = document.createElement("div");
+
+ const shadowRoot = shadowHost.attachShadow({mode: "open"});
+ shadowRoot.appendChild(document.createElement("input"));
+
+ document.body.appendChild(shadowHost);
+ const inputElement = shadowRoot.querySelector("input");
+ inputElement.focus();
+
+ assert_equals(document.activeElement, shadowHost);
+ assert_equals(shadowRoot.activeElement, inputElement);
+
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+ dialog.close();
+
+ assert_equals(document.activeElement, shadowHost);
+ assert_equals(shadowRoot.activeElement, inputElement);
+
+ // Clean up
+ shadowHost.remove();
+}
+
+// Test moving the focus doesn't scroll the viewport
+async function test_move_focus_dont_scroll_viewport(showModal) {
+ const outViewPortButton = document.createElement("button");
+ outViewPortButton.style.top = (window.innerHeight + 10).toString() + "px";
+ outViewPortButton.style.position = "absolute";
+ document.body.appendChild(outViewPortButton);
+
+ await new Promise(resolve => {
+ document.addEventListener("scroll", resolve, { once: true });
+ outViewPortButton.focus();
+ });
+
+ // Since the outViewPortButton is focused, so the viewport should be
+ // scrolled to it
+ assert_true(document.documentElement.scrollTop > 0 );
+
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ window.scrollTo(0, 0);
+ assert_equals(document.documentElement.scrollTop, 0);
+
+ dialog.close();
+ assert_equals(document.documentElement.scrollTop, 0);
+
+ assert_equals(document.activeElement, outViewPortButton);
+}
+
+test(() => {
+ test_move_to_previously_focused(true);
+ test_move_to_previously_focused(false);
+}, "Focus should be moved to the previously focused element (Simple dialog usage)");
+
+promise_test(async () => {
+ await test_move_to_previously_focused_with_complex_dialog_usage(true);
+ await test_move_to_previously_focused_with_complex_dialog_usage(false);
+}, "Focus should be moved to the previously focused element (Complex dialog usage)");
+
+test(() => {
+ test_element_move_in_between_show_close(true);
+ test_element_move_in_between_show_close(false);
+}, "Focus should be moved to the previously focused element even if it has moved in between show/close");
+
+test(() => {
+ test_element_move_to_shadow_root_in_between_show_close(true);
+ test_element_move_to_shadow_root_in_between_show_close(false);
+}, "Focus should be moved to the previously focused element even if it has moved to shadow DOM root in between show/close");
+
+test(() => {
+ test_move_to_body_if_fails(true);
+ test_move_to_body_if_fails(false);
+}, "Focus should be moved to the body if the previously focused element is removed");
+
+test(() => {
+ test_move_to_shadow_host(true);
+ test_move_to_shadow_host(false);
+}, "Focus should be moved to the shadow DOM host if the previouly focused element is a shadow DOM node");
+
+promise_test(async () => {
+ await test_move_focus_dont_scroll_viewport(true);
+ await test_move_focus_dont_scroll_viewport(false);
+}, "Focus should not scroll if the previously focused element is outside the viewport");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-previous-iframe.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-previous-iframe.tentative.html
new file mode 100644
index 0000000000..c31daa4876
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-previous-iframe.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Test focus is moved to the previously focused element when dialog is closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<dialog>Dialog in parent</dialog>
+
+<iframe srcdoc="<input><dialog> Dialog in child </dialog>"></iframe>
+
+<input>
+<script>
+test(() => {
+ window.onload = function() {
+ const iframe = document.querySelector("iframe");
+ const input = iframe.contentDocument.querySelector("input");
+ // <input> in the child document is focused
+ input.focus();
+
+ const dialog = document.querySelector("dialog");
+ // <dialog> in the parent document is opened
+ dialog.showModal();
+ dialog.close();
+
+ assert_equals(document.activeElement, iframe);
+ assert_equals(iframe.contentDocument.activeElement, input);
+ }
+}, "Focus should move back from parent document to child document");
+
+test(() => {
+ window.onload = function() {
+ const iframe = document.querySelector("iframe");
+ const input = document.querySelector("input");
+ // <input> in the parent document is focused
+ input.focus();
+
+ const dialog = iframe.contentDocument.querySelector("dialog");
+
+ // <dialog> in the child document is focused
+ dialog.showModal();
+ dialog.close();
+
+ assert_equals(document.activeElement, input);
+ }
+}, "Focus should move back from child document to parent document");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html
new file mode 100644
index 0000000000..cd23c32a06
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+body { background: red; }
+
+.backdrop {
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.backdrop,
+.pseudodialog {
+ background: green;
+}
+</style>
+<body>
+<div class="backdrop"></div>
+<div class="pseudodialog">PASS if no red shows</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html
new file mode 100644
index 0000000000..b3b0c0a929
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+button {
+ color: green;
+}
+
+button:disabled {
+ color: red;
+}
+
+.trigger-style-recalc {
+ /* No change, we just need a new style recalculation. */
+ font-weight:bold;
+}
+</style>
+</head>
+<body style="color: green">
+<button>The test passes if this is in green.</button>
+<dialog></dialog>
+<script>
+"use strict";
+test(function() {
+ document.querySelector('dialog').showModal();
+ var button = document.querySelector('button');
+ button.classList.add('trigger-style-recalc');
+ var color = document.defaultView.getComputedStyle(button).getPropertyValue('color');
+ assert_equals(color, 'rgb(0, 128, 0)');
+}, "Tests inert elements do not match the :disabled selector.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html
new file mode 100644
index 0000000000..2ccc133285
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=242848">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe height=400 width=600 id="main-iframe">
+ <frameset rows="*" cols="50,50">
+ <frame src="resources/inert-focus-in-frames-frame1.html">
+ <frame src="resources/inert-focus-in-frames-frame2.html">
+ </frameset>
+</iframe>
+
+<script>
+let framesLoadedResolver = null;
+const framesLoadedPromise = new Promise(resolve => framesLoadedResolver = resolve);
+framesLoaded = 0;
+numFrames = 4;
+
+function frameLoaded() {
+ framesLoaded++;
+ if (framesLoaded == numFrames)
+ framesLoadedResolver();
+}
+var mainIframe = document.getElementById('main-iframe');
+mainIframe.contentDocument.write(mainIframe.textContent);
+mainIframe.contentDocument.close();
+mainIframe.contentWindow.frames[1].window.onload = frameLoaded;
+window.onload = frameLoaded;
+
+promise_test(async () => {
+ await framesLoadedPromise;
+
+ function testFocus(element, expectFocus) {
+ let focusedElement = null;
+ element.addEventListener('focus', function() { focusedElement = element; }, false);
+ element.focus();
+ if (expectFocus) {
+ assert_equals(focusedElement, element, element.id);
+ } else {
+ assert_not_equals(focusedElement, element, element.id);
+ }
+ }
+
+ // Opening a modal dialog in frame1. It blocks other nodes in its document.
+ const frame1 = mainIframe.contentWindow.frames[0].document;
+ frame1.querySelector('dialog').showModal();
+
+ testFocus(frame1.querySelector('.target'), false);
+ const iframe = frame1.querySelector('#iframe1').contentDocument;
+ testFocus(iframe.querySelector('.target'), true);
+
+ // Even a modal dialog in the iframe is blocked by the modal dialog in the parent frame1.
+ iframe.querySelector('dialog').showModal();
+ testFocus(iframe.querySelector('button'), false);
+
+ // An iframe within a modal dialog can still be focused.
+ var dialogIframe = frame1.querySelector('#iframe-in-dialog').contentDocument;
+ testFocus(dialogIframe.querySelector('.target'), true);
+
+ // A modal dialog does not block nodes in a sibling frame.
+ var frame2 = mainIframe.contentWindow.frames[1].document;
+ testFocus(frame2.querySelector('.target'), true);
+
+ // Closing the dialog in frame1. The modal dialog in the iframe does not block nodes in its parent.
+ frame1.querySelector('dialog').close();
+ testFocus(iframe.querySelector('.target'), false);
+ testFocus(frame1.querySelector('.target'), true);
+
+}, 'Tests inert node focusing across frames and iframes.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html
new file mode 100644
index 0000000000..5ee4113985
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=241699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, click on all the "Click me"s.
+The test fails if you see red.
+</p>
+
+<style>
+dialog {
+ width: 50px;
+}
+</style>
+
+<a id="a" href="javascript:void(0)">Click me</a>
+<button id="button">Click me</button>
+<div id="div" style="background-color: blue; width: 50px; height: 50px">Click meeee</div>
+<span id="span">Click me</span>
+<div id="dialog-parent" style="width: 50px; height: 50px">
+ <span id="dialog-sibling">Click meeee</span>
+ <dialog></dialog>
+</div>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ let absoluteTop = 0;
+ let absoluteLeft = 0;
+ for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = Math.round(absoluteLeft + element.offsetWidth / 2);
+ const y = Math.round(absoluteTop + element.offsetHeight / 2);
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ function eventFiredOnInertElement(e) {
+ e.target.style.background = 'red';
+ inertElementFiredOn = true;
+ }
+
+ inertElements = ['a', 'button', 'div', 'span']
+ inertElements.forEach(function(id) {
+ element = document.getElementById(id);
+ element.addEventListener('click', eventFiredOnInertElement);
+ element.addEventListener('mousemove', eventFiredOnInertElement);
+ });
+
+ document.addEventListener('click', function(e) {
+ document.firedOn = true;
+ });
+
+ document.getElementById('dialog-parent').addEventListener('click', function(e) {
+ e.target.firedOn = true;
+ });
+
+ document.querySelector('dialog').showModal();
+ for (const id of inertElements) {
+ expectedTarget = document;
+ if (id == 'dialog-sibling')
+ expectedTarget = document.getElementById('dialog-parent')
+ element = document.getElementById(id);
+ inertElementFiredOn = false;
+ expectedTarget.firedOn = false;
+ await clickOn(element);
+ assert_false(inertElementFiredOn, 'clicking on ' + id);
+ assert_true(expectedTarget.firedOn, 'clicking on ' + id);
+ }
+}, 'Tests that inert inlines do not receive mouse events.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
new file mode 100644
index 0000000000..61e3ddeaf2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=242848">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<label for="submit">Label for Submit</label>
+<dialog>
+ <input id="text" type="text">
+ <input id="submit" type="submit">
+</dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ let absoluteTop = 0;
+ let absoluteLeft = 0;
+ for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = Math.round(absoluteLeft + element.offsetWidth / 2);
+ const y = Math.round(absoluteTop + element.offsetHeight / 2);
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ document.querySelector('dialog').showModal();
+ document.querySelector('#text').focus();
+
+ label = document.querySelector('label');
+ submit = document.querySelector('#submit');
+ label.focus();
+ assert_equals(document.activeElement, submit,
+ 'label.focus() should send focus to the target.');
+
+ await clickOn(label);
+ assert_not_equals(document.activeElement, label,
+ 'Clicking the label should not focus the label.');
+ assert_not_equals(document.activeElement, submit,
+ 'Clicking the label should not focus the submit input.');
+}, 'Tests focusing of an inert label for a non-inert target.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html
new file mode 100644
index 0000000000..1b757ecf62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body p, span {
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<p>Test that inert nodes are not painted as being selected. The test passes if
+none of the text outside the dialog is highlighted when selected.</p>
+
+<p>Although not shown as selected, the inert nodes are in window.getSelection()
+and copied to the clipboard, which is the same behavior as user-select:
+none (crbug.com/147490).</p>
+
+<br><span>This text shouldn't be highlighted as selected.</span>
+
+<dialog>
+ <div id="selectable">I'm selectable.</div>
+</dialog>
+
+<script>
+dialog = document.querySelector('dialog');
+dialog.showModal();
+selectable = document.querySelector('#selectable');
+window.getSelection().selectAllChildren(selectable);
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html
new file mode 100644
index 0000000000..f6db38ed72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="inert-node-is-not-highlighted-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<p>Test that inert nodes are not painted as being selected. The test passes if
+none of the text outside the dialog is highlighted when selected.</p>
+
+<p>Although not shown as selected, the inert nodes are in window.getSelection()
+and copied to the clipboard, which is the same behavior as user-select:
+none (crbug.com/147490).</p>
+
+<br> <!-- Needed to the trigger the bug. -->
+This text shouldn't be highlighted as selected.
+
+<dialog>
+ <div id="selectable">I'm selectable.</div>
+</dialog>
+
+<script>
+dialog = document.querySelector('dialog');
+dialog.showModal();
+document.execCommand('SelectAll');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html
new file mode 100644
index 0000000000..9141a383b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=252071">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<span id="not-editable" contenteditable>I'm not editable while the dialog is showing.</span>
+<dialog>
+ <span id="editable" contenteditable>I'm editable.</span>
+</dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ let absoluteTop = 0;
+ let absoluteLeft = 0;
+ for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = Math.round(absoluteLeft + element.offsetWidth / 2);
+ const y = Math.round(absoluteTop + element.offsetHeight / 2);
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ dialog = document.querySelector('dialog');
+ dialog.showModal();
+ notEditable = document.querySelector('#not-editable');
+ editable = document.querySelector('#editable');
+
+ await clickOn(notEditable);
+ oldValue = notEditable.textContent;
+ await (new test_driver.Actions().keyDown('a').keyUp('a').send());
+ assert_equals(notEditable.textContent, oldValue);
+
+ await clickOn(editable);
+ oldValue = editable.textContent;
+ await (new test_driver.Actions().keyDown('a').keyUp('a').send());
+ assert_not_equals(editable.textContent, oldValue);
+
+ notEditable.remove();
+ editable.remove();
+}, 'Test that inert nodes cannot be edited. The test passes if the only text you can edit is in the dialog.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html
new file mode 100644
index 0000000000..74379f50e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html id="html" tabindex="1">
+<head>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#blocked-by-a-modal-dialog">
+<meta name="assert" content="Checks that, when opening modal dialogs, inert nodes are not focusable.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body id="body" tabindex="1">
+<style>
+dialog {
+ outline: none;
+}
+</style>
+<dialog id="top-dialog" tabindex="1" style="width: 100px; top: 30px"><button id="top-dialog-button">I get focus</button></dialog>
+<dialog id="bottom-dialog" tabindex="-1" style="width: 100px; bottom: 30px"><button id="bottom-dialog-button">I don't get focus.</button></dialog>
+<div id="container">
+ <input id="text" type="text">
+ <input id="datetime" type="datetime">
+ <input id="color" type="color">
+ <select id="select">
+ <optgroup id="optgroup">
+ <option id="option">Option</option>
+ </optgroup>
+ </select>
+ <div id="contenteditable-div" contenteditable>I'm editable</div>
+ <span id="tabindex-span" tabindex="0">I'm tabindexed.</div>
+ <embed id="embed" type="application/x-blink-test-plugin" width=100 height=100></embed>
+ <a id="anchor" href="">Link</a>
+</div>
+<script>
+"use strict";
+// The test passses if only the topmost dialog and its button are focusable.
+
+function testFocus(element, expectFocus) {
+ test(function() {
+ var focusedElement = null;
+ element.addEventListener('focus', function() { focusedElement = element; }, false);
+ element.focus();
+ var theElement = element;
+ if (expectFocus) {
+ assert_equals(focusedElement, theElement);
+ } else {
+ assert_not_equals(focusedElement, theElement);
+ }
+ }, `#${CSS.escape(element.id)} is ${expectFocus ? "" : "not "} focusable`);
+}
+
+function testTree(element, expectFocus) {
+ if (element.nodeType == Node.ELEMENT_NODE)
+ testFocus(element, expectFocus);
+ var childNodes = element.childNodes;
+ for (var i = 0; i < childNodes.length; i++)
+ testTree(childNodes[i], expectFocus);
+}
+
+var bottomDialog = document.getElementById('bottom-dialog');
+var topDialog = document.getElementById('top-dialog');
+setup(function() {
+ bottomDialog.showModal();
+ topDialog.showModal();
+ add_completion_callback(function() {
+ topDialog.close();
+ bottomDialog.close();
+ });
+});
+
+testFocus(document.documentElement, false);
+testFocus(document.body, false);
+testTree(topDialog, true);
+testTree(bottomDialog, false);
+testTree(document.getElementById('container'), false);
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html
new file mode 100644
index 0000000000..2889e1e90a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=252071">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+Here is a text node you can't select while the dialog is open.
+<dialog>I'm selectable.</dialog>
+
+<script>
+test(() => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+ document.execCommand('SelectAll');
+ assert_equals(window.getSelection().toString(), "I'm selectable.");
+}, 'Test that inert nodes cannot be selected. The test passes if the only text you can select is inside the dialog.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html
new file mode 100644
index 0000000000..579aca7775
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Hit-testing with SVG made inert by modal dialog</title>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert">
+<meta assert="assert" content="SVG made inert by modal dialog should be unreachable with hit-testing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<div id="wrapper">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
+ <rect width="500" height="500" id="target" fill="red">
+ </svg>
+</div>
+
+<dialog id="dialog">Content behind the open modal dialog should not be clickable</dialog>
+
+<style>
+dialog::backdrop {
+ display: none;
+}
+</style>
+
+<script>
+const dialog = document.getElementById("dialog");
+const wrapper = document.getElementById("wrapper");
+const target = document.getElementById("target");
+
+promise_test(async function() {
+ dialog.showModal();
+ this.add_cleanup(() => dialog.close());
+
+ let reachedTarget = false;
+ target.addEventListener("mousedown", () => {
+ reachedTarget = true;
+ }, { once: true });
+
+ let wrapperRect = wrapper.getBoundingClientRect();
+ await new test_driver.Actions()
+ .pointerMove(wrapperRect.x + 1, wrapperRect.y + 1, { origin: "viewport" })
+ .pointerDown()
+ .send();
+
+ assert_false(target.matches(":active"), "target is not active");
+ assert_false(target.matches(":hover"), "target is not hovered");
+ assert_false(reachedTarget, "target didn't get event");
+}, "Hit-testing doesn't reach contents of an inert SVG");
+
+promise_test(async function() {
+ assert_false(dialog.open, "dialog is closed");
+
+ let reachedTarget = false;
+ target.addEventListener("mousedown", () => {
+ reachedTarget = true;
+ }, { once: true });
+
+ await new test_driver.Actions()
+ .pointerMove(0, 0, { origin: wrapper })
+ .pointerDown()
+ .send();
+
+ assert_true(target.matches(":active"), "target is active");
+ assert_true(reachedTarget, "target got event");
+}, "Hit-testing can reach contents of a no longer inert SVG");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html
new file mode 100644
index 0000000000..1a509f7f36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Inertness with modal dialogs and iframes</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert">
+<meta name="assert" content="Checks that a modal dialog marks outer nodes as inert,
+ but only in its document, not in the parent browsing context,
+ nor in nested browsing contexts.">
+<div id="log"></div>
+<div id="wrapper">
+ (main document: outer text)
+ <iframe id="outerIframe" srcdoc="
+ <div id='wrapper'>
+ (outer iframe: outer text)
+ <dialog id='dialog' style='display: block'>
+ (outer iframe: dialog)
+ </dialog>
+ </div>
+ "></iframe>
+ <dialog id="dialog" style="display: block">
+ (main document: dialog)
+ <iframe id="innerIframe" srcdoc="
+ <div id='wrapper'>
+ (inner iframe: outer text)
+ <dialog id='dialog' style='display: block'>
+ (inner iframe: dialog)
+ </dialog>
+ </div>
+ "></iframe>
+ </dialog>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const innerIframeWindow = innerIframe.contentWindow;
+const outerIframeWindow = outerIframe.contentWindow;
+promise_setup(async () => {
+ for (let global of [innerIframeWindow, outerIframeWindow]) {
+ if (global.location.href === "about:blank" ||
+ global.document.readyState !== "complete") {
+ await new Promise(resolve => {
+ global.frameElement.addEventListener("load", resolve, {once: true});
+ });
+ }
+ }
+});
+add_completion_callback(() => {
+ for (let global of [window, innerIframeWindow, outerIframeWindow]) {
+ global.getSelection().removeAllRanges();
+ }
+});
+
+function checkSelection(global, expectedText) {
+ const selection = global.getSelection();
+ selection.selectAllChildren(global.wrapper);
+
+ // Remove whitespace between parentheses since it varies among browsers,
+ // but that's not relevant to this test.
+ const actualText = selection.toString().replace(/\)\s*\(/g, ")(").trim();
+ assert_equals(actualText, expectedText);
+}
+
+function showModals(test, globals) {
+ for (let global of globals) {
+ global.dialog.showModal();
+ test.add_cleanup(() => { global.dialog.close(); });
+ }
+}
+
+promise_test(async function() {
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Initially, no node is inert");
+
+promise_test(async function() {
+ showModals(this, [outerIframeWindow]);
+
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialog in the outer iframe marks outer nodes in that iframe as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow]);
+
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Modal dialog in the inner iframe marks outer nodes in that iframe as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow, outerIframeWindow]);
+
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialogs in both iframes mark outer nodes in these iframes as inert.");
+
+promise_test(async function() {
+ showModals(this, [window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Modal dialog in the main document marks outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow, window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Modal dialogs in the main document and inner iframe mark outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+
+promise_test(async function() {
+ showModals(this, [outerIframeWindow, window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialogs in the main document and outer iframe mark outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow, outerIframeWindow, window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialogs in the main document and both iframes mark outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html
new file mode 100644
index 0000000000..c6bcb5d4ca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=329407">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+#ancestor {
+ position: absolute;
+ height: 50px;
+ width: 50px;
+ top: 200px;
+ left: 100px;
+ border: 1px solid;
+}
+
+dialog {
+ height: 50px;
+ width: 50px;
+ top: 200px;
+ left: 200px;
+ margin: 0;
+}
+
+dialog::backdrop {
+ display: none;
+}
+</style>
+
+<div id="ancestor">
+ <dialog></dialog>
+</div>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const rect = element.getBoundingClientRect();
+ const actions = new test_driver.Actions()
+ .pointerMove(rect.left + rect.width / 2, rect.top + rect.height / 2)
+ .pointerDown()
+ .pointerUp();
+ await actions.send();
+ }
+
+ const div = document.querySelector('#ancestor');
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const handledEvent = {};
+ document.addEventListener('click', function(event) {
+ handledEvent['document'] = true;
+ });
+
+ document.body.addEventListener('click', function(event) {
+ handledEvent['body'] = true;
+ // body should get a event only via bubbling.
+ if (event.target != dialog) {
+ assert_unreached('body was targeted for an click event');
+ div.style.backgroundColor = 'red';
+ }
+ });
+
+ div.addEventListener('click', function(event) {
+ handledEvent['div'] = true;
+ // div should get a event only via bubbling.
+ if (event.target != dialog) {
+ assert_unreached('div was targeted for an click event');
+ div.style.backgroundColor = 'red';
+ }
+ });
+
+ dialog.addEventListener('click', function(event) {
+ handledEvent['dialog'] = true;
+ dialog.style.backgroundColor = 'green';
+ if (event.target != dialog) {
+ assert_unreached('dialog was not targeted for a click event');
+ dialog.style.backgroundColor = 'red';
+ }
+ });
+
+ const nodes = [ 'document', 'body', 'div', 'dialog' ];
+ nodes.map(function(node) { handledEvent[node] = false; });
+ await clickOn(div);
+ assert_true(handledEvent.document, 'Clicking on ancestor.');
+ assert_false(handledEvent.body, 'Clicking on ancestor.');
+ assert_false(handledEvent.dialog, 'Clicking on ancestor.');
+ assert_false(handledEvent.div, 'Clicking on ancestor.');
+ handledEvent.document = false;
+
+ await clickOn(dialog);
+ assert_true(handledEvent.document, 'Clicking on dialog.');
+ assert_true(handledEvent.body, 'Clicking on dialog.');
+ assert_true(handledEvent.dialog, 'Clicking on dialog.');
+ assert_true(handledEvent.div, 'Clicking on dialog.');
+}, 'Test that ancestors of modal dialog are inert.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity-ref.html
new file mode 100644
index 0000000000..b50e6ae026
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.backdrop {
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ background-color: green;
+ opacity: 0.5;
+}
+</style>
+<body>
+<div class="backdrop"></div>
+<div class="pseudodialog">Test passes if you see a green backdrop at half opacity.</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html
new file mode 100644
index 0000000000..09c31ce2af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="modal-dialog-backdrop-opacity-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#user-agent-level-style-sheet-defaults">
+<style>
+dialog::backdrop {
+ background-color: rgb(0, 128, 0);
+ opacity: 0.5;
+}
+dialog:focus {
+ outline: none;
+}
+</style>
+<body>
+<dialog>Test passes if you see a green backdrop at half opacity.</dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html
new file mode 100644
index 0000000000..d703b7f28e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<style>
+.dialog-default-ua-style {
+ position: absolute;
+ overflow: auto;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+}
+
+#dialog {
+ margin: auto;
+ height: 100px;
+ width: 100px;
+ top: 100px;
+ z-index: 1000;
+ background: green;
+}
+
+#backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0,0,0,0.1);
+ z-index: 100;
+}
+</style>
+<body>
+Test for the default user agent style of dialog::backdrop. The test passes if
+there is a green box, above a very lightly translucent gray box spanning the
+viewport.
+<div id="backdrop"></div>
+<div class="dialog-default-ua-style" id="dialog"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html
new file mode 100644
index 0000000000..55d7132f8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="match" href="modal-dialog-backdrop-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#user-agent-level-style-sheet-defaults">
+<style>
+dialog {
+ top: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+ outline: none;
+}
+</style>
+<body>
+Test for the default user agent style of dialog::backdrop. The test passes if
+there is a green box, above a very lightly translucent gray box spanning the
+viewport.
+<dialog></dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html
new file mode 100644
index 0000000000..f6c0ec0ccb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=110952">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, move the mouse to the blue box, click, and then move the
+mouse outside. Then repeat for the red box. The test succeeds if both boxes
+turn green
+</p>
+
+<style>
+#inert-div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+}
+
+dialog {
+ width: 100px;
+}
+
+dialog::backdrop {
+ display: none;
+}
+
+#dialog-div {
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+</style>
+
+<div id="inert-div"></div>
+<dialog id="dialog">
+ <div id="dialog-div"></div>
+</dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const rect = element.getBoundingClientRect();
+ const actions = new test_driver.Actions()
+ .pointerMove(
+ Math.floor(rect.left + rect.width / 2),
+ Math.floor(rect.top + rect.height / 2))
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ dialog.showModal();
+
+ inertDivHandledEvent = false;
+ inertDiv = document.getElementById('inert-div');
+ eventFiredOnInertNode = function(event) {
+ inertDivHandledEvent = true;
+ inertDiv.style.backgroundColor = 'red';
+ };
+
+ events = ['mousedown', 'mouseup', 'click', 'mousemove', 'mouseover', 'mouseout'];
+ dialogDiv = document.getElementById('dialog-div');
+ handledEvents = {};
+ handledEvents.dialogDiv = {};
+ eventFiredOnDialog = function(event) {
+ handledEvents.dialogDiv[event.type] = true;
+ if (Object.keys(handledEvents.dialogDiv).length == events.length)
+ dialogDiv.style.backgroundColor = 'green';
+ };
+
+ handledEvents.document = {};
+ expectedEventCountForDocument = events.length - 1; // document won't get 'mouseout'
+ eventFiredOnDocument = function(event) {
+ handledEvents.document[event.type] = true;
+ if (Object.keys(handledEvents.document).length == document.expectedEventCount && !inertDivHandledEvent) {
+ inertDiv.style.backgroundColor = 'green';
+ }
+ };
+
+ for (let i = 0; i < events.length; ++i) {
+ inertDiv.addEventListener(events[i], eventFiredOnInertNode);
+ dialogDiv.addEventListener(events[i], eventFiredOnDialog);
+ document.addEventListener(events[i], eventFiredOnDocument);
+ }
+
+ await clickOn(inertDiv);
+ assert_false(inertDivHandledEvent, 'Clicking on inert box');
+ assert_equals(Object.keys(handledEvents.document).length, expectedEventCountForDocument, 'Clicking on inert box');
+
+ await clickOn(dialogDiv);
+ assert_false(inertDivHandledEvent, 'Clicking on non-inert box');
+ assert_equals(Object.keys(handledEvents.dialogDiv).length, events.length, 'Clicking on non-inert box');
+}, 'Ensure that mouse events are not dispatched to an inert node.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html
new file mode 100644
index 0000000000..7ac66f5095
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<title>Reference: Test that display: contents; on modal dialog & ::backdrop acts like display: block</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<p>Test passes if there is a green dialog</p>
+<p>Dialog is display:block</p>
+<p>Dialog::backdrop is display:block</p>
+<dialog>Dialog Contents</dialog>
+<style>
+dialog {
+ background-color: green;
+}
+</style>
+<script>
+document.querySelector("dialog").showModal();
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html
new file mode 100644
index 0000000000..032033de01
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<title>Test that display: contents; on modal dialog & ::backdrop acts like display: block</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel="match" href="modal-dialog-display-contents-ref.html">
+<p>Test passes if there is a green dialog</p>
+<p>Dialog is display:<span id="computed-value"></span></p>
+<p>Dialog::backdrop is display:<span id="computed-value-backdrop"></span></p>
+<dialog>Dialog Contents</dialog>
+<style>
+dialog {
+ display: contents;
+ background-color: green;
+}
+dialog::backdrop {
+ display: contents;
+}
+</style>
+<script>
+dialog = document.querySelector("dialog");
+dialog.showModal();
+document.getElementById("computed-value").textContent = getComputedStyle(dialog).display;
+document.getElementById("computed-value-backdrop").textContent = getComputedStyle(dialog, "::backdrop").display;
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html
new file mode 100644
index 0000000000..10c9897c63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<style>
+#dialog {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+
+#dialog-before {
+ position: absolute;
+ top: 0px;
+}
+
+#dialog-after {
+ position: absolute;
+ bottom: 0px;
+}
+
+#dialog-backdrop {
+ position: absolute;
+ top: 100px;
+ left: 300px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+<body>
+Test for a modal dialog with ::before, ::after, and ::backdrop. The test passes
+if there are two green boxes, one with the texts "::before" and "::after" in it.
+<div id="dialog">
+ <div id="dialog-before">::before</div>
+ <div id="dialog-after">::after</div>
+</div>
+<div id="dialog-backdrop"></div>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html
new file mode 100644
index 0000000000..96b97f8ec3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<link rel="match" href="modal-dialog-generated-content-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ padding: 0px;
+ border: none;
+ margin: 0px;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+ outline: none;
+}
+
+dialog::before {
+ content: '::before';
+ position: absolute;
+ top: 0px;
+}
+
+dialog::after {
+ content: '::after';
+ position: absolute;
+ bottom: 0px;
+}
+
+dialog::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 300px;
+ height: 100px;
+ width: 100px;
+ background: green;
+ content: 'THIS TEXT SHOULD NOT BE SEEN';
+}
+
+dialog::backdrop::before {
+ content: '::backdrop::before';
+ position: absolute;
+ top: 0px;
+ background: red;
+}
+dialog::backdrop::after {
+ content: '::backdrop::after';
+ position: absolute;
+ bottom: 0px;
+ background: red;
+}
+</style>
+<body>
+Test for a modal dialog with ::before, ::after, and ::backdrop. The test passes
+if there are two green boxes, one with the texts "::before" and "::after" in it.
+<dialog></dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html
new file mode 100644
index 0000000000..b6c52b7d7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html
new file mode 100644
index 0000000000..f6440583fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Modal dialog inside iframe should not generate box</title>
+<link rel=match href="modal-dialog-in-iframe-ref.html">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/6939">
+<link rel=help href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: red;
+ border-color: red;
+}
+</style>
+<iframe></iframe>
+<script>
+ const iframe = document.querySelector('iframe');
+ const dialog = document.createElement('dialog');
+ iframe.appendChild(dialog);
+ dialog.showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html
new file mode 100644
index 0000000000..38e15c1d79
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<object width="200" type="image/svg+xml" data="../../../../images/100px-green-rect.svg"></object>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html
new file mode 100644
index 0000000000..728748a7ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Modal dialog inside object should not generate box</title>
+<link rel=match href="modal-dialog-in-object-ref.html">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/6939">
+<link rel=help href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+<object width="200" type="image/svg+xml" data="../../../../images/100px-green-rect.svg">
+ <dialog id="dialog"></dialog>
+</object>
+<script>
+document.getElementById('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html
new file mode 100644
index 0000000000..c837503caf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+div {
+ content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPAQMAAAABGAcJAAAAA1BMVEUAgACc+aWRAAAADElEQVR42mNgIAEAAAAtAAH7KhMqAAAAAElFTkSuQmCC);
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see a green square near the top of the viewport.
+<div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html
new file mode 100644
index 0000000000..75727b42f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Modal dialog inside replaced renderer should not generate box</title>
+<link rel="match" href="modal-dialog-in-replaced-renderer-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+div {
+ content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPAQMAAAABGAcJAAAAA1BMVEUAgACc+aWRAAAADElEQVR42mNgIAEAAAAtAAH7KhMqAAAAAElFTkSuQmCC);
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see a green square near the top of the viewport.
+<div>
+<dialog id="dialog"></dialog>
+</div>
+<script>
+document.getElementById('dialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html
new file mode 100644
index 0000000000..0310d1ba24
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see no green rectangle.
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html
new file mode 100644
index 0000000000..3d72826b96
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Modal dialog inside display: table-column should not generate box</title>
+<link rel="match" href="modal-dialog-in-table-column-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+div {
+ display: table-column;
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see no green rectangle.
+<div>
+<dialog id="dialog"></dialog>
+</div>
+<script>
+document.getElementById('dialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html
new file mode 100644
index 0000000000..abba08cfde
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<title>Test that modal dialogs have visibility: visible set from the UA sheet</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3:is-modal">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="visibility: hidden">
+ <dialog>This is a dialog</dialog>
+</div>
+
+<script>
+let dialog = document.querySelector("dialog");
+
+test(t => {
+ dialog.show();
+ t.add_cleanup(() => dialog.close());
+ assert_equals(getComputedStyle(dialog).visibility, "hidden");
+}, "Non-modal dialog should let parent visibility inherit");
+
+test(t => {
+ dialog.showModal();
+ t.add_cleanup(() => dialog.close());
+ assert_equals(getComputedStyle(dialog).visibility, "visible");
+}, "Modal dialog should have visibility: visible by default in UA sheet");
+
+test(t => {
+ dialog.style.visibility = "hidden";
+ dialog.showModal();
+ t.add_cleanup(() => {
+ dialog.style.removeProperty("visibility");
+ dialog.close();
+ });
+ assert_equals(getComputedStyle(dialog).visibility, "hidden");
+}, "Modal dialog visibility should be overridable");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html
new file mode 100644
index 0000000000..638217f021
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:skobes@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=403136">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+.spacer {
+ height: 500px;
+}
+dialog {
+ border: 0;
+ margin: 0;
+ padding: 1px;
+}
+</style>
+<div class="spacer"></div>
+<dialog>
+ <div class="spacer"></div>
+</dialog>
+
+<script>
+test(() => {
+ document.querySelector('dialog').showModal();
+ assert_equals(document.scrollingElement.scrollHeight, window.innerHeight);
+}, 'dialogs should be centered before computing overflow.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html
new file mode 100644
index 0000000000..0242080268
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Content selection in modal dialog</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#content-selection">
+<meta name="assert" content="Checks that text can be selected in a modal dialog, except with 'user-select: none'.">
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+dialog {
+ font: 10px/1 Ahem;
+ text-align: center;
+}
+</style>
+
+<dialog>123456789A</dialog>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script>
+const dialog = document.querySelector("dialog");
+dialog.showModal();
+
+function selectSomeText() {
+ // Clear existing selection.
+ getSelection().removeAllRanges();
+
+ // The dialog contains 10 characters. Select the 6 ones at the center.
+ return new test_driver.Actions()
+ .pointerMove(-3e1, 0, {origin: dialog})
+ .pointerDown()
+ .pointerMove(+3e1, 0, {origin: dialog})
+ .pointerUp()
+ .send();
+}
+
+function clickOnBackdrop() {
+ getSelection().removeAllRanges();
+
+ return new test_driver.Actions()
+ .pointerMove(10, 10)
+ .pointerDown()
+ .pointerUp()
+ .send();
+}
+
+promise_test(async function() {
+ await selectSomeText();
+ assert_equals(getSelection().toString(), "345678");
+}, "By default, text inside a modal dialog can be selected");
+
+promise_test(async function() {
+ await clickOnBackdrop();
+ assert_equals(getSelection().toString(), "");
+}, "Clicking on backdrop doesn't select text");
+
+promise_test(async function() {
+ dialog.style.userSelect = "none";
+
+ await selectSomeText();
+ assert_equals(getSelection().toString(), "");
+
+ dialog.style.userSelect = "";
+}, "'user-select: none' prevents text from being selected");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html
new file mode 100644
index 0000000000..38b628c309
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/103477">103477</a>: Make
+NodeRenderingContext::parentRenderer and nextRenderer top layer aware
+<p>The test passes if you see a green rectangle in the center of the viewport.
+<dialog id="dialog"></dialog>
+<script>
+document.getElementById('dialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html
new file mode 100644
index 0000000000..85cc61890a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="modal-dialog-sibling-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/103477">103477</a>: Make
+NodeRenderingContext::parentRenderer and nextRenderer top layer aware
+<p>The test passes if you see a green rectangle in the center of the viewport.
+<div style="display: none" id="div"></div>
+<dialog id="dialog"></dialog>
+<script>
+document.getElementById('dialog').showModal();
+document.getElementById('dialog').offsetTop; // force a layout/renderer creation
+document.getElementById('div').style.display = 'block';
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html
new file mode 100644
index 0000000000..70bb3810e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ height: 10000px;
+}
+
+dialog {
+ padding: 0;
+ height: 50px;
+ width: 50px;
+}
+
+#console {
+ position: fixed;
+}
+</style>
+
+<dialog id="top-dialog"></dialog>
+<dialog id="first-middle-dialog"></dialog>
+<dialog id="second-middle-dialog" style="left: 100px"></dialog>
+<dialog id="bottom-dialog"></dialog>
+
+<script>
+test(() => {
+ function documentHeight() {
+ // clientHeight is an integer, but we want the correct floating point
+ // value. Start a binary search at clientHeight-1 and clientHeight+1.
+ let min = document.documentElement.clientHeight;
+ let max = min + 1;
+ --min;
+
+ // binary search with media queries to find the correct height
+ for (let iter = 0; iter < 10; ++iter) {
+ let test = (min + max) / 2;
+ if (window.matchMedia(`(min-height: ${test}px)`).matches)
+ min = test;
+ else
+ max = test;
+ }
+ return min;
+ }
+ function expectedTop(dialog) {
+ let height = documentHeight();
+ return (height - dialog.getBoundingClientRect().height) / 2;
+ }
+
+ function showAndTest(id) {
+ dialog = document.getElementById(id);
+ dialog.showModal();
+ assert_approx_equals(dialog.getBoundingClientRect().top, expectedTop(dialog), 0.05, id);
+ }
+
+ showAndTest('top-dialog');
+
+ window.scroll(0, 100);
+ showAndTest('first-middle-dialog');
+ showAndTest('second-middle-dialog');
+
+ window.scroll(0, 200);
+ showAndTest('bottom-dialog');
+}, 'Test that multiple dialogs are centered properly.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html
new file mode 100644
index 0000000000..b550ba288e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=110952">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, click the red box. The test succeeds if the red box turns green.
+</p>
+
+<style>
+#div {
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+</style>
+
+<div id="div"></div>
+<dialog id="dialog"></dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const actions = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: element})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ const dialog = document.getElementById('dialog');
+ dialog.show();
+
+ const div = document.getElementById('div');
+ div.firedOn = false;
+ div.addEventListener('click', function(event) {
+ div.firedOn = true;
+ div.style.backgroundColor = 'green';
+ });
+
+ await clickOn(div);
+
+ assert_true(div.firedOn);
+}, 'Ensure that non-modal dialogs do not block mouse events.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html
new file mode 100644
index 0000000000..248bec86f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=382594">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+/* Remove body margin and dialog styles for easier positioning expected values */
+body {
+ height: 10000px;
+ margin: 0;
+}
+
+dialog {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ width: auto;
+ height: auto;
+ max-width: initial;
+ max-height: initial;
+}
+
+#absolute-div {
+ position: absolute;
+ top: 800px;
+ height: 50px;
+ width: 90%;
+}
+
+#relative-div {
+ position: relative;
+ top: 20px;
+ height: 30px;
+}
+</style>
+
+<div id="absolute-div">
+ <div id="relative-div">
+ <dialog id="dialog">It is my dialog.</dialog>
+ </div>
+</div>
+
+<script>
+test(() => {
+ const dialog = document.querySelector('#dialog');
+ const div = document.querySelector('#div-dialog');
+ const relativeContainer = document.querySelector('#relative-div');
+ const offset = 50;
+ dialog.style.top = offset + 'px';
+ dialog.style.left = offset + 'px';
+
+ dialog.style.position = 'absolute';
+ dialog.show();
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ relativeContainer.getBoundingClientRect().top + offset,
+ 'Absolute position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ relativeContainer.getBoundingClientRect().left + offset,
+ 'Absolute position.');
+
+ dialog.style.position = 'static';
+ assert_true(dialog.open);
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ relativeContainer.getBoundingClientRect().top,
+ 'Static position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ relativeContainer.getBoundingClientRect().left,
+ 'Static position.');
+ dialog.close();
+
+ dialog.style.position = 'relative';
+ dialog.show();
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ relativeContainer.getBoundingClientRect().top + offset,
+ 'Relative position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ relativeContainer.getBoundingClientRect().left + offset,
+ 'Relative position.');
+ dialog.close();
+
+ dialog.style.position = 'fixed';
+ dialog.show();
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ offset,
+ 'Fixed position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ offset,
+ 'Fixed position.');
+ dialog.close();
+}, 'Tests layout of non-modal dialogs.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/pass-dialog-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/pass-dialog-ref.html
new file mode 100644
index 0000000000..6f1a8fde21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/pass-dialog-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<style>
+ dialog::backdrop { background-color: black; }
+</style>
+<dialog id="dialog">PASS</dialog>
+<script>
+ dialog.showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html
new file mode 100644
index 0000000000..2f2fbad1fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body id="body">
+ <dialog>
+ This is a dialog
+ </dialog>
+ <input />
+<script>
+"use strict";
+function testFocus(element, expectFocus) {
+ var focusedElement = null;
+ element.addEventListener('focus', function() { focusedElement = element; }, false);
+ element.focus();
+ var theElement = element;
+ assert_equals(focusedElement === theElement, expectFocus, element.id);
+}
+
+test(function() {
+ var dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ var input = document.querySelector('input');
+ testFocus(input, false);
+
+ dialog.remove();
+ testFocus(input, true);
+}, "Test that removing dialog unblocks the document.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html
new file mode 100644
index 0000000000..0856d6f9f1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 100px;
+ width: 100px;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 0px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 50px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+<div id="bottomDialog" class="pseudodialog"></div>
+<div id="topDialog" class="pseudodialog"></div>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html
new file mode 100644
index 0000000000..b0e50e3869
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="removed-element-is-removed-from-top-layer-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ height: 100px;
+ width: 100px;
+}
+
+::backdrop {
+ display: none;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 231px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 50px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+<dialog id="bottomDialog"></dialog>
+<dialog id="topDialog"></dialog>
+<script>
+document.getElementById('topDialog').showModal();
+var bottomDialog = document.getElementById('bottomDialog');
+bottomDialog.showModal();
+bottomDialog.offsetTop; // force a layout
+var parent = bottomDialog.parentNode;
+parent.removeChild(bottomDialog);
+parent.appendChild(bottomDialog);
+</script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js
new file mode 100644
index 0000000000..c72ed7f19c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js
@@ -0,0 +1,18 @@
+function waitUntilLoadedAndAutofocused() {
+ return new Promise(function(resolve) {
+ var loaded = false;
+ var autofocused = false;
+ window.addEventListener('load', function() {
+ loaded = true;
+ if (autofocused)
+ resolve();
+ }, false);
+ document.addEventListener('focusin', function() {
+ if (autofocused)
+ return;
+ autofocused = true;
+ if (loaded)
+ resolve();
+ }, false);
+ });
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css
new file mode 100644
index 0000000000..571e7b8b6f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css
@@ -0,0 +1,14 @@
+.pseudodialog {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: fit-content;
+ height: fit-content;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+}
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html
new file mode 100644
index 0000000000..c5566bc092
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+window.onload = parent.parent.frameLoaded;
+</script>
+</head>
+<body>
+<dialog id="dialog">
+ <button id="dialog-button" tabindex="0">Button</button>
+ <iframe id="iframe-in-dialog" srcdoc='
+ <input id="iframe-under-dialog-input" class="target" type="date">
+ '></iframe>
+</dialog>
+<input id="frame1-input" class="target" type="text">
+<iframe id="iframe1" srcdoc='
+ <dialog id="iframe-dialog">
+ <button id="iframe-dialog-button" tabindex="0">Button</button>
+ </dialog>
+ <input id="iframe-input" class="target" type="date">
+ <script>window.onload = parent.parent.parent.frameLoaded;</script>
+'>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html
new file mode 100644
index 0000000000..167c56945d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html
@@ -0,0 +1 @@
+<div id="frame2-div" class="target" tabindex="0">Hello</div>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpg b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpg
new file mode 100644
index 0000000000..8909de2430
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html
new file mode 100644
index 0000000000..6a2ad8c4a0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/common.js"></script>
+<script>
+promise_test(() => {
+ return waitUntilLoadedAndAutofocused().then(() => {
+ outerButton = document.getElementById('outer-button');
+ assert_equals(document.activeElement, outerButton);
+
+ // Test that focus goes to the dialog if the dialog has no focusable elements
+ var outerDialog = document.getElementById('outer-dialog');
+ outerDialog.showModal();
+ assert_equals(document.activeElement, outerDialog);
+
+ // Test that an autofocus element in the dialog gets focus.
+ var dialog = document.getElementById('dialog');
+ dialog.showModal();
+ autofocusButton = document.getElementById('autofocus-button');
+ assert_equals(document.activeElement, autofocusButton);
+ dialog.close();
+
+ // ... or else first focusable element in the dialog gets focus.
+ autofocusButton.parentNode.removeChild(autofocusButton);
+ dialog.showModal();
+ firstButton = document.getElementById('first-button');
+ assert_equals(document.activeElement, firstButton);
+ dialog.close();
+
+ // ... or else the dialog itself gets focus.;
+ var buttons = dialog.querySelectorAll('button');
+ for (var i = 0; i < buttons.length; ++i)
+ buttons[i].hidden = true;
+ dialog.showModal();
+ assert_equals(document.activeElement, dialog);
+ dialog.close();
+
+ document.getElementById('outer-dialog').close();
+ });
+}, "focus when a modal dialog is opened");
+</script>
+</head>
+<body>
+<button id="outer-button" autofocus></button>
+<dialog id="outer-dialog">
+ <dialog id="dialog" tabindex=0>
+ <button disabled></button>
+ <dialog>
+ <button autofocus></button>
+ </dialog>
+ <button id="first-button"></button>
+ <div>
+ <span>
+ <button id="autofocus-button" autofocus></button>
+ </span>
+ </div>
+ <button id="final-button"></button>
+ </dialog>
+</dialog>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html
new file mode 100644
index 0000000000..c9cc150099
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:futhark@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=850664">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=851384">
+
+<div id="dialog">
+ <div id="item"></div>
+</div>
+<script>
+const itemRoot = item.attachShadow({mode: 'open'});
+const dialogRoot = dialog.attachShadow({mode: 'open'});
+dialogRoot.innerHTML = '<dialog><slot></slot></dialog>';
+dialog.offsetTop;
+dialogRoot.firstChild.showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html
new file mode 100644
index 0000000000..a1d792010d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class=test-wait>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:noel@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=804047">
+
+<template>
+ <custom-dialog></custom-dialog>
+</template>
+<div id=shadow></div>
+<iframe id=sibling></iframe>
+
+<script>
+customElements.define('custom-dialog',class extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({mode: 'open'}).innerHTML = '<dialog></dialog>';
+ }
+ show() {
+ this.shadowRoot.querySelector('dialog').showModal();
+ }
+});
+
+onload = () => {
+ const template = document.querySelector('template');
+ const content = document.importNode(template.content, true);
+ const dialog = content.querySelector('custom-dialog');
+ document.querySelector('div').appendChild(dialog);
+ dialog.show();
+ document.documentElement.classList.remove('test-wait');
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html
new file mode 100644
index 0000000000..8ff8a7e86c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=241699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>Ensure that simulated click is still dispatched to an inert node.
+To test manually, click the CLICK ME label and verify it does change the value of the checkbox.</p>
+<div>
+</div>
+<input type="checkbox" id="target">
+<dialog><label for="target">CLICK ME</label></dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const actions = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: element})
+ .pointerDown()
+ .pointerUp()
+ await actions.send();
+ }
+
+ document.querySelector('dialog').showModal();
+ await clickOn(document.querySelector('label'));
+ assert_true(document.getElementById('target').checked);
+}, 'Ensure that simulated click is still dispatched to an inert node.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html
new file mode 100644
index 0000000000..5954993d19
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=304827">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog>
+ <form method="dialog">
+ <input id="goodbye" type="submit" value="Goodbye">
+ <input id="hello" type="submit" value="Hello">
+ </form>
+</dialog>
+
+<script>
+async_test(t => {
+ const dialog = document.querySelector('dialog');
+ dialog.show();
+ dialog.addEventListener('close', t.step_func(() => {
+ assert_false(dialog.open);
+ assert_equals(dialog.returnValue, 'Goodbye');
+
+ dialog.show();
+ dialog.addEventListener('close', t.step_func_done(() => {
+ assert_false(dialog.open);
+ assert_equals(dialog.returnValue, 'Hello');
+ }));
+ document.querySelector('#hello').click();
+ }), {once: true});
+
+ document.querySelector('#goodbye').click();
+}, 'Tests submitting a dialog on a close event triggered by a previous submission.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html
new file mode 100644
index 0000000000..3be8213cd4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=241699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+dialog {
+ width: 50px;
+}
+</style>
+
+<button>Click me</button>
+<div id="div">Click me too</div>
+<dialog></dialog>
+
+<script>
+test(() => {
+ dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ const div = document.getElementById('div');
+ let clicked = false;
+
+ [button, div].forEach(function(element) {
+ element.addEventListener('click', () => clicked = true);
+
+ clicked = false;
+ element.click();
+ assert_true(clicked, 'Calling click() on ' + element.tagName);
+
+ clicked = false;
+ element.dispatchEvent(new Event('click'));
+ assert_true(clicked, 'Calling dispatchEvent() on ' + element.tagName);
+ });
+}, 'Test that inert nodes still get programmatic click events');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html
new file mode 100644
index 0000000000..40b72cf5ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+</head>
+<body>
+<p>
+This tests that a modal dialog's containing block is in the initial containing block and that it is unaffected by
+ancestor elements with overflow or opacity.
+<div class="pseudodialog" style="position: absolute; top: 100px; height: 250px; width: 90%; background-color: yellow">
+ This dialog should be onscreen with a width of 90% of the page. It is the child of an narrow element
+ positioned off screen, but the containing block of a top layer element is the initial containing block, so its
+ position and percent lengths are relative to that.
+</div>
+<div class="pseudodialog" style="position: absolute; top: 200px; left: 0px; height: 100px; background-color: cyan">
+ This dialog should be unaffected by its ancestor with overflow. It should not be clipped.
+</div>
+<div class="pseudodialog" style="position: absolute; top: 250px; left: 0px; background-color: magenta">
+ This dialog should be unaffected by its ancestor with opacity.
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html
new file mode 100644
index 0000000000..0886c2cd2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-containing-block-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<p>
+This tests that a modal dialog's containing block is in the initial containing block and that it is unaffected by
+ancestor elements with overflow or opacity.
+<div style="position: absolute; top: 400px; opacity: 0.3">
+ <dialog id="opaqueDialog" style="position: absolute; top: 250px; left: 0px; background-color: magenta; outline: none">
+ This dialog should be unaffected by its ancestor with opacity.
+ </dialog>
+</div>
+<div style="position: absolute; overflow: hidden; width: 500px; height: 150px; top: 400px; left: 300px">
+ <dialog id="unclippedDialog" style="position: absolute; top: 200px; left: 0px; height: 100px; background-color: cyan">
+ This dialog should be unaffected by its ancestor with overflow. It should not be clipped.
+ </dialog>
+</div>
+<div style="position: absolute; top: 1000px; left: 1000px; width: 20px;">
+ <dialog id="bottomDialog" style="position: absolute; top: 100px; height: 250px; width: 90%; background-color: yellow">
+ This dialog should be onscreen with a width of 90% of the page. It is the child of an narrow element
+ positioned off screen, but the containing block of a top layer element is the initial containing block, so its
+ position and percent lengths are relative to that.
+ </dialog>
+</div>
+<script>
+document.getElementById('bottomDialog').showModal();
+document.getElementById('unclippedDialog').showModal();
+document.getElementById('opaqueDialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html
new file mode 100644
index 0000000000..1880668cc3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 150px;
+ width: 150px;
+}
+</style>
+</head>
+<body>
+This tests that a top layer element is not rendered if it, or an ancestor, has display: none.
+It passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<div class="pseudodialog" style="top: 50px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 100px; left: 50px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html
new file mode 100644
index 0000000000..ba790c1db9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-display-none-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ height: 150px;
+ width: 150px;
+ outline: none;
+}
+
+::backdrop {
+ display: none;
+}
+
+.red {
+ background-color: red;
+ top: 200px;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 50px;
+ display: none;
+}
+
+#topDialog {
+ background-color: green;
+ top: 100px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+This tests that a top layer element is not rendered if it, or an ancestor, has display: none.
+It passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<dialog id="hiddenDialog" class="red" style="display: none;"></dialog>
+<div id="container">
+ <div>
+ <dialog id="displayNoneChild1" class="red"></dialog>
+ <dialog id="displayNoneChild2" class="red"></dialog>
+ </div>
+</div>
+<dialog id="bottomDialog"></dialog>
+<dialog id="topDialog"></dialog>
+<script>
+document.getElementById('hiddenDialog').showModal();
+document.getElementById('displayNoneChild1').showModal();
+document.getElementById('container').style.display = 'none';
+document.getElementById('displayNoneChild2').showModal();
+
+// Test that stacking works even if an element is added to the top layer when it has no renderer.
+document.getElementById('bottomDialog').showModal();
+document.getElementById('topDialog').showModal();
+document.getElementById('bottomDialog').style.display = 'block';
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html
new file mode 100644
index 0000000000..0a2936abbe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.pseudodialog {
+ height: 150px;
+ width: 150px;
+ position: absolute;
+ top: 0; right: 0; bottom: 0; left: 0;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+}
+</style>
+</head>
+<body>
+This tests that top layer elements are stacked correctly even if nested in the DOM tree.
+The test passes if you see no red rectangles and see 3 rectangles stacked in the following order (from bottom to top): yellow, blue, green.
+
+<div class="pseudodialog" style="top: 100px; background-color: yellow"></div>
+<div class="pseudodialog" style="top: 150px; left: 50px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 200px; left: 100px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html
new file mode 100644
index 0000000000..9e0616e952
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-nesting-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ height: 150px;
+ width: 150px;
+ outline: none;
+}
+
+::backdrop {
+ display: none;
+}
+
+#bottomDialog {
+ background-color: yellow;
+ top: 100px;
+ z-index: 1000;
+}
+
+#middleDialog {
+ background-color: blue;
+ top: 150px;
+ left: 50px;
+ z-index: -500;
+}
+
+#topDialog {
+ background-color: green;
+ top: 200px;
+ left: 100px;
+ z-index: -1000;
+}
+
+.red {
+ background-color: red;
+ top: 250px;
+ left: 0px;
+}
+</style>
+</head>
+<body>
+This tests that top layer elements are stacked correctly even if nested in the DOM tree.
+The test passes if you see no red rectangles and see 3 rectangles stacked in the following order (from bottom to top): yellow, blue, green.
+
+<dialog id="topDialog">
+ <dialog id="middleDialog">
+ <dialog id="bottomDialog">
+ <dialog id="hiddenDialog" class="red">
+ <dialog id="hiddenDialogChild" class="red"></dialog>
+ </dialog>
+ </dialog>
+ </dialog>
+</dialog>
+<script>
+document.getElementById('hiddenDialogChild').showModal();
+document.getElementById('hiddenDialog').showModal();
+document.getElementById('bottomDialog').showModal();
+document.getElementById('middleDialog').showModal();
+document.getElementById('topDialog').showModal();
+document.getElementById('hiddenDialog').close();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html
new file mode 100644
index 0000000000..6e3c52aa02
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent clip-path does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ clip-path: circle(5%);
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html
new file mode 100644
index 0000000000..589d539779
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent filter does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ filter: blur(100px) opacity(50%);
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ position: absolute;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html
new file mode 100644
index 0000000000..8ba3ed47c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent mask does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ mask-image: radial-gradient(black, transparent);
+ mask-size: 100px;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html
new file mode 100644
index 0000000000..46c5de2a6d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent opacity does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=229317">
+<style>
+body { background: red; }
+
+#parent {
+ opacity: 0;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html
new file mode 100644
index 0000000000..d805954969
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent overflow: clip; does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ max-width: 0;
+ max-height: 0;
+ width: 0;
+ height: 0;
+ overflow: clip;
+ position: absolute;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ position: absolute;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html
new file mode 100644
index 0000000000..f5389ddc09
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent overflow: hidden; does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ max-width: 0;
+ max-height: 0;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ position: absolute;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html
new file mode 100644
index 0000000000..a230defeea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent overflow: scroll; does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ max-width: 0;
+ max-height: 0;
+ width: 0;
+ height: 0;
+ overflow: scroll;
+ position: absolute;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ position: absolute;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html
new file mode 100644
index 0000000000..ac6f3cffc3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent transform does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ transform: scale(0);
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ outline: none;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html
new file mode 100644
index 0000000000..01eff8c4de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+
+<style>
+dialog {
+ background-color: green;
+ height: 50px;
+ width: 50px;
+ border: none;
+ padding: 0;
+ margin: 0;
+
+ position: absolute;
+ top: 100px;
+ left: 100px;
+}
+</style>
+
+<dialog></dialog>
+
+<script>
+document.querySelector('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html
new file mode 100644
index 0000000000..0dbef7d2ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=106538">
+<link rel=match href="top-layer-position-ref.html">
+<meta name=assert content="Position relative computes to absolute in the top layer for dialog elements.">
+
+<style>
+dialog {
+ background-color: green;
+ height: 50px;
+ width: 50px;
+ border: none;
+ padding: 0;
+ margin: 0;
+
+ position: relative;
+ top: 100px;
+ left: 100px;
+}
+</style>
+
+<dialog></dialog>
+
+<script>
+document.querySelector('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html
new file mode 100644
index 0000000000..86d8c89f88
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=106538">
+<link rel=match href="top-layer-position-ref.html">
+<meta name=assert content="Position static computes to absolute in the top layer for dialog elements.">
+
+<style>
+dialog {
+ background-color: green;
+ height: 50px;
+ width: 50px;
+ border: none;
+ padding: 0;
+ margin: 0;
+
+ position: static;
+ top: 100px;
+ left: 100px;
+}
+</style>
+
+<dialog></dialog>
+
+<script>
+document.querySelector('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html
new file mode 100644
index 0000000000..1fdbca5c1e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+test(() => {
+ const dialog = document.createElement('dialog');
+ document.body.appendChild(dialog);
+
+ dialog.style = 'position:static';
+ assert_equals(getComputedStyle(dialog).position, 'static');
+ dialog.showModal();
+ assert_true(dialog.open);
+ assert_equals(getComputedStyle(dialog).position, 'absolute',
+ `dialog should be position:absolute when element.style has position:static.`);
+ dialog.close();
+ assert_false(dialog.open);
+
+ dialog.style = 'position:relative';
+ assert_equals(getComputedStyle(dialog).position, 'relative');
+ dialog.showModal();
+ assert_true(dialog.open);
+ assert_equals(getComputedStyle(dialog).position, 'absolute',
+ `dialog should be position:absolute when element.style has position:relative.`);
+ dialog.close();
+ assert_false(dialog.open);
+}, `Verifies that position:static and position:relative computed to position:absolute in the top layer.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute-ref.html
new file mode 100644
index 0000000000..7aadaf51b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<head>
+ <title>Shown modal dialog where the popover attribute is removed</title>
+</head>
+<body>
+ <dialog popover style="padding: 2em"></dialog>
+ <script>
+ const d = document.querySelector("dialog");
+ d.showModal();
+ </script>
+</body>
+<html>
+
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html
new file mode 100644
index 0000000000..3827e90c5b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<meta name="assert" content="Removing the popover attribute of a hidden popover should not remove the dialog from the top layer.">
+<head>
+ <title>Shown modal dialog where the popover attribute is removed</title>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm">
+ <link rel="match" href="top-layer-remove-popover-attribute-ref.html">
+</head>
+<body>
+ <dialog popover style="padding: 2em"></dialog>
+ <script>
+ const d = document.querySelector("dialog");
+ d.showModal();
+ d.popover = null;
+ </script>
+</body>
+<html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html
new file mode 100644
index 0000000000..392d1ca46e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 100px;
+ width: 100px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+
+<div class="pseudodialog" style="top: 100px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 150px; left: 50px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html
new file mode 100644
index 0000000000..4fdd28820d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-stacking-correct-order-remove-readd-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<style>
+dialog {
+ height: 100px;
+ width: 100px;
+ outline: none;
+}
+
+::backdrop {
+ display: none;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 100px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 150px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+
+<dialog id="topDialog"></dialog>
+<dialog id="bottomDialog"></dialog>
+<script>
+var topDialog = document.getElementById('topDialog');
+var bottomDialog = document.getElementById('bottomDialog');
+topDialog.showModal();
+bottomDialog.showModal();
+topDialog.offsetTop; // force a layout
+topDialog.close();
+topDialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html
new file mode 100644
index 0000000000..6ddb317633
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 150px;
+ width: 150px;
+}
+</style>
+</head>
+<body>
+This tests top layer element stacking order after dynamically calling show/close and removal from the DOM tree.
+The test passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<div class="pseudodialog" style="top: 50px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 100px; left: 50px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html
new file mode 100644
index 0000000000..ebccdc66cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-stacking-dynamic-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<style>
+dialog {
+ height: 150px;
+ width: 150px;
+ outline: none;
+}
+
+::backdrop {
+ display: none;
+}
+
+.red {
+ background-color: red;
+ top: 200px;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 50px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 100px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+This tests top layer element stacking order after dynamically calling show/close and removal from the DOM tree.
+The test passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<dialog id="topDialog"></dialog>
+<dialog id="bottomDialog"></dialog>
+<dialog id="removedDialog" class="red">
+ <dialog id="removedDialogChild" class="red"></dialog>
+</dialog>
+<script>
+document.getElementById('topDialog').showModal();
+var removedDialog = document.getElementById('removedDialog');
+removedDialog.showModal();
+document.getElementById('bottomDialog').showModal();
+document.getElementById('removedDialogChild').showModal();
+removedDialog.parentNode.removeChild(removedDialog);
+document.getElementById('topDialog').close();
+document.getElementById('topDialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html
new file mode 100644
index 0000000000..b271ef47b4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+</head>
+<style>
+.box {
+ height: 150px;
+ width: 150px;
+}
+.container {
+ perspective: 500px;
+ border: 1px solid black;
+ background-color: magenta;
+}
+.transformed {
+ transform: rotateY(45deg);
+ background-color: cyan;
+}
+</style>
+<body>
+<div class="pseudodialog" style="position: fixed; top: 10px; z-index:3000">
+ This white box is the topmost modal dialog. It should be on top of everything.
+</div>
+<div style="position: absolute; top: 0px; z-index: 3; background-color: red; left: 0; right: 0; height: 200px;"></div>
+<div class="pseudodialog" style="position: absolute; top: 50px; background-color: green; width: 75%; height: 400px; z-index:2000; overflow: auto;">
+ This green box is also a modal dialog. It should be rendered above the red and yellow regions.
+ <div class="container box">
+ <div class="transformed box">A transform within the dialog's subtree.</div>
+ </div>
+ <div class="box" style="position: absolute; top:300px; z-index: 2; background-color: cyan">
+ This shows z-index stacking within the dialog's subtree. The cyan box should be on top of the magenta one.
+ </div>
+ <div class="box" style="position: absolute; top:350px; left:50px; z-index: 1; background-color: magenta"></div>
+ <div style="position: fixed; top: 90px; left: 30px; background-color: green">This is part of the green dialog.</div>
+</div>
+<div style="position: absolute; top: 100px; left: 0px; right: 0px; height: 200em; background-color: yellow; z-index:1000">
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.tentative.html
new file mode 100644
index 0000000000..6407ef23c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.tentative.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- This tests that top layer elements are rendered above z-indexed elements
+and stacked in the correct order amongst themselves. Also, layer features like
+transforms and z-index are tested inside a top layer element subtree. -->
+<html>
+<head>
+<link rel="match" href="top-layer-stacking-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+.box {
+ height:150px;
+ width:150px;
+}
+
+::backdrop {
+ display: none;
+}
+
+.container {
+ perspective: 500px;
+ border: 1px solid black;
+ background-color: magenta;
+}
+.transformed {
+ transform: rotateY(45deg);
+ background-color: cyan;
+}
+</style>
+</head>
+<body>
+<dialog id="hiddenDialog" style="display: none; color: red">This should not be displayed.</dialog>
+<dialog id="topDialog" style="position: fixed; top: 10px; z-index: -10;">
+ This white box is the topmost modal dialog. It should be on top of everything.
+</dialog>
+<div style="position: absolute; top: 0px; z-index: 3; background-color: red; left: 0; right: 0; height: 200px;">
+ <dialog id="bottomDialog" style="position: absolute; top: 50px; background-color: green; width: 75%; height: 400px;">
+ This green box is also a modal dialog. It should be rendered above the red and yellow regions.
+ <div class="container box">
+ <div class="transformed box">A transform within the dialog's subtree.</div>
+ </div>
+ <div class="box" style="position: absolute; top:300px; z-index: 2; background-color: cyan">
+ This shows z-index stacking within the dialog's subtree. The cyan box should be on top of the magenta one.
+ </div>
+ <div class="box" style="position: absolute; top:350px; left:50px; z-index: 1; background-color: magenta"></div>
+ <div style="position: fixed; top: 90px; left: 30px; background-color: green">This is part of the green dialog.</div>
+ </dialog>
+</div>
+<div style="position: absolute; top: 100px; left: 0px; right: 0px; height: 200em; background-color: yellow; z-index:1000">
+</div>
+<script>
+document.getElementById('bottomDialog').showModal();
+document.getElementById('topDialog').showModal();
+document.getElementById('hiddenDialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html
new file mode 100644
index 0000000000..4a3693bd2d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: activation behavior</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-summary-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<details id="happy-path-starts-closed">
+ <summary id="happy-path-starts-closed-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="happy-path-starts-open" open>
+ <summary id="happy-path-starts-open-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="details-not-being-rendered" style="display: none">
+ <summary id="details-not-being-rendered-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="summary-not-being-rendered">
+ <summary id="summary-not-being-rendered-summary" style="display: none">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="has-preceding-element">
+ <span></span>
+ <summary id="has-preceding-element-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="has-preceding-summary">
+ <summary>Summary 1</summary>
+ <summary id="has-preceding-summary-summary">Summary 2</summary>
+ <p>Contents</p>
+</details>
+
+<details id="has-preceding-summary-descendant">
+ <span><summary>Summary 1</summary></span>
+ <summary id="has-preceding-summary-descendant-summary">Summary 2</summary>
+ <p>Contents</p>
+</details>
+
+<details id="summary-nested">
+ <span><summary id="summary-nested-summary">Summary</summary></span>
+ <p>Contents</p>
+</details>
+
+<details id="toggle-tester">
+ <summary>Summary</summary>
+ <p>Contents</p>
+</details>
+
+<script>
+"use strict";
+
+testSummary(
+ "happy-path-starts-closed", false, true,
+ "Should open a closed details if all conditions are met"
+);
+
+testSummary(
+ "happy-path-starts-open", true, false,
+ "Should close an open details if all conditions are met"
+);
+
+testSummary(
+ "details-not-being-rendered", false, true,
+ "Should open a closed details even if the details is not being rendered"
+);
+
+testSummary(
+ "summary-not-being-rendered", false, true,
+ "Should open a closed details even if the summary is not being rendered"
+);
+
+testSummary(
+ "has-preceding-element", false, true,
+ "Should open a closed details if a span element precedes the summary"
+);
+
+testSummary(
+ "has-preceding-summary", false, false,
+ "Should stay closed if another summary element precedes the summary"
+);
+
+testSummary(
+ "has-preceding-summary-descendant", false, true,
+ "Should open a closed details if another summary element *nested inside a span* precedes the summary"
+);
+
+testSummary(
+ "summary-nested", false, false,
+ "Should stay closed if the summary element is nested inside a span element"
+);
+
+async_test(t => {
+ const details = document.getElementById("toggle-tester");
+ const summary = details.firstElementChild;
+
+ let timesToggleFired = 0;
+ details.addEventListener("toggle", t.step_func(() => {
+ ++timesToggleFired;
+ }));
+
+ t.step_timeout(() => {
+ assert_equals(timesToggleFired, 1, "Expected toggle to fire exactly once");
+ t.done();
+ }, 200);
+
+ summary.click();
+ summary.click();
+ summary.click();
+ summary.click();
+ Promise.resolve().then(() => summary.click());
+
+}, "toggle events should be coalesced even when using the activation behavior of a summary");
+
+function testSummary(detailsId, expectedBefore, expectedAfter, name) {
+ test(() => {
+ const details = document.getElementById(detailsId);
+ const summary = document.getElementById(detailsId + "-summary");
+
+ assert_equals(details.open, expectedBefore, "Before activation: expected open to be " + expectedBefore);
+ summary.click();
+ assert_equals(details.open, expectedAfter, "After activation: expected open to be " + expectedAfter);
+ }, name);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html
new file mode 100644
index 0000000000..6910a5de93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: clicking on anchor containing inline element</title>
+<link rel="author" title="Yu Han" href="mailto:yuzhehan@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-summary-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<details id="details_i">
+ <summary>Anchor text is wrapped with &lt;i&gt; tag <a href="#with_i_tag"><i id="with_i">permalink</i></a></summary>
+ <p>asdf</p>
+</details>
+
+<details id="details_span">
+ <summary>This one uses &lt;span&gt;. <a href="#with_span_tag"><span id="with_span">permalink</span></a></summary>
+ <p>asdf</p>
+</details>
+
+<details id="details_svg">
+ <summary>
+ <svg style="width: 100px;" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
+ <a href="#inside_svg_w_circle">
+ <circle id="svg_circle" cx="50" cy="40" r="35"/>
+ </a>
+ <a href="#inside_svg_w_text">
+ <text id="svg_text" x="50" y="90" text-anchor="middle">
+ &lt;circle&gt;
+ </text>
+ </a>
+ </svg>
+ </summary>
+ <p>asdf</p>
+</details>
+
+<script>
+function testClickingOnInlineElement(detailsId, targetId, expected, testName) {
+ const details = document.getElementById(detailsId);
+ const target = document.getElementById(targetId);
+ const test = async_test(testName);
+
+ const promise = new Promise((resolve, reject) => {
+ window.onhashchange = test.step_func_done(() => {
+ assert_false(details.open);
+ assert_equals(location.hash, expected);
+ resolve();
+ });
+ });
+
+ if (target.click) {
+ target.click();
+ }
+ else {
+ // svg element don't have click method
+ target.dispatchEvent(new MouseEvent('click', {
+ view: window,
+ bubbles: true,
+ cancelable: true
+ }));
+ }
+ return promise;
+};
+
+async function testAll() {
+ try {
+ await testClickingOnInlineElement("details_i", "with_i", "#with_i_tag", "Expected <a> containing <i> to navigate");
+ await testClickingOnInlineElement("details_span", "with_span", "#with_span_tag", "Expected <a> containing <span> to navigate");
+ await testClickingOnInlineElement("details_svg", "svg_circle", "#inside_svg_w_circle", "Expected <a>, inside svg, containing <circle> to navigate");
+ await testClickingOnInlineElement("details_svg", "svg_text", "#inside_svg_w_text", "Expected <a>, inside svg, containing <text> to navigate");
+ } catch (exception) {
+ assert_unreached("should NOT-THROW exception");
+ }
+};
+
+var allTests = async_test("Clicking on anchor with embedded inline element should navigate instead of opening details");
+testAll().then(()=>{ allTests.done(); });
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html
new file mode 100644
index 0000000000..edaf786b25
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: clicking on anchor without link</title>
+<link rel="author" title="Di Zhang" href="mailto:dizhangg@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-summary-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<details id="details">
+ <summary><a id="no_inline">Details</a></summary>
+ <p>Text</p>
+</details>
+
+<details id="details_inline">
+ <summary><a><i id="has_inline">Details</i></a></summary>
+ <p>Text</p>
+</details>
+
+
+<script>
+
+async function testClickingOnAnchorWithoutLink (detailsId, targetId) {
+ const details = document.getElementById(detailsId);
+ const target = document.getElementById(targetId);
+ const initialLoc = location.hash;
+
+ assert_false(details.open);
+ target.click();
+ assert_true(details.open);
+ assert_equals(location.hash, initialLoc);
+}
+
+promise_test(() => testClickingOnAnchorWithoutLink('details', 'no_inline'),
+ "clicking on anchor without link should open details and not navigate.");
+
+promise_test(() => testClickingOnAnchorWithoutLink('details_inline', 'has_inline'),
+ "clicking on anchor without link, with embedded inline element should open details and not navigate.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html
new file mode 100644
index 0000000000..4418413fef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: click behavior</title>
+<link rel="author" title="Mu-An Chiou" href="mailto:hi@muan.co">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-summary-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+ <div id="log"></div>
+
+ <details id="details">
+ <summary id="summary">Summary</summary>
+ <p>Contents</p>
+ </details>
+</body>
+
+<script>
+ // This behavior is not specified by HTML standards, but setting focus on
+ // clicked summary tag is the current behavior on Chrome, Safari, and Firefox
+ // in both Windows and macOS.
+ async_test(t => {
+ const details = document.getElementById("details")
+ const summary = document.getElementById("summary")
+
+ t.step_timeout(() => {
+ details.addEventListener("toggle", t.step_func_done(function () {
+ assert_equals(details.open, true, "details should be open")
+ assert_equals(document.activeElement, summary, "active element should be summary")
+ t.done()
+ }))
+
+ new test_driver.click(summary)
+ }, 200)
+ }, "clicking on summary should open details and set focus on summary")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html
new file mode 100644
index 0000000000..57cc45478e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=969619">
+<summary style="display:table;"><rt></rt></summary>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> { }, "No crash");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html
new file mode 100644
index 0000000000..21b66d52e7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Summary</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<details>
+ <summary>Summary</summary>
+ Details
+</details>
+<script type="module">
+const details = document.querySelector("details");
+details.addEventListener("toggle",
+ (e) => assert_true(false, 'details should not be toggled'));
+
+const summary = document.querySelector("summary");
+summary.addEventListener("click",
+ (e) => assert_true(false, `summary should not be clicked`));
+
+test(() => {
+ // keyCode: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: " ",
+ })
+ );
+}, `Dispatching untrusted keypress events to summary should not cause click event`);
+
+test(() => {
+ // keyCode: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 13,
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: "Enter",
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 32,
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: " ",
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: " ",
+ })
+ );
+}, `Dispatching untrusted keyup/keydown events to summary should not cause click event`);
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interfaces.html b/testing/web-platform/tests/html/semantics/interfaces.html
new file mode 100644
index 0000000000..dc7b600e0a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interfaces.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test of interfaces</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/">
+<link rel="help" href="https://webidl.spec.whatwg.org/#host-objects">
+<link rel="help" href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf#page=96">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=interfaces.js></script>
+<div id="log"></div>
+<script>
+function do_test(local_name, iface, variant) {
+ test(function() {
+ var e;
+ var i = "HTML" + iface + "Element";
+ if (variant === "useNS") {
+ // Use createElementNS here to preserve the case of local_name.
+ e = document.createElementNS("http://www.w3.org/1999/xhtml", local_name);
+ } else if (variant === "useParser") {
+ e = new DOMParser().parseFromString("<" + local_name + ">", "text/html").querySelector(local_name);
+ } else {
+ e = document.createElement(local_name);
+ }
+ assert_class_string(e, i,
+ "Element " + local_name + " should have " + i +
+ " as its primary interface.");
+ assert_true(e instanceof window[i],
+ "Element " + local_name + " should implement " + i + ".");
+ assert_true(e instanceof HTMLElement,
+ "Element " + local_name + " should implement HTMLElement.");
+ assert_true(e instanceof Element,
+ "Element " + local_name + " should implement Element.");
+ assert_true(e instanceof Node,
+ "Element " + local_name + " should implement Node.");
+ }, "Interfaces for " + local_name + ": " + variant);
+}
+
+// Some elements have weird parser behavior / insertion modes and would be
+// skipped by the parser, so skip those.
+function should_do_parser_test(local_name) {
+ return ![
+ "foo-BAR",
+ "tbody",
+ "td",
+ "tfoot",
+ "th",
+ "thead",
+ "tr",
+ "Ã¥-bar",
+ "caption",
+ "col",
+ "colgroup",
+ "frame",
+ "image",
+ "frameset",
+ ].includes(local_name)
+}
+
+elements.forEach(function(a) {
+ do_test(a[0], a[1], "useNS");
+
+ if (should_do_parser_test(a[0])) {
+ do_test(a[0], a[1], "useParser");
+ }
+
+ // Only run the createElement variant if the input is all-lowercase, because createElement
+ // case-folds to lowercase. Custom elements are required to use all-lowercase to implement
+ // HTMLElement, otherwise they use HTMLUnknownElement per spec. Example: "foo-BAR".
+ if (a[0] === a[0].toLowerCase()) {
+ do_test(a[0].toUpperCase(), a[1], "createElement");
+ }
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interfaces.js b/testing/web-platform/tests/html/semantics/interfaces.js
new file mode 100644
index 0000000000..05fc82f767
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interfaces.js
@@ -0,0 +1,150 @@
+var elements = [
+ ["a", "Anchor"],
+ ["abbr", ""],
+ ["acronym", ""],
+ ["address", ""],
+ ["applet", "Unknown"],
+ ["area", "Area"],
+ ["article", ""],
+ ["aside", ""],
+ ["audio", "Audio"],
+ ["b", ""],
+ ["base", "Base"],
+ ["basefont", ""],
+ ["bdi", ""],
+ ["bdo", ""],
+ ["bgsound", "Unknown"],
+ ["big", ""],
+ ["blink", "Unknown"],
+ ["blockquote", "Quote"],
+ ["body", "Body"],
+ ["br", "BR"],
+ ["button", "Button"],
+ ["canvas", "Canvas"],
+ ["caption", "TableCaption"],
+ ["center", ""],
+ ["cite", ""],
+ ["code", ""],
+ ["col", "TableCol"],
+ ["colgroup", "TableCol"],
+ ["command", "Unknown"],
+ ["data", "Data"],
+ ["datalist", "DataList"],
+ ["dd", ""],
+ ["del", "Mod"],
+ ["details", "Details"],
+ ["dfn", ""],
+ ["dialog", "Dialog"],
+ ["dir", "Directory"],
+ ["directory", "Unknown"],
+ ["div", "Div"],
+ ["dl", "DList"],
+ ["dt", ""],
+ ["em", ""],
+ ["embed", "Embed"],
+ ["fieldset", "FieldSet"],
+ ["figcaption", ""],
+ ["figure", ""],
+ ["font", "Font"],
+ ["foo-BAR", "Unknown"], // not a valid custom element name
+ ["foo-bar", ""], // valid custom element name
+ ["foo", "Unknown"],
+ ["footer", ""],
+ ["form", "Form"],
+ ["frame", "Frame"],
+ ["frameset", "FrameSet"],
+ ["h1", "Heading"],
+ ["h2", "Heading"],
+ ["h3", "Heading"],
+ ["h4", "Heading"],
+ ["h5", "Heading"],
+ ["h6", "Heading"],
+ ["head", "Head"],
+ ["header", ""],
+ ["hgroup", ""],
+ ["hr", "HR"],
+ ["html", "Html"],
+ ["i", ""],
+ ["iframe", "IFrame"],
+ ["image", "Unknown"],
+ ["img", "Image"],
+ ["input", "Input"],
+ ["ins", "Mod"],
+ ["isindex", "Unknown"],
+ ["kbd", ""],
+ ["keygen", "Unknown"],
+ ["label", "Label"],
+ ["legend", "Legend"],
+ ["li", "LI"],
+ ["link", "Link"],
+ ["listing", "Pre"],
+ ["main", ""],
+ ["map", "Map"],
+ ["mark", ""],
+ ["marquee", "Marquee"],
+ ["menu", "Menu"],
+ ["meta", "Meta"],
+ ["meter", "Meter"],
+ ["mod", "Unknown"],
+ ["multicol", "Unknown"],
+ ["nav", ""],
+ ["nextid", "Unknown"],
+ ["nobr", ""],
+ ["noembed", ""],
+ ["noframes", ""],
+ ["noscript", ""],
+ ["object", "Object"],
+ ["ol", "OList"],
+ ["optgroup", "OptGroup"],
+ ["option", "Option"],
+ ["output", "Output"],
+ ["p", "Paragraph"],
+ ["param", "Param"],
+ ["permission", "Permission"],
+ ["picture", "Picture"],
+ ["plaintext", ""],
+ ["pre", "Pre"],
+ ["progress", "Progress"],
+ ["q", "Quote"],
+ ["quasit", "Unknown"],
+ ["rb", ""],
+ ["rp", ""],
+ ["rt", ""],
+ ["rtc", ""],
+ ["ruby", ""],
+ ["s", ""],
+ ["samp", ""],
+ ["script", "Script"],
+ ["section", ""],
+ ["select", "Select"],
+ ["slot", "Slot"],
+ ["small", ""],
+ ["source", "Source"],
+ ["spacer", "Unknown"],
+ ["span", "Span"],
+ ["strike", ""],
+ ["strong", ""],
+ ["style", "Style"],
+ ["sub", ""],
+ ["summary", ""],
+ ["sup", ""],
+ ["table", "Table"],
+ ["tbody", "TableSection"],
+ ["td", "TableCell"],
+ ["textarea", "TextArea"],
+ ["tfoot", "TableSection"],
+ ["th", "TableCell"],
+ ["thead", "TableSection"],
+ ["time", "Time"],
+ ["title", "Title"],
+ ["tr", "TableRow"],
+ ["track", "Track"],
+ ["tt", ""],
+ ["u", ""],
+ ["ul", "UList"],
+ ["var", ""],
+ ["video", "Video"],
+ ["wbr", ""],
+ ["xmp", "Pre"],
+ ["\u00E5-bar", "Unknown"], // not a valid custom element name
+];
diff --git a/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html b/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html
new file mode 100644
index 0000000000..b215f65813
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="/resources/idlharness.js"></script>
+
+<script>
+ idl_test(["invokers.tentative"], ["html", "dom"], (idl_array) => {
+ idl_array.add_objects({
+ InvokeEvent: ['new InvokeEvent("invoke")'],
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html
new file mode 100644
index 0000000000..b003daf20d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button id="invoker" invoketarget="invokee"></button>
+<div id="invokee"></div>
+
+<script>
+ test(function () {
+ assert_equals(invoker.invokeTargetElement, invokee);
+ }, "invokeTargetElement reflects invokee HTML element");
+
+ test(function () {
+ const div = document.body.appendChild(document.createElement("div"));
+ invoker.invokeTargetElement = div;
+ assert_equals(invoker.invokeTargetElement, div);
+ assert_equals(invoker.getAttribute("invoketarget"), "");
+ assert_false(invoker.hasAttribute("invokeaction"));
+ }, "invokeTargetElement reflects set value");
+
+ test(function () {
+ const host = document.body.appendChild(document.createElement("div"));
+ const shadow = host.attachShadow({ mode: "open" });
+ const button = shadow.appendChild(document.createElement("button"));
+ button.invokeTargetElement = invokee;
+ assert_equals(button.invokeTargetElement, invokee);
+ assert_equals(invoker.getAttribute("invoketarget"), "");
+ assert_false(invoker.hasAttribute("invokeaction"));
+ }, "invokeTargetElement reflects set value across shadow root into light dom");
+
+ test(function () {
+ const host = document.body.appendChild(document.createElement("div"));
+ const shadow = host.attachShadow({ mode: "open" });
+ const div = shadow.appendChild(document.createElement("div"));
+ invoker.invokeTargetElement = div;
+ assert_equals(invoker.invokeTargetElement, null);
+ assert_equals(invoker.getAttribute("invoketarget"), "");
+ assert_false(invoker.hasAttribute("invokeaction"));
+ }, "invokeTargetElement does not reflect set value inside shadowroot");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ invoker.invokeTargetElement = {};
+ },
+ "invokeTargetElement attribute must be an instance of Element",
+ );
+ }, "invokeTargetElement throws error on assignment of non Element");
+
+ test(function () {
+ assert_false(invoker.hasAttribute("invokeaction"));
+ assert_equals(invoker.invokeAction, "auto");
+ }, "invokeAction reflects 'auto' when attribute not present");
+
+ test(function () {
+ invoker.setAttribute("invokeaction", "");
+ assert_equals(invoker.getAttribute("invokeaction"), "");
+ assert_equals(invoker.invokeAction, "auto");
+ }, "invokeAction reflects 'auto' when attribute empty");
+
+ test(function () {
+ invoker.invokeAction = "fooBarBaz";
+ assert_equals(invoker.getAttribute("invokeaction"), "fooBarBaz");
+ assert_equals(invoker.invokeAction, "fooBarBaz");
+ }, "invokeAction reflects same casing");
+
+ test(function () {
+ invoker.invokeAction = "";
+ assert_equals(invoker.getAttribute("invokeaction"), "");
+ assert_equals(invoker.invokeAction, "auto");
+ }, "invokeAction reflects 'auto' when attribute empty 2");
+
+ test(function () {
+ invoker.invokeAction = [1, 2, 3];
+ assert_equals(invoker.getAttribute("invokeaction"), "1,2,3");
+ assert_equals(invoker.invokeAction, "1,2,3");
+ }, "invokeAction reflects tostring value");
+
+ test(function () {
+ invoker.invokeAction = [];
+ assert_equals(invoker.getAttribute("invokeaction"), "");
+ assert_equals(invoker.invokeAction, "auto");
+ }, "invokeAction reflects 'auto' when attribute set to []");
+
+ test(function () {
+ invoker.invokeAction = {};
+ assert_equals(invoker.invokeAction, "[object Object]");
+ }, "invokeAction reflects tostring value 2");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html
new file mode 100644
index 0000000000..84337d5723
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="div"></div>
+<button id="button"></button>
+
+<script>
+ test(function () {
+ const host = document.createElement("div");
+ const child = host.appendChild(document.createElement("p"));
+ const shadow = host.attachShadow({ mode: "closed" });
+ const slot = shadow.appendChild(document.createElement("slot"));
+ let childEvent = null;
+ let childEventTarget = null;
+ let childEventInvoker = null;
+ let hostEvent = null;
+ let hostEventTarget = null;
+ let hostEventInvoker = null;
+ slot.addEventListener(
+ "invoke",
+ (e) => {
+ childEvent = e;
+ childEventTarget = e.target;
+ childEventInvoker = e.invoker;
+ },
+ { once: true },
+ );
+ host.addEventListener(
+ "invoke",
+ (e) => {
+ hostEvent = e;
+ hostEventTarget = e.target;
+ hostEventInvoker = e.invoker;
+ },
+ { once: true },
+ );
+ const event = new InvokeEvent("invoke", {
+ bubbles: true,
+ invoker: slot,
+ composed: true,
+ });
+ slot.dispatchEvent(event);
+ assert_true(childEvent instanceof InvokeEvent, "slot saw invoke event");
+ assert_equals(
+ childEventTarget,
+ slot,
+ "target is child inside shadow boundary",
+ );
+ assert_equals(
+ childEventInvoker,
+ slot,
+ "invoker is child inside shadow boundary",
+ );
+ assert_equals(
+ hostEvent,
+ childEvent,
+ "event dispatch propagates across shadow boundary",
+ );
+ assert_equals(
+ hostEventTarget,
+ host,
+ "target is retargeted to shadowroot host",
+ );
+ assert_equals(
+ hostEventInvoker,
+ host,
+ "invoker is retargeted to shadowroot host",
+ );
+ }, "InvokeEvent propagates across shadow boundaries retargeting invoker");
+
+ test(function (t) {
+ const host = document.createElement("div");
+ document.body.append(host);
+ t.add_cleanup(() => host.remove());
+ const shadow = host.attachShadow({ mode: "open" });
+ const button = shadow.appendChild(document.createElement("button"));
+ const invokee = host.appendChild(document.createElement("div"));
+ button.invokeTargetElement = invokee;
+ let event = null;
+ let eventTarget = null;
+ let eventInvoker = null;
+ invokee.addEventListener(
+ "invoke",
+ (e) => {
+ event = e;
+ eventTarget = e.target;
+ eventInvoker = e.invoker;
+ },
+ { once: true },
+ );
+ button.click();
+ assert_true(event instanceof InvokeEvent);
+ assert_equals(eventTarget, invokee, "target is invokee");
+ assert_equals(eventInvoker, host, "invoker is host");
+ }, "cross shadow InvokeEvent retargets invoker to host element");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html
new file mode 100644
index 0000000000..82910b3d44
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html
@@ -0,0 +1,166 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="div"></div>
+<button id="button"></button>
+
+<script>
+ test(function () {
+ const event = new InvokeEvent("test");
+ assert_equals(event.action, "auto");
+ assert_readonly(event, "action", "readonly attribute value");
+ }, "action is a readonly defaulting to 'auto'");
+
+ test(function () {
+ const event = new InvokeEvent("test");
+ assert_equals(event.invoker, null);
+ assert_readonly(event, "invoker", "readonly attribute value");
+ }, "invoker is readonly defaulting to null");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: "sAmPle" });
+ assert_equals(event.action, "sAmPle");
+ }, "action reflects initialized attribute");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: undefined });
+ assert_equals(event.action, "auto");
+ }, "action set to undefined");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: null });
+ assert_equals(event.action, "null");
+ }, "action set to null");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: false });
+ assert_equals(event.action, "false");
+ }, "action set to false");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: "" });
+ assert_equals(event.action, "");
+ }, "action explicitly set to empty string");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: true });
+ assert_equals(event.action, "true");
+ }, "action set to true");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: 0.5 });
+ assert_equals(event.action, "0.5");
+ }, "action set to a number");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: [] });
+ assert_equals(event.action, "");
+ }, "action set to []");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: [1, 2, 3] });
+ assert_equals(event.action, "1,2,3");
+ }, "action set to [1, 2, 3]");
+
+ test(function () {
+ const event = new InvokeEvent("test", { action: { sample: 0.5 } });
+ assert_equals(event.action, "[object Object]");
+ }, "action set to an object");
+
+ test(function () {
+ const event = new InvokeEvent("test", {
+ action: {
+ toString() {
+ return "sample";
+ },
+ },
+ });
+ assert_equals(event.action, "sample");
+ }, "action set to an object with a toString function");
+
+ test(function () {
+ const eventInit = { action: "sample", invoker: document.body };
+ const event = new InvokeEvent("test", eventInit);
+ assert_equals(event.action, "sample");
+ assert_equals(event.invoker, document.body);
+ }, "InvokeEventInit properties set value");
+
+ test(function () {
+ const eventInit = {
+ action: "open",
+ invoker: document.getElementById("div"),
+ };
+ const event = new InvokeEvent("beforetoggle", eventInit);
+ assert_equals(event.action, "open");
+ assert_equals(event.invoker, document.getElementById("div"));
+ }, "InvokeEventInit properties set value 2");
+
+ test(function () {
+ const eventInit = {
+ action: "closed",
+ invoker: document.getElementById("button"),
+ };
+ const event = new InvokeEvent("toggle", eventInit);
+ assert_equals(event.action, "closed");
+ assert_equals(event.invoker, document.getElementById("button"));
+ }, "InvokeEventInit properties set value 3");
+
+ test(function () {
+ const event = new InvokeEvent("test", { invoker: undefined });
+ assert_equals(event.invoker, null);
+ }, "invoker set to undefined");
+
+ test(function () {
+ const event = new InvokeEvent("test", { invoker: null });
+ assert_equals(event.invoker, null);
+ }, "invoker set to null");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ new InvokeEvent("test", { invoker: false });
+ },
+ "invoker is not an object",
+ );
+ }, "invoker set to false");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ const event = new InvokeEvent("test", { invoker: true });
+ },
+ "invoker is not an object",
+ );
+ }, "invoker set to true");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ const event = new InvokeEvent("test", { invoker: {} });
+ },
+ "invoker is not an object",
+ );
+ }, "invoker set to {}");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ const eventInit = { action: "closed", invoker: new XMLHttpRequest() };
+ const event = new InvokeEvent("toggle", eventInit);
+ },
+ "invoker is not an Element",
+ );
+ }, "invoker set to non-Element EventTarget");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html
new file mode 100644
index 0000000000..b19c1d3adc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html
@@ -0,0 +1,119 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="invokee"></div>
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ promise_test(async function (t) {
+ let event = null;
+ invokee.addEventListener("invoke", (e) => (event = e), { once: true });
+ await clickOn(invokerbutton);
+ assert_true(event instanceof InvokeEvent, "event is InvokeEvent");
+ assert_equals(event.type, "invoke", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "auto", "action");
+ assert_equals(event.target, invokee, "target");
+ assert_equals(event.invoker, invokerbutton, "invoker");
+ }, "event dispatches on click");
+
+ promise_test(async function (t) {
+ let event = null;
+ invokee.addEventListener("invoke", (e) => (event = e), { once: true });
+ invokerbutton.invokeAction = "fooBar";
+ await clickOn(invokerbutton);
+ assert_true(event instanceof InvokeEvent, "event is InvokeEvent");
+ assert_equals(event.type, "invoke", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "fooBar", "action");
+ assert_equals(event.target, invokee, "target");
+ assert_equals(event.invoker, invokerbutton, "invoker");
+ }, "event action is set to invokeAction");
+
+ promise_test(async function (t) {
+ let event = null;
+ invokee.addEventListener("invoke", (e) => (event = e), { once: true });
+ invokerbutton.setAttribute("invokeaction", "BaRbAz");
+ await clickOn(invokerbutton);
+ assert_true(event instanceof InvokeEvent, "event is InvokeEvent");
+ assert_equals(event.type, "invoke", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "BaRbAz", "action");
+ assert_equals(event.target, invokee, "target");
+ assert_equals(event.invoker, invokerbutton, "invoker");
+ }, "event action is set to invokeaction attribute");
+
+ promise_test(async function (t) {
+ let called = false;
+ invokerbutton.addEventListener(
+ "click",
+ (event) => {
+ event.preventDefault();
+ },
+ { once: true },
+ );
+ invokee.addEventListener(
+ "invoke",
+ (event) => {
+ called = true;
+ },
+ { once: true },
+ );
+ await clickOn(invokerbutton);
+ assert_false(called, "event was not called");
+ }, "event does not dispatch if click:preventDefault is called");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => invokerbutton.removeAttribute('disabled'));
+ let called = false;
+ invokee.addEventListener(
+ "invoke",
+ (event) => {
+ called = true;
+ },
+ { once: true },
+ );
+ invokerbutton.setAttribute("disabled", "");
+ await clickOn(invokerbutton);
+ assert_false(called, "event was not called");
+ }, "event does not dispatch if invoker is disabled");
+
+ promise_test(async function (t) {
+ svgInvokee = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ t.add_cleanup(() => {
+ invokerbutton.invokeTargetElement = invokee;
+ svgInvokee.remove();
+ });
+ document.body.append(svgInvokee);
+ let called = false;
+ assert_false(svgInvokee instanceof HTMLElement);
+ assert_true(svgInvokee instanceof Element);
+ let eventInvoker = null;
+ svgInvokee.addEventListener(
+ "invoke",
+ (event) => {
+ eventInvoker = event.invoker;
+ called = true;
+ },
+ { once: true },
+ );
+ invokerbutton.invokeTargetElement = svgInvokee;
+ await clickOn(invokerbutton);
+ assert_true(called, "event was called");
+ assert_true(eventInvoker == svgInvokee, "event invoker is set to right element");
+ }, "event dispatches if invoker is non-HTML Element");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html
new file mode 100644
index 0000000000..b72020283e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html
@@ -0,0 +1,175 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="invokee">
+ Fullscreen content
+ <button id="invokerbutton" invoketarget="invokee"></button>
+</div>
+
+<script>
+ // auto
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ assert_false(invokee.matches(":fullscreen"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking div with auto action is no-op");
+
+ // toggleFullscreen
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ assert_false(invokee.matches(":fullscreen"));
+ invokerbutton.setAttribute("invokeaction", "toggleFullscreen");
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches(":fullscreen"));
+ }, "invoking div with toggleFullscreen action makes div fullscreen");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ assert_false(invokee.matches(":fullscreen"));
+ invokerbutton.setAttribute("invokeaction", "toggleFullscreen");
+ invokerbutton.click();
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking div with toggleFullscreen action (without user activation) is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_false(invokee.matches(":fullscreen"));
+ invokerbutton.setAttribute("invokeaction", "toggleFullscreen");
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking div with toggleFullscreen action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ invokerbutton.setAttribute("invokeaction", "toggleFullscreen");
+ await test_driver.bless('go fullscreen');
+ await invokee.requestFullscreen();
+ assert_true(invokee.matches(":fullscreen"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking fullscreen div with toggleFullscreen action exits fullscreen");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ invokerbutton.setAttribute("invokeaction", "tOgGlEFullscreen");
+ await test_driver.bless('go fullscreen');
+ await invokee.requestFullscreen();
+ assert_true(invokee.matches(":fullscreen"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking fullscreen div with toggleFullscreen (case-insensitive) action exits fullscreen");
+
+ // requestFullscreen
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ assert_false(invokee.matches(":fullscreen"));
+ invokerbutton.setAttribute("invokeaction", "requestFullscreen");
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches(":fullscreen"));
+ }, "invoking div with requestFullscreen action makes div fullscreen");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_false(invokee.matches(":fullscreen"));
+ invokerbutton.setAttribute("invokeaction", "requestFullscreen");
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking div with requestFullscreen action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ invokerbutton.setAttribute("invokeaction", "requestFullscreen");
+ await test_driver.bless('go fullscreen');
+ await invokee.requestFullscreen();
+ assert_true(invokee.matches(":fullscreen"));
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches(":fullscreen"));
+ }, "invoking fullscreen div with requestFullscreen action is a no-op");
+
+ // exitFullscreen
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ assert_false(invokee.matches(":fullscreen"));
+ invokerbutton.setAttribute("invokeaction", "exitFullscreen");
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking div with exitFullscreen action is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ invokerbutton.setAttribute("invokeaction", "exitFullscreen");
+ await test_driver.bless('go fullscreen');
+ await invokee.requestFullscreen();
+ assert_true(invokee.matches(":fullscreen"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":fullscreen"));
+ }, "invoking fullscreen div with exitFullscreen action exits fullscreen");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ if (document.fullscreenElement) await document.exitFullscreen();
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ invokerbutton.setAttribute("invokeaction", "exitFullscreen");
+ await test_driver.bless('go fullscreen');
+ await invokee.requestFullscreen();
+ assert_true(invokee.matches(":fullscreen"));
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches(":fullscreen"));
+ }, "invoking fullscreen div with exitFullscreen action and preventDefault is a no-op");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-generic-eventtarget-crash.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-generic-eventtarget-crash.tentative.html
new file mode 100644
index 0000000000..b2179640dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-generic-eventtarget-crash.tentative.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<div id="invoker"></div>
+
+<script type="module">
+ const invokeEvent = new InvokeEvent('invoke', { bubbles: true });
+ document.body.addEventListener('invoke', e => {
+ e.invoker.toString();
+ });
+ invoker.addEventListener('invoke', e => {
+ e.invoker.toString();
+ });
+ invoker.dispatchEvent(invokeEvent);
+ await Promise.resolve();
+ invokeEvent.invoker.toString();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html
new file mode 100644
index 0000000000..f3abeae165
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html
@@ -0,0 +1,285 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<audio controls id="invokee" src="/media/sound_5.mp3"></audio>
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ // auto
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking audio with auto action is no-op");
+
+ // playpause
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "playpause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking audio with playpause action makes audio play");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "playpause");
+ invokerbutton.click();
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking audio with playpause action (without user activation) is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "playpause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking audio with playpause action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ await test_driver.bless('play audio');
+ invokee.play();
+ assert_false(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "playpause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking playing audio with playpause action pauses it");
+
+ // play
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "play");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking audio with play action makes audio play");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "play");
+ invokerbutton.click();
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking audio with play action (without user activation) is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "play");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking audio with play action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ await test_driver.bless('play audio');
+ invokee.play();
+ assert_false(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "play");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking playing audio with play action is a no-op");
+
+ // pause
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "pause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking audio with pause action is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "pause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking audio with pause action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ await test_driver.bless('play audio');
+ invokee.play();
+ assert_false(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "pause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking playing audio with pause action makes it pause");
+
+ // mute
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_false(invokee.muted);
+ invokerbutton.setAttribute("invokeaction", "toggleMuted");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.muted);
+ }, "invoking audio with toggleMuted action mutes it");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_false(invokee.muted);
+ invokerbutton.setAttribute("invokeaction", "toggleMuted");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.muted);
+ }, "invoking audio with toggleMuted action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.muted = true;
+ assert_true(invokee.muted);
+ invokerbutton.setAttribute("invokeaction", "toggleMuted");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.muted);
+ }, "invoking muted audio with toggleMuted action unmutes it");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html
new file mode 100644
index 0000000000..c6735e2611
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html
@@ -0,0 +1,218 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<details id="invokee">
+ Details Contents
+</details>
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ // auto
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_true(invokee.matches("[open]"));
+ }, "invoking closed details with auto action opens");
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches("[open]"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_false(invokee.matches("[open]"));
+ }, "invoking closed details with auto action and preventDefault does not open");
+
+ promise_test(async function (t) {
+ invokee.setAttribute('open', '');
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches("[open]"));
+ }, "invoking open details with auto action closes");
+
+ promise_test(async function (t) {
+ invokee.setAttribute('open', '');
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches("[open]"));
+ }, "invoking open details with auto action and preventDefault does not close");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ invokee.addEventListener("invoke", (e) => {
+ invokee.setAttribute('open', '');
+ }, {
+ once: true,
+ });
+ assert_false(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches("[open]"));
+ }, "invoking details with auto action where event listener opens leads to a closed details");
+
+ promise_test(async function (t) {
+ invokee.setAttribute('open', '');
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ invokee.addEventListener("invoke", (e) => {
+ invokee.removeAttribute('open');
+ }, {
+ once: true,
+ });
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches("[open]"));
+ }, "invoking open details with auto action where event listener closes leads to an open details");
+
+ // toggle
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches("[open]"));
+ invokerbutton.setAttribute("invokeaction", "toggle");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_true(invokee.matches("[open]"));
+ }, "invoking closed details with toggle action opens");
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches("[open]"));
+ invokerbutton.setAttribute("invokeaction", "tOgGlE");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_true(invokee.matches("[open]"));
+ }, "invoking closed details with toggle (case-insensitive) action opens");
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches("[open]"));
+ invokerbutton.setAttribute("invokeaction", "toggle");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_false(invokee.matches("[open]"));
+ }, "invoking closed details with toggle action and preventDefault does not open");
+
+ promise_test(async function (t) {
+ invokee.setAttribute('open', '');
+ invokerbutton.setAttribute("invokeaction", "toggle");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches("[open]"));
+ }, "invoking open details with toggle action closes");
+
+ promise_test(async function (t) {
+ invokee.setAttribute('open', '');
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ invokerbutton.setAttribute("invokeaction", "toggle");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches("[open]"));
+ }, "invoking open details with toggle action and preventDefault does not close");
+
+ // open
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "open");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_true(invokee.matches("[open]"));
+ }, "invoking closed details with open action opens");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "oPeN");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_true(invokee.matches("[open]"));
+ }, "invoking closed details with open (case insensitive) action opens");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "open");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.setAttribute('open', '');
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_true(invokee.matches("[open]"));
+ }, "invoking open details with open action is noop");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "open");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches("[open]"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_false(invokee.matches("[open]"));
+ }, "invoking closed popover with open action and preventDefault does not open");
+
+ // close
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "close");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches("[open]"));
+ }, "invoking closed details with close action is noop");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "close");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.setAttribute('open', '');
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_false(invokee.matches("[open]"));
+ }, "invoking open details with close action closes");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "cLoSe");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.setAttribute('open', '');
+ assert_true(invokee.matches("[open]"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_false(invokee.matches("[open]"));
+ }, "invoking open details with close (case insensitive) action closes");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "close");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.setAttribute('open', '');
+ t.add_cleanup(() => invokee.removeAttribute('open'));
+ assert_true(invokee.matches("[open]"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ assert_true(invokee.matches("[open]"));
+ }, "invoking open details with close action with preventDefault does not close");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html
new file mode 100644
index 0000000000..03eba22285
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html
@@ -0,0 +1,209 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="invokee" popover>
+ <button id="invokerbutton2" invoketarget="invokee"></button>
+</div>
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ // auto
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as auto) closed popover opens");
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches(":popover-open"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as auto) closed popover with preventDefault does not open");
+
+ promise_test(async function (t) {
+ invokee.showPopover();
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as auto) open popover closes");
+
+ promise_test(async function (t) {
+ invokee.showPopover();
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton2);
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as auto) from within open popover closes");
+
+ promise_test(async function (t) {
+ invokee.showPopover();
+ t.add_cleanup(() => invokee.hidePopover());
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton2);
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as auto) open popover with preventDefault does not close");
+
+ // togglepopover
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches(":popover-open"));
+ invokerbutton.setAttribute("invokeaction", "togglepopover");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as togglepopover) closed popover opens");
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches(":popover-open"));
+ invokerbutton.setAttribute("invokeaction", "tOgGlEpOpOvEr");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as togglepopover - case insensitive) closed popover opens");
+
+ promise_test(async function (t) {
+ assert_false(invokee.matches(":popover-open"));
+ invokerbutton.setAttribute("invokeaction", "togglepopover");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as togglepopover) closed popover with preventDefault does not open");
+
+ promise_test(async function (t) {
+ invokee.showPopover();
+ invokerbutton2.setAttribute("invokeaction", "togglepopover");
+ t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction"));
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as togglepopover) open popover closes");
+
+ promise_test(async function (t) {
+ invokee.showPopover();
+ invokerbutton2.setAttribute("invokeaction", "togglepopover");
+ t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction"));
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton2);
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as togglepopover) from within open popover closes");
+
+ promise_test(async function (t) {
+ invokee.showPopover();
+ t.add_cleanup(() => invokee.hidePopover());
+ invokerbutton2.setAttribute("invokeaction", "togglepopover");
+ t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton2);
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as togglepopover) open popover with preventDefault does not close");
+
+ // showpopover
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "showpopover");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as showpopover) closed popover opens");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "sHoWpOpOvEr");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as showpopover - case insensitive) closed popover opens");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "showpopover");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ invokee.showPopover();
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as showpopover) open popover is noop");
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "showpopover");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches(":popover-open"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as showpopover) closed popover with preventDefault does not open");
+
+ // hidepopover
+
+ promise_test(async function (t) {
+ invokerbutton.setAttribute("invokeaction", "hidepopover");
+ t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction"));
+ assert_false(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton);
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as hidepopover) closed popover is noop");
+
+ promise_test(async function (t) {
+ invokerbutton2.setAttribute("invokeaction", "hidepopover");
+ t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction"));
+ invokee.showPopover();
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton2);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as hidepopover) open popover closes");
+
+ promise_test(async function (t) {
+ invokerbutton2.setAttribute("invokeaction", "hIdEpOpOvEr");
+ t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction"));
+ invokee.showPopover();
+ assert_true(invokee.matches(":popover-open"));
+ await clickOn(invokerbutton2);
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_false(invokee.matches(":popover-open"));
+ }, "invoking (as hidepopover - case insensitive) open popover closes");
+
+ promise_test(async function (t) {
+ invokerbutton2.setAttribute("invokeaction", "hidepopover");
+ t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction"));
+ invokee.showPopover();
+ t.add_cleanup(() => invokee.hidePopover());
+ assert_true(invokee.matches(":popover-open"));
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton2);
+ assert_true(invokee.matches(":popover-open"));
+ }, "invoking (as hidepopover) open popover with preventDefault does not close");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html
new file mode 100644
index 0000000000..5bbcd83e72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html
@@ -0,0 +1,253 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<video controls id="invokee" src="/media/movie_5.mp4"></video>
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ // auto
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking video with auto action is no-op");
+
+ // playpause
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "playpause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking video with playpause action makes video play");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "playpause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking video with playpause action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ await test_driver.bless('play video');
+ invokee.play();
+ assert_false(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "playpause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking playing video with playpause action pauses it");
+
+ // play
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "play");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking video with play action makes video play");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "play");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking video with play action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ await test_driver.bless('play video');
+ invokee.play();
+ assert_false(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "play");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.paused);
+ }, "invoking playing video with play action is a no-op");
+
+ // pause
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "pause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking video with pause action is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_true(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "pause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking video with pause action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ await test_driver.bless('play video');
+ invokee.play();
+ assert_false(invokee.paused);
+ invokerbutton.setAttribute("invokeaction", "pause");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.paused);
+ }, "invoking playing video with pause action makes it pause");
+
+ // mute
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ assert_false(invokee.muted);
+ invokerbutton.setAttribute("invokeaction", "toggleMuted");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_true(invokee.muted);
+ }, "invoking video with toggleMuted action mutes it");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ assert_false(invokee.muted);
+ invokerbutton.setAttribute("invokeaction", "toggleMuted");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.muted);
+ }, "invoking video with toggleMuted action and preventDefault is a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(async () => {
+ invokerbutton.removeAttribute("invokeaction");
+ invokee.pause();
+ invokee.currentTime = 0;
+ invokee.muted = false;
+ });
+ invokee.muted = true;
+ assert_true(invokee.muted);
+ invokerbutton.setAttribute("invokeaction", "toggleMuted");
+ await clickOn(invokerbutton);
+ await new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ assert_false(invokee.muted);
+ }, "invoking muted video with toggleMuted action unmutes it");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js b/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js
new file mode 100644
index 0000000000..317945502d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js
@@ -0,0 +1,12 @@
+function waitForRender() {
+ return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+}
+async function clickOn(element) {
+ const actions = new test_driver.Actions();
+ await waitForRender();
+ await actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ await waitForRender();
+}
diff --git a/testing/web-platform/tests/html/semantics/links/META.yml b/testing/web-platform/tests/html/semantics/links/META.yml
new file mode 100644
index 0000000000..b2167370d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - annevk
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer-when-downgrade.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer-when-downgrade.html
new file mode 100644
index 0000000000..466868dd7b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer-when-downgrade.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header No Referrer When Downgrade Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='no-referrer-when-downgrade'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer.html
new file mode 100644
index 0000000000..cd7a1804f3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-no-referrer.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header No Referrer Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='no-referrer'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader("null");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin-when-cross-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin-when-cross-origin.html
new file mode 100644
index 0000000000..98115aa653
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin-when-cross-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header Origin When Cross Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='origin-when-cross-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin.html
new file mode 100644
index 0000000000..194ca9d4ad
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-same-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-same-origin.html
new file mode 100644
index 0000000000..eb86708d5b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-same-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header Same Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='same-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin-when-cross-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin-when-cross-origin.html
new file mode 100644
index 0000000000..f6514ff2ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin-when-cross-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header Strict Origin When Cross Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='strict-origin-when-cross-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin.html
new file mode 100644
index 0000000000..4aa311e833
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-strict-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header Strict Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='strict-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-unsafe-url.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-unsafe-url.html
new file mode 100644
index 0000000000..59742404fe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin-unsafe-url.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin Header Unsafe Url Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='unsafe-url'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.html
new file mode 100644
index 0000000000..189e2e66d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Origin no Referrer Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-origin.js"></script>
+ <script>
+ testOriginHeader(self.location.origin);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.js b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.js
new file mode 100644
index 0000000000..acc62ef93b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-origin.js
@@ -0,0 +1,40 @@
+const RESOURCES_DIR = "/html/semantics/links/downloading-resources/resources/";
+
+function testOriginHeader(expectedOrigin) {
+ var id = self.token();
+ let testUrl = RESOURCES_DIR + "inspect-header.py?header=origin&cmd=put&id=" + id;
+
+ promise_test(function(test) {
+ const anchor = document.getElementById("a");
+ anchor.setAttribute("ping", testUrl);
+ anchor.click();
+ return pollResult(id) .then(result => {
+ assert_equals(result, expectedOrigin, "Correct origin header result");
+ });
+ }, "Test origin header " + RESOURCES_DIR);
+}
+
+// Sending a ping is an asynchronous and non-blocking request to a web server.
+// We may have to create a poll loop to get result from server
+function pollResult(id) {
+ let checkUrl = RESOURCES_DIR + "inspect-header.py?header=origin&cmd=get&id=" + id;
+
+ return new Promise(resolve => {
+ function checkResult() {
+ fetch(checkUrl).then(
+ function(response) {
+ assert_equals(response.status, 200, "Inspect header response's status is 200");
+ let result = response.headers.get("x-request-origin");
+
+ if (result != undefined) {
+ resolve(result);
+ } else {
+ step_timeout(checkResult.bind(this), 100);
+ }
+ });
+ }
+
+ checkResult();
+ });
+
+}
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer-when-downgrade.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer-when-downgrade.html
new file mode 100644
index 0000000000..96c19d1d0e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer-when-downgrade.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header No Referrer When Downgrade Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='no-referrer-when-downgrade'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer.html
new file mode 100644
index 0000000000..065063075c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-no-referrer.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header No Referrer Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='no-referrer'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin-when-cross-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin-when-cross-origin.html
new file mode 100644
index 0000000000..f0394261a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin-when-cross-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header Origin When Cross Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='origin-when-cross-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin.html
new file mode 100644
index 0000000000..bef435581c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-same-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-same-origin.html
new file mode 100644
index 0000000000..19b2d022af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-same-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header Same Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='same-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin-when-cross-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin-when-cross-origin.html
new file mode 100644
index 0000000000..95132eee5c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin-when-cross-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header Strict Origin When Cross Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='strict-origin-when-cross-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin.html
new file mode 100644
index 0000000000..e2678e8de8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-strict-origin.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header Strict Origin Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='strict-origin'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-unsafe-url.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-unsafe-url.html
new file mode 100644
index 0000000000..cc3d1dde86
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer-unsafe-url.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute Referrer Header Unsafe Url Policy</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <meta name='referrer' content='unsafe-url'>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.html b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.html
new file mode 100644
index 0000000000..5e2d136443
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Ping attribute no Referrer Header given</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <a id="a" href="#">
+ <script src="/common/utils.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/resources/chromium/enable-hyperlink-auditing.js"></script>
+ <script src="header-referrer.js"></script>
+ <script>
+ testReferrerHeader("");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.js b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.js
new file mode 100644
index 0000000000..818649fbff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/header-referrer.js
@@ -0,0 +1,40 @@
+const RESOURCES_DIR = "/html/semantics/links/downloading-resources/resources/";
+
+function testReferrerHeader(expectedReferrer) {
+ let id = self.token();
+ let testUrl = RESOURCES_DIR + "inspect-header.py?header=referer&cmd=put&id=" + id;
+
+ promise_test(function(test) {
+ const anchor = document.getElementById("a");
+ anchor.setAttribute("ping", testUrl);
+ anchor.click();
+ return pollResult(id) .then(result => {
+ assert_equals(result, expectedReferrer, "Correct referrer header result");
+ });
+ }, "Test referer header " + RESOURCES_DIR);
+}
+
+// Sending a ping is an asynchronous and non-blocking request to a web server.
+// We may have to create a poll loop to get result from server
+function pollResult(id) {
+ let checkUrl = RESOURCES_DIR + "inspect-header.py?header=referer&cmd=get&id=" + id;
+
+ return new Promise(resolve => {
+ function checkResult() {
+ fetch(checkUrl).then(
+ function(response) {
+ assert_equals(response.status, 200, "Inspect header response's status is 200");
+ let result = response.headers.get("x-request-referer");
+
+ if (result != undefined) {
+ resolve(result);
+ } else {
+ step_timeout(checkResult.bind(this), 100);
+ }
+ });
+ }
+
+ checkResult();
+ });
+
+}
diff --git a/testing/web-platform/tests/html/semantics/links/downloading-resources/resources/inspect-header.py b/testing/web-platform/tests/html/semantics/links/downloading-resources/resources/inspect-header.py
new file mode 100644
index 0000000000..2c68e475ff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/downloading-resources/resources/inspect-header.py
@@ -0,0 +1,18 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/plain")]
+ command = request.GET.first(b"cmd").lower()
+ test_id = request.GET.first(b"id")
+ header = request.GET.first(b"header")
+ if command == b"put":
+ request.server.stash.put(test_id, request.headers.get(header, b""))
+
+ elif command == b"get":
+ stashed_header = request.server.stash.take(test_id)
+ if stashed_header is not None:
+ headers.append((b"x-request-" + header, stashed_header))
+
+ else:
+ response.set_error(400, u"Bad Command")
+ return b"ERROR: Bad Command!"
+
+ return headers, b""
diff --git a/testing/web-platform/tests/html/semantics/links/following-hyperlinks/activation-behavior.window.js b/testing/web-platform/tests/html/semantics/links/following-hyperlinks/activation-behavior.window.js
new file mode 100644
index 0000000000..d530642b9e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/following-hyperlinks/activation-behavior.window.js
@@ -0,0 +1,50 @@
+["a",
+ "area"].forEach(type => {
+
+ const followed = type === "a" ? true : false;
+ async_test(t => {
+ const target = document.createElement("iframe"),
+ link = document.createElement(type);
+ t.add_cleanup(() => target.remove());
+ target.name = "certifiedrandom" + type;
+ link.target = "certifiedrandom" + type;
+ link.href = "/";
+ document.body.appendChild(target);
+ target.onload = t.step_func(() => {
+ if(target.contentWindow.location.href === "about:blank")
+ return;
+ if(followed) {
+ assert_equals(target.contentWindow.location.pathname, "/");
+ t.done();
+ } else {
+ assert_unreached();
+ }
+ });
+ link.click();
+ t.step_timeout(() => {
+ if(followed) {
+ assert_unreached();
+ } else {
+ t.done();
+ }
+ }, 500);
+ }, "<" + type + "> that is not connected should " + (followed ? "" : "not ") + "be followed");
+
+ async_test(t => {
+ const target = document.createElement("iframe"),
+ doc = document.implementation.createDocument("", ""),
+ link = doc.createElementNS("http://www.w3.org/1999/xhtml", type);
+ t.add_cleanup(() => target.remove());
+ target.name = "certifiedrandom2" + type;
+ link.target = "certifiedrandom2" + type;
+ link.href = "/";
+ document.body.appendChild(target);
+ target.onload = t.step_func(() => {
+ if(target.contentWindow.location.href === "about:blank")
+ return;
+ assert_unreached();
+ });
+ link.click();
+ t.step_timeout(() => t.done(), 500);
+ }, "<" + type + "> that is from an inactive document should not be followed");
+});
diff --git a/testing/web-platform/tests/html/semantics/links/following-hyperlinks/active-document.window.js b/testing/web-platform/tests/html/semantics/links/following-hyperlinks/active-document.window.js
new file mode 100644
index 0000000000..efa16e7d17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/following-hyperlinks/active-document.window.js
@@ -0,0 +1,23 @@
+["a",
+ "area",
+ "link"].forEach(type => {
+ async_test(t => {
+ const frame = document.createElement("iframe"),
+ link = document.createElement(type);
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ // See https://github.com/whatwg/html/issues/490
+ if(frame.contentWindow.location.href === "about:blank")
+ return;
+ link.click(); // must be ignored because document is not active
+ t.step_timeout(() => {
+ assert_equals(frame.contentWindow.location.pathname, "/common/blank.html");
+ t.done();
+ }, 500);
+ });
+ document.body.appendChild(frame);
+ frame.contentDocument.body.appendChild(link);
+ link.href = "/";
+ frame.src = "/common/blank.html";
+ }, "<" + type + "> in navigated away <iframe>'s document cannot follow hyperlinks");
+});
diff --git a/testing/web-platform/tests/html/semantics/links/hyperlink-auditing/headers.optional.html b/testing/web-platform/tests/html/semantics/links/hyperlink-auditing/headers.optional.html
new file mode 100644
index 0000000000..dd524fa5fa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/hyperlink-auditing/headers.optional.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<iframe id="i" src="/common/blank.html"></iframe>
+
+<!-- Test is optional because hyperlink auditing is optional. -->
+
+<script>
+promise_test(async t => {
+ await new Promise(resolve => window.onload = resolve);
+
+ const id = token();
+
+ const el = document.createElement("a");
+ el.ping = new URL(`resources/stash-headers.py?id=${id}`, location.href); // this will be a POST
+ el.href = "/common/blank.html?1";
+
+ i.contentDocument.body.append(el);
+ el.click();
+
+ let headers;
+ await pollForConditionFunc(t, async () => {
+ const res = await fetch(el.ping); // this will be a GET
+ const json = await res.json();
+
+ if (json !== "no headers yet") {
+ headers = json;
+ return true;
+ }
+ return false;
+ });
+
+ assert_equals(headers["content-type"], "text/ping", "content-type");
+ assert_equals(headers["ping-from"], i.src, "ping-from");
+ assert_equals(headers["ping-to"], el.href, "ping-to");
+});
+
+async function pollForConditionFunc(t, func, timeout = 3000, interval = 100) {
+ let remaining = Math.ceil(timeout / interval);
+
+ while (remaining > 0) {
+ --remaining;
+ await new Promise(resolve => t.step_timeout(resolve, interval));
+
+ if (await func()) {
+ return;
+ }
+ }
+
+ assert_true(false, "Condition never became true");
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/links/hyperlink-auditing/resources/stash-headers.py b/testing/web-platform/tests/html/semantics/links/hyperlink-auditing/resources/stash-headers.py
new file mode 100644
index 0000000000..a0d4a38812
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/hyperlink-auditing/resources/stash-headers.py
@@ -0,0 +1,27 @@
+import json
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ key = request.GET[b"id"]
+
+ if request.method == "POST":
+ content_type = request.headers.get(b"content-type", b"no content-type header")
+ ping_from = request.headers.get(b"ping-from", b"no ping-from header")
+ ping_to = request.headers.get(b"ping-to", b"no ping-to header")
+
+ value = json.dumps({
+ 'content-type': isomorphic_decode(content_type),
+ 'ping-from': isomorphic_decode(ping_from),
+ 'ping-to': isomorphic_decode(ping_to)
+ })
+ request.server.stash.put(key, value)
+
+ return (204, [], "")
+
+ elif request.method == "GET":
+ value = request.server.stash.take(key)
+ if value is None:
+ value = "\"no headers yet\""
+ return (200, [("Content-Type", "application/json")], str(value))
+
+ return (405, [], "")
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_attribute-getter-setter.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_attribute-getter-setter.html
new file mode 100644
index 0000000000..2db3082e21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_attribute-getter-setter.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<head>
+<title>HTMLAnchorElement getters and setters</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<a>anchor</a>
+<script>
+function test_gettersetter(property, oldresult, newval, newresult, oldurl, newurl) {
+ var a = document.querySelector('a');
+ a.href = oldurl;
+ var r1 = a[property];
+ assert_equals(r1, oldresult);
+ a[property] = newval;
+ var r2 = a[property];
+ assert_equals(r2, newresult);
+ var r3 = a.href;
+ assert_equals(r3, newurl);
+}
+
+//Elements for each test: [property, oldresult, newvalue, newresult, oldurl, newurl]
+// [0] [1] [2] [3] [4] [5]
+tests = [
+ ["hash", "#somehash", "someother", "#someother",
+ "http://google.com/index.html#somehash",
+ "http://google.com/index.html#someother"],
+ ["hash", "#somehash", "#someother", "#someother",
+ "http://google.com/index.html#somehash",
+ "http://google.com/index.html#someother"],
+ ["host", "google.com:1234", "github.com:4444", "github.com:4444",
+ "http://google.com:1234/somedir",
+ "http://github.com:4444/somedir"],
+ ["hostname", "google.com", "github.com", "github.com",
+ "http://google.com:1234/somedir",
+ "http://github.com:1234/somedir"],
+ ["href", "http://google.com:1234/somedir", "http://goo-gle.com:1234/other/x.html", "http://goo-gle.com:1234/other/x.html",
+ "http://google.com:1234/somedir",
+ "http://goo-gle.com:1234/other/x.html"],
+ ["password", "flabada", "blubb", "blubb",
+ "https://anonymous:flabada@developer.mozilla.org/en-US/docs/",
+ "https://anonymous:blubb@developer.mozilla.org/en-US/docs/"],
+ ["pathname", "/somedir/someotherdir/index.html", "/newpath/x.txt", "/newpath/x.txt",
+ "http://google.com:1234/somedir/someotherdir/index.html",
+ "http://google.com:1234/newpath/x.txt"],
+ ["port", "1234", "4444", "4444", "http://google.com:1234/somedir", "http://google.com:4444/somedir"],
+ ["protocol", "http:", "ftp:", "ftp:", "http://google.com/somedir", "ftp://google.com/somedir"],
+ ["protocol", "http:", "ftp", "ftp:", "http://google.com/somedir", "ftp://google.com/somedir"],
+ ["search", "?ho", "?hi", "?hi", "http://google.com/q.php?ho", "http://google.com/q.php?hi"],
+ ["search", "?ho", "hi", "?hi", "http://google.com/q.php?ho", "http://google.com/q.php?hi"],
+ ["search", "?ho", "?hi", "?hi", "http://google.com/?ho", "http://google.com/?hi"],
+ ["search", "?ho", "hi", "?hi", "http://google.com/?ho", "http://google.com/?hi"],
+ ["username", "anonymous", "wellknown", "wellknown",
+ "https://anonymous:pwd@developer.mozilla.org:1234/en-US/",
+ "https://wellknown:pwd@developer.mozilla.org:1234/en-US/"]
+];
+
+for (var i = 0; i < tests.length; i++) {
+ test(function() {
+ test_gettersetter(tests[i][0], tests[i][1], tests[i][2], tests[i][3], tests[i][4], tests[i][5])
+ }, "Getter and setter for attribute of anchor element(" + i + "):" + tests[i][0] );
+}
+</script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_getter.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_getter.html
new file mode 100644
index 0000000000..759eada220
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_getter.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<head>
+<title>HTMLAnchorElement getters test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<a id=a1 href="http://google.com?hi">a1</a>
+<a id=a2 href="http://google.com#somehash">a2</a>
+<a id=a3 href="http://google.com:1234/somedir">a3</a>
+<a id=a4 href="http://google.com:1234/somedir">a4</a>
+<a id=a5 href="http://google.com:1234/somedir">a5</a>
+<a id=a6 href="https://anonymous:flabada@developer.mozilla.org/en-US/docs/">a6</a>
+<a id=a7 href="http://google.com:1234/somedir/someotherdir/index.html">a7</a>
+<a id=a8 href="http://google.com:1234/somedir">a8</a>
+<a id=a9 href="http://google.com/somedir">a9</a>
+<a id=a10 href="https://anonymous:pwd@developer.mozilla.org:1234/en-US/">a10</a>
+<script>
+function test_getter(property, result, id) {
+ var a = document.getElementById(id);
+ var r = a[property];
+ assert_equals(r, result);
+}
+
+//Elements for each test: [property, result, id]
+// [0] [1] [2]
+tests = [
+ ["search", "?hi", "a1"],
+ ["hash", "#somehash", "a2"],
+ ["host", "google.com:1234", "a3"],
+ ["hostname", "google.com", "a4"],
+ ["href", "http://google.com:1234/somedir", "a5"],
+ ["password", "flabada", "a6"],
+ ["pathname", "/somedir/someotherdir/index.html", "a7"],
+ ["port", "1234", "a8"],
+ ["protocol", "http:", "a9"],
+ ["username", "anonymous", "a10"]
+];
+
+for (var i = 0; i < tests.length; i++) {
+ test(function() {
+ test_getter(tests[i][0], tests[i][1], tests[i][2])
+ }, "Getter for attribute of anchor element(" + i + "):" + tests[i][0]);
+}
+</script>
+</head>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_noopener.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_noopener.html
new file mode 100644
index 0000000000..95ab1c81fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_noopener.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test behavior of rel="noopener" links</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe name="oursubframe"></iframe>
+<a href="support/noopener-target-2.html" rel="noopener" target="ourpopup"></a>
+<a href="support/noopener-target-2.html" rel="noopener" target="oursubframe"></a>
+<script>
+var tests = [];
+// First test the special targets
+function target1Loaded(win) {
+ // Find the relevant test
+ var test = tests.find((t) => t.openedWindow == win);
+ test.step(function() {
+ assert_equals(win.opener, window);
+ win.close();
+ test.done();
+ });
+}
+/**
+ * Test that <a rel="noopener"> targeted at one of _self, _parent, _top does the
+ * load in the appropriate existing browsing context instead of opening a new
+ * one. The test is run in a separate popup window we open and which we can
+ * navigate without causing the test harness going into conniptions.
+ */
+for (var target of ["self", "parent", "top"]) {
+ var t = async_test("Check that rel=noopener with target=_" + target + " does a normal load");
+ tests.push(t);
+ t.openedWindow = window.open("support/noopener-popup.html");
+ t.targetName = target;
+ t.openedWindow.onload = t.step_func(function() {
+ this.openedWindow.findLink(this.targetName).click();
+ });
+}
+
+/**
+ * And now check that a noopener load targeted at something other than one of
+ * the three special targets above is still able to reuse existing things with the
+ * given name. We do this in two ways. First, by opening a window named
+ * "ourpopup" and then doing a load via <a rel="noopener" target="ourpopup"> and
+ * verifying that the load happens in a window with a null opener, etc, while
+ * the opener of the thing we opened is not modified. And second, by targeting
+ * <a rel="noopener"> at a name that an existing subframe has, and ensuring that
+ * this subframe is not navigated.
+ */
+var t1 = async_test("Check that targeting of rel=noopener with a given name reuses an existing window with that name");
+var w;
+t1.add_cleanup(function() { w.close(); });
+var channel = new BroadcastChannel("ourpopup");
+channel.onmessage = t1.step_func_done(function(e) {
+ var data = e.data;
+ assert_true(data.hasOpener);
+ assert_false(data.hasParent);
+ assert_equals(data.name, "ourpopup");
+ assert_equals(w.opener, window);
+ assert_not_equals(w.location.href, "about:blank");
+});
+t1.step(function() {
+ w = window.open("", "ourpopup");
+ assert_equals(w.opener, window);
+ document.querySelectorAll("a")[0].click();
+});
+
+var t2 = async_test("Check that targeting of rel=noopener with a given name reuses an existing subframe with that name");
+var channel = new BroadcastChannel("oursubframe");
+channel.onmessage = t2.step_func_done(function(e) {
+ var data = e.data;
+ assert_false(data.hasOpener);
+ assert_true(data.hasParent);
+ assert_equals(data.name, "oursubframe");
+ assert_not_equals(document.querySelector("iframe").contentWindow.location.href,
+ "about:blank");
+});
+t2.step(function() {
+ document.querySelectorAll("a")[1].click();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-parsable-url-getter-setter.window.js b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-parsable-url-getter-setter.window.js
new file mode 100644
index 0000000000..587f9b9c46
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-parsable-url-getter-setter.window.js
@@ -0,0 +1,54 @@
+[
+ {
+ "property": "origin",
+ "set": null
+ },
+ {
+ "property": "protocol",
+ "get": ":",
+ "set": "https"
+ },
+ {
+ "property": "username"
+ },
+ {
+ "property": "password"
+ },
+ {
+ "property": "host"
+ },
+ {
+ "property": "hostname"
+ },
+ {
+ "property": "port",
+ "set": "8000"
+ },
+ {
+ "property": "pathname"
+ },
+ {
+ "property": "search"
+ },
+ {
+ "property": "hash"
+ }
+].forEach(({ property, get = "", set = "string" }) => {
+ ["a", "area"].forEach(name => {
+ test(() => {
+ const link = document.createElement(name);
+ link.href = "http://test:test/"; // non-parsable URL
+ assert_equals(link[property], get);
+ }, `<${name} href="http://test:test/">.${property} getter`);
+
+ if (set !== null) {
+ test(() => {
+ const link = document.createElement(name);
+ link.href = "http://test:test/"; // non-parsable URL
+ link[property] = set;
+ assert_equals(link[property], get);
+ assert_equals(link.href, "http://test:test/");
+ }, `<${name} href="http://test:test/">.${property} setter`);
+ }
+ });
+});
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-opaque-path-url-getter-setter.window.js b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-opaque-path-url-getter-setter.window.js
new file mode 100644
index 0000000000..9549c6e2a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-opaque-path-url-getter-setter.window.js
@@ -0,0 +1,59 @@
+[
+ {
+ "property": "origin",
+ "get": "null",
+ "set": null
+ },
+ {
+ "property": "protocol",
+ "get": "non-special:",
+ "set": "super-special",
+ "setget": "super-special:"
+ },
+ {
+ "property": "username"
+ },
+ {
+ "property": "password"
+ },
+ {
+ "property": "host",
+ },
+ {
+ "property": "hostname",
+ },
+ {
+ "property": "port",
+ "set": "8000"
+ },
+ {
+ "property": "pathname",
+ "get": "opaque",
+ "setget": "opaque"
+ },
+ {
+ "property": "search",
+ "setget": "?string"
+ },
+ {
+ "property": "hash",
+ "setget": "#string"
+ }
+].forEach(({ property, get = "", set = "string", setget = get }) => {
+ ["a", "area"].forEach(name => {
+ test(() => {
+ const link = document.createElement(name);
+ link.href = "non-special:opaque";
+ assert_equals(link[property], get);
+ }, `<${name} href="non-special:opaque">.${property} getter`);
+
+ if (set !== null) {
+ test(() => {
+ const link = document.createElement(name);
+ link.href = "non-special:opaque";
+ link[property] = set;
+ assert_equals(link[property], setget);
+ }, `<${name} href="non-special:opaque">.${property} setter`);
+ }
+ });
+});
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-url-getter-setter.window.js b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-url-getter-setter.window.js
new file mode 100644
index 0000000000..de528a2f97
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/non-special-url-getter-setter.window.js
@@ -0,0 +1,63 @@
+[
+ {
+ "property": "origin",
+ "get": "null",
+ "set": null
+ },
+ {
+ "property": "protocol",
+ "get": "non-special:",
+ "set": "super-special",
+ "setget": "super-special:"
+ },
+ {
+ "property": "username"
+ },
+ {
+ "property": "password"
+ },
+ {
+ "property": "host",
+ "get": "test:9001",
+ "setget": "string:9001"
+ },
+ {
+ "property": "hostname",
+ "get": "test"
+ },
+ {
+ "property": "port",
+ "get": "9001",
+ "set": "8000"
+ },
+ {
+ "property": "pathname",
+ "get": "/",
+ "setget": "/string"
+ },
+ {
+ "property": "search",
+ "setget": "?string"
+ },
+ {
+ "property": "hash",
+ "setget": "#string"
+ }
+].forEach(({ property, get = "", set = "string", setget = set }) => {
+ ["a", "area"].forEach(name => {
+ test(() => {
+ const link = document.createElement(name);
+ link.href = "non-special://test:9001/";
+ assert_equals(link[property], get);
+ }, `<${name} href="non-special://test:9001/">.${property} getter`);
+
+ if (set !== null) {
+ test(() => {
+ const link = document.createElement(name);
+ link.href = "non-special://test:9001/";
+ link[property] = set;
+ assert_equals(link[property], setget);
+ }, `<${name} href="non-special://test:9001/">.${property} setter`);
+ }
+ });
+});
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-popup.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-popup.html
new file mode 100644
index 0000000000..2057dbf0be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-popup.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script>
+ function findLink(arg) {
+ var doc;
+ if (arg == "self") {
+ doc = document;
+ } else {
+ doc = frames[0].document;
+ }
+ return doc.getElementById(arg + "target");
+ }
+</script>
+<a rel="noopener" target="_self" id="selftarget"
+ href="noopener-target-1.html"></a>
+<iframe srcdoc='
+ <a rel="noopener" target="_parent" id="parenttarget"
+ href="noopener-target-1.html"></a>
+ <a rel="noopener" target="_top" id="toptarget"
+ href="noopener-target-1.html"></a>'></iframe>
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-1.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-1.html
new file mode 100644
index 0000000000..0dbd14275c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ opener.target1Loaded(this);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-2.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-2.html
new file mode 100644
index 0000000000..dd2d719134
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/noopener-target-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+ var channel = new BroadcastChannel(this.name);
+ channel.postMessage({ hasOpener: opener !== null ,
+ hasParent: parent != this,
+ name: window.name });
+ window.close();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/target_blank_implicit_noopener.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/target_blank_implicit_noopener.html
new file mode 100644
index 0000000000..bf6a1ae5bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/support/target_blank_implicit_noopener.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+ let bc = new BroadcastChannel(window.location.search.substring(1));
+ bc.postMessage({ hasOpener: opener !== null });
+ window.close();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html
new file mode 100644
index 0000000000..73eebaff70
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset=utf-8>
+ <meta name="timeout" content="long">
+ <title>Test behavior of target=_blank links</title>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+</head>
+<body>
+ <a href="support/target_blank_implicit_noopener.html?a1" id="a1" rel="noopener" target="_blank">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a2" id="a2" rel="opener" target="_blank">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a3" id="a3" target="_blank">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a4" id="a4" rel="opener noopener" target="_blank">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a5" id="a5" rel="noopener opener" target="_blank">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a6" id="a6" rel="noreferrer" target="_blank">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a7" id="a7" rel="opener noreferrer" target="_blank">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a8" id="a8" rel="noopener opener noreferrer" target="_blank">Click me</a>
+
+ <!-- Although this is not valid, per the processing model of area it ought to work -->
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area1" id="area1" rel="noopener" target="_blank">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area2" id="area2" rel="opener" target="_blank">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area3" id="area3" target="_blank">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area4" id="area4" rel="opener noopener" target="_blank">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area5" id="area5" rel="noopener opener" target="_blank">
+
+ <script>
+
+ let tests = [
+ { id: "a1", hasOpener: false, name: "Anchor element with target=_blank with rel=noopener" },
+ { id: "a2", hasOpener: true, name: "Anchor element with target=_blank with rel=opener" },
+ { id: "a3", hasOpener: false, name: "Anchor element with target=_blank with implicit rel=noopener" },
+ { id: "a4", hasOpener: false, name: "Anchor element with target=_blank with rel=opener+noopener" },
+ { id: "a5", hasOpener: false, name: "Anchor element with target=_blank with rel=noopener+opener" },
+ { id: "a6", hasOpener: false, name: "Anchor element with target=_blank with rel=noreferrer" },
+ { id: "a7", hasOpener: false, name: "Anchor element with target=_blank with rel=opener+noreferrer" },
+ { id: "a8", hasOpener: false, name: "Anchor element with target=_blank with rel=noopener+opener+noreferrer" },
+ { id: "area1", hasOpener: false, name: "Area element with target=_blank with rel=noopener" },
+ { id: "area2", hasOpener: true, name: "Area element with target=_blank with rel=opener" },
+ { id: "area3", hasOpener: false, name: "Area element with target=_blank with implicit rel=noopener" },
+ { id: "area4", hasOpener: false, name: "Area element with target=_blank with rel=opener+noopener" },
+ { id: "area5", hasOpener: false, name: "Area element with target=_blank with rel=noopener+opener" },
+ ];
+
+ tests.forEach(data => {
+ async_test(
+ test => {
+ let bc = new BroadcastChannel(data.id);
+ bc.addEventListener("message", test.step_func_done(e => {
+ assert_equals(e.data.hasOpener, data.hasOpener);
+ }), {once: true});
+
+ document.getElementById(data.id).click();
+ }, data.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html
new file mode 100644
index 0000000000..3da6a49ef8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset=utf-8>
+ <meta name="timeout" content="long">
+ <title>Test behavior of base target=_blank links</title>
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <base target=_blank>
+</head>
+<body>
+ <a href="support/target_blank_implicit_noopener.html?a1" id="a1" rel="noopener">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a2" id="a2" rel="opener">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a3" id="a3">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a4" id="a4" rel="opener noopener">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a5" id="a5" rel="noopener opener">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a6" id="a6" rel="noreferrer">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a7" id="a7" rel="opener noreferrer">Click me</a>
+ <a href="support/target_blank_implicit_noopener.html?a8" id="a8" rel="noopener opener noreferrer">Click me</a>
+
+ <!-- Although this is not valid, per the processing model of area it ought to work -->
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area1" id="area1" rel="noopener">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area2" id="area2" rel="opener">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area3" id="area3">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area4" id="area4" rel="opener noopener">
+ <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area5" id="area5" rel="noopener opener">
+
+ <script>
+
+ let tests = [
+ { id: "a1", hasOpener: false, name: "Anchor element with base target=_blank with rel=noopener" },
+ { id: "a2", hasOpener: true, name: "Anchor element with base target=_blank with rel=opener" },
+ { id: "a3", hasOpener: false, name: "Anchor element with base target=_blank with implicit rel=noopener" },
+ { id: "a4", hasOpener: false, name: "Anchor element with base target=_blank with rel=opener+noopener" },
+ { id: "a5", hasOpener: false, name: "Anchor element with base target=_blank with rel=noopener+opener" },
+ { id: "a6", hasOpener: false, name: "Anchor element with base target=_blank with rel=noreferrer" },
+ { id: "a7", hasOpener: false, name: "Anchor element with base target=_blank with rel=opener+noreferrer" },
+ { id: "a8", hasOpener: false, name: "Anchor element with base target=_blank with rel=noopener+opener+noreferrer" },
+ { id: "area1", hasOpener: false, name: "Area element with base target=_blank with rel=noopener" },
+ { id: "area2", hasOpener: true, name: "Area element with base target=_blank with rel=opener" },
+ { id: "area3", hasOpener: false, name: "Area element with base target=_blank with implicit rel=noopener" },
+ { id: "area4", hasOpener: false, name: "Area element with base target=_blank with rel=opener+noopener" },
+ { id: "area5", hasOpener: false, name: "Area element with base target=_blank with rel=noopener+opener" },
+ ];
+
+ tests.forEach(data => {
+ async_test(
+ test => {
+ let bc = new BroadcastChannel(data.id);
+ bc.addEventListener("message", test.step_func_done(e => {
+ assert_equals(e.data.hasOpener, data.hasOpener);
+ }), {once: true});
+
+ document.getElementById(data.id).click();
+ }, data.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/alternate-css-ref.html b/testing/web-platform/tests/html/semantics/links/linktypes/alternate-css-ref.html
new file mode 100644
index 0000000000..ec961eac15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/alternate-css-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference of Alternate css</title>
+<link rel="stylesheet" href="preferred.css" title="preferred">
+<div>foobar</div>
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/alternate-css.html b/testing/web-platform/tests/html/semantics/links/linktypes/alternate-css.html
new file mode 100644
index 0000000000..366d6c5593
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/alternate-css.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Alternate css</title>
+<link rel="match" href="alternate-css-ref.html">
+<link rel="stylesheet" href="preferred.css" title="preferred">
+<link rel="alternate stylesheet" href="alternate.css" title="alternate">
+<div>foobar</div>
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/alternate-import.css b/testing/web-platform/tests/html/semantics/links/linktypes/alternate-import.css
new file mode 100644
index 0000000000..7db3df1d78
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/alternate-import.css
@@ -0,0 +1,3 @@
+body {
+ background-color: black;
+}
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/alternate.css b/testing/web-platform/tests/html/semantics/links/linktypes/alternate.css
new file mode 100644
index 0000000000..b101ab91f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/alternate.css
@@ -0,0 +1,5 @@
+@import url("alternate-import.css");
+
+div {
+ background-color: red;
+}
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-lower.css b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-lower.css
new file mode 100644
index 0000000000..a19c9dfd72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-lower.css
@@ -0,0 +1 @@
+#z-lower:after { content: "PASS"; color: green; }
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-mixed.css b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-mixed.css
new file mode 100644
index 0000000000..7389ea1a1a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-mixed.css
@@ -0,0 +1 @@
+#z-mixed:after { content: "PASS"; color: green; }
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-other.css b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-other.css
new file mode 100644
index 0000000000..a6c2616d86
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-other.css
@@ -0,0 +1 @@
+#z-other:after { content: "FAIL"; color: red; }
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-ref.html b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-ref.html
new file mode 100644
index 0000000000..5ac2432547
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+p:after { font-weight: bold; }
+p:after { content: "PASS"; color: green; }
+</style>
+<p>text/css treated as CSS?
+<p>TeXt/CsS treated as CSS?
+<p>text/cſs ignored?
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.css b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.css
new file mode 100644
index 0000000000..5d647d0f2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.css
@@ -0,0 +1,3 @@
+p:after { font-weight: bold; }
+p:after { content: "FAIL"; color: red; }
+#z-other:after { content: "PASS"; color: green; }
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.html b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.html
new file mode 100644
index 0000000000..39fd5520d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/link-type-stylesheet/process-stylesheet-linked-resource-ascii-case-insensitive.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-stylesheet:process-the-linked-resource">
+<link rel="help" href="https://html.spec.whatwg.org/#content-type">
+<link rel="help" href="https://mimesniff.spec.whatwg.org/#mime-type-representation">
+<link rel="match" href="process-stylesheet-linked-resource-ascii-case-insensitive-ref.html">
+<meta name="assert" content="link@type values for stylesheet resources are ASCII case-insensitive">
+<link rel="stylesheet" href="process-stylesheet-linked-resource-ascii-case-insensitive.css">
+<link rel="stylesheet" href="process-stylesheet-linked-resource-ascii-case-insensitive-lower.css" type="text/css">
+<link rel="stylesheet" href="process-stylesheet-linked-resource-ascii-case-insensitive-mixed.css" type="TeXt/CsS">
+<link rel="stylesheet" href="process-stylesheet-linked-resource-ascii-case-insensitive-other.css" type="text/cſs">
+<p id="z-lower">text/css treated as CSS?
+<p id="z-mixed">TeXt/CsS treated as CSS?
+<p id="z-other">text/cſs ignored?
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/original-id.json b/testing/web-platform/tests/html/semantics/links/linktypes/original-id.json
new file mode 100644
index 0000000000..1e5f7b5ed3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/original-id.json
@@ -0,0 +1 @@
+{"original_id":"linkTypes"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/links/linktypes/preferred.css b/testing/web-platform/tests/html/semantics/links/linktypes/preferred.css
new file mode 100644
index 0000000000..54b95ac280
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/links/linktypes/preferred.css
@@ -0,0 +1,3 @@
+div {
+ border: 4px solid green;
+}
diff --git a/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.html b/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.html
new file mode 100644
index 0000000000..5fcce1421b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<!--The permission element should have no end tag or content.
+ Therefore the parsing should stop after the beginning tag, and the following
+ element and text will become part of the 'body' element's contents.
+-->
+<permission type="geolocation"><span>this is some text</span></permission>
+
+<script>
+ test(function(){
+ assert_equals(3, document.body.childElementCount); // permission, span, script
+ assert_equals(document.body.innerText, "this is some text", "The text should be part of the 'body' element's text");
+
+ assert_true(document.body.children[0] instanceof HTMLPermissionElement, "First element should be a permission element");
+ var permission = document.body.children[0];
+ assert_equals(permission.innerText, "", "The permission element should have no text");
+ assert_equals(permission.childElementCount, 0, "The permission element should have no children");
+
+ assert_true(document.body.children[1] instanceof HTMLSpanElement, "Second element should be a span element");
+ var span = document.body.children[1];
+ assert_equals(span.innerText, "this is some text", "The span element should contain the text");
+ }, "The permission element should have no end tag or contents");
+</script>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/popovers/WEB_FEATURES.yml b/testing/web-platform/tests/html/semantics/popovers/WEB_FEATURES.yml
new file mode 100644
index 0000000000..e1b9f82de3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: popover
+ files: "**"
diff --git a/testing/web-platform/tests/html/semantics/popovers/hide-other-popover-side-effects.html b/testing/web-platform/tests/html/semantics/popovers/hide-other-popover-side-effects.html
new file mode 100644
index 0000000000..7cc95a95e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/hide-other-popover-side-effects.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://chromium-review.googlesource.com/c/chromium/src/+/4094463/8/third_party/blink/renderer/core/html/html_element.cc#1404">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=popover1 popover=auto>popover1</div>
+<div id=popover2 popover=auto>popover2</div>
+
+<script>
+test(() => {
+ const popover2 = document.getElementById('popover2');
+ popover1.showPopover();
+ popover1.addEventListener('beforetoggle', () => {
+ popover2.remove();
+ });
+ assert_throws_dom('InvalidStateError', () => popover2.showPopover(),
+ "popover1's beforetoggle event handler removes popover2 so showPopover should throw.");
+ assert_false(popover2.matches(':popover-open'), 'popover2 should not match :popover-open once it is closed.');
+}, 'Removing a popover while it is opening and force closing another popover should throw an exception.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/invoker-show-crash.html b/testing/web-platform/tests/html/semantics/popovers/invoker-show-crash.html
new file mode 100644
index 0000000000..7da57f9788
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/invoker-show-crash.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1463384">
+<link rel=help href="https://github.com/whatwg/html/issues/9383">
+
+<button id=button popovertarget=popover>button</button>
+<div id=popover popover=auto>popover</div>
+
+<script>
+button.click();
+popover.showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/label-in-invoker.html b/testing/web-platform/tests/html/semantics/popovers/label-in-invoker.html
new file mode 100644
index 0000000000..bf8ab9710d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/label-in-invoker.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1523168">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<button popovertarget=mypopover>
+ <label>label</label>
+</button>
+<div id=mypopover popover=auto>popover</div>
+
+<script>
+promise_test(async() => {
+ const label = document.querySelector('label');
+ assert_false(mypopover.matches(':popover-open'),
+ 'Popover should be closed at the start of the test.');
+ await test_driver.click(label);
+ assert_true(mypopover.matches(':popover-open'),
+ 'The popover should be opened by clicking on the label.');
+}, 'Buttons with popovertarget should invoke targets even if there is a label in the button.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/light-dismiss-event-ordering.html b/testing/web-platform/tests/html/semantics/popovers/light-dismiss-event-ordering.html
new file mode 100644
index 0000000000..be39050ac6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/light-dismiss-event-ordering.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://chromium-review.googlesource.com/c/chromium/src/+/4023021">
+<link rel=help href="https://github.com/whatwg/html/pull/8221#discussion_r1041135388">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<button id=target>target</button>
+<div id=popover popover=auto>popover</div>
+
+<script>
+for (const capture of [true, false]) {
+ for (const eventName of ['pointerdown', 'pointerup', 'mousedown', 'mouseup', 'click']) {
+ promise_test(async t => {
+ t.add_cleanup(() => {
+ try {
+ popover.hidePopover();
+ } catch {}
+ });
+
+ popover.showPopover();
+ document.addEventListener(eventName, event => {
+ event.preventDefault();
+ }, {capture, once: true});
+ // Click away from the popover to activate light dismiss.
+ await clickOn(target);
+ assert_equals(document.querySelectorAll(':popover-open').length, 0,
+ 'The popover should be closed via light dismiss even when preventDefault is called.');
+
+ popover.showPopover();
+ document.addEventListener(eventName, event => {
+ event.stopPropagation();
+ }, {capture, once: true});
+ // Click away from the popover to activate light dismiss.
+ await clickOn(target);
+ assert_equals(document.querySelectorAll(':popover-open').length, 0,
+ 'The popover should be closed via light dismiss even when stopPropagation is called.');
+
+ }, `Tests the interactions between popover light dismiss and pointer/mouse events. eventName: ${eventName}, capture: ${capture}`);
+ }
+}
+
+promise_test(async t => {
+ t.add_cleanup(() => {
+ try {
+ popover.hidePopover();
+ } catch {}
+ });
+ popover.showPopover();
+
+ const expectedEvents = [
+ 'pointerdown',
+ 'mousedown',
+ 'beforetoggle newState: closed',
+ 'pointerup',
+ 'mouseup',
+ 'click'
+ ];
+ const events = [];
+
+ for (const eventName of ['pointerdown', 'pointerup', 'mousedown', 'mouseup', 'click']) {
+ document.addEventListener(eventName, () => events.push(eventName));
+ }
+ popover.addEventListener('beforetoggle', event => {
+ events.push('beforetoggle newState: ' + event.newState);
+ });
+
+ // Click away from the popover to activate light dismiss.
+ await clickOn(target);
+
+ assert_array_equals(events, expectedEvents,
+ 'pointer and popover events should be fired in the correct order.');
+
+ assert_equals(document.querySelectorAll(':popover-open').length, 0,
+ 'The popover should be closed via light dismiss.');
+
+}, 'Tests the order of pointer/mouse events during popover light dismiss.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display-ref.html
new file mode 100644
index 0000000000..9530e7d3c4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<p>There should be a green box attached to the right side of each orange box.</p>
+<div class=ex><div class=anchor></div><div class=popover></div></div>
+<div class=ex><div class=anchor></div><div class=popover></div></div>
+
+<style>
+ .ex {
+ margin: 25px;
+ font-size: 0;
+ }
+ .ex div {
+ display:inline-block;
+ width: 100px;
+ height: 100px;
+ }
+ .anchor {
+ background: orange;
+ }
+ .popover {
+ background: lime;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display.tentative.html
new file mode 100644
index 0000000000..435929a6c1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-change-display.tentative.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:xiaochengh@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-anchor-change-display-ref.html">
+<script src="resources/popover-utils.js"></script>
+
+<p>There should be a green box attached to the right side of each orange box.</p>
+
+<div class=ex>
+ <div class=anchor id=anchor1></div>
+ <div id=popover1 popover=manual defaultopen></div>
+</div>
+
+<div class=ex>
+ <div class=anchor id=will-be-anchor2></div>
+ <div id=popover2 popover=manual anchor=anchor2 defaultopen></div>
+</div>
+
+<script>
+showDefaultopenPopoversOnLoad();
+
+function runTest() {
+ document.body.offsetLeft; // Force layout
+
+ document.getElementById('popover1').setAttribute('anchor', 'anchor1');
+ document.getElementById('will-be-anchor2').setAttribute('id', 'anchor2');
+}
+window.addEventListener('load', runTest);
+</script>
+
+<style>
+ .ex {
+ margin: 25px;
+ }
+ .ex div {
+ width: 100px;
+ height: 100px;
+ }
+ .anchor {
+ background: orange;
+ }
+ [popover] {
+ background: lime;
+ padding:0;
+ border:0;
+ left: anchor(right);
+ top: anchor(top);
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-none.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-none.tentative.html
new file mode 100644
index 0000000000..55a11fafdb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-none.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests that a popover can be anchored to an unrendered element.</title>
+<link rel=author href="mailto:xiaochengh@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=popover popover anchor=anchor></div>
+<div id=anchor></div>
+
+<style>
+ #anchor {
+ display: none;
+ }
+ [popover] {
+ background: lime;
+ padding: 0;
+ border: 0;
+ width: 100px;
+ height: 100px;
+ top: anchor(top, 100px);
+ left: anchor(left, 100px);
+ }
+</style>
+
+<script>
+test(() => {
+ popover.showPopover();
+ assert_equals(popover.offsetLeft, 100);
+ assert_equals(popover.offsetTop, 100);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-ref.html
new file mode 100644
index 0000000000..f701810da2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+
+<p>There should be a green box attached to the right side of each orange box.</p>
+<div class=ex id=ex1><div class=anchor></div><div class=popover></div></div>
+<div class=ex id=ex2><div class=anchor></div><div class=popover></div></div>
+<div class=ex id=ex3><div class=anchor></div><div class=popover></div></div>
+<div class=ex id=ex4><div class=anchor></div><div class=popover></div></div>
+<div class=ex id=ex5><div class=anchor></div><div class=popover></div></div>
+<div class=ex id=ex6><div class=anchor></div><div class=popover></div></div>
+
+<style>
+ .ex {
+ margin: 15px;
+ font-size: 0;
+ }
+ .ex div {
+ display:inline-block;
+ width: 50px;
+ height: 50px;
+ }
+ .anchor {
+ background: orange;
+ }
+ .popover {
+ background: lime;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html
new file mode 100644
index 0000000000..d50dd6c857
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-anchor-display-ref.html">
+<link rel=stylesheet href="/fonts/ahem.css">
+<script src="resources/popover-utils.js"></script>
+
+<p>There should be a green box attached to the right side of each orange box.</p>
+
+<!-- Example using the `anchor` implicit reference element -->
+<div class=ex>
+ <div class=anchor id=anchor1></div>
+ <div id=popover1 popover=manual anchor=anchor1 defaultopen></div>
+</div>
+
+<!-- Example with `anchor` attribute but not using it for anchor pos -->
+<div class=ex>
+ <div id=anchor2 class=anchor></div>
+ <div id=popover2 popover=manual anchor defaultopen></div>
+</div>
+
+<!-- Example using `anchor-name` plus inset, and no `anchor` attribute -->
+<div class=ex>
+ <div id=anchor3 class=anchor></div>
+ <div id=popover3 popover=manual defaultopen></div>
+</div>
+
+<!-- Example using implicit anchor reference and inline anchor element -->
+<div class=ex>
+ <span id=anchor4>X</span>
+ <div id=popover4 popover=manual anchor=anchor4 defaultopen></div>
+</div>
+
+<!-- Example using an implicit anchor which is not the default anchor -->
+<div class=ex>
+ <div class=anchor id=anchor5></div>
+ <div id=popover5 popover=manual anchor=anchor5 defaultopen></div>
+</div>
+
+<!-- Example using a default anchor which is not the implicit anchor -->
+<div class=ex>
+ <div class=anchor id=anchor6></div>
+ <div id=popover6 popover=manual anchor=anchor1 defaultopen></div>
+</div>
+
+<script>
+showDefaultopenPopoversOnLoad();
+</script>
+
+<style>
+ .ex {
+ margin: 15px;
+ }
+ .ex div {
+ width: 50px;
+ height: 50px;
+ }
+ .anchor {
+ background: orange;
+ }
+ [popover] {
+ background: lime;
+ padding:0;
+ border:0;
+ }
+ #popover1 {
+ left: anchor(right);
+ top: anchor(top);
+ }
+ #anchor2 {
+ anchor-name: --anchor2;
+ }
+ #popover2 {
+ left: anchor(--anchor2 right);
+ top: anchor(--anchor2 top);
+ }
+ #anchor3 {
+ anchor-name: --anchor3;
+ }
+ #popover3 {
+ inset:auto;
+ left: anchor(--anchor3 right);
+ top: anchor(--anchor3 top);
+ }
+ #anchor4 {
+ font-family: Ahem;
+ font-size: 50px;
+ color: orange;
+ }
+ #popover4 {
+ left: anchor(right);
+ top: anchor(top);
+ }
+ #popover5 {
+ anchor-default: --anchor1; /* shouldn't be used */
+ left: anchor(implicit right);
+ top: anchor(implicit top);
+ }
+ #anchor6 {
+ anchor-name: --anchor6;
+ }
+ #popover6 {
+ anchor-default: --anchor6;
+ left: anchor(right); /* shouldn't use the implicit anchor */
+ top: anchor(top);
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-idl-property.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-idl-property.tentative.html
new file mode 100644
index 0000000000..1e255339f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-idl-property.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div>
+ <button id=b1>This is an anchor button</button>
+ <div popover id=p1 anchor=b1>This is a popover</div>
+ <button id=b2 popovertarget=p1>This button invokes the popover but isn't an anchor</button>
+</div>
+
+<script>
+ test(function() {
+ assert_equals(p1.anchorElement,b1);
+ }, "popover anchorElement IDL property returns the anchor element");
+
+ test(function() {
+ assert_equals(p1.anchorElement,b1);
+ p1.anchorElement = b2;
+ assert_equals(p1.anchorElement,b2);
+ assert_equals(p1.getAttribute('anchor'),'','Idref is empty after setting element');
+ p1.anchorElement = b1; // Reset
+ }, "popover anchorElement is settable");
+</script>
+
+<div>
+ <button id=b3>button</button>
+ <div id=p2>Anchored div</div>
+</div>
+<style>
+ * {margin:0;padding:0;}
+ #b3 {width: 200px;}
+ #p2 {
+ position: absolute;
+ left: anchor(right);
+ }
+</style>
+
+<script>
+ test(function() {
+ assert_equals(p2.anchorElement,null);
+ const button = document.getElementById('b3');
+ assert_true(!!button);
+ p2.anchorElement = button;
+ assert_equals(p2.getAttribute('anchor'),'','Idref should be empty after setting element');
+ assert_equals(p2.anchorElement,button,'Element reference should be button');
+ assert_equals(p2.offsetLeft, 200, 'The anchor relationship should be functional');
+ }, "anchorElement affects anchor positioning");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-multicol-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-multicol-display.tentative.html
new file mode 100644
index 0000000000..fe65ec5ba4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-multicol-display.tentative.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Tests popovers with implicit anchors in out-of-flow boxes</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#determining">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<style>
+.relpos {
+ position: relative;
+}
+.columns {
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 10px;
+ column-width: 100px;
+ width: 210px;
+ height: 50px;
+}
+#anchor1 {
+ position: absolute;
+ width: 10px;
+ height: 30px;
+ background: orange;
+}
+.target {
+ /*
+ * We need a popover to use implicit anchors, and force showing it with CSS
+ * so that it's not in the top layer.
+ */
+ display: block;
+ position: absolute;
+ margin: 0;
+ border: 0;
+ padding: 0;
+ width: anchor-size(width);
+ height: anchor-size(height);
+ background: lime;
+}
+</style>
+<body onload="checkLayout('.target')">
+ <div class="spacer" style="height: 10px"></div>
+ <div class="relpos">
+ <div class="columns">
+ <div class="spacer" style="height: 10px"></div>
+ <div class="relpos">
+ <div class="spacer" style="height: 10px"></div>
+ <div class="relpos">
+ <div class="spacer" style="height: 10px"></div>
+ <div id="anchor1"></div>
+ </div>
+ <div class="target" popover anchor="anchor1"
+ data-expected-height=50></div>
+ </div>
+ </div>
+ </div>
+
+</body>
+
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display-ref.html
new file mode 100644
index 0000000000..9942b41e36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:xiaochengh@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+
+<button id=main-menu-button>Show menu</button>
+
+<div id=main-menu>
+ <div>Foo</div>
+ <button id=nested-menu-button>
+ Show nested menu
+ </button>
+ <div>Bar</div>
+</div>
+
+<div id=nested-menu>
+ Baz
+</div>
+
+<style>
+#main-menu-button {
+ position: absolute;
+ top: 200px;
+ left: 100px;
+ width: 100px;
+}
+
+#main-menu {
+ position: absolute;
+ top: 200px;;
+ left: 200px;
+ width: 150px;
+ line-height: 20px;
+}
+
+#nested-menu-button {
+ width: 100%;
+}
+
+#nested-menu {
+ position: absolute;
+ top: 220px;
+ left: 350px;
+}
+
+[popover] {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+</style>
+
+<script>
+document.getElementById('main-menu-button').click();
+document.getElementById('nested-menu-button').click();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display.tentative.html
new file mode 100644
index 0000000000..b60ff49e09
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nested-display.tentative.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:xiaochengh@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-anchor-nested-display-ref.html">
+
+<button id=main-menu-button popovertarget=main-menu>Show menu</button>
+
+<div id=main-menu popover anchor=main-menu-button>
+ <div>Foo</div>
+ <button id=nested-menu-button popovertarget=nested-menu>
+ Show nested menu
+ </button>
+ <div>Bar</div>
+</div>
+
+<div id=nested-menu popover anchor=nested-menu-button>
+ Baz
+</div>
+
+<style>
+#main-menu-button {
+ position: absolute;
+ top: 200px;
+ left: 100px;
+ width: 100px;
+}
+
+#main-menu {
+ top: anchor(top);
+ left: anchor(right);
+ width: 150px;
+ line-height: 20px;
+}
+
+#nested-menu-button {
+ width: 100%;
+}
+
+#nested-menu {
+ top: anchor(top);
+ left: anchor(right);
+}
+
+[popover] {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+</style>
+
+<script>
+document.getElementById('main-menu-button').click();
+document.getElementById('nested-menu-button').click();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nesting.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nesting.tentative.html
new file mode 100644
index 0000000000..c3ea4f2165
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-nesting.tentative.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover anchor nesting</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<body>
+
+<!-- This example has the anchor (b1) for one popover (p1)
+ which contains a separate popover (p2) which is anchored
+ by a separate anchor (b2). -->
+<button id=b1 onclick='p1.showPopover()'>Popover 1
+ <div popover id=p2 anchor=b2>
+ <span id=inside2>Inside popover 2</span>
+ </div>
+</button>
+<div popover id=p1 anchor=b1>This is popover 1</div>
+<button id=b2 onclick='p2.showPopover()'>Popover 2</button>
+
+<style>
+ #p1 { top:50px; }
+ #p2 { top:50px; left:250px; }
+ [popover] { border: 5px solid red; }
+</style>
+
+
+<script>
+ const popover1 = document.querySelector('#p1');
+ const button1 = document.querySelector('#b1');
+ const popover2 = document.querySelector('#p2');
+
+ (async function() {
+ setup({ explicit_done: true });
+
+ popover2.showPopover();
+ assert_false(popover1.matches(':popover-open'));
+ assert_true(popover2.matches(':popover-open'));
+ await clickOn(button1);
+ test(t => {
+ // Button1 is the anchor for popover1, and an ancestor of popover2.
+ // Since popover2 is open, but not popover1, button1 should not be
+ // the anchor of any open popover. So popover2 should be closed.
+ assert_false(popover2.matches(':popover-open'));
+ assert_true(popover1.matches(':popover-open'));
+ },'Nested popovers (inside anchor elements) do not affect light dismiss');
+
+ done();
+ })();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display-ref.html
new file mode 100644
index 0000000000..926a171e9a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<div class=spacer style="height: 200px"></div>
+
+<p>There should be a green box attached to the right side of each orange box.</p>
+<div class=ex id=ex1><div class=anchor></div><div class=popover></div></div>
+<div class=ex id=ex2><div class=anchor></div><div class=popover></div></div>
+
+<div class=spacer style="height: 200vh"></div>
+
+<style>
+ .ex {
+ margin: 25px;
+ font-size: 0;
+ }
+ .ex div {
+ display:inline-block;
+ width: 100px;
+ height: 100px;
+ }
+ .anchor {
+ background: orange;
+ }
+ .popover {
+ background: lime;
+ }
+</style>
+
+<script>
+document.documentElement.scrollTop = 100;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html
new file mode 100644
index 0000000000..7ed6cf1adf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<link rel=author href="mailto:xiaochengh@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-anchor-scroll-display-ref.html">
+
+<div class=spacer style="height: 200px"></div>
+
+<p>There should be a green box attached to the right side of each orange box.</p>
+
+<!-- Example using the `anchor` implicit reference element -->
+<div class=ex>
+ <div class=anchor id=anchor1></div>
+ <div id=popover1 popover=manual anchor=anchor1></div>
+</div>
+
+<!-- Example using a default anchor that is not the implicit anchor -->
+<div class=ex>
+ <div class=anchor id=anchor2></div>
+ <div id=popover2 popover=manual anchor=fake-anchor></div>
+</div>
+
+<!-- A position:fixed fake anchor. Any popover anchored to it won't move when
+ the document is scrolled. -->
+<div id=fake-anchor></div>
+
+<div class=spacer style="height: 200vh"></div>
+
+<style>
+ .ex {
+ margin: 25px;
+ }
+ .ex div {
+ width: 100px;
+ height: 100px;
+ }
+ .anchor {
+ background: orange;
+ }
+ [popover] {
+ background: lime;
+ padding:0;
+ border:0;
+ }
+ #popover1 {
+ left: anchor(right);
+ top: anchor(top);
+ }
+ #fake-anchor {
+ position: fixed;
+ anchor-name: --fake-anchor;
+ }
+ #anchor2 {
+ anchor-name: --anchor2;
+ }
+ #popover2 {
+ anchor-default: --anchor2;
+ left: anchor(right);
+ top: anchor(top);
+ }
+</style>
+
+<script>
+function raf() {
+ return new Promise(resolve => requestAnimationFrame(resolve));
+}
+
+async function runTest() {
+ document.querySelectorAll('[popover]').forEach(
+ popover => popover.showPopover());
+
+ // Render a frame at the intial scroll position.
+ await raf();
+ await raf();
+
+ document.documentElement.scrollTop = 100;
+ document.documentElement.classList.remove('reftest-wait');
+
+ // The popover should still be attached to the anchor.
+}
+runTest();
+</script>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-transition.tentative.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-transition.tentative.tentative.html
new file mode 100644
index 0000000000..ae2a3a8e41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-transition.tentative.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests transitioning display property of anchored popover</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel="help" href="https://github.com/whatwg/html/pull/9144">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+#target {
+ transition: display 2s;
+}
+</style>
+
+<div popover anchor id="target">
+ Popover
+</div>
+
+<script>
+test(() => {
+ target.showPopover();
+ const xBefore = target.offsetLeft;
+ const yBefore = target.offsetTop;
+
+ target.hidePopover();
+ assert_equals(target.offsetLeft, xBefore, 'Should not shift in x axis');
+ assert_equals(target.offsetTop, yBefore, 'Should not shift in y axis')
+}, 'Transitioning display property of an anchored popover should not cause a position shift');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-and-svg-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-and-svg-ref.html
new file mode 100644
index 0000000000..db52e77d2b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-and-svg-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover is only effective on HTMLElement, not on svg element</title>
+<style>
+svg {
+ width: 100px;
+ height: 100px;
+ background-color:green;
+}
+</style>
+<svg ></svg>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-and-svg.html b/testing/web-platform/tests/html/semantics/popovers/popover-and-svg.html
new file mode 100644
index 0000000000..c5e8bb42a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-and-svg.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover is only effective on HTMLElement, not on svg element</title>
+<link rel="author" href="mailto:cathiechen@igalia.com">
+<link rel=help href="https://html.spec.whatwg.org/#the-popover-attribute">
+<link rel="match" href="popover-and-svg-ref.html">
+<style>
+svg {
+ width: 100px;
+ height: 100px;
+ background-color:green;
+}
+[popover] {
+ top: 100px;
+ bottom: auto;
+}
+</style>
+<svg popover></svg>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-appearance-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-appearance-ref.html
new file mode 100644
index 0000000000..7ceca94559
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-appearance-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover element appearance</title>
+<link rel="stylesheet" href="resources/popover-styles.css">
+
+<style>
+.fake-popover {top: 100px; bottom: auto;}
+#blank {left: -300px;}
+#auto {left: -100px;}
+#manual {left: 100px;}
+#invalid {left: 300px;}
+</style>
+
+<p>There should be four popovers with similar appearance.</p>
+<div class="fake-popover" id=blank>Blank</div>
+<div class="fake-popover" id=auto>Auto</div>
+<div class="fake-popover" id=manual>Manual</div>
+<div class="fake-popover" id=invalid>Invalid</div>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-appearance.html b/testing/web-platform/tests/html/semantics/popovers/popover-appearance.html
new file mode 100644
index 0000000000..e9050bdeb9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-appearance.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover element appearance</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel="match" href="popover-appearance-ref.html">
+
+<style>
+[popover] {top: 100px; bottom: auto;}
+[popover=""] {left: -300px}
+[popover=auto] {left: -100px; }
+[popover=manual] {left: 100px; }
+[popover=invalid] {left: 300px; }
+</style>
+
+<p>There should be four popovers with similar appearance.</p>
+<div popover>Blank
+ <div popover=auto>Auto</div>
+</div>
+<div popover=manual>Manual</div>
+<!-- This ensures unsupported popover values are treated as popover=manual -->
+<div popover=invalid>Invalid</div>
+<script>
+ document.querySelectorAll('[popover]').forEach(p => p.showPopover());
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-attribute-all-elements.html b/testing/web-platform/tests/html/semantics/popovers/popover-attribute-all-elements.html
new file mode 100644
index 0000000000..5a536f026e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-attribute-all-elements.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+<script src="../../resources/common.js"></script>
+
+<body>
+<script>
+setup({ explicit_done: true });
+window.onload = () => {
+ // Loop through all HTML elements that render a box by default:
+ let elementsThatDontRender = ['area', 'audio','base','br','datalist','dialog','embed','head','link','meta','noscript','optgroup','option','param','rp','script','slot','style','template','title','wbr'];
+ const elements = HTML5_ELEMENTS.filter(el => !elementsThatDontRender.includes(el));
+ elements.forEach(tag => {
+ test((t) => {
+ const element = document.createElement(tag);
+ element.setAttribute('popover','auto');
+ document.body.appendChild(element);
+ t.add_cleanup(() => element.remove());
+ assertIsFunctionalPopover(element, true);
+ }, `A <${tag} popover> element should behave as a popover.`);
+ test((t) => {
+ const element = document.createElement(tag);
+ document.body.appendChild(element);
+ t.add_cleanup(() => element.remove());
+ assertNotAPopover(element);
+ }, `A <${tag}> element should *not* behave as a popover.`);
+ });
+ elementsThatDontRender.forEach(tag => {
+ test((t) => {
+ const element = document.createElement(tag);
+ element.setAttribute('popover','auto');
+ document.body.appendChild(element);
+ t.add_cleanup(() => element.remove());
+ assertIsFunctionalPopover(element, false);
+ }, `A <${tag} popover> element should not be rendered.`);
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html b/testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html
new file mode 100644
index 0000000000..2af3bbc137
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html
@@ -0,0 +1,359 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<div id=popovers>
+ <div popover id=boolean>Popover</div>
+ <div popover="">Popover</div>
+ <div popover=auto>Popover</div>
+ <div popover=hint>Popover</div>
+ <div popover=manual>Popover</div>
+ <article popover>Different element type</article>
+ <header popover>Different element type</header>
+ <nav popover>Different element type</nav>
+ <input type=text popover value="Different element type">
+ <dialog popover>Dialog with popover attribute</dialog>
+ <dialog popover="manual">Dialog with popover=manual</dialog>
+ <div popover=true>Invalid popover value - defaults to popover=manual</div>
+ <div popover=popover>Invalid popover value - defaults to popover=manual</div>
+ <div popover=invalid>Invalid popover value - defaults to popover=manual</div>
+</div>
+
+<div id=nonpopovers>
+ <div>Not a popover</div>
+ <dialog open>Dialog without popover attribute</dialog>
+</div>
+
+<div id=outside></div>
+<style>
+[popover] {
+ inset:auto;
+ top:0;
+ left:0;
+}
+#outside {
+ position:fixed;
+ top:200px;
+ left:200px;
+ height:10px;
+ width:10px;
+}
+</style>
+
+<script>
+setup({ explicit_done: true });
+window.onload = () => {
+ const outsideElement = document.getElementById('outside');
+
+ // Start with the provided examples:
+ Array.from(document.getElementById('popovers').children).forEach(popover => {
+ test((t) => {
+ assertIsFunctionalPopover(popover, true);
+ }, `The element ${popover.outerHTML} should behave as a popover.`);
+ });
+ Array.from(document.getElementById('nonpopovers').children).forEach(nonPopover => {
+ test((t) => {
+ assertNotAPopover(nonPopover);
+ }, `The element ${nonPopover.outerHTML} should *not* behave as a popover.`);
+ });
+
+ function createPopover(t) {
+ const popover = document.createElement('div');
+ document.body.appendChild(popover);
+ t.add_cleanup(() => popover.remove());
+ popover.setAttribute('popover','auto');
+ return popover;
+ }
+
+ test((t) => {
+ // You can set the `popover` attribute to anything.
+ // Setting the `popover` IDL to a string sets the content attribute to exactly that, always.
+ // Getting the `popover` IDL value only retrieves valid values.
+ const popover = createPopover(t);
+ assert_equals(popover.popover,'auto');
+ popover.setAttribute('popover','auto');
+ assert_equals(popover.popover,'auto');
+ popover.setAttribute('popover','AuTo');
+ assert_equals(popover.popover,'auto','Case is normalized in IDL');
+ assert_equals(popover.getAttribute('popover'),'AuTo','Case is *not* normalized/changed in the content attribute');
+ popover.popover='aUtO';
+ assert_equals(popover.popover,'auto','Case is normalized in IDL');
+ assert_equals(popover.getAttribute('popover'),'aUtO','Value set from IDL is propagated exactly to the content attribute');
+ popover.setAttribute('popover','invalid');
+ assert_equals(popover.popover,'manual','Invalid values should reflect as "manual"');
+ popover.removeAttribute('popover');
+ assert_equals(popover.popover,null,'No value should reflect as null');
+ if (popoverHintSupported()) {
+ popover.popover='hint';
+ assert_equals(popover.getAttribute('popover'),'hint');
+ }
+ popover.popover='auto';
+ assert_equals(popover.getAttribute('popover'),'auto');
+ popover.popover='';
+ assert_equals(popover.getAttribute('popover'),'');
+ assert_equals(popover.popover,'auto');
+ popover.popover='AuTo';
+ assert_equals(popover.getAttribute('popover'),'AuTo');
+ assert_equals(popover.popover,'auto');
+ popover.popover='invalid';
+ assert_equals(popover.getAttribute('popover'),'invalid','IDL setter allows any value');
+ assert_equals(popover.popover,'manual','but IDL getter reflects "manual"');
+ popover.popover='';
+ assert_equals(popover.getAttribute('popover'),'','IDL setter propagates exactly');
+ assert_equals(popover.popover,'auto','Empty should map to auto in IDL');
+ popover.popover='auto';
+ popover.popover=null;
+ assert_equals(popover.getAttribute('popover'),null,'Setting null for the IDL property should remove the content attribute');
+ assert_equals(popover.popover,null,'Null returns null');
+ popover.popover='auto';
+ popover.popover=undefined;
+ assert_equals(popover.getAttribute('popover'),null,'Setting undefined for the IDL property should remove the content attribute');
+ assert_equals(popover.popover,null,'undefined returns null');
+ },'IDL attribute reflection');
+
+ test((t) => {
+ const popover = createPopover(t);
+ assertIsFunctionalPopover(popover, true);
+ popover.removeAttribute('popover');
+ assertNotAPopover(popover);
+ popover.setAttribute('popover','AuTo');
+ assertIsFunctionalPopover(popover, true);
+ popover.removeAttribute('popover');
+ popover.setAttribute('PoPoVeR','AuTo');
+ assertIsFunctionalPopover(popover, true);
+ // Via IDL also
+ popover.popover = 'auto';
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'aUtO';
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'invalid'; // treated as "manual"
+ assertIsFunctionalPopover(popover, true);
+ },'Popover attribute value should be case insensitive');
+
+ test((t) => {
+ const popover = createPopover(t);
+ assertIsFunctionalPopover(popover, true);
+ popover.setAttribute('popover','manual'); // Change popover type
+ assertIsFunctionalPopover(popover, true);
+ popover.setAttribute('popover','invalid'); // Change popover type to something invalid
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'manual'; // Change popover type via IDL
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'invalid'; // Make invalid via IDL (treated as "manual")
+ assertIsFunctionalPopover(popover, true);
+ },'Changing attribute values for popover should work');
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ if (popoverHintSupported()) {
+ popover.setAttribute('popover','hint'); // Change popover type
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ }
+ popover.setAttribute('popover','manual');
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.setAttribute('popover','invalid');
+ assert_true(popover.matches(':popover-open'),'From "manual" to "invalid" (which is interpreted as "manual") should not close the popover');
+ popover.setAttribute('popover','auto');
+ assert_false(popover.matches(':popover-open'),'From "invalid" ("manual") to "auto" should hide the popover');
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.setAttribute('popover','invalid');
+ assert_false(popover.matches(':popover-open'),'From "auto" to "invalid" (which is interpreted as "manual") should close the popover');
+ },'Changing attribute values should close open popovers');
+
+ const validTypes = popoverHintSupported() ? ["auto","hint","manual"] : ["auto","manual"];
+ validTypes.forEach(type => {
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover',type);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.remove();
+ assert_false(popover.matches(':popover-open'));
+ document.body.appendChild(popover);
+ assert_false(popover.matches(':popover-open'));
+ },`Removing a visible popover=${type} element from the document should close the popover`);
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover',type);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_false(popover.matches(':modal'));
+ popover.hidePopover();
+ },`A showing popover=${type} does not match :modal`);
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover',type);
+ assert_false(popover.matches(':popover-open'));
+ // FIXME: Once :open/:closed are defined in HTML we should remove these two constants.
+ const openPseudoClassIsSupported = CSS.supports('selector(:open))');
+ const closePseudoClassIsSupported = CSS.supports('selector(:closed))');
+ assert_false(openPseudoClassIsSupported && popover.matches(':open'),'popovers never match :open');
+ assert_false(closePseudoClassIsSupported && popover.matches(':closed'),'popovers never match :closed');
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_false(openPseudoClassIsSupported && popover.matches(':open'),'popovers never match :open');
+ assert_false(closePseudoClassIsSupported && popover.matches(':closed'),'popovers never match :closed');
+ popover.hidePopover();
+ },`A popover=${type} never matches :open or :closed`);
+ });
+
+ test((t) => {
+ const other_popover = createPopover(t);
+ other_popover.setAttribute('popover','auto');
+ other_popover.showPopover();
+ const popover = createPopover(t);
+ popover.setAttribute('popover','auto');
+ other_popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ popover.setAttribute('popover','manual');
+ },{once: true});
+ assert_true(other_popover.matches(':popover-open'));
+ assert_false(popover.matches(':popover-open'));
+ assert_throws_dom('InvalidStateError', () => popover.showPopover());
+ assert_false(other_popover.matches(':popover-open'),'unrelated popover is hidden');
+ assert_false(popover.matches(':popover-open'),'popover is not shown if its type changed during show');
+ },`Changing the popover type in a "beforetoggle" event handler should throw an exception (during showPopover())`);
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover','auto');
+ const other_popover = createPopover(t);
+ other_popover.setAttribute('popover','auto');
+ popover.appendChild(other_popover);
+ popover.showPopover();
+ other_popover.showPopover();
+ let nested_popover_hidden=false;
+ other_popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ nested_popover_hidden = true;
+ popover.setAttribute('popover','manual');
+ },{once: true});
+ popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ assert_true(nested_popover_hidden,'The nested popover should be hidden first');
+ },{once: true});
+ assert_true(popover.matches(':popover-open'));
+ assert_true(other_popover.matches(':popover-open'));
+ popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
+ assert_false(other_popover.matches(':popover-open'),'unrelated popover is hidden');
+ assert_false(popover.matches(':popover-open'),'popover is still hidden if its type changed during hide event');
+ other_popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
+ },`Changing the popover type in a "beforetoggle" event handler during hidePopover() should not throw an exception`);
+
+ test(t => {
+ const popover = document.createElement('div');
+ assert_throws_dom('NotSupportedError', () => popover.hidePopover(),
+ 'Calling hidePopover on an element without a popover attribute should throw.');
+ popover.setAttribute('popover', 'auto');
+ popover.hidePopover(); // Calling hidePopover on a disconnected popover should not throw.
+ assert_throws_dom('InvalidStateError', () => popover.showPopover(),
+ 'Calling showPopover on a disconnected popover should throw.');
+ },'Calling hidePopover on a disconnected popover should not throw.');
+
+ function interpretedType(typeString,method) {
+ if (validTypes.includes(typeString))
+ return typeString;
+ if (typeString === undefined)
+ return "invalid-value-undefined";
+ if (method === "idl" && typeString === null)
+ return "invalid-value-idl-null";
+ return "manual"; // Invalid types default to "manual"
+ }
+ function setPopoverValue(popover,type,method) {
+ switch (method) {
+ case "attr":
+ if (type === undefined) {
+ popover.removeAttribute('popover');
+ } else {
+ popover.setAttribute('popover',type);
+ }
+ break;
+ case "idl":
+ popover.popover = type;
+ break;
+ default:
+ assert_notreached();
+ }
+ }
+ ["attr","idl"].forEach(method => {
+ validTypes.forEach(type => {
+ [...validTypes,"invalid",null,undefined].forEach(newType => {
+ [...validTypes,"invalid",null,undefined].forEach(inEventType => {
+ promise_test(async (t) => {
+ const popover = createPopover(t);
+ setPopoverValue(popover,type,method);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ let gotEvent = false;
+ popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ gotEvent = true;
+ setPopoverValue(popover,inEventType,method);
+ },{once:true});
+ setPopoverValue(popover,newType,method);
+ if (type===interpretedType(newType,method)) {
+ // Keeping the type the same should not hide it or fire events.
+ assert_true(popover.matches(':popover-open'),'popover should remain open when not changing the type');
+ assert_false(gotEvent);
+ try {
+ popover.hidePopover(); // Cleanup
+ } catch (e) {}
+ } else {
+ // Changing the type at all should hide the popover. The hide event
+ // handler should run, set a new type, and that type should end up
+ // as the final result.
+ assert_false(popover.matches(':popover-open'));
+ assert_true(gotEvent);
+ if (inEventType === undefined || (method ==="idl" && inEventType === null)) {
+ assert_throws_dom("NotSupportedError",() => popover.showPopover(),'We should have removed the popover attribute, so showPopover should throw');
+ } else {
+ // Make sure the attribute is correct.
+ assert_equals(popover.getAttribute('popover'),String(inEventType),'Content attribute');
+ assert_equals(popover.popover, interpretedType(inEventType,method),'IDL attribute');
+ // Make sure the type is really correct, via behavior.
+ popover.showPopover(); // Show it
+ assert_true(popover.matches(':popover-open'),'Popover should function');
+ await clickOn(outsideElement); // Try to light dismiss
+ switch (interpretedType(inEventType,method)) {
+ case 'manual':
+ assert_true(popover.matches(':popover-open'),'A popover=manual should not light-dismiss');
+ popover.hidePopover();
+ break;
+ case 'auto':
+ case 'hint':
+ assert_false(popover.matches(':popover-open'),'A popover=auto should light-dismiss');
+ break;
+ }
+ }
+ }
+ },`Changing a popover from ${type} to ${newType} (via ${method}), and then ${inEventType} during 'beforetoggle' works`);
+ });
+ });
+ });
+ });
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance-ref.html
new file mode 100644
index 0000000000..bf2b16c3f5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover ::backdrop pseudo element appearance</title>
+<link rel="stylesheet" href="resources/popover-styles.css">
+
+<style>
+#bottom { top: 70px; left: 70px; }
+#middle { top: 120px; left: 120px; }
+#top { top: 170px; left: 170px; }
+.fake-popover-backdrop {
+ height: 200px;
+ width: 200px;
+}
+#bottom-backdrop {
+ top: 50px;
+ left: 50px;
+ background-color: rgb(0, 50, 0);
+}
+#middle-backdrop {
+ top: 100px;
+ left: 100px;
+ background-color: rgb(0, 130, 0);
+}
+#top-backdrop {
+ top: 150px;
+ left: 150px;
+ background-color: rgb(0, 210, 0);
+}
+.fake-popover {
+ margin:0;
+}
+</style>
+<p>Test for [popover]::backdrop presence and stacking order. The test passes
+ if there are 3 stacked boxes, with the brightest green on top.</p>
+<div popover id=bottom>Bottom
+ <div popover id=middle>Middle
+ <div popover=manual id=top>Top</div>
+ </div>
+</div>
+<div id="bottom-backdrop" class="fake-popover-backdrop"></div>
+<div id="bottom" class="fake-popover">Bottom</div>
+<div id="middle-backdrop" class="fake-popover-backdrop"></div>
+<div id="middle" class="fake-popover">Middle</div>
+<div id="top-backdrop" class="fake-popover-backdrop"></div>
+<div id="top" class="fake-popover">Top</div>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance.html b/testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance.html
new file mode 100644
index 0000000000..cf57aee69e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-backdrop-appearance.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover ::backdrop pseudo element appearance</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel="match" href="popover-backdrop-appearance-ref.html">
+
+<style>
+#bottom { top: 70px; left: 70px; }
+#middle { top: 120px; left: 120px; }
+#top { top: 170px; left: 170px; }
+::backdrop { height: 200px; width: 200px; }
+#bottom::backdrop {
+ top: 50px;
+ left: 50px;
+ background-color: rgb(0, 50, 0);
+ z-index: 100; /* z-index has no effect. */
+}
+#middle::backdrop {
+ top: 100px;
+ left: 100px;
+ background-color: rgb(0, 130, 0);
+ z-index: -100; /* z-index has no effect. */
+}
+#top::backdrop {
+ top: 150px;
+ left: 150px;
+ background-color: rgb(0, 210, 0);
+ z-index: 0; /* z-index has no effect. */
+}
+[popover] {
+ margin:0;
+}
+</style>
+<p>Test for [popover]::backdrop presence and stacking order. The test passes
+ if there are 3 stacked boxes, with the brightest green on top.</p>
+<div popover id=bottom>Bottom
+ <div popover=hint id=middle>Middle
+ <div popover=manual id=top>Top</div>
+ </div>
+</div>
+<script>
+document.getElementById('bottom').showPopover();
+document.getElementById('middle').showPopover();
+document.getElementById('top').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-beforetoggle-opening-event.html b/testing/web-platform/tests/html/semantics/popovers/popover-beforetoggle-opening-event.html
new file mode 100644
index 0000000000..41bb9aa82c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-beforetoggle-opening-event.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover beforetoggle event</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div popover></div>
+
+<script>
+test(() => {
+ let frameCount = 0;
+ requestAnimationFrame(() => {++frameCount;});
+ const popover = document.querySelector('[popover]');
+ const testText = 'Show Event Occurred';
+ popover.addEventListener('beforetoggle',(e) => {
+ assert_false(e.bubbles, 'beforetoggle event does not bubble');
+ if (e.newState !== "open")
+ return;
+ popover.textContent = testText;
+ })
+ popover.offsetHeight;
+ assert_equals(popover.textContent,"");
+ assert_equals(frameCount,0);
+ popover.showPopover();
+ popover.offsetHeight;
+ assert_equals(popover.textContent,testText);
+ assert_equals(frameCount,0,'nothing should be rendered before the popover is updated');
+ popover.hidePopover(); // Cleanup
+},'Ensure the `beforetoggle` event can be used to populate content before the popover renders');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-change-type.html b/testing/web-platform/tests/html/semantics/popovers/popover-change-type.html
new file mode 100644
index 0000000000..978d1d1495
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-change-type.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9034">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/html/semantics/popovers/resources/popover-utils.js"></script>
+
+<div id=mypopover>popover</div>
+
+<script>
+promise_test(async () => {
+ const mypopover = document.getElementById('mypopover');
+
+ mypopover.popover = "manual";
+ mypopover.showPopover();
+
+ await new Promise(resolve => {
+ mypopover.addEventListener("beforetoggle", (e) => {
+ if (e.newState === "closed") {
+ mypopover.remove();
+ requestAnimationFrame(() => {
+ document.body.append(mypopover);
+ mypopover.showPopover();
+ resolve();
+ });
+ }
+ }, {once: true});
+
+ mypopover.popover = "auto";
+ });
+
+ assert_true(mypopover.matches(':popover-open'),
+ 'The popover should be open after the toggling sequence.');
+
+ await sendEscape();
+ assert_false(mypopover.matches(':popover-open'),
+ 'The popover should light dismiss because it is in the auto state.');
+}, 'Changing the popover attribute should always update the auto/manual behavior.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-close-request.html b/testing/web-platform/tests/html/semantics/popovers/popover-close-request.html
new file mode 100644
index 0000000000..830a40e060
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-close-request.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popover close request behavior</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="/close-watcher/resources/helpers.js"></script>
+
+<div popover id=p1>
+ Inside popover 1
+ <div popover id=p2>Inside popover 2</div>
+</div>
+
+<script>
+promise_test(async () => {
+ const popover1 = document.querySelector('#p1');
+ const popover2 = document.querySelector('#p2');
+
+ popover1.showPopover();
+
+ // Bless the opening of popover2, so it doesn't get grouped with popover1 by
+ // the close watcher infrastructure.
+ await blessTopLayer(popover1);
+ popover2.showPopover();
+
+ assert_true(popover1.matches(':popover-open'), "Starting: popover1 must be open");
+ assert_true(popover2.matches(':popover-open'), "Starting: popover2 must be open");
+
+ await sendCloseRequest();
+ assert_true(popover1.matches(':popover-open'), "After one close request, popover1 must be open");
+ assert_false(popover2.matches(':popover-open'), "After one close request, popover2 must be closed");
+
+ await sendCloseRequest();
+ assert_false(popover1.matches(':popover-open'), "After two close requests, popover1 must be closed");
+ assert_false(popover2.matches(':popover-open'), "After two close requests, popover2 must be closed");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-css-properties.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-css-properties.tentative.html
new file mode 100644
index 0000000000..93d388b02b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-css-properties.tentative.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popover API CSS parsing with computed values</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<div id=target></div>
+<div id=scratch></div>
+
+<script>
+function testprop(prop) {
+ // Computed values:
+ test_computed_value(prop, '0s');
+ test_computed_value(prop, '0ms', '0s');
+ test_computed_value(prop, '32s');
+ test_computed_value(prop, '123ms', '0.123s');
+
+ // Valid values:
+ test_valid_value(prop, '0s');
+ test_valid_value(prop, '0ms');
+ test_valid_value(prop, '32s');
+ test_valid_value(prop, '123ms');
+ test_valid_value(prop, 'inherit');
+
+ // Invalid values:
+ test_invalid_value(prop, '0');
+ test_invalid_value(prop, 'foo');
+ test_invalid_value(prop, '-1s');
+ test_invalid_value(prop, 'none');
+ test_invalid_value(prop, 'auto');
+
+ // Animations:
+ test_interpolation({
+ property: prop,
+ from: '1s',
+ to: '2000ms',
+ }, [
+ {at: -1.5, expect: '0s'}, // Clamping at 0
+ {at: -0.3, expect: '0.7s'},
+ {at: 0, expect: '1s'},
+ {at: 0.5, expect: '1.5s'},
+ {at: 1, expect: '2s'},
+ {at: 1.5, expect: '2.5s'},
+ ]);
+}
+
+testprop('popover-show-delay');
+testprop('popover-hide-delay');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance-ref.html
new file mode 100644
index 0000000000..12efbb6b1e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Dialog-Popover appearance</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+<p>Both dialogs should have the same shades of background.</p>
+<p>The popover should have a completely-transparent ::backdrop.</p>
+<dialog popover id=d1>This is a modal dialog</dialog>
+<dialog popover id=d2>This is a dialog popover</dialog>
+
+<style>
+ dialog {
+ left: 50px;
+ right: auto;
+ bottom: auto;
+ }
+ #d1 {top:100px;}
+ #d2 {top:150px;}
+ /* Force backdrop to spec: */
+ #d1::backdrop {
+ /* https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3 */
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+ #d2::backdrop {
+ /* When shown as a popover, backdrop must be transparent */
+ background-color: transparent;
+ }
+</style>
+
+<script>
+ document.getElementById('d1').showModal();
+ document.getElementById('d2').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance.html b/testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance.html
new file mode 100644
index 0000000000..8b4edadee9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-dialog-appearance.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Dialog-Popover appearance</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel="match" href="popover-dialog-appearance-ref.html">
+
+<p>Both dialogs should have the same shades of background.</p>
+<p>The popover should have a completely-transparent ::backdrop.</p>
+<dialog popover id=d1>This is a modal dialog</dialog>
+<dialog popover id=d2>This is a dialog popover</dialog>
+
+<style>
+ dialog {
+ left: 50px;
+ right: auto;
+ bottom: auto;
+ }
+ #d1 {top:100px;}
+ #d2 {top:150px;}
+</style>
+
+<script>
+ document.getElementById('d1').showModal();
+ document.getElementById('d2').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-dialog-crash.html b/testing/web-platform/tests/html/semantics/popovers/popover-dialog-crash.html
new file mode 100644
index 0000000000..e7579d5a38
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-dialog-crash.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8" />
+<title>Dialog-Popover crash</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<p>This test passes if it does not crash.</p>
+<dialog popover>This is a modal dialog</dialog>
+<div popover>This is a popover</div>
+
+<script>
+ const dialog = document.querySelector('dialog[popover]');
+ const popover = document.querySelector('div[popover]');
+ dialog.showModal();
+ popover.showPopover();
+ clickOn(dialog)
+ .then(() => {
+ document.documentElement.classList.remove("reftest-wait");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-document-open.html b/testing/web-platform/tests/html/semantics/popovers/popover-document-open.html
new file mode 100644
index 0000000000..80ac86aced
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-document-open.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div popover id=popover1>Popover</div>
+
+<script>
+ window.onload = () => {
+ test((t) => {
+ const popover1 = document.querySelector('#popover1');
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(!document.querySelector('#popover2'));
+ document.open();
+ document.write('<!DOCTYPE html><div popover id=popover2>Popover</div>');
+ document.close();
+ assert_true(!document.querySelector('#popover1'),'popover1 should be removed from the document');
+ assert_true(!!document.querySelector('#popover2'),'popover2 should be in the document');
+ assert_false(popover1.matches(':popover-open'),'popover1 should have been hidden when it was removed from the document');
+ assert_false(popover1.matches(':popover-open'),'popover2 shouldn\'t be showing yet');
+ popover2.showPopover();
+ assert_true(popover2.matches(':popover-open'),'popover2 should be able to be shown');
+ popover2.hidePopover();
+ },'document.open should not break popovers');
+ };
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-events.html b/testing/web-platform/tests/html/semantics/popovers/popover-events.html
new file mode 100644
index 0000000000..4d58001f7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-events.html
@@ -0,0 +1,216 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover events</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<div popover>Popover</div>
+
+<script>
+function getPopoverAndSignal(t) {
+ const popover = document.querySelector('[popover]');
+ const controller = new AbortController();
+ const signal = controller.signal;
+ t.add_cleanup(() => controller.abort());
+ return {popover, signal};
+}
+window.onload = () => {
+ for(const method of ["listener","attribute"]) {
+ promise_test(async t => {
+ const {popover,signal} = getPopoverAndSignal(t);
+ assert_false(popover.matches(':popover-open'));
+ let showCount = 0;
+ let afterShowCount = 0;
+ let hideCount = 0;
+ let afterHideCount = 0;
+ function listener(e) {
+ assert_false(e.bubbles,'toggle events should not bubble');
+ if (e.type === "beforetoggle") {
+ if (e.newState === "open") {
+ ++showCount;
+ assert_equals(e.oldState,"closed",'The "beforetoggle" event should be fired before the popover is open');
+ assert_false(e.target.matches(':popover-open'),'The popover should *not* be in the :popover-open state when the opening event fires.');
+ assert_true(e.cancelable,'beforetoggle should be cancelable only for the "show" transition');
+ } else {
+ ++hideCount;
+ assert_equals(e.newState,"closed",'Popover toggleevent states should be "open" and "closed"');
+ assert_equals(e.oldState,"open",'The "beforetoggle" event should be fired before the popover is closed')
+ assert_true(e.target.matches(':popover-open'),'The popover should be in the :popover-open state when the hiding event fires.');
+ assert_false(e.cancelable,'beforetoggle should be cancelable only for the "show" transition');
+ e.preventDefault(); // beforetoggle should be cancelable only for the "show" transition
+ }
+ } else {
+ assert_equals(e.type,"toggle",'Popover events should be "beforetoggle" and "toggle"')
+ assert_false(e.cancelable,'toggle should never be cancelable');
+ e.preventDefault(); // toggle should never be cancelable
+ if (e.newState === "open") {
+ ++afterShowCount;
+ if (document.body.contains(e.target)) {
+ assert_true(e.target.matches(':popover-open'),'The popover should be in the :popover-open state when the after opening event fires.');
+ }
+ } else {
+ ++afterHideCount;
+ assert_equals(e.newState,"closed",'Popover toggleevent states should be "open" and "closed"');
+ assert_false(e.target.matches(':popover-open'),'The popover should *not* be in the :popover-open state when the after hiding event fires.');
+ }
+ e.preventDefault(); // "toggle" should not be cancelable.
+ }
+ };
+ switch (method) {
+ case "listener":
+ // These events do *not* bubble.
+ popover.addEventListener('beforetoggle', listener, {signal});
+ popover.addEventListener('toggle', listener, {signal});
+ break;
+ case "attribute":
+ assert_false(popover.hasAttribute('onbeforetoggle'));
+ t.add_cleanup(() => popover.removeAttribute('onbeforetoggle'));
+ popover.onbeforetoggle = listener;
+ assert_false(popover.hasAttribute('ontoggle'));
+ t.add_cleanup(() => popover.removeAttribute('ontoggle'));
+ popover.ontoggle = listener;
+ break;
+ default: assert_unreached();
+ }
+ assert_equals(0,showCount);
+ assert_equals(0,hideCount);
+ assert_equals(0,afterShowCount);
+ assert_equals(0,afterHideCount);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_equals(1,showCount);
+ assert_equals(0,hideCount);
+ assert_equals(0,afterShowCount);
+ assert_equals(0,afterHideCount);
+ await waitForRender();
+ assert_equals(1,afterShowCount,'toggle show is fired asynchronously');
+ assert_equals(0,afterHideCount);
+ assert_true(popover.matches(':popover-open'));
+ popover.hidePopover();
+ assert_false(popover.matches(':popover-open'));
+ assert_equals(1,showCount);
+ assert_equals(1,hideCount);
+ assert_equals(1,afterShowCount);
+ assert_equals(0,afterHideCount);
+ await waitForRender();
+ assert_equals(1,afterShowCount);
+ assert_equals(1,afterHideCount,'toggle hide is fired asynchronously');
+ // No additional events
+ await waitForRender();
+ await waitForRender();
+ assert_false(popover.matches(':popover-open'));
+ assert_equals(1,showCount);
+ assert_equals(1,hideCount);
+ assert_equals(1,afterShowCount);
+ assert_equals(1,afterHideCount);
+ }, `The "beforetoggle" event (${method}) get properly dispatched for popovers`);
+ }
+
+ promise_test(async t => {
+ const {popover,signal} = getPopoverAndSignal(t);
+ let cancel = true;
+ popover.addEventListener('beforetoggle',(e) => {
+ if (e.newState !== "open")
+ return;
+ if (cancel)
+ e.preventDefault();
+ }, {signal});
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_false(popover.matches(':popover-open'),'The "beforetoggle" event should be cancelable for the "opening" transition');
+ cancel = false;
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.hidePopover();
+ assert_false(popover.matches(':popover-open'));
+ }, 'The "beforetoggle" event is cancelable for the "opening" transition');
+
+ promise_test(async t => {
+ const {popover,signal} = getPopoverAndSignal(t);
+ popover.addEventListener('beforetoggle',(e) => {
+ assert_not_equals(e.newState,"closed",'The "beforetoggle" event was fired for the closing transition');
+ }, {signal});
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ t.add_cleanup(() => {document.body.appendChild(popover);});
+ popover.remove();
+ await waitForRender(); // Check for async events also
+ await waitForRender(); // Check for async events also
+ assert_false(popover.matches(':popover-open'));
+ }, 'The "beforetoggle" event is not fired for element removal');
+
+ promise_test(async t => {
+ const {popover,signal} = getPopoverAndSignal(t);
+ let events;
+ function resetEvents() {
+ events = {
+ singleShow: false,
+ singleHide: false,
+ coalescedShow: false,
+ coalescedHide: false,
+ };
+ }
+ function setEvent(type) {
+ assert_equals(events[type],false,'event repeated');
+ events[type] = true;
+ }
+ function assertOnly(type,msg) {
+ Object.keys(events).forEach(val => {
+ assert_equals(events[val],val===type,`${msg} (${val})`);
+ });
+ }
+ popover.addEventListener('toggle',(e) => {
+ switch (e.newState) {
+ case "open":
+ switch (e.oldState) {
+ case "open": setEvent('coalescedShow'); break;
+ case "closed": setEvent('singleShow'); break;
+ default: assert_unreached();
+ }
+ break;
+ case "closed":
+ switch (e.oldState) {
+ case "closed": setEvent('coalescedHide'); break;
+ case "open": setEvent('singleHide'); break;
+ default: assert_unreached();
+ }
+ break;
+ default: assert_unreached();
+ }
+ }, {signal});
+
+ resetEvents();
+ assertOnly('none');
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ await waitForRender();
+ assert_true(popover.matches(':popover-open'));
+ assertOnly('singleShow','Single event should have been fired, which is a "show"');
+
+ resetEvents();
+ popover.hidePopover();
+ popover.showPopover(); // Immediate re-show
+ await waitForRender();
+ assert_true(popover.matches(':popover-open'));
+ assertOnly('coalescedShow','Single coalesced event should have been fired, which is a "show"');
+
+ resetEvents();
+ popover.hidePopover();
+ await waitForRender();
+ assertOnly('singleHide','Single event should have been fired, which is a "hide"');
+ assert_false(popover.matches(':popover-open'));
+
+ resetEvents();
+ popover.showPopover();
+ popover.hidePopover(); // Immediate re-hide
+ await waitForRender();
+ assertOnly('coalescedHide','Single coalesced event should have been fired, which is a "hide"');
+ assert_false(popover.matches(':popover-open'));
+ }, 'The "toggle" event is coalesced');
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-focus-2.html b/testing/web-platform/tests/html/semantics/popovers/popover-focus-2.html
new file mode 100644
index 0000000000..892e5fd68f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-focus-2.html
@@ -0,0 +1,174 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover focus behaviors</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<div id=fixup>
+ <button id=button1 tabindex="0">Button1</button>
+ <div popover id=popover0 tabindex="0" style="top:300px">
+ </div>
+ <div popover id=popover1 style="top:100px">
+ <button id=inside_popover1 tabindex="0">Inside1</button>
+ <button id=invoker2 popovertarget=popover2 tabindex="0">Nested Invoker 2</button>
+ <button id=inside_popover2 tabindex="0">Inside2</button>
+ </div>
+ <button id=button2 tabindex="0">Button2</button>
+ <div popover id=popover_no_invoker tabindex="0" style="top:300px"></div>
+ <button popovertarget=popover0 id=invoker0 tabindex="0">Invoker0</button>
+ <button popovertarget=popover1 id=invoker1 tabindex="0">Invoker1</button>
+ <button id=button3 tabindex="0">Button3</button>
+ <div popover id=popover2 style="top:200px">
+ <button id=inside_popover3 tabindex="0">Inside3</button>
+ <button id=invoker3 popovertarget=popover3 tabindex="0">Nested Invoker 3</button>
+ </div>
+ <div popover id=popover3 style="top:300px">
+ Non-focusable popover
+ </div>
+ <button id=button4 tabindex="0">Button4</button>
+</div>
+<style>
+ #fixup [popover] {
+ bottom:auto;
+ }
+</style>
+<script>
+async function verifyFocusOrder(order,description) {
+ order[0].focus();
+ for(let i=0;i<order.length;++i) {
+ const control = order[i];
+ assert_equals(document.activeElement,control,`${description}: Step ${i+1}`);
+ await sendTab();
+ }
+ for(let i=order.length-1;i>=0;--i) {
+ const control = order[i];
+ await sendShiftTab();
+ assert_equals(document.activeElement,control,`${description}: Step ${i+1} (backwards)`);
+ }
+}
+promise_test(async t => {
+ button1.focus();
+ assert_equals(document.activeElement,button1);
+ await sendTab();
+ assert_equals(document.activeElement,button2,'Hidden popover should be skipped');
+ await sendShiftTab();
+ assert_equals(document.activeElement,button1,'Hidden popover should be skipped backwards');
+ popover_no_invoker.showPopover();
+ await sendTab();
+ await sendTab();
+ assert_equals(document.activeElement,popover_no_invoker,"Focusable popover that is opened without an invoker should get focused");
+ await sendTab();
+ assert_equals(document.activeElement,invoker0);
+ await sendEnter(); // Activate the invoker0
+ assert_true(popover0.matches(':popover-open'), 'popover0 should be invoked by invoker0');
+ assert_equals(document.activeElement,invoker0,'Focus should not move when popover is shown');
+ await sendTab();
+ await sendEnter(); // Activate the invoker
+ assert_true(popover1.matches(':popover-open'), 'popover1 should be invoked by invoker1');
+ assert_equals(document.activeElement,invoker1,'Focus should not move when popover is shown');
+ await sendTab();
+ // Make invoker1 non-focusable.
+ invoker1.disabled = true;
+ assert_equals(document.activeElement,inside_popover1,'Focus should move from invoker into the open popover');
+ await sendTab();
+ assert_equals(document.activeElement,invoker2,'Focus should move within popover');
+ await sendShiftTab();
+ await sendShiftTab();
+ assert_equals(document.activeElement, button1 ,'Focus should not move back to invoker as it is non-focusable');
+ // Reset invoker1 to focusable.
+ invoker1.disabled = false;
+ await verifyFocusOrder([button1, button2, invoker0, invoker1, inside_popover1, invoker2, inside_popover2, button3, button4],'set 1');
+ invoker2.focus();
+ await sendEnter(); // Activate the nested invoker
+ assert_true(popover2.matches(':popover-open'), 'popover2 should be invoked by nested invoker');
+ assert_equals(document.activeElement,invoker2,'Focus should stay on the invoker');
+ await sendTab();
+ assert_equals(document.activeElement,inside_popover3,'Focus should move into nested popover');
+ await sendTab();
+ assert_equals(document.activeElement,invoker3);
+ await sendEnter(); // Activate the (empty) nested invoker
+ assert_true(popover3.matches(':popover-open'), 'popover3 should be invoked by nested invoker');
+ assert_equals(document.activeElement,invoker3,'Focus should stay on the invoker');
+ await sendTab();
+ assert_equals(document.activeElement,inside_popover2,'Focus should skip popover without focusable content, going back to higher scope');
+ await sendShiftTab();
+ assert_equals(document.activeElement,invoker3,'Shift-tab from the higher scope should return to the lower scope');
+ await sendTab();
+ assert_equals(document.activeElement,inside_popover2);
+ await sendTab();
+ assert_equals(document.activeElement,button3,'Focus should exit popovers');
+ await sendTab();
+ assert_equals(document.activeElement,button4,'Focus should skip popovers');
+ button1.focus();
+ await verifyFocusOrder([button1, button2, invoker0, invoker1, inside_popover1, invoker2, inside_popover3, invoker3, inside_popover2, button3, button4],'set 2');
+}, "Popover focus navigation");
+</script>
+
+<button id=circular0 popovertarget=popover4 tabindex="0">Invoker</button>
+<div id=popover4 popover>
+ <button id=circular1 autofocus popovertarget=popover4 popovertargetaction=hide tabindex="0"></button>
+ <button id=circular2 popovertarget=popover4 popovertargetaction=show tabindex="0"></button>
+ <button id=circular3 popovertarget=popover4 tabindex="0"></button>
+</div>
+<button id=circular4 tabindex="0">after</button>
+<script>
+promise_test(async t => {
+ circular0.focus();
+ await sendEnter(); // Activate the invoker
+ await verifyFocusOrder([circular0, circular1, circular2, circular3, circular4],'circular reference');
+ popover4.hidePopover();
+}, "Circular reference tab navigation");
+</script>
+
+<div id=focus-return1>
+ <button popovertarget=focus-return1-p popovertargetaction=show tabindex="0">Show popover</button>
+ <div popover id=focus-return1-p>
+ <button popovertarget=focus-return1-p popovertargetaction=hide autofocus tabindex="0">Hide popover</button>
+ </div>
+</div>
+<script>
+promise_test(async t => {
+ const invoker = document.querySelector('#focus-return1>button');
+ const popover = document.querySelector('#focus-return1>[popover]');
+ const hideButton = popover.querySelector('[popovertargetaction=hide]');
+ invoker.focus(); // Make sure button is focused.
+ assert_equals(document.activeElement,invoker);
+ await sendEnter(); // Activate the invoker
+ assert_true(popover.matches(':popover-open'), 'popover should be invoked by invoker');
+ assert_equals(document.activeElement,hideButton,'Hide button should be focused due to autofocus attribute');
+ await sendEnter(); // Activate the hide invoker
+ assert_false(popover.matches(':popover-open'), 'popover should be hidden by invoker');
+ assert_equals(document.activeElement,invoker,'Focus should be returned to the invoker');
+}, "Popover focus returns when popover is hidden by invoker");
+</script>
+
+<div id=focus-return2>
+ <button popovertarget=focus-return2-p tabindex="0">Toggle popover</button>
+ <div popover id=focus-return2-p>Popover with <button tabindex="0">focusable element</button></div>
+ <span tabindex=0>Other focusable element</span>
+</div>
+<script>
+promise_test(async t => {
+ const invoker = document.querySelector('#focus-return2>button');
+ const popover = document.querySelector('#focus-return2>[popover]');
+ const otherElement = document.querySelector('#focus-return2>span');
+ invoker.focus(); // Make sure button is focused.
+ assert_equals(document.activeElement,invoker);
+ invoker.click(); // Activate the invoker
+ assert_true(popover.matches(':popover-open'), 'popover should be invoked by invoker');
+ assert_equals(document.activeElement,invoker,'invoker should still be focused');
+ await sendTab();
+ assert_equals(document.activeElement,popover.querySelector('button'),'next up is the popover');
+ await sendTab();
+ assert_equals(document.activeElement,otherElement,'next focus stop is outside the popover');
+ await sendEscape(); // Close the popover via ESC
+ assert_false(popover.matches(':popover-open'), 'popover should be hidden');
+ assert_equals(document.activeElement,otherElement,'focus does not move because it was not inside the popover');
+}, "Popover focus only returns to invoker when focus is within the popover");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-focus-harness.html b/testing/web-platform/tests/html/semantics/popovers/popover-focus-harness.html
new file mode 100644
index 0000000000..a22a68ea2b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-focus-harness.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover utils - harness test</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<button id=button1 tabindex="0">Button1</button>
+<button id=button2 tabindex="0">Button2</button>
+<button id=button3 tabindex="0">Button3</button>
+
+<script>
+promise_test(async t => {
+ button1.focus();
+ assert_equals(document.activeElement,button1);
+ await sendTab();
+ assert_equals(document.activeElement,button2,'Tab should move to button 2');
+ await sendShiftTab();
+ assert_equals(document.activeElement,button1,'Shift-Tab should move back to button 1');
+}, "Test sendShiftTab");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-focus.html b/testing/web-platform/tests/html/semantics/popovers/popover-focus.html
new file mode 100644
index 0000000000..230492022c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-focus.html
@@ -0,0 +1,292 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover focus behaviors</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<div popover data-test='default behavior - popover is not focused' data-no-focus>
+ <p>This is a popover</p>
+ <button tabindex="0">first button</button>
+</div>
+
+<div popover data-test='autofocus popover' autofocus tabindex=-1 class=should-be-focused>
+ <p>This is a popover</p>
+</div>
+
+<div popover data-test='autofocus empty popover' autofocus tabindex=-1 class=should-be-focused></div>
+
+<div popover data-test='autofocus popover with button' autofocus tabindex=-1 class=should-be-focused>
+ <p>This is a popover</p>
+ <button tabindex="0">button</button>
+</div>
+
+<div popover data-test='autofocus child'>
+ <p>This is a popover</p>
+ <button autofocus class=should-be-focused tabindex="0">autofocus button</button>
+</div>
+
+<div popover data-test='autofocus on tabindex=0 element'>
+ <p autofocus tabindex=0 class=should-be-focused>This is a popover with autofocus on a tabindex=0 element</p>
+ <button tabindex="0">button</button>
+</div>
+
+<div popover data-test='autofocus multiple children'>
+ <p>This is a popover</p>
+ <button autofocus class=should-be-focused tabindex="0">autofocus button</button>
+ <button autofocus tabindex="0">second autofocus button</button>
+</div>
+
+<div popover autofocus tabindex=-1 data-test='autofocus popover and multiple autofocus children' class=should-be-focused>
+ <p>This is a popover</p>
+ <button autofocus tabindex="0">autofocus button</button>
+ <button autofocus tabindex="0">second autofocus button</button>
+</div>
+
+<dialog popover=auto data-test='Opening dialogs as popovers should use dialog initial focus algorithm.'>
+ <button class=should-be-focused>button</button>
+</dialog>
+
+<dialog popover=auto autofocus class=should-be-focused data-test='Opening dialogs as popovers which have autofocus should focus the dialog.'>
+ <button>button</button>
+</dialog>
+
+<style>
+ [popover] {
+ border: 2px solid black;
+ top:150px;
+ left:150px;
+ }
+ :focus-within { border: 5px dashed red; }
+ :focus { border: 5px solid lime; }
+</style>
+
+<script>
+ function addInvoker(t, popover) {
+ const button = document.createElement('button');
+ button.innerText = 'Click me';
+ const popoverId = 'popover-id';
+ assert_equals(document.querySelectorAll('#' + popoverId).length, 0);
+ document.body.appendChild(button);
+ t.add_cleanup(function() {
+ popover.removeAttribute('id');
+ button.remove();
+ });
+ popover.id = popoverId;
+ button.setAttribute('tabindex', '0');
+ button.setAttribute('popovertarget', popoverId);
+ return button;
+ }
+ function addPriorFocus(t) {
+ const priorFocus = document.createElement('button');
+ priorFocus.setAttribute("tabindex", "0");
+ priorFocus.id = 'priorFocus';
+ document.body.appendChild(priorFocus);
+ t.add_cleanup(() => priorFocus.remove());
+ return priorFocus;
+ }
+ function activateAndVerify(popover) {
+ const testName = popover.getAttribute('data-test');
+ promise_test(async t => {
+ const priorFocus = addPriorFocus(t);
+ let expectedFocusedElement = popover.matches('.should-be-focused') ? popover : popover.querySelector('.should-be-focused');
+ const changesFocus = !popover.hasAttribute('data-no-focus');
+ if (!changesFocus) {
+ expectedFocusedElement = priorFocus;
+ }
+ assert_true(!!expectedFocusedElement);
+ assert_false(popover.matches(':popover-open'));
+
+ // Directly show and hide the popover:
+ priorFocus.focus();
+ assert_equals(document.activeElement, priorFocus);
+ popover.showPopover();
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by popover.showPopover()`);
+ popover.hidePopover();
+ assert_equals(document.activeElement, priorFocus, 'prior element should get focus on hide, or if focus didn\'t shift on show, focus should stay where it was');
+ assert_false(isElementVisible(popover));
+
+ // Manual popover does not restore focus
+ popover.popover = 'manual';
+ priorFocus.focus();
+ assert_equals(document.activeElement, priorFocus);
+ popover.showPopover();
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by popover.showPopover()`);
+ popover.hidePopover();
+ if (!popover.hasAttribute('data-no-focus')) {
+ assert_not_equals(document.activeElement, priorFocus, 'prior element should *not* get focus when the popover is manual');
+ }
+ assert_false(isElementVisible(popover));
+ popover.popover = 'auto';
+
+ // Hit Escape:
+ priorFocus.focus();
+ assert_equals(document.activeElement, priorFocus);
+ popover.showPopover();
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by popover.showPopover()`);
+ await sendEscape();
+ assert_equals(document.activeElement, priorFocus, 'prior element should get focus after Escape');
+ assert_false(isElementVisible(popover));
+
+ // Move focus into the popover, then hit Escape:
+ let containedButton = popover.querySelector('button');
+ if (containedButton) {
+ priorFocus.focus();
+ assert_equals(document.activeElement, priorFocus);
+ popover.showPopover();
+ containedButton.focus();
+ assert_equals(document.activeElement, containedButton);
+ await sendEscape();
+ assert_equals(document.activeElement, priorFocus, 'prior element should get focus after Escape');
+ assert_false(isElementVisible(popover));
+ }
+
+ // Change the popover type:
+ priorFocus.focus();
+ popover.showPopover();
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by popover.showPopover()`);
+ assert_equals(popover.popover, 'auto', 'All popovers in this test should start as popover=auto');
+ popover.popover = 'manual';
+ assert_false(popover.matches(':popover-open'), 'Changing the popover type should hide the popover');
+ assert_equals(document.activeElement, priorFocus, 'prior element should get focus when the type is changed');
+ assert_false(isElementVisible(popover));
+ popover.popover = 'auto';
+
+ // Remove from the document:
+ priorFocus.focus();
+ popover.showPopover();
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by popover.showPopover()`);
+ popover.remove();
+ assert_false(isElementVisible(popover), 'Removing the popover should hide it immediately');
+ if (!popover.hasAttribute('data-no-focus')) {
+ assert_not_equals(document.activeElement, priorFocus, 'prior element should *not* get focus when the popover is removed from the document');
+ }
+ document.body.appendChild(popover);
+
+ // Show a modal dialog:
+ priorFocus.focus();
+ popover.showPopover();
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by popover.showPopover()`);
+ const dialog = document.body.appendChild(document.createElement('dialog'));
+ dialog.showModal();
+ assert_false(popover.matches(':popover-open'), 'Opening a modal dialog should hide the popover');
+ assert_not_equals(document.activeElement, priorFocus, 'prior element should *not* get focus when a modal dialog is shown');
+ assert_false(isElementVisible(popover));
+ dialog.close();
+ dialog.remove();
+
+ // Use an activating element:
+ const button = addInvoker(t, popover);
+ priorFocus.focus();
+ button.click();
+ assert_true(popover.matches(':popover-open'));
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by button.click()`);
+
+ // Make sure Escape works in the invoker case:
+ await sendEscape();
+ assert_equals(document.activeElement, priorFocus, 'prior element should get focus after Escape (via invoker)');
+ assert_false(isElementVisible(popover));
+
+ // Make sure we can directly focus the (already open) popover:
+ priorFocus.focus();
+ button.click();
+ assert_true(popover.matches(':popover-open'));
+ assert_equals(document.activeElement, expectedFocusedElement, `${testName} activated by button.click()`);
+ popover.focus();
+ assert_equals(document.activeElement, popover.hasAttribute('tabindex') || popover.tagName === 'DIALOG' ? popover : expectedFocusedElement, `${testName} directly focus with popover.focus()`);
+ button.click(); // Button is set to toggle the popover
+ assert_false(popover.matches(':popover-open'));
+ assert_equals(document.activeElement, priorFocus, 'prior element should get focus on button-toggled hide');
+ assert_false(isElementVisible(popover));
+ }, "Popover focus test: " + testName);
+
+ promise_test(async t => {
+ const priorFocus = addPriorFocus(t);
+ assert_false(popover.matches(':popover-open'), 'popover should start out hidden');
+ let button = addInvoker(t, popover);
+ assert_equals(button.getAttribute('popovertarget'), popover.id, 'This test assumes the button uses `popovertarget`.');
+ assert_not_equals(button, priorFocus, 'Stranger things have happened');
+ assert_false(popover.contains(button), 'Start with a non-contained button');
+ priorFocus.focus();
+ assert_equals(document.activeElement, priorFocus);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ await clickOn(button); // This will *not* light dismiss, but will "toggle" the popover.
+ assert_false(popover.matches(':popover-open'));
+ assert_equals(document.activeElement, button, 'focus should move to the button when clicked, and should stay there when the popover closes');
+ assert_false(isElementVisible(popover));
+
+ // Same thing, but the button is contained within the popover
+ button.setAttribute('popovertarget', popover.id);
+ button.setAttribute('popovertargetaction', 'hide');
+ popover.appendChild(button);
+ t.add_cleanup(() => button.remove());
+ priorFocus.focus();
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ const changesFocus = !popover.hasAttribute('data-no-focus');
+ if (changesFocus) {
+ assert_not_equals(document.activeElement, priorFocus, 'focus should shift for this element');
+ }
+ await clickOn(button);
+ assert_false(popover.matches(':popover-open'), 'clicking button should hide the popover');
+ assert_equals(document.activeElement, priorFocus, 'Contained button should return focus to the previously focused element');
+ assert_false(isElementVisible(popover));
+
+ // Same thing, but the button is unrelated (no popovertarget)
+ button = document.createElement('button');
+ button.setAttribute("tabindex", "0");
+ document.body.appendChild(button);
+ priorFocus.focus();
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ await clickOn(button); // This will light dismiss the popover, focus the prior focus, then focus this button.
+ assert_false(popover.matches(':popover-open'), 'clicking button should hide the popover (via light dismiss)');
+ assert_equals(document.activeElement, button, 'Focus should go to unrelated button on light dismiss');
+ assert_false(isElementVisible(popover));
+ }, "Popover button click focus test: " + testName);
+
+ promise_test(async t => {
+ if (popover.hasAttribute('data-no-focus')) {
+ // This test only applies if the popover changes focus
+ return;
+ }
+ const priorFocus = addPriorFocus(t);
+ assert_false(popover.matches(':popover-open'), 'popover should start out hidden');
+
+ // Move the prior focus out of the document
+ priorFocus.focus();
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ const newFocus = document.activeElement;
+ assert_not_equals(newFocus, priorFocus, 'focus should shift for this element');
+ priorFocus.remove();
+ assert_equals(document.activeElement, newFocus, 'focus should not change when prior focus is removed');
+ popover.hidePopover();
+ assert_not_equals(document.activeElement, priorFocus, 'focused element has been removed');
+ assert_false(isElementVisible(popover));
+ document.body.appendChild(priorFocus); // Put it back
+
+ // Move the prior focus inside the (already open) popover
+ priorFocus.focus();
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_false(popover.contains(priorFocus), 'Start with a non-contained prior focus');
+ popover.appendChild(priorFocus); // Move inside the popover
+ assert_true(popover.contains(priorFocus));
+ assert_true(popover.matches(':popover-open'), 'popover should stay open');
+ popover.hidePopover();
+ assert_false(isElementVisible(popover));
+ assert_not_equals(document.activeElement, priorFocus, 'focused element is display:none inside the popover');
+ document.body.appendChild(priorFocus); // Put it back
+ }, "Popover corner cases test: " + testName);
+ }
+
+ document.querySelectorAll('body > [popover]').forEach(popover => activateAndVerify(popover));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-hidden-display-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-hidden-display-ref.html
new file mode 100644
index 0000000000..2dc0d558b6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-hidden-display-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel="stylesheet" href="resources/popover-styles.css">
+
+
+<div class=fake-popover>This content should be visible and green</div>
+<div class=fake-popover style="top:100px;">This content should be visible and green</div>
+<div class=fake-popover style="top:200px;">This content should be visible and green</div>
+
+<style>
+ .fake-popover {
+ top: 0;
+ margin:10px;
+ width: 300px;
+ height: 50px;
+ background: green;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-hidden-display.html b/testing/web-platform/tests/html/semantics/popovers/popover-hidden-display.html
new file mode 100644
index 0000000000..db61802db6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-hidden-display.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-hidden-display-ref.html">
+<meta name=fuzzy content="0-1;0-15">
+
+<div class=nottoplayer popover >This content should be visible and green</div>
+<div class=nottoplayer popover=invalid style="top:100px;">This content should be visible and green</div>
+<div class=toplayer popover style="top:200px;">This content should be visible and green</div>
+
+<style>
+ [popover] {
+ display: block; /* This should make the popover visible */
+ top: 0;
+ margin:10px;
+ width: 300px;
+ height: 50px;
+ }
+ [popover].nottoplayer {
+ background: green;
+ }
+ [popover].toplayer {
+ background: red;
+ }
+ [popover].toplayer:popover-open {
+ background: green;
+ }
+ [popover].nottoplayer:popover-open {
+ background: red;
+ }
+</style>
+<script>
+ const toplayer = document.querySelectorAll('[popover].toplayer');
+ if (toplayer.length !== 1)
+ document.write('FAIL');
+ toplayer[0].showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-hint-crash.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-hint-crash.tentative.html
new file mode 100644
index 0000000000..82f83538e9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-hint-crash.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8" />
+<title>Popover=hint crash test</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<p>This test passes if it does not crash.</p>
+
+<div popover id=popover1>Popover 1
+ <div popover id=popover2 style="top:100px">Popover 2</div>
+</div>
+<div popover=manual id=popover3 style="top:200px">Popover 3</div>
+<div popover=hint id=popover4 anchor=popover3 style="inset:0;top:300px">Popover 4 - Click me</div>
+<script>
+popover1.showPopover();
+popover2.showPopover();
+popover3.showPopover();
+popover4.showPopover();
+clickOn(popover4)
+ .then(() => {
+ document.documentElement.classList.remove("reftest-wait");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hide.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hide.tentative.html
new file mode 100644
index 0000000000..57ca5723de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hide.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>The popover-hide-delay CSS property</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<body>
+<script src="resources/popover-hover-hide-common.js"></script>
+<script>
+
+// See popover-hover-hide-common.js for documentation.
+runHoverHideTestsForInvokerAction('hide');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hover.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hover.tentative.html
new file mode 100644
index 0000000000..d0036c0fe7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-hover.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>The popover-hide-delay CSS property</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<body>
+<script src="resources/popover-hover-hide-common.js"></script>
+<script>
+
+// See popover-hover-hide-common.js for documentation.
+runHoverHideTestsForInvokerAction('hover');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-show.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-show.tentative.html
new file mode 100644
index 0000000000..7b3fa2b302
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-show.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>The popover-hide-delay CSS property</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<body>
+<script src="resources/popover-hover-hide-common.js"></script>
+<script>
+
+// See popover-hover-hide-common.js for documentation.
+runHoverHideTestsForInvokerAction('show');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-toggle.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-toggle.tentative.html
new file mode 100644
index 0000000000..d6d4079e7e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-hover-hide-toggle.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>The popover-hide-delay CSS property</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<body>
+<script src="resources/popover-hover-hide-common.js"></script>
+<script>
+
+// See popover-hover-hide-common.js for documentation.
+runHoverHideTestsForInvokerAction('toggle');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none-ref.html
new file mode 100644
index 0000000000..3d58e4ca09
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+
+No popover should be displayed here.<p>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none.html b/testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none.html
new file mode 100644
index 0000000000..24ce7c6fc6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-inside-display-none.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-inside-display-none-ref.html">
+
+No popover should be displayed here.<p>
+
+<div style="display:none">
+ <div popover>This content should be hidden</div>
+</div>
+
+<script>
+ const popover = document.querySelector('[popover]');
+ popover.showPopover();
+ if (!popover.matches(':popover-open'))
+ document.body.appendChild(document.createTextNode('FAIL'));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-invoker-reset.html b/testing/web-platform/tests/html/semantics/popovers/popover-invoker-reset.html
new file mode 100644
index 0000000000..bfc79fd629
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-invoker-reset.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://github.com/whatwg/html/issues/9152">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<div id=p1 popover>Popover 1
+ <button popovertarget=p2>Button</button>
+</div>
+<div id=p2 popover>Popover 2</div>
+
+<script>
+ test((t) => {
+ p1.showPopover();
+ assert_true(p1.matches(':popover-open'));
+ const invoker = p1.querySelector('button');
+ p2.addEventListener('beforetoggle',(e) => {
+ assert_equals(e.newState,'open');
+ e.preventDefault();
+ },{once:true});
+ invoker.click(); // Will be cancelled
+ assert_false(p2.matches(':popover-open'));
+ assert_true(p1.matches(':popover-open'));
+ p2.showPopover();
+ assert_true(p2.matches(':popover-open'));
+ assert_false(p1.matches(':popover-open'),'invoker was not used to show p2, so p1 should close');
+ },'Invoker gets reset appropriately');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute-hint.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute-hint.tentative.html
new file mode 100644
index 0000000000..b531ddc460
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute-hint.tentative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover invoking attribute</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+<script src="resources/popover-invoking-attribute.js"></script>
+
+<body>
+<script>
+runPopoverInvokerTests(["hint"]);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute.html b/testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute.html
new file mode 100644
index 0000000000..8e312e90d7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-invoking-attribute.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover invoking attribute</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+<script src="resources/popover-invoking-attribute.js"></script>
+
+<body>
+<script>
+runPopoverInvokerTests(["auto","manual"]);
+</script>
+
+<button popovertarget=p1>Toggle Popover 1</button>
+<div popover id=p1 style="border: 5px solid red;top: 100px;left: 100px;">This is popover #1</div>
+
+<script>
+function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+}
+
+const popover = document.querySelector('[popover]');
+const button = document.querySelector('button');
+let showCount = 0;
+let hideCount = 0;
+popover.addEventListener('beforetoggle',(e) => {
+ if (e.newState === "open")
+ ++showCount;
+ else
+ ++hideCount;
+ });
+
+async function assertState(expectOpen,expectShow,expectHide) {
+ assert_equals(popover.matches(':popover-open'),expectOpen,'Popover open state is incorrect');
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ assert_equals(showCount,expectShow,'Show count is incorrect');
+ assert_equals(hideCount,expectHide,'Hide count is incorrect');
+}
+
+window.addEventListener('load', () => {
+ promise_test(async () => {
+ showCount = hideCount = 0;
+ await assertState(false,0,0);
+ await clickOn(button);
+ await assertState(true,1,0);
+ popover.hidePopover();
+ await assertState(false,1,1);
+ button.click();
+ await assertState(true,2,1);
+ popover.hidePopover();
+ await assertState(false,2,2);
+ }, "Clicking a popovertarget button opens a closed popover (also check event counts)");
+
+ promise_test(async () => {
+ showCount = hideCount = 0;
+ await assertState(false,0,0);
+ await clickOn(button);
+ await assertState(true,1,0);
+ await clickOn(button);
+ await assertState(false,1,1);
+ }, "Clicking a popovertarget button closes an open popover (also check event counts)");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree-nested.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree-nested.html
new file mode 100644
index 0000000000..ef3b35aea4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree-nested.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test that popover light dismiss uses the flat tree when nested shadow roots.</title>
+ <link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev">
+</head>
+<body>
+ <p>Test passes if the inner popover opens after clicking the inner toggle.</p>
+ <button popovertarget="outerPopover" popovertargetaction="toggle" id="outerPopoverToggle">Toggle</button>
+ <div id="outerPopover" popover>
+ <template shadowrootmode="open">
+ Outer
+ <button id="innerPopoverToggle">Toggle</button>
+ <div id="innerContainer">
+ <template shadowrootmode="open">
+ <div id="innerPopover" popover>
+ Inner
+ </div>
+ </template>
+ </div>
+ </template>
+ </div>
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/popover-utils.js"></script>
+ <script>
+ promise_test(async () => {
+ const innerPopoverToggle = outerPopover.shadowRoot.querySelector("#innerPopoverToggle");
+ const innerContainer = outerPopover.shadowRoot.querySelector('#innerContainer');
+ const innerPopover = innerContainer.shadowRoot.querySelector("#innerPopover");
+ innerPopoverToggle.onclick = () => {
+ innerPopover.togglePopover();
+ }
+
+ assert_false(outerPopover.matches(":popover-open"), "outer popover is initially hidden");
+ assert_false(innerPopover.matches(":popover-open"), "inner popover is initially hidden");
+
+ await clickOn(outerPopoverToggle);
+
+ assert_true(outerPopover.matches(":popover-open"), "outer popover is open after clicking the toggle");
+ assert_false(innerPopover.matches(":popover-open"), "inner popover is initially hidden");
+
+ await clickOn(innerPopoverToggle);
+
+ assert_true(outerPopover.matches(":popover-open"), "outer popover is not dismissed after clicking the second toggle");
+ assert_true(innerPopover.matches(":popover-open"), "inner popover is open after clicking the second toggle");
+ }, "Popover light dismiss uses the flat tree when nested shadow root");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree.html
new file mode 100644
index 0000000000..65008e28ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-flat-tree.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test that popover light dismiss uses the flat tree.</title>
+ <link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+</head>
+<body>
+ <p>Test passes if the inner popover opens after clicking the inner toggle.</p>
+ <button popovertarget="outerPopover" popovertargetaction="toggle" id="outerPopoverToggle">Toggle</button>
+ <div id="outerPopover" popover>
+ <template shadowrootmode="open">
+ Outer
+ <button popovertarget="innerPopover" popovertargetaction="toggle" id="innerPopoverToggle">Toggle</button>
+ <div id="innerPopover" popover>
+ Inner
+ </div>
+ </template>
+ </div>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/popover-utils.js"></script>
+ <script>
+ promise_test(async () => {
+ const innerPopoverToggle = outerPopover.shadowRoot.querySelector("#innerPopoverToggle");
+ const innerPopover = outerPopover.shadowRoot.querySelector("#innerPopover");
+
+ assert_false(outerPopover.matches(":popover-open"), "outer popover is initially hidden");
+ assert_false(innerPopover.matches(":popover-open"), "inner popover is initially hidden");
+
+ await clickOn(outerPopoverToggle);
+
+ assert_true(outerPopover.matches(":popover-open"), "outer popover is open after clicking the toggle");
+ assert_false(innerPopover.matches(":popover-open"), "inner popover is initially hidden");
+
+ await clickOn(innerPopoverToggle);
+
+ assert_true(outerPopover.matches(":popover-open"), "outer popover is not dismissed after clicking the second toggle");
+ assert_true(innerPopover.matches(":popover-open"), "inner popover is open after clicking the second toggle");
+ }, "Popover light dismiss uses the flat tree");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-hint.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-hint.tentative.html
new file mode 100644
index 0000000000..f07363115f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-hint.tentative.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover light dismiss behavior for hints</title>
+<meta name="timeout" content="long">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover-hint.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<div id=outside></div>
+<div popover id=auto1>auto 1
+ <div popover id=auto2>auto 2
+ <div popover=hint id=innerhint1>inner hint 1
+ <div popover=hint id=innerhint2>inner hint 2
+ <div popover id=invalidauto1>Improperly nested auto 1</div>
+ </div>
+ </div>
+ </div>
+</div>
+<div popover=hint id=hint1>hint 1
+ <div popover=hint id=hint2>hint 2
+ <div popover id=invalidauto2>Improperly nested auto 2</div>
+ </div>
+</div>
+<div popover=manual id=manual1>Manual</div>
+
+<style>
+ [popover] {right:auto;bottom:auto;}
+ #auto1 {left:100px; top:100px;}
+ #auto2 {left:100px; top:200px;}
+ #innerhint1 {left:100px; top:300px;}
+ #innerhint2 {left:100px; top:400px;}
+ #invalidauto1 {left:100px; top:500px;}
+ #hint1 {left:200px; top:100px;}
+ #hint2 {left:200px; top:200px;}
+ #invalidauto1 {left:200px; top:400px;}
+ #manual1 {left:300px; top:100px;}
+ #outside {width:25px;height:25px}
+</style>
+
+<script>
+ const popovers = [
+ document.querySelector('#auto1'),
+ document.querySelector('#auto2'),
+ document.querySelector('#innerhint1'),
+ document.querySelector('#innerhint2'),
+ document.querySelector('#hint1'),
+ document.querySelector('#hint2'),
+ document.querySelector('#manual1'),
+ ];
+ function assertState(expectedState,description) {
+ description = description || 'Error';
+ const n = popovers.length;
+ assert_equals(expectedState.length,n,'Invalid');
+ for(let i=0;i<n;++i) {
+ assert_equals(popovers[i].matches(':popover-open'),expectedState[i],`${description}, index ${i} (${popovers[i].id})`);
+ }
+ }
+ function openall(t) {
+ // All popovers can be open at once, if shown in order:
+ popovers.forEach((p) => p.hidePopover());
+ popovers.forEach((p) => p.showPopover());
+ assertState(Array(popovers.length).fill(true),'All popovers should be able to be open at once');
+ t.add_cleanup(() => popovers.forEach((p) => p.hidePopover()));
+ }
+ function nvals(n,val) {
+ return new Array(n).fill(val);
+ }
+ for(let i=0;i<(popovers.length-1);++i) {
+ promise_test(async (t) => {
+ openall(t);
+ await clickOn(popovers[i]);
+ let expectedState = [...nvals(i+1,true),...nvals(popovers.length-i-2,false),true];
+ assertState(expectedState);
+ },`Mixed auto/hint light dismiss behavior, click on ${popovers[i].id}`);
+ }
+
+ promise_test(async (t) => {
+ openall(t);
+ await clickOn(outside);
+ assertState([false,false,false,false,false,false,true]);
+ },'Clicking outside closes all');
+
+ promise_test(async (t) => {
+ openall(t);
+ invalidauto1.showPopover();
+ assertState([true,true,false,false,false,false,true],'auto inside hint ignores the hints and gets nested within auto2');
+ assert_true(invalidauto1.matches(':popover-open'),'the inner nested auto should be open');
+ invalidauto1.hidePopover();
+ assertState([true,true,false,false,false,false,true]);
+ assert_false(invalidauto1.matches(':popover-open'));
+ },'Auto cannot be nested inside hint (invalidauto1)');
+
+ promise_test(async (t) => {
+ openall(t);
+ invalidauto2.showPopover();
+ assertState([false,false,false,false,false,false,true],'auto inside hint works as an independent (non-nested) auto');
+ assert_true(invalidauto2.matches(':popover-open'),'the inner nested auto should be open');
+ invalidauto2.hidePopover();
+ assertState([false,false,false,false,false,false,true]);
+ assert_false(invalidauto2.matches(':popover-open'));
+ },'Auto cannot be nested inside hint (invalidauto2)');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-on-scroll.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-on-scroll.html
new file mode 100644
index 0000000000..382addadef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-on-scroll.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8" />
+<title>Popover should *not* light dismiss on scroll</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=help href="https://github.com/openui/open-ui/issues/240">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=scroller>
+ Scroll me<br><br>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
+ ut labore et dolore magna aliqua. Enim ut sem viverra aliquet eget sit amet tellus. Massa
+ sed elementum tempus egestas sed sed risus pretium. Felis bibendum ut tristique et egestas
+ quis. Tortor dignissim convallis aenean et. Eu mi bibendum neque egestas congue quisque
+</div>
+
+<div popover id=popover1>
+ This is popover 1
+ <div popover id=popover2 anchor=anchor>
+ This is popover 2
+ </div>
+</div>
+<button onclick='popover1.showPopover();popover2.showPopover();'>Open popovers</button>
+
+<style>
+ #popover1 { top:50px; left: 50px; }
+ #popover2 { top:150px; left: 50px; }
+ #scroller {
+ height: 150px;
+ width: 150px;
+ overflow-y: scroll;
+ border: 1px solid black;
+ }
+</style>
+
+<script>
+ const popovers = document.querySelectorAll('[popover]');
+ function assertAll(showing) {
+ for(let popover of popovers) {
+ assert_equals(popover.matches(':popover-open'),showing);
+ }
+ }
+ async_test(t => {
+ for(let popover of popovers) {
+ popover.addEventListener('beforetoggle',e => {
+ if (e.newState !== "closed")
+ return;
+ assert_unreached('Scrolling should not light-dismiss a popover');
+ });
+ }
+ assertAll(/*showing*/false);
+ popovers[0].showPopover();
+ popovers[1].showPopover();
+ assertAll(/*showing*/true);
+ scroller.scrollTo(0, 100);
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ assertAll(/*showing*/true);
+ t.done();
+ });
+ });
+ },'Scrolling should not light-dismiss popovers');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-with-anchor.tentative.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-with-anchor.tentative.tentative.html
new file mode 100644
index 0000000000..c4e545c4fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss-with-anchor.tentative.tentative.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover light dismiss with anchor behavior</title>
+<meta name="timeout" content="long">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<button id=p1anchor tabindex="0">Popover1 anchor (no action)</button>
+<div popover id=p1 anchor=p1anchor>
+ <span id=inside1>Inside popover 1</span>
+ <button id=b2 popovertarget='p2' popovertargetaction=show>Popover 2</button>
+</div>
+<div popover id=p2 anchor=b2>
+ <span id=inside2>Inside popover 2</span>
+</div>
+<style>
+ #p1 {top: 50px;}
+ #p2 {top: 120px;}
+ [popover] {bottom:auto;}
+ [popover]::backdrop {
+ /* This should *not* affect anything: */
+ pointer-events: auto;
+ }
+</style>
+<script>
+ const popover1 = document.querySelector('#p1');
+ const popover1anchor = document.querySelector('#p1anchor');
+ const popover2 = document.querySelector('#p2');
+ const inside1 = document.querySelector('#inside1');
+ const inside2 = document.querySelector('#inside2');
+
+ let popover1HideCount = 0;
+ popover1.addEventListener('beforetoggle',(e) => {
+ if (e.newState !== "closed")
+ return;
+ ++popover1HideCount;
+ e.preventDefault(); // 'beforetoggle' should not be cancellable.
+ });
+ let popover2HideCount = 0;
+ popover2.addEventListener('beforetoggle',(e) => {
+ if (e.newState !== "closed")
+ return;
+ ++popover2HideCount;
+ e.preventDefault(); // 'beforetoggle' should not be cancellable.
+ });
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ let p2HideCount = popover2HideCount;
+ await clickOn(inside2);
+ assert_true(popover1.matches(':popover-open'),'popover1 should be open');
+ assert_true(popover2.matches(':popover-open'),'popover2 should be open');
+ assert_equals(popover1HideCount,p1HideCount,'popover1');
+ assert_equals(popover2HideCount,p2HideCount,'popover2');
+ popover1.hidePopover();
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ },'Clicking inside a child popover shouldn\'t close either popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ p2HideCount = popover2HideCount;
+ await clickOn(inside1);
+ assert_true(popover1.matches(':popover-open'));
+ assert_equals(popover1HideCount,p1HideCount);
+ assert_false(popover2.matches(':popover-open'));
+ assert_equals(popover2HideCount,p2HideCount+1);
+ popover1.hidePopover();
+ },'Clicking inside a parent popover should close child popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ await waitForRender();
+ await clickOn(popover1anchor);
+ assert_false(popover1.matches(':popover-open'),'popover1 should close');
+ },'Clicking on anchor element (that isn\'t an invoking element) shouldn\'t prevent its popover from being closed');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html
new file mode 100644
index 0000000000..916d52ef5e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html
@@ -0,0 +1,607 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover light dismiss behavior</title>
+<meta name="timeout" content="long">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<button id=b1t popovertarget='p1'>Popover 1</button>
+<button id=b1s popovertarget='p1' popovertargetaction=show>Popover 1</button>
+<span id=outside>Outside all popovers</span>
+<div popover id=p1>
+ <span id=inside1>Inside popover 1</span>
+ <button id=b2 popovertarget='p2' popovertargetaction=show>Popover 2</button>
+ <span id=inside1after>Inside popover 1 after button</span>
+ <div popover id=p2>
+ <span id=inside2>Inside popover 2</span>
+ </div>
+</div>
+<button id=after_p1 tabindex="0">Next control after popover1</button>
+<style>
+ #p1 {top: 50px;}
+ #p2 {top: 120px;}
+ [popover] {bottom:auto;}
+ [popover]::backdrop {
+ /* This should *not* affect anything: */
+ pointer-events: auto;
+ }
+</style>
+<script>
+ const popover1 = document.querySelector('#p1');
+ const button1toggle = document.querySelector('#b1t');
+ const button1show = document.querySelector('#b1s');
+ const inside1After = document.querySelector('#inside1after');
+ const button2 = document.querySelector('#b2');
+ const popover2 = document.querySelector('#p2');
+ const outside = document.querySelector('#outside');
+ const inside1 = document.querySelector('#inside1');
+ const inside2 = document.querySelector('#inside2');
+ const afterp1 = document.querySelector('#after_p1');
+
+ let popover1HideCount = 0;
+ popover1.addEventListener('beforetoggle',(e) => {
+ if (e.newState !== "closed")
+ return;
+ ++popover1HideCount;
+ e.preventDefault(); // 'beforetoggle' should not be cancellable.
+ });
+ let popover2HideCount = 0;
+ popover2.addEventListener('beforetoggle',(e) => {
+ if (e.newState !== "closed")
+ return;
+ ++popover2HideCount;
+ e.preventDefault(); // 'beforetoggle' should not be cancellable.
+ });
+ promise_test(async () => {
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ let p1HideCount = popover1HideCount;
+ await clickOn(outside);
+ assert_false(popover1.matches(':popover-open'));
+ assert_equals(popover1HideCount,p1HideCount+1);
+ },'Clicking outside a popover will dismiss the popover');
+
+ promise_test(async (t) => {
+ const controller = new AbortController();
+ t.add_cleanup(() => controller.abort());
+ function addListener(eventName) {
+ document.addEventListener(eventName,(e) => e.preventDefault(),{signal:controller.signal,capture: true});
+ }
+ addListener('pointerdown');
+ addListener('pointerup');
+ addListener('mousedown');
+ addListener('mouseup');
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ let p1HideCount = popover1HideCount;
+ await clickOn(outside);
+ assert_false(popover1.matches(':popover-open'),'preventDefault should not prevent light dismiss');
+ assert_equals(popover1HideCount,p1HideCount+1);
+ },'Canceling pointer events should not keep clicks from light dismissing popovers');
+
+ promise_test(async () => {
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(inside1);
+ assert_true(popover1.matches(':popover-open'));
+ assert_equals(popover1HideCount,p1HideCount);
+ popover1.hidePopover();
+ },'Clicking inside a popover does not close that popover');
+
+ promise_test(async () => {
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ await waitForRender();
+ assert_true(popover1.matches(':popover-open'));
+ await new test_driver.Actions()
+ .pointerMove(0, 0, {origin: outside})
+ .pointerDown()
+ .send();
+ await waitForRender();
+ assert_true(popover1.matches(':popover-open'),'pointerdown (outside the popover) should not hide the popover');
+ await new test_driver.Actions()
+ .pointerUp()
+ .send();
+ await waitForRender();
+ assert_false(popover1.matches(':popover-open'),'pointerup (outside the popover) should trigger light dismiss');
+ },'Popovers close on pointerup, not pointerdown');
+
+ promise_test(async (t) => {
+ t.add_cleanup(() => popover1.hidePopover());
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ async function testOne(eventName) {
+ document.body.dispatchEvent(new PointerEvent(eventName));
+ document.body.dispatchEvent(new MouseEvent(eventName));
+ document.body.dispatchEvent(new ProgressEvent(eventName));
+ await waitForRender();
+ assert_true(popover1.matches(':popover-open'),`A synthetic "${eventName}" event should not hide the popover`);
+ }
+ await testOne('pointerup');
+ await testOne('pointerdown');
+ await testOne('mouseup');
+ await testOne('mousedown');
+ },'Synthetic events can\'t close popovers');
+
+ promise_test(async (t) => {
+ t.add_cleanup(() => popover1.hidePopover());
+ popover1.showPopover();
+ await clickOn(inside1After);
+ assert_true(popover1.matches(':popover-open'));
+ await sendTab();
+ assert_equals(document.activeElement,afterp1,'Focus should move to a button outside the popover');
+ assert_true(popover1.matches(':popover-open'));
+ },'Moving focus outside the popover should not dismiss the popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ let p2HideCount = popover2HideCount;
+ await clickOn(inside2);
+ assert_true(popover1.matches(':popover-open'),'popover1 should be open');
+ assert_true(popover2.matches(':popover-open'),'popover2 should be open');
+ assert_equals(popover1HideCount,p1HideCount,'popover1');
+ assert_equals(popover2HideCount,p2HideCount,'popover2');
+ popover1.hidePopover();
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ },'Clicking inside a child popover shouldn\'t close either popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ p2HideCount = popover2HideCount;
+ await clickOn(inside1);
+ assert_true(popover1.matches(':popover-open'));
+ assert_equals(popover1HideCount,p1HideCount);
+ assert_false(popover2.matches(':popover-open'));
+ assert_equals(popover2HideCount,p2HideCount+1);
+ popover1.hidePopover();
+ },'Clicking inside a parent popover should close child popover');
+
+ promise_test(async () => {
+ await clickOn(button1show);
+ assert_true(popover1.matches(':popover-open'));
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(button1show);
+ assert_true(popover1.matches(':popover-open'),'popover1 should stay open');
+ assert_equals(popover1HideCount,p1HideCount,'popover1 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ await clickOn(button2);
+ assert_true(popover2.matches(':popover-open'),'button2 should activate popover2');
+ p2HideCount = popover2HideCount;
+ await clickOn(button2);
+ assert_true(popover2.matches(':popover-open'),'popover2 should stay open');
+ assert_equals(popover2HideCount,p2HideCount,'popover2 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover (nested case)');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(popover2.matches(':popover-open'));
+ p2HideCount = popover2HideCount;
+ await clickOn(button2);
+ assert_true(popover2.matches(':popover-open'),'popover2 should stay open');
+ assert_equals(popover2HideCount,p2HideCount,'popover2 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover (nested case, not used for invocation)');
+
+ promise_test(async () => {
+ popover1.showPopover(); // Directly show the popover
+ assert_true(popover1.matches(':popover-open'));
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(button1show);
+ assert_true(popover1.matches(':popover-open'),'popover1 should stay open');
+ assert_equals(popover1HideCount,p1HideCount,'popover1 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ },'Clicking on invoking element, even if it wasn\'t used for activation, shouldn\'t close its popover');
+
+ promise_test(async () => {
+ popover1.showPopover(); // Directly show the popover
+ assert_true(popover1.matches(':popover-open'));
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(button1toggle);
+ assert_false(popover1.matches(':popover-open'),'popover1 should be hidden by popovertarget');
+ assert_equals(popover1HideCount,p1HideCount+1,'popover1 should get hidden only once by popovertarget');
+ },'Clicking on popovertarget element, even if it wasn\'t used for activation, should hide it exactly once');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover(); // Popover1 is an ancestral element for popover2.
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(popover2.matches(':popover-open'));
+ const drag_actions = new test_driver.Actions();
+ // Drag *from* popover2 *to* popover1 (its ancestor).
+ await drag_actions.pointerMove(0,0,{origin: popover2})
+ .pointerDown({button: drag_actions.ButtonType.LEFT})
+ .pointerMove(0,0,{origin: popover1})
+ .pointerUp({button: drag_actions.ButtonType.LEFT})
+ .send();
+ assert_true(popover1.matches(':popover-open'),'popover1 should be open');
+ assert_true(popover2.matches(':popover-open'),'popover1 should be open');
+ popover1.hidePopover();
+ assert_false(popover2.matches(':popover-open'));
+ },'Dragging from an open popover outside an open popover should leave the popover open');
+</script>
+
+<button id=b3 popovertarget=p3>Popover 3 - button 3
+ <div popover id=p4>Inside popover 4</div>
+</button>
+<div popover id=p3>Inside popover 3</div>
+<div popover id=p5>Inside popover 5
+ <button popovertarget=p3>Popover 3 - button 4 - unused</button>
+</div>
+<style>
+ #p3 {top:100px;}
+ #p4 {top:200px;}
+ #p5 {top:200px;}
+</style>
+<script>
+ const popover3 = document.querySelector('#p3');
+ const popover4 = document.querySelector('#p4');
+ const popover5 = document.querySelector('#p5');
+ const button3 = document.querySelector('#b3');
+ promise_test(async () => {
+ await clickOn(button3);
+ assert_true(popover3.matches(':popover-open'),'invoking element should open popover');
+ popover4.showPopover();
+ assert_true(popover4.matches(':popover-open'));
+ assert_false(popover3.matches(':popover-open'),'popover3 is unrelated to popover4');
+ popover4.hidePopover(); // Cleanup
+ assert_false(popover4.matches(':popover-open'));
+ },'A popover inside an invoking element doesn\'t participate in that invoker\'s ancestor chain');
+
+ promise_test(async () => {
+ popover5.showPopover();
+ assert_true(popover5.matches(':popover-open'));
+ assert_false(popover3.matches(':popover-open'));
+ popover3.showPopover();
+ assert_true(popover3.matches(':popover-open'));
+ assert_false(popover5.matches(':popover-open'),'Popover 5 was not invoked from popover3\'s invoker');
+ popover3.hidePopover();
+ assert_false(popover3.matches(':popover-open'));
+ },'An invoking element that was not used to invoke the popover is not part of the ancestor chain');
+</script>
+
+<div popover id=p6>Inside popover 6
+ <div style="height:2000px;background:lightgreen"></div>
+ Bottom of popover6
+</div>
+<button popovertarget=p6>Popover 6</button>
+<style>
+ #p6 {
+ width: 300px;
+ height: 300px;
+ overflow-y: scroll;
+ }
+</style>
+<script>
+ const popover6 = document.querySelector('#p6');
+ promise_test(async () => {
+ popover6.showPopover();
+ assert_equals(popover6.scrollTop,0,'popover6 should start non-scrolled');
+ await new test_driver.Actions()
+ .scroll(0, 0, 0, 50, {origin: popover6})
+ .send();
+ await waitForRender();
+ assert_true(popover6.matches(':popover-open'),'popover6 should stay open');
+ assert_equals(popover6.scrollTop,50,'popover6 should be scrolled');
+ popover6.hidePopover();
+ },'Scrolling within a popover should not close the popover');
+</script>
+
+<my-element id="myElement">
+ <template shadowrootmode="open">
+ <button id=b7 popovertarget=p7 popovertargetaction=show tabindex="0">Popover7</button>
+ <div popover id=p7 style="top: 100px;">
+ <p>Popover content.</p>
+ <input id="inside7" type="text" placeholder="some text">
+ </div>
+ </template>
+</my-element>
+<script>
+ const button7 = document.querySelector('#myElement').shadowRoot.querySelector('#b7');
+ const popover7 = document.querySelector('#myElement').shadowRoot.querySelector('#p7');
+ const inside7 = document.querySelector('#myElement').shadowRoot.querySelector('#inside7');
+ promise_test(async () => {
+ button7.click();
+ assert_true(popover7.matches(':popover-open'),'invoking element should open popover');
+ inside7.click();
+ assert_true(popover7.matches(':popover-open'));
+ popover7.hidePopover();
+ },'Clicking inside a shadow DOM popover does not close that popover');
+
+ promise_test(async () => {
+ button7.click();
+ inside7.click();
+ assert_true(popover7.matches(':popover-open'));
+ await clickOn(outside);
+ assert_false(popover7.matches(':popover-open'));
+ },'Clicking outside a shadow DOM popover should close that popover');
+</script>
+
+<div popover id=p8>
+ <button tabindex="0">Button</button>
+ <span id=inside8after>Inside popover 8 after button</span>
+</div>
+<button id=p8invoker popovertarget=p8 tabindex="0">Popover8 invoker (no action)</button>
+<script>
+ promise_test(async () => {
+ const popover8 = document.querySelector('#p8');
+ const inside8After = document.querySelector('#inside8after');
+ const popover8Invoker = document.querySelector('#p8invoker');
+ assert_false(popover8.matches(':popover-open'));
+ popover8.showPopover();
+ await clickOn(inside8After);
+ assert_true(popover8.matches(':popover-open'));
+ await sendTab();
+ assert_equals(document.activeElement,popover8Invoker,'Focus should move to the invoker element');
+ assert_true(popover8.matches(':popover-open'),'popover should stay open');
+ popover8.hidePopover(); // Cleanup
+ },'Moving focus back to the invoker element should not dismiss the popover');
+</script>
+
+<!-- Convoluted ancestor relationship -->
+<div popover id=convoluted_p1>Popover 1
+ <button popovertarget=convoluted_p2>Open Popover 2</button>
+<div popover id=convoluted_p2>Popover 2
+ <button popovertarget=convoluted_p3>Open Popover 3</button>
+ <button popovertarget=convoluted_p2 popovertargetaction=show>Self-linked invoker</button>
+ </div>
+ <div popover id=convoluted_p3>Popover 3
+ <button popovertarget=convoluted_p4>Open Popover 4</button>
+ </div>
+ <div popover id=convoluted_p4><p>Popover 4</p></div>
+</div>
+<button onclick="convoluted_p1.showPopover()" tabindex="0">Open convoluted popover</button>
+<style>
+ #convoluted_p1 {top:50px;}
+ #convoluted_p2 {top:150px;}
+ #convoluted_p3 {top:250px;}
+ #convoluted_p4 {top:350px;}
+</style>
+<script>
+const convPopover1 = document.querySelector('#convoluted_p1');
+const convPopover2 = document.querySelector('#convoluted_p2');
+const convPopover3 = document.querySelector('#convoluted_p3');
+const convPopover4 = document.querySelector('#convoluted_p4');
+promise_test(async () => {
+ convPopover1.showPopover(); // Programmatically open p1
+ assert_true(convPopover1.matches(':popover-open'));
+ convPopover1.querySelector('button').click(); // Click to invoke p2
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ convPopover2.querySelector('button').click(); // Click to invoke p3
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_true(convPopover3.matches(':popover-open'));
+ convPopover3.querySelector('button').click(); // Click to invoke p4
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_true(convPopover3.matches(':popover-open'));
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover4.firstElementChild.click(); // Click within p4
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_true(convPopover3.matches(':popover-open'));
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover1.hidePopover();
+ assert_false(convPopover1.matches(':popover-open'));
+ assert_false(convPopover2.matches(':popover-open'));
+ assert_false(convPopover3.matches(':popover-open'));
+ assert_false(convPopover4.matches(':popover-open'));
+},'Ensure circular/convoluted ancestral relationships are functional');
+
+promise_test(async () => {
+ convPopover1.showPopover(); // Programmatically open p1
+ convPopover1.querySelector('button').click(); // Click to invoke p2
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_false(convPopover3.matches(':popover-open'));
+ assert_false(convPopover4.matches(':popover-open'));
+ convPopover4.showPopover(); // Programmatically open p4
+ assert_true(convPopover1.matches(':popover-open'),'popover1 stays open because it is a DOM ancestor of popover4');
+ assert_false(convPopover2.matches(':popover-open'),'popover2 closes because it isn\'t connected to popover4 via active invokers');
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover4.firstElementChild.click(); // Click within p4
+ assert_true(convPopover1.matches(':popover-open'),'nothing changes');
+ assert_false(convPopover2.matches(':popover-open'));
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover1.hidePopover();
+ assert_false(convPopover1.matches(':popover-open'));
+ assert_false(convPopover2.matches(':popover-open'));
+ assert_false(convPopover3.matches(':popover-open'));
+ assert_false(convPopover4.matches(':popover-open'));
+},'Ensure circular/convoluted ancestral relationships are functional, with a direct showPopover()');
+</script>
+
+<div popover id=p13>Popover 1
+ <div popover id=p14>Popover 2
+ <div popover id=p15>Popover 3</div>
+ </div>
+</div>
+<style>
+ #p13 {top: 100px;}
+ #p14 {top: 200px;}
+ #p15 {top: 300px;}
+</style>
+<script>
+promise_test(async () => {
+ const p13 = document.querySelector('#p13');
+ const p14 = document.querySelector('#p14');
+ const p15 = document.querySelector('#p15');
+ p13.showPopover();
+ p14.showPopover();
+ p15.showPopover();
+ p15.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ p14.hidePopover();
+ },{once:true});
+ assert_true(p13.matches(':popover-open') && p14.matches(':popover-open') && p15.matches(':popover-open'),'all three should be open');
+ p14.hidePopover();
+ assert_true(p13.matches(':popover-open'),'p13 should still be open');
+ assert_false(p14.matches(':popover-open'));
+ assert_false(p15.matches(':popover-open'));
+ p13.hidePopover(); // Cleanup
+},'Hide the target popover during "hide all popovers until"');
+</script>
+
+<div id=p16 popover>Popover 16
+ <div id=p17 popover>Popover 17</div>
+ <div id=p18 popover>Popover 18</div>
+</div>
+
+<script>
+promise_test(async () => {
+ p16.showPopover();
+ p18.showPopover();
+ let events = [];
+ const logEvents = (e) => {events.push(`${e.newState==='open' ? 'show' : 'hide'} ${e.target.id}`)};
+ p16.addEventListener('beforetoggle', logEvents);
+ p17.addEventListener('beforetoggle', logEvents);
+ p18.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p17.showPopover();
+ });
+ p16.hidePopover();
+ assert_array_equals(events,['hide p18','show p17','hide p16'],'There should not be a hide event for p17');
+ assert_false(p16.matches(':popover-open'));
+ assert_false(p17.matches(':popover-open'));
+ assert_false(p18.matches(':popover-open'));
+},'Show a sibling popover during "hide all popovers until"');
+</script>
+
+<div id=p19 popover>Popover 19</div>
+<div id=p20 popover>Popover 20</div>
+<button id=example2 tabindex="0">Example 2</button>
+
+<script>
+promise_test(async () => {
+ p19.showPopover();
+ let events = [];
+ const logEvents = (e) => {events.push(`${e.newState==='open' ? 'show' : 'hide'} ${e.target.id}`)};
+ p19.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p20.showPopover();
+ });
+ p20.addEventListener('beforetoggle', logEvents);
+ p19.hidePopover();
+ assert_array_equals(events,['hide p19','show p20'],'There should not be a second hide event for 19');
+ assert_false(p19.matches(':popover-open'));
+ assert_true(p20.matches(':popover-open'));
+ p20.hidePopover(); // Cleanup
+},'Show an unrelated popover during "hide popover"');
+</script>
+
+<div id=p21 popover>21
+ <div id=p22 popover>22</div>
+ <div id=p23 popover>23</div>
+ <div id=p24 popover>24</div>
+</div>
+
+<script>
+promise_test(async () => {
+ p21.showPopover();
+ p22.showPopover();
+ let events = [];
+ const logEvents = (e) => { events.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
+ p22.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p24.showPopover()
+ });
+ p23.addEventListener('beforetoggle', logEvents);
+ p24.addEventListener('beforetoggle', logEvents);
+ p23.showPopover();
+ assert_array_equals(events, ['show p23', 'hide p22', 'show p24'], 'hiding p24 does not fire event');
+ assert_false(p22.matches(':popover-open'));
+ assert_true(p23.matches(':popover-open'));
+ assert_false(p24.matches(':popover-open'));
+ p21.hidePopover(); // Cleanup
+},'Show other auto popover during "hide all popover until"');
+</script>
+
+<div id=p25 popover>
+ <div id=p26 popover>26</div>
+ <div id=p27 popover>27</div>
+ <div id=p28 popover>28</div>
+</div>
+<script>
+promise_test(async () => {
+ p25.showPopover();
+ p26.showPopover();
+ let events = [];
+ const logEvents = (e) => { events.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
+ p26.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p28.showPopover();
+ });
+ p27.addEventListener('beforetoggle', logEvents);
+ p28.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p27.showPopover();
+ });
+ p27.showPopover();
+ assert_array_equals(events, ['show p27', 'hide p26', 'show p28', 'show p27'], 'Nested showPopover should not fire event for its HideAllPopoversUntil');
+ assert_false(p26.matches(':popover-open'));
+ assert_true(p27.matches(':popover-open'));
+ assert_false(p28.matches(':popover-open'));
+ p25.hidePopover(); // Cleanup
+}, 'Nested showPopover');
+</script>
+
+<div id=p29 popover>Popover 29</div>
+<button id=b29 popovertarget=p29>Open popover 29</button>
+<iframe id=iframe29 width=100 height=100></iframe>
+<script>
+promise_test(async () => {
+ let iframe_url = (new URL("/common/blank.html", location.href)).href;
+ iframe29.src = iframe_url;
+ iframe29.contentDocument.body.style.height = '100%';
+ assert_false(p29.matches(':popover-open'));
+ p29.showPopover();
+ assert_true(p29.matches(':popover-open'));
+ let actions = new test_driver.Actions();
+ await actions.pointerMove(0,0,{origin: b29})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .send();
+
+ actions = new test_driver.Actions();
+ await actions.pointerMove(0,0,{origin: iframe29.contentDocument.body})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ assert_true(p29.matches(':popover-open'));
+},`Pointer down in one document and pointer up in another document shouldn't dismiss popover`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-manual-crash.html b/testing/web-platform/tests/html/semantics/popovers/popover-manual-crash.html
new file mode 100644
index 0000000000..535eb4c7d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-manual-crash.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8" />
+<title>Popover=manual crash test</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<style>
+[popover] {top: 100px; bottom: auto;}
+[popover=""] {left: -200px}
+[popover=auto] {left: 0; }
+[popover=manual] {left: 200px; }
+</style>
+
+<p>This test passes if it does not crash.</p>
+<div popover>Auto1
+ <div popover=auto>Auto2</div>
+</div>
+<div popover=manual>Manual</div>
+<script>
+ document.querySelectorAll('[popover]').forEach(p => p.showPopover());
+ const manual = document.querySelector('[popover=manual]');
+ clickOn(manual)
+ .then(() => {
+ document.documentElement.classList.remove("reftest-wait");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-move-documents.html b/testing/web-platform/tests/html/semantics/popovers/popover-move-documents.html
new file mode 100644
index 0000000000..11f52c2f2f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-move-documents.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9177">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async function iframeLoaded(iframe) {
+ return new Promise(resolve => {
+ if (iframe.contentWindow.document.readyState == 'complete') {
+ resolve();
+ } else {
+ iframe.onload = resolve;
+ }
+ });
+}
+</script>
+
+<iframe id=myframe srcdoc="<p>iframe</p>"></iframe>
+<div id=p1 popover=auto>p1</div>
+<script>
+promise_test(async () => {
+ await iframeLoaded(myframe);
+ await new Promise(resolve => {
+ if (myframe.contentWindow.document.readyState == 'complete') {
+ resolve();
+ } else {
+
+ }
+ });
+ p1.addEventListener('beforetoggle', () => {
+ myframe.contentWindow.document.body.appendChild(p1);
+ });
+ assert_throws_dom('InvalidStateError', () => p1.showPopover());
+}, 'Moving popovers between documents while showing should throw an exception.');
+</script>
+
+<iframe id=myframe2 srcdoc="<p>iframe</p>"></iframe>
+<div id=p2 popover=auto>p2</div>
+<script>
+promise_test(async () => {
+ await iframeLoaded(myframe2);
+ const p2 = document.getElementById('p2');
+ p2.showPopover();
+ p2.addEventListener('beforetoggle', () => {
+ myframe2.contentWindow.document.body.appendChild(p2);
+ });
+ assert_true(p2.matches(':popover-open'),
+ 'The popover should be open after calling showPopover()');
+
+ p2.hidePopover();
+ assert_false(p2.matches(':popover-open'),
+ 'The popover should be closed after moving it between documents.');
+}, 'Moving popovers between documents while hiding should not throw an exception.');
+</script>
+
+<iframe id=myframe3 srcdoc="<p>iframe</p>"></iframe>
+<div id=p3 popover=auto>
+ p3
+ <div id=p4 popover=auto>p4</div>
+ <div id=p5 popover=auto>p5</div>
+</div>
+<script>
+promise_test(async () => {
+ await iframeLoaded(myframe3);
+ p3.showPopover();
+ p4.showPopover();
+ p4.addEventListener('beforetoggle', event => {
+ if (event.newState === 'closed') {
+ assert_true(p3.matches(':popover-open'),
+ 'p3 should be showing in the event handler.');
+ assert_true(p4.matches(':popover-open'),
+ 'p4 should be showing in the event handler.');
+ assert_equals(event.target, p4,
+ 'The events target should be p4.');
+ myframe3.contentWindow.document.body.appendChild(p5);
+ }
+ });
+ assert_true(p3.matches(':popover-open'),
+ 'p3 should be open after calling showPopover on it.');
+ assert_true(p4.matches(':popover-open'),
+ 'p4 should be open after calling showPopover on it.');
+
+ const p5 = document.getElementById('p5');
+ assert_throws_dom('InvalidStateError', () => p5.showPopover());
+ assert_false(p5.matches(':popover-open'),
+ 'p5 should be closed after moving it between documents.');
+}, 'Moving popovers between documents during light dismiss should throw an exception.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-not-keyboard-focusable.html b/testing/web-platform/tests/html/semantics/popovers/popover-not-keyboard-focusable.html
new file mode 100644
index 0000000000..55c70aa643
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-not-keyboard-focusable.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover keyboard focus behaviors</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<button id=firstfocus tabindex="0">Button 1</button>
+<div popover>
+ <p>This is a popover without a focusable element</p>
+</div>
+<button id=secondfocus tabindex="0">Button 2</button>
+
+<script>
+promise_test(async () => {
+ const b1 = document.getElementById('firstfocus');
+ const b2 = document.getElementById('secondfocus');
+ const popover = document.querySelector('[popover]');
+ b1.focus();
+ assert_equals(document.activeElement,b1);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_equals(document.activeElement,b1);
+ // Tab once
+ await new test_driver.send_keys(document.body,'\uE004'); // Tab
+ assert_equals(document.activeElement, b2, 'Keyboard focus should skip the open popover');
+ assert_true(popover.matches(':popover-open'),'changing focus should not close the popover');
+ popover.hidePopover();
+
+ // Add a focusable button to the popover and make sure we can focus that
+ const button = document.createElement('button');
+ button.setAttribute("tabindex", "0");
+ popover.appendChild(button);
+ b1.focus();
+ popover.showPopover();
+ assert_equals(document.activeElement,b1);
+ // Tab once
+ await new test_driver.send_keys(document.body,'\uE004'); // Tab
+ assert_equals(document.activeElement, button, 'Keyboard focus should go to the contained button');
+ assert_true(popover.matches(':popover-open'),'changing focus to the popover should leave it showing');
+ popover.hidePopover();
+ assert_false(popover.matches(':popover-open'));
+}, "Popover should not be keyboard focusable");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-open-display-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-open-display-ref.html
new file mode 100644
index 0000000000..144b81e645
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-open-display-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel="stylesheet" href="resources/popover-styles.css">
+
+<div class=topmost></div>
+<div class=fake-popover>This is a popover</div>
+
+<style>
+ .topmost {
+ position:fixed;
+ top:0;
+ left:0;
+ width:1000px;
+ height:1000px;
+ background:green;
+ margin:0;
+ padding:0;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-open-display.html b/testing/web-platform/tests/html/semantics/popovers/popover-open-display.html
new file mode 100644
index 0000000000..bc4d16fe80
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-open-display.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-open-display-ref.html">
+
+<div popover>This is a popover</div>
+<div class=topmost></div>
+
+<style>
+ .topmost {
+ position:fixed;
+ z-index: 999999;
+ top:0;
+ left:0;
+ width:1000px;
+ height:1000px;
+ background:green;
+ margin:0;
+ padding:0;
+ }
+</style>
+
+<script>
+ document.querySelector('[popover]').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-open-in-beforetoggle.html b/testing/web-platform/tests/html/semantics/popovers/popover-open-in-beforetoggle.html
new file mode 100644
index 0000000000..1e22b73c68
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-open-in-beforetoggle.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover beforetoggle event opening new popovers</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/indices.html#event-beforetoggle">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<div popover id=p1>Popover 1
+ <div popover id=p2>Popover 2
+ <div popover id=p3>Popover 3</div>
+ </div>
+</div>
+<div id=outside>Outside</div>
+<dialog id=dialog>Dialog</dialog>
+
+<script>
+ function getSignal(t) {
+ const controller = new AbortController();
+ t.add_cleanup(() => controller.abort());
+ return controller.signal;
+ }
+
+ test((t) => {
+ p1.showPopover();
+ p1.addEventListener('beforetoggle',() => p2.showPopover(),{signal: getSignal(t)});
+ p1.hidePopover(); // Potential crash here
+ assert_false(p1.matches(':popover-open'));
+ assert_false(p2.matches(':popover-open'));
+ },'Open popover from closing beforetoggle event');
+
+ test((t) => {
+ p1.showPopover();
+ p1.addEventListener('beforetoggle',() => p2.showPopover(),{signal: getSignal(t)});
+ p2.addEventListener('beforetoggle',() => p3.showPopover(),{signal: getSignal(t)});
+ p1.hidePopover(); // Potential crash here
+ assert_false(p1.matches(':popover-open'));
+ assert_false(p2.matches(':popover-open'));
+ assert_false(p3.matches(':popover-open'));
+ },'Open double-nested popovers from closing beforetoggle event');
+
+ test(t => {
+ p1.showPopover();
+ p1.addEventListener('beforetoggle',() => p2.showPopover(),{signal: getSignal(t)});
+ p2.addEventListener('beforetoggle',() => p3.showPopover(),{signal: getSignal(t)});
+ dialog.showModal(); // Potential crash here
+ assert_false(p1.matches(':popover-open'));
+ assert_false(p2.matches(':popover-open'));
+ assert_false(p3.matches(':popover-open'));
+ dialog.close();
+ },'Open double-nested popovers from closing beforetoggle event, dialog open');
+
+ promise_test(async t => {
+ p1.showPopover();
+ p1.addEventListener('beforetoggle',() => p2.showPopover(),{signal: getSignal(t)});
+ p2.addEventListener('beforetoggle',() => p3.showPopover(),{signal: getSignal(t)});
+ await clickOn(outside); // Potential crash here
+ assert_false(p1.matches(':popover-open'));
+ assert_false(p2.matches(':popover-open'));
+ assert_false(p3.matches(':popover-open'));
+ },'Open double-nested popovers from closing beforetoggle event, light dismiss');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-2.html b/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-2.html
new file mode 100644
index 0000000000..f2388b7642
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-2.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/popover-utils.js"></script>
+<script>
+async function checkStatus(p) {
+ p.showPopover();
+ await waitForRender();
+ assert_true(p.matches(":popover-open"));
+ p.hidePopover();
+ await waitForRender();
+}
+</script>
+
+<div id=container style="overflow: hidden; position: absolute;">
+ <div popover="auto" id=p1 style="position: absolute; top: 100px;">Absolute popover inside absolute element</div>
+</div>
+<script>
+promise_test(async () => {
+ await checkStatus(document.querySelector("#p1"));
+}, "Absolute popover inside absolute element");
+</script>
+
+<div id=p2 popover="auto" style="overflow: hidden; position: absolute;">
+ <div style="position: absolute; top: 100px;">Absolute element inside absolute popover</div>
+</div>
+<script>
+promise_test(async () => {
+ await checkStatus(document.querySelector("#p2"));
+}, "Absolute element inside absolute popover");
+</script>
+
+<div id=container style="overflow: hidden; position: fixed;">
+ <div popover="auto" id=p3 style="position: fixed; top: 100px;">Fixed popover inside fixed element</div>
+</div>
+<script>
+promise_test(async () => {
+ await checkStatus(document.querySelector("#p3"));
+}, "Fixed popover inside fixed element");
+</script>
+
+<div id=p4 popover="auto" style="overflow: hidden; position: fixed;">
+ <div style="position: fixed; top: 100px;">Fixed element inside fixed popover</div>
+</div>
+<script>
+promise_test(async () => {
+ await checkStatus(document.querySelector("#p4"));
+}, "Fixed element inside fixed popover");
+</script>
+
+<div id=container style="overflow: hidden; position: fixed;">
+ <div popover="auto" id=p5 style="position: absolute; top: 100px;">Absolute popover inside fixed element</div>
+</div>
+<script>
+promise_test(async () => {
+ await checkStatus(document.querySelector("#p5"));
+}, "Absolute popover inside fixed element");
+</script>
+
+<div id=p6 popover="auto" style="overflow: hidden; position: absolute;">
+ <div style="position: fixed; top: 100px;">Fixed element inside absolute popover</div>
+</div>
+<script>
+promise_test(async () => {
+ await checkStatus(document.querySelector("#p6"));
+}, "Fixed element inside absolute popover");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-ref.html
new file mode 100644
index 0000000000..0d14050e85
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+
+<div popover id=p1>This is popover 1<div id=anchor2></div></div>
+<div popover id=p2 anchor=anchor2>This is popover 2<div id=anchor3></div></div>
+<div popover id=p3 anchor=anchor3>This is popover 3</div>
+
+<style>
+ #p2 {
+ top: 100px;
+ }
+ #p3 {
+ top:200px;
+ }
+</style>
+
+<script>
+ document.querySelector('#p1').showPopover();
+ document.querySelector('#p2').showPopover();
+ document.querySelector('#p3').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display.tentative.html
new file mode 100644
index 0000000000..3d4d833063
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-open-overflow-display.tentative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-open-overflow-display-ref.html">
+
+<div id=container>
+ <div popover id=p1>This is popover 1<div id=anchor2></div></div>
+ <div popover id=p2 anchor=anchor2>This is popover 2<div id=anchor3></div></div>
+ <div popover id=p3 anchor=anchor3>This is popover 3</div>
+</div>
+
+<style>
+ #container {
+ overflow:hidden;
+ position: absolute;
+ top: 100px;
+ left: 50px;
+ width: 30px;
+ height: 30px;
+ }
+ #p2 {
+ position: absolute;
+ top: 100px;
+ }
+ #p3 {
+ position: relative;
+ top:200px;
+ }
+</style>
+
+<script>
+ document.querySelector('#p1').showPopover();
+ document.querySelector('#p2').showPopover();
+ document.querySelector('#p3').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-overlay.html b/testing/web-platform/tests/html/semantics/popovers/popover-overlay.html
new file mode 100644
index 0000000000..a607844aee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-overlay.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<title>popover: overlay</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/popover.html#the-popover-attribute">
+<link rel="help" href="https://drafts.csswg.org/css-position-4/#overlay">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<dialog popover id="popover-show-dialog"></dialog>
+<dialog popover id="popover-show-modal-dialog"></dialog>
+<dialog popover id="popover-dialog"></dialog>
+<div popover id="popover-div"></div>
+<script>
+ test(() => {
+ const popover_show_dialog = document.getElementById("popover-show-dialog");
+ assert_equals(getComputedStyle(popover_show_dialog).overlay, "none",
+ "Computed overlay");
+ popover_show_dialog.show();
+ assert_equals(getComputedStyle(popover_show_dialog).overlay, "none",
+ "Computed overlay after show()");
+ popover_show_dialog.close();
+ }, "dialog.show() should not put popover dialog in top layer");
+
+ test(() => {
+ const popover_show_modal_dialog = document.getElementById("popover-show-modal-dialog");
+ assert_equals(getComputedStyle(popover_show_modal_dialog).overlay, "none",
+ "Computed overlay");
+ popover_show_modal_dialog.showModal();
+ assert_equals(getComputedStyle(popover_show_modal_dialog).overlay, "auto",
+ "Computed overlay after showModal()");
+ popover_show_modal_dialog.close();
+ }, "dialog.showModal() should put popover dialog in top layer");
+
+ test(() => {
+ const popover_dialog = document.getElementById("popover-dialog");
+ assert_equals(getComputedStyle(popover_dialog).overlay, "none",
+ "Computed overlay");
+ popover_dialog.showPopover();
+ assert_equals(getComputedStyle(popover_dialog).overlay, "auto",
+ "Computed overlay after showPopover()");
+ popover_dialog.hidePopover();
+ }, "dialog.showPopover() should put popover dialog in top layer");
+
+ test(() => {
+ const popover_div = document.getElementById("popover-div");
+ assert_equals(getComputedStyle(popover_div).overlay, "none",
+ "Computed overlay");
+ popover_div.showPopover();
+ assert_equals(getComputedStyle(popover_div).overlay, "auto",
+ "Computed overlay after showPopover()");
+ popover_div.hidePopover();
+ }, "div.showPopover() should put popover div in top layer");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-removal-2.html b/testing/web-platform/tests/html/semantics/popovers/popover-removal-2.html
new file mode 100644
index 0000000000..b21c0bb557
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-removal-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover document removal behavior</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id=frame1 srcdoc="<div popover id=popover>Popover</div>"></iframe>
+<iframe id=frame2></iframe>
+
+<script>
+ window.onload = () => {
+ test(t => {
+ const frame1Doc = document.getElementById('frame1').contentDocument;
+ const frame2Doc = document.getElementById('frame2').contentDocument;
+ const popover = frame1Doc.querySelector('[popover]');
+ assert_true(!!popover);
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ frame2Doc.body.appendChild(popover);
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ }, 'Moving popover between documents shouldn\'t cause issues');
+ };
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-removal.html b/testing/web-platform/tests/html/semantics/popovers/popover-removal.html
new file mode 100644
index 0000000000..d2b664b464
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-removal.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover document removal behavior</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div popover id=popover>Popover</div>
+
+<script>
+promise_test(async t => {
+ function loadCompleted() {
+ return new Promise(resolve => {
+ window.addEventListener('load', resolve);
+ });
+ }
+ const popover = document.querySelector('[popover]');
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.remove(); // Shouldn't cause any issues
+ document.body.click(); // Shouldn't cause light dismiss problems
+ await loadCompleted(); // The document should finish loading
+}, 'Removal from the document shouldn\'t cause issues');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-shadow-dom.html b/testing/web-platform/tests/html/semantics/popovers/popover-shadow-dom.html
new file mode 100644
index 0000000000..62aa135b56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-shadow-dom.html
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<script>
+ function ensureShadowDom(host) {
+ host.querySelectorAll('my-element').forEach(host => {
+ if (host.shadowRoot)
+ return; // Declarative Shadow DOM is enabled
+ const template = host.firstElementChild;
+ assert_true(template instanceof HTMLTemplateElement);
+ const shadow = host.attachShadow({mode: 'open'});
+ shadow.appendChild(template.content);
+ template.remove();
+ })
+ }
+ function findPopovers(root) {
+ let popovers = [];
+ if (!root)
+ return popovers;
+ if (root instanceof Element && root.matches('[popover]'))
+ popovers.push(root);
+ popovers.push(...findPopovers(root.shadowRoot));
+ root.childNodes.forEach(child => {
+ popovers.push(...findPopovers(child));
+ })
+ return popovers;
+ }
+ function getPopoverReferences(testId) {
+ const testRoot = document.querySelector(`#${testId}`);
+ assert_true(!!testRoot);
+ ensureShadowDom(testRoot);
+ return findPopovers(testRoot);
+ }
+ function showTestPopover(testId,popoverNum) {
+ getPopoverReferences(testId)[popoverNum].showPopover();
+ }
+</script>
+
+<div id=test1>
+ <button onclick='showTestPopover("test1",0)'>Test1 Popover</button>
+ <my-element>
+ <template shadowrootmode=open>
+ <div popover>
+ <p>This should show, even though it is inside shadow DOM.</p>
+ </div>
+ </template>
+ </my-element>
+</div>
+
+<script>
+ test(function() {
+ const popover = getPopoverReferences('test1')[0];
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_true(isElementVisible(popover));
+ popover.hidePopover(); // Cleanup
+ }, "Popovers located inside shadow DOM can still be shown");
+</script>
+
+
+<div id=test2>
+ <button id=t2b1 onclick='showTestPopover("test2",0)'>Test 2 Popover 1</button>
+ <div popover anchor=t2b1 style="top: 200px;">
+ <p>Popover 1</p>
+ <button id=t2b2 onclick='showTestPopover("test2",1)'>Test 2 Popover 2</button>
+ </div>
+ <my-element>
+ <template shadowrootmode=open>
+ <div popover anchor=t2b2 style="top: 400px;">
+ <p>Hiding this popover will hide *all* open popovers,</p>
+ <p>because t2b2 doesn't exist in this context.</p>
+ </div>
+ </template>
+ </my-element>
+</div>
+
+<script>
+ test(function() {
+ const [popover1,popover2] = getPopoverReferences('test2');
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(isElementVisible(popover1));
+ popover2.showPopover();
+ assert_false(popover1.matches(':popover-open'), 'popover1 open'); // P1 was closed by P2
+ assert_false(isElementVisible(popover1), 'popover1 visible');
+ assert_true(popover2.matches(':popover-open'), 'popover2 open'); // P2 is open
+ assert_true(isElementVisible(popover2), 'popover2 visible');
+ popover2.hidePopover(); // Cleanup
+ }, "anchor references do not cross shadow boundaries");
+</script>
+
+
+<div id=test3>
+ <my-element>
+ <template shadowrootmode=open>
+ <button id=t3b1 onclick='showTestPopover("test3",0)'>Test 3 Popover 1</button>
+ <div popover anchor=t3b1>
+ <p>This popover will stay open when popover2 shows.</p>
+ <slot></slot>
+ </div>
+ </template>
+ <button id=t3b2 onclick='showTestPopover("test3",1)'>Test 3 Popover 2</button>
+ </my-element>
+ <div popover anchor=t3b2>Popover 2</div>
+</div>
+
+<script>
+ promise_test(async function() {
+ const [popover1,popover2] = getPopoverReferences('test3');
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(isElementVisible(popover1));
+ // Showing popover2 should not close popover1, since it is a flat
+ // tree ancestor of popover2's anchor button.
+ popover2.showPopover();
+ assert_true(popover2.matches(':popover-open'));
+ assert_true(isElementVisible(popover2));
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(isElementVisible(popover1));
+ popover1.hidePopover();
+ await waitForRender();
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(isElementVisible(popover1));
+ assert_false(popover2.matches(':popover-open'));
+ assert_false(isElementVisible(popover2));
+ }, "anchor references use the flat tree not the DOM tree");
+</script>
+
+
+<div id=test4>
+ <button id=t4b1 onclick='showTestPopover("test4",0)'>Test 4 Popover 1</button>
+ <div popover anchor=t4b1>
+ <p>This should not get hidden when popover2 opens.</p>
+ <my-element>
+ <template shadowrootmode=open>
+ <button id=t4b2 onclick='showTestPopover("test4",1)'>Test 4 Popover 2</button>
+ <div popover anchor=t4b2>
+ <p>This should not hide popover1.</p>
+ </div>
+ </template>
+ </my-element>
+ </div>
+</div>
+
+<script>
+ promise_test(async function() {
+ const [popover1,popover2] = getPopoverReferences('test4');
+ popover1.showPopover();
+ popover2.showPopover();
+ // Both 1 and 2 should be open at this point.
+ assert_true(popover1.matches(':popover-open'), 'popover1 not open');
+ assert_true(isElementVisible(popover1));
+ assert_true(popover2.matches(':popover-open'), 'popover2 not open');
+ assert_true(isElementVisible(popover2));
+ // This should hide both of them.
+ popover1.hidePopover();
+ await waitForRender();
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(isElementVisible(popover1));
+ assert_false(popover2.matches(':popover-open'));
+ assert_false(isElementVisible(popover2));
+ }, "The popover stack is preserved across shadow-inclusive ancestors");
+</script>
+
+
+<div id=test5>
+ <template shadowrootmode=open>
+ <button popovertarget=p1>Test 5 Popover 1</button>
+ <div popover id=p1>Popover 1
+ <p>This should not get hidden when popover2 opens.</p>
+ <button popovertarget=p2>Click</button>
+ </div>
+ <div popover id=p2>Popover 2
+ <p>This should not hide popover1.</p>
+ </div>
+ </template>
+</div>
+<script>
+ promise_test(async function() {
+ const [popover1,popover2] = getPopoverReferences('test5');
+ popover1.showPopover();
+ popover1.querySelector('button').click(); // Use invoker to keep 2 visible
+ // Both 1 and 2 should be open at this point.
+ assert_true(popover1.matches(':popover-open'), 'popover1 not open');
+ assert_true(isElementVisible(popover1));
+ assert_true(popover2.matches(':popover-open'), 'popover2 not open');
+ assert_true(isElementVisible(popover2));
+ // This should hide both of them.
+ popover1.hidePopover();
+ await waitForRender();
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(isElementVisible(popover1));
+ assert_false(popover2.matches(':popover-open'));
+ assert_false(isElementVisible(popover2));
+ }, "Popover ancestor relationships are within a root, not within the document");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-shadowhost-focus.html b/testing/web-platform/tests/html/semantics/popovers/popover-shadowhost-focus.html
new file mode 100644
index 0000000000..91ee547d4e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-shadowhost-focus.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/8994">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div popover=auto tabindex=0 data-test="autofocus=true, delegatesfocus=false" autofocus class=should-be-focused>
+ <template shadowrootmode=open>
+ <button autofocus>autofocus button</button>
+ </template>
+</div>
+
+<!-- The autofocus popover is what focus() gets called on, but since it has a
+ delegatesFocus shadowroot, focus() itself goes into the shadowroot. -->
+<div popover=auto tabindex=0 data-test="autofocus=true, delegatesfocus=true" autofocus>
+ <template shadowrootmode=open shadowrootdelegatesfocus>
+ <button autofocus class=should-be-focused>autofocus button</button>
+ </template>
+</div>
+
+<div popover=auto tabindex=0 data-test="autofocus=false, delegatesfocus=false">
+ <template shadowrootmode=open>
+ <button autofocus>autofocus button</button>
+ </template>
+</div>
+
+<div popover=auto tabindex=0 data-test="autofocus=false, delegatesfocus=true">
+ <template shadowrootmode=open shadowrootdelegatesfocus>
+ <button autofocus>autofocus button</button>
+ </template>
+</div>
+
+<script>
+document.querySelectorAll('body > [popover]').forEach(popover => {
+ promise_test(async () => {
+ const expectedFocusedElement = (popover.matches('.should-be-focused') ? popover : null)
+ || popover.querySelector('.should-be-focused')
+ || popover.shadowRoot.querySelector('.should-be-focused')
+ || document.body;
+
+ popover.showPopover();
+
+ let actualFocusedElement = document.activeElement;
+ if (actualFocusedElement.shadowRoot && actualFocusedElement.shadowRoot.activeElement) {
+ actualFocusedElement = actualFocusedElement.shadowRoot.activeElement;
+ }
+
+ popover.hidePopover();
+
+ // Resetting focus may happen asynchronously
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ assert_equals(actualFocusedElement, expectedFocusedElement);
+ }, popover.getAttribute('data-test'));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-stacking-anchor-attribute.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-stacking-anchor-attribute.tentative.html
new file mode 100644
index 0000000000..6895b8625a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-stacking-anchor-attribute.tentative.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://www.w3.org/TR/css-anchor-position-1/#implicit">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- Once this test is made non-tentative, it can be folded back into popover-stacking.html -->
+
+<div class="example">
+ <p>anchor attribute relationship</p>
+ <div id=anchor1 popover class=ancestor><p>Ancestor popover</p></div>
+ <div anchor=anchor1 popover class=child><p>Child popover</p></div>
+</div>
+
+<div class="example">
+ <p>indirect anchor attribute relationship</p>
+ <div popover class=ancestor>
+ <p>Ancestor popover</p>
+ <div>
+ <div>
+ <span id=anchor2>Anchor</span>
+ </div>
+ </div>
+ </div>
+ <div anchor=anchor2 popover class=child><p>Child popover</p></div>
+</div>
+
+<!-- Other examples -->
+
+<div popover id=p1 anchor=b1><p>This is popover #1</p>
+ <button id=b2 onclick='p2.showPopover()'>Popover 2</button>
+ <button id=b4 onclick='p4.showPopover()'>Popover 4</button>
+</div>
+<div popover id=p2 anchor=b2><p>This is popover #2</p>
+ <button id=b3 onclick='p3.showPopover()'>Popover 3</button>
+</div>
+<div popover id=p3 anchor=b3><p>This is popover #3</p></div>
+<div popover id=p4 anchor=b4><p>This is popover #4</p></div>
+<button id=b1 onclick='p1.showPopover()'>Popover 1</button>
+
+<dialog id=d1>This is a dialog<button onclick='this.parentElement.close()'>Close</button></dialog>
+<button id=b5 onclick='d1.showPopover()'>Dialog</button>
+
+<script>
+ // Test basic ancestor relationships
+ for(let example of document.querySelectorAll('.example')) {
+ const descr = example.querySelector('p').textContent;
+ const ancestor = example.querySelector('[popover].ancestor');
+ const child = example.querySelector('[popover].child');
+ const clickToActivate = example.querySelector('.clickme');
+ test(function() {
+ assert_true(!!descr && !!ancestor && !!child);
+ assert_false(ancestor.matches(':popover-open'));
+ assert_false(child.matches(':popover-open'));
+ ancestor.showPopover();
+ if (clickToActivate)
+ clickToActivate.click();
+ else
+ child.showPopover();
+ assert_true(child.matches(':popover-open'));
+ assert_true(ancestor.matches(':popover-open'));
+ ancestor.hidePopover();
+ assert_false(ancestor.matches(':popover-open'));
+ assert_false(child.matches(':popover-open'));
+ },descr);
+ }
+
+ const popovers = [p1, p2, p3, p4];
+
+ function assertState(...states) {
+ assert_equals(popovers.length,states.length);
+ for(let i=0;i<popovers.length;++i) {
+ assert_equals(popovers[i].matches(':popover-open'),states[i],`Popover #${i+1} incorrect state`);
+ }
+ }
+ test(function() {
+ assertState(false,false,false,false);
+ p1.showPopover();
+ assertState(true,false,false,false);
+ p2.showPopover();
+ assertState(true,true,false,false);
+ p3.showPopover();
+ assertState(true,true,true,false);
+ // P4 is a sibling of P2, so showing it should
+ // close P2 and P3.
+ p4.showPopover();
+ assertState(true,false,false,true);
+ // P2 should close P4 now.
+ p2.showPopover();
+ assertState(true,true,false,false);
+ // Hiding P1 should hide all.
+ p1.hidePopover();
+ assertState(false,false,false,false);
+ }, "more complex nesting, all using anchor ancestry")
+</script>
+
+<style>
+ #p1 { top:350px; }
+ #p2 { top:350px; left:200px; }
+ #p3 { top:500px; }
+ #p4 { top:500px;left:200px; }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-stacking-context-ref.html b/testing/web-platform/tests/html/semantics/popovers/popover-stacking-context-ref.html
new file mode 100644
index 0000000000..4d4ca6973f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-stacking-context-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="stylesheet" href="resources/popover-styles.css">
+
+<div class="fake-popover">
+ Inside popover
+ <div class=z style="z-index: 2; background:lightgreen">z-index 2
+ <div class=z style="z-index: 3; background:lightblue; left: 20px;">z-index 3</div>
+ <div class=z style="z-index: 1; background:pink; top:-20px; left: 10px;">z-index 1</div>
+ </div>
+ <div class=z style="background:green; top:-100px; left: 250px; width: 100px;">Outside</div>
+ Bottom of popover
+</div>
+
+<style>
+ .fake-popover {
+ width: 200px;
+ height: 230px;
+ border: 1px solid red;
+ top:50px;
+ left:50px;
+ }
+ .z {
+ position: relative;
+ border: 1px solid black;
+ padding: 1em;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-stacking-context.html b/testing/web-platform/tests/html/semantics/popovers/popover-stacking-context.html
new file mode 100644
index 0000000000..ba4e85a897
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-stacking-context.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=match href="popover-stacking-context-ref.html">
+
+<div popover>
+ Inside popover
+ <div class=z style="z-index: 2; background:lightgreen">z-index 2
+ <div class=z style="z-index: 3; background:lightblue; left: 20px;">z-index 3</div>
+ <div class=z style="z-index: 1; background:pink; top:-20px; left: 10px;">z-index 1</div>
+ </div>
+ <div class=z style="background:green; top:-100px; left: 250px; width: 100px;">Outside</div>
+ Bottom of popover
+</div>
+
+<style>
+ [popover] {
+ width: 200px;
+ height: 230px;
+ border: 1px solid red;
+ top:50px;
+ left:50px;
+ }
+ .z {
+ position: relative;
+ border: 1px solid black;
+ padding: 1em;
+ }
+</style>
+
+<script>
+ document.querySelector('[popover]').showPopover();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-stacking.html b/testing/web-platform/tests/html/semantics/popovers/popover-stacking.html
new file mode 100644
index 0000000000..1c352d566e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-stacking.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- Enumerate all the ways of creating an ancestor popover relationship -->
+
+<div class="example">
+ <p>Direct DOM children</p>
+ <div popover class=ancestor><p>Ancestor popover</p>
+ <div popover class=child><p>Child popover</p></div>
+ </div>
+</div>
+
+<div class="example">
+ <p>Grandchildren</p>
+ <div popover class=ancestor><p>Ancestor popover</p>
+ <div>
+ <div>
+ <div popover class=child><p>Child popover</p></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="example">
+ <p>popovertarget attribute relationship</p>
+ <div popover class=ancestor><p>Ancestor popover</p>
+ <button popovertarget=trigger1 class=clickme>Button</button>
+ </div>
+ <div id=trigger1 popover class=child><p>Child popover</p></div>
+</div>
+
+<div class="example">
+ <p>nested popovertarget attribute relationship</p>
+ <div popover class=ancestor><p>Ancestor popover</p>
+ <div>
+ <div>
+ <button popovertarget=trigger2 class=clickme>Button</button>
+ </div>
+ </div>
+ </div>
+ <div id=trigger2 popover class=child><p>Child popover</p></div>
+</div>
+
+<!-- Other examples -->
+
+<button id=b1 onclick='p1.showPopover()'>Popover 1</button>
+<div popover id=p1><p>This is popover #1</p>
+ <button id=b2 onclick='p2.showPopover()'>Popover 2</button>
+ <div popover id=p2><p>This is popover #2</p>
+ <button id=b3 onclick='p3.showPopover()'>Popover 3</button>
+ <div popover id=p3><p>This is popover #3</p></div>
+ </div>
+</div>
+
+<dialog id=d1>This is a dialog<button onclick='this.parentElement.close()'>Close</button></dialog>
+<button id=b5 onclick='d1.showPopover()'>Dialog</button>
+
+<script>
+ // Test basic ancestor relationships
+ for(let example of document.querySelectorAll('.example')) {
+ const descr = example.querySelector('p').textContent;
+ const ancestor = example.querySelector('[popover].ancestor');
+ const child = example.querySelector('[popover].child');
+ const clickToActivate = example.querySelector('.clickme');
+ test(function() {
+ assert_true(!!descr && !!ancestor && !!child);
+ assert_false(ancestor.matches(':popover-open'));
+ assert_false(child.matches(':popover-open'));
+ ancestor.showPopover();
+ if (clickToActivate)
+ clickToActivate.click();
+ else
+ child.showPopover();
+ assert_true(child.matches(':popover-open'));
+ assert_true(ancestor.matches(':popover-open'));
+ ancestor.hidePopover();
+ assert_false(ancestor.matches(':popover-open'));
+ assert_false(child.matches(':popover-open'));
+ },descr);
+ }
+
+ const popovers = [p1, p2, p3];
+
+ function assertState(...states) {
+ assert_equals(popovers.length,states.length);
+ for(let i=0;i<popovers.length;++i) {
+ assert_equals(popovers[i].matches(':popover-open'),states[i],`Popover #${i+1} incorrect state`);
+ }
+ }
+
+ test(function() {
+ function openManyPopovers() {
+ p1.showPopover();
+ p2.showPopover();
+ p3.showPopover();
+ assertState(true,true,true);
+ }
+ openManyPopovers();
+ d1.show(); // Dialog.show() should hide all popovers.
+ assertState(false,false,false);
+ d1.close();
+ openManyPopovers();
+ d1.showModal(); // Dialog.showModal() should also hide all popovers.
+ assertState(false,false,false);
+ d1.close();
+ }, "popovers should be closed by dialogs")
+
+ test(function() {
+ // Note: d1 is a <dialog> element, not a popover.
+ assert_false(d1.open);
+ d1.show();
+ assert_true(d1.open);
+ p1.showPopover();
+ assertState(true,false,false);
+ assert_true(d1.open);
+ p1.hidePopover();
+ assert_true(d1.open);
+ d1.close();
+ assert_false(d1.open);
+ }, "dialogs should not be closed by popovers")
+</script>
+
+<style>
+ #p1 { top:350px; }
+ #p2 { top:350px; left:200px; }
+ #p3 { top:500px; }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html
new file mode 100644
index 0000000000..b03ec78ebf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>The popovertargetaction=hover behavior</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<body>
+<style>
+.unrelated {top:0;}
+.invoker {top:100px; width:fit-content; height:fit-content;}
+[popover] {top: 200px;}
+.offset-child {top:300px; left:300px;}
+</style>
+
+<script>
+const popoverShowDelay = 100; // The CSS delay setting.
+const hoverWaitTime = 200; // How long to wait to cover the delay for sure.
+async function makePopoverAndInvoker(test, popoverType, invokerType, delayMs) {
+ delayMs = delayMs || popoverShowDelay;
+ const popover = Object.assign(document.createElement('div'),{popover: popoverType});
+ document.body.appendChild(popover);
+ popover.textContent = 'Popover';
+ // Set popover-show-delay on the popover to 0 - it should be ignored.
+ popover.setAttribute('style',`popover-show-delay: 0; popover-hide-delay: 1000s;`);
+ let invoker = document.createElement('button');
+ invoker.setAttribute('class','invoker');
+ invoker.popoverTargetElement = popover;
+ invoker.popoverTargetAction = "hover";
+ // Set popover-hide-delay on the invoker to 0 - it should be ignored.
+ invoker.setAttribute('style',`popover-show-delay: ${delayMs}ms; popover-hide-delay: 0;`);
+ document.body.appendChild(invoker);
+ const actualHoverDelay = Number(getComputedStyle(invoker)['popoverShowDelay'].slice(0,-1))*1000;
+ assert_equals(actualHoverDelay,delayMs,'popover-show-delay is incorrect');
+ const originalInvoker = invoker;
+ const reassignPopoverFn = (p) => {originalInvoker.popoverTargetElement = p};
+ switch (invokerType) {
+ case 'plain':
+ // Invoker is just a button.
+ invoker.textContent = 'Invoker';
+ break;
+ case 'nested':
+ // Invoker is just a button containing a div.
+ const child1 = invoker.appendChild(document.createElement('div'));
+ child1.textContent = 'Invoker';
+ break;
+ case 'nested-offset':
+ // Invoker is a child of the invoking button, and is not contained within
+ // the bounds of the popovertarget element.
+ invoker.textContent = 'Invoker';
+ // Reassign invoker to the child:
+ invoker = invoker.appendChild(document.createElement('div'));
+ invoker.textContent = 'Invoker child';
+ invoker.setAttribute('class','offset-child');
+ break;
+ case 'none':
+ // No invoker.
+ invoker.remove();
+ break;
+ default:
+ assert_unreached(`Invalid invokerType ${invokerType}`);
+ }
+ const unrelated = document.createElement('div');
+ document.body.appendChild(unrelated);
+ unrelated.textContent = 'Unrelated';
+ unrelated.setAttribute('class','unrelated');
+ test.add_cleanup(async () => {
+ popover.remove();
+ invoker.remove();
+ originalInvoker.remove();
+ unrelated.remove();
+ await waitForRender();
+ });
+ await mouseOver(unrelated); // Start by mousing over the unrelated element
+ await waitForRender();
+ return {popover,invoker,reassignPopoverFn};
+}
+
+// NOTE about testing methodology:
+// This test checks whether popovers are triggered *after* the appropriate hover
+// delay. The delay used for testing is kept low, to avoid this test taking too
+// long, but that means that sometimes on a slow bot/client, the hover delay can
+// elapse before we are able to check the popover status. And that can make this
+// test flaky. To avoid that, the msSinceMouseOver() function is used to check
+// that not-too-much time has passed, and if it has, the test is simply skipped.
+
+["auto","hint","manual"].forEach(type => {
+ ["plain","nested","nested-offset"].forEach(invokerType => {
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ assert_false(popover.matches(':popover-open'));
+ await mouseOver(invoker);
+ let showing = popover.matches(':popover-open');
+ // See NOTE above.
+ if (msSinceMouseOver() < popoverShowDelay)
+ assert_false(showing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ assert_true(msSinceMouseOver() >= hoverWaitTime,'waitForHoverTime should wait the specified time');
+ assert_true(popover.matches(':popover-open'),'popover should show after delay');
+ assert_true(hoverWaitTime > popoverShowDelay,'popoverShowDelay is the CSS setting, hoverWaitTime should be longer than that');
+ popover.hidePopover(); // Cleanup
+ },`popovertargetaction=hover shows a popover with popover=${type}, invokerType=${invokerType}`);
+
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ assert_false(popover.matches(':popover-open'));
+ invoker.click(); // Click the invoker
+ assert_true(popover.matches(':popover-open'),'Clicking the invoker should show the popover, even when popovertargetaction=hover');
+ popover.hidePopover(); // Cleanup
+ },`popovertargetaction=hover should also allow click activation, for popover=${type}, invokerType=${invokerType}`);
+
+ promise_test(async (t) => {
+ const longerHoverDelay = hoverWaitTime*2;
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType,longerHoverDelay);
+ await mouseOver(invoker);
+ let showing = popover.matches(':popover-open');
+ // See NOTE above.
+ if (msSinceMouseOver() >= longerHoverDelay)
+ return; // The WPT runner was too slow.
+ assert_false(showing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ showing = popover.matches(':popover-open');
+ if (msSinceMouseOver() >= longerHoverDelay)
+ return; // The WPT runner was too slow.
+ assert_false(showing,'popover should not show after not long enough of a delay');
+ },`popovertargetaction=hover popover-show-delay is respected (popover=${type}, invokerType=${invokerType})`);
+
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ await mouseOver(invoker);
+ assert_true(popover.matches(':popover-open'),'popover should stay showing on mouseover');
+ await waitForHoverTime(hoverWaitTime);
+ assert_true(popover.matches(':popover-open'),'popover should stay showing after delay');
+ popover.hidePopover(); // Cleanup
+ },`popovertargetaction=hover does nothing when popover is already showing (popover=${type}, invokerType=${invokerType})`);
+
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ await mouseOver(invoker);
+ let showing = popover.matches(':popover-open');
+ popover.remove();
+ // See NOTE above.
+ if (msSinceMouseOver() >= popoverShowDelay)
+ return; // The WPT runner was too slow.
+ assert_false(showing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'popover should not show even after a delay');
+ // Now put it back in the document and make sure it doesn't trigger.
+ document.body.appendChild(popover);
+ await waitForHoverTime(hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'popover should not show even when returned to the document');
+ },`popovertargetaction=hover does nothing when popover is moved out of the document (popover=${type}, invokerType=${invokerType})`);
+
+ promise_test(async (t) => {
+ const {popover,invoker,reassignPopoverFn} = await makePopoverAndInvoker(t,type,invokerType);
+ const popover2 = Object.assign(document.createElement('div'),{popover: type});
+ document.body.appendChild(popover2);
+ t.add_cleanup(() => popover2.remove());
+ await mouseOver(invoker);
+ let eitherShowing = popover.matches(':popover-open') || popover2.matches(':popover-open');
+ reassignPopoverFn(popover2);
+ // See NOTE above.
+ if (msSinceMouseOver() >= popoverShowDelay)
+ return; // The WPT runner was too slow.
+ assert_false(eitherShowing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'popover #1 should not show since popovertarget was reassigned');
+ assert_false(popover2.matches(':popover-open'),'popover #2 should not show since popovertarget was reassigned');
+ },`popovertargetaction=hover does nothing when target changes (popover=${type}, invokerType=${invokerType})`);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-target-element-disabled.html b/testing/web-platform/tests/html/semantics/popovers/popover-target-element-disabled.html
new file mode 100644
index 0000000000..d5c951768c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-target-element-disabled.html
@@ -0,0 +1,159 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/8221#discussion_r1049379113">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=outerpopover popover=auto>
+ <button popovertarget=innerpopover disabled>toggle popover</button>
+</div>
+<div id=innerpopover popover=auto>popover</div>
+<script>
+test(() => {
+ outerpopover.showPopover();
+ outerpopover.querySelector('button').click(); // Invoke innerpopover
+ assert_false(innerpopover.matches(':popover-open'),
+ 'disabled button shouldn\'t open the target popover');
+ assert_true(outerpopover.matches(':popover-open'));
+ innerpopover.showPopover();
+ assert_true(innerpopover.matches(':popover-open'),
+ 'The inner popover should be able to open successfully.');
+ assert_false(outerpopover.matches(':popover-open'),
+ 'The outer popover should be closed by opening the inner one.');
+}, 'Disabled popover*target buttons should not affect the popover heirarchy.');
+</script>
+
+<div id=outerpopover2 popover=auto>
+ <button id=togglebutton2 popovertarget=innerpopover2>toggle popover</button>
+</div>
+<div id=innerpopover2 popover=auto>popover</div>
+<script>
+test(() => {
+ outerpopover2.showPopover();
+ outerpopover2.querySelector('button').click(); // Invoke innerpopover
+ assert_true(innerpopover2.matches(':popover-open'),
+ 'The inner popover should be able to open successfully.');
+ assert_true(outerpopover2.matches(':popover-open'),
+ 'The outer popover should stay open when opening the inner one.');
+
+ togglebutton2.disabled = true;
+ assert_true(innerpopover2.matches(':popover-open'),
+ 'Changing disabled states after popovers are open shouldn\'t close anything');
+ assert_true(outerpopover2.matches(':popover-open'),
+ 'Changing disabled states after popovers are open shouldn\'t close anything');
+}, 'Disabling popover*target buttons when popovers are open should not cause popovers to be closed.');
+</script>
+
+<div id=outerpopover4 popover=auto>
+ <button id=togglebutton4 popovertarget=innerpopover4>toggle popover</button>
+</div>
+<div id=innerpopover4 popover=auto>popover</div>
+<form id=submitform>form</form>
+<script>
+test(() => {
+ outerpopover4.showPopover();
+ outerpopover4.querySelector('button').click(); // Invoke innerpopover
+ assert_true(innerpopover4.matches(':popover-open'),
+ 'The inner popover should be able to open successfully.');
+ assert_true(outerpopover4.matches(':popover-open'),
+ 'The outer popover should stay open when opening the inner one.');
+
+ togglebutton4.setAttribute('form', 'submitform');
+ assert_true(innerpopover4.matches(':popover-open'),
+ 'The inner popover be should be not closed when invoking buttons cease to be invokers.');
+ assert_true(outerpopover4.matches(':popover-open'),
+ 'The outer popover be should be not closed when invoking buttons cease to be invokers.');
+}, 'Setting the form attribute on popover*target buttons when popovers are open should not close them.');
+</script>
+
+<div id=outerpopover5 popover=auto>
+ <input type=button id=togglebutton5 popovertarget=innerpopover5>toggle popover</button>
+</div>
+<div id=innerpopover5 popover=auto>popover</div>
+<script>
+test(() => {
+ outerpopover5.showPopover();
+ outerpopover5.querySelector('input').click(); // Invoke innerpopover
+ assert_true(innerpopover5.matches(':popover-open'),
+ 'The inner popover should be able to open successfully.');
+ assert_true(outerpopover5.matches(':popover-open'),
+ 'The outer popover should stay open when opening the inner one.');
+
+ togglebutton5.setAttribute('type', 'text');
+ assert_true(innerpopover5.matches(':popover-open'),
+ 'The inner popover be should be not closed when invoking buttons cease to be invokers.');
+ assert_true(outerpopover5.matches(':popover-open'),
+ 'The outer popover be should be not closed when invoking buttons cease to be invokers.');
+}, 'Changing the input type on a popover*target button when popovers are open should not close anything.');
+</script>
+
+<div id=outerpopover6 popover=auto>
+ <button id=togglebutton6 popovertarget=innerpopover6>toggle popover</button>
+</div>
+<div id=innerpopover6 popover=auto>popover</div>
+<script>
+test(() => {
+ outerpopover6.showPopover();
+ outerpopover6.querySelector('button').click(); // Invoke innerpopover
+ assert_true(innerpopover6.matches(':popover-open'),
+ 'The inner popover should be able to open successfully.');
+ assert_true(outerpopover6.matches(':popover-open'),
+ 'The outer popover should stay open when opening the inner one.');
+
+ togglebutton6.remove();
+ assert_true(innerpopover6.matches(':popover-open'),
+ 'The inner popover be should be not closed when invoking buttons are removed.');
+ assert_true(outerpopover6.matches(':popover-open'),
+ 'The outer popover be should be not closed when invoking buttons are removed.');
+}, 'Disconnecting popover*target buttons when popovers are open should not close anything.');
+</script>
+
+<div id=outerpopover7 popover=auto>
+ <button id=togglebutton7 popovertarget=innerpopover7>toggle popover</button>
+</div>
+<div id=innerpopover7 popover=auto>popover</div>
+<script>
+test(() => {
+ outerpopover7.showPopover();
+ outerpopover7.querySelector('button').click(); // Invoke innerpopover
+ assert_true(innerpopover7.matches(':popover-open'),
+ 'The inner popover should be able to open successfully.');
+ assert_true(outerpopover7.matches(':popover-open'),
+ 'The outer popover should stay open when opening the inner one.');
+
+ togglebutton7.setAttribute('popovertarget', 'otherpopover7');
+ assert_true(innerpopover7.matches(':popover-open'),
+ 'The inner popover be should be not closed when invoking buttons are retargeted.');
+ assert_true(outerpopover7.matches(':popover-open'),
+ 'The outer popover be should be not closed when invoking buttons are retargeted.');
+}, 'Changing the popovertarget attribute to break the chain should not close anything.');
+</script>
+
+<div id=outerpopover8 popover=auto>
+ <div id=middlepopover8 popover=auto>
+ <div id=innerpopover8 popover=auto>hello</div>
+ </div>
+</div>
+<div id=otherpopover8 popover=auto>other popover</div>
+<button id=togglebutton8 popovertarget=middlepopover8>other button</div>
+<script>
+test(() => {
+ outerpopover8.showPopover();
+ middlepopover8.showPopover();
+ innerpopover8.showPopover();
+ assert_true(innerpopover8.matches(':popover-open'),
+ 'The inner popover should be able to open successfully.');
+ assert_true(middlepopover8.matches(':popover-open'),
+ 'The middle popover should stay open when opening the inner one.');
+ assert_true(outerpopover8.matches(':popover-open'),
+ 'The outer popover should stay open when opening the inner one.');
+
+ togglebutton8.setAttribute('popovertarget', 'otherpopover8');
+ assert_true(innerpopover8.matches(':popover-open'),
+ 'The inner popover should remain open.');
+ assert_true(middlepopover8.matches(':popover-open'),
+ 'The middle popover should remain open.');
+ assert_true(outerpopover8.matches(':popover-open'),
+ 'The outer popover should remain open.');
+}, `Modifying popovertarget on a button which doesn't break the chain shouldn't close any popovers.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-combinations.html b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-combinations.html
new file mode 100644
index 0000000000..024794f578
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-combinations.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popover combined with dialog/fullscreen behavior</title>
+<link rel=author href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<button id=visible>Visible button</button>
+<div id=examples>
+ <dialog popover>Popover Dialog</dialog>
+ <dialog popover open style="top:50px;">Open Non-modal Popover Dialog</dialog>
+ <div popover class=fullscreen>Fullscreen Popover</div>
+ <dialog popover class=fullscreen>Fullscreen Popover Dialog</dialog>
+ <dialog popover open class=fullscreen style="top:200px;">Fullscreen Open Non-modal Popover Dialog</dialog>
+</div>
+
+<style>
+ [popover] {
+ inset:auto;
+ top:0;
+ left:0;
+ }
+ [popover].fullscreen.visible {
+ display:block;
+ }
+</style>
+
+<script>
+const isDialog = (ex) => ex instanceof HTMLDialogElement;
+const isFullscreen = (ex) => ex.classList.contains('fullscreen');
+function ensureIsOpenPopover(ex,message) {
+ // Because :popover-open will eventually support <dialog>, this does extra work to
+ // verify we're dealing with an :popover-open Popover. Note that this will also throw
+ // if this is an element with the `popover` attribute that has been made
+ // visible via an explicit `display:block` style rule.
+ message = message || 'Error';
+ assert_true(ex.matches(':popover-open'),`${message}: Popover doesn\'t match :popover-open`);
+ ex.hidePopover(); // Shouldn't throw if this is a showing popover
+ ex.showPopover(); // Show it again to avoid state change
+ assert_true(ex.matches(':popover-open'),`${message}: Sanity`);
+}
+window.onload = () => requestAnimationFrame(() => requestAnimationFrame(() => {
+ const examples = Array.from(document.querySelectorAll('#examples>*'));
+ examples.forEach(ex => {
+ promise_test(async (t) => {
+ t.add_cleanup(() => ex.remove());
+ // Test initial conditions
+ if (ex.hasAttribute('open')) {
+ assert_true(isDialog(ex));
+ assert_true(isElementVisible(ex),'Open dialog should be visible by default');
+ ex.showPopover(); // Should not throw
+ ex.removeAttribute('open');
+ ex.hidePopover();
+ assert_false(isElementVisible(ex),'Removing the open attribute should hide the dialog');
+ } else {
+ ex.showPopover(); // Should not throw
+ ensureIsOpenPopover(ex,'showPopover should work');
+ ex.hidePopover(); // Should not throw
+ assert_false(ex.matches(':popover-open'),'hidePopover should work');
+ }
+ assert_false(isElementVisible(ex));
+
+ // Start with popover, try the other API
+ ex.showPopover();
+ ensureIsOpenPopover(ex);
+ let tested_something=false;
+ if (isDialog(ex)) {
+ tested_something=true;
+ assert_throws_dom("InvalidStateError",() => ex.showModal(),'Calling showModal() on an already-showing Popover should throw InvalidStateError');
+ ex.show(); // Should not throw
+ ex.close();
+ ex.showPopover();
+ }
+ if (isFullscreen(ex)) {
+ tested_something=true;
+ let requestSucceeded = false;
+ await blessTopLayer(ex);
+ await ex.requestFullscreen()
+ .then(() => {requestSucceeded = true;}) // We should not hit this.
+ .catch((exception) => {
+ // This exception is expected.
+ assert_equals(exception.name,'TypeError',`Invalid exception from requestFullscreen() (${exception.message})`);
+ });
+ assert_false(requestSucceeded,'requestFullscreen() should not succeed when the element is an already-showing Popover');
+ }
+ assert_true(tested_something);
+ ensureIsOpenPopover(ex);
+ ex.hidePopover();
+
+ // Start with the other API, then try popover
+ if (isDialog(ex)) {
+ ex.show();
+ assert_true(ex.hasAttribute('open'));
+ ex.showPopover(); // Should not throw
+ ex.close();
+ assert_false(ex.hasAttribute('open'));
+ ex.hidePopover();
+ ex.showModal();
+ assert_true(ex.hasAttribute('open'));
+ assert_throws_dom("InvalidStateError",() => ex.showPopover(),'Calling showPopover() on an already-showing modal dialog should throw InvalidStateError');
+ ex.close();
+ assert_false(ex.hasAttribute('open'));
+ ex.hidePopover();
+ } else if (isFullscreen(ex)) {
+ let requestSucceeded = false;
+ await blessTopLayer(visible);
+ await ex.requestFullscreen()
+ .then(() => {
+ assert_throws_dom("InvalidStateError",() => ex.showPopover(),'Calling showPopover() on an already-fullscreen element should throw InvalidStateError');
+ });
+ await document.exitFullscreen()
+ .then(() => assert_true(true));
+ }
+
+ // Finally, try invoking these combined popovers via a declarative invoker
+ const button = document.createElement('button');
+ t.add_cleanup(() => button.remove());
+ document.body.appendChild(button);
+ button.popoverTargetElement = ex;
+ button.popoverTargetAction = "toggle";
+ assert_false(ex.matches(':popover-open'));
+ await clickOn(button);
+ ensureIsOpenPopover(ex,'Invoking element should be able to invoke all popovers');
+ ex.hidePopover();
+ if (isDialog(ex)) {
+ ex.showModal();
+ assert_true(ex.hasAttribute('open'));
+ } else if (isFullscreen(ex)) {
+ // Popover fullscreen isn't visible by default, so explicitly add
+ // display:block, so that calls to "clickOn" can succeed.
+ ex.classList.add('visible');
+ await blessTopLayer(ex);
+ await ex.requestFullscreen();
+ } else {
+ assert_unreached('Not a dialog or fullscreen');
+ }
+ ex.appendChild(button); // Add button to the element, so it's visible to click
+ await clickOn(button);
+ assert_false(ex.matches(':popover-open'),'The invoker click should have failed on the already-open dialog/fullscreen');
+ if (isDialog(ex)) {
+ ex.close();
+ } else {
+ await document.exitFullscreen()
+ }
+ }, `Popover combination: ${ex.textContent}`);
+ });
+}));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-interactions.html b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-interactions.html
new file mode 100644
index 0000000000..6d050ed99b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-interactions.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Interactions between top layer element types</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<body>
+<script>
+const types = Object.freeze({
+ popover: Symbol("Popover"),
+ modalDialog: Symbol("Modal Dialog"),
+ fullscreen: Symbol("Fullscreen Element"),
+});
+const examples = [
+ {
+ type: types.popover,
+ closes: [types.popover],
+ createElement: () => Object.assign(document.createElement('div'), {popover: 'auto'}),
+ trigger: function() {this.element.showPopover()},
+ close: function() {this.element.hidePopover()},
+ isTopLayer: function() {return this.element.matches(':popover-open')},
+ },
+ {
+ type: types.modalDialog,
+ closes: [types.popover],
+ createElement: () => document.createElement('dialog'),
+ trigger: function() {this.element.showModal()},
+ close: function() {this.element.close()},
+ isTopLayer: function() {return this.element.matches(':modal')},
+ },
+ {
+ type: types.fullscreen,
+ closes: [types.popover],
+ createElement: () => document.createElement('div'),
+ trigger: async function(visibleElement) {assert_false(this.isTopLayer());await blessTopLayer(visibleElement);await this.element.requestFullscreen();},
+ close: async function() {await document.exitFullscreen();},
+ isTopLayer: function() {return this.element.matches(':fullscreen')},
+ },
+];
+
+function createElement(ex) {
+ assert_true(!ex.element);
+ const element = ex.element = ex.createElement();
+ assert_true(!!element);
+ element.appendChild(document.createTextNode(`This is a ${ex.type.description}`));
+ document.body.appendChild(element);
+ assert_false(ex.isTopLayer(),'Element should start out not in the top layer');
+ return element;
+}
+async function doneWithExample(ex) {
+ assert_true(!!ex.element);
+ if (ex.isTopLayer())
+ await ex.close();
+ ex.element.remove();
+ ex.element = null;
+}
+// Test interactions between top layer elements
+for(let i=0;i<examples.length;++i) {
+ for(let j=0;j<examples.length;++j) {
+ const example1 = Object.assign([],examples[i]);
+ const example2 = Object.assign([],examples[j]);
+ const shouldClose = example2.closes.includes(example1.type);
+ const desc = `A ${example2.type.description} should${shouldClose ? "" : " *not*"} close a ${example1.type.description}.`;
+ promise_test(async t => {
+ const element1 = createElement(example1);
+ const element2 = createElement(example2);
+ t.add_cleanup(() => {
+ return Promise.all([
+ doneWithExample(example1),
+ doneWithExample(example2),
+ ]);
+ });
+ await example1.trigger(document.body); // Open the 1st top layer element
+ assert_true(example1.isTopLayer()); // Make sure it is top layer
+ await example2.trigger(element1); // Open the 2nd top layer element
+ assert_true(example2.isTopLayer()); // Make sure it is top layer
+ assert_equals(shouldClose,!example1.isTopLayer(),desc);
+ },desc);
+ }
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-anchor.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-anchor.tentative.html
new file mode 100644
index 0000000000..4520ab0577
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-anchor.tentative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="resources/popover-utils.js"></script>
+<script src="resources/popover-top-layer-nesting.js"></script>
+
+<div id=tests>
+ <div> Single popover=auto ancestor
+ <div popover=auto class=target data-stay-open=true></div>
+ </div>
+
+ <div> Single popover=manual ancestor
+ <div popover=auto class=target data-stay-open=true></div>
+ </div>
+
+ <div> Nested popover=auto ancestors
+ <div popover=auto data-stay-open=true>
+ <div popover=auto class=target data-stay-open=true></div>
+ </div>
+ </div>
+
+ <div> Nested popover=auto ancestors, target is outer
+ <div popover=auto class=target data-stay-open=true>
+ <div popover=auto data-stay-open=false></div>
+ </div>
+ </div>
+
+ <div> Top layer inside of nested element
+ <div popover=auto data-stay-open=true>
+ <button class=target></button>
+ </div>
+ </div>
+</div>
+
+<script>
+ const tests = Array.from(document.querySelectorAll('#tests>div'));
+ runTopLayerTests(tests,/*testAnchorAttribute*/true);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-hints.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-hints.tentative.html
new file mode 100644
index 0000000000..4ec1f49bda
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting-hints.tentative.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://open-ui.org/components/popover-hint.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="resources/popover-utils.js"></script>
+<script src="resources/popover-top-layer-nesting.js"></script>
+
+<div id=tests>
+ <div> Single popover=hint ancestor
+ <div popover=hint class=target data-stay-open=true></div>
+ </div>
+
+ <div> Nested auto/hint ancestors
+ <div popover=auto data-stay-open=true>
+ <div popover=hint class=target data-stay-open=true></div>
+ </div>
+ </div>
+
+ <div> Nested auto/hint ancestors, target is auto
+ <div popover=auto class=target data-stay-open=true>
+ <div popover=hint data-stay-open=false></div>
+ </div>
+ </div>
+
+ <div> Unrelated hint, target=hint
+ <div popover=auto data-stay-open=true></div>
+ <div popover=hint class=target data-stay-open=true></div>
+ </div>
+
+ <div> Unrelated hint, target=auto
+ <div popover=auto class=target data-stay-open=true></div>
+ <div popover=hint data-stay-open=false></div>
+ </div>
+</div>
+
+<script>
+ const tests = Array.from(document.querySelectorAll('#tests>div'));
+ runTopLayerTests(tests);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.tentative.html
new file mode 100644
index 0000000000..a0b3b60b72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.tentative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/top-layer.js"></script>
+<script src="resources/popover-utils.js"></script>
+<script src="resources/popover-top-layer-nesting.js"></script>
+
+<div id=tests>
+ <div> Single popover=auto ancestor
+ <div popover=auto class=target data-stay-open=true></div>
+ </div>
+
+ <div> Single popover=manual ancestor
+ <div popover=auto class=target data-stay-open=true></div>
+ </div>
+
+ <div> Nested popover=auto ancestors
+ <div popover=auto data-stay-open=true>
+ <div popover=auto class=target data-stay-open=true></div>
+ </div>
+ </div>
+
+ <div> Nested popover=auto ancestors, target is outer
+ <div popover=auto class=target data-stay-open=true>
+ <div popover=auto data-stay-open=false></div>
+ </div>
+ </div>
+
+ <div> Top layer inside of nested element
+ <div popover=auto data-stay-open=true>
+ <button class=target></button>
+ </div>
+ </div>
+</div>
+
+<script>
+ const tests = Array.from(document.querySelectorAll('#tests>div'));
+ runTopLayerTests(tests);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-types-with-hints.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-types-with-hints.tentative.html
new file mode 100644
index 0000000000..07f0e26fce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-types-with-hints.tentative.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ function getPopovers() {
+ return Array.from(document.currentScript.parentElement.querySelectorAll('[popover]'));
+ }
+ function assertState(expectedState,description) {
+ description = description || 'Error';
+ const popovers = getPopovers();
+ const n = popovers.length;
+ assert_equals(expectedState.length,n,'Invalid expected state length');
+ for(let i=0;i<n;++i) {
+ const html = '<' + popovers[i].outerHTML.split('<')[1] + '</div>';
+ assert_equals(popovers[i].matches(':popover-open'),expectedState[i],`${description}, index ${i} (${html})`);
+ }
+ }
+</script>
+
+<div>
+ <div popover>Popover</div>
+ <div popover=hint>Hint</div>
+ <div popover=manual>Async</div>
+ <div popover=manual>Async</div>
+ <script>
+ {
+ const auto = getPopovers()[0];
+ const hint = getPopovers()[1];
+ const manual = getPopovers()[2];
+ const manual2 = getPopovers()[3];
+ test(() => {
+ assertState([false,false,false,false]);
+ auto.showPopover();
+ assertState([true,false,false,false]);
+ hint.showPopover();
+ assertState([true,true,false,false]);
+ manual.showPopover();
+ assertState([true,true,true,false]);
+ manual2.showPopover();
+ assertState([true,true,true,true]);
+ hint.hidePopover();
+ assertState([true,false,true,true]);
+ auto.hidePopover();
+ assertState([false,false,true,true]);
+ auto.showPopover();
+ hint.showPopover();
+ assertState([true,true,true,true]);
+ auto.hidePopover();
+ assertState([false,false,true,true]);
+ hint.hidePopover();
+ manual.hidePopover();
+ assertState([false,false,false,true]);
+ manual2.hidePopover();
+ assertState([false,false,false,false]);
+ },'manuals do not close popovers');
+
+ test(() => {
+ assertState([false,false,false,false]);
+ hint.showPopover();
+ manual.showPopover();
+ manual2.showPopover();
+ assertState([false,true,true,true]);
+ auto.showPopover();
+ assertState([true,false,true,true]);
+ auto.hidePopover();
+ assertState([false,false,true,true]);
+ manual.hidePopover();
+ manual2.hidePopover();
+ assertState([false,false,false,false]);
+ },'autos close hints but not manuals');
+ }
+ </script>
+</div>
+
+<div>
+ <div popover>popover 1
+ <div popover>popover 2
+ <p id=anchorid>Anchor</p>
+ <div popover>popover 3</div>
+ </div>
+ </div>
+ <div popover=hint anchor=anchorid>Hint anchored to popover</div>
+ <script>
+ {
+ const popover1 = getPopovers()[0];
+ const popover2 = getPopovers()[1];
+ const popover3 = getPopovers()[2];
+ const hint = getPopovers()[3];
+ test(() => {
+ assertState([false,false,false,false]);
+ popover1.showPopover();
+ popover2.showPopover();
+ popover3.showPopover();
+ assertState([true,true,true,false]);
+ hint.showPopover(); // Because hint is nested in popover2, popover3 should be hidden
+ assertState([true,true,false,true]);
+ popover1.hidePopover(); // Should close the hint, which is anchored to popover2
+ assertState([false,false,false,false]);
+ },'hint is not closed by pre-existing auto');
+ }
+ </script>
+</div>
+
+<div>
+ <div popover=hint>Hint
+ <div popover=hint>Nested hint</div>
+ </div>
+ <script>
+ test(() => {
+ const hint1 = getPopovers()[0];
+ const hint2 = getPopovers()[1];
+ hint1.showPopover();
+ assertState([true,false]);
+ hint2.showPopover();
+ assertState([true,true]);
+ hint1.hidePopover();
+ assertState([false,false]);
+ },'You can nest hint popovers');
+ </script>
+</div>
+
+<div>
+ <div popover="hint">Hint
+ <div popover>Nested auto (note - never visible, since inside display:none subtree)</div>
+ </div>
+ <script>
+ test(() => {
+ const hint = getPopovers()[0];
+ const auto = getPopovers()[1];
+ hint.showPopover();
+ assertState([true,false]);
+ auto.showPopover();
+ assertState([false,true]);
+ auto.hidePopover();
+ assertState([false,false]);
+ },'If a popover=auto is shown, it should hide any open popover=hint, including if the popover=hint is an ancestral popover of the popover=auto. (You can\'t nest a popover=auto inside a popover=hint)');
+ </script>
+</div>
+
+<div>
+ <div popover>Auto
+ <div popover>Nested Auto</div>
+ <div popover=hint>Nested hint</div>
+ </div>
+ <script>
+ test(() => {
+ const auto = getPopovers()[0];
+ const auto2 = getPopovers()[1];
+ const hint = getPopovers()[2];
+ auto.showPopover();
+ auto2.showPopover();
+ assertState([true,true,false]);
+ hint.showPopover(); // This should hide auto2, since it is nested in auto1.
+ assertState([true,false,true]);
+ auto.hidePopover(); // Should hide both auto and hint.
+ assertState([false,false,false]);
+ },'If you: a) show a popover=auto (call it D), then b) show a descendent popover=hint of D (call it T), then c) hide D, then T should be hidden. (A popover=hint can be nested inside a popover=auto)');
+ </script>
+</div>
+
+<div>
+ <div popover>Auto</div>
+ <div popover=hint>Non-Nested hint</div>
+ <script>
+ test(() => {
+ const auto = getPopovers()[0]
+ const hint = getPopovers()[1];
+ auto.showPopover();
+ hint.showPopover();
+ assertState([true,true]);
+ auto.hidePopover();
+ assertState([false,false]);
+ },'If you: a) show a popover=auto (call it D), then b) show a non-descendent popover=hint of D (call it T), then c) hide D, then T should be hidden. (Non-nested popover=hint gets hidden when unrelated popover=autos are hidden)');
+ </script>
+</div>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-types.html b/testing/web-platform/tests/html/semantics/popovers/popover-types.html
new file mode 100644
index 0000000000..d4ad81e52b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-types.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="container">
+ <div popover>Popover</div>
+ <div popover=manual>Async</div>
+ <div popover=manual>Async</div>
+</div>
+<script>
+ const auto = container.querySelector('[popover=""]');
+ const manual = container.querySelectorAll('[popover=manual]')[0];
+ const manual2 = container.querySelectorAll('[popover=manual]')[1];
+ function assert_state_1(autoOpen,manualOpen,manual2Open) {
+ assert_equals(auto.matches(':popover-open'),autoOpen,'auto open state is incorrect');
+ assert_equals(manual.matches(':popover-open'),manualOpen,'manual open state is incorrect');
+ assert_equals(manual2.matches(':popover-open'),manual2Open,'manual2 open state is incorrect');
+ }
+ test(() => {
+ assert_state_1(false,false,false);
+ auto.showPopover();
+ assert_state_1(true,false,false);
+ manual.showPopover();
+ assert_state_1(true,true,false);
+ manual2.showPopover();
+ assert_state_1(true,true,true);
+ auto.hidePopover();
+ assert_state_1(false,true,true);
+ manual.hidePopover();
+ assert_state_1(false,false,true);
+ manual2.hidePopover();
+ assert_state_1(false,false,false);
+ }, 'manuals do not close popovers');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-undefined-remove-crash.html b/testing/web-platform/tests/html/semantics/popovers/popover-undefined-remove-crash.html
new file mode 100644
index 0000000000..3c273ea6f3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-undefined-remove-crash.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9459#issuecomment-1630466911">
+
+<div id="po" popover>
+PO
+</div>
+
+<script>
+po.popover = undefined;
+po.remove();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/popovertarget-reflection.html b/testing/web-platform/tests/html/semantics/popovers/popovertarget-reflection.html
new file mode 100644
index 0000000000..d0750fdd4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popovertarget-reflection.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1523410">
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1879001">
+<link rel=help href="https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button id=mybutton popovertarget="mypopover">toggle popover</button>
+<div id=mypopover popover=auto>popover</div>
+
+<script>
+test(() => {
+ assert_equals(mybutton.popoverTargetElement.id, "mypopover",
+ 'Setting element.popoverTargetElement to a valid element should work');
+
+ mybutton.popoverTargetElement = null;
+ assert_false(mybutton.hasAttribute('popovertarget'),
+ 'Setting element.popoverTargetElement to null should unset popovertarget attribute.');
+ assert_equals(mybutton.popoverTargetElement, null,
+ 'Setting element.popoverTargetElement to null should remove the existing element from element.popoverTargetElement.');
+
+ mybutton.popoverTargetElement = mypopover;
+ assert_true(mybutton.hasAttribute('popovertarget'),
+ 'Assigning to element.popoverTargetElement should set the popovertarget attribute.');
+
+ mybutton.removeAttribute('popovertarget');
+ assert_equals(mybutton.popoverTargetElement, null,
+ 'Removing the popovertarget attribute should remove the element from element.popoverTargetElement.');
+
+ mybutton.popoverTargetElement = mypopover;
+ assert_true(mybutton.hasAttribute('popovertarget'),
+ 'Assigning to element.popoverTargetElement should set the popovertarget attribute.');
+
+ mybutton.setAttribute("popovertarget", 'invalid');
+ assert_equals(mybutton.popoverTargetElement, null,
+ 'Setting the popovertarget attribute to a localName that is not attr should remove the existing element from element.popoverTargetElement.');
+
+ mybutton.popoverTargetElement = mypopover;
+ mybutton.setAttribute("popovertarget", "");
+ assert_equals(mybutton.popoverTargetElement.id, "mypopover",
+ 'Setting the popovertarget attribute to empty string right after explicitly setting attribute element should have no effect.');
+
+ mybutton.setAttribute("popovertarget", "mypopover");
+ assert_equals(mybutton.popoverTargetElement.id, "mypopover",
+ 'Setting the popovertarget attribute to a value should set the popover target element.');
+ mybutton.setAttribute("popovertarget", "");
+ assert_equals(mybutton.getAttribute('popovertarget'), "",
+ 'Assigning to element.popoverTargetElement to empty string should update the attribute value to empty string.');
+ assert_equals(mybutton.popoverTargetElement, null,
+ 'Setting the popovertarget attribute to empty string should remove the existing element from element.popoverTargetElement.');
+}, 'Element attribute reflection of popoverTargetElement/popovertarget should be kept in sync.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/resources/popover-hover-hide-common.js b/testing/web-platform/tests/html/semantics/popovers/resources/popover-hover-hide-common.js
new file mode 100644
index 0000000000..9f407ef157
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/resources/popover-hover-hide-common.js
@@ -0,0 +1,139 @@
+// NOTE about testing methodology:
+// This test checks whether popovers are hidden *after* the appropriate de-hover
+// delay. The delay used for testing is kept low, to avoid this test taking too
+// long, but that means that sometimes on a slow bot/client, the delay can
+// elapse before we are able to check the popover status. And that can make this
+// test flaky. To avoid that, the msSinceMouseOver() function is used to check
+// that not-too-much time has passed, and if it has, the test is simply skipped.
+
+const hoverDelays = 100; // This needs to match the style block below.
+const hoverWaitTime = 200; // How long to wait to cover the delay for sure.
+
+async function initialPopoverShow(invoker) {
+ const popover = invoker.popoverTargetElement;
+ assert_false(popover.matches(':popover-open'));
+ await mouseOver(invoker); // Always start with the mouse over the invoker
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+}
+
+function runHoverHideTest(popoverType, invokerType, invokerAction) {
+ const descr = `popover=${popoverType}, invoker=${invokerType}, popovertargetaction=${invokerAction}`;
+ promise_test(async (t) => {
+ const {popover,invoker} = makeTestParts(t, popoverType, invokerType, invokerAction);
+ await initialPopoverShow(invoker);
+ await mouseOver(unrelated);
+ let showing = popover.matches(':popover-open');
+ if (msSinceMouseOver() >= hoverDelays)
+ return; // The WPT runner was too slow.
+ assert_true(showing,'popover shouldn\'t immediately hide');
+ await mouseHover(unrelated,hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'popover should hide after delay');
+ },`The popover-hide-delay causes a popover to be hidden after a delay, ${descr}`);
+
+ promise_test(async (t) => {
+ const {popover,invoker} = makeTestParts(t, popoverType, invokerType, invokerAction);
+ await initialPopoverShow(invoker);
+ await mouseHover(popover,hoverWaitTime);
+ assert_true(popover.matches(':popover-open'),'hovering the popover should keep it showing');
+ await mouseOver(unrelated);
+ let showing = popover.matches(':popover-open');
+ if (msSinceMouseOver() >= hoverDelays)
+ return; // The WPT runner was too slow.
+ assert_true(showing,'subsequently hovering unrelated element shouldn\'t immediately hide the popover');
+ await mouseHover(unrelated,hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'hovering unrelated element should hide popover after delay');
+ },`hovering the popover keeps it from being hidden, ${descr}`);
+
+ promise_test(async (t) => {
+ const {popover,invoker,mouseOverInvoker} = makeTestParts(t, popoverType, invokerType, invokerAction);
+ await initialPopoverShow(invoker);
+ assert_true(popover.matches(':popover-open'));
+ await mouseHover(popover,hoverWaitTime);
+ await mouseHover(mouseOverInvoker,hoverWaitTime);
+ assert_true(popover.matches(':popover-open'),'Moving hover between invoker and popover should keep popover from being hidden');
+ await mouseHover(unrelated,hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'Moving hover to unrelated should finally hide the popover');
+ },`hovering an invoking element keeps the popover from being hidden, ${descr}`);
+}
+
+function runHoverHideTestsForInvokerAction(invokerAction) {
+ promise_test(async (t) => {
+ const {popover,invoker} = makeTestParts(t, 'auto', 'button', 'show');
+ assert_false(popover.matches(':popover-open'));
+ assert_true(invoker.matches('[popovertarget]'),'invoker needs to match [popovertarget]');
+ assert_equals(invoker.popoverTargetElement,popover,'invoker should point to popover');
+ await mouseHover(invoker,hoverWaitTime);
+ assert_true(msSinceMouseOver() >= hoverWaitTime,'waitForHoverTime should wait the specified time');
+ assert_true(hoverWaitTime > hoverDelays,'hoverDelays is the value from CSS, hoverWaitTime should be longer than that');
+ assert_equals(getComputedStyleTimeMs(invoker,'popoverShowDelay'),hoverDelays,'popover-show-delay is incorrect');
+ assert_equals(getComputedStyleTimeMs(popover,'popoverHideDelay'),hoverDelays,'popover-hide-delay is incorrect');
+ },'Test the harness');
+
+ // Run for all invoker and popover types.
+ ["button","input"].forEach(invokerType => {
+ ["auto","hint","manual"].forEach(popoverType => {
+ runHoverHideTest(popoverType, invokerType, invokerAction);
+ });
+ });
+}
+
+// Setup stuff
+const unrelated = document.createElement('div');
+unrelated.id = 'unrelated';
+unrelated.textContent = 'Unrelated element';
+const style = document.createElement('style');
+document.body.append(unrelated,style);
+style.textContent = `
+ div, button, input {
+ /* Fixed position everything to ensure nothing overlaps */
+ position: fixed;
+ max-height: 100px;
+ }
+ #unrelated {top: 100px;}
+ [popovertarget] {
+ top:200px;
+ popover-show-delay: 100ms;
+ }
+ [popover] {
+ width: 200px;
+ height: 100px;
+ top:300px;
+ popover-hide-delay: 100ms;
+ }
+`;
+
+function makeTestParts(t,popoverType,invokerType,invokerAction) {
+ const popover = document.createElement('div');
+ popover.id = `popover-${popoverType}-${invokerType}-${invokerAction}`;
+ document.body.appendChild(popover);
+ popover.popover = popoverType;
+ assert_equals(popover.popover, popoverType, `Type ${popoverType} not supported`);
+ const invoker = document.createElement(invokerType);
+ document.body.appendChild(invoker);
+ invoker.popoverTargetElement = popover;
+ invoker.popoverTargetAction = invokerAction;
+ assert_equals(invoker.popoverTargetAction, invokerAction, `Invoker action ${invokerAction} not supported`);
+ let mouseOverInvoker;
+ switch (invokerType) {
+ case 'button':
+ invoker.innerHTML = '<span><span data-note=nested_element>Click me</span></span>';
+ mouseOverInvoker = invoker.firstElementChild.firstElementChild;
+ assert_true(!!mouseOverInvoker);
+ break;
+ case 'input':
+ invoker.type = 'button';
+ mouseOverInvoker = invoker;
+ break;
+ default:
+ assert_unreached('Invalid invokerType ' + invokerType);
+ break;
+ }
+ t.add_cleanup(() => {popover.remove(); invoker.remove();});
+ return {popover, invoker, mouseOverInvoker};
+}
+
+function getComputedStyleTimeMs(element,property) {
+ // Times are in seconds, so just strip off the 's'.
+ return Number(getComputedStyle(element)[property].slice(0,-1))*1000;
+}
diff --git a/testing/web-platform/tests/html/semantics/popovers/resources/popover-invoking-attribute.js b/testing/web-platform/tests/html/semantics/popovers/resources/popover-invoking-attribute.js
new file mode 100644
index 0000000000..d2911647e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/resources/popover-invoking-attribute.js
@@ -0,0 +1,122 @@
+const actionReflectionLogic = (action) => {
+ switch (action?.toLowerCase()) {
+ case "show": return "show";
+ case "hide": return "hide";
+ default: return "toggle";
+ }
+}
+const noActivationLogic = (action) => {
+ return "none";
+}
+function makeElementWithType(element,type) {
+ return (test) => {
+ const el = Object.assign(document.createElement(element),{type});
+ document.body.appendChild(el);
+ test.add_cleanup(() => el.remove());
+ return el;
+ };
+}
+const supportedButtonTypes = ['button','reset','submit',''].map(type => {
+ return {
+ name: `<button type="${type}">`,
+ makeElement: makeElementWithType('button',type),
+ invokeFn: el => {el.focus(); el.click()},
+ getExpectedLogic: actionReflectionLogic,
+ };
+});
+const supportedInputButtonTypes = ['button','reset','submit','image'].map(type => {
+ return {
+ name: `<input type="${type}">`,
+ makeElement: makeElementWithType('input',type),
+ invokeFn: el => {el.focus(); el.click()},
+ getExpectedLogic: actionReflectionLogic,
+ };
+});
+const unsupportedTypes = ['text','email','password','search','tel','url','checkbox','radio','range','file','color','date','datetime-local','month','time','week','number'].map(type => {
+ return {
+ name: `<input type="${type}">`,
+ makeElement: makeElementWithType('input',type),
+ invokeFn: (el) => {el.focus();},
+ getExpectedLogic: noActivationLogic, // None of these support popover invocation
+ };
+});
+const invokers = [
+ ...supportedButtonTypes,
+ ...supportedInputButtonTypes,
+ ...unsupportedTypes,
+];
+function runPopoverInvokerTests(popoverTypes) {
+ window.addEventListener('load', () => {
+ popoverTypes.forEach(type => {
+ invokers.forEach(testcase => {
+ ["toggle","hide","show","ShOw","garbage",null,undefined].forEach(action => {
+ [false,true].forEach(use_idl_for_target => {
+ [false,true].forEach(use_idl_for_action => {
+ promise_test(async test => {
+ const popover = Object.assign(document.createElement('div'),{popover: type, id: 'my-popover'});
+ assert_equals(popover.popover,type,'reflection');
+ const invoker = testcase.makeElement(test);
+ if (use_idl_for_target) {
+ invoker.popoverTargetElement = popover;
+ assert_equals(invoker.getAttribute('popovertarget'),'','attribute value');
+ } else {
+ invoker.setAttribute('popovertarget',popover.id);
+ }
+ if (use_idl_for_action) {
+ invoker.popoverTargetAction = action;
+ assert_equals(invoker.getAttribute('popovertargetaction'),String(action),'action reflection');
+ } else {
+ invoker.setAttribute('popovertargetaction',action);
+ }
+ assert_true(!document.getElementById(popover.id));
+ assert_equals(invoker.popoverTargetElement,null,'targetElement should be null before the popover is in the document');
+ assert_equals(invoker.popoverTargetAction,actionReflectionLogic(action),'action should be correct immediately');
+ document.body.appendChild(popover);
+ test.add_cleanup(() => {popover.remove();});
+ assert_equals(invoker.popoverTargetElement,popover,'target element should be returned once it\'s in the document');
+ assert_false(popover.matches(':popover-open'));
+ await testcase.invokeFn(invoker);
+ assert_equals(document.activeElement,invoker,'Focus should end up on the invoker');
+ expectedBehavior = testcase.getExpectedLogic(action);
+ switch (expectedBehavior) {
+ case "toggle":
+ case "show":
+ assert_true(popover.matches(':popover-open'),'Toggle or show should show the popover');
+ popover.hidePopover(); // Hide the popover
+ break;
+ case "hide":
+ case "none":
+ assert_false(popover.matches(':popover-open'),'Hide or none should leave the popover hidden');
+ break;
+ default:
+ assert_unreached();
+ }
+ if (expectedBehavior === "none") {
+ // If no behavior is expected, then there is nothing left to test. Even re-focusing
+ // a control that has no expected behavior may hide an open popover via light dismiss.
+ return;
+ }
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover(); // Show the popover directly
+ assert_equals(document.activeElement,invoker,'The popover should not shift focus');
+ assert_true(popover.matches(':popover-open'));
+ await testcase.invokeFn(invoker);
+ switch (expectedBehavior) {
+ case "toggle":
+ case "hide":
+ assert_false(popover.matches(':popover-open'),'Toggle or hide should hide the popover');
+ break;
+ case "show":
+ assert_true(popover.matches(':popover-open'),'Show should leave the popover showing');
+ break;
+ default:
+ assert_unreached();
+ }
+ },`Test ${testcase.name}, action=${action}, ${use_idl_for_target ? "popoverTarget IDL" : "popovertarget attr"}, ${use_idl_for_action ? "popoverTargetAction IDL" : "popovertargetaction attr"}, with popover=${type}`);
+ });
+ });
+ });
+ });
+ });
+ });
+}
diff --git a/testing/web-platform/tests/html/semantics/popovers/resources/popover-styles.css b/testing/web-platform/tests/html/semantics/popovers/resources/popover-styles.css
new file mode 100644
index 0000000000..df683c3c64
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/resources/popover-styles.css
@@ -0,0 +1,17 @@
+.fake-popover {
+ position: fixed;
+ inset: 0;
+ width: fit-content;
+ height: fit-content;
+ margin: auto;
+ border: solid;
+ padding: 0.25em;
+ overflow: auto;
+ color: CanvasText;
+ background-color: Canvas;
+}
+.fake-popover-backdrop {
+ position: fixed;
+ inset:0;
+ pointer-events: none !important;
+}
diff --git a/testing/web-platform/tests/html/semantics/popovers/resources/popover-top-layer-nesting.js b/testing/web-platform/tests/html/semantics/popovers/resources/popover-top-layer-nesting.js
new file mode 100644
index 0000000000..ace10b3f7b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/resources/popover-top-layer-nesting.js
@@ -0,0 +1,108 @@
+function createTopLayerElement(t,topLayerType) {
+ let element, show, showing;
+ switch (topLayerType) {
+ case 'dialog':
+ element = document.createElement('dialog');
+ show = () => element.showModal();
+ showing = () => element.matches(':modal');
+ break;
+ case 'fullscreen':
+ element = document.createElement('div');
+ show = async (topmostElement) => {
+ // Be sure to add user activation to the topmost visible target:
+ await blessTopLayer(topmostElement);
+ await element.requestFullscreen();
+ };
+ showing = () => document.fullscreenElement === element;
+ break;
+ default:
+ assert_unreached('Invalid top layer type');
+ }
+ t.add_cleanup(() => element.remove());
+ return {element,show,showing};
+}
+function runTopLayerTests(testCases, testAnchorAttribute) {
+ testAnchorAttribute = testAnchorAttribute || false;
+ testCases.forEach(test => {
+ const description = test.firstChild.data.trim();
+ assert_equals(test.querySelectorAll('.target').length,1,'There should be exactly one target');
+ const target = test.querySelector('.target');
+ assert_true(!!target,'Invalid test case');
+ const popovers = Array.from(test.querySelectorAll('[popover]'));
+ assert_true(popovers.length > 0,'No popovers found');
+ ['dialog','fullscreen'].forEach(topLayerType => {
+ promise_test(async t => {
+ const {element,show,showing} = createTopLayerElement(t,topLayerType);
+ target.appendChild(element);
+
+ // Show the popovers.
+ t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover()));
+ popovers.forEach(popover => popover.showPopover());
+ popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open'));
+
+ // Activate the top layer element.
+ await show(popovers[popovers.length-1]);
+ assert_true(showing());
+ popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Incorrect behavior'));
+
+ // Add another popover within the top layer element and make sure entire stack stays open.
+ const newPopover = document.createElement('div');
+ t.add_cleanup(() => newPopover.remove());
+ newPopover.popover = popoverHintSupported() ? 'hint' : 'auto';
+ element.appendChild(newPopover);
+ popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Adding another popover shouldn\'t change anything'));
+ assert_true(showing(),'top layer element should still be top layer');
+ newPopover.showPopover();
+ assert_true(newPopover.matches(':popover-open'));
+ popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Showing the popover shouldn\'t change anything'));
+ assert_true(showing(),'top layer element should still be top layer');
+ },`${description} with ${topLayerType}`);
+
+ promise_test(async t => {
+ const {element,show,showing} = createTopLayerElement(t,topLayerType);
+ element.popover = popoverHintSupported() ? 'hint' : 'auto';
+ target.appendChild(element);
+
+ // Show the popovers.
+ t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover()));
+ popovers.forEach(popover => popover.showPopover());
+ popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open'));
+ const targetWasOpenPopover = target.matches(':popover-open');
+
+ // Show the top layer element as a popover first.
+ element.showPopover();
+ assert_true(element.matches(':popover-open'),'element should be open as a popover');
+ assert_equals(target.matches(':popover-open'),targetWasOpenPopover,'target shouldn\'t change popover state');
+
+ try {
+ await show(element);
+ assert_unreached('It is an error to activate a top layer element that is already a showing popover');
+ } catch (e) {
+ // We expect an InvalidStateError for dialogs, and a TypeError for fullscreens.
+ // Anything else should fall through to the test harness.
+ if (e.name !== 'InvalidStateError' && e.name !== 'TypeError') {
+ throw e;
+ }
+ }
+ },`${description} with ${topLayerType}, top layer element *is* a popover`);
+
+ if (testAnchorAttribute) {
+ promise_test(async t => {
+ const {element,show,showing} = createTopLayerElement(t,topLayerType);
+ element.anchorElement = target;
+ document.body.appendChild(element);
+
+ // Show the popovers.
+ t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover()));
+ popovers.forEach(popover => popover.showPopover());
+ popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open'));
+
+ // Activate the top layer element.
+ await show(popovers[popovers.length-1]);
+ assert_true(showing());
+ popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Incorrect behavior'));
+ },`${description} with ${topLayerType}, anchor attribute`);
+ }
+ });
+ });
+}
diff --git a/testing/web-platform/tests/html/semantics/popovers/resources/popover-utils.js b/testing/web-platform/tests/html/semantics/popovers/resources/popover-utils.js
new file mode 100644
index 0000000000..bfc1f89ec1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/resources/popover-utils.js
@@ -0,0 +1,176 @@
+function waitForRender() {
+ return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+}
+
+function waitForTick() {
+ return new Promise(resolve => step_timeout(resolve, 0));
+}
+
+async function clickOn(element) {
+ const actions = new test_driver.Actions();
+ await waitForRender();
+ await actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ await waitForRender();
+}
+async function sendTab() {
+ await waitForRender();
+ const kTab = '\uE004';
+ await new test_driver.send_keys(document.activeElement || document.documentElement, kTab);
+ await waitForRender();
+}
+async function sendShiftTab() {
+ await waitForRender();
+ const kShift = '\uE008';
+ const kTab = '\uE004';
+ await new test_driver.Actions()
+ .keyDown(kShift)
+ .keyDown(kTab)
+ .keyUp(kTab)
+ .keyUp(kShift)
+ .send();
+ await waitForRender();
+}
+async function sendEscape() {
+ await waitForRender();
+ await new test_driver.send_keys(document.activeElement || document.documentElement,'\uE00C'); // Escape
+ await waitForRender();
+}
+async function sendEnter() {
+ await waitForRender();
+ await new test_driver.send_keys(document.activeElement || document.documentElement,'\uE007'); // Enter
+ await waitForRender();
+}
+function isElementVisible(el) {
+ return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
+}
+async function finishAnimations(popover) {
+ popover.getAnimations({subtree: true}).forEach(animation => animation.finish());
+ await waitForRender();
+}
+let mousemoveInfo;
+function mouseOver(element) {
+ mousemoveInfo?.controller?.abort();
+ const controller = new AbortController();
+ mousemoveInfo = {element, controller, moved: false, started: performance.now()};
+ return (new test_driver.Actions())
+ .pointerMove(0, 0, {origin: element})
+ .send()
+ .then(() => {
+ document.addEventListener("mousemove", (e) => {mousemoveInfo.moved = true;}, {signal: controller.signal});
+ })
+}
+function msSinceMouseOver() {
+ return performance.now() - mousemoveInfo.started;
+}
+function assertMouseStillOver(element) {
+ assert_equals(mousemoveInfo.element, element, 'Broken test harness');
+ assert_false(mousemoveInfo.moved,'Broken test harness');
+}
+async function waitForHoverTime(hoverWaitTimeMs) {
+ await new Promise(resolve => step_timeout(resolve,hoverWaitTimeMs));
+ await waitForRender();
+};
+async function mouseHover(element,hoverWaitTimeMs) {
+ await mouseOver(element);
+ await waitForHoverTime(hoverWaitTimeMs);
+ assertMouseStillOver(element);
+}
+
+// This is a "polyfill" of sorts for the `defaultopen` attribute.
+// It can be called before window.load is complete, and it will
+// show defaultopen popovers according to the rules previously part
+// of the popover API: any popover=manual popover can be shown this
+// way, and only the first popover=auto popover.
+function showDefaultopenPopoversOnLoad() {
+ function show() {
+ const popovers = Array.from(document.querySelectorAll('[popover][defaultopen]'));
+ popovers.forEach((p) => {
+ // The showPopover calls below aren't guarded by a check on the popover
+ // open/closed status. If they throw exceptions, this function was
+ // probably called at a bad time. However, a check is made for open
+ // <dialog open> elements.
+ if (p instanceof HTMLDialogElement && p.hasAttribute('open'))
+ return;
+ switch (p.popover) {
+ case 'auto':
+ if (!document.querySelector('[popover]:popover-open'))
+ p.showPopover();
+ return;
+ case 'manual':
+ p.showPopover();
+ return;
+ default:
+ assert_unreached(`Unknown popover type ${p.popover}`);
+ }
+ });
+ }
+ if (document.readyState === 'complete') {
+ show();
+ } else {
+ window.addEventListener('load',show,{once:true});
+ }
+}
+function popoverHintSupported() {
+ // TODO(crbug.com/1416284): This function should be removed, and
+ // any calls replaced with `true`, once popover=hint ships.
+ const testElement = document.createElement('div');
+ testElement.popover = 'hint';
+ return testElement.popover === 'hint';
+}
+
+function assertPopoverVisibility(popover, isPopover, expectedVisibility, message) {
+ const isVisible = isElementVisible(popover);
+ assert_equals(isVisible, expectedVisibility,`${message}: Expected this element to be ${expectedVisibility ? "visible" : "not visible"}`);
+ // Check other things related to being visible or not:
+ if (isVisible) {
+ assert_not_equals(window.getComputedStyle(popover).display,'none');
+ assert_equals(popover.matches(':popover-open'),isPopover,`${message}: Visible popovers should match :popover-open`);
+ } else {
+ assert_equals(window.getComputedStyle(popover).display,'none',`${message}: Non-showing popovers should have display:none`);
+ assert_false(popover.matches(':popover-open'),`${message}: Non-showing popovers should *not* match :popover-open`);
+ }
+}
+
+function assertIsFunctionalPopover(popover, checkVisibility) {
+ assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'A popover should start out hidden');
+ popover.showPopover();
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After showPopover(), a popover should be visible');
+ popover.showPopover(); // Calling showPopover on a showing popover should not throw.
+ popover.hidePopover();
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After hidePopover(), a popover should be hidden');
+ popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
+ popover.togglePopover();
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover() on hidden popover, it should be visible');
+ popover.togglePopover();
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover() on visible popover, it should be hidden');
+ popover.togglePopover(/*force=*/true);
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on hidden popover, it should be visible');
+ popover.togglePopover(/*force=*/true);
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/true, 'After togglePopover(true) on visible popover, it should be visible');
+ popover.togglePopover(/*force=*/false);
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on visible popover, it should be hidden');
+ popover.togglePopover(/*force=*/false);
+ if (checkVisibility) assertPopoverVisibility(popover, /*isPopover*/true, /*expectedVisibility*/false, 'After togglePopover(false) on hidden popover, it should be hidden');
+ const parent = popover.parentElement;
+ popover.remove();
+ assert_throws_dom("InvalidStateError",() => popover.showPopover(),'Calling showPopover on a disconnected popover should throw InvalidStateError');
+ popover.hidePopover(); // Calling hidePopover on a disconnected popover should not throw.
+ assert_throws_dom("InvalidStateError",() => popover.togglePopover(),'Calling hidePopover on a disconnected popover should throw InvalidStateError');
+ parent.appendChild(popover);
+}
+
+function assertNotAPopover(nonPopover) {
+ // If the non-popover element nonetheless has a 'popover' attribute, it should
+ // be invisible. Otherwise, it should be visible.
+ const expectVisible = !nonPopover.hasAttribute('popover');
+ assertPopoverVisibility(nonPopover, /*isPopover*/false, expectVisible, 'A non-popover should start out visible');
+ assert_throws_dom("NotSupportedError",() => nonPopover.showPopover(),'Calling showPopover on a non-popover should throw NotSupported');
+ assertPopoverVisibility(nonPopover, /*isPopover*/false, expectVisible, 'Calling showPopover on a non-popover should leave it visible');
+ assert_throws_dom("NotSupportedError",() => nonPopover.hidePopover(),'Calling hidePopover on a non-popover should throw NotSupported');
+ assertPopoverVisibility(nonPopover, /*isPopover*/false, expectVisible, 'Calling hidePopover on a non-popover should leave it visible');
+ assert_throws_dom("NotSupportedError",() => nonPopover.togglePopover(),'Calling togglePopover on a non-popover should throw NotSupported');
+ assertPopoverVisibility(nonPopover, /*isPopover*/false, expectVisible, 'Calling togglePopover on a non-popover should leave it visible');
+}
diff --git a/testing/web-platform/tests/html/semantics/popovers/togglePopover.html b/testing/web-platform/tests/html/semantics/popovers/togglePopover.html
new file mode 100644
index 0000000000..388617fccc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/togglePopover.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9043">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=popover popover=auto>popover</div>
+<div id=popover2 popover=auto>popover</div>
+
+<script>
+test(() => {
+ assert_false(popover.matches(':popover-open'),
+ 'Popover should be closed when the test starts.');
+
+ assert_true(popover.togglePopover(),
+ 'togglePopover() should return true.');
+ assert_true(popover.matches(':popover-open'),
+ 'togglePopover() should open the popover.');
+
+ assert_true(popover.togglePopover(/*force=*/true),
+ 'togglePopover(true) should return true.');
+ assert_true(popover.matches(':popover-open'),
+ 'togglePopover(true) should open the popover.');
+
+ assert_false(popover.togglePopover(),
+ 'togglePopover() should return false.');
+ assert_false(popover.matches(':popover-open'),
+ 'togglePopover() should close the popover.');
+
+ assert_false(popover.togglePopover(/*force=*/false),
+ 'togglePopover(false) should return false.');
+ assert_false(popover.matches(':popover-open'),
+ 'togglePopover(false) should close the popover.');
+}, 'togglePopover should toggle the popover and return true or false as specified.');
+
+test(() => {
+ const popover = document.getElementById('popover2');
+ popover.addEventListener('beforetoggle', event => event.preventDefault(), {once: true});
+ assert_false(popover.togglePopover(/*force=*/true),
+ 'togglePopover(true) should return false when the popover does not get opened.');
+ assert_false(popover.matches(':popover-open'));
+
+ // We could also add a test for the return value of togglePopover(false),
+ // but every way to prevent that from hiding the popover also throws an
+ // exception, so the return value is not testable.
+}, `togglePopover's return value should reflect what the end state is, not just the force parameter.`);
+
+test(() => {
+ const popover = document.createElement('div');
+ document.body.appendChild(popover);
+
+ assert_throws_dom('NotSupportedError', () => popover.togglePopover(),
+ 'togglePopover() should throw an exception when the element has no popover attribute.');
+ assert_throws_dom('NotSupportedError', () => popover.togglePopover(true),
+ 'togglePopover(true) should throw an exception when the element has no popover attribute.');
+ assert_throws_dom('NotSupportedError', () => popover.togglePopover(false),
+ 'togglePopover(false) should throw an exception when the element has no popover attribute.');
+
+ popover.setAttribute('popover', 'auto');
+ popover.remove();
+
+ assert_throws_dom('InvalidStateError', () => popover.togglePopover(),
+ 'togglePopover() should throw an exception when the element is disconnected.');
+ assert_throws_dom('InvalidStateError', () => popover.togglePopover(true),
+ 'togglePopover(true) should throw an exception when the element is disconnected.');
+ assert_throws_dom('InvalidStateError', () => popover.togglePopover(false),
+ 'togglePopover(false) should throw an exception when the element is disconnected.');
+
+ document.body.appendChild(popover);
+ // togglePopover(false) should not throw just because the popover is already hidden.
+ popover.togglePopover(false);
+ popover.showPopover();
+ // togglePopover(true) should not throw just because the popover is already showing.
+ popover.togglePopover(true);
+
+ // cleanup
+ popover.hidePopover();
+}, 'togglePopover should throw an exception when there is no popover attribute.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/popovers/toggleevent-interface.html b/testing/web-platform/tests/html/semantics/popovers/toggleevent-interface.html
new file mode 100644
index 0000000000..09ce3f3b56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/toggleevent-interface.html
@@ -0,0 +1,208 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+test(function() {
+ var event = new ToggleEvent("");
+ assert_true(event instanceof window.ToggleEvent);
+}, "the event is an instance of ToggleEvent");
+
+test(function() {
+ var event = new ToggleEvent("");
+ assert_true(event instanceof window.Event);
+}, "the event inherts from Event");
+
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new ToggleEvent();
+ }, 'First argument (type) is required, so was expecting a TypeError.');
+}, 'Missing type argument');
+
+test(function() {
+ var event = new ToggleEvent("test");
+ assert_equals(event.type, "test");
+}, "type argument is string");
+
+test(function() {
+ var event = new ToggleEvent(null);
+ assert_equals(event.type, "null");
+}, "type argument is null");
+
+test(function() {
+ var event = new ToggleEvent(undefined);
+ assert_equals(event.type, "undefined");
+}, "event type set to undefined");
+
+test(function() {
+ var event = new ToggleEvent("test");
+ assert_equals(event.oldState, "");
+}, "oldState has default value of empty string");
+
+test(function() {
+ var event = new ToggleEvent("test");
+ assert_readonly(event, "oldState", "readonly attribute value");
+}, "oldState is readonly");
+
+test(function() {
+ var event = new ToggleEvent("test");
+ assert_equals(event.newState, "");
+}, "newState has default value of empty string");
+
+test(function() {
+ var event = new ToggleEvent("test");
+ assert_readonly(event, "newState", "readonly attribute value");
+}, "newState is readonly");
+
+test(function() {
+ var event = new ToggleEvent("test", null);
+ assert_equals(event.oldState, "");
+ assert_equals(event.newState, "");
+}, "ToggleEventInit argument is null");
+
+test(function() {
+ var event = new ToggleEvent("test", undefined);
+ assert_equals(event.oldState, "");
+ assert_equals(event.newState, "");
+}, "ToggleEventInit argument is undefined");
+
+test(function() {
+ var event = new ToggleEvent("test", {});
+ assert_equals(event.oldState, "");
+ assert_equals(event.newState, "");
+}, "ToggleEventInit argument is empty dictionary");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: "sample"});
+ assert_equals(event.oldState, "sample");
+}, "oldState set to 'sample'");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: undefined});
+ assert_equals(event.oldState, "");
+}, "oldState set to undefined");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: null});
+ assert_equals(event.oldState, "null");
+}, "oldState set to null");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: false});
+ assert_equals(event.oldState, "false");
+}, "oldState set to false");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: true});
+ assert_equals(event.oldState, "true");
+}, "oldState set to true");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: 0.5});
+ assert_equals(event.oldState, "0.5");
+}, "oldState set to a number");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: []});
+ assert_equals(event.oldState, "");
+}, "oldState set to []");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: [1, 2, 3]});
+ assert_equals(event.oldState, "1,2,3");
+}, "oldState set to [1, 2, 3]");
+
+test(function() {
+ var event = new ToggleEvent("test", {oldState: {sample: 0.5}});
+ assert_equals(event.oldState, "[object Object]");
+}, "oldState set to an object");
+
+test(function() {
+ var event = new ToggleEvent("test",
+ {oldState: {valueOf: function () { return 'sample'; }}});
+ assert_equals(event.oldState, "[object Object]");
+}, "oldState set to an object with a valueOf function");
+
+test(function() {
+ var eventInit = {oldState: "sample",newState: "sample2"};
+ var event = new ToggleEvent("test", eventInit);
+ assert_equals(event.oldState, "sample");
+ assert_equals(event.newState, "sample2");
+}, "ToggleEventInit properties set value");
+
+test(function() {
+ var eventInit = {oldState: "open",newState: "closed"};
+ var event = new ToggleEvent("beforetoggle", eventInit);
+ assert_equals(event.oldState, "open");
+ assert_equals(event.newState, "closed");
+}, "ToggleEventInit properties set value 2");
+
+test(function() {
+ var eventInit = {oldState: "closed",newState: "open"};
+ var event = new ToggleEvent("toggle", eventInit);
+ assert_equals(event.oldState, "closed");
+ assert_equals(event.newState, "open");
+}, "ToggleEventInit properties set value 3");
+
+test(function() {
+ var eventInit = {oldState: "open",newState: "open"};
+ var event = new ToggleEvent("beforetoggle", eventInit);
+ assert_equals(event.oldState, "open");
+ assert_equals(event.newState, "open");
+}, "ToggleEventInit properties set value 4");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: "sample"});
+ assert_equals(event.newState, "sample");
+}, "newState set to 'sample'");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: undefined});
+ assert_equals(event.newState, "");
+}, "newState set to undefined");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: null});
+ assert_equals(event.newState, "null");
+}, "newState set to null");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: false});
+ assert_equals(event.newState, "false");
+}, "newState set to false");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: true});
+ assert_equals(event.newState, "true");
+}, "newState set to true");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: 0.5});
+ assert_equals(event.newState, "0.5");
+}, "newState set to a number");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: []});
+ assert_equals(event.newState, "");
+}, "newState set to []");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: [1, 2, 3]});
+ assert_equals(event.newState, "1,2,3");
+}, "newState set to [1, 2, 3]");
+
+test(function() {
+ var event = new ToggleEvent("test", {newState: {sample: 0.5}});
+ assert_equals(event.newState, "[object Object]");
+}, "newState set to an object");
+
+test(function() {
+ var event = new ToggleEvent("test",
+ {newState: {valueOf: function () { return 'sample'; }}});
+ assert_equals(event.newState, "[object Object]");
+}, "newState set to an object with a valueOf function");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/rellist-feature-detection.html b/testing/web-platform/tests/html/semantics/rellist-feature-detection.html
new file mode 100644
index 0000000000..6866de1906
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/rellist-feature-detection.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<title>Test relList attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+let link_support_table = {};
+// https://html.spec.whatwg.org/multipage/links.html#linkTypes
+link_support_table['link'] = {
+ supported : ['modulepreload', 'preload', 'preconnect', 'dns-prefetch',
+ 'stylesheet', 'icon', 'alternate', 'prefetch',
+ 'prerender', 'next', 'manifest', 'apple-touch-icon',
+ 'apple-touch-icon-precomposed', 'canonical'],
+ unsupported : ['author', 'bookmark', 'external', 'help', 'import',
+ 'license', 'nofollow', 'pingback', 'prev', 'search',
+ 'tag', 'noreferrer', 'noopener']
+};
+link_support_table['a'] = {
+ supported : ['noreferrer', 'noopener', 'opener'],
+ unsupported : ['author', 'bookmark', 'external', 'help', 'license',
+ 'nofollow', 'pingback', 'prev', 'search', 'tag',
+ 'modulepreload', 'preload', 'preconnect', 'dns-prefetch',
+ 'stylesheet', 'import', 'icon', 'alternate', 'prefetch',
+ 'prerender', 'next', 'manifest', 'apple-touch-icon',
+ 'apple-touch-icon-precomposed', 'canonical']
+};
+link_support_table['area'] = link_support_table['a'];
+link_support_table['form'] = link_support_table['a'];
+
+function test_rellist(tag_name) {
+ const rel_table = link_support_table[tag_name];
+ const element = document.createElement(tag_name);
+ let tag = element.tagName;
+ // Test that setting rel is also setting relList, for both
+ // valid and invalid values.
+ element.rel = 'whatever';
+ assert_true(element.relList.contains('whatever'), 'tag = ' + tag + ', setting rel must work');
+ element.rel = 'prefetch';
+ assert_true(element.relList.contains('prefetch'), 'tag = ' + tag + ', setting rel must work');
+ // Test that add() works.
+ element.relList.add('preloadwhatever');
+ assert_equals(element.rel, 'prefetch preloadwhatever', 'tag = ' + tag + ', add must work');
+ assert_true(element.relList.contains('preloadwhatever'), 'tag = ' + tag + ', add must work');
+ // Test that remove() works.
+ element.relList.remove('preloadwhatever');
+ assert_equals(element.rel, 'prefetch', 'tag = ' + tag + ', remove must work');
+ assert_false(element.relList.contains('preloadwhatever'), 'tag = ' + tag + ', remove must work');
+ // Test that toggle() works.
+ element.relList.toggle('prefetch', false);
+ assert_equals(element.rel, '', 'tag = ' + tag + ', toggle must work');
+ element.relList.toggle('prefetch', true);
+ assert_equals(element.rel, 'prefetch', 'tag = ' + tag + ', toggle must work');
+ // Test that replace() works.
+ element.relList.replace('prefetch', 'first');
+ assert_equals(element.rel, 'first', 'tag = ' + tag + ', replace must work');
+ // Test that indexed item getter works.
+ element.relList.add('second');
+ assert_equals(element.relList.length, 2, 'tag = ' + tag + ', relList length must be correct');
+ assert_equals(element.relList[0], 'first', 'tag = ' + tag + ', relList indexed item must work');
+ assert_equals(element.relList[1], 'second', 'tag = ' + tag + ', relList indexed item must work');
+ // Test that relList is [SameObject].
+ let savedRelList = element.relList;
+ element.rel = 'something';
+ assert_equals(element.relList, savedRelList, 'tag = ' + tag + ', SameObject must work');
+
+ // Test that supports() is returning true for valid values
+ // and false for invalid ones.
+ let supported = rel_table['supported'];
+ for (let link_type in supported) {
+ assert_true(element.relList.supports(supported[link_type]), 'tag = ' + tag + ', link type = ' + supported[link_type] + ' must be supported');
+ assert_true(element.relList.supports(supported[link_type].toUpperCase()), 'tag = ' + tag + ', link type = ' + supported[link_type].toUpperCase() + ' must be supported');
+ }
+ let unsupported = rel_table['unsupported'];
+ for (let link_type in unsupported) {
+ assert_false(element.relList.supports(unsupported[link_type]), 'tag = ' + tag + ', link type = ' + unsupported[link_type] + ' must be unsupported');
+ }
+}
+
+['link', 'a', 'area', 'form'].forEach(tag_name => {
+ test(
+ () => test_rellist(tag_name),
+ `Make sure that relList based feature detection is working for <${tag_name}>`
+ );
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/META.yml b/testing/web-platform/tests/html/semantics/scripting-1/META.yml
new file mode 100644
index 0000000000..0f3cf59653
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - domenic
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-noscript-element/non-html-noscript.html b/testing/web-platform/tests/html/semantics/scripting-1/the-noscript-element/non-html-noscript.html
new file mode 100644
index 0000000000..2f85d1d47d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-noscript-element/non-html-noscript.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>noscript rules don't apply to non-html elements</title>
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-noscript-element">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1470150">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+test(function() {
+ let non_html_noscript = document.createElementNS("http://www.w3.org/2000/svg", "noscript");
+ document.body.appendChild(non_html_noscript);
+ assert_not_equals(getComputedStyle(non_html_noscript).display, "none");
+}, "Non-HTML <noscript> element shouldn't be undisplayed by a UA rule");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/README.md b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/README.md
new file mode 100644
index 0000000000..ab4a4820af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/README.md
@@ -0,0 +1,16 @@
+# Script tests
+
+## Import attributes & JSON/CSS modules
+
+The import attributes proposal changed the keyword from `assert` to `with`, after that it was already been implemented and shipped in some browsers. Thus, there are some implementations that only support the `assert` syntax and others that only support the `with` syntax.
+
+For this reason, the import attributes, JSON modules and CSS modules are duplicated to use both keywords:
+| `with` keyword | `assert` keyword |
+|:----------------------|:---------------------------|
+| `./import-attributes` | `./import-assertions` |
+| `./json-module` | `./json-module-assertions` |
+| `./css-module` | `./css-module-assertions` |
+
+All changes in one folder should be reflected in the corresponding folder, because the two syntaxes have the same semantics.
+
+The web compatibility of removing the `assert` keyword is being investigated. If it will be deemed feasible, it will be removed from the proposal and the `assert` tests can be deleted.
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_001.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_001.htm
new file mode 100644
index 0000000000..370152683b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_001.htm
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Async property on a dynamically-created script is true by default</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test checks the Async property on a dynamically-created script element. By default it should be true." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-script-async"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ test(function() {assert_true(document.createElement("script").async)}, "Async property on a dynamically-created script is true by default");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_002.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_002.htm
new file mode 100644
index 0000000000..e1850ff6e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_002.htm
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Changes to the 'async' attribute are reflected in the async property</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures changes to the 'async' attribute are reflected in the async property." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-script-async"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ test(function() {
+ var s = document.createElement("script");
+ s.async = false;
+ s.setAttribute('async', ''); /*Should change s.async to true*/
+ assert_true(s.async)
+ }, "Test 'async' attribute are reflected in the async property with setAttribute");
+
+ test(function() {
+ var s = document.createElement("script");
+ s.async = false;
+ s.setAttribute('async', ''); /*Should change s.async to true*/
+ s.removeAttribute('async'); /*Should change s.async to false*/
+ assert_false(s.async)
+ }, "Test 'async' attribute are reflected in the async property with removeAttribute");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_003.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_003.htm
new file mode 100644
index 0000000000..c6d84f9a87
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_003.htm
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>An async script does not block the parser while downloading</title>
+ <meta name="timeout" content="long">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures an async script does not block the parser while downloading." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-script-async"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ var t = async_test("An async script does not block the parser while downloading");
+
+ function timeout()
+ {
+ t.step(function(){ assert_equals(document.getElementById("testresult").innerHTML, "21")});
+ t.done();
+ }
+
+ var timer = setTimeout(timeout, 4000);
+
+ function log(text)
+ {
+ var textNode = document.createTextNode(text);
+ document.getElementById("testresult").appendChild(textNode);
+ }
+ </script>
+
+ <span id="testresult"></span>
+
+ <script src="log.py?sec=3&id=1" async></script>
+ <script>
+ log('2');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_004.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_004.htm
new file mode 100644
index 0000000000..5d9df099b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_004.htm
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>An async script executes as soon as possible after a download is complete</title>
+ <meta name="timeout" content="long">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures an async script executes as soon as possible after a download is complete." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-script-async"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ var t = async_test("async script executes as soon as possible after a download is complete");
+
+ function timeout()
+ {
+ t.step(function(){ assert_equals(document.getElementById("testresult").innerHTML, "21")});
+ t.done();
+ }
+
+ var timer = setTimeout(timeout, 4000);
+
+ function log(text)
+ {
+ var textNode = document.createTextNode(text);
+ document.getElementById("testresult").appendChild(textNode);
+ }
+ </script>
+
+ <span id="testresult"></span>
+
+ <script src="log.py?sec=3&id=1" async></script>
+ <script src="log.py?sec=1&id=2" async></script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_005.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_005.htm
new file mode 100644
index 0000000000..03f9adeb67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_005.htm
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>A script element with both async and defer set should execute asynchronously</title>
+ <meta name="timeout" content="long">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures a script element with both async and defer set should execute asynchronously." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-script-async"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ var t = async_test("A script element with both async and defer set should execute asynchronously");
+
+ function timeout()
+ {
+ t.step(function(){
+ var actual = document.getElementById("testresult").innerHTML;
+ assert_in_array(actual, ["2134", "2341"]);
+ });
+ t.done();
+ }
+
+ var timer = setTimeout(timeout, 5000);
+
+ function log(text)
+ {
+ var textNode = document.createTextNode(text);
+ document.getElementById("testresult").appendChild(textNode);
+ }
+ </script>
+
+ <span id="testresult"></span>
+
+ <script type="text/javascript" src="log.py?sec=1&id=1" defer async></script>
+ <script type="text/javascript">log('2');</script>
+ <script type="text/javascript" src="log.py?sec=3&id=3"></script>
+ <script type="text/javascript">log('4');</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_006.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_006.htm
new file mode 100644
index 0000000000..ed3d8b22c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_006.htm
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>A dynamically created external script executes asynchronously</title>
+ <meta name="timeout" content="long">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures a dynamically created external script executes asynchronously." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#force-async"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ var t = async_test("dynamically created external script executes asynchronously");
+
+ function timeout()
+ {
+ t.step(function(){ assert_equals(document.getElementById("testresult").innerHTML, "321")});
+ t.done();
+ }
+
+ var timer = setTimeout(timeout, 4000);
+
+ function log(text)
+ {
+ var textNode = document.createTextNode(text);
+ document.getElementById("testresult").appendChild(textNode);
+ }
+ </script>
+
+ <span id="testresult"></span>
+ <script type="text/javascript">
+ var one = document.createElement("script");
+ one.src="log.py?sec=3&id=1";
+ document.head.appendChild(one);
+
+ var two = document.createElement("script");
+ two.src="log.py?sec=1&id=2";
+ document.head.appendChild(two);
+
+ log('3');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_007.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_007.htm
new file mode 100644
index 0000000000..6c4ae29e06
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_007.htm
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Ordered async script execution when script.async == false</title>
+ <meta name="timeout" content="long">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures Ordered async script execution when script.async == false" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#script-processing-src-sync"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ var t = async_test("Ordered async script execution when script.async == false");
+
+ function timeout()
+ {
+ t.step(function(){ assert_equals(document.getElementById("testresult").innerHTML, "312")});
+ t.done();
+ }
+
+ var timer = setTimeout(timeout, 8000);
+
+ function log(text)
+ {
+ var textNode = document.createTextNode(text);
+ document.getElementById("testresult").appendChild(textNode);
+ }
+ </script>
+
+ <span id="testresult"></span>
+ <script type="text/javascript">
+ var one = document.createElement("script");
+ one.src="log.py?sec=3&id=1";
+ one.async = false;
+ document.head.appendChild(one);
+
+ var two = document.createElement("script");
+ two.src="log.py?sec=1&id=2";
+ two.async = false;
+ document.head.appendChild(two);
+ </script>
+ <script type="text/javascript">
+ log('3');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_008.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_008.htm
new file mode 100644
index 0000000000..73529cc318
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_008.htm
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Async script element execution delays the window's load event</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures an async script element's execution delays the window's load event." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#delay-the-load-event"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript">
+ var t = async_test("Async script element execution delays the window's load event");
+
+ function timeout()
+ {
+ t.step(function(){ assert_equals(document.getElementById("testresult").innerHTML, "213")});
+ t.done();
+ }
+
+ var timer = setTimeout(timeout, 8000);
+
+ function log(text)
+ {
+ var textNode = document.createTextNode(text);
+ document.getElementById("testresult").appendChild(textNode);
+ }
+ </script>
+
+ <span id="testresult"></span>
+ <script type="text/javascript">
+ window.addEventListener("load", function() {
+ log("3");
+ timeout();
+ }, false);
+
+ var s1 = document.createElement("script");
+ s1.src = "log.py?sec=2&id=1";
+ document.head.appendChild(s1);
+ </script>
+ <script type="text/javascript">
+ log('2');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_009.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_009.htm
new file mode 100644
index 0000000000..307aa46412
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_009.htm
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Document.write() silently fails from an Async script</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures Document.write() silently fails from an Async script." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+ var t = async_test("Document.write() silently fails from an Async script");
+
+ var log = t.step_func(function() {
+ document.write("<span id='writtenText'></span>");
+ assert_equals(null, document.getElementById('writtenText'));
+ t.done();
+ });
+ </script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script type="text/javascript" src="log.py?sec=1&id=1" async></script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_010.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_010.htm
new file mode 100644
index 0000000000..c54defc00d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_010.htm
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Removing an async script before execution</title>
+ <meta name="timeout" content="long">
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="This test ensures that an async script still executes if it is removed from a markup before the download is complete. The other two scripts that come after it in insertion order should execute as well." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+ var t = async_test("Removing an async script before execution");
+
+ function timeout()
+ {
+ t.step(function(){ assert_equals(document.getElementById("testresult").innerHTML, "4123")});
+ t.done();
+ }
+
+ var timer = setTimeout(timeout, 8000);
+
+ function log(text)
+ {
+ var textNode = document.createTextNode(text);
+ document.getElementById("testresult").appendChild(textNode);
+ }
+ </script>
+ </head>
+ <body>
+ <div id=log></div>
+ <span id="testresult"></span>
+ <script type="text/javascript">
+ var s1 = document.createElement("script");
+ s1.src="log.py?sec=2&id=1";
+ s1.async = false;
+ document.body.appendChild(s1);
+
+ var s2 = document.createElement("script");
+ s2.src="log.py?sec=1&id=2";
+ s2.async = false;
+ document.body.appendChild(s2);
+
+ var s3 = document.createElement("script");
+ s3.id = "s3";
+ s3.src="log.py?sec=0&id=3";
+ s3.async = false;
+ document.body.appendChild(s3);
+
+ //Remove s1 (Should still execute)
+ document.body.removeChild(s1);
+ </script>
+ <script type="text/javascript">log('4');</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_011.htm b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_011.htm
new file mode 100644
index 0000000000..d80e463cee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/async_011.htm
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>An empty parser-inserted script element should return async=true</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta description="An empty parser-inserted script element should return async=true." />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script></script>
+ <script type="text/javascript">
+ test(function() { assert_true(document.getElementsByTagName("script")[2].async)}, "An empty parser-inserted script element should return async=true");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py
new file mode 100644
index 0000000000..b315afed56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript"), (b"Cache-control", b"public, max-age=100")]
+ body = u"throw('fox');"
+ return 200, headers, body
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-2.html
new file mode 100644
index 0000000000..aaa2960aaf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-2.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="windows-1250">
+<title>CSS modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/utf-8.css" assert { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"�湿�\"");
+ }, "CSS module should be loaded as utf-8 even though document's encoding is windows-1250");
+</script>
+<script type="module">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/windows-1250.css&ct=text/css%3Bcharset=windows-1250" assert { type: "css"};
+ test(() => {
+ assert_not_equals(styleSheet.rules[0].style.content, "\"�湿�\"",
+ 'Should be decoded as UTF-8');
+ }, "CSS module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header, and this document's encoding is windows-1250");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-bom.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-bom.html
new file mode 100644
index 0000000000..e26ee08d31
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset-bom.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>CSS Module scripts should ignore BOMs and always use UTF-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module">
+ import utf8BOMSheet from './resources/bom-utf-8.css' assert { type: 'css' };
+ test(function() {
+ assert_equals(utf8BOMSheet.rules[0].selectorText, 'div', 'No UTF-8 BOM expected in selector');
+ }, 'UTF-8 BOM should be stripped when decoding JSON module script');
+
+ import utf16BEBOMSheet from './resources/bom-utf-16be.css' assert { type: 'css' };
+ test(function() {
+ assert_equals(utf16BEBOMSheet.rules[0].selectorText, '\ufffd\ufffd\ufffdd\ufffdi\ufffdv\ufffd \ufffd', 'Expected UTF-8 decoded selectorText with 0xfffd replacement characters');
+ }, 'UTF-16BE BOM should be ignored, so CSS module should be UTF-8 decoded');
+
+ import utf16LEBOMSheet from './resources/bom-utf-16le.css' assert { type: 'css' };
+ test(function() {
+ assert_equals(utf16LEBOMSheet.rules[0].selectorText, '\ufffd\ufffdd\ufffdi\ufffdv\ufffd \ufffd', 'Expected UTF-8 decoded selectorText with 0xfffd replacement characters');
+ }, 'UTF-16LE BOM should be ignored, so CSS module should be UTF-8 decoded');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset.html
new file mode 100644
index 0000000000..2127ec7280
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/charset.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CSS modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/utf-8.css&ct=text/css%3Bcharset=utf-8" assert { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=utf8 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/utf-8.css&ct=text/css%3Bcharset=shift-jis" assert { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=shift-jis is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/utf-8.css&ct=text/css%3Bcharset=windows-1252" assert { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=windows-1252 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/utf-8.css&ct=text/css%3Bcharset=utf-7" assert { type: "css"};;
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=utf-7 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/windows-1250.css&ct=text/css%3Bcharset=windows-1250" assert { type: "css"};
+ test(() => {
+ assert_not_equals(styleSheet.rules[0].style.content, "\"śćążź\"",
+ 'Should be decoded as UTF-8');
+ }, "CSS module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/content-type-checking.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/content-type-checking.html
new file mode 100644
index 0000000000..3ade863305
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/content-type-checking.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CSS modules: Content-Type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function check(t, styleSheet) {
+ t.step(() => {
+ assert_equals(styleSheet.rules[0].cssText, "#test { background-color: rgb(255, 0, 0); }");
+ t.done();
+ });
+}
+const t1 = async_test("text/css");
+const t2 = async_test("application/css");
+const t3 = async_test("text/html+css");
+const t4 = async_test("text/css;boundary=something");
+const t5 = async_test("text/css;foo=bar");
+</script>
+<script type="module" onerror="t1.unreached_func()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/basic.css&ct=text/css" assert { type: "css"};
+ check(t1, styleSheet);
+</script>
+<script type="module" onerror="t2.step_func_done()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/basic.css&ct=application/css" assert { type: "css"};
+ t2.unreached_func("Should not have loaded with MIME type application/css")();
+</script>
+<script type="module" onerror="t3.step_func_done()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/basic.css&ct=text/html+css" assert { type: "css"};
+ t3.unreached_func("Should not have loaded with MIME type text/html+css")();
+</script>
+<script type="module" onerror="t4.unreached_func()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/basic.css&ct=text/css;boundary=something" assert { type: "css"};
+ check(t4, styleSheet);
+</script>
+<script type="module" onerror="t5.unreached_func()()">
+import styleSheet from "../serve-with-content-type.py?fn=css-module-assertions/resources/basic.css&ct=text/css;foo=bar" assert { type: "css"};
+check(t5, styleSheet);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/cors-crossorigin-requests.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/cors-crossorigin-requests.html
new file mode 100644
index 0000000000..b9de562b72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/cors-crossorigin-requests.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+<head>
+ <title>css-module-assertions-crossorigin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>css-module-assertions-crossorigin</h1>
+ <iframe id="import-WithCORS" src="resources/crossorigin-import-with-cors.sub.html"></iframe>
+ <iframe id="import-NoCORS" src="resources/crossorigin-import-without-cors.sub.html"></iframe>
+ <iframe id="import-parseerror-WithCors" src="resources/crossorigin-import-parse-error-with-cors.sub.html"></iframe>
+ <script>
+
+ var tests = [
+ { "obj": async_test("Imported CSS module, cross-origin with CORS"), "id": "import-WithCORS", "expected": "imported CSS: #test { background-color: rgb(255, 0, 0); }" },
+ { "obj": async_test("Imported CSS module, cross-origin, missing CORS ACAO header"), "id": "import-NoCORS", "expected": "error" },
+ { "obj": async_test("Imported CSS module with parse error, cross-origin, with CORS"), "id": "import-parseerror-WithCors", "expected": "imported CSS rules count: 0" },
+ ];
+
+ window.addEventListener("load", function () {
+ tests.forEach(function (test) {
+ var target = document.getElementById(test.id);
+ test.obj.step(function () {
+ assert_equals(target.contentDocument._log, test.expected, "Unexpected _log value");
+ });
+ test.obj.done();
+ });
+ });
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/credentials.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/credentials.sub.html
new file mode 100644
index 0000000000..8c2f0f8073
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/credentials.sub.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+document.cookie = 'milk=1';
+
+const setCookiePromise = fetch(
+ 'http://{{domains[www2]}}:{{ports[http][0]}}/cookies/resources/set-cookie.py?name=milk&path=/html/semantics/scripting-1/the-script-element/css-module-assertions/',
+ {
+ mode: 'no-cors',
+ credentials: 'include',
+ });
+
+const windowLoadPromise = new Promise(resolve => {
+ window.addEventListener('load', () => {
+ resolve();
+ });
+});
+
+promise_test(t => {
+ const iframe = document.createElement('iframe');
+
+ return Promise.all([setCookiePromise, windowLoadPromise]).then(() => {
+ const messagePromise = new Promise(resolve => {
+ window.addEventListener('message', event => {
+ resolve();
+ });
+ });
+
+ iframe.src = 'resources/credentials-iframe.sub.html';
+ document.body.appendChild(iframe);
+
+ return messagePromise;
+ }).then(() => {
+ const w = iframe.contentWindow;
+
+ assert_equals(w.sameOriginNoneDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is not specified and the target is same-origin');
+ assert_equals(w.sameOriginAnonymousDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is same-origin');
+ assert_equals(w.sameOriginUseCredentialsDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is same-origin');
+ assert_equals(w.crossOriginNoneDescendant, false,
+ 'Descendant CSS modules should not be loaded with the credentials when the crossOrigin attribute is not specified and the target is cross-origin');
+ assert_equals(w.crossOriginAnonymousDescendant, false,
+ 'Descendant CSS modules should not be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is cross-origin');
+ assert_equals(w.crossOriginUseCredentialsDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is cross-origin');
+});
+}, 'CSS Modules should be loaded with or without the credentials based on the same-origin-ness and the crossOrigin attribute');
+</script>
+<body>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/css-module-worker-test.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/css-module-worker-test.html
new file mode 100644
index 0000000000..7ff672da6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/css-module-worker-test.html
@@ -0,0 +1,54 @@
+<!doctype html>
+
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/utils.js"></script>
+</head>
+
+<body>
+ <script>
+ setup({allow_uncaught_exception: true});
+ promise_test(function (test) {
+ const uuid = token();
+ const worker = new Worker(`./resources/worker.sub.js?key=${uuid}`, {
+ type: "module"
+ });
+ return new Promise((resolve, reject) => {
+ worker.addEventListener("error", resolve);
+ worker.addEventListener("message", reject);
+ }).then(async () => {
+ const fetchResponse = await fetch(`./resources/record-fetch.py?key=${uuid}&action=getCount`);
+ const fetchData = await fetchResponse.json();
+ assert_equals(fetchData.count, 0, "Shouldn't have tried fetching CSS module in worker");
+ });
+ }, "A static import CSS Module within a web worker should not load and should not attempt to fetch the module.");
+
+ promise_test(function (test) {
+ const uuid = token();
+ const worker = new Worker(`./resources/worker-dynamic-import.sub.js?key=${uuid}`, {
+ type: "module"
+ });
+
+ return new Promise(resolve => {
+ worker.addEventListener("message", resolve);
+ }).then(async (event) => {
+ assert_equals(event.data, "NOT LOADED");
+ const fetchResponse = await fetch(`./resources/record-fetch.py?key=${uuid}&action=getCount`);
+ const fetchData = await fetchResponse.json();
+ assert_equals(fetchData.count, 0, "Shouldn't have tried fetching CSS module in worker");
+ });
+ }, "A dynamic import CSS Module within a web worker should not load and should not attempt to fetch the module.");
+
+ promise_test(function (test) {
+ const worker = new Worker("./resources/basic.css", {
+ type: "module"
+ });
+ return new Promise(resolve => {
+ worker.onerror = resolve;
+ });
+ }, "An attempt to load a CSS module as a worker should fail.");
+
+ </script>
+
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-basic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-basic.html
new file mode 100644
index 0000000000..4ea1790aab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-basic.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="test">I am a test div.</div>
+ <div id="test2">I am a test div.</div>
+ <div id="test3">I am a test div.</div>
+ <div id="test3b">I am a test div.</div>
+ <div id="test4">I am a test div.</div>
+ <div id="test4b">I am a test div.</div>
+ <script>
+ window.errorCount = 0;
+ window.onerror = (errorMsg, url, lineNumber, column, errorObj) => {
+ window.errorCount++;
+ };
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/basic.css" assert { type: "css" };
+ test(() => {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(getComputedStyle(document.querySelector('#test'))
+ .backgroundColor, "rgb(255, 0, 0)", "CSS module import should succeed");
+ }, "A CSS Module should load");
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/basic-large.css" assert { type: "css" };
+ test(() => {
+ // This tests potential streaming compilation of modules in
+ // Chromium that is triggered only for large (32>KiB) files in older
+ // versions.
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(getComputedStyle(document.querySelector('#test2'))
+ .backgroundColor, "rgb(255, 0, 0)",
+ "CSS module import should succeed");
+ }, "A large CSS Module should load");
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/bad-import.css" assert { type: "css" };
+ test(() => {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(window.errorCount, 0);
+ assert_equals(sheet.cssRules.length, 1, "Parser should skip @import rule");
+ assert_equals(getComputedStyle(document.querySelector('#test3b'))
+ .backgroundColor, "rgba(0, 0, 0, 0)",
+ "CSS module @import should not succeed");
+ assert_equals(getComputedStyle(document.querySelector('#test3'))
+ .backgroundColor, "rgb(0, 255, 0)",
+ "Rule after @import should still be applied");
+ }, "An @import CSS Module should not load, but should not throw an exception");
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/malformed.css" assert { type: "css" };
+ test(() => {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(window.errorCount, 0);
+ assert_equals(sheet.cssRules.length, 1, "Import of malformed CSS should succeed and rules after the parse error should still be parsed");
+ assert_equals(getComputedStyle(document.querySelector('#test4'))
+ .backgroundColor, "rgba(0, 0, 0, 0)",
+ "Malformed CSS rule should not be applied");
+ assert_equals(getComputedStyle(document.querySelector('#test4b'))
+ .backgroundColor, "rgb(0, 255, 0)",
+ "Parsing should recover and rules after malformed rules should be applied");
+ }, "A parse error should not prevent subsequent rules from being included in a CSS module");
+ </script>
+ <script type="module">
+ promise_test(function (test) {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/css-module-without-assertion-iframe.html";
+ return new Promise(resolve => {
+ iframe.onload = resolve;
+ document.body.appendChild(iframe);
+ }).then(event => {
+ assert_equals(iframe.contentDocument.window_onerror, undefined);
+ assert_equals(iframe.contentDocument.script_onerror.type, "error");
+ assert_equals(getComputedStyle(iframe.contentDocument.querySelector('#test'))
+ .backgroundColor, "rgba(0, 0, 0, 0)",
+ "CSS module without type assertion should result in a fetch error");
+ });
+ }, "CSS module without type assertion should result in a fetch error");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-dynamic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-dynamic.html
new file mode 100644
index 0000000000..13967858cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/import-css-module-dynamic.html
@@ -0,0 +1,23 @@
+<!doctype html>
+
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <script>
+ promise_test(async function (test) {
+ const css_module = await import("./resources/basic.css", { assert: { type: "css" }});
+ assert_true(css_module.default instanceof CSSStyleSheet);
+ assert_equals(css_module.default.cssRules[0].cssText,
+ "#test { background-color: rgb(255, 0, 0); }");
+ }, "Load a CSS module with dynamic import()");
+
+ promise_test(function (test) {
+ return promise_rejects_js(test, TypeError,
+ import("./resources/basic.css"),
+ "Attempting to import() a CSS module without a type assertion should fail");
+ }, "Ensure that loading a CSS module with dymnamic import() fails without a type assertion");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/integrity.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/integrity.html
new file mode 100644
index 0000000000..1dd0dad470
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/integrity.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;script> integrity="" with CSS modules</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+window.matchesLog = [];
+window.matchesEvents = [];
+
+window.mismatchesLog = [];
+window.mismatchesEvents = [];
+</script>
+<script type="module" src="resources/integrity-matches.js" integrity="sha384-xvbfmg9iJFHqmCoOS4VNMCwnFPPxEoIlW1Ojzl+fgEd+Wf8Pyez+SMWue+KNovjA" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script>
+<script type="module" src="resources/integrity-mismatches.js" integrity="sha384-doesnotmatch" onload="window.mismatchesEvents.push('load');" onerror="window.mismatchesEvents.push('error')"></script>
+
+<script type="module">
+test(() => {
+ assert_array_equals(window.matchesLog, ["integrity-matches,css:#test { background-color: rgb(255, 0, 0); }"], "The module and its dependency must have executed");
+ assert_array_equals(window.matchesEvents, ["load"], "The load event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a CSS module and allow it to execute when it matches");
+
+test(() => {
+ assert_array_equals(window.mismatchesLog, [], "The module and its dependency must not have executed");
+ assert_array_equals(window.mismatchesEvents, ["error"], "The error event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a CSS module and not allow it to execute when there's a mismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/load-error-events.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/load-error-events.html
new file mode 100644
index 0000000000..3457452c93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/load-error-events.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for CSS modules</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+ "use strict";
+
+ var test1_load = event_test('inline, 200, parser-inserted', false, false);
+ var test1_error = event_test('inline, 404, parser-inserted', false, true);
+
+ var test2_load = event_test('src, 200, parser-inserted', true, false);
+ var test2_error = event_test('src, 404, parser-inserted', false, true);
+
+ var test3_dynamic_load = event_test('src, 200, not parser-inserted', true, false);
+ var test3_dynamic_error = event_test('src, 404, not parser-inserted', false, true);
+
+ var test4_dynamic_load = event_test('inline, 200, not parser-inserted', false, false);
+ var test4_dynamic_error = event_test('inline, 404, not parser-inserted', false, true);
+
+ var script3_dynamic_load = document.createElement('script');
+ script3_dynamic_load.setAttribute('type', 'module');
+ script3_dynamic_load.onload = () => onLoad(test3_dynamic_load);
+ script3_dynamic_load.onerror = () => onError(test3_dynamic_load);
+ script3_dynamic_load.src = "./resources/load-error-events.py?test=test3_dynamic_load";
+ document.head.appendChild(script3_dynamic_load);
+
+ var script3_dynamic_error = document.createElement('script');
+ script3_dynamic_error.setAttribute('type', 'module');
+ script3_dynamic_error.onload = () => onLoad(test3_dynamic_error);
+ script3_dynamic_error.onerror = () => onError(test3_dynamic_error);
+ script3_dynamic_error.src = "./resources/load-error-events.py?test=test3_dynamic_error";
+ document.head.appendChild(script3_dynamic_error);
+
+ var script4_dynamic_load = document.createElement('script');
+ script4_dynamic_load.setAttribute('type', 'module');
+ script4_dynamic_load.onload = () => onLoad(test4_dynamic_load);
+ script4_dynamic_load.onerror = () => onError(test4_dynamic_load);
+ script4_dynamic_load.async = true;
+ script4_dynamic_load.appendChild(document.createTextNode(`
+ import "./resources/basic.css" assert { type: "css" };
+ onExecute(test4_dynamic_load);`
+ ));
+ document.head.appendChild(script4_dynamic_load);
+
+ var script4_dynamic_error = document.createElement('script');
+ script4_dynamic_error.setAttribute('type', 'module');
+ script4_dynamic_error.onload = () => onLoad(test4_dynamic_error);
+ script4_dynamic_error.onerror = () => onError(test4_dynamic_error);
+ script4_dynamic_error.async = true;
+ script4_dynamic_error.appendChild(document.createTextNode(`import "./not_found.css" assert { type: "css" };`));
+ document.head.appendChild(script4_dynamic_error);
+</script>
+<script onload="onLoad(test1_load);" onerror="onError(test1_load);" type="module">
+ import "./resources/basic.css" assert { type: "css"};
+ onExecute(test1_load);
+</script>
+<script onload="onLoad(test1_error);" onerror="onError(test1_error);" type="module">
+ import "./not_found.css" assert { type: "css"};
+ onExecute(test1_error);
+</script>
+<script src="./resources/load-error-events.py?test=test2_load" onload="onLoad(test2_load);" onerror="onError(test2_load);" type="module"></script>
+<script src="./resources/load-error-events.py?test=test2_error" onload="onLoad(test2_error);" onerror="onError(test2_error);" type="module"></script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/referrer-policies.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/referrer-policies.sub.html
new file mode 100644
index 0000000000..f078437e09
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/referrer-policies.sub.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrers with CSS module requests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+ // "name" parameter is necessary for bypassing the module map.
+ import referrerSame from "./resources/referrer-checker.py?name=sameNoReferrerPolicy" assert { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py?name=remoteNoReferrerPolicy" assert { type: "css"};
+
+ const origin = (new URL(location.href)).origin + "/";
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '"' + originUrl + '"',
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the default referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '"' + origin + '"',
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the default referrer policy.");
+</script>
+<script type="module" referrerpolicy="origin">
+ import referrerSame from "./resources/referrer-checker.py?name=sameReferrerPolicyOrigin" assert { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py?name=remoteReferrerPolicyOrigin" assert { type: "css"};
+
+ const origin = (new URL(location.href)).origin + "/";
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '"' + origin + '"',
+ "Referrer origin should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the origin policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '"' + origin + '"',
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the origin policy.");
+
+</script>
+<script type="module" referrerpolicy="no-referrer">
+ import referrerSame from "./resources/referrer-checker.py?name=sameReferrerPolicyNoReferrer" assert { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py?name=remoteReferrerPolicyNoReferrer" assert { type: "css"};
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '""',
+ "No referrer should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the no-referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '""',
+ "No referrer should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the no-referrer policy.");
+
+</script>
+<script type="module" referrerpolicy="unsafe-url">
+ import referrerSame from "./resources/referrer-checker.py?name=sameNoReferrerPolicyUnsafeUrl" assert { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py?name=remoteNoReferrerPolicyUnsafeUrl" assert { type: "css"};
+
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '"' + originUrl + '"',
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the unsafe-url referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '"' + originUrl + '"',
+ "Referrer URL should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the unsafe-url referrer policy.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/relative-urls.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/relative-urls.html
new file mode 100644
index 0000000000..22971d28d9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/relative-urls.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<head>
+ <title>Test resolution of relative URL in CSS module</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="target"></div>
+ <script type="module">
+ import styleSheet from "./resources/load-relative-url.css" assert { type: "css"};
+ test(() => {
+ const target = document.querySelector("#target");
+ document.adoptedStyleSheets = [ styleSheet ];
+ let backgroundStyle = window.getComputedStyle(target).background;
+ assert_not_equals(backgroundStyle.indexOf("css-module-assertions/resources/image.png"), -1);
+ }, "A relative URL in a CSS module should be resolved relative to the CSS file's URL, not the importing document's URL");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/atImported.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/atImported.css
new file mode 100644
index 0000000000..8629a846d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/atImported.css
@@ -0,0 +1,3 @@
+#test3b {
+ background-color: #FF0000;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bad-import.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bad-import.css
new file mode 100644
index 0000000000..a6e1a0f395
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bad-import.css
@@ -0,0 +1,4 @@
+@import "atImported.css";
+#test3 {
+ background-color:#00FF00;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic-large.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic-large.css
new file mode 100644
index 0000000000..555ab70d2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic-large.css
@@ -0,0 +1,7 @@
+#test2 {
+ background-color:red;
+}
+
+#test:before {
+ content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic.css
new file mode 100644
index 0000000000..e034ed9ac7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic.css
@@ -0,0 +1,3 @@
+#test {
+ background-color: #FF0000;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16be.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16be.css
new file mode 100644
index 0000000000..9e17902a1d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16be.css
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16le.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16le.css
new file mode 100644
index 0000000000..ef90843d8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-16le.css
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-8.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-8.css
new file mode 100644
index 0000000000..5cf81232b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/bom-utf-8.css
@@ -0,0 +1 @@
+div { background-color: blue; } \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/credentials-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/credentials-iframe.sub.html
new file mode 100644
index 0000000000..cf1d621ce3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/credentials-iframe.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script type="module">
+ import styleSheet from "./cross-origin.py?id=sameOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "css" };
+ window.sameOriginNoneDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="anonymous">
+ import styleSheet from "./cross-origin.py?id=sameOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "css" };
+ window.sameOriginAnonymousDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="use-credentials">
+ import styleSheet from "./cross-origin.py?id=sameOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "css" };
+ window.sameOriginUseCredentialsDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/cross-origin.py?id=crossOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "css" };
+ window.crossOriginNoneDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="anonymous">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/cross-origin.py?id=crossOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "css" };
+ window.crossOriginAnonymousDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="use-credentials">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/cross-origin.py?id=crossOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "css" };
+ window.crossOriginUseCredentialsDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+
+<script type="text/javascript">
+window.addEventListener('load', event => {
+ window.parent.postMessage({}, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/cross-origin.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/cross-origin.py
new file mode 100644
index 0000000000..d744fc9514
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/cross-origin.py
@@ -0,0 +1,17 @@
+def main(request, response):
+
+ headers = [
+ (b"Content-Type", b"text/css"),
+ (b"Access-Control-Allow-Origin", request.GET.first(b"origin")),
+ (b"Access-Control-Allow-Credentials", b"true")
+ ]
+
+ milk = request.cookies.first(b"milk", None)
+
+ # Send back
+ if milk is None:
+ return headers, u'.requestDidNotHaveCookies { }'
+ elif milk.value == b"1":
+ return headers, u'.requestHadCookies { }'
+
+ return headers, u'.requestDidNotHaveCookies { }'
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-parse-error-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-parse-error-with-cors.sub.html
new file mode 100644
index 0000000000..3afb8edeb1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-parse-error-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>css-module-assertions-import-cross-domain-parse-error-WithCORS</title>
+ <script src="../../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>css-module-assertions-import-cross-domain-parse-error-WithCORS</h1>
+ <script type="module" crossorigin>
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/parse-error.css?pipe=header(Access-Control-Allow-Origin,*)" assert { type: "css" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported CSS rules count: ${styleSheet.rules.length}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-with-cors.sub.html
new file mode 100644
index 0000000000..f8754c2a56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>css-module-assertions-import-cross-domain-WithCORS</title>
+ <script src="../../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>css-module-assertions-import-cross-domain-WithCORS</h1>
+ <script type="module" crossorigin>
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic.css?pipe=header(Access-Control-Allow-Origin,*)" assert { type: "css" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported CSS: ${styleSheet.rules[0].cssText}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-without-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-without-cors.sub.html
new file mode 100644
index 0000000000..ab70b73573
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/crossorigin-import-without-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>css-module-assertions-import-cross-domain-NoCORS</title>
+ <script src="../../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>css-module-assertions-import-cross-domain-NoCORS</h1>
+ <script type="module" onerror="document._log.push('error');">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/basic.css" assert { type: "css" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported CSS: ${styleSheet.rules[0].cssText}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/css-module-without-assertion-iframe.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/css-module-without-assertion-iframe.html
new file mode 100644
index 0000000000..3d1be841ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/css-module-without-assertion-iframe.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ window.onerror = function (errorMsg, url, lineNumber, column, errorObj)
+ {
+ document.window_onerror = errorObj.name;
+ return true;
+ };
+
+ function scriptErrorHandler(e) {
+ document.script_onerror = e;
+ }
+ </script>
+ <script type="module" onerror="scriptErrorHandler(event)">
+ import v from "./basic.css";
+ document.adoptedStyleSheets = [v];
+ </script>
+
+ <div id="test">
+ I am a test div.
+ </div>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-matches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-matches.js
new file mode 100644
index 0000000000..95be445311
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-matches.js
@@ -0,0 +1,2 @@
+import styleSheet from "./basic.css" assert { type: "css" };
+window.matchesLog.push(`integrity-matches,css:${styleSheet.cssRules[0].cssText}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-mismatches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-mismatches.js
new file mode 100644
index 0000000000..af6fc24de4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/integrity-mismatches.js
@@ -0,0 +1,2 @@
+import styleSheet from "./basic.css" assert { type: "css" };
+window.matchesLog.push(`integrity-mismatches,css:${styleSheet.cssRules[0].cssText}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-error-events.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-error-events.py
new file mode 100644
index 0000000000..b61b1ca834
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-error-events.py
@@ -0,0 +1,14 @@
+import re
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ test = request.GET.first(b'test')
+ assert(re.match(b'^[a-zA-Z0-9_]+$', test))
+
+ status = 200
+ if test.find(b'_load') >= 0:
+ content = b'import "./basic.css" assert { type: "css"}; %s.executed = true;' % test
+ else:
+ content = b'import "./not_found.css" assert { type: "css"}; %s.test.step(function() { assert_unreached("404 script should not be executed"); });' % test
+
+ return status, headers, content
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-relative-url.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-relative-url.css
new file mode 100644
index 0000000000..27f2987610
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/load-relative-url.css
@@ -0,0 +1,5 @@
+
+#target {
+ /* image.png doesn't exist, but that's irrelevant to the test. */
+ background: url('./image.png');
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/malformed.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/malformed.css
new file mode 100644
index 0000000000..28819bfdf5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/malformed.css
@@ -0,0 +1,7 @@
+#test4 } {
+ background-color: #FF0000;
+}
+
+#test4b {
+ background-color: #00FF00;
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/parse-error.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/parse-error.css
new file mode 100644
index 0000000000..2bee3ff996
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/parse-error.css
@@ -0,0 +1,2 @@
+div /* Opening bracket skipped intentionally. */ }
+
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/record-fetch.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/record-fetch.py
new file mode 100644
index 0000000000..4928cb4acb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/record-fetch.py
@@ -0,0 +1,20 @@
+def main(request, response):
+ try:
+ stash_key = request.GET.first(b"key")
+ action = request.GET.first(b"action")
+
+ run_count = request.server.stash.take(stash_key)
+ if not run_count:
+ run_count = 0
+
+ if action == b"incCount":
+ request.server.stash.put(stash_key, run_count + 1)
+ response.headers.set(b"Content-Type", b"text/css")
+ response.content = b'#test { background-color: #FF0000; }'
+ elif action == b"getCount":
+ response.headers.set(b"Content-Type", b"text/json")
+ response.content = b'{"count": %d }' % run_count
+ else:
+ response.set_error(400, u"Invalid action")
+ except:
+ response.set_error(400, u"Not enough parameters")
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py
new file mode 100644
index 0000000000..c1eaed8e46
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/referrer-checker.py
@@ -0,0 +1,7 @@
+def main(request, response):
+ referrer = request.headers.get(b"referer", b"")
+ response_headers = [(b"Content-Type", b"text/css"),
+ (b"Access-Control-Allow-Origin", b"*")]
+ # Put the referrer in a CSS rule that can be read by the importer through CSSOM
+ return (200, response_headers,
+ b'.referrer { content: "' + referrer + b'" }')
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/utf-8.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/utf-8.css
new file mode 100644
index 0000000000..0a8b46656f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/utf-8.css
@@ -0,0 +1,3 @@
+#test {
+ content: "śćążź";
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/windows-1250.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/windows-1250.css
new file mode 100644
index 0000000000..9beac4d618
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/windows-1250.css
@@ -0,0 +1,3 @@
+#test {
+ content: "�湿�";
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker-dynamic-import.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker-dynamic-import.sub.js
new file mode 100644
index 0000000000..791bd7d3f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker-dynamic-import.sub.js
@@ -0,0 +1,3 @@
+import("./record-fetch.py?key={{GET[key]}}&action=incCount", { assert: { type: "css" } })
+ .then(() => postMessage("LOADED"))
+ .catch(e => postMessage("NOT LOADED")); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker.sub.js
new file mode 100644
index 0000000000..ffee312d21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/resources/worker.sub.js
@@ -0,0 +1,2 @@
+import "./record-fetch.py?key={{GET[key]}}&action=incCount" assert { type: "css" };
+postMessage("Unexpectedly loaded"); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/script-element-css-src.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/script-element-css-src.html
new file mode 100644
index 0000000000..231d02db47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module-assertions/script-element-css-src.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>&lt;script&gt; with CSS src</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that <script> doesn't load when the src is CSS.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["error"]);
+ }));
+</script>
+<script type="module" src="./resources/basic.css" onload="t.unreached_func('CSS src should fail to load')" onerror="log.push('error')"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-2.html
new file mode 100644
index 0000000000..fb25b1ffb3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-2.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="windows-1250">
+<title>CSS modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/utf-8.css" with { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"�湿�\"");
+ }, "CSS module should be loaded as utf-8 even though document's encoding is windows-1250");
+</script>
+<script type="module">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/windows-1250.css&ct=text/css%3Bcharset=windows-1250" with { type: "css"};
+ test(() => {
+ assert_not_equals(styleSheet.rules[0].style.content, "\"�湿�\"",
+ 'Should be decoded as UTF-8');
+ }, "CSS module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header, and this document's encoding is windows-1250");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-bom.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-bom.html
new file mode 100644
index 0000000000..113ae63ea4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset-bom.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>CSS Module scripts should ignore BOMs and always use UTF-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module">
+ import utf8BOMSheet from './resources/bom-utf-8.css' with { type: 'css' };
+ test(function() {
+ assert_equals(utf8BOMSheet.rules[0].selectorText, 'div', 'No UTF-8 BOM expected in selector');
+ }, 'UTF-8 BOM should be stripped when decoding JSON module script');
+
+ import utf16BEBOMSheet from './resources/bom-utf-16be.css' with { type: 'css' };
+ test(function() {
+ assert_equals(utf16BEBOMSheet.rules[0].selectorText, '\ufffd\ufffd\ufffdd\ufffdi\ufffdv\ufffd \ufffd', 'Expected UTF-8 decoded selectorText with 0xfffd replacement characters');
+ }, 'UTF-16BE BOM should be ignored, so CSS module should be UTF-8 decoded');
+
+ import utf16LEBOMSheet from './resources/bom-utf-16le.css' with { type: 'css' };
+ test(function() {
+ assert_equals(utf16LEBOMSheet.rules[0].selectorText, '\ufffd\ufffdd\ufffdi\ufffdv\ufffd \ufffd', 'Expected UTF-8 decoded selectorText with 0xfffd replacement characters');
+ }, 'UTF-16LE BOM should be ignored, so CSS module should be UTF-8 decoded');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset.html
new file mode 100644
index 0000000000..e7010c2f57
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/charset.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CSS modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/utf-8.css&ct=text/css%3Bcharset=utf-8" with { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=utf8 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/utf-8.css&ct=text/css%3Bcharset=shift-jis" with { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=shift-jis is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/utf-8.css&ct=text/css%3Bcharset=windows-1252" with { type: "css"};
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=windows-1252 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/utf-8.css&ct=text/css%3Bcharset=utf-7" with { type: "css"};;
+ test(() => {
+ assert_equals(styleSheet.rules[0].style.content, "\"śćążź\"");
+ }, "CSS module should be loaded as utf-8 when charset=utf-7 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/windows-1250.css&ct=text/css%3Bcharset=windows-1250" with { type: "css"};
+ test(() => {
+ assert_not_equals(styleSheet.rules[0].style.content, "\"śćążź\"",
+ 'Should be decoded as UTF-8');
+ }, "CSS module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/content-type-checking.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/content-type-checking.html
new file mode 100644
index 0000000000..5be0d50c38
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/content-type-checking.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CSS modules: Content-Type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function check(t, styleSheet) {
+ t.step(() => {
+ assert_equals(styleSheet.rules[0].cssText, "#test { background-color: rgb(255, 0, 0); }");
+ t.done();
+ });
+}
+const t1 = async_test("text/css");
+const t2 = async_test("application/css");
+const t3 = async_test("text/html+css");
+const t4 = async_test("text/css;boundary=something");
+const t5 = async_test("text/css;foo=bar");
+</script>
+<script type="module" onerror="t1.unreached_func()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/basic.css&ct=text/css" with { type: "css"};
+ check(t1, styleSheet);
+</script>
+<script type="module" onerror="t2.step_func_done()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/basic.css&ct=application/css" with { type: "css"};
+ t2.unreached_func("Should not have loaded with MIME type application/css")();
+</script>
+<script type="module" onerror="t3.step_func_done()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/basic.css&ct=text/html+css" with { type: "css"};
+ t3.unreached_func("Should not have loaded with MIME type text/html+css")();
+</script>
+<script type="module" onerror="t4.unreached_func()()">
+ import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/basic.css&ct=text/css;boundary=something" with { type: "css"};
+ check(t4, styleSheet);
+</script>
+<script type="module" onerror="t5.unreached_func()()">
+import styleSheet from "../serve-with-content-type.py?fn=css-module/resources/basic.css&ct=text/css;foo=bar" with { type: "css"};
+check(t5, styleSheet);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/cors-crossorigin-requests.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/cors-crossorigin-requests.html
new file mode 100644
index 0000000000..e699ef927e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/cors-crossorigin-requests.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+<head>
+ <title>css-module-crossorigin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>css-module-crossorigin</h1>
+ <iframe id="import-WithCORS" src="resources/crossorigin-import-with-cors.sub.html"></iframe>
+ <iframe id="import-NoCORS" src="resources/crossorigin-import-without-cors.sub.html"></iframe>
+ <iframe id="import-parseerror-WithCors" src="resources/crossorigin-import-parse-error-with-cors.sub.html"></iframe>
+ <script>
+
+ var tests = [
+ { "obj": async_test("Imported CSS module, cross-origin with CORS"), "id": "import-WithCORS", "expected": "imported CSS: #test { background-color: rgb(255, 0, 0); }" },
+ { "obj": async_test("Imported CSS module, cross-origin, missing CORS ACAO header"), "id": "import-NoCORS", "expected": "error" },
+ { "obj": async_test("Imported CSS module with parse error, cross-origin, with CORS"), "id": "import-parseerror-WithCors", "expected": "imported CSS rules count: 0" },
+ ];
+
+ window.addEventListener("load", function () {
+ tests.forEach(function (test) {
+ var target = document.getElementById(test.id);
+ test.obj.step(function () {
+ assert_equals(target.contentDocument._log, test.expected, "Unexpected _log value");
+ });
+ test.obj.done();
+ });
+ });
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/credentials.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/credentials.sub.html
new file mode 100644
index 0000000000..0da573dad2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/credentials.sub.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+document.cookie = 'milk=1';
+
+const setCookiePromise = fetch(
+ 'http://{{domains[www2]}}:{{ports[http][0]}}/cookies/resources/set-cookie.py?name=milk&path=/html/semantics/scripting-1/the-script-element/css-module/',
+ {
+ mode: 'no-cors',
+ credentials: 'include',
+ });
+
+const windowLoadPromise = new Promise(resolve => {
+ window.addEventListener('load', () => {
+ resolve();
+ });
+});
+
+promise_test(t => {
+ const iframe = document.createElement('iframe');
+
+ return Promise.all([setCookiePromise, windowLoadPromise]).then(() => {
+ const messagePromise = new Promise(resolve => {
+ window.addEventListener('message', event => {
+ resolve();
+ });
+ });
+
+ iframe.src = 'resources/credentials-iframe.sub.html';
+ document.body.appendChild(iframe);
+
+ return messagePromise;
+ }).then(() => {
+ const w = iframe.contentWindow;
+
+ assert_equals(w.sameOriginNoneDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is not specified and the target is same-origin');
+ assert_equals(w.sameOriginAnonymousDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is same-origin');
+ assert_equals(w.sameOriginUseCredentialsDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is same-origin');
+ assert_equals(w.crossOriginNoneDescendant, false,
+ 'Descendant CSS modules should not be loaded with the credentials when the crossOrigin attribute is not specified and the target is cross-origin');
+ assert_equals(w.crossOriginAnonymousDescendant, false,
+ 'Descendant CSS modules should not be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is cross-origin');
+ assert_equals(w.crossOriginUseCredentialsDescendant, true,
+ 'Descendant CSS modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is cross-origin');
+});
+}, 'CSS Modules should be loaded with or without the credentials based on the same-origin-ness and the crossOrigin attribute');
+</script>
+<body>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/css-module-worker-test.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/css-module-worker-test.html
new file mode 100644
index 0000000000..7ff672da6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/css-module-worker-test.html
@@ -0,0 +1,54 @@
+<!doctype html>
+
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/utils.js"></script>
+</head>
+
+<body>
+ <script>
+ setup({allow_uncaught_exception: true});
+ promise_test(function (test) {
+ const uuid = token();
+ const worker = new Worker(`./resources/worker.sub.js?key=${uuid}`, {
+ type: "module"
+ });
+ return new Promise((resolve, reject) => {
+ worker.addEventListener("error", resolve);
+ worker.addEventListener("message", reject);
+ }).then(async () => {
+ const fetchResponse = await fetch(`./resources/record-fetch.py?key=${uuid}&action=getCount`);
+ const fetchData = await fetchResponse.json();
+ assert_equals(fetchData.count, 0, "Shouldn't have tried fetching CSS module in worker");
+ });
+ }, "A static import CSS Module within a web worker should not load and should not attempt to fetch the module.");
+
+ promise_test(function (test) {
+ const uuid = token();
+ const worker = new Worker(`./resources/worker-dynamic-import.sub.js?key=${uuid}`, {
+ type: "module"
+ });
+
+ return new Promise(resolve => {
+ worker.addEventListener("message", resolve);
+ }).then(async (event) => {
+ assert_equals(event.data, "NOT LOADED");
+ const fetchResponse = await fetch(`./resources/record-fetch.py?key=${uuid}&action=getCount`);
+ const fetchData = await fetchResponse.json();
+ assert_equals(fetchData.count, 0, "Shouldn't have tried fetching CSS module in worker");
+ });
+ }, "A dynamic import CSS Module within a web worker should not load and should not attempt to fetch the module.");
+
+ promise_test(function (test) {
+ const worker = new Worker("./resources/basic.css", {
+ type: "module"
+ });
+ return new Promise(resolve => {
+ worker.onerror = resolve;
+ });
+ }, "An attempt to load a CSS module as a worker should fail.");
+
+ </script>
+
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-basic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-basic.html
new file mode 100644
index 0000000000..8e9b84691a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-basic.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="test">I am a test div.</div>
+ <div id="test2">I am a test div.</div>
+ <div id="test3">I am a test div.</div>
+ <div id="test3b">I am a test div.</div>
+ <div id="test4">I am a test div.</div>
+ <div id="test4b">I am a test div.</div>
+ <script>
+ window.errorCount = 0;
+ window.onerror = (errorMsg, url, lineNumber, column, errorObj) => {
+ window.errorCount++;
+ };
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/basic.css" with { type: "css" };
+ test(() => {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(getComputedStyle(document.querySelector('#test'))
+ .backgroundColor, "rgb(255, 0, 0)", "CSS module import should succeed");
+ }, "A CSS Module should load");
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/basic-large.css" with { type: "css" };
+ test(() => {
+ // This tests potential streaming compilation of modules in
+ // Chromium that is triggered only for large (32>KiB) files in older
+ // versions.
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(getComputedStyle(document.querySelector('#test2'))
+ .backgroundColor, "rgb(255, 0, 0)",
+ "CSS module import should succeed");
+ }, "A large CSS Module should load");
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/bad-import.css" with { type: "css" };
+ test(() => {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(window.errorCount, 0);
+ assert_equals(sheet.cssRules.length, 1, "Parser should skip @import rule");
+ assert_equals(getComputedStyle(document.querySelector('#test3b'))
+ .backgroundColor, "rgba(0, 0, 0, 0)",
+ "CSS module @import should not succeed");
+ assert_equals(getComputedStyle(document.querySelector('#test3'))
+ .backgroundColor, "rgb(0, 255, 0)",
+ "Rule after @import should still be applied");
+ }, "An @import CSS Module should not load, but should not throw an exception");
+ </script>
+ <script type="module" onerror="unreachable()">
+ import sheet from "./resources/malformed.css" with { type: "css" };
+ test(() => {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
+ assert_equals(window.errorCount, 0);
+ assert_equals(sheet.cssRules.length, 1, "Import of malformed CSS should succeed and rules after the parse error should still be parsed");
+ assert_equals(getComputedStyle(document.querySelector('#test4'))
+ .backgroundColor, "rgba(0, 0, 0, 0)",
+ "Malformed CSS rule should not be applied");
+ assert_equals(getComputedStyle(document.querySelector('#test4b'))
+ .backgroundColor, "rgb(0, 255, 0)",
+ "Parsing should recover and rules after malformed rules should be applied");
+ }, "A parse error should not prevent subsequent rules from being included in a CSS module");
+ </script>
+ <script type="module">
+ promise_test(function (test) {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/css-module-without-attribute-iframe.html";
+ return new Promise(resolve => {
+ iframe.onload = resolve;
+ document.body.appendChild(iframe);
+ }).then(event => {
+ assert_equals(iframe.contentDocument.window_onerror, undefined);
+ assert_equals(iframe.contentDocument.script_onerror.type, "error");
+ assert_equals(getComputedStyle(iframe.contentDocument.querySelector('#test'))
+ .backgroundColor, "rgba(0, 0, 0, 0)",
+ "CSS module without type attribute should result in a fetch error");
+ });
+ }, "CSS module without type attribute should result in a fetch error");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-dynamic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-dynamic.html
new file mode 100644
index 0000000000..5774a31cb2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/import-css-module-dynamic.html
@@ -0,0 +1,23 @@
+<!doctype html>
+
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <script>
+ promise_test(async function (test) {
+ const css_module = await import("./resources/basic.css", { with: { type: "css" }});
+ assert_true(css_module.default instanceof CSSStyleSheet);
+ assert_equals(css_module.default.cssRules[0].cssText,
+ "#test { background-color: rgb(255, 0, 0); }");
+ }, "Load a CSS module with dynamic import()");
+
+ promise_test(function (test) {
+ return promise_rejects_js(test, TypeError,
+ import("./resources/basic.css"),
+ "Attempting to import() a CSS module without a type attribute should fail");
+ }, "Ensure that loading a CSS module with dymnamic import() fails without a type attribute");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html
new file mode 100644
index 0000000000..1dd0dad470
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;script> integrity="" with CSS modules</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+window.matchesLog = [];
+window.matchesEvents = [];
+
+window.mismatchesLog = [];
+window.mismatchesEvents = [];
+</script>
+<script type="module" src="resources/integrity-matches.js" integrity="sha384-xvbfmg9iJFHqmCoOS4VNMCwnFPPxEoIlW1Ojzl+fgEd+Wf8Pyez+SMWue+KNovjA" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script>
+<script type="module" src="resources/integrity-mismatches.js" integrity="sha384-doesnotmatch" onload="window.mismatchesEvents.push('load');" onerror="window.mismatchesEvents.push('error')"></script>
+
+<script type="module">
+test(() => {
+ assert_array_equals(window.matchesLog, ["integrity-matches,css:#test { background-color: rgb(255, 0, 0); }"], "The module and its dependency must have executed");
+ assert_array_equals(window.matchesEvents, ["load"], "The load event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a CSS module and allow it to execute when it matches");
+
+test(() => {
+ assert_array_equals(window.mismatchesLog, [], "The module and its dependency must not have executed");
+ assert_array_equals(window.mismatchesEvents, ["error"], "The error event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a CSS module and not allow it to execute when there's a mismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/load-error-events.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/load-error-events.html
new file mode 100644
index 0000000000..703a0734ff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/load-error-events.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for CSS modules</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+ "use strict";
+
+ var test1_load = event_test('inline, 200, parser-inserted', false, false);
+ var test1_error = event_test('inline, 404, parser-inserted', false, true);
+
+ var test2_load = event_test('src, 200, parser-inserted', true, false);
+ var test2_error = event_test('src, 404, parser-inserted', false, true);
+
+ var test3_dynamic_load = event_test('src, 200, not parser-inserted', true, false);
+ var test3_dynamic_error = event_test('src, 404, not parser-inserted', false, true);
+
+ var test4_dynamic_load = event_test('inline, 200, not parser-inserted', false, false);
+ var test4_dynamic_error = event_test('inline, 404, not parser-inserted', false, true);
+
+ var script3_dynamic_load = document.createElement('script');
+ script3_dynamic_load.setAttribute('type', 'module');
+ script3_dynamic_load.onload = () => onLoad(test3_dynamic_load);
+ script3_dynamic_load.onerror = () => onError(test3_dynamic_load);
+ script3_dynamic_load.src = "./resources/load-error-events.py?test=test3_dynamic_load";
+ document.head.appendChild(script3_dynamic_load);
+
+ var script3_dynamic_error = document.createElement('script');
+ script3_dynamic_error.setAttribute('type', 'module');
+ script3_dynamic_error.onload = () => onLoad(test3_dynamic_error);
+ script3_dynamic_error.onerror = () => onError(test3_dynamic_error);
+ script3_dynamic_error.src = "./resources/load-error-events.py?test=test3_dynamic_error";
+ document.head.appendChild(script3_dynamic_error);
+
+ var script4_dynamic_load = document.createElement('script');
+ script4_dynamic_load.setAttribute('type', 'module');
+ script4_dynamic_load.onload = () => onLoad(test4_dynamic_load);
+ script4_dynamic_load.onerror = () => onError(test4_dynamic_load);
+ script4_dynamic_load.async = true;
+ script4_dynamic_load.appendChild(document.createTextNode(`
+ import "./resources/basic.css" with { type: "css" };
+ onExecute(test4_dynamic_load);`
+ ));
+ document.head.appendChild(script4_dynamic_load);
+
+ var script4_dynamic_error = document.createElement('script');
+ script4_dynamic_error.setAttribute('type', 'module');
+ script4_dynamic_error.onload = () => onLoad(test4_dynamic_error);
+ script4_dynamic_error.onerror = () => onError(test4_dynamic_error);
+ script4_dynamic_error.async = true;
+ script4_dynamic_error.appendChild(document.createTextNode(`import "./not_found.css" with { type: "css" };`));
+ document.head.appendChild(script4_dynamic_error);
+</script>
+<script onload="onLoad(test1_load);" onerror="onError(test1_load);" type="module">
+ import "./resources/basic.css" with { type: "css"};
+ onExecute(test1_load);
+</script>
+<script onload="onLoad(test1_error);" onerror="onError(test1_error);" type="module">
+ import "./not_found.css" with { type: "css"};
+ onExecute(test1_error);
+</script>
+<script src="./resources/load-error-events.py?test=test2_load" onload="onLoad(test2_load);" onerror="onError(test2_load);" type="module"></script>
+<script src="./resources/load-error-events.py?test=test2_error" onload="onLoad(test2_error);" onerror="onError(test2_error);" type="module"></script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/referrer-policies.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/referrer-policies.sub.html
new file mode 100644
index 0000000000..a507ee52fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/referrer-policies.sub.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrers with CSS module requests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+ // "name" parameter is necessary for bypassing the module map.
+ import referrerSame from "./resources/referrer-checker.py?name=sameNoReferrerPolicy" with { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py?name=remoteNoReferrerPolicy" with { type: "css"};
+
+ const origin = (new URL(location.href)).origin + "/";
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '"' + originUrl + '"',
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the default referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '"' + origin + '"',
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the default referrer policy.");
+</script>
+<script type="module" referrerpolicy="origin">
+ import referrerSame from "./resources/referrer-checker.py?name=sameReferrerPolicyOrigin" with { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py?name=remoteReferrerPolicyOrigin" with { type: "css"};
+
+ const origin = (new URL(location.href)).origin + "/";
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '"' + origin + '"',
+ "Referrer origin should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the origin policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '"' + origin + '"',
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the origin policy.");
+
+</script>
+<script type="module" referrerpolicy="no-referrer">
+ import referrerSame from "./resources/referrer-checker.py?name=sameReferrerPolicyNoReferrer" with { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py?name=remoteReferrerPolicyNoReferrer" with { type: "css"};
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '""',
+ "No referrer should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the no-referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '""',
+ "No referrer should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the no-referrer policy.");
+
+</script>
+<script type="module" referrerpolicy="unsafe-url">
+ import referrerSame from "./resources/referrer-checker.py?name=sameNoReferrerPolicyUnsafeUrl" with { type: "css"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py?name=remoteNoReferrerPolicyUnsafeUrl" with { type: "css"};
+
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.rules[0].style.content, '"' + originUrl + '"',
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the unsafe-url referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.rules[0].style.content, '"' + originUrl + '"',
+ "Referrer URL should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the unsafe-url referrer policy.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/relative-urls.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/relative-urls.html
new file mode 100644
index 0000000000..0aafb9aae5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/relative-urls.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<head>
+ <title>Test resolution of relative URL in CSS module</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="target"></div>
+ <script type="module">
+ import styleSheet from "./resources/load-relative-url.css" with { type: "css"};
+ test(() => {
+ const target = document.querySelector("#target");
+ document.adoptedStyleSheets = [ styleSheet ];
+ let backgroundStyle = window.getComputedStyle(target).background;
+ assert_not_equals(backgroundStyle.indexOf("css-module/resources/image.png"), -1);
+ }, "A relative URL in a CSS module should be resolved relative to the CSS file's URL, not the importing document's URL");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/atImported.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/atImported.css
new file mode 100644
index 0000000000..8629a846d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/atImported.css
@@ -0,0 +1,3 @@
+#test3b {
+ background-color: #FF0000;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bad-import.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bad-import.css
new file mode 100644
index 0000000000..a6e1a0f395
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bad-import.css
@@ -0,0 +1,4 @@
+@import "atImported.css";
+#test3 {
+ background-color:#00FF00;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic-large.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic-large.css
new file mode 100644
index 0000000000..555ab70d2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic-large.css
@@ -0,0 +1,7 @@
+#test2 {
+ background-color:red;
+}
+
+#test:before {
+ content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic.css
new file mode 100644
index 0000000000..e034ed9ac7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/basic.css
@@ -0,0 +1,3 @@
+#test {
+ background-color: #FF0000;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16be.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16be.css
new file mode 100644
index 0000000000..9e17902a1d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16be.css
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16le.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16le.css
new file mode 100644
index 0000000000..ef90843d8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-16le.css
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-8.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-8.css
new file mode 100644
index 0000000000..5cf81232b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/bom-utf-8.css
@@ -0,0 +1 @@
+div { background-color: blue; } \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/credentials-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/credentials-iframe.sub.html
new file mode 100644
index 0000000000..c71b0e4bb7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/credentials-iframe.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script type="module">
+ import styleSheet from "./cross-origin.py?id=sameOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "css" };
+ window.sameOriginNoneDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="anonymous">
+ import styleSheet from "./cross-origin.py?id=sameOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "css" };
+ window.sameOriginAnonymousDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="use-credentials">
+ import styleSheet from "./cross-origin.py?id=sameOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "css" };
+ window.sameOriginUseCredentialsDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/cross-origin.py?id=crossOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "css" };
+ window.crossOriginNoneDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="anonymous">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/cross-origin.py?id=crossOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "css" };
+ window.crossOriginAnonymousDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+<script type="module" crossOrigin="use-credentials">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/cross-origin.py?id=crossOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "css" };
+ window.crossOriginUseCredentialsDescendant = (styleSheet.cssRules[0].cssText.indexOf(".requestHadCookies") !== -1);
+</script>
+
+<script type="text/javascript">
+window.addEventListener('load', event => {
+ window.parent.postMessage({}, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/cross-origin.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/cross-origin.py
new file mode 100644
index 0000000000..d744fc9514
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/cross-origin.py
@@ -0,0 +1,17 @@
+def main(request, response):
+
+ headers = [
+ (b"Content-Type", b"text/css"),
+ (b"Access-Control-Allow-Origin", request.GET.first(b"origin")),
+ (b"Access-Control-Allow-Credentials", b"true")
+ ]
+
+ milk = request.cookies.first(b"milk", None)
+
+ # Send back
+ if milk is None:
+ return headers, u'.requestDidNotHaveCookies { }'
+ elif milk.value == b"1":
+ return headers, u'.requestHadCookies { }'
+
+ return headers, u'.requestDidNotHaveCookies { }'
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-parse-error-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-parse-error-with-cors.sub.html
new file mode 100644
index 0000000000..59b4afa721
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-parse-error-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>css-module-import-cross-domain-parse-error-WithCORS</title>
+ <script src="../../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>css-module-import-cross-domain-parse-error-WithCORS</h1>
+ <script type="module" crossorigin>
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/parse-error.css?pipe=header(Access-Control-Allow-Origin,*)" with { type: "css" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported CSS rules count: ${styleSheet.rules.length}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-with-cors.sub.html
new file mode 100644
index 0000000000..2a383d8be9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>css-module-import-cross-domain-WithCORS</title>
+ <script src="../../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>css-module-import-cross-domain-WithCORS</h1>
+ <script type="module" crossorigin>
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/basic.css?pipe=header(Access-Control-Allow-Origin,*)" with { type: "css" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported CSS: ${styleSheet.rules[0].cssText}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-without-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-without-cors.sub.html
new file mode 100644
index 0000000000..256a76a1d9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/crossorigin-import-without-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>css-module-import-cross-domain-NoCORS</title>
+ <script src="../../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>css-module-import-cross-domain-NoCORS</h1>
+ <script type="module" onerror="document._log.push('error');">
+ import styleSheet from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/css-module/resources/basic.css" with { type: "css" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported CSS: ${styleSheet.rules[0].cssText}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/css-module-without-attribute-iframe.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/css-module-without-attribute-iframe.html
new file mode 100644
index 0000000000..3d1be841ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/css-module-without-attribute-iframe.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ window.onerror = function (errorMsg, url, lineNumber, column, errorObj)
+ {
+ document.window_onerror = errorObj.name;
+ return true;
+ };
+
+ function scriptErrorHandler(e) {
+ document.script_onerror = e;
+ }
+ </script>
+ <script type="module" onerror="scriptErrorHandler(event)">
+ import v from "./basic.css";
+ document.adoptedStyleSheets = [v];
+ </script>
+
+ <div id="test">
+ I am a test div.
+ </div>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-matches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-matches.js
new file mode 100644
index 0000000000..4bd004ce96
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-matches.js
@@ -0,0 +1,2 @@
+import styleSheet from "./basic.css" with { type: "css" };
+window.matchesLog.push(`integrity-matches,css:${styleSheet.cssRules[0].cssText}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-mismatches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-mismatches.js
new file mode 100644
index 0000000000..7f3bf54762
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/integrity-mismatches.js
@@ -0,0 +1,2 @@
+import styleSheet from "./basic.css" with { type: "css" };
+window.matchesLog.push(`integrity-mismatches,css:${styleSheet.cssRules[0].cssText}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-error-events.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-error-events.py
new file mode 100644
index 0000000000..490bdc42aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-error-events.py
@@ -0,0 +1,14 @@
+import re
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ test = request.GET.first(b'test')
+ assert(re.match(b'^[a-zA-Z0-9_]+$', test))
+
+ status = 200
+ if test.find(b'_load') >= 0:
+ content = b'import "./basic.css" with { type: "css"}; %s.executed = true;' % test
+ else:
+ content = b'import "./not_found.css" with { type: "css"}; %s.test.step(function() { assert_unreached("404 script should not be executed"); });' % test
+
+ return status, headers, content
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-relative-url.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-relative-url.css
new file mode 100644
index 0000000000..27f2987610
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/load-relative-url.css
@@ -0,0 +1,5 @@
+
+#target {
+ /* image.png doesn't exist, but that's irrelevant to the test. */
+ background: url('./image.png');
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/malformed.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/malformed.css
new file mode 100644
index 0000000000..28819bfdf5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/malformed.css
@@ -0,0 +1,7 @@
+#test4 } {
+ background-color: #FF0000;
+}
+
+#test4b {
+ background-color: #00FF00;
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/parse-error.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/parse-error.css
new file mode 100644
index 0000000000..2bee3ff996
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/parse-error.css
@@ -0,0 +1,2 @@
+div /* Opening bracket skipped intentionally. */ }
+
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/record-fetch.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/record-fetch.py
new file mode 100644
index 0000000000..4928cb4acb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/record-fetch.py
@@ -0,0 +1,20 @@
+def main(request, response):
+ try:
+ stash_key = request.GET.first(b"key")
+ action = request.GET.first(b"action")
+
+ run_count = request.server.stash.take(stash_key)
+ if not run_count:
+ run_count = 0
+
+ if action == b"incCount":
+ request.server.stash.put(stash_key, run_count + 1)
+ response.headers.set(b"Content-Type", b"text/css")
+ response.content = b'#test { background-color: #FF0000; }'
+ elif action == b"getCount":
+ response.headers.set(b"Content-Type", b"text/json")
+ response.content = b'{"count": %d }' % run_count
+ else:
+ response.set_error(400, u"Invalid action")
+ except:
+ response.set_error(400, u"Not enough parameters")
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py
new file mode 100644
index 0000000000..c1eaed8e46
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/referrer-checker.py
@@ -0,0 +1,7 @@
+def main(request, response):
+ referrer = request.headers.get(b"referer", b"")
+ response_headers = [(b"Content-Type", b"text/css"),
+ (b"Access-Control-Allow-Origin", b"*")]
+ # Put the referrer in a CSS rule that can be read by the importer through CSSOM
+ return (200, response_headers,
+ b'.referrer { content: "' + referrer + b'" }')
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/utf-8.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/utf-8.css
new file mode 100644
index 0000000000..0a8b46656f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/utf-8.css
@@ -0,0 +1,3 @@
+#test {
+ content: "śćążź";
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/windows-1250.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/windows-1250.css
new file mode 100644
index 0000000000..9beac4d618
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/windows-1250.css
@@ -0,0 +1,3 @@
+#test {
+ content: "�湿�";
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker-dynamic-import.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker-dynamic-import.sub.js
new file mode 100644
index 0000000000..30dc8cde85
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker-dynamic-import.sub.js
@@ -0,0 +1,3 @@
+import("./record-fetch.py?key={{GET[key]}}&action=incCount", { with: { type: "css" } })
+ .then(() => postMessage("LOADED"))
+ .catch(e => postMessage("NOT LOADED")); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker.sub.js
new file mode 100644
index 0000000000..dc85c13ed9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/resources/worker.sub.js
@@ -0,0 +1,2 @@
+import "./record-fetch.py?key={{GET[key]}}&action=incCount" with { type: "css" };
+postMessage("Unexpectedly loaded"); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/script-element-css-src.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/script-element-css-src.html
new file mode 100644
index 0000000000..231d02db47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/script-element-css-src.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>&lt;script&gt; with CSS src</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that <script> doesn't load when the src is CSS.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["error"]);
+ }));
+</script>
+<script type="module" src="./resources/basic.css" onload="t.unreached_func('CSS src should fail to load')" onerror="log.push('error')"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/data-url.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/data-url.html
new file mode 100644
index 0000000000..6fad505271
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/data-url.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test data URL and scripts errors</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+ setup({allow_uncaught_exception:true});
+ async_test(function(t) {
+ var counter = 1
+ window.onerror = t.step_func((message, url, lineno, colno, e) => {
+ // Test that error is not muted as data URLs have a response type of "default"
+ // and errors should only be muted if the response type is "opaque" or "opaqueredirect"
+ assert_not_equals(message, "Script error.")
+ assert_not_equals(url, null);
+ assert_not_equals(url, "");
+ assert_equals(typeof lineno, "number");
+ assert_not_equals(lineno, 0);
+ assert_equals(typeof colno, "number");
+ assert_not_equals(colno, 0);
+ assert_equals(typeof e, "number")
+ assert_equals(e, counter)
+ if (counter == 3) {
+ t.done()
+ }
+ counter++
+ });
+ });
+</script>
+<script src="data:,throw 1"></script>
+<script src="data:,throw 2" crossorigin></script>
+<script src="data:,throw 3" crossorigin=use-credentials></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/README.md b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/README.md
new file mode 100644
index 0000000000..ac5c91c9a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/README.md
@@ -0,0 +1,7 @@
+The tests in this directory is intended for Chromium's DeferAllScript
+experiment https://crbug.com/1339112, containing scenarios that would be
+affected by DeferAllScript, to monitor the behavior on Chromium and other
+browsers.
+
+The same set of expectations (when async/defer scripts are evaluated) should
+already be covered somewhere in WPT.
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script-2.html
new file mode 100644
index 0000000000..f7377d847a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script-2.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Async Script Execution Order</title>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="resources/helper.js"></script>
+</head>
+<body>
+ <script>
+ setup({single_test: true});
+ function finish() {
+ assert_array_equals(
+ window.result,
+ ["Inline1", "Sync1", "Async1", "Sync2", "EndOfBody",
+ "DOMContentLoaded"],
+ "Execution order");
+ // Chromium's force-defer order would be:
+ // ["EndOfBody", "Inline1", "Sync1", "Sync2",
+ // "DOMContentLoaded", "Async1"]
+ //
+ // If we delay async script e.g. after DOMContentLoaded,
+ // the order would be:
+ // ["Inline1", "Sync1", "Sync2", "EndOfBody",
+ // "DOMContentLoaded", "Async1"]
+ done();
+ }
+ logScript("Inline1");
+ window.addEventListener("load", finish);
+ </script>
+ <script src="resources/sync-script-1.js"></script>
+ <!-- To test the async script loaded before force-deferred scripts
+ should be evaluated after the force-deferred scripts
+ in Chromium's force-defer order. -->
+ <script src="resources/async-script-1.js?pipe=trickle(d1)" async></script>
+ <script src="resources/sync-script-2.js?pipe=trickle(d2)"></script>
+ <pre id="bodyend">End</pre>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script.html
new file mode 100644
index 0000000000..90f657679f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/async-script.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Async Script Execution Order</title>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name=variant content="?default">
+ <meta name=variant content="?reload">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="resources/helper.js"></script>
+</head>
+<body>
+ <script>
+ let child_path = 'support/async-script.html';
+ if (location.search == '?reload') {
+ child_path += '?reload';
+ }
+ const child_window = window.open(child_path);
+ fetch_tests_from_window(child_window);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml.xhtml
new file mode 100644
index 0000000000..9d02ff39f5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml.xhtml
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Defer Script Execution Order</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="resources/helper.js"></script>
+</head>
+<body>
+ <div id="scriptlog"/>
+ <input id="testElement"/>
+ <script>
+ setup({single_test: true});
+ function finish() {
+ assert_array_equals(
+ window.result,
+ ["Inline1", "Sync1", "Inline2", "Sync2", "EndOfBody",
+ "Defer1", "Defer2", "DOMContentLoaded"],
+ "Execution order");
+ // Chromium order is (due to https://crbug.com/874749):
+ // ["Inline1", "Sync1", "Defer1", "Inline2", "Defer2", "Sync2",
+ // "EndOfBody", "DOMContentLoaded"]
+ done();
+ }
+ logScript("Inline1");
+ window.addEventListener("load", finish);
+ </script>
+
+ <script src="resources/sync-script-1.js"></script>
+ <script src="resources/defer-script-1.js" defer="defer"></script>
+ <script>
+ logScript("Inline2");
+ </script>
+ <script src="resources/defer-script-2.js" defer="defer"></script>
+ <script src="resources/sync-script-2.js"></script>
+ <pre id="bodyend">End</pre>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script.html
new file mode 100644
index 0000000000..62c3a74014
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/defer-script.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>Defer Script Execution Order</title>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="resources/helper.js"></script>
+</head>
+<body>
+ <script>
+ setup({single_test: true});
+ function finish() {
+ assert_array_equals(
+ window.result,
+ ["Inline1", "Sync1", "Inline2", "Sync2", "EndOfBody",
+ "Defer1", "Defer2", "DOMContentLoaded"],
+ "Execution order");
+ // Chromium's force defer order would be:
+ // ["EndOfBody", "Inline1", "Sync1", "Inline2", "Sync2",
+ // "Defer1", "Defer2", "DOMContentLoaded"]
+ done();
+ }
+ logScript("Inline1");
+ window.addEventListener("load", finish);
+ </script>
+
+ <script src="resources/sync-script-1.js"></script>
+ <script src="resources/defer-script-1.js" defer></script>
+ <script>
+ logScript("Inline2");
+ </script>
+ <script src="resources/defer-script-2.js" defer></script>
+ <script src="resources/sync-script-2.js"></script>
+ <pre id="bodyend">End</pre>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/document-write.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/document-write.html
new file mode 100644
index 0000000000..63e251bae5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/document-write.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<title>DeferAllScript: document.write()</title>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ const t1 = async_test("document.write()");
+ const t2 = async_test("document.write(),close()");
+ const t3 = async_test("document.open(),write()");
+ const t4 = async_test("document.open(),write(),close()");
+ function finish() {
+ const expected = ["Inline1", "Sync2", "Async1", "Sync1",
+ "EndOfBody", "DOMContentLoaded", "WindowLoad"];
+ t1.step_func_done(() => {
+ assert_array_equals(
+ document.getElementById("document-write").contentWindow.result,
+ expected,
+ "Execution order");
+ })();
+
+ t2.step_func_done(() => {
+ assert_array_equals(
+ document.getElementById("document-write-close").contentWindow.result,
+ expected,
+ "Execution order");
+ })();
+
+ t3.step_func_done(() => {
+ assert_array_equals(
+ document.getElementById("document-open-write").contentWindow.result,
+ expected,
+ "Execution order");
+ })();
+
+ t4.step_func_done(() => {
+ assert_array_equals(
+ document.getElementById(
+ "document-open-write-close").contentWindow.result,
+ expected,
+ "Execution order");
+ })();
+ // For cases where documents are kept open, call `document.close()` here
+ // to finish the test harness.
+ for (const iframe of document.querySelectorAll("iframe")) {
+ iframe.contentDocument.close();
+ }
+ }
+
+ // For cases where documents are kept open (that should never occur in
+ // non-intervention cases), schedule `finish()` because Window load events
+ // might be not fired.
+ setTimeout(finish, 5000);
+ </script>
+</head>
+<body onload="finish()">
+<iframe id="document-write"
+ src="resources/document-write-iframe.sub.html?script=document-write.js"></iframe>
+<iframe id="document-write-close"
+ src="resources/document-write-iframe.sub.html?script=document-write-close.js"></iframe>
+<iframe id="document-open-write"
+ src="resources/document-write-iframe.sub.html?script=document-open-write.js"></iframe>
+<iframe id="document-open-write-close"
+ src="resources/document-write-iframe.sub.html?script=document-open-write-close.js"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/async-script-1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/async-script-1.js
new file mode 100644
index 0000000000..267f324aa6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/async-script-1.js
@@ -0,0 +1 @@
+logScript("Async1");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-1.js
new file mode 100644
index 0000000000..1a0524f4fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-1.js
@@ -0,0 +1 @@
+logScript("Defer1");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-2.js
new file mode 100644
index 0000000000..d644e37f18
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-2.js
@@ -0,0 +1 @@
+logScript("Defer2");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write-close.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write-close.js
new file mode 100644
index 0000000000..80703d5c0e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write-close.js
@@ -0,0 +1,3 @@
+document.open();
+document.write(`<script src="sync-script-2.js"></script>`);
+document.close();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write.js
new file mode 100644
index 0000000000..178c374df6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write.js
@@ -0,0 +1,2 @@
+document.open();
+document.write(`<script src="sync-script-2.js"></script>`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-close.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-close.js
new file mode 100644
index 0000000000..7cdde0d78f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-close.js
@@ -0,0 +1,2 @@
+document.write(`<script src="sync-script-2.js"></script>`);
+document.close();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-iframe.sub.html
new file mode 100644
index 0000000000..e3022e3bf1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-iframe.sub.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<body>
+<script src="helper.js"></script>
+<script>
+logScript("Inline1");
+window.addEventListener("load", () => logScript("WindowLoad"));
+</script>
+<script src="{{GET[script]}}?pipe=trickle(d1)"></script>
+<script src="async-script-1.js" async></script>
+<script src="sync-script-1.js?pipe=trickle(d2)"></script>
+<pre id="bodyend">End</pre>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write.js
new file mode 100644
index 0000000000..413a9bc621
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write.js
@@ -0,0 +1 @@
+document.write(`<script src="sync-script-2.js"></script>`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/helper.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/helper.js
new file mode 100644
index 0000000000..89c6d1e828
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/helper.js
@@ -0,0 +1,17 @@
+window.result = [];
+function log(msg) {
+ window.result.push(msg);
+}
+function checkIfReachedBodyEnd() {
+ const endelement = document.getElementById("bodyend");
+ // `<pre id="bodyend">End</pre>` is needed at the end of HTML.
+ if (endelement && endelement.textContent === "End") {
+ log("EndOfBody");
+ endelement.textContent = "Detected";
+ }
+}
+function logScript(msg) {
+ checkIfReachedBodyEnd();
+ log(msg);
+}
+document.addEventListener("DOMContentLoaded", function() { logScript("DOMContentLoaded"); });
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-1.js
new file mode 100644
index 0000000000..726b56346e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-1.js
@@ -0,0 +1 @@
+logScript("Sync1");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-2.js
new file mode 100644
index 0000000000..ba2edfbf27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-2.js
@@ -0,0 +1 @@
+logScript("Sync2");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/support/async-script.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/support/async-script.html
new file mode 100644
index 0000000000..d513bafe4f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer-script/support/async-script.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Child Async Script Execution Order</title>
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="../resources/helper.js"></script>
+</head>
+
+<body>
+ <script>
+ const reload_key = 'run-after-reload';
+ if (location.search == '?reload' && !window.localStorage.getItem(reload_key)) {
+ window.localStorage.setItem(reload_key, true);
+ location.reload();
+ } else {
+ window.localStorage.clear();
+ test();
+ }
+
+ function test() {
+ setup({ single_test: true });
+ function finish() {
+ assert_array_equals(
+ window.result,
+ ["Inline1", "Sync1", "EndOfBody", "DOMContentLoaded", "Async1"],
+ "Execution order");
+ // Chromium's force-defer order would be:
+ // ["EndOfBody", "Inline1", "Sync1", "DOMContentLoaded", "Async1"]
+ done();
+ }
+ logScript("Inline1");
+ window.addEventListener("load", finish);
+ }
+ </script>
+ <script src="../resources/sync-script-1.js"></script>
+ <!-- Delays are added to make DOMContentLoaded be fired before
+ async script load completion -->
+ <script src="../resources/async-script-1.js?pipe=trickle(d1)" async></script>
+ <pre id="bodyend">End</pre>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer.js
new file mode 100644
index 0000000000..c4449ca7c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/defer.js
@@ -0,0 +1,4 @@
+t.step(() => {
+ assert_equals(script_run_status, "deferred", "the script run status");
+});
+t.done();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/emptyish-script-elements.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/emptyish-script-elements.html
new file mode 100644
index 0000000000..37f4a87c95
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/emptyish-script-elements.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Treatment of various empty-ish script elements</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#already-started">
+<link rel="help" href="https://github.com/whatwg/html/issues/3419">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script id="no-children"></script>
+<script id="whitespace-child"> </script>
+
+<script id="gets-a-no-text-child"></script>
+<script id="gets-an-empty-text-child"></script>
+<script id="gets-a-text-child"></script>
+<script id="gets-a-comment-child"></script>
+<script id="gets-a-text-descendant"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ const el = document.querySelector("#no-children");
+ el.appendChild(document.createTextNode("window.noChildrenExecuted = true;"));
+ assert_true(window.noChildrenExecuted);
+}, "A script with no children bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+ const el = document.querySelector("#whitespace-child");
+ el.appendChild(document.createTextNode("window.whitespaceChildExecuted = true;"));
+ assert_equals(window.whitespaceChildExecuted, undefined);
+}, "A script with a whitespace child executes, setting already-started, so adding text is a no-op");
+
+test(() => {
+ const el = document.querySelector("#gets-a-no-text-child");
+ el.appendChild(document.createElement("span"));
+ el.appendChild(document.createTextNode("window.getsANoTextChildExecuted = true;"));
+ assert_true(window.getsANoTextChildExecuted);
+}, "A script with an empty element inserted bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+ const el = document.querySelector("#gets-an-empty-text-child");
+ el.appendChild(document.createTextNode(""));
+ el.appendChild(document.createTextNode("window.getsAnEmptyTextChildExecuted = true;"));
+ assert_true(window.getsAnEmptyTextChildExecuted);
+}, "A script with an empty text node inserted bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+ const el = document.querySelector("#gets-a-text-child");
+ el.appendChild(document.createTextNode("window.getsATextChildExecuted1 = true;"));
+ el.appendChild(document.createTextNode("window.getsATextChildExecuted2 = true;"));
+ assert_true(window.getsATextChildExecuted1);
+ assert_equals(window.getsATextChildExecuted2, undefined);
+}, "A script with a text child inserted executes, setting already-started, so adding text is a no-op");
+
+test(() => {
+ const el = document.querySelector("#gets-a-comment-child");
+ el.appendChild(document.createComment("window.getsACommentChild1 = true;"));
+ el.appendChild(document.createTextNode("window.getsACommentChild2 = true;"));
+ assert_equals(window.getsACommentChild1, undefined);
+ assert_true(window.getsACommentChild2);
+}, "A script with a comment child inserted bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+ const el = document.querySelector("#gets-a-text-descendant");
+ const child = document.createElement("span");
+ child.appendChild(document.createTextNode("window.getsATextDescendantExecuted1 = true;"));
+ el.appendChild(child);
+ el.appendChild(document.createTextNode("window.getsATextDescendantExecuted2 = true;"));
+ assert_equals(window.getsATextDescendantExecuted1, undefined);
+ assert_true(window.getsATextDescendantExecuted2);
+}, "A script with an element containing text inserted bails early, before setting already-started, so can be executed when adding text");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/001.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/001.html
new file mode 100644
index 0000000000..3f54f764f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/001.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: inline in markup </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');</script>
+ <script>log('inline script #2');</script>
+
+ <script type="text/javascript">
+
+ var t = async_test()
+
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'inline script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/002.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/002.html
new file mode 100644
index 0000000000..df7ca95799
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/002.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: external in markup </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script src="scripts/include-1.js"></script>
+ <script src="scripts/include-2.js"></script>
+
+ <script type="text/javascript">
+
+ var t = async_test()
+
+
+ function test() {
+ assert_array_equals(eventOrder, ['external script #1', 'external script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/003.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/003.html
new file mode 100644
index 0000000000..9c23b7e715
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/003.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: inline+external in markup </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1')</script>
+ <script src="scripts/include-2.js"></script>
+
+ <script type="text/javascript">
+
+ var t = async_test()
+
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'external script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/004.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/004.html
new file mode 100644
index 0000000000..a21dd388eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/004.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: external+inline in markup </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script src="scripts/include-1.js"></script>
+ <script>log('inline script #2')</script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['external script #1', 'inline script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/005.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/005.html
new file mode 100644
index 0000000000..ff4a66d25e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/005.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write inline in markup </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script>log(\'doc write script\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'doc write script', 'end script #1' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/006.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/006.html
new file mode 100644
index 0000000000..b8785a60c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/006.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write inline - multiple</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script>log(\'doc write script 1\')<\/script>' );
+ document.write( '<script>log(\'doc write script 2\')<\/script>' );
+ eval('log(\'eval 1\')');
+ document.write( '<script>log(\'doc write script 3\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'doc write script 1','doc write script 2', 'eval 1','doc write script 3', 'end script #1' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/007.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/007.html
new file mode 100644
index 0000000000..edd9920757
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/007.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write external</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script src="scripts/include-1.js">log(\'ignore this\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/008.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/008.html
new file mode 100644
index 0000000000..dce763987f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/008.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write external - multiple</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script src="scripts/include-1.js">log(\'ignore this\')<\/script>' );
+ document.write( '<script src="scripts/include-2.js">log(\'ignore this\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1', 'external script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/009.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/009.html
new file mode 100644
index 0000000000..9d5b2de081
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/009.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write external - multiple with doc.write</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script src="scripts/include-1.js">log(\'ignore this\')<\/script>' );
+ document.write( '<script src="scripts/include-3.js">log(\'ignore this\')<\/script>' );
+ document.write( '<script src="scripts/include-2.js">log(\'ignore this\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1', 'external script before doc write', 'document.write external script', 'external script after doc write', 'external script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/010.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/010.html
new file mode 100644
index 0000000000..69a462c301
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/010.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write external + inline - multiple with doc.write</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script src="scripts/include-1.js">log(\'ignore this\')<\/script>' );
+ document.write( '<script>log(\'inline with doc.write #1\')<\/script>' );
+ document.write( '<script src="scripts/include-2.js">log(\'ignore this\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1', 'inline with doc.write #1', 'external script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/011.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/011.html
new file mode 100644
index 0000000000..33024ba59e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/011.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write external + inline - multiple with doc.write + subsequent markup</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script src="scripts/include-1.js">log(\'ignore this\')<\/script>' );
+ document.write( '<script>log(\'inline with doc.write #1\')<\/script>' );
+ document.write( '<script src="scripts/include-2.js">log(\'ignore this\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1', 'inline with doc.write #1', 'external script #2', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/012.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/012.html
new file mode 100644
index 0000000000..01c9293b20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/012.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write external and onload events </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script src="scripts/include-1.js" onload = "log(\'include-1 load\')">log(\'ignore this\')<\/script>' )
+ document.write( '<script src="scripts/include-3.js" onload = "log(\'include-3 load\')"><\/script>' )
+ document.write( '<script src="scripts/include-2.js" onload = "log(\'include-2 load\')">log(\'ignore this\')<\/script>' )
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1', 'include-1 load', 'external script before doc write', 'document.write external script', 'external script after doc write', 'include-3 load', 'external script #2', 'include-2 load', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/013.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/013.html
new file mode 100644
index 0000000000..09616a67d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/013.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added inline script earlier in document</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('log(\'head script #1\')', {}, document.getElementsByTagName('head')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'head script #1','end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){setTimeout(t.step_func(test), 100); })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/014.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/014.html
new file mode 100644
index 0000000000..41c90a3421
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/014.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: SCRIPT elements that move themselves in DOM </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.getElementsByTagName('head')[0].appendChild(document.getElementsByTagName('script')[2]);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log('script #2');
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015.html
new file mode 100644
index 0000000000..1fa67e22ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added inline+external+inline script earlier in document</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('log(\'head script #1\')', {}, document.getElementsByTagName('head')[0], false);
+ var s = testlib.addScript('', { 'src':'scripts/include-1.js' }, document.getElementsByTagName('head')[0], false);
+ testlib.addScript('log(\'head script #2\')', {}, document.getElementsByTagName('head')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ ///XXX I think the spec allows this case to race
+ onload = function(){
+ setTimeout(t.step_func(
+ function() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'head script #1', 'head script #2', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'head script #1', 'head script #2', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done();
+ }),
+ 100);}
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015a.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015a.html
new file mode 100644
index 0000000000..94763c3542
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/015a.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added inline+external+inline script earlier in document</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('log(\'head script #1\')', {}, document.getElementsByTagName('head')[0], false);
+ var s = testlib.addScript('', { 'src':'scripts/include-1.js?pipe=trickle(d1)' }, document.getElementsByTagName('head')[0], false);
+ testlib.addScript('log(\'head script #2\')', {}, document.getElementsByTagName('head')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'head script #1', 'head script #2', 'end script #1', 'inline script #2', 'external script #1']);
+ t.done();
+ }
+ onload = function(){setTimeout(t.step_func(function() {test.apply(t)}), 2000); }
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/016.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/016.html
new file mode 100644
index 0000000000..1149dcc752
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/016.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added inline script later in document</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('log(\'body script #1\')', {}, document.body, false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'body script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){setTimeout(t.step_func(test), 100); })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/017.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/017.html
new file mode 100644
index 0000000000..66675bcf11
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/017.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: multiple DOM added scripts later in document</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('log(\'body script #1\')', {}, document.getElementsByTagName('body')[0], false);
+ testlib.addScript('', { 'src':'scripts/include-1.js' }, document.getElementsByTagName('body')[0], false);
+ testlib.addScript('log(\'body script #2\')', {}, document.getElementsByTagName('body')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ //The order of the external script vs the second inline script is undefined because the added script is async by default
+ //But we expect most UAs to have the second order
+ onload = function() {setTimeout(t.step_func(function() {
+ assert_any(assert_array_equals, eventOrder, [
+ ['inline script #1', 'body script #1', 'body script #2', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'body script #1', 'body script #2', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done();
+ }), 100);}
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/018.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/018.html
new file mode 100644
index 0000000000..5a349bf556
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/018.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added scripts and doc.write</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('', { 'src':'scripts/include-3.js' }, document.getElementsByTagName('head')[0], false);
+ testlib.addScript('log(\'body script #2\')', {}, document.getElementsByTagName('body')[0], true);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ //XXX Need to test this delaying the document after we insert the external script and delaying the external script itself; afaict the spec allows us to race here on whether the document.write
+ //ever actually happens or not according to whether the insertion point is defined at the point at which the script is executed.
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [
+ ['inline script #1', 'body script #2', 'end script #1', 'external script before doc write', 'document.write external script', 'external script after doc write', 'inline script #2'],
+ ['inline script #1', 'body script #2', 'end script #1', 'inline script #2', 'external script before doc write', 'document.write external script', 'external script after doc write'],
+ ['inline script #1', 'body script #2', 'end script #1', 'inline script #2', 'external script before doc write', 'external script after doc write']
+ ]);
+ t.done();
+}
+ onload = t.step_func(function(){setTimeout(test.apply(t), 100); })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/019.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/019.html
new file mode 100644
index 0000000000..64ee4f1c52
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/019.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added scripts and event handling </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script onload="log('inline #1 load')">
+ log('inline script #1');
+ testlib.addScript('', {'src':'scripts/include-1.js', 'onload':function(){log("external #1 load")}}, document.getElementsByTagName('head')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'external script #1', 'external #1 load', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'external script #1', 'external #1 load']
+ ]);
+ t.done();
+ }
+ onload = function(){setTimeout(t.step_func(test), 100); }
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/020.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/020.html
new file mode 100644
index 0000000000..7d8f953e40
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/020.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added script with data: URL </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ testlib.addScript('', { 'src':'data:text/javascript,log("data URL script")' }, document.getElementsByTagName('body')[0], true);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'data URL script', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'data URL script']]);
+ t.done();
+ }
+ onload = function() {setTimeout( t.step_func(test), 100); }
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/021.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/021.html
new file mode 100644
index 0000000000..34fdc95cb1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/021.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added script with javascript: URL </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ testlib.addScript('', { 'src':'javascript:log("JS URL script")' }, document.getElementsByTagName('body')[0], true);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', /*'JS URL script',*/ 'inline script #2']);
+ t.done();
+ /* pass condition changed 2010-12-01 due to CT-198 */
+ }
+ onload = t.step_func(function(){setTimeout( test, 100); })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/022.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/022.html
new file mode 100644
index 0000000000..ccbcb347dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/022.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added script, late .src </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ var script = testlib.addScript('', { }, document.getElementsByTagName('body')[0], false);
+ script.src='scripts/include-1.js';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ onload = function() {setTimeout(t.step_func(
+ function() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done()
+ }),
+ 100)}
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/023.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/023.html
new file mode 100644
index 0000000000..dc687ffe4d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/023.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added script, even later .src </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ var script = testlib.addScript('', { }, document.getElementsByTagName('body')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2', 'external script #1']);
+ t.done();
+}
+ onload = t.step_func(function(){
+ script.src='scripts/include-1.js';
+ script.onload = t.step_func(test);
+ })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/024.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/024.html
new file mode 100644
index 0000000000..ee807b56f9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/024.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added script, .src set twice</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ var script = testlib.addScript('', { }, document.getElementsByTagName('body')[0], false);
+ script.src='scripts/include-1.js';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done();
+ }
+ onload = t.step_func(function(){
+ script.src='scripts/include-2.js'; // needs to be ignored, script already "is executed"
+ setTimeout(t.step_func(test), 100);
+ })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/025.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/025.html
new file mode 100644
index 0000000000..05c6a97284
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/025.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM added script, .src set on script with content</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ var script = testlib.addScript('log("inline DOM script #1")', { }, document.getElementsByTagName('body')[0], false);
+ script.src='scripts/include-1.js';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'inline DOM script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){
+ setTimeout(t.step_func(test), 100);
+ })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/026.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/026.html
new file mode 100644
index 0000000000..34110ff5ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/026.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: doc write added script, .src set later</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ var t = async_test();
+ document.write('<script><\/script>');
+ var scripts = document.getElementsByTagName('script');
+ scripts[scripts.length - 1].src = 'scripts/include-1.js';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+
+ onload = function() {
+ setTimeout(
+ t.step_func(function() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done();
+ }),
+ 100);
+ }
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/027.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/027.html
new file mode 100644
index 0000000000..e9fbe7f15c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/027.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: doc write added script with content, .src set later</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ document.write('<script>log(\'doc.write script\')<\/script>');
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ document.getElementsByTagName('script')[4].src='scripts/include-1.js';
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'doc.write script', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){
+ setTimeout(t.step_func(test), 100);
+ })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/028.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/028.html
new file mode 100644
index 0000000000..e383d4f1a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/028.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: javascript: URL</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <script>log('inline script #1');
+ window.location.replace('javascript:log(\'JS URL\')');
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [
+ ['inline script #1', 'end script #1', 'JS URL', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'JS URL']]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/030.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/030.html
new file mode 100644
index 0000000000..f01c257e0d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/030.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: javascript: URL in HREF, onclick handler</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <p><a href="javascript:log('JS URL')" onclick="log('click event');return true;"></a></p>
+ <script>log('inline script #1');
+ if(document.links[0].click){
+ document.links[0].click();
+ }else{
+ var evt = document.createEvent("MouseEvents");
+ evt.initMouseEvent("click", true, true, window,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ document.links[0].dispatchEvent(evt);
+ }
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ var w = window;
+ onload = function() {setTimeout(
+ t.step_func(function() {
+ w.assert_any(w.assert_array_equals, w.eventOrder,
+ [['inline script #1', 'click event', 'end script #1', 'JS URL', 'inline script #2'],
+ ['inline script #1', 'click event', 'end script #1', 'inline script #2', 'JS URL']]);
+ t.done();
+ }), 200);
+ }
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/031.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/031.html
new file mode 100644
index 0000000000..3ddb36ab8d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/031.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: focus and blur events</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <form><input type="button" onclick="log('click event')" onfocus="log('focus event')" onblur="log('blur event')"><input type="button" onfocus="log('focus el 2 event')" onblur="log('blur event')"></form>
+ <script>log('inline script #1');
+ document.forms[0][0].focus();
+ document.forms[0][1].click();
+ document.forms[0][1].focus();
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log('inline script #2');
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'focus event', 'blur event', 'focus el 2 event', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){setTimeout(t.step_func(test), 200);})
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/032.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/032.html
new file mode 100644
index 0000000000..da3969740c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/032.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: innerHTML and scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ // script added with innerHTML should not run..
+ document.getElementsByTagName('div')[1].innerHTML = '<script>log("innerHTML script runs")<\/script><script src="scripts/include-1.js"><\/script>';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){setTimeout(t.step_func(test), 200);})
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/033.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/033.html
new file mode 100644
index 0000000000..5c41effcde
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/033.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: innerHTML and scripts moved in DOM</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ // script added with innerHTML should not run..
+ document.getElementsByTagName('div')[0].innerHTML = '<script>log("innerHTML script runs")<\/script><script src="scripts/include-1.js"><\/script>';
+ try{
+ document.body.appendChild( document.getElementsByTagName('div')[0].firstChild );
+ document.body.appendChild( document.getElementsByTagName('div')[0].firstChild );
+ }catch(e){
+ log('ERROR while testing');
+ }
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){setTimeout(t.step_func(test), 200);})
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/034.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/034.html
new file mode 100644
index 0000000000..13664253a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/034.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: innerHTML adding frames with JS in</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ document.getElementsByTagName('div')[1].innerHTML = '<iframe src="pages/helloworld.html"></iframe>';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2', 'frame/popup script']);
+ t.done();
+ }
+ onload = t.step_func(function(){setTimeout(t.step_func(test), 200);})
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/035.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/035.html
new file mode 100644
index 0000000000..406c3c548f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/035.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: innerHTML adding frames with JS in and moving scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ document.getElementsByTagName('div')[1].innerHTML = '<iframe src="pages/helloworld.html"></iframe>';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2', 'frame/popup script']);
+ t.done();
+ /*, ['inline script #1', 'end script #1', 'frame/popup script', 'inline script #2'] */
+ }
+ onload = t.step_func(function(){
+ try{
+ document.body.appendChild(document.importNode( top.frames[0].document.getElementsByTagName('script')[0], true ));
+ }catch(e){ log('ERROR - tested functionality not supported'); }
+ setTimeout(t.step_func(test), 200);
+ });
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/036.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/036.html
new file mode 100644
index 0000000000..113541dab9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/036.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM cloning</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ testlib.addScript('log(\'head script #1\')', {}, document.getElementsByTagName('head')[0], true);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ try{
+ var script = document.getElementsByTagName('script')[0].cloneNode(true);
+ document.body.appendChild(script);
+ }catch(e){ log('ERROR - tested functionality not supported'); }
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'head script #1', 'end script #1', 'inline script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(function(){
+ setTimeout(t.step_func(test), 200);
+ });
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/037.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/037.html
new file mode 100644
index 0000000000..15bd8a96e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/037.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM movement with appendChild, inline</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ var script = testlib.addScript('log(\'head script #1\')', {}, document.getElementsByTagName('head')[0], true);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ try{
+ document.body.appendChild(script);
+ }catch(e){ log('ERROR - tested functionality not supported'); }
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'head script #1', 'end script #1', 'inline script #2' ]);
+ t.done();
+ }
+ onload = t.step_func(function(){
+ setTimeout(t.step_func(test), 200);
+ });
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/038.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/038.html
new file mode 100644
index 0000000000..db6cec3520
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/038.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM movement with appendChild, external</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ var script = testlib.addScript('', { 'src':'scripts/include-1.js' }, document.getElementsByTagName('head')[0], true);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ try{
+ document.body.appendChild(script);
+ }catch(e){ log('ERROR - tested functionality not supported'); }
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done();
+ }
+ onload = function() {
+ setTimeout(t.step_func(test), 200);
+ };
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/039.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/039.html
new file mode 100644
index 0000000000..3720e24866
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/039.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: IFRAMEs added with DOM</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ for( var i=0; i<2; i++ ){
+ var iframe=document.createElement('iframe');
+ document.getElementsByTagName('div')[1].appendChild(iframe);
+ iframe.src='pages/helloworld.html?'+i+'&'+Math.random();
+ }
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ promise_test(() => {
+ const frames = document.querySelectorAll("iframe");
+ return Promise.all([
+ new Promise(resolve => window.addEventListener('load', resolve)),
+ new Promise(resolve => frames[0].addEventListener('load', resolve)),
+ new Promise(resolve => frames[1].addEventListener('load', resolve)),
+ ]).then(() => {
+ assert_equals(eventOrder.length, 5);
+ assert_array_equals(
+ eventOrder.slice(0, 3),
+ ['inline script #1', 'end script #1', 'inline script #2'],
+ "inline scripts should run first");
+ assert_in_array('frame/popup script 0', eventOrder.slice(3, 5), 'iframe should have loaded');
+ assert_in_array('frame/popup script 1', eventOrder.slice(3, 5), 'iframe should have loaded');
+ });
+ }, 'iframes should load asynchronously after inline script run');
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/040.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/040.html
new file mode 100644
index 0000000000..3cdf87f07b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/040.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: IFRAMEs added with DOM (innerHTML), javascript: URL</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>log('inline script #1');
+ document.getElementsByTagName('div')[1].innerHTML = '<iframe src="javascript:parent.log(\'JS URL\');\'<html><script>parent.log(\\\'frame script\\\')<\/script></html>\'"></iframe>';
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [
+ ['inline script #1', 'end script #1', 'JS URL', 'inline script #2', 'frame script'],
+ /* the following combination seems quite unlikely? */
+ ['inline script #1', 'end script #1', 'JS URL', 'frame script', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'JS URL', 'frame script']]);
+ t.done();
+ }
+ onload = t.step_func(function(){
+ setTimeout(t.step_func(test), 200);
+ });
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/041.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/041.html
new file mode 100644
index 0000000000..bce7041185
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/041.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write scripts that write scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.write( '<script>log(\'inline with doc.write #1\'); document.write(\'<script src="scripts/include-4.js"><\\\/script>\');log(\'end inline with doc.write\');<\/script>' );
+ document.write( '<script src="scripts/include-1.js">log(\'ignore this\')<\/script>' );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'inline with doc.write #1', 'end inline with doc.write', 'end script #1', 'include-4 before doc write', 'include-4 after doc write', 'external script before doc write', 'document.write external script', 'external script after doc write', 'external script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/042.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/042.html
new file mode 100644
index 0000000000..df3a2f88f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/042.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM mutation events when adding scripts: DOMNodeInserted </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.addEventListener( 'DOMNodeInserted', function(){ log('DOMNodeInserted'); }, false );
+ testlib.addScript('log(\'head script #1\')', {}, document.getElementsByTagName('head')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'head script #1', 'DOMNodeInserted', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/043.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/043.html
new file mode 100644
index 0000000000..bcfd90cba4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/043.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM mutation events when adding external scripts: DOMNodeInserted </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.addEventListener( 'DOMNodeInserted', function(){ log('DOMNodeInserted'); }, false );
+ testlib.addScript('', { src: 'scripts/include-1.js' }, document.getElementsByTagName('head')[0], false);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'DOMNodeInserted', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'DOMNodeInserted', 'end script #1', 'inline script #2', 'external script #1']]
+ );
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/044.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/044.html
new file mode 100644
index 0000000000..8d412079e4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/044.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM mutation events when adding scripts: DOMNodeInsertedIntoDocument </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('log(\'head script #1\')', {}, document.getElementsByTagName('head')[0], false, function(s){s.addEventListener( 'DOMNodeInsertedIntoDocument', function(){ log('DOMNodeInsertedIntoDocument'); }, false ); } );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'head script #1', 'DOMNodeInsertedIntoDocument', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/045.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/045.html
new file mode 100644
index 0000000000..254e0d1366
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/045.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: DOM mutation events when adding external scripts: DOMNodeInsertedIntoDocument </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('', {src:'scripts/include-1.js'}, document.getElementsByTagName('head')[0], false, function(s){s.addEventListener( 'DOMNodeInsertedIntoDocument', function(){ log('DOMNodeInsertedIntoDocument'); }, false);});
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ onload = t.step_func(function() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'DOMNodeInsertedIntoDocument', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'DOMNodeInsertedIntoDocument', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done();
+ });
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/046.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/046.html
new file mode 100644
index 0000000000..4f145d63e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/046.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: no readystatechange events when adding external scripts </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('', {src:'scripts/include-1.js', onreadystatechange:function() {log( 'readystatechange '+ this.readyState );}}, document.getElementsByTagName('head')[0], false );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/047.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/047.html
new file mode 100644
index 0000000000..88509e9d43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/047.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding and removing external script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=testlib.addScript('', {src:'scripts/include-1.js'}, document.getElementsByTagName('head')[0], false );
+ script.parentNode.removeChild(script);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/048.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/048.html
new file mode 100644
index 0000000000..8879f035d3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/048.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding inline script which sets its own .src </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=testlib.addScript('log(\'HEAD script start\');document.getElementsByTagName(\'script\')[0].src=\'scripts/include-1.js\';log(\'HEAD script end\')', {}, document.getElementsByTagName('head')[0], true );
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'HEAD script start', 'HEAD script end', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/049.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/049.html
new file mode 100644
index 0000000000..455a20c549
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/049.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding external script but removeAttribute( src ) before it runs</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=testlib.addScript('log(\'HEAD script\');', { src:'scripts/include-1.js' }, document.getElementsByTagName('head')[0], false );
+ script.removeAttribute('src');
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #1']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/050.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/050.html
new file mode 100644
index 0000000000..a400749f18
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/050.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding external script that removes all scripts from document</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=testlib.addScript('', { src:'scripts/include-5.js' }, document.getElementsByTagName('head')[0], false );
+ // caching might affect whether the below script runs or not. Adding Math.random() makes the test a bit more predictable? :-p
+ var script=testlib.addScript('', { src:'scripts/include-1.js?pipe=trickle(d1)&'+Math.random() }, document.getElementsByTagName('head')[0], false );
+ log('end script #1');
+ </script>
+ <script src="scripts/include-2.js?pipe=trickle(d4)"></script>
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ // Per-spec, non-blocking/async scripts can execute at any time.
+ // Therefore, there are two possibilities for the script order here.
+ // 1. inline script first, followed by include-5 (async), then
+ // external script #1 (slow async) and finally external #2
+ // (inline).
+ // 2. inline script, external '2, 'include 5', then include-1.
+ assert_array_equals(eventOrder.slice(0, 2), [
+ 'inline script #1', 'end script #1'
+ ]);
+ if (eventOrder[2] == 'include-5 before removing scripts') {
+ assert_array_equals(eventOrder.slice(3), [
+ 'include-5 after removing scripts', 'external script #1',
+ 'external script #2'
+ ]);
+ } else {
+ assert_array_equals(eventOrder.slice(2), ['external script #2',
+ 'include-5 before removing scripts',
+ 'include-5 after removing scripts',
+ 'external script #1'
+ ]);
+ }
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/051.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/051.html
new file mode 100644
index 0000000000..a0b674304f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/051.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: interaction of parsing and script execution - script added through DOM</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ testlib.addScript('', { src: 'scripts/count-script-tags.js' }, document.body, true);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'script tags in DOM: 5', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'inline script #2', 'script tags in DOM: 6']]);
+ t.done();
+ }
+ onload = function(){setTimeout(t.step_func(test), 100); }
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/052.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/052.html
new file mode 100644
index 0000000000..21a151cb79
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/052.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: interaction of parsing and script execution - external script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script src="scripts/count-script-tags.js"></script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['script tags in DOM: 4', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(function(){setTimeout(t.step_func(test), 100); })
+ </script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/053.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/053.html
new file mode 100644
index 0000000000..810197437d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/053.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding external script that removes itself from document when loading</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=testlib.addScript('', { src:'scripts/include-1.js', onload:function() {this.parentNode.removeChild(this);log('removed ' + this.localName);} }, document.getElementsByTagName('body')[0], true );
+ log('end script #1');
+ </script>
+ <script src="scripts/include-2.js"></script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'end script #1', 'external script #1', 'removed script', 'external script #2', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'external script #2', 'external script #1', 'removed script', 'inline script #2'],
+ ['inline script #1', 'end script #1', 'external script #2', 'inline script #2', 'external script #1', 'removed script']]
+ );
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/054.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/054.html
new file mode 100644
index 0000000000..29ede23414
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/054.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: removing newly inserted script from DOMNodeInserted handler - external script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.addEventListener( 'DOMNodeInserted', function listener(e){
+ log('DOMNodeInserted event');
+ e.target.parentNode.removeChild(e.target);
+ document.removeEventListener('DOMNodeInserted', listener);
+ }, false );
+ var script=testlib.addScript('', { src:'scripts/include-1.js?'+Math.random() }, document.getElementsByTagName('body')[0], true );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'DOMNodeInserted event', 'end script #1', 'inline script #2', 'external script #1'],
+ ['inline script #1', 'DOMNodeInserted event', 'end script #1', 'external script #1', 'inline script #2']]);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/055.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/055.html
new file mode 100644
index 0000000000..c837d78174
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/055.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: removing newly inserted script from DOMNodeInserted handler - inline script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ document.addEventListener( 'DOMNodeInserted', function listener(e){
+ log('DOMNodeInserted event');
+ e.target.parentNode.removeChild(e.target);
+ document.removeEventListener('DOMNodeInserted', listener);
+ }, false );
+ var script=testlib.addScript('log(\'added script\')', { }, document.getElementsByTagName('body')[0], true );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'added script', 'DOMNodeInserted event', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/056.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/056.html
new file mode 100644
index 0000000000..e2d0868034
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/056.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: appending code to initially empty SCRIPT tag in DOM </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ script.appendChild( document.createTextNode('log("injected script code");') );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ // Test asserts the injected script should run
+ assert_array_equals(eventOrder, ['inline script #1', 'injected script code', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/057.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/057.html
new file mode 100644
index 0000000000..4dc8e1384e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/057.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: appending code to initially non-empty SCRIPT tag in DOM (whitespace only) </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>
+ </script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ script.appendChild( document.createTextNode('log("injected script code");') );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/058.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/058.html
new file mode 100644
index 0000000000..15deb785c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/058.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: appending code to initially non-empty SCRIPT tag in DOM (comment only) </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>/**/</script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ script.appendChild( document.createTextNode('log("injected script code");') );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/059.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/059.html
new file mode 100644
index 0000000000..b3a34367b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/059.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: appending code to initially non-empty SCRIPT tag in DOM after removing its initial child </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>/**/</script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ script.removeChild(script.firstChild);
+ script.appendChild( document.createTextNode('log("injected script code");') );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/060.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/060.html
new file mode 100644
index 0000000000..905dfe233f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/060.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: appending code to initially non-empty SCRIPT tag in DOM after setting textContent/innerHTML</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>log('HEAD script');</script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ script.innerHTML='';
+ script.appendChild( document.createTextNode('log("injected script code 1");') );
+ script.textContent='';
+ script.appendChild( document.createTextNode('log("injected script code 2");') );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['HEAD script', 'inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/061.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/061.html
new file mode 100644
index 0000000000..9950b1c7ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/061.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: cloneNode and script execution</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>log('HEAD script');</script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ document.body.appendChild( script.cloneNode(true) );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['HEAD script', 'inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/062.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/062.html
new file mode 100644
index 0000000000..c5e0ee2d46
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/062.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: cloneNode (shallow) and script execution</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>log('HEAD script');</script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ document.body.appendChild( script.cloneNode(false) ).appendChild(document.createTextNode('log("clone");'));
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['HEAD script', 'inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/063.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/063.html
new file mode 100644
index 0000000000..6824074a8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/063.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: cloneNode (deep) of the currently executing script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>log('HEAD script');
+ if( !window.already_cloned ){
+ window.already_cloned=true;
+ var script=document.getElementsByTagName('script')[3];
+ document.getElementsByTagName('head')[0].appendChild( script.cloneNode(true) );
+ }
+ </script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['HEAD script', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/064.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/064.html
new file mode 100644
index 0000000000..ceedc3da2d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/064.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: cloneNode with external script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script src="scripts/include-1.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3];
+ document.body.appendChild( script.cloneNode(true) );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['external script #1', 'inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/065.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/065.html
new file mode 100644
index 0000000000..5859dc9e0b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/065.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: cloneNode with external script, changed .src</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script src="scripts/include-1.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3].cloneNode(true);
+ script.src='scripts/include-2.js'
+ document.body.appendChild( script );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['external script #1', 'inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/066.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/066.html
new file mode 100644
index 0000000000..a8e346dfd8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/066.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: cloneNode with external script, removing .src and adding content</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script src="scripts/include-1.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=document.getElementsByTagName('script')[3].cloneNode(true);
+ script.removeAttribute('src');
+ script.appendChild(document.createTextNode( 'log("cloned script");' ));
+ document.body.appendChild( script );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['external script #1', 'inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/067.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/067.html
new file mode 100644
index 0000000000..7be0fd0ab0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/067.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: is a script with syntax error marked as "has run"? </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>setup({allow_uncaught_exception:true})
+ var t = async_test()
+ </script>
+ <script>
+ log(This script will never run..
+ </script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>t.step(function() {
+ log('inline script #1');
+ var script=document.getElementsByTagName('script')[3].cloneNode(true);
+ script.removeChild(script.firstChild);
+ script.appendChild(document.createTextNode( 'log("cloned script");' ));
+ document.body.appendChild( script );
+ log('end script #1');
+ })
+ </script>
+ <script type="text/javascript">
+ t.step(function() {
+ log( 'inline script #2' );
+ });
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+</script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/068.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/068.html
new file mode 100644
index 0000000000..a58158b6c0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/068.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: external script and parsing of markup added with document.write </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ log('inline script #1');
+ document.write('<script src="scripts/find-foo.js">log(\'inline code in external script (not expected to run!!)\')<\/script>' + '<div id="foo"></div>');
+ log('end script #1');
+
+ var t = async_test()
+
+
+ function test() {
+ if(!window.findFooLoaded) {
+ return setTimeout(t.step_func(test),200);
+ }
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'found #foo element: NO']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/069.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/069.html
new file mode 100644
index 0000000000..4d4aed2658
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/069.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: external files added through DOM should not block further parsing while loading</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>
+ testlib.addScript('',{src:'scripts/find-body.js?pipe=trickle(d1)'},document.getElementsByTagName('head')[0], true );
+ </script>
+</head>
+<body>
+ <script>
+ testlib.addScript('', {src:'scripts/find-foo.js?pipe=trickle(d1)'}, document.getElementsByTagName('head')[0], true);
+ </script>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <p><span id="foo"></span></p>
+
+ <script type="text/javascript">
+ var t = async_test()
+
+ function test() {
+ if(!(window.findFooLoaded && window.findBodyLoaded)) {
+ return setTimeout(t.step_func(test), 200);
+ }
+ assert_any(assert_array_equals, eventOrder,
+ [['document.body: <BODY>', 'found #foo element: YES'],
+ ['found #foo element: YES', 'document.body: <BODY>']]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/070.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/070.html
new file mode 100644
index 0000000000..4b82a9e83f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/070.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write into IFRAME a script that adds a SCRIPT through DOM</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <iframe></iframe>
+
+ <script type="text/javascript">
+ var doc = document.getElementsByTagName('iframe')[0].contentDocument;
+ doc.open();
+
+ log("calling document.write");
+ doc.write('<script>top.log("inline script #1");'+
+ 'var s=document.createElement("script");'+
+ 's.src="scripts/include-6.js?'+new Date().getTime()+'";'+
+ 'document.getElementsByTagName("head")[0].appendChild(s);'+
+ '<\/script>'+
+ '<div id="foo"></div>'+
+ '<script>top.log("inline script #2");<\/script>'
+ );
+
+ log("calling document.close");
+ doc.close();
+
+ var t = async_test()
+
+
+ function test() {
+ if(!window.include6Loaded) {
+ return setTimeout(t.step_func(test),200);
+ }
+ assert_array_equals(eventOrder, ['calling document.write', 'inline script #1', 'inline script #2', 'calling document.close', 'external script (#foo found? YES)']);
+ t.done();
+ }
+
+ onload = t.step_func(test)
+ </script>
+</head>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/071.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/071.html
new file mode 100644
index 0000000000..802c4a8ce3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/071.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write several scripts into IFRAME </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <iframe style="width:1px;height:1px"></iframe>
+
+ <script type="text/javascript">
+ var doc = document.getElementsByTagName('iframe')[0].contentDocument;
+ doc.open();
+
+ var html = '<html><head><title>test</title></head>'+
+ '<script>top.log("inline script #1");'+
+ '<\/script>'+
+ /* made url unique because Chrome will change
+ order depending on file cached status */
+ '<script src="scripts/include-6.js?'+new Date().getTime()+'"><\/script>'+
+ '</head>'+
+ '<body>'+
+ '<div id="foo"></div>'+
+ '</body></html>'+
+ '<script>top.log("inline script #2");<\/script>';
+ log("calling document.write");
+ doc.write(html);
+
+ log("calling document.close");
+ doc.close();
+
+ var t = async_test()
+
+
+ function test() {
+ if( !window.include6Loaded )return setTimeout(t.step_func(test),200); // try checking again if external script didn't run yet
+ assert_array_equals(eventOrder, ['calling document.write',
+ 'inline script #1',
+ 'calling document.close',
+ 'external script (#foo found? NO)',
+ 'inline script #2'
+ ]);
+ t.done();
+ }
+
+ onload = t.step_func(test)
+ </script>
+</head>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/072.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/072.html
new file mode 100644
index 0000000000..e502a35736
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/072.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write into IFRAME a script that creates new inline script in parent </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <iframe style="width:1px;height:1px"></iframe>
+
+ <script type="text/javascript">
+ var doc = document.getElementsByTagName('iframe')[0].contentDocument;
+ doc.open();
+
+ var html = '<html><head><title>test</title></head>'+
+ '<script>top.log("inline script #1");'+
+ '<\/script>'+
+ '</head>'+
+ '<body>'+
+ '<div id="foo"></div>'+
+ '</body></html>'+
+ '<script>top.testlib.addScript( \'log("inline script added to parent")\', null, top.document.body, true )<\/script>';
+ log("calling document.write");
+ doc.write(html);
+
+ log("calling document.close");
+ doc.close();
+
+ var t = async_test()
+
+
+ function test() {
+ assert_array_equals(eventOrder, ['calling document.write',
+ 'inline script #1',
+ 'inline script added to parent',
+ 'calling document.close',
+ ]);
+ t.done();
+ }
+
+ onload = t.step_func(test)
+ </script>
+</head>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/073.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/073.html
new file mode 100644
index 0000000000..6f65c4ae19
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/073.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write into IFRAME a script that creates new external script in parent </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <iframe style="width:1px;height:1px"></iframe>
+
+ <script type="text/javascript">
+ var doc = document.getElementsByTagName('iframe')[0].contentDocument;
+ doc.open();
+
+ var html = '<html><head><title>test</title></head>'+
+ '<script>top.log("inline script #1");'+
+ '<\/script>'+
+ '</head>'+
+ '<body>'+
+ '<div id="foo"></div>'+
+ '</body></html>'+
+ '<script>top.testlib.addScript( \'\', { src:\'scripts/include-1.js\' }, top.document.body, true )<\/script>';
+ log("calling document.write");
+ doc.write(html);
+
+ log("calling document.close");
+ doc.close();
+
+ var t = async_test()
+
+
+ function test() {
+
+ assert_array_equals(eventOrder, ['calling document.write',
+ 'inline script #1',
+ 'calling document.close',
+ 'external script #1'
+ ]);
+
+ t.done();
+ }
+
+ onload = t.step_func(test)
+ </script>
+</head>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/074.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/074.html
new file mode 100644
index 0000000000..70d7b88b74
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/074.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: document.write into IFRAME a script that creates new inline script in parent that again adds script to IFRAME </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <iframe style="width:1px;height:1px"></iframe>
+
+ <script type="text/javascript">
+ var doc = document.getElementsByTagName('iframe')[0].contentDocument;
+ doc.open();
+ var str1='';
+ var html = '<html><head><title>test</title></head>'+
+ '<script>top.log("inline script #1");'+
+ '<\/script>'+
+ '</head>'+
+ '<body>'+
+ '<script>top.testlib.addScript( \'top.log("inline script added to parent");top.doc.write( "<script>top.log(\\\\"inline script added to iframe\\\\")<\\\/script>");\', null, top.document.body, true ) <\/script>'+
+ '</body></html>';
+ log("calling document.write");
+ doc.write(html);
+
+ log("calling document.close");
+ doc.close();
+
+ var t = async_test()
+
+
+ function test() {
+ assert_array_equals(eventOrder, ['calling document.write',
+ 'inline script #1',
+ 'inline script added to parent',
+ 'inline script added to iframe',
+ 'calling document.close',
+ ]);
+ t.done();
+ }
+
+ onload = t.step_func(test)
+ </script>
+</head>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/075.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/075.html
new file mode 100644
index 0000000000..40ec9bbb6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/075.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>dispatchEvent from child frame during document.write :-o </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ </head>
+ <body onclick="log('click event')">
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <iframe></iframe>
+ <script>
+ var doc = document.getElementsByTagName("iframe")[0].contentDocument;
+ doc.open("text/html");
+ doc.write('<html><head><title>event dispatcher</title></head><body>Before script<script>top.log("inline script before event");var ev = parent.document.createEvent("MouseEvents");ev.initMouseEvent("click", true, false, null, 0, 0, 0, 0, 0, false, false, false, false, 0, null);parent.document.body.dispatchEvent(ev);top.log("inline script after event");</sc'+'ript> After script</body>');
+ log( 'end main script' );
+
+
+ </script>
+
+<script>
+ var t = async_test()
+
+ function test() {
+ if(test.ran)return; test.ran=true;
+
+ assert_array_equals(eventOrder, ['inline script before event',
+ 'click event',
+ 'inline script after event',
+ 'end main script'
+ ]);
+ doc.close();
+ t.done();
+}
+
+ onload = t.step_func(test)
+ /* onload doesn't fire in this test, a fallback.. */
+ setTimeout(t.step_func(test), 800 );
+</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/076.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/076.html
new file mode 100644
index 0000000000..2b8b692d92
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/076.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding and removing external and inline scripts </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>log('inline script #1');
+ var script=testlib.addScript('', {src:'scripts/include-1.js', onload:function(e){ e.target.parentNode.removeChild(e.target); }}, document.getElementsByTagName('head')[0], false );
+ var script=testlib.addScript( 'log( "dynamically added inline script" )', null, document.getElementsByTagName('head')[0], false );
+ script.parentNode.removeChild(script);
+ log('end script #1');
+ </script>
+
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ }
+ onload = t.step_func(function() {
+ assert_any(assert_array_equals, eventOrder, [['inline script #1', 'dynamically added inline script', 'end script #1', 'external script #1', 'inline script #2'],
+ ['inline script #1', 'dynamically added inline script', 'end script #1', 'inline script #2', 'external script #1']]);
+ t.done();
+ })
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/077.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/077.html
new file mode 100644
index 0000000000..dbcd16bea5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/077.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title> adding several types of scripts through the DOM and removing some of them confuses scheduler </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script type="text/javascript">
+ var head = document.getElementsByTagName('head')[0];
+ function createScript(url, contents) {
+ props = {};
+ if (url) {
+ props.src = url;
+ }
+ return testlib.addScript(contents, props, head, false);
+ }
+ var t = async_test()
+
+ function test() {
+ var script = createScript('data:text\/javascript,log("Script %231 ran")');
+ var script2 = createScript('','log("Script #2 ran")');
+ if(script2) {
+ head.removeChild(script2);
+ }
+ var script3 = createScript('data:text\/javascript, log("Script %233 ran"); createScript(\'\', \'log("Script %234 ran")\')');
+ if(script3) {
+ head.removeChild(script3);
+ }
+ setTimeout(t.step_func(function(){
+ assert_array_equals(eventOrder, ['Script #2 ran', 'Script #1 ran', 'Script #3 ran','Script #4 ran']);
+ t.done();
+ }), 400);
+
+ };
+ onload = t.step_func(test)
+ </script>
+ </head>
+ <body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ </body>
+</html*>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/078.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/078.html
new file mode 100644
index 0000000000..da4db4a2e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/078.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> adding several types of scripts through the DOM and removing some of them confuses scheduler (slow-loading scripts) </title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="testlib/testlib.js"></script>
+<script type="text/javascript">
+ setup({ explicit_done: true });
+ var head = document.getElementsByTagName('head')[0];
+ function createScript(url, contents) {
+ props = {};
+ if (url) {
+ props.src = url;
+ }
+ return testlib.addScript(contents, props, head, false);
+ }
+ var t = async_test()
+
+ function test() {
+ document.getElementById("log").textContent = "Please wait..."
+ var url = 'scripts/include-1.js?pipe=trickle(d1)';
+ var script = createScript(url);
+ var script2 = createScript('', 'log("Script #2 ran")');
+ head.removeChild(script2);
+ var url = 'scripts/include-2.js?pipe=trickle(d2)';
+ var script3 = createScript(url);
+ head.removeChild(script3);
+
+ setTimeout(t.step_func(function () {
+ done();
+ assert_array_equals(eventOrder, ['Script #2 ran', 'external script #1', 'external script #2']);
+ t.done();
+ }), 5500);
+
+ };
+ onload = t.step_func(test)
+</script>
+</head>
+<body>
+<div id="log">FAILED (This TC requires JavaScript enabled)</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/079.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/079.html
new file mode 100644
index 0000000000..8d684cebf2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/079.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title> setting location to javascript URL from event handler </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="testlib/testlib.js"></script>
+<script type="text/javascript">
+log('inline script #1');
+var t = async_test()
+
+onload = t.step_func(function() {
+ log('onload handler');
+ document.getElementById("log").textContent = 'please wait...';
+ window.location='javascript:log("javascript: URL")';
+ setTimeout(t.step_func(function(){
+ log('timeout');
+ assert_array_equals(eventOrder, ['inline script #1', 'onload handler', 'onload ends', 'javascript: URL', 'timeout']);
+ t.done();
+ }), 200);
+ log('onload ends');
+});
+</script>
+</head>
+<body>
+<div id="log">FAILED (This TC requires JavaScript enabled)</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/081.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/081.html
new file mode 100644
index 0000000000..1b9bc991c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/081.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: slow loading external script added with DOM (appendChild)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>
+ var t = async_test()
+ log('inline script #1');
+ testlib.addScript('', { src:'scripts/include-1.js?pipe=trickle(d1)&'+Math.random() }, document.getElementsByTagName('head')[0], false );
+ log('end script #1');
+ </script>
+ <script src="scripts/include-2.js"></script>
+ <script>
+ log( 'inline script #2' );
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'external script #2', 'inline script #2', 'external script #1']);
+ t.done();
+ }
+ onload = t.step_func(function() {
+ setTimeout(t.step_func(test), 12);
+ });
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/082.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/082.html
new file mode 100644
index 0000000000..3e88fc73d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/082.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: multiple slow loading external scripts added with DOM (appendChild)</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div></div>
+ <script>
+
+ log('inline script #1');
+ function scriptLoadListener(){
+ log( 'load on '+this.src.match( /include-\d\.js/ ) );
+ }
+ var script=testlib.addScript('', { src:'scripts/include-1.js?pipe=trickle(d1)&' + Math.random(), onload:scriptLoadListener }, document.getElementsByTagName('head')[0], false );
+ var script=testlib.addScript('', { src:'scripts/include-2.js?pipe=trickle(d3)&' + Math.random(), onload:scriptLoadListener }, document.getElementsByTagName('head')[0], false );
+ var script=testlib.addScript('', { src:'scripts/include-7.js?pipe=trickle(d2)&' + Math.random() , onload:scriptLoadListener }, document.getElementsByTagName('head')[0], false );
+ log('end script #1');
+ </script>
+ <script type="text/javascript">
+ log('inline script #2');
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end script #1', 'inline script #2', 'external script #1', 'load on include-1.js', 'external script #7', 'load on include-7.js', 'external script #2', 'load on include-2.js']);
+ t.done();
+ }
+ onload = function() {setTimeout(t.step_func(test), 12)};
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/083.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/083.html
new file mode 100644
index 0000000000..2ac0015c8b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/083.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: event listener defined by script in a document in history</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <iframe src="about:blank"></iframe>
+ <script>
+ log('inline script #1');
+ function fireFooEvent(){
+ var evt=document.createEvent('Event');
+ evt.initEvent('foo', true, true);
+ document.dispatchEvent(evt);
+ }
+ var doc = frames[0].document;
+ doc.open('text/html');
+ doc.write('<script>top.log("IFRAME script");top.document.addEventListener("foo", function(e){ top.log("event: "+e.type); }, false)<\/script>');
+ log('end script #1');
+ </script>
+ <script>
+ fireFooEvent();
+ frames[0].location='about:blank'; // returning to about:blank should de-activate document that defined event listener..?
+ </script>
+ <script>
+ fireFooEvent();
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_equals(frames[0].location.toString(), "about:blank");
+ assert_array_equals(eventOrder, ['inline script #1',
+ 'IFRAME script',
+ 'end script #1',
+ 'event: foo',
+ 'inline script #2'
+ ]);
+ t.done();
+ }
+ onload = function() {setTimeout(t.step_func(function() {fireFooEvent(); test()}), 80)};
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/084.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/084.html
new file mode 100644
index 0000000000..3027fa1994
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/084.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: event listener defined by script in a removed IFRAME</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <iframe src="about:blank"></iframe>
+ <script>
+ log('inline script #1');
+ function fireFooEvent(){
+ var evt=document.createEvent('Event');
+ evt.initEvent('foo', true, true);
+ document.dispatchEvent(evt);
+ }
+ var doc=frames[0].document;
+ doc.open( 'text/html' );
+ doc.write( '<script>top.log("IFRAME script");top.document.addEventListener("foo", function(e){ top.log("event: "+e.type); }, false)<\/script>' );
+ log('end script #1');
+ </script>
+ <script>
+ fireFooEvent();
+ frames[0].frameElement.parentNode.removeChild( frames[0].frameElement ); // removing the IFRAME should de-activate document that defined event listener..?
+ </script>
+ <script>
+ fireFooEvent();
+ </script>
+ <script type="text/javascript">
+ log( 'inline script #2' );
+ var t = async_test()
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1',
+ 'IFRAME script',
+ 'end script #1',
+ 'event: foo',
+ 'inline script #2'
+ ]);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/085.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/085.html
new file mode 100644
index 0000000000..6577527a8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/085.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: async script and slow-loading defer script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script src="scripts/include-1.js?pipe=trickle(d1)" defer></script>
+ <script src="scripts/include-2.js" async></script>
+
+ <script type="text/javascript">
+ var t = async_test();
+ function test() {
+ assert_array_equals(eventOrder, ['external script #2', 'external script #1']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/086.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/086.html
new file mode 100644
index 0000000000..4ccd16de89
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/086.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: async script and slow-loading async script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script src="scripts/include-1.js?pipe=trickle(d2)" async></script>
+ <script src="scripts/include-2.js" async></script>
+
+ <script type="text/javascript">
+ var t = async_test();
+ function test() {
+ assert_array_equals(eventOrder, ['external script #2', 'external script #1']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/087.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/087.html
new file mode 100644
index 0000000000..8e225f48ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/087.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: multiple defer scripts, one slow loading</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script src="scripts/include-1.js?pipe=trickle(d2)" defer></script>
+ <script src="scripts/include-2.js" defer></script>
+
+ <script type="text/javascript">
+ var t = async_test();
+ function test() {
+ assert_array_equals(eventOrder, ['external script #1', 'external script #2']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/088.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/088.html
new file mode 100644
index 0000000000..f41f3d5be2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/088.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: multiple scripts with defer and async attributes</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script src="scripts/include-1.js?pipe=trickle(d2)" defer async></script>
+ <script src="scripts/include-2.js" defer async></script>
+
+ <script type="text/javascript">
+ var t = async_test();
+ function test() {
+ assert_array_equals(eventOrder, ['external script #2', 'external script #1']);
+ t.done();
+ }
+ onload = t.step_func(test);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/089.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/089.html
new file mode 100644
index 0000000000..9ed5e0e1da
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/089.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: async attribute on inline script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script async>
+ var t = async_test();
+ log('inline script #1');
+ t.step(function() {
+ assert_array_equals(eventOrder, ['inline script #1']);
+ });
+ </script>
+ <script async>
+ log('inline script #2');
+ t.step(function() {
+ assert_array_equals(eventOrder, ['inline script #1', 'inline script #2']);
+ });
+ </script>
+
+ <script>
+ log('inline script #3');
+ t.step(function() {
+ assert_array_equals(eventOrder, ['inline script #1', 'inline script #2', 'inline script #3']);
+ });
+ onload = function() {t.done()};
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/090.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/090.html
new file mode 100644
index 0000000000..17d1d1effe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/090.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: defer attribute on inline script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script defer>
+ var t = async_test();
+ log('inline script #1');
+ t.step(function() {
+ assert_array_equals(eventOrder, ['inline script #1']);
+ });
+ </script>
+ <script defer>
+ log('inline script #2');
+ t.step(function() {
+ assert_array_equals(eventOrder, ['inline script #1', 'inline script #2']);
+ });
+ </script>
+
+ <script>
+ log('inline script #3');
+ t.step(function() {
+ assert_array_equals(eventOrder, ['inline script #1', 'inline script #2', 'inline script #3']);
+ });
+ onload = function() {t.done()};
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/091.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/091.html
new file mode 100644
index 0000000000..f706cd31f5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/091.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: force-async off on non-parser-inserted script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ var t = async_test();
+
+ sources = ["scripts/include-1.js?pipe=trickle(d2)", "scripts/include-2.js?pipe=trickle(d1)"];
+ sources.forEach(function(x) {
+ var script = document.createElement("script");
+ script.src = x;
+ t.step(function() {assert_equals(script.async, true, "async IDL attribute on script creation")});
+ script.async = false;
+ t.step(function() {assert_equals(script.async, false, "async IDL attribute after setting")});
+ t.step(function() {assert_equals(script.getAttribute("async"), null, "async content attribute after setting")});
+ document.head.appendChild(script);
+ });
+
+ onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ['external script #1', 'external script #2']);
+ t.done();
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/092.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/092.html
new file mode 100644
index 0000000000..40fda5a0c0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/092.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: defer script and slow-loading non-async external script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ var t = async_test();
+
+ var script = document.createElement("script");
+ script.src = "scripts/include-2.js?pipe=trickle(d2)";
+ script.async = false;
+ document.head.appendChild(script);
+
+ onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ['external script #1', 'external script #2']);
+ t.done();
+ });
+ </script>
+ <script defer src="scripts/include-1.js"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/094.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/094.html
new file mode 100644
index 0000000000..cc9d1bf0fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/094.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: parser-created defer script after document load</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <iframe id="myFrame"></iframe>
+
+ <script>
+ var t = async_test();
+ onload = t.step_func(function() {
+ var doc = document.getElementById("myFrame").contentDocument;
+ var win = document.getElementById("myFrame").contentWindow;
+ doc.open();
+ doc.write("<title> scheduler: parser-created defer script after document load</title><script src='/resources/testharness.js'><\/script><script src='/resources/testharnessreport.js'><\/script><script src='testlib/testlib.js'><\/script><script>var t=async_test()<\/script><div id=log></div><script defer src='data:text/javascript,parent.t.done();'><\/script>");
+ doc.close();
+ })
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/095.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/095.html
new file mode 100644
index 0000000000..5e3b388cf1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/095.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: slow-loading script added from defer blocking load event</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ var t = async_test();
+ function test() {
+ t.step(function() {
+ assert_array_equals(eventOrder, ['external script #8', 'external script #9']);
+ t.done();
+ });
+ }
+ //assert that the test is completed before onload fires
+ onload = t.step_func(function() {assert_unreached()});
+ </script>
+ <script defer src="scripts/include-8.js"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/096.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/096.html
new file mode 100644
index 0000000000..a2e15b782a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/096.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: defer script added from document.write relative to DOMContentLoaded</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ log("inline script #1");
+ document.write("<script defer src='scripts/include-1.js'><\/script>")
+ </script>
+ <script>
+ log("inline script #2");
+ var t = async_test();
+
+ addEventListener("DOMContentLoaded", t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "external script #1"]);
+ log("inline script #3");
+ }), false);
+
+ onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "external script #1", "inline script #3"]);
+ t.done();
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/097.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/097.html
new file mode 100644
index 0000000000..a31d49e5b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/097.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: slow-loading async script added from document.write</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ log("inline script #1");
+ document.write("<script async src='scripts/include-1.js?pipe=trickle(d2)'><\/script>")
+ </script>
+ <script>
+ log("inline script #2");
+ var t = async_test();
+
+ addEventListener("DOMContentLoaded", t.step_func(function() {assert_array_equals(eventOrder, ["inline script #1", "inline script #2"])}), false);
+
+ onload = t.step_func(
+ function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "external script #1"]);
+ t.done();
+ });
+
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/099.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/099.html
new file mode 100644
index 0000000000..987fcc7c71
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/099.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: defer adding iframe containing script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script defer src="scripts/include-11.js"></script>
+ <script>
+ var t = async_test();
+
+ onload = t.step_func(function() {assert_array_equals(eventOrder, ["external script before adding iframe", "script in iframe"]); t.done();});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/101.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/101.html
new file mode 100644
index 0000000000..b868f9a447
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/101.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: defer script after initial onload event</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <iframe id="myFrame"></iframe>
+
+ <script>
+ var t = async_test();
+ onload = t.step_func(
+ function() {
+ var doc = document.getElementById("myFrame").contentDocument;
+ var win = document.getElementById("myFrame").contentWindow;
+ doc.open();
+ doc.write("<title> scheduler: defer script after initial onload event</title><script src='testlib/testlib.js'><\/script><div id='log'>document.written content</div><script>log('inline script #1');<\/script><script src='scripts/include-1.js'><\/script><script defer src='scripts/include-2.js'><\/script>");
+ doc.close();
+ //Note that the *window* object has changed but the *global scope* of the script has not.
+ var run_t = window.t.step_func(function() {
+ if (!win.eventOrder || win.eventOrder.length != 3) {
+ window.setTimeout(run_t, 100);
+ return;
+ }
+ window.assert_array_equals(win.eventOrder, ['inline script #1', 'external script #1', 'external script #2']);
+ window.t.done();
+ });
+ run_t();
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/102.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/102.html
new file mode 100644
index 0000000000..439023833f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/102.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: defer script after initial onload event</title>
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ onload = function() {
+ document.open();
+ document.write("<title> scheduler: defer script after initial onload event</title><script src='/resources/testharness.js'><\/script><script src='/resources/testharnessreport.js'><\/script><script src='testlib/testlib.js'><\/script><div id='log'>document.written content</div><script>var t = async_test(); log('inline script #1')<\/script><script src='scripts/include-1.js'><\/script><script async src='scripts/include-2.js'><\/script>");
+ document.close();
+ window.setTimeout(function() {
+ window.t.step(function() {
+ window.assert_any(window.assert_array_equals, window.eventOrder,
+ [['inline script #1', 'external script #1', 'external script #2'],
+ ['inline script #1', 'external script #2', 'external script #1']]);
+ window.t.done();
+ })},
+ 1000);
+ };
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/103.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/103.html
new file mode 100644
index 0000000000..f619472a4f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/103.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: removing defer attribute at runtime</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script id="defer-script" defer src="scripts/include-2.js"></script>
+ <script src="scripts/include-1.js"></script>
+
+ <script>
+ var t = async_test();
+
+ t.step(function() {
+ document.getElementById("defer-script").removeAttribute("defer");
+ });
+
+ var ran_defer_check = false;
+
+ document.addEventListener("readystatechange", t.step_func(function () {
+ if (document.readyState == "interactive") {
+ ran_defer_check = true;
+ assert_array_equals(eventOrder, ["external script #1"]);
+ }
+ }), false);
+
+ addEventListener("load", t.step_func(function () {
+ assert_true(ran_defer_check);
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+ }), false);
+
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/104.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/104.html
new file mode 100644
index 0000000000..95a5a22237
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/104.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding defer attribute at runtime</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script id="defer-script" src="scripts/include-1.js"></script>
+ <script src="scripts/include-2.js"></script>
+
+ <script>
+ var t = async_test();
+
+ t.step(function() {
+ document.getElementById("defer-script").setAttribute("defer", "defer");
+ });
+
+ var ran_defer_check = false;
+
+ document.addEventListener("readystatechange", t.step_func(function () {
+ if (document.readyState == "interactive") {
+ ran_defer_check = true;
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ }
+ }), false);
+
+ addEventListener("load", t.step_func(function () {
+ assert_true(ran_defer_check);
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+ }), false);
+
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/105.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/105.html
new file mode 100644
index 0000000000..19be9e1d03
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/105.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: adding async attribute at runtime</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ var t = async_test();
+
+ var sources = ["scripts/include-1.js?pipe=trickle(d2)",
+ "scripts/include-2.js"]
+ var scripts = sources.map(function(x) {
+ var script = document.createElement("script");
+ script.src = x;
+ script.async = false;
+ document.body.appendChild(script);
+ return script;
+ });
+ scripts[0].async = true;
+
+ addEventListener("load", t.step_func(function () {
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+ }), false);
+
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-import.html
new file mode 100644
index 0000000000..451e218ef7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-import.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking defer scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script defer src="scripts/check-style-sheet.js"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-noimport.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-noimport.html
new file mode 100644
index 0000000000..704b880bcf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-defer-noimport.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking defer scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script defer src="scripts/check-style-sheet.js"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-import.html
new file mode 100644
index 0000000000..4fe526a274
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-import.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking external parser-blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script src="scripts/check-style-sheet.js"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-import.html
new file mode 100644
index 0000000000..ea873746e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-import.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking external module scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script src="scripts/check-style-sheet.js" type="module"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-noimport.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-noimport.html
new file mode 100644
index 0000000000..71c59fb4d6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-module-noimport.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking external module scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script src="scripts/check-style-sheet.js" type="module"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-noimport.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-noimport.html
new file mode 100644
index 0000000000..3694481b86
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-external-noimport.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking external parser-blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script src="scripts/check-style-sheet.js"></script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import-xhtml.xhtml
new file mode 100644
index 0000000000..3b2e8b5296
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import-xhtml.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Stylesheet in XHTML HEAD with @import blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)" />
+</head>
+<body>
+ <div id="test">Test</div>
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import.html
new file mode 100644
index 0000000000..b8afeda135
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-import.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-import.html
new file mode 100644
index 0000000000..d3f02ffd19
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-import.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking module scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script type="module">
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-noimport.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-noimport.html
new file mode 100644
index 0000000000..83cd29f267
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-module-noimport.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking module scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script type="module">
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport-xhtml.xhtml
new file mode 100644
index 0000000000..1cae3c9965
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport-xhtml.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Stylesheet in XHTML HEAD blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)" />
+</head>
+<body>
+ <div id="test">Test</div>
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport.html
new file mode 100644
index 0000000000..bd8ec8633e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/106-noimport.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)">
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-import.html
new file mode 100644
index 0000000000..0b572b0724
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-import.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking scripts document.write</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <!-- this stylesheet blocks scripts -->
+ <script>
+ test(function() {
+ document.write("<link rel='stylesheet' href='css/import.css?pipe=trickle(d2)'>");
+ // note that the pass condition here is not per spec (but does match implementations) as of 2012-06-26
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "static");
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-noimport.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-noimport.html
new file mode 100644
index 0000000000..ce57d1f1c5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/107-noimport.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: stylesheets blocking scripts document.write</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+
+ <!-- this stylesheet blocks scripts -->
+ <script>
+ test(function() {
+ document.write("<link rel='stylesheet' href='css/background.css?pipe=trickle(d2)'>");
+ // note that the pass condition here is not per spec (but does match implementations) as of 2012-06-26
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "static");
+ });
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/108.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/108.html
new file mode 100644
index 0000000000..79be9721d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/108.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: javascript URL in iframe</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log">Not tested</div>
+ <script>
+ var t = async_test();
+ var iframe_onload = false;
+
+ t.step(function() {
+ log('inline script #1');
+ document.write("<iframe src='javascript:void(top.log(&quot;iframe script #1&quot;));'></iframe>");
+ log('inline script #2')
+ })
+
+ onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "iframe script #1"]);
+ t.done();
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/109.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/109.html
new file mode 100644
index 0000000000..d103ffcbd7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/109.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: javascript URL in iframe, src set via DOM</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log">Not tested</div>
+ <script>
+ var t = async_test();
+
+ t.step(function() {
+ var iframe_onload = false;
+ log("inline script #1");
+ var iframe = document.createElement("iframe");
+ iframe.src = "javascript:void(top.log('JS URL'));";
+ log("inline script #2");
+ iframe.onload = function () { log("iframe onload") };
+ document.body.appendChild(iframe);
+ log("inline script #3");
+ })
+
+ onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "inline script #3", "JS URL", "iframe onload"]);
+ t.done();
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/110.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/110.html
new file mode 100644
index 0000000000..5affb9ed23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/110.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: removing defer script at runtime</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script id="defer-script" defer src="scripts/include-2.js"></script>
+ <script src="scripts/include-1.js"></script>
+
+ <script>
+ var t = async_test();
+
+ t.step(function() {
+ var s = document.getElementById("defer-script");
+ s.parentNode.removeChild(s);
+ });
+
+ addEventListener("load", t.step_func(function () {
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+ }), false);
+
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/111.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/111.html
new file mode 100644
index 0000000000..c932a7b95c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/111.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: removing async attribute at runtime</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script id="async-script" async src="scripts/include-2.js?pipe=trickle(d3)"></script>
+
+ <script>
+ var t = async_test();
+
+ t.step(function() {
+ document.getElementById("async-script").removeAttribute("async");
+ var s = document.createElement("script");
+ s.async = false;
+ s.src = "scripts/include-1.js";
+ document.body.appendChild(s);
+ });
+
+ addEventListener("load", t.step_func(function () {
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+ }), false);
+
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/112.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/112.html
new file mode 100644
index 0000000000..a0cc647e0d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/112.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: removing async attribute at runtime, script also has defer attribute</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script id="async-script" async defer src="scripts/include-1.js?pipe=trickle(d3)"></script>
+
+ <script>
+ var t = async_test();
+ document.getElementById("async-script").removeAttribute("async");
+
+ addEventListener("DOMContentLoaded", t.step_func(function () {
+ assert_array_equals(eventOrder, []);
+ }), false);
+
+ addEventListener("load", t.step_func(function () {
+ assert_array_equals(eventOrder, ["external script #1"]);
+ t.done();
+ }), false);
+
+ </script>
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/113.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/113.html
new file mode 100644
index 0000000000..32740be37e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/113.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: Altering DOM using innerHTML during parse </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script>
+ var t = async_test();
+
+ addEventListener("load", t.step_func(function () {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2"]);
+ t.done();
+ }), false);
+
+ </script>
+ <div id="container">
+ <script>t.step(function() {
+ log("inline script #1");
+ document.getElementById("container").innerHTML = "";
+ });
+ </script>
+ <script>t.step(function() {log("inline script #2")});</script>
+ </div>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/114.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/114.html
new file mode 100644
index 0000000000..ce3733208b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/114.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: Changing src of defer script before it runs </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script id="defer-script" defer src="scripts/include-1.js"></script>
+
+ <script>
+ var t = async_test();
+
+ document.getElementById("defer-script").src = "scripts/include-2.js"
+
+ addEventListener("load", t.step_func(function () {
+ assert_array_equals(eventOrder, ["external script #1"]);
+ t.done();
+ }), false);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/115.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/115.html
new file mode 100644
index 0000000000..6234e02020
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/115.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+ <title> scheduler: Removing src of defer script before it runs </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+
+ <div id="test"></div>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+
+ <script id="defer-script" defer src="scripts/include-1.js">t.step(function() {assert_unreached()})</script>
+
+ <script>
+ var t = async_test();
+
+ document.getElementById("defer-script").removeAttribute("src");
+
+ addEventListener("load", t.step_func(function () {
+ assert_array_equals(eventOrder, ["external script #1"]);
+ t.done();
+ }), false);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/116.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/116.html
new file mode 100644
index 0000000000..62da398868
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/116.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: adding script to head of frameset document</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+ <script>
+ // add a script that looks for document.body as first child of HEAD
+ testlib.addScript('',{src:'scripts/find-body.js'},document.getElementsByTagName('head')[0], true );
+ var div = document.createElement("div");
+ div.id = "log";
+ var t = async_test();
+ function test() {
+ if(!(window.findBodyLoaded)) {
+ return setTimeout(t.step_func(test),200);
+ }
+ document.body.appendChild(div);
+ assert_array_equals(eventOrder, ['document.body: <FRAMESET>']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ </script>
+</head>
+<frameset>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/117.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/117.html
new file mode 100644
index 0000000000..46a9900c94
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/117.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: inline script created with createContextualFragment</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+ <div id="log"></div>
+ <script>
+ log('inline script #1');
+ var t = async_test();
+
+ t.step(function() {
+ var range = document.createRange();
+ var fragment = range.createContextualFragment("<script>log('fragment script #1')<\/script>");
+ document.body.appendChild(fragment.firstChild);
+ });
+
+ function test() {
+ assert_array_equals(eventOrder, ['inline script #1', 'fragment script #1', 'end inline script #1']);
+ t.done();
+ }
+ onload = t.step_func(test)
+ log('end inline script #1');
+ </script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/118.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/118.html
new file mode 100644
index 0000000000..e002ea9601
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/118.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: external script created with createContextualFragment</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+ <div id="log"></div>
+ <script>
+ log('inline script #1');
+ var t = async_test();
+
+ t.step(function() {
+ var range = document.createRange();
+ var fragment = range.createContextualFragment("<script src='scripts/include-1.js'><\/script>");
+ document.body.appendChild(fragment.firstChild);
+ });
+
+ addEventListener("load", t.step_func(function() {
+ assert_array_equals(eventOrder, ['inline script #1', 'end inline script #1', 'external script #1']);
+ t.done();
+ }), false);
+
+ log('end inline script #1');
+ </script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/119.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/119.html
new file mode 100644
index 0000000000..d1ed823093
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/119.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: external defer script created with createContextualFragment</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <script>
+ log('inline script #1');
+ var t = async_test();
+
+ t.step(function () {
+ var range = document.createRange();
+ var fragment = range.createContextualFragment("<script defer src='scripts/include-1.js?pipe=trickle(d1)'><\/script>");
+ document.body.appendChild(fragment.firstChild);
+ });
+
+ addEventListener("DOMContentLoaded", t.step_func(function () {
+ assert_array_equals(eventOrder, ['inline script #1', 'end inline script #1']);
+ }));
+
+ addEventListener("load", t.step_func_done(function () {
+ assert_array_equals(eventOrder, ['inline script #1', 'end inline script #1', 'external script #1']);
+ }));
+
+ log('end inline script #1');
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/120.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/120.html
new file mode 100644
index 0000000000..2cfe5221dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/120.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: script created without a window </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+
+ var doc = document.implementation.createHTMLDocument("");
+ doc.write("<script>t.step(function() {assert_unreached()})<\/script>");
+
+ document.body.appendChild(doc.head.firstChild);
+
+ onload = function() {t.done()}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/121.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/121.html
new file mode 100644
index 0000000000..d6de27025f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/121.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: altering the type attribute </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test" type="text/plain">t.step(function() {assert_unreached()}</script>
+<script>
+t.step(function() {
+ document.getElementById("test").removeAttribute("type");
+ setTimeout(t.step_func(function() {t.done()}), 100);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/122.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/122.html
new file mode 100644
index 0000000000..a8994c6aeb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/122.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: altering the type attribute and adding/removing external script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test" type="text/plain" src="scripts/include-1.js?pipe=trickle(d1)"></script>
+<script>
+t.step(function() {
+ var script = document.getElementById("test");
+ script.removeAttribute("type");
+ var marker = document.createElement("script");
+ marker.src = "scripts/include-2.js?pipe=trickle(d2)";
+ marker.async = false;
+ script.parentNode.appendChild(marker);
+ script.parentNode.appendChild(script);
+ test(function() {assert_true(script.async)}, "Reinserted script async IDL attribute");
+});
+onload = t.step_func(function () {
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/123.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/123.html
new file mode 100644
index 0000000000..dc145eb550
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/123.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: altering the type attribute and adding/removing external script with async=false </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test" type="text/plain" src="scripts/include-2.js?pipe=trickle(d1)"></script>
+<script>
+t.step(function() {
+ var script = document.getElementById("test");
+ script.removeAttribute("type");
+ script.async = false;
+ var marker = document.createElement("script");
+ marker.src = "scripts/include-1.js?pipe=trickle(d2)";
+ marker.async = false;
+ script.parentNode.appendChild(marker);
+ script.parentNode.appendChild(script);
+});
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/124.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/124.html
new file mode 100644
index 0000000000..5c7208dfde
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/124.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: altering the type attribute and changing script data inline script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test" type="text/plain">t.step(function() {log("inline script #1")});</script>
+<script>
+t.step(function() {
+ log("inline script #2");
+ var script = document.getElementById("test");
+ script.removeAttribute("type");
+ script.appendChild(document.createTextNode(""));
+ log("end inline script #2");
+});
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #2", "inline script #1", "end inline script #2"]);
+ t.done();
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/125.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/125.html
new file mode 100644
index 0000000000..5074f2a107
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/125.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: altering the type attribute and changing script data external script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test" type="text/plain" src="scripts/include-1.js?pipe=trickle(d1)"></script>
+<script>
+t.step(function() {
+ var script = document.getElementById("test");
+ script.removeAttribute("type");
+ var marker = document.createElement("script");
+ marker.src = "scripts/include-2.js?pipe=trickle(d2)";
+ marker.async = false;
+ script.parentNode.appendChild(marker);
+ script.appendChild(document.createTextNode(""));
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/126.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/126.html
new file mode 100644
index 0000000000..1b2bb17634
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/126.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: altering the type attribute and changing script data external script async=false </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test" type="text/plain" src="scripts/include-2.js"></script>
+<script>
+t.step(function() {
+ var script = document.getElementById("test");
+ script.removeAttribute("type");
+ script.async = false;
+ var marker = document.createElement("script");
+ marker.src = "scripts/include-1.js?pipe=trickle(d2)";
+ marker.async = false;
+ script.parentNode.appendChild(marker);
+ script.appendChild(document.createTextNode(""));
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/127.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/127.html
new file mode 100644
index 0000000000..149078a327
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/127.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: appending non-text children to script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test"></script>
+<script>
+t.step(function() {
+ log("inline script #1");
+ var script = document.getElementById("test");
+
+ var frag = document.createDocumentFragment();
+ var div = document.createElement("div");
+
+ div.textContent = "assert_unreached();"
+ frag.appendChild(document.createTextNode("t.step(function() {log('inline script #2');\n"));
+ frag.appendChild(div);
+ frag.appendChild(document.createTextNode("log('end inline script #2');})"));
+
+ script.appendChild(frag);
+ log("end inline script #1");
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "end inline script #2", "end inline script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/128.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/128.html
new file mode 100644
index 0000000000..39ec24f681
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/128.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: appending script element to script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test"></script>
+<script>
+t.step(function() {
+ log("inline script #1");
+ var script = document.getElementById("test");
+
+ var frag = document.createDocumentFragment();
+ var inner_script = document.createElement("script");
+
+ inner_script.textContent = "t.step(function() {log('inline script #3');});"
+ frag.appendChild(document.createTextNode("t.step(function() {log('inline script #2');\n"));
+ frag.appendChild(inner_script);
+ frag.appendChild(document.createTextNode("log('end inline script #2');})"));
+
+ script.appendChild(frag);
+ log("end inline script #1");
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #3", "inline script #2", "end inline script #2", "end inline script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/129.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/129.html
new file mode 100644
index 0000000000..8c12735677
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/129.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: appending multiple script elements</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<div id="container"></div>
+<script>
+t.step(function() {
+ log("inline script #1");
+
+ var frag = document.createDocumentFragment();
+
+ scripts = ["2", "3", "4"].map(function(x) {
+ var s = document.createElement("script");
+ s.textContent = "t.step(function() {log('inline script #" + x + "')});";
+ return s
+ });
+
+
+ frag.appendChild(scripts[0]);
+ var div = document.createElement(div);
+ div.appendChild(scripts[1]);
+ frag.appendChild(div);
+ frag.appendChild(scripts[2]);
+
+ document.getElementById("container").appendChild(frag);
+ log("end inline script #1");
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "inline script #3", "inline script #4", "end inline script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/130.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/130.html
new file mode 100644
index 0000000000..c6643d9fdf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/130.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: appending external script element to script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<script id="test"></script>
+<script>
+t.step(function() {
+ log("inline script #1");
+ var script = document.getElementById("test");
+
+ var frag = document.createDocumentFragment();
+ var inner_script = document.createElement("script");
+
+ inner_script.src = "scripts/include-1.js?pipe=trickle(d1)";
+ frag.appendChild(document.createTextNode("t.step(function() {log('inline script #2');\n"));
+ frag.appendChild(inner_script);
+ frag.appendChild(document.createTextNode("log('end inline script #2');})"));
+
+ script.appendChild(frag);
+ log("end inline script #1");
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "end inline script #2", "end inline script #1", "external script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/131.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/131.html
new file mode 100644
index 0000000000..541483ed6b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/131.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: inline svg script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ log("inline script #1")
+</script>
+<svg>
+<script>log("inline script #2")</script>
+</svg>
+<script>
+log("inline script #3");
+t.step(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "inline script #3"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/132.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/132.html
new file mode 100644
index 0000000000..3edb959594
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/132.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: external svg script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ log("inline script #1")
+</script>
+<svg>
+<script xlink:href="scripts/include-1.js"></script>
+</svg>
+<script>
+log("inline script #2");
+t.step(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "external script #1", "inline script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/133.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/133.html
new file mode 100644
index 0000000000..e6b327f709
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/133.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: inline HTML script added by SVG script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ log("inline script #1")
+</script>
+<svg>
+<script>
+log("inline script #2")
+var s = document.createElement("script");
+s.textContent = "log('inline script #3');";
+document.getElementsByTagName("svg")[0].appendChild(s);
+log("end inline script #2");
+</script>
+</svg>
+<script>
+log("inline script #4");
+t.step(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "inline script #3",
+ "end inline script #2", "inline script #4"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/134.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/134.html
new file mode 100644
index 0000000000..bb2ad4f66b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/134.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: external HTML script added by SVG script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ log("inline script #1")
+</script>
+<svg>
+<script>
+log("inline script #2")
+var s = document.createElement("script");
+s.src = "scripts/include-1.js"
+document.getElementsByTagName("svg")[0].appendChild(s);
+log("end inline script #2");
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2",
+ "end inline script #2", "external script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/135.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/135.html
new file mode 100644
index 0000000000..dd8b895218
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/135.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: external SVG script added by SVG script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+ log("inline script #1")
+</script>
+<svg>
+<script>
+log("inline script #2")
+var s = document.createElementNS("http://www.w3.org/2000/svg", "script");
+s.setAttributeNS("http://www.w3.org/1999/xlink", "href", "scripts/include-1.js");
+document.getElementsByTagName("svg")[0].appendChild(s);
+log("end inline script #2");
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2",
+ "end inline script #2", "external script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/136.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/136.html
new file mode 100644
index 0000000000..b08a8b9798
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/136.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: DOM added external SVG script, force-async? </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script>
+
+var s1 = document.createElement("script");
+s1.src = "scripts/include-1.js";
+s1.async = false;
+
+var s = document.createElementNS("http://www.w3.org/2000/svg", "script");
+s.setAttributeNS("http://www.w3.org/1999/xlink", "href", "scripts/include-2.js?pipe=trickle(d2)");
+
+document.getElementsByTagName("svg")[0].appendChild(s);
+document.getElementsByTagName("svg")[0].appendChild(s1);
+
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ <!-- assumes that the SVg script should be async -->
+ assert_array_equals(eventOrder, ["external script #1", "external script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/137.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/137.html
new file mode 100644
index 0000000000..35a49b806d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/137.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG script empty xlink:href</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script xlink:href="">
+t.step(function() {assert_unreached()});
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/138.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/138.html
new file mode 100644
index 0000000000..0ff25471e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/138.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG script nested inlines</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script>
+t.step(function() {
+log("inline script #2");
+var a = {
+ <script>
+ t.step(function() {log("inline script #1")})
+ </script>
+a:1}
+log("end inline script #2");
+});
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "end inline script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/139.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/139.html
new file mode 100644
index 0000000000..7bb703d8a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/139.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG script nested external in inline</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script>
+t.step(function() {
+log("inline script #1");
+var a = {
+ <script xlink:href="scripts/include-1.js">
+ t.step(function() {assert_unreached()})
+ </script>
+a:1}
+log("end inline script #1");
+});
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["external script #1", "inline script #1", "end inline script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/140.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/140.html
new file mode 100644
index 0000000000..9b54d09d58
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/140.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG script nested inline in external</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script xlink:href="scripts/include-1.js">
+ <script>
+ t.step(function() {log("inline script #1")});
+ </script>
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "external script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/141.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/141.html
new file mode 100644
index 0000000000..54aa3f3686
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/141.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG inline script that document.writes inline script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script>
+ t.step(function() {
+ log('inline script #1');
+ document.write("<" + "script>t.step(function() {log('inline script #2')})<" + "/script>");
+ log('end inline script #1');
+ });
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "end inline script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/142.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/142.html
new file mode 100644
index 0000000000..d314eddb90
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/142.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG inline script that document.writes external script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script>
+ t.step(function() {
+ log('inline script #1');
+ document.write("<" + "script xlink:href='scripts/include-1.js'><" + "/script>");
+ log('end inline script #1');
+ });
+</script>
+<script>t.step(function() {log("inline script #2")});</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "end inline script #1", "external script #1", "inline script #2"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/143.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/143.html
new file mode 100644
index 0000000000..a0d9012f68
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/143.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG nested inline script that document.writes inline script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script>
+ t.step(function() {
+ log('inline script #3');
+ });
+ <script>
+ log("inline script #1")
+ document.write("<" + "script>t.step(function() {log('inline script #2')})<" + "/script><" + "/script>");
+ </script>
+ t.step(function() {
+ assert_unreached():
+ });
+</script>
+</svg>
+<script>
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "inline script #3"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/144.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/144.html
new file mode 100644
index 0000000000..3962c4d692
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/144.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG inline script changing the type attribute </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script type="text/plain">
+t.step(function() {assert_unreached()});
+</script>
+</svg>
+<script>
+t.step(function() {
+ var s = document.querySelector("svg > script");
+ s.textContent = "t.step(function() {log('inline script #1')})";
+ s.type = "";
+ s.parentNode.appendChild(s);
+});
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/145.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/145.html
new file mode 100644
index 0000000000..9e2d73bafc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/145.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG inline script adding text to empty script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>
+ var t = async_test();
+</script>
+<svg>
+<script></script>
+</svg>
+<script>
+t.step(function() {
+ var s = document.querySelector("svg > script");
+ s.textContent = "t.step(function() {log('inline script #1')})";
+});
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146-href.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146-href.html
new file mode 100644
index 0000000000..6c0869db54
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146-href.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG script adding src attribute </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>var t = async_test();</script>
+<svg>
+<script></script>
+</svg>
+<script>
+t.step(function() {
+ var s = document.querySelector("svg > script");
+ s.setAttribute("href", "scripts/include-1.js");
+});
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["external script #1"]);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146.html
new file mode 100644
index 0000000000..333ac3fa0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/146.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: SVG script adding src attribute </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<div id="log"></div>
+<script>var t = async_test();</script>
+<svg>
+<script></script>
+</svg>
+<script>
+t.step(function() {
+ var s = document.querySelector("svg > script");
+ s.src = "scripts/include-1.js";
+});
+onload = t.step_func(function() {
+ // SVG <script> element uses href attribute, so src attribute is ignored.
+ assert_array_equals(eventOrder, []);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/147.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/147.html
new file mode 100644
index 0000000000..07dc4d97c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/147.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: insert multiple inline scripts; first script moves subsequent scripts </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+var t = async_test();
+</script>
+<div id="container"></div>
+<script>
+t.step(function() {
+ log("inline script #1");
+ var container = document.getElementById("container");
+
+ var frag = document.createDocumentFragment();
+ var frag_script_1 = document.createElement("script");
+ var frag_script_2 = document.createElement("script");
+ frag_script_2.id = "movee";
+ var frag_script_3 = document.createElement("script");
+
+ frag_script_1.textContent = "t.step(function() {log('inline script #2'); var s = document.getElementById('movee'); s.parentNode.appendChild(s)});";
+ frag_script_2.textContent = "t.step(function() {log('inline script #3');})";
+ frag_script_3.textContent = "t.step(function() {log('inline script #4');})";
+
+ [frag_script_1, frag_script_2, frag_script_3].forEach(function(x) {frag.appendChild(x)});
+
+ container.appendChild(frag);
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "inline script #3", "inline script #4"]);
+ t.done();
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/148.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/148.html
new file mode 100644
index 0000000000..e2da8e8f0b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/148.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: insert multiple inline scripts; first script deletes subsequent script </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+var t = async_test();
+</script>
+<div id="container"></div>
+<script>
+t.step(function() {
+ log("inline script #1");
+ var container = document.getElementById("container");
+
+ var frag = document.createDocumentFragment();
+ var frag_script_1 = document.createElement("script");
+ var frag_script_2 = document.createElement("script");
+ frag_script_2.id = "delete";
+ var frag_script_3 = document.createElement("script");
+
+ frag_script_1.textContent = "t.step(function() {log('inline script #2'); var s = document.getElementById('delete'); s.parentNode.removeChild(s)});";
+ frag_script_2.textContent = "t.step(function() {log('inline script #3');})";
+ frag_script_3.textContent = "t.step(function() {log('inline script #4');})";
+
+ [frag_script_1, frag_script_2, frag_script_3].forEach(function(x) {frag.appendChild(x)});
+
+ container.appendChild(frag);
+});
+
+onload = t.step_func(function() {
+ assert_array_equals(eventOrder, ["inline script #1", "inline script #2", "inline script #3", "inline script #4"]);
+ t.done();
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/149.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/149.html
new file mode 100644
index 0000000000..40594d8048
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/149.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html><head>
+ <title>scheduler: event/for attribute on script</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+attributes = [
+ {for:"window", event:"onload()", expect:true},
+ {for:"window", event:"onload", expect:true},
+ {for:" WINdow\t\n", event:"ONload\t\n", expect:true},
+ {for:"window", event:"load", expect:false},
+ {for:"window", event:"onpageshow", expect:false},
+ {for:"document", event:"onload", expect:false},
+]
+
+function test_maker(array_name) {
+ return function(x, i) {
+ var title = "for='" + x.for + "' event='" + x.event + "' " + array_name.replace("_", " ") + " " + (x.expect ? "executes immediately" : "does not execute");
+ script_content = "var d =" + array_name + "[" + i + "];"
+ script_content += x.expect?"d[1].step(function() {d[3] = true});":"d[1].step(function() {assert_unreached()});"
+ return [x, async_test(title), script_content, false];
+ }
+}
+
+parser_inserted = attributes.map(test_maker("parser_inserted"));
+dom_inserted = attributes.map(test_maker("dom_inserted"));
+
+parser_inserted.forEach(function(x) {
+ var d = x[0];
+ document.write("<script for='" + d.for + "' event='" + d.event + "'>" + x[2] + "<\/script>");
+});
+
+dom_inserted.forEach(function(x) {
+ var d = x[0];
+ var s = document.createElement("script");
+ s.setAttribute("event", d.event);
+ s.setAttribute("for", d.for);
+ s.textContent = x[2];
+ document.body.appendChild(s);
+});
+</script>
+
+<script>
+var all_tests = parser_inserted.concat(dom_inserted);
+
+all_tests.filter(function(x) {return x[0]["expect"]}).forEach(function(x) {var t = x[1]; t.step(function() {assert_true(x[3])});})
+
+onload = function() {
+ all_tests.forEach(function(x) {var t = x[1]; t.step(function() {t.done()})});
+}
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import-xhtml.xhtml
new file mode 100644
index 0000000000..d6144795db
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import-xhtml.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Stylesheet in XHTML BODY with @import blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="log">FAILED (This TC requires JavaScript enabled)</div>
+ <div id="test">Test</div>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)" />
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import.html
new file mode 100644
index 0000000000..40223ed104
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-import.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Stylesheet in BODY with @import blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="test">Test</div>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/import.css?pipe=trickle(d2)">
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport-xhtml.xhtml
new file mode 100644
index 0000000000..fa587a2ddb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport-xhtml.xhtml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Stylesheet in XHTML BODY blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="test">Test</div>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)" />
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport.html
new file mode 100644
index 0000000000..b5829d4da5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/150-noimport.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Stylesheet in BODY blocking scripts</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="testlib/testlib.js"></script>
+</head>
+<body>
+ <div id="test">Test</div>
+ <!-- this stylesheet blocks scripts -->
+ <link rel="stylesheet" href="css/background.css?pipe=trickle(d2)">
+ <script>
+ test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/background.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/background.css
new file mode 100644
index 0000000000..86a155b811
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/background.css
@@ -0,0 +1 @@
+#test {position:fixed} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/import.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/import.css
new file mode 100644
index 0000000000..d1664c29a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/css/import.css
@@ -0,0 +1 @@
+@import url("background.css") \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/non-external-no-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/non-external-no-import.html
new file mode 100644
index 0000000000..50836e0d7b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/non-external-no-import.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Module scripts with no imports always execute asynchronously</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="help" href="https://github.com/whatwg/html/issues/3746">
+</head>
+<body>
+<script>
+async_test(t => {
+ window.results = [];
+ window.logExecution = msg => window.results.push(msg);
+
+ const script = document.createElement('script');
+ script.type = 'module';
+ script.textContent = "window.logExecution('module')";
+ document.body.append(script);
+ window.logExecution('classic');
+
+ window.onload = t.step_func_done(e => {
+ assert_array_equals(window.results, ['classic', 'module']);
+ });
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld-postMessage.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld-postMessage.html
new file mode 100644
index 0000000000..2ed8731ceb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld-postMessage.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html><head>
+ <title> TC component </title>
+</head>
+<body>
+
+ <p>This page should appear in popup or frame</p>
+
+ <script type="text/javascript">
+ var target = opener || top;
+ var id = location.search?' '+location.search.substring(1) : '';
+ target.log('frame/popup script'+id);
+ window.onload=function(){
+ target.log('load event inside frame/popup script'+id);
+ target.postMessage('msg evt frame/popup script'+id, '*');
+ }
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld.html
new file mode 100644
index 0000000000..271bc8f569
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/pages/helloworld.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html><head>
+ <title> TC component </title>
+</head>
+<body>
+
+ <p>This page should appear in popup or frame</p>
+
+ <script type="text/javascript">
+ var target = top || opener;
+ var id = location.search?' '+parseInt(location.search.substring(1)) : '';
+ target.log('frame/popup script'+id);
+ </script>
+
+</body></html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/check-style-sheet.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/check-style-sheet.js
new file mode 100644
index 0000000000..cbab154f5a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/check-style-sheet.js
@@ -0,0 +1,4 @@
+test(function() {
+ assert_equals(getComputedStyle(document.getElementById("test")).position,
+ "fixed");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/count-script-tags.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/count-script-tags.js
new file mode 100644
index 0000000000..8fba4ecb3c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/count-script-tags.js
@@ -0,0 +1 @@
+log('script tags in DOM: '+document.getElementsByTagName('script').length); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-body.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-body.js
new file mode 100644
index 0000000000..1ce198f13e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-body.js
@@ -0,0 +1,4 @@
+log(
+ 'document.body: ' +
+ (document.body ? '<' + document.body.localName.toUpperCase() + '>' : null));
+var findBodyLoaded=true;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-foo.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-foo.js
new file mode 100644
index 0000000000..52d0ec91cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/find-foo.js
@@ -0,0 +1,2 @@
+log('found #foo element: ' + ( document.getElementById('foo') ? 'YES' : 'NO' ));
+var findFooLoaded=true; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js
new file mode 100644
index 0000000000..8ff291ad57
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js
@@ -0,0 +1 @@
+log('external script #1'); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-10.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-10.js
new file mode 100644
index 0000000000..8dc770ddc0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-10.js
@@ -0,0 +1 @@
+document.write("<script src='scripts/include-9.js?pipe=trickle(d2)' defer></script>");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-11.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-11.js
new file mode 100644
index 0000000000..016913c4b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-11.js
@@ -0,0 +1,4 @@
+log("external script before adding iframe");
+var iframe = document.createElement("iframe");
+iframe.srcdoc = "<script>parent.log('script in iframe')</script>"
+document.body.appendChild(iframe);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js
new file mode 100644
index 0000000000..31319423af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js
@@ -0,0 +1 @@
+log('external script #2'); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js
new file mode 100644
index 0000000000..53352e0f83
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js
@@ -0,0 +1,3 @@
+log('external script before doc write');
+document.write( '<script>log(\'document.write external script\');</script>');
+log('external script after doc write'); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js
new file mode 100644
index 0000000000..0597a22624
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js
@@ -0,0 +1,3 @@
+log('include-4 before doc write');
+document.write( '<script src="scripts/include-3.js"></script>');
+log('include-4 after doc write'); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-5.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-5.js
new file mode 100644
index 0000000000..52952d7379
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-5.js
@@ -0,0 +1,7 @@
+log('include-5 before removing scripts');
+var scripts=[].slice.call(document.getElementsByTagName('script'), 3);
+for(var i = 0; i < scripts.length; i++) {
+ var s = scripts[i];
+ s.parentNode.removeChild(s);
+}
+log('include-5 after removing scripts');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-6.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-6.js
new file mode 100644
index 0000000000..77da2af232
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-6.js
@@ -0,0 +1,6 @@
+top.log(
+ 'external script (#foo found? ' +
+ (document.getElementById('foo') ? 'YES' : 'NO' ) +
+ ')'
+);
+top.include6Loaded=true; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-7.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-7.js
new file mode 100644
index 0000000000..57c5508015
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-7.js
@@ -0,0 +1 @@
+log('external script #7'); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-8.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-8.js
new file mode 100644
index 0000000000..960f2129fe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-8.js
@@ -0,0 +1,4 @@
+log("external script #8");
+var s = document.createElement("script")
+s.src='scripts/include-9.js?pipe=trickle(d2)'
+document.body.appendChild(s);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-9.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-9.js
new file mode 100644
index 0000000000..9042882024
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-9.js
@@ -0,0 +1,2 @@
+log("external script #9");
+test(); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/testlib/testlib.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/testlib/testlib.js
new file mode 100644
index 0000000000..a6fd39426b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/execution-timing/testlib/testlib.js
@@ -0,0 +1,43 @@
+/*
+* Utility functions for script scheduler test
+*/
+(function(){ /* namespace hiding local variables like arOrderOfAllEvents from global scope */
+ window.testlib = {};
+ window.eventOrder = [];
+ var arNumberOfScriptsParsedPerEvent=[];
+ window.log = function (str){
+ eventOrder.push(str);
+ arNumberOfScriptsParsedPerEvent.push(document.getElementsByTagName('script').length);
+ }
+
+ window.testlib.addScript = function(source, attributes, parent, firstInParent,funcPrepare) {
+ try{
+ parent = parent||document.body;
+ var script = document.createElement('script');
+ if(funcPrepare) {
+ funcPrepare(script);
+ }
+ if(source)script.appendChild( document.createTextNode(source) );
+ for( var name in attributes){
+ if(/^on/i.test(name)) {
+ script[name] = attributes[name];
+ } else {
+ script.setAttribute(name, attributes[name]);
+ }
+ }
+ if (firstInParent && parent.firstChild) {
+ parent.insertBefore(script, parent.firstChild);
+ } else {
+ parent.appendChild(script);
+ }
+ } catch(e) {
+ log('ERROR when adding script to DOM!');
+ alert(e);
+ }
+ return script;
+ }
+
+ window.testlib.urlParam = function(relativeURL) {
+ return location.href.replace( /\d*\.html$/, relativeURL);
+ }
+})();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-utf8.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-utf8.js
new file mode 100644
index 0000000000..eb442c97bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-utf8.js
@@ -0,0 +1,5 @@
+(function() {
+ window.getSomeString = function() {
+ return "śćążź"; //<- these are five Polish letters, similar to scazz. It can be read correctly only with windows 1250 encoding.
+ };
+})();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-windows1250.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-windows1250.js
new file mode 100644
index 0000000000..50de6932ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/external-script-windows1250.js
@@ -0,0 +1,5 @@
+(function() {
+ window.getSomeString = function() {
+ return "œæ¹¿Ÿ"; //<- these are five Polish letters, similar to scazz. It can be read correctly only with windows 1250 encoding.
+ };
+})();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/base.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/base.html
new file mode 100644
index 0000000000..dc0fa9dabb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/base.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Script src with a base URL</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<base href=../beta/>
+<div id=log></div>
+<script>
+function do_test(path) {
+ test(function() {
+ assert_equals(path, "beta");
+ });
+}
+</script>
+<script src=test.js></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/test.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/test.js
new file mode 100644
index 0000000000..3cbbb12e2e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/alpha/test.js
@@ -0,0 +1 @@
+do_test("alpha");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/beta/test.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/beta/test.js
new file mode 100644
index 0000000000..4ce0f5338d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/beta/test.js
@@ -0,0 +1 @@
+do_test("beta");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty-with-base.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty-with-base.html
new file mode 100644
index 0000000000..edc2c3d6f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty-with-base.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Script src with an empty URL</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<base href=unreachable.js>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ window.unreachable = this.unreached_func("Should not load unreachable.js");
+ var queued = false;
+ var script = document.createElement("script");
+ script.onerror = this.step_func_done(function(ev) {
+ assert_equals(ev.type, "error");
+ assert_false(ev.bubbles, "bubbles");
+ assert_false(ev.cancelable, "cancelable");
+ assert_true(ev.isTrusted, "isTrusted");
+ assert_equals(ev.target, script);
+ assert_true(ev instanceof Event, "instanceof Event");
+ assert_class_string(ev, "Event");
+ assert_true(queued, "event should not be dispatched synchronously");
+ });
+ script.setAttribute("src", "");
+ document.body.appendChild(script);
+ queued = true;
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty.html
new file mode 100644
index 0000000000..d127f1eb3b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/empty.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Script src with an empty URL</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+// For a better error message in case the UA tries to load "" (which resolves
+// to this document).
+setup({
+ "allow_uncaught_exception": true,
+});
+async_test(function(t) {
+ window.onerror = this.unreached_func("Should not get an error reported to " +
+ "the window before the script");
+ var queued = false;
+ var script = document.createElement("script");
+ script.onerror = this.step_func_done(function(ev) {
+ assert_equals(ev.type, "error");
+ assert_false(ev.bubbles, "bubbles");
+ assert_false(ev.cancelable, "cancelable");
+ assert_true(ev.isTrusted, "isTrusted");
+ assert_equals(ev.target, script);
+ assert_true(ev instanceof Event, "instanceof Event");
+ assert_class_string(ev, "Event");
+ assert_true(queued, "event should not be dispatched synchronously");
+ });
+ script.setAttribute("src", "");
+ document.body.appendChild(script);
+ queued = true;
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/failure.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/failure.html
new file mode 100644
index 0000000000..b49e51740f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/failure.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Script src with an invalid URL</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+async_test(function(t) {
+ var queued = false;
+ var script = document.createElement("script");
+ script.onerror = this.step_func_done(function(ev) {
+ assert_equals(ev.type, "error");
+ assert_false(ev.bubbles, "bubbles");
+ assert_false(ev.cancelable, "cancelable");
+ assert_true(ev.isTrusted, "isTrusted");
+ assert_equals(ev.target, script);
+ assert_true(ev instanceof Event, "instanceof Event");
+ assert_class_string(ev, "Event");
+ assert_true(queued, "event should not be dispatched synchronously");
+ });
+ script.setAttribute("src", "//[]");
+ document.body.appendChild(script);
+ queued = true;
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/unreachable.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/unreachable.js
new file mode 100644
index 0000000000..ca7fdba71f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/fetch-src/unreachable.js
@@ -0,0 +1 @@
+unreachable();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/historical.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/historical.html
new file mode 100644
index 0000000000..1f1a91228c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/historical.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<title>Historical script element features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function t(property) {
+ test(function() {
+ assert_false(property in document.createElement('script'));
+ }, 'script.' + property + ' should not be supported');
+}
+// added in https://github.com/whatwg/html/commit/69f83cf2eacf4543860ccb7abab0ff5bb1e8b594
+// removed in https://github.com/whatwg/html/commit/1a0b5e8377d59462e05a5cffda4b8592324a2785
+t('onbeforescriptexecute');
+t('onafterscriptexecute');
+
+var t_onbeforescriptexecute_attr = async_test('onbeforescriptexecute content attribute should not be supported');
+var t_onafterscriptexecute_attr = async_test('onafterscriptexecute content attribute should not be supported');
+var t_beforescriptexecute_event = async_test(function() {
+ addEventListener('beforescriptexecute', this.step_func(function() {
+ assert_unreached();
+ }), true);
+}, 'beforescriptexecute event should not be supported');
+var t_afterscriptexecute_event = async_test(function() {
+ addEventListener('afterscriptexecute', this.step_func(function() {
+ assert_unreached();
+ }), true);
+}, 'afterscriptexecute event should not be supported');
+
+var a = false;
+
+onload = function() {
+ t_onbeforescriptexecute_attr.step(function() {
+ assert_true(a);
+ this.done();
+ });
+ t_onafterscriptexecute_attr.step(function() {
+ assert_true(a);
+ this.done();
+ });
+ t_beforescriptexecute_event.step(function() {
+ assert_true(a);
+ this.done();
+ });
+ t_afterscriptexecute_event.step(function() {
+ assert_true(a);
+ this.done();
+ });
+};
+</script>
+<script onbeforescriptexecute="t_onbeforescriptexecute_attr.step(function() { assert_unreached(); });"
+ onafterscriptexecute="t_onafterscriptexecute_attr.step(function() { assert_unreached(); });">
+a = true;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/dynamic-import-with-assertion-argument.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/dynamic-import-with-assertion-argument.any.js
new file mode 100644
index 0000000000..7efb2050b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/dynamic-import-with-assertion-argument.any.js
@@ -0,0 +1,18 @@
+// META: global=window,dedicatedworker,sharedworker
+
+promise_test(async test => {
+ const result = await import("./export-hello.js", { assert: { } });
+ assert_equals(result.default, "hello");
+}, "Dynamic import with an empty assert clause should succeed");
+
+promise_test(async test => {
+ return promise_rejects_js(test, TypeError,
+ import("./export-hello.js", { assert: { unsupportedAssertionKey: "unsupportedAssertionValue"} }),
+ "Dynamic import with an unsupported import assertion should fail");
+}, "Dynamic import with an unsupported import assertion should fail");
+
+promise_test(test => {
+ return promise_rejects_js(test, TypeError,
+ import("./export-hello.js", { assert: { type: "notARealType"} } ),
+ "Dynamic import with an unsupported type assertion should fail");
+}, "Dynamic import with an unsupported type assertion should fail");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.html
new file mode 100644
index 0000000000..3a7c371189
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Handling of empty import assertion clause</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that no error occurs when an empty import assertion clause is provided.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(window.log, ["hello", "empty-assertion-clause"]);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./empty-assertion-clause.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.js
new file mode 100644
index 0000000000..6913dd61df
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.js
@@ -0,0 +1,2 @@
+import "./hello.js" assert { };
+log.push("empty-assertion-clause");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-type-assertion.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-type-assertion.js
new file mode 100644
index 0000000000..5bb9b1ddb8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/empty-type-assertion.js
@@ -0,0 +1,2 @@
+import "./hello.js#2" assert { type: "" };
+log.push("empty-type-assertion");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/export-hello.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/export-hello.js
new file mode 100644
index 0000000000..34b58e6e12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/export-hello.js
@@ -0,0 +1 @@
+export default "hello";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/hello.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/hello.js
new file mode 100644
index 0000000000..2f34844460
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/hello.js
@@ -0,0 +1 @@
+log.push("hello"); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-import-errors-order.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-import-errors-order.html
new file mode 100644
index 0000000000..17c95faea0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-import-errors-order.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>Handling of invalid module type import attributes</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ var log = [];
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that the errors order for invalid import declarations is" +
+ " specifier, then attribute key, and then type attribute value.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 3);
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_equals(log[1].constructor, SyntaxError);
+ assert_equals(log[2].constructor, SyntaxError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" onerror="unreachable()">
+ // unknown attribute is reported before invalid specifier
+ import "INVALID" assert { unknown: "foo" };
+</script>
+<script type="module" onerror="unreachable()">
+ // unknown attribute is reported before unknown type
+ import "./valid" assert { unknown: "foo", type: "unknown" };
+</script>
+<script type="module" onerror="unreachable()">
+ // unknown attribute is reported before invalid specifier in subsequent import
+ import "./valid" assert { unknown: "foo" };
+ import "INVALID";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion-error.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion-error.html
new file mode 100644
index 0000000000..0adcc47569
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion-error.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Handling of invalid module type import assertions</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that invalid module type assertion leads to TypeError on window.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 4);
+ assert_equals(log[0].constructor, TypeError);
+ assert_equals(log[1].constructor, TypeError);
+ assert_equals(log[2].constructor, TypeError);
+ assert_equals(log[3].constructor, TypeError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./invalid-type-assertion.js" onerror="unreachable()"></script>
+<script type="module" src="./empty-type-assertion.js" onerror="unreachable()"></script>
+<script type="module" src="./js-type-assertion.js" onerror="unreachable()"></script>
+<script type="module" src="./javascript-type-assertion.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion.js
new file mode 100644
index 0000000000..e28c0176d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/invalid-type-assertion.js
@@ -0,0 +1,2 @@
+import "./hello.js#1" assert { type: "notARealType" };
+log.push("invalid-type-assertion");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/javascript-type-assertion.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/javascript-type-assertion.js
new file mode 100644
index 0000000000..cc0f531026
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/javascript-type-assertion.js
@@ -0,0 +1,2 @@
+import "./hello.js#4" assert { type: "javascript" };
+log.push("javascript-type-assertion");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/js-type-assertion.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/js-type-assertion.js
new file mode 100644
index 0000000000..c649c95ffe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/js-type-assertion.js
@@ -0,0 +1,2 @@
+import "./hello.js#3" assert { type: "js" };
+log.push("js-type-assertion");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.html
new file mode 100644
index 0000000000..72977347a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Handling of unsupported assertion</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that invalid module assertion leads to SyntaxError on window.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 1);
+ assert_equals(log[0].constructor, SyntaxError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./unsupported-assertion.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.js
new file mode 100644
index 0000000000..45f6d60c9d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.js
@@ -0,0 +1,2 @@
+import "./hello.js" assert { unsupportedAssertionKey: "unsupportedAssertionValue" };
+log.push("unsupported-assertion");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/dynamic-import-with-attributes-argument.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/dynamic-import-with-attributes-argument.any.js
new file mode 100644
index 0000000000..2addb0fcb9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/dynamic-import-with-attributes-argument.any.js
@@ -0,0 +1,18 @@
+// META: global=window,dedicatedworker,sharedworker
+
+promise_test(async test => {
+ const result = await import("./export-hello.js", { with: { } });
+ assert_equals(result.default, "hello");
+}, "Dynamic import with an empty with clause should succeed");
+
+promise_test(async test => {
+ return promise_rejects_js(test, TypeError,
+ import("./export-hello.js", { with: { unsupportedAssertionKey: "unsupportedAssertionValue"} }),
+ "Dynamic import with an unsupported import attribute should fail");
+}, "Dynamic import with an unsupported import attribute should fail");
+
+promise_test(test => {
+ return promise_rejects_js(test, TypeError,
+ import("./export-hello.js", { with: { type: "notARealType"} } ),
+ "Dynamic import with an unsupported type attribute should fail");
+}, "Dynamic import with an unsupported type attribute should fail");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.html
new file mode 100644
index 0000000000..0a8868b2f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Handling of empty import attributes clause</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that no error occurs when an empty import attributes clause is provided.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(window.log, ["hello", "empty-attributes-clause"]);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./empty-attributes-clause.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.js
new file mode 100644
index 0000000000..98c4573179
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.js
@@ -0,0 +1,2 @@
+import "./hello.js" with { };
+log.push("empty-attributes-clause");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-type-attribute.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-type-attribute.js
new file mode 100644
index 0000000000..72272b5102
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/empty-type-attribute.js
@@ -0,0 +1,2 @@
+import "./hello.js#2" with { type: "" };
+log.push("empty-type-assertion");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/export-hello.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/export-hello.js
new file mode 100644
index 0000000000..34b58e6e12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/export-hello.js
@@ -0,0 +1 @@
+export default "hello";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/hello.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/hello.js
new file mode 100644
index 0000000000..2f34844460
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/hello.js
@@ -0,0 +1 @@
+log.push("hello"); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-import-errors-order.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-import-errors-order.html
new file mode 100644
index 0000000000..8c438f626b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-import-errors-order.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>Handling of invalid module type import attributes</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ var log = [];
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that the errors order for invalid import declarations is" +
+ " specifier, then attribute key, and then type attribute value.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 3);
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_equals(log[1].constructor, SyntaxError);
+ assert_equals(log[2].constructor, SyntaxError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" onerror="unreachable()">
+ // unknown attribute is reported before invalid specifier
+ import "INVALID" with { unknown: "foo" };
+</script>
+<script type="module" onerror="unreachable()">
+ // unknown attribute is reported before unknown type
+ import "./valid" with { unknown: "foo", type: "unknown" };
+</script>
+<script type="module" onerror="unreachable()">
+ // unknown attribute is reported before invalid specifier in subsequent import
+ import "./valid" with { unknown: "foo" };
+ import "INVALID";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute-error.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute-error.html
new file mode 100644
index 0000000000..ac6d61a642
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute-error.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Handling of invalid module type import attributes</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that invalid module type attribute leads to TypeError on window.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 4);
+ assert_equals(log[0].constructor, TypeError);
+ assert_equals(log[1].constructor, TypeError);
+ assert_equals(log[2].constructor, TypeError);
+ assert_equals(log[3].constructor, TypeError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./invalid-type-attribute.js" onerror="unreachable()"></script>
+<script type="module" src="./empty-type-attribute.js" onerror="unreachable()"></script>
+<script type="module" src="./js-type-attribute.js" onerror="unreachable()"></script>
+<script type="module" src="./javascript-type-attribute.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute.js
new file mode 100644
index 0000000000..10e35b6b56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute.js
@@ -0,0 +1,2 @@
+import "./hello.js#1" with { type: "notARealType" };
+log.push("invalid-type-attribute");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/javascript-type-attribute.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/javascript-type-attribute.js
new file mode 100644
index 0000000000..568db18196
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/javascript-type-attribute.js
@@ -0,0 +1,2 @@
+import "./hello.js#4" with { type: "javascript" };
+log.push("javascript-type-attribute");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/js-type-attribute.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/js-type-attribute.js
new file mode 100644
index 0000000000..b7a454b4f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/js-type-attribute.js
@@ -0,0 +1,2 @@
+import "./hello.js#3" with { type: "js" };
+log.push("js-type-attribute");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.html
new file mode 100644
index 0000000000..3656e465ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Handling of unsupported attribute</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that invalid module attribute leads to SyntaxError on window.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 1);
+ assert_equals(log[0].constructor, SyntaxError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./unsupported-attribute.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.js
new file mode 100644
index 0000000000..2fc6829cfb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.js
@@ -0,0 +1,2 @@
+import "./hello.js" with { unsupportedAttributeKey: "unsupportedAttributeValue" };
+log.push("unsupported-attribute");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-module-goal.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-module-goal.mjs
new file mode 100644
index 0000000000..b533fc2e90
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-module-goal.mjs
@@ -0,0 +1 @@
+import "./serve-with-content-type.py?fn=is-module-goal.mjs&ct=text%2Fjavascript%3Bgoal=module"; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-script-goal.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-script-goal.js
new file mode 100644
index 0000000000..069363dd40
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/is-script-goal.js
@@ -0,0 +1,3 @@
+with ({}) {
+ ;
+}; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/array.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/array.json
new file mode 100644
index 0000000000..e77e32d338
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/array.json
@@ -0,0 +1 @@
+["en", "try"]
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16be.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16be.json
new file mode 100644
index 0000000000..d22a45a591
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16be.json
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16le.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16le.json
new file mode 100644
index 0000000000..4d1aa264a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-16le.json
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-8.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-8.json
new file mode 100644
index 0000000000..07ba933e86
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/bom-utf-8.json
@@ -0,0 +1 @@
+{ "data": "hello" } \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-2.html
new file mode 100644
index 0000000000..686178bfe1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-2.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="windows-1250">
+<title>JSON modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module">
+ import json from "../serve-with-content-type.py?fn=json-module-assertions/utf-8.json" assert { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 even though document's encoding is windows-1250");
+</script>
+<script type="module">
+ import json from "../serve-with-content-type.py?fn=json-module-assertions/windows-1250.json&ct=text/json%3Bcharset=windows-1250" assert { type: "json"};
+ test(() => {
+ assert_not_equals(json.data, "śćążź",
+ 'Should be decoded as UTF-8');
+ }, "JSON module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header, and this document's encoding is windows-1250");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-bom.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-bom.any.js
new file mode 100644
index 0000000000..d2dbe3e468
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset-bom.any.js
@@ -0,0 +1,17 @@
+// META: global=window,dedicatedworker,sharedworker
+// META: script=/common/utils.js
+
+promise_test(async () => {
+ const jsonModule = await import('./bom-utf-8.json', { assert: { type: 'json' } });
+ assert_equals(jsonModule.default.data, 'hello');
+}, 'UTF-8 BOM should be stripped when decoding JSON module script');
+
+promise_test(async test => {
+ await promise_rejects_js(test, SyntaxError,
+ import('./bom-utf-16be.json', { assert: { type: 'json' } }), 'Expected parse error from UTF-16BE BOM');
+}, 'UTF-16BE BOM should result in parse error in JSON module script');
+
+promise_test(async test => {
+ await promise_rejects_js(test, SyntaxError,
+ import('./bom-utf-16le.json', { assert: { type: 'json' } }), 'Expected parse error from UTF-16LE BOM');
+}, 'UTF-16LE BOM should result in parse error in JSON module script');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset.html
new file mode 100644
index 0000000000..7c74b9bf6c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/charset.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module-assertions/utf-8.json&ct=text/json%3Bcharset=utf-8" assert { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=utf8 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module-assertions/utf-8.json&ct=text/json%3Bcharset=shift-jis" assert { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=shift-jis is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module-assertions/utf-8.json&ct=text/json%3Bcharset=windows-1252" assert { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=windows-1252 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module-assertions/utf-8.json&ct=text/json%3Bcharset=utf-7" assert { type: "json"};;
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=utf-7 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module-assertions/windows-1250.json&ct=text/json%3Bcharset=windows-1250" assert { type: "json"};
+ test(() => {
+ assert_not_equals(json.data, "śćążź",
+ 'Should be decoded as UTF-8');
+ }, "JSON module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cors-crossorigin-requests.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cors-crossorigin-requests.html
new file mode 100644
index 0000000000..757af2901c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cors-crossorigin-requests.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+<head>
+ <title>json-module-assertions-crossorigin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>json-module-assertions-crossorigin</h1>
+ <iframe id="import-WithCORS" src="crossorigin-import-with-cors.sub.html"></iframe>
+ <iframe id="import-NoCORS" src="crossorigin-import-without-cors.sub.html"></iframe>
+ <iframe id="import-parseerror-WithCors" src="crossorigin-import-parse-error-with-cors.sub.html"></iframe>
+ <script>
+
+ var tests = [
+ { "obj": async_test("Imported JSON module, cross-origin with CORS"), "id": "import-WithCORS", "expected": "imported JSON: 42" },
+ { "obj": async_test("Imported JSON module, cross-origin, missing CORS ACAO header"), "id": "import-NoCORS", "expected": "error" },
+ { "obj": async_test("Imported JSON module with parse error, cross-origin, with CORS"), "id": "import-parseerror-WithCors", "expected": "0-0" },
+ ];
+
+ window.addEventListener("load", function () {
+ tests.forEach(function (test) {
+ var target = document.getElementById(test.id);
+ test.obj.step(function () {
+ assert_equals(target.contentDocument._log, test.expected, "Unexpected _log value");
+ });
+ test.obj.done();
+ });
+ });
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials-iframe.sub.html
new file mode 100644
index 0000000000..44e6402faa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials-iframe.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script type="module">
+ import json from "./cross-origin.py?id=sameOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "json" };
+ window.sameOriginNoneDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="anonymous">
+ import json from "./cross-origin.py?id=sameOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "json" };
+ window.sameOriginAnonymousDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="use-credentials">
+ import json from "./cross-origin.py?id=sameOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "json" };
+ window.sameOriginUseCredentialsDescendant = json.requestHadCookies;
+</script>
+<script type="module">
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/cross-origin.py?id=crossOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "json" };
+ window.crossOriginNoneDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="anonymous">
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/cross-origin.py?id=crossOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "json" };
+ window.crossOriginAnonymousDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="use-credentials">
+import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/cross-origin.py?id=crossOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" assert { type: "json" };
+window.crossOriginUseCredentialsDescendant = json.requestHadCookies;
+</script>
+
+<script type="text/javascript">
+window.addEventListener('load', event => {
+ window.parent.postMessage({}, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials.sub.html
new file mode 100644
index 0000000000..d11d2f9589
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/credentials.sub.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+document.cookie = 'milk=1';
+
+const setCookiePromise = fetch(
+ 'http://{{domains[www2]}}:{{ports[http][0]}}/cookies/resources/set-cookie.py?name=milk&path=/html/semantics/scripting-1/the-script-element/json-module-assertions/',
+ {
+ mode: 'no-cors',
+ credentials: 'include',
+ });
+
+const windowLoadPromise = new Promise(resolve => {
+ window.addEventListener('load', () => {
+ resolve();
+ });
+});
+
+promise_test(t => {
+ const iframe = document.createElement('iframe');
+
+ return Promise.all([setCookiePromise, windowLoadPromise]).then(() => {
+ const messagePromise = new Promise(resolve => {
+ window.addEventListener('message', event => {
+ resolve();
+ });
+ });
+
+ iframe.src = 'credentials-iframe.sub.html';
+ document.body.appendChild(iframe);
+
+ return messagePromise;
+ }).then(() => {
+ const w = iframe.contentWindow;
+
+ assert_equals(w.sameOriginNoneDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is not specified and the target is same-origin');
+ assert_equals(w.sameOriginAnonymousDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is same-origin');
+ assert_equals(w.sameOriginUseCredentialsDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is same-origin');
+ assert_equals(w.crossOriginNoneDescendant, false,
+ 'Descendant JSON modules should not be loaded with the credentials when the crossOrigin attribute is not specified and the target is cross-origin');
+ assert_equals(w.crossOriginAnonymousDescendant, false,
+ 'Descendant JSON modules should not be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is cross-origin');
+ assert_equals(w.crossOriginUseCredentialsDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is cross-origin');
+});
+}, 'JSON Modules should be loaded with or without the credentials based on the same-origin-ness and the crossOrigin attribute');
+</script>
+<body>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cross-origin.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cross-origin.py
new file mode 100644
index 0000000000..cd56c3628a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/cross-origin.py
@@ -0,0 +1,16 @@
+def main(request, response):
+
+ headers = [
+ (b"Content-Type", b"application/json"),
+ (b"Access-Control-Allow-Origin", request.GET.first(b"origin")),
+ (b"Access-Control-Allow-Credentials", b"true")
+ ]
+
+ milk = request.cookies.first(b"milk", None)
+
+ if milk is None:
+ return headers, u'{"requestHadCookies": false}'
+ elif milk.value == b"1":
+ return headers, u'{"requestHadCookies": true}'
+
+ return headers, u'{"requestHadCookies": false}'
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-parse-error-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-parse-error-with-cors.sub.html
new file mode 100644
index 0000000000..7d044f8579
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-parse-error-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>json-module-assertions-import-cross-domain-parse-error-WithCORS</title>
+ <script src="../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>json-module-assertions-import-cross-domain-parse-error-WithCORS</h1>
+ <script type="module" crossorigin>
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.json?pipe=header(Access-Control-Allow-Origin,*)" assert { type: "json" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported JSON: ${json.answer}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-with-cors.sub.html
new file mode 100644
index 0000000000..d71938dae4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>json-module-assertions-import-cross-domain-WithCORS</title>
+ <script src="../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>json-module-assertions-import-cross-domain-WithCORS</h1>
+ <script type="module" crossorigin>
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/data.json?pipe=header(Access-Control-Allow-Origin,*)" assert { type: "json" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported JSON: ${json.answer}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-without-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-without-cors.sub.html
new file mode 100644
index 0000000000..9d07d6c727
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/crossorigin-import-without-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>json-module-assertions-import-cross-domain-NoCORS</title>
+ <script src="../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>json-module-assertions-import-cross-domain-NoCORS</h1>
+ <script type="module" onerror="document._log.push('error');">
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/data.json" assert { type: "json" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported JSON: ${json.answer}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/data.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/data.json
new file mode 100644
index 0000000000..14a0526ebb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/data.json
@@ -0,0 +1,3 @@
+{
+ "answer": 42
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/false.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/false.json
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/false.json
@@ -0,0 +1 @@
+false
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-matches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-matches.js
new file mode 100644
index 0000000000..969c90c290
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-matches.js
@@ -0,0 +1,2 @@
+import json from "./data.json" assert { type: "json" };
+window.matchesLog.push(`integrity-matches,json:${json.answer}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-mismatches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-mismatches.js
new file mode 100644
index 0000000000..3c88a98dbc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity-mismatches.js
@@ -0,0 +1,2 @@
+import json "./data.json" assert { type: "json" };
+window.mismatchesLog.push(`integrity-mismatches,json:${json.answer}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity.html
new file mode 100644
index 0000000000..68a794b973
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/integrity.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;script> integrity=""</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+window.matchesLog = [];
+window.matchesEvents = [];
+
+window.mismatchesLog = [];
+window.mismatchesEvents = [];
+</script>
+<script type="module" src="integrity-matches.js" integrity="sha384-VmQQfGzBiLKdyzw4FA4kL4ohu4tyujV68ddgW1aN/1v3cBZNNBn2gDFdVQxfL7+a" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script>
+<script type="module" src="integrity-mismatches.js" integrity="sha384-doesnotmatch" onload="window.mismatchesEvents.push('load');" onerror="window.mismatchesEvents.push('error')"></script>
+
+<script type="module">
+test(() => {
+ assert_array_equals(window.matchesLog, ["integrity-matches,json:42"], "The module and its dependency must have executed");
+ assert_array_equals(window.matchesEvents, ["load"], "The load event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a JSON module and allow it to execute when it matches");
+
+test(() => {
+ assert_array_equals(window.mismatchesLog, [], "The module and its dependency must not have executed");
+ assert_array_equals(window.mismatchesEvents, ["error"], "The error event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a JSON module and not allow it to execute when there's a mismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/invalid-content-type.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/invalid-content-type.any.js
new file mode 100644
index 0000000000..cbccbd4842
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/invalid-content-type.any.js
@@ -0,0 +1,17 @@
+// META: global=window,dedicatedworker,sharedworker
+
+const content_types = [
+ "application/json+protobuf",
+ "application/json+blah",
+ "text/x-json",
+ "text/json+blah",
+ "application/blahjson",
+ "image/json",
+];
+for (const content_type of content_types) {
+ promise_test(async test => {
+ await promise_rejects_js(test, TypeError,
+ import(`./module.json?pipe=header(Content-Type,${content_type})`, { assert: { type: "json"} }),
+ `Import of a JSON module with MIME type ${content_type} should fail`);
+ }, `Try importing JSON module with MIME type ${content_type}`);
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/json-module-service-worker-test.https.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/json-module-service-worker-test.https.html
new file mode 100644
index 0000000000..cc47da1499
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/json-module-service-worker-test.https.html
@@ -0,0 +1,29 @@
+<!doctype html>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ promise_test(async (test) => {
+ const reg = await navigator.serviceWorker.register('./serviceworker.js', { type: 'module' });
+ test.add_cleanup(() => reg.unregister());
+ assert_not_equals(reg.installing, undefined);
+ }, "Javascript importing JSON Module should load within the context of a service worker");
+
+ promise_test(test => {
+ return promise_rejects_dom(test, "SecurityError",
+ navigator.serviceWorker.register('./module.json', { type: 'module' }),
+ "Attempting to load JSON as a service worker should fail");
+ }, "Trying to register a service worker with a top-level JSON Module should fail");
+
+ promise_test(async (test) => {
+ const reg = await navigator.serviceWorker.register('./serviceworker-dynamic-import.js', { type: 'module' });
+ test.add_cleanup(() => reg.unregister());
+ assert_not_equals(reg.installing, undefined);
+ reg.installing.postMessage("PING");
+ const msgEvent = await new Promise(resolve => {
+ navigator.serviceWorker.onmessage = resolve;
+ });
+ assert_equals(msgEvent.data, "FAILED");
+ }, "JSON Module dynamic import should not load within the context of a service worker");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.html
new file mode 100644
index 0000000000..a9dfc1e691
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for JSON modules</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+ "use strict";
+
+ var test1_load = event_test('inline, 200, parser-inserted', false, false);
+ var test1_error = event_test('inline, 404, parser-inserted', false, true);
+
+ var test2_load = event_test('src, 200, parser-inserted', true, false);
+ var test2_error = event_test('src, 404, parser-inserted', false, true);
+
+ var test3_dynamic_load = event_test('src, 200, not parser-inserted', true, false);
+ var test3_dynamic_error = event_test('src, 404, not parser-inserted', false, true);
+
+ var test4_dynamic_load = event_test('inline, 200, not parser-inserted', false, false);
+ var test4_dynamic_error = event_test('inline, 404, not parser-inserted', false, true);
+
+ var script3_dynamic_load = document.createElement('script');
+ script3_dynamic_load.setAttribute('type', 'module');
+ script3_dynamic_load.onload = () => onLoad(test3_dynamic_load);
+ script3_dynamic_load.onerror = () => onError(test3_dynamic_load);
+ script3_dynamic_load.src = "./load-error-events.py?test=test3_dynamic_load";
+ document.head.appendChild(script3_dynamic_load);
+
+ var script3_dynamic_error = document.createElement('script');
+ script3_dynamic_error.setAttribute('type', 'module');
+ script3_dynamic_error.onload = () => onLoad(test3_dynamic_error);
+ script3_dynamic_error.onerror = () => onError(test3_dynamic_error);
+ script3_dynamic_error.src = "./load-error-events.py?test=test3_dynamic_error";
+ document.head.appendChild(script3_dynamic_error);
+
+ var script4_dynamic_load = document.createElement('script');
+ script4_dynamic_load.setAttribute('type', 'module');
+ script4_dynamic_load.onload = () => onLoad(test4_dynamic_load);
+ script4_dynamic_load.onerror = () => onError(test4_dynamic_load);
+ script4_dynamic_load.async = true;
+ script4_dynamic_load.appendChild(document.createTextNode(`
+ import "./module.json" assert { type: "json" };
+ onExecute(test4_dynamic_load);`
+ ));
+ document.head.appendChild(script4_dynamic_load);
+
+ var script4_dynamic_error = document.createElement('script');
+ script4_dynamic_error.setAttribute('type', 'module');
+ script4_dynamic_error.onload = () => onLoad(test4_dynamic_error);
+ script4_dynamic_error.onerror = () => onError(test4_dynamic_error);
+ script4_dynamic_error.async = true;
+ script4_dynamic_error.appendChild(document.createTextNode(`import "./not_found.json" assert { type: "json" };`));
+ document.head.appendChild(script4_dynamic_error);
+</script>
+<script onload="onLoad(test1_load);" onerror="onError(test1_load);" type="module">
+ import "./module.json" assert { type: "json"};
+ onExecute(test1_load);
+</script>
+<script onload="onLoad(test1_error);" onerror="onError(test1_error);" type="module">
+ import "./not_found.json" assert { type: "json"};
+ onExecute(test1_error);
+</script>
+<script src="./load-error-events.py?test=test2_load" onload="onLoad(test2_load);" onerror="onError(test2_load);" type="module"></script>
+<script src="./load-error-events.py?test=test2_error" onload="onLoad(test2_error);" onerror="onError(test2_error);" type="module"></script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.py
new file mode 100644
index 0000000000..4018adcdf7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/load-error-events.py
@@ -0,0 +1,14 @@
+import re
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ test = request.GET.first(b'test')
+ assert(re.match(b'^[a-zA-Z0-9_]+$', test))
+
+ status = 200
+ if test.find(b'_load') >= 0:
+ content = b'import "./module.json" assert { type: "json"}; %s.executed = true;' % test
+ else:
+ content = b'import "./not_found.json" assert { type: "json"}; %s.test.step(function() { assert_unreached("404 script should not be executed"); });' % test
+
+ return status, headers, content
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.html
new file mode 100644
index 0000000000..a495d4ac18
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+const t = async_test();
+</script>
+<script type="module" onerror="t.step(() => assert_unreached(event))">
+import v from "./module.json" assert { type: "json" };
+t.step(() => {
+ assert_equals(typeof v, "object");
+ assert_array_equals(Object.keys(v), ["test"]);
+ assert_equals(v.test, true);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.json
new file mode 100644
index 0000000000..f834b2a4e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/module.json
@@ -0,0 +1,3 @@
+{
+ "test": true
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/non-object.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/non-object.any.js
new file mode 100644
index 0000000000..37fbcae9fa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/non-object.any.js
@@ -0,0 +1,14 @@
+// META: global=window,dedicatedworker,sharedworker
+
+for (const value of [null, true, false, "string"]) {
+ promise_test(async t => {
+ const result = await import(`./${value}.json`, { assert: { type: "json" } });
+ assert_equals(result.default, value);
+ }, `Non-object: ${value}`);
+}
+
+promise_test(async t => {
+ const result = await import("./array.json", { assert: { type: "json" } });
+ assert_array_equals(result.default, ["en", "try"]);
+}, "Non-object: array");
+
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/null.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/null.json
new file mode 100644
index 0000000000..19765bd501
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/null.json
@@ -0,0 +1 @@
+null
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.html
new file mode 100644
index 0000000000..9d0e3284fe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules: parse error</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+setup({
+ allow_uncaught_exception: true,
+});
+async_test(t => {
+ window.addEventListener("error", t.step_func_done(e => {
+ assert_true(e instanceof ErrorEvent, "ErrorEvent");
+ assert_equals(e.filename, new URL("parse-error.json", location).href);
+ assert_true(e.error instanceof SyntaxError, "SyntaxError");
+ }));
+});
+</script>
+<script type="module">
+import v from "./parse-error.json" assert { type: "json" };
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.json
new file mode 100644
index 0000000000..98232c64fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/parse-error.json
@@ -0,0 +1 @@
+{
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py
new file mode 100644
index 0000000000..e9f0f1789b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py
@@ -0,0 +1,6 @@
+def main(request, response):
+ referrer = request.headers.get(b"referer", b"")
+ response_headers = [(b"Content-Type", b"application/json"),
+ (b"Access-Control-Allow-Origin", b"*")]
+ return (200, response_headers,
+ b'{"referrer": "' + referrer + b'"}')
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-policies.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-policies.sub.html
new file mode 100644
index 0000000000..655c962ab7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-policies.sub.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrers with JSON module requests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+ // "name" parameter is necessary for bypassing the module map.
+ import referrerSame from "./referrer-checker.py?name=sameNoReferrerPolicy" assert { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py?name=remoteNoReferrerPolicy" assert { type: "json"};
+
+ const origin = (new URL(location.href)).origin + "/";
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, originUrl,
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the default referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, origin,
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the default referrer policy.");
+</script>
+<script type="module" referrerpolicy="origin">
+ import referrerSame from "./referrer-checker.py?name=sameReferrerPolicyOrigin" assert { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py?name=remoteReferrerPolicyOrigin" assert { type: "json"};
+
+ const origin = (new URL(location.href)).origin + "/";
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, origin,
+ "Referrer origin should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the origin policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, origin,
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the origin policy.");
+
+</script>
+<script type="module" referrerpolicy="no-referrer">
+ import referrerSame from "./referrer-checker.py?name=sameReferrerPolicyNoReferrer" assert { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py?name=remoteReferrerPolicyNoReferrer" assert { type: "json"};
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, "",
+ "No referrer should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the no-referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, "",
+ "No referrer should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the no-referrer policy.");
+
+</script>
+<script type="module" referrerpolicy="unsafe-url">
+ import referrerSame from "./referrer-checker.py?name=sameNoReferrerPolicyUnsafeUrl" assert { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module-assertions/referrer-checker.py?name=remoteNoReferrerPolicyUnsafeUrl" assert { type: "json"};
+
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, originUrl,
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the unsafe-url referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, originUrl,
+ "Referrer URL should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the unsafe-url referrer policy.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/repeated-imports.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/repeated-imports.any.js
new file mode 100644
index 0000000000..5cc3ee5b7c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/repeated-imports.any.js
@@ -0,0 +1,65 @@
+// META: global=window,dedicatedworker,sharedworker
+// META: script=/common/utils.js
+
+promise_test(async test => {
+ await promise_rejects_js(test, TypeError,
+ import("./module.json"),
+ "Dynamic import of a JSON module without a type assertion should fail");
+
+ // This time the import should succeed because we're using the correct
+ // import even though the previous attempt with the same specifier failed.
+ const result = await import("./module.json", { assert: { type: "json" } });
+ assert_true(result.default.test);
+}, "Importing a specifier that previously failed due to an incorrect type assertion can succeed if the correct assertion is later given");
+
+promise_test(async test => {
+ // Append a URL fragment to the specifier so that this is independent
+ // from the previous test.
+ const result = await import("./module.json#2", { assert: { type: "json" } });
+ assert_true(result.default.test);
+
+ await promise_rejects_js(test, TypeError,
+ import("./module.json#2"),
+ "Dynamic import should fail with the type assertion missing even if the same specifier previously succeeded");
+}, "Importing a specifier that previously succeeded with the correct type assertion should fail if the incorrect assertion is later given");
+
+promise_test(async test => {
+ const uuid_token = token();
+ // serve-json-then-js.py gives us JSON the first time
+ const result_json = await import(`../serve-json-then-js.py?key=${uuid_token}`, { assert: { type: "json" } });
+ assert_equals(result_json.default.hello, "world");
+
+ // Import using the same specifier again; this time we get JS, which
+ // should succeed since we're not asserting a non-JS type this time.
+ const result_js = await import(`../serve-json-then-js.py?key=${uuid_token}`);
+ assert_equals(result_js.default, "hello");
+}, "Two modules of different type with the same specifier can load if the server changes its responses");
+
+promise_test(async test => {
+ const uuid_token = token();
+ // serve-json-then-js.py gives us JSON the first time
+ await promise_rejects_js(test, TypeError,
+ import(`../serve-json-then-js.py?key=${uuid_token}`),
+ "Dynamic import of JS with a JSON type assertion should fail");
+
+ // Import using the same specifier/module type pair again; this time we get JS,
+ // but the import should still fail because the module map entry for this
+ // specifier/module type pair already contains a failure.
+ await promise_rejects_js(test, TypeError,
+ import(`../serve-json-then-js.py?key=${uuid_token}`),
+ "import should always fail if the same specifier/type assertion pair failed previously");
+}, "An import should always fail if the same specifier/type assertion pair failed previously");
+
+promise_test(async test => {
+ const uuid_token = token();
+ // serve-json-then-js.py gives us JSON the first time
+ const result_json = await import(`../serve-json-then-js.py?key=${uuid_token}`, { assert: { type: "json" } });
+ assert_equals(result_json.default.hello, "world");
+
+ // If this were to do another fetch, the import would fail because
+ // serve-json-then-js.py would give us JS this time. But, the module map
+ // entry for this specifier/module type pair already exists, so we
+ // successfully reuse the entry instead of fetching again.
+ const result_json_2 = await import(`../serve-json-then-js.py?key=${uuid_token}`, { assert: { type: "json" } });
+ assert_equals(result_json_2.default.hello, "world");
+}, "If an import previously succeeded for a given specifier/type assertion pair, future uses of that pair should yield the same result");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/script-element-json-src.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/script-element-json-src.html
new file mode 100644
index 0000000000..c6d7c9a76e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/script-element-json-src.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>&lt;script&gt; with JSON src</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that <script> doesn't load when the src is JSON.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["error"]);
+ }));
+</script>
+<script type="module" src="./module.json" onload="t.unreached_func('JSON src should fail to load')" onerror="log.push('error')"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker-dynamic-import.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker-dynamic-import.js
new file mode 100644
index 0000000000..9466c6fbe4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker-dynamic-import.js
@@ -0,0 +1,5 @@
+onmessage = e => {
+ e.waitUntil(import("./module.json", { assert: { type: "json" } })
+ .then(module => e.source.postMessage("LOADED"))
+ .catch(error => e.source.postMessage("FAILED")));
+ }; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker.js
new file mode 100644
index 0000000000..3f0a4d1664
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/serviceworker.js
@@ -0,0 +1 @@
+import './module.json' assert { type: "json" }; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/string.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/string.json
new file mode 100644
index 0000000000..ace2d72d9d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/string.json
@@ -0,0 +1 @@
+"string"
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/true.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/true.json
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/true.json
@@ -0,0 +1 @@
+true
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/utf-8.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/utf-8.json
new file mode 100644
index 0000000000..088d982358
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/utf-8.json
@@ -0,0 +1,4 @@
+{
+ "data": "śćążź",
+ "comment": "The data above are five Polish letters, similar to scazz. It can be read correctly only with utf-8 encoding."
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/valid-content-type.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/valid-content-type.html
new file mode 100644
index 0000000000..162f2d6afc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/valid-content-type.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules: Content-Type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function check(t, v) {
+ t.step(() => {
+ assert_equals(typeof v, "object");
+ assert_array_equals(Object.keys(v), ["test"]);
+ assert_equals(v.test, true);
+ t.done();
+ });
+}
+const t1 = async_test("text/json");
+const t2 = async_test("application/json");
+const t3 = async_test("text/html+json");
+const t4 = async_test("image/svg+json");
+const t5 = async_test("text/json;boundary=something");
+const t6 = async_test("text/json;foo=bar");
+</script>
+<script type="module" onerror="t1.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module-assertions/module.json&ct=text/json" assert { type: "json"};
+check(t1, v);
+</script>
+<script type="module" onerror="t2.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module-assertions/module.json&ct=application/json" assert { type: "json"};
+check(t2, v);
+</script>
+<script type="module" onerror="t3.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module-assertions/module.json&ct=text/html%2Bjson" assert { type: "json"};
+check(t3, v);
+</script>
+<script type="module" onerror="t4.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module-assertions/module.json&ct=image/svg%2Bjson" assert { type: "json"};
+check(t4, v);
+</script>
+<script type="module" onerror="t5.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module-assertions/module.json&ct=text/json;boundary=something" assert { type: "json"};
+check(t5, v);
+</script>
+<script type="module" onerror="t6.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module-assertions/module.json&ct=text/json;foo=bar" assert { type: "json"};
+check(t6, v);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/windows-1250.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/windows-1250.json
new file mode 100644
index 0000000000..490e752ce9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module-assertions/windows-1250.json
@@ -0,0 +1,4 @@
+{
+ "data": "�湿�",
+ "comment": "The data above are five Polish letters, similar to scazz. It can be read correctly only with windows1250 encoding."
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/array.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/array.json
new file mode 100644
index 0000000000..e77e32d338
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/array.json
@@ -0,0 +1 @@
+["en", "try"]
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16be.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16be.json
new file mode 100644
index 0000000000..d22a45a591
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16be.json
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16le.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16le.json
new file mode 100644
index 0000000000..4d1aa264a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-16le.json
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-8.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-8.json
new file mode 100644
index 0000000000..07ba933e86
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/bom-utf-8.json
@@ -0,0 +1 @@
+{ "data": "hello" } \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-2.html
new file mode 100644
index 0000000000..dfadaba4d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-2.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="windows-1250">
+<title>JSON modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module">
+ import json from "../serve-with-content-type.py?fn=json-module/utf-8.json" with { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 even though document's encoding is windows-1250");
+</script>
+<script type="module">
+ import json from "../serve-with-content-type.py?fn=json-module/windows-1250.json&ct=text/json%3Bcharset=windows-1250" with { type: "json"};
+ test(() => {
+ assert_not_equals(json.data, "śćążź",
+ 'Should be decoded as UTF-8');
+ }, "JSON module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header, and this document's encoding is windows-1250");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-bom.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-bom.any.js
new file mode 100644
index 0000000000..483936a4f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset-bom.any.js
@@ -0,0 +1,17 @@
+// META: global=window,dedicatedworker,sharedworker
+// META: script=/common/utils.js
+
+promise_test(async () => {
+ const jsonModule = await import('./bom-utf-8.json', { with: { type: 'json' } });
+ assert_equals(jsonModule.default.data, 'hello');
+}, 'UTF-8 BOM should be stripped when decoding JSON module script');
+
+promise_test(async test => {
+ await promise_rejects_js(test, SyntaxError,
+ import('./bom-utf-16be.json', { with: { type: 'json' } }), 'Expected parse error from UTF-16BE BOM');
+}, 'UTF-16BE BOM should result in parse error in JSON module script');
+
+promise_test(async test => {
+ await promise_rejects_js(test, SyntaxError,
+ import('./bom-utf-16le.json', { with: { type: 'json' } }), 'Expected parse error from UTF-16LE BOM');
+}, 'UTF-16LE BOM should result in parse error in JSON module script');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset.html
new file mode 100644
index 0000000000..ce72f0ef1b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/charset.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules: UTF-8 decoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module/utf-8.json&ct=text/json%3Bcharset=utf-8" with { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=utf8 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module/utf-8.json&ct=text/json%3Bcharset=shift-jis" with { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=shift-jis is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module/utf-8.json&ct=text/json%3Bcharset=windows-1252" with { type: "json"};
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=windows-1252 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module/utf-8.json&ct=text/json%3Bcharset=utf-7" with { type: "json"};;
+ test(() => {
+ assert_equals(json.data, "śćążź");
+ }, "JSON module should be loaded as utf-8 when charset=utf-7 is specified");
+</script>
+<script type="module" onerror="unreachable()">
+ import json from "../serve-with-content-type.py?fn=json-module/windows-1250.json&ct=text/json%3Bcharset=windows-1250" with { type: "json"};
+ test(() => {
+ assert_not_equals(json.data, "śćążź",
+ 'Should be decoded as UTF-8');
+ }, "JSON module should be loaded as utf-8 even if it is encoded in windows-1250 and served with a windows-1250 charset response header");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cors-crossorigin-requests.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cors-crossorigin-requests.html
new file mode 100644
index 0000000000..99ff2f67e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cors-crossorigin-requests.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+<head>
+ <title>json-module-crossorigin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>json-module-crossorigin</h1>
+ <iframe id="import-WithCORS" src="crossorigin-import-with-cors.sub.html"></iframe>
+ <iframe id="import-NoCORS" src="crossorigin-import-without-cors.sub.html"></iframe>
+ <iframe id="import-parseerror-WithCors" src="crossorigin-import-parse-error-with-cors.sub.html"></iframe>
+ <script>
+
+ var tests = [
+ { "obj": async_test("Imported JSON module, cross-origin with CORS"), "id": "import-WithCORS", "expected": "imported JSON: 42" },
+ { "obj": async_test("Imported JSON module, cross-origin, missing CORS ACAO header"), "id": "import-NoCORS", "expected": "error" },
+ { "obj": async_test("Imported JSON module with parse error, cross-origin, with CORS"), "id": "import-parseerror-WithCors", "expected": "0-0" },
+ ];
+
+ window.addEventListener("load", function () {
+ tests.forEach(function (test) {
+ var target = document.getElementById(test.id);
+ test.obj.step(function () {
+ assert_equals(target.contentDocument._log, test.expected, "Unexpected _log value");
+ });
+ test.obj.done();
+ });
+ });
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials-iframe.sub.html
new file mode 100644
index 0000000000..b89edf8d31
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials-iframe.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script type="module">
+ import json from "./cross-origin.py?id=sameOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "json" };
+ window.sameOriginNoneDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="anonymous">
+ import json from "./cross-origin.py?id=sameOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "json" };
+ window.sameOriginAnonymousDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="use-credentials">
+ import json from "./cross-origin.py?id=sameOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "json" };
+ window.sameOriginUseCredentialsDescendant = json.requestHadCookies;
+</script>
+<script type="module">
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/cross-origin.py?id=crossOriginNoneDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "json" };
+ window.crossOriginNoneDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="anonymous">
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/cross-origin.py?id=crossOriginAnonymousDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "json" };
+ window.crossOriginAnonymousDescendant = json.requestHadCookies;
+</script>
+<script type="module" crossOrigin="use-credentials">
+import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/cross-origin.py?id=crossOriginUseCredentialsDescendant&origin=http://{{host}}:{{ports[http][0]}}" with { type: "json" };
+window.crossOriginUseCredentialsDescendant = json.requestHadCookies;
+</script>
+
+<script type="text/javascript">
+window.addEventListener('load', event => {
+ window.parent.postMessage({}, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials.sub.html
new file mode 100644
index 0000000000..a6df506e21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/credentials.sub.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+document.cookie = 'milk=1';
+
+const setCookiePromise = fetch(
+ 'http://{{domains[www2]}}:{{ports[http][0]}}/cookies/resources/set-cookie.py?name=milk&path=/html/semantics/scripting-1/the-script-element/json-module/',
+ {
+ mode: 'no-cors',
+ credentials: 'include',
+ });
+
+const windowLoadPromise = new Promise(resolve => {
+ window.addEventListener('load', () => {
+ resolve();
+ });
+});
+
+promise_test(t => {
+ const iframe = document.createElement('iframe');
+
+ return Promise.all([setCookiePromise, windowLoadPromise]).then(() => {
+ const messagePromise = new Promise(resolve => {
+ window.addEventListener('message', event => {
+ resolve();
+ });
+ });
+
+ iframe.src = 'credentials-iframe.sub.html';
+ document.body.appendChild(iframe);
+
+ return messagePromise;
+ }).then(() => {
+ const w = iframe.contentWindow;
+
+ assert_equals(w.sameOriginNoneDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is not specified and the target is same-origin');
+ assert_equals(w.sameOriginAnonymousDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is same-origin');
+ assert_equals(w.sameOriginUseCredentialsDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is same-origin');
+ assert_equals(w.crossOriginNoneDescendant, false,
+ 'Descendant JSON modules should not be loaded with the credentials when the crossOrigin attribute is not specified and the target is cross-origin');
+ assert_equals(w.crossOriginAnonymousDescendant, false,
+ 'Descendant JSON modules should not be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is cross-origin');
+ assert_equals(w.crossOriginUseCredentialsDescendant, true,
+ 'Descendant JSON modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is cross-origin');
+});
+}, 'JSON Modules should be loaded with or without the credentials based on the same-origin-ness and the crossOrigin attribute');
+</script>
+<body>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cross-origin.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cross-origin.py
new file mode 100644
index 0000000000..cd56c3628a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/cross-origin.py
@@ -0,0 +1,16 @@
+def main(request, response):
+
+ headers = [
+ (b"Content-Type", b"application/json"),
+ (b"Access-Control-Allow-Origin", request.GET.first(b"origin")),
+ (b"Access-Control-Allow-Credentials", b"true")
+ ]
+
+ milk = request.cookies.first(b"milk", None)
+
+ if milk is None:
+ return headers, u'{"requestHadCookies": false}'
+ elif milk.value == b"1":
+ return headers, u'{"requestHadCookies": true}'
+
+ return headers, u'{"requestHadCookies": false}'
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-parse-error-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-parse-error-with-cors.sub.html
new file mode 100644
index 0000000000..9972c53d1b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-parse-error-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>json-module-import-cross-domain-parse-error-WithCORS</title>
+ <script src="../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>json-module-import-cross-domain-parse-error-WithCORS</h1>
+ <script type="module" crossorigin>
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/parse-error.json?pipe=header(Access-Control-Allow-Origin,*)" with { type: "json" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported JSON: ${json.answer}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-with-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-with-cors.sub.html
new file mode 100644
index 0000000000..95fd156df2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-with-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>json-module-import-cross-domain-WithCORS</title>
+ <script src="../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>json-module-import-cross-domain-WithCORS</h1>
+ <script type="module" crossorigin>
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/data.json?pipe=header(Access-Control-Allow-Origin,*)" with { type: "json" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported JSON: ${json.answer}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-without-cors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-without-cors.sub.html
new file mode 100644
index 0000000000..b9318c8b20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/crossorigin-import-without-cors.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>json-module-import-cross-domain-NoCORS</title>
+ <script src="../module/crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>json-module-import-cross-domain-NoCORS</h1>
+ <script type="module" onerror="document._log.push('error');">
+ import json from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/data.json" with { type: "json" };
+ // Push an event to the log indicating that the script was executed.
+ document._log.push(`imported JSON: ${json.answer}`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/data.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/data.json
new file mode 100644
index 0000000000..14a0526ebb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/data.json
@@ -0,0 +1,3 @@
+{
+ "answer": 42
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/false.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/false.json
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/false.json
@@ -0,0 +1 @@
+false
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-matches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-matches.js
new file mode 100644
index 0000000000..20459c17e3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-matches.js
@@ -0,0 +1,2 @@
+import json from "./data.json" with { type: "json" };
+window.matchesLog.push(`integrity-matches,json:${json.answer}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-mismatches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-mismatches.js
new file mode 100644
index 0000000000..0406dbcca5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity-mismatches.js
@@ -0,0 +1,2 @@
+import json "./data.json" with { type: "json" };
+window.mismatchesLog.push(`integrity-mismatches,json:${json.answer}`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html
new file mode 100644
index 0000000000..68a794b973
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;script> integrity=""</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+window.matchesLog = [];
+window.matchesEvents = [];
+
+window.mismatchesLog = [];
+window.mismatchesEvents = [];
+</script>
+<script type="module" src="integrity-matches.js" integrity="sha384-VmQQfGzBiLKdyzw4FA4kL4ohu4tyujV68ddgW1aN/1v3cBZNNBn2gDFdVQxfL7+a" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script>
+<script type="module" src="integrity-mismatches.js" integrity="sha384-doesnotmatch" onload="window.mismatchesEvents.push('load');" onerror="window.mismatchesEvents.push('error')"></script>
+
+<script type="module">
+test(() => {
+ assert_array_equals(window.matchesLog, ["integrity-matches,json:42"], "The module and its dependency must have executed");
+ assert_array_equals(window.matchesEvents, ["load"], "The load event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a JSON module and allow it to execute when it matches");
+
+test(() => {
+ assert_array_equals(window.mismatchesLog, [], "The module and its dependency must not have executed");
+ assert_array_equals(window.mismatchesEvents, ["error"], "The error event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module loading a JSON module and not allow it to execute when there's a mismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.any.js
new file mode 100644
index 0000000000..4226c3dc03
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.any.js
@@ -0,0 +1,17 @@
+// META: global=window,dedicatedworker,sharedworker
+
+const content_types = [
+ "application/json+protobuf",
+ "application/json+blah",
+ "text/x-json",
+ "text/json+blah",
+ "application/blahjson",
+ "image/json",
+];
+for (const content_type of content_types) {
+ promise_test(async test => {
+ await promise_rejects_js(test, TypeError,
+ import(`./module.json?pipe=header(Content-Type,${content_type})`, { with: { type: "json"} }),
+ `Import of a JSON module with MIME type ${content_type} should fail`);
+ }, `Try importing JSON module with MIME type ${content_type}`);
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.html
new file mode 100644
index 0000000000..cc47da1499
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.html
@@ -0,0 +1,29 @@
+<!doctype html>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ promise_test(async (test) => {
+ const reg = await navigator.serviceWorker.register('./serviceworker.js', { type: 'module' });
+ test.add_cleanup(() => reg.unregister());
+ assert_not_equals(reg.installing, undefined);
+ }, "Javascript importing JSON Module should load within the context of a service worker");
+
+ promise_test(test => {
+ return promise_rejects_dom(test, "SecurityError",
+ navigator.serviceWorker.register('./module.json', { type: 'module' }),
+ "Attempting to load JSON as a service worker should fail");
+ }, "Trying to register a service worker with a top-level JSON Module should fail");
+
+ promise_test(async (test) => {
+ const reg = await navigator.serviceWorker.register('./serviceworker-dynamic-import.js', { type: 'module' });
+ test.add_cleanup(() => reg.unregister());
+ assert_not_equals(reg.installing, undefined);
+ reg.installing.postMessage("PING");
+ const msgEvent = await new Promise(resolve => {
+ navigator.serviceWorker.onmessage = resolve;
+ });
+ assert_equals(msgEvent.data, "FAILED");
+ }, "JSON Module dynamic import should not load within the context of a service worker");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.html
new file mode 100644
index 0000000000..54c1892540
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for JSON modules</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+ "use strict";
+
+ var test1_load = event_test('inline, 200, parser-inserted', false, false);
+ var test1_error = event_test('inline, 404, parser-inserted', false, true);
+
+ var test2_load = event_test('src, 200, parser-inserted', true, false);
+ var test2_error = event_test('src, 404, parser-inserted', false, true);
+
+ var test3_dynamic_load = event_test('src, 200, not parser-inserted', true, false);
+ var test3_dynamic_error = event_test('src, 404, not parser-inserted', false, true);
+
+ var test4_dynamic_load = event_test('inline, 200, not parser-inserted', false, false);
+ var test4_dynamic_error = event_test('inline, 404, not parser-inserted', false, true);
+
+ var script3_dynamic_load = document.createElement('script');
+ script3_dynamic_load.setAttribute('type', 'module');
+ script3_dynamic_load.onload = () => onLoad(test3_dynamic_load);
+ script3_dynamic_load.onerror = () => onError(test3_dynamic_load);
+ script3_dynamic_load.src = "./load-error-events.py?test=test3_dynamic_load";
+ document.head.appendChild(script3_dynamic_load);
+
+ var script3_dynamic_error = document.createElement('script');
+ script3_dynamic_error.setAttribute('type', 'module');
+ script3_dynamic_error.onload = () => onLoad(test3_dynamic_error);
+ script3_dynamic_error.onerror = () => onError(test3_dynamic_error);
+ script3_dynamic_error.src = "./load-error-events.py?test=test3_dynamic_error";
+ document.head.appendChild(script3_dynamic_error);
+
+ var script4_dynamic_load = document.createElement('script');
+ script4_dynamic_load.setAttribute('type', 'module');
+ script4_dynamic_load.onload = () => onLoad(test4_dynamic_load);
+ script4_dynamic_load.onerror = () => onError(test4_dynamic_load);
+ script4_dynamic_load.async = true;
+ script4_dynamic_load.appendChild(document.createTextNode(`
+ import "./module.json" with { type: "json" };
+ onExecute(test4_dynamic_load);`
+ ));
+ document.head.appendChild(script4_dynamic_load);
+
+ var script4_dynamic_error = document.createElement('script');
+ script4_dynamic_error.setAttribute('type', 'module');
+ script4_dynamic_error.onload = () => onLoad(test4_dynamic_error);
+ script4_dynamic_error.onerror = () => onError(test4_dynamic_error);
+ script4_dynamic_error.async = true;
+ script4_dynamic_error.appendChild(document.createTextNode(`import "./not_found.json" with { type: "json" };`));
+ document.head.appendChild(script4_dynamic_error);
+</script>
+<script onload="onLoad(test1_load);" onerror="onError(test1_load);" type="module">
+ import "./module.json" with { type: "json"};
+ onExecute(test1_load);
+</script>
+<script onload="onLoad(test1_error);" onerror="onError(test1_error);" type="module">
+ import "./not_found.json" with { type: "json"};
+ onExecute(test1_error);
+</script>
+<script src="./load-error-events.py?test=test2_load" onload="onLoad(test2_load);" onerror="onError(test2_load);" type="module"></script>
+<script src="./load-error-events.py?test=test2_error" onload="onLoad(test2_error);" onerror="onError(test2_error);" type="module"></script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.py
new file mode 100644
index 0000000000..244552a693
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/load-error-events.py
@@ -0,0 +1,14 @@
+import re
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ test = request.GET.first(b'test')
+ assert(re.match(b'^[a-zA-Z0-9_]+$', test))
+
+ status = 200
+ if test.find(b'_load') >= 0:
+ content = b'import "./module.json" with { type: "json"}; %s.executed = true;' % test
+ else:
+ content = b'import "./not_found.json" with { type: "json"}; %s.test.step(function() { assert_unreached("404 script should not be executed"); });' % test
+
+ return status, headers, content
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.html
new file mode 100644
index 0000000000..05fc264f36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+const t = async_test();
+</script>
+<script type="module" onerror="t.step(() => assert_unreached(event))">
+import v from "./module.json" with { type: "json" };
+t.step(() => {
+ assert_equals(typeof v, "object");
+ assert_array_equals(Object.keys(v), ["test"]);
+ assert_equals(v.test, true);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.json
new file mode 100644
index 0000000000..f834b2a4e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/module.json
@@ -0,0 +1,3 @@
+{
+ "test": true
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/non-object.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/non-object.any.js
new file mode 100644
index 0000000000..ae78ddf072
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/non-object.any.js
@@ -0,0 +1,14 @@
+// META: global=window,dedicatedworker,sharedworker
+
+for (const value of [null, true, false, "string"]) {
+ promise_test(async t => {
+ const result = await import(`./${value}.json`, { with: { type: "json" } });
+ assert_equals(result.default, value);
+ }, `Non-object: ${value}`);
+}
+
+promise_test(async t => {
+ const result = await import("./array.json", { with: { type: "json" } });
+ assert_array_equals(result.default, ["en", "try"]);
+}, "Non-object: array");
+
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/null.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/null.json
new file mode 100644
index 0000000000..19765bd501
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/null.json
@@ -0,0 +1 @@
+null
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.html
new file mode 100644
index 0000000000..88fb23a00d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules: parse error</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+setup({
+ allow_uncaught_exception: true,
+});
+async_test(t => {
+ window.addEventListener("error", t.step_func_done(e => {
+ assert_true(e instanceof ErrorEvent, "ErrorEvent");
+ assert_equals(e.filename, new URL("parse-error.json", location).href);
+ assert_true(e.error instanceof SyntaxError, "SyntaxError");
+ }));
+});
+</script>
+<script type="module">
+import v from "./parse-error.json" with { type: "json" };
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.json
new file mode 100644
index 0000000000..98232c64fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/parse-error.json
@@ -0,0 +1 @@
+{
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py
new file mode 100644
index 0000000000..e9f0f1789b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py
@@ -0,0 +1,6 @@
+def main(request, response):
+ referrer = request.headers.get(b"referer", b"")
+ response_headers = [(b"Content-Type", b"application/json"),
+ (b"Access-Control-Allow-Origin", b"*")]
+ return (200, response_headers,
+ b'{"referrer": "' + referrer + b'"}')
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-policies.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-policies.sub.html
new file mode 100644
index 0000000000..1509c853e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/referrer-policies.sub.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrers with JSON module requests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+ // "name" parameter is necessary for bypassing the module map.
+ import referrerSame from "./referrer-checker.py?name=sameNoReferrerPolicy" with { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py?name=remoteNoReferrerPolicy" with { type: "json"};
+
+ const origin = (new URL(location.href)).origin + "/";
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, originUrl,
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the default referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, origin,
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the default referrer policy.");
+</script>
+<script type="module" referrerpolicy="origin">
+ import referrerSame from "./referrer-checker.py?name=sameReferrerPolicyOrigin" with { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py?name=remoteReferrerPolicyOrigin" with { type: "json"};
+
+ const origin = (new URL(location.href)).origin + "/";
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, origin,
+ "Referrer origin should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the origin policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, origin,
+ "Referrer origin should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the origin policy.");
+
+</script>
+<script type="module" referrerpolicy="no-referrer">
+ import referrerSame from "./referrer-checker.py?name=sameReferrerPolicyNoReferrer" with { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py?name=remoteReferrerPolicyNoReferrer" with { type: "json"};
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, "",
+ "No referrer should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the no-referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, "",
+ "No referrer should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the no-referrer policy.");
+
+</script>
+<script type="module" referrerpolicy="unsafe-url">
+ import referrerSame from "./referrer-checker.py?name=sameNoReferrerPolicyUnsafeUrl" with { type: "json"};
+ import referrerRemote from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/json-module/referrer-checker.py?name=remoteNoReferrerPolicyUnsafeUrl" with { type: "json"};
+
+ const originUrl = location.href;
+
+ test(t => {
+ assert_equals(
+ referrerSame.referrer, originUrl,
+ "Referrer URL should be sent for the same-origin top-level script.");
+ }, "Importing a same-origin top-level script with the unsafe-url referrer policy.");
+
+ test(t => {
+ assert_equals(
+ referrerRemote.referrer, originUrl,
+ "Referrer URL should be sent for the remote-origin top-level script.");
+ }, "Importing a remote-origin top-level script with the unsafe-url referrer policy.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/repeated-imports.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/repeated-imports.any.js
new file mode 100644
index 0000000000..722251b84d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/repeated-imports.any.js
@@ -0,0 +1,65 @@
+// META: global=window,dedicatedworker,sharedworker
+// META: script=/common/utils.js
+
+promise_test(async test => {
+ await promise_rejects_js(test, TypeError,
+ import("./module.json"),
+ "Dynamic import of a JSON module without a type attribute should fail");
+
+ // This time the import should succeed because we're using the correct
+ // import even though the previous attempt with the same specifier failed.
+ const result = await import("./module.json", { with: { type: "json" } });
+ assert_true(result.default.test);
+}, "Importing a specifier that previously failed due to an incorrect type attribute can succeed if the correct attribute is later given");
+
+promise_test(async test => {
+ // Append a URL fragment to the specifier so that this is independent
+ // from the previous test.
+ const result = await import("./module.json#2", { with: { type: "json" } });
+ assert_true(result.default.test);
+
+ await promise_rejects_js(test, TypeError,
+ import("./module.json#2"),
+ "Dynamic import should fail with the type attribute missing even if the same specifier previously succeeded");
+}, "Importing a specifier that previously succeeded with the correct type attribute should fail if the incorrect attribute is later given");
+
+promise_test(async test => {
+ const uuid_token = token();
+ // serve-json-then-js.py gives us JSON the first time
+ const result_json = await import(`../serve-json-then-js.py?key=${uuid_token}`, { with: { type: "json" } });
+ assert_equals(result_json.default.hello, "world");
+
+ // Import using the same specifier again; this time we get JS, which
+ // should succeed since we're not asserting a non-JS type this time.
+ const result_js = await import(`../serve-json-then-js.py?key=${uuid_token}`);
+ assert_equals(result_js.default, "hello");
+}, "Two modules of different type with the same specifier can load if the server changes its responses");
+
+promise_test(async test => {
+ const uuid_token = token();
+ // serve-json-then-js.py gives us JSON the first time
+ await promise_rejects_js(test, TypeError,
+ import(`../serve-json-then-js.py?key=${uuid_token}`),
+ "Dynamic import of JS with a JSON type attribute should fail");
+
+ // Import using the same specifier/module type pair again; this time we get JS,
+ // but the import should still fail because the module map entry for this
+ // specifier/module type pair already contains a failure.
+ await promise_rejects_js(test, TypeError,
+ import(`../serve-json-then-js.py?key=${uuid_token}`),
+ "import should always fail if the same specifier/type attribute pair failed previously");
+}, "An import should always fail if the same specifier/type attribute pair failed previously");
+
+promise_test(async test => {
+ const uuid_token = token();
+ // serve-json-then-js.py gives us JSON the first time
+ const result_json = await import(`../serve-json-then-js.py?key=${uuid_token}`, { with: { type: "json" } });
+ assert_equals(result_json.default.hello, "world");
+
+ // If this were to do another fetch, the import would fail because
+ // serve-json-then-js.py would give us JS this time. But, the module map
+ // entry for this specifier/module type pair already exists, so we
+ // successfully reuse the entry instead of fetching again.
+ const result_json_2 = await import(`../serve-json-then-js.py?key=${uuid_token}`, { with: { type: "json" } });
+ assert_equals(result_json_2.default.hello, "world");
+}, "If an import previously succeeded for a given specifier/type attribute pair, future uses of that pair should yield the same result");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/script-element-json-src.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/script-element-json-src.html
new file mode 100644
index 0000000000..c6d7c9a76e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/script-element-json-src.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>&lt;script&gt; with JSON src</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that <script> doesn't load when the src is JSON.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["error"]);
+ }));
+</script>
+<script type="module" src="./module.json" onload="t.unreached_func('JSON src should fail to load')" onerror="log.push('error')"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker-dynamic-import.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker-dynamic-import.js
new file mode 100644
index 0000000000..cd39c789cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker-dynamic-import.js
@@ -0,0 +1,5 @@
+onmessage = e => {
+ e.waitUntil(import("./module.json", { with: { type: "json" } })
+ .then(module => e.source.postMessage("LOADED"))
+ .catch(error => e.source.postMessage("FAILED")));
+ }; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker.js
new file mode 100644
index 0000000000..65210fe3e7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/serviceworker.js
@@ -0,0 +1 @@
+import './module.json' with { type: "json" }; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/string.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/string.json
new file mode 100644
index 0000000000..ace2d72d9d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/string.json
@@ -0,0 +1 @@
+"string"
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/true.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/true.json
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/true.json
@@ -0,0 +1 @@
+true
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/utf-8.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/utf-8.json
new file mode 100644
index 0000000000..088d982358
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/utf-8.json
@@ -0,0 +1,4 @@
+{
+ "data": "śćążź",
+ "comment": "The data above are five Polish letters, similar to scazz. It can be read correctly only with utf-8 encoding."
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html
new file mode 100644
index 0000000000..3232b84d27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>JSON modules: Content-Type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function check(t, v) {
+ t.step(() => {
+ assert_equals(typeof v, "object");
+ assert_array_equals(Object.keys(v), ["test"]);
+ assert_equals(v.test, true);
+ t.done();
+ });
+}
+const t1 = async_test("text/json");
+const t2 = async_test("application/json");
+const t3 = async_test("text/html+json");
+const t4 = async_test("image/svg+json");
+const t5 = async_test("text/json;boundary=something");
+const t6 = async_test("text/json;foo=bar");
+</script>
+<script type="module" onerror="t1.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module/module.json&ct=text/json" with { type: "json"};
+check(t1, v);
+</script>
+<script type="module" onerror="t2.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module/module.json&ct=application/json" with { type: "json"};
+check(t2, v);
+</script>
+<script type="module" onerror="t3.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module/module.json&ct=text/html%2Bjson" with { type: "json"};
+check(t3, v);
+</script>
+<script type="module" onerror="t4.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module/module.json&ct=image/svg%2Bjson" with { type: "json"};
+check(t4, v);
+</script>
+<script type="module" onerror="t5.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module/module.json&ct=text/json;boundary=something" with { type: "json"};
+check(t5, v);
+</script>
+<script type="module" onerror="t6.step(() => assert_unreached(event))">
+import v from "../serve-with-content-type.py?fn=json-module/module.json&ct=text/json;foo=bar" with { type: "json"};
+check(t6, v);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/windows-1250.json b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/windows-1250.json
new file mode 100644
index 0000000000..490e752ce9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/windows-1250.json
@@ -0,0 +1,4 @@
+{
+ "data": "�湿�",
+ "comment": "The data above are five Polish letters, similar to scazz. It can be read correctly only with windows1250 encoding."
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-1.html
new file mode 100644
index 0000000000..45571550e1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-1.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for classic scripts</title>
+<!-- For module scripts see module/load-error-events*.html -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+"use strict";
+var test1_load = event_test('src, 200, parser-inserted, defer, no async', true, false);
+var test2_load = event_test('src, 200, parser-inserted, no defer, no async', true, false);
+var test4_load = event_test('src, 200, parser-inserted, no defer, async', true, false);
+
+var test3_dynamic_load = event_test('src, 200, not parser-inserted, no defer, no async, no non-blocking', true, false);
+var test4_dynamic_load = event_test('src, 200, not parser-inserted, no defer, async', true, false);
+
+var test1_error = event_test('src, 404, parser-inserted, defer, no async', false, true);
+var test2_error = event_test('src, 404, parser-inserted, no defer, no async', false, true);
+var test4_error = event_test('src, 404, parser-inserted, no defer, async', false, true);
+
+var test3_dynamic_error = event_test('src, 404, not parser-inserted, no defer, no async, no non-blocking', false, true);
+var test4_dynamic_error = event_test('src, 404, not parser-inserted, no defer, async', false, true);
+
+var test6_load = event_test('no src, parser-inserted, no style sheets blocking scripts', false, false);
+
+var script3_dynamic_load = document.createElement('script');
+script3_dynamic_load.onload = () => onLoad(test3_dynamic_load);
+script3_dynamic_load.onerror = () => onError(test3_dynamic_load);
+script3_dynamic_load.async = false;
+script3_dynamic_load.src = "resources/load-error-events.py?test=test3_dynamic_load";
+document.head.appendChild(script3_dynamic_load);
+
+var script3_dynamic_error = document.createElement('script');
+script3_dynamic_error.onload = () => onLoad(test3_dynamic_error);
+script3_dynamic_error.onerror = () => onError(test3_dynamic_error);
+script3_dynamic_error.async = false;
+script3_dynamic_error.src = "resources/load-error-events.py?test=test3_dynamic_error";
+document.head.appendChild(script3_dynamic_error);
+
+var script4_dynamic_load = document.createElement('script');
+script4_dynamic_load.onload = () => onLoad(test4_dynamic_load);
+script4_dynamic_load.onerror = () => onError(test4_dynamic_load);
+script4_dynamic_load.async = true;
+script4_dynamic_load.src = "resources/load-error-events.py?test=test4_dynamic_load";
+document.head.appendChild(script4_dynamic_load);
+
+var script4_dynamic_error = document.createElement('script');
+script4_dynamic_error.onload = () => onLoad(test4_dynamic_error);
+script4_dynamic_error.onerror = () => onError(test4_dynamic_error);
+script4_dynamic_error.async = true;
+script4_dynamic_error.src = "resources/load-error-events.py?test=test4_dynamic_error";
+document.head.appendChild(script4_dynamic_error);
+</script>
+
+<script src="resources/load-error-events.py?test=test1_load" onload="onLoad(test1_load);" onerror="onError(test1_load);" defer></script>
+<script src="resources/load-error-events.py?test=test2_load" onload="onLoad(test2_load);" onerror="onError(test2_load);"></script>
+<script src="resources/load-error-events.py?test=test4_load" onload="onLoad(test4_load);" onerror="onError(test4_load);" async></script>
+<script src="resources/load-error-events.py?test=test1_error" onload="onLoad(test1_error);" onerror="onError(test1_error);" defer></script>
+<script src="resources/load-error-events.py?test=test2_error" onload="onLoad(test2_error);" onerror="onError(test2_error);"></script>
+<script src="resources/load-error-events.py?test=test4_error" onload="onLoad(test4_error);" onerror="onError(test4_error);" async></script>
+
+<script onload="onLoad(test6_load);" onerror="onError(test6_load);">
+"use strict";
+onExecute(test6_load);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-2.html
new file mode 100644
index 0000000000..0748b45909
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for classic scripts with a style sheet that is blocking scripts</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script>
+"use strict";
+var test5_load = event_test('no src, parser-inserted, has style sheets blocking scripts, script nesting level == 1', false, false);
+</script>
+
+<link rel="stylesheet" href="/common/slow.py"></link>
+<!-- This is testing the case where an inline classic script is inserted
+by parser while there is an loading stylesheet. Therefore, it is critical to
+place a <link rel="stylesheet"> just above the <script> to be tested. -->
+<script onload="onLoad(test5_load);" onerror="onError(test5_load);">
+"use strict";
+onExecute(test5_load);
+</script>
+</head>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-3.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-3.html
new file mode 100644
index 0000000000..83a752ce2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-error-events-3.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for classic scripts with a style sheet that is blocking scripts and script nesting level &gt; 1</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+"use strict";
+var test6_load = event_test('no src, parser-inserted, has style sheets blocking scripts, script nesting level == 2',
+ false, false);
+
+document.write(
+ `<link rel="stylesheet" href="/common/slow.py"></link>
+ <script onload="onLoad(test6_load);"
+ onerror="onError(test6_load);">
+ "use strict";
+ onExecute(test6_load);
+ </scr` + `ipt>`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/log.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/log.py
new file mode 100644
index 0000000000..2a6cc33029
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/log.py
@@ -0,0 +1,13 @@
+import time
+
+def main(request, response):
+ response.headers.append(b"Content-Type", b"text/javascript")
+ try:
+ script_id = int(request.GET.first(b"id"))
+ delay = int(request.GET.first(b"sec"))
+ except:
+ response.set_error(400, u"Invalid parameter")
+
+ time.sleep(int(delay))
+
+ return u"log('%s')" % script_id
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror-module.html
new file mode 100644
index 0000000000..728ce32c38
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror-module.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>Microtask checkpoint after window.onerror events (module)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="resources/checkpoint-after-error-event.js" type="module"></script>
+ <script type="module">self.postMessage("foo");</script>
+</head>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror.html
new file mode 100644
index 0000000000..72a197ca6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-window-onerror.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>Microtask checkpoint after window.onerror events (classic)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="resources/checkpoint-after-error-event.js"></script>
+ <script>self.postMessage("foo");</script>
+</head>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror-module.html
new file mode 100644
index 0000000000..ff2b5d4943
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>Microtask checkpoint after window.onerror events (module)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ const worker = new Worker(
+ "resources/checkpoint-after-error-event-worker-module.js",
+ {type: "module"});
+ fetch_tests_from_worker(worker);
+ worker.postMessage("foo");
+ </script>
+</head>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror.html
new file mode 100644
index 0000000000..1932c7183b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-after-workerglobalscope-onerror.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>Microtask checkpoint after window.onerror events (module)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ const worker = new Worker(
+ "resources/checkpoint-after-error-event-worker.js");
+ fetch_tests_from_worker(worker);
+ worker.postMessage("foo");
+ </script>
+</head>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-importScripts.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-importScripts.any.js
new file mode 100644
index 0000000000..8791a099b6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/checkpoint-importScripts.any.js
@@ -0,0 +1,40 @@
+// META: global=dedicatedworker,sharedworker
+
+// The `then` handlers for `Promise.resolve()` are evaluated in the first
+// microtasks checkpoint after `Promise.resolve()`.
+
+// ----------------------------------------------------------------
+// Check when microtasks checkpoint is performed around importScripts().
+
+// The expectation is: the `then` handlers are evaluated after the script
+// calling importScripts() is finished, not immediately after importScripts().
+// Although #clean-up-after-running-script is executed as a part of
+// #run-a-classic-script for importScripts()ed scripts, but at that time
+// microtasks checkpoint is NOT performed because JavaScript execution context
+// stack is not empty.
+
+self.log = [];
+
+// Microtasks should be executed before
+// #run-a-classic-script/#run-a-module-script is completed, and thus before
+// script evaluation scheduled by setTimeout().
+async_test(t => {
+ self.addEventListener('error',
+ t.unreached_func('error event should not be fired'));
+
+ t.step_timeout(() => {
+ assert_array_equals(log, [
+ 'importScripts()ed script',
+ 'catch',
+ 'promise'
+ ]);
+ t.done();
+ },
+ 0);
+}, "Promise resolved during importScripts()");
+
+try {
+ importScripts('resources/resolve-then-throw.js');
+} catch (e) {
+ self.log.push('catch');
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-importScripts.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-importScripts.any.js
new file mode 100644
index 0000000000..bacfc9fd04
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-importScripts.any.js
@@ -0,0 +1,11 @@
+// META: global=dedicatedworker,sharedworker
+// META: script=./resources/evaluation-order-setup.js
+
+// Spec: https://html.spec.whatwg.org/C/#run-a-classic-script
+// called from https://html.spec.whatwg.org/C/#import-scripts-into-worker-global-scope
+setupTest("importScripts() queueing a microtask then throwing an exception", [
+ "body",
+ "microtask",
+]);
+
+importScripts('./resources/evaluation-order-1-nothrow.js');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-static-import.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-static-import.any.js
new file mode 100644
index 0000000000..006eab7a7e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-nothrow-static-import.any.js
@@ -0,0 +1,5 @@
+// META: global=dedicatedworker-module,sharedworker-module
+// META: script=./resources/evaluation-order-setup.js
+
+import './resources/evaluation-order-1-nothrow-setup.js';
+import './resources/evaluation-order-1-nothrow.js';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-importScripts.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-importScripts.any.js
new file mode 100644
index 0000000000..0b42ea1e50
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-importScripts.any.js
@@ -0,0 +1,22 @@
+// META: global=dedicatedworker,sharedworker
+// META: script=./resources/evaluation-order-setup.js
+
+// Spec: https://html.spec.whatwg.org/C/#run-a-classic-script
+// called from https://html.spec.whatwg.org/C/#import-scripts-into-worker-global-scope
+setupTest("importScripts() queueing a microtask then throwing an exception", [
+ // Step 6 of #run-a-classic-script.
+ "body",
+
+ // Step 7.1.1 ("Clean up after running script") is no-op because JavaScript
+ // execution context stack is still non-empty immediately after
+ // importScripts() as the outer script is still executing.
+
+ // Step 7.1.2 (Rethrowing an exception) causes worker onerror.
+ "global-error",
+
+ // Microtask checkpoint is performed later, perhaps
+ // "Clean up after running script" after the outer script is finished.
+ "microtask",
+]);
+
+importScripts('./resources/evaluation-order-1-throw.js');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-static-import.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-static-import.any.js
new file mode 100644
index 0000000000..f6cc427c71
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1-throw-static-import.any.js
@@ -0,0 +1,5 @@
+// META: global=dedicatedworker-module,sharedworker-module
+// META: script=./resources/evaluation-order-setup.js
+
+import './resources/evaluation-order-1-throw-setup.js';
+import './resources/evaluation-order-1-throw.js';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1.html
new file mode 100644
index 0000000000..4800ef81bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/evaluation-order-setup.js"></script>
+
+<script>
+// Spec: https://html.spec.whatwg.org/C/#run-a-classic-script
+setupTest("Classic script queueing a microtask then throwing an exception", [
+ "body", // Step 6.
+ "global-error", // "Report the exception" at Step 7.3.
+ "microtask", // "Clean up after running script" at Step 7.2.
+ ]);
+</script>
+<script src="resources/evaluation-order-1-throw.js"
+ onerror="unreachable()" onload="testDone()"></script>
+
+<script>
+// Spec: https://html.spec.whatwg.org/C/#run-a-classic-script
+setupTest("Classic script queueing a microtask", [
+ "body", // Step 6.
+ "microtask", // "Clean up after running script" at Step 7.2.
+ ]);
+</script>
+<script src="resources/evaluation-order-1-nothrow.js"
+ onerror="unreachable()" onload="testDone()"></script>
+
+
+<script type="module" src="resources/evaluation-order-1-throw-setup.js"></script>
+<script type="module" src="resources/evaluation-order-1-throw.js"
+ onerror="unreachable()" onload="testDone()"></script>
+
+<script type="module" src="resources/evaluation-order-1-nothrow-setup.js"></script>
+<script type="module" src="resources/evaluation-order-1-nothrow.js"
+ onerror="unreachable()" onload="testDone()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.any.js
new file mode 100644
index 0000000000..bbc6474823
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.any.js
@@ -0,0 +1,5 @@
+// META: global=dedicatedworker-module,sharedworker-module
+// META: script=./resources/evaluation-order-setup.js
+
+import './resources/evaluation-order-2-setup.js';
+import './resources/evaluation-order-2.1.mjs';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.html
new file mode 100644
index 0000000000..e55c2ecbed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/evaluation-order-setup.js"></script>
+
+<script type="module" src="resources/evaluation-order-2-setup.js"></script>
+<script type="module" src="resources/evaluation-order-2.1.mjs"
+ onerror="unreachable()" onload="testDone()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.any.js
new file mode 100644
index 0000000000..19e94714e5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.any.js
@@ -0,0 +1,5 @@
+// META: global=dedicatedworker-module,sharedworker-module
+// META: script=./resources/evaluation-order-setup.js
+
+import './resources/evaluation-order-3-setup.js';
+import './resources/evaluation-order-3.1.mjs';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.html
new file mode 100644
index 0000000000..ef351acd28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-3.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/evaluation-order-setup.js"></script>
+
+<script type="module" src="resources/evaluation-order-3-setup.js"></script>
+<script type="module" src="resources/evaluation-order-3.1.mjs"
+ onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-4.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-4.html
new file mode 100644
index 0000000000..f27678439d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-4.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/evaluation-order-setup.js"></script>
+
+<script>
+// Spec: https://html.spec.whatwg.org/C/#run-a-module-script
+setupTest("Module script queueing a microtask then top-level await", [
+ "step-4.1-1", "step-4.1-2",
+ "microtask-4.1",
+ "script-load",
+ "step-4.2-1", "step-4.2-2",
+ "microtask-4.2",
+]);
+window.onerror = testDone;
+</script>
+<script type="module" src="resources/evaluation-order-4.1.mjs"
+ onerror="unreachable()" onload="log.push('script-load')"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker-module.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker-module.js
new file mode 100644
index 0000000000..b9ae7142d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker-module.js
@@ -0,0 +1,2 @@
+import "/resources/testharness.js";
+import "./checkpoint-after-error-event.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker.js
new file mode 100644
index 0000000000..4694db1e44
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event-worker.js
@@ -0,0 +1,2 @@
+importScripts("/resources/testharness.js");
+importScripts("checkpoint-after-error-event.js");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event.js
new file mode 100644
index 0000000000..1a2b9404a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/checkpoint-after-error-event.js
@@ -0,0 +1,89 @@
+// The `then` handlers for `Promise.resolve()` are evaluated in the first
+// microtasks checkpoint after `Promise.resolve()`.
+
+self.setup({allow_uncaught_exception: true});
+
+// ----------------------------------------------------------------
+// Check when microtasks checkpoint is performed
+// - Around `self`'s error event fired via
+// https://html.spec.whatwg.org/C/#report-the-error during
+// - https://html.spec.whatwg.org/C/#run-a-classic-script or
+// - https://html.spec.whatwg.org/C/#run-a-module-script.
+
+// The expectation is: the `then` handlers are evaluated after all error event
+// handlers are evaluated, not after each error event handler.
+
+// Just after each event handler is invoked,
+// https://webidl.spec.whatwg.org/#call-a-user-objects-operation
+// calls #clean-up-after-running-script, but this doesn't execute new
+// microtasks immediately, because:
+// - Before https://github.com/whatwg/html/pull/4352:
+// #report-the-error is called before #clean-up-after-running-script by
+// #run-a-classic-script/#run-a-module-script, so microtask checkpoint
+// is not performed because JavaScript execution context stack is not empty.
+// - After https://github.com/whatwg/html/pull/4352:
+// #report-the-error is called during #perform-a-microtask-checkpoint (because
+// it is called on rejection of promises), so #perform-a-microtask-checkpoint
+// is executed but early exited.
+self.log = [];
+
+self.addEventListener('error', () => {
+ log.push('handler 1');
+ Promise.resolve().then(() => log.push('handler 1 promise'));
+});
+self.addEventListener('error', () => {
+ log.push('handler 2');
+ Promise.resolve().then(() => log.push('handler 2 promise'));
+});
+
+// Microtasks should be executed before
+// #run-a-classic-script/#run-a-module-script is completed, and thus before
+// script evaluation scheduled by setTimeout().
+async_test(t => {
+ t.step_timeout(() => {
+ assert_array_equals(log, [
+ 'handler 1',
+ 'handler 2',
+ 'handler 1 promise',
+ 'handler 2 promise'
+ ]);
+ t.done();
+ },
+ 0);
+}, "Promise resolved during #report-the-error");
+
+// ----------------------------------------------------------------
+// Check when microtasks checkpoint is performed
+// around event events other than the `self` error event cases above.
+// In this case, microtasks are executed just after each event handler is
+// invoked via #clean-up-after-running-script called from
+// https://webidl.spec.whatwg.org/#call-a-user-objects-operation,
+// because the event handlers are executed outside the
+// #prepare-to-run-script/#clean-up-after-running-script scopes in
+// #run-a-classic-script/#run-a-module-script.
+self.log2 = [];
+self.t2 = async_test(
+ "Promise resolved during event handlers other than error");
+
+self.addEventListener('message', () => {
+ log2.push('handler 1');
+ Promise.resolve().then(() => log2.push('handler 1 promise'));
+});
+self.addEventListener('message', () => {
+ log2.push('handler 2');
+ Promise.resolve().then(t2.step_func_done(() => {
+ log2.push('handler 2 promise');
+ assert_array_equals(log2, [
+ 'handler 1',
+ 'handler 1 promise',
+ 'handler 2',
+ 'handler 2 promise'
+ ]);
+ }));
+});
+
+// ----------------------------------------------------------------
+
+done();
+
+throw new Error('script 1');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow-setup.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow-setup.js
new file mode 100644
index 0000000000..1b42e99593
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow-setup.js
@@ -0,0 +1,5 @@
+// Spec: https://html.spec.whatwg.org/C/#run-a-module-script
+setupTest("Module script queueing a microtask", [
+ "body", // Step 6.
+ "microtask", // "Clean up after running script" at Step 8.
+]);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow.js
new file mode 100644
index 0000000000..e19d9b1b13
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-nothrow.js
@@ -0,0 +1,2 @@
+queueMicrotask(() => globalThis.log.push("microtask"));
+globalThis.log.push("body");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw-setup.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw-setup.js
new file mode 100644
index 0000000000..651a494e53
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw-setup.js
@@ -0,0 +1,10 @@
+// Spec: https://html.spec.whatwg.org/C/#run-a-module-script
+setupTest("Module script queueing a microtask then throwing an exception", [
+ "body", // Step 6.
+ "microtask", // "Clean up after running script" at Step 8.
+ "global-error", // "Clean up after running script" at Step 8, because
+ // `evaluationPromise` is synchronously rejected and the rejection is
+ // processed in the microtask checkpoint here (See also Step 7).
+ // As `evaluationPromise` is rejected after the microtask queued during
+ // evaluation, "global-error" occurs after "microtask".
+]);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw.js
new file mode 100644
index 0000000000..2451df1c15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-1-throw.js
@@ -0,0 +1,4 @@
+queueMicrotask(() => globalThis.log.push("microtask"));
+globalThis.log.push("body");
+
+throw new Error("error");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2-setup.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2-setup.js
new file mode 100644
index 0000000000..46f7354538
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2-setup.js
@@ -0,0 +1,10 @@
+// Spec: https://html.spec.whatwg.org/C/#run-a-module-script
+setupTest("Module script queueing a microtask then throwing an exception", [
+ "step-2.2-1", "step-2.2-2", // Step 6.
+ "microtask-2.2", // "Clean up after running script" at Step 8.
+ "global-error", // "Clean up after running script" at Step 8,
+ // because `evaluationPromise` is synchronously rejected and the rejection
+ // is processed in the microtask checkpoint here (See also Step 7).
+ // As `evaluationPromise` is rejected after the microtask queued during
+ // evaluation, "global-error" occurs after "microtask".
+]);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.1.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.1.mjs
new file mode 100644
index 0000000000..d6c2afa2f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.1.mjs
@@ -0,0 +1,8 @@
+globalThis.log.push("step-2.1-1");
+queueMicrotask(() => globalThis.log.push("microtask-2.1"));
+globalThis.log.push("step-2.1-2");
+
+// import is evaluated first.
+import "./evaluation-order-2.2.mjs";
+
+globalThis.log.push("step-2.1-3");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.2.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.2.mjs
new file mode 100644
index 0000000000..5c67c4f9a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-2.2.mjs
@@ -0,0 +1,7 @@
+globalThis.log.push("step-2.2-1");
+queueMicrotask(() => globalThis.log.push("microtask-2.2"));
+globalThis.log.push("step-2.2-2");
+
+globalThis.test_load.step_timeout(() => globalThis.testDone(), 0);
+
+throw new Error("error");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3-setup.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3-setup.js
new file mode 100644
index 0000000000..edc046910e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3-setup.js
@@ -0,0 +1,7 @@
+setupTest("Module script queueing a microtask then throwing an exception", [
+ "step-3.1-1", "step-3.1-2", "step-3.1-3",
+ "microtask-3.1",
+ "step-3.2-1", "step-3.2-2",
+ "microtask-3.2",
+ "import-catch", "error",
+]);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.1.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.1.mjs
new file mode 100644
index 0000000000..ef320632af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.1.mjs
@@ -0,0 +1,11 @@
+globalThis.log.push("step-3.1-1");
+queueMicrotask(() => globalThis.log.push("microtask-3.1"));
+globalThis.log.push("step-3.1-2");
+
+import("./evaluation-order-3.2.mjs").catch(
+ exception => {
+ globalThis.log.push("import-catch", exception.message);
+ globalThis.testDone();
+ });
+
+globalThis.log.push("step-3.1-3");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.2.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.2.mjs
new file mode 100644
index 0000000000..8ccb581206
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-3.2.mjs
@@ -0,0 +1,5 @@
+globalThis.log.push("step-3.2-1");
+queueMicrotask(() => globalThis.log.push("microtask-3.2"));
+globalThis.log.push("step-3.2-2");
+
+throw new Error("error");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.1.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.1.mjs
new file mode 100644
index 0000000000..f3347c1d28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.1.mjs
@@ -0,0 +1,8 @@
+log.push("step-4.1-1");
+queueMicrotask(() => log.push("microtask-4.1"));
+log.push("step-4.1-2");
+
+await import("./evaluation-order-4.2.mjs");
+
+// Not happening as we throw in the above module.
+log.push("step-4.1-3");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.2.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.2.mjs
new file mode 100644
index 0000000000..96a5cca3a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-4.2.mjs
@@ -0,0 +1,5 @@
+log.push("step-4.2-1");
+queueMicrotask(() => log.push("microtask-4.2"));
+log.push("step-4.2-2");
+
+throw new Error("error");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-setup.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-setup.js
new file mode 100644
index 0000000000..d2e28935c4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/evaluation-order-setup.js
@@ -0,0 +1,30 @@
+globalThis.setup({allow_uncaught_exception: true});
+
+// Must be called after previous tests are completed.
+globalThis.setupTest = (description, expectedLog) => {
+ globalThis.log = [];
+ globalThis.onerror = message => {
+ globalThis.log.push("global-error");
+ return true;
+ };
+ globalThis.onunhandledrejection =
+ () => globalThis.log.push('unhandled-promise-rejection');
+
+ globalThis.unreachable = () => globalThis.log.push("unreachable");
+
+ globalThis.test_load = async_test(description);
+ globalThis.testDone = globalThis.test_load.step_func_done(() => {
+ assert_array_equals(globalThis.log, expectedLog);
+ });
+
+ if (!('Window' in globalThis && globalThis instanceof Window)) {
+ // In workers, there are no <script> load event, so scheduling `testDone()`
+ // here, assuming the target script is loaded and evaluated soon.
+ globalThis.test_load.step_timeout(() => globalThis.testDone(), 1000);
+
+ // In workers, call `done()` here because the auto-generated `done()` calls
+ // by `any.js` etc. are at the end of main script and thus are not
+ // evaluated when the target script throws an exception.
+ done();
+ }
+};
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/resolve-then-throw.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/resolve-then-throw.js
new file mode 100644
index 0000000000..a841eb780a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/microtasks/resources/resolve-then-throw.js
@@ -0,0 +1,3 @@
+self.log.push('importScripts()ed script');
+Promise.resolve().then(() => self.log.push('promise'));
+throw new Error('foo');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/bad-module-specifier.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/bad-module-specifier.js
new file mode 100644
index 0000000000..a53a3bebe7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/bad-module-specifier.js
@@ -0,0 +1,3 @@
+import "string-without-dot-slash-prefix";
+import "./this.js";
+log.push("bad-module-specifier");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-01.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-01.html
new file mode 100644
index 0000000000..7cd4916309
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-01.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Root module scripts should always use UTF-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module" src="../serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript&dummy=1"></script>
+<script type="module">
+test(function() {
+ assert_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'UTF-8 module script');
+</script>
+
+<script type="module" src="../serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript&dummy=2" charset="windows-1250"></script>
+<script type="module">
+test(function() {
+ assert_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'UTF-8 module script with wrong charset in attribute');
+</script>
+
+<script type="module" src="../serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript%3Bcharset=windows-1250&dummy=3"></script>
+<script type="module">
+test(function() {
+ assert_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'UTF-8 module script with wrong charset in Content-Type');
+</script>
+
+<script type="module" src="../serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript&dummy=1"></script>
+<script type="module">
+test(function() {
+ assert_not_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'Non-UTF-8 module script');
+</script>
+
+<script type="module" src="../serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript&dummy=2" charset="windows-1250"></script>
+<script type="module">
+test(function() {
+ assert_not_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'Non-UTF-8 module script with charset in attribute');
+</script>
+
+<script type="module" src="../serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript%3Bcharset=windows-1250"></script>
+<script type="module">
+test(function() {
+ assert_not_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'Non-UTF-8 module script with charset in Content-Type');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-02.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-02.html
new file mode 100644
index 0000000000..c7c4517ad3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-02.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Module scripts should ignore BOMs and always use UTF-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({allow_uncaught_exception: true});
+</script>
+<script type="module" src="../serve-with-content-type.py?fn=resources/bom-utf-8.js&ct=text/javascript"></script>
+<script type="module" src="../serve-with-content-type.py?fn=resources/bom-utf-16be.js&ct=text/javascript"></script>
+<script type="module" src="../serve-with-content-type.py?fn=resources/bom-utf-16le.js&ct=text/javascript"></script>
+<script type="module">
+test(function() {
+ assert_equals(window.executed_utf8_bom, '\u4e09\u6751\u304b\u306a\u5b50',
+ 'Should be decoded as UTF-8');
+}, 'UTF-8 module script with UTF-8 BOM');
+
+test(function() {
+ assert_equals(window.executed_utf16be_bom, undefined,
+ 'Should result in compile error because of UTF-16BE BOM');
+}, 'UTF-16 module script with UTF-16BE BOM');
+
+test(function() {
+ assert_equals(window.executed_utf16le_bom, undefined,
+ 'Should result in compile error because of UTF-16LE BOM');
+}, 'UTF-16 module script with UTF-16LE BOM');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-03.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-03.html
new file mode 100644
index 0000000000..666cb2e68b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/charset-03.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Imported module scripts should always use UTF-8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script type="module" src="resources/import-utf8.js"></script>
+<script type="module">
+test(function() {
+ assert_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'UTF-8 imported module script');
+</script>
+
+<script type="module" src="resources/import-utf8-with-charset-header.js"></script>
+<script type="module">
+test(function() {
+ assert_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'UTF-8 imported module script with wrong charset in Content-Type');
+</script>
+
+<script type="module" src="resources/import-non-utf8.js"></script>
+<script type="module">
+test(function() {
+ assert_not_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'Non-UTF-8 imported module script');
+</script>
+
+<script type="module" src="resources/import-non-utf8-with-charset-header.js"></script>
+<script type="module">
+test(function() {
+ assert_not_equals(window.getSomeString(), "śćążź",
+ 'Should be decoded as UTF-8');
+}, 'Non-UTF-8 imported module script with charset in Content-Type');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1.html
new file mode 100644
index 0000000000..50933da2c1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Choice of parse errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "Parse errors in different files should be reported " +
+ "depending on different roots");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 4);
+
+ // Two different parse errors from different module scripts
+ // should be reported for each <script> element.
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_equals(log[1], 1);
+
+ assert_equals(log[2].constructor, SyntaxError);
+ assert_equals(log[3], 2);
+
+ assert_not_equals(log[0], log[2],
+ 'two different parse errors should be reported');
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./choice-of-error-1a.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./choice-of-error-1b.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1a.js
new file mode 100644
index 0000000000..f479e5e1fa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1a.js
@@ -0,0 +1,2 @@
+import './choice-of-error-1b.js';
+import './syntaxerror.js?1c';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1b.js
new file mode 100644
index 0000000000..257f4a4678
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-1b.js
@@ -0,0 +1,2 @@
+import './choice-of-error-1a.js';
+import './syntaxerror.js?1d';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2.html
new file mode 100644
index 0000000000..51adb09d11
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Choice of instantiation errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "Instantiation errors in different files should be reported " +
+ "depending on different roots");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 4);
+
+ // Two different instantiation errors from different module scripts
+ // should be reported for each <script> element.
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_equals(log[1], 1);
+
+ assert_equals(log[2].constructor, SyntaxError);
+ assert_equals(log[3], 2);
+
+ assert_not_equals(log[0], log[2],
+ 'two different instantiation errors should be reported');
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./choice-of-error-2a.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./choice-of-error-2b.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2a.js
new file mode 100644
index 0000000000..2dc7aac11a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2a.js
@@ -0,0 +1,2 @@
+import './choice-of-error-2b.js';
+import './instantiation-error-1.js?2c';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2b.js
new file mode 100644
index 0000000000..2adb9eea59
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-2b.js
@@ -0,0 +1,2 @@
+import './choice-of-error-2a.js';
+import './instantiation-error-1.js?2d';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3.html
new file mode 100644
index 0000000000..bc52119bfe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>Choice of evaluation errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "Evaluation errors are cached in intermediate module scripts");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 5);
+
+ // Evaluation errors, unlike parse/instantiation errors, are remembered
+ // and cached in module scripts between the root and the script that
+ // caused an evaluation error, and thus the same evaluation error
+ // is reported for both <script> elements.
+ assert_equals(log[0], "throw2");
+ assert_true(log[1].bar);
+ assert_equals(log[2], 1);
+
+ assert_true(log[3].bar);
+ assert_equals(log[4], 2);
+
+ assert_equals(log[1], log[3], 'evaluation errors must be the same');
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./choice-of-error-3a.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./choice-of-error-3b.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3a.js
new file mode 100644
index 0000000000..71154674a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3a.js
@@ -0,0 +1,2 @@
+import './choice-of-error-3b.js';
+import './throw.js?3c';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3b.js
new file mode 100644
index 0000000000..2131a35eff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/choice-of-error-3b.js
@@ -0,0 +1,2 @@
+import './choice-of-error-3a.js';
+import './throw2.js?3d';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-1.html
new file mode 100644
index 0000000000..ff580d4899
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Handling of compilation errors, 1</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that syntax errors lead to SyntaxError events on window, " +
+ "and that exceptions are remembered.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 5);
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_true(log.every(exn => exn === log[0]));
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./syntaxerror.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror-nested.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror-nested.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-2.html
new file mode 100644
index 0000000000..131a6e439f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/compilation-error-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Handling of compilation errors, 2</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that syntax errors lead to SyntaxError events on window, " +
+ "and that exceptions are remembered.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 5);
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_true(log.every(exn => exn === log[0]));
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./syntaxerror-nested.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror-nested.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror-nested.js" onerror="unreachable()"></script>
+<script type="module" src="./syntaxerror.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/credentials.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/credentials.sub.html
new file mode 100644
index 0000000000..983961ae44
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/credentials.sub.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+document.cookie = 'same=1';
+
+const setCookiePromise = fetch(
+ 'http://{{domains[www2]}}:{{ports[http][0]}}/cookies/resources/set-cookie.py?name=cross&path=/html/semantics/scripting-1/the-script-element/module/',
+ {
+ mode: 'no-cors',
+ credentials: 'include',
+ });
+
+const windowLoadPromise = new Promise(resolve => {
+ window.addEventListener('load', () => {
+ resolve();
+ });
+});
+
+promise_test(t => {
+ const iframe = document.createElement('iframe');
+
+ return Promise.all([setCookiePromise, windowLoadPromise]).then(() => {
+ const messagePromise = new Promise(resolve => {
+ window.addEventListener('message', event => {
+ resolve();
+ });
+ });
+
+ iframe.src = 'resources/credentials-iframe.sub.html';
+ document.body.appendChild(iframe);
+
+ return messagePromise;
+ }).then(() => {
+ const w = iframe.contentWindow;
+
+ assert_equals(w.sameOriginNone, 'found',
+ 'Modules should be loaded with the credentials when the crossOrigin attribute is not specified and the target is same-origin');
+ assert_equals(w.sameOriginAnonymous, 'found',
+ 'Modules should be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is same-origin');
+ assert_equals(w.sameOriginUseCredentials, 'found',
+ 'Modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is same-origin');
+ assert_equals(w.crossOriginNone, 'not found',
+ 'Modules should not be loaded with the credentials when the crossOrigin attribute is not specified and the target is cross-origin');
+ assert_equals(w.crossOriginAnonymous, 'not found',
+ 'Modules should not be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is cross-origin');
+ assert_equals(w.crossOriginUseCredentials, 'found',
+ 'Modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is cross-origin');
+
+ assert_equals(w.sameOriginNoneDescendant, 'found',
+ 'Descendant modules should be loaded with the credentials when the crossOrigin attribute is not specified and the target is same-origin');
+ assert_equals(w.sameOriginAnonymousDescendant, 'found',
+ 'Descendant modules should be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is same-origin');
+ assert_equals(w.sameOriginUseCredentialsDescendant, 'found',
+ 'Descendant modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is same-origin');
+ assert_equals(w.crossOriginNoneDescendant, 'not found',
+ 'Descendant modules should not be loaded with the credentials when the crossOrigin attribute is not specified and the target is cross-origin');
+ assert_equals(w.crossOriginAnonymousDescendant, 'not found',
+ 'Descendant modules should not be loaded with the credentials when the crossOrigin attribute is specified with "anonymous" as its value and the target is cross-origin');
+ assert_equals(w.crossOriginUseCredentialsDescendant, 'found',
+ 'Descendant modules should be loaded with the credentials when the crossOrigin attribute is specified with "use-credentials" as its value and the target is cross-origin');
+});
+}, 'Modules should be loaded with or without the credentials based on the same-origin-ness and the crossOrigin attribute');
+</script>
+<body>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-common.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-common.js
new file mode 100644
index 0000000000..59bf0fd42f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-common.js
@@ -0,0 +1,8 @@
+document._log = [];
+window.addEventListener("error", function (ev) {
+ var errorSerialized = ev.lineno + "-" + ev.colno;
+ document._log.push(errorSerialized);
+});
+window.addEventListener("load", function () {
+ document._log = document._log.join(",");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-different.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-different.sub.html
new file mode 100644
index 0000000000..8eb3292e89
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-different.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-import-NoCORS</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-import-NoCORS</h1>
+ <script type="module" onerror="document._log.push('error');">
+
+ import { foo } from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js";
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-missingheader.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-missingheader.sub.html
new file mode 100644
index 0000000000..cccb30f718
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-missingheader.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-root-BlockedMissingHeader</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-root-BlockedMissingHeader</h1>
+ <script type="module" onerror="document._log.push('error');" crossorigin>
+
+ import { foo } from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js";
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-same.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-same.sub.html
new file mode 100644
index 0000000000..84f4de1d7e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-same.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-root-WithCORS</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-root-WithCORS</h1>
+ <script type="module" crossorigin>
+
+ import { foo } from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js?pipe=header(Access-Control-Allow-Origin,*)";
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-wrongheader.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-wrongheader.sub.html
new file mode 100644
index 0000000000..5743a9e304
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-import-wrongheader.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-root-BlockedWrongHeader</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-root-BlockedWrongHeader</h1>
+ <script type="module" onerror="document._log.push('error');" crossorigin>
+
+ import { foo } from "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js?pipe=header(Access-Control-Allow-Origin,http://{{domains[www2]}}:{{ports[http][0]}})";
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-different.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-different.sub.html
new file mode 100644
index 0000000000..54d4354c53
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-different.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-root-NoCORS</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-root-NoCORS</h1>
+ <script type="module" src="http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js" onerror="document._log.push('error');"></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-missingheader.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-missingheader.sub.html
new file mode 100644
index 0000000000..03943002ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-missingheader.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-root-BlockedMissingHeader</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-root-BlockedMissingHeader</h1>
+ <script type="module" src="http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js" onerror="document._log.push('error');" crossorigin></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-same.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-same.sub.html
new file mode 100644
index 0000000000..3ec839e9a9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-same.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-root-WithCORS</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-root-WithCORS</h1>
+ <script type="module" src="http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js?pipe=header(Access-Control-Allow-Origin,*)" crossorigin></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-wrongheader.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-wrongheader.sub.html
new file mode 100644
index 0000000000..c45736a896
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-root-wrongheader.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin-root-BlockedWrongHeader</title>
+ <script src="crossorigin-common.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin-root-BlockedWrongHeader</h1>
+ <script type="module" src="http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js?pipe=header(Access-Control-Allow-Origin,http://{{domains[www2]}}:{{ports[http][0]}})" onerror="document._log.push('error');" crossorigin></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js
new file mode 100644
index 0000000000..29d04ec619
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin-scripterror.js
@@ -0,0 +1,8 @@
+export var foo = {};
+
+// Push an event to the log indicating that the script was executed.
+document._log.push("running");
+
+// Deliberately trigger an error to test what details of the error
+// the (possibly) cross-origin parent can listen to.
+nonExistentMethod();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin.html
new file mode 100644
index 0000000000..5c8d6667b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/crossorigin.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+<head>
+ <title>html-script-module-crossOrigin</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>html-script-module-crossOrigin</h1>
+ <iframe id="root-WithCORS" src="crossorigin-root-same.sub.html"></iframe>
+ <iframe id="root-NoCORS" src="crossorigin-root-different.sub.html"></iframe>
+ <iframe id="root-BlockedMissingHeader" src="crossorigin-root-missingheader.sub.html"></iframe>
+ <iframe id="root-BlockedWrongHeader" src="crossorigin-root-wrongheader.sub.html"></iframe>
+ <iframe id="import-WithCORS" src="crossorigin-import-same.sub.html"></iframe>
+ <iframe id="import-NoCORS" src="crossorigin-import-different.sub.html"></iframe>
+ <iframe id="import-BlockedMissingHeader" src="crossorigin-import-missingheader.sub.html"></iframe>
+ <iframe id="import-BlockedWrongHeader" src="crossorigin-import-wrongheader.sub.html"></iframe>
+ <script>
+
+ var tests = [
+ { "obj": async_test("Root module, Error in CORS-same-origin script"), "id": "root-WithCORS", "expected": "running,8-1" },
+ { "obj": async_test("Root module, Blocked script download, missing CORS ACAO header"), "id": "root-NoCORS", "expected": "error" },
+ { "obj": async_test("Root module, Blocked script download, crossorigin attribute with missing CORS ACAO header"), "id": "root-BlockedMissingHeader", "expected": "error" },
+ { "obj": async_test("Root module, Blocked script download, mismatched CORS ACAO header"), "id": "root-BlockedWrongHeader", "expected": "error" },
+ { "obj": async_test("Imported module, Error in CORS-same-origin script"), "id": "import-WithCORS", "expected": "running,8-1" },
+ { "obj": async_test("Imported module, Blocked script download, missing CORS ACAO header"), "id": "import-NoCORS", "expected": "error" },
+ { "obj": async_test("Imported module, Blocked script download, crossorigin attribute with missing CORS ACAO header"), "id": "import-BlockedMissingHeader", "expected": "error" },
+ { "obj": async_test("Imported module, Blocked script download, mismatched CORS ACAO header"), "id": "import-BlockedWrongHeader", "expected": "error" },
+ ];
+
+ window.addEventListener("load", function () {
+ tests.forEach(function (test) {
+ var target = document.getElementById(test.id);
+ test.obj.step(function () {
+ assert_equals(target.contentDocument._log, test.expected, "Unexpected _log value");
+ });
+ test.obj.done();
+ });
+ });
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentScript-null.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentScript-null.html
new file mode 100644
index 0000000000..146a9db60d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentScript-null.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module" src="set-currentScript-on-window.js"></script>
+<script type="module">
+import { currentScriptOnImportedModule } from "./currentscript.js";
+
+test(() => {
+ assert_equals(document.currentScript, null, "document.currentScript on inline scripts should be null");
+ assert_equals(currentScriptOnImportedModule, null, "document.currentScript on imported scripts should be null");
+ assert_equals(window.currentScriptRecorded, null, "document.currentScript on external module scripts should be null");
+}, "currentScript on script type=module should be all null");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentscript.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentscript.js
new file mode 100644
index 0000000000..547359ff96
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/currentscript.js
@@ -0,0 +1 @@
+export let currentScriptOnImportedModule = window.document.currentScript;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/custom-element-exception.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/custom-element-exception.html
new file mode 100644
index 0000000000..bd77a8f1bb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/custom-element-exception.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Handling of exceptions in custom element constructors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that exceptions from the constructor of a custom element " +
+ "inside a module are propagated as expected.\n");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(1, log.length);
+ const exception = log[0];
+ assert_true(exception instanceof Error);
+ assert_equals(exception.message, "custom element error");
+ }));
+</script>
+<script type="module">
+ class XThrower extends HTMLElement {
+ constructor() {
+ super();
+ throw new Error("custom element error");
+ }
+ }
+ customElements.define("x-thrower", XThrower);
+ document.createElement("x-thrower");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access-a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access-a.js
new file mode 100644
index 0000000000..1f91f93eb1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access-a.js
@@ -0,0 +1,3 @@
+log.push("cycle-tdz-access-a");
+import { Y } from "./cycle-tdz-access.js";
+export var X = Y;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access.js
new file mode 100644
index 0000000000..9df68b3b27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-tdz-access.js
@@ -0,0 +1,3 @@
+log.push("cycle-tdz-access");
+import { X } from "./cycle-tdz-access-a.js";
+export let Y = X;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable-a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable-a.js
new file mode 100644
index 0000000000..12994f23d0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable-a.js
@@ -0,0 +1,2 @@
+export {x} from "./cycle-unresolvable.js";
+log.push("cycle-unresolvable-a");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable.js
new file mode 100644
index 0000000000..61c6d8dcb0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/cycle-unresolvable.js
@@ -0,0 +1,2 @@
+export {x} from "./cycle-unresolvable-a.js";
+log.push("cycle-unresolvable");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-1.html
new file mode 100644
index 0000000000..57002a3e07
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Importing a module multiple times with the same specifier</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+window.log = [];
+</script>
+<script type="module">
+import { foo } from './export-something.js';
+import { set_foo } from './export-something.js';
+import default1 from './export-default.js';
+import default2 from './export-default.js';
+
+test(() => {
+ assert_array_equals(log, ['export-something', 'export-default']);
+ assert_equals(foo, 42);
+ set_foo(43);
+ assert_equals(foo, 43);
+ assert_equals(default1, "fox");
+ assert_equals(default2, "fox");
+}, 'Duplicated imports with the same specifier');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-2.html
new file mode 100644
index 0000000000..6a01495c33
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/duplicated-imports-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Importing a module multiple times with the different specifier</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+window.log = [];
+</script>
+<script type="module">
+import { foo } from './export-something.js';
+import { set_foo } from '../module/export-something.js';
+import default1 from './export-default.js';
+import default2 from '../module/export-default.js';
+
+test(() => {
+ assert_array_equals(log, ['export-something', 'export-default']);
+ assert_equals(foo, 42);
+ set_foo(43);
+ assert_equals(foo, 43);
+ assert_equals(default1, "fox");
+ assert_equals(default2, "fox");
+}, 'Duplicated imports with the different specifier');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html
new file mode 100644
index 0000000000..817cf6d5dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>Base URLs used in resolving specifiers in dynamic imports from importScripts()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+fetch_tests_from_worker(new Worker("./worker-importScripts.sub.js"));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker.sub.html
new file mode 100644
index 0000000000..a12204281c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker.sub.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<title>Base URLs used in resolving specifiers in dynamic imports from workers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+fetch_tests_from_worker(new Worker(
+ "../beta/redirect.py?location=http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js"));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html
new file mode 100644
index 0000000000..f7d4927a10
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Base URLs used in resolving specifiers in dynamic imports</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+self.testName = "same origin classic <script>";
+self.baseUrlSanitized = false;
+</script>
+<script src="../beta/redirect.py?location=http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js"></script>
+
+<script>
+self.testName = "cross origin classic <script> without crossorigin attribute";
+self.baseUrlSanitized = true;
+</script>
+<script src="../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js"></script>
+
+<script>
+self.testName = "cross origin classic <script> with crossorigin attribute";
+self.baseUrlSanitized = false;
+</script>
+<script src="../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js%3Fpipe=header(Access-Control-Allow-Origin,*)" crossorigin></script>
+
+<script>
+self.testName = "cross origin module <script>";
+self.baseUrlSanitized = false;
+</script>
+<script src="../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js%3Fpipe=header(Access-Control-Allow-Origin,*)" type="module"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/code-cache.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/code-cache.js
new file mode 100644
index 0000000000..8a8530c9b6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/code-cache.js
@@ -0,0 +1,9 @@
+promise_test(() => {
+ return (new Function('w', 'return import(w)'))("./import.js?Function")
+ .then(module => assert_equals(module.A.from, 'alpha/import.js'));
+}, 'alpha - Function');
+
+promise_test(() => {
+ return eval('import("./import.js?eval")')
+ .then(module => assert_equals(module.A.from, 'alpha/import.js'));
+}, 'alpha - eval');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/import.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/import.js
new file mode 100644
index 0000000000..b2ac52df2a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/import.js
@@ -0,0 +1 @@
+export const A = { "from": "alpha/import.js" };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/worker-importScripts.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/worker-importScripts.sub.js
new file mode 100644
index 0000000000..904d32f9bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/worker-importScripts.sub.js
@@ -0,0 +1,15 @@
+"use strict";
+
+importScripts("/resources/testharness.js");
+
+// CORS-same-origin
+self.testName = "same-origin importScripts()";
+self.baseUrlSanitized = false;
+importScripts("../beta/redirect.py?location=http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js");
+
+// CORS-cross-origin
+self.testName = "cross-origin importScripts()";
+self.baseUrlSanitized = true;
+importScripts("../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js");
+
+done();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/code-cache.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/code-cache.js
new file mode 100644
index 0000000000..1c2a2b636a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/code-cache.js
@@ -0,0 +1,9 @@
+promise_test(() => {
+ return (new Function('w', 'return import(w)'))("./import.js?Function")
+ .then(module => assert_equals(module.A.from, 'beta/import.js'));
+}, 'beta - Function');
+
+promise_test(() => {
+ return eval('import("./import.js?eval")')
+ .then(module => assert_equals(module.A.from, 'beta/import.js'));
+}, 'beta - eval');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/import.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/import.js
new file mode 100644
index 0000000000..7de1c68182
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/import.js
@@ -0,0 +1 @@
+export const A = { "from": "beta/import.js" };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/redirect.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/redirect.py
new file mode 100644
index 0000000000..f2fd1ebd51
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/redirect.py
@@ -0,0 +1,19 @@
+def main(request, response):
+ """Simple handler that causes redirection.
+
+ The request should typically have two query parameters:
+ status - The status to use for the redirection. Defaults to 302.
+ location - The resource to redirect to.
+ """
+ status = 302
+ if b"status" in request.GET:
+ try:
+ status = int(request.GET.first(b"status"))
+ except ValueError:
+ pass
+
+ response.status = status
+
+ location = request.GET.first(b"location")
+
+ response.headers.set(b"Location", location)
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url-workers.window.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url-workers.window.js
new file mode 100644
index 0000000000..70cb20fa57
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url-workers.window.js
@@ -0,0 +1,60 @@
+function objectUrlFromModule(module) {
+ const blob = new Blob([module], { type: "text/javascript" });
+ return URL.createObjectURL(blob);
+}
+
+const moduleText = `export const foo = "bar";`;
+
+async_test((t) => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+ t.add_cleanup(() => URL.revokeObjectURL(moduleBlobUrl));
+
+ const worker = new Worker("./resources/blob-url-worker.js");
+ worker.postMessage(moduleBlobUrl);
+
+ worker.addEventListener(
+ "message",
+ t.step_func_done((evt) => {
+ assert_true(evt.data.importSucceeded);
+ assert_equals(evt.data.module.foo, "bar");
+ })
+ );
+}, "A blob URL created in a window agent can be imported from a worker");
+
+async_test((t) => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+ URL.revokeObjectURL(moduleBlobUrl);
+
+ const worker = new Worker("./resources/blob-url-worker.js");
+ worker.postMessage(moduleBlobUrl);
+
+ worker.addEventListener(
+ "message",
+ t.step_func_done((evt) => {
+ assert_false(evt.data.importSucceeded);
+ assert_equals(evt.data.errorName, "TypeError");
+ })
+ );
+}, "A blob URL revoked in a window agent will not resolve in a worker");
+
+promise_test(async (t) => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+
+ await import(moduleBlobUrl);
+
+ URL.revokeObjectURL(moduleBlobUrl);
+
+ const worker = new Worker("./resources/blob-url-worker.js");
+ worker.postMessage(moduleBlobUrl);
+
+ await new Promise((resolve) => {
+ worker.addEventListener(
+ "message",
+ t.step_func((evt) => {
+ assert_false(evt.data.importSucceeded);
+ assert_equals(evt.data.errorName, "TypeError");
+ resolve();
+ })
+ );
+ });
+}, "A revoked blob URL will not resolve in a worker even if it's in the window's module graph");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js
new file mode 100644
index 0000000000..0719a18bf1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js
@@ -0,0 +1,66 @@
+// META: global=window,dedicatedworker,sharedworker,dedicatedworker-module,sharedworker-module
+
+function objectUrlFromModule(module) {
+ const blob = new Blob([module], { type: "text/javascript" });
+ return URL.createObjectURL(blob);
+}
+
+const moduleText = `export const foo = "bar";`;
+
+promise_test(async (t) => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+ t.add_cleanup(() => URL.revokeObjectURL(moduleBlobUrl));
+
+ const module = await import(moduleBlobUrl);
+ assert_equals(module.foo, "bar");
+}, "Blob URLs are supported in dynamic imports");
+
+promise_test(async (t) => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+ t.add_cleanup(() => URL.revokeObjectURL(moduleBlobUrl));
+
+ const module1 = await import(moduleBlobUrl);
+ const module2 = await import(moduleBlobUrl);
+ assert_equals(module1, module2);
+}, "Identical blob URLs resolve to the same module");
+
+promise_test(async (t) => {
+ const moduleBlob = new Blob([moduleText], { type: "text/javascript" });
+ const moduleBlobUrl1 = URL.createObjectURL(moduleBlob);
+ const moduleBlobUrl2 = URL.createObjectURL(moduleBlob);
+ t.add_cleanup(() => {
+ URL.revokeObjectURL(moduleBlobUrl1);
+ URL.revokeObjectURL(moduleBlobUrl2);
+ });
+
+ const module1 = await import(moduleBlobUrl1);
+ const module2 = await import(moduleBlobUrl2);
+ assert_not_equals(module1, module2);
+}, "Different blob URLs pointing to the same blob resolve to different modules");
+
+promise_test(async (t) => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+ URL.revokeObjectURL(moduleBlobUrl);
+
+ await promise_rejects_js(t, TypeError, import(moduleBlobUrl));
+}, "A revoked blob URL will not resolve");
+
+promise_test(async () => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+ const module1 = await import(moduleBlobUrl);
+
+ URL.revokeObjectURL(moduleBlobUrl);
+
+ const module2 = await import(moduleBlobUrl);
+ assert_equals(module1, module2);
+}, "A revoked blob URL will resolve if it's already in the module graph");
+
+promise_test(async () => {
+ const moduleBlobUrl = objectUrlFromModule(moduleText);
+
+ const importPromise = import(moduleBlobUrl);
+ URL.revokeObjectURL(moduleBlobUrl);
+
+ const module = await importPromise;
+ assert_equals(module.foo, "bar");
+}, "Revoking a blob URL immediately after calling import will not fail");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-base-url.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-base-url.html
new file mode 100644
index 0000000000..688a6193a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-base-url.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+Regression test for https://crbug.com/990810:
+Functions with the same source code shouldn't be cached if their referencing
+scripts and host defined options are different.
+
+Each Function in the following three scripts should have different base URLs
+respectively, but in https://crbug.com/990810 the Function in
+`gamma/code-cache.js` reuses the Function in `beta/code-cache.js`, resulting in
+wrong base URL.
+-->
+
+<script src="alpha/code-cache.js"></script>
+<script src="beta/code-cache.js"></script>
+<script src="gamma/code-cache.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-nonce.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-nonce.html
new file mode 100644
index 0000000000..bd920f8107
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/code-cache-nonce.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+// Regression test for https://crbug.com/979351:
+// This test loads a same script file (`resources/code-cache-nonce.js`)
+// with three different nonces (i.e. different host defined options).
+// Dynamic imports from the script therefore should have different nonces,
+// but when code caching ignores the difference in nonces, the first nonce
+// ('abc') is reused incorrectly for subsequent dynamic imports, causing
+// CSP violation (and thus dynamic import rejection).
+
+function runTest(nonce, description) {
+ // Perform a dynamic import with nonce=`nonce`
+ // from a page (`iframe`) with a matching CSP script-src 'nonce-`nonce`'.
+ // This should be successful.
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement('iframe');
+ iframe.src = 'resources/code-cache-nonce-iframe.sub.html?nonce=' + nonce;
+ iframe.onload = () => {
+ // `globalThis.promise` is set by `resources/code-cache-nonce.js`.
+ // `t.step_timeout()` is workaround for https://crbug.com/1247801.
+ globalThis.promise.then(
+ v => t.step_timeout(() => resolve(v), 0),
+ v => t.step_timeout(() => reject(v), 0)
+ );
+ };
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+ });
+ }, description);
+}
+
+// As `promise_test` are serialized, each iframe is created after previous
+// iframes and scripts are completely loaded.
+runTest('abc', 'First dynamic import should use nonce=abc');
+runTest('def', 'Second dynamic import should use nonce=def');
+runTest('ghi', 'Third dynamic import should use nonce=ghi');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/delay-load-event.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/delay-load-event.html
new file mode 100644
index 0000000000..5ec6433e65
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/delay-load-event.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Dynamic imports don't delay the load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// Dynamic imports don't #delay-the-load-event.
+// Therefore, Window load event would be fired
+// just after the dynamic import() below starts.
+window.loaded = [];
+window.addEventListener('load', () => loaded.push('Window load event'));
+promise_test(t => {
+ loaded.push('import start');
+ // This 'loading' log is added to detect the previous Chromium behavior
+ // where the Window load event is delayed until just before script
+ // element's load event.
+ t.step_timeout(() => loaded.push('loading'), 1000);
+ return import("../resources/slow-module.js?pipe=trickle(d2)")
+ .then(() => {
+ assert_array_equals(
+ loaded,
+ ['import start', 'Window load event', 'loading', 'slow'],
+ "Window load event shouldn't be delayed by dynamic imports");
+ });
+}, "Dynamic imports don't delay the load event.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials-setTimeout.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials-setTimeout.sub.html
new file mode 100644
index 0000000000..189aa819fa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials-setTimeout.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/dynamic-import-credentials-helper.sub.js"></script>
+
+<script type="text/javascript">
+runTestsFromIframe('../resources/dynamic-import-credentials-setTimeout-iframe.sub.html');
+</script>
+<body>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html
new file mode 100644
index 0000000000..910644531c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/dynamic-import-credentials-helper.sub.js"></script>
+
+<script type="text/javascript">
+runTestsFromIframe('../resources/dynamic-import-credentials-iframe.sub.html');
+</script>
+<body>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-fetch-error.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-fetch-error.sub.html
new file mode 100644
index 0000000000..12738c7b97
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-fetch-error.sub.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>import(): error cases occuring during fetching</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script type="module">
+const cases = [
+ ["wrong MIME type", "../errorhandling-wrongMimetype.js?pipe=header(Content-Type,text/plain)"],
+ ["wrong MIME type of a dependency", "../errorhandling-wrongMimetype-import.js"],
+ ["404", "../resources/404-but-js.asis"],
+ ["404 of a dependency", "../resources/imports-404-but-js.js"],
+ ["500", "../resources/500-but-js.asis"],
+ ["500 of a dependency", "../resources/imports-500-but-js.js"],
+ ["cross-origin module (without CORS)", "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/imports-a.js"],
+ ["cross-origin module dependency (without CORS)", "../resources/imports-b-cross-origin.sub.js"]
+];
+
+for (const [label, specifier] of cases) {
+ promise_test(t => {
+ return promise_rejects_js(t, TypeError, import(specifier));
+ }, "import() must reject when there is a " + label);
+
+ promise_test(async t => {
+ // The use of ?x is used to separate tests so they don't interfere with each other.
+ // (However, it shouldn't matter anyway, in a spec-compliant implementation.)
+ const suffix = (specifier.includes("?") ? "&" : "?") + "x";
+ const promise1 = import(specifier + suffix);
+ const promise2 = import(specifier + suffix);
+
+ await promise_rejects_js(t, TypeError, promise1, "It must reject the first time");
+ await promise_rejects_js(t, TypeError, promise2, "It must reject the second time");
+
+ const error1 = await promise1.catch(e => e);
+ const error2 = await promise2.catch(e => e);
+
+ assert_not_equals(error1, error2, "The error objects must be different");
+ }, "import() must reject with a different error object for each import when there is a " + label);
+}
+
+promise_test(async t => {
+ const id = token();
+ const url = `./resources/status-changing-script.py?id=${id}`;
+
+ // Serve HTTP 404 for the first import().
+ await fetch(url + '&newStatus=404');
+ const promise1 = import(url);
+ await promise_rejects_js(t, TypeError, promise1,
+ "First import() must be rejected due to 404");
+
+ // Serve HTTP 200 after the first import() completes.
+ await fetch(url + '&newStatus=200');
+ const r = await fetch(url, { cache: 'no-cache' });
+ assert_equals(r.status, 200);
+
+ const promise2 = import(url);
+ await promise_rejects_js(t, TypeError, promise2,
+ "Second import() must be rejected, because the result of " +
+ "the first import() is cached in the module map");
+}, "import() fetch errors must be cached");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-script-error.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-script-error.html
new file mode 100644
index 0000000000..e9b10e3bab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-script-error.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<title>import(): error cases caused by the imported module script</title>
+<link rel="author" title="Kouhei Ueno" href="mailto:kouhei@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script type="module">
+const cases = [
+ ["parse error", "../syntaxerror.js", SyntaxError],
+ ["bad module specifier", "does-not-start-with-dot.js", TypeError, { differentErrorObjects: true }],
+ ["bad module specifier in a dependency", "../bad-module-specifier.js", TypeError],
+ ["instantiation error", "../instantiation-error-1.js", SyntaxError, { differentErrorObjects: true }],
+ ["evaluation error", "../throw-error.js", Error]
+];
+
+for (const [label, specifier, error, { differentErrorObjects } = {}] of cases) {
+ promise_test(t => {
+ return promise_rejects_js(t, error, import(specifier));
+ }, "import() must reject when there is a " + label);
+
+ const errorObjectsLabel = differentErrorObjects ? "different error objects" : "the same error object";
+ promise_test(async t => {
+ // The use of ?x is used to separate tests so they don't interfere with each other.
+ // (However, it shouldn't matter anyway, in a spec-compliant implementation.)
+ const promise1 = import(specifier + "?x");
+ const promise2 = import(specifier + "?x");
+
+ await promise_rejects_js(t, error, promise1, "It must reject the first time");
+ await promise_rejects_js(t, error, promise2, "It must reject the second time");
+
+ const error1 = await promise1.catch(e => e);
+ const error2 = await promise2.catch(e => e);
+
+ if (differentErrorObjects) {
+ assert_not_equals(error1, error2, "The error objects must be different");
+ } else {
+ assert_equals(error1, error2, "The error objects must be equal");
+ }
+ }, `import() must reject with ${errorObjectsLabel} for each import when there is a ${label}`);
+}
+
+promise_test(t => {
+ delete window.before_throwing_error;
+ delete window.after_throwing_error;
+
+ return promise_rejects_js(t, Error, import("../throw-error.js?y")).then(() => {
+ assert_true(window.before_throwing_error,
+ "the module script should run to a point where it throws exception");
+ assert_equals(window.after_throwing_error, undefined,
+ "the module script should not run after it throws exception");
+ });
+}, "import()ing a module with an evaluation error must stop evaluation");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports.html
new file mode 100644
index 0000000000..9807e21b0d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Basic dynamic imports</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module">
+promise_test(t => {
+ return import("./../imports-a.js").then(module => {
+ assert_true(window.evaluated_imports_a);
+ assert_equals(module.A["from"], "imports-a.js");
+ });
+}, "Dynamic imports should resolve module.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js
new file mode 100644
index 0000000000..ec7784983d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js
@@ -0,0 +1,58 @@
+"use strict";
+
+// This script triggers import(), and thus the base URL of this script
+// (either loaded by `<script>` or `importScripts()`) is used as the base URL
+// of resolving relative URL-like specifiers in `import()`.
+
+// The following fields should be set by the callers of this script
+// (unless loaded as the worker top-level script):
+// - self.testName (string)
+// - self.baseUrlSanitized (boolean)
+
+// When this script is loaded as the worker top-level script:
+if ('DedicatedWorkerGlobalScope' in self &&
+ self instanceof DedicatedWorkerGlobalScope &&
+ !self.testName) {
+ importScripts("/resources/testharness.js");
+ self.testName = 'worker top-level script';
+ // Worker top-level scripts are always same-origin.
+ self.baseUrlSanitized = false;
+}
+
+{
+ // This could change by the time the test is executed, so we save it now.
+ // As this script is loaded multiple times, savedBaseUrlSanitized is scoped.
+ const savedBaseUrlSanitized = self.baseUrlSanitized;
+
+ promise_test(() => {
+ const promise = import("./import.js?pipe=header(Access-Control-Allow-Origin,*)&label=relative-" + self.testName);
+ if (savedBaseUrlSanitized) {
+ // The base URL is "about:blank" and thus import() here should fail.
+ return promise.then(module => {
+ // This code should be unreached, but assert_equals() is used here
+ // to log `module.A["from"]` in case of unexpected resolution.
+ assert_equals(module.A["from"], "(unreached)",
+ "Relative URL-like specifier resolution should fail");
+ assert_unreached();
+ },
+ () => {});
+ } else {
+ // The base URL is the response URL of this script, i.e.
+ // `.../gamma/base-url.sub.js`.
+ return promise.then(module => {
+ assert_equals(module.A["from"], "gamma/import.js");
+ });
+ }
+ },
+ "Relative URL-like from " + self.testName);
+}
+
+promise_test(() => {
+ return import("http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js?pipe=header(Access-Control-Allow-Origin,*)&label=absolute-" + self.testName)
+ .then(module => {
+ assert_equals(module.A["from"], "gamma/import.js");
+ })
+ },
+ "Absolute URL-like from " + self.testName);
+
+done();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/code-cache.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/code-cache.js
new file mode 100644
index 0000000000..b470bc8ae5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/code-cache.js
@@ -0,0 +1,9 @@
+promise_test(() => {
+ return (new Function('w', 'return import(w)'))("./import.js?Function")
+ .then(module => assert_equals(module.A.from, 'gamma/import.js'));
+}, 'gamma - Function');
+
+promise_test(() => {
+ return eval('import("./import.js?eval")')
+ .then(module => assert_equals(module.A.from, 'gamma/import.js'));
+}, 'gamma - eval');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js
new file mode 100644
index 0000000000..435c1369be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js
@@ -0,0 +1 @@
+export const A = { "from": "gamma/import.js" };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/inline-event-handler.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/inline-event-handler.html
new file mode 100644
index 0000000000..13cc8b6624
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/inline-event-handler.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id='div' onmousedown='import("./../imports-a.js").then(window.continueTest);'></div>
+<script>
+const div = document.getElementById('div');
+
+promise_test(t => {
+ const promise = new Promise(resolve => window.continueTest = resolve);
+
+ const event = new MouseEvent('mousedown', {'button': 1});
+ div.dispatchEvent(event);
+
+ return promise.then(() => {
+ assert_true(window.evaluated_imports_a);
+ div.parentNode.removeChild(div);
+ });
+}, "dynamic import should work when triggered from inline event handlers");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/basic.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/basic.any.js
new file mode 100644
index 0000000000..82cb3b215d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/basic.any.js
@@ -0,0 +1,32 @@
+// META: global=window,dedicatedworker,sharedworker
+// META: script=ticker.js
+
+promise_test(async t => {
+ const getCount = ticker(1000);
+
+ const importP = import("<invalid>");
+ await promise_rejects_js(t, TypeError, importP, 'import() should reject');
+
+ assert_less_than(getCount(), 1000);
+}, "import() should not drain the microtask queue if it fails during specifier resolution");
+
+promise_test(async t => {
+ // Use Date.now() to ensure that the module is not in the module map
+ const specifier = "./empty-module.js?" + Date.now();
+
+ await import(specifier);
+
+ const getCount = ticker(1000);
+ await import(specifier);
+ assert_less_than(getCount(), 1000);
+}, "import() should not drain the microtask queue when loading an already loaded module");
+
+promise_test(async t => {
+ // Use Date.now() to ensure that the module is not in the module map
+ const specifier = "./empty-module.js?" + Date.now();
+
+ const getCount = ticker(1e7);
+ await import(specifier);
+ assert_equals(getCount(), 1e7);
+}, "import() should drain the microtask queue when fetching a new module");
+
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/css-import-in-worker.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/css-import-in-worker.any.js
new file mode 100644
index 0000000000..bd6f5d092f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/css-import-in-worker.any.js
@@ -0,0 +1,14 @@
+// META: global=dedicatedworker,sharedworker
+// META: script=ticker.js
+
+promise_test(async t => {
+ // Use Date.now() to ensure that the module is not in the module map
+ const specifier = "./empty-module.css?" + Date.now();
+
+ const getCount = ticker(1000);
+
+ const importP = import(specifier, { assert: { type: "css" } });
+ await promise_rejects_js(t, TypeError, importP, 'import() should reject');
+
+ assert_less_than(getCount(), 1000);
+}, "import() should not drain the microtask queue if it fails because of the 'type: css' assertion in a worker");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.css b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.css
new file mode 100644
index 0000000000..108e7649bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.css
@@ -0,0 +1,4 @@
+/*
+This file is empty, because all it matters is if the
+dynamic import that loads it fails or succedes.
+*/
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.js
new file mode 100644
index 0000000000..108e7649bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/empty-module.js
@@ -0,0 +1,4 @@
+/*
+This file is empty, because all it matters is if the
+dynamic import that loads it fails or succedes.
+*/
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/serviceworker.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/serviceworker.any.js
new file mode 100644
index 0000000000..4c75cab1b6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/serviceworker.any.js
@@ -0,0 +1,14 @@
+// META: global=serviceworker
+// META: script=ticker.js
+
+promise_test(async t => {
+ // Use Date.now() to ensure that the module is not in the module map
+ const specifier = "./empty-module.js?" + Date.now();
+
+ const getCount = ticker(1000);
+
+ const importP = import(specifier);
+ await promise_rejects_js(t, TypeError, importP, 'import() should reject');
+
+ assert_less_than(getCount(), 1000);
+}, "import() should not drain the microtask queue if it fails because it's used in a ServiceWorker");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/ticker.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/ticker.js
new file mode 100644
index 0000000000..42619b6e70
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/ticker.js
@@ -0,0 +1,13 @@
+globalThis.ticker = function ticker(max) {
+ let i = 0;
+ let stop = false;
+ Promise.resolve().then(function loop() {
+ if (stop || i >= max) return;
+ i++;
+ Promise.resolve().then(loop);
+ });
+ return () => {
+ stop = true;
+ return i;
+ };
+};
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/with-import-assertions.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/with-import-assertions.any.js
new file mode 100644
index 0000000000..f67ba9a1ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/with-import-assertions.any.js
@@ -0,0 +1,15 @@
+// META: global=window,dedicatedworker,sharedworker
+// META: script=ticker.js
+
+promise_test(async t => {
+ // Use Date.now() to ensure that the module is not in the module map
+ const specifier = "./empty-module.js?" + Date.now();
+
+ const getCount = ticker(1000);
+
+ const importP = import(specifier, { assert: { type: "<invalid>" } });
+ await promise_rejects_js(t, TypeError, importP, 'import() should reject');
+
+ assert_less_than(getCount(), 1000);
+}, "import() should not drain the microtask queue if it fails while validating the 'type' assertion");
+
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet-ref.https.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet-ref.https.html
new file mode 100644
index 0000000000..6c598aee39
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet-ref.https.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<canvas id ="output" width="100" height="100" style="background: blue;"></canvas>
+<script>
+"use strict";
+const canvas = document.getElementById('output');
+const ctx = canvas.getContext('2d');
+
+ctx.fillStyle = 'green';
+ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet.https.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet.https.html
new file mode 100644
index 0000000000..5cd59f86dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/worklet.https.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://html.spec.whatwg.org/#hostimportmoduledynamically(referencingscriptormodule,-specifier,-promisecapability)">
+<link rel="match" href="worklet-ref.https.html">
+<style>
+#output {
+ width: 100px;
+ height: 100px;
+ background-image: paint(rects);
+ background-color: blue;
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+<body>
+<div id="output"></div>
+
+<script id="code" type="text/worklet">
+registerPaint('rects', class {
+ async paint(ctx, geom) {
+ ctx.fillStyle = 'red';
+
+ const getCount = ticker(1000);
+
+ try {
+ // Use Date.now() to ensure that the module is not in the module map
+ await import("./empty-module.js?" + Date.now());
+ } catch (e) {
+ if (getCount() < 1000) {
+ ctx.fillStyle = 'green';
+ }
+ }
+ ctx.fillRect(0, 0, geom.width, geom.height);
+ }
+});
+</script>
+
+<script>
+"use strict";
+CSS.paintWorklet.addModule("./ticker.js").then(() =>
+ importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent)
+);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-classic-manual.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-classic-manual.html
new file mode 100644
index 0000000000..b01f595c03
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-classic-manual.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Dynamic import when there is no active script</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/4181">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p>Click these buttons in sequence:</p>
+
+<button id="button1">Click me 1</button>
+
+<button id="button2">Click me 2</button>
+
+<p>The result will be pass/fail per the testharness.js results</p>
+
+<!-- We set the attributes from a separate script to specifically make
+ sure that it's not that script's base URL that gets used, but instead this page's. -->
+<script src="scripts/no-active-script.js"></script>
+
+<script>
+"use strict";
+setup({ explicit_timeout: true });
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = new Promise((resolve, reject) => {
+ window.continueTest1 = resolve;
+ window.errorTest1 = reject;
+ });
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+}, "onclick that directly imports should successfully import, using page's base URL");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = new Promise((resolve, reject) => {
+ window.continueTest2 = resolve;
+ window.errorTest2 = reject;
+ });
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+}, "onclick that indirectly imports after a task should successfully import, using page's base URL");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-module-manual.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-module-manual.html
new file mode 100644
index 0000000000..359b71d821
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-module-manual.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Dynamic import when there is no active script</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/4181">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p>Click these buttons in sequence:</p>
+
+<button id="button1">Click me 1</button>
+
+<button id="button2">Click me 2</button>
+
+<p>The result will be pass/fail per the testharness.js results</p>
+
+<!-- We set the attributes from a separate script to specifically make
+ sure that it's not that script's base URL that gets used, but instead this page's. -->
+<script src="scripts/no-active-script.js" type="module"></script>
+
+<script type="module">
+"use strict";
+setup({ explicit_timeout: true });
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = new Promise((resolve, reject) => {
+ window.continueTest1 = resolve;
+ window.errorTest1 = reject;
+ });
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+}, "onclick that directly imports should successfully import, using page's base URL");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = new Promise((resolve, reject) => {
+ window.continueTest2 = resolve;
+ window.errorTest2 = reject;
+ });
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+}, "onclick that indirectly imports after a task should successfully import, using page's base URL");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-classic.html
new file mode 100644
index 0000000000..0958cfbeba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-classic.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta http-equiv="content-security-policy" content="script-src 'nonce-correct'">
+<script nonce="correct" src="/resources/testharness.js"></script>
+<script nonce="correct" src="/resources/testharnessreport.js"></script>
+<script nonce="correct" src="./propagate-nonce-external.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html
new file mode 100644
index 0000000000..47c422e59c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta http-equiv="content-security-policy" content="script-src 'nonce-correct'">
+<script nonce="correct" src="/resources/testharness.js"></script>
+<script nonce="correct" src="/resources/testharnessreport.js"></script>
+<script type="module" nonce="correct" src="./propagate-nonce-external.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external.js
new file mode 100644
index 0000000000..3b97d2f40e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external.js
@@ -0,0 +1,7 @@
+// This file is loaded both as a module and as a classic script.
+promise_test(t => {
+ return import("../imports-a.js").then(module => {
+ assert_true(window.evaluated_imports_a);
+ assert_equals(module.A["from"], "imports-a.js");
+ });
+}, "Dynamically imported module should eval when imported from script w/ a valid nonce.");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-classic.html
new file mode 100644
index 0000000000..754110cb5e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-classic.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta http-equiv="content-security-policy" content="script-src 'nonce-correct'">
+<script nonce="correct" src="/resources/testharness.js"></script>
+<script nonce="correct" src="/resources/testharnessreport.js"></script>
+<script nonce="correct">
+promise_test(t => {
+ return import("./../imports-a.js").then(module => {
+ assert_true(window.evaluated_imports_a);
+ assert_equals(module.A["from"], "imports-a.js");
+ });
+}, "Dynamically imported module should eval when imported from script w/ a valid nonce.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html
new file mode 100644
index 0000000000..f3322773a4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta http-equiv="content-security-policy" content="script-src 'nonce-correct'">
+<script nonce="correct" src="/resources/testharness.js"></script>
+<script nonce="correct" src="/resources/testharnessreport.js"></script>
+<script type="module" nonce="correct">
+promise_test(t => {
+ return import("./../imports-a.js").then(module => {
+ assert_true(window.evaluated_imports_a);
+ assert_equals(module.A["from"], "imports-a.js");
+ });
+}, "Dynamically imported module should eval when imported from script w/ a valid nonce.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/blob-url-worker.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/blob-url-worker.js
new file mode 100644
index 0000000000..07071e05ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/blob-url-worker.js
@@ -0,0 +1,11 @@
+self.addEventListener("message", (evt) => {
+ const importModule = import(evt.data);
+ importModule.then(
+ (module) => {
+ self.postMessage({ importSucceeded: true, module: { ...module } });
+ },
+ (error) => {
+ self.postMessage({ importSucceeded: false, errorName: error.name });
+ }
+ );
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce-iframe.sub.html
new file mode 100644
index 0000000000..56f915aac1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce-iframe.sub.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta http-equiv="content-security-policy"
+ content="script-src 'unsafe-eval' 'nonce-{{GET[nonce]}}'">
+<script src="code-cache-nonce.js" nonce="{{GET[nonce]}}"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce.js
new file mode 100644
index 0000000000..e9983fb6cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/code-cache-nonce.js
@@ -0,0 +1,4 @@
+// Note that the function source text is intentionally different from e.g.
+// ../alpha/code-cache.js to avoid caching Functions between different sets
+// of tests.
+parent.promise = (new Function('x', 'return import(x)'))('../../imports-a.js');
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/empty-iframe.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/empty-iframe.html
new file mode 100644
index 0000000000..ad5ab30eda
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/empty-iframe.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <div id="dummy"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/status-changing-script.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/status-changing-script.py
new file mode 100644
index 0000000000..a44d3dd3eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/status-changing-script.py
@@ -0,0 +1,18 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript"),
+ (b"Cache-Control", b"private, no-store")]
+
+ id = request.GET.first(b"id")
+
+ with request.server.stash.lock:
+ status = request.server.stash.take(id)
+ if status is None:
+ status = 200
+
+ new_status = request.GET.first(b"newStatus", None)
+ if new_status is not None:
+ status = int(new_status)
+
+ request.server.stash.put(id, status)
+
+ return status, headers, b""
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache-iframe.sub.html
new file mode 100644
index 0000000000..c3a870b3f1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache-iframe.sub.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta http-equiv="content-security-policy"
+ content="script-src 'nonce-{{GET[nonce]}}'">
+<!--
+base element to make the base URLs of the Document and the script different
+-->
+<base href="../">
+<script src="resources/v8-code-cache.js?pipe=header(Cache-Control,max-age=1000)"
+ nonce="{{GET[nonce]}}" type="{{GET[type]}}"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache.js
new file mode 100644
index 0000000000..1d3e88a51d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/resources/v8-code-cache.js
@@ -0,0 +1,74 @@
+parent.promise = import('../../imports-a.js');
+
+// Padding for triggering V8 Code Cache on Chromium.
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
+// ============================================================================
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/Function.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/Function.js
new file mode 100644
index 0000000000..447e5060b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/Function.js
@@ -0,0 +1,2 @@
+// import()s in a dynamically created function are resolved relative to the script.
+Function(`import('../../imports-a.js?label=' + window.label).then(window.continueTest, window.errorTest)`)();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/eval.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/eval.js
new file mode 100644
index 0000000000..100602733a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/eval.js
@@ -0,0 +1,2 @@
+// import()s in eval are resolved relative to the script.
+eval(`import('../../imports-a.js?label=' + window.label).then(window.continueTest, window.errorTest)`);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/inline-event-handlers-UA-code.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/inline-event-handlers-UA-code.js
new file mode 100644
index 0000000000..3202ce47b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/inline-event-handlers-UA-code.js
@@ -0,0 +1,3 @@
+// import()s in an event handler are resolved relative to the document base.
+window.dummyDiv.setAttribute("onclick", `import('./imports-a.js?label=' + window.label).then(window.continueTest, window.errorTest)`);
+window.dummyDiv.click(); // different from **on**click()
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/no-active-script.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/no-active-script.js
new file mode 100644
index 0000000000..85d8ac29ec
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/no-active-script.js
@@ -0,0 +1,5 @@
+"use strict";
+
+document.querySelector("#button1").setAttribute("onclick", "import('../imports-a.js?label=button1').then(window.continueTest1, window.errorTest1)");
+
+document.querySelector("#button2").setAttribute("onclick", "Promise.resolve(`import('../imports-a.js?label=button2')`).then(eval).then(window.continueTest2, window.errorTest2);");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/reflected-inline-event-handlers.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/reflected-inline-event-handlers.js
new file mode 100644
index 0000000000..923eb7d8b6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/reflected-inline-event-handlers.js
@@ -0,0 +1,3 @@
+// import()s in an event handler are resolved relative to the document base.
+window.dummyDiv.setAttribute("onclick", `import('./imports-a.js?label=' + window.label).then(window.continueTest, window.errorTest)`);
+window.dummyDiv.onclick();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/setTimeout.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/setTimeout.js
new file mode 100644
index 0000000000..342b342e8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/scripts/setTimeout.js
@@ -0,0 +1,2 @@
+// import()s in a timeout handler are resolved relative to the script.
+setTimeout(`import('../../imports-a.js?label=' + window.label).then(window.continueTest, window.errorTest)`, 0);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html
new file mode 100644
index 0000000000..855705e585
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings uses the script base URL inside a classic script that is loaded from a file</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="dummy"></div>
+
+<base href="scripts/foo/">
+<script>
+// Tweak the base URL of the document here to distinguish:
+// - document URL
+// - document base URL = ../
+// - External scripts' base URL = ./scripts/eval.js etc.
+// - This inline script's base URL = ./scripts/foo/
+document.querySelector("base").remove();
+const base = document.createElement("base");
+base.setAttribute("href", "../");
+document.body.appendChild(base);
+
+function load(scriptSrc) {
+ const el = document.createElement("script");
+ el.src = scriptSrc;
+ document.body.appendChild(el);
+}
+
+function createTestPromise() {
+ return new Promise((resolve, reject) => {
+ window.dummyDiv.removeAttribute("onclick");
+ delete window.evaluated_imports_a;
+ delete window.label;
+
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+window.dummyDiv = document.querySelector("#dummy");
+
+const evaluators = [
+ "setTimeout",
+ "eval",
+ "Function",
+ "reflected-inline-event-handlers",
+ "inline-event-handlers-UA-code"
+];
+
+for (const label of evaluators) {
+ promise_test(() => {
+ const promise = createTestPromise();
+
+ window.label = label;
+ load(`dynamic-import/scripts/${label}.js`);
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html
new file mode 100644
index 0000000000..d90af9c96a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings uses the script base URL inside a module script that is loaded from a file</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="dummy"></div>
+
+<base href="scripts/foo/">
+<script type="module">
+// Tweak the base URL of the document here to distinguish:
+// - document URL
+// - document base URL = ../
+// - External scripts' base URL = ./scripts/eval.js etc.
+// - This inline script's base URL = ./scripts/foo/
+document.querySelector("base").remove();
+const base = document.createElement("base");
+base.setAttribute("href", "../");
+document.body.appendChild(base);
+
+function load(scriptSrc) {
+ const el = document.createElement("script");
+ el.type = "module";
+ el.src = scriptSrc;
+ document.body.appendChild(el);
+}
+
+function createTestPromise() {
+ return new Promise((resolve, reject) => {
+ window.dummyDiv.removeAttribute("onclick");
+ delete window.evaluated_imports_a;
+ delete window.label;
+
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+window.dummyDiv = document.querySelector("#dummy");
+
+const evaluators = [
+ "setTimeout",
+ "eval",
+ "Function",
+ "reflected-inline-event-handlers",
+ "inline-event-handlers-UA-code"
+];
+
+for (const label of evaluators) {
+ promise_test(() => {
+ const promise = createTestPromise();
+
+ window.label = label;
+ load(`dynamic-import/scripts/${label}.js`);
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html
new file mode 100644
index 0000000000..2fe1f75535
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings uses the script base URL (= document base URL) inside an inline classic script</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<div id="dummy"></div>
+
+<base href="scripts/foo/">
+<script>
+// Tweak the base URL of the document here to distinguish:
+// - document URL
+// - document base URL = ../
+// - This inline script's base URL = ./scripts/foo/
+document.querySelector("base").remove();
+const base = document.createElement("base");
+base.setAttribute("href", "../");
+document.body.appendChild(base);
+
+function createTestPromise() {
+ return new Promise((resolve, reject) => {
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+const dummyDiv = document.querySelector("#dummy");
+
+function doTest(label, evaluator, path) {
+ promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = createTestPromise();
+
+ evaluator(`import('${path}/imports-a.js?label=${label}').then(window.continueTest, window.errorTest);`);
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+}
+
+// Inline script's base URL should be used.
+doTest("setTimeout", setTimeout, "../../..");
+doTest("eval", eval, "../../..");
+doTest("the Function constructor",
+ (x) => {
+ Function(x)();
+ },
+ "../../..");
+
+// Document's base URL should be used, as there are no active scripts.
+doTest("reflected inline event handlers",
+ (x) => {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.onclick();
+ },
+ ".");
+
+doTest("inline event handlers triggered via UA code",
+ (x) => {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.click(); // different from .**on**click()
+ },
+ ".");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html
new file mode 100644
index 0000000000..1691550ecb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings uses the script base URL (= document base URL) inside an inline module script</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<div id="dummy"></div>
+
+<base href="scripts/foo/">
+<script type="module">
+// Tweak the base URL of the document here to distinguish:
+// - document URL
+// - document base URL = ../
+// - This inline script's base URL = ./scripts/foo/
+document.querySelector("base").remove();
+const base = document.createElement("base");
+base.setAttribute("href", "../");
+document.body.appendChild(base);
+
+function createTestPromise() {
+ return new Promise((resolve, reject) => {
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+const dummyDiv = document.querySelector("#dummy");
+
+function doTest(label, evaluator, path) {
+ promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = createTestPromise();
+
+ evaluator(`import('${path}/imports-a.js?label=${label}').then(window.continueTest, window.errorTest);`);
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+}
+
+// Inline script's base URL should be used.
+doTest("setTimeout", setTimeout, "../../..");
+doTest("eval", eval, "../../..");
+doTest("the Function constructor",
+ (x) => {
+ Function(x)();
+ },
+ "../../..");
+
+// Document's base URL should be used, as there are no active scripts.
+doTest("reflected inline event handlers",
+ (x) => {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.onclick();
+ },
+ ".");
+
+doTest("inline event handlers triggered via UA code",
+ (x) => {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.click(); // different from .**on**click()
+ },
+ ".");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html
new file mode 100644
index 0000000000..34ea00abc8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings inside a classic script</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="dummy"></div>
+
+<script>
+function createTestPromise() {
+ return new Promise((resolve, reject) => {
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+const dummyDiv = document.querySelector("#dummy");
+
+const evaluators = {
+ eval,
+ setTimeout,
+ "the Function constructor"(x) {
+ Function(x)();
+ },
+ "reflected inline event handlers"(x) {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.onclick();
+ },
+ "inline event handlers triggered via UA code"(x) {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.click(); // different from .**on**click()
+ }
+};
+
+for (const [label, evaluator] of Object.entries(evaluators)) {
+ promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = createTestPromise();
+
+ evaluator(`import('../imports-a.js?label=${label}').then(window.continueTest, window.errorTest);`);
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html
new file mode 100644
index 0000000000..b85d446d8d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings inside a module script</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="dummy"></div>
+
+<script type="module">
+function createTestPromise() {
+ return new Promise((resolve, reject) => {
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+const dummyDiv = document.querySelector("#dummy");
+
+const evaluators = {
+ eval,
+ setTimeout,
+ "the Function constructor"(x) {
+ Function(x)();
+ },
+ "reflected inline event handlers"(x) {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.onclick();
+ },
+ "inline event handlers triggered via UA code"(x) {
+ dummyDiv.setAttribute("onclick", x);
+ dummyDiv.click(); // different from .**on**click()
+ }
+};
+
+for (const [label, evaluator] of Object.entries(evaluators)) {
+ promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ delete window.evaluated_imports_a;
+ });
+
+ const promise = createTestPromise();
+
+ evaluator(`import('../imports-a.js?label=${label}').then(window.continueTest, window.errorTest);`);
+
+ return promise.then(module => {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html
new file mode 100644
index 0000000000..b582eba8b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings uses the appropriate nonce inside a classic script</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<meta http-equiv="content-security-policy" content="script-src 'nonce-correct' 'unsafe-eval' 'unsafe-hashes' 'sha256-cAMzxBL19bKt4KwKGbxy/ZOFIIjH5AmRjlVbsD5pvNw=' 'sha256-3VjoJYNK/9HJMS8rrZHlqSZgUssDY+GPyc7AU8lNM3k='">
+
+<script nonce="correct" src="/resources/testharness.js"></script>
+<script nonce="correct" src="/resources/testharnessreport.js"></script>
+
+<div id="dummy"></div>
+
+<script nonce="correct">
+"use strict";
+const dummyDiv = document.querySelector("#dummy");
+
+function createTestPromise(t) {
+ t.add_cleanup(() => {
+ delete window.evaluated_imports_a;
+ delete window.unreached;
+ delete window.continueTest;
+ delete window.errorTest;
+ });
+
+ return new Promise((resolve, reject) => {
+ window.unreached = t.unreached_func("Must not reach this");
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+function assertSuccessful(module) {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+}
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ setTimeout(`import('../imports-a.js?label=setTimeout').then(window.continueTest, window.errorTest)`, 0);
+
+ return promise.then(assertSuccessful);
+}, "setTimeout must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ eval(`import('../imports-a.js?label=direct eval').then(window.continueTest, window.errorTest)`);
+
+ return promise.then(assertSuccessful);
+}, "direct eval must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ const evalAlias = eval;
+ evalAlias(`import('../imports-a.js?label=indirect eval').then(window.continueTest, window.errorTest)`);
+
+ return promise.then(assertSuccessful);
+}, "indirect eval must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ Function(`import('../imports-a.js?label=the Function constructor').then(window.continueTest, window.errorTest)`)();
+
+ return promise.then(assertSuccessful);
+}, "the Function constructor must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ });
+
+ const promise = createTestPromise(t);
+
+ // This only works because of the 'unsafe-hashes' and the hash in the CSP policy
+ dummyDiv.setAttribute(
+ "onclick",
+ `import('../imports-a.js?label=reflected inline event handlers').then(window.continueTest, window.errorTest)`
+ );
+ dummyDiv.onclick();
+
+ return promise_rejects_js(t, TypeError, promise);
+}, "reflected inline event handlers must not inherit the nonce from the triggering script, thus fail");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ });
+
+ const promise = createTestPromise(t);
+
+ // This only works because of the 'unsafe-hashes' and the hash in the CSP policy
+ dummyDiv.setAttribute(
+ "onclick",
+ `import('../imports-a.js?label=inline event handlers triggered via UA code').then(window.continueTest, window.errorTest)`
+ );
+ assert_equals(typeof dummyDiv.onclick, "function", "the browser must be able to parse a string containing the import() syntax into a function");
+ dummyDiv.click(); // different from **on**click()
+
+ return promise_rejects_js(t, TypeError, promise);
+}, "inline event handlers triggered via UA code must not inherit the nonce from the triggering script, thus fail");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html
new file mode 100644
index 0000000000..4fa1cc5877
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings uses the appropriate nonce inside a module script</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<meta http-equiv="content-security-policy" content="script-src 'nonce-correct' 'unsafe-eval' 'unsafe-hashes' 'sha256-cAMzxBL19bKt4KwKGbxy/ZOFIIjH5AmRjlVbsD5pvNw=' 'sha256-3VjoJYNK/9HJMS8rrZHlqSZgUssDY+GPyc7AU8lNM3k='">
+
+<script nonce="correct" src="/resources/testharness.js"></script>
+<script nonce="correct" src="/resources/testharnessreport.js"></script>
+
+<div id="dummy"></div>
+
+<script type="module" nonce="correct">
+const dummyDiv = document.querySelector("#dummy");
+
+function createTestPromise(t) {
+ t.add_cleanup(() => {
+ delete window.evaluated_imports_a;
+ delete window.unreached;
+ delete window.continueTest;
+ delete window.errorTest;
+ });
+
+ return new Promise((resolve, reject) => {
+ window.unreached = t.unreached_func("Must not reach this");
+ window.continueTest = resolve;
+ window.errorTest = reject;
+ });
+}
+
+function assertSuccessful(module) {
+ assert_true(window.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+}
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ setTimeout(`import('../imports-a.js?label=setTimeout').then(window.continueTest, window.errorTest)`, 0);
+
+ return promise.then(assertSuccessful);
+}, "setTimeout must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ eval(`import('../imports-a.js?label=direct eval').then(window.continueTest, window.errorTest)`);
+
+ return promise.then(assertSuccessful);
+}, "direct eval must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ const evalAlias = eval;
+ evalAlias(`import('../imports-a.js?label=indirect eval').then(window.continueTest, window.errorTest)`);
+
+ return promise.then(assertSuccessful);
+}, "indirect eval must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ const promise = createTestPromise(t);
+
+ Function(`import('../imports-a.js?label=the Function constructor').then(window.continueTest, window.errorTest)`)();
+
+ return promise.then(assertSuccessful);
+}, "the Function constructor must inherit the nonce from the triggering script, thus execute");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ });
+
+ const promise = createTestPromise(t);
+
+ // This only works because of the 'unsafe-hashes' and the hash in the CSP policy
+ dummyDiv.setAttribute(
+ "onclick",
+ `import('../imports-a.js?label=reflected inline event handlers').then(window.continueTest, window.errorTest)`
+ );
+ dummyDiv.onclick();
+
+ return promise_rejects_js(t, TypeError, promise);
+}, "reflected inline event handlers must not inherit the nonce from the triggering script, thus fail");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ dummyDiv.removeAttribute("onclick");
+ });
+
+ const promise = createTestPromise(t);
+
+ // This only works because of the 'unsafe-hashes' and the hash in the CSP policy
+ dummyDiv.setAttribute(
+ "onclick",
+ `import('../imports-a.js?label=inline event handlers triggered via UA code').then(window.continueTest, window.errorTest)`
+ );
+ assert_equals(typeof dummyDiv.onclick, 'function', "the browser must be able to parse a string containing the import() syntax into a function");
+ dummyDiv.click(); // different from **on**click()
+
+ return promise_rejects_js(t, TypeError, promise);
+}, "inline event handlers triggered via UA code must not inherit the nonce from the triggering script, thus fail");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html
new file mode 100644
index 0000000000..5514049c78
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>import() inside compiled strings inside a classic script</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/3163">
+<link rel="help" href="https://github.com/tc39/ecma262/issues/871#issuecomment-292493142">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<base href="scripts/foo/">
+<script>
+"use strict";
+
+// This test is based on the current specification, but all browser
+// implementations aren't conformant. See
+// https://bugs.chromium.org/p/chromium/issues/detail?id=1245063
+// https://github.com/heycam/webidl/pull/902
+
+// Tweak the base URL of the document here to distinguish:
+// - document URL
+// - document base URL = ../
+// - This inline script's base URL = ./scripts/foo/
+document.querySelector("base").remove();
+const base = document.createElement("base");
+base.setAttribute("href", "../");
+document.body.appendChild(base);
+
+self.ran = false;
+
+// The active script for the dynamic import is this inline script
+// (see https://html.spec.whatwg.org/C/#hostmakejobcallback).
+promise_test(t => {
+ t.add_cleanup(() => {
+ self.ran = false;
+ })
+
+ return Promise.resolve(`import("../../../imports-a.js?1").then(() => { self.ran = true; })`)
+ .then(eval)
+ .then(() => {
+ assert_true(self.ran);
+ });
+}, "Evaled the script via eval, successful import");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ self.ran = false;
+ })
+
+ return Promise.resolve(`import("bad-specifier?1").catch(() => { self.ran = true; })`)
+ .then(eval)
+ .then(() => {
+ assert_true(self.ran);
+ });
+}, "Evaled the script via eval, failed import");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ self.ran = false;
+ })
+
+ return Promise.resolve(`return import("../../../imports-a.js?2").then(() => { self.ran = true; })`)
+ .then(Function)
+ .then(Function.prototype.call.bind(Function.prototype.call))
+ .then(() => {
+ assert_true(self.ran);
+ });
+}, "Evaled the script via Function, successful import");
+
+promise_test(t => {
+ t.add_cleanup(() => {
+ self.ran = false;
+ })
+
+ return Promise.resolve(`return import("bad-specifier?2").catch(() => { self.ran = true; })`)
+ .then(Function)
+ .then(Function.prototype.call.bind(Function.prototype.call))
+ .then(() => {
+ assert_true(self.ran);
+ });
+}, "Evaled the script via Function, failed import");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-other-document.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-other-document.html
new file mode 100644
index 0000000000..3b1d98f6b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-other-document.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Check import() works when active script is in another document</title>
+<link rel="author" title="Jon Coppeard" href="mailto:jcoppeard@mozilla.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<iframe id="frame" src="resources/empty-iframe.html"></iframe>
+
+<script>
+
+function startTest() {
+ const otherWindow = document.getElementById("frame").contentWindow;
+ const otherDiv = otherWindow.document.getElementById("dummy");
+
+ function createTestPromise() {
+ return new Promise((resolve, reject) => {
+ otherWindow.continueTest = resolve;
+ otherWindow.errorTest = reject;
+ });
+ }
+
+ const evaluators = {
+ eval: otherWindow.eval,
+ setTimeout: otherWindow.setTimeout,
+ "the Function constructor"(x) {
+ otherWindow.Function(x)();
+ },
+ };
+
+ for (const [label, evaluator] of Object.entries(evaluators)) {
+ promise_test(t => {
+ t.add_cleanup(() => {
+ otherDiv.removeAttribute("onclick");
+ delete otherWindow.evaluated_imports_a;
+ });
+
+ const promise = createTestPromise();
+
+ evaluator(`import('../imports-a.js?label=${label}').then(window.continueTest, window.errorTest);`);
+
+ return promise.then(module => {
+ assert_true(otherWindow.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+ };
+
+ const eventHandlerEvaluators = {
+ "reflected inline event handlers"(x) {
+ otherDiv.setAttribute("onclick", x);
+ otherDiv.onclick();
+ },
+ "inline event handlers triggered by JS"(x) {
+ otherDiv.setAttribute("onclick", x);
+ otherDiv.click(); // different from .**on**click()
+ }
+ };
+
+ for (const [label, evaluator] of Object.entries(eventHandlerEvaluators)) {
+ promise_test(t => {
+ t.add_cleanup(() => {
+ otherDiv.removeAttribute("onclick");
+ delete otherWindow.evaluated_imports_a;
+ });
+
+ const promise = createTestPromise();
+
+ evaluator(`import('../../imports-a.js?label=${label}').then(window.continueTest, window.errorTest);`);
+
+ return promise.then(module => {
+ assert_true(otherWindow.evaluated_imports_a, "The module must have been evaluated");
+ assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
+ });
+ }, label + " should successfully import");
+ };
+}
+</script>
+<body onLoad="startTest()"></body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/v8-code-cache.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/v8-code-cache.html
new file mode 100644
index 0000000000..77de19e74b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/v8-code-cache.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+// Regression test for https://crbug.com/1244145:
+// This test loads a same script file (`resources/v8-code-cache.js`)
+// multiple times to trigger V8 Code Cache.
+// Host defined options (including base URLs and nonces) are lost when the
+// script is compiled using the cached metadata, and thus causing
+// dynamic import failures due to wrong base URLs and wrong nonces.
+
+function runTest(type, nonce, description) {
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement('iframe');
+ iframe.src = 'resources/v8-code-cache-iframe.sub.html?nonce=' + nonce + '&type=' + type;
+ iframe.onload = () => {
+ // `window.promise` is set by `resources/v8-code-cache.js`.
+ window.promise.then(resolve, reject);
+ };
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+ });
+ }, type + ': ' + description);
+}
+
+// As `promise_test` are serialized, each iframe is created after previous
+// iframes and scripts are completely loaded.
+for (const type of ['text/javascript', 'module']) {
+ // Cache the script in V8 Code Cache by running multiple times.
+ runTest(type, 'abc', 'Run #1');
+ runTest(type, 'abc', 'Run #2');
+ runTest(type, 'abc', 'Run #3');
+ runTest(type, 'abc', 'Run #4');
+ // Changing the nonce seems to disable compilation cache, trigger compilation
+ // using V8 Code Cache and thus expose the bug.
+ runTest(type, 'def', 'Run #5');
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-and-slow-dependency.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-and-slow-dependency.html
new file mode 100644
index 0000000000..f336276f3f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-and-slow-dependency.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Module importing syntax error script and slow script should not crash UA</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module">
+setup({allow_uncaught_exception: true});
+window.log = [];
+window.loaded = false;
+</script>
+<script type="module">
+import "./syntaxerror.js";
+import "./resources/delayed-modulescript.py";
+
+window.loaded = true;
+</script>
+<script type="module">
+test(() => {
+ assert_false(loaded);
+ }, "module graph with a syntax error should not evaulate but should not crash UA.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.html
new file mode 100644
index 0000000000..2480a60d6d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Handling of different types of errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "network error has higher priority than parse error");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 3);
+
+ // A parse error is reported for the first top-level
+ // <script> element for syntaxerror.js.
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_equals(log[1], 1);
+
+ // onerror is called (with no errors reported) due to a network error
+ // for the second top-level <script>.
+ assert_equals(log[2], 2);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./syntaxerror.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./error-type-1.js"
+ onerror="log.push(2)" onload="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.js
new file mode 100644
index 0000000000..4882d3f2a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-1.js
@@ -0,0 +1,2 @@
+import './syntaxerror.js';
+import './404.js';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.html
new file mode 100644
index 0000000000..673bf28ca2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Handling of different types of errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "parse error has higher priority than instantiation error");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 4);
+
+ // An instantiation error is reported for the first top-level
+ // <script> element for instantiation-error-1.js.
+ assert_equals(log[0].constructor, SyntaxError);
+ assert_equals(log[1], 1);
+
+ // A parse error is reported for the second top-level <script>.
+ assert_equals(log[2].constructor, SyntaxError);
+ assert_equals(log[3], 2);
+ assert_not_equals(log[0], log[2]);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./instantiation-error-1.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./error-type-2.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.js
new file mode 100644
index 0000000000..6b11397300
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-2.js
@@ -0,0 +1,2 @@
+import './instantiation-error-1.js';
+import './syntaxerror.js';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.html
new file mode 100644
index 0000000000..8a16266f4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Handling of different types of errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "instantiation error has higher priority than evaluation error");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 5);
+
+ // An evaluation error is reported for the first top-level
+ // <script> element for throw.js.
+ assert_equals(log[0], 'throw');
+ assert_true(log[1].foo);
+ assert_equals(log[2], 1);
+
+ // An instantiation error is reported for the second top-level <script>.
+ assert_equals(log[3].constructor, SyntaxError);
+ assert_equals(log[4], 2);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./throw.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./error-type-3.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.js
new file mode 100644
index 0000000000..542be52846
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/error-type-3.js
@@ -0,0 +1,2 @@
+import './throw.js';
+import './instantiation-error-1.js';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-common.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-common.js
new file mode 100644
index 0000000000..4eb5597a58
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-common.js
@@ -0,0 +1,10 @@
+function errorHandler(ev)
+{
+ document._errorReported.push("error");
+}
+
+document._errorReported = [];
+window.addEventListener("error", errorHandler);
+window.addEventListener("load", function () {
+ document._errorReported = document._errorReported.join(",");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.html
new file mode 100644
index 0000000000..3a00f62f00
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+ <title>html-script-module-errorHandling-parseError-Dependent</title>
+ <script src="errorhandling-parseerror-common.js"></script>
+</head>
+<body>
+ <script type="module" onerror="errorHandler(event);">
+
+ // No parse errors in the root module, just in the dependent module
+ import test from "./errorhandling-parseerror-dependent.js";
+ document._errorReported = "shouldn't have run dependent module";
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.js
new file mode 100644
index 0000000000..71872c47cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependent.js
@@ -0,0 +1,2 @@
+// Parse error in a dependent module
+1A
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.html
new file mode 100644
index 0000000000..7775aeabb4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+<head>
+ <title>html-script-module-errorHandling-parseError-DependentMultiple</title>
+ <script src="errorhandling-parseerror-common.js"></script>
+</head>
+<body>
+ <script type="module" onerror="errorHandler(event)">
+
+ // No parse errors in the root module, just in the dependent module
+ import test from "./errorhandling-parseerror-dependentmultiple.js";
+ document._errorReported = "shouldn't have run dependent module";
+
+ </script>
+ <script type="module" onerror="errorHandler(event)">
+
+ // With the broken dependent module already acquired, try to import it
+ // again from another root. This root should be unwound appropriately.
+ import test from "./errorhandling-parseerror-dependentmultiple.js";
+ document._errorReported = "really shouldn't have run dependent module";
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.js
new file mode 100644
index 0000000000..71872c47cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-dependentmultiple.js
@@ -0,0 +1,2 @@
+// Parse error in a dependent module
+1A
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-root.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-root.html
new file mode 100644
index 0000000000..012f3e9b8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-parseerror-root.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<head>
+ <title>html-script-module-errorHandling-parseError-Root</title>
+ <script src="errorhandling-parseerror-common.js"></script>
+</head>
+<body>
+ <script type="module">
+
+ // Parse error in a root module
+ 1A
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype-import.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype-import.js
new file mode 100644
index 0000000000..286e1a4229
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype-import.js
@@ -0,0 +1,8 @@
+import foo from "./errorhandling-wrongMimetype.js?pipe=header(Content-Type,text/plain)";
+
+// We don't expect this code to run, the import above should fail!
+// If we do run though, don't trigger an error that the testharness
+// might misinterpret as the import itself failing to load.
+
+var A = null;
+export { A };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype.js
new file mode 100644
index 0000000000..373a532445
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling-wrongMimetype.js
@@ -0,0 +1,7 @@
+// This is a plain JavaScript file, but since it will only be accessed with
+// a Content-Type of text/plain and nosniff, it will be seen as invalid.
+// The file itself will have no errors/effects, so if it does actually run,
+// no error will be detected, and the test will fail.
+
+var foo = null;
+export foo; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling.html
new file mode 100644
index 0000000000..cf47465b49
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/errorhandling.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<html>
+<head>
+ <title>html-script-module-errorHandling</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+
+ iframe
+ { display: none; }
+
+ </style>
+</head>
+<body>
+ <h1>html-script-module-errorHandling</h1>
+ <iframe id="iframe_parseError_Root" src="errorhandling-parseerror-root.html"></iframe>
+ <iframe id="iframe_parseError_Dependent" src="errorhandling-parseerror-dependent.html"></iframe>
+ <iframe id="iframe_parseError_DependentMultiple" src="errorhandling-parseerror-dependentmultiple.html"></iframe>
+ <script>
+
+ var tests = [
+ { "id": "iframe_parseError_Root", "expected": "error" },
+ { "id": "iframe_parseError_Dependent", "expected": "error" },
+ { "id": "iframe_parseError_DependentMultiple", "expected": "error,error" },
+ ];
+ tests.forEach(function (testObj) {
+ var testHandle = async_test("IFrame test: '" + testObj.id + "'");
+ var testTarget = document.getElementById(testObj.id);
+ testTarget.addEventListener("load", testHandle.step_func(function () {
+ assert_equals(testTarget.contentDocument._errorReported, testObj.expected, "Unexpected _errorReported value");
+ testHandle.done();
+ }));
+ });
+
+ var test_wrongMimetype_root = async_test("External root module with non-script mimetype");
+ var script_wrongMimetype_root = document.createElement("script");
+ script_wrongMimetype_root.type = "module";
+ script_wrongMimetype_root.src = "errorhandling-wrongMimetype.js?pipe=header(Content-Type,text/plain)";
+ script_wrongMimetype_root.addEventListener("error", test_wrongMimetype_root.step_func(function () {
+ test_wrongMimetype_root.done();
+ }));
+ script_wrongMimetype_root.addEventListener("load", test_wrongMimetype_root.step_func(function () {
+ assert_unreached("This script should not have loaded!");
+ }));
+ document.body.appendChild(script_wrongMimetype_root);
+
+ var test_wrongMimetype_import = async_test("Module with imported non-script mimetype");
+ var script_wrongMimetype_import = document.createElement("script");
+ script_wrongMimetype_import.type = "module";
+ script_wrongMimetype_import.src = "errorhandling-wrongMimetype-import.js";
+ script_wrongMimetype_import.addEventListener("error", test_wrongMimetype_import.step_func(function () {
+ test_wrongMimetype_import.done();
+ }));
+ script_wrongMimetype_import.addEventListener("load", test_wrongMimetype_import.step_func(function () {
+ assert_unreached("This script should not have loaded!");
+ }));
+ document.body.appendChild(script_wrongMimetype_import);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-1.html
new file mode 100644
index 0000000000..3f2bb35f4e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Handling of evaluation errors, 1</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "Test that exceptions during evaluation lead to error events on " +
+ "window, and that exceptions are remembered.\n");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const exn = log[1];
+ assert_array_equals(log,
+ ["throw", exn, "load", exn, "load", exn, "load", exn, "load"]);
+ assert_true(exn.foo);
+ }));
+
+ function logLoad() { log.push("load") }
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="throw.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="throw.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="throw.js" async onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="throw.js" nomodule onerror="unreachable()"
+ onload="logLoad()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-2.html
new file mode 100644
index 0000000000..4f2b3c5a74
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Handling of evaluation errors, 2</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "Test that ill-founded cyclic dependencies cause ReferenceError " +
+ "during evaluation, which leads to error events on window, and that " +
+ "exceptions are remembered.\n");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const exn = log[1];
+ assert_array_equals(log,
+ ["cycle-tdz-access-a", exn, "load", exn, "load", exn, "load"]);
+ assert_equals(exn.constructor, ReferenceError);
+ }));
+
+ function logLoad() { log.push("load"); }
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="cycle-tdz-access.js" async onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="cycle-tdz-access.js" nomodule onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="cycle-tdz-access.js" onerror="unreachable()"
+ onload="logLoad()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-3.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-3.html
new file mode 100644
index 0000000000..9bfb5df2cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Handling of evaluation errors, 3</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "Test that exceptions during evaluation lead to error events on " +
+ "window, and that exceptions are remembered.\n");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const exn = log[1];
+ assert_array_equals(log,
+ ["throw", exn, "load", exn, "load", exn, "load", exn, "load",
+ exn, "load"]);
+ assert_true(exn.foo);
+ }));
+
+ function logLoad() { log.push("load"); }
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./throw.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw-nested.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw-nested.js" onerror="unreachable()"
+ onload="logLoad()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-4.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-4.html
new file mode 100644
index 0000000000..0b4b7d1662
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/evaluation-error-4.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Handling of evaluation errors, 4</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+ window.addEventListener("onunhandledrejection", unreachable);
+
+ const test_load = async_test(
+ "Test that exceptions during evaluation lead to error events on " +
+ "window, and that exceptions are remembered.\n");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const exn = log[1];
+ assert_array_equals(log,
+ ["throw", exn, "load", exn, "load", exn, "load", exn, "load",
+ exn, "load"]);
+ assert_true(exn.foo);
+ }));
+
+ function logLoad() { log.push("load"); }
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./throw-nested.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw-nested.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw-nested.js" onerror="unreachable()"
+ onload="logLoad()"></script>
+<script type="module" src="./throw.js" onerror="unreachable()"
+ onload="logLoad()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered2.js
new file mode 100644
index 0000000000..d7115a2ac6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered2.js
@@ -0,0 +1,3 @@
+test_dynamicOrdered.step(function() {
+ assert_execCount(1, 2, "External script element (#1) should have fired second");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered3.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered3.js
new file mode 100644
index 0000000000..c04e101bb8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered3.js
@@ -0,0 +1,3 @@
+test_dynamicOrdered.step(function() {
+ assert_execCount(1, 3, "External script element (#2) should have fired third");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered4.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered4.js
new file mode 100644
index 0000000000..958736a3d8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicordered4.js
@@ -0,0 +1,3 @@
+test_dynamicOrdered.step(function() {
+ assert_execCount(1, 4, "External script element (#3) should have fired fourth");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered1.js
new file mode 100644
index 0000000000..a54cb7a303
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered1.js
@@ -0,0 +1,3 @@
+test_dynamicUnordered1.step(function() {
+ test_dynamicUnordered1.done();
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered2.js
new file mode 100644
index 0000000000..df0cd85421
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-dynamicunordered2.js
@@ -0,0 +1,3 @@
+test_dynamicUnordered2.step(function() {
+ test_dynamicUnordered2.done();
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered2.js
new file mode 100644
index 0000000000..fca73bd9db
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered2.js
@@ -0,0 +1,3 @@
+test_parsedOrdered.step(function() {
+ assert_execCount(0, 2, "External deferred (#1) script element should have fired second");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered4.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered4.js
new file mode 100644
index 0000000000..6435333bb9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedordered4.js
@@ -0,0 +1,3 @@
+test_parsedOrdered.step(function() {
+ assert_execCount(0, 4, "External deferred (#2) script element should have fired fourth");
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered1.js
new file mode 100644
index 0000000000..ea0bb1b486
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered1.js
@@ -0,0 +1,3 @@
+test_parsedUnordered1.step(function() {
+ test_parsedUnordered1.done();
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered2.js
new file mode 100644
index 0000000000..ce219ee0ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder-parsedunordered2.js
@@ -0,0 +1,3 @@
+test_parsedUnordered2.step(function() {
+ test_parsedUnordered2.done();
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder.html
new file mode 100644
index 0000000000..6a7513dc13
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/execorder.html
@@ -0,0 +1,106 @@
+<!doctype html>
+<html>
+<head>
+ <title>html-script-module-execOrder</title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+
+ var execCounts = [
+ 0, // test_parsedOrdered
+ 0, // test_dynamicOrdered
+ ];
+ function assert_execCount(set, expected, description)
+ {
+ if (!execCounts[set])
+ {
+ execCounts[set] = 0;
+ }
+ assert_equals(++execCounts[set], expected, description);
+ }
+
+ function create_script(src, opts)
+ {
+ var element = document.createElement("script");
+ element.src = src;
+ element.async = (opts.asyncOrdered ? false : true);
+ element.type = (opts.module ? "module" : "text/javascript");
+ document.body.appendChild(element);
+ }
+
+ </script>
+</head>
+<body>
+ <h1>html-script-module-execOrder</h1>
+ <script>
+
+ /////
+ // Start test_parsedUnordered*
+ /////
+ var test_parsedUnordered1 = async_test("Unordered module script execution (parsed, unordered #1)");
+ var test_parsedUnordered2 = async_test("Unordered module script execution (parsed, unordered #2)");
+ </script>
+ <script type="module" src="execorder-parsedunordered1.js"></script>
+ <script type="module" src="execorder-parsedunordered2.js"></script>
+ <script>
+ /////
+ // End test_parsedUnordered*
+ /////
+
+ /////
+ // Start test_dynamicUnordered*
+ /////
+ var test_dynamicUnordered1 = async_test("Unordered module script execution (dynamic, unordered #1)");
+ var test_dynamicUnordered2 = async_test("Unordered module script execution (dynamic, unordered #2)");
+ create_script("execorder-dynamicunordered1.js", { module: true });
+ create_script("execorder-dynamicunordered2.js", { module: true });
+ /////
+ // End test_dynamicUnordered*
+ /////
+
+ /////
+ // Begin test_parsedOrdered
+ /////
+ var test_parsedOrdered = async_test("Interlaced module/non-module script execution (parsed, async-ordered)");
+ window.addEventListener("load", test_parsedOrdered.step_func(function() {
+ assert_execCount(0, 5, "onload should have fired fifth");
+ test_parsedOrdered.done();
+ }));
+ </script>
+ <script src="execorder-parsedordered2.js" defer></script>
+ <script type="module">
+ test_parsedOrdered.step(function() {
+ assert_execCount(0, 3, "Inline module-typed script element should have fired third");
+ });
+ </script>
+ <script src="execorder-parsedordered4.js" defer></script>
+ <script>
+ test_parsedOrdered.step(function() {
+ assert_execCount(0, 1, "Inline untyped script element should have fired first");
+ });
+ /////
+ // End test_parsedOrdered
+ /////
+
+ /////
+ // Start test_dynamicOrdered
+ /////
+ var test_dynamicOrdered = async_test("Interlaced module/non-module script execution (dynamic, async-ordered)");
+ window.addEventListener("load", test_dynamicOrdered.step_func(function() {
+ assert_execCount(1, 5, "onload should have fired fifth (last)");
+ test_dynamicOrdered.done();
+ }));
+ create_script("execorder-dynamicordered2.js", { asyncOrdered: true, module: false });
+ create_script("execorder-dynamicordered3.js", { asyncOrdered: true, module: true });
+ create_script("execorder-dynamicordered4.js", { asyncOrdered: true, module: false });
+ test_dynamicOrdered.step(function() {
+ assert_execCount(1, 1, "Inline untyped script element should have fired first");
+ });
+ /////
+ // End test_dynamicOrdered
+ /////
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-default.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-default.js
new file mode 100644
index 0000000000..283830ab28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-default.js
@@ -0,0 +1,2 @@
+log.push("export-default");
+export default "fox";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something-nested.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something-nested.js
new file mode 100644
index 0000000000..ca806eb8b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something-nested.js
@@ -0,0 +1,2 @@
+log.push("export-something-nested");
+export * from "./export-something.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something.js
new file mode 100644
index 0000000000..cf2c3a99fe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/export-something.js
@@ -0,0 +1,3 @@
+log.push("export-something");
+export let foo = 42;
+export function set_foo(x) { foo = x };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-1.html
new file mode 100644
index 0000000000..170bb665ff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Handling of fetch errors, 1</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that failure to fetch root leads to error event on script.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["script"]);
+ }));
+</script>
+<script type="module" src="./no-such-file.js" onerror="log.push('script')"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.html
new file mode 100644
index 0000000000..9386ce603a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Handling of fetch errors, 2</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that failure to fetch dependency leads to error event on script.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["script"]);
+ }));
+</script>
+<script type="module" src="./fetch-error-2.js" onerror="log.push('script')"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.js
new file mode 100644
index 0000000000..20c0ea6402
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/fetch-error-2.js
@@ -0,0 +1,2 @@
+import "./no-such-file.js"
+import "./this.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-dependent.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-dependent.js
new file mode 100644
index 0000000000..cfaeabc47e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-dependent.js
@@ -0,0 +1 @@
+export let importMetaOnDependentModule = import.meta;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-object.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-object.any.js
new file mode 100644
index 0000000000..494e168102
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-object.any.js
@@ -0,0 +1,20 @@
+// META: global=dedicatedworker-module,sharedworker-module,serviceworker-module
+
+test(() => {
+ assert_equals(typeof import.meta, "object");
+ assert_not_equals(import.meta, null);
+}, "import.meta is an object");
+
+test(() => {
+ import.meta.newProperty = 1;
+ assert_true(Object.isExtensible(import.meta));
+}, "import.meta is extensible");
+
+test(() => {
+ for (const name of Reflect.ownKeys(import.meta)) {
+ const desc = Object.getOwnPropertyDescriptor(import.meta, name);
+ assert_equals(desc.writable, true);
+ assert_equals(desc.enumerable, true);
+ assert_equals(desc.configurable, true);
+ }
+}, "import.meta's properties are writable, configurable, and enumerable");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-importmap.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-importmap.html
new file mode 100644
index 0000000000..214b9bb59c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-importmap.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+ More extensive tests of import maps and import.meta.resolve() will be
+ located in the import maps test suite. This contains some basic tests plus
+ tests some tricky parts of the import.meta.resolve() algorithm around string
+ conversion which are only testable with import maps.
+-->
+
+<script type="importmap">
+{
+ "imports": {
+ "bare": "https://example.com/",
+ "https://example.com/rewrite": "https://example.com/rewritten",
+
+ "1": "https://example.com/PASS-1",
+ "null": "https://example.com/PASS-null",
+ "undefined": "https://example.com/PASS-undefined",
+ "[object Object]": "https://example.com/PASS-object",
+
+ "./start": "./resources/export-1.mjs",
+ "./resources/export-1.mjs": "./resources/export-2.mjs"
+ }
+}
+</script>
+
+<script type="module">
+test(() => {
+ assert_equals(import.meta.resolve("bare"), "https://example.com/");
+}, "import.meta.resolve() given an import mapped bare specifier");
+
+test(() => {
+ assert_equals(import.meta.resolve("https://example.com/rewrite"), "https://example.com/rewritten");
+}, "import.meta.resolve() given an import mapped URL-like specifier");
+
+test(() => {
+ assert_equals(import.meta.resolve(), "https://example.com/PASS-undefined", "no-arg case");
+
+ assert_equals(import.meta.resolve(1), "https://example.com/PASS-1");
+ assert_equals(import.meta.resolve(null), "https://example.com/PASS-null");
+ assert_equals(import.meta.resolve(undefined), "https://example.com/PASS-undefined");
+
+ // Only toString() methods are consulted by ToString, not valueOf() ones.
+ // So this becomes "[object Object]".
+ assert_equals(import.meta.resolve({ valueOf() { return "./x"; } }), "https://example.com/PASS-object");
+}, "Testing the ToString() step of import.meta.resolve() via import maps");
+
+promise_test(async () => {
+ const one = (await import("./start")).default;
+ assert_equals(one, 1);
+
+ const two = (await import(import.meta.resolve("./start"))).default;
+ assert_equals(two, 2);
+}, "import(import.meta.resolve(x)) can be different from import(x)");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-multiple-scripts.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-multiple-scripts.html
new file mode 100644
index 0000000000..d2e0f185e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve-multiple-scripts.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="resources/store-import-meta.html"></iframe>
+
+<script type="module">
+import * as otherImportMeta from "./resources/export-import-meta.mjs";
+setup({ explicit_done: true });
+
+window.onload = () => {
+ test(() => {
+ assert_not_equals(frames[0].importMetaURL, import.meta.url,
+ "Precondition check: we've set things up so that the other script has a different import.meta.url");
+
+ const expected = (new URL("resources/x", location.href)).href;
+ assert_equals(frames[0].importMetaResolve("./x"), expected);
+ }, "import.meta.resolve resolves URLs relative to the import.meta.url, not relative to the active script when it is called: another global's inline script");
+
+ test(() => {
+ const otherFrameImportMetaResolve = frames[0].importMetaResolve;
+
+ document.querySelector("iframe").remove();
+
+ const expected = (new URL("resources/x", location.href)).href;
+ assert_equals(otherFrameImportMetaResolve("./x"), expected);
+ }, "import.meta.resolve still works if its global has been destroyed (by detaching the iframe)");
+
+ test(() => {
+ assert_not_equals(otherImportMeta.url, import.meta.url,
+ "Precondition check: we've set things up so that the other script has a different import.meta.url");
+
+ const expected = (new URL("resources/x", location.href)).href;
+ assert_equals(otherImportMeta.resolve("./x"), expected);
+ }, "import.meta.resolve resolves URLs relative to the import.meta.url, not relative to the active script when it is called: another module script");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve.any.js
new file mode 100644
index 0000000000..5b8a84efaf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-resolve.any.js
@@ -0,0 +1,77 @@
+// META: global=dedicatedworker-module,sharedworker-module,serviceworker-module
+
+import { importMetaOnRootModule, importMetaOnDependentModule }
+ from "./import-meta-root.js";
+
+test(() => {
+ assert_equals(typeof import.meta.resolve, "function");
+ assert_equals(import.meta.resolve.name, "resolve");
+ assert_equals(import.meta.resolve.length, 1);
+ assert_equals(Object.getPrototypeOf(import.meta.resolve), Function.prototype);
+}, "import.meta.resolve is a function with the right properties");
+
+test(() => {
+ assert_false(isConstructor(import.meta.resolve));
+
+ assert_throws_js(TypeError, () => new import.meta.resolve("./x"));
+}, "import.meta.resolve is not a constructor");
+
+test(() => {
+ // See also tests in ./import-meta-resolve-importmap.html.
+
+ assert_equals(import.meta.resolve({ toString() { return "./x"; } }), resolveURL("x"));
+ assert_throws_js(TypeError, () => import.meta.resolve(Symbol("./x")),
+ "symbol");
+ assert_throws_js(TypeError, () => import.meta.resolve(),
+ "no argument (which is treated like \"undefined\")");
+}, "import.meta.resolve ToString()s its argument");
+
+test(() => {
+ assert_equals(import.meta.resolve("./x"), resolveURL("x"),
+ "current module import.meta");
+ assert_equals(importMetaOnRootModule.resolve("./x"), resolveURL("x"),
+ "sibling module import.meta");
+ assert_equals(importMetaOnDependentModule.resolve("./x"), resolveURL("x"),
+ "dependency module import.meta");
+}, "Relative URL-like specifier resolution");
+
+test(() => {
+ assert_equals(import.meta.resolve("https://example.com/"), "https://example.com/",
+ "current module import.meta");
+ assert_equals(importMetaOnRootModule.resolve("https://example.com/"), "https://example.com/",
+ "sibling module import.meta");
+ assert_equals(importMetaOnDependentModule.resolve("https://example.com/"), "https://example.com/",
+ "dependency module import.meta");
+}, "Absolute URL-like specifier resolution");
+
+test(() => {
+ const invalidSpecifiers = [
+ "https://eggplant:b/c",
+ "pumpkins.js",
+ ".tomato",
+ "..zuccini.mjs",
+ ".\\yam.es"
+ ];
+
+ for (const specifier of invalidSpecifiers) {
+ assert_throws_js(TypeError, () => import.meta.resolve(specifier), specifier);
+ }
+}, "Invalid module specifiers");
+
+test(() => {
+ const { resolve } = import.meta;
+ assert_equals(resolve("https://example.com/"), "https://example.com/", "current module import.meta");
+}, "Works fine with no this value");
+
+function resolveURL(urlRelativeToThisTest) {
+ return (new URL(urlRelativeToThisTest, location.href)).href;
+}
+
+function isConstructor(o) {
+ try {
+ new (new Proxy(o, { construct: () => ({}) }));
+ return true;
+ } catch {
+ return false;
+ }
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-root.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-root.js
new file mode 100644
index 0000000000..62ec082a8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-root.js
@@ -0,0 +1,2 @@
+export let importMetaOnRootModule = import.meta;
+export { importMetaOnDependentModule } from "./import-meta-dependent.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.any.js
new file mode 100644
index 0000000000..61d96f35af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.any.js
@@ -0,0 +1,38 @@
+// META: global=dedicatedworker-module,sharedworker-module,serviceworker-module
+
+import { importMetaOnRootModule, importMetaOnDependentModule }
+ from "./import-meta-root.js";
+
+const base = location.href.slice(0, location.href.lastIndexOf('/'));
+
+test(() => {
+ assert_equals(importMetaOnRootModule.url,
+ base + "/import-meta-root.js");
+}, "import.meta.url in a root external script");
+
+test(() => {
+ assert_equals(importMetaOnDependentModule.url,
+ base + "/import-meta-dependent.js");
+}, "import.meta.url in a dependent external script");
+
+
+import { importMetaOnRootModule as hashedImportMetaOnRootModule1,
+ importMetaOnDependentModule as hashedImportMetaOnDependentModule1 }
+ from "./import-meta-root.js#1";
+
+import { importMetaOnRootModule as hashedImportMetaOnRootModule2,
+ importMetaOnDependentModule as hashedImportMetaOnDependentModule2 }
+ from "./import-meta-root.js#2";
+
+test(() => {
+ assert_equals(hashedImportMetaOnRootModule1.url,
+ base + "/import-meta-root.js#1");
+ assert_equals(hashedImportMetaOnRootModule2.url,
+ base + "/import-meta-root.js#2");
+
+ // Must not be affected
+ assert_equals(hashedImportMetaOnDependentModule1.url,
+ base + "/import-meta-dependent.js");
+ assert_equals(hashedImportMetaOnDependentModule2.url,
+ base + "/import-meta-dependent.js");
+}, "import.meta.url when importing the module with different fragments");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.html
new file mode 100644
index 0000000000..284a15f2b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module" src="import-meta-url.any.js"></script>
+
+<script type="module">
+const base = location.href.slice(0, location.href.lastIndexOf('/'));
+
+test(() => {
+ assert_equals(import.meta.url, location.href);
+}, "import.meta.url in a root inline script");
+
+for (const workerType of ['DedicatedWorker', 'SharedWorker']) {
+ promise_test(async t => {
+ const worker_request_url =
+ new URL(`postmessage-worker.js?${workerType}`, location);
+ let w;
+ let port;
+ if (workerType === 'DedicatedWorker') {
+ w = new Worker(worker_request_url.href, {type: 'module'});
+ port = w;
+ } else {
+ w = new SharedWorker(worker_request_url.href, {type: 'module'});
+ port = w.port;
+ w.port.start();
+ }
+ w.onerror = t.unreached_func('Worker error');
+ const url = await new Promise(resolve => {
+ port.onmessage = evt => resolve(evt.data);
+ });
+ assert_equals(url, worker_request_url.href);
+ }, `import.meta.url at top-level module ${workerType}`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/postmessage-worker.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/postmessage-worker.js
new file mode 100644
index 0000000000..3618137aef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/postmessage-worker.js
@@ -0,0 +1,12 @@
+if ('DedicatedWorkerGlobalScope' in self &&
+ self instanceof DedicatedWorkerGlobalScope) {
+ postMessage(import.meta.url);
+} else if (
+ 'SharedWorkerGlobalScope' in self &&
+ self instanceof SharedWorkerGlobalScope) {
+ self.onconnect = function(e) {
+ const port = e.ports[0];
+ port.start();
+ port.postMessage(import.meta.url);
+ };
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-1.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-1.mjs
new file mode 100644
index 0000000000..aef22247d7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-1.mjs
@@ -0,0 +1 @@
+export default 1;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-2.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-2.mjs
new file mode 100644
index 0000000000..842e368a0a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-2.mjs
@@ -0,0 +1 @@
+export default 2;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-import-meta.mjs b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-import-meta.mjs
new file mode 100644
index 0000000000..488ca74c93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/export-import-meta.mjs
@@ -0,0 +1,2 @@
+export const url = import.meta.url;
+export const resolve = import.meta.resolve;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/store-import-meta.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/store-import-meta.html
new file mode 100644
index 0000000000..c9751da408
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-meta/resources/store-import-meta.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script type="module">
+window.importMetaURL = import.meta.url;
+window.importMetaResolve = import.meta.resolve;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-something-namespace.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-something-namespace.js
new file mode 100644
index 0000000000..32d90287d7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-something-namespace.js
@@ -0,0 +1,5 @@
+log.push("import-something-namespace");
+log.push(m.foo);
+m.set_foo(43);
+log.push(m.foo);
+import * as m from "./export-something.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-subgraph-404.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-subgraph-404.html
new file mode 100644
index 0000000000..4911a071a0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/import-subgraph-404.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module">
+import { delayedLoaded } from "./resources/delayed-modulescript.py";
+import { A } from "./404.js";
+window.loadSuccess = delayedLoaded;
+</script>
+<script type="module">
+test(function () {
+ assert_equals(window.loadSuccess, undefined,
+ "module tree w/ its sub graph 404 should fail to load without crashing");
+}, "Import a module graph w/ sub-graph 404.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-a.js
new file mode 100644
index 0000000000..44d1ac96c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-a.js
@@ -0,0 +1,3 @@
+var A = { "from": "imports-a.js" };
+window.evaluated_imports_a = true;
+export { A };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-b.js
new file mode 100644
index 0000000000..ae194e4d8a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-b.js
@@ -0,0 +1 @@
+export var B = { "from": "imports-b.js" };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-a.js
new file mode 100644
index 0000000000..8bd8526f76
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-a.js
@@ -0,0 +1,2 @@
+import { CycleB } from "./imports-cycle-b.js";
+export var CycleA = "CycleA";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-b.js
new file mode 100644
index 0000000000..218f350c39
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle-b.js
@@ -0,0 +1,2 @@
+import { CycleA } from "./imports-cycle-a.js";
+export var CycleB = "CycleB";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle.js
new file mode 100644
index 0000000000..88a77a4d67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-cycle.js
@@ -0,0 +1,6 @@
+import { CycleA } from "./imports-cycle-a.js";
+
+test_importCycle.step(function () {
+ assert_equals(CycleA, "CycleA");
+ test_importCycle.done();
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-a.js
new file mode 100644
index 0000000000..8cb2298e8a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-a.js
@@ -0,0 +1,2 @@
+import { A } from "./imports-a.js";
+export { A as INC_A };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-ab.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-ab.js
new file mode 100644
index 0000000000..b7d84005c5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-ab.js
@@ -0,0 +1,5 @@
+import { A } from "./imports-a.js";
+export { A as INC_AB_A };
+
+import { B } from "./imports-b.js";
+export { B as INC_AB_B };
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-b.js
new file mode 100644
index 0000000000..243b84fdd0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-inc-b.js
@@ -0,0 +1,2 @@
+import { B } from "./imports-b.js";
+export { B as INC_B }; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self-inner.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self-inner.js
new file mode 100644
index 0000000000..40eca1c8df
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self-inner.js
@@ -0,0 +1,2 @@
+import { SelfInner as SelfInnerA } from "./imports-self-inner.js";
+export var SelfInner = "SelfInner";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self.js
new file mode 100644
index 0000000000..05fa60e2dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports-self.js
@@ -0,0 +1,6 @@
+import { SelfInner } from "./imports-self-inner.js";
+
+test_importSelf.step(function () {
+ assert_equals(SelfInner, "SelfInner");
+ test_importSelf.done();
+});
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports.html
new file mode 100644
index 0000000000..ca6900744d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/imports.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>html-script-module-imports</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1>html-script-module-imports</h1>
+
+ <script type="module">
+
+ import { A } from "./imports-a.js";
+
+ test(function () {
+ assert_equals(A.from, "imports-a.js", "Unexpected A");
+ }, "Import a simple module");
+
+ </script>
+ <script type="module">
+
+ import { B as B_RENAMED } from "./imports-b.js";
+
+ test(function () {
+ assert_equals(B_RENAMED.from, "imports-b.js", "Unexpected B_RENAMED");
+
+ try
+ {
+ B;
+ assert_unreached("Unexpectedly defined B");
+ }
+ catch (ex)
+ {}
+ }, "Import a simple module, renamed");
+
+ </script>
+ <script type="module">
+
+ import { INC_A } from "./imports-inc-a.js";
+ import { INC_B } from "./imports-inc-b.js";
+ import { INC_AB_A, INC_AB_B } from "./imports-inc-ab.js";
+
+ test(function () {
+ assert_equals(INC_A.from, "imports-a.js", "Unexpected INC_A");
+ assert_equals(INC_B.from, "imports-b.js", "Unexpected INC_A");
+ assert_equals(INC_AB_A.from, "imports-a.js", "Unexpected INC_A");
+ assert_equals(INC_AB_B.from, "imports-b.js", "Unexpected INC_A");
+ assert_equals(INC_A, INC_AB_A, "INC_A and INC_AB_A should be the same");
+ assert_equals(INC_B, INC_AB_B, "INC_B and INC_AB_B should be the same");
+ }, "Import the same module multiple times");
+
+ </script>
+
+ <script>
+ var test_importSelf = async_test("Import a module that validly imports itself");
+ </script>
+ <script type="module" src="imports-self.js"></script>
+
+ <script>
+ var test_importCycle = async_test("Import a module with a valid cyclical module dependency");
+ </script>
+ <script type="module" src="imports-cycle.js"></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inactive-context-import.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inactive-context-import.html
new file mode 100644
index 0000000000..ce88c0a152
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inactive-context-import.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Dynamic import triggered from inactive context should not crash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="container">
+<iframe></iframe>
+</div>
+
+<script>
+test(() => {
+ const iframe = document.querySelector('iframe');
+ const otherWindow = iframe.contentWindow;
+ iframe.remove();
+
+ // Below should not crash
+ otherWindow.eval(`import('foobar');`);
+}, 'dynamic import from inactive context should not crash');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-execorder.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-execorder.html
new file mode 100644
index 0000000000..db03612e82
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-execorder.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <title>Inline async module script execution order</title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ let loaded = [];
+ let test = async_test("Inline async module script execution order");
+ window.addEventListener("load", test.step_func(function() {
+ assert_array_equals(loaded,
+ ["fast", "fast", "fast", "slow", "slow", "slow"]);
+ test.done();
+ }));
+ </script>
+ <script type="module" async src="resources/slow-module.js?pipe=trickle(d2)&unique=1"></script>
+ <script type="module" async>
+ import "./resources/slow-module.js?pipe=trickle(d2)&unique=2";
+ loaded.push("slow");
+ </script>
+ <script type="module" async src="resources/fast-module.js?unique=1"></script>
+ <script type="module" async>
+ import "./resources/fast-module.js?unique=2";
+ loaded.push("fast");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-onload.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-onload.html
new file mode 100644
index 0000000000..314abc98dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-async-onload.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <title>Inline async module script without external deps onload blocking</title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ let loadFired = false;
+ let moduleRan = false
+ let test = async_test("Inline async module script vs. onload");
+ window.addEventListener("load", test.step_func(function() {
+ loadFired = true;
+ assert_true(moduleRan, "Module should have run before the load event");
+ test.step_timeout(function() {
+ test.done();
+ }, 0);
+ }));
+ </script>
+ <script type="module" async>
+ moduleRan = true;
+ test.step_func(function() {
+ assert_false(loadFired, "onload should not have fired yet");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-defer-onload.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-defer-onload.html
new file mode 100644
index 0000000000..0a0bc36f8b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-defer-onload.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <title>Inline defer module script without external deps onload blocking</title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ let loadFired = false;
+ let moduleRan = false
+ let test = async_test("Inline defer module script vs. onload");
+ window.addEventListener("load", test.step_func(function() {
+ loadFired = true;
+ assert_true(moduleRan, "Module should have run before the load event");
+ test.step_timeout(function() {
+ test.done();
+ }, 0);
+ }));
+ </script>
+ <!-- defer should be equivalent to neither defer nor async specified -->
+ <script type="module" defer>
+ moduleRan = true;
+ test.step_func(function() {
+ assert_false(loadFired, "onload should not have fired yet");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-no-async-onload.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-no-async-onload.html
new file mode 100644
index 0000000000..50f42c6a38
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/inline-no-async-onload.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <title>Inline module script without external deps onload blocking</title>
+ <meta name=timeout content=long>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ let loadFired = false;
+ let moduleRan = false
+ let test = async_test("Inline module script vs. onload");
+ window.addEventListener("load", test.step_func(function() {
+ loadFired = true;
+ assert_true(moduleRan, "Module should have run before the load event");
+ test.step_timeout(function() {
+ test.done();
+ }, 0);
+ }));
+ </script>
+ <script type="module">
+ moduleRan = true;
+ test.step_func(function() {
+ assert_false(loadFired, "onload should not have fired yet");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html
new file mode 100644
index 0000000000..57b40f5baa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 1</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Test that missing exports lead to SyntaxError events on window and " +
+ "load events on script");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, SyntaxError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const msg = log[0];
+ assert_array_equals(log, [msg, 1, msg, 2, msg, 3, msg, 4, msg, 5]);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./missing-export.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./missing-export.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
+<script type="module" src="./missing-export-nested.js"
+ onerror="unreachable()" onload="log.push(3)"></script>
+<script type="module" src="./missing-export.js"
+ onerror="unreachable()" onload="log.push(4)"></script>
+<script type="module" src="./missing-export-nested.js"
+ onerror="unreachable()" onload="log.push(5)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.js
new file mode 100644
index 0000000000..e317b01cc2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-1.js
@@ -0,0 +1 @@
+import something from "./instantiation-error-1.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html
new file mode 100644
index 0000000000..27ba006fc7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 2</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Test that missing exports lead to SyntaxError events on window and " +
+ "load events on script");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, SyntaxError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const msg = log[0];
+ assert_array_equals(log, [msg, 1, msg, 2, msg, 3, msg, 4, msg, 5]);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./missing-export-nested.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./missing-export-nested.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
+<script type="module" src="./missing-export.js"
+ onerror="unreachable()" onload="log.push(3)"></script>
+<script type="module" src="./missing-export-nested.js"
+ onerror="unreachable()" onload="log.push(4)"></script>
+<script type="module" src="./missing-export.js"
+ onerror="unreachable()" onload="log.push(5)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html
new file mode 100644
index 0000000000..32f0a4a243
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 3</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Test that unresolvable cycles lead to SyntaxError events on window " +
+ "and load events on script");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, SyntaxError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 6, 'Log length');
+ assert_equals(log[1], 1);
+ assert_equals(log[3], 2);
+ assert_equals(log[5], 3);
+ assert_not_equals(log[0], log[2],
+ 'Instantiation error objects for different root scripts');
+ assert_equals(log[0], log[4],
+ 'Instantiation error objects for the same root script');
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./cycle-unresolvable.js"
+ onerror="unreachable()" onload="log.push(1)" nomodule></script>
+<script type="module" src="./cycle-unresolvable-a.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
+<script type="module" src="./cycle-unresolvable.js"
+ onerror="unreachable()" onload="log.push(3)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html
new file mode 100644
index 0000000000..8d3f23819a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 4</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+ const test_load = async_test(
+ "Test that loading a graph in which a module is already " +
+ "errored results in an error.");
+
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, SyntaxError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 4, 'Log length');
+ assert_equals(log[1], 1);
+ assert_equals(log[3], 2);
+ assert_not_equals(log[0], log[2],
+ 'Instantiation error objects for different root scripts');
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./instantiation-error-4a.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./instantiation-error-4d.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4a.js
new file mode 100644
index 0000000000..6fed27f1c7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4a.js
@@ -0,0 +1,2 @@
+import "./instantiation-error-4b.js";
+log.push("instantiation-error-4a");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4b.js
new file mode 100644
index 0000000000..4b702cae67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4b.js
@@ -0,0 +1,3 @@
+import "./instantiation-error-4c.js";
+import "./instantiation-error-4d.js";
+log.push("instantiation-error-4b");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4c.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4c.js
new file mode 100644
index 0000000000..ef699f6ca3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4c.js
@@ -0,0 +1,2 @@
+import {something} from "./instantiation-error-4c.js";
+log.push("instantiation-error-4c");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4d.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4d.js
new file mode 100644
index 0000000000..ac04ccb9b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-4d.js
@@ -0,0 +1,2 @@
+import {something} from "./instantiation-error-4d.js";
+log.push("instantiation-error-4d");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html
new file mode 100644
index 0000000000..20df0bb5c9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 5</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Test that loading a graph in which a module is already " +
+ "errored results an error.");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, SyntaxError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 4, 'Log length');
+ assert_equals(log[1], 1);
+ assert_equals(log[3], 2);
+ assert_not_equals(log[0], log[2],
+ 'Instantiation error objects for different root scripts');
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./instantiation-error-5a.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./instantiation-error-5d.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5a.js
new file mode 100644
index 0000000000..b2e6b106b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5a.js
@@ -0,0 +1,2 @@
+import "./instantiation-error-5b.js";
+log.push("instantiation-error-5a");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5b.js
new file mode 100644
index 0000000000..2d37ae8fff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5b.js
@@ -0,0 +1,3 @@
+import "./instantiation-error-5c.js";
+import "./instantiation-error-5d.js";
+log.push("instantiation-error-5b");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5c.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5c.js
new file mode 100644
index 0000000000..ba221b6fc0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5c.js
@@ -0,0 +1,2 @@
+import {something} from "./instantiation-error-5c.js";
+log.push("instantiation-error-5c");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5d.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5d.js
new file mode 100644
index 0000000000..9775e04f82
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5d.js
@@ -0,0 +1,3 @@
+import "./instantiation-error-5e.js";
+import "./instantiation-error-5a.js";
+log.push("instantiation-error-5d");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5e.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5e.js
new file mode 100644
index 0000000000..8bd3b3c3bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-5e.js
@@ -0,0 +1,2 @@
+import {something} from "./instantiation-error-5e.js";
+log.push("instantiation-error-5e");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6.html
new file mode 100644
index 0000000000..8d3ce121ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 6</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that ambiguous star exports lead to an instantiation error " +
+ "and that the correct module is blamed.");
+ // Concretely, instantiation-error-6a.js fails to instantiate because it
+ // requests a name from instantion-error-6b.js that is ambiguous there.
+ // instantiation-error-6b.js itself, however, is fine, and it instantiates
+ // and evaluates successfully.
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const exn = log[0];
+ assert_array_equals(log, [
+ exn, 1,
+ "instantiation-error-6c",
+ "instantiation-error-6d",
+ "instantiation-error-6b", 2
+ ]);
+ assert_equals(exn.constructor, SyntaxError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./instantiation-error-6a.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./instantiation-error-6b.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6a.js
new file mode 100644
index 0000000000..4db49c6c46
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6a.js
@@ -0,0 +1,2 @@
+import {foo} from "./instantiation-error-6b.js";
+log.push("instantiation-error-6a");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6b.js
new file mode 100644
index 0000000000..35272fe550
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6b.js
@@ -0,0 +1,3 @@
+export * from "./instantiation-error-6c.js";
+export * from "./instantiation-error-6d.js";
+log.push("instantiation-error-6b");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6c.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6c.js
new file mode 100644
index 0000000000..69d616b4ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6c.js
@@ -0,0 +1,2 @@
+export let foo = "c";
+log.push("instantiation-error-6c");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6d.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6d.js
new file mode 100644
index 0000000000..d1336a57a2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-6d.js
@@ -0,0 +1,2 @@
+export let foo = "d";
+log.push("instantiation-error-6d");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7.html
new file mode 100644
index 0000000000..57f1f87216
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 7</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that ambiguous star exports lead to an instantiation error, " +
+ "even when discovered through a star export, and that the correct " +
+ "module is blamed.");
+ // This is a variation of instantiation-error-6.html (see the explanation
+ // there).
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ const exn = log[0];
+ assert_array_equals(log, [
+ exn, 1,
+ "instantiation-error-7d",
+ "instantiation-error-7e",
+ "instantiation-error-7c",
+ "instantiation-error-7f",
+ "instantiation-error-7b", 2
+ ]);
+ assert_equals(exn.constructor, SyntaxError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./instantiation-error-7a.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./instantiation-error-7b.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7a.js
new file mode 100644
index 0000000000..d27a44865c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7a.js
@@ -0,0 +1,2 @@
+import {foo} from "./instantiation-error-7b.js";
+log.push("instantiation-error-7a");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7b.js
new file mode 100644
index 0000000000..8c05d3b727
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7b.js
@@ -0,0 +1,3 @@
+export * from "./instantiation-error-7c.js";
+export * from "./instantiation-error-7f.js";
+log.push("instantiation-error-7b");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7c.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7c.js
new file mode 100644
index 0000000000..fff1368034
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7c.js
@@ -0,0 +1,3 @@
+export * from "./instantiation-error-7d.js";
+export * from "./instantiation-error-7e.js";
+log.push("instantiation-error-7c");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7d.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7d.js
new file mode 100644
index 0000000000..fa5e7651f4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7d.js
@@ -0,0 +1,2 @@
+export let foo = "d";
+log.push("instantiation-error-7d");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7e.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7e.js
new file mode 100644
index 0000000000..6547c3fe6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7e.js
@@ -0,0 +1,2 @@
+export let foo = "e";
+log.push("instantiation-error-7e");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7f.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7f.js
new file mode 100644
index 0000000000..7f9ec5d12e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-7f.js
@@ -0,0 +1,2 @@
+export let foo = "f";
+log.push("instantiation-error-7f");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-8.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-8.html
new file mode 100644
index 0000000000..080b170233
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/instantiation-error-8.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Handling of instantiation errors, 8</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- The below module tree should fail to instantiate, since it references undefined identifier. -->
+<script type="module" src="instantiation-error-1.js"></script>
+<script>
+setup({allow_uncaught_exception: true});
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.addEventListener("error", e => {
+ assert_equals(e.error.constructor, SyntaxError);
+ resolve();
+ }, { once: true });
+ }).then(() => new Promise(resolve => {
+ window.addEventListener("error", e => {
+ assert_equals(e.error.constructor, SyntaxError);
+ resolve();
+ }, { once: true });
+ // Load another module tree w/ previously instantiate-failed tree as its sub-tree.
+ document.head.appendChild(Object.assign(
+ document.createElement('script'),
+ { type: 'module', innerText: 'import "./instantiation-error-1.js"'}));
+ }));
+}, "Instantiate attempt on a tree w/ previously instantiate-failed tree as a sub-tree shouldn't crash.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches-inner.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches-inner.js
new file mode 100644
index 0000000000..369b3e7827
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches-inner.js
@@ -0,0 +1 @@
+window.matchesLog.push("integrity-matches-inner");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches.js
new file mode 100644
index 0000000000..d8c4219e90
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-matches.js
@@ -0,0 +1,2 @@
+import "./integrity-matches-inner.js";
+window.matchesLog.push("integrity-matches");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches-inner.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches-inner.js
new file mode 100644
index 0000000000..8182d4de16
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches-inner.js
@@ -0,0 +1 @@
+window.mismatchesLog.push("integrity-mismatches-inner");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches.js
new file mode 100644
index 0000000000..2d47344a5a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity-mismatches.js
@@ -0,0 +1,2 @@
+import "./integrity-mismatches-inner.js";
+window.mismatchesLog.push("integrity-mismatches");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity.html
new file mode 100644
index 0000000000..c79843624f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/integrity.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;script> integrity=""</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+window.inlineRan = false;
+
+window.matchesLog = [];
+window.matchesEvents = [];
+
+window.mismatchesLog = [];
+window.mismatchesEvents = [];
+</script>
+
+<script type="module" integrity="sha384-garbage">
+window.inlineRan = true;
+</script>
+
+<script type="module" src="integrity-matches.js" integrity="sha384-1/XwTy38IAlmvk1O674Efus1/REqfuX6x0V/B2/GX5R3lNbRjhrIwlWyEDPyOwpN" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script>
+<script type="module" src="integrity-mismatches.js" integrity="sha384-doesnotmatch" onload="window.mismatchesEvents.push('load');" onerror="window.mismatchesEvents.push('error')"></script>
+
+<script type="module">
+test(() => {
+ assert_true(window.inlineRan);
+}, "The integrity attribute must have no affect on inline module scripts");
+
+test(() => {
+ assert_array_equals(window.matchesLog, ["integrity-matches-inner", "integrity-matches"], "The module and its dependency must have executed");
+ assert_array_equals(window.matchesEvents, ["load"], "The load event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module and allow it to execute when it matches");
+
+test(() => {
+ assert_array_equals(window.mismatchesLog, [], "The module and its dependency must not have executed");
+ assert_array_equals(window.mismatchesEvents, ["error"], "The error event must have fired");
+}, "The integrity attribute must be verified on the top-level of a module and not allow it to execute when there's a mismatch");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-namespace-request.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-namespace-request.html
new file mode 100644
index 0000000000..00269efdf9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-namespace-request.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Late namespace request</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test the situation where a module is instantiated without the " +
+ "need for a namespace object, but later on a different module " +
+ "requests the namespace.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log,
+ ["export-something",
+ "import-something-namespace", 42, 43]);
+ }));
+</script>
+<script type="module" src="export-something.js"></script>
+<script type="module" src="import-something-namespace.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-star-export-request.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-star-export-request.html
new file mode 100644
index 0000000000..d40bb0aca7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/late-star-export-request.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Late star-export request</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test the situation where a module is instantiated without a use of " +
+ "its star-exports, but later on a different module requests them.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, [
+ "export-something", "export-something-nested",
+ "import-something-namespace", 42, 43]);
+ }));
+</script>
+<script type="module" src="export-something-nested.js"></script>
+<script type="module">
+ log.push("import-something-namespace");
+ log.push(foo);
+ set_foo(43);
+ log.push(foo);
+ import {foo, set_foo} from "./export-something-nested.js";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events-inline.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events-inline.html
new file mode 100644
index 0000000000..58397dd07d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events-inline.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for inline module scripts</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+"use strict";
+
+var test1_load = event_test('src, 200, parser-inserted, defer, no async', false, false);
+var test4_load = event_test('src, 200, parser-inserted, no defer, async', false, false);
+
+var test3_dynamic_load = event_test('src, 200, not parser-inserted, no defer, no async, no non-blocking', false, false);
+var test4_dynamic_load = event_test('src, 200, not parser-inserted, no defer, async', false, false);
+
+var test1_error = event_test('src, 404, parser-inserted, defer, no async', false, true);
+var test4_error = event_test('src, 404, parser-inserted, no defer, async', false, true);
+
+var test3_dynamic_error = event_test('src, 404, not parser-inserted, no defer, no async, no non-blocking', false, true);
+var test4_dynamic_error = event_test('src, 404, not parser-inserted, no defer, async', false, true);
+
+var script3_dynamic_load = document.createElement('script');
+script3_dynamic_load.setAttribute('type', 'module');
+script3_dynamic_load.onload = () => onLoad(test3_dynamic_load);
+script3_dynamic_load.onerror = () => onError(test3_dynamic_load);
+script3_dynamic_load.async = false;
+script3_dynamic_load.appendChild(document.createTextNode('onExecute(test3_dynamic_load);'));
+document.head.appendChild(script3_dynamic_load);
+
+var script3_dynamic_error = document.createElement('script');
+script3_dynamic_error.setAttribute('type', 'module');
+script3_dynamic_error.onload = () => onLoad(test3_dynamic_error);
+script3_dynamic_error.onerror = () => onError(test3_dynamic_error);
+script3_dynamic_error.async = false;
+script3_dynamic_error.appendChild(document.createTextNode('import "./not_found.js";'));
+document.head.appendChild(script3_dynamic_error);
+
+var script4_dynamic_load = document.createElement('script');
+script4_dynamic_load.setAttribute('type', 'module');
+script4_dynamic_load.onload = () => onLoad(test4_dynamic_load);
+script4_dynamic_load.onerror = () => onError(test4_dynamic_load);
+script4_dynamic_load.async = true;
+script4_dynamic_load.appendChild(document.createTextNode('onExecute(test4_dynamic_load);'));
+document.head.appendChild(script4_dynamic_load);
+
+var script4_dynamic_error = document.createElement('script');
+script4_dynamic_error.setAttribute('type', 'module');
+script4_dynamic_error.onload = () => onLoad(test4_dynamic_error);
+script4_dynamic_error.onerror = () => onError(test4_dynamic_error);
+script4_dynamic_error.async = true;
+script4_dynamic_error.appendChild(document.createTextNode('import "./not_found.js";'));
+document.head.appendChild(script4_dynamic_error);
+</script>
+
+<script onload="onLoad(test1_load);" onerror="onError(test1_load);" type="module">"use strict";onExecute(test1_load);</script>
+<script onload="onLoad(test4_load);" onerror="onError(test4_load);" type="module" async>"use strict";onExecute(test4_load);</script>
+<script onload="onLoad(test1_error);" onerror="onError(test1_error);" type="module">"use strict";import "./not_found.js";</script>
+<script onload="onLoad(test4_error);" onerror="onError(test4_error);" type="module" async>"use strict";import "./not_found.js";</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events.html
new file mode 100644
index 0000000000..d9bf05226c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/load-error-events.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<head>
+<title>load/error events for external module scripts</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/load-error-events-helpers.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+</head>
+<script>
+"use strict";
+
+var test1_load = event_test('src, 200, parser-inserted, defer, no async', true, false);
+var test4_load = event_test('src, 200, parser-inserted, no defer, async', true, false);
+
+var test3_dynamic_load = event_test('src, 200, not parser-inserted, no defer, no async, no non-blocking', true, false);
+var test4_dynamic_load = event_test('src, 200, not parser-inserted, no defer, async', true, false);
+
+var test1_error = event_test('src, 404, parser-inserted, defer, no async', false, true);
+var test4_error = event_test('src, 404, parser-inserted, no defer, async', false, true);
+
+var test3_dynamic_error = event_test('src, 404, not parser-inserted, no defer, no async, no non-blocking', false, true);
+var test4_dynamic_error = event_test('src, 404, not parser-inserted, no defer, async', false, true);
+
+var script3_dynamic_load = document.createElement('script');
+script3_dynamic_load.setAttribute('type', 'module');
+script3_dynamic_load.onload = () => onLoad(test3_dynamic_load);
+script3_dynamic_load.onerror = () => onError(test3_dynamic_load);
+script3_dynamic_load.async = false;
+script3_dynamic_load.src = "../resources/load-error-events.py?test=test3_dynamic_load";
+document.head.appendChild(script3_dynamic_load);
+
+var script3_dynamic_error = document.createElement('script');
+script3_dynamic_error.setAttribute('type', 'module');
+script3_dynamic_error.onload = () => onLoad(test3_dynamic_error);
+script3_dynamic_error.onerror = () => onError(test3_dynamic_error);
+script3_dynamic_error.async = false;
+script3_dynamic_error.src = "../resources/load-error-events.py?test=test3_dynamic_error";
+document.head.appendChild(script3_dynamic_error);
+
+var script4_dynamic_load = document.createElement('script');
+script4_dynamic_load.setAttribute('type', 'module');
+script4_dynamic_load.onload = () => onLoad(test4_dynamic_load);
+script4_dynamic_load.onerror = () => onError(test4_dynamic_load);
+script4_dynamic_load.async = true;
+script4_dynamic_load.src = "../resources/load-error-events.py?test=test4_dynamic_load";
+document.head.appendChild(script4_dynamic_load);
+
+var script4_dynamic_error = document.createElement('script');
+script4_dynamic_error.setAttribute('type', 'module');
+script4_dynamic_error.onload = () => onLoad(test4_dynamic_error);
+script4_dynamic_error.onerror = () => onError(test4_dynamic_error);
+script4_dynamic_error.async = true;
+script4_dynamic_error.src = "../resources/load-error-events.py?test=test4_dynamic_error";
+document.head.appendChild(script4_dynamic_error);
+</script>
+
+<script src="../resources/load-error-events.py?test=test1_load" onload="onLoad(test1_load);" onerror="onError(test1_load);" type="module"></script>
+<script src="../resources/load-error-events.py?test=test4_load" onload="onLoad(test4_load);" onerror="onError(test4_load);" type="module" async></script>
+<script src="../resources/load-error-events.py?test=test1_error" onload="onLoad(test1_error);" onerror="onError(test1_error);" type="module"></script>
+<script src="../resources/load-error-events.py?test=test4_error" onload="onLoad(test4_error);" onerror="onError(test4_error);" type="module" async></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export-nested.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export-nested.js
new file mode 100644
index 0000000000..860d2bf341
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export-nested.js
@@ -0,0 +1,2 @@
+import "./missing-export.js";
+log.push("nested-missing-export");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export.js
new file mode 100644
index 0000000000..e6f5746eb7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/missing-export.js
@@ -0,0 +1,2 @@
+import something from "./missing-export.js";
+log.push("missing-export");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.html
new file mode 100644
index 0000000000..7348b88075
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrer for module imports</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>setup({ explicit_done: true })</script>
+</head>
+<body>
+<script type="module">
+
+import { referrerExternalStatic, referrerExternalDynamic } from "./module-import-referrer.js";
+
+// "name" parameter is necessary for bypassing the module map.
+import { referrer as referrerInlineStatic } from "./resources/referrer-checker.py?name=internal-static"
+const { referrer: referrerInlineDynamic } = await import("./resources/referrer-checker.py?name=internal-dynamic");
+
+const scriptURL = new URL("module-import-referrer.js", location.href)
+
+test(t => {
+ assert_equals(
+ referrerInlineStatic, location.href,
+ "Referrer should be the document URL");
+}, "Static imports from inline modules in the HTML document");
+
+test(t => {
+ assert_equals(
+ referrerInlineDynamic, location.href,
+ "Referrer should be the document URL");
+}, "Dynamic imports from inline modules in the HTML document");
+
+test(t => {
+ assert_equals(
+ referrerExternalStatic, scriptURL.href,
+ "Referrer should be the importer module URL");
+}, "Static imports from external modules");
+
+test(t => {
+ assert_equals(
+ referrerExternalDynamic, scriptURL.href,
+ "Referrer should be the document URL");
+}, "Dynamic imports from external modules");
+
+done();
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.js
new file mode 100644
index 0000000000..8710f5c621
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-import-referrer.js
@@ -0,0 +1,2 @@
+export { referrer as referrerExternalStatic } from "./resources/referrer-checker.py?name=external-static"
+export const { referrer: referrerExternalDynamic } = await import("./resources/referrer-checker.py?name=external-dynamic");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-in-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-in-xhtml.xhtml
new file mode 100644
index 0000000000..1655e61a7c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-in-xhtml.xhtml
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+window.evaluated_module_script = true;
+</script>
+<script>
+ var test = async_test("module script in XHTML documents should be evaluated.");
+ window.addEventListener("load", () => {
+ test.step(() => { assert_true(window.evaluated_module_script); });
+ test.done();
+ });
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-1.html
new file mode 100644
index 0000000000..ae82e1348a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Once as module script, once as classic script</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that evaluating something as classic script does not prevent " +
+ "it from being evaluated as module script.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, [window, undefined]);
+ }));
+</script>
+<script type="module" src="this.js"></script>
+<script src="this.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-2.html
new file mode 100644
index 0000000000..fb512b4b30
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/module-vs-script-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Once as classic script, once as module script</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that evaluating something as classic script does not prevent " +
+ "it from being evaluated as module script.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, [window, undefined]);
+ }));
+</script>
+<script src="this.js"></script>
+<script type="module" src="this.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-a.js
new file mode 100644
index 0000000000..a127aeb559
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-a.js
@@ -0,0 +1 @@
+import { b } from "./nested-imports-b.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-b.js
new file mode 100644
index 0000000000..18a5af40cc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-b.js
@@ -0,0 +1,2 @@
+import { c } from "./nested-imports-c.js";
+export const b = "b";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-c.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-c.js
new file mode 100644
index 0000000000..ec44596aea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-c.js
@@ -0,0 +1,2 @@
+import { d } from "./nested-imports-d.js";
+export const c = "c";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-d.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-d.js
new file mode 100644
index 0000000000..cee87849c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-d.js
@@ -0,0 +1,3 @@
+import { e } from "./nested-imports-e.js";
+import "./resources/delayed-modulescript.py";
+export const d = "d";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-e.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-e.js
new file mode 100644
index 0000000000..ec6f0360a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-e.js
@@ -0,0 +1,2 @@
+import { f } from "./nested-imports-f.js";
+export const e = "e";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-f.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-f.js
new file mode 100644
index 0000000000..0591e0b316
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-f.js
@@ -0,0 +1,2 @@
+import { g } from "./nested-imports-g.js";
+export const f = "f";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-g.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-g.js
new file mode 100644
index 0000000000..86cbe7d3f8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-g.js
@@ -0,0 +1,2 @@
+import { h } from "./nested-imports-h.js";
+export const g = "g";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-h.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-h.js
new file mode 100644
index 0000000000..a161291259
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports-h.js
@@ -0,0 +1,2 @@
+import { c } from "./nested-imports-c.js";
+export const h = "h";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports.html
new file mode 100644
index 0000000000..23bb595d0e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-imports.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Test imports under more than 3 levels in different modules</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({ allow_uncaught_exception: true });
+
+ window.log = [];
+
+ const test_load = async_test("should load all modules successfully");
+ window.addEventListener(
+ "load",
+ test_load.step_func_done((ev) => {
+ assert_equals(log.length, 2);
+
+ assert_equals(log[0], 1);
+ assert_equals(log[1], 2);
+ })
+ );
+
+ function unreachable() {
+ log.push("unexpected");
+ }
+</script>
+<script type="module" src="./nested-imports-a.js" onerror="unreachable()"
+ onload="log.push(1)"></script>
+<script type="module" src="./nested-imports-e.js" onerror="unreachable()"
+ onload="log.push(2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-missing-export.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-missing-export.js
new file mode 100644
index 0000000000..3801ae847a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nested-missing-export.js
@@ -0,0 +1,2 @@
+import "./missing-export.js";
+log.push("missing-export-nested");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nomodule-attribute.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nomodule-attribute.html
new file mode 100644
index 0000000000..656c99b292
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/nomodule-attribute.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>The 'nomodule' attribute</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that 'nomodule' has the desired effect on classic scripts, but " +
+ "no effect on module scripts.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, [undefined]);
+ }));
+
+</script>
+<script type="module" src="this.js" nomodule></script>
+<script src="this.js" nomodule></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-no-referrer.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-no-referrer.sub.html
new file mode 100644
index 0000000000..b8866f9511
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-no-referrer.sub.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrer with the no-referrer policy</title>
+<meta name="referrer" content="no-referrer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+
+// "name" parameter is necessary for bypassing the module map.
+
+import { referrer as referrerSame } from "./resources/referrer-checker.py?name=same";
+
+import { referrer as referrerRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name=remote";
+
+import { referrer as referrerSameSame } from "./resources/import-referrer-checker.sub.js?name=same_same";
+
+import { referrer as referrerSameRemote } from "./resources/import-remote-origin-referrer-checker.sub.js?name=same_remote";
+
+import { referrer as referrerRemoteRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_remote";
+
+import { referrer as referrerRemoteSame } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_same";
+
+test(t => {
+ assert_equals(
+ referrerSame, "",
+ "Referrer should not be sent for the same-origin top-level script.");
+}, "Importing a same-origin top-level script with the no-referrer policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemote, "",
+ "Referrer should not be sent for the remote-origin top-level script.");
+}, "Importing a remote-origin top-level script with the no-referrer policy.");
+
+test(t => {
+ assert_equals(
+ referrerSameSame, "",
+ "Referrer should not be sent for the same-origin descendant script.");
+}, "Importing a same-origin descendant script from a same-origin top-level " +
+ "script with the no-referrer policy.");
+
+test(t => {
+ assert_equals(
+ referrerSameRemote, "",
+ "Referrer should not be sent for the remote-origin descendant script.");
+}, "Importing a remote-origin descendant script from a same-origin top-level " +
+ "script with the no-referrer policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemoteRemote, "",
+ "Referrer should not be sent for the remote-origin descendant script.");
+}, "Importing a remote-origin descendant script from a remote-origin " +
+ "top-level script with the no-referrer policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemoteSame, "",
+ "Referrer should not be sent for the same-origin descendant script.");
+}, "Importing a same-origin descendant script from a remote-origin " +
+ "top-level script with the no-referrer policy.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html
new file mode 100644
index 0000000000..fd4c1ea496
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrer with the origin-when-cross-origin policy</title>
+<meta name="referrer" content="origin-when-cross-origin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+
+// "name" parameter is necessary for bypassing the module map.
+
+import { referrer as referrerSame } from "./resources/referrer-checker.py?name=same";
+
+import { referrer as referrerRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name=remote";
+
+import { referrer as referrerSameSame } from "./resources/import-referrer-checker.sub.js?name=same_same";
+
+import { referrer as referrerSameRemote } from "./resources/import-remote-origin-referrer-checker.sub.js?name=same_remote";
+
+import { referrer as referrerRemoteRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_remote";
+
+import { referrer as referrerRemoteSame } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_same";
+
+const origin = (new URL(location.href)).origin + "/";
+const remoteOrigin = new URL("http://{{domains[www1]}}:{{ports[http][0]}}/").origin + "/";
+
+test(t => {
+ assert_equals(
+ referrerSame, location.href,
+ "Full referrer should be sent for the same-origin top-level script.");
+}, "Importing a same-origin top-level script with the " +
+ "origin-when-cross-origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemote, origin,
+ "Referrer should be stripped to the origin when importing " +
+ "remote-origin top-level script.");
+}, "Importing a remote-origin top-level script with the " +
+ "origin-when-cross-origin policy.");
+
+test(t => {
+ const scriptURL =
+ new URL("resources/import-referrer-checker.sub.js", location.href)
+ assert_equals(
+ referrerSameSame, scriptURL + "?name=same_same",
+ "Full referrer should be sent for same-origin descendant script" +
+ "imported by same-origin top-level script.");
+}, "Importing a same-origin descendant script from a same-origin top-level " +
+ "script with the origin-when-cross-origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerSameRemote, origin,
+ "Referrer should be stripped to the origin for the remote-origin " +
+ "descendant script imported from same-origin top-level script.");
+}, "Importing a remote-origin descendant script from a same-origin top-level " +
+ "script with the origin-when-cross-origin policy.");
+
+test(t => {
+ const scriptURL = new URL(
+ "html/semantics/scripting-1/the-script-element/module/resources/" +
+ "import-referrer-checker.sub.js",
+ remoteOrigin);
+ assert_equals(referrerRemoteRemote, scriptURL + "?name=remote_remote",
+ "Full referrer should be sent for the remote-origin descendant script " +
+ "imported from a remote-origin top-level script.");
+}, "Importing a remote-origin descendant script from a remote-origin " +
+ "top-level script with the origin-when-cross-origin policy.");
+
+test(t => {
+ assert_equals(referrerRemoteSame, remoteOrigin,
+ "Referrer should be stripped to the origin for the same-origin " +
+ "descendant script imported by remote-origin top-level script.");
+}, "Importing a same-origin descendant script from a remote-origin " +
+ "top-level script with the origin-when-cross-origin policy.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin.sub.html
new file mode 100644
index 0000000000..a554fb4b0c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-origin.sub.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrer with the origin policy</title>
+<meta name="referrer" content="origin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+
+// "name" parameter is necessary for bypassing the module map.
+
+import { referrer as referrerSame } from "./resources/referrer-checker.py?name=same";
+
+import { referrer as referrerRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name=remote";
+
+import { referrer as referrerSameSame } from "./resources/import-referrer-checker.sub.js?name=same_same";
+
+import { referrer as referrerSameRemote } from "./resources/import-remote-origin-referrer-checker.sub.js?name=same_remote";
+
+import { referrer as referrerRemoteRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_remote";
+
+import { referrer as referrerRemoteSame } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_same";
+
+const origin = (new URL(location.href)).origin + "/";
+const remoteOrigin = "http://{{domains[www1]}}:{{ports[http][0]}}/";
+
+test(t => {
+ assert_equals(
+ referrerSame, origin,
+ "Referrer should be sent for the same-origin top-level script.");
+}, "Importing a same-origin top-level script with the origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemote, origin,
+ "Referrer should be sent for the remote-origin top-level script.");
+}, "Importing a remote-origin top-level script with the origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerSameSame, origin,
+ "Referrer should be sent for the same-origin descendant script.");
+}, "Importing a same-origin descendant script from a same-origin top-level " +
+ "script with the origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerSameRemote, origin,
+ "Referrer should be sent for the remote-origin descendant script.");
+}, "Importing a remote-origin descendant script from a same-origin top-level " +
+ "script with the origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemoteRemote, remoteOrigin,
+ "Referrer should be sent for the remote-origin descendant script.");
+}, "Importing a remote-origin descendant script from a remote-origin " +
+ "top-level script with the origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemoteSame, remoteOrigin,
+ "Referrer should be sent for the same-origin descendant script.");
+}, "Importing a same-origin descendant script from a remote-origin " +
+ "top-level script with the origin policy.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-policy-for-descendants.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-policy-for-descendants.sub.html
new file mode 100644
index 0000000000..4a7005da84
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-policy-for-descendants.sub.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Module script descendants use the referrer policy on their ancestor, if one exists</title>
+<link rel=help href="https://github.com/whatwg/html/pull/9210">
+<meta name="referrer" content="no-referrer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+
+// "name" parameter is necessary for bypassing the module map.
+//
+// The case where the parent module script is sent with no referrer policy at
+// all is covered in `referrer-no-referrer.sub.html` (and others like it). In
+// that case, the parent module inherits its referencing document's referrer
+// policy for use in descendant imports.
+
+import { referrer as parentWithNoReferrerWhenDowngrade } from
+ "./resources/import-referrer-checker.sub.js?name=same_same&pipe=header(Referrer-Policy,no-referrer-when-downgrade)";
+
+import { referrer as parentWithOrigin} from
+ "./resources/import-referrer-checker.sub.js?name=same_remote&pipe=header(Referrer-Policy,origin)";
+
+import { referrer as parentWithSameOrigin} from
+ "./resources/import-referrer-checker.sub.js?name=same_remote_so&pipe=header(Referrer-Policy,same-origin)";
+
+import { referrer as parentWithOriginWhenCrossOriginRemoteDescendant} from
+ "./resources/import-remote-origin-referrer-checker.sub.js?name=same_remote_owco&pipe=header(Referrer-Policy,origin-when-cross-origin)";
+
+import { referrer as remoteParentWithOriginWhenCrossOriginSameOriginDescendant} from
+ "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_same&pipe=header(Referrer-Policy,origin-when-cross-origin)";
+
+import { referrer as remoteParentWithSameOriginWhenCrossOriginSameOriginDescendant} from
+ "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_same_so&pipe=header(Referrer-Policy,same-origin)";
+
+import { referrer as remoteParentWithOriginWhenCrossOriginRemoteDescendant} from
+ "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_remote&pipe=header(Referrer-Policy,origin-when-cross-origin)";
+
+import { referrer as remoteParentWithSameOriginWhenCrossOriginRemoteDescendant} from
+ "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_remote_so&pipe=header(Referrer-Policy,same-origin)";
+
+import { referrer as parentWithInvalidPolicy } from
+ "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_same_invalid&pipe=header(Referrer-Policy,invalid-policy)";
+
+test(t => {
+ const expected_url =
+ new URL("html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=same_same&pipe=header(Referrer-Policy,no-referrer-when-downgrade)",
+ window.origin);
+ assert_equals(
+ parentWithNoReferrerWhenDowngrade, expected_url.toString(),
+ "Descendant referrer should be the parent's full URL.");
+}, "Parent module delivered with `no-referrer-when-downgrade` policy importing a same-origin descendant script.");
+
+test(t => {
+ assert_equals(
+ parentWithOrigin, window.origin + "/",
+ "Descendant referrer should be the parent's origin.");
+}, "Parent module delivered with `origin` policy importing a same-origin descendant script.");
+
+test(t => {
+ const expected_url =
+ new URL("html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=same_remote_so&pipe=header(Referrer-Policy,same-origin)",
+ window.origin);
+ assert_equals(
+ parentWithSameOrigin, expected_url.toString(),
+ "Descendant referrer should be the parent's full URL.");
+}, "Parent module delivered with `same-origin` policy importing a same-origin descendant script.");
+
+test(t => {
+ assert_equals(
+ parentWithOriginWhenCrossOriginRemoteDescendant, window.origin + "/",
+ "Remote descendant referrer should be the parent's origin.");
+}, "Parent module delivered with `origin-when-cross-origin` policy importing a cross-origin descendant script.");
+
+test(t => {
+ const expected_url =
+ new URL("/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_same&pipe=header(Referrer-Policy,origin-when-cross-origin)",
+ "http://{{domains[www1]}}:{{ports[http][0]}}/");
+ assert_equals(
+ remoteParentWithOriginWhenCrossOriginSameOriginDescendant, expected_url.toString(),
+ "Same-origin descendant referrer should be the parent's full URL.");
+}, "Remote parent module delivered with `origin-when-cross-origin` policy importing a same-origin-to-parent-module descendant script.");
+
+test(t => {
+ const expected_url =
+ new URL("/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_same_so&pipe=header(Referrer-Policy,same-origin)",
+ "http://{{domains[www1]}}:{{ports[http][0]}}/");
+ assert_equals(
+ remoteParentWithSameOriginWhenCrossOriginSameOriginDescendant, expected_url.toString(),
+ "Same-origin descendant referrer should be the parent's full URL.");
+}, "Remote parent module delivered with `same-origin` policy importing a same-origin-to-parent-module descendant script.");
+
+test(t => {
+ assert_equals(
+ remoteParentWithOriginWhenCrossOriginRemoteDescendant, "http://{{domains[www1]}}:{{ports[http][0]}}/",
+ "Remote-origin descendant referrer should be the parent's origin.");
+}, "Remote parent module delivered with `origin-when-cross-origin` policy importing a cross-origin-to-parent-module descendant script.");
+
+test(t => {
+ assert_equals(
+ remoteParentWithSameOriginWhenCrossOriginRemoteDescendant, "");
+}, "Remote parent module delivered with `same-origin` policy importing a cross-origin-to-parent-module descendant script.");
+
+// This tests the following spec line:
+//
+// "If referrerPolicy is not the empty string, set options's referrer policy to
+// referrerPolicy."
+//
+// In other words, invalid referrer policies are ignored, so the referrer policy
+// from the referencing document is inherited as usual, which is `no-referrer`
+// in this case.
+test(t => {
+ assert_equals(
+ parentWithInvalidPolicy, "",
+ "Descendant referrer should be empty.");
+}, "Parent module delivered with invalid policy importing a same-origin descendant script.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-same-origin.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-same-origin.sub.html
new file mode 100644
index 0000000000..7d6d515a47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-same-origin.sub.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrer with the same-origin policy</title>
+<meta name="referrer" content="same-origin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+
+// "name" parameter is necessary for bypassing the module map.
+
+import { referrer as referrerSame } from "./resources/referrer-checker.py?name=same";
+
+import { referrer as referrerRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name=remote";
+
+import { referrer as referrerSameSame } from "./resources/import-referrer-checker.sub.js?name=same_same";
+
+import { referrer as referrerSameRemote } from "./resources/import-remote-origin-referrer-checker.sub.js?name=same_remote";
+
+import { referrer as referrerRemoteRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_remote";
+
+import { referrer as referrerRemoteSame } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_same";
+
+const remoteOrigin = "http://{{domains[www1]}}:{{ports[http][0]}}/";
+
+test(t => {
+ assert_equals(
+ referrerSame, location.href,
+ "Referrer should be sent for the same-origin top-level script.");
+}, "Importing a same-origin top-level script with the same-origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemote, "",
+ "Referrer should not be sent for the remote-origin top-level script.");
+}, "Importing a remote-origin top-level script with the same-origin policy.");
+
+test(t => {
+ const path =
+ new URL("resources/import-referrer-checker.sub.js", location.href);
+ assert_equals(
+ referrerSameSame, path + `?name=same_same`,
+ "Referrer should be sent for the same-origin descendant script.");
+}, "Importing a same-origin descendant script from a same-origin top-level " +
+ "script with the same-origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerSameRemote, "",
+ "Referrer should not be sent for the remote-origin descendant script.");
+}, "Importing a remote-origin descendant script from a same-origin top-level " +
+ "script with the same-origin policy.");
+
+test(t => {
+ const scriptURL = new URL(
+ "html/semantics/scripting-1/the-script-element/module/resources/" +
+ "import-referrer-checker.sub.js", remoteOrigin);
+ assert_equals(
+ referrerRemoteRemote, scriptURL + "?name=remote_remote",
+ "Referrer should be sent for the remote-origin descendant script " +
+ "when it is imported from a top-level script in the same remote-origin.");
+}, "Importing a remote-origin descendant script from a remote-origin " +
+ "top-level script with the same-origin policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemoteSame, "",
+ "Referrer should not be sent for the same-origin descendant script " +
+ "when it is imported from a top-level remote-origin script.");
+}, "Importing a same-origin descendant script from a remote-origin " +
+ "top-level script with the same-origin policy.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-strict-policies.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-strict-policies.sub.html
new file mode 100644
index 0000000000..1984d875b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-strict-policies.sub.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrer with the strict-origin referrer policy</title>
+<meta name="referrer" content="strict-origin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+
+// "name" parameter is necessary for bypassing the module map in descendant import.
+
+import { referrer as insecureImport } from "./resources/import-referrer-checker-insecure.sub.js?name=insecure_import";
+import { referrer as secureImport } from "https://{{domains[www]}}:{{ports[https][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js?name=secure_import";
+
+const origin = (new URL(location.href)).origin + "/";
+
+test(t => {
+ assert_equals(
+ insecureImport, origin,
+ "A document with the strict-origin referrer policy served over HTTP, " +
+ "imports an module script over HTTP, that imports a descendant script " +
+ "over HTTP. The request for the descendant script is sent with a " +
+ "`Referer` header with the page's origin");
+
+ assert_equals(
+ secureImport, "",
+ "A document with the strict-origin referrer policy served over HTTP, " +
+ "imports an module script over HTTPS, that imports a descendant script " +
+ "over HTTP. The request for the descendant script is sent with no " +
+ "`Referer` header");
+}, "The strict-* referrer policies compare the trustworthiness of a " +
+ "request's referrer string against its URL");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-unsafe-url.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-unsafe-url.sub.html
new file mode 100644
index 0000000000..443731c1b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/referrer-unsafe-url.sub.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Referrer with the unsafe-url policy</title>
+<meta name="referrer" content="unsafe-url">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script type="module">
+
+// "name" parameter is necessary for bypassing the module map.
+
+import { referrer as referrerSame } from "./resources/referrer-checker.py?name=same";
+
+import { referrer as referrerRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name=remote";
+
+import { referrer as referrerSameSame } from "./resources/import-referrer-checker.sub.js?name=same_same";
+
+import { referrer as referrerSameRemote } from "./resources/import-remote-origin-referrer-checker.sub.js?name=same_remote";
+
+import { referrer as referrerRemoteRemote } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js?name=remote_remote";
+
+import { referrer as referrerRemoteSame } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_same";
+
+test(t => {
+ assert_equals(
+ referrerSame, location.href,
+ "Referrer should be sent for the same-origin top-level script.");
+}, "Importing a same-origin top-level script with the unsafe-url policy.");
+
+test(t => {
+ assert_equals(
+ referrerRemote, location.href,
+ "Referrer should be sent for the remote-origin top-level script.");
+}, "Importing a remote-origin top-level script with the unsafe-url policy.");
+
+test(t => {
+ const scriptURL =
+ new URL("resources/import-referrer-checker.sub.js", location.href)
+ assert_equals(
+ referrerSameSame, scriptURL + "?name=same_same",
+ "Referrer should be sent for the same-origin descendant script.");
+}, "Importing a same-origin descendant script from a same-origin top-level " +
+ "script with the unsafe-url policy.");
+
+test(t => {
+ const scriptURL =
+ new URL("resources/import-remote-origin-referrer-checker.sub.js",
+ location.href)
+ assert_equals(
+ referrerSameRemote, scriptURL + "?name=same_remote",
+ "Referrer should be sent for the remote-origin descendant script.");
+}, "Importing a remote-origin descendant script from a same-origin top-level " +
+ "script with the unsafe-url policy.");
+
+test(t => {
+ const scriptURL =
+ "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/" +
+ "scripting-1/the-script-element/module/resources/" +
+ "import-referrer-checker.sub.js";
+ assert_equals(
+ referrerRemoteRemote, scriptURL + "?name=remote_remote",
+ "Referrer should be sent for the remote-origin descendant script.");
+}, "Importing a remote-origin descendant script from a remote-origin " +
+ "top-level script with the unsafe-url policy.");
+
+test(t => {
+ const scriptURL =
+ "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/" +
+ "scripting-1/the-script-element/module/resources/" +
+ "import-same-origin-referrer-checker-from-remote-origin.sub.js";
+ assert_equals(
+ referrerRemoteSame, scriptURL + "?name=remote_same",
+ "Referrer should be sent for the same-origin descendant script.");
+}, "Importing a same-origin descendant script from a remote-origin " +
+ "top-level script with the unsafe-url policy.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/404-but-js.asis b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/404-but-js.asis
new file mode 100644
index 0000000000..0fe1379e56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/404-but-js.asis
@@ -0,0 +1,4 @@
+HTTP/1.1 404 Not Found
+Content-Type: text/javascript
+
+window.ran404 = true;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/500-but-js.asis b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/500-but-js.asis
new file mode 100644
index 0000000000..c81202f7ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/500-but-js.asis
@@ -0,0 +1,4 @@
+HTTP/1.1 500 Not Found
+Content-Type: text/javascript
+
+window.ran500 = true;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py
new file mode 100644
index 0000000000..90551e92c3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py
@@ -0,0 +1,20 @@
+def main(request, response):
+ headers = [
+ (b"Content-Type", b"text/javascript"),
+ (b"Access-Control-Allow-Origin", request.GET.first(b"origin")),
+ (b"Access-Control-Allow-Credentials", b"true")
+ ]
+ identifier = request.GET.first(b"id")
+ cookie_name = request.GET.first(b"cookieName")
+ cookie = request.cookies.first(cookie_name, None)
+ if identifier is None or cookie_name is None:
+ return headers, b""
+
+ if cookie is None:
+ result = b"not found"
+ elif cookie.value == b"1":
+ result = b"found"
+ else:
+ result = b"different value: " + cookie.value
+
+ return headers, b"window." + identifier + b" = '" + result + b"';"
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/credentials-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/credentials-iframe.sub.html
new file mode 100644
index 0000000000..dbc14dffec
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/credentials-iframe.sub.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script type="module"
+ src="check-cookie.py?id=sameOriginNone&cookieName=same&origin=http://{{host}}:{{ports[http][0]}}">
+</script>
+<script type="module"
+ src="check-cookie.py?id=sameOriginAnonymous&cookieName=same&origin=http://{{host}}:{{ports[http][0]}}"
+ crossOrigin="anonymous">
+</script>
+<script type="module"
+ src="check-cookie.py?id=sameOriginUseCredentials&cookieName=same&origin=http://{{host}}:{{ports[http][0]}}"
+ crossOrigin="use-credentials">
+</script>
+<script type="module"
+ src="http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py?id=crossOriginNone&cookieName=cross&origin=http://{{host}}:{{ports[http][0]}}">
+</script>
+<script type="module"
+ src="http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py?id=crossOriginAnonymous&cookieName=cross&origin=http://{{host}}:{{ports[http][0]}}"
+ crossOrigin="anonymous">
+</script>
+<script type="module"
+ src="http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py?id=crossOriginUseCredentials&cookieName=cross&origin=http://{{host}}:{{ports[http][0]}}"
+ crossOrigin="use-credentials">
+</script>
+
+<script type="module">
+import "./check-cookie.py?id=sameOriginNoneDescendant&cookieName=same&origin=http://{{host}}:{{ports[http][0]}}";
+</script>
+<script type="module" crossOrigin="anonymous">
+import "./check-cookie.py?id=sameOriginAnonymousDescendant&cookieName=same&origin=http://{{host}}:{{ports[http][0]}}";
+</script>
+<script type="module" crossOrigin="use-credentials">
+import "./check-cookie.py?id=sameOriginUseCredentialsDescendant&cookieName=same&origin=http://{{host}}:{{ports[http][0]}}";
+</script>
+<script type="module">
+import "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py?id=crossOriginNoneDescendant&cookieName=cross&origin=http://{{host}}:{{ports[http][0]}}";
+</script>
+<script type="module" crossOrigin="anonymous">
+import "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py?id=crossOriginAnonymousDescendant&cookieName=cross&origin=http://{{host}}:{{ports[http][0]}}";
+</script>
+<script type="module" crossOrigin="use-credentials">
+import "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py?id=crossOriginUseCredentialsDescendant&cookieName=cross&origin=http://{{host}}:{{ports[http][0]}}";
+</script>
+
+<script type="text/javascript">
+window.addEventListener('load', event => {
+ window.parent.postMessage({}, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/delayed-modulescript.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/delayed-modulescript.py
new file mode 100644
index 0000000000..52dbfba445
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/delayed-modulescript.py
@@ -0,0 +1,7 @@
+import time
+
+def main(request, response):
+ delay = float(request.GET.first(b"ms", 500))
+ time.sleep(delay / 1E3)
+
+ return [(b"Content-type", b"text/javascript")], u"export let delayedLoaded = true;"
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-helper.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-helper.sub.js
new file mode 100644
index 0000000000..7d9b024e75
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-helper.sub.js
@@ -0,0 +1,67 @@
+// runTestsFromIframe() is used in the top-level HTML to set cookies and then
+// start actual tests in iframe.
+function runTestsFromIframe(iframe_url) {
+ const setSameOriginCookiePromise = fetch(
+ '/cookies/resources/set-cookie.py?name=same&path=/html/semantics/scripting-1/the-script-element/module/',
+ {
+ mode: 'no-cors',
+ credentials: 'include',
+ });
+ const setCrossOriginCookiePromise = fetch(
+ 'http://{{domains[www2]}}:{{ports[http][0]}}/cookies/resources/set-cookie.py?name=cross&path=/html/semantics/scripting-1/the-script-element/module/',
+ {
+ mode: 'no-cors',
+ credentials: 'include',
+ });
+ const windowLoadPromise = new Promise(resolve => {
+ window.addEventListener('load', () => {
+ resolve();
+ });
+ });
+
+ const iframe = document.createElement('iframe');
+ Promise.all([setSameOriginCookiePromise,
+ setCrossOriginCookiePromise,
+ windowLoadPromise]).then(() => {
+ iframe.src = iframe_url;
+ document.body.appendChild(iframe);
+ fetch_tests_from_window(iframe.contentWindow);
+ });
+}
+
+// The functions below are used from tests within the iframe.
+
+let testNumber = 0;
+
+// importFunc and setTimeoutFunc is used to make the active script at the time
+// of import() to be the script elements that call `runTest()`,
+// NOT this script defining runTest().
+
+function runTest(importFunc, origin, expected, source) {
+ let url;
+ let description;
+ if (origin === 'same') {
+ url = "./check-cookie.py";
+ description = "Same-origin dynamic import from " + source;
+ } else {
+ url = "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/check-cookie.py";
+ description = "Cross-origin dynamic import from " + source;
+ }
+ promise_test(() => {
+ const id = "test" + testNumber;
+ testNumber += 1;
+ return importFunc(url + "?id=" + id + "&cookieName=" + origin + "&origin=" + location.origin)
+ .then(() => {
+ assert_equals(window[id], expected, "cookie");
+ });
+ }, description);
+}
+
+function setTimeoutWrapper(setTimeoutFunc) {
+ return url => {
+ return new Promise(resolve => {
+ window.resolve = resolve;
+ setTimeoutFunc(`import("${url}").then(window.resolve)`);
+ });
+ };
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-iframe.sub.html
new file mode 100644
index 0000000000..88204ef00b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-iframe.sub.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="dynamic-import-credentials-helper.sub.js"></script>
+
+<!--
+The active script at the time of import() is the script elements below, and
+thus the credentials mode of the fetch options of the script elements below
+are used for dynamic import requests.
+-->
+
+<script>
+runTest(url => import(url),
+ "same", "found", "classic script (crossOrigin not specified)");
+runTest(url => import(url),
+ "cross", "not found", "classic script (crossOrigin not specified)");
+</script>
+
+<script crossOrigin="anonymous">
+runTest(url => import(url), "same", "found",
+ "classic script (crossOrigin=anonymous)");
+runTest(url => import(url), "cross", "not found",
+ "classic script (crossOrigin=anonymous)");
+</script>
+
+<script crossOrigin="use-credentials">
+runTest(url => import(url),
+ "same", "found", "classic script (crossOrigin=use-credentials)");
+runTest(url => import(url),
+ "cross", "found", "classic script (crossOrigin=use-credentials)");
+</script>
+
+<script type="module">
+runTest(url => import(url),
+ "same", "found", "module script (crossOrigin not specified)");
+runTest(url => import(url),
+ "cross", "not found", "module script (crossOrigin not specified)");
+</script>
+
+<script type="module" crossOrigin="anonymous">
+runTest(url => import(url), "same", "found",
+ "module script (crossOrigin=anonymous)");
+runTest(url => import(url), "cross", "not found",
+ "module script (crossOrigin=anonymous)");
+</script>
+
+<script type="module" crossOrigin="use-credentials">
+runTest(url => import(url),
+ "same", "found", "module script (crossOrigin=use-credentials)");
+runTest(url => import(url),
+ "cross", "found", "module script (crossOrigin=use-credentials)");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-setTimeout-iframe.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-setTimeout-iframe.sub.html
new file mode 100644
index 0000000000..ffba141527
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/dynamic-import-credentials-setTimeout-iframe.sub.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="dynamic-import-credentials-helper.sub.js"></script>
+
+<!--
+The active script at the time of import() is the classic script created by
+https://html.spec.whatwg.org/multipage/C/#timer-initialisation-steps
+and the active script at the time of setTimeout() is the script elements below,
+thus the credentials mode of the fetch options of the script elements below
+are used for dynamic import requests.
+
+setTimeout() calls below can't be wrapped (e.g. by step_timeout())
+because wrapping setTimeout() would set active scripts differently.
+-->
+
+<script>
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "same", "found", "setTimeout(string) from classic script (crossOrigin not specified)");
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "cross", "not found", "setTimeout(string) from classic script (crossOrigin not specified)");
+</script>
+
+<script crossOrigin="anonymous">
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "same", "found", "setTimeout(string) from classic script (crossOrigin=anonymous)");
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "cross", "not found", "setTimeout(string) from classic script (crossOrigin=anonymous)");
+</script>
+
+<script crossOrigin="use-credentials">
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "same", "found", "setTimeout(string) from classic script (crossOrigin=use-credentials)");
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "cross", "found", "setTimeout(string) from classic script (crossOrigin=use-credentials)");
+</script>
+
+<script type="module">
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "same", "found", "setTimeout(string) from module script (crossOrigin not specified)");
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "cross", "not found", "setTimeout(string) from module script (crossOrigin not specified)");
+</script>
+
+<script type="module" crossOrigin="anonymous">
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "same", "found", "setTimeout(string) from module script (crossOrigin=anonymous)");
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "cross", "not found", "setTimeout(string) from module script (crossOrigin=anonymous)");
+</script>
+
+<script type="module" crossOrigin="use-credentials">
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "same", "found", "setTimeout(string) from module script (crossOrigin=use-credentials)");
+runTest(setTimeoutWrapper(x => setTimeout(x, 0)),
+ "cross", "found", "setTimeout(string) from module script (crossOrigin=use-credentials)");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/fast-module.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/fast-module.js
new file mode 100644
index 0000000000..3a76cf71f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/fast-module.js
@@ -0,0 +1 @@
+loaded.push("fast");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8-with-charset-header.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8-with-charset-header.js
new file mode 100644
index 0000000000..6fc4ad395c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8-with-charset-header.js
@@ -0,0 +1 @@
+import "../../serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript%3Bcharset=windows-1250&dummy=6";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8.js
new file mode 100644
index 0000000000..3ae805d78d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-non-utf8.js
@@ -0,0 +1 @@
+import "../../serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript&dummy=5";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js
new file mode 100644
index 0000000000..2d6fd96712
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js
@@ -0,0 +1 @@
+export { referrer } from 'http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name={{GET[name]}}';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js.headers b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js.headers
new file mode 100644
index 0000000000..cb762eff80
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker-insecure.sub.js.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js
new file mode 100644
index 0000000000..2c7dce9dff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js
@@ -0,0 +1,2 @@
+import { referrer as referrerImport } from './referrer-checker.py?name={{GET[name]}}';
+export const referrer = referrerImport;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js.headers b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js.headers
new file mode 100644
index 0000000000..cb762eff80
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-referrer-checker.sub.js.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-remote-origin-referrer-checker.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-remote-origin-referrer-checker.sub.js
new file mode 100644
index 0000000000..45a2520b68
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-remote-origin-referrer-checker.sub.js
@@ -0,0 +1,2 @@
+import { referrer as referrerImport } from 'http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name={{GET[name]}}';
+export const referrer = referrerImport;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js
new file mode 100644
index 0000000000..5a53bcd4d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js
@@ -0,0 +1,2 @@
+import { referrer as referrerImport } from 'http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py?name={{GET[name]}}';
+export const referrer = referrerImport;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js.headers b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js.headers
new file mode 100644
index 0000000000..cb762eff80
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8-with-charset-header.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8-with-charset-header.js
new file mode 100644
index 0000000000..c2ccab7c62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8-with-charset-header.js
@@ -0,0 +1 @@
+import "../../serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript%3Bcharset=windows-1250&dummy=6";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8.js
new file mode 100644
index 0000000000..5708a26e07
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/import-utf8.js
@@ -0,0 +1 @@
+import "../../serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript&dummy=5";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-404-but-js.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-404-but-js.js
new file mode 100644
index 0000000000..d62e4f05be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-404-but-js.js
@@ -0,0 +1 @@
+import "./404-but-js.asis";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-500-but-js.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-500-but-js.js
new file mode 100644
index 0000000000..d62e4f05be
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-500-but-js.js
@@ -0,0 +1 @@
+import "./404-but-js.asis";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-b-cross-origin.sub.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-b-cross-origin.sub.js
new file mode 100644
index 0000000000..6db57b5017
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/imports-b-cross-origin.sub.js
@@ -0,0 +1 @@
+import "http://{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/imports-b.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py
new file mode 100644
index 0000000000..413f48d381
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/referrer-checker.py
@@ -0,0 +1,6 @@
+def main(request, response):
+ referrer = request.headers.get(b"referer", b"")
+ response_headers = [(b"Content-Type", b"text/javascript"),
+ (b"Access-Control-Allow-Origin", b"*")]
+ return (200, response_headers,
+ b"export const referrer = '" + referrer + b"';")
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/slow-module.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/slow-module.js
new file mode 100644
index 0000000000..4623ef7360
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/resources/slow-module.js
@@ -0,0 +1,3 @@
+// This module is imported with pipe=trickle(d2) to make it load more slowly
+// than fast-module.js
+loaded.push("slow");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/script-for-event.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/script-for-event.html
new file mode 100644
index 0000000000..e3b8e15b41
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/script-for-event.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<title>Module scripts with for and event attributes</title>
+<link rel="author" title="Matheus Kerschbaum" href="mailto:matjk7@gmail.com">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var expected = [
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+];
+var run = expected.map(function() { return false });
+</script>
+<script for="w&#x130;ndow" event="onload" type="module">
+run[0] = true;
+</script>
+<script for="window" event="onload x" type="module">
+run[1] = true;
+</script>
+<script for="window" event="onload(x" type="module">
+run[2] = true;
+</script>
+<script for="window" event="onload(x)" type="module">
+run[3] = true;
+</script>
+<script for="window" event="onclick" type="module">
+run[4] = true;
+</script>
+<script for="" event="onload" type="module">
+run[5] = true;
+</script>
+<script for="window" event="" type="module">
+run[6] = true;
+</script>
+<script for="" event="" type="module">
+run[7] = true;
+</script>
+<script for="&#xa0;window" event="onload" type="module">
+run[8] = true;
+</script>
+<script for="window&#xa0;" event="onload" type="module">
+run[9] = true;
+</script>
+<script for="window" event="&#xa0;onload" type="module">
+run[10] = true;
+</script>
+<script for="window" event="onload&#xa0;" type="module">
+run[11] = true;
+</script>
+<script for=" window " event=" onload " type="module">
+run[12] = true;
+</script>
+<script for=" window " event=" onload() " type="module">
+run[13] = true;
+</script>
+<script for="object" event="handler" type="module">
+run[14] = true;
+</script>
+<script event="handler" type="module">
+run[15] = true;
+</script>
+<script for="object" type="module">
+run[16] = true;
+</script>
+<script type="module">
+test(function() {
+ for (var i = 0; i < run.length; ++i) {
+ test(function() {
+ var script = document.querySelectorAll("script[for], script[event]")[i];
+ assert_equals(run[i], expected[i],
+ "script for=" + format_value(script.getAttribute("for")) +
+ " event=" + format_value(script.getAttribute("event")));
+ }, "Script " + i);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/set-currentScript-on-window.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/set-currentScript-on-window.js
new file mode 100644
index 0000000000..6863075bd9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/set-currentScript-on-window.js
@@ -0,0 +1 @@
+window.currentScriptRecorded = document.currentScript;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-1.html
new file mode 100644
index 0000000000..cc4e2d69b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Single evaluation, 1</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that a module is evaluated only once, and that 'this' is " +
+ "undefined (because of strict mode).");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, [undefined, "this-nested"]);
+ }));
+</script>
+<script type="module" src="this.js"></script>
+<script type="module" src="this.js"></script>
+<script type="module" src="this-nested.js"></script>
+<script type="module" src="this.js"></script>
+<script type="module" src="this-nested.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-2.html
new file mode 100644
index 0000000000..790e2fa9c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/single-evaluation-2.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Single evaluation, 2</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.log = [];
+
+ const test_load = async_test(
+ "Test that a module is evaluated only once, and that 'this' is " +
+ "undefined (because of strict mode).");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, [undefined, "this-nested"]);
+ }));
+</script>
+<script type="module" src="this-nested.js"></script>
+<script type="module" src="this-nested.js"></script>
+<script type="module" src="this.js"></script>
+<script type="module" src="this-nested.js"></script>
+<script type="module" src="this.js"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-cycle.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-cycle.html
new file mode 100644
index 0000000000..3a42cf9e30
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-cycle.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Cyclic graph with slow imports</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module">
+import { loaded } from "./slow-module-graph-a.js";
+
+test(() => {
+ assert_true(loaded);
+}, "module graph with cycles load even if part of the graph loads slow");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-a.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-a.js
new file mode 100644
index 0000000000..48701aa1d0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-a.js
@@ -0,0 +1,3 @@
+import "./slow-module-graph-b.js";
+import "./resources/delayed-modulescript.py"
+export let loaded = true;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-b.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-b.js
new file mode 100644
index 0000000000..53a8f2026e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/slow-module-graph-b.js
@@ -0,0 +1 @@
+import "./slow-module-graph-a.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/specifier-error.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/specifier-error.html
new file mode 100644
index 0000000000..d07005caaa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/specifier-error.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Handling of invalid specifiers</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that invalid module specifier leads to TypeError on window.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 1);
+ assert_equals(log[0].constructor, TypeError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./bad-module-specifier.js" onerror="unreachable()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror-nested.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror-nested.js
new file mode 100644
index 0000000000..de1b053c5a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror-nested.js
@@ -0,0 +1,2 @@
+import "./syntaxerror.js";
+log.push("nested-syntaxerror");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror.js
new file mode 100644
index 0000000000..31a9e2cbdf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/syntaxerror.js
@@ -0,0 +1,2 @@
+log.push("syntaxerror");
+%!#$@#$@#$@
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this-nested.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this-nested.js
new file mode 100644
index 0000000000..f204812fd1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this-nested.js
@@ -0,0 +1,2 @@
+import "./this.js";
+log.push("this-nested");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this.js
new file mode 100644
index 0000000000..996a439df0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/this.js
@@ -0,0 +1 @@
+log.push(this);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-error.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-error.js
new file mode 100644
index 0000000000..9769c84b23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-error.js
@@ -0,0 +1,3 @@
+window.before_throwing_error = true;
+throw new Error;
+window.after_throwing_error = true;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-nested.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-nested.js
new file mode 100644
index 0000000000..f1801ea366
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw-nested.js
@@ -0,0 +1,2 @@
+import "./throw.js";
+log.push("throw-nested");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw.js
new file mode 100644
index 0000000000..cef7918216
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw.js
@@ -0,0 +1,2 @@
+log.push("throw");
+throw {foo: true}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw2.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw2.js
new file mode 100644
index 0000000000..2931eec500
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/throw2.js
@@ -0,0 +1,2 @@
+log.push("throw2");
+throw {bar: true}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked.any.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked.any.js
new file mode 100644
index 0000000000..820f995aaf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked.any.js
@@ -0,0 +1,11 @@
+promise_test(async t => {
+ const { checkMicrotask } = await import("./sibling-imports-not-blocked__microtask__parent.js");
+
+ assert_equals(checkMicrotask, "PASS");
+}, "Async modules only scheduling microtasks don't block execution of sibling modules");
+
+promise_test(async t => {
+ const { checkTask } = await import("./sibling-imports-not-blocked__task__parent.js");
+
+ assert_equals(checkTask, "PASS");
+}, "Async modules scheduling tasks don't block execution of sibling modules");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-get-sync.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-get-sync.js
new file mode 100644
index 0000000000..063697310a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-get-sync.js
@@ -0,0 +1 @@
+export const { checkMicrotask } = globalThis;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-set-async.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-set-async.js
new file mode 100644
index 0000000000..98930fed0b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__check-set-async.js
@@ -0,0 +1,3 @@
+globalThis.checkMicrotask = "PASS";
+await 0;
+globalThis.checkMicrotask = "FAIL";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__parent.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__parent.js
new file mode 100644
index 0000000000..a28fcef9cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__microtask__parent.js
@@ -0,0 +1,2 @@
+import "./sibling-imports-not-blocked__microtask__check-set-async.js";
+export { checkMicrotask } from "./sibling-imports-not-blocked__microtask__check-get-sync.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-get-sync.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-get-sync.js
new file mode 100644
index 0000000000..833acdd7ec
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-get-sync.js
@@ -0,0 +1 @@
+export const { checkTask } = globalThis;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-set-async.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-set-async.js
new file mode 100644
index 0000000000..0973b3c65a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__check-set-async.js
@@ -0,0 +1,3 @@
+globalThis.checkTask = "PASS";
+await new Promise(r => setTimeout(r, 0));
+globalThis.checkTask = "FAIL";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__parent.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__parent.js
new file mode 100644
index 0000000000..56ee5134d7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/top-level-await/sibling-imports-not-blocked__task__parent.js
@@ -0,0 +1,2 @@
+import "./sibling-imports-not-blocked__task__check-set-async.js";
+export { checkTask } from "./sibling-imports-not-blocked__task__check-get-sync.js";
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/type.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/type.html
new file mode 100644
index 0000000000..5817ae4d43
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/module/type.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Type attribute of module scripts</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+window.t1 = async_test('type="module"');
+window.t2 = async_test('type="MODULE"');
+window.t3 = async_test('type="Module"');
+window.t4 = async_test('type="module "');
+window.t5 = async_test('type=" module"');
+</script>
+<script type="module">window.t1.done();</script>
+<script type="MODULE">window.t2.done();</script>
+<script type="Module">window.t3.done();</script>
+<script type="module ">window.t4.unreached_func('Unexpectedly evaluated');</script>
+<script type=" module">window.t5.unreached_func('Unexpectedly evaluated');</script>
+<script type="module">
+window.t1.unreached_func('Unexpectedly not evaluated')();
+window.t2.unreached_func('Unexpectedly not evaluated')();
+window.t3.unreached_func('Unexpectedly not evaluated')();
+window.t4.done();
+window.t5.done();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents-during-evaluation.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents-during-evaluation.html
new file mode 100644
index 0000000000..e4cd887c61
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents-during-evaluation.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Moving script elements between documents during evaluation</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script id="outerScript">
+"use strict";
+
+async_test(t => {
+ const outerScript = document.querySelector('#outerScript');
+ assert_equals(document.currentScript, outerScript);
+
+ const innerScript = document.createElement('script');
+ window.innerScript = innerScript;
+
+ window.innerScriptEvaluated = false;
+ window.anotherDocument = null;
+
+ innerScript.innerText = `
+ window.innerScriptEvaluated = true;
+ const innerScript = window.innerScript;
+ assert_equals(document.currentScript, innerScript,
+ '[1] Before move: currentScript of source Document');
+ assert_equals(innerScript.ownerDocument, document,
+ '[1] Before move: ownerDocument');
+
+ window.anotherDocument = document.implementation.createHTMLDocument();
+ window.anotherDocument.body.appendChild(innerScript);
+
+ assert_equals(innerScript.ownerDocument, anotherDocument,
+ '[2] Just after move: ownerDocument');
+ assert_equals(document.currentScript, innerScript,
+ '[2] Just after move: currentScript of source Document');
+ assert_equals(anotherDocument.currentScript, null,
+ '[2] Just after move: currentScript of destination Document');
+ `;
+
+ document.body.appendChild(innerScript);
+ assert_true(window.innerScriptEvaluated,
+ 'Inner script should be evaluated synchronously');
+
+ assert_equals(document.currentScript, outerScript,
+ '[3] After inner script: currentScript of source Document');
+ assert_equals(window.anotherDocument.currentScript, null,
+ '[3] After inner script: currentScript of destination Document');
+
+ t.step_timeout(() => {
+ assert_equals(document.currentScript, null,
+ '[4] After outer script: currentScript of source Document');
+ assert_equals(anotherDocument.currentScript, null,
+ '[4] After outer script: currentScript of destination Document');
+ t.done();
+ }, 0);
+}, 'Script moved between documents during evaluation');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/README.md b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/README.md
new file mode 100644
index 0000000000..f95e0a63a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/README.md
@@ -0,0 +1,16 @@
+The tests in this directory are for script elements moved between documents.
+
+Use
+
+```
+$ tools/generate.py
+```
+
+to generate test HTML files (except for tests in subdirectories).
+
+Background:
+
+- https://www.w3.org/Bugs/Public/show_bug.cgi?id=11323
+- https://github.com/whatwg/html/issues/2137
+- https://github.com/whatwg/html/issues/2469
+- https://github.com/whatwg/html/pull/2673
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-classic.html
new file mode 100644
index 0000000000..2b84ae8088
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "createHTMLDocument", "fetch-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-module.html
new file mode 100644
index 0000000000..09ee1490c1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-fetch-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "createHTMLDocument", "fetch-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-classic.html
new file mode 100644
index 0000000000..477abd32cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "createHTMLDocument", "parse-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-module.html
new file mode 100644
index 0000000000..9da2276408
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-parse-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "createHTMLDocument", "parse-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-classic.html
new file mode 100644
index 0000000000..bc4deb6f17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "createHTMLDocument", "success", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-module.html
new file mode 100644
index 0000000000..ff76954f9d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-createHTMLDocument-success-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "createHTMLDocument", "success", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-classic.html
new file mode 100644
index 0000000000..768120e9d8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "fetch-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-module.html
new file mode 100644
index 0000000000..50dd80662d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-fetch-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "fetch-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-classic.html
new file mode 100644
index 0000000000..981a068230
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "parse-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-module.html
new file mode 100644
index 0000000000..6debb4189e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "parse-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-inline-classic.html
new file mode 100644
index 0000000000..53389f20db
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-parse-error-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "parse-error", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-classic.html
new file mode 100644
index 0000000000..9c4a12226a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "success", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-module.html
new file mode 100644
index 0000000000..0a0490cfc1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "success", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-inline-classic.html
new file mode 100644
index 0000000000..932825709f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-iframe-success-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("after-prepare", "iframe", "success", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-classic.html
new file mode 100644
index 0000000000..444382ac20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "fetch-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-module.html
new file mode 100644
index 0000000000..e1a1f7c08f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-fetch-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "fetch-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-classic.html
new file mode 100644
index 0000000000..6bb5ebddbd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "parse-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-module.html
new file mode 100644
index 0000000000..10a6549f62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "parse-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-classic.html
new file mode 100644
index 0000000000..28bd935995
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "parse-error", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-module.html
new file mode 100644
index 0000000000..e665a75629
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-parse-error-inline-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "parse-error", "inline", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-classic.html
new file mode 100644
index 0000000000..8ee0dd1de1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "success", "empty-src", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-module.html
new file mode 100644
index 0000000000..4791149c57
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-empty-src-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "success", "empty-src", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-classic.html
new file mode 100644
index 0000000000..3a3aceaf26
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "success", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-module.html
new file mode 100644
index 0000000000..c15b4fc77f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "success", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-classic.html
new file mode 100644
index 0000000000..576f4d4684
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "success", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-module.html
new file mode 100644
index 0000000000..c84d61c89a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-createHTMLDocument-success-inline-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "createHTMLDocument", "success", "inline", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-classic.html
new file mode 100644
index 0000000000..febf6fcc55
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "fetch-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-module.html
new file mode 100644
index 0000000000..f936260b8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-fetch-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "fetch-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-classic.html
new file mode 100644
index 0000000000..870c900abf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "parse-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-module.html
new file mode 100644
index 0000000000..fb44a89df1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "parse-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-classic.html
new file mode 100644
index 0000000000..986e4fa396
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "parse-error", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-module.html
new file mode 100644
index 0000000000..3a3fc38479
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-parse-error-inline-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "parse-error", "inline", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-classic.html
new file mode 100644
index 0000000000..4f03a94358
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "success", "empty-src", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-module.html
new file mode 100644
index 0000000000..a7bd42fd32
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-empty-src-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "success", "empty-src", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-classic.html
new file mode 100644
index 0000000000..08a8ac4afa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "success", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-module.html
new file mode 100644
index 0000000000..b8c3f79fea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "success", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-classic.html
new file mode 100644
index 0000000000..b639f6109d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "success", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-module.html
new file mode 100644
index 0000000000..616e46310f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/before-prepare-iframe-success-inline-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("before-prepare", "iframe", "success", "inline", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-classic.html
new file mode 100644
index 0000000000..745c62d898
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "createHTMLDocument", "fetch-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-module.html
new file mode 100644
index 0000000000..f6353a05fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-fetch-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "createHTMLDocument", "fetch-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-classic.html
new file mode 100644
index 0000000000..21099c3ff9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "createHTMLDocument", "parse-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-module.html
new file mode 100644
index 0000000000..2eb153acfe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-parse-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "createHTMLDocument", "parse-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-classic.html
new file mode 100644
index 0000000000..88821826ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "createHTMLDocument", "success", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-module.html
new file mode 100644
index 0000000000..f2a9d95741
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-createHTMLDocument-success-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "createHTMLDocument", "success", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-classic.html
new file mode 100644
index 0000000000..d96bfe0d50
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "fetch-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-module.html
new file mode 100644
index 0000000000..7e71bfbe15
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-fetch-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "fetch-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-classic.html
new file mode 100644
index 0000000000..757c879897
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "parse-error", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-module.html
new file mode 100644
index 0000000000..6ec6c5970e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "parse-error", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-inline-classic.html
new file mode 100644
index 0000000000..c0b0f9d404
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-parse-error-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "parse-error", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-classic.html
new file mode 100644
index 0000000000..7955dbce62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "success", "external", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-module.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-module.html
new file mode 100644
index 0000000000..af17eb01f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-external-module.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "success", "external", "module");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-inline-classic.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-inline-classic.html
new file mode 100644
index 0000000000..8a44e2feaf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/move-back-iframe-success-inline-classic.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("move-back", "iframe", "success", "inline", "classic");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/README.md b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/README.md
new file mode 100644
index 0000000000..dcf5597e2d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/README.md
@@ -0,0 +1,6 @@
+The tests in this directory checks side effects (other than script
+evaluation/event firing, which is covered by the tests in the parent directory)
+caused by scripts moved between Documents.
+
+The tests assume that script loading is not canceled when moved between
+documents (which is not explicitly specified as of Jan 2022).
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-1.html
new file mode 100644
index 0000000000..c5812765eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helper.js"></script>
+
+<body>
+<script>
+runDelayEventTest('Script elements (parser-blocking)');
+</script>
+
+<script id="to-be-moved" src="../../resources/throw.js?pipe=trickle(d3)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-2.html
new file mode 100644
index 0000000000..916673894d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helper.js"></script>
+
+<body>
+<script>
+runDelayEventTest('Script elements (async)');
+
+const script = document.createElement('script');
+script.setAttribute('id', 'to-be-moved');
+script.setAttribute('src', '../../resources/throw.js?pipe=trickle(d3)');
+document.body.appendChild(script);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-iframe.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-iframe.html
new file mode 100644
index 0000000000..efec7657c3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/delay-load-event-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<body onload="parent.onloadIframe()">
+<script src="../../resources/throw.js?pipe=trickle(d2)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/helper.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/helper.js
new file mode 100644
index 0000000000..cb5f45eb0d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/helper.js
@@ -0,0 +1,31 @@
+function runDelayEventTest(description) {
+ const t_original = async_test(description +
+ ' still delay the load event in the original Document after move');
+ const t_new = async_test(description +
+ ' does not delay the load event in the new Document after move');
+ const iframe = document.createElement('iframe');
+ iframe.setAttribute('src', 'delay-load-event-iframe.html');
+ const start_time = performance.now();
+ document.body.appendChild(iframe);
+
+ window.onload = t_original.step_func_done(() => {
+ // The `#to-be-moved` script should delay the load event until it is loaded
+ // (i.e. 3 seconds), not just until it is moved out to another Document
+ // (i.e. 1 second). Here we expect the delay should be at least 2 seconds,
+ // as the latency can be slightly less than 3 seconds due to preloading.
+ assert_greater_than(performance.now() - start_time, 2000,
+ 'Load event should be delayed until script is loaded');
+ });
+
+ window.onloadIframe = t_new.step_func_done(() => {
+ // The iframe's load event is fired after 2 seconds of its subresource
+ // loading, and shouldn't wait for the `#to-be-moved` script.
+ assert_less_than(performance.now() - start_time, 3000,
+ 'Load event should not be delayed until moved script is loaded');
+ });
+
+ t_original.step_timeout(() => {
+ const script = document.querySelector('#to-be-moved');
+ iframe.contentDocument.body.appendChild(script);
+ }, 1000);
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/in-order.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/in-order.html
new file mode 100644
index 0000000000..6a3e2b54a7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/in-order.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/C/#list-of-scripts-that-will-execute-in-order-as-soon-as-possible">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helper.js"></script>
+<body>
+<script>
+const t = async_test('Script elements (in-order) still block subsequent in-order scripts in the original Document after moved to another Document');
+const start_time = performance.now();
+const iframe = document.createElement('iframe');
+document.body.appendChild(iframe);
+
+const onScript2Evaluated = t.step_func_done(() => {
+ // `script1` should remain the
+ // #list-of-scripts-that-will-execute-in-order-as-soon-as-possible of the
+ // original Document and thus blocks `script2` evaluation until it is loaded.
+ assert_greater_than(performance.now() - start_time, 2000,
+ 'In-order scripts should block subsequent in-order scripts');
+});
+
+const script1 = document.createElement('script');
+script1.async = false;
+script1.setAttribute('src', '../../resources/throw.js?pipe=trickle(d2)');
+document.body.appendChild(script1);
+
+const script2 = document.createElement('script');
+script2.async = false;
+script2.setAttribute('src', 'data:text/javascript,onScript2Evaluated()');
+document.body.appendChild(script2);
+
+t.step_timeout(() => {
+ iframe.contentDocument.body.appendChild(script1);
+}, 1000);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/parser-blocking.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/parser-blocking.html
new file mode 100644
index 0000000000..9edde13736
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/parser-blocking.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/C/#pending-parsing-blocking-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helper.js"></script>
+<body>
+<script>
+const t = async_test('Script elements (parser-blocking) still block the parser in the original Document after moved to another Document');
+const start_time = performance.now();
+const iframe = document.createElement('iframe');
+document.body.appendChild(iframe);
+
+t.step_timeout(() => {
+ const script = document.querySelector('#to-be-moved');
+ iframe.contentDocument.body.appendChild(script);
+}, 1000);
+
+let syncScriptEvaluated = false;
+
+const onSyncScript = t.step_func(() => {
+ syncScriptEvaluated = true;
+
+ // The `#to-be-moved` script should block the parser and thus the sync
+ // script after `#to-be-moved` should be delayed until `#to-be-moved` is
+ // loaded (i.e. 3 seconds).
+ // Here we expect the delay should be at least 2 seconds,
+ // as the latency can be slightly less than 3 seconds due to preloading.
+ assert_greater_than(performance.now() - start_time, 2000,
+ 'Parser should be blocked until script is loaded');
+});
+
+document.addEventListener('DOMContentLoaded', t.step_func_done(() => {
+ assert_true(syncScriptEvaluated,
+ 'sync script should be evaluated before DOMContentLoaded');
+ assert_greater_than(performance.now() - start_time, 2000,
+ 'DOMContentLoaded event should be delayed until script is loaded');
+}));
+</script>
+<script id="to-be-moved" src="../../resources/throw.js?pipe=trickle(d3)"></script>
+<script src="data:text/javascript,onSyncScript()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-helper.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-helper.js
new file mode 100644
index 0000000000..de4af6ac10
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-helper.js
@@ -0,0 +1,214 @@
+"use strict";
+
+function createDocument(documentType, result, inlineOrExternal, type, hasBlockingStylesheet) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.src =
+ "resources/moving-between-documents-iframe.py" +
+ "?result=" + result +
+ "&inlineOrExternal=" + inlineOrExternal +
+ "&type=" + type +
+ "&hasBlockingStylesheet=" + hasBlockingStylesheet +
+ "&cache=" + Math.random();
+ // As blocking stylesheets delays Document load events, we use
+ // DOMContentLoaded here.
+ // After that point, we expect iframe.contentDocument exists
+ // while still waiting for blocking stylesheet loading.
+ document.body.appendChild(iframe);
+
+ window.addEventListener('message', (event) => {
+ if (documentType === "iframe") {
+ resolve([iframe.contentWindow, iframe.contentDocument]);
+ } else if (documentType === "createHTMLDocument") {
+ resolve([
+ iframe.contentWindow,
+ iframe.contentDocument.implementation.createHTMLDocument("")]);
+ } else {
+ reject(new Error("Invalid document type: " + documentType));
+ }
+ }, {once: true});
+ });
+}
+
+window.didExecute = undefined;
+
+// For a script, there are three associated Documents that can
+// potentially different:
+//
+// [1] script's parser document
+// https://html.spec.whatwg.org/C/#parser-document
+//
+// [2] script's preparation-time document
+// https://html.spec.whatwg.org/C/#preparation-time-document
+// == script's node document at the beginning of #prepare-a-script
+//
+// [3] script's node document at the beginning of
+// #execute-the-script-block
+//
+// This helper is for tests where [1]/[2]/[3] are different.
+
+// In the spec, scripts are executed only if [1]/[2]/[3] are all the same
+// (or [1] is null and [2]==[3]).
+//
+// A check for [1]==[2] is in #prepare-a-script and
+// a check for [1]==[3] is in #execute-the-script-block,
+// but these are under debate: https://github.com/whatwg/html/issues/2137
+//
+// A check for [2]==[3] is in #execute-the-script-block, which is added by
+// https://github.com/whatwg/html/pull/2673
+
+// timing:
+// "before-prepare":
+// A <script> is moved during parsing before #prepare-a-script.
+// [1] != [2] == [3]
+//
+// "after-prepare":
+// A <script> is moved after parsing/#prepare-a-script but
+// before #execute-the-script-block.
+// [1] == [2] != [3]
+//
+// To move such scripts, #has-a-style-sheet-that-is-blocking-scripts
+// is utilized to block inline scripts after #prepare-a-script.
+// Note: this is a corner case in the spec which might be removed
+// from the spec in the future, e.g.
+// https://github.com/whatwg/html/issues/1349
+// https://github.com/chrishtr/rendering/blob/master/stylesheet-loading-proposal.md
+//
+// TODO(domfarolino): Remove the "parsing but moved back" tests, because if a
+// <script> is moved before #prepare-a-script, per spec it should never make
+// it to #execute-the-script-block. If an implementation does not implement
+// the check in #prepare-a-script, then it will fail the "before-prepare"
+// tests, so these are not necessary.
+// "parsing but moved back"
+// A <script> is moved before #prepare-a-script, but moved back again
+// to the original Document after #prepare-a-script.
+// [1] == [3] != [2]
+//
+// destType: "iframe" or "createHTMLDocument".
+// result: "fetch-error", "parse-error", or "success".
+// inlineOrExternal: "inline" or "external" or "empty-src".
+// type: "classic" or "module".
+async function runTest(timing, destType, result, inlineOrExternal, type) {
+ const description =
+ `Move ${result} ${inlineOrExternal} ${type} script ` +
+ `to ${destType} ${timing}`;
+
+ const t = async_test("Eval: " + description);
+ const tScriptLoadEvent = async_test("<script> load: " + description);
+ const tScriptErrorEvent = async_test("<script> error: " + description);
+ const tWindowErrorEvent = async_test("window error: " + description);
+
+ // If scripts should be moved after #prepare-a-script before
+ // #execute-the-script-block, we add a style sheet that is
+ // blocking scripts.
+ const hasBlockingStylesheet =
+ timing === "after-prepare" || timing === "move-back";
+
+ const [sourceWindow, sourceDocument] = await createDocument(
+ "iframe", result, inlineOrExternal, type, hasBlockingStylesheet);
+
+ // Due to https://crbug.com/1034176, Chromium needs
+ // blocking stylesheets also in the destination Documents.
+ const [destWindow, destDocument] = await createDocument(
+ destType, null, null, null, hasBlockingStylesheet);
+
+ const scriptOnLoad =
+ tScriptLoadEvent.unreached_func("Script load event fired unexpectedly");
+ const scriptOnError = (event) => {
+ // For Firefox: Prevent window.onerror is fired due to propagation
+ // from <script>'s error event.
+ event.stopPropagation();
+
+ tScriptErrorEvent.unreached_func("Script error evennt fired unexpectedly")();
+ };
+
+ sourceWindow.didExecute = false;
+ sourceWindow.t = t;
+ sourceWindow.scriptOnLoad = scriptOnLoad;
+ sourceWindow.scriptOnError = scriptOnError;
+ sourceWindow.onerror = tWindowErrorEvent.unreached_func(
+ "Window error event shouldn't fired on source window");
+ sourceWindow.readyToEvaluate = false;
+
+ destWindow.didExecute = false;
+ destWindow.t = t;
+ destWindow.scriptOnLoad = scriptOnLoad;
+ destWindow.scriptOnError = scriptOnError;
+ destWindow.onerror = tWindowErrorEvent.unreached_func(
+ "Window error event shouldn't fired on destination window");
+ destWindow.readyToEvaluate = false;
+
+ // t=0 sec: Move between documents before #prepare-a-script.
+ // At this time, the script element is not yet inserted to the DOM.
+ if (timing === "before-prepare" || timing === "move-back") {
+ destDocument.body.appendChild(
+ sourceDocument.querySelector("streaming-element"));
+ }
+ if (timing === "before-prepare") {
+ sourceWindow.readyToEvaluate = true;
+ destWindow.readyToEvaluate = true;
+ }
+
+ // t=1 sec: the script element is inserted to the DOM, i.e.
+ // #prepare-a-script is triggered (see monving-between-documents-iframe.py).
+ // In the case of `before-prepare`, the script can be evaluated.
+ // In other cases, the script evaluation is blocked by a style sheet.
+ await new Promise(resolve => step_timeout(resolve, 2000));
+
+ // t=2 sec: Move between documents after #prepare-a-script.
+ if (timing === "after-prepare") {
+ // At this point, the script hasn't been moved yet, so we'll move it for the
+ // first time, after #prepare-a-script, but before #execute-the-script-block.
+ destDocument.body.appendChild(
+ sourceDocument.querySelector("streaming-element"));
+ } else if (timing === "move-back") {
+ // At this point the script has already been moved to the destination block
+ // before #prepare-a-script, so we'll move it back to the source document
+ // before #execute-the-script-block.
+ sourceDocument.body.appendChild(
+ destDocument.querySelector("streaming-element"));
+ }
+ sourceWindow.readyToEvaluate = true;
+ destWindow.readyToEvaluate = true;
+
+ // t=3 or 5 sec: Blocking stylesheet and external script are loaded,
+ // and thus script evaulation is unblocked.
+
+ // Note: scripts are expected to be loaded at t=3, because the fetch
+ // is started by #prepare-a-script at t=1, and the script's delay is
+ // 2 seconds. However in Chromium, due to preload scanner, the script
+ // loading might take 4 seconds, because the first request by preload
+ // scanner of the source Document takes 2 seconds (between t=1 and t=3)
+ // which blocks the second request by #prepare-a-script that takes
+ // another 2 seconds (between t=3 and t=5).
+
+ // t=6 sec: After all possible script evaluation points, test whether
+ // the script/events were evaluated/fired or not.
+ // As we have concurrent tests, a single global step_timeout() is
+ // used instead of multiple `t.step_timeout()` etc.,
+ // to avoid potential race conditions between `t.step_timeout()`s.
+ return new Promise(resolve => {
+ step_timeout(() => {
+ tWindowErrorEvent.done();
+ tScriptLoadEvent.done();
+ tScriptErrorEvent.done();
+
+ t.step_func_done(() => {
+ assert_false(sourceWindow.didExecute,
+ "The script must not have executed in source window");
+ assert_false(destWindow.didExecute,
+ "The script must not have executed in destination window");
+ })();
+ resolve();
+ }, 4000);
+ });
+}
+
+async_test(t => {
+ t.step_timeout(() => {
+ assert_equals(window.didExecute, undefined,
+ "The script must not have executed in the top-level window");
+ t.done();
+ },
+ 4000);
+}, "Sanity check around top-level Window");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-iframe.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-iframe.py
new file mode 100644
index 0000000000..dbcfe9b8d0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/moving-between-documents-iframe.py
@@ -0,0 +1,102 @@
+import random
+import time
+
+from wptserve.utils import isomorphic_decode
+
+
+"""
+This script serves
+"""
+
+def main(request, response):
+ inlineOrExternal = request.GET.first(b"inlineOrExternal", b"null")
+ hasBlockingStylesheet = request.GET.first(b"hasBlockingStylesheet", b"true") == b"true"
+ result = request.GET.first(b"result", b"success")
+ type = u"text/javascript" if request.GET.first(b"type", b"classic") == b"classic" else u"module"
+
+ response.headers.set(b"Content-Type", b"text/html; charset=utf-8")
+ response.headers.set(b"Transfer-Encoding", b"chunked")
+ response.write_status_headers()
+
+ # Step 1: Start parsing.
+ body = u"""<!DOCTYPE html>
+ <head>
+ <script>
+ parent.postMessage("fox", "*");
+ </script>
+ """
+
+ if hasBlockingStylesheet:
+ body += u"""
+ <link rel="stylesheet" href="slow-flag-setter.py?result=css&cache=%f">
+ """ % random.random()
+
+ body += u"""
+ </head>
+ <body>
+ """
+
+ if inlineOrExternal == b"inline" or inlineOrExternal == b"external" or inlineOrExternal == b"empty-src":
+ body += u"""
+ <streaming-element>
+ """
+
+ # Trigger DOM processing
+ body += u"A" * 100000
+
+ response.writer.write(u"%x\r\n" % len(body))
+ response.writer.write(body)
+ response.writer.write(u"\r\n")
+
+ body = u""
+
+ if inlineOrExternal == b"inline":
+ time.sleep(1)
+ body += u"""
+ <script id="s1" type="%s"
+ onload="scriptOnLoad()"
+ onerror="scriptOnError(event)">
+ if (!window.readyToEvaluate) {
+ window.didExecute = "executed too early";
+ } else {
+ window.didExecute = "executed";
+ }
+ """ % type
+ if result == b"parse-error":
+ body += u"1=2 parse error\n"
+
+ body += u"""
+ </script>
+ </streaming-element>
+ """
+ elif inlineOrExternal == b"external":
+ time.sleep(1)
+ body += u"""
+ <script id="s1" type="%s"
+ src="slow-flag-setter.py?result=%s&cache=%s"
+ onload="scriptOnLoad()"
+ onerror="scriptOnError(event)"></script>
+ </streaming-element>
+ """ % (type, isomorphic_decode(result), random.random())
+ elif inlineOrExternal == b"empty-src":
+ time.sleep(1)
+ body += u"""
+ <script id="s1" type="%s"
+ src=""
+ onload="scriptOnLoad()"
+ onerror="scriptOnError(event)"></script>
+ </streaming-element>
+ """ % (type,)
+
+ # // if readyToEvaluate is false, the script is probably
+ # // wasn't blocked by stylesheets as expected.
+
+ # Trigger DOM processing
+ body += u"B" * 100000
+
+ response.writer.write(u"%x\r\n" % len(body))
+ response.writer.write(body)
+ response.writer.write(u"\r\n")
+
+ response.writer.write(u"0\r\n")
+ response.writer.write(u"\r\n")
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/slow-flag-setter.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/slow-flag-setter.py
new file mode 100644
index 0000000000..20d7ed4bd0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/resources/slow-flag-setter.py
@@ -0,0 +1,29 @@
+
+import time
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+
+ result = request.GET.first(b"result", b"success")
+ if result == b"css":
+ time.sleep(3)
+ headers = [(b"Content-Type", b"text/css")]
+ body = u""
+ else:
+ time.sleep(2)
+
+ body = u"""
+ fetch('exec');
+ console.log('exec');
+ if (!window.readyToEvaluate) {
+ window.didExecute = "executed too early";
+ } else {
+ window.didExecute = "executed";
+ }
+ """
+ if result == b"parse-error":
+ body = u"1=2 parse error;"
+ if result == b"fetch-error":
+ return 404, [(b'Content-Type', b'text/plain')], u"""window.didExecute = "fetch error";"""
+
+ return headers, body
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/tools/generate.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/tools/generate.py
new file mode 100644
index 0000000000..80a655e821
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/moving-between-documents/tools/generate.py
@@ -0,0 +1,61 @@
+template = '''<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Moving script elements between documents</title>
+<!-- This is generated by tools/generate.py. Do not manually edit. -->
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#execute-the-script-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/moving-between-documents-helper.js"></script>
+
+<body>
+<script>
+runTest("%s", "%s", "%s", "%s", "%s");
+</script>
+'''
+
+n = 0
+for timing in ["before-prepare", "after-prepare", "move-back"]:
+ for destType in ["iframe", "createHTMLDocument"]:
+ for inlineOrExternal in ["inline", "external", "empty-src"]:
+ for result in ["fetch-error", "parse-error", "success"]:
+ for type in ["classic", "module"]:
+ # The |inlineOrExternal| keyword creates a certain kind of script,
+ # and the |result| keyword can influence the generated script in
+ # different ways i.e., giving the script a parse-error, or creating
+ # a script that fails to load. When we're creating an inline script,
+ # it doesn't make sense to test the fetch-error case, so we ignore
+ # this combination, as the server will not react to it in any
+ # meaningful way.
+ if inlineOrExternal == "inline" and result == "fetch-error":
+ continue
+
+ if inlineOrExternal == "empty-src":
+ # The "empty-src" tests aim to exercise #prepare-a-script step 26
+ # substep 2, where the <script> has a src attribute that is empty:
+ # "If src is the empty string, queue a task to fire an event named
+ # error at the element, and return."
+ # Therefore, the server will generate a script that does not have a
+ # "parse-error" or "fetch-error", so we can ignore these combinations.
+ if result != "success":
+ continue
+
+ # The "empty-src" tests check that the parser document <=> node document
+ # check is implemented correctly in #prepare-a-script. Therefore we're
+ # only interested in tests that move the <script> before #prepare-a-script.
+ if timing != "before-prepare":
+ continue
+
+ # The current test helper uses
+ # #has-a-style-sheet-that-is-blocking-scripts to block script
+ # evaluation after #prepare-a-script, but in some cases this
+ # doesn't work:
+ # - inline scripts to createHTMLDocument
+ if timing != "before-prepare" and destType == "createHTMLDocument" and inlineOrExternal == "inline":
+ continue
+ # - module inline scripts https://github.com/whatwg/html/issues/3890
+ if timing != "before-prepare" and inlineOrExternal == "inline" and type == "module":
+ continue
+
+ with open('%s-%s-%s-%s-%s.html' % (timing, destType, result, inlineOrExternal, type), 'w') as f:
+ f.write(template % (timing, destType, result, inlineOrExternal, type))
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html
new file mode 100644
index 0000000000..255e79e191
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="cacheable-script-throw.py?iframe"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors.sub.html
new file mode 100644
index 0000000000..61643c5f08
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/muted-errors.sub.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<title>Muted Errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// https://html.spec.whatwg.org/multipage/webappapis.html#report-the-error
+// If script's muted errors is true, then set message to "Script error.",
+// urlString to the empty string, line and col to 0, and errorValue to null.
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+ window.addEventListener("error", ev => log.push(ev));
+
+ function check(shouldBeMuted) {
+ assert_equals(log.length, 1);
+ var ev = log[0];
+ log = [];
+ if (shouldBeMuted) {
+ assert_equals(ev.message, "Script error.");
+ assert_equals(ev.error, null, 'error');
+ assert_equals(ev.filename, "", 'filename');
+ assert_equals(ev.lineno, 0, 'lineno');
+ assert_equals(ev.colno, 0, 'colno');
+ } else {
+ assert_not_equals(ev.message, "Script error.");
+ assert_not_equals(ev.error, null);
+ }
+ }
+
+ var test1 = async_test("Errors for same-origin script shouldn't be muted");
+ var check1 = test1.step_func_done(() => check(false));
+
+ var test2 = async_test("Errors for cross-origin script should be muted");
+ var check2 = test2.step_func_done(() => check(true));
+
+ var test3 = async_test("Errors for cross-origin script should be muted " +
+ "even if the script is once loaded as same-origin");
+ function step3() {
+ var script = document.createElement('script');
+ script.setAttribute('src', "//{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py?iframe");
+ script.onerror = test3.unreached_func();
+ script.onload = test3.step_func_done(() => check(true));
+ document.body.appendChild(script);
+ }
+
+ var test4 = async_test("Errors for same-origin scripts redirected to a " +
+ "cross-origin url and redirected back to " +
+ "same-origin should be muted");
+ var check4 = test4.step_func_done(() => check(true));
+
+ var test5 = async_test("Errors for cross-origin scripts redirected to a " +
+ "same-origin url should be muted");
+ var check5 = test5.step_func_done(() => check(true));
+
+ const test6 = async_test("Non-synthetic errors for same-origin scripts redirected to a " +
+ "cross-origin URL and redirected back to same-origin should be " +
+ "muted");
+ const check6 = test6.step_func_done(() => check(true));
+
+ const test7 = async_test("Syntax error for same-origin script redirected to a " +
+ "cross-origin URL and redirected back to same-origin should be " +
+ "muted");
+ const check7 = test7.step_func_done(() => check(true));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script src="cacheable-script-throw.py" onerror="test1.unreached_func()()" onload="check1()"></script>
+<script src="//{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py"
+ onerror="test2.unreached_func()()" onload="check2()"></script>
+<iframe src="//{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html"
+ onerror="test3.unreached_func()()" onload="step3()"></iframe>
+<script src="/fetch/api/resources/redirect.py?location=
+//{{domains[www2]}}:{{ports[http][0]}}/fetch/api/resources/redirect.py?location=
+//{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py?same-cross-same"
+onerror="test4.unreached_func()()" onload="check4()"></script>
+<script src="//{{domains[www2]}}:{{ports[http][0]}}/fetch/api/resources/redirect.py?location=
+//{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py?cross-same"
+onerror="test5.unreached_func()()" onload="check5()"></script>
+<script src="//{{domains[www2]}}:{{ports[http][0]}}/fetch/api/resources/redirect.py?location=
+//{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/resources/throw.js"
+onerror="test6.unreached_func()()" onload="check6()"></script>
+<script src="//{{domains[www2]}}:{{ports[http][0]}}/fetch/api/resources/redirect.py?location=
+//{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/resources/syntax-error.js"
+onerror="test7.unreached_func()()" onload="check7()"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-reflect.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-reflect.html
new file mode 100644
index 0000000000..d6a850f58f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-reflect.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>noModule IDL attribute must reflect nomodule content attribute</title>
+<link rel="author" title="Yusuke Suzuki" href="mailto:utatane.tea@gmail.com">
+<link rel="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script id="classicWithoutNomodule"></script>
+<script id="classicWithNomodule" nomodule></script>
+<script id="moduleWithoutNomodule" type=module></script>
+<script id="moduleWithNomodule" type=module nomodule></script>
+<script>
+
+test(() => {
+ assert_false(document.getElementById('classicWithoutNomodule').noModule);
+}, 'noModule IDL attribute on a parser created classic script element without nomodule content attribute');
+
+test(() => {
+ assert_true(document.getElementById('classicWithNomodule').noModule);
+}, 'noModule IDL attribute on a parser created classic script element with nomodule content attribute');
+
+test(() => {
+ assert_false(document.getElementById('moduleWithoutNomodule').noModule);
+}, 'noModule IDL attribute on a parser created module script element without nomodule content attribute');
+
+test(() => {
+ assert_true(document.getElementById('moduleWithNomodule').noModule);
+}, 'noModule IDL attribute on a parser created module script element with nomodule content attribute');
+
+
+test(() => {
+ const script = document.createElement('script');
+ assert_false(script.noModule);
+}, 'noModule IDL attribute on a dynamically created script element without nomodule content attribute');
+
+test(() => {
+ const script = document.createElement('script');
+ script.setAttribute('nomodule', 'nomodule');
+ assert_true(script.noModule);
+}, 'noModule IDL attribute on a dynamically created script element after nomodule content attribute is set to "nomodule"');
+
+test(() => {
+ const script = document.createElement('script');
+ script.setAttribute('nomodule', '');
+ assert_true(script.noModule);
+}, 'noModule IDL attribute on a dynamically created script element after nomodule content attribute is set to ""');
+
+test(() => {
+ const script = document.createElement('script');
+ script.setAttribute('nomodule', 'nomodule');
+ assert_true(script.noModule);
+ script.removeAttribute('nomodule');
+ assert_false(script.noModule);
+}, 'noModule IDL attribute on a dynamically created script element after nomodule content attribute had been removed');
+
+test(() => {
+ const script = document.createElement('script');
+ assert_false(script.hasAttribute('nomodule'));
+ script.noModule = true;
+ assert_true(script.hasAttribute('nomodule'));
+}, 'noModule IDL attribute must add nomodule content attribute on setting to true');
+
+test(() => {
+ const script = document.createElement('script');
+ script.setAttribute('nomodule', 'nomodule');
+ script.noModule = false;
+ assert_false(script.hasAttribute('nomodule'));
+}, 'noModule IDL attribute must remove nomodule content attribute on setting to false');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-async-classic-script.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-async-classic-script.html
new file mode 100644
index 0000000000..25de796830
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-async-classic-script.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>External classic scripts with nomodule content attribute must not run</title>
+<link rel="author" title="Yusuke Suzuki" href="mailto:utatane.tea@gmail.com">
+<link rel="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- Load this script synchronously to ensure test cases below can load it in 200ms -->
+<script src="resources/set-script-executed.js"></script>
+</head>
+<body>
+<script>
+let supportsNoModule = "noModule" in document.getElementsByTagName("script")[0];
+
+waitForLoadEvent = new Promise((resolve) => {
+ window.onload = resolve;
+});
+
+promise_test(() => {
+ window.executed = false;
+ let loaded = false;
+ let errored = false;
+
+ let script = document.createElement('script');
+
+ script.src = './resources/set-script-executed.js';
+ script.onload = () => loaded = true;
+ script.onerror = () => errored = true;
+ script.noModule = false;
+ document.body.appendChild(script);
+
+ return waitForLoadEvent.then(() => {
+ assert_true(supportsNoModule);
+ assert_true(executed);
+ assert_true(loaded);
+ assert_false(errored);
+ });
+}, 'An asynchronously loaded classic script with noModule set to false must run');
+
+promise_test(() => {
+ window.executed = false;
+ let loaded = false;
+ let errored = false;
+
+ let script = document.createElement('script');
+ script.src = './resources/set-script-executed.js';
+ script.onload = () => loaded = true;
+ script.onerror = () => errored = true;
+ script.noModule = true;
+ document.body.appendChild(script);
+
+ return waitForLoadEvent.then(() => {
+ assert_true(supportsNoModule);
+ assert_false(executed);
+ assert_false(loaded);
+ assert_false(errored);
+ });
+}, 'An asynchronously loaded classic script with noModule set to true must not run');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-external-module-script.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-external-module-script.html
new file mode 100644
index 0000000000..138b33c9fa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-external-module-script.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>An external module script with nomodule must run</title>
+<link rel="author" title="Yusuke Suzuki" href="mailto:utatane.tea@gmail.com">
+<link rel="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script nomodule type="module" src="./resources/exports-cocoa.js"></script>
+<script>
+
+waitForLoadEvent = new Promise((resolve) => {
+ window.onload = resolve;
+});
+
+promise_test(() => {
+ return waitForLoadEvent.then(() => {
+ assert_equals(typeof cocoa, 'undefined');
+ assert_equals(typeof exportedCocoa, 'object');
+ assert_equals(exportedCocoa.taste(), 'awesome');
+ });
+}, 'An external module script with nomodule content attribute must run');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-classic-scripts.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-classic-scripts.html
new file mode 100644
index 0000000000..588d59975c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-classic-scripts.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Inline classic scripts with nomodule content attribute must not run</title>
+<link rel="author" title="Yusuke Suzuki" href="mailto:utatane.tea@gmail.com">
+<link rel="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+window.executed = true;
+</script>
+<script>
+
+test(() => {
+ assert_true(executed);
+}, 'An inline classic script without nomodule content attribute must run');
+
+
+window.executed = false;
+</script>
+<script nomodule>
+window.executed = true;
+</script>
+<script>
+
+test(() => {
+ assert_false(executed);
+}, 'An inline classic script with nomodule content attribute must not run');
+
+</script>
+<script>
+
+test(() => {
+ window.executed = false;
+ const element = document.createElement("script");
+ element.noModule = false;
+ element.textContent = `window.executed = true`;
+ document.body.appendChild(element);
+ assert_true(window.executed);
+}, 'An inline classic script element dynamically inserted after noModule was set to false must run.');
+
+test(() => {
+ window.executed = false;
+ const element = document.createElement("script");
+ element.noModule = true;
+ element.textContent = `window.executed = true`;
+ document.body.appendChild(element);
+ assert_false(window.executed);
+}, 'An inline classic script element dynamically inserted after noModule was set to true must not run.');
+
+window.executed = false;
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-module-script.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-module-script.html
new file mode 100644
index 0000000000..b11c25932e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-inline-module-script.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>An inline module script with nomodule must run</title>
+<link rel="author" title="Yusuke Suzuki" href="mailto:utatane.tea@gmail.com">
+<link rel="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script nomodule type="module">
+import Cocoa from "./resources/cocoa-module.js";
+var cocoa = new Cocoa();
+window.exportedCocoa = cocoa;
+</script>
+<script>
+
+waitForLoadEvent = new Promise((resolve) => {
+ window.onload = resolve;
+});
+
+promise_test(() => {
+ return waitForLoadEvent.then(() => {
+ assert_equals(typeof cocoa, 'undefined');
+ assert_equals(typeof exportedCocoa, 'object');
+ assert_equals(exportedCocoa.taste(), 'awesome');
+ });
+}, 'An inline module script with nomodule content attribute must run');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-synchronously-loaded-classic-scripts.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-synchronously-loaded-classic-scripts.html
new file mode 100644
index 0000000000..9f6207c9a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/nomodule-set-on-synchronously-loaded-classic-scripts.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>External classic scripts with nomodule content attribute must not run</title>
+<link rel="author" title="Yusuke Suzuki" href="mailto:utatane.tea@gmail.com">
+<link rel="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+
+window.executed = false;
+window.loaded = false;
+window.errored = false;
+</script>
+<script src="./resources/set-script-executed.js" onload="loaded = true" onerror="errored = false"></script>
+<script>
+
+test(() => {
+ assert_true(executed);
+ assert_true(loaded);
+ assert_false(errored);
+}, 'A synchronously loaded external classic script without nomodule content attribute must run');
+
+window.executed = false;
+window.loaded = false;
+window.errored = false;
+</script>
+<script nomodule src="./resources/set-script-executed.js" onload="loaded = true" onerror="errored = false"></script>
+<script>
+
+test(() => {
+ assert_false(executed);
+ assert_false(loaded);
+ assert_false(errored);
+}, 'A synchronously loaded external classic script with nomodule content attribute must not run');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/promise-reject-and-remove.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/promise-reject-and-remove.html
new file mode 100644
index 0000000000..a3b2730e56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/promise-reject-and-remove.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = t.step_func_done();
+}, 'Removing iframe in promise reject handler should not crash');
+</script>
+<iframe src="resources/promise-reject-and-remove-iframe.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16be.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16be.js
new file mode 100644
index 0000000000..8a529e10b1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16be.js
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16le.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16le.js
new file mode 100644
index 0000000000..578f7f1951
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-16le.js
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-8.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-8.js
new file mode 100644
index 0000000000..fb88bdda2a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/bom-utf-8.js
@@ -0,0 +1,2 @@
+// JavaScript file with UTF-8 BOM.
+window.executed_utf8_bom = '三æ‘ã‹ãªå­';
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cocoa-module.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cocoa-module.js
new file mode 100644
index 0000000000..24360a7bfe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cocoa-module.js
@@ -0,0 +1,5 @@
+export default class Cocoa {
+ taste() {
+ return "awesome";
+ }
+};
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py
new file mode 100644
index 0000000000..abac1901b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/cross-origin.py
@@ -0,0 +1,20 @@
+def main(request, response):
+ origin = request.headers.get(b"origin")
+
+ if origin is not None:
+ response.headers.set(b"Access-Control-Allow-Origin", origin)
+ response.headers.set(b"Access-Control-Allow-Methods", b"GET")
+ response.headers.set(b"Access-Control-Allow-Credentials", b"true")
+
+ if request.method == u"OPTIONS":
+ return u""
+
+ headers = [(b"Content-Type", b"text/javascript")]
+ milk = request.cookies.first(b"milk", None)
+
+ if milk is None:
+ return headers, u"var included = false;"
+ elif milk.value == b"yes":
+ return headers, u"var included = true;"
+
+ return headers, u"var included = false;"
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/exports-cocoa.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/exports-cocoa.js
new file mode 100644
index 0000000000..02967bc631
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/exports-cocoa.js
@@ -0,0 +1,3 @@
+import Cocoa from "./cocoa-module.js";
+var cocoa = new Cocoa();
+window.exportedCocoa = cocoa;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/flag-setter.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/flag-setter.js
new file mode 100644
index 0000000000..3274e5bfeb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/flag-setter.js
@@ -0,0 +1,3 @@
+"use strict";
+
+window.didExecute = true;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events-helpers.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events-helpers.js
new file mode 100644
index 0000000000..bbd6b09c6c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events-helpers.js
@@ -0,0 +1,47 @@
+"use strict";
+// Helper functions to be used from load-error-events*.html tests.
+
+function event_test(name, load_to_be_fired, error_to_be_fired) {
+ return {
+ test: async_test(name),
+ executed: false,
+ load_event_to_be_fired: load_to_be_fired,
+ error_event_to_be_fired: error_to_be_fired
+ };
+}
+
+// Should be used as load/error event handlers of script tags,
+// with |t| = the object returned by event_test().
+function onLoad(t) {
+ t.test.step(function() {
+ if (t.load_event_to_be_fired) {
+ assert_true(t.executed,
+ 'Load event should be fired after script execution');
+ // Delay done() a little so that if an error event happens
+ // the assert_unreached is reached and fails the test.
+ t.test.step_timeout(() => t.test.done(), 100);
+ } else {
+ assert_unreached('Load event should not be fired.');
+ }
+ });
+};
+function onError(t) {
+ t.test.step(function() {
+ if (t.error_event_to_be_fired) {
+ assert_false(t.executed);
+ // Delay done() a little so that if a load event happens
+ // the assert_unreached is reached and fails the test.
+ t.test.step_timeout(() => t.test.done(), 100);
+ } else {
+ assert_unreached('Error event should not be fired.');
+ }
+ });
+};
+
+// To be called from inline scripts, which expect no load/error events.
+function onExecute(t) {
+ t.executed = true;
+ // Delay done() a little so that if a load/error event happens
+ // the assert_unreached is reached and fails the test.
+ t.test.step_timeout(() => t.test.done(), 100);
+}
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events.py
new file mode 100644
index 0000000000..1eb82cd497
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/load-error-events.py
@@ -0,0 +1,15 @@
+import re
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ test = request.GET.first(b'test')
+ assert(re.match(b'^[a-zA-Z0-9_]+$', test))
+
+ if test.find(b'_load') >= 0:
+ status = 200
+ content = b'"use strict"; %s.executed = true;' % test
+ else:
+ status = 404
+ content = b'"use strict"; %s.test.step(function() { assert_unreached("404 script should not be executed"); });' % test
+
+ return status, headers, content
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/promise-reject-and-remove-iframe.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/promise-reject-and-remove-iframe.html
new file mode 100644
index 0000000000..6da274469f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/promise-reject-and-remove-iframe.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+const promise = Promise.reject();
+
+window.onload = () => {
+ promise.catch(() => parent.document.querySelector('iframe').remove());
+};
+</script>
+
+<!-- Load a slow script to delay window.onload for a while.
+ Without this, crashes are flaky. -->
+<script src="/common/slow.py"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/script-type-and-language-js.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/script-type-and-language-js.js
new file mode 100644
index 0000000000..d357bc4994
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/script-type-and-language-js.js
@@ -0,0 +1,141 @@
+function testAttribute(attr, val, shouldRun) {
+ test(function() {
+ assert_false(window.ran, "ran variable not reset");
+ let script;
+ if (document.contentType === 'image/svg+xml') {
+ // SVG
+ script = document.createElementNS("http://www.w3.org/2000/svg", "script");
+ } else {
+ // HTML or XHTML
+ script = document.createElement("script");
+ }
+ script.setAttribute(attr, val);
+ script.textContent = "window.ran = true;";
+ document.querySelector('#script-placeholder').appendChild(script);
+ assert_equals(window.ran, shouldRun);
+ }, "Script should" + (shouldRun ? "" : "n't") + " run with " + attr + "=" +
+ format_value(val));
+ window.ran = false;
+}
+function testTypeShouldRun(type) {
+ testAttribute("type", type, true);
+}
+function testLanguageShouldRun(lang) {
+ testAttribute("language", lang, true);
+}
+function testTypeShouldNotRun(type) {
+ testAttribute("type", type, false);
+}
+function testLanguageShouldNotRunUnlessSVG(lang) {
+ // In SVGs, there is no concrete spec but all browsers agree that
+ // language attributes have no effects and thus script elements
+ // without type attributes are always expected to run regardless of
+ // language attributes.
+ const expectedToRun = document.contentType === 'image/svg+xml';
+ testAttribute("language", lang, expectedToRun);
+}
+
+// Unlike `test*()` methods above, there should be a (parser-inserted) script
+// with an invalid type/language that would set `window.ran` to true just
+// before `testParserInsertedDidNotRun()`, and
+// `testParserInsertedDidNotRun()` asserts that the script did not run.
+// `window.ran` should be reset where needed. For example:
+// <script>window.ran = false;</script>
+// <script type="invalid-type">window.ran = true;</script>
+// <script>testParserInsertedDidNotRun('type=invalid-type');</script>
+function testParserInsertedDidNotRun(description) {
+ test(() => assert_false(window.ran),
+ "Script shouldn't run with " + description + " (parser-inserted)");
+ window.ran = false;
+}
+
+// When prefixed by "application/", these match with
+// https://mimesniff.spec.whatwg.org/#javascript-mime-type
+const application = [
+ "ecmascript",
+ "javascript",
+ "x-ecmascript",
+ "x-javascript"
+];
+
+// When prefixed by "text/", these match with
+// https://mimesniff.spec.whatwg.org/#javascript-mime-type
+const text = [
+ "ecmascript",
+ "javascript",
+ "javascript1.0",
+ "javascript1.1",
+ "javascript1.2",
+ "javascript1.3",
+ "javascript1.4",
+ "javascript1.5",
+ "jscript",
+ "livescript",
+ "x-ecmascript",
+ "x-javascript"
+];
+
+const legacyTypes = [
+ "javascript1.6",
+ "javascript1.7",
+ "javascript1.8",
+ "javascript1.9"
+];
+
+const spaces = [" ", "\t", "\n", "\r", "\f"];
+
+window.ran = false;
+
+// Type attribute
+
+testTypeShouldRun("");
+testTypeShouldNotRun(" ");
+
+application.map(t => "application/" + t).forEach(testTypeShouldRun);
+application.map(t => ("application/" + t).toUpperCase()).forEach(
+ testTypeShouldRun);
+
+spaces.forEach(function(s) {
+ application.map(t => "application/" + t + s).forEach(testTypeShouldRun);
+ application.map(t => s + "application/" + t).forEach(testTypeShouldRun);
+});
+
+application.map(t => "application/" + t + "\0").forEach(testTypeShouldNotRun);
+application.map(t => "application/" + t + "\0foo").forEach(
+ testTypeShouldNotRun);
+
+text.map(t => "text/" + t).forEach(testTypeShouldRun);
+text.map(t => ("text/" + t).toUpperCase()).forEach(testTypeShouldRun);
+
+legacyTypes.map(t => "text/" + t).forEach(testTypeShouldNotRun);
+
+spaces.forEach(function(s) {
+ text.map(t => "text/" + t + s).forEach(testTypeShouldRun);
+ text.map(t => s + "text/" + t).forEach(testTypeShouldRun);
+});
+
+text.map(t => "text/" + t + "\0").forEach(testTypeShouldNotRun);
+text.map(t => "text/" + t + "\0foo").forEach(testTypeShouldNotRun);
+
+text.forEach(testTypeShouldNotRun);
+legacyTypes.forEach(testTypeShouldNotRun);
+
+// Language attribute
+
+testLanguageShouldRun("");
+testLanguageShouldNotRunUnlessSVG(" ");
+
+text.forEach(testLanguageShouldRun);
+text.map(t => t.toUpperCase()).forEach(testLanguageShouldRun);
+
+legacyTypes.forEach(testLanguageShouldNotRunUnlessSVG);
+
+spaces.forEach(function(s) {
+ text.map(t => t + s).forEach(testLanguageShouldNotRunUnlessSVG);
+ text.map(t => s + t).forEach(testLanguageShouldNotRunUnlessSVG);
+});
+text.map(t => t + "xyz").forEach(testLanguageShouldNotRunUnlessSVG);
+text.map(t => "xyz" + t).forEach(testLanguageShouldNotRunUnlessSVG);
+
+text.map(t => t + "\0").forEach(testLanguageShouldNotRunUnlessSVG);
+text.map(t => t + "\0foo").forEach(testLanguageShouldNotRunUnlessSVG);
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/set-script-executed.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/set-script-executed.js
new file mode 100644
index 0000000000..a6095097dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/set-script-executed.js
@@ -0,0 +1 @@
+window.executed = true;
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/syntax-error.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/syntax-error.js
new file mode 100644
index 0000000000..40dc81dcfa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/syntax-error.js
@@ -0,0 +1 @@
+This cannot be parsed as JavaScript
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/throw.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/throw.js
new file mode 100644
index 0000000000..be53dd1ef3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/resources/throw.js
@@ -0,0 +1 @@
+document.querySelector(":::not-going-to-be-valid");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-01.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-01.html
new file mode 100644
index 0000000000..c5ac0d0a62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-01.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>Script @type: unknown parameters</title>
+ <link rel="author" title="askalski" href="github.com/askalski">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <div id="log"></div>
+
+ <!-- "Step1" tests -->
+ <!-- charset is set incorrectly via Content Type "text/javascript;charset=utf-8" in response
+ which has priority before a correct setting in "charset" attribute of script tag.
+ -->
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript%3Bcharset=utf-8" charset="windows-1250">
+ </script>
+ <script>
+ test(function() {
+ //these strings should not match, since the file charset is set incorrectly
+ assert_not_equals(window.getSomeString(), "śćążź");
+ });
+ </script>
+ <!-- charset is set correctly via Content Type "text/javascript;charset=utf-8" in response
+ which has priority before a incorrect setting in "charset" attribute of script tag.
+ -->
+
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript%3Bcharset=windows-1250" charset="utf-8">
+ </script>
+ <script>
+ //the charset is set correctly via Content Type "text/javascript;charset=windows-1250" in respones
+ test(function() {
+ assert_equals(window.getSomeString(), "śćążź");
+ });
+ </script>
+
+ <!-- end of step1 tests, now step2 tests -->
+ <!-- in this case, the response's Content Type does not bring charset information.
+ Second step takes block character encoding if available.-->
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript" charset="utf-8">
+ </script>
+ <script>
+ test(function() {
+ //these strings should not match, since the file charset is set incorrectly in "charset" tag of <script> above
+ assert_not_equals(window.getSomeString(), "śćążź");
+ });
+ </script>
+ <!-- charset is set correctly via Content Type "text/javascript;charset=utf-8" in response
+ which has priority before a incorrect setting in "charset" attribute of script tag.
+ -->
+
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript" charset="windows-1250">
+ </script>
+ <script>
+ //the charset is set correctly via content attribute in <script> above
+ test(function() {
+ assert_equals(window.getSomeString(), "śćążź");
+ });
+ </script>
+
+ <!-- end of step2 tests, now step3 tests -->
+ <!-- in this case, neither response's Content Type nor charset attribute bring correct charset information.
+ Third step takes this document's character encoding (declared correctly as UTF-8).-->
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript">
+ </script>
+ <script>
+ test(function() {
+ //these strings should not match, since the tested file is in windows-1250, and document is utf-8
+ assert_not_equals(window.getSomeString(), "śćążź");
+ });
+ </script>
+
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript">
+ </script>
+ <script>
+ //these strings should match, both document and tested file are utf-8
+ test(function() {
+ assert_equals(window.getSomeString(), "śćążź");
+ });
+ </script>
+
+ <!-- the last portion of tests (step4) are in file script-charset-02.html
+
+</head>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-02.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-02.html
new file mode 100644
index 0000000000..63cbe838e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-02.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<head>
+ <title>Script encoding for document encoding windows-1250</title>
+ <link rel="author" title="askalski" href="github.com/askalski">
+ <link rel="author" title="Aaqa Ishtyaq" href="github.com/aaqaishtyaq">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <div id="log"></div>
+ <!-- to avoid conflating tests for script encoding declaring the encoding at the top of file. i.e, windows-1250-->
+ <meta charset="windows-1250">
+ <script>
+ test(function() {
+ assert_equals(document.characterSet, "windows-1250")
+ }, "assumption: document encoding is windows-1250");
+ </script>
+
+ <!-- in this case, neither response's Content Type nor charset attribute bring correct charset information.
+ -->
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-windows1250.js&ct=text/javascript">
+ </script>
+
+ <script>
+ test(function() {
+ //these string should match since, windows-1250 is the fallback encoding.
+ assert_equals(window.getSomeString(), "\u015b\u0107\u0105\u017c\u017a");
+ }, "windows-1250 script decoded using document encoding (also windows-1250)");
+ </script>
+
+ <script type="text/javascript"
+ src="serve-with-content-type.py?fn=external-script-utf8.js&ct=text/javascript">
+ </script>
+ <script>
+ //these strings should match, since this string is the result of decoding the utf-8 text as windows-1250.
+ test(function() {
+ assert_equals(window.getSomeString(), "\u0139\u203a\xc4\u2021\xc4\u2026\u0139\u013d\u0139\u015f");
+ }, "UTF-8 script decoded using document encoding (windows-1250)");
+ </script>
+
+</head>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-03.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-03.html
new file mode 100644
index 0000000000..4ff4cc6b0b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-charset-03.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<title>Script changing @charset</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function() {
+ var s = document.createElement("script");
+ s.src = "external-script-windows1250.js";
+ s.charset = "windows-1250";
+ document.body.appendChild(s);
+ s.charset = "utf-8";
+ window.onload = this.step_func_done(function() {
+ assert_equals(window.getSomeString(), "\u015b\u0107\u0105\u017c\u017a");
+ });
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.sub.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.sub.html
new file mode 100644
index 0000000000..5886ab6f32
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin-network.sub.html
@@ -0,0 +1,120 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTMLScriptElement: crossorigin attribute network test</title>
+<link rel="author" title="KiChjang" href="mailto:kungfukeith11@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#cors-settings-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+ <script type="text/javascript">
+ var test1 = async_test("same-origin use-credentials");
+ var test2 = async_test("same-origin invalid");
+ var test3 = async_test("same-origin missing");
+ var test4 = async_test("cross-origin use-credentials");
+ var test5 = async_test("cross-origin invalid");
+ var test6 = async_test("cross-origin missing");
+ var test7 = async_test("cross-origin use-credentials mixed case");
+ var test8 = async_test("cross-origin use-credentials non-ASCII");
+
+ var same = "resources/cross-origin.py";
+ var cross = new URL(same, location);
+ cross.port = {{ports[http][1]}};
+
+ var script1 = document.createElement("script");
+ script1.src = same;
+ script1.crossOrigin = "use-credentials";
+
+ var script2 = document.createElement("script");
+ script2.src = same;
+ script2.crossOrigin = "gibberish";
+
+ var script3 = document.createElement("script");
+ script3.src = same;
+
+ var script4 = document.createElement("script");
+ script4.src = cross;
+ script4.crossOrigin = "use-credentials";
+
+ var script5 = document.createElement("script");
+ script5.src = cross;
+ script5.crossOrigin = "gibberish";
+
+ var script6 = document.createElement("script");
+ script6.src = cross;
+
+ var script7 = document.createElement("script");
+ script7.src = cross;
+ script7.crossOrigin = "UsE-cReDenTiAlS";
+
+ var script8 = document.createElement("script");
+ script8.src = cross;
+ script8.crossOrigin = "uſe-credentialſ";
+
+ document.cookie = "milk=yes";
+
+ document.body.appendChild(script1);
+ script1.onload = function() {
+ test1.step(function() {
+ assert_true(included, "credentials included (credentialsMode include)");
+ test1.done();
+ });
+ };
+
+ document.body.appendChild(script2);
+ script2.onload = function() {
+ test2.step(function() {
+ assert_true(included, "credentials included (credentialsMode same-origin)");
+ test2.done();
+ });
+ };
+
+ document.body.appendChild(script3);
+ script3.onload = function() {
+ test3.step(function() {
+ assert_true(included, "credentials included (credentialsMode include)");
+ test3.done();
+ });
+ };
+
+ document.body.appendChild(script4);
+ script4.onload = function() {
+ test4.step(function() {
+ assert_true(included, "credentials included (credentialsMode include)");
+ test4.done();
+ });
+ };
+
+ document.body.appendChild(script5);
+ script5.onload = function() {
+ test5.step(function() {
+ assert_false(included, "credentials excluded (credentialsMode same-origin)");
+ test5.done();
+ });
+ };
+
+ document.body.appendChild(script6);
+ script6.onload = function() {
+ test6.step(function() {
+ assert_true(included, "credentials included (credentialsMode include)");
+ test6.done();
+ });
+ };
+
+ document.body.appendChild(script7);
+ script7.onload = function() {
+ test7.step(function() {
+ assert_true(included, "credentials included (credentialsMode include)");
+ test7.done();
+ });
+ };
+
+ document.body.appendChild(script8);
+ script8.onload = function() {
+ test8.step(function() {
+ assert_false(included, "credentials excluded (credentialsMode same-origin)");
+ test8.done();
+ });
+ };
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html
new file mode 100644
index 0000000000..52857a08ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-crossorigin.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>HTMLScriptElement: crossOrigin IDL attribute</title>
+<link rel="author" title="KiChjang" href="mailto:kungfukeith11@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#cors-settings-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script id="script1"></script>
+<script id="script2" crossorigin=""></script>
+<script id="script3" crossorigin="foo"></script>
+<script id="script4" crossorigin="anonymous"></script>
+<script id="script5" crossorigin="use-credentials"></script>
+<script>
+test(function() {
+ var script1 = document.getElementById("script1");
+ var script2 = document.getElementById("script2");
+ var script3 = document.getElementById("script3");
+ var script4 = document.getElementById("script4");
+ var script5 = document.getElementById("script5");
+
+ assert_equals(script1.crossOrigin, null, "Missing value default should be null");
+ assert_equals(script2.crossOrigin, "anonymous", "Empty string should map to anonymous");
+ assert_equals(script3.crossOrigin, "anonymous", "Invalid value default should be anonymous");
+ assert_equals(script4.crossOrigin, "anonymous", "anonymous should be parsed correctly");
+ assert_equals(script5.crossOrigin, "use-credentials", "use-credentials should be parsed correctly");
+
+ script1.crossOrigin = "bar";
+ assert_equals(script1.crossOrigin, "anonymous", "Setting to invalid value would default to anonymous");
+
+ script2.crossOrigin = null;
+ assert_equals(script2.crossOrigin, null, "Resetting to null should work");
+
+ script4.crossOrigin = "use-credentials";
+ assert_equals(script4.crossOrigin, "use-credentials", "Switching from anonymous to use-credentials should work");
+
+ script5.crossOrigin = "anonymous";
+ assert_equals(script5.crossOrigin, "anonymous", "Switching from use-credentials to anonymous should work");
+}, document.title);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer-xhtml.xhtml
new file mode 100644
index 0000000000..3f4a50f779
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer-xhtml.xhtml
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>XHTML Test: HTMLScriptElement - defer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta charset="utf-8" />
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+
+let script_run_status = "inline";
+let t = async_test("the defer script run later");
+
+</script>
+
+<script type="text/javascript" src="defer.js" defer="defer"></script>
+
+<script>
+
+t.step(() => {
+ assert_equals(script_run_status, "inline", "the script run status");
+ script_run_status = "deferred";
+});
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer.html
new file mode 100644
index 0000000000..80eb98dc75
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-defer.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: HTMLScriptElement - defer</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script>
+
+let script_run_status = "inline";
+let t = async_test("the defer script run later");
+
+</script>
+
+<script type="text/javascript" src="defer.js" defer></script>
+
+<script>
+
+t.step(() => {
+ assert_equals(script_run_status, "inline", "the script run status");
+ script_run_status = "deferred";
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event-xhtml.xhtml
new file mode 100644
index 0000000000..69c4ef1f81
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event-xhtml.xhtml
@@ -0,0 +1,22 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Scripts with for and event attributes</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <script>
+ var run = false;
+ </script>
+ <script for="window" event="bar">
+ // This script should not run, but should not cause a parse error either.
+ run = true;
+ </script>
+ <script>
+ test(function() {
+ assert_false(run, "Script was unexpectedly run.")
+ }, "Scripts with for and event attributes should not run.")
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event.html
new file mode 100644
index 0000000000..552ea7041a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-event.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<title>Scripts with for and event attributes</title>
+<link rel="author" title="Matheus Kerschbaum" href="mailto:matjk7@gmail.com">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var expected = [
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ true,
+ false,
+ true,
+ true,
+];
+var run = expected.map(function() { return false });
+</script>
+<script for="w&#x130;ndow" event="onload">
+run[0] = true;
+</script>
+<script for="window" event="onload x">
+run[1] = true;
+</script>
+<script for="window" event="onload(x">
+run[2] = true;
+</script>
+<script for="window" event="onload(x)">
+run[3] = true;
+</script>
+<script for="window" event="onclick">
+run[4] = true;
+</script>
+<script for="" event="onload">
+run[5] = true;
+</script>
+<script for="window" event="">
+run[6] = true;
+</script>
+<script for="" event="">
+run[7] = true;
+</script>
+<script for="&#xa0;window" event="onload">
+run[8] = true;
+</script>
+<script for="window&#xa0;" event="onload">
+run[9] = true;
+</script>
+<script for="window" event="&#xa0;onload">
+run[10] = true;
+</script>
+<script for="window" event="onload&#xa0;">
+run[11] = true;
+</script>
+<script for=" window " event=" onload ">
+run[12] = true;
+</script>
+<script for=" window " event=" onload() ">
+run[13] = true;
+</script>
+<script for="object" event="handler">
+run[14] = true;
+</script>
+<script event="handler">
+run[15] = true;
+</script>
+<script for="object">
+run[16] = true;
+</script>
+<script>
+test(function() {
+ for (var i = 0; i < run.length; ++i) {
+ test(function() {
+ var script = document.querySelectorAll("script[for], script[event]")[i];
+ assert_equals(run[i], expected[i],
+ "script for=" + format_value(script.getAttribute("for")) +
+ " event=" + format_value(script.getAttribute("event")));
+ }, "Script " + i);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-noembed-noframes-iframe.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-noembed-noframes-iframe.xhtml
new file mode 100644
index 0000000000..8dd9ceb9a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-noembed-noframes-iframe.xhtml
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Script inside noembed, noframes and iframe</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+var run = [];
+</script>
+<div id="test">
+<noembed>
+<script>
+run.push("noembed");
+</script>
+</noembed>
+<noframes>
+<script>
+run.push("noframes");
+</script>
+</noframes>
+<iframe>
+<script>
+run.push("iframe");
+</script>
+</iframe>
+</div>
+<script>
+test(function() {
+ assert_array_equals(run, ["noembed", "noframes", "iframe"], "Haven't run.");
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown-child.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown-child.html
new file mode 100644
index 0000000000..2f3ce2368d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown-child.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Script is not executed after script thread is shutdown</title>
+<script>
+onload = function() {
+ script_executed = parent.script_executed;
+ s = document.createElement('script');
+ s.type = 'text/javascript';
+ s.src = 'script-not-executed-after-shutdown.js?pipe=trickle(d3)';
+ document.body.appendChild(s);
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.html
new file mode 100644
index 0000000000..eb4def3ec4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Script is not executed after script thread is shutdown</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="testiframe" src="script-not-executed-after-shutdown-child.html"></iframe>
+<script>
+async_test(function(t) {
+ window.script_executed = t.unreached_func("script executed in removed iframe");
+ let iframe = document.getElementById("testiframe");
+ iframe.onload = function() {
+ iframe.parentNode.removeChild(iframe);
+ };
+ setTimeout(function() {
+ t.done();
+ }, 5000);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.js
new file mode 100644
index 0000000000..ccdf14c0cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-executed-after-shutdown.js
@@ -0,0 +1 @@
+script_executed();
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed-2.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed-2.py
new file mode 100644
index 0000000000..4ff5be7374
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed-2.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ body = u"test2_token = \"script executed\";"
+ return 200, headers, body
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.html
new file mode 100644
index 0000000000..44ad30b018
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>var test1_token = "script not executed";</script>
+<script src="script-not-found-not-executed.py"></script>
+<script>
+test(function(){
+ assert_equals(test1_token, "script not executed");
+}, "Script that 404");
+</script>
+<script>var test2_token = "script not executed";</script>
+<script src="script-not-found-not-executed-2.py"></script>
+<script>
+test(function(){
+ assert_equals(test2_token, "script executed");
+}, "Script that does not 404");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.py
new file mode 100644
index 0000000000..9354d42703
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-not-found-not-executed.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ body = u"test1_token = \"script executed\";"
+ return 404, headers, body
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html
new file mode 100644
index 0000000000..0fe39b11a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that the insertion point is defined in the error event of a parser-inserted script that actually started a fetch (but just had it fail).</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var t = async_test("");
+ var writeDone = t.step_func_done(function(text) {
+ assert_equals(text, "Some text");
+ });
+</script>
+<iframe src="support/script-onerror-insertion-point-1-helper.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html
new file mode 100644
index 0000000000..6d3f3ef09e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that the insertion point is not defined in the error event of a
+ parser-inserted script that has an unparseable URL</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var t = async_test("");
+ var writeDone = t.step_func_done(function(text) {
+ assert_equals(text, "text");
+ });
+</script>
+<iframe src="support/script-onerror-insertion-point-2-helper.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html
new file mode 100644
index 0000000000..ce3ddeee65
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that the insertion point is defined in the load event of a parser-inserted script.</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var t = async_test("");
+ var writeDone = t.step_func_done(function(text) {
+ assert_equals(text, "Some text");
+ });
+</script>
+<iframe src="support/script-onload-insertion-point-helper.html"></iframe>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-string.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-string.html
new file mode 100644
index 0000000000..85f2d4dcfa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-string.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Script: setting onload to a string</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var s = document.createElement("script");
+ assert_equals(s.onload, null);
+ var dummy = function() {};
+ s.onload = dummy;
+ assert_equals(s.onload, dummy);
+ s.onload = "w('load DOM appended')";
+ assert_equals(s.onload, null);
+}, "Setting onload to a string should convert to null.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-referrerpolicy-idl.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-referrerpolicy-idl.html
new file mode 100644
index 0000000000..bf01cb83b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-referrerpolicy-idl.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;script> referrerPolicy IDL</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#referrer-policy-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+ test(() => {
+ const script = document.createElement('script');
+ document.body.appendChild(script);
+ assert_equals(script.referrerPolicy,"",'Missing content attribute should reflect as empty');
+ script.setAttribute('referrerpolicy','no-referrer');
+ assert_equals(script.referrerPolicy,"no-referrer",'Valid value should reflect');
+ script.setAttribute('referrerpolicy','');
+ assert_equals(script.referrerPolicy,"",'Empty string should reflect as empty');
+ script.setAttribute('referrerpolicy','invalid-value-here');
+ assert_equals(script.referrerPolicy,"",'Invalid values should reflect as empty');
+ script.referrerPolicy = 'no-referrer';
+ assert_equals(script.referrerPolicy,"no-referrer",'Valid value via IDL');
+ script.referrerPolicy = null;
+ assert_equals(script.referrerPolicy,"",'Null should reflect as empty');
+ },'Missing/invalid/null referrerPolicy should reflect as the empty string')
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-supports.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-supports.html
new file mode 100644
index 0000000000..495056fce9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-supports.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLScriptElement.supports</title>
+<link rel=help href="https://html.spec.whatwg.org/#dom-script-supports">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(typeof HTMLScriptElement.supports, 'function');
+}, 'Type of HTMLScriptElement.supports is function');
+
+test(function() {
+ assert_true(HTMLScriptElement.supports('classic'));
+}, 'HTMLScriptElement.supports resurns true for \'classic\'');
+
+test(function() {
+ assert_true(HTMLScriptElement.supports('module'));
+}, 'HTMLScriptElement.supports resurns true for \'module\'');
+
+test(function() {
+ assert_false(HTMLScriptElement.supports('application/ecmascript'));
+ assert_false(HTMLScriptElement.supports('application/javascript'));
+ assert_false(HTMLScriptElement.supports('application/x-ecmascript'));
+ assert_false(HTMLScriptElement.supports('application/x-javascript'));
+ assert_false(HTMLScriptElement.supports('text/ecmascript'));
+ assert_false(HTMLScriptElement.supports('text/javascript'));
+ assert_false(HTMLScriptElement.supports('text/javascript1.0'));
+ assert_false(HTMLScriptElement.supports('text/javascript1.1'));
+ assert_false(HTMLScriptElement.supports('text/javascript1.2'));
+ assert_false(HTMLScriptElement.supports('text/javascript1.3'));
+ assert_false(HTMLScriptElement.supports('text/javascript1.4'));
+ assert_false(HTMLScriptElement.supports('text/javascript1.5'));
+ assert_false(HTMLScriptElement.supports('text/jscript'));
+ assert_false(HTMLScriptElement.supports('text/livescript'));
+ assert_false(HTMLScriptElement.supports('text/x-ecmascript'));
+ assert_false(HTMLScriptElement.supports('text/x-javascript'));
+}, 'HTMLScriptElement.supports returns false for JavaScript MIME types');
+
+test(function() {
+ assert_false(HTMLScriptElement.supports(''));
+ assert_false(HTMLScriptElement.supports(' '));
+ assert_false(HTMLScriptElement.supports('classic '));
+ assert_false(HTMLScriptElement.supports('module '));
+ assert_false(HTMLScriptElement.supports(' classic '));
+ assert_false(HTMLScriptElement.supports(' module '));
+ assert_false(HTMLScriptElement.supports('classics'));
+ assert_false(HTMLScriptElement.supports('modules'));
+ assert_false(HTMLScriptElement.supports('Classic'));
+ assert_false(HTMLScriptElement.supports('Module'));
+ assert_false(HTMLScriptElement.supports('unsupported'));
+}, 'HTMLScriptElement.supports returns false for unsupported types');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications-csp.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications-csp.html
new file mode 100644
index 0000000000..a991151066
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications-csp.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<head>
+<meta charset=utf-8>
+<title>Modify HTMLScriptElement's text after #prepare-a-script that violates CSP</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta http-equiv="content-security-policy" content="script-src
+ 'nonce-allow'
+ 'sha256-2+5xh6b9uuIi4GaJtmHWtgR2nwRXJpBtMY4nVaOBpfc='
+">
+<!-- The hash is that of the original content of `script0`. -->
+
+<script nonce="allow">
+window.t = async_test("Modify inline script element's text " +
+ "after prepare-a-script before evaluation (CSP)");
+
+const updatedText =
+ 't.unreached_func("CSP check was done against the original text but the updated text was evaluated")();';
+
+function changeScriptText() {
+ document.querySelector('#script0').textContent = updatedText;
+}
+
+t.step_timeout(changeScriptText, 500);
+</script>
+
+<!-- This is "a style sheet that is blocking scripts" and thus ... -->
+<link rel="stylesheet" href="/common/slow.py?pipe=trickle(d1)"></link>
+
+<!-- This inline script becomes a parser-blocking script, and thus
+the step_timeout is evaluated after script0 is inserted into DOM,
+prepare-a-script'ed, but before its evaluation. -->
+<script id="script0">
+t.step(() => {
+ // When this is evaluated after the stylesheet is loaded,
+ // script0's textContent is modified by the async script above,
+ // but the evaluated script is still the original script here,
+ // not what is overwritten, because "child text content" is taken in
+ // #prepare-a-script and passed to "creating a classic script".
+ var s = document.getElementById('script0');
+ assert_equals(s.textContent, updatedText,
+ "<script>'s textContent should be already modified");
+ t.done();
+ });
+</script>
+<script nonce="allow">
+// If this makes the test fail, it indicates `script0` (the original or updated
+// text) was not evaluated, probably blocked by CSP that was checked against the
+// updated text.
+t.unreached_func("CSP check was done against the updated text")();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications.html
new file mode 100644
index 0000000000..cb54da6995
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-modifications.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<head>
+<meta charset=utf-8>
+<title>Modify HTMLScriptElement's text after #prepare-a-script</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+var t = async_test("Modify inline script element's text " +
+ "after prepare-a-script before evaluation");
+
+function changeScriptText() {
+ document.querySelector('#script0').textContent =
+ 't.unreached_func("This should not be evaluated")();';
+}
+
+t.step_timeout(changeScriptText, 500);
+</script>
+
+<!-- This is "a style sheet that is blocking scripts" and thus ... -->
+<link rel="stylesheet" href="/common/slow.py?pipe=trickle(d1)"></link>
+
+<!-- This inline script becomes a parser-blocking script, and thus
+the step_timeout is evaluated after script0 is inserted into DOM,
+prepare-a-script'ed, but before its evaluation. -->
+<script id="script0">
+t.step(() => {
+ // When this is evaluated after the stylesheet is loaded,
+ // script0's textContent is modified by the async script above,
+ // but the evaluated script is still the original script here,
+ // not what is overwritten, because "child text content" is taken in
+ // #prepare-a-script and passed to "creating a classic script".
+ var s = document.getElementById('script0');
+ assert_equals(s.textContent,
+ 't.unreached_func("This should not be evaluated")();',
+ "<script>'s textContent should be already modified");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-xhtml.xhtml
new file mode 100644
index 0000000000..33a4635db3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text-xhtml.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>HTMLScriptElement.text</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-script-text"/>
+<script src="/resources/testharness.js"/>
+<script src="/resources/testharnessreport.js"/>
+</head>
+<body>
+<div id="log"></div>
+<script>
+<x>7;</x>
+<![CDATA[
+ var x = "y";
+]]>
+</script>
+<script>
+var script;
+setup(function() {
+ script = document.body.getElementsByTagName("script")[0];
+})
+test(function() {
+ assert_equals(script.text, '\n\n\n var x = "y";\n\n')
+ assert_equals(script.textContent, '\n7;\n\n var x = "y";\n\n')
+}, "Getter with CDATA section")
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text.html
new file mode 100644
index 0000000000..6e86472246
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-text.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLScriptElement.text</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#dom-script-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var script;
+setup(function() {
+ script = document.createElement("script")
+ script.appendChild(document.createComment("COMMENT"))
+ script.appendChild(document.createTextNode(" TEXT "))
+ script.appendChild(document.createProcessingInstruction("P", "I"))
+ script.appendChild(document.createElement("a"))
+ .appendChild(document.createTextNode("ELEMENT"))
+})
+
+test(function() {
+ assert_equals(script.text, " TEXT ")
+ assert_equals(script.textContent, " TEXT ELEMENT")
+}, "Getter")
+
+test(function() {
+ script.text = " text "
+ assert_equals(script.text, " text ")
+ assert_equals(script.textContent, " text ")
+ assert_equals(script.firstChild.nodeType, Node.TEXT_NODE)
+ assert_equals(script.firstChild.data, " text ")
+ assert_equals(script.firstChild, script.lastChild)
+ assert_array_equals(script.childNodes, [script.firstChild])
+}, "Setter (non-empty string)")
+
+test(function() {
+ script.text = ""
+ assert_equals(script.text, "")
+ assert_equals(script.textContent, "")
+ assert_equals(script.firstChild, null)
+}, "Setter (empty string)")
+
+test(function() {
+ script.text = null
+ assert_equals(script.text, "null")
+ assert_equals(script.textContent, "null")
+ assert_equals(script.firstChild.nodeType, Node.TEXT_NODE)
+ assert_equals(script.firstChild.data, "null")
+ assert_equals(script.firstChild, script.lastChild)
+}, "Setter (null)")
+
+test(function() {
+ script.text = undefined
+ assert_equals(script.text, "undefined")
+ assert_equals(script.textContent, "undefined")
+ assert_equals(script.firstChild.nodeType, Node.TEXT_NODE)
+ assert_equals(script.firstChild.data, "undefined")
+ assert_equals(script.firstChild, script.lastChild)
+}, "Setter (undefined)")
+
+test(function() {
+ var s = document.createElement("script");
+ var text = document.createTextNode("one");
+ s.appendChild(text);
+
+ assert_equals(s.firstChild, text);
+ assert_equals(text.nodeValue, "one");
+
+ s.text = "two";
+ assert_not_equals(s.firstChild, text);
+ assert_equals(text.nodeValue, "one");
+ assert_equals(s.firstChild.nodeValue, "two");
+}, "Setter (text node reuse)")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-empty.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-empty.html
new file mode 100644
index 0000000000..6ce1b279f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-empty.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>Script @type and @language: empty strings</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- Setup -->
+<script>
+window.run1 = window.run2 = window.run3 = window.run4 = false;
+</script>
+
+<!-- Systems under test -->
+<script type="">
+window.run1 = true;
+</script>
+
+<script type="" language="foo">
+window.run2 = true;
+</script>
+
+<script type="" language="">
+window.run3 = true;
+</script>
+
+<script language="">
+window.run4 = true;
+</script>
+
+<!-- Asserts -->
+<script>
+test(() => {
+ assert_true(window.run1);
+}, "A script with empty type and no language should run");
+
+test(() => {
+ assert_true(window.run2);
+}, "A script with empty type and a random language should run");
+
+test(() => {
+ assert_true(window.run3);
+}, "A script with empty type and empty language should run");
+
+test(() => {
+ assert_true(window.run4);
+}, "A script with no type and empty language should run");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-svg.svg b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-svg.svg
new file mode 100644
index 0000000000..2f31d4d75c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-svg.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:h="http://www.w3.org/1999/xhtml">
+<metadata>
+ <h:link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages" />
+ <h:link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script" />
+</metadata>
+<h:script src="/resources/testharness.js"/>
+<h:script src="/resources/testharnessreport.js"/>
+<h:div id="script-placeholder"/>
+<h:script src="resources/script-type-and-language-js.js"/>
+<script>window.ran = false;</script>
+<script type="javascript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript');</script>
+<script type="javascript1.0">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.0');</script>
+<script type="javascript1.1">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.1');</script>
+<script type="javascript1.2">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.2');</script>
+<script type="javascript1.3">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.3');</script>
+<script type="javascript1.4">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.4');</script>
+<script type="javascript1.5">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.5');</script>
+<script type="javascript1.6">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.6');</script>
+<script type="javascript1.7">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.7');</script>
+<script type="livescript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=livescript');</script>
+<script type="ecmascript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=ecmascript');</script>
+<script type="jscript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=jscript');</script>
+</svg>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-xhtml.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-xhtml.xhtml
new file mode 100644
index 0000000000..0bfdfce3c4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-xhtml.xhtml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Script @type and @language: JavaScript types</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="script-placeholder"/>
+<script src="resources/script-type-and-language-js.js"></script>
+
+<script>ran = false;</script>
+<script type="javascript">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript');</script>
+<script type="javascript1.0">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.0');</script>
+<script type="javascript1.1">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.1');</script>
+<script type="javascript1.2">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.2');</script>
+<script type="javascript1.3">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.3');</script>
+<script type="javascript1.4">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.4');</script>
+<script type="javascript1.5">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.5');</script>
+<script type="javascript1.6">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.6');</script>
+<script type="javascript1.7">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.7');</script>
+<script type="livescript">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=livescript');</script>
+<script type="ecmascript">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=ecmascript');</script>
+<script type="jscript">ran = true;</script>
+<script>testParserInsertedDidNotRun('type=jscript');</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js.html
new file mode 100644
index 0000000000..009123de2b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Script @type and @language: JavaScript types</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="script-placeholder"></div>
+<script src="resources/script-type-and-language-js.js"></script>
+
+<script>window.ran = false;</script>
+<script type="javascript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript');</script>
+<script type="javascript1.0">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.0');</script>
+<script type="javascript1.1">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.1');</script>
+<script type="javascript1.2">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.2');</script>
+<script type="javascript1.3">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.3');</script>
+<script type="javascript1.4">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.4');</script>
+<script type="javascript1.5">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.5');</script>
+<script type="javascript1.6">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.6');</script>
+<script type="javascript1.7">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=javascript1.7');</script>
+<script type="livescript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=livescript');</script>
+<script type="ecmascript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=ecmascript');</script>
+<script type="jscript">window.ran = true;</script>
+<script>testParserInsertedDidNotRun('type=jscript');</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-with-params.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-with-params.html
new file mode 100644
index 0000000000..977ee7d0a4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-and-language-with-params.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Script @type and @language: unknown type parameters</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- Setup -->
+<script>
+window.run1 = window.run2 = window.run3 = false;
+</script>
+
+<!-- Systems under test -->
+<script type="text/javascript;charset=UTF-8">
+window.run1 = true;
+</script>
+
+<script type="text/javascript;x-test=abc">
+window.run2 = true;
+</script>
+
+<script language="javascript" type="text/javascript;charset=UTF-8">
+window.run3 = true;
+</script>
+
+<!-- Asserts -->
+<script>
+test(() => {
+ assert_false(window.run1);
+}, "A script with a charset param in its type should not run");
+
+test(() => {
+ assert_false(window.run2);
+}, "A script with an x-test param in its type should not run");
+
+test(() => {
+ assert_false(window.run3);
+}, "A script with a charset param in its type should not run, even with language=javascript");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-whitespace.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-whitespace.html
new file mode 100644
index 0000000000..5e8acb1a17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-type-whitespace.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>&lt;script type> non-ASCII whitespace handling</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+function testParserInsertedDidNotRun(description) {
+ test(() => assert_false(window.ran),
+ "Script shouldn't run with " + description + " (parser-inserted)");
+ window.ran = false;
+}
+</script>
+
+<script>window.ran = false;</script>
+<script type="text/javascript&#x000B;">window.ran = true;</script>
+<script>testParserInsertedDidNotRun("type=\"text/javascript&#x000B;\"");</script>
+
+<script type="text/javascript&#x0085;">window.ran = true;</script>
+<script>testParserInsertedDidNotRun("type=\"text/javascript&#x0085;\"");</script>
+
+<script type="text/javascript&#x00A0;">window.ran = true;</script>
+<script>testParserInsertedDidNotRun("type=\"text/javascript&#x00A0;\"");</script>
+
+<script type="text/javascript&#x1680;">window.ran = true;</script>
+<script>testParserInsertedDidNotRun("type=\"text/javascript&#x1680;\"");</script>
+
+<script type="text/javascript&#x3000;">window.ran = true;</script>
+<script>testParserInsertedDidNotRun("type=\"text/javascript&#x3000;\"");</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/scripting-enabled.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/scripting-enabled.html
new file mode 100644
index 0000000000..a2671a78f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/scripting-enabled.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>JS is disabled on documents created without a browsing context</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#concept-n-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function(t) {
+ var doc = document.implementation.createHTMLDocument();
+ window.fail_test = t.unreached_func('should not have been called');
+
+ var script = doc.createElement('script');
+ script.textContent = 'fail_test();';
+ doc.documentElement.appendChild(script);
+}, 'script on document returned by createHTMLDocument should not execute');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-json-then-js.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-json-then-js.py
new file mode 100644
index 0000000000..9610734d44
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-json-then-js.py
@@ -0,0 +1,21 @@
+# Respond with valid JSON to the first request with the given key,
+# and with valid JavaScript to the second. Used for testing scenarios where
+# the same request URL results in different responses on subsequent requests.
+def main(request, response):
+ try:
+ stash_key = request.GET.first(b"key")
+
+ run_count = request.server.stash.take(stash_key)
+ if not run_count:
+ run_count = 0
+
+ if run_count == 0:
+ response.headers.set(b"Content-Type", b"text/json")
+ response.content = '{"hello": "world"}'
+ else:
+ response.headers.set(b"Content-Type", b"application/javascript")
+ response.content = "export default 'hello';"
+
+ request.server.stash.put(stash_key, run_count + 1)
+ except:
+ response.set_error(400, u"Not enough parameters")
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-with-content-type.py b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-with-content-type.py
new file mode 100644
index 0000000000..675b3fc3eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/serve-with-content-type.py
@@ -0,0 +1,17 @@
+import os
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ directory = os.path.dirname(isomorphic_decode(__file__))
+
+ try:
+ file_name = request.GET.first(b"fn")
+ content_type = request.GET.first(b"ct")
+ with open(os.path.join(directory, isomorphic_decode(file_name)), u"rb") as fh:
+ content = fh.read()
+
+ response.headers.set(b"Content-Type", content_type)
+ response.content = content
+ except:
+ response.set_error(400, u"Not enough parameters or file not found")
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html
new file mode 100644
index 0000000000..d9b0c84ca4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html
@@ -0,0 +1,2 @@
+Some <script src="nosuchscripthere.js"
+ onerror="document.write('text'); parent.writeDone(document.documentElement.textContent)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html
new file mode 100644
index 0000000000..a9ee80026a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html
@@ -0,0 +1,2 @@
+Some <script src="http://this is not parseable:-80/"
+ onerror="document.write('text'); document.close(); parent.writeDone(document.documentElement.textContent)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html
new file mode 100644
index 0000000000..f0236b4fbb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html
@@ -0,0 +1,2 @@
+Some <script src="script-onload-insertion-point-helper.js"
+ onload="document.write('xt'); parent.writeDone(document.documentElement.textContent)"></script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js
new file mode 100644
index 0000000000..8a96a0b783
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js
@@ -0,0 +1 @@
+document.write("te");
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/node-document.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/node-document.html
new file mode 100644
index 0000000000..8676319b20
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/node-document.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Parsing XHTML: Node's node document</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="Parsing XHTML: Node's node document must be set to that of the element to which it will be appended">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#parsing-xhtml-documents">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+
+test(function() {
+ var doc = newXHTMLDocument();
+ doc.body = doc.createElement('body');
+ doc.body.innerHTML = '<template id="tmpl"></template>';
+
+ var template = doc.querySelector('#tmpl');
+
+ assert_not_equals(template, null, 'Template element should not be null');
+ assert_not_equals(template.content, undefined,
+ 'Content attribute of template element should not be undefined');
+ assert_not_equals(template.content, null,
+ 'Content attribute of template element should not be null');
+
+ assert_equals(template.ownerDocument, doc.body.ownerDocument,
+ 'Wrong template node owner document');
+ var ownerDoc = template.content.ownerDocument;
+ assert_not_equals(ownerDoc, doc, 'Wrong template content owner document');
+ assert_not_equals(ownerDoc, document, 'Wrong template content owner document');
+ assert_equals(ownerDoc.defaultView, null,
+ 'Template content owner document should not have a browsing context');
+
+}, 'Parsing XHTML: Node\'s node document must be set to that of the element '
+ + 'to which it will be appended. Test empty template');
+
+
+
+test(function() {
+ var doc = newXHTMLDocument();
+
+ doc.body = doc.createElement('body');
+ doc.body.innerHTML = '<template id="tmpl"><div>Div content</div></template>';
+
+ var template = doc.querySelector('#tmpl');
+
+ assert_equals(template.ownerDocument, doc.body.ownerDocument,
+ 'Wrong template node owner document');
+
+ assert_not_equals(template, null, 'Template element should not be null');
+ assert_not_equals(template.content, undefined,
+ 'Content attribute of template element should not be undefined');
+ assert_not_equals(template.content, null,
+ 'Content attribute of template element should not be null');
+
+ var div = template.content.querySelector('div');
+ assert_equals(template.content.ownerDocument, div.ownerDocument,
+ 'Wrong DIV node owner document');
+
+}, 'Parsing XHTML: Node\'s node document must be set to that of the element '
+ + 'to which it will be appended. Test not empty template');
+
+
+
+test(function() {
+ var doc = newXHTMLDocument();
+ doc.body = doc.createElement('body');
+ doc.body.innerHTML = ''
+ + '<template id="tmpl"><div>Div content</div> And some more text'
+ + '<template id="tmpl2"><div>Template content</div></template>'
+ + '</template>';
+
+ var template = doc.querySelector('#tmpl');
+ assert_not_equals(template, null, 'Template element should not be null');
+ assert_equals(template.ownerDocument, doc, 'Wrong template node owner document');
+ assert_not_equals(template.content, undefined,
+ 'Content attribute of template element should not be undefined');
+ assert_not_equals(template.content, null,
+ 'Content attribute of template element should not be null');
+
+ var nestedTemplate = template.content.querySelector('#tmpl2');
+ assert_not_equals(nestedTemplate, null, 'Nested template element should not be null');
+ assert_not_equals(nestedTemplate.content, undefined,
+ 'Content attribute of nested template element should not be undefined');
+ assert_not_equals(nestedTemplate.content, null,
+ 'Content attribute of nested template element should not be null');
+
+ assert_equals(nestedTemplate.ownerDocument, template.content.ownerDocument,
+ 'Wrong nested template node owner document');
+
+
+ var div = nestedTemplate.content.querySelector('div');
+ assert_equals(nestedTemplate.content.ownerDocument, div.ownerDocument,
+ 'Wrong DIV node owner document');
+
+}, 'Parsing XHTML: Node\'s node document must be set to that of the element '
+ + 'to which it will be appended. Test nested templates');
+
+
+
+testInIFrame('../resources/template-child-nodes-div.xhtml', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.ownerDocument, doc, 'Wrong template node owner document');
+
+ assert_not_equals(template.content, undefined,
+ 'Content attribute of template element should not be undefined');
+ assert_not_equals(template.content, null,
+ 'Content attribute of template element should not be null');
+
+ var div = template.content.querySelector('div');
+ assert_equals(template.content.ownerDocument, div.ownerDocument,
+ 'Wrong DIV node owner document');
+
+}, 'Parsing XHTML: Node\'s node document must be set to that of the element '
+ + 'to which it will be appended. Test loading XHTML document from a file');
+
+
+
+testInIFrame('../resources/template-child-nodes-nested.xhtml', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.ownerDocument, doc, 'Wrong template node owner document');
+
+ var nestedTemplate = template.content.querySelector('template');
+
+ assert_equals(nestedTemplate.ownerDocument, template.content.ownerDocument,
+ 'Wrong template node owner document');
+
+ var div = nestedTemplate.content.querySelector('div');
+ assert_equals(nestedTemplate.content.ownerDocument, div.ownerDocument,
+ 'Wrong DIV node owner document');
+
+}, 'Parsing XHTML: Node\'s node document must be set to that of the element '
+ + 'to which it will be appended. Test loading of XHTML document '
+ + 'with nested templates from a file');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/tag-name.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/tag-name.xhtml
new file mode 100644
index 0000000000..a1e293d1cb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/tag-name.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>tagName in template</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<template><div></div></template>
+<div id="log"></div>
+<script><![CDATA[
+test(function() {
+ assert_equals(document.getElementsByTagName("template")[0].content.firstChild.tagName, 'div', "tagName case");
+}, "tagName case");
+]]></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/template-child-nodes.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/template-child-nodes.html
new file mode 100644
index 0000000000..40abda5684
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/template-child-nodes.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Child nodes of template element in XHTML documents</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="Child nodes of template element in XHTML documents are always appended to the template content (instead of template itself)">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#parsing-xhtml-documents">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function() {
+ var doc = newXHTMLDocument();
+
+ doc.body = doc.createElement('body');
+ doc.body.innerHTML = '<template id="tmpl1">'
+ + '<div id="div1">This is div inside template</div>'
+ + '<div id="div2">This is another div inside template</div>'
+ + '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ assert_equals(template.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+ assert_equals(template.content.childNodes.length, 2,
+ 'Wrong number of template content child nodes');
+
+}, 'Child nodes of template element in XHTML documents must be appended to template content');
+
+
+
+test(function() {
+ var doc = newXHTMLDocument();
+ doc.body = doc.createElement('body');
+ doc.body.innerHTML = '<template id="tmpl1">'
+ + '<div id="div1">This is div inside template</div>'
+ + '<div id="div2">This is another div inside template</div>'
+ + '<template id="tmpl2">'
+ + '<div id="div3">This is div inside nested template</div>'
+ + '<div id="div4">This is another div inside nested template</div>'
+ + '</template>' + '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ assert_equals(template.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+ assert_equals(template.content.childNodes.length, 3,
+ 'Wrong number of template content child nodes');
+
+ var nestedTemplate = template.content.querySelector('#tmpl2');
+
+ assert_equals(nestedTemplate.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+ assert_equals(nestedTemplate.content.childNodes.length, 2,
+ 'Wrong number of nested template content child nodes');
+
+}, 'Child nodes of nested template element in XHTML documents must be appended to template content');
+
+
+
+testInIFrame('../resources/template-child-nodes-div.xhtml', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+ assert_equals(template.content.querySelectorAll('div').length, 2,
+ 'Wrong number of template content child nodes');
+
+}, 'Child nodes of template element in XHTML documents must be appended to template content. '
+ + 'Test loading XHTML document from a file');
+
+
+testInIFrame('../resources/template-child-nodes-nested.xhtml', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+
+ var nestedTemplate = template.content.querySelector('template');
+
+ assert_equals(nestedTemplate.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+
+ assert_equals(nestedTemplate.content.querySelectorAll('div').length, 2,
+ 'Wrong number of template content child nodes');
+
+}, 'Child nodes of nested template element in XHTML documents must be appended to template content. '
+ + 'Test loading XHTML document from a file');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-serializing-xhtml-documents/outerhtml.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-serializing-xhtml-documents/outerhtml.html
new file mode 100644
index 0000000000..416a3bc615
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-serializing-xhtml-documents/outerhtml.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: serialize template contents instead of template element</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="Template contents should be serialized instead of template element if serializing template element in XHTML document">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#serializing-xhtml-documents">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+test(function () {
+ var doc = newXHTMLDocument();
+ var template = doc.createElement('template');
+
+ var div = doc.createElement('div');
+ div.setAttribute('id', 'div1');
+ div.innerHTML = 'some text';
+ template.content.appendChild(div);
+
+ assert_equals(template.outerHTML, '<template xmlns="http://www.w3.org/1999/xhtml"><div id="div1">some text</div></template>',
+ 'template element is serialized incorrectly');
+
+}, 'Template contents should be serialized instead of template element if serializing template element');
+
+
+
+test(function () {
+ var doc = newXHTMLDocument();
+ var template = doc.createElement('template');
+ var nestedTemplate = doc.createElement('template');
+
+ template.content.appendChild(nestedTemplate);
+
+ var div = doc.createElement('div');
+ div.setAttribute('id', 'div1');
+ div.innerHTML = 'some text';
+ nestedTemplate.content.appendChild(div);
+
+ assert_equals(template.outerHTML, '<template xmlns="http://www.w3.org/1999/xhtml"><template><div id="div1">some text</div></template></template>',
+ 'template element is serialized incorrectly');
+
+
+}, 'Template contents should be serialized instead of template element if serializing template element. '
+ + 'Test nested template');
+
+
+test(function () {
+ var doc = newXHTMLDocument();
+ var template = doc.createElement('template');
+
+ var div = doc.createElement('div');
+ div.setAttribute('id', 'div1');
+ div.innerHTML = 'some text';
+ template.content.appendChild(div);
+ doc.body = doc.createElement('body');
+ doc.body.appendChild(template);
+
+ assert_equals(doc.documentElement.outerHTML, '<html xmlns="http://www.w3.org/1999/xhtml"><body><template><div id="div1">some text</div></template></body></html>',
+ 'template element is serialized incorrectly');
+
+}, 'Template contents should be serialized instead of template element if serializing template element. '
+ + 'Test serializing whole document');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001-ref.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001-ref.html
new file mode 100644
index 0000000000..55c8b2e30c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>Template Reftest Reference</title>
+<link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru"/>
+<body>
+ <p>Test passes if there's no anything below this line.</p>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001.html
new file mode 100644
index 0000000000..fc310f47c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-001.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+ <title>Template Test: check that template content is invisible by default</title>
+ <link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+ <link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#css-additions">
+ <meta name="assert" content="Test checks that the template contents are hidden implicitly">
+ <link rel="match" href="css-user-agent-style-sheet-test-001-ref.html">
+<body>
+ <p>Test passes if there's no anything below this line.</p>
+ <template>
+ <span style="color:red">Test fails if you can see this text</span>
+ </template>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-002.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-002.html
new file mode 100644
index 0000000000..92f3d81eac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-002.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+ <title>Template Test: check that template content is invisible by default</title>
+ <link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+ <link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#css-additions">
+ <meta name="assert" content="The template element itself must be hidden by default">
+ <link rel="match" href="css-user-agent-style-sheet-test-001-ref.html">
+<body>
+ <p>Test passes if there's no anything below this line.</p>
+ <template style="border: 1px solid; width: 100px; height: 100px">
+ <span style="color:red">Test fails if you can see this text or border around it</span>
+ </template>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-003.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-003.html
new file mode 100644
index 0000000000..4c477fde79
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-css-user-agent-style-sheet/css-user-agent-style-sheet-test-003.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+ <title>HTML Templates: template content is invisible by default</title>
+ <link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+ <link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#css-additions">
+ <meta name="assert" content="The template element itself must be hidden by default">
+ <link rel="match" href="css-user-agent-style-sheet-test-001-ref.html">
+ <style>
+ template {
+ border: 1px solid;
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+<body>
+ <p>Test passes if there's no anything below this line.</p>
+ <template>
+ <span style="color:red">Test fails if you can see this text or border around it</span>
+ </template>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/template-clone-children.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/template-clone-children.html
new file mode 100644
index 0000000000..c668d90953
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/template-clone-children.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Clone template node: All the children of template content are copied if 'copy children flag' set to true</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="Clone template node: all the children of template content are copied if 'copy children flag' set to true">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#node-clone-additions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+ var copy = template.cloneNode(true);
+
+ assert_not_equals(copy.content, undefined, 'Template clone content attribute should not be undefined');
+ assert_not_equals(copy.content, null, 'Template clone content attribute should not be null');
+
+ assert_equals(copy.content.childNodes.length, 2,
+ 'Wrong number of template content\'s copy child nodes');
+ assert_not_equals(copy.content.querySelector('#div1'), null,
+ 'Template child node should be copied');
+ assert_not_equals(copy.content.querySelector('#div2'), null,
+ 'Template child node should be copied');
+
+}, 'Clone template node. Test call to cloneNode(true)');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+ var copy = template.cloneNode();
+
+ assert_not_equals(copy.content, undefined, 'Template clone content attribute should not be undefined');
+ assert_not_equals(copy.content, null, 'Template clone content attribute should not be null');
+
+ assert_equals(copy.content.childNodes.length, 0,
+ 'Wrong number of template content\'s copy child nodes');
+
+}, 'Clone template node. Test call to cloneNode() with the default parameter '
+ + '(false by default)');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+ var copy = template.cloneNode(false);
+
+ assert_not_equals(copy.content, undefined, 'Template clone content attribute is undefined');
+ assert_not_equals(copy.content, null, 'Template clone content attribute is null');
+
+ assert_equals(copy.content.childNodes.length, 0,
+ 'Wrong number of template content\'s copy child nodes');
+
+}, 'Clone template node. Test call to cloneNode(false)');
+
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/templates-copy-document-owner.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/templates-copy-document-owner.html
new file mode 100644
index 0000000000..a2afc23041
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/templates-copy-document-owner.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: ownerDocument of cloned template content is set to template content owner</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="ownerDocument of cloned template content is set to template content owner">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#node-clone-additions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+function checkOwnerDocument(node, doc) {
+ if ((node !== null) && (node !== undefined)) {
+ assert_equals(node.ownerDocument, doc,
+ 'Wrong ownerDocument of the template copy\'s node ' + node.nodeName);
+ for (var i = 0; i < node.childNodes.length; i++) {
+ if (node.childNodes[i].nodeType === Node.ELEMENT_NODE) {
+ checkOwnerDocument(node.childNodes[i], doc);
+ if (node.childNodes[i].nodeName === 'TEMPLATE') {
+ checkOwnerDocument(node.childNodes[i].content, doc);
+ }
+ }
+ }
+ }
+}
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+ var copy = template.cloneNode(true);
+
+ assert_equals(copy.content.childNodes.length, 2,
+ 'Wrong number of template content\'s copy child nodes');
+ checkOwnerDocument(copy.content, template.content.ownerDocument);
+
+}, 'ownerDocument of cloned template content is set to template content owner. '
+ + 'Test cloning with children');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+ var copy = template.cloneNode(false);
+
+ assert_equals(copy.content.childNodes.length, 0,
+ 'Wrong number of template content\'s copy child nodes');
+ checkOwnerDocument(copy.content, template.content.ownerDocument);
+
+}, 'ownerDocument of cloned template content is set to template content owner. '
+ + 'Test cloning without children');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+ var copy = template.cloneNode();
+
+ assert_equals(copy.content.childNodes.length, 0,
+ 'Wrong number of template content\'s copy child nodes');
+ checkOwnerDocument(copy.content, template.content.ownerDocument);
+
+}, 'ownerDocument of cloned template content is set to template content owner. '
+ + 'Test cloneNode() with no arguments (false by default)');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '<template id="tmpl2">' +
+ '<div id="div3">This is div inside nested template</div>' +
+ '<div id="div4">This is another div inside nested template</div>' +
+ '</template>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+ var copy = template.cloneNode(true);
+
+ assert_equals(copy.content.childNodes.length, 3,
+ 'Wrong number of template content\'s copy child nodes');
+ checkOwnerDocument(copy.content, template.content.ownerDocument);
+
+}, 'ownerDocument of cloned template content is set to template content owner. '
+ + 'Test cloning nested template');
+
+
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+ var copy = template.cloneNode(true);
+
+ checkOwnerDocument(copy.content, template.content.ownerDocument);
+
+}, 'ownerDocument of cloned template content is set to template content owner. '
+ + 'Test loading HTML document from file');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html
new file mode 100644
index 0000000000..d063acded9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: The template contents owner document type is HTML document</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="The template contents owner document type is HTML document, if template is declared in HTML document">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#definitions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+ var template = doc.querySelector('template');
+ var content_owner = template.content.ownerDocument;
+
+ assert_class_string(content_owner, 'Document',
+ 'Template content owner should be a document');
+ assert_equals(content_owner.createElement('DIV').localName, 'div',
+ 'Template content owner should be an HTML document');
+
+}, 'The template contents owner document type is HTML document ' +
+ '(case when document has browsing context and the template ' +
+ 'is created by HTML parser)');
+
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+ var template = doc.createElement('template');
+ var content_owner = template.content.ownerDocument;
+ var div = doc.createElement('DIV');
+ template.appendChild(div);
+
+ doc.body.appendChild(template);
+
+ assert_class_string(content_owner, 'Document',
+ 'Template content owner should be a document');
+ assert_equals(div.localName, 'div',
+ 'Template content owner should be an HTML document');
+
+}, 'The template contents owner document type is HTML document ' +
+ '(case when document has browsing context and the template ' +
+ 'is created by createElement())');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+ var content_owner = template.content.ownerDocument;
+ var div = doc.createElement('DIV');
+ template.appendChild(div);
+
+ doc.body.appendChild(template);
+
+ assert_class_string(content_owner, 'Document',
+ 'Template content owner should be a document');
+ assert_equals(div.localName, 'div',
+ 'Template content owner should be an HTML document');
+
+}, 'The template contents owner document type is HTML document ' +
+ '(case when document has no browsing context and the template is created ' +
+ 'by createElement())');
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template><div>Hello!</div></template>';
+ var template = doc.querySelector('template');
+ var content_owner = template.content.ownerDocument;
+
+ assert_class_string(content_owner, 'Document',
+ 'Template content owner should be a document');
+ assert_equals(content_owner.createElement('DIV').localName, 'div',
+ 'Template content owner should be an HTML document');
+
+}, 'The template contents owner document type is HTML document ' +
+ '(case when document has no browsing context and the template is created via innerHTML)');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-001.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-001.html
new file mode 100644
index 0000000000..a087f788e5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-001.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: The template contents owner document (no browsing context)</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="Even if template's enclosing document has no browsing context, it gets its own template contents owner">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#definitions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ doc.body.appendChild(template);
+
+ assert_not_equals(template.content.ownerDocument, doc, 'Wrong template content owner');
+
+}, 'Test the template contents owner document when enclosing document has '
+ + 'no browsing content. Template element is created by createElement()');
+
+
+
+test(function() {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<template><div>some text</div></template>';
+
+ var template = doc.querySelector('template');
+
+ assert_not_equals(template.content.ownerDocument, doc, 'Wrong template content owner');
+
+}, 'Test the template contents owner document when enclosing document has '
+ + 'no browsing content. Template element is created by innerHTML');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-002.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-002.html
new file mode 100644
index 0000000000..cf2e30b643
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-002.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: The template contents owner document (there's browsing context)</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="If template's enclosing document has browsing context, then templates content owner must be a new Document node without browsing context">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#definitions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+testInIFrame(null, function(context) {
+ var doc = context.iframes[0].contentDocument;
+ var template = doc.createElement('template');
+
+ var div = doc.createElement('div');
+ div.setAttribute('id', 'div1');
+
+ template.appendChild(div);
+
+ doc.body.appendChild(template);
+
+ // doc has browsing context. There should be another document as a template
+ // content owner
+ assert_not_equals(template.content.ownerDocument, doc, 'Wrong template owner document');
+
+}, 'The template contents owner document must be different from template owner document,' +
+ ' which has browsing context. Template element is created by createElement()');
+
+
+
+testInIFrame(null, function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ doc.body.innerHTML = '<template><div>some text</div></template>';
+
+ var template = doc.querySelector('template');
+
+ // doc has browsing context. There should be another document as a template
+ // content owner
+ assert_not_equals(template.content.ownerDocument, doc, 'Wrong template owner document');
+
+}, 'The template contents owner document must be different from template owner document,' +
+ ' which has browsing context. Template element is created via innerHTML');
+
+
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ // doc has browsing context. There should be another document as a template
+ // content owner
+ assert_not_equals(template.content.ownerDocument, doc, 'Wrong template owner document');
+
+}, 'The template contents owner document must be different from template owner document,' +
+ ' which has browsing context. Template element is created by HTML parser');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents.html
new file mode 100644
index 0000000000..4a61dc8d3b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/definitions/template-contents.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: The template contents is a DocumentFragment</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="The template contents must be a DocumentFragment">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#definitions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a DocumentFragment');
+
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+}, 'The template contents must be a DocumentFragment (empty template)');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<div>This is a div</div><span>This is a span</span>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a DocumentFragment');
+
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+}, 'The template contents must be a DocumentFragment (non empty template)');
+
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<div>This is a div</div>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a documentFragment');
+
+}, 'The template contents must be a DocumentFragment (non empty template '
+ + 'containing div which is an Element instance)');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = 'Some text';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a documentFragment');
+
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+}, 'The template contents must be a DocumentFragment (not empty template '
+ + 'containing text node)');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<template id="t2">Some text</template>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a documentFragment');
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+ var nestedTemplate = template.content.querySelector("#t2");
+ assert_equals(nestedTemplate.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Nested template content should be a documentFragment');
+
+ assert_class_string(nestedTemplate.content, 'DocumentFragment',
+ 'Nested template content class should be a DocumentFragment');
+
+
+}, 'The template contents must be a DocumentFragment (nested template '
+ + 'containing a text node)');
+
+
+testInIFrame('../resources/template-contents-empty.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a documentFragment');
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+
+}, 'The template contents must be a DocumentFragment (the empty template tag '
+ + 'inside HTML file loaded in iframe)');
+
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a documentFragment');
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+}, 'The template contents must be a DocumentFragment (non empty template '
+ + 'tag inside HTML file loaded in iframe)');
+
+
+testInIFrame('../resources/template-contents-text.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a documentFragment');
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+}, 'The template contents must be a DocumentFragment (the template tag '
+ + 'with some text inside HTML file loaded in iframe)');
+
+
+testInIFrame('../resources/template-contents-nested.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Template content should be a documentFragment');
+ assert_class_string(template.content, 'DocumentFragment',
+ 'Template content class should be a DocumentFragment');
+
+ var nestedTemplate = template.content.querySelector("template");
+
+ assert_equals(nestedTemplate.content.nodeType, Node.DOCUMENT_FRAGMENT_NODE,
+ 'Nested template content should be a documentFragment');
+ assert_class_string(nestedTemplate.content, 'DocumentFragment',
+ 'Nested template content class should be a DocumentFragment');
+
+}, 'The template contents must be a DocumentFragment (the template tag '
+ + 'with nested template tag inside HTML file loaded in iframe)');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/innerhtml-on-templates/innerhtml.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/innerhtml-on-templates/innerhtml.html
new file mode 100644
index 0000000000..0968b37f92
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/innerhtml-on-templates/innerhtml.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: innerHTML of template element replaces all referenced by the content attribute</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="innerHTML of template element replaces all referenced by the content attribute">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#innerhtml-on-templates">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+test(function () {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ var div1 = doc.createElement('div');
+ div1.setAttribute('id', 'div1');
+ template.content.appendChild(div1);
+
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Element should present in template content');
+
+ template.innerHTML = '<div id="div2"></div>';
+
+ assert_equals(template.content.querySelector('#div1'), null,
+ 'Template content should be replaced by innerHTML');
+ assert_not_equals(template.content.querySelector('#div2'), null,
+ 'Element should present in template content');
+
+}, 'innerHTML of template element replaces all referenced by the content attribute');
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ var div1 = doc.createElement('div');
+ div1.setAttribute('id', 'div1');
+ template.content.appendChild(div1);
+
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Element should present in template content');
+
+ template.innerHTML = '';
+
+ assert_false(template.content.hasChildNodes(),
+ 'Template content should be removed by innerHTML');
+
+}, 'innerHTML of template element replaces all referenced by the content attribute. '
+ + 'Test empty HTML string');
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+ var nestedTemplate = doc.createElement('template');
+
+ template.content.appendChild(nestedTemplate);
+
+ var div1 = doc.createElement('div');
+ div1.setAttribute('id', 'div1');
+ nestedTemplate.content.appendChild(div1);
+
+ assert_not_equals(nestedTemplate.content.querySelector('#div1'), null,
+ 'Element should present in template content');
+
+ nestedTemplate.innerHTML = '<div id="div2"></div>';
+
+ assert_equals(nestedTemplate.content.querySelector('#div1'), null,
+ 'Template content should be replaced by innerHTML');
+ assert_not_equals(nestedTemplate.content.querySelector('#div2'), null,
+ 'Element should present in template content');
+
+}, 'innerHTML of template element replaces all referenced by the content attribute. '
+ + 'Test nested template');
+
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+ assert_not_equals(template.content.querySelector('div'), null,
+ 'Div element should present in template content');
+
+ template.innerHTML = '<span>span internals</span>';
+
+ assert_equals(template.content.querySelector('div'), null,
+ 'div element should be replaced by span in template content');
+
+ assert_not_equals(template.content.querySelector('span'), null,
+ 'span element should present in template content');
+
+
+}, 'innerHTML of template element replaces all referenced by the content attribute. '
+ + 'Test loading of HTML document from a file');
+
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-body.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-body.html
new file mode 100644
index 0000000000..2cb149853f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-body.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The file contains several &lt;/template&gt; tag in HTML body without start one</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+</head>
+<body>
+ </template>
+ <div>The file contains several &lt;/template&gt; tag in HTML body without start one</div>
+ </template></template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-head.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-head.html
new file mode 100644
index 0000000000..02d0c7be65
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-head.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ </template>
+ <title>The file contains several &lt;/template&gt; tag in HTML head without start one</title>
+ </template></template>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+ </template>
+</head>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/frameset-end-tag.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/frameset-end-tag.html
new file mode 100644
index 0000000000..b84d55595f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/frameset-end-tag.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The file contains frameset with the template and frameset end tag in it</title>
+ <link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+</head>
+<frameset>
+ <template></frameset></template>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-div-no-end-tag.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-div-no-end-tag.html
new file mode 100644
index 0000000000..e4e45bcea5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-div-no-end-tag.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The file contains template element with open div tag, but without end div tag, in the head</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+ <template>
+ <div>Hello, template
+ </template>
+</head>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-table-no-end-tag.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-table-no-end-tag.html
new file mode 100644
index 0000000000..9db2b4af06
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/head-template-contents-table-no-end-tag.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The file contains template element with open table, tr, td tags, but without end td, tr, table tags, in the head</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+ <template>
+ <table>
+ <tr>
+ <td>Hello, cell one!
+ </template>
+</head>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/html-start-tag.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/html-start-tag.html
new file mode 100644
index 0000000000..0de652cf36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/html-start-tag.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html tabindex="5">
+<head>
+ <title>The file contains html root element with attributes and some in the body</title>
+ <link rel="author" title="Sergey G. Grekhovv" href="mailto:sgrekhov@unipro.ru">
+</head>
+<body>
+<template id="tmpl"><html class="htmlClass"></html></template><html id="htmlId" tabindex="5">
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-div.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-div.xhtml
new file mode 100644
index 0000000000..14db5004dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-div.xhtml
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Template tag with children div tags inside</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru"/>
+</head>
+<body>
+ <p>Template tag with div tags inside</p>
+ <template>
+ <div>This is div inside template</div>
+ <div>This is another div inside template</div>
+ </template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-nested.xhtml b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-nested.xhtml
new file mode 100644
index 0000000000..406fa6c3d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-child-nodes-nested.xhtml
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Template tag with children div tags inside another template tag</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru"/>
+</head>
+<body>
+ <p>Template tag with children div tags inside another template tag</p>
+ <template>
+ <template>
+ <div>This is div inside template</div>
+ <div>This is another div inside template</div>
+ </template>
+ </template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-attribute.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-attribute.html
new file mode 100644
index 0000000000..b9dd5f47a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-attribute.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Empty template tag with attribute content</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<head>
+<body>
+ <template content='some text'></template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-body.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-body.html
new file mode 100644
index 0000000000..a1f246fd63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-body.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>BODY tag inside template</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<head>
+<body>
+ <template><body></body></template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-div-no-end-tag.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-div-no-end-tag.html
new file mode 100644
index 0000000000..304acf3025
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-div-no-end-tag.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Div tag inside template tag</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+</head>
+<body>
+ <template>
+ <div>Hello, template
+ </template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-empty.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-empty.html
new file mode 100644
index 0000000000..f1a539cc08
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-empty.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Empty template tag</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<head>
+<body>
+ <template>
+ </template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-frameset.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-frameset.html
new file mode 100644
index 0000000000..4331367df3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-frameset.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>FRAMESET tag inside template</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<head>
+<body>
+ <template><frameset></frameset></template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-head.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-head.html
new file mode 100644
index 0000000000..1e3a337e8d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-head.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HEAD tag inside template</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<head>
+<body>
+ <template><head></head></template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-html.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-html.html
new file mode 100644
index 0000000000..5dd3a28e6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-html.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML tag inside template</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<head>
+<body>
+ <template><html></html></template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-nested.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-nested.html
new file mode 100644
index 0000000000..dc2dc6f15f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-nested.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+ <title>Contains second template tag inside template tag</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<body>
+ <template>
+ <template>
+ <div>Inside nested template</div>
+ </template>
+ </template>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-table-no-end-tag.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-table-no-end-tag.html
new file mode 100644
index 0000000000..4639b4dc8e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-table-no-end-tag.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The file contains template element with open table, tr, td tags, without end td, tr, table tags</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+</head>
+<body>
+ <template>
+ <table>
+ <tr>
+ <td>Hello, cell one!
+ </template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-text.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-text.html
new file mode 100644
index 0000000000..a401848efc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents-text.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Some text inside template tag</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+</head>
+<body>
+ <template>Some text</template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents.html
new file mode 100644
index 0000000000..07256c06a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-contents.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Div tag inside template tag</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+</head>
+<body>
+ <template>
+ <div>Hello, template</div>
+ </template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-body.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-body.html
new file mode 100644
index 0000000000..d64848c8db
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-body.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Div tag inside template tag</title>
+ <link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+</head>
+<body>
+ <template>
+ <div>Hello, template</div>
+ </template>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-frameset.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-frameset.html
new file mode 100644
index 0000000000..4801178454
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-frameset.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Template tag inside frameset</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+</head>
+<frameset>
+ <template>
+ <div>Hello, template</div>
+ </template>
+</frameset>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-head.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-head.html
new file mode 100644
index 0000000000..6bab00ea99
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/template-descendant-head.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Template tag inside head</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+ <template>
+ <div>Hello, template</div>
+ </template>
+</head>
+<body>
+ Nothing interesting here
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/two-templates.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/two-templates.html
new file mode 100644
index 0000000000..f6e9ab58e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/resources/two-templates.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The file contains two template elements</title>
+ <link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+</head>
+<body>
+ <template id="template1">
+ <div>Hello, template</div>
+ </template>
+
+ <template id="template2">
+ <div>Hello, from second template</div>
+ </template>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/serializing-html-templates/outerhtml.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/serializing-html-templates/outerhtml.html
new file mode 100644
index 0000000000..1539afbe1e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/serializing-html-templates/outerhtml.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: serialize template contents instead of template element</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="template contents should be serialized instead of template element if serializing template element">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#serializing-html-templates">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+test(function () {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ var div = doc.createElement('div');
+ div.setAttribute('id', 'div1');
+ div.innerHTML = 'some text';
+ template.content.appendChild(div);
+
+ assert_equals(template.outerHTML, '<template><div id="div1">some text</div></template>',
+ 'template element is serialized incorrectly');
+
+}, 'Template contents should be serialized instead of template element if serializing template element');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+ var nestedTemplate = doc.createElement('template');
+
+ template.content.appendChild(nestedTemplate);
+
+ var div = doc.createElement('div');
+ div.setAttribute('id', 'div1');
+ div.innerHTML = 'some text';
+ nestedTemplate.content.appendChild(div);
+
+ assert_equals(template.outerHTML, '<template><template><div id="div1">some text</div></template></template>',
+ 'template element is serialized incorrectly');
+
+
+}, 'Template contents should be serialized instead of template element if serializing template element. '
+ + 'Test nested template');
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ var div = doc.createElement('div');
+ div.setAttribute('id', 'div1');
+ div.innerHTML = 'some text';
+ template.content.appendChild(div);
+ doc.body.appendChild(template);
+
+ assert_equals(doc.documentElement.outerHTML, '<html><head><title>Test Document</title></head><body><template><div id="div1">some text</div></template></body></html>',
+ 'template element is serialized incorrectly');
+
+}, 'Template contents should be serialized instead of template element if serializing template element. '
+ + 'Test serializing whole document');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/content-attribute.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/content-attribute.html
new file mode 100644
index 0000000000..b4c11b841f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/content-attribute.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Content attribute of template element is read-only</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="Content attribute of template element is read-only">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ assert_readonly(template, 'content',
+ 'Content attribute of template element should be read-only');
+
+}, 'Content attribute of template element is read-only. ' +
+ 'Test empty template');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+ var el1 = doc.createElement('div');
+ var el2 = doc.createElement('span');
+ el1.appendChild(el2);
+
+ template.content.appendChild(el1);
+
+ assert_readonly(template, 'content',
+ 'Content attribute of template element should be read-only');
+
+}, 'Content attribute of template element is read-only. ' +
+ 'Test not empty template populated by appendchild()');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template>Text<div>DIV</div></template>';
+
+ var template = doc.querySelector('template');
+
+ assert_readonly(template, 'content',
+ 'Content attribute of template element should be read-only');
+
+}, 'Content attribute of template element is read-only. ' +
+ 'Test not empty template populated by innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="template1" content="Some text as a content"></template>';
+
+ var template = doc.querySelector('#template1');
+
+ assert_readonly(template, 'content',
+ 'Content attribute of template element should be read-only');
+
+}, 'Content attribute of template element is read-only. ' +
+ 'Test that custom content attribute named \'content\' doesn\'t ' +
+ 'make content IDL attribute writable');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="template1" content="<div id=div1>Div content</div>"></template>';
+
+ var template = doc.querySelector('#template1');
+
+ assert_readonly(template, 'content',
+ 'Content attribute of template element should be read-only');
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Content attribute of template element should be read-only');
+
+}, 'Content attribute of template element is read-only. ' +
+ 'Test that custom content attribute named \'content\' doesn\'t ' +
+ 'affect content IDL attribute');
+
+
+testInIFrame('../resources/template-contents-attribute.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_readonly(template, 'content',
+ 'Content attribute of template element should be read-only');
+
+}, 'Content attribute of template element is read-only. '
+ + 'Text value of content attribute of template tag should be ignored, '
+ + 'when loading document from a file');
+
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_readonly(template, 'content',
+ 'Content attribute of template element should be read-only');
+
+}, 'Content attribute of template element is read-only. '
+ + 'Test content attribute of a document loaded from a file');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/node-document-changes.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/node-document-changes.html
new file mode 100644
index 0000000000..0c60c10738
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/node-document-changes.html
@@ -0,0 +1,192 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: When node's document changes its owner document should be changed</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="When a template element's node document changes, the template element's content DocumentFragment must be adopted into the new node document's template contents owner document">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+test(function() {
+ var doc1 = newHTMLDocument();
+ var template = doc1.createElement('template');
+
+ assert_equals(template.ownerDocument, doc1, 'Wrong template node owner document');
+ assert_not_equals(template.content.ownerDocument, doc1,
+ 'Wrong template content owner document');
+
+ var doc2 = newHTMLDocument();
+ var template2 = doc2.createElement('template');
+ doc2.body.appendChild(template);
+
+ assert_equals(template.ownerDocument, template2.ownerDocument,
+ 'Template node owner document should be changed');
+ assert_equals(template.content.ownerDocument, template2.content.ownerDocument,
+ 'Template content owner document should be changed');
+
+}, 'Changing of template element\'s node document. ' +
+ 'Test that ownerDocument of an empty template and its content changes');
+
+test(function() {
+ var doc1 = newHTMLDocument();
+ doc1.body.innerHTML = '<template id="tmpl"><div>Div content</div> And some more text</template>';
+
+ var template = doc1.querySelector('#tmpl');
+
+ assert_equals(template.ownerDocument, doc1,
+ 'Wrong template node owner document');
+ assert_not_equals(template.content.ownerDocument, doc1,
+ 'Wrong template content owner document');
+
+ var doc2 = newHTMLDocument();
+ var template2 = doc2.createElement('template');
+ doc2.body.appendChild(template);
+
+ assert_equals(template.ownerDocument, template2.ownerDocument,
+ 'Template node owner document should be changed');
+ assert_equals(template.content.ownerDocument, template2.content.ownerDocument,
+ 'Template content owner document should be changed');
+
+ assert_equals(template.content.querySelector('div').ownerDocument,
+ template2.content.ownerDocument,
+ 'Template content descendants owner document should be changed');
+
+}, 'Changing of template element\'s node document. ' +
+ 'Test that ownerDocument of a not empty template and its content changes');
+
+test(function() {
+ var doc1 = newHTMLDocument();
+ doc1.body.innerHTML = ''
+ + '<template id="tmpl"><div>Div content</div> And some more text'
+ + '<template id="tmpl2"><div>Template content</div></template>'
+ + '</template>';
+
+ var template = doc1.querySelector('#tmpl');
+
+ assert_equals(template.ownerDocument, doc1, 'Wrong template node owner document');
+ assert_not_equals(template.content.ownerDocument, doc1,
+ 'Wrong template content owner document');
+
+ var nestedTemplate = template.content.querySelector('#tmpl2');
+
+ assert_equals(nestedTemplate.ownerDocument, template.content.ownerDocument,
+ 'Wrong nested template node owner document');
+ assert_equals(nestedTemplate.content.ownerDocument, template.content.ownerDocument,
+ 'Wrong nested template content owner document');
+
+ var doc2 = newHTMLDocument();
+ var template2 = doc2.createElement('template');
+ doc2.body.appendChild(template);
+
+ assert_equals(template.ownerDocument, template2.ownerDocument,
+ 'Template node owner document should be changed');
+ assert_equals(template.content.ownerDocument, template2.content.ownerDocument,
+ 'Template content owner document should be changed');
+ assert_equals(template.content.querySelector('div').ownerDocument,
+ template2.content.ownerDocument,
+ 'Template content descendants owner document should be changed');
+
+ assert_equals(nestedTemplate.ownerDocument,
+ template2.content.ownerDocument,
+ 'Nested template node owner document should be changed');
+ assert_equals(nestedTemplate.content.ownerDocument,
+ template2.content.ownerDocument,
+ 'Nested template content owner document should be changed');
+ assert_equals(nestedTemplate.content.querySelector('div').ownerDocument,
+ template2.content.ownerDocument,
+ 'Owner document of the nested template content descendants should be changed');
+
+}, 'Changing of template element\'s node document. ' +
+ 'Test that ownerDocument of nested template and its content changes');
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc1 = context.iframes[0].contentDocument;
+
+ var template = doc1.body.querySelector('template');
+
+ var doc2 = newHTMLDocument();
+ var template2 = doc2.createElement('template');
+ doc2.body.appendChild(template);
+
+ assert_equals(template.ownerDocument, template2.ownerDocument,
+ 'Template node owner document should be changed');
+ assert_equals(template.content.ownerDocument,
+ template2.content.ownerDocument,
+ 'Template content owner document should be changed');
+ assert_equals(template.content.querySelector('div').ownerDocument,
+ template2.content.ownerDocument,
+ 'Template content descendants owner document should be changed');
+
+}, 'Changing of template element\'s node document. ' +
+ 'Test document loaded from a file');
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc1 = context.iframes[0].contentDocument;
+
+ var doc2 = newHTMLDocument();
+ var template = doc2.createElement('template');
+ var div = doc2.createElement('div');
+ template.content.appendChild(div);
+
+ doc1.body.appendChild(template);
+
+ assert_not_equals(template.ownerDocument, doc2,
+ 'Template node owner document should be changed');
+ assert_not_equals(template.content.ownerDocument, doc2,
+ 'Template content owner document should be changed');
+ assert_not_equals(div.ownerDocument, doc2,
+ 'Template content descendants owner document should be changed');
+
+ assert_equals(template.ownerDocument, doc1,
+ 'Template node owner document should be changed');
+ // doc1 has browsing context so it cannot be template.content.ownerDocument
+ assert_not_equals(template.content.ownerDocument, doc1,
+ 'Template content owner document should be a new document');
+ assert_equals(div.ownerDocument, template.content.ownerDocument,
+ 'Template content descendants owner document should be ' +
+ 'template content document owner');
+
+}, 'Changing of template element\'s node document. ' +
+ 'Adobt template element into a document that has a browsing context');
+
+testInIFrame('../resources/template-contents.html', function(context) {
+ var doc1 = context.iframes[0].contentDocument;
+
+ var template = doc1.querySelector('template');
+ var div = template.content.querySelector('div');
+ var templateContentOwner = template.content.ownerDocument;
+
+ var doc2 = document;
+
+ doc2.body.appendChild(template);
+
+ assert_not_equals(template.ownerDocument, doc1,
+ 'Template node owner document should be changed');
+ assert_not_equals(template.content.ownerDocument, templateContentOwner,
+ 'Template content owner document should be changed');
+ assert_not_equals(div.ownerDocument, templateContentOwner,
+ 'Template content descendants owner document should be changed');
+
+ assert_equals(template.ownerDocument, doc2,
+ 'Template node owner document should be changed');
+ // doc2 has browsing context, so it cannot be template.content.ownerDocument
+ assert_not_equals(template.content.ownerDocument, doc2,
+ 'Template content owner document should be a new document');
+ assert_equals(div.ownerDocument, template.content.ownerDocument,
+ 'Template content descendants owner document should be ' +
+ 'template content document owner');
+
+}, 'Changing of template element\'s node document. ' +
+ 'Test the case when both old and new owner documents of template element ' +
+ 'have browsing context');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-as-a-descendant.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-as-a-descendant.html
new file mode 100644
index 0000000000..caa1beab47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-as-a-descendant.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Template element as a descendant of the body element.</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="Template element can be a descendant of the body element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-template-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+function templateIsAChild(element) {
+ element.innerHTML = '<template>some text</template>';
+
+ assert_not_equals(element.querySelector('template'), null,
+ 'Template element should be a descendant of the ' + element.tagName + ' element');
+}
+
+function templateIsDisallowedAsAChild(element) {
+ element.innerHTML = '<template>some text</template>';
+
+ assert_equals(element.querySelector('template'), null,
+ 'Template element should not be allowed as a descendant of the ' + element.tagName + ' element');
+}
+
+function templateIsAnIndirectChild(element) {
+ element.innerHTML = '<div><template>some text</template></div>';
+
+ assert_not_equals(element.querySelector('template'), null,
+ 'Template element should be a descendant of the ' + element.tagName + ' element');
+}
+
+function templateIsDisallowedAsAnIndirectChild(element) {
+ element.innerHTML = '<div><template>some text</template></div>';
+
+ assert_equals(element.querySelector('template'), null,
+ 'Template element should not be allowed as indirect descendant of the ' + element.tagName + ' element');
+}
+
+function templateIsAnAppendedChild(doc, element) {
+ var template = doc.createElement('template');
+
+ element.appendChild(template);
+
+ assert_not_equals(element.querySelector('template'), null,
+ 'Template element should be a descendant of the ' + element.tagName + ' element');
+}
+
+function templateIsAnAppendedIndirectChild(doc, element) {
+ var template = doc.createElement('template');
+ var div = doc.createElement('div');
+ div.appendChild(template);
+
+ element.appendChild(div);
+
+ assert_not_equals(element.querySelector('template'), null,
+ 'Template element should be a descendant of the ' + element.tagName + ' element');
+}
+
+var doc = newHTMLDocument();
+var frameset = doc.createElement('frameset');
+
+var parameters = [['Template element as a descendant of the BODY element. ' +
+ 'Template element is created by innerHTML',
+ doc.body],
+ ['Template element as a descendant of the HEAD element. ' +
+ 'Template element is created by innerHTML',
+ doc.head],
+ ];
+// Template element as a descendant of the HEAD and BODY elements
+generate_tests(templateIsAChild, parameters);
+
+parameters = [['Template element as a descendant of the FRAMESET element. ' +
+ 'Template element is created by innerHTML',
+ frameset],
+ ];
+// Template element should be disallowed as a descendant of the FRAMESET elements
+generate_tests(templateIsDisallowedAsAChild, parameters);
+
+
+parameters = [['Template element as an indirect descendant of the BODY element. ' +
+ 'Template element is created by innerHTML',
+ doc.body],
+ ['Template element as an indirect descendant of the HEAD element. ' +
+ 'Template element is created by innerHTML',
+ doc.head],
+ ];
+// Template element as an indirect descendant of the HEAD, BODY and FRAMESET elements
+generate_tests(templateIsAnIndirectChild, parameters);
+
+parameters = [['Template element as an indirect descendant of the FRAMESET element. ' +
+ 'Template element is created by innerHTML',
+ frameset],
+ ];
+// Template element should be disallowed as an indirect descendant of the FRAMESET elements
+generate_tests(templateIsDisallowedAsAnIndirectChild, parameters);
+
+
+
+parameters = [['Template element as a descendant of the BODY element. ' +
+ 'Template element is appended by appendChild()',
+ doc, doc.body],
+ ['Template element as a descendant of the HEAD element. ' +
+ 'Template element is appended by appendChild()',
+ doc, doc.head],
+ ['Template element as a descendant of the FRAMESET element. ' +
+ 'Template element is appended by appendChild()',
+ doc, frameset]
+ ];
+// Template element as a descendant of the HEAD, BODY and FRAMESET elements
+generate_tests(templateIsAnAppendedChild, parameters);
+
+
+
+parameters = [['Template element as an indirect descendant of the BODY element. ' +
+ 'Template element is appended by appendChild()',
+ doc, doc.body],
+ ['Template element as an indirect descendant of the HEAD element. ' +
+ 'Template element is appended by appendChild()',
+ doc, doc.head],
+ ['Template element as an indirect descendant of the FRAMESET element. ' +
+ 'Template element is appended by appendChild()',
+ doc, frameset]
+ ];
+// Template element as a descendant of the HEAD, BODY and FRAMESET elements
+generate_tests(templateIsAnAppendedIndirectChild, parameters);
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-construction-in-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-construction-in-inactive-document-crash.html
new file mode 100644
index 0000000000..496a47b2c5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-construction-in-inactive-document-crash.html
@@ -0,0 +1,5 @@
+<iframe id="i"></iframe>
+<script>
+i.contentDocument.documentElement.appendChild(document.body);
+</script>
+<template><script></script></template>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-hierarcy.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-hierarcy.html
new file mode 100644
index 0000000000..823c0c830f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-hierarcy.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta name="author" title="Takayoshi Kochi" href="mailto:kochi@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<div id="parent">
+ <template id="tmpl"><span>Happy Templating!</span></template>
+</div>
+<script>
+test(() => {
+ var parent = document.getElementById('parent');
+ var tmpl = document.getElementById('tmpl');
+
+ assert_equals(tmpl.innerHTML, '<span>Happy Templating!</span>');
+ var span = tmpl.content.querySelector('span');
+
+ // Hierarchy checks at various combinations.
+ assert_throws_dom('HierarchyRequestError', () => {
+ tmpl.content.appendChild(parent);
+ }, 'Template content should throw if any of ancestor is being appended.');
+ assert_throws_dom('HierarchyRequestError', () => {
+ tmpl.content.appendChild(tmpl);
+ }, 'Template content should throw if its host is being appended.');
+ assert_throws_dom('HierarchyRequestError', () => {
+ span.appendChild(parent);
+ }, 'Template content child should throw if any of ancestor is being appended.');
+ assert_throws_dom('HierarchyRequestError', () => {
+ span.appendChild(tmpl);
+ }, 'Template content child should throw template\'s host is being appended.');
+}, "Template content should throw when its ancestor is being appended.");
+
+test(() => {
+ var parent = document.getElementById('parent');
+ var tmpl = document.getElementById('tmpl');
+
+ assert_equals(tmpl.innerHTML, '<span>Happy Templating!</span>');
+ var span = tmpl.content.querySelector('span');
+
+ var tmpl_doc = tmpl.content.ownerDocument;
+ assert_equals(tmpl.ownerDocument, document);
+ assert_not_equals(tmpl_doc, document);
+
+ var new_doc = document.implementation.createHTMLDocument();
+ assert_not_equals(new_doc, document);
+ assert_not_equals(new_doc, tmpl_doc);
+
+ // Try moving tmpl.content to new_doc and check the results.
+ const tmplContentNodeDocument = tmpl.content.ownerDocument;
+ const tmplContentAdoptResult = new_doc.adoptNode(tmpl.content);
+ assert_equals(tmpl.content, tmplContentAdoptResult);
+ assert_equals(tmpl.ownerDocument, document);
+ assert_equals(tmpl.content.ownerDocument, tmplContentNodeDocument);
+
+ // Hierarchy checks at various combinations.
+ assert_throws_dom('HierarchyRequestError', () => {
+ tmpl.content.appendChild(parent);
+ }, 'Template content should throw if any of ancestor is being appended.');
+ assert_throws_dom('HierarchyRequestError', () => {
+ tmpl.content.appendChild(tmpl);
+ }, 'Template content should throw if its host is being appended.');
+ assert_throws_dom('HierarchyRequestError', () => {
+ span.appendChild(parent);
+ }, 'Template content child should throw if any of ancestor is being appended.');
+ assert_throws_dom('HierarchyRequestError', () => {
+ span.appendChild(tmpl);
+ }, 'Template content child should throw template\'s host is being appended.');
+
+ // Sanity check: template.content before and after move.
+ var tmpl_content_reference = tmpl.content;
+ assert_equals(tmpl.content.firstChild, span,
+ '<span> should be kept until it is removed, even after ' +
+ 'adopted to another document.');
+ new_doc.body.appendChild(tmpl.content);
+ assert_equals(tmpl.content.firstChild, null,
+ '<span> should be removed from template content.');
+ assert_equals(tmpl_content_reference, tmpl.content,
+ 'template.content should be identical before and after ' +
+ 'moving its children.');
+}, 'Template content should throw exception when its ancestor in ' +
+ 'a different document but connected via host is being append.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-in-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-in-inactive-document-crash.html
new file mode 100644
index 0000000000..66c564c77a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-in-inactive-document-crash.html
@@ -0,0 +1,7 @@
+<iframe id="i"></iframe>
+<script>
+var t = i.contentDocument.createElement("template");
+i.contentDocument.documentElement.appendChild(t);
+i.remove();
+t.content;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-move-to-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-move-to-inactive-document-crash.html
new file mode 100644
index 0000000000..89c56d1663
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-move-to-inactive-document-crash.html
@@ -0,0 +1,7 @@
+<div id="d">
+<iframe id="i"></iframe>
+<template id="t"> </template>
+</div>
+<script>
+i.contentDocument.documentElement.appendChild(d);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-node-document.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-node-document.html
new file mode 100644
index 0000000000..da76c6b04f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content-node-document.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Node document of the template content attribute must be template contents owner</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="Node document of the template content attribute must be template contents owner">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+ var nestedTemplate = doc.createElement('template');
+ template.appendChild(nestedTemplate);
+
+ assert_equals(nestedTemplate.content.ownerDocument, template.content.ownerDocument,
+ 'Wrong node document of the template content attribute');
+
+}, 'Node document of the template content attribute must be template contents owner. ' +
+ 'Nested template element created by createElement');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template><template></template></template>';
+ var template = doc.querySelector('template');
+ var nestedTemplate = template.content.querySelector('template');
+
+ assert_equals(nestedTemplate.content.ownerDocument, template.content.ownerDocument,
+ 'Wrong node document of the template content attribute');
+
+}, 'Node document of the template content attribute must be template contents owner. ' +
+ 'Nested template element created by innerHTML');
+
+testInIFrame('../resources/two-templates.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template1 = doc.querySelector('#template1');
+ var template2 = doc.querySelector('#template2');
+
+ // when there is a browsing context, template contents owner is only accessible via template.content.ownerDocument
+ // because template contents owner is bounded to document
+ // verify that multiple templates share the same instance of template contents owner
+
+ assert_equals(template1.content.ownerDocument, template2.content.ownerDocument,
+ 'Wrong node document of the template content attribute');
+}, 'Node document of the template content attribute must be template contents owner. ' +
+ 'Load HTML file with multiple template elements');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content.html
new file mode 100644
index 0000000000..b9e790daf8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-content.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: HTML elements in template content</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="Template may contain any element, except the html element, the head element, the body element, or the frameset element">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+HTML5_ELEMENTS.forEach(function(value) {
+ if (value !== 'body' && value !== 'html' && value !== 'head' && value !== 'frameset') {
+
+ test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+ var element = doc.createElement(value);
+ template.content.appendChild(element);
+ var valueToTest = template.content.querySelector(value);
+
+ doc.body.appendChild(template);
+
+ assert_not_equals(valueToTest, null);
+ }, 'Template may contain ' + value + ' element');
+
+ }
+});
+
+
+
+var parameters = [];
+
+HTML5_ELEMENTS.forEach(function(value) {
+ if (value !== 'body' && value !== 'html' && value !== 'head' && value !== 'frameset') {
+
+ test(function() {
+ var doc = newHTMLDocument();
+
+ if (isVoidElement(value)) {
+ doc.body.innerHTML = '<template><' + value + '/></template>';
+ } else {
+ doc.body.innerHTML = '<template><' + value + '></' + value + '></template>';
+ }
+
+ var template = doc.querySelector('template');
+ var element = template.content.querySelector(value);
+
+ assert_not_equals(element, null);
+ }, 'Template may contain ' + value + ' element. '
+ + 'The template element and contents are added via body.innerHTML');
+
+ }
+});
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-body.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-body.html
new file mode 100644
index 0000000000..70028c5ecd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-body.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Template element as a descendant of the body element.</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="Template element can be a descendant of the body element">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+testInIFrame('../resources/template-contents.html', function(ctx) {
+ var doc = ctx.iframes[0].contentDocument;
+
+ assert_not_equals(doc.body.querySelector('template'), null,
+ 'Template element should be a descendant of the body element');
+
+}, 'Template element as a descendant of the body element. Test loading from a file');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-frameset.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-frameset.html
new file mode 100644
index 0000000000..ce20a7413e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-frameset.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Template element as a descendant of the frameset element.</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="Template element can not be a descendant of the frameset element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#parsing-main-inframeset">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+testInIFrame('../resources/template-descendant-frameset.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var frameset = doc.querySelector('frameset');
+
+ assert_equals(frameset.querySelector('template'), null,
+ 'Template element should not be a descendant of the frameset element');
+
+}, 'Template element as a descendant of the frameset element. Test loading from a file');
+
+
+testInIFrame('../resources/template-descendant-frameset.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var frameset = doc.querySelector('frameset');
+
+ frameset.innerHTML = '';
+ assert_equals(doc.querySelector('template'), null,
+ 'Initial conditions are not satisfied');
+
+ frameset.innerHTML = '<template>some text</template>';
+
+ assert_equals(frameset.querySelector('template'), null,
+ 'Template element should not be a descendant of the frameset element');
+
+}, 'Template element as a descendant of the frameset element. '
+ + 'Test template element is assigned to frameset\'s innerHTML)');
+
+
+testInIFrame('../resources/template-descendant-frameset.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var frameset = doc.querySelector('frameset');
+
+ var template = doc.createElement('template');
+ frameset.appendChild(template);
+
+ assert_equals(frameset.querySelectorAll('template').length, 1,
+ 'Template element should be a descendant of the frameset element');
+
+}, 'Template element as a descendant of the frameset element. '
+ + 'Test template element appended to frameset by appendChild()');
+
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-head.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-head.html
new file mode 100644
index 0000000000..611ec50bb4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-descendant-head.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Template element as a descendant of the head element.</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="Template element can be a descendant of the head element">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src='/html/resources/common.js'></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+testInIFrame('../resources/template-descendant-head.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ assert_not_equals(doc.head.querySelector('template'), null,
+ 'Template element should be a descendant of the head element');
+
+}, 'Template element as a descendant of the head element. Test loading from a file');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-element-clone-into-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-element-clone-into-inactive-document-crash.html
new file mode 100644
index 0000000000..56b199d1eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-element-clone-into-inactive-document-crash.html
@@ -0,0 +1,7 @@
+<template id="t"> </template>
+<iframe id="i"></iframe>
+<script>
+var doc = i.contentDocument;
+i.remove();
+doc.importNode(t, true);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-set-inner-html-in-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-set-inner-html-in-inactive-document-crash.html
new file mode 100644
index 0000000000..79a76ee76f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-element/template-set-inner-html-in-inactive-document-crash.html
@@ -0,0 +1,6 @@
+<iframe id="i"></iframe>
+<script>
+ var doc = i.contentDocument;
+ i.remove();
+ doc.createElement("template").innerHTML = "";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-table-crash.html b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-table-crash.html
new file mode 100644
index 0000000000..0f6a49874d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/scripting-1/the-template-element/template-table-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://crbug.com/1212619">
+<meta name="assert" content="The renderer should not crash.">
+
+<template id=tmpl></template>
+<table id=tbl>
+ <script>
+ tmpl.appendChild(tbl);
+ </script>
+ Crash
+</table>
diff --git a/testing/web-platform/tests/html/semantics/sections/the-h1-h2-h3-h4-h5-and-h6-elements/original-id.json b/testing/web-platform/tests/html/semantics/sections/the-h1-h2-h3-h4-h5-and-h6-elements/original-id.json
new file mode 100644
index 0000000000..748a548ca9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/sections/the-h1-h2-h3-h4-h5-and-h6-elements/original-id.json
@@ -0,0 +1 @@
+{"original_id":"the-h1,-h2,-h3,-h4,-h5,-and-h6-elements"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/selectors/META.yml b/testing/web-platform/tests/html/semantics/selectors/META.yml
new file mode 100644
index 0000000000..3195b8671c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - lilles
diff --git a/testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js b/testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js
new file mode 100644
index 0000000000..1973398bff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js
@@ -0,0 +1,91 @@
+// https://html.spec.whatwg.org/#case-sensitivity-of-selectors
+[
+ "accept",
+ "accept-charset",
+ "align",
+ "alink",
+ "axis",
+ "bgcolor",
+ "charset",
+ "checked",
+ "clear",
+ "codetype",
+ "color",
+ "compact",
+ "declare",
+ "defer",
+ "dir",
+ "direction",
+ "disabled",
+ "enctype",
+ "face",
+ "frame",
+ "hreflang",
+ "http-equiv",
+ "lang",
+ "language",
+ "link",
+ "media",
+ "method",
+ "multiple",
+ "nohref",
+ "noresize",
+ "noshade",
+ "nowrap",
+ "readonly",
+ "rel",
+ "rev",
+ "rules",
+ "scope",
+ "scrolling",
+ "selected",
+ "shape",
+ "target",
+ "text",
+ "type",
+ "valign",
+ "valuetype",
+ "vlink",
+].forEach(attributeName => {
+ const xmlDocument = new Document();
+ const htmlDocument = document;
+ [
+ {
+ input: xmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "a"),
+ expected: false,
+ title: "<html:a> in XML",
+ },
+ {
+ input: xmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "unknown"),
+ expected: false,
+ title: "<html:unknown> in XML",
+ },
+ {
+ input: xmlDocument.createElementNS("", "unknown"),
+ expected: false,
+ title: "<:unknown> in XML"
+ },
+ {
+ input: htmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "a"),
+ expected: true,
+ title: "<html:a> in HTML",
+ },
+ {
+ input: htmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "unknown"),
+ expected: true,
+ title: "<html:unknown> in HTML",
+ },
+ {
+ input: htmlDocument.createElementNS("", "unknown"),
+ expected: false,
+ title: "<:unknown> in HTML"
+ },
+ ].forEach(({ input, expected, title }) => {
+ test(t => {
+ t.add_cleanup(() => input.removeAttribute(attributeName));
+ input.setAttribute(attributeName, "HEYÃ");
+ assert_equals(input.matches(`[${attributeName}^=hey]`), expected, `^=hey`);
+ assert_false(input.matches(`[${attributeName}^=heyi]`));
+ }, `${attributeName}'s value is properly ASCII-case-insensitive for ${title}`);
+ });
+});
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html
new file mode 100644
index 0000000000..a75a157c58
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/7465">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<label id=buttonlabel for=disabledbutton>label for disabled button</label>
+<button id=disabledbutton disabled>disabled</button>
+
+<button id=buttonparent disabled>
+ <div id=buttonchild>child of disabled</div>
+</button>
+
+<input id=disabledinput disabled>
+
+<textarea id=disabledtextarea disabled>disabled textarea</textarea>
+
+<script>
+function testElement(description, clickElement, checkElement) {
+ promise_test(async () => {
+ if (!checkElement)
+ checkElement = clickElement;
+
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, {origin: clickElement})
+ .pointerDown())
+ .send();
+
+ assert_true(checkElement.matches(':active'));
+
+ await (new test_driver.Actions()
+ .pointerUp())
+ .send();
+ }, description);
+}
+
+testElement('Clicking on a disabled button should make it match the :active selector.',
+ disabledbutton);
+
+testElement('Clicking the label for a disabled button should make the button match the :active selector.',
+ buttonlabel, disabledbutton);
+
+testElement('Clicking on a child of a disabled button should make the button match the :active selector.',
+ buttonchild, buttonparent);
+
+testElement('Clicking on a disabled input should make it match the :active selector.',
+ disabledinput);
+
+testElement('Clicking on a disabled textarea should make it match the :active selector.',
+ disabledtextarea);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html
new file mode 100644
index 0000000000..b7c3644bdb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:autofill)</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+test_valid_selector(":autofill");
+test_valid_selector(":-webkit-autofill", [":autofill", ":-webkit-autofill"]);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html
new file mode 100644
index 0000000000..76a963a600
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+ <head>
+ <title>CSS Selectors (:checked)</title>
+ <link rel="author" title="Ian Hickson" href="mailto:ian@hixie.ch"/>
+ <link rel="alternate" href="http://www.hixie.ch/tests/adhoc/css/selectors/checked/001.html"/>
+ <style type="text/css">
+ :checked, :checked + span { border: solid blue; color: blue; background: navy; }
+ </style>
+ </head>
+ <body>
+ <p>Anything that is checked below should be blue.</p>
+ <p><input checked type="checkbox"> <span>X</span></p>
+ <p><input checked type="radio" name="x"> <span>X</span> <input checked type="radio" name="x"> <span>X</span></p>
+ <p><select><option selected>X</option></select></p>
+ <p><select size="2"><option selected>X</option></select></p>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js
new file mode 100644
index 0000000000..167cbdd37f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js
@@ -0,0 +1,27 @@
+test(() => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+
+ assert_false(input.matches(":checked:indeterminate"));
+ assert_false(input.matches(":checked"));
+ assert_false(input.matches(":indeterminate"));
+
+ input.checked = true;
+ input.indeterminate = true;
+
+ assert_true(input.matches(":checked:indeterminate"));
+ assert_true(input.matches(":checked"));
+ assert_true(input.matches(":indeterminate"));
+
+ input.indeterminate = false;
+
+ assert_false(input.matches(":checked:indeterminate"));
+ assert_true(input.matches(":checked"));
+ assert_false(input.matches(":indeterminate"));
+
+ input.checked = false;
+
+ assert_false(input.matches(":checked:indeterminate"));
+ assert_false(input.matches(":checked"));
+ assert_false(input.matches(":indeterminate"));
+}, "An element can be :checked and :indeterminate at the same time");
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html
new file mode 100644
index 0000000000..661d9e4355
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-class :checked input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span { color: red }
+ :checked + span { color: green }
+</style>
+<input id="checked" type="text" checked>
+<span id="sibling">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :checked for type=text");
+
+ checked.type = "radio";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :checked for type=radio");
+ }, "Evaluation of :checked changes on input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html
new file mode 100644
index 0000000000..754c2342bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:checked)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1 selected>option1
+ <option value="option2" id=option2>option2
+ <option value="option2" id=option3 checked>option3
+</select>
+<input type=checkbox id=checkbox1 checked>
+<input type=checkbox id=checkbox2>
+<input type=checkbox id=checkbox3 selected>
+<input type=radio id=radio1 checked>
+<input type=radio id=radio2>
+<form>
+ <p><input type=submit contextmenu=formmenu id="submitbutton"></p>
+ <menu type=context id=formmenu>
+ <!-- historical; these should *not* match -->
+ <menuitem type=checkbox checked default id=menuitem1>
+ <menuitem type=checkbox default id=menuitem2>
+ <menuitem type=checkbox id=menuitem3>
+ <menuitem type=radio checked id=menuitem4>
+ <menuitem type=radio id=menuitem5>
+ </menu>
+</form>
+
+<script>
+ testSelectorIdsMatch(":checked", ["option1", "checkbox1", "radio1"], "':checked' matches checked <input>s in checkbox and radio button states, selected <option>s");
+
+ document.getElementById("checkbox1").removeAttribute("type"); // change type of input
+ document.getElementById("radio1").removeAttribute("type"); // change type of input
+ testSelectorIdsMatch(":checked", ["option1"], "':checked' should no longer match <input>s whose type checkbox/radio has been removed");
+
+ document.getElementById("option2").selected = "selected"; // select option2
+ document.getElementById("checkbox2").click(); // check chekbox2
+ document.getElementById("radio2").click(); // check radio2
+ testSelectorIdsMatch(":checked", ["option2", "checkbox2", "radio2"], "':checked' matches clicked checkbox and radio buttons");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html
new file mode 100644
index 0000000000..3187801f67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:default)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<form>
+ <button id=button1 type=button>button1</button>
+ <button id=button2 type=submit>button2</button>
+</form>
+<form>
+ <button id=button3 type=reset>button3</button>
+ <button id=button4>button4</button>
+</form>
+<button id=button5 type=submit>button5</button>
+<form id=form1>
+ <input type=text id=input1>
+</form>
+<input type=text id=input2 form=form1>
+<form>
+ <input type=submit id=input3>
+ <input type=submit id=input4>
+</form>
+<form>
+ <input type=image id=input5>
+ <input type=image id=input6>
+</form>
+<form>
+ <input type=submit id=input7>
+</form>
+<input type=checkbox id=checkbox1 checked>
+<input type=checkbox id=checkbox2>
+<input type=checkbox id=checkbox3 default>
+<input type=radio name=radios id=radio1 checked>
+<input type=radio name=radios id=radio2>
+<input type=radio name=radios id=radio3 default>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1>option1
+ <option value="option2" id=option2 selected>option2
+</select>
+<dialog id="dialog">
+ <input type=submit id=input8>
+</dialog>
+<form>
+ <button id=button6 type='invalid'>button6</button>
+ <button id=button7>button7</button>
+</form>
+<form>
+ <button id=button8>button8</button>
+ <button id=button9>button9</button>
+</form>
+
+
+<script>
+ testSelectorIdsMatch(":default", ["button2", "button4", "input3", "input5", "input7", "checkbox1", "radio1", "option2", "button6", "button8"], "':default' matches <button>s that are their form's default button, <input>s of type submit/image that are their form's default button, checked <input>s and selected <option>s");
+
+ document.getElementById("button1").type = "submit"; // change the form's default button
+ testSelectorIdsMatch(":default", ["button1", "button4", "input3", "input5", "input7", "checkbox1", "radio1", "option2", "button6", "button8"], "':default' matches dynamically changed form's default buttons");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html
new file mode 100644
index 0000000000..8f2951f8ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="Vincent Hilla" href="mailto:vhilla@mozilla.com">
+ <link rel="help" href="https://html.spec.whatwg.org/#the-directionality">
+</head>
+<body>
+ <input id="inp"/>
+ <textarea id="ta"></textarea>
+ <div id="div"></div>
+ <pre id="pre"></pre>
+
+ <script>
+ function doTest(e) {
+ e.dir = "ltr";
+ assert_true(e.matches(":dir(ltr)"), "dir to ltr on " + e.tagName + " element");
+
+ e.dir = "rtl";
+ assert_true(e.matches(":dir(rtl)"), "dir to rtl on " + e.tagName + " element");
+
+ e.dir = "auto";
+ assert_true(e.matches(":dir(ltr)"), "dir to auto, empty text on " + e.tagName + " element");
+
+ e.value = "\u05D0;";
+ e.textContent = "\u05D0;";
+ assert_true(e.matches(":dir(rtl)"), "auto dir, text to Hebrew on " + e.tagName + " element");
+
+ e.dir = "ltr";
+ assert_true(e.matches(":dir(ltr)"), "dir to ltr, Hebrew text on " + e.tagName + " element");
+
+ e.dir = "auto";
+ assert_true(e.matches(":dir(rtl)"), "dir to auto, Hebrew text on " + e.tagName + " element");
+
+ e.removeAttribute("dir");
+ assert_true(e.matches(":dir(ltr)"), "dir removed, Hebrew text on " + e.tagName + " element");
+ }
+
+ const elements = [inp, ta, div, pre];
+ for (const e of elements) {
+ test(() => doTest(e), "Dynamically changing dir, text on " + e.tagName.toLowerCase() + " element");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html
new file mode 100644
index 0000000000..0c50cec369
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1622900">
+<link rel="help" href="https://html.spec.whatwg.org/#the-directionality">
+<input value="ltr" dir="auto">
+<script>
+test(function() {
+ let input = document.querySelector("input");
+ assert_true(input.matches(":dir(ltr)"), "Input with ltr value should match dir(ltr)");
+ input.textContent = "ï·º";
+ assert_true(input.matches(":dir(ltr)"), "Should still match dir(ltr) after text change");
+ input.value = "ltr2";
+ assert_true(input.matches(":dir(ltr)"), "Should still match dir(ltr) after value change");
+ input.value = "ï·º";
+ assert_true(input.matches(":dir(rtl)"), "Should match dir(rtl) after value change");
+ input.textContent = "ltr";
+ assert_true(input.matches(":dir(rtl)"), "Should match dir(rtl) after text change");
+}, ":dir on <input> isn't altered by text children")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html
new file mode 100644
index 0000000000..588c3c6850
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html id=html>
+ <head id=head>
+ <meta charset=utf-8 id=meta>
+ <title id=title>Selector: pseudo-classes (:dir(ltr), :dir(rtl))</title>
+ <link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+ <link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+ <script src="/resources/testharness.js" id=script1></script>
+ <script src="/resources/testharnessreport.js" id=script2></script>
+ <script src="utils.js" id=script3></script>
+ <style id=style>
+ #span1 {direction: rtl;}
+ #span5, #span6 {display: none;}
+ </style>
+ </head>
+ <body id=body>
+ <div id="log"></div>
+ <bdo dir="rtl" id=bdo1>WERBEH</bdo>
+ <bdo dir="ltr" id=bdo2>HEBREW</bdo>
+ <bdi id=bdi1>HEBREW</bdi>
+ <bdi dir="rtl" id=bdi2>WERBEH</bdi>
+ <bdi dir="ltr" id=bdi3>HEBREW</bdi>
+ <bdi id=bdi4>إيان</bdi>
+ <span id=span1>WERBEH</span>
+ <span dir="rtl" id=span2>WERBEH</span>
+ <span dir="ltr" id=span3>HEBREW</span>
+ &#x202E;<span id=span4>WERBEH</span>&#x202C;
+ <span dir="rtl" id=span5>WERBEH</span>
+ <span dir="ltr" id=span6>HEBREW</span>
+ <span dir="rtl" id=span7>
+ <input type=tel id=input-tel1>
+ <input type=tel id=input-tel2 dir="invalid">
+ </span>
+ <input type=tel id=input-tel3 dir="rtl">
+ <bdo dir="auto" id=bdo3>HEBREW</bdo>
+ <bdo dir="auto" id=bdo4>إيان</bdo>
+ <bdo dir="ltr" id=bdo5>עברית</bdo>
+ <textarea dir="auto" id="ta1">إيان</textarea>
+ <textarea dir="auto" id="ta2">HEBREWإيان</textarea>
+ <textarea dir="auto" id="ta3">إيان</textarea>
+ <pre dir="auto" id="pre1">إيان</pre>
+ <pre dir="auto" id="pre2">HEBREWإيان</pre>
+
+ <script id=script4>
+ ta3.value = "HEBREW";
+
+ const rtlElements = [
+ "bdo1",
+ "bdi2",
+ "bdi4",
+ "span2",
+ "span5",
+ "span7",
+ "input-tel3",
+ "bdo4",
+ "ta1",
+ "pre1",
+ ];
+
+ testSelectorIdsMatch(":dir(rtl)", rtlElements, "':dir(rtl)' matches all elements whose directionality is 'rtl'.");
+
+ const ltrElements = [
+ "html",
+ "head",
+ "meta",
+ "title",
+ "link1",
+ "link2",
+ "script1",
+ "script2",
+ "script3",
+ "style",
+ "body",
+ "log",
+ "bdo2",
+ "bdi1",
+ "bdi3",
+ "span1",
+ "span3",
+ "span4",
+ "span6",
+ "input-tel1",
+ "input-tel2",
+ "bdo3",
+ "bdo5",
+ "ta2",
+ "ta3",
+ "pre2",
+ "script4",
+ ];
+
+ testSelectorIdsMatch(":dir(ltr)", ltrElements, "':dir(ltr)' matches all elements whose directionality is 'ltr'.");
+
+ const bdo = document.createElement("bdo");
+ bdo.setAttribute("dir", "ltr");
+ testSelectorIdsMatch(":dir(ltr)", ltrElements, "':dir(ltr)' doesn't match elements not in the document.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html
new file mode 100644
index 0000000000..61bbd574a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=iso-8859-8 id=meta>
+<title id=title>Selector: pseudo-classes (:dir(ltr), :dir(rtl)) in iso-8859-8 documents</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js" id=script1></script>
+<script src="/resources/testharnessreport.js" id=script2></script>
+<script src="utils.js" id=script3></script>
+<div id="log"></div>
+<div>This text is left to right<div id=div1 style="direction:rtl">this is right to left</div></div>
+<div>This text is left to right<span id=div2 style="direction:rtl">this is left to right</span></div>
+
+<script>
+ var ltr = new Array(),
+ all = document.querySelectorAll('*');
+ for(var i = all.length; i--; ltr.unshift(all[i]));
+ testSelectorElementsMatch(":dir(ltr)", ltr, "direction doesn't affect :dir()");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html
new file mode 100644
index 0000000000..142a12dd12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:disabled)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<style>
+ #input4 {display:none;}
+</style>
+<div id="log"></div>
+<button id=button1 type=submit>button1</button>
+<button id=button2 disabled>button2</button>
+<input id=input1>
+<input id=input2 disabled>
+<input id=input3 readonly>
+<input id=input4>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1 selected>option1
+</select>
+<select disabled id=select2>
+ <optgroup label="options" disabled id=optgroup2>
+ <option value="option2" disabled id=option2>option2
+</select>
+<textarea id=textarea1>textarea1</textarea>
+<textarea disabled id=textarea2>textarea2</textarea>
+<fieldset id=fieldset1></fieldset>
+<fieldset disabled id=fieldset2>
+ <legend><input type=checkbox id=club></legend>
+ <p><label>Name on card: <input id=clubname required></label></p>
+ <p><label>Card number: <input id=clubnum required pattern="[-0-9]+"></label></p>
+</fieldset>
+<label disabled></label>
+<object disabled></object>
+<output disabled></output>
+<img disabled/>
+<meter disabled></meter>
+<progress disabled></progress>
+
+<script>
+ testSelectorIdsMatch(":disabled", ["button2", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should match only disabled elements");
+
+ document.getElementById("button2").removeAttribute("disabled");
+ testSelectorIdsMatch(":disabled", ["input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should not match elements whose disabled attribute has been removed");
+
+ document.getElementById("button1").setAttribute("disabled", "disabled");
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set");
+
+ document.getElementById("button1").setAttribute("disabled", "disabled");
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set twice");
+
+ document.getElementById("input2").setAttribute("type", "submit"); // change input type to submit
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match disabled elements whose type has changed");
+
+ var input = document.createElement("input");
+ input.setAttribute("disabled", "disabled");
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should not match elements not in the document");
+
+ var fieldset = document.createElement("fieldset");
+ fieldset.id = "fieldset_nested";
+ fieldset.innerHTML = `
+ <input id=input_nested>
+ <button id=button_nested>button nested</button>
+ <select id=select_nested>
+ <optgroup label="options" id=optgroup_nested>
+ <option value="options" id=option_nested>option nested</option>
+ </optgroup>
+ </select>
+ <textarea id=textarea_nested>textarea nested</textarea>
+ <object id=object_nested></object>
+ <output id=output_nested></output>
+ <fieldset id=fieldset_nested2>
+ <input id=input_nested2>
+ </fieldset>
+ `;
+ document.getElementById("fieldset2").appendChild(fieldset);
+ testSelectorIdsMatch("#fieldset2 :disabled", ["clubname", "clubnum", "fieldset_nested", "input_nested", "button_nested", "select_nested", "textarea_nested", "fieldset_nested2", "input_nested2"], "':disabled' should match elements that are appended to a disabled fieldset dynamically");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html
new file mode 100644
index 0000000000..0ad0e1b402
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:enabled)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<a id=link3></a>
+<area id=link4></area>
+<link id=link5></link>
+<a href="http://www.w3.org" id=link6></a>
+<area href="http://www.w3.org" id=link7></area>
+<link href="http://www.w3.org" id=link8></link>
+<button id=button1>button1</button>
+<button id=button2 disabled>button2</button>
+<input id=input1>
+<input id=input2 disabled>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1 selected>option1
+</select>
+<select disabled id=select2>
+ <optgroup label="options" disabled id=optgroup2>
+ <option value="option2" disabled id=option2>option2
+</select>
+<textarea id=textarea1>textarea1</textarea>
+<textarea disabled id=textarea2>textarea2</textarea>
+<form>
+ <p><input type=submit contextmenu=formmenu id=submitbutton></p>
+ <menu type=context id=formmenu>
+ <!-- historical; these should *not* match -->
+ <menuitem command="submitbutton" default id=menuitem1>
+ <menuitem command="resetbutton" disabled id=menuitem2>
+ </menu>
+</form>
+<fieldset id=fieldset1></fieldset>
+<fieldset disabled id=fieldset2></fieldset>
+
+<script>
+ testSelectorIdsMatch(":enabled", ["button1", "input1", "select1", "optgroup1", "option1", "textarea1", "submitbutton", "fieldset1"], "':enabled' elements that are not disabled");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html
new file mode 100644
index 0000000000..80a75bb99e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:focus for autofocus)</title>
+<link rel="author" title="Kent Tamura" href="mailto:tkent@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<link rel=help href="https://html.spec.whatwg.org/multipage/forms.html#autofocusing-a-form-control:-the-autofocus-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// This test can't be merged to focus.html because element.focus() may affect
+// autofocus behavior.
+var autofocusTest = async_test(":focus selector should work with an autofocused element.");
+var input = document.createElement("input");
+input.autofocus = true;
+input.addEventListener("focus", function() {
+ autofocusTest.step(function() {
+ assert_array_equals(document.querySelectorAll(":focus"), [input])
+ autofocusTest.done();
+ });
+}, false);
+document.body.appendChild(input);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html
new file mode 100644
index 0000000000..a269f1c671
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:focus)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<input id="inputiframe" type=text value="foobar" />
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html
new file mode 100644
index 0000000000..a319b24ef0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:focus)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<body id=body tabindex=0>
+ <div id="log"></div>
+ <button id=button1 type=submit>button1</button>
+ <input id=input1>
+ <input id=input2 disabled>
+ <textarea id=textarea1>textarea1</textarea>
+ <input type=checkbox id=checkbox1 checked>
+ <input type=radio id=radio1 checked>
+ <div tabindex=0 id=div1>hello</div>
+ <div contenteditable id=div2>content</div>
+ <iframe src="focus-iframe.html" id=iframe></iframe>
+
+ <script>
+ setup({explicit_done: true});
+
+ onload = function() {
+ if (document.hasFocus() || frames[0].document.hasFocus()) {
+ run_test()
+ } else {
+ window.onfocus = run_test;
+ }
+ }
+
+ function run_test() {
+ document.getElementById("input1").focus(); // set the focus on input1
+ testSelectorIdsMatch(":focus", ["input1"], "input1 has the focus");
+
+ document.getElementById("div1").focus();
+ testSelectorIdsMatch(":focus", ["div1"], "tabindex attribute makes the element focusable");
+
+ document.getElementById("div2").focus();
+ testSelectorIdsMatch(":focus", ["div2"], "editable elements are focusable");
+
+ document.body.focus();
+ testSelectorIdsMatch(":focus", ["body"], "':focus' matches focussed body with tabindex");
+
+ document.getElementById("iframe").contentDocument.getElementById("inputiframe").focus();
+ testSelectorIdsMatch(":focus", [], "':focus' doesn't match focused elements in iframe");
+
+ done();
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html
new file mode 100644
index 0000000000..68d95fe240
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test reference</title>
+<style>
+label[for="two"] {
+ background: green;
+}
+</style>
+<form>
+ <input type="radio" name="a" id="one" value="1">
+ <label for="one">One</label>
+
+ <input type="radio" name="a" id="two" value="2" checked>
+ <label for="two">Two</label>
+
+ <input type="radio" name="a" id="three" value="3">
+ <label for="three">Three</label>
+
+ <input type="radio" name="a" id="four" value="4">
+ <label for="four">Four</label>
+</form>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html
new file mode 100644
index 0000000000..a49b957021
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>:indeterminate and input type=radio</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Ãlvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1861346">
+<link rel="match" href="indeterminate-radio-group-ref.html">
+<style>
+input:checked + label {
+ background: green;
+}
+input:indeterminate + label {
+ background: red;
+}
+</style>
+<form>
+ <input type="radio" name="a" id="one" value="1">
+ <label for="one">One</label>
+
+ <input type="radio" name="a" id="two" value="2" checked>
+ <label for="two">Two</label>
+
+ <input type="radio" name="a" id="three" value="3">
+ <label for="three">Three</label>
+
+ <input type="radio" name="a" id="four" value="4">
+ <label for="four">Four</label>
+</form>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html
new file mode 100644
index 0000000000..4a7b2d6ece
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>:indeterminate and input type=radio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type="text/css">
+#test {
+ color: green;
+}
+input:indeterminate + #test {
+ color: red;
+}
+</style>
+<input type="radio" name="radios">
+<div id="test"></div>
+<input type="radio" name="radios" checked>
+<script type="text/javascript">
+test(function() {
+ document.getElementsByTagName("input")[0].indeterminate = true;
+ var target = document.getElementById("test");
+ var val = getComputedStyle(target, null).getPropertyValue("color");
+ assert_equals(val, "rgb(0, 128, 0)",
+ "The indeterminate IDL attribute should not cause the " +
+ ":indeterminate pseudo-class to match on input type=radio");
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html
new file mode 100644
index 0000000000..b3e4cce302
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-class :indeterminate input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span { color: red }
+ :indeterminate + span { color: green }
+</style>
+<input id="indeterminate" type="text">
+<span id="sibling">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :indeterminate for type=text");
+
+ indeterminate.type = "radio";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :indeterminate for type=radio");
+ }, "Evaluation of :indeterminate changes on input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html
new file mode 100644
index 0000000000..df04846676
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:indeterminate)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<input type=checkbox id=checkbox1>
+<input type=checkbox id=checkbox2>
+<input type=radio id=radio1 checked>
+<input type=radio name=radiogroup id=radio2>
+<input type=radio name=radiogroup id=radio3>
+<input type=radio name=group2 id=radio4>
+<input type=radio name=group2 id=radio5>
+<progress id="progress1"></progress>
+<progress id="progress2" value=10></progress>
+
+<script>
+ testSelectorIdsMatch(":indeterminate", ["radio2", "radio3", "radio4", "radio5", "progress1"], "':progress' matches <input>s radio buttons whose radio button group contains no checked input and <progress> elements without value attribute");
+
+ document.getElementById("radio2").setAttribute("checked", "checked");
+ testSelectorIdsMatch(":indeterminate", ["radio4", "radio5", "progress1"], "dynamically check a radio input in a radio button group");
+
+ document.getElementById("radio4").click();
+ testSelectorIdsMatch(":indeterminate", ["progress1"], "click on radio4 which is in the indeterminate state");
+
+ document.getElementById("progress1").setAttribute("value", "20");
+ testSelectorIdsMatch(":indeterminate", [], "adding a value to progress1 should put it in a determinate state");
+
+ document.getElementById("progress2").removeAttribute("value");
+ testSelectorIdsMatch(":indeterminate", ["progress2"], "removing progress2's value should put it in an indeterminate state");
+
+ document.getElementById("checkbox1").indeterminate = true; // set checkbox1 in the indeterminate state
+ testSelectorIdsMatch(":indeterminate", ["checkbox1", "progress2"], "':progress' also matches <input> checkbox whose indeterminate IDL is set to true");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js
new file mode 100644
index 0000000000..b5d9898a64
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js
@@ -0,0 +1,75 @@
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.indeterminate = true;
+
+ assert_false(input.matches(":indeterminate"));
+}, "Switch control does not match :indeterminate");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.indeterminate = true;
+
+ assert_false(input.matches(":indeterminate"));
+
+ input.switch = false;
+ assert_true(input.matches(":indeterminate"));
+}, "Checkbox that is no longer a switch control does match :indeterminate");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.indeterminate = true;
+
+ assert_true(input.matches(":indeterminate"));
+
+ input.setAttribute("switch", "blah");
+ assert_false(input.matches(":indeterminate"));
+}, "Checkbox that becomes a switch control does not match :indeterminate");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.indeterminate = true;
+
+ assert_true(document.body.matches(":has(:indeterminate)"));
+
+ input.switch = true;
+ assert_false(document.body.matches(":has(:indeterminate)"));
+}, "Parent of a checkbox that becomes a switch control does not match :has(:indeterminate)");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true
+ input.checked = true;
+
+ assert_true(document.body.matches(":has(:checked)"));
+
+ input.switch = false;
+ assert_true(document.body.matches(":has(:checked)"));
+
+ input.checked = false;
+ assert_false(document.body.matches(":has(:checked)"));
+}, "Parent of a switch control that becomes a checkbox continues to match :has(:checked)");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.indeterminate = true;
+ assert_false(input.matches(":indeterminate"));
+ input.type = "text";
+ input.removeAttribute("switch");
+ input.type = "checkbox";
+ assert_true(input.matches(":indeterminate"));
+}, "A switch control that becomes a checkbox in a roundabout way");
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html
new file mode 100644
index 0000000000..9c1be9ca27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-classes (:in-range, :out-of-range) input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ }
+ #t1:in-range + span {
+ color: green;
+ }
+ #t2:out-of-range + span {
+ color: green;
+ }
+</style>
+<input id="t1" type="text" min="0" max="10" value="5">
+<span id="sibling1">This text should be green.</span>
+<input id="t2" type="text" min="0" max="10" value="50">
+<span id="sibling2">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling1).color, "rgb(255, 0, 0)",
+ "Not matching :in-range for type=text");
+
+ t1.type = "number";
+
+ assert_equals(getComputedStyle(sibling1).color, "rgb(0, 128, 0)",
+ "Matching :in-range for type=number");
+ }, "Evaluation of :in-range changes for input type change.");
+
+ test(() => {
+ assert_equals(getComputedStyle(sibling2).color, "rgb(255, 0, 0)",
+ "Not matching :out-of-range for type=text");
+
+ t2.type = "number";
+
+ assert_equals(getComputedStyle(sibling2).color, "rgb(0, 128, 0)",
+ "Matching :in-range for type=number");
+ }, "Evaluation of :out-of-range changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html
new file mode 100644
index 0000000000..e9acbb3741
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:in-range, :out-of-range)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id="link1">
+<link rel="author" title="Chris Rebert" href="http://chrisrebert.com" id="link2">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#selector-in-range" id="link3">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#selector-out-of-range" id="link4">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<input type=number value=0 min=0 max=10 id=number1>
+<input type=number value=0 min=0 max=10 id=number2 disabled>
+<input type=number value=0 min=1 max=10 id=number3>
+<input type=number value=11 min=0 max=10 id=number4>
+<input type=number value=0 min=0 max=10 id=number5 readonly>
+
+<input type="date" min="2005-10-10" max="2020-10-10" value="2010-10-10" id="datein">
+<input type="date" min="2010-10-10" max="2020-10-10" value="2005-10-10" id="dateunder">
+<input type="date" min="2010-10-10" max="2020-10-10" value="2030-10-10" id="dateover">
+
+<input type="time" min="01:00:00" max="05:00:00" value="02:00:00" id="timein">
+<input type="time" min="02:00:00" max="05:00:00" value="01:00:00" id="timeunder">
+<input type="time" min="02:00:00" max="05:00:00" value="07:00:00" id="timeover">
+
+<input type="week" min="2016-W05" max="2016-W10" value="2016-W07" id="weekin">
+<input type="week" min="2016-W05" max="2016-W10" value="2016-W02" id="weekunder">
+<input type="week" min="2016-W05" max="2016-W10" value="2016-W26" id="weekover">
+
+<input type="month" min="2000-04" max="2000-09" value="2000-06" id="monthin">
+<input type="month" min="2000-04" max="2000-09" value="2000-02" id="monthunder">
+<input type="month" min="2000-04" max="2000-09" value="2000-11" id="monthover">
+
+<input type="datetime-local" min="2008-03-12T23:59:59" max="2015-02-13T23:59:59" value="2012-11-28T23:59:59" id="datetimelocalin">
+<input type="datetime-local" min="2008-03-12T23:59:59" max="2015-02-13T23:59:59" value="2008-03-01T23:59:59" id="datetimelocalunder">
+<input type="datetime-local" min="2008-03-12T23:59:59" max="2015-02-13T23:59:59" value="2016-01-01T23:59:59" id="datetimelocalover">
+
+<!-- None of the following have range limitations since they have neither min nor max attributes -->
+<input type="number" value="0" id="numbernolimit">
+<input type="date" value="2010-10-10" id="datenolimit">
+<input type="time" value="02:00:00" id="timenolimit">
+<input type="week" value="2016-W07" id="weeknolimit">
+<input type="month" value="2000-06" id="monthnolimit">
+<input type="datetime-local" value="2012-11-28T23:59:59" id="datetimelocalnolimit">
+
+<!-- range inputs have default minimum of 0 and default maximum of 100 -->
+<input type="range" value="50" id="range0">
+
+<!-- range input's value gets immediately clamped to the nearest boundary point -->
+<input type="range" min="2" max="7" value="5" id="range1">
+<input type="range" min="2" max="7" value="1" id="range2">
+<input type="range" min="2" max="7" value="9" id="range3">
+
+<!-- None of the following input types can have range limitations -->
+<input min="1" value="0" type="text">
+<input min="1" value="0" type="search">
+<input min="1" value="0" type="url">
+<input min="1" value="0" type="tel">
+<input min="1" value="0" type="email">
+<input min="1" value="0" type="password">
+<input min="1" value="#000000" type="color">
+<input min="1" value="0" type="checkbox">
+<input min="1" value="0" type="radio">
+<input min="1" value="0" type="file">
+<input min="1" value="0" type="submit">
+<input min="1" value="0" type="image">
+<!-- The following types are also barred from constraint validation -->
+<input min="1" value="0" type="hidden">
+<input min="1" value="0" type="button">
+<input min="1" value="0" type="reset">
+
+<script>
+ testSelectorIdsMatch(":in-range", ["number1", "datein", "timein", "weekin", "monthin", "datetimelocalin", "range0", "range1", "range2", "range3"], "':in-range' matches all elements that are candidates for constraint validation, have range limitations, and that are neither suffering from an underflow nor suffering from an overflow");
+
+ testSelectorIdsMatch(":out-of-range", ["number3", "number4", "dateunder", "dateover", "timeunder", "timeover", "weekunder", "weekover", "monthunder", "monthover", "datetimelocalunder", "datetimelocalover"], "':out-of-range' matches all elements that are candidates for constraint validation, have range limitations, and that are either suffering from an underflow or suffering from an overflow");
+
+ document.getElementById("number1").value = -10;
+ testSelectorIdsMatch(":in-range", ["datein", "timein", "weekin", "monthin", "datetimelocalin", "range0", "range1", "range2", "range3"], "':in-range' update number1's value < min");
+ testSelectorIdsMatch(":out-of-range", ["number1", "number3", "number4", "dateunder", "dateover", "timeunder", "timeover", "weekunder", "weekover", "monthunder", "monthover", "datetimelocalunder", "datetimelocalover"], "':out-of-range' update number1's value < min");
+
+ document.getElementById("number3").min = 0;
+ testSelectorIdsMatch(":in-range", ["number3", "datein", "timein", "weekin", "monthin", "datetimelocalin", "range0", "range1", "range2", "range3"], "':in-range' update number3's min < value");
+ testSelectorIdsMatch(":out-of-range", ["number1", "number4", "dateunder", "dateover", "timeunder", "timeover", "weekunder", "weekover", "monthunder", "monthover", "datetimelocalunder", "datetimelocalover"], "':out-of-range' update number3's min < value");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html
new file mode 100644
index 0000000000..92345602a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<input>
+<textarea></textarea>
+<script>
+promise_test(async () => {
+ for (let tag of ["input", "textarea"]) {
+ let element = document.querySelector(tag);
+ await test_driver.send_keys(element, 'something');
+
+ assert_true(element.validity.valid, tag + ' should be valid');
+
+ element.maxLength = 0;
+ assert_true(element.matches(":invalid"), tag + ' should match :invalid');
+ assert_false(element.validity.valid, tag + ' should be invalid');
+
+ let clone = element.cloneNode(true);
+ assert_true(clone.matches(":invalid"), tag + ' clone should match :invalid');
+ assert_false(clone.validity.valid, tag + 'clone should be invalid');
+ }
+}, 'Cloned invalid inputs / textareas with interactive changes get their validity state copied correctly');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html
new file mode 100644
index 0000000000..e9733eca70
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:link)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<link rel=stylesheet href="non-existent.css" id=link3>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<a id=link4></a>
+<area id=link5></area>
+<link id=link6></link>
+<a href="http://www.w3.org" id=link7></a>
+<area href="http://www.w3.org" id=link8></area>
+<link href="http://www.w3.org" id=link9></link>
+<a href="http://[" id=link10></a>
+
+<script>
+ testSelectorIdsMatch(":link", ["link7", "link8", "link10"], "Only <a>s and <area>s that have a href attribute match ':link'");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html
new file mode 100644
index 0000000000..206ae80c75
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-class :placeholder-shown input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ }
+ :placeholder-shown + span {
+ color: green;
+ }
+</style>
+<input id="input" type="submit" placeholder="placeholder"></input>
+<span id="sibling">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :placeholder-shown for type=submit");
+
+ input.type = "text";
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :placeholder-shown for type=text");
+ }, "Evaluation of :placeholder-shown changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html
new file mode 100644
index 0000000000..90ef1d25d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-classes (:read-write, :read-only) input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ background-color: pink;
+ }
+ :required + span {
+ color: green;
+ }
+ :not(:optional) + span {
+ background-color: lime;
+ }
+</style>
+<input id="hiddenInput" type="hidden" required>
+<span id="sibling">This text should be green on lime background.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :required for type=hidden");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(255, 192, 203)",
+ "Matching :optional for type=hidden");
+
+ hiddenInput.type = "text";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :required for type=text");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(0, 255, 0)",
+ "Matching :not(:optional) for type=text");
+ }, "Evaluation of :required and :optional changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html
new file mode 100644
index 0000000000..fb8a5b9ad7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:read-write, :read-only)</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+
+<div id=set0>
+<!-- The readonly attribute does not apply to the following input types -->
+<input id=checkbox1 type=checkbox>
+<input id=hidden1 type=hidden value=abc>
+<input id=range1 type=range>
+<input id=color1 type=color>
+<input id=radio1 type=radio>
+<input id=file1 type=file>
+<input id=submit1 type=submit>
+<input id=image1 type=image>
+<input id=button1 type=button value="Button">
+<input id=reset1 type=reset>
+</div>
+
+<div id=set1>
+<input id=input1>
+<input id=input2 readonly>
+<input id=input3 disabled>
+<input id=input4 type=checkbox>
+<input id=input5 type=checkbox readonly>
+</div>
+
+<div id=set2>
+<textarea id=textarea1>textarea1</textarea>
+<textarea readonly id=textarea2>textarea2</textarea>
+</div>
+
+<div id=set3>
+<textarea id=textarea3>textarea3</textarea>
+<textarea disabled id=textarea4>textarea4</textarea>
+</div>
+
+<div id=set4>
+<p id=p1>paragraph1.</p>
+<p id=p2 contenteditable>paragraph2.</p>
+</div>
+
+<div id=set5>
+ <div id=cd1 contenteditable>
+ <p id=p3></p>
+ <input id=ci1 readonly>
+ <input id=ci2 disabled>
+ <input id=ci3>
+ <input id=ci4>
+ <textarea id=ct1 readonly></textarea>
+ <textarea id=ct2 disabled></textarea>
+ <textarea id=ct3></textarea>
+ <textarea id=ct4></textarea>
+ </div>
+</div>
+
+<script>
+ testSelectorIdsMatch("#set0 :read-write", [], "The :read-write pseudo-class must not match input elements to which the readonly attribute does not apply");
+
+ testSelectorIdsMatch("#set0 :read-only", ["checkbox1", "hidden1", "range1", "color1", "radio1", "file1", "submit1", "image1", "button1", "reset1"], "The :read-only pseudo-class must match input elements to which the readonly attribute does not apply");
+
+ testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must match input elements to which the readonly attribute applies, and that are mutable");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must not match input elements to which the readonly attribute applies, and that are mutable");
+
+ document.getElementById("input1").setAttribute("readonly", "readonly");
+ testSelectorIdsMatch("#set1 :read-write", [], "The :read-write pseudo-class must not match input elements after the readonly attribute has been added");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input1", "input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the readonly attribute has been added");
+
+ document.getElementById("input1").removeAttribute("readonly");
+ testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must not match input elements after the readonly attribute has been removed");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the readonly attribute has been removed");
+
+ document.getElementById("input1").disabled = true;
+ testSelectorIdsMatch("#set1 :read-write", [], "The :read-write pseudo-class must not match input elements after the disabled attribute has been added");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input1", "input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the disabled attribute has been added");
+
+ document.getElementById("input1").disabled = false;
+
+ testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must match input elements after the disabled attribute has been removed");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must not match input elements after the disabled attribute has been removed");
+
+ testSelectorIdsMatch("#set2 :read-write", ["textarea1"], "The :read-write pseudo-class must match textarea elements that do not have a readonly attribute, and that are not disabled");
+
+ testSelectorIdsMatch("#set2 :read-only", ["textarea2"], "The :read-only pseudo-class must match textarea elements that have a readonly attribute, or that are disabled");
+
+ document.getElementById("textarea1").setAttribute("readonly", "readonly");
+ testSelectorIdsMatch("#set2 :read-write", [], "The :read-write pseudo-class must match textarea elements after the readonly attribute has been added");
+
+ testSelectorIdsMatch("#set2 :read-only", ["textarea1", "textarea2"], "The :read-only pseudo-class must match textarea elements after the readonly attribute has been added");
+
+ testSelectorIdsMatch("#set3 :read-write", ["textarea3"], "The :read-write pseudo-class must not match textarea elements that are disabled");
+
+ testSelectorIdsMatch("#set3 :read-only", ["textarea4"], "The :read-only pseudo-class must match textarea elements that are disabled");
+
+ testSelectorIdsMatch("#set4 :read-write", ["p2"], "The :read-write pseudo-class must match elements that are editable");
+
+ testSelectorIdsMatch("#set4 :read-only", ["p1"], "The :read-only pseudo-class must not match elements that are editable");
+
+ document.designMode = "on";
+
+ testSelectorIdsMatch("#set4 :read-write", ["p1", "p2"], "The :read-write pseudo-class must match elements that are editing hosts");
+
+ testSelectorIdsMatch("#set4 :read-only", [], "The :read-only pseudo-class must not match elements that are editing hosts");
+
+ document.designMode = "off";
+
+ testSelectorIdsMatch("#set5 :read-write", ["cd1", "p3", "ci3", "ci4", "ct3", "ct4"], "The :read-write pseudo-class must match elements that are inside editing hosts, but not match inputs and textareas inside that aren't");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html
new file mode 100644
index 0000000000..fe3d6e2f42
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-classes (:required, :optional) for hidden input</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ background-color: pink;
+ }
+ :required + span {
+ color: green;
+ }
+ :not(:optional) + span {
+ background-color: lime;
+ }
+</style>
+<input id="hiddenInput" type="hidden" required>
+<span id="sibling">This text should be green on lime background.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :required for type=hidden");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(255, 192, 203)",
+ "Matching :optional for type=hidden");
+
+ hiddenInput.type = "text";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :required for type=text");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(0, 255, 0)",
+ "Matching :not(:optional) for type=text");
+ }, "Evaluation of :required and :optional changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html
new file mode 100644
index 0000000000..f06fdfa1e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:required, :optional)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<input type=text id=text1 value="foobar" required>
+<input type=text id=text2 required>
+<input type=text id=text3>
+<select id=select1 required>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1>option1
+</select>
+<select id=select2>
+ <optgroup label="options" id=optgroup2>
+ <option value="option2" id=option2>option2
+</select>
+<textarea required id=textarea1>textarea1</textarea>
+<textarea id=textarea2>textarea2</textarea>
+
+<script>
+ testSelectorIdsMatch(":required", ["text1", "text2", "select1", "textarea1"], "':required' matches required <input>s, <select>s and <textarea>s");
+ testSelectorIdsMatch(":optional", ["text3", "select2", "textarea2"], "':optional' matches elements <input>s, <select>s and <textarea>s that are not required");
+
+ document.getElementById("text1").removeAttribute("required");
+ testSelectorIdsMatch(":required", ["text2", "select1", "textarea1"], "':required' doesn't match elements whose required attribute has been removed");
+ testSelectorIdsMatch(":optional", ["text1", "text3", "select2", "textarea2"], "':optional' matches elements whose required attribute has been removed");
+
+ document.getElementById("select2").setAttribute("required", "required");
+ testSelectorIdsMatch(":required", ["text2", "select1", "select2", "textarea1"], "':required' matches elements whose required attribute has been added");
+ testSelectorIdsMatch(":optional", ["text1", "text3", "textarea2"], "':optional' doesn't match elements whose required attribute has been added");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js
new file mode 100644
index 0000000000..7a2fb77f10
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js
@@ -0,0 +1,20 @@
+function getElementsByIds(ids) {
+ var result = [];
+ ids.forEach(function(id) {
+ result.push(document.getElementById(id));
+ });
+ return result;
+}
+
+function testSelectorIdsMatch(selector, ids, testName) {
+ test(function(){
+ var elements = document.querySelectorAll(selector);
+ assert_array_equals([...elements], getElementsByIds(ids));
+ }, testName);
+}
+
+function testSelectorElementsMatch(selector, elements, testName) {
+ test(function(){
+ assert_array_equals([...document.querySelectorAll(selector)], elements);
+ }, testName);
+}
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html
new file mode 100644
index 0000000000..6ad329438a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:valid, :invalid) on disconnected fieldset element</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/semantics-other.html#selector-valid">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<fieldset id=fieldset1>
+ <input id=input1>
+</fieldset>
+
+<fieldset id=fieldset2>
+ <select id=select1 required multiple>
+ <option>foo
+ </select>
+</fieldset>
+
+<script>
+test(() => {
+ const fieldset = document.querySelector("#fieldset1");
+ const input = document.querySelector("#input1");
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+
+ fieldset.remove();
+ input.setCustomValidity("foo");
+
+ assert_false(fieldset.matches(":valid"));
+ assert_true(fieldset.matches(":invalid"));
+
+ input.setCustomValidity("");
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+}, "<input> element becomes invalid inside disconnected <fieldset>");
+
+test(() => {
+ const fieldset = document.querySelector("#fieldset2");
+ const select = document.querySelector("#select1");
+
+ assert_false(fieldset.matches(":valid"));
+ assert_true(fieldset.matches(":invalid"));
+
+ fieldset.remove();
+ select.required = false;
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+
+ select.required = true;
+ select.firstElementChild.selected = true;
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+}, "<select> element becomes valid inside disconnected <fieldset>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html
new file mode 100644
index 0000000000..d93407707f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:valid, :invalid)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<style>
+ #styleTests form, #styleTests fieldset, #failExample { background-color:red; }
+ #styleTests > :valid, #validExample { background-color:green; }
+ #styleTests > :invalid, #invalidExample { background-color:lime; }
+</style>
+</head>
+<body>
+<div id="log"></div>
+<div id='simpleConstraints'>
+ <input type=text id=text1 value="foobar" required>
+ <input type=text id=text2 required>
+</div>
+<div id='FormSelection'>
+ <form id=form1>
+ <input type=text id=text3 value="foobar" required>
+ </form>
+ <form id=form2>
+ <input type=text id=text4 required>
+ </form>
+</div>
+<div id='FieldSetSelection'>
+ <fieldset id=fieldset1>
+ <input type=text id=text5 value="foobar" required>
+ </fieldset>
+ <fieldset id=fieldset2>
+ <input type=text id=text6 required>
+ </fieldset>
+</div>
+<div id='patternConstraints'>
+ <input type=text id=text7 value="AAA" pattern="[0-9][A-Z]{3}">
+ <input type=text id=text8 value="0AAA" pattern="[0-9][A-Z]{3}">
+</div>
+<div id='numberConstraints'>
+ <input type=number id=number1 value=0 min=1>
+ <input type=number id=number2 value=1 min=1>
+</div>
+<div id='styleTests'>
+ <form>
+ </form>
+ <form>
+ <input type=text min=8 value=4>
+ </form>
+ <form>
+ <input type=number min=8 value=4>
+ </form>
+ <fieldset>
+ </fieldset>
+ <fieldset>
+ <input type=text min=8 value=4>
+ </fieldset>
+ <fieldset>
+ <input type=number min=8 value=4>
+ </fieldset>
+ <div id='validExample'></div>
+ <div id='invalidExample'></div>
+ <div id='failExample'></div>
+</div>
+<script>
+ testSelectorIdsMatch("#simpleConstraints :valid", ["text1"], "':valid' matches elements that satisfy their constraints");
+
+ testSelectorIdsMatch("#FormSelection :valid", ["form1", "text3"], "':valid' matches form elements that are not the form owner of any elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#FieldSetSelection :valid", ["fieldset1", "text5"], "':valid' matches fieldset elements that have no descendant elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#patternConstraints :valid", [ "text8" ], "':valid' matches elements that satisfy their pattern constraints");
+
+ testSelectorIdsMatch("#numberConstraints :valid", [ "number2" ], "':valid' matches elements that satisfy their number constraints");
+
+
+ testSelectorIdsMatch("#simpleConstraints :invalid", ["text2"], "':invalid' matches elements that do not satisfy their simple text constraints");
+
+ testSelectorIdsMatch("#FormSelection :invalid", ["form2", "text4"], "':invalid' matches form elements that are the form owner of one or more elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#FieldSetSelection :invalid", ["fieldset2", "text6"], "':invalid' matches fieldset elements that have of one or more descendant elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#patternConstraints :invalid", ["text7"], "':invalid' matches elements that do not satisfy their pattern constraints");
+
+ testSelectorIdsMatch("#numberConstraints :invalid", ["number1"], "':invalid' matches elements that do not satisfy their number constraints");
+
+ document.getElementById("text7").value="0BBB";
+ testSelectorIdsMatch("#patternConstraints :valid", [ "text7", "text8" ], "':valid' matches new elements that satisfy their constraints");
+ testSelectorIdsMatch("#patternConstraints :invalid", [], "':invalid' doesn't match new elements that satisfy their constraints");
+
+ document.getElementById("text8").value="BBB";
+ testSelectorIdsMatch("#patternConstraints :valid", ["text7"], "':valid' doesn't match new elements that do not satisfy their constraints");
+ testSelectorIdsMatch("#patternConstraints :invalid", ["text8"], "':invalid' matches new elements that do not satisfy their constraints");
+
+ function getBGColor(elem) {
+ return getComputedStyle(elem).backgroundColor;
+ }
+
+ function testStyles(type) {
+ var elems = document.querySelectorAll("#styleTests " + type),
+ empty = elems[0],
+ valid = elems[1],
+ invalid = elems[2],
+ validInput = valid.querySelector("input"),
+ invalidInput = invalid.querySelector("input"),
+ expectedValidBGColor = getBGColor(document.getElementById("validExample")),
+ expectedInvalidBGColor = getBGColor(document.getElementById("invalidExample")),
+ expectedFailBGColor = getBGColor(document.getElementById("failExample"));
+
+ test(function() {
+ assert_equals(getBGColor(empty), expectedValidBGColor, "wrong background-color");
+ }, 'empty ' + type + ' correctly styled on page-load');
+
+ test(function() {
+ assert_equals(getBGColor(valid), expectedValidBGColor, "wrong background-color");
+ }, 'valid ' + type + ' correctly styled on page-load');
+ test(function() {
+ assert_equals(getBGColor(invalid), expectedInvalidBGColor, "wrong background-color");
+ }, 'invalid ' + type + ' correctly styled on page-load');
+
+ test(function() {
+ empty.appendChild(validInput.cloneNode());
+ assert_equals(getBGColor(empty), expectedValidBGColor, "wrong background-color");
+ }, 'programmatically adding valid to empty ' + type + ' results in correct style');
+ test(function() {
+ empty.appendChild(invalidInput.cloneNode());
+ assert_equals(getBGColor(empty), expectedInvalidBGColor, "wrong background-color");
+ }, 'programmatically adding invalid to empty ' + type + ' results in correct style');
+
+ validInput.type = "number";
+ invalidInput.type = "text";
+ test(function() {
+ assert_equals(getBGColor(valid), expectedInvalidBGColor, "wrong background-color");
+ }, 'programmatically-invalidated ' + type + ' correctly styled');
+ test(function() {
+ assert_equals(getBGColor(invalid), expectedValidBGColor, "wrong background-color");
+ }, 'programmatically-validated ' + type + ' correctly styled');
+ }
+ test(testStyles.bind(undefined, "form"), ":valid/:invalid styling for <form>");
+ test(testStyles.bind(undefined, "fieldset"), ":valid/:invalid styling for <fieldset>");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/META.yml b/testing/web-platform/tests/html/semantics/tabular-data/META.yml
new file mode 100644
index 0000000000..ce84e4ae4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - tkent-google
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/attributes-common-to-td-and-th-elements/cellIndex.html b/testing/web-platform/tests/html/semantics/tabular-data/attributes-common-to-td-and-th-elements/cellIndex.html
new file mode 100644
index 0000000000..b8449229d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/attributes-common-to-td-and-th-elements/cellIndex.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTMLTableCellElement.cellIndex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var th = document.createElement("th");
+ assert_true("cellIndex" in th, '"cellIndex" in th');
+ var td = document.createElement("td");
+ assert_true("cellIndex" in td, '"cellIndex" in td');
+}, "cellIndex should exist.")
+test(function() {
+ var th = document.createElement("th");
+ assert_equals(th.cellIndex, -1);
+ var td = document.createElement("td");
+ assert_equals(td.cellIndex, -1);
+}, "For cells without a parent, cellIndex should be -1.")
+test(function() {
+ var table = document.createElement("table");
+ var th = table.appendChild(document.createElement("th"));
+ assert_equals(th.cellIndex, -1);
+ var td = table.appendChild(document.createElement("td"));
+ assert_equals(td.cellIndex, -1);
+}, "For cells whose parent is not a tr, cellIndex should be -1.")
+test(function() {
+ var tr = document.createElementNS("", "tr");
+ var th = tr.appendChild(document.createElement("th"));
+ assert_equals(th.cellIndex, -1);
+ var td = tr.appendChild(document.createElement("td"));
+ assert_equals(td.cellIndex, -1);
+}, "For cells whose parent is not a HTML tr, cellIndex should be -1.")
+test(function() {
+ var tr = document.createElement("tr");
+ var th = tr.appendChild(document.createElement("th"));
+ assert_equals(th.cellIndex, 0);
+ var td = tr.appendChild(document.createElement("td"));
+ assert_equals(td.cellIndex, 1);
+}, "For cells whose parent is a tr, cellIndex should be the index.")
+test(function() {
+ var tr = document.createElement("tr");
+ var th = tr.appendChild(document.createElement("th"));
+ assert_equals(th.cellIndex, 0);
+ tr.appendChild(document.createElement("div"));
+ tr.appendChild(document.createTextNode("Hello World"));
+ var td = tr.appendChild(document.createElement("td"));
+ assert_equals(td.cellIndex, 1)
+}, "For cells whose parent is a tr with non td/th sibling, cellIndex should skip those non td/th siblings.")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/historical.html b/testing/web-platform/tests/html/semantics/tabular-data/historical.html
new file mode 100644
index 0000000000..a6be56e13d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/historical.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Historical table features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function t(property, tagNames) {
+ if (typeof tagNames === "string") {
+ tagNames = [tagNames];
+ }
+ tagNames.forEach(function(tagName) {
+ test(function() {
+ assert_false(property in document.createElement(tagName));
+ }, tagName + '.' + property + ' should not be supported');
+ });
+}
+
+// added in https://github.com/whatwg/html/commit/6db0d8d4e3456140de958c963afe9bb9ec7b6a25
+// removed in https://github.com/whatwg/html/commit/59b7e2466c2b7c5c408a4963b05b13fd808aa07a
+t('onsort', 'table');
+t('sortable', 'table');
+t('stopSorting', 'table');
+t('sorted', 'th');
+t('sort', 'th');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/html-table-section-element.js b/testing/web-platform/tests/html/semantics/tabular-data/html-table-section-element.js
new file mode 100644
index 0000000000..68b68ceed8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/html-table-section-element.js
@@ -0,0 +1,22 @@
+// https://html.spec.whatwg.org/multipage/#dom-tbody-rows
+function testRowsAttribute(localName) {
+ var elem = document.createElement(localName);
+ assert_equals(elem.rows.length, 0);
+
+ // Child <p> should *not* count as a row
+ elem.appendChild(document.createElement("p"));
+ assert_equals(elem.rows.length, 0);
+
+ // Child <tr> should count as a row
+ var childTr = document.createElement("tr");
+ elem.appendChild(childTr);
+ assert_equals(elem.rows.length, 1);
+
+ // Nested table with child <tr> should *not* count as a row
+ var nested = document.createElement(localName);
+ nested.appendChild(document.createElement("tr"));
+ var nestedTable = document.createElement("table");
+ nestedTable.appendChild(nested);
+ childTr.appendChild(nestedTable);
+ assert_equals(elem.rows.length, 1);
+}
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/col-span-limits.html b/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/col-span-limits.html
new file mode 100644
index 0000000000..a4a425b9c1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/col-span-limits.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>Limits on col/colgroup.span</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ div.square {
+ height:20px;
+ width:20px;
+ border:1px solid lime;
+ }
+ main table {
+ border-collapse:collapse;
+ border:1px solid blue;
+ }
+ main table col {
+ border-left:2px solid black;
+ }
+</style>
+<div id=log></div>
+<main>
+<table id=table1>
+ <col span=1000>
+ <tr>
+ <td colspan=999><div class="square"></div></td>
+ <td><div class="square"></div></td>
+ </tr>
+ <tr>
+ <td colspan=1000><div class="square"></div></td>
+ </tr>
+</table>
+<br>
+These two must look the same, each having 2 cells in one row:
+<table id=table2>
+ <col span=1000>
+ <tr>
+ <td colspan=1000><div class="square"></div></td>
+ <td><div class="square"></div></td>
+ </tr>
+</table>
+<br>
+<table id=table3>
+ <col span=1001>
+ <tr>
+ <td colspan=1000><div class="square"></div></td>
+ <td><div class="square"></div></td>
+ </tr>
+</table>
+</main>
+
+<script>
+test(() => {
+ assert_equals(table1.offsetWidth, 53);
+}, "col span of 1000 must work");
+
+test(() => {
+ assert_equals(table2.offsetWidth, 51, "table2 width");
+ assert_equals(table3.offsetWidth, 51, "table3 width");
+}, "col span of 1001 must be treated as 1000");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits.html b/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits.html
new file mode 100644
index 0000000000..cdfa61bbcd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Limits on colSpan/rowSpan</title>
+<meta name="timeout" content="long">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+
+<table border=1>
+ <tr><td colspan=500>a<td colspan=500 id=a1>a
+ <!-- This cell must span the previous two -->
+ <tr><td colspan=1000 id=a2>a
+</table>
+
+<table border=1>
+ <tr><td colspan=1000 id=b1>a<td>a
+ <!-- This cell must span only the first cell in the previous row -->
+ <tr><td colspan=1001 id=b2>a
+</table>
+
+<table border=1 style="float:left">
+ <!-- The first column must go all the way down to the bottom -->
+ <tr><td rowspan=65534 id=c1>a<td>
+ <!-- We'll add another 65533 rows later -->
+</table>
+
+<table border=1>
+ <!-- The first column must go one cell below the bottom -->
+ <tr><td rowspan=65535 id=d1>a<td>
+ <!-- We'll add another 65534 rows later -->
+</table>
+
+<script>
+var $ = document.querySelector.bind(document);
+
+test(() => {
+ assert_equals($("#a2").getBoundingClientRect().right,
+ $("#a1").getBoundingClientRect().right);
+}, "colspan of 1000 must work");
+
+test(() => {
+ assert_equals($("#b2").getBoundingClientRect().right,
+ $("#b1").getBoundingClientRect().right);
+}, "colspan of 1001 must be treated as 1000");
+
+test(() => {
+ var s = "";
+ for (var i = 0; i < 65532; i++) {
+ s += "<tr><td>";
+ }
+ s += "<tr><td id=c2>";
+ document.querySelectorAll("table")[2].firstElementChild.innerHTML += s;
+ assert_equals($("#c1").getBoundingClientRect().bottom,
+ $("#c2").getBoundingClientRect().bottom);
+}, "rowspan of 65534 must work");
+
+test(() => {
+ var s = "";
+ for (var i = 0; i < 65532; i++) {
+ s += "<tr><td>";
+ }
+ s += "<tr><td id=d2><tr><td>a<td>";
+ document.querySelectorAll("table")[3].firstElementChild.innerHTML += s;
+ assert_equals($("#d1").getBoundingClientRect().bottom,
+ $("#d2").getBoundingClientRect().bottom);
+}, "rowspan of 65535 must be treated as 65534");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-caption-element/caption_001.html b/testing/web-platform/tests/html/semantics/tabular-data/the-caption-element/caption_001.html
new file mode 100644
index 0000000000..ecb1bef854
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-caption-element/caption_001.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>HTML5 Table API Tests</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-caption-element" />
+ </head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <body>
+ <div id="log"></div>
+ <table id="table1" style="display:none">
+ <tr><td></td></tr>
+ <caption>first caption</caption>
+ <caption>second caption</caption>
+ </table>
+ <table id="table2" style="display:none">
+ <tr><td></td></tr>
+ </table>
+ <table id="table3" style="display:none">
+ <tr><td></td></tr>
+ </table>
+ <table id="table4" style="display:none">
+ <tr><td></td></tr>
+ <caption>first caption</caption>
+ </table>
+ <script>
+ test(function () {
+ assert_equals(document.getElementById('table1').caption.innerHTML, "first caption");
+ }, "first caption element child of the first table element");
+
+ test(function () {
+ var caption = document.createElement("caption");
+ caption.innerHTML = "new caption";
+ var table = document.getElementById('table1');
+ table.caption = caption;
+
+ assert_equals(caption.parentNode, table);
+ assert_equals(table.firstChild, caption);
+ assert_equals(table.caption.innerHTML, "new caption");
+
+ captions = table.getElementsByTagName('caption');
+ assert_equals(captions.length, 2);
+ assert_equals(captions[0].innerHTML, "new caption");
+ assert_equals(captions[1].innerHTML, "second caption");
+ }, "setting caption on a table");
+
+ test(function () {
+ assert_equals(document.getElementById('table2').caption, null);
+ }, "caption IDL attribute is null");
+
+ test(function () {
+ var table = document.getElementById('table3');
+ var caption = document.createElement("caption")
+ table.rows[0].appendChild(caption);
+ assert_equals(table.caption, null);
+ }, "caption of the third table element should be null");
+
+ test(function () {
+ assert_not_equals(document.getElementById('table4').caption, null);
+
+ var parent = document.getElementById('table4').caption.parentNode;
+ parent.removeChild(document.getElementById('table4').caption);
+
+ assert_equals(document.getElementById('table4').caption, null);
+ }, "dynamically removing caption on a table");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/caption-methods.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/caption-methods.html
new file mode 100644
index 0000000000..a349ed2b77
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/caption-methods.html
@@ -0,0 +1,276 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Creating and deleting captions</title>
+ <link rel="author" title="Erika Navara" href="mailto:edoyle@microsoft.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-table-element" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-table-createcaption" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-table-deletecaption" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <table id="table0" style="display:none">
+ </table>
+ <table id="table1" style="display:none">
+ <caption id="caption1">caption</caption>
+ <tr>
+ <td>cell</td>
+ <td>cell</td>
+ </tr>
+ </table>
+ <table id="table2" style="display:none">
+ <foo:caption>caption</foo:caption>
+ <tr>
+ <td>cell</td>
+ <td>cell</td>
+ </tr>
+ </table>
+ <table id="table3" style="display:none">
+ <caption id="caption3">caption 3</caption>
+ <tr>
+ <td>cell</td>
+ <td>cell</td>
+ </tr>
+ </table>
+ <table id="table4" style="display:none">
+ </table>
+ <table id="table5" style="display:none">
+ </table>
+ <table id="table6" style="display:none">
+ <caption id="caption6">caption 6</caption>
+ <tr>
+ <td>cell</td>
+ <td>cell</td>
+ </tr>
+ </table>
+ <table id="table7" style="display:none">
+ <caption id="caption7">caption 7</caption>
+ <tbody id="tbody7">
+ <tr>
+ <td>cell</td>
+ <td>cell</td>
+ </tr>
+ </tbody>
+ </table>
+ <table id="table10" style="display:none">
+ <tbody>
+ <tr>
+ <td>cell</td>
+ <td>cell</td>
+ </tr>
+ </tbody>
+ <caption>caption 10</caption>
+ </table>
+ <table id="table11" style="display:none">
+ <caption id="caption11">caption 11</caption>
+ </table>
+ <table id="table12" style="display:none">
+ <caption>caption 1</caption>
+ <caption>caption 2</caption>
+ </table>
+ <table id="table13" style="display:none">
+ </table>
+ <table id="table14" style="display:none">
+ <tbody>
+ <tr>
+ <td>cell</td>
+ <td>cell</td>
+ </tr>
+ </tbody>
+ <caption id="caption14">caption 14</caption>
+ </table>
+ <script>
+ test(function () {
+ var table0 = document.getElementById('table0');
+ var caption = document.createElementNS("foo", "caption");
+ table0.appendChild(caption);
+ var table0FirstNode = table0.firstChild;
+ var testCaption = table0.createCaption();
+ assert_not_equals(testCaption, table0FirstNode);
+ assert_equals(testCaption, table0.firstChild);
+ }, "createCaption method creates new caption if existing caption is not in html namespace")
+
+ test(function () {
+ var table1 = document.getElementById('table1');
+ var testCaption = table1.createCaption();
+ var table1FirstCaption = table1.caption;
+ assert_equals(testCaption, table1FirstCaption);
+ }, "createCaption method returns the first caption element child of the table")
+
+ test(function () {
+ var table2 = document.getElementById('table2');
+ var test2Caption = table2.createCaption();
+ var table2FirstNode = table2.firstChild;
+ assert_true(test2Caption instanceof HTMLTableCaptionElement);
+ assert_equals(table2FirstNode, test2Caption);
+ }, "createCaption method creates a new caption and inserts it as the first node of the table element")
+
+ test(function () {
+ var table = document.createElement('table');
+ assert_equals(table.createCaption(), table.createCaption());
+ }, "createCaption will not create new caption if one exists")
+
+ test(function () {
+ var table = document.createElementNS("http://www.w3.org/1999/xhtml", "foo:table")
+ var caption = table.createCaption();
+ assert_equals(caption.prefix, null);
+ }, "createCaption will not copy table's prefix")
+
+ test(function () {
+ var table3 = document.getElementById('table3');
+ assert_equals(table3.caption.textContent, "caption 3");
+ table3.deleteCaption();
+ assert_equals(table3.caption, null);
+ }, "deleteCaption method removes the first caption element child of the table element")
+
+ test(function () {
+ var table4 = document.getElementById('table4');
+ var caption = document.createElementNS("foo", "caption");
+ table4.appendChild(caption);
+ table4.deleteCaption();
+ assert_equals(caption.parentNode, table4);
+ }, "deleteCaption method not remove caption that is not in html namespace")
+
+ test(function() {
+ var table5 = document.getElementById('table5');
+ var caption = document.createElement('caption');
+ caption.appendChild(table5)
+
+ // Node cannot be inserted at the specified point in the hierarchy
+ assert_throws_dom("HierarchyRequestError", function() {
+ table5.caption = caption;
+ });
+
+ assert_not_equals(table5.caption, caption);
+ }, "Setting caption rethrows exception");
+
+ test(function() {
+ var table6 = document.getElementById("table6");
+ var caption = document.getElementById("caption6");
+ assert_equals(table6.caption, caption);
+
+ var newCaption = document.createElement("caption");
+ table6.caption = newCaption;
+ assert_equals(newCaption.parentNode, table6);
+ assert_equals(table6.firstChild, newCaption);
+ assert_equals(table6.caption, newCaption);
+ }, "Assigning a caption to table.caption")
+
+ test(function() {
+ var table7 = document.getElementById("table7");
+ var caption = document.getElementById("caption7");
+ assert_equals(table7.caption, caption);
+
+ table7.caption = null;
+ assert_equals(caption.parentNode, null);
+ assert_equals(table7.firstElementChild, document.getElementById("tbody7"));
+ assert_equals(table7.caption, null);
+ }, "Assigning null to table.caption")
+
+ test(function() {
+ var table8 = document.createElement("table");
+ var caption = document.createElement("captÄ°on");
+ assert_throws_js(TypeError, function() {
+ table8.caption = caption;
+ });
+ }, "Assigning a non-caption to table.caption")
+
+ test(function() {
+ var table9 = document.createElement("table");
+ var caption = document.createElementNS("http://www.example.com", "caption");
+ assert_throws_js(TypeError, function() {
+ table9.caption = caption;
+ });
+ }, "Assigning a foreign caption to table.caption")
+
+ test(function() {
+ var table = document.createElement("table");
+ var caption = document.createElement("caption");
+ caption.innerHTML = "new caption";
+ table.caption = caption;
+
+ assert_equals(caption.parentNode, table);
+ assert_equals(table.firstChild, caption);
+ assert_equals(table.caption.innerHTML, "new caption");
+ }, "Set table.caption when the table doesn't already have a caption")
+
+ test(function() {
+ var table10 = document.getElementById("table10");
+ var caption = document.createElement("caption");
+ caption.innerHTML = "new caption";
+ table10.caption = caption;
+
+ assert_equals(caption.parentNode, table10);
+ assert_equals(table10.firstChild, caption);
+ assert_equals(table10.caption.innerHTML, "new caption");
+
+ var captions = table10.getElementsByTagName('caption');
+ assert_equals(captions.length, 1);
+ }, "Set table.caption when the table has a caption child but with other siblings before it")
+
+ test(function() {
+ var table11 = document.getElementById("table11");
+ var caption = document.createElement("caption");
+ caption.innerHTML = "new caption";
+ table11.caption = caption;
+
+ assert_equals(caption.parentNode, table11);
+ assert_equals(table11.firstChild, caption);
+ assert_equals(table11.caption.innerHTML, "new caption");
+
+ var captions = table11.getElementsByTagName('caption');
+ assert_equals(captions.length, 1);
+ }, "Set table.caption when the table has a caption descendant")
+
+ test(function() {
+ var table12 = document.getElementById("table12");
+ var caption = document.createElement("caption");
+ caption.innerHTML = "new caption";
+ table12.caption = caption;
+
+ assert_equals(caption.parentNode, table12);
+ assert_equals(table12.firstChild, caption);
+ assert_equals(table12.caption.innerHTML, "new caption");
+
+ var captions = table12.getElementsByTagName('caption');
+ assert_equals(captions.length, 2);
+ assert_equals(captions[0].innerHTML, "new caption");
+ assert_equals(captions[1].innerHTML, "caption 2");
+ }, "Set table.caption when the table has two caption children")
+
+ promise_test(async t => {
+ var table13 = document.getElementById("table13");
+ var iframe = document.createElement("iframe");
+ iframe.srcdoc = '<table><caption id="caption13">caption 13</caption></table>';
+ document.body.appendChild(iframe);
+
+ var iframeWatcher = new EventWatcher(t, iframe, "load");
+ await iframeWatcher.wait_for("load");
+ var caption = iframe.contentWindow.document.getElementById("caption13");
+ table13.caption = caption;
+
+ assert_equals(caption.parentNode, table13);
+ assert_equals(table13.firstChild, caption);
+ assert_equals(table13.caption.innerHTML, "caption 13");
+
+ var captions = table13.getElementsByTagName('caption');
+ assert_equals(captions.length, 1);
+ }, "Assigning a caption has a different owner document to table.caption")
+
+ test(function() {
+ var table14 = document.getElementById("table14");
+ var caption = document.getElementById("caption14");
+ table14.caption = caption;
+
+ assert_equals(caption.parentNode, table14);
+ assert_equals(table14.firstChild, caption);
+
+ var captions = table14.getElementsByTagName('caption');
+ assert_equals(captions.length, 1);
+ }, "Assigning the caption already in the table to table.caption")
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/createTBody.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/createTBody.html
new file mode 100644
index 0000000000..6100aedfdf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/createTBody.html
@@ -0,0 +1,173 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLTableElement.createTBody</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+function assert_tbody(tbody) {
+ assert_equals(tbody.localName, "tbody");
+ assert_equals(tbody.namespaceURI, htmlNS);
+ assert_equals(tbody.prefix, null);
+}
+var htmlNS = "http://www.w3.org/1999/xhtml";
+test(function() {
+ var table = document.createElement("table");
+ var tbody = table.createTBody();
+ assert_equals(table.firstChild, tbody);
+ assert_tbody(tbody);
+}, "No child nodes");
+
+test(function() {
+ var table = document.createElement("table");
+ var before = table.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before, tbody]);
+ assert_tbody(tbody);
+}, "One tbody child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before1 = table.appendChild(document.createElement("tbody"));
+ var before2 = table.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before1, before2]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before1, before2, tbody]);
+ assert_tbody(tbody);
+}, "Two tbody child nodes");
+
+test(function() {
+ var table = document.createElement("table");
+ var before1 = table.appendChild(document.createElement("thead"));
+ var before2 = table.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before1, before2]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before1, before2, tbody]);
+ assert_tbody(tbody);
+}, "A thead and a tbody child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before1 = table.appendChild(document.createElement("tfoot"));
+ var before2 = table.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before1, before2]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before1, before2, tbody]);
+ assert_tbody(tbody);
+}, "A tfoot and a tbody child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before = table.appendChild(document.createElement("tbody"));
+ var after = table.appendChild(document.createElement("thead"));
+ assert_array_equals(table.childNodes, [before, after]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before, tbody, after]);
+ assert_tbody(tbody);
+}, "A tbody and a thead child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before = table.appendChild(document.createElement("tbody"));
+ var after = table.appendChild(document.createElement("tfoot"));
+ assert_array_equals(table.childNodes, [before, after]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before, tbody, after]);
+ assert_tbody(tbody);
+}, "A tbody and a tfoot child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before1 = table.appendChild(document.createElement("tbody"));
+ var before2 = table.appendChild(document.createElement("tbody"));
+ var after = table.appendChild(document.createElement("div"));
+ assert_array_equals(table.childNodes, [before1, before2, after]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before1, before2, tbody, after]);
+ assert_tbody(tbody);
+}, "Two tbody child nodes and a div");
+
+test(function() {
+ var table = document.createElement("table");
+ var before = table.appendChild(document.createElement("tbody"));
+ var after = table.appendChild(document.createElementNS("x", "tbody"));
+ assert_array_equals(table.childNodes, [before, after]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before, tbody, after]);
+ assert_tbody(tbody);
+}, "One HTML and one namespaced tbody child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before1 = table.appendChild(document.createElement("tbody"));
+ var before2 = before1.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before1]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before1, tbody]);
+ assert_tbody(tbody);
+}, "Two nested tbody child nodes");
+
+test(function() {
+ var table = document.createElement("table");
+ var before1 = table.appendChild(document.createElement("thead"));
+ var before2 = before1.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before1]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before1, tbody]);
+ assert_tbody(tbody);
+}, "A tbody node inside a thead child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before1 = table.appendChild(document.createElement("tfoot"));
+ var before2 = before1.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before1]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before1, tbody]);
+ assert_tbody(tbody);
+}, "A tbody node inside a tfoot child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before = table.appendChild(document.createElement("tbody"));
+ var after1 = table.appendChild(document.createElement("thead"));
+ var after2 = after1.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before, after1]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before, tbody, after1]);
+ assert_tbody(tbody);
+}, "A tbody node inside a thead child node after a tbody child node");
+
+test(function() {
+ var table = document.createElement("table");
+ var before = table.appendChild(document.createElement("tbody"));
+ var after1 = table.appendChild(document.createElement("tfoot"));
+ var after2 = after1.appendChild(document.createElement("tbody"));
+ assert_array_equals(table.childNodes, [before, after1]);
+
+ var tbody = table.createTBody();
+ assert_array_equals(table.childNodes, [before, tbody, after1]);
+ assert_tbody(tbody);
+}, "A tbody node inside a tfoot child node after a tbody child node");
+
+test(function() {
+ var table = document.createElementNS(htmlNS, "foo:table");
+ var tbody = table.createTBody();
+
+ assert_equals(tbody.prefix, null);
+}, "A prefixed table creates tbody without prefix");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/delete-caption.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/delete-caption.html
new file mode 100644
index 0000000000..6183fa98b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/delete-caption.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>deleteCaption()</title>
+ <link rel="author" title="Ben Boyle" href="mailto:benjamins.boyle@gmail.com">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-table-deletecaption" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <table id="one-caption">
+ <caption>Fixture table caption</caption>
+ </table>
+
+ <table id="two-captions">
+ <caption>Fixture table caption</caption>
+ <caption>A second caption element</caption>
+ </table>
+
+ <table id="zero-captions"></table>
+
+ <table id="descendent-caption">
+ <tr>
+ <td>
+ <table>
+ <caption>Nested caption</caption>
+ </table>
+ </td>
+ </tr>
+ </table>
+
+ <script>
+ // The deleteCaption() method must remove the first caption element child of the table element, if any.
+ // https://html.spec.whatwgorg/multipage/tables.html#dom-table-deletecaption
+ test(function() {
+ var table = document.getElementById('one-caption');
+
+ table.deleteCaption();
+ assert_equals(table.getElementsByTagName('caption').length, 0, 'caption was removed');
+
+ }, 'deleteCaption() delete only caption on table');
+
+ test(function() {
+ var table = document.getElementById('one-caption');
+ var result;
+
+ result = table.deleteCaption();
+ // does .deleteCaption() have a return value?
+ assert_equals(result, undefined, '.deleteCaption() returns undefined');
+ }, 'deleteCaption() returns undefined');
+
+ test(function() {
+ var table = document.getElementById('two-captions');
+
+ table.deleteCaption();
+ assert_equals(table.getElementsByTagName('caption').length, 1, '1 caption (of 2) was removed');
+ assert_equals(table.getElementsByTagName('caption')[0].textContent, 'A second caption element', 'The first caption was removed');
+
+ // removing the only caption
+ table.deleteCaption();
+ assert_equals(table.getElementsByTagName('caption').length, 0, 'last caption was removed');
+ }, 'deleteCaption()');
+
+ test(function() {
+ var table = document.getElementById('zero-captions');
+ // removing a caption when none exists
+ table.deleteCaption();
+
+ assert_equals(table.getElementsByTagName('caption').length, 0, 'no exceptions using .deleteCaption() on a table without any captions');
+
+ }, 'deleteCaption() does not throw any exceptions when called on a table without a caption');
+
+ test(function() {
+ var table = document.getElementById( 'descendent-caption' );
+ table.deleteCaption();
+
+ assert_equals(table.getElementsByTagName('caption').length, 1, 'descendent caption was not deleted');
+ }, 'deleteCaption() does not delete captions in descendent tables');
+
+ test(function() {
+ var table = document.getElementById('zero-captions');
+ var caption;
+
+ caption = document.createElementNS('http://www.w3.org/2000/svg', 'caption');
+ table.insertBefore(caption, table.firstChild);
+ assert_equals(table.getElementsByTagName('caption').length, 1, 'SVG:caption is created');
+
+ table.deleteCaption();
+ assert_equals(table.getElementsByTagName('caption').length, 1, 'SVG:caption is not deleted');
+
+ }, 'deleteCaption() handles captions from different namespaces');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-01.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-01.html
new file mode 100644
index 0000000000..8ed7b5fad6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-01.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>insertRow(): INDEX_SIZE_ERR</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-table-insertrow">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<table>
+<tr>
+<td>
+</table>
+</div>
+<script>
+test(function() {
+ var table = document.getElementById("test").getElementsByTagName("table")[0];
+ assert_throws_dom("INDEX_SIZE_ERR", function() {
+ table.insertRow(-2);
+ })
+ assert_throws_dom("INDEX_SIZE_ERR", function() {
+ table.insertRow(2);
+ })
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-02.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-02.html
new file mode 100644
index 0000000000..410425fb1e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-02.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>insertRow(): Empty table</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-table-insertrow">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<table></table>
+</div>
+<script>
+var HTML = "http://www.w3.org/1999/xhtml";
+test(function() {
+ var table = document.getElementById("test").getElementsByTagName("table")[0];
+ test(function() {
+ assert_equals(table.childNodes.length, 0);
+ assert_equals(table.rows.length, 0);
+ }, "table should start out empty")
+
+ var tr;
+ test(function() {
+ tr = table.insertRow(0);
+ assert_equals(tr.localName, "tr");
+ assert_equals(tr.namespaceURI, HTML);
+ }, "insertRow should insert a tr element")
+
+ var tbody = tr.parentNode;
+ test(function() {
+ assert_equals(tbody.localName, "tbody");
+ assert_equals(tbody.namespaceURI, HTML);
+ assert_equals(tbody.parentNode, table);
+ }, "insertRow should insert a tbody element")
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-03.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-03.html
new file mode 100644
index 0000000000..19c3ceb3c6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/insertRow-method-03.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>insertRow(): non-empty table</title>
+<link rel="author" title="g-k" href="mailto:greg.guthe@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-table-insertrow">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+ <table>
+ <tbody><tr id="first"></tr><tr id="second"></tr></tbody>
+ </table>
+</div>
+<script>
+var HTML = "http://www.w3.org/1999/xhtml";
+test(function() {
+ var table = document.getElementById("test").getElementsByTagName("table")[0];
+ test(function() {
+ assert_equals(table.childNodes.length, 3);
+ assert_equals(table.rows.length, 2);
+ }, "table should start out with two rows")
+
+ var tr;
+ test(function() {
+ tr = table.insertRow(1);
+ assert_equals(tr.localName, "tr");
+ assert_equals(tr.namespaceURI, HTML);
+ assert_equals(table.getElementsByTagName("tr")[0].id, "first");
+ assert_equals(table.getElementsByTagName("tr")[1].id, "");
+ assert_equals(table.getElementsByTagName("tr")[2].id, "second");
+ }, "insertRow should insert a tr element before the second row")
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/remove-row.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/remove-row.html
new file mode 100644
index 0000000000..43a128c57e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/remove-row.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Delete Row tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<table id="element">
+ <thead>
+ <th>First column</th>
+ <th>Second column</th>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1.1</td>
+ <td>1.2</td>
+ </tr>
+ <tr>
+ <td>2.1</td>
+ <td>2.2</td>
+ </tr>
+ </tbody>
+</table>
+
+<script>
+var el = document.getElementById('element');
+
+test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ el.deleteRow(-2)
+ })
+}, 'deleteRow function invalid argument');
+test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ el.deleteRow(el.rows.length)
+ })
+}, 'deleteRow function invalid argument bis');
+
+test(function() {
+ var old_length = el.rows.length;
+ el.insertRow(-1);
+ el.deleteRow(-1);
+ assert_equals(old_length, el.rows.length);
+}, "check normal deleteRow");
+test(function() {
+ assert_equals(el.rows.length, 3);
+ do {
+ var old_length = el.rows.length;
+ el.deleteRow(-1);
+ assert_equals(el.rows.length, old_length - 1);
+ } while (el.rows.length);
+}, "check normal deleteRow bis");
+
+test(function() {
+ assert_equals(el.rows.length, 0);
+ el.deleteRow(-1);
+}, 'deleteRow(-1) with no rows');
+
+test(function() {
+ assert_equals(el.rows.length, 0);
+ assert_throws_dom("IndexSizeError", function() {
+ el.deleteRow(0);
+ });
+}, 'deleteRow(0) with no rows');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tBodies.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tBodies.html
new file mode 100644
index 0000000000..128dbc9f7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tBodies.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>HTMLTableElement.tBodies</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var text =
+ '<html xmlns="http://www.w3.org/1999/xhtml">' +
+ ' <head>' +
+ ' <title>Virtual Library</title>' +
+ ' </head>' +
+ ' <body>' +
+ ' <table id="mytable" border="1">' +
+ ' <tbody>' +
+ ' <tr><td>Cell 1</td><td>Cell 2</td></tr>' +
+ ' <tr><td>Cell 3</td><td>Cell 4</td></tr>' +
+ ' </tbody>' +
+ ' </table>' +
+ ' </body>' +
+ '</html>';
+
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(text, "text/xml");
+
+ // import <table>
+ var table = doc.documentElement.getElementsByTagName('table')[0];
+ var mytable = document.body.appendChild(document.importNode(table, true));
+
+ assert_equals(mytable.tBodies.length, 1);
+ var tbody = document.createElement('tbody');
+ mytable.appendChild(tbody);
+ var tr = tbody.insertRow(-1);
+ tr.insertCell(-1).appendChild(document.createTextNode('Cell 5'));
+ tr.insertCell(-1).appendChild(document.createTextNode('Cell 6'));
+ assert_equals(mytable.tBodies.length, 2);
+ assert_equals(mytable.rows.length, 3);
+ assert_equals(tr.rowIndex, 2);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tFoot.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tFoot.html
new file mode 100644
index 0000000000..40220bc1e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tFoot.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>tFoot tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<table id="t">
+<caption id="tcaption"></caption><thead id="thead"></thead><tbody id="tbody1"></tbody><tbody id="tbody2"></tbody><tfoot id="tfoot1"></tfoot><tfoot id="tfoot2"></tfoot><tfoot id="tfoot3"></tfoot></table>
+<script>
+test(function() {
+ var t = document.getElementById("t");
+ var tfoot1 = document.getElementById("tfoot1");
+
+ assert_equals(t.tFoot, tfoot1);
+
+ var tfoot2 = document.getElementById("tfoot2");
+ t.tFoot = null;
+
+ assert_equals(t.tFoot, tfoot2);
+
+ var tfoot3 = document.getElementById("tfoot3");
+ t.deleteTFoot();
+
+ assert_equals(t.tFoot, tfoot3);
+
+ var tfoot = t.createTFoot();
+ assert_equals(t.tFoot, tfoot);
+ assert_equals(tfoot, tfoot3);
+
+ t.deleteTFoot();
+ assert_equals(t.tFoot, null);
+
+ var tbody2 = document.getElementById("tbody2");
+
+ tfoot = t.createTFoot();
+ assert_equals(t.tFoot, tfoot);
+
+ assert_equals(t.tFoot.previousSibling, tbody2);
+ assert_equals(t.tFoot.nextSibling, null);
+
+ t.deleteTFoot();
+ assert_equals(t.tFoot, null);
+
+ t.tFoot = tfoot;
+ assert_equals(t.tFoot, tfoot);
+
+ assert_equals(t.tFoot.previousSibling, tbody2);
+ assert_equals(t.tFoot.nextSibling, null);
+
+ assert_throws_js(TypeError, function(){
+ t.tFoot = document.createElement("div");
+ });
+
+ assert_throws_dom("HierarchyRequestError", function(){
+ t.tFoot = document.createElement("thead");
+ });
+})
+
+test(function () {
+ var table = document.createElementNS("http://www.w3.org/1999/xhtml", "foo:table")
+ var tfoot = table.createTFoot();
+
+ assert_equals(table.tFoot, tfoot);
+ assert_equals(tfoot.prefix, null);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tHead.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tHead.html
new file mode 100644
index 0000000000..fadebecd6f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/tHead.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>tHead tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<table id="t">
+<caption id="tcaption"></caption><thead id="thead1"></thead><thead id="thead2"></thead><thead id="thead3"></thead><tbody id="tbody1"></tbody><tbody id="tbody2"></tbody><tfoot id="tfoot"></tfoot>
+</table>
+<table>
+<thead id="t2thead">
+<td>
+<table id="t2">
+</table>
+</table>
+<script>
+test(function() {
+ var t = document.getElementById("t");
+ var thead1 = document.getElementById("thead1");
+
+ assert_equals(t.tHead, thead1);
+
+ var thead2 = document.getElementById("thead2");
+ t.tHead = null;
+
+ assert_equals(t.tHead, thead2);
+
+ var thead3 = document.getElementById("thead3");
+ t.deleteTHead();
+
+ assert_equals(t.tHead, thead3);
+
+ var thead = t.createTHead();
+ assert_equals(t.tHead, thead);
+ assert_equals(thead, thead3);
+
+ t.deleteTHead();
+ assert_equals(t.tHead, null);
+
+ var tcaption = document.getElementById("tcaption");
+ var tbody1 = document.getElementById("tbody1");
+
+ thead = t.createTHead();
+ assert_equals(t.tHead, thead);
+
+ assert_equals(t.tHead.previousSibling, tcaption);
+ assert_equals(t.tHead.nextSibling, tbody1);
+
+ assert_throws_js(TypeError, function(){
+ t.tHead = document.createElement("div");
+ });
+
+ assert_throws_dom("HierarchyRequestError", function(){
+ t.tHead = document.createElement("tbody");
+ });
+
+});
+
+test(function() {
+ var t2 = document.getElementById("t2");
+ var t2thead = document.getElementById("t2thead");
+
+ assert_throws_dom("HierarchyRequestError", function() {
+ t2.tHead = t2thead;
+ });
+});
+
+test(function () {
+ var table = document.createElementNS("http://www.w3.org/1999/xhtml", "foo:table")
+ var thead = table.createTHead();
+
+ assert_equals(table.tHead, thead);
+ assert_equals(thead.prefix, null);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-insertRow.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-insertRow.html
new file mode 100644
index 0000000000..8a9574ecdd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-insertRow.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTMLTableElement.insertRow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var HTMLNS = "http://www.w3.org/1999/xhtml"
+ var parentEl = document.createElementNS(HTMLNS, "html:table")
+ assert_equals(parentEl.namespaceURI, HTMLNS, "Parent should be in the HTML namespace")
+ assert_equals(parentEl.prefix, "html", "Parent prefix should be html")
+ assert_equals(parentEl.localName, "table", "Parent local name should be table")
+ assert_equals(parentEl.tagName, "HTML:TABLE", "Parent tag name should be HTML:TABLE")
+
+ var row = parentEl.insertRow(-1)
+ assert_equals(row.namespaceURI, HTMLNS, "Row should be in the HTML namespace")
+ assert_equals(row.prefix, null, "Row prefix should be null")
+ assert_equals(row.localName, "tr", "Row local name should be tr")
+ assert_equals(row.tagName, "TR", "Row tag name should be TR")
+
+ var body = row.parentNode
+ assert_equals(body.namespaceURI, HTMLNS, "Body should be in the HTML namespace")
+ assert_equals(body.prefix, null, "Body prefix should be null")
+ assert_equals(body.localName, "tbody", "Body local name should be tr")
+ assert_equals(body.tagName, "TBODY", "Body tag name should be TR")
+
+ assert_array_equals(parentEl.childNodes, [body])
+ assert_array_equals(body.childNodes, [row])
+ assert_array_equals(parentEl.rows, [row])
+}, "insertRow should not copy prefixes")
+test(function() {
+ var table = document.createElement("table")
+ var head = table.appendChild(document.createElement("thead"))
+ assert_array_equals(table.rows, [])
+
+ var row = table.insertRow(-1)
+ var body = row.parentNode
+ assert_array_equals(table.childNodes, [head, body])
+ assert_array_equals(head.childNodes, [])
+ assert_array_equals(body.childNodes, [row])
+ assert_array_equals(table.rows, [row])
+}, "insertRow should insert into a tbody, not into a thead, if table.rows is empty")
+test(function() {
+ var table = document.createElement("table")
+ var foot = table.appendChild(document.createElement("tfoot"))
+ assert_array_equals(table.rows, [])
+
+ var row = table.insertRow(-1)
+ var body = row.parentNode
+ assert_array_equals(table.childNodes, [foot, body])
+ assert_array_equals(foot.childNodes, [])
+ assert_array_equals(body.childNodes, [row])
+ assert_array_equals(table.rows, [row])
+}, "insertRow should insert into a tbody, not into a tfoot, if table.rows is empty")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-rows.html b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-rows.html
new file mode 100644
index 0000000000..8bc23d5a7c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-table-element/table-rows.html
@@ -0,0 +1,234 @@
+<!DOCTYPE html>
+<title>HTMLTableElement.rows</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function assert_nodelist_equals(actual, expected) {
+ assert_equals(actual.length, expected.length);
+
+ for (var i = 0; i < actual.length; ++i) {
+ assert_true(i in actual);
+ assert_true(actual.hasOwnProperty(i),
+ "property " + i + " expected to be present on the object");
+ assert_equals(actual.item(i), expected[i]);
+ assert_equals(actual[i], expected[i]);
+ }
+}
+
+function test_table_simple(group, table) {
+ var foo1 = group.appendChild(document.createElement("tr"));
+ foo1.id = "foo";
+ var bar1 = group.appendChild(document.createElement("tr"));
+ bar1.id = "bar";
+ var foo2 = group.appendChild(document.createElement("tr"));
+ foo2.id = "foo";
+ var bar2 = group.appendChild(document.createElement("tr"));
+ bar2.id = "bar";
+
+ assert_true(table.rows instanceof HTMLCollection, "table.rows should be a HTMLCollection.");
+ assert_nodelist_equals(table.rows, [foo1, bar1, foo2, bar2]);
+ assert_equals(table.rows.foo, foo1);
+ assert_equals(table.rows["foo"], foo1);
+ assert_equals(table.rows.namedItem("foo"), foo1);
+ assert_equals(table.rows.bar, bar1);
+ assert_equals(table.rows["bar"], bar1);
+ assert_equals(table.rows.namedItem("bar"), bar1);
+ assert_array_equals(Object.getOwnPropertyNames(table.rows), [
+ "0",
+ "1",
+ "2",
+ "3",
+ "foo",
+ "bar"
+ ]);
+}
+test(function() {
+ var table = document.createElement("table");
+ test_table_simple(table, table);
+}, "Children of table");
+test(function() {
+ var table = document.createElement("table");
+ var group = table.appendChild(document.createElement("thead"));
+ test_table_simple(group, table);
+}, "Children of thead");
+test(function() {
+ var table = document.createElement("table");
+ var group = table.appendChild(document.createElement("tbody"));
+ test_table_simple(group, table);
+}, "Children of tbody");
+test(function() {
+ var table = document.createElement("table");
+ var group = table.appendChild(document.createElement("tfoot"));
+ test_table_simple(group, table);
+}, "Children of tfoot");
+test(function() {
+ var table = document.createElement("table");
+ var orphan1 = table.appendChild(document.createElement("tr"));
+ orphan1.id = "orphan1";
+ var foot1 = table.appendChild(document.createElement("tfoot"));
+ var orphan2 = table.appendChild(document.createElement("tr"));
+ orphan2.id = "orphan2";
+ var foot2 = table.appendChild(document.createElement("tfoot"));
+ var orphan3 = table.appendChild(document.createElement("tr"));
+ orphan3.id = "orphan3";
+ var body1 = table.appendChild(document.createElement("tbody"));
+ var orphan4 = table.appendChild(document.createElement("tr"));
+ orphan4.id = "orphan4";
+ var body2 = table.appendChild(document.createElement("tbody"));
+ var orphan5 = table.appendChild(document.createElement("tr"));
+ orphan5.id = "orphan5";
+ var head1 = table.appendChild(document.createElement("thead"));
+ var orphan6 = table.appendChild(document.createElement("tr"));
+ orphan6.id = "orphan6";
+ var head2 = table.appendChild(document.createElement("thead"));
+ var orphan7 = table.appendChild(document.createElement("tr"));
+ orphan7.id = "orphan7";
+
+ var foot1row1 = foot1.appendChild(document.createElement("tr"));
+ foot1row1.id = "foot1row1";
+ var foot1row2 = foot1.appendChild(document.createElement("tr"));
+ foot1row2.id = "foot1row2";
+ var foot2row1 = foot2.appendChild(document.createElement("tr"));
+ foot2row1.id = "foot2row1";
+ var foot2row2 = foot2.appendChild(document.createElement("tr"));
+ foot2row2.id = "foot2row2";
+
+ var body1row1 = body1.appendChild(document.createElement("tr"));
+ body1row1.id = "body1row1";
+ var body1row2 = body1.appendChild(document.createElement("tr"));
+ body1row2.id = "body1row2";
+ var body2row1 = body2.appendChild(document.createElement("tr"));
+ body2row1.id = "body2row1";
+ var body2row2 = body2.appendChild(document.createElement("tr"));
+ body2row2.id = "body2row2";
+
+ var head1row1 = head1.appendChild(document.createElement("tr"));
+ head1row1.id = "head1row1";
+ var head1row2 = head1.appendChild(document.createElement("tr"));
+ head1row2.id = "head1row2";
+ var head2row1 = head2.appendChild(document.createElement("tr"));
+ head2row1.id = "head2row1";
+ var head2row2 = head2.appendChild(document.createElement("tr"));
+ head2row2.id = "head2row2";
+
+ // These elements should not end up in any collection.
+ table.appendChild(document.createElement("div"))
+ .appendChild(document.createElement("tr"));
+ foot1.appendChild(document.createElement("div"))
+ .appendChild(document.createElement("tr"));
+ body1.appendChild(document.createElement("div"))
+ .appendChild(document.createElement("tr"));
+ head1.appendChild(document.createElement("div"))
+ .appendChild(document.createElement("tr"));
+ table.appendChild(document.createElementNS("http://example.com/test", "tr"));
+ foot1.appendChild(document.createElementNS("http://example.com/test", "tr"));
+ body1.appendChild(document.createElementNS("http://example.com/test", "tr"));
+ head1.appendChild(document.createElementNS("http://example.com/test", "tr"));
+
+ assert_true(table.rows instanceof HTMLCollection, "table.rows should be a HTMLCollection.");
+ assert_nodelist_equals(table.rows, [
+ // thead
+ head1row1,
+ head1row2,
+ head2row1,
+ head2row2,
+
+ // tbody + table
+ orphan1,
+ orphan2,
+ orphan3,
+ body1row1,
+ body1row2,
+ orphan4,
+ body2row1,
+ body2row2,
+ orphan5,
+ orphan6,
+ orphan7,
+
+ // tfoot
+ foot1row1,
+ foot1row2,
+ foot2row1,
+ foot2row2
+ ]);
+ assert_array_equals(Object.getOwnPropertyNames(table.rows), [
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ "16",
+ "17",
+ "18",
+ "head1row1",
+ "head1row2",
+ "head2row1",
+ "head2row2",
+ "orphan1",
+ "orphan2",
+ "orphan3",
+ "body1row1",
+ "body1row2",
+ "orphan4",
+ "body2row1",
+ "body2row2",
+ "orphan5",
+ "orphan6",
+ "orphan7",
+ "foot1row1",
+ "foot1row2",
+ "foot2row1",
+ "foot2row2"
+ ]);
+
+ var ids = [
+ "orphan1",
+ "orphan2",
+ "orphan3",
+ "orphan4",
+ "orphan5",
+ "orphan6",
+ "orphan7",
+ "foot1row1",
+ "foot1row2",
+ "foot2row1",
+ "foot2row2",
+ "body1row1",
+ "body1row2",
+ "body2row1",
+ "body2row2",
+ "head1row1",
+ "head1row2",
+ "head2row1",
+ "head2row2"
+ ];
+ ids.forEach(function(id) {
+ assert_equals(table.rows.namedItem(id).id, id);
+ assert_true(id in table.rows);
+ assert_equals(table.rows[id].id, id);
+ assert_true(id in table.rows);
+ });
+ while (table.firstChild) {
+ table.removeChild(table.firstChild);
+ }
+ ids.forEach(function(id) {
+ assert_equals(table.rows.namedItem(id), null);
+ assert_false(id in table.rows);
+ assert_equals(table.rows[id], undefined);
+ assert_false(id in table.rows);
+ });
+}, "Complicated case");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/deleteRow.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/deleteRow.html
new file mode 100644
index 0000000000..695c1ea50b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/deleteRow.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLTableSectionElement#deleteRow</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id ="log"></div>
+
+<table>
+ <tbody id="testBody">
+ <tr><td>ABCDEF</td></tr>
+ <tr><td>12345</td></tr>
+ <tr><td>ABC12345DEF</td></tr>
+ </tbody>
+</table>
+
+<script>
+
+var tbody = document.getElementById("testBody");
+
+test(function () {
+ tbody.deleteRow(0);
+ assert_equals(tbody.rows.length, 2);
+ assert_equals(tbody.rows[0].childNodes[0].innerHTML, "12345");
+}, "HTMLTableSectionElement deleteRow(0)");
+
+test(function () {
+ tbody.deleteRow(-1);
+ assert_equals(tbody.rows.length, 1);
+ assert_equals(tbody.rows[0].childNodes[0].innerHTML, "12345");
+}, "HTMLTableSectionElement deleteRow(-1)");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tbody.deleteRow(tbody.rows.length);
+ });
+}, "HTMLTableSectionElement deleteRow(rows.length)");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tbody.deleteRow(-2);
+ });
+}, "HTMLTableSectionElement deleteRow(-2)");
+
+test(function () {
+ assert_equals(tbody.rows.length, 1);
+ tbody.deleteRow(-1);
+ assert_equals(tbody.rows.length, 0);
+ tbody.deleteRow(-1);
+ assert_equals(tbody.rows.length, 0);
+}, "HTMLTableSectionElement deleteRow(-1) with no rows");
+
+test(function () {
+ assert_equals(tbody.rows.length, 0);
+ assert_throws_dom("IndexSizeError", function () {
+ tbody.deleteRow(0);
+ });
+}, "HTMLTableSectionElement deleteRow(0) with no rows");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/insertRow.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/insertRow.html
new file mode 100644
index 0000000000..f5c2227ca6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/insertRow.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLTableSectionElement#insertRow</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id ="log"></div>
+
+<table>
+ <tbody id="testBody">
+ <tr><td>ABCDEF</td></tr>
+ </tbody>
+</table>
+
+<script>
+
+var tbody = document.getElementById("testBody");
+
+test(function () {
+ var trEle = tbody.insertRow(0);
+ assert_equals(tbody.rows[0], trEle);
+ assert_equals(tbody.rows.length, 2);
+}, "HTMLTableSectionElement insertRow(0)");
+
+test(function () {
+ var trEle = tbody.insertRow(-1);
+ assert_equals(tbody.rows[tbody.rows.length - 1], trEle);
+ assert_equals(tbody.rows.length, 3);
+}, "HTMLTableSectionElement insertRow(-1)");
+
+test(function () {
+ var trEle = tbody.insertRow();
+ assert_equals(tbody.rows[tbody.rows.length - 1], trEle);
+ assert_equals(tbody.rows.length, 4);
+}, "HTMLTableSectionElement insertRow()");
+
+test(function () {
+ var trEle = tbody.insertRow(tbody.rows.length);
+ assert_equals(tbody.rows[tbody.rows.length - 1], trEle);
+ assert_equals(tbody.rows.length, 5);
+}, "HTMLTableSectionElement insertRow(rows.length)");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tbody.insertRow(-2);
+ });
+}, "HTMLTableSectionElement insertRow(-2)");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tbody.insertRow(tbody.rows.length + 1);
+ });
+}, "HTMLTableSectionElement insertRow(rows.length + 1)");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/rows.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/rows.html
new file mode 100644
index 0000000000..eb155de774
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tbody-element/rows.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>'tbody' element, 'rows' attribute</title>
+<link rel="author" title="Corey Farwell" href="mailto:coreyf@rwell.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/semantics/tabular-data/html-table-section-element.js"></script>
+
+<div id ="log"></div>
+
+<script>
+test(function () {
+ testRowsAttribute('tbody');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tfoot-element/rows.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tfoot-element/rows.html
new file mode 100644
index 0000000000..fe70d6f286
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tfoot-element/rows.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>'tfoot' element, 'rows' attribute</title>
+<link rel="author" title="Corey Farwell" href="mailto:coreyf@rwell.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/semantics/tabular-data/html-table-section-element.js"></script>
+
+<div id ="log"></div>
+
+<script>
+test(function () {
+ testRowsAttribute('tfoot');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-thead-element/rows.html b/testing/web-platform/tests/html/semantics/tabular-data/the-thead-element/rows.html
new file mode 100644
index 0000000000..7830281a01
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-thead-element/rows.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>'thead' element, 'rows' attribute</title>
+<link rel="author" title="Corey Farwell" href="mailto:coreyf@rwell.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/semantics/tabular-data/html-table-section-element.js"></script>
+
+<div id ="log"></div>
+
+<script>
+test(function () {
+ testRowsAttribute('thead');
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/cells.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/cells.html
new file mode 100644
index 0000000000..2678d3b1c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/cells.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLTableRowElement#cells</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<table>
+ <tr id="testTr">
+ <td>First</td>
+ <div><td>Second</td></div>
+ <td>Third
+ <table>
+ <tr><td>Nested first</td></tr>
+ </table>
+ </td>
+ <img>
+ </tr>
+</table>
+<script>
+var tr = document.getElementById("testTr");
+
+test(function () {
+ tr.insertBefore(document.createElementNS("foo", "td"), tr.children[1]);
+ assert_array_equals(tr.cells, [tr.children[0], tr.children[2], tr.children[3]]);
+}, "HTMLTableRowElement cells ignores nested tables and non-HTML elements");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/deleteCell.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/deleteCell.html
new file mode 100644
index 0000000000..9962617a71
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/deleteCell.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLTableRowElement#deleteCell</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<table>
+ <tr id="testTr">
+ <td>ABCDE</td>
+ <td>12345</td>
+ <td>ABC12</td>
+ </tr>
+</table>
+
+<script>
+
+var tr = document.getElementById("testTr");
+
+test(function () {
+ tr.deleteCell(0);
+ assert_equals(tr.cells[0].innerHTML, "12345");
+ assert_equals(tr.cells.length, 2);
+}, "HTMLTableRowElement deleteCell(0)");
+
+test(function () {
+ tr.deleteCell(-1);
+ assert_equals(tr.cells[tr.cells.length - 1].innerHTML, "12345");
+ assert_equals(tr.cells.length, 1);
+}, "HTMLTableRowElement deleteCell(-1)");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tr.deleteCell(-2);
+ });
+}, "HTMLTableRowElement deleteCell(-2)");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tr.deleteCell(tr.cells.length);
+ });
+}, "HTMLTableRowElement deleteCell(cells.length)");
+
+test(function () {
+ assert_equals(tr.cells.length, 1);
+ tr.deleteCell(-1);
+ assert_equals(tr.cells.length, 0);
+ tr.deleteCell(-1);
+ assert_equals(tr.cells.length, 0);
+}, "HTMLTableRowElement deleteCell(-1) with no cells");
+
+test(function () {
+ assert_equals(tr.cells.length, 0);
+ assert_throws_dom("IndexSizeError", function () {
+ tr.deleteCell(0);
+ });
+}, "HTMLTableRowElement deleteCell(0) with no cells");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/insertCell.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/insertCell.html
new file mode 100644
index 0000000000..11cd213fe7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/insertCell.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTMLTableRowElement#insertCell</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<table>
+ <tr id="testTr"></tr>
+</table>
+
+<script>
+
+var tr = document.getElementById("testTr");
+
+test(function () {
+ var tdEle = tr.insertCell(0);
+ assert_equals(tr.cells[0], tdEle);
+ assert_equals(tr.cells.length, 1);
+}, "HTMLTableRowElement insertCell(0)");
+
+test(function () {
+ var tdEle = tr.insertCell(-1);
+ assert_equals(tr.cells[tr.cells.length - 1], tdEle);
+ assert_equals(tr.cells.length, 2);
+}, "HTMLTableRowElement insertCell(-1)");
+
+
+test(function () {
+ var tdEle = tr.insertCell(tr.cells.length);
+ assert_equals(tr.cells[tr.cells.length - 1], tdEle);
+ assert_equals(tr.cells.length, 3);
+}, "HTMLTableRowElement insertCell(cells.length)");
+
+test(function () {
+ var tdEle = tr.insertCell();
+ assert_equals(tr.cells[tr.cells.length - 1], tdEle);
+ assert_equals(tr.cells.length, 4);
+}, "HTMLTableRowElement insertCell()");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tr.insertCell(-2);
+ });
+}, "HTMLTableRowElement insertCell(-2)");
+
+test(function () {
+ assert_throws_dom("IndexSizeError", function () {
+ tr.insertCell(tr.cells.length + 1);
+ });
+}, "HTMLTableRowElement insertCell(cells.length + 1)");
+
+test(function () {
+ var table = document.createElementNS("http://www.w3.org/1999/xhtml", "foo:table")
+ var row = table.insertRow(0);
+ var cell = row.insertCell(0);
+
+ assert_equals(row.cells[0], cell);
+ assert_equals(cell.prefix, null);
+}, "HTMLTableRowElement insertCell will not copy table's prefix");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/rowIndex.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/rowIndex.html
new file mode 100644
index 0000000000..117712563d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/rowIndex.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<title>HTMLTableRowElement.rowIndex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElement("div"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElement("thead"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, 0);
+});
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElement("tbody"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, 0);
+});
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElement("tfoot"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, 0);
+});
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, 0);
+});
+test(function() {
+ var row = document.createElementNS("", "table")
+ .appendChild(document.createElement("thead"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+test(function() {
+ var row = document.createElementNS("", "table")
+ .appendChild(document.createElement("tbody"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+test(function() {
+ var row = document.createElementNS("", "table")
+ .appendChild(document.createElement("tfoot"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+test(function() {
+ var row = document.createElementNS("", "table")
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElementNS("", "thead"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElementNS("", "tbody"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+test(function() {
+ var row = document.createElement("table")
+ .appendChild(document.createElementNS("", "tfoot"))
+ .appendChild(document.createElement("tr"));
+ assert_equals(row.rowIndex, -1);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/sectionRowIndex.html b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/sectionRowIndex.html
new file mode 100644
index 0000000000..ef5366739e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/tabular-data/the-tr-element/sectionRowIndex.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTMLTableRowElement.sectionRowIndex</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<table>
+ <thead>
+ <tr id="ht1"></tr>
+ </thead>
+ <tr id="t1"></tr>
+ <tr id="t2">
+ <td>
+ <table>
+ <thead>
+ <tr id="nht1"></tr>
+ </thead>
+ <tr></tr>
+ <tr id="nt1"></tr>
+ <tbody>
+ <tr id="nbt1"></tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ <tbody>
+ <tr></tr>
+ <tr id="bt1"></tr>
+ </tbody>
+ <tfoot>
+ <tr></tr>
+ <tr></tr>
+ <tr id="ft1"></tr>
+ </tfoot>
+</table>
+
+<script>
+test(function() {
+ var tHeadRow = document.getElementById('ht1');
+ assert_equals(tHeadRow.sectionRowIndex, 0);
+}, "Row in thead in HTML");
+
+test(function() {
+ var tRow1 = document.getElementById('t1');
+ assert_equals(tRow1.sectionRowIndex, 0);
+}, "Row in implicit tbody in HTML");
+
+test(function() {
+ var tRow2 = document.getElementById('t2');
+ assert_equals(tRow2.sectionRowIndex, 1);
+}, "Other row in implicit tbody in HTML");
+
+test(function() {
+ var tBodyRow = document.getElementById('bt1');
+ assert_equals(tBodyRow.sectionRowIndex, 1);
+}, "Row in explicit tbody in HTML");
+
+test(function() {
+ var tFootRow = document.getElementById('ft1');
+ assert_equals(tFootRow.sectionRowIndex, 2);
+}, "Row in tfoot in HTML");
+
+test(function() {
+ var childHeadRow = document.getElementById('nht1');
+ assert_equals(childHeadRow.sectionRowIndex, 0);
+}, "Row in thead in nested table in HTML");
+
+test(function() {
+ var childRow = document.getElementById('nt1');
+ assert_equals(childRow.sectionRowIndex, 1);
+}, "Row in implicit tbody in nested table in HTML");
+
+test(function() {
+ var childBodyRow = document.getElementById('nbt1');
+ assert_equals(childBodyRow.sectionRowIndex, 0);
+}, "Row in explicit tbody in nested table in HTML");
+
+/* script create element test */
+var mkTrElm = function (elst) {
+ var elm = document.createElement("table");
+ elst.forEach(function(item) {
+ elm = elm.appendChild(document.createElement(item));
+ });
+ return elm.appendChild(document.createElement("tr"));
+};
+
+test(function() {
+ assert_equals(mkTrElm([]).sectionRowIndex, 0);
+}, "Row in script-created table");
+
+test(function() {
+ assert_equals(mkTrElm(["div"]).sectionRowIndex, -1);
+}, "Row in script-created div in table");
+
+test(function() {
+ assert_equals(mkTrElm(["thead"]).sectionRowIndex, 0);
+}, "Row in script-created thead in table");
+
+test(function() {
+ assert_equals(mkTrElm(["tbody"]).sectionRowIndex, 0);
+}, "Row in script-created tbody in table");
+
+test(function() {
+ assert_equals(mkTrElm(["tfoot"]).sectionRowIndex, 0);
+}, "Row in script-created tfoot in table");
+
+test(function() {
+ assert_equals(mkTrElm(["tbody", "tr"]).sectionRowIndex, -1);
+}, "Row in script-created tr in tbody in table");
+
+test(function() {
+ assert_equals(mkTrElm(["tbody", "tr", "td"]).sectionRowIndex, -1);
+}, "Row in script-created td in tr in tbody in table");
+
+test(function() {
+ assert_equals(mkTrElm(["tbody", "tr", "td", "table"]).sectionRowIndex, 0);
+}, "Row in script-created nested table");
+
+test(function() {
+ assert_equals(mkTrElm(["tbody", "tr", "td", "table", "thead"]).sectionRowIndex, 0);
+}, "Row in script-created thead in nested table");
+
+test(function() {
+ assert_equals(mkTrElm(["tbody", "tr", "td", "table", "tbody"]).sectionRowIndex, 0);
+}, "Row in script-created tbody in nested table");
+
+test(function() {
+ assert_equals(mkTrElm(["tbody", "tr", "td", "table", "tfoot"]).sectionRowIndex, 0);
+}, "Row in script-created tfoot in nested table");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/historical.html b/testing/web-platform/tests/html/semantics/text-level-semantics/historical.html
new file mode 100644
index 0000000000..7fe83a95ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/historical.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>Historical text-level element features should not be supported</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+function t(property, tagNames) {
+ if (typeof tagNames === "string") {
+ tagNames = [tagNames];
+ }
+ tagNames.forEach(function(tagName) {
+ test(function() {
+ assert_false(property in document.createElement(tagName));
+ }, tagName + '.' + property + ' should not be supported');
+ });
+}
+
+// <area> and <link> are in other sections in the spec, but we'll test them here together with <a>
+
+// removed in https://github.com/whatwg/html/commit/790479ab1ba143efc27d1f92cd0465627df48fb0
+t('hreflang', 'area');
+t('type', 'area');
+
+// renamed to dateTime in https://github.com/whatwg/html/commit/8b6732237c7021cd61e3c3463146234ca8ce5bad
+t('datetime', 'time');
+
+// removed in https://github.com/whatwg/html/commit/66fcb2357f205448fe2f40d7834a1e8ea2ed283b
+t('media', ['a', 'area']);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-click-handler-with-null-browsing-context-crash.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-click-handler-with-null-browsing-context-crash.html
new file mode 100644
index 0000000000..a9fd6b82bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-click-handler-with-null-browsing-context-crash.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>HTMLAnchorElement.onclick with null browsing conext</title>
+<link rel="author" title="Nate Chapin" href="mailto:japhet@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-a-element">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1083721">
+<meta name="assert" content="An <a> from a discarded browsing context that is cloned into a new document should not crash when clicked"/>
+<body>
+<iframe id="i" src="resources/a-onclick-handler-iframe.html"></iframe>
+<script>
+window.onload = () => {
+ var range = i.contentDocument.createRange();
+ range.selectNodeContents(i.contentDocument.body);
+ i.remove();
+
+ // Clone the <a> into this document, and ensure clicking it does not crash.
+ document.body.appendChild(range.cloneContents());
+ var a = document.getElementsByTagName('a')[0];
+ a.click();
+};
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-404.py b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-404.py
new file mode 100644
index 0000000000..abb85139b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-404.py
@@ -0,0 +1,2 @@
+def main(request, response):
+ return 404, [(b"Content-Type", b"text/html")], b'Some content for the masses.' * 100
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-404.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-404.html
new file mode 100644
index 0000000000..3c8adc0b97
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-404.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Clicking on an &lt;a> element with a download attribute and href that leads to 404 should not navigate</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#attr-hyperlink-download">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+"use strict";
+async_test(t => {
+ const errorFrame = document.createElement("iframe");
+
+ errorFrame.addEventListener("load", t.step_func(function () {
+ errorFrame.contentWindow.addEventListener(
+ "beforeunload", t.unreached_func("Navigated instead of downloading"));
+
+ errorFrame.contentDocument.querySelector("#error-url").click();
+ t.step_timeout(() => t.done(), 1000);
+ }));
+ errorFrame.src = "resources/a-download-404.html";
+ document.body.appendChild(errorFrame);
+}, "Do not navigate to 404 for anchor with download");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-redirect-to-javascript.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-redirect-to-javascript.html
new file mode 100644
index 0000000000..09f63b6f4d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click-redirect-to-javascript.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Clicking on an &lt;a> element with a download attribute and href that redirects to 'javascript:' should not navigate or execute</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#attr-hyperlink-download">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+"use strict";
+async_test(t => {
+ const errorFrame = document.createElement("iframe");
+
+ errorFrame.addEventListener("load", t.step_func(function () {
+ assert_false(errorFrame.contentWindow.executed);
+ errorFrame.contentWindow.addEventListener(
+ "beforeunload", t.unreached_func("Page should not navigate."));
+
+ errorFrame.contentDocument.querySelector("#error-url").click();
+ t.step_timeout(_ => {
+ assert_false(errorFrame.contentWindow.executed, "Redirecting to javascript: was suppressed.");
+ t.done();
+ }, 1000);
+ }));
+ errorFrame.src = "resources/a-download-redirect-to-javascript.html";
+ document.body.appendChild(errorFrame);
+}, "Do not navigate or execute JS when redirecting a download to 'javascript:..'");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click.html
new file mode 100644
index 0000000000..22d329f245
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-download-click.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Clicking on an &lt;a> element with a download attribute must not throw an exception</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour">
+<link rel="help" href="https://github.com/whatwg/html/issues/2116">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+async_test(t => {
+ const frame = document.createElement("iframe");
+
+ frame.addEventListener("load", t.step_func(function () {
+ frame.contentWindow.addEventListener(
+ "beforeunload", t.unreached_func("Navigated instead of downloading"));
+ const string = "test";
+ const blob = new Blob([string], { type: "text/html" });
+
+ const link = frame.contentDocument.querySelector("#blob-url");
+ link.href = URL.createObjectURL(blob);
+
+ link.click();
+
+ t.step_timeout(() => t.done(), 1000);
+ }));
+ frame.src = "resources/a-download-click.html";
+ document.body.appendChild(frame);
+}, "Clicking on an <a> element with a download attribute must not throw an exception");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-stringifier.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-stringifier.html
new file mode 100644
index 0000000000..1085a74aa6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a-stringifier.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>HTMLAnchorElement stringifier</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-stringifier">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/stringifiers.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ test_stringifier_attribute(document.createElement("a"), "href", false);
+ var a = document.createElement("a");
+ a.setAttribute("href", "foo");
+ test_stringifier_attribute(a, "href", false);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-getter-01.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-getter-01.html
new file mode 100644
index 0000000000..e0bb73be0f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-getter-01.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>HTMLAnchorElement.text getting</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-a-text">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>var b</script>
+<div id="test">
+<a href="a">a b c </a>
+<a href="b">a <!--b-->b c </a>
+<a href="c">a <b>b</b> c </a>
+<a href="d">a <script>b</script> c </a>
+<script>
+var e = document.getElementById("test")
+ .appendChild(document.createElement("a"));
+e.href = "d";
+e.appendChild(document.createTextNode("a "));
+e.appendChild(document.createTextNode("b "));
+e.appendChild(document.createTextNode("c "));
+</script>
+</div>
+<script>
+test(function() {
+ var list = document.getElementById("test")
+ .getElementsByTagName("a");
+ for (var i = 0, il = list.length; i < il; ++i) {
+ test(function() {
+ assert_equals(list[i].text, list[i].textContent);
+ assert_equals(list[i].text, "a b c ");
+ }, "Test for anchor " + i);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-setter-01.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-setter-01.html
new file mode 100644
index 0000000000..879a9e3d08
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/a.text-setter-01.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>HTMLAnchorElement.text setting</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-a-text">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<div id="test">
+<a href="a">a b c</a>
+<a href="b">a <!--b--> c</a>
+<a href="c">a <b>b</b> c</a>
+<script>
+var d = document.getElementById("test")
+ .appendChild(document.createElement("a"));
+d.href = "d";
+d.appendChild(document.createTextNode("a "));
+d.appendChild(document.createTextNode("b "));
+d.appendChild(document.createTextNode("c "));
+</script>
+</div>
+<script>
+test(function() {
+ var list = document.getElementById("test")
+ .getElementsByTagName("a");
+ for (var i = 0, il = list.length; i < il; ++i) {
+ test(function() {
+ list[i].text = "x";
+ assert_equals(list[i].text, "x");
+ assert_equals(list[i].textContent, "x");
+ assert_equals(list[i].firstChild.data, "x");
+ assert_equals(list[i].childNodes.length, 1);
+
+ list[i].textContent = "y";
+ assert_equals(list[i].text, "y");
+ assert_equals(list[i].textContent, "y");
+ assert_equals(list[i].firstChild.data, "y");
+ assert_equals(list[i].childNodes.length, 1);
+ }, "Test for anchor " + i);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-404.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-404.html
new file mode 100644
index 0000000000..8c5d8f4565
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-404.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<a id="error-url" href="../a-download-404.py" download="html.html">Click me</a>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-click.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-click.html
new file mode 100644
index 0000000000..7d36c21d1e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-click.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<a id="blob-url" download="foo.html">Click me</a>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-redirect-to-javascript.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-redirect-to-javascript.html
new file mode 100644
index 0000000000..4ff8b61e3b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-download-redirect-to-javascript.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+ window.executed = false;
+</script>
+<a id="error-url" href="/common/redirect.py?location=javascript:window.executed=true" download="html.html">Click me</a>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-onclick-handler-iframe.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-onclick-handler-iframe.html
new file mode 100644
index 0000000000..711e40f9d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-a-element/resources/a-onclick-handler-iframe.html
@@ -0,0 +1 @@
+<a id="a" href="about:blank" onclick="return false">link</a>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage-notref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage-notref.html
new file mode 100644
index 0000000000..3d3c46a281
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage-notref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Reference File</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+
+<p>You enter a small room. Your sword glows brighter. A rat scurries past the corner wall.</p>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage.html
new file mode 100644
index 0000000000..ff2105dcae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-b-element/b-usage.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML test: b - highlight keywords</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="mismatch" href="b-usage-notref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-b-element"/>
+
+<p>You enter a small room. Your <b>sword</b> glows brighter. A <b>rat</b> scurries past the corner wall.</p>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default-ref.html
new file mode 100644
index 0000000000..eff61bb419
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;[:)], [+- a &#x05D1;], [d &#x05D2; 1]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[d &#x05D2; 1] ,[+- a &#x05D1;] ,[:)]&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[:)], [+- a &#x05D1;], [d &#x05D2; 1]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[d &#x05D2; 1] ,[+- a &#x05D1;] ,[:)]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default.html
new file mode 100644
index 0000000000..3a9d90c76b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-auto-dir-default.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: has dir=auto by default</title>
+ <link rel="match" href="bdi-auto-dir-default-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'The dir global attribute defaults to auto on this element (it never inherits from the parent
+ element like with other elements).'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ In each DIV of the test:
+ - the first BDI, having no characters with strong direction, should inherit the parent direction;
+ - the second BDI, having an LTR character first, should be LTR by default;
+ - the third BDI, having an RTL character first, should be RTL by default.
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>[:)]</bdi>, <bdi>[+- a &#x05D1;]</bdi>, <bdi>[1 &#x05D2; d]</bdi>...</div>
+ <div dir="rtl"><bdi>[(:]</bdi>, <bdi>[+- a &#x05D1;]</bdi>, <bdi>[1 &#x05D2; d]</bdi>...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[:)], [+- a &#x05D1;], [d &#x05D2; 1]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[d &#x05D2; 1] ,[+- a &#x05D1;] ,[:)]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf-ref.html
new file mode 100644
index 0000000000..b4d44c5101
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf.html
new file mode 100644
index 0000000000..1ce9da6b76
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-missing-pdf.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral when contains LRO or RLO without PDF</title>
+ <link rel="match" href="bdi-neutral-missing-pdf-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'
+ Thus, if a BDI contains LRO or RLO characters lacking a matching PDF, these must not affect
+ the visual ordering of the content outside the BDI."/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202E; - the RLO (right-to-left-override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO and RLO.
+ If the BDI in the test's first DIV were a SPAN, the RLO it contains, not being closed by a
+ PDF, would visually reorder the de into ed.
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>&#x05D0;&#x202E;bc</bdi>de...</div>
+ <div dir="ltr"><bdi dir="ltr">&#x05D0;&#x202E;bc</bdi>de...</div>
+ <div dir="ltr"><bdi dir="rtl">&#x05D0;&#x202E;bc</bdi>de...</div>
+ <div dir="rtl"><bdi>a&#x202D;&#x05D1;&#x05D2;</bdi>&#x05D3;&#x05D4;...</div>
+ <div dir="rtl"><bdi dir="ltr">a&#x202D;&#x05D1;&#x05D2;</bdi>&#x05D3;&#x05D4;...</div>
+ <div dir="rtl"><bdi dir="rtl">a&#x202D;&#x05D1;&#x05D2;</bdi>&#x05D3;&#x05D4;...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="ltr">&#x202D;cb&#x05D0;de...&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ <div dir="rtl">&#x202D;...&#x05D4;&#x05D3;a&#x05D1;&#x05D2;&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested-ref.html
new file mode 100644
index 0000000000..d5d7674a45
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested.html
new file mode 100644
index 0000000000..158576885c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-nested.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral when nested</title>
+ <link rel="match" href="bdi-neutral-nested-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'
+ This must apply when a BDI is nested within a BDI."/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x05D0; + <bdi>[a + <bdi>[&#x05D1; + <bdi>[b + 4]</bdi> + 3]</bdi> + 2]</bdi> + 1</div>
+ <div dir="ltr">&#x05D0; + <bdi dir="rtl">[a + <bdi dir="ltr">[&#x05D1; + <bdi dir="rtl">[b + 4]</bdi> + 3]</bdi> + 2]</bdi> + 1</div>
+ <div dir="ltr">&#x05D0; + <bdi dir="ltr">[a + <bdi dir="rtl">[&#x05D1; + <bdi dir="ltr">[b + 4]</bdi> + 3]</bdi> + 2]</bdi> + 1</div>
+ <div dir="rtl">a + <bdi>[&#x05D0; + <bdi>[b + <bdi>[&#x05D1; + 3]</bdi> + 2]</bdi> + 1]</bdi> + 0</div>
+ <div dir="rtl">a + <bdi dir="ltr">[&#x05D0; + <bdi dir="rtl">[b + <bdi dir="ltr">[&#x05D1; + 3]</bdi> + 2]</bdi> + 1]</bdi> + 0</div>
+ <div dir="rtl">a + <bdi dir="rtl">[&#x05D0; + <bdi dir="ltr">[b + <bdi dir="rtl">[&#x05D1; + 3]</bdi> + 2]</bdi> + 1]</bdi> + 0</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="ltr">&#x202D;1 + [a + [3 + [b + 4] + &#x05D1;] + 2] + &#x05D0;&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ <div dir="rtl">&#x202D;a + [1 + [b + [3 + &#x05D1;] + 2] + &#x05D0;] + 0&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number-ref.html
new file mode 100644
index 0000000000..df7af7778a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number.html
new file mode 100644
index 0000000000..37e467c173
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-number.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral when number</title>
+ <link rel="match" href="bdi-neutral-number-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the test's first DIV were a SPAN, the 1 inside it would be visually ordered
+ to the left of the &#x05D0;.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x05D0; - <bdi>[1]</bdi>...</div>
+ <div dir="ltr">&#x05D0; - <bdi dir="ltr">[1]</bdi>...</div>
+ <div dir="ltr">&#x05D0; - <bdi dir="rtl">[1]</bdi>...</div>
+ <div dir="rtl">a - <bdi>[1]</bdi>...</div>
+ <div dir="rtl">a - <bdi dir="ltr">[1]</bdi>...</div>
+ <div dir="rtl">a - <bdi dir="rtl">[1]</bdi>...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; - [1]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[1] - a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate-ref.html
new file mode 100644
index 0000000000..ec8e34627d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0; [1 b] c [d &#x05D4;] &#x05D5;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...f [e &#x05D3;] &#x05D2; [&#x05D1; 1] a&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; [1 b] c [d &#x05D4;] &#x05D5;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...f [e &#x05D3;] &#x05D2; [&#x05D1; 1] a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate.html
new file mode 100644
index 0000000000..7bb8a20811
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-separate.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: paragraph-level container</title>
+ <link rel="match" href="bdi-neutral-separate-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'
+ Thus, under no circumstances should any part of the content outside a BDI be visually
+ reordered inside the BDI's content."/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDIs in the test's first DIV were just SPANs, the &#x05D0; would appear between the 1
+ and the b, and the &#x05D5; between the d and the &#x05D4;.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x05D0; <bdi>[1 b]</bdi> c <bdi>[d &#x05D4;]</bdi> &#x05D5;...</div>
+ <div dir="rtl">a <bdi>[1 &#x05D1;]</bdi> &#x05D2; <bdi>[&#x05D3; e]</bdi> f...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; [1 b] c [d &#x05D4;] &#x05D5;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...f [e &#x05D3;] &#x05D2; [&#x05D1; 1] a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1-ref.html
new file mode 100644
index 0000000000..c0f323ea2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions">Test passes if the two boxes below look exactly the same.</div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1.html
new file mode 100644
index 0000000000..822120721f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-1.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: BDI: neutral to another BDI</title>
+ <link rel="match" href="bdi-neutral-to-another-bdi-1-ref.html" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element" />
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'
+ Thus, when a BDI contains text of the same strong direction as another BDI following it, the
+ two must not form a directional run as would be the case if the BDIs were just SPANs." />
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions">Test passes if the two boxes below look exactly the same.</div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDIs in the test's first DIV were SPANs, the &#x05D1; would be rendered to the left
+ of the &#x05D0;.
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>[&#x05D0;]</bdi> &gt; <bdi>[&#x05D1;]</bdi>...</div>
+ <div dir="ltr"><bdi dir="rtl">[&#x05D0;]</bdi> &gt; <bdi dir="rtl">[&#x05D1;]</bdi>...</div>
+ <div dir="ltr"><bdi dir="ltr">[&#x05D0;]</bdi> &gt; <bdi dir="ltr">[&#x05D1;]</bdi>...</div>
+ <div dir="rtl"><bdi>[a]</bdi> &gt; <bdi>[b]</bdi>...</div>
+ <div dir="rtl"><bdi dir="ltr">[a]</bdi> &gt; <bdi dir="ltr">[b]</bdi>...</div>
+ <div dir="rtl"><bdi dir="rtl">[a]</bdi> &gt; <bdi dir="rtl">[b]</bdi>...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; [a]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2-ref.html
new file mode 100644
index 0000000000..9aef97c0ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions">Test passes if the two boxes below look exactly the same.</div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2.html
new file mode 100644
index 0000000000..85aec46686
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-another-bdi-2.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: BDI: neutral to another immediately following BDI</title>
+ <link rel="match" href="bdi-neutral-to-another-bdi-2-ref.html">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com" />
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com" />
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element" />
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'
+ Thus, when a BDI contains text of the same strong direction as another BDI following it, the
+ two must not form a directional run as would be the case if the BDIs were just SPANs, even if
+ the two BDIs are not separated by anything at all." />
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments {
+ display: none;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="instructions">Test passes if the two boxes below look exactly the same.</div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDIs in the test's first DIV were SPANs, the &#x05D1; would be rendered to the left of
+ the &#x05D0;.
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>&#x05D0;</bdi><bdi>&#x05D1;</bdi>...</div>
+ <div dir="ltr"><bdi dir="rtl">&#x05D0;</bdi><bdi dir="rtl">&#x05D1;</bdi>...</div>
+ <div dir="ltr"><bdi dir="ltr">&#x05D0;</bdi><bdi dir="ltr">&#x05D1;</bdi>...</div>
+ <div dir="rtl"><bdi>a</bdi><bdi>b</bdi>...</div>
+ <div dir="rtl"><bdi dir="ltr">a</bdi><bdi dir="ltr">b</bdi>...</div>
+ <div dir="rtl"><bdi dir="rtl">a</bdi><bdi dir="rtl">b</bdi>...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1-ref.html
new file mode 100644
index 0000000000..a34d09bd24
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1.html
new file mode 100644
index 0000000000..76da57c2b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-1.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral to following letter</title>
+ <link rel="match" href="bdi-neutral-to-letter-following-1-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the test's first DIV were a SPAN, the &#x05D1; would be rendered to the left
+ of the &#x05D0;
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>[&#x05D0;]</bdi> &gt; &#x05D1;...</div>
+ <div dir="ltr"><bdi dir="rtl">[&#x05D0;]</bdi> &gt; &#x05D1;...</div>
+ <div dir="ltr"><bdi dir="ltr">[&#x05D0;]</bdi> &gt; &#x05D1;...</div>
+ <div dir="rtl"><bdi>[a]</bdi> &gt; b...</div>
+ <div dir="rtl"><bdi dir="ltr">[a]</bdi> &gt; b...</div>
+ <div dir="rtl"><bdi dir="rtl">[a]</bdi> &gt; b...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] &gt; &#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...b &lt; [a]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2-ref.html
new file mode 100644
index 0000000000..80f36183b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2.html
new file mode 100644
index 0000000000..ce41983f00
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-following-2.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral to immediately following letter</title>
+ <link rel="match" href="bdi-neutral-to-letter-following-2-ref.html">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the following DIV were a SPAN, the &#x05D1; would be rendered to the left
+ of the &#x05D0;
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>&#x05D0;</bdi>&#x05D1;...</div>
+ <div dir="ltr"><bdi dir="rtl">&#x05D0;</bdi>&#x05D1;...</div>
+ <div dir="ltr"><bdi dir="ltr">&#x05D0;</bdi>&#x05D1;...</div>
+ <div dir="rtl"><bdi>a</bdi>b...</div>
+ <div dir="rtl"><bdi dir="ltr">a</bdi>b...</div>
+ <div dir="rtl"><bdi dir="rtl">a</bdi>b...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1-ref.html
new file mode 100644
index 0000000000..5e39eabd28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1.html
new file mode 100644
index 0000000000..46772de642
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-1.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral to preceding letter</title>
+ <link rel="match" href="bdi-neutral-to-letter-preceding-1-ref.html">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the test's first DIV were a SPAN, the &#x05D1; would be rendered to the left
+ of the &#x05D0;
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x05D0; &gt; <bdi>[&#x05D1;]</bdi>...</div>
+ <div dir="ltr">&#x05D0; &gt; <bdi dir="rtl">[&#x05D1;]</bdi>...</div>
+ <div dir="ltr">&#x05D0; &gt; <bdi dir="ltr">[&#x05D1;]</bdi>...</div>
+ <div dir="rtl">a &gt; <bdi>[b]</bdi>...</div>
+ <div dir="rtl">a &gt; <bdi dir="ltr">[b]</bdi>...</div>
+ <div dir="rtl">a &gt; <bdi dir="rtl">[b]</bdi>...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0; &gt; [&#x05D1;]...&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ <div dir="rtl">&#x202D;...[b] &lt; a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2-ref.html
new file mode 100644
index 0000000000..80f36183b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2.html
new file mode 100644
index 0000000000..192115775c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-letter-preceding-2.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral to immediately preceding letter</title>
+ <link rel="match" href="bdi-neutral-to-letter-preceding-2-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the test's first DIV were a SPAN, the &#x05D1; would be rendered to the left
+ of the &#x05D0;
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x05D0;<bdi>&#x05D1;</bdi>...</div>
+ <div dir="ltr">&#x05D0;<bdi dir="rtl">&#x05D1;</bdi>...</div>
+ <div dir="ltr">&#x05D0;<bdi dir="ltr">&#x05D1;</bdi>...</div>
+ <div dir="rtl">a<bdi>b</bdi>...</div>
+ <div dir="rtl">a<bdi dir="ltr">b</bdi>...</div>
+ <div dir="rtl">a<bdi dir="rtl">b</bdi>...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;&#x05D1;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ <div dir="rtl">&#x202D;...ba&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1-ref.html
new file mode 100644
index 0000000000..ad15d468b8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1.html
new file mode 100644
index 0000000000..ff566737f2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-1.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral to following number</title>
+ <link rel="match" href="bdi-neutral-to-number-following-1-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the test's first DIV were a SPAN, the 3 would be rendered to the left
+ of the &#x05D0;
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>[&#x05D0;]</bdi> (3 reviews)...</div>
+ <div dir="ltr"><bdi dir="rtl">[&#x05D0;]</bdi> (3 reviews)...</div>
+ <div dir="ltr"><bdi dir="ltr">[&#x05D0;]</bdi> (3 reviews)...</div>
+ <div dir="rtl"><bdi>[a]</bdi> (3)...</div>
+ <div dir="rtl"><bdi dir="ltr">[a]</bdi> (3)...</div>
+ <div dir="rtl"><bdi dir="rtl">[a]</bdi> (3)...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="ltr">&#x202D;[&#x05D0;] (3 reviews)...&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ <div dir="rtl">&#x202D;...(3) [a]&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2-ref.html
new file mode 100644
index 0000000000..d0f1097ade
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="Shai Berger" href="mailto:shai@platonix.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2.html
new file mode 100644
index 0000000000..62a3b50ffe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-number-following-2.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral to immediately following number</title>
+ <link rel="match" href="bdi-neutral-to-number-following-2-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'"/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the test's first DIV were a SPAN, the 1 would be rendered to the left
+ of the &#x05D0;
+ </div>
+ <div class="test">
+ <div dir="ltr"><bdi>&#x05D0;</bdi>1...</div>
+ <div dir="ltr"><bdi dir="rtl">&#x05D0;</bdi>1...</div>
+ <div dir="ltr"><bdi dir="ltr">&#x05D0;</bdi>1...</div>
+ <div dir="rtl"><bdi>a</bdi>1...</div>
+ <div dir="rtl"><bdi dir="ltr">a</bdi>1...</div>
+ <div dir="rtl"><bdi dir="rtl">a</bdi>1...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D0;1...&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ <div dir="rtl">&#x202D;...1a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run-ref.html
new file mode 100644
index 0000000000..d7967c77fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run.html
new file mode 100644
index 0000000000..bff339ec34
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-to-surrounding-run.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral to surrounding letters</title>
+ <link rel="match" href="bdi-neutral-to-surrounding-run-ref.html">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'
+ Thus, regardless of its content and its dir attribute (if any), a BDI will not prevent
+ a strongly RTL (or LTR) character preceding it from forming a single directional run with
+ another strongly RTL (LTR) character following it."/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDI in the test's first DIV were a SPAN, its b would prevent the &#x05D0; and the &#x05D1;
+ from forming a single RTL run and thus keep the &gt;s between from being mirrored into &lt;s.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x05D0; &gt; <bdi>[b]</bdi> &gt; &#x05D2;...</div>
+ <div dir="ltr">&#x05D0; &gt; <bdi dir="ltr">[b]</bdi> &gt; &#x05D2;...</div>
+ <div dir="ltr">&#x05D0; &gt; <bdi dir="rtl">[b]</bdi> &gt; &#x05D2;...</div>
+ <div dir="rtl">a &gt; <bdi>[&#x05D1;]</bdi> &gt; c...</div>
+ <div dir="rtl">a &gt; <bdi dir="ltr">[&#x05D1;]</bdi> &gt; c...</div>
+ <div dir="rtl">a &gt; <bdi dir="rtl">[&#x05D1;]</bdi> &gt; c...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="ltr">&#x202D;&#x05D2; &lt; [b] &lt; &#x05D0;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ <div dir="rtl">&#x202D;...a &gt; [&#x05D1;] &gt; c&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped-ref.html
new file mode 100644
index 0000000000..9859de4747
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ &#x202D;b &lt; &#x05D0;&#x202C;<br/>
+ &#x202D;&gt;&gt;&gt;&#x202C;<br/>
+ &#x202D;&#x05D3; &lt; c...&#x202C;
+ </div>
+ <div dir="rtl">
+ &#x202D;a &gt; &#x05D1;&#x202C;<br/>
+ &#x202D;&lt;&lt;&lt;&#x202C;<br/>
+ &#x202D;...&#x05D2; &gt; d&#x202C;
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ &#x202D;b &lt; &#x05D0;&#x202C;<br/>
+ &#x202D;&gt;&gt;&gt;&#x202C;<br/>
+ &#x202D;&#x05D3; &lt; c...&#x202C;
+ </div>
+ <div dir="rtl">
+ &#x202D;a &gt; &#x05D1;&#x202C;<br/>
+ &#x202D;&lt;&lt;&lt;&#x202C;<br/>
+ &#x202D;...&#x05D2; &gt; d&#x202C;
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped.html
new file mode 100644
index 0000000000..3e21fcb0fd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-neutral-wrapped.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: neutral when wrapped</title>
+ <link rel="match" href="bdi-neutral-wrapped-ref.html">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the paragraph-level
+ container that a bdi element finds itself within, the bdi element must be treated
+ like a U+FFFC OBJECT REPLACEMENT CHARACTER.'
+ This should hold even if the BDI's content is wrapped over more than one line."/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#xA0; - Non-breaking space.
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ In the test below, the non-breaking spaces in the BDI's middle "word" make it so long that it
+ must be displayed on a line of its own, with the BDI wrapped before and after it. At the same
+ time, the content surrounding the BDI is supposed to form a single directional run, despite
+ the containing element and the BDI both having the opposite direction, because the BDI must be
+ treated as a neutral. Thus, on the line containing the first part of the BDI, the BDI's
+ content must appear after the content preceding it, and on the line containing the last part
+ of the BDI, the BDI content must appear before the content following it, where both 'before'
+ and 'after' are defined relative to the surrounding directional run.
+ </div>
+ <div class="test">
+ <div dir="ltr">
+ &#x05D0; &gt;
+ <bdi>b
+&gt;&gt;&gt;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;
+ c</bdi>
+ &gt; &#x05D3;...
+ </div>
+ <div dir="rtl">
+ a &gt;
+ <bdi>&#x05D1;
+&gt;&gt;&gt;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;
+ &#x05D2;</bdi>
+ &gt; d...
+ </div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">
+ &#x202D;b &lt; &#x05D0;&#x202C;<br/>
+ &#x202D;&gt;&gt;&gt;&#x202C;<br/>
+ &#x202D;&#x05D3; &lt; c...&#x202C;
+ </div>
+ <div dir="rtl">
+ &#x202D;a &gt; &#x05D1;&#x202C;<br/>
+ &#x202D;&lt;&lt;&lt;&#x202C;<br/>
+ &#x202D;...&#x05D2; &gt; d&#x202C;
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container-ref.html
new file mode 100644
index 0000000000..0c74ecf68a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com">
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com">
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x202D;&#x05D0; [1 2 3 b] c [d &#x05D4;?!] &#x05D5;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...f [!?e &#x05D3;] &#x05D2; [&#x05D1; 3 2 1] a&#x202C;</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; [1 2 3 b] c [d &#x05D4;?!] &#x05D5;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...f [!?e &#x05D3;] &#x05D2; [&#x05D1; 3 2 1] a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container.html
new file mode 100644
index 0000000000..f133a95772
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdi-element/bdi-paragraph-level-container.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BDI: paragraph-level container</title>
+ <link rel="match" href="bdi-paragraph-level-container-ref.html"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdi-element"/>
+ <meta name="assert" content="
+ 'For the purposes of applying the bidirectional algorithm to the contents of a bdi element,
+ user agents must treat the element as a paragraph-level container.'
+ Thus, under no circumstances should the content outside a BDI affect the visual
+ ordering of the BDI's content."/>
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 500px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D5; - The first six Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ If the BDIs in the test's first DIV were just SPANs, the &#x05D0; would order the 1 2 3 as
+ 3 2 1, and the &#x05D5; would (with the &#x05D4;) order the ?! as !?.
+ </div>
+ <div class="test">
+ <div dir="ltr">&#x05D0; <bdi>[1 2 3 b]</bdi> c <bdi>[d &#x05D4;?!]</bdi> &#x05D5;...</div>
+ <div dir="rtl">a <bdi>[1 2 3 &#x05D1;]</bdi> &#x05D2; <bdi>[&#x05D3; e?!]</bdi> f...</div>
+ </div>
+ <div class="ref">
+ <div dir="ltr">&#x202D;&#x05D0; [1 2 3 b] c [d &#x05D4;?!] &#x05D5;...&#x202C;</div>
+ <div dir="rtl">&#x202D;...f [!?e &#x05D3;] &#x05D2; [&#x05D1; 3 2 1] a&#x202C;</div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-child.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-child.html
new file mode 100644
index 0000000000..feadc26d7b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-child.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: bdo - text directionality formatting control for its children</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdo-element">
+ <link rel="match" href="bidi-001-ref.html">
+ <meta name="assert" content="Check if the bdo element represents explicit text directionality formatting control for its children.">
+ </head>
+ <body>
+ <p>Test passes if there is text 'WERBEH'.</p>
+ <bdo dir="rtl">
+ <span>HEBREW</span>
+ </bdo>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-ltr.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-ltr.html
new file mode 100644
index 0000000000..8a7861086a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-ltr.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: The value 'ltr' of dir attribute specifies a left-to-right override</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdo-element">
+ <link rel="match" href="bidi-001-ref.html">
+ <meta name="assert" content="Check if the value ltr of dir attribute specifies a left-to-right override">
+ </head>
+ <body>
+ <p>Test passes if there is text 'WERBEH'.</p>
+ <bdo dir="ltr">WERBEH</bdo>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-override.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-override.html
new file mode 100644
index 0000000000..75a45e198f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bdo-override.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>HTML Test: bdo - override the Unicode bidirectional algorithm</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-bdo-element">
+ <link rel="match" href="bidi-001-ref.html">
+ <meta name="assert" content="Check if authors could override the Unicode bidirectional algorithm
+ by explicitly specifying a direction override of bdo element">
+ </head>
+ <body>
+ <p>Test passes if there is text 'WERBEH'.</p>
+ <p>
+ &#x202E;<bdo dir="ltr">WERBEH</bdo>&#x202C;
+ </p>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001-ref.html
new file mode 100644
index 0000000000..83d2dc4a16
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>directional type reference</title>
+</head>
+<body>
+<p>Test passes if there is text 'WERBEH'.</p>
+<div>WERBEH</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001.html
new file mode 100644
index 0000000000..772dcf43b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-bdo-element/bidi-001.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>directional type</title>
+<meta content="W3C" name="author">
+<link rel="match" href="bidi-001-ref.html">
+<meta name="assert" content="Test text bidirectionality using the bdo element">
+</head>
+<body dir='ltr'>
+<p>Test passes if there is text 'WERBEH'.</p>
+<bdo dir="rtl">HEBREW</bdo>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors-ref.html
new file mode 100644
index 0000000000..c2dd4daa7a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors-ref.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D8; - The first nine Hebrew letters (strongly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ &#x202D;&#x05D0;&#x05D1; &#x05D2;&#x05D3; 1. I like &#x05D4;. fg hi&#x202C;
+ <br/>
+ &#x202D;ab cd 2. &#x05D4; is great! &#x05D5;&#x05D6; &#x05D7;&#x05D8;&#x202C;
+ </div>
+ <div class="ref">
+ &#x202D;&#x05D0;&#x05D1; &#x05D2;&#x05D3; 1. I like &#x05D4;. fg hi&#x202C;
+ <br/>
+ &#x202D;ab cd 2. &#x05D4; is great! &#x05D5;&#x05D6; &#x05D7;&#x05D8;&#x202C;
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors.html
new file mode 100644
index 0000000000..89e7f2f1aa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-in-inline-ancestors.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML Test: BR in inline ancestors</title>
+ <link rel="match" href="br-bidi-in-inline-ancestors-ref.html">
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="author" title="HTML5 bidi test WG" href="mailto:html5bidi@googlegroups.com" />
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-br-element"/>
+ <link rel="help" href="http://www.w3.org/TR/css3-writing-modes/#unicode-bidi" />
+ <meta name="assert" content="
+ 'A br element should separate paragraphs for the purposes of the Unicode bidirectional
+ algorithm.'
+ 'If an inline element is broken around a bidi paragraph boundary (e.g. if split by a block or
+ forced paragraph break), then the bidi control codes corresponding to the end of the element
+ are added before the interruption and the codes corresponding to the start of the element are
+ added after it. (In other words, any embedding levels or overrides started by the element are
+ closed at the paragraph break and reopened on the other side of it.)'" />
+ <style>
+ body{
+ font-size:2em;
+ }
+ .test, .ref {
+ border: medium solid gray;
+ width: 400px;
+ margin: 20px;
+ }
+ .comments { display: none; }
+ </style>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
+ <div class="comments">
+ Key to entities used below:
+ &#x05D0; ... &#x05D8; - The first nine Hebrew letters (strongly RTL).
+ &#x200E; - The LRM (left-to-right mark) formatting character (invisible, stronly LTR).
+ &#x200F; - The RLM (right-to-left mark) formatting character (invisible, stronly RTL).
+ &#x202D; - The LRO (left-to-right override) formatting character.
+ &#x202C; - The PDF (pop directional formatting) formatting character; closes LRO.
+ </div>
+ <div class="test">
+ &#x05D1;&#x05D0;&#x200E;
+ <bdo dir="rtl">ih
+ <bdo dir="ltr">&#x05D2;&#x05D3;
+ <span dir="rtl">fg&#x200F;
+ <span dir="ltr">1. I like &#x05D4;.<br/>
+ 2. &#x05D4; is great!</span>
+ &#x200F;cd</span>
+ &#x05D5;&#x05D6;</bdo>
+ ba</bdo>
+ &#x200E;&#x05D8;&#x05D7;
+ </div>
+ <div class="ref">
+ &#x202D;&#x05D0;&#x05D1; &#x05D2;&#x05D3; 1. I like &#x05D4;. fg hi&#x202C;
+ <br/>
+ &#x202D;ab cd 2. &#x05D4; is great! &#x05D5;&#x05D6; &#x05D7;&#x05D8;&#x202C;
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-ref.html
new file mode 100644
index 0000000000..f07c077917
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML Test reference: BR separates bidi paragraph</title>
+ <link rel="author" title="Amir E. Aharoni" href="mailto:amir.aharoni@mail.huji.ac.il"/>
+ <link rel="author" title="Eyal Sela" href="mailto:eyal@post.isoc.org.il"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-br-element"/>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the rightmost character in the first line below is a full stop and to the left of it is a Hebrew letter.</p></div>
+ <div class="test">
+ A Hebrew letter and a full stop: &#x05d0;.&lrm;
+ <br />
+ &#x05d0; this line begins with a Hebrew letter.
+ </div>
+</p>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi.html
new file mode 100644
index 0000000000..1dfa6836f3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-br-element/br-bidi.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>HTML Test: BR separates bidi paragraph</title>
+ <link rel="match" href="br-bidi-ref.html">
+ <link rel="author" title="Amir E. Aharoni" href="mailto:amir.aharoni@mail.huji.ac.il"/>
+ <link rel="author" title="Eyal Sela" href="mailto:eyal@post.isoc.org.il"/>
+ <link rel="author" title="Aharon Lanin" href="mailto:aharon@google.com"/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-br-element"/>
+ <meta name="assert"
+ content="A br element should separate paragraphs for the purposes of the Unicode bidirectional algorithm."/>
+ </head>
+ <body>
+ <div class="instructions"><p>Test passes if the rightmost character in the first line below is a full stop and to the left of it is a Hebrew letter.</p></div>
+ <div class="test">
+ A Hebrew letter and a full stop: &#x05d0;.
+ <br />
+ &#x05d0; this line begins with a Hebrew letter.
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/rt-without-ruby-crash.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/rt-without-ruby-crash.html
new file mode 100644
index 0000000000..3caed3a02e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/rt-without-ruby-crash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=967255">
+<rt style="display:block;">
+ <div></div>
+</rt>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> { }, "No crash");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage-notref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage-notref.html
new file mode 100644
index 0000000000..f5747811ae
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage-notref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Reference File</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+
+<p>å›ãã‚“å­ã—ã¯å’Œã‚ã—ã¦åŒã©ã†ãœãš</p>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage.html
new file mode 100644
index 0000000000..59c076cd09
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-ruby-element/ruby-usage.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML test: ruby - mark phrasing content</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="mismatch" href="ruby-usage-notref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-ruby-element"/>
+
+<p><ruby>å›<rt>ãã‚“</ruby><ruby>å­<rt>ã—</ruby>ã¯<ruby>å’Œ<rt>ã‚</ruby>ã—ã¦<ruby>åŒ<rt>ã©ã†</ruby>ãœãš</p>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-time-element/001.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-time-element/001.html
new file mode 100644
index 0000000000..e1cd0480ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-time-element/001.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title>HTML time element API</title>
+ <style>
+#time { visibility: hidden; }
+ </style>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-time-element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <!-- intentionally nested to test parsing rules -->
+ <p id="time"><time pubdate datetime="2000-02-01T03:04:05Z">Dummy text <time>2001-06-07T<time>08:09<time></time></time>Z</time></time></p>
+ <script type="text/javascript">
+function makeTime(dateTime,contents,dateTimeProp) {
+ var timeEl = document.createElement('time');
+ if( dateTime ) {
+ timeEl.setAttribute('datetime',dateTime);
+ }
+ if( contents ) {
+ timeEl.innerHTML = contents;
+ }
+ if( dateTimeProp ) {
+ timeEl.dateTime = dateTimeProp;
+ }
+ return timeEl;
+}
+
+var timep = document.getElementById('time');
+var times = timep.getElementsByTagName('time');
+
+//TIME elements
+test(function () {
+ assert_equals( times.length, 4 );
+}, 'HTML parsing should locate 4 time elements in this document');
+test(function () {
+ assert_true( !!window.HTMLTimeElement );
+}, 'HTMLTimeElement should be exposed for prototyping');
+test(function () {
+ assert_true( makeTime() instanceof window.HTMLTimeElement, 'createElement variant' );
+ assert_true( times[0] instanceof window.HTMLTimeElement, 'HTML parsing variant' );
+}, 'the time elements should be instanceof HTMLTimeElement');
+
+//dateTime
+test(function () {
+ assert_equals( makeTime('2000-02-01T03:04:05Z','2001-02-01T03:04:05Z').dateTime, '2000-02-01T03:04:05Z' );
+}, 'the datetime attribute should be reflected by the .dateTime property');
+test(function () {
+ assert_equals( typeof makeTime().dateTime, 'string', 'typeof test' );
+ assert_equals( makeTime().dateTime, '', 'value test' );
+}, 'the dateTime IDL property should default to an empty string');
+test(function () {
+ assert_equals( makeTime(false,false,'2000-02-01T03:04:05Z').dateTime, '2000-02-01T03:04:05Z' );
+}, 'the dateTime property should be read/write');
+test(function () {
+ assert_equals( makeTime('go fish').dateTime, 'go fish' );
+}, 'the datetime attribute should be reflected by the .dateTime property even if it is invalid');
+test(function () {
+ assert_equals( makeTime(false,'2000-02-01T03:04:05Z').dateTime, '' );
+}, 'the datetime attribute should not reflect the textContent');
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element-ref.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element-ref.html
new file mode 100644
index 0000000000..bedb6d62e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>The wbr element</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<meta name="flags" content="ahem">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+ p {font:15px/1 Ahem;}
+</style>
+<p>Loremipsumdolorsit<br>amet,consectetur<br>adipisicingelit,sed<br>doeiusmodtempor<br>incididuntutlaboreet<br>doloremagnaaliqua.Ut<br>enimadminimveniam,<br>quisnostrud<br>exercitationullamco<br>laborisnisiutaliquip<br>exeacommodo<br>consequat.Duisaute<br>iruredolorin<br>reprehenderitin<br>voluptatevelitesse<br>cillumdoloreeufugiat<br>nullapariatur.<br>Excepteursint<br>occaecatcupidatatnon<br>proident,suntinculpa<br>quiofficiadeserunt<br>mollitanimidest<br>laborum.</p>
diff --git a/testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element.html b/testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element.html
new file mode 100644
index 0000000000..aa3912028e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/text-level-semantics/the-wbr-element/wbr-element.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>The wbr element</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#rendering">
+<link rel="match" href="wbr-element-ref.html">
+<meta name="flags" content="ahem">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+ p {max-width: 300px; font:15px/1 Ahem;}
+</style>
+<p>AHEM_<wbr>ipsum<wbr>dolor<wbr>sit<wbr>amet,<wbr>consectetur<wbr>adipisicing<wbr>elit,<wbr>sed<wbr>do<wbr>eiusmod<wbr>tempor<wbr>incididunt<wbr>ut<wbr>labore<wbr>et<wbr>dolore<wbr>magna<wbr>aliqua.<wbr>Ut<wbr>enim<wbr>ad<wbr>minim<wbr>veniam,<wbr>quis<wbr>nostrud<wbr>exercitation<wbr>ullamco<wbr>laboris<wbr>nisi<wbr>ut<wbr>aliquip<wbr>ex<wbr>ea<wbr>commodo<wbr>consequat.<wbr>Duis<wbr>aute<wbr>irure<wbr>dolor<wbr>in<wbr>reprehenderit<wbr>in<wbr>voluptate<wbr>velit<wbr>esse<wbr>cillum<wbr>dolore<wbr>eu<wbr>fugiat<wbr>nulla<wbr>pariatur.<wbr>Excepteur<wbr>sint<wbr>occaecat<wbr>cupidatat<wbr>non<wbr>proident,<wbr>sunt<wbr>in<wbr>culpa<wbr>qui<wbr>officia<wbr>deserunt<wbr>mollit<wbr>anim<wbr>id<wbr>est<wbr>laborum.</p>
diff --git a/testing/web-platform/tests/html/semantics/the-link-element/attr-link-fetchpriority.html b/testing/web-platform/tests/html/semantics/the-link-element/attr-link-fetchpriority.html
new file mode 100644
index 0000000000..8aa515fa4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/the-link-element/attr-link-fetchpriority.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Fetch Priority - Link element</title>
+<meta name="author" title="Dominic Farolino" href="mailto:domfarolino@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<link id=link1 href=resources/stylesheet.css fetchpriority=high>
+<link id=link2 href=resources/stylesheet.css fetchpriority=low>
+<link id=link3 href=resources/stylesheet.css fetchpriority=auto>
+<link id=link4 href=resources/stylesheet.css fetchpriority=xyz>
+<link id=link5 href=resources/stylesheet.css>
+
+<script>
+ test(() => {
+ assert_equals(link1.fetchPriority, "high", "high fetchPriority is a valid IDL value on the link element");
+ assert_equals(link2.fetchPriority, "low", "low fetchPriority is a valid IDL value on the link element");
+ assert_equals(link3.fetchPriority, "auto", "auto fetchPriority is a valid IDL value on the link element");
+ assert_equals(link4.fetchPriority, "auto", "invalid fetchPriority reflects as 'auto' IDL attribute on the link element");
+ assert_equals(link5.fetchPriority, "auto", "missing fetchPriority reflects as 'auto' IDL attribute on the link element");
+ }, "fetchpriority attribute on <link> elements should reflect valid IDL values");
+
+ test(() => {
+ const link = document.createElement("link");
+ assert_equals(link.fetchPriority, "auto");
+ }, "default fetchpriority attribute on <link> elements should be 'auto'");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/the-link-element/resources/stylesheet.css b/testing/web-platform/tests/html/semantics/the-link-element/resources/stylesheet.css
new file mode 100644
index 0000000000..9d9d772fb4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/the-link-element/resources/stylesheet.css
@@ -0,0 +1,3 @@
+body {
+ background-color: green;
+}
diff --git a/testing/web-platform/tests/html/syntax/charset/README.md b/testing/web-platform/tests/html/syntax/charset/README.md
new file mode 100644
index 0000000000..eaa708507c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/README.md
@@ -0,0 +1,6 @@
+These tests don't necessarily match the spec as of September 2021 but instead
+these tests test the behavior that on one hand WebKit and Blink and on the
+other hand https://github.com/whatwg/html/issues/6962 have in common.
+
+The cases where the two approaches differ are in
+https://searchfox.org/mozilla-central/source/testing/web-platform/mozilla/tests/html/syntax/charset
diff --git a/testing/web-platform/tests/html/syntax/charset/after-1kb.html b/testing/web-platform/tests/html/syntax/charset/after-1kb.html
new file mode 100644
index 0000000000..01eedca187
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/after-1kb.html
@@ -0,0 +1,955 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/after-1kb-ref.html">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<meta charset="windows-1251">
+</head>
+<body>
+<p>In <code>head</code>, after first kilobyte.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/after-bogus-after-1kb.html b/testing/web-platform/tests/html/syntax/charset/after-bogus-after-1kb.html
new file mode 100644
index 0000000000..f44fda1e6d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/after-bogus-after-1kb.html
@@ -0,0 +1,933 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/after-bogus-after-1kb-ref.html">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<bogus><meta charset="windows-1251">
+</head>
+<body>
+<p>After <code>bogus</code>, before <code>head</code> end tag, after first kilobyte.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/after-bogus.html b/testing/web-platform/tests/html/syntax/charset/after-bogus.html
new file mode 100644
index 0000000000..7eeedde7af
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/after-bogus.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/after-bogus-ref.html">
+<bogus><meta charset="windows-1251">
+</head>
+<body>
+<p>Meta after <code>bogus</code>.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/after-head-after-1kb-crlf.html b/testing/web-platform/tests/html/syntax/charset/after-head-after-1kb-crlf.html
new file mode 100644
index 0000000000..10a6d1c3d7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/after-head-after-1kb-crlf.html
@@ -0,0 +1,927 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/after-head-after-1kb-crlf-ref.html">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+</head>
+<meta charset="windows-1251">
+<body>
+<p>After <code>head</code>, before <code>body</code>, after first kilobyte, with a CRLF in the first kilobyte.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/after-head-after-1kb.html b/testing/web-platform/tests/html/syntax/charset/after-head-after-1kb.html
new file mode 100644
index 0000000000..d514fdfbff
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/after-head-after-1kb.html
@@ -0,0 +1,933 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/after-head-after-1kb-ref.html">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+</head>
+<meta charset="windows-1251">
+<body>
+<p>After <code>head</code>, before <code>body</code>, after first kilobyte.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/after-head-in-1kb-crlf.html b/testing/web-platform/tests/html/syntax/charset/after-head-in-1kb-crlf.html
new file mode 100644
index 0000000000..e5c95e7036
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/after-head-in-1kb-crlf.html
@@ -0,0 +1,932 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/after-head-in-1kb-crlf-ref.html">
+</head>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<meta charset="windows-1251">
+<body>
+<p>After <code>head</code>, before <code>body</code>, within first kilobyte, with a CRLF in the first kilobyte.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/after-head-in-1kb.html b/testing/web-platform/tests/html/syntax/charset/after-head-in-1kb.html
new file mode 100644
index 0000000000..e73522d373
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/after-head-in-1kb.html
@@ -0,0 +1,938 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/after-head-in-1kb-ref.html">
+</head>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<meta charset="windows-1251">
+<body>
+<p>After <code>head</code>, before <code>body</code>, within first kilobyte.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/baseline.html b/testing/web-platform/tests/html/syntax/charset/baseline.html
new file mode 100644
index 0000000000..40bce2f63f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/baseline.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/baseline-ref.html">
+<meta charset="windows-1251">
+</head>
+<body>
+<p>Normal meta.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/document-write.html b/testing/web-platform/tests/html/syntax/charset/document-write.html
new file mode 100644
index 0000000000..b70a15c567
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/document-write.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/document-write-ref.html">
+<script>document.write('<meta char' + 'set="windows-1251">');</script>
+</head>
+<body>
+<p>Meta from <code>document.write</code> (with concatenation in the middle of <code>charset</code> to require execution for effect).</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-comment.html b/testing/web-platform/tests/html/syntax/charset/in-comment.html
new file mode 100644
index 0000000000..65828a1872
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-comment.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/in-comment-ref.html">
+<!--<meta charset="windows-1251">-->
+</head>
+<body>
+<p>Meta inside comment.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-noscript-after-template-after-1kb.html b/testing/web-platform/tests/html/syntax/charset/in-noscript-after-template-after-1kb.html
new file mode 100644
index 0000000000..d22e83aaa7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-noscript-after-template-after-1kb.html
@@ -0,0 +1,894 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/in-noscript-after-template-after-1kb-ref.html">
+<noscript><template></template>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<meta charset="windows-1251"></noscript>
+</head>
+<body>
+<p>Meta in <code>noscript</code> after <code>template</code> (which is also inside the <code>noscript</code>) after 1kb of padding following the template.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-object.html b/testing/web-platform/tests/html/syntax/charset/in-object.html
new file mode 100644
index 0000000000..32535b8eba
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-object.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/in-object-ref.html">
+<object><meta charset="windows-1251"></object>
+</head>
+<body>
+<p>Meta in <code>object</code>.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-script.html b/testing/web-platform/tests/html/syntax/charset/in-script.html
new file mode 100644
index 0000000000..0c18a4435e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-script.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/in-script-ref.html">
+<script><meta charset="windows-1251"></script>
+</head>
+<body>
+<p>Meta in <code>script</code>.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-style.html b/testing/web-platform/tests/html/syntax/charset/in-style.html
new file mode 100644
index 0000000000..69d8fa429c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-style.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/in-style-ref.html">
+<style><meta charset="windows-1251"></style>
+</head>
+<body>
+<p>Meta in <code>style</code>.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-svg-in-cdata.html b/testing/web-platform/tests/html/syntax/charset/in-svg-in-cdata.html
new file mode 100644
index 0000000000..d1c4ca12b3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-svg-in-cdata.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/in-svg-in-cdata-ref.html">
+</head>
+<body>
+<svg><![CDATA[<meta charset="windows-1251">]]></svg>
+<p>In SVG in CDATA (after head).</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-svg.html b/testing/web-platform/tests/html/syntax/charset/in-svg.html
new file mode 100644
index 0000000000..cb29164289
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-svg.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/in-svg-ref.html">
+</head>
+<body>
+<svg><meta charset="windows-1251"></svg>
+<p>In SVG (after head).</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-template-after-1kb.html b/testing/web-platform/tests/html/syntax/charset/in-template-after-1kb.html
new file mode 100644
index 0000000000..ae77decea2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-template-after-1kb.html
@@ -0,0 +1,1046 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/in-template-after-1kb-ref.html">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<template><meta charset="windows-1251"></template>
+</head>
+<body>
+<p>In <code>template</code>, before <code>head</code> end tag, after first kilobyte.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-template.html b/testing/web-platform/tests/html/syntax/charset/in-template.html
new file mode 100644
index 0000000000..264affc269
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-template.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/in-template-ref.html">
+<template><meta charset="windows-1251"></template>
+</head>
+<body>
+<p>Meta in <code>template</code>.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/in-title.html b/testing/web-platform/tests/html/syntax/charset/in-title.html
new file mode 100644
index 0000000000..7b72c48dd8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/in-title.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="mismatch" href="references/in-title-ref.html">
+<title><meta charset="windows-1251"></title>
+</head>
+<body>
+<p>Meta in <code>title</code>.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta-utf-8.html b/testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta-utf-8.html
new file mode 100644
index 0000000000..fb9e82a1f6
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta-utf-8.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset="utf-8" />
+<title>Inheriting from UTF-8 parent</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+[
+ {
+ "title": "Child with bogus <meta charset>",
+ "url": "resources/bogus-charset.html",
+ "expected": "\uFFFD\n" // 0x00A2 in windows-1252 is 0xFFFD in UTF-8
+ },
+ {
+ "title": "Child with bogus Content-Type charset",
+ "url": "resources/bogus-charset-http.py",
+ "expected": "\uFFFD\n"
+ },
+ {
+ "title": "Child with bogus Content-Type charset, but valid <meta charset>",
+ "url": "resources/bogus-charset-http-valid-meta.py",
+ "expected": "\u045E\n"
+ }
+].forEach(({ title, url, expected }) => {
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = url;
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.body.textContent, expected);
+ });
+ document.body.append(frame);
+ }, title);
+});
+</script>
diff --git a/testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta.html b/testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta.html
new file mode 100644
index 0000000000..616ba51853
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/inheritance-bogus-meta.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset=windows-1253>
+<title>Inheriting from windows-1253 parent</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<div id=log></div>
+<script>
+[
+ {
+ "title": "Child with bogus <meta charset>",
+ "url": "resources/bogus-charset.html",
+ "expected": "¢\n" // 0x00A2 in windows-1252 is at the same position as 0x0386 in windows-1253
+ },
+ {
+ "title": "Child with bogus Content-Type charset",
+ "url": "resources/bogus-charset-http.py",
+ "expected": "¢\n"
+ },
+ {
+ "title": "Child with bogus Content-Type charset, but valid <meta charset>",
+ "url": "resources/bogus-charset-http-valid-meta.py",
+ "expected": "\u045E\n"
+ }
+].forEach(({ title, url, expected }) => {
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = url;
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.body.textContent, expected);
+ });
+ document.body.append(frame);
+ }, title);
+});
+
+async_test(t => {
+ self.onmessage = t.step_func_done(({ data }) => {
+ assert_equals(data, "\u00A2\n");
+ });
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = get_host_info().HTTP_REMOTE_ORIGIN + new URL("resources/bogus-charset.html", location).pathname + "?postMessage";
+ document.body.append(frame);
+}, "Cross-origin child with bogus <meta charset>");
+</script>
diff --git a/testing/web-platform/tests/html/syntax/charset/ncr.html b/testing/web-platform/tests/html/syntax/charset/ncr.html
new file mode 100644
index 0000000000..9f8b7308ad
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/ncr.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/ncr-ref.html">
+<meta charset="&#119;indows-1251">
+</head>
+<body>
+<p>NCR in encoding label.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/non-ascii-in-comment-before.html b/testing/web-platform/tests/html/syntax/charset/non-ascii-in-comment-before.html
new file mode 100644
index 0000000000..148c6544a3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/non-ascii-in-comment-before.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/non-ascii-in-comment-before-ref.html">
+<!-- æ -->
+<meta charset="windows-1251">
+</head>
+<body>
+<p>Normal meta. Non-ASCII in comment before.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/non-ascii-in-title-before.html b/testing/web-platform/tests/html/syntax/charset/non-ascii-in-title-before.html
new file mode 100644
index 0000000000..aac0ca94c1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/non-ascii-in-title-before.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<head>
+<link rel="match" href="references/non-ascii-in-title-before-ref.html">
+<title>æ</title>
+<meta charset="windows-1251">
+</head>
+<body>
+<p>Normal meta. Non-ASCII in title before.</p>
+<p>Test: æ</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/after-1kb-ref.html b/testing/web-platform/tests/html/syntax/charset/references/after-1kb-ref.html
new file mode 100644
index 0000000000..edb0ae9c72
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/after-1kb-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>In <code>head</code>, after first kilobyte.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/after-bogus-after-1kb-ref.html b/testing/web-platform/tests/html/syntax/charset/references/after-bogus-after-1kb-ref.html
new file mode 100644
index 0000000000..c6f129a89e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/after-bogus-after-1kb-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>After <code>bogus</code>, before <code>head</code> end tag, after first kilobyte.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/after-bogus-ref.html b/testing/web-platform/tests/html/syntax/charset/references/after-bogus-ref.html
new file mode 100644
index 0000000000..f8080e60aa
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/after-bogus-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta after <code>bogus</code>.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-crlf-ref.html b/testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-crlf-ref.html
new file mode 100644
index 0000000000..868a9639bf
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-crlf-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>After <code>head</code>, before <code>body</code>, after first kilobyte, with a CRLF in the first kilobyte.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-ref.html b/testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-ref.html
new file mode 100644
index 0000000000..bde4745b26
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/after-head-after-1kb-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>After <code>head</code>, before <code>body</code>, after first kilobyte.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-crlf-ref.html b/testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-crlf-ref.html
new file mode 100644
index 0000000000..0676d7374e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-crlf-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>After <code>head</code>, before <code>body</code>, within first kilobyte, with a CRLF in the first kilobyte.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-ref.html b/testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-ref.html
new file mode 100644
index 0000000000..75fc2dd618
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/after-head-in-1kb-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>After <code>head</code>, before <code>body</code>, within first kilobyte.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/baseline-ref.html b/testing/web-platform/tests/html/syntax/charset/references/baseline-ref.html
new file mode 100644
index 0000000000..21b939efe0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/baseline-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Normal meta.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/document-write-ref.html b/testing/web-platform/tests/html/syntax/charset/references/document-write-ref.html
new file mode 100644
index 0000000000..9902cf77d1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/document-write-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta from <code>document.write</code> (with concatenation in the middle of <code>charset</code> to require execution for effect).</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-comment-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-comment-ref.html
new file mode 100644
index 0000000000..94b2016e0d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-comment-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta inside comment.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-noscript-after-template-after-1kb-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-noscript-after-template-after-1kb-ref.html
new file mode 100644
index 0000000000..12e9b93626
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-noscript-after-template-after-1kb-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta in <code>noscript</code> after <code>template</code> (which is also inside the <code>noscript</code>) after 1kb of padding following the template.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-object-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-object-ref.html
new file mode 100644
index 0000000000..3f52d0efe9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-object-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta in <code>object</code>.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-script-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-script-ref.html
new file mode 100644
index 0000000000..bbb63fd931
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-script-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta in <code>script</code>.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-style-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-style-ref.html
new file mode 100644
index 0000000000..9669146eb5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-style-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta in <code>style</code>.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-svg-in-cdata-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-svg-in-cdata-ref.html
new file mode 100644
index 0000000000..1d17d2720b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-svg-in-cdata-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<svg></svg>
+<p>In SVG in CDATA (after head).</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-svg-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-svg-ref.html
new file mode 100644
index 0000000000..c9e41aa177
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-svg-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<svg></svg>
+<p>In SVG (after head).</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-template-after-1kb-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-template-after-1kb-ref.html
new file mode 100644
index 0000000000..df20eba39b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-template-after-1kb-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<head>
+</head>
+<body>
+<p>In <code>template</code>, before <code>head</code> end tag, after first kilobyte.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-template-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-template-ref.html
new file mode 100644
index 0000000000..83c0e93072
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-template-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Meta in <code>template</code>.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/in-title-ref.html b/testing/web-platform/tests/html/syntax/charset/references/in-title-ref.html
new file mode 100644
index 0000000000..5fb8a05f2d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/in-title-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<title><meta charset="windows-1251"></title>
+</head>
+<body>
+<p>Meta in <code>title</code>.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/ncr-ref.html b/testing/web-platform/tests/html/syntax/charset/references/ncr-ref.html
new file mode 100644
index 0000000000..1d25b4d23d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/ncr-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>NCR in encoding label.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-comment-before-ref.html b/testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-comment-before-ref.html
new file mode 100644
index 0000000000..bd2acd274c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-comment-before-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<p>Normal meta. Non-ASCII in comment before.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-title-before-ref.html b/testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-title-before-ref.html
new file mode 100644
index 0000000000..4f54732432
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/references/non-ascii-in-title-before-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<title>ж</title>
+</head>
+<body>
+<p>Normal meta. Non-ASCII in title before.</p>
+<p>Test: ж</p>
+<p>If &#x0436;, meta takes effect</p>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http-valid-meta.py b/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http-valid-meta.py
new file mode 100644
index 0000000000..f988bc9f8c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http-valid-meta.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ response.headers.set(b"Content-Type", b"text/html;charset=this-is-not-a-charset")
+ # ¢
+ response.content = b"<meta charset=windows-1251>\xA2\n"
diff --git a/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http.py b/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http.py
new file mode 100644
index 0000000000..1b881a1681
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset-http.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ response.headers.set(b"Content-Type", b"text/html;charset=this-is-not-a-charset")
+ # ¢
+ response.content = b"\xA2\n"
diff --git a/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset.html b/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset.html
new file mode 100644
index 0000000000..e77e874466
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/resources/bogus-charset.html
@@ -0,0 +1,7 @@
+<!doctype html><meta charset=this-is-not-a-charset><script>
+onload = () => {
+ if (location.search === "?postMessage") {
+ parent.postMessage(document.body.textContent, "*");
+ }
+}
+</script>¢
diff --git a/testing/web-platform/tests/html/syntax/charset/with-inheritance.html b/testing/web-platform/tests/html/syntax/charset/with-inheritance.html
new file mode 100644
index 0000000000..6ed3f9ec7a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/with-inheritance.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<meta charset="windows-1253">
+<title>meta charset in iframes with windows-1253 inhering from parent</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<script>
+setup({explicit_done:true});
+window.onload = function() {
+ runAllTests();
+ done();
+};
+
+function runAllTests() {
+ const prefix = "Test: ";
+ let iframes = document.getElementsByTagName("iframe");
+ for (let i = 0; i < iframes.length; ++i) {
+ let iframe = iframes[i];
+ test(function() {
+ let doc = iframe.contentWindow.document;
+ let link = doc.getElementsByTagName("link")[0];
+ let match = (link.rel == "match");
+ let content = doc.documentElement.textContent;
+ let index = content.indexOf(prefix);
+ let c = content.substring(index + prefix.length, index + prefix.length + 1);
+ if (match) {
+ assert_equals(doc.characterSet, "windows-1251", 'Check characterSet');
+ assert_equals(c, "\u0436", 'Check character');
+ } else {
+ assert_equals(doc.characterSet, "windows-1253", 'Check characterSet');
+ assert_equals(c, "\u03B6", 'Check character');
+ }
+ }, "Check " + iframe.src);
+ }
+}
+
+</script>
+
+<iframe src="after-1kb.html"></iframe>
+<iframe src="after-bogus-after-1kb.html"></iframe>
+<iframe src="after-bogus.html"></iframe>
+<iframe src="after-head-after-1kb-crlf.html"></iframe>
+<iframe src="after-head-after-1kb.html"></iframe>
+<iframe src="after-head-in-1kb-crlf.html"></iframe>
+<iframe src="after-head-in-1kb.html"></iframe>
+<iframe src="baseline.html"></iframe>
+<iframe src="document-write.html"></iframe>
+<iframe src="in-comment.html"></iframe>
+<iframe src="in-noscript-after-template-after-1kb.html"></iframe>
+<iframe src="in-object.html"></iframe>
+<iframe src="in-script.html"></iframe>
+<iframe src="in-style.html"></iframe>
+<iframe src="in-svg.html"></iframe>
+<iframe src="in-svg-in-cdata.html"></iframe>
+<iframe src="in-template-after-1kb.html"></iframe>
+<iframe src="in-template.html"></iframe>
+<iframe src="in-title.html"></iframe>
+<iframe src="ncr.html"></iframe>
+<iframe src="non-ascii-in-comment-before.html"></iframe>
+<iframe src="non-ascii-in-title-before.html"></iframe>
diff --git a/testing/web-platform/tests/html/syntax/charset/without-inheritance.html b/testing/web-platform/tests/html/syntax/charset/without-inheritance.html
new file mode 100644
index 0000000000..d05945720a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/without-inheritance.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/charset/xhr.html b/testing/web-platform/tests/html/syntax/charset/xhr.html
new file mode 100644
index 0000000000..70af7e720c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/charset/xhr.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<meta charset="windows-1253">
+<title>meta charset in XHR</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<script>
+setup({explicit_done:true});
+window.onload = function() {
+ runNextTest();
+};
+
+let files = [
+ "after-1kb.html",
+ "after-bogus-after-1kb.html",
+ "after-bogus.html",
+ "after-head-after-1kb-crlf.html",
+ "after-head-after-1kb.html",
+ "after-head-in-1kb-crlf.html",
+ "after-head-in-1kb.html",
+ "baseline.html",
+ "document-write.html",
+ "in-comment.html",
+ "in-noscript-after-template-after-1kb.html",
+ "in-object.html",
+ "in-script.html",
+ "in-style.html",
+ "in-svg.html",
+ "in-svg-in-cdata.html",
+ "in-template-after-1kb.html",
+ "in-template.html",
+ "in-title.html",
+ "ncr.html",
+ "non-ascii-in-comment-before.html",
+ "non-ascii-in-title-before.html",
+];
+
+function handler() {
+ const prefix = "Test: ";
+ let doc = this.response;
+ test(function() {
+ let link = doc.getElementsByTagName("link")[0];
+ let match = (link.rel == "match");
+ let content = doc.documentElement.textContent;
+ let index = content.indexOf(prefix);
+ let c = content.substring(index + prefix.length, index + prefix.length + 1);
+ if (match) {
+ assert_equals(doc.characterSet, "windows-1251", 'Check characterSet');
+ assert_equals(c, "\u0436", 'Check character');
+ } else {
+ assert_equals(doc.characterSet, "UTF-8", 'Check characterSet');
+ assert_equals(c, "\uFFFD", 'Check character');
+ }
+ }, "Check " + this.thesrc);
+ runNextTest();
+}
+
+function runNextTest() {
+ let file = files.shift();
+ if (!file) {
+ done();
+ return;
+ }
+ let xhr = new XMLHttpRequest();
+ xhr.responseType = "document";
+ xhr.onload = handler;
+ xhr.thesrc = file; // expando
+ xhr.open("GET", file);
+ xhr.send();
+}
+
+</script>
+
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/innerHTML-setter-default-namespace.xhtml b/testing/web-platform/tests/html/syntax/parsing-html-fragments/innerHTML-setter-default-namespace.xhtml
new file mode 100644
index 0000000000..19f17f1e28
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/innerHTML-setter-default-namespace.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<span xmlns="someNamespace" xmlns:html="http://www.w3.org/1999/xhtml">
+ <html:span id="target"/>
+</span>
+<script>
+<![CDATA[
+
+test(() => {
+ const element = document.getElementById("target");
+ element.innerHTML = '<b /><html:b />';
+ assert_equals(element.firstChild.prefix, null);
+ assert_equals(element.firstChild.namespaceURI, "someNamespace");
+ assert_equals(element.lastChild.prefix, 'html');
+ assert_equals(element.lastChild.namespaceURI, "http://www.w3.org/1999/xhtml");
+}, "Setting innerHTML on a HTML element with a non-HTML namespace as the default namespace");
+
+test(() => {
+ const element = document.getElementById("target");
+ element.outerHTML = '<b /><html:b />';
+ assert_equals(element.firstChild.prefix, null);
+ assert_equals(element.firstChild.namespaceURI, "someNamespace");
+ assert_equals(element.lastChild.prefix, 'html');
+ assert_equals(element.lastChild.namespaceURI, "http://www.w3.org/1999/xhtml");
+}, "Setting outerHTML on a HTML element with a non-HTML namespace as the default namespace");
+
+]]>
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-1.css b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-1.css
new file mode 100644
index 0000000000..956ec70e71
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-1.css
@@ -0,0 +1,4 @@
+@charset "utf-8";
+.test div.ýäè {
+ width: 100px;
+}
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15-inverse.css b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15-inverse.css
new file mode 100644
index 0000000000..4a02854197
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15-inverse.css
@@ -0,0 +1,4 @@
+@charset "utf-8";
+.test div#box.ÜÀÚ {
+ width: 100px;
+}
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15.css b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15.css
new file mode 100644
index 0000000000..ec907a1a94
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-15.css
@@ -0,0 +1,4 @@
+@charset "utf-8";
+.test div.ÜÀÚ {
+ width: 100px;
+}
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-utf8.css b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-utf8.css
new file mode 100644
index 0000000000..3fa2d5f475
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/support/encodingtests-utf8.css
@@ -0,0 +1,4 @@
+@charset "utf-8";
+.test div.ýäè {
+ width: 100px;
+}
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html
new file mode 100644
index 0000000000..a85682adf9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <title>HTTP charset</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-15.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
+
+The only character encoding declaration for this HTML file is in the HTTP header, which sets the encoding to ISO 8859-15.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The character encoding of a page can be set using the HTTP header charset declaration.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html.headers
new file mode 100644
index 0000000000..3d9718c07b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-001.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=iso-8859-15
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-003.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-003.html
new file mode 100644
index 0000000000..5b46a7f0f0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-003.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-004.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-004.html
new file mode 100644
index 0000000000..571d5bf8e8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-004.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html
new file mode 100644
index 0000000000..03dd532b38
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>meta content attribute</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-15.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The only character encoding declaration for this HTML file is in the content attribute of the meta element, which declares the encoding to be ISO 8859-15.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The character encoding of the page can be set by a meta element with http-equiv and content attributes.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-007.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html
new file mode 100644
index 0000000000..1383292832
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta charset="iso-8859-15"> <title>meta charset attribute</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-15.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The only character encoding declaration for this HTML file is in the charset attribute of the meta element, which declares the encoding to be ISO 8859-15.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The character encoding of the page can be set by a meta element with charset attribute.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-009.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html
new file mode 100644
index 0000000000..141ca3e56f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta http-equiv="content-type" content="text/html;charset=iso-8859-1" > <title>HTTP vs meta content</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }.test div { width: 90px; }
+</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-15.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-1.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html.headers
new file mode 100644
index 0000000000..3d9718c07b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-016.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=iso-8859-15
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html
new file mode 100644
index 0000000000..9ee7510e5a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta charset="iso-8859-1" > <title>HTTP vs meta charset</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }.test div { width: 90px; }
+</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-15.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-1.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html.headers
new file mode 100644
index 0000000000..3d9718c07b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-018.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=iso-8859-15
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html
new file mode 100644
index 0000000000..5f85199928
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta charset="iso-8859-15" ><meta http-equiv="content-type" content="text/html;charset=iso-8859-1" > <title>meta charset, then meta content</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }.test div { width: 90px; }
+</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-15.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-15, followed by a meta content attribute that tries to set the encoding to ISO 8859-1.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "An encoding declaration in a meta charset attribute has a higher precedence than a following encoding declaration in a meta charset attribute.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-030.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html
new file mode 100644
index 0000000000..f0b699792e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <title>HTTP vs UTF-8 BOM</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-utf8.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The HTTP header attempts to set the character encoding to ISO 8859-15. The page starts with a UTF-8 signature.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.
+
+If the test is unsuccessful, the characters &#x00EF;&#x00BB;&#x00BF; should appear at the top of the page. These represent the bytes that make up the UTF-8 signature when encountered in the ISO 8859-15 encoding.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html.headers
new file mode 100644
index 0000000000..3d9718c07b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-034.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=iso-8859-15
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html
new file mode 100644
index 0000000000..1fcf157b23
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>UTF-8 BOM vs meta content</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-utf8.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-037.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html
new file mode 100644
index 0000000000..9432113a25
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta charset="iso-8859-15"> <title>UTF-8 BOM vs meta charset</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/#the-input-byte-stream'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<style type='text/css'>
+.test div { width: 50px; }.test div { width: 90px; }
+</style>
+<link rel="stylesheet" type="text/css" href="support/encodingtests-utf8.css">
+</head>
+<body>
+
+
+
+<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
+
+
+<!--Notes:
+
+The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.
+
+The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.
+
+-->
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, "A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html.headers b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/the-input-byte-stream-038.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/html/syntax/parsing-html-fragments/tokenizer-modes-001.html b/testing/web-platform/tests/html/syntax/parsing-html-fragments/tokenizer-modes-001.html
new file mode 100644
index 0000000000..d283274214
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing-html-fragments/tokenizer-modes-001.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <title>Tokenizer states</title>
+<link rel='author' title='Henri Sivonen' href='mailto:hsivonen@hsivonen.fi'>
+<link rel='help' href='https://html.spec.whatwg.org/#html-fragment-parsing-algorithm'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+test(function() {
+ var e = document.createElement("title");
+ e.innerHTML = "</title><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "&lt;/title&gt;&lt;div&gt;");
+}, "</title> should not break out of title.");
+
+test(function() {
+ var e = document.createElement("textarea");
+ e.innerHTML = "</textarea><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "&lt;/textarea&gt;&lt;div&gt;");
+}, "</textarea> should not break out of textarea.");
+
+test(function() {
+ var e = document.createElement("style");
+ e.innerHTML = "</style><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "</style><div>");
+}, "</style> should not break out of style.");
+
+test(function() {
+ var e = document.createElement("xmp");
+ e.innerHTML = "</xmp><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "</xmp><div>");
+}, "</xmp> should not break out of xmp.");
+
+test(function() {
+ var e = document.createElement("iframe");
+ e.innerHTML = "</iframe><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "</iframe><div>");
+}, "</iframe> should not break out of iframe.");
+
+test(function() {
+ var e = document.createElement("noembed");
+ e.innerHTML = "</noembed><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "</noembed><div>");
+}, "</noembed> should not break out of noembed.");
+
+test(function() {
+ var e = document.createElement("noframes");
+ e.innerHTML = "</noframes><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "</noframes><div>");
+}, "</noframes> should not break out of noframes.");
+
+test(function() {
+ var e = document.createElement("script");
+ e.innerHTML = "<\/script><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "<\/script><div>");
+}, "<\/script> should not break out of script.");
+
+
+test(function() {
+ var e = document.createElement("noscript");
+ e.innerHTML = "</noscript><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "</noscript><div>");
+}, "</noscript> should not break out of noscript.");
+
+test(function() {
+ var e = document.createElement("plaintext");
+ e.innerHTML = "</plaintext><div>";
+ assert_equals(e.getElementsByTagName("div").length, 0);
+ assert_equals(e.innerHTML, "</plaintext><div>");
+}, "</plaintext> should not break out of plaintext.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/DOMContentLoaded-defer.html b/testing/web-platform/tests/html/syntax/parsing/DOMContentLoaded-defer.html
new file mode 100644
index 0000000000..44946adf4e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/DOMContentLoaded-defer.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>The end: DOMContentLoaded and defer scripts</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-end">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var dcl;
+var t = async_test(function() {
+ dcl = false;
+ document.addEventListener("DOMContentLoaded", function(e) {
+ dcl = true;
+ });
+});
+</script>
+<script defer src=support/DOMContentLoaded-defer.js></script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-01.html b/testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-01.html
new file mode 100644
index 0000000000..2bdc83145e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-01.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<title>document.getElementsByTagName and foreign parser-inserted
+elements</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-document-getelementsbytagname">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#parsing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<svg>
+<altglyph/>
+ <altglyphdef/>
+ <altglyphitem/>
+ <animatecolor/>
+ <animatemotion/>
+ <animatetransform/>
+ <clippath/>
+ <feblend/>
+ <fecolormatrix/>
+ <fecomponenttransfer/>
+ <fecomposite/>
+ <feconvolvematrix/>
+ <fediffuselighting/>
+ <fedisplacementmap/>
+ <fedistantlight/>
+ <feflood/>
+ <fefunca/>
+ <fefuncb/>
+ <fefuncg/>
+ <fefuncr/>
+ <fegaussianblur/>
+ <feimage/>
+ <femerge/>
+ <femergenode/>
+ <femorphology/>
+ <feoffset/>
+ <fepointlight/>
+ <fespecularlighting/>
+ <fespotlight/>
+ <fetile/>
+ <feturbulence/>
+ <foreignobject/>
+ <glyphref/>
+ <lineargradient/>
+ <radialgradient/>
+ <textpath/>
+ <ALTGLYPH/>
+ <ALTGLYPHDEF/>
+ <ALTGLYPHITEM/>
+ <ANIMATECOLOR/>
+ <ANIMATEMOTION/>
+ <ANIMATETRANSFORM/>
+ <CLIPPATH/>
+ <FEBLEND/>
+ <FECOLORMATRIX/>
+ <FECOMPONENTTRANSFER/>
+ <FECOMPOSITE/>
+ <FECONVOLVEMATRIX/>
+ <FEDIFFUSELIGHTING/>
+ <FEDISPLACEMENTMAP/>
+ <FEDISTANTLIGHT/>
+ <FEFLOOD/>
+ <FEFUNCA/>
+ <FEFUNCB/>
+ <FEFUNCG/>
+ <FEFUNCR/>
+ <FEGAUSSIANBLUR/>
+ <FEIMAGE/>
+ <FEMERGE/>
+ <FEMERGENODE/>
+ <FEMORPHOLOGY/>
+ <FEOFFSET/>
+ <FEPOINTLIGHT/>
+ <FESPECULARLIGHTING/>
+ <FESPOTLIGHT/>
+ <FETILE/>
+ <FETURBULENCE/>
+ <FOREIGNOBJECT/>
+ <GLYPHREF/>
+ <LINEARGRADIENT/>
+ <RADIALGRADIENT/>
+ <TEXTPATH/>
+</svg>
+<script>
+var elements = [
+ "altGlyph",
+ "altGlyphDef",
+ "altGlyphItem",
+ "animateColor",
+ "animateMotion",
+ "animateTransform",
+ "clipPath",
+ "feBlend",
+ "feColorMatrix",
+ "feComponentTransfer",
+ "feComposite",
+ "feConvolveMatrix",
+ "feDiffuseLighting",
+ "feDisplacementMap",
+ "feDistantLight",
+ "feFlood",
+ "feFuncA",
+ "feFuncB",
+ "feFuncG",
+ "feFuncR",
+ "feGaussianBlur",
+ "feImage",
+ "feMerge",
+ "feMergeNode",
+ "feMorphology",
+ "feOffset",
+ "fePointLight",
+ "feSpecularLighting",
+ "feSpotLight",
+ "feTile",
+ "feTurbulence",
+ "foreignObject",
+ "glyphRef",
+ "linearGradient",
+ "radialGradient",
+ "textPath"];
+</script>
+</div>
+<script>
+var SVG = "http://www.w3.org/2000/svg";
+function t(el) {
+ assert_equals(document.getElementsByTagName(el).length, 2);
+ assert_equals(document.getElementsByTagName(el.toUpperCase()).length, 0);
+ assert_equals(document.getElementsByTagName(el.toLowerCase()).length, 0);
+ assert_equals(document.getElementsByTagNameNS(SVG, el).length, 2);
+ assert_equals(document.getElementsByTagNameNS(SVG, el.toUpperCase()).length, 0);
+ assert_equals(document.getElementsByTagNameNS(SVG, el.toLowerCase()).length, 0);
+}
+test(function() {
+ var tests = [];
+ assert_equals(document.getElementsByTagName('svg').length, 1);
+ for (var i = 0, il = elements.length; i < il; ++i) {
+ tests.push(["Testing " + elements[i], elements[i]]);
+ }
+ generate_tests(t, tests);
+});
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-02.html b/testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-02.html
new file mode 100644
index 0000000000..1109a24cfe
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/Document.getElementsByTagName-foreign-02.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>getElementsByTagName and font</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-document-getelementsbytagname">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#parsing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<font></font>
+<svg><font/></svg>
+</div>
+<script>
+var HTML = "http://www.w3.org/1999/xhtml", SVG = "http://www.w3.org/2000/svg";
+test(function() {
+ assert_equals(document.getElementsByTagName("FONT").length, 1);
+ assert_equals(document.getElementsByTagName("FONT")[0].namespaceURI, HTML);
+}, "Upper-case font")
+test(function() {
+ assert_equals(document.getElementsByTagName("font").length, 2);
+ assert_equals(document.getElementsByTagName("font")[0].namespaceURI, HTML);
+ assert_equals(document.getElementsByTagName("font")[1].namespaceURI, SVG);
+}, "Lower-case font")
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-01.html b/testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-01.html
new file mode 100644
index 0000000000..cbad9f6d19
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-01.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>getElementsByTagName and font</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-element-getelementsbytagname">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#parsing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<font></font>
+<svg><font/></svg>
+</div>
+<script>
+var HTML = "http://www.w3.org/1999/xhtml", SVG = "http://www.w3.org/2000/svg";
+var wrapper = document.getElementById("test");
+test(function() {
+ assert_equals(wrapper.getElementsByTagName("FONT").length, 1);
+ assert_equals(wrapper.getElementsByTagName("FONT")[0].namespaceURI, HTML);
+}, "Upper-case font")
+test(function() {
+ assert_equals(wrapper.getElementsByTagName("font").length, 2);
+ assert_equals(wrapper.getElementsByTagName("font")[0].namespaceURI, HTML);
+ assert_equals(wrapper.getElementsByTagName("font")[1].namespaceURI, SVG);
+}, "Lower-case font")
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-02.html b/testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-02.html
new file mode 100644
index 0000000000..aabb169b1b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/Element.getElementsByTagName-foreign-02.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>getElementsByTagName and font</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-element-getelementsbytagname">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#parsing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<svg id="outer">
+<foreignObject>
+<font></font>
+<svg><font/></svg>
+</foreignObject>
+</svg>
+</div>
+<script>
+var HTML = "http://www.w3.org/1999/xhtml", SVG = "http://www.w3.org/2000/svg";
+var wrapper = document.getElementById("outer");
+test(function() {
+ assert_equals(wrapper.getElementsByTagName("FONT").length, 1);
+ assert_equals(wrapper.getElementsByTagName("FONT")[0].namespaceURI, HTML);
+}, "Upper-case font")
+test(function() {
+ assert_equals(wrapper.getElementsByTagName("font").length, 2);
+ assert_equals(wrapper.getElementsByTagName("font")[0].namespaceURI, HTML);
+ assert_equals(wrapper.getElementsByTagName("font")[1].namespaceURI, SVG);
+}, "Lower-case font")
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/README b/testing/web-platform/tests/html/syntax/parsing/README
new file mode 100644
index 0000000000..5c41f78d2e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/README
@@ -0,0 +1,8 @@
+Note: the html5lib_* files in this directory are autogenerated, as are
+the tests under speculative-parsing/.
+
+To update the generated tests, run
+`wpt update-built --include html5lib speculative-parsing`.
+
+The revision of html5lib-tests used to generate the tests is stored in
+html/tools/html5lib_tests_revision
diff --git a/testing/web-platform/tests/html/syntax/parsing/adoption_agency_check_the_end_tag_name.html b/testing/web-platform/tests/html/syntax/parsing/adoption_agency_check_the_end_tag_name.html
new file mode 100644
index 0000000000..f5d49dd45f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/adoption_agency_check_the_end_tag_name.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>The adoption agency algorithm should check the end tag's name</title>
+<link rel="author" href="mailto:n4ag3a2sh1i@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#adoption-agency-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+
+<script>
+'use strict';
+
+// This is a regression test for https://crbug.com/1217523.
+test(() => {
+ const wrapper = document.createElement('div');
+ const html = '<code some-attribute=""><div><code><code><code><code></code></code></code></code></div></code>';
+ wrapper.innerHTML = html;
+ assert_equals(wrapper.innerHTML, html);
+}, 'The algorithm should not reparent properly nested tags');
+
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/ambiguous-ampersand.html b/testing/web-platform/tests/html/syntax/parsing/ambiguous-ampersand.html
new file mode 100644
index 0000000000..e6d5a82215
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/ambiguous-ampersand.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Ambiguous ampersand</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div><a href='?a=b&c=d&a0b=c&copy=1&noti=n&not=in&notin=&notin;&not;&;& &'>Link</a><p>Text: ?a=b&c=d&a0b=c&copy=1&noti=n&not=in&notin=&notin;&not;&;& &</p></div>
+<script>
+var markup = "<div><a href='?a=b&c=d&a0b=c&copy=1&noti=n&not=in&notin=&notin;&not;&;& &'>Link</a><p>Text: ?a=b&c=d&a0b=c&copy=1&noti=n&not=in&notin=&notin;&not;&;& &</p></div>";
+
+for (var i = 0; i < markup.length; ++i) {
+ document.write(markup.charAt(i));
+}
+</script>
+
+<script>
+function checkDiv(div, provenance) {
+ test(function() {
+ assert_equals(div.childNodes.length, 2, "Number of elements " + provenance);
+ let a = div.firstChild;
+ let href = a.href;
+ let question = href.indexOf('?');
+ href = href.substring(question);
+ assert_equals(href, "?a=b&c=d&a0b=c&copy=1&noti=n&not=in&notin=%E2%88%89%C2%AC&;&%20&", "attribute " + provenance);
+ let p = a.nextSibling;
+ assert_equals(p.textContent, "Text: ?a=b&c=d&a0b=c©=1¬i=n¬=in¬in=∉¬&;& &", "text " + provenance)
+ }, "Check div structure: " + provenance);
+}
+
+
+let divs = document.getElementsByTagName("div");
+test(function() {
+ assert_equals(divs.length, 2);
+}, "Check number of divs");
+checkDiv(divs[0], "network");
+checkDiv(divs[1], "document.write");
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/common.js b/testing/web-platform/tests/html/syntax/parsing/common.js
new file mode 100644
index 0000000000..b3ac9ece70
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/common.js
@@ -0,0 +1,24 @@
+function mark_diffs(expected, actual) {
+ var expected_lines = expected.split("\n");
+ var actual_lines = actual.split("\n");
+
+ var max_length = Math.max(expected_lines.length, actual_lines.length);
+
+ var expected_diff = ["code", {}];
+ var actual_diff = ["code", {}];
+
+ for (var i=0; i<max_length; i++) {
+ if (expected_lines[i] === actual_lines[i]) {
+ expected_diff.push(expected_lines[i] + "\n");
+ actual_diff.push(actual_lines[i] + "\n");
+ } else {
+ if (expected_lines[i]) {
+ expected_diff.push(["span", {style:"color:red"}, expected_lines[i] + "\n"]);
+ }
+ if (actual_lines[i]) {
+ actual_diff.push(["span", {style:"color:red"}, actual_lines[i] + "\n"]);
+ }
+ }
+ }
+ return [expected_diff, actual_diff];
+}
diff --git a/testing/web-platform/tests/html/syntax/parsing/empty-doctype-ids.html b/testing/web-platform/tests/html/syntax/parsing/empty-doctype-ids.html
new file mode 100644
index 0000000000..befdce428c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/empty-doctype-ids.html
@@ -0,0 +1,10 @@
+<!doctype html PUBLIC "" "">
+<meta charset=utf-8>
+<title>Doctype with empty ids should trigger the standards mode.</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(function () {
+ assert_equals(document.compatMode, "CSS1Compat");
+}, "Doctype with empty ids should trigger the standards mode.");
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_001.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_001.html
new file mode 100644
index 0000000000..10ebaa8434
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_001.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG inside HTML OBJECT element parses correctly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ var svgNS = "http://www.w3.org/2000/svg";
+ if(document.getElementsByTagName("object")[0].childNodes[1].localName=="svg" && document.getElementsByTagName("rect")[0].namespaceURI==svgNS)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","green");
+ }
+ else
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red");
+ }
+ }
+ catch(ex)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red")
+ }
+ }
+ </script>
+ </head>
+
+ <body onLoad="RunTest()">
+ <div class="testdata">
+ <p id="instructions"> Test passes if a green rectangle is visible on the page below this line. </p>
+ </div>
+
+ <object>
+ <svg width="100px" height="100px" >
+ <rect width="100px" height="100px" fill="none"/>
+ </svg>
+ </object>
+ </body>
+
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_003.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_003.html
new file mode 100644
index 0000000000..320a944092
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_003.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG inside HTML BUTTON element parses correctly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ var svgNS = "http://www.w3.org/2000/svg";
+ if(document.getElementsByTagName("button")[0].childNodes[1].localName=="svg" && document.getElementsByTagName("rect")[0].namespaceURI==svgNS)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","green");
+ }
+ else
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red");
+ }
+ }
+ catch(ex)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red")
+ }
+ }
+ </script>
+ </head>
+
+ <body onLoad="RunTest()">
+
+ <div class="testdata">
+ <p id="instructions">Test passes if the button has a green rectangle while 'FillerText1' and 'FillerText3' are on either side of the button.</p>
+ </div>
+ <div>
+ FillerText1
+ <button>
+ <svg width="100px" height="100px">
+ <rect width="100px" height="100px" fill="none" />
+ </svg>
+
+ </button>
+ FillerText3
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_004.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_004.html
new file mode 100644
index 0000000000..eec49a81d1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_004.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG inside HTML CAPTION element parses correctly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ var svgNS = "http://www.w3.org/2000/svg";
+ if(document.getElementsByTagName("caption")[0].childNodes[1].localName=="svg" && document.getElementsByTagName("rect")[0].namespaceURI==svgNS)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","green");
+ }
+ else
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red");
+ }
+ }
+ catch(ex)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red")
+ }
+ }
+ </script>
+ </head>
+
+ <body onLoad="RunTest()">
+ <div class="testdata">
+ <p id="instructions">Test passes if a green square appears above and centered relative to the table.</p>
+ </div>
+ <div>
+ <table border="1">
+ <caption>
+ <svg width="100px" height="100px">
+ <rect width="100px" height="100px" fill="none" />
+ </svg>
+ </caption>
+ <thead>
+ <th>FillerText</th>
+ <th>FillerText</th>
+ <th>FillerText</th>
+ </thead>
+ <tr>
+ <td>FillerText</td>
+ <td>FillerText</td>
+ <td>FillerText</td>
+ </tr>
+ <tr>
+ <td>FillerText</td>
+ <td>FillerText</td>
+ <td>FillerText</td>
+ </tr>
+ <tr>
+ <td>FillerText</td>
+ <td>FillerText</td>
+ <td>FillerText</td>
+ </tr>
+ </table>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_005.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_005.html
new file mode 100644
index 0000000000..b8629dc7c6
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_005.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG inside HTML FORM element parses correctly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ var svgNS = "http://www.w3.org/2000/svg";
+ if(document.getElementsByTagName("form")[0].childNodes[1].localName=="svg" && document.getElementsByTagName("rect")[0].namespaceURI==svgNS)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","green");
+ }
+ else
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red");
+ }
+ }
+ catch(ex)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red")
+ }
+ }
+ </script>
+ </head>
+
+ <body onLoad="RunTest()">
+ <div class="testdata">
+ <p id="instructions">Test passes if green rectangle is visible below 'FillerText1'.</p>
+ </div>
+ <div>
+ FillerText1
+ <form>
+ <svg width="100px" height="100px">
+ <rect width="100px" height="100px" fill="none" />
+ </svg>
+ </form>
+
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_006.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_006.html
new file mode 100644
index 0000000000..36acda8fc3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_006.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+ <svg width="100px" height="100px">
+ <rect width="100px" height="100px" fill="none" />
+ </svg>
+
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG inside HTML 'HTML' element parses correctly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ if(document.body.childNodes[0].localName=="svg")
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","green");
+ }
+ else
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red");
+ }
+ }
+ catch(ex)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red")
+ }
+ }
+ </script>
+ </head>
+
+ <body onLoad="RunTest()">
+ <div class="testdata">
+ <p id="instructions">Test passes if a green rectangle is visible on the page above this line.</p>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_008.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_008.html
new file mode 100644
index 0000000000..348ff0b713
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_008.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG inside HTML 'BODY' element parses correctly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ if(document.body.childNodes[3].localName=="svg")
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","green");
+ }
+ else
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red");
+ }
+ }
+ catch(ex)
+ {
+ document.getElementsByTagName("rect")[0].setAttribute("fill","red")
+ }
+ }
+ </script>
+ </head>
+
+ <body onLoad="RunTest()">
+ <div class="testdata">
+ <p id="instructions"> Test passes if a green rectangle is visible on the page below this line. </p>
+ </div>
+ <svg width="100px" height="100px" >
+ <rect x="0" y="0" width="100px" height="100px" />
+ </svg>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_009.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_009.html
new file mode 100644
index 0000000000..364dd85781
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_009.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG Self Closing tags parses properly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ var parentNode = document.getElementById("rect1");
+ if(parentNode.childNodes[1].localName=="circle")
+ {
+ document.getElementById("testresult").innerHTML = "PASS";
+ }
+ else
+ {
+ document.getElementById("testresult").innerHTML = "FAIL"
+ }
+ }
+ catch(ex)
+ {
+ document.getElementById("testresult").innerHTML = "FAIL";
+ }
+ }
+ </script>
+ </head>
+
+ <body onload="RunTest()">
+ <div class="testdata">
+ <p id="instructions"> Test passes if the word "PASS" appears below </p>
+ <p> Test Result : </p>
+ <p id="testresult"> RUNNING </div>
+ </div>
+
+ <svg width="100px" height="100px">
+ <rect id="rect1" >
+ <circle> </circle>
+ </rect>
+ </svg>
+
+ </body>
+
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_010.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_010.html
new file mode 100644
index 0000000000..11e1bf1006
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_010.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG Self Closing tags parses properly" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script type="text/javascript">
+ function RunTest()
+ {
+ try
+ {
+ var parentNode = document.getElementById("svg1");
+ if(parentNode.childNodes[1].localName=="rect" && parentNode.childNodes[3].localName=="circle")
+ {
+ document.getElementById("testresult").innerHTML = "PASS";
+ }
+ else
+ {
+ document.getElementById("testresult").innerHTML = "FAIL"
+ }
+ }
+ catch(ex)
+ {
+ document.getElementById("testresult").innerHTML = "FAIL";
+ }
+ }
+ </script>
+ </head>
+
+ <body onload="RunTest()">
+
+ <div class="testdata">
+ <p id="instructions"> Test passes if the word "PASS" appears below </p>
+ <p> Test Result : </p>
+ <p id="testresult"> RUNNING </div>
+ </div>
+ <svg id="svg1" width="100px" height="100px">
+ <rect id="rect1" />
+ <circle id="circle1" />
+ </svg>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_011.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_011.html
new file mode 100644
index 0000000000..a8a9d7a1b6
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_011.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG elements are styled using ID Selector" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <style>
+ #d1
+ {
+ fill: green;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div class="testdata">
+ <p id="instructions">Test passes if a green square is visible below this line.</p>
+ </div>
+ <div id="d1">
+ <svg width="100px" height="100px">
+ <rect width="100px" height="100px" />
+ </svg>
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/foreign_content_013.html b/testing/web-platform/tests/html/syntax/parsing/foreign_content_013.html
new file mode 100644
index 0000000000..e88aae9e4d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/foreign_content_013.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML 5 Foreign Content SVG in HTML </title>
+ <meta description="Test to verify SVG elements are styled using CLASS Selector" />
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <style>
+ .svg1
+ {
+ fill: green ;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div class="testdata">
+ <p id="instructions">Test passes if a green square is visible below this line. </p>
+ </div>
+ <div id="d1">
+ <svg class="svg1" width="100px" height="100px">
+ <rect width="100px" height="100px"/>
+ </svg>
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html-integration-point.html b/testing/web-platform/tests/html/syntax/parsing/html-integration-point.html
new file mode 100644
index 0000000000..be6b42d07d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html-integration-point.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#tree-construction:html-integration-point">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<math><annotation-xml id="point-1" encoding="text/html"><xmp>&lt;/xmp&gt;&lt;img></xmp></annotation-xml></math>
+<math><annotation-xml id="point-2" encoding="application/xhtml+xml"><style>&lt;/style&gt;&lt;img></style></annotation-xml></math>
+<svg><foreignObject id="point-3"><iframe>&lt;/iframe&gt;&lt;img></iframe></foreignObject></svg>
+<svg><desc id="point-4"><noembed>&lt;/noembed&gt;&lt;img></noembed></desc></svg>
+<svg><title id="point-5"><noframes>&lt;/noframes&gt;&lt;img></noframes></title></svg>
+
+<script>
+function generate_test(id) {
+ return () => {
+ let point = document.querySelector('#' + id);
+ assert_not_equals(point.namespaceURI, 'http://www.w3.org/1999/xhtml');
+ let rawTextElement = point.firstChild;
+ assert_equals(rawTextElement.namespaceURI, 'http://www.w3.org/1999/xhtml');
+ assert_equals(rawTextElement.textContent.substr(0, 4), '&lt;',
+ 'Entity references should not be decoded.');
+ };
+}
+
+test(generate_test('point-1'), 'MathML annotation-xml with encoding=text/html should be an HTML integration point');
+test(generate_test('point-2'), 'MathML annotation-xml with encoding=application/xhtml+xml should be an HTML integration point');
+test(generate_test('point-3'), 'SVG foreignObject should be an HTML integration point');
+test(generate_test('point-4'), 'SVG desc should be an HTML integration point');
+test(generate_test('point-5'), 'SVG title should be an HTML integration point');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_adoption01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_adoption01.html
new file mode 100644
index 0000000000..87664da778
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_adoption01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_adoption01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['dab5eca760a630bc57719d678d789dd1ca74f492','a3a46907dc73b7be1e1171f797a9f696b7fb185b','b720cd1d95283d6288e7ca17142540b10ef8f847','83b2bd6f0849b2c7283448f15095806a17c7f0c0','1d56fa2d9d756f9053638a3db25fcd84e57e41ec','c129fce97933067558d7833632ad7ef2d149616e','39a3ec04f54cda2bf1e06c54caf0b302da4fd252','d0bdfe6be48309b2d01b497667b350a8e6ec8ffb','63401082c6afd4b3c3201e348a1a61722f167b21','883cfd89f63da854dffbfbc938da1f31887a55e5','4dc0665051dac0d72c71aba5c95c5b86437dea2c','4f376c0a798e71a91065e215dc0175d3107d5208','52b62611a847a3f5fc3dc607a6b0174f1697247c','88d0d3403d2a7b4058fcfb4e62835acb1c207e0f','d7338f457789f65d47b240e203b9e40a3925f2ca','bf8088acd8bd48d8487bae49a613c248c488c041','39edaa2e298c60ad4a1b0b9fdb4481d4aea98f36',];
+ var tests = {
+ "dab5eca760a630bc57719d678d789dd1ca74f492":[async_test('html5lib_adoption01.html dab5eca760a630bc57719d678d789dd1ca74f492'), "%3Ca%3E%3Cp%3E%3C/a%3E%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E"],"a3a46907dc73b7be1e1171f797a9f696b7fb185b":[async_test('html5lib_adoption01.html a3a46907dc73b7be1e1171f797a9f696b7fb185b'), "%3Ca%3E1%3Cp%3E2%3C/a%3E3%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%223%22"],"b720cd1d95283d6288e7ca17142540b10ef8f847":[async_test('html5lib_adoption01.html b720cd1d95283d6288e7ca17142540b10ef8f847'), "%3Ca%3E1%3Cbutton%3E2%3C/a%3E3%3C/button%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%223%22"],"83b2bd6f0849b2c7283448f15095806a17c7f0c0":[async_test('html5lib_adoption01.html 83b2bd6f0849b2c7283448f15095806a17c7f0c0'), "%3Ca%3E1%3Cb%3E2%3C/a%3E3%3C/b%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%223%22"],"1d56fa2d9d756f9053638a3db25fcd84e57e41ec":[async_test('html5lib_adoption01.html 1d56fa2d9d756f9053638a3db25fcd84e57e41ec'), "%3Ca%3E1%3Cdiv%3E2%3Cdiv%3E3%3C/a%3E4%3C/div%3E5%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%20%20%20%20%224%22%0A%7C%20%20%20%20%20%20%20%225%22"],"c129fce97933067558d7833632ad7ef2d149616e":[async_test('html5lib_adoption01.html c129fce97933067558d7833632ad7ef2d149616e'), "%3Ctable%3E%3Ca%3E1%3Cp%3E2%3C/a%3E3%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"39a3ec04f54cda2bf1e06c54caf0b302da4fd252":[async_test('html5lib_adoption01.html 39a3ec04f54cda2bf1e06c54caf0b302da4fd252'), "%3Cb%3E%3Cb%3E%3Ca%3E%3Cp%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E"],"d0bdfe6be48309b2d01b497667b350a8e6ec8ffb":[async_test('html5lib_adoption01.html d0bdfe6be48309b2d01b497667b350a8e6ec8ffb'), "%3Cb%3E%3Ca%3E%3Cb%3E%3Cp%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E"],"63401082c6afd4b3c3201e348a1a61722f167b21":[async_test('html5lib_adoption01.html 63401082c6afd4b3c3201e348a1a61722f167b21'), "%3Ca%3E%3Cb%3E%3Cb%3E%3Cp%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E"],"883cfd89f63da854dffbfbc938da1f31887a55e5":[async_test('html5lib_adoption01.html 883cfd89f63da854dffbfbc938da1f31887a55e5'), "%3Cp%3E1%3Cs%20id%3D%22A%22%3E2%3Cb%20id%3D%22B%22%3E3%3C/p%3E4%3C/s%3E5%3C/b%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cs%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22A%22%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%22B%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%3Cs%3E%0A%7C%20%20%20%20%20%20%20id%3D%22A%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22B%22%0A%7C%20%20%20%20%20%20%20%20%20%224%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20id%3D%22B%22%0A%7C%20%20%20%20%20%20%20%225%22"],"4dc0665051dac0d72c71aba5c95c5b86437dea2c":[async_test('html5lib_adoption01.html 4dc0665051dac0d72c71aba5c95c5b86437dea2c'), "%3Ctable%3E%3Ca%3E1%3Ctd%3E2%3C/td%3E3%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%222%22"],"4f376c0a798e71a91065e215dc0175d3107d5208":[async_test('html5lib_adoption01.html 4f376c0a798e71a91065e215dc0175d3107d5208'), "%3Ctable%3EA%3Ctd%3EB%3C/td%3EC%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22AC%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22B%22"],"52b62611a847a3f5fc3dc607a6b0174f1697247c":[async_test('html5lib_adoption01.html 52b62611a847a3f5fc3dc607a6b0174f1697247c'), "%3Ca%3E%3Csvg%3E%3Ctr%3E%3Cinput%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20tr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20input%3E"],"88d0d3403d2a7b4058fcfb4e62835acb1c207e0f":[async_test('html5lib_adoption01.html 88d0d3403d2a7b4058fcfb4e62835acb1c207e0f'), "%3Cdiv%3E%3Ca%3E%3Cb%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3Cdiv%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"d7338f457789f65d47b240e203b9e40a3925f2ca":[async_test('html5lib_adoption01.html d7338f457789f65d47b240e203b9e40a3925f2ca'), "%3Cdiv%3E%3Ca%3E%3Cb%3E%3Cu%3E%3Ci%3E%3Ccode%3E%3Cdiv%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cu%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%20%20%3Cu%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E"],"bf8088acd8bd48d8487bae49a613c248c488c041":[async_test('html5lib_adoption01.html bf8088acd8bd48d8487bae49a613c248c488c041'), "%3Cb%3E%3Cb%3E%3Cb%3E%3Cb%3Ex%3C/b%3E%3C/b%3E%3C/b%3E%3C/b%3Ey", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%22y%22"],"39edaa2e298c60ad4a1b0b9fdb4481d4aea98f36":[async_test('html5lib_adoption01.html 39edaa2e298c60ad4a1b0b9fdb4481d4aea98f36'), "%3Cp%3E%3Cb%3E%3Cb%3E%3Cb%3E%3Cb%3E%3Cp%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22x%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_adoption02.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_adoption02.html
new file mode 100644
index 0000000000..81bb8d35f2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_adoption02.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_adoption02.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['cefc9890d621707fedf5eb634f3a86d753659d9b','2a8f569e9d55b3cb7e54b026f758dea8593f4cb4',];
+ var tests = {
+ "cefc9890d621707fedf5eb634f3a86d753659d9b":[async_test('html5lib_adoption02.html cefc9890d621707fedf5eb634f3a86d753659d9b'), "%3Cb%3E1%3Ci%3E2%3Cp%3E3%3C/b%3E4", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%20%20%20%20%224%22"],"2a8f569e9d55b3cb7e54b026f758dea8593f4cb4":[async_test('html5lib_adoption02.html 2a8f569e9d55b3cb7e54b026f758dea8593f4cb4'), "%3Ca%3E%3Cdiv%3E%3Cstyle%3E%3C/style%3E%3Caddress%3E%3Ca%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%3Caddress%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_blocks.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_blocks.html
new file mode 100644
index 0000000000..b953e06829
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_blocks.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_blocks.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['f2c6f8a359cf4782cc8038a9f6a91a7cb67fb9ef','77b909047ffa7bf2ca38cd3de8e25eb3ed0395af','75f77d8a1d072dfabd17237596bb0394754d4e41','b241d42f7a3593e556e2ef4e0d4c0b9af3bbde4b','965c072a3f6b78e9b7c4c8d2b93d3746a2fc360c','c430a64d10f5a10ac3c34f1ec3abdd0d048e3e9f','b730d11baf16843bc04136ffe53ec20e058a50aa','5db636d68e8708a8cfd26ee472a7831239c7efeb','41586061adf395acf518e7ad457d048a911e52c1','1394f443495c04661d70ec27e44d99e6fcb3b536','9a977cfacba03a56f6ccd11dd6595a7fa65b3c3e','284962aa35b722a4d05a22c264c0bbbc859f7d41','88dfa3115905b28f4086717b8d6547b8f88ee46a','d5e8a935c5b5c3723b77ff0a9079bbee3e8f02ad','6cb6f36b9c042e41b18967b46b068db7b36431ef','68111d7cbc22fd47cbec6912d008ab93bc1719ec','5ff74097bb1276f6d595d17674c04d2d9ba9a49a','e38ae5ed14b2fbde7e15bd45bbfb26f828a6fcf6','5fa3d2ad933a13b1bdabe4faa77a2b33923a9871','3ac498b2d74a10dde5987dd6a9a40f8fb4c2cca7','0d3751cad1256413b5514734742f1ea0dadc99c9','c9d17d8d37f012a75fe2d0cf157cb4e4c9290b76','b224c98e66c1b2ede2447430fa3cc29dd490aa27','0cf2e437eb921f7a308d493ab6f776400ec6ba05','e721fb9ccda59a3c0a07f8b7c69698945cec1c7c','4c056b025cbf0f880e9ab93ab95d37d075a3d62e','28ad066f78c1d23110991d1d323748e1c638a285','aabe06c45ead4e713911545d6050dc992603f44c','c19b1a4a7323b8ebc292718c2a1f131493271012','211e55efcc6948df455ffa1d7b5e2fda123cca51','7216ccc21592b2615aabcbc73069bd06c31a3c4f','f2e488a24791d7ac57dd6a25e670f30b5b07454e','69b5e75bb50a6429e776b81ae532103096786266','a7b3390d1ab18f81e40623c92ad8d2cd164c297b','5fd2647163cc33ca7a0c93ff3a31f87cef87d7de','95b901de4aaf79e3230ebc04f32db4c61ce9f7c7','e59e60d6a134290fa7f2d1d2eb2c73f0617106b1','2c8b2df349d7f36b2ab55641d7e9c984ed09104d','1294906ecdf4d6378e0cdfb244192a0ed08343b2','95fc3d268c47b5e0fc87e92542caccbb4cabffe1','0c03d60e2cb91de005d270e8a00589bccefa47b8','125caaaa6687b316711f2b695dbcaebce9874270','d02390d4a365d9e5ee8a8caf920efc5c2baf83b6','a17ff80304101d2d144ee9a2b4ebeee1beeff4d5','6c32d08349975d65ec100346f36beb760252c42f','17f3861b008afd895cc5e642218d4aed099afc77','fe4badd0b7469c6ddb8cd243133f10ffd1b84814','b130e7f7a09b5516e13e281d577c5bcc1f56cb5d',];
+ var tests = {
+ "f2c6f8a359cf4782cc8038a9f6a91a7cb67fb9ef":[async_test('html5lib_blocks.html f2c6f8a359cf4782cc8038a9f6a91a7cb67fb9ef'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Caddress%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Caddress%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"77b909047ffa7bf2ca38cd3de8e25eb3ed0395af":[async_test('html5lib_blocks.html 77b909047ffa7bf2ca38cd3de8e25eb3ed0395af'), "%3C%21doctype%20html%3E%3Caddress%3E%3Cp%3Efoo%3C/address%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Caddress%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"75f77d8a1d072dfabd17237596bb0394754d4e41":[async_test('html5lib_blocks.html 75f77d8a1d072dfabd17237596bb0394754d4e41'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Carticle%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Carticle%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"b241d42f7a3593e556e2ef4e0d4c0b9af3bbde4b":[async_test('html5lib_blocks.html b241d42f7a3593e556e2ef4e0d4c0b9af3bbde4b'), "%3C%21doctype%20html%3E%3Carticle%3E%3Cp%3Efoo%3C/article%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Carticle%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"965c072a3f6b78e9b7c4c8d2b93d3746a2fc360c":[async_test('html5lib_blocks.html 965c072a3f6b78e9b7c4c8d2b93d3746a2fc360c'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Caside%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Caside%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"c430a64d10f5a10ac3c34f1ec3abdd0d048e3e9f":[async_test('html5lib_blocks.html c430a64d10f5a10ac3c34f1ec3abdd0d048e3e9f'), "%3C%21doctype%20html%3E%3Caside%3E%3Cp%3Efoo%3C/aside%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Caside%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"b730d11baf16843bc04136ffe53ec20e058a50aa":[async_test('html5lib_blocks.html b730d11baf16843bc04136ffe53ec20e058a50aa'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cblockquote%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cblockquote%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"5db636d68e8708a8cfd26ee472a7831239c7efeb":[async_test('html5lib_blocks.html 5db636d68e8708a8cfd26ee472a7831239c7efeb'), "%3C%21doctype%20html%3E%3Cblockquote%3E%3Cp%3Efoo%3C/blockquote%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cblockquote%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"41586061adf395acf518e7ad457d048a911e52c1":[async_test('html5lib_blocks.html 41586061adf395acf518e7ad457d048a911e52c1'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Ccenter%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Ccenter%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"1394f443495c04661d70ec27e44d99e6fcb3b536":[async_test('html5lib_blocks.html 1394f443495c04661d70ec27e44d99e6fcb3b536'), "%3C%21doctype%20html%3E%3Ccenter%3E%3Cp%3Efoo%3C/center%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ccenter%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"9a977cfacba03a56f6ccd11dd6595a7fa65b3c3e":[async_test('html5lib_blocks.html 9a977cfacba03a56f6ccd11dd6595a7fa65b3c3e'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cdetails%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cdetails%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"284962aa35b722a4d05a22c264c0bbbc859f7d41":[async_test('html5lib_blocks.html 284962aa35b722a4d05a22c264c0bbbc859f7d41'), "%3C%21doctype%20html%3E%3Cdetails%3E%3Cp%3Efoo%3C/details%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdetails%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"88dfa3115905b28f4086717b8d6547b8f88ee46a":[async_test('html5lib_blocks.html 88dfa3115905b28f4086717b8d6547b8f88ee46a'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cdialog%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cdialog%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"d5e8a935c5b5c3723b77ff0a9079bbee3e8f02ad":[async_test('html5lib_blocks.html d5e8a935c5b5c3723b77ff0a9079bbee3e8f02ad'), "%3C%21doctype%20html%3E%3Cdialog%3E%3Cp%3Efoo%3C/dialog%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdialog%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"6cb6f36b9c042e41b18967b46b068db7b36431ef":[async_test('html5lib_blocks.html 6cb6f36b9c042e41b18967b46b068db7b36431ef'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cdir%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cdir%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"68111d7cbc22fd47cbec6912d008ab93bc1719ec":[async_test('html5lib_blocks.html 68111d7cbc22fd47cbec6912d008ab93bc1719ec'), "%3C%21doctype%20html%3E%3Cdir%3E%3Cp%3Efoo%3C/dir%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdir%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"5ff74097bb1276f6d595d17674c04d2d9ba9a49a":[async_test('html5lib_blocks.html 5ff74097bb1276f6d595d17674c04d2d9ba9a49a'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cdiv%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"e38ae5ed14b2fbde7e15bd45bbfb26f828a6fcf6":[async_test('html5lib_blocks.html e38ae5ed14b2fbde7e15bd45bbfb26f828a6fcf6'), "%3C%21doctype%20html%3E%3Cdiv%3E%3Cp%3Efoo%3C/div%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"5fa3d2ad933a13b1bdabe4faa77a2b33923a9871":[async_test('html5lib_blocks.html 5fa3d2ad933a13b1bdabe4faa77a2b33923a9871'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cdl%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cdl%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"3ac498b2d74a10dde5987dd6a9a40f8fb4c2cca7":[async_test('html5lib_blocks.html 3ac498b2d74a10dde5987dd6a9a40f8fb4c2cca7'), "%3C%21doctype%20html%3E%3Cdl%3E%3Cp%3Efoo%3C/dl%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdl%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"0d3751cad1256413b5514734742f1ea0dadc99c9":[async_test('html5lib_blocks.html 0d3751cad1256413b5514734742f1ea0dadc99c9'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cfieldset%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cfieldset%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"c9d17d8d37f012a75fe2d0cf157cb4e4c9290b76":[async_test('html5lib_blocks.html c9d17d8d37f012a75fe2d0cf157cb4e4c9290b76'), "%3C%21doctype%20html%3E%3Cfieldset%3E%3Cp%3Efoo%3C/fieldset%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfieldset%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"b224c98e66c1b2ede2447430fa3cc29dd490aa27":[async_test('html5lib_blocks.html b224c98e66c1b2ede2447430fa3cc29dd490aa27'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cfigcaption%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cfigcaption%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"0cf2e437eb921f7a308d493ab6f776400ec6ba05":[async_test('html5lib_blocks.html 0cf2e437eb921f7a308d493ab6f776400ec6ba05'), "%3C%21doctype%20html%3E%3Cfigcaption%3E%3Cp%3Efoo%3C/figcaption%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfigcaption%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"e721fb9ccda59a3c0a07f8b7c69698945cec1c7c":[async_test('html5lib_blocks.html e721fb9ccda59a3c0a07f8b7c69698945cec1c7c'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cfigure%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cfigure%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"4c056b025cbf0f880e9ab93ab95d37d075a3d62e":[async_test('html5lib_blocks.html 4c056b025cbf0f880e9ab93ab95d37d075a3d62e'), "%3C%21doctype%20html%3E%3Cfigure%3E%3Cp%3Efoo%3C/figure%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfigure%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"28ad066f78c1d23110991d1d323748e1c638a285":[async_test('html5lib_blocks.html 28ad066f78c1d23110991d1d323748e1c638a285'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cfooter%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cfooter%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"aabe06c45ead4e713911545d6050dc992603f44c":[async_test('html5lib_blocks.html aabe06c45ead4e713911545d6050dc992603f44c'), "%3C%21doctype%20html%3E%3Cfooter%3E%3Cp%3Efoo%3C/footer%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfooter%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"c19b1a4a7323b8ebc292718c2a1f131493271012":[async_test('html5lib_blocks.html c19b1a4a7323b8ebc292718c2a1f131493271012'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cheader%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cheader%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"211e55efcc6948df455ffa1d7b5e2fda123cca51":[async_test('html5lib_blocks.html 211e55efcc6948df455ffa1d7b5e2fda123cca51'), "%3C%21doctype%20html%3E%3Cheader%3E%3Cp%3Efoo%3C/header%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cheader%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"7216ccc21592b2615aabcbc73069bd06c31a3c4f":[async_test('html5lib_blocks.html 7216ccc21592b2615aabcbc73069bd06c31a3c4f'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Chgroup%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Chgroup%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"f2e488a24791d7ac57dd6a25e670f30b5b07454e":[async_test('html5lib_blocks.html f2e488a24791d7ac57dd6a25e670f30b5b07454e'), "%3C%21doctype%20html%3E%3Chgroup%3E%3Cp%3Efoo%3C/hgroup%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Chgroup%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"69b5e75bb50a6429e776b81ae532103096786266":[async_test('html5lib_blocks.html 69b5e75bb50a6429e776b81ae532103096786266'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Clisting%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Clisting%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"a7b3390d1ab18f81e40623c92ad8d2cd164c297b":[async_test('html5lib_blocks.html a7b3390d1ab18f81e40623c92ad8d2cd164c297b'), "%3C%21doctype%20html%3E%3Clisting%3E%3Cp%3Efoo%3C/listing%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Clisting%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"5fd2647163cc33ca7a0c93ff3a31f87cef87d7de":[async_test('html5lib_blocks.html 5fd2647163cc33ca7a0c93ff3a31f87cef87d7de'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cmenu%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cmenu%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"95b901de4aaf79e3230ebc04f32db4c61ce9f7c7":[async_test('html5lib_blocks.html 95b901de4aaf79e3230ebc04f32db4c61ce9f7c7'), "%3C%21doctype%20html%3E%3Cmenu%3E%3Cp%3Efoo%3C/menu%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenu%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"e59e60d6a134290fa7f2d1d2eb2c73f0617106b1":[async_test('html5lib_blocks.html e59e60d6a134290fa7f2d1d2eb2c73f0617106b1'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cnav%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cnav%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"2c8b2df349d7f36b2ab55641d7e9c984ed09104d":[async_test('html5lib_blocks.html 2c8b2df349d7f36b2ab55641d7e9c984ed09104d'), "%3C%21doctype%20html%3E%3Cnav%3E%3Cp%3Efoo%3C/nav%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cnav%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"1294906ecdf4d6378e0cdfb244192a0ed08343b2":[async_test('html5lib_blocks.html 1294906ecdf4d6378e0cdfb244192a0ed08343b2'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Col%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Col%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"95fc3d268c47b5e0fc87e92542caccbb4cabffe1":[async_test('html5lib_blocks.html 95fc3d268c47b5e0fc87e92542caccbb4cabffe1'), "%3C%21doctype%20html%3E%3Col%3E%3Cp%3Efoo%3C/ol%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Col%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"0c03d60e2cb91de005d270e8a00589bccefa47b8":[async_test('html5lib_blocks.html 0c03d60e2cb91de005d270e8a00589bccefa47b8'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cpre%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"125caaaa6687b316711f2b695dbcaebce9874270":[async_test('html5lib_blocks.html 125caaaa6687b316711f2b695dbcaebce9874270'), "%3C%21doctype%20html%3E%3Cpre%3E%3Cp%3Efoo%3C/pre%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"d02390d4a365d9e5ee8a8caf920efc5c2baf83b6":[async_test('html5lib_blocks.html d02390d4a365d9e5ee8a8caf920efc5c2baf83b6'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Csection%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Csection%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"a17ff80304101d2d144ee9a2b4ebeee1beeff4d5":[async_test('html5lib_blocks.html a17ff80304101d2d144ee9a2b4ebeee1beeff4d5'), "%3C%21doctype%20html%3E%3Csection%3E%3Cp%3Efoo%3C/section%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csection%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"6c32d08349975d65ec100346f36beb760252c42f":[async_test('html5lib_blocks.html 6c32d08349975d65ec100346f36beb760252c42f'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Csummary%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Csummary%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"17f3861b008afd895cc5e642218d4aed099afc77":[async_test('html5lib_blocks.html 17f3861b008afd895cc5e642218d4aed099afc77'), "%3C%21doctype%20html%3E%3Csummary%3E%3Cp%3Efoo%3C/summary%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csummary%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"fe4badd0b7469c6ddb8cd243133f10ffd1b84814":[async_test('html5lib_blocks.html fe4badd0b7469c6ddb8cd243133f10ffd1b84814'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cul%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"b130e7f7a09b5516e13e281d577c5bcc1f56cb5d":[async_test('html5lib_blocks.html b130e7f7a09b5516e13e281d577c5bcc1f56cb5d'), "%3C%21doctype%20html%3E%3Cul%3E%3Cp%3Efoo%3C/ul%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_comments01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_comments01.html
new file mode 100644
index 0000000000..ba6818ccfa
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_comments01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_comments01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['3dbda8330033c3fbf3185a55e963075328099578','7476098e9823b3deee1857739daf719ff18e37b4','eca7f97a4659eab6da4127c225420f1664b6c0c4','27ff1671f247daf2fdf19213bcee6ae6cd848583','a6c42885c32fa988374894c0871b5a289786912a','d59b196ec9f6f19ded28d5e1fb48cbec2b1a4187','97486c43262a9a5c3fec1a3b028b99b7ca8c0e3e','06145ae05b1d92b71aed067ed6f1aab1afea50ae','5064d84d3adaf6262cf3573e9e112f40e3abc147','8b36d140a4a223b083a8d41af7c98a1c20377856','1894e23c5ee89d6f4b5f1dbe9b681b42863b4d1f','2cefeae994b6b0be0accbfff4757fef40ed914eb','ac9fd94008255e73cba953dbd374cb41703f5446','617815b6a683613fcb6b9cd5841b2ea7428d838d','bb8faf75d2e28aee13ec4a0d8eab00b4d7475763','89c4ae1ae34df9dff0e516afdef87cd169c3e6a5',];
+ var tests = {
+ "3dbda8330033c3fbf3185a55e963075328099578":[async_test('html5lib_comments01.html 3dbda8330033c3fbf3185a55e963075328099578'), "FOO%3C%21--%20BAR%20--%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"7476098e9823b3deee1857739daf719ff18e37b4":[async_test('html5lib_comments01.html 7476098e9823b3deee1857739daf719ff18e37b4'), "FOO%3C%21--%20BAR%20--%21%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"eca7f97a4659eab6da4127c225420f1664b6c0c4":[async_test('html5lib_comments01.html eca7f97a4659eab6da4127c225420f1664b6c0c4'), "FOO%3C%21--%20BAR%20--%21%20%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20--%21%20%3EBAZ%20--%3E"],"27ff1671f247daf2fdf19213bcee6ae6cd848583":[async_test('html5lib_comments01.html 27ff1671f247daf2fdf19213bcee6ae6cd848583'), "FOO%3C%21--%20BAR%20--%21%0A%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20--%21%0A%3EBAZ%20--%3E"],"a6c42885c32fa988374894c0871b5a289786912a":[async_test('html5lib_comments01.html a6c42885c32fa988374894c0871b5a289786912a'), "FOO%3C%21--%20BAR%20--%20%20%20%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20--%20%20%20%3EBAZ%20--%3E"],"d59b196ec9f6f19ded28d5e1fb48cbec2b1a4187":[async_test('html5lib_comments01.html d59b196ec9f6f19ded28d5e1fb48cbec2b1a4187'), "FOO%3C%21--%20BAR%20--%20%3CQUX%3E%20--%20MUX%20--%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20--%20%3CQUX%3E%20--%20MUX%20%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"97486c43262a9a5c3fec1a3b028b99b7ca8c0e3e":[async_test('html5lib_comments01.html 97486c43262a9a5c3fec1a3b028b99b7ca8c0e3e'), "FOO%3C%21--%20BAR%20--%20%3CQUX%3E%20--%20MUX%20--%21%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20--%20%3CQUX%3E%20--%20MUX%20%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"06145ae05b1d92b71aed067ed6f1aab1afea50ae":[async_test('html5lib_comments01.html 06145ae05b1d92b71aed067ed6f1aab1afea50ae'), "FOO%3C%21--%20BAR%20--%20%3CQUX%3E%20--%20MUX%20--%20%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20BAR%20--%20%3CQUX%3E%20--%20MUX%20--%20%3EBAZ%20--%3E"],"5064d84d3adaf6262cf3573e9e112f40e3abc147":[async_test('html5lib_comments01.html 5064d84d3adaf6262cf3573e9e112f40e3abc147'), "FOO%3C%21----%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"8b36d140a4a223b083a8d41af7c98a1c20377856":[async_test('html5lib_comments01.html 8b36d140a4a223b083a8d41af7c98a1c20377856'), "FOO%3C%21---%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"1894e23c5ee89d6f4b5f1dbe9b681b42863b4d1f":[async_test('html5lib_comments01.html 1894e23c5ee89d6f4b5f1dbe9b681b42863b4d1f'), "FOO%3C%21--%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"2cefeae994b6b0be0accbfff4757fef40ed914eb":[async_test('html5lib_comments01.html 2cefeae994b6b0be0accbfff4757fef40ed914eb'), "%3C%3Fxml%20version%3D%221.0%22%3EHi", "%23document%0A%7C%20%3C%21--%20%3Fxml%20version%3D%221.0%22%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hi%22"],"ac9fd94008255e73cba953dbd374cb41703f5446":[async_test('html5lib_comments01.html ac9fd94008255e73cba953dbd374cb41703f5446'), "%3C%3Fxml%20version%3D%221.0%22%3E", "%23document%0A%7C%20%3C%21--%20%3Fxml%20version%3D%221.0%22%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"617815b6a683613fcb6b9cd5841b2ea7428d838d":[async_test('html5lib_comments01.html 617815b6a683613fcb6b9cd5841b2ea7428d838d'), "%3C%3Fxml%20version", "%23document%0A%7C%20%3C%21--%20%3Fxml%20version%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"bb8faf75d2e28aee13ec4a0d8eab00b4d7475763":[async_test('html5lib_comments01.html bb8faf75d2e28aee13ec4a0d8eab00b4d7475763'), "FOO%3C%21-----%3EBAZ", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3C%21--%20-%20--%3E%0A%7C%20%20%20%20%20%22BAZ%22"],"89c4ae1ae34df9dff0e516afdef87cd169c3e6a5":[async_test('html5lib_comments01.html 89c4ae1ae34df9dff0e516afdef87cd169c3e6a5'), "%3Chtml%3E%3C%21--%20comment%20--%3E%3Ctitle%3EComment%20before%20head%3C/title%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3C%21--%20%20comment%20%20--%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22Comment%20before%20head%22%0A%7C%20%20%20%3Cbody%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_doctype01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_doctype01.html
new file mode 100644
index 0000000000..fcf616e1a5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_doctype01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_doctype01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['8c660236fa29672c9b1ed0b64be59835a2a2f8cf','6a757c8c420fe3273632e84a4d999f17164f1214','a8ba29d82bb59f5ca03cfc193a1af770b889d4df','7a5044da1f470565fc4c6697b05132ff803d33e5','6514baf0155d3d10c1449752666c9da1c3db7141','ab7af413296ccb0e5705d978b98751821eb60ee3','3f3585cd61875a7f824422eda85a568952c1b08d','32bf847dcfae86480610633948efb2eca296abdd','692ab73d917820f55f789498c003ff184694becd','69af8e298e3560620c7598a9d2a6be0c50fe6881','93737d931105f8441c51a61ce6c9c625708aa690','be48ad2b635136c6ae4d99ff06bc930711e26ab7','6b90a61449551f1d7ac3aaa54669e81b1adbeb48','dc641c8a55963188374a5ab0f9be4c00897a113d','a157f7a60a42840003e9a798ca4c3981c17eb08b','826518fc2114e88a3dd7caecef77fa9ed0967f9e','0d86430bce85790c6eb1ead82133ee74077c6ac9','79ef363724b7881ee356c0a105e6add81941d57e','d8d1295947c1944ba51f7be60165e2d5d7592225','8214eb8bef5c817fd5fee4befdc15dbcc49ff6e0','51dd468ef51c8c0ea3e4b5a81517831c6a9168cb','dab4dcff81a1d051394a34771eb9627d07700377','6c679921462b1833e5203e6cd48f761b479d8924','a45fad90fa4d150fdb82f61bf49e0f92a0aaaf94','18cc825780e6be5a1ab60475c07be505759b25ad','37960ee5e9a9252988a63d9653336d30c1645834','095187e72e2336527d8c39293d68d456dfb1a10e','46383e4754c42b37ebba10ad4e3644dcff1eec18','702c2be9dc6bf31da96af43947e0e16d040bd395','7ffeed0800cff7c5117ba7de33f5e70d037129c3','2d57d9c9c46d222676eeb8e8531f3a77ca7aa4c0','544240cd747ec56acbc607b6b8ad9f99de4c4085','af212d55a1c59b5e3658fb39a2f86daab3686f94','10d43f89829998731c313c85eb31e981961b1981','99d16cdde20971b45a3b81ca7a2b9d9858525b22','44ac65856abd110419aafffde69f0dc127f98ec3','a69ffa9525892dbb38be6ee78f4fe1216ef6ea5d',];
+ var tests = {
+ "8c660236fa29672c9b1ed0b64be59835a2a2f8cf":[async_test('html5lib_doctype01.html 8c660236fa29672c9b1ed0b64be59835a2a2f8cf'), "%3C%21DOCTYPE%20html%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"6a757c8c420fe3273632e84a4d999f17164f1214":[async_test('html5lib_doctype01.html 6a757c8c420fe3273632e84a4d999f17164f1214'), "%3C%21dOctYpE%20HtMl%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"a8ba29d82bb59f5ca03cfc193a1af770b889d4df":[async_test('html5lib_doctype01.html a8ba29d82bb59f5ca03cfc193a1af770b889d4df'), "%3C%21DOCTYPEhtml%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"7a5044da1f470565fc4c6697b05132ff803d33e5":[async_test('html5lib_doctype01.html 7a5044da1f470565fc4c6697b05132ff803d33e5'), "%3C%21DOCTYPE%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"6514baf0155d3d10c1449752666c9da1c3db7141":[async_test('html5lib_doctype01.html 6514baf0155d3d10c1449752666c9da1c3db7141'), "%3C%21DOCTYPE%20%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"ab7af413296ccb0e5705d978b98751821eb60ee3":[async_test('html5lib_doctype01.html ab7af413296ccb0e5705d978b98751821eb60ee3'), "%3C%21DOCTYPE%20potato%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"3f3585cd61875a7f824422eda85a568952c1b08d":[async_test('html5lib_doctype01.html 3f3585cd61875a7f824422eda85a568952c1b08d'), "%3C%21DOCTYPE%20potato%20%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"32bf847dcfae86480610633948efb2eca296abdd":[async_test('html5lib_doctype01.html 32bf847dcfae86480610633948efb2eca296abdd'), "%3C%21DOCTYPE%20potato%20taco%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"692ab73d917820f55f789498c003ff184694becd":[async_test('html5lib_doctype01.html 692ab73d917820f55f789498c003ff184694becd'), "%3C%21DOCTYPE%20potato%20taco%20%22ddd%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"69af8e298e3560620c7598a9d2a6be0c50fe6881":[async_test('html5lib_doctype01.html 69af8e298e3560620c7598a9d2a6be0c50fe6881'), "%3C%21DOCTYPE%20potato%20sYstEM%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"93737d931105f8441c51a61ce6c9c625708aa690":[async_test('html5lib_doctype01.html 93737d931105f8441c51a61ce6c9c625708aa690'), "%3C%21DOCTYPE%20potato%20sYstEM%20%20%20%20%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"be48ad2b635136c6ae4d99ff06bc930711e26ab7":[async_test('html5lib_doctype01.html be48ad2b635136c6ae4d99ff06bc930711e26ab7'), "%3C%21DOCTYPE%20%20%20potato%20%20%20%20%20%20%20sYstEM%20%20ggg%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"6b90a61449551f1d7ac3aaa54669e81b1adbeb48":[async_test('html5lib_doctype01.html 6b90a61449551f1d7ac3aaa54669e81b1adbeb48'), "%3C%21DOCTYPE%20potato%20SYSTEM%20taco%20%20%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"dc641c8a55963188374a5ab0f9be4c00897a113d":[async_test('html5lib_doctype01.html dc641c8a55963188374a5ab0f9be4c00897a113d'), "%3C%21DOCTYPE%20potato%20SYSTEM%20%27taco%22%27%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%20%22%22%20%22taco%22%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"a157f7a60a42840003e9a798ca4c3981c17eb08b":[async_test('html5lib_doctype01.html a157f7a60a42840003e9a798ca4c3981c17eb08b'), "%3C%21DOCTYPE%20potato%20SYSTEM%20%22taco%22%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%20%22%22%20%22taco%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"826518fc2114e88a3dd7caecef77fa9ed0967f9e":[async_test('html5lib_doctype01.html 826518fc2114e88a3dd7caecef77fa9ed0967f9e'), "%3C%21DOCTYPE%20potato%20SYSTEM%20%22tai%27co%22%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%20%22%22%20%22tai%27co%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"0d86430bce85790c6eb1ead82133ee74077c6ac9":[async_test('html5lib_doctype01.html 0d86430bce85790c6eb1ead82133ee74077c6ac9'), "%3C%21DOCTYPE%20potato%20SYSTEMtaco%20%22ddd%22%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"79ef363724b7881ee356c0a105e6add81941d57e":[async_test('html5lib_doctype01.html 79ef363724b7881ee356c0a105e6add81941d57e'), "%3C%21DOCTYPE%20potato%20grass%20SYSTEM%20taco%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"d8d1295947c1944ba51f7be60165e2d5d7592225":[async_test('html5lib_doctype01.html d8d1295947c1944ba51f7be60165e2d5d7592225'), "%3C%21DOCTYPE%20potato%20pUbLIc%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"8214eb8bef5c817fd5fee4befdc15dbcc49ff6e0":[async_test('html5lib_doctype01.html 8214eb8bef5c817fd5fee4befdc15dbcc49ff6e0'), "%3C%21DOCTYPE%20potato%20pUbLIc%20%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"51dd468ef51c8c0ea3e4b5a81517831c6a9168cb":[async_test('html5lib_doctype01.html 51dd468ef51c8c0ea3e4b5a81517831c6a9168cb'), "%3C%21DOCTYPE%20potato%20pUbLIcgoof%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"dab4dcff81a1d051394a34771eb9627d07700377":[async_test('html5lib_doctype01.html dab4dcff81a1d051394a34771eb9627d07700377'), "%3C%21DOCTYPE%20potato%20PUBLIC%20goof%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"6c679921462b1833e5203e6cd48f761b479d8924":[async_test('html5lib_doctype01.html 6c679921462b1833e5203e6cd48f761b479d8924'), "%3C%21DOCTYPE%20potato%20PUBLIC%20%22go%27of%22%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%20%22go%27of%22%20%22%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"a45fad90fa4d150fdb82f61bf49e0f92a0aaaf94":[async_test('html5lib_doctype01.html a45fad90fa4d150fdb82f61bf49e0f92a0aaaf94'), "%3C%21DOCTYPE%20potato%20PUBLIC%20%27go%27of%27%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%20%22go%22%20%22%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"18cc825780e6be5a1ab60475c07be505759b25ad":[async_test('html5lib_doctype01.html 18cc825780e6be5a1ab60475c07be505759b25ad'), "%3C%21DOCTYPE%20potato%20PUBLIC%20%27go%3Ahh%20%20%20of%27%20%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%20%22go%3Ahh%20%20%20of%22%20%22%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"37960ee5e9a9252988a63d9653336d30c1645834":[async_test('html5lib_doctype01.html 37960ee5e9a9252988a63d9653336d30c1645834'), "%3C%21DOCTYPE%20potato%20PUBLIC%20%22W3C-//dfdf%22%20SYSTEM%20ggg%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20potato%20%22W3C-//dfdf%22%20%22%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"095187e72e2336527d8c39293d68d456dfb1a10e":[async_test('html5lib_doctype01.html 095187e72e2336527d8c39293d68d456dfb1a10e'), "%3C%21DOCTYPE%20HTML%20PUBLIC%20%22-//W3C//DTD%20HTML%204.01//EN%22%0A%20%20%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20HTML%204.01//EN%22%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"46383e4754c42b37ebba10ad4e3644dcff1eec18":[async_test('html5lib_doctype01.html 46383e4754c42b37ebba10ad4e3644dcff1eec18'), "%3C%21DOCTYPE%20...%3EHello", "%23document%0A%7C%20%3C%21DOCTYPE%20...%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Hello%22"],"702c2be9dc6bf31da96af43947e0e16d040bd395":[async_test('html5lib_doctype01.html 702c2be9dc6bf31da96af43947e0e16d040bd395'), "%3C%21DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.0%20Transitional//EN%22%0A%22http%3A//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20XHTML%201.0%20Transitional//EN%22%20%22http%3A//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"7ffeed0800cff7c5117ba7de33f5e70d037129c3":[async_test('html5lib_doctype01.html 7ffeed0800cff7c5117ba7de33f5e70d037129c3'), "%3C%21DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.0%20Frameset//EN%22%0A%22http%3A//www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20XHTML%201.0%20Frameset//EN%22%20%22http%3A//www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"2d57d9c9c46d222676eeb8e8531f3a77ca7aa4c0":[async_test('html5lib_doctype01.html 2d57d9c9c46d222676eeb8e8531f3a77ca7aa4c0'), "%3C%21DOCTYPE%20root-element%20%5BSYSTEM%20OR%20PUBLIC%20FPI%5D%20%22uri%22%20%5B%20%0A%3C%21--%20internal%20declarations%20--%3E%0A%5D%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20root-element%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%5D%3E%22"],"544240cd747ec56acbc607b6b8ad9f99de4c4085":[async_test('html5lib_doctype01.html 544240cd747ec56acbc607b6b8ad9f99de4c4085'), "%3C%21DOCTYPE%20html%20PUBLIC%0A%20%20%22-//WAPFORUM//DTD%20XHTML%20Mobile%201.0//EN%22%0A%20%20%20%20%22http%3A//www.wapforum.org/DTD/xhtml-mobile10.dtd%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//WAPFORUM//DTD%20XHTML%20Mobile%201.0//EN%22%20%22http%3A//www.wapforum.org/DTD/xhtml-mobile10.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"af212d55a1c59b5e3658fb39a2f86daab3686f94":[async_test('html5lib_doctype01.html af212d55a1c59b5e3658fb39a2f86daab3686f94'), "%3C%21DOCTYPE%20HTML%20SYSTEM%20%22http%3A//www.w3.org/DTD/HTML4-strict.dtd%22%3E%3Cbody%3E%3Cb%3EMine%21%3C/b%3E%3C/body%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22%22%20%22http%3A//www.w3.org/DTD/HTML4-strict.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22Mine%21%22"],"10d43f89829998731c313c85eb31e981961b1981":[async_test('html5lib_doctype01.html 10d43f89829998731c313c85eb31e981961b1981'), "%3C%21DOCTYPE%20HTML%20PUBLIC%20%22-//W3C//DTD%20HTML%204.01//EN%22%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20HTML%204.01//EN%22%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"99d16cdde20971b45a3b81ca7a2b9d9858525b22":[async_test('html5lib_doctype01.html 99d16cdde20971b45a3b81ca7a2b9d9858525b22'), "%3C%21DOCTYPE%20HTML%20PUBLIC%20%22-//W3C//DTD%20HTML%204.01//EN%22%27http%3A//www.w3.org/TR/html4/strict.dtd%27%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20HTML%204.01//EN%22%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"44ac65856abd110419aafffde69f0dc127f98ec3":[async_test('html5lib_doctype01.html 44ac65856abd110419aafffde69f0dc127f98ec3'), "%3C%21DOCTYPE%20HTML%20PUBLIC%22-//W3C//DTD%20HTML%204.01//EN%22%27http%3A//www.w3.org/TR/html4/strict.dtd%27%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20HTML%204.01//EN%22%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"a69ffa9525892dbb38be6ee78f4fe1216ef6ea5d":[async_test('html5lib_doctype01.html a69ffa9525892dbb38be6ee78f4fe1216ef6ea5d'), "%3C%21DOCTYPE%20HTML%20PUBLIC%27-//W3C//DTD%20HTML%204.01//EN%27%27http%3A//www.w3.org/TR/html4/strict.dtd%27%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20HTML%204.01//EN%22%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_domjs-unsafe.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_domjs-unsafe.html
new file mode 100644
index 0000000000..6a8c9d5064
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_domjs-unsafe.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_domjs-unsafe.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['b76df4b192c11a5ef7a3e895a7dd2aaea2dcc5c7','da5cf60e732fcf20d288e772e51a7a9ccd3471e4','c32f77685b52841e1685110a8a6fac17b3ae127a','4fd39e063b0ad1fa82274d6de3122859f76934a8','7208fbe8702c37cf180b1c736c984f5340517408','7b5dd0bcb2a92ebf1974a0ec6017a798e774ca5b','54c3b7008093eea8c5b45f729585097df53641a8','b8de3bd0272a8362093670968eb21ad0bfa3cd40','40033d4270de7ad3595607426efc221988100175','4ffebb41a5c7f9239ba71301793a1e1cfd65dece','4fa68424ed093da8a6c954d64bb154ef4269ea83','41ddb90fa2e7785945dd18c32736dc83c1440fa4','89466f9770c893ffb117d18eac80993bd69564ad','72186fc6836326792e7e46ce4b5432191a5ac192','9863747db1e2676bad75c9929d895db2bb02306c','01818845dd1283fc54707a140be17e689a91f013','3255082e063beec09c3b0e605f0e3bfae177a113','54cd599d050ee568b0ed0060d1926479d3e528c3','bf081558b113ea712baeeb3294f3b53845510996','25e3471f40f3b6b0b448f7f7be41f0ee06403faa','006df46be5600e3d99caaf7dfc6a8af1636fd43a','b9bea556d1a155ef04e3f583328d51cf95995945','9eff404da18d0c20765a19613c718a9f49b874ac','1d246a13e96719a3fb48f1a81d1218671ee3a472','92bb86082f237803c7ace5fcd41a36861b364551','3a89dee49af0d2b537ad0c50fb74bb68d4dfe651','89bd023b6e2cbc5c7a37d85b45eadc88143bd670','9574dbbf3302c84fc4ddb965bf1f43ae7106e68c','341226c7328f8d3d6ab1d373368cc14aad712f20','15b3a23030aed47b3795d9c1ef11cd241b6902cb','4b318ddd18e0e360e70df9da138eabb002e34666','5a107feb60f518e1bed5fbd44de19f10dfde1d1e','92d4f652bcf5160b9815ea29546bde415cb83b04','42ebb8a722e7940826975575b2cb422c0a112c68','1a761bd94b707be3a5bd87fbcce0e0873c0b60ab','2fc6eb9f9eb3e005eb3c4765f638203ce0c34f5f','57672cdb7506becfcf63d59cc06f88d7e240808c','59ad4ed1b502b7adb49cf799c107bf405ddc0cca','4f00716246c38ec50813c6dd1e17b1e87a399252','767cec7804216f7b7bf6565a735c683fb2282059','c3258281fe46017ff5ea19cb08c711a6c281d673','755e6ed1b6f991af24c720c51546b5e0f6ef9ca5','3fb041d606429c494f972b82556759cf1a22171f','2f2ff31150ecec96cae1417fab3c4bda7e626948','6253f7f734608919f9966365fbbdd42163af7af2','db7da9a182140259b590eedd078f64b4831df749','9db35cf980c7170803f09243fb2f631ea0558adc','a96cbc244801cd9c8e201fa5a35e29dbb2a6cf08','920ab711970123a6123c8aa41190b63862399a5b',];
+ var tests = {
+ "b76df4b192c11a5ef7a3e895a7dd2aaea2dcc5c7":[async_test('html5lib_domjs-unsafe.html b76df4b192c11a5ef7a3e895a7dd2aaea2dcc5c7'), "%3Csvg%3E%3C%21%5BCDATA%5Bfoo%0Abar%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%0Abar%22"],"da5cf60e732fcf20d288e772e51a7a9ccd3471e4":[async_test('html5lib_domjs-unsafe.html da5cf60e732fcf20d288e772e51a7a9ccd3471e4'), "%3Csvg%3E%3C%21%5BCDATA%5Bfoo%0Dbar%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%0Abar%22"],"c32f77685b52841e1685110a8a6fac17b3ae127a":[async_test('html5lib_domjs-unsafe.html c32f77685b52841e1685110a8a6fac17b3ae127a'), "%3Csvg%3E%3C%21%5BCDATA%5Bfoo%0D%0Abar%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%0Abar%22"],"4fd39e063b0ad1fa82274d6de3122859f76934a8":[async_test('html5lib_domjs-unsafe.html 4fd39e063b0ad1fa82274d6de3122859f76934a8'), "%3Cscript%3Ea%3D%27%00%27%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22a%3D%27%EF%BF%BD%27%22%0A%7C%20%20%20%3Cbody%3E"],"7208fbe8702c37cf180b1c736c984f5340517408":[async_test('html5lib_domjs-unsafe.html 7208fbe8702c37cf180b1c736c984f5340517408'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%00%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%EF%BF%BD%22%0A%7C%20%20%20%3Cbody%3E"],"7b5dd0bcb2a92ebf1974a0ec6017a798e774ca5b":[async_test('html5lib_domjs-unsafe.html 7b5dd0bcb2a92ebf1974a0ec6017a798e774ca5b'), "%3Cscript%20type%3D%22data%22%3E%3C%21--foo%00%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--foo%EF%BF%BD%22%0A%7C%20%20%20%3Cbody%3E"],"54c3b7008093eea8c5b45f729585097df53641a8":[async_test('html5lib_domjs-unsafe.html 54c3b7008093eea8c5b45f729585097df53641a8'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%20foo-%00%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%20foo-%EF%BF%BD%22%0A%7C%20%20%20%3Cbody%3E"],"b8de3bd0272a8362093670968eb21ad0bfa3cd40":[async_test('html5lib_domjs-unsafe.html b8de3bd0272a8362093670968eb21ad0bfa3cd40'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%20foo--%00%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%20foo--%EF%BF%BD%22%0A%7C%20%20%20%3Cbody%3E"],"40033d4270de7ad3595607426efc221988100175":[async_test('html5lib_domjs-unsafe.html 40033d4270de7ad3595607426efc221988100175'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%20foo-", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%20foo-%22%0A%7C%20%20%20%3Cbody%3E"],"4ffebb41a5c7f9239ba71301793a1e1cfd65dece":[async_test('html5lib_domjs-unsafe.html 4ffebb41a5c7f9239ba71301793a1e1cfd65dece'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%20foo-%3C%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%20foo-%3C%22%0A%7C%20%20%20%3Cbody%3E"],"4fa68424ed093da8a6c954d64bb154ef4269ea83":[async_test('html5lib_domjs-unsafe.html 4fa68424ed093da8a6c954d64bb154ef4269ea83'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%20foo-%3CS", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%20foo-%3CS%22%0A%7C%20%20%20%3Cbody%3E"],"41ddb90fa2e7785945dd18c32736dc83c1440fa4":[async_test('html5lib_domjs-unsafe.html 41ddb90fa2e7785945dd18c32736dc83c1440fa4'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%20foo-%3C/SCRIPT%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%20foo-%22%0A%7C%20%20%20%3Cbody%3E"],"89466f9770c893ffb117d18eac80993bd69564ad":[async_test('html5lib_domjs-unsafe.html 89466f9770c893ffb117d18eac80993bd69564ad'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cp%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cp%3E%22%0A%7C%20%20%20%3Cbody%3E"],"72186fc6836326792e7e46ce4b5432191a5ac192":[async_test('html5lib_domjs-unsafe.html 72186fc6836326792e7e46ce4b5432191a5ac192'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E%3C/script%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"9863747db1e2676bad75c9929d895db2bb02306c":[async_test('html5lib_domjs-unsafe.html 9863747db1e2676bad75c9929d895db2bb02306c'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E%00%3C/script%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%EF%BF%BD%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"01818845dd1283fc54707a140be17e689a91f013":[async_test('html5lib_domjs-unsafe.html 01818845dd1283fc54707a140be17e689a91f013'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E-%00%3C/script%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E-%EF%BF%BD%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"3255082e063beec09c3b0e605f0e3bfae177a113":[async_test('html5lib_domjs-unsafe.html 3255082e063beec09c3b0e605f0e3bfae177a113'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E--%00%3C/script%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E--%EF%BF%BD%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"54cd599d050ee568b0ed0060d1926479d3e528c3":[async_test('html5lib_domjs-unsafe.html 54cd599d050ee568b0ed0060d1926479d3e528c3'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E---%3C/script%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E---%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"bf081558b113ea712baeeb3294f3b53845510996":[async_test('html5lib_domjs-unsafe.html bf081558b113ea712baeeb3294f3b53845510996'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E%3C/scrip%3E%3C/SCRIPT%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/scrip%3E%3C/SCRIPT%3E%22%0A%7C%20%20%20%3Cbody%3E"],"25e3471f40f3b6b0b448f7f7be41f0ee06403faa":[async_test('html5lib_domjs-unsafe.html 25e3471f40f3b6b0b448f7f7be41f0ee06403faa'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E%3C/scrip%20%3C/SCRIPT%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/scrip%20%3C/SCRIPT%3E%22%0A%7C%20%20%20%3Cbody%3E"],"006df46be5600e3d99caaf7dfc6a8af1636fd43a":[async_test('html5lib_domjs-unsafe.html 006df46be5600e3d99caaf7dfc6a8af1636fd43a'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3Cscript%3E%3C/scrip/%3C/SCRIPT%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/scrip/%3C/SCRIPT%3E%22%0A%7C%20%20%20%3Cbody%3E"],"b9bea556d1a155ef04e3f583328d51cf95995945":[async_test('html5lib_domjs-unsafe.html b9bea556d1a155ef04e3f583328d51cf95995945'), "%3Cscript%20type%3D%22data%22%3E%3C/scrip/%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C/scrip/%3E%22%0A%7C%20%20%20%3Cbody%3E"],"9eff404da18d0c20765a19613c718a9f49b874ac":[async_test('html5lib_domjs-unsafe.html 9eff404da18d0c20765a19613c718a9f49b874ac'), "%3Cscript%20type%3D%22data%22%3E%3C/scrip%20%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C/scrip%20%3E%22%0A%7C%20%20%20%3Cbody%3E"],"1d246a13e96719a3fb48f1a81d1218671ee3a472":[async_test('html5lib_domjs-unsafe.html 1d246a13e96719a3fb48f1a81d1218671ee3a472'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3C/scrip%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C/scrip%3E%22%0A%7C%20%20%20%3Cbody%3E"],"92bb86082f237803c7ace5fcd41a36861b364551":[async_test('html5lib_domjs-unsafe.html 92bb86082f237803c7ace5fcd41a36861b364551'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3C/scrip%20%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C/scrip%20%22%0A%7C%20%20%20%3Cbody%3E"],"3a89dee49af0d2b537ad0c50fb74bb68d4dfe651":[async_test('html5lib_domjs-unsafe.html 3a89dee49af0d2b537ad0c50fb74bb68d4dfe651'), "%3Cscript%20type%3D%22data%22%3E%3C%21--%3C/scrip/%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22data%22%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C/scrip/%22%0A%7C%20%20%20%3Cbody%3E"],"89bd023b6e2cbc5c7a37d85b45eadc88143bd670":[async_test('html5lib_domjs-unsafe.html 89bd023b6e2cbc5c7a37d85b45eadc88143bd670'), "%3C%21DOCTYPE%20html%3E%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"9574dbbf3302c84fc4ddb965bf1f43ae7106e68c":[async_test('html5lib_domjs-unsafe.html 9574dbbf3302c84fc4ddb965bf1f43ae7106e68c'), "%3Chtml%3E%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"341226c7328f8d3d6ab1d373368cc14aad712f20":[async_test('html5lib_domjs-unsafe.html 341226c7328f8d3d6ab1d373368cc14aad712f20'), "%3Chtml%3E%3Chead%3E%3C%21DOCTYPE%20html%3E%3C/head%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"15b3a23030aed47b3795d9c1ef11cd241b6902cb":[async_test('html5lib_domjs-unsafe.html 15b3a23030aed47b3795d9c1ef11cd241b6902cb'), "%3Chtml%3E%3Chead%3E%3C/head%3E%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"4b318ddd18e0e360e70df9da138eabb002e34666":[async_test('html5lib_domjs-unsafe.html 4b318ddd18e0e360e70df9da138eabb002e34666'), "%3Cbody%3E%3C/body%3E%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"5a107feb60f518e1bed5fbd44de19f10dfde1d1e":[async_test('html5lib_domjs-unsafe.html 5a107feb60f518e1bed5fbd44de19f10dfde1d1e'), "%3Ctable%3E%3C%21DOCTYPE%20html%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"92d4f652bcf5160b9815ea29546bde415cb83b04":[async_test('html5lib_domjs-unsafe.html 92d4f652bcf5160b9815ea29546bde415cb83b04'), "%3Cselect%3E%3C%21DOCTYPE%20html%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"42ebb8a722e7940826975575b2cb422c0a112c68":[async_test('html5lib_domjs-unsafe.html 42ebb8a722e7940826975575b2cb422c0a112c68'), "%3Ctable%3E%3Ccolgroup%3E%3C%21DOCTYPE%20html%3E%3C/colgroup%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E"],"1a761bd94b707be3a5bd87fbcce0e0873c0b60ab":[async_test('html5lib_domjs-unsafe.html 1a761bd94b707be3a5bd87fbcce0e0873c0b60ab'), "%3Ctable%3E%3Ccolgroup%3E%3C%21--test--%3E%3C/colgroup%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3C%21--%20test%20--%3E"],"2fc6eb9f9eb3e005eb3c4765f638203ce0c34f5f":[async_test('html5lib_domjs-unsafe.html 2fc6eb9f9eb3e005eb3c4765f638203ce0c34f5f'), "%3Ctable%3E%3Ccolgroup%3E%3Chtml%3E%3C/colgroup%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E"],"57672cdb7506becfcf63d59cc06f88d7e240808c":[async_test('html5lib_domjs-unsafe.html 57672cdb7506becfcf63d59cc06f88d7e240808c'), "%3Ctable%3E%3Ccolgroup%3E%20foo%3C/colgroup%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22"],"59ad4ed1b502b7adb49cf799c107bf405ddc0cca":[async_test('html5lib_domjs-unsafe.html 59ad4ed1b502b7adb49cf799c107bf405ddc0cca'), "%3Cselect%3E%3C%21--test--%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3C%21--%20test%20--%3E"],"4f00716246c38ec50813c6dd1e17b1e87a399252":[async_test('html5lib_domjs-unsafe.html 4f00716246c38ec50813c6dd1e17b1e87a399252'), "%3Cselect%3E%3Chtml%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"767cec7804216f7b7bf6565a735c683fb2282059":[async_test('html5lib_domjs-unsafe.html 767cec7804216f7b7bf6565a735c683fb2282059'), "%3Cframeset%3E%3Chtml%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"c3258281fe46017ff5ea19cb08c711a6c281d673":[async_test('html5lib_domjs-unsafe.html c3258281fe46017ff5ea19cb08c711a6c281d673'), "%3Cframeset%3E%3C/frameset%3E%3Chtml%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"755e6ed1b6f991af24c720c51546b5e0f6ef9ca5":[async_test('html5lib_domjs-unsafe.html 755e6ed1b6f991af24c720c51546b5e0f6ef9ca5'), "%3Cframeset%3E%3C/frameset%3E%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"3fb041d606429c494f972b82556759cf1a22171f":[async_test('html5lib_domjs-unsafe.html 3fb041d606429c494f972b82556759cf1a22171f'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%3C/html%3E%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"2f2ff31150ecec96cae1417fab3c4bda7e626948":[async_test('html5lib_domjs-unsafe.html 2f2ff31150ecec96cae1417fab3c4bda7e626948'), "%3Csvg%3E%3C%21DOCTYPE%20html%3E%3C/svg%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"6253f7f734608919f9966365fbbdd42163af7af2":[async_test('html5lib_domjs-unsafe.html 6253f7f734608919f9966365fbbdd42163af7af2'), "%3Csvg%3E%3Cfont%3E%3C/font%3E%3C/svg%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20font%3E"],"db7da9a182140259b590eedd078f64b4831df749":[async_test('html5lib_domjs-unsafe.html db7da9a182140259b590eedd078f64b4831df749'), "%3Csvg%3E%3Cfont%20id%3Dfoo%3E%3C/font%3E%3C/svg%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20font%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22foo%22"],"9db35cf980c7170803f09243fb2f631ea0558adc":[async_test('html5lib_domjs-unsafe.html 9db35cf980c7170803f09243fb2f631ea0558adc'), "%3Csvg%3E%3Cfont%20size%3D4%3E%3C/font%3E%3C/svg%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20size%3D%224%22"],"a96cbc244801cd9c8e201fa5a35e29dbb2a6cf08":[async_test('html5lib_domjs-unsafe.html a96cbc244801cd9c8e201fa5a35e29dbb2a6cf08'), "%3Csvg%3E%3Cfont%20color%3Dred%3E%3C/font%3E%3C/svg%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20color%3D%22red%22"],"920ab711970123a6123c8aa41190b63862399a5b":[async_test('html5lib_domjs-unsafe.html 920ab711970123a6123c8aa41190b63862399a5b'), "%3Csvg%3E%3Cfont%20font%3Dsans%3E%3C/font%3E%3C/svg%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20font%3E%0A%7C%20%20%20%20%20%20%20%20%20font%3D%22sans%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_entities01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_entities01.html
new file mode 100644
index 0000000000..8f0b96e623
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_entities01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_entities01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['16c694bcf0b3ff3723fa070eea7e1e82ef12a337','05e04b39ef06e2367a33326f5dd566913aa6628f','fbf7d9fec595585869c5c595d5588b34fd175278','e59b0a76d7bcfb429b27e00e469f35e08a9bdd1a','5ea854d6ecd4d6dd459cb36d4faf3ed36e11c073','119cd15b852615cd0fce759769b4a3788595e3bb','903cefcfae1125cb71fc77f4a6b7d3546e8f4020','69f08b40c7506153e809415ca98e2ed98992216b','9c00a1833e8cf4af28c8bd94902412ad7052b4b0','b5bcdcbc6e88b380be0e48ca2620fbbb8e92e497','bf6c90305b2856c2d9c9a146dfff867fe7a5e0f3','6b9c8d175a3d7b6cf04ffd72e44a7dc88686460f','76c184d9ce64b8a52c2e67eafeb8d332c096f2be','4c30f8f931eb44c2f208e837555c0cc444dd4612','1db77ef761092d65ce847c0bcd6e7cb892db754d','284c18aa40a73e4052525a9ffb30b23182f237ea','6b336a43e394d3ab7ceb2ab54c63409e8a27aded','436c30dd76bf9b5c2b29a181d9a9412ec0ab4bdf','1373a52ddcb71f20f29d92abb6714eaabeba7424','d60f4f324a1ad9c09c4d3590c8c537af2852eeb4','d21511e2df56c306c78e1449c960c66e565e016e','39107d16f24d4c7bcd40ad1239b5f4f677877ee8','a44b740e8b2349e75c9eb0376f665eab13ff821d','a0e38b1c19eba037b34c68864634cff032f0b892','390d9571a24be0961c8fcd78c69eea16a6414246','ceba8404405dd3b3b423c45411bde15bf72a846d','f8dd2fccc21d3a08790a7877186840a692adf111','d526830d439d3c4e966b22fbedf819d465d3107b','240af7bacbfecce6e2a973de9c89fad817fb8d42','d657585ca1df5b86693fca8a0a2eae76bd9b1c2b','b508fcffb2d9f2424c7837270e51824321fb4570','897ab551df27df14418a46ff1c3acef8338c53f3','493a472ccf903088c813ff6874d54482a161df8f','67d19edb1f6ecbbb6ccb90df2345e52e5c58efc3','dded422b7406c966c944555f220d7d3dfcf2a143','60a76c9e10e4ac53f836f9e45eb0518dd0b7b73b','b47fcc6a614247319908b00935f10ec134399917','3b5c7d0331ae900e1179eaf3545c78d147434fbf','a0f119508046dbb4f8059232f6e99f66c1e8e7a6','daab384be8471edeb755353c5dccaad0c415dac5','8485e4d103a517615f39c0d0b71fe5065c5437db','4c28749faddb096d1f04792b7daf039268c43181','c84c576954c4c493528eaa34233c926653152be5','b2797e18c499df32296545225c259dbf4bea2908','5b2dfe6f187413faecd91336bd353c05768ea722','34af7be5bcff18ec869a306b19daea70f61f7088','8f2e74688427858fc1a895fc472d074a0528a7ae','530d6251a43d688e69959237e519812585de8266','bced9b8c339d0d2838ea0fffe9a64027b4f3a877','9c9e8079df25999c606bc84f46b348544a23b9b4','ec61d22b3bc6f93e54bbff964311bba9a3a06b21','391f0136aebdd0e874c8eb85651ffda7e9f86f24','e2974e7029b008539aacc1ee885705764f8c53f4','23e105ca5329cc0338a96fe7e088ba9b319c46d1','87af28752724c400edef3970e52440639be5b1a8','f50d9e39e2bda3d8c0b1c69d1f1e4e86c4b39ac3','12827fadb8d36b829e9c2c7315e0848d2d7ef278','f603997321070e6ccada6fefe4240a9e6c7e870a','a88e381bf36e74aaa8ac5b0877153b7158bae579','11019fa64a25748a5bfb864fc200ec2710b54aa3','3d238b9146102bd11e898ff4913f86e8ded65be4','a1375bfde7be56e514471700e030b1c7e7090e2f','7936b73efa385d183e93453bea24fd0c4dff4742','41cdf6978b0c48e7044d5e4534fc8bb08de4cdf2','5138f572a4db2e2edc2d723e1bb87af72ab501f4','f30757617b6df330deba1cb607e8d47f71bda13f','a6fd8cdca1fa8cf07519d9a0c5b779eafa438b70','ada342466887e85d89c3b815b127bfced036ac76','74bd99a9263f0b8e8a5fac4d2684fe37d5a1a9cc','bffe7b00046407080251ab6bf58cb97ce2a34893','5aef37f1f2b9ac45adfade044c882eb09a297569','6e2d817539fb3b2023c7bcb88ad220c136f70cf0','d4ac52727ff405f61a1d878a0aa1951ae5264c80','d2584faaa4dda5283955b2dc22812a018d04a72d','56dc3e612fbfa06cfeb26957e357defcf73aa220',];
+ var tests = {
+ "16c694bcf0b3ff3723fa070eea7e1e82ef12a337":[async_test('html5lib_entities01.html 16c694bcf0b3ff3723fa070eea7e1e82ef12a337'), "FOO%26gt%3BBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%3EBAR%22"],"05e04b39ef06e2367a33326f5dd566913aa6628f":[async_test('html5lib_entities01.html 05e04b39ef06e2367a33326f5dd566913aa6628f'), "FOO%26gtBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%3EBAR%22"],"fbf7d9fec595585869c5c595d5588b34fd175278":[async_test('html5lib_entities01.html fbf7d9fec595585869c5c595d5588b34fd175278'), "FOO%26gt%20BAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%3E%20BAR%22"],"e59b0a76d7bcfb429b27e00e469f35e08a9bdd1a":[async_test('html5lib_entities01.html e59b0a76d7bcfb429b27e00e469f35e08a9bdd1a'), "FOO%26gt%3B%3B%3BBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%3E%3B%3BBAR%22"],"5ea854d6ecd4d6dd459cb36d4faf3ed36e11c073":[async_test('html5lib_entities01.html 5ea854d6ecd4d6dd459cb36d4faf3ed36e11c073'), "I%27m%20%26notit%3B%20I%20tell%20you", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22I%27m%20%C2%ACit%3B%20I%20tell%20you%22"],"119cd15b852615cd0fce759769b4a3788595e3bb":[async_test('html5lib_entities01.html 119cd15b852615cd0fce759769b4a3788595e3bb'), "I%27m%20%26notin%3B%20I%20tell%20you", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22I%27m%20%E2%88%89%20I%20tell%20you%22"],"903cefcfae1125cb71fc77f4a6b7d3546e8f4020":[async_test('html5lib_entities01.html 903cefcfae1125cb71fc77f4a6b7d3546e8f4020'), "%26ammmp%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26ammmp%3B%22"],"69f08b40c7506153e809415ca98e2ed98992216b":[async_test('html5lib_entities01.html 69f08b40c7506153e809415ca98e2ed98992216b'), "%26ammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmp%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26ammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmp%3B%22"],"9c00a1833e8cf4af28c8bd94902412ad7052b4b0":[async_test('html5lib_entities01.html 9c00a1833e8cf4af28c8bd94902412ad7052b4b0'), "FOO%26%20BAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%26%20BAR%22"],"b5bcdcbc6e88b380be0e48ca2620fbbb8e92e497":[async_test('html5lib_entities01.html b5bcdcbc6e88b380be0e48ca2620fbbb8e92e497'), "FOO%26%3CBAR%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%26%22%0A%7C%20%20%20%20%20%3Cbar%3E"],"bf6c90305b2856c2d9c9a146dfff867fe7a5e0f3":[async_test('html5lib_entities01.html bf6c90305b2856c2d9c9a146dfff867fe7a5e0f3'), "FOO%26%26%26%26gt%3BBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%26%26%26%3EBAR%22"],"6b9c8d175a3d7b6cf04ffd72e44a7dc88686460f":[async_test('html5lib_entities01.html 6b9c8d175a3d7b6cf04ffd72e44a7dc88686460f'), "FOO%26%2341%3BBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%29BAR%22"],"76c184d9ce64b8a52c2e67eafeb8d332c096f2be":[async_test('html5lib_entities01.html 76c184d9ce64b8a52c2e67eafeb8d332c096f2be'), "FOO%26%23x41%3BBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOOABAR%22"],"4c30f8f931eb44c2f208e837555c0cc444dd4612":[async_test('html5lib_entities01.html 4c30f8f931eb44c2f208e837555c0cc444dd4612'), "FOO%26%23X41%3BBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOOABAR%22"],"1db77ef761092d65ce847c0bcd6e7cb892db754d":[async_test('html5lib_entities01.html 1db77ef761092d65ce847c0bcd6e7cb892db754d'), "FOO%26%23BAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%26%23BAR%22"],"284c18aa40a73e4052525a9ffb30b23182f237ea":[async_test('html5lib_entities01.html 284c18aa40a73e4052525a9ffb30b23182f237ea'), "FOO%26%23ZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%26%23ZOO%22"],"6b336a43e394d3ab7ceb2ab54c63409e8a27aded":[async_test('html5lib_entities01.html 6b336a43e394d3ab7ceb2ab54c63409e8a27aded'), "FOO%26%23xBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C2%BAR%22"],"436c30dd76bf9b5c2b29a181d9a9412ec0ab4bdf":[async_test('html5lib_entities01.html 436c30dd76bf9b5c2b29a181d9a9412ec0ab4bdf'), "FOO%26%23xZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%26%23xZOO%22"],"1373a52ddcb71f20f29d92abb6714eaabeba7424":[async_test('html5lib_entities01.html 1373a52ddcb71f20f29d92abb6714eaabeba7424'), "FOO%26%23XZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%26%23XZOO%22"],"d60f4f324a1ad9c09c4d3590c8c537af2852eeb4":[async_test('html5lib_entities01.html d60f4f324a1ad9c09c4d3590c8c537af2852eeb4'), "FOO%26%2341BAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%29BAR%22"],"d21511e2df56c306c78e1449c960c66e565e016e":[async_test('html5lib_entities01.html d21511e2df56c306c78e1449c960c66e565e016e'), "FOO%26%23x41BAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E4%86%BAR%22"],"39107d16f24d4c7bcd40ad1239b5f4f677877ee8":[async_test('html5lib_entities01.html 39107d16f24d4c7bcd40ad1239b5f4f677877ee8'), "FOO%26%23x41ZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOOAZOO%22"],"a44b740e8b2349e75c9eb0376f665eab13ff821d":[async_test('html5lib_entities01.html a44b740e8b2349e75c9eb0376f665eab13ff821d'), "FOO%26%23x0000%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"a0e38b1c19eba037b34c68864634cff032f0b892":[async_test('html5lib_entities01.html a0e38b1c19eba037b34c68864634cff032f0b892'), "FOO%26%23x0078%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOOxZOO%22"],"390d9571a24be0961c8fcd78c69eea16a6414246":[async_test('html5lib_entities01.html 390d9571a24be0961c8fcd78c69eea16a6414246'), "FOO%26%23x0079%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOOyZOO%22"],"ceba8404405dd3b3b423c45411bde15bf72a846d":[async_test('html5lib_entities01.html ceba8404405dd3b3b423c45411bde15bf72a846d'), "FOO%26%23x0080%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%82%ACZOO%22"],"f8dd2fccc21d3a08790a7877186840a692adf111":[async_test('html5lib_entities01.html f8dd2fccc21d3a08790a7877186840a692adf111'), "FOO%26%23x0081%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C2%81ZOO%22"],"d526830d439d3c4e966b22fbedf819d465d3107b":[async_test('html5lib_entities01.html d526830d439d3c4e966b22fbedf819d465d3107b'), "FOO%26%23x0082%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%9AZOO%22"],"240af7bacbfecce6e2a973de9c89fad817fb8d42":[async_test('html5lib_entities01.html 240af7bacbfecce6e2a973de9c89fad817fb8d42'), "FOO%26%23x0083%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C6%92ZOO%22"],"d657585ca1df5b86693fca8a0a2eae76bd9b1c2b":[async_test('html5lib_entities01.html d657585ca1df5b86693fca8a0a2eae76bd9b1c2b'), "FOO%26%23x0084%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%9EZOO%22"],"b508fcffb2d9f2424c7837270e51824321fb4570":[async_test('html5lib_entities01.html b508fcffb2d9f2424c7837270e51824321fb4570'), "FOO%26%23x0085%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%A6ZOO%22"],"897ab551df27df14418a46ff1c3acef8338c53f3":[async_test('html5lib_entities01.html 897ab551df27df14418a46ff1c3acef8338c53f3'), "FOO%26%23x0086%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%A0ZOO%22"],"493a472ccf903088c813ff6874d54482a161df8f":[async_test('html5lib_entities01.html 493a472ccf903088c813ff6874d54482a161df8f'), "FOO%26%23x0087%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%A1ZOO%22"],"67d19edb1f6ecbbb6ccb90df2345e52e5c58efc3":[async_test('html5lib_entities01.html 67d19edb1f6ecbbb6ccb90df2345e52e5c58efc3'), "FOO%26%23x0088%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%CB%86ZOO%22"],"dded422b7406c966c944555f220d7d3dfcf2a143":[async_test('html5lib_entities01.html dded422b7406c966c944555f220d7d3dfcf2a143'), "FOO%26%23x0089%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%B0ZOO%22"],"60a76c9e10e4ac53f836f9e45eb0518dd0b7b73b":[async_test('html5lib_entities01.html 60a76c9e10e4ac53f836f9e45eb0518dd0b7b73b'), "FOO%26%23x008A%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C5%A0ZOO%22"],"b47fcc6a614247319908b00935f10ec134399917":[async_test('html5lib_entities01.html b47fcc6a614247319908b00935f10ec134399917'), "FOO%26%23x008B%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%B9ZOO%22"],"3b5c7d0331ae900e1179eaf3545c78d147434fbf":[async_test('html5lib_entities01.html 3b5c7d0331ae900e1179eaf3545c78d147434fbf'), "FOO%26%23x008C%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C5%92ZOO%22"],"a0f119508046dbb4f8059232f6e99f66c1e8e7a6":[async_test('html5lib_entities01.html a0f119508046dbb4f8059232f6e99f66c1e8e7a6'), "FOO%26%23x008D%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C2%8DZOO%22"],"daab384be8471edeb755353c5dccaad0c415dac5":[async_test('html5lib_entities01.html daab384be8471edeb755353c5dccaad0c415dac5'), "FOO%26%23x008E%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C5%BDZOO%22"],"8485e4d103a517615f39c0d0b71fe5065c5437db":[async_test('html5lib_entities01.html 8485e4d103a517615f39c0d0b71fe5065c5437db'), "FOO%26%23x008F%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C2%8FZOO%22"],"4c28749faddb096d1f04792b7daf039268c43181":[async_test('html5lib_entities01.html 4c28749faddb096d1f04792b7daf039268c43181'), "FOO%26%23x0090%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C2%90ZOO%22"],"c84c576954c4c493528eaa34233c926653152be5":[async_test('html5lib_entities01.html c84c576954c4c493528eaa34233c926653152be5'), "FOO%26%23x0091%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%98ZOO%22"],"b2797e18c499df32296545225c259dbf4bea2908":[async_test('html5lib_entities01.html b2797e18c499df32296545225c259dbf4bea2908'), "FOO%26%23x0092%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%99ZOO%22"],"5b2dfe6f187413faecd91336bd353c05768ea722":[async_test('html5lib_entities01.html 5b2dfe6f187413faecd91336bd353c05768ea722'), "FOO%26%23x0093%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%9CZOO%22"],"34af7be5bcff18ec869a306b19daea70f61f7088":[async_test('html5lib_entities01.html 34af7be5bcff18ec869a306b19daea70f61f7088'), "FOO%26%23x0094%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%9DZOO%22"],"8f2e74688427858fc1a895fc472d074a0528a7ae":[async_test('html5lib_entities01.html 8f2e74688427858fc1a895fc472d074a0528a7ae'), "FOO%26%23x0095%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%A2ZOO%22"],"530d6251a43d688e69959237e519812585de8266":[async_test('html5lib_entities01.html 530d6251a43d688e69959237e519812585de8266'), "FOO%26%23x0096%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%93ZOO%22"],"bced9b8c339d0d2838ea0fffe9a64027b4f3a877":[async_test('html5lib_entities01.html bced9b8c339d0d2838ea0fffe9a64027b4f3a877'), "FOO%26%23x0097%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%94ZOO%22"],"9c9e8079df25999c606bc84f46b348544a23b9b4":[async_test('html5lib_entities01.html 9c9e8079df25999c606bc84f46b348544a23b9b4'), "FOO%26%23x0098%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%CB%9CZOO%22"],"ec61d22b3bc6f93e54bbff964311bba9a3a06b21":[async_test('html5lib_entities01.html ec61d22b3bc6f93e54bbff964311bba9a3a06b21'), "FOO%26%23x0099%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%84%A2ZOO%22"],"391f0136aebdd0e874c8eb85651ffda7e9f86f24":[async_test('html5lib_entities01.html 391f0136aebdd0e874c8eb85651ffda7e9f86f24'), "FOO%26%23x009A%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C5%A1ZOO%22"],"e2974e7029b008539aacc1ee885705764f8c53f4":[async_test('html5lib_entities01.html e2974e7029b008539aacc1ee885705764f8c53f4'), "FOO%26%23x009B%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%E2%80%BAZOO%22"],"23e105ca5329cc0338a96fe7e088ba9b319c46d1":[async_test('html5lib_entities01.html 23e105ca5329cc0338a96fe7e088ba9b319c46d1'), "FOO%26%23x009C%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C5%93ZOO%22"],"87af28752724c400edef3970e52440639be5b1a8":[async_test('html5lib_entities01.html 87af28752724c400edef3970e52440639be5b1a8'), "FOO%26%23x009D%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C2%9DZOO%22"],"f50d9e39e2bda3d8c0b1c69d1f1e4e86c4b39ac3":[async_test('html5lib_entities01.html f50d9e39e2bda3d8c0b1c69d1f1e4e86c4b39ac3'), "FOO%26%23x009E%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C5%BEZOO%22"],"12827fadb8d36b829e9c2c7315e0848d2d7ef278":[async_test('html5lib_entities01.html 12827fadb8d36b829e9c2c7315e0848d2d7ef278'), "FOO%26%23x009F%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C5%B8ZOO%22"],"f603997321070e6ccada6fefe4240a9e6c7e870a":[async_test('html5lib_entities01.html f603997321070e6ccada6fefe4240a9e6c7e870a'), "FOO%26%23x00A0%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%C2%A0ZOO%22"],"a88e381bf36e74aaa8ac5b0877153b7158bae579":[async_test('html5lib_entities01.html a88e381bf36e74aaa8ac5b0877153b7158bae579'), "FOO%26%23xD7FF%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%ED%9F%BFZOO%22"],"11019fa64a25748a5bfb864fc200ec2710b54aa3":[async_test('html5lib_entities01.html 11019fa64a25748a5bfb864fc200ec2710b54aa3'), "FOO%26%23xD800%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"3d238b9146102bd11e898ff4913f86e8ded65be4":[async_test('html5lib_entities01.html 3d238b9146102bd11e898ff4913f86e8ded65be4'), "FOO%26%23xD801%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"a1375bfde7be56e514471700e030b1c7e7090e2f":[async_test('html5lib_entities01.html a1375bfde7be56e514471700e030b1c7e7090e2f'), "FOO%26%23xDFFE%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"7936b73efa385d183e93453bea24fd0c4dff4742":[async_test('html5lib_entities01.html 7936b73efa385d183e93453bea24fd0c4dff4742'), "FOO%26%23xDFFF%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"41cdf6978b0c48e7044d5e4534fc8bb08de4cdf2":[async_test('html5lib_entities01.html 41cdf6978b0c48e7044d5e4534fc8bb08de4cdf2'), "FOO%26%23xE000%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EE%80%80ZOO%22"],"5138f572a4db2e2edc2d723e1bb87af72ab501f4":[async_test('html5lib_entities01.html 5138f572a4db2e2edc2d723e1bb87af72ab501f4'), "FOO%26%23x10FFFE%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%F4%8F%BF%BEZOO%22"],"f30757617b6df330deba1cb607e8d47f71bda13f":[async_test('html5lib_entities01.html f30757617b6df330deba1cb607e8d47f71bda13f'), "FOO%26%23x1087D4%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%F4%88%9F%94ZOO%22"],"a6fd8cdca1fa8cf07519d9a0c5b779eafa438b70":[async_test('html5lib_entities01.html a6fd8cdca1fa8cf07519d9a0c5b779eafa438b70'), "FOO%26%23x10FFFF%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%F4%8F%BF%BFZOO%22"],"ada342466887e85d89c3b815b127bfced036ac76":[async_test('html5lib_entities01.html ada342466887e85d89c3b815b127bfced036ac76'), "FOO%26%23x110000%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"74bd99a9263f0b8e8a5fac4d2684fe37d5a1a9cc":[async_test('html5lib_entities01.html 74bd99a9263f0b8e8a5fac4d2684fe37d5a1a9cc'), "FOO%26%23xFFFFFF%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"bffe7b00046407080251ab6bf58cb97ce2a34893":[async_test('html5lib_entities01.html bffe7b00046407080251ab6bf58cb97ce2a34893'), "FOO%26%2311111111111", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BD%22"],"5aef37f1f2b9ac45adfade044c882eb09a297569":[async_test('html5lib_entities01.html 5aef37f1f2b9ac45adfade044c882eb09a297569'), "FOO%26%231111111111", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BD%22"],"6e2d817539fb3b2023c7bcb88ad220c136f70cf0":[async_test('html5lib_entities01.html 6e2d817539fb3b2023c7bcb88ad220c136f70cf0'), "FOO%26%23111111111111", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BD%22"],"d4ac52727ff405f61a1d878a0aa1951ae5264c80":[async_test('html5lib_entities01.html d4ac52727ff405f61a1d878a0aa1951ae5264c80'), "FOO%26%2311111111111ZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"d2584faaa4dda5283955b2dc22812a018d04a72d":[async_test('html5lib_entities01.html d2584faaa4dda5283955b2dc22812a018d04a72d'), "FOO%26%231111111111ZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],"56dc3e612fbfa06cfeb26957e357defcf73aa220":[async_test('html5lib_entities01.html 56dc3e612fbfa06cfeb26957e357defcf73aa220'), "FOO%26%23111111111111ZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%EF%BF%BDZOO%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_entities02.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_entities02.html
new file mode 100644
index 0000000000..20df8ed871
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_entities02.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_entities02.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['ea66863900b0b42deee5a77c58a432c2215c32ac','bc2a229b7c801ad045da76f411faf1d4c89886d0','e30755b30108f65919767f57a2200097b638f2b4','17c5acad9075755a413541d57d0d135338450834','ec40a7216b4ac918c5e16cae66bfb8b69bcfabce','54d2e9d13436e6850a5257e8028122581cf7088d','99c8496d0ea75429a5836de44dd18708974f6de8','7e4e70e57f63968ebba82682a2629158bd053a65','dca2db4f61b5fd60121e3da3e15065654f8d8a0c','fe22904d5f3936bedc1fa110e6bde48895b399a0','6553483a30141fcff05787287c2c212df9f468e8','88d7c74afcb27bbee3e3255d9116dce9c3dc6d73','db5d22d3350e0a51d675dc17c641c73251a4739d','ea08276faa7ba526e612fc1e80047d705cd29885','c59d1cfe1b36e75e0f57664e45bef7023a73c9e9','f9d3950620f8adcbe5f9a0542c7967de4be65963','8e35dacd7c296f054e58f1ce83719401c8aff8a0','48edddaa93bbebc5cd1615cc67422ca6508e47a2','9c69a29b53eebd93db20f12d405335274098e662','565c5f6744a27602bb466d6df77803a80f064752','742984a32ecd86cb9cdedffbba47eb212e19c80f','f908b529ac9ca5366e1160856db2c3d17e3898c9','1294ffc6bee2ee41f65a60ac48ba445b99504286','ba7d8cdd4b40020f7af6bdde75a3574b5771fac9','ce23051409f58749cbce6836bc4c7c21e9c548cf','6bbeec30b849cebd1366ebb2e6d2a6c1790e8c68',];
+ var tests = {
+ "ea66863900b0b42deee5a77c58a432c2215c32ac":[async_test('html5lib_entities02.html ea66863900b0b42deee5a77c58a432c2215c32ac'), "%3Cdiv%20bar%3D%22ZZ%26gt%3BYY%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%3EYY%22"],"bc2a229b7c801ad045da76f411faf1d4c89886d0":[async_test('html5lib_entities02.html bc2a229b7c801ad045da76f411faf1d4c89886d0'), "%3Cdiv%20bar%3D%22ZZ%26%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26%22"],"e30755b30108f65919767f57a2200097b638f2b4":[async_test('html5lib_entities02.html e30755b30108f65919767f57a2200097b638f2b4'), "%3Cdiv%20bar%3D%27ZZ%26%27%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26%22"],"17c5acad9075755a413541d57d0d135338450834":[async_test('html5lib_entities02.html 17c5acad9075755a413541d57d0d135338450834'), "%3Cdiv%20bar%3DZZ%26%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26%22"],"ec40a7216b4ac918c5e16cae66bfb8b69bcfabce":[async_test('html5lib_entities02.html ec40a7216b4ac918c5e16cae66bfb8b69bcfabce'), "%3Cdiv%20bar%3D%22ZZ%26gt%3DYY%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26gt%3DYY%22"],"54d2e9d13436e6850a5257e8028122581cf7088d":[async_test('html5lib_entities02.html 54d2e9d13436e6850a5257e8028122581cf7088d'), "%3Cdiv%20bar%3D%22ZZ%26gt0YY%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26gt0YY%22"],"99c8496d0ea75429a5836de44dd18708974f6de8":[async_test('html5lib_entities02.html 99c8496d0ea75429a5836de44dd18708974f6de8'), "%3Cdiv%20bar%3D%22ZZ%26gt9YY%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26gt9YY%22"],"7e4e70e57f63968ebba82682a2629158bd053a65":[async_test('html5lib_entities02.html 7e4e70e57f63968ebba82682a2629158bd053a65'), "%3Cdiv%20bar%3D%22ZZ%26gtaYY%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26gtaYY%22"],"dca2db4f61b5fd60121e3da3e15065654f8d8a0c":[async_test('html5lib_entities02.html dca2db4f61b5fd60121e3da3e15065654f8d8a0c'), "%3Cdiv%20bar%3D%22ZZ%26gtZYY%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26gtZYY%22"],"fe22904d5f3936bedc1fa110e6bde48895b399a0":[async_test('html5lib_entities02.html fe22904d5f3936bedc1fa110e6bde48895b399a0'), "%3Cdiv%20bar%3D%22ZZ%26gt%20YY%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%3E%20YY%22"],"6553483a30141fcff05787287c2c212df9f468e8":[async_test('html5lib_entities02.html 6553483a30141fcff05787287c2c212df9f468e8'), "%3Cdiv%20bar%3D%22ZZ%26gt%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%3E%22"],"88d7c74afcb27bbee3e3255d9116dce9c3dc6d73":[async_test('html5lib_entities02.html 88d7c74afcb27bbee3e3255d9116dce9c3dc6d73'), "%3Cdiv%20bar%3D%27ZZ%26gt%27%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%3E%22"],"db5d22d3350e0a51d675dc17c641c73251a4739d":[async_test('html5lib_entities02.html db5d22d3350e0a51d675dc17c641c73251a4739d'), "%3Cdiv%20bar%3DZZ%26gt%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%3E%22"],"ea08276faa7ba526e612fc1e80047d705cd29885":[async_test('html5lib_entities02.html ea08276faa7ba526e612fc1e80047d705cd29885'), "%3Cdiv%20bar%3D%22ZZ%26pound_id%3D23%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%C2%A3_id%3D23%22"],"c59d1cfe1b36e75e0f57664e45bef7023a73c9e9":[async_test('html5lib_entities02.html c59d1cfe1b36e75e0f57664e45bef7023a73c9e9'), "%3Cdiv%20bar%3D%22ZZ%26prod_id%3D23%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26prod_id%3D23%22"],"f9d3950620f8adcbe5f9a0542c7967de4be65963":[async_test('html5lib_entities02.html f9d3950620f8adcbe5f9a0542c7967de4be65963'), "%3Cdiv%20bar%3D%22ZZ%26pound%3B_id%3D23%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%C2%A3_id%3D23%22"],"8e35dacd7c296f054e58f1ce83719401c8aff8a0":[async_test('html5lib_entities02.html 8e35dacd7c296f054e58f1ce83719401c8aff8a0'), "%3Cdiv%20bar%3D%22ZZ%26prod%3B_id%3D23%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%E2%88%8F_id%3D23%22"],"48edddaa93bbebc5cd1615cc67422ca6508e47a2":[async_test('html5lib_entities02.html 48edddaa93bbebc5cd1615cc67422ca6508e47a2'), "%3Cdiv%20bar%3D%22ZZ%26pound%3D23%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26pound%3D23%22"],"9c69a29b53eebd93db20f12d405335274098e662":[async_test('html5lib_entities02.html 9c69a29b53eebd93db20f12d405335274098e662'), "%3Cdiv%20bar%3D%22ZZ%26prod%3D23%22%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22ZZ%26prod%3D23%22"],"565c5f6744a27602bb466d6df77803a80f064752":[async_test('html5lib_entities02.html 565c5f6744a27602bb466d6df77803a80f064752'), "%3Cdiv%3EZZ%26pound_id%3D23%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22ZZ%C2%A3_id%3D23%22"],"742984a32ecd86cb9cdedffbba47eb212e19c80f":[async_test('html5lib_entities02.html 742984a32ecd86cb9cdedffbba47eb212e19c80f'), "%3Cdiv%3EZZ%26prod_id%3D23%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22ZZ%26prod_id%3D23%22"],"f908b529ac9ca5366e1160856db2c3d17e3898c9":[async_test('html5lib_entities02.html f908b529ac9ca5366e1160856db2c3d17e3898c9'), "%3Cdiv%3EZZ%26pound%3B_id%3D23%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22ZZ%C2%A3_id%3D23%22"],"1294ffc6bee2ee41f65a60ac48ba445b99504286":[async_test('html5lib_entities02.html 1294ffc6bee2ee41f65a60ac48ba445b99504286'), "%3Cdiv%3EZZ%26prod%3B_id%3D23%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22ZZ%E2%88%8F_id%3D23%22"],"ba7d8cdd4b40020f7af6bdde75a3574b5771fac9":[async_test('html5lib_entities02.html ba7d8cdd4b40020f7af6bdde75a3574b5771fac9'), "%3Cdiv%3EZZ%26pound%3D23%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22ZZ%C2%A3%3D23%22"],"ce23051409f58749cbce6836bc4c7c21e9c548cf":[async_test('html5lib_entities02.html ce23051409f58749cbce6836bc4c7c21e9c548cf'), "%3Cdiv%3EZZ%26prod%3D23%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22ZZ%26prod%3D23%22"],"6bbeec30b849cebd1366ebb2e6d2a6c1790e8c68":[async_test('html5lib_entities02.html 6bbeec30b849cebd1366ebb2e6d2a6c1790e8c68'), "%3Cdiv%3EZZ%26AElig%3D%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22ZZ%C3%86%3D%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_html5test-com.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_html5test-com.html
new file mode 100644
index 0000000000..b74b7f9993
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_html5test-com.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_html5test-com.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['71bd5e6b9e907e65295b6d670627e0da4a8a65ed','32cd504d36a6db3584b716b3681ab4b0741423b3','f0bf0506a2d3e5ca4aa5f14a1f260e405882827e','666a215d91c4e83d99f4be4caebb67fd65569480','fd2cd459bdc79db754b24bc537758990d392b1fc','86be28614bf72e24c162d865c04d687447098867','be72b058e5be0f6aef2c442d83c92c0d251fcb7f','ab6e31cf52c8d57d6dfdcaf7165f1abf7bd5e73d','11240d9b03b14eb515d6a1d1595c5a409830ea38','809c1bebcded8f43981af902442ff8a2db5d2578','bcbeb84f40e56a642b794d514e97e3ec303d4a79','1cbb987dd0a35af3a5b2e4fc11eba36a60eba03d','5b5e75eca2f5c80e1c4d5676254b9891090e288e','93e966e2edad3297ecb159f3983bdd2dc84f829e','7a02a2d7ab875dbeedc9a34c6c27b6119bd6d1f0','46615acdb9dd6231e2a65fed5bcce7e19f086d03','381de12234a699cbfb775b3ca7c679f357e7403e','72736fc894b2077928559cc4284a102635cac898','290d5e7fa9684038411e78c4b0e0ade83150eeea','a5fb9cf46ed215a61d073ae4b7e7477ba49fa5d8','88dea19243733a9bda26c6f5290d2c4f5bbac157','9906bb30ae08654f4c67bf6d97040abbca91082d','c6cd25ce02329e4a42b9be9ba35453532fec7599','81af3f9d6ccc2a1a1a58ace18dd530c544675610',];
+ var tests = {
+ "71bd5e6b9e907e65295b6d670627e0da4a8a65ed":[async_test('html5lib_html5test-com.html 71bd5e6b9e907e65295b6d670627e0da4a8a65ed'), "%3Cdiv%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3Cdiv%3E"],"32cd504d36a6db3584b716b3681ab4b0741423b3":[async_test('html5lib_html5test-com.html 32cd504d36a6db3584b716b3681ab4b0741423b3'), "%3Cdiv%20foo%3Cbar%3D%27%27%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20foo%3Cbar%3D%22%22"],"f0bf0506a2d3e5ca4aa5f14a1f260e405882827e":[async_test('html5lib_html5test-com.html f0bf0506a2d3e5ca4aa5f14a1f260e405882827e'), "%3Cdiv%20foo%3D%60bar%60%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20foo%3D%22%60bar%60%22"],"666a215d91c4e83d99f4be4caebb67fd65569480":[async_test('html5lib_html5test-com.html 666a215d91c4e83d99f4be4caebb67fd65569480'), "%3Cdiv%20%5C%22foo%3D%27%27%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%5C%22foo%3D%22%22"],"fd2cd459bdc79db754b24bc537758990d392b1fc":[async_test('html5lib_html5test-com.html fd2cd459bdc79db754b24bc537758990d392b1fc'), "%3Ca%20href%3D%27%5Cnbar%27%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22%5Cnbar%22"],"86be28614bf72e24c162d865c04d687447098867":[async_test('html5lib_html5test-com.html 86be28614bf72e24c162d865c04d687447098867'), "%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"be72b058e5be0f6aef2c442d83c92c0d251fcb7f":[async_test('html5lib_html5test-com.html be72b058e5be0f6aef2c442d83c92c0d251fcb7f'), "%26lang%3B%26rang%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%9F%A8%E2%9F%A9%22"],"ab6e31cf52c8d57d6dfdcaf7165f1abf7bd5e73d":[async_test('html5lib_html5test-com.html ab6e31cf52c8d57d6dfdcaf7165f1abf7bd5e73d'), "%26apos%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%27%22"],"11240d9b03b14eb515d6a1d1595c5a409830ea38":[async_test('html5lib_html5test-com.html 11240d9b03b14eb515d6a1d1595c5a409830ea38'), "%26ImaginaryI%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%85%88%22"],"809c1bebcded8f43981af902442ff8a2db5d2578":[async_test('html5lib_html5test-com.html 809c1bebcded8f43981af902442ff8a2db5d2578'), "%26Kopf%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%F0%9D%95%82%22"],"bcbeb84f40e56a642b794d514e97e3ec303d4a79":[async_test('html5lib_html5test-com.html bcbeb84f40e56a642b794d514e97e3ec303d4a79'), "%26notinva%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%88%89%22"],"1cbb987dd0a35af3a5b2e4fc11eba36a60eba03d":[async_test('html5lib_html5test-com.html 1cbb987dd0a35af3a5b2e4fc11eba36a60eba03d'), "%3C%3Fimport%20namespace%3D%22foo%22%20implementation%3D%22%23bar%22%3E", "%23document%0A%7C%20%3C%21--%20%3Fimport%20namespace%3D%22foo%22%20implementation%3D%22%23bar%22%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"5b5e75eca2f5c80e1c4d5676254b9891090e288e":[async_test('html5lib_html5test-com.html 5b5e75eca2f5c80e1c4d5676254b9891090e288e'), "%3C%21--foo--bar--%3E", "%23document%0A%7C%20%3C%21--%20foo--bar%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"93e966e2edad3297ecb159f3983bdd2dc84f829e":[async_test('html5lib_html5test-com.html 93e966e2edad3297ecb159f3983bdd2dc84f829e'), "%3C%21%5BCDATA%5Bx%5D%5D%3E", "%23document%0A%7C%20%3C%21--%20%5BCDATA%5Bx%5D%5D%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"7a02a2d7ab875dbeedc9a34c6c27b6119bd6d1f0":[async_test('html5lib_html5test-com.html 7a02a2d7ab875dbeedc9a34c6c27b6119bd6d1f0'), "%3Ctextarea%3E%3C%21--%3C/textarea%3E--%3E%3C/textarea%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%20%20%22--%3E%22"],"46615acdb9dd6231e2a65fed5bcce7e19f086d03":[async_test('html5lib_html5test-com.html 46615acdb9dd6231e2a65fed5bcce7e19f086d03'), "%3Ctextarea%3E%3C%21--%3C/textarea%3E--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%20%20%22--%3E%22"],"381de12234a699cbfb775b3ca7c679f357e7403e":[async_test('html5lib_html5test-com.html 381de12234a699cbfb775b3ca7c679f357e7403e'), "%3Cstyle%3E%3C%21--%3C/style%3E--%3E%3C/style%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"72736fc894b2077928559cc4284a102635cac898":[async_test('html5lib_html5test-com.html 72736fc894b2077928559cc4284a102635cac898'), "%3Cstyle%3E%3C%21--%3C/style%3E--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"290d5e7fa9684038411e78c4b0e0ade83150eeea":[async_test('html5lib_html5test-com.html 290d5e7fa9684038411e78c4b0e0ade83150eeea'), "%3Cul%3E%3Cli%3EA%20%3C/li%3E%20%3Cli%3EB%3C/li%3E%3C/ul%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%22A%20%22%0A%7C%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22"],"a5fb9cf46ed215a61d073ae4b7e7477ba49fa5d8":[async_test('html5lib_html5test-com.html a5fb9cf46ed215a61d073ae4b7e7477ba49fa5d8'), "%3Ctable%3E%3Cform%3E%3Cinput%20type%3Dhidden%3E%3Cinput%3E%3C/form%3E%3Cdiv%3E%3C/div%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cform%3E%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%20%20type%3D%22hidden%22"],"88dea19243733a9bda26c6f5290d2c4f5bbac157":[async_test('html5lib_html5test-com.html 88dea19243733a9bda26c6f5290d2c4f5bbac157'), "%3Ci%3EA%3Cb%3EB%3Cp%3E%3C/i%3EC%3C/b%3ED", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%22C%22%0A%7C%20%20%20%20%20%20%20%22D%22"],"9906bb30ae08654f4c67bf6d97040abbca91082d":[async_test('html5lib_html5test-com.html 9906bb30ae08654f4c67bf6d97040abbca91082d'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"c6cd25ce02329e4a42b9be9ba35453532fec7599":[async_test('html5lib_html5test-com.html c6cd25ce02329e4a42b9be9ba35453532fec7599'), "%3Csvg%3E%3C/svg%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"81af3f9d6ccc2a1a1a58ace18dd530c544675610":[async_test('html5lib_html5test-com.html 81af3f9d6ccc2a1a1a58ace18dd530c544675610'), "%3Cmath%3E%3C/math%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_inbody01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_inbody01.html
new file mode 100644
index 0000000000..43064d43f1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_inbody01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_inbody01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['3e20aae3cbc7f10b28cbfc1e20b2949708872a3c','ec6b9d9dccaa3494a317afac0edfcc485b959663','cc4923612d10b2115cd03e269080ddf5463d95ae','a5ebf8808e479239966038951d5383ed65ff4eb6',];
+ var tests = {
+ "3e20aae3cbc7f10b28cbfc1e20b2949708872a3c":[async_test('html5lib_inbody01.html 3e20aae3cbc7f10b28cbfc1e20b2949708872a3c'), "%3Cbutton%3E1%3C/foo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%221%22"],"ec6b9d9dccaa3494a317afac0edfcc485b959663":[async_test('html5lib_inbody01.html ec6b9d9dccaa3494a317afac0edfcc485b959663'), "%3Cfoo%3E1%3Cp%3E2%3C/foo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22"],"cc4923612d10b2115cd03e269080ddf5463d95ae":[async_test('html5lib_inbody01.html cc4923612d10b2115cd03e269080ddf5463d95ae'), "%3Cdd%3E1%3C/foo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdd%3E%0A%7C%20%20%20%20%20%20%20%221%22"],"a5ebf8808e479239966038951d5383ed65ff4eb6":[async_test('html5lib_inbody01.html a5ebf8808e479239966038951d5383ed65ff4eb6'), "%3Cfoo%3E1%3Cdd%3E2%3C/foo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cdd%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_adoption01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_adoption01.html
new file mode 100644
index 0000000000..0e91fa8b20
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_adoption01.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_adoption01.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['0bf80e1546d4c221354aa9734f61713b7d64ee6d',];
+ var tests = {
+ "0bf80e1546d4c221354aa9734f61713b7d64ee6d":[async_test('html5lib_innerHTML_adoption01.html 0bf80e1546d4c221354aa9734f61713b7d64ee6d'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoob%3E%3Cfooc%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_foreign-fragment.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_foreign-fragment.html
new file mode 100644
index 0000000000..d2c342b7ae
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_foreign-fragment.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_foreign-fragment.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['4917b7458e1fff6c5cb21d7baf6863cc9550c61e','b6d19b8ccacd2fde354df002b856f649ae91b20e','0c2411aa96ee023941778adaa11977890b232dc3','af0d0fc82bcd7e5ba5bc40f781701552b19bb862','0135b05656c198b96a9e0f94333aa2c0190ec795','60d4a82dede2a297d6306278a19897d021075c6e','f862d10d81a600b69e7fabd1474ca854ce08cca7','5d1db05a97609488e6749ff191294713aec9a90f','8804aa42daebb5ff2ab0015c6e89d8e40e7a8610','34b599e68117799324663b39aa3ba469bffb2dcb','cc2199d299947f304e204c867bed2c7e910d50cc','87965749e4321e6ea69352296e89af9372af419e','db3b9be701b6878b437ea7096ac3b7ba8c0e2b57','148291c826fd7d1d455a7636f00ce6d6f6c3e890','2007abb6eef0427335962c65b7c8d7315f508afd','2e22f45e60f67be00f4827a4ca18f6bce7d7aacb','ba901841df046ba97bff9470e995f9792cb4aba5','439bec6b2c990ad6650baef76ebfb08220b186cf','6b7a24dd43b1f9059baee275caefabbef4b2d131','0a323099fae55bee0bf410053a7b5dae30ac6621','0b4329325579cf075c3a220f1e19825804e5775f','d2f37bafc0cd3bf4530cc1f6ef2fd261e0720201','6d037965d612d849672a73f2e8649ea90907483d','5bcd7fe5efe9d5a750a8cc293db28b686644cb13','9db26efc179b9a1be9cc7ea3086e4a9554e42562','e2bb8b2426ee1452f323274b05a458d52bf2f631','387ce2642c063a59c09b0a267cb8d35000e0a5a9','849d87495f6410c685eeceaaa9ce7331aa34c347','8377205f1e8ae86e7b7f2679410ed480ddb6bdab','8f929d7038047d37e8faf1e8956c7fa4c5b6b687','4070c271e57701248ff03bc6a2b9e714b69a8d61','59e76a925187915211a6ec38d0908a30577240c0','ea8e786baa7f216bc0346e425991ec6665142d82','1b2d5a4c9bed75970b2a6d71c33e52788c736fac','3657e075c88c2088e844cfb5140d344083c0b85a','909832844e7ff22822696f30c9ad9aba08b86a25','6ed082cc506da530d3128c24b8980cb551f28c43','18609521e139b2ffea10b1cd0cab5dc133b921c4','14e07b0c2adecfe362428885346c200e89f08e66','0cb505b329a178f11563ae05fe553f33f57d01cd','b4c43a4fcdaa1a4c753674c4f92987b70d54d80b','7021fb0933e92112b94ee54b923efb6bc71e3b07','7c4b1614e2180b6649f3b02cf5c4a9d735166e1b','bdeacb9250263776c63c2c7f731717c389bdc34c','2c46c15bdba5835b0f2f0e9eb5bc8566047b7d6d','8dfcfbf823ad6c7b6f7b81efc352f29b4e41e8be','74a8a40517c6fe110f0c71af7efb56d459ea8227','0c1782eb0f62f616627f0132729d6a194f8b7546','9dc5a819afe33d6babc04edc8f92cb8045f0f006','1a7663e45ee098567d82d0fa4351794cc7ef26f9','74fc819300ea11a14c04d5840d1daf6f998be64c','fe33f97f6a5482b2663340572a0d79309199c815','dfaa71da17a62d7f5de9f180beefcf36a05ce447','822f22f8f09e3a5cc1575f17487f3138543ce138','48e58a3e03d51a1676401ad872c03b96c52d3e2e','4e716ca1a2ba1079c8e163f8ba13dfc448abbf0f','33b553bb92ae345ef57f85312ad6e04c742f151e','e4208cd58954d48c8493fdf215595a4afe21621a','6c4299f29a55fe8d2972ab3b0b8af6c8c640c88f','5ebfe160b365c3c93258b726b5aea84cc329c019','8c9adcad849281769ccf069a48dbf6a530f97394','ce74a1ba339d07982908cc088c9057957a442b3e','b941cd3ca955b1025061b0ff0cda775f0edd16bc','69fb90a251264e4e80762fa9acecd2c0bffc0c4c','f856588390b813aafc272f42800d31ba9a4844e6','4c871c875e73e61adb24de1d18fad01363982e21',];
+ var tests = {
+ "4917b7458e1fff6c5cb21d7baf6863cc9550c61e":[async_test('html5lib_innerHTML_foreign-fragment.html 4917b7458e1fff6c5cb21d7baf6863cc9550c61e'), "%3Cnobr%3EX", "%23document%0A%7C%20%3Cnobr%3E%0A%7C%20%20%20%22X%22", 'svg path'],"b6d19b8ccacd2fde354df002b856f649ae91b20e":[async_test('html5lib_innerHTML_foreign-fragment.html b6d19b8ccacd2fde354df002b856f649ae91b20e'), "%3Cfont%20color%3E%3C/font%3EX", "%23document%0A%7C%20%3Cfont%3E%0A%7C%20%20%20color%3D%22%22%0A%7C%20%22X%22", 'svg path'],"0c2411aa96ee023941778adaa11977890b232dc3":[async_test('html5lib_innerHTML_foreign-fragment.html 0c2411aa96ee023941778adaa11977890b232dc3'), "%3Cfont%3E%3C/font%3EX", "%23document%0A%7C%20%3Csvg%20font%3E%0A%7C%20%22X%22", 'svg path'],"af0d0fc82bcd7e5ba5bc40f781701552b19bb862":[async_test('html5lib_innerHTML_foreign-fragment.html af0d0fc82bcd7e5ba5bc40f781701552b19bb862'), "%3Cg%3E%3C/path%3EX", "%23document%0A%7C%20%3Csvg%20g%3E%0A%7C%20%20%20%22X%22", 'svg path'],"0135b05656c198b96a9e0f94333aa2c0190ec795":[async_test('html5lib_innerHTML_foreign-fragment.html 0135b05656c198b96a9e0f94333aa2c0190ec795'), "%3C/path%3EX", "%23document%0A%7C%20%22X%22", 'svg path'],"60d4a82dede2a297d6306278a19897d021075c6e":[async_test('html5lib_innerHTML_foreign-fragment.html 60d4a82dede2a297d6306278a19897d021075c6e'), "%3C/foreignObject%3EX", "%23document%0A%7C%20%22X%22", 'svg foreignObject'],"f862d10d81a600b69e7fabd1474ca854ce08cca7":[async_test('html5lib_innerHTML_foreign-fragment.html f862d10d81a600b69e7fabd1474ca854ce08cca7'), "%3C/desc%3EX", "%23document%0A%7C%20%22X%22", 'svg desc'],"5d1db05a97609488e6749ff191294713aec9a90f":[async_test('html5lib_innerHTML_foreign-fragment.html 5d1db05a97609488e6749ff191294713aec9a90f'), "%3C/title%3EX", "%23document%0A%7C%20%22X%22", 'svg title'],"8804aa42daebb5ff2ab0015c6e89d8e40e7a8610":[async_test('html5lib_innerHTML_foreign-fragment.html 8804aa42daebb5ff2ab0015c6e89d8e40e7a8610'), "%3C/svg%3EX", "%23document%0A%7C%20%22X%22", 'svg svg'],"34b599e68117799324663b39aa3ba469bffb2dcb":[async_test('html5lib_innerHTML_foreign-fragment.html 34b599e68117799324663b39aa3ba469bffb2dcb'), "%3C/mfenced%3EX", "%23document%0A%7C%20%22X%22", 'math mfenced'],"cc2199d299947f304e204c867bed2c7e910d50cc":[async_test('html5lib_innerHTML_foreign-fragment.html cc2199d299947f304e204c867bed2c7e910d50cc'), "%3C/malignmark%3EX", "%23document%0A%7C%20%22X%22", 'math malignmark'],"87965749e4321e6ea69352296e89af9372af419e":[async_test('html5lib_innerHTML_foreign-fragment.html 87965749e4321e6ea69352296e89af9372af419e'), "%3C/math%3EX", "%23document%0A%7C%20%22X%22", 'math math'],"db3b9be701b6878b437ea7096ac3b7ba8c0e2b57":[async_test('html5lib_innerHTML_foreign-fragment.html db3b9be701b6878b437ea7096ac3b7ba8c0e2b57'), "%3C/annotation-xml%3EX", "%23document%0A%7C%20%22X%22", 'math annotation-xml'],"148291c826fd7d1d455a7636f00ce6d6f6c3e890":[async_test('html5lib_innerHTML_foreign-fragment.html 148291c826fd7d1d455a7636f00ce6d6f6c3e890'), "%3C/mtext%3EX", "%23document%0A%7C%20%22X%22", 'math mtext'],"2007abb6eef0427335962c65b7c8d7315f508afd":[async_test('html5lib_innerHTML_foreign-fragment.html 2007abb6eef0427335962c65b7c8d7315f508afd'), "%3C/mi%3EX", "%23document%0A%7C%20%22X%22", 'math mi'],"2e22f45e60f67be00f4827a4ca18f6bce7d7aacb":[async_test('html5lib_innerHTML_foreign-fragment.html 2e22f45e60f67be00f4827a4ca18f6bce7d7aacb'), "%3C/mo%3EX", "%23document%0A%7C%20%22X%22", 'math mo'],"ba901841df046ba97bff9470e995f9792cb4aba5":[async_test('html5lib_innerHTML_foreign-fragment.html ba901841df046ba97bff9470e995f9792cb4aba5'), "%3C/mn%3EX", "%23document%0A%7C%20%22X%22", 'math mn'],"439bec6b2c990ad6650baef76ebfb08220b186cf":[async_test('html5lib_innerHTML_foreign-fragment.html 439bec6b2c990ad6650baef76ebfb08220b186cf'), "%3C/ms%3EX", "%23document%0A%7C%20%22X%22", 'math ms'],"6b7a24dd43b1f9059baee275caefabbef4b2d131":[async_test('html5lib_innerHTML_foreign-fragment.html 6b7a24dd43b1f9059baee275caefabbef4b2d131'), "%3Cb%3E%3C/b%3E%3Cmglyph/%3E%3Ci%3E%3C/i%3E%3Cmalignmark/%3E%3Cu%3E%3C/u%3E%3Cms/%3EX", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%3Cmath%20mglyph%3E%0A%7C%20%3Ci%3E%0A%7C%20%3Cmath%20malignmark%3E%0A%7C%20%3Cu%3E%0A%7C%20%3Cms%3E%0A%7C%20%20%20%22X%22", 'math ms'],"0a323099fae55bee0bf410053a7b5dae30ac6621":[async_test('html5lib_innerHTML_foreign-fragment.html 0a323099fae55bee0bf410053a7b5dae30ac6621'), "%3Cmalignmark%3E%3C/malignmark%3E", "%23document%0A%7C%20%3Cmath%20malignmark%3E", 'math ms'],"0b4329325579cf075c3a220f1e19825804e5775f":[async_test('html5lib_innerHTML_foreign-fragment.html 0b4329325579cf075c3a220f1e19825804e5775f'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'math ms'],"d2f37bafc0cd3bf4530cc1f6ef2fd261e0720201":[async_test('html5lib_innerHTML_foreign-fragment.html d2f37bafc0cd3bf4530cc1f6ef2fd261e0720201'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'math ms'],"6d037965d612d849672a73f2e8649ea90907483d":[async_test('html5lib_innerHTML_foreign-fragment.html 6d037965d612d849672a73f2e8649ea90907483d'), "%3Cb%3E%3C/b%3E%3Cmglyph/%3E%3Ci%3E%3C/i%3E%3Cmalignmark/%3E%3Cu%3E%3C/u%3E%3Cmn/%3EX", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%3Cmath%20mglyph%3E%0A%7C%20%3Ci%3E%0A%7C%20%3Cmath%20malignmark%3E%0A%7C%20%3Cu%3E%0A%7C%20%3Cmn%3E%0A%7C%20%20%20%22X%22", 'math mn'],"5bcd7fe5efe9d5a750a8cc293db28b686644cb13":[async_test('html5lib_innerHTML_foreign-fragment.html 5bcd7fe5efe9d5a750a8cc293db28b686644cb13'), "%3Cmalignmark%3E%3C/malignmark%3E", "%23document%0A%7C%20%3Cmath%20malignmark%3E", 'math mn'],"9db26efc179b9a1be9cc7ea3086e4a9554e42562":[async_test('html5lib_innerHTML_foreign-fragment.html 9db26efc179b9a1be9cc7ea3086e4a9554e42562'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'math mn'],"e2bb8b2426ee1452f323274b05a458d52bf2f631":[async_test('html5lib_innerHTML_foreign-fragment.html e2bb8b2426ee1452f323274b05a458d52bf2f631'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'math mn'],"387ce2642c063a59c09b0a267cb8d35000e0a5a9":[async_test('html5lib_innerHTML_foreign-fragment.html 387ce2642c063a59c09b0a267cb8d35000e0a5a9'), "%3Cb%3E%3C/b%3E%3Cmglyph/%3E%3Ci%3E%3C/i%3E%3Cmalignmark/%3E%3Cu%3E%3C/u%3E%3Cmo/%3EX", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%3Cmath%20mglyph%3E%0A%7C%20%3Ci%3E%0A%7C%20%3Cmath%20malignmark%3E%0A%7C%20%3Cu%3E%0A%7C%20%3Cmo%3E%0A%7C%20%20%20%22X%22", 'math mo'],"849d87495f6410c685eeceaaa9ce7331aa34c347":[async_test('html5lib_innerHTML_foreign-fragment.html 849d87495f6410c685eeceaaa9ce7331aa34c347'), "%3Cmalignmark%3E%3C/malignmark%3E", "%23document%0A%7C%20%3Cmath%20malignmark%3E", 'math mo'],"8377205f1e8ae86e7b7f2679410ed480ddb6bdab":[async_test('html5lib_innerHTML_foreign-fragment.html 8377205f1e8ae86e7b7f2679410ed480ddb6bdab'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'math mo'],"8f929d7038047d37e8faf1e8956c7fa4c5b6b687":[async_test('html5lib_innerHTML_foreign-fragment.html 8f929d7038047d37e8faf1e8956c7fa4c5b6b687'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'math mo'],"4070c271e57701248ff03bc6a2b9e714b69a8d61":[async_test('html5lib_innerHTML_foreign-fragment.html 4070c271e57701248ff03bc6a2b9e714b69a8d61'), "%3Cb%3E%3C/b%3E%3Cmglyph/%3E%3Ci%3E%3C/i%3E%3Cmalignmark/%3E%3Cu%3E%3C/u%3E%3Cmi/%3EX", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%3Cmath%20mglyph%3E%0A%7C%20%3Ci%3E%0A%7C%20%3Cmath%20malignmark%3E%0A%7C%20%3Cu%3E%0A%7C%20%3Cmi%3E%0A%7C%20%20%20%22X%22", 'math mi'],"59e76a925187915211a6ec38d0908a30577240c0":[async_test('html5lib_innerHTML_foreign-fragment.html 59e76a925187915211a6ec38d0908a30577240c0'), "%3Cmalignmark%3E%3C/malignmark%3E", "%23document%0A%7C%20%3Cmath%20malignmark%3E", 'math mi'],"ea8e786baa7f216bc0346e425991ec6665142d82":[async_test('html5lib_innerHTML_foreign-fragment.html ea8e786baa7f216bc0346e425991ec6665142d82'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'math mi'],"1b2d5a4c9bed75970b2a6d71c33e52788c736fac":[async_test('html5lib_innerHTML_foreign-fragment.html 1b2d5a4c9bed75970b2a6d71c33e52788c736fac'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'math mi'],"3657e075c88c2088e844cfb5140d344083c0b85a":[async_test('html5lib_innerHTML_foreign-fragment.html 3657e075c88c2088e844cfb5140d344083c0b85a'), "%3Cb%3E%3C/b%3E%3Cmglyph/%3E%3Ci%3E%3C/i%3E%3Cmalignmark/%3E%3Cu%3E%3C/u%3E%3Cmtext/%3EX", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%3Cmath%20mglyph%3E%0A%7C%20%3Ci%3E%0A%7C%20%3Cmath%20malignmark%3E%0A%7C%20%3Cu%3E%0A%7C%20%3Cmtext%3E%0A%7C%20%20%20%22X%22", 'math mtext'],"909832844e7ff22822696f30c9ad9aba08b86a25":[async_test('html5lib_innerHTML_foreign-fragment.html 909832844e7ff22822696f30c9ad9aba08b86a25'), "%3Cmalignmark%3E%3C/malignmark%3E", "%23document%0A%7C%20%3Cmath%20malignmark%3E", 'math mtext'],"6ed082cc506da530d3128c24b8980cb551f28c43":[async_test('html5lib_innerHTML_foreign-fragment.html 6ed082cc506da530d3128c24b8980cb551f28c43'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'math mtext'],"18609521e139b2ffea10b1cd0cab5dc133b921c4":[async_test('html5lib_innerHTML_foreign-fragment.html 18609521e139b2ffea10b1cd0cab5dc133b921c4'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'math mtext'],"14e07b0c2adecfe362428885346c200e89f08e66":[async_test('html5lib_innerHTML_foreign-fragment.html 14e07b0c2adecfe362428885346c200e89f08e66'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'math annotation-xml'],"0cb505b329a178f11563ae05fe553f33f57d01cd":[async_test('html5lib_innerHTML_foreign-fragment.html 0cb505b329a178f11563ae05fe553f33f57d01cd'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cmath%20figure%3E", 'math annotation-xml'],"b4c43a4fcdaa1a4c753674c4f92987b70d54d80b":[async_test('html5lib_innerHTML_foreign-fragment.html b4c43a4fcdaa1a4c753674c4f92987b70d54d80b'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'math math'],"7021fb0933e92112b94ee54b923efb6bc71e3b07":[async_test('html5lib_innerHTML_foreign-fragment.html 7021fb0933e92112b94ee54b923efb6bc71e3b07'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cmath%20figure%3E", 'math math'],"7c4b1614e2180b6649f3b02cf5c4a9d735166e1b":[async_test('html5lib_innerHTML_foreign-fragment.html 7c4b1614e2180b6649f3b02cf5c4a9d735166e1b'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'svg foreignObject'],"bdeacb9250263776c63c2c7f731717c389bdc34c":[async_test('html5lib_innerHTML_foreign-fragment.html bdeacb9250263776c63c2c7f731717c389bdc34c'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'svg foreignObject'],"2c46c15bdba5835b0f2f0e9eb5bc8566047b7d6d":[async_test('html5lib_innerHTML_foreign-fragment.html 2c46c15bdba5835b0f2f0e9eb5bc8566047b7d6d'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'svg title'],"8dfcfbf823ad6c7b6f7b81efc352f29b4e41e8be":[async_test('html5lib_innerHTML_foreign-fragment.html 8dfcfbf823ad6c7b6f7b81efc352f29b4e41e8be'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'svg title'],"74a8a40517c6fe110f0c71af7efb56d459ea8227":[async_test('html5lib_innerHTML_foreign-fragment.html 74a8a40517c6fe110f0c71af7efb56d459ea8227'), "%3Cfigure%3E%3C/figure%3E", "%23document%0A%7C%20%3Cfigure%3E", 'svg desc'],"0c1782eb0f62f616627f0132729d6a194f8b7546":[async_test('html5lib_innerHTML_foreign-fragment.html 0c1782eb0f62f616627f0132729d6a194f8b7546'), "%3Cdiv%3E%3Ch1%3EX%3C/h1%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E%0A%7C%20%20%20%3Ch1%3E%0A%7C%20%20%20%20%20%22X%22", 'svg svg'],"9dc5a819afe33d6babc04edc8f92cb8045f0f006":[async_test('html5lib_innerHTML_foreign-fragment.html 9dc5a819afe33d6babc04edc8f92cb8045f0f006'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'svg svg'],"1a7663e45ee098567d82d0fa4351794cc7ef26f9":[async_test('html5lib_innerHTML_foreign-fragment.html 1a7663e45ee098567d82d0fa4351794cc7ef26f9'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Cdiv%3E", 'svg desc'],"74fc819300ea11a14c04d5840d1daf6f998be64c":[async_test('html5lib_innerHTML_foreign-fragment.html 74fc819300ea11a14c04d5840d1daf6f998be64c'), "%3Cplaintext%3E%3Cfoo%3E", "%23document%0A%7C%20%3Cplaintext%3E%0A%7C%20%20%20%22%3Cfoo%3E%22", 'svg desc'],"fe33f97f6a5482b2663340572a0d79309199c815":[async_test('html5lib_innerHTML_foreign-fragment.html fe33f97f6a5482b2663340572a0d79309199c815'), "%3Cframeset%3EX", "%23document%0A%7C%20%22X%22", 'svg desc'],"dfaa71da17a62d7f5de9f180beefcf36a05ce447":[async_test('html5lib_innerHTML_foreign-fragment.html dfaa71da17a62d7f5de9f180beefcf36a05ce447'), "%3Chead%3EX", "%23document%0A%7C%20%22X%22", 'svg desc'],"822f22f8f09e3a5cc1575f17487f3138543ce138":[async_test('html5lib_innerHTML_foreign-fragment.html 822f22f8f09e3a5cc1575f17487f3138543ce138'), "%3Cbody%3EX", "%23document%0A%7C%20%22X%22", 'svg desc'],"48e58a3e03d51a1676401ad872c03b96c52d3e2e":[async_test('html5lib_innerHTML_foreign-fragment.html 48e58a3e03d51a1676401ad872c03b96c52d3e2e'), "%3Chtml%3EX", "%23document%0A%7C%20%22X%22", 'svg desc'],"4e716ca1a2ba1079c8e163f8ba13dfc448abbf0f":[async_test('html5lib_innerHTML_foreign-fragment.html 4e716ca1a2ba1079c8e163f8ba13dfc448abbf0f'), "%3Chtml%20class%3D%22foo%22%3EX", "%23document%0A%7C%20%22X%22", 'svg desc'],"33b553bb92ae345ef57f85312ad6e04c742f151e":[async_test('html5lib_innerHTML_foreign-fragment.html 33b553bb92ae345ef57f85312ad6e04c742f151e'), "%3Cbody%20class%3D%22foo%22%3EX", "%23document%0A%7C%20%22X%22", 'svg desc'],"e4208cd58954d48c8493fdf215595a4afe21621a":[async_test('html5lib_innerHTML_foreign-fragment.html e4208cd58954d48c8493fdf215595a4afe21621a'), "%3Csvg%3E%3Cp%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%3Cp%3E", 'div'],"6c4299f29a55fe8d2972ab3b0b8af6c8c640c88f":[async_test('html5lib_innerHTML_foreign-fragment.html 6c4299f29a55fe8d2972ab3b0b8af6c8c640c88f'), "%3Cp%3E", "%23document%0A%7C%20%3Cp%3E", 'svg svg'],"5ebfe160b365c3c93258b726b5aea84cc329c019":[async_test('html5lib_innerHTML_foreign-fragment.html 5ebfe160b365c3c93258b726b5aea84cc329c019'), "%3Csvg%3E%3C/p%3E%3Cfoo%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%3Cp%3E%0A%7C%20%3Cfoo%3E", 'div'],"8c9adcad849281769ccf069a48dbf6a530f97394":[async_test('html5lib_innerHTML_foreign-fragment.html 8c9adcad849281769ccf069a48dbf6a530f97394'), "%3Csvg%3E%3C/br%3E%3Cfoo%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%3Cbr%3E%0A%7C%20%3Cfoo%3E", 'div'],"ce74a1ba339d07982908cc088c9057957a442b3e":[async_test('html5lib_innerHTML_foreign-fragment.html ce74a1ba339d07982908cc088c9057957a442b3e'), "%3C/p%3E%3Cfoo%3E", "%23document%0A%7C%20%3Cp%3E%0A%7C%20%3Csvg%20foo%3E", 'svg svg'],"b941cd3ca955b1025061b0ff0cda775f0edd16bc":[async_test('html5lib_innerHTML_foreign-fragment.html b941cd3ca955b1025061b0ff0cda775f0edd16bc'), "%3C/br%3E%3Cfoo%3E", "%23document%0A%7C%20%3Cbr%3E%0A%7C%20%3Csvg%20foo%3E", 'svg svg'],"69fb90a251264e4e80762fa9acecd2c0bffc0c4c":[async_test('html5lib_innerHTML_foreign-fragment.html 69fb90a251264e4e80762fa9acecd2c0bffc0c4c'), "%3Cbody%3E%3Cfoo%3E", "%23document%0A%7C%20%3Csvg%20foo%3E", 'svg svg'],"f856588390b813aafc272f42800d31ba9a4844e6":[async_test('html5lib_innerHTML_foreign-fragment.html f856588390b813aafc272f42800d31ba9a4844e6'), "%3Cp%3E%3Cfoo%3E", "%23document%0A%7C%20%3Cp%3E%0A%7C%20%20%20%3Cfoo%3E", 'svg svg'],"4c871c875e73e61adb24de1d18fad01363982e21":[async_test('html5lib_innerHTML_foreign-fragment.html 4c871c875e73e61adb24de1d18fad01363982e21'), "%3Cp%3E%3C/p%3E%3Cfoo%3E", "%23document%0A%7C%20%3Cp%3E%0A%7C%20%3Csvg%20foo%3E", 'svg svg'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_math.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_math.html
new file mode 100644
index 0000000000..efa9b8ae7c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_math.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_math.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['0e7e70d0dcf0c26593203b36cac4fa7f6325613e','fa7d4a31838dbcc16bf73672f2f4486cca185673','d9d2e4c0e926a91f5e704846cdbc855e3cb21949','c04b203803f6b3bec3db65db16854e7e624d13ef','4f95d47164955a6b163935fd8ac89ea200767330','e942ee6666e1dc938aab10fc2374a2240806b439','3537413f7f8166cb0c3a324fef8261be5628611d','c0186fb0fe26b48bcd82d58ebe0c90a423f26c28',];
+ var tests = {
+ "0e7e70d0dcf0c26593203b36cac4fa7f6325613e":[async_test('html5lib_innerHTML_math.html 0e7e70d0dcf0c26593203b36cac4fa7f6325613e'), "%3Cmath%3E%3Ctr%3E%3Ctd%3E%3Cmo%3E%3Ctr%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20tr%3E%0A%7C%20%20%20%20%20%3Cmath%20td%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mo%3E", 'td'],"fa7d4a31838dbcc16bf73672f2f4486cca185673":[async_test('html5lib_innerHTML_math.html fa7d4a31838dbcc16bf73672f2f4486cca185673'), "%3Cmath%3E%3Ctr%3E%3Ctd%3E%3Cmo%3E%3Ctr%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20tr%3E%0A%7C%20%20%20%20%20%3Cmath%20td%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mo%3E", 'tr'],"d9d2e4c0e926a91f5e704846cdbc855e3cb21949":[async_test('html5lib_innerHTML_math.html d9d2e4c0e926a91f5e704846cdbc855e3cb21949'), "%3Cmath%3E%3Cthead%3E%3Cmo%3E%3Ctbody%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20thead%3E%0A%7C%20%20%20%20%20%3Cmath%20mo%3E", 'thead'],"c04b203803f6b3bec3db65db16854e7e624d13ef":[async_test('html5lib_innerHTML_math.html c04b203803f6b3bec3db65db16854e7e624d13ef'), "%3Cmath%3E%3Ctfoot%3E%3Cmo%3E%3Ctbody%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20tfoot%3E%0A%7C%20%20%20%20%20%3Cmath%20mo%3E", 'tfoot'],"4f95d47164955a6b163935fd8ac89ea200767330":[async_test('html5lib_innerHTML_math.html 4f95d47164955a6b163935fd8ac89ea200767330'), "%3Cmath%3E%3Ctbody%3E%3Cmo%3E%3Ctfoot%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20tbody%3E%0A%7C%20%20%20%20%20%3Cmath%20mo%3E", 'tbody'],"e942ee6666e1dc938aab10fc2374a2240806b439":[async_test('html5lib_innerHTML_math.html e942ee6666e1dc938aab10fc2374a2240806b439'), "%3Cmath%3E%3Ctbody%3E%3Cmo%3E%3C/table%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20tbody%3E%0A%7C%20%20%20%20%20%3Cmath%20mo%3E", 'tbody'],"3537413f7f8166cb0c3a324fef8261be5628611d":[async_test('html5lib_innerHTML_math.html 3537413f7f8166cb0c3a324fef8261be5628611d'), "%3Cmath%3E%3Cthead%3E%3Cmo%3E%3C/table%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20thead%3E%0A%7C%20%20%20%20%20%3Cmath%20mo%3E", 'tbody'],"c0186fb0fe26b48bcd82d58ebe0c90a423f26c28":[async_test('html5lib_innerHTML_math.html c0186fb0fe26b48bcd82d58ebe0c90a423f26c28'), "%3Cmath%3E%3Ctfoot%3E%3Cmo%3E%3C/table%3E", "%23document%0A%7C%20%3Cmath%20math%3E%0A%7C%20%20%20%3Cmath%20tfoot%3E%0A%7C%20%20%20%20%20%3Cmath%20mo%3E", 'tbody'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_svg.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_svg.html
new file mode 100644
index 0000000000..1fa379d88d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_svg.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_svg.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['9de06fc8491759163a4b2a4e17015c94cab31f13','f4ffd3073d887daeb6b5d293547f3596a6bbe2b3','3abfcccd524311a4afcc0057334c8364616fd830','70fbdb7f147b21143966af3a9467f716cc314637','44994fe2eb6af6ad3511781f23bafcdd2d24692c','a148d278cbb59d5eadc1c6a5576c7887dcadb74e','f1746231c89e495dc6b9d114d6d2f52876a1f5f5','1689d7c8f41a57eb15235d337028f3fe44764349',];
+ var tests = {
+ "9de06fc8491759163a4b2a4e17015c94cab31f13":[async_test('html5lib_innerHTML_svg.html 9de06fc8491759163a4b2a4e17015c94cab31f13'), "%3Csvg%3E%3Ctr%3E%3Ctd%3E%3Ctitle%3E%3Ctr%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20tr%3E%0A%7C%20%20%20%20%20%3Csvg%20td%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E", 'td'],"f4ffd3073d887daeb6b5d293547f3596a6bbe2b3":[async_test('html5lib_innerHTML_svg.html f4ffd3073d887daeb6b5d293547f3596a6bbe2b3'), "%3Csvg%3E%3Ctr%3E%3Ctd%3E%3Ctitle%3E%3Ctr%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20tr%3E%0A%7C%20%20%20%20%20%3Csvg%20td%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E", 'tr'],"3abfcccd524311a4afcc0057334c8364616fd830":[async_test('html5lib_innerHTML_svg.html 3abfcccd524311a4afcc0057334c8364616fd830'), "%3Csvg%3E%3Cthead%3E%3Ctitle%3E%3Ctbody%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20thead%3E%0A%7C%20%20%20%20%20%3Csvg%20title%3E", 'thead'],"70fbdb7f147b21143966af3a9467f716cc314637":[async_test('html5lib_innerHTML_svg.html 70fbdb7f147b21143966af3a9467f716cc314637'), "%3Csvg%3E%3Ctfoot%3E%3Ctitle%3E%3Ctbody%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20tfoot%3E%0A%7C%20%20%20%20%20%3Csvg%20title%3E", 'tfoot'],"44994fe2eb6af6ad3511781f23bafcdd2d24692c":[async_test('html5lib_innerHTML_svg.html 44994fe2eb6af6ad3511781f23bafcdd2d24692c'), "%3Csvg%3E%3Ctbody%3E%3Ctitle%3E%3Ctfoot%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20tbody%3E%0A%7C%20%20%20%20%20%3Csvg%20title%3E", 'tbody'],"a148d278cbb59d5eadc1c6a5576c7887dcadb74e":[async_test('html5lib_innerHTML_svg.html a148d278cbb59d5eadc1c6a5576c7887dcadb74e'), "%3Csvg%3E%3Ctbody%3E%3Ctitle%3E%3C/table%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20tbody%3E%0A%7C%20%20%20%20%20%3Csvg%20title%3E", 'tbody'],"f1746231c89e495dc6b9d114d6d2f52876a1f5f5":[async_test('html5lib_innerHTML_svg.html f1746231c89e495dc6b9d114d6d2f52876a1f5f5'), "%3Csvg%3E%3Cthead%3E%3Ctitle%3E%3C/table%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20thead%3E%0A%7C%20%20%20%20%20%3Csvg%20title%3E", 'tbody'],"1689d7c8f41a57eb15235d337028f3fe44764349":[async_test('html5lib_innerHTML_svg.html 1689d7c8f41a57eb15235d337028f3fe44764349'), "%3Csvg%3E%3Ctfoot%3E%3Ctitle%3E%3C/table%3E", "%23document%0A%7C%20%3Csvg%20svg%3E%0A%7C%20%20%20%3Csvg%20tfoot%3E%0A%7C%20%20%20%20%20%3Csvg%20title%3E", 'tbody'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_template.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_template.html
new file mode 100644
index 0000000000..19dc555a79
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_template.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_template.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['64d3e0e4395745b6ea928e5f5cf888bf675a598b',];
+ var tests = {
+ "64d3e0e4395745b6ea928e5f5cf888bf675a598b":[async_test('html5lib_innerHTML_template.html 64d3e0e4395745b6ea928e5f5cf888bf675a598b'), "%3Ctemplate%3E%3Cform%3E%3Cinput%20name%3D%22q%22%3E%3C/form%3E%3Cdiv%3Esecond%3C/div%3E%3C/template%3E", "%23document%0A%7C%20%3Ctemplate%3E%0A%7C%20%20%20content%0A%7C%20%20%20%20%20%3Cform%3E%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%20%20name%3D%22q%22%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22second%22", 'template'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests4.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests4.html
new file mode 100644
index 0000000000..3c54fff107
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests4.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_tests4.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['8c692a23f8c9b5860cf06fb334041d2e97e96f5c','95cb768746a1ca7ac02d39c0bb2b10d9e965e37c','06bd3583493359d112d19765f68fac3901267408','48d8375ef2d9d73bd133f2947858a3450a988d53','36fb2178fbdfa1b32701a9d9214c3fd115fd7184','d373bc3abdda01b17a6055af21b16c151dd1d697','4f91b1d4c6e2bbc0595c9effc490b7357e9cefc2','38b248763fb49ca6c1abc2f2f9d9e502c5ed5041','054c57cdb4b945b7273a4627a68f470705640e3e',];
+ var tests = {
+ "8c692a23f8c9b5860cf06fb334041d2e97e96f5c":[async_test('html5lib_innerHTML_tests4.html 8c692a23f8c9b5860cf06fb334041d2e97e96f5c'), "direct%20div%20content", "%23document%0A%7C%20%22direct%20div%20content%22", 'div'],"95cb768746a1ca7ac02d39c0bb2b10d9e965e37c":[async_test('html5lib_innerHTML_tests4.html 95cb768746a1ca7ac02d39c0bb2b10d9e965e37c'), "direct%20textarea%20content", "%23document%0A%7C%20%22direct%20textarea%20content%22", 'textarea'],"06bd3583493359d112d19765f68fac3901267408":[async_test('html5lib_innerHTML_tests4.html 06bd3583493359d112d19765f68fac3901267408'), "textarea%20content%20with%20%3Cem%3Epseudo%3C/em%3E%20%3Cfoo%3Emarkup", "%23document%0A%7C%20%22textarea%20content%20with%20%3Cem%3Epseudo%3C/em%3E%20%3Cfoo%3Emarkup%22", 'textarea'],"48d8375ef2d9d73bd133f2947858a3450a988d53":[async_test('html5lib_innerHTML_tests4.html 48d8375ef2d9d73bd133f2947858a3450a988d53'), "this%20is%20%26%23x0043%3BDATA%20inside%20a%20%3Cstyle%3E%20element", "%23document%0A%7C%20%22this%20is%20%26%23x0043%3BDATA%20inside%20a%20%3Cstyle%3E%20element%22", 'style'],"36fb2178fbdfa1b32701a9d9214c3fd115fd7184":[async_test('html5lib_innerHTML_tests4.html 36fb2178fbdfa1b32701a9d9214c3fd115fd7184'), "%3C/plaintext%3E", "%23document%0A%7C%20%22%3C/plaintext%3E%22", 'plaintext'],"d373bc3abdda01b17a6055af21b16c151dd1d697":[async_test('html5lib_innerHTML_tests4.html d373bc3abdda01b17a6055af21b16c151dd1d697'), "setting%20html%27s%20innerHTML", "%23document%0A%7C%20%3Chead%3E%0A%7C%20%3Cbody%3E%0A%7C%20%20%20%22setting%20html%27s%20innerHTML%22", 'html'],"4f91b1d4c6e2bbc0595c9effc490b7357e9cefc2":[async_test('html5lib_innerHTML_tests4.html 4f91b1d4c6e2bbc0595c9effc490b7357e9cefc2'), "%3Ctitle%3Esetting%20head%27s%20innerHTML%3C/title%3E", "%23document%0A%7C%20%3Ctitle%3E%0A%7C%20%20%20%22setting%20head%27s%20innerHTML%22", 'head'],"38b248763fb49ca6c1abc2f2f9d9e502c5ed5041":[async_test('html5lib_innerHTML_tests4.html 38b248763fb49ca6c1abc2f2f9d9e502c5ed5041'), "direct%20%3Ctitle%3E%20content", "%23document%0A%7C%20%22direct%20%3Ctitle%3E%20content%22", 'title'],"054c57cdb4b945b7273a4627a68f470705640e3e":[async_test('html5lib_innerHTML_tests4.html 054c57cdb4b945b7273a4627a68f470705640e3e'), "%3C%21--%20inside%20%3C/script%3E%20--%3E", "%23document%0A%7C%20%22%3C%21--%20inside%20%3C/script%3E%20--%3E%22", 'script'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests6.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests6.html
new file mode 100644
index 0000000000..d0e623080f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests6.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_tests6.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['ccb245e2f1d9fe1580235854daa1a124525aca61','ed2b0f8fe477e3a6a0d9052b46bd94e628fb666a','ed4acc4544b7ee83072a3c2ae078e3cbaf8303fb','28f111cdfb84bfa4a70b57e3aeec1f0aa13337de','616bd17e481973f9fe286aa30727ee22850fc31e','1cfb3baf2ad29109ddd5581daa3a009029c71491','98ba377bacd2ec343919bdc589116eabf94402d5','7cf2db8c65b79da98e39b13772ed0440ff177fd7','cb78efe1d4f4279be6c0a363cce643b3591efdc5','82911b0551c00e9971dd1491f8f2d2782aa3ac63','a3ff1f1809e8018b725620f1d04b6ebb24fda9a4','e35e330f7eb5bb27df1fe702843747e104a193be','c1dc3add1fcb1f506ea395691a710eb8e727b123',];
+ var tests = {
+ "ccb245e2f1d9fe1580235854daa1a124525aca61":[async_test('html5lib_innerHTML_tests6.html ccb245e2f1d9fe1580235854daa1a124525aca61'), "%3Cbody%3E%0A%3Cdiv%3E", "%23document%0A%7C%20%22%0A%22%0A%7C%20%3Cdiv%3E", 'div'],"ed2b0f8fe477e3a6a0d9052b46bd94e628fb666a":[async_test('html5lib_innerHTML_tests6.html ed2b0f8fe477e3a6a0d9052b46bd94e628fb666a'), "%3C/caption%3E%3Cdiv%3E", "%23document%0A%7C%20%3Cdiv%3E", 'caption'],"ed4acc4544b7ee83072a3c2ae078e3cbaf8303fb":[async_test('html5lib_innerHTML_tests6.html ed4acc4544b7ee83072a3c2ae078e3cbaf8303fb'), "%3C/table%3E%3Cdiv%3E", "%23document%0A%7C%20%3Cdiv%3E", 'caption'],"28f111cdfb84bfa4a70b57e3aeec1f0aa13337de":[async_test('html5lib_innerHTML_tests6.html 28f111cdfb84bfa4a70b57e3aeec1f0aa13337de'), "%3C/table%3E%3C/tbody%3E%3C/tfoot%3E%3C/thead%3E%3C/tr%3E%3Cdiv%3E", "%23document%0A%7C%20%3Cdiv%3E", 'td'],"616bd17e481973f9fe286aa30727ee22850fc31e":[async_test('html5lib_innerHTML_tests6.html 616bd17e481973f9fe286aa30727ee22850fc31e'), "foo%3Ccol%3E", "%23document%0A%7C%20%3Ccol%3E", 'colgroup'],"1cfb3baf2ad29109ddd5581daa3a009029c71491":[async_test('html5lib_innerHTML_tests6.html 1cfb3baf2ad29109ddd5581daa3a009029c71491'), "%3C/frameset%3E%3Cframe%3E", "%23document%0A%7C%20%3Cframe%3E", 'frameset'],"98ba377bacd2ec343919bdc589116eabf94402d5":[async_test('html5lib_innerHTML_tests6.html 98ba377bacd2ec343919bdc589116eabf94402d5'), "%3C/body%3E%3Cdiv%3E", "%23document%0A%7C%20%3Cdiv%3E", 'body'],"7cf2db8c65b79da98e39b13772ed0440ff177fd7":[async_test('html5lib_innerHTML_tests6.html 7cf2db8c65b79da98e39b13772ed0440ff177fd7'), "%3C/tr%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"cb78efe1d4f4279be6c0a363cce643b3591efdc5":[async_test('html5lib_innerHTML_tests6.html cb78efe1d4f4279be6c0a363cce643b3591efdc5'), "%3C/tbody%3E%3C/tfoot%3E%3C/thead%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"82911b0551c00e9971dd1491f8f2d2782aa3ac63":[async_test('html5lib_innerHTML_tests6.html 82911b0551c00e9971dd1491f8f2d2782aa3ac63'), "%3Ccaption%3E%3Ccol%3E%3Ccolgroup%3E%3Ctbody%3E%3Ctfoot%3E%3Cthead%3E%3Ctr%3E", "%23document%0A%7C%20%3Ctr%3E", 'tbody'],"a3ff1f1809e8018b725620f1d04b6ebb24fda9a4":[async_test('html5lib_innerHTML_tests6.html a3ff1f1809e8018b725620f1d04b6ebb24fda9a4'), "%3C/table%3E%3Ctr%3E", "%23document%0A%7C%20%3Ctr%3E", 'tbody'],"e35e330f7eb5bb27df1fe702843747e104a193be":[async_test('html5lib_innerHTML_tests6.html e35e330f7eb5bb27df1fe702843747e104a193be'), "%3C/table%3E%3Ctr%3E", "%23document%0A%7C%20%3Ctbody%3E%0A%7C%20%20%20%3Ctr%3E", 'table'],"c1dc3add1fcb1f506ea395691a710eb8e727b123":[async_test('html5lib_innerHTML_tests6.html c1dc3add1fcb1f506ea395691a710eb8e727b123'), "%3Cbody%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chead%3E%0A%7C%20%3Cbody%3E", 'html'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests7.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests7.html
new file mode 100644
index 0000000000..a73d767101
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests7.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_tests7.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['7aabda442dc7b37110c3d03b1465fa893dc25625',];
+ var tests = {
+ "7aabda442dc7b37110c3d03b1465fa893dc25625":[async_test('html5lib_innerHTML_tests7.html 7aabda442dc7b37110c3d03b1465fa893dc25625'), "%3Cbody%3EX%3C/body%3E%3C/body%3E", "%23document%0A%7C%20%3Chead%3E%0A%7C%20%3Cbody%3E%0A%7C%20%20%20%22X%22", 'html'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests_innerHTML_1.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests_innerHTML_1.html
new file mode 100644
index 0000000000..0658d768db
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_tests_innerHTML_1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_tests_innerHTML_1.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['7a9e287595dd570e0f19b7eec0ac424228908daf','6f766fa07c8697a5379c5542adbba2a42f913004','dbbe75ae41228f9264d56a018e620217ec87fd32','32e2fea7531c0c910ac73b35665b2f8bca47a049','7fc9ad33d4bc6af760883f0581cf869f96473f2e','8883d04b33c7e718b0d201a5f0bf5aa6d98754e6','347f73ada7ab974a081c65385444cca0b6a2d446','25a03753c27ab46891e2c59665215e4657ca7177','75e9c86aff595da8999673fce99bc12741552c4e','e35e330f7eb5bb27df1fe702843747e104a193be','b55e32e22980fe99bab895005a0a757a2fcb1a2b','ce6fc19319d2568fec9084d8d4d1cc10f20b8565','b600f8c7df58e42342feff051778923dbf7616af','6056619333f1a780052612e1dda18112147f67fa','d9300c4d8d36e950490bbe5800c1a3ba4e744e2a','411ebe3bea1d0508263ec17618492feaa9ca99d7','6c7c85721f823ab889bcb9e035341a1fb6d6eeee','973d06969d7968d200d5639ce06f7209655b1e26','dfb6ec1e412a6634b497151f222f4272721ae57e','4916096ab7dbdd051dc96de7952ec60674d617a3','58277507de4ce0df9b4eb03262e4cb04d2fcc7af','8e5727f170507ba6b86c1c3bdb3a5201d3480b33','61eb343f71030688ab1a26bb980b9e4409993e3d','3548939a56a148b59781c5f930036c1528db1545','0b88a12102fba0634b44ac9f157544faeca68df6','c300553f45f4ad6e5e1da9d884fafb95f36ab05a','00a7ed4082183eba76af197418305b2d196ec7e6','5dfd84382f85ff8343a62d6bb5028996111e5017','9b1a1cd33bb3f26ec3f969f3158edf5c2db47052','8ab89f758b96bf994a21d8283d27e8ae9804b924','25add8314f59cdc264d7779f36ae4dffd1f9ad29','7104e981f6018b18766fd95109bfbffeb878cc56','28fbaa710ce3f440a56fdf909c4b8bc223a1b965','3380b0143f3d1e2edb216d388acd72702e141165','a630fb272be6de118b728a28d6ce71b296a75694','5adf8b7bd3d63a77ea1dbe6deb6741c5b92de6cd','4e0312ac349a70d07f2bb1ae154740e46e8c9a6e','987061379d2542e88d8a72ce6f0169a211d3ac41','e0798aa003863ba2be750d3e6c2e6766fea11279','586ed8e0d1395198f43ee68843d654a49169f379','8896feedef576c1ed768a4eb67f57c3dc5242fed','89f4f0f289a23d1ebfbf499c2ecd24cd35fc10b4','2c07361470533b905a7ff9a685439cad2fe35549','a36f27cbe53991c647055c95bfa250a0ab734b0d','ac28c352ae8453434e3aefb24e798a9580c1b230','f8770f97671a805b37277db7e42536b40c0804cb','469a071b520d8436e6a0f6da6f9385f5ebd8e2f8','7cf2db8c65b79da98e39b13772ed0440ff177fd7','3940fdb54783cb3c42138670a17d28e77e29e900','3b5ab5fbb3585d7215a5766f1e2377b7929b5cc6','7c4a40203d5830d36432b0f30a09cebea6e9d2e4','e6d930d4239666fdc6c0722106bd2b115b4d3fd3','c8817b3b55a437bd153e978fc5f49fbe10bb56e2','2a302c14f1983aaad9fd7abe49336d9561ed82e9','28cfddd5b5875f7044b0859ba4ce88175fcbf07e','887596c50809eeb809ad24d86c239130a42f5a46','fa3797a2d2baeb8b8b2de81f1e7f33725e6b2aad','dc0e2582ff83e60c0eb549dc3387562d3482e364','4ea02fd705291eb2d14274ebeaa0117ba2b9b306','52cc77eac9488a8bb1a2c8c695f16f8919c52044','c53ab7d84ae27de9f0937521c74eec2fd6c1b1ec','d8747a49503f3486155d77dd366e0ee8ad9512a6','03e71832c254852f206f6f0ae6f4d161a276a699','92786cfafa890c23f200dccea089ca52233ef395','11d284c2b1e2f87d28dd06b938518361fe834855','bf6aebcd54d5dead9e6d56c77b41f01ea666d8b1','4d448a4239cb4c465a21c04997d656e51fdd388f','1e8faedc427045d59305218c1aba2f545c4eb4b7','6762997c1a93b1ec65722498f3fd00f0d8129369','a162461c18d9b09734f6fe5d362b84edb4eed31f','0befc335ffd6cace344d94a35de96af22b1313a5','a23b70f1f246ba08d13b570319391b4a5c3e9456','9d5e0c25bfe921df9ea2897c027f42bc88950e69','9210d577d6deecf5ab3505af86c501c5befa0b50','c34af491c0a339db6ba63fcc478108533347319b','2c4284e6b2bb480daa50bca43bcbe29cfcdeeab4','d75277b65d0118463afeb66b478509d4e27565ab','b354df69dbe9b3ef0c42177648e3aace114cf8ea','fd3be386292ea1f411cea8e86e29595deb177d28','1cfb3baf2ad29109ddd5581daa3a009029c71491','2555d238e04f3d2853cfbc5f6dd366f82cf0e868',];
+ var tests = {
+ "7a9e287595dd570e0f19b7eec0ac424228908daf":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 7a9e287595dd570e0f19b7eec0ac424228908daf'), "%3Cbody%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E", 'body'],"6f766fa07c8697a5379c5542adbba2a42f913004":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 6f766fa07c8697a5379c5542adbba2a42f913004'), "%3Cspan%3E%3Cbody%3E", "%23document%0A%7C%20%3Cspan%3E", 'body'],"dbbe75ae41228f9264d56a018e620217ec87fd32":[async_test('html5lib_innerHTML_tests_innerHTML_1.html dbbe75ae41228f9264d56a018e620217ec87fd32'), "%3Cspan%3E%3Cbody%3E", "%23document%0A%7C%20%3Cspan%3E", 'div'],"32e2fea7531c0c910ac73b35665b2f8bca47a049":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 32e2fea7531c0c910ac73b35665b2f8bca47a049'), "%3Cbody%3E%3Cspan%3E", "%23document%0A%7C%20%3Chead%3E%0A%7C%20%3Cbody%3E%0A%7C%20%20%20%3Cspan%3E", 'html'],"7fc9ad33d4bc6af760883f0581cf869f96473f2e":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 7fc9ad33d4bc6af760883f0581cf869f96473f2e'), "%3Cframeset%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E", 'body'],"8883d04b33c7e718b0d201a5f0bf5aa6d98754e6":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 8883d04b33c7e718b0d201a5f0bf5aa6d98754e6'), "%3Cspan%3E%3Cframeset%3E", "%23document%0A%7C%20%3Cspan%3E", 'body'],"347f73ada7ab974a081c65385444cca0b6a2d446":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 347f73ada7ab974a081c65385444cca0b6a2d446'), "%3Cspan%3E%3Cframeset%3E", "%23document%0A%7C%20%3Cspan%3E", 'div'],"25a03753c27ab46891e2c59665215e4657ca7177":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 25a03753c27ab46891e2c59665215e4657ca7177'), "%3Cframeset%3E%3Cspan%3E", "%23document%0A%7C%20%3Chead%3E%0A%7C%20%3Cframeset%3E", 'html'],"75e9c86aff595da8999673fce99bc12741552c4e":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 75e9c86aff595da8999673fce99bc12741552c4e'), "%3Ctable%3E%3Ctr%3E", "%23document%0A%7C%20%3Ctbody%3E%0A%7C%20%20%20%3Ctr%3E", 'table'],"e35e330f7eb5bb27df1fe702843747e104a193be":[async_test('html5lib_innerHTML_tests_innerHTML_1.html e35e330f7eb5bb27df1fe702843747e104a193be'), "%3C/table%3E%3Ctr%3E", "%23document%0A%7C%20%3Ctbody%3E%0A%7C%20%20%20%3Ctr%3E", 'table'],"b55e32e22980fe99bab895005a0a757a2fcb1a2b":[async_test('html5lib_innerHTML_tests_innerHTML_1.html b55e32e22980fe99bab895005a0a757a2fcb1a2b'), "%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'table'],"ce6fc19319d2568fec9084d8d4d1cc10f20b8565":[async_test('html5lib_innerHTML_tests_innerHTML_1.html ce6fc19319d2568fec9084d8d4d1cc10f20b8565'), "%3Ca%3E%3Ccaption%3Ea", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ccaption%3E%0A%7C%20%20%20%22a%22", 'table'],"b600f8c7df58e42342feff051778923dbf7616af":[async_test('html5lib_innerHTML_tests_innerHTML_1.html b600f8c7df58e42342feff051778923dbf7616af'), "%3Ca%3E%3Ccolgroup%3E%3Ccol%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ccolgroup%3E%0A%7C%20%20%20%3Ccol%3E", 'table'],"6056619333f1a780052612e1dda18112147f67fa":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 6056619333f1a780052612e1dda18112147f67fa'), "%3Ca%3E%3Ctbody%3E%3Ctr%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ctbody%3E%0A%7C%20%20%20%3Ctr%3E", 'table'],"d9300c4d8d36e950490bbe5800c1a3ba4e744e2a":[async_test('html5lib_innerHTML_tests_innerHTML_1.html d9300c4d8d36e950490bbe5800c1a3ba4e744e2a'), "%3Ca%3E%3Ctfoot%3E%3Ctr%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ctfoot%3E%0A%7C%20%20%20%3Ctr%3E", 'table'],"411ebe3bea1d0508263ec17618492feaa9ca99d7":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 411ebe3bea1d0508263ec17618492feaa9ca99d7'), "%3Ca%3E%3Cthead%3E%3Ctr%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Cthead%3E%0A%7C%20%20%20%3Ctr%3E", 'table'],"6c7c85721f823ab889bcb9e035341a1fb6d6eeee":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 6c7c85721f823ab889bcb9e035341a1fb6d6eeee'), "%3Ca%3E%3Ctr%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ctbody%3E%0A%7C%20%20%20%3Ctr%3E", 'table'],"973d06969d7968d200d5639ce06f7209655b1e26":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 973d06969d7968d200d5639ce06f7209655b1e26'), "%3Ca%3E%3Cth%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ctbody%3E%0A%7C%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%3Cth%3E", 'table'],"dfb6ec1e412a6634b497151f222f4272721ae57e":[async_test('html5lib_innerHTML_tests_innerHTML_1.html dfb6ec1e412a6634b497151f222f4272721ae57e'), "%3Ca%3E%3Ctd%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ctbody%3E%0A%7C%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%3Ctd%3E", 'table'],"4916096ab7dbdd051dc96de7952ec60674d617a3":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 4916096ab7dbdd051dc96de7952ec60674d617a3'), "%3Ctable%3E%3C/table%3E%3Ctbody%3E", "%23document%0A%7C%20%3Ctable%3E", 'caption'],"58277507de4ce0df9b4eb03262e4cb04d2fcc7af":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 58277507de4ce0df9b4eb03262e4cb04d2fcc7af'), "%3C/table%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E", 'caption'],"8e5727f170507ba6b86c1c3bdb3a5201d3480b33":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 8e5727f170507ba6b86c1c3bdb3a5201d3480b33'), "%3Cspan%3E%3C/table%3E", "%23document%0A%7C%20%3Cspan%3E", 'caption'],"61eb343f71030688ab1a26bb980b9e4409993e3d":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 61eb343f71030688ab1a26bb980b9e4409993e3d'), "%3C/caption%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E", 'caption'],"3548939a56a148b59781c5f930036c1528db1545":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 3548939a56a148b59781c5f930036c1528db1545'), "%3Cspan%3E%3C/caption%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"0b88a12102fba0634b44ac9f157544faeca68df6":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 0b88a12102fba0634b44ac9f157544faeca68df6'), "%3Cspan%3E%3Ccaption%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"c300553f45f4ad6e5e1da9d884fafb95f36ab05a":[async_test('html5lib_innerHTML_tests_innerHTML_1.html c300553f45f4ad6e5e1da9d884fafb95f36ab05a'), "%3Cspan%3E%3Ccol%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"00a7ed4082183eba76af197418305b2d196ec7e6":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 00a7ed4082183eba76af197418305b2d196ec7e6'), "%3Cspan%3E%3Ccolgroup%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"5dfd84382f85ff8343a62d6bb5028996111e5017":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 5dfd84382f85ff8343a62d6bb5028996111e5017'), "%3Cspan%3E%3Chtml%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"9b1a1cd33bb3f26ec3f969f3158edf5c2db47052":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 9b1a1cd33bb3f26ec3f969f3158edf5c2db47052'), "%3Cspan%3E%3Ctbody%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"8ab89f758b96bf994a21d8283d27e8ae9804b924":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 8ab89f758b96bf994a21d8283d27e8ae9804b924'), "%3Cspan%3E%3Ctd%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"25add8314f59cdc264d7779f36ae4dffd1f9ad29":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 25add8314f59cdc264d7779f36ae4dffd1f9ad29'), "%3Cspan%3E%3Ctfoot%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"7104e981f6018b18766fd95109bfbffeb878cc56":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 7104e981f6018b18766fd95109bfbffeb878cc56'), "%3Cspan%3E%3Cthead%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"28fbaa710ce3f440a56fdf909c4b8bc223a1b965":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 28fbaa710ce3f440a56fdf909c4b8bc223a1b965'), "%3Cspan%3E%3Cth%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"3380b0143f3d1e2edb216d388acd72702e141165":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 3380b0143f3d1e2edb216d388acd72702e141165'), "%3Cspan%3E%3Ctr%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"a630fb272be6de118b728a28d6ce71b296a75694":[async_test('html5lib_innerHTML_tests_innerHTML_1.html a630fb272be6de118b728a28d6ce71b296a75694'), "%3Cspan%3E%3C/table%3E%3Cspan%3E", "%23document%0A%7C%20%3Cspan%3E%0A%7C%20%20%20%3Cspan%3E", 'caption'],"5adf8b7bd3d63a77ea1dbe6deb6741c5b92de6cd":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 5adf8b7bd3d63a77ea1dbe6deb6741c5b92de6cd'), "%3C/colgroup%3E%3Ccol%3E", "%23document%0A%7C%20%3Ccol%3E", 'colgroup'],"4e0312ac349a70d07f2bb1ae154740e46e8c9a6e":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 4e0312ac349a70d07f2bb1ae154740e46e8c9a6e'), "%3Ca%3E%3Ccol%3E", "%23document%0A%7C%20%3Ccol%3E", 'colgroup'],"987061379d2542e88d8a72ce6f0169a211d3ac41":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 987061379d2542e88d8a72ce6f0169a211d3ac41'), "%3Ccaption%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'tbody'],"e0798aa003863ba2be750d3e6c2e6766fea11279":[async_test('html5lib_innerHTML_tests_innerHTML_1.html e0798aa003863ba2be750d3e6c2e6766fea11279'), "%3Ccol%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'tbody'],"586ed8e0d1395198f43ee68843d654a49169f379":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 586ed8e0d1395198f43ee68843d654a49169f379'), "%3Ccolgroup%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'tbody'],"8896feedef576c1ed768a4eb67f57c3dc5242fed":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 8896feedef576c1ed768a4eb67f57c3dc5242fed'), "%3Ctbody%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'tbody'],"89f4f0f289a23d1ebfbf499c2ecd24cd35fc10b4":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 89f4f0f289a23d1ebfbf499c2ecd24cd35fc10b4'), "%3Ctfoot%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'tbody'],"2c07361470533b905a7ff9a685439cad2fe35549":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 2c07361470533b905a7ff9a685439cad2fe35549'), "%3Cthead%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'tbody'],"a36f27cbe53991c647055c95bfa250a0ab734b0d":[async_test('html5lib_innerHTML_tests_innerHTML_1.html a36f27cbe53991c647055c95bfa250a0ab734b0d'), "%3C/table%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'tbody'],"ac28c352ae8453434e3aefb24e798a9580c1b230":[async_test('html5lib_innerHTML_tests_innerHTML_1.html ac28c352ae8453434e3aefb24e798a9580c1b230'), "%3Ca%3E%3Ctr%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ctr%3E", 'tbody'],"f8770f97671a805b37277db7e42536b40c0804cb":[async_test('html5lib_innerHTML_tests_innerHTML_1.html f8770f97671a805b37277db7e42536b40c0804cb'), "%3Ca%3E%3Ctd%3E", "%23document%0A%7C%20%3Ca%3E%0A%7C%20%3Ctr%3E%0A%7C%20%20%20%3Ctd%3E", 'tbody'],"469a071b520d8436e6a0f6da6f9385f5ebd8e2f8":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 469a071b520d8436e6a0f6da6f9385f5ebd8e2f8'), "%3Ctd%3E%3Ctable%3E%3Ctbody%3E%3Ca%3E%3Ctr%3E", "%23document%0A%7C%20%3Ctr%3E%0A%7C%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E", 'tbody'],"7cf2db8c65b79da98e39b13772ed0440ff177fd7":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 7cf2db8c65b79da98e39b13772ed0440ff177fd7'), "%3C/tr%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"3940fdb54783cb3c42138670a17d28e77e29e900":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 3940fdb54783cb3c42138670a17d28e77e29e900'), "%3Ctd%3E%3Ctable%3E%3Ca%3E%3Ctr%3E%3C/tr%3E%3Ctr%3E", "%23document%0A%7C%20%3Ctd%3E%0A%7C%20%20%20%3Ca%3E%0A%7C%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%3Ctr%3E", 'tr'],"3b5ab5fbb3585d7215a5766f1e2377b7929b5cc6":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 3b5ab5fbb3585d7215a5766f1e2377b7929b5cc6'), "%3Ccaption%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"7c4a40203d5830d36432b0f30a09cebea6e9d2e4":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 7c4a40203d5830d36432b0f30a09cebea6e9d2e4'), "%3Ccol%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"e6d930d4239666fdc6c0722106bd2b115b4d3fd3":[async_test('html5lib_innerHTML_tests_innerHTML_1.html e6d930d4239666fdc6c0722106bd2b115b4d3fd3'), "%3Ccolgroup%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"c8817b3b55a437bd153e978fc5f49fbe10bb56e2":[async_test('html5lib_innerHTML_tests_innerHTML_1.html c8817b3b55a437bd153e978fc5f49fbe10bb56e2'), "%3Ctbody%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"2a302c14f1983aaad9fd7abe49336d9561ed82e9":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 2a302c14f1983aaad9fd7abe49336d9561ed82e9'), "%3Ctfoot%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"28cfddd5b5875f7044b0859ba4ce88175fcbf07e":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 28cfddd5b5875f7044b0859ba4ce88175fcbf07e'), "%3Cthead%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"887596c50809eeb809ad24d86c239130a42f5a46":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 887596c50809eeb809ad24d86c239130a42f5a46'), "%3Ctr%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"fa3797a2d2baeb8b8b2de81f1e7f33725e6b2aad":[async_test('html5lib_innerHTML_tests_innerHTML_1.html fa3797a2d2baeb8b8b2de81f1e7f33725e6b2aad'), "%3C/table%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E", 'tr'],"dc0e2582ff83e60c0eb549dc3387562d3482e364":[async_test('html5lib_innerHTML_tests_innerHTML_1.html dc0e2582ff83e60c0eb549dc3387562d3482e364'), "%3Ctd%3E%3Ctable%3E%3C/table%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctd%3E%0A%7C%20%20%20%3Ctable%3E%0A%7C%20%3Ctd%3E", 'tr'],"4ea02fd705291eb2d14274ebeaa0117ba2b9b306":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 4ea02fd705291eb2d14274ebeaa0117ba2b9b306'), "%3Ccaption%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"52cc77eac9488a8bb1a2c8c695f16f8919c52044":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 52cc77eac9488a8bb1a2c8c695f16f8919c52044'), "%3Ccol%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"c53ab7d84ae27de9f0937521c74eec2fd6c1b1ec":[async_test('html5lib_innerHTML_tests_innerHTML_1.html c53ab7d84ae27de9f0937521c74eec2fd6c1b1ec'), "%3Ccolgroup%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"d8747a49503f3486155d77dd366e0ee8ad9512a6":[async_test('html5lib_innerHTML_tests_innerHTML_1.html d8747a49503f3486155d77dd366e0ee8ad9512a6'), "%3Ctbody%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"03e71832c254852f206f6f0ae6f4d161a276a699":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 03e71832c254852f206f6f0ae6f4d161a276a699'), "%3Ctfoot%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"92786cfafa890c23f200dccea089ca52233ef395":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 92786cfafa890c23f200dccea089ca52233ef395'), "%3Cth%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"11d284c2b1e2f87d28dd06b938518361fe834855":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 11d284c2b1e2f87d28dd06b938518361fe834855'), "%3Cthead%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"bf6aebcd54d5dead9e6d56c77b41f01ea666d8b1":[async_test('html5lib_innerHTML_tests_innerHTML_1.html bf6aebcd54d5dead9e6d56c77b41f01ea666d8b1'), "%3Ctr%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"4d448a4239cb4c465a21c04997d656e51fdd388f":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 4d448a4239cb4c465a21c04997d656e51fdd388f'), "%3C/table%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"1e8faedc427045d59305218c1aba2f545c4eb4b7":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 1e8faedc427045d59305218c1aba2f545c4eb4b7'), "%3C/tbody%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"6762997c1a93b1ec65722498f3fd00f0d8129369":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 6762997c1a93b1ec65722498f3fd00f0d8129369'), "%3C/td%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"a162461c18d9b09734f6fe5d362b84edb4eed31f":[async_test('html5lib_innerHTML_tests_innerHTML_1.html a162461c18d9b09734f6fe5d362b84edb4eed31f'), "%3C/tfoot%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"0befc335ffd6cace344d94a35de96af22b1313a5":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 0befc335ffd6cace344d94a35de96af22b1313a5'), "%3C/thead%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"a23b70f1f246ba08d13b570319391b4a5c3e9456":[async_test('html5lib_innerHTML_tests_innerHTML_1.html a23b70f1f246ba08d13b570319391b4a5c3e9456'), "%3C/th%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"9d5e0c25bfe921df9ea2897c027f42bc88950e69":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 9d5e0c25bfe921df9ea2897c027f42bc88950e69'), "%3C/tr%3E%3Ca%3E", "%23document%0A%7C%20%3Ca%3E", 'td'],"9210d577d6deecf5ab3505af86c501c5befa0b50":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 9210d577d6deecf5ab3505af86c501c5befa0b50'), "%3Ctable%3E%3Ctd%3E%3Ctd%3E", "%23document%0A%7C%20%3Ctable%3E%0A%7C%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%3Ctd%3E", 'td'],"c34af491c0a339db6ba63fcc478108533347319b":[async_test('html5lib_innerHTML_tests_innerHTML_1.html c34af491c0a339db6ba63fcc478108533347319b'), "%3C/select%3E%3Coption%3E", "%23document%0A%7C%20%3Coption%3E", 'select'],"2c4284e6b2bb480daa50bca43bcbe29cfcdeeab4":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 2c4284e6b2bb480daa50bca43bcbe29cfcdeeab4'), "%3Cinput%3E%3Coption%3E", "%23document%0A%7C%20%3Coption%3E", 'select'],"d75277b65d0118463afeb66b478509d4e27565ab":[async_test('html5lib_innerHTML_tests_innerHTML_1.html d75277b65d0118463afeb66b478509d4e27565ab'), "%3Ckeygen%3E%3Coption%3E", "%23document%0A%7C%20%3Coption%3E", 'select'],"b354df69dbe9b3ef0c42177648e3aace114cf8ea":[async_test('html5lib_innerHTML_tests_innerHTML_1.html b354df69dbe9b3ef0c42177648e3aace114cf8ea'), "%3Ctextarea%3E%3Coption%3E", "%23document%0A%7C%20%3Coption%3E", 'select'],"fd3be386292ea1f411cea8e86e29595deb177d28":[async_test('html5lib_innerHTML_tests_innerHTML_1.html fd3be386292ea1f411cea8e86e29595deb177d28'), "%3C/html%3E%3C%21--abc--%3E", "%23document%0A%7C%20%3Chead%3E%0A%7C%20%3Cbody%3E%0A%7C%20%3C%21--%20abc%20--%3E", 'html'],"1cfb3baf2ad29109ddd5581daa3a009029c71491":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 1cfb3baf2ad29109ddd5581daa3a009029c71491'), "%3C/frameset%3E%3Cframe%3E", "%23document%0A%7C%20%3Cframe%3E", 'frameset'],"2555d238e04f3d2853cfbc5f6dd366f82cf0e868":[async_test('html5lib_innerHTML_tests_innerHTML_1.html 2555d238e04f3d2853cfbc5f6dd366f82cf0e868'), "", "%23document%0A%7C%20%3Chead%3E%0A%7C%20%3Cbody%3E", 'html'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_webkit02.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_webkit02.html
new file mode 100644
index 0000000000..ae78d7bd22
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_innerHTML_webkit02.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_innerHTML_webkit02.html</title>
+ <meta name="timeout" content="long">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['cac5528d0cbea4d15babba38304646e3903324a6','bafeef55f21b568ab89a91082464614e4ebe7c2f','9461cfc6d9d4f08b05b3a95bbe5baa264f868a44','c2c4647447354abc154f1917a7fbefa4a679d5fb',];
+ var tests = {
+ "cac5528d0cbea4d15babba38304646e3903324a6":[async_test('html5lib_innerHTML_webkit02.html cac5528d0cbea4d15babba38304646e3903324a6'), "%3Cb%3E%3Cem%3E%3Cdcell%3E%3Cpostfield%3E%3Cpostfield%3E%3Cpostfield%3E%3Cpostfield%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Chkern%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cdcell%3E%0A%7C%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chkern%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"bafeef55f21b568ab89a91082464614e4ebe7c2f":[async_test('html5lib_innerHTML_webkit02.html bafeef55f21b568ab89a91082464614e4ebe7c2f'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"9461cfc6d9d4f08b05b3a95bbe5baa264f868a44":[async_test('html5lib_innerHTML_webkit02.html 9461cfc6d9d4f08b05b3a95bbe5baa264f868a44'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoob%3E%3Cfoob%3E%3Cfoob%3E%3Cfoob%3E%3Cfooc%3E%3Cfooc%3E%3Cfooc%3E%3Cfooc%3E%3Cfood%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfood%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"c2c4647447354abc154f1917a7fbefa4a679d5fb":[async_test('html5lib_innerHTML_webkit02.html c2c4647447354abc154f1917a7fbefa4a679d5fb'), "%3Coption%3E%3CXH%3Coptgroup%3E%3C/optgroup%3E", "%23document%0A%7C%20%3Coption%3E", 'select'],
+ }
+ init_tests("innerHTML");
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_isindex.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_isindex.html
new file mode 100644
index 0000000000..be58612328
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_isindex.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_isindex.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['579ca96e69c47b3d2ac83f1aa79a450b745d21f3','cb91f67071d81dd18d7ba9990de8f0f845c375f0','bd8ac64cc8f1422fac94bbe1c8828c0b51dca3f2','4303a393c6933743460836cb5e7dd29ca7fd6f43',];
+ var tests = {
+ "579ca96e69c47b3d2ac83f1aa79a450b745d21f3":[async_test('html5lib_isindex.html 579ca96e69c47b3d2ac83f1aa79a450b745d21f3'), "%3Cisindex%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cisindex%3E"],"cb91f67071d81dd18d7ba9990de8f0f845c375f0":[async_test('html5lib_isindex.html cb91f67071d81dd18d7ba9990de8f0f845c375f0'), "%3Cisindex%20name%3D%22A%22%20action%3D%22B%22%20prompt%3D%22C%22%20foo%3D%22D%22%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cisindex%3E%0A%7C%20%20%20%20%20%20%20action%3D%22B%22%0A%7C%20%20%20%20%20%20%20foo%3D%22D%22%0A%7C%20%20%20%20%20%20%20name%3D%22A%22%0A%7C%20%20%20%20%20%20%20prompt%3D%22C%22"],"bd8ac64cc8f1422fac94bbe1c8828c0b51dca3f2":[async_test('html5lib_isindex.html bd8ac64cc8f1422fac94bbe1c8828c0b51dca3f2'), "%3Cform%3E%3Cisindex%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cform%3E%0A%7C%20%20%20%20%20%20%20%3Cisindex%3E"],"4303a393c6933743460836cb5e7dd29ca7fd6f43":[async_test('html5lib_isindex.html 4303a393c6933743460836cb5e7dd29ca7fd6f43'), "%3C%21doctype%20html%3E%3Cisindex%3Ex%3C/isindex%3Ex", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cisindex%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%22x%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_main-element.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_main-element.html
new file mode 100644
index 0000000000..8f01831505
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_main-element.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_main-element.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['336a047fbc84b86dfd4baea5881b50fe0cdb5ce8','fc887f078ddc2723261a7dfb25829efe2da284f2','ebd10973e73d3a339bdf22f8bbac2f028044e096',];
+ var tests = {
+ "336a047fbc84b86dfd4baea5881b50fe0cdb5ce8":[async_test('html5lib_main-element.html 336a047fbc84b86dfd4baea5881b50fe0cdb5ce8'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Cmain%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cmain%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"fc887f078ddc2723261a7dfb25829efe2da284f2":[async_test('html5lib_main-element.html fc887f078ddc2723261a7dfb25829efe2da284f2'), "%3C%21doctype%20html%3E%3Cmain%3E%3Cp%3Efoo%3C/main%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmain%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"ebd10973e73d3a339bdf22f8bbac2f028044e096":[async_test('html5lib_main-element.html ebd10973e73d3a339bdf22f8bbac2f028044e096'), "%3C%21DOCTYPE%20html%3Exxx%3Csvg%3E%3Cx%3E%3Cg%3E%3Ca%3E%3Cmain%3E%3Cb%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22xxx%22%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20x%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20a%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20main%3E%0A%7C%20%20%20%20%20%3Cb%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_menuitem-element.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_menuitem-element.html
new file mode 100644
index 0000000000..b8688e29a8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_menuitem-element.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_menuitem-element.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['e61b5db0435eb768ec21c1aa7355c649e7969c17','9c975c544402eed521499270b0e97cfa78f155b0','d46fa11c0107d59c84778beae84f388f55bffc31','afcd3b1e3317ac609ddab924d836ba1e3873b80f','95c0c6923fe609297c1592f2cb82bb9f2d0f5aed','e2772fe779cbcefb4458f169a0cd495cf7115845','7a9fa28f6207f045ebb0aa49938debd0c1e7123e','798bb352d9f256153340661e1277e44674f1026d','f2b5a63d94f108207a7a998216222dc24bea4850','778c027d06495eb361dd83baa561feb3a21ec3ea','e4670bee2ce790e82c26a33319b7fe082fbbdaea','79307be24287ca5d0533dfa81b91dd826f5f5e0e','9b995cb730b12529e8e755e4a0b0a2e73d1dfcfa','d4586cd7706bbb3a5b127c52c1f2861d1a3fb781','e2adbd7bf4c7480343cfb8f69289c824be613853','b56d35c73f38f04ad6fdf51aa88f4b70a93ddc48','0f95585196dae2b1b5164e8c27897699c464c35f','03664aa93a55daceccc26d99c0aef841d8862af5','6d596b9e342db2306365fbdfb7615377c5b26347','4b712b488be9ee047c139c1b0cd955bae990b8e5',];
+ var tests = {
+ "e61b5db0435eb768ec21c1aa7355c649e7969c17":[async_test('html5lib_menuitem-element.html e61b5db0435eb768ec21c1aa7355c649e7969c17'), "%3Cmenuitem%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E"],"9c975c544402eed521499270b0e97cfa78f155b0":[async_test('html5lib_menuitem-element.html 9c975c544402eed521499270b0e97cfa78f155b0'), "%3C/menuitem%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"d46fa11c0107d59c84778beae84f388f55bffc31":[async_test('html5lib_menuitem-element.html d46fa11c0107d59c84778beae84f388f55bffc31'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmenuitem%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%22A%22"],"afcd3b1e3317ac609ddab924d836ba1e3873b80f":[async_test('html5lib_menuitem-element.html afcd3b1e3317ac609ddab924d836ba1e3873b80f'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmenuitem%3EA%3Cmenuitem%3EB", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22"],"95c0c6923fe609297c1592f2cb82bb9f2d0f5aed":[async_test('html5lib_menuitem-element.html 95c0c6923fe609297c1592f2cb82bb9f2d0f5aed'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmenuitem%3EA%3Cmenu%3EB%3C/menu%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Cmenu%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22"],"e2772fe779cbcefb4458f169a0cd495cf7115845":[async_test('html5lib_menuitem-element.html e2772fe779cbcefb4458f169a0cd495cf7115845'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmenuitem%3EA%3Chr%3EB", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Chr%3E%0A%7C%20%20%20%20%20%20%20%22B%22"],"7a9fa28f6207f045ebb0aa49938debd0c1e7123e":[async_test('html5lib_menuitem-element.html 7a9fa28f6207f045ebb0aa49938debd0c1e7123e'), "%3C%21DOCTYPE%20html%3E%3Cli%3E%3Cmenuitem%3E%3Cli%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%3Cli%3E"],"798bb352d9f256153340661e1277e44674f1026d":[async_test('html5lib_menuitem-element.html 798bb352d9f256153340661e1277e44674f1026d'), "%3C%21DOCTYPE%20html%3E%3Cmenuitem%3E%3Cp%3E%3C/menuitem%3Ex", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22x%22"],"f2b5a63d94f108207a7a998216222dc24bea4850":[async_test('html5lib_menuitem-element.html f2b5a63d94f108207a7a998216222dc24bea4850'), "%3C%21DOCTYPE%20html%3E%3Cp%3E%3Cb%3E%3C/p%3E%3Cmenuitem%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cmenuitem%3E"],"778c027d06495eb361dd83baa561feb3a21ec3ea":[async_test('html5lib_menuitem-element.html 778c027d06495eb361dd83baa561feb3a21ec3ea'), "%3C%21DOCTYPE%20html%3E%3Cmenuitem%3E%3Casdf%3E%3C/menuitem%3Ex", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%3Casdf%3E%0A%7C%20%20%20%20%20%22x%22"],"e4670bee2ce790e82c26a33319b7fe082fbbdaea":[async_test('html5lib_menuitem-element.html e4670bee2ce790e82c26a33319b7fe082fbbdaea'), "%3C%21DOCTYPE%20html%3E%3C/menuitem%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"79307be24287ca5d0533dfa81b91dd826f5f5e0e":[async_test('html5lib_menuitem-element.html 79307be24287ca5d0533dfa81b91dd826f5f5e0e'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3C/menuitem%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"9b995cb730b12529e8e755e4a0b0a2e73d1dfcfa":[async_test('html5lib_menuitem-element.html 9b995cb730b12529e8e755e4a0b0a2e73d1dfcfa'), "%3C%21DOCTYPE%20html%3E%3Chead%3E%3C/menuitem%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"d4586cd7706bbb3a5b127c52c1f2861d1a3fb781":[async_test('html5lib_menuitem-element.html d4586cd7706bbb3a5b127c52c1f2861d1a3fb781'), "%3C%21DOCTYPE%20html%3E%3Cselect%3E%3Cmenuitem%3E%3C/select%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"e2adbd7bf4c7480343cfb8f69289c824be613853":[async_test('html5lib_menuitem-element.html e2adbd7bf4c7480343cfb8f69289c824be613853'), "%3C%21DOCTYPE%20html%3E%3Coption%3E%3Cmenuitem%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Cmenuitem%3E"],"b56d35c73f38f04ad6fdf51aa88f4b70a93ddc48":[async_test('html5lib_menuitem-element.html b56d35c73f38f04ad6fdf51aa88f4b70a93ddc48'), "%3C%21DOCTYPE%20html%3E%3Cmenuitem%3E%3Coption%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E"],"0f95585196dae2b1b5164e8c27897699c464c35f":[async_test('html5lib_menuitem-element.html 0f95585196dae2b1b5164e8c27897699c464c35f'), "%3C%21DOCTYPE%20html%3E%3Cmenuitem%3E%3C/body%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E"],"03664aa93a55daceccc26d99c0aef841d8862af5":[async_test('html5lib_menuitem-element.html 03664aa93a55daceccc26d99c0aef841d8862af5'), "%3C%21DOCTYPE%20html%3E%3Cmenuitem%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E"],"6d596b9e342db2306365fbdfb7615377c5b26347":[async_test('html5lib_menuitem-element.html 6d596b9e342db2306365fbdfb7615377c5b26347'), "%3C%21DOCTYPE%20html%3E%3Cmenuitem%3E%3Cp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E"],"4b712b488be9ee047c139c1b0cd955bae990b8e5":[async_test('html5lib_menuitem-element.html 4b712b488be9ee047c139c1b0cd955bae990b8e5'), "%3C%21DOCTYPE%20html%3E%3Cmenuitem%3E%3Cli%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmenuitem%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_namespace-sensitivity.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_namespace-sensitivity.html
new file mode 100644
index 0000000000..2e462a9de4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_namespace-sensitivity.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_namespace-sensitivity.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['de0a2051123e97a540e3aeb58375103bda021122',];
+ var tests = {
+ "de0a2051123e97a540e3aeb58375103bda021122":[async_test('html5lib_namespace-sensitivity.html de0a2051123e97a540e3aeb58375103bda021122'), "%3Cbody%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3Ctd%3E%3CforeignObject%3E%3Cspan%3E%3C/td%3EFoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Foo%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20td%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes-plain-text-unsafe.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes-plain-text-unsafe.html
new file mode 100644
index 0000000000..3943758423
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes-plain-text-unsafe.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_pending-spec-changes-plain-text-unsafe.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['8afa8d082dc447be5cab2eeb3e13efb07ec72aa6',];
+ var tests = {
+ "8afa8d082dc447be5cab2eeb3e13efb07ec72aa6":[async_test('html5lib_pending-spec-changes-plain-text-unsafe.html 8afa8d082dc447be5cab2eeb3e13efb07ec72aa6'), "%3Cbody%3E%3Ctable%3E%00filler%00text%00", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22fillertext%22%0A%7C%20%20%20%20%20%3Ctable%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes.html
new file mode 100644
index 0000000000..f7aa17e72a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_pending-spec-changes.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_pending-spec-changes.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['965e062f9d01c4334cb12637e84dcbf438d38faf','8772d25919914a17118b2105e126aaa5bc83f92c','9804e9659cd045f199d9f58ef85c2639724359aa',];
+ var tests = {
+ "965e062f9d01c4334cb12637e84dcbf438d38faf":[async_test('html5lib_pending-spec-changes.html 965e062f9d01c4334cb12637e84dcbf438d38faf'), "%3Cinput%20type%3D%22hidden%22%3E%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"8772d25919914a17118b2105e126aaa5bc83f92c":[async_test('html5lib_pending-spec-changes.html 8772d25919914a17118b2105e126aaa5bc83f92c'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ccaption%3E%3Csvg%3Efoo%3C/table%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"9804e9659cd045f199d9f58ef85c2639724359aa":[async_test('html5lib_pending-spec-changes.html 9804e9659cd045f199d9f58ef85c2639724359aa'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3Cdesc%3E%3Ctd%3E%3C/desc%3E%3Ccircle%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccircle%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_plain-text-unsafe.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_plain-text-unsafe.html
new file mode 100644
index 0000000000..16c03cbe45
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_plain-text-unsafe.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_plain-text-unsafe.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['7e4ca4cb5e73852744a876bf8652dd2c8998d94a','e552342bdd3ac62316bd91126556512683f3d4a9','9112cef60139e6988b66334e522777329051442a','5dfa36c8a466dd245e7977924936f4940df65e53','e057c57a5f32e5e341d70b0a6581bdad39b84ac5','24bcb6928bd42daaf1f3688caf93bb399d159bfc','24ed904940ecdad39c61706e709ffed5ee804514','0829c314a34a62cd1caf52ddc4100050bf32aef3','024ba847361612e5e87bce271d0497eeee07f87f','ec721aae75752c688fa48ad4b251d3a5a394dc8f','1e262b6f330b0870e854458f11b2280c72c8bd16','29f42d8bf04aea6ab4c051bd77da7e79173cf159','1d65ea8967bdc24e89d6548320b7fb9e2ad2c915','f1c51609a504e4ab4f7ae4d61fb6175619974b0d','692122e8d2a27e37cb585c827fb33e22e31c018e','a4537cad9c2386108157889b3bf6ddd414f0366d','252ca95f75b077320bc167cb117a05edfcbd7d0e','dde4bba02ef4e37d010e552111bc668d9660a076','41b9352a4d99b4fd545551fe260754b3137ad148','4b9e317cba617324fea77a4e525602b3ad2717ef','a1e08cb99d89381a1c997fcd60bad23c029c4500','26d850208425cc885d4d0143909cf341f61fa1f1','68f0365c01dc386c706edd2b18672f9d85caaa2e','e415a2e7cf090e2c308af905d52c5f8163ae52ce','822702de65b80ec8e79da19335ab9d6a49f6ec6a','ee8b017ab043ff51b593787961626acb4c6488cd','304960c795639128844445166238350682ba0516','275bb0b518ec00b1e64a28cb9088989371fca9d9','068ac565b7c7bdad572f26dafb4580483cdbc6f7','f5ff59e9765a468913e14a6a9612edb2cd4f30f0','9ff76ac49f95ab0e22278fd07ed4dfee50519286','6a9486cce6ebcb83329c17ebcf3c9b6fe8bb8096','6f2f3f4f91f5484170a6dc8cc6165ce0ce498bff',];
+ var tests = {
+ "7e4ca4cb5e73852744a876bf8652dd2c8998d94a":[async_test('html5lib_plain-text-unsafe.html 7e4ca4cb5e73852744a876bf8652dd2c8998d94a'), "FOO%26%23x000D%3BZOO", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%0DZOO%22"],"e552342bdd3ac62316bd91126556512683f3d4a9":[async_test('html5lib_plain-text-unsafe.html e552342bdd3ac62316bd91126556512683f3d4a9'), "%3Chtml%3E%00%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"9112cef60139e6988b66334e522777329051442a":[async_test('html5lib_plain-text-unsafe.html 9112cef60139e6988b66334e522777329051442a'), "%3Chtml%3E%20%00%20%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"5dfa36c8a466dd245e7977924936f4940df65e53":[async_test('html5lib_plain-text-unsafe.html 5dfa36c8a466dd245e7977924936f4940df65e53'), "%3Chtml%3Ea%00a%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22aa%22"],"e057c57a5f32e5e341d70b0a6581bdad39b84ac5":[async_test('html5lib_plain-text-unsafe.html e057c57a5f32e5e341d70b0a6581bdad39b84ac5'), "%3Chtml%3E%00%00%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"24bcb6928bd42daaf1f3688caf93bb399d159bfc":[async_test('html5lib_plain-text-unsafe.html 24bcb6928bd42daaf1f3688caf93bb399d159bfc'), "%3Chtml%3E%00%0A%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"24ed904940ecdad39c61706e709ffed5ee804514":[async_test('html5lib_plain-text-unsafe.html 24ed904940ecdad39c61706e709ffed5ee804514'), "%3Chtml%3E%3Cselect%3E%00", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"0829c314a34a62cd1caf52ddc4100050bf32aef3":[async_test('html5lib_plain-text-unsafe.html 0829c314a34a62cd1caf52ddc4100050bf32aef3'), "%00", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"024ba847361612e5e87bce271d0497eeee07f87f":[async_test('html5lib_plain-text-unsafe.html 024ba847361612e5e87bce271d0497eeee07f87f'), "%3Cbody%3E%00", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"ec721aae75752c688fa48ad4b251d3a5a394dc8f":[async_test('html5lib_plain-text-unsafe.html ec721aae75752c688fa48ad4b251d3a5a394dc8f'), "%3Cplaintext%3E%00filler%00text%00", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%EF%BF%BDfiller%EF%BF%BDtext%EF%BF%BD%22"],"1e262b6f330b0870e854458f11b2280c72c8bd16":[async_test('html5lib_plain-text-unsafe.html 1e262b6f330b0870e854458f11b2280c72c8bd16'), "%3Csvg%3E%3C%21%5BCDATA%5B%00filler%00text%00%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%EF%BF%BDfiller%EF%BF%BDtext%EF%BF%BD%22"],"29f42d8bf04aea6ab4c051bd77da7e79173cf159":[async_test('html5lib_plain-text-unsafe.html 29f42d8bf04aea6ab4c051bd77da7e79173cf159'), "%3Cbody%3E%3C%21%00%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3C%21--%20%EF%BF%BD%20--%3E"],"1d65ea8967bdc24e89d6548320b7fb9e2ad2c915":[async_test('html5lib_plain-text-unsafe.html 1d65ea8967bdc24e89d6548320b7fb9e2ad2c915'), "%3Cbody%3E%3C%21%00filler%00text%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3C%21--%20%EF%BF%BDfiller%EF%BF%BDtext%20--%3E"],"f1c51609a504e4ab4f7ae4d61fb6175619974b0d":[async_test('html5lib_plain-text-unsafe.html f1c51609a504e4ab4f7ae4d61fb6175619974b0d'), "%3Cbody%3E%3Csvg%3E%3CforeignObject%3E%00filler%00text", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%22fillertext%22"],"692122e8d2a27e37cb585c827fb33e22e31c018e":[async_test('html5lib_plain-text-unsafe.html 692122e8d2a27e37cb585c827fb33e22e31c018e'), "%3Csvg%3E%00filler%00text", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%EF%BF%BDfiller%EF%BF%BDtext%22"],"a4537cad9c2386108157889b3bf6ddd414f0366d":[async_test('html5lib_plain-text-unsafe.html a4537cad9c2386108157889b3bf6ddd414f0366d'), "%3Csvg%3E%00%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%EF%BF%BD%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20frameset%3E"],"252ca95f75b077320bc167cb117a05edfcbd7d0e":[async_test('html5lib_plain-text-unsafe.html 252ca95f75b077320bc167cb117a05edfcbd7d0e'), "%3Csvg%3E%00%20%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%EF%BF%BD%20%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20frameset%3E"],"dde4bba02ef4e37d010e552111bc668d9660a076":[async_test('html5lib_plain-text-unsafe.html dde4bba02ef4e37d010e552111bc668d9660a076'), "%3Csvg%3E%00a%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%EF%BF%BDa%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20frameset%3E"],"41b9352a4d99b4fd545551fe260754b3137ad148":[async_test('html5lib_plain-text-unsafe.html 41b9352a4d99b4fd545551fe260754b3137ad148'), "%3Csvg%3E%00%3C/svg%3E%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"4b9e317cba617324fea77a4e525602b3ad2717ef":[async_test('html5lib_plain-text-unsafe.html 4b9e317cba617324fea77a4e525602b3ad2717ef'), "%3Csvg%3E%00%20%3C/svg%3E%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"a1e08cb99d89381a1c997fcd60bad23c029c4500":[async_test('html5lib_plain-text-unsafe.html a1e08cb99d89381a1c997fcd60bad23c029c4500'), "%3Csvg%3E%00a%3C/svg%3E%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%EF%BF%BDa%22"],"26d850208425cc885d4d0143909cf341f61fa1f1":[async_test('html5lib_plain-text-unsafe.html 26d850208425cc885d4d0143909cf341f61fa1f1'), "%3Csvg%3E%3Cpath%3E%3C/path%3E%3C/svg%3E%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"68f0365c01dc386c706edd2b18672f9d85caaa2e":[async_test('html5lib_plain-text-unsafe.html 68f0365c01dc386c706edd2b18672f9d85caaa2e'), "%3Csvg%3E%3Cp%3E%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"e415a2e7cf090e2c308af905d52c5f8163ae52ce":[async_test('html5lib_plain-text-unsafe.html e415a2e7cf090e2c308af905d52c5f8163ae52ce'), "%3C%21DOCTYPE%20html%3E%3Cpre%3E%0D%0A%0D%0AA%3C/pre%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22%0AA%22"],"822702de65b80ec8e79da19335ab9d6a49f6ec6a":[async_test('html5lib_plain-text-unsafe.html 822702de65b80ec8e79da19335ab9d6a49f6ec6a'), "%3C%21DOCTYPE%20html%3E%3Cpre%3E%0D%0DA%3C/pre%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22%0AA%22"],"ee8b017ab043ff51b593787961626acb4c6488cd":[async_test('html5lib_plain-text-unsafe.html ee8b017ab043ff51b593787961626acb4c6488cd'), "%3C%21DOCTYPE%20html%3E%3Cpre%3E%0DA%3C/pre%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22A%22"],"304960c795639128844445166238350682ba0516":[async_test('html5lib_plain-text-unsafe.html 304960c795639128844445166238350682ba0516'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cmath%3E%3Cmtext%3E%00a", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"275bb0b518ec00b1e64a28cb9088989371fca9d9":[async_test('html5lib_plain-text-unsafe.html 275bb0b518ec00b1e64a28cb9088989371fca9d9'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3CforeignObject%3E%00a", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"068ac565b7c7bdad572f26dafb4580483cdbc6f7":[async_test('html5lib_plain-text-unsafe.html 068ac565b7c7bdad572f26dafb4580483cdbc6f7'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cmi%3Ea%00b", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22ab%22"],"f5ff59e9765a468913e14a6a9612edb2cd4f30f0":[async_test('html5lib_plain-text-unsafe.html f5ff59e9765a468913e14a6a9612edb2cd4f30f0'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cmo%3Ea%00b", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mo%3E%0A%7C%20%20%20%20%20%20%20%20%20%22ab%22"],"9ff76ac49f95ab0e22278fd07ed4dfee50519286":[async_test('html5lib_plain-text-unsafe.html 9ff76ac49f95ab0e22278fd07ed4dfee50519286'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cmn%3Ea%00b", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mn%3E%0A%7C%20%20%20%20%20%20%20%20%20%22ab%22"],"6a9486cce6ebcb83329c17ebcf3c9b6fe8bb8096":[async_test('html5lib_plain-text-unsafe.html 6a9486cce6ebcb83329c17ebcf3c9b6fe8bb8096'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cms%3Ea%00b", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20ms%3E%0A%7C%20%20%20%20%20%20%20%20%20%22ab%22"],"6f2f3f4f91f5484170a6dc8cc6165ce0ce498bff":[async_test('html5lib_plain-text-unsafe.html 6f2f3f4f91f5484170a6dc8cc6165ce0ce498bff'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cmtext%3Ea%00b", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%22ab%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_quirks01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_quirks01.html
new file mode 100644
index 0000000000..333292c9a2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_quirks01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_quirks01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['700e38ecb6ac91e9c5477fd451591b844d51f38e','d196aec77885a47ff43166960d48c8c9eb2cbee0','d4b597fe74a7af1b96750e29cb1180af8b97a1f4','e1bacd20039e6d1bed0de4aff51c37e303762170',];
+ var tests = {
+ "700e38ecb6ac91e9c5477fd451591b844d51f38e":[async_test('html5lib_quirks01.html 700e38ecb6ac91e9c5477fd451591b844d51f38e'), "%3C%21DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.0%20Frameset//EN%22%0A%22http%3A//www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd%22%3E%3Cp%3E%3Ctable%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20XHTML%201.0%20Frameset//EN%22%20%22http%3A//www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"d196aec77885a47ff43166960d48c8c9eb2cbee0":[async_test('html5lib_quirks01.html d196aec77885a47ff43166960d48c8c9eb2cbee0'), "%3C%21DOCTYPE%20html%20SYSTEM%20%22http%3A//www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd%22%3E%3Cp%3E%3Ctable%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22%22%20%22http%3A//www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],"d4b597fe74a7af1b96750e29cb1180af8b97a1f4":[async_test('html5lib_quirks01.html d4b597fe74a7af1b96750e29cb1180af8b97a1f4'), "%3C%21DOCTYPE%20html%20PUBLIC%20%22html%22%3E%3Cp%3E%3Ctable%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22html%22%20%22%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],"e1bacd20039e6d1bed0de4aff51c37e303762170":[async_test('html5lib_quirks01.html e1bacd20039e6d1bed0de4aff51c37e303762170'), "%3C%21DOCTYPE%20HTML%20PUBLIC%20%22-//W3C//DTD%20HTML%203.2//EN%22%0A%20%20%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E%3Cp%3E%3Ctable%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20HTML%203.2//EN%22%20%22http%3A//www.w3.org/TR/html4/strict.dtd%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_ruby.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_ruby.html
new file mode 100644
index 0000000000..b5b66ba883
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_ruby.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_ruby.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['6ee9e8955aa3a8a3af1838c1b728d8392e1cccca','03bbba49b30ec908e06e3c84e1fcede1ac7508ff','de7c3ce0c3484dc3a111d00c34c68f7dea8a013a','720c6bad03d4e4fd5ec7cb06bf4ec8ba5f277486','b3640f1eae210be30f138bbc6dc71e0864240224','3179776aceeadf1740d71e2d2e6385bb84ab97c0','d26f2542fc6fcee4e737b578c8db716a96a22ade','17d5c46418a50b35d893516440b0e091c31bd581','da1e119d0642e3b82a3537326a3fbc3b8c4e9706','bb2028f026500e8f77bd3b8e1f906c28045e6f20','c78f8740b860a39eb27035e19e29b240d0bfa4a8','9e880cf547d015ef52c3377064a9afd92ba26afe','9ed529d1072800af3fcccf268b41d6594480b749','42a6c56b0abc484cbc3d2b7517551c4aa3efc5d3','139a1e8af17546b7964354779331960096481b3c','0050f08d71e8dd050d8129afe674b580387468d9','5f9f25a089e72b4a4e5d7ebbacb440349c53f52f','099a0a617119e858c8e716e0ea9854ed1198ccf1','2cca13809bffb5fa34d03ad73d5727b75d2f96b5','f08a5ba63069cc6eb3804d94ec59b8b33e6f9b91','606924ac3b043141563d94141973a11eda8015ab',];
+ var tests = {
+ "6ee9e8955aa3a8a3af1838c1b728d8392e1cccca":[async_test('html5lib_ruby.html 6ee9e8955aa3a8a3af1838c1b728d8392e1cccca'), "%3Chtml%3E%3Cruby%3Ea%3Crb%3Eb%3Crb%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E"],"03bbba49b30ec908e06e3c84e1fcede1ac7508ff":[async_test('html5lib_ruby.html 03bbba49b30ec908e06e3c84e1fcede1ac7508ff'), "%3Chtml%3E%3Cruby%3Ea%3Crb%3Eb%3Crt%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E"],"de7c3ce0c3484dc3a111d00c34c68f7dea8a013a":[async_test('html5lib_ruby.html de7c3ce0c3484dc3a111d00c34c68f7dea8a013a'), "%3Chtml%3E%3Cruby%3Ea%3Crb%3Eb%3Crtc%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E"],"720c6bad03d4e4fd5ec7cb06bf4ec8ba5f277486":[async_test('html5lib_ruby.html 720c6bad03d4e4fd5ec7cb06bf4ec8ba5f277486'), "%3Chtml%3E%3Cruby%3Ea%3Crb%3Eb%3Crp%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E"],"b3640f1eae210be30f138bbc6dc71e0864240224":[async_test('html5lib_ruby.html b3640f1eae210be30f138bbc6dc71e0864240224'), "%3Chtml%3E%3Cruby%3Ea%3Crb%3Eb%3Cspan%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E"],"3179776aceeadf1740d71e2d2e6385bb84ab97c0":[async_test('html5lib_ruby.html 3179776aceeadf1740d71e2d2e6385bb84ab97c0'), "%3Chtml%3E%3Cruby%3Ea%3Crt%3Eb%3Crb%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E"],"d26f2542fc6fcee4e737b578c8db716a96a22ade":[async_test('html5lib_ruby.html d26f2542fc6fcee4e737b578c8db716a96a22ade'), "%3Chtml%3E%3Cruby%3Ea%3Crt%3Eb%3Crt%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E"],"17d5c46418a50b35d893516440b0e091c31bd581":[async_test('html5lib_ruby.html 17d5c46418a50b35d893516440b0e091c31bd581'), "%3Chtml%3E%3Cruby%3Ea%3Crt%3Eb%3Crtc%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E"],"da1e119d0642e3b82a3537326a3fbc3b8c4e9706":[async_test('html5lib_ruby.html da1e119d0642e3b82a3537326a3fbc3b8c4e9706'), "%3Chtml%3E%3Cruby%3Ea%3Crt%3Eb%3Crp%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E"],"bb2028f026500e8f77bd3b8e1f906c28045e6f20":[async_test('html5lib_ruby.html bb2028f026500e8f77bd3b8e1f906c28045e6f20'), "%3Chtml%3E%3Cruby%3Ea%3Crt%3Eb%3Cspan%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E"],"c78f8740b860a39eb27035e19e29b240d0bfa4a8":[async_test('html5lib_ruby.html c78f8740b860a39eb27035e19e29b240d0bfa4a8'), "%3Chtml%3E%3Cruby%3Ea%3Crtc%3Eb%3Crb%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E"],"9e880cf547d015ef52c3377064a9afd92ba26afe":[async_test('html5lib_ruby.html 9e880cf547d015ef52c3377064a9afd92ba26afe'), "%3Chtml%3E%3Cruby%3Ea%3Crtc%3Eb%3Crt%3Ec%3Crt%3Ed%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22d%22"],"9ed529d1072800af3fcccf268b41d6594480b749":[async_test('html5lib_ruby.html 9ed529d1072800af3fcccf268b41d6594480b749'), "%3Chtml%3E%3Cruby%3Ea%3Crtc%3Eb%3Crtc%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E"],"42a6c56b0abc484cbc3d2b7517551c4aa3efc5d3":[async_test('html5lib_ruby.html 42a6c56b0abc484cbc3d2b7517551c4aa3efc5d3'), "%3Chtml%3E%3Cruby%3Ea%3Crtc%3Eb%3Crp%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%3Crp%3E"],"139a1e8af17546b7964354779331960096481b3c":[async_test('html5lib_ruby.html 139a1e8af17546b7964354779331960096481b3c'), "%3Chtml%3E%3Cruby%3Ea%3Crtc%3Eb%3Cspan%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E"],"0050f08d71e8dd050d8129afe674b580387468d9":[async_test('html5lib_ruby.html 0050f08d71e8dd050d8129afe674b580387468d9'), "%3Chtml%3E%3Cruby%3Ea%3Crp%3Eb%3Crb%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E"],"5f9f25a089e72b4a4e5d7ebbacb440349c53f52f":[async_test('html5lib_ruby.html 5f9f25a089e72b4a4e5d7ebbacb440349c53f52f'), "%3Chtml%3E%3Cruby%3Ea%3Crp%3Eb%3Crt%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E"],"099a0a617119e858c8e716e0ea9854ed1198ccf1":[async_test('html5lib_ruby.html 099a0a617119e858c8e716e0ea9854ed1198ccf1'), "%3Chtml%3E%3Cruby%3Ea%3Crp%3Eb%3Crtc%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E"],"2cca13809bffb5fa34d03ad73d5727b75d2f96b5":[async_test('html5lib_ruby.html 2cca13809bffb5fa34d03ad73d5727b75d2f96b5'), "%3Chtml%3E%3Cruby%3Ea%3Crp%3Eb%3Crp%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E"],"f08a5ba63069cc6eb3804d94ec59b8b33e6f9b91":[async_test('html5lib_ruby.html f08a5ba63069cc6eb3804d94ec59b8b33e6f9b91'), "%3Chtml%3E%3Cruby%3Ea%3Crp%3Eb%3Cspan%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E"],"606924ac3b043141563d94141973a11eda8015ab":[async_test('html5lib_ruby.html 606924ac3b043141563d94141973a11eda8015ab'), "%3Chtml%3E%3Cruby%3E%3Crtc%3E%3Cruby%3Ea%3Crb%3Eb%3Crt%3E%3C/ruby%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Crtc%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Crt%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_scriptdata01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_scriptdata01.html
new file mode 100644
index 0000000000..1233917145
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_scriptdata01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_scriptdata01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['3fc7e140adb65f9cfdf39219996f98e0749f398d','38ab7bda69dfd05e3ff01788cf5dac2d150a712a','09abe1da30643fd813db75facc7fde7b75762d43','193920d1c0fff50991d61655c529c62075a3da7f','66754fc904199af367a0a2b26cc89ea1834939cc','8c8a34698899e39e7b0879c07821aeb90f46cd83','a454052b572d9952465190ec354ad0ec2defca0b','6cc06752976c2cc9a4239f608b47cdb8df2b0b6b','4176aaaa17b9009e1ac8a293acbaa0d0cfd56975','e05f6594ea01489b55fca25aa79e5cffa43d9587','c7728688e5d12d5350c4228ac82c03a9886bef2f','c2a2bd66cbf430ee517678470fb560fa8862fa86','e04bd4c273176c0638e98dce1cbdcfc6438ec7eb','96a8546fd066084179852aa72bb599c1ee6f513f','1dae0f9ba7e933c1f6f9912d2289c290b2c8495a','44437869d870fea50416386a003d0b477a7d4f9d','3c72e7aa1fe7b65598a88a8cba1d7691824ec8a2','026ad2b8a6c85ab7a8804b1553b16560bc36fefd','d6b18cb44b4eda72b75fb64fe4287ee984562aaf','6ebea78eedeb25cbefd06e1ff55423db91f98c6d','fb5424c9ab36d5f8383268df1d64b17bab310e4d','3123b21e759d33987c407189584a5533c31a7482','f208b834bd4a2e13d79cebd59c02659f2f75ff04','0b72c0fef80895598cdf691a4913ff7aa5b1934b','bc7d3b5d37eaf53e338d81215f552e55cb555fc2','df2e36330e2e9609b1b9e1ff8a9440a66281dd46',];
+ var tests = {
+ "3fc7e140adb65f9cfdf39219996f98e0749f398d":[async_test('html5lib_scriptdata01.html 3fc7e140adb65f9cfdf39219996f98e0749f398d'), "FOO%3Cscript%3E%27Hello%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27Hello%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"38ab7bda69dfd05e3ff01788cf5dac2d150a712a":[async_test('html5lib_scriptdata01.html 38ab7bda69dfd05e3ff01788cf5dac2d150a712a'), "FOO%3Cscript%3E%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%22BAR%22"],"09abe1da30643fd813db75facc7fde7b75762d43":[async_test('html5lib_scriptdata01.html 09abe1da30643fd813db75facc7fde7b75762d43'), "FOO%3Cscript%3E%3C/script%20%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%22BAR%22"],"193920d1c0fff50991d61655c529c62075a3da7f":[async_test('html5lib_scriptdata01.html 193920d1c0fff50991d61655c529c62075a3da7f'), "FOO%3Cscript%3E%3C/script/%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%22BAR%22"],"66754fc904199af367a0a2b26cc89ea1834939cc":[async_test('html5lib_scriptdata01.html 66754fc904199af367a0a2b26cc89ea1834939cc'), "FOO%3Cscript%3E%3C/script/%20%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%22BAR%22"],"8c8a34698899e39e7b0879c07821aeb90f46cd83":[async_test('html5lib_scriptdata01.html 8c8a34698899e39e7b0879c07821aeb90f46cd83'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%3C/scriptx%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%3C/scriptx%3EBAR%22"],"a454052b572d9952465190ec354ad0ec2defca0b":[async_test('html5lib_scriptdata01.html a454052b572d9952465190ec354ad0ec2defca0b'), "FOO%3Cscript%3E%3C/script%20foo%3D%22%3E%22%20dd%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%22BAR%22"],"6cc06752976c2cc9a4239f608b47cdb8df2b0b6b":[async_test('html5lib_scriptdata01.html 6cc06752976c2cc9a4239f608b47cdb8df2b0b6b'), "FOO%3Cscript%3E%27%3C%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"4176aaaa17b9009e1ac8a293acbaa0d0cfd56975":[async_test('html5lib_scriptdata01.html 4176aaaa17b9009e1ac8a293acbaa0d0cfd56975'), "FOO%3Cscript%3E%27%3C%21%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"e05f6594ea01489b55fca25aa79e5cffa43d9587":[async_test('html5lib_scriptdata01.html e05f6594ea01489b55fca25aa79e5cffa43d9587'), "FOO%3Cscript%3E%27%3C%21-%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21-%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"c7728688e5d12d5350c4228ac82c03a9886bef2f":[async_test('html5lib_scriptdata01.html c7728688e5d12d5350c4228ac82c03a9886bef2f'), "FOO%3Cscript%3E%27%3C%21--%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"c2a2bd66cbf430ee517678470fb560fa8862fa86":[async_test('html5lib_scriptdata01.html c2a2bd66cbf430ee517678470fb560fa8862fa86'), "FOO%3Cscript%3E%27%3C%21---%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21---%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"e04bd4c273176c0638e98dce1cbdcfc6438ec7eb":[async_test('html5lib_scriptdata01.html e04bd4c273176c0638e98dce1cbdcfc6438ec7eb'), "FOO%3Cscript%3E%27%3C%21--%3E%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%3E%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"96a8546fd066084179852aa72bb599c1ee6f513f":[async_test('html5lib_scriptdata01.html 96a8546fd066084179852aa72bb599c1ee6f513f'), "FOO%3Cscript%3E%27%3C%21--%20potato%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20potato%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"1dae0f9ba7e933c1f6f9912d2289c290b2c8495a":[async_test('html5lib_scriptdata01.html 1dae0f9ba7e933c1f6f9912d2289c290b2c8495a'), "FOO%3Cscript%3E%27%3C%21--%20%3CsCrIpt%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"44437869d870fea50416386a003d0b477a7d4f9d":[async_test('html5lib_scriptdata01.html 44437869d870fea50416386a003d0b477a7d4f9d'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt%3E%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%3E%27%3C/script%3EBAR%22"],"3c72e7aa1fe7b65598a88a8cba1d7691824ec8a2":[async_test('html5lib_scriptdata01.html 3c72e7aa1fe7b65598a88a8cba1d7691824ec8a2'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt%3E%20-%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%3E%20-%27%3C/script%3EBAR%22"],"026ad2b8a6c85ab7a8804b1553b16560bc36fefd":[async_test('html5lib_scriptdata01.html 026ad2b8a6c85ab7a8804b1553b16560bc36fefd'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt%3E%20--%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%3E%20--%27%3C/script%3EBAR%22"],"d6b18cb44b4eda72b75fb64fe4287ee984562aaf":[async_test('html5lib_scriptdata01.html d6b18cb44b4eda72b75fb64fe4287ee984562aaf'), "FOO%3Cscript%3E%27%3C%21--%20%3CsCrIpt%3E%20--%3E%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%3E%20--%3E%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"6ebea78eedeb25cbefd06e1ff55423db91f98c6d":[async_test('html5lib_scriptdata01.html 6ebea78eedeb25cbefd06e1ff55423db91f98c6d'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt%3E%20--%21%3E%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%3E%20--%21%3E%27%3C/script%3EBAR%22"],"fb5424c9ab36d5f8383268df1d64b17bab310e4d":[async_test('html5lib_scriptdata01.html fb5424c9ab36d5f8383268df1d64b17bab310e4d'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt%3E%20--%20%3E%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%3E%20--%20%3E%27%3C/script%3EBAR%22"],"3123b21e759d33987c407189584a5533c31a7482":[async_test('html5lib_scriptdata01.html 3123b21e759d33987c407189584a5533c31a7482'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt%20%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%20%27%3C/script%3EBAR%22"],"f208b834bd4a2e13d79cebd59c02659f2f75ff04":[async_test('html5lib_scriptdata01.html f208b834bd4a2e13d79cebd59c02659f2f75ff04'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt/%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt/%27%3C/script%3EBAR%22"],"0b72c0fef80895598cdf691a4913ff7aa5b1934b":[async_test('html5lib_scriptdata01.html 0b72c0fef80895598cdf691a4913ff7aa5b1934b'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt%5C%27%3C/script%3EBAR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt%5C%27%22%0A%7C%20%20%20%20%20%22BAR%22"],"bc7d3b5d37eaf53e338d81215f552e55cb555fc2":[async_test('html5lib_scriptdata01.html bc7d3b5d37eaf53e338d81215f552e55cb555fc2'), "FOO%3Cscript%20type%3D%22text/plain%22%3E%27%3C%21--%20%3CsCrIpt/%27%3C/script%3EBAR%3C/script%3EQUX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/plain%22%0A%7C%20%20%20%20%20%20%20%22%27%3C%21--%20%3CsCrIpt/%27%3C/script%3EBAR%22%0A%7C%20%20%20%20%20%22QUX%22"],"df2e36330e2e9609b1b9e1ff8a9440a66281dd46":[async_test('html5lib_scriptdata01.html df2e36330e2e9609b1b9e1ff8a9440a66281dd46'), "FOO%3Cscript%3E%3C%21--%3Cscript%3E-%3E%3C/script%3E--%3E%3C/script%3EQUX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22FOO%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E-%3E%3C/script%3E--%3E%22%0A%7C%20%20%20%20%20%22QUX%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_adoption01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_adoption01.html
new file mode 100644
index 0000000000..479208aa91
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_adoption01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_scripted_adoption01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['8970fe21b551a270aa74648bb2e8b905edb54522',];
+ var tests = {
+ "8970fe21b551a270aa74648bb2e8b905edb54522":[async_test('html5lib_scripted_adoption01.html 8970fe21b551a270aa74648bb2e8b905edb54522'), "%3Cp%3E%3Cb%20id%3D%22A%22%3E%3Cscript%3Edocument.getElementById%28%22A%22%29.id%20%3D%20%22B%22%3C/script%3E%3C/p%3ETEXT%3C/b%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22B%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22document.getElementById%28%22A%22%29.id%20%3D%20%22B%22%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20id%3D%22A%22%0A%7C%20%20%20%20%20%20%20%22TEXT%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_ark.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_ark.html
new file mode 100644
index 0000000000..4542b7c7d0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_ark.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_scripted_ark.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['b9a7cd0310cab4fd4eb77aed9149b966918e7ca2',];
+ var tests = {
+ "b9a7cd0310cab4fd4eb77aed9149b966918e7ca2":[async_test('html5lib_scripted_ark.html b9a7cd0310cab4fd4eb77aed9149b966918e7ca2'), "%3Cp%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cscript%3Edocument.getElementsByTagName%28%22font%22%29%5B2%5D.setAttribute%28%22size%22%2C%20%225%22%29%3B%3C/script%3E%3Cfont%20size%3D4%3E%3Cp%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%225%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22document.getElementsByTagName%28%22font%22%29%5B2%5D.setAttribute%28%22size%22%2C%20%225%22%29%3B%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_webkit01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_webkit01.html
new file mode 100644
index 0000000000..ef4f16d9ca
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_scripted_webkit01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_scripted_webkit01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['3ff6ec1125852c7933bf6d89ecb375354e6e1b40','46ae362de712eb9c55916de93110299dbbcb5726',];
+ var tests = {
+ "3ff6ec1125852c7933bf6d89ecb375354e6e1b40":[async_test('html5lib_scripted_webkit01.html 3ff6ec1125852c7933bf6d89ecb375354e6e1b40'), "1%3Cscript%3Edocument.write%28%222%22%29%3C/script%3E3", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22document.write%28%222%22%29%22%0A%7C%20%20%20%20%20%2223%22"],"46ae362de712eb9c55916de93110299dbbcb5726":[async_test('html5lib_scripted_webkit01.html 46ae362de712eb9c55916de93110299dbbcb5726'), "1%3Cscript%3Edocument.write%28%22%3Cscript%3Edocument.write%28%272%27%29%3C/scr%22%2B%20%22ipt%3E%3Cscript%3Edocument.write%28%273%27%29%3C/scr%22%20%2B%20%22ipt%3E%22%29%3C/script%3E4", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22document.write%28%22%3Cscript%3Edocument.write%28%272%27%29%3C/scr%22%2B%20%22ipt%3E%3Cscript%3Edocument.write%28%273%27%29%3C/scr%22%20%2B%20%22ipt%3E%22%29%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22document.write%28%272%27%29%22%0A%7C%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22document.write%28%273%27%29%22%0A%7C%20%20%20%20%20%2234%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_search-element.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_search-element.html
new file mode 100644
index 0000000000..b3d9e3caac
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_search-element.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_search-element.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['114b9f3c8147c0ed8ef3ed1811a9da3f10d74402','2204afb9037ec886f428ab5dcead5ee9f87c65cb','5153f797fbb63a23a40d19e298aca06d53d22f7f',];
+ var tests = {
+ "114b9f3c8147c0ed8ef3ed1811a9da3f10d74402":[async_test('html5lib_search-element.html 114b9f3c8147c0ed8ef3ed1811a9da3f10d74402'), "%3C%21doctype%20html%3E%3Cp%3Efoo%3Csearch%3Ebar%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Csearch%3E%0A%7C%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22baz%22"],"2204afb9037ec886f428ab5dcead5ee9f87c65cb":[async_test('html5lib_search-element.html 2204afb9037ec886f428ab5dcead5ee9f87c65cb'), "%3C%21doctype%20html%3E%3Csearch%3E%3Cp%3Efoo%3C/search%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csearch%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"5153f797fbb63a23a40d19e298aca06d53d22f7f":[async_test('html5lib_search-element.html 5153f797fbb63a23a40d19e298aca06d53d22f7f'), "%3C%21DOCTYPE%20html%3Exxx%3Csvg%3E%3Cx%3E%3Cg%3E%3Ca%3E%3Csearch%3E%3Cb%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22xxx%22%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20x%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20a%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20search%3E%0A%7C%20%20%20%20%20%3Cb%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tables01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tables01.html
new file mode 100644
index 0000000000..fb922f8e68
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tables01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tables01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['86a267778d1960b41f887b7bd2cd3ebf691d2e42','b6c1142484570bb90c36e454ee193cca17bb618a','7c507b825650f9721ea9656b1e844752a2424271','d10316cac9d03820ecfbc85ab373632e12d70c75','331a8c15a2f1dd3a9a1c31f5c65b99d356a65f30','6cdb83f12cb37a56f5ebad018ec3b07c2ad5b89f','9d01d4a7d7519e410e10493c8b108298b6733c31','b4615709ab72b6aa6cbf836582bd40fd93c0deb5','24132334bffa9eea4c8e29c940a8201f4253baf5','103784c31b8e3280b19e30f3b26f467585fbc304','0787e31440ae020f6f6e6207c364e9a2daec1c6f','1c40461886d4fdfca6ac5ef1d766b6fbf360ff9c','aea3c063f853938a36184825893e0820b700e241','6595dc192b10c479a543dbe25e67e6e0b6923ea8','9fcd09d757401684bcc28eaec005ed82718e836f','4be18eaaf0aa53f224b4ff8aeaf47d44ba854260','8e743c4f0ab5783b8973533640f669c92acb3797','201737ac5662941cdf6ece5d6721ea8b0647f0ce','9e7ce39b2b0c14d2cad5039047947000b0310f36',];
+ var tests = {
+ "86a267778d1960b41f887b7bd2cd3ebf691d2e42":[async_test('html5lib_tables01.html 86a267778d1960b41f887b7bd2cd3ebf691d2e42'), "%3Ctable%3E%3Cth%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cth%3E"],"b6c1142484570bb90c36e454ee193cca17bb618a":[async_test('html5lib_tables01.html b6c1142484570bb90c36e454ee193cca17bb618a'), "%3Ctable%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"7c507b825650f9721ea9656b1e844752a2424271":[async_test('html5lib_tables01.html 7c507b825650f9721ea9656b1e844752a2424271'), "%3Ctable%3E%3Ccol%20foo%3D%27bar%27%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20foo%3D%22bar%22"],"d10316cac9d03820ecfbc85ab373632e12d70c75":[async_test('html5lib_tables01.html d10316cac9d03820ecfbc85ab373632e12d70c75'), "%3Ctable%3E%3Ccolgroup%3E%3C/html%3Efoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E"],"331a8c15a2f1dd3a9a1c31f5c65b99d356a65f30":[async_test('html5lib_tables01.html 331a8c15a2f1dd3a9a1c31f5c65b99d356a65f30'), "%3Ctable%3E%3C/table%3E%3Cp%3Efoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"6cdb83f12cb37a56f5ebad018ec3b07c2ad5b89f":[async_test('html5lib_tables01.html 6cdb83f12cb37a56f5ebad018ec3b07c2ad5b89f'), "%3Ctable%3E%3C/body%3E%3C/caption%3E%3C/col%3E%3C/colgroup%3E%3C/html%3E%3C/tbody%3E%3C/td%3E%3C/tfoot%3E%3C/th%3E%3C/thead%3E%3C/tr%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"9d01d4a7d7519e410e10493c8b108298b6733c31":[async_test('html5lib_tables01.html 9d01d4a7d7519e410e10493c8b108298b6733c31'), "%3Ctable%3E%3Cselect%3E%3Coption%3E3%3C/select%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"b4615709ab72b6aa6cbf836582bd40fd93c0deb5":[async_test('html5lib_tables01.html b4615709ab72b6aa6cbf836582bd40fd93c0deb5'), "%3Ctable%3E%3Cselect%3E%3Ctable%3E%3C/table%3E%3C/select%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"24132334bffa9eea4c8e29c940a8201f4253baf5":[async_test('html5lib_tables01.html 24132334bffa9eea4c8e29c940a8201f4253baf5'), "%3Ctable%3E%3Cselect%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"103784c31b8e3280b19e30f3b26f467585fbc304":[async_test('html5lib_tables01.html 103784c31b8e3280b19e30f3b26f467585fbc304'), "%3Ctable%3E%3Cselect%3E%3Coption%3EA%3Ctr%3E%3Ctd%3EB%3C/td%3E%3C/tr%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22B%22"],"0787e31440ae020f6f6e6207c364e9a2daec1c6f":[async_test('html5lib_tables01.html 0787e31440ae020f6f6e6207c364e9a2daec1c6f'), "%3Ctable%3E%3Ctd%3E%3C/body%3E%3C/caption%3E%3C/col%3E%3C/colgroup%3E%3C/html%3Efoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22"],"1c40461886d4fdfca6ac5ef1d766b6fbf360ff9c":[async_test('html5lib_tables01.html 1c40461886d4fdfca6ac5ef1d766b6fbf360ff9c'), "%3Ctable%3E%3Ctd%3EA%3C/table%3EB", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%22B%22"],"aea3c063f853938a36184825893e0820b700e241":[async_test('html5lib_tables01.html aea3c063f853938a36184825893e0820b700e241'), "%3Ctable%3E%3Ctr%3E%3Ccaption%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E"],"6595dc192b10c479a543dbe25e67e6e0b6923ea8":[async_test('html5lib_tables01.html 6595dc192b10c479a543dbe25e67e6e0b6923ea8'), "%3Ctable%3E%3Ctr%3E%3C/body%3E%3C/caption%3E%3C/col%3E%3C/colgroup%3E%3C/html%3E%3C/td%3E%3C/th%3E%3Ctd%3Efoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22"],"9fcd09d757401684bcc28eaec005ed82718e836f":[async_test('html5lib_tables01.html 9fcd09d757401684bcc28eaec005ed82718e836f'), "%3Ctable%3E%3Ctd%3E%3Ctr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"4be18eaaf0aa53f224b4ff8aeaf47d44ba854260":[async_test('html5lib_tables01.html 4be18eaaf0aa53f224b4ff8aeaf47d44ba854260'), "%3Ctable%3E%3Ctd%3E%3Cbutton%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"8e743c4f0ab5783b8973533640f669c92acb3797":[async_test('html5lib_tables01.html 8e743c4f0ab5783b8973533640f669c92acb3797'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3Cdesc%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"201737ac5662941cdf6ece5d6721ea8b0647f0ce":[async_test('html5lib_tables01.html 201737ac5662941cdf6ece5d6721ea8b0647f0ce'), "%3Cdiv%3E%3Ctable%3E%3Csvg%3E%3CforeignObject%3E%3Cselect%3E%3Ctable%3E%3Cs%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cs%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],"9e7ce39b2b0c14d2cad5039047947000b0310f36":[async_test('html5lib_tables01.html 9e7ce39b2b0c14d2cad5039047947000b0310f36'), "%3Ctable%3Ea%3C%21doctype%20html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%3Ctable%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_template.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_template.html
new file mode 100644
index 0000000000..90e0779fdb
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_template.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_template.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['010950d55f4eccf16e9c4af1d263bb747294c646','a838bd54410cef059a42eea9606356488e16535b','27fb9111f6675a7e033b867480c0afddcda161a6','aee883a65775489399a003b2371d58248a6aff6f','89b17b54ab343191bf74ef5434f4d2cfac40ea97','c4433556c7414cfd71f27b420f1ffc4348774f5e','3dcce7d97108b3e9ea7fa96f240ac62bf280e74b','a1f587f7ea85ccfe294bd45bfb501e850cb979e0','cd26a7832f13bdc135697321ca6c2fecdca6ef5d','e30571d90b0e56864499961eb7be955994cf72e2','01cbe9f6a25f286b08d8dc4f7b65421e8eb3500c','96cbbcdffe02c86a8b929604c2fd5f3571a18dbe','d51676f55550e960dd0f5fa7fd0bdfa20bdde046','f9dfd9acfd494489c899604649a01d864741f50f','ea00361c265d3ffb47ce636d919c94ca10d58911','d8ebfcf7694c9d04457e796ac73049210313602e','b4d5e6fe9b92e2c8f54199d7cab3da383c42add0','07724ef8f7a4fa61c77ffcd5180d3101c4781502','e90f8aae8fc690540b42b3ffa3e741e7c1dfbf43','687bdf4adda88a316ec69fe20e84720acc5d1fe6','5b232642f472c2b4c0c7511fed464eebe686b427','dc1ac1830a881d1532a1e6fd6d0cfa56d6571da2','c58747a85e8b4f44d7ae63c04cdad783a903c25e','ca59bfdaec7451f704973176fab46e582bd691b2','cf807d6391a58c172b6c15c3b01d2a99ec0e6cf8','350b7ac850e46de79615308fc923649264406104','a31ff44edf7f377543dabdda8141cda9bb6de134','533c5c1b5f0d0cbb1ede2cc5ae927095c5b21f0b','61f79e083005007853c4f8e431559ac8d3845cfd','e802e85f36792b176b73c102c0e8761d9478621d','51d0797ff7653cd7be34458d689146e08a666c7f','d60e4079a18bd6266740cc61d1ca736e9d5098ce','308709292677b4d74c108a811ad7b7acd0bdfc9c','8965cdf9c4e9936262e25c90c7a7f8673840a445','7dccda789764beb489e09be10188af9347335d05','e15be51c77e1a6de35568a099ed339440ce9426d','503d3782e45940c19f096f360a092282b46ab1ea','b4ab56fd9e9cebf479d14adfa523c06d16483a5e','cd8bc9521f9683086a9e8529dd97314a6869daeb','f915e7b3407c24b28c3aad318e5693cc774020f4','3c5eb261787b3d15aff86fa61de773fd7e439b0e','2b57775750c198d4b98b23aed74ff80a866a01f5','dc3d016610f3ab532525a6c2871f03d6b62b0168','6a184d71d00580a26a8b6bd97aafe5503339f3f6','ce570a6c4bcee8b72a03e25508c6dd72e3cc6c35','e0c3d922f7b1f1654f02f716c3d9b31198ce3385','87e67242bf6debcf3b7dca852d10aa0f7b625b28','35ac4d4c972a01d368ed0cacb41370efef0a644d','5226c39dfc2d624ad4191b4eacb7e40c7ae528eb','aa90cd4db6b12e0a47341914a90cc536eec32d64','48af1faf5fcf48a0854af5a5c33656d9ccf6736b','ed3a029ba5e7f59969d65a4fc490a8f13b098cb9','6c8880d54475ad9574e203dcf2e55820b123cc64','275060925a844cb51b29bae660301de9780d68c8','9f82f6ec4c0a48c1d4dfbe6803b94abd553aea88','f094bf7e94a88b86c80a0643e70c8e5ff3354698','35a07ec3b4bf26ea407dc1ddf52f14195a714059','24faa53b271f994a4ff31d5796c8ff47d6f2c3e6','0f1c491b58c2dd3c402a62e37f833bc1f1db8d21','868d918a7b5d8b5c065c15229492bc2022bfbcba','0538efa44e857596c556033a3821d424378aea3f','e7d7bf3973c70d3cf9b0adad2ebed9f25be48d66','c69d0ac542d477b7312bb24981127b8aa8fdb1df','b496a8c13a7bd75b778bb0de489726aee952ae0c','5d6ee61de40274c9626ca78ee208d51276d5662d','9bd9687a65f258adc24450fc5cbd781fff6c038a','db1baeb846d718c773324746524fbd68f2e9436e','4b0ce46c611dbcc016db272ef007f302bee0c897','1a735e1c7f28f8701f3c7fd5e9404b8911916086','0686eedec06b2db1dc283fac92c1ef1a33114c71','d4dfb87ce626f12923056a6cd77448eaf4660ac2','1f295920f2937b2c8023b3761c43a0d4d9e5353c','3b91fa08fad923d387d924cff37fbf6b4c3a5712','45a1c1ad5d99ad67c573096a79253996a664e01b','0fe3a66773c6048c8f6f2c92f2611f65be972ec1','be40897ca411e1507197c31ab2a9f9752a05f769','dcfb1048ed5c40e406b4fbf0cde24c826713907f','78263aeea68ac97903598682013bae9c0c21d547','5aa177ef1a35bf4502dcb867d8e666288982ba99','5d303375907dc4d4380b477e0317c17b660613e9','d822f726927c34b92fe102b13e63920850878f6a','07acdcaeb4fa639296d46673cf28823ddf2a6ca7','58bd846ce1be0caf7560fba2ef19e2c2070ab123','8eeee377e5ab324731cc592f1fa8abe1045ad610','b30690019090149132fc228a7261c5cf2fd149fc','67a209d928804f90fdb66d070201b23f3d0c8a42','12104886b8f87daa937eac30b5ff0e1e074eaa6f','483cc9957a7225fe435112642be59abb4c459a1e','72d8ac431a154c40ab75d53a258d9d80d47689eb','1125967cbbcd404f4cb14d48270b8ec778970d77','32c963e164b9ec82c60e490bb141c1ccc70b992f','574a95fc9c9f2de3aeaa0c9ee1e6967fc3d4770d','332863a7f9e61bff32bd3427ede7a088b790d453','2121db07146781773df9e53b94fa921a805175ce','8675de267cd7e34f2febdee3feb665614d1562fe','c5d26ad923a2b1e988ddd378ca4fb26eb48353e1','eec1542e2fa0e9eafb7f8d4a51eae56b5a31b3c8','b79387a54c3b136db0f28ed96555ff683b3947fe','c477a29a4deb32d072a415fa809a84a4f2beee0c','26e4480c08e1f5f7b6ac8b8c1832ab0312e3b7c5','24b3b50fdd0bf8d5cf2ebaa6bf502d7bcfde1da4','d3704c68528357189eb5826ab66eea071d6137a5','d958f7d44faf772d1fb60f1a8f186f837ca735d9','3fc4d97fa68fc2658356bdbd4e051c867de8de53','94820107bbf3fab3f82de1f717e8413aead7d3a6','ed920bca1fe1f5ad471bbd81adf8a41f3e2d9b06','657c00ebdda37ae060cc69633ed98482ccc29e18','649fc955a4b60ab2a5b881d94c9493eb4a545002','977041956eb9c7b9db73935168aba92f77c079f6','fafee395fea124791df59bafeb1136342b64d3c6','d5a8beecf5d3c53e947772ad887808d132334aa1',];
+ var tests = {
+ "010950d55f4eccf16e9c4af1d263bb747294c646":[async_test('html5lib_template.html 010950d55f4eccf16e9c4af1d263bb747294c646'), "%3Cbody%3E%3Ctemplate%3EHello%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%22Hello%22"],"a838bd54410cef059a42eea9606356488e16535b":[async_test('html5lib_template.html a838bd54410cef059a42eea9606356488e16535b'), "%3Ctemplate%3EHello%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%22Hello%22%0A%7C%20%20%20%3Cbody%3E"],"27fb9111f6675a7e033b867480c0afddcda161a6":[async_test('html5lib_template.html 27fb9111f6675a7e033b867480c0afddcda161a6'), "%3Ctemplate%3E%3C/template%3E%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"aee883a65775489399a003b2371d58248a6aff6f":[async_test('html5lib_template.html aee883a65775489399a003b2371d58248a6aff6f'), "%3Chtml%3E%3Ctemplate%3EHello%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%22Hello%22%0A%7C%20%20%20%3Cbody%3E"],"89b17b54ab343191bf74ef5434f4d2cfac40ea97":[async_test('html5lib_template.html 89b17b54ab343191bf74ef5434f4d2cfac40ea97'), "%3Chead%3E%3Ctemplate%3E%3Cdiv%3E%3C/div%3E%3C/template%3E%3C/head%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%3Cbody%3E"],"c4433556c7414cfd71f27b420f1ffc4348774f5e":[async_test('html5lib_template.html c4433556c7414cfd71f27b420f1ffc4348774f5e'), "%3Cdiv%3E%3Ctemplate%3E%3Cdiv%3E%3Cspan%3E%3C/template%3E%3Cb%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E"],"3dcce7d97108b3e9ea7fa96f240ac62bf280e74b":[async_test('html5lib_template.html 3dcce7d97108b3e9ea7fa96f240ac62bf280e74b'), "%3Cdiv%3E%3Ctemplate%3E%3C/div%3EHello", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Hello%22"],"a1f587f7ea85ccfe294bd45bfb501e850cb979e0":[async_test('html5lib_template.html a1f587f7ea85ccfe294bd45bfb501e850cb979e0'), "%3Cdiv%3E%3C/template%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"cd26a7832f13bdc135697321ca6c2fecdca6ef5d":[async_test('html5lib_template.html cd26a7832f13bdc135697321ca6c2fecdca6ef5d'), "%3Ctable%3E%3Ctemplate%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content"],"e30571d90b0e56864499961eb7be955994cf72e2":[async_test('html5lib_template.html e30571d90b0e56864499961eb7be955994cf72e2'), "%3Ctable%3E%3Ctemplate%3E%3C/template%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content"],"01cbe9f6a25f286b08d8dc4f7b65421e8eb3500c":[async_test('html5lib_template.html 01cbe9f6a25f286b08d8dc4f7b65421e8eb3500c'), "%3Ctable%3E%3Cdiv%3E%3Ctemplate%3E%3C/template%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%3Ctable%3E"],"96cbbcdffe02c86a8b929604c2fd5f3571a18dbe":[async_test('html5lib_template.html 96cbbcdffe02c86a8b929604c2fd5f3571a18dbe'), "%3Ctable%3E%3Ctemplate%3E%3C/template%3E%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content"],"d51676f55550e960dd0f5fa7fd0bdfa20bdde046":[async_test('html5lib_template.html d51676f55550e960dd0f5fa7fd0bdfa20bdde046'), "%3Ctable%3E%20%20%20%3Ctemplate%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%22%20%20%20%22%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content"],"f9dfd9acfd494489c899604649a01d864741f50f":[async_test('html5lib_template.html f9dfd9acfd494489c899604649a01d864741f50f'), "%3Ctable%3E%3Ctbody%3E%3Ctemplate%3E%3C/template%3E%3C/tbody%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content"],"ea00361c265d3ffb47ce636d919c94ca10d58911":[async_test('html5lib_template.html ea00361c265d3ffb47ce636d919c94ca10d58911'), "%3Ctable%3E%3Ctbody%3E%3Ctemplate%3E%3C/tbody%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content"],"d8ebfcf7694c9d04457e796ac73049210313602e":[async_test('html5lib_template.html d8ebfcf7694c9d04457e796ac73049210313602e'), "%3Ctable%3E%3Ctbody%3E%3Ctemplate%3E%3C/template%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content"],"b4d5e6fe9b92e2c8f54199d7cab3da383c42add0":[async_test('html5lib_template.html b4d5e6fe9b92e2c8f54199d7cab3da383c42add0'), "%3Ctable%3E%3Cthead%3E%3Ctemplate%3E%3C/template%3E%3C/thead%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content"],"07724ef8f7a4fa61c77ffcd5180d3101c4781502":[async_test('html5lib_template.html 07724ef8f7a4fa61c77ffcd5180d3101c4781502'), "%3Ctable%3E%3Ctfoot%3E%3Ctemplate%3E%3C/template%3E%3C/tfoot%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctfoot%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content"],"e90f8aae8fc690540b42b3ffa3e741e7c1dfbf43":[async_test('html5lib_template.html e90f8aae8fc690540b42b3ffa3e741e7c1dfbf43'), "%3Cselect%3E%3Ctemplate%3E%3C/template%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content"],"687bdf4adda88a316ec69fe20e84720acc5d1fe6":[async_test('html5lib_template.html 687bdf4adda88a316ec69fe20e84720acc5d1fe6'), "%3Cselect%3E%3Ctemplate%3E%3Coption%3E%3C/option%3E%3C/template%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Coption%3E"],"5b232642f472c2b4c0c7511fed464eebe686b427":[async_test('html5lib_template.html 5b232642f472c2b4c0c7511fed464eebe686b427'), "%3Ctemplate%3E%3Coption%3E%3C/option%3E%3C/select%3E%3Coption%3E%3C/option%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%3Cbody%3E"],"dc1ac1830a881d1532a1e6fd6d0cfa56d6571da2":[async_test('html5lib_template.html dc1ac1830a881d1532a1e6fd6d0cfa56d6571da2'), "%3Cselect%3E%3Ctemplate%3E%3C/template%3E%3Coption%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%3Coption%3E"],"c58747a85e8b4f44d7ae63c04cdad783a903c25e":[async_test('html5lib_template.html c58747a85e8b4f44d7ae63c04cdad783a903c25e'), "%3Cselect%3E%3Coption%3E%3Ctemplate%3E%3C/template%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content"],"ca59bfdaec7451f704973176fab46e582bd691b2":[async_test('html5lib_template.html ca59bfdaec7451f704973176fab46e582bd691b2'), "%3Cselect%3E%3Ctemplate%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content"],"cf807d6391a58c172b6c15c3b01d2a99ec0e6cf8":[async_test('html5lib_template.html cf807d6391a58c172b6c15c3b01d2a99ec0e6cf8'), "%3Cselect%3E%3Coption%3E%3C/option%3E%3Ctemplate%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content"],"350b7ac850e46de79615308fc923649264406104":[async_test('html5lib_template.html 350b7ac850e46de79615308fc923649264406104'), "%3Cselect%3E%3Coption%3E%3C/option%3E%3Ctemplate%3E%3Coption%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Coption%3E"],"a31ff44edf7f377543dabdda8141cda9bb6de134":[async_test('html5lib_template.html a31ff44edf7f377543dabdda8141cda9bb6de134'), "%3Ctable%3E%3Cthead%3E%3Ctemplate%3E%3Ctd%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"533c5c1b5f0d0cbb1ede2cc5ae927095c5b21f0b":[async_test('html5lib_template.html 533c5c1b5f0d0cbb1ede2cc5ae927095c5b21f0b'), "%3Ctable%3E%3Ctemplate%3E%3Cthead%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cthead%3E"],"61f79e083005007853c4f8e431559ac8d3845cfd":[async_test('html5lib_template.html 61f79e083005007853c4f8e431559ac8d3845cfd'), "%3Cbody%3E%3Ctable%3E%3Ctemplate%3E%3Ctd%3E%3C/tr%3E%3Cdiv%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"e802e85f36792b176b73c102c0e8761d9478621d":[async_test('html5lib_template.html e802e85f36792b176b73c102c0e8761d9478621d'), "%3Ctable%3E%3Ctemplate%3E%3Cthead%3E%3C/template%3E%3C/thead%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cthead%3E"],"51d0797ff7653cd7be34458d689146e08a666c7f":[async_test('html5lib_template.html 51d0797ff7653cd7be34458d689146e08a666c7f'), "%3Ctable%3E%3Cthead%3E%3Ctemplate%3E%3Ctr%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"d60e4079a18bd6266740cc61d1ca736e9d5098ce":[async_test('html5lib_template.html d60e4079a18bd6266740cc61d1ca736e9d5098ce'), "%3Ctable%3E%3Ctemplate%3E%3Ctr%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"308709292677b4d74c108a811ad7b7acd0bdfc9c":[async_test('html5lib_template.html 308709292677b4d74c108a811ad7b7acd0bdfc9c'), "%3Ctable%3E%3Ctr%3E%3Ctemplate%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"8965cdf9c4e9936262e25c90c7a7f8673840a445":[async_test('html5lib_template.html 8965cdf9c4e9936262e25c90c7a7f8673840a445'), "%3Ctable%3E%3Ctemplate%3E%3Ctr%3E%3Ctemplate%3E%3Ctd%3E%3C/template%3E%3C/tr%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"7dccda789764beb489e09be10188af9347335d05":[async_test('html5lib_template.html 7dccda789764beb489e09be10188af9347335d05'), "%3Ctable%3E%3Ctemplate%3E%3Ctr%3E%3Ctemplate%3E%3Ctd%3E%3C/td%3E%3C/template%3E%3C/tr%3E%3C/template%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"e15be51c77e1a6de35568a099ed339440ce9426d":[async_test('html5lib_template.html e15be51c77e1a6de35568a099ed339440ce9426d'), "%3Ctable%3E%3Ctemplate%3E%3Ctd%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"503d3782e45940c19f096f360a092282b46ab1ea":[async_test('html5lib_template.html 503d3782e45940c19f096f360a092282b46ab1ea'), "%3Cbody%3E%3Ctemplate%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"b4ab56fd9e9cebf479d14adfa523c06d16483a5e":[async_test('html5lib_template.html b4ab56fd9e9cebf479d14adfa523c06d16483a5e'), "%3Cbody%3E%3Ctemplate%3E%3Ctemplate%3E%3Ctr%3E%3C/tr%3E%3C/template%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"cd8bc9521f9683086a9e8529dd97314a6869daeb":[async_test('html5lib_template.html cd8bc9521f9683086a9e8529dd97314a6869daeb'), "%3Ctable%3E%3Ccolgroup%3E%3Ctemplate%3E%3Ccol%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"f915e7b3407c24b28c3aad318e5693cc774020f4":[async_test('html5lib_template.html f915e7b3407c24b28c3aad318e5693cc774020f4'), "%3Cframeset%3E%3Ctemplate%3E%3Cframe%3E%3C/frame%3E%3C/template%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E"],"3c5eb261787b3d15aff86fa61de773fd7e439b0e":[async_test('html5lib_template.html 3c5eb261787b3d15aff86fa61de773fd7e439b0e'), "%3Ctemplate%3E%3Cframe%3E%3C/frame%3E%3C/frameset%3E%3Cframe%3E%3C/frame%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%3Cbody%3E"],"2b57775750c198d4b98b23aed74ff80a866a01f5":[async_test('html5lib_template.html 2b57775750c198d4b98b23aed74ff80a866a01f5'), "%3Ctemplate%3E%3Cdiv%3E%3Cframeset%3E%3Cspan%3E%3C/span%3E%3C/div%3E%3Cspan%3E%3C/span%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%3Cbody%3E"],"dc3d016610f3ab532525a6c2871f03d6b62b0168":[async_test('html5lib_template.html dc3d016610f3ab532525a6c2871f03d6b62b0168'), "%3Cbody%3E%3Ctemplate%3E%3Cdiv%3E%3Cframeset%3E%3Cspan%3E%3C/span%3E%3C/div%3E%3Cspan%3E%3C/span%3E%3C/template%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E"],"6a184d71d00580a26a8b6bd97aafe5503339f3f6":[async_test('html5lib_template.html 6a184d71d00580a26a8b6bd97aafe5503339f3f6'), "%3Cbody%3E%3Ctemplate%3E%3Cscript%3Evar%20i%20%3D%201%3B%3C/script%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22var%20i%20%3D%201%3B%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"ce570a6c4bcee8b72a03e25508c6dd72e3cc6c35":[async_test('html5lib_template.html ce570a6c4bcee8b72a03e25508c6dd72e3cc6c35'), "%3Cbody%3E%3Ctemplate%3E%3Ctr%3E%3Cdiv%3E%3C/div%3E%3C/tr%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"e0c3d922f7b1f1654f02f716c3d9b31198ce3385":[async_test('html5lib_template.html e0c3d922f7b1f1654f02f716c3d9b31198ce3385'), "%3Cbody%3E%3Ctemplate%3E%3Ctr%3E%3C/tr%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"87e67242bf6debcf3b7dca852d10aa0f7b625b28":[async_test('html5lib_template.html 87e67242bf6debcf3b7dca852d10aa0f7b625b28'), "%3Cbody%3E%3Ctemplate%3E%3Ctd%3E%3C/td%3E%3C/tr%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"35ac4d4c972a01d368ed0cacb41370efef0a644d":[async_test('html5lib_template.html 35ac4d4c972a01d368ed0cacb41370efef0a644d'), "%3Cbody%3E%3Ctemplate%3E%3Ctd%3E%3C/td%3E%3Ctbody%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"5226c39dfc2d624ad4191b4eacb7e40c7ae528eb":[async_test('html5lib_template.html 5226c39dfc2d624ad4191b4eacb7e40c7ae528eb'), "%3Cbody%3E%3Ctemplate%3E%3Ctd%3E%3C/td%3E%3Ccaption%3E%3C/caption%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"aa90cd4db6b12e0a47341914a90cc536eec32d64":[async_test('html5lib_template.html aa90cd4db6b12e0a47341914a90cc536eec32d64'), "%3Cbody%3E%3Ctemplate%3E%3Ctd%3E%3C/td%3E%3Ccolgroup%3E%3C/caption%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"48af1faf5fcf48a0854af5a5c33656d9ccf6736b":[async_test('html5lib_template.html 48af1faf5fcf48a0854af5a5c33656d9ccf6736b'), "%3Cbody%3E%3Ctemplate%3E%3Ctd%3E%3C/td%3E%3C/table%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"ed3a029ba5e7f59969d65a4fc490a8f13b098cb9":[async_test('html5lib_template.html ed3a029ba5e7f59969d65a4fc490a8f13b098cb9'), "%3Cbody%3E%3Ctemplate%3E%3Ctr%3E%3C/tr%3E%3Ctbody%3E%3Ctr%3E%3C/tr%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"6c8880d54475ad9574e203dcf2e55820b123cc64":[async_test('html5lib_template.html 6c8880d54475ad9574e203dcf2e55820b123cc64'), "%3Cbody%3E%3Ctemplate%3E%3Ctr%3E%3C/tr%3E%3Ccaption%3E%3Ctr%3E%3C/tr%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"275060925a844cb51b29bae660301de9780d68c8":[async_test('html5lib_template.html 275060925a844cb51b29bae660301de9780d68c8'), "%3Cbody%3E%3Ctemplate%3E%3Ctr%3E%3C/tr%3E%3C/table%3E%3Ctr%3E%3C/tr%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"9f82f6ec4c0a48c1d4dfbe6803b94abd553aea88":[async_test('html5lib_template.html 9f82f6ec4c0a48c1d4dfbe6803b94abd553aea88'), "%3Cbody%3E%3Ctemplate%3E%3Cthead%3E%3C/thead%3E%3Ccaption%3E%3C/caption%3E%3Ctbody%3E%3C/tbody%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E"],"f094bf7e94a88b86c80a0643e70c8e5ff3354698":[async_test('html5lib_template.html f094bf7e94a88b86c80a0643e70c8e5ff3354698'), "%3Cbody%3E%3Ctemplate%3E%3Cthead%3E%3C/thead%3E%3C/table%3E%3Ctbody%3E%3C/tbody%3E%3C/template%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E"],"35a07ec3b4bf26ea407dc1ddf52f14195a714059":[async_test('html5lib_template.html 35a07ec3b4bf26ea407dc1ddf52f14195a714059'), "%3Cbody%3E%3Ctemplate%3E%3Cdiv%3E%3Ctr%3E%3C/tr%3E%3C/div%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"24faa53b271f994a4ff31d5796c8ff47d6f2c3e6":[async_test('html5lib_template.html 24faa53b271f994a4ff31d5796c8ff47d6f2c3e6'), "%3Cbody%3E%3Ctemplate%3E%3Cem%3EHello%3C/em%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Hello%22"],"0f1c491b58c2dd3c402a62e37f833bc1f1db8d21":[async_test('html5lib_template.html 0f1c491b58c2dd3c402a62e37f833bc1f1db8d21'), "%3Cbody%3E%3Ctemplate%3E%3C%21--comment--%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3C%21--%20comment%20--%3E"],"868d918a7b5d8b5c065c15229492bc2022bfbcba":[async_test('html5lib_template.html 868d918a7b5d8b5c065c15229492bc2022bfbcba'), "%3Cbody%3E%3Ctemplate%3E%3Cstyle%3E%3C/style%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"0538efa44e857596c556033a3821d424378aea3f":[async_test('html5lib_template.html 0538efa44e857596c556033a3821d424378aea3f'), "%3Cbody%3E%3Ctemplate%3E%3Cmeta%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"e7d7bf3973c70d3cf9b0adad2ebed9f25be48d66":[async_test('html5lib_template.html e7d7bf3973c70d3cf9b0adad2ebed9f25be48d66'), "%3Cbody%3E%3Ctemplate%3E%3Clink%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Clink%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"c69d0ac542d477b7312bb24981127b8aa8fdb1df":[async_test('html5lib_template.html c69d0ac542d477b7312bb24981127b8aa8fdb1df'), "%3Cbody%3E%3Ctable%3E%3Ccolgroup%3E%3Ctemplate%3E%3Ccol%3E%3C/col%3E%3C/template%3E%3C/colgroup%3E%3C/table%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"b496a8c13a7bd75b778bb0de489726aee952ae0c":[async_test('html5lib_template.html b496a8c13a7bd75b778bb0de489726aee952ae0c'), "%3Cbody%20a%3Db%3E%3Ctemplate%3E%3Cdiv%3E%3C/div%3E%3Cbody%20c%3Dd%3E%3Cdiv%3E%3C/div%3E%3C/body%3E%3C/template%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20a%3D%22b%22%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"5d6ee61de40274c9626ca78ee208d51276d5662d":[async_test('html5lib_template.html 5d6ee61de40274c9626ca78ee208d51276d5662d'), "%3Chtml%20a%3Db%3E%3Ctemplate%3E%3Cdiv%3E%3Chtml%20b%3Dc%3E%3Cspan%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20a%3D%22b%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%3Cbody%3E"],"9bd9687a65f258adc24450fc5cbd781fff6c038a":[async_test('html5lib_template.html 9bd9687a65f258adc24450fc5cbd781fff6c038a'), "%3Chtml%20a%3Db%3E%3Ctemplate%3E%3Ccol%3E%3C/col%3E%3Chtml%20b%3Dc%3E%3Ccol%3E%3C/col%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20a%3D%22b%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%3Cbody%3E"],"db1baeb846d718c773324746524fbd68f2e9436e":[async_test('html5lib_template.html db1baeb846d718c773324746524fbd68f2e9436e'), "%3Chtml%20a%3Db%3E%3Ctemplate%3E%3Cframe%3E%3C/frame%3E%3Chtml%20b%3Dc%3E%3Cframe%3E%3C/frame%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20a%3D%22b%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%3Cbody%3E"],"4b0ce46c611dbcc016db272ef007f302bee0c897":[async_test('html5lib_template.html 4b0ce46c611dbcc016db272ef007f302bee0c897'), "%3Cbody%3E%3Ctemplate%3E%3Ctr%3E%3C/tr%3E%3Ctemplate%3E%3C/template%3E%3Ctd%3E%3C/td%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"1a735e1c7f28f8701f3c7fd5e9404b8911916086":[async_test('html5lib_template.html 1a735e1c7f28f8701f3c7fd5e9404b8911916086'), "%3Cbody%3E%3Ctemplate%3E%3Cthead%3E%3C/thead%3E%3Ctemplate%3E%3Ctr%3E%3C/tr%3E%3C/template%3E%3Ctr%3E%3C/tr%3E%3Ctfoot%3E%3C/tfoot%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctfoot%3E"],"0686eedec06b2db1dc283fac92c1ef1a33114c71":[async_test('html5lib_template.html 0686eedec06b2db1dc283fac92c1ef1a33114c71'), "%3Cbody%3E%3Ctemplate%3E%3Ctemplate%3E%3Cb%3E%3Ctemplate%3E%3C/template%3E%3C/template%3Etext%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%22text%22"],"d4dfb87ce626f12923056a6cd77448eaf4660ac2":[async_test('html5lib_template.html d4dfb87ce626f12923056a6cd77448eaf4660ac2'), "%3Cbody%3E%3Ctemplate%3E%3Ccol%3E%3Ccolgroup%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"1f295920f2937b2c8023b3761c43a0d4d9e5353c":[async_test('html5lib_template.html 1f295920f2937b2c8023b3761c43a0d4d9e5353c'), "%3Cbody%3E%3Ctemplate%3E%3Ccol%3E%3C/colgroup%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"3b91fa08fad923d387d924cff37fbf6b4c3a5712":[async_test('html5lib_template.html 3b91fa08fad923d387d924cff37fbf6b4c3a5712'), "%3Cbody%3E%3Ctemplate%3E%3Ccol%3E%3Ccolgroup%3E%3C/template%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"45a1c1ad5d99ad67c573096a79253996a664e01b":[async_test('html5lib_template.html 45a1c1ad5d99ad67c573096a79253996a664e01b'), "%3Cbody%3E%3Ctemplate%3E%3Ccol%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"0fe3a66773c6048c8f6f2c92f2611f65be972ec1":[async_test('html5lib_template.html 0fe3a66773c6048c8f6f2c92f2611f65be972ec1'), "%3Cbody%3E%3Ctemplate%3E%3Ccol%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"be40897ca411e1507197c31ab2a9f9752a05f769":[async_test('html5lib_template.html be40897ca411e1507197c31ab2a9f9752a05f769'), "%3Cbody%3E%3Ctemplate%3E%3Ccol%3EHello", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"dcfb1048ed5c40e406b4fbf0cde24c826713907f":[async_test('html5lib_template.html dcfb1048ed5c40e406b4fbf0cde24c826713907f'), "%3Cbody%3E%3Ctemplate%3E%3Ci%3E%3Cmenu%3EFoo%3C/i%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmenu%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22Foo%22"],"78263aeea68ac97903598682013bae9c0c21d547":[async_test('html5lib_template.html 78263aeea68ac97903598682013bae9c0c21d547'), "%3Cbody%3E%3Ctemplate%3E%3C/div%3E%3Cdiv%3EFoo%3C/div%3E%3Ctemplate%3E%3C/template%3E%3Ctr%3E%3C/tr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Foo%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content"],"5aa177ef1a35bf4502dcb867d8e666288982ba99":[async_test('html5lib_template.html 5aa177ef1a35bf4502dcb867d8e666288982ba99'), "%3Cbody%3E%3Cdiv%3E%3Ctemplate%3E%3C/div%3E%3Ctr%3E%3Ctd%3EFoo%3C/td%3E%3C/tr%3E%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Foo%22"],"5d303375907dc4d4380b477e0317c17b660613e9":[async_test('html5lib_template.html 5d303375907dc4d4380b477e0317c17b660613e9'), "%3Ctemplate%3E%3C/figcaption%3E%3Csub%3E%3Ctable%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Csub%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%3Cbody%3E"],"d822f726927c34b92fe102b13e63920850878f6a":[async_test('html5lib_template.html d822f726927c34b92fe102b13e63920850878f6a'), "%3Ctemplate%3E%3Ctemplate%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%3Cbody%3E"],"07acdcaeb4fa639296d46673cf28823ddf2a6ca7":[async_test('html5lib_template.html 07acdcaeb4fa639296d46673cf28823ddf2a6ca7'), "%3Ctemplate%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%3Cbody%3E"],"58bd846ce1be0caf7560fba2ef19e2c2070ab123":[async_test('html5lib_template.html 58bd846ce1be0caf7560fba2ef19e2c2070ab123'), "%3Ctemplate%3E%3Ctemplate%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%3Cbody%3E"],"8eeee377e5ab324731cc592f1fa8abe1045ad610":[async_test('html5lib_template.html 8eeee377e5ab324731cc592f1fa8abe1045ad610'), "%3Ctemplate%3E%3Ctemplate%3E%3Ctable%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%3Cbody%3E"],"b30690019090149132fc228a7261c5cf2fd149fc":[async_test('html5lib_template.html b30690019090149132fc228a7261c5cf2fd149fc'), "%3Ctemplate%3E%3Ctemplate%3E%3Ctbody%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%3Cbody%3E"],"67a209d928804f90fdb66d070201b23f3d0c8a42":[async_test('html5lib_template.html 67a209d928804f90fdb66d070201b23f3d0c8a42'), "%3Ctemplate%3E%3Ctemplate%3E%3Ctr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%3Cbody%3E"],"12104886b8f87daa937eac30b5ff0e1e074eaa6f":[async_test('html5lib_template.html 12104886b8f87daa937eac30b5ff0e1e074eaa6f'), "%3Ctemplate%3E%3Ctemplate%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%3Cbody%3E"],"483cc9957a7225fe435112642be59abb4c459a1e":[async_test('html5lib_template.html 483cc9957a7225fe435112642be59abb4c459a1e'), "%3Ctemplate%3E%3Ctemplate%3E%3Ccaption%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%3Cbody%3E"],"72d8ac431a154c40ab75d53a258d9d80d47689eb":[async_test('html5lib_template.html 72d8ac431a154c40ab75d53a258d9d80d47689eb'), "%3Ctemplate%3E%3Ctemplate%3E%3Ccolgroup%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%3Cbody%3E"],"1125967cbbcd404f4cb14d48270b8ec778970d77":[async_test('html5lib_template.html 1125967cbbcd404f4cb14d48270b8ec778970d77'), "%3Ctemplate%3E%3Ctemplate%3E%3Ccol%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%3Cbody%3E"],"32c963e164b9ec82c60e490bb141c1ccc70b992f":[async_test('html5lib_template.html 32c963e164b9ec82c60e490bb141c1ccc70b992f'), "%3Ctemplate%3E%3Ctemplate%3E%3Ctbody%3E%3Cselect%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%3Cbody%3E"],"574a95fc9c9f2de3aeaa0c9ee1e6967fc3d4770d":[async_test('html5lib_template.html 574a95fc9c9f2de3aeaa0c9ee1e6967fc3d4770d'), "%3Ctemplate%3E%3Ctemplate%3E%3Ctable%3EFoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22Foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%3Cbody%3E"],"332863a7f9e61bff32bd3427ede7a088b790d453":[async_test('html5lib_template.html 332863a7f9e61bff32bd3427ede7a088b790d453'), "%3Ctemplate%3E%3Ctemplate%3E%3Cframe%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%3Cbody%3E"],"2121db07146781773df9e53b94fa921a805175ce":[async_test('html5lib_template.html 2121db07146781773df9e53b94fa921a805175ce'), "%3Ctemplate%3E%3Ctemplate%3E%3Cscript%3Evar%20i", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22var%20i%22%0A%7C%20%20%20%3Cbody%3E"],"8675de267cd7e34f2febdee3feb665614d1562fe":[async_test('html5lib_template.html 8675de267cd7e34f2febdee3feb665614d1562fe'), "%3Ctemplate%3E%3Ctemplate%3E%3Cstyle%3Evar%20i", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22var%20i%22%0A%7C%20%20%20%3Cbody%3E"],"c5d26ad923a2b1e988ddd378ca4fb26eb48353e1":[async_test('html5lib_template.html c5d26ad923a2b1e988ddd378ca4fb26eb48353e1'), "%3Ctemplate%3E%3Ctable%3E%3C/template%3E%3Cbody%3E%3Cspan%3EFoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%22Foo%22"],"eec1542e2fa0e9eafb7f8d4a51eae56b5a31b3c8":[async_test('html5lib_template.html eec1542e2fa0e9eafb7f8d4a51eae56b5a31b3c8'), "%3Ctemplate%3E%3Ctd%3E%3C/template%3E%3Cbody%3E%3Cspan%3EFoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%22Foo%22"],"b79387a54c3b136db0f28ed96555ff683b3947fe":[async_test('html5lib_template.html b79387a54c3b136db0f28ed96555ff683b3947fe'), "%3Ctemplate%3E%3Cobject%3E%3C/template%3E%3Cbody%3E%3Cspan%3EFoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cobject%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%22Foo%22"],"c477a29a4deb32d072a415fa809a84a4f2beee0c":[async_test('html5lib_template.html c477a29a4deb32d072a415fa809a84a4f2beee0c'), "%3Ctemplate%3E%3Csvg%3E%3Ctemplate%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20template%3E%0A%7C%20%20%20%3Cbody%3E"],"26e4480c08e1f5f7b6ac8b8c1832ab0312e3b7c5":[async_test('html5lib_template.html 26e4480c08e1f5f7b6ac8b8c1832ab0312e3b7c5'), "%3Ctemplate%3E%3Csvg%3E%3Cfoo%3E%3Ctemplate%3E%3CforeignObject%3E%3Cdiv%3E%3C/template%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20template%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"24b3b50fdd0bf8d5cf2ebaa6bf502d7bcfde1da4":[async_test('html5lib_template.html 24b3b50fdd0bf8d5cf2ebaa6bf502d7bcfde1da4'), "%3Cdummy%3E%3Ctemplate%3E%3Cspan%3E%3C/dummy%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdummy%3E%0A%7C%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E"],"d3704c68528357189eb5826ab66eea071d6137a5":[async_test('html5lib_template.html d3704c68528357189eb5826ab66eea071d6137a5'), "%3Cbody%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Ctemplate%3EFoo%3C/template%3E%3Ccaption%3EA%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Foo%22%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%22A%22"],"d958f7d44faf772d1fb60f1a8f186f837ca735d9":[async_test('html5lib_template.html d958f7d44faf772d1fb60f1a8f186f837ca735d9'), "%3Cbody%3E%3C/body%3E%3Ctemplate%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content"],"3fc4d97fa68fc2658356bdbd4e051c867de8de53":[async_test('html5lib_template.html 3fc4d97fa68fc2658356bdbd4e051c867de8de53'), "%3Chead%3E%3C/head%3E%3Ctemplate%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%3Cbody%3E"],"94820107bbf3fab3f82de1f717e8413aead7d3a6":[async_test('html5lib_template.html 94820107bbf3fab3f82de1f717e8413aead7d3a6'), "%3Chead%3E%3C/head%3E%3Ctemplate%3EFoo%3C/template%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%22Foo%22%0A%7C%20%20%20%3Cbody%3E"],"ed920bca1fe1f5ad471bbd81adf8a41f3e2d9b06":[async_test('html5lib_template.html ed920bca1fe1f5ad471bbd81adf8a41f3e2d9b06'), "%3Chtml%3E%3Chead%3E%3C/head%3E%3Ctemplate%3E%3C/template%3E%3Chead%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%3Cbody%3E"],"657c00ebdda37ae060cc69633ed98482ccc29e18":[async_test('html5lib_template.html 657c00ebdda37ae060cc69633ed98482ccc29e18'), "%3C%21DOCTYPE%20HTML%3E%3Cdummy%3E%3Ctable%3E%3Ctemplate%3E%3Ctable%3E%3Ctemplate%3E%3Ctable%3E%3Cscript%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdummy%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cscript%3E"],"649fc955a4b60ab2a5b881d94c9493eb4a545002":[async_test('html5lib_template.html 649fc955a4b60ab2a5b881d94c9493eb4a545002'), "%3Ctemplate%3E%3Ca%3E%3Ctable%3E%3Ca%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%3Cbody%3E"],"977041956eb9c7b9db73935168aba92f77c079f6":[async_test('html5lib_template.html 977041956eb9c7b9db73935168aba92f77c079f6'), "%3C%21DOCTYPE%20HTML%3E%3Ctemplate%3E%3Ctr%3E%3Ctd%3Ecell%3C/td%3E%3C/tr%3E%3C/template%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22cell%22%0A%7C%20%20%20%3Cbody%3E"],"fafee395fea124791df59bafeb1136342b64d3c6":[async_test('html5lib_template.html fafee395fea124791df59bafeb1136342b64d3c6'), "%3C%21DOCTYPE%20HTML%3E%3Ctemplate%3E%20%3Ctr%3E%20%3Ctd%3Ecell%3C/td%3E%20%3C/tr%3E%20%3C/template%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22cell%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%3Cbody%3E"],"d5a8beecf5d3c53e947772ad887808d132334aa1":[async_test('html5lib_template.html d5a8beecf5d3c53e947772ad887808d132334aa1'), "%3C%21DOCTYPE%20HTML%3E%3Ctemplate%3E%3Ctr%3E%3Ctd%3Ecell%3C/td%3E%3C/tr%3Ea%3C/template%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22cell%22%0A%7C%20%20%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%3Cbody%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests1.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests1.html
new file mode 100644
index 0000000000..fa658fc768
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests1.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['4235382bf15f93f7dd1096832ae74cc71edef4d7','ad8515e9db0abd26469d0d2e46b42cebf606d4f3','2433aa5c088d78da9e7824e499f639177f56625d','c99d322c3502e38e9d18ac6c0180fa5462ce612e','d8473f7b5cec9d99526179f980ebf55a0beccbd3','ac7703fbb5c62cadb25024aed762c206c187a919','a00121213e2eb2c846a575f662e8c69389bfc44d','447f22e6a43ddbbc308afbc78b64b16452bc7bbb','cdac424e0f2fb979f21a64f50793d529375c01b3','63587d177231d2478a6ffd25f3c830fed7cc2efe','a3e13da13681f9f16c65334099136f3b5c235e6d','b7a06a5aa0c19d7914b853f5ed497743bb269e56','3d28b753b97f460868ca65ed8fc153021a815b8c','83cce5b1e1e49c92c618aabf1ed60926a8736456','b9e809bc4521004440bf558c7dc5d7dc1ae3dd40','60302916ab9a2128104dbf72629875ad19b5cb16','f4ad8e574fcac3bb08070eeb345855ea7081ea1d','d38fe13d87344a20bdf6e111988b5a09ed247913','e5f2e91cbff6a4bc56149b889f4f9396e455c5ad','18b58d1de184b6866963c902ff8451dd3522f540','88eb93691065e212a4323f55ec326585c7c44262','260db4c31fb80894beb825c4793e6142f178497d','59d9c5e2941952f7af99ed3e21d66e4fdb1df07c','1e788677da8eb26f560409392ff674744c6f8b64','240ee32b47e30bcf34483c7a7530cfeb99a6d1f1','e85dc012a49630206e6163cd1e0fd6c706865106','c440d1f0223d6b3ce85955d8246fdd47ecdfb034','9a2ce05a0b342b377ef054385131ad4d1b5ae84b','daa9d8440e2bc4d560536b52123b01e52aa81692','0b27e026dd03a356bbd78690fff7fb40cd63b606','a2d3321d1ea23b55d9117e5c2014e4ab0fa5e224','cc9c12dd44b43fa78ad340a91d20432e3f119c19','3d0cb9632b521a3fb334f567b3cdec985e85abd7','f158c8b44e4cdf872cc5bb4d148fad3ff27ea03f','1dfb5ce6c1a10d870a35b24314976d887c700c42','1fc4fad5eead893c51be2b6aa1d705cd58ebcfe9','f85417e345053cf627abf572911c0f7ffefe16c8','277ea1a5aade6c61a8386ff73086a91160caf5a2','32714c0ca1bae661ba9342ef275b2e6e5e025c34','619aa593419925064f51b0602000a2b9d13a8bc3','40fd9f6e4a08a69596f0dc0846d44ebd39e4913d','82120b3520ad73b9e11a413631e6f015ed0cf265','a9f265e67b901f8d41ece9bb631696795327ed50','6325e1d53c83784c1e5092861c8b0138fb4871ad','451c02faba02d2768e3497fdcc8ffb0dec41640d','fefda3429288aa79b4c9e8e9e3ba97897d0783c8','6328f70bb445e1dd692c36d6c92e2a2a7ed6ee0f','287320444eec6e3b4481d33738f971b696a2d6f0','65ea3a80efc973b5cd92c1db5a4520365bbb5478','d60de29dd2f3e8a080882986d6689d74fb981619','343048017f1928db8ba4c0b45a4f1dd3dadf3063','cf14f5563275bac3fe2c77f8973e882e51965b5b','2d8f9308951237fd0dcba3ff7709369cfd7563fd','87421a25ca75e2d6a165eb981921235f4de9210a','30d87693aead287d5a63310d2f819623455f4133','20d7a996a47fab66aead30ed7012d50b990dc65d','258c0966cc576ec8a563e41e783fa34f6b5c8baf','f7ee5b858e93b22e1594d6d8cd0bc0def665ac02','6d9127103f8733d168e69cf04b576a7b0bea3d5c','03db7399a2d674955930611fdbcaad9f4064243a','a0a1dcb330314ce12af02d136319a1be6a1ffa53','09b3483ef5f7a83aa9e3224d0335b6f9aa78ac73','34d56f09a1a9c0f51a8abc41be2f157faf0e8d15','dffa0785e6c80af52950a64d8612633de58bbb29','076e6ac3cb344d60f6ce9a5188cf103ff053830c','5f14cb9c8502f09105ad83e27842625c70f3857d','25172f395b855b6eeb61ff95a8162a34ca92195e','053beabf01d70d03a0ca61b809d5d986ddf2ea3d','bd385dbe93b192c2182e09e75d3664b96845ea0a','2ddaacb4f4e566ae79af5259f57f5b4dd166c19f','d430f2fc3bd20e658ed00b55e6c27f14204a7b6f','bbdb0a08b66b0789068ddac96a05ac39e104553d','37ed2a3a96026e7a78f6675154d11746aa9484e0','4177a74406cf1c048d2d6d6bcf774445f55ba517','fc188652d9f486174b21d1919f35ea8c13636ca1','c10f83cc69763e42964e08ddb976a1bd27b51e2f','64452c62ced87bd3cd8fd88df33a33d5531818d6','96dc04673021ad113df40397113df972f090c1f6','26b7eb0b18d9cd0a69d19c7f6ee9f12c2d0b2783','f8d500cd7089942814fa0751c75bd37e63790685','3b386c205ec767f842e63491d14ec90192f562dd','ca8ecc666a82d1f2f48a095d42d9f95700e015bd','6727ab7c4240bf05f0a5d9ca4b384ca8d61e2d4f','3ad08edf5b9690be261dc375da3b1d9ec82e499a','06ed0f32cfd261010c9d810ff8317ef96b47c04c','44ea84c7e4e401c9d3f96d7cc39709e4be81edc8','67af290f1b04c4b1a67131edba1ee832c690432c','2f1899f72fafcb062418e8ce892188040de4708c','ed2a4958c832ef6cec993cb52afc808132714d0a','c7943ccd9d880664b0894a2035e1f2a837f37c7a','bbc836b1f494223d4eb8982930d693489d135740','617fdf08035740698b2f0f4c3874dbb469fd1848','e901fe2093e51eccb4d9d23103214bc527af265c','63970995ab6b8aee63de9e7a7667178d4fc86820','d7607fdd41625431bcbee319a86db1b73fc49edd','ce31a583a3921e306aaa558b1ea798b34d2bb0dc','04ba864e740f7ef104570ddc6af834e6336031ed','ea6ab4a56efd19cc04b5656583ea6d5c9cfd2752','1bb5dfba014f63c0a18b12097ed9a28d64552e07','a2dd4d5a28a61ec99ce9dae35e9d4ffe92812e2f','e26f001557952154c308e3e6f6d1789cf711ef27','34c1f9c212198edd7baf56e658db21d98c59f74c','e1c47adbde197e3eaa9504f3582fd9deac1cbbff','0f5aba4c1ed57ac3c6a2774f3bf43ef598bd9915','d11ff75681c4df2ff24ad731ff244afc3e1c87af','fa392f5dd6f2f6a73635c06208c8989caa874588','5815b2afbb0a7f4756af7914407f71f469658f38','a100bb6a2a80bec65c418d672144b6f647fbd46b','f66797fd63c8b0254b4ef28e9c38c3d4e512c93c','f10a41faca4cf01e39ed92c4eefff0631d1195dc','5d14e20ae19e0b3e14dcb997e3ccad5f0e1956e1','1949f8c612e660985fb5eb28235b6f9386ed4ffc',];
+ var tests = {
+ "4235382bf15f93f7dd1096832ae74cc71edef4d7":[async_test('html5lib_tests1.html 4235382bf15f93f7dd1096832ae74cc71edef4d7'), "Test", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Test%22"],"ad8515e9db0abd26469d0d2e46b42cebf606d4f3":[async_test('html5lib_tests1.html ad8515e9db0abd26469d0d2e46b42cebf606d4f3'), "%3Cp%3EOne%3Cp%3ETwo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22One%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22Two%22"],"2433aa5c088d78da9e7824e499f639177f56625d":[async_test('html5lib_tests1.html 2433aa5c088d78da9e7824e499f639177f56625d'), "Line1%3Cbr%3ELine2%3Cbr%3ELine3%3Cbr%3ELine4", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Line1%22%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%22Line2%22%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%22Line3%22%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%22Line4%22"],"c99d322c3502e38e9d18ac6c0180fa5462ce612e":[async_test('html5lib_tests1.html c99d322c3502e38e9d18ac6c0180fa5462ce612e'), "%3Chtml%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"d8473f7b5cec9d99526179f980ebf55a0beccbd3":[async_test('html5lib_tests1.html d8473f7b5cec9d99526179f980ebf55a0beccbd3'), "%3Chead%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"ac7703fbb5c62cadb25024aed762c206c187a919":[async_test('html5lib_tests1.html ac7703fbb5c62cadb25024aed762c206c187a919'), "%3Cbody%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"a00121213e2eb2c846a575f662e8c69389bfc44d":[async_test('html5lib_tests1.html a00121213e2eb2c846a575f662e8c69389bfc44d'), "%3Chtml%3E%3Chead%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"447f22e6a43ddbbc308afbc78b64b16452bc7bbb":[async_test('html5lib_tests1.html 447f22e6a43ddbbc308afbc78b64b16452bc7bbb'), "%3Chtml%3E%3Chead%3E%3C/head%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"cdac424e0f2fb979f21a64f50793d529375c01b3":[async_test('html5lib_tests1.html cdac424e0f2fb979f21a64f50793d529375c01b3'), "%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"63587d177231d2478a6ffd25f3c830fed7cc2efe":[async_test('html5lib_tests1.html 63587d177231d2478a6ffd25f3c830fed7cc2efe'), "%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"a3e13da13681f9f16c65334099136f3b5c235e6d":[async_test('html5lib_tests1.html a3e13da13681f9f16c65334099136f3b5c235e6d'), "%3Chtml%3E%3Chead%3E%3Cbody%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"b7a06a5aa0c19d7914b853f5ed497743bb269e56":[async_test('html5lib_tests1.html b7a06a5aa0c19d7914b853f5ed497743bb269e56'), "%3Chtml%3E%3Chead%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"3d28b753b97f460868ca65ed8fc153021a815b8c":[async_test('html5lib_tests1.html 3d28b753b97f460868ca65ed8fc153021a815b8c'), "%3Chtml%3E%3Chead%3E%3Cbody%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"83cce5b1e1e49c92c618aabf1ed60926a8736456":[async_test('html5lib_tests1.html 83cce5b1e1e49c92c618aabf1ed60926a8736456'), "%3Chtml%3E%3Cbody%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"b9e809bc4521004440bf558c7dc5d7dc1ae3dd40":[async_test('html5lib_tests1.html b9e809bc4521004440bf558c7dc5d7dc1ae3dd40'), "%3Cbody%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"60302916ab9a2128104dbf72629875ad19b5cb16":[async_test('html5lib_tests1.html 60302916ab9a2128104dbf72629875ad19b5cb16'), "%3Chead%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"f4ad8e574fcac3bb08070eeb345855ea7081ea1d":[async_test('html5lib_tests1.html f4ad8e574fcac3bb08070eeb345855ea7081ea1d'), "%3C/head%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"d38fe13d87344a20bdf6e111988b5a09ed247913":[async_test('html5lib_tests1.html d38fe13d87344a20bdf6e111988b5a09ed247913'), "%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"e5f2e91cbff6a4bc56149b889f4f9396e455c5ad":[async_test('html5lib_tests1.html e5f2e91cbff6a4bc56149b889f4f9396e455c5ad'), "%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"18b58d1de184b6866963c902ff8451dd3522f540":[async_test('html5lib_tests1.html 18b58d1de184b6866963c902ff8451dd3522f540'), "%3Cb%3E%3Ctable%3E%3Ctd%3E%3Ci%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E"],"88eb93691065e212a4323f55ec326585c7c44262":[async_test('html5lib_tests1.html 88eb93691065e212a4323f55ec326585c7c44262'), "%3Cb%3E%3Ctable%3E%3Ctd%3E%3C/b%3E%3Ci%3E%3C/table%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"260db4c31fb80894beb825c4793e6142f178497d":[async_test('html5lib_tests1.html 260db4c31fb80894beb825c4793e6142f178497d'), "%3Ch1%3EHello%3Ch2%3EWorld", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ch1%3E%0A%7C%20%20%20%20%20%20%20%22Hello%22%0A%7C%20%20%20%20%20%3Ch2%3E%0A%7C%20%20%20%20%20%20%20%22World%22"],"59d9c5e2941952f7af99ed3e21d66e4fdb1df07c":[async_test('html5lib_tests1.html 59d9c5e2941952f7af99ed3e21d66e4fdb1df07c'), "%3Ca%3E%3Cp%3EX%3Ca%3EY%3C/a%3EZ%3C/p%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%22Y%22%0A%7C%20%20%20%20%20%20%20%22Z%22"],"1e788677da8eb26f560409392ff674744c6f8b64":[async_test('html5lib_tests1.html 1e788677da8eb26f560409392ff674744c6f8b64'), "%3Cb%3E%3Cbutton%3Efoo%3C/b%3Ebar", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%22bar%22"],"240ee32b47e30bcf34483c7a7530cfeb99a6d1f1":[async_test('html5lib_tests1.html 240ee32b47e30bcf34483c7a7530cfeb99a6d1f1'), "%3C%21DOCTYPE%20html%3E%3Cspan%3E%3Cbutton%3Efoo%3C/span%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foobar%22"],"e85dc012a49630206e6163cd1e0fd6c706865106":[async_test('html5lib_tests1.html e85dc012a49630206e6163cd1e0fd6c706865106'), "%3Cp%3E%3Cb%3E%3Cdiv%3E%3Cmarquee%3E%3C/p%3E%3C/b%3E%3C/div%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmarquee%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"c440d1f0223d6b3ce85955d8246fdd47ecdfb034":[async_test('html5lib_tests1.html c440d1f0223d6b3ce85955d8246fdd47ecdfb034'), "%3Cscript%3E%3Cdiv%3E%3C/script%3E%3C/div%3E%3Ctitle%3E%3Cp%3E%3C/title%3E%3Cp%3E%3Cp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3Cdiv%3E%22%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3Cp%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cp%3E"],"9a2ce05a0b342b377ef054385131ad4d1b5ae84b":[async_test('html5lib_tests1.html 9a2ce05a0b342b377ef054385131ad4d1b5ae84b'), "%3C%21--%3E%3Cdiv%3E--%3C%21--%3E", "%23document%0A%7C%20%3C%21--%20%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22--%22%0A%7C%20%20%20%20%20%20%20%3C%21--%20%20--%3E"],"daa9d8440e2bc4d560536b52123b01e52aa81692":[async_test('html5lib_tests1.html daa9d8440e2bc4d560536b52123b01e52aa81692'), "%3Cp%3E%3Chr%3E%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Chr%3E%0A%7C%20%20%20%20%20%3Cp%3E"],"0b27e026dd03a356bbd78690fff7fb40cd63b606":[async_test('html5lib_tests1.html 0b27e026dd03a356bbd78690fff7fb40cd63b606'), "%3Cselect%3E%3Cb%3E%3Coption%3E%3Cselect%3E%3Coption%3E%3C/b%3E%3C/select%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"a2d3321d1ea23b55d9117e5c2014e4ab0fa5e224":[async_test('html5lib_tests1.html a2d3321d1ea23b55d9117e5c2014e4ab0fa5e224'), "%3Ca%3E%3Ctable%3E%3Ctd%3E%3Ca%3E%3Ctable%3E%3C/table%3E%3Ca%3E%3C/tr%3E%3Ca%3E%3C/table%3E%3Cb%3EX%3C/b%3EC%3Ca%3EY", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%20%20%22C%22%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%22Y%22"],"cc9c12dd44b43fa78ad340a91d20432e3f119c19":[async_test('html5lib_tests1.html cc9c12dd44b43fa78ad340a91d20432e3f119c19'), "%3Ca%20X%3E0%3Cb%3E1%3Ca%20Y%3E2", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20x%3D%22%22%0A%7C%20%20%20%20%20%20%20%220%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20y%3D%22%22%0A%7C%20%20%20%20%20%20%20%20%20%222%22"],"3d0cb9632b521a3fb334f567b3cdec985e85abd7":[async_test('html5lib_tests1.html 3d0cb9632b521a3fb334f567b3cdec985e85abd7'), "%3C%21-----%3E%3Cfont%3E%3Cdiv%3Ehello%3Ctable%3Eexcite%21%3Cb%3Eme%21%3Cth%3E%3Ci%3Eplease%21%3C/tr%3E%3C%21--X--%3E", "%23document%0A%7C%20%3C%21--%20-%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%22helloexcite%21%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22me%21%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22please%21%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3C%21--%20X%20--%3E"],"f158c8b44e4cdf872cc5bb4d148fad3ff27ea03f":[async_test('html5lib_tests1.html f158c8b44e4cdf872cc5bb4d148fad3ff27ea03f'), "%3C%21DOCTYPE%20html%3E%3Cli%3Ehello%3Cli%3Eworld%3Cul%3Ehow%3Cli%3Edo%3C/ul%3Eyou%3C/body%3E%3C%21--do--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%22hello%22%0A%7C%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%22world%22%0A%7C%20%20%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%20%20%22how%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22do%22%0A%7C%20%20%20%20%20%20%20%22you%22%0A%7C%20%20%20%3C%21--%20do%20--%3E"],"1dfb5ce6c1a10d870a35b24314976d887c700c42":[async_test('html5lib_tests1.html 1dfb5ce6c1a10d870a35b24314976d887c700c42'), "%3C%21DOCTYPE%20html%3EA%3Coption%3EB%3Coptgroup%3EC%3Cselect%3ED%3C/option%3EE", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%22B%22%0A%7C%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%22C%22%0A%7C%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%22DE%22"],"1fc4fad5eead893c51be2b6aa1d705cd58ebcfe9":[async_test('html5lib_tests1.html 1fc4fad5eead893c51be2b6aa1d705cd58ebcfe9'), "%3C", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%3C%22"],"f85417e345053cf627abf572911c0f7ffefe16c8":[async_test('html5lib_tests1.html f85417e345053cf627abf572911c0f7ffefe16c8'), "%3C%23", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%3C%23%22"],"277ea1a5aade6c61a8386ff73086a91160caf5a2":[async_test('html5lib_tests1.html 277ea1a5aade6c61a8386ff73086a91160caf5a2'), "%3C/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%3C/%22"],"32714c0ca1bae661ba9342ef275b2e6e5e025c34":[async_test('html5lib_tests1.html 32714c0ca1bae661ba9342ef275b2e6e5e025c34'), "%3C/%23", "%23document%0A%7C%20%3C%21--%20%23%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"619aa593419925064f51b0602000a2b9d13a8bc3":[async_test('html5lib_tests1.html 619aa593419925064f51b0602000a2b9d13a8bc3'), "%3C%3F", "%23document%0A%7C%20%3C%21--%20%3F%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"40fd9f6e4a08a69596f0dc0846d44ebd39e4913d":[async_test('html5lib_tests1.html 40fd9f6e4a08a69596f0dc0846d44ebd39e4913d'), "%3C%3F%23", "%23document%0A%7C%20%3C%21--%20%3F%23%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"82120b3520ad73b9e11a413631e6f015ed0cf265":[async_test('html5lib_tests1.html 82120b3520ad73b9e11a413631e6f015ed0cf265'), "%3C%21", "%23document%0A%7C%20%3C%21--%20%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"a9f265e67b901f8d41ece9bb631696795327ed50":[async_test('html5lib_tests1.html a9f265e67b901f8d41ece9bb631696795327ed50'), "%3C%21%23", "%23document%0A%7C%20%3C%21--%20%23%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"6325e1d53c83784c1e5092861c8b0138fb4871ad":[async_test('html5lib_tests1.html 6325e1d53c83784c1e5092861c8b0138fb4871ad'), "%3C%3FCOMMENT%3F%3E", "%23document%0A%7C%20%3C%21--%20%3FCOMMENT%3F%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"451c02faba02d2768e3497fdcc8ffb0dec41640d":[async_test('html5lib_tests1.html 451c02faba02d2768e3497fdcc8ffb0dec41640d'), "%3C%21COMMENT%3E", "%23document%0A%7C%20%3C%21--%20COMMENT%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"fefda3429288aa79b4c9e8e9e3ba97897d0783c8":[async_test('html5lib_tests1.html fefda3429288aa79b4c9e8e9e3ba97897d0783c8'), "%3C/%20COMMENT%20%3E", "%23document%0A%7C%20%3C%21--%20%20COMMENT%20%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"6328f70bb445e1dd692c36d6c92e2a2a7ed6ee0f":[async_test('html5lib_tests1.html 6328f70bb445e1dd692c36d6c92e2a2a7ed6ee0f'), "%3C%3FCOM--MENT%3F%3E", "%23document%0A%7C%20%3C%21--%20%3FCOM--MENT%3F%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"287320444eec6e3b4481d33738f971b696a2d6f0":[async_test('html5lib_tests1.html 287320444eec6e3b4481d33738f971b696a2d6f0'), "%3C%21COM--MENT%3E", "%23document%0A%7C%20%3C%21--%20COM--MENT%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"65ea3a80efc973b5cd92c1db5a4520365bbb5478":[async_test('html5lib_tests1.html 65ea3a80efc973b5cd92c1db5a4520365bbb5478'), "%3C/%20COM--MENT%20%3E", "%23document%0A%7C%20%3C%21--%20%20COM--MENT%20%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"d60de29dd2f3e8a080882986d6689d74fb981619":[async_test('html5lib_tests1.html d60de29dd2f3e8a080882986d6689d74fb981619'), "%3C%21DOCTYPE%20html%3E%3Cstyle%3E%20EOF", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%20EOF%22%0A%7C%20%20%20%3Cbody%3E"],"343048017f1928db8ba4c0b45a4f1dd3dadf3063":[async_test('html5lib_tests1.html 343048017f1928db8ba4c0b45a4f1dd3dadf3063'), "%3C%21DOCTYPE%20html%3E%3Cscript%3E%20%3C%21--%20%3C/script%3E%20--%3E%20%3C/script%3E%20EOF", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21--%20%22%0A%7C%20%20%20%20%20%22%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%20%20EOF%22"],"cf14f5563275bac3fe2c77f8973e882e51965b5b":[async_test('html5lib_tests1.html cf14f5563275bac3fe2c77f8973e882e51965b5b'), "%3Cb%3E%3Cp%3E%3C/b%3ETEST", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22TEST%22"],"2d8f9308951237fd0dcba3ff7709369cfd7563fd":[async_test('html5lib_tests1.html 2d8f9308951237fd0dcba3ff7709369cfd7563fd'), "%3Cp%20id%3Da%3E%3Cb%3E%3Cp%20id%3Db%3E%3C/b%3ETEST", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20id%3D%22b%22%0A%7C%20%20%20%20%20%20%20%22TEST%22"],"87421a25ca75e2d6a165eb981921235f4de9210a":[async_test('html5lib_tests1.html 87421a25ca75e2d6a165eb981921235f4de9210a'), "%3Cb%20id%3Da%3E%3Cp%3E%3Cb%20id%3Db%3E%3C/p%3E%3C/b%3ETEST", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%22b%22%0A%7C%20%20%20%20%20%20%20%22TEST%22"],"30d87693aead287d5a63310d2f819623455f4133":[async_test('html5lib_tests1.html 30d87693aead287d5a63310d2f819623455f4133'), "%3C%21DOCTYPE%20html%3E%3Ctitle%3EU-test%3C/title%3E%3Cbody%3E%3Cdiv%3E%3Cp%3ETest%3Cu%3E%3C/p%3E%3C/div%3E%3C/body%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22U-test%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22Test%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cu%3E"],"20d7a996a47fab66aead30ed7012d50b990dc65d":[async_test('html5lib_tests1.html 20d7a996a47fab66aead30ed7012d50b990dc65d'), "%3C%21DOCTYPE%20html%3E%3Cfont%3E%3Ctable%3E%3C/font%3E%3C/table%3E%3C/font%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],"258c0966cc576ec8a563e41e783fa34f6b5c8baf":[async_test('html5lib_tests1.html 258c0966cc576ec8a563e41e783fa34f6b5c8baf'), "%3Cfont%3E%3Cp%3Ehello%3Cb%3Ecruel%3C/font%3Eworld", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%22hello%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22cruel%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22world%22"],"f7ee5b858e93b22e1594d6d8cd0bc0def665ac02":[async_test('html5lib_tests1.html f7ee5b858e93b22e1594d6d8cd0bc0def665ac02'), "%3Cb%3ETest%3C/i%3ETest", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22TestTest%22"],"6d9127103f8733d168e69cf04b576a7b0bea3d5c":[async_test('html5lib_tests1.html 6d9127103f8733d168e69cf04b576a7b0bea3d5c'), "%3Cb%3EA%3Ccite%3EB%3Cdiv%3EC", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Ccite%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22C%22"],"03db7399a2d674955930611fdbcaad9f4064243a":[async_test('html5lib_tests1.html 03db7399a2d674955930611fdbcaad9f4064243a'), "%3Cb%3EA%3Ccite%3EB%3Cdiv%3EC%3C/cite%3ED", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Ccite%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22CD%22"],"a0a1dcb330314ce12af02d136319a1be6a1ffa53":[async_test('html5lib_tests1.html a0a1dcb330314ce12af02d136319a1be6a1ffa53'), "%3Cb%3EA%3Ccite%3EB%3Cdiv%3EC%3C/b%3ED", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Ccite%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22C%22%0A%7C%20%20%20%20%20%20%20%22D%22"],"09b3483ef5f7a83aa9e3224d0335b6f9aa78ac73":[async_test('html5lib_tests1.html 09b3483ef5f7a83aa9e3224d0335b6f9aa78ac73'), "", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"34d56f09a1a9c0f51a8abc41be2f157faf0e8d15":[async_test('html5lib_tests1.html 34d56f09a1a9c0f51a8abc41be2f157faf0e8d15'), "%3CDIV%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"dffa0785e6c80af52950a64d8612633de58bbb29":[async_test('html5lib_tests1.html dffa0785e6c80af52950a64d8612633de58bbb29'), "%3CDIV%3E%20abc", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%22"],"076e6ac3cb344d60f6ce9a5188cf103ff053830c":[async_test('html5lib_tests1.html 076e6ac3cb344d60f6ce9a5188cf103ff053830c'), "%3CDIV%3E%20abc%20%3CB%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E"],"5f14cb9c8502f09105ad83e27842625c70f3857d":[async_test('html5lib_tests1.html 5f14cb9c8502f09105ad83e27842625c70f3857d'), "%3CDIV%3E%20abc%20%3CB%3E%20def", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%22"],"25172f395b855b6eeb61ff95a8162a34ca92195e":[async_test('html5lib_tests1.html 25172f395b855b6eeb61ff95a8162a34ca92195e'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E"],"053beabf01d70d03a0ca61b809d5d986ddf2ea3d":[async_test('html5lib_tests1.html 053beabf01d70d03a0ca61b809d5d986ddf2ea3d'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%22"],"bd385dbe93b192c2182e09e75d3664b96845ea0a":[async_test('html5lib_tests1.html bd385dbe93b192c2182e09e75d3664b96845ea0a'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E"],"2ddaacb4f4e566ae79af5259f57f5b4dd166c19f":[async_test('html5lib_tests1.html 2ddaacb4f4e566ae79af5259f57f5b4dd166c19f'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E%20jkl", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20jkl%22"],"d430f2fc3bd20e658ed00b55e6c27f14204a7b6f":[async_test('html5lib_tests1.html d430f2fc3bd20e658ed00b55e6c27f14204a7b6f'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E%20jkl%20%3C/B%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20jkl%20%22"],"bbdb0a08b66b0789068ddac96a05ac39e104553d":[async_test('html5lib_tests1.html bbdb0a08b66b0789068ddac96a05ac39e104553d'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E%20jkl%20%3C/B%3E%20mno", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20jkl%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20mno%22"],"37ed2a3a96026e7a78f6675154d11746aa9484e0":[async_test('html5lib_tests1.html 37ed2a3a96026e7a78f6675154d11746aa9484e0'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E%20jkl%20%3C/B%3E%20mno%20%3C/I%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20jkl%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20mno%20%22"],"4177a74406cf1c048d2d6d6bcf774445f55ba517":[async_test('html5lib_tests1.html 4177a74406cf1c048d2d6d6bcf774445f55ba517'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E%20jkl%20%3C/B%3E%20mno%20%3C/I%3E%20pqr", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20jkl%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20mno%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20pqr%22"],"fc188652d9f486174b21d1919f35ea8c13636ca1":[async_test('html5lib_tests1.html fc188652d9f486174b21d1919f35ea8c13636ca1'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E%20jkl%20%3C/B%3E%20mno%20%3C/I%3E%20pqr%20%3C/P%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20jkl%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20mno%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20pqr%20%22"],"c10f83cc69763e42964e08ddb976a1bd27b51e2f":[async_test('html5lib_tests1.html c10f83cc69763e42964e08ddb976a1bd27b51e2f'), "%3CDIV%3E%20abc%20%3CB%3E%20def%20%3CI%3E%20ghi%20%3CP%3E%20jkl%20%3C/B%3E%20mno%20%3C/I%3E%20pqr%20%3C/P%3E%20stu", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%20abc%20%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20def%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20ghi%20%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20jkl%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20mno%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20pqr%20%22%0A%7C%20%20%20%20%20%20%20%22%20stu%22"],"64452c62ced87bd3cd8fd88df33a33d5531818d6":[async_test('html5lib_tests1.html 64452c62ced87bd3cd8fd88df33a33d5531818d6'), "%3Ctest%20attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctest%3E%0A%7C%20%20%20%20%20%20%20attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------%3D%22%22"],"96dc04673021ad113df40397113df972f090c1f6":[async_test('html5lib_tests1.html 96dc04673021ad113df40397113df972f090c1f6'), "%3Ca%20href%3D%22blah%22%3Eaba%3Ctable%3E%3Ca%20href%3D%22foo%22%3Ebr%3Ctr%3E%3Ctd%3E%3C/td%3E%3C/tr%3Ex%3C/table%3Eaoe", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22blah%22%0A%7C%20%20%20%20%20%20%20%22aba%22%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%22br%22%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%22aoe%22"],"26b7eb0b18d9cd0a69d19c7f6ee9f12c2d0b2783":[async_test('html5lib_tests1.html 26b7eb0b18d9cd0a69d19c7f6ee9f12c2d0b2783'), "%3Ca%20href%3D%22blah%22%3Eaba%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Ca%20href%3D%22foo%22%3Ebr%3C/td%3E%3C/tr%3Ex%3C/table%3Eaoe", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22blah%22%0A%7C%20%20%20%20%20%20%20%22abax%22%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22br%22%0A%7C%20%20%20%20%20%20%20%22aoe%22"],"f8d500cd7089942814fa0751c75bd37e63790685":[async_test('html5lib_tests1.html f8d500cd7089942814fa0751c75bd37e63790685'), "%3Ctable%3E%3Ca%20href%3D%22blah%22%3Eaba%3Ctr%3E%3Ctd%3E%3Ca%20href%3D%22foo%22%3Ebr%3C/td%3E%3C/tr%3Ex%3C/table%3Eaoe", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22blah%22%0A%7C%20%20%20%20%20%20%20%22aba%22%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22blah%22%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22br%22%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22blah%22%0A%7C%20%20%20%20%20%20%20%22aoe%22"],"3b386c205ec767f842e63491d14ec90192f562dd":[async_test('html5lib_tests1.html 3b386c205ec767f842e63491d14ec90192f562dd'), "%3Ca%20href%3Da%3Eaa%3Cmarquee%3Eaa%3Ca%20href%3Db%3Ebb%3C/marquee%3Eaa", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22a%22%0A%7C%20%20%20%20%20%20%20%22aa%22%0A%7C%20%20%20%20%20%20%20%3Cmarquee%3E%0A%7C%20%20%20%20%20%20%20%20%20%22aa%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20href%3D%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22bb%22%0A%7C%20%20%20%20%20%20%20%22aa%22"],"ca8ecc666a82d1f2f48a095d42d9f95700e015bd":[async_test('html5lib_tests1.html ca8ecc666a82d1f2f48a095d42d9f95700e015bd'), "%3Cwbr%3E%3Cstrike%3E%3Ccode%3E%3C/strike%3E%3Ccode%3E%3Cstrike%3E%3C/code%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cwbr%3E%0A%7C%20%20%20%20%20%3Cstrike%3E%0A%7C%20%20%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cstrike%3E"],"6727ab7c4240bf05f0a5d9ca4b384ca8d61e2d4f":[async_test('html5lib_tests1.html 6727ab7c4240bf05f0a5d9ca4b384ca8d61e2d4f'), "%3C%21DOCTYPE%20html%3E%3Cspacer%3Efoo", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cspacer%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"3ad08edf5b9690be261dc375da3b1d9ec82e499a":[async_test('html5lib_tests1.html 3ad08edf5b9690be261dc375da3b1d9ec82e499a'), "%3Ctitle%3E%3Cmeta%3E%3C/title%3E%3Clink%3E%3Ctitle%3E%3Cmeta%3E%3C/title%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3Cmeta%3E%22%0A%7C%20%20%20%20%20%3Clink%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3Cmeta%3E%22%0A%7C%20%20%20%3Cbody%3E"],"06ed0f32cfd261010c9d810ff8317ef96b47c04c":[async_test('html5lib_tests1.html 06ed0f32cfd261010c9d810ff8317ef96b47c04c'), "%3Cstyle%3E%3C%21--%3C/style%3E%3Cmeta%3E%3Cscript%3E--%3E%3Clink%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22--%3E%3Clink%3E%22%0A%7C%20%20%20%3Cbody%3E"],"44ea84c7e4e401c9d3f96d7cc39709e4be81edc8":[async_test('html5lib_tests1.html 44ea84c7e4e401c9d3f96d7cc39709e4be81edc8'), "%3Chead%3E%3Cmeta%3E%3C/head%3E%3Clink%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%3Clink%3E%0A%7C%20%20%20%3Cbody%3E"],"67af290f1b04c4b1a67131edba1ee832c690432c":[async_test('html5lib_tests1.html 67af290f1b04c4b1a67131edba1ee832c690432c'), "%3Ctable%3E%3Ctr%3E%3Ctr%3E%3Ctd%3E%3Ctd%3E%3Cspan%3E%3Cth%3E%3Cspan%3EX%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cth%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"2f1899f72fafcb062418e8ce892188040de4708c":[async_test('html5lib_tests1.html 2f1899f72fafcb062418e8ce892188040de4708c'), "%3Cbody%3E%3Cbody%3E%3Cbase%3E%3Clink%3E%3Cmeta%3E%3Ctitle%3E%3Cp%3E%3C/title%3E%3Cbody%3E%3Cp%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbase%3E%0A%7C%20%20%20%20%20%3Clink%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3Cp%3E%22%0A%7C%20%20%20%20%20%3Cp%3E"],"ed2a4958c832ef6cec993cb52afc808132714d0a":[async_test('html5lib_tests1.html ed2a4958c832ef6cec993cb52afc808132714d0a'), "%3Ctextarea%3E%3Cp%3E%3C/textarea%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3Cp%3E%22"],"c7943ccd9d880664b0894a2035e1f2a837f37c7a":[async_test('html5lib_tests1.html c7943ccd9d880664b0894a2035e1f2a837f37c7a'), "%3Cp%3E%3Cimage%3E%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cimg%3E"],"bbc836b1f494223d4eb8982930d693489d135740":[async_test('html5lib_tests1.html bbc836b1f494223d4eb8982930d693489d135740'), "%3Ca%3E%3Ctable%3E%3Ca%3E%3C/table%3E%3Cp%3E%3Ca%3E%3Cdiv%3E%3Ca%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E"],"617fdf08035740698b2f0f4c3874dbb469fd1848":[async_test('html5lib_tests1.html 617fdf08035740698b2f0f4c3874dbb469fd1848'), "%3Chead%3E%3C/p%3E%3Cmeta%3E%3Cp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E"],"e901fe2093e51eccb4d9d23103214bc527af265c":[async_test('html5lib_tests1.html e901fe2093e51eccb4d9d23103214bc527af265c'), "%3Chead%3E%3C/html%3E%3Cmeta%3E%3Cp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%3Cp%3E"],"63970995ab6b8aee63de9e7a7667178d4fc86820":[async_test('html5lib_tests1.html 63970995ab6b8aee63de9e7a7667178d4fc86820'), "%3Cb%3E%3Ctable%3E%3Ctd%3E%3C/b%3E%3Ci%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E"],"d7607fdd41625431bcbee319a86db1b73fc49edd":[async_test('html5lib_tests1.html d7607fdd41625431bcbee319a86db1b73fc49edd'), "%3Ch1%3E%3Ch2%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ch1%3E%0A%7C%20%20%20%20%20%3Ch2%3E"],"ce31a583a3921e306aaa558b1ea798b34d2bb0dc":[async_test('html5lib_tests1.html ce31a583a3921e306aaa558b1ea798b34d2bb0dc'), "%3Ca%3E%3Cp%3E%3Ca%3E%3C/a%3E%3C/p%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E"],"04ba864e740f7ef104570ddc6af834e6336031ed":[async_test('html5lib_tests1.html 04ba864e740f7ef104570ddc6af834e6336031ed'), "%3Cb%3E%3Cbutton%3E%3C/b%3E%3C/button%3E%3C/b%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E"],"ea6ab4a56efd19cc04b5656583ea6d5c9cfd2752":[async_test('html5lib_tests1.html ea6ab4a56efd19cc04b5656583ea6d5c9cfd2752'), "%3Cp%3E%3Cb%3E%3Cdiv%3E%3Cmarquee%3E%3C/p%3E%3C/b%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmarquee%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E"],"1bb5dfba014f63c0a18b12097ed9a28d64552e07":[async_test('html5lib_tests1.html 1bb5dfba014f63c0a18b12097ed9a28d64552e07'), "%3Cscript%3E%3C/script%3E%3C/div%3E%3Ctitle%3E%3C/title%3E%3Cp%3E%3Cp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cp%3E"],"a2dd4d5a28a61ec99ce9dae35e9d4ffe92812e2f":[async_test('html5lib_tests1.html a2dd4d5a28a61ec99ce9dae35e9d4ffe92812e2f'), "%3Cselect%3E%3Cb%3E%3Coption%3E%3Cselect%3E%3Coption%3E%3C/b%3E%3C/select%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%3Coption%3E"],"e26f001557952154c308e3e6f6d1789cf711ef27":[async_test('html5lib_tests1.html e26f001557952154c308e3e6f6d1789cf711ef27'), "%3Chtml%3E%3Chead%3E%3Ctitle%3E%3C/title%3E%3Cbody%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%3Cbody%3E"],"34c1f9c212198edd7baf56e658db21d98c59f74c":[async_test('html5lib_tests1.html 34c1f9c212198edd7baf56e658db21d98c59f74c'), "%3Ca%3E%3Ctable%3E%3Ctd%3E%3Ca%3E%3Ctable%3E%3C/table%3E%3Ca%3E%3C/tr%3E%3Ca%3E%3C/table%3E%3Ca%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Ca%3E"],"e1c47adbde197e3eaa9504f3582fd9deac1cbbff":[async_test('html5lib_tests1.html e1c47adbde197e3eaa9504f3582fd9deac1cbbff'), "%3Cul%3E%3Cli%3E%3C/li%3E%3Cdiv%3E%3Cli%3E%3C/div%3E%3Cli%3E%3Cli%3E%3Cdiv%3E%3Cli%3E%3Caddress%3E%3Cli%3E%3Cb%3E%3Cem%3E%3C/b%3E%3Cli%3E%3C/ul%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Caddress%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E"],"0f5aba4c1ed57ac3c6a2774f3bf43ef598bd9915":[async_test('html5lib_tests1.html 0f5aba4c1ed57ac3c6a2774f3bf43ef598bd9915'), "%3Cul%3E%3Cli%3E%3Cul%3E%3C/li%3E%3Cli%3Ea%3C/li%3E%3C/ul%3E%3C/li%3E%3C/ul%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"d11ff75681c4df2ff24ad731ff244afc3e1c87af":[async_test('html5lib_tests1.html d11ff75681c4df2ff24ad731ff244afc3e1c87af'), "%3Cframeset%3E%3Cframe%3E%3Cframeset%3E%3Cframe%3E%3C/frameset%3E%3Cnoframes%3E%3C/noframes%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E%0A%7C%20%20%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%20%20%3Cframe%3E%0A%7C%20%20%20%20%20%3Cnoframes%3E"],"fa392f5dd6f2f6a73635c06208c8989caa874588":[async_test('html5lib_tests1.html fa392f5dd6f2f6a73635c06208c8989caa874588'), "%3Ch1%3E%3Ctable%3E%3Ctd%3E%3Ch3%3E%3C/table%3E%3Ch3%3E%3C/h1%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ch1%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ch3%3E%0A%7C%20%20%20%20%20%3Ch3%3E"],"5815b2afbb0a7f4756af7914407f71f469658f38":[async_test('html5lib_tests1.html 5815b2afbb0a7f4756af7914407f71f469658f38'), "%3Ctable%3E%3Ccolgroup%3E%3Ccol%3E%3Ccolgroup%3E%3Ccol%3E%3Ccol%3E%3Ccol%3E%3Ccolgroup%3E%3Ccol%3E%3Ccol%3E%3Cthead%3E%3Ctr%3E%3Ctd%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"a100bb6a2a80bec65c418d672144b6f647fbd46b":[async_test('html5lib_tests1.html a100bb6a2a80bec65c418d672144b6f647fbd46b'), "%3Ctable%3E%3Ccol%3E%3Ctbody%3E%3Ccol%3E%3Ctr%3E%3Ccol%3E%3Ctd%3E%3Ccol%3E%3C/table%3E%3Ccol%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccol%3E"],"f66797fd63c8b0254b4ef28e9c38c3d4e512c93c":[async_test('html5lib_tests1.html f66797fd63c8b0254b4ef28e9c38c3d4e512c93c'), "%3Ctable%3E%3Ccolgroup%3E%3Ctbody%3E%3Ccolgroup%3E%3Ctr%3E%3Ccolgroup%3E%3Ctd%3E%3Ccolgroup%3E%3C/table%3E%3Ccolgroup%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E"],"f10a41faca4cf01e39ed92c4eefff0631d1195dc":[async_test('html5lib_tests1.html f10a41faca4cf01e39ed92c4eefff0631d1195dc'), "%3C/strong%3E%3C/b%3E%3C/em%3E%3C/i%3E%3C/u%3E%3C/strike%3E%3C/s%3E%3C/blink%3E%3C/tt%3E%3C/pre%3E%3C/big%3E%3C/small%3E%3C/font%3E%3C/select%3E%3C/h1%3E%3C/h2%3E%3C/h3%3E%3C/h4%3E%3C/h5%3E%3C/h6%3E%3C/body%3E%3C/br%3E%3C/a%3E%3C/img%3E%3C/title%3E%3C/span%3E%3C/style%3E%3C/script%3E%3C/table%3E%3C/th%3E%3C/td%3E%3C/tr%3E%3C/frame%3E%3C/area%3E%3C/link%3E%3C/param%3E%3C/hr%3E%3C/input%3E%3C/col%3E%3C/base%3E%3C/meta%3E%3C/basefont%3E%3C/bgsound%3E%3C/embed%3E%3C/spacer%3E%3C/p%3E%3C/dd%3E%3C/dt%3E%3C/caption%3E%3C/colgroup%3E%3C/tbody%3E%3C/tfoot%3E%3C/thead%3E%3C/address%3E%3C/blockquote%3E%3C/center%3E%3C/dir%3E%3C/div%3E%3C/dl%3E%3C/fieldset%3E%3C/listing%3E%3C/menu%3E%3C/ol%3E%3C/ul%3E%3C/li%3E%3C/nobr%3E%3C/wbr%3E%3C/form%3E%3C/button%3E%3C/marquee%3E%3C/object%3E%3C/html%3E%3C/frameset%3E%3C/head%3E%3C/iframe%3E%3C/image%3E%3C/isindex%3E%3C/noembed%3E%3C/noframes%3E%3C/noscript%3E%3C/optgroup%3E%3C/option%3E%3C/plaintext%3E%3C/textarea%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%3Cp%3E"],"5d14e20ae19e0b3e14dcb997e3ccad5f0e1956e1":[async_test('html5lib_tests1.html 5d14e20ae19e0b3e14dcb997e3ccad5f0e1956e1'), "%3Ctable%3E%3Ctr%3E%3C/strong%3E%3C/b%3E%3C/em%3E%3C/i%3E%3C/u%3E%3C/strike%3E%3C/s%3E%3C/blink%3E%3C/tt%3E%3C/pre%3E%3C/big%3E%3C/small%3E%3C/font%3E%3C/select%3E%3C/h1%3E%3C/h2%3E%3C/h3%3E%3C/h4%3E%3C/h5%3E%3C/h6%3E%3C/body%3E%3C/br%3E%3C/a%3E%3C/img%3E%3C/title%3E%3C/span%3E%3C/style%3E%3C/script%3E%3C/table%3E%3C/th%3E%3C/td%3E%3C/tr%3E%3C/frame%3E%3C/area%3E%3C/link%3E%3C/param%3E%3C/hr%3E%3C/input%3E%3C/col%3E%3C/base%3E%3C/meta%3E%3C/basefont%3E%3C/bgsound%3E%3C/embed%3E%3C/spacer%3E%3C/p%3E%3C/dd%3E%3C/dt%3E%3C/caption%3E%3C/colgroup%3E%3C/tbody%3E%3C/tfoot%3E%3C/thead%3E%3C/address%3E%3C/blockquote%3E%3C/center%3E%3C/dir%3E%3C/div%3E%3C/dl%3E%3C/fieldset%3E%3C/listing%3E%3C/menu%3E%3C/ol%3E%3C/ul%3E%3C/li%3E%3C/nobr%3E%3C/wbr%3E%3C/form%3E%3C/button%3E%3C/marquee%3E%3C/object%3E%3C/html%3E%3C/frameset%3E%3C/head%3E%3C/iframe%3E%3C/image%3E%3C/isindex%3E%3C/noembed%3E%3C/noframes%3E%3C/noscript%3E%3C/optgroup%3E%3C/option%3E%3C/plaintext%3E%3C/textarea%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%3Cp%3E"],"1949f8c612e660985fb5eb28235b6f9386ed4ffc":[async_test('html5lib_tests1.html 1949f8c612e660985fb5eb28235b6f9386ed4ffc'), "%3Cframeset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests10.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests10.html
new file mode 100644
index 0000000000..26411582b1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests10.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests10.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['33cab27b810c8329105a4447f7767307577cb52f','b833b22c2ce59749e320b26fbc4d277bc015f261','5159857bc8326870c92e525f000abe74b51f9104','fee2541ab7080ca5b363bb4b7b0c18ee1d3699ba','679fbaa80e9d15416f17eb041a8fe04c373f0e12','0fe5609efd1436590d64aa7f47643fcaf1decc9c','7900ad087a3da10cbd7eb3eae57181ca7e417f0e','52f78e3d4b60a998fe249f7c026078f13ef65fe3','01dd3726b85de9278486adaf23c7cd553ea701b0','c993a3cf01522d1b958e942f781c535bd63e7f43','3f8ac796f286506aa56f87a8b430c4d6d2802695','df1f29ea6a1d57e029cdb95216b85e9daac07ad0','ae19484ce5b8e2087e3df7f13183622d2cf059a7','7f9c4aeecce3bbf1483104213487a506cfdc6650','219f8f9866ad87d2e98f0cbbf35e0d7070848f81','a1e3a3515da712da9fa9bae0ab874a0a31207e6b','badaa2069df3f2bb2d2da79f8ae2598f66ab2d0f','64e72bd5af825a0b014a78c77d6d0be8c330892b','6b6728b11b3f67a6b67e0dec03222222783e2c40','c16f6e833fafc8e96c4d888aace5e7df190f8bb5','8b5cc06ce9513c4b0b2be26adad3357c79a10241','f396d620bf4e128981156d61f4768634c407c806','3c262f2c74c6cf5519cfad9440e118dd36fcc37f','4e788a289a02c283f0a7b71340f4947c30fd289e','b0a2a300705d9a5b6ceb8f7b9d4d240a9eb9f9e0','2e92f741697be25c07416f7885b7decc0684c874','3cffa6d987cfd48c6cd5f9e7d198ca0e79b6cdb8','f89f8c14aeaa1d3107b6aa6d87fbf10e8fd5f8c3','663691222288a8f734149fba4f1b7ff5bb201df0','dbe5123aed0264b7fe421fea4b9bc5bd304cc596','982e01ebddec435aeb2dc0f23fa77e4201a38b28','b613b907ffa87da6f3076a24ad775843e5c2a43a','4553fc5ae2782aef4100d8db614041e42c3612e9','6c10b9811a0d228cf26bc0b55ee12c7d99926270','ee2de7753d6594fbf05d321b24e2c8bb6f2323c6','225170b3f08201a11d43f153fa3c8a041173b55f','716a01ee7d076876318395625e08e555065fc3b0','3c5cc236386d2d4a16d14e5c65da7edd26697a43','6c3447a16060d2805f8c969ec0a7b5c551468f25','071f13f248eb7af8f7d512cf453b16c8ccc57483','0ef97c924e3e5e8ddf8a0bc74530868684685c38','25a94dd91d982fa75c89944ad8f066f7961c590b','575adfd2e679ad44140d8afd131fee2e5e819fc0','2eb6893275ff1de69569440710f5588b8558ca17','4f57b212888bfcc7ac6d8e29ef444cc7ae3ec495','c069a88f172919c862432dbef290a425e71414aa','f944f978d6a10ec9f194f03a5e232f9663d96ec1','d5ab79a14609f3ed214baa8e02bedb54ddcf33ec','4318bf46f0256426716a911e194f9bdf6185bd7c','aa5c4b093ee603618493cff853af125b5fb0d401','bfd98674d75ff3291d377f2a7cec3321844a0701','7fc99b729a6d57d28b2ad1edc33bb15167e051bb','8e87f0d5ca606ca162384d1bf3bb8de62bd6f398','6476f80f3d2a04e9c1323ffe78686188c579e9ac',];
+ var tests = {
+ "33cab27b810c8329105a4447f7767307577cb52f":[async_test('html5lib_tests10.html 33cab27b810c8329105a4447f7767307577cb52f'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"b833b22c2ce59749e320b26fbc4d277bc015f261":[async_test('html5lib_tests10.html b833b22c2ce59749e320b26fbc4d277bc015f261'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3C/svg%3E%3C%21%5BCDATA%5Ba%5D%5D%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3C%21--%20%5BCDATA%5Ba%5D%5D%20--%3E"],"5159857bc8326870c92e525f000abe74b51f9104":[async_test('html5lib_tests10.html 5159857bc8326870c92e525f000abe74b51f9104'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"fee2541ab7080ca5b363bb4b7b0c18ee1d3699ba":[async_test('html5lib_tests10.html fee2541ab7080ca5b363bb4b7b0c18ee1d3699ba'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cselect%3E%3Csvg%3E%3C/svg%3E%3C/select%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"679fbaa80e9d15416f17eb041a8fe04c373f0e12":[async_test('html5lib_tests10.html 679fbaa80e9d15416f17eb041a8fe04c373f0e12'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cselect%3E%3Coption%3E%3Csvg%3E%3C/svg%3E%3C/option%3E%3C/select%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E"],"0fe5609efd1436590d64aa7f47643fcaf1decc9c":[async_test('html5lib_tests10.html 0fe5609efd1436590d64aa7f47643fcaf1decc9c'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Csvg%3E%3C/svg%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"7900ad087a3da10cbd7eb3eae57181ca7e417f0e":[async_test('html5lib_tests10.html 7900ad087a3da10cbd7eb3eae57181ca7e417f0e'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3C/svg%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"52f78e3d4b60a998fe249f7c026078f13ef65fe3":[async_test('html5lib_tests10.html 52f78e3d4b60a998fe249f7c026078f13ef65fe3'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3C/svg%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"01dd3726b85de9278486adaf23c7cd553ea701b0":[async_test('html5lib_tests10.html 01dd3726b85de9278486adaf23c7cd553ea701b0'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3C/svg%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E"],"c993a3cf01522d1b958e942f781c535bd63e7f43":[async_test('html5lib_tests10.html c993a3cf01522d1b958e942f781c535bd63e7f43'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Ctr%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3C/svg%3E%3C/tr%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"3f8ac796f286506aa56f87a8b430c4d6d2802695":[async_test('html5lib_tests10.html 3f8ac796f286506aa56f87a8b430c4d6d2802695'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3C/svg%3E%3C/td%3E%3C/tr%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22"],"df1f29ea6a1d57e029cdb95216b85e9daac07ad0":[async_test('html5lib_tests10.html df1f29ea6a1d57e029cdb95216b85e9daac07ad0'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3C/svg%3E%3Cp%3Ebaz%3C/td%3E%3C/tr%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22baz%22"],"ae19484ce5b8e2087e3df7f13183622d2cf059a7":[async_test('html5lib_tests10.html ae19484ce5b8e2087e3df7f13183622d2cf059a7'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccaption%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3C/svg%3E%3Cp%3Ebaz%3C/caption%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22baz%22"],"7f9c4aeecce3bbf1483104213487a506cfdc6650":[async_test('html5lib_tests10.html 7f9c4aeecce3bbf1483104213487a506cfdc6650'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccaption%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"219f8f9866ad87d2e98f0cbbf35e0d7070848f81":[async_test('html5lib_tests10.html 219f8f9866ad87d2e98f0cbbf35e0d7070848f81'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccaption%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"a1e3a3515da712da9fa9bae0ab874a0a31207e6b":[async_test('html5lib_tests10.html a1e3a3515da712da9fa9bae0ab874a0a31207e6b'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccolgroup%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"badaa2069df3f2bb2d2da79f8ae2598f66ab2d0f":[async_test('html5lib_tests10.html badaa2069df3f2bb2d2da79f8ae2598f66ab2d0f'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22foobarbaz%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"64e72bd5af825a0b014a78c77d6d0be8c330892b":[async_test('html5lib_tests10.html 64e72bd5af825a0b014a78c77d6d0be8c330892b'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Cselect%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%22foobarbaz%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"6b6728b11b3f67a6b67e0dec03222222783e2c40":[async_test('html5lib_tests10.html 6b6728b11b3f67a6b67e0dec03222222783e2c40'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3C/body%3E%3C/html%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22baz%22"],"c16f6e833fafc8e96c4d888aace5e7df190f8bb5":[async_test('html5lib_tests10.html c16f6e833fafc8e96c4d888aace5e7df190f8bb5'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3C/body%3E%3Csvg%3E%3Cg%3Efoo%3C/g%3E%3Cg%3Ebar%3C/g%3E%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22baz%22"],"8b5cc06ce9513c4b0b2be26adad3357c79a10241":[async_test('html5lib_tests10.html 8b5cc06ce9513c4b0b2be26adad3357c79a10241'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3E%3Csvg%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cp%3E%3Cspan%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"f396d620bf4e128981156d61f4768634c407c806":[async_test('html5lib_tests10.html f396d620bf4e128981156d61f4768634c407c806'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3E%3C/frameset%3E%3Csvg%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cp%3E%3Cspan%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"3c262f2c74c6cf5519cfad9440e118dd36fcc37f":[async_test('html5lib_tests10.html 3c262f2c74c6cf5519cfad9440e118dd36fcc37f'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%3E%3Csvg%20xlink%3Ahref%3Dfoo%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20xlink%20href%3D%22foo%22"],"4e788a289a02c283f0a7b71340f4947c30fd289e":[async_test('html5lib_tests10.html 4e788a289a02c283f0a7b71340f4947c30fd289e'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%20xml%3Alang%3Den%3E%3Csvg%3E%3Cg%20xml%3Alang%3Den%20xlink%3Ahref%3Dfoo%3E%3C/g%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20xml%3Alang%3D%22en%22%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20xlink%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20xml%20lang%3D%22en%22"],"b0a2a300705d9a5b6ceb8f7b9d4d240a9eb9f9e0":[async_test('html5lib_tests10.html b0a2a300705d9a5b6ceb8f7b9d4d240a9eb9f9e0'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%20xml%3Alang%3Den%3E%3Csvg%3E%3Cg%20xml%3Alang%3Den%20xlink%3Ahref%3Dfoo%20/%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20xml%3Alang%3D%22en%22%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20xlink%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20xml%20lang%3D%22en%22"],"2e92f741697be25c07416f7885b7decc0684c874":[async_test('html5lib_tests10.html 2e92f741697be25c07416f7885b7decc0684c874'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%20xml%3Alang%3Den%3E%3Csvg%3E%3Cg%20xml%3Alang%3Den%20xlink%3Ahref%3Dfoo%20/%3Ebar%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20xml%3Alang%3D%22en%22%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20xlink%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20xml%20lang%3D%22en%22%0A%7C%20%20%20%20%20%20%20%22bar%22"],"3cffa6d987cfd48c6cd5f9e7d198ca0e79b6cdb8":[async_test('html5lib_tests10.html 3cffa6d987cfd48c6cd5f9e7d198ca0e79b6cdb8'), "%3Csvg%3E%3C/path%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"f89f8c14aeaa1d3107b6aa6d87fbf10e8fd5f8c3":[async_test('html5lib_tests10.html f89f8c14aeaa1d3107b6aa6d87fbf10e8fd5f8c3'), "%3Cdiv%3E%3Csvg%3E%3C/div%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%22a%22"],"663691222288a8f734149fba4f1b7ff5bb201df0":[async_test('html5lib_tests10.html 663691222288a8f734149fba4f1b7ff5bb201df0'), "%3Cdiv%3E%3Csvg%3E%3Cpath%3E%3C/div%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20path%3E%0A%7C%20%20%20%20%20%22a%22"],"dbe5123aed0264b7fe421fea4b9bc5bd304cc596":[async_test('html5lib_tests10.html dbe5123aed0264b7fe421fea4b9bc5bd304cc596'), "%3Cdiv%3E%3Csvg%3E%3Cpath%3E%3C/svg%3E%3Cpath%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20path%3E%0A%7C%20%20%20%20%20%20%20%3Cpath%3E"],"982e01ebddec435aeb2dc0f23fa77e4201a38b28":[async_test('html5lib_tests10.html 982e01ebddec435aeb2dc0f23fa77e4201a38b28'), "%3Cdiv%3E%3Csvg%3E%3Cpath%3E%3CforeignObject%3E%3Cmath%3E%3C/div%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20path%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"b613b907ffa87da6f3076a24ad775843e5c2a43a":[async_test('html5lib_tests10.html b613b907ffa87da6f3076a24ad775843e5c2a43a'), "%3Cdiv%3E%3Csvg%3E%3Cpath%3E%3CforeignObject%3E%3Cp%3E%3C/div%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20path%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"4553fc5ae2782aef4100d8db614041e42c3612e9":[async_test('html5lib_tests10.html 4553fc5ae2782aef4100d8db614041e42c3612e9'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3Cdesc%3E%3Cdiv%3E%3Csvg%3E%3Cul%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"6c10b9811a0d228cf26bc0b55ee12c7d99926270":[async_test('html5lib_tests10.html 6c10b9811a0d228cf26bc0b55ee12c7d99926270'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3Cdesc%3E%3Csvg%3E%3Cul%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"ee2de7753d6594fbf05d321b24e2c8bb6f2323c6":[async_test('html5lib_tests10.html ee2de7753d6594fbf05d321b24e2c8bb6f2323c6'), "%3C%21DOCTYPE%20html%3E%3Cp%3E%3Csvg%3E%3Cdesc%3E%3Cp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E"],"225170b3f08201a11d43f153fa3c8a041173b55f":[async_test('html5lib_tests10.html 225170b3f08201a11d43f153fa3c8a041173b55f'), "%3C%21DOCTYPE%20html%3E%3Cp%3E%3Csvg%3E%3Ctitle%3E%3Cp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20title%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E"],"716a01ee7d076876318395625e08e555065fc3b0":[async_test('html5lib_tests10.html 716a01ee7d076876318395625e08e555065fc3b0'), "%3Cdiv%3E%3Csvg%3E%3Cpath%3E%3CforeignObject%3E%3Cp%3E%3C/foreignObject%3E%3Cp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20path%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E"],"3c5cc236386d2d4a16d14e5c65da7edd26697a43":[async_test('html5lib_tests10.html 3c5cc236386d2d4a16d14e5c65da7edd26697a43'), "%3Cmath%3E%3Cmi%3E%3Cdiv%3E%3Cobject%3E%3Cdiv%3E%3Cspan%3E%3C/span%3E%3C/div%3E%3C/object%3E%3C/div%3E%3C/mi%3E%3Cmi%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cobject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E"],"6c3447a16060d2805f8c969ec0a7b5c551468f25":[async_test('html5lib_tests10.html 6c3447a16060d2805f8c969ec0a7b5c551468f25'), "%3Cmath%3E%3Cmi%3E%3Csvg%3E%3CforeignObject%3E%3Cdiv%3E%3Cdiv%3E%3C/div%3E%3C/div%3E%3C/foreignObject%3E%3C/svg%3E%3C/mi%3E%3Cmi%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E"],"071f13f248eb7af8f7d512cf453b16c8ccc57483":[async_test('html5lib_tests10.html 071f13f248eb7af8f7d512cf453b16c8ccc57483'), "%3Csvg%3E%3Cscript%3E%3C/script%3E%3Cpath%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20script%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20path%3E"],"0ef97c924e3e5e8ddf8a0bc74530868684685c38":[async_test('html5lib_tests10.html 0ef97c924e3e5e8ddf8a0bc74530868684685c38'), "%3Ctable%3E%3Csvg%3E%3C/svg%3E%3Ctr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"25a94dd91d982fa75c89944ad8f066f7961c590b":[async_test('html5lib_tests10.html 25a94dd91d982fa75c89944ad8f066f7961c590b'), "%3Cmath%3E%3Cmi%3E%3Cmglyph%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mglyph%3E"],"575adfd2e679ad44140d8afd131fee2e5e819fc0":[async_test('html5lib_tests10.html 575adfd2e679ad44140d8afd131fee2e5e819fc0'), "%3Cmath%3E%3Cmi%3E%3Cmalignmark%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20malignmark%3E"],"2eb6893275ff1de69569440710f5588b8558ca17":[async_test('html5lib_tests10.html 2eb6893275ff1de69569440710f5588b8558ca17'), "%3Cmath%3E%3Cmo%3E%3Cmglyph%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mo%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mglyph%3E"],"4f57b212888bfcc7ac6d8e29ef444cc7ae3ec495":[async_test('html5lib_tests10.html 4f57b212888bfcc7ac6d8e29ef444cc7ae3ec495'), "%3Cmath%3E%3Cmo%3E%3Cmalignmark%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mo%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20malignmark%3E"],"c069a88f172919c862432dbef290a425e71414aa":[async_test('html5lib_tests10.html c069a88f172919c862432dbef290a425e71414aa'), "%3Cmath%3E%3Cmn%3E%3Cmglyph%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mn%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mglyph%3E"],"f944f978d6a10ec9f194f03a5e232f9663d96ec1":[async_test('html5lib_tests10.html f944f978d6a10ec9f194f03a5e232f9663d96ec1'), "%3Cmath%3E%3Cmn%3E%3Cmalignmark%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mn%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20malignmark%3E"],"d5ab79a14609f3ed214baa8e02bedb54ddcf33ec":[async_test('html5lib_tests10.html d5ab79a14609f3ed214baa8e02bedb54ddcf33ec'), "%3Cmath%3E%3Cms%3E%3Cmglyph%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20ms%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mglyph%3E"],"4318bf46f0256426716a911e194f9bdf6185bd7c":[async_test('html5lib_tests10.html 4318bf46f0256426716a911e194f9bdf6185bd7c'), "%3Cmath%3E%3Cms%3E%3Cmalignmark%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20ms%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20malignmark%3E"],"aa5c4b093ee603618493cff853af125b5fb0d401":[async_test('html5lib_tests10.html aa5c4b093ee603618493cff853af125b5fb0d401'), "%3Cmath%3E%3Cmtext%3E%3Cmglyph%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mglyph%3E"],"bfd98674d75ff3291d377f2a7cec3321844a0701":[async_test('html5lib_tests10.html bfd98674d75ff3291d377f2a7cec3321844a0701'), "%3Cmath%3E%3Cmtext%3E%3Cmalignmark%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20malignmark%3E"],"7fc99b729a6d57d28b2ad1edc33bb15167e051bb":[async_test('html5lib_tests10.html 7fc99b729a6d57d28b2ad1edc33bb15167e051bb'), "%3Cmath%3E%3Cannotation-xml%3E%3Csvg%3E%3C/svg%3E%3C/annotation-xml%3E%3Cmi%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E"],"8e87f0d5ca606ca162384d1bf3bb8de62bd6f398":[async_test('html5lib_tests10.html 8e87f0d5ca606ca162384d1bf3bb8de62bd6f398'), "%3Cmath%3E%3Cannotation-xml%3E%3Csvg%3E%3CforeignObject%3E%3Cdiv%3E%3Cmath%3E%3Cmi%3E%3C/mi%3E%3C/math%3E%3Cspan%3E%3C/span%3E%3C/div%3E%3C/foreignObject%3E%3Cpath%3E%3C/path%3E%3C/svg%3E%3C/annotation-xml%3E%3Cmi%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20path%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E"],"6476f80f3d2a04e9c1323ffe78686188c579e9ac":[async_test('html5lib_tests10.html 6476f80f3d2a04e9c1323ffe78686188c579e9ac'), "%3Cmath%3E%3Cannotation-xml%3E%3Csvg%3E%3CforeignObject%3E%3Cmath%3E%3Cmi%3E%3Csvg%3E%3C/svg%3E%3C/mi%3E%3Cmo%3E%3C/mo%3E%3C/math%3E%3Cspan%3E%3C/span%3E%3C/foreignObject%3E%3Cpath%3E%3C/path%3E%3C/svg%3E%3C/annotation-xml%3E%3Cmi%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20path%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests11.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests11.html
new file mode 100644
index 0000000000..f088b691e3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests11.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests11.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['ba7a66dfcf59885c08e8638d15b01df3878531e7','e69c7c08a54ca868ab33b2825d998fa6c3cac90e','0e5897aafe87e460f84576c2d1d983504d12a7db','f71dd198831309fc9ccd66ef91cde63fda9b9bda','a8f7a23601363454b4a13f66aed99ec9708ae87b','f8f7f6c576acc9eb874acb0dce6988f0f7b6fc5f','fb4dc9f70129a8a045fca3a1e3acee052d0990b3','8f77b846acce75edf1988ea655c79f1de4321de9','39c50f080b2b9ae9e7a1070679ab97d7b814f9ec','6d1fa1599f75625fe4d019abb04a064cf6ed39e8','791437ece7ba684e00162d1dd79dfe540e3621a7','af40d26164229c29b9be77ed6dd7dda780cba55c','16e68d18f8f0fb81013fe77a30b7d396c5081e5e',];
+ var tests = {
+ "ba7a66dfcf59885c08e8638d15b01df3878531e7":[async_test('html5lib_tests11.html ba7a66dfcf59885c08e8638d15b01df3878531e7'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%20attributeName%3D%27%27%20attributeType%3D%27%27%20baseFrequency%3D%27%27%20baseProfile%3D%27%27%20calcMode%3D%27%27%20clipPathUnits%3D%27%27%20diffuseConstant%3D%27%27%20edgeMode%3D%27%27%20filterUnits%3D%27%27%20glyphRef%3D%27%27%20gradientTransform%3D%27%27%20gradientUnits%3D%27%27%20kernelMatrix%3D%27%27%20kernelUnitLength%3D%27%27%20keyPoints%3D%27%27%20keySplines%3D%27%27%20keyTimes%3D%27%27%20lengthAdjust%3D%27%27%20limitingConeAngle%3D%27%27%20markerHeight%3D%27%27%20markerUnits%3D%27%27%20markerWidth%3D%27%27%20maskContentUnits%3D%27%27%20maskUnits%3D%27%27%20numOctaves%3D%27%27%20pathLength%3D%27%27%20patternContentUnits%3D%27%27%20patternTransform%3D%27%27%20patternUnits%3D%27%27%20pointsAtX%3D%27%27%20pointsAtY%3D%27%27%20pointsAtZ%3D%27%27%20preserveAlpha%3D%27%27%20preserveAspectRatio%3D%27%27%20primitiveUnits%3D%27%27%20refX%3D%27%27%20refY%3D%27%27%20repeatCount%3D%27%27%20repeatDur%3D%27%27%20requiredExtensions%3D%27%27%20requiredFeatures%3D%27%27%20specularConstant%3D%27%27%20specularExponent%3D%27%27%20spreadMethod%3D%27%27%20startOffset%3D%27%27%20stdDeviation%3D%27%27%20stitchTiles%3D%27%27%20surfaceScale%3D%27%27%20systemLanguage%3D%27%27%20tableValues%3D%27%27%20targetX%3D%27%27%20targetY%3D%27%27%20textLength%3D%27%27%20viewBox%3D%27%27%20viewTarget%3D%27%27%20xChannelSelector%3D%27%27%20yChannelSelector%3D%27%27%20zoomAndPan%3D%27%27%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20attributeName%3D%22%22%0A%7C%20%20%20%20%20%20%20attributeType%3D%22%22%0A%7C%20%20%20%20%20%20%20baseFrequency%3D%22%22%0A%7C%20%20%20%20%20%20%20baseProfile%3D%22%22%0A%7C%20%20%20%20%20%20%20calcMode%3D%22%22%0A%7C%20%20%20%20%20%20%20clipPathUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20diffuseConstant%3D%22%22%0A%7C%20%20%20%20%20%20%20edgeMode%3D%22%22%0A%7C%20%20%20%20%20%20%20filterUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20glyphRef%3D%22%22%0A%7C%20%20%20%20%20%20%20gradientTransform%3D%22%22%0A%7C%20%20%20%20%20%20%20gradientUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelMatrix%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelUnitLength%3D%22%22%0A%7C%20%20%20%20%20%20%20keyPoints%3D%22%22%0A%7C%20%20%20%20%20%20%20keySplines%3D%22%22%0A%7C%20%20%20%20%20%20%20keyTimes%3D%22%22%0A%7C%20%20%20%20%20%20%20lengthAdjust%3D%22%22%0A%7C%20%20%20%20%20%20%20limitingConeAngle%3D%22%22%0A%7C%20%20%20%20%20%20%20markerHeight%3D%22%22%0A%7C%20%20%20%20%20%20%20markerUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20markerWidth%3D%22%22%0A%7C%20%20%20%20%20%20%20maskContentUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20maskUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20numOctaves%3D%22%22%0A%7C%20%20%20%20%20%20%20pathLength%3D%22%22%0A%7C%20%20%20%20%20%20%20patternContentUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20patternTransform%3D%22%22%0A%7C%20%20%20%20%20%20%20patternUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtX%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtY%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtZ%3D%22%22%0A%7C%20%20%20%20%20%20%20preserveAlpha%3D%22%22%0A%7C%20%20%20%20%20%20%20preserveAspectRatio%3D%22%22%0A%7C%20%20%20%20%20%20%20primitiveUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20refX%3D%22%22%0A%7C%20%20%20%20%20%20%20refY%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatCount%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatDur%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredExtensions%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredFeatures%3D%22%22%0A%7C%20%20%20%20%20%20%20specularConstant%3D%22%22%0A%7C%20%20%20%20%20%20%20specularExponent%3D%22%22%0A%7C%20%20%20%20%20%20%20spreadMethod%3D%22%22%0A%7C%20%20%20%20%20%20%20startOffset%3D%22%22%0A%7C%20%20%20%20%20%20%20stdDeviation%3D%22%22%0A%7C%20%20%20%20%20%20%20stitchTiles%3D%22%22%0A%7C%20%20%20%20%20%20%20surfaceScale%3D%22%22%0A%7C%20%20%20%20%20%20%20systemLanguage%3D%22%22%0A%7C%20%20%20%20%20%20%20tableValues%3D%22%22%0A%7C%20%20%20%20%20%20%20targetX%3D%22%22%0A%7C%20%20%20%20%20%20%20targetY%3D%22%22%0A%7C%20%20%20%20%20%20%20textLength%3D%22%22%0A%7C%20%20%20%20%20%20%20viewBox%3D%22%22%0A%7C%20%20%20%20%20%20%20viewTarget%3D%22%22%0A%7C%20%20%20%20%20%20%20xChannelSelector%3D%22%22%0A%7C%20%20%20%20%20%20%20yChannelSelector%3D%22%22%0A%7C%20%20%20%20%20%20%20zoomAndPan%3D%22%22"],"e69c7c08a54ca868ab33b2825d998fa6c3cac90e":[async_test('html5lib_tests11.html e69c7c08a54ca868ab33b2825d998fa6c3cac90e'), "%3C%21DOCTYPE%20html%3E%3CBODY%3E%3CSVG%20ATTRIBUTENAME%3D%27%27%20ATTRIBUTETYPE%3D%27%27%20BASEFREQUENCY%3D%27%27%20BASEPROFILE%3D%27%27%20CALCMODE%3D%27%27%20CLIPPATHUNITS%3D%27%27%20DIFFUSECONSTANT%3D%27%27%20EDGEMODE%3D%27%27%20FILTERUNITS%3D%27%27%20GLYPHREF%3D%27%27%20GRADIENTTRANSFORM%3D%27%27%20GRADIENTUNITS%3D%27%27%20KERNELMATRIX%3D%27%27%20KERNELUNITLENGTH%3D%27%27%20KEYPOINTS%3D%27%27%20KEYSPLINES%3D%27%27%20KEYTIMES%3D%27%27%20LENGTHADJUST%3D%27%27%20LIMITINGCONEANGLE%3D%27%27%20MARKERHEIGHT%3D%27%27%20MARKERUNITS%3D%27%27%20MARKERWIDTH%3D%27%27%20MASKCONTENTUNITS%3D%27%27%20MASKUNITS%3D%27%27%20NUMOCTAVES%3D%27%27%20PATHLENGTH%3D%27%27%20PATTERNCONTENTUNITS%3D%27%27%20PATTERNTRANSFORM%3D%27%27%20PATTERNUNITS%3D%27%27%20POINTSATX%3D%27%27%20POINTSATY%3D%27%27%20POINTSATZ%3D%27%27%20PRESERVEALPHA%3D%27%27%20PRESERVEASPECTRATIO%3D%27%27%20PRIMITIVEUNITS%3D%27%27%20REFX%3D%27%27%20REFY%3D%27%27%20REPEATCOUNT%3D%27%27%20REPEATDUR%3D%27%27%20REQUIREDEXTENSIONS%3D%27%27%20REQUIREDFEATURES%3D%27%27%20SPECULARCONSTANT%3D%27%27%20SPECULAREXPONENT%3D%27%27%20SPREADMETHOD%3D%27%27%20STARTOFFSET%3D%27%27%20STDDEVIATION%3D%27%27%20STITCHTILES%3D%27%27%20SURFACESCALE%3D%27%27%20SYSTEMLANGUAGE%3D%27%27%20TABLEVALUES%3D%27%27%20TARGETX%3D%27%27%20TARGETY%3D%27%27%20TEXTLENGTH%3D%27%27%20VIEWBOX%3D%27%27%20VIEWTARGET%3D%27%27%20XCHANNELSELECTOR%3D%27%27%20YCHANNELSELECTOR%3D%27%27%20ZOOMANDPAN%3D%27%27%3E%3C/SVG%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20attributeName%3D%22%22%0A%7C%20%20%20%20%20%20%20attributeType%3D%22%22%0A%7C%20%20%20%20%20%20%20baseFrequency%3D%22%22%0A%7C%20%20%20%20%20%20%20baseProfile%3D%22%22%0A%7C%20%20%20%20%20%20%20calcMode%3D%22%22%0A%7C%20%20%20%20%20%20%20clipPathUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20diffuseConstant%3D%22%22%0A%7C%20%20%20%20%20%20%20edgeMode%3D%22%22%0A%7C%20%20%20%20%20%20%20filterUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20glyphRef%3D%22%22%0A%7C%20%20%20%20%20%20%20gradientTransform%3D%22%22%0A%7C%20%20%20%20%20%20%20gradientUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelMatrix%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelUnitLength%3D%22%22%0A%7C%20%20%20%20%20%20%20keyPoints%3D%22%22%0A%7C%20%20%20%20%20%20%20keySplines%3D%22%22%0A%7C%20%20%20%20%20%20%20keyTimes%3D%22%22%0A%7C%20%20%20%20%20%20%20lengthAdjust%3D%22%22%0A%7C%20%20%20%20%20%20%20limitingConeAngle%3D%22%22%0A%7C%20%20%20%20%20%20%20markerHeight%3D%22%22%0A%7C%20%20%20%20%20%20%20markerUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20markerWidth%3D%22%22%0A%7C%20%20%20%20%20%20%20maskContentUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20maskUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20numOctaves%3D%22%22%0A%7C%20%20%20%20%20%20%20pathLength%3D%22%22%0A%7C%20%20%20%20%20%20%20patternContentUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20patternTransform%3D%22%22%0A%7C%20%20%20%20%20%20%20patternUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtX%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtY%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtZ%3D%22%22%0A%7C%20%20%20%20%20%20%20preserveAlpha%3D%22%22%0A%7C%20%20%20%20%20%20%20preserveAspectRatio%3D%22%22%0A%7C%20%20%20%20%20%20%20primitiveUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20refX%3D%22%22%0A%7C%20%20%20%20%20%20%20refY%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatCount%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatDur%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredExtensions%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredFeatures%3D%22%22%0A%7C%20%20%20%20%20%20%20specularConstant%3D%22%22%0A%7C%20%20%20%20%20%20%20specularExponent%3D%22%22%0A%7C%20%20%20%20%20%20%20spreadMethod%3D%22%22%0A%7C%20%20%20%20%20%20%20startOffset%3D%22%22%0A%7C%20%20%20%20%20%20%20stdDeviation%3D%22%22%0A%7C%20%20%20%20%20%20%20stitchTiles%3D%22%22%0A%7C%20%20%20%20%20%20%20surfaceScale%3D%22%22%0A%7C%20%20%20%20%20%20%20systemLanguage%3D%22%22%0A%7C%20%20%20%20%20%20%20tableValues%3D%22%22%0A%7C%20%20%20%20%20%20%20targetX%3D%22%22%0A%7C%20%20%20%20%20%20%20targetY%3D%22%22%0A%7C%20%20%20%20%20%20%20textLength%3D%22%22%0A%7C%20%20%20%20%20%20%20viewBox%3D%22%22%0A%7C%20%20%20%20%20%20%20viewTarget%3D%22%22%0A%7C%20%20%20%20%20%20%20xChannelSelector%3D%22%22%0A%7C%20%20%20%20%20%20%20yChannelSelector%3D%22%22%0A%7C%20%20%20%20%20%20%20zoomAndPan%3D%22%22"],"0e5897aafe87e460f84576c2d1d983504d12a7db":[async_test('html5lib_tests11.html 0e5897aafe87e460f84576c2d1d983504d12a7db'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%20attributename%3D%27%27%20attributetype%3D%27%27%20basefrequency%3D%27%27%20baseprofile%3D%27%27%20calcmode%3D%27%27%20clippathunits%3D%27%27%20diffuseconstant%3D%27%27%20edgemode%3D%27%27%20filterunits%3D%27%27%20filterres%3D%27%27%20glyphref%3D%27%27%20gradienttransform%3D%27%27%20gradientunits%3D%27%27%20kernelmatrix%3D%27%27%20kernelunitlength%3D%27%27%20keypoints%3D%27%27%20keysplines%3D%27%27%20keytimes%3D%27%27%20lengthadjust%3D%27%27%20limitingconeangle%3D%27%27%20markerheight%3D%27%27%20markerunits%3D%27%27%20markerwidth%3D%27%27%20maskcontentunits%3D%27%27%20maskunits%3D%27%27%20numoctaves%3D%27%27%20pathlength%3D%27%27%20patterncontentunits%3D%27%27%20patterntransform%3D%27%27%20patternunits%3D%27%27%20pointsatx%3D%27%27%20pointsaty%3D%27%27%20pointsatz%3D%27%27%20preservealpha%3D%27%27%20preserveaspectratio%3D%27%27%20primitiveunits%3D%27%27%20refx%3D%27%27%20refy%3D%27%27%20repeatcount%3D%27%27%20repeatdur%3D%27%27%20requiredextensions%3D%27%27%20requiredfeatures%3D%27%27%20specularconstant%3D%27%27%20specularexponent%3D%27%27%20spreadmethod%3D%27%27%20startoffset%3D%27%27%20stddeviation%3D%27%27%20stitchtiles%3D%27%27%20surfacescale%3D%27%27%20systemlanguage%3D%27%27%20tablevalues%3D%27%27%20targetx%3D%27%27%20targety%3D%27%27%20textlength%3D%27%27%20viewbox%3D%27%27%20viewtarget%3D%27%27%20xchannelselector%3D%27%27%20ychannelselector%3D%27%27%20zoomandpan%3D%27%27%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20attributeName%3D%22%22%0A%7C%20%20%20%20%20%20%20attributeType%3D%22%22%0A%7C%20%20%20%20%20%20%20baseFrequency%3D%22%22%0A%7C%20%20%20%20%20%20%20baseProfile%3D%22%22%0A%7C%20%20%20%20%20%20%20calcMode%3D%22%22%0A%7C%20%20%20%20%20%20%20clipPathUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20diffuseConstant%3D%22%22%0A%7C%20%20%20%20%20%20%20edgeMode%3D%22%22%0A%7C%20%20%20%20%20%20%20filterUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20filterres%3D%22%22%0A%7C%20%20%20%20%20%20%20glyphRef%3D%22%22%0A%7C%20%20%20%20%20%20%20gradientTransform%3D%22%22%0A%7C%20%20%20%20%20%20%20gradientUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelMatrix%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelUnitLength%3D%22%22%0A%7C%20%20%20%20%20%20%20keyPoints%3D%22%22%0A%7C%20%20%20%20%20%20%20keySplines%3D%22%22%0A%7C%20%20%20%20%20%20%20keyTimes%3D%22%22%0A%7C%20%20%20%20%20%20%20lengthAdjust%3D%22%22%0A%7C%20%20%20%20%20%20%20limitingConeAngle%3D%22%22%0A%7C%20%20%20%20%20%20%20markerHeight%3D%22%22%0A%7C%20%20%20%20%20%20%20markerUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20markerWidth%3D%22%22%0A%7C%20%20%20%20%20%20%20maskContentUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20maskUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20numOctaves%3D%22%22%0A%7C%20%20%20%20%20%20%20pathLength%3D%22%22%0A%7C%20%20%20%20%20%20%20patternContentUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20patternTransform%3D%22%22%0A%7C%20%20%20%20%20%20%20patternUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtX%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtY%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsAtZ%3D%22%22%0A%7C%20%20%20%20%20%20%20preserveAlpha%3D%22%22%0A%7C%20%20%20%20%20%20%20preserveAspectRatio%3D%22%22%0A%7C%20%20%20%20%20%20%20primitiveUnits%3D%22%22%0A%7C%20%20%20%20%20%20%20refX%3D%22%22%0A%7C%20%20%20%20%20%20%20refY%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatCount%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatDur%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredExtensions%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredFeatures%3D%22%22%0A%7C%20%20%20%20%20%20%20specularConstant%3D%22%22%0A%7C%20%20%20%20%20%20%20specularExponent%3D%22%22%0A%7C%20%20%20%20%20%20%20spreadMethod%3D%22%22%0A%7C%20%20%20%20%20%20%20startOffset%3D%22%22%0A%7C%20%20%20%20%20%20%20stdDeviation%3D%22%22%0A%7C%20%20%20%20%20%20%20stitchTiles%3D%22%22%0A%7C%20%20%20%20%20%20%20surfaceScale%3D%22%22%0A%7C%20%20%20%20%20%20%20systemLanguage%3D%22%22%0A%7C%20%20%20%20%20%20%20tableValues%3D%22%22%0A%7C%20%20%20%20%20%20%20targetX%3D%22%22%0A%7C%20%20%20%20%20%20%20targetY%3D%22%22%0A%7C%20%20%20%20%20%20%20textLength%3D%22%22%0A%7C%20%20%20%20%20%20%20viewBox%3D%22%22%0A%7C%20%20%20%20%20%20%20viewTarget%3D%22%22%0A%7C%20%20%20%20%20%20%20xChannelSelector%3D%22%22%0A%7C%20%20%20%20%20%20%20yChannelSelector%3D%22%22%0A%7C%20%20%20%20%20%20%20zoomAndPan%3D%22%22"],"f71dd198831309fc9ccd66ef91cde63fda9b9bda":[async_test('html5lib_tests11.html f71dd198831309fc9ccd66ef91cde63fda9b9bda'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmath%20attributeName%3D%27%27%20attributeType%3D%27%27%20baseFrequency%3D%27%27%20baseProfile%3D%27%27%20calcMode%3D%27%27%20clipPathUnits%3D%27%27%20diffuseConstant%3D%27%27%20edgeMode%3D%27%27%20filterUnits%3D%27%27%20glyphRef%3D%27%27%20gradientTransform%3D%27%27%20gradientUnits%3D%27%27%20kernelMatrix%3D%27%27%20kernelUnitLength%3D%27%27%20keyPoints%3D%27%27%20keySplines%3D%27%27%20keyTimes%3D%27%27%20lengthAdjust%3D%27%27%20limitingConeAngle%3D%27%27%20markerHeight%3D%27%27%20markerUnits%3D%27%27%20markerWidth%3D%27%27%20maskContentUnits%3D%27%27%20maskUnits%3D%27%27%20numOctaves%3D%27%27%20pathLength%3D%27%27%20patternContentUnits%3D%27%27%20patternTransform%3D%27%27%20patternUnits%3D%27%27%20pointsAtX%3D%27%27%20pointsAtY%3D%27%27%20pointsAtZ%3D%27%27%20preserveAlpha%3D%27%27%20preserveAspectRatio%3D%27%27%20primitiveUnits%3D%27%27%20refX%3D%27%27%20refY%3D%27%27%20repeatCount%3D%27%27%20repeatDur%3D%27%27%20requiredExtensions%3D%27%27%20requiredFeatures%3D%27%27%20specularConstant%3D%27%27%20specularExponent%3D%27%27%20spreadMethod%3D%27%27%20startOffset%3D%27%27%20stdDeviation%3D%27%27%20stitchTiles%3D%27%27%20surfaceScale%3D%27%27%20systemLanguage%3D%27%27%20tableValues%3D%27%27%20targetX%3D%27%27%20targetY%3D%27%27%20textLength%3D%27%27%20viewBox%3D%27%27%20viewTarget%3D%27%27%20xChannelSelector%3D%27%27%20yChannelSelector%3D%27%27%20zoomAndPan%3D%27%27%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20attributename%3D%22%22%0A%7C%20%20%20%20%20%20%20attributetype%3D%22%22%0A%7C%20%20%20%20%20%20%20basefrequency%3D%22%22%0A%7C%20%20%20%20%20%20%20baseprofile%3D%22%22%0A%7C%20%20%20%20%20%20%20calcmode%3D%22%22%0A%7C%20%20%20%20%20%20%20clippathunits%3D%22%22%0A%7C%20%20%20%20%20%20%20diffuseconstant%3D%22%22%0A%7C%20%20%20%20%20%20%20edgemode%3D%22%22%0A%7C%20%20%20%20%20%20%20filterunits%3D%22%22%0A%7C%20%20%20%20%20%20%20glyphref%3D%22%22%0A%7C%20%20%20%20%20%20%20gradienttransform%3D%22%22%0A%7C%20%20%20%20%20%20%20gradientunits%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelmatrix%3D%22%22%0A%7C%20%20%20%20%20%20%20kernelunitlength%3D%22%22%0A%7C%20%20%20%20%20%20%20keypoints%3D%22%22%0A%7C%20%20%20%20%20%20%20keysplines%3D%22%22%0A%7C%20%20%20%20%20%20%20keytimes%3D%22%22%0A%7C%20%20%20%20%20%20%20lengthadjust%3D%22%22%0A%7C%20%20%20%20%20%20%20limitingconeangle%3D%22%22%0A%7C%20%20%20%20%20%20%20markerheight%3D%22%22%0A%7C%20%20%20%20%20%20%20markerunits%3D%22%22%0A%7C%20%20%20%20%20%20%20markerwidth%3D%22%22%0A%7C%20%20%20%20%20%20%20maskcontentunits%3D%22%22%0A%7C%20%20%20%20%20%20%20maskunits%3D%22%22%0A%7C%20%20%20%20%20%20%20numoctaves%3D%22%22%0A%7C%20%20%20%20%20%20%20pathlength%3D%22%22%0A%7C%20%20%20%20%20%20%20patterncontentunits%3D%22%22%0A%7C%20%20%20%20%20%20%20patterntransform%3D%22%22%0A%7C%20%20%20%20%20%20%20patternunits%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsatx%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsaty%3D%22%22%0A%7C%20%20%20%20%20%20%20pointsatz%3D%22%22%0A%7C%20%20%20%20%20%20%20preservealpha%3D%22%22%0A%7C%20%20%20%20%20%20%20preserveaspectratio%3D%22%22%0A%7C%20%20%20%20%20%20%20primitiveunits%3D%22%22%0A%7C%20%20%20%20%20%20%20refx%3D%22%22%0A%7C%20%20%20%20%20%20%20refy%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatcount%3D%22%22%0A%7C%20%20%20%20%20%20%20repeatdur%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredextensions%3D%22%22%0A%7C%20%20%20%20%20%20%20requiredfeatures%3D%22%22%0A%7C%20%20%20%20%20%20%20specularconstant%3D%22%22%0A%7C%20%20%20%20%20%20%20specularexponent%3D%22%22%0A%7C%20%20%20%20%20%20%20spreadmethod%3D%22%22%0A%7C%20%20%20%20%20%20%20startoffset%3D%22%22%0A%7C%20%20%20%20%20%20%20stddeviation%3D%22%22%0A%7C%20%20%20%20%20%20%20stitchtiles%3D%22%22%0A%7C%20%20%20%20%20%20%20surfacescale%3D%22%22%0A%7C%20%20%20%20%20%20%20systemlanguage%3D%22%22%0A%7C%20%20%20%20%20%20%20tablevalues%3D%22%22%0A%7C%20%20%20%20%20%20%20targetx%3D%22%22%0A%7C%20%20%20%20%20%20%20targety%3D%22%22%0A%7C%20%20%20%20%20%20%20textlength%3D%22%22%0A%7C%20%20%20%20%20%20%20viewbox%3D%22%22%0A%7C%20%20%20%20%20%20%20viewtarget%3D%22%22%0A%7C%20%20%20%20%20%20%20xchannelselector%3D%22%22%0A%7C%20%20%20%20%20%20%20ychannelselector%3D%22%22%0A%7C%20%20%20%20%20%20%20zoomandpan%3D%22%22"],"a8f7a23601363454b4a13f66aed99ec9708ae87b":[async_test('html5lib_tests11.html a8f7a23601363454b4a13f66aed99ec9708ae87b'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%20contentScriptType%3D%27%27%20contentStyleType%3D%27%27%20externalResourcesRequired%3D%27%27%20filterRes%3D%27%27%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20contentscripttype%3D%22%22%0A%7C%20%20%20%20%20%20%20contentstyletype%3D%22%22%0A%7C%20%20%20%20%20%20%20externalresourcesrequired%3D%22%22%0A%7C%20%20%20%20%20%20%20filterres%3D%22%22"],"f8f7f6c576acc9eb874acb0dce6988f0f7b6fc5f":[async_test('html5lib_tests11.html f8f7f6c576acc9eb874acb0dce6988f0f7b6fc5f'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%20CONTENTSCRIPTTYPE%3D%27%27%20CONTENTSTYLETYPE%3D%27%27%20EXTERNALRESOURCESREQUIRED%3D%27%27%20FILTERRES%3D%27%27%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20contentscripttype%3D%22%22%0A%7C%20%20%20%20%20%20%20contentstyletype%3D%22%22%0A%7C%20%20%20%20%20%20%20externalresourcesrequired%3D%22%22%0A%7C%20%20%20%20%20%20%20filterres%3D%22%22"],"fb4dc9f70129a8a045fca3a1e3acee052d0990b3":[async_test('html5lib_tests11.html fb4dc9f70129a8a045fca3a1e3acee052d0990b3'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%20contentscripttype%3D%27%27%20contentstyletype%3D%27%27%20externalresourcesrequired%3D%27%27%20filterres%3D%27%27%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20contentscripttype%3D%22%22%0A%7C%20%20%20%20%20%20%20contentstyletype%3D%22%22%0A%7C%20%20%20%20%20%20%20externalresourcesrequired%3D%22%22%0A%7C%20%20%20%20%20%20%20filterres%3D%22%22"],"8f77b846acce75edf1988ea655c79f1de4321de9":[async_test('html5lib_tests11.html 8f77b846acce75edf1988ea655c79f1de4321de9'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmath%20contentScriptType%3D%27%27%20contentStyleType%3D%27%27%20externalResourcesRequired%3D%27%27%20filterRes%3D%27%27%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20contentscripttype%3D%22%22%0A%7C%20%20%20%20%20%20%20contentstyletype%3D%22%22%0A%7C%20%20%20%20%20%20%20externalresourcesrequired%3D%22%22%0A%7C%20%20%20%20%20%20%20filterres%3D%22%22"],"39c50f080b2b9ae9e7a1070679ab97d7b814f9ec":[async_test('html5lib_tests11.html 39c50f080b2b9ae9e7a1070679ab97d7b814f9ec'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%3E%3CaltGlyph%20/%3E%3CaltGlyphDef%20/%3E%3CaltGlyphItem%20/%3E%3CanimateColor%20/%3E%3CanimateMotion%20/%3E%3CanimateTransform%20/%3E%3CclipPath%20/%3E%3CfeBlend%20/%3E%3CfeColorMatrix%20/%3E%3CfeComponentTransfer%20/%3E%3CfeComposite%20/%3E%3CfeConvolveMatrix%20/%3E%3CfeDiffuseLighting%20/%3E%3CfeDisplacementMap%20/%3E%3CfeDistantLight%20/%3E%3CfeFlood%20/%3E%3CfeFuncA%20/%3E%3CfeFuncB%20/%3E%3CfeFuncG%20/%3E%3CfeFuncR%20/%3E%3CfeGaussianBlur%20/%3E%3CfeImage%20/%3E%3CfeMerge%20/%3E%3CfeMergeNode%20/%3E%3CfeMorphology%20/%3E%3CfeOffset%20/%3E%3CfePointLight%20/%3E%3CfeSpecularLighting%20/%3E%3CfeSpotLight%20/%3E%3CfeTile%20/%3E%3CfeTurbulence%20/%3E%3CforeignObject%20/%3E%3CglyphRef%20/%3E%3ClinearGradient%20/%3E%3CradialGradient%20/%3E%3CtextPath%20/%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyph%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyphDef%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyphItem%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateColor%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateMotion%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateTransform%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20clipPath%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feBlend%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feColorMatrix%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feComponentTransfer%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feComposite%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feConvolveMatrix%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDiffuseLighting%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDisplacementMap%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDistantLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFlood%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncA%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncB%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncG%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncR%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feGaussianBlur%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feImage%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMerge%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMergeNode%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMorphology%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feOffset%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20fePointLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feSpecularLighting%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feSpotLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feTile%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feTurbulence%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20glyphRef%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20linearGradient%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20radialGradient%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20textPath%3E"],"6d1fa1599f75625fe4d019abb04a064cf6ed39e8":[async_test('html5lib_tests11.html 6d1fa1599f75625fe4d019abb04a064cf6ed39e8'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%3E%3Caltglyph%20/%3E%3Caltglyphdef%20/%3E%3Caltglyphitem%20/%3E%3Canimatecolor%20/%3E%3Canimatemotion%20/%3E%3Canimatetransform%20/%3E%3Cclippath%20/%3E%3Cfeblend%20/%3E%3Cfecolormatrix%20/%3E%3Cfecomponenttransfer%20/%3E%3Cfecomposite%20/%3E%3Cfeconvolvematrix%20/%3E%3Cfediffuselighting%20/%3E%3Cfedisplacementmap%20/%3E%3Cfedistantlight%20/%3E%3Cfeflood%20/%3E%3Cfefunca%20/%3E%3Cfefuncb%20/%3E%3Cfefuncg%20/%3E%3Cfefuncr%20/%3E%3Cfegaussianblur%20/%3E%3Cfeimage%20/%3E%3Cfemerge%20/%3E%3Cfemergenode%20/%3E%3Cfemorphology%20/%3E%3Cfeoffset%20/%3E%3Cfepointlight%20/%3E%3Cfespecularlighting%20/%3E%3Cfespotlight%20/%3E%3Cfetile%20/%3E%3Cfeturbulence%20/%3E%3Cforeignobject%20/%3E%3Cglyphref%20/%3E%3Clineargradient%20/%3E%3Cradialgradient%20/%3E%3Ctextpath%20/%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyph%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyphDef%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyphItem%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateColor%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateMotion%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateTransform%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20clipPath%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feBlend%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feColorMatrix%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feComponentTransfer%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feComposite%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feConvolveMatrix%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDiffuseLighting%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDisplacementMap%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDistantLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFlood%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncA%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncB%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncG%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncR%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feGaussianBlur%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feImage%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMerge%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMergeNode%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMorphology%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feOffset%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20fePointLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feSpecularLighting%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feSpotLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feTile%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feTurbulence%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20glyphRef%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20linearGradient%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20radialGradient%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20textPath%3E"],"791437ece7ba684e00162d1dd79dfe540e3621a7":[async_test('html5lib_tests11.html 791437ece7ba684e00162d1dd79dfe540e3621a7'), "%3C%21DOCTYPE%20html%3E%3CBODY%3E%3CSVG%3E%3CALTGLYPH%20/%3E%3CALTGLYPHDEF%20/%3E%3CALTGLYPHITEM%20/%3E%3CANIMATECOLOR%20/%3E%3CANIMATEMOTION%20/%3E%3CANIMATETRANSFORM%20/%3E%3CCLIPPATH%20/%3E%3CFEBLEND%20/%3E%3CFECOLORMATRIX%20/%3E%3CFECOMPONENTTRANSFER%20/%3E%3CFECOMPOSITE%20/%3E%3CFECONVOLVEMATRIX%20/%3E%3CFEDIFFUSELIGHTING%20/%3E%3CFEDISPLACEMENTMAP%20/%3E%3CFEDISTANTLIGHT%20/%3E%3CFEFLOOD%20/%3E%3CFEFUNCA%20/%3E%3CFEFUNCB%20/%3E%3CFEFUNCG%20/%3E%3CFEFUNCR%20/%3E%3CFEGAUSSIANBLUR%20/%3E%3CFEIMAGE%20/%3E%3CFEMERGE%20/%3E%3CFEMERGENODE%20/%3E%3CFEMORPHOLOGY%20/%3E%3CFEOFFSET%20/%3E%3CFEPOINTLIGHT%20/%3E%3CFESPECULARLIGHTING%20/%3E%3CFESPOTLIGHT%20/%3E%3CFETILE%20/%3E%3CFETURBULENCE%20/%3E%3CFOREIGNOBJECT%20/%3E%3CGLYPHREF%20/%3E%3CLINEARGRADIENT%20/%3E%3CRADIALGRADIENT%20/%3E%3CTEXTPATH%20/%3E%3C/SVG%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyph%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyphDef%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20altGlyphItem%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateColor%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateMotion%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20animateTransform%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20clipPath%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feBlend%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feColorMatrix%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feComponentTransfer%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feComposite%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feConvolveMatrix%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDiffuseLighting%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDisplacementMap%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feDistantLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFlood%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncA%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncB%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncG%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feFuncR%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feGaussianBlur%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feImage%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMerge%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMergeNode%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feMorphology%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feOffset%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20fePointLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feSpecularLighting%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feSpotLight%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feTile%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20feTurbulence%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20glyphRef%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20linearGradient%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20radialGradient%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20textPath%3E"],"af40d26164229c29b9be77ed6dd7dda780cba55c":[async_test('html5lib_tests11.html af40d26164229c29b9be77ed6dd7dda780cba55c'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmath%3E%3CaltGlyph%20/%3E%3CaltGlyphDef%20/%3E%3CaltGlyphItem%20/%3E%3CanimateColor%20/%3E%3CanimateMotion%20/%3E%3CanimateTransform%20/%3E%3CclipPath%20/%3E%3CfeBlend%20/%3E%3CfeColorMatrix%20/%3E%3CfeComponentTransfer%20/%3E%3CfeComposite%20/%3E%3CfeConvolveMatrix%20/%3E%3CfeDiffuseLighting%20/%3E%3CfeDisplacementMap%20/%3E%3CfeDistantLight%20/%3E%3CfeFlood%20/%3E%3CfeFuncA%20/%3E%3CfeFuncB%20/%3E%3CfeFuncG%20/%3E%3CfeFuncR%20/%3E%3CfeGaussianBlur%20/%3E%3CfeImage%20/%3E%3CfeMerge%20/%3E%3CfeMergeNode%20/%3E%3CfeMorphology%20/%3E%3CfeOffset%20/%3E%3CfePointLight%20/%3E%3CfeSpecularLighting%20/%3E%3CfeSpotLight%20/%3E%3CfeTile%20/%3E%3CfeTurbulence%20/%3E%3CforeignObject%20/%3E%3CglyphRef%20/%3E%3ClinearGradient%20/%3E%3CradialGradient%20/%3E%3CtextPath%20/%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20altglyph%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20altglyphdef%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20altglyphitem%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20animatecolor%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20animatemotion%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20animatetransform%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20clippath%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20feblend%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fecolormatrix%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fecomponenttransfer%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fecomposite%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20feconvolvematrix%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fediffuselighting%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fedisplacementmap%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fedistantlight%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20feflood%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fefunca%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fefuncb%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fefuncg%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fefuncr%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fegaussianblur%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20feimage%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20femerge%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20femergenode%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20femorphology%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20feoffset%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fepointlight%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fespecularlighting%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fespotlight%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20fetile%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20feturbulence%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20foreignobject%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20glyphref%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20lineargradient%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20radialgradient%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20textpath%3E"],"16e68d18f8f0fb81013fe77a30b7d396c5081e5e":[async_test('html5lib_tests11.html 16e68d18f8f0fb81013fe77a30b7d396c5081e5e'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csvg%3E%3CsolidColor%20/%3E%3C/svg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20solidcolor%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests12.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests12.html
new file mode 100644
index 0000000000..4c1b388f04
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests12.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests12.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['95751b82f57d4feaaf06d208d57b7f6cc4d5fef5','411c792cef85cbb029d5c91f4a2142751a319bc2',];
+ var tests = {
+ "95751b82f57d4feaaf06d208d57b7f6cc4d5fef5":[async_test('html5lib_tests12.html 95751b82f57d4feaaf06d208d57b7f6cc4d5fef5'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cp%3Efoo%3Cmath%3E%3Cmtext%3E%3Ci%3Ebaz%3C/i%3E%3C/mtext%3E%3Cannotation-xml%3E%3Csvg%3E%3Cdesc%3E%3Cb%3Eeggs%3C/b%3E%3C/desc%3E%3Cg%3E%3CforeignObject%3E%3CP%3Espam%3CTABLE%3E%3Ctr%3E%3Ctd%3E%3Cimg%3E%3C/td%3E%3C/table%3E%3C/foreignObject%3E%3C/g%3E%3Cg%3Equux%3C/g%3E%3C/svg%3E%3C/annotation-xml%3E%3C/math%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22eggs%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22spam%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cimg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22quux%22%0A%7C%20%20%20%20%20%20%20%22bar%22"],"411c792cef85cbb029d5c91f4a2142751a319bc2":[async_test('html5lib_tests12.html 411c792cef85cbb029d5c91f4a2142751a319bc2'), "%3C%21DOCTYPE%20html%3E%3Cbody%3Efoo%3Cmath%3E%3Cmtext%3E%3Ci%3Ebaz%3C/i%3E%3C/mtext%3E%3Cannotation-xml%3E%3Csvg%3E%3Cdesc%3E%3Cb%3Eeggs%3C/b%3E%3C/desc%3E%3Cg%3E%3CforeignObject%3E%3CP%3Espam%3CTABLE%3E%3Ctr%3E%3Ctd%3E%3Cimg%3E%3C/td%3E%3C/table%3E%3C/foreignObject%3E%3C/g%3E%3Cg%3Equux%3C/g%3E%3C/svg%3E%3C/annotation-xml%3E%3C/math%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22eggs%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22spam%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cimg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20g%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22quux%22%0A%7C%20%20%20%20%20%22bar%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests14.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests14.html
new file mode 100644
index 0000000000..b1a4323c2d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests14.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests14.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['d0faa36cd34bbc8e41bacd676e995aef68cb8ef7','9d97df65d72e97363840684da4e164b50c4bf1cb','c5de9372cd188bc22d40d4ad08eb6f787ab521ea','d16e1c0655b2086c1bd995cf6f1c5c7106e48ef0','383a71bb62eacf93dcb2399c7dd7419d92a91899','ee5e2e4a3346d225907f27c1f12b3cb2e77c32c4','cd557ae48cd48356c367e470927d0fc108724409',];
+ var tests = {
+ "d0faa36cd34bbc8e41bacd676e995aef68cb8ef7":[async_test('html5lib_tests14.html d0faa36cd34bbc8e41bacd676e995aef68cb8ef7'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Cbody%3E%3Cxyz%3Aabc%3E%3C/xyz%3Aabc%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cxyz%3Aabc%3E"],"9d97df65d72e97363840684da4e164b50c4bf1cb":[async_test('html5lib_tests14.html 9d97df65d72e97363840684da4e164b50c4bf1cb'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Cbody%3E%3Cxyz%3Aabc%3E%3C/xyz%3Aabc%3E%3Cspan%3E%3C/span%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cxyz%3Aabc%3E%0A%7C%20%20%20%20%20%3Cspan%3E"],"c5de9372cd188bc22d40d4ad08eb6f787ab521ea":[async_test('html5lib_tests14.html c5de9372cd188bc22d40d4ad08eb6f787ab521ea'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chtml%20abc%3Adef%3Dgh%3E%3Cxyz%3Aabc%3E%3C/xyz%3Aabc%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20abc%3Adef%3D%22gh%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cxyz%3Aabc%3E"],"d16e1c0655b2086c1bd995cf6f1c5c7106e48ef0":[async_test('html5lib_tests14.html d16e1c0655b2086c1bd995cf6f1c5c7106e48ef0'), "%3C%21DOCTYPE%20html%3E%3Chtml%20xml%3Alang%3Dbar%3E%3Chtml%20xml%3Alang%3Dfoo%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20xml%3Alang%3D%22bar%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"383a71bb62eacf93dcb2399c7dd7419d92a91899":[async_test('html5lib_tests14.html 383a71bb62eacf93dcb2399c7dd7419d92a91899'), "%3C%21DOCTYPE%20html%3E%3Chtml%20123%3D456%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20123%3D%22456%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"ee5e2e4a3346d225907f27c1f12b3cb2e77c32c4":[async_test('html5lib_tests14.html ee5e2e4a3346d225907f27c1f12b3cb2e77c32c4'), "%3C%21DOCTYPE%20html%3E%3Chtml%20123%3D456%3E%3Chtml%20789%3D012%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20123%3D%22456%22%0A%7C%20%20%20789%3D%22012%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"cd557ae48cd48356c367e470927d0fc108724409":[async_test('html5lib_tests14.html cd557ae48cd48356c367e470927d0fc108724409'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Cbody%20789%3D012%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20789%3D%22012%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests15.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests15.html
new file mode 100644
index 0000000000..3e9ac0cbe8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests15.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests15.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['6b53427ced5c2da3830c5053af874df81b59f4dc','e4719f96139431e67dc783631eb1b3b25e0e62e8','6c880904728529240130ab72c30a1f800502e1fc','59cae979c2d8110a1015fd401afb42d1015e2c05','fdac591df8c2ff9db7bdd63adee17e002a620ea1','72352f74535e62a2b0623446073f3edb7698bcf3','93c2841d64ce8d4745de6a0fbd28ddba273fd3d0','e83c7f01a04d3ff180019d1578cc666f67ea71eb','8ed7e05d49dfc6701ef8325a5d9504d596d8d083','938af694979b4eae59e7bd3ab71d76e5254192a0','e7bba1876ae72e36eb68142d21f64fee1043cff6','fc3373f9cb00e32fc98435157d1d74a6a27a7fb9','f4c1486ad877d96aeb9a31fde16d2a93ada53b35','c1176256398117ceca6c3878106893855c39aa5a',];
+ var tests = {
+ "6b53427ced5c2da3830c5053af874df81b59f4dc":[async_test('html5lib_tests15.html 6b53427ced5c2da3830c5053af874df81b59f4dc'), "%3C%21DOCTYPE%20html%3E%3Cp%3E%3Cb%3E%3Ci%3E%3Cu%3E%3C/p%3E%20%3Cp%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cu%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cu%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"e4719f96139431e67dc783631eb1b3b25e0e62e8":[async_test('html5lib_tests15.html e4719f96139431e67dc783631eb1b3b25e0e62e8'), "%3Cp%3E%3Cb%3E%3Ci%3E%3Cu%3E%3C/p%3E%0A%3Cp%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cu%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cu%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"6c880904728529240130ab72c30a1f800502e1fc":[async_test('html5lib_tests15.html 6c880904728529240130ab72c30a1f800502e1fc'), "%3C%21doctype%20html%3E%3C/html%3E%20%3Chead%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%20%22"],"59cae979c2d8110a1015fd401afb42d1015e2c05":[async_test('html5lib_tests15.html 59cae979c2d8110a1015fd401afb42d1015e2c05'), "%3C%21doctype%20html%3E%3C/body%3E%3Cmeta%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmeta%3E"],"fdac591df8c2ff9db7bdd63adee17e002a620ea1":[async_test('html5lib_tests15.html fdac591df8c2ff9db7bdd63adee17e002a620ea1'), "%3Chtml%3E%3C/html%3E%3C%21--%20foo%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%3C%21--%20%20foo%20%20--%3E"],"72352f74535e62a2b0623446073f3edb7698bcf3":[async_test('html5lib_tests15.html 72352f74535e62a2b0623446073f3edb7698bcf3'), "%3C%21doctype%20html%3E%3C/body%3E%3Ctitle%3EX%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"93c2841d64ce8d4745de6a0fbd28ddba273fd3d0":[async_test('html5lib_tests15.html 93c2841d64ce8d4745de6a0fbd28ddba273fd3d0'), "%3C%21doctype%20html%3E%3Ctable%3E%20X%3Cmeta%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%20X%22%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"e83c7f01a04d3ff180019d1578cc666f67ea71eb":[async_test('html5lib_tests15.html e83c7f01a04d3ff180019d1578cc666f67ea71eb'), "%3C%21doctype%20html%3E%3Ctable%3E%20x%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%20x%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"8ed7e05d49dfc6701ef8325a5d9504d596d8d083":[async_test('html5lib_tests15.html 8ed7e05d49dfc6701ef8325a5d9504d596d8d083'), "%3C%21doctype%20html%3E%3Ctable%3E%20x%20%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%20x%20%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"938af694979b4eae59e7bd3ab71d76e5254192a0":[async_test('html5lib_tests15.html 938af694979b4eae59e7bd3ab71d76e5254192a0'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%20x%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%20x%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"e7bba1876ae72e36eb68142d21f64fee1043cff6":[async_test('html5lib_tests15.html e7bba1876ae72e36eb68142d21f64fee1043cff6'), "%3C%21doctype%20html%3E%3Ctable%3EX%3Cstyle%3E%20%3Ctr%3Ex%20%3C/style%3E%20%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20%3Ctr%3Ex%20%22%0A%7C%20%20%20%20%20%20%20%22%20%22"],"fc3373f9cb00e32fc98435157d1d74a6a27a7fb9":[async_test('html5lib_tests15.html fc3373f9cb00e32fc98435157d1d74a6a27a7fb9'), "%3C%21doctype%20html%3E%3Cdiv%3E%3Ctable%3E%3Ca%3Efoo%3C/a%3E%20%3Ctr%3E%3Ctd%3Ebar%3C/td%3E%20%3C/tr%3E%3C/table%3E%3C/div%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20%22"],"f4c1486ad877d96aeb9a31fde16d2a93ada53b35":[async_test('html5lib_tests15.html f4c1486ad877d96aeb9a31fde16d2a93ada53b35'), "%3Cframe%3E%3C/frame%3E%3C/frame%3E%3Cframeset%3E%3Cframe%3E%3Cframeset%3E%3Cframe%3E%3C/frameset%3E%3Cnoframes%3E%3C/frameset%3E%3Cnoframes%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E%0A%7C%20%20%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%20%20%3Cframe%3E%0A%7C%20%20%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%20%20%22%3C/frameset%3E%3Cnoframes%3E%22"],"c1176256398117ceca6c3878106893855c39aa5a":[async_test('html5lib_tests15.html c1176256398117ceca6c3878106893855c39aa5a'), "%3C%21DOCTYPE%20html%3E%3Cobject%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cobject%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests16.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests16.html
new file mode 100644
index 0000000000..f6c8a027ce
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests16.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests16.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['6d8b9d29f1890d59ef2453cff3f6d57b7e398c5c','5d4ac4961f9d52a42f309886d16fbe9c55c198bb','132c6e3cd2659e15b69904c67981a04e81fabe78','bf0b3062e7cbe684380581919947333beef23a8c','cd76a82b9a4bde442e4f8819b37e6308e3eef8f5','747087d5bf2fe9a8a6c0ecf822211d93a4e0cc2a','413482922b0185970bfdd6008e6a0e70ad1b554f','0e3cc8b1f36a34fb3048bb4f01e0e7fec678ceef','3d7a659a8880588e831c7198867b65ac2a974353','46914793d44763c3cd37c3aef0c3689d826602d1','bc8ed1aea5ac5d7eac284386a5defe779eeab3d7','48e7e206aade47bdd9ad3a7cce2c9c86fb4227f6','cc10f706cec3e9356f8c42f77b970f669f74be03','9a6506b01fabf7ba73bb5c90f11b3898c2f119fa','f840264cf775999580e621a83d34af302e139632','efe27c508629d48cf36861e680918f11f48aad15','c51f18f140335e61f0158fadd282fb0f0c75bee6','76d621ce4bd9e462bacaa40ebf43e1ccb569bd21','70e4352779315880955134dfe67c53acb76c4850','b05be3f93446c26026591cbfee84b9603cd6f151','3f08f2e2326b621f819b73336f502610dd94d54f','4932d705ce9c31d4141a630d305e16ae130982e7','accb817d72edbb0d9f72e2c44f47055fb1719d0a','b7b2e78af3f5846dc7f67246c92d95aacc2bd996','52e03d2903a9556823275541c58c173ac077a2a9','1daec6e34a3b4b4ea28f3e90595052090e67cbf7','539e26d76efe146f95bd7b6bfa88ae2d29afd35a','ab43ce067468a33bb658e0d6cc542b9dbcd0c80f','3f93565e7a692675cc519326cc4122b5ea44b533','e579c03d00de7a95bad40602af783b4d7775ab78','1541ebd513bc357af538635050f1b3ec854648e5','ae91f664e0c85f63d21dddaa03a9104d27a9d5ce','17b1bf0912a302c2bed5358791fae3ea6d3efa7d','beac9d7ec99b6317c8504e80118dd0f2d5fad573','d5ead5851ba4d1cdac136d97a449e6b48b0c2cb4','17fe63597371d22c41ccbc4abcd8f468373559d7','26a585731ba7caa063ad5c87a09748a223e56639','91dc36fa03b62334c115db6d4b4a420ef1081753','b0ee0820468ee622b802020e20be120d3c534af2','65907331b39d13cc2128eb57afb7185f0173a953','c284310f4becf9d64252eaec25fe46de8c6e4f2c','63bbe135b3dbb75f2262ce1cc5e9259634596a55','c83e5a48a9409482b62903960b0a04a932832668','193aa9aa570a39d74e340e8d6ddd015ee0a7477c','0ce3d12fa6ac40027b789b09fd987194426e09e6','fd613c2eb713e158fae7a54c45a3d952efa3d5a7','14220896fd483fbeae58f2d69975acc99682be0c','11ca3aa17a2c0b1b6cfc0fb842105247272fbc8d','b524c040a6ab671b006713c8f117d494b11e92ce','9032e7dd5c12caaaf2baf259cee186aaaa67e0e7','aea756cf197079dc506242b729a1d16231644e31','7446d6b66ccedb48db489df92102a0cf439004ce','935849b22e851061994b7da0a7abb626cfe27cab','075cdcf1f8b05fe1a1306ae9702fb7442a76754c','c7d08dec7a358d06f64235f1f6189c0c53e7e75e','c0c7b3e8f7109cf2fef447c2ac28e1566a246a74','d95e3c1ce7b2b79e43654cc23a4a1fc65c084d82','49e2f750500035dfc265752923b58a26d743bc60','c9a6e8a5f0da04035a690465b85c49e1c7259390','7e0c780436a6c11fcdc39dfa30c7e40542bb4745','10b54008a6e2f12bbfcaa0d9e19c0f98d67dfb53','6cdd162096c7fb581b808d491baff3c1234b02e1','243a2da6a25d3d7641fac624e712f4c96376d23c','86ff3afe4315b87db9a5d1d566b029c775e62b94','e20f08402b6afc6d237e8261e512f89ce5299881','c48c5eae7882c00df9026ba16f890266291635f2','d055df57faa87a91d463956c4816bb9c67384c73','33cc450505dd8b55c690589d441a793bb8985f11','40077f2a5b88cf53f3a53485194fc29e39feb39b','10bd03da7b29a7ebe5e18e2163849c2521ae4555','39e7696382843bda945f5717030388257f54dad0','6d0edc9ca958384e4c608386588400b63f8cbc1a','0ec48786ebc1bf532930e5f442c83fc05d5ab873','763803287fa8300b3fd5a0285ce1ab6520640245','f9b350e5c8304caf954b333f54060cd1ab377b47','c82aa963e4443053afe065da586dc6f5df062f9f','a97946be8e03c386e23023e6b6184d11517fc4f5','5b1e9bf7ee6e6222b78d38d99ffd3d0281b930a3','252c0a3870902b1fdf15224188ffd5abf56ced5f','10f2c0e9041fe19c9aea3c9f6a61842372242691','a68fa9f51d285e08b95b34a1ad7ae303d1180cda','1553cebdf01dc953ed7983d39a18752a4fbb24d7','802e7c9b307082d9f15835722eb9ef2dee60ea5f','c7f41e79f00db5b41872c0ef1443094e7ad5bc22','ae3967a139a3ecf61ecbc59c8c769a2731626fac','3586a5a4a1d1d69b139d139b0823af4753bc3e8d','0e99e2603bc91553c252713108e30495d71c3f37','f9858d096fa1e68cce0742d125c551878d2d7020','225e87bce5a4518c3e5cd248ef93ebc39dba14e0','bb08b00b361470ce18b435c97aff4449dc98cc51','cd74b727c1c8233f98e325293a2307e882e10f41','367bf426c092467300f78e5d7526b5a95b490871','96630ff73c222ae5aa31c5d8d32391c00e01d4ae','efd159c8bb96c72857a1b23247240fef25c4bc16','302c14341a82cd1ed9c77beb7ad60ce574f764ff','40369d98631eb17e8ae0cad61d9b7d6dbcddf424','4285eba81853a6d9ea3121ffa93f1b68bb33c157','824bd03d81ed9d5c1d3effe1ea45db39c27d520e','5593651c759624a4b7d91f3e07fc3e02c9bc6642','8862cf5a3972eec607fabbac4bd1dcf5eb50c3f2','bdaf31925507cd81bace2411601cb50be9ed3339','7df307e7cede64fda865c44c0897fc6191ce788c','8f275021b4d57f5235abc4efa2f53d0c633a22d6','b3b17f7b0a43afc62fcc24602488f7c1535eafb1','4383e943d6be1525114a923c5c5c7875d3506f43','c47db9cd301d75cc4ff1f4f66a70efaf04d88d34','c02e6a5ec0971e7e860b61f3638baecfe8d12edc','b3e566664033b9b6c550d44627807a9c17ac0fec','e1b7c9e452fbd47b9f62901e2802d4880c9798d5','20b70b5e4c22aae23ed2f06f957d611b60a16e69','80275b0c1f6bd16a37660822a9accc973b9ad6f6','0707f977884170b8e752fb7956e658e60b96a39c','3634374d8c7b8c049cfeb6cc9cab3284103d7745','1cef2f6f416ac760843e475fdc3aca248fde2a64','bcc75b33353806f86f5ecb7137309fe33fbc39cf','2b2b5615880e8fdd80ec772540344d3bf9f6cf98','87f5714929355b5a84c5bd86ade881e98135bbb8','bc926c61b947962e23f1424d178e8b6caec63984','0f8650ed2fd554c65428eed896e5a6d0276ffe43','e6c000aaa91a5cf04f15a4b6775e17d1bd9143b7','6783594161f7848e42e7c89a32da546163892d75','9c23cc23237032d8decf39d3d886a300c9304707','985c2415bc39a322004bbba6817df37dcdf3f10b','649e75657bd308f81f93189a02efa6b9a7702902','d42bb3557b0fbbe59cf5f606445ba973d4a3d720','d0178734ef1a063624bfd6a737dba933a54bf63d','f6ac88b3fe1743a446da7e0ad895b8f46ea31b23','fb513439a4a4683ec8fc60dc5f2d5588bd656910','4ed828c62d9dcc18ecf8608c9c38708b0b89b83d','51bbdaeed5d24b370a3456040561f8cad226b1a7','45b8f97a3a29d8b1d5e7b7f2586189ead21873b7','1ca26210654f4c95e3f5a337922cd5e8b0694789','a2cd3e4d9dfccfe71bbf5780b537396b5f76c0d6','bb1450fbedebc1605f79eaa4dc501f47d1d7feb6','a9d29f0909132226fd57de4a55282f95682778fd','1fdf0dda892b230252ce864b5c7073ee09aa9165','af1c863ec4e65c29006a0c98ef0872950618a05e','18763f4dd0f9681f043e0124ac26b76795f8afad','499e8bdd759619c5c2248e854e7cd9148ff5fe6d','b442057918b0f6954cf88be6da17b0c5ace03382','8a4c1d1a49ab635498f581f8341f0f037178d01d','c4d806ce1a7cb0abe0cb26e6950839b47134dc68','189378cd03b029adb6e679b6a349124155c697b7','6c9b32168850736c788f14208a39e70272ccc54e','2e9cd7a39540b3bd1df32320556cec71585fb2df','15fee76589fa386fa841a369bf84eb5c75ec131a','689bc409ce3f93662f1223f16271f8ca9883c647','47097173fe23e65b63647777b53b0bdde6ce0f18','fcaa6c59f13ef6a3d6cafeaffaad71b017c34c3c','dd0a2f0c0e6fdcf01c7a917ea662a7ef1e953f11','b29fb15f9b7a448bb70ca27a4b1aac48c1d4f51f','0d91bcb1f42c1a7214168ee61e875166bd75d547','d561d1634333a05fe3428e355f6ab3d67b69ed44','6a13a34b36f6ea738d10b2a2da4958045c243fb7','ee49c31a8c40676f366959341cd98aecdcf9c14f','c541aa24beb8107a3726bd8b9e655ca8f95d7b48','7ae06c19dd24dea99940ceaed8dffdd0e24cd5f9','6d2029f0e2404dcc7f836481cef6bf56f060b248','5c5a26c8bcb37a214c47f4b6d9843de20a8fb7e1','3ee633bf4e4eadc2b70c6eebfa14c600435604c9','ad5d10a0d7d8fc98040ff33f1db197735d6d3d52','2bde3ac14192be5f4e664c2b01b5aaa57c14e347','a87a03c8c3a06269e6ff8d11e142f6ebb05e9306','190325aed218711647eae5581a1f629c76da297c','e244d9f1edb6275b69c4a832620b9439d39bfb22','751f7c9538b9fd00f5c7dd0ed59df07f4d51fdac','762904de405fa26afdd39e024395a441bc6a0f8a','4df21df646897816e166b3dd829a8a3e21157f22','9c241f721c1f6677d736b37ecd86bb230df329df','96ebedcc684f27f984cc7be467c908bdab2470a9','419cfbd87ad35c1d43214b122630cfde1c3ccf1b','cf75ace0d7531b8daa271001dbf92b93b0b4490c','a058b4bb03a689ce8528ed412d62cdd5bb879571','d7c871c41c9db40312ffc5af996ede62ecdfc579','6c353ee8f48c1227eeab81279dc3eb3890c9c3bd','fea28aab54637701c5dfaef4f3fe64c72b272e1c','ecd79b7eb7af2bb4dadf710f70e0d78f62adc40e','43917824cc1d9b5a65601f46e13a0779c3dcff4e','5423bb28649f37e70a0559cba78c3b253a60c277','2c091a50dfd31e766a5a629c0b7c21973e33319d','920feb4f9d1032dcec2abc5c526e4996f642968b','31b9b446263cd5b7a844d43f2a235ed9b0c53efb','dbb5127246fee18718bcffc6cf0730674d12b98a','3872a4cfeba7651f0671de7e5f3922fd5053837b','2fbcb2db61b6416cdf46e0526e1929d146ab3da7','999da234e770bbf681a819423d04ea57415d9bbc','6fc6be53a87bbb2d2a51a5617009063002321d09','b0de72088fa3e543572329eda36fa4bd16e29fa3','2a6d8110b8148a9aa83db81dc38d544becea2fa9','6556aaaca3956eafbc1660bce50a2f3568f1bff6','8ba3d2c7712e9c0484a4cafc8b0ca6f35d8f11ed',];
+ var tests = {
+ "6d8b9d29f1890d59ef2453cff3f6d57b7e398c5c":[async_test('html5lib_tests16.html 6d8b9d29f1890d59ef2453cff3f6d57b7e398c5c'), "%3C%21doctype%20html%3E%3Cscript%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3Cbody%3E"],"5d4ac4961f9d52a42f309886d16fbe9c55c198bb":[async_test('html5lib_tests16.html 5d4ac4961f9d52a42f309886d16fbe9c55c198bb'), "%3C%21doctype%20html%3E%3Cscript%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%3Cbody%3E"],"132c6e3cd2659e15b69904c67981a04e81fabe78":[async_test('html5lib_tests16.html 132c6e3cd2659e15b69904c67981a04e81fabe78'), "%3C%21doctype%20html%3E%3Cscript%3E%3C", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%22%0A%7C%20%20%20%3Cbody%3E"],"bf0b3062e7cbe684380581919947333beef23a8c":[async_test('html5lib_tests16.html bf0b3062e7cbe684380581919947333beef23a8c'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"cd76a82b9a4bde442e4f8819b37e6308e3eef8f5":[async_test('html5lib_tests16.html cd76a82b9a4bde442e4f8819b37e6308e3eef8f5'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/S", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/S%22%0A%7C%20%20%20%3Cbody%3E"],"747087d5bf2fe9a8a6c0ecf822211d93a4e0cc2a":[async_test('html5lib_tests16.html 747087d5bf2fe9a8a6c0ecf822211d93a4e0cc2a'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/SC", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SC%22%0A%7C%20%20%20%3Cbody%3E"],"413482922b0185970bfdd6008e6a0e70ad1b554f":[async_test('html5lib_tests16.html 413482922b0185970bfdd6008e6a0e70ad1b554f'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/SCR", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCR%22%0A%7C%20%20%20%3Cbody%3E"],"0e3cc8b1f36a34fb3048bb4f01e0e7fec678ceef":[async_test('html5lib_tests16.html 0e3cc8b1f36a34fb3048bb4f01e0e7fec678ceef'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/SCRI", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCRI%22%0A%7C%20%20%20%3Cbody%3E"],"3d7a659a8880588e831c7198867b65ac2a974353":[async_test('html5lib_tests16.html 3d7a659a8880588e831c7198867b65ac2a974353'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/SCRIP", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCRIP%22%0A%7C%20%20%20%3Cbody%3E"],"46914793d44763c3cd37c3aef0c3689d826602d1":[async_test('html5lib_tests16.html 46914793d44763c3cd37c3aef0c3689d826602d1'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/SCRIPT", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCRIPT%22%0A%7C%20%20%20%3Cbody%3E"],"bc8ed1aea5ac5d7eac284386a5defe779eeab3d7":[async_test('html5lib_tests16.html bc8ed1aea5ac5d7eac284386a5defe779eeab3d7'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/SCRIPT%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3Cbody%3E"],"48e7e206aade47bdd9ad3a7cce2c9c86fb4227f6":[async_test('html5lib_tests16.html 48e7e206aade47bdd9ad3a7cce2c9c86fb4227f6'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/s", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/s%22%0A%7C%20%20%20%3Cbody%3E"],"cc10f706cec3e9356f8c42f77b970f669f74be03":[async_test('html5lib_tests16.html cc10f706cec3e9356f8c42f77b970f669f74be03'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/sc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/sc%22%0A%7C%20%20%20%3Cbody%3E"],"9a6506b01fabf7ba73bb5c90f11b3898c2f119fa":[async_test('html5lib_tests16.html 9a6506b01fabf7ba73bb5c90f11b3898c2f119fa'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/scr", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/scr%22%0A%7C%20%20%20%3Cbody%3E"],"f840264cf775999580e621a83d34af302e139632":[async_test('html5lib_tests16.html f840264cf775999580e621a83d34af302e139632'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/scri", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/scri%22%0A%7C%20%20%20%3Cbody%3E"],"efe27c508629d48cf36861e680918f11f48aad15":[async_test('html5lib_tests16.html efe27c508629d48cf36861e680918f11f48aad15'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/scrip", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/scrip%22%0A%7C%20%20%20%3Cbody%3E"],"c51f18f140335e61f0158fadd282fb0f0c75bee6":[async_test('html5lib_tests16.html c51f18f140335e61f0158fadd282fb0f0c75bee6'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/script", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"76d621ce4bd9e462bacaa40ebf43e1ccb569bd21":[async_test('html5lib_tests16.html 76d621ce4bd9e462bacaa40ebf43e1ccb569bd21'), "%3C%21doctype%20html%3E%3Cscript%3E%3C/script%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3Cbody%3E"],"70e4352779315880955134dfe67c53acb76c4850":[async_test('html5lib_tests16.html 70e4352779315880955134dfe67c53acb76c4850'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21%22%0A%7C%20%20%20%3Cbody%3E"],"b05be3f93446c26026591cbfee84b9603cd6f151":[async_test('html5lib_tests16.html b05be3f93446c26026591cbfee84b9603cd6f151'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21a", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21a%22%0A%7C%20%20%20%3Cbody%3E"],"3f08f2e2326b621f819b73336f502610dd94d54f":[async_test('html5lib_tests16.html 3f08f2e2326b621f819b73336f502610dd94d54f'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21-", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21-%22%0A%7C%20%20%20%3Cbody%3E"],"4932d705ce9c31d4141a630d305e16ae130982e7":[async_test('html5lib_tests16.html 4932d705ce9c31d4141a630d305e16ae130982e7'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21-a", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21-a%22%0A%7C%20%20%20%3Cbody%3E"],"accb817d72edbb0d9f72e2c44f47055fb1719d0a":[async_test('html5lib_tests16.html accb817d72edbb0d9f72e2c44f47055fb1719d0a'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E"],"b7b2e78af3f5846dc7f67246c92d95aacc2bd996":[async_test('html5lib_tests16.html b7b2e78af3f5846dc7f67246c92d95aacc2bd996'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--a", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--a%22%0A%7C%20%20%20%3Cbody%3E"],"52e03d2903a9556823275541c58c173ac077a2a9":[async_test('html5lib_tests16.html 52e03d2903a9556823275541c58c173ac077a2a9'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3C", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C%22%0A%7C%20%20%20%3Cbody%3E"],"1daec6e34a3b4b4ea28f3e90595052090e67cbf7":[async_test('html5lib_tests16.html 1daec6e34a3b4b4ea28f3e90595052090e67cbf7'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Ca", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ca%22%0A%7C%20%20%20%3Cbody%3E"],"539e26d76efe146f95bd7b6bfa88ae2d29afd35a":[async_test('html5lib_tests16.html 539e26d76efe146f95bd7b6bfa88ae2d29afd35a'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3C/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"ab43ce067468a33bb658e0d6cc542b9dbcd0c80f":[async_test('html5lib_tests16.html ab43ce067468a33bb658e0d6cc542b9dbcd0c80f'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3C/script", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"3f93565e7a692675cc519326cc4122b5ea44b533":[async_test('html5lib_tests16.html 3f93565e7a692675cc519326cc4122b5ea44b533'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3C/script%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E"],"e579c03d00de7a95bad40602af783b4d7775ab78":[async_test('html5lib_tests16.html e579c03d00de7a95bad40602af783b4d7775ab78'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cs", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cs%22%0A%7C%20%20%20%3Cbody%3E"],"1541ebd513bc357af538635050f1b3ec854648e5":[async_test('html5lib_tests16.html 1541ebd513bc357af538635050f1b3ec854648e5'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%22%0A%7C%20%20%20%3Cbody%3E"],"ae91f664e0c85f63d21dddaa03a9104d27a9d5ce":[async_test('html5lib_tests16.html ae91f664e0c85f63d21dddaa03a9104d27a9d5ce'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%22%0A%7C%20%20%20%3Cbody%3E"],"17b1bf0912a302c2bed5358791fae3ea6d3efa7d":[async_test('html5lib_tests16.html 17b1bf0912a302c2bed5358791fae3ea6d3efa7d'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C%22%0A%7C%20%20%20%3Cbody%3E"],"beac9d7ec99b6317c8504e80118dd0f2d5fad573":[async_test('html5lib_tests16.html beac9d7ec99b6317c8504e80118dd0f2d5fad573'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3Ca", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3Ca%22%0A%7C%20%20%20%3Cbody%3E"],"d5ead5851ba4d1cdac136d97a449e6b48b0c2cb4":[async_test('html5lib_tests16.html d5ead5851ba4d1cdac136d97a449e6b48b0c2cb4'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"17fe63597371d22c41ccbc4abcd8f468373559d7":[async_test('html5lib_tests16.html 17fe63597371d22c41ccbc4abcd8f468373559d7'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/s", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/s%22%0A%7C%20%20%20%3Cbody%3E"],"26a585731ba7caa063ad5c87a09748a223e56639":[async_test('html5lib_tests16.html 26a585731ba7caa063ad5c87a09748a223e56639'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"91dc36fa03b62334c115db6d4b4a420ef1081753":[async_test('html5lib_tests16.html 91dc36fa03b62334c115db6d4b4a420ef1081753'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/scripta", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/scripta%22%0A%7C%20%20%20%3Cbody%3E"],"b0ee0820468ee622b802020e20be120d3c534af2":[async_test('html5lib_tests16.html b0ee0820468ee622b802020e20be120d3c534af2'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"65907331b39d13cc2128eb57afb7185f0173a953":[async_test('html5lib_tests16.html 65907331b39d13cc2128eb57afb7185f0173a953'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"c284310f4becf9d64252eaec25fe46de8c6e4f2c":[async_test('html5lib_tests16.html c284310f4becf9d64252eaec25fe46de8c6e4f2c'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script/%22%0A%7C%20%20%20%3Cbody%3E"],"63bbe135b3dbb75f2262ce1cc5e9259634596a55":[async_test('html5lib_tests16.html 63bbe135b3dbb75f2262ce1cc5e9259634596a55'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3C%22%0A%7C%20%20%20%3Cbody%3E"],"c83e5a48a9409482b62903960b0a04a932832668":[async_test('html5lib_tests16.html c83e5a48a9409482b62903960b0a04a932832668'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3Ca", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3Ca%22%0A%7C%20%20%20%3Cbody%3E"],"193aa9aa570a39d74e340e8d6ddd015ee0a7477c":[async_test('html5lib_tests16.html 193aa9aa570a39d74e340e8d6ddd015ee0a7477c'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"0ce3d12fa6ac40027b789b09fd987194426e09e6":[async_test('html5lib_tests16.html 0ce3d12fa6ac40027b789b09fd987194426e09e6'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"fd613c2eb713e158fae7a54c45a3d952efa3d5a7":[async_test('html5lib_tests16.html fd613c2eb713e158fae7a54c45a3d952efa3d5a7'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"14220896fd483fbeae58f2d69975acc99682be0c":[async_test('html5lib_tests16.html 14220896fd483fbeae58f2d69975acc99682be0c'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"11ca3aa17a2c0b1b6cfc0fb842105247272fbc8d":[async_test('html5lib_tests16.html 11ca3aa17a2c0b1b6cfc0fb842105247272fbc8d'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"b524c040a6ab671b006713c8f117d494b11e92ce":[async_test('html5lib_tests16.html b524c040a6ab671b006713c8f117d494b11e92ce'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20-", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20-%22%0A%7C%20%20%20%3Cbody%3E"],"9032e7dd5c12caaaf2baf259cee186aaaa67e0e7":[async_test('html5lib_tests16.html 9032e7dd5c12caaaf2baf259cee186aaaa67e0e7'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20-a", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20-a%22%0A%7C%20%20%20%3Cbody%3E"],"aea756cf197079dc506242b729a1d16231644e31":[async_test('html5lib_tests16.html aea756cf197079dc506242b729a1d16231644e31'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20-%3C", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20-%3C%22%0A%7C%20%20%20%3Cbody%3E"],"7446d6b66ccedb48db489df92102a0cf439004ce":[async_test('html5lib_tests16.html 7446d6b66ccedb48db489df92102a0cf439004ce'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%22%0A%7C%20%20%20%3Cbody%3E"],"935849b22e851061994b7da0a7abb626cfe27cab":[async_test('html5lib_tests16.html 935849b22e851061994b7da0a7abb626cfe27cab'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--a", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--a%22%0A%7C%20%20%20%3Cbody%3E"],"075cdcf1f8b05fe1a1306ae9702fb7442a76754c":[async_test('html5lib_tests16.html 075cdcf1f8b05fe1a1306ae9702fb7442a76754c'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3C", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3C%22%0A%7C%20%20%20%3Cbody%3E"],"c7d08dec7a358d06f64235f1f6189c0c53e7e75e":[async_test('html5lib_tests16.html c7d08dec7a358d06f64235f1f6189c0c53e7e75e'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"c0c7b3e8f7109cf2fef447c2ac28e1566a246a74":[async_test('html5lib_tests16.html c0c7b3e8f7109cf2fef447c2ac28e1566a246a74'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%3C%22%0A%7C%20%20%20%3Cbody%3E"],"d95e3c1ce7b2b79e43654cc23a4a1fc65c084d82":[async_test('html5lib_tests16.html d95e3c1ce7b2b79e43654cc23a4a1fc65c084d82'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"49e2f750500035dfc265752923b58a26d743bc60":[async_test('html5lib_tests16.html 49e2f750500035dfc265752923b58a26d743bc60'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"c9a6e8a5f0da04035a690465b85c49e1c7259390":[async_test('html5lib_tests16.html c9a6e8a5f0da04035a690465b85c49e1c7259390'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"7e0c780436a6c11fcdc39dfa30c7e40542bb4745":[async_test('html5lib_tests16.html 7e0c780436a6c11fcdc39dfa30c7e40542bb4745'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script/", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"10b54008a6e2f12bbfcaa0d9e19c0f98d67dfb53":[async_test('html5lib_tests16.html 10b54008a6e2f12bbfcaa0d9e19c0f98d67dfb53'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"6cdd162096c7fb581b808d491baff3c1234b02e1":[async_test('html5lib_tests16.html 6cdd162096c7fb581b808d491baff3c1234b02e1'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C%5C/script%3E--%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C%5C/script%3E--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"243a2da6a25d3d7641fac624e712f4c96376d23c":[async_test('html5lib_tests16.html 243a2da6a25d3d7641fac624e712f4c96376d23c'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E--%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"86ff3afe4315b87db9a5d1d566b029c775e62b94":[async_test('html5lib_tests16.html 86ff3afe4315b87db9a5d1d566b029c775e62b94'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"e20f08402b6afc6d237e8261e512f89ce5299881":[async_test('html5lib_tests16.html e20f08402b6afc6d237e8261e512f89ce5299881'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%3E%3C%21--%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%3E%3C%21--%22%0A%7C%20%20%20%3Cbody%3E"],"c48c5eae7882c00df9026ba16f890266291635f2":[async_test('html5lib_tests16.html c48c5eae7882c00df9026ba16f890266291635f2'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%20%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%20%3E%22%0A%7C%20%20%20%3Cbody%3E"],"d055df57faa87a91d463956c4816bb9c67384c73":[async_test('html5lib_tests16.html d055df57faa87a91d463956c4816bb9c67384c73'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%3E%22%0A%7C%20%20%20%3Cbody%3E"],"33cc450505dd8b55c690589d441a793bb8985f11":[async_test('html5lib_tests16.html 33cc450505dd8b55c690589d441a793bb8985f11'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%20%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%20%3E%22%0A%7C%20%20%20%3Cbody%3E"],"40077f2a5b88cf53f3a53485194fc29e39feb39b":[async_test('html5lib_tests16.html 40077f2a5b88cf53f3a53485194fc29e39feb39b'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%3E%22%0A%7C%20%20%20%3Cbody%3E"],"10bd03da7b29a7ebe5e18e2163849c2521ae4555":[async_test('html5lib_tests16.html 10bd03da7b29a7ebe5e18e2163849c2521ae4555'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E--%21%3E%3C/script%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E--%21%3E%3C/script%3EX%22%0A%7C%20%20%20%3Cbody%3E"],"39e7696382843bda945f5717030388257f54dad0":[async_test('html5lib_tests16.html 39e7696382843bda945f5717030388257f54dad0'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscr%27%2B%27ipt%3E%3C/script%3E--%3E%3C/script%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscr%27%2B%27ipt%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"6d0edc9ca958384e4c608386588400b63f8cbc1a":[async_test('html5lib_tests16.html 6d0edc9ca958384e4c608386588400b63f8cbc1a'), "%3C%21doctype%20html%3E%3Cscript%3E%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E%3C/script%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E%3C/script%3EX%22%0A%7C%20%20%20%3Cbody%3E"],"0ec48786ebc1bf532930e5f442c83fc05d5ab873":[async_test('html5lib_tests16.html 0ec48786ebc1bf532930e5f442c83fc05d5ab873'), "%3C%21doctype%20html%3E%3Cstyle%3E%3C%21--%3Cstyle%3E%3C/style%3E--%3E%3C/style%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cstyle%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"763803287fa8300b3fd5a0285ce1ab6520640245":[async_test('html5lib_tests16.html 763803287fa8300b3fd5a0285ce1ab6520640245'), "%3C%21doctype%20html%3E%3Cstyle%3E%3C%21--%3C/style%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"f9b350e5c8304caf954b333f54060cd1ab377b47":[async_test('html5lib_tests16.html f9b350e5c8304caf954b333f54060cd1ab377b47'), "%3C%21doctype%20html%3E%3Cstyle%3E%3C%21--...%3C/style%3E...--%3E%3C/style%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--...%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22...--%3E%22"],"c82aa963e4443053afe065da586dc6f5df062f9f":[async_test('html5lib_tests16.html c82aa963e4443053afe065da586dc6f5df062f9f'), "%3C%21doctype%20html%3E%3Cstyle%3E%3C%21--%3Cbr%3E%3Chtml%20xmlns%3Av%3D%22urn%3Aschemas-microsoft-com%3Avml%22%3E%3C%21--%5Bif%20%21mso%5D%3E%3Cstyle%3E%3C/style%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cbr%3E%3Chtml%20xmlns%3Av%3D%22urn%3Aschemas-microsoft-com%3Avml%22%3E%3C%21--%5Bif%20%21mso%5D%3E%3Cstyle%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"a97946be8e03c386e23023e6b6184d11517fc4f5":[async_test('html5lib_tests16.html a97946be8e03c386e23023e6b6184d11517fc4f5'), "%3C%21doctype%20html%3E%3Cstyle%3E%3C%21--...%3Cstyle%3E%3C%21--...--%21%3E%3C/style%3E--%3E%3C/style%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--...%3Cstyle%3E%3C%21--...--%21%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"5b1e9bf7ee6e6222b78d38d99ffd3d0281b930a3":[async_test('html5lib_tests16.html 5b1e9bf7ee6e6222b78d38d99ffd3d0281b930a3'), "%3C%21doctype%20html%3E%3Cstyle%3E%3C%21--...%3C/style%3E%3C%21--%20--%3E%3Cstyle%3E%40import%20...%3C/style%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--...%22%0A%7C%20%20%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%40import%20...%22%0A%7C%20%20%20%3Cbody%3E"],"252c0a3870902b1fdf15224188ffd5abf56ced5f":[async_test('html5lib_tests16.html 252c0a3870902b1fdf15224188ffd5abf56ced5f'), "%3C%21doctype%20html%3E%3Cstyle%3E...%3Cstyle%3E%3C%21--...%3C/style%3E%3C%21--%20--%3E%3C/style%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22...%3Cstyle%3E%3C%21--...%22%0A%7C%20%20%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%3Cbody%3E"],"10f2c0e9041fe19c9aea3c9f6a61842372242691":[async_test('html5lib_tests16.html 10f2c0e9041fe19c9aea3c9f6a61842372242691'), "%3C%21doctype%20html%3E%3Cstyle%3E...%3C%21--%5Bif%20IE%5D%3E%3Cstyle%3E...%3C/style%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22...%3C%21--%5Bif%20IE%5D%3E%3Cstyle%3E...%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"a68fa9f51d285e08b95b34a1ad7ae303d1180cda":[async_test('html5lib_tests16.html a68fa9f51d285e08b95b34a1ad7ae303d1180cda'), "%3C%21doctype%20html%3E%3Ctitle%3E%3C%21--%3Ctitle%3E%3C/title%3E--%3E%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ctitle%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"1553cebdf01dc953ed7983d39a18752a4fbb24d7":[async_test('html5lib_tests16.html 1553cebdf01dc953ed7983d39a18752a4fbb24d7'), "%3C%21doctype%20html%3E%3Ctitle%3E%26lt%3B/title%3E%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3C/title%3E%22%0A%7C%20%20%20%3Cbody%3E"],"802e7c9b307082d9f15835722eb9ef2dee60ea5f":[async_test('html5lib_tests16.html 802e7c9b307082d9f15835722eb9ef2dee60ea5f'), "%3C%21doctype%20html%3E%3Ctitle%3Efoo/title%3E%3Clink%3E%3C/head%3E%3Cbody%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22foo/title%3E%3Clink%3E%3C/head%3E%3Cbody%3EX%22%0A%7C%20%20%20%3Cbody%3E"],"c7f41e79f00db5b41872c0ef1443094e7ad5bc22":[async_test('html5lib_tests16.html c7f41e79f00db5b41872c0ef1443094e7ad5bc22'), "%3C%21doctype%20html%3E%3Cnoscript%3E%3C%21--%3Cnoscript%3E%3C/noscript%3E--%3E%3C/noscript%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cnoscript%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"ae3967a139a3ecf61ecbc59c8c769a2731626fac":[async_test('html5lib_tests16.html ae3967a139a3ecf61ecbc59c8c769a2731626fac'), "%3C%21doctype%20html%3E%3Cnoscript%3E%3C%21--%3C/noscript%3EX%3Cnoscript%3E--%3E%3C/noscript%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22--%3E%22"],"3586a5a4a1d1d69b139d139b0823af4753bc3e8d":[async_test('html5lib_tests16.html 3586a5a4a1d1d69b139d139b0823af4753bc3e8d'), "%3C%21doctype%20html%3E%3Cnoscript%3E%3Ciframe%3E%3C/noscript%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22%3Ciframe%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"0e99e2603bc91553c252713108e30495d71c3f37":[async_test('html5lib_tests16.html 0e99e2603bc91553c252713108e30495d71c3f37'), "%3C%21doctype%20html%3E%3Cnoframes%3E%3C%21--%3Cnoframes%3E%3C/noframes%3E--%3E%3C/noframes%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cnoframes%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"f9858d096fa1e68cce0742d125c551878d2d7020":[async_test('html5lib_tests16.html f9858d096fa1e68cce0742d125c551878d2d7020'), "%3C%21doctype%20html%3E%3Cnoframes%3E%3Cbody%3E%3Cscript%3E%3C%21--...%3C/script%3E%3C/body%3E%3C/noframes%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%20%20%22%3Cbody%3E%3Cscript%3E%3C%21--...%3C/script%3E%3C/body%3E%22%0A%7C%20%20%20%3Cbody%3E"],"225e87bce5a4518c3e5cd248ef93ebc39dba14e0":[async_test('html5lib_tests16.html 225e87bce5a4518c3e5cd248ef93ebc39dba14e0'), "%3C%21doctype%20html%3E%3Ctextarea%3E%3C%21--%3Ctextarea%3E%3C/textarea%3E--%3E%3C/textarea%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ctextarea%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"bb08b00b361470ce18b435c97aff4449dc98cc51":[async_test('html5lib_tests16.html bb08b00b361470ce18b435c97aff4449dc98cc51'), "%3C%21doctype%20html%3E%3Ctextarea%3E%26lt%3B/textarea%3E%3C/textarea%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3C/textarea%3E%22"],"cd74b727c1c8233f98e325293a2307e882e10f41":[async_test('html5lib_tests16.html cd74b727c1c8233f98e325293a2307e882e10f41'), "%3C%21doctype%20html%3E%3Ctextarea%3E%26lt%3B%3C/textarea%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3C%22"],"367bf426c092467300f78e5d7526b5a95b490871":[async_test('html5lib_tests16.html 367bf426c092467300f78e5d7526b5a95b490871'), "%3C%21doctype%20html%3E%3Ctextarea%3Ea%26lt%3Bb%3C/textarea%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22a%3Cb%22"],"96630ff73c222ae5aa31c5d8d32391c00e01d4ae":[async_test('html5lib_tests16.html 96630ff73c222ae5aa31c5d8d32391c00e01d4ae'), "%3C%21doctype%20html%3E%3Ciframe%3E%3C%21--%3Ciframe%3E%3C/iframe%3E--%3E%3C/iframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ciframe%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ciframe%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"efd159c8bb96c72857a1b23247240fef25c4bc16":[async_test('html5lib_tests16.html efd159c8bb96c72857a1b23247240fef25c4bc16'), "%3C%21doctype%20html%3E%3Ciframe%3E...%3C%21--X-%3E...%3C%21--/X-%3E...%3C/iframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ciframe%3E%0A%7C%20%20%20%20%20%20%20%22...%3C%21--X-%3E...%3C%21--/X-%3E...%22"],"302c14341a82cd1ed9c77beb7ad60ce574f764ff":[async_test('html5lib_tests16.html 302c14341a82cd1ed9c77beb7ad60ce574f764ff'), "%3C%21doctype%20html%3E%3Cxmp%3E%3C%21--%3Cxmp%3E%3C/xmp%3E--%3E%3C/xmp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cxmp%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cxmp%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"40369d98631eb17e8ae0cad61d9b7d6dbcddf424":[async_test('html5lib_tests16.html 40369d98631eb17e8ae0cad61d9b7d6dbcddf424'), "%3C%21doctype%20html%3E%3Cnoembed%3E%3C%21--%3Cnoembed%3E%3C/noembed%3E--%3E%3C/noembed%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cnoembed%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cnoembed%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"4285eba81853a6d9ea3121ffa93f1b68bb33c157":[async_test('html5lib_tests16.html 4285eba81853a6d9ea3121ffa93f1b68bb33c157'), "%3Cscript%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3Cbody%3E"],"824bd03d81ed9d5c1d3effe1ea45db39c27d520e":[async_test('html5lib_tests16.html 824bd03d81ed9d5c1d3effe1ea45db39c27d520e'), "%3Cscript%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%3Cbody%3E"],"5593651c759624a4b7d91f3e07fc3e02c9bc6642":[async_test('html5lib_tests16.html 5593651c759624a4b7d91f3e07fc3e02c9bc6642'), "%3Cscript%3E%3C", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%22%0A%7C%20%20%20%3Cbody%3E"],"8862cf5a3972eec607fabbac4bd1dcf5eb50c3f2":[async_test('html5lib_tests16.html 8862cf5a3972eec607fabbac4bd1dcf5eb50c3f2'), "%3Cscript%3E%3C/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"bdaf31925507cd81bace2411601cb50be9ed3339":[async_test('html5lib_tests16.html bdaf31925507cd81bace2411601cb50be9ed3339'), "%3Cscript%3E%3C/S", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/S%22%0A%7C%20%20%20%3Cbody%3E"],"7df307e7cede64fda865c44c0897fc6191ce788c":[async_test('html5lib_tests16.html 7df307e7cede64fda865c44c0897fc6191ce788c'), "%3Cscript%3E%3C/SC", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SC%22%0A%7C%20%20%20%3Cbody%3E"],"8f275021b4d57f5235abc4efa2f53d0c633a22d6":[async_test('html5lib_tests16.html 8f275021b4d57f5235abc4efa2f53d0c633a22d6'), "%3Cscript%3E%3C/SCR", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCR%22%0A%7C%20%20%20%3Cbody%3E"],"b3b17f7b0a43afc62fcc24602488f7c1535eafb1":[async_test('html5lib_tests16.html b3b17f7b0a43afc62fcc24602488f7c1535eafb1'), "%3Cscript%3E%3C/SCRI", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCRI%22%0A%7C%20%20%20%3Cbody%3E"],"4383e943d6be1525114a923c5c5c7875d3506f43":[async_test('html5lib_tests16.html 4383e943d6be1525114a923c5c5c7875d3506f43'), "%3Cscript%3E%3C/SCRIP", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCRIP%22%0A%7C%20%20%20%3Cbody%3E"],"c47db9cd301d75cc4ff1f4f66a70efaf04d88d34":[async_test('html5lib_tests16.html c47db9cd301d75cc4ff1f4f66a70efaf04d88d34'), "%3Cscript%3E%3C/SCRIPT", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/SCRIPT%22%0A%7C%20%20%20%3Cbody%3E"],"c02e6a5ec0971e7e860b61f3638baecfe8d12edc":[async_test('html5lib_tests16.html c02e6a5ec0971e7e860b61f3638baecfe8d12edc'), "%3Cscript%3E%3C/SCRIPT%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3Cbody%3E"],"b3e566664033b9b6c550d44627807a9c17ac0fec":[async_test('html5lib_tests16.html b3e566664033b9b6c550d44627807a9c17ac0fec'), "%3Cscript%3E%3C/s", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/s%22%0A%7C%20%20%20%3Cbody%3E"],"e1b7c9e452fbd47b9f62901e2802d4880c9798d5":[async_test('html5lib_tests16.html e1b7c9e452fbd47b9f62901e2802d4880c9798d5'), "%3Cscript%3E%3C/sc", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/sc%22%0A%7C%20%20%20%3Cbody%3E"],"20b70b5e4c22aae23ed2f06f957d611b60a16e69":[async_test('html5lib_tests16.html 20b70b5e4c22aae23ed2f06f957d611b60a16e69'), "%3Cscript%3E%3C/scr", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/scr%22%0A%7C%20%20%20%3Cbody%3E"],"80275b0c1f6bd16a37660822a9accc973b9ad6f6":[async_test('html5lib_tests16.html 80275b0c1f6bd16a37660822a9accc973b9ad6f6'), "%3Cscript%3E%3C/scri", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/scri%22%0A%7C%20%20%20%3Cbody%3E"],"0707f977884170b8e752fb7956e658e60b96a39c":[async_test('html5lib_tests16.html 0707f977884170b8e752fb7956e658e60b96a39c'), "%3Cscript%3E%3C/scrip", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/scrip%22%0A%7C%20%20%20%3Cbody%3E"],"3634374d8c7b8c049cfeb6cc9cab3284103d7745":[async_test('html5lib_tests16.html 3634374d8c7b8c049cfeb6cc9cab3284103d7745'), "%3Cscript%3E%3C/script", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"1cef2f6f416ac760843e475fdc3aca248fde2a64":[async_test('html5lib_tests16.html 1cef2f6f416ac760843e475fdc3aca248fde2a64'), "%3Cscript%3E%3C/script%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3Cbody%3E"],"bcc75b33353806f86f5ecb7137309fe33fbc39cf":[async_test('html5lib_tests16.html bcc75b33353806f86f5ecb7137309fe33fbc39cf'), "%3Cscript%3E%3C%21", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21%22%0A%7C%20%20%20%3Cbody%3E"],"2b2b5615880e8fdd80ec772540344d3bf9f6cf98":[async_test('html5lib_tests16.html 2b2b5615880e8fdd80ec772540344d3bf9f6cf98'), "%3Cscript%3E%3C%21a", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21a%22%0A%7C%20%20%20%3Cbody%3E"],"87f5714929355b5a84c5bd86ade881e98135bbb8":[async_test('html5lib_tests16.html 87f5714929355b5a84c5bd86ade881e98135bbb8'), "%3Cscript%3E%3C%21-", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21-%22%0A%7C%20%20%20%3Cbody%3E"],"bc926c61b947962e23f1424d178e8b6caec63984":[async_test('html5lib_tests16.html bc926c61b947962e23f1424d178e8b6caec63984'), "%3Cscript%3E%3C%21-a", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21-a%22%0A%7C%20%20%20%3Cbody%3E"],"0f8650ed2fd554c65428eed896e5a6d0276ffe43":[async_test('html5lib_tests16.html 0f8650ed2fd554c65428eed896e5a6d0276ffe43'), "%3Cscript%3E%3C%21--", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E"],"e6c000aaa91a5cf04f15a4b6775e17d1bd9143b7":[async_test('html5lib_tests16.html e6c000aaa91a5cf04f15a4b6775e17d1bd9143b7'), "%3Cscript%3E%3C%21--a", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--a%22%0A%7C%20%20%20%3Cbody%3E"],"6783594161f7848e42e7c89a32da546163892d75":[async_test('html5lib_tests16.html 6783594161f7848e42e7c89a32da546163892d75'), "%3Cscript%3E%3C%21--%3C", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C%22%0A%7C%20%20%20%3Cbody%3E"],"9c23cc23237032d8decf39d3d886a300c9304707":[async_test('html5lib_tests16.html 9c23cc23237032d8decf39d3d886a300c9304707'), "%3Cscript%3E%3C%21--%3Ca", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ca%22%0A%7C%20%20%20%3Cbody%3E"],"985c2415bc39a322004bbba6817df37dcdf3f10b":[async_test('html5lib_tests16.html 985c2415bc39a322004bbba6817df37dcdf3f10b'), "%3Cscript%3E%3C%21--%3C/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"649e75657bd308f81f93189a02efa6b9a7702902":[async_test('html5lib_tests16.html 649e75657bd308f81f93189a02efa6b9a7702902'), "%3Cscript%3E%3C%21--%3C/script", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"d42bb3557b0fbbe59cf5f606445ba973d4a3d720":[async_test('html5lib_tests16.html d42bb3557b0fbbe59cf5f606445ba973d4a3d720'), "%3Cscript%3E%3C%21--%3C/script%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E"],"d0178734ef1a063624bfd6a737dba933a54bf63d":[async_test('html5lib_tests16.html d0178734ef1a063624bfd6a737dba933a54bf63d'), "%3Cscript%3E%3C%21--%3Cs", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cs%22%0A%7C%20%20%20%3Cbody%3E"],"f6ac88b3fe1743a446da7e0ad895b8f46ea31b23":[async_test('html5lib_tests16.html f6ac88b3fe1743a446da7e0ad895b8f46ea31b23'), "%3Cscript%3E%3C%21--%3Cscript", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%22%0A%7C%20%20%20%3Cbody%3E"],"fb513439a4a4683ec8fc60dc5f2d5588bd656910":[async_test('html5lib_tests16.html fb513439a4a4683ec8fc60dc5f2d5588bd656910'), "%3Cscript%3E%3C%21--%3Cscript%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%22%0A%7C%20%20%20%3Cbody%3E"],"4ed828c62d9dcc18ecf8608c9c38708b0b89b83d":[async_test('html5lib_tests16.html 4ed828c62d9dcc18ecf8608c9c38708b0b89b83d'), "%3Cscript%3E%3C%21--%3Cscript%20%3C", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C%22%0A%7C%20%20%20%3Cbody%3E"],"51bbdaeed5d24b370a3456040561f8cad226b1a7":[async_test('html5lib_tests16.html 51bbdaeed5d24b370a3456040561f8cad226b1a7'), "%3Cscript%3E%3C%21--%3Cscript%20%3Ca", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3Ca%22%0A%7C%20%20%20%3Cbody%3E"],"45b8f97a3a29d8b1d5e7b7f2586189ead21873b7":[async_test('html5lib_tests16.html 45b8f97a3a29d8b1d5e7b7f2586189ead21873b7'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"1ca26210654f4c95e3f5a337922cd5e8b0694789":[async_test('html5lib_tests16.html 1ca26210654f4c95e3f5a337922cd5e8b0694789'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/s", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/s%22%0A%7C%20%20%20%3Cbody%3E"],"a2cd3e4d9dfccfe71bbf5780b537396b5f76c0d6":[async_test('html5lib_tests16.html a2cd3e4d9dfccfe71bbf5780b537396b5f76c0d6'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"bb1450fbedebc1605f79eaa4dc501f47d1d7feb6":[async_test('html5lib_tests16.html bb1450fbedebc1605f79eaa4dc501f47d1d7feb6'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/scripta", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/scripta%22%0A%7C%20%20%20%3Cbody%3E"],"a9d29f0909132226fd57de4a55282f95682778fd":[async_test('html5lib_tests16.html a9d29f0909132226fd57de4a55282f95682778fd'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"1fdf0dda892b230252ce864b5c7073ee09aa9165":[async_test('html5lib_tests16.html 1fdf0dda892b230252ce864b5c7073ee09aa9165'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"af1c863ec4e65c29006a0c98ef0872950618a05e":[async_test('html5lib_tests16.html af1c863ec4e65c29006a0c98ef0872950618a05e'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script/%22%0A%7C%20%20%20%3Cbody%3E"],"18763f4dd0f9681f043e0124ac26b76795f8afad":[async_test('html5lib_tests16.html 18763f4dd0f9681f043e0124ac26b76795f8afad'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3C%22%0A%7C%20%20%20%3Cbody%3E"],"499e8bdd759619c5c2248e854e7cd9148ff5fe6d":[async_test('html5lib_tests16.html 499e8bdd759619c5c2248e854e7cd9148ff5fe6d'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3Ca", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3Ca%22%0A%7C%20%20%20%3Cbody%3E"],"b442057918b0f6954cf88be6da17b0c5ace03382":[async_test('html5lib_tests16.html b442057918b0f6954cf88be6da17b0c5ace03382'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"8a4c1d1a49ab635498f581f8341f0f037178d01d":[async_test('html5lib_tests16.html 8a4c1d1a49ab635498f581f8341f0f037178d01d'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"c4d806ce1a7cb0abe0cb26e6950839b47134dc68":[async_test('html5lib_tests16.html c4d806ce1a7cb0abe0cb26e6950839b47134dc68'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"189378cd03b029adb6e679b6a349124155c697b7":[async_test('html5lib_tests16.html 189378cd03b029adb6e679b6a349124155c697b7'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"6c9b32168850736c788f14208a39e70272ccc54e":[async_test('html5lib_tests16.html 6c9b32168850736c788f14208a39e70272ccc54e'), "%3Cscript%3E%3C%21--%3Cscript%20%3C/script%20%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20%3C/script%20%22%0A%7C%20%20%20%3Cbody%3E"],"2e9cd7a39540b3bd1df32320556cec71585fb2df":[async_test('html5lib_tests16.html 2e9cd7a39540b3bd1df32320556cec71585fb2df'), "%3Cscript%3E%3C%21--%3Cscript%20-", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20-%22%0A%7C%20%20%20%3Cbody%3E"],"15fee76589fa386fa841a369bf84eb5c75ec131a":[async_test('html5lib_tests16.html 15fee76589fa386fa841a369bf84eb5c75ec131a'), "%3Cscript%3E%3C%21--%3Cscript%20-a", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20-a%22%0A%7C%20%20%20%3Cbody%3E"],"689bc409ce3f93662f1223f16271f8ca9883c647":[async_test('html5lib_tests16.html 689bc409ce3f93662f1223f16271f8ca9883c647'), "%3Cscript%3E%3C%21--%3Cscript%20--", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%22%0A%7C%20%20%20%3Cbody%3E"],"47097173fe23e65b63647777b53b0bdde6ce0f18":[async_test('html5lib_tests16.html 47097173fe23e65b63647777b53b0bdde6ce0f18'), "%3Cscript%3E%3C%21--%3Cscript%20--a", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--a%22%0A%7C%20%20%20%3Cbody%3E"],"fcaa6c59f13ef6a3d6cafeaffaad71b017c34c3c":[async_test('html5lib_tests16.html fcaa6c59f13ef6a3d6cafeaffaad71b017c34c3c'), "%3Cscript%3E%3C%21--%3Cscript%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"dd0a2f0c0e6fdcf01c7a917ea662a7ef1e953f11":[async_test('html5lib_tests16.html dd0a2f0c0e6fdcf01c7a917ea662a7ef1e953f11'), "%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%3C%22%0A%7C%20%20%20%3Cbody%3E"],"b29fb15f9b7a448bb70ca27a4b1aac48c1d4f51f":[async_test('html5lib_tests16.html b29fb15f9b7a448bb70ca27a4b1aac48c1d4f51f'), "%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%3C/%22%0A%7C%20%20%20%3Cbody%3E"],"0d91bcb1f42c1a7214168ee61e875166bd75d547":[async_test('html5lib_tests16.html 0d91bcb1f42c1a7214168ee61e875166bd75d547'), "%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%3C/script%22%0A%7C%20%20%20%3Cbody%3E"],"d561d1634333a05fe3428e355f6ab3d67b69ed44":[async_test('html5lib_tests16.html d561d1634333a05fe3428e355f6ab3d67b69ed44'), "%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"6a13a34b36f6ea738d10b2a2da4958045c243fb7":[async_test('html5lib_tests16.html 6a13a34b36f6ea738d10b2a2da4958045c243fb7'), "%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script/", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"ee49c31a8c40676f366959341cd98aecdcf9c14f":[async_test('html5lib_tests16.html ee49c31a8c40676f366959341cd98aecdcf9c14f'), "%3Cscript%3E%3C%21--%3Cscript%20--%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%20--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"c541aa24beb8107a3726bd8b9e655ca8f95d7b48":[async_test('html5lib_tests16.html c541aa24beb8107a3726bd8b9e655ca8f95d7b48'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C%5C/script%3E--%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C%5C/script%3E--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"7ae06c19dd24dea99940ceaed8dffdd0e24cd5f9":[async_test('html5lib_tests16.html 7ae06c19dd24dea99940ceaed8dffdd0e24cd5f9'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E--%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"6d2029f0e2404dcc7f836481cef6bf56f060b248":[async_test('html5lib_tests16.html 6d2029f0e2404dcc7f836481cef6bf56f060b248'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E%22%0A%7C%20%20%20%3Cbody%3E"],"5c5a26c8bcb37a214c47f4b6d9843de20a8fb7e1":[async_test('html5lib_tests16.html 5c5a26c8bcb37a214c47f4b6d9843de20a8fb7e1'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%3E%3C%21--%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%3E%3C%21--%22%0A%7C%20%20%20%3Cbody%3E"],"3ee633bf4e4eadc2b70c6eebfa14c600435604c9":[async_test('html5lib_tests16.html 3ee633bf4e4eadc2b70c6eebfa14c600435604c9'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%20%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E--%20%3E%22%0A%7C%20%20%20%3Cbody%3E"],"ad5d10a0d7d8fc98040ff33f1db197735d6d3d52":[async_test('html5lib_tests16.html ad5d10a0d7d8fc98040ff33f1db197735d6d3d52'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%3E%22%0A%7C%20%20%20%3Cbody%3E"],"2bde3ac14192be5f4e664c2b01b5aaa57c14e347":[async_test('html5lib_tests16.html 2bde3ac14192be5f4e664c2b01b5aaa57c14e347'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%20%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%20-%20%3E%22%0A%7C%20%20%20%3Cbody%3E"],"a87a03c8c3a06269e6ff8d11e142f6ebb05e9306":[async_test('html5lib_tests16.html a87a03c8c3a06269e6ff8d11e142f6ebb05e9306'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/script%3E%3Cscript%3E%3C/script%3E-%3E%22%0A%7C%20%20%20%3Cbody%3E"],"190325aed218711647eae5581a1f629c76da297c":[async_test('html5lib_tests16.html 190325aed218711647eae5581a1f629c76da297c'), "%3Cscript%3E%3C%21--%3Cscript%3E--%21%3E%3C/script%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E--%21%3E%3C/script%3EX%22%0A%7C%20%20%20%3Cbody%3E"],"e244d9f1edb6275b69c4a832620b9439d39bfb22":[async_test('html5lib_tests16.html e244d9f1edb6275b69c4a832620b9439d39bfb22'), "%3Cscript%3E%3C%21--%3Cscr%27%2B%27ipt%3E%3C/script%3E--%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscr%27%2B%27ipt%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"751f7c9538b9fd00f5c7dd0ed59df07f4d51fdac":[async_test('html5lib_tests16.html 751f7c9538b9fd00f5c7dd0ed59df07f4d51fdac'), "%3Cscript%3E%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E%3C/script%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cscript%3E%3C/scr%27%2B%27ipt%3E%3C/script%3EX%22%0A%7C%20%20%20%3Cbody%3E"],"762904de405fa26afdd39e024395a441bc6a0f8a":[async_test('html5lib_tests16.html 762904de405fa26afdd39e024395a441bc6a0f8a'), "%3Cstyle%3E%3C%21--%3Cstyle%3E%3C/style%3E--%3E%3C/style%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cstyle%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"4df21df646897816e166b3dd829a8a3e21157f22":[async_test('html5lib_tests16.html 4df21df646897816e166b3dd829a8a3e21157f22'), "%3Cstyle%3E%3C%21--%3C/style%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"9c241f721c1f6677d736b37ecd86bb230df329df":[async_test('html5lib_tests16.html 9c241f721c1f6677d736b37ecd86bb230df329df'), "%3Cstyle%3E%3C%21--...%3C/style%3E...--%3E%3C/style%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--...%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22...--%3E%22"],"96ebedcc684f27f984cc7be467c908bdab2470a9":[async_test('html5lib_tests16.html 96ebedcc684f27f984cc7be467c908bdab2470a9'), "%3Cstyle%3E%3C%21--%3Cbr%3E%3Chtml%20xmlns%3Av%3D%22urn%3Aschemas-microsoft-com%3Avml%22%3E%3C%21--%5Bif%20%21mso%5D%3E%3Cstyle%3E%3C/style%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cbr%3E%3Chtml%20xmlns%3Av%3D%22urn%3Aschemas-microsoft-com%3Avml%22%3E%3C%21--%5Bif%20%21mso%5D%3E%3Cstyle%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"419cfbd87ad35c1d43214b122630cfde1c3ccf1b":[async_test('html5lib_tests16.html 419cfbd87ad35c1d43214b122630cfde1c3ccf1b'), "%3Cstyle%3E%3C%21--...%3Cstyle%3E%3C%21--...--%21%3E%3C/style%3E--%3E%3C/style%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--...%3Cstyle%3E%3C%21--...--%21%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"cf75ace0d7531b8daa271001dbf92b93b0b4490c":[async_test('html5lib_tests16.html cf75ace0d7531b8daa271001dbf92b93b0b4490c'), "%3Cstyle%3E%3C%21--...%3C/style%3E%3C%21--%20--%3E%3Cstyle%3E%40import%20...%3C/style%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--...%22%0A%7C%20%20%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%40import%20...%22%0A%7C%20%20%20%3Cbody%3E"],"a058b4bb03a689ce8528ed412d62cdd5bb879571":[async_test('html5lib_tests16.html a058b4bb03a689ce8528ed412d62cdd5bb879571'), "%3Cstyle%3E...%3Cstyle%3E%3C%21--...%3C/style%3E%3C%21--%20--%3E%3C/style%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22...%3Cstyle%3E%3C%21--...%22%0A%7C%20%20%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%3Cbody%3E"],"d7c871c41c9db40312ffc5af996ede62ecdfc579":[async_test('html5lib_tests16.html d7c871c41c9db40312ffc5af996ede62ecdfc579'), "%3Cstyle%3E...%3C%21--%5Bif%20IE%5D%3E%3Cstyle%3E...%3C/style%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22...%3C%21--%5Bif%20IE%5D%3E%3Cstyle%3E...%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"6c353ee8f48c1227eeab81279dc3eb3890c9c3bd":[async_test('html5lib_tests16.html 6c353ee8f48c1227eeab81279dc3eb3890c9c3bd'), "%3Ctitle%3E%3C%21--%3Ctitle%3E%3C/title%3E--%3E%3C/title%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ctitle%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"fea28aab54637701c5dfaef4f3fe64c72b272e1c":[async_test('html5lib_tests16.html fea28aab54637701c5dfaef4f3fe64c72b272e1c'), "%3Ctitle%3E%26lt%3B/title%3E%3C/title%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3C/title%3E%22%0A%7C%20%20%20%3Cbody%3E"],"ecd79b7eb7af2bb4dadf710f70e0d78f62adc40e":[async_test('html5lib_tests16.html ecd79b7eb7af2bb4dadf710f70e0d78f62adc40e'), "%3Ctitle%3Efoo/title%3E%3Clink%3E%3C/head%3E%3Cbody%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22foo/title%3E%3Clink%3E%3C/head%3E%3Cbody%3EX%22%0A%7C%20%20%20%3Cbody%3E"],"43917824cc1d9b5a65601f46e13a0779c3dcff4e":[async_test('html5lib_tests16.html 43917824cc1d9b5a65601f46e13a0779c3dcff4e'), "%3Cnoscript%3E%3C%21--%3Cnoscript%3E%3C/noscript%3E--%3E%3C/noscript%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cnoscript%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"5423bb28649f37e70a0559cba78c3b253a60c277":[async_test('html5lib_tests16.html 5423bb28649f37e70a0559cba78c3b253a60c277'), "%3Cnoscript%3E%3C%21--%3C/noscript%3EX%3Cnoscript%3E--%3E%3C/noscript%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22--%3E%22"],"2c091a50dfd31e766a5a629c0b7c21973e33319d":[async_test('html5lib_tests16.html 2c091a50dfd31e766a5a629c0b7c21973e33319d'), "%3Cnoscript%3E%3Ciframe%3E%3C/noscript%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22%3Ciframe%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"920feb4f9d1032dcec2abc5c526e4996f642968b":[async_test('html5lib_tests16.html 920feb4f9d1032dcec2abc5c526e4996f642968b'), "%3Cnoframes%3E%3C%21--%3Cnoframes%3E%3C/noframes%3E--%3E%3C/noframes%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cnoframes%3E%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"31b9b446263cd5b7a844d43f2a235ed9b0c53efb":[async_test('html5lib_tests16.html 31b9b446263cd5b7a844d43f2a235ed9b0c53efb'), "%3Cnoframes%3E%3Cbody%3E%3Cscript%3E%3C%21--...%3C/script%3E%3C/body%3E%3C/noframes%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%20%20%22%3Cbody%3E%3Cscript%3E%3C%21--...%3C/script%3E%3C/body%3E%22%0A%7C%20%20%20%3Cbody%3E"],"dbb5127246fee18718bcffc6cf0730674d12b98a":[async_test('html5lib_tests16.html dbb5127246fee18718bcffc6cf0730674d12b98a'), "%3Ctextarea%3E%3C%21--%3Ctextarea%3E%3C/textarea%3E--%3E%3C/textarea%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ctextarea%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"3872a4cfeba7651f0671de7e5f3922fd5053837b":[async_test('html5lib_tests16.html 3872a4cfeba7651f0671de7e5f3922fd5053837b'), "%3Ctextarea%3E%26lt%3B/textarea%3E%3C/textarea%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%3C/textarea%3E%22"],"2fbcb2db61b6416cdf46e0526e1929d146ab3da7":[async_test('html5lib_tests16.html 2fbcb2db61b6416cdf46e0526e1929d146ab3da7'), "%3Ciframe%3E%3C%21--%3Ciframe%3E%3C/iframe%3E--%3E%3C/iframe%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ciframe%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Ciframe%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"999da234e770bbf681a819423d04ea57415d9bbc":[async_test('html5lib_tests16.html 999da234e770bbf681a819423d04ea57415d9bbc'), "%3Ciframe%3E...%3C%21--X-%3E...%3C%21--/X-%3E...%3C/iframe%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ciframe%3E%0A%7C%20%20%20%20%20%20%20%22...%3C%21--X-%3E...%3C%21--/X-%3E...%22"],"6fc6be53a87bbb2d2a51a5617009063002321d09":[async_test('html5lib_tests16.html 6fc6be53a87bbb2d2a51a5617009063002321d09'), "%3Cxmp%3E%3C%21--%3Cxmp%3E%3C/xmp%3E--%3E%3C/xmp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cxmp%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cxmp%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"b0de72088fa3e543572329eda36fa4bd16e29fa3":[async_test('html5lib_tests16.html b0de72088fa3e543572329eda36fa4bd16e29fa3'), "%3Cnoembed%3E%3C%21--%3Cnoembed%3E%3C/noembed%3E--%3E%3C/noembed%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cnoembed%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%3Cnoembed%3E%22%0A%7C%20%20%20%20%20%22--%3E%22"],"2a6d8110b8148a9aa83db81dc38d544becea2fa9":[async_test('html5lib_tests16.html 2a6d8110b8148a9aa83db81dc38d544becea2fa9'), "%3C%21doctype%20html%3E%3Ctable%3E%0A", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22"],"6556aaaca3956eafbc1660bce50a2f3568f1bff6":[async_test('html5lib_tests16.html 6556aaaca3956eafbc1660bce50a2f3568f1bff6'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctd%3E%3Cspan%3E%3Cfont%3E%3C/span%3E%3Cspan%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E"],"8ba3d2c7712e9c0484a4cafc8b0ca6f35d8f11ed":[async_test('html5lib_tests16.html 8ba3d2c7712e9c0484a4cafc8b0ca6f35d8f11ed'), "%3C%21doctype%20html%3E%3Cform%3E%3Ctable%3E%3C/form%3E%3Cform%3E%3C/table%3E%3C/form%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cform%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cform%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests17.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests17.html
new file mode 100644
index 0000000000..6363ca72b2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests17.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests17.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['bad1cc428ca144d278d40343f9da4d22d43b7b50','8a99dcd95c28dc294ec302998adac16111085aea','1d2be91c91dc2506354f01bb0058d0e10123668a','37ef4643c5f11b68f8991c4f1b34c3fe4d78c0af','0739d340b754e400864a668ac7f72259965b8bc7','16cb95e820b074bad119f13b1204cac90db8cf82','8b6c1a5706c668ccc47a4ff61527a30daa14848e','92ebd54a48d986a555e1a456565b3cbd06c78ffa','1fdd1835ee0cb01351d30fc567cd19b3ecda7966','55412bdbb0fe315b9ff46592e8f08ba47568f2e9','d5e1450f92115f6c5704b0b5f27699cd418816f9','a3ed0cf6e162498c7c5b87228c33bbcd960087a6','c1af331cb6400571f5e2b6ec81461223ab603d3d',];
+ var tests = {
+ "bad1cc428ca144d278d40343f9da4d22d43b7b50":[async_test('html5lib_tests17.html bad1cc428ca144d278d40343f9da4d22d43b7b50'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctbody%3E%3Cselect%3E%3Ctr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"8a99dcd95c28dc294ec302998adac16111085aea":[async_test('html5lib_tests17.html 8a99dcd95c28dc294ec302998adac16111085aea'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3Cselect%3E%3Ctd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"1d2be91c91dc2506354f01bb0058d0e10123668a":[async_test('html5lib_tests17.html 1d2be91c91dc2506354f01bb0058d0e10123668a'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Ctd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"37ef4643c5f11b68f8991c4f1b34c3fe4d78c0af":[async_test('html5lib_tests17.html 37ef4643c5f11b68f8991c4f1b34c3fe4d78c0af'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3Cth%3E%3Cselect%3E%3Ctd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cth%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"0739d340b754e400864a668ac7f72259965b8bc7":[async_test('html5lib_tests17.html 0739d340b754e400864a668ac7f72259965b8bc7'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ccaption%3E%3Cselect%3E%3Ctr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"16cb95e820b074bad119f13b1204cac90db8cf82":[async_test('html5lib_tests17.html 16cb95e820b074bad119f13b1204cac90db8cf82'), "%3C%21doctype%20html%3E%3Cselect%3E%3Ctr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"8b6c1a5706c668ccc47a4ff61527a30daa14848e":[async_test('html5lib_tests17.html 8b6c1a5706c668ccc47a4ff61527a30daa14848e'), "%3C%21doctype%20html%3E%3Cselect%3E%3Ctd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"92ebd54a48d986a555e1a456565b3cbd06c78ffa":[async_test('html5lib_tests17.html 92ebd54a48d986a555e1a456565b3cbd06c78ffa'), "%3C%21doctype%20html%3E%3Cselect%3E%3Cth%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"1fdd1835ee0cb01351d30fc567cd19b3ecda7966":[async_test('html5lib_tests17.html 1fdd1835ee0cb01351d30fc567cd19b3ecda7966'), "%3C%21doctype%20html%3E%3Cselect%3E%3Ctbody%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"55412bdbb0fe315b9ff46592e8f08ba47568f2e9":[async_test('html5lib_tests17.html 55412bdbb0fe315b9ff46592e8f08ba47568f2e9'), "%3C%21doctype%20html%3E%3Cselect%3E%3Cthead%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"d5e1450f92115f6c5704b0b5f27699cd418816f9":[async_test('html5lib_tests17.html d5e1450f92115f6c5704b0b5f27699cd418816f9'), "%3C%21doctype%20html%3E%3Cselect%3E%3Ctfoot%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"a3ed0cf6e162498c7c5b87228c33bbcd960087a6":[async_test('html5lib_tests17.html a3ed0cf6e162498c7c5b87228c33bbcd960087a6'), "%3C%21doctype%20html%3E%3Cselect%3E%3Ccaption%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"c1af331cb6400571f5e2b6ec81461223ab603d3d":[async_test('html5lib_tests17.html c1af331cb6400571f5e2b6ec81461223ab603d3d'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3C/table%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%22a%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests18.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests18.html
new file mode 100644
index 0000000000..a17580471f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests18.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests18.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['7471f6a45872ac6d70f69fc3f4e10b13c7c1ac45','0d0085749435e0d0ddb56c9db809bfcbbc995767','9052b915187ac505be8958ab5e9f8d4ca0bfde81','66191ae802c83e7319dd5c747f3a46dd157f5919','12cece9ece2085b8073d668ddd8ebe1ddb39962c','bbbed0387a4534ef054a1f9fcf3c1cb15e12ab49','863c5dd5ef7e9279342de0f714383d3e47033e95','ddeaaff85a50ea415dee1c4ee6c43e98ea331953','61eb7572a2490ac82423953eefe8fcef8dbc78ee','df407b3f102bc2504539136b46afbb895763dc27','9ac591f40aae947707f7d5e83947712bbeca9574','d44cf9a5fcf0759fce78497c7f10e3019c361274','e4eb33f77ae641718853d2cfddbdb2eece6b266b','53ce5b102579af9830bf561b634af681bbdb5dfd','cd24d93d1235e4aabbdcfab1d3acdbe488325666','abae66ad61e145e32fb4fc4946b839f56b16bb3d','9d38e0731d08aec061003c7783c70e682221378b','9df08923a41bf58c6291f9ce6d9e36a29d336bd6','ecba80b891396c970db720681124a1cac2aea91f','c70f0b382961e8449567414fd541cc2ee0695eb9','bb5432233eeaeab374a545ad60bbd004c9d2c02b','e3980c50b81e2673e9ffbd8cc12300e975bd9175','4b6f10fa2d8b7cc70e3b3085aac46c64a0c42eaf','1e88e5946ba773f1202e8af27f2a17ef2658e3ee','17ec3aff2568b56687f00d6ee3aeb6625fdd8ecc','b22cb10082e7328708e1da334a12b015b90535a4','9bfd787aa3b30eb38ce7942696ccc01d991e8e52','41edb5b76ce7a4378daa093e59b1225af546864f','5c6c65bd01758ecb6eb539a979dfacf50ff93a78','76d96fecc3d88820bab5274089886d20bc164d74','28a8566daaae49c529298d0de8a26af1263ff625','13eb2279a1e58eb281541564ae945b87880ae7c2','3c7d4e068461b340966e07dff93d93503fcfa46c','d38ed6488066d3e0d4d74b29888c5d000c2f4808','43180045ad714b405e6fe110a27011f2644f16c2',];
+ var tests = {
+ "7471f6a45872ac6d70f69fc3f4e10b13c7c1ac45":[async_test('html5lib_tests18.html 7471f6a45872ac6d70f69fc3f4e10b13c7c1ac45'), "%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"0d0085749435e0d0ddb56c9db809bfcbbc995767":[async_test('html5lib_tests18.html 0d0085749435e0d0ddb56c9db809bfcbbc995767'), "%3C%21doctype%20html%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"9052b915187ac505be8958ab5e9f8d4ca0bfde81":[async_test('html5lib_tests18.html 9052b915187ac505be8958ab5e9f8d4ca0bfde81'), "%3C%21doctype%20html%3E%3Chtml%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"66191ae802c83e7319dd5c747f3a46dd157f5919":[async_test('html5lib_tests18.html 66191ae802c83e7319dd5c747f3a46dd157f5919'), "%3C%21doctype%20html%3E%3Chead%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"12cece9ece2085b8073d668ddd8ebe1ddb39962c":[async_test('html5lib_tests18.html 12cece9ece2085b8073d668ddd8ebe1ddb39962c'), "%3C%21doctype%20html%3E%3C/head%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"bbbed0387a4534ef054a1f9fcf3c1cb15e12ab49":[async_test('html5lib_tests18.html bbbed0387a4534ef054a1f9fcf3c1cb15e12ab49'), "%3C%21doctype%20html%3E%3Cbody%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"863c5dd5ef7e9279342de0f714383d3e47033e95":[async_test('html5lib_tests18.html 863c5dd5ef7e9279342de0f714383d3e47033e95'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"ddeaaff85a50ea415dee1c4ee6c43e98ea331953":[async_test('html5lib_tests18.html ddeaaff85a50ea415dee1c4ee6c43e98ea331953'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctbody%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E"],"61eb7572a2490ac82423953eefe8fcef8dbc78ee":[async_test('html5lib_tests18.html 61eb7572a2490ac82423953eefe8fcef8dbc78ee'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctbody%3E%3Ctr%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"df407b3f102bc2504539136b46afbb895763dc27":[async_test('html5lib_tests18.html df407b3f102bc2504539136b46afbb895763dc27'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctd%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"9ac591f40aae947707f7d5e83947712bbeca9574":[async_test('html5lib_tests18.html 9ac591f40aae947707f7d5e83947712bbeca9574'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ccaption%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"d44cf9a5fcf0759fce78497c7f10e3019c361274":[async_test('html5lib_tests18.html d44cf9a5fcf0759fce78497c7f10e3019c361274'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ccolgroup%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E"],"e4eb33f77ae641718853d2cfddbdb2eece6b266b":[async_test('html5lib_tests18.html e4eb33f77ae641718853d2cfddbdb2eece6b266b'), "%3C%21doctype%20html%3E%3Cselect%3E%3Cplaintext%3E%3C/plaintext%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"53ce5b102579af9830bf561b634af681bbdb5dfd":[async_test('html5lib_tests18.html 53ce5b102579af9830bf561b634af681bbdb5dfd'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cselect%3E%3Cplaintext%3Ea%3Ccaption%3Eb", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22"],"cd24d93d1235e4aabbdcfab1d3acdbe488325666":[async_test('html5lib_tests18.html cd24d93d1235e4aabbdcfab1d3acdbe488325666'), "%3C%21doctype%20html%3E%3Ctemplate%3E%3Cplaintext%3Ea%3C/template%3Eb", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctemplate%3E%0A%7C%20%20%20%20%20%20%20content%0A%7C%20%20%20%20%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%3C/template%3Eb%22%0A%7C%20%20%20%3Cbody%3E"],"abae66ad61e145e32fb4fc4946b839f56b16bb3d":[async_test('html5lib_tests18.html abae66ad61e145e32fb4fc4946b839f56b16bb3d'), "%3C%21doctype%20html%3E%3Cbody%3E%3C/body%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"9d38e0731d08aec061003c7783c70e682221378b":[async_test('html5lib_tests18.html 9d38e0731d08aec061003c7783c70e682221378b'), "%3C%21doctype%20html%3E%3Cframeset%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"9df08923a41bf58c6291f9ce6d9e36a29d336bd6":[async_test('html5lib_tests18.html 9df08923a41bf58c6291f9ce6d9e36a29d336bd6'), "%3C%21doctype%20html%3E%3Cframeset%3E%3C/frameset%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"ecba80b891396c970db720681124a1cac2aea91f":[async_test('html5lib_tests18.html ecba80b891396c970db720681124a1cac2aea91f'), "%3C%21doctype%20html%3E%3Cbody%3E%3C/body%3E%3C/html%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"c70f0b382961e8449567414fd541cc2ee0695eb9":[async_test('html5lib_tests18.html c70f0b382961e8449567414fd541cc2ee0695eb9'), "%3C%21doctype%20html%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"bb5432233eeaeab374a545ad60bbd004c9d2c02b":[async_test('html5lib_tests18.html bb5432233eeaeab374a545ad60bbd004c9d2c02b'), "%3C%21doctype%20html%3E%3Csvg%3E%3Cplaintext%3Ea%3C/plaintext%3Eb", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20plaintext%3E%0A%7C%20%20%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%22b%22"],"e3980c50b81e2673e9ffbd8cc12300e975bd9175":[async_test('html5lib_tests18.html e3980c50b81e2673e9ffbd8cc12300e975bd9175'), "%3C%21doctype%20html%3E%3Csvg%3E%3Ctitle%3E%3Cplaintext%3Ea%3C/plaintext%3Eb", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%3C/plaintext%3Eb%22"],"4b6f10fa2d8b7cc70e3b3085aac46c64a0c42eaf":[async_test('html5lib_tests18.html 4b6f10fa2d8b7cc70e3b3085aac46c64a0c42eaf'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3Cstyle%3E%3C/script%3E%3C/style%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22abc%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3C/script%3E%22"],"1e88e5946ba773f1202e8af27f2a17ef2658e3ee":[async_test('html5lib_tests18.html 1e88e5946ba773f1202e8af27f2a17ef2658e3ee'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3Cscript%3E%3C/style%3E%3C/script%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22abc%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3C/style%3E%22"],"17ec3aff2568b56687f00d6ee3aeb6625fdd8ecc":[async_test('html5lib_tests18.html 17ec3aff2568b56687f00d6ee3aeb6625fdd8ecc'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ccaption%3E%3Cstyle%3E%3C/script%3E%3C/style%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%3C/script%3E%22%0A%7C%20%20%20%20%20%20%20%20%20%22abc%22"],"b22cb10082e7328708e1da334a12b015b90535a4":[async_test('html5lib_tests18.html b22cb10082e7328708e1da334a12b015b90535a4'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctd%3E%3Cstyle%3E%3C/script%3E%3C/style%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%3C/script%3E%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22abc%22"],"9bfd787aa3b30eb38ce7942696ccc01d991e8e52":[async_test('html5lib_tests18.html 9bfd787aa3b30eb38ce7942696ccc01d991e8e52'), "%3C%21doctype%20html%3E%3Cselect%3E%3Cscript%3E%3C/style%3E%3C/script%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%3C/style%3E%22%0A%7C%20%20%20%20%20%20%20%22abc%22"],"41edb5b76ce7a4378daa093e59b1225af546864f":[async_test('html5lib_tests18.html 41edb5b76ce7a4378daa093e59b1225af546864f'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cselect%3E%3Cscript%3E%3C/style%3E%3C/script%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%3C/style%3E%22%0A%7C%20%20%20%20%20%20%20%22abc%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"5c6c65bd01758ecb6eb539a979dfacf50ff93a78":[async_test('html5lib_tests18.html 5c6c65bd01758ecb6eb539a979dfacf50ff93a78'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3Cselect%3E%3Cscript%3E%3C/style%3E%3C/script%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%3C/style%3E%22%0A%7C%20%20%20%20%20%20%20%22abc%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"76d96fecc3d88820bab5274089886d20bc164d74":[async_test('html5lib_tests18.html 76d96fecc3d88820bab5274089886d20bc164d74'), "%3C%21doctype%20html%3E%3Cframeset%3E%3C/frameset%3E%3Cnoframes%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%22abc%22"],"28a8566daaae49c529298d0de8a26af1263ff625":[async_test('html5lib_tests18.html 28a8566daaae49c529298d0de8a26af1263ff625'), "%3C%21doctype%20html%3E%3Cframeset%3E%3C/frameset%3E%3Cnoframes%3Eabc%3C/noframes%3E%3C%21--abc--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%22abc%22%0A%7C%20%20%20%3C%21--%20abc%20--%3E"],"13eb2279a1e58eb281541564ae945b87880ae7c2":[async_test('html5lib_tests18.html 13eb2279a1e58eb281541564ae945b87880ae7c2'), "%3C%21doctype%20html%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3Cnoframes%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%22abc%22"],"3c7d4e068461b340966e07dff93d93503fcfa46c":[async_test('html5lib_tests18.html 3c7d4e068461b340966e07dff93d93503fcfa46c'), "%3C%21doctype%20html%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3Cnoframes%3Eabc%3C/noframes%3E%3C%21--abc--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%22abc%22%0A%7C%20%3C%21--%20abc%20--%3E"],"d38ed6488066d3e0d4d74b29888c5d000c2f4808":[async_test('html5lib_tests18.html d38ed6488066d3e0d4d74b29888c5d000c2f4808'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctr%3E%3C/tbody%3E%3Ctfoot%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%3Ctfoot%3E"],"43180045ad714b405e6fe110a27011f2644f16c2":[async_test('html5lib_tests18.html 43180045ad714b405e6fe110a27011f2644f16c2'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctd%3E%3Csvg%3E%3C/svg%3Eabc%3Ctd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22abc%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests19.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests19.html
new file mode 100644
index 0000000000..529cf5885c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests19.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests19.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['6135e0cbdbb22a97e8a13c2442c3e9a9e0a53298','6b46dba2f4d7d1a08359ab21fe5e011463dd8746','bd558a6d89fae63fed9c0801e6fd8e8737bc8dc1','a7955e9f06178980cbc13fc4d548f196fef42b13','2fda53e44aa91cb475f8b1aa57e938adcce60d4d','9f55c21807de5c769197a9a2f29f836f08af050b','197605d0b406dbb3de884de6949237dd33669997','3b1730b917da1c33da80ee08d41573c44404c663','ac6b608079815ad00c84b291ad6715eec523ccf4','a68acf7673e0c886fcf8cf609f3a39fb36362de5','266157f051148b068dad52e9786a0bda96e851db','2fd40bb6048be379000d73bc52a50405ca99b356','19652e3b1ec783f279f527ddfb07073684520ab8','e5676878cff9332b572ebfd327e426f87e32ab4d','03bbba49b30ec908e06e3c84e1fcede1ac7508ff','5f9f25a089e72b4a4e5d7ebbacb440349c53f52f','d26f2542fc6fcee4e737b578c8db716a96a22ade','0e8c4b181993b477f4f215d5724b99ad04f18b2d','e3f1de111562f25106efad76583a3b6c0e3516ba','84db6521d1350830f3e46b7d9676527e1b4ee9e2','033d9b356b2a646471de009fa8a75e40500d2dd2','0971186f913b3c7f915817d2b82c73ea6487e932','5257f17c3ecab5b65d74d7c27f0fd492f82ef9b9','b8c3379e60829428188fd57e974ea4d8a7e7ec45','f9ab61cdf08301bcf02208165cc94e6bdbad746c','c6aa9952dcccdde3fc6d4ba104647f049d038e66','3984cbe166b42d77ff6dadc8e1687075db7a8e65','2d78ba34eecedf8261e9a9e3f8858baf00a0d960','8d45a57b7093df38c88dccb1f3fba6a62c810445','871917ed049ff21b35e74e39de2ca1eef6863562','74ad5d3026f1b3eb6553086eb2811eed4418e334','0b1e97138c4d8e8d770f107202e46a18362fd587','02624fcb7ddb77b15e9bc553d8392fc8cd031f68','a2579d287fd8111306e6dcd7248971a6baa4488d','acd1c9218828ea4367920c7a499c54de7f9a618f','36a6346b03982942b4427d5089271a101ebf1605','e58ed5b56f81d057c23be31e068a10e694f52adf','d422166b880468765f34d89075d4db9c4fcbfee9','fc673f5874c0cf1477554dfb733d6292cc01558b','0d2192c16cc224e770abff9e9e89e860aff9da78','d5941f6b68ef817d062636d0d61c24c68d0d86a2','259a085138224e3dfe0dceded2a043e72a8b6d76','869fff76cfe00ff33987de007ce98dc949bf8cbf','c06518cdbf3816faf265644d28d93b7aee9258f7','2fb0cdb3a9f69ca6c3d564e01f89d7173cd51295','138623cd07ccff1ce13bb1aa5dbaaadc962c414f','52e4a8422d6ef591f33f8686842d974dbaa33302','5ba1d0e97f121810025c1d6447a426c669661637','709ec07924db4c9fd66d48eac08575dfdef01d8f','f72edb80bc3be091e4b448cd1de8fe851d623e05','cb7de0dc7e17c1454ceaa7eb49cb9f9476a1f510','511bf4bae15c8119042e5e80b5358b70e7da26c1','c51ee24ef9d92206318cc5bbba784630cd10e531','819d39ae49fd04f0dfce90ac8ed2f8e423bb9329','ef6bdf5b3ba9d41d6c1c08ede60c2e7fb21c6d71','6bf9c038255c31f20d8fbeaebdf3609eb3c9ed75','08983ab2a54f61b09a1df134cdaf51d321f3ef32','2ef94ed028fa71b70540cf41f0884ae0c1cf8077','5c9ed202789fb29566e0d146d155e0d672e8a037','7d766253cbf500df03506dfb7f2cbdd0f0f533c6','4d5629c61993ef0bb83fcbd80ccc6ead7cd776ee','6320d9fa3295984d3da7e4831ced75dfb97d4848','bdb97868e951b7a719fa8b1afc79650bf9aae7a5','81868cde252694a252b6b0c8d366850f8a54736c','20651db48147f8dda189c563f5fa60bfd0913ac0','d098e8a39897cd8453fc18399622e413872b054c','0beb7903dcc0dbee206d8b6e729963a0461eedb3','78e5cdb86bf2aecef4697d70d50499b50de5c25b','cfb1d482e5971a6915c90434f580721b772fc09b','99d8bff8bbcca1c7faa7b6e10b9ff305b9f99594','daaa43db05ffe9bdd8d734a4389fb3c459caddf1','b3d421e57a4135731309152d7f47e26a4866f3d6','aa5eac4b7a0bafdafcd52cb190a8e181ff74144c','91d56a57f4c8af2bce03e9974df0a80f62686fb4','9cbad5cad719a63d2eb7721de6737a346ad42da1','057c2e91a7e6053d39436efe2bc29049f6fa8e82','61e6517e5e82cdaed41ab3e1f6fa927731aa0c82','9274ab4f08baa52c5d7016f6062f186730cfbe6c','fc2fa41ebadc21653b38d70c1097bd4ac5979948','473303e65f26fafacbaf01c11b04d745ff293963','7854276e1637619f693cd87f64542c08c35d40bd','a2d262d392e8d10645ce559edf401df3f3872eb3','d5e50987bf495e285e279ff8670255d9b1314f5d','be54d7506e54a127a05b115a8659a5b52fa57f6c','af389f1a8ef93c457560fd2445df80a6789dab0d','601dfa9502942deb8bf46a91c64f10f649bc1875','4b01442faf29563d3b736d235515f77c20c81863','696a2b60681b0b6758f165a67e29a84b0f75a153','7a48b98cbae5cd7cbbb90f138c0d12da6c4448b3','d7693a0f2be68925250d3aa2cf295b1d1a60bf94','9e4d91f02184de1b1e5d927144bb06d3bc78bb09','9655591702381a52fe0eb3224e63e2d8bfd735e8','fe9aa1c8ec32796e26f3e58022f0e42dc365b5c7','4c9ec04359c3e94d4a56d6932d289f0c4246d1ef','3fcc2f15951b3c3375c3e359cf7888c71187994a','a94b35c317763de75150df6b436c7c153aeb8c51','173b990198dd8fd9534d7808817c19440b75d406','48ed5945491d4ad0c00acfd01c2060a459436d34','392b8d4d25351467da961e4ab011a1d4e970b97a','bd14373173ea3617610fc8154237f59f1a810733','db3c4ddb389a2b8f42ca6b3719007d4de2eccc41','c1741a62627b2e5bb1cfa2b2cd667f3f9f76157e','0d6cf626790e9f06e30ceb6baf3004663b0247e7',];
+ var tests = {
+ "6135e0cbdbb22a97e8a13c2442c3e9a9e0a53298":[async_test('html5lib_tests19.html 6135e0cbdbb22a97e8a13c2442c3e9a9e0a53298'), "%3C%21doctype%20html%3E%3Cmath%3E%3Cmn%20DefinitionUrl%3D%22foo%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mn%3E%0A%7C%20%20%20%20%20%20%20%20%20definitionURL%3D%22foo%22"],"6b46dba2f4d7d1a08359ab21fe5e011463dd8746":[async_test('html5lib_tests19.html 6b46dba2f4d7d1a08359ab21fe5e011463dd8746'), "%3C%21doctype%20html%3E%3Chtml%3E%3C/p%3E%3C%21--foo--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3C%21--%20foo%20--%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"bd558a6d89fae63fed9c0801e6fd8e8737bc8dc1":[async_test('html5lib_tests19.html bd558a6d89fae63fed9c0801e6fd8e8737bc8dc1'), "%3C%21doctype%20html%3E%3Chead%3E%3C/head%3E%3C/p%3E%3C%21--foo--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3C%21--%20foo%20--%3E%0A%7C%20%20%20%3Cbody%3E"],"a7955e9f06178980cbc13fc4d548f196fef42b13":[async_test('html5lib_tests19.html a7955e9f06178980cbc13fc4d548f196fef42b13'), "%3C%21doctype%20html%3E%3Cbody%3E%3Cp%3E%3Cpre%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cpre%3E"],"2fda53e44aa91cb475f8b1aa57e938adcce60d4d":[async_test('html5lib_tests19.html 2fda53e44aa91cb475f8b1aa57e938adcce60d4d'), "%3C%21doctype%20html%3E%3Cbody%3E%3Cp%3E%3Clisting%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Clisting%3E"],"9f55c21807de5c769197a9a2f29f836f08af050b":[async_test('html5lib_tests19.html 9f55c21807de5c769197a9a2f29f836f08af050b'), "%3C%21doctype%20html%3E%3Cp%3E%3Cplaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E"],"197605d0b406dbb3de884de6949237dd33669997":[async_test('html5lib_tests19.html 197605d0b406dbb3de884de6949237dd33669997'), "%3C%21doctype%20html%3E%3Cp%3E%3Ch1%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Ch1%3E"],"3b1730b917da1c33da80ee08d41573c44404c663":[async_test('html5lib_tests19.html 3b1730b917da1c33da80ee08d41573c44404c663'), "%3C%21doctype%20html%3E%3Cisindex%20type%3D%22hidden%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cisindex%3E%0A%7C%20%20%20%20%20%20%20type%3D%22hidden%22"],"ac6b608079815ad00c84b291ad6715eec523ccf4":[async_test('html5lib_tests19.html ac6b608079815ad00c84b291ad6715eec523ccf4'), "%3C%21doctype%20html%3E%3Cruby%3E%3Cp%3E%3Crp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Crp%3E"],"a68acf7673e0c886fcf8cf609f3a39fb36362de5":[async_test('html5lib_tests19.html a68acf7673e0c886fcf8cf609f3a39fb36362de5'), "%3C%21doctype%20html%3E%3Cruby%3E%3Cdiv%3E%3Cspan%3E%3Crp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Crp%3E"],"266157f051148b068dad52e9786a0bda96e851db":[async_test('html5lib_tests19.html 266157f051148b068dad52e9786a0bda96e851db'), "%3C%21doctype%20html%3E%3Cruby%3E%3Cdiv%3E%3Cp%3E%3Crp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Crp%3E"],"2fd40bb6048be379000d73bc52a50405ca99b356":[async_test('html5lib_tests19.html 2fd40bb6048be379000d73bc52a50405ca99b356'), "%3C%21doctype%20html%3E%3Cruby%3E%3Cp%3E%3Crt%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Crt%3E"],"19652e3b1ec783f279f527ddfb07073684520ab8":[async_test('html5lib_tests19.html 19652e3b1ec783f279f527ddfb07073684520ab8'), "%3C%21doctype%20html%3E%3Cruby%3E%3Cdiv%3E%3Cspan%3E%3Crt%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Crt%3E"],"e5676878cff9332b572ebfd327e426f87e32ab4d":[async_test('html5lib_tests19.html e5676878cff9332b572ebfd327e426f87e32ab4d'), "%3C%21doctype%20html%3E%3Cruby%3E%3Cdiv%3E%3Cp%3E%3Crt%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Crt%3E"],"03bbba49b30ec908e06e3c84e1fcede1ac7508ff":[async_test('html5lib_tests19.html 03bbba49b30ec908e06e3c84e1fcede1ac7508ff'), "%3Chtml%3E%3Cruby%3Ea%3Crb%3Eb%3Crt%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E"],"5f9f25a089e72b4a4e5d7ebbacb440349c53f52f":[async_test('html5lib_tests19.html 5f9f25a089e72b4a4e5d7ebbacb440349c53f52f'), "%3Chtml%3E%3Cruby%3Ea%3Crp%3Eb%3Crt%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E"],"d26f2542fc6fcee4e737b578c8db716a96a22ade":[async_test('html5lib_tests19.html d26f2542fc6fcee4e737b578c8db716a96a22ade'), "%3Chtml%3E%3Cruby%3Ea%3Crt%3Eb%3Crt%3E%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Crt%3E"],"0e8c4b181993b477f4f215d5724b99ad04f18b2d":[async_test('html5lib_tests19.html 0e8c4b181993b477f4f215d5724b99ad04f18b2d'), "%3Chtml%3E%3Cruby%3Ea%3Crtc%3Eb%3Crt%3Ec%3Crb%3Ed%3C/ruby%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Crtc%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%20%20%3Crb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22d%22"],"e3f1de111562f25106efad76583a3b6c0e3516ba":[async_test('html5lib_tests19.html e3f1de111562f25106efad76583a3b6c0e3516ba'), "%3C%21doctype%20html%3E%3Cmath/%3E%3Cfoo%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%3Cfoo%3E"],"84db6521d1350830f3e46b7d9676527e1b4ee9e2":[async_test('html5lib_tests19.html 84db6521d1350830f3e46b7d9676527e1b4ee9e2'), "%3C%21doctype%20html%3E%3Csvg/%3E%3Cfoo%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Cfoo%3E"],"033d9b356b2a646471de009fa8a75e40500d2dd2":[async_test('html5lib_tests19.html 033d9b356b2a646471de009fa8a75e40500d2dd2'), "%3C%21doctype%20html%3E%3Cdiv%3E%3C/body%3E%3C%21--foo--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%3C%21--%20foo%20--%3E"],"0971186f913b3c7f915817d2b82c73ea6487e932":[async_test('html5lib_tests19.html 0971186f913b3c7f915817d2b82c73ea6487e932'), "%3C%21doctype%20html%3E%3Ch1%3E%3Cdiv%3E%3Ch3%3E%3Cspan%3E%3C/h1%3Efoo", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ch1%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ch3%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22"],"5257f17c3ecab5b65d74d7c27f0fd492f82ef9b9":[async_test('html5lib_tests19.html 5257f17c3ecab5b65d74d7c27f0fd492f82ef9b9'), "%3C%21doctype%20html%3E%3Cp%3E%3C/h3%3Efoo", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"b8c3379e60829428188fd57e974ea4d8a7e7ec45":[async_test('html5lib_tests19.html b8c3379e60829428188fd57e974ea4d8a7e7ec45'), "%3C%21doctype%20html%3E%3Ch3%3E%3Cli%3Eabc%3C/h2%3Efoo", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ch3%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%22abc%22%0A%7C%20%20%20%20%20%22foo%22"],"f9ab61cdf08301bcf02208165cc94e6bdbad746c":[async_test('html5lib_tests19.html f9ab61cdf08301bcf02208165cc94e6bdbad746c'), "%3C%21doctype%20html%3E%3Ctable%3Eabc%3C%21--foo--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22abc%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3C%21--%20foo%20--%3E"],"c6aa9952dcccdde3fc6d4ba104647f049d038e66":[async_test('html5lib_tests19.html c6aa9952dcccdde3fc6d4ba104647f049d038e66'), "%3C%21doctype%20html%3E%3Ctable%3E%20%20%3C%21--foo--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%22%20%20%22%0A%7C%20%20%20%20%20%20%20%3C%21--%20foo%20--%3E"],"3984cbe166b42d77ff6dadc8e1687075db7a8e65":[async_test('html5lib_tests19.html 3984cbe166b42d77ff6dadc8e1687075db7a8e65'), "%3C%21doctype%20html%3E%3Ctable%3E%20b%20%3C%21--foo--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%20b%20%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3C%21--%20foo%20--%3E"],"2d78ba34eecedf8261e9a9e3f8858baf00a0d960":[async_test('html5lib_tests19.html 2d78ba34eecedf8261e9a9e3f8858baf00a0d960'), "%3C%21doctype%20html%3E%3Cselect%3E%3Coption%3E%3Coption%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E"],"8d45a57b7093df38c88dccb1f3fba6a62c810445":[async_test('html5lib_tests19.html 8d45a57b7093df38c88dccb1f3fba6a62c810445'), "%3C%21doctype%20html%3E%3Cselect%3E%3Coption%3E%3C/optgroup%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E"],"871917ed049ff21b35e74e39de2ca1eef6863562":[async_test('html5lib_tests19.html 871917ed049ff21b35e74e39de2ca1eef6863562'), "%3C%21doctype%20html%3E%3Cdd%3E%3Coptgroup%3E%3Cdd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdd%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%3Cdd%3E"],"74ad5d3026f1b3eb6553086eb2811eed4418e334":[async_test('html5lib_tests19.html 74ad5d3026f1b3eb6553086eb2811eed4418e334'), "%3C%21doctype%20html%3E%3Cp%3E%3Cmath%3E%3Cmi%3E%3Cp%3E%3Ch1%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3E"],"0b1e97138c4d8e8d770f107202e46a18362fd587":[async_test('html5lib_tests19.html 0b1e97138c4d8e8d770f107202e46a18362fd587'), "%3C%21doctype%20html%3E%3Cp%3E%3Cmath%3E%3Cmo%3E%3Cp%3E%3Ch1%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3E"],"02624fcb7ddb77b15e9bc553d8392fc8cd031f68":[async_test('html5lib_tests19.html 02624fcb7ddb77b15e9bc553d8392fc8cd031f68'), "%3C%21doctype%20html%3E%3Cp%3E%3Cmath%3E%3Cmn%3E%3Cp%3E%3Ch1%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mn%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3E"],"a2579d287fd8111306e6dcd7248971a6baa4488d":[async_test('html5lib_tests19.html a2579d287fd8111306e6dcd7248971a6baa4488d'), "%3C%21doctype%20html%3E%3Cp%3E%3Cmath%3E%3Cms%3E%3Cp%3E%3Ch1%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20ms%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3E"],"acd1c9218828ea4367920c7a499c54de7f9a618f":[async_test('html5lib_tests19.html acd1c9218828ea4367920c7a499c54de7f9a618f'), "%3C%21doctype%20html%3E%3Cp%3E%3Cmath%3E%3Cmtext%3E%3Cp%3E%3Ch1%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3E"],"36a6346b03982942b4427d5089271a101ebf1605":[async_test('html5lib_tests19.html 36a6346b03982942b4427d5089271a101ebf1605'), "%3C%21doctype%20html%3E%3Cframeset%3E%3C/noframes%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"e58ed5b56f81d057c23be31e068a10e694f52adf":[async_test('html5lib_tests19.html e58ed5b56f81d057c23be31e068a10e694f52adf'), "%3C%21doctype%20html%3E%3Chtml%20c%3Dd%3E%3Cbody%3E%3C/html%3E%3Chtml%20a%3Db%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20a%3D%22b%22%0A%7C%20%20%20c%3D%22d%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"d422166b880468765f34d89075d4db9c4fcbfee9":[async_test('html5lib_tests19.html d422166b880468765f34d89075d4db9c4fcbfee9'), "%3C%21doctype%20html%3E%3Chtml%20c%3Dd%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3Chtml%20a%3Db%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20a%3D%22b%22%0A%7C%20%20%20c%3D%22d%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"fc673f5874c0cf1477554dfb733d6292cc01558b":[async_test('html5lib_tests19.html fc673f5874c0cf1477554dfb733d6292cc01558b'), "%3C%21doctype%20html%3E%3Chtml%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3C%21--foo--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%3C%21--%20foo%20--%3E"],"0d2192c16cc224e770abff9e9e89e860aff9da78":[async_test('html5lib_tests19.html 0d2192c16cc224e770abff9e9e89e860aff9da78'), "%3C%21doctype%20html%3E%3Chtml%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%20%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%20%20%22"],"d5941f6b68ef817d062636d0d61c24c68d0d86a2":[async_test('html5lib_tests19.html d5941f6b68ef817d062636d0d61c24c68d0d86a2'), "%3C%21doctype%20html%3E%3Chtml%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"259a085138224e3dfe0dceded2a043e72a8b6d76":[async_test('html5lib_tests19.html 259a085138224e3dfe0dceded2a043e72a8b6d76'), "%3C%21doctype%20html%3E%3Chtml%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3Cp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"869fff76cfe00ff33987de007ce98dc949bf8cbf":[async_test('html5lib_tests19.html 869fff76cfe00ff33987de007ce98dc949bf8cbf'), "%3C%21doctype%20html%3E%3Chtml%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3C/p%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"c06518cdbf3816faf265644d28d93b7aee9258f7":[async_test('html5lib_tests19.html c06518cdbf3816faf265644d28d93b7aee9258f7'), "%3Chtml%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%3C%21doctype%20html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"2fb0cdb3a9f69ca6c3d564e01f89d7173cd51295":[async_test('html5lib_tests19.html 2fb0cdb3a9f69ca6c3d564e01f89d7173cd51295'), "%3C%21doctype%20html%3E%3Cbody%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"138623cd07ccff1ce13bb1aa5dbaaadc962c414f":[async_test('html5lib_tests19.html 138623cd07ccff1ce13bb1aa5dbaaadc962c414f'), "%3C%21doctype%20html%3E%3Cp%3E%3Cframeset%3E%3Cframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E"],"52e4a8422d6ef591f33f8686842d974dbaa33302":[async_test('html5lib_tests19.html 52e4a8422d6ef591f33f8686842d974dbaa33302'), "%3C%21doctype%20html%3E%3Cp%3Ea%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22a%22"],"5ba1d0e97f121810025c1d6447a426c669661637":[async_test('html5lib_tests19.html 5ba1d0e97f121810025c1d6447a426c669661637'), "%3C%21doctype%20html%3E%3Cp%3E%20%3Cframeset%3E%3Cframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E"],"709ec07924db4c9fd66d48eac08575dfdef01d8f":[async_test('html5lib_tests19.html 709ec07924db4c9fd66d48eac08575dfdef01d8f'), "%3C%21doctype%20html%3E%3Cpre%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E"],"f72edb80bc3be091e4b448cd1de8fe851d623e05":[async_test('html5lib_tests19.html f72edb80bc3be091e4b448cd1de8fe851d623e05'), "%3C%21doctype%20html%3E%3Clisting%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Clisting%3E"],"cb7de0dc7e17c1454ceaa7eb49cb9f9476a1f510":[async_test('html5lib_tests19.html cb7de0dc7e17c1454ceaa7eb49cb9f9476a1f510'), "%3C%21doctype%20html%3E%3Cli%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cli%3E"],"511bf4bae15c8119042e5e80b5358b70e7da26c1":[async_test('html5lib_tests19.html 511bf4bae15c8119042e5e80b5358b70e7da26c1'), "%3C%21doctype%20html%3E%3Cdd%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdd%3E"],"c51ee24ef9d92206318cc5bbba784630cd10e531":[async_test('html5lib_tests19.html c51ee24ef9d92206318cc5bbba784630cd10e531'), "%3C%21doctype%20html%3E%3Cdt%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdt%3E"],"819d39ae49fd04f0dfce90ac8ed2f8e423bb9329":[async_test('html5lib_tests19.html 819d39ae49fd04f0dfce90ac8ed2f8e423bb9329'), "%3C%21doctype%20html%3E%3Cbutton%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbutton%3E"],"ef6bdf5b3ba9d41d6c1c08ede60c2e7fb21c6d71":[async_test('html5lib_tests19.html ef6bdf5b3ba9d41d6c1c08ede60c2e7fb21c6d71'), "%3C%21doctype%20html%3E%3Capplet%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Capplet%3E"],"6bf9c038255c31f20d8fbeaebdf3609eb3c9ed75":[async_test('html5lib_tests19.html 6bf9c038255c31f20d8fbeaebdf3609eb3c9ed75'), "%3C%21doctype%20html%3E%3Cmarquee%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmarquee%3E"],"08983ab2a54f61b09a1df134cdaf51d321f3ef32":[async_test('html5lib_tests19.html 08983ab2a54f61b09a1df134cdaf51d321f3ef32'), "%3C%21doctype%20html%3E%3Cobject%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cobject%3E"],"2ef94ed028fa71b70540cf41f0884ae0c1cf8077":[async_test('html5lib_tests19.html 2ef94ed028fa71b70540cf41f0884ae0c1cf8077'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"5c9ed202789fb29566e0d146d155e0d672e8a037":[async_test('html5lib_tests19.html 5c9ed202789fb29566e0d146d155e0d672e8a037'), "%3C%21doctype%20html%3E%3Carea%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Carea%3E"],"7d766253cbf500df03506dfb7f2cbdd0f0f533c6":[async_test('html5lib_tests19.html 7d766253cbf500df03506dfb7f2cbdd0f0f533c6'), "%3C%21doctype%20html%3E%3Cbasefont%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cbasefont%3E%0A%7C%20%20%20%3Cframeset%3E"],"4d5629c61993ef0bb83fcbd80ccc6ead7cd776ee":[async_test('html5lib_tests19.html 4d5629c61993ef0bb83fcbd80ccc6ead7cd776ee'), "%3C%21doctype%20html%3E%3Cbgsound%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cbgsound%3E%0A%7C%20%20%20%3Cframeset%3E"],"6320d9fa3295984d3da7e4831ced75dfb97d4848":[async_test('html5lib_tests19.html 6320d9fa3295984d3da7e4831ced75dfb97d4848'), "%3C%21doctype%20html%3E%3Cbr%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbr%3E"],"bdb97868e951b7a719fa8b1afc79650bf9aae7a5":[async_test('html5lib_tests19.html bdb97868e951b7a719fa8b1afc79650bf9aae7a5'), "%3C%21doctype%20html%3E%3Cembed%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cembed%3E"],"81868cde252694a252b6b0c8d366850f8a54736c":[async_test('html5lib_tests19.html 81868cde252694a252b6b0c8d366850f8a54736c'), "%3C%21doctype%20html%3E%3Cimg%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cimg%3E"],"20651db48147f8dda189c563f5fa60bfd0913ac0":[async_test('html5lib_tests19.html 20651db48147f8dda189c563f5fa60bfd0913ac0'), "%3C%21doctype%20html%3E%3Cinput%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cinput%3E"],"d098e8a39897cd8453fc18399622e413872b054c":[async_test('html5lib_tests19.html d098e8a39897cd8453fc18399622e413872b054c'), "%3C%21doctype%20html%3E%3Ckeygen%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ckeygen%3E"],"0beb7903dcc0dbee206d8b6e729963a0461eedb3":[async_test('html5lib_tests19.html 0beb7903dcc0dbee206d8b6e729963a0461eedb3'), "%3C%21doctype%20html%3E%3Cwbr%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cwbr%3E"],"78e5cdb86bf2aecef4697d70d50499b50de5c25b":[async_test('html5lib_tests19.html 78e5cdb86bf2aecef4697d70d50499b50de5c25b'), "%3C%21doctype%20html%3E%3Chr%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Chr%3E"],"cfb1d482e5971a6915c90434f580721b772fc09b":[async_test('html5lib_tests19.html cfb1d482e5971a6915c90434f580721b772fc09b'), "%3C%21doctype%20html%3E%3Ctextarea%3E%3C/textarea%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E"],"99d8bff8bbcca1c7faa7b6e10b9ff305b9f99594":[async_test('html5lib_tests19.html 99d8bff8bbcca1c7faa7b6e10b9ff305b9f99594'), "%3C%21doctype%20html%3E%3Cxmp%3E%3C/xmp%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cxmp%3E"],"daaa43db05ffe9bdd8d734a4389fb3c459caddf1":[async_test('html5lib_tests19.html daaa43db05ffe9bdd8d734a4389fb3c459caddf1'), "%3C%21doctype%20html%3E%3Ciframe%3E%3C/iframe%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ciframe%3E"],"b3d421e57a4135731309152d7f47e26a4866f3d6":[async_test('html5lib_tests19.html b3d421e57a4135731309152d7f47e26a4866f3d6'), "%3C%21doctype%20html%3E%3Cselect%3E%3C/select%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"aa5eac4b7a0bafdafcd52cb190a8e181ff74144c":[async_test('html5lib_tests19.html aa5eac4b7a0bafdafcd52cb190a8e181ff74144c'), "%3C%21doctype%20html%3E%3Csvg%3E%3C/svg%3E%3Cframeset%3E%3Cframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E"],"91d56a57f4c8af2bce03e9974df0a80f62686fb4":[async_test('html5lib_tests19.html 91d56a57f4c8af2bce03e9974df0a80f62686fb4'), "%3C%21doctype%20html%3E%3Cmath%3E%3C/math%3E%3Cframeset%3E%3Cframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E"],"9cbad5cad719a63d2eb7721de6737a346ad42da1":[async_test('html5lib_tests19.html 9cbad5cad719a63d2eb7721de6737a346ad42da1'), "%3C%21doctype%20html%3E%3Csvg%3E%3CforeignObject%3E%3Cdiv%3E%20%3Cframeset%3E%3Cframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E"],"057c2e91a7e6053d39436efe2bc29049f6fa8e82":[async_test('html5lib_tests19.html 057c2e91a7e6053d39436efe2bc29049f6fa8e82'), "%3C%21doctype%20html%3E%3Csvg%3Ea%3C/svg%3E%3Cframeset%3E%3Cframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22a%22"],"61e6517e5e82cdaed41ab3e1f6fa927731aa0c82":[async_test('html5lib_tests19.html 61e6517e5e82cdaed41ab3e1f6fa927731aa0c82'), "%3C%21doctype%20html%3E%3Csvg%3E%20%3C/svg%3E%3Cframeset%3E%3Cframe%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3Cframe%3E"],"9274ab4f08baa52c5d7016f6062f186730cfbe6c":[async_test('html5lib_tests19.html 9274ab4f08baa52c5d7016f6062f186730cfbe6c'), "%3Chtml%3Eaaa%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22aaa%22"],"fc2fa41ebadc21653b38d70c1097bd4ac5979948":[async_test('html5lib_tests19.html fc2fa41ebadc21653b38d70c1097bd4ac5979948'), "%3Chtml%3E%20a%20%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22a%20%22"],"473303e65f26fafacbaf01c11b04d745ff293963":[async_test('html5lib_tests19.html 473303e65f26fafacbaf01c11b04d745ff293963'), "%3C%21doctype%20html%3E%3Cdiv%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"7854276e1637619f693cd87f64542c08c35d40bd":[async_test('html5lib_tests19.html 7854276e1637619f693cd87f64542c08c35d40bd'), "%3C%21doctype%20html%3E%3Cdiv%3E%3Cbody%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"a2d262d392e8d10645ce559edf401df3f3872eb3":[async_test('html5lib_tests19.html a2d262d392e8d10645ce559edf401df3f3872eb3'), "%3C%21doctype%20html%3E%3Cp%3E%3Cmath%3E%3C/p%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%22a%22"],"d5e50987bf495e285e279ff8670255d9b1314f5d":[async_test('html5lib_tests19.html d5e50987bf495e285e279ff8670255d9b1314f5d'), "%3C%21doctype%20html%3E%3Cp%3E%3Cmath%3E%3Cmn%3E%3Cspan%3E%3C/p%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mn%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"be54d7506e54a127a05b115a8659a5b52fa57f6c":[async_test('html5lib_tests19.html be54d7506e54a127a05b115a8659a5b52fa57f6c'), "%3C%21doctype%20html%3E%3Cmath%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E"],"af389f1a8ef93c457560fd2445df80a6789dab0d":[async_test('html5lib_tests19.html af389f1a8ef93c457560fd2445df80a6789dab0d'), "%3C%21doctype%20html%3E%3Cmeta%20charset%3D%22ascii%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%20%20charset%3D%22ascii%22%0A%7C%20%20%20%3Cbody%3E"],"601dfa9502942deb8bf46a91c64f10f649bc1875":[async_test('html5lib_tests19.html 601dfa9502942deb8bf46a91c64f10f649bc1875'), "%3C%21doctype%20html%3E%3Cmeta%20http-equiv%3D%22content-type%22%20content%3D%22text/html%3Bcharset%3Dascii%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%20%20content%3D%22text/html%3Bcharset%3Dascii%22%0A%7C%20%20%20%20%20%20%20http-equiv%3D%22content-type%22%0A%7C%20%20%20%3Cbody%3E"],"4b01442faf29563d3b736d235515f77c20c81863":[async_test('html5lib_tests19.html 4b01442faf29563d3b736d235515f77c20c81863'), "%3C%21doctype%20html%3E%3Chead%3E%3C%21--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--%3E%3Cmeta%20charset%3D%22utf8%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3C%21--%20aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%20--%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%20%20charset%3D%22utf8%22%0A%7C%20%20%20%3Cbody%3E"],"696a2b60681b0b6758f165a67e29a84b0f75a153":[async_test('html5lib_tests19.html 696a2b60681b0b6758f165a67e29a84b0f75a153'), "%3C%21doctype%20html%3E%3Chtml%20a%3Db%3E%3Chead%3E%3C/head%3E%3Chtml%20c%3Dd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20a%3D%22b%22%0A%7C%20%20%20c%3D%22d%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"7a48b98cbae5cd7cbbb90f138c0d12da6c4448b3":[async_test('html5lib_tests19.html 7a48b98cbae5cd7cbbb90f138c0d12da6c4448b3'), "%3C%21doctype%20html%3E%3Cimage/%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cimg%3E"],"d7693a0f2be68925250d3aa2cf295b1d1a60bf94":[async_test('html5lib_tests19.html d7693a0f2be68925250d3aa2cf295b1d1a60bf94'), "%3C%21doctype%20html%3Ea%3Ci%3Eb%3Ctable%3Ec%3Cb%3Ed%3C/i%3Ee%3C/b%3Ef", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22bc%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22de%22%0A%7C%20%20%20%20%20%20%20%22f%22%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],"9e4d91f02184de1b1e5d927144bb06d3bc78bb09":[async_test('html5lib_tests19.html 9e4d91f02184de1b1e5d927144bb06d3bc78bb09'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ci%3Ea%3Cb%3Eb%3Cdiv%3Ec%3Ca%3Ed%3C/i%3Ee%3C/b%3Ef", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22d%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22e%22%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%22f%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"9655591702381a52fe0eb3224e63e2d8bfd735e8":[async_test('html5lib_tests19.html 9655591702381a52fe0eb3224e63e2d8bfd735e8'), "%3C%21doctype%20html%3E%3Ci%3Ea%3Cb%3Eb%3Cdiv%3Ec%3Ca%3Ed%3C/i%3Ee%3C/b%3Ef", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22d%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22e%22%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%22f%22"],"fe9aa1c8ec32796e26f3e58022f0e42dc365b5c7":[async_test('html5lib_tests19.html fe9aa1c8ec32796e26f3e58022f0e42dc365b5c7'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ci%3Ea%3Cb%3Eb%3Cdiv%3Ec%3C/i%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"4c9ec04359c3e94d4a56d6932d289f0c4246d1ef":[async_test('html5lib_tests19.html 4c9ec04359c3e94d4a56d6932d289f0c4246d1ef'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ci%3Ea%3Cdiv%3Eb%3Ctr%3Ec%3Cb%3Ed%3C/i%3Ee", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22d%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22e%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"3fcc2f15951b3c3375c3e359cf7888c71187994a":[async_test('html5lib_tests19.html 3fcc2f15951b3c3375c3e359cf7888c71187994a'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctd%3E%3Ctable%3E%3Ci%3Ea%3Cdiv%3Eb%3Cb%3Ec%3C/i%3Ed", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22d%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E"],"a94b35c317763de75150df6b436c7c153aeb8c51":[async_test('html5lib_tests19.html a94b35c317763de75150df6b436c7c153aeb8c51'), "%3C%21doctype%20html%3E%3Cbody%3E%3Cbgsound%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbgsound%3E"],"173b990198dd8fd9534d7808817c19440b75d406":[async_test('html5lib_tests19.html 173b990198dd8fd9534d7808817c19440b75d406'), "%3C%21doctype%20html%3E%3Cbody%3E%3Cbasefont%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbasefont%3E"],"48ed5945491d4ad0c00acfd01c2060a459436d34":[async_test('html5lib_tests19.html 48ed5945491d4ad0c00acfd01c2060a459436d34'), "%3C%21doctype%20html%3E%3Ca%3E%3Cb%3E%3C/a%3E%3Cbasefont%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cbasefont%3E"],"392b8d4d25351467da961e4ab011a1d4e970b97a":[async_test('html5lib_tests19.html 392b8d4d25351467da961e4ab011a1d4e970b97a'), "%3C%21doctype%20html%3E%3Ca%3E%3Cb%3E%3C/a%3E%3Cbgsound%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cbgsound%3E"],"bd14373173ea3617610fc8154237f59f1a810733":[async_test('html5lib_tests19.html bd14373173ea3617610fc8154237f59f1a810733'), "%3C%21doctype%20html%3E%3Cfigcaption%3E%3Carticle%3E%3C/figcaption%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfigcaption%3E%0A%7C%20%20%20%20%20%20%20%3Carticle%3E%0A%7C%20%20%20%20%20%22a%22"],"db3c4ddb389a2b8f42ca6b3719007d4de2eccc41":[async_test('html5lib_tests19.html db3c4ddb389a2b8f42ca6b3719007d4de2eccc41'), "%3C%21doctype%20html%3E%3Csummary%3E%3Carticle%3E%3C/summary%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csummary%3E%0A%7C%20%20%20%20%20%20%20%3Carticle%3E%0A%7C%20%20%20%20%20%22a%22"],"c1741a62627b2e5bb1cfa2b2cd667f3f9f76157e":[async_test('html5lib_tests19.html c1741a62627b2e5bb1cfa2b2cd667f3f9f76157e'), "%3C%21doctype%20html%3E%3Cp%3E%3Ca%3E%3Cplaintext%3Eb", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%22b%22"],"0d6cf626790e9f06e30ceb6baf3004663b0247e7":[async_test('html5lib_tests19.html 0d6cf626790e9f06e30ceb6baf3004663b0247e7'), "%3C%21DOCTYPE%20html%3E%3Cdiv%3Ea%3Ca%3E%3C/div%3Eb%3Cp%3Ec%3C/p%3Ed", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%22b%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22c%22%0A%7C%20%20%20%20%20%20%20%22d%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests2.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests2.html
new file mode 100644
index 0000000000..eddf34c2ff
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests2.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['e070301fb578bd639ecbc7ec720fa60222d05826','aaf24dabcb42470e447d241a40def0d136c12b93','b6c1142484570bb90c36e454ee193cca17bb618a','1977644a94de1a04245dfef3f3db69c7ac41aa6f','fb0d7207ed9fbc3fe5d1e0f85ad18c247dfb05a3','d662798ca6dd95a205069658396d0f3d78237233','1ec72d53c4c68f9ca56f037ec53a7dc261886131','2d58ae67534b42e52e34c6b2a275fcb30a878008','14836de42a7fb86b75fef03f08823f90d389b7f3','8ced679aed45a123b97a574f24fba909b65f94dd','932ff3ff2c75f7b28ef562dfa9c7cb208f0712d4','c863d867f843bd66c5303db1634931a36afd3ea9','2221f89de75008a31506b22756a5499bc6bda9bd','7471f6a45872ac6d70f69fc3f4e10b13c7c1ac45','47b9eaef1b5aad0e3963a8d415236fed12702d65','decb4ad6eac317f262b4b87c86b33d2d9d700e75','ffcb1856faa7e09cc892c0f5a4d3353716830784','a259db8ee062d858027148f92811ba0f5796e4b9','bf369032d1e6ebb52ab133e4c4b8c2e872349843','73b97cd984a62703ec54ec4a876ec32aa5fd3b8c','2db9616ed62fc2a26056f3395459869cf556974d','b59aa1c714892618eaccd51696658887fcbd2045','98818e7fda2506603bd208662613edb40297c2d3','e0c43080cf61c0696031bdb097bea4f2a647cfc2','f7753d80a422c40b5fa04d99e52d8ae83369757a','7cbd584aef9508a90c98f80040078149a92ec869','e0f7f130b1e3653dd06f10f3492e4f0bf4cd3cfa','4fb9b13eeb5d8a201884c572764a70477822f2d4','7ec82c93064966931ce76618a0cce4f275aafdaf','a8fb1cffc8683f0c47fdf9963e0d52c627343ff0','8dc47e70b94f2bea514ceaa51153ec1beeeda7ef','571719c0f9e1dae32ef993917b02c57f698be3d9','7f3afa5785d4b7ea37f8bae17226528f2a30e818','37918d1876724d3a8980920cf4cf2cbef2c3ac06','5da4e202a8962cacf567ce864873ddbff73f8217','4ca566310edc49450571677e8ef195883919ec2f','8a559c045c3a880e555d31de4dd3aa0b06930b73','3067a820b0195f9c08b8d0fe1dd7f8d800e10779','e1011849d36ebf9d1577c53d940a75c462dcb1e7','c9938e14b139e9c2af300bacd38f2f3cfca3fe58','0582a2e2c0eb00e0ba60b280187006c5e7de6991','478db7eafb3ac4a6abb8dbe083664c8d3ada35d8','c6abe422542794d7e8196d73283e562c309fe2e3','9fd0577023d0eb3662569333f5f231090439a217','54033c6b90b549fabfc15ad346ce9b985fa45fdc','0c917166dc089cb23a100af2f07cbf95f164533a','60f3ef7971b3259c3d800da672d886b2db778276','341bdf232d96b774988ee3163c953f2581752335','84570bfd25f23f0f40e31ba0c6a08906a2676b6d','4dbef924230e654860aa288a28f6304a062b3faf','2e8a5d6aa8cb0011b6caa08a44cd8871e4b15b71','06e43760aeadae330ad5ba80c4b93952ba568b29','4e58f3f3c581dec50f939a660fd5b5828396dac4','693974a6cb0defd3e0b2d63b31d420f39c83d262','3e03ddf29af0af9c9ece091251f0c1c5e08a5e41','9a5211623fcdd9fc3ad2ea4addc608d7c2574b90','39f31f0fbfcc91157104d64ca081d4271bc7e838','86d793db69ce071e78a18c85f8345316f09e1790','182036d2ef28f86873aee09b15125c828179c1b4','2a818d5fd74c60ac2bb369fb2355b84edab31777','9f88d21c8b77696f7238064a4ee87931cc16a03f','1d00919bf0b2493dfee7422a24acee9026de5fff','0c48a9e7584ede9d13d606057202883c5cff3eab',];
+ var tests = {
+ "e070301fb578bd639ecbc7ec720fa60222d05826":[async_test('html5lib_tests2.html e070301fb578bd639ecbc7ec720fa60222d05826'), "%3C%21DOCTYPE%20html%3ETest", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Test%22"],"aaf24dabcb42470e447d241a40def0d136c12b93":[async_test('html5lib_tests2.html aaf24dabcb42470e447d241a40def0d136c12b93'), "%3Ctextarea%3Etest%3C/div%3Etest", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22test%3C/div%3Etest%22"],"b6c1142484570bb90c36e454ee193cca17bb618a":[async_test('html5lib_tests2.html b6c1142484570bb90c36e454ee193cca17bb618a'), "%3Ctable%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"1977644a94de1a04245dfef3f3db69c7ac41aa6f":[async_test('html5lib_tests2.html 1977644a94de1a04245dfef3f3db69c7ac41aa6f'), "%3Ctable%3E%3Ctd%3Etest%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22test%22"],"fb0d7207ed9fbc3fe5d1e0f85ad18c247dfb05a3":[async_test('html5lib_tests2.html fb0d7207ed9fbc3fe5d1e0f85ad18c247dfb05a3'), "%3Cframe%3Etest", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22test%22"],"d662798ca6dd95a205069658396d0f3d78237233":[async_test('html5lib_tests2.html d662798ca6dd95a205069658396d0f3d78237233'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3Etest", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"1ec72d53c4c68f9ca56f037ec53a7dc261886131":[async_test('html5lib_tests2.html 1ec72d53c4c68f9ca56f037ec53a7dc261886131'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3E%20te%20st", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%22%20%20%22"],"2d58ae67534b42e52e34c6b2a275fcb30a878008":[async_test('html5lib_tests2.html 2d58ae67534b42e52e34c6b2a275fcb30a878008'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3E%3C/frameset%3E%20te%20st", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%20%20%22"],"14836de42a7fb86b75fef03f08823f90d389b7f3":[async_test('html5lib_tests2.html 14836de42a7fb86b75fef03f08823f90d389b7f3'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3E%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"8ced679aed45a123b97a574f24fba909b65f94dd":[async_test('html5lib_tests2.html 8ced679aed45a123b97a574f24fba909b65f94dd'), "%3C%21DOCTYPE%20html%3E%3Cfont%3E%3Cp%3E%3Cb%3Etest%3C/font%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22test%22"],"932ff3ff2c75f7b28ef562dfa9c7cb208f0712d4":[async_test('html5lib_tests2.html 932ff3ff2c75f7b28ef562dfa9c7cb208f0712d4'), "%3C%21DOCTYPE%20html%3E%3Cdt%3E%3Cdiv%3E%3Cdd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdt%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%3Cdd%3E"],"c863d867f843bd66c5303db1634931a36afd3ea9":[async_test('html5lib_tests2.html c863d867f843bd66c5303db1634931a36afd3ea9'), "%3Cscript%3E%3C/x", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C/x%22%0A%7C%20%20%20%3Cbody%3E"],"2221f89de75008a31506b22756a5499bc6bda9bd":[async_test('html5lib_tests2.html 2221f89de75008a31506b22756a5499bc6bda9bd'), "%3Ctable%3E%3Cplaintext%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3Ctd%3E%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"7471f6a45872ac6d70f69fc3f4e10b13c7c1ac45":[async_test('html5lib_tests2.html 7471f6a45872ac6d70f69fc3f4e10b13c7c1ac45'), "%3Cplaintext%3E%3C/plaintext%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3C/plaintext%3E%22"],"47b9eaef1b5aad0e3963a8d415236fed12702d65":[async_test('html5lib_tests2.html 47b9eaef1b5aad0e3963a8d415236fed12702d65'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ctr%3ETEST", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22TEST%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"decb4ad6eac317f262b4b87c86b33d2d9d700e75":[async_test('html5lib_tests2.html decb4ad6eac317f262b4b87c86b33d2d9d700e75'), "%3C%21DOCTYPE%20html%3E%3Cbody%20t1%3D1%3E%3Cbody%20t2%3D2%3E%3Cbody%20t3%3D3%20t4%3D4%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20t1%3D%221%22%0A%7C%20%20%20%20%20t2%3D%222%22%0A%7C%20%20%20%20%20t3%3D%223%22%0A%7C%20%20%20%20%20t4%3D%224%22"],"ffcb1856faa7e09cc892c0f5a4d3353716830784":[async_test('html5lib_tests2.html ffcb1856faa7e09cc892c0f5a4d3353716830784'), "%3C/b%20test", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"a259db8ee062d858027148f92811ba0f5796e4b9":[async_test('html5lib_tests2.html a259db8ee062d858027148f92811ba0f5796e4b9'), "%3C%21DOCTYPE%20html%3E%3C/b%20test%3Cb%20%26%3D%26amp%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"bf369032d1e6ebb52ab133e4c4b8c2e872349843":[async_test('html5lib_tests2.html bf369032d1e6ebb52ab133e4c4b8c2e872349843'), "%3C%21doctypehtml%3E%3CscrIPt%20type%3Dtext/x-foobar%3Bbaz%3EX%3C/SCRipt", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20type%3D%22text/x-foobar%3Bbaz%22%0A%7C%20%20%20%20%20%20%20%22X%3C/SCRipt%22%0A%7C%20%20%20%3Cbody%3E"],"73b97cd984a62703ec54ec4a876ec32aa5fd3b8c":[async_test('html5lib_tests2.html 73b97cd984a62703ec54ec4a876ec32aa5fd3b8c'), "%26", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26%22"],"2db9616ed62fc2a26056f3395459869cf556974d":[async_test('html5lib_tests2.html 2db9616ed62fc2a26056f3395459869cf556974d'), "%26%23", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26%23%22"],"b59aa1c714892618eaccd51696658887fcbd2045":[async_test('html5lib_tests2.html b59aa1c714892618eaccd51696658887fcbd2045'), "%26%23X", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26%23X%22"],"98818e7fda2506603bd208662613edb40297c2d3":[async_test('html5lib_tests2.html 98818e7fda2506603bd208662613edb40297c2d3'), "%26%23x", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26%23x%22"],"e0c43080cf61c0696031bdb097bea4f2a647cfc2":[async_test('html5lib_tests2.html e0c43080cf61c0696031bdb097bea4f2a647cfc2'), "%26%2345", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22-%22"],"f7753d80a422c40b5fa04d99e52d8ae83369757a":[async_test('html5lib_tests2.html f7753d80a422c40b5fa04d99e52d8ae83369757a'), "%26x-test", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26x-test%22"],"7cbd584aef9508a90c98f80040078149a92ec869":[async_test('html5lib_tests2.html 7cbd584aef9508a90c98f80040078149a92ec869'), "%3C%21doctypehtml%3E%3Cp%3E%3Cli%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cli%3E"],"e0f7f130b1e3653dd06f10f3492e4f0bf4cd3cfa":[async_test('html5lib_tests2.html e0f7f130b1e3653dd06f10f3492e4f0bf4cd3cfa'), "%3C%21doctypehtml%3E%3Cp%3E%3Cdt%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cdt%3E"],"4fb9b13eeb5d8a201884c572764a70477822f2d4":[async_test('html5lib_tests2.html 4fb9b13eeb5d8a201884c572764a70477822f2d4'), "%3C%21doctypehtml%3E%3Cp%3E%3Cdd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cdd%3E"],"7ec82c93064966931ce76618a0cce4f275aafdaf":[async_test('html5lib_tests2.html 7ec82c93064966931ce76618a0cce4f275aafdaf'), "%3C%21doctypehtml%3E%3Cp%3E%3Cform%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cform%3E"],"a8fb1cffc8683f0c47fdf9963e0d52c627343ff0":[async_test('html5lib_tests2.html a8fb1cffc8683f0c47fdf9963e0d52c627343ff0'), "%3C%21DOCTYPE%20html%3E%3Cp%3E%3C/P%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%22X%22"],"8dc47e70b94f2bea514ceaa51153ec1beeeda7ef":[async_test('html5lib_tests2.html 8dc47e70b94f2bea514ceaa51153ec1beeeda7ef'), "%26AMP", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26%22"],"571719c0f9e1dae32ef993917b02c57f698be3d9":[async_test('html5lib_tests2.html 571719c0f9e1dae32ef993917b02c57f698be3d9'), "%26AMp%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%26AMp%3B%22"],"7f3afa5785d4b7ea37f8bae17226528f2a30e818":[async_test('html5lib_tests2.html 7f3afa5785d4b7ea37f8bae17226528f2a30e818'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3CthisISasillyTESTelementNameToMakeSureCrazyTagNamesArePARSEDcorrectLY%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cthisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly%3E"],"37918d1876724d3a8980920cf4cf2cbef2c3ac06":[async_test('html5lib_tests2.html 37918d1876724d3a8980920cf4cf2cbef2c3ac06'), "%3C%21DOCTYPE%20html%3EX%3C/body%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22XX%22"],"5da4e202a8962cacf567ce864873ddbff73f8217":[async_test('html5lib_tests2.html 5da4e202a8962cacf567ce864873ddbff73f8217'), "%3C%21DOCTYPE%20html%3E%3C%21--%20X", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3C%21--%20%20X%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"4ca566310edc49450571677e8ef195883919ec2f":[async_test('html5lib_tests2.html 4ca566310edc49450571677e8ef195883919ec2f'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ccaption%3Etest%20TEST%3C/caption%3E%3Ctd%3Etest", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%22test%20TEST%22%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22test%22"],"8a559c045c3a880e555d31de4dd3aa0b06930b73":[async_test('html5lib_tests2.html 8a559c045c3a880e555d31de4dd3aa0b06930b73'), "%3C%21DOCTYPE%20html%3E%3Cselect%3E%3Coption%3E%3Coptgroup%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E"],"3067a820b0195f9c08b8d0fe1dd7f8d800e10779":[async_test('html5lib_tests2.html 3067a820b0195f9c08b8d0fe1dd7f8d800e10779'), "%3C%21DOCTYPE%20html%3E%3Cselect%3E%3Coptgroup%3E%3Coption%3E%3C/optgroup%3E%3Coption%3E%3Cselect%3E%3Coption%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%3Coption%3E"],"e1011849d36ebf9d1577c53d940a75c462dcb1e7":[async_test('html5lib_tests2.html e1011849d36ebf9d1577c53d940a75c462dcb1e7'), "%3C%21DOCTYPE%20html%3E%3Cselect%3E%3Coptgroup%3E%3Coption%3E%3Coptgroup%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E"],"c9938e14b139e9c2af300bacd38f2f3cfca3fe58":[async_test('html5lib_tests2.html c9938e14b139e9c2af300bacd38f2f3cfca3fe58'), "%3C%21DOCTYPE%20html%3E%3Cdatalist%3E%3Coption%3Efoo%3C/datalist%3Ebar", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdatalist%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%22bar%22"],"0582a2e2c0eb00e0ba60b280187006c5e7de6991":[async_test('html5lib_tests2.html 0582a2e2c0eb00e0ba60b280187006c5e7de6991'), "%3C%21DOCTYPE%20html%3E%3Cfont%3E%3Cinput%3E%3Cinput%3E%3C/font%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%3Cinput%3E"],"478db7eafb3ac4a6abb8dbe083664c8d3ada35d8":[async_test('html5lib_tests2.html 478db7eafb3ac4a6abb8dbe083664c8d3ada35d8'), "%3C%21DOCTYPE%20html%3E%3C%21--%20XXX%20-%20XXX%20--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3C%21--%20%20XXX%20-%20XXX%20%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"c6abe422542794d7e8196d73283e562c309fe2e3":[async_test('html5lib_tests2.html c6abe422542794d7e8196d73283e562c309fe2e3'), "%3C%21DOCTYPE%20html%3E%3C%21--%20XXX%20-%20XXX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3C%21--%20%20XXX%20-%20XXX%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"9fd0577023d0eb3662569333f5f231090439a217":[async_test('html5lib_tests2.html 9fd0577023d0eb3662569333f5f231090439a217'), "%3C%21DOCTYPE%20html%3E%3C%21--%20XXX%20-%20XXX%20-%20XXX%20--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3C%21--%20%20XXX%20-%20XXX%20-%20XXX%20%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"54033c6b90b549fabfc15ad346ce9b985fa45fdc":[async_test('html5lib_tests2.html 54033c6b90b549fabfc15ad346ce9b985fa45fdc'), "%3C%21DOCTYPE%20html%3E%20%3C%21DOCTYPE%20html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"0c917166dc089cb23a100af2f07cbf95f164533a":[async_test('html5lib_tests2.html 0c917166dc089cb23a100af2f07cbf95f164533a'), "test%0Atest", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22test%0Atest%22"],"60f3ef7971b3259c3d800da672d886b2db778276":[async_test('html5lib_tests2.html 60f3ef7971b3259c3d800da672d886b2db778276'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctitle%3Etest%3C/body%3E%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22test%3C/body%3E%22"],"341bdf232d96b774988ee3163c953f2581752335":[async_test('html5lib_tests2.html 341bdf232d96b774988ee3163c953f2581752335'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctitle%3EX%3C/title%3E%3Cmeta%20name%3Dz%3E%3Clink%20rel%3Dfoo%3E%3Cstyle%3E%0Ax%20%7B%20content%3A%22%3C/style%22%20%7D%20%3C/style%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%20%20name%3D%22z%22%0A%7C%20%20%20%20%20%3Clink%3E%0A%7C%20%20%20%20%20%20%20rel%3D%22foo%22%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%0Ax%20%7B%20content%3A%22%3C/style%22%20%7D%20%22"],"84570bfd25f23f0f40e31ba0c6a08906a2676b6d":[async_test('html5lib_tests2.html 84570bfd25f23f0f40e31ba0c6a08906a2676b6d'), "%3C%21DOCTYPE%20html%3E%3Cselect%3E%3Coptgroup%3E%3C/optgroup%3E%3C/select%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E"],"4dbef924230e654860aa288a28f6304a062b3faf":[async_test('html5lib_tests2.html 4dbef924230e654860aa288a28f6304a062b3faf'), "%20%0A%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"2e8a5d6aa8cb0011b6caa08a44cd8871e4b15b71":[async_test('html5lib_tests2.html 2e8a5d6aa8cb0011b6caa08a44cd8871e4b15b71'), "%3C%21DOCTYPE%20html%3E%20%20%3Chtml%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"06e43760aeadae330ad5ba80c4b93952ba568b29":[async_test('html5lib_tests2.html 06e43760aeadae330ad5ba80c4b93952ba568b29'), "%3C%21DOCTYPE%20html%3E%3Cscript%3E%0A%3C/script%3E%20%20%3Ctitle%3Ex%3C/title%3E%20%20%3C/head%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%22%20%20%22%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%22%20%20%22%0A%7C%20%20%20%3Cbody%3E"],"4e58f3f3c581dec50f939a660fd5b5828396dac4":[async_test('html5lib_tests2.html 4e58f3f3c581dec50f939a660fd5b5828396dac4'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Cbody%3E%3Chtml%20id%3Dx%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20id%3D%22x%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"693974a6cb0defd3e0b2d63b31d420f39c83d262":[async_test('html5lib_tests2.html 693974a6cb0defd3e0b2d63b31d420f39c83d262'), "%3C%21DOCTYPE%20html%3EX%3C/body%3E%3Chtml%20id%3D%22x%22%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20id%3D%22x%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"3e03ddf29af0af9c9ece091251f0c1c5e08a5e41":[async_test('html5lib_tests2.html 3e03ddf29af0af9c9ece091251f0c1c5e08a5e41'), "%3C%21DOCTYPE%20html%3E%3Chead%3E%3Chtml%20id%3Dx%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20id%3D%22x%22%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"9a5211623fcdd9fc3ad2ea4addc608d7c2574b90":[async_test('html5lib_tests2.html 9a5211623fcdd9fc3ad2ea4addc608d7c2574b90'), "%3C%21DOCTYPE%20html%3EX%3C/html%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22XX%22"],"39f31f0fbfcc91157104d64ca081d4271bc7e838":[async_test('html5lib_tests2.html 39f31f0fbfcc91157104d64ca081d4271bc7e838'), "%3C%21DOCTYPE%20html%3EX%3C/html%3E%20", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%20%22"],"86d793db69ce071e78a18c85f8345316f09e1790":[async_test('html5lib_tests2.html 86d793db69ce071e78a18c85f8345316f09e1790'), "%3C%21DOCTYPE%20html%3EX%3C/html%3E%3Cp%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"182036d2ef28f86873aee09b15125c828179c1b4":[async_test('html5lib_tests2.html 182036d2ef28f86873aee09b15125c828179c1b4'), "%3C%21DOCTYPE%20html%3EX%3Cp/x/y/z%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20x%3D%22%22%0A%7C%20%20%20%20%20%20%20y%3D%22%22%0A%7C%20%20%20%20%20%20%20z%3D%22%22"],"2a818d5fd74c60ac2bb369fb2355b84edab31777":[async_test('html5lib_tests2.html 2a818d5fd74c60ac2bb369fb2355b84edab31777'), "%3C%21DOCTYPE%20html%3E%3C%21--x--", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3C%21--%20x%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"9f88d21c8b77696f7238064a4ee87931cc16a03f":[async_test('html5lib_tests2.html 9f88d21c8b77696f7238064a4ee87931cc16a03f'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3C/p%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E"],"1d00919bf0b2493dfee7422a24acee9026de5fff":[async_test('html5lib_tests2.html 1d00919bf0b2493dfee7422a24acee9026de5fff'), "%3C%21DOCTYPE%20%3C%21DOCTYPE%20HTML%3E%3E%3C%21--%3C%21--x--%3E--%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20%3C%21doctype%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%3E%22%0A%7C%20%20%20%20%20%3C%21--%20%3C%21--x%20--%3E%0A%7C%20%20%20%20%20%22--%3E%22"],"0c48a9e7584ede9d13d606057202883c5cff3eab":[async_test('html5lib_tests2.html 0c48a9e7584ede9d13d606057202883c5cff3eab'), "%3C%21doctype%20html%3E%3Cdiv%3E%3Cform%3E%3C/form%3E%3Cdiv%3E%3C/div%3E%3C/div%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cform%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests20.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests20.html
new file mode 100644
index 0000000000..1bda41f46b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests20.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests20.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['cbb5f28bedf4361156ef3c9c37e314c6479be137','e108a4952601b4664098344189c875934ea2f7ab','1ba644a7934a55e4303effe43cae1517ee723e13','8999351d1342a9bcf670dc27eb52aa786d916916','8ecd0d9e2580f78ff920821bd334bd8d04d165e5','e6bf9f94617ae9cb7b663b2c9123a37730600e7f','c5c9728bb5634f47780efefa09f79419db4257c0','5bb84e30031c925aed6eabca8c477d547d522dbb','f1b6f41fa8856b408c3c398b420cbdc40cda555e','d5ea955e51df6ee9c7801d28dfcd85402b8fa710','27f2f34d4101ac4877edf0be8c4065e240aef15c','dcb10e242981a00dc3f6f874ec33318d6f532fff','49781179c56e3e0d4891dfff219a0beda3b86d24','86ac23e2828bb147966ee06690f6051345ce312c','b4eda259a29352b6f8f9cd989e1a0e881793814e','af0e7b907c6791a73f00b4421fa7fe7df6fe07ee','29c71813f1cd5f1ee092548211c8ffaf2d04b4a9','92547b083c29d3ea0ba5304953d254c357bcf11a','ff10749b8b1aca1fba5d07ba86f49612be43caf9','67bcf2e6d67c89c4d5d9c605faa6e5b03e77693f','1e284d72f2de23e31280a0df936043340597d3d0','323a6d9f8d2f74e7e5b88599a87dc5673fb7a0b4','2dd72ef48808b29f998fdd3df6ce7ce18b8753e4','c9e061e2d34063857d7332c66ff0d26beb2aced7','bae8a941fceb91845187cb545c8e02b03af5570d','bb36fb89d118b55c66f17b24ffff0ca09ccc939b','3fed75e1a7f46a6e2a74a078bfd955e7de05de4f','8cb9d8f92ba130618646d6fd1f307ca73f4e4add','2b9e6cb75876b4a33b77ae4b1bd3ef5e73c4a40e','f95c6bf3f935fad9fc354f4cf6e0161d6ebfb08a','0b07b135bf8399742cc9e6f3d14b2ea32e3354d3','076d3588e85ce9566d9575d1564e72e8480fadc8','dfad8fc515d74c19bad1ab72e7a03d59a11c7778','7ff774f225899091d6d64e5512ada13ce32dd72a','2fd8cab5e3cc4635be76e2d430ad6fe6e79b8167','c7399a2e531694b47b436bd153823f60bba9071e','bc4092e0e1aaa4b97464005963345b4b38e1f2a1','d85e49fc5be07a699f3930464996d7ddb6a8e8bd','09cf43bae3a4d875da56c67a46cf79102b20b456','f10ab9901d14c36846c9e6848abce19877f83094','aa2a707a627408758a0ee845896ef088d6136013','bb0737ce6cdbbcc055771e2400afda545d58778f','454bb6d0471536f7b91793ddbc786761b5733902','3c8ff013bf2e80392671760c8d444730b1923c0a','9a4f0cd0bdc672beb1c1c821599a11fc8a3d139d','f5ca3a789b7451da3e6030443aee6fae734fc7a1','ca8826661175c3a1ff7c6f1ebbfe646b238deb75','9e30ae7bbd1193deb0d3599c071960d395ca01f7','4417ddedee8e753fd6e911192458168d3b77d1e8','2de333e449ca13c462832e799e13795e2cf6e3e4','23d4d0aee62db4adc7381010d80776270a6db47b','5463426618ff46188d19595aa1c7fdd86c6909b3','2649b566bd2bccc59813286d647269ad251e2fdf','5df7907b99976e4051d1dd433e2bdbeb4ce200e3','a7e70930137ff8338778253ac93dc66f11eef966','b92100bddc318e13996fbe96c087c35152cbb4e3','f55b68de8cf9ec17e0512a6caae94e0fbf151260','7fb0d4342e3ed3fabee650b83ce66ffb2f66c173','72a6100cd60dd49f780168137ed09e27a8090b34','09ee3f414d08c8a3923e7c72380868617d1a4554','3b854aa3549b6f5cac96176766fccd25c965b5fe','bbc36dce52efacdede3cf4d94582ace2a3fca9fc','d4b924764bf49ceb39aa4f63dc81b7c05bbda84d','19a6c1a25813323193c67a54904a41e389918f48',];
+ var tests = {
+ "cbb5f28bedf4361156ef3c9c37e314c6479be137":[async_test('html5lib_tests20.html cbb5f28bedf4361156ef3c9c37e314c6479be137'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cbutton%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E"],"e108a4952601b4664098344189c875934ea2f7ab":[async_test('html5lib_tests20.html e108a4952601b4664098344189c875934ea2f7ab'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Caddress%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Caddress%3E"],"1ba644a7934a55e4303effe43cae1517ee723e13":[async_test('html5lib_tests20.html 1ba644a7934a55e4303effe43cae1517ee723e13'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Carticle%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Carticle%3E"],"8999351d1342a9bcf670dc27eb52aa786d916916":[async_test('html5lib_tests20.html 8999351d1342a9bcf670dc27eb52aa786d916916'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Caside%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Caside%3E"],"8ecd0d9e2580f78ff920821bd334bd8d04d165e5":[async_test('html5lib_tests20.html 8ecd0d9e2580f78ff920821bd334bd8d04d165e5'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cblockquote%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cblockquote%3E"],"e6bf9f94617ae9cb7b663b2c9123a37730600e7f":[async_test('html5lib_tests20.html e6bf9f94617ae9cb7b663b2c9123a37730600e7f'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Ccenter%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccenter%3E"],"c5c9728bb5634f47780efefa09f79419db4257c0":[async_test('html5lib_tests20.html c5c9728bb5634f47780efefa09f79419db4257c0'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cdetails%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdetails%3E"],"5bb84e30031c925aed6eabca8c477d547d522dbb":[async_test('html5lib_tests20.html 5bb84e30031c925aed6eabca8c477d547d522dbb'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cdialog%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdialog%3E"],"f1b6f41fa8856b408c3c398b420cbdc40cda555e":[async_test('html5lib_tests20.html f1b6f41fa8856b408c3c398b420cbdc40cda555e'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cdir%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdir%3E"],"d5ea955e51df6ee9c7801d28dfcd85402b8fa710":[async_test('html5lib_tests20.html d5ea955e51df6ee9c7801d28dfcd85402b8fa710'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cdiv%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"27f2f34d4101ac4877edf0be8c4065e240aef15c":[async_test('html5lib_tests20.html 27f2f34d4101ac4877edf0be8c4065e240aef15c'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cdl%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdl%3E"],"dcb10e242981a00dc3f6f874ec33318d6f532fff":[async_test('html5lib_tests20.html dcb10e242981a00dc3f6f874ec33318d6f532fff'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cfieldset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfieldset%3E"],"49781179c56e3e0d4891dfff219a0beda3b86d24":[async_test('html5lib_tests20.html 49781179c56e3e0d4891dfff219a0beda3b86d24'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cfigcaption%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfigcaption%3E"],"86ac23e2828bb147966ee06690f6051345ce312c":[async_test('html5lib_tests20.html 86ac23e2828bb147966ee06690f6051345ce312c'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cfigure%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfigure%3E"],"b4eda259a29352b6f8f9cd989e1a0e881793814e":[async_test('html5lib_tests20.html b4eda259a29352b6f8f9cd989e1a0e881793814e'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cfooter%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfooter%3E"],"af0e7b907c6791a73f00b4421fa7fe7df6fe07ee":[async_test('html5lib_tests20.html af0e7b907c6791a73f00b4421fa7fe7df6fe07ee'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cheader%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cheader%3E"],"29c71813f1cd5f1ee092548211c8ffaf2d04b4a9":[async_test('html5lib_tests20.html 29c71813f1cd5f1ee092548211c8ffaf2d04b4a9'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Chgroup%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Chgroup%3E"],"92547b083c29d3ea0ba5304953d254c357bcf11a":[async_test('html5lib_tests20.html 92547b083c29d3ea0ba5304953d254c357bcf11a'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cmain%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmain%3E"],"ff10749b8b1aca1fba5d07ba86f49612be43caf9":[async_test('html5lib_tests20.html ff10749b8b1aca1fba5d07ba86f49612be43caf9'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cmenu%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmenu%3E"],"67bcf2e6d67c89c4d5d9c605faa6e5b03e77693f":[async_test('html5lib_tests20.html 67bcf2e6d67c89c4d5d9c605faa6e5b03e77693f'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cnav%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnav%3E"],"1e284d72f2de23e31280a0df936043340597d3d0":[async_test('html5lib_tests20.html 1e284d72f2de23e31280a0df936043340597d3d0'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Col%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Col%3E"],"323a6d9f8d2f74e7e5b88599a87dc5673fb7a0b4":[async_test('html5lib_tests20.html 323a6d9f8d2f74e7e5b88599a87dc5673fb7a0b4'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E"],"2dd72ef48808b29f998fdd3df6ce7ce18b8753e4":[async_test('html5lib_tests20.html 2dd72ef48808b29f998fdd3df6ce7ce18b8753e4'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Csearch%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csearch%3E"],"c9e061e2d34063857d7332c66ff0d26beb2aced7":[async_test('html5lib_tests20.html c9e061e2d34063857d7332c66ff0d26beb2aced7'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Csection%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csection%3E"],"bae8a941fceb91845187cb545c8e02b03af5570d":[async_test('html5lib_tests20.html bae8a941fceb91845187cb545c8e02b03af5570d'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Csummary%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csummary%3E"],"bb36fb89d118b55c66f17b24ffff0ca09ccc939b":[async_test('html5lib_tests20.html bb36fb89d118b55c66f17b24ffff0ca09ccc939b'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cul%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cul%3E"],"3fed75e1a7f46a6e2a74a078bfd955e7de05de4f":[async_test('html5lib_tests20.html 3fed75e1a7f46a6e2a74a078bfd955e7de05de4f'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Ch1%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ch1%3E"],"8cb9d8f92ba130618646d6fd1f307ca73f4e4add":[async_test('html5lib_tests20.html 8cb9d8f92ba130618646d6fd1f307ca73f4e4add'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Ch6%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ch6%3E"],"2b9e6cb75876b4a33b77ae4b1bd3ef5e73c4a40e":[async_test('html5lib_tests20.html 2b9e6cb75876b4a33b77ae4b1bd3ef5e73c4a40e'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Clisting%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Clisting%3E"],"f95c6bf3f935fad9fc354f4cf6e0161d6ebfb08a":[async_test('html5lib_tests20.html f95c6bf3f935fad9fc354f4cf6e0161d6ebfb08a'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cpre%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cpre%3E"],"0b07b135bf8399742cc9e6f3d14b2ea32e3354d3":[async_test('html5lib_tests20.html 0b07b135bf8399742cc9e6f3d14b2ea32e3354d3'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cform%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cform%3E"],"076d3588e85ce9566d9575d1564e72e8480fadc8":[async_test('html5lib_tests20.html 076d3588e85ce9566d9575d1564e72e8480fadc8'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cli%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cli%3E"],"dfad8fc515d74c19bad1ab72e7a03d59a11c7778":[async_test('html5lib_tests20.html dfad8fc515d74c19bad1ab72e7a03d59a11c7778'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cdd%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdd%3E"],"7ff774f225899091d6d64e5512ada13ce32dd72a":[async_test('html5lib_tests20.html 7ff774f225899091d6d64e5512ada13ce32dd72a'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cdt%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdt%3E"],"2fd8cab5e3cc4635be76e2d430ad6fe6e79b8167":[async_test('html5lib_tests20.html 2fd8cab5e3cc4635be76e2d430ad6fe6e79b8167'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cplaintext%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cplaintext%3E"],"c7399a2e531694b47b436bd153823f60bba9071e":[async_test('html5lib_tests20.html c7399a2e531694b47b436bd153823f60bba9071e'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Ctable%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctable%3E"],"bc4092e0e1aaa4b97464005963345b4b38e1f2a1":[async_test('html5lib_tests20.html bc4092e0e1aaa4b97464005963345b4b38e1f2a1'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Chr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Chr%3E"],"d85e49fc5be07a699f3930464996d7ddb6a8e8bd":[async_test('html5lib_tests20.html d85e49fc5be07a699f3930464996d7ddb6a8e8bd'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3Cxmp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cxmp%3E"],"09cf43bae3a4d875da56c67a46cf79102b20b456":[async_test('html5lib_tests20.html 09cf43bae3a4d875da56c67a46cf79102b20b456'), "%3C%21doctype%20html%3E%3Cp%3E%3Cbutton%3E%3C/p%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E"],"f10ab9901d14c36846c9e6848abce19877f83094":[async_test('html5lib_tests20.html f10ab9901d14c36846c9e6848abce19877f83094'), "%3C%21doctype%20html%3E%3Cbutton%3E%3Cp%3E%3C/button%3Ex", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%22x%22"],"aa2a707a627408758a0ee845896ef088d6136013":[async_test('html5lib_tests20.html aa2a707a627408758a0ee845896ef088d6136013'), "%3C%21doctype%20html%3E%3Caddress%3E%3Cbutton%3E%3C/address%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Caddress%3E%0A%7C%20%20%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%22a%22"],"bb0737ce6cdbbcc055771e2400afda545d58778f":[async_test('html5lib_tests20.html bb0737ce6cdbbcc055771e2400afda545d58778f'), "%3Cp%3E%3Ctable%3E%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],"454bb6d0471536f7b91793ddbc786761b5733902":[async_test('html5lib_tests20.html 454bb6d0471536f7b91793ddbc786761b5733902'), "%3C%21doctype%20html%3E%3Csvg%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"3c8ff013bf2e80392671760c8d444730b1923c0a":[async_test('html5lib_tests20.html 3c8ff013bf2e80392671760c8d444730b1923c0a'), "%3C%21doctype%20html%3E%3Cp%3E%3Cfigcaption%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cfigcaption%3E"],"9a4f0cd0bdc672beb1c1c821599a11fc8a3d139d":[async_test('html5lib_tests20.html 9a4f0cd0bdc672beb1c1c821599a11fc8a3d139d'), "%3C%21doctype%20html%3E%3Cp%3E%3Csummary%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Csummary%3E"],"f5ca3a789b7451da3e6030443aee6fae734fc7a1":[async_test('html5lib_tests20.html f5ca3a789b7451da3e6030443aee6fae734fc7a1'), "%3C%21doctype%20html%3E%3Cform%3E%3Ctable%3E%3Cform%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cform%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],"ca8826661175c3a1ff7c6f1ebbfe646b238deb75":[async_test('html5lib_tests20.html ca8826661175c3a1ff7c6f1ebbfe646b238deb75'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cform%3E%3Cform%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cform%3E"],"9e30ae7bbd1193deb0d3599c071960d395ca01f7":[async_test('html5lib_tests20.html 9e30ae7bbd1193deb0d3599c071960d395ca01f7'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cform%3E%3C/table%3E%3Cform%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cform%3E"],"4417ddedee8e753fd6e911192458168d3b77d1e8":[async_test('html5lib_tests20.html 4417ddedee8e753fd6e911192458168d3b77d1e8'), "%3C%21doctype%20html%3E%3Csvg%3E%3CforeignObject%3E%3Cp%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E"],"2de333e449ca13c462832e799e13795e2cf6e3e4":[async_test('html5lib_tests20.html 2de333e449ca13c462832e799e13795e2cf6e3e4'), "%3C%21doctype%20html%3E%3Csvg%3E%3Ctitle%3Eabc", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E%0A%7C%20%20%20%20%20%20%20%20%20%22abc%22"],"23d4d0aee62db4adc7381010d80776270a6db47b":[async_test('html5lib_tests20.html 23d4d0aee62db4adc7381010d80776270a6db47b'), "%3Coption%3E%3Cspan%3E%3Coption%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E"],"5463426618ff46188d19595aa1c7fdd86c6909b3":[async_test('html5lib_tests20.html 5463426618ff46188d19595aa1c7fdd86c6909b3'), "%3Coption%3E%3Coption%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%3Coption%3E"],"2649b566bd2bccc59813286d647269ad251e2fdf":[async_test('html5lib_tests20.html 2649b566bd2bccc59813286d647269ad251e2fdf'), "%3Cmath%3E%3Cannotation-xml%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"5df7907b99976e4051d1dd433e2bdbeb4ce200e3":[async_test('html5lib_tests20.html 5df7907b99976e4051d1dd433e2bdbeb4ce200e3'), "%3Cmath%3E%3Cannotation-xml%20encoding%3D%22application/svg%2Bxml%22%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20encoding%3D%22application/svg%2Bxml%22%0A%7C%20%20%20%20%20%3Cdiv%3E"],"a7e70930137ff8338778253ac93dc66f11eef966":[async_test('html5lib_tests20.html a7e70930137ff8338778253ac93dc66f11eef966'), "%3Cmath%3E%3Cannotation-xml%20encoding%3D%22application/xhtml%2Bxml%22%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20encoding%3D%22application/xhtml%2Bxml%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"b92100bddc318e13996fbe96c087c35152cbb4e3":[async_test('html5lib_tests20.html b92100bddc318e13996fbe96c087c35152cbb4e3'), "%3Cmath%3E%3Cannotation-xml%20encoding%3D%22aPPlication/xhtmL%2BxMl%22%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20encoding%3D%22aPPlication/xhtmL%2BxMl%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"f55b68de8cf9ec17e0512a6caae94e0fbf151260":[async_test('html5lib_tests20.html f55b68de8cf9ec17e0512a6caae94e0fbf151260'), "%3Cmath%3E%3Cannotation-xml%20encoding%3D%22text/html%22%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20encoding%3D%22text/html%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"7fb0d4342e3ed3fabee650b83ce66ffb2f66c173":[async_test('html5lib_tests20.html 7fb0d4342e3ed3fabee650b83ce66ffb2f66c173'), "%3Cmath%3E%3Cannotation-xml%20encoding%3D%22Text/htmL%22%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20encoding%3D%22Text/htmL%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"72a6100cd60dd49f780168137ed09e27a8090b34":[async_test('html5lib_tests20.html 72a6100cd60dd49f780168137ed09e27a8090b34'), "%3Cmath%3E%3Cannotation-xml%20encoding%3D%22%20text/html%20%22%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20encoding%3D%22%20text/html%20%22%0A%7C%20%20%20%20%20%3Cdiv%3E"],"09ee3f414d08c8a3923e7c72380868617d1a4554":[async_test('html5lib_tests20.html 09ee3f414d08c8a3923e7c72380868617d1a4554'), "%3Cmath%3E%3Cannotation-xml%3E%20%3C/annotation-xml%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22"],"3b854aa3549b6f5cac96176766fccd25c965b5fe":[async_test('html5lib_tests20.html 3b854aa3549b6f5cac96176766fccd25c965b5fe'), "%3Cmath%3E%3Cannotation-xml%3Ec%3C/annotation-xml%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%22c%22"],"bbc36dce52efacdede3cf4d94582ace2a3fca9fc":[async_test('html5lib_tests20.html bbc36dce52efacdede3cf4d94582ace2a3fca9fc'), "%3Cmath%3E%3Cannotation-xml%3E%3C%21--foo--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%3C%21--%20foo%20--%3E"],"d4b924764bf49ceb39aa4f63dc81b7c05bbda84d":[async_test('html5lib_tests20.html d4b924764bf49ceb39aa4f63dc81b7c05bbda84d'), "%3Cmath%3E%3Cannotation-xml%3E%3C/svg%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%22x%22"],"19a6c1a25813323193c67a54904a41e389918f48":[async_test('html5lib_tests20.html 19a6c1a25813323193c67a54904a41e389918f48'), "%3Cmath%3E%3Cannotation-xml%3E%3Csvg%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22x%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests21.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests21.html
new file mode 100644
index 0000000000..e13295a974
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests21.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests21.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['49994884ec96a706a3c1b702049a1612e43a81b5','9128ce0d001f8a331dadfd0091dc0faaadcc27a3','250daec0d65f2528955e24fbe667cf40adaee33f','7cdfc1d2d87a4f9ebd754d18dc7b55f73f5ba58b','3d12442cdaef117f92af7ca90e7e03550e07314a','afeea6e5607448d59d16db2c2dad6e0f4f571f25','30b6ff244d2cd3716c1ad482e30ff294dcfed2b7','49b8f552d24bba176c920b40208e1d9f3d866075','60293a1ec80139eb1c9f05e48ca631e812a708da','753ad0dcb4fd3b1d6da2c87dd59116287e08d412','80607dd011814b8d3ef5c9ca380fec044dd5e1aa','b313be34a12fd540ab959b6cf18610fba03cb63f','c9f579bf49de2d4c553d03e43772c0d94be474c0','e714d65a37389755cd7385275f14a757c36b9510','df235b5f7ba9e6e1032bae6c75e92e6a68f48cda','fec253c6085a518dd72fdaff371da31e0d6bdb96','64150a9d88b6214a4a83cf9335e0b0a35e3be170','8c1c2ca15e12599448980201683ece12d4923d9d','32780672eec230ac59d4b61a1adb209bd6a0ed46','be6b00c6bd7e576953b0bceefd9c38d14b452b8d','9df59cd349097dca330fefef6c1b1bbbfddadae6','671f606f5cb7033854d99b96b040994f0f451496','caa80af33c7880aaddd17824efff1774ece01325',];
+ var tests = {
+ "49994884ec96a706a3c1b702049a1612e43a81b5":[async_test('html5lib_tests21.html 49994884ec96a706a3c1b702049a1612e43a81b5'), "%3Csvg%3E%3C%21%5BCDATA%5Bfoo%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"9128ce0d001f8a331dadfd0091dc0faaadcc27a3":[async_test('html5lib_tests21.html 9128ce0d001f8a331dadfd0091dc0faaadcc27a3'), "%3Cmath%3E%3C%21%5BCDATA%5Bfoo%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"250daec0d65f2528955e24fbe667cf40adaee33f":[async_test('html5lib_tests21.html 250daec0d65f2528955e24fbe667cf40adaee33f'), "%3Cdiv%3E%3C%21%5BCDATA%5Bfoo%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3C%21--%20%5BCDATA%5Bfoo%5D%5D%20--%3E"],"7cdfc1d2d87a4f9ebd754d18dc7b55f73f5ba58b":[async_test('html5lib_tests21.html 7cdfc1d2d87a4f9ebd754d18dc7b55f73f5ba58b'), "%3Csvg%3E%3C%21%5BCDATA%5Bfoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"3d12442cdaef117f92af7ca90e7e03550e07314a":[async_test('html5lib_tests21.html 3d12442cdaef117f92af7ca90e7e03550e07314a'), "%3Csvg%3E%3C%21%5BCDATA%5B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"afeea6e5607448d59d16db2c2dad6e0f4f571f25":[async_test('html5lib_tests21.html afeea6e5607448d59d16db2c2dad6e0f4f571f25'), "%3Csvg%3E%3C%21%5BCDATA%5B%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E"],"30b6ff244d2cd3716c1ad482e30ff294dcfed2b7":[async_test('html5lib_tests21.html 30b6ff244d2cd3716c1ad482e30ff294dcfed2b7'), "%3Csvg%3E%3C%21%5BCDATA%5B%5D%5D%20%3E%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%5D%5D%20%3E%22"],"49b8f552d24bba176c920b40208e1d9f3d866075":[async_test('html5lib_tests21.html 49b8f552d24bba176c920b40208e1d9f3d866075'), "%3Csvg%3E%3C%21%5BCDATA%5B%5D%5D", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%5D%5D%22"],"60293a1ec80139eb1c9f05e48ca631e812a708da":[async_test('html5lib_tests21.html 60293a1ec80139eb1c9f05e48ca631e812a708da'), "%3Csvg%3E%3C%21%5BCDATA%5B%5D", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%5D%22"],"753ad0dcb4fd3b1d6da2c87dd59116287e08d412":[async_test('html5lib_tests21.html 753ad0dcb4fd3b1d6da2c87dd59116287e08d412'), "%3Csvg%3E%3C%21%5BCDATA%5B%5D%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%5D%3Ea%22"],"80607dd011814b8d3ef5c9ca380fec044dd5e1aa":[async_test('html5lib_tests21.html 80607dd011814b8d3ef5c9ca380fec044dd5e1aa'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3C%21%5BCDATA%5Bfoo%5D%5D%5D%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%5D%22"],"b313be34a12fd540ab959b6cf18610fba03cb63f":[async_test('html5lib_tests21.html b313be34a12fd540ab959b6cf18610fba03cb63f'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3C%21%5BCDATA%5Bfoo%5D%5D%5D%5D%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%5D%5D%22"],"c9f579bf49de2d4c553d03e43772c0d94be474c0":[async_test('html5lib_tests21.html c9f579bf49de2d4c553d03e43772c0d94be474c0'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3C%21%5BCDATA%5Bfoo%5D%5D%5D%5D%5D%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22foo%5D%5D%5D%22"],"e714d65a37389755cd7385275f14a757c36b9510":[async_test('html5lib_tests21.html e714d65a37389755cd7385275f14a757c36b9510'), "%3Csvg%3E%3CforeignObject%3E%3Cdiv%3E%3C%21%5BCDATA%5Bfoo%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3C%21--%20%5BCDATA%5Bfoo%5D%5D%20--%3E"],"df235b5f7ba9e6e1032bae6c75e92e6a68f48cda":[async_test('html5lib_tests21.html df235b5f7ba9e6e1032bae6c75e92e6a68f48cda'), "%3Csvg%3E%3C%21%5BCDATA%5B%3Csvg%3E%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3Csvg%3E%22"],"fec253c6085a518dd72fdaff371da31e0d6bdb96":[async_test('html5lib_tests21.html fec253c6085a518dd72fdaff371da31e0d6bdb96'), "%3Csvg%3E%3C%21%5BCDATA%5B%3C/svg%3Ea%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3C/svg%3Ea%22"],"64150a9d88b6214a4a83cf9335e0b0a35e3be170":[async_test('html5lib_tests21.html 64150a9d88b6214a4a83cf9335e0b0a35e3be170'), "%3Csvg%3E%3C%21%5BCDATA%5B%3Csvg%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3Csvg%3Ea%22"],"8c1c2ca15e12599448980201683ece12d4923d9d":[async_test('html5lib_tests21.html 8c1c2ca15e12599448980201683ece12d4923d9d'), "%3Csvg%3E%3C%21%5BCDATA%5B%3C/svg%3Ea", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3C/svg%3Ea%22"],"32780672eec230ac59d4b61a1adb209bd6a0ed46":[async_test('html5lib_tests21.html 32780672eec230ac59d4b61a1adb209bd6a0ed46'), "%3Csvg%3E%3C%21%5BCDATA%5B%3Csvg%3E%5D%5D%3E%3Cpath%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3Csvg%3E%22%0A%7C%20%20%20%20%20%20%20%3Csvg%20path%3E"],"be6b00c6bd7e576953b0bceefd9c38d14b452b8d":[async_test('html5lib_tests21.html be6b00c6bd7e576953b0bceefd9c38d14b452b8d'), "%3Csvg%3E%3C%21%5BCDATA%5B%3Csvg%3E%5D%5D%3E%3C/path%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3Csvg%3E%22"],"9df59cd349097dca330fefef6c1b1bbbfddadae6":[async_test('html5lib_tests21.html 9df59cd349097dca330fefef6c1b1bbbfddadae6'), "%3Csvg%3E%3C%21%5BCDATA%5B%3Csvg%3E%5D%5D%3E%3C%21--path--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3Csvg%3E%22%0A%7C%20%20%20%20%20%20%20%3C%21--%20path%20--%3E"],"671f606f5cb7033854d99b96b040994f0f451496":[async_test('html5lib_tests21.html 671f606f5cb7033854d99b96b040994f0f451496'), "%3Csvg%3E%3C%21%5BCDATA%5B%3Csvg%3E%5D%5D%3Epath", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3Csvg%3Epath%22"],"caa80af33c7880aaddd17824efff1774ece01325":[async_test('html5lib_tests21.html caa80af33c7880aaddd17824efff1774ece01325'), "%3Csvg%3E%3C%21%5BCDATA%5B%3C%21--svg--%3E%5D%5D%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--svg--%3E%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests22.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests22.html
new file mode 100644
index 0000000000..74886e1660
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests22.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests22.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['0aae674c4a721f0a6f6205bca13056f08a516b00','9ee2a7fffc0ac4b0a2d59ac154d9d79b3b62499f','eba4be47ca51e9c659aaa2de43e17aded0d13e50','d69b2cbec658966233616e553d8b0b1dd544310d','4685334adccb0ef98b769fb8546b5696e28af968',];
+ var tests = {
+ "0aae674c4a721f0a6f6205bca13056f08a516b00":[async_test('html5lib_tests22.html 0aae674c4a721f0a6f6205bca13056f08a516b00'), "%3Ca%3E%3Cb%3E%3Cbig%3E%3Cem%3E%3Cstrong%3E%3Cdiv%3EX%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cbig%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cstrong%3E%0A%7C%20%20%20%20%20%3Cbig%3E%0A%7C%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cstrong%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"9ee2a7fffc0ac4b0a2d59ac154d9d79b3b62499f":[async_test('html5lib_tests22.html 9ee2a7fffc0ac4b0a2d59ac154d9d79b3b62499f'), "%3Ca%3E%3Cb%3E%3Cdiv%20id%3D1%3E%3Cdiv%20id%3D2%3E%3Cdiv%20id%3D3%3E%3Cdiv%20id%3D4%3E%3Cdiv%20id%3D5%3E%3Cdiv%20id%3D6%3E%3Cdiv%20id%3D7%3E%3Cdiv%20id%3D8%3EA%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%221%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%222%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%223%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%225%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%226%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%227%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%228%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22A%22"],"eba4be47ca51e9c659aaa2de43e17aded0d13e50":[async_test('html5lib_tests22.html eba4be47ca51e9c659aaa2de43e17aded0d13e50'), "%3Ca%3E%3Cb%3E%3Cdiv%20id%3D1%3E%3Cdiv%20id%3D2%3E%3Cdiv%20id%3D3%3E%3Cdiv%20id%3D4%3E%3Cdiv%20id%3D5%3E%3Cdiv%20id%3D6%3E%3Cdiv%20id%3D7%3E%3Cdiv%20id%3D8%3E%3Cdiv%20id%3D9%3EA%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%221%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%222%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%223%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%225%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%226%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%227%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%228%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%229%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22A%22"],"d69b2cbec658966233616e553d8b0b1dd544310d":[async_test('html5lib_tests22.html d69b2cbec658966233616e553d8b0b1dd544310d'), "%3Ca%3E%3Cb%3E%3Cdiv%20id%3D1%3E%3Cdiv%20id%3D2%3E%3Cdiv%20id%3D3%3E%3Cdiv%20id%3D4%3E%3Cdiv%20id%3D5%3E%3Cdiv%20id%3D6%3E%3Cdiv%20id%3D7%3E%3Cdiv%20id%3D8%3E%3Cdiv%20id%3D9%3E%3Cdiv%20id%3D10%3EA%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%221%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%222%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%223%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%225%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%226%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%227%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%228%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%229%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%2210%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22A%22"],"4685334adccb0ef98b769fb8546b5696e28af968":[async_test('html5lib_tests22.html 4685334adccb0ef98b769fb8546b5696e28af968'), "%3Ccite%3E%3Cb%3E%3Ccite%3E%3Ci%3E%3Ccite%3E%3Ci%3E%3Ccite%3E%3Ci%3E%3Cdiv%3EX%3C/b%3ETEST", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ccite%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccite%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccite%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccite%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22TEST%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests23.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests23.html
new file mode 100644
index 0000000000..b471f34ddf
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests23.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests23.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['79c0dc8da653d983d07795852b5f38d1533ff404','403404388390abddf3fb44db8dd7d643652f5df9','b9a81ae44ab81719fc6a4e3b6460fc6efc684e65','d271d3662baafff4893d32b250cadcf4c2ff3352','f8e296b2f362a64c1c464bedd6248e314f41c701',];
+ var tests = {
+ "79c0dc8da653d983d07795852b5f38d1533ff404":[async_test('html5lib_tests23.html 79c0dc8da653d983d07795852b5f38d1533ff404'), "%3Cp%3E%3Cfont%20size%3D4%3E%3Cfont%20color%3Dred%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20color%3Dred%3E%3Cp%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"403404388390abddf3fb44db8dd7d643652f5df9":[async_test('html5lib_tests23.html 403404388390abddf3fb44db8dd7d643652f5df9'), "%3Cp%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cp%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"b9a81ae44ab81719fc6a4e3b6460fc6efc684e65":[async_test('html5lib_tests23.html b9a81ae44ab81719fc6a4e3b6460fc6efc684e65'), "%3Cp%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D%225%22%3E%3Cfont%20size%3D4%3E%3Cp%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%225%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%225%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"d271d3662baafff4893d32b250cadcf4c2ff3352":[async_test('html5lib_tests23.html d271d3662baafff4893d32b250cadcf4c2ff3352'), "%3Cp%3E%3Cfont%20size%3D4%20id%3Da%3E%3Cfont%20size%3D4%20id%3Db%3E%3Cfont%20size%3D4%3E%3Cfont%20size%3D4%3E%3Cp%3EX", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%22b%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3D%224%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"f8e296b2f362a64c1c464bedd6248e314f41c701":[async_test('html5lib_tests23.html f8e296b2f362a64c1c464bedd6248e314f41c701'), "%3Cp%3E%3Cb%20id%3Da%3E%3Cb%20id%3Da%3E%3Cb%20id%3Da%3E%3Cb%3E%3Cobject%3E%3Cb%20id%3Da%3E%3Cb%20id%3Da%3EX%3C/object%3E%3Cp%3EY", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cobject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20id%3D%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Y%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests24.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests24.html
new file mode 100644
index 0000000000..ecfd895e86
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests24.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests24.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['692c2dbacf18cb758f26a3d9e7d9add4356f9067','614cc9827d3a0c5f91863dde5281dd0d97f64e6d','c517924583ee71b8e684c9ca1f2eed5e88139a39','140a82fab878c139b388d159c511eb999fe2d8c7','a2ddddcccb652a6529daafd4153a0e12b6d5ca8c','da2e30a0b6577b608bf48bbd11a16ff832bc7e46','66a5777f5453bd4b5161f00df02883b6d71f7cea','c8d97f31b70f67005eeacc3c86ac29e577c3d0ed',];
+ var tests = {
+ "692c2dbacf18cb758f26a3d9e7d9add4356f9067":[async_test('html5lib_tests24.html 692c2dbacf18cb758f26a3d9e7d9add4356f9067'), "%3C%21DOCTYPE%20html%3E%26NotEqualTilde%3B", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%89%82%CC%B8%22"],"614cc9827d3a0c5f91863dde5281dd0d97f64e6d":[async_test('html5lib_tests24.html 614cc9827d3a0c5f91863dde5281dd0d97f64e6d'), "%3C%21DOCTYPE%20html%3E%26NotEqualTilde%3BA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%89%82%CC%B8A%22"],"c517924583ee71b8e684c9ca1f2eed5e88139a39":[async_test('html5lib_tests24.html c517924583ee71b8e684c9ca1f2eed5e88139a39'), "%3C%21DOCTYPE%20html%3E%26ThickSpace%3B", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%81%9F%E2%80%8A%22"],"140a82fab878c139b388d159c511eb999fe2d8c7":[async_test('html5lib_tests24.html 140a82fab878c139b388d159c511eb999fe2d8c7'), "%3C%21DOCTYPE%20html%3E%26ThickSpace%3BA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%81%9F%E2%80%8AA%22"],"a2ddddcccb652a6529daafd4153a0e12b6d5ca8c":[async_test('html5lib_tests24.html a2ddddcccb652a6529daafd4153a0e12b6d5ca8c'), "%3C%21DOCTYPE%20html%3E%26NotSubset%3B", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%8A%82%E2%83%92%22"],"da2e30a0b6577b608bf48bbd11a16ff832bc7e46":[async_test('html5lib_tests24.html da2e30a0b6577b608bf48bbd11a16ff832bc7e46'), "%3C%21DOCTYPE%20html%3E%26NotSubset%3BA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%E2%8A%82%E2%83%92A%22"],"66a5777f5453bd4b5161f00df02883b6d71f7cea":[async_test('html5lib_tests24.html 66a5777f5453bd4b5161f00df02883b6d71f7cea'), "%3C%21DOCTYPE%20html%3E%26Gopf%3B", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%F0%9D%94%BE%22"],"c8d97f31b70f67005eeacc3c86ac29e577c3d0ed":[async_test('html5lib_tests24.html c8d97f31b70f67005eeacc3c86ac29e577c3d0ed'), "%3C%21DOCTYPE%20html%3E%26Gopf%3BA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%F0%9D%94%BEA%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests25.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests25.html
new file mode 100644
index 0000000000..86b4c2482c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests25.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests25.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['025adbb619bdef9ad228a6b378b9dd6bef9f93dc','7e561454888535bc5c68e7396de2f4206c81e97d','a14bdd90f5a745296e74c23951245cb7c5046ed1','421ad19f7854b9c8e28a0595a8dd20d6bfcd5376','7e8234523fbf67d37ab60f593e1fc3ac67706651','c705ceb6fb37865e6965641ea80137baf44176b6','277da0c4c937f3852d8a7cacf4e4b426a30b3dda','de4aa726e09215ba9c50b97d257e6c6b880107f1','578e08f11628dc9841f3eb21c016e1e28cec0304','a6e5387b48c4d0399a802215f7fa206bd0d3b492','d56d47bc9d9c7472fb1706bdecda1bbd165dcf91','2779456edd8407b403561e0cf339c8c2308c88b8','17d197ab2416adec6eb92e398c2db6f7efc98b58','1ccc6e280f6dc773e2e00f2cb70cd96e97078296','2c4a3a8d857ae3880197c7fb76cf8867c3171a4c','dcdbc94f31873d250fd208ef59f7c792996bbe5f','e7fa5ef62589df61fea98430b6610c445065618e','86f7866ee190c6614ccce12db874cea22550009a','e83cc2757467bb00e1eba55eb5caa39e1be54a53','aed93adbabc0e882d4900ce6f26d4fb81ab6ef11','48ddaacf5355643f27ef704ece88b227f51fc7cb','bf29bfbd56c9863d19e4d66cd67a0dc1813b2e86','73fc7c062b68d4f89579683dbb7f1eaef4ec27f1','057bc2d868d2f365cb2c0b4d07c231d2fa2b23b7','7f684d19be362ec9aa4fe7ecbba4ff3fc9730a43','d79f9119d02447226cc2d151044e6cffc5409e81',];
+ var tests = {
+ "025adbb619bdef9ad228a6b378b9dd6bef9f93dc":[async_test('html5lib_tests25.html 025adbb619bdef9ad228a6b378b9dd6bef9f93dc'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cfoo%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%22A%22"],"7e561454888535bc5c68e7396de2f4206c81e97d":[async_test('html5lib_tests25.html 7e561454888535bc5c68e7396de2f4206c81e97d'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Carea%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Carea%3E%0A%7C%20%20%20%20%20%22A%22"],"a14bdd90f5a745296e74c23951245cb7c5046ed1":[async_test('html5lib_tests25.html a14bdd90f5a745296e74c23951245cb7c5046ed1'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cbase%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbase%3E%0A%7C%20%20%20%20%20%22A%22"],"421ad19f7854b9c8e28a0595a8dd20d6bfcd5376":[async_test('html5lib_tests25.html 421ad19f7854b9c8e28a0595a8dd20d6bfcd5376'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cbasefont%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbasefont%3E%0A%7C%20%20%20%20%20%22A%22"],"7e8234523fbf67d37ab60f593e1fc3ac67706651":[async_test('html5lib_tests25.html 7e8234523fbf67d37ab60f593e1fc3ac67706651'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cbgsound%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbgsound%3E%0A%7C%20%20%20%20%20%22A%22"],"c705ceb6fb37865e6965641ea80137baf44176b6":[async_test('html5lib_tests25.html c705ceb6fb37865e6965641ea80137baf44176b6'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cbr%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%22A%22"],"277da0c4c937f3852d8a7cacf4e4b426a30b3dda":[async_test('html5lib_tests25.html 277da0c4c937f3852d8a7cacf4e4b426a30b3dda'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ccol%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22"],"de4aa726e09215ba9c50b97d257e6c6b880107f1":[async_test('html5lib_tests25.html de4aa726e09215ba9c50b97d257e6c6b880107f1'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ccommand%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ccommand%3E%0A%7C%20%20%20%20%20%20%20%22A%22"],"578e08f11628dc9841f3eb21c016e1e28cec0304":[async_test('html5lib_tests25.html 578e08f11628dc9841f3eb21c016e1e28cec0304'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cembed%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cembed%3E%0A%7C%20%20%20%20%20%22A%22"],"a6e5387b48c4d0399a802215f7fa206bd0d3b492":[async_test('html5lib_tests25.html a6e5387b48c4d0399a802215f7fa206bd0d3b492'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cframe%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22"],"d56d47bc9d9c7472fb1706bdecda1bbd165dcf91":[async_test('html5lib_tests25.html d56d47bc9d9c7472fb1706bdecda1bbd165dcf91'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Chr%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Chr%3E%0A%7C%20%20%20%20%20%22A%22"],"2779456edd8407b403561e0cf339c8c2308c88b8":[async_test('html5lib_tests25.html 2779456edd8407b403561e0cf339c8c2308c88b8'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cimg%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cimg%3E%0A%7C%20%20%20%20%20%22A%22"],"17d197ab2416adec6eb92e398c2db6f7efc98b58":[async_test('html5lib_tests25.html 17d197ab2416adec6eb92e398c2db6f7efc98b58'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cinput%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%22A%22"],"1ccc6e280f6dc773e2e00f2cb70cd96e97078296":[async_test('html5lib_tests25.html 1ccc6e280f6dc773e2e00f2cb70cd96e97078296'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ckeygen%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ckeygen%3E%0A%7C%20%20%20%20%20%22A%22"],"2c4a3a8d857ae3880197c7fb76cf8867c3171a4c":[async_test('html5lib_tests25.html 2c4a3a8d857ae3880197c7fb76cf8867c3171a4c'), "%3C%21DOCTYPE%20html%3E%3Ckeygen%3EA%3C/keygen%3EB", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ckeygen%3E%0A%7C%20%20%20%20%20%22AB%22"],"dcdbc94f31873d250fd208ef59f7c792996bbe5f":[async_test('html5lib_tests25.html dcdbc94f31873d250fd208ef59f7c792996bbe5f'), "%3C/keygen%3EA", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22"],"e7fa5ef62589df61fea98430b6610c445065618e":[async_test('html5lib_tests25.html e7fa5ef62589df61fea98430b6610c445065618e'), "%3C%21DOCTYPE%20html%3E%3C/keygen%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22"],"86f7866ee190c6614ccce12db874cea22550009a":[async_test('html5lib_tests25.html 86f7866ee190c6614ccce12db874cea22550009a'), "%3C%21DOCTYPE%20html%3E%3Chead%3E%3C/keygen%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22"],"e83cc2757467bb00e1eba55eb5caa39e1be54a53":[async_test('html5lib_tests25.html e83cc2757467bb00e1eba55eb5caa39e1be54a53'), "%3C%21DOCTYPE%20html%3E%3Chead%3E%3C/head%3E%3C/keygen%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22"],"aed93adbabc0e882d4900ce6f26d4fb81ab6ef11":[async_test('html5lib_tests25.html aed93adbabc0e882d4900ce6f26d4fb81ab6ef11'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3C/keygen%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22"],"48ddaacf5355643f27ef704ece88b227f51fc7cb":[async_test('html5lib_tests25.html 48ddaacf5355643f27ef704ece88b227f51fc7cb'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Clink%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Clink%3E%0A%7C%20%20%20%20%20%22A%22"],"bf29bfbd56c9863d19e4d66cd67a0dc1813b2e86":[async_test('html5lib_tests25.html bf29bfbd56c9863d19e4d66cd67a0dc1813b2e86'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmeta%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%22A%22"],"73fc7c062b68d4f89579683dbb7f1eaef4ec27f1":[async_test('html5lib_tests25.html 73fc7c062b68d4f89579683dbb7f1eaef4ec27f1'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cparam%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cparam%3E%0A%7C%20%20%20%20%20%22A%22"],"057bc2d868d2f365cb2c0b4d07c231d2fa2b23b7":[async_test('html5lib_tests25.html 057bc2d868d2f365cb2c0b4d07c231d2fa2b23b7'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Csource%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csource%3E%0A%7C%20%20%20%20%20%22A%22"],"7f684d19be362ec9aa4fe7ecbba4ff3fc9730a43":[async_test('html5lib_tests25.html 7f684d19be362ec9aa4fe7ecbba4ff3fc9730a43'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctrack%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctrack%3E%0A%7C%20%20%20%20%20%22A%22"],"d79f9119d02447226cc2d151044e6cffc5409e81":[async_test('html5lib_tests25.html d79f9119d02447226cc2d151044e6cffc5409e81'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cwbr%3EA", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cwbr%3E%0A%7C%20%20%20%20%20%22A%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests26.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests26.html
new file mode 100644
index 0000000000..2b3849b00b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests26.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests26.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['6232bd8c710002d3b3c375903a712d05163a821d','5e4fff339b6d191d80311bfa258a9b62e063c6aa','8695403efa4e413a1ad1f99984a8c0ecba379698','c3aa0a4f4e81fa4a2fd398c7a7a090d2c3f955f4','167bc3c289b234ab99fe96eaa0845682345de48c','3dacba9a76b8c454b42b9868252bbb6b68327184','8f1c4c0f0eee7b8136934ee18bd3beffbc7f2fe0','7369c472433b6dd76585b797fd3708e474601ecc','c085683feffd9da64f8782545042884173a1d1e3','be84971ac4a451e91099d225d0315dd17a88b830','9d63346ca23dc3ee41e29fe7d8403934bce8c610','8434eb9c0c3daf656b3158f5955d07e39ddc6444','6c95a99290e309b638b958272686a21486fa561e','37d360649a2b092ad05e1be1c9ea65bfec83ca6d','8919621ebbd1f4df0fdaacee3e53dc959a2d9235','3723d028349cbbc1a5c3f014987bb11c8ba804d0','e370b3102af9ba28e24f5c41a08443cdd9dd6d25','d89064b85cdb1cf0d9e01b0d4618f6f5901533e1','d22b654495ccbf7ca93f5356e93c828ba65b00dd','ce65a73998345e8be4da9bccfe2cec9fab57c3be',];
+ var tests = {
+ "6232bd8c710002d3b3c375903a712d05163a821d":[async_test('html5lib_tests26.html 6232bd8c710002d3b3c375903a712d05163a821d'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ca%20href%3D%27%231%27%3E%3Cnobr%3E1%3Cnobr%3E%3C/a%3E%3Cbr%3E%3Ca%20href%3D%27%232%27%3E%3Cnobr%3E2%3Cnobr%3E%3C/a%3E%3Cbr%3E%3Ca%20href%3D%27%233%27%3E%3Cnobr%3E3%3Cnobr%3E%3C/a%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22%231%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20href%3D%22%232%22%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22%232%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20href%3D%22%233%22%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20href%3D%22%233%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E"],"5e4fff339b6d191d80311bfa258a9b62e063c6aa":[async_test('html5lib_tests26.html 5e4fff339b6d191d80311bfa258a9b62e063c6aa'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E%3Cnobr%3E1%3Cnobr%3E%3C/b%3E%3Ci%3E%3Cnobr%3E2%3Cnobr%3E%3C/i%3E3", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%223%22"],"8695403efa4e413a1ad1f99984a8c0ecba379698":[async_test('html5lib_tests26.html 8695403efa4e413a1ad1f99984a8c0ecba379698'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E%3Cnobr%3E1%3Ctable%3E%3Cnobr%3E%3C/b%3E%3Ci%3E%3Cnobr%3E2%3Cnobr%3E%3C/i%3E3", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%223%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ctable%3E"],"c3aa0a4f4e81fa4a2fd398c7a7a090d2c3f955f4":[async_test('html5lib_tests26.html c3aa0a4f4e81fa4a2fd398c7a7a090d2c3f955f4'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E%3Cnobr%3E1%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cnobr%3E%3C/b%3E%3Ci%3E%3Cnobr%3E2%3Cnobr%3E%3C/i%3E3", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%223%22"],"167bc3c289b234ab99fe96eaa0845682345de48c":[async_test('html5lib_tests26.html 167bc3c289b234ab99fe96eaa0845682345de48c'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E%3Cnobr%3E1%3Cdiv%3E%3Cnobr%3E%3C/b%3E%3Ci%3E%3Cnobr%3E2%3Cnobr%3E%3C/i%3E3", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%223%22"],"3dacba9a76b8c454b42b9868252bbb6b68327184":[async_test('html5lib_tests26.html 3dacba9a76b8c454b42b9868252bbb6b68327184'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E%3Cnobr%3E1%3Cnobr%3E%3C/b%3E%3Cdiv%3E%3Ci%3E%3Cnobr%3E2%3Cnobr%3E%3C/i%3E3", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%222%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%223%22"],"8f1c4c0f0eee7b8136934ee18bd3beffbc7f2fe0":[async_test('html5lib_tests26.html 8f1c4c0f0eee7b8136934ee18bd3beffbc7f2fe0'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E%3Cnobr%3E1%3Cnobr%3E%3Cins%3E%3C/b%3E%3Ci%3E%3Cnobr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cins%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E"],"7369c472433b6dd76585b797fd3708e474601ecc":[async_test('html5lib_tests26.html 7369c472433b6dd76585b797fd3708e474601ecc'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E%3Cnobr%3E1%3Cins%3E%3Cnobr%3E%3C/b%3E%3Ci%3E2", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cins%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22"],"c085683feffd9da64f8782545042884173a1d1e3":[async_test('html5lib_tests26.html c085683feffd9da64f8782545042884173a1d1e3'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cb%3E1%3Cnobr%3E%3C/b%3E%3Ci%3E%3Cnobr%3E2%3C/i%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%222%22"],"be84971ac4a451e91099d225d0315dd17a88b830":[async_test('html5lib_tests26.html be84971ac4a451e91099d225d0315dd17a88b830'), "%3Cp%3E%3Ccode%20x%3C/code%3E%3C/p%3E%0A", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%20%20%20%20code%3D%22%22%0A%7C%20%20%20%20%20%20%20%20%20x%3C%3D%22%22%0A%7C%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%20%20code%3D%22%22%0A%7C%20%20%20%20%20%20%20x%3C%3D%22%22%0A%7C%20%20%20%20%20%20%20%22%0A%22"],"9d63346ca23dc3ee41e29fe7d8403934bce8c610":[async_test('html5lib_tests26.html 9d63346ca23dc3ee41e29fe7d8403934bce8c610'), "%3C%21DOCTYPE%20html%3E%3Csvg%3E%3CforeignObject%3E%3Cp%3E%3Ci%3E%3C/p%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"8434eb9c0c3daf656b3158f5955d07e39ddc6444":[async_test('html5lib_tests26.html 8434eb9c0c3daf656b3158f5955d07e39ddc6444'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3CforeignObject%3E%3Cp%3E%3Ci%3E%3C/p%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"6c95a99290e309b638b958272686a21486fa561e":[async_test('html5lib_tests26.html 6c95a99290e309b638b958272686a21486fa561e'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cmtext%3E%3Cp%3E%3Ci%3E%3C/p%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"37d360649a2b092ad05e1be1c9ea65bfec83ca6d":[async_test('html5lib_tests26.html 37d360649a2b092ad05e1be1c9ea65bfec83ca6d'), "%3C%21DOCTYPE%20html%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cmath%3E%3Cmtext%3E%3Cp%3E%3Ci%3E%3C/p%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mtext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"8919621ebbd1f4df0fdaacee3e53dc959a2d9235":[async_test('html5lib_tests26.html 8919621ebbd1f4df0fdaacee3e53dc959a2d9235'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cdiv%3E%3C%21/div%3Ea", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3C%21--%20/div%20--%3E%0A%7C%20%20%20%20%20%20%20%22a%22"],"3723d028349cbbc1a5c3f014987bb11c8ba804d0":[async_test('html5lib_tests26.html 3723d028349cbbc1a5c3f014987bb11c8ba804d0'), "%3Cbutton%3E%3Cp%3E%3Cbutton%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cbutton%3E"],"e370b3102af9ba28e24f5c41a08443cdd9dd6d25":[async_test('html5lib_tests26.html e370b3102af9ba28e24f5c41a08443cdd9dd6d25'), "%3Csvg%3E%3C/p%3E%3Cfoo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cfoo%3E"],"d89064b85cdb1cf0d9e01b0d4618f6f5901533e1":[async_test('html5lib_tests26.html d89064b85cdb1cf0d9e01b0d4618f6f5901533e1'), "%3Csvg%3E%3C/br%3E%3Cfoo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%3Cfoo%3E"],"d22b654495ccbf7ca93f5356e93c828ba65b00dd":[async_test('html5lib_tests26.html d22b654495ccbf7ca93f5356e93c828ba65b00dd'), "%3Cmath%3E%3C/p%3E%3Cfoo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cfoo%3E"],"ce65a73998345e8be4da9bccfe2cec9fab57c3be":[async_test('html5lib_tests26.html ce65a73998345e8be4da9bccfe2cec9fab57c3be'), "%3Cmath%3E%3C/br%3E%3Cfoo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%3Cfoo%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests3.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests3.html
new file mode 100644
index 0000000000..f6f9d62911
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests3.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests3.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['9af28bba864ad2e398d95249fdcd40491e91b23f','be8bf339f25c34d94456b39ceeed74a25167df40','b77d2b4c52c8d57dae80409a39f5e21cb8e5b3bc','7902929c3aa85bf8ffc8d7fa228921acec21808e','16dda22403dee14d6d8627d9139b8c5296f24b61','7022e121d090113a9b6a1f29e8c620b5b6c9b67c','8b5088252a41409e5f5989408f09af986573b007','692cef68475bc2c58dc3a1d6fc804ab69df37117','88bbb8a76e9880c09e8ffcd626660106cf27abce','b43510ea21c96a96255b45aef578af5cbc78475e','5227d81a48fc519767baaca384b9687dad7ba1bf','5bb12f29d0f7c9c30bc8ceb14578c60df73dca2c','9ba44cced626432a79929642154346ab9d01403a','f9031fcb39c793e24b116a1e041dd93ed638a0f4','45ec5c450b3039007112fcb053c2a82ce2e93f17','6a66abfc230b8cfc93c57210ae370b1d5e744b5a','ed9cc49cd8a577e1e6343808c328e242b53ee42d','32c5a1be682ae34b4195cd0481ee6c53c806abeb','daf731117bb7cf43f750f187cbb3528f07c9a012','948e2378d0e6bd68dbc278a993c2774c22b30370','f7fd80272bb4ab6e3bb871de5f7688912740c39f','9eddcf7971dc65d05f25aa4c412acf09a789e5b3','e91b512a3b3307481c8039279d5cf487aa258a9d','6d570cad6386f2e21419f5eb63e7dfa290abe40f',];
+ var tests = {
+ "9af28bba864ad2e398d95249fdcd40491e91b23f":[async_test('html5lib_tests3.html 9af28bba864ad2e398d95249fdcd40491e91b23f'), "%3Chead%3E%3C/head%3E%3Cstyle%3E%3C/style%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%3Cbody%3E"],"be8bf339f25c34d94456b39ceeed74a25167df40":[async_test('html5lib_tests3.html be8bf339f25c34d94456b39ceeed74a25167df40'), "%3Chead%3E%3C/head%3E%3Cscript%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3Cbody%3E"],"b77d2b4c52c8d57dae80409a39f5e21cb8e5b3bc":[async_test('html5lib_tests3.html b77d2b4c52c8d57dae80409a39f5e21cb8e5b3bc'), "%3Chead%3E%3C/head%3E%3C%21--%20--%3E%3Cstyle%3E%3C/style%3E%3C%21--%20--%3E%3Cscript%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%3Cbody%3E"],"7902929c3aa85bf8ffc8d7fa228921acec21808e":[async_test('html5lib_tests3.html 7902929c3aa85bf8ffc8d7fa228921acec21808e'), "%3Chead%3E%3C/head%3E%3C%21--%20--%3Ex%3Cstyle%3E%3C/style%3E%3C%21--%20--%3E%3Cscript%3E%3C/script%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%3C%21--%20%20%20--%3E%0A%7C%20%20%20%20%20%3Cscript%3E"],"16dda22403dee14d6d8627d9139b8c5296f24b61":[async_test('html5lib_tests3.html 16dda22403dee14d6d8627d9139b8c5296f24b61'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cpre%3E%0A%3C/pre%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E"],"7022e121d090113a9b6a1f29e8c620b5b6c9b67c":[async_test('html5lib_tests3.html 7022e121d090113a9b6a1f29e8c620b5b6c9b67c'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cpre%3E%0Afoo%3C/pre%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"8b5088252a41409e5f5989408f09af986573b007":[async_test('html5lib_tests3.html 8b5088252a41409e5f5989408f09af986573b007'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cpre%3E%0A%0Afoo%3C/pre%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22%0Afoo%22"],"692cef68475bc2c58dc3a1d6fc804ab69df37117":[async_test('html5lib_tests3.html 692cef68475bc2c58dc3a1d6fc804ab69df37117'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cpre%3E%0Afoo%0A%3C/pre%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22foo%0A%22"],"88bbb8a76e9880c09e8ffcd626660106cf27abce":[async_test('html5lib_tests3.html 88bbb8a76e9880c09e8ffcd626660106cf27abce'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cpre%3Ex%3C/pre%3E%3Cspan%3E%0A%3C/span%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22"],"b43510ea21c96a96255b45aef578af5cbc78475e":[async_test('html5lib_tests3.html b43510ea21c96a96255b45aef578af5cbc78475e'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cpre%3Ex%0Ay%3C/pre%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22x%0Ay%22"],"5227d81a48fc519767baaca384b9687dad7ba1bf":[async_test('html5lib_tests3.html 5227d81a48fc519767baaca384b9687dad7ba1bf'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cpre%3Ex%3Cdiv%3E%0Ay%3C/pre%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%0Ay%22"],"5bb12f29d0f7c9c30bc8ceb14578c60df73dca2c":[async_test('html5lib_tests3.html 5bb12f29d0f7c9c30bc8ceb14578c60df73dca2c'), "%3C%21DOCTYPE%20html%3E%3Cpre%3E%26%23x0a%3B%26%23x0a%3BA%3C/pre%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%22%0AA%22"],"9ba44cced626432a79929642154346ab9d01403a":[async_test('html5lib_tests3.html 9ba44cced626432a79929642154346ab9d01403a'), "%3C%21DOCTYPE%20html%3E%3CHTML%3E%3CMETA%3E%3CHEAD%3E%3C/HEAD%3E%3C/HTML%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%3Cbody%3E"],"f9031fcb39c793e24b116a1e041dd93ed638a0f4":[async_test('html5lib_tests3.html f9031fcb39c793e24b116a1e041dd93ed638a0f4'), "%3C%21DOCTYPE%20html%3E%3CHTML%3E%3CHEAD%3E%3Chead%3E%3C/HEAD%3E%3C/HTML%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"45ec5c450b3039007112fcb053c2a82ce2e93f17":[async_test('html5lib_tests3.html 45ec5c450b3039007112fcb053c2a82ce2e93f17'), "%3Ctextarea%3Efoo%3Cspan%3Ebar%3C/span%3E%3Ci%3Ebaz", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22foo%3Cspan%3Ebar%3C/span%3E%3Ci%3Ebaz%22"],"6a66abfc230b8cfc93c57210ae370b1d5e744b5a":[async_test('html5lib_tests3.html 6a66abfc230b8cfc93c57210ae370b1d5e744b5a'), "%3Ctitle%3Efoo%3Cspan%3Ebar%3C/em%3E%3Ci%3Ebaz", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22foo%3Cspan%3Ebar%3C/em%3E%3Ci%3Ebaz%22%0A%7C%20%20%20%3Cbody%3E"],"ed9cc49cd8a577e1e6343808c328e242b53ee42d":[async_test('html5lib_tests3.html ed9cc49cd8a577e1e6343808c328e242b53ee42d'), "%3C%21DOCTYPE%20html%3E%3Ctextarea%3E%0A%3C/textarea%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E"],"32c5a1be682ae34b4195cd0481ee6c53c806abeb":[async_test('html5lib_tests3.html 32c5a1be682ae34b4195cd0481ee6c53c806abeb'), "%3C%21DOCTYPE%20html%3E%3Ctextarea%3E%0Afoo%3C/textarea%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22foo%22"],"daf731117bb7cf43f750f187cbb3528f07c9a012":[async_test('html5lib_tests3.html daf731117bb7cf43f750f187cbb3528f07c9a012'), "%3C%21DOCTYPE%20html%3E%3Ctextarea%3E%0A%0Afoo%3C/textarea%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%0Afoo%22"],"948e2378d0e6bd68dbc278a993c2774c22b30370":[async_test('html5lib_tests3.html 948e2378d0e6bd68dbc278a993c2774c22b30370'), "%3C%21DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%3Cul%3E%3Cli%3E%3Cdiv%3E%3Cp%3E%3Cli%3E%3C/ul%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E"],"f7fd80272bb4ab6e3bb871de5f7688912740c39f":[async_test('html5lib_tests3.html f7fd80272bb4ab6e3bb871de5f7688912740c39f'), "%3C%21doctype%20html%3E%3Cnobr%3E%3Cnobr%3E%3Cnobr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E"],"9eddcf7971dc65d05f25aa4c412acf09a789e5b3":[async_test('html5lib_tests3.html 9eddcf7971dc65d05f25aa4c412acf09a789e5b3'), "%3C%21doctype%20html%3E%3Cnobr%3E%3Cnobr%3E%3C/nobr%3E%3Cnobr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cnobr%3E"],"e91b512a3b3307481c8039279d5cf487aa258a9d":[async_test('html5lib_tests3.html e91b512a3b3307481c8039279d5cf487aa258a9d'), "%3C%21doctype%20html%3E%3Chtml%3E%3Cbody%3E%3Cp%3E%3Ctable%3E%3C/table%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"6d570cad6386f2e21419f5eb63e7dfa290abe40f":[async_test('html5lib_tests3.html 6d570cad6386f2e21419f5eb63e7dfa290abe40f'), "%3Cp%3E%3Ctable%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests5.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests5.html
new file mode 100644
index 0000000000..b1314ead67
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests5.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests5.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['c482a88c4feb445945f19c77eda5e460cd6db344','b28eaef63aeeb165eceb56152d50767327f975fa','20c1b55aabcd426fa5975648f21cff40fa3fc2e3','cc87be99b2531e4c0c1fd95f81cd4dd989f699d3','283a0f4eb33a3ee80f718020268bf1794a758ec9','bde8b7a035edd6f123f45708ac10b4f60e81edf4','6ace30add7690cda74de9830481c95bef1f5976d','3c5f82c8db30cc1cce4c7fa8a5d18cf13ce8d007','9cac6179dc295f43afd5a41ed98aef3a9d5a08de','021a5fbf8c725781d08dce099d21f7023c9bb26d','412eae0c0e6e5da254550debd587ff86cff55c0c','410a64500216425d811748b0258c92a49fbad0ff','bd7dfd1a0f74731c22b3e2d331f7c14ba7c9a4e8','5f847a390a413a42fcef3d4510ddc56815c7d722','6d5b2f84df760f8995146c406c2dd07ba5510f7f','eded02e700d7329f650a9a38ef7ea6c0e453766b',];
+ var tests = {
+ "c482a88c4feb445945f19c77eda5e460cd6db344":[async_test('html5lib_tests5.html c482a88c4feb445945f19c77eda5e460cd6db344'), "%3Cstyle%3E%20%3C%21--%20%3C/style%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21--%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22"],"b28eaef63aeeb165eceb56152d50767327f975fa":[async_test('html5lib_tests5.html b28eaef63aeeb165eceb56152d50767327f975fa'), "%3Cstyle%3E%20%3C%21--%20%3C/style%3E%20--%3E%20%3C/style%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21--%20%22%0A%7C%20%20%20%20%20%22%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%20x%22"],"20c1b55aabcd426fa5975648f21cff40fa3fc2e3":[async_test('html5lib_tests5.html 20c1b55aabcd426fa5975648f21cff40fa3fc2e3'), "%3Cstyle%3E%20%3C%21--%3E%20%3C/style%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21--%3E%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22"],"cc87be99b2531e4c0c1fd95f81cd4dd989f699d3":[async_test('html5lib_tests5.html cc87be99b2531e4c0c1fd95f81cd4dd989f699d3'), "%3Cstyle%3E%20%3C%21---%3E%20%3C/style%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21---%3E%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22"],"283a0f4eb33a3ee80f718020268bf1794a758ec9":[async_test('html5lib_tests5.html 283a0f4eb33a3ee80f718020268bf1794a758ec9'), "%3Ciframe%3E%20%3C%21---%3E%20%3C/iframe%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ciframe%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21---%3E%20%22%0A%7C%20%20%20%20%20%22x%22"],"bde8b7a035edd6f123f45708ac10b4f60e81edf4":[async_test('html5lib_tests5.html bde8b7a035edd6f123f45708ac10b4f60e81edf4'), "%3Ciframe%3E%20%3C%21---%20%3C/iframe%3E-%3Ex%3C/iframe%3E%20--%3E%20%3C/iframe%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ciframe%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21---%20%22%0A%7C%20%20%20%20%20%22-%3Ex%20--%3E%20x%22"],"6ace30add7690cda74de9830481c95bef1f5976d":[async_test('html5lib_tests5.html 6ace30add7690cda74de9830481c95bef1f5976d'), "%3Cscript%3E%20%3C%21--%20%3C/script%3E%20--%3E%20%3C/script%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21--%20%22%0A%7C%20%20%20%20%20%22%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%20x%22"],"3c5f82c8db30cc1cce4c7fa8a5d18cf13ce8d007":[async_test('html5lib_tests5.html 3c5f82c8db30cc1cce4c7fa8a5d18cf13ce8d007'), "%3Ctitle%3E%20%3C%21--%20%3C/title%3E%20--%3E%20%3C/title%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21--%20%22%0A%7C%20%20%20%20%20%22%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%20x%22"],"9cac6179dc295f43afd5a41ed98aef3a9d5a08de":[async_test('html5lib_tests5.html 9cac6179dc295f43afd5a41ed98aef3a9d5a08de'), "%3Ctextarea%3E%20%3C%21---%20%3C/textarea%3E-%3Ex%3C/textarea%3E%20--%3E%20%3C/textarea%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctextarea%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21---%20%22%0A%7C%20%20%20%20%20%22-%3Ex%20--%3E%20x%22"],"021a5fbf8c725781d08dce099d21f7023c9bb26d":[async_test('html5lib_tests5.html 021a5fbf8c725781d08dce099d21f7023c9bb26d'), "%3Cstyle%3E%20%3C%21%3C/--%20%3C/style%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21%3C/--%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22"],"412eae0c0e6e5da254550debd587ff86cff55c0c":[async_test('html5lib_tests5.html 412eae0c0e6e5da254550debd587ff86cff55c0c'), "%3Cp%3E%3Cxmp%3E%3C/xmp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cxmp%3E"],"410a64500216425d811748b0258c92a49fbad0ff":[async_test('html5lib_tests5.html 410a64500216425d811748b0258c92a49fbad0ff'), "%3Cxmp%3E%20%3C%21--%20%3E%20--%3E%20%3C/xmp%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cxmp%3E%0A%7C%20%20%20%20%20%20%20%22%20%3C%21--%20%3E%20--%3E%20%22"],"bd7dfd1a0f74731c22b3e2d331f7c14ba7c9a4e8":[async_test('html5lib_tests5.html bd7dfd1a0f74731c22b3e2d331f7c14ba7c9a4e8'), "%3Ctitle%3E%26amp%3B%3C/title%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%26%22%0A%7C%20%20%20%3Cbody%3E"],"5f847a390a413a42fcef3d4510ddc56815c7d722":[async_test('html5lib_tests5.html 5f847a390a413a42fcef3d4510ddc56815c7d722'), "%3Ctitle%3E%3C%21--%26amp%3B--%3E%3C/title%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%26--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"6d5b2f84df760f8995146c406c2dd07ba5510f7f":[async_test('html5lib_tests5.html 6d5b2f84df760f8995146c406c2dd07ba5510f7f'), "%3Ctitle%3E%3C%21--%3C/title%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E"],"eded02e700d7329f650a9a38ef7ea6c0e453766b":[async_test('html5lib_tests5.html eded02e700d7329f650a9a38ef7ea6c0e453766b'), "%3Cnoscript%3E%3C%21--%3C/noscript%3E--%3E%3C/noscript%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22--%3E%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests6.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests6.html
new file mode 100644
index 0000000000..2bc2df75b0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests6.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests6.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['d6280e971dd654968ee3c867bec8a3c7d1b885e5','c647c78bfe3660e1ea7d50a04440ed5ace26bd98','fa05c524ac7918197adf422a2c4be35d5eca9ddc','652ae0a8ca3ab725b2d10e9866898b3419333f64','03dad50ea8dd5ba10d8ed7c182e6ce5e654c02dc','3ef045a4e33856f8dac96feaf6f9b06df4bbb49e','fe9fca0f0d3f199ab59c8ef90017f34bcc670ecb','9f8748b72268a2f55e31f89ded4321c5aa4cfbb2','33832cc94bb649f53372c331cbf9234f9b131bec','cbec315b899132776db7598e77d306464e274d32','5a5e3a8ce3d91e10976d5f33b40a7f140b76bc36','ee14c28b79472014c7f456e7cf38a0d93d67b4d3','2d5d4c4867479737e6b5c45b0d5c5541b8353b88','2ea8b4e550e7f34dc52aec0b1870f3d729c20962','6d027310e96f9ac1628e4c67f49e0c690a009232','ad38bf32b77d3553168d6009b1b66029e59d8f80','78b741dafb63b9a5a78ad53915c4c4ee35d1ed9c','dbd7339544532dabb0b7a9136f6463a9c13c8897','5289376d3bace713fc7100577490c56e52dbfa46','ec71973ac3055b0dcfd47c0fc7e7dadc17cd6987','8c25d5edc43b61c47914db4e75a4d96bdd6b2be6','747c389eaf38370a1a7ec79bfeeb3b12c6512bfd','80e4700f134aa20d20eb82fd6cb35cb5846e0ce5','dd5e1c5216565fa8a816078e94528d7596cc2d68','b56b3db519a860b7d17e3bef387900a0d633d393','65a48cd67ebd59c603f45a5b9c2ad99a1ce2058a','64ef2f60587cbae9e7ffec9dc1a295ee4e1a116f','cec679ccf989d4a6985421a9cc301ebfa4667c23','4e48da4a2bca8ce4592e83eb0a076e7d61c297b0','3b2d876e5021de26cf6fdc8d76f6953d08d62180','fb1378f51de760693e7b779830d94033e93be24d','50adfdb7bde3e9bee16021cec1acadbe20858971','f9ca74f9f03a884b09d6a0cb6c53ff84807932e1','09f8c3624809ce2d40845ec3dea2bf6b95eadef2','f591efd2366066d4d02bb2f75bc7eb1151df3734','d25453a832050a3d5e5a908387901544ce92cc3b','5e2f65ba7616b7b909591edca14004e84db39220','8a5717873290cad9d9d67ad4785836bed8f93d35','3e215e4e155dec8d3d6a5a9190e78fce621083e1',];
+ var tests = {
+ "d6280e971dd654968ee3c867bec8a3c7d1b885e5":[async_test('html5lib_tests6.html d6280e971dd654968ee3c867bec8a3c7d1b885e5'), "%3C%21doctype%20html%3E%3C/head%3E%20%3Chead%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%22%20%22%0A%7C%20%20%20%3Cbody%3E"],"c647c78bfe3660e1ea7d50a04440ed5ace26bd98":[async_test('html5lib_tests6.html c647c78bfe3660e1ea7d50a04440ed5ace26bd98'), "%3C%21doctype%20html%3E%3Cform%3E%3Cdiv%3E%3C/form%3E%3Cdiv%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cform%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"fa05c524ac7918197adf422a2c4be35d5eca9ddc":[async_test('html5lib_tests6.html fa05c524ac7918197adf422a2c4be35d5eca9ddc'), "%3C%21doctype%20html%3E%3Ctitle%3E%26amp%3B%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%26%22%0A%7C%20%20%20%3Cbody%3E"],"652ae0a8ca3ab725b2d10e9866898b3419333f64":[async_test('html5lib_tests6.html 652ae0a8ca3ab725b2d10e9866898b3419333f64'), "%3C%21doctype%20html%3E%3Ctitle%3E%3C%21--%26amp%3B--%3E%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22%3C%21--%26--%3E%22%0A%7C%20%20%20%3Cbody%3E"],"03dad50ea8dd5ba10d8ed7c182e6ce5e654c02dc":[async_test('html5lib_tests6.html 03dad50ea8dd5ba10d8ed7c182e6ce5e654c02dc'), "%3C%21doctype%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"3ef045a4e33856f8dac96feaf6f9b06df4bbb49e":[async_test('html5lib_tests6.html 3ef045a4e33856f8dac96feaf6f9b06df4bbb49e'), "%3C%21---x", "%23document%0A%7C%20%3C%21--%20-x%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"fe9fca0f0d3f199ab59c8ef90017f34bcc670ecb":[async_test('html5lib_tests6.html fe9fca0f0d3f199ab59c8ef90017f34bcc670ecb'), "%3Cframeset%3E%3C/frameset%3E%0Afoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%0A%22"],"9f8748b72268a2f55e31f89ded4321c5aa4cfbb2":[async_test('html5lib_tests6.html 9f8748b72268a2f55e31f89ded4321c5aa4cfbb2'), "%3Cframeset%3E%3C/frameset%3E%0A%3Cnoframes%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%0A%22%0A%7C%20%20%20%3Cnoframes%3E"],"33832cc94bb649f53372c331cbf9234f9b131bec":[async_test('html5lib_tests6.html 33832cc94bb649f53372c331cbf9234f9b131bec'), "%3Cframeset%3E%3C/frameset%3E%0A%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%0A%22"],"cbec315b899132776db7598e77d306464e274d32":[async_test('html5lib_tests6.html cbec315b899132776db7598e77d306464e274d32'), "%3Cframeset%3E%3C/frameset%3E%0A%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%0A%22"],"5a5e3a8ce3d91e10976d5f33b40a7f140b76bc36":[async_test('html5lib_tests6.html 5a5e3a8ce3d91e10976d5f33b40a7f140b76bc36'), "%3Cframeset%3E%3C/frameset%3E%0A%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%0A%22"],"ee14c28b79472014c7f456e7cf38a0d93d67b4d3":[async_test('html5lib_tests6.html ee14c28b79472014c7f456e7cf38a0d93d67b4d3'), "%3Cform%3E%3Cform%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cform%3E"],"2d5d4c4867479737e6b5c45b0d5c5541b8353b88":[async_test('html5lib_tests6.html 2d5d4c4867479737e6b5c45b0d5c5541b8353b88'), "%3Cbutton%3E%3Cbutton%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbutton%3E%0A%7C%20%20%20%20%20%3Cbutton%3E"],"2ea8b4e550e7f34dc52aec0b1870f3d729c20962":[async_test('html5lib_tests6.html 2ea8b4e550e7f34dc52aec0b1870f3d729c20962'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3C/th%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"6d027310e96f9ac1628e4c67f49e0c690a009232":[async_test('html5lib_tests6.html 6d027310e96f9ac1628e4c67f49e0c690a009232'), "%3Ctable%3E%3Ccaption%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"ad38bf32b77d3553168d6009b1b66029e59d8f80":[async_test('html5lib_tests6.html ad38bf32b77d3553168d6009b1b66029e59d8f80'), "%3Ctable%3E%3Ccaption%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"78b741dafb63b9a5a78ad53915c4c4ee35d1ed9c":[async_test('html5lib_tests6.html 78b741dafb63b9a5a78ad53915c4c4ee35d1ed9c'), "%3Ctable%3E%3Ccaption%3E%3Cdiv%3E%3C/caption%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"dbd7339544532dabb0b7a9136f6463a9c13c8897":[async_test('html5lib_tests6.html dbd7339544532dabb0b7a9136f6463a9c13c8897'), "%3Ctable%3E%3Ccaption%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E"],"5289376d3bace713fc7100577490c56e52dbfa46":[async_test('html5lib_tests6.html 5289376d3bace713fc7100577490c56e52dbfa46'), "%3Ctable%3E%3Ccaption%3E%3C/body%3E%3C/col%3E%3C/colgroup%3E%3C/html%3E%3C/tbody%3E%3C/td%3E%3C/tfoot%3E%3C/th%3E%3C/thead%3E%3C/tr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E"],"ec71973ac3055b0dcfd47c0fc7e7dadc17cd6987":[async_test('html5lib_tests6.html ec71973ac3055b0dcfd47c0fc7e7dadc17cd6987'), "%3Ctable%3E%3Ccaption%3E%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"8c25d5edc43b61c47914db4e75a4d96bdd6b2be6":[async_test('html5lib_tests6.html 8c25d5edc43b61c47914db4e75a4d96bdd6b2be6'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3C/body%3E%3C/caption%3E%3C/col%3E%3C/colgroup%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"747c389eaf38370a1a7ec79bfeeb3b12c6512bfd":[async_test('html5lib_tests6.html 747c389eaf38370a1a7ec79bfeeb3b12c6512bfd'), "%3Ctable%3E%3Ccolgroup%3Efoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E"],"80e4700f134aa20d20eb82fd6cb35cb5846e0ce5":[async_test('html5lib_tests6.html 80e4700f134aa20d20eb82fd6cb35cb5846e0ce5'), "%3Ctable%3E%3Ccolgroup%3E%3C/col%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E"],"dd5e1c5216565fa8a816078e94528d7596cc2d68":[async_test('html5lib_tests6.html dd5e1c5216565fa8a816078e94528d7596cc2d68'), "%3Cframeset%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"b56b3db519a860b7d17e3bef387900a0d633d393":[async_test('html5lib_tests6.html b56b3db519a860b7d17e3bef387900a0d633d393'), "%3Cframeset%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"65a48cd67ebd59c603f45a5b9c2ad99a1ce2058a":[async_test('html5lib_tests6.html 65a48cd67ebd59c603f45a5b9c2ad99a1ce2058a'), "%3Ctable%3E%3Ctr%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"64ef2f60587cbae9e7ffec9dc1a295ee4e1a116f":[async_test('html5lib_tests6.html 64ef2f60587cbae9e7ffec9dc1a295ee4e1a116f'), "%3Ctable%3E%3Ctr%3E%3Cdiv%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"cec679ccf989d4a6985421a9cc301ebfa4667c23":[async_test('html5lib_tests6.html cec679ccf989d4a6985421a9cc301ebfa4667c23'), "%3Ctable%3E%3Ctbody%3E%3C/thead%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E"],"4e48da4a2bca8ce4592e83eb0a076e7d61c297b0":[async_test('html5lib_tests6.html 4e48da4a2bca8ce4592e83eb0a076e7d61c297b0'), "%3Ctable%3E%3Ctbody%3E%3C/body%3E%3C/caption%3E%3C/col%3E%3C/colgroup%3E%3C/html%3E%3C/td%3E%3C/th%3E%3C/tr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E"],"3b2d876e5021de26cf6fdc8d76f6953d08d62180":[async_test('html5lib_tests6.html 3b2d876e5021de26cf6fdc8d76f6953d08d62180'), "%3Ctable%3E%3Ctbody%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E"],"fb1378f51de760693e7b779830d94033e93be24d":[async_test('html5lib_tests6.html fb1378f51de760693e7b779830d94033e93be24d'), "%3Ctable%3E%3Ctable%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"50adfdb7bde3e9bee16021cec1acadbe20858971":[async_test('html5lib_tests6.html 50adfdb7bde3e9bee16021cec1acadbe20858971'), "%3Ctable%3E%3C/body%3E%3C/caption%3E%3C/col%3E%3C/colgroup%3E%3C/html%3E%3C/tbody%3E%3C/td%3E%3C/tfoot%3E%3C/th%3E%3C/thead%3E%3C/tr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"f9ca74f9f03a884b09d6a0cb6c53ff84807932e1":[async_test('html5lib_tests6.html f9ca74f9f03a884b09d6a0cb6c53ff84807932e1'), "%3Chtml%3E%3Cframeset%3E%3C/frameset%3E%3C/html%3E%20", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%22%20%22"],"09f8c3624809ce2d40845ec3dea2bf6b95eadef2":[async_test('html5lib_tests6.html 09f8c3624809ce2d40845ec3dea2bf6b95eadef2'), "%3C%21DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20HTML%204.01//EN%22%3E%3Chtml%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%20%22-//W3C//DTD%20HTML%204.01//EN%22%20%22%22%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"f591efd2366066d4d02bb2f75bc7eb1151df3734":[async_test('html5lib_tests6.html f591efd2366066d4d02bb2f75bc7eb1151df3734'), "%3Cparam%3E%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"d25453a832050a3d5e5a908387901544ce92cc3b":[async_test('html5lib_tests6.html d25453a832050a3d5e5a908387901544ce92cc3b'), "%3Csource%3E%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"5e2f65ba7616b7b909591edca14004e84db39220":[async_test('html5lib_tests6.html 5e2f65ba7616b7b909591edca14004e84db39220'), "%3Ctrack%3E%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"8a5717873290cad9d9d67ad4785836bed8f93d35":[async_test('html5lib_tests6.html 8a5717873290cad9d9d67ad4785836bed8f93d35'), "%3C/html%3E%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"3e215e4e155dec8d3d6a5a9190e78fce621083e1":[async_test('html5lib_tests6.html 3e215e4e155dec8d3d6a5a9190e78fce621083e1'), "%3C/body%3E%3Cframeset%3E%3C/frameset%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests7.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests7.html
new file mode 100644
index 0000000000..80101866f7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests7.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests7.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['7cb496e242a4dc9aed321252b5ca6ebf4f02ebcd','c0cffec1e999db2aefb2f6beb679fd9620566dbd','7c644a6da21bfd551385b0a5044b82cf7be0a22f','52fde917ba333b89afeff0e31104421455f4bf1b','b017906f7e2732092551b16ecd1b98df0983abcc','625cdec7c7d867748ac3b5be04e3e801a8c51fa5','6e8dd947155d1db292a0c289b3056891d89edaf5','a8f53ca779c0e5fc484771c4ec2aa6fb6d609779','e4ce65a5fb6a3726b341ec94da583dee7c2c8232','8779e761986b4c724bfe73fee95b7972145fb4d3','620e44a8a55e82cec0d51e9d93025d8a5c4456fc','37b910b755c2df155a3129d5a1150f0c0fdd7934','868bff3a23219b836fdc702063d637f817ce65e1','a33a56f5571b4bcb23138ffb60df3824f5c53773','facf5e60205451cf740f64628b8608f0aee30f3a','8ba11b54fa74a1c229d079b2902d6e33e139f33b','84e2152c284f4dfee7d8d12846c08b2c025578a6','8e3432411baa59cbef731ab3ba2703cb5d518453','e2f6144290512430ad25bbf9598eae77288c7b7a','350ebd648764d585f4aa0c29b925e6276579e9d0','9120ef80d3ee017007f3510121ddf7eba31b79e0','2026cd3ed42e41c168dd37c8c2675584f4eef335','ff2e324237e22efc8430ad7137d50d6d3d311820','02c9eb822611b0c206b544e0f2e5044695195ba8','cb3d1a50dd56a85135a0856cfa1c23a091ef2af4','13847685cfff75642823a0e78c6ef232ecb9d94b','99bb5e9a6e0daf62ba418dd97b5e8e3925f4137e','7a8e5ec2c95e725717c564dd49bfa86c2e1a88ba','17dcea170bb74d18ed4776dbb98f0bac6a11364d','9457c10c9f987bbc95937b34763fe956d61d237b','0fa23bb5d8b2a591afb1842b8f4c00c490c127b4','f6d60b3ae48e2b69b4c25125f9b5a3ab4867521b','5b0b3edcc3ce9fdc9f58eb62d326865ca0aab8c8',];
+ var tests = {
+ "7cb496e242a4dc9aed321252b5ca6ebf4f02ebcd":[async_test('html5lib_tests7.html 7cb496e242a4dc9aed321252b5ca6ebf4f02ebcd'), "%3C%21doctype%20html%3E%3Cbody%3E%3Ctitle%3EX%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"c0cffec1e999db2aefb2f6beb679fd9620566dbd":[async_test('html5lib_tests7.html c0cffec1e999db2aefb2f6beb679fd9620566dbd'), "%3C%21doctype%20html%3E%3Ctable%3E%3Ctitle%3EX%3C/title%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"7c644a6da21bfd551385b0a5044b82cf7be0a22f":[async_test('html5lib_tests7.html 7c644a6da21bfd551385b0a5044b82cf7be0a22f'), "%3C%21doctype%20html%3E%3Chead%3E%3C/head%3E%3Ctitle%3EX%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%3Cbody%3E"],"52fde917ba333b89afeff0e31104421455f4bf1b":[async_test('html5lib_tests7.html 52fde917ba333b89afeff0e31104421455f4bf1b'), "%3C%21doctype%20html%3E%3C/head%3E%3Ctitle%3EX%3C/title%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%3Cbody%3E"],"b017906f7e2732092551b16ecd1b98df0983abcc":[async_test('html5lib_tests7.html b017906f7e2732092551b16ecd1b98df0983abcc'), "%3C%21doctype%20html%3E%3C/head%3E%3Cbase%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cbase%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"625cdec7c7d867748ac3b5be04e3e801a8c51fa5":[async_test('html5lib_tests7.html 625cdec7c7d867748ac3b5be04e3e801a8c51fa5'), "%3C%21doctype%20html%3E%3C/head%3E%3Cbasefont%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cbasefont%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"6e8dd947155d1db292a0c289b3056891d89edaf5":[async_test('html5lib_tests7.html 6e8dd947155d1db292a0c289b3056891d89edaf5'), "%3C%21doctype%20html%3E%3C/head%3E%3Cbgsound%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cbgsound%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22"],"a8f53ca779c0e5fc484771c4ec2aa6fb6d609779":[async_test('html5lib_tests7.html a8f53ca779c0e5fc484771c4ec2aa6fb6d609779'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cmeta%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"e4ce65a5fb6a3726b341ec94da583dee7c2c8232":[async_test('html5lib_tests7.html e4ce65a5fb6a3726b341ec94da583dee7c2c8232'), "%3C%21doctype%20html%3E%3Ctable%3EX%3Ctr%3E%3Ctd%3E%3Ctable%3E%20%3Cmeta%3E%3C/table%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20%22"],"8779e761986b4c724bfe73fee95b7972145fb4d3":[async_test('html5lib_tests7.html 8779e761986b4c724bfe73fee95b7972145fb4d3'), "%3C%21doctype%20html%3E%3Chtml%3E%20%3Chead%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"620e44a8a55e82cec0d51e9d93025d8a5c4456fc":[async_test('html5lib_tests7.html 620e44a8a55e82cec0d51e9d93025d8a5c4456fc'), "%3C%21doctype%20html%3E%20%3Chead%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"37b910b755c2df155a3129d5a1150f0c0fdd7934":[async_test('html5lib_tests7.html 37b910b755c2df155a3129d5a1150f0c0fdd7934'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cstyle%3E%20%3Ctr%3Ex%20%3C/style%3E%20%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20%3Ctr%3Ex%20%22%0A%7C%20%20%20%20%20%20%20%22%20%22"],"868bff3a23219b836fdc702063d637f817ce65e1":[async_test('html5lib_tests7.html 868bff3a23219b836fdc702063d637f817ce65e1'), "%3C%21doctype%20html%3E%3Ctable%3E%3CTBODY%3E%3Cscript%3E%20%3Ctr%3Ex%20%3C/script%3E%20%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20%3Ctr%3Ex%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22"],"a33a56f5571b4bcb23138ffb60df3824f5c53773":[async_test('html5lib_tests7.html a33a56f5571b4bcb23138ffb60df3824f5c53773'), "%3C%21doctype%20html%3E%3Cp%3E%3Capplet%3E%3Cp%3EX%3C/p%3E%3C/applet%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Capplet%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"facf5e60205451cf740f64628b8608f0aee30f3a":[async_test('html5lib_tests7.html facf5e60205451cf740f64628b8608f0aee30f3a'), "%3C%21doctype%20html%3E%3Cp%3E%3Cobject%20type%3D%22application/x-non-existant-plugin%22%3E%3Cp%3EX%3C/p%3E%3C/object%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cobject%3E%0A%7C%20%20%20%20%20%20%20%20%20type%3D%22application/x-non-existant-plugin%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22X%22"],"8ba11b54fa74a1c229d079b2902d6e33e139f33b":[async_test('html5lib_tests7.html 8ba11b54fa74a1c229d079b2902d6e33e139f33b'), "%3C%21doctype%20html%3E%3Clisting%3E%0AX%3C/listing%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Clisting%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"84e2152c284f4dfee7d8d12846c08b2c025578a6":[async_test('html5lib_tests7.html 84e2152c284f4dfee7d8d12846c08b2c025578a6'), "%3C%21doctype%20html%3E%3Cselect%3E%3Cinput%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%22X%22"],"8e3432411baa59cbef731ab3ba2703cb5d518453":[async_test('html5lib_tests7.html 8e3432411baa59cbef731ab3ba2703cb5d518453'), "%3C%21doctype%20html%3E%3Cselect%3E%3Cselect%3EX", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%22X%22"],"e2f6144290512430ad25bbf9598eae77288c7b7a":[async_test('html5lib_tests7.html e2f6144290512430ad25bbf9598eae77288c7b7a'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cinput%20type%3DhidDEN%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%20%20type%3D%22hidDEN%22"],"350ebd648764d585f4aa0c29b925e6276579e9d0":[async_test('html5lib_tests7.html 350ebd648764d585f4aa0c29b925e6276579e9d0'), "%3C%21doctype%20html%3E%3Ctable%3EX%3Cinput%20type%3DhidDEN%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%20%20type%3D%22hidDEN%22"],"9120ef80d3ee017007f3510121ddf7eba31b79e0":[async_test('html5lib_tests7.html 9120ef80d3ee017007f3510121ddf7eba31b79e0'), "%3C%21doctype%20html%3E%3Ctable%3E%20%20%3Cinput%20type%3DhidDEN%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%22%20%20%22%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%20%20type%3D%22hidDEN%22"],"2026cd3ed42e41c168dd37c8c2675584f4eef335":[async_test('html5lib_tests7.html 2026cd3ed42e41c168dd37c8c2675584f4eef335'), "%3C%21doctype%20html%3E%3Ctable%3E%20%20%3Cinput%20type%3D%27hidDEN%27%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%22%20%20%22%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%20%20type%3D%22hidDEN%22"],"ff2e324237e22efc8430ad7137d50d6d3d311820":[async_test('html5lib_tests7.html ff2e324237e22efc8430ad7137d50d6d3d311820'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cinput%20type%3D%22%20hidden%22%3E%3Cinput%20type%3DhidDEN%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20type%3D%22%20hidden%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20%20%20type%3D%22hidDEN%22"],"02c9eb822611b0c206b544e0f2e5044695195ba8":[async_test('html5lib_tests7.html 02c9eb822611b0c206b544e0f2e5044695195ba8'), "%3C%21doctype%20html%3E%3Ctable%3E%3Cselect%3EX%3Ctr%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%22X%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"cb3d1a50dd56a85135a0856cfa1c23a091ef2af4":[async_test('html5lib_tests7.html cb3d1a50dd56a85135a0856cfa1c23a091ef2af4'), "%3C%21doctype%20html%3E%3Cselect%3EX%3C/select%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%22X%22"],"13847685cfff75642823a0e78c6ef232ecb9d94b":[async_test('html5lib_tests7.html 13847685cfff75642823a0e78c6ef232ecb9d94b'), "%3C%21DOCTYPE%20hTmL%3E%3Chtml%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"99bb5e9a6e0daf62ba418dd97b5e8e3925f4137e":[async_test('html5lib_tests7.html 99bb5e9a6e0daf62ba418dd97b5e8e3925f4137e'), "%3C%21DOCTYPE%20HTML%3E%3Chtml%3E%3C/html%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"7a8e5ec2c95e725717c564dd49bfa86c2e1a88ba":[async_test('html5lib_tests7.html 7a8e5ec2c95e725717c564dd49bfa86c2e1a88ba'), "%3Cdiv%3E%3Cp%3Ea%3C/x%3E%20b", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22a%20b%22"],"17dcea170bb74d18ed4776dbb98f0bac6a11364d":[async_test('html5lib_tests7.html 17dcea170bb74d18ed4776dbb98f0bac6a11364d'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Ccode%3E%3C/code%3E%20%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccode%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20%22"],"9457c10c9f987bbc95937b34763fe956d61d237b":[async_test('html5lib_tests7.html 9457c10c9f987bbc95937b34763fe956d61d237b'), "%3Ctable%3E%3Cb%3E%3Ctr%3E%3Ctd%3Eaaa%3C/td%3E%3C/tr%3Ebbb%3C/table%3Eccc", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22bbb%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22aaa%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22ccc%22"],"0fa23bb5d8b2a591afb1842b8f4c00c490c127b4":[async_test('html5lib_tests7.html 0fa23bb5d8b2a591afb1842b8f4c00c490c127b4'), "A%3Ctable%3E%3Ctr%3E%20B%3C/tr%3E%20B%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%20B%20B%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"f6d60b3ae48e2b69b4c25125f9b5a3ab4867521b":[async_test('html5lib_tests7.html f6d60b3ae48e2b69b4c25125f9b5a3ab4867521b'), "A%3Ctable%3E%3Ctr%3E%20B%3C/tr%3E%20%3C/em%3EC%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%20BC%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22"],"5b0b3edcc3ce9fdc9f58eb62d326865ca0aab8c8":[async_test('html5lib_tests7.html 5b0b3edcc3ce9fdc9f58eb62d326865ca0aab8c8'), "%3Cselect%3E%3Ckeygen%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%3Ckeygen%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests8.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests8.html
new file mode 100644
index 0000000000..ac8b5b6c15
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests8.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests8.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['5097f2cd0124cf5a23c7ccbe25f71a06966503df','0e11d51b0f71098caaccd166c368918c93683a7c','5c8ec9b2d6f03c2e971dc192897f3fcff92e5a32','a1fe2c2debb936fc1bf663f0d7228eb509522467','dbd09e012016b52703ab081360265d3bf96f3c76','bf7c4a4a4872a47746e3e26a2e57394352514c2a','a57d838264ec0d79c8b0c3cb1feb5cb941c0084d','263ff1438ee785d081669eea0fa110cca1d0d590','1ace730a87644923b11aa89e4e472cc5dd91edb7','26454c08b0d791754bf2f94fbee62624cae5fa5c',];
+ var tests = {
+ "5097f2cd0124cf5a23c7ccbe25f71a06966503df":[async_test('html5lib_tests8.html 5097f2cd0124cf5a23c7ccbe25f71a06966503df'), "%3Cdiv%3E%0A%3Cdiv%3E%3C/div%3E%0A%3C/span%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%0Ax%22"],"0e11d51b0f71098caaccd166c368918c93683a7c":[async_test('html5lib_tests8.html 0e11d51b0f71098caaccd166c368918c93683a7c'), "%3Cdiv%3Ex%3Cdiv%3E%3C/div%3E%0A%3C/span%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22%0Ax%22"],"5c8ec9b2d6f03c2e971dc192897f3fcff92e5a32":[async_test('html5lib_tests8.html 5c8ec9b2d6f03c2e971dc192897f3fcff92e5a32'), "%3Cdiv%3Ex%3Cdiv%3E%3C/div%3Ex%3C/span%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22xx%22"],"a1fe2c2debb936fc1bf663f0d7228eb509522467":[async_test('html5lib_tests8.html a1fe2c2debb936fc1bf663f0d7228eb509522467'), "%3Cdiv%3Ex%3Cdiv%3E%3C/div%3Ey%3C/span%3Ez", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22yz%22"],"dbd09e012016b52703ab081360265d3bf96f3c76":[async_test('html5lib_tests8.html dbd09e012016b52703ab081360265d3bf96f3c76'), "%3Ctable%3E%3Cdiv%3Ex%3Cdiv%3E%3C/div%3Ex%3C/span%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22xx%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"bf7c4a4a4872a47746e3e26a2e57394352514c2a":[async_test('html5lib_tests8.html bf7c4a4a4872a47746e3e26a2e57394352514c2a'), "%3Ctable%3E%3Cli%3E%3Cli%3E%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"a57d838264ec0d79c8b0c3cb1feb5cb941c0084d":[async_test('html5lib_tests8.html a57d838264ec0d79c8b0c3cb1feb5cb941c0084d'), "x%3Ctable%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22xx%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"263ff1438ee785d081669eea0fa110cca1d0d590":[async_test('html5lib_tests8.html 263ff1438ee785d081669eea0fa110cca1d0d590'), "x%3Ctable%3E%3Ctable%3Ex", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"1ace730a87644923b11aa89e4e472cc5dd91edb7":[async_test('html5lib_tests8.html 1ace730a87644923b11aa89e4e472cc5dd91edb7'), "%3Cb%3Ea%3Cdiv%3E%3C/div%3E%3Cdiv%3E%3C/b%3Ey", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22y%22"],"26454c08b0d791754bf2f94fbee62624cae5fa5c":[async_test('html5lib_tests8.html 26454c08b0d791754bf2f94fbee62624cae5fa5c'), "%3Ca%3E%3Cdiv%3E%3Cp%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tests9.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests9.html
new file mode 100644
index 0000000000..8cd9132d16
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tests9.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tests9.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['cb005f4b2a248cc98dc153d7391715b8d113cd0d','6b687e562bd878d3a6098f0a1b1c05b04dc8c02c','a28615629ac367bd8127ff3049e81b349e7ec7f6','d70e711bf9b7582d9b83488ab14f99b53a0f3a26','b2a8131e72e53265479c08cd18d4f4663278a021','a45a8948b799dadc321a86ff0bebf13167b5f076','9d6809ff0d5796525b655f44e8abe4267cfd84e1','2243c76da49f512eddbdf3f63965a70b8f3f5563','64ccbdfede00715ff2b6c0818d78774e0ea49fd4','58a5b5c13c3cc04948ca053afec1879602812beb','3be30b785cfbb6b1210720d032808e6484004fad','fe4054e577d1a3afa91c02b6782e35b76f2b3a50','9f12eef91092bcaaef567214144e1320b389296a','2f18900946d7a7f7922c929b72537e5ce8aacb70','2d4f10eec8e9623ef881edd20a639d3572134fd5','18485b14fd6568a096121ce8b8683a47945326d3','1d7a80644fe4b5f580c39adee71d5432cb186285','224e1bcb8030f0972c17d0fc68d912be17905e1c','e84d33cef974e49b69bdbc0c663c018a4dd010c0','5f4d3b90e4d99fae5ff97ebb9968185c77ffc591','00a77c689b7b8bb6440604f4273de3bfbcbcbe8b','15d4afd62caf2fcb27bb4aff89ba4fcb0e58c0b9','ef6c7a1da34520d2a4a90a0f2e8de9ed334bd482','0705988884bc08d8133e5d8a8eb693db5c86688e','093c0dbf464f9745c3730de57afebd9da308d34c','35aec8963beaced8149a74366eb9b1eb13be6717','931baaac96aab65ad0449b70c374ba56dcdbab9d',];
+ var tests = {
+ "cb005f4b2a248cc98dc153d7391715b8d113cd0d":[async_test('html5lib_tests9.html cb005f4b2a248cc98dc153d7391715b8d113cd0d'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E"],"6b687e562bd878d3a6098f0a1b1c05b04dc8c02c":[async_test('html5lib_tests9.html 6b687e562bd878d3a6098f0a1b1c05b04dc8c02c'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cmath%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E"],"a28615629ac367bd8127ff3049e81b349e7ec7f6":[async_test('html5lib_tests9.html a28615629ac367bd8127ff3049e81b349e7ec7f6'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cmi%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E"],"d70e711bf9b7582d9b83488ab14f99b53a0f3a26":[async_test('html5lib_tests9.html d70e711bf9b7582d9b83488ab14f99b53a0f3a26'), "%3C%21DOCTYPE%20html%3E%3Cmath%3E%3Cannotation-xml%3E%3Csvg%3E%3Cu%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20annotation-xml%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Cu%3E"],"b2a8131e72e53265479c08cd18d4f4663278a021":[async_test('html5lib_tests9.html b2a8131e72e53265479c08cd18d4f4663278a021'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cselect%3E%3Cmath%3E%3C/math%3E%3C/select%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E"],"a45a8948b799dadc321a86ff0bebf13167b5f076":[async_test('html5lib_tests9.html a45a8948b799dadc321a86ff0bebf13167b5f076'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Cselect%3E%3Coption%3E%3Cmath%3E%3C/math%3E%3C/option%3E%3C/select%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E"],"9d6809ff0d5796525b655f44e8abe4267cfd84e1":[async_test('html5lib_tests9.html 9d6809ff0d5796525b655f44e8abe4267cfd84e1'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Cmath%3E%3C/math%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"2243c76da49f512eddbdf3f63965a70b8f3f5563":[async_test('html5lib_tests9.html 2243c76da49f512eddbdf3f63965a70b8f3f5563'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3C/math%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"64ccbdfede00715ff2b6c0818d78774e0ea49fd4":[async_test('html5lib_tests9.html 64ccbdfede00715ff2b6c0818d78774e0ea49fd4'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3C/math%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Ctable%3E"],"58a5b5c13c3cc04948ca053afec1879602812beb":[async_test('html5lib_tests9.html 58a5b5c13c3cc04948ca053afec1879602812beb'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3C/math%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E"],"3be30b785cfbb6b1210720d032808e6484004fad":[async_test('html5lib_tests9.html 3be30b785cfbb6b1210720d032808e6484004fad'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Ctr%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3C/math%3E%3C/tr%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"fe4054e577d1a3afa91c02b6782e35b76f2b3a50":[async_test('html5lib_tests9.html fe4054e577d1a3afa91c02b6782e35b76f2b3a50'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Ctr%3E%3Ctd%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3C/math%3E%3C/td%3E%3C/tr%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22"],"9f12eef91092bcaaef567214144e1320b389296a":[async_test('html5lib_tests9.html 9f12eef91092bcaaef567214144e1320b389296a'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctbody%3E%3Ctr%3E%3Ctd%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3C/math%3E%3Cp%3Ebaz%3C/td%3E%3C/tr%3E%3C/tbody%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22baz%22"],"2f18900946d7a7f7922c929b72537e5ce8aacb70":[async_test('html5lib_tests9.html 2f18900946d7a7f7922c929b72537e5ce8aacb70'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccaption%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3C/math%3E%3Cp%3Ebaz%3C/caption%3E%3C/table%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22baz%22"],"2d4f10eec8e9623ef881edd20a639d3572134fd5":[async_test('html5lib_tests9.html 2d4f10eec8e9623ef881edd20a639d3572134fd5'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccaption%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"18485b14fd6568a096121ce8b8683a47945326d3":[async_test('html5lib_tests9.html 18485b14fd6568a096121ce8b8683a47945326d3'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccaption%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccaption%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"1d7a80644fe4b5f580c39adee71d5432cb186285":[async_test('html5lib_tests9.html 1d7a80644fe4b5f580c39adee71d5432cb186285'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ccolgroup%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22baz%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"224e1bcb8030f0972c17d0fc68d912be17905e1c":[async_test('html5lib_tests9.html 224e1bcb8030f0972c17d0fc68d912be17905e1c'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22foobarbaz%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"e84d33cef974e49b69bdbc0c663c018a4dd010c0":[async_test('html5lib_tests9.html e84d33cef974e49b69bdbc0c663c018a4dd010c0'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3Ctable%3E%3Cselect%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3Cp%3Ebaz%3C/table%3E%3Cp%3Equux", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%22foobarbaz%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22quux%22"],"5f4d3b90e4d99fae5ff97ebb9968185c77ffc591":[async_test('html5lib_tests9.html 5f4d3b90e4d99fae5ff97ebb9968185c77ffc591'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3C/body%3E%3C/html%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22baz%22"],"00a77c689b7b8bb6440604f4273de3bfbcbcbe8b":[async_test('html5lib_tests9.html 00a77c689b7b8bb6440604f4273de3bfbcbcbe8b'), "%3C%21DOCTYPE%20html%3E%3Cbody%3E%3C/body%3E%3Cmath%3E%3Cmi%3Efoo%3C/mi%3E%3Cmi%3Ebar%3C/mi%3E%3Cp%3Ebaz", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%22bar%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22baz%22"],"15d4afd62caf2fcb27bb4aff89ba4fcb0e58c0b9":[async_test('html5lib_tests9.html 15d4afd62caf2fcb27bb4aff89ba4fcb0e58c0b9'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3E%3Cmath%3E%3Cmi%3E%3C/mi%3E%3Cmi%3E%3C/mi%3E%3Cp%3E%3Cspan%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"ef6c7a1da34520d2a4a90a0f2e8de9ed334bd482":[async_test('html5lib_tests9.html ef6c7a1da34520d2a4a90a0f2e8de9ed334bd482'), "%3C%21DOCTYPE%20html%3E%3Cframeset%3E%3C/frameset%3E%3Cmath%3E%3Cmi%3E%3C/mi%3E%3Cmi%3E%3C/mi%3E%3Cp%3E%3Cspan%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"0705988884bc08d8133e5d8a8eb693db5c86688e":[async_test('html5lib_tests9.html 0705988884bc08d8133e5d8a8eb693db5c86688e'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%3E%3Cmath%20xlink%3Ahref%3Dfoo%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20xlink%20href%3D%22foo%22"],"093c0dbf464f9745c3730de57afebd9da308d34c":[async_test('html5lib_tests9.html 093c0dbf464f9745c3730de57afebd9da308d34c'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%20xml%3Alang%3Den%3E%3Cmath%3E%3Cmi%20xml%3Alang%3Den%20xlink%3Ahref%3Dfoo%3E%3C/mi%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20xml%3Alang%3D%22en%22%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20xlink%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20xml%20lang%3D%22en%22"],"35aec8963beaced8149a74366eb9b1eb13be6717":[async_test('html5lib_tests9.html 35aec8963beaced8149a74366eb9b1eb13be6717'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%20xml%3Alang%3Den%3E%3Cmath%3E%3Cmi%20xml%3Alang%3Den%20xlink%3Ahref%3Dfoo%20/%3E%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20xml%3Alang%3D%22en%22%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20xlink%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20xml%20lang%3D%22en%22"],"931baaac96aab65ad0449b70c374ba56dcdbab9d":[async_test('html5lib_tests9.html 931baaac96aab65ad0449b70c374ba56dcdbab9d'), "%3C%21DOCTYPE%20html%3E%3Cbody%20xlink%3Ahref%3Dfoo%20xml%3Alang%3Den%3E%3Cmath%3E%3Cmi%20xml%3Alang%3Den%20xlink%3Ahref%3Dfoo%20/%3Ebar%3C/math%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20xlink%3Ahref%3D%22foo%22%0A%7C%20%20%20%20%20xml%3Alang%3D%22en%22%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20xlink%20href%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20xml%20lang%3D%22en%22%0A%7C%20%20%20%20%20%20%20%22bar%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_tricky01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_tricky01.html
new file mode 100644
index 0000000000..f90e7f01f2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_tricky01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_tricky01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['06f0a6904729cd6a3ab91f3121c0b0eb54ee04d2','c99581b7d1d8c1cd421054891981c3fe8267e83c','09ba1d973acb46344442ea1e77a37de8736ce6e7','9e40dd21a29521d60a43cb016f4100501ea26ec8','23bed40fe77c77e3119528d9f77e041eeb77eebb','eced3856a69153ad0408470634ee37c284670bcf','f12f21485b685300c282d5eab08fe35c634e7708','8626fa9be928ded0aa6438e32037ef365bfdabc1','ee4120938804980035bacecb2d2597ae8cf254ac',];
+ var tests = {
+ "06f0a6904729cd6a3ab91f3121c0b0eb54ee04d2":[async_test('html5lib_tricky01.html 06f0a6904729cd6a3ab91f3121c0b0eb54ee04d2'), "%3Cb%3E%3Cp%3EBold%20%3C/b%3E%20Not%20bold%3C/p%3E%0AAlso%20not%20bold.", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%22Bold%20%22%0A%7C%20%20%20%20%20%20%20%22%20Not%20bold%22%0A%7C%20%20%20%20%20%22%0AAlso%20not%20bold.%22"],"c99581b7d1d8c1cd421054891981c3fe8267e83c":[async_test('html5lib_tricky01.html c99581b7d1d8c1cd421054891981c3fe8267e83c'), "%3Chtml%3E%0A%3Cfont%20color%3Dred%3E%3Ci%3EItalic%20and%20Red%3Cp%3EItalic%20and%20Red%20%3C/font%3E%20Just%20italic.%3C/p%3E%20Italic%20only.%3C/i%3E%20Plain%0A%3Cp%3EI%20should%20not%20be%20red.%20%3Cfont%20color%3Dred%3ERed.%20%3Ci%3EItalic%20and%20red.%3C/p%3E%0A%3Cp%3EItalic%20and%20red.%20%3C/i%3E%20Red.%3C/font%3E%20I%20should%20not%20be%20red.%3C/p%3E%0A%3Cb%3EBold%20%3Ci%3EBold%20and%20italic%3C/b%3E%20Only%20Italic%20%3C/i%3E%20Plain", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%22Italic%20and%20Red%22%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Italic%20and%20Red%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20Just%20italic.%22%0A%7C%20%20%20%20%20%20%20%22%20Italic%20only.%22%0A%7C%20%20%20%20%20%22%20Plain%0A%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22I%20should%20not%20be%20red.%20%22%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%20%20%22Red.%20%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Italic%20and%20red.%22%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20color%3D%22red%22%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Italic%20and%20red.%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20Red.%22%0A%7C%20%20%20%20%20%20%20%22%20I%20should%20not%20be%20red.%22%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22Bold%20%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%22Bold%20and%20italic%22%0A%7C%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%22%20Only%20Italic%20%22%0A%7C%20%20%20%20%20%22%20Plain%22"],"09ba1d973acb46344442ea1e77a37de8736ce6e7":[async_test('html5lib_tricky01.html 09ba1d973acb46344442ea1e77a37de8736ce6e7'), "%3Chtml%3E%3Cbody%3E%0A%3Cp%3E%3Cfont%20size%3D%227%22%3EFirst%20paragraph.%3C/p%3E%0A%3Cp%3ESecond%20paragraph.%3C/p%3E%3C/font%3E%0A%3Cb%3E%3Cp%3E%3Ci%3EBold%20and%20Italic%3C/b%3E%20Italic%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20size%3D%227%22%0A%7C%20%20%20%20%20%20%20%20%20%22First%20paragraph.%22%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20size%3D%227%22%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%20%20%22Second%20paragraph.%22%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Bold%20and%20Italic%22%0A%7C%20%20%20%20%20%20%20%3Ci%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%20Italic%22"],"9e40dd21a29521d60a43cb016f4100501ea26ec8":[async_test('html5lib_tricky01.html 9e40dd21a29521d60a43cb016f4100501ea26ec8'), "%3Chtml%3E%0A%3Cdl%3E%0A%3Cdt%3E%3Cb%3EBoo%0A%3Cdd%3EGoo%3F%0A%3C/dl%3E%0A%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdl%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%3Cdt%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Boo%0A%22%0A%7C%20%20%20%20%20%20%20%3Cdd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Goo%3F%0A%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22"],"23bed40fe77c77e3119528d9f77e041eeb77eebb":[async_test('html5lib_tricky01.html 23bed40fe77c77e3119528d9f77e041eeb77eebb'), "%3Chtml%3E%3Cbody%3E%0A%3Clabel%3E%3Ca%3E%3Cdiv%3EHello%3Cdiv%3EWorld%3C/div%3E%3C/a%3E%3C/label%3E%20%20%0A%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Clabel%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22Hello%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22World%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20%20%0A%22"],"eced3856a69153ad0408470634ee37c284670bcf":[async_test('html5lib_tricky01.html eced3856a69153ad0408470634ee37c284670bcf'), "%3Ctable%3E%3Ccenter%3E%20%3Cfont%3Ea%3C/center%3E%20%3Cimg%3E%20%3Ctr%3E%3Ctd%3E%20%3C/td%3E%20%3C/tr%3E%20%3C/table%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ccenter%3E%0A%7C%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%3Cimg%3E%0A%7C%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%20%22%0A%7C%20%20%20%20%20%20%20%20%20%22%20%22"],"f12f21485b685300c282d5eab08fe35c634e7708":[async_test('html5lib_tricky01.html f12f21485b685300c282d5eab08fe35c634e7708'), "%3Ctable%3E%3Ctr%3E%3Cp%3E%3Ca%3E%3Cp%3EYou%20should%20see%20this%20text.", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%22You%20should%20see%20this%20text.%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"8626fa9be928ded0aa6438e32037ef365bfdabc1":[async_test('html5lib_tricky01.html 8626fa9be928ded0aa6438e32037ef365bfdabc1'), "%3CTABLE%3E%0A%3CTR%3E%0A%3CCENTER%3E%3CCENTER%3E%3CTD%3E%3C/TD%3E%3C/TR%3E%3CTR%3E%0A%3CFONT%3E%0A%3CTABLE%3E%3Ctr%3E%3C/tr%3E%3C/TABLE%3E%0A%3C/P%3E%0A%3Ca%3E%3C/font%3E%3Cfont%3E%3C/a%3E%0AThis%20page%20contains%20an%20insanely%20badly-nested%20tag%20sequence.", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ccenter%3E%0A%7C%20%20%20%20%20%20%20%3Ccenter%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%22%0AThis%20page%20contains%20an%20insanely%20badly-nested%20tag%20sequence.%22"],"ee4120938804980035bacecb2d2597ae8cf254ac":[async_test('html5lib_tricky01.html ee4120938804980035bacecb2d2597ae8cf254ac'), "%3Chtml%3E%0A%3Cbody%3E%0A%3Cb%3E%3Cnobr%3E%3Cdiv%3EThis%20text%20is%20in%20a%20div%20inside%20a%20nobr%3C/nobr%3EMore%20text%20that%20should%20not%20be%20in%20the%20nobr%2C%20i.e.%2C%20the%0Anobr%20should%20have%20closed%20the%20div%20inside%20it%20implicitly.%20%3C/b%3E%3Cpre%3EA%20pre%20tag%20outside%20everything%20else.%3C/pre%3E%0A%3C/body%3E%0A%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22This%20text%20is%20in%20a%20div%20inside%20a%20nobr%22%0A%7C%20%20%20%20%20%20%20%20%20%22More%20text%20that%20should%20not%20be%20in%20the%20nobr%2C%20i.e.%2C%20the%0Anobr%20should%20have%20closed%20the%20div%20inside%20it%20implicitly.%20%22%0A%7C%20%20%20%20%20%20%20%3Cpre%3E%0A%7C%20%20%20%20%20%20%20%20%20%22A%20pre%20tag%20outside%20everything%20else.%22%0A%7C%20%20%20%20%20%20%20%22%0A%0A%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_webkit01.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_webkit01.html
new file mode 100644
index 0000000000..a94cdc7225
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_webkit01.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_webkit01.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['4235382bf15f93f7dd1096832ae74cc71edef4d7','9906bb30ae08654f4c67bf6d97040abbca91082d','97974a9c541d97c7bb5bd8ba97c2ccbe0c6e55bd','f30960ce7d5b25adc846e47823f977616d38b296','f3ed3ec3a14058fd97c9aad83299bc8836d21283','f073fda1df7d917e37a207c326bdc4db0b4b3481','5533bf52e328c5748a203be1bb245848de592783','5b753a783c228a1b423152d9707cf900e57bc5da','eea9ac89544ec31fb78f7629ea0e065bd7422c98','03a99ca235d60b3191a3c5671ff7df5ffca5372d','c37bc2e44b2765025f58c9680a560c1a3dc3ab93','c6b4dc9c0041dd5a069741dbf228f03439115b8d','d4613a2b82f5d4ec251149508096f8071a8714d5','0f78a3fae382185ef9ac8f767efafb401249c1e1','375260e547e078df727a3669f5c8dcef7ccf71a7','3bea2bf663be5de2bbcdad57ac95c5933e266d42','19944775fd9dd871fbc4cf813eb105c29bc5d834','25b53e528a0ad0b002c8a26b7260213a24981860','ce59a8ae9cf138cb81fd017711097d1643c6f227','913071287591cc570d512c824b419d9a172d4339','2669743ff272e43978ac0f8a2f2c602ec9036c26','a9e759bf3ce415ad6216c58ee906639c03ffa03f','1274e8cdad6a8957fa9b02708ee58b73de41ae02','bbbea9a3752a36a64f2b62f15383406b16113fe7','e0910f26aac678f50612da8d05d20aa29e140655','f30a9e97cdf3c54ffccaa7b9c2067ff109317530','f71e076cf210f54ee646d71fca7a8de5322149ff','1b6a0afa648100ebd5f8b6719b4d8c5887f3f732','0d99b69e40dbb898cd4a188aa4920042c30815b4','a20991a86f6d34fd67ddb4002e3972d82230d879','006dbf89bfa3df51d837e7557c3e32dfbc6f0f4c','7e8f1399f9c87dbd103303c6051873079b265360','5081f4afe652b92f23e80d275f67dec9139df8e3','f904db0ad0c00794f8a2ca238b4c36084993c741','d56fcf271bf902fc4739f055f031f0d11620057c','a897ca0ad0492945709d0772c91e33d485ddf9c7','7274654c671e2e1715a017a239a98399496b7ba6','346ed4219453716e3fee23ccddf283fde408d349','4d1b6f58e6bb11d27e2097f656abdd1122a45a67','bb7bc3a43938aef67cffb49084f27cc678cd9b7a','992947d22821e1eb7b9116a99420b6c7f5ad46f2','88eca99701e0ad1fda391722e5f0cdcf1e2b667d','4b36a9545e5d8df93c447eaa8b1dc42d704d9c61','dc2437252e38b998fec43311653af309a65ef35f','ec499b1124d241faaa28e2f985ecf5f3fa00fcf8','cd236e537fa5d967d11efea30d96cd6ef6c9f46b','2ee90ed930fca8db8278161af28adde1e0c3907b','9804e9659cd045f199d9f58ef85c2639724359aa','a785e349a36349db19df18c06032315c6356486c','401c8625b8574b46d03b8e95acd29358c82b53d3','1390b296dc6152683e9de9820194bca39e18cbd6','7dc7e88fa9eba71234bdb4037a15a4f70183a466',];
+ var tests = {
+ "4235382bf15f93f7dd1096832ae74cc71edef4d7":[async_test('html5lib_webkit01.html 4235382bf15f93f7dd1096832ae74cc71edef4d7'), "Test", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22Test%22"],"9906bb30ae08654f4c67bf6d97040abbca91082d":[async_test('html5lib_webkit01.html 9906bb30ae08654f4c67bf6d97040abbca91082d'), "%3Cdiv%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E"],"97974a9c541d97c7bb5bd8ba97c2ccbe0c6e55bd":[async_test('html5lib_webkit01.html 97974a9c541d97c7bb5bd8ba97c2ccbe0c6e55bd'), "%3Cdiv%3ETest%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22Test%22"],"f30960ce7d5b25adc846e47823f977616d38b296":[async_test('html5lib_webkit01.html f30960ce7d5b25adc846e47823f977616d38b296'), "%3Cdi", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"f3ed3ec3a14058fd97c9aad83299bc8836d21283":[async_test('html5lib_webkit01.html f3ed3ec3a14058fd97c9aad83299bc8836d21283'), "%3Cdiv%3EHello%3C/div%3E%0A%3Cscript%3E%0Aconsole.log%28%22PASS%22%29%3B%0A%3C/script%3E%0A%3Cdiv%3EBye%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22Hello%22%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%0Aconsole.log%28%22PASS%22%29%3B%0A%22%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22Bye%22"],"f073fda1df7d917e37a207c326bdc4db0b4b3481":[async_test('html5lib_webkit01.html f073fda1df7d917e37a207c326bdc4db0b4b3481'), "%3Cdiv%20foo%3D%22bar%22%3EHello%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20foo%3D%22bar%22%0A%7C%20%20%20%20%20%20%20%22Hello%22"],"5533bf52e328c5748a203be1bb245848de592783":[async_test('html5lib_webkit01.html 5533bf52e328c5748a203be1bb245848de592783'), "%3Cdiv%3EHello%3C/div%3E%0A%3Cscript%3E%0Aconsole.log%28%22FOO%3Cspan%3EBAR%3C/span%3EBAZ%22%29%3B%0A%3C/script%3E%0A%3Cdiv%3EBye%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22Hello%22%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cscript%3E%0A%7C%20%20%20%20%20%20%20%22%0Aconsole.log%28%22FOO%3Cspan%3EBAR%3C/span%3EBAZ%22%29%3B%0A%22%0A%7C%20%20%20%20%20%22%0A%22%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%22Bye%22"],"5b753a783c228a1b423152d9707cf900e57bc5da":[async_test('html5lib_webkit01.html 5b753a783c228a1b423152d9707cf900e57bc5da'), "%3Cfoo%20bar%3D%22baz%22%3E%3C/foo%3E%3Cpotato%20quack%3D%22duck%22%3E%3C/potato%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22baz%22%0A%7C%20%20%20%20%20%3Cpotato%3E%0A%7C%20%20%20%20%20%20%20quack%3D%22duck%22"],"eea9ac89544ec31fb78f7629ea0e065bd7422c98":[async_test('html5lib_webkit01.html eea9ac89544ec31fb78f7629ea0e065bd7422c98'), "%3Cfoo%20bar%3D%22baz%22%3E%3Cpotato%20quack%3D%22duck%22%3E%3C/potato%3E%3C/foo%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22baz%22%0A%7C%20%20%20%20%20%20%20%3Cpotato%3E%0A%7C%20%20%20%20%20%20%20%20%20quack%3D%22duck%22"],"03a99ca235d60b3191a3c5671ff7df5ffca5372d":[async_test('html5lib_webkit01.html 03a99ca235d60b3191a3c5671ff7df5ffca5372d'), "%3Cfoo%3E%3C/foo%20bar%3D%22baz%22%3E%3Cpotato%3E%3C/potato%20quack%3D%22duck%22%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%3Cpotato%3E"],"c37bc2e44b2765025f58c9680a560c1a3dc3ab93":[async_test('html5lib_webkit01.html c37bc2e44b2765025f58c9680a560c1a3dc3ab93'), "%3C/%20tttt%3E", "%23document%0A%7C%20%3C%21--%20%20tttt%20--%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"c6b4dc9c0041dd5a069741dbf228f03439115b8d":[async_test('html5lib_webkit01.html c6b4dc9c0041dd5a069741dbf228f03439115b8d'), "%3Cdiv%20FOO%20%3E%3Cimg%3E%3Cimg%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20foo%3D%22%22%0A%7C%20%20%20%20%20%20%20%3Cimg%3E%0A%7C%20%20%20%20%20%20%20%3Cimg%3E"],"d4613a2b82f5d4ec251149508096f8071a8714d5":[async_test('html5lib_webkit01.html d4613a2b82f5d4ec251149508096f8071a8714d5'), "%3Cp%3ETest%3C/p%3Cp%3ETest2%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%22TestTest2%22"],"0f78a3fae382185ef9ac8f767efafb401249c1e1":[async_test('html5lib_webkit01.html 0f78a3fae382185ef9ac8f767efafb401249c1e1'), "%3Crdar%3A//problem/6869687%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Crdar%3A%3E%0A%7C%20%20%20%20%20%20%206869687%3D%22%22%0A%7C%20%20%20%20%20%20%20problem%3D%22%22"],"375260e547e078df727a3669f5c8dcef7ccf71a7":[async_test('html5lib_webkit01.html 375260e547e078df727a3669f5c8dcef7ccf71a7'), "%3CA%3Etest%3C%20/A%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%22test%3C%20/A%3E%22"],"3bea2bf663be5de2bbcdad57ac95c5933e266d42":[async_test('html5lib_webkit01.html 3bea2bf663be5de2bbcdad57ac95c5933e266d42'), "%26lt%3B", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%3C%22"],"19944775fd9dd871fbc4cf813eb105c29bc5d834":[async_test('html5lib_webkit01.html 19944775fd9dd871fbc4cf813eb105c29bc5d834'), "%3Cbody%20foo%3D%27bar%27%3E%3Cbody%20foo%3D%27baz%27%20yo%3D%27mama%27%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20foo%3D%22bar%22%0A%7C%20%20%20%20%20yo%3D%22mama%22"],"25b53e528a0ad0b002c8a26b7260213a24981860":[async_test('html5lib_webkit01.html 25b53e528a0ad0b002c8a26b7260213a24981860'), "%3Cbody%3E%3C/br%20foo%3D%22bar%22%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbr%3E"],"ce59a8ae9cf138cb81fd017711097d1643c6f227":[async_test('html5lib_webkit01.html ce59a8ae9cf138cb81fd017711097d1643c6f227'), "%3Cbdy%3E%3Cbr%20foo%3D%22bar%22%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbdy%3E%0A%7C%20%20%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%20%20%20%20foo%3D%22bar%22"],"913071287591cc570d512c824b419d9a172d4339":[async_test('html5lib_webkit01.html 913071287591cc570d512c824b419d9a172d4339'), "%3Cbody%3E%3C/body%3E%3C/br%20foo%3D%22bar%22%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbr%3E"],"2669743ff272e43978ac0f8a2f2c602ec9036c26":[async_test('html5lib_webkit01.html 2669743ff272e43978ac0f8a2f2c602ec9036c26'), "%3Cbdy%3E%3C/body%3E%3Cbr%20foo%3D%22bar%22%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cbdy%3E%0A%7C%20%20%20%20%20%20%20%3Cbr%3E%0A%7C%20%20%20%20%20%20%20%20%20foo%3D%22bar%22"],"a9e759bf3ce415ad6216c58ee906639c03ffa03f":[async_test('html5lib_webkit01.html a9e759bf3ce415ad6216c58ee906639c03ffa03f'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%3C/html%3E%3C%21--%20Hi%20there%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%3C%21--%20%20Hi%20there%20%20--%3E"],"1274e8cdad6a8957fa9b02708ee58b73de41ae02":[async_test('html5lib_webkit01.html 1274e8cdad6a8957fa9b02708ee58b73de41ae02'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%3C/html%3E%3C%21--%20Comment%20A%20--%3E%3C%21--%20Comment%20B%20--%3E%3C%21--%20Comment%20C%20--%3E%3C%21--%20Comment%20D%20--%3E%3C%21--%20Comment%20E%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%3C%21--%20%20Comment%20A%20%20--%3E%0A%7C%20%3C%21--%20%20Comment%20B%20%20--%3E%0A%7C%20%3C%21--%20%20Comment%20C%20%20--%3E%0A%7C%20%3C%21--%20%20Comment%20D%20%20--%3E%0A%7C%20%3C%21--%20%20Comment%20E%20%20--%3E"],"bbbea9a3752a36a64f2b62f15383406b16113fe7":[async_test('html5lib_webkit01.html bbbea9a3752a36a64f2b62f15383406b16113fe7'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%3C/html%3Ex%3C%21--%20Hi%20there%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3C%21--%20%20Hi%20there%20%20--%3E"],"e0910f26aac678f50612da8d05d20aa29e140655":[async_test('html5lib_webkit01.html e0910f26aac678f50612da8d05d20aa29e140655'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%3C/html%3Ex%3C%21--%20Hi%20there%20--%3E%3C/html%3E%3C%21--%20Again%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3C%21--%20%20Hi%20there%20%20--%3E%0A%7C%20%3C%21--%20%20Again%20%20--%3E"],"f30a9e97cdf3c54ffccaa7b9c2067ff109317530":[async_test('html5lib_webkit01.html f30a9e97cdf3c54ffccaa7b9c2067ff109317530'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%3C/html%3Ex%3C%21--%20Hi%20there%20--%3E%3C/body%3E%3C/html%3E%3C%21--%20Again%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22x%22%0A%7C%20%20%20%20%20%3C%21--%20%20Hi%20there%20%20--%3E%0A%7C%20%3C%21--%20%20Again%20%20--%3E"],"f71e076cf210f54ee646d71fca7a8de5322149ff":[async_test('html5lib_webkit01.html f71e076cf210f54ee646d71fca7a8de5322149ff'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%0A%20%20%20%3C%21--%20Hi%20there%20--%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%0A%20%20%20%22%0A%7C%20%20%20%3C%21--%20%20Hi%20there%20%20--%3E"],"1b6a0afa648100ebd5f8b6719b4d8c5887f3f732":[async_test('html5lib_webkit01.html 1b6a0afa648100ebd5f8b6719b4d8c5887f3f732'), "%3Chtml%3E%3Cbody%3E%3C/body%3E%3C/html%3E%0A%20%20%20%3C%21--%20Hi%20there%20--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22%0A%20%20%20%22%0A%7C%20%3C%21--%20%20Hi%20there%20%20--%3E"],"0d99b69e40dbb898cd4a188aa4920042c30815b4":[async_test('html5lib_webkit01.html 0d99b69e40dbb898cd4a188aa4920042c30815b4'), "%3Chtml%3E%3Cbody%3E%3Cruby%3E%3Cdiv%3E%3Crp%3Exx%3C/rp%3E%3C/div%3E%3C/ruby%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Crp%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22xx%22"],"a20991a86f6d34fd67ddb4002e3972d82230d879":[async_test('html5lib_webkit01.html a20991a86f6d34fd67ddb4002e3972d82230d879'), "%3Chtml%3E%3Cbody%3E%3Cruby%3E%3Cdiv%3E%3Crt%3Exx%3C/rt%3E%3C/div%3E%3C/ruby%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cruby%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Crt%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22xx%22"],"006dbf89bfa3df51d837e7557c3e32dfbc6f0f4c":[async_test('html5lib_webkit01.html 006dbf89bfa3df51d837e7557c3e32dfbc6f0f4c'), "%3Chtml%3E%3Cframeset%3E%3C%21--1--%3E%3Cnoframes%3EA%3C/noframes%3E%3C%21--2--%3E%3C/frameset%3E%3C%21--3--%3E%3Cnoframes%3EB%3C/noframes%3E%3C%21--4--%3E%3C/html%3E%3C%21--5--%3E%3Cnoframes%3EC%3C/noframes%3E%3C%21--6--%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E%0A%7C%20%20%20%20%20%3C%21--%201%20--%3E%0A%7C%20%20%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%3C%21--%202%20--%3E%0A%7C%20%20%20%3C%21--%203%20--%3E%0A%7C%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%22B%22%0A%7C%20%20%20%3C%21--%204%20--%3E%0A%7C%20%20%20%3Cnoframes%3E%0A%7C%20%20%20%20%20%22C%22%0A%7C%20%3C%21--%205%20--%3E%0A%7C%20%3C%21--%206%20--%3E"],"7e8f1399f9c87dbd103303c6051873079b265360":[async_test('html5lib_webkit01.html 7e8f1399f9c87dbd103303c6051873079b265360'), "%3Cselect%3E%3Coption%3EA%3Cselect%3E%3Coption%3EB%3Cselect%3E%3Coption%3EC%3Cselect%3E%3Coption%3ED%3Cselect%3E%3Coption%3EE%3Cselect%3E%3Coption%3EF%3Cselect%3E%3Coption%3EG%3Cselect%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%22B%22%0A%7C%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22C%22%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%22D%22%0A%7C%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22E%22%0A%7C%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%22F%22%0A%7C%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22G%22"],"5081f4afe652b92f23e80d275f67dec9139df8e3":[async_test('html5lib_webkit01.html 5081f4afe652b92f23e80d275f67dec9139df8e3'), "%3Cdd%3E%3Cdd%3E%3Cdt%3E%3Cdt%3E%3Cdd%3E%3Cli%3E%3Cli%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdd%3E%0A%7C%20%20%20%20%20%3Cdd%3E%0A%7C%20%20%20%20%20%3Cdt%3E%0A%7C%20%20%20%20%20%3Cdt%3E%0A%7C%20%20%20%20%20%3Cdd%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E"],"f904db0ad0c00794f8a2ca238b4c36084993c741":[async_test('html5lib_webkit01.html f904db0ad0c00794f8a2ca238b4c36084993c741'), "%3Cdiv%3E%3Cb%3E%3C/div%3E%3Cdiv%3E%3Cnobr%3Ea%3Cnobr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cnobr%3E"],"d56fcf271bf902fc4739f055f031f0d11620057c":[async_test('html5lib_webkit01.html d56fcf271bf902fc4739f055f031f0d11620057c'), "%3Chead%3E%3C/head%3E%0A%3Cbody%3E%3C/body%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%22%0A%22%0A%7C%20%20%20%3Cbody%3E"],"a897ca0ad0492945709d0772c91e33d485ddf9c7":[async_test('html5lib_webkit01.html a897ca0ad0492945709d0772c91e33d485ddf9c7'), "%3Chead%3E%3C/head%3E%20%3Cstyle%3E%3C/style%3Eddd", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%22%20%22%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22ddd%22"],"7274654c671e2e1715a017a239a98399496b7ba6":[async_test('html5lib_webkit01.html 7274654c671e2e1715a017a239a98399496b7ba6'), "%3Ckbd%3E%3Ctable%3E%3C/kbd%3E%3Ccol%3E%3Cselect%3E%3Ctr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ckbd%3E%0A%7C%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E"],"346ed4219453716e3fee23ccddf283fde408d349":[async_test('html5lib_webkit01.html 346ed4219453716e3fee23ccddf283fde408d349'), "%3Ckbd%3E%3Ctable%3E%3C/kbd%3E%3Ccol%3E%3Cselect%3E%3Ctr%3E%3C/table%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ckbd%3E%0A%7C%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ccolgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ccol%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%3Cdiv%3E"],"4d1b6f58e6bb11d27e2097f656abdd1122a45a67":[async_test('html5lib_webkit01.html 4d1b6f58e6bb11d27e2097f656abdd1122a45a67'), "%3Ca%3E%3Cli%3E%3Cstyle%3E%3C/style%3E%3Ctitle%3E%3C/title%3E%3C/a%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctitle%3E"],"bb7bc3a43938aef67cffb49084f27cc678cd9b7a":[async_test('html5lib_webkit01.html bb7bc3a43938aef67cffb49084f27cc678cd9b7a'), "%3Cfont%3E%3C/p%3E%3Cp%3E%3Cmeta%3E%3Ctitle%3E%3C/title%3E%3C/font%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20%3Cfont%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmeta%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctitle%3E"],"992947d22821e1eb7b9116a99420b6c7f5ad46f2":[async_test('html5lib_webkit01.html 992947d22821e1eb7b9116a99420b6c7f5ad46f2'), "%3Ca%3E%3Ccenter%3E%3Ctitle%3E%3C/title%3E%3Ca%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%3Ccenter%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctitle%3E%0A%7C%20%20%20%20%20%20%20%3Ca%3E"],"88eca99701e0ad1fda391722e5f0cdcf1e2b667d":[async_test('html5lib_webkit01.html 88eca99701e0ad1fda391722e5f0cdcf1e2b667d'), "%3Csvg%3E%3Ctitle%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"4b36a9545e5d8df93c447eaa8b1dc42d704d9c61":[async_test('html5lib_webkit01.html 4b36a9545e5d8df93c447eaa8b1dc42d704d9c61'), "%3Csvg%3E%3Ctitle%3E%3Crect%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Crect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"dc2437252e38b998fec43311653af309a65ef35f":[async_test('html5lib_webkit01.html dc2437252e38b998fec43311653af309a65ef35f'), "%3Csvg%3E%3Ctitle%3E%3Csvg%3E%3Cdiv%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"ec499b1124d241faaa28e2f985ecf5f3fa00fcf8":[async_test('html5lib_webkit01.html ec499b1124d241faaa28e2f985ecf5f3fa00fcf8'), "%3Cimg%20%3C%3D%22%22%20FAIL%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cimg%3E%0A%7C%20%20%20%20%20%20%20%3C%3D%22%22%0A%7C%20%20%20%20%20%20%20fail%3D%22%22"],"cd236e537fa5d967d11efea30d96cd6ef6c9f46b":[async_test('html5lib_webkit01.html cd236e537fa5d967d11efea30d96cd6ef6c9f46b'), "%3Cul%3E%3Cli%3E%3Cdiv%20id%3D%27foo%27/%3EA%3C/li%3E%3Cli%3EB%3Cdiv%3EC%3C/div%3E%3C/li%3E%3C/ul%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cul%3E%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20id%3D%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%20%20%3Cli%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22C%22"],"2ee90ed930fca8db8278161af28adde1e0c3907b":[async_test('html5lib_webkit01.html 2ee90ed930fca8db8278161af28adde1e0c3907b'), "%3Csvg%3E%3Cem%3E%3Cdesc%3E%3C/em%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%3Cdesc%3E"],"9804e9659cd045f199d9f58ef85c2639724359aa":[async_test('html5lib_webkit01.html 9804e9659cd045f199d9f58ef85c2639724359aa'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Csvg%3E%3Cdesc%3E%3Ctd%3E%3C/desc%3E%3Ccircle%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Csvg%20desc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ccircle%3E"],"a785e349a36349db19df18c06032315c6356486c":[async_test('html5lib_webkit01.html a785e349a36349db19df18c06032315c6356486c'), "%3Csvg%3E%3Ctfoot%3E%3C/mi%3E%3Ctd%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20tfoot%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Csvg%20td%3E"],"401c8625b8574b46d03b8e95acd29358c82b53d3":[async_test('html5lib_webkit01.html 401c8625b8574b46d03b8e95acd29358c82b53d3'), "%3Cmath%3E%3Cmrow%3E%3Cmrow%3E%3Cmn%3E1%3C/mn%3E%3C/mrow%3E%3Cmi%3Ea%3C/mi%3E%3C/mrow%3E%3C/math%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20%3Cmath%20mrow%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mrow%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cmath%20mn%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%221%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cmath%20mi%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22a%22"],"1390b296dc6152683e9de9820194bca39e18cbd6":[async_test('html5lib_webkit01.html 1390b296dc6152683e9de9820194bca39e18cbd6'), "%3C%21doctype%20html%3E%3Cinput%20type%3D%22hidden%22%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cframeset%3E"],"7dc7e88fa9eba71234bdb4037a15a4f70183a466":[async_test('html5lib_webkit01.html 7dc7e88fa9eba71234bdb4037a15a4f70183a466'), "%3C%21doctype%20html%3E%3Cinput%20type%3D%22button%22%3E%3Cframeset%3E", "%23document%0A%7C%20%3C%21DOCTYPE%20html%3E%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%20%20type%3D%22button%22"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html5lib_webkit02.html b/testing/web-platform/tests/html/syntax/parsing/html5lib_webkit02.html
new file mode 100644
index 0000000000..51900fa54a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html5lib_webkit02.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>HTML 5 Parser tests html5lib_webkit02.html</title>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?run_type=uri">
+ <meta name="variant" content="?run_type=write">
+ <meta name="variant" content="?run_type=write_single">
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+ var order = ['f50b8c15847159a6d2c6ecc2bd1e4a944ba5aae6','326328ea805a2ebdde707e08567713f88a4cf8ab','05138397908cfdad69a3bfe5da5a06098320b504','2aaa2ac0d7cec6144633d8f82f3bcaafa7498cd9','4a256d7ef602c7c917c758e15981b9710f9b4130','98cea04429ddbe4ffaaa0b91fe77b8c0b1f7c1f4','209ad7d6f6c9c53cb856c7d78b2bc4a7f38abd5f','cb9a86fbac96b08a6e708a2dbcd9f78539dfe9c6','c46a4badc6b1ebc524e6f90ea56183310e93ab25','464eeaecc49646ff810cadad537880c9b473a262','7b4eb6981451ede406f2f4112e83a8584e7adbf5','73aed96d7cd3116e4a3e701104616c07d1ec5e0c','139a546c72bfcedf638d031f33da43f24995f688','6e33515b4dc011dd390d433a6358bf68b786b1fd','b6d2377b0dd710ca812c97b2b65cb5d2e93b0e5b','21a5b2b413c4db8ed588334b9a50dea9872bbcfa','90d3f6f2dff994f63293ca46f7cd50a75cde96a6','3e6b37a5fd0b16769f71f8e6a022ef6a972769d0','66c5e72324003827309d3590a9ba56412ba68f79','329d2522353afa12afea478bbfafdb0ff0656572','5463526d91a8677b27b6967866d6605f1bb03aac','4879f476053094cf5602d325724675378856a902','44c88b90236f01ebc8e0123363b527640a07070c','cfb304e8f2d3cbdecc362226e7775cab452d5489','3fc625e7cb9b6ea72a9e252ede84c6fdd9680d87','ecd089f9b5193fad306c5b475c4711547fe5e209','cee2230c74671c594a1140a68d16e3d3e5ae005a','22b9fe36797d70a3b71a6aadc6ad7cff23c3fc90','a82c3bf49c381b5f58c5c8a4bbbe0cef2458e28a','61f8d527795dc8044a95a3e2437de81e16597ceb',];
+ var tests = {
+ "f50b8c15847159a6d2c6ecc2bd1e4a944ba5aae6":[async_test('html5lib_webkit02.html f50b8c15847159a6d2c6ecc2bd1e4a944ba5aae6'), "%3Cfoo%20bar%3Dqux/%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20bar%3D%22qux/%22"],"326328ea805a2ebdde707e08567713f88a4cf8ab":[async_test('html5lib_webkit02.html 326328ea805a2ebdde707e08567713f88a4cf8ab'), "%3Cp%20id%3D%22status%22%3E%3Cnoscript%3E%3Cstrong%3EA%3C/strong%3E%3C/noscript%3E%3Cspan%3EB%3C/span%3E%3C/p%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cp%3E%0A%7C%20%20%20%20%20%20%20id%3D%22status%22%0A%7C%20%20%20%20%20%20%20%3Cnoscript%3E%0A%7C%20%20%20%20%20%20%20%20%20%22%3Cstrong%3EA%3C/strong%3E%22%0A%7C%20%20%20%20%20%20%20%3Cspan%3E%0A%7C%20%20%20%20%20%20%20%20%20%22B%22"],"05138397908cfdad69a3bfe5da5a06098320b504":[async_test('html5lib_webkit02.html 05138397908cfdad69a3bfe5da5a06098320b504'), "%3Cdiv%3E%3Csarcasm%3E%3Cdiv%3E%3C/div%3E%3C/sarcasm%3E%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%3Csarcasm%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E"],"2aaa2ac0d7cec6144633d8f82f3bcaafa7498cd9":[async_test('html5lib_webkit02.html 2aaa2ac0d7cec6144633d8f82f3bcaafa7498cd9'), "%3Chtml%3E%3Cbody%3E%3Cimg%20src%3D%22%22%20border%3D%220%22%20alt%3D%22%3E%3Cdiv%3EA%3C/div%3E%3C/body%3E%3C/html%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E"],"4a256d7ef602c7c917c758e15981b9710f9b4130":[async_test('html5lib_webkit02.html 4a256d7ef602c7c917c758e15981b9710f9b4130'), "%3Ctable%3E%3Ctd%3E%3C/tbody%3EA", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%22A%22%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E"],"98cea04429ddbe4ffaaa0b91fe77b8c0b1f7c1f4":[async_test('html5lib_webkit02.html 98cea04429ddbe4ffaaa0b91fe77b8c0b1f7c1f4'), "%3Ctable%3E%3Ctd%3E%3C/thead%3EA", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22A%22"],"209ad7d6f6c9c53cb856c7d78b2bc4a7f38abd5f":[async_test('html5lib_webkit02.html 209ad7d6f6c9c53cb856c7d78b2bc4a7f38abd5f'), "%3Ctable%3E%3Ctd%3E%3C/tfoot%3EA", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22A%22"],"cb9a86fbac96b08a6e708a2dbcd9f78539dfe9c6":[async_test('html5lib_webkit02.html cb9a86fbac96b08a6e708a2dbcd9f78539dfe9c6'), "%3Ctable%3E%3Cthead%3E%3Ctd%3E%3C/tbody%3EA", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Cthead%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%22A%22"],"c46a4badc6b1ebc524e6f90ea56183310e93ab25":[async_test('html5lib_webkit02.html c46a4badc6b1ebc524e6f90ea56183310e93ab25'), "%3Clegend%3Etest%3C/legend%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Clegend%3E%0A%7C%20%20%20%20%20%20%20%22test%22"],"464eeaecc49646ff810cadad537880c9b473a262":[async_test('html5lib_webkit02.html 464eeaecc49646ff810cadad537880c9b473a262'), "%3Ctable%3E%3Cinput%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cinput%3E%0A%7C%20%20%20%20%20%3Ctable%3E"],"7b4eb6981451ede406f2f4112e83a8584e7adbf5":[async_test('html5lib_webkit02.html 7b4eb6981451ede406f2f4112e83a8584e7adbf5'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoo%3E%3Caside%3E%3C/b%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%3Caside%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E"],"73aed96d7cd3116e4a3e701104616c07d1ec5e0c":[async_test('html5lib_webkit02.html 73aed96d7cd3116e4a3e701104616c07d1ec5e0c'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoo%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Caside%3E%0A%7C%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cb%3E"],"139a546c72bfcedf638d031f33da43f24995f688":[async_test('html5lib_webkit02.html 139a546c72bfcedf638d031f33da43f24995f688'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Caside%3E%3C/b%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%3Caside%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E"],"6e33515b4dc011dd390d433a6358bf68b786b1fd":[async_test('html5lib_webkit02.html 6e33515b4dc011dd390d433a6358bf68b786b1fd'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cb%3E%0A%7C%20%20%20%20%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%3Caside%3E%0A%7C%20%20%20%20%20%20%20%3Cb%3E"],"b6d2377b0dd710ca812c97b2b65cb5d2e93b0e5b":[async_test('html5lib_webkit02.html b6d2377b0dd710ca812c97b2b65cb5d2e93b0e5b'), "%3Csvg%3E%3CforeignObject%3E%3Cdiv%3Efoo%3C/div%3E%3Cplaintext%3E%3C/foreignObject%3E%3C/svg%3E%3Cdiv%3Ebar%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cdiv%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22foo%22%0A%7C%20%20%20%20%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%22%3C/foreignObject%3E%3C/svg%3E%3Cdiv%3Ebar%3C/div%3E%22"],"21a5b2b413c4db8ed588334b9a50dea9872bbcfa":[async_test('html5lib_webkit02.html 21a5b2b413c4db8ed588334b9a50dea9872bbcfa'), "%3Csvg%3E%3CforeignObject%3E%3C/foreignObject%3E%3Ctitle%3E%3C/svg%3Efoo", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20foreignObject%3E%0A%7C%20%20%20%20%20%20%20%3Csvg%20title%3E%0A%7C%20%20%20%20%20%22foo%22"],"90d3f6f2dff994f63293ca46f7cd50a75cde96a6":[async_test('html5lib_webkit02.html 90d3f6f2dff994f63293ca46f7cd50a75cde96a6'), "%3C/foreignObject%3E%3Cplaintext%3E%3Cdiv%3Efoo%3C/div%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cplaintext%3E%0A%7C%20%20%20%20%20%20%20%22%3Cdiv%3Efoo%3C/div%3E%22"],"3e6b37a5fd0b16769f71f8e6a022ef6a972769d0":[async_test('html5lib_webkit02.html 3e6b37a5fd0b16769f71f8e6a022ef6a972769d0'), "%3Csvg%20xml%3Abase%20xml%3Alang%20xml%3Aspace%20xml%3Abaaah%20definitionurl%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Csvg%20svg%3E%0A%7C%20%20%20%20%20%20%20definitionurl%3D%22%22%0A%7C%20%20%20%20%20%20%20xml%20lang%3D%22%22%0A%7C%20%20%20%20%20%20%20xml%20space%3D%22%22%0A%7C%20%20%20%20%20%20%20xml%3Abaaah%3D%22%22%0A%7C%20%20%20%20%20%20%20xml%3Abase%3D%22%22"],"66c5e72324003827309d3590a9ba56412ba68f79":[async_test('html5lib_webkit02.html 66c5e72324003827309d3590a9ba56412ba68f79'), "%3Cmath%20definitionurl%20xlink%3Atitle%20xlink%3Ashow%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20definitionURL%3D%22%22%0A%7C%20%20%20%20%20%20%20xlink%20show%3D%22%22%0A%7C%20%20%20%20%20%20%20xlink%20title%3D%22%22"],"329d2522353afa12afea478bbfafdb0ff0656572":[async_test('html5lib_webkit02.html 329d2522353afa12afea478bbfafdb0ff0656572'), "%3Cmath%20DEFINITIONURL%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cmath%20math%3E%0A%7C%20%20%20%20%20%20%20definitionURL%3D%22%22"],"5463526d91a8677b27b6967866d6605f1bb03aac":[async_test('html5lib_webkit02.html 5463526d91a8677b27b6967866d6605f1bb03aac'), "%3Cselect%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Chr%3E"],"4879f476053094cf5602d325724675378856a902":[async_test('html5lib_webkit02.html 4879f476053094cf5602d325724675378856a902'), "%3Cselect%3E%3Coption%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Chr%3E"],"44c88b90236f01ebc8e0123363b527640a07070c":[async_test('html5lib_webkit02.html 44c88b90236f01ebc8e0123363b527640a07070c'), "%3Cselect%3E%3Coptgroup%3E%3Coption%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Chr%3E"],"cfb304e8f2d3cbdecc362226e7775cab452d5489":[async_test('html5lib_webkit02.html cfb304e8f2d3cbdecc362226e7775cab452d5489'), "%3Cselect%3E%3Coptgroup%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%3Chr%3E"],"3fc625e7cb9b6ea72a9e252ede84c6fdd9680d87":[async_test('html5lib_webkit02.html 3fc625e7cb9b6ea72a9e252ede84c6fdd9680d87'), "%3Cselect%3E%3Coption%3E%3Coptgroup%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%3Chr%3E"],"ecd089f9b5193fad306c5b475c4711547fe5e209":[async_test('html5lib_webkit02.html ecd089f9b5193fad306c5b475c4711547fe5e209'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chr%3E"],"cee2230c74671c594a1140a68d16e3d3e5ae005a":[async_test('html5lib_webkit02.html cee2230c74671c594a1140a68d16e3d3e5ae005a'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Coption%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chr%3E"],"22b9fe36797d70a3b71a6aadc6ad7cff23c3fc90":[async_test('html5lib_webkit02.html 22b9fe36797d70a3b71a6aadc6ad7cff23c3fc90'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Coptgroup%3E%3Coption%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chr%3E"],"a82c3bf49c381b5f58c5c8a4bbbe0cef2458e28a":[async_test('html5lib_webkit02.html a82c3bf49c381b5f58c5c8a4bbbe0cef2458e28a'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Coptgroup%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chr%3E"],"61f8d527795dc8044a95a3e2437de81e16597ceb":[async_test('html5lib_webkit02.html 61f8d527795dc8044a95a3e2437de81e16597ceb'), "%3Ctable%3E%3Ctr%3E%3Ctd%3E%3Cselect%3E%3Coption%3E%3Coptgroup%3E%3Chr%3E", "%23document%0A%7C%20%3Chtml%3E%0A%7C%20%20%20%3Chead%3E%0A%7C%20%20%20%3Cbody%3E%0A%7C%20%20%20%20%20%3Ctable%3E%0A%7C%20%20%20%20%20%20%20%3Ctbody%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cselect%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coptgroup%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chr%3E"],
+ }
+ init_tests(get_type());
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/html_content_in_foreign_context.html b/testing/web-platform/tests/html/syntax/parsing/html_content_in_foreign_context.html
new file mode 100644
index 0000000000..7efafb4fce
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/html_content_in_foreign_context.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Foreign contexts with HTML tag children</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inforeign">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+test(function() {
+ const contexts = ["svg", "math"];
+ const elements = ["/p", "/br", "b", "big", "blockquote", "br", "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "li", "listing", "menu", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small", "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul", "var"];
+ contexts.forEach(c => {
+ elements.forEach(e => {
+ const wrapper = document.createElement('div');
+ const html = `<${c}><${e}></${c}`
+ wrapper.innerHTML = html;
+ assert_not_equals(wrapper.innerHTML, html, "The inner HTML should get mutated");
+
+ const tagname = e[0]=='/' ? e.substr(1) : e;
+ const element = wrapper.getElementsByTagName(tagname)[0];
+ assert_not_equals(element, undefined,`Unable to locate the ${e} node in ${c}`)
+ const parent = element.parentNode
+ assert_equals(element.parentNode, wrapper,`The ${e} tag did not exit the ${c}`)
+ });
+ });
+}, "HTML namespace nodes should exit foreign contexts");
+
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/inhead-noscript-head.html b/testing/web-platform/tests/html/syntax/parsing/inhead-noscript-head.html
new file mode 100644
index 0000000000..bcf42d4793
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/inhead-noscript-head.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that when the scripting flag is disabled, a head start tag in "in head noscript" mode is ignored</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<script>
+promise_test(async function(t) {
+ let iframe = document.createElement("iframe");
+ iframe.srcdoc = "<!DOCTYPE html><head><noscript><head><style></style>";
+ iframe.sandbox = "allow-same-origin";
+ let loaded = new Promise(resolve => iframe.onload = resolve);
+ document.body.append(iframe);
+ await loaded;
+ assert_equals(String(iframe.contentDocument.querySelector("noscript").firstChild), "[object HTMLStyleElement]");
+}, "When the scripting flag is disabled, a head start tag in \"in head noscript\" mode should be ignored");
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/math-parse01.html b/testing/web-platform/tests/html/syntax/parsing/math-parse01.html
new file mode 100644
index 0000000000..3aff716d9f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/math-parse01.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>math in html: parsing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<h1>math in html: parsing</h1>
+
+<div id="log" style="display:block"></div>
+
+<div style="display:none">
+<div><math id="m1"><mtext/></math></div>
+<div id="d1"><math><mrow/><mi/></math></div>
+<div id="d2"><math><mrow><mrow><mn>1</mn></mrow><mi>a</mi></mrow></math></div>
+<div id="d3">&lang;&rang;</div>
+<div id="d4">&Kopf;</div>
+<div id="d5"><math><semantics><mi>a</mi><annotation-xml><foo/><bar/></annotation-xml></semantics></math></div>
+<div id="d6"><math><semantics><mi>a</mi><annotation-xml encoding="text/html"><div></div></annotation-xml></semantics><mn/></math>
+</div>
+
+
+<script>
+
+test(function() {
+assert_equals(document.getElementById("m1"),document.getElementsByTagName("math")[0]);
+},"The id attribute should be recognised on math elements");
+
+test(function() {
+assert_equals(document.getElementById("d1").firstChild.nodeName,"math")
+},"The node name should be math");
+
+test(function() {
+assert_equals(document.getElementById("d1").firstChild.namespaceURI ,"http://www.w3.org/1998/Math/MathML")
+},"math should be in MathML Namespace");
+
+test(function() {
+assert_equals(document.getElementById("d1").firstChild.childNodes.length ,2)
+},"Math has 2 children (empty tag syntax)");
+
+test(function() {
+assert_equals(document.getElementById("d2").firstChild.childNodes.length ,1)
+},"Nested mrow elements should be parsed correctly");
+
+test(function() {
+assert_equals(document.getElementById("d3").firstChild.nodeValue ,"\u27E8\u27E9")
+},"Testing rang and lang entity code points");
+
+test(function() {
+assert_equals(document.getElementById("d4").firstChild.nodeValue ,"\uD835\uDD42")
+},"Testing Kopf (Plane 1) entity code point");
+
+test(function() {
+assert_equals(document.getElementById("d5").firstChild.firstChild.childNodes[1].childNodes.length ,2)
+},"Empty element tags in annotation-xml parsed as per XML.");
+
+test(function() {
+assert_equals(document.getElementById("d6").firstChild.childNodes.length ,2)
+},"html tags allowed in annotation-xml/@encoding='text/html'.");
+
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/math-parse03.html b/testing/web-platform/tests/html/syntax/parsing/math-parse03.html
new file mode 100644
index 0000000000..a3dbdc4e61
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/math-parse03.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>math in html: parsing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<h1>math in html: parsing</h1>
+
+<div id="log"></div>
+
+<div>
+<div><MATH id="m1"><Mtext/></math></div>
+<div id="d1"><math><MI MATHVARIANT="BOLD" /></math></div>
+<div id="d2"><math><semantics DEFINITIONurl="www.example.org/FOO"><mi>a</mi><annotation-xml><foo/><bar/></annotation-xml></semantics></math></div>
+<div><math id="m3span-mtext"><mtext><Span>x</Span></mtext></math></div>
+<div><math id="m3span-mi"><mi><Span>x</Span></mi></math></div>
+<div><math id="m3span-mrow"><mi><Span>x</Span></mrow></math></div>
+<div><math id="m3p-mtext"><mtext><P>x</P></mtext></math></div>
+<div><math id="m3p-mi"><mi><P>x</P></mi></math></div>
+<div id="d3p-mrow"><math><mrow><P>x</P><mi>y</mi></mrow></math></div>
+<div><math id="m4"><mtext><Undefinedelement>x</Undefinedelement></mtext></math></div>
+<div><math id="m5"><mtext><mi>x</mi></mtext></math></div>
+<div><math><semantics><mi>x</mi>
+ <annotation-xml><p id="p6default">x</p></annotation-xml>
+ </semantics></math></div>
+<div><math><semantics><mi>x</mi>
+ <annotation-xml encoding=text/html><p id="p6texthtml">x</p></annotation-xml>
+ </semantics></math></div>
+<div><math><semantics><mi>x</mi>
+ <annotation-xml encoding=TEXT/HTML><p id="p6uctexthtml">x</p></annotation-xml>
+ </semantics></math></div>
+<div><math><semantics><mi>x</mi>
+ <annotation-xml encoding=application/xhtml+xml><p id="p6applicationxhtmlxml">x</p></annotation-xml>
+ </semantics></math></div>
+<div><math><semantics><mi>x</mi>
+ <annotation-xml encoding=foo><p id="p6foo">x</p></annotation-xml>
+ </semantics></math></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById("m1"),document.getElementsByTagName("math")[0]);
+},"MATH element name should be lowercased");
+
+test(function() {
+assert_equals(document.getElementById("d1").firstChild.firstChild.nodeName,"mi");
+assert_equals(document.getElementById("d1").firstChild.firstChild.namespaceURI, "http://www.w3.org/1998/Math/MathML");
+assert_true(document.getElementById("d1").firstChild.firstChild.hasAttribute("mathvariant"));
+assert_equals(document.getElementById("d1").firstChild.firstChild.getAttribute("mathvariant"),"BOLD")
+},"MI element name and mathvariant attribute name should be lowercased, attribute value unchanged");
+
+test(function() {
+assert_true(document.getElementById("d2").firstChild.firstChild.hasAttribute("definitionURL"));
+assert_equals(document.getElementById("d2").firstChild.firstChild.getAttribute("definitionURL"),"www.example.org/FOO")
+},"DEFINITIONurl attribute markup should produce a definitionURL attribute, attribute value unchanged");
+
+test(function() {
+assert_equals(document.getElementById("m3span-mtext").firstChild.firstChild.nodeName,"SPAN");
+assert_equals(document.getElementById("m3span-mtext").firstChild.firstChild.namespaceURI,"http://www.w3.org/1999/xhtml")
+},"html Span in mtext produces SPAN nodename in XHTML namespace");
+
+test(function() {
+assert_equals(document.getElementById("m3span-mi").firstChild.firstChild.nodeName,"SPAN");
+assert_equals(document.getElementById("m3span-mi").firstChild.firstChild.namespaceURI,"http://www.w3.org/1999/xhtml")
+},"html Span in mi produces SPAN nodename in XHTML namespace");
+
+test(function() {
+assert_equals(document.getElementById("m3span-mrow").firstChild.firstChild.nodeName,"SPAN");
+assert_equals(document.getElementById("m3span-mrow").firstChild.firstChild.namespaceURI,"http://www.w3.org/1999/xhtml")
+},"html Span in mrow produces SPAN nodename in XHTML namespace");
+
+test(function() {
+assert_equals(document.getElementById("m3p-mtext").firstChild.firstChild.nodeName,"P");
+assert_equals(document.getElementById("m3p-mtext").firstChild.firstChild.namespaceURI,"http://www.w3.org/1999/xhtml")
+},"html P in mtext produces P nodename in XHTML namespace");
+
+test(function() {
+assert_equals(document.getElementById("m3p-mi").firstChild.firstChild.nodeName,"P");
+assert_equals(document.getElementById("m3p-mi").firstChild.firstChild.namespaceURI,"http://www.w3.org/1999/xhtml")
+},"html P in mi produces P nodename in XHTML namespace");
+
+test(function() {
+assert_equals(document.getElementById("d3p-mrow").childNodes.length ,3)
+},"html P in mrow terminates the math: mrow,P,MI children of div");
+
+test(function() {
+assert_equals(document.getElementById("d3p-mrow").firstChild.childNodes.length ,1)
+},"html P in mrow terminates the math: mrow child of math");
+
+test(function() {
+assert_equals(document.getElementById("d3p-mrow").firstChild.firstChild.childNodes.length ,0)
+},"html P in mrow terminates the math: mrow empty");
+
+test(function() {
+assert_equals(document.getElementById("d3p-mrow").childNodes[0].nodeName,"math");
+assert_equals(document.getElementById("d3p-mrow").childNodes[1].nodeName,"P");
+assert_equals(document.getElementById("d3p-mrow").childNodes[2].nodeName,"MI");
+},"html P in mrow terminates the math: math,P,MI children of div");
+
+test(function() {
+assert_equals(document.getElementById("m4").firstChild.firstChild.nodeName,"UNDEFINEDELEMENT");
+assert_equals(document.getElementById("m4").firstChild.firstChild.namespaceURI,"http://www.w3.org/1999/xhtml")
+},"Undefinedelement in mtext produces UNDEFINEDELEMENT nodename in XHTML namespace");
+
+test(function() {
+assert_equals(document.getElementById("m5").firstChild.firstChild.nodeName,"MI");
+assert_equals(document.getElementById("m5").firstChild.firstChild.namespaceURI,"http://www.w3.org/1999/xhtml")
+},"mi in mtext produces MI nodename in XHTML namespace");
+
+test(function() {
+assert_equals(document.getElementById("p6default").parentNode.nodeName,"DIV")
+},"p in annotation-xml moves to be child of DIV");
+
+test(function() {
+assert_equals(document.getElementById("p6texthtml").parentNode.nodeName,"annotation-xml")
+},"p in annotation-xml encoding=text/html stays as child of annotation-xml");
+
+test(function() {
+assert_equals(document.getElementById("p6uctexthtml").parentNode.nodeName,"annotation-xml")
+},"p in annotation-xml encoding=TEXT/HTML stays as child of annotation-xml");
+
+test(function() {
+assert_equals(document.getElementById("p6applicationxhtmlxml").parentNode.nodeName,"annotation-xml")
+},"p in annotation-xml encoding=application/xhtml+xml stays as child of annotation-xml");
+
+test(function() {
+assert_equals(document.getElementById("p6foo").parentNode.nodeName,"DIV")
+},"p in annotation-xml encoding=foo moves to be child of DIV");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/meta-inhead-insertion-mode.html b/testing/web-platform/tests/html/syntax/parsing/meta-inhead-insertion-mode.html
new file mode 100644
index 0000000000..4317e4eb14
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/meta-inhead-insertion-mode.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Encoding specified in the "charset" attribute should have precedence over "content" attribute.</title>
+<meta http-equiv="Content-Type" content="text/html; charset=koi8-r" charset="iso-8859-15">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(function () {
+ assert_equals(document.characterSet, "ISO-8859-15");
+}, "Encoding specified in the 'charset' attribute should have precedence over 'content' attribute.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/named-character-references-data.js b/testing/web-platform/tests/html/syntax/parsing/named-character-references-data.js
new file mode 100644
index 0000000000..48ab30e473
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/named-character-references-data.js
@@ -0,0 +1,2233 @@
+window.data = {
+ '&AElig': { 'codepoints': [0x000C6], 'characters': '\xC6' },
+ '&AElig;': { 'codepoints': [0x000C6], 'characters': '\xC6' },
+ '&AMP': { 'codepoints': [0x00026], 'characters': '\x26' },
+ '&AMP;': { 'codepoints': [0x00026], 'characters': '\x26' },
+ '&Aacute': { 'codepoints': [0x000C1], 'characters': '\xC1' },
+ '&Aacute;': { 'codepoints': [0x000C1], 'characters': '\xC1' },
+ '&Abreve;': { 'codepoints': [0x00102], 'characters': '\u0102' },
+ '&Acirc': { 'codepoints': [0x000C2], 'characters': '\xC2' },
+ '&Acirc;': { 'codepoints': [0x000C2], 'characters': '\xC2' },
+ '&Acy;': { 'codepoints': [0x00410], 'characters': '\u0410' },
+ '&Afr;': { 'codepoints': [0x1D504], 'characters': '\uD835\uDD04' },
+ '&Agrave': { 'codepoints': [0x000C0], 'characters': '\xC0' },
+ '&Agrave;': { 'codepoints': [0x000C0], 'characters': '\xC0' },
+ '&Alpha;': { 'codepoints': [0x00391], 'characters': '\u0391' },
+ '&Amacr;': { 'codepoints': [0x00100], 'characters': '\u0100' },
+ '&And;': { 'codepoints': [0x02A53], 'characters': '\u2A53' },
+ '&Aogon;': { 'codepoints': [0x00104], 'characters': '\u0104' },
+ '&Aopf;': { 'codepoints': [0x1D538], 'characters': '\uD835\uDD38' },
+ '&ApplyFunction;': { 'codepoints': [0x02061], 'characters': '\u2061' },
+ '&Aring': { 'codepoints': [0x000C5], 'characters': '\xC5' },
+ '&Aring;': { 'codepoints': [0x000C5], 'characters': '\xC5' },
+ '&Ascr;': { 'codepoints': [0x1D49C], 'characters': '\uD835\uDC9C' },
+ '&Assign;': { 'codepoints': [0x02254], 'characters': '\u2254' },
+ '&Atilde': { 'codepoints': [0x000C3], 'characters': '\xC3' },
+ '&Atilde;': { 'codepoints': [0x000C3], 'characters': '\xC3' },
+ '&Auml': { 'codepoints': [0x000C4], 'characters': '\xC4' },
+ '&Auml;': { 'codepoints': [0x000C4], 'characters': '\xC4' },
+ '&Backslash;': { 'codepoints': [0x02216], 'characters': '\u2216' },
+ '&Barv;': { 'codepoints': [0x02AE7], 'characters': '\u2AE7' },
+ '&Barwed;': { 'codepoints': [0x02306], 'characters': '\u2306' },
+ '&Bcy;': { 'codepoints': [0x00411], 'characters': '\u0411' },
+ '&Because;': { 'codepoints': [0x02235], 'characters': '\u2235' },
+ '&Bernoullis;': { 'codepoints': [0x0212C], 'characters': '\u212C' },
+ '&Beta;': { 'codepoints': [0x00392], 'characters': '\u0392' },
+ '&Bfr;': { 'codepoints': [0x1D505], 'characters': '\uD835\uDD05' },
+ '&Bopf;': { 'codepoints': [0x1D539], 'characters': '\uD835\uDD39' },
+ '&Breve;': { 'codepoints': [0x002D8], 'characters': '\u02D8' },
+ '&Bscr;': { 'codepoints': [0x0212C], 'characters': '\u212C' },
+ '&Bumpeq;': { 'codepoints': [0x0224E], 'characters': '\u224E' },
+ '&CHcy;': { 'codepoints': [0x00427], 'characters': '\u0427' },
+ '&COPY': { 'codepoints': [0x000A9], 'characters': '\xA9' },
+ '&COPY;': { 'codepoints': [0x000A9], 'characters': '\xA9' },
+ '&Cacute;': { 'codepoints': [0x00106], 'characters': '\u0106' },
+ '&Cap;': { 'codepoints': [0x022D2], 'characters': '\u22D2' },
+ '&CapitalDifferentialD;': { 'codepoints': [0x02145], 'characters': '\u2145' },
+ '&Cayleys;': { 'codepoints': [0x0212D], 'characters': '\u212D' },
+ '&Ccaron;': { 'codepoints': [0x0010C], 'characters': '\u010C' },
+ '&Ccedil': { 'codepoints': [0x000C7], 'characters': '\xC7' },
+ '&Ccedil;': { 'codepoints': [0x000C7], 'characters': '\xC7' },
+ '&Ccirc;': { 'codepoints': [0x00108], 'characters': '\u0108' },
+ '&Cconint;': { 'codepoints': [0x02230], 'characters': '\u2230' },
+ '&Cdot;': { 'codepoints': [0x0010A], 'characters': '\u010A' },
+ '&Cedilla;': { 'codepoints': [0x000B8], 'characters': '\xB8' },
+ '&CenterDot;': { 'codepoints': [0x000B7], 'characters': '\xB7' },
+ '&Cfr;': { 'codepoints': [0x0212D], 'characters': '\u212D' },
+ '&Chi;': { 'codepoints': [0x003A7], 'characters': '\u03A7' },
+ '&CircleDot;': { 'codepoints': [0x02299], 'characters': '\u2299' },
+ '&CircleMinus;': { 'codepoints': [0x02296], 'characters': '\u2296' },
+ '&CirclePlus;': { 'codepoints': [0x02295], 'characters': '\u2295' },
+ '&CircleTimes;': { 'codepoints': [0x02297], 'characters': '\u2297' },
+ '&ClockwiseContourIntegral;': { 'codepoints': [0x02232], 'characters': '\u2232' },
+ '&CloseCurlyDoubleQuote;': { 'codepoints': [0x0201D], 'characters': '\u201D' },
+ '&CloseCurlyQuote;': { 'codepoints': [0x02019], 'characters': '\u2019' },
+ '&Colon;': { 'codepoints': [0x02237], 'characters': '\u2237' },
+ '&Colone;': { 'codepoints': [0x02A74], 'characters': '\u2A74' },
+ '&Congruent;': { 'codepoints': [0x02261], 'characters': '\u2261' },
+ '&Conint;': { 'codepoints': [0x0222F], 'characters': '\u222F' },
+ '&ContourIntegral;': { 'codepoints': [0x0222E], 'characters': '\u222E' },
+ '&Copf;': { 'codepoints': [0x02102], 'characters': '\u2102' },
+ '&Coproduct;': { 'codepoints': [0x02210], 'characters': '\u2210' },
+ '&CounterClockwiseContourIntegral;': { 'codepoints': [0x02233], 'characters': '\u2233' },
+ '&Cross;': { 'codepoints': [0x02A2F], 'characters': '\u2A2F' },
+ '&Cscr;': { 'codepoints': [0x1D49E], 'characters': '\uD835\uDC9E' },
+ '&Cup;': { 'codepoints': [0x022D3], 'characters': '\u22D3' },
+ '&CupCap;': { 'codepoints': [0x0224D], 'characters': '\u224D' },
+ '&DD;': { 'codepoints': [0x02145], 'characters': '\u2145' },
+ '&DDotrahd;': { 'codepoints': [0x02911], 'characters': '\u2911' },
+ '&DJcy;': { 'codepoints': [0x00402], 'characters': '\u0402' },
+ '&DScy;': { 'codepoints': [0x00405], 'characters': '\u0405' },
+ '&DZcy;': { 'codepoints': [0x0040F], 'characters': '\u040F' },
+ '&Dagger;': { 'codepoints': [0x02021], 'characters': '\u2021' },
+ '&Darr;': { 'codepoints': [0x021A1], 'characters': '\u21A1' },
+ '&Dashv;': { 'codepoints': [0x02AE4], 'characters': '\u2AE4' },
+ '&Dcaron;': { 'codepoints': [0x0010E], 'characters': '\u010E' },
+ '&Dcy;': { 'codepoints': [0x00414], 'characters': '\u0414' },
+ '&Del;': { 'codepoints': [0x02207], 'characters': '\u2207' },
+ '&Delta;': { 'codepoints': [0x00394], 'characters': '\u0394' },
+ '&Dfr;': { 'codepoints': [0x1D507], 'characters': '\uD835\uDD07' },
+ '&DiacriticalAcute;': { 'codepoints': [0x000B4], 'characters': '\xB4' },
+ '&DiacriticalDot;': { 'codepoints': [0x002D9], 'characters': '\u02D9' },
+ '&DiacriticalDoubleAcute;': { 'codepoints': [0x002DD], 'characters': '\u02DD' },
+ '&DiacriticalGrave;': { 'codepoints': [0x00060], 'characters': '\x60' },
+ '&DiacriticalTilde;': { 'codepoints': [0x002DC], 'characters': '\u02DC' },
+ '&Diamond;': { 'codepoints': [0x022C4], 'characters': '\u22C4' },
+ '&DifferentialD;': { 'codepoints': [0x02146], 'characters': '\u2146' },
+ '&Dopf;': { 'codepoints': [0x1D53B], 'characters': '\uD835\uDD3B' },
+ '&Dot;': { 'codepoints': [0x000A8], 'characters': '\xA8' },
+ '&DotDot;': { 'codepoints': [0x020DC], 'characters': '\u20DC' },
+ '&DotEqual;': { 'codepoints': [0x02250], 'characters': '\u2250' },
+ '&DoubleContourIntegral;': { 'codepoints': [0x0222F], 'characters': '\u222F' },
+ '&DoubleDot;': { 'codepoints': [0x000A8], 'characters': '\xA8' },
+ '&DoubleDownArrow;': { 'codepoints': [0x021D3], 'characters': '\u21D3' },
+ '&DoubleLeftArrow;': { 'codepoints': [0x021D0], 'characters': '\u21D0' },
+ '&DoubleLeftRightArrow;': { 'codepoints': [0x021D4], 'characters': '\u21D4' },
+ '&DoubleLeftTee;': { 'codepoints': [0x02AE4], 'characters': '\u2AE4' },
+ '&DoubleLongLeftArrow;': { 'codepoints': [0x027F8], 'characters': '\u27F8' },
+ '&DoubleLongLeftRightArrow;': { 'codepoints': [0x027FA], 'characters': '\u27FA' },
+ '&DoubleLongRightArrow;': { 'codepoints': [0x027F9], 'characters': '\u27F9' },
+ '&DoubleRightArrow;': { 'codepoints': [0x021D2], 'characters': '\u21D2' },
+ '&DoubleRightTee;': { 'codepoints': [0x022A8], 'characters': '\u22A8' },
+ '&DoubleUpArrow;': { 'codepoints': [0x021D1], 'characters': '\u21D1' },
+ '&DoubleUpDownArrow;': { 'codepoints': [0x021D5], 'characters': '\u21D5' },
+ '&DoubleVerticalBar;': { 'codepoints': [0x02225], 'characters': '\u2225' },
+ '&DownArrow;': { 'codepoints': [0x02193], 'characters': '\u2193' },
+ '&DownArrowBar;': { 'codepoints': [0x02913], 'characters': '\u2913' },
+ '&DownArrowUpArrow;': { 'codepoints': [0x021F5], 'characters': '\u21F5' },
+ '&DownBreve;': { 'codepoints': [0x00311], 'characters': '\u0311' },
+ '&DownLeftRightVector;': { 'codepoints': [0x02950], 'characters': '\u2950' },
+ '&DownLeftTeeVector;': { 'codepoints': [0x0295E], 'characters': '\u295E' },
+ '&DownLeftVector;': { 'codepoints': [0x021BD], 'characters': '\u21BD' },
+ '&DownLeftVectorBar;': { 'codepoints': [0x02956], 'characters': '\u2956' },
+ '&DownRightTeeVector;': { 'codepoints': [0x0295F], 'characters': '\u295F' },
+ '&DownRightVector;': { 'codepoints': [0x021C1], 'characters': '\u21C1' },
+ '&DownRightVectorBar;': { 'codepoints': [0x02957], 'characters': '\u2957' },
+ '&DownTee;': { 'codepoints': [0x022A4], 'characters': '\u22A4' },
+ '&DownTeeArrow;': { 'codepoints': [0x021A7], 'characters': '\u21A7' },
+ '&Downarrow;': { 'codepoints': [0x021D3], 'characters': '\u21D3' },
+ '&Dscr;': { 'codepoints': [0x1D49F], 'characters': '\uD835\uDC9F' },
+ '&Dstrok;': { 'codepoints': [0x00110], 'characters': '\u0110' },
+ '&ENG;': { 'codepoints': [0x0014A], 'characters': '\u014A' },
+ '&ETH': { 'codepoints': [0x000D0], 'characters': '\xD0' },
+ '&ETH;': { 'codepoints': [0x000D0], 'characters': '\xD0' },
+ '&Eacute': { 'codepoints': [0x000C9], 'characters': '\xC9' },
+ '&Eacute;': { 'codepoints': [0x000C9], 'characters': '\xC9' },
+ '&Ecaron;': { 'codepoints': [0x0011A], 'characters': '\u011A' },
+ '&Ecirc': { 'codepoints': [0x000CA], 'characters': '\xCA' },
+ '&Ecirc;': { 'codepoints': [0x000CA], 'characters': '\xCA' },
+ '&Ecy;': { 'codepoints': [0x0042D], 'characters': '\u042D' },
+ '&Edot;': { 'codepoints': [0x00116], 'characters': '\u0116' },
+ '&Efr;': { 'codepoints': [0x1D508], 'characters': '\uD835\uDD08' },
+ '&Egrave': { 'codepoints': [0x000C8], 'characters': '\xC8' },
+ '&Egrave;': { 'codepoints': [0x000C8], 'characters': '\xC8' },
+ '&Element;': { 'codepoints': [0x02208], 'characters': '\u2208' },
+ '&Emacr;': { 'codepoints': [0x00112], 'characters': '\u0112' },
+ '&EmptySmallSquare;': { 'codepoints': [0x025FB], 'characters': '\u25FB' },
+ '&EmptyVerySmallSquare;': { 'codepoints': [0x025AB], 'characters': '\u25AB' },
+ '&Eogon;': { 'codepoints': [0x00118], 'characters': '\u0118' },
+ '&Eopf;': { 'codepoints': [0x1D53C], 'characters': '\uD835\uDD3C' },
+ '&Epsilon;': { 'codepoints': [0x00395], 'characters': '\u0395' },
+ '&Equal;': { 'codepoints': [0x02A75], 'characters': '\u2A75' },
+ '&EqualTilde;': { 'codepoints': [0x02242], 'characters': '\u2242' },
+ '&Equilibrium;': { 'codepoints': [0x021CC], 'characters': '\u21CC' },
+ '&Escr;': { 'codepoints': [0x02130], 'characters': '\u2130' },
+ '&Esim;': { 'codepoints': [0x02A73], 'characters': '\u2A73' },
+ '&Eta;': { 'codepoints': [0x00397], 'characters': '\u0397' },
+ '&Euml': { 'codepoints': [0x000CB], 'characters': '\xCB' },
+ '&Euml;': { 'codepoints': [0x000CB], 'characters': '\xCB' },
+ '&Exists;': { 'codepoints': [0x02203], 'characters': '\u2203' },
+ '&ExponentialE;': { 'codepoints': [0x02147], 'characters': '\u2147' },
+ '&Fcy;': { 'codepoints': [0x00424], 'characters': '\u0424' },
+ '&Ffr;': { 'codepoints': [0x1D509], 'characters': '\uD835\uDD09' },
+ '&FilledSmallSquare;': { 'codepoints': [0x025FC], 'characters': '\u25FC' },
+ '&FilledVerySmallSquare;': { 'codepoints': [0x025AA], 'characters': '\u25AA' },
+ '&Fopf;': { 'codepoints': [0x1D53D], 'characters': '\uD835\uDD3D' },
+ '&ForAll;': { 'codepoints': [0x02200], 'characters': '\u2200' },
+ '&Fouriertrf;': { 'codepoints': [0x02131], 'characters': '\u2131' },
+ '&Fscr;': { 'codepoints': [0x02131], 'characters': '\u2131' },
+ '&GJcy;': { 'codepoints': [0x00403], 'characters': '\u0403' },
+ '&GT': { 'codepoints': [0x0003E], 'characters': '\x3E' },
+ '&GT;': { 'codepoints': [0x0003E], 'characters': '\x3E' },
+ '&Gamma;': { 'codepoints': [0x00393], 'characters': '\u0393' },
+ '&Gammad;': { 'codepoints': [0x003DC], 'characters': '\u03DC' },
+ '&Gbreve;': { 'codepoints': [0x0011E], 'characters': '\u011E' },
+ '&Gcedil;': { 'codepoints': [0x00122], 'characters': '\u0122' },
+ '&Gcirc;': { 'codepoints': [0x0011C], 'characters': '\u011C' },
+ '&Gcy;': { 'codepoints': [0x00413], 'characters': '\u0413' },
+ '&Gdot;': { 'codepoints': [0x00120], 'characters': '\u0120' },
+ '&Gfr;': { 'codepoints': [0x1D50A], 'characters': '\uD835\uDD0A' },
+ '&Gg;': { 'codepoints': [0x022D9], 'characters': '\u22D9' },
+ '&Gopf;': { 'codepoints': [0x1D53E], 'characters': '\uD835\uDD3E' },
+ '&GreaterEqual;': { 'codepoints': [0x02265], 'characters': '\u2265' },
+ '&GreaterEqualLess;': { 'codepoints': [0x022DB], 'characters': '\u22DB' },
+ '&GreaterFullEqual;': { 'codepoints': [0x02267], 'characters': '\u2267' },
+ '&GreaterGreater;': { 'codepoints': [0x02AA2], 'characters': '\u2AA2' },
+ '&GreaterLess;': { 'codepoints': [0x02277], 'characters': '\u2277' },
+ '&GreaterSlantEqual;': { 'codepoints': [0x02A7E], 'characters': '\u2A7E' },
+ '&GreaterTilde;': { 'codepoints': [0x02273], 'characters': '\u2273' },
+ '&Gscr;': { 'codepoints': [0x1D4A2], 'characters': '\uD835\uDCA2' },
+ '&Gt;': { 'codepoints': [0x0226B], 'characters': '\u226B' },
+ '&HARDcy;': { 'codepoints': [0x0042A], 'characters': '\u042A' },
+ '&Hacek;': { 'codepoints': [0x002C7], 'characters': '\u02C7' },
+ '&Hat;': { 'codepoints': [0x0005E], 'characters': '\x5E' },
+ '&Hcirc;': { 'codepoints': [0x00124], 'characters': '\u0124' },
+ '&Hfr;': { 'codepoints': [0x0210C], 'characters': '\u210C' },
+ '&HilbertSpace;': { 'codepoints': [0x0210B], 'characters': '\u210B' },
+ '&Hopf;': { 'codepoints': [0x0210D], 'characters': '\u210D' },
+ '&HorizontalLine;': { 'codepoints': [0x02500], 'characters': '\u2500' },
+ '&Hscr;': { 'codepoints': [0x0210B], 'characters': '\u210B' },
+ '&Hstrok;': { 'codepoints': [0x00126], 'characters': '\u0126' },
+ '&HumpDownHump;': { 'codepoints': [0x0224E], 'characters': '\u224E' },
+ '&HumpEqual;': { 'codepoints': [0x0224F], 'characters': '\u224F' },
+ '&IEcy;': { 'codepoints': [0x00415], 'characters': '\u0415' },
+ '&IJlig;': { 'codepoints': [0x00132], 'characters': '\u0132' },
+ '&IOcy;': { 'codepoints': [0x00401], 'characters': '\u0401' },
+ '&Iacute': { 'codepoints': [0x000CD], 'characters': '\xCD' },
+ '&Iacute;': { 'codepoints': [0x000CD], 'characters': '\xCD' },
+ '&Icirc': { 'codepoints': [0x000CE], 'characters': '\xCE' },
+ '&Icirc;': { 'codepoints': [0x000CE], 'characters': '\xCE' },
+ '&Icy;': { 'codepoints': [0x00418], 'characters': '\u0418' },
+ '&Idot;': { 'codepoints': [0x00130], 'characters': '\u0130' },
+ '&Ifr;': { 'codepoints': [0x02111], 'characters': '\u2111' },
+ '&Igrave': { 'codepoints': [0x000CC], 'characters': '\xCC' },
+ '&Igrave;': { 'codepoints': [0x000CC], 'characters': '\xCC' },
+ '&Im;': { 'codepoints': [0x02111], 'characters': '\u2111' },
+ '&Imacr;': { 'codepoints': [0x0012A], 'characters': '\u012A' },
+ '&ImaginaryI;': { 'codepoints': [0x02148], 'characters': '\u2148' },
+ '&Implies;': { 'codepoints': [0x021D2], 'characters': '\u21D2' },
+ '&Int;': { 'codepoints': [0x0222C], 'characters': '\u222C' },
+ '&Integral;': { 'codepoints': [0x0222B], 'characters': '\u222B' },
+ '&Intersection;': { 'codepoints': [0x022C2], 'characters': '\u22C2' },
+ '&InvisibleComma;': { 'codepoints': [0x02063], 'characters': '\u2063' },
+ '&InvisibleTimes;': { 'codepoints': [0x02062], 'characters': '\u2062' },
+ '&Iogon;': { 'codepoints': [0x0012E], 'characters': '\u012E' },
+ '&Iopf;': { 'codepoints': [0x1D540], 'characters': '\uD835\uDD40' },
+ '&Iota;': { 'codepoints': [0x00399], 'characters': '\u0399' },
+ '&Iscr;': { 'codepoints': [0x02110], 'characters': '\u2110' },
+ '&Itilde;': { 'codepoints': [0x00128], 'characters': '\u0128' },
+ '&Iukcy;': { 'codepoints': [0x00406], 'characters': '\u0406' },
+ '&Iuml': { 'codepoints': [0x000CF], 'characters': '\xCF' },
+ '&Iuml;': { 'codepoints': [0x000CF], 'characters': '\xCF' },
+ '&Jcirc;': { 'codepoints': [0x00134], 'characters': '\u0134' },
+ '&Jcy;': { 'codepoints': [0x00419], 'characters': '\u0419' },
+ '&Jfr;': { 'codepoints': [0x1D50D], 'characters': '\uD835\uDD0D' },
+ '&Jopf;': { 'codepoints': [0x1D541], 'characters': '\uD835\uDD41' },
+ '&Jscr;': { 'codepoints': [0x1D4A5], 'characters': '\uD835\uDCA5' },
+ '&Jsercy;': { 'codepoints': [0x00408], 'characters': '\u0408' },
+ '&Jukcy;': { 'codepoints': [0x00404], 'characters': '\u0404' },
+ '&KHcy;': { 'codepoints': [0x00425], 'characters': '\u0425' },
+ '&KJcy;': { 'codepoints': [0x0040C], 'characters': '\u040C' },
+ '&Kappa;': { 'codepoints': [0x0039A], 'characters': '\u039A' },
+ '&Kcedil;': { 'codepoints': [0x00136], 'characters': '\u0136' },
+ '&Kcy;': { 'codepoints': [0x0041A], 'characters': '\u041A' },
+ '&Kfr;': { 'codepoints': [0x1D50E], 'characters': '\uD835\uDD0E' },
+ '&Kopf;': { 'codepoints': [0x1D542], 'characters': '\uD835\uDD42' },
+ '&Kscr;': { 'codepoints': [0x1D4A6], 'characters': '\uD835\uDCA6' },
+ '&LJcy;': { 'codepoints': [0x00409], 'characters': '\u0409' },
+ '&LT': { 'codepoints': [0x0003C], 'characters': '\x3C' },
+ '&LT;': { 'codepoints': [0x0003C], 'characters': '\x3C' },
+ '&Lacute;': { 'codepoints': [0x00139], 'characters': '\u0139' },
+ '&Lambda;': { 'codepoints': [0x0039B], 'characters': '\u039B' },
+ '&Lang;': { 'codepoints': [0x027EA], 'characters': '\u27EA' },
+ '&Laplacetrf;': { 'codepoints': [0x02112], 'characters': '\u2112' },
+ '&Larr;': { 'codepoints': [0x0219E], 'characters': '\u219E' },
+ '&Lcaron;': { 'codepoints': [0x0013D], 'characters': '\u013D' },
+ '&Lcedil;': { 'codepoints': [0x0013B], 'characters': '\u013B' },
+ '&Lcy;': { 'codepoints': [0x0041B], 'characters': '\u041B' },
+ '&LeftAngleBracket;': { 'codepoints': [0x027E8], 'characters': '\u27E8' },
+ '&LeftArrow;': { 'codepoints': [0x02190], 'characters': '\u2190' },
+ '&LeftArrowBar;': { 'codepoints': [0x021E4], 'characters': '\u21E4' },
+ '&LeftArrowRightArrow;': { 'codepoints': [0x021C6], 'characters': '\u21C6' },
+ '&LeftCeiling;': { 'codepoints': [0x02308], 'characters': '\u2308' },
+ '&LeftDoubleBracket;': { 'codepoints': [0x027E6], 'characters': '\u27E6' },
+ '&LeftDownTeeVector;': { 'codepoints': [0x02961], 'characters': '\u2961' },
+ '&LeftDownVector;': { 'codepoints': [0x021C3], 'characters': '\u21C3' },
+ '&LeftDownVectorBar;': { 'codepoints': [0x02959], 'characters': '\u2959' },
+ '&LeftFloor;': { 'codepoints': [0x0230A], 'characters': '\u230A' },
+ '&LeftRightArrow;': { 'codepoints': [0x02194], 'characters': '\u2194' },
+ '&LeftRightVector;': { 'codepoints': [0x0294E], 'characters': '\u294E' },
+ '&LeftTee;': { 'codepoints': [0x022A3], 'characters': '\u22A3' },
+ '&LeftTeeArrow;': { 'codepoints': [0x021A4], 'characters': '\u21A4' },
+ '&LeftTeeVector;': { 'codepoints': [0x0295A], 'characters': '\u295A' },
+ '&LeftTriangle;': { 'codepoints': [0x022B2], 'characters': '\u22B2' },
+ '&LeftTriangleBar;': { 'codepoints': [0x029CF], 'characters': '\u29CF' },
+ '&LeftTriangleEqual;': { 'codepoints': [0x022B4], 'characters': '\u22B4' },
+ '&LeftUpDownVector;': { 'codepoints': [0x02951], 'characters': '\u2951' },
+ '&LeftUpTeeVector;': { 'codepoints': [0x02960], 'characters': '\u2960' },
+ '&LeftUpVector;': { 'codepoints': [0x021BF], 'characters': '\u21BF' },
+ '&LeftUpVectorBar;': { 'codepoints': [0x02958], 'characters': '\u2958' },
+ '&LeftVector;': { 'codepoints': [0x021BC], 'characters': '\u21BC' },
+ '&LeftVectorBar;': { 'codepoints': [0x02952], 'characters': '\u2952' },
+ '&Leftarrow;': { 'codepoints': [0x021D0], 'characters': '\u21D0' },
+ '&Leftrightarrow;': { 'codepoints': [0x021D4], 'characters': '\u21D4' },
+ '&LessEqualGreater;': { 'codepoints': [0x022DA], 'characters': '\u22DA' },
+ '&LessFullEqual;': { 'codepoints': [0x02266], 'characters': '\u2266' },
+ '&LessGreater;': { 'codepoints': [0x02276], 'characters': '\u2276' },
+ '&LessLess;': { 'codepoints': [0x02AA1], 'characters': '\u2AA1' },
+ '&LessSlantEqual;': { 'codepoints': [0x02A7D], 'characters': '\u2A7D' },
+ '&LessTilde;': { 'codepoints': [0x02272], 'characters': '\u2272' },
+ '&Lfr;': { 'codepoints': [0x1D50F], 'characters': '\uD835\uDD0F' },
+ '&Ll;': { 'codepoints': [0x022D8], 'characters': '\u22D8' },
+ '&Lleftarrow;': { 'codepoints': [0x021DA], 'characters': '\u21DA' },
+ '&Lmidot;': { 'codepoints': [0x0013F], 'characters': '\u013F' },
+ '&LongLeftArrow;': { 'codepoints': [0x027F5], 'characters': '\u27F5' },
+ '&LongLeftRightArrow;': { 'codepoints': [0x027F7], 'characters': '\u27F7' },
+ '&LongRightArrow;': { 'codepoints': [0x027F6], 'characters': '\u27F6' },
+ '&Longleftarrow;': { 'codepoints': [0x027F8], 'characters': '\u27F8' },
+ '&Longleftrightarrow;': { 'codepoints': [0x027FA], 'characters': '\u27FA' },
+ '&Longrightarrow;': { 'codepoints': [0x027F9], 'characters': '\u27F9' },
+ '&Lopf;': { 'codepoints': [0x1D543], 'characters': '\uD835\uDD43' },
+ '&LowerLeftArrow;': { 'codepoints': [0x02199], 'characters': '\u2199' },
+ '&LowerRightArrow;': { 'codepoints': [0x02198], 'characters': '\u2198' },
+ '&Lscr;': { 'codepoints': [0x02112], 'characters': '\u2112' },
+ '&Lsh;': { 'codepoints': [0x021B0], 'characters': '\u21B0' },
+ '&Lstrok;': { 'codepoints': [0x00141], 'characters': '\u0141' },
+ '&Lt;': { 'codepoints': [0x0226A], 'characters': '\u226A' },
+ '&Map;': { 'codepoints': [0x02905], 'characters': '\u2905' },
+ '&Mcy;': { 'codepoints': [0x0041C], 'characters': '\u041C' },
+ '&MediumSpace;': { 'codepoints': [0x0205F], 'characters': '\u205F' },
+ '&Mellintrf;': { 'codepoints': [0x02133], 'characters': '\u2133' },
+ '&Mfr;': { 'codepoints': [0x1D510], 'characters': '\uD835\uDD10' },
+ '&MinusPlus;': { 'codepoints': [0x02213], 'characters': '\u2213' },
+ '&Mopf;': { 'codepoints': [0x1D544], 'characters': '\uD835\uDD44' },
+ '&Mscr;': { 'codepoints': [0x02133], 'characters': '\u2133' },
+ '&Mu;': { 'codepoints': [0x0039C], 'characters': '\u039C' },
+ '&NJcy;': { 'codepoints': [0x0040A], 'characters': '\u040A' },
+ '&Nacute;': { 'codepoints': [0x00143], 'characters': '\u0143' },
+ '&Ncaron;': { 'codepoints': [0x00147], 'characters': '\u0147' },
+ '&Ncedil;': { 'codepoints': [0x00145], 'characters': '\u0145' },
+ '&Ncy;': { 'codepoints': [0x0041D], 'characters': '\u041D' },
+ '&NegativeMediumSpace;': { 'codepoints': [0x0200B], 'characters': '\u200B' },
+ '&NegativeThickSpace;': { 'codepoints': [0x0200B], 'characters': '\u200B' },
+ '&NegativeThinSpace;': { 'codepoints': [0x0200B], 'characters': '\u200B' },
+ '&NegativeVeryThinSpace;': { 'codepoints': [0x0200B], 'characters': '\u200B' },
+ '&NestedGreaterGreater;': { 'codepoints': [0x0226B], 'characters': '\u226B' },
+ '&NestedLessLess;': { 'codepoints': [0x0226A], 'characters': '\u226A' },
+ '&NewLine;': { 'codepoints': [0x0000A], 'characters': '\x0A' },
+ '&Nfr;': { 'codepoints': [0x1D511], 'characters': '\uD835\uDD11' },
+ '&NoBreak;': { 'codepoints': [0x02060], 'characters': '\u2060' },
+ '&NonBreakingSpace;': { 'codepoints': [0x000A0], 'characters': '\xA0' },
+ '&Nopf;': { 'codepoints': [0x02115], 'characters': '\u2115' },
+ '&Not;': { 'codepoints': [0x02AEC], 'characters': '\u2AEC' },
+ '&NotCongruent;': { 'codepoints': [0x02262], 'characters': '\u2262' },
+ '&NotCupCap;': { 'codepoints': [0x0226D], 'characters': '\u226D' },
+ '&NotDoubleVerticalBar;': { 'codepoints': [0x02226], 'characters': '\u2226' },
+ '&NotElement;': { 'codepoints': [0x02209], 'characters': '\u2209' },
+ '&NotEqual;': { 'codepoints': [0x02260], 'characters': '\u2260' },
+ '&NotEqualTilde;': { 'codepoints': [0x02242, 0x00338], 'characters': '\u2242\u0338' },
+ '&NotExists;': { 'codepoints': [0x02204], 'characters': '\u2204' },
+ '&NotGreater;': { 'codepoints': [0x0226F], 'characters': '\u226F' },
+ '&NotGreaterEqual;': { 'codepoints': [0x02271], 'characters': '\u2271' },
+ '&NotGreaterFullEqual;': { 'codepoints': [0x02267, 0x00338], 'characters': '\u2267\u0338' },
+ '&NotGreaterGreater;': { 'codepoints': [0x0226B, 0x00338], 'characters': '\u226B\u0338' },
+ '&NotGreaterLess;': { 'codepoints': [0x02279], 'characters': '\u2279' },
+ '&NotGreaterSlantEqual;': { 'codepoints': [0x02A7E, 0x00338], 'characters': '\u2A7E\u0338' },
+ '&NotGreaterTilde;': { 'codepoints': [0x02275], 'characters': '\u2275' },
+ '&NotHumpDownHump;': { 'codepoints': [0x0224E, 0x00338], 'characters': '\u224E\u0338' },
+ '&NotHumpEqual;': { 'codepoints': [0x0224F, 0x00338], 'characters': '\u224F\u0338' },
+ '&NotLeftTriangle;': { 'codepoints': [0x022EA], 'characters': '\u22EA' },
+ '&NotLeftTriangleBar;': { 'codepoints': [0x029CF, 0x00338], 'characters': '\u29CF\u0338' },
+ '&NotLeftTriangleEqual;': { 'codepoints': [0x022EC], 'characters': '\u22EC' },
+ '&NotLess;': { 'codepoints': [0x0226E], 'characters': '\u226E' },
+ '&NotLessEqual;': { 'codepoints': [0x02270], 'characters': '\u2270' },
+ '&NotLessGreater;': { 'codepoints': [0x02278], 'characters': '\u2278' },
+ '&NotLessLess;': { 'codepoints': [0x0226A, 0x00338], 'characters': '\u226A\u0338' },
+ '&NotLessSlantEqual;': { 'codepoints': [0x02A7D, 0x00338], 'characters': '\u2A7D\u0338' },
+ '&NotLessTilde;': { 'codepoints': [0x02274], 'characters': '\u2274' },
+ '&NotNestedGreaterGreater;': { 'codepoints': [0x02AA2, 0x00338], 'characters': '\u2AA2\u0338' },
+ '&NotNestedLessLess;': { 'codepoints': [0x02AA1, 0x00338], 'characters': '\u2AA1\u0338' },
+ '&NotPrecedes;': { 'codepoints': [0x02280], 'characters': '\u2280' },
+ '&NotPrecedesEqual;': { 'codepoints': [0x02AAF, 0x00338], 'characters': '\u2AAF\u0338' },
+ '&NotPrecedesSlantEqual;': { 'codepoints': [0x022E0], 'characters': '\u22E0' },
+ '&NotReverseElement;': { 'codepoints': [0x0220C], 'characters': '\u220C' },
+ '&NotRightTriangle;': { 'codepoints': [0x022EB], 'characters': '\u22EB' },
+ '&NotRightTriangleBar;': { 'codepoints': [0x029D0, 0x00338], 'characters': '\u29D0\u0338' },
+ '&NotRightTriangleEqual;': { 'codepoints': [0x022ED], 'characters': '\u22ED' },
+ '&NotSquareSubset;': { 'codepoints': [0x0228F, 0x00338], 'characters': '\u228F\u0338' },
+ '&NotSquareSubsetEqual;': { 'codepoints': [0x022E2], 'characters': '\u22E2' },
+ '&NotSquareSuperset;': { 'codepoints': [0x02290, 0x00338], 'characters': '\u2290\u0338' },
+ '&NotSquareSupersetEqual;': { 'codepoints': [0x022E3], 'characters': '\u22E3' },
+ '&NotSubset;': { 'codepoints': [0x02282, 0x020D2], 'characters': '\u2282\u20D2' },
+ '&NotSubsetEqual;': { 'codepoints': [0x02288], 'characters': '\u2288' },
+ '&NotSucceeds;': { 'codepoints': [0x02281], 'characters': '\u2281' },
+ '&NotSucceedsEqual;': { 'codepoints': [0x02AB0, 0x00338], 'characters': '\u2AB0\u0338' },
+ '&NotSucceedsSlantEqual;': { 'codepoints': [0x022E1], 'characters': '\u22E1' },
+ '&NotSucceedsTilde;': { 'codepoints': [0x0227F, 0x00338], 'characters': '\u227F\u0338' },
+ '&NotSuperset;': { 'codepoints': [0x02283, 0x020D2], 'characters': '\u2283\u20D2' },
+ '&NotSupersetEqual;': { 'codepoints': [0x02289], 'characters': '\u2289' },
+ '&NotTilde;': { 'codepoints': [0x02241], 'characters': '\u2241' },
+ '&NotTildeEqual;': { 'codepoints': [0x02244], 'characters': '\u2244' },
+ '&NotTildeFullEqual;': { 'codepoints': [0x02247], 'characters': '\u2247' },
+ '&NotTildeTilde;': { 'codepoints': [0x02249], 'characters': '\u2249' },
+ '&NotVerticalBar;': { 'codepoints': [0x02224], 'characters': '\u2224' },
+ '&Nscr;': { 'codepoints': [0x1D4A9], 'characters': '\uD835\uDCA9' },
+ '&Ntilde': { 'codepoints': [0x000D1], 'characters': '\xD1' },
+ '&Ntilde;': { 'codepoints': [0x000D1], 'characters': '\xD1' },
+ '&Nu;': { 'codepoints': [0x0039D], 'characters': '\u039D' },
+ '&OElig;': { 'codepoints': [0x00152], 'characters': '\u0152' },
+ '&Oacute': { 'codepoints': [0x000D3], 'characters': '\xD3' },
+ '&Oacute;': { 'codepoints': [0x000D3], 'characters': '\xD3' },
+ '&Ocirc': { 'codepoints': [0x000D4], 'characters': '\xD4' },
+ '&Ocirc;': { 'codepoints': [0x000D4], 'characters': '\xD4' },
+ '&Ocy;': { 'codepoints': [0x0041E], 'characters': '\u041E' },
+ '&Odblac;': { 'codepoints': [0x00150], 'characters': '\u0150' },
+ '&Ofr;': { 'codepoints': [0x1D512], 'characters': '\uD835\uDD12' },
+ '&Ograve': { 'codepoints': [0x000D2], 'characters': '\xD2' },
+ '&Ograve;': { 'codepoints': [0x000D2], 'characters': '\xD2' },
+ '&Omacr;': { 'codepoints': [0x0014C], 'characters': '\u014C' },
+ '&Omega;': { 'codepoints': [0x003A9], 'characters': '\u03A9' },
+ '&Omicron;': { 'codepoints': [0x0039F], 'characters': '\u039F' },
+ '&Oopf;': { 'codepoints': [0x1D546], 'characters': '\uD835\uDD46' },
+ '&OpenCurlyDoubleQuote;': { 'codepoints': [0x0201C], 'characters': '\u201C' },
+ '&OpenCurlyQuote;': { 'codepoints': [0x02018], 'characters': '\u2018' },
+ '&Or;': { 'codepoints': [0x02A54], 'characters': '\u2A54' },
+ '&Oscr;': { 'codepoints': [0x1D4AA], 'characters': '\uD835\uDCAA' },
+ '&Oslash': { 'codepoints': [0x000D8], 'characters': '\xD8' },
+ '&Oslash;': { 'codepoints': [0x000D8], 'characters': '\xD8' },
+ '&Otilde': { 'codepoints': [0x000D5], 'characters': '\xD5' },
+ '&Otilde;': { 'codepoints': [0x000D5], 'characters': '\xD5' },
+ '&Otimes;': { 'codepoints': [0x02A37], 'characters': '\u2A37' },
+ '&Ouml': { 'codepoints': [0x000D6], 'characters': '\xD6' },
+ '&Ouml;': { 'codepoints': [0x000D6], 'characters': '\xD6' },
+ '&OverBar;': { 'codepoints': [0x0203E], 'characters': '\u203E' },
+ '&OverBrace;': { 'codepoints': [0x023DE], 'characters': '\u23DE' },
+ '&OverBracket;': { 'codepoints': [0x023B4], 'characters': '\u23B4' },
+ '&OverParenthesis;': { 'codepoints': [0x023DC], 'characters': '\u23DC' },
+ '&PartialD;': { 'codepoints': [0x02202], 'characters': '\u2202' },
+ '&Pcy;': { 'codepoints': [0x0041F], 'characters': '\u041F' },
+ '&Pfr;': { 'codepoints': [0x1D513], 'characters': '\uD835\uDD13' },
+ '&Phi;': { 'codepoints': [0x003A6], 'characters': '\u03A6' },
+ '&Pi;': { 'codepoints': [0x003A0], 'characters': '\u03A0' },
+ '&PlusMinus;': { 'codepoints': [0x000B1], 'characters': '\xB1' },
+ '&Poincareplane;': { 'codepoints': [0x0210C], 'characters': '\u210C' },
+ '&Popf;': { 'codepoints': [0x02119], 'characters': '\u2119' },
+ '&Pr;': { 'codepoints': [0x02ABB], 'characters': '\u2ABB' },
+ '&Precedes;': { 'codepoints': [0x0227A], 'characters': '\u227A' },
+ '&PrecedesEqual;': { 'codepoints': [0x02AAF], 'characters': '\u2AAF' },
+ '&PrecedesSlantEqual;': { 'codepoints': [0x0227C], 'characters': '\u227C' },
+ '&PrecedesTilde;': { 'codepoints': [0x0227E], 'characters': '\u227E' },
+ '&Prime;': { 'codepoints': [0x02033], 'characters': '\u2033' },
+ '&Product;': { 'codepoints': [0x0220F], 'characters': '\u220F' },
+ '&Proportion;': { 'codepoints': [0x02237], 'characters': '\u2237' },
+ '&Proportional;': { 'codepoints': [0x0221D], 'characters': '\u221D' },
+ '&Pscr;': { 'codepoints': [0x1D4AB], 'characters': '\uD835\uDCAB' },
+ '&Psi;': { 'codepoints': [0x003A8], 'characters': '\u03A8' },
+ '&QUOT': { 'codepoints': [0x00022], 'characters': '\x22' },
+ '&QUOT;': { 'codepoints': [0x00022], 'characters': '\x22' },
+ '&Qfr;': { 'codepoints': [0x1D514], 'characters': '\uD835\uDD14' },
+ '&Qopf;': { 'codepoints': [0x0211A], 'characters': '\u211A' },
+ '&Qscr;': { 'codepoints': [0x1D4AC], 'characters': '\uD835\uDCAC' },
+ '&RBarr;': { 'codepoints': [0x02910], 'characters': '\u2910' },
+ '&REG': { 'codepoints': [0x000AE], 'characters': '\xAE' },
+ '&REG;': { 'codepoints': [0x000AE], 'characters': '\xAE' },
+ '&Racute;': { 'codepoints': [0x00154], 'characters': '\u0154' },
+ '&Rang;': { 'codepoints': [0x027EB], 'characters': '\u27EB' },
+ '&Rarr;': { 'codepoints': [0x021A0], 'characters': '\u21A0' },
+ '&Rarrtl;': { 'codepoints': [0x02916], 'characters': '\u2916' },
+ '&Rcaron;': { 'codepoints': [0x00158], 'characters': '\u0158' },
+ '&Rcedil;': { 'codepoints': [0x00156], 'characters': '\u0156' },
+ '&Rcy;': { 'codepoints': [0x00420], 'characters': '\u0420' },
+ '&Re;': { 'codepoints': [0x0211C], 'characters': '\u211C' },
+ '&ReverseElement;': { 'codepoints': [0x0220B], 'characters': '\u220B' },
+ '&ReverseEquilibrium;': { 'codepoints': [0x021CB], 'characters': '\u21CB' },
+ '&ReverseUpEquilibrium;': { 'codepoints': [0x0296F], 'characters': '\u296F' },
+ '&Rfr;': { 'codepoints': [0x0211C], 'characters': '\u211C' },
+ '&Rho;': { 'codepoints': [0x003A1], 'characters': '\u03A1' },
+ '&RightAngleBracket;': { 'codepoints': [0x027E9], 'characters': '\u27E9' },
+ '&RightArrow;': { 'codepoints': [0x02192], 'characters': '\u2192' },
+ '&RightArrowBar;': { 'codepoints': [0x021E5], 'characters': '\u21E5' },
+ '&RightArrowLeftArrow;': { 'codepoints': [0x021C4], 'characters': '\u21C4' },
+ '&RightCeiling;': { 'codepoints': [0x02309], 'characters': '\u2309' },
+ '&RightDoubleBracket;': { 'codepoints': [0x027E7], 'characters': '\u27E7' },
+ '&RightDownTeeVector;': { 'codepoints': [0x0295D], 'characters': '\u295D' },
+ '&RightDownVector;': { 'codepoints': [0x021C2], 'characters': '\u21C2' },
+ '&RightDownVectorBar;': { 'codepoints': [0x02955], 'characters': '\u2955' },
+ '&RightFloor;': { 'codepoints': [0x0230B], 'characters': '\u230B' },
+ '&RightTee;': { 'codepoints': [0x022A2], 'characters': '\u22A2' },
+ '&RightTeeArrow;': { 'codepoints': [0x021A6], 'characters': '\u21A6' },
+ '&RightTeeVector;': { 'codepoints': [0x0295B], 'characters': '\u295B' },
+ '&RightTriangle;': { 'codepoints': [0x022B3], 'characters': '\u22B3' },
+ '&RightTriangleBar;': { 'codepoints': [0x029D0], 'characters': '\u29D0' },
+ '&RightTriangleEqual;': { 'codepoints': [0x022B5], 'characters': '\u22B5' },
+ '&RightUpDownVector;': { 'codepoints': [0x0294F], 'characters': '\u294F' },
+ '&RightUpTeeVector;': { 'codepoints': [0x0295C], 'characters': '\u295C' },
+ '&RightUpVector;': { 'codepoints': [0x021BE], 'characters': '\u21BE' },
+ '&RightUpVectorBar;': { 'codepoints': [0x02954], 'characters': '\u2954' },
+ '&RightVector;': { 'codepoints': [0x021C0], 'characters': '\u21C0' },
+ '&RightVectorBar;': { 'codepoints': [0x02953], 'characters': '\u2953' },
+ '&Rightarrow;': { 'codepoints': [0x021D2], 'characters': '\u21D2' },
+ '&Ropf;': { 'codepoints': [0x0211D], 'characters': '\u211D' },
+ '&RoundImplies;': { 'codepoints': [0x02970], 'characters': '\u2970' },
+ '&Rrightarrow;': { 'codepoints': [0x021DB], 'characters': '\u21DB' },
+ '&Rscr;': { 'codepoints': [0x0211B], 'characters': '\u211B' },
+ '&Rsh;': { 'codepoints': [0x021B1], 'characters': '\u21B1' },
+ '&RuleDelayed;': { 'codepoints': [0x029F4], 'characters': '\u29F4' },
+ '&SHCHcy;': { 'codepoints': [0x00429], 'characters': '\u0429' },
+ '&SHcy;': { 'codepoints': [0x00428], 'characters': '\u0428' },
+ '&SOFTcy;': { 'codepoints': [0x0042C], 'characters': '\u042C' },
+ '&Sacute;': { 'codepoints': [0x0015A], 'characters': '\u015A' },
+ '&Sc;': { 'codepoints': [0x02ABC], 'characters': '\u2ABC' },
+ '&Scaron;': { 'codepoints': [0x00160], 'characters': '\u0160' },
+ '&Scedil;': { 'codepoints': [0x0015E], 'characters': '\u015E' },
+ '&Scirc;': { 'codepoints': [0x0015C], 'characters': '\u015C' },
+ '&Scy;': { 'codepoints': [0x00421], 'characters': '\u0421' },
+ '&Sfr;': { 'codepoints': [0x1D516], 'characters': '\uD835\uDD16' },
+ '&ShortDownArrow;': { 'codepoints': [0x02193], 'characters': '\u2193' },
+ '&ShortLeftArrow;': { 'codepoints': [0x02190], 'characters': '\u2190' },
+ '&ShortRightArrow;': { 'codepoints': [0x02192], 'characters': '\u2192' },
+ '&ShortUpArrow;': { 'codepoints': [0x02191], 'characters': '\u2191' },
+ '&Sigma;': { 'codepoints': [0x003A3], 'characters': '\u03A3' },
+ '&SmallCircle;': { 'codepoints': [0x02218], 'characters': '\u2218' },
+ '&Sopf;': { 'codepoints': [0x1D54A], 'characters': '\uD835\uDD4A' },
+ '&Sqrt;': { 'codepoints': [0x0221A], 'characters': '\u221A' },
+ '&Square;': { 'codepoints': [0x025A1], 'characters': '\u25A1' },
+ '&SquareIntersection;': { 'codepoints': [0x02293], 'characters': '\u2293' },
+ '&SquareSubset;': { 'codepoints': [0x0228F], 'characters': '\u228F' },
+ '&SquareSubsetEqual;': { 'codepoints': [0x02291], 'characters': '\u2291' },
+ '&SquareSuperset;': { 'codepoints': [0x02290], 'characters': '\u2290' },
+ '&SquareSupersetEqual;': { 'codepoints': [0x02292], 'characters': '\u2292' },
+ '&SquareUnion;': { 'codepoints': [0x02294], 'characters': '\u2294' },
+ '&Sscr;': { 'codepoints': [0x1D4AE], 'characters': '\uD835\uDCAE' },
+ '&Star;': { 'codepoints': [0x022C6], 'characters': '\u22C6' },
+ '&Sub;': { 'codepoints': [0x022D0], 'characters': '\u22D0' },
+ '&Subset;': { 'codepoints': [0x022D0], 'characters': '\u22D0' },
+ '&SubsetEqual;': { 'codepoints': [0x02286], 'characters': '\u2286' },
+ '&Succeeds;': { 'codepoints': [0x0227B], 'characters': '\u227B' },
+ '&SucceedsEqual;': { 'codepoints': [0x02AB0], 'characters': '\u2AB0' },
+ '&SucceedsSlantEqual;': { 'codepoints': [0x0227D], 'characters': '\u227D' },
+ '&SucceedsTilde;': { 'codepoints': [0x0227F], 'characters': '\u227F' },
+ '&SuchThat;': { 'codepoints': [0x0220B], 'characters': '\u220B' },
+ '&Sum;': { 'codepoints': [0x02211], 'characters': '\u2211' },
+ '&Sup;': { 'codepoints': [0x022D1], 'characters': '\u22D1' },
+ '&Superset;': { 'codepoints': [0x02283], 'characters': '\u2283' },
+ '&SupersetEqual;': { 'codepoints': [0x02287], 'characters': '\u2287' },
+ '&Supset;': { 'codepoints': [0x022D1], 'characters': '\u22D1' },
+ '&THORN': { 'codepoints': [0x000DE], 'characters': '\xDE' },
+ '&THORN;': { 'codepoints': [0x000DE], 'characters': '\xDE' },
+ '&TRADE;': { 'codepoints': [0x02122], 'characters': '\u2122' },
+ '&TSHcy;': { 'codepoints': [0x0040B], 'characters': '\u040B' },
+ '&TScy;': { 'codepoints': [0x00426], 'characters': '\u0426' },
+ '&Tab;': { 'codepoints': [0x00009], 'characters': '\x09' },
+ '&Tau;': { 'codepoints': [0x003A4], 'characters': '\u03A4' },
+ '&Tcaron;': { 'codepoints': [0x00164], 'characters': '\u0164' },
+ '&Tcedil;': { 'codepoints': [0x00162], 'characters': '\u0162' },
+ '&Tcy;': { 'codepoints': [0x00422], 'characters': '\u0422' },
+ '&Tfr;': { 'codepoints': [0x1D517], 'characters': '\uD835\uDD17' },
+ '&Therefore;': { 'codepoints': [0x02234], 'characters': '\u2234' },
+ '&Theta;': { 'codepoints': [0x00398], 'characters': '\u0398' },
+ '&ThickSpace;': { 'codepoints': [0x0205F, 0x0200A], 'characters': '\u205F\u200A' },
+ '&ThinSpace;': { 'codepoints': [0x02009], 'characters': '\u2009' },
+ '&Tilde;': { 'codepoints': [0x0223C], 'characters': '\u223C' },
+ '&TildeEqual;': { 'codepoints': [0x02243], 'characters': '\u2243' },
+ '&TildeFullEqual;': { 'codepoints': [0x02245], 'characters': '\u2245' },
+ '&TildeTilde;': { 'codepoints': [0x02248], 'characters': '\u2248' },
+ '&Topf;': { 'codepoints': [0x1D54B], 'characters': '\uD835\uDD4B' },
+ '&TripleDot;': { 'codepoints': [0x020DB], 'characters': '\u20DB' },
+ '&Tscr;': { 'codepoints': [0x1D4AF], 'characters': '\uD835\uDCAF' },
+ '&Tstrok;': { 'codepoints': [0x00166], 'characters': '\u0166' },
+ '&Uacute': { 'codepoints': [0x000DA], 'characters': '\xDA' },
+ '&Uacute;': { 'codepoints': [0x000DA], 'characters': '\xDA' },
+ '&Uarr;': { 'codepoints': [0x0219F], 'characters': '\u219F' },
+ '&Uarrocir;': { 'codepoints': [0x02949], 'characters': '\u2949' },
+ '&Ubrcy;': { 'codepoints': [0x0040E], 'characters': '\u040E' },
+ '&Ubreve;': { 'codepoints': [0x0016C], 'characters': '\u016C' },
+ '&Ucirc': { 'codepoints': [0x000DB], 'characters': '\xDB' },
+ '&Ucirc;': { 'codepoints': [0x000DB], 'characters': '\xDB' },
+ '&Ucy;': { 'codepoints': [0x00423], 'characters': '\u0423' },
+ '&Udblac;': { 'codepoints': [0x00170], 'characters': '\u0170' },
+ '&Ufr;': { 'codepoints': [0x1D518], 'characters': '\uD835\uDD18' },
+ '&Ugrave': { 'codepoints': [0x000D9], 'characters': '\xD9' },
+ '&Ugrave;': { 'codepoints': [0x000D9], 'characters': '\xD9' },
+ '&Umacr;': { 'codepoints': [0x0016A], 'characters': '\u016A' },
+ '&UnderBar;': { 'codepoints': [0x0005F], 'characters': '\x5F' },
+ '&UnderBrace;': { 'codepoints': [0x023DF], 'characters': '\u23DF' },
+ '&UnderBracket;': { 'codepoints': [0x023B5], 'characters': '\u23B5' },
+ '&UnderParenthesis;': { 'codepoints': [0x023DD], 'characters': '\u23DD' },
+ '&Union;': { 'codepoints': [0x022C3], 'characters': '\u22C3' },
+ '&UnionPlus;': { 'codepoints': [0x0228E], 'characters': '\u228E' },
+ '&Uogon;': { 'codepoints': [0x00172], 'characters': '\u0172' },
+ '&Uopf;': { 'codepoints': [0x1D54C], 'characters': '\uD835\uDD4C' },
+ '&UpArrow;': { 'codepoints': [0x02191], 'characters': '\u2191' },
+ '&UpArrowBar;': { 'codepoints': [0x02912], 'characters': '\u2912' },
+ '&UpArrowDownArrow;': { 'codepoints': [0x021C5], 'characters': '\u21C5' },
+ '&UpDownArrow;': { 'codepoints': [0x02195], 'characters': '\u2195' },
+ '&UpEquilibrium;': { 'codepoints': [0x0296E], 'characters': '\u296E' },
+ '&UpTee;': { 'codepoints': [0x022A5], 'characters': '\u22A5' },
+ '&UpTeeArrow;': { 'codepoints': [0x021A5], 'characters': '\u21A5' },
+ '&Uparrow;': { 'codepoints': [0x021D1], 'characters': '\u21D1' },
+ '&Updownarrow;': { 'codepoints': [0x021D5], 'characters': '\u21D5' },
+ '&UpperLeftArrow;': { 'codepoints': [0x02196], 'characters': '\u2196' },
+ '&UpperRightArrow;': { 'codepoints': [0x02197], 'characters': '\u2197' },
+ '&Upsi;': { 'codepoints': [0x003D2], 'characters': '\u03D2' },
+ '&Upsilon;': { 'codepoints': [0x003A5], 'characters': '\u03A5' },
+ '&Uring;': { 'codepoints': [0x0016E], 'characters': '\u016E' },
+ '&Uscr;': { 'codepoints': [0x1D4B0], 'characters': '\uD835\uDCB0' },
+ '&Utilde;': { 'codepoints': [0x00168], 'characters': '\u0168' },
+ '&Uuml': { 'codepoints': [0x000DC], 'characters': '\xDC' },
+ '&Uuml;': { 'codepoints': [0x000DC], 'characters': '\xDC' },
+ '&VDash;': { 'codepoints': [0x022AB], 'characters': '\u22AB' },
+ '&Vbar;': { 'codepoints': [0x02AEB], 'characters': '\u2AEB' },
+ '&Vcy;': { 'codepoints': [0x00412], 'characters': '\u0412' },
+ '&Vdash;': { 'codepoints': [0x022A9], 'characters': '\u22A9' },
+ '&Vdashl;': { 'codepoints': [0x02AE6], 'characters': '\u2AE6' },
+ '&Vee;': { 'codepoints': [0x022C1], 'characters': '\u22C1' },
+ '&Verbar;': { 'codepoints': [0x02016], 'characters': '\u2016' },
+ '&Vert;': { 'codepoints': [0x02016], 'characters': '\u2016' },
+ '&VerticalBar;': { 'codepoints': [0x02223], 'characters': '\u2223' },
+ '&VerticalLine;': { 'codepoints': [0x0007C], 'characters': '\x7C' },
+ '&VerticalSeparator;': { 'codepoints': [0x02758], 'characters': '\u2758' },
+ '&VerticalTilde;': { 'codepoints': [0x02240], 'characters': '\u2240' },
+ '&VeryThinSpace;': { 'codepoints': [0x0200A], 'characters': '\u200A' },
+ '&Vfr;': { 'codepoints': [0x1D519], 'characters': '\uD835\uDD19' },
+ '&Vopf;': { 'codepoints': [0x1D54D], 'characters': '\uD835\uDD4D' },
+ '&Vscr;': { 'codepoints': [0x1D4B1], 'characters': '\uD835\uDCB1' },
+ '&Vvdash;': { 'codepoints': [0x022AA], 'characters': '\u22AA' },
+ '&Wcirc;': { 'codepoints': [0x00174], 'characters': '\u0174' },
+ '&Wedge;': { 'codepoints': [0x022C0], 'characters': '\u22C0' },
+ '&Wfr;': { 'codepoints': [0x1D51A], 'characters': '\uD835\uDD1A' },
+ '&Wopf;': { 'codepoints': [0x1D54E], 'characters': '\uD835\uDD4E' },
+ '&Wscr;': { 'codepoints': [0x1D4B2], 'characters': '\uD835\uDCB2' },
+ '&Xfr;': { 'codepoints': [0x1D51B], 'characters': '\uD835\uDD1B' },
+ '&Xi;': { 'codepoints': [0x0039E], 'characters': '\u039E' },
+ '&Xopf;': { 'codepoints': [0x1D54F], 'characters': '\uD835\uDD4F' },
+ '&Xscr;': { 'codepoints': [0x1D4B3], 'characters': '\uD835\uDCB3' },
+ '&YAcy;': { 'codepoints': [0x0042F], 'characters': '\u042F' },
+ '&YIcy;': { 'codepoints': [0x00407], 'characters': '\u0407' },
+ '&YUcy;': { 'codepoints': [0x0042E], 'characters': '\u042E' },
+ '&Yacute': { 'codepoints': [0x000DD], 'characters': '\xDD' },
+ '&Yacute;': { 'codepoints': [0x000DD], 'characters': '\xDD' },
+ '&Ycirc;': { 'codepoints': [0x00176], 'characters': '\u0176' },
+ '&Ycy;': { 'codepoints': [0x0042B], 'characters': '\u042B' },
+ '&Yfr;': { 'codepoints': [0x1D51C], 'characters': '\uD835\uDD1C' },
+ '&Yopf;': { 'codepoints': [0x1D550], 'characters': '\uD835\uDD50' },
+ '&Yscr;': { 'codepoints': [0x1D4B4], 'characters': '\uD835\uDCB4' },
+ '&Yuml;': { 'codepoints': [0x00178], 'characters': '\u0178' },
+ '&ZHcy;': { 'codepoints': [0x00416], 'characters': '\u0416' },
+ '&Zacute;': { 'codepoints': [0x00179], 'characters': '\u0179' },
+ '&Zcaron;': { 'codepoints': [0x0017D], 'characters': '\u017D' },
+ '&Zcy;': { 'codepoints': [0x00417], 'characters': '\u0417' },
+ '&Zdot;': { 'codepoints': [0x0017B], 'characters': '\u017B' },
+ '&ZeroWidthSpace;': { 'codepoints': [0x0200B], 'characters': '\u200B' },
+ '&Zeta;': { 'codepoints': [0x00396], 'characters': '\u0396' },
+ '&Zfr;': { 'codepoints': [0x02128], 'characters': '\u2128' },
+ '&Zopf;': { 'codepoints': [0x02124], 'characters': '\u2124' },
+ '&Zscr;': { 'codepoints': [0x1D4B5], 'characters': '\uD835\uDCB5' },
+ '&aacute': { 'codepoints': [0x000E1], 'characters': '\xE1' },
+ '&aacute;': { 'codepoints': [0x000E1], 'characters': '\xE1' },
+ '&abreve;': { 'codepoints': [0x00103], 'characters': '\u0103' },
+ '&ac;': { 'codepoints': [0x0223E], 'characters': '\u223E' },
+ '&acE;': { 'codepoints': [0x0223E, 0x00333], 'characters': '\u223E\u0333' },
+ '&acd;': { 'codepoints': [0x0223F], 'characters': '\u223F' },
+ '&acirc': { 'codepoints': [0x000E2], 'characters': '\xE2' },
+ '&acirc;': { 'codepoints': [0x000E2], 'characters': '\xE2' },
+ '&acute': { 'codepoints': [0x000B4], 'characters': '\xB4' },
+ '&acute;': { 'codepoints': [0x000B4], 'characters': '\xB4' },
+ '&acy;': { 'codepoints': [0x00430], 'characters': '\u0430' },
+ '&aelig': { 'codepoints': [0x000E6], 'characters': '\xE6' },
+ '&aelig;': { 'codepoints': [0x000E6], 'characters': '\xE6' },
+ '&af;': { 'codepoints': [0x02061], 'characters': '\u2061' },
+ '&afr;': { 'codepoints': [0x1D51E], 'characters': '\uD835\uDD1E' },
+ '&agrave': { 'codepoints': [0x000E0], 'characters': '\xE0' },
+ '&agrave;': { 'codepoints': [0x000E0], 'characters': '\xE0' },
+ '&alefsym;': { 'codepoints': [0x02135], 'characters': '\u2135' },
+ '&aleph;': { 'codepoints': [0x02135], 'characters': '\u2135' },
+ '&alpha;': { 'codepoints': [0x003B1], 'characters': '\u03B1' },
+ '&amacr;': { 'codepoints': [0x00101], 'characters': '\u0101' },
+ '&amalg;': { 'codepoints': [0x02A3F], 'characters': '\u2A3F' },
+ '&amp': { 'codepoints': [0x00026], 'characters': '\x26' },
+ '&amp;': { 'codepoints': [0x00026], 'characters': '\x26' },
+ '&and;': { 'codepoints': [0x02227], 'characters': '\u2227' },
+ '&andand;': { 'codepoints': [0x02A55], 'characters': '\u2A55' },
+ '&andd;': { 'codepoints': [0x02A5C], 'characters': '\u2A5C' },
+ '&andslope;': { 'codepoints': [0x02A58], 'characters': '\u2A58' },
+ '&andv;': { 'codepoints': [0x02A5A], 'characters': '\u2A5A' },
+ '&ang;': { 'codepoints': [0x02220], 'characters': '\u2220' },
+ '&ange;': { 'codepoints': [0x029A4], 'characters': '\u29A4' },
+ '&angle;': { 'codepoints': [0x02220], 'characters': '\u2220' },
+ '&angmsd;': { 'codepoints': [0x02221], 'characters': '\u2221' },
+ '&angmsdaa;': { 'codepoints': [0x029A8], 'characters': '\u29A8' },
+ '&angmsdab;': { 'codepoints': [0x029A9], 'characters': '\u29A9' },
+ '&angmsdac;': { 'codepoints': [0x029AA], 'characters': '\u29AA' },
+ '&angmsdad;': { 'codepoints': [0x029AB], 'characters': '\u29AB' },
+ '&angmsdae;': { 'codepoints': [0x029AC], 'characters': '\u29AC' },
+ '&angmsdaf;': { 'codepoints': [0x029AD], 'characters': '\u29AD' },
+ '&angmsdag;': { 'codepoints': [0x029AE], 'characters': '\u29AE' },
+ '&angmsdah;': { 'codepoints': [0x029AF], 'characters': '\u29AF' },
+ '&angrt;': { 'codepoints': [0x0221F], 'characters': '\u221F' },
+ '&angrtvb;': { 'codepoints': [0x022BE], 'characters': '\u22BE' },
+ '&angrtvbd;': { 'codepoints': [0x0299D], 'characters': '\u299D' },
+ '&angsph;': { 'codepoints': [0x02222], 'characters': '\u2222' },
+ '&angst;': { 'codepoints': [0x000C5], 'characters': '\xC5' },
+ '&angzarr;': { 'codepoints': [0x0237C], 'characters': '\u237C' },
+ '&aogon;': { 'codepoints': [0x00105], 'characters': '\u0105' },
+ '&aopf;': { 'codepoints': [0x1D552], 'characters': '\uD835\uDD52' },
+ '&ap;': { 'codepoints': [0x02248], 'characters': '\u2248' },
+ '&apE;': { 'codepoints': [0x02A70], 'characters': '\u2A70' },
+ '&apacir;': { 'codepoints': [0x02A6F], 'characters': '\u2A6F' },
+ '&ape;': { 'codepoints': [0x0224A], 'characters': '\u224A' },
+ '&apid;': { 'codepoints': [0x0224B], 'characters': '\u224B' },
+ '&apos;': { 'codepoints': [0x00027], 'characters': '\x27' },
+ '&approx;': { 'codepoints': [0x02248], 'characters': '\u2248' },
+ '&approxeq;': { 'codepoints': [0x0224A], 'characters': '\u224A' },
+ '&aring': { 'codepoints': [0x000E5], 'characters': '\xE5' },
+ '&aring;': { 'codepoints': [0x000E5], 'characters': '\xE5' },
+ '&ascr;': { 'codepoints': [0x1D4B6], 'characters': '\uD835\uDCB6' },
+ '&ast;': { 'codepoints': [0x0002A], 'characters': '\x2A' },
+ '&asymp;': { 'codepoints': [0x02248], 'characters': '\u2248' },
+ '&asympeq;': { 'codepoints': [0x0224D], 'characters': '\u224D' },
+ '&atilde': { 'codepoints': [0x000E3], 'characters': '\xE3' },
+ '&atilde;': { 'codepoints': [0x000E3], 'characters': '\xE3' },
+ '&auml': { 'codepoints': [0x000E4], 'characters': '\xE4' },
+ '&auml;': { 'codepoints': [0x000E4], 'characters': '\xE4' },
+ '&awconint;': { 'codepoints': [0x02233], 'characters': '\u2233' },
+ '&awint;': { 'codepoints': [0x02A11], 'characters': '\u2A11' },
+ '&bNot;': { 'codepoints': [0x02AED], 'characters': '\u2AED' },
+ '&backcong;': { 'codepoints': [0x0224C], 'characters': '\u224C' },
+ '&backepsilon;': { 'codepoints': [0x003F6], 'characters': '\u03F6' },
+ '&backprime;': { 'codepoints': [0x02035], 'characters': '\u2035' },
+ '&backsim;': { 'codepoints': [0x0223D], 'characters': '\u223D' },
+ '&backsimeq;': { 'codepoints': [0x022CD], 'characters': '\u22CD' },
+ '&barvee;': { 'codepoints': [0x022BD], 'characters': '\u22BD' },
+ '&barwed;': { 'codepoints': [0x02305], 'characters': '\u2305' },
+ '&barwedge;': { 'codepoints': [0x02305], 'characters': '\u2305' },
+ '&bbrk;': { 'codepoints': [0x023B5], 'characters': '\u23B5' },
+ '&bbrktbrk;': { 'codepoints': [0x023B6], 'characters': '\u23B6' },
+ '&bcong;': { 'codepoints': [0x0224C], 'characters': '\u224C' },
+ '&bcy;': { 'codepoints': [0x00431], 'characters': '\u0431' },
+ '&bdquo;': { 'codepoints': [0x0201E], 'characters': '\u201E' },
+ '&becaus;': { 'codepoints': [0x02235], 'characters': '\u2235' },
+ '&because;': { 'codepoints': [0x02235], 'characters': '\u2235' },
+ '&bemptyv;': { 'codepoints': [0x029B0], 'characters': '\u29B0' },
+ '&bepsi;': { 'codepoints': [0x003F6], 'characters': '\u03F6' },
+ '&bernou;': { 'codepoints': [0x0212C], 'characters': '\u212C' },
+ '&beta;': { 'codepoints': [0x003B2], 'characters': '\u03B2' },
+ '&beth;': { 'codepoints': [0x02136], 'characters': '\u2136' },
+ '&between;': { 'codepoints': [0x0226C], 'characters': '\u226C' },
+ '&bfr;': { 'codepoints': [0x1D51F], 'characters': '\uD835\uDD1F' },
+ '&bigcap;': { 'codepoints': [0x022C2], 'characters': '\u22C2' },
+ '&bigcirc;': { 'codepoints': [0x025EF], 'characters': '\u25EF' },
+ '&bigcup;': { 'codepoints': [0x022C3], 'characters': '\u22C3' },
+ '&bigodot;': { 'codepoints': [0x02A00], 'characters': '\u2A00' },
+ '&bigoplus;': { 'codepoints': [0x02A01], 'characters': '\u2A01' },
+ '&bigotimes;': { 'codepoints': [0x02A02], 'characters': '\u2A02' },
+ '&bigsqcup;': { 'codepoints': [0x02A06], 'characters': '\u2A06' },
+ '&bigstar;': { 'codepoints': [0x02605], 'characters': '\u2605' },
+ '&bigtriangledown;': { 'codepoints': [0x025BD], 'characters': '\u25BD' },
+ '&bigtriangleup;': { 'codepoints': [0x025B3], 'characters': '\u25B3' },
+ '&biguplus;': { 'codepoints': [0x02A04], 'characters': '\u2A04' },
+ '&bigvee;': { 'codepoints': [0x022C1], 'characters': '\u22C1' },
+ '&bigwedge;': { 'codepoints': [0x022C0], 'characters': '\u22C0' },
+ '&bkarow;': { 'codepoints': [0x0290D], 'characters': '\u290D' },
+ '&blacklozenge;': { 'codepoints': [0x029EB], 'characters': '\u29EB' },
+ '&blacksquare;': { 'codepoints': [0x025AA], 'characters': '\u25AA' },
+ '&blacktriangle;': { 'codepoints': [0x025B4], 'characters': '\u25B4' },
+ '&blacktriangledown;': { 'codepoints': [0x025BE], 'characters': '\u25BE' },
+ '&blacktriangleleft;': { 'codepoints': [0x025C2], 'characters': '\u25C2' },
+ '&blacktriangleright;': { 'codepoints': [0x025B8], 'characters': '\u25B8' },
+ '&blank;': { 'codepoints': [0x02423], 'characters': '\u2423' },
+ '&blk12;': { 'codepoints': [0x02592], 'characters': '\u2592' },
+ '&blk14;': { 'codepoints': [0x02591], 'characters': '\u2591' },
+ '&blk34;': { 'codepoints': [0x02593], 'characters': '\u2593' },
+ '&block;': { 'codepoints': [0x02588], 'characters': '\u2588' },
+ '&bne;': { 'codepoints': [0x0003D, 0x020E5], 'characters': '\x3D\u20E5' },
+ '&bnequiv;': { 'codepoints': [0x02261, 0x020E5], 'characters': '\u2261\u20E5' },
+ '&bnot;': { 'codepoints': [0x02310], 'characters': '\u2310' },
+ '&bopf;': { 'codepoints': [0x1D553], 'characters': '\uD835\uDD53' },
+ '&bot;': { 'codepoints': [0x022A5], 'characters': '\u22A5' },
+ '&bottom;': { 'codepoints': [0x022A5], 'characters': '\u22A5' },
+ '&bowtie;': { 'codepoints': [0x022C8], 'characters': '\u22C8' },
+ '&boxDL;': { 'codepoints': [0x02557], 'characters': '\u2557' },
+ '&boxDR;': { 'codepoints': [0x02554], 'characters': '\u2554' },
+ '&boxDl;': { 'codepoints': [0x02556], 'characters': '\u2556' },
+ '&boxDr;': { 'codepoints': [0x02553], 'characters': '\u2553' },
+ '&boxH;': { 'codepoints': [0x02550], 'characters': '\u2550' },
+ '&boxHD;': { 'codepoints': [0x02566], 'characters': '\u2566' },
+ '&boxHU;': { 'codepoints': [0x02569], 'characters': '\u2569' },
+ '&boxHd;': { 'codepoints': [0x02564], 'characters': '\u2564' },
+ '&boxHu;': { 'codepoints': [0x02567], 'characters': '\u2567' },
+ '&boxUL;': { 'codepoints': [0x0255D], 'characters': '\u255D' },
+ '&boxUR;': { 'codepoints': [0x0255A], 'characters': '\u255A' },
+ '&boxUl;': { 'codepoints': [0x0255C], 'characters': '\u255C' },
+ '&boxUr;': { 'codepoints': [0x02559], 'characters': '\u2559' },
+ '&boxV;': { 'codepoints': [0x02551], 'characters': '\u2551' },
+ '&boxVH;': { 'codepoints': [0x0256C], 'characters': '\u256C' },
+ '&boxVL;': { 'codepoints': [0x02563], 'characters': '\u2563' },
+ '&boxVR;': { 'codepoints': [0x02560], 'characters': '\u2560' },
+ '&boxVh;': { 'codepoints': [0x0256B], 'characters': '\u256B' },
+ '&boxVl;': { 'codepoints': [0x02562], 'characters': '\u2562' },
+ '&boxVr;': { 'codepoints': [0x0255F], 'characters': '\u255F' },
+ '&boxbox;': { 'codepoints': [0x029C9], 'characters': '\u29C9' },
+ '&boxdL;': { 'codepoints': [0x02555], 'characters': '\u2555' },
+ '&boxdR;': { 'codepoints': [0x02552], 'characters': '\u2552' },
+ '&boxdl;': { 'codepoints': [0x02510], 'characters': '\u2510' },
+ '&boxdr;': { 'codepoints': [0x0250C], 'characters': '\u250C' },
+ '&boxh;': { 'codepoints': [0x02500], 'characters': '\u2500' },
+ '&boxhD;': { 'codepoints': [0x02565], 'characters': '\u2565' },
+ '&boxhU;': { 'codepoints': [0x02568], 'characters': '\u2568' },
+ '&boxhd;': { 'codepoints': [0x0252C], 'characters': '\u252C' },
+ '&boxhu;': { 'codepoints': [0x02534], 'characters': '\u2534' },
+ '&boxminus;': { 'codepoints': [0x0229F], 'characters': '\u229F' },
+ '&boxplus;': { 'codepoints': [0x0229E], 'characters': '\u229E' },
+ '&boxtimes;': { 'codepoints': [0x022A0], 'characters': '\u22A0' },
+ '&boxuL;': { 'codepoints': [0x0255B], 'characters': '\u255B' },
+ '&boxuR;': { 'codepoints': [0x02558], 'characters': '\u2558' },
+ '&boxul;': { 'codepoints': [0x02518], 'characters': '\u2518' },
+ '&boxur;': { 'codepoints': [0x02514], 'characters': '\u2514' },
+ '&boxv;': { 'codepoints': [0x02502], 'characters': '\u2502' },
+ '&boxvH;': { 'codepoints': [0x0256A], 'characters': '\u256A' },
+ '&boxvL;': { 'codepoints': [0x02561], 'characters': '\u2561' },
+ '&boxvR;': { 'codepoints': [0x0255E], 'characters': '\u255E' },
+ '&boxvh;': { 'codepoints': [0x0253C], 'characters': '\u253C' },
+ '&boxvl;': { 'codepoints': [0x02524], 'characters': '\u2524' },
+ '&boxvr;': { 'codepoints': [0x0251C], 'characters': '\u251C' },
+ '&bprime;': { 'codepoints': [0x02035], 'characters': '\u2035' },
+ '&breve;': { 'codepoints': [0x002D8], 'characters': '\u02D8' },
+ '&brvbar': { 'codepoints': [0x000A6], 'characters': '\xA6' },
+ '&brvbar;': { 'codepoints': [0x000A6], 'characters': '\xA6' },
+ '&bscr;': { 'codepoints': [0x1D4B7], 'characters': '\uD835\uDCB7' },
+ '&bsemi;': { 'codepoints': [0x0204F], 'characters': '\u204F' },
+ '&bsim;': { 'codepoints': [0x0223D], 'characters': '\u223D' },
+ '&bsime;': { 'codepoints': [0x022CD], 'characters': '\u22CD' },
+ '&bsol;': { 'codepoints': [0x0005C], 'characters': '\x5C' },
+ '&bsolb;': { 'codepoints': [0x029C5], 'characters': '\u29C5' },
+ '&bsolhsub;': { 'codepoints': [0x027C8], 'characters': '\u27C8' },
+ '&bull;': { 'codepoints': [0x02022], 'characters': '\u2022' },
+ '&bullet;': { 'codepoints': [0x02022], 'characters': '\u2022' },
+ '&bump;': { 'codepoints': [0x0224E], 'characters': '\u224E' },
+ '&bumpE;': { 'codepoints': [0x02AAE], 'characters': '\u2AAE' },
+ '&bumpe;': { 'codepoints': [0x0224F], 'characters': '\u224F' },
+ '&bumpeq;': { 'codepoints': [0x0224F], 'characters': '\u224F' },
+ '&cacute;': { 'codepoints': [0x00107], 'characters': '\u0107' },
+ '&cap;': { 'codepoints': [0x02229], 'characters': '\u2229' },
+ '&capand;': { 'codepoints': [0x02A44], 'characters': '\u2A44' },
+ '&capbrcup;': { 'codepoints': [0x02A49], 'characters': '\u2A49' },
+ '&capcap;': { 'codepoints': [0x02A4B], 'characters': '\u2A4B' },
+ '&capcup;': { 'codepoints': [0x02A47], 'characters': '\u2A47' },
+ '&capdot;': { 'codepoints': [0x02A40], 'characters': '\u2A40' },
+ '&caps;': { 'codepoints': [0x02229, 0x0FE00], 'characters': '\u2229\uFE00' },
+ '&caret;': { 'codepoints': [0x02041], 'characters': '\u2041' },
+ '&caron;': { 'codepoints': [0x002C7], 'characters': '\u02C7' },
+ '&ccaps;': { 'codepoints': [0x02A4D], 'characters': '\u2A4D' },
+ '&ccaron;': { 'codepoints': [0x0010D], 'characters': '\u010D' },
+ '&ccedil': { 'codepoints': [0x000E7], 'characters': '\xE7' },
+ '&ccedil;': { 'codepoints': [0x000E7], 'characters': '\xE7' },
+ '&ccirc;': { 'codepoints': [0x00109], 'characters': '\u0109' },
+ '&ccups;': { 'codepoints': [0x02A4C], 'characters': '\u2A4C' },
+ '&ccupssm;': { 'codepoints': [0x02A50], 'characters': '\u2A50' },
+ '&cdot;': { 'codepoints': [0x0010B], 'characters': '\u010B' },
+ '&cedil': { 'codepoints': [0x000B8], 'characters': '\xB8' },
+ '&cedil;': { 'codepoints': [0x000B8], 'characters': '\xB8' },
+ '&cemptyv;': { 'codepoints': [0x029B2], 'characters': '\u29B2' },
+ '&cent': { 'codepoints': [0x000A2], 'characters': '\xA2' },
+ '&cent;': { 'codepoints': [0x000A2], 'characters': '\xA2' },
+ '&centerdot;': { 'codepoints': [0x000B7], 'characters': '\xB7' },
+ '&cfr;': { 'codepoints': [0x1D520], 'characters': '\uD835\uDD20' },
+ '&chcy;': { 'codepoints': [0x00447], 'characters': '\u0447' },
+ '&check;': { 'codepoints': [0x02713], 'characters': '\u2713' },
+ '&checkmark;': { 'codepoints': [0x02713], 'characters': '\u2713' },
+ '&chi;': { 'codepoints': [0x003C7], 'characters': '\u03C7' },
+ '&cir;': { 'codepoints': [0x025CB], 'characters': '\u25CB' },
+ '&cirE;': { 'codepoints': [0x029C3], 'characters': '\u29C3' },
+ '&circ;': { 'codepoints': [0x002C6], 'characters': '\u02C6' },
+ '&circeq;': { 'codepoints': [0x02257], 'characters': '\u2257' },
+ '&circlearrowleft;': { 'codepoints': [0x021BA], 'characters': '\u21BA' },
+ '&circlearrowright;': { 'codepoints': [0x021BB], 'characters': '\u21BB' },
+ '&circledR;': { 'codepoints': [0x000AE], 'characters': '\xAE' },
+ '&circledS;': { 'codepoints': [0x024C8], 'characters': '\u24C8' },
+ '&circledast;': { 'codepoints': [0x0229B], 'characters': '\u229B' },
+ '&circledcirc;': { 'codepoints': [0x0229A], 'characters': '\u229A' },
+ '&circleddash;': { 'codepoints': [0x0229D], 'characters': '\u229D' },
+ '&cire;': { 'codepoints': [0x02257], 'characters': '\u2257' },
+ '&cirfnint;': { 'codepoints': [0x02A10], 'characters': '\u2A10' },
+ '&cirmid;': { 'codepoints': [0x02AEF], 'characters': '\u2AEF' },
+ '&cirscir;': { 'codepoints': [0x029C2], 'characters': '\u29C2' },
+ '&clubs;': { 'codepoints': [0x02663], 'characters': '\u2663' },
+ '&clubsuit;': { 'codepoints': [0x02663], 'characters': '\u2663' },
+ '&colon;': { 'codepoints': [0x0003A], 'characters': '\x3A' },
+ '&colone;': { 'codepoints': [0x02254], 'characters': '\u2254' },
+ '&coloneq;': { 'codepoints': [0x02254], 'characters': '\u2254' },
+ '&comma;': { 'codepoints': [0x0002C], 'characters': '\x2C' },
+ '&commat;': { 'codepoints': [0x00040], 'characters': '\x40' },
+ '&comp;': { 'codepoints': [0x02201], 'characters': '\u2201' },
+ '&compfn;': { 'codepoints': [0x02218], 'characters': '\u2218' },
+ '&complement;': { 'codepoints': [0x02201], 'characters': '\u2201' },
+ '&complexes;': { 'codepoints': [0x02102], 'characters': '\u2102' },
+ '&cong;': { 'codepoints': [0x02245], 'characters': '\u2245' },
+ '&congdot;': { 'codepoints': [0x02A6D], 'characters': '\u2A6D' },
+ '&conint;': { 'codepoints': [0x0222E], 'characters': '\u222E' },
+ '&copf;': { 'codepoints': [0x1D554], 'characters': '\uD835\uDD54' },
+ '&coprod;': { 'codepoints': [0x02210], 'characters': '\u2210' },
+ '&copy': { 'codepoints': [0x000A9], 'characters': '\xA9' },
+ '&copy;': { 'codepoints': [0x000A9], 'characters': '\xA9' },
+ '&copysr;': { 'codepoints': [0x02117], 'characters': '\u2117' },
+ '&crarr;': { 'codepoints': [0x021B5], 'characters': '\u21B5' },
+ '&cross;': { 'codepoints': [0x02717], 'characters': '\u2717' },
+ '&cscr;': { 'codepoints': [0x1D4B8], 'characters': '\uD835\uDCB8' },
+ '&csub;': { 'codepoints': [0x02ACF], 'characters': '\u2ACF' },
+ '&csube;': { 'codepoints': [0x02AD1], 'characters': '\u2AD1' },
+ '&csup;': { 'codepoints': [0x02AD0], 'characters': '\u2AD0' },
+ '&csupe;': { 'codepoints': [0x02AD2], 'characters': '\u2AD2' },
+ '&ctdot;': { 'codepoints': [0x022EF], 'characters': '\u22EF' },
+ '&cudarrl;': { 'codepoints': [0x02938], 'characters': '\u2938' },
+ '&cudarrr;': { 'codepoints': [0x02935], 'characters': '\u2935' },
+ '&cuepr;': { 'codepoints': [0x022DE], 'characters': '\u22DE' },
+ '&cuesc;': { 'codepoints': [0x022DF], 'characters': '\u22DF' },
+ '&cularr;': { 'codepoints': [0x021B6], 'characters': '\u21B6' },
+ '&cularrp;': { 'codepoints': [0x0293D], 'characters': '\u293D' },
+ '&cup;': { 'codepoints': [0x0222A], 'characters': '\u222A' },
+ '&cupbrcap;': { 'codepoints': [0x02A48], 'characters': '\u2A48' },
+ '&cupcap;': { 'codepoints': [0x02A46], 'characters': '\u2A46' },
+ '&cupcup;': { 'codepoints': [0x02A4A], 'characters': '\u2A4A' },
+ '&cupdot;': { 'codepoints': [0x0228D], 'characters': '\u228D' },
+ '&cupor;': { 'codepoints': [0x02A45], 'characters': '\u2A45' },
+ '&cups;': { 'codepoints': [0x0222A, 0x0FE00], 'characters': '\u222A\uFE00' },
+ '&curarr;': { 'codepoints': [0x021B7], 'characters': '\u21B7' },
+ '&curarrm;': { 'codepoints': [0x0293C], 'characters': '\u293C' },
+ '&curlyeqprec;': { 'codepoints': [0x022DE], 'characters': '\u22DE' },
+ '&curlyeqsucc;': { 'codepoints': [0x022DF], 'characters': '\u22DF' },
+ '&curlyvee;': { 'codepoints': [0x022CE], 'characters': '\u22CE' },
+ '&curlywedge;': { 'codepoints': [0x022CF], 'characters': '\u22CF' },
+ '&curren': { 'codepoints': [0x000A4], 'characters': '\xA4' },
+ '&curren;': { 'codepoints': [0x000A4], 'characters': '\xA4' },
+ '&curvearrowleft;': { 'codepoints': [0x021B6], 'characters': '\u21B6' },
+ '&curvearrowright;': { 'codepoints': [0x021B7], 'characters': '\u21B7' },
+ '&cuvee;': { 'codepoints': [0x022CE], 'characters': '\u22CE' },
+ '&cuwed;': { 'codepoints': [0x022CF], 'characters': '\u22CF' },
+ '&cwconint;': { 'codepoints': [0x02232], 'characters': '\u2232' },
+ '&cwint;': { 'codepoints': [0x02231], 'characters': '\u2231' },
+ '&cylcty;': { 'codepoints': [0x0232D], 'characters': '\u232D' },
+ '&dArr;': { 'codepoints': [0x021D3], 'characters': '\u21D3' },
+ '&dHar;': { 'codepoints': [0x02965], 'characters': '\u2965' },
+ '&dagger;': { 'codepoints': [0x02020], 'characters': '\u2020' },
+ '&daleth;': { 'codepoints': [0x02138], 'characters': '\u2138' },
+ '&darr;': { 'codepoints': [0x02193], 'characters': '\u2193' },
+ '&dash;': { 'codepoints': [0x02010], 'characters': '\u2010' },
+ '&dashv;': { 'codepoints': [0x022A3], 'characters': '\u22A3' },
+ '&dbkarow;': { 'codepoints': [0x0290F], 'characters': '\u290F' },
+ '&dblac;': { 'codepoints': [0x002DD], 'characters': '\u02DD' },
+ '&dcaron;': { 'codepoints': [0x0010F], 'characters': '\u010F' },
+ '&dcy;': { 'codepoints': [0x00434], 'characters': '\u0434' },
+ '&dd;': { 'codepoints': [0x02146], 'characters': '\u2146' },
+ '&ddagger;': { 'codepoints': [0x02021], 'characters': '\u2021' },
+ '&ddarr;': { 'codepoints': [0x021CA], 'characters': '\u21CA' },
+ '&ddotseq;': { 'codepoints': [0x02A77], 'characters': '\u2A77' },
+ '&deg': { 'codepoints': [0x000B0], 'characters': '\xB0' },
+ '&deg;': { 'codepoints': [0x000B0], 'characters': '\xB0' },
+ '&delta;': { 'codepoints': [0x003B4], 'characters': '\u03B4' },
+ '&demptyv;': { 'codepoints': [0x029B1], 'characters': '\u29B1' },
+ '&dfisht;': { 'codepoints': [0x0297F], 'characters': '\u297F' },
+ '&dfr;': { 'codepoints': [0x1D521], 'characters': '\uD835\uDD21' },
+ '&dharl;': { 'codepoints': [0x021C3], 'characters': '\u21C3' },
+ '&dharr;': { 'codepoints': [0x021C2], 'characters': '\u21C2' },
+ '&diam;': { 'codepoints': [0x022C4], 'characters': '\u22C4' },
+ '&diamond;': { 'codepoints': [0x022C4], 'characters': '\u22C4' },
+ '&diamondsuit;': { 'codepoints': [0x02666], 'characters': '\u2666' },
+ '&diams;': { 'codepoints': [0x02666], 'characters': '\u2666' },
+ '&die;': { 'codepoints': [0x000A8], 'characters': '\xA8' },
+ '&digamma;': { 'codepoints': [0x003DD], 'characters': '\u03DD' },
+ '&disin;': { 'codepoints': [0x022F2], 'characters': '\u22F2' },
+ '&div;': { 'codepoints': [0x000F7], 'characters': '\xF7' },
+ '&divide': { 'codepoints': [0x000F7], 'characters': '\xF7' },
+ '&divide;': { 'codepoints': [0x000F7], 'characters': '\xF7' },
+ '&divideontimes;': { 'codepoints': [0x022C7], 'characters': '\u22C7' },
+ '&divonx;': { 'codepoints': [0x022C7], 'characters': '\u22C7' },
+ '&djcy;': { 'codepoints': [0x00452], 'characters': '\u0452' },
+ '&dlcorn;': { 'codepoints': [0x0231E], 'characters': '\u231E' },
+ '&dlcrop;': { 'codepoints': [0x0230D], 'characters': '\u230D' },
+ '&dollar;': { 'codepoints': [0x00024], 'characters': '\x24' },
+ '&dopf;': { 'codepoints': [0x1D555], 'characters': '\uD835\uDD55' },
+ '&dot;': { 'codepoints': [0x002D9], 'characters': '\u02D9' },
+ '&doteq;': { 'codepoints': [0x02250], 'characters': '\u2250' },
+ '&doteqdot;': { 'codepoints': [0x02251], 'characters': '\u2251' },
+ '&dotminus;': { 'codepoints': [0x02238], 'characters': '\u2238' },
+ '&dotplus;': { 'codepoints': [0x02214], 'characters': '\u2214' },
+ '&dotsquare;': { 'codepoints': [0x022A1], 'characters': '\u22A1' },
+ '&doublebarwedge;': { 'codepoints': [0x02306], 'characters': '\u2306' },
+ '&downarrow;': { 'codepoints': [0x02193], 'characters': '\u2193' },
+ '&downdownarrows;': { 'codepoints': [0x021CA], 'characters': '\u21CA' },
+ '&downharpoonleft;': { 'codepoints': [0x021C3], 'characters': '\u21C3' },
+ '&downharpoonright;': { 'codepoints': [0x021C2], 'characters': '\u21C2' },
+ '&drbkarow;': { 'codepoints': [0x02910], 'characters': '\u2910' },
+ '&drcorn;': { 'codepoints': [0x0231F], 'characters': '\u231F' },
+ '&drcrop;': { 'codepoints': [0x0230C], 'characters': '\u230C' },
+ '&dscr;': { 'codepoints': [0x1D4B9], 'characters': '\uD835\uDCB9' },
+ '&dscy;': { 'codepoints': [0x00455], 'characters': '\u0455' },
+ '&dsol;': { 'codepoints': [0x029F6], 'characters': '\u29F6' },
+ '&dstrok;': { 'codepoints': [0x00111], 'characters': '\u0111' },
+ '&dtdot;': { 'codepoints': [0x022F1], 'characters': '\u22F1' },
+ '&dtri;': { 'codepoints': [0x025BF], 'characters': '\u25BF' },
+ '&dtrif;': { 'codepoints': [0x025BE], 'characters': '\u25BE' },
+ '&duarr;': { 'codepoints': [0x021F5], 'characters': '\u21F5' },
+ '&duhar;': { 'codepoints': [0x0296F], 'characters': '\u296F' },
+ '&dwangle;': { 'codepoints': [0x029A6], 'characters': '\u29A6' },
+ '&dzcy;': { 'codepoints': [0x0045F], 'characters': '\u045F' },
+ '&dzigrarr;': { 'codepoints': [0x027FF], 'characters': '\u27FF' },
+ '&eDDot;': { 'codepoints': [0x02A77], 'characters': '\u2A77' },
+ '&eDot;': { 'codepoints': [0x02251], 'characters': '\u2251' },
+ '&eacute': { 'codepoints': [0x000E9], 'characters': '\xE9' },
+ '&eacute;': { 'codepoints': [0x000E9], 'characters': '\xE9' },
+ '&easter;': { 'codepoints': [0x02A6E], 'characters': '\u2A6E' },
+ '&ecaron;': { 'codepoints': [0x0011B], 'characters': '\u011B' },
+ '&ecir;': { 'codepoints': [0x02256], 'characters': '\u2256' },
+ '&ecirc': { 'codepoints': [0x000EA], 'characters': '\xEA' },
+ '&ecirc;': { 'codepoints': [0x000EA], 'characters': '\xEA' },
+ '&ecolon;': { 'codepoints': [0x02255], 'characters': '\u2255' },
+ '&ecy;': { 'codepoints': [0x0044D], 'characters': '\u044D' },
+ '&edot;': { 'codepoints': [0x00117], 'characters': '\u0117' },
+ '&ee;': { 'codepoints': [0x02147], 'characters': '\u2147' },
+ '&efDot;': { 'codepoints': [0x02252], 'characters': '\u2252' },
+ '&efr;': { 'codepoints': [0x1D522], 'characters': '\uD835\uDD22' },
+ '&eg;': { 'codepoints': [0x02A9A], 'characters': '\u2A9A' },
+ '&egrave': { 'codepoints': [0x000E8], 'characters': '\xE8' },
+ '&egrave;': { 'codepoints': [0x000E8], 'characters': '\xE8' },
+ '&egs;': { 'codepoints': [0x02A96], 'characters': '\u2A96' },
+ '&egsdot;': { 'codepoints': [0x02A98], 'characters': '\u2A98' },
+ '&el;': { 'codepoints': [0x02A99], 'characters': '\u2A99' },
+ '&elinters;': { 'codepoints': [0x023E7], 'characters': '\u23E7' },
+ '&ell;': { 'codepoints': [0x02113], 'characters': '\u2113' },
+ '&els;': { 'codepoints': [0x02A95], 'characters': '\u2A95' },
+ '&elsdot;': { 'codepoints': [0x02A97], 'characters': '\u2A97' },
+ '&emacr;': { 'codepoints': [0x00113], 'characters': '\u0113' },
+ '&empty;': { 'codepoints': [0x02205], 'characters': '\u2205' },
+ '&emptyset;': { 'codepoints': [0x02205], 'characters': '\u2205' },
+ '&emptyv;': { 'codepoints': [0x02205], 'characters': '\u2205' },
+ '&emsp13;': { 'codepoints': [0x02004], 'characters': '\u2004' },
+ '&emsp14;': { 'codepoints': [0x02005], 'characters': '\u2005' },
+ '&emsp;': { 'codepoints': [0x02003], 'characters': '\u2003' },
+ '&eng;': { 'codepoints': [0x0014B], 'characters': '\u014B' },
+ '&ensp;': { 'codepoints': [0x02002], 'characters': '\u2002' },
+ '&eogon;': { 'codepoints': [0x00119], 'characters': '\u0119' },
+ '&eopf;': { 'codepoints': [0x1D556], 'characters': '\uD835\uDD56' },
+ '&epar;': { 'codepoints': [0x022D5], 'characters': '\u22D5' },
+ '&eparsl;': { 'codepoints': [0x029E3], 'characters': '\u29E3' },
+ '&eplus;': { 'codepoints': [0x02A71], 'characters': '\u2A71' },
+ '&epsi;': { 'codepoints': [0x003B5], 'characters': '\u03B5' },
+ '&epsilon;': { 'codepoints': [0x003B5], 'characters': '\u03B5' },
+ '&epsiv;': { 'codepoints': [0x003F5], 'characters': '\u03F5' },
+ '&eqcirc;': { 'codepoints': [0x02256], 'characters': '\u2256' },
+ '&eqcolon;': { 'codepoints': [0x02255], 'characters': '\u2255' },
+ '&eqsim;': { 'codepoints': [0x02242], 'characters': '\u2242' },
+ '&eqslantgtr;': { 'codepoints': [0x02A96], 'characters': '\u2A96' },
+ '&eqslantless;': { 'codepoints': [0x02A95], 'characters': '\u2A95' },
+ '&equals;': { 'codepoints': [0x0003D], 'characters': '\x3D' },
+ '&equest;': { 'codepoints': [0x0225F], 'characters': '\u225F' },
+ '&equiv;': { 'codepoints': [0x02261], 'characters': '\u2261' },
+ '&equivDD;': { 'codepoints': [0x02A78], 'characters': '\u2A78' },
+ '&eqvparsl;': { 'codepoints': [0x029E5], 'characters': '\u29E5' },
+ '&erDot;': { 'codepoints': [0x02253], 'characters': '\u2253' },
+ '&erarr;': { 'codepoints': [0x02971], 'characters': '\u2971' },
+ '&escr;': { 'codepoints': [0x0212F], 'characters': '\u212F' },
+ '&esdot;': { 'codepoints': [0x02250], 'characters': '\u2250' },
+ '&esim;': { 'codepoints': [0x02242], 'characters': '\u2242' },
+ '&eta;': { 'codepoints': [0x003B7], 'characters': '\u03B7' },
+ '&eth': { 'codepoints': [0x000F0], 'characters': '\xF0' },
+ '&eth;': { 'codepoints': [0x000F0], 'characters': '\xF0' },
+ '&euml': { 'codepoints': [0x000EB], 'characters': '\xEB' },
+ '&euml;': { 'codepoints': [0x000EB], 'characters': '\xEB' },
+ '&euro;': { 'codepoints': [0x020AC], 'characters': '\u20AC' },
+ '&excl;': { 'codepoints': [0x00021], 'characters': '\x21' },
+ '&exist;': { 'codepoints': [0x02203], 'characters': '\u2203' },
+ '&expectation;': { 'codepoints': [0x02130], 'characters': '\u2130' },
+ '&exponentiale;': { 'codepoints': [0x02147], 'characters': '\u2147' },
+ '&fallingdotseq;': { 'codepoints': [0x02252], 'characters': '\u2252' },
+ '&fcy;': { 'codepoints': [0x00444], 'characters': '\u0444' },
+ '&female;': { 'codepoints': [0x02640], 'characters': '\u2640' },
+ '&ffilig;': { 'codepoints': [0x0FB03], 'characters': '\uFB03' },
+ '&fflig;': { 'codepoints': [0x0FB00], 'characters': '\uFB00' },
+ '&ffllig;': { 'codepoints': [0x0FB04], 'characters': '\uFB04' },
+ '&ffr;': { 'codepoints': [0x1D523], 'characters': '\uD835\uDD23' },
+ '&filig;': { 'codepoints': [0x0FB01], 'characters': '\uFB01' },
+ '&fjlig;': { 'codepoints': [0x00066, 0x0006A], 'characters': '\x66\x6A' },
+ '&flat;': { 'codepoints': [0x0266D], 'characters': '\u266D' },
+ '&fllig;': { 'codepoints': [0x0FB02], 'characters': '\uFB02' },
+ '&fltns;': { 'codepoints': [0x025B1], 'characters': '\u25B1' },
+ '&fnof;': { 'codepoints': [0x00192], 'characters': '\u0192' },
+ '&fopf;': { 'codepoints': [0x1D557], 'characters': '\uD835\uDD57' },
+ '&forall;': { 'codepoints': [0x02200], 'characters': '\u2200' },
+ '&fork;': { 'codepoints': [0x022D4], 'characters': '\u22D4' },
+ '&forkv;': { 'codepoints': [0x02AD9], 'characters': '\u2AD9' },
+ '&fpartint;': { 'codepoints': [0x02A0D], 'characters': '\u2A0D' },
+ '&frac12': { 'codepoints': [0x000BD], 'characters': '\xBD' },
+ '&frac12;': { 'codepoints': [0x000BD], 'characters': '\xBD' },
+ '&frac13;': { 'codepoints': [0x02153], 'characters': '\u2153' },
+ '&frac14': { 'codepoints': [0x000BC], 'characters': '\xBC' },
+ '&frac14;': { 'codepoints': [0x000BC], 'characters': '\xBC' },
+ '&frac15;': { 'codepoints': [0x02155], 'characters': '\u2155' },
+ '&frac16;': { 'codepoints': [0x02159], 'characters': '\u2159' },
+ '&frac18;': { 'codepoints': [0x0215B], 'characters': '\u215B' },
+ '&frac23;': { 'codepoints': [0x02154], 'characters': '\u2154' },
+ '&frac25;': { 'codepoints': [0x02156], 'characters': '\u2156' },
+ '&frac34': { 'codepoints': [0x000BE], 'characters': '\xBE' },
+ '&frac34;': { 'codepoints': [0x000BE], 'characters': '\xBE' },
+ '&frac35;': { 'codepoints': [0x02157], 'characters': '\u2157' },
+ '&frac38;': { 'codepoints': [0x0215C], 'characters': '\u215C' },
+ '&frac45;': { 'codepoints': [0x02158], 'characters': '\u2158' },
+ '&frac56;': { 'codepoints': [0x0215A], 'characters': '\u215A' },
+ '&frac58;': { 'codepoints': [0x0215D], 'characters': '\u215D' },
+ '&frac78;': { 'codepoints': [0x0215E], 'characters': '\u215E' },
+ '&frasl;': { 'codepoints': [0x02044], 'characters': '\u2044' },
+ '&frown;': { 'codepoints': [0x02322], 'characters': '\u2322' },
+ '&fscr;': { 'codepoints': [0x1D4BB], 'characters': '\uD835\uDCBB' },
+ '&gE;': { 'codepoints': [0x02267], 'characters': '\u2267' },
+ '&gEl;': { 'codepoints': [0x02A8C], 'characters': '\u2A8C' },
+ '&gacute;': { 'codepoints': [0x001F5], 'characters': '\u01F5' },
+ '&gamma;': { 'codepoints': [0x003B3], 'characters': '\u03B3' },
+ '&gammad;': { 'codepoints': [0x003DD], 'characters': '\u03DD' },
+ '&gap;': { 'codepoints': [0x02A86], 'characters': '\u2A86' },
+ '&gbreve;': { 'codepoints': [0x0011F], 'characters': '\u011F' },
+ '&gcirc;': { 'codepoints': [0x0011D], 'characters': '\u011D' },
+ '&gcy;': { 'codepoints': [0x00433], 'characters': '\u0433' },
+ '&gdot;': { 'codepoints': [0x00121], 'characters': '\u0121' },
+ '&ge;': { 'codepoints': [0x02265], 'characters': '\u2265' },
+ '&gel;': { 'codepoints': [0x022DB], 'characters': '\u22DB' },
+ '&geq;': { 'codepoints': [0x02265], 'characters': '\u2265' },
+ '&geqq;': { 'codepoints': [0x02267], 'characters': '\u2267' },
+ '&geqslant;': { 'codepoints': [0x02A7E], 'characters': '\u2A7E' },
+ '&ges;': { 'codepoints': [0x02A7E], 'characters': '\u2A7E' },
+ '&gescc;': { 'codepoints': [0x02AA9], 'characters': '\u2AA9' },
+ '&gesdot;': { 'codepoints': [0x02A80], 'characters': '\u2A80' },
+ '&gesdoto;': { 'codepoints': [0x02A82], 'characters': '\u2A82' },
+ '&gesdotol;': { 'codepoints': [0x02A84], 'characters': '\u2A84' },
+ '&gesl;': { 'codepoints': [0x022DB, 0x0FE00], 'characters': '\u22DB\uFE00' },
+ '&gesles;': { 'codepoints': [0x02A94], 'characters': '\u2A94' },
+ '&gfr;': { 'codepoints': [0x1D524], 'characters': '\uD835\uDD24' },
+ '&gg;': { 'codepoints': [0x0226B], 'characters': '\u226B' },
+ '&ggg;': { 'codepoints': [0x022D9], 'characters': '\u22D9' },
+ '&gimel;': { 'codepoints': [0x02137], 'characters': '\u2137' },
+ '&gjcy;': { 'codepoints': [0x00453], 'characters': '\u0453' },
+ '&gl;': { 'codepoints': [0x02277], 'characters': '\u2277' },
+ '&glE;': { 'codepoints': [0x02A92], 'characters': '\u2A92' },
+ '&gla;': { 'codepoints': [0x02AA5], 'characters': '\u2AA5' },
+ '&glj;': { 'codepoints': [0x02AA4], 'characters': '\u2AA4' },
+ '&gnE;': { 'codepoints': [0x02269], 'characters': '\u2269' },
+ '&gnap;': { 'codepoints': [0x02A8A], 'characters': '\u2A8A' },
+ '&gnapprox;': { 'codepoints': [0x02A8A], 'characters': '\u2A8A' },
+ '&gne;': { 'codepoints': [0x02A88], 'characters': '\u2A88' },
+ '&gneq;': { 'codepoints': [0x02A88], 'characters': '\u2A88' },
+ '&gneqq;': { 'codepoints': [0x02269], 'characters': '\u2269' },
+ '&gnsim;': { 'codepoints': [0x022E7], 'characters': '\u22E7' },
+ '&gopf;': { 'codepoints': [0x1D558], 'characters': '\uD835\uDD58' },
+ '&grave;': { 'codepoints': [0x00060], 'characters': '\x60' },
+ '&gscr;': { 'codepoints': [0x0210A], 'characters': '\u210A' },
+ '&gsim;': { 'codepoints': [0x02273], 'characters': '\u2273' },
+ '&gsime;': { 'codepoints': [0x02A8E], 'characters': '\u2A8E' },
+ '&gsiml;': { 'codepoints': [0x02A90], 'characters': '\u2A90' },
+ '&gt': { 'codepoints': [0x0003E], 'characters': '\x3E' },
+ '&gt;': { 'codepoints': [0x0003E], 'characters': '\x3E' },
+ '&gtcc;': { 'codepoints': [0x02AA7], 'characters': '\u2AA7' },
+ '&gtcir;': { 'codepoints': [0x02A7A], 'characters': '\u2A7A' },
+ '&gtdot;': { 'codepoints': [0x022D7], 'characters': '\u22D7' },
+ '&gtlPar;': { 'codepoints': [0x02995], 'characters': '\u2995' },
+ '&gtquest;': { 'codepoints': [0x02A7C], 'characters': '\u2A7C' },
+ '&gtrapprox;': { 'codepoints': [0x02A86], 'characters': '\u2A86' },
+ '&gtrarr;': { 'codepoints': [0x02978], 'characters': '\u2978' },
+ '&gtrdot;': { 'codepoints': [0x022D7], 'characters': '\u22D7' },
+ '&gtreqless;': { 'codepoints': [0x022DB], 'characters': '\u22DB' },
+ '&gtreqqless;': { 'codepoints': [0x02A8C], 'characters': '\u2A8C' },
+ '&gtrless;': { 'codepoints': [0x02277], 'characters': '\u2277' },
+ '&gtrsim;': { 'codepoints': [0x02273], 'characters': '\u2273' },
+ '&gvertneqq;': { 'codepoints': [0x02269, 0x0FE00], 'characters': '\u2269\uFE00' },
+ '&gvnE;': { 'codepoints': [0x02269, 0x0FE00], 'characters': '\u2269\uFE00' },
+ '&hArr;': { 'codepoints': [0x021D4], 'characters': '\u21D4' },
+ '&hairsp;': { 'codepoints': [0x0200A], 'characters': '\u200A' },
+ '&half;': { 'codepoints': [0x000BD], 'characters': '\xBD' },
+ '&hamilt;': { 'codepoints': [0x0210B], 'characters': '\u210B' },
+ '&hardcy;': { 'codepoints': [0x0044A], 'characters': '\u044A' },
+ '&harr;': { 'codepoints': [0x02194], 'characters': '\u2194' },
+ '&harrcir;': { 'codepoints': [0x02948], 'characters': '\u2948' },
+ '&harrw;': { 'codepoints': [0x021AD], 'characters': '\u21AD' },
+ '&hbar;': { 'codepoints': [0x0210F], 'characters': '\u210F' },
+ '&hcirc;': { 'codepoints': [0x00125], 'characters': '\u0125' },
+ '&hearts;': { 'codepoints': [0x02665], 'characters': '\u2665' },
+ '&heartsuit;': { 'codepoints': [0x02665], 'characters': '\u2665' },
+ '&hellip;': { 'codepoints': [0x02026], 'characters': '\u2026' },
+ '&hercon;': { 'codepoints': [0x022B9], 'characters': '\u22B9' },
+ '&hfr;': { 'codepoints': [0x1D525], 'characters': '\uD835\uDD25' },
+ '&hksearow;': { 'codepoints': [0x02925], 'characters': '\u2925' },
+ '&hkswarow;': { 'codepoints': [0x02926], 'characters': '\u2926' },
+ '&hoarr;': { 'codepoints': [0x021FF], 'characters': '\u21FF' },
+ '&homtht;': { 'codepoints': [0x0223B], 'characters': '\u223B' },
+ '&hookleftarrow;': { 'codepoints': [0x021A9], 'characters': '\u21A9' },
+ '&hookrightarrow;': { 'codepoints': [0x021AA], 'characters': '\u21AA' },
+ '&hopf;': { 'codepoints': [0x1D559], 'characters': '\uD835\uDD59' },
+ '&horbar;': { 'codepoints': [0x02015], 'characters': '\u2015' },
+ '&hscr;': { 'codepoints': [0x1D4BD], 'characters': '\uD835\uDCBD' },
+ '&hslash;': { 'codepoints': [0x0210F], 'characters': '\u210F' },
+ '&hstrok;': { 'codepoints': [0x00127], 'characters': '\u0127' },
+ '&hybull;': { 'codepoints': [0x02043], 'characters': '\u2043' },
+ '&hyphen;': { 'codepoints': [0x02010], 'characters': '\u2010' },
+ '&iacute': { 'codepoints': [0x000ED], 'characters': '\xED' },
+ '&iacute;': { 'codepoints': [0x000ED], 'characters': '\xED' },
+ '&ic;': { 'codepoints': [0x02063], 'characters': '\u2063' },
+ '&icirc': { 'codepoints': [0x000EE], 'characters': '\xEE' },
+ '&icirc;': { 'codepoints': [0x000EE], 'characters': '\xEE' },
+ '&icy;': { 'codepoints': [0x00438], 'characters': '\u0438' },
+ '&iecy;': { 'codepoints': [0x00435], 'characters': '\u0435' },
+ '&iexcl': { 'codepoints': [0x000A1], 'characters': '\xA1' },
+ '&iexcl;': { 'codepoints': [0x000A1], 'characters': '\xA1' },
+ '&iff;': { 'codepoints': [0x021D4], 'characters': '\u21D4' },
+ '&ifr;': { 'codepoints': [0x1D526], 'characters': '\uD835\uDD26' },
+ '&igrave': { 'codepoints': [0x000EC], 'characters': '\xEC' },
+ '&igrave;': { 'codepoints': [0x000EC], 'characters': '\xEC' },
+ '&ii;': { 'codepoints': [0x02148], 'characters': '\u2148' },
+ '&iiiint;': { 'codepoints': [0x02A0C], 'characters': '\u2A0C' },
+ '&iiint;': { 'codepoints': [0x0222D], 'characters': '\u222D' },
+ '&iinfin;': { 'codepoints': [0x029DC], 'characters': '\u29DC' },
+ '&iiota;': { 'codepoints': [0x02129], 'characters': '\u2129' },
+ '&ijlig;': { 'codepoints': [0x00133], 'characters': '\u0133' },
+ '&imacr;': { 'codepoints': [0x0012B], 'characters': '\u012B' },
+ '&image;': { 'codepoints': [0x02111], 'characters': '\u2111' },
+ '&imagline;': { 'codepoints': [0x02110], 'characters': '\u2110' },
+ '&imagpart;': { 'codepoints': [0x02111], 'characters': '\u2111' },
+ '&imath;': { 'codepoints': [0x00131], 'characters': '\u0131' },
+ '&imof;': { 'codepoints': [0x022B7], 'characters': '\u22B7' },
+ '&imped;': { 'codepoints': [0x001B5], 'characters': '\u01B5' },
+ '&in;': { 'codepoints': [0x02208], 'characters': '\u2208' },
+ '&incare;': { 'codepoints': [0x02105], 'characters': '\u2105' },
+ '&infin;': { 'codepoints': [0x0221E], 'characters': '\u221E' },
+ '&infintie;': { 'codepoints': [0x029DD], 'characters': '\u29DD' },
+ '&inodot;': { 'codepoints': [0x00131], 'characters': '\u0131' },
+ '&int;': { 'codepoints': [0x0222B], 'characters': '\u222B' },
+ '&intcal;': { 'codepoints': [0x022BA], 'characters': '\u22BA' },
+ '&integers;': { 'codepoints': [0x02124], 'characters': '\u2124' },
+ '&intercal;': { 'codepoints': [0x022BA], 'characters': '\u22BA' },
+ '&intlarhk;': { 'codepoints': [0x02A17], 'characters': '\u2A17' },
+ '&intprod;': { 'codepoints': [0x02A3C], 'characters': '\u2A3C' },
+ '&iocy;': { 'codepoints': [0x00451], 'characters': '\u0451' },
+ '&iogon;': { 'codepoints': [0x0012F], 'characters': '\u012F' },
+ '&iopf;': { 'codepoints': [0x1D55A], 'characters': '\uD835\uDD5A' },
+ '&iota;': { 'codepoints': [0x003B9], 'characters': '\u03B9' },
+ '&iprod;': { 'codepoints': [0x02A3C], 'characters': '\u2A3C' },
+ '&iquest': { 'codepoints': [0x000BF], 'characters': '\xBF' },
+ '&iquest;': { 'codepoints': [0x000BF], 'characters': '\xBF' },
+ '&iscr;': { 'codepoints': [0x1D4BE], 'characters': '\uD835\uDCBE' },
+ '&isin;': { 'codepoints': [0x02208], 'characters': '\u2208' },
+ '&isinE;': { 'codepoints': [0x022F9], 'characters': '\u22F9' },
+ '&isindot;': { 'codepoints': [0x022F5], 'characters': '\u22F5' },
+ '&isins;': { 'codepoints': [0x022F4], 'characters': '\u22F4' },
+ '&isinsv;': { 'codepoints': [0x022F3], 'characters': '\u22F3' },
+ '&isinv;': { 'codepoints': [0x02208], 'characters': '\u2208' },
+ '&it;': { 'codepoints': [0x02062], 'characters': '\u2062' },
+ '&itilde;': { 'codepoints': [0x00129], 'characters': '\u0129' },
+ '&iukcy;': { 'codepoints': [0x00456], 'characters': '\u0456' },
+ '&iuml': { 'codepoints': [0x000EF], 'characters': '\xEF' },
+ '&iuml;': { 'codepoints': [0x000EF], 'characters': '\xEF' },
+ '&jcirc;': { 'codepoints': [0x00135], 'characters': '\u0135' },
+ '&jcy;': { 'codepoints': [0x00439], 'characters': '\u0439' },
+ '&jfr;': { 'codepoints': [0x1D527], 'characters': '\uD835\uDD27' },
+ '&jmath;': { 'codepoints': [0x00237], 'characters': '\u0237' },
+ '&jopf;': { 'codepoints': [0x1D55B], 'characters': '\uD835\uDD5B' },
+ '&jscr;': { 'codepoints': [0x1D4BF], 'characters': '\uD835\uDCBF' },
+ '&jsercy;': { 'codepoints': [0x00458], 'characters': '\u0458' },
+ '&jukcy;': { 'codepoints': [0x00454], 'characters': '\u0454' },
+ '&kappa;': { 'codepoints': [0x003BA], 'characters': '\u03BA' },
+ '&kappav;': { 'codepoints': [0x003F0], 'characters': '\u03F0' },
+ '&kcedil;': { 'codepoints': [0x00137], 'characters': '\u0137' },
+ '&kcy;': { 'codepoints': [0x0043A], 'characters': '\u043A' },
+ '&kfr;': { 'codepoints': [0x1D528], 'characters': '\uD835\uDD28' },
+ '&kgreen;': { 'codepoints': [0x00138], 'characters': '\u0138' },
+ '&khcy;': { 'codepoints': [0x00445], 'characters': '\u0445' },
+ '&kjcy;': { 'codepoints': [0x0045C], 'characters': '\u045C' },
+ '&kopf;': { 'codepoints': [0x1D55C], 'characters': '\uD835\uDD5C' },
+ '&kscr;': { 'codepoints': [0x1D4C0], 'characters': '\uD835\uDCC0' },
+ '&lAarr;': { 'codepoints': [0x021DA], 'characters': '\u21DA' },
+ '&lArr;': { 'codepoints': [0x021D0], 'characters': '\u21D0' },
+ '&lAtail;': { 'codepoints': [0x0291B], 'characters': '\u291B' },
+ '&lBarr;': { 'codepoints': [0x0290E], 'characters': '\u290E' },
+ '&lE;': { 'codepoints': [0x02266], 'characters': '\u2266' },
+ '&lEg;': { 'codepoints': [0x02A8B], 'characters': '\u2A8B' },
+ '&lHar;': { 'codepoints': [0x02962], 'characters': '\u2962' },
+ '&lacute;': { 'codepoints': [0x0013A], 'characters': '\u013A' },
+ '&laemptyv;': { 'codepoints': [0x029B4], 'characters': '\u29B4' },
+ '&lagran;': { 'codepoints': [0x02112], 'characters': '\u2112' },
+ '&lambda;': { 'codepoints': [0x003BB], 'characters': '\u03BB' },
+ '&lang;': { 'codepoints': [0x027E8], 'characters': '\u27E8' },
+ '&langd;': { 'codepoints': [0x02991], 'characters': '\u2991' },
+ '&langle;': { 'codepoints': [0x027E8], 'characters': '\u27E8' },
+ '&lap;': { 'codepoints': [0x02A85], 'characters': '\u2A85' },
+ '&laquo': { 'codepoints': [0x000AB], 'characters': '\xAB' },
+ '&laquo;': { 'codepoints': [0x000AB], 'characters': '\xAB' },
+ '&larr;': { 'codepoints': [0x02190], 'characters': '\u2190' },
+ '&larrb;': { 'codepoints': [0x021E4], 'characters': '\u21E4' },
+ '&larrbfs;': { 'codepoints': [0x0291F], 'characters': '\u291F' },
+ '&larrfs;': { 'codepoints': [0x0291D], 'characters': '\u291D' },
+ '&larrhk;': { 'codepoints': [0x021A9], 'characters': '\u21A9' },
+ '&larrlp;': { 'codepoints': [0x021AB], 'characters': '\u21AB' },
+ '&larrpl;': { 'codepoints': [0x02939], 'characters': '\u2939' },
+ '&larrsim;': { 'codepoints': [0x02973], 'characters': '\u2973' },
+ '&larrtl;': { 'codepoints': [0x021A2], 'characters': '\u21A2' },
+ '&lat;': { 'codepoints': [0x02AAB], 'characters': '\u2AAB' },
+ '&latail;': { 'codepoints': [0x02919], 'characters': '\u2919' },
+ '&late;': { 'codepoints': [0x02AAD], 'characters': '\u2AAD' },
+ '&lates;': { 'codepoints': [0x02AAD, 0x0FE00], 'characters': '\u2AAD\uFE00' },
+ '&lbarr;': { 'codepoints': [0x0290C], 'characters': '\u290C' },
+ '&lbbrk;': { 'codepoints': [0x02772], 'characters': '\u2772' },
+ '&lbrace;': { 'codepoints': [0x0007B], 'characters': '\x7B' },
+ '&lbrack;': { 'codepoints': [0x0005B], 'characters': '\x5B' },
+ '&lbrke;': { 'codepoints': [0x0298B], 'characters': '\u298B' },
+ '&lbrksld;': { 'codepoints': [0x0298F], 'characters': '\u298F' },
+ '&lbrkslu;': { 'codepoints': [0x0298D], 'characters': '\u298D' },
+ '&lcaron;': { 'codepoints': [0x0013E], 'characters': '\u013E' },
+ '&lcedil;': { 'codepoints': [0x0013C], 'characters': '\u013C' },
+ '&lceil;': { 'codepoints': [0x02308], 'characters': '\u2308' },
+ '&lcub;': { 'codepoints': [0x0007B], 'characters': '\x7B' },
+ '&lcy;': { 'codepoints': [0x0043B], 'characters': '\u043B' },
+ '&ldca;': { 'codepoints': [0x02936], 'characters': '\u2936' },
+ '&ldquo;': { 'codepoints': [0x0201C], 'characters': '\u201C' },
+ '&ldquor;': { 'codepoints': [0x0201E], 'characters': '\u201E' },
+ '&ldrdhar;': { 'codepoints': [0x02967], 'characters': '\u2967' },
+ '&ldrushar;': { 'codepoints': [0x0294B], 'characters': '\u294B' },
+ '&ldsh;': { 'codepoints': [0x021B2], 'characters': '\u21B2' },
+ '&le;': { 'codepoints': [0x02264], 'characters': '\u2264' },
+ '&leftarrow;': { 'codepoints': [0x02190], 'characters': '\u2190' },
+ '&leftarrowtail;': { 'codepoints': [0x021A2], 'characters': '\u21A2' },
+ '&leftharpoondown;': { 'codepoints': [0x021BD], 'characters': '\u21BD' },
+ '&leftharpoonup;': { 'codepoints': [0x021BC], 'characters': '\u21BC' },
+ '&leftleftarrows;': { 'codepoints': [0x021C7], 'characters': '\u21C7' },
+ '&leftrightarrow;': { 'codepoints': [0x02194], 'characters': '\u2194' },
+ '&leftrightarrows;': { 'codepoints': [0x021C6], 'characters': '\u21C6' },
+ '&leftrightharpoons;': { 'codepoints': [0x021CB], 'characters': '\u21CB' },
+ '&leftrightsquigarrow;': { 'codepoints': [0x021AD], 'characters': '\u21AD' },
+ '&leftthreetimes;': { 'codepoints': [0x022CB], 'characters': '\u22CB' },
+ '&leg;': { 'codepoints': [0x022DA], 'characters': '\u22DA' },
+ '&leq;': { 'codepoints': [0x02264], 'characters': '\u2264' },
+ '&leqq;': { 'codepoints': [0x02266], 'characters': '\u2266' },
+ '&leqslant;': { 'codepoints': [0x02A7D], 'characters': '\u2A7D' },
+ '&les;': { 'codepoints': [0x02A7D], 'characters': '\u2A7D' },
+ '&lescc;': { 'codepoints': [0x02AA8], 'characters': '\u2AA8' },
+ '&lesdot;': { 'codepoints': [0x02A7F], 'characters': '\u2A7F' },
+ '&lesdoto;': { 'codepoints': [0x02A81], 'characters': '\u2A81' },
+ '&lesdotor;': { 'codepoints': [0x02A83], 'characters': '\u2A83' },
+ '&lesg;': { 'codepoints': [0x022DA, 0x0FE00], 'characters': '\u22DA\uFE00' },
+ '&lesges;': { 'codepoints': [0x02A93], 'characters': '\u2A93' },
+ '&lessapprox;': { 'codepoints': [0x02A85], 'characters': '\u2A85' },
+ '&lessdot;': { 'codepoints': [0x022D6], 'characters': '\u22D6' },
+ '&lesseqgtr;': { 'codepoints': [0x022DA], 'characters': '\u22DA' },
+ '&lesseqqgtr;': { 'codepoints': [0x02A8B], 'characters': '\u2A8B' },
+ '&lessgtr;': { 'codepoints': [0x02276], 'characters': '\u2276' },
+ '&lesssim;': { 'codepoints': [0x02272], 'characters': '\u2272' },
+ '&lfisht;': { 'codepoints': [0x0297C], 'characters': '\u297C' },
+ '&lfloor;': { 'codepoints': [0x0230A], 'characters': '\u230A' },
+ '&lfr;': { 'codepoints': [0x1D529], 'characters': '\uD835\uDD29' },
+ '&lg;': { 'codepoints': [0x02276], 'characters': '\u2276' },
+ '&lgE;': { 'codepoints': [0x02A91], 'characters': '\u2A91' },
+ '&lhard;': { 'codepoints': [0x021BD], 'characters': '\u21BD' },
+ '&lharu;': { 'codepoints': [0x021BC], 'characters': '\u21BC' },
+ '&lharul;': { 'codepoints': [0x0296A], 'characters': '\u296A' },
+ '&lhblk;': { 'codepoints': [0x02584], 'characters': '\u2584' },
+ '&ljcy;': { 'codepoints': [0x00459], 'characters': '\u0459' },
+ '&ll;': { 'codepoints': [0x0226A], 'characters': '\u226A' },
+ '&llarr;': { 'codepoints': [0x021C7], 'characters': '\u21C7' },
+ '&llcorner;': { 'codepoints': [0x0231E], 'characters': '\u231E' },
+ '&llhard;': { 'codepoints': [0x0296B], 'characters': '\u296B' },
+ '&lltri;': { 'codepoints': [0x025FA], 'characters': '\u25FA' },
+ '&lmidot;': { 'codepoints': [0x00140], 'characters': '\u0140' },
+ '&lmoust;': { 'codepoints': [0x023B0], 'characters': '\u23B0' },
+ '&lmoustache;': { 'codepoints': [0x023B0], 'characters': '\u23B0' },
+ '&lnE;': { 'codepoints': [0x02268], 'characters': '\u2268' },
+ '&lnap;': { 'codepoints': [0x02A89], 'characters': '\u2A89' },
+ '&lnapprox;': { 'codepoints': [0x02A89], 'characters': '\u2A89' },
+ '&lne;': { 'codepoints': [0x02A87], 'characters': '\u2A87' },
+ '&lneq;': { 'codepoints': [0x02A87], 'characters': '\u2A87' },
+ '&lneqq;': { 'codepoints': [0x02268], 'characters': '\u2268' },
+ '&lnsim;': { 'codepoints': [0x022E6], 'characters': '\u22E6' },
+ '&loang;': { 'codepoints': [0x027EC], 'characters': '\u27EC' },
+ '&loarr;': { 'codepoints': [0x021FD], 'characters': '\u21FD' },
+ '&lobrk;': { 'codepoints': [0x027E6], 'characters': '\u27E6' },
+ '&longleftarrow;': { 'codepoints': [0x027F5], 'characters': '\u27F5' },
+ '&longleftrightarrow;': { 'codepoints': [0x027F7], 'characters': '\u27F7' },
+ '&longmapsto;': { 'codepoints': [0x027FC], 'characters': '\u27FC' },
+ '&longrightarrow;': { 'codepoints': [0x027F6], 'characters': '\u27F6' },
+ '&looparrowleft;': { 'codepoints': [0x021AB], 'characters': '\u21AB' },
+ '&looparrowright;': { 'codepoints': [0x021AC], 'characters': '\u21AC' },
+ '&lopar;': { 'codepoints': [0x02985], 'characters': '\u2985' },
+ '&lopf;': { 'codepoints': [0x1D55D], 'characters': '\uD835\uDD5D' },
+ '&loplus;': { 'codepoints': [0x02A2D], 'characters': '\u2A2D' },
+ '&lotimes;': { 'codepoints': [0x02A34], 'characters': '\u2A34' },
+ '&lowast;': { 'codepoints': [0x02217], 'characters': '\u2217' },
+ '&lowbar;': { 'codepoints': [0x0005F], 'characters': '\x5F' },
+ '&loz;': { 'codepoints': [0x025CA], 'characters': '\u25CA' },
+ '&lozenge;': { 'codepoints': [0x025CA], 'characters': '\u25CA' },
+ '&lozf;': { 'codepoints': [0x029EB], 'characters': '\u29EB' },
+ '&lpar;': { 'codepoints': [0x00028], 'characters': '\x28' },
+ '&lparlt;': { 'codepoints': [0x02993], 'characters': '\u2993' },
+ '&lrarr;': { 'codepoints': [0x021C6], 'characters': '\u21C6' },
+ '&lrcorner;': { 'codepoints': [0x0231F], 'characters': '\u231F' },
+ '&lrhar;': { 'codepoints': [0x021CB], 'characters': '\u21CB' },
+ '&lrhard;': { 'codepoints': [0x0296D], 'characters': '\u296D' },
+ '&lrm;': { 'codepoints': [0x0200E], 'characters': '\u200E' },
+ '&lrtri;': { 'codepoints': [0x022BF], 'characters': '\u22BF' },
+ '&lsaquo;': { 'codepoints': [0x02039], 'characters': '\u2039' },
+ '&lscr;': { 'codepoints': [0x1D4C1], 'characters': '\uD835\uDCC1' },
+ '&lsh;': { 'codepoints': [0x021B0], 'characters': '\u21B0' },
+ '&lsim;': { 'codepoints': [0x02272], 'characters': '\u2272' },
+ '&lsime;': { 'codepoints': [0x02A8D], 'characters': '\u2A8D' },
+ '&lsimg;': { 'codepoints': [0x02A8F], 'characters': '\u2A8F' },
+ '&lsqb;': { 'codepoints': [0x0005B], 'characters': '\x5B' },
+ '&lsquo;': { 'codepoints': [0x02018], 'characters': '\u2018' },
+ '&lsquor;': { 'codepoints': [0x0201A], 'characters': '\u201A' },
+ '&lstrok;': { 'codepoints': [0x00142], 'characters': '\u0142' },
+ '&lt': { 'codepoints': [0x0003C], 'characters': '\x3C' },
+ '&lt;': { 'codepoints': [0x0003C], 'characters': '\x3C' },
+ '&ltcc;': { 'codepoints': [0x02AA6], 'characters': '\u2AA6' },
+ '&ltcir;': { 'codepoints': [0x02A79], 'characters': '\u2A79' },
+ '&ltdot;': { 'codepoints': [0x022D6], 'characters': '\u22D6' },
+ '&lthree;': { 'codepoints': [0x022CB], 'characters': '\u22CB' },
+ '&ltimes;': { 'codepoints': [0x022C9], 'characters': '\u22C9' },
+ '&ltlarr;': { 'codepoints': [0x02976], 'characters': '\u2976' },
+ '&ltquest;': { 'codepoints': [0x02A7B], 'characters': '\u2A7B' },
+ '&ltrPar;': { 'codepoints': [0x02996], 'characters': '\u2996' },
+ '&ltri;': { 'codepoints': [0x025C3], 'characters': '\u25C3' },
+ '&ltrie;': { 'codepoints': [0x022B4], 'characters': '\u22B4' },
+ '&ltrif;': { 'codepoints': [0x025C2], 'characters': '\u25C2' },
+ '&lurdshar;': { 'codepoints': [0x0294A], 'characters': '\u294A' },
+ '&luruhar;': { 'codepoints': [0x02966], 'characters': '\u2966' },
+ '&lvertneqq;': { 'codepoints': [0x02268, 0x0FE00], 'characters': '\u2268\uFE00' },
+ '&lvnE;': { 'codepoints': [0x02268, 0x0FE00], 'characters': '\u2268\uFE00' },
+ '&mDDot;': { 'codepoints': [0x0223A], 'characters': '\u223A' },
+ '&macr': { 'codepoints': [0x000AF], 'characters': '\xAF' },
+ '&macr;': { 'codepoints': [0x000AF], 'characters': '\xAF' },
+ '&male;': { 'codepoints': [0x02642], 'characters': '\u2642' },
+ '&malt;': { 'codepoints': [0x02720], 'characters': '\u2720' },
+ '&maltese;': { 'codepoints': [0x02720], 'characters': '\u2720' },
+ '&map;': { 'codepoints': [0x021A6], 'characters': '\u21A6' },
+ '&mapsto;': { 'codepoints': [0x021A6], 'characters': '\u21A6' },
+ '&mapstodown;': { 'codepoints': [0x021A7], 'characters': '\u21A7' },
+ '&mapstoleft;': { 'codepoints': [0x021A4], 'characters': '\u21A4' },
+ '&mapstoup;': { 'codepoints': [0x021A5], 'characters': '\u21A5' },
+ '&marker;': { 'codepoints': [0x025AE], 'characters': '\u25AE' },
+ '&mcomma;': { 'codepoints': [0x02A29], 'characters': '\u2A29' },
+ '&mcy;': { 'codepoints': [0x0043C], 'characters': '\u043C' },
+ '&mdash;': { 'codepoints': [0x02014], 'characters': '\u2014' },
+ '&measuredangle;': { 'codepoints': [0x02221], 'characters': '\u2221' },
+ '&mfr;': { 'codepoints': [0x1D52A], 'characters': '\uD835\uDD2A' },
+ '&mho;': { 'codepoints': [0x02127], 'characters': '\u2127' },
+ '&micro': { 'codepoints': [0x000B5], 'characters': '\xB5' },
+ '&micro;': { 'codepoints': [0x000B5], 'characters': '\xB5' },
+ '&mid;': { 'codepoints': [0x02223], 'characters': '\u2223' },
+ '&midast;': { 'codepoints': [0x0002A], 'characters': '\x2A' },
+ '&midcir;': { 'codepoints': [0x02AF0], 'characters': '\u2AF0' },
+ '&middot': { 'codepoints': [0x000B7], 'characters': '\xB7' },
+ '&middot;': { 'codepoints': [0x000B7], 'characters': '\xB7' },
+ '&minus;': { 'codepoints': [0x02212], 'characters': '\u2212' },
+ '&minusb;': { 'codepoints': [0x0229F], 'characters': '\u229F' },
+ '&minusd;': { 'codepoints': [0x02238], 'characters': '\u2238' },
+ '&minusdu;': { 'codepoints': [0x02A2A], 'characters': '\u2A2A' },
+ '&mlcp;': { 'codepoints': [0x02ADB], 'characters': '\u2ADB' },
+ '&mldr;': { 'codepoints': [0x02026], 'characters': '\u2026' },
+ '&mnplus;': { 'codepoints': [0x02213], 'characters': '\u2213' },
+ '&models;': { 'codepoints': [0x022A7], 'characters': '\u22A7' },
+ '&mopf;': { 'codepoints': [0x1D55E], 'characters': '\uD835\uDD5E' },
+ '&mp;': { 'codepoints': [0x02213], 'characters': '\u2213' },
+ '&mscr;': { 'codepoints': [0x1D4C2], 'characters': '\uD835\uDCC2' },
+ '&mstpos;': { 'codepoints': [0x0223E], 'characters': '\u223E' },
+ '&mu;': { 'codepoints': [0x003BC], 'characters': '\u03BC' },
+ '&multimap;': { 'codepoints': [0x022B8], 'characters': '\u22B8' },
+ '&mumap;': { 'codepoints': [0x022B8], 'characters': '\u22B8' },
+ '&nGg;': { 'codepoints': [0x022D9, 0x00338], 'characters': '\u22D9\u0338' },
+ '&nGt;': { 'codepoints': [0x0226B, 0x020D2], 'characters': '\u226B\u20D2' },
+ '&nGtv;': { 'codepoints': [0x0226B, 0x00338], 'characters': '\u226B\u0338' },
+ '&nLeftarrow;': { 'codepoints': [0x021CD], 'characters': '\u21CD' },
+ '&nLeftrightarrow;': { 'codepoints': [0x021CE], 'characters': '\u21CE' },
+ '&nLl;': { 'codepoints': [0x022D8, 0x00338], 'characters': '\u22D8\u0338' },
+ '&nLt;': { 'codepoints': [0x0226A, 0x020D2], 'characters': '\u226A\u20D2' },
+ '&nLtv;': { 'codepoints': [0x0226A, 0x00338], 'characters': '\u226A\u0338' },
+ '&nRightarrow;': { 'codepoints': [0x021CF], 'characters': '\u21CF' },
+ '&nVDash;': { 'codepoints': [0x022AF], 'characters': '\u22AF' },
+ '&nVdash;': { 'codepoints': [0x022AE], 'characters': '\u22AE' },
+ '&nabla;': { 'codepoints': [0x02207], 'characters': '\u2207' },
+ '&nacute;': { 'codepoints': [0x00144], 'characters': '\u0144' },
+ '&nang;': { 'codepoints': [0x02220, 0x020D2], 'characters': '\u2220\u20D2' },
+ '&nap;': { 'codepoints': [0x02249], 'characters': '\u2249' },
+ '&napE;': { 'codepoints': [0x02A70, 0x00338], 'characters': '\u2A70\u0338' },
+ '&napid;': { 'codepoints': [0x0224B, 0x00338], 'characters': '\u224B\u0338' },
+ '&napos;': { 'codepoints': [0x00149], 'characters': '\u0149' },
+ '&napprox;': { 'codepoints': [0x02249], 'characters': '\u2249' },
+ '&natur;': { 'codepoints': [0x0266E], 'characters': '\u266E' },
+ '&natural;': { 'codepoints': [0x0266E], 'characters': '\u266E' },
+ '&naturals;': { 'codepoints': [0x02115], 'characters': '\u2115' },
+ '&nbsp': { 'codepoints': [0x000A0], 'characters': '\xA0' },
+ '&nbsp;': { 'codepoints': [0x000A0], 'characters': '\xA0' },
+ '&nbump;': { 'codepoints': [0x0224E, 0x00338], 'characters': '\u224E\u0338' },
+ '&nbumpe;': { 'codepoints': [0x0224F, 0x00338], 'characters': '\u224F\u0338' },
+ '&ncap;': { 'codepoints': [0x02A43], 'characters': '\u2A43' },
+ '&ncaron;': { 'codepoints': [0x00148], 'characters': '\u0148' },
+ '&ncedil;': { 'codepoints': [0x00146], 'characters': '\u0146' },
+ '&ncong;': { 'codepoints': [0x02247], 'characters': '\u2247' },
+ '&ncongdot;': { 'codepoints': [0x02A6D, 0x00338], 'characters': '\u2A6D\u0338' },
+ '&ncup;': { 'codepoints': [0x02A42], 'characters': '\u2A42' },
+ '&ncy;': { 'codepoints': [0x0043D], 'characters': '\u043D' },
+ '&ndash;': { 'codepoints': [0x02013], 'characters': '\u2013' },
+ '&ne;': { 'codepoints': [0x02260], 'characters': '\u2260' },
+ '&neArr;': { 'codepoints': [0x021D7], 'characters': '\u21D7' },
+ '&nearhk;': { 'codepoints': [0x02924], 'characters': '\u2924' },
+ '&nearr;': { 'codepoints': [0x02197], 'characters': '\u2197' },
+ '&nearrow;': { 'codepoints': [0x02197], 'characters': '\u2197' },
+ '&nedot;': { 'codepoints': [0x02250, 0x00338], 'characters': '\u2250\u0338' },
+ '&nequiv;': { 'codepoints': [0x02262], 'characters': '\u2262' },
+ '&nesear;': { 'codepoints': [0x02928], 'characters': '\u2928' },
+ '&nesim;': { 'codepoints': [0x02242, 0x00338], 'characters': '\u2242\u0338' },
+ '&nexist;': { 'codepoints': [0x02204], 'characters': '\u2204' },
+ '&nexists;': { 'codepoints': [0x02204], 'characters': '\u2204' },
+ '&nfr;': { 'codepoints': [0x1D52B], 'characters': '\uD835\uDD2B' },
+ '&ngE;': { 'codepoints': [0x02267, 0x00338], 'characters': '\u2267\u0338' },
+ '&nge;': { 'codepoints': [0x02271], 'characters': '\u2271' },
+ '&ngeq;': { 'codepoints': [0x02271], 'characters': '\u2271' },
+ '&ngeqq;': { 'codepoints': [0x02267, 0x00338], 'characters': '\u2267\u0338' },
+ '&ngeqslant;': { 'codepoints': [0x02A7E, 0x00338], 'characters': '\u2A7E\u0338' },
+ '&nges;': { 'codepoints': [0x02A7E, 0x00338], 'characters': '\u2A7E\u0338' },
+ '&ngsim;': { 'codepoints': [0x02275], 'characters': '\u2275' },
+ '&ngt;': { 'codepoints': [0x0226F], 'characters': '\u226F' },
+ '&ngtr;': { 'codepoints': [0x0226F], 'characters': '\u226F' },
+ '&nhArr;': { 'codepoints': [0x021CE], 'characters': '\u21CE' },
+ '&nharr;': { 'codepoints': [0x021AE], 'characters': '\u21AE' },
+ '&nhpar;': { 'codepoints': [0x02AF2], 'characters': '\u2AF2' },
+ '&ni;': { 'codepoints': [0x0220B], 'characters': '\u220B' },
+ '&nis;': { 'codepoints': [0x022FC], 'characters': '\u22FC' },
+ '&nisd;': { 'codepoints': [0x022FA], 'characters': '\u22FA' },
+ '&niv;': { 'codepoints': [0x0220B], 'characters': '\u220B' },
+ '&njcy;': { 'codepoints': [0x0045A], 'characters': '\u045A' },
+ '&nlArr;': { 'codepoints': [0x021CD], 'characters': '\u21CD' },
+ '&nlE;': { 'codepoints': [0x02266, 0x00338], 'characters': '\u2266\u0338' },
+ '&nlarr;': { 'codepoints': [0x0219A], 'characters': '\u219A' },
+ '&nldr;': { 'codepoints': [0x02025], 'characters': '\u2025' },
+ '&nle;': { 'codepoints': [0x02270], 'characters': '\u2270' },
+ '&nleftarrow;': { 'codepoints': [0x0219A], 'characters': '\u219A' },
+ '&nleftrightarrow;': { 'codepoints': [0x021AE], 'characters': '\u21AE' },
+ '&nleq;': { 'codepoints': [0x02270], 'characters': '\u2270' },
+ '&nleqq;': { 'codepoints': [0x02266, 0x00338], 'characters': '\u2266\u0338' },
+ '&nleqslant;': { 'codepoints': [0x02A7D, 0x00338], 'characters': '\u2A7D\u0338' },
+ '&nles;': { 'codepoints': [0x02A7D, 0x00338], 'characters': '\u2A7D\u0338' },
+ '&nless;': { 'codepoints': [0x0226E], 'characters': '\u226E' },
+ '&nlsim;': { 'codepoints': [0x02274], 'characters': '\u2274' },
+ '&nlt;': { 'codepoints': [0x0226E], 'characters': '\u226E' },
+ '&nltri;': { 'codepoints': [0x022EA], 'characters': '\u22EA' },
+ '&nltrie;': { 'codepoints': [0x022EC], 'characters': '\u22EC' },
+ '&nmid;': { 'codepoints': [0x02224], 'characters': '\u2224' },
+ '&nopf;': { 'codepoints': [0x1D55F], 'characters': '\uD835\uDD5F' },
+ '&not': { 'codepoints': [0x000AC], 'characters': '\xAC' },
+ '&not;': { 'codepoints': [0x000AC], 'characters': '\xAC' },
+ '&notin;': { 'codepoints': [0x02209], 'characters': '\u2209' },
+ '&notinE;': { 'codepoints': [0x022F9, 0x00338], 'characters': '\u22F9\u0338' },
+ '&notindot;': { 'codepoints': [0x022F5, 0x00338], 'characters': '\u22F5\u0338' },
+ '&notinva;': { 'codepoints': [0x02209], 'characters': '\u2209' },
+ '&notinvb;': { 'codepoints': [0x022F7], 'characters': '\u22F7' },
+ '&notinvc;': { 'codepoints': [0x022F6], 'characters': '\u22F6' },
+ '&notni;': { 'codepoints': [0x0220C], 'characters': '\u220C' },
+ '&notniva;': { 'codepoints': [0x0220C], 'characters': '\u220C' },
+ '&notnivb;': { 'codepoints': [0x022FE], 'characters': '\u22FE' },
+ '&notnivc;': { 'codepoints': [0x022FD], 'characters': '\u22FD' },
+ '&npar;': { 'codepoints': [0x02226], 'characters': '\u2226' },
+ '&nparallel;': { 'codepoints': [0x02226], 'characters': '\u2226' },
+ '&nparsl;': { 'codepoints': [0x02AFD, 0x020E5], 'characters': '\u2AFD\u20E5' },
+ '&npart;': { 'codepoints': [0x02202, 0x00338], 'characters': '\u2202\u0338' },
+ '&npolint;': { 'codepoints': [0x02A14], 'characters': '\u2A14' },
+ '&npr;': { 'codepoints': [0x02280], 'characters': '\u2280' },
+ '&nprcue;': { 'codepoints': [0x022E0], 'characters': '\u22E0' },
+ '&npre;': { 'codepoints': [0x02AAF, 0x00338], 'characters': '\u2AAF\u0338' },
+ '&nprec;': { 'codepoints': [0x02280], 'characters': '\u2280' },
+ '&npreceq;': { 'codepoints': [0x02AAF, 0x00338], 'characters': '\u2AAF\u0338' },
+ '&nrArr;': { 'codepoints': [0x021CF], 'characters': '\u21CF' },
+ '&nrarr;': { 'codepoints': [0x0219B], 'characters': '\u219B' },
+ '&nrarrc;': { 'codepoints': [0x02933, 0x00338], 'characters': '\u2933\u0338' },
+ '&nrarrw;': { 'codepoints': [0x0219D, 0x00338], 'characters': '\u219D\u0338' },
+ '&nrightarrow;': { 'codepoints': [0x0219B], 'characters': '\u219B' },
+ '&nrtri;': { 'codepoints': [0x022EB], 'characters': '\u22EB' },
+ '&nrtrie;': { 'codepoints': [0x022ED], 'characters': '\u22ED' },
+ '&nsc;': { 'codepoints': [0x02281], 'characters': '\u2281' },
+ '&nsccue;': { 'codepoints': [0x022E1], 'characters': '\u22E1' },
+ '&nsce;': { 'codepoints': [0x02AB0, 0x00338], 'characters': '\u2AB0\u0338' },
+ '&nscr;': { 'codepoints': [0x1D4C3], 'characters': '\uD835\uDCC3' },
+ '&nshortmid;': { 'codepoints': [0x02224], 'characters': '\u2224' },
+ '&nshortparallel;': { 'codepoints': [0x02226], 'characters': '\u2226' },
+ '&nsim;': { 'codepoints': [0x02241], 'characters': '\u2241' },
+ '&nsime;': { 'codepoints': [0x02244], 'characters': '\u2244' },
+ '&nsimeq;': { 'codepoints': [0x02244], 'characters': '\u2244' },
+ '&nsmid;': { 'codepoints': [0x02224], 'characters': '\u2224' },
+ '&nspar;': { 'codepoints': [0x02226], 'characters': '\u2226' },
+ '&nsqsube;': { 'codepoints': [0x022E2], 'characters': '\u22E2' },
+ '&nsqsupe;': { 'codepoints': [0x022E3], 'characters': '\u22E3' },
+ '&nsub;': { 'codepoints': [0x02284], 'characters': '\u2284' },
+ '&nsubE;': { 'codepoints': [0x02AC5, 0x00338], 'characters': '\u2AC5\u0338' },
+ '&nsube;': { 'codepoints': [0x02288], 'characters': '\u2288' },
+ '&nsubset;': { 'codepoints': [0x02282, 0x020D2], 'characters': '\u2282\u20D2' },
+ '&nsubseteq;': { 'codepoints': [0x02288], 'characters': '\u2288' },
+ '&nsubseteqq;': { 'codepoints': [0x02AC5, 0x00338], 'characters': '\u2AC5\u0338' },
+ '&nsucc;': { 'codepoints': [0x02281], 'characters': '\u2281' },
+ '&nsucceq;': { 'codepoints': [0x02AB0, 0x00338], 'characters': '\u2AB0\u0338' },
+ '&nsup;': { 'codepoints': [0x02285], 'characters': '\u2285' },
+ '&nsupE;': { 'codepoints': [0x02AC6, 0x00338], 'characters': '\u2AC6\u0338' },
+ '&nsupe;': { 'codepoints': [0x02289], 'characters': '\u2289' },
+ '&nsupset;': { 'codepoints': [0x02283, 0x020D2], 'characters': '\u2283\u20D2' },
+ '&nsupseteq;': { 'codepoints': [0x02289], 'characters': '\u2289' },
+ '&nsupseteqq;': { 'codepoints': [0x02AC6, 0x00338], 'characters': '\u2AC6\u0338' },
+ '&ntgl;': { 'codepoints': [0x02279], 'characters': '\u2279' },
+ '&ntilde': { 'codepoints': [0x000F1], 'characters': '\xF1' },
+ '&ntilde;': { 'codepoints': [0x000F1], 'characters': '\xF1' },
+ '&ntlg;': { 'codepoints': [0x02278], 'characters': '\u2278' },
+ '&ntriangleleft;': { 'codepoints': [0x022EA], 'characters': '\u22EA' },
+ '&ntrianglelefteq;': { 'codepoints': [0x022EC], 'characters': '\u22EC' },
+ '&ntriangleright;': { 'codepoints': [0x022EB], 'characters': '\u22EB' },
+ '&ntrianglerighteq;': { 'codepoints': [0x022ED], 'characters': '\u22ED' },
+ '&nu;': { 'codepoints': [0x003BD], 'characters': '\u03BD' },
+ '&num;': { 'codepoints': [0x00023], 'characters': '\x23' },
+ '&numero;': { 'codepoints': [0x02116], 'characters': '\u2116' },
+ '&numsp;': { 'codepoints': [0x02007], 'characters': '\u2007' },
+ '&nvDash;': { 'codepoints': [0x022AD], 'characters': '\u22AD' },
+ '&nvHarr;': { 'codepoints': [0x02904], 'characters': '\u2904' },
+ '&nvap;': { 'codepoints': [0x0224D, 0x020D2], 'characters': '\u224D\u20D2' },
+ '&nvdash;': { 'codepoints': [0x022AC], 'characters': '\u22AC' },
+ '&nvge;': { 'codepoints': [0x02265, 0x020D2], 'characters': '\u2265\u20D2' },
+ '&nvgt;': { 'codepoints': [0x0003E, 0x020D2], 'characters': '\x3E\u20D2' },
+ '&nvinfin;': { 'codepoints': [0x029DE], 'characters': '\u29DE' },
+ '&nvlArr;': { 'codepoints': [0x02902], 'characters': '\u2902' },
+ '&nvle;': { 'codepoints': [0x02264, 0x020D2], 'characters': '\u2264\u20D2' },
+ '&nvlt;': { 'codepoints': [0x0003C, 0x020D2], 'characters': '\x3C\u20D2' },
+ '&nvltrie;': { 'codepoints': [0x022B4, 0x020D2], 'characters': '\u22B4\u20D2' },
+ '&nvrArr;': { 'codepoints': [0x02903], 'characters': '\u2903' },
+ '&nvrtrie;': { 'codepoints': [0x022B5, 0x020D2], 'characters': '\u22B5\u20D2' },
+ '&nvsim;': { 'codepoints': [0x0223C, 0x020D2], 'characters': '\u223C\u20D2' },
+ '&nwArr;': { 'codepoints': [0x021D6], 'characters': '\u21D6' },
+ '&nwarhk;': { 'codepoints': [0x02923], 'characters': '\u2923' },
+ '&nwarr;': { 'codepoints': [0x02196], 'characters': '\u2196' },
+ '&nwarrow;': { 'codepoints': [0x02196], 'characters': '\u2196' },
+ '&nwnear;': { 'codepoints': [0x02927], 'characters': '\u2927' },
+ '&oS;': { 'codepoints': [0x024C8], 'characters': '\u24C8' },
+ '&oacute': { 'codepoints': [0x000F3], 'characters': '\xF3' },
+ '&oacute;': { 'codepoints': [0x000F3], 'characters': '\xF3' },
+ '&oast;': { 'codepoints': [0x0229B], 'characters': '\u229B' },
+ '&ocir;': { 'codepoints': [0x0229A], 'characters': '\u229A' },
+ '&ocirc': { 'codepoints': [0x000F4], 'characters': '\xF4' },
+ '&ocirc;': { 'codepoints': [0x000F4], 'characters': '\xF4' },
+ '&ocy;': { 'codepoints': [0x0043E], 'characters': '\u043E' },
+ '&odash;': { 'codepoints': [0x0229D], 'characters': '\u229D' },
+ '&odblac;': { 'codepoints': [0x00151], 'characters': '\u0151' },
+ '&odiv;': { 'codepoints': [0x02A38], 'characters': '\u2A38' },
+ '&odot;': { 'codepoints': [0x02299], 'characters': '\u2299' },
+ '&odsold;': { 'codepoints': [0x029BC], 'characters': '\u29BC' },
+ '&oelig;': { 'codepoints': [0x00153], 'characters': '\u0153' },
+ '&ofcir;': { 'codepoints': [0x029BF], 'characters': '\u29BF' },
+ '&ofr;': { 'codepoints': [0x1D52C], 'characters': '\uD835\uDD2C' },
+ '&ogon;': { 'codepoints': [0x002DB], 'characters': '\u02DB' },
+ '&ograve': { 'codepoints': [0x000F2], 'characters': '\xF2' },
+ '&ograve;': { 'codepoints': [0x000F2], 'characters': '\xF2' },
+ '&ogt;': { 'codepoints': [0x029C1], 'characters': '\u29C1' },
+ '&ohbar;': { 'codepoints': [0x029B5], 'characters': '\u29B5' },
+ '&ohm;': { 'codepoints': [0x003A9], 'characters': '\u03A9' },
+ '&oint;': { 'codepoints': [0x0222E], 'characters': '\u222E' },
+ '&olarr;': { 'codepoints': [0x021BA], 'characters': '\u21BA' },
+ '&olcir;': { 'codepoints': [0x029BE], 'characters': '\u29BE' },
+ '&olcross;': { 'codepoints': [0x029BB], 'characters': '\u29BB' },
+ '&oline;': { 'codepoints': [0x0203E], 'characters': '\u203E' },
+ '&olt;': { 'codepoints': [0x029C0], 'characters': '\u29C0' },
+ '&omacr;': { 'codepoints': [0x0014D], 'characters': '\u014D' },
+ '&omega;': { 'codepoints': [0x003C9], 'characters': '\u03C9' },
+ '&omicron;': { 'codepoints': [0x003BF], 'characters': '\u03BF' },
+ '&omid;': { 'codepoints': [0x029B6], 'characters': '\u29B6' },
+ '&ominus;': { 'codepoints': [0x02296], 'characters': '\u2296' },
+ '&oopf;': { 'codepoints': [0x1D560], 'characters': '\uD835\uDD60' },
+ '&opar;': { 'codepoints': [0x029B7], 'characters': '\u29B7' },
+ '&operp;': { 'codepoints': [0x029B9], 'characters': '\u29B9' },
+ '&oplus;': { 'codepoints': [0x02295], 'characters': '\u2295' },
+ '&or;': { 'codepoints': [0x02228], 'characters': '\u2228' },
+ '&orarr;': { 'codepoints': [0x021BB], 'characters': '\u21BB' },
+ '&ord;': { 'codepoints': [0x02A5D], 'characters': '\u2A5D' },
+ '&order;': { 'codepoints': [0x02134], 'characters': '\u2134' },
+ '&orderof;': { 'codepoints': [0x02134], 'characters': '\u2134' },
+ '&ordf': { 'codepoints': [0x000AA], 'characters': '\xAA' },
+ '&ordf;': { 'codepoints': [0x000AA], 'characters': '\xAA' },
+ '&ordm': { 'codepoints': [0x000BA], 'characters': '\xBA' },
+ '&ordm;': { 'codepoints': [0x000BA], 'characters': '\xBA' },
+ '&origof;': { 'codepoints': [0x022B6], 'characters': '\u22B6' },
+ '&oror;': { 'codepoints': [0x02A56], 'characters': '\u2A56' },
+ '&orslope;': { 'codepoints': [0x02A57], 'characters': '\u2A57' },
+ '&orv;': { 'codepoints': [0x02A5B], 'characters': '\u2A5B' },
+ '&oscr;': { 'codepoints': [0x02134], 'characters': '\u2134' },
+ '&oslash': { 'codepoints': [0x000F8], 'characters': '\xF8' },
+ '&oslash;': { 'codepoints': [0x000F8], 'characters': '\xF8' },
+ '&osol;': { 'codepoints': [0x02298], 'characters': '\u2298' },
+ '&otilde': { 'codepoints': [0x000F5], 'characters': '\xF5' },
+ '&otilde;': { 'codepoints': [0x000F5], 'characters': '\xF5' },
+ '&otimes;': { 'codepoints': [0x02297], 'characters': '\u2297' },
+ '&otimesas;': { 'codepoints': [0x02A36], 'characters': '\u2A36' },
+ '&ouml': { 'codepoints': [0x000F6], 'characters': '\xF6' },
+ '&ouml;': { 'codepoints': [0x000F6], 'characters': '\xF6' },
+ '&ovbar;': { 'codepoints': [0x0233D], 'characters': '\u233D' },
+ '&par;': { 'codepoints': [0x02225], 'characters': '\u2225' },
+ '&para': { 'codepoints': [0x000B6], 'characters': '\xB6' },
+ '&para;': { 'codepoints': [0x000B6], 'characters': '\xB6' },
+ '&parallel;': { 'codepoints': [0x02225], 'characters': '\u2225' },
+ '&parsim;': { 'codepoints': [0x02AF3], 'characters': '\u2AF3' },
+ '&parsl;': { 'codepoints': [0x02AFD], 'characters': '\u2AFD' },
+ '&part;': { 'codepoints': [0x02202], 'characters': '\u2202' },
+ '&pcy;': { 'codepoints': [0x0043F], 'characters': '\u043F' },
+ '&percnt;': { 'codepoints': [0x00025], 'characters': '\x25' },
+ '&period;': { 'codepoints': [0x0002E], 'characters': '\x2E' },
+ '&permil;': { 'codepoints': [0x02030], 'characters': '\u2030' },
+ '&perp;': { 'codepoints': [0x022A5], 'characters': '\u22A5' },
+ '&pertenk;': { 'codepoints': [0x02031], 'characters': '\u2031' },
+ '&pfr;': { 'codepoints': [0x1D52D], 'characters': '\uD835\uDD2D' },
+ '&phi;': { 'codepoints': [0x003C6], 'characters': '\u03C6' },
+ '&phiv;': { 'codepoints': [0x003D5], 'characters': '\u03D5' },
+ '&phmmat;': { 'codepoints': [0x02133], 'characters': '\u2133' },
+ '&phone;': { 'codepoints': [0x0260E], 'characters': '\u260E' },
+ '&pi;': { 'codepoints': [0x003C0], 'characters': '\u03C0' },
+ '&pitchfork;': { 'codepoints': [0x022D4], 'characters': '\u22D4' },
+ '&piv;': { 'codepoints': [0x003D6], 'characters': '\u03D6' },
+ '&planck;': { 'codepoints': [0x0210F], 'characters': '\u210F' },
+ '&planckh;': { 'codepoints': [0x0210E], 'characters': '\u210E' },
+ '&plankv;': { 'codepoints': [0x0210F], 'characters': '\u210F' },
+ '&plus;': { 'codepoints': [0x0002B], 'characters': '\x2B' },
+ '&plusacir;': { 'codepoints': [0x02A23], 'characters': '\u2A23' },
+ '&plusb;': { 'codepoints': [0x0229E], 'characters': '\u229E' },
+ '&pluscir;': { 'codepoints': [0x02A22], 'characters': '\u2A22' },
+ '&plusdo;': { 'codepoints': [0x02214], 'characters': '\u2214' },
+ '&plusdu;': { 'codepoints': [0x02A25], 'characters': '\u2A25' },
+ '&pluse;': { 'codepoints': [0x02A72], 'characters': '\u2A72' },
+ '&plusmn': { 'codepoints': [0x000B1], 'characters': '\xB1' },
+ '&plusmn;': { 'codepoints': [0x000B1], 'characters': '\xB1' },
+ '&plussim;': { 'codepoints': [0x02A26], 'characters': '\u2A26' },
+ '&plustwo;': { 'codepoints': [0x02A27], 'characters': '\u2A27' },
+ '&pm;': { 'codepoints': [0x000B1], 'characters': '\xB1' },
+ '&pointint;': { 'codepoints': [0x02A15], 'characters': '\u2A15' },
+ '&popf;': { 'codepoints': [0x1D561], 'characters': '\uD835\uDD61' },
+ '&pound': { 'codepoints': [0x000A3], 'characters': '\xA3' },
+ '&pound;': { 'codepoints': [0x000A3], 'characters': '\xA3' },
+ '&pr;': { 'codepoints': [0x0227A], 'characters': '\u227A' },
+ '&prE;': { 'codepoints': [0x02AB3], 'characters': '\u2AB3' },
+ '&prap;': { 'codepoints': [0x02AB7], 'characters': '\u2AB7' },
+ '&prcue;': { 'codepoints': [0x0227C], 'characters': '\u227C' },
+ '&pre;': { 'codepoints': [0x02AAF], 'characters': '\u2AAF' },
+ '&prec;': { 'codepoints': [0x0227A], 'characters': '\u227A' },
+ '&precapprox;': { 'codepoints': [0x02AB7], 'characters': '\u2AB7' },
+ '&preccurlyeq;': { 'codepoints': [0x0227C], 'characters': '\u227C' },
+ '&preceq;': { 'codepoints': [0x02AAF], 'characters': '\u2AAF' },
+ '&precnapprox;': { 'codepoints': [0x02AB9], 'characters': '\u2AB9' },
+ '&precneqq;': { 'codepoints': [0x02AB5], 'characters': '\u2AB5' },
+ '&precnsim;': { 'codepoints': [0x022E8], 'characters': '\u22E8' },
+ '&precsim;': { 'codepoints': [0x0227E], 'characters': '\u227E' },
+ '&prime;': { 'codepoints': [0x02032], 'characters': '\u2032' },
+ '&primes;': { 'codepoints': [0x02119], 'characters': '\u2119' },
+ '&prnE;': { 'codepoints': [0x02AB5], 'characters': '\u2AB5' },
+ '&prnap;': { 'codepoints': [0x02AB9], 'characters': '\u2AB9' },
+ '&prnsim;': { 'codepoints': [0x022E8], 'characters': '\u22E8' },
+ '&prod;': { 'codepoints': [0x0220F], 'characters': '\u220F' },
+ '&profalar;': { 'codepoints': [0x0232E], 'characters': '\u232E' },
+ '&profline;': { 'codepoints': [0x02312], 'characters': '\u2312' },
+ '&profsurf;': { 'codepoints': [0x02313], 'characters': '\u2313' },
+ '&prop;': { 'codepoints': [0x0221D], 'characters': '\u221D' },
+ '&propto;': { 'codepoints': [0x0221D], 'characters': '\u221D' },
+ '&prsim;': { 'codepoints': [0x0227E], 'characters': '\u227E' },
+ '&prurel;': { 'codepoints': [0x022B0], 'characters': '\u22B0' },
+ '&pscr;': { 'codepoints': [0x1D4C5], 'characters': '\uD835\uDCC5' },
+ '&psi;': { 'codepoints': [0x003C8], 'characters': '\u03C8' },
+ '&puncsp;': { 'codepoints': [0x02008], 'characters': '\u2008' },
+ '&qfr;': { 'codepoints': [0x1D52E], 'characters': '\uD835\uDD2E' },
+ '&qint;': { 'codepoints': [0x02A0C], 'characters': '\u2A0C' },
+ '&qopf;': { 'codepoints': [0x1D562], 'characters': '\uD835\uDD62' },
+ '&qprime;': { 'codepoints': [0x02057], 'characters': '\u2057' },
+ '&qscr;': { 'codepoints': [0x1D4C6], 'characters': '\uD835\uDCC6' },
+ '&quaternions;': { 'codepoints': [0x0210D], 'characters': '\u210D' },
+ '&quatint;': { 'codepoints': [0x02A16], 'characters': '\u2A16' },
+ '&quest;': { 'codepoints': [0x0003F], 'characters': '\x3F' },
+ '&questeq;': { 'codepoints': [0x0225F], 'characters': '\u225F' },
+ '&quot': { 'codepoints': [0x00022], 'characters': '\x22' },
+ '&quot;': { 'codepoints': [0x00022], 'characters': '\x22' },
+ '&rAarr;': { 'codepoints': [0x021DB], 'characters': '\u21DB' },
+ '&rArr;': { 'codepoints': [0x021D2], 'characters': '\u21D2' },
+ '&rAtail;': { 'codepoints': [0x0291C], 'characters': '\u291C' },
+ '&rBarr;': { 'codepoints': [0x0290F], 'characters': '\u290F' },
+ '&rHar;': { 'codepoints': [0x02964], 'characters': '\u2964' },
+ '&race;': { 'codepoints': [0x0223D, 0x00331], 'characters': '\u223D\u0331' },
+ '&racute;': { 'codepoints': [0x00155], 'characters': '\u0155' },
+ '&radic;': { 'codepoints': [0x0221A], 'characters': '\u221A' },
+ '&raemptyv;': { 'codepoints': [0x029B3], 'characters': '\u29B3' },
+ '&rang;': { 'codepoints': [0x027E9], 'characters': '\u27E9' },
+ '&rangd;': { 'codepoints': [0x02992], 'characters': '\u2992' },
+ '&range;': { 'codepoints': [0x029A5], 'characters': '\u29A5' },
+ '&rangle;': { 'codepoints': [0x027E9], 'characters': '\u27E9' },
+ '&raquo': { 'codepoints': [0x000BB], 'characters': '\xBB' },
+ '&raquo;': { 'codepoints': [0x000BB], 'characters': '\xBB' },
+ '&rarr;': { 'codepoints': [0x02192], 'characters': '\u2192' },
+ '&rarrap;': { 'codepoints': [0x02975], 'characters': '\u2975' },
+ '&rarrb;': { 'codepoints': [0x021E5], 'characters': '\u21E5' },
+ '&rarrbfs;': { 'codepoints': [0x02920], 'characters': '\u2920' },
+ '&rarrc;': { 'codepoints': [0x02933], 'characters': '\u2933' },
+ '&rarrfs;': { 'codepoints': [0x0291E], 'characters': '\u291E' },
+ '&rarrhk;': { 'codepoints': [0x021AA], 'characters': '\u21AA' },
+ '&rarrlp;': { 'codepoints': [0x021AC], 'characters': '\u21AC' },
+ '&rarrpl;': { 'codepoints': [0x02945], 'characters': '\u2945' },
+ '&rarrsim;': { 'codepoints': [0x02974], 'characters': '\u2974' },
+ '&rarrtl;': { 'codepoints': [0x021A3], 'characters': '\u21A3' },
+ '&rarrw;': { 'codepoints': [0x0219D], 'characters': '\u219D' },
+ '&ratail;': { 'codepoints': [0x0291A], 'characters': '\u291A' },
+ '&ratio;': { 'codepoints': [0x02236], 'characters': '\u2236' },
+ '&rationals;': { 'codepoints': [0x0211A], 'characters': '\u211A' },
+ '&rbarr;': { 'codepoints': [0x0290D], 'characters': '\u290D' },
+ '&rbbrk;': { 'codepoints': [0x02773], 'characters': '\u2773' },
+ '&rbrace;': { 'codepoints': [0x0007D], 'characters': '\x7D' },
+ '&rbrack;': { 'codepoints': [0x0005D], 'characters': '\x5D' },
+ '&rbrke;': { 'codepoints': [0x0298C], 'characters': '\u298C' },
+ '&rbrksld;': { 'codepoints': [0x0298E], 'characters': '\u298E' },
+ '&rbrkslu;': { 'codepoints': [0x02990], 'characters': '\u2990' },
+ '&rcaron;': { 'codepoints': [0x00159], 'characters': '\u0159' },
+ '&rcedil;': { 'codepoints': [0x00157], 'characters': '\u0157' },
+ '&rceil;': { 'codepoints': [0x02309], 'characters': '\u2309' },
+ '&rcub;': { 'codepoints': [0x0007D], 'characters': '\x7D' },
+ '&rcy;': { 'codepoints': [0x00440], 'characters': '\u0440' },
+ '&rdca;': { 'codepoints': [0x02937], 'characters': '\u2937' },
+ '&rdldhar;': { 'codepoints': [0x02969], 'characters': '\u2969' },
+ '&rdquo;': { 'codepoints': [0x0201D], 'characters': '\u201D' },
+ '&rdquor;': { 'codepoints': [0x0201D], 'characters': '\u201D' },
+ '&rdsh;': { 'codepoints': [0x021B3], 'characters': '\u21B3' },
+ '&real;': { 'codepoints': [0x0211C], 'characters': '\u211C' },
+ '&realine;': { 'codepoints': [0x0211B], 'characters': '\u211B' },
+ '&realpart;': { 'codepoints': [0x0211C], 'characters': '\u211C' },
+ '&reals;': { 'codepoints': [0x0211D], 'characters': '\u211D' },
+ '&rect;': { 'codepoints': [0x025AD], 'characters': '\u25AD' },
+ '&reg': { 'codepoints': [0x000AE], 'characters': '\xAE' },
+ '&reg;': { 'codepoints': [0x000AE], 'characters': '\xAE' },
+ '&rfisht;': { 'codepoints': [0x0297D], 'characters': '\u297D' },
+ '&rfloor;': { 'codepoints': [0x0230B], 'characters': '\u230B' },
+ '&rfr;': { 'codepoints': [0x1D52F], 'characters': '\uD835\uDD2F' },
+ '&rhard;': { 'codepoints': [0x021C1], 'characters': '\u21C1' },
+ '&rharu;': { 'codepoints': [0x021C0], 'characters': '\u21C0' },
+ '&rharul;': { 'codepoints': [0x0296C], 'characters': '\u296C' },
+ '&rho;': { 'codepoints': [0x003C1], 'characters': '\u03C1' },
+ '&rhov;': { 'codepoints': [0x003F1], 'characters': '\u03F1' },
+ '&rightarrow;': { 'codepoints': [0x02192], 'characters': '\u2192' },
+ '&rightarrowtail;': { 'codepoints': [0x021A3], 'characters': '\u21A3' },
+ '&rightharpoondown;': { 'codepoints': [0x021C1], 'characters': '\u21C1' },
+ '&rightharpoonup;': { 'codepoints': [0x021C0], 'characters': '\u21C0' },
+ '&rightleftarrows;': { 'codepoints': [0x021C4], 'characters': '\u21C4' },
+ '&rightleftharpoons;': { 'codepoints': [0x021CC], 'characters': '\u21CC' },
+ '&rightrightarrows;': { 'codepoints': [0x021C9], 'characters': '\u21C9' },
+ '&rightsquigarrow;': { 'codepoints': [0x0219D], 'characters': '\u219D' },
+ '&rightthreetimes;': { 'codepoints': [0x022CC], 'characters': '\u22CC' },
+ '&ring;': { 'codepoints': [0x002DA], 'characters': '\u02DA' },
+ '&risingdotseq;': { 'codepoints': [0x02253], 'characters': '\u2253' },
+ '&rlarr;': { 'codepoints': [0x021C4], 'characters': '\u21C4' },
+ '&rlhar;': { 'codepoints': [0x021CC], 'characters': '\u21CC' },
+ '&rlm;': { 'codepoints': [0x0200F], 'characters': '\u200F' },
+ '&rmoust;': { 'codepoints': [0x023B1], 'characters': '\u23B1' },
+ '&rmoustache;': { 'codepoints': [0x023B1], 'characters': '\u23B1' },
+ '&rnmid;': { 'codepoints': [0x02AEE], 'characters': '\u2AEE' },
+ '&roang;': { 'codepoints': [0x027ED], 'characters': '\u27ED' },
+ '&roarr;': { 'codepoints': [0x021FE], 'characters': '\u21FE' },
+ '&robrk;': { 'codepoints': [0x027E7], 'characters': '\u27E7' },
+ '&ropar;': { 'codepoints': [0x02986], 'characters': '\u2986' },
+ '&ropf;': { 'codepoints': [0x1D563], 'characters': '\uD835\uDD63' },
+ '&roplus;': { 'codepoints': [0x02A2E], 'characters': '\u2A2E' },
+ '&rotimes;': { 'codepoints': [0x02A35], 'characters': '\u2A35' },
+ '&rpar;': { 'codepoints': [0x00029], 'characters': '\x29' },
+ '&rpargt;': { 'codepoints': [0x02994], 'characters': '\u2994' },
+ '&rppolint;': { 'codepoints': [0x02A12], 'characters': '\u2A12' },
+ '&rrarr;': { 'codepoints': [0x021C9], 'characters': '\u21C9' },
+ '&rsaquo;': { 'codepoints': [0x0203A], 'characters': '\u203A' },
+ '&rscr;': { 'codepoints': [0x1D4C7], 'characters': '\uD835\uDCC7' },
+ '&rsh;': { 'codepoints': [0x021B1], 'characters': '\u21B1' },
+ '&rsqb;': { 'codepoints': [0x0005D], 'characters': '\x5D' },
+ '&rsquo;': { 'codepoints': [0x02019], 'characters': '\u2019' },
+ '&rsquor;': { 'codepoints': [0x02019], 'characters': '\u2019' },
+ '&rthree;': { 'codepoints': [0x022CC], 'characters': '\u22CC' },
+ '&rtimes;': { 'codepoints': [0x022CA], 'characters': '\u22CA' },
+ '&rtri;': { 'codepoints': [0x025B9], 'characters': '\u25B9' },
+ '&rtrie;': { 'codepoints': [0x022B5], 'characters': '\u22B5' },
+ '&rtrif;': { 'codepoints': [0x025B8], 'characters': '\u25B8' },
+ '&rtriltri;': { 'codepoints': [0x029CE], 'characters': '\u29CE' },
+ '&ruluhar;': { 'codepoints': [0x02968], 'characters': '\u2968' },
+ '&rx;': { 'codepoints': [0x0211E], 'characters': '\u211E' },
+ '&sacute;': { 'codepoints': [0x0015B], 'characters': '\u015B' },
+ '&sbquo;': { 'codepoints': [0x0201A], 'characters': '\u201A' },
+ '&sc;': { 'codepoints': [0x0227B], 'characters': '\u227B' },
+ '&scE;': { 'codepoints': [0x02AB4], 'characters': '\u2AB4' },
+ '&scap;': { 'codepoints': [0x02AB8], 'characters': '\u2AB8' },
+ '&scaron;': { 'codepoints': [0x00161], 'characters': '\u0161' },
+ '&sccue;': { 'codepoints': [0x0227D], 'characters': '\u227D' },
+ '&sce;': { 'codepoints': [0x02AB0], 'characters': '\u2AB0' },
+ '&scedil;': { 'codepoints': [0x0015F], 'characters': '\u015F' },
+ '&scirc;': { 'codepoints': [0x0015D], 'characters': '\u015D' },
+ '&scnE;': { 'codepoints': [0x02AB6], 'characters': '\u2AB6' },
+ '&scnap;': { 'codepoints': [0x02ABA], 'characters': '\u2ABA' },
+ '&scnsim;': { 'codepoints': [0x022E9], 'characters': '\u22E9' },
+ '&scpolint;': { 'codepoints': [0x02A13], 'characters': '\u2A13' },
+ '&scsim;': { 'codepoints': [0x0227F], 'characters': '\u227F' },
+ '&scy;': { 'codepoints': [0x00441], 'characters': '\u0441' },
+ '&sdot;': { 'codepoints': [0x022C5], 'characters': '\u22C5' },
+ '&sdotb;': { 'codepoints': [0x022A1], 'characters': '\u22A1' },
+ '&sdote;': { 'codepoints': [0x02A66], 'characters': '\u2A66' },
+ '&seArr;': { 'codepoints': [0x021D8], 'characters': '\u21D8' },
+ '&searhk;': { 'codepoints': [0x02925], 'characters': '\u2925' },
+ '&searr;': { 'codepoints': [0x02198], 'characters': '\u2198' },
+ '&searrow;': { 'codepoints': [0x02198], 'characters': '\u2198' },
+ '&sect': { 'codepoints': [0x000A7], 'characters': '\xA7' },
+ '&sect;': { 'codepoints': [0x000A7], 'characters': '\xA7' },
+ '&semi;': { 'codepoints': [0x0003B], 'characters': '\x3B' },
+ '&seswar;': { 'codepoints': [0x02929], 'characters': '\u2929' },
+ '&setminus;': { 'codepoints': [0x02216], 'characters': '\u2216' },
+ '&setmn;': { 'codepoints': [0x02216], 'characters': '\u2216' },
+ '&sext;': { 'codepoints': [0x02736], 'characters': '\u2736' },
+ '&sfr;': { 'codepoints': [0x1D530], 'characters': '\uD835\uDD30' },
+ '&sfrown;': { 'codepoints': [0x02322], 'characters': '\u2322' },
+ '&sharp;': { 'codepoints': [0x0266F], 'characters': '\u266F' },
+ '&shchcy;': { 'codepoints': [0x00449], 'characters': '\u0449' },
+ '&shcy;': { 'codepoints': [0x00448], 'characters': '\u0448' },
+ '&shortmid;': { 'codepoints': [0x02223], 'characters': '\u2223' },
+ '&shortparallel;': { 'codepoints': [0x02225], 'characters': '\u2225' },
+ '&shy': { 'codepoints': [0x000AD], 'characters': '\xAD' },
+ '&shy;': { 'codepoints': [0x000AD], 'characters': '\xAD' },
+ '&sigma;': { 'codepoints': [0x003C3], 'characters': '\u03C3' },
+ '&sigmaf;': { 'codepoints': [0x003C2], 'characters': '\u03C2' },
+ '&sigmav;': { 'codepoints': [0x003C2], 'characters': '\u03C2' },
+ '&sim;': { 'codepoints': [0x0223C], 'characters': '\u223C' },
+ '&simdot;': { 'codepoints': [0x02A6A], 'characters': '\u2A6A' },
+ '&sime;': { 'codepoints': [0x02243], 'characters': '\u2243' },
+ '&simeq;': { 'codepoints': [0x02243], 'characters': '\u2243' },
+ '&simg;': { 'codepoints': [0x02A9E], 'characters': '\u2A9E' },
+ '&simgE;': { 'codepoints': [0x02AA0], 'characters': '\u2AA0' },
+ '&siml;': { 'codepoints': [0x02A9D], 'characters': '\u2A9D' },
+ '&simlE;': { 'codepoints': [0x02A9F], 'characters': '\u2A9F' },
+ '&simne;': { 'codepoints': [0x02246], 'characters': '\u2246' },
+ '&simplus;': { 'codepoints': [0x02A24], 'characters': '\u2A24' },
+ '&simrarr;': { 'codepoints': [0x02972], 'characters': '\u2972' },
+ '&slarr;': { 'codepoints': [0x02190], 'characters': '\u2190' },
+ '&smallsetminus;': { 'codepoints': [0x02216], 'characters': '\u2216' },
+ '&smashp;': { 'codepoints': [0x02A33], 'characters': '\u2A33' },
+ '&smeparsl;': { 'codepoints': [0x029E4], 'characters': '\u29E4' },
+ '&smid;': { 'codepoints': [0x02223], 'characters': '\u2223' },
+ '&smile;': { 'codepoints': [0x02323], 'characters': '\u2323' },
+ '&smt;': { 'codepoints': [0x02AAA], 'characters': '\u2AAA' },
+ '&smte;': { 'codepoints': [0x02AAC], 'characters': '\u2AAC' },
+ '&smtes;': { 'codepoints': [0x02AAC, 0x0FE00], 'characters': '\u2AAC\uFE00' },
+ '&softcy;': { 'codepoints': [0x0044C], 'characters': '\u044C' },
+ '&sol;': { 'codepoints': [0x0002F], 'characters': '\x2F' },
+ '&solb;': { 'codepoints': [0x029C4], 'characters': '\u29C4' },
+ '&solbar;': { 'codepoints': [0x0233F], 'characters': '\u233F' },
+ '&sopf;': { 'codepoints': [0x1D564], 'characters': '\uD835\uDD64' },
+ '&spades;': { 'codepoints': [0x02660], 'characters': '\u2660' },
+ '&spadesuit;': { 'codepoints': [0x02660], 'characters': '\u2660' },
+ '&spar;': { 'codepoints': [0x02225], 'characters': '\u2225' },
+ '&sqcap;': { 'codepoints': [0x02293], 'characters': '\u2293' },
+ '&sqcaps;': { 'codepoints': [0x02293, 0x0FE00], 'characters': '\u2293\uFE00' },
+ '&sqcup;': { 'codepoints': [0x02294], 'characters': '\u2294' },
+ '&sqcups;': { 'codepoints': [0x02294, 0x0FE00], 'characters': '\u2294\uFE00' },
+ '&sqsub;': { 'codepoints': [0x0228F], 'characters': '\u228F' },
+ '&sqsube;': { 'codepoints': [0x02291], 'characters': '\u2291' },
+ '&sqsubset;': { 'codepoints': [0x0228F], 'characters': '\u228F' },
+ '&sqsubseteq;': { 'codepoints': [0x02291], 'characters': '\u2291' },
+ '&sqsup;': { 'codepoints': [0x02290], 'characters': '\u2290' },
+ '&sqsupe;': { 'codepoints': [0x02292], 'characters': '\u2292' },
+ '&sqsupset;': { 'codepoints': [0x02290], 'characters': '\u2290' },
+ '&sqsupseteq;': { 'codepoints': [0x02292], 'characters': '\u2292' },
+ '&squ;': { 'codepoints': [0x025A1], 'characters': '\u25A1' },
+ '&square;': { 'codepoints': [0x025A1], 'characters': '\u25A1' },
+ '&squarf;': { 'codepoints': [0x025AA], 'characters': '\u25AA' },
+ '&squf;': { 'codepoints': [0x025AA], 'characters': '\u25AA' },
+ '&srarr;': { 'codepoints': [0x02192], 'characters': '\u2192' },
+ '&sscr;': { 'codepoints': [0x1D4C8], 'characters': '\uD835\uDCC8' },
+ '&ssetmn;': { 'codepoints': [0x02216], 'characters': '\u2216' },
+ '&ssmile;': { 'codepoints': [0x02323], 'characters': '\u2323' },
+ '&sstarf;': { 'codepoints': [0x022C6], 'characters': '\u22C6' },
+ '&star;': { 'codepoints': [0x02606], 'characters': '\u2606' },
+ '&starf;': { 'codepoints': [0x02605], 'characters': '\u2605' },
+ '&straightepsilon;': { 'codepoints': [0x003F5], 'characters': '\u03F5' },
+ '&straightphi;': { 'codepoints': [0x003D5], 'characters': '\u03D5' },
+ '&strns;': { 'codepoints': [0x000AF], 'characters': '\xAF' },
+ '&sub;': { 'codepoints': [0x02282], 'characters': '\u2282' },
+ '&subE;': { 'codepoints': [0x02AC5], 'characters': '\u2AC5' },
+ '&subdot;': { 'codepoints': [0x02ABD], 'characters': '\u2ABD' },
+ '&sube;': { 'codepoints': [0x02286], 'characters': '\u2286' },
+ '&subedot;': { 'codepoints': [0x02AC3], 'characters': '\u2AC3' },
+ '&submult;': { 'codepoints': [0x02AC1], 'characters': '\u2AC1' },
+ '&subnE;': { 'codepoints': [0x02ACB], 'characters': '\u2ACB' },
+ '&subne;': { 'codepoints': [0x0228A], 'characters': '\u228A' },
+ '&subplus;': { 'codepoints': [0x02ABF], 'characters': '\u2ABF' },
+ '&subrarr;': { 'codepoints': [0x02979], 'characters': '\u2979' },
+ '&subset;': { 'codepoints': [0x02282], 'characters': '\u2282' },
+ '&subseteq;': { 'codepoints': [0x02286], 'characters': '\u2286' },
+ '&subseteqq;': { 'codepoints': [0x02AC5], 'characters': '\u2AC5' },
+ '&subsetneq;': { 'codepoints': [0x0228A], 'characters': '\u228A' },
+ '&subsetneqq;': { 'codepoints': [0x02ACB], 'characters': '\u2ACB' },
+ '&subsim;': { 'codepoints': [0x02AC7], 'characters': '\u2AC7' },
+ '&subsub;': { 'codepoints': [0x02AD5], 'characters': '\u2AD5' },
+ '&subsup;': { 'codepoints': [0x02AD3], 'characters': '\u2AD3' },
+ '&succ;': { 'codepoints': [0x0227B], 'characters': '\u227B' },
+ '&succapprox;': { 'codepoints': [0x02AB8], 'characters': '\u2AB8' },
+ '&succcurlyeq;': { 'codepoints': [0x0227D], 'characters': '\u227D' },
+ '&succeq;': { 'codepoints': [0x02AB0], 'characters': '\u2AB0' },
+ '&succnapprox;': { 'codepoints': [0x02ABA], 'characters': '\u2ABA' },
+ '&succneqq;': { 'codepoints': [0x02AB6], 'characters': '\u2AB6' },
+ '&succnsim;': { 'codepoints': [0x022E9], 'characters': '\u22E9' },
+ '&succsim;': { 'codepoints': [0x0227F], 'characters': '\u227F' },
+ '&sum;': { 'codepoints': [0x02211], 'characters': '\u2211' },
+ '&sung;': { 'codepoints': [0x0266A], 'characters': '\u266A' },
+ '&sup1': { 'codepoints': [0x000B9], 'characters': '\xB9' },
+ '&sup1;': { 'codepoints': [0x000B9], 'characters': '\xB9' },
+ '&sup2': { 'codepoints': [0x000B2], 'characters': '\xB2' },
+ '&sup2;': { 'codepoints': [0x000B2], 'characters': '\xB2' },
+ '&sup3': { 'codepoints': [0x000B3], 'characters': '\xB3' },
+ '&sup3;': { 'codepoints': [0x000B3], 'characters': '\xB3' },
+ '&sup;': { 'codepoints': [0x02283], 'characters': '\u2283' },
+ '&supE;': { 'codepoints': [0x02AC6], 'characters': '\u2AC6' },
+ '&supdot;': { 'codepoints': [0x02ABE], 'characters': '\u2ABE' },
+ '&supdsub;': { 'codepoints': [0x02AD8], 'characters': '\u2AD8' },
+ '&supe;': { 'codepoints': [0x02287], 'characters': '\u2287' },
+ '&supedot;': { 'codepoints': [0x02AC4], 'characters': '\u2AC4' },
+ '&suphsol;': { 'codepoints': [0x027C9], 'characters': '\u27C9' },
+ '&suphsub;': { 'codepoints': [0x02AD7], 'characters': '\u2AD7' },
+ '&suplarr;': { 'codepoints': [0x0297B], 'characters': '\u297B' },
+ '&supmult;': { 'codepoints': [0x02AC2], 'characters': '\u2AC2' },
+ '&supnE;': { 'codepoints': [0x02ACC], 'characters': '\u2ACC' },
+ '&supne;': { 'codepoints': [0x0228B], 'characters': '\u228B' },
+ '&supplus;': { 'codepoints': [0x02AC0], 'characters': '\u2AC0' },
+ '&supset;': { 'codepoints': [0x02283], 'characters': '\u2283' },
+ '&supseteq;': { 'codepoints': [0x02287], 'characters': '\u2287' },
+ '&supseteqq;': { 'codepoints': [0x02AC6], 'characters': '\u2AC6' },
+ '&supsetneq;': { 'codepoints': [0x0228B], 'characters': '\u228B' },
+ '&supsetneqq;': { 'codepoints': [0x02ACC], 'characters': '\u2ACC' },
+ '&supsim;': { 'codepoints': [0x02AC8], 'characters': '\u2AC8' },
+ '&supsub;': { 'codepoints': [0x02AD4], 'characters': '\u2AD4' },
+ '&supsup;': { 'codepoints': [0x02AD6], 'characters': '\u2AD6' },
+ '&swArr;': { 'codepoints': [0x021D9], 'characters': '\u21D9' },
+ '&swarhk;': { 'codepoints': [0x02926], 'characters': '\u2926' },
+ '&swarr;': { 'codepoints': [0x02199], 'characters': '\u2199' },
+ '&swarrow;': { 'codepoints': [0x02199], 'characters': '\u2199' },
+ '&swnwar;': { 'codepoints': [0x0292A], 'characters': '\u292A' },
+ '&szlig': { 'codepoints': [0x000DF], 'characters': '\xDF' },
+ '&szlig;': { 'codepoints': [0x000DF], 'characters': '\xDF' },
+ '&target;': { 'codepoints': [0x02316], 'characters': '\u2316' },
+ '&tau;': { 'codepoints': [0x003C4], 'characters': '\u03C4' },
+ '&tbrk;': { 'codepoints': [0x023B4], 'characters': '\u23B4' },
+ '&tcaron;': { 'codepoints': [0x00165], 'characters': '\u0165' },
+ '&tcedil;': { 'codepoints': [0x00163], 'characters': '\u0163' },
+ '&tcy;': { 'codepoints': [0x00442], 'characters': '\u0442' },
+ '&tdot;': { 'codepoints': [0x020DB], 'characters': '\u20DB' },
+ '&telrec;': { 'codepoints': [0x02315], 'characters': '\u2315' },
+ '&tfr;': { 'codepoints': [0x1D531], 'characters': '\uD835\uDD31' },
+ '&there4;': { 'codepoints': [0x02234], 'characters': '\u2234' },
+ '&therefore;': { 'codepoints': [0x02234], 'characters': '\u2234' },
+ '&theta;': { 'codepoints': [0x003B8], 'characters': '\u03B8' },
+ '&thetasym;': { 'codepoints': [0x003D1], 'characters': '\u03D1' },
+ '&thetav;': { 'codepoints': [0x003D1], 'characters': '\u03D1' },
+ '&thickapprox;': { 'codepoints': [0x02248], 'characters': '\u2248' },
+ '&thicksim;': { 'codepoints': [0x0223C], 'characters': '\u223C' },
+ '&thinsp;': { 'codepoints': [0x02009], 'characters': '\u2009' },
+ '&thkap;': { 'codepoints': [0x02248], 'characters': '\u2248' },
+ '&thksim;': { 'codepoints': [0x0223C], 'characters': '\u223C' },
+ '&thorn': { 'codepoints': [0x000FE], 'characters': '\xFE' },
+ '&thorn;': { 'codepoints': [0x000FE], 'characters': '\xFE' },
+ '&tilde;': { 'codepoints': [0x002DC], 'characters': '\u02DC' },
+ '&times': { 'codepoints': [0x000D7], 'characters': '\xD7' },
+ '&times;': { 'codepoints': [0x000D7], 'characters': '\xD7' },
+ '&timesb;': { 'codepoints': [0x022A0], 'characters': '\u22A0' },
+ '&timesbar;': { 'codepoints': [0x02A31], 'characters': '\u2A31' },
+ '&timesd;': { 'codepoints': [0x02A30], 'characters': '\u2A30' },
+ '&tint;': { 'codepoints': [0x0222D], 'characters': '\u222D' },
+ '&toea;': { 'codepoints': [0x02928], 'characters': '\u2928' },
+ '&top;': { 'codepoints': [0x022A4], 'characters': '\u22A4' },
+ '&topbot;': { 'codepoints': [0x02336], 'characters': '\u2336' },
+ '&topcir;': { 'codepoints': [0x02AF1], 'characters': '\u2AF1' },
+ '&topf;': { 'codepoints': [0x1D565], 'characters': '\uD835\uDD65' },
+ '&topfork;': { 'codepoints': [0x02ADA], 'characters': '\u2ADA' },
+ '&tosa;': { 'codepoints': [0x02929], 'characters': '\u2929' },
+ '&tprime;': { 'codepoints': [0x02034], 'characters': '\u2034' },
+ '&trade;': { 'codepoints': [0x02122], 'characters': '\u2122' },
+ '&triangle;': { 'codepoints': [0x025B5], 'characters': '\u25B5' },
+ '&triangledown;': { 'codepoints': [0x025BF], 'characters': '\u25BF' },
+ '&triangleleft;': { 'codepoints': [0x025C3], 'characters': '\u25C3' },
+ '&trianglelefteq;': { 'codepoints': [0x022B4], 'characters': '\u22B4' },
+ '&triangleq;': { 'codepoints': [0x0225C], 'characters': '\u225C' },
+ '&triangleright;': { 'codepoints': [0x025B9], 'characters': '\u25B9' },
+ '&trianglerighteq;': { 'codepoints': [0x022B5], 'characters': '\u22B5' },
+ '&tridot;': { 'codepoints': [0x025EC], 'characters': '\u25EC' },
+ '&trie;': { 'codepoints': [0x0225C], 'characters': '\u225C' },
+ '&triminus;': { 'codepoints': [0x02A3A], 'characters': '\u2A3A' },
+ '&triplus;': { 'codepoints': [0x02A39], 'characters': '\u2A39' },
+ '&trisb;': { 'codepoints': [0x029CD], 'characters': '\u29CD' },
+ '&tritime;': { 'codepoints': [0x02A3B], 'characters': '\u2A3B' },
+ '&trpezium;': { 'codepoints': [0x023E2], 'characters': '\u23E2' },
+ '&tscr;': { 'codepoints': [0x1D4C9], 'characters': '\uD835\uDCC9' },
+ '&tscy;': { 'codepoints': [0x00446], 'characters': '\u0446' },
+ '&tshcy;': { 'codepoints': [0x0045B], 'characters': '\u045B' },
+ '&tstrok;': { 'codepoints': [0x00167], 'characters': '\u0167' },
+ '&twixt;': { 'codepoints': [0x0226C], 'characters': '\u226C' },
+ '&twoheadleftarrow;': { 'codepoints': [0x0219E], 'characters': '\u219E' },
+ '&twoheadrightarrow;': { 'codepoints': [0x021A0], 'characters': '\u21A0' },
+ '&uArr;': { 'codepoints': [0x021D1], 'characters': '\u21D1' },
+ '&uHar;': { 'codepoints': [0x02963], 'characters': '\u2963' },
+ '&uacute': { 'codepoints': [0x000FA], 'characters': '\xFA' },
+ '&uacute;': { 'codepoints': [0x000FA], 'characters': '\xFA' },
+ '&uarr;': { 'codepoints': [0x02191], 'characters': '\u2191' },
+ '&ubrcy;': { 'codepoints': [0x0045E], 'characters': '\u045E' },
+ '&ubreve;': { 'codepoints': [0x0016D], 'characters': '\u016D' },
+ '&ucirc': { 'codepoints': [0x000FB], 'characters': '\xFB' },
+ '&ucirc;': { 'codepoints': [0x000FB], 'characters': '\xFB' },
+ '&ucy;': { 'codepoints': [0x00443], 'characters': '\u0443' },
+ '&udarr;': { 'codepoints': [0x021C5], 'characters': '\u21C5' },
+ '&udblac;': { 'codepoints': [0x00171], 'characters': '\u0171' },
+ '&udhar;': { 'codepoints': [0x0296E], 'characters': '\u296E' },
+ '&ufisht;': { 'codepoints': [0x0297E], 'characters': '\u297E' },
+ '&ufr;': { 'codepoints': [0x1D532], 'characters': '\uD835\uDD32' },
+ '&ugrave': { 'codepoints': [0x000F9], 'characters': '\xF9' },
+ '&ugrave;': { 'codepoints': [0x000F9], 'characters': '\xF9' },
+ '&uharl;': { 'codepoints': [0x021BF], 'characters': '\u21BF' },
+ '&uharr;': { 'codepoints': [0x021BE], 'characters': '\u21BE' },
+ '&uhblk;': { 'codepoints': [0x02580], 'characters': '\u2580' },
+ '&ulcorn;': { 'codepoints': [0x0231C], 'characters': '\u231C' },
+ '&ulcorner;': { 'codepoints': [0x0231C], 'characters': '\u231C' },
+ '&ulcrop;': { 'codepoints': [0x0230F], 'characters': '\u230F' },
+ '&ultri;': { 'codepoints': [0x025F8], 'characters': '\u25F8' },
+ '&umacr;': { 'codepoints': [0x0016B], 'characters': '\u016B' },
+ '&uml': { 'codepoints': [0x000A8], 'characters': '\xA8' },
+ '&uml;': { 'codepoints': [0x000A8], 'characters': '\xA8' },
+ '&uogon;': { 'codepoints': [0x00173], 'characters': '\u0173' },
+ '&uopf;': { 'codepoints': [0x1D566], 'characters': '\uD835\uDD66' },
+ '&uparrow;': { 'codepoints': [0x02191], 'characters': '\u2191' },
+ '&updownarrow;': { 'codepoints': [0x02195], 'characters': '\u2195' },
+ '&upharpoonleft;': { 'codepoints': [0x021BF], 'characters': '\u21BF' },
+ '&upharpoonright;': { 'codepoints': [0x021BE], 'characters': '\u21BE' },
+ '&uplus;': { 'codepoints': [0x0228E], 'characters': '\u228E' },
+ '&upsi;': { 'codepoints': [0x003C5], 'characters': '\u03C5' },
+ '&upsih;': { 'codepoints': [0x003D2], 'characters': '\u03D2' },
+ '&upsilon;': { 'codepoints': [0x003C5], 'characters': '\u03C5' },
+ '&upuparrows;': { 'codepoints': [0x021C8], 'characters': '\u21C8' },
+ '&urcorn;': { 'codepoints': [0x0231D], 'characters': '\u231D' },
+ '&urcorner;': { 'codepoints': [0x0231D], 'characters': '\u231D' },
+ '&urcrop;': { 'codepoints': [0x0230E], 'characters': '\u230E' },
+ '&uring;': { 'codepoints': [0x0016F], 'characters': '\u016F' },
+ '&urtri;': { 'codepoints': [0x025F9], 'characters': '\u25F9' },
+ '&uscr;': { 'codepoints': [0x1D4CA], 'characters': '\uD835\uDCCA' },
+ '&utdot;': { 'codepoints': [0x022F0], 'characters': '\u22F0' },
+ '&utilde;': { 'codepoints': [0x00169], 'characters': '\u0169' },
+ '&utri;': { 'codepoints': [0x025B5], 'characters': '\u25B5' },
+ '&utrif;': { 'codepoints': [0x025B4], 'characters': '\u25B4' },
+ '&uuarr;': { 'codepoints': [0x021C8], 'characters': '\u21C8' },
+ '&uuml': { 'codepoints': [0x000FC], 'characters': '\xFC' },
+ '&uuml;': { 'codepoints': [0x000FC], 'characters': '\xFC' },
+ '&uwangle;': { 'codepoints': [0x029A7], 'characters': '\u29A7' },
+ '&vArr;': { 'codepoints': [0x021D5], 'characters': '\u21D5' },
+ '&vBar;': { 'codepoints': [0x02AE8], 'characters': '\u2AE8' },
+ '&vBarv;': { 'codepoints': [0x02AE9], 'characters': '\u2AE9' },
+ '&vDash;': { 'codepoints': [0x022A8], 'characters': '\u22A8' },
+ '&vangrt;': { 'codepoints': [0x0299C], 'characters': '\u299C' },
+ '&varepsilon;': { 'codepoints': [0x003F5], 'characters': '\u03F5' },
+ '&varkappa;': { 'codepoints': [0x003F0], 'characters': '\u03F0' },
+ '&varnothing;': { 'codepoints': [0x02205], 'characters': '\u2205' },
+ '&varphi;': { 'codepoints': [0x003D5], 'characters': '\u03D5' },
+ '&varpi;': { 'codepoints': [0x003D6], 'characters': '\u03D6' },
+ '&varpropto;': { 'codepoints': [0x0221D], 'characters': '\u221D' },
+ '&varr;': { 'codepoints': [0x02195], 'characters': '\u2195' },
+ '&varrho;': { 'codepoints': [0x003F1], 'characters': '\u03F1' },
+ '&varsigma;': { 'codepoints': [0x003C2], 'characters': '\u03C2' },
+ '&varsubsetneq;': { 'codepoints': [0x0228A, 0x0FE00], 'characters': '\u228A\uFE00' },
+ '&varsubsetneqq;': { 'codepoints': [0x02ACB, 0x0FE00], 'characters': '\u2ACB\uFE00' },
+ '&varsupsetneq;': { 'codepoints': [0x0228B, 0x0FE00], 'characters': '\u228B\uFE00' },
+ '&varsupsetneqq;': { 'codepoints': [0x02ACC, 0x0FE00], 'characters': '\u2ACC\uFE00' },
+ '&vartheta;': { 'codepoints': [0x003D1], 'characters': '\u03D1' },
+ '&vartriangleleft;': { 'codepoints': [0x022B2], 'characters': '\u22B2' },
+ '&vartriangleright;': { 'codepoints': [0x022B3], 'characters': '\u22B3' },
+ '&vcy;': { 'codepoints': [0x00432], 'characters': '\u0432' },
+ '&vdash;': { 'codepoints': [0x022A2], 'characters': '\u22A2' },
+ '&vee;': { 'codepoints': [0x02228], 'characters': '\u2228' },
+ '&veebar;': { 'codepoints': [0x022BB], 'characters': '\u22BB' },
+ '&veeeq;': { 'codepoints': [0x0225A], 'characters': '\u225A' },
+ '&vellip;': { 'codepoints': [0x022EE], 'characters': '\u22EE' },
+ '&verbar;': { 'codepoints': [0x0007C], 'characters': '\x7C' },
+ '&vert;': { 'codepoints': [0x0007C], 'characters': '\x7C' },
+ '&vfr;': { 'codepoints': [0x1D533], 'characters': '\uD835\uDD33' },
+ '&vltri;': { 'codepoints': [0x022B2], 'characters': '\u22B2' },
+ '&vnsub;': { 'codepoints': [0x02282, 0x020D2], 'characters': '\u2282\u20D2' },
+ '&vnsup;': { 'codepoints': [0x02283, 0x020D2], 'characters': '\u2283\u20D2' },
+ '&vopf;': { 'codepoints': [0x1D567], 'characters': '\uD835\uDD67' },
+ '&vprop;': { 'codepoints': [0x0221D], 'characters': '\u221D' },
+ '&vrtri;': { 'codepoints': [0x022B3], 'characters': '\u22B3' },
+ '&vscr;': { 'codepoints': [0x1D4CB], 'characters': '\uD835\uDCCB' },
+ '&vsubnE;': { 'codepoints': [0x02ACB, 0x0FE00], 'characters': '\u2ACB\uFE00' },
+ '&vsubne;': { 'codepoints': [0x0228A, 0x0FE00], 'characters': '\u228A\uFE00' },
+ '&vsupnE;': { 'codepoints': [0x02ACC, 0x0FE00], 'characters': '\u2ACC\uFE00' },
+ '&vsupne;': { 'codepoints': [0x0228B, 0x0FE00], 'characters': '\u228B\uFE00' },
+ '&vzigzag;': { 'codepoints': [0x0299A], 'characters': '\u299A' },
+ '&wcirc;': { 'codepoints': [0x00175], 'characters': '\u0175' },
+ '&wedbar;': { 'codepoints': [0x02A5F], 'characters': '\u2A5F' },
+ '&wedge;': { 'codepoints': [0x02227], 'characters': '\u2227' },
+ '&wedgeq;': { 'codepoints': [0x02259], 'characters': '\u2259' },
+ '&weierp;': { 'codepoints': [0x02118], 'characters': '\u2118' },
+ '&wfr;': { 'codepoints': [0x1D534], 'characters': '\uD835\uDD34' },
+ '&wopf;': { 'codepoints': [0x1D568], 'characters': '\uD835\uDD68' },
+ '&wp;': { 'codepoints': [0x02118], 'characters': '\u2118' },
+ '&wr;': { 'codepoints': [0x02240], 'characters': '\u2240' },
+ '&wreath;': { 'codepoints': [0x02240], 'characters': '\u2240' },
+ '&wscr;': { 'codepoints': [0x1D4CC], 'characters': '\uD835\uDCCC' },
+ '&xcap;': { 'codepoints': [0x022C2], 'characters': '\u22C2' },
+ '&xcirc;': { 'codepoints': [0x025EF], 'characters': '\u25EF' },
+ '&xcup;': { 'codepoints': [0x022C3], 'characters': '\u22C3' },
+ '&xdtri;': { 'codepoints': [0x025BD], 'characters': '\u25BD' },
+ '&xfr;': { 'codepoints': [0x1D535], 'characters': '\uD835\uDD35' },
+ '&xhArr;': { 'codepoints': [0x027FA], 'characters': '\u27FA' },
+ '&xharr;': { 'codepoints': [0x027F7], 'characters': '\u27F7' },
+ '&xi;': { 'codepoints': [0x003BE], 'characters': '\u03BE' },
+ '&xlArr;': { 'codepoints': [0x027F8], 'characters': '\u27F8' },
+ '&xlarr;': { 'codepoints': [0x027F5], 'characters': '\u27F5' },
+ '&xmap;': { 'codepoints': [0x027FC], 'characters': '\u27FC' },
+ '&xnis;': { 'codepoints': [0x022FB], 'characters': '\u22FB' },
+ '&xodot;': { 'codepoints': [0x02A00], 'characters': '\u2A00' },
+ '&xopf;': { 'codepoints': [0x1D569], 'characters': '\uD835\uDD69' },
+ '&xoplus;': { 'codepoints': [0x02A01], 'characters': '\u2A01' },
+ '&xotime;': { 'codepoints': [0x02A02], 'characters': '\u2A02' },
+ '&xrArr;': { 'codepoints': [0x027F9], 'characters': '\u27F9' },
+ '&xrarr;': { 'codepoints': [0x027F6], 'characters': '\u27F6' },
+ '&xscr;': { 'codepoints': [0x1D4CD], 'characters': '\uD835\uDCCD' },
+ '&xsqcup;': { 'codepoints': [0x02A06], 'characters': '\u2A06' },
+ '&xuplus;': { 'codepoints': [0x02A04], 'characters': '\u2A04' },
+ '&xutri;': { 'codepoints': [0x025B3], 'characters': '\u25B3' },
+ '&xvee;': { 'codepoints': [0x022C1], 'characters': '\u22C1' },
+ '&xwedge;': { 'codepoints': [0x022C0], 'characters': '\u22C0' },
+ '&yacute': { 'codepoints': [0x000FD], 'characters': '\xFD' },
+ '&yacute;': { 'codepoints': [0x000FD], 'characters': '\xFD' },
+ '&yacy;': { 'codepoints': [0x0044F], 'characters': '\u044F' },
+ '&ycirc;': { 'codepoints': [0x00177], 'characters': '\u0177' },
+ '&ycy;': { 'codepoints': [0x0044B], 'characters': '\u044B' },
+ '&yen': { 'codepoints': [0x000A5], 'characters': '\xA5' },
+ '&yen;': { 'codepoints': [0x000A5], 'characters': '\xA5' },
+ '&yfr;': { 'codepoints': [0x1D536], 'characters': '\uD835\uDD36' },
+ '&yicy;': { 'codepoints': [0x00457], 'characters': '\u0457' },
+ '&yopf;': { 'codepoints': [0x1D56A], 'characters': '\uD835\uDD6A' },
+ '&yscr;': { 'codepoints': [0x1D4CE], 'characters': '\uD835\uDCCE' },
+ '&yucy;': { 'codepoints': [0x0044E], 'characters': '\u044E' },
+ '&yuml': { 'codepoints': [0x000FF], 'characters': '\xFF' },
+ '&yuml;': { 'codepoints': [0x000FF], 'characters': '\xFF' },
+ '&zacute;': { 'codepoints': [0x0017A], 'characters': '\u017A' },
+ '&zcaron;': { 'codepoints': [0x0017E], 'characters': '\u017E' },
+ '&zcy;': { 'codepoints': [0x00437], 'characters': '\u0437' },
+ '&zdot;': { 'codepoints': [0x0017C], 'characters': '\u017C' },
+ '&zeetrf;': { 'codepoints': [0x02128], 'characters': '\u2128' },
+ '&zeta;': { 'codepoints': [0x003B6], 'characters': '\u03B6' },
+ '&zfr;': { 'codepoints': [0x1D537], 'characters': '\uD835\uDD37' },
+ '&zhcy;': { 'codepoints': [0x00436], 'characters': '\u0436' },
+ '&zigrarr;': { 'codepoints': [0x021DD], 'characters': '\u21DD' },
+ '&zopf;': { 'codepoints': [0x1D56B], 'characters': '\uD835\uDD6B' },
+ '&zscr;': { 'codepoints': [0x1D4CF], 'characters': '\uD835\uDCCF' },
+ '&zwj;': { 'codepoints': [0x0200D], 'characters': '\u200D' },
+ '&zwnj;': { 'codepoints': [0x0200C], 'characters': '\u200C' }
+};
diff --git a/testing/web-platform/tests/html/syntax/parsing/named-character-references.html b/testing/web-platform/tests/html/syntax/parsing/named-character-references.html
new file mode 100644
index 0000000000..d09915cd97
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/named-character-references.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests for known named character references</title>
+<meta name=viewport content="width=device-width">
+<!-- Alternative output: http://mathias.html5.org/tests/html/named-character-references/ -->
+<div id=log></div>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=named-character-references-data.js></script>
+<script>
+ (function() {
+
+ function pad(string, totalCharacters) {
+ return totalCharacters < string.length ? string : (Array(totalCharacters + 1).join('0') + string).slice(-totalCharacters);
+ }
+
+ var dummy = document.createElement('p');
+
+ Object.keys(data).forEach(function(entity) {
+ var object = data[entity];
+ dummy.innerHTML = entity;
+ test(
+ function() {
+ assert_equals(
+ dummy.textContent,
+ object.characters
+ );
+ },
+ entity + ' should match ' + object.codepoints.map(function(codePoint) {
+ return 'U+' + pad(codePoint.toString(16).toUpperCase(), 5);
+ }).join(' ')
+ );
+ });
+
+ }());
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/no-doctype-name.html b/testing/web-platform/tests/html/syntax/parsing/no-doctype-name.html
new file mode 100644
index 0000000000..cfd57e41d9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/no-doctype-name.html
@@ -0,0 +1,22 @@
+<!doctype>
+<meta charset=utf-8>
+<title>Doctype without root name should have empty-string name in the DOM even if null in the tokenizer spec.</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+setup({explicit_done:true});
+window.onload = function() {
+ test(function () {
+ assert_equals(document.doctype.name, "", "Top-level");
+ let iframes = document.getElementsByTagName("iframe");
+ for (let i = 0; i < iframes.length; ++i) {
+ let iframe = iframes[i];
+ assert_equals(iframe.contentDocument.doctype.name, "", iframe.title);
+ }
+ }, "Doctype without root name should have the empty string as the name.");
+ done();
+}
+</script>
+<iframe src="support/no-doctype-name-space.html" title='space'></iframe>
+<iframe src="support/no-doctype-name-line.html" title='line'></iframe>
+<iframe src="support/no-doctype-name-eof.html" title='eof'></iframe>
diff --git a/testing/web-platform/tests/html/syntax/parsing/quotes-in-meta.html b/testing/web-platform/tests/html/syntax/parsing/quotes-in-meta.html
new file mode 100644
index 0000000000..3d1eaf02a2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/quotes-in-meta.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta http-equiv="Content-Type" content='charset="windows-1251'>
+<meta charset=windows-1250>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<link rel=help href="https://html.spec.whatwg.org/#algorithm-for-extracting-a-character-encoding-from-a-meta-element">
+<script>
+test(function() {
+ assert_equals(document.characterSet, "windows-1250");
+});
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/support/DOMContentLoaded-defer.js b/testing/web-platform/tests/html/syntax/parsing/support/DOMContentLoaded-defer.js
new file mode 100644
index 0000000000..35ce9327bf
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/support/DOMContentLoaded-defer.js
@@ -0,0 +1,14 @@
+t.step(function() {
+ assert_false(dcl, "DOMContentLoaded should not have fired before executing " +
+ "a defer script");
+
+ t.step_timeout(function() {
+ assert_false(dcl, "DOMContentLoaded should not have fired before " +
+ "executing a task queued from a defer script");
+ t.step_timeout(function() {
+ assert_true(dcl, "DOMContentLoaded should have fired in a task that " +
+ "was queued after the DOMContentLoaded task was queued");
+ t.done();
+ }, 0);
+ }, 0);
+});
diff --git a/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-eof.html b/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-eof.html
new file mode 100644
index 0000000000..b7717521bb
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-eof.html
@@ -0,0 +1 @@
+<!DOCTYPE \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-line.html b/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-line.html
new file mode 100644
index 0000000000..ba90711872
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-line.html
@@ -0,0 +1,2 @@
+<!DOCTYPE
+>
diff --git a/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-space.html b/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-space.html
new file mode 100644
index 0000000000..f8391aa102
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/support/no-doctype-name-space.html
@@ -0,0 +1 @@
+<!DOCTYPE >
diff --git a/testing/web-platform/tests/html/syntax/parsing/support/svg-script-self-closing.js b/testing/web-platform/tests/html/syntax/parsing/support/svg-script-self-closing.js
new file mode 100644
index 0000000000..6dd286e7bc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/support/svg-script-self-closing.js
@@ -0,0 +1 @@
+scriptSelfClosing = true;
diff --git a/testing/web-platform/tests/html/syntax/parsing/template.js b/testing/web-platform/tests/html/syntax/parsing/template.js
new file mode 100644
index 0000000000..b249fb64c7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template.js
@@ -0,0 +1,214 @@
+ /*
+ * Template code
+ *
+ * A template is just a javascript structure. An element is represented as:
+ *
+ * [tag_name, {attr_name:attr_value}, child1, child2]
+ *
+ * the children can either be strings (which act like text nodes), other templates or
+ * functions (see below)
+ *
+ * A text node is represented as
+ *
+ * ["{text}", value]
+ *
+ * String values have a simple substitution syntax; ${foo} represents a variable foo.
+ *
+ * It is possible to embed logic in templates by using a function in a place where a
+ * node would usually go. The function must either return part of a template or null.
+ *
+ * In cases where a set of nodes are required as output rather than a single node
+ * with children it is possible to just use a list
+ * [node1, node2, node3]
+ *
+ * Usage:
+ *
+ * render(template, substitutions) - take a template and an object mapping
+ * variable names to parameters and return either a DOM node or a list of DOM nodes
+ *
+ * substitute(template, substitutions) - take a template and variable mapping object,
+ * make the variable substitutions and return the substituted template
+ *
+ */
+
+ function is_single_node(template)
+ {
+ return typeof template[0] === "string";
+ }
+
+ function substitute(template, substitutions)
+ {
+ if (typeof template === "function") {
+ var replacement = template(substitutions);
+ if (replacement)
+ {
+ var rv = substitute(replacement, substitutions);
+ return rv;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else if (is_single_node(template))
+ {
+ return substitute_single(template, substitutions);
+ }
+ else
+ {
+ return filter(map(template, function(x) {
+ return substitute(x, substitutions);
+ }), function(x) {return x !== null;});
+ }
+ }
+ expose(substitute, "template.substitute");
+
+ function substitute_single(template, substitutions)
+ {
+ var substitution_re = /\${([^ }]*)}/g;
+
+ function do_substitution(input) {
+ var components = input.split(substitution_re);
+ var rv = [];
+ for (var i=0; i<components.length; i+=2)
+ {
+ rv.push(components[i]);
+ if (components[i+1])
+ {
+ rv.push(substitutions[components[i+1]]);
+ }
+ }
+ return rv;
+ }
+
+ var rv = [];
+ rv.push(do_substitution(String(template[0])).join(""));
+
+ if (template[0] === "{text}") {
+ substitute_children(template.slice(1), rv);
+ } else {
+ substitute_attrs(template[1], rv);
+ substitute_children(template.slice(2), rv);
+ }
+
+ function substitute_attrs(attrs, rv)
+ {
+ rv[1] = {};
+ for (name in template[1])
+ {
+ if (attrs.hasOwnProperty(name))
+ {
+ var new_name = do_substitution(name).join("");
+ var new_value = do_substitution(attrs[name]).join("");
+ rv[1][new_name] = new_value;
+ };
+ }
+ }
+
+ function substitute_children(children, rv)
+ {
+ for (var i=0; i<children.length; i++)
+ {
+ if (children[i] instanceof Object) {
+ var replacement = substitute(children[i], substitutions);
+ if (replacement !== null)
+ {
+ if (is_single_node(replacement))
+ {
+ rv.push(replacement);
+ }
+ else
+ {
+ extend(rv, replacement);
+ }
+ }
+ }
+ else
+ {
+ extend(rv, do_substitution(String(children[i])));
+ }
+ }
+ return rv;
+ }
+
+ return rv;
+ }
+
+ function make_dom_single(template)
+ {
+ if (template[0] === "{text}")
+ {
+ var element = document.createTextNode("");
+ for (var i=1; i<template.length; i++)
+ {
+ element.data += template[i];
+ }
+ }
+ else
+ {
+ var element = document.createElement(template[0]);
+ for (name in template[1]) {
+ if (template[1].hasOwnProperty(name))
+ {
+ element.setAttribute(name, template[1][name]);
+ }
+ }
+ for (var i=2; i<template.length; i++)
+ {
+ if (template[i] instanceof Object)
+ {
+ var sub_element = make_dom(template[i]);
+ element.appendChild(sub_element);
+ }
+ else
+ {
+ var text_node = document.createTextNode(template[i]);
+ element.appendChild(text_node);
+ }
+ }
+ }
+
+ return element;
+ }
+
+
+
+ function make_dom(template, substitutions)
+ {
+ if (is_single_node(template))
+ {
+ return make_dom_single(template);
+ }
+ else
+ {
+ return map(template, function(x) {
+ return make_dom_single(x);
+ });
+ }
+ }
+
+ function render(template, substitutions)
+ {
+ return make_dom(substitute(template, substitutions));
+ }
+ expose(render, "template.render");
+
+function expose(object, name)
+{
+ var components = name.split(".");
+ var target = window;
+ for (var i=0; i<components.length - 1; i++)
+ {
+ if (!(components[i] in target))
+ {
+ target[components[i]] = {};
+ }
+ target = target[components[i]];
+ }
+ target[components[components.length - 1]] = object;
+}
+
+function extend(array, items)
+{
+ Array.prototype.push.apply(array, items);
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-a-foster-parent-element.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-a-foster-parent-element.html
new file mode 100644
index 0000000000..6f6e7a7255
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-a-foster-parent-element.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Template is a foster parent element</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="The last template element with either no table element is below it, or a table element immediately below it, in the stack of open elements is the foster parent element (NOT the template's parent!)">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#foster-parent-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '' +
+ '<div id="tmplParent">' +
+ '<template id="tmpl1">' +
+ '<table id="tbl">' +
+ '<tr><td>Cell 1</td></tr>' +
+ // Misplaced <div>. It should be foster parented
+ '<div id="orphanDiv">Orphan div content</div>' +
+ '<tr><td>Cell 2</td></tr>' +
+ '</table>' +
+ '</template>' +
+ '</div>';
+
+ var template = doc.querySelector('#tmpl1');
+ var div = template.content.querySelector('#orphanDiv');
+
+ assert_equals(div.parentNode, template.content, 'Wrong foster parent element');
+
+}, 'Template is a foster parent element. Test <table> immediately below <template>');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '' +
+ '<div id="tmplParent">' +
+ '<template id="tmpl1">' +
+ '<tr><td>Cell 1</td></tr>' +
+ // Misplaced <div>. It should be foster parented
+ '<div id="orphanDiv">Orphan div content</div>' +
+ '<tr><td>Cell 2</td></tr>' +
+ '</template>' +
+ '</div>';
+
+ var template = doc.querySelector('#tmpl1');
+ var div = template.content.querySelector('#orphanDiv');
+
+ assert_equals(div.parentNode, template.content, 'Wrong foster parent element');
+
+}, 'Template is a foster parent element. Test <template> element without <table>');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-not-a-foster-parent-element.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-not-a-foster-parent-element.html
new file mode 100644
index 0000000000..677dfaf3ef
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-foster-parenting/template-is-not-a-foster-parent-element.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Template is not a foster parent element</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="When template element shouldn't be a foster parent then regular rules of foster parenting should be applied">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#foster-parent-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '' +
+ '<div id="tmplParent">' +
+ '<template id="tmpl1">' +
+ '<div id="fosterParent">' +
+ '<table id="tbl">' +
+ '<tr><td>Cell 1</td></tr>' +
+ // Misplaced <div>. It should be foster parented
+ '<div id="orphanDiv">Orphan div content</div>' +
+ '<tr><td>Cell 2</td></tr>' +
+ '</table>' +
+ '</div>' +
+ '</template>' +
+ '</div>';
+
+ var template = doc.querySelector('#tmpl1');
+ var fosterParent = template.content.querySelector('#fosterParent');
+ var div = template.content.querySelector('#orphanDiv');
+
+ assert_equals(div.parentNode, fosterParent, 'Wrong foster parent element');
+
+}, 'Template is not a foster parent element. '
+ + 'Test the case when <template> is higher in stack of open elements');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '' +
+ '<div id="fosterParent">' +
+ '<table id="tbl">' +
+ '<tr><td><template id="tmpl1">Template content</template></td></tr>' +
+ // Misplaced <div>. It should be foster parented
+ '<div id="orphanDiv">Orphan div content</div>' +
+ '<tr><td>Cell 2</td></tr>' +
+ '</table>' +
+ '</div>' +
+ '</div>';
+
+ var t = doc.querySelector('#tmpl1');
+ var fosterParent = doc.querySelector('#fosterParent');
+ var div = doc.querySelector('#orphanDiv');
+
+ assert_equals(div.parentNode, fosterParent, 'Wrong foster parent element');
+
+}, 'Template is not a foster parent element. '
+ + 'Test the case when <template> is lower in stack of open elements');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/generating-of-implied-end-tags.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/generating-of-implied-end-tags.html
new file mode 100644
index 0000000000..6edce84ef2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/generating-of-implied-end-tags.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: 'In body' insertion mode: when template end tag is met, implied end tags should be generated</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="'In body' insertion mode: when template end tag is met, implied end tags should be generated">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ //No end </td></tr></table> tags. Should be added implicitly
+ doc.body.innerHTML = '<template id="tpl">'
+ + '<table id="tbl"><tr id="tr"><td id="td"></template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(doc.querySelector('#tbl'), null, 'Table element should not be available');
+ assert_equals(doc.querySelector('#tr'), null, 'TR element should not be available');
+ assert_equals(doc.querySelector('#td'), null, 'TD element should not be available');
+
+ assert_not_equals(template.content.querySelector('#tbl'), null,
+ 'Template should contain table element');
+ assert_not_equals(template.content.querySelector('#tr'), null,
+ 'Template should contain TR element');
+ assert_not_equals(template.content.querySelector('#td'), null,
+ 'Template should contain TD element');
+
+}, 'Generating of implied end tags. Test table elements');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ //No end </div> tag. Should be added implicitly
+ doc.body.innerHTML = '<template id="tpl"><div id="dv">Div content</template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(doc.querySelector('#dv'), null, 'DIV element should not be available');
+
+ assert_not_equals(template.content.querySelector('#dv'), null,
+ 'Template should contain DIV element');
+
+}, 'Generating of implied end tags. Test div element');
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ //No end </div> tag. Should be added implicitly after text content
+ doc.body.innerHTML = '<template id="tpl">Template text<div id="dv">Div content</template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(doc.querySelector('#dv'), null, 'DIV element should not be available');
+
+ var div = template.content.querySelector('#dv');
+
+ assert_not_equals( div, null, 'Template should contain DIV element');
+ assert_equals(div.textContent, 'Div content', 'Wrong template content inner text');
+
+}, 'Generating of implied end tags. Test some text and DIV element');
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ // Wrong end tag. Correct end tag must be added implicitly, wrong one ignored
+ doc.body.innerHTML = '<template id="tpl"><div id="dv">Div content</span></template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Wrong number of template\'s children');
+
+ assert_equals(doc.querySelector('#dv'), null, 'DIV element should not be available');
+
+ assert_not_equals(template.content.querySelector('#dv'), null,
+ 'Template should contain DIV element');
+ assert_equals(template.content.querySelector('#dv').textContent,
+ 'Div content', 'Wrong template content inner text');
+
+}, 'Generating of implied end tags. Test wrong end tag');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-table-no-end-tag.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_not_equals(template.content.querySelector('table'), null,
+ 'Template should contain table element');
+ assert_not_equals(template.content.querySelector('tr'), null,
+ 'Template should contain TR element');
+ assert_not_equals(template.content.querySelector('td'), null,
+ 'Template should contain TD element');
+
+}, 'Generating of implied end tags. Test table elements. Loading of HTML document from a file');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-div-no-end-tag.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ var div = template.content.querySelector('div');
+ assert_not_equals(div, null, 'Template should contain div element');
+ assert_equals(div.textContent, 'Hello, template\n ', 'Invalid div contents');
+
+}, 'Generating of implied end tags. Test div element. Loading of HTML document from a file');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-body-token.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-body-token.html
new file mode 100644
index 0000000000..4549f5fecc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-body-token.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: In body insertion mode: parser should ignore BODY token</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+/*
+ * According to http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-contents-insertion-mode
+ * when parser is in "template content" mode and meets <body> tag it should be switched to
+ * "in body" insertion mode.
+ * According to http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition
+ * this token (BODY) should be ignored
+ */
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<body></body>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain BODY element');
+
+}, 'Ignore BODY token. Test empty BODY element assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<body><div>Some content</div></body>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Wrong number of template content children');
+ assert_equals(template.content.firstChild.nodeName, 'DIV',
+ 'Template should contain children of ignored BODY element');
+
+}, 'Ignore BODY token. Test not empty BODY element assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<body><div <div id="div1">Some content</div></body><div id="div2">Some valid content</div>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 2,
+ 'Wrong number of template content children');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain children of the ignored BODY element');
+ assert_not_equals(template.content.querySelector('#div2'), null,
+ 'Template should contain valid element');
+
+}, 'Ignore BODY token. '
+ + 'Test BODY element and some valid element after BODY tag assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<div id="div1">Some valid content</div><body><div id="div2">Some content</div></body>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 2,
+ 'Template cannot contain BODY element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+ assert_not_equals(template.content.querySelector('#div2'), null,
+ 'Template should contain children of the ignored BODY element');
+
+}, 'Ignore BODY token. '
+ + 'Test BODY element and some valid element before BODY tag assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<template id="t2"><body><span>Body!<span></body></template>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template should contain nested template');
+ assert_not_equals(template.content.querySelector('#t2'), null,
+ 'Template should contain nested element');
+
+ var nestedTemplate = template.content.querySelector('#t2');
+
+ assert_equals(nestedTemplate.content.childNodes.length, 1,
+ 'Template cannot contain BODY element');
+ assert_equals(nestedTemplate.content.firstChild.nodeName, 'SPAN',
+ 'Template cannot contain BODY element');
+
+}, 'Ignore BODY token. '
+ + 'Test template with not empty BODY element inside assigned to another '
+ + 'template\'s innerHTML');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-body.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain BODY element');
+
+}, 'Ignore BODY token. '
+ + 'Test loading a HTML file with BODY tag inside template');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-frameset-token.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-frameset-token.html
new file mode 100644
index 0000000000..121115075d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-frameset-token.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: In body insertion mode: parser should ignore FRAMESET token</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="If parser is in 'in body' insertion mode and meets HTML token it should be ignored">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+/*
+ * According to http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-contents-insertion-mode
+ * when parser is in "template content" mode and meets <frameset> tag it should be switched to
+ * "in body" insertion mode.
+ * According to https://html.spec.whatwg.org/multipage/#parsing-main-inbody
+ * this token (FRAMESET) should be ignored
+ */
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<frameset cols="25%,*,25%">'
+ + '<frame src="frame_a.htm">'
+ + '<frame src="frame_b.htm">' + '<frame src="frame_c.htm">'
+ + '</frameset>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain FRAMESET element');
+
+}, 'Ignore frameset token. Test FRAMESET element assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<div id="div1">Some text</div>'
+ + '<frameset cols="25%,*,25%">'
+ + '<frame src="frame_a.htm">'
+ + '<frame src="frame_b.htm">'
+ + '<frame src="frame_c.htm">'
+ + '</frameset>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template cannot contain FRAMESET element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+
+}, 'Ignore frameset token. '
+ + 'Test FRAMESET element and some valid element before it, assigned '
+ + 'to the template\'s innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<frameset cols="25%,*,25%">'
+ + '<frame src="frame_a.htm">'
+ + '<frame src="frame_b.htm">'
+ + '<frame src="frame_c.htm">'
+ + '</frameset><div id="div1">Some text</div>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template cannot contain FRAMESET element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+
+}, 'Ignore frameset token. '
+ + 'Test FRAMESET element and some valid element after it, assigned '
+ + 'to the template\'s innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<template id="t2">'
+ + '<frameset cols="25%,*,25%">'
+ + '<frame src="frame_a.htm">'
+ + '<frame src="frame_b.htm">'
+ + '<frame src="frame_c.htm">'
+ + '</frameset></template>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template should contain nested template');
+ assert_not_equals(template.content.querySelector('#t2'), null,
+ 'Template should contain nested element');
+
+ var nestedTemplate = template.content.querySelector('#t2');
+
+ assert_equals(nestedTemplate.content.childNodes.length, 0,
+ 'Template cannot contain FRAMESET element');
+
+}, 'Ignore frameset token. '
+ + 'Test FRAMESET tag inside template tag assigned to another template\'s innerHTML');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-frameset.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain FRAMESET element');
+}, 'Ignore frameset token. Test loading a HTML file with FRAMESET tag inside template');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-head-token.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-head-token.html
new file mode 100644
index 0000000000..9b14df9177
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-head-token.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: In body insertion mode: parser should ignore HEAD token</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="If parser is in 'in body' insertion mode and meets HEAD token it should be ignored">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+/*
+ * According to http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-contents-insertion-mode
+ * when parser is in "template content" mode and meets <head> tag it should be switched to
+ * "in body" insertion mode.
+ * According to https://html.spec.whatwg.org/multipage/#parsing-main-inbody
+ * this token (HEAD) should be ignored
+ */
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<head></head>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain HEAD element');
+
+}, 'Ignore HEAD token. Test empty HEAD element assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<head><title>test</title></head>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Wrong number of template content children');
+ assert_equals(template.content.firstChild.nodeName, 'TITLE',
+ 'Template should contain children of ignored HEAD element');
+
+}, 'Ignore HEAD token. Test not empty HEAD element assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<div id="div1">Some text</div><head><title>test</title></head>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 2,
+ 'Wrong number of template content children');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+ assert_equals(template.content.lastChild.tagName, 'TITLE',
+ 'Template should contain children of ignored HEAD element');
+
+}, 'Ignore HEAD token. '
+ + 'Test HEAD element and some valid element before it, assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<head><title>test</title></head><div id="div1">Some text</div>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 2,
+ 'Wrong number of template content children');
+ assert_equals(template.content.firstChild.tagName, 'TITLE',
+ 'Template should contain children of ignored HEAD element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+
+}, 'Ignore HEAD token. '
+ + 'Test HEAD element and some valid element after it, assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<template id="t2"><head><title>test</title></head></template>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template should contain nested template');
+ assert_not_equals(template.content.querySelector('#t2'), null,
+ 'Template should contain nested element');
+
+ var nestedTemplate = template.content.querySelector('#t2');
+
+ assert_equals(nestedTemplate.content.childNodes.length, 1,
+ 'Wrong number of template content children');
+ assert_equals(nestedTemplate.content.firstChild.tagName, 'TITLE',
+ 'Template should contain children of ignored HEAD element');
+
+}, 'Ignore HEAD token. '
+ + 'Test HEAD tag inside template tag assigned to another template\'s innerHTML');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-head.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain HEAD element');
+
+}, 'Ignore HEAD token. Test loading a HTML file with HEAD tag inside template');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-html-token.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-html-token.html
new file mode 100644
index 0000000000..5c53be8425
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-html-token.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: In body insertion mode: parser should ignore HTML token</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="If parser is in 'in body' insertion mode and meets HTML token it should be ignored">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+/*
+ * According to http://www.w3.org/TR/2013/WD-html-templates-20130214/#template-contents-insertion-mode
+ * when parser is in "template content" mode and meets <html> tag it should be switched to
+ * "in body" insertion mode.
+ * According to https://html.spec.whatwg.org/multipage/#parsing-main-inbody
+ * this token (HTML) should be ignored
+ */
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<html><body></body></html>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain HTML element');
+
+}, 'Ignore HTML token. Test HTML element assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<div id="div1">Some text</div><html><body></body></html>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template cannot contain HTML element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+
+}, 'Ignore HTML token.'
+ + 'Test HTML element and some valid element before it, assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<html><body></body></html><div id="div1">Some text</div>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template cannot contain HTML element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+
+}, 'Ignore HTML token. '
+ + 'Test HEAD element and some valid element after it, assigned to template innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<template id="t2"><html><body></body></html></template>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template should contain nested template');
+ assert_not_equals(template.content.querySelector('#t2'), null,
+ 'Template should contain nested element');
+
+ var nestedTemplate = template.content.querySelector('#t2');
+
+ assert_equals(nestedTemplate.content.childNodes.length, 0,
+ 'Template cannot contain HTML element');
+
+}, 'Ignore HTML token. '
+ + 'Test HTML tag inside template tag assigned to another template\'s innerHTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<html><div id="div1">Some text</div></html>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template cannot contain HTML element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain a valid element');
+
+}, 'Ignore HTML token. Test some valid element inside HTML element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<html><body><div id="div1">Some text</div><body></html>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Template cannot contain HTML element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid element');
+
+}, 'Ignore HTML token. Test valid element inside HTML and BODY elements');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var template = doc.createElement('template');
+
+ template.innerHTML = '<html><span id="span1">Span</span><body><div id="div1">Some text</div><body></html>';
+
+ doc.body.appendChild(template);
+
+ assert_equals(template.content.childNodes.length, 2,
+ 'Template cannot contain HTML element');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Template should contain valid DIV element');
+
+ assert_not_equals(template.content.querySelector('#span1'), null,
+ 'Template should contain valid SPAN element');
+
+}, 'Ignore HTML token. Test valid element inside and between HTML and BODY elements');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-html.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ assert_equals(template.content.childNodes.length, 0,
+ 'Template cannot contain HTML element');
+
+}, 'Ignore HTML token. Test loading a HTML file with HTML tag inside template');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-body.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-body.html
new file mode 100644
index 0000000000..738c86106a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-body.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: In body insertion mode: Template contains a start tag whose tag name is body</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="If the stack of open elements has a template element in html scope then ignore <body> the token. (fragment or template contents case)">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<template id="tmpl"><body></template>';
+
+ var template = doc.querySelector('#tmpl');
+
+ assert_equals(template.content.childNodes.length, 0, 'Element must be ignored');
+
+}, 'In body insertion mode: Template contains a start tag whose tag name is body.'
+ + 'Test <body> tag only');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<template id="tmpl"><body>Body text content</body></template>';
+
+ var template = doc.querySelector('#tmpl');
+
+ assert_equals(template.content.querySelector('body'), null,
+ '<body> element must be ignored');
+ assert_equals(template.content.childNodes.length, 1, 'Text shouldn\'t be ignored');
+ assert_equals(template.content.firstChild.nodeType, Node.TEXT_NODE,
+ 'Text shouldn\'t be ignored');
+
+}, 'In body insertion mode: Template contains a start tag whose tag name is body. '
+ + 'Test <body> tag containing some text');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<template id="tmpl"><body>'
+ + '<div id="div1">DIV 1</div>'
+ + '<div id="div2">DIV 2</div>'
+ + '</body></template>';
+
+ var template = doc.querySelector('#tmpl');
+
+ assert_equals(template.content.querySelector('body'), null,
+ '<body> element must be ignored');
+ assert_equals(template.content.childNodes.length, 2,
+ 'Only body tag should be ignored');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Children of <body tag shouldn\'t be ignored');
+ assert_not_equals(template.content.querySelector('#div2'), null,
+ 'Children of <body tag shouldn\'t be ignored');
+
+}, 'In body insertion mode: Template contains a start tag whose tag name is body. '
+ + 'Test <body> tag containing some other elements');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<template id="tmpl1"><template id="tmpl2"><body>'
+ + '<div id="div1">DIV 1</div>'
+ + '<div id="div2">DIV 2</div>'
+ + '</body></template></template>';
+
+ var template = doc.querySelector('#tmpl1').content.querySelector('#tmpl2');
+
+ assert_equals(template.content.querySelector('body'), null,
+ '<body> element must be ignored');
+ assert_equals(template.content.childNodes.length, 2,
+ 'Only body tag should be ignored');
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Children of <body tag shouldn\'t be ignored');
+ assert_not_equals(template.content.querySelector('#div2'), null,
+ 'Children of <body tag shouldn\'t be ignored');
+
+}, 'In body insertion mode: Template contains a start tag whose tag name is body. '
+ + 'Test nested template tag containing <body> tag with some other elements');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-html.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-html.html
new file mode 100644
index 0000000000..33c43cd50c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-html.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: In body insertion mode: A start tag whose tag name is html</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="If HTML parser is in 'in body' insertion mode and meets HTML start tag, then for each attribute on the token, check to see if the attribute is already present on the top element of the stack of open elements. If it is not, add the attribute and its corresponding value to that element">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+// test <template><html class="htmlClass"></html></template><html id="htmlId" tabindex="5">
+// id attribute should be added to root <html> element
+// tabindex attribute should not be modified
+//class attribute should be ignored
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/html-start-tag.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.body.querySelector('template');
+
+ var html = doc.documentElement;
+
+ assert_equals(html.getAttribute('tabindex'), '5', 'Attribute should be accessible');
+ assert_equals(html.getAttribute('id'), 'htmlId',
+ 'Attribute \'id\' should be added and accessible');
+ assert_false(html.hasAttribute('class'), 'Attribute \'class\' should be ignored');
+ assert_equals(template.content.childNodes.length, 0, 'Template should not contain HTML element');
+
+
+}, 'In body insertion mode: html start tag should add only absent attributes');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/template-end-tag-without-start-one.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/template-end-tag-without-start-one.html
new file mode 100644
index 0000000000..ca124ee798
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/template-end-tag-without-start-one.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: 'In body' insertion mode: Template end tag without start one. Element should be ignored</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="If parser in 'in body' insertion mode meets template end tag and if the stack of open elements has no template element in html scope, then this is a parse error; ignore the token">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-body-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '</template>';
+
+ assert_equals(doc.body.childNodes.length, 0, 'Element must be ignored');
+
+}, '</template> tag in HTML body without start one should be ignored');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<template id="tmpl"></template></template>';
+
+ assert_equals(doc.body.childNodes.length, 1, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Element should present it document body');
+
+}, '</template> tag in HTML body without start one should be ignored. '
+ + 'Test valid <template> element and </template> tag after it');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '</template><template id="tmpl"></template>';
+
+ assert_equals(doc.body.childNodes.length, 1, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Element should present it document body');
+
+}, '</template> tag in HTML body without start one should be ignored. '
+ + 'Test valid <template> element and </template> tag before it');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '</template><template id="tmpl"></template><title></title>';
+
+ assert_equals(doc.body.childNodes.length, 2, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Valid element should present it document body');
+ assert_not_equals(doc.querySelector('title'), null,
+ 'Valid title element should present it document body');
+
+}, '</template> tag in HTML body without start one should be ignored. '
+ + 'Test valid <template> element, <title> element and </template> tag before them');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<template id="tmpl"></template><title></title></template>';
+
+ assert_equals(doc.body.childNodes.length, 2, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Valid element should present it document body');
+ assert_not_equals(doc.querySelector('title'), null,
+ 'Valid title element should present it document body');
+
+}, '</template> tag in HTML body without start one should be ignored. '
+ + 'Test valid <template> element, <title> element and </template> tag after them');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-body.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ assert_equals(doc.body.querySelector('template'), null,
+ '</template> must be ignored');
+ assert_not_equals(doc.body.querySelector('div'), null,
+ 'Valid element should present it document body');
+
+}, '</template> tag in HTML body without start one should be ignored. '
+ + 'Test HTML document loaded from file');
+
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-frameset-insertion-mode/end-tag-frameset.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-frameset-insertion-mode/end-tag-frameset.html
new file mode 100644
index 0000000000..f03f5a30bc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-frameset-insertion-mode/end-tag-frameset.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: additions to 'in frameset' insertion mode</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="If parser is in 'in frameset' insertion mode then a start tag or an end tag whose name is 'template' is a parsing error">
+<link rel="help" href="https://www.w3.org/TR/2015/WD-html51-20151008/syntax.html#parsing-main-inframeset">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/frameset-end-tag.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var frameset = doc.querySelector('frameset');
+ assert_equals(frameset.children.length, 0, 'Wrong number of frameset children elements');
+
+}, '<template> tag should be ignored in "in frameset" insertion mode');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/generating-of-implied-end-tags.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/generating-of-implied-end-tags.html
new file mode 100644
index 0000000000..2f7e6f63c2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/generating-of-implied-end-tags.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: When template end tag is met, implied end tags should be generated</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="When template end tag is met, implied end tags should be generated">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-head-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ //No end </td></tr></table> tags. Should be added implicitly
+ doc.head.innerHTML = '<template id="tpl">'
+ + '<table id="tbl"><tr id="tr"><td id="td"></template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(doc.querySelector('#tbl'), null, 'Table element should not be available');
+ assert_equals(doc.querySelector('#tr'), null, 'TR element should not be available');
+ assert_equals(doc.querySelector('#td'), null, 'TD element should not be available');
+
+ assert_not_equals(template.content.querySelector('#tbl'), null,
+ 'Template should contain table element');
+ assert_not_equals(template.content.querySelector('#tr'), null,
+ 'Template should contain TR element');
+ assert_not_equals(template.content.querySelector('#td'), null,
+ 'Template should contain TD element');
+
+}, 'Generating of implied end tags. Test table elements');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ //No end </div> tag. Should be added implicitly
+ doc.head.innerHTML = '<template id="tpl"><div id="dv">Div content</template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(doc.querySelector('#dv'), null, 'DIV element should not be available');
+
+ assert_not_equals(template.content.querySelector('#dv'), null,
+ 'Template should contain DIV element');
+
+}, 'Generating of implied end tags. Test DIV element');
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ //No end </div> tag. Should be added implicitly after text content
+ doc.head.innerHTML = '<template id="tpl">Template text<div id="dv">Div content</template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(doc.querySelector('#dv'), null, 'DIV element should not be available');
+
+ var div = template.content.querySelector('#dv');
+
+ assert_not_equals( div, null, 'Template should contain DIV element');
+ assert_equals(div.textContent, 'Div content', 'Wrong template content inner text');
+
+}, 'Generating of implied end tags. Test some text and DIV element');
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ // Wrong end tag. Correct end tag must be added implicitly, wrong one ignored
+ doc.head.innerHTML = '<template id="tpl"><div id="dv">Div content</span></template>';
+
+ var template = doc.querySelector('#tpl');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ assert_equals(template.content.childNodes.length, 1,
+ 'Wrong number of template\'s children');
+
+ assert_equals(doc.querySelector('#dv'), null, 'DIV element should not be available');
+
+ assert_not_equals(template.content.querySelector('#dv'), null,
+ 'Template should contain DIV element');
+ assert_equals(template.content.querySelector('#dv').textContent,
+ 'Div content', 'Wrong template content inner text');
+
+}, 'Generating of implied end tags. Test wrong end tag');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/head-template-contents-table-no-end-tag.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.head.querySelector('template');
+
+ assert_not_equals(template, null,
+ 'Template element must be parsed');
+
+ assert_not_equals(template.content.querySelector('table'), null,
+ 'Template should contain table element');
+ assert_not_equals(template.content.querySelector('tr'), null,
+ 'Template should contain TR element');
+ assert_not_equals(template.content.querySelector('td'), null,
+ 'Template should contain TD element');
+
+}, 'Generating of implied end tags. Test table elements. Load HTML document from file');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/head-template-contents-div-no-end-tag.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.head.querySelector('template');
+
+ assert_not_equals(template, null, 'Template element must be parsed');
+
+ var div = template.content.querySelector('div');
+ assert_not_equals(div, null, 'Template should contain div element');
+ assert_equals(div.textContent, 'Hello, template\n ', 'Invalid div contents');
+
+}, 'Generating of implied end tags. Test div element. Load HTML document from file');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/template-end-tag-without-start-one.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/template-end-tag-without-start-one.html
new file mode 100644
index 0000000000..ccb43341bc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/template-end-tag-without-start-one.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Template end tag without start one. Element should be ignored</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="a.semenov@unipro.ru">
+<meta name="assert" content="If parser in 'in head' insertion mode meets template end tag and if the stack of open elements has no template element in html scope, then this is a parse error; ignore the token">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-head-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.head.innerHTML = '</template>';
+
+ assert_equals(doc.head.childNodes.length, 0, 'Element must be ignored');
+
+}, '</template> tag in HTML head without start one should be ignored');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.head.innerHTML = '<template id="tmpl"></template></template>';
+
+ assert_equals(doc.head.childNodes.length, 1, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Element should present it document head');
+
+}, '</template> tag in HTML head without start one should be ignored. '
+ + 'Test valid <template> element and </template> tag after it');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.head.innerHTML = '</template><template id="tmpl"></template>';
+
+ assert_equals(doc.head.childNodes.length, 1, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Element should present it document head');
+
+}, '</template> tag in HTML head without start one should be ignored. '
+ + 'Test valid <template> element and </template> tag before it');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.head.innerHTML = '</template><template id="tmpl"></template><title></title>';
+
+ assert_equals(doc.head.childNodes.length, 2, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Valid element should present it document head');
+ assert_not_equals(doc.querySelector('title'), null,
+ 'Valid title element should present it document head');
+
+}, '</template> tag in HTML head without start one should be ignored. '
+ + 'Test valid <template> element, <title> element and </template> tag before them');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.head.innerHTML = '<template id="tmpl"></template><title></title></template>';
+
+ assert_equals(doc.head.childNodes.length, 2, 'Element must be ignored');
+ assert_not_equals(doc.querySelector('#tmpl'), null,
+ 'Valid element should present it document head');
+ assert_not_equals(doc.querySelector('title'), null,
+ 'Valid title element should present it document head');
+
+}, '</template> tag in HTML head without start one should be ignored. '
+ + 'Test valid <template> element, <title> element and </template> tag after them');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/end-template-tag-in-head.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ assert_equals(doc.head.querySelector('template'), null, '</template> must be ignored');
+ assert_not_equals(doc.head.querySelector('title'), null,
+ 'Valid element should present it document head');
+
+}, '</template> tag in HTML head without start one should be ignored. '
+ + 'Test HTML document loaded from file');
+
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-table-insertion-mode/end-tag-table.html b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-table-insertion-mode/end-tag-table.html
new file mode 100644
index 0000000000..feb2eb1080
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/additions-to-the-in-table-insertion-mode/end-tag-table.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: 'In table' insertion mode: ignore TABLE end tag</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="If parser is in 'in table' insertion mode and end tag table is met the ignore this token">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#in-table-addition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<table id="table">'
+ + '<template id="template">'
+ + '</table>'
+ + '</template>'
+ + '<tr><td></td></tr>'
+ + '</table>';
+
+ var table = doc.querySelector('#table');
+ var template = table.querySelector('#template');
+
+ assert_equals(table.childNodes.length, 2, 'Wrong number of table children');
+ assert_not_equals(template, null, 'Template element must be parsed');
+ assert_equals(table.rows.length, 1, 'Wrong number of table rows');
+ assert_equals(template.childNodes.length, 0, 'Wrong number of the template child nodes');
+ assert_equals(template.content.childNodes.length, 0,
+ 'Wrong number of the template child nodes');
+
+
+}, 'In table insertion mode. Ignore </table> token');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/appending-to-a-template/template-child-nodes.html b/testing/web-platform/tests/html/syntax/parsing/template/appending-to-a-template/template-child-nodes.html
new file mode 100644
index 0000000000..6292c35222
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/appending-to-a-template/template-child-nodes.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: HTML parser appends child nodes only to the template contents node</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="HTML parser must append template's child nodes only to the template contents node.">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#appending-to-a-template">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ assert_equals(template.childNodes.length, 0, 'Wrong number of template child nodes');
+ assert_equals(template.content.childNodes.length, 2,
+ 'Wrong number of template content child nodes');
+
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Element is absent in the template content');
+ assert_not_equals(template.content.querySelector('#div2'), null,
+ 'Element is absent in the template content');
+
+}, 'Template child nodes must be appended to template content node');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">' +
+ '<div id="div1">This is div inside template</div>' +
+ '<div id="div2">This is another div inside template</div>' +
+ '<template id="tmpl2">' +
+ '<div id="div3">This is div inside nested template</div>' +
+ '<div id="div4">This is another div inside nested template</div>' +
+ '</template>' +
+ '</template>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ assert_equals(template.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+ assert_equals(template.content.childNodes.length, 3,
+ 'Wrong number of template content child nodes');
+
+ assert_not_equals(template.content.querySelector('#div1'), null,
+ 'Element is absent in the template content');
+ assert_not_equals(template.content.querySelector('#div2'), null,
+ 'Element is absent in the template content');
+
+ var nestedTemplate = template.content.querySelector('#tmpl2');
+
+ assert_equals(nestedTemplate.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+ assert_equals(nestedTemplate.content.childNodes.length, 2,
+ 'Wrong number of nested template content child nodes');
+
+ assert_not_equals(nestedTemplate.content.querySelector('#div3'), null,
+ 'Element is absent in the template content');
+ assert_not_equals(nestedTemplate.content.querySelector('#div4'), null,
+ 'Element is absent in the template content');
+
+}, 'Template child nodes must be appended to template content. Test nested template');
+
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.childNodes.length, 0, 'Wrong number of template child nodes');
+
+ assert_not_equals(template.content.querySelector('div'), null,
+ 'Element is absent in the template content');
+
+}, 'Template child nodes must be appended to template content node. '
+ + 'Load HTML document from a file');
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-nested.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ assert_equals(template.childNodes.length, 0, 'Wrong number of template child nodes');
+
+ var nestedTemplate = template.content.querySelector('template');
+
+ assert_not_equals(nestedTemplate, null,
+ 'Element is absent in the template content');
+
+ assert_equals(nestedTemplate.childNodes.length, 0,
+ 'Wrong number of template child nodes');
+
+ assert_not_equals(nestedTemplate.content.querySelector('div'), null,
+ 'Element is absent in the template content');
+
+}, 'Template child nodes must be appended to nested template content node. '
+ + 'Load HTML document from a file');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-body-context.html b/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-body-context.html
new file mode 100644
index 0000000000..07af33d01e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-body-context.html
@@ -0,0 +1,183 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Clearing stack back to a table body context</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="Clearing the stack back to a table body context must be aborted if the current node is template">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#clearing-the-stack">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+function doTest(doc, tagToTest, templateInnerHTML, id, tagName, bodiesNum = null, footerIsNull,
+ footerId, headerIsNull, headerId) {
+
+ doc.body.innerHTML = '' +
+ '<table id="tbl">' +
+ '<' + tagToTest + '>' +
+ '<template id="tmpl1">' +
+ // When parser meets <tr>, </tbody>, </tfoot>, </thead>, <caption>, <col>,
+ // <colgroup>, <tbody>, <tfoot>, <thead>, </table>
+ // stack must be cleared back to table body context. But <template> tag should
+ // abort this
+ templateInnerHTML +
+ '</template>' +
+ '<tr id="tr">' +
+ '<td id="td">' +
+ '</td>' +
+ '</tr>' +
+ '</' + tagToTest + '>' +
+ '</table>';
+
+ var table = doc.querySelector('#tbl');
+ var tr = doc.querySelector('#tr');
+ var td = doc.querySelector('#td');
+ var template = doc.querySelector('#tmpl1');
+
+ assert_equals(table.rows.length, 1, 'Wrong number of table rows');
+ assert_equals(table.rows[0].cells.length, 1, 'Wrong number of table cells');
+ if (id !== null) {
+ assert_not_equals(template.content.querySelector('#' + id), null,
+ 'Element should present in the template content');
+ }
+ if (tagName !== null) {
+ assert_equals(template.content.querySelector('#' + id).tagName, tagName,
+ 'Wrong element in the template content');
+ }
+
+ assert_equals(table.caption, null, 'Table should have no caption');
+
+ if (bodiesNum !== null) {
+ assert_equals(table.tBodies.length, bodiesNum, 'Table should have '
+ + bodiesNum + ' body');
+ }
+ if (footerIsNull) {
+ assert_equals(table.tFoot, null, 'Table should have no footer');
+ }
+ if (footerId) {
+ assert_not_equals(table.tFoot.id, footerId,
+ 'Table should have no footer with id="' + footerId + '"');
+ }
+ if (headerIsNull) {
+ assert_equals(table.tHead, null, 'Table should have no header');
+ }
+ if (headerId) {
+ assert_not_equals(table.tHead.id, headerId,
+ 'Table should have no header with id="' + headerId + '"');
+ }
+}
+
+
+
+var doc = newHTMLDocument();
+var parameters = [
+ ['Clearing stack back to a table body context. Test <tr> in <tbody>',
+ doc, 'tbody', '<tr id="tr1"><td>Cell content</td></tr>', 'tr1', 'TR'],
+
+ ['Clearing stack back to a table body context. Test <tr> in <thead>',
+ doc, 'thead', '<tr id="tr2"><td>Cell content</td></tr>', 'tr2', 'TR'],
+
+ ['Clearing stack back to a table body context. Test <tr> in <tfoot>',
+ doc, 'tfoot', '<tr id="tr3"><td>Cell content</td></tr>', 'tr3', 'TR'],
+
+ ['Clearing stack back to a table body context. Test </tbody>',
+ doc, 'tbody', '</tbody>', null, null],
+
+ ['Clearing stack back to a table body context. Test </thead>',
+ doc, 'thead', '</thead>', null, null],
+
+ ['Clearing stack back to a table body context. Test </tfoot>',
+ doc, 'tfoot', '</tfoot>', null, null],
+
+ ['Clearing stack back to a table body context. Test <caption> in <tbody>',
+ doc, 'tbody', '<caption id="caption1">Table Caption</caption>', 'caption1', 'CAPTION'],
+
+ ['Clearing stack back to a table body context. Test <caption> in <tfoot>',
+ doc, 'tfoot', '<caption id="caption2">Table Caption</caption>', 'caption2', 'CAPTION'],
+
+ ['Clearing stack back to a table body context. Test <caption> in <thead>',
+ doc, 'thead', '<caption id="caption3">Table Caption</caption>', 'caption3', 'CAPTION'],
+
+ ['Clearing stack back to a table body context. Test <col> in <tbody>',
+ doc, 'tbody', '<col id="col1" width="150"/>', 'col1', 'COL'],
+
+ ['Clearing stack back to a table body context. Test <col> in <tfoot>',
+ doc, 'tfoot', '<col id="col2" width="150"/>', 'col2', 'COL'],
+
+ ['Clearing stack back to a table body context. Test <col> in <thead>',
+ doc, 'thead', '<col id="col3" width="150"/>', 'col3', 'COL'],
+
+ ['Clearing stack back to a table body context. Test <colgroup> in <tbody>',
+ doc, 'tbody', '<colgroup id="colgroup1" width="150"></colgroup>', 'colgroup1', 'COLGROUP'],
+
+ ['Clearing stack back to a table body context. Test <colgroup> in <tfoot>',
+ doc, 'tfoot', '<colgroup id="colgroup2" width="150"></colgroup>', 'colgroup2', 'COLGROUP'],
+
+ ['Clearing stack back to a table body context. Test <colgroup> in <thead>',
+ doc, 'thead', '<colgroup id="colgroup3" width="150"></colgroup>', 'colgroup3', 'COLGROUP'],
+
+ ['Clearing stack back to a table body context. Test <tbody> in <tbody>',
+ doc, 'tbody', '<tbody id="tbody1"></tbody>', 'tbody1', 'TBODY', 1],
+
+ ['Clearing stack back to a table body context. Test <tbody> in <tfoot>',
+ doc, 'tfoot', '<tbody id="tbody2"></tbody>', 'tbody2', 'TBODY', 0],
+
+ ['Clearing stack back to a table body context. Test <tbody> in <thead>',
+ doc, 'thead', '<tbody id="tbody3"></tbody>', 'tbody3', 'TBODY', 0],
+
+ ['Clearing stack back to a table body context. Test <tfoot> in <tbody>',
+ doc, 'tbody', '<tfoot id="tfoot1"></tfoot>', 'tfoot1', 'TFOOT', null, true],
+
+ ['Clearing stack back to a table body context. Test <tfoot> in <tfoot>',
+ doc, 'tfoot', '<tfoot id="tfoot2"></tfoot>', 'tfoot2', 'TFOOT', null, false, 'tfoot2'],
+
+ ['Clearing stack back to a table body context. Test <tfoot> in <thead>',
+ doc, 'thead', '<tfoot id="tfoot3"></tfoot>', 'tfoot3', 'TFOOT', null, true],
+
+ ['Clearing stack back to a table body context. Test <thead> in <tbody>',
+ doc, 'tbody', '<thead id="thead1"></thead>', 'thead1', 'THEAD', null, false, null, true],
+
+ ['Clearing stack back to a table body context. Test <thead> in <tfoot>',
+ doc, 'tfoot', '<thead id="thead2"></thead>', 'thead2', 'THEAD', null, false, null, true],
+
+ ['Clearing stack back to a table body context. Test <thead> in <thead>',
+ doc, 'thead', '<thead id="thead3"></thead>', 'thead3', 'THEAD', null, false, null, false, 'thead3'],
+
+ ['Clearing stack back to a table body context. Test </table> in <tbody>',
+ doc, 'tbody', '</table>', null, null, null, false, null, true],
+
+ ['Clearing stack back to a table body context. Test </table> in <tfoot>',
+ doc, 'tfoot', '</table>', null, null, null, false, null, true],
+
+ ['Clearing stack back to a table body context. Test </table> in <thead>',
+ doc, 'thead', '</table>', null, null],
+
+ ['Clearing stack back to a table body context. Test </tbody> in <thead>',
+ doc, 'thead', '</tbody>', null, null],
+
+ ['Clearing stack back to a table body context. Test </tbody> in <tfoot>',
+ doc, 'tfoot', '</tbody>', null, null],
+
+ ['Clearing stack back to a table body context. Test </thead> in <tbody>',
+ doc, 'tbody', '</thead>', null, null],
+
+ ['Clearing stack back to a table body context. Test </thead> in <tfoot>',
+ doc, 'tfoot', '</thead>', null, null],
+
+ ['Clearing stack back to a table body context. Test </tfoot> in <thead>',
+ doc, 'thead', '</tfoot>', null, null],
+
+ ['Clearing stack back to a table body context. Test </tfoot> in <tbody>',
+ doc, 'tbody', '</tfoot>', null, null]
+];
+
+// Clearing stack back to a table body context.
+generate_tests(doTest, parameters);
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-context.html b/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-context.html
new file mode 100644
index 0000000000..1bf9c5bba6
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-context.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Clearing stack back to a table context</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="Clearing the stack back to a table context must be aborted if the current node is template">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#clearing-the-stack">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+function doTest(doc, templateInnerHTML, id, tagName, bodiesNum = null, footerIsNull,
+ headerIsNull) {
+
+ doc.body.innerHTML = '' +
+ '<table id="tbl">' +
+ '<template id="tmpl1">' +
+ // When parser meets <caption>, <colgroup>, <tbody>, <tfoot>, <thead>, <col>
+ // stack must be cleared back to table context.
+ //But <template> tag should abort this process
+ templateInnerHTML +
+ '</template>' +
+ '<tr id="tr">' +
+ '<td id="td">' +
+ '</td>' +
+ '</tr>' +
+ '</table>';
+
+ var table = doc.querySelector('#tbl');
+ var tr = doc.querySelector('#tr');
+ var td = doc.querySelector('#td');
+ var template = doc.querySelector('#tmpl1');
+
+ assert_equals(table.rows.length, 1, 'Wrong number of table rows');
+ assert_equals(table.rows[0].cells.length, 1, 'Wrong number of table cells');
+ assert_equals(template.parentNode, table, 'Wrong template parent');
+ assert_not_equals(template.content.querySelector('#' + id), null,
+ 'Element should present in the template content');
+ assert_equals(doc.querySelector('#tbl').caption, null, 'Table should have no caption');
+ assert_equals(template.content.querySelector('#' + id).tagName, tagName,
+ 'Wrong element in the template content');
+ if (bodiesNum !== null) {
+ assert_equals(table.tBodies.length, bodiesNum, 'Table should have '
+ + bodiesNum + ' body');
+ }
+ if (footerIsNull) {
+ assert_equals(table.tFoot, null, 'Table should have no footer');
+ }
+ if (headerIsNull) {
+ assert_equals(table.tHead, null, 'Table should have no header');
+ }
+}
+
+
+var doc = newHTMLDocument();
+var parameters = [
+ ['Clearing stack back to a table context. Test <caption>',
+ doc, '<caption id="caption1">Table caption</caption>', 'caption1', 'CAPTION'],
+
+ ['Clearing stack back to a table context. Test <colgroup>',
+ doc, '<colgroup id="colgroup1" width="100%"></colgroup>', 'colgroup1', 'COLGROUP'],
+
+ ['Clearing stack back to a table context. Test <tbody>',
+ doc, '<tbody id="tbody1"></tbody>', 'tbody1', 'TBODY', 1],
+
+ ['Clearing stack back to a table context. Test <tfoot>',
+ doc, '<tfoot id="tfoot1"></tfoot>', 'tfoot1', 'TFOOT', null, true],
+
+ ['Clearing stack back to a table context. Test <thead>',
+ doc, '<thead id="thead1"></thead>', 'thead1', 'THEAD', null, false, true],
+
+ ['Clearing stack back to a table context. Test <col>',
+ doc, '<col id="col1" width="100%"/>', 'col1', 'COL']
+];
+
+// Clearing stack back to a table body context.
+generate_tests(doTest, parameters);
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-row-context.html b/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-row-context.html
new file mode 100644
index 0000000000..d213c0fbdd
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-row-context.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: Clearing stack back to a table row context</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="Clearing the stack back to a table row context must be aborted if the current node is template">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#clearing-the-stack">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+function doTest(doc, templateInnerHTML, id, tagName, elementId) {
+
+ doc.body.innerHTML = '' +
+ '<table id="tbl">' +
+ '<tr id="tr">' +
+ '<template id="tmpl1">' +
+ // When parser meets <th>, <td>, </tr>, stack must be cleared
+ // back to table row context.
+ // But <template> tag should abort this
+ templateInnerHTML +
+ '</template>' +
+ '<td id="td">' +
+ '</td>' +
+ '</tr>' +
+ '</table>';
+
+ var table = doc.querySelector('#tbl');
+ var tr = doc.querySelector('#tr');
+ var td = doc.querySelector('#td');
+ var template = doc.querySelector('#tmpl1');
+
+ assert_equals(table.rows.length, 1, 'Wrong number of table rows');
+ assert_equals(table.rows[0].cells.length, 1, 'Wrong number of table cells');
+ assert_equals(template.parentNode, tr, 'Wrong template parent');
+ if (id !== null) {
+ assert_not_equals(template.content.querySelector('#' + id), null,
+ 'Element should present in the template content');
+ }
+ if (tagName !== null) {
+ assert_equals(template.content.querySelector('#' + id).tagName, tagName,
+ 'Wrong element in the template content');
+ }
+ if (elementId) {
+ assert_equals(doc.querySelector('#' + elementId), null,
+ 'Table should have no element with ID ' + elementId);
+ }
+}
+
+
+var doc = newHTMLDocument();
+var parameters = [
+ ['Clearing stack back to a table row context. Test <th>',
+ doc, '<th id="th1">Table header</th>', 'th1', 'TH', 'th1'],
+
+ ['Clearing stack back to a table row context. Test <td>',
+ doc, '<td id="td1">Table cell</td>', 'td1', 'TD', 'td1'],
+
+ ['Clearing stack back to a table row context. Test </tr>',
+ doc, '</tr>', null, null]
+];
+
+// Clearing stack back to a table body context.
+generate_tests(doTest, parameters);
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html b/testing/web-platform/tests/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html
new file mode 100644
index 0000000000..6738a6fde5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html
@@ -0,0 +1,222 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>HTML Templates: ownerDocument property of the element in template</title>
+<meta name="timeout" content="long">
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="ownerDocument property of the element appended to template must be set to the template contents owner of the ownerDocument of the template element">
+<link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#creating-an-element-for-a-token">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<div><template id="tmpl1"><div id="div">DIV</div></template></div>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ var div = template.content.querySelector('#div');
+
+ assert_equals(div.ownerDocument, template.content.ownerDocument,
+ 'Wrong ownerDocument of the element in template');
+
+}, 'Test ownerDocument property of the element in a template. '
+ + 'Current DOCUMENT has no browsing context. Test template element inside the div');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1"><div id="div">DIV</div></template>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ var div = template.content.querySelector('#div');
+
+ assert_equals(div.ownerDocument, template.content.ownerDocument,
+ 'Wrong ownerDocument of the element in template');
+
+}, 'Test ownerDocument property of the element in a template. '
+ + 'Current DOCUMENT has no browsing context. Test template element '
+ + 'in the root of the body');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.head.innerHTML = '<template id="tmpl1"><div id="div">DIV</div></template>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ var div = template.content.querySelector('#div');
+
+ assert_equals(div.ownerDocument, template.content.ownerDocument,
+ 'Wrong ownerDocument of the element in template');
+
+}, 'Test ownerDocument property of the element in a template. '
+ + 'Current DOCUMENT has no browsing context. Test template element '
+ + 'in the root of the head');
+
+
+
+test(function () {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<template id="tmpl1">'
+ + '<template id="tmpl2"><div id="div">DIV</div></template></template>';
+
+ var template = doc.querySelector('#tmpl1');
+
+ var nestedTemplate = template.content.querySelector('#tmpl2');
+
+ assert_equals(nestedTemplate.ownerDocument, template.content.ownerDocument,
+ 'Wrong nested template owner document');
+
+ var div = nestedTemplate.content.querySelector('#div');
+
+ assert_equals(div.ownerDocument, nestedTemplate.content.ownerDocument,
+ 'Wrong div ownerDocument');
+
+}, 'Test ownerDocument property of the element in a nested template');
+
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ var div = template.content.querySelector('div');
+
+ assert_equals(div.ownerDocument, template.content.ownerDocument,
+ 'Wrong ownerDocument of the element in template');
+
+}, 'Test ownerDocument property of the element in a template. '
+ + 'Load HTML document from a file, current DOCUMENT has browsing context');
+
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/template-contents-nested.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template = doc.querySelector('template');
+
+ var nestedTemplate = template.content.querySelector('template');
+
+ assert_equals(nestedTemplate.ownerDocument, template.content.ownerDocument,
+ 'Wrong nested template owner document');
+
+ var div = nestedTemplate.content.querySelector('div');
+
+ assert_equals(div.ownerDocument, nestedTemplate.content.ownerDocument,
+ 'Wrong div ownerDocument');
+
+}, 'Test ownerDocument property of the element in a nested template. '
+ + 'Load HTML document from a file, current DOCUMENT has browsing context');
+
+
+
+testInIFrame('/html/semantics/scripting-1/the-template-element/resources/two-templates.html', function(context) {
+ var doc = context.iframes[0].contentDocument;
+
+ var template1 = doc.querySelector('#template1');
+ var div1 = template1.content.querySelector('div');
+ var template2 = doc.querySelector('#template2');
+ var div2 = template2.content.querySelector('div');
+
+ assert_equals(div1.ownerDocument, template1.content.ownerDocument,
+ 'Wrong ownerDocument of the element in template');
+ assert_equals(div2.ownerDocument, template2.content.ownerDocument,
+ 'Wrong ownerDocument of the element in template');
+ assert_equals(div1.ownerDocument, div2.ownerDocument,
+ 'Different elements in the same document should share the same template contents owner');
+
+}, 'Test ownerDocument property of two elements in a template. '
+ + 'Load HTML document from a file, current DOCUMENT has browsing context');
+
+
+var parameters = [];
+
+HTML5_ELEMENTS.forEach(function(value) {
+ if (value !== 'body' && value !== 'html' && value !== 'head' && value !== 'frameset') {
+
+ var doc = newHTMLDocument();
+
+ if (isVoidElement(value)) {
+ doc.body.innerHTML = '<template><' + value + '/></template>';
+ } else {
+ doc.body.innerHTML = '<template><' + value + '></' + value + '></template>';
+ }
+
+ var template = doc.querySelector('template');
+ var element = template.content.querySelector(value);
+
+ doc.body.appendChild(template);
+
+ parameters.push([
+ 'Test ownerDocument for the element ' + value + ' in the template',
+ element,
+ template
+ ]);
+ }
+});
+
+function compare_owners(element, template) {
+ assert_equals(element.ownerDocument, template.content.ownerDocument)
+}
+
+// Test ownerDocument property of all HTML5 elements in a template.
+// Current DOCUMENT has no browsing context.
+generate_tests(compare_owners, parameters);
+
+var context = newContext();
+parameters = [];
+
+try {
+
+ HTML5_ELEMENTS.forEach(function(value) {
+
+ if (value !== 'body' && value !== 'html' && value !== 'head' && value !== 'frameset') {
+
+ var doc = newRenderedHTMLDocument(context);
+
+ if (isVoidElement(value)) {
+ doc.body.innerHTML = '<template><' + value + '/></template>';
+ } else {
+ doc.body.innerHTML = '<template><' + value + '></' + value + '></template>';
+ }
+
+ var template = doc.querySelector('template');
+ var element = template.content.querySelector(value);
+
+ doc.body.appendChild(template);
+
+ parameters.push([
+ 'Test ownerDocument for the element ' + value + ' in the template. '
+ + 'Document has browsing context',
+ element,
+ template
+ ]);
+ }
+ });
+ generate_tests(compare_owners, parameters,
+ 'Test ownerDocument property of all HTML5 elements in a template. '
+ + 'Current DOCUMENT has browsing context.');
+
+} finally {
+ try {
+ cleanContext(context);
+ } catch (e) {
+ //do nothing
+ }
+}
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/parsing/test.js b/testing/web-platform/tests/html/syntax/parsing/test.js
new file mode 100644
index 0000000000..e725107b80
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/test.js
@@ -0,0 +1,350 @@
+var namespaces = {
+ "html":"http://www.w3.org/1999/xhtml",
+ "math":"http://www.w3.org/1998/Math/MathML",
+ "mathml":"http://www.w3.org/1998/Math/MathML",
+ "svg":"http://www.w3.org/2000/svg",
+ "xlink":"http://www.w3.org/1999/xlink",
+ "xml":"http://www.w3.org/XML/1998/namespace",
+ "xmlns":"http://www.w3.org/2000/xmlns/"
+};
+
+var prefixes = {};
+for (var prefix in namespaces) {
+ if (namespaces.hasOwnProperty(prefix)) {
+ prefixes[namespaces[prefix]] = prefix;
+ }
+}
+prefixes[namespaces["mathml"]] = "math";
+
+function format(format_string) {
+ var insertions = Array.prototype.slice.call(arguments, 1);
+ var regexp = /%s/g;
+ var match_count = 0;
+ var rv = format_string.replace(regexp, function(match) {
+ var rv = insertions[match_count];
+ match_count++;
+ return rv;
+ });
+ return rv;
+}
+
+function test_serializer(element) {
+ element.normalize();
+ var lines = [];
+ function serialize_element(element, indent) {
+ var indent_spaces = (new Array(indent)).join(" ");
+ switch(element.nodeType) {
+ case Node.DOCUMENT_TYPE_NODE:
+ if (element.name) {
+ if (element.publicId || element.systemId) {
+ var publicId = element.publicId ? element.publicId : "";
+ var systemId = element.systemId ? element.systemId : "";
+ lines.push(format("|%s<!DOCTYPE %s \"%s\" \"%s\">", indent_spaces,
+ element.name, publicId, systemId));
+ } else {
+ lines.push(format("|%s<!DOCTYPE %s>", indent_spaces,
+ element.name));
+ }
+ } else {
+ lines.push(format("|%s<!DOCTYPE >", indent_spaces));
+ }
+ break;
+ case Node.DOCUMENT_NODE:
+ lines.push("#document");
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ lines.push("#document-fragment");
+ break;
+ case Node.COMMENT_NODE:
+ lines.push(format("|%s<!-- %s -->", indent_spaces, element.nodeValue));
+ break;
+ case Node.TEXT_NODE:
+ lines.push(format("|%s\"%s\"", indent_spaces, element.nodeValue));
+ break;
+ case Node.ELEMENT_NODE:
+ if (element.getAttribute("data-skip") !== null) {
+ return;
+ }
+ if (element.namespaceURI !== null && element.namespaceURI !== namespaces.html) {
+ var name = format("%s %s", prefixes[element.namespaceURI],
+ element.localName);
+ } else {
+ var name = element.localName;
+ }
+ lines.push(format("|%s<%s>", indent_spaces, name));
+
+ var attributes = Array.prototype.map.call(
+ element.attributes,
+ function(attr) {
+ var name = (attr.namespaceURI ? prefixes[attr.namespaceURI] + " " : "") +
+ attr.localName;
+ return [name, attr.value];
+ });
+ attributes.sort(function (a, b) {
+ var x = a[0];
+ var y = b[0];
+ if (x === y) {
+ return 0;
+ }
+ return x > y ? 1 : -1;
+ });
+
+ attributes.forEach(
+ function(attr) {
+ var indent_spaces = (new Array(indent + 2)).join(" ");
+ lines.push(format("|%s%s=\"%s\"", indent_spaces, attr[0], attr[1]));
+ }
+ );
+ if ("HTMLTemplateElement" in window &&
+ Object.prototype.toString.call(element) === "[object HTMLTemplateElement]") {
+ indent += 2;
+ indent_spaces = (new Array(indent)).join(" ");
+ lines.push(format("|%scontent", indent_spaces));
+ indent += 2;
+ Array.prototype.forEach.call(element.content.childNodes,
+ function(node) {
+ serialize_element(node, indent);
+ });
+ indent -= 4;
+ }
+ break;
+ }
+ indent += 2;
+ Array.prototype.forEach.call(element.childNodes,
+ function(node) {
+ serialize_element(node, indent);
+ });
+ }
+ serialize_element(element, 0);
+ return lines.join("\n");
+}
+
+function parse_query() {
+ var query = location.search.slice(1);
+ var vars = query.split("&");
+ var fields = vars.map(function (x) {
+ var split = x.split("=");
+ return [split[0], split.slice(1).join("=")];
+ });
+ return fields;
+}
+
+function get_type() {
+ var run_type = "uri";
+ var fields = parse_query();
+ fields.forEach(function(x) {
+ if(x[0] == "run_type") {
+ run_type = x[1];
+ }
+ });
+ return run_type;
+};
+
+var test_in_blob_uri = get_test_func(function (iframe, uri_encoded_input, t) {
+ var b = new Blob([decodeURIComponent(uri_encoded_input)], { type: "text/html" });
+ var blobURL = URL.createObjectURL(b);
+ iframe.src = blobURL;
+ t.add_cleanup(function() {
+ URL.revokeObjectURL(blobURL);
+ });
+ });
+
+var test_document_write = get_test_func(function(iframe, uri_encoded_input, t) {
+ iframe.contentDocument.open();
+ var input = decodeURIComponent(uri_encoded_input);
+ iframe.contentDocument.write(input);
+ iframe.contentDocument.close();
+ });
+
+var test_document_write_single = get_test_func(function(iframe, uri_encoded_input, t) {
+ iframe.contentDocument.open();
+ var input = decodeURIComponent(uri_encoded_input);
+ for (var i=0; i< input.length; i++) {
+ iframe.contentDocument.write(input[i]);
+ }
+ iframe.contentDocument.close();
+ });
+
+function get_test_func(inject_func) {
+ function test_func(iframe, t, test_id, uri_encoded_input, escaped_expected) {
+ var expected = decodeURIComponent(escaped_expected);
+ current_tests[iframe.id] = {test_id:test_id,
+ uri_encoded_input:uri_encoded_input,
+ expected:expected,
+ actual:null
+ };
+
+ iframe.onload = function() {
+ t.step(function() {
+ iframe.onload = null;
+ var serialized_dom = test_serializer(iframe.contentDocument);
+ current_tests[iframe.id].actual = serialized_dom;
+ assert_equals(serialized_dom, expected);
+ t.done();
+ }
+ );
+ };
+ inject_func(iframe, uri_encoded_input, t);
+ }
+ return test_func;
+}
+
+function test_fragment(iframe, t, test_id, uri_encoded_input, escaped_expected, container) {
+ var input_string = decodeURIComponent(uri_encoded_input);
+ var expected = decodeURIComponent(escaped_expected);
+ current_tests[iframe.id] = {
+ test_id:test_id,
+ input:uri_encoded_input,
+ expected:expected,
+ actual:null,
+ container:container
+ };
+
+ var components = container.split(" ");
+ var container_elem = null;
+ if (components.length > 1) {
+ var namespace = namespaces[components[0]];
+ container_elem = document.createElementNS(namespace,
+ components[0] + ":" +
+ components[1]);
+ } else {
+ container_elem = document.createElement(container);
+ }
+ container_elem.innerHTML = input_string;
+ var serialized_dom;
+ if (container_elem.namespaceURI === namespaces["html"] && container_elem.localName === "template") {
+ serialized_dom = test_serializer(container_elem.content);
+ } else {
+ serialized_dom = test_serializer(container_elem);
+ }
+ current_tests[iframe.id].actual = serialized_dom;
+ serialized_dom = convert_innerHTML(serialized_dom);
+ assert_equals(serialized_dom, expected);
+ t.done();
+}
+
+function convert_innerHTML(serialized_dom) {
+ var lines = serialized_dom.split("\n");
+ assert_not_equals(lines[0], "<template>", "template is never the innerHTML context object");
+ lines[0] = "#document";
+ return lines.join("\n");
+}
+
+function print_diffs(test_id, uri_encoded_input, expected, actual, container) {
+ container = container ? container : null;
+ if (actual) {
+ var diffs = mark_diffs(expected, actual);
+ var expected_text = diffs[0];
+ var actual_text = diffs[1];
+ } else {
+ var expected_text = expected;
+ var actual_text = "";
+ }
+
+ var tmpl = ["div", {"id":"${test_id}"},
+ ["h2", {}, "${test_id}"],
+ function(vars) {
+ if (vars.container !== null) {
+ return ["div", {"class":"container"},
+ ["h3", {}, "innerHTML Container"],
+ ["pre", {}, vars.container]];
+ } else {
+ return null;
+ }
+ },
+ ["div", {"id":"input_${test_id}"}, ["h3", {}, "Input"], ["pre", {},
+ ["code", {}, decodeURIComponent(uri_encoded_input)]]],
+ ["div", {"id":"expected_${test_id}"}, ["h3", {}, "Expected"],
+ ["pre", {}, ["code", {}, expected_text]]],
+ ["div", {"id":"actual_${test_id}"}, ["h3", {}, "Actual"],
+ ["pre", {}, ["code", {}, actual_text]]]
+ ];
+
+ var diff_dom = template.render(tmpl, {test_id:test_id, container:container});
+ document.body.appendChild(diff_dom);
+}
+
+var current_tests = {};
+var iframe_map = {};
+
+function init_tests(test_type) {
+ var test_func = null;
+ var test_funcs = {
+ "write":test_document_write,
+ "write_single":test_document_write_single,
+ "uri":test_in_blob_uri,
+ "innerHTML":test_fragment
+ };
+ var tests_started = 0;
+ var tests_complete = 0;
+
+ setup(function() {
+ test_func = test_funcs[test_type];
+
+ var fails = [];
+
+ add_result_callback(function(test) {
+ tests_complete++;
+ var iframe = document.getElementById(iframe_map[test.name]);
+ if (test.status !== test.PASS) {
+ fails.push(current_tests[iframe.id]);
+ var new_iframe = document.createElement("iframe");
+ new_iframe.style.display = "none";
+ new_iframe.id = iframe.id;
+ document.body.replaceChild(new_iframe, iframe);
+ iframe = new_iframe;
+ }
+ if (tests_complete === order.length) {
+ done();
+ } else if (tests_started < order.length) {
+ test_next(iframe);
+ }
+ });
+
+ add_completion_callback(function() {
+ fails.forEach(function(t) {
+ print_diffs(t.test_id, t.uri_encoded_input,
+ t.expected, t.actual);
+ });
+ });
+
+ //Create the iframes we will use to test
+ //in the innerHTML case these are not actually used
+ //but it is convenient to reuse the same code
+ for (var i=0; i<num_iframes; i++) {
+ var iframe = document.createElement("iframe");
+ iframe.id = "iframe_" + i;
+ iframe.style.display = "none";
+ document.body.appendChild(iframe);
+ }
+ },
+ {explicit_done:true});
+
+ function test_next(iframe) {
+ var test_id = order[tests_started];
+ tests_started++;
+ var x = tests[test_id];
+ var t = x[0];
+ iframe_map[t.name] = iframe.id;
+ step_timeout(function() {
+ t.step(function() {
+ var string_uri_encoded_input = x[1];
+ var string_escaped_expected = x[2];
+ if (test_type === "innerHTML") {
+ var container = x[3];
+ }
+ test_func(iframe, t, test_id, string_uri_encoded_input, string_escaped_expected,
+ container);
+ });
+ }, 0);
+ }
+
+ onload = function() {
+ Array.prototype.forEach.call(document.getElementsByTagName("iframe"),
+ function(iframe) {
+ if (tests_started<order.length) {
+ test_next(iframe);
+ }
+ });
+ };
+}
diff --git a/testing/web-platform/tests/html/syntax/parsing/the-end.html b/testing/web-platform/tests/html/syntax/parsing/the-end.html
new file mode 100644
index 0000000000..79e32bb912
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/the-end.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>The end</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-end">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function() {
+ document.addEventListener("DOMContentLoaded", this.step_func_done(function(e) {
+ assert_equals(e.type, "DOMContentLoaded");
+ assert_true(e.bubbles, "bubbles should be true");
+ assert_false(e.cancelable, "cancelable should be false");
+ assert_equals(e.target, document, "target should be document");
+ assert_true(e.isTrusted, "isTrusted should be true");
+ assert_class_string(e, "Event");
+ }));
+}, "DOMContentLoaded");
+
+async_test(function() {
+ window.addEventListener("load", this.step_func_done(function(e) {
+ assert_equals(e.type, "load");
+ assert_false(e.bubbles, "bubbles should be false");
+ assert_false(e.cancelable, "cancelable should be false");
+ assert_equals(e.target, document, "target should be document");
+ assert_true(e.isTrusted, "isTrusted should be true");
+ assert_class_string(e, "Event");
+ }));
+}, "load");
+
+async_test(function() {
+ window.addEventListener("pageshow", this.step_func_done(function(e) {
+ assert_equals(e.type, "pageshow");
+
+ // https://github.com/whatwg/html/issues/6794
+ assert_true(e.bubbles, "bubbles should be true");
+ assert_true(e.cancelable, "cancelable should be true");
+
+ assert_equals(e.target, document, "target should be document");
+ assert_true(e.isTrusted, "isTrusted should be true");
+ assert_class_string(e, "PageTransitionEvent");
+ }));
+}, "pageshow");
+
+async_test(function() {
+ var seen_dcl = false;
+ var seen_load = false;
+ document.addEventListener("DOMContentLoaded", this.step_func(function() {
+ seen_dcl = true;
+ }));
+ window.addEventListener("load", this.step_func(function() {
+ seen_load = true;
+ assert_true(seen_dcl, "DOMContentLoaded should be fired before load");
+ }));
+ window.addEventListener("pageshow", this.step_func_done(function() {
+ assert_true(seen_load, "load should be fired before pageshow")
+ }));
+}, "order");
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/unclosed-svg-script.html b/testing/web-platform/tests/html/syntax/parsing/unclosed-svg-script.html
new file mode 100644
index 0000000000..ce7b89cb10
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/unclosed-svg-script.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ var scriptWithEndTagRan = false;
+ var scriptWithoutEndTagRan = false;
+ var scriptWithBogusEndTagInsideRan = false;
+ var scriptWithBreakout = false;
+ var scriptSelfClosing = false;
+</script>
+<svg>
+ <script>scriptWithEndTagRan = true;</script>
+</svg>
+<svg>
+ <script>scriptWithoutEndTagRan = true;
+</svg>
+<svg>
+ <script>scriptWithBogusEndTagInsideRan = true;</g></script>
+</svg>
+<svg>
+ <script>scriptWithBreakout = true;<s></script>
+</svg>
+<svg>
+ <script href="support/svg-script-self-closing.js"/>
+</svg>
+</s>
+<script>
+ test(function() {
+ assert_true(scriptWithEndTagRan);
+ }, "SVG scripts with end tag should run");
+ test(function() {
+ assert_false(scriptWithoutEndTagRan);
+ }, "SVG scripts without end tag should not run");
+ test(function() {
+ assert_true(scriptWithBogusEndTagInsideRan);
+ }, "SVG scripts with bogus end tag inside should run");
+ test(function() {
+ assert_false(scriptWithBreakout);
+ }, "SVG scripts ended by HTML breakout should not run");
+ test(function() {
+ assert_true(scriptSelfClosing);
+ }, "SVG scripts with self-closing start tag should run");
+</script>
diff --git a/testing/web-platform/tests/html/syntax/parsing/zero.html b/testing/web-platform/tests/html/syntax/parsing/zero.html
new file mode 100644
index 0000000000..9ff793ae7c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/parsing/zero.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<div></div>
+<script>
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "<!-\u0000>";
+ assert_equals(div.firstChild.data, "-\uFFFD");
+ }, "U+0000 should get replaced with U+FFFD after markup declaration hyphen");
+
+
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "&\u0000auml;";
+ assert_equals(div.firstChild.data, "&auml;");
+ }, "U+0000 should vanish after ampersand");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "&a\u0000uml;";
+ assert_equals(div.firstChild.data, "&auml;");
+ }, "U+0000 should vanish after ampersand and one letter of entity prefix");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "&au\u0000ml;";
+ assert_equals(div.firstChild.data, "&auml;");
+ }, "U+0000 should vanish after ampersand and two letters of entity prefix");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "&aum\u0000l;";
+ assert_equals(div.firstChild.data, "&auml;");
+ }, "U+0000 should vanish after ampersand and three letters of entity prefix");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "&auml\u0000;";
+ assert_equals(div.firstChild.data, "\u00E4;");
+ }, "U+0000 should vanish after semicolonless entity");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "&notin\u0000;";
+ assert_equals(div.firstChild.data, "\u00ACin;");
+ }, "U+0000 should vanish before required semicolon");
+
+
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "<span title='&\u0000auml;'>";
+ assert_equals(div.firstChild.title, "&\uFFFDauml;");
+ }, "U+0000 should get replaced with U+FFFD after ampersand");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "<span title='&a\u0000uml;'>";
+ assert_equals(div.firstChild.title, "&a\uFFFDuml;");
+ }, "U+0000 should get replaced with U+FFFD after ampersand and one letter of entity prefix");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "<span title='&au\u0000ml;'>";
+ assert_equals(div.firstChild.title, "&au\uFFFDml;");
+ }, "U+0000 should get replaced with U+FFFD after ampersand and two letters of entity prefix");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "<span title='&aum\u0000l;'>";
+ assert_equals(div.firstChild.title, "&aum\uFFFDl;");
+ }, "U+0000 should get replaced with U+FFFD after ampersand and three letters of entity prefix");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "<span title='&auml\u0000;'>";
+ assert_equals(div.firstChild.title, "\u00E4\uFFFD;");
+ }, "U+0000 should get replaced with U+FFFD after semicolonless entity");
+
+ test(function() {
+ var div = document.getElementsByTagName("div")[0];
+ div.innerHTML = "<span title='&notin\u0000;'>";
+ assert_equals(div.firstChild.title, "&notin\uFFFD;");
+ }, "U+0000 should get replaced with U+FFFD before required semicolon");
+
+
+
+</script>
diff --git a/testing/web-platform/tests/html/syntax/serializing-html-fragments/escaping.html b/testing/web-platform/tests/html/syntax/serializing-html-fragments/escaping.html
new file mode 100644
index 0000000000..7bbc5566fc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/serializing-html-fragments/escaping.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Serialization of script-disabled documents should follow escaping rules</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+
+<script>
+const html_escaped = '&amp;&nbsp;&lt;&gt;';
+const html_unescaped = '& <>';
+function getHtml(deEscapeParse) {
+ return `<noscript>${deEscapeParse ? html_escaped : html_unescaped}</noscript>`;
+}
+function testDoc(escapeSerialize, parsedNode) {
+ const node = parsedNode.firstChild;
+ const innerText = node.textContent;
+ assert_equals(innerText, html_unescaped, 'Content should be unescaped');
+ const serialized = node.innerHTML;
+ const expectation = escapeSerialize ? html_escaped : html_unescaped;
+ assert_equals(serialized, expectation, `Serialization ${escapeSerialize ? 'should' : 'should NOT'} re-escape <noscript> contents`);
+}
+
+test(() => {
+ const div = document.createElement('div');
+ document.body.appendChild(div);
+ div.innerHTML = getHtml(false);
+ testDoc(false, div);
+}, 'div.innerHTML');
+
+test(() => {
+ const div = document.createElement('div');
+ div.insertAdjacentHTML('afterbegin',getHtml(false));
+ testDoc(false, div);
+}, 'div.insertAdjacentHTML');
+
+test(() => {
+ const id = 'doc-write-1';
+ document.write(`<div id=${id} style="display:none">${getHtml(false)}</div>`);
+ testDoc(false, document.getElementById(id));
+}, 'document.write on main document');
+
+test(() => {
+ const doc = (new DOMParser()).parseFromString(`<body>${getHtml(true)}</body>`, 'text/html');
+ testDoc(true, doc.body);
+}, 'DOMParser.parseFromString');
+
+test(() => {
+ const template = document.createElement('template');
+ document.body.appendChild(template);
+ template.innerHTML = getHtml(true);
+ testDoc(true, template.content);
+}, 'template.innerHTML');
+
+test(() => {
+ const doc = document.implementation.createHTMLDocument('');
+ doc.body.innerHTML=`<pre>${getHtml(true)}</pre>`;
+ testDoc(true, doc.body.firstChild);
+}, 'document.implementation.createHTMLDocument and innerHTML');
+
+test(() => {
+ const doc = document.implementation.createHTMLDocument('');
+ let range = doc.createRange();
+ range.selectNode(doc.body);
+ const frag = range.createContextualFragment(getHtml(true));
+ testDoc(true, frag);
+}, 'document.implementation.createHTMLDocument and createContextualFragment');
+
+test(() => {
+ const id = 'doc-write-2';
+ const doc = document.implementation.createHTMLDocument('');
+ doc.write(`<div id=${id} style="display:none">${getHtml(false)}</div>`);
+ testDoc(true, doc.getElementById(id));
+}, 'document.implementation.createHTMLDocument and document.write');
+
+async_test((t) => {
+ let client = new XMLHttpRequest();
+ client.addEventListener('load', t.step_func_done(() => {
+ assert_true(client.status == 200 && client.responseXML != null);
+ testDoc(true, client.responseXML.body.firstChild);
+ t.done();
+ }));
+ client.open("GET", `data:text/html,<pre>${getHtml(true)}</pre>`);
+ client.responseType = 'document';
+ client.send();
+}, 'XMLHttpRequest');
+</script>
diff --git a/testing/web-platform/tests/html/syntax/serializing-html-fragments/initial-linefeed-pre.html b/testing/web-platform/tests/html/syntax/serializing-html-fragments/initial-linefeed-pre.html
new file mode 100644
index 0000000000..d4e30bb60d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/serializing-html-fragments/initial-linefeed-pre.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<title>innerHTML getter for pre/textarea/listing with initial LF</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="outer">
+<div id="inner">
+<pre id="pre1">
+x</pre>
+<pre id="pre2">
+
+x</pre>
+<textarea id="textarea1">
+x</textarea>
+<textarea id="textarea2">
+
+x</textarea>
+<listing id="listing1">
+x</listing>
+<listing id="listing2">
+
+x</listing>
+</div>
+</div>
+
+<script>
+var expected_outer = '\n<div id="inner">\n<pre id="pre1">x</pre>\n<pre id="pre2">\nx</pre>\n<textarea id="textarea1">x</textarea>\n<textarea id="textarea2">\nx</textarea>\n<listing id="listing1">x</listing>\n<listing id="listing2">\nx</listing>\n</div>\n';
+var expected_inner = expected_outer.replace('\n<div id="inner">', '').replace('</div>\n', '');
+var expected_1 = 'x';
+var expected_2 = '\nx';
+
+test(function() {
+ assert_equals(outer.innerHTML, expected_outer);
+}, 'outer div');
+
+test(function() {
+ assert_equals(inner.innerHTML, expected_inner);
+}, 'inner div');
+
+['pre', 'textarea', 'listing'].forEach(function(tag) {
+ test(function() {
+ assert_equals(document.getElementById(tag + '1').innerHTML, expected_1);
+ }, tag + '1');
+
+ test(function() {
+ assert_equals(document.getElementById(tag + '2').innerHTML, expected_2);
+ }, tag + '2');
+});
+</script>
diff --git a/testing/web-platform/tests/html/syntax/serializing-html-fragments/outerHTML.html b/testing/web-platform/tests/html/syntax/serializing-html-fragments/outerHTML.html
new file mode 100644
index 0000000000..491bf9e900
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/serializing-html-fragments/outerHTML.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML Test: element.outerHTML to verify HTML fragment serialization algorithm</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/#html-fragment-serialization-algorithm">
+ <link rel="help" href="https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../../resources/common.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function() {
+ const non_void_elements = HTML5_ELEMENTS.filter(el => !HTML5_VOID_ELEMENTS.includes(el));
+ non_void_elements.forEach(function(ele) {
+ test(function() {
+ var e = document.createElement(ele);
+ assert_equals(e.outerHTML, "<" + ele + "></" + ele + ">", ele + " node created." );
+ }, "Node for " + ele);
+ });
+ HTML5_VOID_ELEMENTS.forEach(function(ele) {
+ test(function() {
+ var e = document.createElement(ele);
+ assert_equals(e.outerHTML, "<" + ele + ">", ele + " node created." );
+ }, "Node for " + ele);
+ });
+ }, document.title);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/serializing-html-fragments/serializing.html b/testing/web-platform/tests/html/syntax/serializing-html-fragments/serializing.html
new file mode 100644
index 0000000000..1bccbf5608
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/serializing-html-fragments/serializing.html
@@ -0,0 +1,336 @@
+<!DOCTYPE html>
+<title>innerHTML in HTML</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<!-- test elments. Each has an expected innerHTML and outerHTML in an array in the <script>-->
+<div id="test" style="display:none">
+<span></span>
+<span><a></a></span>
+<span><a b=c></a></span>
+<span><a b='c'></a></span>
+<span><a b='&'></a></span>
+<span><a b='&nbsp;'></a></span>
+<span><a b='"'></a></span>
+<span><a b="<"></a></span>
+<span><a b=">"></a></span>
+<span><a href="javascript:&quot;&lt;>&quot;"></a></span>
+<span><svg xlink:href="a"></svg></span>
+<span><svg xmlns:svg="test"></svg></span>
+<span>a</span>
+<span>&amp;</span>
+<span>&nbsp;</span>
+<span>&lt;</span>
+<span>&gt;</span>
+<span>&quot;</span>
+<span><style><&></style></span>
+<span><script type="test"><&></script></span>
+<script type="test"><&></script>
+<span><xmp><&></xmp></span>
+<span><iframe><&></iframe></span>
+<span><noembed><&></noembed></span>
+<span><noframes><&></noframes></span>
+<span><noscript><&></noscript></span>
+<span><!--data--></span>
+<span><a><b><c></c></b><d>e</d><f><g>h</g></f></a></span>
+<span b=c></span>
+</div>
+<!-- TODO: template element -->
+<script>
+
+var test_data = document.getElementById("test").children;
+var expected = [
+["", "<span></span>"],
+["<a></a>", "<span><a></a></span>"],
+["<a b=\"c\"></a>", "<span><a b=\"c\"></a></span>"],
+["<a b=\"c\"></a>", "<span><a b=\"c\"></a></span>"],
+["<a b=\"&amp;\"></a>", "<span><a b=\"&amp;\"></a></span>"],
+["<a b=\"&nbsp;\"></a>", "<span><a b=\"&nbsp;\"></a></span>"],
+["<a b=\"&quot;\"></a>", "<span><a b=\"&quot;\"></a></span>"],
+["<a b=\"<\"></a>", "<span><a b=\"<\"></a></span>"],
+["<a b=\">\"></a>", "<span><a b=\">\"></a></span>"],
+["<a href=\"javascript:&quot;<>&quot;\"></a>", "<span><a href=\"javascript:&quot;<>&quot;\"></a></span>"],
+["<svg xlink:href=\"a\"></svg>", "<span><svg xlink:href=\"a\"></svg></span>"],
+["<svg xmlns:svg=\"test\"></svg>", "<span><svg xmlns:svg=\"test\"></svg></span>"],
+["a", "<span>a</span>"],
+["&amp;", "<span>&amp;</span>"],
+["&nbsp;", "<span>&nbsp;</span>"],
+["&lt;", "<span>&lt;</span>"],
+["&gt;", "<span>&gt;</span>"],
+["\"", "<span>\"</span>"],
+["<style><&></style>", "<span><style><&></style></span>"],
+["<script type=\"test\"><&><\/script>", "<span><script type=\"test\"><&><\/script></span>"],
+["<&>", "<script type=\"test\"><&><\/script>"],
+["<xmp><&></xmp>", "<span><xmp><&></xmp></span>"],
+["<iframe><&></iframe>", "<span><iframe><&></iframe></span>"],
+["<noembed><&></noembed>", "<span><noembed><&></noembed></span>"],
+["<noframes><&></noframes>", "<span><noframes><&></noframes></span>"],
+["<noscript><&></noscript>", "<span><noscript><&></noscript></span>"],
+["<!--data-->", "<span><!--data--></span>"],
+["<a><b><c></c></b><d>e</d><f><g>h</g></f></a>", "<span><a><b><c></c></b><d>e</d><f><g>h</g></f></a></span>"],
+["", "<span b=\"c\"></span>"]
+];
+
+var dom_tests = [
+ ["Attribute in the XML namespace",
+ function() {
+ var span = document.createElement("span");
+ var svg = document.createElement("svg");
+ svg.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:foo", "test");
+ span.appendChild(svg);
+ return span;
+ },
+ '<svg xml:foo="test"></svg>',
+ '<span><svg xml:foo="test"></svg></span>'],
+
+ ["Attribute in the XML namespace with the prefix not set to xml:",
+ function() {
+ var span = document.createElement("span");
+ var svg = document.createElement("svg");
+ svg.setAttributeNS("http://www.w3.org/XML/1998/namespace", "abc:foo", "test");
+ span.appendChild(svg);
+ return span;
+ },
+ '<svg xml:foo="test"></svg>',
+ '<span><svg xml:foo="test"></svg></span>'],
+
+ ["Non-'xmlns' attribute in the xmlns namespace",
+ function() {
+ var span = document.createElement("span");
+ var svg = document.createElement("svg");
+ svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:foo", "test")
+ span.appendChild(svg);
+ return span;
+ },
+ '<svg xmlns:foo="test"></svg>',
+ '<span><svg xmlns:foo="test"></svg></span>'],
+
+ ["'xmlns' attribute in the xmlns namespace",
+ function() {
+ var span = document.createElement("span");
+ var svg = document.createElement("svg");
+ svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", "test")
+ span.appendChild(svg);
+ return span;
+ },
+ '<svg xmlns="test"></svg>',
+ '<span><svg xmlns="test"></svg></span>'],
+
+ ["Attribute in non-standard namespace",
+ function() {
+ var span = document.createElement("span");
+ var svg = document.createElement("svg");
+ svg.setAttributeNS("fake_ns", "abc:def", "test")
+ span.appendChild(svg);
+ return span;
+ },
+ '<svg abc:def="test"></svg>',
+ '<span><svg abc:def="test"></svg></span>'],
+
+ ["<span> starting with U+000A",
+ function() {
+ var elem = document.createElement("span");
+ elem.appendChild(document.createTextNode("\x0A"));
+ return elem;
+ },
+ "\x0A",
+ "<span>\x0A</span>"],
+
+ //TODO: Processing instructions
+]
+
+var text_elements = ["pre", "textarea", "listing"];
+
+var text_tests = [
+ ["<%text> context starting with U+000A",
+ function(elem) {
+ elem.appendChild(document.createTextNode("\x0A"));
+ return elem;
+ },
+ "\x0A",
+ "<%text>\x0A</%text>"],
+
+ ["<%text> context not starting with U+000A",
+ function(elem) {
+ elem.appendChild(document.createTextNode("a\x0A"));
+ return elem;
+ },
+ "a\x0A",
+ "<%text>a\x0A</%text>"],
+
+ ["<%text> non-context starting with U+000A",
+ function(elem) {
+ var span = document.createElement("span");
+ elem.appendChild(document.createTextNode("\x0A"));
+ span.appendChild(elem);
+ return span;
+ },
+ "<%text>\x0A</%text>",
+ "<span><%text>\x0A</%text></span>"],
+
+ ["<%text> non-context not starting with U+000A",
+ function(elem) {
+ var span = document.createElement("span");
+ elem.appendChild(document.createTextNode("a\x0A"));
+ span.appendChild(elem);
+ return span;
+ },
+ "<%text>a\x0A</%text>",
+ "<span><%text>a\x0A</%text></span>"],
+]
+
+var void_elements = [
+ "area", "base", "basefont", "bgsound", "br", "col", "embed",
+ "frame", "hr", "img", "input", "keygen", "link",
+ "meta", "param", "source", "track", "wbr"
+];
+
+var void_tests = [
+ ["Void context node",
+ function (void_elem) {
+ return void_elem;
+ },
+ "",
+ "<%void>"
+ ],
+ ["void as first child with following siblings",
+ function (void_elem) {
+ var span = document.createElement("span");
+ span.appendChild(void_elem);
+ span.appendChild(document.createElement("a")).appendChild(document.createTextNode("test"));
+ span.appendChild(document.createElement("b"))
+ return span
+ },
+ "<%void><a>test</a><b></b>",
+ "<span><%void><a>test</a><b></b></span>"
+ ],
+ ["void as second child with following siblings",
+ function (void_elem) {
+ var span = document.createElement("span");
+ span.appendChild(document.createElement("a")).appendChild(document.createTextNode("test"));
+ span.appendChild(void_elem);
+ span.appendChild(document.createElement("b"))
+ return span;
+ },
+ "<a>test</a><%void><b></b>",
+ "<span><a>test</a><%void><b></b></span>"
+ ],
+ ["void as last child with preceding siblings",
+ function (void_elem) {
+ var span = document.createElement("span");
+ span.appendChild(document.createElement("a")).appendChild(document.createTextNode("test"));
+ span.appendChild(document.createElement("b"))
+ span.appendChild(void_elem);
+ return span;
+ },
+ "<a>test</a><b></b><%void>",
+ "<span><a>test</a><b></b><%void></span>"
+ ],
+]
+
+function cross_map(a1, a2, f) {
+ var rv = [];
+ a1.forEach(function(a1_elem) {
+ a2.forEach(function(a2_elem) {
+ rv.push(f(a1_elem, a2_elem));
+ })
+ });
+ return rv;
+}
+
+function innerHTML_test(func, elem, expected) {
+ assert_equals(func(elem).innerHTML, expected);
+}
+
+function outerHTML_test(func, elem, expected) {
+ assert_equals(func(elem).outerHTML, expected);
+}
+
+
+function make_void(name) {
+ var rv = document.createElement(name);
+ rv.appendChild(document.createElement("a")).appendChild(document.createComment("abc"))
+ rv.appendChild(document.createElement("b")).
+ appendChild(document.createElement("c")).
+ appendChild(document.createTextNode("abc"))
+ return rv;
+}
+
+function make_text(name) {
+ return document.createElement(name);
+}
+
+generate_tests(innerHTML_test,
+ expected.map(function(item, i) {
+ return ["innerHTML " + i + " " + expected[i][0],
+ function() {return test_data[i]},
+ null,
+ item[0]];
+ }))
+
+generate_tests(outerHTML_test,
+ expected.map(function(item, i) {
+ return ["outerHTML " + i + " " + expected[i][1],
+ function() {return test_data[i]},
+ null,
+ item[1]];
+ }))
+
+generate_tests(innerHTML_test,
+ dom_tests.map(function(item) {
+ return ["innerHTML " + item[0],
+ item[1],
+ null,
+ item[2]];
+ }))
+
+generate_tests(outerHTML_test,
+ dom_tests.map(function(item) {
+ return ["outerHTML " + item[0],
+ item[1],
+ null,
+ item[3]];
+ }))
+
+generate_tests(innerHTML_test,
+ cross_map(text_tests, text_elements,
+ function(test_data, elem_name) {
+ var rv = ["innerHTML " + test_data[0].replace(/%text/g, elem_name),
+ test_data[1],
+ document.createElement(elem_name),
+ test_data[2].replace(/%text/g, elem_name)];
+ return rv;
+ }))
+
+generate_tests(outerHTML_test,
+ cross_map(text_tests, text_elements,
+ function(test_data, elem_name) {
+ var rv = ["outerHTML " + test_data[0].replace(/%text/g, elem_name),
+ test_data[1],
+ document.createElement(elem_name),
+ test_data[3].replace(/%text/g, elem_name)];
+ return rv;
+ }))
+
+generate_tests(innerHTML_test,
+ cross_map(void_tests, void_elements,
+ function(test_data, elem_name) {
+ var rv = ["innerHTML " + test_data[0] + " " + elem_name,
+ test_data[1],
+ make_void(elem_name),
+ test_data[2].replace(/%void/g, elem_name)];
+ return rv;
+ }))
+
+generate_tests(outerHTML_test,
+ cross_map(void_tests, void_elements,
+ function(test_data, elem_name) {
+ var rv = ["outerHTML " + test_data[0] + " " + elem_name,
+ test_data[1],
+ make_void(elem_name),
+ test_data[3].replace(/%void/g, elem_name)];
+ return rv;
+ }))
+
+</script>
diff --git a/testing/web-platform/tests/html/syntax/serializing-html-fragments/template.html b/testing/web-platform/tests/html/syntax/serializing-html-fragments/template.html
new file mode 100644
index 0000000000..ea050384f2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/serializing-html-fragments/template.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', '<div><template><table><td></table></template></div>');
+ let t = document.querySelector('template');
+ assert_equals(t.innerHTML, '<table><tbody><tr><td></td></tr></tbody></table>');
+ assert_equals(t.parentNode.innerHTML, '<template><table><tbody><tr><td></td></tr></tbody></table></template>');
+}, 'Template element content is correctly serialized');
+
+test(() => {
+ let t = document.createElement('template');
+ let c = t.content.appendChild(document.createElementNS("xx", "div"));
+ c.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:lang', 'en-us');
+ c.setAttributeNS('uri2', 'p:attr', 'v');
+ let doc = new Document();
+ doc.adoptNode(t.content);
+ assert_equals(t.innerHTML, '<div xml:lang="en-us" p:attr="v"></div>');
+}, 'HTML fragment serialization algorithm should be applied to the template content');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html b/testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html
new file mode 100644
index 0000000000..c6582fa9db
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>HTML Test: element.outerHTML to verify XML fragment serialization algorithm</title>
+ <link rel="author" title="Intel" href="http://www.intel.com/">
+ <link rel="help" href="https://w3c.github.io/DOM-Parsing/#dfn-concept-serialize-xml">
+ <link rel="help" href="https://w3c.github.io/DOM-Parsing/#widl-Element-outerHTML">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../../resources/common.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function() {
+ var doc = document.implementation.createDocument(null, "");
+ assert_equals(doc.contentType, "application/xml");
+ var html_ns = "http://www.w3.org/1999/xhtml";
+ const non_void_elements = HTML5_ELEMENTS.filter(el => !HTML5_VOID_ELEMENTS.includes(el));
+ non_void_elements.forEach(function(ele) {
+ test(function() {
+ var e = doc.createElementNS(html_ns, ele);
+ assert_equals(e.outerHTML,
+ `<${ele} xmlns="${html_ns}"></${ele}>`,
+ ele + " node created." );
+ }, "Node for " + ele);
+ });
+ HTML5_VOID_ELEMENTS.forEach(function(ele) {
+ test(function() {
+ var e = doc.createElementNS(html_ns, ele);
+ assert_equals(e.outerHTML,
+ `<${ele} xmlns="${html_ns}" />`,
+ ele + " node created." );
+ }, "Node for " + ele);
+ });
+ }, document.title);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/syntax/speculative-charset/speculative-script.tentative.html b/testing/web-platform/tests/html/syntax/speculative-charset/speculative-script.tentative.html
new file mode 100644
index 0000000000..e665510946
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-charset/speculative-script.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="windows-1253">
+<title>Speculative script</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<body>
+<script>
+const uuid = token();
+
+window.onmessage = function(e) {
+ // The script is first speculatively loaded in the windows-1253 context (inherited from this doc), so
+ // the Greek hex NCR turns into a byte. This byte is stored. Then the script is fetched
+ // non-speculatively, because the Greek hexNCR in the URL makes the URL not match in the
+ // windows-1251 context. Now the Greek hex NCR turns in to a decimal NCR and the original Greek
+ // character comes back as a byte that gets a Cyrillic interpretation.
+ assert_equals(e.data, `token: ${uuid}, character: &#950;, previous character: \u0436, byte: \u0436`, "Check result");
+ done();
+}
+
+setup({single_test: true});
+const iframe = document.createElement('iframe');
+iframe.src = `support/speculative-script.py?uuid=${uuid}`;
+document.body.appendChild(iframe);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-charset/support/script.py b/testing/web-platform/tests/html/syntax/speculative-charset/support/script.py
new file mode 100644
index 0000000000..0dbf5be0ac
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-charset/support/script.py
@@ -0,0 +1,18 @@
+import time
+
+def main(request, response):
+ response.add_required_headers = False # Don't implicitly add HTTP headers
+ response.writer.write_status(200)
+ response.writer.write_header("Content-Type", "text/javascript")
+ response.writer.end_headers()
+
+ token = request.GET[b"uuid"]
+ character = request.GET[b"character"]
+ old_character = b"NOT LOADED PREVIOUSLY";
+ from_stash = request.server.stash.take(token);
+ if from_stash:
+ old_character = from_stash
+ else:
+ request.server.stash.put(token, character)
+
+ response.writer.write(b'parent.postMessage("token: %s, character: %s, previous character: %s, byte: \xE6", "*");' % (token, character, old_character));
diff --git a/testing/web-platform/tests/html/syntax/speculative-charset/support/speculative-script.py b/testing/web-platform/tests/html/syntax/speculative-charset/support/speculative-script.py
new file mode 100644
index 0000000000..c72c469ce3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-charset/support/speculative-script.py
@@ -0,0 +1,11 @@
+import time
+
+def main(request, response):
+ response.add_required_headers = False # Don't implicitly add HTTP headers
+ response.writer.write_status(200)
+ response.writer.write_header("Content-Type", "text/html")
+ response.writer.end_headers()
+
+ response.writer.write(b'<!DOCTYPE html><script src="script.py?uuid=%s&character=&#x03B6;"></script>' % request.GET[b"uuid"]);
+ time.sleep(0.2)
+ response.writer.write(b'<meta charset="windows-1251"><p>Test: \xE6</p>');
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/base-href-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/base-href-script-src.tentative.sub.html
new file mode 100644
index 0000000000..23d2777c63
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/base-href-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): base-href-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'base-href-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <base href=//{{domains[www1]}}:{{ports[http][0]}}><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/image-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/image-src.tentative.sub.html
new file mode 100644
index 0000000000..b026985011
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/image-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): image-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'image-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <image src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-data-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-data-src.tentative.sub.html
new file mode 100644
index 0000000000..21c14e382c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-data-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): img-data-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'img-data-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <img data-src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-crossorigin.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-crossorigin.tentative.sub.html
new file mode 100644
index 0000000000..8d038471c8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-crossorigin.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): img-src-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-src-crossorigin', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" crossorigin>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-loading-lazy.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-loading-lazy.tentative.sub.html
new file mode 100644
index 0000000000..a1b1c92af8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-loading-lazy.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): img-src-loading-lazy</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'img-src-loading-lazy', false))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" loading=lazy>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-referrerpolicy-no-referrer.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-referrerpolicy-no-referrer.tentative.sub.html
new file mode 100644
index 0000000000..c26b933be8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src-referrerpolicy-no-referrer.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): img-src-referrerpolicy-no-referrer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-src-referrerpolicy-no-referrer', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src.tentative.sub.html
new file mode 100644
index 0000000000..b4d16da8e9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-srcset.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-srcset.tentative.sub.html
new file mode 100644
index 0000000000..b08d597a82
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/img-srcset.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): img-srcset</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-srcset', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <img srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-no-rel.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-no-rel.tentative.sub.html
new file mode 100644
index 0000000000..260f7a53a2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-no-rel.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-no-rel</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-no-rel', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-alternate-stylesheet.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-alternate-stylesheet.tentative.sub.html
new file mode 100644
index 0000000000..0b82352e3b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-alternate-stylesheet.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-alternate-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-alternate-stylesheet', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel="alternate stylesheet" href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-font-crossorigin.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-font-crossorigin.tentative.sub.html
new file mode 100644
index 0000000000..2ae82c066d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-font-crossorigin.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-preload-as-font-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-font-crossorigin', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=preload as=font href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" crossorigin>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-image.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-image.tentative.sub.html
new file mode 100644
index 0000000000..098e08f5d1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-image.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-preload-as-image</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-image', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=preload as=image href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-script.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-script.tentative.sub.html
new file mode 100644
index 0000000000..bfa48f0e79
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-script.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-preload-as-script</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-script', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=preload as=script href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-style.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-style.tentative.sub.html
new file mode 100644
index 0000000000..75e5a9de6b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-preload-as-style.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-preload-as-style</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-style', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=preload as=style href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-crossorigin.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-crossorigin.tentative.sub.html
new file mode 100644
index 0000000000..66ac2e4e44
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-crossorigin.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-crossorigin', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" crossorigin>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub.html
new file mode 100644
index 0000000000..58dbfe6aee
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet-disabled</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-disabled', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel="stylesheet" href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" disabled>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-integrity.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-integrity.tentative.sub.html
new file mode 100644
index 0000000000..35369c3f53
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-integrity.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet-integrity</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-integrity', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-nomatch-media.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-nomatch-media.tentative.sub.html
new file mode 100644
index 0000000000..74cf92bf1b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-nomatch-media.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet-nomatch-media</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-nomatch-media', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" media="not all">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.sub.html
new file mode 100644
index 0000000000..f25a60f7da
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet-referrerpolicy-no-referrer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-referrerpolicy-no-referrer', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-type-text-css.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-type-text-css.tentative.sub.html
new file mode 100644
index 0000000000..c5dcfc4a61
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-type-text-css.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet-type-text-css</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-type-text-css', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" type=text/css>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-unsupported-type.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-unsupported-type.tentative.sub.html
new file mode 100644
index 0000000000..ed441e7eae
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-unsupported-type.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet-unsupported-type</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-unsupported-type', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" type=text/plain>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet.tentative.sub.html
new file mode 100644
index 0000000000..de6df7e457
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): link-rel-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-face-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-face-script-src.tentative.sub.html
new file mode 100644
index 0000000000..62b5bffaa2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-face-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): math-font-face-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'math-font-face-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <math><font face><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></font></math>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub.html
new file mode 100644
index 0000000000..4fd7ea8ff6
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): math-font-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'math-font-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <math><font><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></font></math>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub.html
new file mode 100644
index 0000000000..00876cae14
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): math-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'math-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <math><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></math>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-charset-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-charset-script-src.tentative.sub.html
new file mode 100644
index 0000000000..8b5072a415
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-charset-script-src.tentative.sub.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<!-- no meta charset -->
+<!-- (padding to exceed 1024 bytes processed by the character encoding scanner) -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<title>Speculative parsing, document.write(): meta-charset-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'meta-charset-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <meta\ charset=windows-1254><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-asterisk.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-asterisk.tentative.sub.html
new file mode 100644
index 0000000000..2d9f8aeb1d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-asterisk.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): meta-csp-img-src-asterisk</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'meta-csp-img-src-asterisk', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; img-src *"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-none.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-none.tentative.sub.html
new file mode 100644
index 0000000000..0266ae72d5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-csp-img-src-none.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): meta-csp-img-src-none</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'meta-csp-img-src-none', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; img-src 'none'"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-referrer-no-referrer-img-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-referrer-no-referrer-img-src.tentative.sub.html
new file mode 100644
index 0000000000..b0d0b910e2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-referrer-no-referrer-img-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): meta-referrer-no-referrer-img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'meta-referrer-no-referrer-img-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <meta name=referrer content=no-referrer><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub.html
new file mode 100644
index 0000000000..b7ab5ff97a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): meta-viewport-link-stylesheet-media</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'meta-viewport-link-stylesheet-media', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <meta name=viewport content="width=400, initial-scale=1"><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" media="(min-width: 401px)">
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-1.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-1.tentative.sub.html
new file mode 100644
index 0000000000..c6006ebc0d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-1.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): nested-template-shadowrootmode-1</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'nested-template-shadowrootmode-1', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <template><div><template shadowrootmode="closed"><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></template></div></template>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-2.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-2.tentative.sub.html
new file mode 100644
index 0000000000..282b0bb555
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/nested-template-shadowrootmode-2.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): nested-template-shadowrootmode-2</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'nested-template-shadowrootmode-2', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <div><template shadowrootmode="closed"><template><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></template></template></div>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-br-img.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-br-img.tentative.sub.html
new file mode 100644
index 0000000000..914dfb83c9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-br-img.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): picture-source-br-img</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-br-img', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><br><img></picture>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-no-img.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-no-img.tentative.sub.html
new file mode 100644
index 0000000000..6ca192edac
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-no-img.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): picture-source-no-img</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-no-img', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></picture>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-nomatch-media.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-nomatch-media.tentative.sub.html
new file mode 100644
index 0000000000..d48d3104f2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-nomatch-media.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): picture-source-nomatch-media</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-nomatch-media', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" media="not all"><img></picture>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-unsupported-type.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-unsupported-type.tentative.sub.html
new file mode 100644
index 0000000000..e387d51bab
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/picture-source-unsupported-type.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): picture-source-unsupported-type</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-unsupported-type', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" type=text/plain><img></picture>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-async.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-async.tentative.sub.html
new file mode 100644
index 0000000000..f4b42c484e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-async.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-async</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-async', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" async><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-crossorigin.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-crossorigin.tentative.sub.html
new file mode 100644
index 0000000000..b95f5502ef
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-crossorigin.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-crossorigin', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" crossorigin><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-defer.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-defer.tentative.sub.html
new file mode 100644
index 0000000000..5cf8e15c67
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-defer.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-defer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-defer', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" defer><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-integrity.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-integrity.tentative.sub.html
new file mode 100644
index 0000000000..6f66636d76
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-integrity.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-integrity</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-integrity', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb"><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-module.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-module.tentative.sub.html
new file mode 100644
index 0000000000..229b2b88c4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-module.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-module</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-module', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" type=module><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-nomodule.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-nomodule.tentative.sub.html
new file mode 100644
index 0000000000..3f61dba8d4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-nomodule.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-nomodule</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'script-src-nomodule', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" nomodule><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-referrerpolicy-no-referrer.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-referrerpolicy-no-referrer.tentative.sub.html
new file mode 100644
index 0000000000..ea67a33a22
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-referrerpolicy-no-referrer.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-referrerpolicy-no-referrer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-referrerpolicy-no-referrer', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-type-application-ecmascript.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-type-application-ecmascript.tentative.sub.html
new file mode 100644
index 0000000000..000843abaa
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-type-application-ecmascript.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-type-application-ecmascript</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-type-application-ecmascript', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" type=application/ecmascript><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-unsupported-type.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-unsupported-type.tentative.sub.html
new file mode 100644
index 0000000000..1eaa56494d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src-unsupported-type.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src-unsupported-type</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'script-src-unsupported-type', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;" type=text/plain><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src.tentative.sub.html
new file mode 100644
index 0000000000..4339e82360
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-href.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-href.tentative.sub.html
new file mode 100644
index 0000000000..1f7974c0dd
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-href.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): svg-image-href</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-image-href', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <svg><image href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></image></svg>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-src.tentative.sub.html
new file mode 100644
index 0000000000..50b6320b68
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): svg-image-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'svg-image-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <svg><image src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></image></svg>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-xlinkhref.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-xlinkhref.tentative.sub.html
new file mode 100644
index 0000000000..7d8a0f0357
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-image-xlinkhref.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): svg-image-xlinkhref</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-image-xlinkhref', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <svg><image xlink:href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></image></svg>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-href.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-href.tentative.sub.html
new file mode 100644
index 0000000000..13b6cda989
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-href.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): svg-script-href</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-script-href', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <svg><script href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></svg>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub.html
new file mode 100644
index 0000000000..ea37cc74a4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): svg-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'svg-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <svg><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></svg>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-xlinkhref.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-xlinkhref.tentative.sub.html
new file mode 100644
index 0000000000..df0f28ec47
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/svg-script-xlinkhref.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): svg-script-xlinkhref</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-script-xlinkhref', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <svg><script xlink:href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></svg>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-img-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-img-src.tentative.sub.html
new file mode 100644
index 0000000000..60928c10e9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-img-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): template-img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'template-img-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <template><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></template>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-link-stylesheet.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-link-stylesheet.tentative.sub.html
new file mode 100644
index 0000000000..590975e370
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-link-stylesheet.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): template-link-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'template-link-stylesheet', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <template><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></template>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-script-src.tentative.sub.html
new file mode 100644
index 0000000000..b9e4177d8d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): template-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'template-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <template><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></template>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-img-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-img-src.tentative.sub.html
new file mode 100644
index 0000000000..d9594f11ae
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-img-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): template-shadowrootmode-img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'template-shadowrootmode-img-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <div><template shadowrootmode="closed"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></template></div>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-link-stylesheet.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-link-stylesheet.tentative.sub.html
new file mode 100644
index 0000000000..f536059720
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-link-stylesheet.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): template-shadowrootmode-link-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'template-shadowrootmode-link-stylesheet', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <div><template shadowrootmode="closed"><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></template></div>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-script-src.tentative.sub.html
new file mode 100644
index 0000000000..a9b6798da2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/template-shadowrootmode-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): template-shadowrootmode-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'template-shadowrootmode-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <div><template shadowrootmode="closed"><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></template></div>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/video-poster.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/video-poster.tentative.sub.html
new file mode 100644
index 0000000000..e14bf19fc9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/video-poster.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): video-poster</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'video-poster', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <video poster="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"></video>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/xmp-script-src.tentative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/xmp-script-src.tentative.sub.html
new file mode 100644
index 0000000000..89ecaf1981
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/document-write/xmp-script-src.tentative.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, document.write(): xmp-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'xmp-script-src', true))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay=1500"><\/script>
+ <script>
+ document.write('<plaintext>');
+ <\/script>
+ <\!-- speculative case in document.write -->
+ <xmp><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"><\/script></xmp>
+ `);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/base-href-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/base-href-script-src.tentative.html
new file mode 100644
index 0000000000..d37d68333d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/base-href-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: base-href-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/base-href-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'base-href-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/image-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/image-src.tentative.html
new file mode 100644
index 0000000000..40929371ca
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/image-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: image-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/image-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'image-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-data-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-data-src.tentative.html
new file mode 100644
index 0000000000..54bed4582a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-data-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: img-data-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/img-data-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'img-data-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-crossorigin.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-crossorigin.tentative.html
new file mode 100644
index 0000000000..3b47252140
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-crossorigin.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: img-src-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/img-src-crossorigin-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-src-crossorigin', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-loading-lazy.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-loading-lazy.tentative.html
new file mode 100644
index 0000000000..7fd4a1207a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-loading-lazy.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: img-src-loading-lazy</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/img-src-loading-lazy-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'img-src-loading-lazy', false))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-referrerpolicy-no-referrer.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-referrerpolicy-no-referrer.tentative.html
new file mode 100644
index 0000000000..e41d2c3ef1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src-referrerpolicy-no-referrer.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: img-src-referrerpolicy-no-referrer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/img-src-referrerpolicy-no-referrer-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-src-referrerpolicy-no-referrer', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src.tentative.html
new file mode 100644
index 0000000000..fda270a9f2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/img-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-srcset.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-srcset.tentative.html
new file mode 100644
index 0000000000..5878e14600
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/img-srcset.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: img-srcset</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/img-srcset-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'img-srcset', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-no-rel.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-no-rel.tentative.html
new file mode 100644
index 0000000000..7b9255175b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-no-rel.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-no-rel</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-no-rel-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-no-rel', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-alternate-stylesheet.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-alternate-stylesheet.tentative.html
new file mode 100644
index 0000000000..7a8eb2ac53
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-alternate-stylesheet.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-alternate-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-alternate-stylesheet-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-alternate-stylesheet', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-font-crossorigin.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-font-crossorigin.tentative.html
new file mode 100644
index 0000000000..f679c97c59
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-font-crossorigin.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-preload-as-font-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-preload-as-font-crossorigin-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-font-crossorigin', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-image.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-image.tentative.html
new file mode 100644
index 0000000000..adc59b7e09
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-image.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-preload-as-image</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-preload-as-image-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-image', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-script.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-script.tentative.html
new file mode 100644
index 0000000000..705b56b9a9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-script.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-preload-as-script</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-preload-as-script-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-script', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-style.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-style.tentative.html
new file mode 100644
index 0000000000..ea0cd3945d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-preload-as-style.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-preload-as-style</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-preload-as-style-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-preload-as-style', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-crossorigin.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-crossorigin.tentative.html
new file mode 100644
index 0000000000..c227593727
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-crossorigin.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-crossorigin-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-crossorigin', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-disabled.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-disabled.tentative.html
new file mode 100644
index 0000000000..5f232aa07a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-disabled.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet-disabled</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-disabled-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-disabled', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-integrity.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-integrity.tentative.html
new file mode 100644
index 0000000000..266fb5b7da
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-integrity.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet-integrity</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-integrity-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-integrity', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-nomatch-media.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-nomatch-media.tentative.html
new file mode 100644
index 0000000000..49feea0bb0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-nomatch-media.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet-nomatch-media</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-nomatch-media-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-nomatch-media', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.html
new file mode 100644
index 0000000000..635f23444f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-referrerpolicy-no-referrer.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet-referrerpolicy-no-referrer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-referrerpolicy-no-referrer-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-referrerpolicy-no-referrer', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-type-text-css.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-type-text-css.tentative.html
new file mode 100644
index 0000000000..a2af61a95d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-type-text-css.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet-type-text-css</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-type-text-css-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-type-text-css', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-unsupported-type.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-unsupported-type.tentative.html
new file mode 100644
index 0000000000..17fb1722f5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet-unsupported-type.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet-unsupported-type</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-unsupported-type-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet-unsupported-type', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet.tentative.html
new file mode 100644
index 0000000000..07e4152c83
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/link-rel-stylesheet.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: link-rel-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/link-rel-stylesheet-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'link-rel-stylesheet', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-face-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-face-script-src.tentative.html
new file mode 100644
index 0000000000..47516bab94
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-face-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: math-font-face-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/math-font-face-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'math-font-face-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-script-src.tentative.html
new file mode 100644
index 0000000000..9a99c63df2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-font-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: math-font-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/math-font-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'math-font-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-script-src.tentative.html
new file mode 100644
index 0000000000..54e6d40f18
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/math-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: math-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/math-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'math-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-charset-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-charset-script-src.tentative.html
new file mode 100644
index 0000000000..e8dfaaf614
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-charset-script-src.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<!-- no meta charset -->
+<!-- (padding to exceed 1024 bytes processed by the character encoding scanner) -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<title>Speculative parsing, page load: meta-charset-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/meta-charset-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'meta-charset-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-asterisk.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-asterisk.tentative.html
new file mode 100644
index 0000000000..35d7374f8e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-asterisk.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: meta-csp-img-src-asterisk</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/meta-csp-img-src-asterisk-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'meta-csp-img-src-asterisk', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-none.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-none.tentative.html
new file mode 100644
index 0000000000..21984fd9e5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-csp-img-src-none.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: meta-csp-img-src-none</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/meta-csp-img-src-none-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'meta-csp-img-src-none', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-referrer-no-referrer-img-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-referrer-no-referrer-img-src.tentative.html
new file mode 100644
index 0000000000..7d03f49e6e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-referrer-no-referrer-img-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: meta-referrer-no-referrer-img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/meta-referrer-no-referrer-img-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'meta-referrer-no-referrer-img-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-viewport-link-stylesheet-media.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-viewport-link-stylesheet-media.tentative.html
new file mode 100644
index 0000000000..0ba60c7c04
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/meta-viewport-link-stylesheet-media.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: meta-viewport-link-stylesheet-media</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/meta-viewport-link-stylesheet-media-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'meta-viewport-link-stylesheet-media', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-1.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-1.tentative.html
new file mode 100644
index 0000000000..fe61837dcf
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-1.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: nested-template-shadowrootmode-1</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/nested-template-shadowrootmode-1-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'nested-template-shadowrootmode-1', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-2.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-2.tentative.html
new file mode 100644
index 0000000000..7d4732cd18
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/nested-template-shadowrootmode-2.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: nested-template-shadowrootmode-2</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/nested-template-shadowrootmode-2-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'nested-template-shadowrootmode-2', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-br-img.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-br-img.tentative.html
new file mode 100644
index 0000000000..38acd4707e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-br-img.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: picture-source-br-img</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/picture-source-br-img-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-br-img', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-no-img.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-no-img.tentative.html
new file mode 100644
index 0000000000..a7196c7740
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-no-img.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: picture-source-no-img</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/picture-source-no-img-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-no-img', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-nomatch-media.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-nomatch-media.tentative.html
new file mode 100644
index 0000000000..d1d5e2263c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-nomatch-media.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: picture-source-nomatch-media</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/picture-source-nomatch-media-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-nomatch-media', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-unsupported-type.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-unsupported-type.tentative.html
new file mode 100644
index 0000000000..981805569d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/picture-source-unsupported-type.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: picture-source-unsupported-type</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/picture-source-unsupported-type-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'picture-source-unsupported-type', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/base-href-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/base-href-script-src-framed.sub.html
new file mode 100644
index 0000000000..c0913c261e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/base-href-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): base-href-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<base href=//{{domains[www1]}}:{{ports[http][0]}}><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/image-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/image-src-framed.sub.html
new file mode 100644
index 0000000000..4a335c7b81
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/image-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): image-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<image src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-data-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-data-src-framed.sub.html
new file mode 100644
index 0000000000..20df9ba529
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-data-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): img-data-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<img data-src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-crossorigin-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-crossorigin-framed.sub.html
new file mode 100644
index 0000000000..9302abd3d5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-crossorigin-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): img-src-crossorigin</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-framed.sub.html
new file mode 100644
index 0000000000..ba2607e46d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): img-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-loading-lazy-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-loading-lazy-framed.sub.html
new file mode 100644
index 0000000000..9cc49cf324
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-loading-lazy-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): img-src-loading-lazy</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" loading=lazy>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-referrerpolicy-no-referrer-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-referrerpolicy-no-referrer-framed.sub.html
new file mode 100644
index 0000000000..7f466d11ba
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-src-referrerpolicy-no-referrer-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): img-src-referrerpolicy-no-referrer</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-srcset-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-srcset-framed.sub.html
new file mode 100644
index 0000000000..3c791bb857
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/img-srcset-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): img-srcset</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<img srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-no-rel-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-no-rel-framed.sub.html
new file mode 100644
index 0000000000..12e79cda04
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-no-rel-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-no-rel</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-alternate-stylesheet-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-alternate-stylesheet-framed.sub.html
new file mode 100644
index 0000000000..2d4dc3db12
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-alternate-stylesheet-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-alternate-stylesheet</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel="alternate stylesheet" href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-font-crossorigin-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-font-crossorigin-framed.sub.html
new file mode 100644
index 0000000000..e1ac7d5395
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-font-crossorigin-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-preload-as-font-crossorigin</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=preload as=font href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-image-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-image-framed.sub.html
new file mode 100644
index 0000000000..6cc48035d7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-image-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-preload-as-image</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=preload as=image href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-script-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-script-framed.sub.html
new file mode 100644
index 0000000000..10d1092ab1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-script-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-preload-as-script</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=preload as=script href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-style-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-style-framed.sub.html
new file mode 100644
index 0000000000..e77d4da77b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-preload-as-style-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-preload-as-style</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=preload as=style href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-crossorigin-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-crossorigin-framed.sub.html
new file mode 100644
index 0000000000..4dc7917f4b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-crossorigin-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet-crossorigin</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-disabled-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-disabled-framed.sub.html
new file mode 100644
index 0000000000..ec535addfd
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-disabled-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet-disabled</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel="stylesheet" href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" disabled>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-framed.sub.html
new file mode 100644
index 0000000000..7b3bf61019
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-integrity-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-integrity-framed.sub.html
new file mode 100644
index 0000000000..752077d674
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-integrity-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet-integrity</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-nomatch-media-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-nomatch-media-framed.sub.html
new file mode 100644
index 0000000000..8ddb717c60
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-nomatch-media-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet-nomatch-media</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" media="not all">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-referrerpolicy-no-referrer-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-referrerpolicy-no-referrer-framed.sub.html
new file mode 100644
index 0000000000..2f149ef438
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-referrerpolicy-no-referrer-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet-referrerpolicy-no-referrer</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-type-text-css-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-type-text-css-framed.sub.html
new file mode 100644
index 0000000000..c402422868
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-type-text-css-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet-type-text-css</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/css>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-unsupported-type-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-unsupported-type-framed.sub.html
new file mode 100644
index 0000000000..f0c5016081
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/link-rel-stylesheet-unsupported-type-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): link-rel-stylesheet-unsupported-type</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/plain>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-face-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-face-script-src-framed.sub.html
new file mode 100644
index 0000000000..90e99fb0d9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-face-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): math-font-face-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<math><font face><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></font></math>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-script-src-framed.sub.html
new file mode 100644
index 0000000000..fc359406ff
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-font-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): math-font-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<math><font><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></font></math>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-script-src-framed.sub.html
new file mode 100644
index 0000000000..71697e6766
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/math-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): math-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<math><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></math>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-charset-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-charset-script-src-framed.sub.html
new file mode 100644
index 0000000000..001c0c2eea
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-charset-script-src-framed.sub.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<!-- no meta charset -->
+<!-- (padding to exceed 1024 bytes processed by the character encoding scanner) -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<title>Speculative parsing, page load (helper file): meta-charset-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<meta charset=windows-1254><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-asterisk-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-asterisk-framed.sub.html
new file mode 100644
index 0000000000..3ac046dd67
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-asterisk-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): meta-csp-img-src-asterisk</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; img-src *"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-none-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-none-framed.sub.html
new file mode 100644
index 0000000000..08929d860b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-csp-img-src-none-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): meta-csp-img-src-none</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; img-src 'none'"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-referrer-no-referrer-img-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-referrer-no-referrer-img-src-framed.sub.html
new file mode 100644
index 0000000000..a8a344909e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-referrer-no-referrer-img-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): meta-referrer-no-referrer-img-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<meta name=referrer content=no-referrer><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-viewport-link-stylesheet-media-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-viewport-link-stylesheet-media-framed.sub.html
new file mode 100644
index 0000000000..8e294e8c46
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/meta-viewport-link-stylesheet-media-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): meta-viewport-link-stylesheet-media</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<meta name=viewport content="width=400, initial-scale=1"><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" media="(min-width: 401px)">
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-1-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-1-framed.sub.html
new file mode 100644
index 0000000000..19e60e9828
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-1-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): nested-template-shadowrootmode-1</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<template><div><template shadowrootmode="closed"><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template></div></template>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-2-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-2-framed.sub.html
new file mode 100644
index 0000000000..5cea082c4f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/nested-template-shadowrootmode-2-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): nested-template-shadowrootmode-2</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<div><template shadowrootmode="closed"><template><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template></template></div>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-br-img-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-br-img-framed.sub.html
new file mode 100644
index 0000000000..aea18d1e04
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-br-img-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): picture-source-br-img</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"><br><img></picture>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-no-img-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-no-img-framed.sub.html
new file mode 100644
index 0000000000..5367cd4085
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-no-img-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): picture-source-no-img</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></picture>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-nomatch-media-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-nomatch-media-framed.sub.html
new file mode 100644
index 0000000000..a8e696f901
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-nomatch-media-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): picture-source-nomatch-media</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" media="not all"><img></picture>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-unsupported-type-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-unsupported-type-framed.sub.html
new file mode 100644
index 0000000000..0ed95368c0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/picture-source-unsupported-type-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): picture-source-unsupported-type</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/plain><img></picture>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-async-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-async-framed.sub.html
new file mode 100644
index 0000000000..5e6cbff21d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-async-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-async</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" async></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-crossorigin-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-crossorigin-framed.sub.html
new file mode 100644
index 0000000000..098d07c5e3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-crossorigin-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-crossorigin</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-defer-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-defer-framed.sub.html
new file mode 100644
index 0000000000..7decc7bc60
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-defer-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-defer</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" defer></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-framed.sub.html
new file mode 100644
index 0000000000..baab010750
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-integrity-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-integrity-framed.sub.html
new file mode 100644
index 0000000000..7c0ca5e59f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-integrity-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-integrity</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-module-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-module-framed.sub.html
new file mode 100644
index 0000000000..5c365229df
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-module-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-module</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=module></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-nomodule-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-nomodule-framed.sub.html
new file mode 100644
index 0000000000..46af7c87e2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-nomodule-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-nomodule</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" nomodule></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-referrerpolicy-no-referrer-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-referrerpolicy-no-referrer-framed.sub.html
new file mode 100644
index 0000000000..566d2f67e0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-referrerpolicy-no-referrer-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-referrerpolicy-no-referrer</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-type-application-ecmascript-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-type-application-ecmascript-framed.sub.html
new file mode 100644
index 0000000000..08609b1c12
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-type-application-ecmascript-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-type-application-ecmascript</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=application/ecmascript></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-unsupported-type-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-unsupported-type-framed.sub.html
new file mode 100644
index 0000000000..1f38560ee1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/script-src-unsupported-type-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): script-src-unsupported-type</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/plain></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-href-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-href-framed.sub.html
new file mode 100644
index 0000000000..fc4e531849
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-href-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): svg-image-href</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<svg><image href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></image></svg>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-src-framed.sub.html
new file mode 100644
index 0000000000..16d97e00eb
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): svg-image-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<svg><image src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></image></svg>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-xlinkhref-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-xlinkhref-framed.sub.html
new file mode 100644
index 0000000000..8e5dd1e8dc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-image-xlinkhref-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): svg-image-xlinkhref</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<svg><image xlink:href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></image></svg>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-href-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-href-framed.sub.html
new file mode 100644
index 0000000000..6a208cbc4f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-href-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): svg-script-href</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<svg><script href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></svg>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-src-framed.sub.html
new file mode 100644
index 0000000000..e8c1784c60
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): svg-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<svg><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></svg>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-xlinkhref-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-xlinkhref-framed.sub.html
new file mode 100644
index 0000000000..e634000d01
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/svg-script-xlinkhref-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): svg-script-xlinkhref</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<svg><script xlink:href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></svg>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-img-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-img-src-framed.sub.html
new file mode 100644
index 0000000000..1f8986c2d1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-img-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): template-img-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<template><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-link-stylesheet-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-link-stylesheet-framed.sub.html
new file mode 100644
index 0000000000..cfc2502335
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-link-stylesheet-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): template-link-stylesheet</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<template><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-script-src-framed.sub.html
new file mode 100644
index 0000000000..8582d10ece
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): template-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<template><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-img-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-img-src-framed.sub.html
new file mode 100644
index 0000000000..32d0aa9a45
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-img-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): template-shadowrootmode-img-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<div><template shadowrootmode="closed"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template></div>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-link-stylesheet-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-link-stylesheet-framed.sub.html
new file mode 100644
index 0000000000..3557749c81
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-link-stylesheet-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): template-shadowrootmode-link-stylesheet</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<div><template shadowrootmode="closed"><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template></div>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-script-src-framed.sub.html
new file mode 100644
index 0000000000..ab4ad10df4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/template-shadowrootmode-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): template-shadowrootmode-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<div><template shadowrootmode="closed"><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template></div>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/video-poster-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/video-poster-framed.sub.html
new file mode 100644
index 0000000000..ad2c7cef95
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/video-poster-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): video-poster</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<video poster="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></video>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/xmp-script-src-framed.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/xmp-script-src-framed.sub.html
new file mode 100644
index 0000000000..3c5ea9252e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/resources/xmp-script-src-framed.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load (helper file): xmp-script-src</title>
+<script src="/common/slow.py?delay=1500"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+<xmp><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></xmp>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-async.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-async.tentative.html
new file mode 100644
index 0000000000..d462508a7e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-async.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-async</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-async-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-async', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-crossorigin.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-crossorigin.tentative.html
new file mode 100644
index 0000000000..59d3d9e07e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-crossorigin.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-crossorigin</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-crossorigin-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-crossorigin', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-defer.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-defer.tentative.html
new file mode 100644
index 0000000000..b8bf489890
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-defer.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-defer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-defer-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-defer', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-integrity.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-integrity.tentative.html
new file mode 100644
index 0000000000..42719e994a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-integrity.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-integrity</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-integrity-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-integrity', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-module.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-module.tentative.html
new file mode 100644
index 0000000000..606a1ff469
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-module.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-module</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-module-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-module', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-nomodule.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-nomodule.tentative.html
new file mode 100644
index 0000000000..20ca80222e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-nomodule.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-nomodule</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-nomodule-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'script-src-nomodule', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-referrerpolicy-no-referrer.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-referrerpolicy-no-referrer.tentative.html
new file mode 100644
index 0000000000..cf35f5262b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-referrerpolicy-no-referrer.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-referrerpolicy-no-referrer</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-referrerpolicy-no-referrer-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-referrerpolicy-no-referrer', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-type-application-ecmascript.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-type-application-ecmascript.tentative.html
new file mode 100644
index 0000000000..611afc7085
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-type-application-ecmascript.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-type-application-ecmascript</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-type-application-ecmascript-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src-type-application-ecmascript', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-unsupported-type.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-unsupported-type.tentative.html
new file mode 100644
index 0000000000..c7c5b483a4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src-unsupported-type.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src-unsupported-type</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-unsupported-type-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'script-src-unsupported-type', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src.tentative.html
new file mode 100644
index 0000000000..1d545ff0e1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-href.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-href.tentative.html
new file mode 100644
index 0000000000..b5e22c1de9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-href.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: svg-image-href</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/svg-image-href-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-image-href', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-src.tentative.html
new file mode 100644
index 0000000000..63232fa492
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: svg-image-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/svg-image-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'svg-image-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-xlinkhref.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-xlinkhref.tentative.html
new file mode 100644
index 0000000000..9887004bc4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-image-xlinkhref.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: svg-image-xlinkhref</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/svg-image-xlinkhref-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-image-xlinkhref', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-href.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-href.tentative.html
new file mode 100644
index 0000000000..54d4e313f9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-href.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: svg-script-href</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/svg-script-href-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-script-href', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-src.tentative.html
new file mode 100644
index 0000000000..8b2397e071
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: svg-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/svg-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'svg-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-xlinkhref.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-xlinkhref.tentative.html
new file mode 100644
index 0000000000..08b4efc3ee
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/svg-script-xlinkhref.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: svg-script-xlinkhref</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/svg-script-xlinkhref-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'svg-script-xlinkhref', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-img-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-img-src.tentative.html
new file mode 100644
index 0000000000..bd83a9359a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-img-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: template-img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/template-img-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'template-img-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-link-stylesheet.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-link-stylesheet.tentative.html
new file mode 100644
index 0000000000..d7718bc23b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-link-stylesheet.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: template-link-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/template-link-stylesheet-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'template-link-stylesheet', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-script-src.tentative.html
new file mode 100644
index 0000000000..f5c23dddb2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: template-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/template-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'template-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-img-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-img-src.tentative.html
new file mode 100644
index 0000000000..96a48dc739
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-img-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: template-shadowrootmode-img-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/template-shadowrootmode-img-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'template-shadowrootmode-img-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-link-stylesheet.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-link-stylesheet.tentative.html
new file mode 100644
index 0000000000..3f08ab3bb7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-link-stylesheet.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: template-shadowrootmode-link-stylesheet</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/template-shadowrootmode-link-stylesheet-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'template-shadowrootmode-link-stylesheet', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-script-src.tentative.html
new file mode 100644
index 0000000000..6d78fcfff1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/template-shadowrootmode-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: template-shadowrootmode-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/template-shadowrootmode-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'template-shadowrootmode-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/video-poster.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/video-poster.tentative.html
new file mode 100644
index 0000000000..73b9cb50de
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/video-poster.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: video-poster</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/video-poster-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, true)
+ .then(compare_with_nonspeculative(uuid, 'video-poster', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/xmp-script-src.tentative.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/xmp-script-src.tentative.html
new file mode 100644
index 0000000000..cee708ff72
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/page-load/xmp-script-src.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, page load: xmp-script-src</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({single_test: true});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/xmp-script-src-framed.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, false)
+ .then(compare_with_nonspeculative(uuid, 'xmp-script-src', true))
+ .then(done);
+</script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/base-href-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/base-href-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..e860f0e93d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/base-href-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): base-href-script-src</title>
+<!-- non-speculative case -->
+<base href=//{{domains[www1]}}:{{ports[http][0]}}><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/image-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/image-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..3d0ec70855
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/image-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): image-src</title>
+<!-- non-speculative case -->
+<image src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-data-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-data-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..f739f59070
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-data-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): img-data-src</title>
+<!-- non-speculative case -->
+<img data-src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-crossorigin-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-crossorigin-nonspeculative.sub.html
new file mode 100644
index 0000000000..bb7c7d2c84
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-crossorigin-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): img-src-crossorigin</title>
+<!-- non-speculative case -->
+<img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..dc27cf837c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): img-src</title>
+<!-- non-speculative case -->
+<img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-referrerpolicy-no-referrer-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-referrerpolicy-no-referrer-nonspeculative.sub.html
new file mode 100644
index 0000000000..6fd38fdc0c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-src-referrerpolicy-no-referrer-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): img-src-referrerpolicy-no-referrer</title>
+<!-- non-speculative case -->
+<img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-srcset-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-srcset-nonspeculative.sub.html
new file mode 100644
index 0000000000..3adcae9dce
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/img-srcset-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): img-srcset</title>
+<!-- non-speculative case -->
+<img srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-no-rel-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-no-rel-nonspeculative.sub.html
new file mode 100644
index 0000000000..50fee5964c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-no-rel-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-no-rel</title>
+<!-- non-speculative case -->
+<link href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-alternate-stylesheet-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-alternate-stylesheet-nonspeculative.sub.html
new file mode 100644
index 0000000000..cdd3a0cfaa
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-alternate-stylesheet-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-alternate-stylesheet</title>
+<!-- non-speculative case -->
+<link rel="alternate stylesheet" href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-font-crossorigin-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-font-crossorigin-nonspeculative.sub.html
new file mode 100644
index 0000000000..6329cd0fcb
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-font-crossorigin-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-preload-as-font-crossorigin</title>
+<!-- non-speculative case -->
+<link rel=preload as=font href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-image-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-image-nonspeculative.sub.html
new file mode 100644
index 0000000000..80258dfa63
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-image-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-preload-as-image</title>
+<!-- non-speculative case -->
+<link rel=preload as=image href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-script-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-script-nonspeculative.sub.html
new file mode 100644
index 0000000000..04fe1b1dc0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-script-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-preload-as-script</title>
+<!-- non-speculative case -->
+<link rel=preload as=script href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-style-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-style-nonspeculative.sub.html
new file mode 100644
index 0000000000..4cc7ef40bc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-preload-as-style-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-preload-as-style</title>
+<!-- non-speculative case -->
+<link rel=preload as=style href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-crossorigin-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-crossorigin-nonspeculative.sub.html
new file mode 100644
index 0000000000..81a28c07a3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-crossorigin-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet-crossorigin</title>
+<!-- non-speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-disabled-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-disabled-nonspeculative.sub.html
new file mode 100644
index 0000000000..5584e7d4fc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-disabled-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet-disabled</title>
+<!-- non-speculative case -->
+<link rel="stylesheet" href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" disabled>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-integrity-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-integrity-nonspeculative.sub.html
new file mode 100644
index 0000000000..9e9ae176a5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-integrity-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet-integrity</title>
+<!-- non-speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nomatch-media-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nomatch-media-nonspeculative.sub.html
new file mode 100644
index 0000000000..b10375c2c9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nomatch-media-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet-nomatch-media</title>
+<!-- non-speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" media="not all">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nonspeculative.sub.html
new file mode 100644
index 0000000000..4aeb803ca3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet</title>
+<!-- non-speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-referrerpolicy-no-referrer-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-referrerpolicy-no-referrer-nonspeculative.sub.html
new file mode 100644
index 0000000000..0c79457bdd
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-referrerpolicy-no-referrer-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet-referrerpolicy-no-referrer</title>
+<!-- non-speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-type-text-css-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-type-text-css-nonspeculative.sub.html
new file mode 100644
index 0000000000..c851eee104
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-type-text-css-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet-type-text-css</title>
+<!-- non-speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/css>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-unsupported-type-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-unsupported-type-nonspeculative.sub.html
new file mode 100644
index 0000000000..7db5b49cd7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/link-rel-stylesheet-unsupported-type-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): link-rel-stylesheet-unsupported-type</title>
+<!-- non-speculative case -->
+<link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/plain>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-face-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-face-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..1ea61d5a28
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-face-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): math-font-face-script-src</title>
+<!-- non-speculative case -->
+<math><font face><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></font></math>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..d3d6567a42
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-font-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): math-font-script-src</title>
+<!-- non-speculative case -->
+<math><font><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></font></math>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..7e27036e46
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/math-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): math-script-src</title>
+<!-- non-speculative case -->
+<math><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></math>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-charset-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-charset-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..2a47f520ae
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-charset-script-src-nonspeculative.sub.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<!-- no meta charset -->
+<!-- (padding to exceed 1024 bytes processed by the character encoding scanner) -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<title>Speculative parsing, non-speculative (helper file): meta-charset-script-src</title>
+<!-- non-speculative case -->
+<!-- no meta charset --><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-asterisk-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-asterisk-nonspeculative.sub.html
new file mode 100644
index 0000000000..50954c86c3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-asterisk-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): meta-csp-img-src-asterisk</title>
+<!-- non-speculative case -->
+<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; img-src *"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-none-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-none-nonspeculative.sub.html
new file mode 100644
index 0000000000..fe651e834e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-csp-img-src-none-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): meta-csp-img-src-none</title>
+<!-- non-speculative case -->
+<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; img-src 'none'"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-referrer-no-referrer-img-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-referrer-no-referrer-img-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..0e7a13ee32
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-referrer-no-referrer-img-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): meta-referrer-no-referrer-img-src</title>
+<!-- non-speculative case -->
+<meta name=referrer content=no-referrer><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-viewport-link-stylesheet-media-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-viewport-link-stylesheet-media-nonspeculative.sub.html
new file mode 100644
index 0000000000..cb0f992adb
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/meta-viewport-link-stylesheet-media-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): meta-viewport-link-stylesheet-media</title>
+<!-- non-speculative case -->
+<meta name=viewport content="width=400, initial-scale=1"><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" media="(min-width: 401px)">
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-1-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-1-nonspeculative.sub.html
new file mode 100644
index 0000000000..ec14d9b9c4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-1-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): nested-template-shadowrootmode-1</title>
+<!-- non-speculative case -->
+<template><div><template shadowrootmode="closed"><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template></div></template>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-2-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-2-nonspeculative.sub.html
new file mode 100644
index 0000000000..71231ee70c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/nested-template-shadowrootmode-2-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): nested-template-shadowrootmode-2</title>
+<!-- non-speculative case -->
+<div><template shadowrootmode="closed"><template><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template></template></div>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-br-img-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-br-img-nonspeculative.sub.html
new file mode 100644
index 0000000000..371600284d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-br-img-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): picture-source-br-img</title>
+<!-- non-speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"><br><img></picture>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-no-img-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-no-img-nonspeculative.sub.html
new file mode 100644
index 0000000000..858485acef
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-no-img-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): picture-source-no-img</title>
+<!-- non-speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></picture>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-nomatch-media-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-nomatch-media-nonspeculative.sub.html
new file mode 100644
index 0000000000..e4dc1444ce
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-nomatch-media-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): picture-source-nomatch-media</title>
+<!-- non-speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" media="not all"><img></picture>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-unsupported-type-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-unsupported-type-nonspeculative.sub.html
new file mode 100644
index 0000000000..2c7372f1f9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/picture-source-unsupported-type-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): picture-source-unsupported-type</title>
+<!-- non-speculative case -->
+<picture><source srcset="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/plain><img></picture>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-async-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-async-nonspeculative.sub.html
new file mode 100644
index 0000000000..cf8acf5fe0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-async-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-async</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" async></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-crossorigin-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-crossorigin-nonspeculative.sub.html
new file mode 100644
index 0000000000..c3e200c0aa
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-crossorigin-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-crossorigin</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" crossorigin></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-defer-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-defer-nonspeculative.sub.html
new file mode 100644
index 0000000000..2bda3229e3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-defer-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-defer</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" defer></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-integrity-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-integrity-nonspeculative.sub.html
new file mode 100644
index 0000000000..a52d956e7b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-integrity-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-integrity</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb"></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-module-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-module-nonspeculative.sub.html
new file mode 100644
index 0000000000..7cb2839621
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-module-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-module</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=module></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nomodule-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nomodule-nonspeculative.sub.html
new file mode 100644
index 0000000000..1ffedbb55a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nomodule-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-nomodule</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" nomodule></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..494f854d5e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-referrerpolicy-no-referrer-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-referrerpolicy-no-referrer-nonspeculative.sub.html
new file mode 100644
index 0000000000..4581eb03ad
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-referrerpolicy-no-referrer-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-referrerpolicy-no-referrer</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" referrerpolicy=no-referrer></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-type-application-ecmascript-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-type-application-ecmascript-nonspeculative.sub.html
new file mode 100644
index 0000000000..1746cd2e42
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-type-application-ecmascript-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-type-application-ecmascript</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=application/ecmascript></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-unsupported-type-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-unsupported-type-nonspeculative.sub.html
new file mode 100644
index 0000000000..94098b5c77
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/script-src-unsupported-type-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): script-src-unsupported-type</title>
+<!-- non-speculative case -->
+<script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;" type=text/plain></script>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-href-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-href-nonspeculative.sub.html
new file mode 100644
index 0000000000..6c61c3208a
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-href-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): svg-image-href</title>
+<!-- non-speculative case -->
+<svg><image href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></image></svg>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..0aba339a16
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): svg-image-src</title>
+<!-- non-speculative case -->
+<svg><image src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></image></svg>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-xlinkhref-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-xlinkhref-nonspeculative.sub.html
new file mode 100644
index 0000000000..c4e9e3f8b7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-image-xlinkhref-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): svg-image-xlinkhref</title>
+<!-- non-speculative case -->
+<svg><image xlink:href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></image></svg>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-href-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-href-nonspeculative.sub.html
new file mode 100644
index 0000000000..9a28a6cd12
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-href-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): svg-script-href</title>
+<!-- non-speculative case -->
+<svg><script href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></svg>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..c37091ce3c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): svg-script-src</title>
+<!-- non-speculative case -->
+<svg><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></svg>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-xlinkhref-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-xlinkhref-nonspeculative.sub.html
new file mode 100644
index 0000000000..376fafdcf1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/svg-script-xlinkhref-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): svg-script-xlinkhref</title>
+<!-- non-speculative case -->
+<svg><script xlink:href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></svg>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-img-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-img-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..db331fc88b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-img-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): template-img-src</title>
+<!-- non-speculative case -->
+<template><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-link-stylesheet-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-link-stylesheet-nonspeculative.sub.html
new file mode 100644
index 0000000000..cc3072d890
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-link-stylesheet-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): template-link-stylesheet</title>
+<!-- non-speculative case -->
+<template><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..0ec64a0163
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): template-script-src</title>
+<!-- non-speculative case -->
+<template><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-img-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-img-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..ec3ca52442
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-img-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): template-shadowrootmode-img-src</title>
+<!-- non-speculative case -->
+<div><template shadowrootmode="closed"><img src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template></div>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-link-stylesheet-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-link-stylesheet-nonspeculative.sub.html
new file mode 100644
index 0000000000..ed813b3fd5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-link-stylesheet-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): template-shadowrootmode-link-stylesheet</title>
+<!-- non-speculative case -->
+<div><template shadowrootmode="closed"><link rel=stylesheet href="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></template></div>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..cd401584d8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/template-shadowrootmode-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): template-shadowrootmode-script-src</title>
+<!-- non-speculative case -->
+<div><template shadowrootmode="closed"><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></template></div>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/video-poster-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/video-poster-nonspeculative.sub.html
new file mode 100644
index 0000000000..c233e899cb
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/video-poster-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): video-poster</title>
+<!-- non-speculative case -->
+<video poster="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></video>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/xmp-script-src-nonspeculative.sub.html b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/xmp-script-src-nonspeculative.sub.html
new file mode 100644
index 0000000000..e3a93d441d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/generated/resources/xmp-script-src-nonspeculative.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->
+<meta charset=utf-8>
+<title>Speculative parsing, non-speculative (helper file): xmp-script-src</title>
+<!-- non-speculative case -->
+<xmp><script src="/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"></script></xmp>
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay=1500"></script>
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/resources/speculative-parsing-util.js b/testing/web-platform/tests/html/syntax/speculative-parsing/resources/speculative-parsing-util.js
new file mode 100644
index 0000000000..b675665ca9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/resources/speculative-parsing-util.js
@@ -0,0 +1,58 @@
+function expect_fetched_onload(uuid, expectation) {
+ return new Promise(resolve => {
+ addEventListener('load', resolve);
+ }).then(async () => {
+ const result = await get_result(uuid);
+ if (expectation) {
+ assert_not_equals(result, '', 'speculative case did not fetch');
+ } else {
+ assert_equals(result, '', 'speculative case incorrectly fetched');
+ }
+ return result;
+ });
+}
+
+function compare_with_nonspeculative(uuid, title, test_nonspeculative) {
+ return function(speculative_result) {
+ if (!test_nonspeculative) {
+ return Promise.resolve();
+ }
+ return new Promise(resolve => {
+ const iframe = document.createElement('iframe');
+ iframe.onload = resolve;
+ iframe.src = `../resources/${title}-nonspeculative.sub.html?uuid=${uuid}`;
+ document.body.appendChild(iframe);
+ }).then(async () => {
+ const result = await get_result(uuid);
+ if (speculative_result === '') {
+ assert_equals(result, '', 'non-speculative case incorrectly fetched')
+ } else {
+ assert_not_equals(result, '', 'non-speculative case did not fetch');
+ const speculative_headers = speculative_result.trim().split("\n");
+ const nonspeculative_headers = result.trim().split("\n");
+ assert_equals(speculative_headers.length, nonspeculative_headers.length, 'expected the same number of headers between speculative and non-speculative')
+ for (let i = 0; i < speculative_headers.length; ++i) {
+ let [s_header, s_value] = split_header(speculative_headers[i]);
+ let [ns_header, ns_value] = split_header(nonspeculative_headers[i]);
+ assert_equals(s_header, ns_header, 'expected the order of headers to match between speculative and non-speculative');
+ assert_equals(s_value, ns_value, `expected \`${s_header}\` values to match between speculative and non-speculative`);
+ }
+ }
+ });
+ }
+}
+
+function split_header(line) {
+ let [header, value] = line.split(': ');
+ header = header.toLowerCase();
+ value = value.trim();
+ if (header === 'referer') {
+ value = value.replace(/\/generated\/.+$/, '/generated/...');
+ }
+ return [header, value];
+}
+
+async function get_result(uuid) {
+ const response = await fetch(`/html/syntax/speculative-parsing/resources/stash.py?action=take&uuid=${uuid}`);
+ return await response.text();
+}
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/resources/stash.py b/testing/web-platform/tests/html/syntax/speculative-parsing/resources/stash.py
new file mode 100644
index 0000000000..5ad04e1558
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/resources/stash.py
@@ -0,0 +1,12 @@
+def main(request, response):
+ if request.GET[b"action"] == b"put":
+ encodingcheck = u"param-encodingcheck: " + request.url_parts.query.split(u"&encodingcheck=")[1] + u"\r\n"
+ headers = []
+ for line in str(request.raw_headers).split(u'\n'):
+ header = line.split(':')[0]
+ # TODO(zcorpan): also test Cookie
+ if header in [u'Origin', u'Accept', u'Referer']:
+ headers.append(line)
+ request.server.stash.put(request.GET[b"uuid"], encodingcheck + u"\r\n".join(headers))
+ return u''
+ return request.server.stash.take(request.GET[b"uuid"])
diff --git a/testing/web-platform/tests/html/syntax/speculative-parsing/tools/generate.py b/testing/web-platform/tests/html/syntax/speculative-parsing/tools/generate.py
new file mode 100755
index 0000000000..3457f94d70
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/speculative-parsing/tools/generate.py
@@ -0,0 +1,701 @@
+#!/usr/bin/env python3
+
+# Usage: python3 generate.py
+#
+# This will remove all existing .html files in the generated directories and generate new tests.
+
+
+# Notes on potential confusion with the 3 string substitution features in different layers:
+#
+# - In Python strings when calling .format(): {something} or {}
+# To get a literal {} use {{}}.
+# The template_* variables are ones below are those that will use .format().
+# https://docs.python.org/3/library/string.html#formatstrings
+# - JS template literals: ${something}
+# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
+# - wptserve server-side substitution when generating a response: {{GET[something]}}
+# https://web-platform-tests.org/writing-tests/server-pipes.html#sub
+
+import os, shutil
+
+target_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/generated"
+
+delay = u'1500' # Lower value makes the test complete faster, but also higher risk of flaky results
+
+# Test data
+
+tentative_tests = [
+ # title,
+ # encoding,
+ # template_testcase_markup,
+ # template_nonspeculative_testcase_markup (if different from template_testcase_markup),
+ # expect_load,
+ # test_nonspeculative
+ (
+ u'script-src',
+ u'utf-8',
+ u'<script src="{}"></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'meta-charset-script-src',
+ None,
+ u'<meta charset=windows-1254><script src="{}"></script>',
+ u'<!-- no meta charset --><script src="{}"></script>',
+ u'true',
+ u'true'
+ ),
+ (
+ # This test is only valid on "mobile" where meta viewport has an effect
+ u'meta-viewport-link-stylesheet-media',
+ u'utf-8',
+ u'<meta name=viewport content="width=400, initial-scale=1"><link rel=stylesheet href="{}" media="(min-width: 401px)">',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'meta-csp-img-src-none',
+ u'utf-8',
+ u'<meta http-equiv="Content-Security-Policy" content="script-src \'self\' \'unsafe-inline\'; img-src \'none\'"><img src="{}">',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'meta-csp-img-src-asterisk',
+ u'utf-8',
+ u'<meta http-equiv="Content-Security-Policy" content="script-src \'self\' \'unsafe-inline\'; img-src *"><img src="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'meta-referrer-no-referrer-img-src',
+ u'utf-8',
+ u'<meta name=referrer content=no-referrer><img src="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'base-href-script-src',
+ u'utf-8',
+ u'<base href=//{{{{domains[www1]}}}}:{{{{ports[http][0]}}}}><script src="{}"></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'script-src-unsupported-type',
+ u'utf-8',
+ u'<script src="{}" type=text/plain></script>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'script-src-type-application-ecmascript',
+ u'utf-8',
+ u'<script src="{}" type=application/ecmascript></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'script-src-nomodule',
+ u'utf-8',
+ u'<script src="{}" nomodule></script>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'script-src-module',
+ u'utf-8',
+ u'<script src="{}" type=module></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'script-src-async',
+ u'utf-8',
+ u'<script src="{}" async></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'script-src-defer',
+ u'utf-8',
+ u'<script src="{}" defer></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'script-src-crossorigin',
+ u'utf-8',
+ u'<script src="{}" crossorigin></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'script-src-integrity',
+ u'utf-8',
+ u'<script src="{}" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb"></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'script-src-referrerpolicy-no-referrer',
+ u'utf-8',
+ u'<script src="{}" referrerpolicy=no-referrer></script>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'template-script-src',
+ u'utf-8',
+ u'<template><script src="{}"></script></template>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'template-link-stylesheet',
+ u'utf-8',
+ u'<template><link rel=stylesheet href="{}"></template>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'template-img-src',
+ u'utf-8',
+ u'<template><img src="{}"></template>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'template-shadowrootmode-script-src',
+ u'utf-8',
+ u'<div><template shadowrootmode="closed"><script src="{}"></script></template></div>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'template-shadowrootmode-link-stylesheet',
+ u'utf-8',
+ u'<div><template shadowrootmode="closed"><link rel=stylesheet href="{}"></template></div>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'template-shadowrootmode-img-src',
+ u'utf-8',
+ u'<div><template shadowrootmode="closed"><img src="{}"></template></div>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'nested-template-shadowrootmode-1',
+ u'utf-8',
+ u'<template><div><template shadowrootmode="closed"><script src="{}"></script></template></div></template>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'nested-template-shadowrootmode-2',
+ u'utf-8',
+ u'<div><template shadowrootmode="closed"><template><script src="{}"></script></template></template></div>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'link-no-rel',
+ u'utf-8',
+ u'<link href="{}">',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet',
+ u'utf-8',
+ u'<link rel=stylesheet href="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-alternate-stylesheet',
+ u'utf-8',
+ u'<link rel="alternate stylesheet" href="{}">',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet-disabled',
+ u'utf-8',
+ u'<link rel="stylesheet" href="{}" disabled>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet-nomatch-media',
+ u'utf-8',
+ u'<link rel=stylesheet href="{}" media="not all">',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet-unsupported-type',
+ u'utf-8',
+ u'<link rel=stylesheet href="{}" type=text/plain>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet-type-text-css',
+ u'utf-8',
+ u'<link rel=stylesheet href="{}" type=text/css>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet-crossorigin',
+ u'utf-8',
+ u'<link rel=stylesheet href="{}" crossorigin>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet-integrity',
+ u'utf-8',
+ u'<link rel=stylesheet href="{}" integrity="sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-stylesheet-referrerpolicy-no-referrer',
+ u'utf-8',
+ u'<link rel=stylesheet href="{}" referrerpolicy=no-referrer>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-preload-as-style',
+ u'utf-8',
+ u'<link rel=preload as=style href="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-preload-as-font-crossorigin',
+ u'utf-8',
+ u'<link rel=preload as=font href="{}" crossorigin>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-preload-as-script',
+ u'utf-8',
+ u'<link rel=preload as=script href="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'link-rel-preload-as-image',
+ u'utf-8',
+ u'<link rel=preload as=image href="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'img-src',
+ u'utf-8',
+ u'<img src="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'img-data-src',
+ u'utf-8',
+ u'<img data-src="{}">',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ # <image> is turned into <img> in the tree builder
+ u'image-src',
+ u'utf-8',
+ u'<image src="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'img-srcset',
+ u'utf-8',
+ u'<img srcset="{}">',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'img-src-crossorigin',
+ u'utf-8',
+ u'<img src="{}" crossorigin>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'img-src-referrerpolicy-no-referrer',
+ u'utf-8',
+ u'<img src="{}" referrerpolicy=no-referrer>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'img-src-loading-lazy',
+ u'utf-8',
+ u'<img src="{}" loading=lazy>',
+ None,
+ u'false',
+ u'false'
+ ),
+ (
+ u'picture-source-unsupported-type',
+ u'utf-8',
+ u'<picture><source srcset="{}" type=text/plain><img></picture>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'picture-source-nomatch-media',
+ u'utf-8',
+ u'<picture><source srcset="{}" media="not all"><img></picture>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'picture-source-no-img',
+ u'utf-8',
+ u'<picture><source srcset="{}"></picture>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'picture-source-br-img',
+ u'utf-8',
+ u'<picture><source srcset="{}"><br><img></picture>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'video-poster',
+ u'utf-8',
+ u'<video poster="{}"></video>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'xmp-script-src',
+ u'utf-8',
+ u'<xmp><script src="{}"></script></xmp>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ # MathML doesn't have script
+ u'math-script-src',
+ u'utf-8',
+ u'<math><script src="{}"></script></math>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'math-font-script-src',
+ u'utf-8',
+ u'<math><font><script src="{}"></script></font></math>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ # This breaks out of foreign content, so the script is an HTML script
+ # https://html.spec.whatwg.org/multipage/#parsing-main-inforeign
+ u'math-font-face-script-src',
+ u'utf-8',
+ u'<math><font face><script src="{}"></script></font></math>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'svg-script-href',
+ u'utf-8',
+ u'<svg><script href="{}"></script></svg>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'svg-script-xlinkhref',
+ u'utf-8',
+ u'<svg><script xlink:href="{}"></script></svg>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ # SVG script element doesn't have a src attribute
+ u'svg-script-src',
+ u'utf-8',
+ u'<svg><script src="{}"></script></svg>',
+ None,
+ u'false',
+ u'true'
+ ),
+ (
+ u'svg-image-href',
+ u'utf-8',
+ u'<svg><image href="{}"></image></svg>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ u'svg-image-xlinkhref',
+ u'utf-8',
+ u'<svg><image xlink:href="{}"></image></svg>',
+ None,
+ u'true',
+ u'true'
+ ),
+ (
+ # SVG image element doesn't have a src attribute
+ u'svg-image-src',
+ u'utf-8',
+ u'<svg><image src="{}"></image></svg>',
+ None,
+ u'false',
+ u'true'
+ ),
+]
+
+tests = [
+ # title,
+ # encoding,
+ # template_testcase_markup,
+ # expect_load,
+ # test_nonspeculative
+]
+
+# Templates
+
+preamble = u"""<!DOCTYPE html>
+<!-- DO NOT EDIT. This file has been generated. Source:
+ /html/syntax/speculative-parsing/tools/generate.py
+-->"""
+
+no_meta_charset = u"""<!-- no meta charset -->
+<!-- (padding to exceed 1024 bytes processed by the character encoding scanner) -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->
+<!-- -->"""
+
+# Notes on `encodingcheck` in the URL below
+#
+# - &Gbreve; is the HTML character reference for U+011E LATIN CAPITAL LETTER G WITH BREVE
+# - In windows-1254, this character is encoded as 0xD0.
+# When used in the query part of a URL, it gets percent-encoded as %D0.
+# - In windows-1252 (usually the fallback encoding), that character can't be encoded, so is instead
+# represented as &#286; percent-encoded, so %26%23286%3B.
+# https://url.spec.whatwg.org/#query-state
+# https://url.spec.whatwg.org/#code-point-percent-encode-after-encoding
+# - In utf-8, it's percent-encoded as utf-8: %C4%9E
+# - stash.py will store this value as "param-encodingcheck"
+
+url_wptserve_sub = u"/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid={{GET[uuid]}}&amp;encodingcheck=&Gbreve;"
+url_js_sub = u"/html/syntax/speculative-parsing/resources/stash.py?action=put&amp;uuid=${uuid}&amp;encodingcheck=&Gbreve;"
+
+
+# Non-speculative (normal) case to compare results with
+
+template_nonspeculative = u"""{preamble}
+{encoding_decl}
+<title>Speculative parsing, non-speculative (helper file): {title}</title>
+<!-- non-speculative case -->
+{nonspeculative_testcase_markup}
+<!-- block the load event for a bit: -->
+<script src="/common/slow.py?delay={delay}"></script>
+"""
+
+# Scenario: page load
+
+template_pageload_toplevel = u"""{preamble}
+{encoding_decl}
+<title>Speculative parsing, page load: {title}</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<body>
+<script>
+ setup({{single_test: true}});
+ const uuid = token();
+ const iframe = document.createElement('iframe');
+ iframe.src = `resources/{title}-framed.sub.html?uuid=${{uuid}}`;
+ document.body.appendChild(iframe);
+ expect_fetched_onload(uuid, {expect_load})
+ .then(compare_with_nonspeculative(uuid, '{title}', {test_nonspeculative}))
+ .then(done);
+</script>
+"""
+
+template_pageload_framed = u"""{preamble}
+{encoding_decl}
+<title>Speculative parsing, page load (helper file): {title}</title>
+<script src="/common/slow.py?delay={delay}"></script>
+<script>
+ document.write('<plaintext>');
+</script>
+<!-- speculative case -->
+{testcase_markup}
+"""
+
+# Scenario: document.write()
+
+template_docwrite = u"""{preamble}
+{encoding_decl}
+<title>Speculative parsing, document.write(): {title}</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=/html/syntax/speculative-parsing/resources/speculative-parsing-util.js></script>
+<script>
+ setup({{single_test: true}});
+ const uuid = token();
+ expect_fetched_onload(uuid, {expect_load})
+ .then(compare_with_nonspeculative(uuid, '{title}', {test_nonspeculative}))
+ .then(done);
+ document.write(`
+ <script src="/common/slow.py?delay={delay}"><\\/script>
+ <script>
+ document.write('<plaintext>');
+ <\\/script>
+ <\\!-- speculative case in document.write -->
+ {testcase_markup}
+ `);
+</script>
+"""
+
+# Scenario: <link rel=prerender> - TODO(zcorpan)
+
+template_prerender_toplevel = u"""{preamble}
+{encoding_decl}
+<title>Speculative parsing, prerender: {title}</title>
+...
+"""
+
+template_prerender_linked = u"""{preamble}
+{encoding_decl}
+<title>Speculative parsing, prerender (helper file): {title}</title>
+...
+"""
+
+# Generate tests
+
+# wipe target_dir of HTML files
+if os.path.isdir(target_dir):
+ for root, dirs, files in os.walk(target_dir):
+ for name in files:
+ if name.endswith('.html'):
+ path = os.path.join(root, name)
+ if os.path.isfile(path):
+ os.remove(path)
+
+def write_file(path, content):
+ path = os.path.join(target_dir, path)
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ file = open(os.path.join(target_dir, path), 'w')
+ file.write(content)
+ file.close()
+
+def generate_tests(testcase, tentative):
+ title, encoding, template_testcase_markup, template_nonspeculative_testcase_markup, expect_load, test_nonspeculative = testcase
+ if template_nonspeculative_testcase_markup == None:
+ template_nonspeculative_testcase_markup = template_testcase_markup
+ ext = u""
+ if tentative:
+ ext = u".tentative"
+
+ if encoding == None:
+ encoding_decl = no_meta_charset
+ else:
+ encoding_decl = f"<meta charset={encoding}>"
+
+ html_testcase_markup = template_testcase_markup.format(url_wptserve_sub)
+ html_nonspeculative_testcase_markup = template_nonspeculative_testcase_markup.format(url_wptserve_sub)
+ js_testcase_markup = template_testcase_markup.format(url_js_sub).replace(u"</script>", u"<\/script>").replace(u"<meta charset", u"<meta\ charset")
+
+ if test_nonspeculative == u'true':
+ nonspeculative = template_nonspeculative.format(preamble=preamble, encoding_decl=encoding_decl, title=title, nonspeculative_testcase_markup=html_nonspeculative_testcase_markup, delay=delay)
+ write_file(f"resources/{title}-nonspeculative.sub.html", nonspeculative)
+
+ pageload_toplevel = template_pageload_toplevel.format(preamble=preamble, encoding_decl=encoding_decl, title=title, expect_load=expect_load, test_nonspeculative=test_nonspeculative)
+ write_file(f"page-load/{title}{ext}.html", pageload_toplevel)
+ pageload_framed = template_pageload_framed.format(preamble=preamble, encoding_decl=encoding_decl, title=title, testcase_markup=html_testcase_markup, delay=delay)
+ write_file(f"page-load/resources/{title}-framed.sub.html", pageload_framed)
+
+ docwrite = template_docwrite.format(preamble=preamble, encoding_decl=encoding_decl, title=title, expect_load=expect_load, testcase_markup=js_testcase_markup, test_nonspeculative=test_nonspeculative, delay=delay)
+ write_file(f"document-write/{title}{ext}.sub.html", docwrite)
+
+for testcase in tests:
+ generate_tests(testcase, False)
+
+for testcase in tentative_tests:
+ generate_tests(testcase, True)
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING-trail.htm
new file mode 100644
index 0000000000..d01acf5c91
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING.htm
new file mode 100644
index 0000000000..8e9cf2c373
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/ENCODING.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" ENCODING="windows-1251"?>
+<p>XML decl with upper-case <code>ENCODING=</code></p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/VERSION-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/VERSION-trail.htm
new file mode 100644
index 0000000000..0859f3bd43
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/VERSION-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/VERSION.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/VERSION.htm
new file mode 100644
index 0000000000..a98f7b9533
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/VERSION.htm
@@ -0,0 +1,4 @@
+<?xml VERSION="1.0" encoding="windows-1251"?>
+<p>XML decl with upper-case <code>VERSION</code></p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS-trail.htm
new file mode 100644
index 0000000000..909b5ce368
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS.htm
new file mode 100644
index 0000000000..25f068fbb4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/WINDOWS.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="WINDOWS-1251"?>
+<p>Normal XML decl with upper case encoding label</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/XML-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/XML-trail.htm
new file mode 100644
index 0000000000..f9bb3d5d9b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/XML-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/XML.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/XML.htm
new file mode 100644
index 0000000000..c77b422590
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/XML.htm
@@ -0,0 +1,4 @@
+<?XML version="1.0" encoding="windows-1251"?>
+<p>XML decl with upper case XML</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/ascii-decl-for-utf-16.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/ascii-decl-for-utf-16.htm
new file mode 100644
index 0000000000..0c5e20b33e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/ascii-decl-for-utf-16.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-16"?>
+<p>Normal ASCII-based XML decl declares UTF-16, file is not UTF-16</p>
+<p>Test: æ</p>
+<p>If &#xFFFD;, XML decl takes effect as UTF-8</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/baseline-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/baseline-trail.htm
new file mode 100644
index 0000000000..e9c0352bf7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/baseline-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/baseline.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/baseline.htm
new file mode 100644
index 0000000000..ff720a2da2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/baseline.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<p>Normal XML decl</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/cp1251-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/cp1251-trail.htm
new file mode 100644
index 0000000000..16687e7507
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/cp1251-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/cp1251.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/cp1251.htm
new file mode 100644
index 0000000000..d928084097
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/cp1251.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="cp1251"?>
+<p>Normal XML decl with non-primary label</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding-trail.htm
new file mode 100644
index 0000000000..5d08852601
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding.htm
new file mode 100644
index 0000000000..fd12c188dc
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/encoding-equals-encoding.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding=encoding="windows-1251"?>
+<p><code>encoding=encoding=</code></p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding-trail.htm
new file mode 100644
index 0000000000..fee1949a10
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding.htm
new file mode 100644
index 0000000000..67a3900d68
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/encodingencoding.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encodingencoding="windows-1251"?>
+<p><code>encodingencoding=</code></p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding-trail.htm
new file mode 100644
index 0000000000..f5078bf6be
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding.htm
new file mode 100644
index 0000000000..6427b6afd8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/gt-between-xml-and-encoding.htm
@@ -0,0 +1,4 @@
+<?xml>encoding="windows-1251"?>
+<p>XML decl without version with &gt; between <code>xml</code> and <code>encoding</code></p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta-trail.htm
new file mode 100644
index 0000000000..afee7ba0eb
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta.htm
new file mode 100644
index 0000000000..231c6551ab
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16be-and-meta.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta-trail.htm
new file mode 100644
index 0000000000..a81fcb8f15
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta.htm
new file mode 100644
index 0000000000..77f511f48d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/incomplete-utf-16le-and-meta.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after-trail.htm
new file mode 100644
index 0000000000..71c61d11f7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after.htm
new file mode 100644
index 0000000000..76119c69a3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-after.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251" ?>
+<p>Normal XML decl with whitespace after encoding such that &gt; is the 1024th byte</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before-trail.htm
new file mode 100644
index 0000000000..f9ecc9f081
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before.htm
new file mode 100644
index 0000000000..8e209886a1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-before.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<p>Normal XML decl with whitespace before encoding such that &gt; is the 1024th byte</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after-trail.htm
new file mode 100644
index 0000000000..2f9513bc18
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after.htm
new file mode 100644
index 0000000000..8a837b5e97
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-after.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251" ?>
+<p>Normal XML decl with whitespace after encoding such that &gt; is the 1025th byte</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before-trail.htm
new file mode 100644
index 0000000000..1f3fa5ad9e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before.htm
new file mode 100644
index 0000000000..f0ddd21aa2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/kilobyte-plus-one-before.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<p>Normal XML decl with whitespace before encoding such that &gt; is the 1025th byte</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding-trail.htm
new file mode 100644
index 0000000000..bafb01454d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding.htm
new file mode 100644
index 0000000000..93d012c85e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/letter-between-xml-and-encoding.htm
@@ -0,0 +1,4 @@
+<?xmlaencoding="windows-1251"?>
+<p>XML decl without version with letter a between <code>xml</code> and <code>encoding</code></p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding-trail.htm
new file mode 100644
index 0000000000..65b6900357
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding.htm
new file mode 100644
index 0000000000..240b44820f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/lt-between-xml-and-encoding.htm
@@ -0,0 +1,4 @@
+<?xml<encoding="windows-1251"?>
+<p>XML decl without version with &lt; between <code>xml</code> and <code>encoding</code></p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding-trail.htm
new file mode 100644
index 0000000000..39b048c238
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding.htm
new file mode 100644
index 0000000000..4e548ad83c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-charset-before-encoding.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" <meta charset="windows-1253" encoding="windows-1251"?>
+<p>Meta inside XML declaration with shared greater-than; charset before encoding</p>
+<p>Test: æ</p>
+<p>If &#x03B6;, meta takes precedence. If &#x0436;, XML decl takes precedence</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset-trail.htm
new file mode 100644
index 0000000000..e594801c09
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset.htm
new file mode 100644
index 0000000000..77725ed963
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/meta-inside-xml-encoding-before-charset.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" <meta encoding="windows-1251" charset="windows-1253"?>
+<p>Meta inside XML declaration with shared greater-than; encoding before charse</p>
+<p>Test: æ</p>
+<p>If &#x03B6;, meta takes precedence. If &#x0436;, XML decl takes precedence</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space-trail.htm
new file mode 100644
index 0000000000..79a3edf492
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space.htm
new file mode 100644
index 0000000000..354bbdcdd2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-space.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding=windows-1251 ?>
+<p>XML decl without quotes around encoding label and with space before question mark</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-trail.htm
new file mode 100644
index 0000000000..e0f5346728
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes.htm
new file mode 100644
index 0000000000..813ba1cdfe
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-quotes.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding=windows-1251?>
+<p>XML decl without quotes around encoding label and with question mark immediately after</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trail.htm
new file mode 100644
index 0000000000..d7db8d1a7f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals-trail.htm
new file mode 100644
index 0000000000..861d873572
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals.htm
new file mode 100644
index 0000000000..677b440e04
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals.htm
@@ -0,0 +1,6 @@
+<?xmlencoding
+ =
+ 'windows-1251'<body>
+<p>XML decl using single quotes with spaces around equals without version or space or trailing question mark with trailing less-than before greater-than</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals-trail.htm
new file mode 100644
index 0000000000..c7d2e31371
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals.htm
new file mode 100644
index 0000000000..ae3b580bca
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals.htm
@@ -0,0 +1,4 @@
+<?xmlencoding = 'windows-1251'<body>
+<p>XML decl using single quotes with spaces around equals without version or space or trailing question mark with trailing less-than before greater-than</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-trail.htm
new file mode 100644
index 0000000000..75963c8648
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes.htm
new file mode 100644
index 0000000000..ba56539d6b
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-single-quotes.htm
@@ -0,0 +1,4 @@
+<?xmlencoding='windows-1251'<body>
+<p>XML decl using single quotes without version or space or trailing question mark with trailing less-than before greater-than</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-trail.htm
new file mode 100644
index 0000000000..c873c4cd45
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body.htm
new file mode 100644
index 0000000000..199471e8ab
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-body.htm
@@ -0,0 +1,4 @@
+<?xmlencoding="windows-1251"<body>
+<p>XML decl without version or space or trailing question mark with trailing less-than before greater-than</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt-trail.htm
new file mode 100644
index 0000000000..d5a51adca9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt.htm
new file mode 100644
index 0000000000..a6c04781f7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question-trailing-lt.htm
@@ -0,0 +1,4 @@
+<?xmlencoding="windows-1251"<>
+<p>XML decl without version or space or trailing question mark with trailing less-than before greater-than</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question.htm
new file mode 100644
index 0000000000..282f6842e8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-or-trailing-question.htm
@@ -0,0 +1,4 @@
+<?xmlencoding="windows-1251">
+<p>XML decl without version or space or trailing question mark</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-trail.htm
new file mode 100644
index 0000000000..1d5ed24999
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space.htm
new file mode 100644
index 0000000000..bcf5cdcf19
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-or-space.htm
@@ -0,0 +1,4 @@
+<?xmlencoding="windows-1251"?>
+<p>XML decl without version or space</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-trail.htm
new file mode 100644
index 0000000000..e25ca2e12f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/no-version.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version.htm
new file mode 100644
index 0000000000..a04995501d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/no-version.htm
@@ -0,0 +1,4 @@
+<?xml encoding="windows-1251"?>
+<p>XML decl without version</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals-trail.htm
new file mode 100644
index 0000000000..32c67a0311
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals.htm
new file mode 100644
index 0000000000..fb5413dad8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-equals.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<p>\x01 around the equals sign for encoding</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label-trail.htm
new file mode 100644
index 0000000000..372c8993b9
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label.htm
new file mode 100644
index 0000000000..0a867feb83
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/one-around-label.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<p>\x01 around the label inside the quotes</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/replacement-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/replacement-trail.htm
new file mode 100644
index 0000000000..2179613d83
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/replacement-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/replacement.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/replacement.htm
new file mode 100644
index 0000000000..dc7d2d4ef5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/replacement.htm
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="ISO-2022-KR"?>
+<p>Normal XML decl with a label that resolves to replacement</p>
+<p>If this text is visible, the label did not resolve to replacement</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label-trail.htm
new file mode 100644
index 0000000000..4795ddf7d1
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label.htm
new file mode 100644
index 0000000000..9e5a2eb817
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/space-around-label.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding=" windows-1251 "?>
+<p>Spaces around the label inside the quotes</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/space-before-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/space-before-trail.htm
new file mode 100644
index 0000000000..998efe5438
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/space-before-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/space-before.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/space-before.htm
new file mode 100644
index 0000000000..e5c303abd8
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/space-before.htm
@@ -0,0 +1,4 @@
+ <?xml version="1.0" encoding="windows-1251"?>
+<p>Normal XML decl with space before</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/test_support.js b/testing/web-platform/tests/html/syntax/xmldecl/support/test_support.js
new file mode 100644
index 0000000000..ec3668d1bf
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/test_support.js
@@ -0,0 +1,26 @@
+function runAllTests() {
+ let divs = document.getElementsByTagName("div");
+ for (let i = 0; i < divs.length; ++i) {
+ let div = divs[i];
+ let expectation = div.className;
+ let iframes = div.getElementsByTagName("iframe");
+ for (let j = 0; j < iframes.length; ++j) {
+ let iframe = iframes[j];
+ let src = iframe.src;
+ let doc = iframe.contentWindow.document;
+ test(function() {
+ assert_equals(doc.characterSet, expectation, 'Check');
+ if (expectation == "windows-1251" || expectation == "windows-1252" && !(src.endsWith("/XML.htm") || src.endsWith("/XML-trail.htm"))) {
+ let fc = doc.firstChild;
+ assert_equals(fc.nodeType, Node.COMMENT_NODE, 'Should have comment node');
+ assert_true(fc.nodeValue.startsWith("?xml"), 'Should start with ?xml');
+ } else if (expectation == "UTF-16BE" || expectation == "UTF-16LE") {
+ let fc = doc.firstChild;
+ assert_equals(fc.nodeType, Node.COMMENT_NODE, 'Should have comment node');
+ assert_true(fc.nodeValue.startsWith("?x"), 'Should start with ?x');
+ }
+ }, "Check encoding " + expectation + ", " + src.substring(src.lastIndexOf("/") + 1));
+ }
+ }
+}
+
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/trickle.py b/testing/web-platform/tests/html/syntax/xmldecl/support/trickle.py
new file mode 100644
index 0000000000..f1ef785c94
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/trickle.py
@@ -0,0 +1,12 @@
+import time
+
+def main(request, response):
+ response.add_required_headers = False # Don't implicitly add HTTP headers
+ response.writer.write_status(200)
+ response.writer.write_header("Content-Type", "text/html")
+ response.writer.end_headers()
+
+ for b in b'<?xml version="1.0" encoding="windows-1251"?':
+ response.writer.write(bytes([b]))
+ time.sleep(0.05)
+ response.writer.write(b'>\n<p>Normal XML declation as slow byte-by-byte trickle</p>\n<p>Test: \xE6</p>\n<p>If &#x0436;, XML decl takes effect</p>')
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes-trail.htm
new file mode 100644
index 0000000000..afc015cab2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes.htm
new file mode 100644
index 0000000000..f4d05b390c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/unmatched-quotes.htm
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251'?>
+<p>XML decl without double quote before and single quote after encoding label</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta-trail.htm
new file mode 100644
index 0000000000..5117f6002c
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta.htm
new file mode 100644
index 0000000000..4b90b91c67
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-and-meta.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.html b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.html
new file mode 100644
index 0000000000..212095d0ee
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.html.headers b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.html.headers
new file mode 100644
index 0000000000..e853d6cee5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http-trail.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=UTF-8
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.html b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.html
new file mode 100644
index 0000000000..18f24fbe50
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.html.headers b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.html.headers
new file mode 100644
index 0000000000..e853d6cee5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16be-vs-http.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=UTF-8
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta-trail.htm
new file mode 100644
index 0000000000..8f7adac8de
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta.htm
new file mode 100644
index 0000000000..e530d1e49d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-and-meta.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.html b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.html
new file mode 100644
index 0000000000..f9d0655386
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.html.headers b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.html.headers
new file mode 100644
index 0000000000..e853d6cee5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http-trail.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=UTF-8
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.html b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.html
new file mode 100644
index 0000000000..466ac26e29
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.html.headers b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.html.headers
new file mode 100644
index 0000000000..e853d6cee5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/utf-16le-vs-http.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=UTF-8
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta-trail.htm
new file mode 100644
index 0000000000..ddee02383d
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta.htm
new file mode 100644
index 0000000000..3e8f64be0f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-and-meta.htm
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<meta charset="windows-1253">
+<p>Normal XML decl and meta</p>
+<p>Test: æ</p>
+<p>If &#x03B6;, meta takes precedence. If &#x0436;, XML decl takes precedence</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.html b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.html
new file mode 100644
index 0000000000..e9c0352bf7
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.html
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.html.headers b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.html.headers
new file mode 100644
index 0000000000..e853d6cee5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http-trail.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=UTF-8
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html
new file mode 100644
index 0000000000..ff720a2da2
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<p>Normal XML decl</p>
+<p>Test: æ</p>
+<p>If &#x0436;, XML decl takes effect</p>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html.headers b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html.headers
new file mode 100644
index 0000000000..e853d6cee5
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/xml-vs-http.html.headers
@@ -0,0 +1 @@
+Content-Type: text/html; charset=UTF-8
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals-trail.htm
new file mode 100644
index 0000000000..60179cbbf3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals.htm
new file mode 100644
index 0000000000..7fecbca830
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-equals.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label-trail.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label-trail.htm
new file mode 100644
index 0000000000..761c76cfb3
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label-trail.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label.htm b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label.htm
new file mode 100644
index 0000000000..dfc05b0b3f
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/support/zero-around-label.htm
Binary files differ
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-1.html b/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-1.html
new file mode 100644
index 0000000000..40ebb932e4
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-1.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<meta charset="windows-1252">
+<title>Bogo-XML declaration</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support/test_support.js></script>
+
+<script>
+setup({explicit_done:true});
+window.onload = function() {
+ runAllTests();
+ done();
+};
+</script>
+
+<section style="display: none;">
+<div class="windows-1251">
+<iframe src="support/baseline.htm"></iframe>
+<iframe src="support/cp1251.htm"></iframe>
+<iframe src="support/kilobyte-after.htm"></iframe>
+<iframe src="support/kilobyte-before.htm"></iframe>
+<iframe src="support/letter-between-xml-and-encoding.htm"></iframe>
+<iframe src="support/lt-between-xml-and-encoding.htm"></iframe>
+<iframe src="support/meta-inside-xml-charset-before-encoding.htm"></iframe>
+<iframe src="support/meta-inside-xml-encoding-before-charset.htm"></iframe>
+<iframe src="support/no-version.htm"></iframe>
+<iframe src="support/no-version-or-space.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body-single-quotes.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-lt.htm"></iframe>
+<iframe src="support/one-around-equals.htm"></iframe>
+<iframe src="support/VERSION.htm"></iframe>
+<iframe src="support/WINDOWS.htm"></iframe>
+<iframe src="support/zero-around-equals.htm"></iframe>
+<iframe src="support/baseline-trail.htm"></iframe>
+<iframe src="support/cp1251-trail.htm"></iframe>
+<iframe src="support/kilobyte-after-trail.htm"></iframe>
+<iframe src="support/kilobyte-before-trail.htm"></iframe>
+<iframe src="support/letter-between-xml-and-encoding-trail.htm"></iframe>
+<iframe src="support/lt-between-xml-and-encoding-trail.htm"></iframe>
+<iframe src="support/meta-inside-xml-charset-before-encoding-trail.htm"></iframe>
+<iframe src="support/meta-inside-xml-encoding-before-charset-trail.htm"></iframe>
+<iframe src="support/no-version-trail.htm"></iframe>
+<iframe src="support/no-version-or-space-trail.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trail.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body-trail.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-trail.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-and-line-breaks-around-equals-trail.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-body-single-quotes-spaces-around-equals-trail.htm"></iframe>
+<iframe src="support/no-version-or-space-or-trailing-question-trailing-lt-trail.htm"></iframe>
+<iframe src="support/one-around-equals-trail.htm"></iframe>
+<iframe src="support/VERSION-trail.htm"></iframe>
+<iframe src="support/WINDOWS-trail.htm"></iframe>
+<iframe src="support/zero-around-equals-trail.htm"></iframe>
+</div>
+</section>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-2.html b/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-2.html
new file mode 100644
index 0000000000..6cb07afbb0
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-2.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<meta charset="windows-1252">
+<title>Bogo-XML declaration</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support/test_support.js></script>
+
+<script>
+setup({explicit_done:true});
+window.onload = function() {
+ runAllTests();
+ done();
+};
+</script>
+
+<section style="display: none;">
+<div class="windows-1251">
+<iframe src="support/kilobyte-plus-one-after.htm"></iframe>
+<iframe src="support/kilobyte-plus-one-before.htm"></iframe>
+<iframe src="support/kilobyte-plus-one-after-trail.htm"></iframe>
+<iframe src="support/kilobyte-plus-one-before-trail.htm"></iframe>
+</div>
+<div class="windows-1252">
+<iframe src="support/encodingencoding.htm"></iframe>
+<iframe src="support/encoding-equals-encoding.htm"></iframe>
+<iframe src="support/ENCODING.htm"></iframe>
+<iframe src="support/gt-between-xml-and-encoding.htm"></iframe>
+<iframe src="support/no-quotes.htm"></iframe>
+<iframe src="support/no-quotes-space.htm"></iframe>
+<iframe src="support/one-around-label.htm"></iframe>
+<iframe src="support/space-around-label.htm"></iframe>
+<iframe src="support/space-before.htm"></iframe>
+<iframe src="support/unmatched-quotes.htm"></iframe>
+<iframe src="support/XML.htm"></iframe>
+<iframe src="support/zero-around-label.htm"></iframe>
+<iframe src="support/encodingencoding-trail.htm"></iframe>
+<iframe src="support/encoding-equals-encoding-trail.htm"></iframe>
+<iframe src="support/ENCODING-trail.htm"></iframe>
+<iframe src="support/gt-between-xml-and-encoding-trail.htm"></iframe>
+<iframe src="support/no-quotes-trail.htm"></iframe>
+<iframe src="support/no-quotes-space-trail.htm"></iframe>
+<iframe src="support/one-around-label-trail.htm"></iframe>
+<iframe src="support/space-around-label-trail.htm"></iframe>
+<iframe src="support/space-before-trail.htm"></iframe>
+<iframe src="support/unmatched-quotes-trail.htm"></iframe>
+<iframe src="support/XML-trail.htm"></iframe>
+<iframe src="support/zero-around-label-trail.htm"></iframe>
+</div>
+<div class="windows-1253">
+<iframe src="support/xml-and-meta.htm"></iframe>
+<iframe src="support/incomplete-utf-16le-and-meta.htm"></iframe>
+<iframe src="support/incomplete-utf-16be-and-meta.htm"></iframe>
+<iframe src="support/xml-and-meta-trail.htm"></iframe>
+<iframe src="support/incomplete-utf-16le-and-meta-trail.htm"></iframe>
+<iframe src="support/incomplete-utf-16be-and-meta-trail.htm"></iframe>
+</div>
+<div class="UTF-16LE">
+<iframe src="support/utf-16le-and-meta.htm"></iframe>
+<iframe src="support/utf-16le-and-meta-trail.htm"></iframe>
+</div>
+<div class="UTF-16BE">
+<iframe src="support/utf-16be-and-meta.htm"></iframe>
+<iframe src="support/utf-16be-and-meta-trail.htm"></iframe>
+</div>
+<div class="UTF-8">
+<iframe src="support/utf-16le-vs-http.html"></iframe>
+<iframe src="support/utf-16be-vs-http.html"></iframe>
+<iframe src="support/xml-vs-http.html"></iframe>
+<iframe src="support/utf-16le-vs-http-trail.html"></iframe>
+<iframe src="support/utf-16be-vs-http-trail.html"></iframe>
+<iframe src="support/xml-vs-http-trail.html"></iframe>
+<iframe src="support/ascii-decl-for-utf-16.htm"></iframe>
+</div>
+<div class="replacement">
+<iframe src="support/replacement.htm"></iframe>
+<iframe src="support/replacement-trail.htm"></iframe>
+</div>
+</section>
diff --git a/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-3.html b/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-3.html
new file mode 100644
index 0000000000..a9f179de4e
--- /dev/null
+++ b/testing/web-platform/tests/html/syntax/xmldecl/xmldecl-3.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="windows-1252">
+<meta name="timeout" content="long">
+<title>Bogo-XML declaration</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support/test_support.js></script>
+
+<script>
+setup({explicit_done:true});
+window.onload = function() {
+ runAllTests();
+ done();
+};
+</script>
+
+<section style="display: none;">
+<div class="windows-1251">
+<iframe src="support/trickle.py"></iframe>
+</div>
+</section>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001-ref.html b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001-ref.html
new file mode 100644
index 0000000000..5b512e72f5
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Test reference</title>
+<style>
+ html, body { margin: 0 }
+</style>
+<iframe src="about:blank"></iframe>
+<div>
+ PASS
+</div>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html
new file mode 100644
index 0000000000..74018b4ad0
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Appending from the parser after adopting in an XML document doesn't miss notifications</title>
+<link rel="match" href="adopt-while-parsing-001-ref.html">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1511329">
+<link rel="author" title="Emilio Cobos Ãlvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<style>
+ html, body { margin: 0 }
+</style>
+<script>
+ // If we don't get notified of the <div> insertion, the PASS text will never appear.
+ function parsingInterrupted() {
+ let frameDoc = document.querySelector("iframe").contentDocument;
+ let root = frameDoc.documentElement;
+ document.documentElement.appendChild(root);
+ root.offsetTop;
+ }
+</script>
+<iframe src="adopt-while-parsing.xhtml"></iframe>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing.xhtml b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing.xhtml
new file mode 100644
index 0000000000..2d85d21558
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing.xhtml
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+ <script>
+ window.parent.parsingInterrupted();
+ </script>
+ <div>
+ PASS
+ </div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd-ref.html b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd-ref.html
new file mode 100644
index 0000000000..7e081a1805
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+Test passes if it correctly shows &Aacute; in the subframe.
+<hr>
+<iframe srcdoc="&amp;Aacute"></iframe>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd.html b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd.html
new file mode 100644
index 0000000000..3672ef6184
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+<html>
+ <head>
+ <title>
+ Test that an XHTML document with a data: URL still handles the XHTML DTD
+ properly even if the DTD URL is given as a relative URL.
+ </title>
+ <link rel="author" title="Boris Zbarsky" href="bzbarsky@mit.edu">
+ <link rel="match" href="data-xhtml-with-dtd-ref.html">
+ </head>
+ <body>
+ Test passes if it correctly shows &Aacute; in the subframe.
+ <hr>
+ <!-- Document in the subframe is:
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <body>
+ &Aacute;
+ </body>
+</html>
+ -->
+ <iframe src='data:application/xml,%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3C!DOCTYPE%20html%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20XHTML%201.0%20Strict%2F%2FEN%22%20%22DTD%2Fxhtml1-strict.dtd%22%3E%0A%3Chtml%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml%22%3E%0A%20%20%3Cbody%3E%0A%20%20%20%20%26Aacute%3B%0A%20%20%3C%2Fbody%3E%0A%3C%2Fhtml%3E%0A'></iframe>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/entities.json b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/entities.json
new file mode 100644
index 0000000000..8a1f590a6a
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/entities.json
@@ -0,0 +1,2233 @@
+{
+ "&Aacute;": { "codepoints": [193], "characters": "\u00C1" },
+ "&Aacute": { "codepoints": [193], "characters": "\u00C1" },
+ "&aacute;": { "codepoints": [225], "characters": "\u00E1" },
+ "&aacute": { "codepoints": [225], "characters": "\u00E1" },
+ "&Abreve;": { "codepoints": [258], "characters": "\u0102" },
+ "&abreve;": { "codepoints": [259], "characters": "\u0103" },
+ "&ac;": { "codepoints": [8766], "characters": "\u223E" },
+ "&acd;": { "codepoints": [8767], "characters": "\u223F" },
+ "&acE;": { "codepoints": [8766, 819], "characters": "\u223E\u0333" },
+ "&Acirc;": { "codepoints": [194], "characters": "\u00C2" },
+ "&Acirc": { "codepoints": [194], "characters": "\u00C2" },
+ "&acirc;": { "codepoints": [226], "characters": "\u00E2" },
+ "&acirc": { "codepoints": [226], "characters": "\u00E2" },
+ "&acute;": { "codepoints": [180], "characters": "\u00B4" },
+ "&acute": { "codepoints": [180], "characters": "\u00B4" },
+ "&Acy;": { "codepoints": [1040], "characters": "\u0410" },
+ "&acy;": { "codepoints": [1072], "characters": "\u0430" },
+ "&AElig;": { "codepoints": [198], "characters": "\u00C6" },
+ "&AElig": { "codepoints": [198], "characters": "\u00C6" },
+ "&aelig;": { "codepoints": [230], "characters": "\u00E6" },
+ "&aelig": { "codepoints": [230], "characters": "\u00E6" },
+ "&af;": { "codepoints": [8289], "characters": "\u2061" },
+ "&Afr;": { "codepoints": [120068], "characters": "\uD835\uDD04" },
+ "&afr;": { "codepoints": [120094], "characters": "\uD835\uDD1E" },
+ "&Agrave;": { "codepoints": [192], "characters": "\u00C0" },
+ "&Agrave": { "codepoints": [192], "characters": "\u00C0" },
+ "&agrave;": { "codepoints": [224], "characters": "\u00E0" },
+ "&agrave": { "codepoints": [224], "characters": "\u00E0" },
+ "&alefsym;": { "codepoints": [8501], "characters": "\u2135" },
+ "&aleph;": { "codepoints": [8501], "characters": "\u2135" },
+ "&Alpha;": { "codepoints": [913], "characters": "\u0391" },
+ "&alpha;": { "codepoints": [945], "characters": "\u03B1" },
+ "&Amacr;": { "codepoints": [256], "characters": "\u0100" },
+ "&amacr;": { "codepoints": [257], "characters": "\u0101" },
+ "&amalg;": { "codepoints": [10815], "characters": "\u2A3F" },
+ "&amp;": { "codepoints": [38], "characters": "\u0026" },
+ "&amp": { "codepoints": [38], "characters": "\u0026" },
+ "&AMP;": { "codepoints": [38], "characters": "\u0026" },
+ "&AMP": { "codepoints": [38], "characters": "\u0026" },
+ "&andand;": { "codepoints": [10837], "characters": "\u2A55" },
+ "&And;": { "codepoints": [10835], "characters": "\u2A53" },
+ "&and;": { "codepoints": [8743], "characters": "\u2227" },
+ "&andd;": { "codepoints": [10844], "characters": "\u2A5C" },
+ "&andslope;": { "codepoints": [10840], "characters": "\u2A58" },
+ "&andv;": { "codepoints": [10842], "characters": "\u2A5A" },
+ "&ang;": { "codepoints": [8736], "characters": "\u2220" },
+ "&ange;": { "codepoints": [10660], "characters": "\u29A4" },
+ "&angle;": { "codepoints": [8736], "characters": "\u2220" },
+ "&angmsdaa;": { "codepoints": [10664], "characters": "\u29A8" },
+ "&angmsdab;": { "codepoints": [10665], "characters": "\u29A9" },
+ "&angmsdac;": { "codepoints": [10666], "characters": "\u29AA" },
+ "&angmsdad;": { "codepoints": [10667], "characters": "\u29AB" },
+ "&angmsdae;": { "codepoints": [10668], "characters": "\u29AC" },
+ "&angmsdaf;": { "codepoints": [10669], "characters": "\u29AD" },
+ "&angmsdag;": { "codepoints": [10670], "characters": "\u29AE" },
+ "&angmsdah;": { "codepoints": [10671], "characters": "\u29AF" },
+ "&angmsd;": { "codepoints": [8737], "characters": "\u2221" },
+ "&angrt;": { "codepoints": [8735], "characters": "\u221F" },
+ "&angrtvb;": { "codepoints": [8894], "characters": "\u22BE" },
+ "&angrtvbd;": { "codepoints": [10653], "characters": "\u299D" },
+ "&angsph;": { "codepoints": [8738], "characters": "\u2222" },
+ "&angst;": { "codepoints": [197], "characters": "\u00C5" },
+ "&angzarr;": { "codepoints": [9084], "characters": "\u237C" },
+ "&Aogon;": { "codepoints": [260], "characters": "\u0104" },
+ "&aogon;": { "codepoints": [261], "characters": "\u0105" },
+ "&Aopf;": { "codepoints": [120120], "characters": "\uD835\uDD38" },
+ "&aopf;": { "codepoints": [120146], "characters": "\uD835\uDD52" },
+ "&apacir;": { "codepoints": [10863], "characters": "\u2A6F" },
+ "&ap;": { "codepoints": [8776], "characters": "\u2248" },
+ "&apE;": { "codepoints": [10864], "characters": "\u2A70" },
+ "&ape;": { "codepoints": [8778], "characters": "\u224A" },
+ "&apid;": { "codepoints": [8779], "characters": "\u224B" },
+ "&apos;": { "codepoints": [39], "characters": "\u0027" },
+ "&ApplyFunction;": { "codepoints": [8289], "characters": "\u2061" },
+ "&approx;": { "codepoints": [8776], "characters": "\u2248" },
+ "&approxeq;": { "codepoints": [8778], "characters": "\u224A" },
+ "&Aring;": { "codepoints": [197], "characters": "\u00C5" },
+ "&Aring": { "codepoints": [197], "characters": "\u00C5" },
+ "&aring;": { "codepoints": [229], "characters": "\u00E5" },
+ "&aring": { "codepoints": [229], "characters": "\u00E5" },
+ "&Ascr;": { "codepoints": [119964], "characters": "\uD835\uDC9C" },
+ "&ascr;": { "codepoints": [119990], "characters": "\uD835\uDCB6" },
+ "&Assign;": { "codepoints": [8788], "characters": "\u2254" },
+ "&ast;": { "codepoints": [42], "characters": "\u002A" },
+ "&asymp;": { "codepoints": [8776], "characters": "\u2248" },
+ "&asympeq;": { "codepoints": [8781], "characters": "\u224D" },
+ "&Atilde;": { "codepoints": [195], "characters": "\u00C3" },
+ "&Atilde": { "codepoints": [195], "characters": "\u00C3" },
+ "&atilde;": { "codepoints": [227], "characters": "\u00E3" },
+ "&atilde": { "codepoints": [227], "characters": "\u00E3" },
+ "&Auml;": { "codepoints": [196], "characters": "\u00C4" },
+ "&Auml": { "codepoints": [196], "characters": "\u00C4" },
+ "&auml;": { "codepoints": [228], "characters": "\u00E4" },
+ "&auml": { "codepoints": [228], "characters": "\u00E4" },
+ "&awconint;": { "codepoints": [8755], "characters": "\u2233" },
+ "&awint;": { "codepoints": [10769], "characters": "\u2A11" },
+ "&backcong;": { "codepoints": [8780], "characters": "\u224C" },
+ "&backepsilon;": { "codepoints": [1014], "characters": "\u03F6" },
+ "&backprime;": { "codepoints": [8245], "characters": "\u2035" },
+ "&backsim;": { "codepoints": [8765], "characters": "\u223D" },
+ "&backsimeq;": { "codepoints": [8909], "characters": "\u22CD" },
+ "&Backslash;": { "codepoints": [8726], "characters": "\u2216" },
+ "&Barv;": { "codepoints": [10983], "characters": "\u2AE7" },
+ "&barvee;": { "codepoints": [8893], "characters": "\u22BD" },
+ "&barwed;": { "codepoints": [8965], "characters": "\u2305" },
+ "&Barwed;": { "codepoints": [8966], "characters": "\u2306" },
+ "&barwedge;": { "codepoints": [8965], "characters": "\u2305" },
+ "&bbrk;": { "codepoints": [9141], "characters": "\u23B5" },
+ "&bbrktbrk;": { "codepoints": [9142], "characters": "\u23B6" },
+ "&bcong;": { "codepoints": [8780], "characters": "\u224C" },
+ "&Bcy;": { "codepoints": [1041], "characters": "\u0411" },
+ "&bcy;": { "codepoints": [1073], "characters": "\u0431" },
+ "&bdquo;": { "codepoints": [8222], "characters": "\u201E" },
+ "&becaus;": { "codepoints": [8757], "characters": "\u2235" },
+ "&because;": { "codepoints": [8757], "characters": "\u2235" },
+ "&Because;": { "codepoints": [8757], "characters": "\u2235" },
+ "&bemptyv;": { "codepoints": [10672], "characters": "\u29B0" },
+ "&bepsi;": { "codepoints": [1014], "characters": "\u03F6" },
+ "&bernou;": { "codepoints": [8492], "characters": "\u212C" },
+ "&Bernoullis;": { "codepoints": [8492], "characters": "\u212C" },
+ "&Beta;": { "codepoints": [914], "characters": "\u0392" },
+ "&beta;": { "codepoints": [946], "characters": "\u03B2" },
+ "&beth;": { "codepoints": [8502], "characters": "\u2136" },
+ "&between;": { "codepoints": [8812], "characters": "\u226C" },
+ "&Bfr;": { "codepoints": [120069], "characters": "\uD835\uDD05" },
+ "&bfr;": { "codepoints": [120095], "characters": "\uD835\uDD1F" },
+ "&bigcap;": { "codepoints": [8898], "characters": "\u22C2" },
+ "&bigcirc;": { "codepoints": [9711], "characters": "\u25EF" },
+ "&bigcup;": { "codepoints": [8899], "characters": "\u22C3" },
+ "&bigodot;": { "codepoints": [10752], "characters": "\u2A00" },
+ "&bigoplus;": { "codepoints": [10753], "characters": "\u2A01" },
+ "&bigotimes;": { "codepoints": [10754], "characters": "\u2A02" },
+ "&bigsqcup;": { "codepoints": [10758], "characters": "\u2A06" },
+ "&bigstar;": { "codepoints": [9733], "characters": "\u2605" },
+ "&bigtriangledown;": { "codepoints": [9661], "characters": "\u25BD" },
+ "&bigtriangleup;": { "codepoints": [9651], "characters": "\u25B3" },
+ "&biguplus;": { "codepoints": [10756], "characters": "\u2A04" },
+ "&bigvee;": { "codepoints": [8897], "characters": "\u22C1" },
+ "&bigwedge;": { "codepoints": [8896], "characters": "\u22C0" },
+ "&bkarow;": { "codepoints": [10509], "characters": "\u290D" },
+ "&blacklozenge;": { "codepoints": [10731], "characters": "\u29EB" },
+ "&blacksquare;": { "codepoints": [9642], "characters": "\u25AA" },
+ "&blacktriangle;": { "codepoints": [9652], "characters": "\u25B4" },
+ "&blacktriangledown;": { "codepoints": [9662], "characters": "\u25BE" },
+ "&blacktriangleleft;": { "codepoints": [9666], "characters": "\u25C2" },
+ "&blacktriangleright;": { "codepoints": [9656], "characters": "\u25B8" },
+ "&blank;": { "codepoints": [9251], "characters": "\u2423" },
+ "&blk12;": { "codepoints": [9618], "characters": "\u2592" },
+ "&blk14;": { "codepoints": [9617], "characters": "\u2591" },
+ "&blk34;": { "codepoints": [9619], "characters": "\u2593" },
+ "&block;": { "codepoints": [9608], "characters": "\u2588" },
+ "&bne;": { "codepoints": [61, 8421], "characters": "\u003D\u20E5" },
+ "&bnequiv;": { "codepoints": [8801, 8421], "characters": "\u2261\u20E5" },
+ "&bNot;": { "codepoints": [10989], "characters": "\u2AED" },
+ "&bnot;": { "codepoints": [8976], "characters": "\u2310" },
+ "&Bopf;": { "codepoints": [120121], "characters": "\uD835\uDD39" },
+ "&bopf;": { "codepoints": [120147], "characters": "\uD835\uDD53" },
+ "&bot;": { "codepoints": [8869], "characters": "\u22A5" },
+ "&bottom;": { "codepoints": [8869], "characters": "\u22A5" },
+ "&bowtie;": { "codepoints": [8904], "characters": "\u22C8" },
+ "&boxbox;": { "codepoints": [10697], "characters": "\u29C9" },
+ "&boxdl;": { "codepoints": [9488], "characters": "\u2510" },
+ "&boxdL;": { "codepoints": [9557], "characters": "\u2555" },
+ "&boxDl;": { "codepoints": [9558], "characters": "\u2556" },
+ "&boxDL;": { "codepoints": [9559], "characters": "\u2557" },
+ "&boxdr;": { "codepoints": [9484], "characters": "\u250C" },
+ "&boxdR;": { "codepoints": [9554], "characters": "\u2552" },
+ "&boxDr;": { "codepoints": [9555], "characters": "\u2553" },
+ "&boxDR;": { "codepoints": [9556], "characters": "\u2554" },
+ "&boxh;": { "codepoints": [9472], "characters": "\u2500" },
+ "&boxH;": { "codepoints": [9552], "characters": "\u2550" },
+ "&boxhd;": { "codepoints": [9516], "characters": "\u252C" },
+ "&boxHd;": { "codepoints": [9572], "characters": "\u2564" },
+ "&boxhD;": { "codepoints": [9573], "characters": "\u2565" },
+ "&boxHD;": { "codepoints": [9574], "characters": "\u2566" },
+ "&boxhu;": { "codepoints": [9524], "characters": "\u2534" },
+ "&boxHu;": { "codepoints": [9575], "characters": "\u2567" },
+ "&boxhU;": { "codepoints": [9576], "characters": "\u2568" },
+ "&boxHU;": { "codepoints": [9577], "characters": "\u2569" },
+ "&boxminus;": { "codepoints": [8863], "characters": "\u229F" },
+ "&boxplus;": { "codepoints": [8862], "characters": "\u229E" },
+ "&boxtimes;": { "codepoints": [8864], "characters": "\u22A0" },
+ "&boxul;": { "codepoints": [9496], "characters": "\u2518" },
+ "&boxuL;": { "codepoints": [9563], "characters": "\u255B" },
+ "&boxUl;": { "codepoints": [9564], "characters": "\u255C" },
+ "&boxUL;": { "codepoints": [9565], "characters": "\u255D" },
+ "&boxur;": { "codepoints": [9492], "characters": "\u2514" },
+ "&boxuR;": { "codepoints": [9560], "characters": "\u2558" },
+ "&boxUr;": { "codepoints": [9561], "characters": "\u2559" },
+ "&boxUR;": { "codepoints": [9562], "characters": "\u255A" },
+ "&boxv;": { "codepoints": [9474], "characters": "\u2502" },
+ "&boxV;": { "codepoints": [9553], "characters": "\u2551" },
+ "&boxvh;": { "codepoints": [9532], "characters": "\u253C" },
+ "&boxvH;": { "codepoints": [9578], "characters": "\u256A" },
+ "&boxVh;": { "codepoints": [9579], "characters": "\u256B" },
+ "&boxVH;": { "codepoints": [9580], "characters": "\u256C" },
+ "&boxvl;": { "codepoints": [9508], "characters": "\u2524" },
+ "&boxvL;": { "codepoints": [9569], "characters": "\u2561" },
+ "&boxVl;": { "codepoints": [9570], "characters": "\u2562" },
+ "&boxVL;": { "codepoints": [9571], "characters": "\u2563" },
+ "&boxvr;": { "codepoints": [9500], "characters": "\u251C" },
+ "&boxvR;": { "codepoints": [9566], "characters": "\u255E" },
+ "&boxVr;": { "codepoints": [9567], "characters": "\u255F" },
+ "&boxVR;": { "codepoints": [9568], "characters": "\u2560" },
+ "&bprime;": { "codepoints": [8245], "characters": "\u2035" },
+ "&breve;": { "codepoints": [728], "characters": "\u02D8" },
+ "&Breve;": { "codepoints": [728], "characters": "\u02D8" },
+ "&brvbar;": { "codepoints": [166], "characters": "\u00A6" },
+ "&brvbar": { "codepoints": [166], "characters": "\u00A6" },
+ "&bscr;": { "codepoints": [119991], "characters": "\uD835\uDCB7" },
+ "&Bscr;": { "codepoints": [8492], "characters": "\u212C" },
+ "&bsemi;": { "codepoints": [8271], "characters": "\u204F" },
+ "&bsim;": { "codepoints": [8765], "characters": "\u223D" },
+ "&bsime;": { "codepoints": [8909], "characters": "\u22CD" },
+ "&bsolb;": { "codepoints": [10693], "characters": "\u29C5" },
+ "&bsol;": { "codepoints": [92], "characters": "\u005C" },
+ "&bsolhsub;": { "codepoints": [10184], "characters": "\u27C8" },
+ "&bull;": { "codepoints": [8226], "characters": "\u2022" },
+ "&bullet;": { "codepoints": [8226], "characters": "\u2022" },
+ "&bump;": { "codepoints": [8782], "characters": "\u224E" },
+ "&bumpE;": { "codepoints": [10926], "characters": "\u2AAE" },
+ "&bumpe;": { "codepoints": [8783], "characters": "\u224F" },
+ "&Bumpeq;": { "codepoints": [8782], "characters": "\u224E" },
+ "&bumpeq;": { "codepoints": [8783], "characters": "\u224F" },
+ "&Cacute;": { "codepoints": [262], "characters": "\u0106" },
+ "&cacute;": { "codepoints": [263], "characters": "\u0107" },
+ "&capand;": { "codepoints": [10820], "characters": "\u2A44" },
+ "&capbrcup;": { "codepoints": [10825], "characters": "\u2A49" },
+ "&capcap;": { "codepoints": [10827], "characters": "\u2A4B" },
+ "&cap;": { "codepoints": [8745], "characters": "\u2229" },
+ "&Cap;": { "codepoints": [8914], "characters": "\u22D2" },
+ "&capcup;": { "codepoints": [10823], "characters": "\u2A47" },
+ "&capdot;": { "codepoints": [10816], "characters": "\u2A40" },
+ "&CapitalDifferentialD;": { "codepoints": [8517], "characters": "\u2145" },
+ "&caps;": { "codepoints": [8745, 65024], "characters": "\u2229\uFE00" },
+ "&caret;": { "codepoints": [8257], "characters": "\u2041" },
+ "&caron;": { "codepoints": [711], "characters": "\u02C7" },
+ "&Cayleys;": { "codepoints": [8493], "characters": "\u212D" },
+ "&ccaps;": { "codepoints": [10829], "characters": "\u2A4D" },
+ "&Ccaron;": { "codepoints": [268], "characters": "\u010C" },
+ "&ccaron;": { "codepoints": [269], "characters": "\u010D" },
+ "&Ccedil;": { "codepoints": [199], "characters": "\u00C7" },
+ "&Ccedil": { "codepoints": [199], "characters": "\u00C7" },
+ "&ccedil;": { "codepoints": [231], "characters": "\u00E7" },
+ "&ccedil": { "codepoints": [231], "characters": "\u00E7" },
+ "&Ccirc;": { "codepoints": [264], "characters": "\u0108" },
+ "&ccirc;": { "codepoints": [265], "characters": "\u0109" },
+ "&Cconint;": { "codepoints": [8752], "characters": "\u2230" },
+ "&ccups;": { "codepoints": [10828], "characters": "\u2A4C" },
+ "&ccupssm;": { "codepoints": [10832], "characters": "\u2A50" },
+ "&Cdot;": { "codepoints": [266], "characters": "\u010A" },
+ "&cdot;": { "codepoints": [267], "characters": "\u010B" },
+ "&cedil;": { "codepoints": [184], "characters": "\u00B8" },
+ "&cedil": { "codepoints": [184], "characters": "\u00B8" },
+ "&Cedilla;": { "codepoints": [184], "characters": "\u00B8" },
+ "&cemptyv;": { "codepoints": [10674], "characters": "\u29B2" },
+ "&cent;": { "codepoints": [162], "characters": "\u00A2" },
+ "&cent": { "codepoints": [162], "characters": "\u00A2" },
+ "&centerdot;": { "codepoints": [183], "characters": "\u00B7" },
+ "&CenterDot;": { "codepoints": [183], "characters": "\u00B7" },
+ "&cfr;": { "codepoints": [120096], "characters": "\uD835\uDD20" },
+ "&Cfr;": { "codepoints": [8493], "characters": "\u212D" },
+ "&CHcy;": { "codepoints": [1063], "characters": "\u0427" },
+ "&chcy;": { "codepoints": [1095], "characters": "\u0447" },
+ "&check;": { "codepoints": [10003], "characters": "\u2713" },
+ "&checkmark;": { "codepoints": [10003], "characters": "\u2713" },
+ "&Chi;": { "codepoints": [935], "characters": "\u03A7" },
+ "&chi;": { "codepoints": [967], "characters": "\u03C7" },
+ "&circ;": { "codepoints": [710], "characters": "\u02C6" },
+ "&circeq;": { "codepoints": [8791], "characters": "\u2257" },
+ "&circlearrowleft;": { "codepoints": [8634], "characters": "\u21BA" },
+ "&circlearrowright;": { "codepoints": [8635], "characters": "\u21BB" },
+ "&circledast;": { "codepoints": [8859], "characters": "\u229B" },
+ "&circledcirc;": { "codepoints": [8858], "characters": "\u229A" },
+ "&circleddash;": { "codepoints": [8861], "characters": "\u229D" },
+ "&CircleDot;": { "codepoints": [8857], "characters": "\u2299" },
+ "&circledR;": { "codepoints": [174], "characters": "\u00AE" },
+ "&circledS;": { "codepoints": [9416], "characters": "\u24C8" },
+ "&CircleMinus;": { "codepoints": [8854], "characters": "\u2296" },
+ "&CirclePlus;": { "codepoints": [8853], "characters": "\u2295" },
+ "&CircleTimes;": { "codepoints": [8855], "characters": "\u2297" },
+ "&cir;": { "codepoints": [9675], "characters": "\u25CB" },
+ "&cirE;": { "codepoints": [10691], "characters": "\u29C3" },
+ "&cire;": { "codepoints": [8791], "characters": "\u2257" },
+ "&cirfnint;": { "codepoints": [10768], "characters": "\u2A10" },
+ "&cirmid;": { "codepoints": [10991], "characters": "\u2AEF" },
+ "&cirscir;": { "codepoints": [10690], "characters": "\u29C2" },
+ "&ClockwiseContourIntegral;": { "codepoints": [8754], "characters": "\u2232" },
+ "&CloseCurlyDoubleQuote;": { "codepoints": [8221], "characters": "\u201D" },
+ "&CloseCurlyQuote;": { "codepoints": [8217], "characters": "\u2019" },
+ "&clubs;": { "codepoints": [9827], "characters": "\u2663" },
+ "&clubsuit;": { "codepoints": [9827], "characters": "\u2663" },
+ "&colon;": { "codepoints": [58], "characters": "\u003A" },
+ "&Colon;": { "codepoints": [8759], "characters": "\u2237" },
+ "&Colone;": { "codepoints": [10868], "characters": "\u2A74" },
+ "&colone;": { "codepoints": [8788], "characters": "\u2254" },
+ "&coloneq;": { "codepoints": [8788], "characters": "\u2254" },
+ "&comma;": { "codepoints": [44], "characters": "\u002C" },
+ "&commat;": { "codepoints": [64], "characters": "\u0040" },
+ "&comp;": { "codepoints": [8705], "characters": "\u2201" },
+ "&compfn;": { "codepoints": [8728], "characters": "\u2218" },
+ "&complement;": { "codepoints": [8705], "characters": "\u2201" },
+ "&complexes;": { "codepoints": [8450], "characters": "\u2102" },
+ "&cong;": { "codepoints": [8773], "characters": "\u2245" },
+ "&congdot;": { "codepoints": [10861], "characters": "\u2A6D" },
+ "&Congruent;": { "codepoints": [8801], "characters": "\u2261" },
+ "&conint;": { "codepoints": [8750], "characters": "\u222E" },
+ "&Conint;": { "codepoints": [8751], "characters": "\u222F" },
+ "&ContourIntegral;": { "codepoints": [8750], "characters": "\u222E" },
+ "&copf;": { "codepoints": [120148], "characters": "\uD835\uDD54" },
+ "&Copf;": { "codepoints": [8450], "characters": "\u2102" },
+ "&coprod;": { "codepoints": [8720], "characters": "\u2210" },
+ "&Coproduct;": { "codepoints": [8720], "characters": "\u2210" },
+ "&copy;": { "codepoints": [169], "characters": "\u00A9" },
+ "&copy": { "codepoints": [169], "characters": "\u00A9" },
+ "&COPY;": { "codepoints": [169], "characters": "\u00A9" },
+ "&COPY": { "codepoints": [169], "characters": "\u00A9" },
+ "&copysr;": { "codepoints": [8471], "characters": "\u2117" },
+ "&CounterClockwiseContourIntegral;": { "codepoints": [8755], "characters": "\u2233" },
+ "&crarr;": { "codepoints": [8629], "characters": "\u21B5" },
+ "&cross;": { "codepoints": [10007], "characters": "\u2717" },
+ "&Cross;": { "codepoints": [10799], "characters": "\u2A2F" },
+ "&Cscr;": { "codepoints": [119966], "characters": "\uD835\uDC9E" },
+ "&cscr;": { "codepoints": [119992], "characters": "\uD835\uDCB8" },
+ "&csub;": { "codepoints": [10959], "characters": "\u2ACF" },
+ "&csube;": { "codepoints": [10961], "characters": "\u2AD1" },
+ "&csup;": { "codepoints": [10960], "characters": "\u2AD0" },
+ "&csupe;": { "codepoints": [10962], "characters": "\u2AD2" },
+ "&ctdot;": { "codepoints": [8943], "characters": "\u22EF" },
+ "&cudarrl;": { "codepoints": [10552], "characters": "\u2938" },
+ "&cudarrr;": { "codepoints": [10549], "characters": "\u2935" },
+ "&cuepr;": { "codepoints": [8926], "characters": "\u22DE" },
+ "&cuesc;": { "codepoints": [8927], "characters": "\u22DF" },
+ "&cularr;": { "codepoints": [8630], "characters": "\u21B6" },
+ "&cularrp;": { "codepoints": [10557], "characters": "\u293D" },
+ "&cupbrcap;": { "codepoints": [10824], "characters": "\u2A48" },
+ "&cupcap;": { "codepoints": [10822], "characters": "\u2A46" },
+ "&CupCap;": { "codepoints": [8781], "characters": "\u224D" },
+ "&cup;": { "codepoints": [8746], "characters": "\u222A" },
+ "&Cup;": { "codepoints": [8915], "characters": "\u22D3" },
+ "&cupcup;": { "codepoints": [10826], "characters": "\u2A4A" },
+ "&cupdot;": { "codepoints": [8845], "characters": "\u228D" },
+ "&cupor;": { "codepoints": [10821], "characters": "\u2A45" },
+ "&cups;": { "codepoints": [8746, 65024], "characters": "\u222A\uFE00" },
+ "&curarr;": { "codepoints": [8631], "characters": "\u21B7" },
+ "&curarrm;": { "codepoints": [10556], "characters": "\u293C" },
+ "&curlyeqprec;": { "codepoints": [8926], "characters": "\u22DE" },
+ "&curlyeqsucc;": { "codepoints": [8927], "characters": "\u22DF" },
+ "&curlyvee;": { "codepoints": [8910], "characters": "\u22CE" },
+ "&curlywedge;": { "codepoints": [8911], "characters": "\u22CF" },
+ "&curren;": { "codepoints": [164], "characters": "\u00A4" },
+ "&curren": { "codepoints": [164], "characters": "\u00A4" },
+ "&curvearrowleft;": { "codepoints": [8630], "characters": "\u21B6" },
+ "&curvearrowright;": { "codepoints": [8631], "characters": "\u21B7" },
+ "&cuvee;": { "codepoints": [8910], "characters": "\u22CE" },
+ "&cuwed;": { "codepoints": [8911], "characters": "\u22CF" },
+ "&cwconint;": { "codepoints": [8754], "characters": "\u2232" },
+ "&cwint;": { "codepoints": [8753], "characters": "\u2231" },
+ "&cylcty;": { "codepoints": [9005], "characters": "\u232D" },
+ "&dagger;": { "codepoints": [8224], "characters": "\u2020" },
+ "&Dagger;": { "codepoints": [8225], "characters": "\u2021" },
+ "&daleth;": { "codepoints": [8504], "characters": "\u2138" },
+ "&darr;": { "codepoints": [8595], "characters": "\u2193" },
+ "&Darr;": { "codepoints": [8609], "characters": "\u21A1" },
+ "&dArr;": { "codepoints": [8659], "characters": "\u21D3" },
+ "&dash;": { "codepoints": [8208], "characters": "\u2010" },
+ "&Dashv;": { "codepoints": [10980], "characters": "\u2AE4" },
+ "&dashv;": { "codepoints": [8867], "characters": "\u22A3" },
+ "&dbkarow;": { "codepoints": [10511], "characters": "\u290F" },
+ "&dblac;": { "codepoints": [733], "characters": "\u02DD" },
+ "&Dcaron;": { "codepoints": [270], "characters": "\u010E" },
+ "&dcaron;": { "codepoints": [271], "characters": "\u010F" },
+ "&Dcy;": { "codepoints": [1044], "characters": "\u0414" },
+ "&dcy;": { "codepoints": [1076], "characters": "\u0434" },
+ "&ddagger;": { "codepoints": [8225], "characters": "\u2021" },
+ "&ddarr;": { "codepoints": [8650], "characters": "\u21CA" },
+ "&DD;": { "codepoints": [8517], "characters": "\u2145" },
+ "&dd;": { "codepoints": [8518], "characters": "\u2146" },
+ "&DDotrahd;": { "codepoints": [10513], "characters": "\u2911" },
+ "&ddotseq;": { "codepoints": [10871], "characters": "\u2A77" },
+ "&deg;": { "codepoints": [176], "characters": "\u00B0" },
+ "&deg": { "codepoints": [176], "characters": "\u00B0" },
+ "&Del;": { "codepoints": [8711], "characters": "\u2207" },
+ "&Delta;": { "codepoints": [916], "characters": "\u0394" },
+ "&delta;": { "codepoints": [948], "characters": "\u03B4" },
+ "&demptyv;": { "codepoints": [10673], "characters": "\u29B1" },
+ "&dfisht;": { "codepoints": [10623], "characters": "\u297F" },
+ "&Dfr;": { "codepoints": [120071], "characters": "\uD835\uDD07" },
+ "&dfr;": { "codepoints": [120097], "characters": "\uD835\uDD21" },
+ "&dHar;": { "codepoints": [10597], "characters": "\u2965" },
+ "&dharl;": { "codepoints": [8643], "characters": "\u21C3" },
+ "&dharr;": { "codepoints": [8642], "characters": "\u21C2" },
+ "&DiacriticalAcute;": { "codepoints": [180], "characters": "\u00B4" },
+ "&DiacriticalDot;": { "codepoints": [729], "characters": "\u02D9" },
+ "&DiacriticalDoubleAcute;": { "codepoints": [733], "characters": "\u02DD" },
+ "&DiacriticalGrave;": { "codepoints": [96], "characters": "\u0060" },
+ "&DiacriticalTilde;": { "codepoints": [732], "characters": "\u02DC" },
+ "&diam;": { "codepoints": [8900], "characters": "\u22C4" },
+ "&diamond;": { "codepoints": [8900], "characters": "\u22C4" },
+ "&Diamond;": { "codepoints": [8900], "characters": "\u22C4" },
+ "&diamondsuit;": { "codepoints": [9830], "characters": "\u2666" },
+ "&diams;": { "codepoints": [9830], "characters": "\u2666" },
+ "&die;": { "codepoints": [168], "characters": "\u00A8" },
+ "&DifferentialD;": { "codepoints": [8518], "characters": "\u2146" },
+ "&digamma;": { "codepoints": [989], "characters": "\u03DD" },
+ "&disin;": { "codepoints": [8946], "characters": "\u22F2" },
+ "&div;": { "codepoints": [247], "characters": "\u00F7" },
+ "&divide;": { "codepoints": [247], "characters": "\u00F7" },
+ "&divide": { "codepoints": [247], "characters": "\u00F7" },
+ "&divideontimes;": { "codepoints": [8903], "characters": "\u22C7" },
+ "&divonx;": { "codepoints": [8903], "characters": "\u22C7" },
+ "&DJcy;": { "codepoints": [1026], "characters": "\u0402" },
+ "&djcy;": { "codepoints": [1106], "characters": "\u0452" },
+ "&dlcorn;": { "codepoints": [8990], "characters": "\u231E" },
+ "&dlcrop;": { "codepoints": [8973], "characters": "\u230D" },
+ "&dollar;": { "codepoints": [36], "characters": "\u0024" },
+ "&Dopf;": { "codepoints": [120123], "characters": "\uD835\uDD3B" },
+ "&dopf;": { "codepoints": [120149], "characters": "\uD835\uDD55" },
+ "&Dot;": { "codepoints": [168], "characters": "\u00A8" },
+ "&dot;": { "codepoints": [729], "characters": "\u02D9" },
+ "&DotDot;": { "codepoints": [8412], "characters": "\u20DC" },
+ "&doteq;": { "codepoints": [8784], "characters": "\u2250" },
+ "&doteqdot;": { "codepoints": [8785], "characters": "\u2251" },
+ "&DotEqual;": { "codepoints": [8784], "characters": "\u2250" },
+ "&dotminus;": { "codepoints": [8760], "characters": "\u2238" },
+ "&dotplus;": { "codepoints": [8724], "characters": "\u2214" },
+ "&dotsquare;": { "codepoints": [8865], "characters": "\u22A1" },
+ "&doublebarwedge;": { "codepoints": [8966], "characters": "\u2306" },
+ "&DoubleContourIntegral;": { "codepoints": [8751], "characters": "\u222F" },
+ "&DoubleDot;": { "codepoints": [168], "characters": "\u00A8" },
+ "&DoubleDownArrow;": { "codepoints": [8659], "characters": "\u21D3" },
+ "&DoubleLeftArrow;": { "codepoints": [8656], "characters": "\u21D0" },
+ "&DoubleLeftRightArrow;": { "codepoints": [8660], "characters": "\u21D4" },
+ "&DoubleLeftTee;": { "codepoints": [10980], "characters": "\u2AE4" },
+ "&DoubleLongLeftArrow;": { "codepoints": [10232], "characters": "\u27F8" },
+ "&DoubleLongLeftRightArrow;": { "codepoints": [10234], "characters": "\u27FA" },
+ "&DoubleLongRightArrow;": { "codepoints": [10233], "characters": "\u27F9" },
+ "&DoubleRightArrow;": { "codepoints": [8658], "characters": "\u21D2" },
+ "&DoubleRightTee;": { "codepoints": [8872], "characters": "\u22A8" },
+ "&DoubleUpArrow;": { "codepoints": [8657], "characters": "\u21D1" },
+ "&DoubleUpDownArrow;": { "codepoints": [8661], "characters": "\u21D5" },
+ "&DoubleVerticalBar;": { "codepoints": [8741], "characters": "\u2225" },
+ "&DownArrowBar;": { "codepoints": [10515], "characters": "\u2913" },
+ "&downarrow;": { "codepoints": [8595], "characters": "\u2193" },
+ "&DownArrow;": { "codepoints": [8595], "characters": "\u2193" },
+ "&Downarrow;": { "codepoints": [8659], "characters": "\u21D3" },
+ "&DownArrowUpArrow;": { "codepoints": [8693], "characters": "\u21F5" },
+ "&DownBreve;": { "codepoints": [785], "characters": "\u0311" },
+ "&downdownarrows;": { "codepoints": [8650], "characters": "\u21CA" },
+ "&downharpoonleft;": { "codepoints": [8643], "characters": "\u21C3" },
+ "&downharpoonright;": { "codepoints": [8642], "characters": "\u21C2" },
+ "&DownLeftRightVector;": { "codepoints": [10576], "characters": "\u2950" },
+ "&DownLeftTeeVector;": { "codepoints": [10590], "characters": "\u295E" },
+ "&DownLeftVectorBar;": { "codepoints": [10582], "characters": "\u2956" },
+ "&DownLeftVector;": { "codepoints": [8637], "characters": "\u21BD" },
+ "&DownRightTeeVector;": { "codepoints": [10591], "characters": "\u295F" },
+ "&DownRightVectorBar;": { "codepoints": [10583], "characters": "\u2957" },
+ "&DownRightVector;": { "codepoints": [8641], "characters": "\u21C1" },
+ "&DownTeeArrow;": { "codepoints": [8615], "characters": "\u21A7" },
+ "&DownTee;": { "codepoints": [8868], "characters": "\u22A4" },
+ "&drbkarow;": { "codepoints": [10512], "characters": "\u2910" },
+ "&drcorn;": { "codepoints": [8991], "characters": "\u231F" },
+ "&drcrop;": { "codepoints": [8972], "characters": "\u230C" },
+ "&Dscr;": { "codepoints": [119967], "characters": "\uD835\uDC9F" },
+ "&dscr;": { "codepoints": [119993], "characters": "\uD835\uDCB9" },
+ "&DScy;": { "codepoints": [1029], "characters": "\u0405" },
+ "&dscy;": { "codepoints": [1109], "characters": "\u0455" },
+ "&dsol;": { "codepoints": [10742], "characters": "\u29F6" },
+ "&Dstrok;": { "codepoints": [272], "characters": "\u0110" },
+ "&dstrok;": { "codepoints": [273], "characters": "\u0111" },
+ "&dtdot;": { "codepoints": [8945], "characters": "\u22F1" },
+ "&dtri;": { "codepoints": [9663], "characters": "\u25BF" },
+ "&dtrif;": { "codepoints": [9662], "characters": "\u25BE" },
+ "&duarr;": { "codepoints": [8693], "characters": "\u21F5" },
+ "&duhar;": { "codepoints": [10607], "characters": "\u296F" },
+ "&dwangle;": { "codepoints": [10662], "characters": "\u29A6" },
+ "&DZcy;": { "codepoints": [1039], "characters": "\u040F" },
+ "&dzcy;": { "codepoints": [1119], "characters": "\u045F" },
+ "&dzigrarr;": { "codepoints": [10239], "characters": "\u27FF" },
+ "&Eacute;": { "codepoints": [201], "characters": "\u00C9" },
+ "&Eacute": { "codepoints": [201], "characters": "\u00C9" },
+ "&eacute;": { "codepoints": [233], "characters": "\u00E9" },
+ "&eacute": { "codepoints": [233], "characters": "\u00E9" },
+ "&easter;": { "codepoints": [10862], "characters": "\u2A6E" },
+ "&Ecaron;": { "codepoints": [282], "characters": "\u011A" },
+ "&ecaron;": { "codepoints": [283], "characters": "\u011B" },
+ "&Ecirc;": { "codepoints": [202], "characters": "\u00CA" },
+ "&Ecirc": { "codepoints": [202], "characters": "\u00CA" },
+ "&ecirc;": { "codepoints": [234], "characters": "\u00EA" },
+ "&ecirc": { "codepoints": [234], "characters": "\u00EA" },
+ "&ecir;": { "codepoints": [8790], "characters": "\u2256" },
+ "&ecolon;": { "codepoints": [8789], "characters": "\u2255" },
+ "&Ecy;": { "codepoints": [1069], "characters": "\u042D" },
+ "&ecy;": { "codepoints": [1101], "characters": "\u044D" },
+ "&eDDot;": { "codepoints": [10871], "characters": "\u2A77" },
+ "&Edot;": { "codepoints": [278], "characters": "\u0116" },
+ "&edot;": { "codepoints": [279], "characters": "\u0117" },
+ "&eDot;": { "codepoints": [8785], "characters": "\u2251" },
+ "&ee;": { "codepoints": [8519], "characters": "\u2147" },
+ "&efDot;": { "codepoints": [8786], "characters": "\u2252" },
+ "&Efr;": { "codepoints": [120072], "characters": "\uD835\uDD08" },
+ "&efr;": { "codepoints": [120098], "characters": "\uD835\uDD22" },
+ "&eg;": { "codepoints": [10906], "characters": "\u2A9A" },
+ "&Egrave;": { "codepoints": [200], "characters": "\u00C8" },
+ "&Egrave": { "codepoints": [200], "characters": "\u00C8" },
+ "&egrave;": { "codepoints": [232], "characters": "\u00E8" },
+ "&egrave": { "codepoints": [232], "characters": "\u00E8" },
+ "&egs;": { "codepoints": [10902], "characters": "\u2A96" },
+ "&egsdot;": { "codepoints": [10904], "characters": "\u2A98" },
+ "&el;": { "codepoints": [10905], "characters": "\u2A99" },
+ "&Element;": { "codepoints": [8712], "characters": "\u2208" },
+ "&elinters;": { "codepoints": [9191], "characters": "\u23E7" },
+ "&ell;": { "codepoints": [8467], "characters": "\u2113" },
+ "&els;": { "codepoints": [10901], "characters": "\u2A95" },
+ "&elsdot;": { "codepoints": [10903], "characters": "\u2A97" },
+ "&Emacr;": { "codepoints": [274], "characters": "\u0112" },
+ "&emacr;": { "codepoints": [275], "characters": "\u0113" },
+ "&empty;": { "codepoints": [8709], "characters": "\u2205" },
+ "&emptyset;": { "codepoints": [8709], "characters": "\u2205" },
+ "&EmptySmallSquare;": { "codepoints": [9723], "characters": "\u25FB" },
+ "&emptyv;": { "codepoints": [8709], "characters": "\u2205" },
+ "&EmptyVerySmallSquare;": { "codepoints": [9643], "characters": "\u25AB" },
+ "&emsp13;": { "codepoints": [8196], "characters": "\u2004" },
+ "&emsp14;": { "codepoints": [8197], "characters": "\u2005" },
+ "&emsp;": { "codepoints": [8195], "characters": "\u2003" },
+ "&ENG;": { "codepoints": [330], "characters": "\u014A" },
+ "&eng;": { "codepoints": [331], "characters": "\u014B" },
+ "&ensp;": { "codepoints": [8194], "characters": "\u2002" },
+ "&Eogon;": { "codepoints": [280], "characters": "\u0118" },
+ "&eogon;": { "codepoints": [281], "characters": "\u0119" },
+ "&Eopf;": { "codepoints": [120124], "characters": "\uD835\uDD3C" },
+ "&eopf;": { "codepoints": [120150], "characters": "\uD835\uDD56" },
+ "&epar;": { "codepoints": [8917], "characters": "\u22D5" },
+ "&eparsl;": { "codepoints": [10723], "characters": "\u29E3" },
+ "&eplus;": { "codepoints": [10865], "characters": "\u2A71" },
+ "&epsi;": { "codepoints": [949], "characters": "\u03B5" },
+ "&Epsilon;": { "codepoints": [917], "characters": "\u0395" },
+ "&epsilon;": { "codepoints": [949], "characters": "\u03B5" },
+ "&epsiv;": { "codepoints": [1013], "characters": "\u03F5" },
+ "&eqcirc;": { "codepoints": [8790], "characters": "\u2256" },
+ "&eqcolon;": { "codepoints": [8789], "characters": "\u2255" },
+ "&eqsim;": { "codepoints": [8770], "characters": "\u2242" },
+ "&eqslantgtr;": { "codepoints": [10902], "characters": "\u2A96" },
+ "&eqslantless;": { "codepoints": [10901], "characters": "\u2A95" },
+ "&Equal;": { "codepoints": [10869], "characters": "\u2A75" },
+ "&equals;": { "codepoints": [61], "characters": "\u003D" },
+ "&EqualTilde;": { "codepoints": [8770], "characters": "\u2242" },
+ "&equest;": { "codepoints": [8799], "characters": "\u225F" },
+ "&Equilibrium;": { "codepoints": [8652], "characters": "\u21CC" },
+ "&equiv;": { "codepoints": [8801], "characters": "\u2261" },
+ "&equivDD;": { "codepoints": [10872], "characters": "\u2A78" },
+ "&eqvparsl;": { "codepoints": [10725], "characters": "\u29E5" },
+ "&erarr;": { "codepoints": [10609], "characters": "\u2971" },
+ "&erDot;": { "codepoints": [8787], "characters": "\u2253" },
+ "&escr;": { "codepoints": [8495], "characters": "\u212F" },
+ "&Escr;": { "codepoints": [8496], "characters": "\u2130" },
+ "&esdot;": { "codepoints": [8784], "characters": "\u2250" },
+ "&Esim;": { "codepoints": [10867], "characters": "\u2A73" },
+ "&esim;": { "codepoints": [8770], "characters": "\u2242" },
+ "&Eta;": { "codepoints": [919], "characters": "\u0397" },
+ "&eta;": { "codepoints": [951], "characters": "\u03B7" },
+ "&ETH;": { "codepoints": [208], "characters": "\u00D0" },
+ "&ETH": { "codepoints": [208], "characters": "\u00D0" },
+ "&eth;": { "codepoints": [240], "characters": "\u00F0" },
+ "&eth": { "codepoints": [240], "characters": "\u00F0" },
+ "&Euml;": { "codepoints": [203], "characters": "\u00CB" },
+ "&Euml": { "codepoints": [203], "characters": "\u00CB" },
+ "&euml;": { "codepoints": [235], "characters": "\u00EB" },
+ "&euml": { "codepoints": [235], "characters": "\u00EB" },
+ "&euro;": { "codepoints": [8364], "characters": "\u20AC" },
+ "&excl;": { "codepoints": [33], "characters": "\u0021" },
+ "&exist;": { "codepoints": [8707], "characters": "\u2203" },
+ "&Exists;": { "codepoints": [8707], "characters": "\u2203" },
+ "&expectation;": { "codepoints": [8496], "characters": "\u2130" },
+ "&exponentiale;": { "codepoints": [8519], "characters": "\u2147" },
+ "&ExponentialE;": { "codepoints": [8519], "characters": "\u2147" },
+ "&fallingdotseq;": { "codepoints": [8786], "characters": "\u2252" },
+ "&Fcy;": { "codepoints": [1060], "characters": "\u0424" },
+ "&fcy;": { "codepoints": [1092], "characters": "\u0444" },
+ "&female;": { "codepoints": [9792], "characters": "\u2640" },
+ "&ffilig;": { "codepoints": [64259], "characters": "\uFB03" },
+ "&fflig;": { "codepoints": [64256], "characters": "\uFB00" },
+ "&ffllig;": { "codepoints": [64260], "characters": "\uFB04" },
+ "&Ffr;": { "codepoints": [120073], "characters": "\uD835\uDD09" },
+ "&ffr;": { "codepoints": [120099], "characters": "\uD835\uDD23" },
+ "&filig;": { "codepoints": [64257], "characters": "\uFB01" },
+ "&FilledSmallSquare;": { "codepoints": [9724], "characters": "\u25FC" },
+ "&FilledVerySmallSquare;": { "codepoints": [9642], "characters": "\u25AA" },
+ "&fjlig;": { "codepoints": [102, 106], "characters": "\u0066\u006A" },
+ "&flat;": { "codepoints": [9837], "characters": "\u266D" },
+ "&fllig;": { "codepoints": [64258], "characters": "\uFB02" },
+ "&fltns;": { "codepoints": [9649], "characters": "\u25B1" },
+ "&fnof;": { "codepoints": [402], "characters": "\u0192" },
+ "&Fopf;": { "codepoints": [120125], "characters": "\uD835\uDD3D" },
+ "&fopf;": { "codepoints": [120151], "characters": "\uD835\uDD57" },
+ "&forall;": { "codepoints": [8704], "characters": "\u2200" },
+ "&ForAll;": { "codepoints": [8704], "characters": "\u2200" },
+ "&fork;": { "codepoints": [8916], "characters": "\u22D4" },
+ "&forkv;": { "codepoints": [10969], "characters": "\u2AD9" },
+ "&Fouriertrf;": { "codepoints": [8497], "characters": "\u2131" },
+ "&fpartint;": { "codepoints": [10765], "characters": "\u2A0D" },
+ "&frac12;": { "codepoints": [189], "characters": "\u00BD" },
+ "&frac12": { "codepoints": [189], "characters": "\u00BD" },
+ "&frac13;": { "codepoints": [8531], "characters": "\u2153" },
+ "&frac14;": { "codepoints": [188], "characters": "\u00BC" },
+ "&frac14": { "codepoints": [188], "characters": "\u00BC" },
+ "&frac15;": { "codepoints": [8533], "characters": "\u2155" },
+ "&frac16;": { "codepoints": [8537], "characters": "\u2159" },
+ "&frac18;": { "codepoints": [8539], "characters": "\u215B" },
+ "&frac23;": { "codepoints": [8532], "characters": "\u2154" },
+ "&frac25;": { "codepoints": [8534], "characters": "\u2156" },
+ "&frac34;": { "codepoints": [190], "characters": "\u00BE" },
+ "&frac34": { "codepoints": [190], "characters": "\u00BE" },
+ "&frac35;": { "codepoints": [8535], "characters": "\u2157" },
+ "&frac38;": { "codepoints": [8540], "characters": "\u215C" },
+ "&frac45;": { "codepoints": [8536], "characters": "\u2158" },
+ "&frac56;": { "codepoints": [8538], "characters": "\u215A" },
+ "&frac58;": { "codepoints": [8541], "characters": "\u215D" },
+ "&frac78;": { "codepoints": [8542], "characters": "\u215E" },
+ "&frasl;": { "codepoints": [8260], "characters": "\u2044" },
+ "&frown;": { "codepoints": [8994], "characters": "\u2322" },
+ "&fscr;": { "codepoints": [119995], "characters": "\uD835\uDCBB" },
+ "&Fscr;": { "codepoints": [8497], "characters": "\u2131" },
+ "&gacute;": { "codepoints": [501], "characters": "\u01F5" },
+ "&Gamma;": { "codepoints": [915], "characters": "\u0393" },
+ "&gamma;": { "codepoints": [947], "characters": "\u03B3" },
+ "&Gammad;": { "codepoints": [988], "characters": "\u03DC" },
+ "&gammad;": { "codepoints": [989], "characters": "\u03DD" },
+ "&gap;": { "codepoints": [10886], "characters": "\u2A86" },
+ "&Gbreve;": { "codepoints": [286], "characters": "\u011E" },
+ "&gbreve;": { "codepoints": [287], "characters": "\u011F" },
+ "&Gcedil;": { "codepoints": [290], "characters": "\u0122" },
+ "&Gcirc;": { "codepoints": [284], "characters": "\u011C" },
+ "&gcirc;": { "codepoints": [285], "characters": "\u011D" },
+ "&Gcy;": { "codepoints": [1043], "characters": "\u0413" },
+ "&gcy;": { "codepoints": [1075], "characters": "\u0433" },
+ "&Gdot;": { "codepoints": [288], "characters": "\u0120" },
+ "&gdot;": { "codepoints": [289], "characters": "\u0121" },
+ "&ge;": { "codepoints": [8805], "characters": "\u2265" },
+ "&gE;": { "codepoints": [8807], "characters": "\u2267" },
+ "&gEl;": { "codepoints": [10892], "characters": "\u2A8C" },
+ "&gel;": { "codepoints": [8923], "characters": "\u22DB" },
+ "&geq;": { "codepoints": [8805], "characters": "\u2265" },
+ "&geqq;": { "codepoints": [8807], "characters": "\u2267" },
+ "&geqslant;": { "codepoints": [10878], "characters": "\u2A7E" },
+ "&gescc;": { "codepoints": [10921], "characters": "\u2AA9" },
+ "&ges;": { "codepoints": [10878], "characters": "\u2A7E" },
+ "&gesdot;": { "codepoints": [10880], "characters": "\u2A80" },
+ "&gesdoto;": { "codepoints": [10882], "characters": "\u2A82" },
+ "&gesdotol;": { "codepoints": [10884], "characters": "\u2A84" },
+ "&gesl;": { "codepoints": [8923, 65024], "characters": "\u22DB\uFE00" },
+ "&gesles;": { "codepoints": [10900], "characters": "\u2A94" },
+ "&Gfr;": { "codepoints": [120074], "characters": "\uD835\uDD0A" },
+ "&gfr;": { "codepoints": [120100], "characters": "\uD835\uDD24" },
+ "&gg;": { "codepoints": [8811], "characters": "\u226B" },
+ "&Gg;": { "codepoints": [8921], "characters": "\u22D9" },
+ "&ggg;": { "codepoints": [8921], "characters": "\u22D9" },
+ "&gimel;": { "codepoints": [8503], "characters": "\u2137" },
+ "&GJcy;": { "codepoints": [1027], "characters": "\u0403" },
+ "&gjcy;": { "codepoints": [1107], "characters": "\u0453" },
+ "&gla;": { "codepoints": [10917], "characters": "\u2AA5" },
+ "&gl;": { "codepoints": [8823], "characters": "\u2277" },
+ "&glE;": { "codepoints": [10898], "characters": "\u2A92" },
+ "&glj;": { "codepoints": [10916], "characters": "\u2AA4" },
+ "&gnap;": { "codepoints": [10890], "characters": "\u2A8A" },
+ "&gnapprox;": { "codepoints": [10890], "characters": "\u2A8A" },
+ "&gne;": { "codepoints": [10888], "characters": "\u2A88" },
+ "&gnE;": { "codepoints": [8809], "characters": "\u2269" },
+ "&gneq;": { "codepoints": [10888], "characters": "\u2A88" },
+ "&gneqq;": { "codepoints": [8809], "characters": "\u2269" },
+ "&gnsim;": { "codepoints": [8935], "characters": "\u22E7" },
+ "&Gopf;": { "codepoints": [120126], "characters": "\uD835\uDD3E" },
+ "&gopf;": { "codepoints": [120152], "characters": "\uD835\uDD58" },
+ "&grave;": { "codepoints": [96], "characters": "\u0060" },
+ "&GreaterEqual;": { "codepoints": [8805], "characters": "\u2265" },
+ "&GreaterEqualLess;": { "codepoints": [8923], "characters": "\u22DB" },
+ "&GreaterFullEqual;": { "codepoints": [8807], "characters": "\u2267" },
+ "&GreaterGreater;": { "codepoints": [10914], "characters": "\u2AA2" },
+ "&GreaterLess;": { "codepoints": [8823], "characters": "\u2277" },
+ "&GreaterSlantEqual;": { "codepoints": [10878], "characters": "\u2A7E" },
+ "&GreaterTilde;": { "codepoints": [8819], "characters": "\u2273" },
+ "&Gscr;": { "codepoints": [119970], "characters": "\uD835\uDCA2" },
+ "&gscr;": { "codepoints": [8458], "characters": "\u210A" },
+ "&gsim;": { "codepoints": [8819], "characters": "\u2273" },
+ "&gsime;": { "codepoints": [10894], "characters": "\u2A8E" },
+ "&gsiml;": { "codepoints": [10896], "characters": "\u2A90" },
+ "&gtcc;": { "codepoints": [10919], "characters": "\u2AA7" },
+ "&gtcir;": { "codepoints": [10874], "characters": "\u2A7A" },
+ "&gt;": { "codepoints": [62], "characters": "\u003E" },
+ "&gt": { "codepoints": [62], "characters": "\u003E" },
+ "&GT;": { "codepoints": [62], "characters": "\u003E" },
+ "&GT": { "codepoints": [62], "characters": "\u003E" },
+ "&Gt;": { "codepoints": [8811], "characters": "\u226B" },
+ "&gtdot;": { "codepoints": [8919], "characters": "\u22D7" },
+ "&gtlPar;": { "codepoints": [10645], "characters": "\u2995" },
+ "&gtquest;": { "codepoints": [10876], "characters": "\u2A7C" },
+ "&gtrapprox;": { "codepoints": [10886], "characters": "\u2A86" },
+ "&gtrarr;": { "codepoints": [10616], "characters": "\u2978" },
+ "&gtrdot;": { "codepoints": [8919], "characters": "\u22D7" },
+ "&gtreqless;": { "codepoints": [8923], "characters": "\u22DB" },
+ "&gtreqqless;": { "codepoints": [10892], "characters": "\u2A8C" },
+ "&gtrless;": { "codepoints": [8823], "characters": "\u2277" },
+ "&gtrsim;": { "codepoints": [8819], "characters": "\u2273" },
+ "&gvertneqq;": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" },
+ "&gvnE;": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" },
+ "&Hacek;": { "codepoints": [711], "characters": "\u02C7" },
+ "&hairsp;": { "codepoints": [8202], "characters": "\u200A" },
+ "&half;": { "codepoints": [189], "characters": "\u00BD" },
+ "&hamilt;": { "codepoints": [8459], "characters": "\u210B" },
+ "&HARDcy;": { "codepoints": [1066], "characters": "\u042A" },
+ "&hardcy;": { "codepoints": [1098], "characters": "\u044A" },
+ "&harrcir;": { "codepoints": [10568], "characters": "\u2948" },
+ "&harr;": { "codepoints": [8596], "characters": "\u2194" },
+ "&hArr;": { "codepoints": [8660], "characters": "\u21D4" },
+ "&harrw;": { "codepoints": [8621], "characters": "\u21AD" },
+ "&Hat;": { "codepoints": [94], "characters": "\u005E" },
+ "&hbar;": { "codepoints": [8463], "characters": "\u210F" },
+ "&Hcirc;": { "codepoints": [292], "characters": "\u0124" },
+ "&hcirc;": { "codepoints": [293], "characters": "\u0125" },
+ "&hearts;": { "codepoints": [9829], "characters": "\u2665" },
+ "&heartsuit;": { "codepoints": [9829], "characters": "\u2665" },
+ "&hellip;": { "codepoints": [8230], "characters": "\u2026" },
+ "&hercon;": { "codepoints": [8889], "characters": "\u22B9" },
+ "&hfr;": { "codepoints": [120101], "characters": "\uD835\uDD25" },
+ "&Hfr;": { "codepoints": [8460], "characters": "\u210C" },
+ "&HilbertSpace;": { "codepoints": [8459], "characters": "\u210B" },
+ "&hksearow;": { "codepoints": [10533], "characters": "\u2925" },
+ "&hkswarow;": { "codepoints": [10534], "characters": "\u2926" },
+ "&hoarr;": { "codepoints": [8703], "characters": "\u21FF" },
+ "&homtht;": { "codepoints": [8763], "characters": "\u223B" },
+ "&hookleftarrow;": { "codepoints": [8617], "characters": "\u21A9" },
+ "&hookrightarrow;": { "codepoints": [8618], "characters": "\u21AA" },
+ "&hopf;": { "codepoints": [120153], "characters": "\uD835\uDD59" },
+ "&Hopf;": { "codepoints": [8461], "characters": "\u210D" },
+ "&horbar;": { "codepoints": [8213], "characters": "\u2015" },
+ "&HorizontalLine;": { "codepoints": [9472], "characters": "\u2500" },
+ "&hscr;": { "codepoints": [119997], "characters": "\uD835\uDCBD" },
+ "&Hscr;": { "codepoints": [8459], "characters": "\u210B" },
+ "&hslash;": { "codepoints": [8463], "characters": "\u210F" },
+ "&Hstrok;": { "codepoints": [294], "characters": "\u0126" },
+ "&hstrok;": { "codepoints": [295], "characters": "\u0127" },
+ "&HumpDownHump;": { "codepoints": [8782], "characters": "\u224E" },
+ "&HumpEqual;": { "codepoints": [8783], "characters": "\u224F" },
+ "&hybull;": { "codepoints": [8259], "characters": "\u2043" },
+ "&hyphen;": { "codepoints": [8208], "characters": "\u2010" },
+ "&Iacute;": { "codepoints": [205], "characters": "\u00CD" },
+ "&Iacute": { "codepoints": [205], "characters": "\u00CD" },
+ "&iacute;": { "codepoints": [237], "characters": "\u00ED" },
+ "&iacute": { "codepoints": [237], "characters": "\u00ED" },
+ "&ic;": { "codepoints": [8291], "characters": "\u2063" },
+ "&Icirc;": { "codepoints": [206], "characters": "\u00CE" },
+ "&Icirc": { "codepoints": [206], "characters": "\u00CE" },
+ "&icirc;": { "codepoints": [238], "characters": "\u00EE" },
+ "&icirc": { "codepoints": [238], "characters": "\u00EE" },
+ "&Icy;": { "codepoints": [1048], "characters": "\u0418" },
+ "&icy;": { "codepoints": [1080], "characters": "\u0438" },
+ "&Idot;": { "codepoints": [304], "characters": "\u0130" },
+ "&IEcy;": { "codepoints": [1045], "characters": "\u0415" },
+ "&iecy;": { "codepoints": [1077], "characters": "\u0435" },
+ "&iexcl;": { "codepoints": [161], "characters": "\u00A1" },
+ "&iexcl": { "codepoints": [161], "characters": "\u00A1" },
+ "&iff;": { "codepoints": [8660], "characters": "\u21D4" },
+ "&ifr;": { "codepoints": [120102], "characters": "\uD835\uDD26" },
+ "&Ifr;": { "codepoints": [8465], "characters": "\u2111" },
+ "&Igrave;": { "codepoints": [204], "characters": "\u00CC" },
+ "&Igrave": { "codepoints": [204], "characters": "\u00CC" },
+ "&igrave;": { "codepoints": [236], "characters": "\u00EC" },
+ "&igrave": { "codepoints": [236], "characters": "\u00EC" },
+ "&ii;": { "codepoints": [8520], "characters": "\u2148" },
+ "&iiiint;": { "codepoints": [10764], "characters": "\u2A0C" },
+ "&iiint;": { "codepoints": [8749], "characters": "\u222D" },
+ "&iinfin;": { "codepoints": [10716], "characters": "\u29DC" },
+ "&iiota;": { "codepoints": [8489], "characters": "\u2129" },
+ "&IJlig;": { "codepoints": [306], "characters": "\u0132" },
+ "&ijlig;": { "codepoints": [307], "characters": "\u0133" },
+ "&Imacr;": { "codepoints": [298], "characters": "\u012A" },
+ "&imacr;": { "codepoints": [299], "characters": "\u012B" },
+ "&image;": { "codepoints": [8465], "characters": "\u2111" },
+ "&ImaginaryI;": { "codepoints": [8520], "characters": "\u2148" },
+ "&imagline;": { "codepoints": [8464], "characters": "\u2110" },
+ "&imagpart;": { "codepoints": [8465], "characters": "\u2111" },
+ "&imath;": { "codepoints": [305], "characters": "\u0131" },
+ "&Im;": { "codepoints": [8465], "characters": "\u2111" },
+ "&imof;": { "codepoints": [8887], "characters": "\u22B7" },
+ "&imped;": { "codepoints": [437], "characters": "\u01B5" },
+ "&Implies;": { "codepoints": [8658], "characters": "\u21D2" },
+ "&incare;": { "codepoints": [8453], "characters": "\u2105" },
+ "&in;": { "codepoints": [8712], "characters": "\u2208" },
+ "&infin;": { "codepoints": [8734], "characters": "\u221E" },
+ "&infintie;": { "codepoints": [10717], "characters": "\u29DD" },
+ "&inodot;": { "codepoints": [305], "characters": "\u0131" },
+ "&intcal;": { "codepoints": [8890], "characters": "\u22BA" },
+ "&int;": { "codepoints": [8747], "characters": "\u222B" },
+ "&Int;": { "codepoints": [8748], "characters": "\u222C" },
+ "&integers;": { "codepoints": [8484], "characters": "\u2124" },
+ "&Integral;": { "codepoints": [8747], "characters": "\u222B" },
+ "&intercal;": { "codepoints": [8890], "characters": "\u22BA" },
+ "&Intersection;": { "codepoints": [8898], "characters": "\u22C2" },
+ "&intlarhk;": { "codepoints": [10775], "characters": "\u2A17" },
+ "&intprod;": { "codepoints": [10812], "characters": "\u2A3C" },
+ "&InvisibleComma;": { "codepoints": [8291], "characters": "\u2063" },
+ "&InvisibleTimes;": { "codepoints": [8290], "characters": "\u2062" },
+ "&IOcy;": { "codepoints": [1025], "characters": "\u0401" },
+ "&iocy;": { "codepoints": [1105], "characters": "\u0451" },
+ "&Iogon;": { "codepoints": [302], "characters": "\u012E" },
+ "&iogon;": { "codepoints": [303], "characters": "\u012F" },
+ "&Iopf;": { "codepoints": [120128], "characters": "\uD835\uDD40" },
+ "&iopf;": { "codepoints": [120154], "characters": "\uD835\uDD5A" },
+ "&Iota;": { "codepoints": [921], "characters": "\u0399" },
+ "&iota;": { "codepoints": [953], "characters": "\u03B9" },
+ "&iprod;": { "codepoints": [10812], "characters": "\u2A3C" },
+ "&iquest;": { "codepoints": [191], "characters": "\u00BF" },
+ "&iquest": { "codepoints": [191], "characters": "\u00BF" },
+ "&iscr;": { "codepoints": [119998], "characters": "\uD835\uDCBE" },
+ "&Iscr;": { "codepoints": [8464], "characters": "\u2110" },
+ "&isin;": { "codepoints": [8712], "characters": "\u2208" },
+ "&isindot;": { "codepoints": [8949], "characters": "\u22F5" },
+ "&isinE;": { "codepoints": [8953], "characters": "\u22F9" },
+ "&isins;": { "codepoints": [8948], "characters": "\u22F4" },
+ "&isinsv;": { "codepoints": [8947], "characters": "\u22F3" },
+ "&isinv;": { "codepoints": [8712], "characters": "\u2208" },
+ "&it;": { "codepoints": [8290], "characters": "\u2062" },
+ "&Itilde;": { "codepoints": [296], "characters": "\u0128" },
+ "&itilde;": { "codepoints": [297], "characters": "\u0129" },
+ "&Iukcy;": { "codepoints": [1030], "characters": "\u0406" },
+ "&iukcy;": { "codepoints": [1110], "characters": "\u0456" },
+ "&Iuml;": { "codepoints": [207], "characters": "\u00CF" },
+ "&Iuml": { "codepoints": [207], "characters": "\u00CF" },
+ "&iuml;": { "codepoints": [239], "characters": "\u00EF" },
+ "&iuml": { "codepoints": [239], "characters": "\u00EF" },
+ "&Jcirc;": { "codepoints": [308], "characters": "\u0134" },
+ "&jcirc;": { "codepoints": [309], "characters": "\u0135" },
+ "&Jcy;": { "codepoints": [1049], "characters": "\u0419" },
+ "&jcy;": { "codepoints": [1081], "characters": "\u0439" },
+ "&Jfr;": { "codepoints": [120077], "characters": "\uD835\uDD0D" },
+ "&jfr;": { "codepoints": [120103], "characters": "\uD835\uDD27" },
+ "&jmath;": { "codepoints": [567], "characters": "\u0237" },
+ "&Jopf;": { "codepoints": [120129], "characters": "\uD835\uDD41" },
+ "&jopf;": { "codepoints": [120155], "characters": "\uD835\uDD5B" },
+ "&Jscr;": { "codepoints": [119973], "characters": "\uD835\uDCA5" },
+ "&jscr;": { "codepoints": [119999], "characters": "\uD835\uDCBF" },
+ "&Jsercy;": { "codepoints": [1032], "characters": "\u0408" },
+ "&jsercy;": { "codepoints": [1112], "characters": "\u0458" },
+ "&Jukcy;": { "codepoints": [1028], "characters": "\u0404" },
+ "&jukcy;": { "codepoints": [1108], "characters": "\u0454" },
+ "&Kappa;": { "codepoints": [922], "characters": "\u039A" },
+ "&kappa;": { "codepoints": [954], "characters": "\u03BA" },
+ "&kappav;": { "codepoints": [1008], "characters": "\u03F0" },
+ "&Kcedil;": { "codepoints": [310], "characters": "\u0136" },
+ "&kcedil;": { "codepoints": [311], "characters": "\u0137" },
+ "&Kcy;": { "codepoints": [1050], "characters": "\u041A" },
+ "&kcy;": { "codepoints": [1082], "characters": "\u043A" },
+ "&Kfr;": { "codepoints": [120078], "characters": "\uD835\uDD0E" },
+ "&kfr;": { "codepoints": [120104], "characters": "\uD835\uDD28" },
+ "&kgreen;": { "codepoints": [312], "characters": "\u0138" },
+ "&KHcy;": { "codepoints": [1061], "characters": "\u0425" },
+ "&khcy;": { "codepoints": [1093], "characters": "\u0445" },
+ "&KJcy;": { "codepoints": [1036], "characters": "\u040C" },
+ "&kjcy;": { "codepoints": [1116], "characters": "\u045C" },
+ "&Kopf;": { "codepoints": [120130], "characters": "\uD835\uDD42" },
+ "&kopf;": { "codepoints": [120156], "characters": "\uD835\uDD5C" },
+ "&Kscr;": { "codepoints": [119974], "characters": "\uD835\uDCA6" },
+ "&kscr;": { "codepoints": [120000], "characters": "\uD835\uDCC0" },
+ "&lAarr;": { "codepoints": [8666], "characters": "\u21DA" },
+ "&Lacute;": { "codepoints": [313], "characters": "\u0139" },
+ "&lacute;": { "codepoints": [314], "characters": "\u013A" },
+ "&laemptyv;": { "codepoints": [10676], "characters": "\u29B4" },
+ "&lagran;": { "codepoints": [8466], "characters": "\u2112" },
+ "&Lambda;": { "codepoints": [923], "characters": "\u039B" },
+ "&lambda;": { "codepoints": [955], "characters": "\u03BB" },
+ "&lang;": { "codepoints": [10216], "characters": "\u27E8" },
+ "&Lang;": { "codepoints": [10218], "characters": "\u27EA" },
+ "&langd;": { "codepoints": [10641], "characters": "\u2991" },
+ "&langle;": { "codepoints": [10216], "characters": "\u27E8" },
+ "&lap;": { "codepoints": [10885], "characters": "\u2A85" },
+ "&Laplacetrf;": { "codepoints": [8466], "characters": "\u2112" },
+ "&laquo;": { "codepoints": [171], "characters": "\u00AB" },
+ "&laquo": { "codepoints": [171], "characters": "\u00AB" },
+ "&larrb;": { "codepoints": [8676], "characters": "\u21E4" },
+ "&larrbfs;": { "codepoints": [10527], "characters": "\u291F" },
+ "&larr;": { "codepoints": [8592], "characters": "\u2190" },
+ "&Larr;": { "codepoints": [8606], "characters": "\u219E" },
+ "&lArr;": { "codepoints": [8656], "characters": "\u21D0" },
+ "&larrfs;": { "codepoints": [10525], "characters": "\u291D" },
+ "&larrhk;": { "codepoints": [8617], "characters": "\u21A9" },
+ "&larrlp;": { "codepoints": [8619], "characters": "\u21AB" },
+ "&larrpl;": { "codepoints": [10553], "characters": "\u2939" },
+ "&larrsim;": { "codepoints": [10611], "characters": "\u2973" },
+ "&larrtl;": { "codepoints": [8610], "characters": "\u21A2" },
+ "&latail;": { "codepoints": [10521], "characters": "\u2919" },
+ "&lAtail;": { "codepoints": [10523], "characters": "\u291B" },
+ "&lat;": { "codepoints": [10923], "characters": "\u2AAB" },
+ "&late;": { "codepoints": [10925], "characters": "\u2AAD" },
+ "&lates;": { "codepoints": [10925, 65024], "characters": "\u2AAD\uFE00" },
+ "&lbarr;": { "codepoints": [10508], "characters": "\u290C" },
+ "&lBarr;": { "codepoints": [10510], "characters": "\u290E" },
+ "&lbbrk;": { "codepoints": [10098], "characters": "\u2772" },
+ "&lbrace;": { "codepoints": [123], "characters": "\u007B" },
+ "&lbrack;": { "codepoints": [91], "characters": "\u005B" },
+ "&lbrke;": { "codepoints": [10635], "characters": "\u298B" },
+ "&lbrksld;": { "codepoints": [10639], "characters": "\u298F" },
+ "&lbrkslu;": { "codepoints": [10637], "characters": "\u298D" },
+ "&Lcaron;": { "codepoints": [317], "characters": "\u013D" },
+ "&lcaron;": { "codepoints": [318], "characters": "\u013E" },
+ "&Lcedil;": { "codepoints": [315], "characters": "\u013B" },
+ "&lcedil;": { "codepoints": [316], "characters": "\u013C" },
+ "&lceil;": { "codepoints": [8968], "characters": "\u2308" },
+ "&lcub;": { "codepoints": [123], "characters": "\u007B" },
+ "&Lcy;": { "codepoints": [1051], "characters": "\u041B" },
+ "&lcy;": { "codepoints": [1083], "characters": "\u043B" },
+ "&ldca;": { "codepoints": [10550], "characters": "\u2936" },
+ "&ldquo;": { "codepoints": [8220], "characters": "\u201C" },
+ "&ldquor;": { "codepoints": [8222], "characters": "\u201E" },
+ "&ldrdhar;": { "codepoints": [10599], "characters": "\u2967" },
+ "&ldrushar;": { "codepoints": [10571], "characters": "\u294B" },
+ "&ldsh;": { "codepoints": [8626], "characters": "\u21B2" },
+ "&le;": { "codepoints": [8804], "characters": "\u2264" },
+ "&lE;": { "codepoints": [8806], "characters": "\u2266" },
+ "&LeftAngleBracket;": { "codepoints": [10216], "characters": "\u27E8" },
+ "&LeftArrowBar;": { "codepoints": [8676], "characters": "\u21E4" },
+ "&leftarrow;": { "codepoints": [8592], "characters": "\u2190" },
+ "&LeftArrow;": { "codepoints": [8592], "characters": "\u2190" },
+ "&Leftarrow;": { "codepoints": [8656], "characters": "\u21D0" },
+ "&LeftArrowRightArrow;": { "codepoints": [8646], "characters": "\u21C6" },
+ "&leftarrowtail;": { "codepoints": [8610], "characters": "\u21A2" },
+ "&LeftCeiling;": { "codepoints": [8968], "characters": "\u2308" },
+ "&LeftDoubleBracket;": { "codepoints": [10214], "characters": "\u27E6" },
+ "&LeftDownTeeVector;": { "codepoints": [10593], "characters": "\u2961" },
+ "&LeftDownVectorBar;": { "codepoints": [10585], "characters": "\u2959" },
+ "&LeftDownVector;": { "codepoints": [8643], "characters": "\u21C3" },
+ "&LeftFloor;": { "codepoints": [8970], "characters": "\u230A" },
+ "&leftharpoondown;": { "codepoints": [8637], "characters": "\u21BD" },
+ "&leftharpoonup;": { "codepoints": [8636], "characters": "\u21BC" },
+ "&leftleftarrows;": { "codepoints": [8647], "characters": "\u21C7" },
+ "&leftrightarrow;": { "codepoints": [8596], "characters": "\u2194" },
+ "&LeftRightArrow;": { "codepoints": [8596], "characters": "\u2194" },
+ "&Leftrightarrow;": { "codepoints": [8660], "characters": "\u21D4" },
+ "&leftrightarrows;": { "codepoints": [8646], "characters": "\u21C6" },
+ "&leftrightharpoons;": { "codepoints": [8651], "characters": "\u21CB" },
+ "&leftrightsquigarrow;": { "codepoints": [8621], "characters": "\u21AD" },
+ "&LeftRightVector;": { "codepoints": [10574], "characters": "\u294E" },
+ "&LeftTeeArrow;": { "codepoints": [8612], "characters": "\u21A4" },
+ "&LeftTee;": { "codepoints": [8867], "characters": "\u22A3" },
+ "&LeftTeeVector;": { "codepoints": [10586], "characters": "\u295A" },
+ "&leftthreetimes;": { "codepoints": [8907], "characters": "\u22CB" },
+ "&LeftTriangleBar;": { "codepoints": [10703], "characters": "\u29CF" },
+ "&LeftTriangle;": { "codepoints": [8882], "characters": "\u22B2" },
+ "&LeftTriangleEqual;": { "codepoints": [8884], "characters": "\u22B4" },
+ "&LeftUpDownVector;": { "codepoints": [10577], "characters": "\u2951" },
+ "&LeftUpTeeVector;": { "codepoints": [10592], "characters": "\u2960" },
+ "&LeftUpVectorBar;": { "codepoints": [10584], "characters": "\u2958" },
+ "&LeftUpVector;": { "codepoints": [8639], "characters": "\u21BF" },
+ "&LeftVectorBar;": { "codepoints": [10578], "characters": "\u2952" },
+ "&LeftVector;": { "codepoints": [8636], "characters": "\u21BC" },
+ "&lEg;": { "codepoints": [10891], "characters": "\u2A8B" },
+ "&leg;": { "codepoints": [8922], "characters": "\u22DA" },
+ "&leq;": { "codepoints": [8804], "characters": "\u2264" },
+ "&leqq;": { "codepoints": [8806], "characters": "\u2266" },
+ "&leqslant;": { "codepoints": [10877], "characters": "\u2A7D" },
+ "&lescc;": { "codepoints": [10920], "characters": "\u2AA8" },
+ "&les;": { "codepoints": [10877], "characters": "\u2A7D" },
+ "&lesdot;": { "codepoints": [10879], "characters": "\u2A7F" },
+ "&lesdoto;": { "codepoints": [10881], "characters": "\u2A81" },
+ "&lesdotor;": { "codepoints": [10883], "characters": "\u2A83" },
+ "&lesg;": { "codepoints": [8922, 65024], "characters": "\u22DA\uFE00" },
+ "&lesges;": { "codepoints": [10899], "characters": "\u2A93" },
+ "&lessapprox;": { "codepoints": [10885], "characters": "\u2A85" },
+ "&lessdot;": { "codepoints": [8918], "characters": "\u22D6" },
+ "&lesseqgtr;": { "codepoints": [8922], "characters": "\u22DA" },
+ "&lesseqqgtr;": { "codepoints": [10891], "characters": "\u2A8B" },
+ "&LessEqualGreater;": { "codepoints": [8922], "characters": "\u22DA" },
+ "&LessFullEqual;": { "codepoints": [8806], "characters": "\u2266" },
+ "&LessGreater;": { "codepoints": [8822], "characters": "\u2276" },
+ "&lessgtr;": { "codepoints": [8822], "characters": "\u2276" },
+ "&LessLess;": { "codepoints": [10913], "characters": "\u2AA1" },
+ "&lesssim;": { "codepoints": [8818], "characters": "\u2272" },
+ "&LessSlantEqual;": { "codepoints": [10877], "characters": "\u2A7D" },
+ "&LessTilde;": { "codepoints": [8818], "characters": "\u2272" },
+ "&lfisht;": { "codepoints": [10620], "characters": "\u297C" },
+ "&lfloor;": { "codepoints": [8970], "characters": "\u230A" },
+ "&Lfr;": { "codepoints": [120079], "characters": "\uD835\uDD0F" },
+ "&lfr;": { "codepoints": [120105], "characters": "\uD835\uDD29" },
+ "&lg;": { "codepoints": [8822], "characters": "\u2276" },
+ "&lgE;": { "codepoints": [10897], "characters": "\u2A91" },
+ "&lHar;": { "codepoints": [10594], "characters": "\u2962" },
+ "&lhard;": { "codepoints": [8637], "characters": "\u21BD" },
+ "&lharu;": { "codepoints": [8636], "characters": "\u21BC" },
+ "&lharul;": { "codepoints": [10602], "characters": "\u296A" },
+ "&lhblk;": { "codepoints": [9604], "characters": "\u2584" },
+ "&LJcy;": { "codepoints": [1033], "characters": "\u0409" },
+ "&ljcy;": { "codepoints": [1113], "characters": "\u0459" },
+ "&llarr;": { "codepoints": [8647], "characters": "\u21C7" },
+ "&ll;": { "codepoints": [8810], "characters": "\u226A" },
+ "&Ll;": { "codepoints": [8920], "characters": "\u22D8" },
+ "&llcorner;": { "codepoints": [8990], "characters": "\u231E" },
+ "&Lleftarrow;": { "codepoints": [8666], "characters": "\u21DA" },
+ "&llhard;": { "codepoints": [10603], "characters": "\u296B" },
+ "&lltri;": { "codepoints": [9722], "characters": "\u25FA" },
+ "&Lmidot;": { "codepoints": [319], "characters": "\u013F" },
+ "&lmidot;": { "codepoints": [320], "characters": "\u0140" },
+ "&lmoustache;": { "codepoints": [9136], "characters": "\u23B0" },
+ "&lmoust;": { "codepoints": [9136], "characters": "\u23B0" },
+ "&lnap;": { "codepoints": [10889], "characters": "\u2A89" },
+ "&lnapprox;": { "codepoints": [10889], "characters": "\u2A89" },
+ "&lne;": { "codepoints": [10887], "characters": "\u2A87" },
+ "&lnE;": { "codepoints": [8808], "characters": "\u2268" },
+ "&lneq;": { "codepoints": [10887], "characters": "\u2A87" },
+ "&lneqq;": { "codepoints": [8808], "characters": "\u2268" },
+ "&lnsim;": { "codepoints": [8934], "characters": "\u22E6" },
+ "&loang;": { "codepoints": [10220], "characters": "\u27EC" },
+ "&loarr;": { "codepoints": [8701], "characters": "\u21FD" },
+ "&lobrk;": { "codepoints": [10214], "characters": "\u27E6" },
+ "&longleftarrow;": { "codepoints": [10229], "characters": "\u27F5" },
+ "&LongLeftArrow;": { "codepoints": [10229], "characters": "\u27F5" },
+ "&Longleftarrow;": { "codepoints": [10232], "characters": "\u27F8" },
+ "&longleftrightarrow;": { "codepoints": [10231], "characters": "\u27F7" },
+ "&LongLeftRightArrow;": { "codepoints": [10231], "characters": "\u27F7" },
+ "&Longleftrightarrow;": { "codepoints": [10234], "characters": "\u27FA" },
+ "&longmapsto;": { "codepoints": [10236], "characters": "\u27FC" },
+ "&longrightarrow;": { "codepoints": [10230], "characters": "\u27F6" },
+ "&LongRightArrow;": { "codepoints": [10230], "characters": "\u27F6" },
+ "&Longrightarrow;": { "codepoints": [10233], "characters": "\u27F9" },
+ "&looparrowleft;": { "codepoints": [8619], "characters": "\u21AB" },
+ "&looparrowright;": { "codepoints": [8620], "characters": "\u21AC" },
+ "&lopar;": { "codepoints": [10629], "characters": "\u2985" },
+ "&Lopf;": { "codepoints": [120131], "characters": "\uD835\uDD43" },
+ "&lopf;": { "codepoints": [120157], "characters": "\uD835\uDD5D" },
+ "&loplus;": { "codepoints": [10797], "characters": "\u2A2D" },
+ "&lotimes;": { "codepoints": [10804], "characters": "\u2A34" },
+ "&lowast;": { "codepoints": [8727], "characters": "\u2217" },
+ "&lowbar;": { "codepoints": [95], "characters": "\u005F" },
+ "&LowerLeftArrow;": { "codepoints": [8601], "characters": "\u2199" },
+ "&LowerRightArrow;": { "codepoints": [8600], "characters": "\u2198" },
+ "&loz;": { "codepoints": [9674], "characters": "\u25CA" },
+ "&lozenge;": { "codepoints": [9674], "characters": "\u25CA" },
+ "&lozf;": { "codepoints": [10731], "characters": "\u29EB" },
+ "&lpar;": { "codepoints": [40], "characters": "\u0028" },
+ "&lparlt;": { "codepoints": [10643], "characters": "\u2993" },
+ "&lrarr;": { "codepoints": [8646], "characters": "\u21C6" },
+ "&lrcorner;": { "codepoints": [8991], "characters": "\u231F" },
+ "&lrhar;": { "codepoints": [8651], "characters": "\u21CB" },
+ "&lrhard;": { "codepoints": [10605], "characters": "\u296D" },
+ "&lrm;": { "codepoints": [8206], "characters": "\u200E" },
+ "&lrtri;": { "codepoints": [8895], "characters": "\u22BF" },
+ "&lsaquo;": { "codepoints": [8249], "characters": "\u2039" },
+ "&lscr;": { "codepoints": [120001], "characters": "\uD835\uDCC1" },
+ "&Lscr;": { "codepoints": [8466], "characters": "\u2112" },
+ "&lsh;": { "codepoints": [8624], "characters": "\u21B0" },
+ "&Lsh;": { "codepoints": [8624], "characters": "\u21B0" },
+ "&lsim;": { "codepoints": [8818], "characters": "\u2272" },
+ "&lsime;": { "codepoints": [10893], "characters": "\u2A8D" },
+ "&lsimg;": { "codepoints": [10895], "characters": "\u2A8F" },
+ "&lsqb;": { "codepoints": [91], "characters": "\u005B" },
+ "&lsquo;": { "codepoints": [8216], "characters": "\u2018" },
+ "&lsquor;": { "codepoints": [8218], "characters": "\u201A" },
+ "&Lstrok;": { "codepoints": [321], "characters": "\u0141" },
+ "&lstrok;": { "codepoints": [322], "characters": "\u0142" },
+ "&ltcc;": { "codepoints": [10918], "characters": "\u2AA6" },
+ "&ltcir;": { "codepoints": [10873], "characters": "\u2A79" },
+ "&lt;": { "codepoints": [60], "characters": "\u003C" },
+ "&lt": { "codepoints": [60], "characters": "\u003C" },
+ "&LT;": { "codepoints": [60], "characters": "\u003C" },
+ "&LT": { "codepoints": [60], "characters": "\u003C" },
+ "&Lt;": { "codepoints": [8810], "characters": "\u226A" },
+ "&ltdot;": { "codepoints": [8918], "characters": "\u22D6" },
+ "&lthree;": { "codepoints": [8907], "characters": "\u22CB" },
+ "&ltimes;": { "codepoints": [8905], "characters": "\u22C9" },
+ "&ltlarr;": { "codepoints": [10614], "characters": "\u2976" },
+ "&ltquest;": { "codepoints": [10875], "characters": "\u2A7B" },
+ "&ltri;": { "codepoints": [9667], "characters": "\u25C3" },
+ "&ltrie;": { "codepoints": [8884], "characters": "\u22B4" },
+ "&ltrif;": { "codepoints": [9666], "characters": "\u25C2" },
+ "&ltrPar;": { "codepoints": [10646], "characters": "\u2996" },
+ "&lurdshar;": { "codepoints": [10570], "characters": "\u294A" },
+ "&luruhar;": { "codepoints": [10598], "characters": "\u2966" },
+ "&lvertneqq;": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" },
+ "&lvnE;": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" },
+ "&macr;": { "codepoints": [175], "characters": "\u00AF" },
+ "&macr": { "codepoints": [175], "characters": "\u00AF" },
+ "&male;": { "codepoints": [9794], "characters": "\u2642" },
+ "&malt;": { "codepoints": [10016], "characters": "\u2720" },
+ "&maltese;": { "codepoints": [10016], "characters": "\u2720" },
+ "&Map;": { "codepoints": [10501], "characters": "\u2905" },
+ "&map;": { "codepoints": [8614], "characters": "\u21A6" },
+ "&mapsto;": { "codepoints": [8614], "characters": "\u21A6" },
+ "&mapstodown;": { "codepoints": [8615], "characters": "\u21A7" },
+ "&mapstoleft;": { "codepoints": [8612], "characters": "\u21A4" },
+ "&mapstoup;": { "codepoints": [8613], "characters": "\u21A5" },
+ "&marker;": { "codepoints": [9646], "characters": "\u25AE" },
+ "&mcomma;": { "codepoints": [10793], "characters": "\u2A29" },
+ "&Mcy;": { "codepoints": [1052], "characters": "\u041C" },
+ "&mcy;": { "codepoints": [1084], "characters": "\u043C" },
+ "&mdash;": { "codepoints": [8212], "characters": "\u2014" },
+ "&mDDot;": { "codepoints": [8762], "characters": "\u223A" },
+ "&measuredangle;": { "codepoints": [8737], "characters": "\u2221" },
+ "&MediumSpace;": { "codepoints": [8287], "characters": "\u205F" },
+ "&Mellintrf;": { "codepoints": [8499], "characters": "\u2133" },
+ "&Mfr;": { "codepoints": [120080], "characters": "\uD835\uDD10" },
+ "&mfr;": { "codepoints": [120106], "characters": "\uD835\uDD2A" },
+ "&mho;": { "codepoints": [8487], "characters": "\u2127" },
+ "&micro;": { "codepoints": [181], "characters": "\u00B5" },
+ "&micro": { "codepoints": [181], "characters": "\u00B5" },
+ "&midast;": { "codepoints": [42], "characters": "\u002A" },
+ "&midcir;": { "codepoints": [10992], "characters": "\u2AF0" },
+ "&mid;": { "codepoints": [8739], "characters": "\u2223" },
+ "&middot;": { "codepoints": [183], "characters": "\u00B7" },
+ "&middot": { "codepoints": [183], "characters": "\u00B7" },
+ "&minusb;": { "codepoints": [8863], "characters": "\u229F" },
+ "&minus;": { "codepoints": [8722], "characters": "\u2212" },
+ "&minusd;": { "codepoints": [8760], "characters": "\u2238" },
+ "&minusdu;": { "codepoints": [10794], "characters": "\u2A2A" },
+ "&MinusPlus;": { "codepoints": [8723], "characters": "\u2213" },
+ "&mlcp;": { "codepoints": [10971], "characters": "\u2ADB" },
+ "&mldr;": { "codepoints": [8230], "characters": "\u2026" },
+ "&mnplus;": { "codepoints": [8723], "characters": "\u2213" },
+ "&models;": { "codepoints": [8871], "characters": "\u22A7" },
+ "&Mopf;": { "codepoints": [120132], "characters": "\uD835\uDD44" },
+ "&mopf;": { "codepoints": [120158], "characters": "\uD835\uDD5E" },
+ "&mp;": { "codepoints": [8723], "characters": "\u2213" },
+ "&mscr;": { "codepoints": [120002], "characters": "\uD835\uDCC2" },
+ "&Mscr;": { "codepoints": [8499], "characters": "\u2133" },
+ "&mstpos;": { "codepoints": [8766], "characters": "\u223E" },
+ "&Mu;": { "codepoints": [924], "characters": "\u039C" },
+ "&mu;": { "codepoints": [956], "characters": "\u03BC" },
+ "&multimap;": { "codepoints": [8888], "characters": "\u22B8" },
+ "&mumap;": { "codepoints": [8888], "characters": "\u22B8" },
+ "&nabla;": { "codepoints": [8711], "characters": "\u2207" },
+ "&Nacute;": { "codepoints": [323], "characters": "\u0143" },
+ "&nacute;": { "codepoints": [324], "characters": "\u0144" },
+ "&nang;": { "codepoints": [8736, 8402], "characters": "\u2220\u20D2" },
+ "&nap;": { "codepoints": [8777], "characters": "\u2249" },
+ "&napE;": { "codepoints": [10864, 824], "characters": "\u2A70\u0338" },
+ "&napid;": { "codepoints": [8779, 824], "characters": "\u224B\u0338" },
+ "&napos;": { "codepoints": [329], "characters": "\u0149" },
+ "&napprox;": { "codepoints": [8777], "characters": "\u2249" },
+ "&natural;": { "codepoints": [9838], "characters": "\u266E" },
+ "&naturals;": { "codepoints": [8469], "characters": "\u2115" },
+ "&natur;": { "codepoints": [9838], "characters": "\u266E" },
+ "&nbsp;": { "codepoints": [160], "characters": "\u00A0" },
+ "&nbsp": { "codepoints": [160], "characters": "\u00A0" },
+ "&nbump;": { "codepoints": [8782, 824], "characters": "\u224E\u0338" },
+ "&nbumpe;": { "codepoints": [8783, 824], "characters": "\u224F\u0338" },
+ "&ncap;": { "codepoints": [10819], "characters": "\u2A43" },
+ "&Ncaron;": { "codepoints": [327], "characters": "\u0147" },
+ "&ncaron;": { "codepoints": [328], "characters": "\u0148" },
+ "&Ncedil;": { "codepoints": [325], "characters": "\u0145" },
+ "&ncedil;": { "codepoints": [326], "characters": "\u0146" },
+ "&ncong;": { "codepoints": [8775], "characters": "\u2247" },
+ "&ncongdot;": { "codepoints": [10861, 824], "characters": "\u2A6D\u0338" },
+ "&ncup;": { "codepoints": [10818], "characters": "\u2A42" },
+ "&Ncy;": { "codepoints": [1053], "characters": "\u041D" },
+ "&ncy;": { "codepoints": [1085], "characters": "\u043D" },
+ "&ndash;": { "codepoints": [8211], "characters": "\u2013" },
+ "&nearhk;": { "codepoints": [10532], "characters": "\u2924" },
+ "&nearr;": { "codepoints": [8599], "characters": "\u2197" },
+ "&neArr;": { "codepoints": [8663], "characters": "\u21D7" },
+ "&nearrow;": { "codepoints": [8599], "characters": "\u2197" },
+ "&ne;": { "codepoints": [8800], "characters": "\u2260" },
+ "&nedot;": { "codepoints": [8784, 824], "characters": "\u2250\u0338" },
+ "&NegativeMediumSpace;": { "codepoints": [8203], "characters": "\u200B" },
+ "&NegativeThickSpace;": { "codepoints": [8203], "characters": "\u200B" },
+ "&NegativeThinSpace;": { "codepoints": [8203], "characters": "\u200B" },
+ "&NegativeVeryThinSpace;": { "codepoints": [8203], "characters": "\u200B" },
+ "&nequiv;": { "codepoints": [8802], "characters": "\u2262" },
+ "&nesear;": { "codepoints": [10536], "characters": "\u2928" },
+ "&nesim;": { "codepoints": [8770, 824], "characters": "\u2242\u0338" },
+ "&NestedGreaterGreater;": { "codepoints": [8811], "characters": "\u226B" },
+ "&NestedLessLess;": { "codepoints": [8810], "characters": "\u226A" },
+ "&NewLine;": { "codepoints": [10], "characters": "\u000A" },
+ "&nexist;": { "codepoints": [8708], "characters": "\u2204" },
+ "&nexists;": { "codepoints": [8708], "characters": "\u2204" },
+ "&Nfr;": { "codepoints": [120081], "characters": "\uD835\uDD11" },
+ "&nfr;": { "codepoints": [120107], "characters": "\uD835\uDD2B" },
+ "&ngE;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+ "&nge;": { "codepoints": [8817], "characters": "\u2271" },
+ "&ngeq;": { "codepoints": [8817], "characters": "\u2271" },
+ "&ngeqq;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+ "&ngeqslant;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+ "&nges;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+ "&nGg;": { "codepoints": [8921, 824], "characters": "\u22D9\u0338" },
+ "&ngsim;": { "codepoints": [8821], "characters": "\u2275" },
+ "&nGt;": { "codepoints": [8811, 8402], "characters": "\u226B\u20D2" },
+ "&ngt;": { "codepoints": [8815], "characters": "\u226F" },
+ "&ngtr;": { "codepoints": [8815], "characters": "\u226F" },
+ "&nGtv;": { "codepoints": [8811, 824], "characters": "\u226B\u0338" },
+ "&nharr;": { "codepoints": [8622], "characters": "\u21AE" },
+ "&nhArr;": { "codepoints": [8654], "characters": "\u21CE" },
+ "&nhpar;": { "codepoints": [10994], "characters": "\u2AF2" },
+ "&ni;": { "codepoints": [8715], "characters": "\u220B" },
+ "&nis;": { "codepoints": [8956], "characters": "\u22FC" },
+ "&nisd;": { "codepoints": [8954], "characters": "\u22FA" },
+ "&niv;": { "codepoints": [8715], "characters": "\u220B" },
+ "&NJcy;": { "codepoints": [1034], "characters": "\u040A" },
+ "&njcy;": { "codepoints": [1114], "characters": "\u045A" },
+ "&nlarr;": { "codepoints": [8602], "characters": "\u219A" },
+ "&nlArr;": { "codepoints": [8653], "characters": "\u21CD" },
+ "&nldr;": { "codepoints": [8229], "characters": "\u2025" },
+ "&nlE;": { "codepoints": [8806, 824], "characters": "\u2266\u0338" },
+ "&nle;": { "codepoints": [8816], "characters": "\u2270" },
+ "&nleftarrow;": { "codepoints": [8602], "characters": "\u219A" },
+ "&nLeftarrow;": { "codepoints": [8653], "characters": "\u21CD" },
+ "&nleftrightarrow;": { "codepoints": [8622], "characters": "\u21AE" },
+ "&nLeftrightarrow;": { "codepoints": [8654], "characters": "\u21CE" },
+ "&nleq;": { "codepoints": [8816], "characters": "\u2270" },
+ "&nleqq;": { "codepoints": [8806, 824], "characters": "\u2266\u0338" },
+ "&nleqslant;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+ "&nles;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+ "&nless;": { "codepoints": [8814], "characters": "\u226E" },
+ "&nLl;": { "codepoints": [8920, 824], "characters": "\u22D8\u0338" },
+ "&nlsim;": { "codepoints": [8820], "characters": "\u2274" },
+ "&nLt;": { "codepoints": [8810, 8402], "characters": "\u226A\u20D2" },
+ "&nlt;": { "codepoints": [8814], "characters": "\u226E" },
+ "&nltri;": { "codepoints": [8938], "characters": "\u22EA" },
+ "&nltrie;": { "codepoints": [8940], "characters": "\u22EC" },
+ "&nLtv;": { "codepoints": [8810, 824], "characters": "\u226A\u0338" },
+ "&nmid;": { "codepoints": [8740], "characters": "\u2224" },
+ "&NoBreak;": { "codepoints": [8288], "characters": "\u2060" },
+ "&NonBreakingSpace;": { "codepoints": [160], "characters": "\u00A0" },
+ "&nopf;": { "codepoints": [120159], "characters": "\uD835\uDD5F" },
+ "&Nopf;": { "codepoints": [8469], "characters": "\u2115" },
+ "&Not;": { "codepoints": [10988], "characters": "\u2AEC" },
+ "&not;": { "codepoints": [172], "characters": "\u00AC" },
+ "&not": { "codepoints": [172], "characters": "\u00AC" },
+ "&NotCongruent;": { "codepoints": [8802], "characters": "\u2262" },
+ "&NotCupCap;": { "codepoints": [8813], "characters": "\u226D" },
+ "&NotDoubleVerticalBar;": { "codepoints": [8742], "characters": "\u2226" },
+ "&NotElement;": { "codepoints": [8713], "characters": "\u2209" },
+ "&NotEqual;": { "codepoints": [8800], "characters": "\u2260" },
+ "&NotEqualTilde;": { "codepoints": [8770, 824], "characters": "\u2242\u0338" },
+ "&NotExists;": { "codepoints": [8708], "characters": "\u2204" },
+ "&NotGreater;": { "codepoints": [8815], "characters": "\u226F" },
+ "&NotGreaterEqual;": { "codepoints": [8817], "characters": "\u2271" },
+ "&NotGreaterFullEqual;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+ "&NotGreaterGreater;": { "codepoints": [8811, 824], "characters": "\u226B\u0338" },
+ "&NotGreaterLess;": { "codepoints": [8825], "characters": "\u2279" },
+ "&NotGreaterSlantEqual;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+ "&NotGreaterTilde;": { "codepoints": [8821], "characters": "\u2275" },
+ "&NotHumpDownHump;": { "codepoints": [8782, 824], "characters": "\u224E\u0338" },
+ "&NotHumpEqual;": { "codepoints": [8783, 824], "characters": "\u224F\u0338" },
+ "&notin;": { "codepoints": [8713], "characters": "\u2209" },
+ "&notindot;": { "codepoints": [8949, 824], "characters": "\u22F5\u0338" },
+ "&notinE;": { "codepoints": [8953, 824], "characters": "\u22F9\u0338" },
+ "&notinva;": { "codepoints": [8713], "characters": "\u2209" },
+ "&notinvb;": { "codepoints": [8951], "characters": "\u22F7" },
+ "&notinvc;": { "codepoints": [8950], "characters": "\u22F6" },
+ "&NotLeftTriangleBar;": { "codepoints": [10703, 824], "characters": "\u29CF\u0338" },
+ "&NotLeftTriangle;": { "codepoints": [8938], "characters": "\u22EA" },
+ "&NotLeftTriangleEqual;": { "codepoints": [8940], "characters": "\u22EC" },
+ "&NotLess;": { "codepoints": [8814], "characters": "\u226E" },
+ "&NotLessEqual;": { "codepoints": [8816], "characters": "\u2270" },
+ "&NotLessGreater;": { "codepoints": [8824], "characters": "\u2278" },
+ "&NotLessLess;": { "codepoints": [8810, 824], "characters": "\u226A\u0338" },
+ "&NotLessSlantEqual;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+ "&NotLessTilde;": { "codepoints": [8820], "characters": "\u2274" },
+ "&NotNestedGreaterGreater;": { "codepoints": [10914, 824], "characters": "\u2AA2\u0338" },
+ "&NotNestedLessLess;": { "codepoints": [10913, 824], "characters": "\u2AA1\u0338" },
+ "&notni;": { "codepoints": [8716], "characters": "\u220C" },
+ "&notniva;": { "codepoints": [8716], "characters": "\u220C" },
+ "&notnivb;": { "codepoints": [8958], "characters": "\u22FE" },
+ "&notnivc;": { "codepoints": [8957], "characters": "\u22FD" },
+ "&NotPrecedes;": { "codepoints": [8832], "characters": "\u2280" },
+ "&NotPrecedesEqual;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+ "&NotPrecedesSlantEqual;": { "codepoints": [8928], "characters": "\u22E0" },
+ "&NotReverseElement;": { "codepoints": [8716], "characters": "\u220C" },
+ "&NotRightTriangleBar;": { "codepoints": [10704, 824], "characters": "\u29D0\u0338" },
+ "&NotRightTriangle;": { "codepoints": [8939], "characters": "\u22EB" },
+ "&NotRightTriangleEqual;": { "codepoints": [8941], "characters": "\u22ED" },
+ "&NotSquareSubset;": { "codepoints": [8847, 824], "characters": "\u228F\u0338" },
+ "&NotSquareSubsetEqual;": { "codepoints": [8930], "characters": "\u22E2" },
+ "&NotSquareSuperset;": { "codepoints": [8848, 824], "characters": "\u2290\u0338" },
+ "&NotSquareSupersetEqual;": { "codepoints": [8931], "characters": "\u22E3" },
+ "&NotSubset;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+ "&NotSubsetEqual;": { "codepoints": [8840], "characters": "\u2288" },
+ "&NotSucceeds;": { "codepoints": [8833], "characters": "\u2281" },
+ "&NotSucceedsEqual;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+ "&NotSucceedsSlantEqual;": { "codepoints": [8929], "characters": "\u22E1" },
+ "&NotSucceedsTilde;": { "codepoints": [8831, 824], "characters": "\u227F\u0338" },
+ "&NotSuperset;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+ "&NotSupersetEqual;": { "codepoints": [8841], "characters": "\u2289" },
+ "&NotTilde;": { "codepoints": [8769], "characters": "\u2241" },
+ "&NotTildeEqual;": { "codepoints": [8772], "characters": "\u2244" },
+ "&NotTildeFullEqual;": { "codepoints": [8775], "characters": "\u2247" },
+ "&NotTildeTilde;": { "codepoints": [8777], "characters": "\u2249" },
+ "&NotVerticalBar;": { "codepoints": [8740], "characters": "\u2224" },
+ "&nparallel;": { "codepoints": [8742], "characters": "\u2226" },
+ "&npar;": { "codepoints": [8742], "characters": "\u2226" },
+ "&nparsl;": { "codepoints": [11005, 8421], "characters": "\u2AFD\u20E5" },
+ "&npart;": { "codepoints": [8706, 824], "characters": "\u2202\u0338" },
+ "&npolint;": { "codepoints": [10772], "characters": "\u2A14" },
+ "&npr;": { "codepoints": [8832], "characters": "\u2280" },
+ "&nprcue;": { "codepoints": [8928], "characters": "\u22E0" },
+ "&nprec;": { "codepoints": [8832], "characters": "\u2280" },
+ "&npreceq;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+ "&npre;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+ "&nrarrc;": { "codepoints": [10547, 824], "characters": "\u2933\u0338" },
+ "&nrarr;": { "codepoints": [8603], "characters": "\u219B" },
+ "&nrArr;": { "codepoints": [8655], "characters": "\u21CF" },
+ "&nrarrw;": { "codepoints": [8605, 824], "characters": "\u219D\u0338" },
+ "&nrightarrow;": { "codepoints": [8603], "characters": "\u219B" },
+ "&nRightarrow;": { "codepoints": [8655], "characters": "\u21CF" },
+ "&nrtri;": { "codepoints": [8939], "characters": "\u22EB" },
+ "&nrtrie;": { "codepoints": [8941], "characters": "\u22ED" },
+ "&nsc;": { "codepoints": [8833], "characters": "\u2281" },
+ "&nsccue;": { "codepoints": [8929], "characters": "\u22E1" },
+ "&nsce;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+ "&Nscr;": { "codepoints": [119977], "characters": "\uD835\uDCA9" },
+ "&nscr;": { "codepoints": [120003], "characters": "\uD835\uDCC3" },
+ "&nshortmid;": { "codepoints": [8740], "characters": "\u2224" },
+ "&nshortparallel;": { "codepoints": [8742], "characters": "\u2226" },
+ "&nsim;": { "codepoints": [8769], "characters": "\u2241" },
+ "&nsime;": { "codepoints": [8772], "characters": "\u2244" },
+ "&nsimeq;": { "codepoints": [8772], "characters": "\u2244" },
+ "&nsmid;": { "codepoints": [8740], "characters": "\u2224" },
+ "&nspar;": { "codepoints": [8742], "characters": "\u2226" },
+ "&nsqsube;": { "codepoints": [8930], "characters": "\u22E2" },
+ "&nsqsupe;": { "codepoints": [8931], "characters": "\u22E3" },
+ "&nsub;": { "codepoints": [8836], "characters": "\u2284" },
+ "&nsubE;": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" },
+ "&nsube;": { "codepoints": [8840], "characters": "\u2288" },
+ "&nsubset;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+ "&nsubseteq;": { "codepoints": [8840], "characters": "\u2288" },
+ "&nsubseteqq;": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" },
+ "&nsucc;": { "codepoints": [8833], "characters": "\u2281" },
+ "&nsucceq;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+ "&nsup;": { "codepoints": [8837], "characters": "\u2285" },
+ "&nsupE;": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" },
+ "&nsupe;": { "codepoints": [8841], "characters": "\u2289" },
+ "&nsupset;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+ "&nsupseteq;": { "codepoints": [8841], "characters": "\u2289" },
+ "&nsupseteqq;": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" },
+ "&ntgl;": { "codepoints": [8825], "characters": "\u2279" },
+ "&Ntilde;": { "codepoints": [209], "characters": "\u00D1" },
+ "&Ntilde": { "codepoints": [209], "characters": "\u00D1" },
+ "&ntilde;": { "codepoints": [241], "characters": "\u00F1" },
+ "&ntilde": { "codepoints": [241], "characters": "\u00F1" },
+ "&ntlg;": { "codepoints": [8824], "characters": "\u2278" },
+ "&ntriangleleft;": { "codepoints": [8938], "characters": "\u22EA" },
+ "&ntrianglelefteq;": { "codepoints": [8940], "characters": "\u22EC" },
+ "&ntriangleright;": { "codepoints": [8939], "characters": "\u22EB" },
+ "&ntrianglerighteq;": { "codepoints": [8941], "characters": "\u22ED" },
+ "&Nu;": { "codepoints": [925], "characters": "\u039D" },
+ "&nu;": { "codepoints": [957], "characters": "\u03BD" },
+ "&num;": { "codepoints": [35], "characters": "\u0023" },
+ "&numero;": { "codepoints": [8470], "characters": "\u2116" },
+ "&numsp;": { "codepoints": [8199], "characters": "\u2007" },
+ "&nvap;": { "codepoints": [8781, 8402], "characters": "\u224D\u20D2" },
+ "&nvdash;": { "codepoints": [8876], "characters": "\u22AC" },
+ "&nvDash;": { "codepoints": [8877], "characters": "\u22AD" },
+ "&nVdash;": { "codepoints": [8878], "characters": "\u22AE" },
+ "&nVDash;": { "codepoints": [8879], "characters": "\u22AF" },
+ "&nvge;": { "codepoints": [8805, 8402], "characters": "\u2265\u20D2" },
+ "&nvgt;": { "codepoints": [62, 8402], "characters": "\u003E\u20D2" },
+ "&nvHarr;": { "codepoints": [10500], "characters": "\u2904" },
+ "&nvinfin;": { "codepoints": [10718], "characters": "\u29DE" },
+ "&nvlArr;": { "codepoints": [10498], "characters": "\u2902" },
+ "&nvle;": { "codepoints": [8804, 8402], "characters": "\u2264\u20D2" },
+ "&nvlt;": { "codepoints": [60, 8402], "characters": "\u003C\u20D2" },
+ "&nvltrie;": { "codepoints": [8884, 8402], "characters": "\u22B4\u20D2" },
+ "&nvrArr;": { "codepoints": [10499], "characters": "\u2903" },
+ "&nvrtrie;": { "codepoints": [8885, 8402], "characters": "\u22B5\u20D2" },
+ "&nvsim;": { "codepoints": [8764, 8402], "characters": "\u223C\u20D2" },
+ "&nwarhk;": { "codepoints": [10531], "characters": "\u2923" },
+ "&nwarr;": { "codepoints": [8598], "characters": "\u2196" },
+ "&nwArr;": { "codepoints": [8662], "characters": "\u21D6" },
+ "&nwarrow;": { "codepoints": [8598], "characters": "\u2196" },
+ "&nwnear;": { "codepoints": [10535], "characters": "\u2927" },
+ "&Oacute;": { "codepoints": [211], "characters": "\u00D3" },
+ "&Oacute": { "codepoints": [211], "characters": "\u00D3" },
+ "&oacute;": { "codepoints": [243], "characters": "\u00F3" },
+ "&oacute": { "codepoints": [243], "characters": "\u00F3" },
+ "&oast;": { "codepoints": [8859], "characters": "\u229B" },
+ "&Ocirc;": { "codepoints": [212], "characters": "\u00D4" },
+ "&Ocirc": { "codepoints": [212], "characters": "\u00D4" },
+ "&ocirc;": { "codepoints": [244], "characters": "\u00F4" },
+ "&ocirc": { "codepoints": [244], "characters": "\u00F4" },
+ "&ocir;": { "codepoints": [8858], "characters": "\u229A" },
+ "&Ocy;": { "codepoints": [1054], "characters": "\u041E" },
+ "&ocy;": { "codepoints": [1086], "characters": "\u043E" },
+ "&odash;": { "codepoints": [8861], "characters": "\u229D" },
+ "&Odblac;": { "codepoints": [336], "characters": "\u0150" },
+ "&odblac;": { "codepoints": [337], "characters": "\u0151" },
+ "&odiv;": { "codepoints": [10808], "characters": "\u2A38" },
+ "&odot;": { "codepoints": [8857], "characters": "\u2299" },
+ "&odsold;": { "codepoints": [10684], "characters": "\u29BC" },
+ "&OElig;": { "codepoints": [338], "characters": "\u0152" },
+ "&oelig;": { "codepoints": [339], "characters": "\u0153" },
+ "&ofcir;": { "codepoints": [10687], "characters": "\u29BF" },
+ "&Ofr;": { "codepoints": [120082], "characters": "\uD835\uDD12" },
+ "&ofr;": { "codepoints": [120108], "characters": "\uD835\uDD2C" },
+ "&ogon;": { "codepoints": [731], "characters": "\u02DB" },
+ "&Ograve;": { "codepoints": [210], "characters": "\u00D2" },
+ "&Ograve": { "codepoints": [210], "characters": "\u00D2" },
+ "&ograve;": { "codepoints": [242], "characters": "\u00F2" },
+ "&ograve": { "codepoints": [242], "characters": "\u00F2" },
+ "&ogt;": { "codepoints": [10689], "characters": "\u29C1" },
+ "&ohbar;": { "codepoints": [10677], "characters": "\u29B5" },
+ "&ohm;": { "codepoints": [937], "characters": "\u03A9" },
+ "&oint;": { "codepoints": [8750], "characters": "\u222E" },
+ "&olarr;": { "codepoints": [8634], "characters": "\u21BA" },
+ "&olcir;": { "codepoints": [10686], "characters": "\u29BE" },
+ "&olcross;": { "codepoints": [10683], "characters": "\u29BB" },
+ "&oline;": { "codepoints": [8254], "characters": "\u203E" },
+ "&olt;": { "codepoints": [10688], "characters": "\u29C0" },
+ "&Omacr;": { "codepoints": [332], "characters": "\u014C" },
+ "&omacr;": { "codepoints": [333], "characters": "\u014D" },
+ "&Omega;": { "codepoints": [937], "characters": "\u03A9" },
+ "&omega;": { "codepoints": [969], "characters": "\u03C9" },
+ "&Omicron;": { "codepoints": [927], "characters": "\u039F" },
+ "&omicron;": { "codepoints": [959], "characters": "\u03BF" },
+ "&omid;": { "codepoints": [10678], "characters": "\u29B6" },
+ "&ominus;": { "codepoints": [8854], "characters": "\u2296" },
+ "&Oopf;": { "codepoints": [120134], "characters": "\uD835\uDD46" },
+ "&oopf;": { "codepoints": [120160], "characters": "\uD835\uDD60" },
+ "&opar;": { "codepoints": [10679], "characters": "\u29B7" },
+ "&OpenCurlyDoubleQuote;": { "codepoints": [8220], "characters": "\u201C" },
+ "&OpenCurlyQuote;": { "codepoints": [8216], "characters": "\u2018" },
+ "&operp;": { "codepoints": [10681], "characters": "\u29B9" },
+ "&oplus;": { "codepoints": [8853], "characters": "\u2295" },
+ "&orarr;": { "codepoints": [8635], "characters": "\u21BB" },
+ "&Or;": { "codepoints": [10836], "characters": "\u2A54" },
+ "&or;": { "codepoints": [8744], "characters": "\u2228" },
+ "&ord;": { "codepoints": [10845], "characters": "\u2A5D" },
+ "&order;": { "codepoints": [8500], "characters": "\u2134" },
+ "&orderof;": { "codepoints": [8500], "characters": "\u2134" },
+ "&ordf;": { "codepoints": [170], "characters": "\u00AA" },
+ "&ordf": { "codepoints": [170], "characters": "\u00AA" },
+ "&ordm;": { "codepoints": [186], "characters": "\u00BA" },
+ "&ordm": { "codepoints": [186], "characters": "\u00BA" },
+ "&origof;": { "codepoints": [8886], "characters": "\u22B6" },
+ "&oror;": { "codepoints": [10838], "characters": "\u2A56" },
+ "&orslope;": { "codepoints": [10839], "characters": "\u2A57" },
+ "&orv;": { "codepoints": [10843], "characters": "\u2A5B" },
+ "&oS;": { "codepoints": [9416], "characters": "\u24C8" },
+ "&Oscr;": { "codepoints": [119978], "characters": "\uD835\uDCAA" },
+ "&oscr;": { "codepoints": [8500], "characters": "\u2134" },
+ "&Oslash;": { "codepoints": [216], "characters": "\u00D8" },
+ "&Oslash": { "codepoints": [216], "characters": "\u00D8" },
+ "&oslash;": { "codepoints": [248], "characters": "\u00F8" },
+ "&oslash": { "codepoints": [248], "characters": "\u00F8" },
+ "&osol;": { "codepoints": [8856], "characters": "\u2298" },
+ "&Otilde;": { "codepoints": [213], "characters": "\u00D5" },
+ "&Otilde": { "codepoints": [213], "characters": "\u00D5" },
+ "&otilde;": { "codepoints": [245], "characters": "\u00F5" },
+ "&otilde": { "codepoints": [245], "characters": "\u00F5" },
+ "&otimesas;": { "codepoints": [10806], "characters": "\u2A36" },
+ "&Otimes;": { "codepoints": [10807], "characters": "\u2A37" },
+ "&otimes;": { "codepoints": [8855], "characters": "\u2297" },
+ "&Ouml;": { "codepoints": [214], "characters": "\u00D6" },
+ "&Ouml": { "codepoints": [214], "characters": "\u00D6" },
+ "&ouml;": { "codepoints": [246], "characters": "\u00F6" },
+ "&ouml": { "codepoints": [246], "characters": "\u00F6" },
+ "&ovbar;": { "codepoints": [9021], "characters": "\u233D" },
+ "&OverBar;": { "codepoints": [8254], "characters": "\u203E" },
+ "&OverBrace;": { "codepoints": [9182], "characters": "\u23DE" },
+ "&OverBracket;": { "codepoints": [9140], "characters": "\u23B4" },
+ "&OverParenthesis;": { "codepoints": [9180], "characters": "\u23DC" },
+ "&para;": { "codepoints": [182], "characters": "\u00B6" },
+ "&para": { "codepoints": [182], "characters": "\u00B6" },
+ "&parallel;": { "codepoints": [8741], "characters": "\u2225" },
+ "&par;": { "codepoints": [8741], "characters": "\u2225" },
+ "&parsim;": { "codepoints": [10995], "characters": "\u2AF3" },
+ "&parsl;": { "codepoints": [11005], "characters": "\u2AFD" },
+ "&part;": { "codepoints": [8706], "characters": "\u2202" },
+ "&PartialD;": { "codepoints": [8706], "characters": "\u2202" },
+ "&Pcy;": { "codepoints": [1055], "characters": "\u041F" },
+ "&pcy;": { "codepoints": [1087], "characters": "\u043F" },
+ "&percnt;": { "codepoints": [37], "characters": "\u0025" },
+ "&period;": { "codepoints": [46], "characters": "\u002E" },
+ "&permil;": { "codepoints": [8240], "characters": "\u2030" },
+ "&perp;": { "codepoints": [8869], "characters": "\u22A5" },
+ "&pertenk;": { "codepoints": [8241], "characters": "\u2031" },
+ "&Pfr;": { "codepoints": [120083], "characters": "\uD835\uDD13" },
+ "&pfr;": { "codepoints": [120109], "characters": "\uD835\uDD2D" },
+ "&Phi;": { "codepoints": [934], "characters": "\u03A6" },
+ "&phi;": { "codepoints": [966], "characters": "\u03C6" },
+ "&phiv;": { "codepoints": [981], "characters": "\u03D5" },
+ "&phmmat;": { "codepoints": [8499], "characters": "\u2133" },
+ "&phone;": { "codepoints": [9742], "characters": "\u260E" },
+ "&Pi;": { "codepoints": [928], "characters": "\u03A0" },
+ "&pi;": { "codepoints": [960], "characters": "\u03C0" },
+ "&pitchfork;": { "codepoints": [8916], "characters": "\u22D4" },
+ "&piv;": { "codepoints": [982], "characters": "\u03D6" },
+ "&planck;": { "codepoints": [8463], "characters": "\u210F" },
+ "&planckh;": { "codepoints": [8462], "characters": "\u210E" },
+ "&plankv;": { "codepoints": [8463], "characters": "\u210F" },
+ "&plusacir;": { "codepoints": [10787], "characters": "\u2A23" },
+ "&plusb;": { "codepoints": [8862], "characters": "\u229E" },
+ "&pluscir;": { "codepoints": [10786], "characters": "\u2A22" },
+ "&plus;": { "codepoints": [43], "characters": "\u002B" },
+ "&plusdo;": { "codepoints": [8724], "characters": "\u2214" },
+ "&plusdu;": { "codepoints": [10789], "characters": "\u2A25" },
+ "&pluse;": { "codepoints": [10866], "characters": "\u2A72" },
+ "&PlusMinus;": { "codepoints": [177], "characters": "\u00B1" },
+ "&plusmn;": { "codepoints": [177], "characters": "\u00B1" },
+ "&plusmn": { "codepoints": [177], "characters": "\u00B1" },
+ "&plussim;": { "codepoints": [10790], "characters": "\u2A26" },
+ "&plustwo;": { "codepoints": [10791], "characters": "\u2A27" },
+ "&pm;": { "codepoints": [177], "characters": "\u00B1" },
+ "&Poincareplane;": { "codepoints": [8460], "characters": "\u210C" },
+ "&pointint;": { "codepoints": [10773], "characters": "\u2A15" },
+ "&popf;": { "codepoints": [120161], "characters": "\uD835\uDD61" },
+ "&Popf;": { "codepoints": [8473], "characters": "\u2119" },
+ "&pound;": { "codepoints": [163], "characters": "\u00A3" },
+ "&pound": { "codepoints": [163], "characters": "\u00A3" },
+ "&prap;": { "codepoints": [10935], "characters": "\u2AB7" },
+ "&Pr;": { "codepoints": [10939], "characters": "\u2ABB" },
+ "&pr;": { "codepoints": [8826], "characters": "\u227A" },
+ "&prcue;": { "codepoints": [8828], "characters": "\u227C" },
+ "&precapprox;": { "codepoints": [10935], "characters": "\u2AB7" },
+ "&prec;": { "codepoints": [8826], "characters": "\u227A" },
+ "&preccurlyeq;": { "codepoints": [8828], "characters": "\u227C" },
+ "&Precedes;": { "codepoints": [8826], "characters": "\u227A" },
+ "&PrecedesEqual;": { "codepoints": [10927], "characters": "\u2AAF" },
+ "&PrecedesSlantEqual;": { "codepoints": [8828], "characters": "\u227C" },
+ "&PrecedesTilde;": { "codepoints": [8830], "characters": "\u227E" },
+ "&preceq;": { "codepoints": [10927], "characters": "\u2AAF" },
+ "&precnapprox;": { "codepoints": [10937], "characters": "\u2AB9" },
+ "&precneqq;": { "codepoints": [10933], "characters": "\u2AB5" },
+ "&precnsim;": { "codepoints": [8936], "characters": "\u22E8" },
+ "&pre;": { "codepoints": [10927], "characters": "\u2AAF" },
+ "&prE;": { "codepoints": [10931], "characters": "\u2AB3" },
+ "&precsim;": { "codepoints": [8830], "characters": "\u227E" },
+ "&prime;": { "codepoints": [8242], "characters": "\u2032" },
+ "&Prime;": { "codepoints": [8243], "characters": "\u2033" },
+ "&primes;": { "codepoints": [8473], "characters": "\u2119" },
+ "&prnap;": { "codepoints": [10937], "characters": "\u2AB9" },
+ "&prnE;": { "codepoints": [10933], "characters": "\u2AB5" },
+ "&prnsim;": { "codepoints": [8936], "characters": "\u22E8" },
+ "&prod;": { "codepoints": [8719], "characters": "\u220F" },
+ "&Product;": { "codepoints": [8719], "characters": "\u220F" },
+ "&profalar;": { "codepoints": [9006], "characters": "\u232E" },
+ "&profline;": { "codepoints": [8978], "characters": "\u2312" },
+ "&profsurf;": { "codepoints": [8979], "characters": "\u2313" },
+ "&prop;": { "codepoints": [8733], "characters": "\u221D" },
+ "&Proportional;": { "codepoints": [8733], "characters": "\u221D" },
+ "&Proportion;": { "codepoints": [8759], "characters": "\u2237" },
+ "&propto;": { "codepoints": [8733], "characters": "\u221D" },
+ "&prsim;": { "codepoints": [8830], "characters": "\u227E" },
+ "&prurel;": { "codepoints": [8880], "characters": "\u22B0" },
+ "&Pscr;": { "codepoints": [119979], "characters": "\uD835\uDCAB" },
+ "&pscr;": { "codepoints": [120005], "characters": "\uD835\uDCC5" },
+ "&Psi;": { "codepoints": [936], "characters": "\u03A8" },
+ "&psi;": { "codepoints": [968], "characters": "\u03C8" },
+ "&puncsp;": { "codepoints": [8200], "characters": "\u2008" },
+ "&Qfr;": { "codepoints": [120084], "characters": "\uD835\uDD14" },
+ "&qfr;": { "codepoints": [120110], "characters": "\uD835\uDD2E" },
+ "&qint;": { "codepoints": [10764], "characters": "\u2A0C" },
+ "&qopf;": { "codepoints": [120162], "characters": "\uD835\uDD62" },
+ "&Qopf;": { "codepoints": [8474], "characters": "\u211A" },
+ "&qprime;": { "codepoints": [8279], "characters": "\u2057" },
+ "&Qscr;": { "codepoints": [119980], "characters": "\uD835\uDCAC" },
+ "&qscr;": { "codepoints": [120006], "characters": "\uD835\uDCC6" },
+ "&quaternions;": { "codepoints": [8461], "characters": "\u210D" },
+ "&quatint;": { "codepoints": [10774], "characters": "\u2A16" },
+ "&quest;": { "codepoints": [63], "characters": "\u003F" },
+ "&questeq;": { "codepoints": [8799], "characters": "\u225F" },
+ "&quot;": { "codepoints": [34], "characters": "\u0022" },
+ "&quot": { "codepoints": [34], "characters": "\u0022" },
+ "&QUOT;": { "codepoints": [34], "characters": "\u0022" },
+ "&QUOT": { "codepoints": [34], "characters": "\u0022" },
+ "&rAarr;": { "codepoints": [8667], "characters": "\u21DB" },
+ "&race;": { "codepoints": [8765, 817], "characters": "\u223D\u0331" },
+ "&Racute;": { "codepoints": [340], "characters": "\u0154" },
+ "&racute;": { "codepoints": [341], "characters": "\u0155" },
+ "&radic;": { "codepoints": [8730], "characters": "\u221A" },
+ "&raemptyv;": { "codepoints": [10675], "characters": "\u29B3" },
+ "&rang;": { "codepoints": [10217], "characters": "\u27E9" },
+ "&Rang;": { "codepoints": [10219], "characters": "\u27EB" },
+ "&rangd;": { "codepoints": [10642], "characters": "\u2992" },
+ "&range;": { "codepoints": [10661], "characters": "\u29A5" },
+ "&rangle;": { "codepoints": [10217], "characters": "\u27E9" },
+ "&raquo;": { "codepoints": [187], "characters": "\u00BB" },
+ "&raquo": { "codepoints": [187], "characters": "\u00BB" },
+ "&rarrap;": { "codepoints": [10613], "characters": "\u2975" },
+ "&rarrb;": { "codepoints": [8677], "characters": "\u21E5" },
+ "&rarrbfs;": { "codepoints": [10528], "characters": "\u2920" },
+ "&rarrc;": { "codepoints": [10547], "characters": "\u2933" },
+ "&rarr;": { "codepoints": [8594], "characters": "\u2192" },
+ "&Rarr;": { "codepoints": [8608], "characters": "\u21A0" },
+ "&rArr;": { "codepoints": [8658], "characters": "\u21D2" },
+ "&rarrfs;": { "codepoints": [10526], "characters": "\u291E" },
+ "&rarrhk;": { "codepoints": [8618], "characters": "\u21AA" },
+ "&rarrlp;": { "codepoints": [8620], "characters": "\u21AC" },
+ "&rarrpl;": { "codepoints": [10565], "characters": "\u2945" },
+ "&rarrsim;": { "codepoints": [10612], "characters": "\u2974" },
+ "&Rarrtl;": { "codepoints": [10518], "characters": "\u2916" },
+ "&rarrtl;": { "codepoints": [8611], "characters": "\u21A3" },
+ "&rarrw;": { "codepoints": [8605], "characters": "\u219D" },
+ "&ratail;": { "codepoints": [10522], "characters": "\u291A" },
+ "&rAtail;": { "codepoints": [10524], "characters": "\u291C" },
+ "&ratio;": { "codepoints": [8758], "characters": "\u2236" },
+ "&rationals;": { "codepoints": [8474], "characters": "\u211A" },
+ "&rbarr;": { "codepoints": [10509], "characters": "\u290D" },
+ "&rBarr;": { "codepoints": [10511], "characters": "\u290F" },
+ "&RBarr;": { "codepoints": [10512], "characters": "\u2910" },
+ "&rbbrk;": { "codepoints": [10099], "characters": "\u2773" },
+ "&rbrace;": { "codepoints": [125], "characters": "\u007D" },
+ "&rbrack;": { "codepoints": [93], "characters": "\u005D" },
+ "&rbrke;": { "codepoints": [10636], "characters": "\u298C" },
+ "&rbrksld;": { "codepoints": [10638], "characters": "\u298E" },
+ "&rbrkslu;": { "codepoints": [10640], "characters": "\u2990" },
+ "&Rcaron;": { "codepoints": [344], "characters": "\u0158" },
+ "&rcaron;": { "codepoints": [345], "characters": "\u0159" },
+ "&Rcedil;": { "codepoints": [342], "characters": "\u0156" },
+ "&rcedil;": { "codepoints": [343], "characters": "\u0157" },
+ "&rceil;": { "codepoints": [8969], "characters": "\u2309" },
+ "&rcub;": { "codepoints": [125], "characters": "\u007D" },
+ "&Rcy;": { "codepoints": [1056], "characters": "\u0420" },
+ "&rcy;": { "codepoints": [1088], "characters": "\u0440" },
+ "&rdca;": { "codepoints": [10551], "characters": "\u2937" },
+ "&rdldhar;": { "codepoints": [10601], "characters": "\u2969" },
+ "&rdquo;": { "codepoints": [8221], "characters": "\u201D" },
+ "&rdquor;": { "codepoints": [8221], "characters": "\u201D" },
+ "&rdsh;": { "codepoints": [8627], "characters": "\u21B3" },
+ "&real;": { "codepoints": [8476], "characters": "\u211C" },
+ "&realine;": { "codepoints": [8475], "characters": "\u211B" },
+ "&realpart;": { "codepoints": [8476], "characters": "\u211C" },
+ "&reals;": { "codepoints": [8477], "characters": "\u211D" },
+ "&Re;": { "codepoints": [8476], "characters": "\u211C" },
+ "&rect;": { "codepoints": [9645], "characters": "\u25AD" },
+ "&reg;": { "codepoints": [174], "characters": "\u00AE" },
+ "&reg": { "codepoints": [174], "characters": "\u00AE" },
+ "&REG;": { "codepoints": [174], "characters": "\u00AE" },
+ "&REG": { "codepoints": [174], "characters": "\u00AE" },
+ "&ReverseElement;": { "codepoints": [8715], "characters": "\u220B" },
+ "&ReverseEquilibrium;": { "codepoints": [8651], "characters": "\u21CB" },
+ "&ReverseUpEquilibrium;": { "codepoints": [10607], "characters": "\u296F" },
+ "&rfisht;": { "codepoints": [10621], "characters": "\u297D" },
+ "&rfloor;": { "codepoints": [8971], "characters": "\u230B" },
+ "&rfr;": { "codepoints": [120111], "characters": "\uD835\uDD2F" },
+ "&Rfr;": { "codepoints": [8476], "characters": "\u211C" },
+ "&rHar;": { "codepoints": [10596], "characters": "\u2964" },
+ "&rhard;": { "codepoints": [8641], "characters": "\u21C1" },
+ "&rharu;": { "codepoints": [8640], "characters": "\u21C0" },
+ "&rharul;": { "codepoints": [10604], "characters": "\u296C" },
+ "&Rho;": { "codepoints": [929], "characters": "\u03A1" },
+ "&rho;": { "codepoints": [961], "characters": "\u03C1" },
+ "&rhov;": { "codepoints": [1009], "characters": "\u03F1" },
+ "&RightAngleBracket;": { "codepoints": [10217], "characters": "\u27E9" },
+ "&RightArrowBar;": { "codepoints": [8677], "characters": "\u21E5" },
+ "&rightarrow;": { "codepoints": [8594], "characters": "\u2192" },
+ "&RightArrow;": { "codepoints": [8594], "characters": "\u2192" },
+ "&Rightarrow;": { "codepoints": [8658], "characters": "\u21D2" },
+ "&RightArrowLeftArrow;": { "codepoints": [8644], "characters": "\u21C4" },
+ "&rightarrowtail;": { "codepoints": [8611], "characters": "\u21A3" },
+ "&RightCeiling;": { "codepoints": [8969], "characters": "\u2309" },
+ "&RightDoubleBracket;": { "codepoints": [10215], "characters": "\u27E7" },
+ "&RightDownTeeVector;": { "codepoints": [10589], "characters": "\u295D" },
+ "&RightDownVectorBar;": { "codepoints": [10581], "characters": "\u2955" },
+ "&RightDownVector;": { "codepoints": [8642], "characters": "\u21C2" },
+ "&RightFloor;": { "codepoints": [8971], "characters": "\u230B" },
+ "&rightharpoondown;": { "codepoints": [8641], "characters": "\u21C1" },
+ "&rightharpoonup;": { "codepoints": [8640], "characters": "\u21C0" },
+ "&rightleftarrows;": { "codepoints": [8644], "characters": "\u21C4" },
+ "&rightleftharpoons;": { "codepoints": [8652], "characters": "\u21CC" },
+ "&rightrightarrows;": { "codepoints": [8649], "characters": "\u21C9" },
+ "&rightsquigarrow;": { "codepoints": [8605], "characters": "\u219D" },
+ "&RightTeeArrow;": { "codepoints": [8614], "characters": "\u21A6" },
+ "&RightTee;": { "codepoints": [8866], "characters": "\u22A2" },
+ "&RightTeeVector;": { "codepoints": [10587], "characters": "\u295B" },
+ "&rightthreetimes;": { "codepoints": [8908], "characters": "\u22CC" },
+ "&RightTriangleBar;": { "codepoints": [10704], "characters": "\u29D0" },
+ "&RightTriangle;": { "codepoints": [8883], "characters": "\u22B3" },
+ "&RightTriangleEqual;": { "codepoints": [8885], "characters": "\u22B5" },
+ "&RightUpDownVector;": { "codepoints": [10575], "characters": "\u294F" },
+ "&RightUpTeeVector;": { "codepoints": [10588], "characters": "\u295C" },
+ "&RightUpVectorBar;": { "codepoints": [10580], "characters": "\u2954" },
+ "&RightUpVector;": { "codepoints": [8638], "characters": "\u21BE" },
+ "&RightVectorBar;": { "codepoints": [10579], "characters": "\u2953" },
+ "&RightVector;": { "codepoints": [8640], "characters": "\u21C0" },
+ "&ring;": { "codepoints": [730], "characters": "\u02DA" },
+ "&risingdotseq;": { "codepoints": [8787], "characters": "\u2253" },
+ "&rlarr;": { "codepoints": [8644], "characters": "\u21C4" },
+ "&rlhar;": { "codepoints": [8652], "characters": "\u21CC" },
+ "&rlm;": { "codepoints": [8207], "characters": "\u200F" },
+ "&rmoustache;": { "codepoints": [9137], "characters": "\u23B1" },
+ "&rmoust;": { "codepoints": [9137], "characters": "\u23B1" },
+ "&rnmid;": { "codepoints": [10990], "characters": "\u2AEE" },
+ "&roang;": { "codepoints": [10221], "characters": "\u27ED" },
+ "&roarr;": { "codepoints": [8702], "characters": "\u21FE" },
+ "&robrk;": { "codepoints": [10215], "characters": "\u27E7" },
+ "&ropar;": { "codepoints": [10630], "characters": "\u2986" },
+ "&ropf;": { "codepoints": [120163], "characters": "\uD835\uDD63" },
+ "&Ropf;": { "codepoints": [8477], "characters": "\u211D" },
+ "&roplus;": { "codepoints": [10798], "characters": "\u2A2E" },
+ "&rotimes;": { "codepoints": [10805], "characters": "\u2A35" },
+ "&RoundImplies;": { "codepoints": [10608], "characters": "\u2970" },
+ "&rpar;": { "codepoints": [41], "characters": "\u0029" },
+ "&rpargt;": { "codepoints": [10644], "characters": "\u2994" },
+ "&rppolint;": { "codepoints": [10770], "characters": "\u2A12" },
+ "&rrarr;": { "codepoints": [8649], "characters": "\u21C9" },
+ "&Rrightarrow;": { "codepoints": [8667], "characters": "\u21DB" },
+ "&rsaquo;": { "codepoints": [8250], "characters": "\u203A" },
+ "&rscr;": { "codepoints": [120007], "characters": "\uD835\uDCC7" },
+ "&Rscr;": { "codepoints": [8475], "characters": "\u211B" },
+ "&rsh;": { "codepoints": [8625], "characters": "\u21B1" },
+ "&Rsh;": { "codepoints": [8625], "characters": "\u21B1" },
+ "&rsqb;": { "codepoints": [93], "characters": "\u005D" },
+ "&rsquo;": { "codepoints": [8217], "characters": "\u2019" },
+ "&rsquor;": { "codepoints": [8217], "characters": "\u2019" },
+ "&rthree;": { "codepoints": [8908], "characters": "\u22CC" },
+ "&rtimes;": { "codepoints": [8906], "characters": "\u22CA" },
+ "&rtri;": { "codepoints": [9657], "characters": "\u25B9" },
+ "&rtrie;": { "codepoints": [8885], "characters": "\u22B5" },
+ "&rtrif;": { "codepoints": [9656], "characters": "\u25B8" },
+ "&rtriltri;": { "codepoints": [10702], "characters": "\u29CE" },
+ "&RuleDelayed;": { "codepoints": [10740], "characters": "\u29F4" },
+ "&ruluhar;": { "codepoints": [10600], "characters": "\u2968" },
+ "&rx;": { "codepoints": [8478], "characters": "\u211E" },
+ "&Sacute;": { "codepoints": [346], "characters": "\u015A" },
+ "&sacute;": { "codepoints": [347], "characters": "\u015B" },
+ "&sbquo;": { "codepoints": [8218], "characters": "\u201A" },
+ "&scap;": { "codepoints": [10936], "characters": "\u2AB8" },
+ "&Scaron;": { "codepoints": [352], "characters": "\u0160" },
+ "&scaron;": { "codepoints": [353], "characters": "\u0161" },
+ "&Sc;": { "codepoints": [10940], "characters": "\u2ABC" },
+ "&sc;": { "codepoints": [8827], "characters": "\u227B" },
+ "&sccue;": { "codepoints": [8829], "characters": "\u227D" },
+ "&sce;": { "codepoints": [10928], "characters": "\u2AB0" },
+ "&scE;": { "codepoints": [10932], "characters": "\u2AB4" },
+ "&Scedil;": { "codepoints": [350], "characters": "\u015E" },
+ "&scedil;": { "codepoints": [351], "characters": "\u015F" },
+ "&Scirc;": { "codepoints": [348], "characters": "\u015C" },
+ "&scirc;": { "codepoints": [349], "characters": "\u015D" },
+ "&scnap;": { "codepoints": [10938], "characters": "\u2ABA" },
+ "&scnE;": { "codepoints": [10934], "characters": "\u2AB6" },
+ "&scnsim;": { "codepoints": [8937], "characters": "\u22E9" },
+ "&scpolint;": { "codepoints": [10771], "characters": "\u2A13" },
+ "&scsim;": { "codepoints": [8831], "characters": "\u227F" },
+ "&Scy;": { "codepoints": [1057], "characters": "\u0421" },
+ "&scy;": { "codepoints": [1089], "characters": "\u0441" },
+ "&sdotb;": { "codepoints": [8865], "characters": "\u22A1" },
+ "&sdot;": { "codepoints": [8901], "characters": "\u22C5" },
+ "&sdote;": { "codepoints": [10854], "characters": "\u2A66" },
+ "&searhk;": { "codepoints": [10533], "characters": "\u2925" },
+ "&searr;": { "codepoints": [8600], "characters": "\u2198" },
+ "&seArr;": { "codepoints": [8664], "characters": "\u21D8" },
+ "&searrow;": { "codepoints": [8600], "characters": "\u2198" },
+ "&sect;": { "codepoints": [167], "characters": "\u00A7" },
+ "&sect": { "codepoints": [167], "characters": "\u00A7" },
+ "&semi;": { "codepoints": [59], "characters": "\u003B" },
+ "&seswar;": { "codepoints": [10537], "characters": "\u2929" },
+ "&setminus;": { "codepoints": [8726], "characters": "\u2216" },
+ "&setmn;": { "codepoints": [8726], "characters": "\u2216" },
+ "&sext;": { "codepoints": [10038], "characters": "\u2736" },
+ "&Sfr;": { "codepoints": [120086], "characters": "\uD835\uDD16" },
+ "&sfr;": { "codepoints": [120112], "characters": "\uD835\uDD30" },
+ "&sfrown;": { "codepoints": [8994], "characters": "\u2322" },
+ "&sharp;": { "codepoints": [9839], "characters": "\u266F" },
+ "&SHCHcy;": { "codepoints": [1065], "characters": "\u0429" },
+ "&shchcy;": { "codepoints": [1097], "characters": "\u0449" },
+ "&SHcy;": { "codepoints": [1064], "characters": "\u0428" },
+ "&shcy;": { "codepoints": [1096], "characters": "\u0448" },
+ "&ShortDownArrow;": { "codepoints": [8595], "characters": "\u2193" },
+ "&ShortLeftArrow;": { "codepoints": [8592], "characters": "\u2190" },
+ "&shortmid;": { "codepoints": [8739], "characters": "\u2223" },
+ "&shortparallel;": { "codepoints": [8741], "characters": "\u2225" },
+ "&ShortRightArrow;": { "codepoints": [8594], "characters": "\u2192" },
+ "&ShortUpArrow;": { "codepoints": [8593], "characters": "\u2191" },
+ "&shy;": { "codepoints": [173], "characters": "\u00AD" },
+ "&shy": { "codepoints": [173], "characters": "\u00AD" },
+ "&Sigma;": { "codepoints": [931], "characters": "\u03A3" },
+ "&sigma;": { "codepoints": [963], "characters": "\u03C3" },
+ "&sigmaf;": { "codepoints": [962], "characters": "\u03C2" },
+ "&sigmav;": { "codepoints": [962], "characters": "\u03C2" },
+ "&sim;": { "codepoints": [8764], "characters": "\u223C" },
+ "&simdot;": { "codepoints": [10858], "characters": "\u2A6A" },
+ "&sime;": { "codepoints": [8771], "characters": "\u2243" },
+ "&simeq;": { "codepoints": [8771], "characters": "\u2243" },
+ "&simg;": { "codepoints": [10910], "characters": "\u2A9E" },
+ "&simgE;": { "codepoints": [10912], "characters": "\u2AA0" },
+ "&siml;": { "codepoints": [10909], "characters": "\u2A9D" },
+ "&simlE;": { "codepoints": [10911], "characters": "\u2A9F" },
+ "&simne;": { "codepoints": [8774], "characters": "\u2246" },
+ "&simplus;": { "codepoints": [10788], "characters": "\u2A24" },
+ "&simrarr;": { "codepoints": [10610], "characters": "\u2972" },
+ "&slarr;": { "codepoints": [8592], "characters": "\u2190" },
+ "&SmallCircle;": { "codepoints": [8728], "characters": "\u2218" },
+ "&smallsetminus;": { "codepoints": [8726], "characters": "\u2216" },
+ "&smashp;": { "codepoints": [10803], "characters": "\u2A33" },
+ "&smeparsl;": { "codepoints": [10724], "characters": "\u29E4" },
+ "&smid;": { "codepoints": [8739], "characters": "\u2223" },
+ "&smile;": { "codepoints": [8995], "characters": "\u2323" },
+ "&smt;": { "codepoints": [10922], "characters": "\u2AAA" },
+ "&smte;": { "codepoints": [10924], "characters": "\u2AAC" },
+ "&smtes;": { "codepoints": [10924, 65024], "characters": "\u2AAC\uFE00" },
+ "&SOFTcy;": { "codepoints": [1068], "characters": "\u042C" },
+ "&softcy;": { "codepoints": [1100], "characters": "\u044C" },
+ "&solbar;": { "codepoints": [9023], "characters": "\u233F" },
+ "&solb;": { "codepoints": [10692], "characters": "\u29C4" },
+ "&sol;": { "codepoints": [47], "characters": "\u002F" },
+ "&Sopf;": { "codepoints": [120138], "characters": "\uD835\uDD4A" },
+ "&sopf;": { "codepoints": [120164], "characters": "\uD835\uDD64" },
+ "&spades;": { "codepoints": [9824], "characters": "\u2660" },
+ "&spadesuit;": { "codepoints": [9824], "characters": "\u2660" },
+ "&spar;": { "codepoints": [8741], "characters": "\u2225" },
+ "&sqcap;": { "codepoints": [8851], "characters": "\u2293" },
+ "&sqcaps;": { "codepoints": [8851, 65024], "characters": "\u2293\uFE00" },
+ "&sqcup;": { "codepoints": [8852], "characters": "\u2294" },
+ "&sqcups;": { "codepoints": [8852, 65024], "characters": "\u2294\uFE00" },
+ "&Sqrt;": { "codepoints": [8730], "characters": "\u221A" },
+ "&sqsub;": { "codepoints": [8847], "characters": "\u228F" },
+ "&sqsube;": { "codepoints": [8849], "characters": "\u2291" },
+ "&sqsubset;": { "codepoints": [8847], "characters": "\u228F" },
+ "&sqsubseteq;": { "codepoints": [8849], "characters": "\u2291" },
+ "&sqsup;": { "codepoints": [8848], "characters": "\u2290" },
+ "&sqsupe;": { "codepoints": [8850], "characters": "\u2292" },
+ "&sqsupset;": { "codepoints": [8848], "characters": "\u2290" },
+ "&sqsupseteq;": { "codepoints": [8850], "characters": "\u2292" },
+ "&square;": { "codepoints": [9633], "characters": "\u25A1" },
+ "&Square;": { "codepoints": [9633], "characters": "\u25A1" },
+ "&SquareIntersection;": { "codepoints": [8851], "characters": "\u2293" },
+ "&SquareSubset;": { "codepoints": [8847], "characters": "\u228F" },
+ "&SquareSubsetEqual;": { "codepoints": [8849], "characters": "\u2291" },
+ "&SquareSuperset;": { "codepoints": [8848], "characters": "\u2290" },
+ "&SquareSupersetEqual;": { "codepoints": [8850], "characters": "\u2292" },
+ "&SquareUnion;": { "codepoints": [8852], "characters": "\u2294" },
+ "&squarf;": { "codepoints": [9642], "characters": "\u25AA" },
+ "&squ;": { "codepoints": [9633], "characters": "\u25A1" },
+ "&squf;": { "codepoints": [9642], "characters": "\u25AA" },
+ "&srarr;": { "codepoints": [8594], "characters": "\u2192" },
+ "&Sscr;": { "codepoints": [119982], "characters": "\uD835\uDCAE" },
+ "&sscr;": { "codepoints": [120008], "characters": "\uD835\uDCC8" },
+ "&ssetmn;": { "codepoints": [8726], "characters": "\u2216" },
+ "&ssmile;": { "codepoints": [8995], "characters": "\u2323" },
+ "&sstarf;": { "codepoints": [8902], "characters": "\u22C6" },
+ "&Star;": { "codepoints": [8902], "characters": "\u22C6" },
+ "&star;": { "codepoints": [9734], "characters": "\u2606" },
+ "&starf;": { "codepoints": [9733], "characters": "\u2605" },
+ "&straightepsilon;": { "codepoints": [1013], "characters": "\u03F5" },
+ "&straightphi;": { "codepoints": [981], "characters": "\u03D5" },
+ "&strns;": { "codepoints": [175], "characters": "\u00AF" },
+ "&sub;": { "codepoints": [8834], "characters": "\u2282" },
+ "&Sub;": { "codepoints": [8912], "characters": "\u22D0" },
+ "&subdot;": { "codepoints": [10941], "characters": "\u2ABD" },
+ "&subE;": { "codepoints": [10949], "characters": "\u2AC5" },
+ "&sube;": { "codepoints": [8838], "characters": "\u2286" },
+ "&subedot;": { "codepoints": [10947], "characters": "\u2AC3" },
+ "&submult;": { "codepoints": [10945], "characters": "\u2AC1" },
+ "&subnE;": { "codepoints": [10955], "characters": "\u2ACB" },
+ "&subne;": { "codepoints": [8842], "characters": "\u228A" },
+ "&subplus;": { "codepoints": [10943], "characters": "\u2ABF" },
+ "&subrarr;": { "codepoints": [10617], "characters": "\u2979" },
+ "&subset;": { "codepoints": [8834], "characters": "\u2282" },
+ "&Subset;": { "codepoints": [8912], "characters": "\u22D0" },
+ "&subseteq;": { "codepoints": [8838], "characters": "\u2286" },
+ "&subseteqq;": { "codepoints": [10949], "characters": "\u2AC5" },
+ "&SubsetEqual;": { "codepoints": [8838], "characters": "\u2286" },
+ "&subsetneq;": { "codepoints": [8842], "characters": "\u228A" },
+ "&subsetneqq;": { "codepoints": [10955], "characters": "\u2ACB" },
+ "&subsim;": { "codepoints": [10951], "characters": "\u2AC7" },
+ "&subsub;": { "codepoints": [10965], "characters": "\u2AD5" },
+ "&subsup;": { "codepoints": [10963], "characters": "\u2AD3" },
+ "&succapprox;": { "codepoints": [10936], "characters": "\u2AB8" },
+ "&succ;": { "codepoints": [8827], "characters": "\u227B" },
+ "&succcurlyeq;": { "codepoints": [8829], "characters": "\u227D" },
+ "&Succeeds;": { "codepoints": [8827], "characters": "\u227B" },
+ "&SucceedsEqual;": { "codepoints": [10928], "characters": "\u2AB0" },
+ "&SucceedsSlantEqual;": { "codepoints": [8829], "characters": "\u227D" },
+ "&SucceedsTilde;": { "codepoints": [8831], "characters": "\u227F" },
+ "&succeq;": { "codepoints": [10928], "characters": "\u2AB0" },
+ "&succnapprox;": { "codepoints": [10938], "characters": "\u2ABA" },
+ "&succneqq;": { "codepoints": [10934], "characters": "\u2AB6" },
+ "&succnsim;": { "codepoints": [8937], "characters": "\u22E9" },
+ "&succsim;": { "codepoints": [8831], "characters": "\u227F" },
+ "&SuchThat;": { "codepoints": [8715], "characters": "\u220B" },
+ "&sum;": { "codepoints": [8721], "characters": "\u2211" },
+ "&Sum;": { "codepoints": [8721], "characters": "\u2211" },
+ "&sung;": { "codepoints": [9834], "characters": "\u266A" },
+ "&sup1;": { "codepoints": [185], "characters": "\u00B9" },
+ "&sup1": { "codepoints": [185], "characters": "\u00B9" },
+ "&sup2;": { "codepoints": [178], "characters": "\u00B2" },
+ "&sup2": { "codepoints": [178], "characters": "\u00B2" },
+ "&sup3;": { "codepoints": [179], "characters": "\u00B3" },
+ "&sup3": { "codepoints": [179], "characters": "\u00B3" },
+ "&sup;": { "codepoints": [8835], "characters": "\u2283" },
+ "&Sup;": { "codepoints": [8913], "characters": "\u22D1" },
+ "&supdot;": { "codepoints": [10942], "characters": "\u2ABE" },
+ "&supdsub;": { "codepoints": [10968], "characters": "\u2AD8" },
+ "&supE;": { "codepoints": [10950], "characters": "\u2AC6" },
+ "&supe;": { "codepoints": [8839], "characters": "\u2287" },
+ "&supedot;": { "codepoints": [10948], "characters": "\u2AC4" },
+ "&Superset;": { "codepoints": [8835], "characters": "\u2283" },
+ "&SupersetEqual;": { "codepoints": [8839], "characters": "\u2287" },
+ "&suphsol;": { "codepoints": [10185], "characters": "\u27C9" },
+ "&suphsub;": { "codepoints": [10967], "characters": "\u2AD7" },
+ "&suplarr;": { "codepoints": [10619], "characters": "\u297B" },
+ "&supmult;": { "codepoints": [10946], "characters": "\u2AC2" },
+ "&supnE;": { "codepoints": [10956], "characters": "\u2ACC" },
+ "&supne;": { "codepoints": [8843], "characters": "\u228B" },
+ "&supplus;": { "codepoints": [10944], "characters": "\u2AC0" },
+ "&supset;": { "codepoints": [8835], "characters": "\u2283" },
+ "&Supset;": { "codepoints": [8913], "characters": "\u22D1" },
+ "&supseteq;": { "codepoints": [8839], "characters": "\u2287" },
+ "&supseteqq;": { "codepoints": [10950], "characters": "\u2AC6" },
+ "&supsetneq;": { "codepoints": [8843], "characters": "\u228B" },
+ "&supsetneqq;": { "codepoints": [10956], "characters": "\u2ACC" },
+ "&supsim;": { "codepoints": [10952], "characters": "\u2AC8" },
+ "&supsub;": { "codepoints": [10964], "characters": "\u2AD4" },
+ "&supsup;": { "codepoints": [10966], "characters": "\u2AD6" },
+ "&swarhk;": { "codepoints": [10534], "characters": "\u2926" },
+ "&swarr;": { "codepoints": [8601], "characters": "\u2199" },
+ "&swArr;": { "codepoints": [8665], "characters": "\u21D9" },
+ "&swarrow;": { "codepoints": [8601], "characters": "\u2199" },
+ "&swnwar;": { "codepoints": [10538], "characters": "\u292A" },
+ "&szlig;": { "codepoints": [223], "characters": "\u00DF" },
+ "&szlig": { "codepoints": [223], "characters": "\u00DF" },
+ "&Tab;": { "codepoints": [9], "characters": "\u0009" },
+ "&target;": { "codepoints": [8982], "characters": "\u2316" },
+ "&Tau;": { "codepoints": [932], "characters": "\u03A4" },
+ "&tau;": { "codepoints": [964], "characters": "\u03C4" },
+ "&tbrk;": { "codepoints": [9140], "characters": "\u23B4" },
+ "&Tcaron;": { "codepoints": [356], "characters": "\u0164" },
+ "&tcaron;": { "codepoints": [357], "characters": "\u0165" },
+ "&Tcedil;": { "codepoints": [354], "characters": "\u0162" },
+ "&tcedil;": { "codepoints": [355], "characters": "\u0163" },
+ "&Tcy;": { "codepoints": [1058], "characters": "\u0422" },
+ "&tcy;": { "codepoints": [1090], "characters": "\u0442" },
+ "&tdot;": { "codepoints": [8411], "characters": "\u20DB" },
+ "&telrec;": { "codepoints": [8981], "characters": "\u2315" },
+ "&Tfr;": { "codepoints": [120087], "characters": "\uD835\uDD17" },
+ "&tfr;": { "codepoints": [120113], "characters": "\uD835\uDD31" },
+ "&there4;": { "codepoints": [8756], "characters": "\u2234" },
+ "&therefore;": { "codepoints": [8756], "characters": "\u2234" },
+ "&Therefore;": { "codepoints": [8756], "characters": "\u2234" },
+ "&Theta;": { "codepoints": [920], "characters": "\u0398" },
+ "&theta;": { "codepoints": [952], "characters": "\u03B8" },
+ "&thetasym;": { "codepoints": [977], "characters": "\u03D1" },
+ "&thetav;": { "codepoints": [977], "characters": "\u03D1" },
+ "&thickapprox;": { "codepoints": [8776], "characters": "\u2248" },
+ "&thicksim;": { "codepoints": [8764], "characters": "\u223C" },
+ "&ThickSpace;": { "codepoints": [8287, 8202], "characters": "\u205F\u200A" },
+ "&ThinSpace;": { "codepoints": [8201], "characters": "\u2009" },
+ "&thinsp;": { "codepoints": [8201], "characters": "\u2009" },
+ "&thkap;": { "codepoints": [8776], "characters": "\u2248" },
+ "&thksim;": { "codepoints": [8764], "characters": "\u223C" },
+ "&THORN;": { "codepoints": [222], "characters": "\u00DE" },
+ "&THORN": { "codepoints": [222], "characters": "\u00DE" },
+ "&thorn;": { "codepoints": [254], "characters": "\u00FE" },
+ "&thorn": { "codepoints": [254], "characters": "\u00FE" },
+ "&tilde;": { "codepoints": [732], "characters": "\u02DC" },
+ "&Tilde;": { "codepoints": [8764], "characters": "\u223C" },
+ "&TildeEqual;": { "codepoints": [8771], "characters": "\u2243" },
+ "&TildeFullEqual;": { "codepoints": [8773], "characters": "\u2245" },
+ "&TildeTilde;": { "codepoints": [8776], "characters": "\u2248" },
+ "&timesbar;": { "codepoints": [10801], "characters": "\u2A31" },
+ "&timesb;": { "codepoints": [8864], "characters": "\u22A0" },
+ "&times;": { "codepoints": [215], "characters": "\u00D7" },
+ "&times": { "codepoints": [215], "characters": "\u00D7" },
+ "&timesd;": { "codepoints": [10800], "characters": "\u2A30" },
+ "&tint;": { "codepoints": [8749], "characters": "\u222D" },
+ "&toea;": { "codepoints": [10536], "characters": "\u2928" },
+ "&topbot;": { "codepoints": [9014], "characters": "\u2336" },
+ "&topcir;": { "codepoints": [10993], "characters": "\u2AF1" },
+ "&top;": { "codepoints": [8868], "characters": "\u22A4" },
+ "&Topf;": { "codepoints": [120139], "characters": "\uD835\uDD4B" },
+ "&topf;": { "codepoints": [120165], "characters": "\uD835\uDD65" },
+ "&topfork;": { "codepoints": [10970], "characters": "\u2ADA" },
+ "&tosa;": { "codepoints": [10537], "characters": "\u2929" },
+ "&tprime;": { "codepoints": [8244], "characters": "\u2034" },
+ "&trade;": { "codepoints": [8482], "characters": "\u2122" },
+ "&TRADE;": { "codepoints": [8482], "characters": "\u2122" },
+ "&triangle;": { "codepoints": [9653], "characters": "\u25B5" },
+ "&triangledown;": { "codepoints": [9663], "characters": "\u25BF" },
+ "&triangleleft;": { "codepoints": [9667], "characters": "\u25C3" },
+ "&trianglelefteq;": { "codepoints": [8884], "characters": "\u22B4" },
+ "&triangleq;": { "codepoints": [8796], "characters": "\u225C" },
+ "&triangleright;": { "codepoints": [9657], "characters": "\u25B9" },
+ "&trianglerighteq;": { "codepoints": [8885], "characters": "\u22B5" },
+ "&tridot;": { "codepoints": [9708], "characters": "\u25EC" },
+ "&trie;": { "codepoints": [8796], "characters": "\u225C" },
+ "&triminus;": { "codepoints": [10810], "characters": "\u2A3A" },
+ "&TripleDot;": { "codepoints": [8411], "characters": "\u20DB" },
+ "&triplus;": { "codepoints": [10809], "characters": "\u2A39" },
+ "&trisb;": { "codepoints": [10701], "characters": "\u29CD" },
+ "&tritime;": { "codepoints": [10811], "characters": "\u2A3B" },
+ "&trpezium;": { "codepoints": [9186], "characters": "\u23E2" },
+ "&Tscr;": { "codepoints": [119983], "characters": "\uD835\uDCAF" },
+ "&tscr;": { "codepoints": [120009], "characters": "\uD835\uDCC9" },
+ "&TScy;": { "codepoints": [1062], "characters": "\u0426" },
+ "&tscy;": { "codepoints": [1094], "characters": "\u0446" },
+ "&TSHcy;": { "codepoints": [1035], "characters": "\u040B" },
+ "&tshcy;": { "codepoints": [1115], "characters": "\u045B" },
+ "&Tstrok;": { "codepoints": [358], "characters": "\u0166" },
+ "&tstrok;": { "codepoints": [359], "characters": "\u0167" },
+ "&twixt;": { "codepoints": [8812], "characters": "\u226C" },
+ "&twoheadleftarrow;": { "codepoints": [8606], "characters": "\u219E" },
+ "&twoheadrightarrow;": { "codepoints": [8608], "characters": "\u21A0" },
+ "&Uacute;": { "codepoints": [218], "characters": "\u00DA" },
+ "&Uacute": { "codepoints": [218], "characters": "\u00DA" },
+ "&uacute;": { "codepoints": [250], "characters": "\u00FA" },
+ "&uacute": { "codepoints": [250], "characters": "\u00FA" },
+ "&uarr;": { "codepoints": [8593], "characters": "\u2191" },
+ "&Uarr;": { "codepoints": [8607], "characters": "\u219F" },
+ "&uArr;": { "codepoints": [8657], "characters": "\u21D1" },
+ "&Uarrocir;": { "codepoints": [10569], "characters": "\u2949" },
+ "&Ubrcy;": { "codepoints": [1038], "characters": "\u040E" },
+ "&ubrcy;": { "codepoints": [1118], "characters": "\u045E" },
+ "&Ubreve;": { "codepoints": [364], "characters": "\u016C" },
+ "&ubreve;": { "codepoints": [365], "characters": "\u016D" },
+ "&Ucirc;": { "codepoints": [219], "characters": "\u00DB" },
+ "&Ucirc": { "codepoints": [219], "characters": "\u00DB" },
+ "&ucirc;": { "codepoints": [251], "characters": "\u00FB" },
+ "&ucirc": { "codepoints": [251], "characters": "\u00FB" },
+ "&Ucy;": { "codepoints": [1059], "characters": "\u0423" },
+ "&ucy;": { "codepoints": [1091], "characters": "\u0443" },
+ "&udarr;": { "codepoints": [8645], "characters": "\u21C5" },
+ "&Udblac;": { "codepoints": [368], "characters": "\u0170" },
+ "&udblac;": { "codepoints": [369], "characters": "\u0171" },
+ "&udhar;": { "codepoints": [10606], "characters": "\u296E" },
+ "&ufisht;": { "codepoints": [10622], "characters": "\u297E" },
+ "&Ufr;": { "codepoints": [120088], "characters": "\uD835\uDD18" },
+ "&ufr;": { "codepoints": [120114], "characters": "\uD835\uDD32" },
+ "&Ugrave;": { "codepoints": [217], "characters": "\u00D9" },
+ "&Ugrave": { "codepoints": [217], "characters": "\u00D9" },
+ "&ugrave;": { "codepoints": [249], "characters": "\u00F9" },
+ "&ugrave": { "codepoints": [249], "characters": "\u00F9" },
+ "&uHar;": { "codepoints": [10595], "characters": "\u2963" },
+ "&uharl;": { "codepoints": [8639], "characters": "\u21BF" },
+ "&uharr;": { "codepoints": [8638], "characters": "\u21BE" },
+ "&uhblk;": { "codepoints": [9600], "characters": "\u2580" },
+ "&ulcorn;": { "codepoints": [8988], "characters": "\u231C" },
+ "&ulcorner;": { "codepoints": [8988], "characters": "\u231C" },
+ "&ulcrop;": { "codepoints": [8975], "characters": "\u230F" },
+ "&ultri;": { "codepoints": [9720], "characters": "\u25F8" },
+ "&Umacr;": { "codepoints": [362], "characters": "\u016A" },
+ "&umacr;": { "codepoints": [363], "characters": "\u016B" },
+ "&uml;": { "codepoints": [168], "characters": "\u00A8" },
+ "&uml": { "codepoints": [168], "characters": "\u00A8" },
+ "&UnderBar;": { "codepoints": [95], "characters": "\u005F" },
+ "&UnderBrace;": { "codepoints": [9183], "characters": "\u23DF" },
+ "&UnderBracket;": { "codepoints": [9141], "characters": "\u23B5" },
+ "&UnderParenthesis;": { "codepoints": [9181], "characters": "\u23DD" },
+ "&Union;": { "codepoints": [8899], "characters": "\u22C3" },
+ "&UnionPlus;": { "codepoints": [8846], "characters": "\u228E" },
+ "&Uogon;": { "codepoints": [370], "characters": "\u0172" },
+ "&uogon;": { "codepoints": [371], "characters": "\u0173" },
+ "&Uopf;": { "codepoints": [120140], "characters": "\uD835\uDD4C" },
+ "&uopf;": { "codepoints": [120166], "characters": "\uD835\uDD66" },
+ "&UpArrowBar;": { "codepoints": [10514], "characters": "\u2912" },
+ "&uparrow;": { "codepoints": [8593], "characters": "\u2191" },
+ "&UpArrow;": { "codepoints": [8593], "characters": "\u2191" },
+ "&Uparrow;": { "codepoints": [8657], "characters": "\u21D1" },
+ "&UpArrowDownArrow;": { "codepoints": [8645], "characters": "\u21C5" },
+ "&updownarrow;": { "codepoints": [8597], "characters": "\u2195" },
+ "&UpDownArrow;": { "codepoints": [8597], "characters": "\u2195" },
+ "&Updownarrow;": { "codepoints": [8661], "characters": "\u21D5" },
+ "&UpEquilibrium;": { "codepoints": [10606], "characters": "\u296E" },
+ "&upharpoonleft;": { "codepoints": [8639], "characters": "\u21BF" },
+ "&upharpoonright;": { "codepoints": [8638], "characters": "\u21BE" },
+ "&uplus;": { "codepoints": [8846], "characters": "\u228E" },
+ "&UpperLeftArrow;": { "codepoints": [8598], "characters": "\u2196" },
+ "&UpperRightArrow;": { "codepoints": [8599], "characters": "\u2197" },
+ "&upsi;": { "codepoints": [965], "characters": "\u03C5" },
+ "&Upsi;": { "codepoints": [978], "characters": "\u03D2" },
+ "&upsih;": { "codepoints": [978], "characters": "\u03D2" },
+ "&Upsilon;": { "codepoints": [933], "characters": "\u03A5" },
+ "&upsilon;": { "codepoints": [965], "characters": "\u03C5" },
+ "&UpTeeArrow;": { "codepoints": [8613], "characters": "\u21A5" },
+ "&UpTee;": { "codepoints": [8869], "characters": "\u22A5" },
+ "&upuparrows;": { "codepoints": [8648], "characters": "\u21C8" },
+ "&urcorn;": { "codepoints": [8989], "characters": "\u231D" },
+ "&urcorner;": { "codepoints": [8989], "characters": "\u231D" },
+ "&urcrop;": { "codepoints": [8974], "characters": "\u230E" },
+ "&Uring;": { "codepoints": [366], "characters": "\u016E" },
+ "&uring;": { "codepoints": [367], "characters": "\u016F" },
+ "&urtri;": { "codepoints": [9721], "characters": "\u25F9" },
+ "&Uscr;": { "codepoints": [119984], "characters": "\uD835\uDCB0" },
+ "&uscr;": { "codepoints": [120010], "characters": "\uD835\uDCCA" },
+ "&utdot;": { "codepoints": [8944], "characters": "\u22F0" },
+ "&Utilde;": { "codepoints": [360], "characters": "\u0168" },
+ "&utilde;": { "codepoints": [361], "characters": "\u0169" },
+ "&utri;": { "codepoints": [9653], "characters": "\u25B5" },
+ "&utrif;": { "codepoints": [9652], "characters": "\u25B4" },
+ "&uuarr;": { "codepoints": [8648], "characters": "\u21C8" },
+ "&Uuml;": { "codepoints": [220], "characters": "\u00DC" },
+ "&Uuml": { "codepoints": [220], "characters": "\u00DC" },
+ "&uuml;": { "codepoints": [252], "characters": "\u00FC" },
+ "&uuml": { "codepoints": [252], "characters": "\u00FC" },
+ "&uwangle;": { "codepoints": [10663], "characters": "\u29A7" },
+ "&vangrt;": { "codepoints": [10652], "characters": "\u299C" },
+ "&varepsilon;": { "codepoints": [1013], "characters": "\u03F5" },
+ "&varkappa;": { "codepoints": [1008], "characters": "\u03F0" },
+ "&varnothing;": { "codepoints": [8709], "characters": "\u2205" },
+ "&varphi;": { "codepoints": [981], "characters": "\u03D5" },
+ "&varpi;": { "codepoints": [982], "characters": "\u03D6" },
+ "&varpropto;": { "codepoints": [8733], "characters": "\u221D" },
+ "&varr;": { "codepoints": [8597], "characters": "\u2195" },
+ "&vArr;": { "codepoints": [8661], "characters": "\u21D5" },
+ "&varrho;": { "codepoints": [1009], "characters": "\u03F1" },
+ "&varsigma;": { "codepoints": [962], "characters": "\u03C2" },
+ "&varsubsetneq;": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" },
+ "&varsubsetneqq;": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" },
+ "&varsupsetneq;": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" },
+ "&varsupsetneqq;": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" },
+ "&vartheta;": { "codepoints": [977], "characters": "\u03D1" },
+ "&vartriangleleft;": { "codepoints": [8882], "characters": "\u22B2" },
+ "&vartriangleright;": { "codepoints": [8883], "characters": "\u22B3" },
+ "&vBar;": { "codepoints": [10984], "characters": "\u2AE8" },
+ "&Vbar;": { "codepoints": [10987], "characters": "\u2AEB" },
+ "&vBarv;": { "codepoints": [10985], "characters": "\u2AE9" },
+ "&Vcy;": { "codepoints": [1042], "characters": "\u0412" },
+ "&vcy;": { "codepoints": [1074], "characters": "\u0432" },
+ "&vdash;": { "codepoints": [8866], "characters": "\u22A2" },
+ "&vDash;": { "codepoints": [8872], "characters": "\u22A8" },
+ "&Vdash;": { "codepoints": [8873], "characters": "\u22A9" },
+ "&VDash;": { "codepoints": [8875], "characters": "\u22AB" },
+ "&Vdashl;": { "codepoints": [10982], "characters": "\u2AE6" },
+ "&veebar;": { "codepoints": [8891], "characters": "\u22BB" },
+ "&vee;": { "codepoints": [8744], "characters": "\u2228" },
+ "&Vee;": { "codepoints": [8897], "characters": "\u22C1" },
+ "&veeeq;": { "codepoints": [8794], "characters": "\u225A" },
+ "&vellip;": { "codepoints": [8942], "characters": "\u22EE" },
+ "&verbar;": { "codepoints": [124], "characters": "\u007C" },
+ "&Verbar;": { "codepoints": [8214], "characters": "\u2016" },
+ "&vert;": { "codepoints": [124], "characters": "\u007C" },
+ "&Vert;": { "codepoints": [8214], "characters": "\u2016" },
+ "&VerticalBar;": { "codepoints": [8739], "characters": "\u2223" },
+ "&VerticalLine;": { "codepoints": [124], "characters": "\u007C" },
+ "&VerticalSeparator;": { "codepoints": [10072], "characters": "\u2758" },
+ "&VerticalTilde;": { "codepoints": [8768], "characters": "\u2240" },
+ "&VeryThinSpace;": { "codepoints": [8202], "characters": "\u200A" },
+ "&Vfr;": { "codepoints": [120089], "characters": "\uD835\uDD19" },
+ "&vfr;": { "codepoints": [120115], "characters": "\uD835\uDD33" },
+ "&vltri;": { "codepoints": [8882], "characters": "\u22B2" },
+ "&vnsub;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+ "&vnsup;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+ "&Vopf;": { "codepoints": [120141], "characters": "\uD835\uDD4D" },
+ "&vopf;": { "codepoints": [120167], "characters": "\uD835\uDD67" },
+ "&vprop;": { "codepoints": [8733], "characters": "\u221D" },
+ "&vrtri;": { "codepoints": [8883], "characters": "\u22B3" },
+ "&Vscr;": { "codepoints": [119985], "characters": "\uD835\uDCB1" },
+ "&vscr;": { "codepoints": [120011], "characters": "\uD835\uDCCB" },
+ "&vsubnE;": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" },
+ "&vsubne;": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" },
+ "&vsupnE;": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" },
+ "&vsupne;": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" },
+ "&Vvdash;": { "codepoints": [8874], "characters": "\u22AA" },
+ "&vzigzag;": { "codepoints": [10650], "characters": "\u299A" },
+ "&Wcirc;": { "codepoints": [372], "characters": "\u0174" },
+ "&wcirc;": { "codepoints": [373], "characters": "\u0175" },
+ "&wedbar;": { "codepoints": [10847], "characters": "\u2A5F" },
+ "&wedge;": { "codepoints": [8743], "characters": "\u2227" },
+ "&Wedge;": { "codepoints": [8896], "characters": "\u22C0" },
+ "&wedgeq;": { "codepoints": [8793], "characters": "\u2259" },
+ "&weierp;": { "codepoints": [8472], "characters": "\u2118" },
+ "&Wfr;": { "codepoints": [120090], "characters": "\uD835\uDD1A" },
+ "&wfr;": { "codepoints": [120116], "characters": "\uD835\uDD34" },
+ "&Wopf;": { "codepoints": [120142], "characters": "\uD835\uDD4E" },
+ "&wopf;": { "codepoints": [120168], "characters": "\uD835\uDD68" },
+ "&wp;": { "codepoints": [8472], "characters": "\u2118" },
+ "&wr;": { "codepoints": [8768], "characters": "\u2240" },
+ "&wreath;": { "codepoints": [8768], "characters": "\u2240" },
+ "&Wscr;": { "codepoints": [119986], "characters": "\uD835\uDCB2" },
+ "&wscr;": { "codepoints": [120012], "characters": "\uD835\uDCCC" },
+ "&xcap;": { "codepoints": [8898], "characters": "\u22C2" },
+ "&xcirc;": { "codepoints": [9711], "characters": "\u25EF" },
+ "&xcup;": { "codepoints": [8899], "characters": "\u22C3" },
+ "&xdtri;": { "codepoints": [9661], "characters": "\u25BD" },
+ "&Xfr;": { "codepoints": [120091], "characters": "\uD835\uDD1B" },
+ "&xfr;": { "codepoints": [120117], "characters": "\uD835\uDD35" },
+ "&xharr;": { "codepoints": [10231], "characters": "\u27F7" },
+ "&xhArr;": { "codepoints": [10234], "characters": "\u27FA" },
+ "&Xi;": { "codepoints": [926], "characters": "\u039E" },
+ "&xi;": { "codepoints": [958], "characters": "\u03BE" },
+ "&xlarr;": { "codepoints": [10229], "characters": "\u27F5" },
+ "&xlArr;": { "codepoints": [10232], "characters": "\u27F8" },
+ "&xmap;": { "codepoints": [10236], "characters": "\u27FC" },
+ "&xnis;": { "codepoints": [8955], "characters": "\u22FB" },
+ "&xodot;": { "codepoints": [10752], "characters": "\u2A00" },
+ "&Xopf;": { "codepoints": [120143], "characters": "\uD835\uDD4F" },
+ "&xopf;": { "codepoints": [120169], "characters": "\uD835\uDD69" },
+ "&xoplus;": { "codepoints": [10753], "characters": "\u2A01" },
+ "&xotime;": { "codepoints": [10754], "characters": "\u2A02" },
+ "&xrarr;": { "codepoints": [10230], "characters": "\u27F6" },
+ "&xrArr;": { "codepoints": [10233], "characters": "\u27F9" },
+ "&Xscr;": { "codepoints": [119987], "characters": "\uD835\uDCB3" },
+ "&xscr;": { "codepoints": [120013], "characters": "\uD835\uDCCD" },
+ "&xsqcup;": { "codepoints": [10758], "characters": "\u2A06" },
+ "&xuplus;": { "codepoints": [10756], "characters": "\u2A04" },
+ "&xutri;": { "codepoints": [9651], "characters": "\u25B3" },
+ "&xvee;": { "codepoints": [8897], "characters": "\u22C1" },
+ "&xwedge;": { "codepoints": [8896], "characters": "\u22C0" },
+ "&Yacute;": { "codepoints": [221], "characters": "\u00DD" },
+ "&Yacute": { "codepoints": [221], "characters": "\u00DD" },
+ "&yacute;": { "codepoints": [253], "characters": "\u00FD" },
+ "&yacute": { "codepoints": [253], "characters": "\u00FD" },
+ "&YAcy;": { "codepoints": [1071], "characters": "\u042F" },
+ "&yacy;": { "codepoints": [1103], "characters": "\u044F" },
+ "&Ycirc;": { "codepoints": [374], "characters": "\u0176" },
+ "&ycirc;": { "codepoints": [375], "characters": "\u0177" },
+ "&Ycy;": { "codepoints": [1067], "characters": "\u042B" },
+ "&ycy;": { "codepoints": [1099], "characters": "\u044B" },
+ "&yen;": { "codepoints": [165], "characters": "\u00A5" },
+ "&yen": { "codepoints": [165], "characters": "\u00A5" },
+ "&Yfr;": { "codepoints": [120092], "characters": "\uD835\uDD1C" },
+ "&yfr;": { "codepoints": [120118], "characters": "\uD835\uDD36" },
+ "&YIcy;": { "codepoints": [1031], "characters": "\u0407" },
+ "&yicy;": { "codepoints": [1111], "characters": "\u0457" },
+ "&Yopf;": { "codepoints": [120144], "characters": "\uD835\uDD50" },
+ "&yopf;": { "codepoints": [120170], "characters": "\uD835\uDD6A" },
+ "&Yscr;": { "codepoints": [119988], "characters": "\uD835\uDCB4" },
+ "&yscr;": { "codepoints": [120014], "characters": "\uD835\uDCCE" },
+ "&YUcy;": { "codepoints": [1070], "characters": "\u042E" },
+ "&yucy;": { "codepoints": [1102], "characters": "\u044E" },
+ "&yuml;": { "codepoints": [255], "characters": "\u00FF" },
+ "&yuml": { "codepoints": [255], "characters": "\u00FF" },
+ "&Yuml;": { "codepoints": [376], "characters": "\u0178" },
+ "&Zacute;": { "codepoints": [377], "characters": "\u0179" },
+ "&zacute;": { "codepoints": [378], "characters": "\u017A" },
+ "&Zcaron;": { "codepoints": [381], "characters": "\u017D" },
+ "&zcaron;": { "codepoints": [382], "characters": "\u017E" },
+ "&Zcy;": { "codepoints": [1047], "characters": "\u0417" },
+ "&zcy;": { "codepoints": [1079], "characters": "\u0437" },
+ "&Zdot;": { "codepoints": [379], "characters": "\u017B" },
+ "&zdot;": { "codepoints": [380], "characters": "\u017C" },
+ "&zeetrf;": { "codepoints": [8488], "characters": "\u2128" },
+ "&ZeroWidthSpace;": { "codepoints": [8203], "characters": "\u200B" },
+ "&Zeta;": { "codepoints": [918], "characters": "\u0396" },
+ "&zeta;": { "codepoints": [950], "characters": "\u03B6" },
+ "&zfr;": { "codepoints": [120119], "characters": "\uD835\uDD37" },
+ "&Zfr;": { "codepoints": [8488], "characters": "\u2128" },
+ "&ZHcy;": { "codepoints": [1046], "characters": "\u0416" },
+ "&zhcy;": { "codepoints": [1078], "characters": "\u0436" },
+ "&zigrarr;": { "codepoints": [8669], "characters": "\u21DD" },
+ "&zopf;": { "codepoints": [120171], "characters": "\uD835\uDD6B" },
+ "&Zopf;": { "codepoints": [8484], "characters": "\u2124" },
+ "&Zscr;": { "codepoints": [119989], "characters": "\uD835\uDCB5" },
+ "&zscr;": { "codepoints": [120015], "characters": "\uD835\uDCCF" },
+ "&zwj;": { "codepoints": [8205], "characters": "\u200D" },
+ "&zwnj;": { "codepoints": [8204], "characters": "\u200C" }
+}
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/xhtml-mathml-dtd-entity.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/xhtml-mathml-dtd-entity.htm
new file mode 100644
index 0000000000..af3fe90284
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/support/xhtml-mathml-dtd-entity.htm
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script>
+ var parser = new DOMParser();
+ var parse = parser.parseFromString.bind(parser);
+
+ function generateTestFunction(entitystring, expectedString, publicId, systemId, mimeType, friendlyMime) {
+ return function () {
+ var doctypeString = '<!DOCTYPE html';
+ if (publicId != null)
+ doctypeString += ' PUBLIC "' + publicId + '" "' + systemId + '">';
+ else if (systemId != null)
+ doctypeString += ' SYSTEM "' + systemId + '">';
+ else // both are null
+ doctypeString += '>';
+ var doc = parse(doctypeString + "<html><head></head><body id='test'>"+entitystring+"</body></html>", mimeType);
+ var root = doc.getElementById('test');
+ parent.assert_not_equals(root, null, friendlyMime + " parsing the entity reference caused a parse error;");
+ parent.assert_true(!!root.firstChild);
+ // Next line because some browsers include the partial parsed result in the parser error returned document.
+ parent.assert_equals(root.firstChild.nodeType, 3/*Text*/, friendlyMime + " parsing the entity reference caused a parse error;");
+ var text = root.firstChild.data;
+ for (var i = 0, len = expectedString.length; i < len; i++) {
+ parent.assert_equals(text.charCodeAt(i),expectedString.charCodeAt(i));
+ }
+ }
+ }
+
+ function setupTests(jsonEntities, publicId, systemId, mimeType, friendlyMime) {
+ for (entityName in jsonEntities) {
+ if ((mimeType == "text/html") || /;$/.test(entityName)) {
+ parent.test(generateTestFunction(entityName, jsonEntities[entityName].characters, publicId, systemId, mimeType, friendlyMime), friendlyMime + " parsing " + entityName);
+ }
+ }
+ }
+
+ parent.setup(function() {}, {explicit_done: true});
+
+ function run(row) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "entities.json");
+ xhr.onload = function () {
+ var entitiesJSON = JSON.parse(xhr.response);
+ setupTests(entitiesJSON, row[1], row[2], row[0], row[3]);
+ parent.done();
+ }
+ xhr.send();
+ }
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-1.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-1.htm
new file mode 100644
index 0000000000..a5d2015890
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-1.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD XHTML 1.0 Transitional//EN", "foo", "XHTML1.0 Transitional"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-10.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-10.htm
new file mode 100644
index 0000000000..a8ca0c2256
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-10.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["text/html", null, null, "HTML"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-2.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-2.htm
new file mode 100644
index 0000000000..d3e942d528
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-2.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD XHTML 1.1//EN", "foo", "XHTML1.1"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-3.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-3.htm
new file mode 100644
index 0000000000..b375d50a8d
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-3.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD XHTML 1.0 Strict//EN", "foo", "XHTML1.0 Strict"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-4.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-4.htm
new file mode 100644
index 0000000000..dda2893d27
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-4.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD XHTML 1.0 Frameset//EN", "foo", "XHTML1.0 Frameset"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-5.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-5.htm
new file mode 100644
index 0000000000..2fedc6be55
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-5.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD XHTML Basic 1.0//EN", "foo", "XHTML Basic"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-6.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-6.htm
new file mode 100644
index 0000000000..c7f7549ac4
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-6.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN", "foo", "XHTML1.1+MathML"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-7.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-7.htm
new file mode 100644
index 0000000000..1e61c2fa6b
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-7.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN", "foo", "XHTML1.1+MathML+SVG"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-8.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-8.htm
new file mode 100644
index 0000000000..cf809063e6
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-8.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//W3C//DTD MathML 2.0//EN", "foo", "MathML"]);
+</script>
diff --git a/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-9.htm b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-9.htm
new file mode 100644
index 0000000000..8f71d48e8a
--- /dev/null
+++ b/testing/web-platform/tests/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-9.htm
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>HTML entities for various XHTML Doctype</title>
+<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
+
+<script>
+onload = () => document.getElementById("test").contentWindow.run(
+["application/xhtml+xml", "-//WAPFORUM//DTD XHTML Mobile 1.0//EN", "foo", "XHTML Mobile"]);
+</script>
diff --git a/testing/web-platform/tests/html/tools/README b/testing/web-platform/tests/html/tools/README
new file mode 100644
index 0000000000..2ebde65fcd
--- /dev/null
+++ b/testing/web-platform/tests/html/tools/README
@@ -0,0 +1 @@
+See ../syntax/parsing/README.
diff --git a/testing/web-platform/tests/html/tools/html5lib_python_revision b/testing/web-platform/tests/html/tools/html5lib_python_revision
new file mode 100644
index 0000000000..e0f5dd464a
--- /dev/null
+++ b/testing/web-platform/tests/html/tools/html5lib_python_revision
@@ -0,0 +1 @@
+f7cab6f019ce94a1ec0192b6ff29aaebaf10b50d \ No newline at end of file
diff --git a/testing/web-platform/tests/html/tools/html5lib_test.xml b/testing/web-platform/tests/html/tools/html5lib_test.xml
new file mode 100644
index 0000000000..8af4adce16
--- /dev/null
+++ b/testing/web-platform/tests/html/tools/html5lib_test.xml
@@ -0,0 +1,29 @@
+<html xmlns:py="http://genshi.edgewall.org/">
+ <head>
+ <meta charset="utf8"/>
+ <title>HTML 5 Parser tests ${file_name}</title>
+ <meta name="timeout" content="long"/>
+ <meta name="variant" content="?run_type=uri"/>
+ <meta name="variant" content="?run_type=write"/>
+ <meta name="variant" content="?run_type=write_single"/>
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ var num_iframes = 8;
+
+ var order = [<py:for each="test in tests">'${test.id}',</py:for>];
+ var tests = {
+ <py:for each="test in tests">"${test.id}":[async_test('${file_name} ${test.id}'), ${test.string_uri_encoded_input}, ${test.string_escaped_expected}],</py:for>
+ }
+ init_tests(get_type());
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/tools/html5lib_test_fragment.xml b/testing/web-platform/tests/html/tools/html5lib_test_fragment.xml
new file mode 100644
index 0000000000..794d13f1c4
--- /dev/null
+++ b/testing/web-platform/tests/html/tools/html5lib_test_fragment.xml
@@ -0,0 +1,28 @@
+<html xmlns:py="http://genshi.edgewall.org/">
+ <head>
+ <meta charset="utf8"/>
+ <title>HTML 5 Parser tests ${file_name}</title>
+ <meta name="timeout" content="long"/>
+ </head>
+ <body>
+ <h1>html5lib Parser Test</h1>
+ <div id="log"></div>
+ <script src="common.js"></script>
+ <script src="test.js"></script>
+ <script src="template.js"></script>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+
+ var num_iframes = 8;
+
+ var order = [<py:for each="test in tests">'${test.id}',</py:for>];
+ var tests = {
+ <py:for each="test in tests">"${test.id}":[async_test('${file_name} ${test.id}'), ${test.string_uri_encoded_input}, ${test.string_escaped_expected}, '${test.container}'],</py:for>
+ }
+
+ init_tests("innerHTML");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/tools/html5lib_tests_revision b/testing/web-platform/tests/html/tools/html5lib_tests_revision
new file mode 100644
index 0000000000..3ea6d572fd
--- /dev/null
+++ b/testing/web-platform/tests/html/tools/html5lib_tests_revision
@@ -0,0 +1 @@
+a9f44960a9fedf265093d22b2aa3c7ca123727b9
diff --git a/testing/web-platform/tests/html/tools/update_html5lib_tests.py b/testing/web-platform/tests/html/tools/update_html5lib_tests.py
new file mode 100644
index 0000000000..2374bb0cda
--- /dev/null
+++ b/testing/web-platform/tests/html/tools/update_html5lib_tests.py
@@ -0,0 +1,201 @@
+import glob
+import hashlib
+import itertools
+import json
+import os
+import re
+import shutil
+import site
+import subprocess
+import sys
+import tempfile
+import urllib
+from importlib import reload
+
+
+import genshi
+from genshi.template import MarkupTemplate
+
+
+TESTS_PATH = "html/syntax/parsing/"
+
+def get_paths():
+ script_path = os.path.dirname(os.path.abspath(__file__))
+ repo_base = get_repo_base(script_path)
+ tests_path = os.path.join(repo_base, TESTS_PATH)
+ return script_path, tests_path
+
+
+def get_repo_base(path):
+ while path:
+ if os.path.exists(os.path.join(path, ".git")):
+ return path
+ else:
+ path = os.path.dirname(path)
+
+
+def get_expected(data):
+ data = "#document\n" + data
+ return data
+
+
+def get_hash(data, container=None):
+ if container == None:
+ container = ""
+ return hashlib.sha1(b"#container%s#data%s"%(container.encode("utf8"),
+ data.encode("utf8"))).hexdigest()
+
+
+class Html5libInstall:
+ def __init__(self, rev=None, tests_rev=None):
+ self.html5lib_dir = None
+ self.rev = rev
+ self.tests_rev = tests_rev
+
+ def __enter__(self):
+ self.html5lib_dir = tempfile.TemporaryDirectory()
+ html5lib_path = self.html5lib_dir.__enter__()
+ html5lib_python_path = os.path.join(html5lib_path, "html5lib")
+ html5lib_tests_path = os.path.join(
+ html5lib_python_path, "html5lib", "tests", "testdata"
+ )
+
+ subprocess.check_call(
+ [
+ "git",
+ "clone",
+ "--no-checkout",
+ "https://github.com/html5lib/html5lib-python.git",
+ "html5lib",
+ ],
+ cwd=html5lib_path,
+ )
+
+ rev = self.rev if self.rev is not None else "origin/master"
+ subprocess.check_call(
+ ["git", "checkout", rev], cwd=html5lib_python_path
+ )
+
+ subprocess.check_call(
+ [
+ "git",
+ "submodule",
+ "update",
+ "--init",
+ "--recursive",
+ ],
+ cwd=html5lib_python_path,
+ )
+
+ subprocess.check_call(["pip", "install", "-e", "html5lib"], cwd=html5lib_path)
+ reload(site)
+
+ tests_rev = self.tests_rev if self.tests_rev is not None else "origin/master"
+ subprocess.check_call(["git", "checkout", tests_rev], cwd=html5lib_tests_path)
+
+ def __exit__(self, *args, **kwargs):
+ subprocess.call(["pip", "uninstall", "-y", "html5lib"], cwd=self.html5lib_dir.name)
+ self.html5lib_dir.__exit__(*args, **kwargs)
+ self.html5lib_dir = None
+
+
+def make_tests(script_dir, out_dir, input_file_name, test_data):
+ tests = []
+ innerHTML_tests = []
+ ids_seen = {}
+ print(input_file_name)
+ for test in test_data:
+ if "script-off" in test:
+ continue
+ is_innerHTML = "document-fragment" in test
+ data = test["data"]
+ container = test["document-fragment"] if is_innerHTML else None
+ assert test["document"], test
+ expected = get_expected(test["document"])
+ test_list = innerHTML_tests if is_innerHTML else tests
+ test_id = get_hash(data, container)
+ if test_id in ids_seen:
+ print("WARNING: id %s seen multiple times in file %s this time for test (%s, %s) before for test %s, skipping"%(test_id, input_file_name, container, data, ids_seen[test_id]))
+ continue
+ ids_seen[test_id] = (container, data)
+ test_list.append({'string_uri_encoded_input':"\"%s\""%urllib.parse.quote(data.encode("utf8")),
+ 'input':data,
+ 'expected':expected,
+ 'string_escaped_expected':json.dumps(urllib.parse.quote(expected.encode("utf8"))),
+ 'id':test_id,
+ 'container':container
+ })
+ path_normal = None
+ if tests:
+ path_normal = write_test_file(script_dir, out_dir,
+ tests, "html5lib_%s"%input_file_name,
+ "html5lib_test.xml")
+ path_innerHTML = None
+ if innerHTML_tests:
+ path_innerHTML = write_test_file(script_dir, out_dir,
+ innerHTML_tests, "html5lib_innerHTML_%s"%input_file_name,
+ "html5lib_test_fragment.xml")
+
+ return path_normal, path_innerHTML
+
+def write_test_file(script_dir, out_dir, tests, file_name, template_file_name):
+ file_name = os.path.join(out_dir, file_name + ".html")
+ short_name = os.path.basename(file_name)
+
+ with open(os.path.join(script_dir, template_file_name), "r") as f:
+ template = MarkupTemplate(f)
+
+ stream = template.generate(file_name=short_name, tests=tests)
+
+ with open(file_name, "w") as f:
+ f.write(str(stream.render('html', doctype='html5',
+ encoding="utf8"), "utf-8"))
+ return file_name
+
+def escape_js_string(in_data):
+ return in_data.encode("utf8").encode("string-escape")
+
+def serialize_filenames(test_filenames):
+ return "[" + ",\n".join("\"%s\""%item for item in test_filenames) + "]"
+
+def main():
+ script_dir, out_dir = get_paths()
+
+ test_files = []
+ inner_html_files = []
+ with open(os.path.join(script_dir, "html5lib_python_revision"), "r") as f:
+ html5lib_rev = f.read().strip()
+
+ with open(os.path.join(script_dir, "html5lib_tests_revision"), "r") as f:
+ html5lib_tests_rev = f.read().strip()
+
+ with Html5libInstall(html5lib_rev, html5lib_tests_rev):
+ from html5lib.tests import support
+
+ if len(sys.argv) > 2:
+ test_iterator = zip(
+ itertools.repeat(False),
+ sorted(os.path.abspath(item) for item in
+ glob.glob(os.path.join(sys.argv[2], "*.dat"))))
+ else:
+ test_iterator = itertools.chain(
+ zip(itertools.repeat(False),
+ sorted(support.get_data_files("tree-construction"))),
+ zip(itertools.repeat(True),
+ sorted(support.get_data_files(
+ os.path.join("tree-construction", "scripted")))))
+
+ for (scripted, test_file) in test_iterator:
+ input_file_name = os.path.splitext(os.path.basename(test_file))[0]
+ if scripted:
+ input_file_name = "scripted_" + input_file_name
+ test_data = support.TestData(test_file)
+ test_filename, inner_html_file_name = make_tests(script_dir, out_dir,
+ input_file_name, test_data)
+ if test_filename is not None:
+ test_files.append(test_filename)
+ if inner_html_file_name is not None:
+ inner_html_files.append(inner_html_file_name)
+
+if __name__ == "__main__":
+ main()
diff --git a/testing/web-platform/tests/html/user-activation/META.yml b/testing/web-platform/tests/html/user-activation/META.yml
new file mode 100644
index 0000000000..e50fcfc84f
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/META.yml
@@ -0,0 +1,3 @@
+spec: https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation
+suggested_reviewers:
+ - marcoscaceres
diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-enter.html b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-enter.html
new file mode 100644
index 0000000000..be32d999b1
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-enter.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+</head>
+<body onload="runTests()">
+ <h1>Test for keyboard activation trigger for ENTER key</h1>
+ <p>Tests user activation from a ENTER keyboard event.</p>
+ <input type="text" autofocus />
+ <ol id="instructions">
+ <li>Press ENTER key.
+ </ol>
+ <script>
+ function runTests() {
+ promise_test(async () => {
+ const ENTER_KEY = '\uE007';
+
+ let keydown_event = getEvent('keydown');
+ let keypress_event = getEvent('keypress');
+ let keyup_event = getEvent('keyup');
+
+ await test_driver.send_keys(document.body, ENTER_KEY);
+
+ await keydown_event;
+ let consumed = await consumeTransientActivation();
+ assert_true(consumed,
+ "ENTER keydown event should result in activation");
+
+ await keypress_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "ENTER keypress should have no activation after keydown consumption");
+
+ await keyup_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "ENTER keyup should have no activation after keydown consumption");
+ }, "Activation through ENTER keyboard event");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-escape.html b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-escape.html
new file mode 100644
index 0000000000..82c94d84c2
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-escape.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+</head>
+<body onload="runTests()">
+ <h1>Test for keyboard activation trigger for ESCAPE key</h1>
+ <p>Tests missing user activation from a ESCAPE keyboard event.</p>
+ <input type="text" autofocus />
+ <ol id="instructions">
+ <li>Press ESCAPE key.
+ </ol>
+ <script>
+ function runTests() {
+ promise_test(async () => {
+ const ESCAPE_KEY = '\uE00C';
+
+ let keydown_event = getEvent('keydown');
+ let keyup_event = getEvent('keyup');
+
+ await test_driver.send_keys(document.body, ESCAPE_KEY);
+
+ await keydown_event;
+ let consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "ESCAPE keydown event should not result in activation");
+
+ await keyup_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "ESCAPE keyup should have no activation after keydown consumption");
+ }, "Activation through ESCAPE keyboard event");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-left.html b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-left.html
new file mode 100644
index 0000000000..75a248c425
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-left.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+</head>
+<body onload="runTests()">
+ <h1>Test for click activation trigger</h1>
+ <p>Tests user activation from a mouse click.</p>
+ <ol id="instructions">
+ <li>Click anywhere in the document.
+ </ol>
+ <script>
+ function runTests() {
+ promise_test(async () => {
+
+ let mousedown_event = getEvent('mousedown');
+ let mouseup_event = getEvent('mouseup');
+ let click_event = getEvent('click');
+
+ await test_driver.click(document.body);
+
+ await mousedown_event;
+ let consumed = await consumeTransientActivation();
+ assert_true(consumed,
+ "mousedown event should result in activation");
+
+ await mouseup_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "mouseup should have no activation after mousedown consumption");
+
+ await click_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "click should have no activation after mousedown consumption");
+ }, "Activation through left-click mouse event");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-right.html b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-right.html
new file mode 100644
index 0000000000..f98a54fe23
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-right.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+</head>
+<body onload="runTests()">
+ <h1>Test for right-click activation trigger</h1>
+ <p>Tests user activation from a mouse right-click.</p>
+ <ol id="instructions">
+ <li>Right-click anywhere in the document.
+ </ol>
+ <script>
+ function runTests() {
+ promise_test(async () => {
+ var actions = new test_driver.Actions();
+ actions.pointerMove(0, 0, {origin: document.body})
+ .pointerDown({button: actions.ButtonType.RIGHT})
+ .pointerUp({button: actions.ButtonType.RIGHT})
+ .send();
+
+ // In most non-Windows platforms the right-click context-menu appears on mousedown, so
+ // mouseup and auxclick events are not received by the page if the menu is modal. We
+ // are suppressing the context-menu to guarantee receiving those later events.
+ document.body.addEventListener("contextmenu", e => e.preventDefault());
+
+ let mousedown_event = getEvent('mousedown');
+ let mouseup_event = getEvent('mouseup');
+ let auxclick_event = getEvent('auxclick');
+ let contextmenu_event = getEvent('contextmenu');
+
+ await mousedown_event;
+ let consumed = await consumeTransientActivation();
+ assert_true(consumed,
+ "mousedown event should result in activation");
+
+ await mouseup_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "mouseup should have no activation after mousedown consumption");
+
+ await auxclick_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "auxclick should have no activation after mousedown consumption");
+
+ await contextmenu_event;
+ consumed = await consumeTransientActivation();
+ assert_false(consumed,
+ "contextmenu should have no activation after mousedown consumption");
+ }, "Activation through right-click mouse event");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-pointerevent.html b/testing/web-platform/tests/html/user-activation/activation-trigger-pointerevent.html
new file mode 100644
index 0000000000..e3042b718d
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/activation-trigger-pointerevent.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event">
+ <meta name="variant" content="?mouse">
+ <meta name="variant" content="?pen">
+ <meta name="variant" content="?touch">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+</head>
+<body onload="runTests()">
+ <h1>Test for pointerevent click activation trigger</h1>
+ <p>Tests user activation from a pointer click.</p>
+ <ol id="instructions">
+ <li>Click anywhere in the document.
+ </ol>
+ <script>
+ function runTests() {
+ let pointer_type = location.search.substring(1);
+
+ promise_test(async () => {
+ const test_pointer = pointer_type + "TestPointer";
+
+ let pointerdown_event = getEvent('pointerdown');
+ let pointerup_event = getEvent('pointerup');
+ let click_event = getEvent('click');
+
+ await new test_driver.Actions().addPointer(test_pointer, pointer_type)
+ .pointerMove(0, 0, {origin:document.body, sourceName:test_pointer})
+ .pointerDown({sourceName:test_pointer})
+ .pointerUp({sourceName:test_pointer})
+ .send();
+
+ await pointerdown_event;
+ let consumed_pointerdown = await consumeTransientActivation();
+ await pointerup_event;
+ let consumed_pointerup = await consumeTransientActivation();
+ await click_event;
+ let consumed_click = await consumeTransientActivation();
+
+ if (pointer_type === "mouse") {
+ assert_true(consumed_pointerdown,
+ pointer_type + " pointerdown event should result in activation");
+ assert_false(consumed_pointerup,
+ pointer_type + " pointerup should have no activation after pointerdown consumption");
+ assert_false(consumed_click,
+ pointer_type + " click should have no activation after pointerdown consumption");
+ } else {
+ assert_false(consumed_pointerdown,
+ pointer_type + " pointerdown event should not result in activation");
+ assert_true(consumed_pointerup,
+ pointer_type + " pointerup event should result in activation");
+ assert_false(consumed_click,
+ pointer_type + " click should have no activation after pointerup consumption");
+ }
+ }, "Activation through " + pointer_type + " pointerevent click");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/chained-setTimeout.html b/testing/web-platform/tests/html/user-activation/chained-setTimeout.html
new file mode 100644
index 0000000000..a530837392
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/chained-setTimeout.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script>
+ let chained_timeout_test = async_test("Chained setTimeout test");
+
+ const max_call_depth = 3;
+ const delay_ms = 10;
+
+ function testInitialStates(depth) {
+ assert_true(1 <= depth && depth <= max_call_depth);
+
+ chained_timeout_test.step_timeout(() => {
+ let test_name = "Call-depth=" + depth + ": initial activation states are false";
+ test(() => {
+ assert_false(navigator.userActivation.isActive);
+ assert_false(navigator.userActivation.hasBeenActive);
+ }, test_name);
+
+ if (depth < max_call_depth)
+ testInitialStates(depth+1);
+ else
+ test_driver.click(document.body);
+ }, delay_ms);
+ }
+
+ function testFinalStates(depth) {
+ assert_true(1 <= depth && depth <= max_call_depth);
+
+ chained_timeout_test.step_timeout(() => {
+ let test_name = "Call-depth=" + depth + ": after-click activation states are true";
+ test(() => {
+ assert_true(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ }, test_name);
+
+ if (depth < max_call_depth)
+ testFinalStates(depth+1);
+ else
+ chained_timeout_test.done();
+ }, delay_ms)
+ }
+
+ function run() {
+ window.addEventListener("click", event => {
+ testFinalStates(1);
+ });
+
+ testInitialStates(1);
+ }
+ </script>
+</head>
+<body onload="run()">
+ <h1>User activation state in chained setTimeout calls</h1>
+ <p>Tests that user activation state is visible in arbitrary call depth of setTimeout.</p>
+ <ol id="instructions">
+ <li>Click anywhere in the document.
+ </ol></body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/detached-iframe.html b/testing/web-platform/tests/html/user-activation/detached-iframe.html
new file mode 100644
index 0000000000..af3d23072b
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/detached-iframe.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+ </head>
+ <body></body>
+ <script>
+ async function attachIframe() {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ await new Promise((r) => {
+ iframe.addEventListener("load", r, { once: true });
+ document.body.append(iframe);
+ });
+ return iframe;
+ }
+
+ promise_test(async () => {
+ const iframe = await attachIframe();
+ const { userActivation } = iframe.contentWindow.navigator;
+
+ assert_false(
+ userActivation.isActive,
+ "No transient activation before click"
+ );
+ assert_false(
+ userActivation.hasBeenActive,
+ "No sticky activation before click"
+ );
+
+ // Confirm we have activation
+ await test_driver.bless("click", null, iframe.contentWindow);
+ assert_true(userActivation.isActive, "is active after click");
+ assert_true(userActivation.hasBeenActive, "has been active");
+
+ // Remove the context
+ iframe.remove();
+ assert_equals(iframe.contentWindow, null, "No more global");
+ assert_true(userActivation.isActive, "isActive");
+ assert_true(userActivation.hasBeenActive, "hasBeenActive");
+ }, "navigator.userActivation retains state even if global is removed");
+ </script>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html b/testing/web-platform/tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html
new file mode 100644
index 0000000000..2a16f45496
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/whatwg/html/issues/1983
+-->
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+ <h1>Clicking in iframe has activation state in child via MessageEvent</h1>
+ <ol id="instructions">
+ <li>Click inside the red area.
+ </ol>
+ <iframe id="child" width="200" height="200">
+ </iframe>
+ <script>
+ async_test(function(t) {
+ var child = document.getElementById("child");
+ child.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-message-event-api.html";
+ assert_false(navigator.userActivation.isActive);
+ assert_false(navigator.userActivation.hasBeenActive);
+ window.addEventListener("message", t.step_func(event => {
+ if (event.data == 'child-loaded') {
+ // values have false after load
+ assert_true(event.userActivation != null);
+ assert_false(event.userActivation.isActive);
+ assert_false(event.userActivation.hasBeenActive);
+ test_driver.click(child);
+ } else if (event.data == 'child-clicked') {
+ // values have activation state on click
+ assert_true(navigator.userActivation.hasBeenActive);
+ assert_true(event.userActivation != null);
+ assert_true(event.userActivation.isActive);
+ assert_true(event.userActivation.hasBeenActive);
+ child.contentWindow.postMessage('report', "*");
+ } else if (event.data == 'child-report') {
+ assert_false(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ assert_true(event.userActivation != null);
+ assert_false(event.userActivation.isActive);
+ assert_true(event.userActivation.hasBeenActive);
+ child.contentWindow.postMessage('report-no-activation', "*");
+ } else if (event.data == 'child-report-no-activation') {
+ assert_equals(event.userActivation, null);
+ t.done();
+ }
+ }));
+ }, "Message propagates values on post");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/message-event-init.tentative.html b/testing/web-platform/tests/html/user-activation/message-event-init.tentative.html
new file mode 100644
index 0000000000..1f3ef55170
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/message-event-init.tentative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!--
+ Tentative due to:
+ https://github.com/whatwg/html/issues/1983
+-->
+<title>MessageEvent constructor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ var ev = new MessageEvent("test", { userActivation: navigator.userActivation })
+ assert_equals(ev.userActivation, navigator.userActivation, "userActivation attribute")
+}, "MessageEventInit user activation set")
+test(function() {
+ var ev = new MessageEvent("test")
+ assert_equals(ev.userActivation, null, "userActivation attribute")
+}, "MessageEventInit user activation not set")
+
+</script>
diff --git a/testing/web-platform/tests/html/user-activation/navigation-state-reset-crossorigin.sub.html b/testing/web-platform/tests/html/user-activation/navigation-state-reset-crossorigin.sub.html
new file mode 100644
index 0000000000..5e16e9d46c
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/navigation-state-reset-crossorigin.sub.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+</head>
+<body>
+ <h1>Post-navigation activation state in child</h1>
+ <p>Tests that navigating a cross-origin child frame resets its activation states.</p>
+ <ol id="instructions">
+ <li>Click inside the yellow area.
+ </ol>
+ <iframe id="child" width="200" height="50">
+ </iframe>
+ <script>
+ async_test(function(t) {
+ var child = document.getElementById("child");
+ child.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-one.html";
+ window.addEventListener("message", t.step_func(event => {
+ // Test driver can send messages too...
+ if (typeof event.data !== "string") return;
+
+ var msg = JSON.parse(event.data);
+ if (msg.type == 'child-one-loaded') {
+ assert_false(navigator.userActivation.isActive);
+ assert_false(navigator.userActivation.hasBeenActive);
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+
+ delayByFrames(() => test_driver.click(child), 5);
+ } else if (msg.type == 'child-one-clicked') {
+ assert_true(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ assert_true(msg.isActive);
+ assert_true(msg.hasBeenActive);
+
+ child.src = "http://{{hosts[alt][]}}:{{ports[http][1]}}/html/user-activation/resources/child-two.html";
+ } else if (msg.type == 'child-two-loaded') {
+ assert_true(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+
+ t.done();
+ }
+ }));
+ }, "Post-navigation state reset.");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/navigation-state-reset-sameorigin.html b/testing/web-platform/tests/html/user-activation/navigation-state-reset-sameorigin.html
new file mode 100644
index 0000000000..c240f96b2c
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/navigation-state-reset-sameorigin.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ </head>
+ <body>
+ <h1>Post-navigation activation state in child</h1>
+ <p>
+ Tests that navigating a same-origin child frame resets its activation
+ states.
+ </p>
+ <ol id="instructions">
+ <li>Click inside the yellow area.</li>
+ </ol>
+
+ <iframe id="child" width="200" height="50"> </iframe>
+ <script>
+ function message(type) {
+ return new Promise((resolve) => {
+ window.addEventListener("message", function listener(event) {
+ const data = JSON.parse(event.data);
+ if (data.type === type) {
+ window.removeEventListener("message", listener);
+ resolve(data);
+ }
+ });
+ });
+ }
+ promise_test(async (t) => {
+ var child = document.getElementById("child");
+ child.src = "./resources/child-one.html";
+ const unclickeData = await message("child-one-loaded");
+ assert_false(navigator.userActivation.isActive);
+ assert_false(navigator.userActivation.hasBeenActive);
+ assert_false(unclickeData.isActive);
+ assert_false(unclickeData.hasBeenActive);
+
+ const [, child1Data] = await Promise.all([
+ test_driver.click(child),
+ message("child-one-clicked"),
+ ]);
+
+ assert_true(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ assert_true(child1Data.isActive);
+ assert_true(child1Data.hasBeenActive);
+
+ child.src = "./resources/child-two.html";
+
+ const child2Data = await message("child-two-loaded");
+
+ assert_true(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ assert_false(child2Data.isActive);
+ assert_false(child2Data.hasBeenActive);
+ }, "Post-navigation state reset.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/no-activation-thru-escape-key.html b/testing/web-platform/tests/html/user-activation/no-activation-thru-escape-key.html
new file mode 100644
index 0000000000..0045e20788
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/no-activation-thru-escape-key.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>No user activation through 'Escape' key</title>
+ <meta name="timeout" content="long">
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <style>
+ #target {
+ width: 40ex;
+ background-color: yellow;
+ }
+ </style>
+ <script type="text/javascript">
+ let keydown_event_fired = false;
+ let keyup_event_fired = false;
+
+ function run() {
+ let textbox_elem = document.getElementById("target");
+ let test_esc_key = async_test("'Escape' key doesn't activate a page.");
+
+ test_esc_key.step(() => {
+ assert_true(!!navigator.userActivation, "This test requires user activation query API");
+ });
+
+ textbox_elem.focus();
+
+ on_event(textbox_elem, "keydown", () => {
+ test_esc_key.step(() => {
+ keydown_event_fired = true;
+ assert_false(navigator.userActivation.isActive, "No user activation on keydown");
+ });
+ });
+
+ on_event(textbox_elem, "keyup", () => {
+ test_esc_key.step(() => {
+ if (keydown_event_fired)
+ keyup_event_fired = true;
+ assert_true(keydown_event_fired, "keydown event fired before keyup");
+ assert_false(navigator.userActivation.isActive, "No user activation on keyup");
+ });
+ });
+
+ // Inject mouse inputs.
+ const escape_key = "\uE00C";
+ test_driver
+ .send_keys(textbox_elem, escape_key)
+ .then(() => {
+ assert_true(keyup_event_fired, "keydown event fired before keyup");
+ test_esc_key.done();
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>No user activation through 'Escape' key</h1>
+ <h4>Tests that pressing/releasing 'Escape' key is not treated as a user activation.</h4>
+ <input id="target" value="Press and release the 'Esc' key." />
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/propagation-crossorigin.sub.html b/testing/web-platform/tests/html/user-activation/propagation-crossorigin.sub.html
new file mode 100644
index 0000000000..d317764036
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/propagation-crossorigin.sub.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+ <script>
+ // Frame layout:
+ // top=origin0:this-file [
+ // child1=origin1:child-one.html,
+ // child-xo=origin2:propagation-crossorigin-child.html [
+ // gchild=origin3:child-two.html
+ // ]
+ // ]
+ let propagation_test = async_test("Propagation test");
+
+ let num_children_to_load = 3;
+ let num_children_to_report = 3;
+
+ function finishLoadPhase() {
+ assert_equals(num_children_to_load, 0);
+
+ test(() => {
+ assert_false(navigator.userActivation.isActive);
+ assert_false(navigator.userActivation.hasBeenActive);
+ }, "Parent frame initial state");
+
+ delayByFrames(() => test_driver.click(document.getElementById("child-xo")), 7);
+ }
+
+ function finishReportPhase() {
+ assert_equals(num_children_to_report, 0);
+
+ test(() => {
+ assert_true(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ }, "Parent frame final state");
+
+ propagation_test.done();
+ }
+
+ window.addEventListener("message", event => {
+ // Test driver can send messages too...
+ if (typeof event.data !== "string") return;
+
+ var msg = JSON.parse(event.data);
+
+ if (msg.type == 'child-one-loaded') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Child1 frame initial state");
+ } else if (msg.type == 'child-crossorigin-loaded') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Child2 frame initial state");
+ } else if (msg.type == 'child-two-loaded') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Grandchild frame initial state");
+ } else if (msg.type == 'child-one-report') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Child1 frame final state");
+ } else if (msg.type == 'child-crossorigin-report') {
+ // This msg was triggered by a user click.
+ test(() => {
+ assert_true(msg.isActive);
+ assert_true(msg.hasBeenActive);
+ }, "Child2 frame final state");
+
+ // Ask remaining frames to report states.
+ let ask_report = JSON.stringify({"type": "report"});
+ frames[0].postMessage(ask_report, "*");
+ frames[1].frames[0].postMessage(ask_report, "*");
+ } else if (msg.type == 'child-two-report') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Grand child frame final state");
+ }
+
+ // Phase switching.
+ if (msg.type.endsWith("-loaded")) {
+ if (--num_children_to_load == 0)
+ finishLoadPhase();
+ } else if (msg.type.endsWith("-report")) {
+ if (--num_children_to_report == 0)
+ finishReportPhase();
+ }
+ });
+ async function createIframes() {
+ const child1 = document.createElement("iframe");
+ child1.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-one.html";
+ child1.id = "child1";
+ document.body.appendChild(child1);
+ await new Promise((resolve) => {
+ child1.onload = resolve;
+ document.body.appendChild(child1);
+ });
+ const childXO = document.createElement("iframe");
+ childXO.id = "child-xo";
+ childXO.src = "http://{{hosts[alt][]}}:{{ports[http][1]}}/html/user-activation/resources/propagation-crossorigin-child.sub.html";
+ document.body.appendChild(childXO);
+ }
+ </script>
+</head>
+<body onload="createIframes()">
+ <h1>User activation propagation across cross-origin frame boundary</h1>
+ <p>Tests that user activation does not propagate across cross-origin frame boundary.</p>
+ <ol id="instructions">
+ <li>Click anywhere on the green area (child frame).
+ </ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/propagation-same-and-cross-origin.sub.html b/testing/web-platform/tests/html/user-activation/propagation-same-and-cross-origin.sub.html
new file mode 100644
index 0000000000..31a7222448
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/propagation-same-and-cross-origin.sub.html
@@ -0,0 +1,151 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+ </head>
+ <body>
+ <h1>Check that cross origin iframes don't get activated</h1>
+ <p>
+ Tests that activating a same-origin navigable doesn't activate a cross
+ origin navigable.
+ </p>
+ <ol id="instructions">
+ <li>Click inside the yellow area.</li>
+ </ol>
+ <h1>Same origin</h1>
+ <iframe id="so-child" width="200" height="50"></iframe>
+ <h1>Cross origin</h1>
+ <iframe id="xo-child" width="200" height="50"></iframe>
+ </body>
+ <script>
+ const soChild = document.getElementById("so-child");
+ const xoChild = document.getElementById("xo-child");
+
+ function requestXOReport() {
+ xoChild.contentWindow.postMessage(
+ JSON.stringify({ type: "report" }),
+ "*"
+ );
+ return receiveMessage(`child-two-report`);
+ }
+
+ promise_setup(() => {
+ soChild.src = "./resources/child-one.html";
+ xoChild.src =
+ "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-two.html";
+ return Promise.all([
+ receiveMessage("child-one-loaded"),
+ receiveMessage("child-two-loaded"),
+ ]);
+ });
+
+ promise_test(async (t) => {
+ const unclickedCrossOrigin = await requestXOReport();
+ const soActivation = soChild.contentWindow.navigator.userActivation;
+ assert_false(
+ navigator.userActivation.isActive,
+ "top-frame navigator.userActivation.isActive must be false"
+ );
+ assert_false(
+ navigator.userActivation.hasBeenActive,
+ "top-frame navigator.userActivation.hasBeenActive must be false"
+ );
+
+ assert_false(soActivation.isActive, "child-one isActive must be false");
+ assert_false(
+ soActivation.hasBeenActive,
+ "child-one hasBeenActive must be false"
+ );
+ assert_false(
+ unclickedCrossOrigin.isActive,
+ "child-two isActive must be false"
+ );
+ assert_false(
+ unclickedCrossOrigin.hasBeenActive,
+ "child-two hasBeenActive must be false"
+ );
+ }, "Check Initial states of user activation are all false");
+
+ promise_test(async (t) => {
+ await test_driver.click(soChild);
+ const xoActivation = await requestXOReport();
+ const soActivation = soChild.contentWindow.navigator.userActivation;
+ assert_true(
+ navigator.userActivation.isActive,
+ "top-frame navigator.userActivation.isActive must be true"
+ );
+ assert_true(
+ navigator.userActivation.hasBeenActive,
+ "top-frame navigator.userActivation.hasBeenActive must be true"
+ );
+ assert_true(soActivation.isActive, "child-one isActive must be true");
+ assert_true(
+ soActivation.hasBeenActive,
+ "child-one hasBeenActive must be true"
+ );
+ assert_false(xoActivation.isActive, "child-two isActive must be false");
+ assert_false(
+ xoActivation.hasBeenActive,
+ "child-two hasBeenActive must be false"
+ );
+ }, "Check that activating a same-origin navigable doesn't activate a cross origin navigable");
+
+ promise_test(async (t) => {
+ await consumeTransientActivation();
+ const soActivation = soChild.contentWindow.navigator.userActivation;
+ // Before click...
+ assert_false(
+ navigator.userActivation.isActive,
+ "top-frame navigator.userActivation.isActive must be false"
+ );
+ assert_true(
+ navigator.userActivation.hasBeenActive,
+ "top-frame navigator.userActivation.hasBeenActive must be true"
+ );
+ assert_false(soActivation.isActive, "child-one isActive must be false");
+ assert_true(
+ soActivation.hasBeenActive,
+ "child-one hasBeenActive must be true"
+ );
+ const xoActivation = await requestXOReport();
+ assert_false(xoActivation.isActive, "child-two isActive must be false");
+ assert_false(
+ xoActivation.hasBeenActive,
+ "child-two hasBeenActive must be false"
+ );
+
+ // Click!
+ const [, xoActivationAfterClick] = await Promise.all([
+ test_driver.click(xoChild),
+ receiveMessage("child-two-clicked"),
+ ]);
+
+ // After click...
+ assert_true(
+ navigator.userActivation.isActive,
+ "top-frame navigator.userActivation.isActive must be true"
+ );
+ assert_true(
+ navigator.userActivation.hasBeenActive,
+ "top-frame navigator.userActivation.hasBeenActive must remain true"
+ );
+ assert_true(
+ xoActivationAfterClick.isActive,
+ "child-two isActive must be true"
+ );
+ assert_true(
+ xoActivationAfterClick.hasBeenActive,
+ "child-two hasBeenActive must be true"
+ );
+ assert_false(soActivation.isActive, "child-one isActive must be false");
+ assert_true(
+ soActivation.hasBeenActive,
+ "child-one hasBeenActive must remain true"
+ );
+ }, "Clicking on the cross-origin navigable activates parent navigable.");
+ </script>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/propagation-sameorigin.html b/testing/web-platform/tests/html/user-activation/propagation-sameorigin.html
new file mode 100644
index 0000000000..94d15c2d41
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/propagation-sameorigin.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script>
+ // Frame layout:
+ // top=this-file [
+ // child1=child-one.html,
+ // child-so=propagation-sameorigin-child.html [
+ // gchild=child-two.html
+ // ]
+ // ]
+ let propagation_test = async_test("Propagation test");
+
+ let num_children_to_load = 3;
+ let num_children_to_report = 3;
+
+ function finishLoadPhase() {
+ assert_equals(num_children_to_load, 0);
+
+ test(() => {
+ assert_false(navigator.userActivation.isActive);
+ assert_false(navigator.userActivation.hasBeenActive);
+ }, "Parent frame initial state");
+
+ test_driver.click(document.getElementById("child-so"));
+ }
+
+ function finishReportPhase() {
+ assert_equals(num_children_to_report, 0);
+
+ test(() => {
+ assert_true(navigator.userActivation.isActive);
+ assert_true(navigator.userActivation.hasBeenActive);
+ }, "Parent frame final state");
+
+ propagation_test.done();
+ }
+
+ window.addEventListener("message", event => {
+ // Test driver can send messages too...
+ if (typeof event.data !== "string") return;
+
+ var msg = JSON.parse(event.data);
+
+ if (msg.type == 'child-one-loaded') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Child1 frame initial state");
+ } else if (msg.type == 'child-sameorigin-loaded') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Child2 frame initial state");
+ } else if (msg.type == 'child-two-loaded') {
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Grandchild frame initial state");
+ } else if (msg.type == 'child-one-report') {
+ // Siblings (same or cross origin) should not be activated per spec
+ // Spec issue discussing: https://github.com/whatwg/html/issues/9831
+ test(() => {
+ assert_false(msg.isActive);
+ assert_false(msg.hasBeenActive);
+ }, "Child1 frame final state");
+ } else if (msg.type == 'child-sameorigin-report') {
+ // This msg was triggered by a user click.
+ test(() => {
+ assert_true(msg.isActive);
+ assert_true(msg.hasBeenActive);
+ }, "Child2 frame final state");
+
+ // Ask remaining frames to report states.
+ let ask_report = JSON.stringify({"type": "report"});
+ frames[0].postMessage(ask_report, "*");
+ frames[1].frames[0].postMessage(ask_report, "*");
+ } else if (msg.type == 'child-two-report') {
+ test(() => {
+ assert_true(msg.isActive);
+ assert_true(msg.hasBeenActive);
+ }, "Grand child frame final state");
+ }
+
+ // Phase switching.
+ if (msg.type.endsWith("-loaded")) {
+ if (--num_children_to_load == 0)
+ finishLoadPhase();
+ } else if (msg.type.endsWith("-report")) {
+ if (--num_children_to_report == 0)
+ finishReportPhase();
+ }
+ });
+ async function createIframes() {
+ const child1 = document.createElement("iframe");
+ child1.src = "resources/child-one.html";
+ child1.id = "child1";
+ await new Promise((resolve) => {
+ child1.onload = resolve;
+ document.body.appendChild(child1);
+ });
+ const childSO = document.createElement("iframe");
+ childSO.id = "child-so";
+ childSO.src = "resources/propagation-sameorigin-child.html";
+ document.body.appendChild(childSO);
+ }
+ </script>
+</head>
+<body onload="createIframes()">
+ <h1>User activation propagation across same-origin frame boundary</h1>
+ <p>Tests that user activation propagates across same-origin frame boundary.</p>
+ <ol id="instructions">
+ <li>Click anywhere on the green area (child frame).
+ </ol>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/resources/child-message-event-api.html b/testing/web-platform/tests/html/user-activation/resources/child-message-event-api.html
new file mode 100644
index 0000000000..a0001633c2
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/child-message-event-api.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<body style="background: red;">
+ <script>
+ window.parent.postMessage("child-loaded",
+ {targetOrigin: "*", includeUserActivation: true});
+ window.addEventListener("click", event => {
+ window.parent.postMessage("child-clicked",
+ {targetOrigin: "*", includeUserActivation: true});
+ var win = window.open('404.html');
+ win.close();
+ });
+
+ window.addEventListener("message", event => {
+ if (event.data == "report") {
+ window.parent.postMessage("child-report",
+ {targetOrigin: "*", includeUserActivation: true});
+ }
+ if (event.data == "report-no-activation") {
+ window.parent.postMessage("child-report-no-activation",
+ {targetOrigin: "*", includeUserActivation: false});
+ }
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/user-activation/resources/child-one.html b/testing/web-platform/tests/html/user-activation/resources/child-one.html
new file mode 100644
index 0000000000..9668372620
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/child-one.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<body style="background: yellow;">
+ <script>
+ window.top.postMessage(JSON.stringify({
+ "type": "child-one-loaded",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+
+ window.addEventListener("click", event => {
+ window.top.postMessage(JSON.stringify({
+ "type": "child-one-clicked",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ });
+
+ window.addEventListener("message", event => {
+ var msg = JSON.parse(event.data);
+ if (msg.type == "report") {
+ window.top.postMessage(JSON.stringify({
+ "type": "child-one-report",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ }
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/user-activation/resources/child-two.html b/testing/web-platform/tests/html/user-activation/resources/child-two.html
new file mode 100644
index 0000000000..9fb68dbd51
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/child-two.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<body style="background: lightgrey;">
+ <script>
+ window.top.postMessage(JSON.stringify({
+ "type": "child-two-loaded",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+
+ window.addEventListener("click", event => {
+ window.top.postMessage(JSON.stringify({
+ "type": "child-two-clicked",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ });
+
+ window.addEventListener("message", event => {
+ var msg = JSON.parse(event.data);
+ if (msg.type == "report") {
+ window.top.postMessage(JSON.stringify({
+ "type": "child-two-report",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ }
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/user-activation/resources/consumption-crossorigin-child.sub.html b/testing/web-platform/tests/html/user-activation/resources/consumption-crossorigin-child.sub.html
new file mode 100644
index 0000000000..518e000d0b
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/consumption-crossorigin-child.sub.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script>
+ window.top.postMessage(JSON.stringify({
+ "type": "child-crossorigin-loaded",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+
+ window.addEventListener("click", event => {
+ window.open().close();
+
+ window.top.postMessage(JSON.stringify({
+ "type": "child-crossorigin-report",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ });
+ </script>
+</head>
+<body style="background: lightgreen;">
+ <!-- The midpoint of this frame should be outside the grandchild frame. -->
+ <div style="height: 75px;">Cross-origin child frame</div>
+ <iframe id="child2" width="270px" height="30px"
+ src="http://{{hosts[][]}}:{{ports[http][1]}}/html/user-activation/resources/child-two.html">
+ </iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/resources/consumption-sameorigin-child.html b/testing/web-platform/tests/html/user-activation/resources/consumption-sameorigin-child.html
new file mode 100644
index 0000000000..9e421fc0f1
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/consumption-sameorigin-child.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script>
+ window.top.postMessage(JSON.stringify({
+ "type": "child-sameorigin-loaded",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+
+ window.addEventListener("click", event => {
+ window.open().close();
+
+ window.top.postMessage(JSON.stringify({
+ "type": "child-sameorigin-report",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ });
+ </script>
+</head>
+<body style="background: lightgreen;">
+ <!-- The midpoint of this frame should be outside the grandchild frame. -->
+ <div style="height: 75px;">Same-origin child frame</div>
+ <iframe id="child2" width="270px" height="30px"
+ src="child-two.html">
+ </iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/resources/propagation-crossorigin-child.sub.html b/testing/web-platform/tests/html/user-activation/resources/propagation-crossorigin-child.sub.html
new file mode 100644
index 0000000000..e920566a21
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/propagation-crossorigin-child.sub.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script>
+ window.top.postMessage(JSON.stringify({
+ "type": "child-crossorigin-loaded",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+
+ window.addEventListener("click", event => {
+ window.top.postMessage(JSON.stringify({
+ "type": "child-crossorigin-report",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ });
+ </script>
+</head>
+<body style="background: lightgreen;">
+ <!-- The midpoint of this frame should be outside the grandchild frame. -->
+ <div style="height: 75px;">Cross-origin child frame</div>
+ <iframe id="child2" width="270px" height="30px"
+ src="http://{{hosts[][]}}:{{ports[http][1]}}/html/user-activation/resources/child-two.html">
+ </iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/resources/propagation-sameorigin-child.html b/testing/web-platform/tests/html/user-activation/resources/propagation-sameorigin-child.html
new file mode 100644
index 0000000000..69ad50cb71
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/propagation-sameorigin-child.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script>
+ window.top.postMessage(JSON.stringify({
+ "type": "child-sameorigin-loaded",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+
+ window.addEventListener("click", event => {
+ window.top.postMessage(JSON.stringify({
+ "type": "child-sameorigin-report",
+ "isActive": navigator.userActivation.isActive,
+ "hasBeenActive": navigator.userActivation.hasBeenActive
+ }), "*");
+ });
+ </script>
+</head>
+<body style="background: lightgreen;">
+ <!-- The midpoint of this frame should be outside the grandchild frame. -->
+ <div style="height: 75px;">Same-origin child frame</div>
+ <iframe id="child2" width="270px" height="30px"
+ src="child-two.html">
+ </iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/user-activation/resources/utils.js b/testing/web-platform/tests/html/user-activation/resources/utils.js
new file mode 100644
index 0000000000..5d3302583f
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/resources/utils.js
@@ -0,0 +1,48 @@
+function delayByFrames(f, num_frames) {
+ function recurse(depth) {
+ if (depth == 0)
+ f();
+ else
+ requestAnimationFrame(() => recurse(depth-1));
+ }
+ recurse(num_frames);
+}
+
+// Returns a Promise which is resolved with the event object when the event is
+// fired.
+function getEvent(eventType) {
+ return new Promise(resolve => {
+ document.body.addEventListener(eventType, e => resolve(e), {once: true});
+ });
+}
+
+
+// Returns a Promise which is resolved with a "true" iff transient activation
+// was available and successfully consumed.
+//
+// This function relies on Fullscreen API to check/consume user activation
+// state.
+async function consumeTransientActivation() {
+ try {
+ await document.body.requestFullscreen();
+ await document.exitFullscreen();
+ return true;
+ } catch(e) {
+ return false;
+ }
+}
+
+function receiveMessage(type) {
+ return new Promise((resolve) => {
+ window.addEventListener("message", function listener(event) {
+ if (typeof event.data !== "string") {
+ return;
+ }
+ const data = JSON.parse(event.data);
+ if (data.type === type) {
+ window.removeEventListener("message", listener);
+ resolve(data);
+ }
+ });
+ });
+}
diff --git a/testing/web-platform/tests/html/user-activation/user-activation-interface.html b/testing/web-platform/tests/html/user-activation/user-activation-interface.html
new file mode 100644
index 0000000000..8ece08f11d
--- /dev/null
+++ b/testing/web-platform/tests/html/user-activation/user-activation-interface.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="resources/utils.js"></script>
+</head>
+<body onload="runTests()">
+ <h1>Basic test for navigator.userActivation interface</h1>
+ <p>Tests that navigator.userActivation shows user activation states.</p>
+ <ol id="instructions">
+ <li>Click anywhere in the document.
+ </ol>
+ <script>
+ function runTests() {
+ promise_test(async () => {
+ assert_true(!!navigator.userActivation, "This test requires navigator.userActivation API");
+
+ assert_false(navigator.userActivation.hasBeenActive, "No sticky activation before click");
+ assert_false(navigator.userActivation.isActive, "No transient activation before click");
+
+ await test_driver.click(document.body);
+
+ assert_true(navigator.userActivation.hasBeenActive, "Has sticky activation after click");
+ assert_true(navigator.userActivation.isActive, "Has transient activation after click");
+ }, "navigator.userActivation shows correct states before/after a click");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/callback-cross-realm-report-exception.html b/testing/web-platform/tests/html/webappapis/animation-frames/callback-cross-realm-report-exception.html
new file mode 100644
index 0000000000..1b8aa41a6d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/callback-cross-realm-report-exception.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>requestAnimationFrame() reports the exception from its callback in the callback's global object</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+setup({ allow_uncaught_exception: true });
+
+const onerrorCalls = [];
+window.onerror = () => { onerrorCalls.push("top"); };
+frames[0].onerror = () => { onerrorCalls.push("frame0"); };
+frames[1].onerror = () => { onerrorCalls.push("frame1"); };
+frames[2].onerror = () => { onerrorCalls.push("frame2"); };
+
+async_test(t => {
+ window.onload = t.step_func(() => {
+ frames[0].requestAnimationFrame(new frames[1].Function(`throw new parent.frames[2].Error("PASS");`));
+ document.querySelector("iframe").height = 200;
+
+ t.step_timeout(() => {
+ assert_array_equals(onerrorCalls, ["frame1"]);
+ t.done();
+ }, 100);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/callback-exception.html b/testing/web-platform/tests/html/webappapis/animation-frames/callback-exception.html
new file mode 100644
index 0000000000..3867f0c41d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/callback-exception.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <title>requestAnimationFrame callback exception reported to error handler</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="help" href="https://w3c.github.io/web-performance/specs/RequestAnimationFrame/Overview.html#dom-windowanimationtiming-requestanimationframe"/>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var custom_exception = 'requestAnimationFrameException';
+ setup({allow_uncaught_exception : true});
+ async_test(function (t) {
+ addEventListener("error",function(e) {
+ t.step(function() {
+ assert_equals(e.error.message, custom_exception);
+ t.done();
+ })
+ });
+ window.requestAnimationFrame(function () {
+ throw new Error(custom_exception);
+ });
+ }, "requestAnimationFrame callback exceptions are reported to error handler");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/callback-handle.html b/testing/web-platform/tests/html/webappapis/animation-frames/callback-handle.html
new file mode 100644
index 0000000000..f1b8830031
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/callback-handle.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>AnimationTiming Test: FrameRequestCallback - valid callback handle</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#animation-frames">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+ test(() => {
+ let requestId = window.requestAnimationFrame(() => {});
+ assert_greater_than(requestId, 0, "callback handle is a integer greater than zero");
+ }, "Check window.requestAnimationFrame can return a valid callback handle");
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/callback-invoked.html b/testing/web-platform/tests/html/webappapis/animation-frames/callback-invoked.html
new file mode 100644
index 0000000000..ca34e455a2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/callback-invoked.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+ <head>
+ <title>requestAnimationFrame must be triggered once</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="help" href="https://w3c.github.io/web-performance/specs/RequestAnimationFrame/Overview.html#dom-windowanimationtiming-requestanimationframe"/>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ async_test(function (t) {
+ assert_false(document.hidden, "document.hidden must exist and be false to run this test properly");
+ window.requestAnimationFrame(t.step_func_done());
+ }, "requestAnimationFrame callback is invoked at least once before the timeout");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/callback-multicalls.html b/testing/web-platform/tests/html/webappapis/animation-frames/callback-multicalls.html
new file mode 100644
index 0000000000..38f34171ea
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/callback-multicalls.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>AnimationTiming Test: multiple calls to requestAnimationFrame with the same callback</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-window-requestanimationframe">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+ async_test(function(t) {
+ var counter = 0;
+ window.requestAnimationFrame(callback);
+
+ function callback() {
+ ++counter;
+ if (counter == 2) {
+ t.done();
+ } else {
+ window.requestAnimationFrame(callback);
+ }
+ };
+
+ }, "Check that multiple calls to requestAnimationFrame with the same callback will result in multiple entries being in the list with that same callback.");
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/callback-timestamp.html b/testing/web-platform/tests/html/webappapis/animation-frames/callback-timestamp.html
new file mode 100644
index 0000000000..8e61db61b8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/callback-timestamp.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>AnimationTiming Test: FrameRequestCallback - timestamp argument</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#animation-frames">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+ async_test(t => {
+ requestAnimationFrame(t.step_func_done(time => {
+ assert_equals(typeof time, "number", "callback contains a number argument");
+ }))
+ }, "Check FrameRequestCallback has a DOMHighResTimeStamp argument");
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/cancel-handle-manual.html b/testing/web-platform/tests/html/webappapis/animation-frames/cancel-handle-manual.html
new file mode 100644
index 0000000000..0328272522
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/cancel-handle-manual.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>AnimationTiming Test: cancelAnimationFrame used to cancel request callback</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#animation-frames">
+
+<style>
+ #animated {
+ background: blue;
+ color: white;
+ height: 100px;
+ width: 100px;
+ position: absolute;
+ }
+</style>
+
+<p>
+ Test passes if there is a filled blue square with 'Filler Text',
+ which moves from left to right repeatly, when click the 'stop' button,
+ the square stops.
+</p>
+<button onclick="stop()">stop</button>
+<div id="animated">Filler Text</div>
+
+<script>
+
+let requestId = 0;
+let requestAnimation = window.requestAnimationFrame;
+let cancelAnimation = window.cancelAnimationFrame;
+
+function animate(time) {
+ let div = document.getElementById("animated");
+ div.style.left = (time - animationStartTime) % 2000 / 4 + "px";
+ requestId = requestAnimation(animate);
+}
+
+function start() {
+ animationStartTime = window.performance.now();
+ requestId = requestAnimation(animate);
+}
+
+function stop() {
+ if (requestId) {
+ cancelAnimation(requestId);
+ requestId = 0;
+ }
+}
+
+start();
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/cancel-invoked.html b/testing/web-platform/tests/html/webappapis/animation-frames/cancel-invoked.html
new file mode 100644
index 0000000000..d075c0fdac
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/cancel-invoked.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+ <head>
+ <title>cancelAnimationFrame does nothing</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="help" href="https://w3c.github.io/web-performance/specs/RequestAnimationFrame/Overview.html#dom-windowanimationtiming-cancelanimationframe"/>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function (t) {
+ window.cancelAnimationFrame(42);
+ assert_true(true);
+ }, "cancelAnimationFrame does nothing if there is no callback with the given handle");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/cancel-pending.html b/testing/web-platform/tests/html/webappapis/animation-frames/cancel-pending.html
new file mode 100644
index 0000000000..9c9aff511d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/cancel-pending.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>cancelAnimationFrame cancels a pending animation frame callback</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#run-the-animation-frame-callbacks">
+<div id="log"></div>
+<script>
+async_test(t => {
+ let didCall = false;
+
+ function callbackOne() {
+ cancelAnimationFrame(twoHandle);
+ requestAnimationFrame(t.step_func(() => {
+ assert_false(didCall, 'Should NOT have called the second callback');
+ t.done();
+ }));
+ }
+
+ function callbackTwo() {
+ didCall = true;
+ }
+
+ requestAnimationFrame(callbackOne);
+ const twoHandle = requestAnimationFrame(callbackTwo);
+}, 'cancelAnimationFrame cancels a pending animation frame callback');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/animation-frames/same-dispatch-time.html b/testing/web-platform/tests/html/webappapis/animation-frames/same-dispatch-time.html
new file mode 100644
index 0000000000..28e94f1e33
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/animation-frames/same-dispatch-time.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+ <head>
+ <title>requestAnimationFrame in queue get the same timestamp</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="help" href="http://w3c.github.io/animation-timing/#dfn-invoke-callbacks-algorithm"/>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ async_test(function (t) {
+ var a = 0, b = 0;
+
+ /* REASONING:
+ * These two methods that will be called with a timestamp. Because
+ * they execute right after eachother, they're added to the same
+ * queue and SHOULD be timestamped with the same value.
+ */
+ requestAnimationFrame(t.step_func(function() { a = arguments[0]; }));
+ requestAnimationFrame(t.step_func(function() {
+ b = arguments[0];
+ assert_not_equals(a, 0);
+ assert_not_equals(b, 0);
+ assert_equals(a, b);
+ t.done();
+ }));
+ }, "requestAnimationFrame will timestamp events in the same queue with the same time");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/atob/base64.any.js b/testing/web-platform/tests/html/webappapis/atob/base64.any.js
new file mode 100644
index 0000000000..7f433f4d8a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/atob/base64.any.js
@@ -0,0 +1,163 @@
+/**
+ * btoa() as defined by the HTML5 spec, which mostly just references RFC4648.
+ */
+function mybtoa(s) {
+ // String conversion as required by WebIDL.
+ s = String(s);
+
+ // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the
+ // method's first argument contains any character whose code point is
+ // greater than U+00FF."
+ for (var i = 0; i < s.length; i++) {
+ if (s.charCodeAt(i) > 255) {
+ return "INVALID_CHARACTER_ERR";
+ }
+ }
+
+ var out = "";
+ for (var i = 0; i < s.length; i += 3) {
+ var groupsOfSix = [undefined, undefined, undefined, undefined];
+ groupsOfSix[0] = s.charCodeAt(i) >> 2;
+ groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4;
+ if (s.length > i + 1) {
+ groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4;
+ groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2;
+ }
+ if (s.length > i + 2) {
+ groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6;
+ groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f;
+ }
+ for (var j = 0; j < groupsOfSix.length; j++) {
+ if (typeof groupsOfSix[j] == "undefined") {
+ out += "=";
+ } else {
+ out += btoaLookup(groupsOfSix[j]);
+ }
+ }
+ }
+ return out;
+}
+
+/**
+ * Lookup table for mybtoa(), which converts a six-bit number into the
+ * corresponding ASCII character.
+ */
+function btoaLookup(idx) {
+ if (idx < 26) {
+ return String.fromCharCode(idx + 'A'.charCodeAt(0));
+ }
+ if (idx < 52) {
+ return String.fromCharCode(idx - 26 + 'a'.charCodeAt(0));
+ }
+ if (idx < 62) {
+ return String.fromCharCode(idx - 52 + '0'.charCodeAt(0));
+ }
+ if (idx == 62) {
+ return '+';
+ }
+ if (idx == 63) {
+ return '/';
+ }
+ // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests.
+}
+
+function btoaException(input) {
+ input = String(input);
+ for (var i = 0; i < input.length; i++) {
+ if (input.charCodeAt(i) > 255) {
+ return true;
+ }
+ }
+ return false;
+}
+
+function testBtoa(input) {
+ // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the
+ // method's first argument contains any character whose code point is
+ // greater than U+00FF."
+ var normalizedInput = String(input);
+ for (var i = 0; i < normalizedInput.length; i++) {
+ if (normalizedInput.charCodeAt(i) > 255) {
+ assert_throws_dom("InvalidCharacterError", function() { btoa(input); },
+ "Code unit " + i + " has value " + normalizedInput.charCodeAt(i) + ", which is greater than 255");
+ return;
+ }
+ }
+ assert_equals(btoa(input), mybtoa(input));
+ assert_equals(atob(btoa(input)), String(input), "atob(btoa(input)) must be the same as String(input)");
+}
+
+var tests = ["עברית", "", "ab", "abc", "abcd", "abcde",
+ // This one is thrown in because IE9 seems to fail atob(btoa()) on it. Or
+ // possibly to fail btoa(). I actually can't tell what's happening here,
+ // but it doesn't hurt.
+ "\xff\xff\xc0",
+ // Is your DOM implementation binary-safe?
+ "\0a", "a\0b",
+ // WebIDL tests.
+ undefined, null, 7, 12, 1.5, true, false, NaN, +Infinity, -Infinity, 0, -0,
+ {toString: function() { return "foo" }},
+];
+for (var i = 0; i < 258; i++) {
+ tests.push(String.fromCharCode(i));
+}
+tests.push(String.fromCharCode(10000));
+tests.push(String.fromCharCode(65534));
+tests.push(String.fromCharCode(65535));
+
+// This is supposed to be U+10000.
+tests.push(String.fromCharCode(0xd800, 0xdc00));
+tests = tests.map(
+ function(elem) {
+ var expected = mybtoa(elem);
+ if (expected === "INVALID_CHARACTER_ERR") {
+ return ["btoa(" + format_value(elem) + ") must raise INVALID_CHARACTER_ERR", elem];
+ }
+ return ["btoa(" + format_value(elem) + ") == " + format_value(mybtoa(elem)), elem];
+ }
+);
+
+var everything = "";
+for (var i = 0; i < 256; i++) {
+ everything += String.fromCharCode(i);
+}
+tests.push(["btoa(first 256 code points concatenated)", everything]);
+
+generate_tests(testBtoa, tests);
+
+promise_test(() => fetch("../../../fetch/data-urls/resources/base64.json").then(res => res.json()).then(runAtobTests), "atob() setup.");
+
+const idlTests = [
+ [undefined, null],
+ [null, [158, 233, 101]],
+ [7, null],
+ [12, [215]],
+ [1.5, null],
+ [true, [182, 187]],
+ [false, null],
+ [NaN, [53, 163]],
+ [+Infinity, [34, 119, 226, 158, 43, 114]],
+ [-Infinity, null],
+ [0, null],
+ [-0, null],
+ [{toString: function() { return "foo" }}, [126, 138]],
+ [{toString: function() { return "abcd" }}, [105, 183, 29]]
+];
+
+function runAtobTests(tests) {
+ const allTests = tests.concat(idlTests);
+ for(let i = 0; i < allTests.length; i++) {
+ const input = allTests[i][0],
+ output = allTests[i][1];
+ test(() => {
+ if(output === null) {
+ assert_throws_dom("InvalidCharacterError", () => globalThis.atob(input));
+ } else {
+ const result = globalThis.atob(input);
+ for(let ii = 0; ii < output.length; ii++) {
+ assert_equals(result.charCodeAt(ii), output[ii]);
+ }
+ }
+ }, "atob(" + format_value(input) + ")");
+ }
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document-close-with-pending-script.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document-close-with-pending-script.html
new file mode 100644
index 0000000000..1584ca5f97
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document-close-with-pending-script.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>document.close called while a script is pending</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+ <script>
+ window.t = async_test();
+ // We want start a document load, create an non-blocking script load inside
+ // it, have the parser complete, then call document.open()/document.close()
+ // after the parser is done but before the non-blocking script load
+ // completes. After we do that, the document should reach the 'complete'
+ // ready state and the iframe's load event should fire.
+ var loadFired = false;
+ var i;
+
+ var finish = t.step_func_done(() => {
+ assert_equals(loadFired, true, "Should have fired a load event");
+ assert_equals(i.contentDocument.readyState, "complete",
+ "Should be fully loaded");
+ });
+
+ var checkForLoad = t.step_func(() => {
+ if (loadFired) {
+ finish();
+ } else {
+ i.addEventListener("load", finish);
+ }
+ });
+
+ window.parserDone = t.step_func(() => {
+ var doc = i.contentDocument;
+ i.onload = () => { loadFired = true; }
+ doc.open();
+ doc.close();
+ // It's not very clearly specced whether the document is
+ // supposed to be fully loaded at this point or not, so allow
+ // that to be the case, or to happen soonish.
+ assert_true(doc.readyState == "interactive" ||
+ doc.readyState == "complete", "Should be almost loaded");
+ if (doc.readyState == "complete") {
+ checkForLoad();
+ } else {
+ doc.addEventListener("readystatechange", checkForLoad);
+ }
+ });
+
+ t.step(() => {
+ i = document.createElement("iframe");
+ i.srcdoc = `
+ <script>
+ parent.t.step(() => {
+ var s = document.createElement("script");
+ s.src = "/common/slow.py";
+ document.documentElement.appendChild(s);
+ // Call into the parent async, so we finish our "end of parse"
+ // work before it runs.
+ document.addEventListener(
+ "DOMContentLoaded",
+ () => parent.t.step_timeout(parent.parserDone, 0));
+ });
+ <\/script>
+ `;
+ document.body.appendChild(i);
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document.close-01.xhtml b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document.close-01.xhtml
new file mode 100644
index 0000000000..164d71d191
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/document.close-01.xhtml
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.close in XHTML</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#closing-the-input-stream"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ document.close();
+ }, "document.close in XHTML should throw an INVALID_STATE_ERR ");
+}, "document.close in XHTML");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/load-event-after-location-set-during-write.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/load-event-after-location-set-during-write.window.js
new file mode 100644
index 0000000000..d5c8469baf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/closing-the-input-stream/load-event-after-location-set-during-write.window.js
@@ -0,0 +1,19 @@
+// Make sure that the load event for an iframe doesn't fire at the
+// point when a navigation triggered by document.write() starts in it,
+// but rather when that navigation completes.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ const doc = frame.contentDocument;
+ const url = URL.createObjectURL(new Blob(["PASS"], { type: "text/html"}));
+
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.body.textContent, "PASS",
+ "Why is our load event firing before the new document loaded?");
+ });
+
+ doc.open();
+ doc.write(`FAIL<script>location = "${url}"</` + "script>");
+ doc.close();
+}, "Setting location from document.write() call should not trigger load event until that load completes");
+
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/001.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/001.html
new file mode 100644
index 0000000000..3ac6423f4a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/001.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("PASS");
+ assert_equals(document.body.textContent, "PASS");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/002.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/002.html
new file mode 100644
index 0000000000..08975bca7b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/002.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/003.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/003.html
new file mode 100644
index 0000000000..915e1f6d61
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/003.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<");
+ document.write("i>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/004.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/004.html
new file mode 100644
index 0000000000..dd01725860
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/004.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i");
+ document.write(">Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.html
new file mode 100644
index 0000000000..4c161c4d47
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i>");
+ document.write("Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.js
new file mode 100644
index 0000000000..ebfd7e2585
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/005.js
@@ -0,0 +1 @@
+order.push(3); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.html
new file mode 100644
index 0000000000..92bfb44c3a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id='test'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.js
new file mode 100644
index 0000000000..ebfd7e2585
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/006.js
@@ -0,0 +1 @@
+order.push(3); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.html
new file mode 100644
index 0000000000..753316b89c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i ");
+ document.write("id='test'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.js
new file mode 100644
index 0000000000..31fcf18d49
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/007.js
@@ -0,0 +1,4 @@
+t.step(function() {
+ order.push(2);
+ document.write("<script>t.step(function() {order.push(3)})</script>");
+ }); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008-1.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008-1.js
new file mode 100644
index 0000000000..ef90c722b7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008-1.js
@@ -0,0 +1,3 @@
+t.step(function() {
+ order.push(3);
+ }); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.html
new file mode 100644
index 0000000000..4818bc388f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i i");
+ document.write("d='test'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.js
new file mode 100644
index 0000000000..367597515d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/008.js
@@ -0,0 +1,4 @@
+t.step(function() {
+ order.push(2);
+ document.write("<script src=\"008-1.js\"></script>");
+ }); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/009.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/009.html
new file mode 100644
index 0000000000..d7b78333b8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/009.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id");
+ document.write("='test'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010-1.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010-1.js
new file mode 100644
index 0000000000..fd815bab77
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010-1.js
@@ -0,0 +1,4 @@
+t.step(function() {
+ order.push(4);
+ assert_equals(document.getElementsByTagName("meta").length, 1);
+ }); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.html
new file mode 100644
index 0000000000..c8b9958258
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id=");
+ document.write("'test'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.js
new file mode 100644
index 0000000000..bb328ad55a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/010.js
@@ -0,0 +1,4 @@
+t.step(function() {
+ order.push(3);
+ assert_equals(document.getElementsByTagName("meta").length, 0);
+ });
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011-1.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011-1.js
new file mode 100644
index 0000000000..944b70d2d0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011-1.js
@@ -0,0 +1,5 @@
+t.step(function() {
+ order.push(4);
+ document.write("<meta>");
+ assert_equals(document.getElementsByTagName("meta").length, 1);
+ }); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.html
new file mode 100644
index 0000000000..33464429e6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id='");
+ document.write("test'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.js
new file mode 100644
index 0000000000..ce47bcd283
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/011.js
@@ -0,0 +1,5 @@
+t.step(function() {
+ order.push(3);
+ document.write("<script src='011-1.js'></script" + "><meta>");
+ assert_equals(document.getElementsByTagName("meta").length, 0);
+ }); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.html
new file mode 100644
index 0000000000..c9902a4875
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id='te");
+ document.write("st'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.js
new file mode 100644
index 0000000000..7ab4c6b386
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/012.js
@@ -0,0 +1,5 @@
+t.step(
+function() {
+ order.push(5);
+ assert_equals(document.getElementsByTagName("meta").length, 0);
+}); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.html
new file mode 100644
index 0000000000..7b87d28976
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id='test");
+ document.write("'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.js
new file mode 100644
index 0000000000..b5ce5f27da
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/013.js
@@ -0,0 +1 @@
+document.write('<svg><![CDATA['); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/014.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/014.html
new file mode 100644
index 0000000000..75518a8981
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/014.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id='test'");
+ document.write(">Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/015.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/015.html
new file mode 100644
index 0000000000..3dd79a63ef
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/015.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i id='test'");
+ document.write("class='a'>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute("id"), "test");
+ assert_equals(document.body.firstChild.getAttribute("class"), "a");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/016.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/016.html
new file mode 100644
index 0000000000..4c2f58912a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/016.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<i>Filler Text");
+ document.write("</i><b>Filler Text");
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+ assert_equals(document.body.childNodes[1].localName, "b");
+ assert_equals(document.body.childNodes[1].textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/017.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/017.html
new file mode 100644
index 0000000000..8d1b24b06e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/017.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ var s = "<i id=test>Filler Text</i><b>Filler Text"
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+ assert_equals(document.body.firstChild.localName, "i");
+ assert_equals(document.body.firstChild.getAttribute('id'), "test");
+ assert_equals(document.body.firstChild.textContent, "Filler Text");
+ assert_equals(document.body.childNodes[1].localName, "b");
+ assert_equals(document.body.childNodes[1].textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/018.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/018.html
new file mode 100644
index 0000000000..cf8dddbc54
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/018.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+function() {
+ document.write("<body>");
+ var s = "<!--comment--><i>Filler Text</i>"
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+ assert_equals(document.body.firstChild.nodeType, document.COMMENT_NODE);
+ assert_equals(document.body.firstChild.data, "comment");
+ assert_equals(document.body.childNodes[1].localName, "i");
+ assert_equals(document.body.childNodes[1].textContent, "Filler Text");
+}
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/019.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/019.html
new file mode 100644
index 0000000000..5e988f79ef
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/019.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<i");
+});
+</script>
+>Filler Text</i>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "i");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/020.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/020.html
new file mode 100644
index 0000000000..1d31bbf35d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/020.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><");
+});
+</script>!--comment-->
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].nodeType, document.COMMENT_NODE);
+ assert_equals(document.body.childNodes[0].data, "comment");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/021.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/021.html
new file mode 100644
index 0000000000..500bb19398
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/021.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><sp");
+});
+</script>an>Filler Text</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/022.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/022.html
new file mode 100644
index 0000000000..53ba299012
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/022.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span>");
+});
+</script>Filler Text</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/023.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/023.html
new file mode 100644
index 0000000000..ca89e0e0bc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/023.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span ");
+});
+</script>id=a>Filler Text</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/024.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/024.html
new file mode 100644
index 0000000000..2a47d76cb3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/024.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span i");
+});
+</script>d=a>Filler Text</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/025.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/025.html
new file mode 100644
index 0000000000..31c68cf7df
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/025.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span id");
+});
+</script>=a>Filler Text</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/026.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/026.html
new file mode 100644
index 0000000000..a9bce7743e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/026.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span id=");
+});
+</script>a>Filler Text</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/027.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/027.html
new file mode 100644
index 0000000000..dcfd67c0f7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/027.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span id=a");
+});
+</script>>Filler Text</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/028.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/028.html
new file mode 100644
index 0000000000..f5b7e9ef2b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/028.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span id=a>Filler Text<");
+});
+</script>/span><b>Filler Text</b></span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+ assert_equals(document.body.childNodes[1].localName, "b");
+ assert_equals(document.body.childNodes[1].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/029.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/029.html
new file mode 100644
index 0000000000..f005a72227
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/029.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span id=a>Filler Text</");
+});
+</script>span><b>Filler Text</b></span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+ assert_equals(document.body.childNodes[1].localName, "b");
+ assert_equals(document.body.childNodes[1].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/030.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/030.html
new file mode 100644
index 0000000000..cc361d3aaf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/030.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span id=a>Filler Text</sp");
+});
+</script>an><b>Filler Text</b></span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+ assert_equals(document.body.childNodes[1].localName, "b");
+ assert_equals(document.body.childNodes[1].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/031.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/031.html
new file mode 100644
index 0000000000..32c97c5056
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/031.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span id=a>Filler Text</span");
+});
+</script>><b>Filler Text</b></span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "span");
+ assert_equals(document.body.childNodes[0].getAttribute("id"), "a");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+ assert_equals(document.body.childNodes[1].localName, "b");
+ assert_equals(document.body.childNodes[1].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/032.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/032.html
new file mode 100644
index 0000000000..1a33408f1b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/032.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var tag_name_length = 100000;
+ var tag_name = "";
+ for (var i=0; i<tag_name_length; i++) {
+ tag_name += "a";
+ }
+ document.write("<body><" + tag_name + ">Filler Text</" + tag_name + ">");
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/033.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/033.html
new file mode 100644
index 0000000000..1b8e1c2706
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/033.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+test(
+ function() {
+ document.writeln("<i");
+ var s = " b='a'>Filler"
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]+"\n");
+ }
+ document.writeln("</i");
+ document.writeln(">");
+ assert_equals(document.body.childNodes[0].localName, "i");
+ assert_equals(document.body.childNodes[0].getAttribute("b"), "\na\n");
+ assert_equals(document.body.childNodes[0].textContent, "\nF\ni\nl\nl\ne\nr\n");
+ }
+);
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/034.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/034.html
new file mode 100644
index 0000000000..abd481a64d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/034.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<svg><![CDATA[Filler Text]]></svg>";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "svg");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/035.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/035.html
new file mode 100644
index 0000000000..a1e7f9ee67
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/035.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<svg><!";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script>[CDATA[Filler Text]]></svg>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "svg");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/036.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/036.html
new file mode 100644
index 0000000000..8719e0598d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/036.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<svg><![CDATA[Filler Text]";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script>]></svg>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "svg");
+ assert_equals(document.body.childNodes[0].textContent, "Filler Text");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/037.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/037.html
new file mode 100644
index 0000000000..cf0787ce76
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/037.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<body><!DOCTYPE html>";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script><script>
+t.step(function() {
+ //Nothing should be inserted into the DOM for the doctype node so
+ //just checking nothing odd happens
+ assert_equals(document.body.childNodes[0].localName, "script");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/038.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/038.html
new file mode 100644
index 0000000000..4ae9d32b23
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/038.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<body><";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script>!DOCTYPE html><script>
+t.step(function() {
+ //Nothing should be inserted into the DOM for the doctype node so
+ //just checking nothing odd happens
+ assert_equals(document.body.childNodes[0].localName, "script");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/039.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/039.html
new file mode 100644
index 0000000000..611a01390c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/039.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<body><!";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script>DOCTYPE html><script>
+t.step(function() {
+ //Nothing should be inserted into the DOM for the doctype node so
+ //just checking nothing odd happens
+ assert_equals(document.body.childNodes[0].localName, "script");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/040.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/040.html
new file mode 100644
index 0000000000..d76deffa40
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/040.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>document.write entity</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = test(function() {
+ document.write("<body><span>&notin;abc");
+ assert_equals(document.body.childNodes[0].textContent, "\u2209abc");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/041.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/041.html
new file mode 100644
index 0000000000..592711c94f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/041.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>document.write entity</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = test(function() {
+ var s = "<body><span>&notin;abc";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+ assert_equals(document.body.childNodes[0].textContent, "\u2209abc");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/042.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/042.html
new file mode 100644
index 0000000000..e15f1d0c0f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/042.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>document.write entity</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span>&not");
+});
+</script>in;abc</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].textContent, "\u2209abc");
+})
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/043.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/043.html
new file mode 100644
index 0000000000..4058e7a823
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/043.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>document.write entity</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><span>&");
+});
+</script>notabc</span>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].textContent, "\u00ACabc");
+})
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/044.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/044.html
new file mode 100644
index 0000000000..4c9f50273c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/044.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<body><textarea><span>Filler</span></textarea>");
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "textarea");
+ assert_equals(document.body.childNodes[0].textContent, "<span>Filler</span>");
+})
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/045.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/045.html
new file mode 100644
index 0000000000..987eabf0f4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/045.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<body><textarea><span>Filler</span></textarea>";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "textarea");
+ assert_equals(document.body.childNodes[0].textContent, "<span>Filler</span>");
+})
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/046.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/046.html
new file mode 100644
index 0000000000..e87e9cc825
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/046.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<body><textarea>";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script><span>Filler</span></textarea>
+<script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].localName, "textarea");
+ assert_equals(document.body.childNodes[0].textContent, "<span>Filler</span>");
+})
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047-1.html
new file mode 100644
index 0000000000..6a43faec51
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047-1.html
@@ -0,0 +1,7 @@
+<script>
+onload = opener.t.step_func_done(function() {
+ document.write("<body>Filler Text<div id='log'></div>");
+ opener.assert_equals(document.body.textContent, "Filler Text");
+});
+</script>
+<body>FAIL
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047.html
new file mode 100644
index 0000000000..677d3e1786
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/047.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var win;
+var t = async_test(() => {
+ win = window.open("047-1.html");
+});
+t.add_cleanup(() => win.close());
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/049.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/049.html
new file mode 100644
index 0000000000..0ec282f2bc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/049.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>document.write plaintext</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<div id="log"></div><script>
+test(function() {
+ var s = "<table><tr><td>Text</tr><plaintext><tr><td>Filler ";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+ document.close();
+ assert_equals(document.body.childNodes[2].nodeType, document.ELEMENT_NODE);
+ assert_equals(document.body.childNodes[2].localName, "plaintext");
+ assert_equals(document.body.childNodes[2].textContent, "<tr><td>Filler ");
+ assert_equals(document.body.childNodes[3].nodeType, document.ELEMENT_NODE);
+ assert_equals(document.body.childNodes[3].localName, "table");
+ assert_equals(document.body.childNodes[3].textContent, "Text");
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/050.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/050.html
new file mode 100644
index 0000000000..0a37fa4c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/050.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>document.write plaintext</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<div id="log"></div><script>
+var t = async_test();
+
+t.step(function() {
+ document.write("<plaintext>");
+ assert_equals(document.body.childNodes[2].nodeType, document.ELEMENT_NODE);
+ assert_equals(document.body.childNodes[2].localName, "plaintext");
+ var s = "Filler ";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ assert_equals(document.body.childNodes[2].textContent, s.slice(0,i+1));
+ }
+ document.close();
+});
+
+onload = function() {
+ t.step(function() {
+ assert_equals(document.body.childNodes[2].textContent, "Filler Text\n");
+ });
+ t.done();
+}
+</script>Text
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/051.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/051.html
new file mode 100644
index 0000000000..80ea279dad
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/051.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>document.write \r\n</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script>
+var t = async_test();
+
+t.step(function() {
+ document.write("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nA");
+})
+
+onload = function() {
+ t.step(function() {
+ const lastNode = document.getElementById('after');
+ assert_equals(lastNode.previousSibling.data, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nAB");
+ });
+ t.done();
+};
+</script>B<div id=after></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/contentType.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/contentType.window.js
new file mode 100644
index 0000000000..5a91203874
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/contentType.window.js
@@ -0,0 +1,28 @@
+// META: script=/common/media.js
+
+const videoURL = getVideoURI("/images/pattern"),
+ videoMIMEType = getMediaContentType(videoURL);
+
+[
+ [videoURL, videoMIMEType, "video"],
+ ["/images/red.png", "image/png", "image"],
+ ["/common/text-plain.txt", "text/plain", "text"],
+ ["/common/blank.html", "text/html", "HTML"]
+].forEach(val => {
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.src = val[0];
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.contentType, val[1]);
+ frame.contentDocument.write("<b>Heya</b>");
+ assert_equals(frame.contentDocument.body.firstChild.localName, "b");
+ assert_equals(frame.contentDocument.body.firstChild.textContent, "Heya");
+ assert_equals(frame.contentDocument.contentType, val[1]);
+
+ // Make sure a load event is fired across browsers
+ // https://github.com/web-platform-tests/wpt/pull/10239
+ frame.contentDocument.close();
+ });
+ }, "document.write(): " + val[2] + " document");
+});
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-01.xhtml b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-01.xhtml
new file mode 100644
index 0000000000..fc21d4e2bf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-01.xhtml
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.write in XHTML</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.write%28%29"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ document.write("Failure: document.write actually worked");
+ }, "document.write in XHTML should throw an INVALID_STATE_ERR ");
+}, "document.write in XHTML");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-02.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-02.html
new file mode 100644
index 0000000000..4c25da8b68
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/document.write-02.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>document.write and null/undefined</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-write%28%29">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#documents-in-the-dom">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ doc = iframe.contentDocument;
+ test(function() {
+ doc.open();
+ doc.write(null);
+ doc.close();
+ assert_equals(doc.documentElement.textContent, "null");
+ }, "document.write(null)");
+ test(function() {
+ doc.open();
+ doc.write(undefined);
+ doc.close();
+ assert_equals(doc.documentElement.textContent, "undefined");
+ }, "document.write(undefined)");
+}, "Calling document.write with null and undefined");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/during-readystatechange.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/during-readystatechange.window.js
new file mode 100644
index 0000000000..49d5051c25
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/during-readystatechange.window.js
@@ -0,0 +1,24 @@
+// This tests whether the insertion point gets reset before or after the readystatechange event.
+// See https://github.com/whatwg/html/pull/6613#discussion_r620171070.
+// Recall that resetting the insertion point means that document.write() performs the document open
+// steps and blows away previous content in the document.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { frame.remove(); });
+ frame.src = "../opening-the-input-stream/resources/dummy.html";
+ frame.onload = t.step_func_done(() => {
+ const states = [];
+ frame.contentDocument.onreadystatechange = t.step_func(() => {
+ if (frame.contentDocument.readyState === "interactive") {
+ assert_not_equals(frame.contentDocument.textContent, "", "Precondition check: dummy document is not empty");
+
+ frame.contentDocument.write("Some text");
+
+ // If the insertion point is reset before the readystatechange handler, then the
+ // document.write() call above will blow away the text originally in dummy.html, leaving only what we wrote.
+ assert_equals(frame.contentDocument.textContent, "Some text");
+ }
+ });
+ });
+}, "document.write() during readystatechange to interactive");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/empty.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/empty.html
new file mode 100644
index 0000000000..0dc101b533
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/empty.html
@@ -0,0 +1 @@
+<html><body></body></html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_001.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_001.html
new file mode 100644
index 0000000000..8b54560c6c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_001.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>document.write into iframe</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+test(
+function() {
+ var iframe = document.getElementById("test");
+ iframe.contentDocument.write("Filler Text");
+ iframe.contentDocument.close();
+ assert_equals(iframe.contentDocument.body.textContent, "Filler Text");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_002.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_002.html
new file mode 100644
index 0000000000..f77819adb6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_002.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>document.write into iframe</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+test(
+function() {
+ var iframe = document.getElementById("test");
+ var s = "<i id='a'>Filler Text</i><b id=b>Filler Text</b>"
+ for (var i=0; i<s.length; i++) {
+ iframe.contentDocument.write(s[i]);
+ }
+ iframe.contentDocument.close();
+ assert_equals(iframe.contentDocument.body.childNodes[0].textContent, "Filler Text");
+ assert_equals(iframe.contentDocument.body.childNodes[0].localName, "i");
+ assert_equals(iframe.contentDocument.body.childNodes[0].getAttribute('id'), "a");
+ assert_equals(iframe.contentDocument.body.childNodes[1].textContent, "Filler Text");
+ assert_equals(iframe.contentDocument.body.childNodes[1].localName, "b");
+ assert_equals(iframe.contentDocument.body.childNodes[1].getAttribute('id'), "b");
+});
+</script>
+<div id="log"></div> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_003.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_003.html
new file mode 100644
index 0000000000..9865874da4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_003.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>document.write script into iframe</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+test(
+function() {
+ var iframe = document.getElementById("test");
+ var s = "<script>document.write(\"<i id='a'>Filler Text</i>\")</script" + "><b id=b>Filler Text</b>"
+ for (var i=0; i<s.length; i++) {
+ iframe.contentDocument.write(s[i]);
+ }
+ iframe.contentDocument.close();
+ //Note: <script> ends up in <head>
+ assert_equals(iframe.contentDocument.body.childNodes[0].textContent, "Filler Text");
+ assert_equals(iframe.contentDocument.body.childNodes[0].localName, "i");
+ assert_equals(iframe.contentDocument.body.childNodes[0].getAttribute('id'), "a");
+ assert_equals(iframe.contentDocument.body.childNodes[1].textContent, "Filler Text");
+ assert_equals(iframe.contentDocument.body.childNodes[1].localName, "b");
+ assert_equals(iframe.contentDocument.body.childNodes[1].getAttribute('id'), "b");
+});
+</script>
+<div id="log"></div> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_004.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_004.html
new file mode 100644
index 0000000000..a4d7b1ddad
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_004.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>document.write script into iframe write back into parent</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var iframe = document.getElementById("test");
+var order = [];
+t.step(function() {
+ order.push(1);
+ var s = "<script>parent.order.push(2); parent.document.write('<script>order.push(3);</script'+'>'); parent.order.push(4)</script" + ">";
+ for (var i=0; i<s.length; i++) {
+ iframe.contentDocument.write(s[i]);
+ }
+ iframe.contentDocument.close();
+ order.push(5);
+ assert_array_equals(order, [1,2,3,4,5])
+}
+);
+t.done();
+</script>
+<div id="log"></div> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html
new file mode 100644
index 0000000000..7bc3ed6c29
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>document.write external script into iframe write back into parent</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var iframe = document.getElementById("test");
+var order = [];
+t.step(function() {
+ order.push(1);
+ var s = "<script src='iframe_005.js'></script" + ">";
+ iframe.contentDocument.write(s);
+ iframe.contentDocument.close();
+ order.push(2);
+ assert_array_equals(order, [1,2])
+}
+);
+addEventListener("load", function() {
+ t.step(function() {
+ assert_array_equals(order, [1,2,3,4,5])
+ });
+ t.done();
+}, false);
+</script>
+<div id="log"></div> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.js
new file mode 100644
index 0000000000..bf038f7004
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.js
@@ -0,0 +1,3 @@
+parent.order.push(3);
+document.write("<script>parent.order.push(4)</script>");
+parent.order.push(5); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_006.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_006.html
new file mode 100644
index 0000000000..d080ee3673
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_006.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write external script into iframe write back into parent</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var iframe = document.getElementById("test");
+var order = [];
+t.step(function() {
+ order.push(1);
+ var s = "<script>parent.order.push(2); parent.document.write('<script>order.push(3); iframe.contentDocument.write(\"<script>parent.order.push(4)</script\"+\">\");order.push(5);</script' + '>'); parent.order.push(6)</script"+">";
+ iframe.contentDocument.write(s);
+ iframe.contentDocument.close();
+ order.push(7);
+ assert_array_equals(order, [1,2,3,4,5,6,7]);
+});
+t.done();
+</script>
+<div id="log"></div> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_007.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_007.html
new file mode 100644
index 0000000000..c00aa7062d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_007.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>document.write comment into iframe</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+test(function() {
+ var iframe = document.getElementById("test");
+ var s = "<!--Filler-->";
+ for (var i=0; i<s.length; i++) {
+ iframe.contentDocument.write(s);
+ }
+ iframe.contentDocument.close();
+ assert_equals(iframe.contentDocument.childNodes[0].nodeType, document.COMMENT_NODE);
+ assert_equals(iframe.contentDocument.childNodes[0].data, "Filler");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_008.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_008.html
new file mode 100644
index 0000000000..c814958d19
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_008.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>document.write plaintext into iframe</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+test(function() {
+ var iframe = document.getElementById("test");
+ var s = "<plaintext><span>Filler Text";
+ for (var i=0; i<s.length; i++) {
+ iframe.contentDocument.write(s[i]);
+ }
+ iframe.contentDocument.close();
+ assert_equals(iframe.contentDocument.body.childNodes[0].nodeType, document.ELEMENT_NODE);
+ assert_equals(iframe.contentDocument.body.childNodes[0].localName, "plaintext");
+ assert_equals(iframe.contentDocument.body.childNodes[0].textContent, "<span>Filler Text");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_009.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_009.html
new file mode 100644
index 0000000000..8b271c7a03
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_009.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>document.write plaintext into iframe</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+test(function() {
+ var iframe = document.getElementById("test");
+ var s = "<table><tr><td>Text</tr><plaintext><tr><td>Filler ";
+ for (var i=0; i<s.length; i++) {
+ iframe.contentDocument.write(s[i]);
+ }
+ iframe.contentDocument.close();
+ assert_equals(iframe.contentDocument.body.childNodes[0].nodeType, document.ELEMENT_NODE);
+ assert_equals(iframe.contentDocument.body.childNodes[0].localName, "plaintext");
+ assert_equals(iframe.contentDocument.body.childNodes[0].textContent, "<tr><td>Filler ");
+ assert_equals(iframe.contentDocument.body.childNodes[1].nodeType, document.ELEMENT_NODE);
+ assert_equals(iframe.contentDocument.body.childNodes[1].localName, "table");
+ assert_equals(iframe.contentDocument.body.childNodes[1].textContent, "Text");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_010.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_010.html
new file mode 100644
index 0000000000..8dc21a013a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/iframe_010.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>document.write plaintext</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var iframe = document.getElementById("test");
+
+function check_dom() {
+ assert_equals(iframe.contentDocument.body.childNodes[0].localName, "plaintext")
+ assert_equals(iframe.contentDocument.body.childNodes[0].textContent, "Filler ")
+ assert_equals(iframe.contentDocument.body.childNodes[1].localName, "table")
+}
+
+t.step(function() {
+ var s = "<script>document.write('<table><plaintext>Filler '); document.close(); top.t.step(function() {top.check_dom()})</script" + ">";
+ for (var i=0; i<s.length; i++) {
+ iframe.contentDocument.write(s[i]);
+ }
+ t.done();
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed-iframe.html
new file mode 100644
index 0000000000..f97f597238
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed-iframe.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<script type=module>
+window.parent.document.test.step_timeout(() => {
+ document.write("document.write body contents\n")
+ document.close();
+ window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
+}, 0);
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed.html
new file mode 100644
index 0000000000..acdeab59ff
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-delayed.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>async document.write in a module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ // Expose {test} in the iframe for using the step_timeout helper.
+ document.test = t;
+ const iframe = document.createElement("iframe");
+
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ let onLoadWasCalled = false;
+ iframe.onload = t.step_func(() => {
+ onLoadWasCalled = true;
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ // Don't call the event handler another time after document.write.
+ iframe.onload = null;
+ });
+ document.addEventListener("documentWriteDone", t.step_func_done(() => {
+ assert_true(onLoadWasCalled, "onload must be called");
+ assert_equals(iframe.contentDocument.body.textContent, "document.write body contents\n");
+ }));
+
+ iframe.src = "module-delayed-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import-iframe.html
new file mode 100644
index 0000000000..672bb953d6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import-iframe.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script type=module>
+(async () => {
+ let module = await import("./module-dynamic-import.mjs");
+})();
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.html
new file mode 100644
index 0000000000..5939968f05
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>document.write in an imported module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ const iframe = document.createElement("iframe");
+
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ let onLoadWasCalled = false;
+ iframe.onload = t.step_func(() => {
+ onLoadWasCalled = true;
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ // Don't call the event handler another time after document.write.
+ iframe.onload = null;
+ });
+ document.addEventListener("documentWriteDone", t.step_func_done(() => {
+ assert_true(onLoadWasCalled, "onload must be called");
+ assert_equals(iframe.contentDocument.body.textContent, "document.write body contents\n");
+ }));
+
+ iframe.src = "module-dynamic-import-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.mjs b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.mjs
new file mode 100644
index 0000000000..74d2427537
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.mjs
@@ -0,0 +1,4 @@
+document.write("document.write body contents\n");
+document.close();
+
+window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-iframe.html
new file mode 100644
index 0000000000..f8646df56b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-iframe.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script type=module>
+document.write("document.write body contents\n");
+document.close();
+window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed-iframe.html
new file mode 100644
index 0000000000..3ae1464653
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed-iframe.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script type=module>
+import "./module-static-import-delayed.mjs"
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html
new file mode 100644
index 0000000000..a6e003907f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>document.write in an imported module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ // Expose {test} in the iframe for using the step_timeout helper.
+ document.test = t;
+ const iframe = document.createElement("iframe");
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ let onLoadWasCalled = false;
+ iframe.onload = t.step_func(() => {
+ onLoadWasCalled = true;
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ // Don't call the event handler another time after document.write.
+ iframe.onload = null;
+ });
+ document.addEventListener("documentWriteDone", t.step_func_done(() => {
+ assert_true(onLoadWasCalled, "onload must be called");
+ assert_equals(iframe.contentDocument.body.textContent, "document.write body contents\n");
+ }));
+
+ iframe.src = "module-static-import-delayed-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.mjs b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.mjs
new file mode 100644
index 0000000000..45478d6f63
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.mjs
@@ -0,0 +1,5 @@
+window.parent.document.test.step_timeout(() => {
+ document.write("document.write body contents\n")
+ document.close();
+ window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
+}, 0);
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-iframe.html
new file mode 100644
index 0000000000..ed4f6d1c6c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-iframe.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script type=module>
+import "./module-static-import.mjs"
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.html
new file mode 100644
index 0000000000..3cae88047e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>document.write in an imported module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ const iframe = document.createElement("iframe");
+
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ let testEndWasCalled = false;
+ document.addEventListener("documentWriteDone", t.step_func(() => {
+ testEndWasCalled = true;
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ }));
+ iframe.onload = t.step_func_done(() => {
+ assert_true(testEndWasCalled, "onload must be called");
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ });
+
+ iframe.src = "module-static-import-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
+ß
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.mjs b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.mjs
new file mode 100644
index 0000000000..74d2427537
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-static-import.mjs
@@ -0,0 +1,4 @@
+document.write("document.write body contents\n");
+document.close();
+
+window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed-iframe.html
new file mode 100644
index 0000000000..5629c47be7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed-iframe.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<script type=module>
+let delay = new Promise(
+ resolve => window.parent.document.test.step_timeout(resolve, 0));
+
+delay.then(() => {
+ document.write("document.write body contents\n");
+ document.close();
+ window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
+});
+
+await delay;
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html
new file mode 100644
index 0000000000..5fa8216600
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>document.write in an imported module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ // Expose {test} in the iframe for using the step_timeout helper.
+ document.test = t;
+
+ const iframe = document.createElement("iframe");
+
+ iframe.onunhandledrejection = t.unreached_func("Unhandled promise rejection detected");
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ let onLoadWasCalled = false;
+ iframe.onload = t.step_func(() => {
+ onLoadWasCalled = true;
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ iframe.onload = null;
+ });
+ document.addEventListener("documentWriteDone", t.step_func_done(() => {
+ assert_true(onLoadWasCalled, "onload must be called");
+ assert_equals(iframe.contentDocument.body.textContent, "document.write body contents\n");
+ }));
+
+ iframe.src = "module-tla-delayed-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise-iframe.html
new file mode 100644
index 0000000000..3e90fb2ea7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise-iframe.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<script type=module>
+await new Promise(resolve => {
+ document.write("document.write body contents\n");
+ document.close();
+ window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
+ resolve();
+});
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html
new file mode 100644
index 0000000000..f60aa38e00
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>document.write in an imported module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ // Expose {test} in the iframe for using the step_timeout helper.
+ document.test = t;
+
+ const iframe = document.createElement("iframe");
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ let onLoadWasCalled = false;
+ iframe.onload = t.step_func(() => {
+ onLoadWasCalled = true;
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ // Don't call the event handler another time after document.write.
+ iframe.onload = null;
+ });
+ document.addEventListener("documentWriteDone", t.step_func_done(() => {
+ assert_false(onLoadWasCalled, "onload must not be called yet");
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ }));
+
+ iframe.src = "module-tla-immediate-promise-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import-iframe.html
new file mode 100644
index 0000000000..ec4a6ed6aa
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import-iframe.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script type=module>
+await import("./module-tla-import.mjs");
+</script>
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html
new file mode 100644
index 0000000000..20645f4d78
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>document.write in an imported module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ let onLoadWasCalled = false;
+
+ iframe.onload = t.step_func(() => {
+ onLoadWasCalled = true;
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ // Don't call the event handler another time after document.write.
+ iframe.onload = null;
+ });
+ document.addEventListener("documentWriteDone", t.step_func_done(() => {
+ assert_true(onLoadWasCalled, "onload must be called");
+ assert_equals(iframe.contentDocument.body.textContent, "document.write body contents\n");
+ }));
+
+ iframe.src = "module-tla-import-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.mjs b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.mjs
new file mode 100644
index 0000000000..74d2427537
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.mjs
@@ -0,0 +1,4 @@
+document.write("document.write body contents\n");
+document.close();
+
+window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise-iframe.html
new file mode 100644
index 0000000000..edc9e80cb3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise-iframe.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<script type=module>
+await new Promise(resolve => {
+ window.parent.document.test.step_timeout(resolve, 0);
+ document.write("document.write body contents\n");
+ document.close();
+ window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
+});
+</script>
+
+Initial body contents
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html
new file mode 100644
index 0000000000..4f1281bcce
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>document.write in an imported module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ // Expose {test} in the iframe for using the step_timeout helper.
+ document.test = t;
+
+ const iframe = document.createElement("iframe");
+ iframe.onerror = t.unreached_func("Error loading iframe");
+
+ document.addEventListener("documentWriteDone", t.step_func(() => {
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ }));
+ iframe.onload = t.step_func_done(() => {
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ });
+
+ iframe.src = "module-tla-promise-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module.html
new file mode 100644
index 0000000000..7e970d3fd9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/module.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>document.write in a module</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(t => {
+ const iframe = document.createElement("iframe");
+
+ iframe.onerror = t.unreached_func("Error loading iframe");
+ document.addEventListener("documentWriteDone", t.step_func(() => {
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ }));
+ iframe.onload = t.step_func_done(() => {
+ assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
+ });
+
+ iframe.src = "module-iframe.html";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-1.html
new file mode 100644
index 0000000000..c7a7a1db4e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<body>You should see the word "worked" below<br><script>document.write("\u003cscript>document.write(\"\\u003cscript src='nested-document-write-external.js'>\\u003c/script>r\"); document.write(\"k\");\u003c/script>e"); document.write("d");</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-2.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-2.html
new file mode 100644
index 0000000000..60b8eae1ef
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<body>
+You should see the word "worked" in the frame below.<br>
+<iframe></iframe>
+<script>
+var doc = document.getElementsByTagName("iframe")[0].contentDocument;
+doc.open(); doc.write("\u003cscript>document.write(\"\\u003cscript src='nested-document-write-external.js'>\\u003c/script>r\"); document.write(\"k\");\u003c/script>e"); doc.write("d"); doc.close();</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-external.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-external.js
new file mode 100644
index 0000000000..bf91daf986
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/nested-document-write-external.js
@@ -0,0 +1 @@
+document.write("w"); document.write("o");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/original-id.json b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/original-id.json
new file mode 100644
index 0000000000..08bd4d0d4e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/original-id.json
@@ -0,0 +1 @@
+{"original_id":"document.write()"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_001.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_001.html
new file mode 100644
index 0000000000..43c7adb4d4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_001.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>document.write script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<script>t.done();<"+"/script>");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_002.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_002.html
new file mode 100644
index 0000000000..3879d8489f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_002.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>document.write script executed synchronously</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ document.write("<script>t.step(function() {order.push(1);});<"+"/script>");
+ order.push(2);
+});
+</script>
+<script>
+t.step(function() {
+ order.push(3);
+ assert_array_equals(order, [1,2,3]);
+})
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_003.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_003.html
new file mode 100644
index 0000000000..e669252f75
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_003.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>document.write script writing a further script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ document.write("<script>document.write('<script>t.done()</script'+'>')<"+"/script>");
+});
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_004.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_004.html
new file mode 100644
index 0000000000..15fda325b1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_004.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write script writing script; order of execution</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+ document.write("<script>order.push(2); document.write('<script>order.push(3);</script'+'>'); order.push(4);<"+"/script>");
+ order.push(5);
+});
+</script>
+<script>
+t.step(function() {
+ assert_array_equals(order, [1,2,3,4,5]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_005.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_005.html
new file mode 100644
index 0000000000..b99196c7d0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_005.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>document.write external script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+ document.write("<script src='005.js'><"+"/script>");
+ order.push(2);
+});
+</script>
+<script>
+order.push(4);
+t.step(function() {
+ assert_array_equals(order, [1,2,3,4]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_006.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_006.html
new file mode 100644
index 0000000000..c8dd9a5f9a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_006.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>document.write external script followed by internal script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+ document.write("<script src='006.js'><"+"/script><script>t.step(function(){order.push(4)})</script"+">");
+ order.push(2);
+});
+</script>
+<script>
+t.step(function() {
+ order.push(5);
+ assert_array_equals(order, [1,2,3,4,5]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_007.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_007.html
new file mode 100644
index 0000000000..fbbe5b2f86
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_007.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write external script that document.writes inline script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+});
+</script>
+<script src="007.js"></script>
+<script>
+t.step(function() {
+ order.push(4);
+ assert_array_equals(order, [1,2,3,4]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_008.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_008.html
new file mode 100644
index 0000000000..c5a44dc700
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_008.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write external script that document.writes external script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+});
+</script>
+<script src="008.js"></script>
+<script>
+t.step(function() {
+ order.push(4);
+ assert_array_equals(order, [1,2,3,4]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_009.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_009.html
new file mode 100644
index 0000000000..d12d934ea0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_009.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>document.write script that document.writes script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+ document.write("<script>order.push(2); document.write('<script>order.push(3); document.write(\"<script>order.push(4);</script\"+\">\"); order.push(5);</script' + '>'); order.push(6);</script" + ">");
+ order.push(7);
+});
+</script>
+<script>
+t.step(function() {
+ assert_array_equals(order, [1,2,3,4,5,6,7]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_010.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_010.html
new file mode 100644
index 0000000000..93728d6f27
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_010.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>document.write external script tokenizer order</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+ document.write("<script src='010.js'></script" + "><meta><script src='010-1.js'></script" + ">");
+ order.push(2);
+ assert_equals(document.getElementsByTagName("meta").length, 0);
+});
+</script>
+<script>
+t.step(function() {
+ order.push(5);
+ assert_equals(document.getElementsByTagName("meta").length, 1);
+ assert_array_equals(order, [1,2,3,4,5]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_011.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_011.html
new file mode 100644
index 0000000000..2bbcaf976e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_011.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>document.write external script that document.writes external script</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+ document.write("<script src='011.js'></script" + "><meta>");
+ order.push(2);
+ assert_equals(document.getElementsByTagName("meta").length, 0);
+});
+</script>
+<script>
+t.step(function() {
+ order.push(5);
+ assert_equals(document.getElementsByTagName("meta").length, 3, "Number of meta elements at end");
+ assert_array_equals(order, [1,2,3,4,5]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_012.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_012.html
new file mode 100644
index 0000000000..57755f4c94
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_012.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>document.write external script tokenizer order</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+var order = [];
+t.step(function() {
+ order.push(1);
+ document.write("<script>order.push(2); document.write('<script src=\"012.js\"></script' + '><meta>'); order.push(3); t.step(function() {assert_equals(document.getElementsByTagName('meta').length, 0)});</script" + "><meta>");
+ order.push(4);
+ assert_equals(document.getElementsByTagName("meta").length, 0);
+});
+</script>
+<script>
+t.step(function() {
+ order.push(6);
+ assert_equals(document.getElementsByTagName("meta").length, 2);
+ assert_array_equals(order, [1,2,3,4,5,6]);
+});
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_013.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_013.html
new file mode 100644
index 0000000000..0e71e5eb0c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/script_013.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>document.write</title>
+<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test();
+t.step(function() {
+ var s = "<script src='013.js'><" + "/script></svg>]]><path></svg>";
+ for (var i=0; i<s.length; i++) {
+ document.write(s[i]);
+ }
+});
+</script><script>
+t.step(function() {
+ assert_equals(document.body.childNodes[0].nodeType, document.ELEMENT_NODE);
+ assert_equals(document.body.childNodes[0].localName, "svg");
+ assert_equals(document.body.childNodes[0].childNodes[0].nodeType, document.TEXT_NODE);
+ assert_equals(document.body.childNodes[0].childNodes[0].data, "</svg>");
+ assert_equals(document.body.childNodes[0].childNodes[1].nodeType, document.ELEMENT_NODE);
+ assert_equals(document.body.childNodes[0].childNodes[1].localName, "path");
+}
+);
+t.done();
+</script>
+<div id="log"></div>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/write-active-document.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/write-active-document.html
new file mode 100644
index 0000000000..6faffd81de
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-write/write-active-document.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>document.write only writes to active documents</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body><div id="log"></div></body>
+<script>
+ async_test(function(t) {
+ var child = document.createElement("iframe");
+ child.src = "empty.html?1";
+ child.onload = t.step_func(function() {
+ var child1 = child.contentDocument;
+ var link = child1.createElement("a");
+ link.href = "data:text/html,Clicked.";
+ link.innerText = "Link.";
+ child1.body.appendChild(link);
+ var grandchild = child1.createElement("iframe");
+ grandchild.src = "empty.html?2";
+ grandchild.onload = t.step_func(function() {
+ var grandchild1 = grandchild.contentDocument;
+ child.onload = t.step_func(function() {
+ // This is a write to an inactive document
+ child1.write('WRITE HAPPENED');
+ assert_equals(child1.body.lastChild.tagName, "IFRAME");
+ // This is a write to an active but not fully active document
+ grandchild1.write('WRITE HAPPENED');
+ assert_equals(grandchild1.body.innerHTML, "WRITE HAPPENED");
+ t.done();
+ });
+ link.click();
+ });
+ child1.body.appendChild(grandchild);
+ });
+ document.body.appendChild(child);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-01.xhtml b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-01.xhtml
new file mode 100644
index 0000000000..cb5ec3a33a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-01.xhtml
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.writeln in XHTML</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.writeln%28%29"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ document.writeln("Failure: document.writeln actually worked");
+ }, "document.writeln in XHTML should throw an INVALID_STATE_ERR ");
+}, "document.writeln in XHTML");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-02.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-02.html
new file mode 100644
index 0000000000..2a64ac7561
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-02.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>document.writeln and null/undefined</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-writeln%28%29">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#documents-in-the-dom">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ doc = iframe.contentDocument;
+ test(function() {
+ doc.open();
+ doc.writeln(null);
+ doc.close();
+ assert_equals(doc.documentElement.textContent, "null\n");
+ }, "document.writeln(null)");
+ test(function() {
+ doc.open();
+ doc.writeln(undefined);
+ doc.close();
+ assert_equals(doc.documentElement.textContent, "undefined\n");
+ }, "document.writeln(undefined)");
+}, "Calling document.writeln with null and undefined");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-03.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-03.html
new file mode 100644
index 0000000000..df9a7a15c2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/document.writeln-03.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>document.writeln with multiple arguments</title>
+<link rel="author" title="Sebmaster" href="mailto:wpt@smayr.name">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-writeln%28%29">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#documents-in-the-dom">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ var doc = iframe.contentDocument;
+ doc.open();
+ doc.writeln('a', 'b');
+ doc.close();
+ assert_equals(doc.documentElement.textContent, "ab\n");
+}, "Calling document.writeln with multiple arguments");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/original-id.json b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/original-id.json
new file mode 100644
index 0000000000..0cc32be6a2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/document-writeln/original-id.json
@@ -0,0 +1 @@
+{"original_id":"document.writeln()"} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.html
new file mode 100644
index 0000000000..1debcafa3b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="windows-1252"> <!-- intentional to make sure the results are UTF-8 anyway -->
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9538">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This was adapted from DOMParser-parseFromString-encoding.html -->
+
+<script>
+function assertEncoding(doc) {
+ assert_equals(doc.charset, "UTF-8", "document.charset");
+ assert_equals(doc.characterSet, "UTF-8", "document.characterSet");
+ assert_equals(doc.inputEncoding, "UTF-8", "document.characterSet");
+}
+
+setup(() => {
+ assert_equals(document.characterSet, "windows-1252", "the meta charset must be in effect, making the main document windows-1252");
+});
+
+test(() => {
+ const doc = Document.parseHTMLUnsafe('');
+ assertEncoding(doc);
+}, 'Parse empty string');
+
+test(() => {
+ const doc = Document.parseHTMLUnsafe(`<meta charset="latin2">`);
+ assertEncoding(doc);
+}, "meta charset latin2");
+
+test(() => {
+ const doc = Document.parseHTMLUnsafe(`<?xml version="1.0" encoding="latin2"?><x/>`);
+ assertEncoding(doc);
+}, "XML declaration");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.html
new file mode 100644
index 0000000000..cd092a6034
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from style_attribute_html.html -->
+<meta charset=utf-8>
+<title>Style attribute in HTML</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+var div;
+setup(function() {
+ var input = '<div style="color: red">Foo</div>';
+ var doc = Document.parseHTMLUnsafe(input);
+ div = doc.querySelector('div');
+});
+
+test(function() {
+ var style = div.style;
+ assert_equals(style.cssText, 'color: red;');
+ assert_equals(style.color, 'red');
+ assert_equals(div.getAttribute("style"), 'color: red',
+ 'Value of style attribute should match the string value that was set');
+}, 'Parsing of initial style attribute');
+
+test(function() {
+ var style = div.style;
+ div.setAttribute('style', 'color:: invalid');
+ assert_equals(style.cssText, '');
+ assert_equals(style.color, '');
+ assert_equals(div.getAttribute('style'), 'color:: invalid',
+ 'Value of style attribute should match the string value that was set');
+}, 'Parsing of invalid style attribute');
+
+test(function() {
+ var style = div.style;
+ div.setAttribute('style', 'color: green');
+ assert_equals(style.cssText, 'color: green;');
+ assert_equals(style.color, 'green');
+ assert_equals(div.getAttribute('style'), 'color: green',
+ 'Value of style attribute should match the string value that was set');
+}, 'Parsing of style attribute');
+
+test(function() {
+ var style = div.style;
+ style.backgroundColor = 'blue';
+ assert_equals(style.cssText, 'color: green; background-color: blue;',
+ 'Should not drop the existing style');
+ assert_equals(style.color, 'green',
+ 'Should not drop the existing style');
+ assert_equals(div.getAttribute('style'), 'color: green; background-color: blue;',
+ 'Should update style attribute');
+}, 'Update style.backgroundColor');
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.html
new file mode 100644
index 0000000000..a8eab59511
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url-base-pushstate.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (base, pushstate)</title>
+<base href="/fake/base-from-outer-frame">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html" onload="window.resolveLoadPromise();"></iframe>
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-outer-frame");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.html
new file mode 100644
index 0000000000..62b2c09aae
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This was adapted from DOMParser-parseFromString-url-base.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (base, no pushstate)</title>
+<base href="/fake/base-from-outer-frame">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html" onload="window.resolveLoadPromise();"></iframe>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.html
new file mode 100644
index 0000000000..41335ef035
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url-moretests.html -->
+<meta charset=utf-8>
+<title>Document.parseHTMLUnsafe: Document's url</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function() {
+ var iframe = document.createElement("iframe");
+ iframe.onload = this.step_func(function() {
+ var child = iframe.contentWindow;
+
+ test(function() {
+ var doc = Document.parseHTMLUnsafe("<html></html>");
+ assert_equals(doc.URL, "about:blank");
+ }, "Parent window");
+
+ test(function() {
+ var doc = child.Document.parseHTMLUnsafe("<html></html>");
+ assert_equals(doc.URL, "about:blank");
+ }, "Child window");
+
+ var dpBeforeNavigation = child.Document, urlBeforeNavigation = child.document.URL;
+ iframe.onload = this.step_func_done(function() {
+ test(function() {
+ var doc = dpBeforeNavigation.parseHTMLUnsafe("<html></html>");
+ assert_equals(doc.URL, "about:blank");
+ }, "Child window crossing navigation");
+
+ test(function() {
+ var doc = child.Document.parseHTMLUnsafe("<html></html>");
+ assert_equals(doc.URL, "about:blank");
+ }, "Child window after navigation");
+ });
+ iframe.src = "/common/blank.html?2";
+ });
+ iframe.src = "/common/blank.html?1";
+ document.body.appendChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.html
new file mode 100644
index 0000000000..7930bf0ddb
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url-pushstate.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (no base, pushstate)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html" onload="window.resolveLoadPromise();"></iframe>
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-outer-frame");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.html
new file mode 100644
index 0000000000..911e183706
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (no pushstate, no base)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="resources/parseHTMLUnsafe-iframe.html" onload="window.resolveLoadPromise();"></iframe>
+<script src="resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.html
new file mode 100644
index 0000000000..2a89370825
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This was adapted from DOMParser-parseFromString-html.html -->
+<title>parseHTMLUnsafe basic test of HTML parsing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// |expected| should be an object indicating the expected type of node.
+function assert_node(actual, expected) {
+ assert_true(actual instanceof expected.type,
+ 'Node type mismatch: actual = ' + actual.constructor.name + ', expected = ' + expected.type.name);
+ if (typeof(expected.id) !== 'undefined')
+ assert_equals(actual.id, expected.id, expected.idMessage);
+}
+
+var doc;
+setup(function() {
+ doc = Document.parseHTMLUnsafe('<html id="root"><head></head><body></body></html>');
+});
+
+test(function() {
+ var root = doc.documentElement;
+ assert_node(root, { type: HTMLHtmlElement, id: 'root',
+ idMessage: 'documentElement id attribute should be root.' });
+}, 'Parsing of id attribute');
+
+test(function() {
+ assert_equals(doc.contentType, "text/html")
+}, 'contentType');
+
+test(function() {
+ assert_equals(doc.compatMode, "BackCompat")
+}, 'compatMode');
+
+test(function() {
+ doc = Document.parseHTMLUnsafe('<!DOCTYPE html><html id="root"><head></head><body></body></html>');
+ assert_equals(doc.compatMode, "CSS1Compat")
+}, 'compatMode for a proper DOCTYPE');
+
+// URL- and encoding-related stuff tested separately.
+
+test(function() {
+ assert_equals(doc.location, null,
+ 'The document must have a location value of null.');
+}, 'Location value');
+
+test(function() {
+ var htmldoc = Document.parseHTMLUnsafe("<!DOCTYPE foo></><foo></multiple></>");
+ assert_equals(htmldoc.documentElement.localName, "html");
+ assert_equals(htmldoc.documentElement.namespaceURI, "http://www.w3.org/1999/xhtml");
+}, "Document.parseHTMLUnsafe parses HTML tag soup with no problems");
+
+test(function() {
+ const doc = Document.parseHTMLUnsafe('<noembed>&lt;a&gt;</noembed>');
+ assert_equals(doc.querySelector('noembed').textContent, '&lt;a&gt;');
+}, 'Document.parseHTMLUnsafe should handle the content of <noembed> as raw text');
+
+test(() => {
+ const doc = Document.parseHTMLUnsafe(`
+<html><body>
+<style>
+ @import url(/dummy.css)
+</style>
+<script>document.x = 8<\/script>
+</body></html>`);
+
+ assert_not_equals(doc.querySelector('script'), null, 'script must be found');
+ assert_equals(doc.x, undefined, 'script must not be executed on the inner document');
+ assert_equals(document.x, undefined, 'script must not be executed on the outer document');
+}, 'script is found synchronously even when there is a css import');
+
+test(() => {
+ const doc = Document.parseHTMLUnsafe(`<body><noscript><p id="test1">test1<p id="test2">test2</noscript>`);
+ assert_node(doc.body.firstChild.childNodes[0], { type: HTMLParagraphElement, id: 'test1' });
+ assert_node(doc.body.firstChild.childNodes[1], { type: HTMLParagraphElement, id: 'test2' });
+}, 'must be parsed with scripting disabled, so noscript works');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.html
new file mode 100644
index 0000000000..4a9e300afc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>setHTMLUnsafe in HTML</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from innerhtml-04.html -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function testIsChild(p, c) {
+ assert_equals(p.firstChild, c);
+ assert_equals(c.parentNode, p);
+}
+test(function() {
+ var p = document.createElement('p');
+ var b = p.appendChild(document.createElement('b'));
+ var t = b.appendChild(document.createTextNode("foo"));
+ testIsChild(p, b);
+ testIsChild(b, t);
+ assert_equals(t.data, "foo");
+ p.setHTMLUnsafe("");
+ testIsChild(b, t);
+ assert_equals(t.data, "foo");
+}, "setHTMLUnsafe should leave the removed children alone.")
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html
new file mode 100644
index 0000000000..53b855968c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff with base and pushstates itself</title>
+<base href="/fake/base-from-iframe">
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-iframe");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html
new file mode 100644
index 0000000000..6977e93811
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff with base</title>
+<base href="/fake/base-from-iframe">
+
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html
new file mode 100644
index 0000000000..5e1fae0441
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff and pushstates itself</title>
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-iframe");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html
new file mode 100644
index 0000000000..f47d9605fc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff</title>
+<script src="parseHTMLUnsafe-iframe.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js
new file mode 100644
index 0000000000..4a0b56869b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js
@@ -0,0 +1,3 @@
+window.doParse = (html) => {
+ return Document.parseHTMLUnsafe(html);
+};
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js
new file mode 100644
index 0000000000..88344d127c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js
@@ -0,0 +1,36 @@
+const loadPromise = new Promise(resolve => { window.resolveLoadPromise = resolve; });
+
+function assertURL(doc) {
+ assert_equals(doc.URL, "about:blank", "document.URL");
+ assert_equals(doc.documentURI, "about:blank", "document.documentURI");
+ assert_equals(doc.baseURI, "about:blank", "document.baseURI");
+}
+
+const inputs = {
+ valid: "<html></html>",
+ "invalid XML": `<span x:test="testing">1</span>`
+};
+
+for (const [inputName, input] of Object.entries(inputs)) {
+ test(() => {
+ const doc = Document.parseHTMLUnsafe(input);
+
+ assertURL(doc);
+ }, `${inputName}: created normally`);
+
+ promise_test(async () => {
+ await loadPromise;
+
+ const doc = frames[0].Document.parseHTMLUnsafe(input);
+
+ assertURL(doc);
+ }, `${inputName}: created using another iframe's parseHTMLUnsafe from this frame`);
+
+ promise_test(async () => {
+ await loadPromise;
+
+ const doc = frames[0].doParse(input);
+
+ assertURL(doc);
+ }, `${inputName}: created using another iframe's parseHTMLUnsafe from that frame`);
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-CEReactions.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-CEReactions.html
new file mode 100644
index 0000000000..3aa14516f2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-CEReactions.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9957">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+class MyElement extends HTMLElement {
+ constructor() {
+ super();
+ this.numConnectedCallback = 0;
+ this.numDisconnectedCallback = 0;
+ this.attributeChangedCalls = [];
+ }
+
+ connectedCallback() {
+ this.numConnectedCallback++;
+ }
+
+ disconnectedCallback() {
+ this.numDisconnectedCallback++;
+ }
+
+ static observedAttributes = ['foo'];
+ attributeChangedCallback(name, oldValue, newValue) {
+ this.attributeChangedCalls.push({name, oldValue, newValue});
+ }
+}
+customElements.define('my-element', MyElement);
+
+['Element', 'ShadowRoot'].forEach(containerType => {
+ test(() => {
+ let container = null;
+ if (containerType === 'Element') {
+ container = document.createElement('div');
+ document.body.appendChild(container);
+ } else if (containerType === 'ShadowRoot') {
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ container = host.attachShadow({mode: 'closed'});
+ }
+
+ container.setHTMLUnsafe('<my-element>');
+ const myElement1 = container.querySelector('my-element');
+ assert_equals(myElement1.numConnectedCallback, 1,
+ 'myElement1.numConnectedCallback after first setHTMLUnsafe.');
+ assert_equals(myElement1.numDisconnectedCallback, 0,
+ 'myElement1.numDisconnectedCallback after first setHTMLUnsafe.');
+ assert_equals(JSON.stringify(myElement1.attributeChangedCalls),
+ JSON.stringify([]),
+ 'myElement1.attributeChangedCalls after first setHTMLUnsafe.');
+
+ container.setHTMLUnsafe('<my-element foo=bar>');
+ const myElement2 = container.querySelector('my-element');
+ assert_equals(myElement1.numConnectedCallback, 1,
+ 'myElement1.numConnectedCallback after second setHTMLUnsafe.');
+ assert_equals(myElement1.numDisconnectedCallback, 1,
+ 'myElement1.numDisconnectedCallback after second setHTMLUnsafe.');
+ assert_array_equals(myElement1.attributeChangedCalls, [],
+ 'myElement1.attributeChangedCalls after second setHTMLUnsafe.');
+ assert_equals(myElement2.numConnectedCallback, 1,
+ 'myElement2.numConnectedCallback after second setHTMLUnsafe.');
+ assert_equals(myElement2.numDisconnectedCallback, 0,
+ 'myElement2.numDisconnectedCallback after second setHTMLUnsafe.');
+ assert_equals(JSON.stringify(myElement2.attributeChangedCalls),
+ JSON.stringify([{name: 'foo', oldValue: null, newValue: 'bar'}]),
+ 'myElement2.attributeChangedCalls after second setHTMLUnsafe.');
+ }, `${containerType}.setHTMLUnsafe should trigger custom element reactions.`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-xml.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-xml.html
new file mode 100644
index 0000000000..d7211e97f1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-xml.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9538">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+test(() => {
+ const xmlDoc = document.implementation.createDocument(null, 'root', null);
+ assert_equals(xmlDoc.contentType, 'application/xml');
+ // Intentionally unclosed and misnested tags
+ xmlDoc.documentElement.setHTMLUnsafe('<p><foo><b><i>test</b></i>');
+ assert_equals(xmlDoc.documentElement.innerHTML,
+ '<p xmlns="http://www.w3.org/1999/xhtml"><foo><b><i>test</i></b></foo></p>',
+ 'Element.setHTMLUnsafe should use the HTML parser in XML documents.');
+}, 'setHTMLUnsafe should still parse HTML even in XML documents.');
+
+test(() => {
+ const svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'root', null);
+ assert_equals(svgDoc.contentType, 'image/svg+xml');
+ // Intentionally unclosed and misnested tags
+ svgDoc.documentElement.setHTMLUnsafe('<p><foo><b><i>test</b></i>');
+ assert_equals(svgDoc.documentElement.innerHTML,
+ '<p xmlns="http://www.w3.org/1999/xhtml"><foo><b><i>test</i></b></foo></p>',
+ 'Element.setHTMLUnsafe should use the HTML parser in SVG documents.');
+}, 'setHTMLUnsafe should still parse HTML even in SVG documents.');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.html
new file mode 100644
index 0000000000..1c0b115569
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9538">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+for (const containerType of ['Element', 'ShadowRoot']) {
+ const createContainer = () => {
+ if (containerType == 'ShadowRoot') {
+ return document.createElement('div').attachShadow({mode: 'open'});
+ } else if (containerType == 'Element') {
+ return document.createElement('div');
+ }
+ };
+
+ test(() => {
+ const container = createContainer();
+ container.setHTMLUnsafe('<span title=hello>world</span>');
+
+ assert_equals(container.children.length, 1, 'Only one child node should be created.');
+ assert_equals(container.firstChild.tagName, 'SPAN', 'The child element should be a span.');
+ assert_equals(container.firstChild.getAttribute('title'), 'hello',
+ 'The title attribute should be set to hello.');
+ assert_equals(container.firstChild.childNodes.length, 1,
+ 'The span should have one child.');
+ assert_true(container.firstChild.childNodes[0] instanceof Text,
+ 'The spans child should be a text node.');
+ assert_equals(container.firstChild.textContent, 'world',
+ 'The spans textContent should be world.');
+ }, `${containerType}: setHTMLUnsafe with no shadowdom.`);
+
+ test(() => {
+ const container = createContainer();
+ container.setHTMLUnsafe(`<div><template shadowrootmode=open><div>hello</div></template></div>`);
+
+ assert_equals(container.children.length, 1, 'One child should be created in the container.');
+ const shadowRoot = container.firstChild.shadowRoot;
+ assert_true(!!shadowRoot, 'The containers child should have a ShadowRoot.');
+ assert_equals(shadowRoot.children.length, 1, 'One child should be created in the ShadowRoot.');
+ assert_equals(shadowRoot.firstChild.textContent, 'hello',
+ 'The ShadowRoots childs textContent should be hello.');
+ }, `${containerType}: setHTMLUnsafe with shadowdom.`);
+}
+
+test(() => {
+ const template = document.createElement('template');
+ template.setHTMLUnsafe('<div>hello world</div>');
+
+ assert_equals(template.children.length, 0, 'template should not have any child nodes.');
+ assert_equals(template.content.children.length, 1, 'template content should have a child div.');
+ assert_equals(template.content.children[0].textContent, 'hello world', 'text content should be set.');
+}, 'template.setHTMLUnsafe() should modify template content fragment rather than actual children.');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/002.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/002.html
new file mode 100644
index 0000000000..5584bf9afb
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/002.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>document.open during parsing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var log = document.getElementById("log");
+ assert_equals(document.open(), document);
+ assert_equals(document.getElementById("log"), log);
+})
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/004.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/004.html
new file mode 100644
index 0000000000..3fb443a993
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/004.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Reuse of document object after document.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="/common/blank.html"></iframe>
+<script>
+var t = async_test();
+var iframe;
+onload = t.step_func(function() {
+ var iframe = document.getElementsByTagName("iframe")[0];
+ var handle = iframe.contentDocument;
+ iframe.contentDocument.test_state = 1;
+ assert_equals(iframe.contentDocument.open(), handle);
+ assert_equals(iframe.contentDocument.test_state, 1);
+ assert_equals(iframe.contentDocument, handle);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/006.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/006.html
new file mode 100644
index 0000000000..1dcb92615d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/006.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Cancelling error after document.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="/common/blank.html"></iframe>
+<script>
+var t = async_test();
+var iframe;
+onload = t.step_func(function() {
+ var iframe = document.getElementsByTagName("iframe")[0];
+ var img = iframe.contentDocument.createElement("img");
+ img.onerror = t.step_func(function() {assert_unreached()})
+ img.src = "missing";
+ iframe.contentDocument.body.appendChild(img);
+ assert_equals(iframe.contentDocument.open(), iframe.contentDocument);
+ setTimeout(function() {t.done();}, 500);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011-1.html
new file mode 100644
index 0000000000..37973fd52e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011-1.html
@@ -0,0 +1,5 @@
+<script>
+parent.t.step(() => { parent.assert_equals(document.open(), document); });
+setTimeout(parent.t.step_func(function() {parent.t.done()}), 0);
+document.close();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011.html
new file mode 100644
index 0000000000..2acc884c54
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/011.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Timeout after document.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+</script>
+<iframe src="011-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012-1.html
new file mode 100644
index 0000000000..644b30827d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012-1.html
@@ -0,0 +1,7 @@
+<script>
+onload = parent.t.step_func(function() {
+ parent.assert_equals(document.open(), document);
+ setTimeout(parent.t.step_func(function() {parent.t.done()}), 0);
+ document.close();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012.html
new file mode 100644
index 0000000000..518454858d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/012.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Timeout after document.open in load event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+</script>
+<iframe src="012-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013-1.html
new file mode 100644
index 0000000000..ea321238ed
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013-1.html
@@ -0,0 +1,7 @@
+<script>
+addEventListener("DOMContentLoaded", parent.t.step_func(function() {
+ parent.assert_equals(document.open(), document);
+ setTimeout(parent.t.step_func(function() {parent.t.done()}), 0);
+ document.close();
+}), false);
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013.html
new file mode 100644
index 0000000000..5749361aa8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/013.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Timeout after document.open in DOMContentLoaded event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+</script>
+<iframe src="013-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014-1.html
new file mode 100644
index 0000000000..0e97808116
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014-1.html
@@ -0,0 +1,9 @@
+<script>
+onload = parent.t.step_func(function() {
+ setTimeout(parent.t.step_func(function() {
+ parent.assert_equals(document.open(), document);
+ setTimeout(parent.t.step_func(function() {parent.t.done()}), 0);
+ document.close();
+ }), 100)
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014.html
new file mode 100644
index 0000000000..b4e4b17cf4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/014.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Timeout after document.open after document is completely loaded</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+</script>
+<iframe src="014-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015-1.html
new file mode 100644
index 0000000000..c325bd0801
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015-1.html
@@ -0,0 +1,17 @@
+<script>
+onload = function() {
+ window.test_prop = 1;
+ parent.tests[0].step(function() {parent.assert_equals(test_prop, 1)});
+ parent.tests[0].step(function() {parent.assert_equals(document.open(), document)});
+ document.write("<script>test_prop = 2;<\/script>");
+ document.close();
+ parent.tests[0].step(function() {parent.assert_equals(test_prop, 2)});
+ parent.tests[1].step(function() {parent.assert_equals(window.test_prop, 2)});
+ parent.tests[2].step(function() {parent.assert_equals(get_this(), window)});
+ parent.tests_done();
+};
+
+function get_this() {
+ return this;
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015.html
new file mode 100644
index 0000000000..cce9e65d4c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/015.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Window vs global scope after document.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var tests = [async_test("global scope unchanged"),
+ async_test("window object unchanged"),
+ async_test("this is the window object")];
+function tests_done() {
+ tests.forEach(function(t) {t.done()});
+}
+</script>
+<iframe src="015-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016-1.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016-1.html
new file mode 100644
index 0000000000..ceeeb64df6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016-1.html
@@ -0,0 +1,41 @@
+<script>
+window.test_prop = 1;
+</script>
+<script>
+onload = function() {
+ parent.tests[0].step(function() {
+ parent.assert_equals(document.open(), document);
+ });
+ document.write("<script>test_prop = 2; timeout_fired=false;<\/script>");
+ document.close();
+
+ setTimeout(function() {
+ parent.tests[0].step(function() {
+ parent.assert_equals(test_prop, 2, "Global scope from original window timeout");
+ parent.assert_equals(window.test_prop, 2, "Window property from original window timeout")
+ });
+ parent.tests[1].step(function() {
+ var t = get_this();
+ parent.assert_equals(t.test_prop, 2, "Window property from original window timeout");
+ parent.assert_equals(t, window, "Global scope from original window timeout");
+ });
+ }, 0);
+
+ window.setTimeout(function() {
+ parent.tests[2].step(function() {
+ parent.assert_equals(test_prop, 2, "Global scope from original window timeout");
+ parent.assert_equals(window.test_prop, 2, "Window property from original window timeout")
+ });
+ parent.tests[3].step(function() {
+ var t = get_this();
+ parent.assert_equals(t.test_prop, 2, "Window property from original window timeout");
+ parent.assert_equals(t, window, "Global scope from original window timeout");
+ });
+ parent.tests_done();
+ }, 100);
+};
+
+function get_this() {
+ return this;
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016.html
new file mode 100644
index 0000000000..1c70fce591
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/016.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>setTimeout document.open</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var tests = [async_test("Timeout on original window, scope"),
+ async_test("Timeout on original window, this object"),
+ async_test("Timeout on new window, scope"),
+ async_test("Timeout on new window, this object")];
+function tests_done() {
+ tests.forEach(function(t) {t.done()});
+}
+</script>
+<iframe src="016-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-immediate.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-immediate.window.js
new file mode 100644
index 0000000000..8d045b9e0a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-immediate.window.js
@@ -0,0 +1,119 @@
+// The following tests deal with the <meta http-equiv=refresh> pragma and the
+// `Refresh` header. The spec is still hazy on the precise behavior in those
+// cases but we use https://github.com/whatwg/html/issues/4003 as a guideline.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.onabort = t.step_func_done();
+ client.send();
+
+ frame.contentDocument.open();
+ });
+ frame.src = "resources/meta-refresh.py?0";
+}, "document.open() aborts documents that are queued for navigation through <meta> refresh with timeout 0 (XMLHttpRequest)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.unreached_func("Fetch should have been aborted"),
+ t.step_func_done());
+
+ frame.contentDocument.open();
+ });
+ frame.src = "resources/meta-refresh.py?0";
+}, "document.open() aborts documents that are queued for navigation through <meta> refresh with timeout 0 (fetch())");
+
+// We cannot test for img element's error event for this test, as Firefox does
+// not fire the event if the fetch is aborted while Chrome does.
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.unreached_func("Image loading should not have succeeded");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ frame.contentDocument.open();
+ happened = true;
+ });
+ // If 3 seconds have passed and the image has still not loaded, we consider
+ // it aborted. slow-png.py only sleeps for 2 wallclock seconds.
+ t.step_timeout(t.step_func_done(() => {
+ assert_true(happened);
+ }), 3000);
+ });
+ frame.src = "resources/meta-refresh.py?0";
+}, "document.open() aborts documents that are queued for navigation through <meta> refresh with timeout 0 (image loading)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.onabort = t.step_func_done();
+ client.send();
+
+ frame.contentDocument.open();
+ });
+ frame.src = "resources/http-refresh.py?0";
+}, "document.open() aborts documents that are queued for navigation through Refresh header with timeout 0 (XMLHttpRequest)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.unreached_func("Fetch should have been aborted"),
+ t.step_func_done());
+
+ frame.contentDocument.open();
+ });
+ frame.src = "resources/http-refresh.py?0";
+}, "document.open() aborts documents that are queued for navigation through Refresh header with timeout 0 (fetch())");
+
+// We cannot test for img element's error event for this test, as Firefox does
+// not fire the event if the fetch is aborted while Chrome does.
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.unreached_func("Image loading should not have succeeded");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ frame.contentDocument.open();
+ happened = true;
+ });
+ // If 3 seconds have passed and the image has still not loaded, we consider
+ // it aborted. slow-png.py only sleeps for 2 wallclock seconds.
+ t.step_timeout(t.step_func_done(() => {
+ assert_true(happened);
+ }), 3000);
+ });
+ frame.src = "resources/http-refresh.py?0";
+}, "document.open() aborts documents that are queued for navigation through Refresh header with timeout 0 (image loading)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-header.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-header.window.js
new file mode 100644
index 0000000000..8c6c1267c4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-header.window.js
@@ -0,0 +1,69 @@
+// The following tests deal with the <meta http-equiv=refresh> pragma and the
+// `Refresh` header. The spec is still hazy on the precise behavior in those
+// cases but we use https://github.com/whatwg/html/issues/4003 as a guideline.
+//
+// This is separate from abort-refresh-multisecond-meta.window.js to avoid
+// browser interventions that limit the number of connections in a tab.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.onload = t.step_func_done(() => {
+ assert_true(happened);
+ });
+ client.onerror = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.onabort = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.ontimeout = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.send();
+
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "resources/http-refresh.py?1";
+}, "document.open() does NOT abort documents that are queued for navigation through Refresh header with 1-sec timeout (XMLHttpRequest)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.step_func_done(() => {
+ assert_true(happened);
+ }),
+ t.unreached_func("Fetch should have succeeded")
+ );
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "resources/http-refresh.py?1";
+}, "document.open() does NOT abort documents that are queued for navigation through Refresh header with 1-sec timeout (fetch())");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.step_func_done(() => {
+ assert_true(happened);
+ });
+ img.onerror = t.unreached_func("Image loading should not have errored");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ frame.contentDocument.open();
+ happened = true;
+ });
+ });
+ frame.src = "resources/http-refresh.py?4";
+}, "document.open() does NOT abort documents that are queued for navigation through Refresh header with 4-sec timeout (image loading)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-meta.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-meta.window.js
new file mode 100644
index 0000000000..2895f959e5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-refresh-multisecond-meta.window.js
@@ -0,0 +1,69 @@
+// The following tests deal with the <meta http-equiv=refresh> pragma and the
+// `Refresh` header. The spec is still hazy on the precise behavior in those
+// cases but we use https://github.com/whatwg/html/issues/4003 as a guideline.
+//
+// This is separate from abort-refresh-multisecond-header.window.js to avoid
+// browser interventions that limit the number of connections in a tab.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.onload = t.step_func_done(() => {
+ assert_true(happened);
+ });
+ client.onerror = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.onabort = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.ontimeout = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.send();
+
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "resources/meta-refresh.py?1";
+}, "document.open() does NOT abort documents that are queued for navigation through <meta> refresh with 1-sec timeout (XMLHttpRequest)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.step_func_done(() => {
+ assert_true(happened);
+ }),
+ t.unreached_func("Fetch should have succeeded")
+ );
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "resources/meta-refresh.py?1";
+}, "document.open() does NOT abort documents that are queued for navigation through <meta> refresh with 1-sec timeout (fetch())");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.step_func_done(() => {
+ assert_true(happened);
+ });
+ img.onerror = t.unreached_func("Image loading should not have errored");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ frame.contentDocument.open();
+ happened = true;
+ });
+ });
+ frame.src = "resources/meta-refresh.py?4";
+}, "document.open() does NOT abort documents that are queued for navigation through <meta> refresh with 4-sec timeout (image loading)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-while-navigating.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-while-navigating.window.js
new file mode 100644
index 0000000000..e3efeffb8b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-while-navigating.window.js
@@ -0,0 +1,179 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ // The abort event handler is called synchronously in Chrome but
+ // asynchronously in Firefox. See https://crbug.com/879620.
+ client.onabort = t.step_func_done();
+ client.send();
+ frame.contentWindow.location.href = new URL("resources/dummy.html", document.URL);
+ frame.contentDocument.open();
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() aborts documents that are navigating through Location (XMLHttpRequest)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.unreached_func("Fetch should have been aborted"),
+ t.step_func_done(() => {
+ assert_true(happened);
+ }));
+ frame.contentWindow.location.href = new URL("resources/dummy.html", document.URL);
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() aborts documents that are navigating through Location (fetch())");
+
+// We cannot test for img element's error event for this test, as Firefox does
+// not fire the event if the fetch is aborted while Chrome does.
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.unreached_func("Image loading should not have succeeded");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ frame.contentWindow.location.href = new URL("resources/dummy.html", document.URL);
+ frame.contentDocument.open();
+ happened = true;
+ });
+ // If 3 seconds have passed and the image has still not loaded, we consider
+ // it aborted. slow-png.py only sleeps for 2 wallclock seconds.
+ t.step_timeout(t.step_func_done(() => {
+ assert_true(happened);
+ }), 3000);
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() aborts documents that are navigating through Location (image loading)");
+
+async_test(t => {
+ const div = document.body.appendChild(document.createElement("div"));
+ t.add_cleanup(() => div.remove());
+ div.innerHTML = "<iframe src='/common/slow.py'></iframe>";
+ const frame = div.childNodes[0];
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.onabort = t.step_func_done();
+ client.send();
+ frame.contentDocument.open();
+}, "document.open() aborts documents that are navigating through iframe loading (XMLHttpRequest)");
+
+async_test(t => {
+ const div = document.body.appendChild(document.createElement("div"));
+ t.add_cleanup(() => div.remove());
+ div.innerHTML = "<iframe src='/common/slow.py'></iframe>";
+ const frame = div.childNodes[0];
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.unreached_func("Fetch should have been aborted"),
+ t.step_func_done());
+ frame.contentDocument.open();
+}, "document.open() aborts documents that are navigating through iframe loading (fetch())");
+
+// We cannot test for img element's error event for this test, as Firefox does
+// not fire the event if the fetch is aborted while Chrome does.
+//
+// We use /common/slow.py here as the source of the iframe, to prevent the
+// situation where when document.open() is called the initial about:blank
+// document has already become inactive.
+async_test(t => {
+ const div = document.body.appendChild(document.createElement("div"));
+ t.add_cleanup(() => div.remove());
+ div.innerHTML = "<iframe src='/common/slow.py'></iframe>";
+ const frame = div.childNodes[0];
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.unreached_func("Image loading should not have succeeded");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ frame.contentDocument.open();
+ happened = true;
+ });
+ // If 3 seconds have passed and the image has still not loaded, we consider
+ // it aborted. slow-png.py only sleeps for 2 wallclock seconds.
+ t.step_timeout(t.step_func_done(() => {
+ assert_true(happened);
+ }), 3000);
+}, "document.open() aborts documents that are navigating through iframe loading (image loading)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ const link = frame.contentDocument.body.appendChild(frame.contentDocument.createElement("a"));
+ link.href = new URL("resources/dummy.html", document.URL);
+
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.onabort = t.step_func_done();
+ client.send();
+
+ link.click();
+ frame.contentDocument.open();
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() aborts documents that are queued for navigation through .click() (XMLHttpRequest)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ const link = frame.contentDocument.body.appendChild(frame.contentDocument.createElement("a"));
+ link.href = new URL("resources/dummy.html", document.URL);
+
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.unreached_func("Fetch should have been aborted"),
+ t.step_func_done());
+
+ link.click();
+ frame.contentDocument.open();
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() aborts documents that are queued for navigation through .click() (fetch())");
+
+// We cannot test for img element's error event for this test, as Firefox does
+// not fire the event if the fetch is aborted while Chrome does.
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ const link = frame.contentDocument.body.appendChild(frame.contentDocument.createElement("a"));
+ link.href = new URL("resources/dummy.html", document.URL);
+
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.unreached_func("Image loading should not have succeeded");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ link.click();
+ frame.contentDocument.open();
+ happened = true;
+ });
+ // If 3 seconds have passed and the image has still not loaded, we consider
+ // it aborted. slow-png.py only sleeps for 2 wallclock seconds.
+ t.step_timeout(t.step_func_done(() => {
+ assert_true(happened);
+ }), 3000);
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() aborts documents that are queued for navigation through .click() (image loading)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort.sub.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort.sub.window.js
new file mode 100644
index 0000000000..b2f05cf056
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort.sub.window.js
@@ -0,0 +1,104 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ const client = new frame.contentWindow.XMLHttpRequest();
+ client.open("GET", "/common/blank.html");
+ client.onload = t.step_func_done(e => {
+ assert_true(happened);
+ });
+ client.onerror = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.onabort = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.ontimeout = t.unreached_func("XMLHttpRequest should have succeeded");
+ client.send();
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() does not abort documents that are not navigating (XMLHttpRequest)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ frame.contentWindow.fetch("/common/blank.html").then(
+ t.step_func_done(() => {
+ assert_true(happened);
+ }),
+ t.unreached_func("Fetch should have succeeded")
+ );
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() does not abort documents that are not navigating (fetch())");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ const img = frame.contentDocument.createElement("img");
+ img.src = new URL("resources/slow-png.py", document.URL);
+ img.onload = t.step_func_done(() => {
+ assert_true(happened);
+ });
+ img.onerror = t.unreached_func("Image loading should not have errored");
+ // The image fetch starts in a microtask, so let's be sure to test after
+ // the fetch has started.
+ t.step_timeout(() => {
+ frame.contentDocument.open();
+ happened = true;
+ });
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() does not abort documents that are not navigating (image loading)");
+
+async_test(t => {
+ const __SERVER__NAME = "{{host}}";
+ const __PORT = {{ports[ws][0]}};
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ const ws = new frame.contentWindow.WebSocket(`ws://${__SERVER__NAME}:${__PORT}/echo`);
+ ws.onopen = t.step_func_done(() => {
+ assert_true(happened);
+ });
+ ws.onclose = t.unreached_func("WebSocket fetch should have succeeded");
+ ws.onerror = t.unreached_func("WebSocket should have no error");
+ frame.contentDocument.open();
+ happened = true;
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() does not abort documents that are not navigating (establish a WebSocket connection)");
+
+// An already established WebSocket connection shouldn't be terminated during
+// an "abort a document" anyway. Test just for completeness.
+async_test(t => {
+ const __SERVER__NAME = "{{host}}";
+ const __PORT = {{ports[ws][0]}};
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ frame.onload = null;
+ let happened = false;
+ const ws = new frame.contentWindow.WebSocket(`ws://${__SERVER__NAME}:${__PORT}/echo`);
+ ws.onopen = t.step_func(() => {
+ t.step_timeout(t.step_func_done(() => {
+ assert_true(happened);
+ }), 100);
+ frame.contentDocument.open();
+ happened = true;
+ });
+ ws.onclose = t.unreached_func("WebSocket should not be closed");
+ ws.onerror = t.unreached_func("WebSocket should have no error");
+ });
+ frame.src = "/common/blank.html";
+}, "document.open() does not abort documents that are not navigating (already established WebSocket connection)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js
new file mode 100644
index 0000000000..ba7278ef18
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js
@@ -0,0 +1,31 @@
+// document.open() bails out early if there is an active parser with non-zero
+// script nesting level or if a load was aborted while there was an active
+// parser. window.stop() aborts the current parser, so once it has been called
+// while a parser is active, document.open() will no longer do anything to that
+// document,
+
+window.handlers = {};
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.src = "resources/aborted-parser-frame.html";
+ window.handlers.afterOpen = t.step_func_done(() => {
+ const openCalled = frame.contentDocument.childNodes.length === 0;
+ assert_false(openCalled, "child document should not be empty");
+ assert_equals(frame.contentDocument.querySelector("p").textContent,
+ "Text", "Should still have our paragraph");
+ });
+}, "document.open() after parser is aborted");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.src = "resources/aborted-parser-async-frame.html";
+ window.handlers.afterOpenAsync = t.step_func_done(() => {
+ const openCalled = frame.contentDocument.childNodes.length === 0;
+ assert_false(openCalled, "child document should not be empty");
+ assert_equals(frame.contentDocument.querySelector("p").textContent,
+ "Text", "Should still have our paragraph");
+ });
+}, "async document.open() after parser is aborted");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/active.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/active.window.js
new file mode 100644
index 0000000000..f96710999a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/active.window.js
@@ -0,0 +1,98 @@
+function assertOpenIsEffective(doc, initialNodeCount) {
+ assert_equals(doc.childNodes.length, initialNodeCount);
+
+ // Test direct document.open() call.
+ assert_equals(doc.open(), doc);
+ assert_equals(doc.childNodes.length, 0, "after open: no nodes in document");
+ doc.write("<!DOCTYPE html>");
+ assert_equals(doc.childNodes.length, 1, "after write: doctype node in document");
+ doc.close();
+ assert_equals(doc.childNodes.length, 2, "after parser close: doctype node and an html element in document");
+
+ // Test implicit document.open() call through write(). Since we called
+ // doc.close() above, which sets the insertion point of the parser to
+ // undefined, document.write() will run the document open steps.
+ doc.write();
+ assert_equals(doc.childNodes.length, 0, "after implicit open: no nodes in document");
+ doc.write("<!DOCTYPE html>");
+ assert_equals(doc.childNodes.length, 1, "after write: doctype node in document");
+ doc.close();
+ assert_equals(doc.childNodes.length, 2, "after parser close: doctype node and an html element in document");
+}
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ assertOpenIsEffective(frame.contentDocument, 1);
+}, "document.open() removes the document's children (fully active document)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ const childFrame = frame.contentDocument.querySelector("iframe");
+ const childDoc = childFrame.contentDocument;
+ const childWin = childFrame.contentWindow;
+
+ // Right now childDoc is still fully active.
+
+ frame.onload = t.step_func_done(() => {
+ // Now childDoc is still active but no longer fully active.
+ assertOpenIsEffective(childDoc, 1);
+ });
+ frame.src = "/common/blank.html";
+ });
+ frame.src = "resources/page-with-frame.html";
+}, "document.open() removes the document's children (active but not fully active document)");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ const doc = frame.contentDocument;
+
+ // Right now the frame is connected and it has an active document.
+ frame.remove();
+
+ // Now the frame is no longer connected. Its document is no longer active.
+ assertOpenIsEffective(doc, 1);
+}, "document.open() removes the document's children (non-active document with an associated Window object; frame is removed)");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.src = "resources/dummy.html";
+
+ frame.onload = t.step_func(() => {
+ const firstDocument = frame.contentDocument;
+ // Right now the frame is connected and it has an active document.
+
+ frame.onload = t.step_func_done(() => {
+ // Now even though the frame is still connected, its document is no
+ // longer active.
+ assert_not_equals(frame.contentDocument, firstDocument);
+ assertOpenIsEffective(firstDocument, 2);
+ });
+
+ frame.src = "/common/blank.html";
+ });
+}, "document.open() removes the document's children (non-active document with an associated Window object; navigated away)");
+
+test(t => {
+ const doc = document.implementation.createHTMLDocument();
+ assertOpenIsEffective(doc, 2);
+}, "document.open() removes the document's children (non-active document without an associated Window object; createHTMLDocument)");
+
+test(t => {
+ const doc = new DOMParser().parseFromString("", "text/html");
+ assertOpenIsEffective(doc, 1);
+}, "document.open() removes the document's children (non-active document without an associated Window object; DOMParser)");
+
+async_test(t => {
+ const xhr = new XMLHttpRequest();
+ xhr.onload = t.step_func_done(() => {
+ assert_equals(xhr.status, 200);
+ assertOpenIsEffective(xhr.responseXML, 2);
+ });
+ xhr.responseType = "document";
+ xhr.open("GET", "resources/dummy.html");
+ xhr.send();
+}, "document.open() removes the document's children (non-active document without an associated Window object; XMLHttpRequest)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-origin.sub.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-origin.sub.window.js
new file mode 100644
index 0000000000..b20c3e3f31
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-origin.sub.window.js
@@ -0,0 +1,117 @@
+document.domain = "{{host}}";
+
+// In many cases in this test, we want to delay execution of a piece of code so
+// that the entry settings object would be the top-level page. A microtask is
+// perfect for this purpose as it is executed in the "clean up after running
+// script" algorithm, which is generally called right after the callback.
+function setEntryToTopLevel(cb) {
+ Promise.resolve().then(cb);
+}
+
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { iframe.remove(); });
+ iframe.onload = t.step_func_done(() => {
+ // Since this is called as an event handler on an element of this window,
+ // the entry settings object is that of this browsing context.
+ assert_throws_dom(
+ "InvalidStateError",
+ iframe.contentWindow.DOMException,
+ () => {
+ iframe.contentDocument.open();
+ },
+ "opening an XML document should throw an InvalidStateError"
+ );
+ });
+ const frameURL = new URL("resources/bailout-order-xml-with-domain-frame.sub.xhtml", document.URL);
+ frameURL.port = "{{ports[http][1]}}";
+ iframe.src = frameURL.href;
+}, "document.open should throw an InvalidStateError with XML document even if it is cross-origin");
+
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { iframe.remove(); });
+ window.onCustomElementReady = t.step_func(() => {
+ window.onCustomElementReady = t.unreached_func("onCustomElementReady called again");
+ // Here, the entry settings object is still the iframe's, as the function
+ // is called from a custom element constructor in the iframe document.
+ // Delay execution in such a way that makes the entry settings object the
+ // top-level page's, but without delaying too much that the
+ // throw-on-dynamic-markup-insertion counter gets decremented (which is
+ // what this test tries to pit against the cross-origin document check).
+ //
+ // "Clean up after running script" is executed through the "construct" Web
+ // IDL algorithm in "create an element", called by "create an element for a
+ // token" in the parser.
+ setEntryToTopLevel(t.step_func_done(() => {
+ assert_throws_dom(
+ "InvalidStateError",
+ iframe.contentWindow.DOMException,
+ () => {
+ iframe.contentDocument.open();
+ },
+ "opening a document when the throw-on-dynamic-markup-insertion counter is incremented should throw an InvalidStateError"
+ );
+ }));
+ });
+ const frameURL = new URL("resources/bailout-order-custom-element-with-domain-frame.sub.html", document.URL);
+ frameURL.port = "{{ports[http][1]}}";
+ iframe.src = frameURL.href;
+}, "document.open should throw an InvalidStateError when the throw-on-dynamic-markup-insertion counter is incremented even if the document is cross-origin");
+
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { iframe.remove(); });
+ self.testSynchronousScript = t.step_func(() => {
+ // Here, the entry settings object is still the iframe's, as the function
+ // is synchronously called from a <script> element in the iframe's
+ // document.
+ //
+ // "Clean up after running script" is executed when the </script> tag is
+ // seen by the HTML parser.
+ setEntryToTopLevel(t.step_func_done(() => {
+ assert_throws_dom(
+ "SecurityError",
+ iframe.contentWindow.DOMException,
+ () => {
+ iframe.contentDocument.open();
+ },
+ "opening a same origin-domain (but not same origin) document should throw a SecurityError"
+ );
+ }));
+ });
+ const frameURL = new URL("resources/bailout-order-synchronous-script-with-domain-frame.sub.html", document.URL);
+ frameURL.port = "{{ports[http][1]}}";
+ iframe.src = frameURL.href;
+}, "document.open should throw a SecurityError with cross-origin document even when there is an active parser executing script");
+
+for (const ev of ["beforeunload", "pagehide", "unload"]) {
+ async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { iframe.remove(); });
+ iframe.addEventListener("load", t.step_func(() => {
+ iframe.contentWindow.addEventListener(ev, t.step_func(() => {
+ // Here, the entry settings object should be the top-level page's, as
+ // the callback context of this event listener is the incumbent
+ // settings object, which is the this page. However, due to a Chrome
+ // bug (https://crbug.com/606900), the entry settings object may be
+ // mis-set to the iframe's.
+ //
+ // "Clean up after running script" is called in the task that
+ // navigates.
+ setEntryToTopLevel(t.step_func_done(() => {
+ assert_throws_dom(
+ "SecurityError",
+ iframe.contentWindow.DOMException,
+ () => {
+ iframe.contentDocument.open();
+ },
+ "opening a same origin-domain (but not same origin) document should throw a SecurityError"
+ );
+ }));
+ }));
+ iframe.src = "about:blank";
+ }), { once: true });
+ iframe.src = "http://{{host}}:{{ports[http][1]}}/common/domain-setter.sub.html";
+ }, `document.open should throw a SecurityError with cross-origin document even when the ignore-opens-during-unload counter is greater than 0 (during ${ev} event)`);
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-xml.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-xml.window.js
new file mode 100644
index 0000000000..45a67f925b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-exception-vs-return-xml.window.js
@@ -0,0 +1,26 @@
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { iframe.remove(); });
+ self.testSynchronousScript = t.step_func_done(() => {
+ assert_throws_dom("InvalidStateError", iframe.contentWindow.DOMException, () => {
+ iframe.contentDocument.open();
+ }, "opening an XML document should throw");
+ });
+ iframe.src = "resources/bailout-order-xml-with-synchronous-script-frame.xhtml";
+}, "document.open should throw an InvalidStateError with XML document even when there is an active parser executing script");
+
+for (const ev of ["beforeunload", "pagehide", "unload"]) {
+ async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { iframe.remove(); });
+ iframe.addEventListener("load", t.step_func(() => {
+ iframe.contentWindow.addEventListener(ev, t.step_func_done(() => {
+ assert_throws_dom("InvalidStateError", iframe.contentWindow.DOMException, () => {
+ iframe.contentDocument.open();
+ }, "opening an XML document should throw");
+ }));
+ iframe.src = "about:blank";
+ }), { once: true });
+ iframe.src = "/common/dummy.xhtml";
+ }, `document.open should throw an InvalidStateError with XML document even when the ignore-opens-during-unload counter is greater than 0 (during ${ev} event)`);
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window.js
new file mode 100644
index 0000000000..98ffba20a1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window.js
@@ -0,0 +1,18 @@
+// META: script=resources/document-open-side-effects.js
+
+for (const ev of ["unload", "beforeunload", "pagehide"]) {
+ async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "/common/blank.html";
+ iframe.onload = t.step_func(() => {
+ iframe.contentWindow.addEventListener(ev, t.step_func_done(() => {
+ const origURL = iframe.contentDocument.URL;
+ assertDocumentIsReadyForSideEffectsTest(iframe.contentDocument, `ignore-opens-during-unload counter is greater than 0 during ${ev} event`);
+ assert_equals(iframe.contentDocument.open(), iframe.contentDocument);
+ assertOpenHasNoSideEffects(iframe.contentDocument, origURL, `ignore-opens-during-unload counter is greater than 0 during ${ev} event`);
+ }));
+ iframe.src = "about:blank";
+ });
+ }, `document.open bailout should not have any side effects (ignore-opens-during-unload is greater than 0 during ${ev} event)`);
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-same-origin-domain.sub.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-same-origin-domain.sub.window.js
new file mode 100644
index 0000000000..f5edd7aed9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-same-origin-domain.sub.window.js
@@ -0,0 +1,14 @@
+// META: script=/html/resources/common.js
+// META: script=resources/document-open-side-effects.js
+
+document.domain = "{{host}}";
+
+testInIFrame("http://{{host}}:{{ports[http][1]}}/common/domain-setter.sub.html", (ctx) => {
+ const iframe = ctx.iframes[0];
+ const origURL = iframe.contentDocument.URL;
+ assertDocumentIsReadyForSideEffectsTest(iframe.contentDocument, "same origin-domain (but not same origin) document");
+ assert_throws_dom("SecurityError", iframe.contentWindow.DOMException, () => {
+ ctx.iframes[0].contentDocument.open();
+ }, "document.open() should throw a SecurityError on a same origin-domain (but not same origin) document");
+ assertOpenHasNoSideEffects(iframe.contentDocument, origURL, "same origin-domain (but not same origin) document");
+}, "document.open bailout should not have any side effects (same origin-domain (but not same origin) document)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-synchronous-script.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-synchronous-script.window.js
new file mode 100644
index 0000000000..fb26c70a9c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-synchronous-script.window.js
@@ -0,0 +1,19 @@
+// META: script=resources/document-open-side-effects.js
+
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ self.testSynchronousScript = t.step_func(() => {
+ // Here, the entry settings object is still the iframe's. Delay it in such
+ // a way that makes the entry settings object the top-level page's, but
+ // without delaying too much that the parser becomes inactive. A microtask
+ // is perfect as it's executed in "clean up after running script".
+ Promise.resolve().then(t.step_func_done(() => {
+ const origURL = iframe.contentDocument.URL;
+ assertDocumentIsReadyForSideEffectsTest(iframe.contentDocument, "active parser whose script nesting level is greater than 0");
+ assert_equals(iframe.contentDocument.open(), iframe.contentDocument);
+ assertOpenHasNoSideEffects(iframe.contentDocument, origURL, "active parser whose script nesting level is greater than 0");
+ }));
+ });
+ iframe.src = "resources/bailout-order-synchronous-script-frame.html";
+}, "document.open bailout should not have any side effects (active parser whose script nesting level is greater than 0)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-xml.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-xml.window.js
new file mode 100644
index 0000000000..bbfc015c68
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-xml.window.js
@@ -0,0 +1,20 @@
+// META: script=resources/document-open-side-effects.js
+
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "/common/dummy.xhtml";
+ iframe.onload = t.step_func_done(() => {
+ const origURL = iframe.contentDocument.URL;
+ assertDocumentIsReadyForSideEffectsTest(iframe.contentDocument, "XML document");
+ assert_throws_dom(
+ "InvalidStateError",
+ iframe.contentWindow.DOMException,
+ () => {
+ iframe.contentDocument.open();
+ },
+ "document.open() should throw on XML documents"
+ );
+ assertOpenHasNoSideEffects(iframe.contentDocument, origURL, "XML document");
+ });
+}, "document.open bailout should not have any side effects (XML document)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/beforeunload.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/beforeunload.window.js
new file mode 100644
index 0000000000..1e2f891c17
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/beforeunload.window.js
@@ -0,0 +1,18 @@
+// In an earlier version of the HTML Standard, document open steps had "prompt
+// to unload document" as a step. Test that this no longer happens.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func(() => {
+ frame.contentWindow.onbeforeunload = t.unreached_func("beforeunload should not be fired");
+ frame.contentDocument.open();
+ t.step_timeout(t.step_func_done(() => {
+ // If the beforeunload event has still not fired by this point, we
+ // consider the test a success. `frame.remove()` above will allow the
+ // `load` event to be fired on the top-level Window, thus unblocking
+ // testharness.
+ }), 500);
+ });
+}, "document.open() should not fire a beforeunload event");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/crbug-583445-regression.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/crbug-583445-regression.window.js
new file mode 100644
index 0000000000..3809c2e081
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/crbug-583445-regression.window.js
@@ -0,0 +1,127 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+//
+// This is a regression test for crbug.com/583445. It checks an obscure bug in
+// Chromium's handling of `document.open()` whereby the URL change would affect
+// the document's origin after a javascript navigation.
+//
+// See also dcheng@'s comments on the original code review in which he
+// introduced the precursor to this test:
+// https://codereview.chromium.org/1675473002.
+
+function nextMessage() {
+ return new Promise((resolve) => {
+ window.addEventListener("message", (e) => { resolve(e.data); }, {
+ once: true
+ });
+ });
+}
+
+promise_test(async (t) => {
+ // Embed a cross-origin frame A and set up remote code execution.
+ const iframeA = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { iframeA.remove(); });
+
+ const uuidA = token();
+ iframeA.src = remoteExecutorUrl(uuidA, { host: get_host_info().REMOTE_HOST });
+ const ctxA = new RemoteContext(uuidA);
+
+ // Frame A embeds a cross-origin frame B, which is same-origin with the
+ // top-level frame. Frame B is the center of this test: it is where we will
+ // verify that a bug does not grant it UXSS in frame A.
+ //
+ // Though we could reach into `iframeA.frames[0]` to get a proxy to frame B
+ // and use `setTimeout()` like below to execute code inside it, we set up
+ // remote code execution using `dispatcher.js` for better ergonomics.
+ const uuidB = token();
+ await ctxA.execute_script((url) => {
+ const iframeB = document.createElement("iframe");
+ iframeB.src = url;
+ document.body.appendChild(iframeB);
+ }, [remoteExecutorUrl(uuidB).href]);
+
+ // Start listening for a message, which will come as a result of executing
+ // the code below in frame B.
+ const message = nextMessage();
+
+ const ctxB = new RemoteContext(uuidB);
+ await ctxB.execute_script(() => {
+ // Frame B embeds an `about:blank` frame C.
+ const iframeC = document.body.appendChild(document.createElement("iframe"));
+
+ // We wish to execute code inside frame C, but it is important to this test
+ // that its URL remain `about:blank`, so we cannot use `dispatcher.js`.
+ // Instead we rely on `setTimeout()`.
+ //
+ // We use `setTimeout(string, ...)` instead of `setTimeout(function, ...)`
+ // as the given script executes against the target window's global object
+ // and does not capture any local variables.
+ //
+ // In order to have nice syntax highlighting and avoid quote-escaping hell,
+ // we use a trick employed by `dispatcher.js`. We rely on the fact that
+ // functions in JS have a stringifier that returns their source code. Thus
+ // `"(" + func + ")()"` is a string that executes `func()` when evaluated.
+ iframeC.contentWindow.setTimeout("(" + (() => {
+ // This executes in frame C.
+
+ // Frame C calls `document.open()` on its parent, which results in B's
+ // URL being set to `about:blank` (C's URL).
+ //
+ // However, just before `document.open()` is called, B schedules a
+ // self-navigation to a `javascript:` URL. This will occur after
+ // `document.open()`, so the document will navigate from `about:blank` to
+ // the new URL.
+ //
+ // This should not result in B's origin changing, so B should remain
+ // same-origin with the top-level frame.
+ //
+ // Due to crbug.com/583445, this used to behave wrongly in Chromium. The
+ // navigation code incorrectly assumed that B's origin should be inherited
+ // from its parent A because B's URL was `about:blank`.
+ //
+ // It is important to schedule this from within the child, as this
+ // guarantees that `document.open()` will be called before the navigation.
+ // A previous version of this test scheduled this from within frame B
+ // right after scheduling the call to `document.open()`, but that ran the
+ // risk of races depending on which timeout fired first.
+ parent.window.setTimeout("(" + (() => {
+ // This executes in frame B.
+
+ location = "javascript:(" + (() => {
+ /* This also executes in frame B.
+ *
+ * Note that because this whole function gets stuffed in a JS URL,
+ * single-line comments do not work, as they affect the following
+ * lines. */
+
+ let error;
+ try {
+ /* This will fail with a `SecurityError` if frame B is no longer
+ * same-origin with the top-level frame. */
+ top.window.testSameOrigin = true;
+ } catch (e) {
+ error = e;
+ }
+
+ top.postMessage({
+ error: error?.toString(),
+ }, "*");
+
+ }) + ")()";
+
+ }) + ")()", 0);
+
+ // This executes in frame C.
+ parent.document.open();
+
+ }) + ")()", 0);
+ });
+
+ // Await the message from frame B after its navigation.
+ const { error } = await message;
+ assert_equals(error, undefined, "error accessing top frame from frame B");
+ assert_true(window.testSameOrigin, "top frame testSameOrigin is mutated");
+
+}, "Regression test for crbug.com/583445");
+
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/custom-element.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/custom-element.window.js
new file mode 100644
index 0000000000..1ad06b3d37
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/custom-element.window.js
@@ -0,0 +1,39 @@
+// The document open steps have:
+//
+// 2. If document's throw-on-dynamic-markup-insertion counter is greater than
+// 0, then throw an "InvalidStateError" DOMException.
+//
+// The throw-on-dynamic-markup-insertion counter is only incremented when the
+// parser creates a custom element, not when createElement is called. Test for
+// this.
+//
+// See: https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps
+
+const noError = Symbol("no error");
+let err = noError;
+
+class CustomElement extends HTMLElement {
+ constructor() {
+ super();
+ try {
+ assert_equals(document.open(), document);
+ } catch (e) {
+ err = e;
+ }
+ }
+}
+customElements.define("custom-element", CustomElement);
+
+test(t => {
+ err = noError;
+ document.createElement("custom-element");
+ assert_equals(err, noError);
+}, "document.open() works in custom element constructor for createElement()");
+
+test(t => {
+ err = noError;
+ document.write("<custom-element></custom-element>");
+ assert_throws_dom("InvalidStateError", () => {
+ throw err;
+ });
+}, "document.open() is forbidden in custom element constructor when creating element from parser");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document-open-cancels-javascript-url-navigation.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document-open-cancels-javascript-url-navigation.html
new file mode 100644
index 0000000000..5596382f22
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document-open-cancels-javascript-url-navigation.html
@@ -0,0 +1,17 @@
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = t.step_func_done(() => assert_equals(i.contentDocument.body.innerText, "PASS"));
+
+ var i = document.createElement('iframe');
+ i.id ='i';
+ i.src = "javascript:'FAIL'";
+ document.body.appendChild(i);
+ i.contentDocument.open();
+ i.contentDocument.write("PASS")
+ i.contentDocument.close();
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-01.xhtml b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-01.xhtml
new file mode 100644
index 0000000000..c02b3e4db5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-01.xhtml
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>document.open in XHTML</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#opening-the-input-stream"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ document.open();
+ }, "document.open in XHTML should throw an INVALID_STATE_ERR ");
+}, "document.open in XHTML");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-02.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-02.html
new file mode 100644
index 0000000000..c7e67a0cf7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-02.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>document.open with three arguments</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-open">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function open() {
+ assert_unreached("The call should be redirected to the real window.open")
+}
+test(function(t) {
+ var w;
+ t.add_cleanup(function() {try {w.close()} catch(e) {}});
+ w = document.open("/resources/testharness.js", "", "");
+ assert_true(w instanceof w.Window, "Expected a window");
+}, "document.open should redirect to window.open when called with three arguments");
+
+test(function() {
+ var parser = new DOMParser();
+ var doc = parser.parseFromString("", "text/html");
+ assert_equals(doc.defaultView, null);
+ assert_throws_dom("INVALID_ACCESS_ERR", function() {
+ doc.open("/resources/testharness.js", "", "");
+ });
+}, "document.open should throw when it has no window and is called with three arguments");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03-frame.html
new file mode 100644
index 0000000000..a4b370cea4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03-frame.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script>
+onload = function() {
+ document.open();
+ document.close();
+ parent.report(window.setTimeout === setTimeout, true, "setTimeout");
+ parent.report(window === this, true, "this");
+ parent.done();
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html
new file mode 100644
index 0000000000..e446d70219
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>document.open and no singleton replacement</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-open">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test();
+function report(actual, expected, message) {
+ t.step(function() {
+ assert_equals(actual, expected, message);
+ });
+}
+function done() {
+ t.done();
+}
+</script>
+<iframe src=document.open-03-frame.html></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/encoding.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/encoding.window.js
new file mode 100644
index 0000000000..f0d133a532
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/encoding.window.js
@@ -0,0 +1,12 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ frame.src = "resources/encoding-frame.html";
+ frame.onload = t.step_func_done(t => {
+ // Using toLowerCase() to avoid an Edge bug
+ assert_equals(frame.contentDocument.characterSet.toLowerCase(), "shift_jis", "precondition");
+ assert_equals(frame.contentDocument.open(), frame.contentDocument);
+ assert_equals(frame.contentDocument.characterSet.toLowerCase(), "shift_jis", "actual test");
+ frame.contentDocument.close();
+ assert_equals(frame.contentDocument.characterSet.toLowerCase(), "shift_jis", "might as well");
+ });
+}, "doucment.open() and the document's encoding");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/event-listeners.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/event-listeners.window.js
new file mode 100644
index 0000000000..df07124d81
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/event-listeners.window.js
@@ -0,0 +1,308 @@
+// Many of the active-related test cases in this file came from
+// active.window.js. However, we cannot test the "navigated away" non-active
+// case right now due to https://github.com/whatwg/html/issues/3997.
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ body = frame.contentDocument.body;
+ t.add_cleanup(() => frame.remove());
+ const div = body.appendChild(frame.contentDocument.createElement("div"));
+ div.addEventListener("click", t.unreached_func("element event listener not removed"));
+ frame.contentDocument.open();
+ div.click();
+ frame.contentDocument.close();
+}, "Standard event listeners are to be removed");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ body = frame.contentDocument.body;
+ t.add_cleanup(() => frame.remove());
+ frame.contentDocument.addEventListener("x", t.unreached_func("document event listener not removed"));
+ body.addEventListener("x", t.unreached_func("body event listener not removed"));
+ frame.contentDocument.open();
+ frame.contentDocument.dispatchEvent(new Event("x"));
+ body.dispatchEvent(new Event("x"));
+ frame.contentDocument.close();
+}, "Custom event listeners are to be removed");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ body = frame.contentDocument.body;
+ t.add_cleanup(() => frame.remove());
+ // Focus on the current window so that the frame's window is blurred.
+ window.focus();
+ assert_false(frame.contentDocument.hasFocus());
+ frame.contentWindow.addEventListener("focus", t.unreached_func("window event listener not removed"));
+ body.onfocus = t.unreached_func("body event listener not removed");
+ frame.contentDocument.open();
+ assert_equals(body.onfocus, null);
+ frame.contentWindow.focus();
+ frame.contentDocument.close();
+}, "Standard event listeners are to be removed from Window");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ const childFrame = frame.contentDocument.querySelector("iframe");
+ const childWin = childFrame.contentWindow;
+ const childDoc = childFrame.contentDocument;
+ const childBody = childDoc.body;
+
+ // Right now childDoc is still fully active.
+
+ frame.onload = t.step_func_done(() => {
+ // Focus on the current window so that the frame's window is blurred.
+ window.focus();
+ // Now childDoc is still active but no longer fully active.
+ childWin.addEventListener("focus", t.unreached_func("window event listener not removed"));
+ childBody.onfocus = t.unreached_func("body event listener not removed");
+
+ childDoc.open();
+ assert_equals(childBody.onfocus, null);
+
+ // Now try to fire the focus event two different ways.
+ childWin.focus();
+ const focusEvent = new FocusEvent("focus");
+ childWin.dispatchEvent(focusEvent);
+ childDoc.close();
+ });
+ frame.src = "/common/blank.html";
+ });
+ frame.src = "resources/page-with-frame.html";
+}, "Standard event listeners are to be removed from Window for an active but not fully active document");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ const win = frame.contentWindow;
+ const doc = frame.contentDocument;
+ const body = doc.body;
+
+ // Right now the frame is connected and it has an active document.
+ frame.remove();
+
+ win.addEventListener("focus", t.unreached_func("window event listener not removed"));
+ body.onfocus = t.unreached_func("body event listener not removed");
+ doc.open();
+ assert_equals(body.onfocus, null);
+
+ // Now try to fire the focus event two different ways.
+ win.focus();
+ const focusEvent = new FocusEvent("focus");
+ win.dispatchEvent(focusEvent);
+ doc.close();
+}, "Standard event listeners are to be removed from Window for a non-active document that is the associated Document of a Window (frame is removed)");
+
+test(t => {
+ let winHappened = 0;
+ const winListener = t.step_func(() => { winHappened++; });
+ window.addEventListener("focus", winListener);
+ t.add_cleanup(() => { window.removeEventListener("focus", winListener); });
+
+ let bodyHappened = 0;
+ const bodyListener = t.step_func(() => { bodyHappened++; });
+ document.body.onfocus = bodyListener;
+ t.add_cleanup(() => { document.body.onfocus = null; });
+
+ const doc = document.implementation.createHTMLDocument();
+ doc.open();
+
+ const focusEvent = new FocusEvent("focus");
+ window.dispatchEvent(focusEvent);
+
+ assert_equals(winHappened, 1);
+ assert_equals(bodyHappened, 1);
+}, "Standard event listeners are NOT to be removed from Window for a Window-less document (createHTMLDocument)");
+
+test(t => {
+ let winHappened = 0;
+ const winListener = t.step_func(() => { winHappened++; });
+ window.addEventListener("focus", winListener);
+ t.add_cleanup(() => { window.removeEventListener("focus", winListener); });
+
+ let bodyHappened = 0;
+ const bodyListener = t.step_func(() => { bodyHappened++; });
+ document.body.onfocus = bodyListener;
+ t.add_cleanup(() => { document.body.onfocus = null; });
+
+ const doc = new DOMParser().parseFromString("", "text/html");
+ doc.open();
+
+ const focusEvent = new FocusEvent("focus");
+ window.dispatchEvent(focusEvent);
+
+ assert_equals(winHappened, 1);
+ assert_equals(bodyHappened, 1);
+}, "Standard event listeners are NOT to be removed from Window for a Window-less document (DOMParser)");
+
+async_test(t => {
+ const xhr = new XMLHttpRequest();
+ xhr.onload = t.step_func_done(() => {
+ assert_equals(xhr.status, 200);
+ const doc = xhr.responseXML;
+
+ let winHappened = 0;
+ const winListener = t.step_func(() => { winHappened++; });
+ window.addEventListener("focus", winListener);
+ t.add_cleanup(() => { window.removeEventListener("focus", winListener); });
+
+ let bodyHappened = 0;
+ const bodyListener = t.step_func(() => { bodyHappened++; });
+ document.body.onfocus = bodyListener;
+ t.add_cleanup(() => { document.body.onfocus = null; });
+
+ doc.open();
+
+ const focusEvent = new FocusEvent("focus");
+ window.dispatchEvent(focusEvent);
+
+ assert_equals(winHappened, 1);
+ assert_equals(bodyHappened, 1);
+ });
+ xhr.responseType = "document";
+ xhr.open("GET", "resources/dummy.html");
+ xhr.send();
+}, "Standard event listeners are NOT to be removed from Window for a Window-less document (XMLHttpRequest)");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.contentWindow.addEventListener("x", t.unreached_func("window event listener not removed"));
+ frame.contentDocument.open();
+ frame.contentWindow.dispatchEvent(new Event("x"));
+ frame.contentDocument.close();
+}, "Custom event listeners are to be removed from Window");
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ const childFrame = frame.contentDocument.querySelector("iframe");
+ const childDoc = childFrame.contentDocument;
+ const childWin = childFrame.contentWindow;
+
+ // Right now childDoc is still fully active.
+
+ frame.onload = t.step_func_done(() => {
+ // Now childDoc is still active but no longer fully active.
+ childWin.addEventListener("x", t.unreached_func("window event listener not removed"));
+ childDoc.open();
+ childWin.dispatchEvent(new Event("x"));
+ childDoc.close();
+ });
+ frame.src = "/common/blank.html";
+ });
+ frame.src = "resources/page-with-frame.html";
+}, "Custom event listeners are to be removed from Window for an active but not fully active document");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ const win = frame.contentWindow;
+ const doc = frame.contentDocument;
+
+ // Right now the frame is connected and it has an active document.
+ frame.remove();
+
+ win.addEventListener("x", t.unreached_func("window event listener not removed"));
+ doc.open();
+ win.dispatchEvent(new Event("x"));
+ doc.close();
+}, "Custom event listeners are to be removed from Window for a non-active document that is the associated Document of a Window (frame is removed)");
+
+test(t => {
+ const doc = document.implementation.createHTMLDocument();
+ let happened = false;
+ window.addEventListener("createHTMLDocumentTest", t.step_func(() => { happened = true; }));
+ doc.open();
+ window.dispatchEvent(new Event("createHTMLDocumentTest"));
+ assert_true(happened);
+}, "Custom event listeners are NOT to be removed from Window for a Window-less document (createHTMLDocument)");
+
+test(t => {
+ const doc = new DOMParser().parseFromString("", "text/html");
+ let happened = false;
+ window.addEventListener("DOMParserTest", t.step_func(() => { happened = true; }));
+ doc.open();
+ window.dispatchEvent(new Event("DOMParserTest"));
+ assert_true(happened);
+}, "Custom event listeners are NOT to be removed from Window for a Window-less document (DOMParser)");
+
+async_test(t => {
+ const xhr = new XMLHttpRequest();
+ xhr.onload = t.step_func_done(() => {
+ assert_equals(xhr.status, 200);
+ const doc = xhr.responseXML;
+ let happened = false;
+ window.addEventListener("XHRTest", t.step_func(() => { happened = true; }));
+ doc.open();
+ window.dispatchEvent(new Event("XHRTest"));
+ assert_true(happened);
+ });
+ xhr.responseType = "document";
+ xhr.open("GET", "resources/dummy.html");
+ xhr.send();
+}, "Custom event listeners are NOT to be removed from Window for a Window-less document (XMLHttpRequest)");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ body = frame.contentDocument.body;
+ t.add_cleanup(() => frame.remove());
+ const div = body.appendChild(frame.contentDocument.createElement("div"));
+ div.onclick = t.unreached_func("element event listener not removed");
+ frame.contentDocument.open();
+ assert_equals(div.onclick, null);
+ const e = frame.contentDocument.createEvent("mouseevents")
+ e.initEvent("click", false, false);
+ div.dispatchEvent(e);
+ frame.contentDocument.close();
+}, "IDL attribute event handlers are to be deactivated");
+
+var thrower;
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ body = frame.contentDocument.body;
+ t.add_cleanup(() => frame.remove());
+ const div = body.appendChild(frame.contentDocument.createElement("div"));
+ thrower = t.step_func(() => { throw new Error('element event listener not removed'); });
+ div.setAttribute("onclick", "parent.thrower()");
+ assert_not_equals(div.onclick, null);
+ frame.contentDocument.open();
+ assert_equals(div.getAttribute("onclick"), "parent.thrower()");
+ assert_equals(div.onclick, null);
+ const e = frame.contentDocument.createEvent("mouseevents")
+ e.initEvent("click", false, false);
+ div.dispatchEvent(e);
+ frame.contentDocument.close();
+}, "Content attribute event handlers are to be deactivated");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ let once = false;
+ frame.contentDocument.addEventListener("x", () => {
+ frame.contentDocument.open();
+ once = true;
+ });
+ frame.contentDocument.addEventListener("x", t.unreached_func("second event listener not removed"));
+ frame.contentDocument.dispatchEvent(new Event("x"));
+ assert_true(once);
+ frame.contentDocument.close();
+}, "Event listeners are to be removed with immediate effect");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ shadow = frame.contentDocument.body.attachShadow({ mode: "closed" }),
+ shadowChild = shadow.appendChild(document.createElement("div")),
+ shadowShadow = shadowChild.attachShadow({ mode: "open" }),
+ nodes = [shadow, shadowChild, shadowShadow];
+ t.add_cleanup(() => frame.remove());
+ nodes.forEach(node => {
+ node.addEventListener("x", t.unreached_func(node + "'s event listener not removed"));
+ });
+ frame.contentDocument.open();
+ nodes.forEach(node => {
+ node.dispatchEvent(new Event("x"));
+ });
+ frame.contentDocument.close();
+}, "Event listeners are to be removed from shadow trees as well");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/form-control-state.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/form-control-state.html
new file mode 100644
index 0000000000..7d03a885f0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/form-control-state.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Writing out a document with form controls with values</title>
+<link rel="author" href="mailto:bzbarsky@mit.edu"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+function asyncHop(t, arg) {
+ return new Promise(res => t.step_timeout(res.bind(null, arg), 0));
+}
+
+function loadPromise(t, iframe) {
+ var p = new Promise(res =>
+ iframe.addEventListener("load", res.bind(null, iframe), { once: true }));
+ // We need to do one trip through the event loop to make sure we're
+ // not still under the load event firing when we start doing our
+ // document.open bits.
+ return p.then(asyncHop.bind(null, t));
+}
+
+async function createIframe(t) {
+ var i = document.createElement("iframe");
+ t.add_cleanup(() => i.remove());
+ var p = loadPromise(t, i);
+ document.body.appendChild(i);
+ return p;
+}
+
+async function replaceIframe(t, i, text) {
+ var p = loadPromise(t, i);
+ var doc = i.contentDocument;
+ doc.open();
+ doc.write(text);
+ doc.close();
+ return p;
+}
+
+promise_test(async function(t) {
+ var i = await createIframe(t);
+ var str = "<textarea>123</textarea>";
+ await replaceIframe(t, i, str);
+ i.contentDocument.querySelector("textarea").value = "abc";
+ await replaceIframe(t, i, str);
+ assert_equals(i.contentDocument.querySelector("textarea").value, "123");
+}, "textarea state");
+
+promise_test(async function(t) {
+ var i = await createIframe(t);
+ var str = "<input value='123'>";
+ await replaceIframe(t, i, str);
+ i.contentDocument.querySelector("input").value = "abc";
+ await replaceIframe(t, i, str);
+ assert_equals(i.contentDocument.querySelector("input").value, "123");
+}, "input state");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history-state.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history-state.window.js
new file mode 100644
index 0000000000..7fb172a141
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history-state.window.js
@@ -0,0 +1,29 @@
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "/common/blank.html";
+ iframe.onload = t.step_func_done(() => {
+ const win = iframe.contentWindow;
+ const doc = iframe.contentDocument;
+ assert_equals(win.history.state, null);
+ win.history.replaceState("state", "");
+ assert_equals(win.history.state, "state");
+ assert_equals(doc.open(), doc);
+ assert_equals(win.history.state, "state");
+ });
+}, "history.state is kept by document.open()");
+
+async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "/common/blank.html";
+ iframe.onload = t.step_func_done(() => {
+ const win = iframe.contentWindow;
+ const doc = iframe.contentDocument;
+ assert_equals(win.history.state, null);
+ win.history.replaceState("state", "");
+ assert_equals(win.history.state, "state");
+ assert_equals(doc.open("", "replace"), doc);
+ assert_equals(win.history.state, "state");
+ });
+}, "history.state is kept by document.open() (with historical replace parameter set)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history.window.js
new file mode 100644
index 0000000000..0134da24f0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/history.window.js
@@ -0,0 +1,29 @@
+// Historically, document.open() created an entry in the session history so
+// that the original page could be seen by going back. Test that this behavior
+// no longer occurs.
+//
+// This test uses window.open() for variety, as most other tests in this
+// directory use document.open(). An <iframe> would probably work also. We can
+// always add an <iframe>-based test later if it is deemed necessary.
+
+const t = async_test("document.open should not add an entry to the session history");
+
+const frameURL = new URL("resources/history-frame.html", document.URL).href;
+
+let origLength;
+window.onFrameLoaded = t.step_func(() => {
+ window.onFrameLoaded = t.unreached_func("onFrameLoaded should only be called once");
+ assert_equals(win.document.URL, frameURL);
+ assert_true(win.document.body.textContent.includes("Old"));
+ origLength = win.history.length;
+});
+window.onDocumentOpen = t.step_func_done(() => {
+ window.onDocumentOpen = t.unreached_func("onDocumentOpen should only be called once");
+ assert_equals(win.document.URL, frameURL);
+ assert_true(win.document.body.textContent.includes("New"));
+ assert_not_equals(origLength, undefined);
+ assert_equals(win.history.length, origLength);
+});
+
+const win = window.open(frameURL);
+t.add_cleanup(() => win.close());
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/ignore-opens-during-unload.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/ignore-opens-during-unload.window.js
new file mode 100644
index 0000000000..43506a22a4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/ignore-opens-during-unload.window.js
@@ -0,0 +1,60 @@
+for (const [ev, target] of [
+ ["beforeunload", iframe => iframe.contentWindow],
+ ["pagehide", iframe => iframe.contentWindow],
+ ["unload", iframe => iframe.contentWindow],
+ ["visibilitychange", iframe => iframe.contentDocument],
+]) {
+ async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "/common/blank.html";
+ iframe.onload = t.step_func(() => {
+ target(iframe).addEventListener(ev, t.step_func_done(() => {
+ assert_not_equals(iframe.contentDocument.childNodes.length, 0);
+ assert_equals(iframe.contentDocument.open(), iframe.contentDocument);
+ assert_not_equals(iframe.contentDocument.childNodes.length, 0);
+ }));
+ iframe.src = "about:blank";
+ });
+ }, `document.open should bail out when ignore-opens-during-unload is greater than 0 during ${ev} event (in top-level browsing context)`);
+
+ async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "/common/blank.html?1";
+ iframe.onload = t.step_func(() => {
+ const doc = iframe.contentDocument;
+ const innerIframe = doc.body.appendChild(doc.createElement("iframe"));
+ innerIframe.src = "/common/blank.html?2";
+ innerIframe.onload = t.step_func(() => {
+ // Navigate the parent, listen on the child, and open() the parent.
+ target(innerIframe).addEventListener(ev, t.step_func_done(() => {
+ assert_not_equals(iframe.contentDocument.childNodes.length, 0);
+ iframe.contentDocument.open();
+ assert_not_equals(iframe.contentDocument.childNodes.length, 0);
+ }));
+ iframe.src = "about:blank";
+ });
+ });
+ }, `document.open should bail out when ignore-opens-during-unload is greater than 0 during ${ev} event (open(parent) while unloading parent and child)`);
+
+ async_test(t => {
+ const iframe = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "/common/blank.html?1";
+ iframe.onload = t.step_func(() => {
+ const doc = iframe.contentDocument;
+ const innerIframe = doc.body.appendChild(doc.createElement("iframe"));
+ innerIframe.src = "/common/blank.html?2";
+ innerIframe.onload = t.step_func(() => {
+ // Navigate the child, listen on the child, and open() the parent.
+ target(innerIframe).addEventListener(ev, t.step_func_done(() => {
+ assert_not_equals(iframe.contentDocument.childNodes.length, 0);
+ iframe.contentDocument.open();
+ assert_equals(iframe.contentDocument.childNodes.length, 0);
+ }));
+ innerIframe.src = "about:blank";
+ });
+ });
+ }, `document.open should bail out when ignore-opens-during-unload is greater than 0 during ${ev} event (open(parent) while unloading child only)`);
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html
new file mode 100644
index 0000000000..a3bdd86ee6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+ <script>
+ var t = async_test("Location sets should cancel current navigation and prevent later document.open() from doing anything");
+
+ var finishTest = t.step_func_done(function() {
+ assert_equals(frames[0].document.body.textContent, "PASS",
+ "Should not have FAIL in our textContent");
+ });
+
+ t.step(function() {
+ var i = document.createElement("iframe");
+ i.srcdoc = `
+ <script>
+ var blob = new Blob(["PASS"], { type: "text/html" });
+ var url = URL.createObjectURL(blob);
+ location.href = url;
+ frameElement.onload = parent.finishTest;
+ document.open();
+ document.write("FAIL");
+ document.close();
+ <\/script>`;
+ document.body.appendChild(i);
+ });
+
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-events.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-events.window.js
new file mode 100644
index 0000000000..4efbb863c6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-events.window.js
@@ -0,0 +1,22 @@
+// In an ideal world this test would eventually be obsolete due to mutation events disappearing. Or
+// would have to change to account for mutation events not firing synchronously. Neither seems
+// realistic to the author though.
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ frame.contentWindow.addEventListener("DOMNodeInserted", t.unreached_func());
+ frame.contentWindow.addEventListener("DOMNodeInserted", t.unreached_func(), true);
+ frame.contentWindow.addEventListener("DOMNodeInsertedIntoDocument", t.unreached_func(), true);
+ frame.contentWindow.addEventListener("DOMNodeRemoved", t.unreached_func());
+ frame.contentWindow.addEventListener("DOMNodeRemoved", t.unreached_func(), true);
+ frame.contentWindow.addEventListener("DOMNodeRemovedFromDocument", t.unreached_func(), true);
+ frame.contentWindow.addEventListener("DOMSubtreeModified", t.unreached_func());
+ frame.contentWindow.addEventListener("DOMSubtreeModified", t.unreached_func(), true);
+ assert_equals(frame.contentDocument.documentElement.localName, "html");
+ assert_equals(frame.contentDocument.open(), frame.contentDocument);
+ assert_equals(frame.contentDocument.documentElement, null);
+ frame.contentDocument.write("<div>heya</div>");
+ frame.contentDocument.close();
+ assert_equals(frame.contentDocument.documentElement.localName, "html");
+ frame.remove();
+}, "document.open(), the HTML parser, and mutation events");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-observer.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-observer.window.js
new file mode 100644
index 0000000000..34e73146a9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/mutation-observer.window.js
@@ -0,0 +1,19 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { frame.remove(); });
+ const originalHTMLElement = frame.contentDocument.documentElement;
+ assert_equals(originalHTMLElement.localName, "html");
+ const observer = new frame.contentWindow.MutationObserver(t.step_func_done(records => {
+ // Even though we passed `subtree: true` to observer.observe, due to the
+ // fact that "replace all" algorithm removes children with the "suppress
+ // observers flag" set, we still only get the html element as the sole
+ // removed node.
+ assert_equals(records.length, 1);
+ assert_equals(records[0].type, "childList");
+ assert_equals(records[0].target, frame.contentDocument);
+ assert_array_equals(records[0].addedNodes, []);
+ assert_array_equals(records[0].removedNodes, [originalHTMLElement]);
+ }));
+ observer.observe(frame.contentDocument, { childList: true, subtree: true });
+ assert_equals(frame.contentDocument.open(), frame.contentDocument);
+}, "document.open() should inform mutation observer of node removal");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/no-new-global.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/no-new-global.window.js
new file mode 100644
index 0000000000..d4a9296fca
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/no-new-global.window.js
@@ -0,0 +1,57 @@
+// In an earlier version of the HTML Standard, document open steps created a
+// new JavaScript realm and migrated the existing objects to use the new realm.
+// Test that this no longer happens.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ // Ensure a load event gets dispatched to unblock testharness
+ t.add_cleanup(() => frame.remove());
+ frame.src = "resources/global-variables-frame.html";
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentWindow.hey, "You", "precondition");
+ frame.contentDocument.open();
+ assert_equals(frame.contentWindow.hey, "You", "actual check");
+ });
+}, "Obtaining a variable from a global whose document had open() invoked");
+
+function testIdentity(desc, frameToObject, frameToConstructor) {
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ // Ensure a load event gets dispatched to unblock testharness
+ t.add_cleanup(() => frame.remove());
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func_done(() => {
+ const obj = frameToObject(frame);
+ frame.contentDocument.open();
+ assert_equals(frameToObject(frame), obj);
+ });
+ }, `${desc} maintains object identity through open()`);
+
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ // Ensure a load event gets dispatched to unblock testharness
+ t.add_cleanup(() => frame.remove());
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func_done(() => {
+ const obj = frameToObject(frame);
+ const origProto = Object.getPrototypeOf(obj);
+ const origCtor = frameToConstructor(frame);
+ const sym = Symbol();
+ obj[sym] = "foo";
+ frame.contentDocument.open();
+ assert_equals(frameToObject(frame)[sym], "foo");
+ assert_true(frameToObject(frame) instanceof origCtor);
+ assert_equals(Object.getPrototypeOf(frameToObject(frame)), origProto);
+ assert_equals(frameToConstructor(frame), origCtor);
+ });
+ }, `${desc} maintains its prototype and properties through open()`);
+}
+
+testIdentity("Document", frame => frame.contentDocument, frame => frame.contentWindow.Document);
+testIdentity("WindowProxy", frame => frame.contentWindow, frame => frame.contentWindow.Window);
+testIdentity("BarProp", frame => frame.contentWindow.locationbar, frame => frame.contentWindow.BarProp);
+testIdentity("History", frame => frame.contentWindow.history, frame => frame.contentWindow.History);
+testIdentity("localStorage", frame => frame.contentWindow.localStorage, frame => frame.contentWindow.Storage);
+testIdentity("Location", frame => frame.contentWindow.location, frame => frame.contentWindow.Location);
+testIdentity("sessionStorage", frame => frame.contentWindow.sessionStorage, frame => frame.contentWindow.Storage);
+testIdentity("Navigator", frame => frame.contentWindow.navigator, frame => frame.contentWindow.Navigator);
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-basic.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-basic.html
new file mode 100644
index 0000000000..118be71af1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-basic.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Origin check in document.open() - Basic usage</title>
+<link rel="author" title="Jochen Eisinger" href="mailto:jochen@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#opening-the-input-stream">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+<body>
+<script>
+testInIFrame(undefined, (ctx) => {
+ try {
+ ctx.iframes[0].contentDocument.open();
+ } catch (e) {
+ assert_unreached("Opening a same origin document throws");
+ }
+}, "It should be possible to open same origin documents.");
+
+testInIFrame(undefined, (ctx) => {
+ try {
+ ctx.iframes[0].contentDocument.write("");
+ } catch (e) {
+ assert_unreached("Implicitly opening a same origin document throws");
+ }
+}, "It should be possible to implicitly open same origin documents.");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-same-origin-domain.sub.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-same-origin-domain.sub.html
new file mode 100644
index 0000000000..ba4ef3bae8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/origin-check-in-document-open-same-origin-domain.sub.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Origin check in document.open() - same origin-domain (but not same origin) documents</title>
+<link rel="author" title="Jochen Eisinger" href="mailto:jochen@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#opening-the-input-stream">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/resources/common.js"></script>
+<body>
+<script>
+testInIFrame("http://{{host}}:{{ports[http][1]}}/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/set-document-domain.html", (ctx) => {
+ document.domain = document.domain;
+ let doc = ctx.iframes[0].contentDocument;
+ let constructor = ctx.iframes[0].contentWindow.DOMException;
+ assert_throws_dom("SecurityError", constructor, doc.open.bind(doc), "Opening a same origin-domain (but not same origin) document doesn't throw.");
+}, "It should not be possible to open same origin-domain (but not same origin) documents.");
+
+testInIFrame("http://{{host}}:{{ports[http][1]}}/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/set-document-domain.html", (ctx) => {
+ document.domain = document.domain;
+ let doc = ctx.iframes[0].contentDocument;
+ let constructor = ctx.iframes[0].contentWindow.DOMException;
+ assert_throws_dom("SecurityError", constructor, doc.write.bind(doc, ""), "Implicitly opening a same origin-domain (but not same origin) document doesn't throw.");
+}, "It should not be possible to implicitly open same origin-domain (but not same origin) documents.");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/quirks.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/quirks.window.js
new file mode 100644
index 0000000000..0ff0bb9944
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/quirks.window.js
@@ -0,0 +1,74 @@
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.contentDocument.close());
+ assert_equals(frame.contentDocument.compatMode, "BackCompat");
+ frame.contentDocument.open();
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+ frame.contentDocument.close();
+ assert_equals(frame.contentDocument.compatMode, "BackCompat");
+}, "document.open() sets document to no-quirks mode (write no doctype)");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.contentDocument.close());
+ assert_equals(frame.contentDocument.compatMode, "BackCompat");
+ frame.contentDocument.open();
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+ frame.contentDocument.write("<!doctype html public");
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+ frame.contentDocument.write(" \"-//IETF//DTD HTML 3//\"");
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+ frame.contentDocument.write(">");
+ assert_equals(frame.contentDocument.compatMode, "BackCompat");
+ frame.contentDocument.close();
+ assert_equals(frame.contentDocument.compatMode, "BackCompat");
+}, "document.open() sets document to no-quirks mode (write old doctype)");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.contentDocument.close());
+ assert_equals(frame.contentDocument.compatMode, "BackCompat");
+ frame.contentDocument.open();
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+ frame.contentDocument.write("<!doctype html");
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+ frame.contentDocument.write(">");
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+ frame.contentDocument.close();
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+}, "document.open() sets document to no-quirks mode (write new doctype)");
+
+// This tests the document.open() call in fact sets the document to no-quirks
+// mode, not limited-quirks mode. It is derived from
+// quirks/blocks-ignore-line-height.html in WPT, as there is no direct way to
+// distinguish between a no-quirks document and a limited-quirks document. It
+// assumes that the user agent passes the linked test, which at the time of
+// writing is all major web browsers.
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.contentDocument.close());
+ assert_equals(frame.contentDocument.compatMode, "BackCompat");
+ frame.contentDocument.open();
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+
+ // Create the DOM tree manually rather than going through document.write() to
+ // bypass the parser, which resets the document mode.
+ const html = frame.contentDocument.appendChild(frame.contentDocument.createElement("html"));
+ const body = html.appendChild(frame.contentDocument.createElement("body"));
+ assert_equals(frame.contentDocument.body, body);
+ body.innerHTML = `
+ <style>#ref { display:block }</style>
+ <div id=test><font size=1>x</font></div>
+ <font id=ref size=1>x</font>
+ <div id=s_ref>x</div>
+ `;
+ assert_equals(frame.contentDocument.compatMode, "CSS1Compat");
+
+ const idTest = frame.contentDocument.getElementById("test");
+ const idRef = frame.contentDocument.getElementById("ref");
+ const idSRef = frame.contentDocument.getElementById("s_ref");
+ assert_equals(frame.contentWindow.getComputedStyle(idTest).height,
+ frame.contentWindow.getComputedStyle(idSRef).height);
+ assert_not_equals(frame.contentWindow.getComputedStyle(idTest).height,
+ frame.contentWindow.getComputedStyle(idRef).height);
+}, "document.open() sets document to no-quirks mode, not limited-quirks mode");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/readiness.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/readiness.window.js
new file mode 100644
index 0000000000..729a958700
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/readiness.window.js
@@ -0,0 +1,25 @@
+// This tests the behavior of dynamic markup insertion APIs with a document's
+// readiness.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { frame.remove(); });
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func_done(() => {
+ const states = [];
+ frame.contentDocument.onreadystatechange = t.step_func(() => {
+ states.push(frame.contentDocument.readyState);
+ });
+ assert_equals(frame.contentDocument.readyState, "complete");
+ assert_array_equals(states, []);
+
+ // When open() is called, it first removes the event listeners and handlers
+ // from all nodes in the DOM tree. Then, after a new parser is created and
+ // initialized, it changes the current document readiness to "loading".
+ // However, because all event listeners are removed, we cannot observe the
+ // readystatechange event fired for "loading" inside open().
+ frame.contentDocument.open();
+ assert_equals(frame.contentDocument.readyState, "loading");
+ assert_array_equals(states, []);
+ });
+}, "document.open() and readiness");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js
new file mode 100644
index 0000000000..279020f64d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/reload.window.js
@@ -0,0 +1,71 @@
+// This test tests for the nonexistence of a reload override buffer, which is
+// used in a previous version of the HTML Standard to make reloads of a
+// document.open()'d document load the written-to document rather than doing an
+// actual reload of the document's URL.
+//
+// This test has a somewhat interesting structure compared to the other tests
+// in this directory. It eschews the <iframe> structure used by other tests,
+// since when the child frame is reloaded it would adopt the URL of the test
+// page (the responsible document of the entry settings object), and the spec
+// forbids navigation in nested browsing contexts to the same URL as their
+// parent. To work around that, we use window.open() which does not suffer from
+// that restriction.
+//
+// In any case, this test as the caller of `document.open()` would be used both
+// as the test file and as part of the test file. The `if (window.name !==
+// "opened-dummy-window")` condition controls what role this file plays.
+
+if (window.name !== "opened-dummy-window") {
+ async_test(t => {
+ const testURL = document.URL;
+ const dummyURL = new URL("resources/dummy.html", document.URL).href;
+
+ // 1. Open an auxiliary window.
+ const win = window.open("resources/dummy.html", "opened-dummy-window");
+ t.add_cleanup(() => { win.close(); });
+
+ win.addEventListener("load", t.step_func(() => {
+ // The timeout seems to be necessary for Firefox, which when `load` is
+ // called may still have an active parser.
+ t.step_timeout(() => {
+ const doc = win.document;
+ assert_true(doc.body.textContent.includes("Dummy"), "precondition");
+ assert_equals(doc.URL, dummyURL, "precondition");
+
+ window.onChildLoad = t.step_func(message => {
+ // 3. The dynamically overwritten content will trigger this function,
+ // which puts in place the actual test.
+
+ assert_equals(message, "Written", "script on written page is executed");
+ assert_true(win.document.body.textContent.includes("Content"), "page is written to");
+ assert_equals(win.document.URL, testURL, "postcondition: after document.write()");
+ assert_equals(win.document, doc, "document.open should not change the document object");
+ window.onChildLoad = t.step_func_done(message => {
+ // 6. This function should be called from the if (opener) branch of
+ // this file. It would throw an assertion error if the overwritten
+ // content was executed instead.
+ assert_equals(message, "Done!", "actual test");
+ assert_true(win.document.body.textContent.includes("Back to the test"), "test is reloaded");
+ assert_equals(win.document.URL, testURL, "postcondition: after reload");
+ assert_not_equals(win.document, doc, "reload should change the document object");
+ });
+
+ // 4. Reload the pop-up window. Because of the doc.open() call, this
+ // pop-up window will reload to the same URL as this test itself.
+ win.location.reload();
+ });
+
+ // 2. When it is loaded, dynamically overwrite its content.
+ assert_equals(doc.open(), doc);
+ assert_equals(doc.URL, testURL, "postcondition: after document.open()");
+ doc.write("<p>Content</p><script>opener.onChildLoad('Written');</script>");
+ doc.close();
+ }, 100);
+ }), { once: true });
+ }, "Reloading a document.open()'d page should reload the URL of the entry realm's responsible document");
+} else {
+ document.write("<p>Back to the test</p>");
+ // 5. Since this window is window.open()'d, opener refers to the test window.
+ // Inform the opener that reload succeeded.
+ opener.onChildLoad("Done!");
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/remove-initial-about-blankness.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/remove-initial-about-blankness.window.js
new file mode 100644
index 0000000000..7442bc4925
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/remove-initial-about-blankness.window.js
@@ -0,0 +1,65 @@
+// This tests the issues discussed in https://github.com/whatwg/html/issues/4299
+// and fixed in https://github.com/whatwg/html/pull/6567.
+
+// Note: because browsers do not interoperate on the spec's notion of window reuse (see e.g. https://crbug.com/778318)
+// we pick a specific interoperable test case, which is "currently on initial about:blank, but loading something".
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+
+ // We can't just leave it at the actual initial about:blank because of the interop issues mentioned above.
+ // So put it in the "currently on initial about:blank, but loading something" state which interoperably does Window
+ // reuse.
+ iframe.src = "/common/blank.html";
+
+ // Create the Window object. It will be for the initial about:blank since the load of /common/blank.html hasn't
+ // completed.
+ document.body.append(iframe);
+
+ // Store a string on that Window object so we can later test if it's reused.
+ iframe.contentWindow.persistedString = "Hello world!";
+
+ // This will reset the initial about:blank-ness. But, it will also cancel any ongoing loads.
+ iframe.contentDocument.open();
+
+ // So, re-start the load of /common/blank.html.
+ iframe.src = "/common/blank.html";
+
+ // When the load finally happens, will it reuse the Window object or not?
+ // Because document.open() resets the initial about:blank-ness, it will *not* reuse the Window object.
+ // The point of the test is to assert that.
+ iframe.addEventListener("load", t.step_func_done(() => {
+ assert_equals(
+ iframe.contentDocument.URL,
+ iframe.src,
+ "Prerequisite check: we are getting the right load event"
+ );
+
+ assert_equals(iframe.contentWindow.persistedString, undefined);
+ }), { once: true });
+}, "document.open() removes the initial about:blank-ness of the document");
+
+// This test is redundant with others in WPT but it's intended to make it clear that document.open() is the
+// distinguishing factor. It does the same exact thing but without document.open() and with the resulting final assert
+// flipped.
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ document.body.append(iframe);
+
+ iframe.contentWindow.persistedString = "Hello world!";
+
+ // NO document.open() call.
+
+ iframe.src = "/common/blank.html";
+
+ iframe.addEventListener("load", t.step_func_done(() => {
+ assert_equals(
+ iframe.contentDocument.URL,
+ iframe.src,
+ "Prerequisite check: we are getting the right load event"
+ );
+
+ assert_equals(iframe.contentWindow.persistedString, "Hello world!");
+ }), { once: true });
+}, "Double-check: without document.open(), Window reuse indeed happens");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html
new file mode 100644
index 0000000000..d5535630be
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<p>Text</p>
+<script>
+window.stop();
+parent.step_timeout(() => {
+ document.open();
+ parent.handlers.afterOpenAsync();
+}, 10);
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html
new file mode 100644
index 0000000000..d9ec23590b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<p>Text</p>
+<script>
+window.stop();
+document.open();
+parent.handlers.afterOpen();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-custom-element-with-domain-frame.sub.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-custom-element-with-domain-frame.sub.html
new file mode 100644
index 0000000000..4de97e8ed1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-custom-element-with-domain-frame.sub.html
@@ -0,0 +1,13 @@
+<p>Text</p>
+<script>
+document.domain = "{{host}}";
+
+class CustomElement extends HTMLElement {
+ constructor() {
+ super();
+ parent.onCustomElementReady();
+ }
+}
+customElements.define("custom-element", CustomElement);
+</script>
+<custom-element></custom-element>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-frame.html
new file mode 100644
index 0000000000..632b2934ac
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-frame.html
@@ -0,0 +1,4 @@
+<p>Text</p>
+<script>
+parent.testSynchronousScript();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-with-domain-frame.sub.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-with-domain-frame.sub.html
new file mode 100644
index 0000000000..7ca7b5f44c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-synchronous-script-with-domain-frame.sub.html
@@ -0,0 +1,5 @@
+<p>Text</p>
+<script>
+document.domain = "{{host}}";
+parent.testSynchronousScript();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-domain-frame.sub.xhtml b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-domain-frame.sub.xhtml
new file mode 100644
index 0000000000..b054c0fe3a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-domain-frame.sub.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head><title>XHTML document with domain set</title></head>
+ <body>
+ <p>Text</p>
+ <script>
+ document.domain = "{{host}}";
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-synchronous-script-frame.xhtml b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-synchronous-script-frame.xhtml
new file mode 100644
index 0000000000..00fc71eccf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/bailout-order-xml-with-synchronous-script-frame.xhtml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head><title>XHTML document with hook to run script from a script tag</title></head>
+ <body>
+ <p>Text</p>
+ <script>
+ parent.testSynchronousScript();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/document-open-side-effects.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/document-open-side-effects.js
new file mode 100644
index 0000000000..7cb86dcba0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/document-open-side-effects.js
@@ -0,0 +1,8 @@
+function assertDocumentIsReadyForSideEffectsTest(doc, description) {
+ assert_not_equals(doc.childNodes.length, 0, `document should not be empty before side effects test (${description})`);
+}
+
+function assertOpenHasNoSideEffects(doc, originalURL, description) {
+ assert_not_equals(doc.childNodes.length, 0, `document nodes should not be cleared (${description})`);
+ assert_equals(doc.URL, originalURL, `The original URL should be kept (${description})`);
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/dummy.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/dummy.html
new file mode 100644
index 0000000000..a092f4e2d7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/dummy.html
@@ -0,0 +1,2 @@
+<!-- Like /common/blank.html, but with some content in it. -->
+<p>Dummy</p>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/encoding-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/encoding-frame.html
new file mode 100644
index 0000000000..843c3a2c79
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/encoding-frame.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<meta charset=ms932>
+<p>Encoded in Shift_JIS.</p>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/global-variables-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/global-variables-frame.html
new file mode 100644
index 0000000000..0fe189914c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/global-variables-frame.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+hey = "You";
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/history-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/history-frame.html
new file mode 100644
index 0000000000..2404105b09
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/history-frame.html
@@ -0,0 +1,20 @@
+<script>
+function queueTest() {
+ // The timeout is necessary to avoid the parser still being active when
+ // `document.open()` is called and becoming a no-op.
+ //
+ // We also cannot use setTimeout(..., 0), as the parser is terminated in a
+ // task with DOM manipulation task source while the timeout is run in a task
+ // on the timer task source. The order is therefore not guaranteed. Let's
+ // play it safer and use some actual timeout.
+ setTimeout(() => {
+ document.open();
+ document.write("<p>New content</p>");
+ document.close();
+ opener.onDocumentOpen();
+ }, 200);
+}
+</script>
+<body onload="opener.onFrameLoaded(); queueTest();">
+<p>Old content</p>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/http-refresh.py b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/http-refresh.py
new file mode 100644
index 0000000000..161a34b6b5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/http-refresh.py
@@ -0,0 +1,5 @@
+from wptserve.utils import isomorphic_encode
+
+def main(request, response):
+ time = isomorphic_encode(request.url_parts.query) if request.url_parts.query else b'0'
+ return 200, [(b'Refresh', time), (b'Content-Type', b"text/html")], b''
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/meta-refresh.py b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/meta-refresh.py
new file mode 100644
index 0000000000..2dfbab6e76
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/meta-refresh.py
@@ -0,0 +1,3 @@
+def main(request, response):
+ time = request.url_parts.query if request.url_parts.query else u'0'
+ return 200, [[b'Content-Type', b'text/html']], u'<meta http-equiv=refresh content=%s>' % time
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/page-with-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/page-with-frame.html
new file mode 100644
index 0000000000..a1ab01e072
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/page-with-frame.html
@@ -0,0 +1 @@
+<iframe src="/common/blank.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/set-document-domain.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/set-document-domain.html
new file mode 100644
index 0000000000..a92a7ae39f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/set-document-domain.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+document.domain = document.domain;
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/slow-png.py b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/slow-png.py
new file mode 100644
index 0000000000..fced22aa26
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/slow-png.py
@@ -0,0 +1,8 @@
+import time
+from base64 import decodebytes
+
+png_response = decodebytes(b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg==')
+
+def main(request, response):
+ time.sleep(2)
+ return 200, [], png_response
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-incumbent-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-incumbent-frame.html
new file mode 100644
index 0000000000..bd78d8ee52
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-incumbent-frame.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+window.callDocumentMethod = methodName => document[methodName]();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-timer-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-timer-frame.html
new file mode 100644
index 0000000000..b2c050768c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-entry-document-timer-frame.html
@@ -0,0 +1,3 @@
+<script>
+setTimeout(parent.timerTest, 10);
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-frame.html b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-frame.html
new file mode 100644
index 0000000000..be483ff0ae
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/url-frame.html
@@ -0,0 +1,9 @@
+<script>
+onload = () => {
+ const beforeURL = document.URL;
+ document.open();
+ const afterURL = document.URL;
+ document.close();
+ parent.testDone(beforeURL, afterURL);
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js
new file mode 100644
index 0000000000..887adcb739
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js
@@ -0,0 +1,106 @@
+// An older version of the HTML Standard mandated that document.open() remove
+// all tasks associated with the document on which open() is called. This step
+// has been proposed to be removed. This series of tests ensures that this step
+// is no longer executed.
+//
+// This file comprehensively (but not exhaustively) tests for many queued tasks
+// that may be observable. Each taskTest() call in fact runs two tests: the
+// first one "tasks without document.open()" does not actually run
+// document.open(), just to test that the tested task works ordinarily; the
+// second actually calls document.open() to test if the method call removes
+// that specific task from the queue.
+
+// This is necessary to allow the promise rejection test below.
+setup({
+ allow_uncaught_exception: true
+});
+
+function taskTest(description, testBody) {
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ // The empty HTML seems to be necessary to cajole Chrome and Safari into
+ // firing a load event asynchronously, which is necessary to make sure the
+ // frame's document doesn't have a parser associated with it.
+ // See: https://crbug.com/569511
+ frame.src = "/common/blank.html";
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ // Make sure there is no parser. Firefox seems to have an additional
+ // non-spec-compliant readiness state "uninitialized", so test for the
+ // two known valid readiness states instead.
+ // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1191683
+ assert_in_array(frame.contentDocument.readyState, ["interactive", "complete"]);
+ testBody(t, frame, doc => {});
+ });
+ }, `tasks without document.open() (${description})`);
+
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ // The empty HTML seems to be necessary to cajole Chrome into firing a load
+ // event, which is necessary to make sure the frame's document doesn't have
+ // a parser associated with it.
+ // See: https://crbug.com/569511
+ frame.src = "/common/blank.html";
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ // Make sure there is no parser. Firefox seems to have an additional
+ // non-spec-compliant readiness state "uninitialized", so test for the
+ // two known valid readiness states instead.
+ // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1191683
+ assert_in_array(frame.contentDocument.readyState, ["interactive", "complete"]);
+ testBody(t, frame, doc => doc.open());
+ });
+ }, `document.open() and tasks (${description})`);
+}
+
+taskTest("timeout", (t, frame, open) => {
+ frame.contentWindow.setTimeout(t.step_func_done(), 100);
+ open(frame.contentDocument);
+});
+
+taskTest("window message", (t, frame, open) => {
+ let counter = 0;
+ frame.contentWindow.postMessage(undefined, "*");
+ open(frame.contentDocument);
+ frame.contentWindow.postMessage(undefined, "*");
+ frame.contentWindow.onmessage = t.step_func(e => {
+ assert_equals(e.data, undefined);
+ counter++;
+ assert_less_than_equal(counter, 2);
+ if (counter == 2) {
+ t.done();
+ }
+ });
+});
+
+taskTest("canvas.toBlob()", (t, frame, open) => {
+ const canvas = frame.contentDocument.body.appendChild(frame.contentDocument.createElement("canvas"));
+ canvas.toBlob(t.step_func_done());
+ open(frame.contentDocument);
+});
+
+taskTest("MessagePort", (t, frame, open) => {
+ frame.contentWindow.eval(`({ port1, port2 } = new MessageChannel());`);
+ frame.contentWindow.port2.onmessage = t.step_func_done(ev => {
+ assert_equals(ev.data, "Hello world");
+ });
+ frame.contentWindow.port1.postMessage("Hello world");
+ open(frame.contentDocument);
+});
+
+taskTest("Promise rejection", (t, frame, open) => {
+ // There is currently some ambiguity on which Window object the
+ // unhandledrejection event should be fired on. Here, let's account for that
+ // ambiguity and allow event fired on _any_ global to pass this test.
+ // See:
+ // - https://github.com/whatwg/html/issues/958,
+ // - https://bugs.webkit.org/show_bug.cgi?id=187822
+ const promise = frame.contentWindow.eval("Promise.reject(42);");
+ open(frame.contentDocument);
+ const listener = t.step_func_done(ev => {
+ assert_equals(ev.promise, promise);
+ assert_equals(ev.reason, 42);
+ });
+ frame.contentWindow.onunhandledrejection = listener;
+ window.onunhandledrejection = listener;
+});
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt
new file mode 100644
index 0000000000..3e715502b9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt
@@ -0,0 +1 @@
+Some text.
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js
new file mode 100644
index 0000000000..ab1d9706a4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js
@@ -0,0 +1,23 @@
+["replace",
+ "NOBODY",
+ "@ FD ;",
+ "it does not matter, you see \f",
+ "text/plain",
+ "text/xml",
+ "application/octet-stream",
+ "\0"].forEach(type => {
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ frame.src = "type-argument-plaintext-subframe.txt";
+ document.body.appendChild(frame);
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.open(type), frame.contentDocument);
+ frame.contentDocument.write("<B>heya</b>");
+ frame.contentDocument.close();
+ assert_equals(frame.contentDocument.body.firstChild.localName, "b");
+ assert_equals(frame.contentDocument.body.textContent, "heya");
+ assert_equals(frame.contentDocument.contentType, "text/plain");
+ });
+ }, "document.open() on plaintext document with type set to: " + type + " (type argument is supposed to be ignored)");
+});
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js
new file mode 100644
index 0000000000..9174008da3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js
@@ -0,0 +1,20 @@
+["replace",
+ "NOBODY",
+ "@ FD ;",
+ "it does not matter, you see \f",
+ "text/plain",
+ "text/xml",
+ "application/octet-stream",
+ "\0"].forEach(type => {
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ assert_equals(frame.contentDocument.open(type), frame.contentDocument);
+ frame.contentDocument.write("<B>heya</b>");
+ frame.contentDocument.close();
+ assert_equals(frame.contentDocument.body.firstChild.localName, "b");
+ assert_equals(frame.contentDocument.body.textContent, "heya");
+ assert_equals(frame.contentDocument.contentType, "text/html");
+ t.done();
+ }, "document.open() with type set to: " + type + " (type argument is supposed to be ignored)");
+});
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/unload.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/unload.window.js
new file mode 100644
index 0000000000..e275a4987a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/unload.window.js
@@ -0,0 +1,19 @@
+// In an earlier version of the HTML Standard, document open steps had "unload
+// document" as a step. Test that this no longer happens.
+
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.src = "/common/blank.html";
+ frame.onload = t.step_func(() => {
+ frame.contentWindow.onpagehide = t.unreached_func("onpagehide got called");
+ frame.contentDocument.onvisibilitychange = t.unreached_func("onvisibilitychange got called");
+ frame.contentWindow.onunload = t.unreached_func("onunload got called");
+ frame.contentDocument.open();
+ t.step_timeout(t.step_func_done(() => {
+ // If none of the three events have been fired by this point, we consider
+ // the test a success. `frame.remove()` above will allow the `load` event
+ // to be fired on the top-level Window, thus unblocking testharness.
+ }), 500);
+ });
+}, "document.open(): Do not fire pagehide, visibilitychange, or unload events");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document-sync-call.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document-sync-call.window.js
new file mode 100644
index 0000000000..f20b4341e3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document-sync-call.window.js
@@ -0,0 +1,13 @@
+for (const methodName of ["open", "write", "writeln"]) {
+ async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => { frame.remove(); });
+ const frameURL = new URL("resources/url-entry-document-incumbent-frame.html", document.URL).href;
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.URL, frameURL);
+ frame.contentWindow.callDocumentMethod(methodName);
+ assert_equals(frame.contentDocument.URL, document.URL);
+ });
+ frame.src = frameURL;
+ }, `document.${methodName}() changes document's URL to the entry global object's associate document's (sync call)`);
+}
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document.window.js
new file mode 100644
index 0000000000..c3a1c3a874
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document.window.js
@@ -0,0 +1,18 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ const frameURL = new URL("resources/url-entry-document-timer-frame.html", document.URL).href;
+ window.timerTest = t.step_func_done(() => {
+ assert_equals(frame.contentDocument.URL, frameURL);
+ assert_equals(frame.contentWindow.location.href, frameURL);
+
+ // In this case, the entry settings object was set when this function is
+ // executed in the timer task through Web IDL's "invoke a callback
+ // function" algorithm, to be the relevant settings object of this
+ // function. Therefore the URL of this document would be inherited.
+ assert_equals(frame.contentDocument.open(), frame.contentDocument);
+ assert_equals(frame.contentDocument.URL, document.URL);
+ assert_equals(frame.contentWindow.location.href, document.URL);
+ });
+ frame.src = frameURL;
+}, "document.open() changes document's URL to the entry settings object's responsible document's (through timeouts)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-fragment.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-fragment.window.js
new file mode 100644
index 0000000000..0c528935b5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-fragment.window.js
@@ -0,0 +1,26 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe")),
+ urlSansHash = document.URL;
+ t.add_cleanup(() => { frame.remove(); });
+ assert_equals(frame.contentDocument.URL, "about:blank");
+ assert_equals(frame.contentWindow.location.href, "about:blank");
+ self.onhashchange = t.step_func_done(() => {
+ frame.contentDocument.open();
+ assert_equals(frame.contentDocument.URL, urlSansHash);
+ assert_equals(frame.contentWindow.location.href, urlSansHash);
+ });
+ self.location.hash = "heya";
+}, "document.open() and document's URL containing a fragment (entry is not relevant)");
+
+window.testDone = undefined;
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"))
+ t.add_cleanup(() => { frame.remove(); });
+ frame.src = "resources/url-frame.html#heya";
+ window.testDone = t.step_func_done((beforeURL, afterURL) => {
+ assert_equals(beforeURL, frame.src);
+ assert_equals(afterURL, frame.src);
+ assert_equals(frame.contentDocument.URL, frame.src);
+ assert_equals(frame.contentWindow.location.href, frame.src);
+ });
+}, "document.open() and document's URL containing a fragment (entry is relevant)");
diff --git a/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window.js b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window.js
new file mode 100644
index 0000000000..4e7c649f45
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url.window.js
@@ -0,0 +1,93 @@
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ assert_equals(frame.contentDocument.URL, "about:blank");
+ assert_equals(frame.contentWindow.location.href, "about:blank");
+ assert_equals(frame.contentDocument.open(), frame.contentDocument);
+ assert_equals(frame.contentDocument.URL, document.URL);
+ assert_equals(frame.contentWindow.location.href, document.URL);
+}, "document.open() changes document's URL (fully active document)");
+
+async_test(t => {
+ const blankURL = new URL("/common/blank.html", document.URL).href;
+ const frameURL = new URL("resources/page-with-frame.html", document.URL).href;
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ frame.onload = t.step_func(() => {
+ assert_equals(frame.contentDocument.URL, frameURL);
+ assert_equals(frame.contentWindow.location.href, frameURL);
+ const childFrame = frame.contentDocument.querySelector("iframe");
+ const childDoc = childFrame.contentDocument;
+ const childWin = childFrame.contentWindow;
+ assert_equals(childDoc.URL, blankURL);
+ assert_equals(childWin.location.href, blankURL);
+
+ // Right now childDoc is still fully active.
+
+ frame.onload = t.step_func_done(() => {
+ // Now childDoc is still active but no longer fully active.
+ assert_equals(childDoc.open(), childDoc);
+ assert_equals(childDoc.URL, blankURL);
+ assert_equals(childWin.location.href, blankURL);
+ });
+ frame.src = "/common/blank.html";
+ });
+ frame.src = frameURL;
+}, "document.open() does not change document's URL (active but not fully active document)");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ const doc = frame.contentDocument;
+
+ // We do not test for win.location.href in this test due to
+ // https://github.com/whatwg/html/issues/3959.
+
+ // Right now the frame is connected and it has an active document.
+ assert_equals(doc.URL, "about:blank");
+
+ frame.remove();
+
+ // Now the frame is no longer connected. Its document is no longer active.
+ assert_equals(doc.URL, "about:blank");
+ assert_equals(doc.open(), doc);
+ assert_equals(doc.URL, "about:blank");
+}, "document.open() does not change document's URL (non-active document with an associated Window object; frame is removed)");
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+
+ // We do not test for win.location.href in this test due to
+ // https://github.com/whatwg/html/issues/3959.
+
+ frame.onload = t.step_func(() => {
+ const doc = frame.contentDocument;
+ // Right now the frame is connected and it has an active document.
+ assert_equals(doc.URL, "about:blank");
+
+ frame.onload = t.step_func_done(() => {
+ // Now even though the frame is still connected, its document is no
+ // longer active.
+ assert_not_equals(frame.contentDocument, doc);
+ assert_equals(doc.URL, "about:blank");
+ assert_equals(doc.open(), doc);
+ assert_equals(doc.URL, "about:blank");
+ });
+
+ frame.src = "/common/blank.html";
+ });
+
+ // We need to connect the frame after the load event is set up to mitigate
+ // against https://crbug.com/569511.
+ document.body.appendChild(frame);
+}, "document.open() does not change document's URL (non-active document with an associated Window object; navigated away)");
+
+test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+ const doc = frame.contentDocument.implementation.createHTMLDocument();
+ assert_equals(doc.URL, "about:blank");
+ assert_equals(doc.open(), doc);
+ assert_equals(doc.URL, "about:blank");
+}, "document.open() does not change document's URL (non-active document without an associated Window object)");
diff --git a/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html
new file mode 100644
index 0000000000..fa153f8f96
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>queueMicrotask() reports the exception from its callback in the callback's global object</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+setup({ allow_uncaught_exception: true });
+
+const onerrorCalls = [];
+window.onerror = () => { onerrorCalls.push("top"); };
+frames[0].onerror = () => { onerrorCalls.push("frame0"); };
+frames[1].onerror = () => { onerrorCalls.push("frame1"); };
+frames[2].onerror = () => { onerrorCalls.push("frame2"); };
+
+async_test(t => {
+ window.onload = t.step_func(() => {
+ frames[0].queueMicrotask(new frames[1].Function(`throw new parent.frames[2].Error("PASS");`));
+
+ t.step_timeout(() => {
+ assert_array_equals(onerrorCalls, ["frame1"]);
+ t.done();
+ }, 4);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js
new file mode 100644
index 0000000000..01f32ac9ba
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js
@@ -0,0 +1,15 @@
+// META: global=window,worker
+"use strict";
+
+setup({
+ allow_uncaught_exception: true
+});
+
+async_test(t => {
+ const error = new Error("boo");
+ self.addEventListener("error", t.step_func_done(ev => {
+ assert_equals(ev.error, error);
+ }));
+
+ queueMicrotask(() => { throw error; });
+}, "It rethrows exceptions");
diff --git a/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.any.js b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.any.js
new file mode 100644
index 0000000000..e67765fade
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.any.js
@@ -0,0 +1,39 @@
+// META: global=window,worker
+"use strict";
+
+test(() => {
+ assert_equals(typeof queueMicrotask, "function");
+}, "It exists and is a function");
+
+test(() => {
+ assert_throws_js(TypeError, () => queueMicrotask(), "no argument");
+ assert_throws_js(TypeError, () => queueMicrotask(undefined), "undefined");
+ assert_throws_js(TypeError, () => queueMicrotask(null), "null");
+ assert_throws_js(TypeError, () => queueMicrotask(0), "0");
+ assert_throws_js(TypeError, () => queueMicrotask({ handleEvent() { } }), "an event handler object");
+ assert_throws_js(TypeError, () => queueMicrotask("window.x = 5;"), "a string");
+}, "It throws when given non-functions");
+
+async_test(t => {
+ let called = false;
+ queueMicrotask(t.step_func_done(() => {
+ called = true;
+ }));
+ assert_false(called);
+}, "It calls the callback asynchronously");
+
+async_test(t => {
+ queueMicrotask(t.step_func_done(function () { // note: intentionally not an arrow function
+ assert_array_equals(arguments, []);
+ }), "x", "y");
+}, "It does not pass any arguments");
+
+async_test(t => {
+ const happenings = [];
+ Promise.resolve().then(() => happenings.push("a"));
+ queueMicrotask(() => happenings.push("b"));
+ Promise.reject().catch(() => happenings.push("c"));
+ queueMicrotask(t.step_func_done(() => {
+ assert_array_equals(happenings, ["a", "b", "c"]);
+ }));
+}, "It interleaves with promises as expected");
diff --git a/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.window.js b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.window.js
new file mode 100644
index 0000000000..78cdcfc5d9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/microtask-queuing/queue-microtask.window.js
@@ -0,0 +1,45 @@
+"use strict";
+
+// This does not work as you expect because mutation observer compound microtasks are confusing.
+// Basically you can only use it once per test.
+function queueMicrotaskViaMO(cb) {
+ const observer = new MutationObserver(cb);
+ const node = document.createTextNode("");
+ observer.observe(node, { characterData: true });
+ node.data = "foo";
+}
+
+// Need to use promise_test to get sequential ordering; otherwise the global mutation observer
+// compound microtask business screws us over.
+
+promise_test(() => {
+ return new Promise(resolve => {
+ const happenings = [];
+
+ queueMicrotaskViaMO(() => happenings.push("x"));
+ queueMicrotask(() => happenings.push("a"));
+
+ queueMicrotask(() => {
+ assert_array_equals(happenings, ["x", "a"]);
+ resolve();
+ });
+ });
+}, "It interleaves with MutationObservers as expected");
+
+promise_test(() => {
+ return new Promise(resolve => {
+ const happenings = [];
+
+ queueMicrotask(() => happenings.push("a"));
+ Promise.reject().catch(() => happenings.push("x"));
+ queueMicrotaskViaMO(() => happenings.push(1));
+ Promise.resolve().then(() => happenings.push("y"));
+ queueMicrotask(() => happenings.push("b"));
+ queueMicrotask(() => happenings.push("c"));
+
+ queueMicrotask(() => {
+ assert_array_equals(happenings, ["a", "x", 1, "y", "b", "c"]);
+ resolve();
+ });
+ });
+}, "It interleaves with MutationObservers and promises together as expected");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js b/testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js
new file mode 100644
index 0000000000..950a8a29ee
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js
@@ -0,0 +1,29 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+
+ frame.onload = t.step_func(() => {
+ // Right now the doc of the iframe inside "frame" is still "fully-active".
+ // Navigate parent away, making the child iframe's doc "active", not "fully-active".
+ frame.contentWindow.location = "/common/blank.html";
+
+ frame.onload = t.step_func(() => {
+ // The child iframe's doc is "active", not "fully-active", and should not receive the storage notification.
+ sessionStorage.setItem('myCat', 'Tom');
+ t.step_timeout(() => {
+ // The child iframe's hasn't received the storage notification.
+ assert_equals(sessionStorage.getItem("Received storage event"), null);
+ frame.contentWindow.history.go(-1);
+ t.step_timeout(() => {
+ // Now The child iframe's doc is "fully-active" again,
+ // the previously not run storage task should now have been run.
+ assert_equals(sessionStorage.getItem("Received storage event"), "true");
+ t.done();
+ }, 1000);
+ }, 1000);
+ });
+ });
+
+ frame.src = "resources/page-with-frame.html";
+}, "Tasks for documents that are not fully active are stored, and run when the documents becomes fully-active");
+
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html
new file mode 100644
index 0000000000..824dbc4b92
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<head>
+<link rel=author title="Aleks Totic" href="mailto:atotic@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/#clean-up-after-running-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+</head>
+<body style="height:2000px;">
+<script>
+/*
+promise 1, promise 2 execute immediately after rAF
+promise 1 child executes immediately after promise 2.
+
+Relevant specs:
+
+https://html.spec.whatwg.org/#clean-up-after-running-script
+If the JavaScript execution context stack is now empty, perform a microtask checkpoint.
+
+https://html.spec.whatwg.org/#perform-a-microtask-checkpoint
+"perform a microtask checkpoint" runs in a loop until all microtasks have been delivered.
+*/
+
+var test = async_test("Microtask execute immediately after script");
+
+window.requestAnimationFrame( function() {
+ var events = [];
+
+ Promise.resolve()
+ .then(function() {
+ events.push("promise 1");
+ return Promise.resolve();
+ })
+ .then(function() {
+ test.step(function() {
+ events.push("promise 1 child");
+ assert_array_equals(events, ["promise 1", "promise 2", "promise 1 child"]);
+ test.done();
+ });
+ });
+ Promise.resolve()
+ .then(function() {
+ events.push("promise 2");
+ });
+
+ // Set up events that must be executed after Promise.
+ window.setTimeout(function() {
+ events.push('timeout');
+ }, 0);
+ window.addEventListener('scroll', function() {
+ events.push('scroll');
+ });
+ window.scrollBy(0,10);
+
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html
new file mode 100644
index 0000000000..799a0de605
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<head>
+<link rel=author title="Aleks Totic" href="mailto:atotic@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/#clean-up-after-running-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+</head>
+<body style="height:2000px;">
+<script>
+/*
+promise 1, promise 2 execute immediately after script tag
+promise 1 child executes immediately after promise 2.
+
+Relevant specs:
+
+https://html.spec.whatwg.org/#clean-up-after-running-script
+If the JavaScript execution context stack is now empty, perform a microtask checkpoint.
+
+https://html.spec.whatwg.org/#perform-a-microtask-checkpoint
+"perform a microtask checkpoint" runs in a loop until all microtasks have been delivered.
+*/
+
+var test = async_test("Microtask immediately after script");
+
+var events = [];
+
+Promise.resolve()
+.then(function() {
+ events.push("promise 1");
+ return Promise.resolve();
+})
+.then(function() {
+ test.step(function() {
+ events.push("promise 1 child");
+ assert_array_equals(events, ["promise 1", "promise 2", "promise 1 child"]);
+ test.done();
+ });
+});
+Promise.resolve()
+.then(function() {
+ events.push("promise 2");
+});
+
+// Set up events that must be executed after Promise.
+window.setTimeout(function() {
+ events.push('timeout');
+}, 0);
+window.addEventListener('scroll', function() {
+ events.push('scroll');
+});
+window.scrollBy(0,10);
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js
new file mode 100644
index 0000000000..e2279f93dd
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js
@@ -0,0 +1,20 @@
+// Helper for tests that just want to verify the ordering of a series of events.
+// Usage:
+// log_test(function(t, log) {
+// log('first');
+// log('second');
+// }, ['first', 'second'], 'Ordinal numbers are ordinal');
+
+function log_test(func, expected, description) {
+ async_test(function(t) {
+ var actual = [];
+ function log(entry) {
+ actual.push(entry);
+ if (expected.length <= actual.length) {
+ assert_array_equals(actual, expected);
+ t.done();
+ }
+ }
+ func(t, t.step_func(log));
+ }, description);
+}
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html
new file mode 100644
index 0000000000..32e4862360
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Childframe</title>
+<script>
+ window.addEventListener('storage', () => {
+ sessionStorage.setItem("Received storage event", true);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html
new file mode 100644
index 0000000000..f13170576e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html
@@ -0,0 +1 @@
+<iframe src="iframe.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html
new file mode 100644
index 0000000000..ed2f70e196
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<title>Task and Microtask Ordering </title>
+<link rel=author title="Joshua Bell" href="mailto:jsbell@google.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#event-loops">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+<style>
+.inner { padding: 46px; width: 0; margin: 0 auto; background: #d4d4d4; }
+.outer { padding: 25px; width: 92px; background: #f1f1f1; }
+</style>
+
+<p>Click on the inner box:</p>
+<div class="outer">
+ <div class="inner"></div>
+</div>
+
+<script>
+
+// Based on: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
+
+log_test(function(t, log) {
+ // Let's get hold of those elements
+ var outer = document.querySelector('.outer');
+ var inner = document.querySelector('.inner');
+
+ // Let's listen for attribute changes on the
+ // outer element
+ new MutationObserver(function() {
+ log('mutate');
+ }).observe(outer, {
+ attributes: true
+ });
+
+ // Here's a click listener...
+ function onClick() {
+ log('click');
+
+ setTimeout(function() {
+ log('timeout');
+ }, 0);
+
+ Promise.resolve().then(function() {
+ log('promise');
+ });
+
+ outer.setAttribute('data-random', Math.random());
+ }
+
+ // ...which we'll attach to both elements
+ inner.addEventListener('click', onClick);
+ outer.addEventListener('click', onClick);
+}, [
+ 'click',
+ 'promise',
+ 'mutate',
+ 'click',
+ 'promise',
+ 'mutate',
+ 'timeout',
+ 'timeout'
+], 'Level 1 bossfight (manual click)');
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html
new file mode 100644
index 0000000000..c14a043b6a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<title>Task and Microtask Ordering </title>
+<link rel=author title="Joshua Bell" href="mailto:jsbell@google.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#event-loops">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+
+<div class="outer">
+ <div class="inner"></div>
+</div>
+
+<script>
+
+// Based on: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
+
+log_test(function(t, log) {
+ log('script start');
+
+ setTimeout(function() {
+ log('setTimeout');
+ }, 0);
+
+ Promise.resolve().then(function() {
+ log('promise1');
+ }).then(function() {
+ log('promise2');
+ });
+
+ log('script end');
+}, [
+ 'script start',
+ 'script end',
+ 'promise1',
+ 'promise2',
+ 'setTimeout'
+], 'Basic task and microtask ordering');
+
+log_test(function(t, log) {
+ // Let's get hold of those elements
+ var outer = document.querySelector('.outer');
+ var inner = document.querySelector('.inner');
+
+ // Let's listen for attribute changes on the
+ // outer element
+ new MutationObserver(function() {
+ log('mutate');
+ }).observe(outer, {
+ attributes: true
+ });
+
+ // Here's a click listener...
+ function onClick() {
+ log('click');
+
+ setTimeout(function() {
+ log('timeout');
+ }, 0);
+
+ Promise.resolve().then(function() {
+ log('promise');
+ });
+
+ outer.setAttribute('data-random', Math.random());
+ }
+
+ // ...which we'll attach to both elements
+ inner.addEventListener('click', onClick);
+ outer.addEventListener('click', onClick);
+
+ // Note that this will behave differently than a real click,
+ // since the dispatch is synchronous and microtasks will not
+ // run between event bubbling steps.
+ inner.click();
+}, [
+ 'click',
+ 'click',
+ 'promise',
+ 'mutate',
+ 'promise',
+ 'timeout',
+ 'timeout'
+], 'Level 1 bossfight (synthetic click)');
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html b/testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html
new file mode 100644
index 0000000000..1e43d1ccd4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>HTMLBodyElement.onload</title>
+<link rel="author" title="Boris Zbarsky" href="mailto:bzbarsky@mit.edu">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("body.onload should set the window.onload handler")
+window.onload = t.step_func(function() {
+ assert_unreached("This handler should be overwritten.")
+})
+var b = document.createElement("body")
+b.onload = t.step_func(function(e) {
+ assert_equals(e.currentTarget, window,
+ "The event should be fired at the window.")
+ t.done()
+})
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html
new file mode 100644
index 0000000000..e31bd2496a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Form's lexical scope is established only for form-associated elements</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#form-associated-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form">
+ <input onclick="window.inputOnClickElements = elements;">
+ <img onclick="window.imgOnClickElements = elements;" alt="img">
+ <div onclick="window.divOnClickElements = elements;">div</div>
+ <x-foo onclick="window.xFooOnClickElements = elements;">x-foo</x-foo>
+</form>
+
+<script>
+"use strict";
+
+window.elements = "global_elements";
+
+test(() => {
+ const input = form.querySelector("input");
+ input.click();
+ assert_equals(window.inputOnClickElements, form.elements);
+}, "<input> has a form owner");
+
+test(() => {
+ const img = form.querySelector("img");
+ img.click();
+ assert_equals(window.imgOnClickElements, form.elements);
+}, "<img> has a form owner");
+
+test(() => {
+ const div = form.querySelector("div");
+ div.click();
+ assert_equals(window.divOnClickElements, window.elements);
+}, "<div> doesn't have a form owner");
+
+test(() => {
+ customElements.define("x-foo", class extends HTMLElement {
+ static formAssociated = true;
+ });
+
+ const xFoo = form.querySelector("x-foo");
+ xFoo.click();
+ assert_equals(window.xFooOnClickElements, form.elements);
+}, "form-associated <x-foo> has a form owner");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html
new file mode 100644
index 0000000000..ed6c006651
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Lexical scopes when compiling an inline event handler</title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#getting-the-current-value-of-the-event-handler">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+setup({allow_uncaught_exception: true});
+</script>
+
+<!-- test case 1: element, document, and window -->
+
+<table>
+ <tr id="test1_outer">
+ <td id="test1_target" onclick="
+window.testResults.complete = typeof(complete);
+window.testResults.cellIndex = typeof(cellIndex);
+window.testResults.cells = typeof(cells);
+window.testResults.domain = typeof(domain);
+window.testResults.print = typeof(print);
+window.testResults.testResults = typeof(testResults);
+window.testResults.target_own_property = typeof(target_own_property);
+window.testResults.inner_own_property = typeof(inner_own_property);
+window.testResults.outer_own_property = typeof(outer_own_property);
+window.testResults.event = typeof(event);
+">
+ <img id="test1_inner">
+ </td>
+ </tr>
+</table>
+
+<script>
+"use strict";
+
+test(() => {
+ const target_element = document.getElementById("test1_target");
+ const inner_element = document.getElementById("test1_inner");
+ const outer_element = document.getElementById("test1_outer");
+
+ target_element.target_own_property = {};
+ inner_element.inner_own_property = {};
+ outer_element.outer_own_property = {};
+
+ const results = window.testResults = {};
+ // Clicking an inner element makes the event target where the event handler is
+ // registered doesn't match the event target that the event is fired. From a
+ // point of view |target_element|, event.target != event.currentTarget.
+ inner_element.click();
+ // Expected scopes are: |target_element|, document, and window.
+ assert_equals(results.complete, "undefined", "HTMLImageElement.prototype.complete");
+ assert_equals(results.cellIndex, "number", "HTMLTableCellElement.prototype.cellIndex");
+ assert_equals(results.cells, "undefined", "HTMLTableRowElement.prototype.cellIndex");
+ assert_equals(results.domain, "string", "Document.prototype.domain");
+ assert_equals(results.print, "function", "window.print");
+ assert_equals(results.testResults, "object");
+ assert_equals(results.target_own_property, "object");
+ assert_equals(results.inner_own_property, "undefined");
+ assert_equals(results.outer_own_property, "undefined");
+ assert_equals(results.event, "object", "The argument of event handler");
+}, "The EventHandler is an element's event handler and has no form owner.");
+</script>
+
+
+<!-- test case 2: element, form owner, document, and window -->
+
+<form id="test2_form_owner" onsubmit="return false;">
+ <!-- 'button' is a form-associated element and has a form owner.
+ https://html.spec.whatwg.org/C/#form-associated-element
+ -->
+ <button id="test2_target" onclick="
+window.testResults.cite = typeof(cite);
+window.testResults.autofocus = typeof(autofocus);
+window.testResults.form = typeof(form);
+window.testResults.encoding = typeof(encoding);
+window.testResults.domain = typeof(domain);
+window.testResults.print = typeof(print);
+window.testResults.testResults = typeof(testResults);
+window.testResults.target_own_property = typeof(target_own_property);
+window.testResults.inner_own_property = typeof(inner_own_property);
+window.testResults.form_owner_own_property = typeof(form_owner_own_property);
+window.testResults.event = typeof(event);
+">
+ <q id="test2_inner"></q>
+ </button>
+</form>
+
+<script>
+"use strict";
+
+test(() => {
+ const target_element = document.getElementById("test2_target");
+ const inner_element = document.getElementById("test2_inner");
+ const form_owner_element = document.getElementById("test2_form_owner");
+
+ target_element.target_own_property = {};
+ inner_element.inner_own_property = {};
+ form_owner_element.form_owner_own_property = {};
+
+ const results = window.testResults = {};
+ // Clicking an inner element makes the event target where the event handler is
+ // registered doesn't match the event target that the event is fired. From a
+ // point of view |target_element|, event.target != event.currentTarget.
+ inner_element.click();
+ // Expected scopes are: |target_element|, form owner, document, and window.
+ assert_equals(results.cite, "undefined", "HTMLQuoteElement.prototype.cite");
+ assert_equals(results.autofocus, "boolean", "HTMLButtonElement.prototype.autofocus");
+ assert_equals(results.form, "object", "HTMLButtonElement.prototype.form");
+ assert_equals(results.encoding, "string", "HTMLFormElement.prototype.encoding");
+ assert_equals(results.domain, "string", "Document.prototype.domain");
+ assert_equals(results.print, "function", "window.print");
+ assert_equals(results.testResults, "object");
+ assert_equals(results.target_own_property, "object");
+ assert_equals(results.inner_own_property, "undefined");
+ assert_equals(results.form_owner_own_property, "object");
+ assert_equals(results.event, "object", "The argument of event handler");
+}, "The EventHandler is an element's event handler and has a form owner.");
+</script>
+
+
+<!-- test case 3: element and window -->
+
+<a id="test3_inner"></a>
+
+<script>
+"use strict";
+
+// This test is placed at last so that it can safely use a global variable
+// without conflicting other tests. Only this test is asynchronous.
+async_test(t => {
+ const target_element = window;
+ const inner_element = document.getElementById("test3_inner");
+
+ target_element.target_own_property = {};
+ inner_element.inner_own_property = {};
+ document.body.body_own_property = {};
+
+ // "onerror" is one of the Window-reflecting body element event handler set.
+ // https://html.spec.whatwg.org/C/#window-reflecting-body-element-event-handler-set
+ // So, the EventHandler is treated as a Window's event handler.
+ document.body.setAttribute("onerror", "\
+window.testResults.ping = typeof(ping); \
+window.testResults.domain = typeof(domain); \
+window.testResults.print = typeof(print); \
+window.testResults.testResults = typeof(testResults); \
+window.testResults.target_own_property = typeof(target_own_property); \
+window.testResults.inner_own_property = typeof(inner_own_property); \
+window.testResults.body_own_property = typeof(body_own_property); \
+window.testResults.event = typeof(event); \
+");
+
+ const results = window.testResults = {};
+ window.addEventListener("error", t.step_func_done(() => {
+ // Expected scopes are: |target_element| and window only.
+ assert_equals(results.domain, "undefined", "Document.prototype.domain");
+ assert_equals(results.print, "function", "window.print");
+ assert_equals(results.testResults, "object");
+ assert_equals(results.target_own_property, "object");
+ assert_equals(results.inner_own_property, "undefined");
+ assert_in_array(results.event, ["object", "string"], "The first argument of onerror event handler");
+ }));
+
+ // Make a compilation error happen in order to invoke onerror event handler.
+ inner_element.setAttribute("onclick", "cause a compilation error");
+ inner_element.click();
+}, "The EventHandler is not an element's event handler (i.e. Window's event handler) and has no form owner.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html
new file mode 100644
index 0000000000..29ac9b8ced
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Entry and incumbent settings objects when compiling an inline event handler</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+window.name = "parent frame";
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/compiled-event-handler-settings-objects-support.html";
+ iframe.onload = t.step_func(() => {
+ const button = iframe.contentDocument.querySelector("button");
+ const compiled = button.onclick;
+
+ assert_equals(compiled.constructor, iframe.contentWindow.Function, "The constructor must be from the iframe");
+ assert_not_equals(compiled.constructor, Function, "The constructor must not be from this page");
+
+ t.done();
+ });
+
+ document.body.appendChild(iframe);
+
+}, "The Function instance must be created in the Realm of the node document");
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/compiled-event-handler-settings-objects-support.html";
+ iframe.onload = t.step_func(() => {
+ const button = iframe.contentDocument.querySelector("button");
+
+ window.onWindowLoaded = t.step_func_done(url => {
+ const pathname = new URL(url).pathname;
+ assert_equals(pathname,
+ "/html/webappapis/scripting/events/resources/open-window.html");
+ // This tests that the entry settings object used to resolve URLs in that window.open() was the same as that
+ // of the node document (i.e. the iframe document), not e.g. this window.
+ });
+
+ button.click();
+ });
+
+ document.body.appendChild(iframe);
+
+}, "The entry settings object while executing the compiled callback via Web IDL's invoke must be that " +
+ "of the node document");
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/compiled-event-handler-settings-objects-support.html";
+ iframe.onload = t.step_func(() => {
+ window.onmessage = t.step_func_done(event => {
+ assert_equals(event.data, "PASS");
+ assert_equals(event.source.name, "iframe");
+ assert_equals(event.source, iframe.contentWindow, "The source must be the iframe");
+ });
+
+ iframe.src = "about:blank";
+ });
+
+ document.body.appendChild(iframe);
+
+}, "The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that " +
+ "of the node document");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html
new file mode 100644
index 0000000000..c840059e68
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="UTF-8" />
+<title>Inline event handler scopes exclude unscopable properties</title>
+<link rel="author" title="ExE Boss" href="https://ExE-Boss.tech" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ "use strict";
+ window.testVariable = {};
+</script>
+
+<!-- test case 1: element, document, and window -->
+<div id="test1_target" onclick='
+ "use strict";
+
+ window.testResults.testVariable = testVariable;
+'></div>
+
+<script>
+ "use strict";
+
+ test(() => {
+ const results = window.testResults = {};
+
+ document[Symbol.unscopables].testVariable = true;
+ document.testVariable = "FAIL (document)";
+
+ document.getElementById("test1_target").click();
+ assert_equals(results.testVariable, window.testVariable);
+ }, "unscopable `document.testVariable` doesn't shadow `window.testVariable`");
+
+ test(() => {
+ const results = window.testResults = {};
+ const element = document.getElementById("test1_target");
+
+ element[Symbol.unscopables].testVariable = true;
+ element.testVariable = "FAIL (element)";
+
+ element.click();
+ assert_equals(results.testVariable, window.testVariable);
+ }, "unscopable `element.testVariable` doesn't shadow `window.testVariable`");
+</script>
+
+<!-- test case 2: element, form owner, document, and window -->
+<form id="test2_form_owner" onsubmit="return false;">
+ <!-- <button> is a form-associated element and has a form owner.
+ https://html.spec.whatwg.org/C/#form-associated-element -->
+ <button id="test2_target" onclick='
+ "use strict";
+
+ window.testResults.testVariable = testVariable;
+ '></button>
+</form>
+
+<script>
+ "use strict";
+
+ test(() => {
+ const results = window.testResults = {};
+ const element = document.getElementById("test2_target");
+ const formOwner = document.getElementById("test2_form_owner");
+
+ formOwner[Symbol.unscopables].testVariable = true;
+ formOwner.testVariable = "FAIL (formOwner)";
+
+ element.click();
+ assert_equals(results.testVariable, window.testVariable);
+ }, "unscopable `formOwner.testVariable` doesn't shadow `window.testVariable`")
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm b/testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm
new file mode 100644
index 0000000000..2331fa17ee
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML contextmenu event is a MouseEvent</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>#contextmenutarget { width: 100px; height: 100px; background-color: red; }</style>
+ </head>
+ <body>
+ <div id='contextmenutarget'>Trigger context menu in this box.</div>
+ <div id="log"></div>
+ <script type="text/javascript">
+var t = async_test('contextmenu event generated from user action is MouseEvent');
+document.querySelector("#contextmenutarget").addEventListener('contextmenu', t.step_func(function (e) {
+ assert_equals(e.constructor, window.MouseEvent);
+ document.querySelector("#contextmenutarget").style.backgroundColor = "green";
+ t.done();
+}));
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html
new file mode 100644
index 0000000000..ee8c34ced3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<title>GlobalEventHandlers</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#globaleventhandlers">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-idl-attributes">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-content-attributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+
+<script>
+"use strict";
+
+// The prefixed animation events are special; their event types are
+// camel-case.
+const prefixedAnimationAttributeToEventType = new Map([
+ ["webkitanimationend", "webkitAnimationEnd"],
+ ["webkitanimationiteration", "webkitAnimationIteration"],
+ ["webkitanimationstart", "webkitAnimationStart"],
+ ["webkittransitionend", "webkitTransitionEnd"],
+]);
+
+setup({ explicit_done: true });
+
+fetch("/interfaces/html.idl").then(res => res.text()).then(htmlIDL => {
+ const parsedHTMLIDL = WebIDL2.parse(htmlIDL);
+ const globalEventHandlers = parsedHTMLIDL.find(idl => idl.name === "GlobalEventHandlers");
+
+ // onerror is too special
+ const names = globalEventHandlers.members.map(member => member.name).filter(name => name !== "onerror");
+
+ for (const name of names) {
+ const withoutOn = name.substring(2);
+
+ test(() => {
+ for (const location of [window, HTMLElement.prototype, SVGElement.prototype, Document.prototype]) {
+ assert_true(location.hasOwnProperty(name),
+ `${location.constructor.name} has an own property named "${name}"`);
+ }
+ assert_false(name in Element.prototype, `Element.prototype must not contain a "${name}" property`);
+ }, `${name}: must be on the appropriate locations for GlobalEventHandlers`);
+
+ test(() => {
+ const htmlElement = document.createElement("span");
+ const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "g");
+
+ for (var location of [window, htmlElement, svgElement, document]) {
+ assert_equals(location[name], null,
+ `The default value of the property is null for a ${location.constructor.name} instance`);
+ }
+ }, `${name}: the default value must be null`);
+
+ test(() => {
+ const el = document.createElement("div");
+ el.setAttribute(name, `window.${name}Happened = true;`);
+ const compiledHandler = el[name];
+
+ assert_equals(typeof compiledHandler, "function", `The ${name} property must be a function`);
+ compiledHandler();
+ assert_true(window[name + "Happened"], "Calling the handler must run the code");
+ }, `${name}: the content attribute must be compiled into a function as the corresponding property`);
+
+ test(() => {
+ const el = document.createElement("div");
+ el.setAttribute(name, `window.${name}Happened2 = true;`);
+
+ let eventType = withoutOn;
+ if (prefixedAnimationAttributeToEventType.has(eventType)) {
+ eventType = prefixedAnimationAttributeToEventType.get(eventType);
+ }
+ el.dispatchEvent(new Event(eventType));
+
+ assert_true(window[name + "Happened2"], "Dispatching an event must run the code");
+ }, `${name}: the content attribute must execute when an event is dispatched`);
+
+ test(() => {
+ const element = document.createElement("meta");
+ element[name] = e => {
+ assert_equals(e.currentTarget, element, "The event must be fired at the <meta> element");
+ };
+
+ element.dispatchEvent(new Event(withoutOn));
+ }, `${name}: dispatching an Event at a <meta> element must trigger element.${name}`);
+ }
+
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html
new file mode 100644
index 0000000000..e8055d99f3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>HTMLBodyElement event handlers</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="resources/event-handler-body.js"></script>
+<div id="log"></div>
+<body>
+<script>
+setup({ explicit_done: true });
+
+handlersListPromise.then(({ shadowedHandlers, notShadowedHandlers }) => {
+ eventHandlerTest(shadowedHandlers, notShadowedHandlers, "body");
+
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
new file mode 100644
index 0000000000..b583eca52d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>event handlers</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="resources/event-handler-body.js"></script>
+<script>
+setup({ explicit_done: true });
+
+handlersListPromise.then(({ shadowedHandlers, notShadowedHandlers }) => {
+ eventHandlerTest(shadowedHandlers, notShadowedHandlers, "frameset");
+
+ // The testharness framework appends test results to document.body,
+ // show test results in frame after test done.
+ add_completion_callback(() => {
+ const log_elem = document.getElementById("log");
+ const frame_elem = document.querySelector("frame");
+ if (log_elem) {
+ frame_elem.contentDocument.body.innerHTML = log_elem.innerHTML;
+ }
+ });
+
+ done();
+});
+</script>
+<frameset>
+ <frame src="/common/blank.html" />
+</frameset>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html
new file mode 100644
index 0000000000..9b81d42ff7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="resources/event-handler-body.js"></script>
+<script>
+setup({ explicit_done: true });
+const elements = ['body', 'frameset'];
+handlersListPromise.then(({ shadowedHandlers, notShadowedHandlers }) => {
+ elements.forEach(function (elementName) {
+ shadowedHandlers.forEach(function (eventName) {
+ var handlerName = "on" + eventName;
+
+ test(function() {
+ var windowHandler = function () { return "Handler attached to the window"; };
+ window[handlerName] = windowHandler;
+
+ var d = (new DOMParser).parseFromString('', 'text/html');
+ var b = d.createElement(elementName);
+
+ assert_equals(b[handlerName], null);
+
+ window[handlerName] = null;
+ }, "Return null when getting the " + eventName + " event handler of a windowless " + elementName);
+
+ test(function() {
+ var windowHandler = function () { return "Handler attached to the window"; };
+ window[handlerName] = windowHandler;
+
+ var d = (new DOMParser).parseFromString('', 'text/html');
+ var b = d.createElement(elementName);
+ b[handlerName] = function() { return "Handler attached to windowless element"; };
+
+ assert_equals(window[handlerName], windowHandler);
+ assert_equals(b[handlerName], null);
+
+ // Clean up window event handler
+ window[handlerName] = null;
+ }, "Ignore setting of " + eventName + " window event handlers on windowless " + elementName);
+ });
+
+ notShadowedHandlers.forEach(function (eventName) {
+ var handlerName = "on" + eventName;
+
+ test(function() {
+ var windowHandler = function () { return "Handler attached to the window"; };
+ window[handlerName] = windowHandler;
+
+ var d = (new DOMParser).parseFromString('', 'text/html');
+ var b = d.createElement(elementName);
+
+ assert_equals(b[handlerName], null);
+
+ var elementHandler = function () { return "Handler attached to the element"; };
+ b[handlerName] = elementHandler;
+
+ assert_equals(window[handlerName], windowHandler);
+ assert_equals(b[handlerName], elementHandler);
+
+ // Clean up window event handler
+ window[handlerName] = null;
+ }, eventName + " is unaffected on a windowless " + elementName);
+ });
+ });
+
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html
new file mode 100644
index 0000000000..8039bac7ad
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>"handleEvent" property of EventHandler should be ignored</title>
+<link rel="help" href="https://html.spec.whatwg.org/#eventhandler">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+
+test(t => {
+ const handler = Object.create(null, {
+ handleEvent: {
+ get: t.unreached_func('"handleEvent" property should not be looked up'),
+ },
+ });
+
+ const el = document.createElement("div");
+ el.onmouseenter = handler;
+ el.dispatchEvent(new MouseEvent("mouseenter"));
+}, 'plain object "mouseenter" handler');
+
+async_test(t => {
+ const handler = Object.create(Function.prototype, {
+ handleEvent: {
+ get: t.unreached_func('"handleEvent" property should not be looked up'),
+ },
+ });
+ assert_true(handler instanceof Function);
+
+ window.onmessage = handler;
+ window.postMessage({}, "*");
+
+ step_timeout(() => {
+ t.done();
+ }, 50);
+}, 'non-callable "message" handler that is instance of Function');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html
new file mode 100644
index 0000000000..657a37839d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Event handler with labels</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body onload="javascript:
+ for (var i = 0; i < 2; ++i) {
+ for (var j = 0; j < 2; ++j) {
+ t.step(function() {
+ assert_equals(i, 0);
+ assert_equals(j, 0);
+ });
+ break javascript;
+ }
+ }
+ t.done();
+">
+<div id="log"></div>
+<script>
+var t = async_test("Event handlers starting with 'javascript:' should treat that as a label.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html
new file mode 100644
index 0000000000..e4454a20a1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Window Object 'onmove'</title>
+<link rel="author" title="Sonja Laurila" href="mailto:laurila@google.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onmove">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script>
+promise_test(async t => {
+ var onMoveCalled = false;
+ window.onmove = (event) => { onMoveCalled = true; }
+ window.dispatchEvent(new Event('move'));
+ assert_true(onMoveCalled, "The onMove handler has been executed.");
+}, "Window move event");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html
new file mode 100644
index 0000000000..a751f39d30
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Window Object 'onmove'</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onmove">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script>
+promise_test((t) => {
+ var expectedRect = {'x': 100, 'y': 200};
+ return new Promise(resolve => {
+ window.addEventListener("move", resolve);
+ test_driver.set_window_rect(expectedRect);
+ }).then(event => {
+ return test_driver.get_window_rect()
+ .then(rect => {
+ assert_equals(rect.x, expectedRect.x, "The window rect X is correct.")
+ assert_equals(rect.y, expectedRect.y, "The window rect Y is correct.")
+ });
+ });
+}, "Window move event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html
new file mode 100644
index 0000000000..0e44e7272f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>HTMLBodyElement.onresize</title>
+<link rel="author" title="His-Name-Is-Joof" href="mailto:jeffrharrison@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onresize">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("body.onresize should set the window.onresize handler")
+window.onresize = t.step_func(function() {
+ assert_unreached("This handler should be overwritten.")
+})
+
+var body = document.createElement("body")
+body.onresize = t.step_func(function(e) {
+ assert_equals(e.currentTarget, window,
+ "The event should be fired at the window.")
+ t.done()
+})
+window.dispatchEvent(new Event('resize'));
+
+t = async_test("document.onresize should set the document.onresize handler");
+document.onresize = t.step_func(function(e) {
+ assert_equals(e.currentTarget, document,
+ "The event should be fired at the document")
+ t.done()
+})
+document.dispatchEvent(new Event('resize'));
+
+t = async_test("meta.onresize should set the meta.onresize handler");
+var meta = document.createElement("meta")
+meta.onresize = t.step_func(function(e) {
+ assert_equals(e.currentTarget, meta,
+ "The event should be fired at the <meta> object")
+ t.done()
+})
+meta.dispatchEvent(new Event('resize'));
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html
new file mode 100644
index 0000000000..9ab0020ec3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.body.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ document.body.dispatchEvent(new ErrorEvent("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ document.body.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ document.body.dispatchEvent(new ErrorEvent("error", {
+ bubbles: true,
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on Window, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html
new file mode 100644
index 0000000000..9ed2638416
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.body.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ document.body.dispatchEvent(new Event("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Window, with a synthetic Event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html
new file mode 100644
index 0000000000..4165beaf63
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, document, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ assert_equals(e.message, "");
+ assert_equals(e.filename, "");
+ assert_equals(e.lineno, 0);
+ assert_equals(e.colno, 0);
+ assert_equals(e.error, undefined);
+ });
+
+ document.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Document, with a synthetic ErrorEvent");
+
+test(() => {
+ const e = new ErrorEvent("error");
+ assert_equals(e.message, "");
+ assert_equals(e.filename, "");
+ assert_equals(e.lineno, 0);
+ assert_equals(e.colno, 0);
+ assert_equals(e.error, undefined);
+}, "Initial values of ErrorEvent members")
+
+test(() => {
+ const e = new ErrorEvent("error", {error : null});
+ assert_equals(e.error, null);
+}, "error member can be set to null")
+
+test(() => {
+ const e = new ErrorEvent("error", {error : undefined});
+ assert_equals(e.error, undefined);
+}, "error member can be set to undefined")
+
+test(() => {
+ const e = new ErrorEvent("error", {error : "foo"});
+ assert_equals(e.error, "foo");
+}, "error member can be set to arbitrary")
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html
new file mode 100644
index 0000000000..6cf44e9d35
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, document, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ document.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Document, with a synthetic Event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html
new file mode 100644
index 0000000000..20d87dbacf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<iframe name="framesetWindow" src="resources/frameset-frame.html"></iframe>
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+window.onload = () => {
+
+const frameset = framesetWindow.document.querySelector("frameset");
+
+promise_test(t => {
+ frameset.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, framesetWindow, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ frameset.dispatchEvent(new ErrorEvent("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ frameset.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, framesetWindow, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ frameset.dispatchEvent(new ErrorEvent("error", {
+ bubbles: true,
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on Window, with a synthetic ErrorEvent");
+
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html
new file mode 100644
index 0000000000..2fdca3ad86
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<iframe name="framesetWindow" src="resources/frameset-frame.html"></iframe>
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+window.onload = () => {
+
+const frameset = framesetWindow.document.querySelector("frameset");
+
+promise_test(t => {
+ frameset.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, framesetWindow, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ frameset.dispatchEvent(new Event("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Window, with a synthetic Event");
+
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html
new file mode 100644
index 0000000000..028be4919e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<frameset></frameset>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js
new file mode 100644
index 0000000000..3918c74e44
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js
@@ -0,0 +1 @@
+"use strict";
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js
new file mode 100644
index 0000000000..dc9a0dbf4a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js
@@ -0,0 +1 @@
+< 3;
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html
new file mode 100644
index 0000000000..f3ef1165e0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ const script = document.createElement("script");
+ script.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, script, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.constructor, Event); // not ErrorEvent
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ script.src = "404.js";
+ document.body.appendChild(script);
+
+ return promise;
+}, "error event behaves normally (return true does not cancel; one arg) on a script element, with a 404 error");
+
+promise_test(t => {
+ const script = document.createElement("script");
+ script.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, script, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ script.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event behaves normally (return true does not cancel; one arg) on a script element, with a synthetic Event");
+
+promise_test(t => {
+ const script = document.createElement("script");
+ script.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, script, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ script.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event behaves normally (return true does not cancel; one arg) on a script element, with a synthetic ErrorEvent");
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html
new file mode 100644
index 0000000000..75a1772485
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: click events using ErrorEvent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+promise_test(t => {
+ document.onclick = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, document, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ document.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on Document, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ window.onclick = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ window.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const el = document.createElement("script");
+ el.onclick = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, el, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ el.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on a script element, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const worker = new Worker("resources/no-op-worker.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ worker.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on Worker, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js
new file mode 100644
index 0000000000..177a99e2ce
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js
@@ -0,0 +1,22 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ self.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on WorkerGlobalScope, with a synthetic ErrorEvent");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html
new file mode 100644
index 0000000000..1b387ca81c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ window.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a runtime error");
+
+promise_test(t => {
+ window.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(typeof message, "string", "message argument must be a string");
+ assert_equals(typeof filename, "string", "filename argument must be a string");
+ assert_equals(typeof lineno, "number", "lineno argument must be a number");
+ assert_equals(typeof colno, "number", "colno argument must be a number");
+ assert_equals(typeof error, "object", "error argument must be an object");
+ assert_equals(error.constructor, ReferenceError, "error argument must be a ReferenceError");
+ return true;
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ return eventWatcher.wait_for("error");
+}, "error event has the right 5 args on Window, with a runtime error");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html
new file mode 100644
index 0000000000..2d62d8a204
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ window.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ window.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ window.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ window.dispatchEvent(new ErrorEvent("error", {
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on Window, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html
new file mode 100644
index 0000000000..0bcc7defb7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ window.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ window.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Window, with a synthetic Event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html
new file mode 100644
index 0000000000..a8c0d97ce2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ const worker = new Worker("resources/worker-with-syntax-error.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Worker, with a syntax error in the worker code");
+
+promise_test(t => {
+ const worker = new Worker("resources/no-op-worker.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ worker.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Worker, with a synthetic Event");
+
+promise_test(t => {
+ const worker = new Worker("resources/no-op-worker.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ worker.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Worker, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js
new file mode 100644
index 0000000000..264fef810d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js
@@ -0,0 +1,40 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on WorkerGlobalScope, with a runtime error");
+
+promise_test(t => {
+ self.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(typeof message, "string", "message argument must be a string");
+ assert_equals(typeof filename, "string", "filename argument must be a string");
+ assert_equals(typeof lineno, "number", "lineno argument must be a number");
+ assert_equals(typeof colno, "number", "colno argument must be a number");
+ assert_equals(typeof error, "object", "error argument must be an object");
+ assert_equals(error.constructor, ReferenceError, "error argument must be a ReferenceError");
+ return true;
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ return eventWatcher.wait_for("error");
+}, "error event has the right 5 args on WorkerGlobalScope, with a runtime error");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js
new file mode 100644
index 0000000000..a14f6e01a9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js
@@ -0,0 +1,49 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ self.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on WorkerGlobalScope, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ self.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ self.dispatchEvent(new ErrorEvent("error", {
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on WorkerGlobalScope, with a synthetic ErrorEvent");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js
new file mode 100644
index 0000000000..a3e16ded88
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js
@@ -0,0 +1,22 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ self.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on WorkerGlobalScope, with a synthetic Event");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html
new file mode 100644
index 0000000000..205e876c1d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: manual tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<style>
+ div[id^="d"] {
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ }
+</style>
+
+<div id="log"></div>
+
+<p>Mouseover these four divs</p>
+
+<div id="d1"></div>
+<div id="d2"></div>
+
+<div id="d3" onmouseover="return false"></div>
+<div id="d4" onmouseover="return true"></div>
+
+<script>
+"use strict";
+async_test(t => {
+ const div = document.querySelector("#d1");
+
+ div.onmouseover = t.step_func(() => false);
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, true);
+ }));
+}, "Listener added via JavaScript, returns false: cancels the event");
+
+async_test(t => {
+ const div = document.querySelector("#d2");
+
+ div.onmouseover = t.step_func(() => true);
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, false);
+ }));
+}, "Listener added via JavaScript, returns true: does not cancel the event");
+
+async_test(t => {
+ const div = document.querySelector("#d3");
+
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, true);
+ }));
+}, "Listener added via markup, returns false: cancels the event");
+
+async_test(t => {
+ const div = document.querySelector("#d4");
+
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, false);
+ }));
+}, "Listener added via markup, returns true: does not cancel the event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html
new file mode 100644
index 0000000000..f5423d7ed4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<title>Event handlers processing algorithm</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+ <body>
+ <div id="foo" style="width: 100px; height: 100px; background-color: black"></div>
+ <script>
+
+ // Historically mouseover was special in the spec, but now it is not. See https://github.com/whatwg/html/pull/2398.
+ test(function(t) {
+ var ev = new Event('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return false });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true)
+ }, "mouseover listener returning false cancels event (using Event)");
+
+ test(function(t) {
+ var ev = new MouseEvent('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return false });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true)
+ }, "mouseover listener returning false cancels event (using MouseEvent)");
+
+ test(function(t) {
+ var ev = new Event('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return true });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, false)
+ }, "mouseover listener returning true doesn't cancel event (using Event)");
+
+ test(function(t) {
+ var ev = new MouseEvent('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return true });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, false)
+ }, "mouseover listener returning true doesn't cancel event (using MouseEvent)");
+
+ // beforeunload is tested in html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
+
+ test(function(t) {
+ var ev = new Event("click", {cancelable: true});
+ document.getElementById("foo").onclick = t.step_func(function() { return false; });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true);
+ }, "click listener returning false cancels event");
+
+ test(function(t) {
+ var ev = new Event("blur", {cancelable: true});
+ document.getElementById("foo").onblur = t.step_func(function() { return false; });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true);
+ }, "blur listener returning false cancels event");
+
+ test(function(t) {
+ var ev = new Event("dblclick", {cancelable: true});
+ document.getElementById("foo").ondblclick = t.step_func(function() { return false; });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true);
+ }, "dblclick listener returning false cancels event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js
new file mode 100644
index 0000000000..a20e2ec1d2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js
@@ -0,0 +1,76 @@
+let firstEventHandler;
+
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.onclick = null; // but de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 4); }); // and re-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 5);
+}, "Event handler set through content attribute should be removed when they are set to null.");
+
+let happened = 0;
+test(() => {
+ var script = "happened++;";
+ var button = document.createElement('button');
+ button.setAttribute('onclick', script); // event handler is activated here
+ button.onclick = null; // but de-activated here
+ assert_equals(button.getAttribute("onclick"), script)
+ button.setAttribute('onclick', script); // and re-activated here
+ button.click()
+ assert_equals(happened, 1);
+}, "Event handler set through content attribute should be re-activated even if content is the same.");
+
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.removeAttribute('onclick'); // but de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 4); }); // and re-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(i, 5);
+}, "Event handler set through content attribute should be deactivated when the content attribute is removed.");
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.onclick = t.unreached_func('First event handler.'); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.onclick = null; // but de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 4); }); // and re-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(i, 5);
+}, "Event handler set through IDL should be deactivated when the IDL attribute is set to null.");
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.onclick = t.unreached_func('First event handler.'); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.removeAttribute('onclick'); // and NOT de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 2); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(i, 5);
+}, "Event handler set through IDL should NOT be deactivated when the content attribute is removed.");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html
new file mode 100644
index 0000000000..57555faa7b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test the sourceText of event handlers</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/5500">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+test(() => {
+ const el = document.createElement("div");
+ el.setAttribute("onclick", "foo");
+ assert_equals(el.onclick.toString(), "function onclick(event) {\nfoo\n}");
+}, "non-error event handler");
+
+test(() => {
+ const el = document.createElement("div");
+ el.setAttribute("onerror", "foo");
+ assert_equals(el.onerror.toString(), "function onerror(event) {\nfoo\n}");
+}, "error event handler not on body");
+
+test(() => {
+ const el = document.createElement("body");
+ el.setAttribute("onerror", "foo");
+ assert_equals(el.onerror.toString(), "function onerror(event, source, lineno, colno, error) {\nfoo\n}");
+}, "error event handler on disconnected body");
+
+test(() => {
+ const el = document.createElement("frameset");
+ el.setAttribute("onerror", "foo");
+ assert_equals(el.onerror.toString(), "function onerror(event, source, lineno, colno, error) {\nfoo\n}");
+}, "error event handler on disconnected frameset");
+
+test(() => {
+ document.body.setAttribute("onerror", "foo");
+ assert_equals(window.onerror.toString(), "function onerror(event, source, lineno, colno, error) {\nfoo\n}");
+}, "error event handler on connected body, reflected to Window");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js
new file mode 100644
index 0000000000..abf46882aa
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js
@@ -0,0 +1,55 @@
+var objects = [{}, function() {}, new Number(42), new String()];
+var primitives = [42, null, undefined, ""];
+var firstEventHandler;
+objects.forEach(function(object) {
+ test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.onclick = object; // event handler listener is registered here
+ assert_equals(button.onclick, object);
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.setAttribute('onclick', uncalled);
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 1); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 4);
+ }, "Event handler listeners should be registered when they are first set to an object value " +
+ "(" + format_value(object) + ").");
+});
+primitives.forEach(function(primitive) {
+ test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.onclick = primitive;
+ assert_equals(button.onclick, null);
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler listener is registered here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 2); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 4);
+ }, "Event handler listeners should be registered when they are first set to an object value " +
+ "(" + format_value(primitive) + ").");
+});
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler listener is registered here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 2); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 4);
+}, "Event handler listeners should be registered when they are first set to an object value.");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html b/testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html
new file mode 100644
index 0000000000..6be581fa24
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<!-- A window to work with that won't trigger the harness exception detection
+ when we fire "error" events at it -->
+<iframe style="display: none"></iframe>
+<script>
+ test(function() {
+ var blob = new Blob([""]);
+ // Most targets disabled for now until
+ // https://github.com/whatwg/html/issues/2296 is sorted out.
+ var targets = [ frames[0] /*, document, document.documentElement,
+ new Worker(URL.createObjectURL(blob) */ ];
+ // Event constructors also mostly disabled until
+ // https://github.com/whatwg/html/issues/2296 is sorted out.
+ var eventCtors = [ /* Event, */ ErrorEvent /*, MouseEvent */ ];
+ var values = [true, false, "", "abc", {}, 0, 1, -1, null, undefined,
+ 2.5, NaN, Infinity, Symbol.toStringTag ];
+ // Event types also mostly disabled pending
+ // https://github.com/whatwg/html/issues/2296
+ var eventTypes = [ "error"/*, "click", "load"*/ ];
+
+ // Variables that keep track of which subtest we're running.
+ var curTarget;
+ var curValue;
+ var curCtor;
+ var curType;
+
+ function defaultPreventedTester(event) {
+ var expectedValue;
+ if (curTarget === frames[0] &&
+ curCtor === ErrorEvent &&
+ curValue === true &&
+ curType == "error") {
+ expectedValue = true;
+ } else {
+ // This will need adjusting once we allow more targets and event
+ // constructors above!
+ expectedValue = false;
+ }
+ var valueRepr;
+ if (typeof curValue == "string") {
+ valueRepr = '"' + curValue + '"';
+ } else {
+ valueRepr = String(curValue);
+ }
+ test(function() {
+ assert_equals(event.defaultPrevented, expectedValue);
+ }, "Returning " + valueRepr +
+ " from " + String(curTarget) + "'s on" + curType +
+ " event handler while " + curCtor.name +
+ " is firing should" +
+ (expectedValue ? "" : " not") +
+ " cancel the event");
+ }
+
+ for (curCtor of eventCtors) {
+ for (curTarget of targets) {
+ for (curType of eventTypes) {
+ for (curValue of values) {
+ // We have to make sure that defaultPreventedTester is added after
+ // our event handler.
+ curTarget["on" + curType] = function() { return curValue; }
+ curTarget.addEventListener(curType, defaultPreventedTester);
+ var e = new curCtor(curType, { cancelable: true });
+ curTarget.dispatchEvent(e);
+ curTarget["on" + curType] = null;
+ curTarget.removeEventListener(curType, defaultPreventedTester);
+ }
+ }
+ }
+ }
+ }, "event handler cancellation behavior");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html b/testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html
new file mode 100644
index 0000000000..aae0f1abf8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Inline event handlers retain their ordering even when invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+setup({ allow_uncaught_exception: true });
+var events = [];
+
+test(function() {
+ events = [];
+ var e = document.createElement("div");
+ document.body.appendChild(e);
+ e.addEventListener("click", function() { events.push("ONE") });
+ e.setAttribute("onclick", "window.open(");
+ e.addEventListener("click", function() { events.push("THREE") });
+ // Try to compile the event handler.
+ e.onclick;
+ e.setAttribute("onclick", "events.push('TWO')");
+ e.dispatchEvent(new Event("click"));
+ var expected_events = ["ONE", "TWO", "THREE"];
+ assert_array_equals(events, expected_events);
+}, "Inline event handlers retain their ordering when invalid and force-compiled");
+
+test(function() {
+ events = [];
+ var e = document.createElement("div");
+ document.body.appendChild(e);
+ e.addEventListener("click", function() { events.push("ONE") });
+ e.setAttribute("onclick", "window.open(");
+ e.addEventListener("click", function() { events.push("THREE") });
+ e.dispatchEvent(new Event("click"));
+ e.setAttribute("onclick", "events.push('TWO')");
+ e.dispatchEvent(new Event("click"));
+ var expected_events = ["ONE", "THREE", "ONE", "TWO", "THREE"];
+ assert_array_equals(events, expected_events);
+}, "Inline event handlers retain their ordering when invalid and force-compiled via dispatch");
+
+test(function() {
+ events = [];
+ var e = document.createElement("div");
+ document.body.appendChild(e);
+ e.addEventListener("click", function() { events.push("ONE") });
+ e.setAttribute("onclick", "window.open(");
+ e.addEventListener("click", function() { events.push("THREE") });
+ e.setAttribute("onclick", "events.push('TWO')");
+ e.dispatchEvent(new Event("click"));
+ var expected_events = ["ONE", "TWO", "THREE"];
+ assert_array_equals(events, expected_events);
+}, "Inline event handlers retain their ordering when invalid and lazy-compiled");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js
new file mode 100644
index 0000000000..2892a4c3ab
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js
@@ -0,0 +1,16 @@
+setup({ allow_uncaught_exception: true });
+
+test(function() {
+ var events = [];
+ window.onerror = function() {
+ events.push("error");
+ };
+
+ var div = document.createElement("div");
+ div.addEventListener("click", function (e) { events.push("click 1") });
+ div.setAttribute("onclick", "}");
+ div.addEventListener("click", function (e) { events.push("click 2") });
+ div.dispatchEvent(new Event("click"));
+ assert_equals(div.onclick, null);
+ assert_array_equals(events, ["click 1", "error", "click 2"]);
+}, "Invalid uncompiled raw handlers should only be compiled when about to call them");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js
new file mode 100644
index 0000000000..b39b54b0e9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js
@@ -0,0 +1,14 @@
+setup({ allow_uncaught_exception: true });
+
+var errors = 0;
+window.onerror = function() {
+ errors++;
+};
+
+test(function() {
+ var e = document.body;
+ e.setAttribute("onclick", "window.open(");
+ assert_equals(e.onclick, null);
+ assert_equals(e.onclick, null);
+ assert_equals(errors, 1);
+}, "Invalid uncompiled raw handlers should only be compiled once");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js
new file mode 100644
index 0000000000..f9443bf99a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js
@@ -0,0 +1,20 @@
+setup({ allow_uncaught_exception: true });
+
+test(function() {
+ var events = [];
+ window.onerror = function() {
+ events.push("error");
+ };
+
+ var div = document.createElement("div");
+ div.addEventListener("click", function (e) { events.push("click 1"); });
+ div.setAttribute("onclick", "}");
+ div.addEventListener("click", function (e) { events.push("click 3"); });
+ assert_equals(div.onclick, null);
+ assert_array_equals(events, ["error"]);
+
+ events = [];
+ div.onclick = function (e) { events.push("click 2"); };
+ div.dispatchEvent(new Event("click"));
+ assert_array_equals(events, ["click 1", "click 2", "click 3"]);
+}, "Compiling invalid uncompiled raw handlers should keep the position in event listener list");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html b/testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html
new file mode 100644
index 0000000000..2d62019dbf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<title>MessageEvent constructor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+test(function() {
+ assert_throws_js(
+ TypeError,
+ () => MessageEvent(""),
+ "Calling MessageEvent constructor without 'new' must throw"
+ );
+}, "MessageEvent constructor called as normal function");
+
+test(function() {
+ var ev = new MessageEvent("test")
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.target, null, "target attribute")
+ assert_equals(ev.currentTarget, null, "currentTarget attribute")
+ assert_equals(ev.eventPhase, Event.NONE, "eventPhase attribute")
+ assert_equals(ev.bubbles, false, "bubbles attribute")
+ assert_equals(ev.cancelable, false, "cancelable attribute")
+ assert_equals(ev.defaultPrevented, false, "defaultPrevented attribute")
+ assert_equals(ev.isTrusted, false, "isTrusted attribute")
+ assert_true(ev.timeStamp > 0, "timeStamp attribute")
+ assert_true("initMessageEvent" in ev, "initMessageEvent operation")
+ assert_equals(ev.data, null, "data attribute")
+ assert_equals(ev.origin, "", "origin attribute")
+ assert_equals(ev.lastEventId, "", "lastEventId attribute")
+ assert_equals(ev.source, null, "source attribute")
+ assert_array_equals(ev.ports, [], "ports attribute")
+}, "Default event values")
+
+test(function() {
+ var channel = new MessageChannel()
+ var ev = new MessageEvent("test", { data: "testData", origin: "testOrigin", lastEventId: "testId", source: window, ports: [channel.port1] })
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.data, "testData", "data attribute")
+ assert_equals(ev.origin, "testOrigin", "origin attribute")
+ assert_equals(ev.lastEventId, "testId", "lastEventId attribute")
+ assert_equals(ev.source, window, "source attribute")
+ assert_array_equals(ev.ports, [channel.port1], "ports attribute")
+}, "MessageEventInit dictionary")
+
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new MessageEvent("test", { ports: null })
+ })
+}, "Passing null for ports member")
+
+test(function() {
+ var ev = new MessageEvent("test", { ports: [] })
+ assert_true(Array.isArray(ev.ports), "Array.isArray() should return true")
+ assert_true(Object.isFrozen(ev.ports), "Object.isFrozen() should return true")
+ assert_equals(ev.ports, ev.ports, "ev.ports should return the same object")
+
+ const oldPorts = ev.ports;
+ ev.initMessageEvent("test", false, false, null, "", "", null, ev.ports);
+ assert_not_equals(oldPorts, ev.ports, "initMessageEvent() changes ev.ports");
+}, "ports attribute should be a FrozenArray")
+
+test(function() {
+ var ev = document.createEvent("messageevent");
+ var channel = new MessageChannel()
+ ev.initMessageEvent("test", true, false, "testData", "testOrigin", "testId", window, [channel.port1])
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.bubbles, true, "bubbles attribute")
+ assert_equals(ev.cancelable, false, "bubbles attribute")
+ assert_equals(ev.data, "testData", "data attribute")
+ assert_equals(ev.origin, "testOrigin", "origin attribute")
+ assert_equals(ev.lastEventId, "testId", "lastEventId attribute")
+ assert_equals(ev.source, window, "source attribute")
+ assert_array_equals(ev.ports, [channel.port1], "ports attribute")
+}, "initMessageEvent operation")
+
+test(function() {
+ var ev = document.createEvent("messageevent")
+ assert_throws_js(TypeError, function() {
+ ev.initMessageEvent("test", true, false, "testData", "testOrigin", "testId", window, null)
+ })
+}, "Passing null for ports parameter to initMessageEvent")
+
+test(function() {
+ var ev = document.createEvent("messageevent")
+ assert_equals(MessageEvent.prototype.initMessageEvent.length, 1, "MessageEvent.prototype.initMessageEvent.length should be 1")
+ ev.initMessageEvent("test")
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.bubbles, false, "bubbles attribute")
+ assert_equals(ev.cancelable, false, "bubbles attribute")
+ assert_equals(ev.data, null, "data attribute")
+ assert_equals(ev.origin, "", "origin attribute")
+ assert_equals(ev.lastEventId, "", "lastEventId attribute")
+ assert_equals(ev.source, null, "source attribute")
+ assert_array_equals(ev.ports, [], "ports attribute")
+}, "initMessageEvent operation default parameter values")
+
+promise_test(function(t) {
+ var worker_url = "/service-workers/service-worker/resources/empty-worker.js";
+ var scope = "/service-workers/service-worker/resources/";
+ var registration;
+
+ return service_worker_unregister_and_register(t, worker_url, scope)
+ .then(function(r) {
+ registration = r;
+ return wait_for_state(t, r.installing, "activated");
+ })
+ .then(function() {
+ var ev = new MessageEvent("test", { source: registration.active });
+ assert_equals(ev.source, registration.active, "source attribute should return the ServiceWorker");
+ service_worker_unregister(t, scope);
+ });
+ }, "Passing ServiceWorker for source member");
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html
new file mode 100644
index 0000000000..79e4af3020
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html
@@ -0,0 +1,56 @@
+<body></body>
+<script>
+function check1(args, callee) {
+ parent.t.step(function() {
+ parent.assert_equals(callee.length, 5);
+ parent.assert_equals(args.length, 5);
+ parent.assert_equals(args[0], reference_error.message);
+ parent.assert_equals(args[1], reference_error.filename);
+ parent.assert_equals(args[2], reference_error.lineno);
+ parent.assert_equals(args[3], reference_error.colno);
+ parent.assert_equals(args[4], reference_error.error);
+ parent.t.done();
+ });
+}
+
+var reference_error = new ErrorEvent("error", {
+ filename: "error_file.js",
+ lineno: 333,
+ colno: 999,
+ message: "there was an error",
+ error: {nondefault: 'some unusual object'},
+});
+
+parent.t.step(function() {
+ document.body.outerHTML = "<body onerror='check1(arguments, arguments.callee)'></body>"
+ window.dispatchEvent(reference_error);
+});
+
+function check2(args, callee) {
+ parent.t2.step(function() {
+ parent.assert_equals(callee.length, 5);
+ parent.assert_equals(args.length, 1);
+ parent.assert_false(args[0] instanceof ErrorEvent);
+ parent.t2.done()
+ });
+}
+
+parent.t2.step(function() {
+ document.body.outerHTML = "<body onerror='check2(arguments, arguments.callee)'></body>"
+ window.dispatchEvent(new Event("error"));
+});
+
+function check3(args, callee) {
+ parent.t3.step(function() {
+ parent.assert_equals(args.length, 1);
+ parent.assert_equals(callee.length, 1);
+ });
+}
+
+parent.t3.step(function() {
+ document.body.outerHTML = "<body><span onerror='check3(arguments, arguments.callee)'></span></body>"
+ document.body.firstChild.dispatchEvent(reference_error);
+ document.body.firstChild.dispatchEvent(new Event("error"));
+ parent.t3.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html
new file mode 100644
index 0000000000..60fc674d57
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>OnErrorEventHandler + ErrorEvent is treated differently</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+var t = async_test("onerror + ErrorEvent + Window");
+var t2 = async_test("onerror + !ErrorEvent + Window");
+var t3 = async_test("onerror + Document");
+</script>
+<iframe src="onerroreventhandler-frame.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html b/testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html
new file mode 100644
index 0000000000..d40c0b9cce
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This will be in an iframe</title>
+
+<script>
+window.name = "iframe";
+</script>
+
+<body onbeforeunload="return { toString: parent.postMessage.bind(parent, 'PASS', '*') };">
+
+<!-- window.open() uses the entry settings object to determine how the URL will be parsed -->
+<button onclick="var w = window.open('open-window.html'); w.onload = () => { parent.onWindowLoaded(w.document.URL); };">This will be clicked</button>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js b/testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js
new file mode 100644
index 0000000000..d7889e230e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js
@@ -0,0 +1,61 @@
+const windowReflectingBodyElementEventHandlerSet =
+ new Set(['blur', 'error', 'focus', 'load', 'resize', 'scroll']);
+
+function handlersInInterface(mainIDL, name) {
+ return mainIDL.find(idl => idl.name === name).members.map(member => member.name.slice(2));
+}
+
+const handlersListPromise = fetch("/interfaces/html.idl").then(res => res.text()).then(htmlIDL => {
+ const parsedHTMLIDL = WebIDL2.parse(htmlIDL);
+ const windowEventHandlers = handlersInInterface(parsedHTMLIDL, "WindowEventHandlers");
+ const globalEventHandlers = handlersInInterface(parsedHTMLIDL, "GlobalEventHandlers");
+
+ const shadowedHandlers = [
+ ...windowReflectingBodyElementEventHandlerSet,
+ ...windowEventHandlers
+ ];
+ const notShadowedHandlers = globalEventHandlers.filter(name => !windowReflectingBodyElementEventHandlerSet.has(name));
+ return {
+ shadowedHandlers,
+ notShadowedHandlers
+ };
+});
+
+function eventHandlerTest(shadowedHandlers, notShadowedHandlers, element) {
+ const altBody = document.createElement(element);
+ for (const [des, obj1, obj2, obj3, des1, des2, des3] of [
+ ["document.body", document.body, altBody, window, "body", "alternative body", "window"],
+ [`document.createElement("${element}")`, altBody, document.body, window, "alternative body", "body", "window"],
+ ["window", window, document.body, altBody, "window", "body", "alternative body"]
+ ]) {
+ const f = () => 0;
+
+ shadowedHandlers.forEach(handler => {
+ const eventHandler = obj1['on' + handler];
+ test(() => {
+ obj1['on' + handler] = f;
+ assert_equals(obj2['on' + handler], f, `${des2} should reflect`);
+ assert_equals(obj3['on' + handler], f, `${des3} should reflect`);
+ }, `shadowed ${handler} (${des})`);
+ obj1['on' + handler] = eventHandler;
+ });
+
+ notShadowedHandlers.forEach(handler => {
+ const eventHandler = obj1['on' + handler];
+ test(() => {
+ obj1['on' + handler] = f;
+ assert_equals(obj2['on' + handler], null, `${des2} should reflect`);
+ assert_equals(obj3['on' + handler], null, `${des3} should reflect`);
+ }, `not shadowed ${handler} (${des})`);
+ obj1['on' + handler] = eventHandler;
+ });
+
+ shadowedHandlers.forEach(handler => {
+ test(() => {
+ assert_equals(obj1['on' + handler], null, `${des1} should reflect changes to itself`);
+ assert_equals(obj2['on' + handler], null, `${des2} should reflect`);
+ assert_equals(obj3['on' + handler], null, `${des3} should reflect`);
+ }, `shadowed ${handler} removal (${des})`);
+ });
+ }
+}
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html b/testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html
new file mode 100644
index 0000000000..1d23263570
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This window will open during the course of the test</title>
+<h1>Hello</h1>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html b/testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html
new file mode 100644
index 0000000000..a912b32d7f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Uncompiled event handler check that scripting is enabled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({ allow_uncaught_exception: true });
+ test(function() {
+ var invoked = false;
+ window.addEventListener("error", function() {
+ invoked = true;
+ });
+
+ // Make sure that `this_will_error` will in fact error when it's referenced
+ assert_equals(typeof this_will_error, "undefined");
+ var dom = (new DOMParser()).parseFromString("<div id=\"has-event-handler\" onclick=\"this_will_error;\"></div>", "text/html");
+ var click = new MouseEvent("click");
+ dom.getElementById("has-event-handler").dispatchEvent(click);
+ assert_equals(invoked, false);
+ }, "when scripting is disabled, the handler is never compiled");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html
new file mode 100644
index 0000000000..dbb1cdd5a9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - addEventListener</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.addEventListener('error', t.step_func(function(e){
+ ran = true;
+ assert_true(e.isTrusted, 'isTrusted');
+ }), false);
+ </script>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ for (;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html
new file mode 100644
index 0000000000..66e1dfed4d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+ <head>
+ <title>&lt;body onerror> - compile error in &lt;script src=data:...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ </script>
+ <body onerror="
+ t.step(function(){
+ ran = true;
+ assert_equals(typeof event, 'string', 'first arg');
+ assert_equals(source, 'data:text/javascript,for(;){}', 'second arg');
+ assert_equals(typeof lineno, 'number', 'third arg');
+ });
+ t_col.step(function() {
+ assert_equals(typeof colno, 'number', 'fourth arg');
+ });
+ ">
+ <div id=log></div>
+ <script src="data:text/javascript,for(;){}"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html
new file mode 100644
index 0000000000..0f65f73999
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>&lt;body onerror> - compile error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ </script>
+ <body onerror="
+ t.step(function(){
+ ran = true;
+ assert_equals(typeof event, 'string', 'first arg');
+ assert_equals(source, location.href, 'second arg');
+ assert_equals(typeof lineno, 'number', 'third arg');
+ });
+ t_col.step(function() {
+ assert_equals(typeof colno, 'number', 'fourth arg');
+ });
+ ">
+ <div id=log></div>
+ <script>
+ for(;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html
new file mode 100644
index 0000000000..faaddd9ed9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>&lt;body onerror> - runtime error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ </script>
+ <body onerror="
+ t.step(function(){
+ ran = true;
+ assert_equals(typeof event, 'string', 'first arg');
+ assert_equals(source, location.href, 'second arg');
+ assert_equals(typeof lineno, 'number', 'third arg');
+ });
+ t_col.step(function(){
+ assert_equals(typeof colno, 'number', 'fourth arg');
+ });
+ ">
+ <div id=log></div>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html
new file mode 100644
index 0000000000..c4028e650b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in cross-origin setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ var interval;
+ window.addEventListener('error', t.step_func(e => {
+ clearInterval(interval);
+ ran = true;
+ assert_equals(e.error.constructor, SyntaxError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/syntax-error-in-setInterval.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html
new file mode 100644
index 0000000000..1eebf82fbb
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in cross-origin setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.addEventListener('error', t.step_func(e => {
+ ran = true;
+ assert_equals(e.error.constructor, SyntaxError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/syntax-error-in-setTimeout.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html
new file mode 100644
index 0000000000..b7e989529f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=//www1...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(a, 'Script error.', 'first arg');
+ assert_equals(b, '', 'second arg');
+ assert_equals(c, 0, 'third arg');
+ });
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/syntax-error.js');
+ document.body.appendChild(script);
+ onload = function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(col_value, 0, 'fourth arg');
+ t_col.done();
+ });
+ };
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html
new file mode 100644
index 0000000000..08ce2f348f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=data:...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, 'data:text/javascript,for(;){}', 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="data:text/javascript,for(;){}"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html
new file mode 100644
index 0000000000..864d09fc1e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in attribute</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <p onclick="{"></p>
+ <script>
+ t.step(function(){
+ var ev = document.createEvent('Event');
+ ev.initEvent('click', false, false);
+ document.querySelector('p').dispatchEvent(ev);
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html
new file mode 100644
index 0000000000..0b094e71c3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;body onerror></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.onerror = t.step_func(function(){
+ ran = true;
+ });
+ </script>
+ </head>
+ <body onerror="{"><!-- sets the event handler to null before compiling -->
+ <div id=log></div>
+ <script>
+ for(;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_false(ran, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html
new file mode 100644
index 0000000000..79ca7d524a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ var interval;
+ window.onerror = t.step_func(function(a, b, c, d){
+ clearInterval(interval);
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ interval = setInterval("{", 10);
+ step_timeout(function(){
+ t.step(function(){
+ clearInterval(interval);
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html
new file mode 100644
index 0000000000..1bb730e134
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ setTimeout("{", 10);
+ setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html
new file mode 100644
index 0000000000..c367e6cb2f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=...> with hash</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/syntax-error.js#"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/syntax-error.js#"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html
new file mode 100644
index 0000000000..71c28b584d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/syntax-error.js"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/syntax-error.js"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html
new file mode 100644
index 0000000000..a4bdfd9c47
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script>
+ for(;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js
new file mode 100644
index 0000000000..3a3ea40d77
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js
@@ -0,0 +1,25 @@
+// META: global=window,dedicatedworker
+
+promise_test(async () => {
+ const sab = new SharedArrayBuffer(64);
+ const ta = new Int32Array(sab);
+
+ const waitAsyncObj = Atomics.waitAsync(ta, 0, 0, 10);
+ assert_equals(waitAsyncObj.async, true);
+ const v = await waitAsyncObj.value;
+ assert_equals(v, "timed-out");
+}, `Atomics.waitAsync timeout in a ${self.constructor.name}`);
+
+promise_test(async () => {
+ const sab = new SharedArrayBuffer(64);
+ const ta = new Int32Array(sab);
+
+ const waitAsyncObj = Atomics.waitAsync(ta, 0, 0);
+ assert_equals(waitAsyncObj.async, true);
+
+ const worker = new Worker("resources/notify-worker.js");
+ worker.postMessage(sab);
+
+ const v = await waitAsyncObj.value;
+ assert_equals(v, "ok");
+}, `Atomics.waitAsync notification in a ${self.constructor.name}`);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers
new file mode 100644
index 0000000000..5f8621ef83
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js
new file mode 100644
index 0000000000..fddf85dbed
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js
@@ -0,0 +1,11 @@
+// META: global=window,serviceworker
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ const ta = new Int32Array(sab);
+
+ assert_throws_js(TypeError, () => {
+ Atomics.wait(ta, 0, 0, 10);
+ });
+}, `[[CanBlock]] in a ${self.constructor.name}`);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js
new file mode 100644
index 0000000000..0da449a7cf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js
@@ -0,0 +1,9 @@
+// META: global=dedicatedworker,sharedworker
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ const ta = new Int32Array(sab);
+
+ assert_equals(Atomics.wait(ta, 0, 0, 10), "timed-out");
+}, `[[CanBlock]] in a ${self.constructor.name}`);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js
new file mode 100644
index 0000000000..2780e5bc34
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js
@@ -0,0 +1,5 @@
+onmessage = (e) => {
+ const sab = e.data;
+ const ta = new Int32Array(sab);
+ Atomics.notify(ta, 0);
+};
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html
new file mode 100644
index 0000000000..71f03a4dcf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Entry settings object for promise jobs when the function realm is different from the test realm</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- https://github.com/whatwg/html/pull/5212 -->
+<!-- https://github.com/whatwg/html/issues/1426 -->
+
+<!-- This is what would normally be considered the entry page. However, we use functions from the
+ resources/function/function.html realm. So window.open() should resolve relative to that realm
+ inside promise jobs. -->
+
+<iframe src="resources/promise-job-entry-incumbent.html"></iframe>
+<iframe src="resources/function/function.html" id="function-frame"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+const relativeURL = "resources/window-to-open.html";
+const expectedURL = (new URL(relativeURL, document.querySelector("#function-frame").src)).href;
+
+const incumbentWindow = frames[0];
+const functionWindow = frames[1];
+const FunctionFromAnotherWindow = frames[1].Function;
+
+window.onload = () => {
+ async_test(t => {
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ Promise.resolve([incumbentWindow, relativeURL, t, assert_equals, expectedURL]).then(func);
+ }, "Fulfillment handler on fulfilled promise");
+
+ async_test(t => {
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ Promise.reject([incumbentWindow, relativeURL, t, assert_equals, expectedURL]).catch(func);
+ }, "Rejection handler on rejected promise");
+
+ async_test(t => {
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ p.then(func);
+ t.step_timeout(() => resolve([incumbentWindow, relativeURL, t, assert_equals, expectedURL]), 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise");
+
+ async_test(t => {
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ p.catch(func);
+ t.step_timeout(() => reject([incumbentWindow, relativeURL, t, assert_equals, expectedURL]), 0);
+ }, "Rejection handler on pending-then-rejected promise");
+
+ async_test(t => {
+ t.add_cleanup(() => { delete frames[1].args; });
+ frames[1].args = [incumbentWindow, relativeURL, t, assert_equals, expectedURL];
+
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = window.args;
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ const thenable = { then: func };
+
+ Promise.resolve(thenable);
+ }, "Thenable resolution");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html
new file mode 100644
index 0000000000..6d075d674c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Entry settings object for promise jobs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- https://github.com/whatwg/html/pull/5212 -->
+<!-- https://github.com/whatwg/html/issues/1426 -->
+
+<!-- This is the entry page, so window.open() should resolve relative to it, even inside promise jobs. -->
+
+<iframe src="resources/promise-job-entry-incumbent.html"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+const relativeURL = "resources/window-to-open.html";
+const expectedURL = (new URL(relativeURL, location.href)).href;
+
+const incumbentWindow = frames[0];
+
+window.onload = () => {
+ async_test(t => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ }, "Sanity check: this all works as expected with no promises involved");
+
+ async_test(t => {
+ // No t.step_func because that could change the realms
+ Promise.resolve().then(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+ }, "Fulfillment handler on fulfilled promise");
+
+ async_test(t => {
+ // No t.step_func because that could change the realms
+ Promise.reject().catch(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+ }, "Rejection handler on rejected promise");
+
+ async_test(t => {
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ // No t.step_func because that could change the realms
+ p.then(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+
+ t.step_timeout(resolve, 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise");
+
+ async_test(t => {
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ // No t.step_func because that could change the realms
+ p.catch(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+
+ t.step_timeout(reject, 0);
+ }, "Rejection handler on pending-then-rejected promise");
+
+ async_test(t => {
+ const thenable = {
+ // No t.step_func because that could change the realms
+ then(f) {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ }
+ };
+
+ Promise.resolve(thenable);
+ }, "Thenable resolution");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html
new file mode 100644
index 0000000000..af00f834c1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent settings object for promise jobs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This is the entry page. -->
+
+<iframe src="resources/promise-job-incumbent-incumbent.html"></iframe>
+<iframe src="resources/promise-job-incumbent-resolver.html"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+// postMessage should pick the incumbent page as its .source value to set on the MessageEvent, even
+// inside promise jobs.
+const expectedURL = (new URL("resources/promise-job-incumbent-incumbent.html", location.href)).href;
+
+let testId = 0;
+
+window.onload = () => {
+ const relevantWindow = frames[0].document.querySelector("#r").contentWindow;
+ const runInResolver = frames[1].runWhatYouGiveMe;
+
+ function setupTest(t) {
+ ++testId;
+ const thisTestId = testId;
+
+ relevantWindow.addEventListener("messagereceived", t.step_func(e => {
+ const [receivedTestId, receivedSourceURL] = e.detail;
+
+ if (receivedTestId !== thisTestId) {
+ return;
+ }
+
+ assert_equals(receivedSourceURL, expectedURL);
+ t.done();
+ }));
+
+ return thisTestId;
+ }
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ }, "Sanity check: this all works as expected with no promises involved");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ // No t.step_func because that could change the realms
+ Promise.resolve().then(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+ }, "Fulfillment handler on fulfilled promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ const p = Promise.resolve();
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "then", thisTestId, "*");
+ }, "Fulfillment handler on fulfilled promise, using backup incumbent settings object stack");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ // No t.step_func because that could change the realms
+ Promise.reject().catch(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+ }, "Rejection handler on rejected promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ const p = Promise.reject();
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "catch", thisTestId, "*");
+ }, "Rejection handler on rejected promise, using backup incumbent settings object stack");
+
+ // The following tests test that we derive the incumbent settings object at promise-job time from
+ // the incumbent realm at the time the handler was added, not at the time the resolve()/reject()
+ // was done. See https://github.com/whatwg/html/issues/5213 for the spec side of this issue.
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ // No t.step_func because that could change the realms
+ p.then(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+
+ t.step_timeout(() => {
+ runInResolver(resolve);
+ }, 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "then", thisTestId, "*");
+
+ t.step_timeout(() => {
+ runInResolver(resolve);
+ }, 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise, using backup incumbent settings object stack");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ // No t.step_func because that could change the realms
+ p.catch(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+
+ t.step_timeout(() => {
+ runInResolver(reject);
+ }, 0);
+ }, "Rejection handler on pending-then-rejected promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "catch", thisTestId, "*");
+
+ t.step_timeout(() => {
+ runInResolver(reject);
+ }, 0);
+ }, "Rejection handler on pending-then-rejected promise, using backup incumbent settings object stack");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ const thenable = {
+ // No t.step_func because that could change the realms
+ then(f) {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ }
+ };
+
+ Promise.resolve(thenable);
+ }, "Thenable resolution");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ frames[0].resolveThenableThatRunsWindowPostMessageVeryIndirectlyWithNoUserCode(testId, "*", []);
+ }, "Thenable resolution, using backup incumbent settings object stack");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md
new file mode 100644
index 0000000000..a89258a4e0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md
@@ -0,0 +1,5 @@
+A couple notes about the files scattered in this `resources/` directory:
+
+* The nested directory structure is necessary here so that relative URL resolution can be tested; we need different sub-paths for each document.
+
+* The semi-duplicate `window-to-open.html`s scattered throughout are present because Firefox, at least, does not fire `Window` `load` events for 404s, so we want to ensure that no matter which global is used, `window`'s `load` event is hit and our tests can proceed.
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html
new file mode 100644
index 0000000000..63d9c437fc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Current page used as a test helper</title>
+
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html
new file mode 100644
index 0000000000..1bc4cca9a3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the current settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html
new file mode 100644
index 0000000000..15841d387d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Realm for a "then" function used as a test helper</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html
new file mode 100644
index 0000000000..3928c1f8aa
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the function's settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html
new file mode 100644
index 0000000000..3740c1467d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent page used as a test helper</title>
+
+<iframe src="relevant/relevant.html" id="r"></iframe>
+<iframe src="current/current.html" id="c"></iframe>
+
+<script>
+ const relevant = document.querySelector("#r");
+ const current = document.querySelector("#c");
+
+ window.runWindowOpenVeryIndirectly = (...args) => {
+ return current.contentWindow.open.call(relevant.contentWindow, ...args);
+ };
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html
new file mode 100644
index 0000000000..57dd5dff10
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent page used as a test helper</title>
+
+<iframe src="relevant/relevant.html" id="r"></iframe>
+<iframe src="current/current.html" id="c"></iframe>
+
+<script>
+ const relevant = document.querySelector("#r");
+ const current = document.querySelector("#c");
+
+ window.runWindowPostMessageVeryIndirectly = (...args) => {
+ return current.contentWindow.postMessage.call(relevant.contentWindow, ...args);
+ };
+
+ // This tests the backup incumbent settings object stack scenario, by avoiding putting user code on the stack.
+ window.runWindowPostMessageVeryIndirectlyWithNoUserCode = (promise, promiseMethod, ...args) => {
+ const runWindowPostMessage = current.contentWindow.postMessage.bind(relevant.contentWindow, ...args);
+ promise[promiseMethod](runWindowPostMessage);
+ };
+
+ window.resolveThenableThatRunsWindowPostMessageVeryIndirectlyWithNoUserCode = (...args) => {
+ Promise.resolve({
+ then: current.contentWindow.postMessage.bind(relevant.contentWindow, ...args)
+ });
+ };
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html
new file mode 100644
index 0000000000..a730b9c3ce
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent page used as a test helper</title>
+
+<script>
+ window.runWhatYouGiveMe = (func) => {
+ func();
+ };
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html
new file mode 100644
index 0000000000..f5965f2231
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Relevant page used as a test helper</title>
+
+<script>
+// promise-job-incumbent will end up posting a message to here. We need to signal back the "source".
+
+window.onmessage = e => {
+ const testId = e.data;
+ const sourceURL = e.source.document.URL;
+
+ window.dispatchEvent(new CustomEvent("messagereceived", { detail: [testId, sourceURL] }));
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html
new file mode 100644
index 0000000000..4138b5a084
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the relevant settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html
new file mode 100644
index 0000000000..7743b9b578
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the incumbent settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html
new file mode 100644
index 0000000000..ce357937f5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the entry settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html
new file mode 100644
index 0000000000..8b92f7d148
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in cross-origin setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ var interval;
+ window.addEventListener('error', t.step_func(e => {
+ clearInterval(interval);
+ ran = true;
+ assert_equals(e.error.constructor, ReferenceError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/undefined-variable-in-setInterval.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html
new file mode 100644
index 0000000000..2e1a9d2315
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in cross-origin setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.addEventListener('error', t.step_func(e => {
+ ran = true;
+ assert_equals(e.error.constructor, ReferenceError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/undefined-variable-in-setTimeout.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html
new file mode 100644
index 0000000000..d63aaa6d3b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=//www1...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(a, 'Script error.', 'first arg');
+ assert_equals(b, '', 'second arg');
+ assert_equals(c, 0, 'third arg');
+ });
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/undefined-variable.js');
+ document.body.appendChild(script);
+ onload = function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(col_value, 0, 'fourth arg');
+ t_col.done();
+ });
+ };
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html
new file mode 100644
index 0000000000..485ce90aa6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=data:...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, 'data:text/javascript,undefined_variable;', 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="data:text/javascript,undefined_variable;"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html
new file mode 100644
index 0000000000..b4f69da7a2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in attribute</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <p onclick="undefined_variable;"></p>
+ <script>
+ t.step(function(){
+ var ev = document.createEvent('Event');
+ ev.initEvent('click', false, false);
+ document.querySelector('p').dispatchEvent(ev);
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html
new file mode 100644
index 0000000000..e0fd1dcbd5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>runtime error in &lt;body onerror></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = 0;
+ </script>
+ </head>
+ <body onerror="ran++; undefined_variable_in_onerror;">
+ <div id=log></div>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_equals(ran, 1, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html
new file mode 100644
index 0000000000..090e1dd78e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ var interval;
+ window.onerror = t.step_func(function(a, b, c, d){
+ clearInterval(interval);
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ interval = setInterval("undefined_variable;", 10);
+ step_timeout(function(){
+ clearInterval(interval);
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html
new file mode 100644
index 0000000000..cebcd4346c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ setTimeout("undefined_variable;", 10);
+ setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html
new file mode 100644
index 0000000000..150a793b79
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>runtime error in window.onerror</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = 0;
+ window.onerror = function(){
+ ran++;
+ undefined_variable_in_onerror;
+ };
+ </script>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_equals(ran, 1, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html
new file mode 100644
index 0000000000..dc6ec059a5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=...> with hash</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/undefined-variable.js#"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/undefined-variable.js#"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html
new file mode 100644
index 0000000000..8f3cfb70b2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/undefined-variable.js"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/undefined-variable.js"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html
new file mode 100644
index 0000000000..7907494aa6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js
new file mode 100644
index 0000000000..afec114458
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js
@@ -0,0 +1,8 @@
+interval = setInterval('{', 10);
+step_timeout(function(){
+ clearInterval(interval);
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js
new file mode 100644
index 0000000000..427542b42e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js
@@ -0,0 +1,7 @@
+setTimeout('{', 10);
+setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js
new file mode 100644
index 0000000000..0f74a6fca6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js
@@ -0,0 +1 @@
+for (;) {} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js
new file mode 100644
index 0000000000..c2a017a2ab
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js
@@ -0,0 +1,8 @@
+interval = setInterval('undefined_variable;', 10);
+step_timeout(function(){
+ clearInterval(interval);
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js
new file mode 100644
index 0000000000..6fa54cda9f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js
@@ -0,0 +1,7 @@
+setTimeout('undefined_variable;', 10);
+setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js
new file mode 100644
index 0000000000..e73a62ceda
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js
@@ -0,0 +1 @@
+undefined_variable; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html
new file mode 100644
index 0000000000..7524604113
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cors/support.js?pipe=sub"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<link rel="help" href="https://html.spec.whatwg.org/#muted-errors">
+
+<body>
+<script>
+'use strict';
+setup({
+ allow_uncaught_exception: true
+});
+
+async_test(function(t) {
+ addEventListener('unhandledrejection', t.step_func(function(e) {
+ assert_equals(e.reason, 42, 'reason should be the one given by the script');
+ t.done();
+ }));
+}, 'Promise rejection event should be received for the cross-origin CORS script');
+
+(function() {
+ var scriptEl = document.createElement('script');
+ scriptEl.src = CROSSDOMAIN + 'support/promise-access-control.py?allow=true';
+ scriptEl.crossOrigin = 'anonymous';
+ document.body.appendChild(scriptEl);
+}());
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html
new file mode 100644
index 0000000000..d61618a53e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cors/support.js?pipe=sub"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<link rel="help" href="https://html.spec.whatwg.org/#muted-errors">
+
+<body>
+<script>
+'use strict';
+
+setup({
+ allow_uncaught_exception: true
+});
+
+(function() {
+ var resolveLoaded;
+ var loadedPromise = new Promise(function(resolve) { resolveLoaded = resolve; });
+
+ promise_test(function(t) {
+ var unreachedUnhandled = t.unreached_func('unhandledrejection event should never be triggered');
+ var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+ addEventListener('unhandledrejection', unreachedUnhandled);
+ addEventListener('rejectionhandled', unreachedHandled);
+ ensureCleanup(t, unreachedUnhandled, unreachedHandled);
+
+ return loadedPromise.then(t.step_func(function() {
+ return new Promise(function(resolve) {
+ t.step_timeout(function() {
+ resolve();
+ }, 1000);
+ });
+ }));
+ }, 'Promise rejection event should be muted for cross-origin non-CORS script');
+
+ promise_test(function(t) {
+ var unreachedUnhandled = t.unreached_func('unhandledrejection event should never be triggered');
+ var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+ addEventListener('unhandledrejection', unreachedUnhandled);
+ addEventListener('rejectionhandled', unreachedHandled);
+ ensureCleanup(t, unreachedUnhandled, unreachedHandled);
+
+ return new Promise(function(resolve) {
+ handleRejectedPromise(new Promise(function(resolve, reject) { reject(42); }));
+ t.step_timeout(function() {
+ resolve();
+ }, 1000);
+ });
+ }, 'Promise rejection should be muted if the rejected promise is handled in cross-origin non-CORS script');
+
+ promise_test(function(t) {
+ var promise = new Promise(function(resolve, reject) { reject(42); });
+ var resolveReceived;
+ var eventPromise = new Promise(function(resolve) { resolveReceived = resolve; });
+ var unhandled = t.step_func(function(e) {
+ if (e.promise === promise) {
+ handleRejectedPromise(promise);
+ resolveReceived();
+ }
+ });
+ var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', unreachedHandled);
+ ensureCleanup(t, unhandled, unreachedHandled);
+
+ return eventPromise.then(t.step_func(function() {
+ return new Promise(function(resolve) {
+ t.step_timeout(function() {
+ resolve();
+ }, 1000);
+ });
+ }));
+ }, 'Promise rejection should be muted if the rejected promise is handled in unhandledrejection event handler in cross-origin non-CORS script');
+
+ function ensureCleanup(t, unhandled, handled) {
+ t.add_cleanup(function() {
+ if (unhandled) {
+ removeEventListener('unhandledrejection', unhandled);
+ }
+ if (handled) {
+ removeEventListener('rejectionhandled', handled);
+ }
+ });
+ }
+
+ var scriptEl = document.createElement('script');
+ scriptEl.src = CROSSDOMAIN + 'support/promise-access-control.py?allow=false';
+ scriptEl.onload = resolveLoaded;
+ document.body.appendChild(scriptEl);
+}());
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html
new file mode 100644
index 0000000000..4319deee7b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#the-promiserejectionevent-interface">
+<script>
+'use strict';
+
+test(function() {
+ var p = new Promise(function(resolve, reject) {});
+
+ assert_throws_js(TypeError,
+ function() {
+ PromiseRejectionEvent('', { promise: p });
+ },
+ "Calling PromiseRejectionEvent constructor without 'new' must throw");
+
+ // No custom options are passed (besides required promise).
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).bubbles, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).cancelable, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).promise, p);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).reason, undefined);
+
+ // No promise is passed.
+ assert_throws_js(TypeError,
+ function() {
+ new PromiseRejectionEvent('eventType', { bubbles: false });
+ },
+ 'Cannot construct PromiseRejectionEventInit without promise');
+
+ // bubbles is passed.
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: false, promise: p }).bubbles, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, promise: p }).bubbles, true);
+
+ // cancelable is passed.
+ assert_equals(new PromiseRejectionEvent('eventType', { cancelable: false, promise: p }).cancelable, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { cancelable: true, promise: p }).cancelable, true);
+
+ // reason is passed.
+ var r = new Error();
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p, reason: r }).reason, r);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p, reason: null }).reason, null);
+
+ // All initializers are passed.
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).bubbles, true);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).cancelable, true);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).promise, p);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).reason, r);
+}, "This tests the constructor for the PromiseRejectionEvent DOM class.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html
new file mode 100644
index 0000000000..160dad9b36
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection during initial parsing of document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<body>
+<p>The script in this test is executed immediately while parsing is ongoing, and
+<a
+href="https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script">cleaning
+up after running script</a> involves queueing a task on the DOM manipulation
+task source to fire the <code>unhandledrejection</code> event. Parsing then
+completes, immediately transitioning the document's readiness state to
+"interactive," and queuing another task on the DOM manipulation task source to
+transition the state to "complete."
+</p>
+<script>
+'use strict';
+setup({ allow_uncaught_exception: true });
+
+async_test(function(t) {
+ const events = [];
+ document.addEventListener('readystatechange', t.step_func(function() {
+ events.push('readystatechange:' + document.readyState);
+ }));
+ addEventListener('unhandledrejection', t.step_func(function() {
+ events.push('unhandledrejection');
+ }));
+
+ Promise.reject(new Error('this error is intentional'));
+
+ addEventListener('load', t.step_func(function() {
+ assert_array_equals(
+ events,
+ [
+ 'readystatechange:interactive',
+ 'unhandledrejection',
+ 'readystatechange:complete'
+ ]
+ );
+ t.done();
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html
new file mode 100644
index 0000000000..b151bd812f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<script>
+'use strict';
+setup({
+ allow_uncaught_exception: true
+});
+async_test(function(t) {
+ var e = new Error('e');
+ var p = Promise.reject(e);
+
+ window.onunhandledrejection = function(evt) {
+ t.step(function() {
+ assert_equals(evt.promise, p);
+ assert_equals(evt.reason, e);
+ });
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ t.step_timeout(function() { t.done(); }, 10);
+ });
+ };
+
+ window.onrejectionhandled = t.unreached_func('rejectionhandled event should not be invoked');
+}, 'Attaching a handler in unhandledrejection should not trigger rejectionhandled.');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html
new file mode 100644
index 0000000000..c749eadef4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<div id="log"></div><br>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+'use strict';
+
+setup({
+ allow_uncaught_exception: true
+});
+
+async_test(function(t) {
+ createIframeAndStartTest(t, function(w) {
+ let e = new Error();
+ let promise = new w.Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+
+ let unhandled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_equals(evt.reason, e);
+ assert_equals(evt.promise, promise);
+ });
+ t.done();
+ }
+ };
+ let handled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_unreached('rejectionhandled event is not supposed to be triggered');
+ });
+ }
+ };
+
+ w.addEventListener('unhandledrejection', unhandled);
+ w.addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, w, unhandled, handled);
+ });
+}, "unhandledrejection: promise is created in iframe and being rejected elsewhere");
+
+async_test(function(t) {
+ createIframeAndStartTest(t, function(w) {
+ let e = new Error();
+ let promise = w.Promise.reject(e);
+
+ let unhandled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_unreached('unhandledrejection event is not supposed to be triggered');
+ });
+ }
+ };
+ let handled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_unreached('rejectionhandled event is not supposed to be triggered');
+ });
+ }
+ };
+
+ w.addEventListener('unhandledrejection', unhandled);
+ w.addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, w, unhandled, handled);
+
+ promise.catch(function() {});
+ setTimeout(function() {
+ t.done();
+ }, 10);
+ });
+}, 'no unhandledrejection/rejectionhandled: promise is created in iframe and being rejected elsewhere');
+
+async_test(function(t) {
+ createIframeAndStartTest(t, function(w) {
+ let e = new Error();
+ let promise = w.Promise.reject(e);
+ var unhandledPromises = [];
+ var unhandledReasons = [];
+ var handledPromises = [];
+ var handledReasons = [];
+
+ let unhandled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ unhandledPromises.push(evt.promise);
+ unhandledReasons.push(evt.reason);
+
+ setTimeout(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise.then(unreached, function(reason) {
+ assert_equals(reason, e);
+ setTimeout(function() {
+ assert_array_equals(handledPromises, [promise]);
+ assert_array_equals(handledReasons, [e]);
+ t.done();
+ }, 10);
+ });
+ }, 10);
+ });
+ }
+ };
+ let handled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_array_equals(unhandledPromises, [promise]);
+ assert_array_equals(unhandledReasons, [e]);
+ handledPromises.push(evt.promise);
+ handledReasons.push(evt.reason);
+ });
+ }
+ };
+
+ w.addEventListener('unhandledrejection', unhandled);
+ w.addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, w, unhandled, handled);
+ });
+}, 'delayed handling: promise is created in iframe and being rejected elsewhere');
+
+// Helpers
+
+function createIframeAndStartTest(t, runTest) {
+ var iframe = document.createElement("iframe");
+ iframe.onload = function() {
+ t.add_cleanup(() => iframe.remove());
+ runTest(iframe.contentWindow);
+ };
+ iframe.srcdoc = '';
+ document.documentElement.appendChild(iframe);
+}
+
+function ensureCleanup(t, win, unhandled, handled) {
+ t.add_cleanup(function() {
+ if (unhandled) {
+ win.removeEventListener('unhandledrejection', unhandled);
+ }
+ if (handled) {
+ win.removeEventListener('rejectionhandled', handled);
+ }
+ });
+}
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html
new file mode 100644
index 0000000000..b6c02d27c9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#runtime-script-errors">
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<script>
+'use strict';
+setup({
+ allow_uncaught_exception: true
+});
+async_test(function(t) {
+ var e = new Error('e');
+ var e2 = new Error('e2');
+
+ window.onerror = function (msg, url, line, col, error) {
+ t.step(function() {
+ assert_true(msg.includes('e2'));
+ assert_equals(error, e2);
+ });
+ t.done();
+ };
+
+ window.onrejectionhandled = function() {
+ // This should cause onerror
+ throw e2;
+ };
+
+ var p = Promise.reject(e);
+ queueTask(function() {
+ queueTask(t.step_func(function() {
+ // This will cause onrejectionhandled
+ p.catch(function() {});
+ }));
+ });
+}, 'Throwing inside an unhandledrejection handler invokes the error handler.');
+
+// This function queues a task in "DOM manipulation task source"
+function queueTask(f) {
+ var d = document.createElement("details");
+ d.ontoggle = function() {
+ f();
+ };
+
+ d.setAttribute("open", "");
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html
new file mode 100644
index 0000000000..b6a4a9f3e6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a dedicated worker context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script>
+'use strict';
+fetch_tests_from_worker(new Worker('support/promise-rejection-events.js'));
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html
new file mode 100644
index 0000000000..2fdfe26025
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a Window context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script src="support/promise-rejection-events.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html
new file mode 100644
index 0000000000..9d12125928
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a service worker context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script>
+'use strict';
+service_worker_test('support/promise-rejection-events.js', 'Service worker setup');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html
new file mode 100644
index 0000000000..d832d1822f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a shared worker context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script>
+'use strict';
+fetch_tests_from_worker(new SharedWorker('support/promise-rejection-events.js'));
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html
new file mode 100644
index 0000000000..314063025f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection ordering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint">
+<body>
+<p>A microtask checkpoint should notify about rejected promises. After
+<a
+href="https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script">cleaning
+up after running script</a> involves running a microtask checkpoint. So the order of unhandledrejection
+should occur before error2.
+</p>
+
+<script>
+'use strict';
+setup({ allow_uncaught_exception: true });
+
+async_test(function(t) {
+ const events = [];
+ addEventListener('unhandledrejection', t.step_func(() => {
+ events.push('unhandledrejection');
+ }));
+
+ function insertInvalidScript(id) {
+ // Inserting <script> with an empty source schedules dispatching an 'error' event on
+ // the dom manipulation task source.
+ let script = document.createElement('script');
+ script.setAttribute('src', ' ');
+ script.addEventListener('error', t.step_func(() => {
+ events.push(`error${id}`);
+
+ // This will be the end of the test. Verify the results are correct.
+ if (id == 2) {
+ assert_array_equals(
+ events,
+ [
+ 'raf1',
+ 'resolve1',
+ 'raf2',
+ 'resolve2',
+ 'error1',
+ 'unhandledrejection',
+ 'error2'
+ ]
+ );
+ t.done();
+ }
+ }));
+ document.body.append(script);
+ }
+
+ requestAnimationFrame(t.step_func(() => {
+ events.push('raf1');
+ Promise.reject();
+ Promise.resolve(0).then(t.step_func(() => {
+ events.push('resolve1');
+ }));
+ insertInvalidScript(1);
+ }));
+
+ requestAnimationFrame(t.step_func(() => {
+ events.push('raf2');
+ Promise.resolve(0).then(t.step_func(() => {
+ events.push('resolve2');
+ }));
+ insertInvalidScript(2);
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py
new file mode 100644
index 0000000000..cf8ed5e492
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py
@@ -0,0 +1,18 @@
+def main(request, response):
+ allow = request.GET.first(b"allow", b"false")
+
+ headers = [(b"Content-Type", b"application/javascript")]
+ if allow != b"false":
+ headers.append((b"Access-Control-Allow-Origin", b"*"))
+
+ body = b"""
+ function handleRejectedPromise(promise) {
+ promise.catch(() => {});
+ }
+
+ (function() {
+ new Promise(function(resolve, reject) { reject(42); });
+ })();
+ """
+
+ return headers, body
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js
new file mode 100644
index 0000000000..036e1784db
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js
@@ -0,0 +1,961 @@
+'use strict';
+
+if (self.importScripts) {
+ importScripts('/resources/testharness.js');
+}
+
+setup({
+ allow_uncaught_exception: true
+});
+
+//
+// Straightforward unhandledrejection tests
+//
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.reject(e);
+}, 'unhandledrejection: from Promise.reject');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ reject(e);
+ });
+}, 'unhandledrejection: from a synchronous rejection in new Promise');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ queueTask(function() {
+ reject(e);
+ });
+ });
+}, 'unhandledrejection: from a task-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+}, 'unhandledrejection: from a setTimeout-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var e2 = new Error();
+ var promise2;
+
+ onUnhandledSucceed(t, e2, function() { return promise2; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise2 = Promise.reject(e).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ throw e2;
+ });
+}, 'unhandledrejection: from a throw in a rejection handler chained off of Promise.reject');
+
+async_test(function(t) {
+ var e = new Error();
+ var e2 = new Error();
+ var promise2;
+
+ onUnhandledSucceed(t, e2, function() { return promise2; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise2 = new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ throw e2;
+ });
+}, 'unhandledrejection: from a throw in a rejection handler chained off of a setTimeout-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var e2 = new Error();
+ var promise2;
+
+ onUnhandledSucceed(t, e2, function() { return promise2; });
+
+ var promise = new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise2 = promise.then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ throw e2;
+ });
+ });
+ }, 1);
+ });
+}, 'unhandledrejection: from a throw in a rejection handler attached one microtask after a setTimeout-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.resolve().then(function() {
+ return Promise.reject(e);
+ });
+}, 'unhandledrejection: from returning a Promise.reject-created rejection in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.resolve().then(function() {
+ throw e;
+ });
+}, 'unhandledrejection: from a throw in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.resolve().then(function() {
+ return new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+ });
+}, 'unhandledrejection: from returning a setTimeout-delayed rejection in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.all([Promise.reject(e)]);
+}, 'unhandledrejection: from Promise.reject, indirected through Promise.all');
+
+async_test(function(t) {
+ var p;
+
+ var unhandled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ assert_equals(ev.reason.name, 'InvalidStateError');
+ assert_equals(ev.promise, p);
+ });
+ t.done();
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ ensureCleanup(t, unhandled);
+
+ p = createImageBitmap(new Blob());
+}, 'unhandledrejection: from createImageBitmap which is UA triggered');
+
+//
+// Negative unhandledrejection/rejectionhandled tests with immediate attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.reject(e).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise from Promise.reject');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.all([Promise.reject(e)]).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise from ' +
+ 'Promise.reject, indirecting through Promise.all');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = new Promise(function(_, reject) {
+ reject(e);
+ }).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a synchronously-rejected ' +
+ 'promise created with new Promise');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.resolve().then(function() {
+ throw e;
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ });
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'throwing in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.resolve().then(function() {
+ return Promise.reject(e);
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ });
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'returning a Promise.reject-created promise in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.resolve().then(function() {
+ return new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ });
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'returning a setTimeout-delayed rejection in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ p = Promise.resolve().then(function() {
+ return Promise.reject(e);
+ })
+ .catch(function() {});
+ });
+}, 'no unhandledrejection/rejectionhandled: all inside a queued task, a rejection handler attached synchronously to ' +
+ 'a promise created from returning a Promise.reject-created promise in a fulfillment handler');
+
+async_test(function(t) {
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = createImageBitmap(new Blob()).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'createImageBitmap');
+
+//
+// Negative unhandledrejection/rejectionhandled tests with microtask-delayed attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+}, 'delayed handling: a microtask delay before attaching a handler prevents both events (Promise.reject-created ' +
+ 'promise)');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ reject(e);
+ });
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+}, 'delayed handling: a microtask delay before attaching a handler prevents both events (immediately-rejected new ' +
+ 'Promise-created promise)');
+
+async_test(function(t) {
+ var e = new Error();
+ var p1;
+ var p2;
+
+ onUnhandledFail(t, function() { return p1; });
+ onUnhandledFail(t, function() { return p2; });
+
+ p1 = new Promise(function(_, reject) {
+ mutationObserverMicrotask(function() {
+ reject(e);
+ });
+ });
+ p2 = Promise.all([p1]);
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p2.then(unreached, function() {});
+ });
+}, 'delayed handling: a microtask delay before attaching the handler, and before rejecting the promise, indirected ' +
+ 'through Promise.all');
+
+//
+// Negative unhandledrejection/rejectionhandled tests with nested-microtask-delayed attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, ' +
+ 'all inside a queueTask');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ setTimeout(function() {
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ }, 0);
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, ' +
+ 'all inside a setTimeout');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ p = Promise.reject(e);
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, ' +
+ 'all inside a queueTask');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ setTimeout(function() {
+ p = Promise.reject(e);
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ }, 0);
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, ' +
+ 'all inside a setTimeout');
+
+
+// For workers, queueTask() involves posting tasks to other threads, so
+// the following tests don't work there.
+
+if ('document' in self) {
+ //
+ // Negative unhandledrejection/rejectionhandled tests with task-delayed attachment
+ //
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var _reject;
+ p = new Promise(function(_, reject) {
+ _reject = reject;
+ });
+ _reject(e);
+ queueTask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+ }, 'delayed handling: a task delay before attaching a handler prevents unhandledrejection');
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ }, 'delayed handling: queueTask after promise creation/rejection, plus promise microtasks, is not too late to ' +
+ 'attach a rejection handler');
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ p = Promise.reject(e);
+ }, 'delayed handling: queueTask before promise creation/rejection, plus many promise microtasks, is not too ' +
+ 'late to attach a rejection handler');
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ }, 'delayed handling: queueTask after promise creation/rejection, plus many promise microtasks, is not too ' +
+ 'late to attach a rejection handler');
+}
+
+//
+// Positive unhandledrejection/rejectionhandled tests with delayed attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ var _reject;
+ p = new Promise(function(_, reject) {
+ _reject = reject;
+ });
+ _reject(e);
+ queueTask(function() {
+ queueTask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+ });
+}, 'delayed handling: a nested-task delay before attaching a handler causes unhandledrejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+}, 'delayed handling: a nested-queueTask after promise creation/rejection, plus promise microtasks, is too ' +
+ 'late to attach a rejection handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ queueTask(function() {
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ });
+ p = Promise.reject(e);
+}, 'delayed handling: a nested-queueTask before promise creation/rejection, plus many promise microtasks, is ' +
+ 'too late to attach a rejection handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ });
+}, 'delayed handling: a nested-queueTask after promise creation/rejection, plus many promise microtasks, is ' +
+ 'too late to attach a rejection handler');
+
+async_test(function(t) {
+ var unhandledPromises = [];
+ var unhandledReasons = [];
+ var e = new Error();
+ var p;
+
+ var unhandled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ unhandledPromises.push(ev.promise);
+ unhandledReasons.push(ev.reason);
+ });
+ }
+ };
+ var handled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ assert_array_equals(unhandledPromises, [p]);
+ assert_array_equals(unhandledReasons, [e]);
+ assert_equals(ev.promise, p);
+ assert_equals(ev.reason, e);
+ });
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+
+ p = new Promise(function() {
+ throw e;
+ });
+ setTimeout(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function(reason) {
+ assert_equals(reason, e);
+ setTimeout(function() { t.done(); }, 10);
+ });
+ }, 10);
+}, 'delayed handling: delaying handling by setTimeout(,10) will cause both events to fire');
+
+async_test(function(t) {
+ var unhandledPromises = [];
+ var unhandledReasons = [];
+ var p;
+
+ var unhandled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ unhandledPromises.push(ev.promise);
+ unhandledReasons.push(ev.reason.name);
+ });
+ }
+ };
+ var handled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ assert_array_equals(unhandledPromises, [p]);
+ assert_array_equals(unhandledReasons, ['InvalidStateError']);
+ assert_equals(ev.promise, p);
+ assert_equals(ev.reason.name, 'InvalidStateError');
+ });
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+
+ p = createImageBitmap(new Blob());
+ setTimeout(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function(reason) {
+ assert_equals(reason.name, 'InvalidStateError');
+ setTimeout(function() { t.done(); }, 10);
+ });
+ }, 10);
+}, 'delayed handling: delaying handling rejected promise created from createImageBitmap will cause both events to fire');
+
+//
+// Miscellaneous tests about integration with the rest of the platform
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var l = function(ev) {
+ var order = [];
+ mutationObserverMicrotask(function() {
+ order.push(1);
+ });
+ setTimeout(function() {
+ order.push(2);
+ t.step(function() {
+ assert_array_equals(order, [1, 2]);
+ });
+ t.done();
+ }, 1);
+ };
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+ Promise.reject(e);
+}, 'mutationObserverMicrotask vs. queueTask ordering is not disturbed inside unhandledrejection events');
+
+// For workers, queueTask() involves posting tasks to other threads, so
+// the following tests don't work there.
+
+if ('document' in self) {
+
+ // For the next two see https://github.com/domenic/unhandled-rejections-browser-spec/issues/2#issuecomment-121121695
+ // and the following comments.
+
+ async_test(function(t) {
+ var sequenceOfEvents = [];
+
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+
+ var p1 = Promise.reject();
+ var p2;
+ queueTask(function() {
+ p2 = Promise.reject();
+ queueTask(function() {
+ sequenceOfEvents.push('queueTask');
+ checkSequence();
+ });
+ });
+
+ function l(ev) {
+ if (ev.promise === p1 || ev.promise === p2) {
+ sequenceOfEvents.push(ev.promise);
+ checkSequence();
+ }
+ }
+
+ function checkSequence() {
+ if (sequenceOfEvents.length === 3) {
+ t.step(function() {
+ assert_array_equals(sequenceOfEvents, [p1, 'queueTask', p2]);
+ });
+ t.done();
+ }
+ }
+ }, 'queueTask ordering vs. the task queued for unhandled rejection notification (1)');
+
+ async_test(function(t) {
+ var sequenceOfEvents = [];
+
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+
+ var p2;
+ queueTask(function() {
+ p2 = Promise.reject();
+ queueTask(function() {
+ sequenceOfEvents.push('queueTask');
+ checkSequence();
+ });
+ });
+
+ function l(ev) {
+ if (ev.promise == p2) {
+ sequenceOfEvents.push(ev.promise);
+ checkSequence();
+ }
+ }
+
+ function checkSequence() {
+ if (sequenceOfEvents.length === 2) {
+ t.step(function() {
+ assert_array_equals(sequenceOfEvents, ['queueTask', p2]);
+ });
+ t.done();
+ }
+ }
+ }, 'queueTask ordering vs. the task queued for unhandled rejection notification (2)');
+
+ async_test(function(t) {
+ var sequenceOfEvents = [];
+
+
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+
+ var p = Promise.reject();
+
+ function unhandled(ev) {
+ if (ev.promise === p) {
+ sequenceOfEvents.push('unhandled');
+ checkSequence();
+ setTimeout(function() {
+ queueTask(function() {
+ sequenceOfEvents.push('task before catch');
+ checkSequence();
+ });
+
+ p.catch(function() {
+ sequenceOfEvents.push('catch');
+ checkSequence();
+ });
+
+ queueTask(function() {
+ sequenceOfEvents.push('task after catch');
+ checkSequence();
+ });
+
+ sequenceOfEvents.push('after catch');
+ checkSequence();
+ }, 10);
+ }
+ }
+
+ function handled(ev) {
+ if (ev.promise === p) {
+ sequenceOfEvents.push('handled');
+ checkSequence();
+ }
+ }
+
+ function checkSequence() {
+ if (sequenceOfEvents.length === 6) {
+ t.step(function() {
+ assert_array_equals(sequenceOfEvents,
+ ['unhandled', 'after catch', 'catch', 'task before catch', 'handled', 'task after catch']);
+ });
+ t.done();
+ }
+ }
+ }, 'rejectionhandled is dispatched from a queued task, and not immediately');
+}
+
+//
+// HELPERS
+//
+
+// This function queues a task in "DOM manipulation task source" in window
+// context, but not in workers.
+function queueTask(f) {
+ if ('document' in self) {
+ var d = document.createElement("details");
+ d.ontoggle = function() {
+ f();
+ };
+ d.setAttribute("open", "");
+ } else {
+ // We need to fix this to use something that can queue tasks in
+ // "DOM manipulation task source" to ensure the order is correct
+ var channel = new MessageChannel();
+ channel.port1.onmessage = function() { channel.port1.close(); f(); };
+ channel.port2.postMessage('abusingpostmessageforfunandprofit');
+ channel.port2.close();
+ }
+}
+
+function mutationObserverMicrotask(f) {
+ if ('document' in self) {
+ var observer = new MutationObserver(function() { f(); });
+ var node = document.createTextNode('');
+ observer.observe(node, { characterData: true });
+ node.data = 'foo';
+ } else {
+ // We don't have mutation observers on workers, so just post a promise-based
+ // microtask.
+ Promise.resolve().then(function() { f(); });
+ }
+}
+
+function onUnhandledSucceed(t, expectedReason, expectedPromiseGetter) {
+ var l = function(ev) {
+ if (ev.promise === expectedPromiseGetter()) {
+ t.step(function() {
+ assert_equals(ev.reason, expectedReason);
+ assert_equals(ev.promise, expectedPromiseGetter());
+ });
+ t.done();
+ }
+ };
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+}
+
+function onUnhandledFail(t, expectedPromiseGetter) {
+ var unhandled = function(evt) {
+ if (evt.promise === expectedPromiseGetter()) {
+ t.step(function() {
+ assert_unreached('unhandledrejection event is not supposed to be triggered');
+ });
+ }
+ };
+ var handled = function(evt) {
+ if (evt.promise === expectedPromiseGetter()) {
+ t.step(function() {
+ assert_unreached('rejectionhandled event is not supposed to be triggered');
+ });
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+ setTimeout(function() {
+ t.done();
+ }, 10);
+}
+
+function ensureCleanup(t, unhandled, handled) {
+ t.add_cleanup(function() {
+ if (unhandled)
+ removeEventListener('unhandledrejection', unhandled);
+ if (handled)
+ removeEventListener('rejectionhandled', handled);
+ });
+}
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html
new file mode 100644
index 0000000000..3c21df49c9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror: parse errors</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!--
+
+ In https://html.spec.whatwg.org/multipage/#creating-scripts ,
+ step 3 describes parsing the script, and step 5 says:
+ # Otherwise, report the error using the onerror event handler of
+ # the script's global object. If the error is still not handled
+ # after this, then the error may be reported to the user.
+ which links to
+ https://html.spec.whatwg.org/multipage/#report-the-error ,
+ which describes what to do when onerror is a Function.
+
+ -->
+ </head>
+ <body>
+
+ <div id="log"></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var error_count = 0;
+ window.onerror = function(msg, url, lineno) {
+ ++error_count;
+ test(function() {assert_equals(url, window.location.href)},
+ "correct url passed to window.onerror");
+ test(function() {assert_equals(lineno, 34)},
+ "correct line number passed to window.onerror");
+ };
+ </script>
+ <script>This script does not parse correctly.</script>
+ <script>
+ test(function() {assert_equals(error_count, 1)},
+ "correct number of calls to window.onerror");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html
new file mode 100644
index 0000000000..4b2bc1f22c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror: runtime scripterrors</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!--
+
+ https://html.spec.whatwg.org/multipage/#runtime-script-errors
+ says what to do for uncaught runtime script errors, and just below
+ describes what to do when onerror is a Function.
+
+ -->
+ </head>
+ <body>
+
+ <div id="log"></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var error_count = 0;
+ window.onerror = function(msg, url, lineno) {
+ ++error_count;
+ test(function() {assert_equals(url, window.location.href)},
+ "correct url passed to window.onerror");
+ test(function() {assert_equals(lineno, 36)},
+ "correct line number passed to window.onerror");
+ };
+ </script>
+ <script>
+ try {
+ // This error is caught, so it should NOT trigger onerror.
+ throw "foo";
+ } catch (ex) {
+ }
+ // This error is NOT caught, so it should trigger onerror.
+ throw "bar";
+ </script>
+ <script>
+ test(function() {assert_equals(error_count, 1)},
+ "correct number of calls to window.onerror");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html
new file mode 100644
index 0000000000..1fdab521ae
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror: runtime scripterrors</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!--
+
+ https://html.spec.whatwg.org/multipage/#runtime-script-errors
+ says what to do for uncaught runtime script errors, and just below
+ describes what to do when onerror is a Function.
+
+ -->
+ </head>
+ <body>
+
+ <div id="log"></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var error_count = 0;
+ window.onerror = function(msg, url, lineno) {
+ ++error_count;
+ test(function() {assert_equals(url, window.location.href)},
+ "correct url passed to window.onerror");
+ test(function() {assert_equals(lineno, 36)},
+ "correct line number passed to window.onerror");
+ };
+ </script>
+ <script>
+ try {
+ // This error is caught, so it should NOT trigger onerror.
+ window.nonexistentproperty.oops();
+ } catch (ex) {
+ }
+ // This error is NOT caught, so it should trigger onerror.
+ window.nonexistentproperty.oops();
+ </script>
+ <script>
+ test(function() {assert_equals(error_count, 1)},
+ "correct number of calls to window.onerror");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html
new file mode 100644
index 0000000000..65a1a02b11
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window B via the
+ addEventListener function from window B, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[0].Function("thereIsNoSuchCallable()");
+ frames[1].document.addEventListener("myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_true(frame0ErrorFired);
+ assert_false(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html
new file mode 100644
index 0000000000..6c5476542b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window B via the
+ addEventListener function from window A, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[0].Function("thereIsNoSuchCallable()");
+ frames[0].document.addEventListener.call(frames[1].document, "myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_true(frame0ErrorFired);
+ assert_false(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html
new file mode 100644
index 0000000000..5e78baa8de
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window A via the
+ addEventListener function from window A, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[1].Function("thereIsNoSuchCallable()");
+ frames[1].document.addEventListener("myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_false(frame0ErrorFired);
+ assert_true(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html
new file mode 100644
index 0000000000..a5f35d613f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window A via the
+ addEventListener function from window B, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[1].Function("thereIsNoSuchCallable()");
+ frames[0].document.addEventListener.call(frames[1].document, "myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_false(frame0ErrorFired);
+ assert_true(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html
new file mode 100644
index 0000000000..da93e782ca
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>window.onerror listener reports the exception in global object of its callback</title>
+<link rel=help href="https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+setup({ allow_uncaught_exception: true });
+
+window.onload = () => {
+ test(() => {
+ window.onerrorCalls = [];
+ window.onerror = () => { onerrorCalls.push("top"); };
+ frames[0].onerror = new frames[1].Function(`top.onerrorCalls.push("frame0"); throw new parent.frames[2].Error("PASS");`);
+ frames[1].onerror = () => { onerrorCalls.push("frame1"); };
+ frames[2].onerror = () => { onerrorCalls.push("frame2"); };
+
+ frames[0].dispatchEvent(new ErrorEvent("error", { error: new Error("foo") }));
+ assert_array_equals(onerrorCalls, ["frame0", "frame1"]);
+ });
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html b/testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html
new file mode 100644
index 0000000000..6e2c2aae8f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>self.reportError() dispatches an "error" event for this's relevant global object</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#dom-reporterror">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+setup({ allow_uncaught_exception: true });
+
+async_test(t => {
+ window.addEventListener("error", t.unreached_func("'error' event should not be dispatched for top window!"));
+
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func_done(() => {
+ let eventFired = false;
+ const error = new TypeError("foo");
+ const otherWindow = iframe.contentWindow;
+ otherWindow.addEventListener("error", t.step_func(event => {
+ assert_equals(event.error, error);
+ eventFired = true;
+ }));
+
+ window.reportError.call(otherWindow, error);
+ assert_true(eventFired);
+ });
+ document.body.append(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html b/testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html
new file mode 100644
index 0000000000..33662caa4d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>window.reportError() doesn't crash when window is detached</title>
+<link rel="help" href="https://crbug.com/1445375">
+<body>
+<script>
+let i = document.createElement("iframe");
+document.body.appendChild(i);
+let i_win = i.contentWindow;
+i.remove();
+i_win.reportError("an error");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js b/testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js
new file mode 100644
index 0000000000..b9e7ba25bc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js
@@ -0,0 +1,49 @@
+setup({ allow_uncaught_exception:true });
+
+[
+ 1,
+ new TypeError(),
+ undefined
+].forEach(throwable => {
+ test(t => {
+ let happened = false;
+ self.addEventListener("error", t.step_func(e => {
+ assert_true(e.message !== "");
+ assert_equals(e.filename, new URL("reporterror.any.js", location.href).href);
+ assert_greater_than(e.lineno, 0);
+ assert_greater_than(e.colno, 0);
+ assert_equals(e.error, throwable);
+ happened = true;
+ }), { once:true });
+ self.reportError(throwable);
+ assert_true(happened);
+ }, `self.reportError(${throwable})`);
+});
+
+test(() => {
+ assert_throws_js(TypeError, () => self.reportError());
+}, `self.reportError() (without arguments) throws`);
+
+test(() => {
+ // Workaround for https://github.com/web-platform-tests/wpt/issues/32105
+ let invoked = false;
+ self.reportError({
+ get name() {
+ invoked = true;
+ assert_unreached('get name')
+ },
+ get message() {
+ invoked = true;
+ assert_unreached('get message');
+ },
+ get fileName() {
+ invoked = true;
+ assert_unreached('get fileName');
+ },
+ get lineNumber() {
+ invoked = true;
+ assert_unreached('get lineNumber');
+ }
+ });
+ assert_false(invoked);
+}, `self.reportError() doesn't invoke getters`);
diff --git a/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js
new file mode 100644
index 0000000000..00a86fa74b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js
@@ -0,0 +1,45 @@
+/**
+ * Runs a collection of tests that determine if an API implements structured clone
+ * correctly.
+ *
+ * The `runner` parameter has the following properties:
+ * - `setup()`: An optional function run once before testing starts
+ * - `teardown()`: An option function run once after all tests are done
+ * - `preTest()`: An optional, async function run before a test
+ * - `postTest()`: An optional, async function run after a test is done
+ * - `structuredClone(obj, transferList)`: Required function that somehow
+ * structurally clones an object.
+ * Must return a promise.
+ * - `hasDocument`: When true, disables tests that require a document. True by default.
+ */
+
+function runStructuredCloneBatteryOfTests(runner) {
+ const defaultRunner = {
+ setup() {},
+ preTest() {},
+ postTest() {},
+ teardown() {},
+ hasDocument: true
+ };
+ runner = Object.assign({}, defaultRunner, runner);
+
+ let setupPromise = runner.setup();
+ const allTests = structuredCloneBatteryOfTests.map(test => {
+
+ if (!runner.hasDocument && test.requiresDocument) {
+ return;
+ }
+
+ return new Promise(resolve => {
+ promise_test(async t => {
+ test = await test;
+ await setupPromise;
+ await runner.preTest(test);
+ await test.f(runner, t)
+ await runner.postTest(test);
+ resolve();
+ }, test.description);
+ }).catch(_ => {});
+ });
+ Promise.all(allTests).then(_ => runner.teardown());
+}
diff --git a/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js
new file mode 100644
index 0000000000..23cf4f651a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js
@@ -0,0 +1,169 @@
+structuredCloneBatteryOfTests.push({
+ description: 'ArrayBuffer',
+ async f(runner) {
+ const buffer = new Uint8Array([1]).buffer;
+ const copy = await runner.structuredClone(buffer, [buffer]);
+ assert_equals(buffer.byteLength, 0);
+ assert_equals(copy.byteLength, 1);
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'MessagePort',
+ async f(runner) {
+ const {port1, port2} = new MessageChannel();
+ const copy = await runner.structuredClone(port2, [port2]);
+ const msg = new Promise(resolve => port1.onmessage = resolve);
+ copy.postMessage('ohai');
+ assert_equals((await msg).data, 'ohai');
+ }
+});
+
+// TODO: ImageBitmap
+
+structuredCloneBatteryOfTests.push({
+ description: 'A detached ArrayBuffer cannot be transferred',
+ async f(runner, t) {
+ const buffer = new ArrayBuffer();
+ await runner.structuredClone(buffer, [buffer]);
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(buffer, [buffer])
+ );
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'A detached platform object cannot be transferred',
+ async f(runner, t) {
+ const {port1} = new MessageChannel();
+ await runner.structuredClone(port1, [port1]);
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(port1, [port1])
+ );
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Transferring a non-transferable platform object fails',
+ async f(runner, t) {
+ const blob = new Blob();
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(blob, [blob])
+ );
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'An object whose interface is deleted from the global object must still be received',
+ async f(runner) {
+ const {port1} = new MessageChannel();
+ const messagePortInterface = globalThis.MessagePort;
+ delete globalThis.MessagePort;
+ try {
+ const transfer = await runner.structuredClone(port1, [port1]);
+ assert_true(transfer instanceof messagePortInterface);
+ } finally {
+ globalThis.MessagePort = messagePortInterface;
+ }
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'A subclass instance will be received as its closest transferable superclass',
+ async f(runner) {
+ // MessagePort doesn't have a constructor, so we must use something else.
+
+ // Make sure that ReadableStream is transferable before we test its subclasses.
+ try {
+ const stream = new ReadableStream();
+ await runner.structuredClone(stream, [stream]);
+ } catch(err) {
+ if (err instanceof DOMException && err.code === DOMException.DATA_CLONE_ERR) {
+ throw new OptionalFeatureUnsupportedError("ReadableStream isn't transferable");
+ } else {
+ throw err;
+ }
+ }
+
+ class ReadableStreamSubclass extends ReadableStream {}
+ const original = new ReadableStreamSubclass();
+ const transfer = await runner.structuredClone(original, [original]);
+ assert_equals(Object.getPrototypeOf(transfer), ReadableStream.prototype);
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Resizable ArrayBuffer is transferable',
+ async f(runner) {
+ const buffer = new ArrayBuffer(16, { maxByteLength: 1024 });
+ const copy = await runner.structuredClone(buffer, [buffer]);
+ assert_equals(buffer.byteLength, 0);
+ assert_equals(copy.byteLength, 16);
+ assert_equals(copy.maxByteLength, 1024);
+ assert_true(copy.resizable);
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Length-tracking TypedArray is transferable',
+ async f(runner) {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ const ta = new Uint8Array(ab);
+ const copy = await runner.structuredClone(ta, [ab]);
+ assert_equals(ab.byteLength, 0);
+ assert_equals(copy.buffer.byteLength, 16);
+ assert_equals(copy.buffer.maxByteLength, 1024);
+ assert_true(copy.buffer.resizable);
+ copy.buffer.resize(32);
+ assert_equals(copy.byteLength, 32);
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Length-tracking DataView is transferable',
+ async f(runner) {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ const dv = new DataView(ab);
+ const copy = await runner.structuredClone(dv, [ab]);
+ assert_equals(ab.byteLength, 0);
+ assert_equals(copy.buffer.byteLength, 16);
+ assert_equals(copy.buffer.maxByteLength, 1024);
+ assert_true(copy.buffer.resizable);
+ copy.buffer.resize(32);
+ assert_equals(copy.byteLength, 32);
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Transferring OOB TypedArray throws',
+ async f(runner, t) {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ const ta = new Uint8Array(ab, 8);
+ ab.resize(0);
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(ta, [ab])
+ );
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Transferring OOB DataView throws',
+ async f(runner, t) {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ const dv = new DataView(ab, 8);
+ ab.resize(0);
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(dv, [ab])
+ );
+ }
+});
diff --git a/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests.js b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests.js
new file mode 100644
index 0000000000..923ac9dc16
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-battery-of-tests.js
@@ -0,0 +1,753 @@
+/* This file is mostly a remix of @zcorpan’s web worker test suite */
+
+structuredCloneBatteryOfTests = [];
+
+function check(description, input, callback, requiresDocument = false) {
+ structuredCloneBatteryOfTests.push({
+ description,
+ async f(runner) {
+ let newInput = input;
+ if (typeof input === 'function') {
+ newInput = input();
+ }
+ const copy = await runner.structuredClone(newInput);
+ await callback(copy, newInput);
+ },
+ requiresDocument
+ });
+}
+
+function compare_primitive(actual, input) {
+ assert_equals(actual, input);
+}
+function compare_Array(callback) {
+ return async function(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof Array, 'instanceof Array');
+ assert_not_equals(actual, input);
+ assert_equals(actual.length, input.length, 'length');
+ await callback(actual, input);
+ }
+}
+
+function compare_Object(callback) {
+ return async function(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof Object, 'instanceof Object');
+ assert_false(actual instanceof Array, 'instanceof Array');
+ assert_not_equals(actual, input);
+ await callback(actual, input);
+ }
+}
+
+function enumerate_props(compare_func) {
+ return async function(actual, input) {
+ for (const x in input) {
+ await compare_func(actual[x], input[x]);
+ }
+ };
+}
+
+check('primitive undefined', undefined, compare_primitive);
+check('primitive null', null, compare_primitive);
+check('primitive true', true, compare_primitive);
+check('primitive false', false, compare_primitive);
+check('primitive string, empty string', '', compare_primitive);
+check('primitive string, lone high surrogate', '\uD800', compare_primitive);
+check('primitive string, lone low surrogate', '\uDC00', compare_primitive);
+check('primitive string, NUL', '\u0000', compare_primitive);
+check('primitive string, astral character', '\uDBFF\uDFFD', compare_primitive);
+check('primitive number, 0.2', 0.2, compare_primitive);
+check('primitive number, 0', 0, compare_primitive);
+check('primitive number, -0', -0, compare_primitive);
+check('primitive number, NaN', NaN, compare_primitive);
+check('primitive number, Infinity', Infinity, compare_primitive);
+check('primitive number, -Infinity', -Infinity, compare_primitive);
+check('primitive number, 9007199254740992', 9007199254740992, compare_primitive);
+check('primitive number, -9007199254740992', -9007199254740992, compare_primitive);
+check('primitive number, 9007199254740994', 9007199254740994, compare_primitive);
+check('primitive number, -9007199254740994', -9007199254740994, compare_primitive);
+check('primitive BigInt, 0n', 0n, compare_primitive);
+check('primitive BigInt, -0n', -0n, compare_primitive);
+check('primitive BigInt, -9007199254740994000n', -9007199254740994000n, compare_primitive);
+check('primitive BigInt, -9007199254740994000900719925474099400090071992547409940009007199254740994000n', -9007199254740994000900719925474099400090071992547409940009007199254740994000n, compare_primitive);
+
+check('Array primitives', [undefined,
+ null,
+ true,
+ false,
+ '',
+ '\uD800',
+ '\uDC00',
+ '\u0000',
+ '\uDBFF\uDFFD',
+ 0.2,
+ 0,
+ -0,
+ NaN,
+ Infinity,
+ -Infinity,
+ 9007199254740992,
+ -9007199254740992,
+ 9007199254740994,
+ -9007199254740994,
+ -12n,
+ -0n,
+ 0n], compare_Array(enumerate_props(compare_primitive)));
+check('Object primitives', {'undefined':undefined,
+ 'null':null,
+ 'true':true,
+ 'false':false,
+ 'empty':'',
+ 'high surrogate':'\uD800',
+ 'low surrogate':'\uDC00',
+ 'nul':'\u0000',
+ 'astral':'\uDBFF\uDFFD',
+ '0.2':0.2,
+ '0':0,
+ '-0':-0,
+ 'NaN':NaN,
+ 'Infinity':Infinity,
+ '-Infinity':-Infinity,
+ '9007199254740992':9007199254740992,
+ '-9007199254740992':-9007199254740992,
+ '9007199254740994':9007199254740994,
+ '-9007199254740994':-9007199254740994}, compare_Object(enumerate_props(compare_primitive)));
+
+function compare_Boolean(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof Boolean, 'instanceof Boolean');
+ assert_equals(String(actual), String(input), 'converted to primitive');
+ assert_not_equals(actual, input);
+}
+check('Boolean true', new Boolean(true), compare_Boolean);
+check('Boolean false', new Boolean(false), compare_Boolean);
+check('Array Boolean objects', [new Boolean(true), new Boolean(false)], compare_Array(enumerate_props(compare_Boolean)));
+check('Object Boolean objects', {'true':new Boolean(true), 'false':new Boolean(false)}, compare_Object(enumerate_props(compare_Boolean)));
+
+function compare_obj(what) {
+ const Type = self[what];
+ return function(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof Type, 'instanceof '+what);
+ assert_equals(Type(actual), Type(input), 'converted to primitive');
+ assert_not_equals(actual, input);
+ };
+}
+check('String empty string', new String(''), compare_obj('String'));
+check('String lone high surrogate', new String('\uD800'), compare_obj('String'));
+check('String lone low surrogate', new String('\uDC00'), compare_obj('String'));
+check('String NUL', new String('\u0000'), compare_obj('String'));
+check('String astral character', new String('\uDBFF\uDFFD'), compare_obj('String'));
+check('Array String objects', [new String(''),
+ new String('\uD800'),
+ new String('\uDC00'),
+ new String('\u0000'),
+ new String('\uDBFF\uDFFD')], compare_Array(enumerate_props(compare_obj('String'))));
+check('Object String objects', {'empty':new String(''),
+ 'high surrogate':new String('\uD800'),
+ 'low surrogate':new String('\uDC00'),
+ 'nul':new String('\u0000'),
+ 'astral':new String('\uDBFF\uDFFD')}, compare_Object(enumerate_props(compare_obj('String'))));
+
+check('Number 0.2', new Number(0.2), compare_obj('Number'));
+check('Number 0', new Number(0), compare_obj('Number'));
+check('Number -0', new Number(-0), compare_obj('Number'));
+check('Number NaN', new Number(NaN), compare_obj('Number'));
+check('Number Infinity', new Number(Infinity), compare_obj('Number'));
+check('Number -Infinity', new Number(-Infinity), compare_obj('Number'));
+check('Number 9007199254740992', new Number(9007199254740992), compare_obj('Number'));
+check('Number -9007199254740992', new Number(-9007199254740992), compare_obj('Number'));
+check('Number 9007199254740994', new Number(9007199254740994), compare_obj('Number'));
+check('Number -9007199254740994', new Number(-9007199254740994), compare_obj('Number'));
+// BigInt does not have a non-throwing constructor
+check('BigInt -9007199254740994n', Object(-9007199254740994n), compare_obj('BigInt'));
+
+check('Array Number objects', [new Number(0.2),
+ new Number(0),
+ new Number(-0),
+ new Number(NaN),
+ new Number(Infinity),
+ new Number(-Infinity),
+ new Number(9007199254740992),
+ new Number(-9007199254740992),
+ new Number(9007199254740994),
+ new Number(-9007199254740994)], compare_Array(enumerate_props(compare_obj('Number'))));
+check('Object Number objects', {'0.2':new Number(0.2),
+ '0':new Number(0),
+ '-0':new Number(-0),
+ 'NaN':new Number(NaN),
+ 'Infinity':new Number(Infinity),
+ '-Infinity':new Number(-Infinity),
+ '9007199254740992':new Number(9007199254740992),
+ '-9007199254740992':new Number(-9007199254740992),
+ '9007199254740994':new Number(9007199254740994),
+ '-9007199254740994':new Number(-9007199254740994)}, compare_Object(enumerate_props(compare_obj('Number'))));
+
+function compare_Date(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof Date, 'instanceof Date');
+ assert_equals(Number(actual), Number(input), 'converted to primitive');
+ assert_not_equals(actual, input);
+}
+check('Date 0', new Date(0), compare_Date);
+check('Date -0', new Date(-0), compare_Date);
+check('Date -8.64e15', new Date(-8.64e15), compare_Date);
+check('Date 8.64e15', new Date(8.64e15), compare_Date);
+check('Array Date objects', [new Date(0),
+ new Date(-0),
+ new Date(-8.64e15),
+ new Date(8.64e15)], compare_Array(enumerate_props(compare_Date)));
+check('Object Date objects', {'0':new Date(0),
+ '-0':new Date(-0),
+ '-8.64e15':new Date(-8.64e15),
+ '8.64e15':new Date(8.64e15)}, compare_Object(enumerate_props(compare_Date)));
+
+function compare_RegExp(expected_source) {
+ // XXX ES6 spec doesn't define exact serialization for `source` (it allows several ways to escape)
+ return function(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof RegExp, 'instanceof RegExp');
+ assert_equals(actual.global, input.global, 'global');
+ assert_equals(actual.ignoreCase, input.ignoreCase, 'ignoreCase');
+ assert_equals(actual.multiline, input.multiline, 'multiline');
+ assert_equals(actual.source, expected_source, 'source');
+ assert_equals(actual.sticky, input.sticky, 'sticky');
+ assert_equals(actual.unicode, input.unicode, 'unicode');
+ assert_equals(actual.lastIndex, 0, 'lastIndex');
+ assert_not_equals(actual, input);
+ }
+}
+function func_RegExp_flags_lastIndex() {
+ const r = /foo/gim;
+ r.lastIndex = 2;
+ return r;
+}
+function func_RegExp_sticky() {
+ return new RegExp('foo', 'y');
+}
+function func_RegExp_unicode() {
+ return new RegExp('foo', 'u');
+}
+check('RegExp flags and lastIndex', func_RegExp_flags_lastIndex, compare_RegExp('foo'));
+check('RegExp sticky flag', func_RegExp_sticky, compare_RegExp('foo'));
+check('RegExp unicode flag', func_RegExp_unicode, compare_RegExp('foo'));
+check('RegExp empty', new RegExp(''), compare_RegExp('(?:)'));
+check('RegExp slash', new RegExp('/'), compare_RegExp('\\/'));
+check('RegExp new line', new RegExp('\n'), compare_RegExp('\\n'));
+check('Array RegExp object, RegExp flags and lastIndex', [func_RegExp_flags_lastIndex()], compare_Array(enumerate_props(compare_RegExp('foo'))));
+check('Array RegExp object, RegExp sticky flag', function() { return [func_RegExp_sticky()]; }, compare_Array(enumerate_props(compare_RegExp('foo'))));
+check('Array RegExp object, RegExp unicode flag', function() { return [func_RegExp_unicode()]; }, compare_Array(enumerate_props(compare_RegExp('foo'))));
+check('Array RegExp object, RegExp empty', [new RegExp('')], compare_Array(enumerate_props(compare_RegExp('(?:)'))));
+check('Array RegExp object, RegExp slash', [new RegExp('/')], compare_Array(enumerate_props(compare_RegExp('\\/'))));
+check('Array RegExp object, RegExp new line', [new RegExp('\n')], compare_Array(enumerate_props(compare_RegExp('\\n'))));
+check('Object RegExp object, RegExp flags and lastIndex', {'x':func_RegExp_flags_lastIndex()}, compare_Object(enumerate_props(compare_RegExp('foo'))));
+check('Object RegExp object, RegExp sticky flag', function() { return {'x':func_RegExp_sticky()}; }, compare_Object(enumerate_props(compare_RegExp('foo'))));
+check('Object RegExp object, RegExp unicode flag', function() { return {'x':func_RegExp_unicode()}; }, compare_Object(enumerate_props(compare_RegExp('foo'))));
+check('Object RegExp object, RegExp empty', {'x':new RegExp('')}, compare_Object(enumerate_props(compare_RegExp('(?:)'))));
+check('Object RegExp object, RegExp slash', {'x':new RegExp('/')}, compare_Object(enumerate_props(compare_RegExp('\\/'))));
+check('Object RegExp object, RegExp new line', {'x':new RegExp('\n')}, compare_Object(enumerate_props(compare_RegExp('\\n'))));
+
+function compare_Error(actual, input) {
+ assert_true(actual instanceof Error, "Checking instanceof");
+ assert_equals(actual.constructor, input.constructor, "Checking constructor");
+ assert_equals(actual.name, input.name, "Checking name");
+ assert_equals(actual.hasOwnProperty("message"), input.hasOwnProperty("message"), "Checking message existence");
+ assert_equals(actual.message, input.message, "Checking message");
+ assert_equals(actual.foo, undefined, "Checking for absence of custom property");
+}
+
+check('Empty Error object', new Error, compare_Error);
+
+const errorConstructors = [Error, EvalError, RangeError, ReferenceError,
+ SyntaxError, TypeError, URIError];
+for (const constructor of errorConstructors) {
+ check(`${constructor.name} object`, () => {
+ let error = new constructor("Error message here");
+ error.foo = "testing";
+ return error;
+ }, compare_Error);
+}
+
+async function compare_Blob(actual, input, expect_File) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof Blob, 'instanceof Blob');
+ if (!expect_File)
+ assert_false(actual instanceof File, 'instanceof File');
+ assert_equals(actual.size, input.size, 'size');
+ assert_equals(actual.type, input.type, 'type');
+ assert_not_equals(actual, input);
+ const ab1 = await new Response(actual).arrayBuffer();
+ const ab2 = await new Response(input).arrayBuffer();
+ assert_equals(ab1.byteLength, ab2.byteLength, 'byteLength');
+ const ta1 = new Uint8Array(ab1);
+ const ta2 = new Uint8Array(ab2);
+ for(let i = 0; i < ta1.size; i++) {
+ assert_equals(ta1[i], ta2[i]);
+ }
+}
+function func_Blob_basic() {
+ return new Blob(['foo'], {type:'text/x-bar'});
+}
+check('Blob basic', func_Blob_basic, compare_Blob);
+
+function b(str) {
+ return parseInt(str, 2);
+}
+function encode_cesu8(codeunits) {
+ // http://www.unicode.org/reports/tr26/ section 2.2
+ // only the 3-byte form is supported
+ const rv = [];
+ codeunits.forEach(function(codeunit) {
+ rv.push(b('11100000') + ((codeunit & b('1111000000000000')) >> 12));
+ rv.push(b('10000000') + ((codeunit & b('0000111111000000')) >> 6));
+ rv.push(b('10000000') + (codeunit & b('0000000000111111')));
+ });
+ return rv;
+}
+function func_Blob_bytes(arr) {
+ return function() {
+ const buffer = new ArrayBuffer(arr.length);
+ const view = new DataView(buffer);
+ for (let i = 0; i < arr.length; ++i) {
+ view.setUint8(i, arr[i]);
+ }
+ return new Blob([view]);
+ };
+}
+check('Blob unpaired high surrogate (invalid utf-8)', func_Blob_bytes(encode_cesu8([0xD800])), compare_Blob);
+check('Blob unpaired low surrogate (invalid utf-8)', func_Blob_bytes(encode_cesu8([0xDC00])), compare_Blob);
+check('Blob paired surrogates (invalid utf-8)', func_Blob_bytes(encode_cesu8([0xD800, 0xDC00])), compare_Blob);
+
+function func_Blob_empty() {
+ return new Blob(['']);
+}
+check('Blob empty', func_Blob_empty , compare_Blob);
+function func_Blob_NUL() {
+ return new Blob(['\u0000']);
+}
+check('Blob NUL', func_Blob_NUL, compare_Blob);
+
+check('Array Blob object, Blob basic', [func_Blob_basic()], compare_Array(enumerate_props(compare_Blob)));
+check('Array Blob object, Blob unpaired high surrogate (invalid utf-8)', [func_Blob_bytes([0xD800])()], compare_Array(enumerate_props(compare_Blob)));
+check('Array Blob object, Blob unpaired low surrogate (invalid utf-8)', [func_Blob_bytes([0xDC00])()], compare_Array(enumerate_props(compare_Blob)));
+check('Array Blob object, Blob paired surrogates (invalid utf-8)', [func_Blob_bytes([0xD800, 0xDC00])()], compare_Array(enumerate_props(compare_Blob)));
+check('Array Blob object, Blob empty', [func_Blob_empty()], compare_Array(enumerate_props(compare_Blob)));
+check('Array Blob object, Blob NUL', [func_Blob_NUL()], compare_Array(enumerate_props(compare_Blob)));
+check('Array Blob object, two Blobs', [func_Blob_basic(), func_Blob_empty()], compare_Array(enumerate_props(compare_Blob)));
+
+check('Object Blob object, Blob basic', {'x':func_Blob_basic()}, compare_Object(enumerate_props(compare_Blob)));
+check('Object Blob object, Blob unpaired high surrogate (invalid utf-8)', {'x':func_Blob_bytes([0xD800])()}, compare_Object(enumerate_props(compare_Blob)));
+check('Object Blob object, Blob unpaired low surrogate (invalid utf-8)', {'x':func_Blob_bytes([0xDC00])()}, compare_Object(enumerate_props(compare_Blob)));
+check('Object Blob object, Blob paired surrogates (invalid utf-8)', {'x':func_Blob_bytes([0xD800, 0xDC00])() }, compare_Object(enumerate_props(compare_Blob)));
+check('Object Blob object, Blob empty', {'x':func_Blob_empty()}, compare_Object(enumerate_props(compare_Blob)));
+check('Object Blob object, Blob NUL', {'x':func_Blob_NUL()}, compare_Object(enumerate_props(compare_Blob)));
+
+async function compare_File(actual, input) {
+ assert_true(actual instanceof File, 'instanceof File');
+ assert_equals(actual.name, input.name, 'name');
+ assert_equals(actual.lastModified, input.lastModified, 'lastModified');
+ await compare_Blob(actual, input, true);
+}
+function func_File_basic() {
+ return new File(['foo'], 'bar', {type:'text/x-bar', lastModified:42});
+}
+check('File basic', func_File_basic, compare_File);
+
+function compare_FileList(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof FileList, 'instanceof FileList');
+ assert_equals(actual.length, input.length, 'length');
+ assert_not_equals(actual, input);
+ // XXX when there's a way to populate or construct a FileList,
+ // check the items in the FileList
+}
+function func_FileList_empty() {
+ const input = document.createElement('input');
+ input.type = 'file';
+ return input.files;
+}
+check('FileList empty', func_FileList_empty, compare_FileList, true);
+check('Array FileList object, FileList empty', () => ([func_FileList_empty()]), compare_Array(enumerate_props(compare_FileList)), true);
+check('Object FileList object, FileList empty', () => ({'x':func_FileList_empty()}), compare_Object(enumerate_props(compare_FileList)), true);
+
+function compare_ArrayBuffer(actual, input) {
+ assert_true(actual instanceof ArrayBuffer, 'instanceof ArrayBuffer');
+ assert_equals(actual.byteLength, input.byteLength, 'byteLength');
+ assert_equals(actual.maxByteLength, input.maxByteLength, 'maxByteLength');
+ assert_equals(actual.resizable, input.resizable, 'resizable');
+ assert_equals(actual.growable, input.growable, 'growable');
+}
+
+function compare_ArrayBufferView(view) {
+ const Type = self[view];
+ return function(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof Type, 'instanceof '+view);
+ assert_equals(actual.length, input.length, 'length');
+ assert_equals(actual.byteLength, input.byteLength, 'byteLength');
+ assert_equals(actual.byteOffset, input.byteOffset, 'byteOffset');
+ assert_not_equals(actual.buffer, input.buffer, 'buffer');
+ for (let i = 0; i < actual.length; ++i) {
+ assert_equals(actual[i], input[i], 'actual['+i+']');
+ }
+ };
+}
+function compare_ImageData(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_equals(actual.width, input.width, 'width');
+ assert_equals(actual.height, input.height, 'height');
+ assert_not_equals(actual.data, input.data, 'data');
+ compare_ArrayBufferView('Uint8ClampedArray')(actual.data, input.data, null);
+}
+function func_ImageData_1x1_transparent_black() {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ return ctx.createImageData(1, 1);
+}
+check('ImageData 1x1 transparent black', func_ImageData_1x1_transparent_black, compare_ImageData, true);
+function func_ImageData_1x1_non_transparent_non_black() {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ const imagedata = ctx.createImageData(1, 1);
+ imagedata.data[0] = 100;
+ imagedata.data[1] = 101;
+ imagedata.data[2] = 102;
+ imagedata.data[3] = 103;
+ return imagedata;
+}
+check('ImageData 1x1 non-transparent non-black', func_ImageData_1x1_non_transparent_non_black, compare_ImageData, true);
+check('Array ImageData object, ImageData 1x1 transparent black', () => ([func_ImageData_1x1_transparent_black()]), compare_Array(enumerate_props(compare_ImageData)), true);
+check('Array ImageData object, ImageData 1x1 non-transparent non-black', () => ([func_ImageData_1x1_non_transparent_non_black()]), compare_Array(enumerate_props(compare_ImageData)), true);
+check('Object ImageData object, ImageData 1x1 transparent black', () => ({'x':func_ImageData_1x1_transparent_black()}), compare_Object(enumerate_props(compare_ImageData)), true);
+check('Object ImageData object, ImageData 1x1 non-transparent non-black', () => ({'x':func_ImageData_1x1_non_transparent_non_black()}), compare_Object(enumerate_props(compare_ImageData)), true);
+
+
+check('Array sparse', new Array(10), compare_Array(enumerate_props(compare_primitive)));
+check('Array with non-index property', function() {
+ const rv = [];
+ rv.foo = 'bar';
+ return rv;
+}, compare_Array(enumerate_props(compare_primitive)));
+check('Object with index property and length', {'0':'foo', 'length':1}, compare_Object(enumerate_props(compare_primitive)));
+function check_circular_property(prop) {
+ return function(actual) {
+ assert_equals(actual[prop], actual);
+ };
+}
+check('Array with circular reference', function() {
+ const rv = [];
+ rv[0] = rv;
+ return rv;
+}, compare_Array(check_circular_property('0')));
+check('Object with circular reference', function() {
+ const rv = {};
+ rv['x'] = rv;
+ return rv;
+}, compare_Object(check_circular_property('x')));
+function check_identical_property_values(prop1, prop2) {
+ return function(actual) {
+ assert_equals(actual[prop1], actual[prop2]);
+ };
+}
+check('Array with identical property values', function() {
+ const obj = {}
+ return [obj, obj];
+}, compare_Array(check_identical_property_values('0', '1')));
+check('Object with identical property values', function() {
+ const obj = {}
+ return {'x':obj, 'y':obj};
+}, compare_Object(check_identical_property_values('x', 'y')));
+
+function check_absent_property(prop) {
+ return function(actual) {
+ assert_false(prop in actual);
+ };
+}
+check('Object with property on prototype', function() {
+ const Foo = function() {};
+ Foo.prototype = {'foo':'bar'};
+ return new Foo();
+}, compare_Object(check_absent_property('foo')));
+
+check('Object with non-enumerable property', function() {
+ const rv = {};
+ Object.defineProperty(rv, 'foo', {value:'bar', enumerable:false, writable:true, configurable:true});
+ return rv;
+}, compare_Object(check_absent_property('foo')));
+
+function check_writable_property(prop) {
+ return function(actual, input) {
+ assert_equals(actual[prop], input[prop]);
+ actual[prop] += ' baz';
+ assert_equals(actual[prop], input[prop] + ' baz');
+ };
+}
+check('Object with non-writable property', function() {
+ const rv = {};
+ Object.defineProperty(rv, 'foo', {value:'bar', enumerable:true, writable:false, configurable:true});
+ return rv;
+}, compare_Object(check_writable_property('foo')));
+
+function check_configurable_property(prop) {
+ return function(actual, input) {
+ assert_equals(actual[prop], input[prop]);
+ delete actual[prop];
+ assert_false('prop' in actual);
+ };
+}
+check('Object with non-configurable property', function() {
+ const rv = {};
+ Object.defineProperty(rv, 'foo', {value:'bar', enumerable:true, writable:true, configurable:false});
+ return rv;
+}, compare_Object(check_configurable_property('foo')));
+
+structuredCloneBatteryOfTests.push({
+ description: 'Object with a getter that throws',
+ async f(runner, t) {
+ const exception = new Error();
+ const testObject = {
+ get testProperty() {
+ throw exception;
+ }
+ };
+ await promise_rejects_exactly(
+ t,
+ exception,
+ runner.structuredClone(testObject)
+ );
+ }
+});
+
+/* The tests below are inspired by @zcorpan’s work but got some
+more substantial changed due to their previous async setup */
+
+function get_canvas_1x1_transparent_black() {
+ const canvas = document.createElement('canvas');
+ canvas.width = 1;
+ canvas.height = 1;
+ return canvas;
+}
+
+function get_canvas_1x1_non_transparent_non_black() {
+ const canvas = document.createElement('canvas');
+ canvas.width = 1;
+ canvas.height = 1;
+ const ctx = canvas.getContext('2d');
+ const imagedata = ctx.getImageData(0, 0, 1, 1);
+ imagedata.data[0] = 100;
+ imagedata.data[1] = 101;
+ imagedata.data[2] = 102;
+ imagedata.data[3] = 103;
+ return canvas;
+}
+
+function compare_ImageBitmap(actual, input) {
+ if (typeof actual === 'string')
+ assert_unreached(actual);
+ assert_true(actual instanceof ImageBitmap, 'instanceof ImageBitmap');
+ assert_not_equals(actual, input);
+ // XXX paint the ImageBitmap on a canvas and check the data
+}
+
+structuredCloneBatteryOfTests.push({
+ description: 'ImageBitmap 1x1 transparent black',
+ async f(runner) {
+ const canvas = get_canvas_1x1_transparent_black();
+ const bm = await createImageBitmap(canvas);
+ const copy = await runner.structuredClone(bm);
+ compare_ImageBitmap(bm, copy);
+ },
+ requiresDocument: true
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'ImageBitmap 1x1 non-transparent non-black',
+ async f(runner) {
+ const canvas = get_canvas_1x1_non_transparent_non_black();
+ const bm = await createImageBitmap(canvas);
+ const copy = await runner.structuredClone(bm);
+ compare_ImageBitmap(bm, copy);
+ },
+ requiresDocument: true
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Array ImageBitmap object, ImageBitmap 1x1 transparent black',
+ async f(runner) {
+ const canvas = get_canvas_1x1_transparent_black();
+ const bm = [await createImageBitmap(canvas)];
+ const copy = await runner.structuredClone(bm);
+ compare_Array(enumerate_props(compare_ImageBitmap))(bm, copy);
+ },
+ requiresDocument: true
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Array ImageBitmap object, ImageBitmap 1x1 transparent non-black',
+ async f(runner) {
+ const canvas = get_canvas_1x1_non_transparent_non_black();
+ const bm = [await createImageBitmap(canvas)];
+ const copy = await runner.structuredClone(bm);
+ compare_Array(enumerate_props(compare_ImageBitmap))(bm, copy);
+ },
+ requiresDocument: true
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Object ImageBitmap object, ImageBitmap 1x1 transparent black',
+ async f(runner) {
+ const canvas = get_canvas_1x1_transparent_black();
+ const bm = {x: await createImageBitmap(canvas)};
+ const copy = await runner.structuredClone(bm);
+ compare_Object(enumerate_props(compare_ImageBitmap))(bm, copy);
+ },
+ requiresDocument: true
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Object ImageBitmap object, ImageBitmap 1x1 transparent non-black',
+ async f(runner) {
+ const canvas = get_canvas_1x1_non_transparent_non_black();
+ const bm = {x: await createImageBitmap(canvas)};
+ const copy = await runner.structuredClone(bm);
+ compare_Object(enumerate_props(compare_ImageBitmap))(bm, copy);
+ },
+ requiresDocument: true
+});
+
+check('ObjectPrototype must lose its exotic-ness when cloned',
+ () => Object.prototype,
+ (copy, original) => {
+ assert_not_equals(copy, original);
+ assert_true(copy instanceof Object);
+
+ const newProto = { some: 'proto' };
+ // Must not throw:
+ Object.setPrototypeOf(copy, newProto);
+
+ assert_equals(Object.getPrototypeOf(copy), newProto);
+ }
+);
+
+structuredCloneBatteryOfTests.push({
+ description: 'Serializing a non-serializable platform object fails',
+ async f(runner, t) {
+ const request = new Response();
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(request)
+ );
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'An object whose interface is deleted from the global must still deserialize',
+ async f(runner) {
+ const blob = new Blob();
+ const blobInterface = globalThis.Blob;
+ delete globalThis.Blob;
+ try {
+ const copy = await runner.structuredClone(blob);
+ assert_true(copy instanceof blobInterface);
+ } finally {
+ globalThis.Blob = blobInterface;
+ }
+ }
+});
+
+check(
+ 'A subclass instance will deserialize as its closest serializable superclass',
+ () => {
+ class FileSubclass extends File {}
+ return new FileSubclass([], "");
+ },
+ (copy) => {
+ assert_equals(Object.getPrototypeOf(copy), File.prototype);
+ }
+);
+
+check(
+ 'Resizable ArrayBuffer',
+ () => {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ assert_true(ab.resizable);
+ return ab;
+ },
+ compare_ArrayBuffer);
+
+structuredCloneBatteryOfTests.push({
+ description: 'Growable SharedArrayBuffer',
+ async f(runner) {
+ const sab = createBuffer('SharedArrayBuffer', 16, { maxByteLength: 1024 });
+ assert_true(sab.growable);
+ try {
+ const copy = await runner.structuredClone(sab);
+ compare_ArrayBuffer(sab, copy);
+ } catch (e) {
+ // If we're cross-origin isolated, cloning SABs should not fail.
+ if (e instanceof DOMException && e.code === DOMException.DATA_CLONE_ERR) {
+ assert_false(self.crossOriginIsolated);
+ } else {
+ throw e;
+ }
+ }
+ }
+});
+
+check(
+ 'Length-tracking TypedArray',
+ () => {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ assert_true(ab.resizable);
+ return new Uint8Array(ab);
+ },
+ compare_ArrayBufferView('Uint8Array'));
+
+check(
+ 'Length-tracking DataView',
+ () => {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ assert_true(ab.resizable);
+ return new DataView(ab);
+ },
+ compare_ArrayBufferView('DataView'));
+
+structuredCloneBatteryOfTests.push({
+ description: 'Serializing OOB TypedArray throws',
+ async f(runner, t) {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ const ta = new Uint8Array(ab, 8);
+ ab.resize(0);
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(ta)
+ );
+ }
+});
+
+structuredCloneBatteryOfTests.push({
+ description: 'Serializing OOB DataView throws',
+ async f(runner, t) {
+ const ab = new ArrayBuffer(16, { maxByteLength: 1024 });
+ const dv = new DataView(ab, 8);
+ ab.resize(0);
+ await promise_rejects_dom(
+ t,
+ "DataCloneError",
+ runner.structuredClone(dv)
+ );
+ }
+});
diff --git a/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-cross-realm-method.html b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-cross-realm-method.html
new file mode 100644
index 0000000000..d80010e0df
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-cross-realm-method.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>self.structuredClone() uses this's relevant Realm for deserialization</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/structured-data.html#structured-cloning">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+const iframe = document.createElement("iframe");
+iframe.onload = () => {
+ const otherWindow = iframe.contentWindow;
+ for (const key of ["Object", "Array", "Date", "RegExp"]) {
+ test(() => {
+ const cloned = otherWindow.structuredClone.call(window, new otherWindow[key]);
+ assert_true(cloned instanceof window[key]);
+ }, `${key} instance`);
+ }
+};
+document.body.append(iframe);
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-detached-window-crash.html b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-detached-window-crash.html
new file mode 100644
index 0000000000..75971023d2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone-detached-window-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>window.structuredClone() doesn't crash when window is detached</title>
+<link rel="help" href="https://crbug.com/1445375">
+<body>
+<script>
+let i = document.createElement("iframe");
+document.body.appendChild(i);
+let i_win = i.contentWindow;
+i.remove();
+i_win.structuredClone("some data");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone.any.js b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone.any.js
new file mode 100644
index 0000000000..1358a71fc0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/structured-clone/structured-clone.any.js
@@ -0,0 +1,14 @@
+// META: title=structuredClone() tests
+// META: script=/common/sab.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js
+// META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js
+
+runStructuredCloneBatteryOfTests({
+ structuredClone: (obj, transfer) => {
+ return new Promise(resolve => {
+ resolve(self.structuredClone(obj, { transfer }));
+ });
+ },
+ hasDocument: typeof document !== "undefined",
+});
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js
new file mode 100644
index 0000000000..197a3b13e9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js
@@ -0,0 +1,8 @@
+test(() => {
+ assert_equals(window.clientInformation, window.navigator);
+}, "window.clientInformation exists and equals window.navigator");
+
+test(() => {
+ window.clientInformation = 1;
+ assert_equals(window.clientInformation, 1);
+}, "window.clientInformation is Replaceable");
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html
new file mode 100644
index 0000000000..4bdab91121
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>NavigatorLanguage: navigator.language returns the user's preferred language</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigatorlanguage">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h2>Precondition</h2>
+<p>The user agent's preferred language is set as English (en).</p>
+<div id="log"></div>
+<script>
+ test(function() {
+ assert_equals(navigator.language, "en");
+ });
+</script>
+
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js
new file mode 100644
index 0000000000..f6b9db078e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js
@@ -0,0 +1,16 @@
+[
+ "registerContentHandler",
+ "isProtocolHandlerRegistered",
+ "isContentHandlerRegistered",
+ "unregisterContentHandler"
+].forEach(method => {
+ test(() => {
+ assert_false(method in self.navigator);
+ }, method + "() is removed");
+});
+
+test(() => {
+ let called = false;
+ self.navigator.registerProtocolHandler("web+test", "%s", { toString: () => called = true });
+ assert_false(called);
+}, "registerProtocolHandler has no third argument");
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html
new file mode 100644
index 0000000000..a971fe9d1c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for lack of indexed getter on Navigator</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-navigator-object">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_false("0" in window.navigator);
+ assert_equals(window.navigator[0], undefined);
+}, "window.navigator[0] should not exist");
+test(function() {
+ window.navigator[0] = "pass";
+ assert_true("0" in window.navigator);
+ assert_equals(window.navigator[0], "pass");
+}, "window.navigator[0] should be settable");
+test(function() {
+ assert_false("-1" in window.navigator);
+ assert_equals(window.navigator[-1], undefined);
+}, "window.navigator[-1] should not exist");
+test(function() {
+ window.navigator[-1] = "pass";
+ assert_true("-1" in window.navigator);
+ assert_equals(window.navigator[-1], "pass");
+}, "window.navigator[-1] should be settable");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html
new file mode 100644
index 0000000000..9cff8d3163
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>navigator.windowControlsOverlay</title>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<script>
+ test(function(){
+ assert_idl_attribute(navigator, 'windowControlsOverlay');
+ }, 'the windowControlsOverlay object should exist on the navigator object');
+
+ test(function(){
+ assert_idl_attribute(navigator.windowControlsOverlay, 'visible');
+ }, 'visible should be a member of the windowControlsOverlay object');
+
+ test(function(){
+ assert_false(navigator.windowControlsOverlay.visible);
+ }, 'visible should be false');
+
+ test(function(){
+ assert_idl_attribute(navigator.windowControlsOverlay, 'getTitlebarAreaRect');
+ }, 'getTitlebarAreaRect should be a method of the windowControlsOverlay object');
+
+ test(function(){
+ var rect = navigator.windowControlsOverlay.getTitlebarAreaRect();
+ assert_true(rect instanceof DOMRect);
+ }, 'getTitlebarAreaRect return type should be DOMRect');
+
+ test(function(){
+ var rect = navigator.windowControlsOverlay.getTitlebarAreaRect();
+ assert_equals(rect.x, 0);
+ assert_equals(rect.y, 0);
+ assert_equals(rect.width, 0);
+ assert_equals(rect.height, 0);
+ }, 'getTitlebarAreaRect should return a empty DOMRect');
+
+ test(function(){
+ assert_idl_attribute(navigator.windowControlsOverlay, 'ongeometrychange');
+ }, 'ongeometrychange should be a member of the windowControlsOverlay object');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js
new file mode 100644
index 0000000000..07bccb7880
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js
@@ -0,0 +1,106 @@
+ var compatibilityMode;
+ if (navigator.userAgent.includes("Chrome")) {
+ compatibilityMode = "Chrome";
+ } else if (navigator.userAgent.includes("WebKit")) {
+ compatibilityMode = "WebKit";
+ } else {
+ compatibilityMode = "Gecko";
+ }
+
+ test(function() {
+ assert_equals(navigator.appCodeName, "Mozilla");
+ }, "appCodeName");
+
+ test(function() {
+ assert_equals(navigator.appName, "Netscape");
+ }, "appName");
+
+ test(function() {
+ assert_equals(typeof navigator.appVersion, "string",
+ "navigator.appVersion should be a string");
+ }, "appVersion");
+
+ test(function() {
+ assert_equals(typeof navigator.platform, "string",
+ "navigator.platform should be a string");
+ }, "platform");
+
+ test(function() {
+ assert_equals(navigator.product, "Gecko");
+ }, "product");
+
+ test(function() {
+ if ("window" in self) {
+ if (compatibilityMode == "Gecko") {
+ assert_equals(navigator.productSub, "20100101");
+ } else {
+ assert_equals(navigator.productSub, "20030107");
+ }
+ } else {
+ assert_false("productSub" in navigator);
+ }
+ }, "productSub");
+
+ test(function() {
+ assert_equals(typeof navigator.userAgent, "string",
+ "navigator.userAgent should be a string");
+ }, "userAgent type");
+
+ async_test(function() {
+ var request = new XMLHttpRequest();
+ request.onload = this.step_func_done(function() {
+ assert_equals("User-Agent: " + navigator.userAgent + "\n",
+ request.response,
+ "userAgent should return the value sent in the " +
+ "User-Agent header");
+ });
+ request.open("GET", "/xhr/resources/inspect-headers.py?" +
+ "filter_name=User-Agent");
+ request.send();
+ }, "userAgent value");
+
+ test(function() {
+ if ("window" in self) {
+ if (compatibilityMode == "Chrome") {
+ assert_equals(navigator.vendor, "Google Inc.");
+ } else if (compatibilityMode == "WebKit") {
+ assert_equals(navigator.vendor, "Apple Computer, Inc.");
+ } else {
+ assert_equals(navigator.vendor, "");
+ }
+ } else {
+ assert_false("vendor" in navigator);
+ }
+ }, "vendor");
+
+ test(function() {
+ if ("window" in self) {
+ assert_equals(navigator.vendorSub, "");
+ } else {
+ assert_false("vendorSub" in navigator);
+ }
+ }, "vendorSub");
+
+ // "If the navigator compatibility mode is Gecko, then the user agent must
+ // also support the following partial interface" (taintEnabled() and oscpu)
+ // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=22555 and
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=27820
+
+ test(function() {
+ if ("window" in self && compatibilityMode == "Gecko") {
+ assert_false(navigator.taintEnabled());
+ } else {
+ assert_false("taintEnabled" in navigator);
+ }
+ }, "taintEnabled");
+
+ test(function() {
+ if ("window" in self && compatibilityMode == "Gecko") {
+ assert_equals(typeof navigator.oscpu, "string",
+ "navigator.oscpu should be a string");
+ } else {
+ assert_false("oscpu" in navigator);
+ }
+ }, "oscpu");
+
+done()
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html
new file mode 100644
index 0000000000..10b3dd2249
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(t => {
+ assert_true("userAgentData" in navigator);
+ assert_true("NavigatorUAData" in window);
+ assert_equals(typeof self.NavigatorUAData, "function")
+ }, "navigator.userAgentData is exposed.");
+
+ promise_test(async t => {
+ let didMicrotaskRun = false;
+ const uaData = navigator.userAgentData;
+ const brandRegex = /^[a-zA-Z ()\-.\/:;=?_]+$/;
+ for (brandVersionPair of uaData.brands) {
+ assert_equals(typeof brandVersionPair.brand, "string", "brand should be a string");
+ assert_regexp_match(brandVersionPair.brand, brandRegex, "brand should not contain unexpected characters");
+ assert_equals(typeof brandVersionPair.version, "string", "version should be a string");
+ }
+ assert_equals(typeof uaData.mobile, "boolean", "mobile should be a boolean");
+
+ const highEntropyData = await uaData.getHighEntropyValues([
+ "platformVersion", "architecture", "model", "uaFullVersion", "fullVersionList", "formFactor"]);
+ assert_equals(typeof highEntropyData["platform"], "string", "Platform brand should be a string");
+ assert_equals(typeof highEntropyData["platformVersion"], "string", "Platform version should be a string");
+ assert_equals(typeof highEntropyData["architecture"], "string", "Architecture should be a string");
+ assert_equals(typeof highEntropyData["model"], "string", "Model should be a string");
+ assert_equals(typeof highEntropyData["uaFullVersion"], "string", "UAFullVersion should be a string");
+ for (formFactor of highEntropyData['formFactor']) {
+ assert_equals(typeof formFactor, "string", "Each FormFactor should be a string");
+ }
+ for (brandVersionPair of highEntropyData['fullVersionList']) {
+ assert_equals(typeof brandVersionPair.brand, "string", "brand should be a string");
+ assert_regexp_match(brandVersionPair.brand, brandRegex, "brand should not contain unexpected characters");
+ assert_equals(typeof brandVersionPair.version, "string", "version should be a string");
+ }
+ const highEntropyData2 = await uaData.getHighEntropyValues([]);
+ assert_equals(typeof highEntropyData["platform"], "string", "Platform brand should be a string");
+ assert_false("platformVersion" in highEntropyData2, "Platform version should be an empty string");
+ assert_false("architecture" in highEntropyData2, "Architecture should be an empty string");
+ assert_false("model" in highEntropyData2, "Model should be an empty string");
+ assert_false("uaFullVersion" in highEntropyData2, "UAFullVersion should be an empty string");
+ assert_false("formFactor" in highEntropyData2, "FormFactor should be an empty string");
+ assert_false("fullVersionList" in highEntropyData2, "fullVersionList should not be present");
+ let finalPromise = uaData.getHighEntropyValues([]).then(() => {
+ assert_true(didMicrotaskRun, "getHighEntropyValues queued on a task");
+ });
+ await Promise.resolve().then(function() {
+ didMicrotaskRun = true;
+ });
+ return finalPromise;
+ }, "navigator.userAgentData returns a UserAgentMetadata object.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html
new file mode 100644
index 0000000000..dd4c531070
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(t => {
+ assert_false("getUserAgent" in navigator);
+ }, "navigator.getUserAgent() is not available in non-secure contexts.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html
new file mode 100644
index 0000000000..adcea90a36
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>navigator.cookieEnabled false</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/#dom-navigator-cookieenabled">
+<meta name="flags" content="interact">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<h2>Preconditions</h2>
+<p>Disable cookies in browser settings.</p>
+
+<script>
+ test(() => {
+ assert_false(navigator.cookieEnabled);
+ }, "navigator.cookieEnabled is false when cookies are disabled");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html
new file mode 100644
index 0000000000..fc7f49d143
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>navigator.cookieEnabled true</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/#dom-navigator-cookieenabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ test(() => {
+ assert_true(navigator.cookieEnabled);
+ }, "navigator.cookieEnabled is true when cookies are enabled");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html
new file mode 100644
index 0000000000..d56df8a3d8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>NavigatorLanguage: the most preferred language is the one returned by navigator.language</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigatorlanguage">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ test(function() {
+ assert_true("language" in navigator);
+ assert_true("languages" in navigator);
+
+ assert_equals(navigator.languages[0], navigator.language,
+ "navigator.languages is the most preferred language first");
+
+ });
+</script>
+
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js
new file mode 100644
index 0000000000..016aef8c4e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js
@@ -0,0 +1,4 @@
+// META: script=/common/object-association.js
+
+testIsPerWindow("navigator");
+testIsPerWindow("clientInformation");
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html
new file mode 100644
index 0000000000..af05015801
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigator.plugins and navigator.mimeTypes behavior</title>
+<link rel="author" href="mailto:domenic@chromium.org">
+<link rel="help" href="https://github.com/whatwg/html/pull/6738">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+test(() => {
+ assert_true('pdfViewerEnabled' in navigator, "property exists");
+ assert_equals(typeof navigator.pdfViewerEnabled, 'boolean', "property is boolean");
+}, "navigator.pdfViewerEnabled exists");
+
+const pluginNames = [
+ "PDF Viewer",
+ "Chrome PDF Viewer",
+ "Chromium PDF Viewer",
+ "Microsoft Edge PDF Viewer",
+ "WebKit built-in PDF"
+];
+
+const mimeTypes = [
+ "application/pdf",
+ "text/pdf"
+];
+
+if (navigator.pdfViewerEnabled) {
+ test(() => {
+ assert_equals(navigator.mimeTypes.length, mimeTypes.length, "length");
+
+ for (let i = 0; i < mimeTypes.length; ++i) {
+ const mimeType = mimeTypes[i];
+ const mimeTypeObject = navigator.mimeTypes.item(i);
+
+ assert_equals(mimeTypeObject.type, mimeType, `${i}th type`);
+ assert_equals(mimeTypeObject.description, "Portable Document Format", `${i}th description`);
+ assert_equals(mimeTypeObject.suffixes, "pdf", `${i}th suffixes`);
+ assert_equals(mimeTypeObject, navigator.mimeTypes.namedItem(mimeType), `mimeTypes.item(${i}) matches namedItem("${mimeType}")`);
+ assert_equals(mimeTypeObject.enabledPlugin, navigator.plugins[0], `${i}th enabledPlugin matches 0th Plugin`)
+ }
+ }, "navigator.mimeTypes contains the hard-coded list");
+
+ test(() => {
+ assert_equals(navigator.plugins.length, pluginNames.length, "length");
+
+ for (let i = 0; i < pluginNames.length; ++i) {
+ const pluginName = pluginNames[i];
+ const pluginObject = navigator.plugins.item(i);
+
+ assert_equals(pluginObject.name, pluginName, `${i}th name`);
+ assert_equals(pluginObject.description, "Portable Document Format", `${i}th description`);
+ assert_equals(pluginObject.filename, "internal-pdf-viewer", `${i}th filename`);
+ assert_equals(pluginObject, navigator.plugins.namedItem(pluginName), `plugins.item(${i}) matches namedItem("${pluginName}")`);
+
+ for (let j = 0; j < mimeTypes.length; ++j) {
+ const mimeType = mimeTypes[j];
+ assert_equals(pluginObject.item(j).type, navigator.mimeTypes[j].type, `item(${j}) on plugin(${i}) (${pluginObject.name})`);
+ assert_equals(pluginObject.namedItem(mimeType).type, navigator.mimeTypes.item(j).type, `namedItem("${mimeType}") on plugin(${i})`);
+ }
+ }
+ }, "navigator.plugins contains the hard-coded list");
+} else {
+ test(() => {
+ assert_equals(navigator.mimeTypes.length, 0, "length");
+ assert_equals(navigator.mimeTypes.item(0), null, "item");
+
+ for (const mimeType of mimeTypes) {
+ assert_equals(navigator.mimeTypes.namedItem(mimeType), null, `namedItem("${mimeType}")`);
+ }
+ }, "navigator.mimeTypes is empty");
+
+ test(() => {
+ assert_equals(navigator.plugins.length, 0, "length");
+ assert_equals(navigator.plugins.item(0), null, "item");
+
+ for (const pluginName of pluginNames) {
+ assert_equals(navigator.plugins.namedItem(pluginName), null, `namedItem("${pluginName}")`);
+ }
+ }, "navigator.plugins is empty");
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html
new file mode 100644
index 0000000000..1561786569
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html
new file mode 100644
index 0000000000..be3a6be666
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment (does not use a service worker)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html
new file mode 100644
index 0000000000..61b9722ec2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment (does not use a service worker)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html
new file mode 100644
index 0000000000..1eaefaa45f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html
new file mode 100644
index 0000000000..085c5723ec
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the path</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "path";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html
new file mode 100644
index 0000000000..a86e79e42e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the path</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "path";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html
new file mode 100644
index 0000000000..8ce65a5bad
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html
new file mode 100644
index 0000000000..9b4473fb89
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query (does not use a service worker)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html
new file mode 100644
index 0000000000..97f67e94ec
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query (does not use a service worker)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html
new file mode 100644
index 0000000000..8c7b2e1cfd
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html
new file mode 100644
index 0000000000..add78d2fd4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html
@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>protocol handlers</title>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<noscript><p>Enable JavaScript and reload.</p></noscript>
+
+<p><strong>Note:</strong> If your browser limits the number of handler
+registration requests on a page, you might need to disable or significantly
+increase that limit for the tests below to run.</p>
+
+<script>
+test(() => {
+ assert_idl_attribute(navigator, 'registerProtocolHandler');
+}, 'the registerProtocolHandler method should exist on the navigator object');
+
+test(() => {
+ assert_idl_attribute(navigator, 'unregisterProtocolHandler');
+}, 'the unregisterProtocolHandler method should exist on the navigator object');
+
+/* URL argument */
+[
+ '%s',
+ 'foo/%s',
+ `%s${location.href}`,
+ location.href.replace(location.protocol,
+ `${location.protocol[0]}%s${location.protocol.substring(1)}`),
+ location.href.replace(location.protocol, `${location.protocol}%s`),
+ location.href + '/%s',
+ location.href + '#%s',
+ location.href + '?foo=%s',
+ location.href + '?foo=%s&bar',
+ location.href + '/%s/bar/baz/',
+ location.href + '/%s/bar/baz/?foo=1337&bar#baz',
+ location.href + '/%s/foo/%s/',
+].forEach(url => {
+ test(() => {
+ navigator.registerProtocolHandler('tel', url, 'foo');
+ }, 'registerProtocolHandler: Valid URL "' + url + '" should work.');
+
+ test(() => {
+ navigator.unregisterProtocolHandler('tel', url);
+ }, 'unregisterProtocolHandler: Valid URL "' + url + '" should work.');
+});
+
+/* Invalid URLs */
+[
+ '',
+ '%S',
+ 'http://%s.com',
+ 'http://%s.example.com',
+ location.href.replace(location.hostname, `%s${location.hostname}`),
+ location.href.replace(location.port, `%s${location.port}`),
+ location.href + '',
+ location.href + '/%',
+ location.href + '/%a',
+ 'http://example.com',
+ 'http://[v8.:::]//url=%s',
+ 'https://test:test/',
+].forEach(url => {
+ test(() => {
+ assert_throws_dom('SYNTAX_ERR', () => { navigator.registerProtocolHandler('mailto', url, 'foo'); });
+ assert_throws_dom('SECURITY_ERR', () => { navigator.registerProtocolHandler('x', url, 'foo'); });
+ }, `registerProtocolHandler: Invalid URL "${url}" should throw (but after scheme)`);
+
+ test(() => {
+ assert_throws_dom('SYNTAX_ERR', () => { navigator.unregisterProtocolHandler('mailto', url); });
+ assert_throws_dom('SECURITY_ERR', () => { navigator.unregisterProtocolHandler('x', url, 'foo'); });
+ }, `unregisterProtocolHandler: Invalid URL "${url}" should throw (but after scheme)`);
+});
+
+[
+ 'http://example.com/%s',
+ 'https://example.com/%s',
+ 'http://foobar.example.com/%s',
+ 'mailto:%s@example.com',
+ 'mailto:%s',
+ `chrome://${location.host}/%s`,
+ `foo://${location.host}/%s`,
+ URL.createObjectURL(new Blob()) + "#%s",
+].forEach(url => {
+ const title = url.startsWith("blob:") ? "blob: URL" : url;
+ test(() => {
+ assert_throws_dom('SECURITY_ERR', () => { navigator.registerProtocolHandler('mailto', url, 'foo'); });
+ }, `registerProtocolHandler: Invalid URL "${title}" should throw SECURITY_ERR.`);
+
+ test(() => {
+ assert_throws_dom('SECURITY_ERR', () => { navigator.unregisterProtocolHandler('mailto', url); });
+ }, `unregisterProtocolHandler: Invalid URL "${title}" should throw SECURITY_ERR.`);
+});
+
+/* Protocol argument */
+
+/* Overriding any of the following protocols must never be allowed. That would
+ * break the browser. */
+[
+ 'about',
+ 'attachment',
+ 'blob',
+ 'chrome',
+ 'cid',
+ 'data',
+ 'file',
+ 'http',
+ 'https',
+ 'javascript',
+ 'livescript',
+ 'mid',
+ 'mocha',
+ 'moz-icon',
+ 'opera',
+ 'operamail',
+ 'res',
+ 'resource',
+ 'shttp',
+ 'tcl',
+ 'vbscript',
+ 'view-source',
+ 'ws',
+ 'wss',
+ 'wyciwyg',
+ /* other invalid schemes */
+ 'unrecognized',
+ 'mаilto', /* a cyrillic "а" */
+ 'mailto:',
+ 'mailto://',
+ 'mailto' + String.fromCharCode(0),
+ 'mailtoo' + String.fromCharCode(8),
+ 'mailto' + String.fromCharCode(10),
+ 'http://',
+ 'ssh:/',
+ 'magnet:+',
+ 'tel:sip',
+ 'foo',
+ 'fweb+oo',
+ /* web+ prefixed schemes must be followed by 1+ ascii alphas */
+ 'web+',
+ 'web+1',
+ 'web+namewithid123',
+ 'web+namewithtrailingspace ',
+ 'web+préfixewithaccent', // é is not ascii alpha
+ 'web+Kelvinsign', // ASCII-lower KELVIN SIGN is not k
+ 'web+latinsmallletterlongſ', // ASCII-lower LATIN SMALL LETTER LONG S is not s
+ 'web+dots.are.forbidden',
+ 'web+dashes-are-forbidden',
+ 'web+underscores_are_forbidden',
+ 'web+spaces are forbidden',
+ 'web+non*alpha*are*forbidden',
+ 'web+digits123areforbidden',
+].forEach(scheme => {
+ test(() => {
+ // https://test:test/ does not parse and does not contain %s, but the scheme check happens first
+ assert_throws_dom('SECURITY_ERR', () => { navigator.registerProtocolHandler(scheme, 'https://test:test/', 'foo'); });
+ }, 'registerProtocolHandler: Attempting to override the "' + scheme + '" protocol should throw SECURITY_ERR.');
+
+ test(() => {
+ assert_throws_dom('SECURITY_ERR', () => { navigator.unregisterProtocolHandler(scheme, 'https://test:test/'); });
+ }, 'unregisterProtocolHandler: Attempting to override the "' + scheme + '" protocol should throw SECURITY_ERR.');
+});
+
+/* The following protocols must be possible to override.
+ * We're just testing that the call goes through here. Whether or not they
+ * actually work as handlers is covered by the interactive tests. */
+
+[
+ /* safelisted schemes listed in
+ * https://html.spec.whatwg.org/multipage/system-state.html#safelisted-scheme */
+ 'bitcoin',
+ 'ftp',
+ 'ftps',
+ 'geo',
+ 'im',
+ 'irc',
+ 'ircs',
+ 'magnet',
+ 'mailto',
+ 'matrix',
+ 'mms',
+ 'news',
+ 'nntp',
+ 'openpgp4fpr',
+ 'sftp',
+ 'sip',
+ 'sms',
+ 'smsto',
+ 'ssh',
+ 'tel',
+ 'urn',
+ 'webcal',
+ 'wtai',
+ 'xmpp',
+ /* other valid schemes */
+ 'BitcoIn',
+ 'Irc',
+ 'MagneT',
+ 'Matrix',
+ 'SmsTo',
+ 'TEL',
+ 'teL',
+ 'WebCAL',
+ 'WTAI',
+ 'web+myprotocol',
+ 'web+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', // all alphas
+ 'web+UpperCasedIsLowercased',
+ 'WEB+seeabove',
+ 'WeB+SeEaBoVe'
+].forEach(scheme => {
+ test(() => {
+ navigator.registerProtocolHandler(scheme, location.href + '/%s', "foo");
+ }, 'registerProtocolHandler: overriding the "' + scheme + '" protocol should work');
+
+ test(() => {
+ navigator.unregisterProtocolHandler(scheme, location.href + '/%s');
+ }, 'unregisterProtocolHandler: overriding the "' + scheme + '" protocol should work');
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html
new file mode 100644
index 0000000000..0120aaa12f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>protocol handlers</title>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<script>
+// This should be merged into protocol.https.html when/if
+// https://github.com/whatwg/html/pull/5482 is approved.
+[
+ 'cabal',
+ 'dat',
+ 'did',
+ 'dweb',
+ 'ethereum',
+ 'hyper',
+ 'ipfs',
+ 'ipns',
+ 'ssb',
+].forEach(scheme => {
+ test(() => {
+ navigator.registerProtocolHandler(scheme, location.href + '/%s', "foo");
+ }, 'registerProtocolHandler: overriding the "' + scheme + '" protocol should work');
+
+ test(() => {
+ navigator.unregisterProtocolHandler(scheme, location.href + '/%s');
+ }, 'unregisterProtocolHandler: overriding the "' + scheme + '" protocol should work');
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js
new file mode 100644
index 0000000000..5fd915d17f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js
@@ -0,0 +1,3 @@
+onfetch = e => {
+ e.respondWith(fetch("handler.html"));
+}
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js
new file mode 100644
index 0000000000..88c62ec373
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js
@@ -0,0 +1,53 @@
+// These can be used in an environment that has these global variables defined:
+// * type (one of "path", "query", or "fragment")
+// * noSW (a boolean)
+
+if (type === "path" && noSW) {
+ throw new Error("There is no support for a path handler without a service worker.");
+}
+
+const swString = noSW ? "" : "sw";
+const handler = {
+ "path": "PSS%sPSE/?QES\u2020QEE#FES\u2020FEE",
+ "query": "?QES\u2020QEEPSS%sPSE#FES\u2020FEE",
+ "fragment": "?QES\u2020QEE#FES\u2020FEEPSS%sPSE"
+}[type];
+const scheme = `web+wpt${type}${swString}`;
+
+function register() {
+ const handlerURL = noSW ? `resources/handler.html${handler}${type}` : `resources/handler/${type}/${handler}`;
+ navigator.registerProtocolHandler(scheme, handlerURL, `WPT ${type} handler${noSW ? ", without service worker" : ""}`);
+}
+
+function runTest({ includeNull = false } = {}) {
+ promise_test(async t => {
+ const bc = new BroadcastChannel(`protocol-handler-${type}${swString}`);
+ if (!noSW) {
+ const reg = await service_worker_unregister_and_register(t, "resources/handler-sw.js", "resources/handler/");
+ t.add_cleanup(async () => await reg.unregister());
+ await wait_for_state(t, reg.installing, 'activated');
+ }
+ const a = document.body.appendChild(document.createElement("a"));
+ const codePoints = [];
+ let i = includeNull ? 0 : 1;
+ for (; i < 0x82; i++) {
+ codePoints.push(String.fromCharCode(i));
+ }
+ a.href = `${scheme}:${codePoints.join("")}`;
+ a.target = "_blank";
+ a.click();
+ await new Promise(resolve => {
+ bc.onmessage = t.step_func(e => {
+ resultingURL = e.data;
+ assert_equals(stringBetweenMarkers(resultingURL, "QES", "QEE"), "%86", "query baseline");
+ assert_equals(stringBetweenMarkers(resultingURL, "FES", "FEE"), "%E2%80%A0", "fragment baseline");
+ assert_equals(stringBetweenMarkers(resultingURL, "PSS", "PSE"), `${encodeURIComponent(scheme)}%3A${includeNull ? "%2500" : ""}%2501%2502%2503%2504%2505%2506%2507%2508%250B%250C%250E%250F%2510%2511%2512%2513%2514%2515%2516%2517%2518%2519%251A%251B%251C%251D%251E%251F%20!%22%23%24%25%26${type === "query" ? "%27" : "'"}()*%2B%2C-.%2F0123456789%3A%3B%253C%3D%253E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%2560abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%257F%25C2%2580%25C2%2581`, "actual test");
+ resolve();
+ });
+ });
+ });
+}
+
+function stringBetweenMarkers(string, start, end) {
+ return string.substring(string.indexOf(start) + start.length, string.indexOf(end));
+}
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html
new file mode 100644
index 0000000000..552e541784
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<p>This popup can be closed if it does not close itself.
+<p>
+<script>
+// This resource either gets navigated to through a service worker as a result of a URL that looks
+// like:
+// https://.../html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler/{type}/...
+// (the host is excluded to not upset the lint tool)
+// or it gets navigated to directly with the type appended to the end of the URL. In that case type
+// can only be fragment or query.
+
+let type = null;
+let swString = null;
+if (new URL(document.URL).pathname.endsWith("handler.html")) {
+ swString = "";
+ type = (document.URL.endsWith("fragment")) ? "fragment" : "query";
+} else {
+ type = document.URL.split("/")[9];
+ swString = "sw";
+}
+new BroadcastChannel(`protocol-handler-${type}${swString}`).postMessage(document.URL);
+window.close();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html
new file mode 100644
index 0000000000..685f5d19d7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ test(t => {
+ assert_false('registerProtocolHandler' in navigator);
+ assert_equals(navigator.registerProtocolHandler, undefined);
+ }, "navigator.registerProtocolHandler does not exist in non-secure contexts.");
+
+ test(t => {
+ assert_false('unregisterProtocolHandler' in navigator);
+ assert_equals(navigator.unregisterProtocolHandler, undefined);
+ }, "navigator.unregisterProtocolHandler does not exist in non-secure contexts.");
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/README.md b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/README.md
new file mode 100644
index 0000000000..10ae3e5f03
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/README.md
@@ -0,0 +1 @@
+`self.crossOriginIsolated` is tested in `html/cross-origin-opener-policy/coep.https.html`, `html/cross-origin-opener-policy/no-https.html`, `html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-coop-coep.https.any.js`, and `html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js`.
diff --git a/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/Worker_Self_Origin.html b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/Worker_Self_Origin.html
new file mode 100644
index 0000000000..22b28b3e35
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/Worker_Self_Origin.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test workers self.origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function assertOriginWorker(workerSource, expectedOrigin, testName) {
+ async_test(function(t) {
+ w = new Worker(workerSource);
+ w.onmessage = t.step_func(function(e) {
+ assert_equals(e.data, expectedOrigin);
+ t.done();
+ });
+ }, testName + ' Worker');
+}
+
+function assertOriginSharedWorker(workerSource, expectedOrigin, testName) {
+ async_test(function(t) {
+ w = new SharedWorker(workerSource);
+ w.port.start();
+ w.port.onmessage = t.step_func(function(e) {
+ assert_equals(e.data, expectedOrigin);
+ t.done();
+ });
+ }, testName + ' SharedWorker');
+}
+
+// Test same-origin workers
+assertOriginWorker("./support/WorkerSelfOriginWorker.js", self.origin, "Same Origin");
+assertOriginSharedWorker("./support/WorkerSelfOriginSharedWorker.js", self.origin, "Same Origin");
+
+// Test data url workers have opaque origin
+assertOriginWorker("data:application/javascript,postMessage(self.origin);", "null", "Data Url");
+assertOriginSharedWorker("data:application/javascript,onconnect = function(e) { e.ports[0].postMessage(self.origin); }", "null", "Data Url");
+
+// Test blob url workers
+blob = new Blob(["postMessage(self.origin);"]);
+blobUrl = URL.createObjectURL(blob);
+assertOriginWorker(blobUrl, self.origin, "Blob Url");
+
+blob = new Blob(["onconnect = function(e) { e.ports[0].postMessage(self.origin); }"]);
+blobUrl = URL.createObjectURL(blob);
+assertOriginSharedWorker(blobUrl, self.origin, "Blob Url");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginSharedWorker.js b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginSharedWorker.js
new file mode 100644
index 0000000000..3acc57102a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginSharedWorker.js
@@ -0,0 +1,5 @@
+// Post back the location of the worker
+
+onconnect = function(e) {
+ e.ports[0].postMessage(self.origin);
+}
diff --git a/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginWorker.js b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginWorker.js
new file mode 100644
index 0000000000..1a69b55d3f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/the-windoworworkerglobalscope-mixin/support/WorkerSelfOriginWorker.js
@@ -0,0 +1,4 @@
+// Post back the location of the worker
+
+postMessage(self.origin);
+
diff --git a/testing/web-platform/tests/html/webappapis/timers/clearinterval-from-callback.any.js b/testing/web-platform/tests/html/webappapis/timers/clearinterval-from-callback.any.js
new file mode 100644
index 0000000000..bf4eb7cf5a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/clearinterval-from-callback.any.js
@@ -0,0 +1,19 @@
+async_test((t) => {
+ let wasPreviouslyCalled = false;
+
+ const handle = setInterval(
+ t.step_func(() => {
+ if (!wasPreviouslyCalled) {
+ wasPreviouslyCalled = true;
+
+ clearInterval(handle);
+
+ // Make the test succeed after the callback would've run next.
+ setInterval(t.step_func_done(), 750);
+ } else {
+ assert_unreached();
+ }
+ }),
+ 500
+ );
+}, "Clearing an interval from the callback should still clear it.");
diff --git a/testing/web-platform/tests/html/webappapis/timers/cleartimeout-clearinterval.any.js b/testing/web-platform/tests/html/webappapis/timers/cleartimeout-clearinterval.any.js
new file mode 100644
index 0000000000..44551aa8a1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/cleartimeout-clearinterval.any.js
@@ -0,0 +1,29 @@
+async_test((t) => {
+ const handle = setTimeout(
+ t.step_func(() => {
+ assert_unreached("Timeout was not canceled");
+ }),
+ 0
+ );
+
+ clearInterval(handle);
+
+ setTimeout(() => {
+ t.done();
+ }, 100);
+}, "Clear timeout with clearInterval");
+
+async_test((t) => {
+ const handle = setInterval(
+ t.step_func(() => {
+ assert_unreached("Interval was not canceled");
+ }),
+ 0
+ );
+
+ clearTimeout(handle);
+
+ setTimeout(() => {
+ t.done();
+ }, 100);
+}, "Clear interval with clearTimeout");
diff --git a/testing/web-platform/tests/html/webappapis/timers/evil-spec-example.any.js b/testing/web-platform/tests/html/webappapis/timers/evil-spec-example.any.js
new file mode 100644
index 0000000000..17215e218a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/evil-spec-example.any.js
@@ -0,0 +1,12 @@
+var t = async_test("Interaction of setTimeout and WebIDL")
+function finishTest() {
+ assert_equals(log, "ONE TWO ")
+ t.done()
+}
+var log = '';
+function logger(s) { log += s + ' '; }
+
+setTimeout({ toString: function () {
+ setTimeout("logger('ONE')", 100);
+ return "logger('TWO'); t.step(finishTest)";
+} }, 100);
diff --git a/testing/web-platform/tests/html/webappapis/timers/missing-timeout-setinterval.any.js b/testing/web-platform/tests/html/webappapis/timers/missing-timeout-setinterval.any.js
new file mode 100644
index 0000000000..33a1cc073c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/missing-timeout-setinterval.any.js
@@ -0,0 +1,34 @@
+function timeout_trampoline(t, timeout, message) {
+ t.step_timeout(function() {
+ // Yield in case we managed to be called before the second interval callback.
+ t.step_timeout(function() {
+ assert_unreached(message);
+ }, timeout);
+ }, timeout);
+}
+
+async_test(function(t) {
+ let ctr = 0;
+ let h = setInterval(t.step_func(function() {
+ if (++ctr == 2) {
+ clearInterval(h);
+ t.done();
+ return;
+ }
+ }) /* no interval */);
+
+ timeout_trampoline(t, 100, "Expected setInterval callback to be called two times");
+}, "Calling setInterval with no interval should be the same as if called with 0 interval");
+
+async_test(function(t) {
+ let ctr = 0;
+ let h = setInterval(t.step_func(function() {
+ if (++ctr == 2) {
+ clearInterval(h);
+ t.done();
+ return;
+ }
+ }), undefined);
+
+ timeout_trampoline(t, 100, "Expected setInterval callback to be called two times");
+}, "Calling setInterval with undefined interval should be the same as if called with 0 interval");
diff --git a/testing/web-platform/tests/html/webappapis/timers/negative-setinterval.any.js b/testing/web-platform/tests/html/webappapis/timers/negative-setinterval.any.js
new file mode 100644
index 0000000000..5646140c2a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/negative-setinterval.any.js
@@ -0,0 +1,12 @@
+setup({ single_test: true });
+var i = 0;
+var interval;
+function next() {
+ i++;
+ if (i === 20) {
+ clearInterval(interval);
+ done();
+ }
+}
+setTimeout(assert_unreached, 1000);
+interval = setInterval(next, -100);
diff --git a/testing/web-platform/tests/html/webappapis/timers/negative-settimeout.any.js b/testing/web-platform/tests/html/webappapis/timers/negative-settimeout.any.js
new file mode 100644
index 0000000000..da191f1bf0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/negative-settimeout.any.js
@@ -0,0 +1,3 @@
+setup({ single_test: true });
+setTimeout(done, -100);
+setTimeout(assert_unreached, 10);
diff --git a/testing/web-platform/tests/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html b/testing/web-platform/tests/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html
new file mode 100644
index 0000000000..4a780fc932
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>window.setInterval() reports the exception from its callback in the callback's global object</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+setup({ allow_uncaught_exception: true });
+
+const onerrorCalls = [];
+window.onerror = () => { onerrorCalls.push("top"); };
+frames[0].onerror = () => { onerrorCalls.push("frame0"); };
+frames[1].onerror = () => { onerrorCalls.push("frame1"); };
+frames[2].onerror = () => { onerrorCalls.push("frame2"); };
+
+async_test(t => {
+ window.onload = t.step_func(() => {
+ const id = frames[0].setInterval(new frames[1].Function(`
+ parent.clearThisInterval();
+ throw new parent.frames[2].Error("PASS");
+ `), 4);
+ window.clearThisInterval = () => { frames[0].clearInterval(id); };
+
+ t.step_timeout(() => {
+ assert_array_equals(onerrorCalls, ["frame1"]);
+ t.done();
+ }, 8);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html b/testing/web-platform/tests/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html
new file mode 100644
index 0000000000..b4860151a6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>window.setTimeout() reports the exception from its callback in the callback's global object</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+setup({ allow_uncaught_exception: true });
+
+const onerrorCalls = [];
+window.onerror = () => { onerrorCalls.push("top"); };
+frames[0].onerror = () => { onerrorCalls.push("frame0"); };
+frames[1].onerror = () => { onerrorCalls.push("frame1"); };
+frames[2].onerror = () => { onerrorCalls.push("frame2"); };
+
+async_test(t => {
+ window.onload = t.step_func(() => {
+ frames[0].setTimeout(new frames[1].Function(`throw new parent.frames[2].Error("PASS");`), 4);
+
+ t.step_timeout(() => {
+ assert_array_equals(onerrorCalls, ["frame1"]);
+ t.done();
+ }, 8);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/timers/type-long-setinterval.any.js b/testing/web-platform/tests/html/webappapis/timers/type-long-setinterval.any.js
new file mode 100644
index 0000000000..164527f18b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/type-long-setinterval.any.js
@@ -0,0 +1,8 @@
+setup({ single_test: true });
+var interval;
+function next() {
+ clearInterval(interval);
+ done();
+}
+interval = setInterval(next, Math.pow(2, 32));
+setTimeout(assert_unreached, 100);
diff --git a/testing/web-platform/tests/html/webappapis/timers/type-long-settimeout.any.js b/testing/web-platform/tests/html/webappapis/timers/type-long-settimeout.any.js
new file mode 100644
index 0000000000..9092f13f3b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/timers/type-long-settimeout.any.js
@@ -0,0 +1,3 @@
+setup({ single_test: true });
+setTimeout(done, Math.pow(2, 32));
+setTimeout(assert_unreached, 100);
diff --git a/testing/web-platform/tests/html/webappapis/update-rendering/child-document-raf-order.html b/testing/web-platform/tests/html/webappapis/update-rendering/child-document-raf-order.html
new file mode 100644
index 0000000000..222c1af444
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/update-rendering/child-document-raf-order.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>Ordering of steps in "Update the Rendering" - child document requestAnimationFrame order</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering">
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Mozilla" href="https://mozilla.org/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=log></div>
+
+<!--
+
+This test tests the interaction of just two substeps of the "Update the
+rendering" steps in
+https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering
+
+These are:
+
+ 1. Let docs be the list of Document objects associated with the event
+ loop in question, sorted arbitrarily except that the following
+ conditions must be met:
+
+ - Any Document B that is nested through a Document A must be listed
+ after A in the list.
+
+ - If there are two documents A and B whose browsing contexts are
+ both nested browsing contexts and their browsing context
+ containers are both elements in the same Document C, then the
+ order of A and B in the list must match the relative tree order of
+ their respective browsing context containers in C.
+
+ In the steps below that iterate over docs, each Document must be
+ processed in the order it is found in the list.
+
+and later:
+
+10. For each fully active Document in docs, run the animation frame
+ callbacks for that Document, passing in now as the timestamp.
+
+
+It tests this by setting up a tree of three documents, two children and
+one parent, and testing for the relative order of the animation frame
+callbacks for each.
+
+-->
+
+<script>
+
+async_test(function (t) {
+ step_timeout(setup, 0);
+
+ let first_frame, second_frame;
+
+ let notification_sequence = [];
+
+ function setup() {
+ // Start by creating two iframes. To test (a little bit) the rule
+ // about iteration being in document order, insert them in the reverse
+ // order of creation.
+ let body = document.body;
+ function make_iframe() {
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute("srcdoc", "<body onload='parent.child_ready()'>");
+ iframe.setAttribute("width", "30");
+ iframe.setAttribute("height", "15");
+ return iframe;
+ }
+ second_frame = make_iframe();
+ body.prepend(second_frame);
+ first_frame = make_iframe();
+ body.prepend(first_frame);
+
+ let children_waiting = 2;
+ window.child_ready = function() {
+ if (--children_waiting == 0) {
+ // Call requestAnimationFrame in neither the order nor the reverse
+ // of the order in which we expect to be called (which is parent,
+ // first, second).
+ first_frame.contentWindow.requestAnimationFrame(first_child_raf);
+ second_frame.contentWindow.requestAnimationFrame(second_child_raf);
+ window.requestAnimationFrame(parent_raf);
+ }
+ };
+ }
+
+ let parent_raf = t.step_func(function() {
+ notification_sequence.push("parent_raf");
+
+ // Request another notification to help ensure we're getting expected behavior.
+ window.requestAnimationFrame(parent_raf);
+ });
+
+ let first_child_raf = t.step_func(function() {
+ notification_sequence.push("first_child_raf");
+
+ // Request another notification to help ensure we're getting expected behavior.
+ first_frame.contentWindow.requestAnimationFrame(first_child_raf);
+ });
+
+ let second_child_raf = t.step_func(function() {
+ notification_sequence.push("second_child_raf");
+
+ // Request another notification to help ensure we're getting expected behavior.
+ second_frame.contentWindow.requestAnimationFrame(second_child_raf);
+
+ step_timeout(finish, 0);
+ });
+
+ let finish = t.step_func(function() {
+ assert_array_equals(notification_sequence,
+ ["parent_raf", "first_child_raf", "second_child_raf"],
+ "expected order of notifications");
+ t.done();
+ });
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/confirm-different-origin-frame.sub.html b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/confirm-different-origin-frame.sub.html
new file mode 100644
index 0000000000..693cde2192
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/confirm-different-origin-frame.sub.html
@@ -0,0 +1,24 @@
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ setup({ single_test: true });
+ function handleEvent(e) {
+ assert_equals(e.data, 'pass');
+ done();
+ }
+ function on_iframe_load() {
+ var frameWin = document.getElementById('confirmFrame').contentWindow;
+ frameWin.postMessage('Confirm', '*');
+ }
+ window.addEventListener('message', handleEvent);
+</script>
+
+<body>
+ <iframe id='confirmFrame'
+ src='http://{{hosts[alt][www]}}:{{ports[http][0]}}/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/confirm.html'
+ onload='on_iframe_load()'></iframe>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/prompt-different-origin-frame.sub.html b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/prompt-different-origin-frame.sub.html
new file mode 100644
index 0000000000..151def9b5a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/prompt-different-origin-frame.sub.html
@@ -0,0 +1,24 @@
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ setup({ single_test: true });
+ function handleEvent(e) {
+ assert_equals(e.data, 'pass');
+ done();
+ }
+ function on_iframe_load() {
+ var frameWin = document.getElementById('promptFrame').contentWindow;
+ frameWin.postMessage('Prompt', '*');
+ }
+ window.addEventListener('message', handleEvent);
+</script>
+
+<body>
+ <iframe id='promptFrame'
+ src='http://{{hosts[alt][www]}}:{{ports[http][0]}}/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/prompt.html'
+ onload='on_iframe_load()'></iframe>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/confirm.html b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/confirm.html
new file mode 100644
index 0000000000..80b2c61b39
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/confirm.html
@@ -0,0 +1,11 @@
+<script>
+ function handleEvent(e) {
+ var conf = window.confirm('Confirm Dialog');
+ if (conf == false) {
+ window.parent.postMessage('pass', '*');
+ } else {
+ window.parent.postMessage('fail', '*');
+ }
+ }
+ window.addEventListener('message', handleEvent);
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/prompt.html b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/prompt.html
new file mode 100644
index 0000000000..db40774770
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/cannot-show-simple-dialogs/support/prompt.html
@@ -0,0 +1,11 @@
+<script>
+ function handleEvent(e) {
+ var conf = window.prompt('Prompt Dialog');
+ if (conf == null) {
+ window.parent.postMessage('pass', '*');
+ } else {
+ window.parent.postMessage('fail', '*');
+ }
+ }
+ window.addEventListener('message', handleEvent);
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/newline-normalization-manual.html b/testing/web-platform/tests/html/webappapis/user-prompts/newline-normalization-manual.html
new file mode 100644
index 0000000000..55cb5ce527
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/newline-normalization-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Newline normalization in simple dialogs</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#simple-dialogs">
+
+<p>The dialogs should all contain text looking like:</p>
+
+<pre>Line 1.1
+Line 1.2
+Line 1.3
+Line 1.4
+
+Line 2.1</pre>
+
+<script>
+"use strict";
+
+for (const func of [alert, confirm, prompt]) {
+ func('Line 1.1\nLine 1.2\rLine 1.3\r\nLine 1.4\n\rLine 2.1');
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/print-during-beforeunload.html b/testing/web-platform/tests/html/webappapis/user-prompts/print-during-beforeunload.html
new file mode 100644
index 0000000000..5925bdde4d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/print-during-beforeunload.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>print() during beforeunload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const w = window.open("resources/print-during-event.sub.html?event=beforeunload");
+ t.add_cleanup(() => w.close());
+
+ const messages = [];
+ window.addEventListener("message", t.step_func(({ data }) => {
+ messages.push(data);
+
+ if (messages.length === 1) {
+ assert_array_equals(messages, ["start"]);
+ w.location.href = "resources/destination.html";
+ } else if (messages.length === 2) {
+ // The test passes if we've reached this point because the print() dialog did not block the navigation.
+ assert_array_equals(messages, ["start", "destination"]);
+ t.done();
+ }
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/print-during-unload.html b/testing/web-platform/tests/html/webappapis/user-prompts/print-during-unload.html
new file mode 100644
index 0000000000..81259a9fe0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/print-during-unload.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>print() during unload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+async_test(t => {
+ const w = window.open("resources/print-during-event.sub.html?event=unload");
+ t.add_cleanup(() => w.close());
+
+ const messages = [];
+ window.addEventListener("message", t.step_func(({ data }) => {
+ messages.push(data);
+
+ if (messages.length === 1) {
+ assert_array_equals(messages, ["start"]);
+ w.location.href = "resources/destination.html";
+ } else if (messages.length === 2) {
+ // The test passes if we've reached this point because the print() dialog did not block the navigation.
+ assert_array_equals(messages, ["start", "destination"]);
+ t.done();
+ }
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/print-in-detached-frame.html b/testing/web-platform/tests/html/webappapis/user-prompts/print-in-detached-frame.html
new file mode 100644
index 0000000000..474f4f4d2a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/print-in-detached-frame.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>print() in a detached iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+test(() => {
+ const iframe = document.createElement("iframe");
+ document.body.append(iframe);
+ const print = iframe.contentWindow.print;
+ iframe.remove();
+
+ print();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/print-manual.html b/testing/web-platform/tests/html/webappapis/user-prompts/print-manual.html
new file mode 100644
index 0000000000..67cbd0dd48
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/print-manual.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Printing</title>
+<link rel=help href="https://html.spec.whatwg.org/#printing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({explicit_timeout: true})
+</script>
+
+<p>Click on the button below.</p>
+<p>If the browser offers to print the current page, dismiss the prompt to
+return to this page. The following should then appear in the box marked as
+"Output":</p>
+<pre>
+before calling print()
+beforeprint
+afterprint
+after calling print()
+</pre>
+<p>If no user dialog appears, either the above or the following should be
+printed in the box marked as "Output":</p>
+<pre>
+before calling print()
+after calling print()
+</pre>
+<p>The test passes if the above criteria are satisfied and "PASS" appears in a
+table below this paragraph. The test fails otherwise.</p>
+
+<button onclick="testBtn(this)">Click here!</button>
+<h3>Output</h3>
+<pre id=output></pre>
+
+<script>
+"use strict";
+// This test is actually synchronous, but we use async_test()'s nice
+// infrastructure around t.step() to capture errors in event listeners. This is
+// necessary since it seems like in Chrome, exceptions in beforeprint/afterprint
+// event listeners are not propagated to error events. See
+// https://crbug.com/977828.
+const t = async_test();
+const log = [];
+function out(str) {
+ log.push(str);
+ output.appendChild(document.createTextNode(str + "\n"));
+}
+function testBtn(btn) {
+ btn.remove();
+ window.addEventListener("beforeprint", function (ev) {
+ // t.step_func() does not preserve `this`, which we test below.
+ t.step(() => {
+ out("beforeprint");
+ assert_equals(Object.getPrototypeOf(ev), Event.prototype);
+ assert_equals(this, window);
+ assert_equals(ev.target, window);
+ assert_equals(ev.type, "beforeprint");
+ });
+ });
+ window.addEventListener("afterprint", function (ev) {
+ // t.step_func() does not preserve `this`, which we test below.
+ t.step(() => {
+ out("afterprint");
+ assert_equals(Object.getPrototypeOf(ev), Event.prototype);
+ assert_equals(this, window);
+ assert_equals(ev.target, window);
+ assert_equals(ev.type, "afterprint");
+ });
+ });
+ out("before calling print()");
+ print();
+ out("after calling print()");
+ t.step(() => {
+ try {
+ assert_array_equals(log, [
+ "before calling print()",
+ "beforeprint",
+ "afterprint",
+ "after calling print()",
+ ]);
+ } catch (err) {
+ try {
+ assert_array_equals(log, [
+ "before calling print()",
+ "after calling print()",
+ ]);
+ } catch (err) {
+ assert_unreached("Output does not match either possibility");
+ }
+ }
+ });
+ t.done();
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/resources/destination.html b/testing/web-platform/tests/html/webappapis/user-prompts/resources/destination.html
new file mode 100644
index 0000000000..87fb35cea3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/resources/destination.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script>
+"use strict";
+
+opener.postMessage("destination", "*");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/user-prompts/resources/print-during-event.sub.html b/testing/web-platform/tests/html/webappapis/user-prompts/resources/print-during-event.sub.html
new file mode 100644
index 0000000000..6f6a78b03c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/user-prompts/resources/print-during-event.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page that tries to print during an event</title>
+
+<script>
+"use strict";
+
+window.on{{GET[event]}} = () => {
+ try {
+ window.print();
+ } catch (e) {
+ window.opener.postMessage(`error: ${e.message}`);
+ }
+};
+
+window.opener.postMessage("start", "*");
+</script>